diff options
52 files changed, 1055 insertions, 241 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 diff --git a/config/application.rb b/config/application.rb index 2c720474a..1383d45a5 100644 --- a/config/application.rb +++ b/config/application.rb @@ -27,6 +27,7 @@ module Mastodon config.i18n.available_locales = [ :en, + :bg, :de, :eo, :es, diff --git a/config/locales/bg.yml b/config/locales/bg.yml new file mode 100644 index 000000000..a8687f3ca --- /dev/null +++ b/config/locales/bg.yml @@ -0,0 +1,169 @@ +--- +bg: + about: + about_mastodon: Mastodon е <em>безплатен</em> сървър с <em>отворен код</em> за социални мрежи. Като <em>децентрализирана</em> алтернатива на комерсиалните платформи, той позволява избягването на риска от монополизация на твоята комуникация от единични компании. Изберете си сървър, на който се доверявате, и ще можете да контактувате с всички останали. Всеки може да пусне Mastodon и лесно да вземе участие в <em>социалната мрежа</em>. + about_this: За тази инстанция + apps: Приложения + business_email: 'Служебен e-mail:' + closed_registrations: В момента регистрациите за тази инстанция са затворени. + contact: За контакти + description_headline: Какво е %{domain}? + domain_count_after: други инстанции + domain_count_before: Свързани към + features: + api: Отворено API за приложения и услуги + blocks: Богат на инструменти за блокиране и заглушаване + characters: Публикации от 500 символа + chronology: Публикациите се показват хронологично + ethics: 'Етичен дизайн: без реклами и проследяване' + gifv: GIFV комплекти и кратки видео клипове + privacy: Настройване на поверителността за всяка публикация + public: Публични канали + features_headline: Какво откроява Mastodon + get_started: Първи стъпки + links: Връзки + other_instances: Други инстанции + source_code: Програмен код + status_count_after: публикации + status_count_before: Написали + terms: Условия + user_count_after: потребители + user_count_before: Дом на + accounts: + follow: Последвай + followers: Последователи + following: Следва + nothing_here: Тук няма никого! + people_followed_by: Хора, които %{name} следва + people_who_follow: Хора, които следват %{name} + posts: Публикации + remote_follow: Последвай + unfollow: Не следвай + application_mailer: + settings: 'Промяна на предпочитанията за e-mail: %{link}' + signature: Mastodon известия от %{instance} + view: 'Преглед:' + applications: + invalid_url: Предоставеният URL е невалиден + auth: + change_password: Идентификационни данни + didnt_get_confirmation: Не получих инструкции за потвърждение + forgot_password: Забравих си паролата + login: Влизане + logout: Излизане + register: Регистрация + resend_confirmation: Изпрати отново инструкции за потвърждение + reset_password: Подновяване на паролата + set_new_password: Задай нова парола + authorize_follow: + error: Възникна грешка в откриването на потребителя + follow: Последвай + prompt_html: '(<strong>%{self}</strong>), молбата ти беше изпратена до:' + title: Последвай %{acct} + datetime: + distance_in_words: + about_x_hours: "%{count} ч." + about_x_months: "%{count} м." + about_x_years: "%{count} г." + almost_x_years: "%{count} г." + half_a_minute: Току-що + less_than_x_minutes: "%{count} мин." + less_than_x_seconds: Току-що + over_x_years: "%{count} г." + x_days: "%{count} дни" + x_minutes: "%{count} мин." + x_months: "%{count} м." + x_seconds: "%{count} сек." + exports: + blocks: Вашите блокирания + csv: CSV + follows: Вашите следвания + storage: Съхранение на мултимедия + generic: + changes_saved_msg: Успешно запазване на промените! + powered_by: поддържано от %{link} + save_changes: Запази промените + validation_errors: + one: Нещо все още не е наред! Моля, прегледай грешката по-долу + other: Нещо все още не е наред! Моля, прегледай грешките по-долу + imports: + preface: Можеш да импортираш някои данни, като например всички хора, които следваш или блокираш в акаунта си на тази инстанция, от файлове, създадени чрез експорт в друга инстанция. + success: Твоите данни бяха успешно качени и ще бъдат обработени впоследствие. + types: + blocking: Списък на блокираните + following: Списък на последователите + upload: Качване + landing_strip_html: <strong>%{name}</strong> е потребител от <strong>%{domain}</strong>. Можеш да ги следваш, или да контактуваш с тях, ако имаш акаунт където и да е из федерираната вселена на Mastodon. Ако нямаш акаунт, можеш да си <a href="%{sign_up_path}">създадеш ето тук</a>. + notification_mailer: + digest: + body: 'Ето кратко резюме на нещата, които се случиха от последното ти посещение в %{instance} на %{since}:' + mention: "%{name} те спомена в:" + new_followers_summary: + one: Имаш един нов последовател! Ура! + other: Имаш %{count} нови последователи! Изумително! + subject: + one: "1 ново известие от последното ти посещение \U0001F418" + other: "%{count} нови известия от последното ти посещение \U0001F418" + favourite: + body: 'Публикацията ти беше харесана от %{name}:' + subject: "%{name} хареса твоята публикация" + follow: + body: "%{name} те последва!" + subject: "%{name} те последва" + follow_request: + body: "%{name} помоли за разрешение да те последва" + subject: 'Чакащ последовател: %{name}' + mention: + body: '%{name} те спомена в:' + subject: '%{name} те спомена' + reblog: + body: 'Твоята публикация беше споделена от %{name}:' + subject: "%{name} сподели публикацията ти" + pagination: + next: Напред + prev: Назад + remote_follow: + acct: Въведи потребителско_име@домейн, от които искаш да следваш + missing_resource: Неуспешно търсене на нужния URL за пренасочване за твоя акаунт + proceed: Започни следване + prompt: 'Ще последваш:' + settings: + authorized_apps: Упълномощени приложения + back: Обратно към Mastodon + edit_profile: Редактирай профила си + export: Експортиране на данни + import: Импортиране + preferences: Предпочитания + settings: Настройки + two_factor_auth: Двустепенно удостоверяване + statuses: + open_in_web: Отвори в уеб + over_character_limit: прехвърлен лимит от %{max} символа + show_more: Покажи повече + visibilities: + private: Покажи само на последователите си + public: Публично + unlisted: Публично, но не показвай в публичния канал + stream_entries: + click_to_show: Покажи + reblogged: споделено + sensitive_content: Деликатно съдържание + time: + formats: + default: "%d %b, %Y, %H:%M" + two_factor_auth: + description_html: При активация на <strong>двустепенно удостоверяване</strong>, за да влезеш в приложението, ще трябва да използваш телефона си. През него ще се генерира код, който да въвеждаш при влизане. + disable: Деактивирай + enable: Активирай + instructions_html: "<strong>Сканирай този QR код с Google Authenticator или подобно приложение от своя телефон</strong>. Oтсега нататък, това приложение ще генерира код, който ще трябва да въвеждаш при всяко влизане." + plaintext_secret_html: "Тайна в обикновен текст: <samp>%{secret}</samp>" + warning: Ако не можеш да настроиш приложението за удостверяване сега, избери "Деактивирай". В противен случай, няма да можеш да влезеш в акаунта си. + users: + invalid_email: E-mail адресът е невалиден + invalid_otp_token: Невалиден код + will_paginate: + page_gap: "…" + media_attachments: + validations: + too_many: Не мога да прикача повече от 4 файла + images_and_video: Не мога да прикача видеоклип към публикация, която вече съдържа изображения diff --git a/config/locales/devise.bg.yml b/config/locales/devise.bg.yml new file mode 100644 index 000000000..7485b8236 --- /dev/null +++ b/config/locales/devise.bg.yml @@ -0,0 +1,61 @@ +--- +bg: + devise: + confirmations: + confirmed: Твоят профил беше успешно потвърден. Влизането в профила е успешно. + send_instructions: Ще получиш писмо с инструкции как да потвърдиш своя профил до няколко минути. + send_paranoid_instructions: Ако твоят имейл адрес съществува в базата ни, ще получиш там инструкции как да потвърдиш своя профил. + failure: + already_authenticated: Вече си вътре в профила си. + inactive: Профилът ти все още не е активиран. + invalid: Невалиден имейл адрес или парола. + last_attempt: Разполагаш с още един опит преди профилът ти да бъде заключен. + locked: Профилът ти е заключен. + not_found_in_database: "Невалидни стойности за %{authentication_keys} или парола." + timeout: Сесията ти изтече, моля влез отново, за да продължиш. + unauthenticated: Преди да продължиш, трябва да влезеш в профила си или да се регистрираш. + unconfirmed: Преди да продължиш, трябва да потвърдиш регистрацията си. + mailer: + confirmation_instructions: + subject: 'Mastodon: Инструкции за потвърждаване' + password_change: + subject: 'Mastodon: Паролата е променена' + reset_password_instructions: + subject: 'Инструкции за смяна на паролата' + unlock_instructions: + subject: 'Инструкции за отключване' + omniauth_callbacks: + failure: "Не успяхме да те упълномощим чрез %{kind}, защото \"%{reason}\"." + success: "Успешно упълномощаване чрез %{kind} профил." + passwords: + no_token: Може да достъпваш тази страница само от имейл за промяна на паролата. Ако тази страница е отворена от такъв имейл, увери се, че използваш целия URL-адрес, който сме ти изпратили. + send_instructions: Ще получиш писмо с инструкции как да промениш паролата си до няколко минути. + send_paranoid_instructions: Ако твоят имейл адрес съществува в базата ни, ще получиш там инструкции за промяна на своята парола. + updated: Паролата ти беше променена успешно. Влизането в профила е успешно. + updated_not_active: Паролата ти беше променена успешно. + registrations: + destroyed: Довиждане! Твоят профил беше успешно изтрит. Надяваме се скоро да те видим отново. + signed_up: Привет! Регистрирацията ти е успешна. + signed_up_but_inactive: Регистрирацията ти е успешна. Въпреки това, не можеш да влезеш в профила си, защото той все още не е потвърден. + signed_up_but_locked: Регистрирацията ти е успешна. Въпреки това, не можеш да влезеш в профила си, защото той е заключен. + signed_up_but_unconfirmed: Писмо с връзка за потвърждаване на профила ти беше изпратено на твоя имейл адрес. Моля, отвори връзката, за да активираш своя профил. + update_needs_confirmation: Профилът ти е успешно променен, но ние трябва да проверим твоя нов имейл адрес. Моля, провери пощата си и отвори връзката за потвърждаване на новия адрес. + updated: Профилът ти е успешно променен. + sessions: + already_signed_out: Успешно излизане от профила. + signed_in: Успешно влизане. + signed_out: Успешно излизане. + unlocks: + send_instructions: Ще получиш писмо с инструкции как да отключиш профила си до няколко минути. + send_paranoid_instructions: Ако твоят профил съществува в базата ни, на своя имейл адрес ще получиш инструкции за отключването му до няколко минути. + unlocked: Твоят профил беше отключен успешно. За да продължиш, влез в него. + errors: + messages: + already_confirmed: е вече потвърден, моля опитай да влезеш в профила си с него + confirmation_period_expired: "трябва да се потвърди в рамките на %{period}, моля направи нова заявка за потвърждение" + expired: е изтекъл, моля заяви нов + not_found: не е намерен + not_locked: не бе заключен + not_saved: + one: "Една грешка попречи този %{resource} да бъде записан:" + other: "%{count} грешки попречиха този %{resource} да бъде записан:" diff --git a/config/locales/doorkeeper.bg.yml b/config/locales/doorkeeper.bg.yml new file mode 100644 index 000000000..6fafdfc55 --- /dev/null +++ b/config/locales/doorkeeper.bg.yml @@ -0,0 +1,113 @@ +--- +bg: + activerecord: + attributes: + doorkeeper/application: + name: Име + redirect_uri: URI за пренасочване + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: не може да съдържа фрагмент. + invalid_uri: трябва да е валидно URI. + relative_uri: трябва да е абсолютно URI. + secured_uri: трябва да е HTTPS/SSL URI. + doorkeeper: + applications: + buttons: + authorize: Упълномощаване + cancel: Отказ + destroy: Унищожаване + edit: Редакция + submit: Изпращане + confirmations: + destroy: Потвърждаваш ли изтриването? + edit: + title: Редактиране на приложението + form: + error: О, не! Провери формата за възможни грешки + help: + native_redirect_uri: Изполвай %{native_redirect_uri} за локални тестове + redirect_uri: Използвай един ред за всяко URI + scopes: Разделяй диапазоните с интервал. Остави празно, за да използваш диапазона по подразбиране. + index: + callback_url: URL за обратно повикване + name: Име + new: Ново приложение + title: Твоите приложения + new: + title: Ново приложение + show: + actions: Действия + application_id: Идентификатор на приложението + callback_urls: URL-и за обратно повикване + scopes: Диапазони + secret: Тайна + title: 'Приложение: %{name}' + authorizations: + buttons: + authorize: Упълномощаване + deny: Отказ + error: + title: Възникна грешка + new: + able_to: Ще е възможно + prompt: Приложението %{client_name} заявява достъп до твоя акаунт + title: Изисква се упълномощаване + show: + title: Код за упълномощаване + authorized_applications: + buttons: + revoke: Отмяна + confirmations: + revoke: Потвърждаваш ли отмяната? + index: + application: Приложение + created_at: Създадено на + date_format: "%Y-%m-%d %H:%M:%S" + scopes: Диапазони + title: Твоите упълномощени приложения + errors: + messages: + access_denied: Заявката беше отказана от собственика на ресурса или от сървъра за упълномощаване. + credential_flow_not_configured: Resource Owner Password Credentials предизвика грешка, заради това, че настройките за Doorkeeper.configure.resource_owner_from_credentials липсват. + invalid_client: Удостоверяването на клиента предизвика грешка, поради непознат клиент, липсващо клиентско удостоверяване, или заради това, че методът на удостоверяване не се поддържа. + invalid_grant: Предоставеното удостоверение за достъп е невалидно, изтекло, отхвърлено, не съвпада с пренасочващото URI, използвано в заявката за удостоверение, или е бил издадено от друг клиент. + invalid_redirect_uri: Наличното пренасочващо URI е невалидно. + invalid_request: Заявката е с липсващ задължителен параметър, включва стойност на параметъра, която не се поддържа, или е изкривена по друг начин. + invalid_resource_owner: Предоставените идентификационни данни на притежателя на ресурса са невалидни, или притежателят не може да бъде намерен. + invalid_scope: Заявеният диапазон е невалиден, неизвестен или изкривен. + invalid_token: + expired: Маркерът за достъп изтече + revoked: Маркерът за достъп беше отхвърлен + unknown: Маркерът за достъп е невалиден + resource_owner_authenticator_not_configured: Намирането на Resource Owner се провали поради липса на конфигурация на Doorkeeper.configure.resource_owner_authenticator. + server_error: Сървърът за удостоверяване попадна на неочаквано условие, което предотврати изпълнението на заявката. + temporarily_unavailable: Сървърът за удостоверяване не може да се справи със заявката в момента поради временно претоварване или профилактика на сървъра. + unauthorized_client: Клиентът не е удостоверен да изпълни заявката по този начин. + unsupported_grant_type: Типът на удостоврението за достъп не се поддържа от сървъра за удостоверяване. + unsupported_response_type: Удостоверяващият сървър не поддържа този тип отговор. + flash: + applications: + create: + notice: Приложението е създадено. + destroy: + notice: Приложението е изтрито. + update: + notice: Приложението е обновено. + authorized_applications: + destroy: + notice: Приложението е отказано. + layouts: + admin: + nav: + applications: Приложения + oauth2_provider: OAuth2 доставчик + application: + title: Нужно е упълномощаване по OAuth + scopes: + follow: следването, блокирането, деблокирането и отмяната на следването на акаунтите + read: четенето на данните от твоя акаунт + write: публикуването от твое име diff --git a/config/locales/doorkeeper.ja.yml b/config/locales/doorkeeper.ja.yml index 35592bc49..d3ea93789 100644 --- a/config/locales/doorkeeper.ja.yml +++ b/config/locales/doorkeeper.ja.yml @@ -25,7 +25,7 @@ ja: confirmations: destroy: 本当に削除しますか? edit: - title: アプリケーションの編集 + title: アプリの編集 form: error: フォームにエラーが無いか確認してください。 help: @@ -35,17 +35,17 @@ ja: index: callback_url: コールバックURL name: 名前 - new: 新規アプリケーション - title: あなたのアプリケーション + new: 新規アプリ + title: アプリ new: - title: 新規アプリケーション + title: 新規アプリ show: actions: アクション application_id: アクションId callback_urls: コールバックurl scopes: アクセス権 secret: 非公開 - title: 'アプリケーション: %{name}' + title: 'アプリ: %{name}' authorizations: buttons: authorize: 承認 @@ -53,8 +53,8 @@ ja: error: title: エラーが発生しました。 new: - able_to: このアプリケーションは以下のことができます - prompt: アプリケーション %{client_name} があなたのアカウントへのアクセスを要求しています。 + able_to: このアプリは以下のことができます + prompt: アプリ %{client_name} があなたのアカウントへのアクセスを要求しています。 title: 認証が必要です。 show: title: 認証コード @@ -68,7 +68,7 @@ ja: created_at: 許可した日時 date_format: "%Y年%m月%d日 %H時%M分%S秒" scopes: アクセス権 - title: 認証済みアプリケーション + title: 認証済みアプリ errors: messages: access_denied: リソースの所有者または認証サーバーが要求を拒否しました。 @@ -92,22 +92,22 @@ ja: flash: applications: create: - notice: アプリケーションが作成されました。 + notice: アプリが作成されました。 destroy: - notice: アプリケーションが削除されました。 + notice: アプリが削除されました。 update: - notice: アプリケーションが更新されました。 + notice: アプリが更新されました。 authorized_applications: destroy: - notice: アプリケーションが取り消されました。 + notice: アプリが取り消されました。 layouts: admin: nav: - applications: アプリケーション + applications: アプリ oauth2_provider: OAuth2プロバイダー application: - title: OAuth認証が必要です。 + title: OAuth認証 scopes: follow: アカウントのフォロー, ブロック, ブロック解除, フォロー解除 - read: アカウントへのデータの読み取り - write: アカウントからの投稿の書き込み + read: アカウントからのデータの読み取り + write: アカウントへのデータの書き込み diff --git a/config/locales/ja.yml b/config/locales/ja.yml index 9407c7669..cd6b6543d 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -46,7 +46,7 @@ ja: applications: invalid_url: URLが無効です auth: - change_password: 資格情報 + change_password: ログイン情報 didnt_get_confirmation: 確認メールを受信できませんか? forgot_password: パスワードをお忘れですか? login: ログイン diff --git a/config/locales/ru.yml b/config/locales/ru.yml index fab178629..0c2725855 100644 --- a/config/locales/ru.yml +++ b/config/locales/ru.yml @@ -9,7 +9,7 @@ ru: contact: Связаться description_headline: Что такое %{domain}? domain_count_after: другими узлами - domain_count_before: Связывается с + domain_count_before: Связан с features: api: Открытый API для приложений и сервисов blocks: Продвинутые инструменты блокирования и глушения @@ -25,7 +25,7 @@ ru: other_instances: Другие узлы source_code: Исходный код status_count_after: статусов - status_count_before: Автор + status_count_before: Опубликовано terms: Условия user_count_after: пользователей user_count_before: Здесь живет @@ -42,7 +42,7 @@ ru: application_mailer: settings: 'Изменить настройки e-mail: %{link}' signature: Уведомления Mastodon от %{instance} - view: 'View:' + view: 'Просмотр:' applications: invalid_url: Введенный URL неверен auth: @@ -126,7 +126,7 @@ ru: acct: Введите username@domain, откуда Вы хотите подписаться missing_resource: Поиск требуемого перенаправления URL для Вашего аккаунта завершился неудачей proceed: Продолжить подписку - prompt: 'Вы ходите подписаться на:' + prompt: 'Вы хотите подписаться на:' settings: authorized_apps: Авторизованные приложения back: Назад в Mastodon @@ -142,8 +142,8 @@ ru: show_more: Подробнее visibilities: private: Показывать только подписчикам - public: Публичный - unlisted: Публичный, но без отображения в публичных лентах + public: Показывать всем + unlisted: Показывать всем, но не отображать в публичных лентах stream_entries: click_to_show: Показать reblogged: продвинул(а) @@ -156,8 +156,13 @@ ru: disable: Отключить enable: Включить instructions_html: "<strong>Отсканируйте этот QR-код с помощью Google Authenticator или другого подобного приложения на Вашем телефоне</strong>. С этого момента приложение будет генерировать токены, которые будет необходимо ввести для входа." + manual_instructions: 'Если Вы не можете отсканировать QR-код и хотите ввести его вручную, секрет представлен здесь открытым текстом:' plaintext_secret_html: 'Секрет открытым текстом: <samp>%{secret}</samp>' - warning: Если сейчас у Вас не получается настроить аутентификатор, нажмите "отключить", иначе Вы не сможете войти! + code_hint: 'Для подтверждения введите код, сгенерированный приложением аутентификатора' + setup: Настроить + warning: 'Если сейчас у Вас не получается настроить аутентификатор, нажмите "отключить", иначе Вы не сможете войти!' users: invalid_email: Введенный e-mail неверен invalid_otp_token: Введен неверный код + will_paginate: + page_gap: "…" diff --git a/config/locales/simple_form.bg.yml b/config/locales/simple_form.bg.yml new file mode 100644 index 000000000..55b80277d --- /dev/null +++ b/config/locales/simple_form.bg.yml @@ -0,0 +1,46 @@ +--- +bg: + simple_form: + hints: + defaults: + avatar: PNG, GIF или JPG. До 2MB. Ще бъде смалена до 120x120 пиксела + display_name: До 30 символа + header: PNG, GIF или JPG. До 2MB. Ще бъде смалена до 700x335 пиксела + locked: Изисква ръчно одобрение на последователите. По подразбиране, публикациите са достъпни само до последователи. + note: До 160 символа + imports: + data: CSV файл, експортиран от друга инстанция на Mastodon + labels: + defaults: + avatar: Аватар + confirm_new_password: Потвърди новата парола + confirm_password: Потвърди паролата + current_password: Текуща парола + data: Данни + display_name: Показвано име + email: E-mail адрес + header: Заглавен ред + locale: Език + locked: Направи акаунта поверителен + new_password: Нова парола + note: Био + otp_attempt: Двустепенен код + password: Парола + setting_default_privacy: Поверителност на публикациите + type: Тип на импортиране + username: Потребителско име + interactions: + must_be_follower: Блокирай известия от не-последователи + must_be_following: Блокирай известия от хора, които не следваш + notification_emails: + digest: Изпращай извлечения на съобщенията + favourite: Изпращай e-mail, когато някой хареса твоя публикация + follow: Изпращай e-mail, когато някой те последва + follow_request: Изпращай e-mail, когато някой пожелае да те последва + mention: Изпращай e-mail, когато някой те спомене + reblog: Изпращай e-mail, когато някой сподели твоя публикация + 'no': 'Не' + required: + mark: "*" + text: задължително + 'yes': 'Да' diff --git a/config/locales/simple_form.ru.yml b/config/locales/simple_form.ru.yml index 6f4873bfd..b7d8e4e05 100644 --- a/config/locales/simple_form.ru.yml +++ b/config/locales/simple_form.ru.yml @@ -26,7 +26,7 @@ ru: note: О Вас otp_attempt: Двухфакторный код password: Пароль - setting_default_privacy: Приватность постов + setting_default_privacy: Видимость постов type: Тип импорта username: Имя пользователя interactions: diff --git a/config/routes.rb b/config/routes.rb index 78bf7870c..6e48d3b92 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,3 +1,4 @@ + # frozen_string_literal: true require 'sidekiq/web' @@ -14,8 +15,8 @@ Rails.application.routes.draw do controllers authorizations: 'oauth/authorizations', authorized_applications: 'oauth/authorized_applications' end - get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta - get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger, defaults: { format: 'json' } + get '.well-known/host-meta', to: 'well_known/host_meta#show', as: :host_meta, defaults: { format: 'xml' } + get '.well-known/webfinger', to: 'well_known/webfinger#show', as: :webfinger, defaults: { format: 'json' } devise_for :users, path: 'auth', controllers: { sessions: 'auth/sessions', @@ -89,12 +90,8 @@ Rails.application.routes.draw do end resources :accounts, only: [:index, :show] do - member do - post :silence - post :unsilence - post :suspend - post :unsuspend - end + resource :silence, only: [:create, :destroy] + resource :suspension, only: [:create, :destroy] end end diff --git a/spec/controllers/admin/silences_controller_spec.rb b/spec/controllers/admin/silences_controller_spec.rb new file mode 100644 index 000000000..7c541d797 --- /dev/null +++ b/spec/controllers/admin/silences_controller_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +describe Admin::SilencesController do + let(:account) { Fabricate(:account) } + before do + sign_in Fabricate(:user, admin: true), scope: :user + end + + describe 'POST #create' do + it 'redirects to admin accounts page' do + post :create, params: { account_id: account.id } + + expect(response).to redirect_to(admin_accounts_path) + end + end + + describe 'DELETE #destroy' do + it 'redirects to admin accounts page' do + delete :destroy, params: { account_id: account.id } + + expect(response).to redirect_to(admin_accounts_path) + end + end +end diff --git a/spec/controllers/admin/suspensions_controller_spec.rb b/spec/controllers/admin/suspensions_controller_spec.rb new file mode 100644 index 000000000..9096f067e --- /dev/null +++ b/spec/controllers/admin/suspensions_controller_spec.rb @@ -0,0 +1,24 @@ +require 'rails_helper' + +describe Admin::SuspensionsController do + let(:account) { Fabricate(:account) } + before do + sign_in Fabricate(:user, admin: true), scope: :user + end + + describe 'POST #create' do + it 'redirects to admin accounts page' do + post :create, params: { account_id: account.id } + + expect(response).to redirect_to(admin_accounts_path) + end + end + + describe 'DELETE #destroy' do + it 'redirects to admin accounts page' do + delete :destroy, params: { account_id: account.id } + + expect(response).to redirect_to(admin_accounts_path) + end + end +end diff --git a/spec/controllers/settings/exports_controller_spec.rb b/spec/controllers/settings/exports_controller_spec.rb index ff98f3ad9..2be6e4744 100644 --- a/spec/controllers/settings/exports_controller_spec.rb +++ b/spec/controllers/settings/exports_controller_spec.rb @@ -1,6 +1,8 @@ require 'rails_helper' describe Settings::ExportsController do + render_views + before do sign_in Fabricate(:user), scope: :user end @@ -8,6 +10,7 @@ describe Settings::ExportsController do describe 'GET #show' do it 'returns http success' do get :show + expect(response).to have_http_status(:success) end end diff --git a/spec/controllers/well_known/host_meta_controller_spec.rb b/spec/controllers/well_known/host_meta_controller_spec.rb new file mode 100644 index 000000000..8a040021a --- /dev/null +++ b/spec/controllers/well_known/host_meta_controller_spec.rb @@ -0,0 +1,13 @@ +require 'rails_helper' + +describe WellKnown::HostMetaController, type: :controller do + render_views + + describe 'GET #show' do + it 'returns http success' do + get :show, format: :xml + + expect(response).to have_http_status(:success) + end + end +end diff --git a/spec/controllers/well_known/webfinger_controller_spec.rb b/spec/controllers/well_known/webfinger_controller_spec.rb new file mode 100644 index 000000000..6e493b037 --- /dev/null +++ b/spec/controllers/well_known/webfinger_controller_spec.rb @@ -0,0 +1,21 @@ +require 'rails_helper' + +describe WellKnown::WebfingerController, type: :controller do + render_views + + describe 'GET #show' do + let(:alice) { Fabricate(:account, username: 'alice') } + + it 'returns http success when account can be found' do + get :show, params: { resource: alice.to_webfinger_s }, format: :json + + expect(response).to have_http_status(:success) + end + + it 'returns http not found when account cannot be found' do + get :show, params: { resource: 'acct:not@existing.com' }, format: :json + + expect(response).to have_http_status(:not_found) + end + end +end diff --git a/spec/controllers/xrd_controller_spec.rb b/spec/controllers/xrd_controller_spec.rb deleted file mode 100644 index 33b17f152..000000000 --- a/spec/controllers/xrd_controller_spec.rb +++ /dev/null @@ -1,26 +0,0 @@ -require 'rails_helper' - -RSpec.describe XrdController, type: :controller do - render_views - - describe 'GET #host_meta' do - it 'returns http success' do - get :host_meta - expect(response).to have_http_status(:success) - end - end - - describe 'GET #webfinger' do - let(:alice) { Fabricate(:account, username: 'alice') } - - it 'returns http success when account can be found' do - get :webfinger, params: { resource: alice.to_webfinger_s }, format: :json - expect(response).to have_http_status(:success) - end - - it 'returns http not found when account cannot be found' do - get :webfinger, params: { resource: 'acct:not@existing.com' }, format: :json - expect(response).to have_http_status(:not_found) - end - end -end diff --git a/spec/models/account_filter_spec.rb b/spec/models/account_filter_spec.rb new file mode 100644 index 000000000..1599c5ae8 --- /dev/null +++ b/spec/models/account_filter_spec.rb @@ -0,0 +1,31 @@ +require 'rails_helper' + +describe AccountFilter do + describe 'with empty params' do + it 'defaults to alphabetic account list' do + filter = AccountFilter.new({}) + + expect(filter.results).to eq Account.alphabetic + end + end + + describe 'with invalid params' do + it 'raises with key error' do + filter = AccountFilter.new(wrong: true) + + expect { filter.results }.to raise_error(/wrong/) + end + end + + describe 'with valid params' do + it 'combines filters on Account' do + filter = AccountFilter.new(by_domain: 'test.com', silenced: true) + + allow(Account).to receive(:where).and_return(Account.none) + allow(Account).to receive(:silenced).and_return(Account.none) + filter.results + expect(Account).to have_received(:where).with(domain: 'test.com') + expect(Account).to have_received(:silenced) + end + end +end diff --git a/spec/requests/host_meta_request_spec.rb b/spec/requests/host_meta_request_spec.rb new file mode 100644 index 000000000..0c51b5f48 --- /dev/null +++ b/spec/requests/host_meta_request_spec.rb @@ -0,0 +1,12 @@ +require "rails_helper" + +describe "The host_meta route" do + describe "requested without accepts headers" do + it "returns an xml response" do + get host_meta_url + + expect(response).to have_http_status(:success) + expect(response.content_type).to eq "application/xrd+xml" + end + end +end diff --git a/spec/routing/well_known_routes_spec.rb b/spec/routing/well_known_routes_spec.rb new file mode 100644 index 000000000..9540c3de3 --- /dev/null +++ b/spec/routing/well_known_routes_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +describe 'the host-meta route' do + it 'routes to correct place with xml format' do + expect(get('/.well-known/host-meta')). + to route_to('well_known/host_meta#show', format: 'xml') + end +end + +describe 'the webfinger route' do + it 'routes to correct place with json format' do + expect(get('/.well-known/webfinger')). + to route_to('well_known/webfinger#show', format: 'json') + end +end |