Browse Source

Merge branch 'master' into master

master
Eugen 7 years ago
committed by GitHub
parent
commit
1a12fd14d4
52 changed files with 1055 additions and 241 deletions
  1. +2
    -3
      app/assets/javascripts/components/containers/mastodon.jsx
  2. +1
    -1
      app/assets/javascripts/components/features/community_timeline/index.jsx
  3. +1
    -1
      app/assets/javascripts/components/features/compose/components/compose_form.jsx
  4. +1
    -1
      app/assets/javascripts/components/features/compose/index.jsx
  5. +2
    -2
      app/assets/javascripts/components/features/getting_started/index.jsx
  6. +1
    -1
      app/assets/javascripts/components/features/public_timeline/index.jsx
  7. +68
    -0
      app/assets/javascripts/components/locales/bg.jsx
  8. +105
    -48
      app/assets/javascripts/components/locales/en.jsx
  9. +2
    -1
      app/assets/javascripts/components/locales/index.jsx
  10. +2
    -2
      app/assets/javascripts/components/locales/ja.jsx
  11. +59
    -3
      app/assets/javascripts/components/locales/pt.jsx
  12. +38
    -5
      app/assets/javascripts/components/locales/ru.jsx
  13. +1
    -1
      app/assets/stylesheets/forms.scss
  14. +14
    -34
      app/controllers/admin/accounts_controller.rb
  15. +23
    -0
      app/controllers/admin/silences_controller.rb
  16. +23
    -0
      app/controllers/admin/suspensions_controller.rb
  17. +1
    -1
      app/controllers/settings/exports/base_controller.rb
  18. +2
    -2
      app/controllers/settings/exports/blocked_accounts_controller.rb
  19. +2
    -2
      app/controllers/settings/exports/following_accounts_controller.rb
  20. +2
    -2
      app/controllers/settings/exports/muted_accounts_controller.rb
  21. +1
    -4
      app/controllers/settings/exports_controller.rb
  22. +13
    -0
      app/controllers/well_known/host_meta_controller.rb
  23. +43
    -0
      app/controllers/well_known/webfinger_controller.rb
  24. +0
    -55
      app/controllers/xrd_controller.rb
  25. +1
    -0
      app/helpers/settings_helper.rb
  26. +36
    -0
      app/models/account_filter.rb
  27. +34
    -4
      app/models/export.rb
  28. +4
    -4
      app/views/admin/accounts/show.html.haml
  29. +1
    -1
      app/views/auth/sessions/two_factor.html.haml
  30. +4
    -4
      app/views/settings/exports/show.html.haml
  31. +0
    -0
      app/views/well_known/host_meta/show.xml.ruby
  32. +0
    -0
      app/views/well_known/webfinger/show.json.rabl
  33. +0
    -0
      app/views/well_known/webfinger/show.xml.ruby
  34. +1
    -0
      config/application.rb
  35. +169
    -0
      config/locales/bg.yml
  36. +61
    -0
      config/locales/devise.bg.yml
  37. +113
    -0
      config/locales/doorkeeper.bg.yml
  38. +16
    -16
      config/locales/doorkeeper.ja.yml
  39. +1
    -1
      config/locales/ja.yml
  40. +12
    -7
      config/locales/ru.yml
  41. +46
    -0
      config/locales/simple_form.bg.yml
  42. +1
    -1
      config/locales/simple_form.ru.yml
  43. +5
    -8
      config/routes.rb
  44. +24
    -0
      spec/controllers/admin/silences_controller_spec.rb
  45. +24
    -0
      spec/controllers/admin/suspensions_controller_spec.rb
  46. +3
    -0
      spec/controllers/settings/exports_controller_spec.rb
  47. +13
    -0
      spec/controllers/well_known/host_meta_controller_spec.rb
  48. +21
    -0
      spec/controllers/well_known/webfinger_controller_spec.rb
  49. +0
    -26
      spec/controllers/xrd_controller_spec.rb
  50. +31
    -0
      spec/models/account_filter_spec.rb
  51. +12
    -0
      spec/requests/host_meta_request_spec.rb
  52. +15
    -0
      spec/routing/well_known_routes_spec.rb

+ 2
- 3
app/assets/javascripts/components/containers/mastodon.jsx View File

@ -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: {

+ 1
- 1
app/assets/javascripts/components/features/community_timeline/index.jsx View File

@ -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 => ({

+ 1
- 1
app/assets/javascripts/components/features/compose/components/compose_form.jsx View File

@ -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({

+ 1
- 1
app/assets/javascripts/components/features/compose/index.jsx View File

@ -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' }

+ 2
- 2
app/assets/javascripts/components/features/getting_started/index.jsx View File

@ -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' }

+ 1
- 1
app/assets/javascripts/components/features/public_timeline/index.jsx View File

@ -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 => ({

+ 68
- 0
app/assets/javascripts/components/locales/bg.jsx View File

@ -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;

+ 105
- 48
app/assets/javascripts/components/locales/en.jsx View File

@ -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;

+ 2
- 1
app/assets/javascripts/components/locales/index.jsx View File

@ -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) {

+ 2
- 2
app/assets/javascripts/components/locales/ja.jsx View File

@ -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": "トゥート",

+ 59
- 3
app/assets/javascripts/components/locales/pt.jsx View File

@ -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;

+ 38
- 5
app/assets/javascripts/components/locales/ru.jsx View File

@ -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;

+ 1
- 1
app/assets/stylesheets/forms.scss View File

@ -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;

+ 14
- 34
app/controllers/admin/accounts_controller.rb View File

@ -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

+ 23
- 0
app/controllers/admin/silences_controller.rb View File

@ -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

+ 23
- 0
app/controllers/admin/suspensions_controller.rb View File

@ -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

+ 1
- 1
app/controllers/settings/exports/base_controller.rb View File

@ -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 }

+ 2
- 2
app/controllers/settings/exports/blocked_accounts_controller.rb View File

@ -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

+ 2
- 2
app/controllers/settings/exports/following_accounts_controller.rb View File

@ -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

+ 2
- 2
app/controllers/settings/exports/muted_accounts_controller.rb View File

@ -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

+ 1
- 4
app/controllers/settings/exports_controller.rb View File

@ -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

+ 13
- 0
app/controllers/well_known/host_meta_controller.rb View File

@ -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

+ 43
- 0
app/controllers/well_known/webfinger_controller.rb View File

@ -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

+ 0
- 55
app/controllers/xrd_controller.rb View File

@ -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

+ 1
- 0
app/helpers/settings_helper.rb View File

@ -16,6 +16,7 @@ module SettingsHelper
ja: '日本語',
'zh-CN': '简体中文',
'zh-HK': '繁體中文(香港)',
bg: 'Български',
}.freeze
def human_locale(locale)

+ 36
- 0
app/models/account_filter.rb View File

@ -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

+ 34
- 4
app/models/export.rb View File

@ -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)]

+ 4
- 4
app/views/admin/accounts/show.html.haml View File

@ -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'

+ 1
- 1
app/views/auth/sessions/two_factor.html.haml View File

@ -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

+ 4
- 4
app/views/settings/exports/show.html.haml View File

@ -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)

app/views/xrd/host_meta.xml.ruby → app/views/well_known/host_meta/show.xml.ruby View File


app/views/xrd/webfinger.json.rabl → app/views/well_known/webfinger/show.json.rabl View File


app/views/xrd/webfinger.xml.ruby → app/views/well_known/webfinger/show.xml.ruby View File


+ 1
- 0
config/application.rb View File

@ -27,6 +27,7 @@ module Mastodon
config.i18n.available_locales = [
:en,
:bg,
:de,
:eo,
:es,

+ 169
- 0
config/locales/bg.yml View File

@ -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: "&hellip;"
media_attachments:
validations:
too_many: Не мога да прикача повече от 4 файла
images_and_video: Не мога да прикача видеоклип към публикация, която вече съдържа изображения

+ 61
- 0
config/locales/devise.bg.yml View File

@ -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} да бъде записан:"

+ 113
- 0
config/locales/doorkeeper.bg.yml View File

@ -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: публикуването от твое име

+ 16
- 16
config/locales/doorkeeper.ja.yml View File

@ -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: アカウントへのデータの書き込み

+ 1
- 1
config/locales/ja.yml View File

@ -46,7 +46,7 @@ ja:
applications:
invalid_url: URLが無効です
auth:
change_password: 資格情報
change_password: ログイン情報
didnt_get_confirmation: 確認メールを受信できませんか?
forgot_password: パスワードをお忘れですか?
login: ログイン

+ 12
- 7
config/locales/ru.yml View File

@ -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: "&hellip;"

+ 46
- 0
config/locales/simple_form.bg.yml View File

@ -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': 'Да'

+ 1
- 1
config/locales/simple_form.ru.yml View File

@ -26,7 +26,7 @@ ru:
note: О Вас
otp_attempt: Двухфакторный код
password: Пароль
setting_default_privacy: Приватность постов
setting_default_privacy: Видимость постов
type: Тип импорта
username: Имя пользователя
interactions:

+ 5
- 8
config/routes.rb View File

@ -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

+ 24
- 0
spec/controllers/admin/silences_controller_spec.rb View File

@ -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

+ 24
- 0
spec/controllers/admin/suspensions_controller_spec.rb View File

@ -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

+ 3
- 0
spec/controllers/settings/exports_controller_spec.rb View File

@ -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

+ 13
- 0
spec/controllers/well_known/host_meta_controller_spec.rb View File

@ -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

+ 21
- 0
spec/controllers/well_known/webfinger_controller_spec.rb View File

@ -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

+ 0
- 26
spec/controllers/xrd_controller_spec.rb View File

@ -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

+ 31
- 0
spec/models/account_filter_spec.rb View File

@ -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

+ 12
- 0
spec/requests/host_meta_request_spec.rb View File

@ -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

+ 15
- 0
spec/routing/well_known_routes_spec.rb View File

@ -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

Loading…
Cancel
Save