diff options
Diffstat (limited to 'app')
33 files changed, 487 insertions, 182 deletions
diff --git a/app/assets/javascripts/components/containers/mastodon.jsx b/app/assets/javascripts/components/containers/mastodon.jsx index d8810dc64..b9086de42 100644 --- a/app/assets/javascripts/components/containers/mastodon.jsx +++ b/app/assets/javascripts/components/containers/mastodon.jsx @@ -52,8 +52,8 @@ import no from 'react-intl/locale-data/no'; import ru from 'react-intl/locale-data/ru'; import uk from 'react-intl/locale-data/uk'; import zh from 'react-intl/locale-data/zh'; +import bg from 'react-intl/locale-data/bg'; import { localeData as zh_hk } from '../locales/zh-hk'; - import getMessagesForLocale from '../locales'; import { hydrateStore } from '../actions/store'; import createStream from '../stream'; @@ -66,7 +66,6 @@ const browserHistory = useRouterHistory(createBrowserHistory)({ basename: '/web' }); - addLocaleData([ ...en, ...de, @@ -82,9 +81,9 @@ addLocaleData([ ...uk, ...zh, ...zh_hk, + ...bg, ]); - const Mastodon = React.createClass({ propTypes: { diff --git a/app/assets/javascripts/components/features/community_timeline/index.jsx b/app/assets/javascripts/components/features/community_timeline/index.jsx index 0957338cf..acfc30b65 100644 --- a/app/assets/javascripts/components/features/community_timeline/index.jsx +++ b/app/assets/javascripts/components/features/community_timeline/index.jsx @@ -14,7 +14,7 @@ import ColumnBackButtonSlim from '../../components/column_back_button_slim'; import createStream from '../../stream'; const messages = defineMessages({ - title: { id: 'column.community', defaultMessage: 'Local' } + title: { id: 'column.community', defaultMessage: 'Local timeline' } }); const mapStateToProps = state => ({ diff --git a/app/assets/javascripts/components/features/compose/components/compose_form.jsx b/app/assets/javascripts/components/features/compose/components/compose_form.jsx index cb4b62f6c..d2e65359f 100644 --- a/app/assets/javascripts/components/features/compose/components/compose_form.jsx +++ b/app/assets/javascripts/components/features/compose/components/compose_form.jsx @@ -19,7 +19,7 @@ import TextIconButton from './text_icon_button'; const messages = defineMessages({ placeholder: { id: 'compose_form.placeholder', defaultMessage: 'What is on your mind?' }, spoiler_placeholder: { id: 'compose_form.spoiler_placeholder', defaultMessage: 'Content warning' }, - publish: { id: 'compose_form.publish', defaultMessage: 'Publish' } + publish: { id: 'compose_form.publish', defaultMessage: 'Toot' } }); const ComposeForm = React.createClass({ diff --git a/app/assets/javascripts/components/features/compose/index.jsx b/app/assets/javascripts/components/features/compose/index.jsx index 9421de3ff..33e16472c 100644 --- a/app/assets/javascripts/components/features/compose/index.jsx +++ b/app/assets/javascripts/components/features/compose/index.jsx @@ -12,7 +12,7 @@ import SearchResultsContainer from './containers/search_results_container'; const messages = defineMessages({ start: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, - public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Whole Known Network' }, + public: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' }, community: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, logout: { id: 'navigation_bar.logout', defaultMessage: 'Logout' } diff --git a/app/assets/javascripts/components/features/getting_started/index.jsx b/app/assets/javascripts/components/features/getting_started/index.jsx index 0656bf69a..05bfcc221 100644 --- a/app/assets/javascripts/components/features/getting_started/index.jsx +++ b/app/assets/javascripts/components/features/getting_started/index.jsx @@ -7,11 +7,11 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; const messages = defineMessages({ heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' }, - public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Whole Known Network' }, + public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' }, community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' }, preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' }, follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' }, - sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Sign out' }, + sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' }, favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' }, blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' }, info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' } diff --git a/app/assets/javascripts/components/features/public_timeline/index.jsx b/app/assets/javascripts/components/features/public_timeline/index.jsx index 6d766a83b..a7ac95ab4 100644 --- a/app/assets/javascripts/components/features/public_timeline/index.jsx +++ b/app/assets/javascripts/components/features/public_timeline/index.jsx @@ -14,7 +14,7 @@ import ColumnBackButtonSlim from '../../components/column_back_button_slim'; import createStream from '../../stream'; const messages = defineMessages({ - title: { id: 'column.public', defaultMessage: 'Whole Known Network' } + title: { id: 'column.public', defaultMessage: 'Federated timeline' } }); const mapStateToProps = state => ({ diff --git a/app/assets/javascripts/components/locales/bg.jsx b/app/assets/javascripts/components/locales/bg.jsx new file mode 100644 index 000000000..cac984aae --- /dev/null +++ b/app/assets/javascripts/components/locales/bg.jsx @@ -0,0 +1,68 @@ +const bg = { + "column_back_button.label": "Назад", + "lightbox.close": "Затвори", + "loading_indicator.label": "Зареждане...", + "status.mention": "Споменаване", + "status.delete": "Изтриване", + "status.reply": "Отговор", + "status.reblog": "Споделяне", + "status.favourite": "Предпочитани", + "status.reblogged_by": "{name} сподели", + "status.sensitive_warning": "Деликатно съдържание", + "status.sensitive_toggle": "Покажи", + "video_player.toggle_sound": "Звук", + "account.mention": "Споменаване", + "account.edit_profile": "Редактирай профила си", + "account.unblock": "Не блокирай", + "account.unfollow": "Не следвай", + "account.block": "Блокирай", + "account.follow": "Последвай", + "account.posts": "Публикации", + "account.follows": "Следвам", + "account.followers": "Последователи", + "account.follows_you": "Твой последовател", + "account.requested": "В очакване на одобрение", + "getting_started.heading": "Първи стъпки", + "getting_started.about_addressing": "Можеш да последваш потребител, ако знаеш потребителското му име и домейна, на който се намира, като в полето за търсене ги въведеш по този начин: име@домейн", + "getting_started.about_shortcuts": "Ако с търсения потребител се намирате на един и същ домейн, достатъчно е да въведеш само името. Същото важи и за споменаване на хора в публикации.", + "getting_started.about_developer": "Можеш да потърсиш разработчика на този проект като: Gargron@mastodon.social", + "getting_started.open_source_notice": "Mastodon е софтуер с отворен код. Можеш да помогнеш или да докладваш за проблеми в Github: {github}.", + "column.home": "Начало", + "column.mentions": "Споменавания", + "column.public": "Публичен канал", + "column.notifications": "Известия", + "tabs_bar.compose": "Съставяне", + "tabs_bar.home": "Начало", + "tabs_bar.mentions": "Споменавания", + "tabs_bar.public": "Публичен канал", + "tabs_bar.notifications": "Известия", + "compose_form.placeholder": "Какво си мислиш?", + "compose_form.publish": "Раздумай", + "compose_form.sensitive": "Отбележи съдържанието като деликатно", + "compose_form.spoiler": "Скрий текста зад предупреждение", + "compose_form.private": "Отбележи като поверително", + "compose_form.privacy_disclaimer": "Поверителни публикации ще бъдат изпратени до споменатите потребители на {domains}. Доверяваш ли се на {domainsCount, plural, one {that server} other {those servers}}, че няма да издаде твоята публикация?", + "compose_form.unlisted": "Не показвай в публичния канал", + "navigation_bar.edit_profile": "Редактирай профил", + "navigation_bar.preferences": "Предпочитания", + "navigation_bar.public_timeline": "Публичен канал", + "navigation_bar.logout": "Излизане", + "reply_indicator.cancel": "Отказ", + "search.placeholder": "Търсене", + "search.account": "Акаунт", + "search.hashtag": "Хаштаг", + "upload_button.label": "Добави медия", + "upload_form.undo": "Отмяна", + "notification.follow": "{name} те последва", + "notification.favourite": "{name} хареса твоята публикация", + "notification.reblog": "{name} сподели твоята публикация", + "notification.mention": "{name} те спомена", + "notifications.column_settings.alert": "Десктоп известия", + "notifications.column_settings.show": "Покажи в колона", + "notifications.column_settings.follow": "Нови последователи:", + "notifications.column_settings.favourite": "Предпочитани:", + "notifications.column_settings.mention": "Споменавания:", + "notifications.column_settings.reblog": "Споделяния:", +}; + +export default en; diff --git a/app/assets/javascripts/components/locales/en.jsx b/app/assets/javascripts/components/locales/en.jsx index f249b1967..1834567f1 100644 --- a/app/assets/javascripts/components/locales/en.jsx +++ b/app/assets/javascripts/components/locales/en.jsx @@ -1,72 +1,129 @@ +/** + * Note for Contributors: + * This file (en.jsx) serve as a template for other languages. + * To make other contributors' life easier, please REMEMBER: + * 1. to add your new string here; and + * 2. to remove old strings that are no longer needed; and + * 3. to sort the strings by the key. + * Thanks! + */ const en = { - "column_back_button.label": "Back", - "lightbox.close": "Close", - "loading_indicator.label": "Loading...", - "status.mention": "Mention @{name}", - "status.delete": "Delete", - "status.reply": "Reply", - "status.reblog": "Boost", - "status.favourite": "Favourite", - "status.reblogged_by": "{name} boosted", - "status.sensitive_warning": "Sensitive content", - "status.sensitive_toggle": "Click to view", - "status.show_more": "Show more", - "status.show_less": "Show less", - "status.open": "Expand this status", - "status.report": "Report @{name}", - "video_player.toggle_sound": "Toggle sound", - "account.mention": "Mention @{name}", - "account.edit_profile": "Edit profile", - "account.unblock": "Unblock @{name}", - "account.unfollow": "Unfollow", "account.block": "Block @{name}", + "account.disclaimer": "This user is from another instance. This number may be larger.", + "account.edit_profile": "Edit profile", "account.follow": "Follow", - "account.posts": "Posts", - "account.follows": "Follows", "account.followers": "Followers", "account.follows_you": "Follows you", + "account.follows": "Follows", + "account.mention": "Mention @{name}", + "account.mute": "Mute @{name}", + "account.posts": "Posts", + "account.report": "Report @{name}", "account.requested": "Awaiting approval", - "getting_started.heading": "Getting started", - "getting_started.about_addressing": "You can follow people if you know their username and the domain they are on by entering an e-mail-esque address into the search form.", - "getting_started.about_shortcuts": "If the target user is on the same domain as you, just the username will work. The same rule applies to mentioning people in statuses.", - "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.", - "column.home": "Home", + "account.unblock": "Unblock @{name}", + "account.unfollow": "Unfollow", + "account.unmute": "Unmute @{name}", + "boost_modal.combo": "You can press {combo} to skip this next time", + "column_back_button.label": "Back", + "column.blocks": "Blocked users", "column.community": "Local timeline", - "column.public": "Federated timeline", + "column.favourites": "Favourites", + "column.follow_requests": "Follow requests", + "column.home": "Home", "column.notifications": "Notifications", - "tabs_bar.compose": "Compose", - "tabs_bar.home": "Home", - "tabs_bar.mentions": "Mentions", - "tabs_bar.public": "Federated timeline", - "tabs_bar.notifications": "Notifications", + "column.public": "Federated timeline", "compose_form.placeholder": "What is on your mind?", + "compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.", "compose_form.publish": "Toot", "compose_form.sensitive": "Mark media as sensitive", + "compose_form.spoiler_placeholder": "Content warning", "compose_form.spoiler": "Hide text behind warning", - "compose_form.private": "Mark as private", - "compose_form.privacy_disclaimer": "Your private status will be delivered to mentioned users on {domains}. Do you trust {domainsCount, plural, one {that server} other {those servers}}? Post privacy only works on Mastodon instances. If {domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}, there will be no indication that your post is private, and it may be boosted or otherwise made visible to unintended recipients.", - "compose_form.unlisted": "Do not display on public timelines", + "emoji_button.label": "Insert emoji", + "empty_column.community": "The local timeline is empty. Write something publicly to get the ball rolling!", + "empty_column.hashtag": "There is nothing in this hashtag yet.", + "empty_column.home.public_timeline": "the public timeline", + "empty_column.home": "You aren't following anyone yet. Visit {public} or use search to get started and meet other users.", + "empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.", + "empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up", + "follow_request.authorize": "Authorize", + "follow_request.reject": "Rejec", + "getting_started.apps": "Various apps are available", + "getting_started.heading": "Getting started", + "getting_started.open_source_notice": "Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.", + "home.column_settings.advanced": "Advanced", + "home.column_settings.basic": "Basic", + "home.column_settings.filter_regex": "Filter out by regular expressions", + "home.column_settings.show_reblogs": "Show boosts", + "home.column_settings.show_replies": "Show replies", + "home.settings": "Column settings", + "lightbox.close": "Close", + "loading_indicator.label": "Loading...", + "media_gallery.toggle_visible": "Toggle visibility", + "missing_indicator.label": "Not found", + "navigation_bar.blocks": "Blocked users", + "navigation_bar.community_timeline": "Local timeline", "navigation_bar.edit_profile": "Edit profile", + "navigation_bar.favourites": "Favourites", + "navigation_bar.follow_requests": "Follow requests", + "navigation_bar.info": "Extended information", + "navigation_bar.logout": "Logout", "navigation_bar.preferences": "Preferences", - "navigation_bar.community_timeline": "Local timeline", "navigation_bar.public_timeline": "Federated timeline", - "navigation_bar.logout": "Logout", - "reply_indicator.cancel": "Cancel", - "search.placeholder": "Search", - "search.account": "Account", - "search.hashtag": "Hashtag", - "upload_button.label": "Add media", - "upload_form.undo": "Undo", - "notification.follow": "{name} followed you", "notification.favourite": "{name} favourited your status", + "notification.follow": "{name} followed you", "notification.reblog": "{name} boosted your status", - "notification.mention": "{name} mentioned you", + "notifications.clear_confirmation": "Are you sure you want to clear all your notifications?", + "notifications.clear": "Clear notifications", "notifications.column_settings.alert": "Desktop notifications", - "notifications.column_settings.show": "Show in column", - "notifications.column_settings.follow": "New followers:", "notifications.column_settings.favourite": "Favourites:", + "notifications.column_settings.follow": "New followers:", "notifications.column_settings.mention": "Mentions:", "notifications.column_settings.reblog": "Boosts:", + "notifications.column_settings.show": "Show in column", + "notifications.column_settings.sound": "Play sound", + "notifications.settings": "Column settings", + "privacy.change": "Adjust status privacy", + "privacy.direct.long": "Post to mentioned users only", + "privacy.direct.short": "Direct", + "privacy.private.long": "Post to followers only", + "privacy.private.short": "Private", + "privacy.public.long": "Post to public timelines", + "privacy.public.short": "Public", + "privacy.unlisted.long": "Do not show in public timelines", + "privacy.unlisted.short": "Unlisted", + "reply_indicator.cancel": "Cancel", + "report.heading": "New report", + "report.placeholder": "Additional comments", + "report.submit": "Submit", + "report.target": "Reporting", + "search_results.total": "{count} {count, plural, one {result} other {results}}", + "search.placeholder": "Search", + "search.status_by": "Status by {name}", + "status.delete": "Delete", + "status.favourite": "Favourite", + "status.load_more": "Load more", + "status.media_hidden": "Media hidden", + "status.mention": "Mention @{name}", + "status.open": "Expand this status", + "status.reblog": "Boost", + "status.reblogged_by": "{name} boosted", + "status.reply": "Reply", + "status.report": "Report @{name}", + "status.sensitive_toggle": "Click to view", + "status.sensitive_warning": "Sensitive content", + "status.show_less": "Show less", + "status.show_more": "Show more", + "tabs_bar.compose": "Compose", + "tabs_bar.federated_timeline": "Federated", + "tabs_bar.home": "Home", + "tabs_bar.local_timeline": "Local", + "tabs_bar.notifications": "Notifications", + "upload_area.title": "Drag & drop to upload", + "upload_button.label": "Add media", + "upload_form.undo": "Undo", + "upload_progress.label": "Uploading...", + "video_player.toggle_sound": "Toggle sound", + "video_player.toggle_visible": "Toggle visibility", }; export default en; diff --git a/app/assets/javascripts/components/locales/index.jsx b/app/assets/javascripts/components/locales/index.jsx index e772c1074..f14568a3d 100644 --- a/app/assets/javascripts/components/locales/index.jsx +++ b/app/assets/javascripts/components/locales/index.jsx @@ -11,7 +11,7 @@ import eo from './eo'; import ru from './ru'; import ja from './ja'; import zh_hk from './zh-hk'; - +import bg from './bg'; const locales = { en, @@ -27,6 +27,7 @@ const locales = { ru, ja, 'zh-HK': zh_hk, + bg, }; export default function getMessagesForLocale (locale) { diff --git a/app/assets/javascripts/components/locales/ja.jsx b/app/assets/javascripts/components/locales/ja.jsx index 25a6f7f67..fdfc91c29 100644 --- a/app/assets/javascripts/components/locales/ja.jsx +++ b/app/assets/javascripts/components/locales/ja.jsx @@ -39,8 +39,8 @@ const ja = { "tabs_bar.compose": "投稿", "tabs_bar.home": "ホーム", "tabs_bar.mentions": "返信", - "tabs_bar.local_timeline": "ローカルTL", - "tabs_bar.federated_timeline": "連合TL", + "tabs_bar.local_timeline": "ローカル", + "tabs_bar.federated_timeline": "連合", "tabs_bar.notifications": "通知", "compose_form.placeholder": "今なにしてる?", "compose_form.publish": "トゥート", diff --git a/app/assets/javascripts/components/locales/pt.jsx b/app/assets/javascripts/components/locales/pt.jsx index 8d1b88c75..cd345a585 100644 --- a/app/assets/javascripts/components/locales/pt.jsx +++ b/app/assets/javascripts/components/locales/pt.jsx @@ -14,59 +14,115 @@ const pt = { "status.show_less": "Mostrar menos", "status.open": "Expandir", "status.report": "Reportar @{name}", + "status.load_more": "Carregar mais", + "status.media_hidden": "Media escondida", "video_player.toggle_sound": "Ligar/Desligar som", + "video_player.toggle_visible": "Ligar/Desligar vídeo", "account.mention": "Mencionar @{name}", "account.edit_profile": "Editar perfil", "account.unblock": "Não bloquear @{name}", "account.unfollow": "Não seguir", "account.block": "Bloquear @{name}", + "account.mute": "Mute", + "account.unmute": "Remover Mute", "account.follow": "Seguir", "account.posts": "Posts", "account.follows": "Segue", "account.followers": "Seguidores", "account.follows_you": "É teu seguidor", "account.requested": "A aguardar aprovação", + "account.report": "Denunciar", + "account.disclaimer": "Essa conta está localizado em outra instância. Os nomes podem ser maiores.", "getting_started.heading": "Primeiros passos", "getting_started.about_addressing": "Podes seguir pessoas se sabes o nome de usuário deles e o domínio em que estão colocando um endereço similar a e-mail no campo no topo da barra lateral.", "getting_started.about_shortcuts": "Se o usuário alvo está no mesmo domínio, só o nome funcionará. A mesma regra se aplica a mencionar pessoas nas postagens.", + "getting_started.about_developer": "Pode seguir o developer deste projecto em Gargron@mastodon.social", "getting_started.open_source_notice": "Mastodon é software de fonte aberta. Podes contribuir ou repostar problemas no GitHub do projecto: {github}. {apps}.", "column.home": "Home", "column.community": "Local", - "column.public": "Público", + "column.public": "Global", "column.notifications": "Notificações", + "column.blocks": "Utilizadores Bloqueados", + "column.favourites": "Favoritos", + "column.follow_requests": "Seguidores Pendentes", + "empty_column.notifications": "Não tens notificações. Interage com outros utilizadores para iniciar uma conversa.", + "empty_column.public": "Não há nada aqui! Escreve algo publicamente ou segue outros utilizadores para ver aqui os conteúdos públicos.", + "empty_column.home": "Ainda não segues qualquer utilizador. Visita {public} ou utiliza a pesquisa para procurar outros utilizadores.", + "empty_column.home.public_timeline": "global", + "empty_column.community": "Ainda não existem conteúdo local para mostrar!", + "empty_column.hashtag": "Não existe qualquer conteúdo com essa hashtag", "tabs_bar.compose": "Criar", "tabs_bar.home": "Home", "tabs_bar.mentions": "Menções", "tabs_bar.public": "Público", "tabs_bar.notifications": "Notificações", + "tabs_bar.local_timeline": "Local", + "tabs_bar.federated_timeline": "Global", "compose_form.placeholder": "Em que estás a pensar?", "compose_form.publish": "Publicar", - "compose_form.sensitive": "Media com conteúdo sensível", + "compose_form.sensitive": "Marcar media como conteúdo sensível", "compose_form.spoiler": "Esconder texto com aviso", + "compose_form.spoiler_placeholder": "Aviso", "compose_form.private": "Tornar privado", "compose_form.privacy_disclaimer": "O teu conteúdo privado vai ser partilhado com os utilizadores do {domains}. Confias {domainsCount, plural, one {neste servidor} other {nestes servidores}}? A privacidade só funciona em instâncias do Mastodon. Se {domains} {domainsCount, plural, one {não é uma instância} other {não são instâncias}}, não existem indicadores da privacidade da tua partilha, e podem ser partilhados com outros.", "compose_form.unlisted": "Não mostrar na listagem pública", + "emoji_button.label": "Inserir Emoji", "navigation_bar.edit_profile": "Editar perfil", "navigation_bar.preferences": "Preferências", "navigation_bar.community_timeline": "Local", - "navigation_bar.public_timeline": "Público", + "navigation_bar.public_timeline": "Global", + "navigation_bar.blocks": "Utilizadores bloqueados", + "navigation_bar.favourites": "Favoritos", + "navigation_bar.info": "Mais informações", "navigation_bar.logout": "Sair", + "navigation_bar.follow_requests": "Seguidores pendentes", "reply_indicator.cancel": "Cancelar", "search.placeholder": "Pesquisar", "search.account": "Conta", "search.hashtag": "Hashtag", + "search_results.total": "{count} {count, plural, one {resultado} other {resultados}}", + "search.status_by": "Post de {name}", "upload_button.label": "Adicionar media", "upload_form.undo": "Anular", + "upload_progress.label": "A gravar…", + "upload_area.title": "Arraste e solte para enviar", "notification.follow": "{name} seguiu-te", "notification.favourite": "{name} adicionou o teu post aos favoritos", "notification.reblog": "{name} partilhou o teu post", "notification.mention": "{name} mencionou-te", "notifications.column_settings.alert": "Notificações no computador", "notifications.column_settings.show": "Mostrar nas colunas", + "notifications.column_settings.sound": "Reproduzir som", "notifications.column_settings.follow": "Novos seguidores:", "notifications.column_settings.favourite": "Favoritos:", "notifications.column_settings.mention": "Menções:", "notifications.column_settings.reblog": "Partilhas:", + "notifications.clear": "Limpar notificações", + "notifications.clear_confirmation": "Queres mesmo limpar todas as notificações?", + "notifications.settings": "Parâmetros da lista de Notificações", + "privacy.public.short": "Público", + "privacy.public.long": "Publicar em todos os feeds", + "privacy.unlisted.short": "Não listar", + "privacy.unlisted.long": "Não publicar nos feeds públicos", + "privacy.private.short": "Privado", + "privacy.private.long": "Apenas para os seguidores", + "privacy.direct.short": "Directo", + "privacy.direct.long": "Apenas para utilizadores mencionados", + "privacy.change": "Ajustar a privacidade da mensagem", + "media_gallery.toggle_visible": "Modificar a visibilidade", + "missing_indicator.label": "Não encontrado", + "follow_request.authorize": "Autorizar", + "follow_request.reject": "Rejeitar", + "home.settings": "Parâmetros da coluna Home", + "home.column_settings.basic": "Básico", + "home.column_settings.show_reblogs": "Mostrar as partilhas", + "home.column_settings.show_replies": "Mostrar as respostas", + "home.column_settings.advanced": "Avançadas", + "home.column_settings.filter_regex": "Filtrar com uma expressão regular", + "report.heading": "Nova denuncia", + "report.placeholder": "Comentários adicionais", + "report.submit": "Enviar", + "report.target": "Denunciar" }; export default pt; diff --git a/app/assets/javascripts/components/locales/ru.jsx b/app/assets/javascripts/components/locales/ru.jsx index e109005a7..30a92df86 100644 --- a/app/assets/javascripts/components/locales/ru.jsx +++ b/app/assets/javascripts/components/locales/ru.jsx @@ -10,22 +10,29 @@ const ru = { "status.reblogged_by": "{name} продвинул(а)", "status.sensitive_warning": "Чувствительный контент", "status.sensitive_toggle": "Нажмите для просмотра", + "status.show_more": "Развернуть", + "status.show_less": "Свернуть", + "status.open": "Развернуть статус", + "status.report": "Пожаловаться", + "status.load_more": "Показать еще", "video_player.toggle_sound": "Вкл./выкл. звук", - "account.mention": "Упомянуть @{name}", + "account.mention": "Упомянуть", "account.edit_profile": "Изменить профиль", - "account.unblock": "Разблокировать @{name}", + "account.unblock": "Разблокировать", "account.unfollow": "Отписаться", - "account.block": "Блокировать @{name}", + "account.block": "Блокировать", + "account.mute": "Заглушить", "account.follow": "Подписаться", "account.posts": "Посты", "account.follows": "Подписки", - "account.followers": "Подписчики", + "account.followers": "Подписаны", "account.follows_you": "Подписан(а) на Вас", "account.requested": "Ожидает подтверждения", "getting_started.heading": "Добро пожаловать", "getting_started.about_addressing": "Вы можете подписаться на человека, зная имя пользователя и домен, на котором он находится, введя e-mail-подобный адрес в форму поиска.", "getting_started.about_shortcuts": "Если пользователь находится на одном с Вами домене, можно использовать только имя. То же правило применимо к упоминанию пользователей в статусах.", "getting_started.open_source_notice": "Mastodon - программа с открытым исходным кодом. Вы можете помочь проекту или сообщить о проблемах на GitHub по адресу {github}. {apps}.", + "getting_started.apps": "Доступны различные приложения.", "column.home": "Главная", "column.community": "Локальная лента", "column.public": "Глобальная лента", @@ -36,7 +43,7 @@ const ru = { "tabs_bar.public": "Глобальная лента", "tabs_bar.notifications": "Уведомления", "compose_form.placeholder": "О чем Вы думаете?", - "compose_form.publish": "Протрубить", + "compose_form.publish": "Трубить", "compose_form.sensitive": "Отметить как чувствительный контент", "compose_form.spoiler": "Скрыть текст за предупреждением", "compose_form.private": "Отметить как приватное", @@ -47,6 +54,9 @@ const ru = { "navigation_bar.community_timeline": "Локальная лента", "navigation_bar.public_timeline": "Глобальная лента", "navigation_bar.logout": "Выйти", + "navigation_bar.info": "Об узле", + "navigation_bar.favourites": "Понравившееся", + "navigation_bar.blocks": "Список блокировки", "reply_indicator.cancel": "Отмена", "search.placeholder": "Поиск", "search.account": "Аккаунт", @@ -57,12 +67,35 @@ const ru = { "notification.favourite": "{name} понравился Ваш статус", "notification.reblog": "{name} продвинул(а) Ваш статус", "notification.mention": "{name} упомянул(а) Вас", + "home.settings": "Настройки колонки", + "home.column_settings.basic": "Основные", + "home.column_settings.advanced": "Дополнительные", + "home.column_settings.filter_regex": "Отфильтровать регулярным выражением", + "home.column_settings.show_replies": "Показывать продвижения", + "home.column_settings.show_replies": "Показывать ответы", + "notifications.clear": "Очистить уведомления", + "notifications.settings": "Настройки колонки", "notifications.column_settings.alert": "Десктопные уведомления", "notifications.column_settings.show": "Показывать в колонке", "notifications.column_settings.follow": "Новые подписчики:", "notifications.column_settings.favourite": "Нравится:", "notifications.column_settings.mention": "Упоминания:", "notifications.column_settings.reblog": "Продвижения:", + "notifications.column_settings.sound": "Проигрывать звук", + "empty_column.notifications": "У Вас еще нет уведомлений. Заведите знакомство с другими пользователями, чтобы начать разговор.", + "empty_column.hashtag": "Статусов с таким хэштегом еще не существует.", + "empty_column.community": "Локальная лента пуста. Напишите что-нибудь, чтобы разогреть народ!", + "empty_column.public": "Здесь ничего нет! Опубликуйте что-нибудь или подпишитесь на пользователей с других узлов, чтобы заполнить ленту.", + "empty_column.home": "Пока Вы ни на кого не подписаны. Полистайте {public} или используйте поиск, чтобы освоиться и завести новые знакомства.", + "empty_column.home.public_timeline": "публичные ленты", + "privacy.public.short": "Публичный", + "privacy.public.long": "Показать в публичных лентах", + "privacy.unlisted.short": "Скрытый", + "privacy.unlisted.long": "Не показывать в лентах", + "privacy.private.short": "Приватный", + "privacy.private.long": "Показать только подписчикам", + "privacy.direct.short": "Направленный", + "privacy.direct.long": "Показать только упомянутым", }; export default ru; diff --git a/app/assets/stylesheets/forms.scss b/app/assets/stylesheets/forms.scss index 2e3a4f147..e5e8697a0 100644 --- a/app/assets/stylesheets/forms.scss +++ b/app/assets/stylesheets/forms.scss @@ -88,7 +88,7 @@ code { } } - input[type=text], input[type=email], input[type=password], textarea { + input[type=text], input[type=number], input[type=email], input[type=password], textarea { background: transparent; box-sizing: border-box; border: 0; diff --git a/app/controllers/admin/accounts_controller.rb b/app/controllers/admin/accounts_controller.rb index 71cb8edd8..0e9e52f42 100644 --- a/app/controllers/admin/accounts_controller.rb +++ b/app/controllers/admin/accounts_controller.rb @@ -2,49 +2,29 @@ module Admin class AccountsController < BaseController - before_action :set_account, except: :index - def index - @accounts = Account.alphabetic.page(params[:page]) - - @accounts = @accounts.local if params[:local].present? - @accounts = @accounts.remote if params[:remote].present? - @accounts = @accounts.where(domain: params[:by_domain]) if params[:by_domain].present? - @accounts = @accounts.silenced if params[:silenced].present? - @accounts = @accounts.recent if params[:recent].present? - @accounts = @accounts.suspended if params[:suspended].present? - end - - def show; end - - def suspend - Admin::SuspensionWorker.perform_async(@account.id) - redirect_to admin_accounts_path + @accounts = filtered_accounts.page(params[:page]) end - def unsuspend - @account.update(suspended: false) - redirect_to admin_accounts_path - end - - def silence - @account.update(silenced: true) - redirect_to admin_accounts_path - end - - def unsilence - @account.update(silenced: false) - redirect_to admin_accounts_path + def show + @account = Account.find(params[:id]) end private - def set_account - @account = Account.find(params[:id]) + def filtered_accounts + AccountFilter.new(filter_params).results end - def account_params - params.require(:account).permit(:silenced, :suspended) + def filter_params + params.permit( + :local, + :remote, + :by_domain, + :silenced, + :recent, + :suspended + ) end end end diff --git a/app/controllers/admin/silences_controller.rb b/app/controllers/admin/silences_controller.rb new file mode 100644 index 000000000..81a3008b9 --- /dev/null +++ b/app/controllers/admin/silences_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Admin + class SilencesController < BaseController + before_action :set_account + + def create + @account.update(silenced: true) + redirect_to admin_accounts_path + end + + def destroy + @account.update(silenced: false) + redirect_to admin_accounts_path + end + + private + + def set_account + @account = Account.find(params[:account_id]) + end + end +end diff --git a/app/controllers/admin/suspensions_controller.rb b/app/controllers/admin/suspensions_controller.rb new file mode 100644 index 000000000..5d9048d94 --- /dev/null +++ b/app/controllers/admin/suspensions_controller.rb @@ -0,0 +1,23 @@ +# frozen_string_literal: true + +module Admin + class SuspensionsController < BaseController + before_action :set_account + + def create + Admin::SuspensionWorker.perform_async(@account.id) + redirect_to admin_accounts_path + end + + def destroy + @account.update(suspended: false) + redirect_to admin_accounts_path + end + + private + + def set_account + @account = Account.find(params[:account_id]) + end + end +end diff --git a/app/controllers/settings/exports/base_controller.rb b/app/controllers/settings/exports/base_controller.rb index 0b790959f..c082ed806 100644 --- a/app/controllers/settings/exports/base_controller.rb +++ b/app/controllers/settings/exports/base_controller.rb @@ -6,7 +6,7 @@ module Settings before_action :authenticate_user! def index - export_data = Export.new(export_accounts).to_csv + @export = Export.new(current_account) respond_to do |format| format.csv { send_data export_data, filename: export_filename } diff --git a/app/controllers/settings/exports/blocked_accounts_controller.rb b/app/controllers/settings/exports/blocked_accounts_controller.rb index 9c4bcaa53..f1115b21e 100644 --- a/app/controllers/settings/exports/blocked_accounts_controller.rb +++ b/app/controllers/settings/exports/blocked_accounts_controller.rb @@ -5,8 +5,8 @@ module Settings class BlockedAccountsController < BaseController private - def export_accounts - current_account.blocking + def export_data + @export.to_blocked_accounts_csv end end end diff --git a/app/controllers/settings/exports/following_accounts_controller.rb b/app/controllers/settings/exports/following_accounts_controller.rb index 8d06bcc95..0011d2463 100644 --- a/app/controllers/settings/exports/following_accounts_controller.rb +++ b/app/controllers/settings/exports/following_accounts_controller.rb @@ -5,8 +5,8 @@ module Settings class FollowingAccountsController < BaseController private - def export_accounts - current_account.following + def export_data + @export.to_following_accounts_csv end end end diff --git a/app/controllers/settings/exports/muted_accounts_controller.rb b/app/controllers/settings/exports/muted_accounts_controller.rb index a77a9af6d..dfe72cfcb 100644 --- a/app/controllers/settings/exports/muted_accounts_controller.rb +++ b/app/controllers/settings/exports/muted_accounts_controller.rb @@ -5,8 +5,8 @@ module Settings class MutedAccountsController < BaseController private - def export_accounts - current_account.muting + def export_data + @export.to_muted_accounts_csv end end end diff --git a/app/controllers/settings/exports_controller.rb b/app/controllers/settings/exports_controller.rb index 77dea3231..ae62f00c1 100644 --- a/app/controllers/settings/exports_controller.rb +++ b/app/controllers/settings/exports_controller.rb @@ -6,9 +6,6 @@ class Settings::ExportsController < ApplicationController before_action :authenticate_user! def show - @total_storage = current_account.media_attachments.sum(:file_file_size) - @total_follows = current_account.following.count - @total_blocks = current_account.blocking.count - @total_mutes = current_account.muting.count + @export = Export.new(current_account) end end diff --git a/app/controllers/well_known/host_meta_controller.rb b/app/controllers/well_known/host_meta_controller.rb new file mode 100644 index 000000000..2f0960acd --- /dev/null +++ b/app/controllers/well_known/host_meta_controller.rb @@ -0,0 +1,13 @@ + # frozen_string_literal: true + +module WellKnown + class HostMetaController < ApplicationController + def show + @webfinger_template = "#{webfinger_url}?resource={uri}" + + respond_to do |format| + format.xml { render content_type: 'application/xrd+xml' } + end + end + end +end diff --git a/app/controllers/well_known/webfinger_controller.rb b/app/controllers/well_known/webfinger_controller.rb new file mode 100644 index 000000000..1a8ef5f90 --- /dev/null +++ b/app/controllers/well_known/webfinger_controller.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +module WellKnown + class WebfingerController < ApplicationController + def show + @account = Account.find_local!(username_from_resource) + @canonical_account_uri = @account.to_webfinger_s + @magic_key = pem_to_magic_key(@account.keypair.public_key) + + respond_to do |format| + format.xml { render content_type: 'application/xrd+xml' } + format.json { render content_type: 'application/jrd+json' } + end + rescue ActiveRecord::RecordNotFound + head 404 + end + + private + + def username_from_resource + WebfingerResource.new(resource_param).username + end + + def pem_to_magic_key(public_key) + modulus, exponent = [public_key.n, public_key.e].map do |component| + result = [] + + until component.zero? + result << [component % 256].pack('C') + component >>= 8 + end + + result.reverse.join + end + + (['RSA'] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.') + end + + def resource_param + params.require(:resource) + end + end +end diff --git a/app/controllers/xrd_controller.rb b/app/controllers/xrd_controller.rb deleted file mode 100644 index 2886315ac..000000000 --- a/app/controllers/xrd_controller.rb +++ /dev/null @@ -1,55 +0,0 @@ -# frozen_string_literal: true - -class XrdController < ApplicationController - before_action :set_default_format_xml, only: :host_meta - - def host_meta - @webfinger_template = "#{webfinger_url}?resource={uri}" - - respond_to do |format| - format.xml { render content_type: 'application/xrd+xml' } - end - end - - def webfinger - @account = Account.find_local!(username_from_resource) - @canonical_account_uri = @account.to_webfinger_s - @magic_key = pem_to_magic_key(@account.keypair.public_key) - - respond_to do |format| - format.xml { render content_type: 'application/xrd+xml' } - format.json { render content_type: 'application/jrd+json' } - end - rescue ActiveRecord::RecordNotFound - head 404 - end - - private - - def set_default_format_xml - request.format = 'xml' if request.headers['HTTP_ACCEPT'].nil? && params[:format].nil? - end - - def username_from_resource - WebfingerResource.new(resource_param).username - end - - def pem_to_magic_key(public_key) - modulus, exponent = [public_key.n, public_key.e].map do |component| - result = [] - - until component.zero? - result << [component % 256].pack('C') - component >>= 8 - end - - result.reverse.join - end - - (['RSA'] + [modulus, exponent].map { |n| Base64.urlsafe_encode64(n) }).join('.') - end - - def resource_param - params.require(:resource) - end -end diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 211b57042..212f88c39 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -16,6 +16,7 @@ module SettingsHelper ja: '日本語', 'zh-CN': '简体中文', 'zh-HK': '繁體中文(香港)', + bg: 'Български', }.freeze def human_locale(locale) diff --git a/app/models/account_filter.rb b/app/models/account_filter.rb new file mode 100644 index 000000000..a8d8c8837 --- /dev/null +++ b/app/models/account_filter.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class AccountFilter + attr_reader :params + + def initialize(params) + @params = params + end + + def results + scope = Account.alphabetic + params.each do |key, value| + scope = scope.merge scope_for(key, value) + end + scope + end + + def scope_for(key, value) + case key + when /local/ + Account.local + when /remote/ + Account.remote + when /by_domain/ + Account.where(domain: value) + when /silenced/ + Account.silenced + when /recent/ + Account.recent + when /suspended/ + Account.suspended + else + raise "Unknown filter: #{key}" + end + end +end diff --git a/app/models/export.rb b/app/models/export.rb index cd1a58eb6..f0d5dd255 100644 --- a/app/models/export.rb +++ b/app/models/export.rb @@ -2,13 +2,43 @@ require 'csv' class Export - attr_reader :accounts + attr_reader :account - def initialize(accounts) - @accounts = accounts + def initialize(account) + @account = account end - def to_csv + def to_blocked_accounts_csv + to_csv account.blocking + end + + def to_muted_accounts_csv + to_csv account.muting + end + + def to_following_accounts_csv + to_csv account.following + end + + def total_storage + account.media_attachments.sum(:file_file_size) + end + + def total_follows + account.following.count + end + + def total_blocks + account.blocking.count + end + + def total_mutes + account.muting.count + end + + private + + def to_csv(accounts) CSV.generate do |csv| accounts.each do |account| csv << [(account.local? ? account.local_username_and_domain : account.acct)] diff --git a/app/views/admin/accounts/show.html.haml b/app/views/admin/accounts/show.html.haml index ba1c3bae7..22901aed1 100644 --- a/app/views/admin/accounts/show.html.haml +++ b/app/views/admin/accounts/show.html.haml @@ -62,11 +62,11 @@ = number_to_human_size @account.media_attachments.sum('file_file_size') - if @account.silenced? - = link_to 'Undo silence', unsilence_admin_account_path(@account.id), method: :post, class: 'button' + = link_to 'Undo silence', admin_account_silence_path(@account.id), method: :delete, class: 'button' - else - = link_to 'Silence', silence_admin_account_path(@account.id), method: :post, class: 'button' + = link_to 'Silence', admin_account_silence_path(@account.id), method: :post, class: 'button' - if @account.suspended? - = link_to 'Undo suspension', unsuspend_admin_account_path(@account.id), method: :post, class: 'button' + = link_to 'Undo suspension', admin_account_suspension_path(@account.id), method: :delete, class: 'button' - else - = link_to 'Perform full suspension', suspend_admin_account_path(@account.id), method: :post, data: { confirm: 'Are you sure?' }, class: 'button' + = link_to 'Perform full suspension', admin_account_suspension_path(@account.id), method: :post, data: { confirm: 'Are you sure?' }, class: 'button' diff --git a/app/views/auth/sessions/two_factor.html.haml b/app/views/auth/sessions/two_factor.html.haml index 8bf998554..1deff82b2 100644 --- a/app/views/auth/sessions/two_factor.html.haml +++ b/app/views/auth/sessions/two_factor.html.haml @@ -2,7 +2,7 @@ = t('auth.login') = simple_form_for(resource, as: resource_name, url: session_path(resource_name), method: :post) do |f| - = f.input :otp_attempt, placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt') }, required: true, autofocus: true, autocomplete: 'off' + = f.input :otp_attempt, type: :number, placeholder: t('simple_form.labels.defaults.otp_attempt'), input_html: { 'aria-label' => t('simple_form.labels.defaults.otp_attempt') }, required: true, autofocus: true, autocomplete: 'off' .actions = f.button :button, t('auth.login'), type: :submit diff --git a/app/views/settings/exports/show.html.haml b/app/views/settings/exports/show.html.haml index 51be40fb6..f2f6f9556 100644 --- a/app/views/settings/exports/show.html.haml +++ b/app/views/settings/exports/show.html.haml @@ -5,17 +5,17 @@ %tbody %tr %th= t('exports.storage') - %td= number_to_human_size @total_storage + %td= number_to_human_size @export.total_storage %td %tr %th= t('exports.follows') - %td= @total_follows + %td= @export.total_follows %td= table_link_to 'download', t('exports.csv'), settings_exports_follows_path(format: :csv) %tr %th= t('exports.blocks') - %td= @total_blocks + %td= @export.total_blocks %td= table_link_to 'download', t('exports.csv'), settings_exports_blocks_path(format: :csv) %tr %th= t('exports.mutes') - %td= @total_mutes + %td= @export.total_mutes %td= table_link_to 'download', t('exports.csv'), settings_exports_mutes_path(format: :csv) diff --git a/app/views/xrd/host_meta.xml.ruby b/app/views/well_known/host_meta/show.xml.ruby index 07d026471..07d026471 100644 --- a/app/views/xrd/host_meta.xml.ruby +++ b/app/views/well_known/host_meta/show.xml.ruby diff --git a/app/views/xrd/webfinger.json.rabl b/app/views/well_known/webfinger/show.json.rabl index e637ed9d3..e637ed9d3 100644 --- a/app/views/xrd/webfinger.json.rabl +++ b/app/views/well_known/webfinger/show.json.rabl diff --git a/app/views/xrd/webfinger.xml.ruby b/app/views/well_known/webfinger/show.xml.ruby index 80ac71d27..80ac71d27 100644 --- a/app/views/xrd/webfinger.xml.ruby +++ b/app/views/well_known/webfinger/show.xml.ruby |