diff --git a/.rubocop_todo.yml b/.rubocop_todo.yml
index 9e69426fcf7..8706ca0ddda 100644
--- a/.rubocop_todo.yml
+++ b/.rubocop_todo.yml
@@ -6,10 +6,6 @@
# Note that changes in the inspected code, or installation of new
# versions of RuboCop, may require this file to be generated again.
-Lint/NonLocalExitFromIterator:
- Exclude:
- - 'app/helpers/json_ld_helper.rb'
-
# Configuration parameters: AllowedMethods, AllowedPatterns, CountRepeatedAttributes.
Metrics/AbcSize:
Max: 82
diff --git a/Gemfile.lock b/Gemfile.lock
index d0472d538c0..8c537e52b91 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -468,7 +468,7 @@ GEM
hashie (>= 3.4.6)
rack (>= 2.2.3)
rack-protection
- omniauth-cas (3.0.1)
+ omniauth-cas (3.0.2)
addressable (~> 2.8)
nokogiri (~> 1.12)
omniauth (~> 2.1)
@@ -860,7 +860,7 @@ GEM
stoplight (4.1.1)
redlock (~> 1.0)
stringio (3.1.7)
- strong_migrations (2.4.0)
+ strong_migrations (2.5.0)
activerecord (>= 7.1)
swd (2.0.3)
activesupport (>= 3)
diff --git a/app/controllers/settings/migration/redirects_controller.rb b/app/controllers/settings/migration/redirects_controller.rb
index d850e05e94f..08b01d6b108 100644
--- a/app/controllers/settings/migration/redirects_controller.rb
+++ b/app/controllers/settings/migration/redirects_controller.rb
@@ -22,7 +22,7 @@ class Settings::Migration::RedirectsController < Settings::BaseController
end
def destroy
- if current_account.moved_to_account_id.present?
+ if current_account.moved?
current_account.update!(moved_to_account: nil)
ActivityPub::UpdateDistributionWorker.perform_async(current_account.id)
end
diff --git a/app/helpers/formatting_helper.rb b/app/helpers/formatting_helper.rb
index 1b364a000cf..c27edbb0730 100644
--- a/app/helpers/formatting_helper.rb
+++ b/app/helpers/formatting_helper.rb
@@ -65,12 +65,12 @@ module FormattingHelper
end
def rss_content_preroll(status)
- if status.spoiler_text?
- safe_join [
- tag.p { spoiler_with_warning(status) },
- tag.hr,
- ]
- end
+ return unless status.spoiler_text?
+
+ safe_join [
+ tag.p { spoiler_with_warning(status) },
+ tag.hr,
+ ]
end
def spoiler_with_warning(status)
@@ -81,10 +81,10 @@ module FormattingHelper
end
def rss_content_postroll(status)
- if status.preloadable_poll
- tag.p do
- poll_option_tags(status)
- end
+ return unless status.preloadable_poll
+
+ tag.p do
+ poll_option_tags(status)
end
end
diff --git a/app/helpers/json_ld_helper.rb b/app/helpers/json_ld_helper.rb
index 078aba456ae..675d8b87309 100644
--- a/app/helpers/json_ld_helper.rb
+++ b/app/helpers/json_ld_helper.rb
@@ -134,7 +134,7 @@ module JsonLdHelper
patch_for_forwarding!(value, compacted_value)
elsif value.is_a?(Array)
compacted_value = [compacted_value] unless compacted_value.is_a?(Array)
- return if value.size != compacted_value.size
+ return nil if value.size != compacted_value.size
compacted[key] = value.zip(compacted_value).map do |v, vc|
if v.is_a?(Hash) && vc.is_a?(Hash)
diff --git a/app/helpers/theme_helper.rb b/app/helpers/theme_helper.rb
index 0f24063385b..00b4a6d2b3f 100644
--- a/app/helpers/theme_helper.rb
+++ b/app/helpers/theme_helper.rb
@@ -24,24 +24,24 @@ module ThemeHelper
end
def custom_stylesheet
- if active_custom_stylesheet.present?
- stylesheet_link_tag(
- custom_css_path(active_custom_stylesheet),
- host: root_url,
- media: :all,
- skip_pipeline: true
- )
- end
+ return if active_custom_stylesheet.blank?
+
+ stylesheet_link_tag(
+ custom_css_path(active_custom_stylesheet),
+ host: root_url,
+ media: :all,
+ skip_pipeline: true
+ )
end
private
def active_custom_stylesheet
- if cached_custom_css_digest.present?
- [:custom, cached_custom_css_digest.to_s.first(8)]
- .compact_blank
- .join('-')
- end
+ return if cached_custom_css_digest.blank?
+
+ [:custom, cached_custom_css_digest.to_s.first(8)]
+ .compact_blank
+ .join('-')
end
def cached_custom_css_digest
diff --git a/app/javascript/mastodon/components/status_content.jsx b/app/javascript/mastodon/components/status_content.jsx
index e1fd7734e9e..5f0f7079aec 100644
--- a/app/javascript/mastodon/components/status_content.jsx
+++ b/app/javascript/mastodon/components/status_content.jsx
@@ -48,13 +48,13 @@ class TranslateButton extends PureComponent {
return (
-
-
-
-
+
+
+
+
);
}
diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json
index 24acd77e316..0443710638c 100644
--- a/app/javascript/mastodon/locales/ar.json
+++ b/app/javascript/mastodon/locales/ar.json
@@ -110,7 +110,7 @@
"announcement.announcement": "إعلان",
"annual_report.summary.archetype.booster": "The cool-hunter",
"annual_report.summary.archetype.lurker": "المتصفح الصامت",
- "annual_report.summary.archetype.oracle": "حكيم",
+ "annual_report.summary.archetype.oracle": "الحكيم",
"annual_report.summary.archetype.pollster": "مستطلع للرأي",
"annual_report.summary.archetype.replier": "الفراشة الاجتماعية",
"annual_report.summary.followers.followers": "المُتابِعُون",
@@ -845,6 +845,7 @@
"status.bookmark": "أضفه إلى الفواصل المرجعية",
"status.cancel_reblog_private": "إلغاء إعادة النشر",
"status.cannot_reblog": "لا يمكن إعادة نشر هذا المنشور",
+ "status.context.load_new_replies": "الردود الجديدة المتاحة",
"status.continued_thread": "تكملة للخيط",
"status.copy": "انسخ رابط الرسالة",
"status.delete": "احذف",
diff --git a/app/javascript/mastodon/locales/da.json b/app/javascript/mastodon/locales/da.json
index 1cd633c7476..15597e9a279 100644
--- a/app/javascript/mastodon/locales/da.json
+++ b/app/javascript/mastodon/locales/da.json
@@ -324,7 +324,7 @@
"empty_column.follow_requests": "Du har endnu ingen følgeanmodninger. Når du modtager én, vil den dukke op her.",
"empty_column.followed_tags": "Ingen hashtags følges endnu. Når det sker, vil de fremgå her.",
"empty_column.hashtag": "Der er intet med dette hashtag endnu.",
- "empty_column.home": "Din hjemmetidslinje er tom! Følg nogle personer, for at fylde den op.",
+ "empty_column.home": "Din hjem-tidslinje er tom! Følg nogle personer, for at fylde den op.",
"empty_column.list": "Der er ikke noget på denne liste endnu. Når medlemmer af denne liste udgiver nye indlæg, vil de blive vist her.",
"empty_column.mutes": "Du har endnu ikke skjult nogle brugere.",
"empty_column.notification_requests": "Alt er klar! Der er intet her. Når der modtages nye notifikationer, fremgår de her jævnfør dine indstillinger.",
@@ -476,7 +476,7 @@
"keyboard_shortcuts.favourites": "Åbn favoritlisten",
"keyboard_shortcuts.federated": "Åbn fødereret tidslinje",
"keyboard_shortcuts.heading": "Tastaturgenveje",
- "keyboard_shortcuts.home": "Åbn hjemmetidslinje",
+ "keyboard_shortcuts.home": "Åbn hjem-tidslinje",
"keyboard_shortcuts.hotkey": "Hurtigtast",
"keyboard_shortcuts.legend": "Vis dette symbol",
"keyboard_shortcuts.local": "Åbn lokal tidslinje",
@@ -518,7 +518,7 @@
"lists.done": "Færdig",
"lists.edit": "Redigér liste",
"lists.exclusive": "Skjul medlemmer i Hjem",
- "lists.exclusive_hint": "Er nogen er på denne liste, skjul personen i hjemme-feeds for at undgå at se vedkommendes indlæg to gange.",
+ "lists.exclusive_hint": "Hvis nogen er på denne liste, så skjul dem i hjem-feed for at undgå at se deres indlæg to gange.",
"lists.find_users_to_add": "Find brugere at tilføje",
"lists.list_members_count": "{count, plural, one {# medlem} other {# medlemmer}}",
"lists.list_name": "Listetitel",
@@ -792,7 +792,7 @@
"report.thanks.title": "Ønsker ikke at se dette?",
"report.thanks.title_actionable": "Tak for anmeldelsen, der vil blive set nærmere på dette.",
"report.unfollow": "Følg ikke længere @{name}",
- "report.unfollow_explanation": "Du følger denne konto. For ikke længere at se vedkommendes indlæg i din hjemmestrøm, kan du stoppe med at følge dem.",
+ "report.unfollow_explanation": "Du følger denne konto. Hvis du ikke længere vil se vedkommendes indlæg i dit hjem-feed, så stop med at følge dem.",
"report_notification.attached_statuses": "{count, plural, one {{count} indlæg} other {{count} indlæg}} vedhæftet",
"report_notification.categories.legal": "Juridisk",
"report_notification.categories.legal_sentence": "ikke-tilladt indhold",
diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json
index 75f99fef4c8..fc34c0fe191 100644
--- a/app/javascript/mastodon/locales/de.json
+++ b/app/javascript/mastodon/locales/de.json
@@ -43,7 +43,7 @@
"account.followers": "Follower",
"account.followers.empty": "Diesem Profil folgt noch niemand.",
"account.followers_counter": "{count, plural, one {{counter} Follower} other {{counter} Follower}}",
- "account.followers_you_know_counter": "{counter} Follower kennst Du",
+ "account.followers_you_know_counter": "{counter} bekannt",
"account.following": "Folge ich",
"account.following_counter": "{count, plural, one {{counter} Folge ich} other {{counter} Folge ich}}",
"account.follows.empty": "Dieses Profil folgt noch niemandem.",
diff --git a/app/javascript/mastodon/locales/en-GB.json b/app/javascript/mastodon/locales/en-GB.json
index 441cfee6d10..31d1c71b0ef 100644
--- a/app/javascript/mastodon/locales/en-GB.json
+++ b/app/javascript/mastodon/locales/en-GB.json
@@ -1,7 +1,7 @@
{
"about.blocks": "Moderated servers",
"about.contact": "Contact:",
- "about.default_locale": "Default",
+ "about.default_locale": "Varsayılan",
"about.disclaimer": "Mastodon is free, open-source software, and a trademark of Mastodon gGmbH.",
"about.domain_blocks.no_reason_available": "Reason not available",
"about.domain_blocks.preamble": "Mastodon generally allows you to view content from and interact with users from any other server in the Fediverse. These are the exceptions that have been made on this particular server.",
@@ -9,11 +9,11 @@
"about.domain_blocks.silenced.title": "Limited",
"about.domain_blocks.suspended.explanation": "No data from this server will be processed, stored or exchanged, making any interaction or communication with users from this server impossible.",
"about.domain_blocks.suspended.title": "Suspended",
- "about.language_label": "Language",
+ "about.language_label": "Dil",
"about.not_available": "This information has not been made available on this server.",
"about.powered_by": "Decentralised social media powered by {mastodon}",
"about.rules": "Server rules",
- "account.account_note_header": "Personal note",
+ "account.account_note_header": "Kişisel not",
"account.add_or_remove_from_list": "Add or Remove from lists",
"account.badges.bot": "Automated",
"account.badges.group": "Group",
@@ -845,6 +845,8 @@
"status.bookmark": "Bookmark",
"status.cancel_reblog_private": "Unboost",
"status.cannot_reblog": "This post cannot be boosted",
+ "status.context.load_new_replies": "Yeni yanıtlar geldi.",
+ "status.context.loading": "Daha fazla yanıt kontrol ediliyor",
"status.continued_thread": "Continued thread",
"status.copy": "Copy link to status",
"status.delete": "Delete",
diff --git a/app/javascript/mastodon/locales/lt.json b/app/javascript/mastodon/locales/lt.json
index ed5fa83171e..6060cfeda3e 100644
--- a/app/javascript/mastodon/locales/lt.json
+++ b/app/javascript/mastodon/locales/lt.json
@@ -292,6 +292,7 @@
"emoji_button.search_results": "Paieškos rezultatai",
"emoji_button.symbols": "Simboliai",
"emoji_button.travel": "Kelionės ir vietos",
+ "empty_column.account_featured_other.unknown": "Ši paskyra dar nieko neparodė.",
"empty_column.account_hides_collections": "Šis (-i) naudotojas (-a) pasirinko nepadaryti šią informaciją prieinamą.",
"empty_column.account_suspended": "Paskyra pristabdyta.",
"empty_column.account_timeline": "Nėra čia įrašų.",
@@ -794,6 +795,8 @@
"status.bookmark": "Pridėti į žymės",
"status.cancel_reblog_private": "Nebepasidalinti",
"status.cannot_reblog": "Šis įrašas negali būti pakeltas.",
+ "status.context.load_new_replies": "Yra naujų atsakymų",
+ "status.context.loading": "Tikrinama dėl daugiau atsakymų",
"status.continued_thread": "Tęsiama gijoje",
"status.copy": "Kopijuoti nuorodą į įrašą",
"status.delete": "Ištrinti",
diff --git a/app/javascript/mastodon/locales/nan.json b/app/javascript/mastodon/locales/nan.json
index e7e8d2c0cd1..8120312d80f 100644
--- a/app/javascript/mastodon/locales/nan.json
+++ b/app/javascript/mastodon/locales/nan.json
@@ -845,6 +845,8 @@
"status.bookmark": "冊籤",
"status.cancel_reblog_private": "取消轉送",
"status.cannot_reblog": "Tsit篇PO文bē當轉送",
+ "status.context.load_new_replies": "有新ê回應",
+ "status.context.loading": "Leh檢查其他ê回應",
"status.continued_thread": "接續ê討論線",
"status.copy": "Khóo-pih PO文ê連結",
"status.delete": "Thâi掉",
diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json
index 091ddf8ca24..723207a4be4 100644
--- a/app/javascript/mastodon/locales/sk.json
+++ b/app/javascript/mastodon/locales/sk.json
@@ -27,6 +27,8 @@
"account.edit_profile": "Upraviť profil",
"account.enable_notifications": "Zapnúť upozornenia na príspevky od @{name}",
"account.endorse": "Zobraziť na vlastnom profile",
+ "account.familiar_followers_one": "Nasledovanie od {name1}",
+ "account.familiar_followers_two": "Nasledovanie od {name1} a {name2}",
"account.featured": "Zviditeľnené",
"account.featured.accounts": "Profily",
"account.featured.hashtags": "Hashtagy",
diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json
index c50ac3cbaa1..9a8354d052e 100644
--- a/app/javascript/mastodon/locales/tr.json
+++ b/app/javascript/mastodon/locales/tr.json
@@ -845,6 +845,8 @@
"status.bookmark": "Yer işareti ekle",
"status.cancel_reblog_private": "Yeniden paylaşımı geri al",
"status.cannot_reblog": "Bu gönderi yeniden paylaşılamaz",
+ "status.context.load_new_replies": "Yeni yanıtlar mevcut",
+ "status.context.loading": "Daha fazla yanıt için kontrol ediliyor",
"status.continued_thread": "Devam eden akış",
"status.copy": "Gönderi bağlantısını kopyala",
"status.delete": "Sil",
diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json
index c6e0aa7f276..127ae87f637 100644
--- a/app/javascript/mastodon/locales/zh-CN.json
+++ b/app/javascript/mastodon/locales/zh-CN.json
@@ -301,6 +301,9 @@
"emoji_button.search_results": "搜索结果",
"emoji_button.symbols": "符号",
"emoji_button.travel": "旅行与地点",
+ "empty_column.account_featured.me": "你尚未设置任何精选。你知道吗?你也可以将自己最常使用的话题标签,甚至是好友的帐号,在你的个人主页上设为精选。",
+ "empty_column.account_featured.other": "{acct} 尚未设置任何精选。你知道吗?你也可以将自己最常使用的话题标签,甚至是好友的帐号,在你的个人主页上设为精选。",
+ "empty_column.account_featured_other.unknown": "该用户尚未设置任何精选。",
"empty_column.account_hides_collections": "该用户选择不公开此信息",
"empty_column.account_suspended": "账号已被停用",
"empty_column.account_timeline": "这里没有嘟文!",
@@ -402,8 +405,10 @@
"hashtag.counter_by_accounts": "{count, plural,other {{counter} 人讨论}}",
"hashtag.counter_by_uses": "{count, plural, other {{counter} 条嘟文}}",
"hashtag.counter_by_uses_today": "今日 {count, plural, other {{counter} 条嘟文}}",
+ "hashtag.feature": "设为精选",
"hashtag.follow": "关注话题",
"hashtag.mute": "停止提醒 #{hashtag}",
+ "hashtag.unfeature": "取消精选",
"hashtag.unfollow": "取消关注话题",
"hashtags.and_other": "… 和另外 {count, plural, other {# 个话题}}",
"hints.profiles.followers_may_be_missing": "该账号的关注者列表可能没有完全显示。",
@@ -558,6 +563,7 @@
"navigation_bar.preferences": "偏好设置",
"navigation_bar.privacy_and_reach": "隐私与可达性",
"navigation_bar.search": "搜索",
+ "navigation_bar.search_trends": "搜索/热门趋势",
"navigation_panel.collapse_lists": "收起菜单列表",
"navigation_panel.expand_lists": "展开菜单列表",
"not_signed_in_indicator.not_signed_in": "你需要登录才能访问此资源。",
@@ -786,6 +792,7 @@
"report_notification.categories.violation": "违反规则",
"report_notification.categories.violation_sentence": "违反规则",
"report_notification.open": "打开举报",
+ "search.clear": "清空搜索内容",
"search.no_recent_searches": "无最近搜索",
"search.placeholder": "搜索",
"search.quick_action.account_search": "包含 {x} 的账号",
@@ -827,6 +834,8 @@
"status.bookmark": "添加到书签",
"status.cancel_reblog_private": "取消转嘟",
"status.cannot_reblog": "不能转嘟这条嘟文",
+ "status.context.load_new_replies": "有新回复",
+ "status.context.loading": "正在检查更多回复",
"status.continued_thread": "上接嘟文串",
"status.copy": "复制嘟文链接",
"status.delete": "删除",
@@ -854,8 +863,10 @@
"status.pin": "在个人资料页面置顶",
"status.quote_error.filtered": "已根据你的筛选器过滤",
"status.quote_error.not_found": "无法显示这篇贴文。",
+ "status.quote_error.pending_approval": "此嘟文正在等待原作者批准。",
"status.quote_error.rejected": "由于原作者不允许引用转发,无法显示这篇贴文。",
"status.quote_error.removed": "该帖子已被作者删除。",
+ "status.quote_error.unauthorized": "你无权查看此嘟文,因此无法显示。",
"status.quote_post_author": "{name} 的嘟文",
"status.read_more": "查看更多",
"status.reblog": "转嘟",
diff --git a/app/lib/admin/metrics/dimension/base_dimension.rb b/app/lib/admin/metrics/dimension/base_dimension.rb
index bd2e4ececbe..0e055e0e75e 100644
--- a/app/lib/admin/metrics/dimension/base_dimension.rb
+++ b/app/lib/admin/metrics/dimension/base_dimension.rb
@@ -65,4 +65,16 @@ class Admin::Metrics::Dimension::BaseDimension
def canonicalized_params
params.to_h.to_a.sort_by { |k, _v| k.to_s }.map { |k, v| "#{k}=#{v}" }.join(';')
end
+
+ def earliest_status_id
+ snowflake_id(@start_at.beginning_of_day)
+ end
+
+ def latest_status_id
+ snowflake_id(@end_at.end_of_day)
+ end
+
+ def snowflake_id(datetime)
+ Mastodon::Snowflake.id_at(datetime, with_random: false)
+ end
end
diff --git a/app/lib/admin/metrics/dimension/instance_languages_dimension.rb b/app/lib/admin/metrics/dimension/instance_languages_dimension.rb
index 661e6d93b75..5f4bb95cb8f 100644
--- a/app/lib/admin/metrics/dimension/instance_languages_dimension.rb
+++ b/app/lib/admin/metrics/dimension/instance_languages_dimension.rb
@@ -19,7 +19,7 @@ class Admin::Metrics::Dimension::InstanceLanguagesDimension < Admin::Metrics::Di
end
def sql_array
- [sql_query_string, { domain: params[:domain], earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }]
+ [sql_query_string, { domain: params[:domain], earliest_status_id:, latest_status_id:, limit: @limit }]
end
def sql_query_string
@@ -36,14 +36,6 @@ class Admin::Metrics::Dimension::InstanceLanguagesDimension < Admin::Metrics::Di
SQL
end
- def earliest_status_id
- Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
- end
-
- def latest_status_id
- Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
- end
-
def params
@params.permit(:domain)
end
diff --git a/app/lib/admin/metrics/dimension/servers_dimension.rb b/app/lib/admin/metrics/dimension/servers_dimension.rb
index 2c8406d52fd..7e3ab603d03 100644
--- a/app/lib/admin/metrics/dimension/servers_dimension.rb
+++ b/app/lib/admin/metrics/dimension/servers_dimension.rb
@@ -14,7 +14,7 @@ class Admin::Metrics::Dimension::ServersDimension < Admin::Metrics::Dimension::B
end
def sql_array
- [sql_query_string, { earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }]
+ [sql_query_string, { earliest_status_id:, latest_status_id:, limit: @limit }]
end
def sql_query_string
@@ -28,12 +28,4 @@ class Admin::Metrics::Dimension::ServersDimension < Admin::Metrics::Dimension::B
LIMIT :limit
SQL
end
-
- def earliest_status_id
- Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
- end
-
- def latest_status_id
- Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
- end
end
diff --git a/app/lib/admin/metrics/dimension/tag_languages_dimension.rb b/app/lib/admin/metrics/dimension/tag_languages_dimension.rb
index 6e283d2c655..b7b9abc8b6f 100644
--- a/app/lib/admin/metrics/dimension/tag_languages_dimension.rb
+++ b/app/lib/admin/metrics/dimension/tag_languages_dimension.rb
@@ -19,7 +19,7 @@ class Admin::Metrics::Dimension::TagLanguagesDimension < Admin::Metrics::Dimensi
end
def sql_array
- [sql_query_string, { tag_id: tag_id, earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }]
+ [sql_query_string, { tag_id: tag_id, earliest_status_id:, latest_status_id:, limit: @limit }]
end
def sql_query_string
@@ -39,14 +39,6 @@ class Admin::Metrics::Dimension::TagLanguagesDimension < Admin::Metrics::Dimensi
params[:id]
end
- def earliest_status_id
- Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
- end
-
- def latest_status_id
- Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
- end
-
def params
@params.permit(:id)
end
diff --git a/app/lib/admin/metrics/dimension/tag_servers_dimension.rb b/app/lib/admin/metrics/dimension/tag_servers_dimension.rb
index db820e965c5..29145e14871 100644
--- a/app/lib/admin/metrics/dimension/tag_servers_dimension.rb
+++ b/app/lib/admin/metrics/dimension/tag_servers_dimension.rb
@@ -18,7 +18,7 @@ class Admin::Metrics::Dimension::TagServersDimension < Admin::Metrics::Dimension
end
def sql_array
- [sql_query_string, { tag_id: tag_id, earliest_status_id: earliest_status_id, latest_status_id: latest_status_id, limit: @limit }]
+ [sql_query_string, { tag_id: tag_id, earliest_status_id:, latest_status_id:, limit: @limit }]
end
def sql_query_string
@@ -39,14 +39,6 @@ class Admin::Metrics::Dimension::TagServersDimension < Admin::Metrics::Dimension
params[:id]
end
- def earliest_status_id
- Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
- end
-
- def latest_status_id
- Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
- end
-
def params
@params.permit(:id)
end
diff --git a/app/lib/admin/metrics/measure/base_measure.rb b/app/lib/admin/metrics/measure/base_measure.rb
index 8b7fe39b55a..eabbe0890b6 100644
--- a/app/lib/admin/metrics/measure/base_measure.rb
+++ b/app/lib/admin/metrics/measure/base_measure.rb
@@ -104,4 +104,16 @@ class Admin::Metrics::Measure::BaseMeasure
def canonicalized_params
params.to_h.to_a.sort_by { |k, _v| k.to_s }.map { |k, v| "#{k}=#{v}" }.join(';')
end
+
+ def earliest_status_id
+ snowflake_id(@start_at.beginning_of_day)
+ end
+
+ def latest_status_id
+ snowflake_id(@end_at.end_of_day)
+ end
+
+ def snowflake_id(datetime)
+ Mastodon::Snowflake.id_at(datetime, with_random: false)
+ end
end
diff --git a/app/lib/admin/metrics/measure/instance_statuses_measure.rb b/app/lib/admin/metrics/measure/instance_statuses_measure.rb
index 324d427b18b..f0f797876e6 100644
--- a/app/lib/admin/metrics/measure/instance_statuses_measure.rb
+++ b/app/lib/admin/metrics/measure/instance_statuses_measure.rb
@@ -28,7 +28,7 @@ class Admin::Metrics::Measure::InstanceStatusesMeasure < Admin::Metrics::Measure
end
def sql_array
- [sql_query_string, { start_at: @start_at, end_at: @end_at, domain: params[:domain], earliest_status_id: earliest_status_id, latest_status_id: latest_status_id }]
+ [sql_query_string, { start_at: @start_at, end_at: @end_at, domain: params[:domain], earliest_status_id:, latest_status_id: }]
end
def sql_query_string
@@ -50,14 +50,6 @@ class Admin::Metrics::Measure::InstanceStatusesMeasure < Admin::Metrics::Measure
SQL
end
- def earliest_status_id
- Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
- end
-
- def latest_status_id
- Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
- end
-
def params
@params.permit(:domain, :include_subdomains)
end
diff --git a/app/lib/admin/metrics/measure/tag_servers_measure.rb b/app/lib/admin/metrics/measure/tag_servers_measure.rb
index 5db1076062b..e8d9cc43b8d 100644
--- a/app/lib/admin/metrics/measure/tag_servers_measure.rb
+++ b/app/lib/admin/metrics/measure/tag_servers_measure.rb
@@ -22,7 +22,7 @@ class Admin::Metrics::Measure::TagServersMeasure < Admin::Metrics::Measure::Base
end
def sql_array
- [sql_query_string, { start_at: @start_at, end_at: @end_at, tag_id: tag.id, earliest_status_id: earliest_status_id, latest_status_id: latest_status_id }]
+ [sql_query_string, { start_at: @start_at, end_at: @end_at, tag_id: tag.id, earliest_status_id:, latest_status_id: }]
end
def sql_query_string
@@ -45,14 +45,6 @@ class Admin::Metrics::Measure::TagServersMeasure < Admin::Metrics::Measure::Base
SQL
end
- def earliest_status_id
- Mastodon::Snowflake.id_at(@start_at.beginning_of_day, with_random: false)
- end
-
- def latest_status_id
- Mastodon::Snowflake.id_at(@end_at.end_of_day, with_random: false)
- end
-
def tag
@tag ||= Tag.find(params[:id])
end
diff --git a/app/lib/webfinger.rb b/app/lib/webfinger.rb
index 83b5415a33f..c39c25e994a 100644
--- a/app/lib/webfinger.rb
+++ b/app/lib/webfinger.rb
@@ -84,22 +84,18 @@ class Webfinger
def body_from_host_meta
host_meta_request.perform do |res|
- if res.code == 200
- body_from_webfinger(url_from_template(res.body_with_limit), use_fallback: false)
- else
- raise Webfinger::Error, "Request for #{@uri} returned HTTP #{res.code}"
- end
+ raise Webfinger::Error, "Request for #{@uri} returned HTTP #{res.code}" unless res.code == 200
+
+ body_from_webfinger(url_from_template(res.body_with_limit), use_fallback: false)
end
end
def url_from_template(str)
link = Nokogiri::XML(str).at_xpath('//xmlns:Link[@rel="lrdd"]')
- if link.present?
- link['template'].gsub('{uri}', @uri)
- else
- raise Webfinger::Error, "Request for #{@uri} returned host-meta without link to Webfinger"
- end
+ raise Webfinger::Error, "Request for #{@uri} returned host-meta without link to Webfinger" if link.blank?
+
+ link['template'].gsub('{uri}', @uri)
rescue Nokogiri::XML::XPath::SyntaxError
raise Webfinger::Error, "Invalid XML encountered in host-meta for #{@uri}"
end
diff --git a/app/lib/webfinger_resource.rb b/app/lib/webfinger_resource.rb
index e2027e164de..95de496a6d5 100644
--- a/app/lib/webfinger_resource.rb
+++ b/app/lib/webfinger_resource.rb
@@ -54,11 +54,9 @@ class WebfingerResource
end
def username_from_acct
- if domain_matches_local?
- local_username
- else
- raise ActiveRecord::RecordNotFound
- end
+ raise ActiveRecord::RecordNotFound unless domain_matches_local?
+
+ local_username
end
def split_acct
diff --git a/app/models/account_migration.rb b/app/models/account_migration.rb
index 7bda388f2a8..6cdc7128ef4 100644
--- a/app/models/account_migration.rb
+++ b/app/models/account_migration.rb
@@ -74,7 +74,7 @@ class AccountMigration < ApplicationRecord
errors.add(:acct, I18n.t('migrations.errors.not_found'))
else
errors.add(:acct, I18n.t('migrations.errors.missing_also_known_as')) unless target_account.also_known_as.include?(ActivityPub::TagManager.instance.uri_for(account))
- errors.add(:acct, I18n.t('migrations.errors.already_moved')) if account.moved_to_account_id.present? && account.moved_to_account_id == target_account.id
+ errors.add(:acct, I18n.t('migrations.errors.already_moved')) if account.moved? && account.moved_to_account_id == target_account.id
errors.add(:acct, I18n.t('migrations.errors.move_to_self')) if account.id == target_account.id
end
end
diff --git a/app/models/announcement_reaction.rb b/app/models/announcement_reaction.rb
index 46d9fc290ff..855dfc9e3a6 100644
--- a/app/models/announcement_reaction.rb
+++ b/app/models/announcement_reaction.rb
@@ -14,7 +14,7 @@
#
class AnnouncementReaction < ApplicationRecord
- before_validation :set_custom_emoji
+ before_validation :set_custom_emoji, if: :name?
after_commit :queue_publish
belongs_to :account
@@ -27,7 +27,7 @@ class AnnouncementReaction < ApplicationRecord
private
def set_custom_emoji
- self.custom_emoji = CustomEmoji.local.enabled.find_by(shortcode: name) if name.present?
+ self.custom_emoji = CustomEmoji.local.enabled.find_by(shortcode: name)
end
def queue_publish
diff --git a/app/models/concerns/rate_limitable.rb b/app/models/concerns/rate_limitable.rb
index ad1b5e44e36..c6b5d3e0844 100644
--- a/app/models/concerns/rate_limitable.rb
+++ b/app/models/concerns/rate_limitable.rb
@@ -3,12 +3,8 @@
module RateLimitable
extend ActiveSupport::Concern
- def rate_limit=(value)
- @rate_limit = value
- end
-
- def rate_limit?
- @rate_limit
+ included do
+ attribute :rate_limit, :boolean, default: false
end
def rate_limiter(by, options = {})
diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb
index 964d4e279a1..0b518036b10 100644
--- a/app/models/follow_request.rb
+++ b/app/models/follow_request.rb
@@ -37,7 +37,7 @@ class FollowRequest < ApplicationRecord
if account.local?
ListAccount.where(follow_request: self).update_all(follow_request_id: nil, follow_id: follow.id)
MergeWorker.perform_async(target_account.id, account.id, 'home')
- MergeWorker.push_bulk(List.where(account: account).joins(:list_accounts).where(list_accounts: { account_id: target_account.id }).pluck(:id)) do |list_id|
+ MergeWorker.push_bulk(account.owned_lists.with_list_account(target_account).pluck(:id)) do |list_id|
[target_account.id, list_id, 'list']
end
end
diff --git a/app/models/form/redirect.rb b/app/models/form/redirect.rb
index c5b3c1f8f39..6ab95f21f1f 100644
--- a/app/models/form/redirect.rb
+++ b/app/models/form/redirect.rb
@@ -40,7 +40,7 @@ class Form::Redirect
if target_account.nil?
errors.add(:acct, I18n.t('migrations.errors.not_found'))
else
- errors.add(:acct, I18n.t('migrations.errors.already_moved')) if account.moved_to_account_id.present? && account.moved_to_account_id == target_account.id
+ errors.add(:acct, I18n.t('migrations.errors.already_moved')) if account.moved? && account.moved_to_account_id == target_account.id
errors.add(:acct, I18n.t('migrations.errors.move_to_self')) if account.id == target_account.id
end
end
diff --git a/app/models/list.rb b/app/models/list.rb
index 76c116ce244..8fd1953ab31 100644
--- a/app/models/list.rb
+++ b/app/models/list.rb
@@ -32,6 +32,8 @@ class List < ApplicationRecord
before_destroy :clean_feed_manager
+ scope :with_list_account, ->(account) { joins(:list_accounts).where(list_accounts: { account: }) }
+
private
def validate_account_lists_limit
diff --git a/app/models/preview_card.rb b/app/models/preview_card.rb
index 56fe4836355..8e0e13cdb94 100644
--- a/app/models/preview_card.rb
+++ b/app/models/preview_card.rb
@@ -170,10 +170,9 @@ class PreviewCard < ApplicationRecord
private
def serialized_authors
- if author_name? || author_url? || author_account_id?
- PreviewCard::Author
- .new(self)
- end
+ return unless author_name? || author_url? || author_account_id?
+
+ PreviewCard::Author.new(self)
end
def extract_dimensions
diff --git a/app/models/tag.rb b/app/models/tag.rb
index 8e21ddca82b..dff10111123 100644
--- a/app/models/tag.rb
+++ b/app/models/tag.rb
@@ -164,9 +164,10 @@ class Tag < ApplicationRecord
end
def validate_display_name_change
- unless HashtagNormalizer.new.normalize(display_name).casecmp(name).zero?
- errors.add(:display_name,
- I18n.t('tags.does_not_match_previous_name'))
- end
+ errors.add(:display_name, I18n.t('tags.does_not_match_previous_name')) unless display_name_matches_name?
+ end
+
+ def display_name_matches_name?
+ HashtagNormalizer.new.normalize(display_name).casecmp(name).zero?
end
end
diff --git a/app/models/user.rb b/app/models/user.rb
index 762522f2822..2ba8c2926d9 100644
--- a/app/models/user.rb
+++ b/app/models/user.rb
@@ -466,16 +466,17 @@ class User < ApplicationRecord
yield
- if new_user
- # Avoid extremely unlikely race condition when approving and confirming
- # the user at the same time
- reload unless approved?
+ after_confirmation_tasks if new_user
+ end
- if approved?
- prepare_new_user!
- else
- notify_staff_about_pending_account!
- end
+ def after_confirmation_tasks
+ # Handle condition when approving and confirming a user at the same time
+ reload unless approved?
+
+ if approved?
+ prepare_new_user!
+ else
+ notify_staff_about_pending_account!
end
end
@@ -539,10 +540,10 @@ class User < ApplicationRecord
def regenerate_feed!
home_feed = HomeFeed.new(account)
- unless home_feed.regenerating?
- home_feed.regeneration_in_progress!
- RegenerationWorker.perform_async(account_id)
- end
+ return if home_feed.regenerating?
+
+ home_feed.regeneration_in_progress!
+ RegenerationWorker.perform_async(account_id)
end
def needs_feed_update?
diff --git a/app/models/worker_batch.rb b/app/models/worker_batch.rb
index f741071ba95..0a22ce61419 100644
--- a/app/models/worker_batch.rb
+++ b/app/models/worker_batch.rb
@@ -19,17 +19,22 @@ class WorkerBatch
redis.hset(key, { 'async_refresh_key' => async_refresh_key, 'threshold' => threshold })
end
+ def within
+ raise NoBlockGivenError unless block_given?
+
+ begin
+ Thread.current[:batch] = self
+ yield
+ ensure
+ Thread.current[:batch] = nil
+ end
+ end
+
# Add jobs to the batch. Usually when the batch is created.
# @param [Array] jids
def add_jobs(jids)
if jids.blank?
- async_refresh_key = redis.hget(key, 'async_refresh_key')
-
- if async_refresh_key.present?
- async_refresh = AsyncRefresh.new(async_refresh_key)
- async_refresh.finish!
- end
-
+ finish!
return
end
@@ -55,8 +60,23 @@ class WorkerBatch
if async_refresh_key.present?
async_refresh = AsyncRefresh.new(async_refresh_key)
async_refresh.increment_result_count(by: 1)
- async_refresh.finish! if pending.zero? || processed >= threshold.to_f * (processed + pending)
end
+
+ if pending.zero? || processed >= (threshold || 1.0).to_f * (processed + pending)
+ async_refresh&.finish!
+ cleanup
+ end
+ end
+
+ def finish!
+ async_refresh_key = redis.hget(key, 'async_refresh_key')
+
+ if async_refresh_key.present?
+ async_refresh = AsyncRefresh.new(async_refresh_key)
+ async_refresh.finish!
+ end
+
+ cleanup
end
# Get pending jobs.
@@ -76,4 +96,8 @@ class WorkerBatch
def key(suffix = nil)
"worker_batch:#{@id}#{":#{suffix}" if suffix}"
end
+
+ def cleanup
+ redis.del(key, key('jobs'))
+ end
end
diff --git a/app/services/activitypub/fetch_replies_service.rb b/app/services/activitypub/fetch_replies_service.rb
index 25eb275ca5c..239df0ba584 100644
--- a/app/services/activitypub/fetch_replies_service.rb
+++ b/app/services/activitypub/fetch_replies_service.rb
@@ -17,7 +17,12 @@ class ActivityPub::FetchRepliesService < BaseService
batch = WorkerBatch.new
batch.connect(async_refresh_key) if async_refresh_key.present?
- batch.add_jobs(FetchReplyWorker.push_bulk(@items) { |reply_uri| [reply_uri, { 'request_id' => request_id, 'batch_id' => batch.id }] })
+ batch.finish! if @items.empty?
+ batch.within do
+ FetchReplyWorker.push_bulk(@items) do |reply_uri|
+ [reply_uri, { 'request_id' => request_id, 'batch_id' => batch.id }]
+ end
+ end
[@items, n_pages]
end
diff --git a/app/services/follow_service.rb b/app/services/follow_service.rb
index 2928c85390a..5ff1b63503c 100644
--- a/app/services/follow_service.rb
+++ b/app/services/follow_service.rb
@@ -82,7 +82,7 @@ class FollowService < BaseService
LocalNotificationWorker.perform_async(@target_account.id, follow.id, follow.class.name, 'follow')
MergeWorker.perform_async(@target_account.id, @source_account.id, 'home')
- MergeWorker.push_bulk(List.where(account: @source_account).joins(:list_accounts).where(list_accounts: { account_id: @target_account.id }).pluck(:id)) do |list_id|
+ MergeWorker.push_bulk(@source_account.owned_lists.with_list_account(@target_account).pluck(:id)) do |list_id|
[@target_account.id, list_id, 'list']
end
diff --git a/app/services/unfollow_service.rb b/app/services/unfollow_service.rb
index b3f2cd66f67..1ea8af69926 100644
--- a/app/services/unfollow_service.rb
+++ b/app/services/unfollow_service.rb
@@ -34,7 +34,7 @@ class UnfollowService < BaseService
unless @options[:skip_unmerge]
UnmergeWorker.perform_async(@target_account.id, @source_account.id, 'home')
- UnmergeWorker.push_bulk(List.where(account: @source_account).joins(:list_accounts).where(list_accounts: { account_id: @target_account.id }).pluck(:list_id)) do |list_id|
+ UnmergeWorker.push_bulk(@source_account.owned_lists.with_list_account(@target_account).pluck(:list_id)) do |list_id|
[@target_account.id, list_id, 'list']
end
end
diff --git a/app/services/unmute_service.rb b/app/services/unmute_service.rb
index 9262961f7d7..0a9604bae2e 100644
--- a/app/services/unmute_service.rb
+++ b/app/services/unmute_service.rb
@@ -9,7 +9,7 @@ class UnmuteService < BaseService
if account.following?(target_account)
MergeWorker.perform_async(target_account.id, account.id, 'home')
- MergeWorker.push_bulk(List.where(account: account).joins(:list_accounts).where(list_accounts: { account_id: target_account.id }).pluck(:id)) do |list_id|
+ MergeWorker.push_bulk(account.owned_lists.with_list_account(target_account).pluck(:id)) do |list_id|
[target_account.id, list_id, 'list']
end
end
diff --git a/app/views/admin/accounts/_counters.html.haml b/app/views/admin/accounts/_counters.html.haml
index 00ab98d094a..3c99da9f2c8 100644
--- a/app/views/admin/accounts/_counters.html.haml
+++ b/app/views/admin/accounts/_counters.html.haml
@@ -30,9 +30,9 @@
= t('admin.accounts.suspended')
- elsif account.silenced?
= t('admin.accounts.silenced')
- - elsif account.local? && account.user&.disabled?
+ - elsif account.local? && account.user_disabled?
= t('admin.accounts.disabled')
- - elsif account.local? && !account.user&.confirmed?
+ - elsif account.local? && !account.user_confirmed?
= t('admin.accounts.confirming')
- elsif account.local? && !account.user_approved?
= t('admin.accounts.pending')
diff --git a/app/views/admin/accounts/_local_account.html.haml b/app/views/admin/accounts/_local_account.html.haml
index 892afcc5428..bff752332c6 100644
--- a/app/views/admin/accounts/_local_account.html.haml
+++ b/app/views/admin/accounts/_local_account.html.haml
@@ -34,7 +34,7 @@
%tr
%th= t('admin.accounts.email_status')
%td
- - if account.user&.confirmed?
+ - if account.user_confirmed?
= t('admin.accounts.confirmed')
- else
= t('admin.accounts.confirming')
diff --git a/app/views/admin/reports/index.html.haml b/app/views/admin/reports/index.html.haml
index 6f762d94ebf..44664b85fd9 100644
--- a/app/views/admin/reports/index.html.haml
+++ b/app/views/admin/reports/index.html.haml
@@ -42,7 +42,7 @@
%span.red= t('admin.accounts.suspended')
- elsif target_account.silenced?
%span.red= t('admin.accounts.silenced')
- - elsif target_account.user&.disabled?
+ - elsif target_account.user_disabled?
%span.red= t('admin.accounts.disabled')
- else
%span.neutral= t('admin.accounts.no_limits_imposed')
diff --git a/app/workers/move_worker.rb b/app/workers/move_worker.rb
index a18f38556bb..58d20ba94b3 100644
--- a/app/workers/move_worker.rb
+++ b/app/workers/move_worker.rb
@@ -81,7 +81,7 @@ class MoveWorker
def copy_account_notes!
AccountNote.where(target_account: @source_account).find_each do |note|
- text = I18n.with_locale(note.account.user&.locale.presence || I18n.default_locale) do
+ text = I18n.with_locale(note.account.user_locale.presence || I18n.default_locale) do
I18n.t('move_handler.copy_account_note_text', acct: @source_account.acct)
end
@@ -104,7 +104,7 @@ class MoveWorker
def carry_blocks_over!
@source_account.blocked_by_relationships.where(account: Account.local).find_each do |block|
- unless block.account.blocking?(@target_account) || block.account.following?(@target_account)
+ unless skip_block_move?(block)
BlockService.new.call(block.account, @target_account)
add_account_note_if_needed!(block.account, 'move_handler.carry_blocks_over_text')
end
@@ -115,7 +115,7 @@ class MoveWorker
def carry_mutes_over!
@source_account.muted_by_relationships.where(account: Account.local).find_each do |mute|
- MuteService.new.call(mute.account, @target_account, notifications: mute.hide_notifications) unless mute.account.muting?(@target_account) || mute.account.following?(@target_account)
+ MuteService.new.call(mute.account, @target_account, notifications: mute.hide_notifications) unless skip_mute_move?(mute)
add_account_note_if_needed!(mute.account, 'move_handler.carry_mutes_over_text')
rescue => e
@deferred_error = e
@@ -123,11 +123,19 @@ class MoveWorker
end
def add_account_note_if_needed!(account, id)
- unless AccountNote.exists?(account: account, target_account: @target_account)
- text = I18n.with_locale(account.user&.locale.presence || I18n.default_locale) do
- I18n.t(id, acct: @source_account.acct)
- end
- AccountNote.create!(account: account, target_account: @target_account, comment: text)
+ return if AccountNote.exists?(account: account, target_account: @target_account)
+
+ text = I18n.with_locale(account.user_locale.presence || I18n.default_locale) do
+ I18n.t(id, acct: @source_account.acct)
end
+ AccountNote.create!(account: account, target_account: @target_account, comment: text)
+ end
+
+ def skip_mute_move?(mute)
+ mute.account.muting?(@target_account) || mute.account.following?(@target_account)
+ end
+
+ def skip_block_move?(block)
+ block.account.blocking?(@target_account) || block.account.following?(@target_account)
end
end
diff --git a/app/workers/publish_scheduled_status_worker.rb b/app/workers/publish_scheduled_status_worker.rb
index 0ec081de917..bcf20b49431 100644
--- a/app/workers/publish_scheduled_status_worker.rb
+++ b/app/workers/publish_scheduled_status_worker.rb
@@ -9,7 +9,7 @@ class PublishScheduledStatusWorker
scheduled_status = ScheduledStatus.find(scheduled_status_id)
scheduled_status.destroy!
- return true if scheduled_status.account.user.disabled?
+ return true if scheduled_status.account.user_disabled?
PostStatusService.new.call(
scheduled_status.account,
diff --git a/config/initializers/sidekiq.rb b/config/initializers/sidekiq.rb
index 3c2f12780c0..7edaf38a60a 100644
--- a/config/initializers/sidekiq.rb
+++ b/config/initializers/sidekiq.rb
@@ -1,6 +1,7 @@
# frozen_string_literal: true
require_relative '../../lib/mastodon/sidekiq_middleware'
+require_relative '../../lib/mastodon/worker_batch_middleware'
Sidekiq.configure_server do |config|
config.redis = REDIS_CONFIGURATION.sidekiq
@@ -72,14 +73,12 @@ Sidekiq.configure_server do |config|
config.server_middleware do |chain|
chain.add Mastodon::SidekiqMiddleware
- end
-
- config.server_middleware do |chain|
chain.add SidekiqUniqueJobs::Middleware::Server
end
config.client_middleware do |chain|
chain.add SidekiqUniqueJobs::Middleware::Client
+ chain.add Mastodon::WorkerBatchMiddleware
end
config.on(:startup) do
@@ -105,6 +104,7 @@ Sidekiq.configure_client do |config|
config.client_middleware do |chain|
chain.add SidekiqUniqueJobs::Middleware::Client
+ chain.add Mastodon::WorkerBatchMiddleware
end
config.logger.level = Logger.const_get(ENV.fetch('RAILS_LOG_LEVEL', 'info').upcase.to_s)
diff --git a/config/locales/br.yml b/config/locales/br.yml
index e5b9ff2559c..4bfd268d15a 100644
--- a/config/locales/br.yml
+++ b/config/locales/br.yml
@@ -560,6 +560,8 @@ br:
one: "%{count} skeudenn"
other: "%{count} skeudenn"
two: "%{count} skeudenn"
+ errors:
+ quoted_status_not_found: War a seblant, n'eus ket eus an embannadenn emaoc'h o klask menegiñ.
pin_errors:
ownership: N'hallit ket spilhennañ embannadurioù ar re all
quote_policies:
diff --git a/config/locales/ca.yml b/config/locales/ca.yml
index 1c858fe4cde..a3e62d7d721 100644
--- a/config/locales/ca.yml
+++ b/config/locales/ca.yml
@@ -1870,6 +1870,7 @@ ca:
edited_at_html: Editat %{date}
errors:
in_reply_not_found: El tut al qual intentes respondre sembla que no existeix.
+ quoted_status_not_found: Sembla que la publicació que vols citar no existeix.
over_character_limit: Límit de caràcters de %{max} superat
pin_errors:
direct: Els tuts que només són visibles per als usuaris mencionats no poden ser fixats
diff --git a/config/locales/cs.yml b/config/locales/cs.yml
index 1ad8df6e71f..7df98b90bbf 100644
--- a/config/locales/cs.yml
+++ b/config/locales/cs.yml
@@ -1958,6 +1958,7 @@ cs:
edited_at_html: Upraven %{date}
errors:
in_reply_not_found: Příspěvek, na který se pokoušíte odpovědět, neexistuje.
+ quoted_status_not_found: Zdá se, že příspěvek, který se pokoušíte citovat neexistuje.
over_character_limit: byl překročen limit %{max} znaků
pin_errors:
direct: Příspěvky viditelné pouze zmíněným uživatelům nelze připnout
diff --git a/config/locales/cy.yml b/config/locales/cy.yml
index d7242fbf2e1..ab45a600128 100644
--- a/config/locales/cy.yml
+++ b/config/locales/cy.yml
@@ -2043,6 +2043,7 @@ cy:
edited_at_html: Wedi'i olygu %{date}
errors:
in_reply_not_found: Nid yw'n ymddangos bod y postiad rydych chi'n ceisio ei ateb yn bodoli.
+ quoted_status_not_found: Nid yw'n ymddangos bod y postiad rydych chi'n ceisio'i ddyfynnu yn bodoli.
over_character_limit: wedi mynd y tu hwnt i'r terfyn nodau o %{max}
pin_errors:
direct: Nid oes modd pinio postiadau sy'n weladwy i ddefnyddwyr a grybwyllwyd yn unig
diff --git a/config/locales/da.yml b/config/locales/da.yml
index b822ae9cb17..d2f6753181b 100644
--- a/config/locales/da.yml
+++ b/config/locales/da.yml
@@ -1394,7 +1394,7 @@ da:
filters:
contexts:
account: Profiler
- home: Hjemmetidslinje
+ home: Hjem og lister
notifications: Notifikationer
public: Offentlig tidslinje
thread: Samtaler
@@ -1872,6 +1872,7 @@ da:
edited_at_html: Redigeret %{date}
errors:
in_reply_not_found: Indlægget, der forsøges besvaret, ser ikke ud til at eksistere.
+ quoted_status_not_found: Indlægget, du forsøger at citere, ser ikke ud til at eksistere.
over_character_limit: grænsen på %{max} tegn overskredet
pin_errors:
direct: Indlæg, som kun kan ses af omtalte brugere, kan ikke fastgøres
diff --git a/config/locales/de.yml b/config/locales/de.yml
index 1eb5ef1c4f9..4b29dbac5de 100644
--- a/config/locales/de.yml
+++ b/config/locales/de.yml
@@ -1872,6 +1872,7 @@ de:
edited_at_html: 'Bearbeitet: %{date}'
errors:
in_reply_not_found: Der Beitrag, auf den du antworten möchtest, scheint nicht zu existieren.
+ quoted_status_not_found: Der Beitrag, den du zitieren möchtest, scheint nicht zu existieren.
over_character_limit: Begrenzung von %{max} Zeichen überschritten
pin_errors:
direct: Beiträge, die nur für erwähnte Profile sichtbar sind, können nicht angeheftet werden
diff --git a/config/locales/el.yml b/config/locales/el.yml
index f7c3df0f54e..b1df6962ecf 100644
--- a/config/locales/el.yml
+++ b/config/locales/el.yml
@@ -1872,6 +1872,7 @@ el:
edited_at_html: Επεξεργάστηκε στις %{date}
errors:
in_reply_not_found: Η ανάρτηση στην οποία προσπαθείς να απαντήσεις δεν φαίνεται να υπάρχει.
+ quoted_status_not_found: Η ανάρτηση την οποία προσπαθείς να παραθέσεις δεν φαίνεται να υπάρχει.
over_character_limit: υπέρβαση μέγιστου ορίου %{max} χαρακτήρων
pin_errors:
direct: Αναρτήσεις που είναι ορατές μόνο στους αναφερόμενους χρήστες δεν μπορούν να καρφιτσωθούν
diff --git a/config/locales/es-AR.yml b/config/locales/es-AR.yml
index 3b26eebd294..93d4c3c31ad 100644
--- a/config/locales/es-AR.yml
+++ b/config/locales/es-AR.yml
@@ -1872,6 +1872,7 @@ es-AR:
edited_at_html: Editado el %{date}
errors:
in_reply_not_found: El mensaje al que intentás responder no existe.
+ quoted_status_not_found: El mensaje al que intentás citar parece que no existe.
over_character_limit: se excedió el límite de %{max} caracteres
pin_errors:
direct: Los mensajes que sólo son visibles para los usuarios mencionados no pueden ser fijados
diff --git a/config/locales/es-MX.yml b/config/locales/es-MX.yml
index cfc573fab8d..e839a605e15 100644
--- a/config/locales/es-MX.yml
+++ b/config/locales/es-MX.yml
@@ -1872,6 +1872,7 @@ es-MX:
edited_at_html: Editado %{date}
errors:
in_reply_not_found: La publicación a la que estás intentando responder no existe.
+ quoted_status_not_found: La publicación que intentas citar no parece existir.
over_character_limit: Límite de caracteres de %{max} superado
pin_errors:
direct: Las publicaciones que son visibles solo para los usuarios mencionados no pueden fijarse
diff --git a/config/locales/es.yml b/config/locales/es.yml
index c2aabea9f55..c0adb8fa6ce 100644
--- a/config/locales/es.yml
+++ b/config/locales/es.yml
@@ -1872,6 +1872,7 @@ es:
edited_at_html: Editado %{date}
errors:
in_reply_not_found: La publicación a la que intentas responder no existe.
+ quoted_status_not_found: La publicación que estás intentando citar no existe.
over_character_limit: Límite de caracteres de %{max} superado
pin_errors:
direct: Las publicaciones que son visibles solo para los usuarios mencionados no pueden fijarse
diff --git a/config/locales/fo.yml b/config/locales/fo.yml
index 23a6b589765..9af87098a42 100644
--- a/config/locales/fo.yml
+++ b/config/locales/fo.yml
@@ -1872,6 +1872,7 @@ fo:
edited_at_html: Rættað %{date}
errors:
in_reply_not_found: Posturin, sum tú roynir at svara, sýnist ikki at finnast.
+ quoted_status_not_found: Posturin, sum tú roynir at sitera, sýnist ikki at finnast.
over_character_limit: mesta tal av teknum, %{max}, rokkið
pin_errors:
direct: Postar, sum einans eru sjónligir hjá nevndum brúkarum, kunnu ikki festast
diff --git a/config/locales/gl.yml b/config/locales/gl.yml
index d19d47ab26b..0b3a4133f55 100644
--- a/config/locales/gl.yml
+++ b/config/locales/gl.yml
@@ -1872,6 +1872,7 @@ gl:
edited_at_html: Editado %{date}
errors:
in_reply_not_found: A publicación á que tentas responder semella que non existe.
+ quoted_status_not_found: Parece que a publicación que intentas citar non existe.
over_character_limit: Excedeu o límite de caracteres %{max}
pin_errors:
direct: As publicacións que só son visibles para as usuarias mencionadas non se poden fixar
diff --git a/config/locales/he.yml b/config/locales/he.yml
index 50711387649..ed80dfd6c99 100644
--- a/config/locales/he.yml
+++ b/config/locales/he.yml
@@ -1957,7 +1957,8 @@ he:
two: 'מכיל את התגיות האסורות: %{tags}'
edited_at_html: נערך ב-%{date}
errors:
- in_reply_not_found: נראה שההודעה שאת/ה מנסה להגיב לה לא קיימת.
+ in_reply_not_found: נראה שההודעה שנסית להגיב לה לא קיימת.
+ quoted_status_not_found: נראה שההודעה שנסית לצטט לא קיימת.
over_character_limit: חריגה מגבול התווים של %{max}
pin_errors:
direct: לא ניתן לקבע הודעות שנראותן מוגבלת למכותבים בלבד
diff --git a/config/locales/hu.yml b/config/locales/hu.yml
index b46062b2c7b..ddb1841f1eb 100644
--- a/config/locales/hu.yml
+++ b/config/locales/hu.yml
@@ -1872,6 +1872,7 @@ hu:
edited_at_html: 'Szerkesztve: %{date}'
errors:
in_reply_not_found: Már nem létezik az a bejegyzés, melyre válaszolni szeretnél.
+ quoted_status_not_found: Már nem létezik az a bejegyzés, amelyből idézni szeretnél.
over_character_limit: túllépted a maximális %{max} karakteres keretet
pin_errors:
direct: A csak a megemlített felhasználók számára látható bejegyzések nem tűzhetők ki
diff --git a/config/locales/is.yml b/config/locales/is.yml
index 5e33189cfb2..2e47f3245a9 100644
--- a/config/locales/is.yml
+++ b/config/locales/is.yml
@@ -1876,6 +1876,7 @@ is:
edited_at_html: Breytt %{date}
errors:
in_reply_not_found: Færslan sem þú ert að reyna að svara að er líklega ekki til.
+ quoted_status_not_found: Færslan sem þú ert að reyna að vitna í virðist ekki vera til.
over_character_limit: hámarksfjölda stafa (%{max}) náð
pin_errors:
direct: Ekki er hægt að festa færslur sem einungis eru sýnilegar þeim notendum sem minnst er á
diff --git a/config/locales/lt.yml b/config/locales/lt.yml
index 41cf7cae96f..3b31b138b8f 100644
--- a/config/locales/lt.yml
+++ b/config/locales/lt.yml
@@ -912,6 +912,10 @@ lt:
your_appeal_rejected: Tavo apeliacija buvo atmesta
edit_profile:
hint_html: "Tinkink tai, ką žmonės mato tavo viešame profilyje ir šalia įrašų. Kiti žmonės labiau linkę sekti atgal ir bendrauti su tavimi, jei tavo profilis yra užpildytas ir turi profilio nuotrauką."
+ emoji_styles:
+ auto: Automatinis
+ native: Vietiniai
+ twemoji: Tvejaustukai
errors:
'403': Jūs neturie prieigos matyti šiam puslapiui.
'404': Puslapis nerastas.
@@ -1183,6 +1187,8 @@ lt:
other: "%{count} vaizdų"
boosted_from_html: Pakelta iš %{acct_link}
content_warning: 'Turinio įspėjimas: %{warning}'
+ errors:
+ quoted_status_not_found: Įrašas, kurį bandote cituoti, atrodo, neegzistuoja.
over_character_limit: pasiektas %{max} simbolių limitas
pin_errors:
limit: Jūs jau prisegėte maksimalų toot'ų skaičų
diff --git a/config/locales/nan.yml b/config/locales/nan.yml
index 354ef8b0f85..007ca847a81 100644
--- a/config/locales/nan.yml
+++ b/config/locales/nan.yml
@@ -97,7 +97,7 @@ nan:
silenced: 受限制
suspended: 權限中止ah
title: 管理
- moderation_notes: 管理ê註釋
+ moderation_notes: 管理ê筆記
most_recent_activity: 最近ê活動時間
most_recent_ip: 最近ê IP
no_account_selected: 因為無揀任何口座,所以lóng無改變
@@ -245,7 +245,7 @@ nan:
create_announcement_html: "%{name} kā公告 %{target} 建立ah"
create_canonical_email_block_html: "%{name} kā hash是 %{target} ê電子phue封鎖ah"
create_custom_emoji_html: "%{name} kā 新ê emoji %{target} 傳上去ah"
- create_domain_allow_html: "%{name} 允准 %{target} 域名加入聯邦宇宙"
+ create_domain_allow_html: "%{name} 允准 %{target} 域名加入聯邦"
create_domain_block_html: "%{name} 封鎖域名 %{target}"
create_email_domain_block_html: "%{name} kā 電子phue域名 %{target} 封鎖ah"
create_ip_block_html: "%{name} 建立 IP %{target} ê規則"
@@ -256,7 +256,7 @@ nan:
destroy_announcement_html: "%{name} kā公告 %{target} thâi掉ah"
destroy_canonical_email_block_html: "%{name} kā hash是 %{target} ê電子phue取消封鎖ah"
destroy_custom_emoji_html: "%{name} kā 新ê emoji %{target} thâi掉ah"
- destroy_domain_allow_html: "%{name} 無允准 %{target} 域名加入聯邦宇宙"
+ destroy_domain_allow_html: "%{name} 無允准 %{target} 域名加入聯邦"
destroy_domain_block_html: "%{name} 取消封鎖域名 %{target}"
destroy_email_domain_block_html: "%{name} kā 電子phue域名 %{target} 取消封鎖ah"
destroy_instance_html: "%{name} 清除域名 %{target}"
@@ -531,16 +531,111 @@ nan:
content_policies:
comment: 內部ê筆記
description_html: Lí ē當定義用tī所有tuì tsit ê域名kap伊ê子域名來ê口座ê內容政策。
+ limited_federation_mode_description_html: Lí通選擇kám beh允准tsit ê域名加入聯邦。
+ policies:
+ reject_media: 拒絕媒體
+ reject_reports: 拒絕檢舉
+ silence: 限制
+ suspend: 中止權限
+ policy: 政策
+ reason: 公開ê理由
+ title: 內容政策
dashboard:
+ instance_accounts_dimension: 上tsē lâng跟tuè ê口座
+ instance_accounts_measure: 儲存ê口座
+ instance_followers_measure: lán tī hia ê跟tuè者
+ instance_follows_measure: in tī tsia ê跟tuè者
instance_languages_dimension: Tsia̍p用ê語言
+ instance_media_attachments_measure: 儲存ê媒體附件
+ instance_reports_measure: 關係in ê檢舉
+ instance_statuses_measure: 儲存ê PO文
+ delivery:
+ all: 全部
+ clear: 清寄送ê錯誤
+ failing: 失敗
+ restart: 重頭啟動寄送
+ stop: 停止寄送
+ unavailable: Bē當用
+ delivery_available: 通寄送
+ delivery_error_days: 寄送錯誤ê日數
+ delivery_error_hint: Nā連續 %{count} kang bē當寄送,就ē自動標做bē當寄送。
+ destroyed_msg: Tuì %{domain} 來ê資料,teh排隊beh thâi掉。
+ empty: Tshuē無域名。
+ known_accounts:
+ other: "%{count} ê知影ê口座"
+ moderation:
+ all: 全部
+ limited: 受限制
+ title: 管理
+ moderation_notes:
+ create: 加添管理筆記
+ created_msg: 站臺ê管理記錄成功建立!
+ description_html: 檢視á是替別ê管理者kap未來ê家己留筆記
+ destroyed_msg: 站臺ê管理記錄成功thâi掉!
+ placeholder: 關係本站、行ê行動,á是其他通幫tsān lí未來管本站ê資訊。
+ title: 管理ê筆記
+ private_comment: 私人評論
+ public_comment: 公開ê評論
+ purge: 清除
+ purge_description_html: Nā lí想講tsit ê域名ē永永斷線,ē當tuì儲存內底thâi掉uì tsit ê域名來ê所有口座記錄kap相關資料。Huân-sè ē開點á時間。
+ title: 聯邦
+ total_blocked_by_us: Hōo lán封鎖
+ total_followed_by_them: Hōo in跟tuè
+ total_followed_by_us: Hōo lán跟tuè
+ total_reported: 關係in ê檢舉
+ total_storage: 媒體ê附件
+ totals_time_period_hint_html: 下kha顯示ê總計包含ta̍k時ê資料。
+ unknown_instance: 佇本服務器,現tsú時iáu無tsit ê域名ê記錄。
invites:
+ deactivate_all: Lóng停用
filter:
+ all: 全部
available: 通用ê
expired: 過期ê
title: 過濾器
title: 邀請
ip_blocks:
add_new: 建立規則
+ created_msg: 成功加添新ê IP規則
+ delete: Thâi掉
+ expires_in:
+ '1209600': 2 禮拜
+ '15778476': 6個月
+ '2629746': 1 個月
+ '31556952': 1 年
+ '86400': 1 kang
+ '94670856': 3 年
+ new:
+ title: 建立新ê IP規則
+ no_ip_block_selected: 因為無揀任何IP規則,所以lóng無改變
+ title: IP規則
+ relationships:
+ title: "%{acct} ê關係"
+ relays:
+ add_new: 加添新ê中繼
+ delete: Thâi掉
+ description_html: "聯邦ê中繼站 是中lâng ê服侍器,ē tī訂koh公開kàu hit ê中繼站ê服侍器之間,交換tsē-tsē ê 公開PO文。中繼站通幫tsān小型kap中型服侍器tuì聯邦宇宙發現內容,本地ê用者免手動跟tuè遠距離ê服侍器ê別lâng。"
+ disable: 停止使用
+ disabled: 停止使用ê
+ enable: 啟用
+ enable_hint: Lí ê服侍器tsi̍t-ē啟動,ē訂tuì tsit ê中繼逐ê公開PO文,mā ē開始送tsit ê服侍器ê公開PO文kàu hia。
+ enabled: 啟用ê
+ inbox_url: 中繼 URL
+ pending: Teh等中繼站允准
+ save_and_enable: 儲存koh啟用
+ setup: 設定中繼ê連結
+ signatures_not_enabled: Nā啟用安全模式á是受限ê聯邦模式,中繼可能buē-tàng正常運作
+ status: 狀態
+ title: 中繼
+ report_notes:
+ created_msg: 檢舉記錄成功建立!
+ destroyed_msg: 檢舉記錄成功thâi掉!
+ reports:
+ account:
+ notes:
+ other: "%{count} 篇筆記"
+ action_log: 審查日誌
+ action_taken_by: 操作由
statuses:
language: 語言
trends:
diff --git a/config/locales/nl.yml b/config/locales/nl.yml
index b53aa68a652..6d26903a20b 100644
--- a/config/locales/nl.yml
+++ b/config/locales/nl.yml
@@ -1872,6 +1872,7 @@ nl:
edited_at_html: Bewerkt op %{date}
errors:
in_reply_not_found: Het bericht waarop je probeert te reageren lijkt niet te bestaan.
+ quoted_status_not_found: Het bericht die je probeert te citeren lijkt niet te bestaan.
over_character_limit: Limiet van %{max} tekens overschreden
pin_errors:
direct: Berichten die alleen zichtbaar zijn voor vermelde gebruikers, kunnen niet worden vastgezet
diff --git a/config/locales/pt-PT.yml b/config/locales/pt-PT.yml
index f2171b80786..1e988b5baed 100644
--- a/config/locales/pt-PT.yml
+++ b/config/locales/pt-PT.yml
@@ -1872,6 +1872,7 @@ pt-PT:
edited_at_html: Editado em %{date}
errors:
in_reply_not_found: A publicação a que estás a tentar responder parece não existir.
+ quoted_status_not_found: A publicação que está a tentar citar parece não existir.
over_character_limit: limite de caracteres %{max} excedido
pin_errors:
direct: As publicações que só são visíveis para os utilizadores mencionados não podem ser fixadas
diff --git a/config/locales/ru.yml b/config/locales/ru.yml
index 35f79c67c01..4cc2e15b5a7 100644
--- a/config/locales/ru.yml
+++ b/config/locales/ru.yml
@@ -1352,7 +1352,7 @@ ru:
edit_profile:
basic_information: Основные данные
hint_html: "Здесь вы можете изменить всё то, что будет отображаться в вашем публичном профиле и рядом с вашими постами. На вас будут чаще подписываться и с вами будут чаще взаимодействовать, если у вас будет заполнен профиль и добавлено фото профиля."
- other: Прочее
+ other: Разное
emoji_styles:
auto: Автоматически
native: Как в системе
@@ -1630,19 +1630,19 @@ ru:
media_attachments:
validations:
images_and_video: Нельзя добавить видео к посту с изображениями
- not_found: Медиа %{ids} не найдено или уже прикреплено к другому сообщению
- not_ready: Не удаётся прикрепить файлы, обработка которых не завершена. Повторите попытку чуть позже!
- too_many: Нельзя добавить более 4 файлов
+ not_found: Медиа %{ids} не найдены или уже прикреплены к другому посту
+ not_ready: Обработка некоторых прикреплённых файлов ещё не окончена. Подождите немного и попробуйте снова!
+ too_many: Можно прикрепить не более 4 файлов
migrations:
- acct: имя@домен новой учётной записи
+ acct: Куда
cancel: Отменить переезд
- cancel_explanation: Отмена перенаправления повторно активирует текущую учётную запись, но не вернёт обратно подписчиков, которые были перемещены на другую.
- cancelled_msg: Переезд был успешно отменён.
+ cancel_explanation: После отмены перенаправления ваша текущая учётная запись снова станет активна, но ранее перенесённые подписчики не будут возвращены.
+ cancelled_msg: Переезд отменён.
errors:
- already_moved: это та же учётная запись, на которую вы мигрировали
- missing_also_known_as: не ссылается на эту учетную запись
+ already_moved: не может быть той учётной записью, куда уже настроен переезд
+ missing_also_known_as: должна быть связанной учётной записью
move_to_self: не может быть текущей учётной записью
- not_found: не удалось найти
+ not_found: не найдена
on_cooldown: Вы пока не можете переезжать
followers_count: Подписчиков на момент переезда
incoming_migrations: Переезд со старой учётной записи
@@ -1650,25 +1650,25 @@ ru:
moved_msg: Теперь ваша учётная запись перенаправляет к %{acct}, туда же перемещаются подписчики.
not_redirecting: Для вашей учётной записи пока не настроено перенаправление.
on_cooldown: Вы уже недавно переносили свою учётную запись. Эта возможность будет снова доступна через %{count} дн.
- past_migrations: Прошлые переезды
+ past_migrations: История переездов
proceed_with_move: Перенести подписчиков
redirected_msg: Ваша учётная запись теперь перенаправляется на %{acct}.
redirecting_to: Ваша учётная запись перенаправляет к %{acct}.
set_redirect: Настроить перенаправление
warning:
- backreference_required: Новая учётная запись должна быть сначала настроена так, чтоб ссылаться на текущую
- before: 'Прежде чем продолжить, внимательно прочитайте следующую информацию:'
- cooldown: После переезда наступает период, в течение которого вы не сможете ещё раз переехать
+ backreference_required: Текущая учётная запись сначала должна быть добавлена как связанная в настройках новой учётной записи
+ before: 'Внимательно ознакомьтесь со следующими замечаниями перед тем как продолжить:'
+ cooldown: После переезда наступит период ожидания, в течение которого переезд будет невозможен
disabled_account: Вашу текущую учётная запись впоследствии нельзя будет больше использовать. При этом, у вас будет доступ к экспорту данных, а также к повторной активации учётной записи.
- followers: Это действие перенесёт всех ваших подписчиков с текущей учётной записи на новую
- only_redirect_html: Или же вы можете просто настроить перенаправление в ваш профиль.
+ followers: В результате переезда все ваши подписчики будут перенесены с текущей учётной записи на новую
+ only_redirect_html: Также вы можете настроить перенаправление без переноса подписчиков.
other_data: Никакие другие данные не будут автоматически перенесены
- redirect: Профиль этой учётной записи будет обновлён с заметкой о перенаправлении, а также исключён из поиска
+ redirect: Профиль текущей учётной записи будет исключён из поиска, а в нём появится объявление о переезде
moderation:
title: Модерация
move_handler:
carry_blocks_over_text: Этот пользователь переехал с учётной записи %{acct}, которую вы заблокировали.
- carry_mutes_over_text: Этот пользователь перешёл с учётной записи %{acct}, которую вы игнорируете.
+ carry_mutes_over_text: Этот пользователь переехал с учётной записи %{acct}, которую вы игнорируете.
copy_account_note_text: 'Этот пользователь переехал с %{acct}, вот ваша предыдущая заметка о нём:'
navigation:
toggle_menu: Переключить меню
@@ -1717,8 +1717,9 @@ ru:
billion: млрд
million: млн
quadrillion: квадрлн
- thousand: тыс
+ thousand: тыс.
trillion: трлн
+ unit: ''
otp_authentication:
code_hint: Для подтверждения введите код, сгенерированный приложением-аутентификатором
description_html: Подключив двуфакторную авторизацию, для входа в свою учётную запись вам будет необходим смартфон и приложение-аутентификатор на нём, которое будет генерировать специальные временные коды. Без этих кодов войти в учётную запись не получиться, даже если все данные верны, что существенно увеличивает безопасность вашей учётной записи.
@@ -1728,32 +1729,32 @@ ru:
setup: Настроить
wrong_code: Введенный код недействителен! Время сервера и время устройства правильно?
pagination:
- newer: Новее
- next: След
- older: Старше
- prev: Пред
+ newer: Позже
+ next: Вперёд
+ older: Раньше
+ prev: Назад
truncate: "…"
polls:
errors:
- already_voted: Вы уже голосовали в этом опросе
+ already_voted: Вы уже проголосовали в этом опросе
duplicate_options: не должны повторяться
duration_too_long: слишком велика
duration_too_short: слишком мала
- expired: Опрос уже завершился
- invalid_choice: Выбранного варианта голосования не существует
+ expired: Этот опрос уже завершился
+ invalid_choice: Выбранного вами варианта ответа не существует
over_character_limit: не должны превышать %{max} символов
self_vote: Вы не можете голосовать в своих опросах
too_few_options: должны содержать не меньше двух опций
too_many_options: должны ограничиваться максимум %{max} опциями
preferences:
- other: Остальное
- posting_defaults: Настройки отправки по умолчанию
+ other: Разное
+ posting_defaults: Предустановки для новых постов
public_timelines: Публичные ленты
privacy:
hint_html: "Настройте, как вы хотите, чтобы ваш профиль и ваши сообщения были найдены. Различные функции в Mastodon могут помочь вам охватить более широкую аудиторию, если они включены. Уделите время изучению этих настроек, чтобы убедиться, что они подходят для вашего случая использования."
- privacy: Конфиденциальность
+ privacy: Приватность
privacy_hint_html: Определите, какую информацию вы хотите раскрыть в интересах других. Люди находят интересные профили и приложения, просматривая список подписчиков других людей и узнавая, из каких приложений они публикуют свои сообщения, но вы можете предпочесть скрыть это.
- reach: Охват
+ reach: Видимость
reach_hint_html: Укажите, хотите ли вы, чтобы новые люди обнаруживали вас и могли следить за вами. Хотите ли вы, чтобы ваши сообщения появлялись на экране Обзора? Хотите ли вы, чтобы другие люди видели вас в своих рекомендациях? Хотите ли вы автоматически принимать всех новых подписчиков или иметь возможность детально контролировать каждого из них?
search: Поиск
search_hint_html: Определите, как вас могут найти. Хотите ли вы, чтобы люди находили вас по тому, о чём вы публично писали? Хотите ли вы, чтобы люди за пределами Mastodon находили ваш профиль при поиске в Интернете? Следует помнить, что полное исключение из всех поисковых систем не может быть гарантировано для публичной информации.
diff --git a/config/locales/simple_form.da.yml b/config/locales/simple_form.da.yml
index 09041a4113d..025e2decd54 100644
--- a/config/locales/simple_form.da.yml
+++ b/config/locales/simple_form.da.yml
@@ -9,7 +9,7 @@ da:
fields: Din hjemmeside, dine pronominer, din alder, eller hvad du har lyst til.
indexable: Dine offentlige indlæg vil kunne vises i Mastodon-søgeresultater. Folk, som har interageret med dem, vil kunne finde dem uanset.
note: 'Du kan @omtale andre personer eller #hashtags.'
- show_collections: Folk vil ikke kunne tjekke dine Følger og Følgere. Folk, du selv følger, vil stadig kunne se dette.
+ show_collections: Folk vil kunne se, hvem du følger, og hvem der følger dig. Personer, som du følger, vil kunne se, at du følger dem.
unlocked: Man vil kunne følges af folk uden først at godkende dem. Ønsker man at gennemgå Følg-anmodninger og individuelt acceptere/afvise nye følgere, så fjern markeringen.
account_alias:
acct: Angiv brugernavn@domain for den konto, hvorfra du vil flytte
@@ -75,7 +75,7 @@ da:
featured_tag:
name: 'Her er nogle af dine hyppigst brugte hashtags:'
filters:
- action: Vælg handlingen til eksekvering, når et indlæg matcher filteret
+ action: Vælg, hvilken handling, der skal udføres, når et indlæg matcher filteret
actions:
blur: Skjul medier bag en advarsel, uden at skjule selve teksten
hide: Skjul det filtrerede indhold fuldstændigt og gør, som om det ikke eksisterer
diff --git a/config/locales/simple_form.lt.yml b/config/locales/simple_form.lt.yml
index 4aa89ed9ecb..bbd22e3f768 100644
--- a/config/locales/simple_form.lt.yml
+++ b/config/locales/simple_form.lt.yml
@@ -60,6 +60,7 @@ lt:
setting_display_media_default: Slėpti mediją, pažymėtą kaip jautrią
setting_display_media_hide_all: Visada slėpti mediją
setting_display_media_show_all: Visada rodyti mediją
+ setting_emoji_style: Kaip rodyti emodžius. „Auto“ bandys naudoti vietinius jaustukus, bet senesnėse naršyklėse grįš prie Tvejaustukų.
setting_system_scrollbars_ui: Taikoma tik darbalaukio naršyklėms, karkasiniais „Safari“ ir „Chrome“.
setting_use_blurhash: Gradientai pagrįsti paslėptų vizualizacijų spalvomis, bet užgožia bet kokias detales.
setting_use_pending_items: Slėpti laiko skalės naujienas po paspaudimo, vietoj automatinio srauto slinkimo.
@@ -120,6 +121,11 @@ lt:
min_age: Neturėtų būti žemiau mažiausio amžiaus, reikalaujamo pagal jūsų jurisdikcijos įstatymus.
user:
chosen_languages: Kai pažymėta, viešose laiko skalėse bus rodomi tik įrašai pasirinktomis kalbomis.
+ date_of_birth:
+ few: Turime įsitikinti, kad esate bent %{count} norint naudotis %{domain}. Mes to neišsaugosime.
+ many: Turime įsitikinti, kad esate bent %{count} norint naudotis %{domain}. Mes to neišsaugosime.
+ one: Turime įsitikinti, kad esate bent %{count} norint naudotis %{domain}. Mes to neišsaugosime.
+ other: Turime įsitikinti, kad esate bent %{count} norint naudotis %{domain}. Mes to neišsaugosime.
role: Vaidmuo valdo, kokius leidimus naudotojas turi.
labels:
account:
@@ -162,6 +168,7 @@ lt:
setting_display_media: Medijos rodymas
setting_display_media_hide_all: Slėpti viską
setting_display_media_show_all: Rodyti viską
+ setting_emoji_style: Jaustuko stilius
setting_expand_spoilers: Visada išplėsti įrašus, pažymėtus turinio įspėjimais
setting_hide_network: Slėpti savo socialinę diagramą
setting_missing_alt_text_modal: Rodyti patvirtinimo dialogo langą prieš skelbiant mediją be alternatyvaus teksto.
diff --git a/config/locales/tr.yml b/config/locales/tr.yml
index 89c51ca6e08..2b0164e9bb1 100644
--- a/config/locales/tr.yml
+++ b/config/locales/tr.yml
@@ -1872,6 +1872,7 @@ tr:
edited_at_html: "%{date} tarihinde düzenlendi"
errors:
in_reply_not_found: Yanıtlamaya çalıştığınız durum yok gibi görünüyor.
+ quoted_status_not_found: Alıntılamaya çalıştığınız gönderi mevcut görünmüyor.
over_character_limit: "%{max} karakter limiti aşıldı"
pin_errors:
direct: Sadece değinilen kullanıcıların görebileceği gönderiler üstte tutulamaz
diff --git a/config/locales/uk.yml b/config/locales/uk.yml
index ceadfc34664..88a14b0aa47 100644
--- a/config/locales/uk.yml
+++ b/config/locales/uk.yml
@@ -1846,6 +1846,7 @@ uk:
edited_at_html: Відредаговано %{date}
errors:
in_reply_not_found: Допису, на який ви намагаєтеся відповісти, не існує.
+ quoted_status_not_found: Повідомлення, яке ви намагаєтеся цитувати, не існує.
over_character_limit: перевищено ліміт символів %{max}
pin_errors:
direct: Не можливо прикріпити дописи, які видимі лише згаданим користувачам
diff --git a/config/locales/vi.yml b/config/locales/vi.yml
index d56878d1615..adc801c449c 100644
--- a/config/locales/vi.yml
+++ b/config/locales/vi.yml
@@ -1829,6 +1829,7 @@ vi:
edited_at_html: Sửa %{date}
errors:
in_reply_not_found: Bạn đang trả lời một tút không còn tồn tại.
+ quoted_status_not_found: Bạn đang trích dẫn một tút không còn tồn tại.
over_character_limit: vượt quá giới hạn %{max} ký tự
pin_errors:
direct: Không thể ghim những tút nhắn riêng
diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml
index 546ac7d989a..bc29a30b332 100644
--- a/config/locales/zh-TW.yml
+++ b/config/locales/zh-TW.yml
@@ -1830,7 +1830,8 @@ zh-TW:
other: 含有不得使用的標籤: %{tags}
edited_at_html: 編輯於 %{date}
errors:
- in_reply_not_found: 您嘗試回覆的嘟文看起來不存在。
+ in_reply_not_found: 您嘗試回覆之嘟文似乎不存在。
+ quoted_status_not_found: 您嘗試引用之嘟文似乎不存在。
over_character_limit: 已超過 %{max} 字的限制
pin_errors:
direct: 無法釘選只有僅提及使用者可見之嘟文
diff --git a/db/seeds/04_admin.rb b/db/seeds/04_admin.rb
index 887b4a22130..43290c47a46 100644
--- a/db/seeds/04_admin.rb
+++ b/db/seeds/04_admin.rb
@@ -7,7 +7,17 @@ if Rails.env.development?
admin = Account.where(username: 'admin').first_or_initialize(username: 'admin')
admin.save(validate: false)
- user = User.where(email: "admin@#{domain}").first_or_initialize(email: "admin@#{domain}", password: 'mastodonadmin', password_confirmation: 'mastodonadmin', confirmed_at: Time.now.utc, role: UserRole.find_by(name: 'Owner'), account: admin, agreement: true, approved: true)
+ user = User.where(email: "admin@#{domain}").first_or_initialize(
+ email: "admin@#{domain}",
+ password: 'mastodonadmin',
+ password_confirmation: 'mastodonadmin',
+ confirmed_at: Time.now.utc,
+ role: UserRole.find_by(name: 'Owner'),
+ account: admin,
+ agreement: true,
+ approved: true,
+ bypass_registration_checks: true
+ )
user.save!
user.approve!
end
diff --git a/lib/mastodon/worker_batch_middleware.rb b/lib/mastodon/worker_batch_middleware.rb
new file mode 100644
index 00000000000..c4623013327
--- /dev/null
+++ b/lib/mastodon/worker_batch_middleware.rb
@@ -0,0 +1,11 @@
+# frozen_string_literal: true
+
+class Mastodon::WorkerBatchMiddleware
+ def call(_worker, msg, _queue, _redis_pool = nil)
+ if (batch = Thread.current[:batch])
+ batch.add_jobs([msg['jid']])
+ end
+
+ yield
+ end
+end
diff --git a/spec/helpers/json_ld_helper_spec.rb b/spec/helpers/json_ld_helper_spec.rb
index d76c5167a7d..f216588d978 100644
--- a/spec/helpers/json_ld_helper_spec.rb
+++ b/spec/helpers/json_ld_helper_spec.rb
@@ -180,6 +180,14 @@ RSpec.describe JsonLdHelper do
expect(compacted.dig('object', 'tag', 0, 'href')).to eq ['foo']
expect(safe_for_forwarding?(json, compacted)).to be true
end
+
+ context 'when array size mismatch exists' do
+ subject { helper.patch_for_forwarding!(json, alternate) }
+
+ let(:alternate) { json.merge('to' => %w(one two three)) }
+
+ it { is_expected.to be_nil }
+ end
end
describe 'safe_for_forwarding?' do
diff --git a/spec/lib/webfinger_spec.rb b/spec/lib/webfinger_spec.rb
index 5015deac7ff..e214a03536b 100644
--- a/spec/lib/webfinger_spec.rb
+++ b/spec/lib/webfinger_spec.rb
@@ -4,15 +4,15 @@ require 'rails_helper'
RSpec.describe Webfinger do
describe 'self link' do
+ subject { described_class.new('acct:alice@example.com').perform }
+
context 'when self link is specified with type application/activity+json' do
let!(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice', type: 'application/activity+json' }] } }
it 'correctly parses the response' do
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
- response = described_class.new('acct:alice@example.com').perform
-
- expect(response.self_link_href).to eq 'https://example.com/alice'
+ expect(subject.self_link_href).to eq 'https://example.com/alice'
end
end
@@ -22,9 +22,7 @@ RSpec.describe Webfinger do
it 'correctly parses the response' do
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
- response = described_class.new('acct:alice@example.com').perform
-
- expect(response.self_link_href).to eq 'https://example.com/alice'
+ expect(subject.self_link_href).to eq 'https://example.com/alice'
end
end
@@ -34,7 +32,45 @@ RSpec.describe Webfinger do
it 'raises an error' do
stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
- expect { described_class.new('acct:alice@example.com').perform }.to raise_error(Webfinger::Error)
+ expect { subject }
+ .to raise_error(Webfinger::Error)
+ end
+ end
+
+ context 'when webfinger fails and host meta is used' do
+ before { stub_request(:get, 'https://example.com/.well-known/webfinger?resource=acct:alice@example.com').to_return(status: 404) }
+
+ context 'when host meta succeeds' do
+ let(:host_meta) do
+ <<~XML
+
+
+
+
+ XML
+ end
+ let(:webfinger) { { subject: 'acct:alice@example.com', links: [{ rel: 'self', href: 'https://example.com/alice-from-NS', type: 'application/activity+json' }] } }
+
+ before do
+ stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(body: host_meta, headers: { 'Content-Type': 'application/jrd+json' })
+ stub_request(:get, 'https://example.com/.well-known/nonStandardWebfinger?resource=acct:alice@example.com').to_return(body: Oj.dump(webfinger), headers: { 'Content-Type': 'application/jrd+json' })
+ end
+
+ it 'uses host meta details' do
+ expect(subject.self_link_href)
+ .to eq 'https://example.com/alice-from-NS'
+ end
+ end
+
+ context 'when host meta fails' do
+ before do
+ stub_request(:get, 'https://example.com/.well-known/host-meta').to_return(status: 500)
+ end
+
+ it 'raises error' do
+ expect { subject }
+ .to raise_error(Webfinger::Error)
+ end
end
end
end
diff --git a/spec/models/announcement_reaction_spec.rb b/spec/models/announcement_reaction_spec.rb
new file mode 100644
index 00000000000..e02a8dcd069
--- /dev/null
+++ b/spec/models/announcement_reaction_spec.rb
@@ -0,0 +1,63 @@
+# frozen_string_literal: true
+
+require 'rails_helper'
+
+RSpec.describe AnnouncementReaction do
+ describe 'Associations' do
+ it { is_expected.to belong_to(:account) }
+ it { is_expected.to belong_to(:announcement).inverse_of(:announcement_reactions) }
+ it { is_expected.to belong_to(:custom_emoji).optional }
+ end
+
+ describe 'Validations' do
+ subject { Fabricate.build :announcement_reaction }
+
+ it { is_expected.to validate_presence_of(:name) }
+ it { is_expected.to allow_values('😀').for(:name) }
+ it { is_expected.to_not allow_values('INVALID').for(:name) }
+
+ context 'when reaction limit is reached' do
+ subject { Fabricate.build :announcement_reaction, announcement: announcement_reaction.announcement }
+
+ let(:announcement_reaction) { Fabricate :announcement_reaction, name: '😊' }
+
+ before { stub_const 'ReactionValidator::LIMIT', 1 }
+
+ it { is_expected.to_not allow_values('😀').for(:name).against(:base) }
+ end
+ end
+
+ describe 'Callbacks' do
+ describe 'Setting custom emoji association' do
+ subject { Fabricate.build :announcement_reaction, name: }
+
+ context 'when name is missing' do
+ let(:name) { '' }
+
+ it 'does not set association' do
+ expect { subject.valid? }
+ .to not_change(subject, :custom_emoji).from(be_blank)
+ end
+ end
+
+ context 'when name matches a custom emoji shortcode' do
+ let(:name) { 'custom' }
+ let!(:custom_emoji) { Fabricate :custom_emoji, shortcode: 'custom' }
+
+ it 'sets association' do
+ expect { subject.valid? }
+ .to change(subject, :custom_emoji).from(be_blank).to(custom_emoji)
+ end
+ end
+
+ context 'when name does not match a custom emoji' do
+ let(:name) { 'custom' }
+
+ it 'does not set association' do
+ expect { subject.valid? }
+ .to not_change(subject, :custom_emoji).from(be_blank)
+ end
+ end
+ end
+ end
+end
diff --git a/spec/models/list_spec.rb b/spec/models/list_spec.rb
index 48c273d3ecb..e2d91835ec7 100644
--- a/spec/models/list_spec.rb
+++ b/spec/models/list_spec.rb
@@ -28,4 +28,26 @@ RSpec.describe List do
end
end
end
+
+ describe 'Scopes' do
+ describe '.with_list_account' do
+ let(:alice) { Fabricate :account }
+ let(:bob) { Fabricate :account }
+ let(:list) { Fabricate :list }
+ let(:other_list) { Fabricate :list }
+
+ before do
+ Fabricate :follow, account: list.account, target_account: alice
+ Fabricate :follow, account: other_list.account, target_account: bob
+ Fabricate :list_account, list: list, account: alice
+ Fabricate :list_account, list: other_list, account: bob
+ end
+
+ it 'returns lists connected to the account' do
+ expect(described_class.with_list_account(alice))
+ .to include(list)
+ .and not_include(other_list)
+ end
+ end
+ end
end
diff --git a/spec/models/tag_spec.rb b/spec/models/tag_spec.rb
index 0831ac34b8b..18378c000d2 100644
--- a/spec/models/tag_spec.rb
+++ b/spec/models/tag_spec.rb
@@ -17,6 +17,7 @@ RSpec.describe Tag do
subject { Fabricate :tag, name: 'original' }
it { is_expected.to_not allow_value('changed').for(:name).with_message(previous_name_error_message) }
+ it { is_expected.to allow_value('ORIGINAL').for(:name) }
end
end
@@ -31,6 +32,7 @@ RSpec.describe Tag do
subject { Fabricate :tag, name: 'original', display_name: 'OriginalDisplayName' }
it { is_expected.to_not allow_value('ChangedDisplayName').for(:display_name).with_message(previous_name_error_message) }
+ it { is_expected.to allow_value('ORIGINAL').for(:display_name) }
end
end
diff --git a/yarn.lock b/yarn.lock
index 2c1faaa91a1..746487e0659 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -13791,8 +13791,8 @@ __metadata:
linkType: hard
"vite-plugin-pwa@npm:^1.0.0":
- version: 1.0.1
- resolution: "vite-plugin-pwa@npm:1.0.1"
+ version: 1.0.2
+ resolution: "vite-plugin-pwa@npm:1.0.2"
dependencies:
debug: "npm:^4.3.6"
pretty-bytes: "npm:^6.1.1"
@@ -13807,7 +13807,7 @@ __metadata:
peerDependenciesMeta:
"@vite-pwa/assets-generator":
optional: true
- checksum: 10c0/ceca04df97877ca97eb30805207d4826bd6340796194c9015afeefeb781931bf9019a630c5a0bdaa6dffcada11ce1fdf8595ac48a08d751dff81601aa0c7db38
+ checksum: 10c0/e4f2f4dfff843ee2585a0d89e74187168ba20da77cd0d127ce7ad7eebcf5a68b2bf09000afb6bb86d43a2034fea9f568cd6db2a2d4b47a72e175d999a5e07eb1
languageName: node
linkType: hard