Merge branch 'main' into glitch-soc/merge-upstream
Conflicts: - `app/lib/formatter.rb`: Upstream completely refactored the formatting code and removed that file, while glitch-soc had code for Markdown and HTML toots. Took upstream code, glitch-soc changes will be re-implemented on top of the refactored classes in a later commit. - `app/models/status.rb`: Upstream refactored status edit handling and moved code to `app/models/concerns/status_snapshot_concern.rb`. Applied glitch-soc's changes to that file. - `app/serializers/activitypub/note_serializer.rb`: Not really a conflict, just a line added too close to one modified by glitch-soc. Applied upstream changes while keeping the glitch-soc-modified one. - `app/services/update_status_service.rb`: Not really a conflict, upstream modified a line adjacent to one added by glitch-soc. Applied upstream changes while keeping the glitch-soc line. - `app/views/statuses/_simple_status.html.haml`: Upstream refactored formatting, glitch-soc changed the markup slightly. Applied upstream changes. - `spec/lib/formatter_spec.rb`: Upstream completely refactored the formatting code and removed that file, while glitch-soc had code for Markdown and HTML toots. Took upstream code, glitch-soc changes will be re-implemented on top of the refactored classes in a later commit.main
commit
aaa9ec340b
|
@ -81,6 +81,7 @@ All notable changes to this project will be documented in this file.
|
||||||
- Add lazy loading for emoji picker in web UI ([mashirozx](https://github.com/mastodon/mastodon/pull/16907), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17011))
|
- Add lazy loading for emoji picker in web UI ([mashirozx](https://github.com/mastodon/mastodon/pull/16907), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17011))
|
||||||
- Add single option votes tooltip in polls in web UI ([Brawaru](https://github.com/mastodon/mastodon/pull/16849))
|
- Add single option votes tooltip in polls in web UI ([Brawaru](https://github.com/mastodon/mastodon/pull/16849))
|
||||||
- Add confirmation modal when closing media edit modal with unsaved changes in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16518))
|
- Add confirmation modal when closing media edit modal with unsaved changes in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16518))
|
||||||
|
- Add hint about missing media attachment description in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/17845))
|
||||||
- Add support for fetching Create and Announce activities by URI in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16383))
|
- Add support for fetching Create and Announce activities by URI in ActivityPub ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16383))
|
||||||
- Add `S3_FORCE_SINGLE_REQUEST` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16866))
|
- Add `S3_FORCE_SINGLE_REQUEST` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/16866))
|
||||||
- Add `OMNIAUTH_ONLY` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17288), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17345))
|
- Add `OMNIAUTH_ONLY` environment variable ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17288), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/17345))
|
||||||
|
@ -130,6 +131,11 @@ All notable changes to this project will be documented in this file.
|
||||||
|
|
||||||
### Fixed
|
### Fixed
|
||||||
|
|
||||||
|
- Fix IDN domains not being rendered correctly in a few left-over places ([Gargron](https://github.com/mastodon/mastodon/pull/17848))
|
||||||
|
- Fix Sanskrit translation not being used in web UI ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17820))
|
||||||
|
- Fix Kurdish languages having the wrong language codes ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17812))
|
||||||
|
- Fix pghero making database schema suggestions ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17807))
|
||||||
|
- Fix encoding glitch in the OpenGraph description of a profile page ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17821))
|
||||||
- Fix web manifest not permitting PWA usage from alternate domains ([HolgerHuo](https://github.com/mastodon/mastodon/pull/16714))
|
- Fix web manifest not permitting PWA usage from alternate domains ([HolgerHuo](https://github.com/mastodon/mastodon/pull/16714))
|
||||||
- Fix not being able to edit media attachments for scheduled posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17690))
|
- Fix not being able to edit media attachments for scheduled posts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17690))
|
||||||
- Fix subscribed relay activities being recorded as boosts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17571))
|
- Fix subscribed relay activities being recorded as boosts ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/17571))
|
||||||
|
|
|
@ -57,7 +57,7 @@ class StatusesIndex < Chewy::Index
|
||||||
field :id, type: 'long'
|
field :id, type: 'long'
|
||||||
field :account_id, type: 'long'
|
field :account_id, type: 'long'
|
||||||
|
|
||||||
field :text, type: 'text', value: ->(status) { [status.spoiler_text, Formatter.instance.plaintext(status)].concat(status.ordered_media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do
|
field :text, type: 'text', value: ->(status) { [status.spoiler_text, PlainTextFormatter.new(status.text, status.local?).to_s].concat(status.ordered_media_attachments.map(&:description)).concat(status.preloadable_poll ? status.preloadable_poll.options : []).join("\n\n") } do
|
||||||
field :stemmed, type: 'text', analyzer: 'content'
|
field :stemmed, type: 'text', analyzer: 'content'
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
class Api::V1::Trends::LinksController < Api::BaseController
|
class Api::V1::Trends::LinksController < Api::BaseController
|
||||||
before_action :set_links
|
before_action :set_links
|
||||||
|
|
||||||
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
|
DEFAULT_LINKS_LIMIT = 10
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render json: @links, each_serializer: REST::Trends::LinkSerializer
|
render json: @links, each_serializer: REST::Trends::LinkSerializer
|
||||||
end
|
end
|
||||||
|
@ -20,6 +24,26 @@ class Api::V1::Trends::LinksController < Api::BaseController
|
||||||
end
|
end
|
||||||
|
|
||||||
def links_from_trends
|
def links_from_trends
|
||||||
Trends.links.query.allowed.in_locale(content_locale).limit(limit_param(10))
|
Trends.links.query.allowed.in_locale(content_locale).offset(offset_param).limit(limit_param(DEFAULT_LINKS_LIMIT))
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_pagination_headers
|
||||||
|
set_pagination_headers(next_path, prev_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(:limit).permit(:limit).merge(core_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v1_trends_links_url pagination_params(offset: offset_param + limit_param(DEFAULT_LINKS_LIMIT))
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v1_trends_links_url pagination_params(offset: offset_param - limit_param(DEFAULT_LINKS_LIMIT)) if offset_param > limit_param(DEFAULT_LINKS_LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def offset_param
|
||||||
|
params[:offset].to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,8 @@
|
||||||
class Api::V1::Trends::StatusesController < Api::BaseController
|
class Api::V1::Trends::StatusesController < Api::BaseController
|
||||||
before_action :set_statuses
|
before_action :set_statuses
|
||||||
|
|
||||||
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render json: @statuses, each_serializer: REST::StatusSerializer
|
render json: @statuses, each_serializer: REST::StatusSerializer
|
||||||
end
|
end
|
||||||
|
@ -22,6 +24,26 @@ class Api::V1::Trends::StatusesController < Api::BaseController
|
||||||
def statuses_from_trends
|
def statuses_from_trends
|
||||||
scope = Trends.statuses.query.allowed.in_locale(content_locale)
|
scope = Trends.statuses.query.allowed.in_locale(content_locale)
|
||||||
scope = scope.filtered_for(current_account) if user_signed_in?
|
scope = scope.filtered_for(current_account) if user_signed_in?
|
||||||
scope.limit(limit_param(DEFAULT_STATUSES_LIMIT))
|
scope.offset(offset_param).limit(limit_param(DEFAULT_STATUSES_LIMIT))
|
||||||
|
end
|
||||||
|
|
||||||
|
def insert_pagination_headers
|
||||||
|
set_pagination_headers(next_path, prev_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(:limit).permit(:limit).merge(core_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v1_trends_statuses_url pagination_params(offset: offset_param + limit_param(DEFAULT_STATUSES_LIMIT))
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v1_trends_statuses_url pagination_params(offset: offset_param - limit_param(DEFAULT_STATUSES_LIMIT)) if offset_param > limit_param(DEFAULT_STATUSES_LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def offset_param
|
||||||
|
params[:offset].to_i
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,6 +3,10 @@
|
||||||
class Api::V1::Trends::TagsController < Api::BaseController
|
class Api::V1::Trends::TagsController < Api::BaseController
|
||||||
before_action :set_tags
|
before_action :set_tags
|
||||||
|
|
||||||
|
after_action :insert_pagination_headers
|
||||||
|
|
||||||
|
DEFAULT_TAGS_LIMIT = 10
|
||||||
|
|
||||||
def index
|
def index
|
||||||
render json: @tags, each_serializer: REST::TagSerializer
|
render json: @tags, each_serializer: REST::TagSerializer
|
||||||
end
|
end
|
||||||
|
@ -12,10 +16,30 @@ class Api::V1::Trends::TagsController < Api::BaseController
|
||||||
def set_tags
|
def set_tags
|
||||||
@tags = begin
|
@tags = begin
|
||||||
if Setting.trends
|
if Setting.trends
|
||||||
Trends.tags.query.allowed.limit(limit_param(10))
|
Trends.tags.query.allowed.limit(limit_param(DEFAULT_TAGS_LIMIT))
|
||||||
else
|
else
|
||||||
[]
|
[]
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def insert_pagination_headers
|
||||||
|
set_pagination_headers(next_path, prev_path)
|
||||||
|
end
|
||||||
|
|
||||||
|
def pagination_params(core_params)
|
||||||
|
params.slice(:limit).permit(:limit).merge(core_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
def next_path
|
||||||
|
api_v1_trends_tags_url pagination_params(offset: offset_param + limit_param(DEFAULT_TAGS_LIMIT))
|
||||||
|
end
|
||||||
|
|
||||||
|
def prev_path
|
||||||
|
api_v1_trends_tags_url pagination_params(offset: offset_param - limit_param(DEFAULT_TAGS_LIMIT)) if offset_param > limit_param(DEFAULT_TAGS_LIMIT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def offset_param
|
||||||
|
params[:offset].to_i
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -15,7 +15,7 @@ class Api::Web::EmbedsController < Api::Web::BaseController
|
||||||
return not_found if oembed.nil?
|
return not_found if oembed.nil?
|
||||||
|
|
||||||
begin
|
begin
|
||||||
oembed[:html] = Formatter.instance.sanitize(oembed[:html], Sanitize::Config::MASTODON_OEMBED)
|
oembed[:html] = Sanitize.fragment(oembed[:html], Sanitize::Config::MASTODON_OEMBED)
|
||||||
rescue ArgumentError
|
rescue ArgumentError
|
||||||
return not_found
|
return not_found
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,10 +2,12 @@
|
||||||
|
|
||||||
module AccountsHelper
|
module AccountsHelper
|
||||||
def display_name(account, **options)
|
def display_name(account, **options)
|
||||||
|
str = account.display_name.presence || account.username
|
||||||
|
|
||||||
if options[:custom_emojify]
|
if options[:custom_emojify]
|
||||||
Formatter.instance.format_display_name(account, **options)
|
prerender_custom_emojis(h(str), account.emojis)
|
||||||
else
|
else
|
||||||
account.display_name.presence || account.username
|
str
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -12,9 +12,6 @@ module Admin::Trends::StatusesHelper
|
||||||
|
|
||||||
return '' if text.blank?
|
return '' if text.blank?
|
||||||
|
|
||||||
html = Formatter.instance.send(:encode, text)
|
prerender_custom_emojis(h(text), status.emojis)
|
||||||
html = Formatter.instance.send(:encode_custom_emojis, html, status.emojis, prefers_autoplay?)
|
|
||||||
|
|
||||||
html.html_safe # rubocop:disable Rails/OutputSafety
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -240,4 +240,8 @@ module ApplicationHelper
|
||||||
end
|
end
|
||||||
end.values
|
end.values
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def prerender_custom_emojis(html, custom_emojis)
|
||||||
|
EmojiFormatter.new(html, custom_emojis, animate: prefers_autoplay?).to_s
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,19 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module FormattingHelper
|
||||||
|
def html_aware_format(text, local, options = {})
|
||||||
|
HtmlAwareFormatter.new(text, local, options).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def linkify(text, options = {})
|
||||||
|
TextFormatter.new(text, options).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_plain_text(text, local)
|
||||||
|
PlainTextFormatter.new(text, local).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
def status_content_format(status)
|
||||||
|
html_aware_format(status.text, status.local?, preloaded_accounts: [status.account] + (status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []))
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
module RoutingHelper
|
module RoutingHelper
|
||||||
extend ActiveSupport::Concern
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
include Rails.application.routes.url_helpers
|
include Rails.application.routes.url_helpers
|
||||||
include ActionView::Helpers::AssetTagHelper
|
include ActionView::Helpers::AssetTagHelper
|
||||||
include Webpacker::Helper
|
include Webpacker::Helper
|
||||||
|
@ -22,8 +23,6 @@ module RoutingHelper
|
||||||
full_asset_url(asset_pack_path(source, **options))
|
full_asset_url(asset_pack_path(source, **options))
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def use_storage?
|
def use_storage?
|
||||||
Rails.configuration.x.use_s3 || Rails.configuration.x.use_swift
|
Rails.configuration.x.use_s3 || Rails.configuration.x.use_swift
|
||||||
end
|
end
|
||||||
|
|
|
@ -113,20 +113,6 @@ module StatusesHelper
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def simplified_text(text)
|
|
||||||
text.dup.tap do |new_text|
|
|
||||||
URI.extract(new_text).each do |url|
|
|
||||||
new_text.gsub!(url, '')
|
|
||||||
end
|
|
||||||
|
|
||||||
new_text.gsub!(Account::MENTION_RE, '')
|
|
||||||
new_text.gsub!(Tag::HASHTAG_RE, '')
|
|
||||||
new_text.gsub!(/\s+/, '')
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def embedded_view?
|
def embedded_view?
|
||||||
params[:controller] == EMBEDDED_CONTROLLER && params[:action] == EMBEDDED_ACTION
|
params[:controller] == EMBEDDED_CONTROLLER && params[:action] == EMBEDDED_ACTION
|
||||||
end
|
end
|
||||||
|
|
|
@ -33,6 +33,7 @@ export default class Counter extends React.PureComponent {
|
||||||
label: PropTypes.string.isRequired,
|
label: PropTypes.string.isRequired,
|
||||||
href: PropTypes.string,
|
href: PropTypes.string,
|
||||||
params: PropTypes.object,
|
params: PropTypes.object,
|
||||||
|
target: PropTypes.string,
|
||||||
};
|
};
|
||||||
|
|
||||||
state = {
|
state = {
|
||||||
|
@ -54,7 +55,7 @@ export default class Counter extends React.PureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { label, href } = this.props;
|
const { label, href, target } = this.props;
|
||||||
const { loading, data } = this.state;
|
const { loading, data } = this.state;
|
||||||
|
|
||||||
let content;
|
let content;
|
||||||
|
@ -100,7 +101,7 @@ export default class Counter extends React.PureComponent {
|
||||||
|
|
||||||
if (href) {
|
if (href) {
|
||||||
return (
|
return (
|
||||||
<a href={href} className='sparkline'>
|
<a href={href} className='sparkline' target={target}>
|
||||||
{inner}
|
{inner}
|
||||||
</a>
|
</a>
|
||||||
);
|
);
|
||||||
|
|
|
@ -3,16 +3,16 @@
|
||||||
"account.add_or_remove_from_list": "افزودن یا برداشتن از سیاههها",
|
"account.add_or_remove_from_list": "افزودن یا برداشتن از سیاههها",
|
||||||
"account.badges.bot": "روبات",
|
"account.badges.bot": "روبات",
|
||||||
"account.badges.group": "گروه",
|
"account.badges.group": "گروه",
|
||||||
"account.block": "مسدود کردن @{name}",
|
"account.block": "مسدود کردن @{name}",
|
||||||
"account.block_domain": "مسدود کردن دامنهٔ {domain}",
|
"account.block_domain": "مسدود کردن دامنهٔ {domain}",
|
||||||
"account.blocked": "مسدود",
|
"account.blocked": "مسدود",
|
||||||
"account.browse_more_on_origin_server": "مرور بیشتر روی نمایهٔ اصلی",
|
"account.browse_more_on_origin_server": "مرور بیشتر روی نمایهٔ اصلی",
|
||||||
"account.cancel_follow_request": "لغو درخواست پیگیری",
|
"account.cancel_follow_request": "لغو درخواست پیگیری",
|
||||||
"account.direct": "پیام مستقیم به @{name}",
|
"account.direct": "پیام مستقیم به @{name}",
|
||||||
"account.disable_notifications": "آگاهی به من هنگام فرستادنهای @{name} پایان یابد",
|
"account.disable_notifications": "آگاهی به من هنگام فرستادنهای @{name} پایان یابد",
|
||||||
"account.domain_blocked": "دامنه مسدود شد",
|
"account.domain_blocked": "دامنه مسدود شد",
|
||||||
"account.edit_profile": "ویرایش نمایه",
|
"account.edit_profile": "ویرایش نمایه",
|
||||||
"account.enable_notifications": "هنگام فرستههای @{name} مرا آگاه کن",
|
"account.enable_notifications": "هنگام فرستههای @{name} مرا آگاه کن",
|
||||||
"account.endorse": "معرّفی در نمایه",
|
"account.endorse": "معرّفی در نمایه",
|
||||||
"account.follow": "پیگیری",
|
"account.follow": "پیگیری",
|
||||||
"account.followers": "پیگیرندگان",
|
"account.followers": "پیگیرندگان",
|
||||||
|
@ -22,34 +22,34 @@
|
||||||
"account.following_counter": "{count, plural, one {{counter} پیگرفته} other {{counter} پیگرفته}}",
|
"account.following_counter": "{count, plural, one {{counter} پیگرفته} other {{counter} پیگرفته}}",
|
||||||
"account.follows.empty": "این کاربر هنوز پیگیر کسی نیست.",
|
"account.follows.empty": "این کاربر هنوز پیگیر کسی نیست.",
|
||||||
"account.follows_you": "پی میگیردتان",
|
"account.follows_you": "پی میگیردتان",
|
||||||
"account.hide_reblogs": "نهفتن تقویتهای @{name}",
|
"account.hide_reblogs": "نهفتن تقویتهای @{name}",
|
||||||
"account.joined": "پیوسته از {date}",
|
"account.joined": "پیوسته از {date}",
|
||||||
"account.link_verified_on": "مالکیت این پیوند در {date} بررسی شد",
|
"account.link_verified_on": "مالکیت این پیوند در {date} بررسی شد",
|
||||||
"account.locked_info": "این حساب خصوصی است. صاحبش تصمیم میگیرد که چه کسی پیگیرش باشد.",
|
"account.locked_info": "این حساب خصوصی است. صاحبش تصمیم میگیرد که چه کسی پیگیرش باشد.",
|
||||||
"account.media": "رسانه",
|
"account.media": "رسانه",
|
||||||
"account.mention": "نامبردن از @{name}",
|
"account.mention": "نامبردن از @{name}",
|
||||||
"account.moved_to": "{name} منتقل شده به:",
|
"account.moved_to": "{name} منتقل شده به:",
|
||||||
"account.mute": "خموشاندن @{name}",
|
"account.mute": "خموشاندن @{name}",
|
||||||
"account.mute_notifications": "خموشاندن آگاهیها از @{name}",
|
"account.mute_notifications": "خموشاندن آگاهیهای @{name}",
|
||||||
"account.muted": "خموش",
|
"account.muted": "خموش",
|
||||||
"account.posts": "فرسته",
|
"account.posts": "فرسته",
|
||||||
"account.posts_with_replies": "فرستهها و پاسخها",
|
"account.posts_with_replies": "فرستهها و پاسخها",
|
||||||
"account.report": "گزارش @{name}",
|
"account.report": "گزارش @{name}",
|
||||||
"account.requested": "منتظر پذیرش است. برای لغو درخواست پیگیری کلیک کنید",
|
"account.requested": "منتظر پذیرش است. برای لغو درخواست پیگیری کلیک کنید",
|
||||||
"account.share": "همرسانی نمایهٔ @{name}",
|
"account.share": "همرسانی نمایهٔ @{name}",
|
||||||
"account.show_reblogs": "نمایش تقویتهای @{name}",
|
"account.show_reblogs": "نمایش تقویتهای @{name}",
|
||||||
"account.statuses_counter": "{count, plural, one {{counter} فرسته} other {{counter} فرسته}}",
|
"account.statuses_counter": "{count, plural, one {{counter} فرسته} other {{counter} فرسته}}",
|
||||||
"account.unblock": "رفع مسدودیت @{name}",
|
"account.unblock": "رفع مسدودیت @{name}",
|
||||||
"account.unblock_domain": "رفع مسدودیت دامنهٔ {domain}",
|
"account.unblock_domain": "رفع مسدودیت دامنهٔ {domain}",
|
||||||
"account.unblock_short": "رفع مسدودیت",
|
"account.unblock_short": "رفع مسدودیت",
|
||||||
"account.unendorse": "معرّفی نکردن در نمایه",
|
"account.unendorse": "معرّفی نکردن در نمایه",
|
||||||
"account.unfollow": "ناپیگیری",
|
"account.unfollow": "ناپیگیری",
|
||||||
"account.unmute": "ناخموشی @{name}",
|
"account.unmute": "ناخموشی @{name}",
|
||||||
"account.unmute_notifications": "ناخموشی آگاهیها از @{name}",
|
"account.unmute_notifications": "ناخموشی آگاهیهای @{name}",
|
||||||
"account.unmute_short": "ناخموشی",
|
"account.unmute_short": "ناخموشی",
|
||||||
"account_note.placeholder": "برای افزودن یادداشت کلیک کنید",
|
"account_note.placeholder": "برای افزودن یادداشت کلیک کنید",
|
||||||
"admin.dashboard.daily_retention": "User retention rate by day after sign-up",
|
"admin.dashboard.daily_retention": "نرخ حفظ کاربر در روز پس از ثبت نام",
|
||||||
"admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
|
"admin.dashboard.monthly_retention": "نرخ حفظ کاربر در ماه پس از ثبت نام",
|
||||||
"admin.dashboard.retention.average": "میانگین",
|
"admin.dashboard.retention.average": "میانگین",
|
||||||
"admin.dashboard.retention.cohort": "ماه ثبتنام",
|
"admin.dashboard.retention.cohort": "ماه ثبتنام",
|
||||||
"admin.dashboard.retention.cohort_size": "کاربران جدید",
|
"admin.dashboard.retention.cohort_size": "کاربران جدید",
|
||||||
|
@ -79,13 +79,13 @@
|
||||||
"column.lists": "سیاههها",
|
"column.lists": "سیاههها",
|
||||||
"column.mutes": "کاربران خموش",
|
"column.mutes": "کاربران خموش",
|
||||||
"column.notifications": "آگاهیها",
|
"column.notifications": "آگاهیها",
|
||||||
"column.pins": "فرستههای سنجاقشده",
|
"column.pins": "فرستههای سنجاق شده",
|
||||||
"column.public": "خط زمانی همگانی",
|
"column.public": "خط زمانی همگانی",
|
||||||
"column_back_button.label": "بازگشت",
|
"column_back_button.label": "بازگشت",
|
||||||
"column_header.hide_settings": "نهفتن تنظیمات",
|
"column_header.hide_settings": "نهفتن تنظیمات",
|
||||||
"column_header.moveLeft_settings": "جابهجایی ستون به چپ",
|
"column_header.moveLeft_settings": "جابهجایی ستون به چپ",
|
||||||
"column_header.moveRight_settings": "جابهجایی ستون به راست",
|
"column_header.moveRight_settings": "جابهجایی ستون به راست",
|
||||||
"column_header.pin": "سنجاقکردن",
|
"column_header.pin": "سنجاق کردن",
|
||||||
"column_header.show_settings": "نمایش تنظیمات",
|
"column_header.show_settings": "نمایش تنظیمات",
|
||||||
"column_header.unpin": "برداشتن سنجاق",
|
"column_header.unpin": "برداشتن سنجاق",
|
||||||
"column_subheading.settings": "تنظیمات",
|
"column_subheading.settings": "تنظیمات",
|
||||||
|
@ -94,7 +94,7 @@
|
||||||
"community.column_settings.remote_only": "تنها دوردست",
|
"community.column_settings.remote_only": "تنها دوردست",
|
||||||
"compose_form.direct_message_warning": "این فرسته تنها به کاربرانی که از آنها نام برده شده فرستاده خواهد شد.",
|
"compose_form.direct_message_warning": "این فرسته تنها به کاربرانی که از آنها نام برده شده فرستاده خواهد شد.",
|
||||||
"compose_form.direct_message_warning_learn_more": "بیشتر بدانید",
|
"compose_form.direct_message_warning_learn_more": "بیشتر بدانید",
|
||||||
"compose_form.hashtag_warning": "از آنجا که این فرسته فهرستنشده است، در نتایج جستوجوی برچسبها پیدا نخواهد شد. تنها فرستههای عمومی را میتوان با جستوجوی برچسب یافت.",
|
"compose_form.hashtag_warning": "از آنجا که این فرسته فهرست نشده است، در نتایج جستوجوی هشتگها پیدا نخواهد شد. تنها فرستههای عمومی را میتوان با جستوجوی هشتگ یافت.",
|
||||||
"compose_form.lock_disclaimer": "حسابتان {locked} نیست. هر کسی میتواند پیگیرتان شده و فرستههای ویژهٔ پیگیرانتان را ببیند.",
|
"compose_form.lock_disclaimer": "حسابتان {locked} نیست. هر کسی میتواند پیگیرتان شده و فرستههای ویژهٔ پیگیرانتان را ببیند.",
|
||||||
"compose_form.lock_disclaimer.lock": "قفلشده",
|
"compose_form.lock_disclaimer.lock": "قفلشده",
|
||||||
"compose_form.placeholder": "تازه چه خبر؟",
|
"compose_form.placeholder": "تازه چه خبر؟",
|
||||||
|
@ -144,7 +144,7 @@
|
||||||
"directory.local": "تنها از {domain}",
|
"directory.local": "تنها از {domain}",
|
||||||
"directory.new_arrivals": "تازهواردان",
|
"directory.new_arrivals": "تازهواردان",
|
||||||
"directory.recently_active": "کاربران فعال اخیر",
|
"directory.recently_active": "کاربران فعال اخیر",
|
||||||
"embed.instructions": "برای جاگذاری این فرسته در سایت خودتان، کد زیر را کپی کنید.",
|
"embed.instructions": "برای جاسازی این فرسته در سایت خودتان، کد زیر را رونوشت کنید.",
|
||||||
"embed.preview": "این گونه دیده خواهد شد:",
|
"embed.preview": "این گونه دیده خواهد شد:",
|
||||||
"emoji_button.activity": "فعالیت",
|
"emoji_button.activity": "فعالیت",
|
||||||
"emoji_button.custom": "سفارشی",
|
"emoji_button.custom": "سفارشی",
|
||||||
|
@ -164,11 +164,11 @@
|
||||||
"empty_column.account_timeline": "هیچ فرستهای اینجا نیست!",
|
"empty_column.account_timeline": "هیچ فرستهای اینجا نیست!",
|
||||||
"empty_column.account_unavailable": "نمایهٔ موجود نیست",
|
"empty_column.account_unavailable": "نمایهٔ موجود نیست",
|
||||||
"empty_column.blocks": "هنوز کسی را مسدود نکردهاید.",
|
"empty_column.blocks": "هنوز کسی را مسدود نکردهاید.",
|
||||||
"empty_column.bookmarked_statuses": "هنوز هیچ فرستهٔ نشانشدهای ندارید. هنگامی که فرستهای را نشانکنید، اینجا نشان داده خواهد شد.",
|
"empty_column.bookmarked_statuses": "هنوز هیچ فرستهٔ نشانهگذاری شدهای ندارید. هنگامی که فرستهای را نشانهگذاری کنید، اینجا نشان داده خواهد شد.",
|
||||||
"empty_column.community": "خط زمانی محلّی خالی است. چیزی بنویسید تا چرخش بچرخد!",
|
"empty_column.community": "خط زمانی محلّی خالی است. چیزی بنویسید تا چرخش بچرخد!",
|
||||||
"empty_column.direct": "هنوز هیچ پیام مستقیمی ندارید. هنگامی که چنین پیامی بگیرید یا بفرستید اینجا نشان داده خواهد شد.",
|
"empty_column.direct": "هنوز هیچ پیام مستقیمی ندارید. هنگامی که چنین پیامی بگیرید یا بفرستید اینجا نشان داده خواهد شد.",
|
||||||
"empty_column.domain_blocks": "هنوز هیچ دامنهای مسدود نشده است.",
|
"empty_column.domain_blocks": "هنوز هیچ دامنهای مسدود نشده است.",
|
||||||
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
|
"empty_column.explore_statuses": "الآن چیزی پرطرفدار نیست. بعداً دوباره بررسی کنید!",
|
||||||
"empty_column.favourited_statuses": "شما هنوز هیچ فرستهای را نپسندیدهاید. هنگامی که فرستهای را بپسندید، اینجا نشان داده خواهد شد.",
|
"empty_column.favourited_statuses": "شما هنوز هیچ فرستهای را نپسندیدهاید. هنگامی که فرستهای را بپسندید، اینجا نشان داده خواهد شد.",
|
||||||
"empty_column.favourites": "هنوز هیچ کسی این فرسته را نپسندیده است. هنگامی که کسی آن را بپسندد، اینجا نشان داده خواهد شد.",
|
"empty_column.favourites": "هنوز هیچ کسی این فرسته را نپسندیده است. هنگامی که کسی آن را بپسندد، اینجا نشان داده خواهد شد.",
|
||||||
"empty_column.follow_recommendations": "ظاهرا هیچ پیشنهادی برای شما نمیتوانیم تولید کنیم. میتوانید از امکان جستوجو برای یافتن افرادی که ممکن است بشناسید و یا کاوش میان برچسبهای داغ استفاده کنید.",
|
"empty_column.follow_recommendations": "ظاهرا هیچ پیشنهادی برای شما نمیتوانیم تولید کنیم. میتوانید از امکان جستوجو برای یافتن افرادی که ممکن است بشناسید و یا کاوش میان برچسبهای داغ استفاده کنید.",
|
||||||
|
@ -247,7 +247,7 @@
|
||||||
"keyboard_shortcuts.my_profile": "گشودن نمایهتان",
|
"keyboard_shortcuts.my_profile": "گشودن نمایهتان",
|
||||||
"keyboard_shortcuts.notifications": "گشودن ستون آگاهیها",
|
"keyboard_shortcuts.notifications": "گشودن ستون آگاهیها",
|
||||||
"keyboard_shortcuts.open_media": "گشودن رسانه",
|
"keyboard_shortcuts.open_media": "گشودن رسانه",
|
||||||
"keyboard_shortcuts.pinned": "گشودن سیاههٔ فرستههای سنجاق شده",
|
"keyboard_shortcuts.pinned": "گشودن فهرست فرستههای سنجاق شده",
|
||||||
"keyboard_shortcuts.profile": "گشودن نمایهٔ نویسنده",
|
"keyboard_shortcuts.profile": "گشودن نمایهٔ نویسنده",
|
||||||
"keyboard_shortcuts.reply": "پاسخ به فرسته",
|
"keyboard_shortcuts.reply": "پاسخ به فرسته",
|
||||||
"keyboard_shortcuts.requests": "گشودن سیاههٔ درخواستهای پیگیری",
|
"keyboard_shortcuts.requests": "گشودن سیاههٔ درخواستهای پیگیری",
|
||||||
|
@ -305,7 +305,7 @@
|
||||||
"navigation_bar.logout": "خروج",
|
"navigation_bar.logout": "خروج",
|
||||||
"navigation_bar.mutes": "کاربران خموشانده",
|
"navigation_bar.mutes": "کاربران خموشانده",
|
||||||
"navigation_bar.personal": "شخصی",
|
"navigation_bar.personal": "شخصی",
|
||||||
"navigation_bar.pins": "فرستههای سنجاقشده",
|
"navigation_bar.pins": "فرستههای سنجاق شده",
|
||||||
"navigation_bar.preferences": "ترجیحات",
|
"navigation_bar.preferences": "ترجیحات",
|
||||||
"navigation_bar.public_timeline": "خط زمانی همگانی",
|
"navigation_bar.public_timeline": "خط زمانی همگانی",
|
||||||
"navigation_bar.security": "امنیت",
|
"navigation_bar.security": "امنیت",
|
||||||
|
@ -392,40 +392,40 @@
|
||||||
"report.block": "مسدود کردن",
|
"report.block": "مسدود کردن",
|
||||||
"report.block_explanation": "شما فرستههایشان را نخواهید دید. آنها نمیتوانند فرستههایتان را ببینند یا شما را پیبگیرند. آنها میتوانند بگویند که مسدود شدهاند.",
|
"report.block_explanation": "شما فرستههایشان را نخواهید دید. آنها نمیتوانند فرستههایتان را ببینند یا شما را پیبگیرند. آنها میتوانند بگویند که مسدود شدهاند.",
|
||||||
"report.categories.other": "غیره",
|
"report.categories.other": "غیره",
|
||||||
"report.categories.spam": "Spam",
|
"report.categories.spam": "هرزنامه",
|
||||||
"report.categories.violation": "Content violates one or more server rules",
|
"report.categories.violation": "محتوا یک یا چند قانون کارساز را نقض میکند",
|
||||||
"report.category.subtitle": "Choose the best match",
|
"report.category.subtitle": "منطبقترین را انتخاب کنید",
|
||||||
"report.category.title": "Tell us what's going on with this {type}",
|
"report.category.title": "به ما بگویید با این {type} چه مشکلی دارید",
|
||||||
"report.category.title_account": "profile",
|
"report.category.title_account": "نمایه",
|
||||||
"report.category.title_status": "post",
|
"report.category.title_status": "فرسته",
|
||||||
"report.close": "Done",
|
"report.close": "انجام شد",
|
||||||
"report.comment.title": "Is there anything else you think we should know?",
|
"report.comment.title": "آیا چیز دیگری هست که فکر میکنید باید بدانیم؟",
|
||||||
"report.forward": "فرستادن به {target}",
|
"report.forward": "فرستادن به {target}",
|
||||||
"report.forward_hint": "این حساب در کارساز دیگری ثبت شده. آیا میخواهید رونوشتی ناشناس از این گزارش به آنجا هم فرستاده شود؟",
|
"report.forward_hint": "این حساب در کارساز دیگری ثبت شده. آیا میخواهید رونوشتی ناشناس از این گزارش به آنجا هم فرستاده شود؟",
|
||||||
"report.mute": "Mute",
|
"report.mute": "خموش",
|
||||||
"report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.",
|
"report.mute_explanation": "شما فرستههای آنها را نخواهید دید. آنها همچنان میتوانند شما را پیبگیرند و فرستههایتان را ببینند و نمیدانند که خموش شدهاند.",
|
||||||
"report.next": "Next",
|
"report.next": "بعدی",
|
||||||
"report.placeholder": "توضیحات اضافه",
|
"report.placeholder": "توضیحات اضافه",
|
||||||
"report.reasons.dislike": "I don't like it",
|
"report.reasons.dislike": "من آن را دوست ندارم",
|
||||||
"report.reasons.dislike_description": "It is not something you want to see",
|
"report.reasons.dislike_description": "این چیزی نیست که بخواهید ببینید",
|
||||||
"report.reasons.other": "It's something else",
|
"report.reasons.other": "بخواطر چیز دیگری است",
|
||||||
"report.reasons.other_description": "The issue does not fit into other categories",
|
"report.reasons.other_description": "این موضوع در دستهبندیهای دیگر نمیگنجد",
|
||||||
"report.reasons.spam": "It's spam",
|
"report.reasons.spam": "این هرزنامه است",
|
||||||
"report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies",
|
"report.reasons.spam_description": "پیوندهای مخرب، تعامل جعلی یا پاسخهای تکراری",
|
||||||
"report.reasons.violation": "It violates server rules",
|
"report.reasons.violation": "قوانین کارساز را نقض میکند",
|
||||||
"report.reasons.violation_description": "You are aware that it breaks specific rules",
|
"report.reasons.violation_description": "شما آگاه هستید که قوانین خاصی را زیر پا میگذارد",
|
||||||
"report.rules.subtitle": "Select all that apply",
|
"report.rules.subtitle": "همهٔ موارد انجام شده را برگزینید",
|
||||||
"report.rules.title": "Which rules are being violated?",
|
"report.rules.title": "کدام قوانین نقض شدهاند؟",
|
||||||
"report.statuses.subtitle": "Select all that apply",
|
"report.statuses.subtitle": "همهٔ موارد انجام شده را برگزینید",
|
||||||
"report.statuses.title": "Are there any posts that back up this report?",
|
"report.statuses.title": "آیا فرستهای وجود دارد که از این گزارش پشتیبانی کند؟",
|
||||||
"report.submit": "فرستادن",
|
"report.submit": "فرستادن",
|
||||||
"report.target": "در حال گزارش {target}",
|
"report.target": "در حال گزارش {target}",
|
||||||
"report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:",
|
"report.thanks.take_action": "در اینجا گزینههایی برای کنترل آنچه در ماستودون میبینید، وجود دارد:",
|
||||||
"report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:",
|
"report.thanks.take_action_actionable": "در حالی که ما این مورد را بررسی میکنیم، میتوانید علیه @{name} اقدام کنید:",
|
||||||
"report.thanks.title": "Don't want to see this?",
|
"report.thanks.title": "نمیخواهید این را ببینید؟",
|
||||||
"report.thanks.title_actionable": "Thanks for reporting, we'll look into this.",
|
"report.thanks.title_actionable": "ممنون بابت گزارش، ما آن را بررسی خواهیم کرد.",
|
||||||
"report.unfollow": "Unfollow @{name}",
|
"report.unfollow": "ناپیگیری @{name}",
|
||||||
"report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.",
|
"report.unfollow_explanation": "شما این حساب را پیگرفتهاید، برای اینکه دیگر فرستههایش را در خوراک خانهتان نبینید؛ آن را پینگیرید.",
|
||||||
"search.placeholder": "جستوجو",
|
"search.placeholder": "جستوجو",
|
||||||
"search_popout.search_format": "راهنمای جستوجوی پیشرفته",
|
"search_popout.search_format": "راهنمای جستوجوی پیشرفته",
|
||||||
"search_popout.tips.full_text": "جستوجوی متنی ساده فرستههایی که نوشته، پسندیده، تقویتکرده یا در آنها نامبرده شدهاید را به علاوهٔ نامهای کاربری، نامهای نمایشی و برچسبها برمیگرداند.",
|
"search_popout.tips.full_text": "جستوجوی متنی ساده فرستههایی که نوشته، پسندیده، تقویتکرده یا در آنها نامبرده شدهاید را به علاوهٔ نامهای کاربری، نامهای نمایشی و برچسبها برمیگرداند.",
|
||||||
|
@ -434,39 +434,39 @@
|
||||||
"search_popout.tips.text": "جستوجوی متنی ساده برای نامها، نامهای کاربری، و برچسبها",
|
"search_popout.tips.text": "جستوجوی متنی ساده برای نامها، نامهای کاربری، و برچسبها",
|
||||||
"search_popout.tips.user": "کاربر",
|
"search_popout.tips.user": "کاربر",
|
||||||
"search_results.accounts": "افراد",
|
"search_results.accounts": "افراد",
|
||||||
"search_results.all": "All",
|
"search_results.all": "همه",
|
||||||
"search_results.hashtags": "برچسبها",
|
"search_results.hashtags": "برچسبها",
|
||||||
"search_results.nothing_found": "Could not find anything for these search terms",
|
"search_results.nothing_found": "چیزی برای این عبارت جستوجو یافت نشد",
|
||||||
"search_results.statuses": "فرستهها",
|
"search_results.statuses": "فرستهها",
|
||||||
"search_results.statuses_fts_disabled": "جستوجوی محتوای فرستهها در این کارساز ماستودون فعال نشده است.",
|
"search_results.statuses_fts_disabled": "جستوجوی محتوای فرستهها در این کارساز ماستودون به کار انداخته نشده است.",
|
||||||
"search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
|
"search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}",
|
||||||
"status.admin_account": "گشودن واسط مدیریت برای @{name}",
|
"status.admin_account": "گشودن واسط مدیریت برای @{name}",
|
||||||
"status.admin_status": "گشودن این فرسته در واسط مدیریت",
|
"status.admin_status": "گشودن این فرسته در واسط مدیریت",
|
||||||
"status.block": "مسدود کردن @{name}",
|
"status.block": "مسدود کردن @{name}",
|
||||||
"status.bookmark": "نشانک",
|
"status.bookmark": "نشانک",
|
||||||
"status.cancel_reblog_private": "لغو تقویت",
|
"status.cancel_reblog_private": "لغو تقویت",
|
||||||
"status.cannot_reblog": "این فرسته قابل تقویت نیست",
|
"status.cannot_reblog": "این فرسته قابل تقویت نیست",
|
||||||
"status.copy": "رونویسی از نشانی فرسته",
|
"status.copy": "رونوشت پیوند فرسته",
|
||||||
"status.delete": "حذف",
|
"status.delete": "حذف",
|
||||||
"status.detailed_status": "نمایش کامل گفتگو",
|
"status.detailed_status": "نمایش کامل گفتگو",
|
||||||
"status.direct": "پیام مستقیم به @{name}",
|
"status.direct": "پیام مستقیم به @{name}",
|
||||||
"status.edit": "Edit",
|
"status.edit": "ویرایش",
|
||||||
"status.edited": "Edited {date}",
|
"status.edited": "ویرایش شده در {date}",
|
||||||
"status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
|
"status.edited_x_times": "{count, plural, one {{count} مرتبه} other {{count} مرتبه}} ویرایش شد",
|
||||||
"status.embed": "جاگذاری",
|
"status.embed": "جاسازی",
|
||||||
"status.favourite": "پسندیدن",
|
"status.favourite": "پسندیدن",
|
||||||
"status.filtered": "پالوده",
|
"status.filtered": "پالوده",
|
||||||
"status.history.created": "{name} created {date}",
|
"status.history.created": "توسط {name} در {date} ایجاد شد",
|
||||||
"status.history.edited": "{name} edited {date}",
|
"status.history.edited": "توسط {name} در {date} ویرایش شد",
|
||||||
"status.load_more": "بار کردن بیشتر",
|
"status.load_more": "بار کردن بیشتر",
|
||||||
"status.media_hidden": "رسانهٔ نهفته",
|
"status.media_hidden": "رسانهٔ نهفته",
|
||||||
"status.mention": "نامبردن از @{name}",
|
"status.mention": "نامبردن از @{name}",
|
||||||
"status.more": "بیشتر",
|
"status.more": "بیشتر",
|
||||||
"status.mute": "خموشاندن @{name}",
|
"status.mute": "خموشاندن @{name}",
|
||||||
"status.mute_conversation": "خموشاندن گفتوگو",
|
"status.mute_conversation": "خموشاندن گفتوگو",
|
||||||
"status.open": "گسترش این فرسته",
|
"status.open": "گسترش این فرسته",
|
||||||
"status.pin": "سنجاقکردن در نمایه",
|
"status.pin": "سنجاق کردن در نمایه",
|
||||||
"status.pinned": "فرستهٔ سنجاقشده",
|
"status.pinned": "فرستهٔ سنجاق شده",
|
||||||
"status.read_more": "بیشتر بخوانید",
|
"status.read_more": "بیشتر بخوانید",
|
||||||
"status.reblog": "تقویت",
|
"status.reblog": "تقویت",
|
||||||
"status.reblog_private": "تقویت برای مخاطبان نخستین",
|
"status.reblog_private": "تقویت برای مخاطبان نخستین",
|
||||||
|
@ -476,7 +476,7 @@
|
||||||
"status.remove_bookmark": "برداشتن نشانک",
|
"status.remove_bookmark": "برداشتن نشانک",
|
||||||
"status.reply": "پاسخ",
|
"status.reply": "پاسخ",
|
||||||
"status.replyAll": "پاسخ به رشته",
|
"status.replyAll": "پاسخ به رشته",
|
||||||
"status.report": "گزارش @{name}",
|
"status.report": "گزارش @{name}",
|
||||||
"status.sensitive_warning": "محتوای حساس",
|
"status.sensitive_warning": "محتوای حساس",
|
||||||
"status.share": "همرسانی",
|
"status.share": "همرسانی",
|
||||||
"status.show_less": "نمایش کمتر",
|
"status.show_less": "نمایش کمتر",
|
||||||
|
|
|
@ -192,7 +192,7 @@
|
||||||
"errors.unexpected_crash.copy_stacktrace": "スタックトレースをクリップボードにコピー",
|
"errors.unexpected_crash.copy_stacktrace": "スタックトレースをクリップボードにコピー",
|
||||||
"errors.unexpected_crash.report_issue": "問題を報告",
|
"errors.unexpected_crash.report_issue": "問題を報告",
|
||||||
"explore.search_results": "検索結果",
|
"explore.search_results": "検索結果",
|
||||||
"explore.suggested_follows": "あなたに",
|
"explore.suggested_follows": "おすすめ",
|
||||||
"explore.title": "エクスプローラー",
|
"explore.title": "エクスプローラー",
|
||||||
"explore.trending_links": "ニュース",
|
"explore.trending_links": "ニュース",
|
||||||
"explore.trending_statuses": "投稿",
|
"explore.trending_statuses": "投稿",
|
||||||
|
|
|
@ -342,7 +342,7 @@
|
||||||
"notifications.filter.all": "Hemû",
|
"notifications.filter.all": "Hemû",
|
||||||
"notifications.filter.boosts": "Bilindkirî",
|
"notifications.filter.boosts": "Bilindkirî",
|
||||||
"notifications.filter.favourites": "Bijarte",
|
"notifications.filter.favourites": "Bijarte",
|
||||||
"notifications.filter.follows": "Şopîner",
|
"notifications.filter.follows": "Dişopîne",
|
||||||
"notifications.filter.mentions": "Qalkirin",
|
"notifications.filter.mentions": "Qalkirin",
|
||||||
"notifications.filter.polls": "Encamên rapirsiyê",
|
"notifications.filter.polls": "Encamên rapirsiyê",
|
||||||
"notifications.filter.statuses": "Ji kesên tu dişopînî re rojanekirin",
|
"notifications.filter.statuses": "Ji kesên tu dişopînî re rojanekirin",
|
||||||
|
@ -501,7 +501,7 @@
|
||||||
"time_remaining.seconds": "{number, plural, one {# çirke} other {# çirke}} maye",
|
"time_remaining.seconds": "{number, plural, one {# çirke} other {# çirke}} maye",
|
||||||
"timeline_hint.remote_resource_not_displayed": "{resource} Ji rajekerên din nayê dîtin.",
|
"timeline_hint.remote_resource_not_displayed": "{resource} Ji rajekerên din nayê dîtin.",
|
||||||
"timeline_hint.resources.followers": "Şopîner",
|
"timeline_hint.resources.followers": "Şopîner",
|
||||||
"timeline_hint.resources.follows": "Şopîner",
|
"timeline_hint.resources.follows": "Dişopîne",
|
||||||
"timeline_hint.resources.statuses": "Şandiyên kevn",
|
"timeline_hint.resources.statuses": "Şandiyên kevn",
|
||||||
"trends.counter_by_accounts": "{count, plural, one {{counter} kes} other {{counter} kes}} diaxivin",
|
"trends.counter_by_accounts": "{count, plural, one {{counter} kes} other {{counter} kes}} diaxivin",
|
||||||
"trends.trending_now": "Rojev",
|
"trends.trending_now": "Rojev",
|
||||||
|
|
|
@ -9,44 +9,44 @@
|
||||||
"account.browse_more_on_origin_server": "Meer op het originele profiel bekijken",
|
"account.browse_more_on_origin_server": "Meer op het originele profiel bekijken",
|
||||||
"account.cancel_follow_request": "Volgverzoek annuleren",
|
"account.cancel_follow_request": "Volgverzoek annuleren",
|
||||||
"account.direct": "@{name} een direct bericht sturen",
|
"account.direct": "@{name} een direct bericht sturen",
|
||||||
"account.disable_notifications": "Geef geen melding meer wanneer @{name} toot",
|
"account.disable_notifications": "Geef geen melding meer wanneer @{name} een bericht plaatst",
|
||||||
"account.domain_blocked": "Domein geblokkeerd",
|
"account.domain_blocked": "Domein geblokkeerd",
|
||||||
"account.edit_profile": "Profiel bewerken",
|
"account.edit_profile": "Profiel bewerken",
|
||||||
"account.enable_notifications": "Geef een melding wanneer @{name} toot",
|
"account.enable_notifications": "Geef een melding wanneer @{name} een bericht plaatst",
|
||||||
"account.endorse": "Op profiel weergeven",
|
"account.endorse": "Op profiel weergeven",
|
||||||
"account.follow": "Volgen",
|
"account.follow": "Volgen",
|
||||||
"account.followers": "Volgers",
|
"account.followers": "Volgers",
|
||||||
"account.followers.empty": "Niemand volgt nog deze gebruiker.",
|
"account.followers.empty": "Niemand volgt nog deze gebruiker.",
|
||||||
"account.followers_counter": "{count, plural, one {{counter} volger} other {{counter} volgers}}",
|
"account.followers_counter": "{count, plural, one {{counter} volger} other {{counter} volgers}}",
|
||||||
"account.following": "Following",
|
"account.following": "Volgend",
|
||||||
"account.following_counter": "{count, plural, one {{counter} volgend} other {{counter} volgend}}",
|
"account.following_counter": "{count, plural, one {{counter} volgend} other {{counter} volgend}}",
|
||||||
"account.follows.empty": "Deze gebruiker volgt nog niemand.",
|
"account.follows.empty": "Deze gebruiker volgt nog niemand.",
|
||||||
"account.follows_you": "Volgt jou",
|
"account.follows_you": "Volgt jou",
|
||||||
"account.hide_reblogs": "Boosts van @{name} verbergen",
|
"account.hide_reblogs": "Boosts van @{name} verbergen",
|
||||||
"account.joined": "Geregistreerd in {date}",
|
"account.joined": "Geregistreerd in {date}",
|
||||||
"account.link_verified_on": "Eigendom van deze link is gecontroleerd op {date}",
|
"account.link_verified_on": "Eigendom van deze link is gecontroleerd op {date}",
|
||||||
"account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie hen kan volgen.",
|
"account.locked_info": "De privacystatus van dit account is op besloten gezet. De eigenaar bepaalt handmatig wie diegene kan volgen.",
|
||||||
"account.media": "Media",
|
"account.media": "Media",
|
||||||
"account.mention": "@{name} vermelden",
|
"account.mention": "@{name} vermelden",
|
||||||
"account.moved_to": "{name} is verhuisd naar:",
|
"account.moved_to": "{name} is verhuisd naar:",
|
||||||
"account.mute": "@{name} negeren",
|
"account.mute": "@{name} negeren",
|
||||||
"account.mute_notifications": "Meldingen van @{name} negeren",
|
"account.mute_notifications": "Meldingen van @{name} negeren",
|
||||||
"account.muted": "Genegeerd",
|
"account.muted": "Genegeerd",
|
||||||
"account.posts": "Toots",
|
"account.posts": "Berichten",
|
||||||
"account.posts_with_replies": "Toots en reacties",
|
"account.posts_with_replies": "Berichten en reacties",
|
||||||
"account.report": "@{name} rapporteren",
|
"account.report": "@{name} rapporteren",
|
||||||
"account.requested": "Wacht op goedkeuring. Klik om het volgverzoek te annuleren",
|
"account.requested": "Wacht op goedkeuring. Klik om het volgverzoek te annuleren",
|
||||||
"account.share": "Profiel van @{name} delen",
|
"account.share": "Profiel van @{name} delen",
|
||||||
"account.show_reblogs": "Boosts van @{name} tonen",
|
"account.show_reblogs": "Boosts van @{name} tonen",
|
||||||
"account.statuses_counter": "{count, plural, one {{counter} toot} other {{counter} toots}}",
|
"account.statuses_counter": "{count, plural, one {{counter} bericht} other {{counter} berichten}}",
|
||||||
"account.unblock": "@{name} deblokkeren",
|
"account.unblock": "@{name} deblokkeren",
|
||||||
"account.unblock_domain": "{domain} niet langer verbergen",
|
"account.unblock_domain": "{domain} niet langer verbergen",
|
||||||
"account.unblock_short": "Unblock",
|
"account.unblock_short": "Deblokkeren",
|
||||||
"account.unendorse": "Niet op profiel weergeven",
|
"account.unendorse": "Niet op profiel weergeven",
|
||||||
"account.unfollow": "Ontvolgen",
|
"account.unfollow": "Ontvolgen",
|
||||||
"account.unmute": "@{name} niet langer negeren",
|
"account.unmute": "@{name} niet langer negeren",
|
||||||
"account.unmute_notifications": "Meldingen van @{name} niet langer negeren",
|
"account.unmute_notifications": "Meldingen van @{name} niet langer negeren",
|
||||||
"account.unmute_short": "Unmute",
|
"account.unmute_short": "Niet langer negeren",
|
||||||
"account_note.placeholder": "Klik om een opmerking toe te voegen",
|
"account_note.placeholder": "Klik om een opmerking toe te voegen",
|
||||||
"admin.dashboard.daily_retention": "User retention rate by day after sign-up",
|
"admin.dashboard.daily_retention": "User retention rate by day after sign-up",
|
||||||
"admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
|
"admin.dashboard.monthly_retention": "User retention rate by month after sign-up",
|
||||||
|
@ -79,7 +79,7 @@
|
||||||
"column.lists": "Lijsten",
|
"column.lists": "Lijsten",
|
||||||
"column.mutes": "Genegeerde gebruikers",
|
"column.mutes": "Genegeerde gebruikers",
|
||||||
"column.notifications": "Meldingen",
|
"column.notifications": "Meldingen",
|
||||||
"column.pins": "Vastgezette toots",
|
"column.pins": "Vastgezette berichten",
|
||||||
"column.public": "Globale tijdlijn",
|
"column.public": "Globale tijdlijn",
|
||||||
"column_back_button.label": "Terug",
|
"column_back_button.label": "Terug",
|
||||||
"column_header.hide_settings": "Instellingen verbergen",
|
"column_header.hide_settings": "Instellingen verbergen",
|
||||||
|
@ -92,10 +92,10 @@
|
||||||
"community.column_settings.local_only": "Alleen lokaal",
|
"community.column_settings.local_only": "Alleen lokaal",
|
||||||
"community.column_settings.media_only": "Alleen media",
|
"community.column_settings.media_only": "Alleen media",
|
||||||
"community.column_settings.remote_only": "Alleen andere servers",
|
"community.column_settings.remote_only": "Alleen andere servers",
|
||||||
"compose_form.direct_message_warning": "Deze toot wordt alleen naar vermelde gebruikers verstuurd.",
|
"compose_form.direct_message_warning": "Dit bericht wordt alleen naar vermelde gebruikers verstuurd.",
|
||||||
"compose_form.direct_message_warning_learn_more": "Meer leren",
|
"compose_form.direct_message_warning_learn_more": "Meer leren",
|
||||||
"compose_form.hashtag_warning": "Deze toot valt niet onder een hashtag te bekijken, omdat deze niet op openbare tijdlijnen wordt getoond. Alleen openbare toots kunnen via hashtags gevonden worden.",
|
"compose_form.hashtag_warning": "Dit bericht valt niet onder een hashtag te bekijken, omdat deze niet op openbare tijdlijnen wordt getoond. Alleen openbare berichten kunnen via hashtags gevonden worden.",
|
||||||
"compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en kan de toots zien die je alleen aan jouw volgers hebt gericht.",
|
"compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en kan de berichten zien die je alleen aan jouw volgers hebt gericht.",
|
||||||
"compose_form.lock_disclaimer.lock": "besloten",
|
"compose_form.lock_disclaimer.lock": "besloten",
|
||||||
"compose_form.placeholder": "Wat wil je kwijt?",
|
"compose_form.placeholder": "Wat wil je kwijt?",
|
||||||
"compose_form.poll.add_option": "Keuze toevoegen",
|
"compose_form.poll.add_option": "Keuze toevoegen",
|
||||||
|
@ -106,7 +106,7 @@
|
||||||
"compose_form.poll.switch_to_single": "Poll wijzigen om een enkele keuze toe te staan",
|
"compose_form.poll.switch_to_single": "Poll wijzigen om een enkele keuze toe te staan",
|
||||||
"compose_form.publish": "Toot",
|
"compose_form.publish": "Toot",
|
||||||
"compose_form.publish_loud": "{publish}!",
|
"compose_form.publish_loud": "{publish}!",
|
||||||
"compose_form.save_changes": "Save changes",
|
"compose_form.save_changes": "Wijzigingen opslaan",
|
||||||
"compose_form.sensitive.hide": "{count, plural, one {Media als gevoelig markeren} other {Media als gevoelig markeren}}",
|
"compose_form.sensitive.hide": "{count, plural, one {Media als gevoelig markeren} other {Media als gevoelig markeren}}",
|
||||||
"compose_form.sensitive.marked": "{count, plural, one {Media is als gevoelig gemarkeerd} other {Media is als gevoelig gemarkeerd}}",
|
"compose_form.sensitive.marked": "{count, plural, one {Media is als gevoelig gemarkeerd} other {Media is als gevoelig gemarkeerd}}",
|
||||||
"compose_form.sensitive.unmarked": "{count, plural, one {Media is niet als gevoelig gemarkeerd} other {Media is niet als gevoelig gemarkeerd}}",
|
"compose_form.sensitive.unmarked": "{count, plural, one {Media is niet als gevoelig gemarkeerd} other {Media is niet als gevoelig gemarkeerd}}",
|
||||||
|
@ -118,22 +118,22 @@
|
||||||
"confirmations.block.confirm": "Blokkeren",
|
"confirmations.block.confirm": "Blokkeren",
|
||||||
"confirmations.block.message": "Weet je het zeker dat je {name} wilt blokkeren?",
|
"confirmations.block.message": "Weet je het zeker dat je {name} wilt blokkeren?",
|
||||||
"confirmations.delete.confirm": "Verwijderen",
|
"confirmations.delete.confirm": "Verwijderen",
|
||||||
"confirmations.delete.message": "Weet je het zeker dat je deze toot wilt verwijderen?",
|
"confirmations.delete.message": "Weet je het zeker dat je dit bericht wilt verwijderen?",
|
||||||
"confirmations.delete_list.confirm": "Verwijderen",
|
"confirmations.delete_list.confirm": "Verwijderen",
|
||||||
"confirmations.delete_list.message": "Weet je zeker dat je deze lijst definitief wilt verwijderen?",
|
"confirmations.delete_list.message": "Weet je zeker dat je deze lijst definitief wilt verwijderen?",
|
||||||
"confirmations.discard_edit_media.confirm": "Weggooien",
|
"confirmations.discard_edit_media.confirm": "Weggooien",
|
||||||
"confirmations.discard_edit_media.message": "Je hebt niet-opgeslagen wijzigingen in de mediabeschrijving of voorvertonning, wil je deze toch weggooien?",
|
"confirmations.discard_edit_media.message": "Je hebt niet-opgeslagen wijzigingen in de mediabeschrijving of voorvertonning, wil je deze toch weggooien?",
|
||||||
"confirmations.domain_block.confirm": "Verberg alles van deze server",
|
"confirmations.domain_block.confirm": "Verberg alles van deze server",
|
||||||
"confirmations.domain_block.message": "Weet je het echt heel erg zeker dat je alles van {domain} wilt negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en beter. Je zult geen toots van deze server op openbare tijdlijnen zien of in jouw meldingen. Jouw volgers van deze server worden verwijderd.",
|
"confirmations.domain_block.message": "Weet je het echt heel erg zeker dat je alles van {domain} wilt negeren? In de meeste gevallen is het blokkeren of negeren van een paar specifieke personen voldoende en beter. Je zult geen berichten van deze server op openbare tijdlijnen zien of in jouw meldingen. Jouw volgers van deze server worden verwijderd.",
|
||||||
"confirmations.logout.confirm": "Uitloggen",
|
"confirmations.logout.confirm": "Uitloggen",
|
||||||
"confirmations.logout.message": "Weet je zeker dat je wilt uitloggen?",
|
"confirmations.logout.message": "Weet je zeker dat je wilt uitloggen?",
|
||||||
"confirmations.mute.confirm": "Negeren",
|
"confirmations.mute.confirm": "Negeren",
|
||||||
"confirmations.mute.explanation": "Dit verbergt toots van hen en toots waar hen in wordt vermeld, maar hen kan nog steeds jouw toots bekijken en jou volgen.",
|
"confirmations.mute.explanation": "Dit verbergt diens berichten en berichten waar diegene in wordt vermeld, maar diegene kan nog steeds jouw berichten bekijken en jou volgen.",
|
||||||
"confirmations.mute.message": "Weet je het zeker dat je {name} wilt negeren?",
|
"confirmations.mute.message": "Weet je het zeker dat je {name} wilt negeren?",
|
||||||
"confirmations.redraft.confirm": "Verwijderen en herschrijven",
|
"confirmations.redraft.confirm": "Verwijderen en herschrijven",
|
||||||
"confirmations.redraft.message": "Weet je zeker dat je deze toot wilt verwijderen en herschrijven? Je verliest wel de boosts en favorieten, en de reacties op de originele toot zitten niet meer aan de nieuwe toot vast.",
|
"confirmations.redraft.message": "Weet je zeker dat je dit bericht wilt verwijderen en herschrijven? Je verliest wel de boosts en favorieten, en de reacties op het originele bericht raak je kwijt.",
|
||||||
"confirmations.reply.confirm": "Reageren",
|
"confirmations.reply.confirm": "Reageren",
|
||||||
"confirmations.reply.message": "Door nu te reageren overschrijf je de toot die je op dit moment aan het schrijven bent. Weet je zeker dat je verder wil gaan?",
|
"confirmations.reply.message": "Door nu te reageren overschrijf je het bericht dat je op dit moment aan het schrijven bent. Weet je zeker dat je verder wil gaan?",
|
||||||
"confirmations.unfollow.confirm": "Ontvolgen",
|
"confirmations.unfollow.confirm": "Ontvolgen",
|
||||||
"confirmations.unfollow.message": "Weet je het zeker dat je {name} wilt ontvolgen?",
|
"confirmations.unfollow.message": "Weet je het zeker dat je {name} wilt ontvolgen?",
|
||||||
"conversation.delete": "Gesprek verwijderen",
|
"conversation.delete": "Gesprek verwijderen",
|
||||||
|
@ -144,7 +144,7 @@
|
||||||
"directory.local": "Alleen {domain}",
|
"directory.local": "Alleen {domain}",
|
||||||
"directory.new_arrivals": "Nieuwe accounts",
|
"directory.new_arrivals": "Nieuwe accounts",
|
||||||
"directory.recently_active": "Onlangs actief",
|
"directory.recently_active": "Onlangs actief",
|
||||||
"embed.instructions": "Embed deze toot op jouw website, door de onderstaande code te kopiëren.",
|
"embed.instructions": "Embed dit bericht op jouw website door de onderstaande code te kopiëren.",
|
||||||
"embed.preview": "Zo komt het eruit te zien:",
|
"embed.preview": "Zo komt het eruit te zien:",
|
||||||
"emoji_button.activity": "Activiteiten",
|
"emoji_button.activity": "Activiteiten",
|
||||||
"emoji_button.custom": "Lokale emoji’s",
|
"emoji_button.custom": "Lokale emoji’s",
|
||||||
|
@ -161,41 +161,41 @@
|
||||||
"emoji_button.symbols": "Symbolen",
|
"emoji_button.symbols": "Symbolen",
|
||||||
"emoji_button.travel": "Reizen en locaties",
|
"emoji_button.travel": "Reizen en locaties",
|
||||||
"empty_column.account_suspended": "Account opgeschort",
|
"empty_column.account_suspended": "Account opgeschort",
|
||||||
"empty_column.account_timeline": "Hier zijn geen toots!",
|
"empty_column.account_timeline": "Hier zijn geen berichten!",
|
||||||
"empty_column.account_unavailable": "Profiel is niet beschikbaar",
|
"empty_column.account_unavailable": "Profiel is niet beschikbaar",
|
||||||
"empty_column.blocks": "Jij hebt nog geen enkele gebruiker geblokkeerd.",
|
"empty_column.blocks": "Jij hebt nog geen enkele gebruiker geblokkeerd.",
|
||||||
"empty_column.bookmarked_statuses": "Jij hebt nog geen toots aan je bladwijzers toegevoegd. Wanneer je er een aan jouw bladwijzers toevoegt, valt deze hier te zien.",
|
"empty_column.bookmarked_statuses": "Jij hebt nog geen berichten aan je bladwijzers toegevoegd. Wanneer je er een aan jouw bladwijzers toevoegt, valt deze hier te zien.",
|
||||||
"empty_column.community": "De lokale tijdlijn is nog leeg. Toot iets in het openbaar om de spits af te bijten!",
|
"empty_column.community": "De lokale tijdlijn is nog leeg. Plaats een openbaar bericht om de spits af te bijten!",
|
||||||
"empty_column.direct": "Je hebt nog geen directe berichten. Wanneer je er een verzend of ontvangt, zijn deze hier te zien.",
|
"empty_column.direct": "Je hebt nog geen directe berichten. Wanneer je er een verzend of ontvangt, zijn deze hier te zien.",
|
||||||
"empty_column.domain_blocks": "Er zijn nog geen geblokkeerde domeinen.",
|
"empty_column.domain_blocks": "Er zijn nog geen geblokkeerde domeinen.",
|
||||||
"empty_column.explore_statuses": "Nothing is trending right now. Check back later!",
|
"empty_column.explore_statuses": "Momenteel zijn er geen trends. Kom later terug!",
|
||||||
"empty_column.favourited_statuses": "Jij hebt nog geen favoriete toots. Wanneer je er een aan jouw favorieten toevoegt, valt deze hier te zien.",
|
"empty_column.favourited_statuses": "Jij hebt nog geen favoriete berichten. Wanneer je er een aan jouw favorieten toevoegt, valt deze hier te zien.",
|
||||||
"empty_column.favourites": "Niemand heeft deze toot nog aan hun favorieten toegevoegd. Wanneer iemand dit doet, valt dat hier te zien.",
|
"empty_column.favourites": "Niemand heeft dit bericht nog aan diens favorieten toegevoegd. Wanneer iemand dit doet, valt dat hier te zien.",
|
||||||
"empty_column.follow_recommendations": "Het lijkt er op dat er geen aanbevelingen voor jou aangemaakt kunnen worden. Je kunt proberen te zoeken naar mensen die je wellicht kent, zoeken op hashtags, de lokale en globale tijdlijnen bekijken of de gebruikersgids doorbladeren.",
|
"empty_column.follow_recommendations": "Het lijkt er op dat er geen aanbevelingen voor jou aangemaakt kunnen worden. Je kunt proberen te zoeken naar mensen die je wellicht kent, zoeken op hashtags, de lokale en globale tijdlijnen bekijken of de gebruikersgids doorbladeren.",
|
||||||
"empty_column.follow_requests": "Jij hebt nog enkel volgverzoek ontvangen. Wanneer je er eentje ontvangt, valt dat hier te zien.",
|
"empty_column.follow_requests": "Jij hebt nog enkel volgverzoek ontvangen. Wanneer je er eentje ontvangt, valt dat hier te zien.",
|
||||||
"empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
|
"empty_column.hashtag": "Er is nog niks te vinden onder deze hashtag.",
|
||||||
"empty_column.home": "Deze tijdlijn is leeg! Volg meer mensen om het te vullen. {suggestions}",
|
"empty_column.home": "Deze tijdlijn is leeg! Volg meer mensen om het te vullen. {suggestions}",
|
||||||
"empty_column.home.suggestions": "Enkele aanbevelingen bekijken",
|
"empty_column.home.suggestions": "Enkele aanbevelingen bekijken",
|
||||||
"empty_column.list": "Er is nog niks te zien in deze lijst. Wanneer lijstleden nieuwe toots publiceren, zijn deze hier te zien.",
|
"empty_column.list": "Er is nog niks te zien in deze lijst. Wanneer lijstleden nieuwe berichten plaatsen, zijn deze hier te zien.",
|
||||||
"empty_column.lists": "Jij hebt nog geen enkele lijst. Wanneer je er eentje hebt aangemaakt, valt deze hier te zien.",
|
"empty_column.lists": "Jij hebt nog geen enkele lijst. Wanneer je er eentje hebt aangemaakt, valt deze hier te zien.",
|
||||||
"empty_column.mutes": "Jij hebt nog geen gebruikers genegeerd.",
|
"empty_column.mutes": "Jij hebt nog geen gebruikers genegeerd.",
|
||||||
"empty_column.notifications": "Je hebt nog geen meldingen. Begin met iemand een gesprek.",
|
"empty_column.notifications": "Je hebt nog geen meldingen. Begin met iemand een gesprek.",
|
||||||
"empty_column.public": "Er is hier helemaal niks! Toot iets in het openbaar of volg mensen van andere servers om het te vullen",
|
"empty_column.public": "Er is hier helemaal niks! Plaatst een openbaar bericht of volg mensen van andere servers om het te vullen",
|
||||||
"error.unexpected_crash.explanation": "Als gevolg van een bug in onze broncode of als gevolg van een compatibiliteitsprobleem met jouw webbrowser, kan deze pagina niet goed worden weergegeven.",
|
"error.unexpected_crash.explanation": "Als gevolg van een bug in onze broncode of als gevolg van een compatibiliteitsprobleem met jouw webbrowser, kan deze pagina niet goed worden weergegeven.",
|
||||||
"error.unexpected_crash.explanation_addons": "Deze pagina kon niet correct geladen worden. Deze fout wordt waarschijnlijk door een browser-add-on of een automatische vertalingshulpmiddel veroorzaakt.",
|
"error.unexpected_crash.explanation_addons": "Deze pagina kon niet correct geladen worden. Deze fout wordt waarschijnlijk door een browser-add-on of een automatische vertalingshulpmiddel veroorzaakt.",
|
||||||
"error.unexpected_crash.next_steps": "Probeer deze pagina te vernieuwen. Wanneer dit niet helpt is het nog steeds mogelijk om Mastodon in een andere webbrowser of mobiele app te gebruiken.",
|
"error.unexpected_crash.next_steps": "Probeer deze pagina te vernieuwen. Wanneer dit niet helpt is het nog steeds mogelijk om Mastodon in een andere webbrowser of mobiele app te gebruiken.",
|
||||||
"error.unexpected_crash.next_steps_addons": "Probeer deze uit te schakelen en de pagina te verversen. Wanneer dat niet helpt, kun je Mastodon nog altijd met een andere webbrowser of mobiele app gebruiken.",
|
"error.unexpected_crash.next_steps_addons": "Probeer deze uit te schakelen en de pagina te verversen. Wanneer dat niet helpt, kun je Mastodon nog altijd met een andere webbrowser of mobiele app gebruiken.",
|
||||||
"errors.unexpected_crash.copy_stacktrace": "Stacktrace naar klembord kopiëren",
|
"errors.unexpected_crash.copy_stacktrace": "Stacktrace naar klembord kopiëren",
|
||||||
"errors.unexpected_crash.report_issue": "Technisch probleem melden",
|
"errors.unexpected_crash.report_issue": "Technisch probleem melden",
|
||||||
"explore.search_results": "Search results",
|
"explore.search_results": "Zoekresultaten",
|
||||||
"explore.suggested_follows": "For you",
|
"explore.suggested_follows": "Voor jou",
|
||||||
"explore.title": "Explore",
|
"explore.title": "Verkennen",
|
||||||
"explore.trending_links": "News",
|
"explore.trending_links": "Nieuws",
|
||||||
"explore.trending_statuses": "Posts",
|
"explore.trending_statuses": "Berichten",
|
||||||
"explore.trending_tags": "Hashtags",
|
"explore.trending_tags": "Hashtags",
|
||||||
"follow_recommendations.done": "Klaar",
|
"follow_recommendations.done": "Klaar",
|
||||||
"follow_recommendations.heading": "Volg mensen waarvan je graag toots wil zien! Hier zijn enkele aanbevelingen.",
|
"follow_recommendations.heading": "Volg mensen waarvan je graag berichten wil zien! Hier zijn enkele aanbevelingen.",
|
||||||
"follow_recommendations.lead": "Toots van mensen die je volgt zullen in chronologische volgorde onder start verschijnen. Wees niet bang om hierin fouten te maken, want je kunt mensen op elk moment net zo eenvoudig ontvolgen!",
|
"follow_recommendations.lead": "Berichten van mensen die je volgt zullen in chronologische volgorde onder start verschijnen. Wees niet bang om hierin fouten te maken, want je kunt mensen op elk moment net zo eenvoudig ontvolgen!",
|
||||||
"follow_request.authorize": "Goedkeuren",
|
"follow_request.authorize": "Goedkeuren",
|
||||||
"follow_request.reject": "Afkeuren",
|
"follow_request.reject": "Afkeuren",
|
||||||
"follow_requests.unlocked_explanation": "Ook al is jouw account niet besloten, de medewerkers van {domain} denken dat jij misschien de volgende volgverzoeken handmatig wil controleren.",
|
"follow_requests.unlocked_explanation": "Ook al is jouw account niet besloten, de medewerkers van {domain} denken dat jij misschien de volgende volgverzoeken handmatig wil controleren.",
|
||||||
|
@ -227,13 +227,13 @@
|
||||||
"intervals.full.minutes": "{number, plural, one {# minuut} other {# minuten}}",
|
"intervals.full.minutes": "{number, plural, one {# minuut} other {# minuten}}",
|
||||||
"keyboard_shortcuts.back": "Ga terug",
|
"keyboard_shortcuts.back": "Ga terug",
|
||||||
"keyboard_shortcuts.blocked": "Geblokkeerde gebruikers tonen",
|
"keyboard_shortcuts.blocked": "Geblokkeerde gebruikers tonen",
|
||||||
"keyboard_shortcuts.boost": "Toot boosten",
|
"keyboard_shortcuts.boost": "Bericht boosten",
|
||||||
"keyboard_shortcuts.column": "Op één van de kolommen focussen",
|
"keyboard_shortcuts.column": "Op één van de kolommen focussen",
|
||||||
"keyboard_shortcuts.compose": "Tekstveld voor toots focussen",
|
"keyboard_shortcuts.compose": "Tekstveld om een bericht te schrijven focussen",
|
||||||
"keyboard_shortcuts.description": "Omschrijving",
|
"keyboard_shortcuts.description": "Omschrijving",
|
||||||
"keyboard_shortcuts.direct": "Jouw directe berichten tonen",
|
"keyboard_shortcuts.direct": "Jouw directe berichten tonen",
|
||||||
"keyboard_shortcuts.down": "Naar beneden in de lijst bewegen",
|
"keyboard_shortcuts.down": "Naar beneden in de lijst bewegen",
|
||||||
"keyboard_shortcuts.enter": "Toot volledig tonen",
|
"keyboard_shortcuts.enter": "Volledig bericht tonen",
|
||||||
"keyboard_shortcuts.favourite": "Aan jouw favorieten toevoegen",
|
"keyboard_shortcuts.favourite": "Aan jouw favorieten toevoegen",
|
||||||
"keyboard_shortcuts.favourites": "Favorieten tonen",
|
"keyboard_shortcuts.favourites": "Favorieten tonen",
|
||||||
"keyboard_shortcuts.federated": "Globale tijdlijn tonen",
|
"keyboard_shortcuts.federated": "Globale tijdlijn tonen",
|
||||||
|
@ -247,7 +247,7 @@
|
||||||
"keyboard_shortcuts.my_profile": "Jouw profiel tonen",
|
"keyboard_shortcuts.my_profile": "Jouw profiel tonen",
|
||||||
"keyboard_shortcuts.notifications": "Meldingen tonen",
|
"keyboard_shortcuts.notifications": "Meldingen tonen",
|
||||||
"keyboard_shortcuts.open_media": "Media openen",
|
"keyboard_shortcuts.open_media": "Media openen",
|
||||||
"keyboard_shortcuts.pinned": "Jouw vastgezette toots tonen",
|
"keyboard_shortcuts.pinned": "Jouw vastgemaakte berichten tonen",
|
||||||
"keyboard_shortcuts.profile": "Gebruikersprofiel auteur openen",
|
"keyboard_shortcuts.profile": "Gebruikersprofiel auteur openen",
|
||||||
"keyboard_shortcuts.reply": "Reageren",
|
"keyboard_shortcuts.reply": "Reageren",
|
||||||
"keyboard_shortcuts.requests": "Jouw volgverzoeken tonen",
|
"keyboard_shortcuts.requests": "Jouw volgverzoeken tonen",
|
||||||
|
@ -256,7 +256,7 @@
|
||||||
"keyboard_shortcuts.start": "\"Aan de slag\" tonen",
|
"keyboard_shortcuts.start": "\"Aan de slag\" tonen",
|
||||||
"keyboard_shortcuts.toggle_hidden": "Inhoudswaarschuwing tonen/verbergen",
|
"keyboard_shortcuts.toggle_hidden": "Inhoudswaarschuwing tonen/verbergen",
|
||||||
"keyboard_shortcuts.toggle_sensitivity": "Media tonen/verbergen",
|
"keyboard_shortcuts.toggle_sensitivity": "Media tonen/verbergen",
|
||||||
"keyboard_shortcuts.toot": "Nieuwe toot schrijven",
|
"keyboard_shortcuts.toot": "Nieuw bericht schrijven",
|
||||||
"keyboard_shortcuts.unfocus": "Tekst- en zoekveld ontfocussen",
|
"keyboard_shortcuts.unfocus": "Tekst- en zoekveld ontfocussen",
|
||||||
"keyboard_shortcuts.up": "Naar boven in de lijst bewegen",
|
"keyboard_shortcuts.up": "Naar boven in de lijst bewegen",
|
||||||
"lightbox.close": "Sluiten",
|
"lightbox.close": "Sluiten",
|
||||||
|
@ -289,12 +289,12 @@
|
||||||
"navigation_bar.blocks": "Geblokkeerde gebruikers",
|
"navigation_bar.blocks": "Geblokkeerde gebruikers",
|
||||||
"navigation_bar.bookmarks": "Bladwijzers",
|
"navigation_bar.bookmarks": "Bladwijzers",
|
||||||
"navigation_bar.community_timeline": "Lokale tijdlijn",
|
"navigation_bar.community_timeline": "Lokale tijdlijn",
|
||||||
"navigation_bar.compose": "Nieuw toot schrijven",
|
"navigation_bar.compose": "Nieuw bericht schrijven",
|
||||||
"navigation_bar.direct": "Directe berichten",
|
"navigation_bar.direct": "Directe berichten",
|
||||||
"navigation_bar.discover": "Ontdekken",
|
"navigation_bar.discover": "Ontdekken",
|
||||||
"navigation_bar.domain_blocks": "Geblokkeerde domeinen",
|
"navigation_bar.domain_blocks": "Geblokkeerde domeinen",
|
||||||
"navigation_bar.edit_profile": "Profiel bewerken",
|
"navigation_bar.edit_profile": "Profiel bewerken",
|
||||||
"navigation_bar.explore": "Explore",
|
"navigation_bar.explore": "Verkennen",
|
||||||
"navigation_bar.favourites": "Favorieten",
|
"navigation_bar.favourites": "Favorieten",
|
||||||
"navigation_bar.filters": "Filters",
|
"navigation_bar.filters": "Filters",
|
||||||
"navigation_bar.follow_requests": "Volgverzoeken",
|
"navigation_bar.follow_requests": "Volgverzoeken",
|
||||||
|
@ -305,23 +305,23 @@
|
||||||
"navigation_bar.logout": "Uitloggen",
|
"navigation_bar.logout": "Uitloggen",
|
||||||
"navigation_bar.mutes": "Genegeerde gebruikers",
|
"navigation_bar.mutes": "Genegeerde gebruikers",
|
||||||
"navigation_bar.personal": "Persoonlijk",
|
"navigation_bar.personal": "Persoonlijk",
|
||||||
"navigation_bar.pins": "Vastgezette toots",
|
"navigation_bar.pins": "Vastgemaakte berichten",
|
||||||
"navigation_bar.preferences": "Instellingen",
|
"navigation_bar.preferences": "Instellingen",
|
||||||
"navigation_bar.public_timeline": "Globale tijdlijn",
|
"navigation_bar.public_timeline": "Globale tijdlijn",
|
||||||
"navigation_bar.security": "Beveiliging",
|
"navigation_bar.security": "Beveiliging",
|
||||||
"notification.admin.sign_up": "{name} signed up",
|
"notification.admin.sign_up": "{name} heeft zich aangemeld",
|
||||||
"notification.favourite": "{name} voegde jouw toot als favoriet toe",
|
"notification.favourite": "{name} voegde jouw bericht als favoriet toe",
|
||||||
"notification.follow": "{name} volgt jou nu",
|
"notification.follow": "{name} volgt jou nu",
|
||||||
"notification.follow_request": "{name} wil jou graag volgen",
|
"notification.follow_request": "{name} wil jou graag volgen",
|
||||||
"notification.mention": "{name} vermeldde jou",
|
"notification.mention": "{name} vermeldde jou",
|
||||||
"notification.own_poll": "Jouw poll is beëindigd",
|
"notification.own_poll": "Jouw poll is beëindigd",
|
||||||
"notification.poll": "Een poll waaraan jij hebt meegedaan is beëindigd",
|
"notification.poll": "Een poll waaraan jij hebt meegedaan is beëindigd",
|
||||||
"notification.reblog": "{name} boostte jouw toot",
|
"notification.reblog": "{name} boostte jouw bericht",
|
||||||
"notification.status": "{name} heeft zojuist een toot geplaatst",
|
"notification.status": "{name} heeft zojuist een bericht geplaatst",
|
||||||
"notification.update": "{name} edited a post",
|
"notification.update": "{name} heeft een bericht bewerkt",
|
||||||
"notifications.clear": "Meldingen verwijderen",
|
"notifications.clear": "Meldingen verwijderen",
|
||||||
"notifications.clear_confirmation": "Weet je het zeker dat je al jouw meldingen wilt verwijderen?",
|
"notifications.clear_confirmation": "Weet je het zeker dat je al jouw meldingen wilt verwijderen?",
|
||||||
"notifications.column_settings.admin.sign_up": "New sign-ups:",
|
"notifications.column_settings.admin.sign_up": "Nieuwe aanmeldingen:",
|
||||||
"notifications.column_settings.alert": "Desktopmeldingen",
|
"notifications.column_settings.alert": "Desktopmeldingen",
|
||||||
"notifications.column_settings.favourite": "Favorieten:",
|
"notifications.column_settings.favourite": "Favorieten:",
|
||||||
"notifications.column_settings.filter_bar.advanced": "Alle categorieën tonen",
|
"notifications.column_settings.filter_bar.advanced": "Alle categorieën tonen",
|
||||||
|
@ -335,10 +335,10 @@
|
||||||
"notifications.column_settings.reblog": "Boosts:",
|
"notifications.column_settings.reblog": "Boosts:",
|
||||||
"notifications.column_settings.show": "In kolom tonen",
|
"notifications.column_settings.show": "In kolom tonen",
|
||||||
"notifications.column_settings.sound": "Geluid afspelen",
|
"notifications.column_settings.sound": "Geluid afspelen",
|
||||||
"notifications.column_settings.status": "Nieuwe toots:",
|
"notifications.column_settings.status": "Nieuwe berichten:",
|
||||||
"notifications.column_settings.unread_notifications.category": "Ongelezen meldingen",
|
"notifications.column_settings.unread_notifications.category": "Ongelezen meldingen",
|
||||||
"notifications.column_settings.unread_notifications.highlight": "Ongelezen meldingen markeren",
|
"notifications.column_settings.unread_notifications.highlight": "Ongelezen meldingen markeren",
|
||||||
"notifications.column_settings.update": "Edits:",
|
"notifications.column_settings.update": "Bewerkingen:",
|
||||||
"notifications.filter.all": "Alles",
|
"notifications.filter.all": "Alles",
|
||||||
"notifications.filter.boosts": "Boosts",
|
"notifications.filter.boosts": "Boosts",
|
||||||
"notifications.filter.favourites": "Favorieten",
|
"notifications.filter.favourites": "Favorieten",
|
||||||
|
@ -365,7 +365,7 @@
|
||||||
"poll.votes": "{votes, plural, one {# stem} other {# stemmen}}",
|
"poll.votes": "{votes, plural, one {# stem} other {# stemmen}}",
|
||||||
"poll_button.add_poll": "Poll toevoegen",
|
"poll_button.add_poll": "Poll toevoegen",
|
||||||
"poll_button.remove_poll": "Poll verwijderen",
|
"poll_button.remove_poll": "Poll verwijderen",
|
||||||
"privacy.change": "Zichtbaarheid van toot aanpassen",
|
"privacy.change": "Zichtbaarheid van bericht aanpassen",
|
||||||
"privacy.direct.long": "Alleen aan vermelde gebruikers tonen",
|
"privacy.direct.long": "Alleen aan vermelde gebruikers tonen",
|
||||||
"privacy.direct.short": "Direct",
|
"privacy.direct.short": "Direct",
|
||||||
"privacy.private.long": "Alleen aan volgers tonen",
|
"privacy.private.long": "Alleen aan volgers tonen",
|
||||||
|
@ -378,100 +378,100 @@
|
||||||
"regeneration_indicator.label": "Aan het laden…",
|
"regeneration_indicator.label": "Aan het laden…",
|
||||||
"regeneration_indicator.sublabel": "Jouw tijdlijn wordt aangemaakt!",
|
"regeneration_indicator.sublabel": "Jouw tijdlijn wordt aangemaakt!",
|
||||||
"relative_time.days": "{number}d",
|
"relative_time.days": "{number}d",
|
||||||
"relative_time.full.days": "{number, plural, one {# day} other {# days}} ago",
|
"relative_time.full.days": "{number, plural, one {# dag} other {# dagen}} geleden",
|
||||||
"relative_time.full.hours": "{number, plural, one {# hour} other {# hours}} ago",
|
"relative_time.full.hours": "{number, plural, one {# uur} other {# uur}} geleden",
|
||||||
"relative_time.full.just_now": "just now",
|
"relative_time.full.just_now": "zojuist",
|
||||||
"relative_time.full.minutes": "{number, plural, one {# minute} other {# minutes}} ago",
|
"relative_time.full.minutes": "{number, plural, one {# minuut} other {# minuten}} geleden",
|
||||||
"relative_time.full.seconds": "{number, plural, one {# second} other {# seconds}} ago",
|
"relative_time.full.seconds": "{number, plural, one {# seconde} other {# seconden}} geleden",
|
||||||
"relative_time.hours": "{number}u",
|
"relative_time.hours": "{number}u",
|
||||||
"relative_time.just_now": "nu",
|
"relative_time.just_now": "nu",
|
||||||
"relative_time.minutes": "{number}m",
|
"relative_time.minutes": "{number}m",
|
||||||
"relative_time.seconds": "{number}s",
|
"relative_time.seconds": "{number}s",
|
||||||
"relative_time.today": "vandaag",
|
"relative_time.today": "vandaag",
|
||||||
"reply_indicator.cancel": "Annuleren",
|
"reply_indicator.cancel": "Annuleren",
|
||||||
"report.block": "Block",
|
"report.block": "Blokkeren",
|
||||||
"report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
|
"report.block_explanation": "Je kunt diens berichten niet zien. Je kunt door diegene niet gevolgd worden en jouw berichten zijn onzichtbaar. Diegene kan zien dat die door jou is geblokkeerd.",
|
||||||
"report.categories.other": "Other",
|
"report.categories.other": "Overig",
|
||||||
"report.categories.spam": "Spam",
|
"report.categories.spam": "Spam",
|
||||||
"report.categories.violation": "Content violates one or more server rules",
|
"report.categories.violation": "De inhoud overtreedt een of meerdere serverregels",
|
||||||
"report.category.subtitle": "Choose the best match",
|
"report.category.subtitle": "Kies wat het meeste overeenkomt",
|
||||||
"report.category.title": "Tell us what's going on with this {type}",
|
"report.category.title": "Vertel ons wat er met dit {type} aan de hand is",
|
||||||
"report.category.title_account": "profile",
|
"report.category.title_account": "profiel",
|
||||||
"report.category.title_status": "post",
|
"report.category.title_status": "bericht",
|
||||||
"report.close": "Done",
|
"report.close": "Klaar",
|
||||||
"report.comment.title": "Is there anything else you think we should know?",
|
"report.comment.title": "Zijn er nog andere dingen waarvan je denkt dat wij dat moeten weten?",
|
||||||
"report.forward": "Naar {target} doorsturen",
|
"report.forward": "Naar {target} doorsturen",
|
||||||
"report.forward_hint": "Het account bevindt zich op een andere server. Wil je daar eveneens een geanonimiseerde kopie van deze rapportage naar toe sturen?",
|
"report.forward_hint": "Het account bevindt zich op een andere server. Wil je daar eveneens een geanonimiseerde kopie van deze rapportage naar toe sturen?",
|
||||||
"report.mute": "Mute",
|
"report.mute": "Negeren",
|
||||||
"report.mute_explanation": "You will not see their posts. They can still follow you and see your posts and will not know that they are muted.",
|
"report.mute_explanation": "Je kunt diens berichten niet zien. Je kunt nog wel gevolgd worden en jouw berichten zijn nog zichtbaar, maar diegene kan niet zien dat die wordt genegeerd.",
|
||||||
"report.next": "Next",
|
"report.next": "Volgende",
|
||||||
"report.placeholder": "Extra opmerkingen",
|
"report.placeholder": "Extra opmerkingen",
|
||||||
"report.reasons.dislike": "I don't like it",
|
"report.reasons.dislike": "Ik vind het niet leuk",
|
||||||
"report.reasons.dislike_description": "It is not something you want to see",
|
"report.reasons.dislike_description": "Het is iets wat je niet wilt zien",
|
||||||
"report.reasons.other": "It's something else",
|
"report.reasons.other": "Het is iets anders",
|
||||||
"report.reasons.other_description": "The issue does not fit into other categories",
|
"report.reasons.other_description": "Het probleem past niet in een andere categorie",
|
||||||
"report.reasons.spam": "It's spam",
|
"report.reasons.spam": "Het is spam",
|
||||||
"report.reasons.spam_description": "Malicious links, fake engagement, or repetitive replies",
|
"report.reasons.spam_description": "Schadelijke links, reclame, misleiding of herhalende antwoorden",
|
||||||
"report.reasons.violation": "It violates server rules",
|
"report.reasons.violation": "Het schendt de serverregels",
|
||||||
"report.reasons.violation_description": "You are aware that it breaks specific rules",
|
"report.reasons.violation_description": "Je weet dat het specifieke regels schendt",
|
||||||
"report.rules.subtitle": "Select all that apply",
|
"report.rules.subtitle": "Selecteer wat van toepassing is",
|
||||||
"report.rules.title": "Which rules are being violated?",
|
"report.rules.title": "Welke regels worden geschonden?",
|
||||||
"report.statuses.subtitle": "Select all that apply",
|
"report.statuses.subtitle": "Selecteer wat van toepassing is",
|
||||||
"report.statuses.title": "Are there any posts that back up this report?",
|
"report.statuses.title": "Zijn er berichten die deze rapportage ondersteunen?",
|
||||||
"report.submit": "Verzenden",
|
"report.submit": "Verzenden",
|
||||||
"report.target": "{target} rapporteren",
|
"report.target": "{target} rapporteren",
|
||||||
"report.thanks.take_action": "Here are your options for controlling what you see on Mastodon:",
|
"report.thanks.take_action": "Hier zijn jouw opties waarmee je kunt bepalen wat je in Mastodon wilt zien:",
|
||||||
"report.thanks.take_action_actionable": "While we review this, you can take action against @{name}:",
|
"report.thanks.take_action_actionable": "Terwijl wij jouw rapportage beroordelen, kun je deze acties ondernemen tegen @{name}:",
|
||||||
"report.thanks.title": "Don't want to see this?",
|
"report.thanks.title": "Wil je dit niet zien?",
|
||||||
"report.thanks.title_actionable": "Thanks for reporting, we'll look into this.",
|
"report.thanks.title_actionable": "Dank je voor het rapporteren. Wij gaan er naar kijken.",
|
||||||
"report.unfollow": "Unfollow @{name}",
|
"report.unfollow": "@{name} ontvolgen",
|
||||||
"report.unfollow_explanation": "You are following this account. To not see their posts in your home feed anymore, unfollow them.",
|
"report.unfollow_explanation": "Je volgt dit account. Om diens berichten niet meer op jouw starttijdlijn te zien, kun je diegene ontvolgen.",
|
||||||
"search.placeholder": "Zoeken",
|
"search.placeholder": "Zoeken",
|
||||||
"search_popout.search_format": "Geavanceerd zoeken",
|
"search_popout.search_format": "Geavanceerd zoeken",
|
||||||
"search_popout.tips.full_text": "Gebruik gewone tekst om te zoeken in jouw toots, gebooste toots, favorieten en in toots waarin je bent vermeldt, en tevens naar gebruikersnamen, weergavenamen en hashtags.",
|
"search_popout.tips.full_text": "Gebruik gewone tekst om te zoeken in jouw berichten, gebooste berichten, favorieten en in berichten waarin je bent vermeldt, en tevens naar gebruikersnamen, weergavenamen en hashtags.",
|
||||||
"search_popout.tips.hashtag": "hashtag",
|
"search_popout.tips.hashtag": "hashtag",
|
||||||
"search_popout.tips.status": "toot",
|
"search_popout.tips.status": "bericht",
|
||||||
"search_popout.tips.text": "Gebruik gewone tekst om te zoeken op weergavenamen, gebruikersnamen en hashtags",
|
"search_popout.tips.text": "Gebruik gewone tekst om te zoeken op weergavenamen, gebruikersnamen en hashtags",
|
||||||
"search_popout.tips.user": "gebruiker",
|
"search_popout.tips.user": "gebruiker",
|
||||||
"search_results.accounts": "Gebruikers",
|
"search_results.accounts": "Gebruikers",
|
||||||
"search_results.all": "All",
|
"search_results.all": "Alles",
|
||||||
"search_results.hashtags": "Hashtags",
|
"search_results.hashtags": "Hashtags",
|
||||||
"search_results.nothing_found": "Could not find anything for these search terms",
|
"search_results.nothing_found": "Deze zoektermen leveren geen resultaat op",
|
||||||
"search_results.statuses": "Toots",
|
"search_results.statuses": "Berichten",
|
||||||
"search_results.statuses_fts_disabled": "Het zoeken in toots is op deze Mastodon-server niet ingeschakeld.",
|
"search_results.statuses_fts_disabled": "Het zoeken in berichten is op deze Mastodon-server niet ingeschakeld.",
|
||||||
"search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}",
|
"search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}",
|
||||||
"status.admin_account": "Moderatie-omgeving van @{name} openen",
|
"status.admin_account": "Moderatie-omgeving van @{name} openen",
|
||||||
"status.admin_status": "Deze toot in de moderatie-omgeving openen",
|
"status.admin_status": "Dit bericht in de moderatie-omgeving openen",
|
||||||
"status.block": "@{name} blokkeren",
|
"status.block": "@{name} blokkeren",
|
||||||
"status.bookmark": "Bladwijzer toevoegen",
|
"status.bookmark": "Bladwijzer toevoegen",
|
||||||
"status.cancel_reblog_private": "Niet langer boosten",
|
"status.cancel_reblog_private": "Niet langer boosten",
|
||||||
"status.cannot_reblog": "Deze toot kan niet geboost worden",
|
"status.cannot_reblog": "Dit bericht kan niet geboost worden",
|
||||||
"status.copy": "Link naar toot kopiëren",
|
"status.copy": "Link naar bericht kopiëren",
|
||||||
"status.delete": "Verwijderen",
|
"status.delete": "Verwijderen",
|
||||||
"status.detailed_status": "Uitgebreide gespreksweergave",
|
"status.detailed_status": "Uitgebreide gespreksweergave",
|
||||||
"status.direct": "@{name} een direct bericht sturen",
|
"status.direct": "@{name} een direct bericht sturen",
|
||||||
"status.edit": "Edit",
|
"status.edit": "Bewerken",
|
||||||
"status.edited": "Edited {date}",
|
"status.edited": "Bewerkt op {date}",
|
||||||
"status.edited_x_times": "Edited {count, plural, one {{count} time} other {{count} times}}",
|
"status.edited_x_times": "{count, plural, one {{count} keer} other {{count} keer}} bewerkt",
|
||||||
"status.embed": "Insluiten",
|
"status.embed": "Insluiten",
|
||||||
"status.favourite": "Favoriet",
|
"status.favourite": "Favoriet",
|
||||||
"status.filtered": "Gefilterd",
|
"status.filtered": "Gefilterd",
|
||||||
"status.history.created": "{name} created {date}",
|
"status.history.created": "{name} plaatste dit {date}",
|
||||||
"status.history.edited": "{name} edited {date}",
|
"status.history.edited": "{name} bewerkte dit {date}",
|
||||||
"status.load_more": "Meer laden",
|
"status.load_more": "Meer laden",
|
||||||
"status.media_hidden": "Media verborgen",
|
"status.media_hidden": "Media verborgen",
|
||||||
"status.mention": "@{name} vermelden",
|
"status.mention": "@{name} vermelden",
|
||||||
"status.more": "Meer",
|
"status.more": "Meer",
|
||||||
"status.mute": "@{name} negeren",
|
"status.mute": "@{name} negeren",
|
||||||
"status.mute_conversation": "Negeer gesprek",
|
"status.mute_conversation": "Negeer gesprek",
|
||||||
"status.open": "Volledige toot tonen",
|
"status.open": "Volledig bericht tonen",
|
||||||
"status.pin": "Aan profielpagina vastmaken",
|
"status.pin": "Aan profielpagina vastmaken",
|
||||||
"status.pinned": "Vastgemaakte toot",
|
"status.pinned": "Vastgemaakt bericht",
|
||||||
"status.read_more": "Meer lezen",
|
"status.read_more": "Meer lezen",
|
||||||
"status.reblog": "Boosten",
|
"status.reblog": "Boosten",
|
||||||
"status.reblog_private": "Boost naar oorspronkelijke ontvangers",
|
"status.reblog_private": "Boost naar oorspronkelijke ontvangers",
|
||||||
"status.reblogged_by": "{name} boostte",
|
"status.reblogged_by": "{name} boostte",
|
||||||
"status.reblogs.empty": "Niemand heeft deze toot nog geboost. Wanneer iemand dit doet, valt dat hier te zien.",
|
"status.reblogs.empty": "Niemand heeft dit bericht nog geboost. Wanneer iemand dit doet, valt dat hier te zien.",
|
||||||
"status.redraft": "Verwijderen en herschrijven",
|
"status.redraft": "Verwijderen en herschrijven",
|
||||||
"status.remove_bookmark": "Bladwijzer verwijderen",
|
"status.remove_bookmark": "Bladwijzer verwijderen",
|
||||||
"status.reply": "Reageren",
|
"status.reply": "Reageren",
|
||||||
|
@ -502,7 +502,7 @@
|
||||||
"timeline_hint.remote_resource_not_displayed": "{resource} van andere servers worden niet getoond.",
|
"timeline_hint.remote_resource_not_displayed": "{resource} van andere servers worden niet getoond.",
|
||||||
"timeline_hint.resources.followers": "Volgers",
|
"timeline_hint.resources.followers": "Volgers",
|
||||||
"timeline_hint.resources.follows": "Volgend",
|
"timeline_hint.resources.follows": "Volgend",
|
||||||
"timeline_hint.resources.statuses": "Oudere toots",
|
"timeline_hint.resources.statuses": "Oudere berichten",
|
||||||
"trends.counter_by_accounts": "{count, plural, one {{counter} persoon} other {{counter} personen}} zijn aan het praten",
|
"trends.counter_by_accounts": "{count, plural, one {{counter} persoon} other {{counter} personen}} zijn aan het praten",
|
||||||
"trends.trending_now": "Huidige trends",
|
"trends.trending_now": "Huidige trends",
|
||||||
"ui.beforeunload": "Je concept gaat verloren wanneer je Mastodon verlaat.",
|
"ui.beforeunload": "Je concept gaat verloren wanneer je Mastodon verlaat.",
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::Activity::Create < ActivityPub::Activity
|
class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
def perform
|
def perform
|
||||||
dereference_object!
|
dereference_object!
|
||||||
|
|
||||||
|
@ -367,7 +369,7 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
|
||||||
end
|
end
|
||||||
|
|
||||||
def converted_text
|
def converted_text
|
||||||
Formatter.instance.linkify([@status_parser.title.presence, @status_parser.spoiler_text.presence, @status_parser.url || @status_parser.uri].compact.join("\n\n"))
|
linkify([@status_parser.title.presence, @status_parser.spoiler_text.presence, @status_parser.url || @status_parser.uri].compact.join("\n\n"))
|
||||||
end
|
end
|
||||||
|
|
||||||
def unsupported_media_type?(mime_type)
|
def unsupported_media_type?(mime_type)
|
||||||
|
|
|
@ -27,7 +27,9 @@ class ActivityPub::Parser::MediaAttachmentParser
|
||||||
end
|
end
|
||||||
|
|
||||||
def description
|
def description
|
||||||
@json['summary'].presence || @json['name'].presence
|
str = @json['summary'].presence || @json['name'].presence
|
||||||
|
str = str.strip[0...MediaAttachment::MAX_DESCRIPTION_LENGTH] if str.present?
|
||||||
|
str
|
||||||
end
|
end
|
||||||
|
|
||||||
def focus
|
def focus
|
||||||
|
|
|
@ -5,6 +5,7 @@ class Admin::SystemCheck
|
||||||
Admin::SystemCheck::DatabaseSchemaCheck,
|
Admin::SystemCheck::DatabaseSchemaCheck,
|
||||||
Admin::SystemCheck::SidekiqProcessCheck,
|
Admin::SystemCheck::SidekiqProcessCheck,
|
||||||
Admin::SystemCheck::RulesCheck,
|
Admin::SystemCheck::RulesCheck,
|
||||||
|
Admin::SystemCheck::ElasticsearchCheck,
|
||||||
].freeze
|
].freeze
|
||||||
|
|
||||||
def self.perform
|
def self.perform
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
|
||||||
|
def pass?
|
||||||
|
return true unless Chewy.enabled?
|
||||||
|
|
||||||
|
running_version.present? && compatible_version?
|
||||||
|
end
|
||||||
|
|
||||||
|
def message
|
||||||
|
if running_version.present?
|
||||||
|
Admin::SystemCheck::Message.new(:elasticsearch_version_check, I18n.t('admin.system_checks.elasticsearch_version_check.version_comparison', running_version: running_version, required_version: required_version))
|
||||||
|
else
|
||||||
|
Admin::SystemCheck::Message.new(:elasticsearch_running_check)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def running_version
|
||||||
|
@running_version ||= begin
|
||||||
|
Chewy.client.info['version']['number']
|
||||||
|
rescue Faraday::ConnectionFailed
|
||||||
|
nil
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def required_version
|
||||||
|
'7.x'
|
||||||
|
end
|
||||||
|
|
||||||
|
def compatible_version?
|
||||||
|
Gem::Version.new(running_version) >= Gem::Version.new(required_version)
|
||||||
|
end
|
||||||
|
|
||||||
|
def missing_queues
|
||||||
|
@missing_queues ||= Sidekiq::ProcessSet.new.reduce(SIDEKIQ_QUEUES) { |queues, process| queues - process['queues'] }
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,98 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class EmojiFormatter
|
||||||
|
include RoutingHelper
|
||||||
|
|
||||||
|
DISALLOWED_BOUNDING_REGEX = /[[:alnum:]:]/.freeze
|
||||||
|
|
||||||
|
attr_reader :html, :custom_emojis, :options
|
||||||
|
|
||||||
|
# @param [ActiveSupport::SafeBuffer] html
|
||||||
|
# @param [Array<CustomEmoji>] custom_emojis
|
||||||
|
# @param [Hash] options
|
||||||
|
# @option options [Boolean] :animate
|
||||||
|
def initialize(html, custom_emojis, options = {})
|
||||||
|
raise ArgumentError unless html.html_safe?
|
||||||
|
|
||||||
|
@html = html
|
||||||
|
@custom_emojis = custom_emojis
|
||||||
|
@options = options
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
return html if custom_emojis.empty? || html.blank?
|
||||||
|
|
||||||
|
i = -1
|
||||||
|
tag_open_index = nil
|
||||||
|
inside_shortname = false
|
||||||
|
shortname_start_index = -1
|
||||||
|
invisible_depth = 0
|
||||||
|
last_index = 0
|
||||||
|
result = ''.dup
|
||||||
|
|
||||||
|
while i + 1 < html.size
|
||||||
|
i += 1
|
||||||
|
|
||||||
|
if invisible_depth.zero? && inside_shortname && html[i] == ':'
|
||||||
|
inside_shortname = false
|
||||||
|
shortcode = html[shortname_start_index + 1..i - 1]
|
||||||
|
char_after = html[i + 1]
|
||||||
|
|
||||||
|
next unless (char_after.nil? || !DISALLOWED_BOUNDING_REGEX.match?(char_after)) && (emoji = emoji_map[shortcode])
|
||||||
|
|
||||||
|
result << html[last_index..shortname_start_index - 1] if shortname_start_index.positive?
|
||||||
|
result << image_for_emoji(shortcode, emoji)
|
||||||
|
last_index = i + 1
|
||||||
|
elsif tag_open_index && html[i] == '>'
|
||||||
|
tag = html[tag_open_index..i]
|
||||||
|
tag_open_index = nil
|
||||||
|
|
||||||
|
if invisible_depth.positive?
|
||||||
|
invisible_depth += count_tag_nesting(tag)
|
||||||
|
elsif tag == '<span class="invisible">'
|
||||||
|
invisible_depth = 1
|
||||||
|
end
|
||||||
|
elsif html[i] == '<'
|
||||||
|
tag_open_index = i
|
||||||
|
inside_shortname = false
|
||||||
|
elsif !tag_open_index && html[i] == ':' && (i.zero? || !DISALLOWED_BOUNDING_REGEX.match?(html[i - 1]))
|
||||||
|
inside_shortname = true
|
||||||
|
shortname_start_index = i
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
result << html[last_index..-1]
|
||||||
|
|
||||||
|
result.html_safe # rubocop:disable Rails/OutputSafety
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def emoji_map
|
||||||
|
@emoji_map ||= custom_emojis.each_with_object({}) { |e, h| h[e.shortcode] = [full_asset_url(e.image.url), full_asset_url(e.image.url(:static))] }
|
||||||
|
end
|
||||||
|
|
||||||
|
def count_tag_nesting(tag)
|
||||||
|
if tag[1] == '/'
|
||||||
|
-1
|
||||||
|
elsif tag[-2] == '/'
|
||||||
|
0
|
||||||
|
else
|
||||||
|
1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def image_for_emoji(shortcode, emoji)
|
||||||
|
original_url, static_url = emoji
|
||||||
|
|
||||||
|
if animate?
|
||||||
|
image_tag(original_url, draggable: false, class: 'emojione', alt: ":#{shortcode}:", title: ":#{shortcode}:")
|
||||||
|
else
|
||||||
|
image_tag(original_url, draggable: false, class: 'emojione custom-emoji', alt: ":#{shortcode}:", title: ":#{shortcode}:", data: { original: original_url, static: static_url })
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def animate?
|
||||||
|
@options[:animate]
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,18 +5,34 @@ module Extractor
|
||||||
|
|
||||||
module_function
|
module_function
|
||||||
|
|
||||||
# :yields: username, list_slug, start, end
|
def extract_entities_with_indices(text, options = {}, &block)
|
||||||
|
entities = begin
|
||||||
|
extract_urls_with_indices(text, options) +
|
||||||
|
extract_hashtags_with_indices(text, check_url_overlap: false) +
|
||||||
|
extract_mentions_or_lists_with_indices(text) +
|
||||||
|
extract_extra_uris_with_indices(text)
|
||||||
|
end
|
||||||
|
|
||||||
|
return [] if entities.empty?
|
||||||
|
|
||||||
|
entities = remove_overlapping_entities(entities)
|
||||||
|
entities.each(&block) if block_given?
|
||||||
|
entities
|
||||||
|
end
|
||||||
|
|
||||||
def extract_mentions_or_lists_with_indices(text)
|
def extract_mentions_or_lists_with_indices(text)
|
||||||
return [] unless Twitter::TwitterText::Regex[:at_signs].match?(text)
|
return [] unless text && Twitter::TwitterText::Regex[:at_signs].match?(text)
|
||||||
|
|
||||||
possible_entries = []
|
possible_entries = []
|
||||||
|
|
||||||
text.to_s.scan(Account::MENTION_RE) do |screen_name, _|
|
text.scan(Account::MENTION_RE) do |screen_name, _|
|
||||||
match_data = $LAST_MATCH_INFO
|
match_data = $LAST_MATCH_INFO
|
||||||
after = $'
|
after = $'
|
||||||
|
|
||||||
unless Twitter::TwitterText::Regex[:end_mention_match].match?(after)
|
unless Twitter::TwitterText::Regex[:end_mention_match].match?(after)
|
||||||
start_position = match_data.char_begin(1) - 1
|
start_position = match_data.char_begin(1) - 1
|
||||||
end_position = match_data.char_end(1)
|
end_position = match_data.char_end(1)
|
||||||
|
|
||||||
possible_entries << {
|
possible_entries << {
|
||||||
screen_name: screen_name,
|
screen_name: screen_name,
|
||||||
indices: [start_position, end_position],
|
indices: [start_position, end_position],
|
||||||
|
@ -29,18 +45,21 @@ module Extractor
|
||||||
yield mention[:screen_name], mention[:indices].first, mention[:indices].last
|
yield mention[:screen_name], mention[:indices].first, mention[:indices].last
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
possible_entries
|
possible_entries
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_hashtags_with_indices(text, **)
|
def extract_hashtags_with_indices(text, _options = {})
|
||||||
return [] unless /#/.match?(text)
|
return [] unless text&.index('#')
|
||||||
|
|
||||||
|
possible_entries = []
|
||||||
|
|
||||||
tags = []
|
|
||||||
text.scan(Tag::HASHTAG_RE) do |hash_text, _|
|
text.scan(Tag::HASHTAG_RE) do |hash_text, _|
|
||||||
match_data = $LAST_MATCH_INFO
|
match_data = $LAST_MATCH_INFO
|
||||||
start_position = match_data.char_begin(1) - 1
|
start_position = match_data.char_begin(1) - 1
|
||||||
end_position = match_data.char_end(1)
|
end_position = match_data.char_end(1)
|
||||||
after = $'
|
after = $'
|
||||||
|
|
||||||
if %r{\A://}.match?(after)
|
if %r{\A://}.match?(after)
|
||||||
hash_text.match(/(.+)(https?\Z)/) do |matched|
|
hash_text.match(/(.+)(https?\Z)/) do |matched|
|
||||||
hash_text = matched[1]
|
hash_text = matched[1]
|
||||||
|
@ -48,17 +67,48 @@ module Extractor
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
tags << {
|
possible_entries << {
|
||||||
hashtag: hash_text,
|
hashtag: hash_text,
|
||||||
indices: [start_position, end_position],
|
indices: [start_position, end_position],
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
tags.each { |tag| yield tag[:hashtag], tag[:indices].first, tag[:indices].last } if block_given?
|
if block_given?
|
||||||
tags
|
possible_entries.each do |tag|
|
||||||
|
yield tag[:hashtag], tag[:indices].first, tag[:indices].last
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
possible_entries
|
||||||
end
|
end
|
||||||
|
|
||||||
def extract_cashtags_with_indices(_text)
|
def extract_cashtags_with_indices(_text)
|
||||||
[] # always returns empty array
|
[]
|
||||||
|
end
|
||||||
|
|
||||||
|
def extract_extra_uris_with_indices(text)
|
||||||
|
return [] unless text&.index(':')
|
||||||
|
|
||||||
|
possible_entries = []
|
||||||
|
|
||||||
|
text.scan(Twitter::TwitterText::Regex[:valid_extended_uri]) do
|
||||||
|
valid_uri_match_data = $LAST_MATCH_INFO
|
||||||
|
|
||||||
|
start_position = valid_uri_match_data.char_begin(3)
|
||||||
|
end_position = valid_uri_match_data.char_end(3)
|
||||||
|
|
||||||
|
possible_entries << {
|
||||||
|
url: valid_uri_match_data[3],
|
||||||
|
indices: [start_position, end_position],
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
if block_given?
|
||||||
|
possible_entries.each do |url|
|
||||||
|
yield url[:url], url[:indices].first, url[:indices].last
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
possible_entries
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,6 +5,7 @@ require 'singleton'
|
||||||
class FeedManager
|
class FeedManager
|
||||||
include Singleton
|
include Singleton
|
||||||
include Redisable
|
include Redisable
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
# Maximum number of items stored in a single feed
|
# Maximum number of items stored in a single feed
|
||||||
MAX_ITEMS = 400
|
MAX_ITEMS = 400
|
||||||
|
@ -503,7 +504,7 @@ class FeedManager
|
||||||
status = status.reblog if status.reblog?
|
status = status.reblog if status.reblog?
|
||||||
|
|
||||||
combined_text = [
|
combined_text = [
|
||||||
Formatter.instance.plaintext(status),
|
extract_plain_text(status.text, status.local?),
|
||||||
status.spoiler_text,
|
status.spoiler_text,
|
||||||
status.preloadable_poll ? status.preloadable_poll.options.join("\n\n") : nil,
|
status.preloadable_poll ? status.preloadable_poll.options.join("\n\n") : nil,
|
||||||
status.ordered_media_attachments.map(&:description).join("\n\n"),
|
status.ordered_media_attachments.map(&:description).join("\n\n"),
|
||||||
|
|
|
@ -1,382 +0,0 @@
|
||||||
# frozen_string_literal: true
|
|
||||||
|
|
||||||
require 'singleton'
|
|
||||||
|
|
||||||
class HTMLRenderer < Redcarpet::Render::HTML
|
|
||||||
def block_code(code, language)
|
|
||||||
"<pre><code>#{encode(code).gsub("\n", "<br/>")}</code></pre>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def autolink(link, link_type)
|
|
||||||
return link if link_type == :email
|
|
||||||
Formatter.instance.link_url(link)
|
|
||||||
rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
|
|
||||||
encode(link)
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def html_entities
|
|
||||||
@html_entities ||= HTMLEntities.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def encode(html)
|
|
||||||
html_entities.encode(html)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
class Formatter
|
|
||||||
include Singleton
|
|
||||||
include RoutingHelper
|
|
||||||
|
|
||||||
include ActionView::Helpers::TextHelper
|
|
||||||
|
|
||||||
def format(status, **options)
|
|
||||||
if status.respond_to?(:reblog?) && status.reblog?
|
|
||||||
prepend_reblog = status.reblog.account.acct
|
|
||||||
status = status.proper
|
|
||||||
else
|
|
||||||
prepend_reblog = false
|
|
||||||
end
|
|
||||||
|
|
||||||
raw_content = status.text
|
|
||||||
|
|
||||||
if options[:inline_poll_options] && status.preloadable_poll
|
|
||||||
raw_content = raw_content + "\n\n" + status.preloadable_poll.options.map { |title| "[ ] #{title}" }.join("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
return '' if raw_content.blank?
|
|
||||||
|
|
||||||
unless status.local?
|
|
||||||
html = reformat(raw_content)
|
|
||||||
html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
|
|
||||||
return html.html_safe # rubocop:disable Rails/OutputSafety
|
|
||||||
end
|
|
||||||
|
|
||||||
linkable_accounts = status.respond_to?(:active_mentions) ? status.active_mentions.map(&:account) : []
|
|
||||||
linkable_accounts << status.account
|
|
||||||
|
|
||||||
html = raw_content
|
|
||||||
html = "RT @#{prepend_reblog} #{html}" if prepend_reblog
|
|
||||||
html = format_markdown(html) if status.content_type == 'text/markdown'
|
|
||||||
html = encode_and_link_urls(html, linkable_accounts, keep_html: %w(text/markdown text/html).include?(status.content_type))
|
|
||||||
html = reformat(html, true) if %w(text/markdown text/html).include?(status.content_type)
|
|
||||||
html = encode_custom_emojis(html, status.emojis, options[:autoplay]) if options[:custom_emojify]
|
|
||||||
|
|
||||||
unless %w(text/markdown text/html).include?(status.content_type)
|
|
||||||
html = simple_format(html, {}, sanitize: false)
|
|
||||||
html = html.delete("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
html.html_safe # rubocop:disable Rails/OutputSafety
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_markdown(html)
|
|
||||||
html = markdown_formatter.render(html)
|
|
||||||
html.delete("\r").delete("\n")
|
|
||||||
end
|
|
||||||
|
|
||||||
def reformat(html, outgoing = false)
|
|
||||||
sanitize(html, Sanitize::Config::MASTODON_STRICT.merge(outgoing: outgoing))
|
|
||||||
rescue ArgumentError
|
|
||||||
''
|
|
||||||
end
|
|
||||||
|
|
||||||
def plaintext(status)
|
|
||||||
return status.text if status.local?
|
|
||||||
|
|
||||||
text = status.text.gsub(/(<br \/>|<br>|<\/p>)+/) { |match| "#{match}\n" }
|
|
||||||
strip_tags(text)
|
|
||||||
end
|
|
||||||
|
|
||||||
def simplified_format(account, **options)
|
|
||||||
return '' if account.note.blank?
|
|
||||||
|
|
||||||
html = account.local? ? linkify(account.note) : reformat(account.note)
|
|
||||||
html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify]
|
|
||||||
html.html_safe # rubocop:disable Rails/OutputSafety
|
|
||||||
end
|
|
||||||
|
|
||||||
def sanitize(html, config)
|
|
||||||
Sanitize.fragment(html, config)
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_spoiler(status, **options)
|
|
||||||
html = encode(status.spoiler_text)
|
|
||||||
html = encode_custom_emojis(html, status.emojis, options[:autoplay])
|
|
||||||
html.html_safe # rubocop:disable Rails/OutputSafety
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_poll_option(status, option, **options)
|
|
||||||
html = encode(option.title)
|
|
||||||
html = encode_custom_emojis(html, status.emojis, options[:autoplay])
|
|
||||||
html.html_safe # rubocop:disable Rails/OutputSafety
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_display_name(account, **options)
|
|
||||||
html = encode(account.display_name.presence || account.username)
|
|
||||||
html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify]
|
|
||||||
html.html_safe # rubocop:disable Rails/OutputSafety
|
|
||||||
end
|
|
||||||
|
|
||||||
def format_field(account, str, **options)
|
|
||||||
html = account.local? ? encode_and_link_urls(str, me: true, with_domain: true) : reformat(str)
|
|
||||||
html = encode_custom_emojis(html, account.emojis, options[:autoplay]) if options[:custom_emojify]
|
|
||||||
html.html_safe # rubocop:disable Rails/OutputSafety
|
|
||||||
end
|
|
||||||
|
|
||||||
def linkify(text)
|
|
||||||
html = encode_and_link_urls(text)
|
|
||||||
html = simple_format(html, {}, sanitize: false)
|
|
||||||
html = html.delete("\n")
|
|
||||||
|
|
||||||
html.html_safe # rubocop:disable Rails/OutputSafety
|
|
||||||
end
|
|
||||||
|
|
||||||
def link_url(url)
|
|
||||||
"<a href=\"#{encode(url)}\" target=\"blank\" rel=\"nofollow noopener noreferrer\">#{link_html(url)}</a>"
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def markdown_formatter
|
|
||||||
extensions = {
|
|
||||||
autolink: true,
|
|
||||||
no_intra_emphasis: true,
|
|
||||||
fenced_code_blocks: true,
|
|
||||||
disable_indented_code_blocks: true,
|
|
||||||
strikethrough: true,
|
|
||||||
lax_spacing: true,
|
|
||||||
space_after_headers: true,
|
|
||||||
superscript: true,
|
|
||||||
underline: true,
|
|
||||||
highlight: true,
|
|
||||||
footnotes: false,
|
|
||||||
}
|
|
||||||
|
|
||||||
renderer = HTMLRenderer.new({
|
|
||||||
filter_html: false,
|
|
||||||
escape_html: false,
|
|
||||||
no_images: true,
|
|
||||||
no_styles: true,
|
|
||||||
safe_links_only: true,
|
|
||||||
hard_wrap: true,
|
|
||||||
link_attributes: { target: '_blank', rel: 'nofollow noopener' },
|
|
||||||
})
|
|
||||||
|
|
||||||
Redcarpet::Markdown.new(renderer, extensions)
|
|
||||||
end
|
|
||||||
|
|
||||||
def html_entities
|
|
||||||
@html_entities ||= HTMLEntities.new
|
|
||||||
end
|
|
||||||
|
|
||||||
def encode(html)
|
|
||||||
html_entities.encode(html)
|
|
||||||
end
|
|
||||||
|
|
||||||
def encode_and_link_urls(html, accounts = nil, options = {})
|
|
||||||
if accounts.is_a?(Hash)
|
|
||||||
options = accounts
|
|
||||||
accounts = nil
|
|
||||||
end
|
|
||||||
|
|
||||||
entities = options[:keep_html] ? html_friendly_extractor(html) : utf8_friendly_extractor(html, extract_url_without_protocol: false)
|
|
||||||
|
|
||||||
rewrite(html.dup, entities, options[:keep_html]) do |entity|
|
|
||||||
if entity[:url]
|
|
||||||
link_to_url(entity, options)
|
|
||||||
elsif entity[:hashtag]
|
|
||||||
link_to_hashtag(entity)
|
|
||||||
elsif entity[:screen_name]
|
|
||||||
link_to_mention(entity, accounts, options)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def count_tag_nesting(tag)
|
|
||||||
if tag[1] == '/' then -1
|
|
||||||
elsif tag[-2] == '/' then 0
|
|
||||||
else 1
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# rubocop:disable Metrics/BlockNesting
|
|
||||||
def encode_custom_emojis(html, emojis, animate = false)
|
|
||||||
return html if emojis.empty?
|
|
||||||
|
|
||||||
emoji_map = emojis.each_with_object({}) { |e, h| h[e.shortcode] = [full_asset_url(e.image.url), full_asset_url(e.image.url(:static))] }
|
|
||||||
|
|
||||||
i = -1
|
|
||||||
tag_open_index = nil
|
|
||||||
inside_shortname = false
|
|
||||||
shortname_start_index = -1
|
|
||||||
invisible_depth = 0
|
|
||||||
|
|
||||||
while i + 1 < html.size
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
if invisible_depth.zero? && inside_shortname && html[i] == ':'
|
|
||||||
shortcode = html[shortname_start_index + 1..i - 1]
|
|
||||||
emoji = emoji_map[shortcode]
|
|
||||||
|
|
||||||
if emoji
|
|
||||||
original_url, static_url = emoji
|
|
||||||
replacement = begin
|
|
||||||
if animate
|
|
||||||
image_tag(original_url, draggable: false, class: 'emojione', alt: ":#{shortcode}:", title: ":#{shortcode}:")
|
|
||||||
else
|
|
||||||
image_tag(original_url, draggable: false, class: 'emojione custom-emoji', alt: ":#{shortcode}:", title: ":#{shortcode}:", data: { original: original_url, static: static_url })
|
|
||||||
end
|
|
||||||
end
|
|
||||||
before_html = shortname_start_index.positive? ? html[0..shortname_start_index - 1] : ''
|
|
||||||
html = before_html + replacement + html[i + 1..-1]
|
|
||||||
i += replacement.size - (shortcode.size + 2) - 1
|
|
||||||
else
|
|
||||||
i -= 1
|
|
||||||
end
|
|
||||||
|
|
||||||
inside_shortname = false
|
|
||||||
elsif tag_open_index && html[i] == '>'
|
|
||||||
tag = html[tag_open_index..i]
|
|
||||||
tag_open_index = nil
|
|
||||||
if invisible_depth.positive?
|
|
||||||
invisible_depth += count_tag_nesting(tag)
|
|
||||||
elsif tag == '<span class="invisible">'
|
|
||||||
invisible_depth = 1
|
|
||||||
end
|
|
||||||
elsif html[i] == '<'
|
|
||||||
tag_open_index = i
|
|
||||||
inside_shortname = false
|
|
||||||
elsif !tag_open_index && html[i] == ':'
|
|
||||||
inside_shortname = true
|
|
||||||
shortname_start_index = i
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
html
|
|
||||||
end
|
|
||||||
# rubocop:enable Metrics/BlockNesting
|
|
||||||
|
|
||||||
def rewrite(text, entities, keep_html = false)
|
|
||||||
text = text.to_s
|
|
||||||
|
|
||||||
# Sort by start index
|
|
||||||
entities = entities.sort_by do |entity|
|
|
||||||
indices = entity.respond_to?(:indices) ? entity.indices : entity[:indices]
|
|
||||||
indices.first
|
|
||||||
end
|
|
||||||
|
|
||||||
result = []
|
|
||||||
|
|
||||||
last_index = entities.reduce(0) do |index, entity|
|
|
||||||
indices = entity.respond_to?(:indices) ? entity.indices : entity[:indices]
|
|
||||||
result << (keep_html ? text[index...indices.first] : encode(text[index...indices.first]))
|
|
||||||
result << yield(entity)
|
|
||||||
indices.last
|
|
||||||
end
|
|
||||||
|
|
||||||
result << (keep_html ? text[last_index..-1] : encode(text[last_index..-1]))
|
|
||||||
|
|
||||||
result.flatten.join
|
|
||||||
end
|
|
||||||
|
|
||||||
def utf8_friendly_extractor(text, options = {})
|
|
||||||
# Note: I couldn't obtain list_slug with @user/list-name format
|
|
||||||
# for mention so this requires additional check
|
|
||||||
special = Extractor.extract_urls_with_indices(text, options)
|
|
||||||
standard = Extractor.extract_entities_with_indices(text, options)
|
|
||||||
extra = Extractor.extract_extra_uris_with_indices(text, options)
|
|
||||||
|
|
||||||
Extractor.remove_overlapping_entities(special + standard + extra)
|
|
||||||
end
|
|
||||||
|
|
||||||
def html_friendly_extractor(html, options = {})
|
|
||||||
gaps = []
|
|
||||||
total_offset = 0
|
|
||||||
|
|
||||||
escaped = html.gsub(/<[^>]*>|&#[0-9]+;/) do |match|
|
|
||||||
total_offset += match.length - 1
|
|
||||||
end_offset = Regexp.last_match.end(0)
|
|
||||||
gaps << [end_offset - total_offset, total_offset]
|
|
||||||
"\u200b"
|
|
||||||
end
|
|
||||||
|
|
||||||
entities = Extractor.extract_hashtags_with_indices(escaped, :check_url_overlap => false) +
|
|
||||||
Extractor.extract_mentions_or_lists_with_indices(escaped)
|
|
||||||
Extractor.remove_overlapping_entities(entities).map do |extract|
|
|
||||||
pos = extract[:indices].first
|
|
||||||
offset_idx = gaps.rindex { |gap| gap.first <= pos }
|
|
||||||
offset = offset_idx.nil? ? 0 : gaps[offset_idx].last
|
|
||||||
next extract.merge(
|
|
||||||
:indices => [extract[:indices].first + offset, extract[:indices].last + offset]
|
|
||||||
)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def link_to_url(entity, options = {})
|
|
||||||
url = Addressable::URI.parse(entity[:url])
|
|
||||||
html_attrs = { target: '_blank', rel: 'nofollow noopener noreferrer' }
|
|
||||||
|
|
||||||
html_attrs[:rel] = "me #{html_attrs[:rel]}" if options[:me]
|
|
||||||
|
|
||||||
Twitter::TwitterText::Autolink.send(:link_to_text, entity, link_html(entity[:url]), url, html_attrs)
|
|
||||||
rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
|
|
||||||
encode(entity[:url])
|
|
||||||
end
|
|
||||||
|
|
||||||
def link_to_mention(entity, linkable_accounts, options = {})
|
|
||||||
acct = entity[:screen_name]
|
|
||||||
|
|
||||||
return link_to_account(acct, options) unless linkable_accounts
|
|
||||||
|
|
||||||
same_username_hits = 0
|
|
||||||
account = nil
|
|
||||||
username, domain = acct.split('@')
|
|
||||||
domain = nil if TagManager.instance.local_domain?(domain)
|
|
||||||
|
|
||||||
linkable_accounts.each do |item|
|
|
||||||
same_username = item.username.casecmp(username).zero?
|
|
||||||
same_domain = item.domain.nil? ? domain.nil? : item.domain.casecmp(domain)&.zero?
|
|
||||||
|
|
||||||
if same_username && !same_domain
|
|
||||||
same_username_hits += 1
|
|
||||||
elsif same_username && same_domain
|
|
||||||
account = item
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
account ? mention_html(account, with_domain: same_username_hits.positive? || options[:with_domain]) : "@#{encode(acct)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def link_to_account(acct, options = {})
|
|
||||||
username, domain = acct.split('@')
|
|
||||||
|
|
||||||
domain = nil if TagManager.instance.local_domain?(domain)
|
|
||||||
account = EntityCache.instance.mention(username, domain)
|
|
||||||
|
|
||||||
account ? mention_html(account, with_domain: options[:with_domain]) : "@#{encode(acct)}"
|
|
||||||
end
|
|
||||||
|
|
||||||
def link_to_hashtag(entity)
|
|
||||||
hashtag_html(entity[:hashtag])
|
|
||||||
end
|
|
||||||
|
|
||||||
def link_html(url)
|
|
||||||
url = Addressable::URI.parse(url).to_s
|
|
||||||
prefix = url.match(/\A(https?:\/\/(www\.)?|xmpp:)/).to_s
|
|
||||||
text = url[prefix.length, 30]
|
|
||||||
suffix = url[prefix.length + 30..-1]
|
|
||||||
cutoff = url[prefix.length..-1].length > 30
|
|
||||||
|
|
||||||
"<span class=\"invisible\">#{encode(prefix)}</span><span class=\"#{cutoff ? 'ellipsis' : ''}\">#{encode(text)}</span><span class=\"invisible\">#{encode(suffix)}</span>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def hashtag_html(tag)
|
|
||||||
"<a href=\"#{encode(tag_url(tag))}\" class=\"mention hashtag\" rel=\"tag\">#<span>#{encode(tag)}</span></a>"
|
|
||||||
end
|
|
||||||
|
|
||||||
def mention_html(account, with_domain: false)
|
|
||||||
"<span class=\"h-card\"><a href=\"#{encode(ActivityPub::TagManager.instance.url_for(account))}\" class=\"u-url mention\">@<span>#{encode(with_domain ? account.pretty_acct : account.username)}</span></a></span>"
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,38 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class HtmlAwareFormatter
|
||||||
|
attr_reader :text, :local, :options
|
||||||
|
|
||||||
|
alias local? local
|
||||||
|
|
||||||
|
# @param [String] text
|
||||||
|
# @param [Boolean] local
|
||||||
|
# @param [Hash] options
|
||||||
|
def initialize(text, local, options = {})
|
||||||
|
@text = text
|
||||||
|
@local = local
|
||||||
|
@options = options
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
return ''.html_safe if text.blank?
|
||||||
|
|
||||||
|
if local?
|
||||||
|
linkify
|
||||||
|
else
|
||||||
|
reformat.html_safe # rubocop:disable Rails/OutputSafety
|
||||||
|
end
|
||||||
|
rescue ArgumentError
|
||||||
|
''.html_safe
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def reformat
|
||||||
|
Sanitize.fragment(text, Sanitize::Config::MASTODON_STRICT)
|
||||||
|
end
|
||||||
|
|
||||||
|
def linkify
|
||||||
|
TextFormatter.new(text, options).to_s
|
||||||
|
end
|
||||||
|
end
|
|
@ -208,7 +208,7 @@ class LinkDetailsExtractor
|
||||||
end
|
end
|
||||||
|
|
||||||
def valid_url_or_nil(str, same_origin_only: false)
|
def valid_url_or_nil(str, same_origin_only: false)
|
||||||
return if str.blank?
|
return if str.blank? || str == 'null'
|
||||||
|
|
||||||
url = @original_url + Addressable::URI.parse(str)
|
url = @original_url + Addressable::URI.parse(str)
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class PlainTextFormatter
|
||||||
|
include ActionView::Helpers::TextHelper
|
||||||
|
|
||||||
|
NEWLINE_TAGS_RE = /(<br \/>|<br>|<\/p>)+/.freeze
|
||||||
|
|
||||||
|
attr_reader :text, :local
|
||||||
|
|
||||||
|
alias local? local
|
||||||
|
|
||||||
|
def initialize(text, local)
|
||||||
|
@text = text
|
||||||
|
@local = local
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
if local?
|
||||||
|
text
|
||||||
|
else
|
||||||
|
strip_tags(insert_newlines).chomp
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def insert_newlines
|
||||||
|
text.gsub(NEWLINE_TAGS_RE) { |match| "#{match}\n" }
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class RSS::Serializer
|
class RSS::Serializer
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def render_statuses(builder, statuses)
|
def render_statuses(builder, statuses)
|
||||||
|
@ -9,7 +11,7 @@ class RSS::Serializer
|
||||||
item.title(status_title(status))
|
item.title(status_title(status))
|
||||||
.link(ActivityPub::TagManager.instance.url_for(status))
|
.link(ActivityPub::TagManager.instance.url_for(status))
|
||||||
.pub_date(status.created_at)
|
.pub_date(status.created_at)
|
||||||
.description(status.spoiler_text.presence || Formatter.instance.format(status, inline_poll_options: true).to_str)
|
.description(status_description(status))
|
||||||
|
|
||||||
status.ordered_media_attachments.each do |media|
|
status.ordered_media_attachments.each do |media|
|
||||||
item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size)
|
item.enclosure(full_asset_url(media.file.url(:original, false)), media.file.content_type, media.file.size)
|
||||||
|
@ -19,9 +21,8 @@ class RSS::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def status_title(status)
|
def status_title(status)
|
||||||
return "#{status.account.acct} deleted status" if status.destroyed?
|
|
||||||
|
|
||||||
preview = status.proper.spoiler_text.presence || status.proper.text
|
preview = status.proper.spoiler_text.presence || status.proper.text
|
||||||
|
|
||||||
if preview.length > 30 || preview[0, 30].include?("\n")
|
if preview.length > 30 || preview[0, 30].include?("\n")
|
||||||
preview = preview[0, 30]
|
preview = preview[0, 30]
|
||||||
preview = preview[0, preview.index("\n").presence || 30] + '…'
|
preview = preview[0, preview.index("\n").presence || 30] + '…'
|
||||||
|
@ -35,4 +36,20 @@ class RSS::Serializer
|
||||||
"#{status.account.acct}: #{preview}"
|
"#{status.account.acct}: #{preview}"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def status_description(status)
|
||||||
|
if status.proper.spoiler_text?
|
||||||
|
status.proper.spoiler_text
|
||||||
|
else
|
||||||
|
html = status_content_format(status.proper).to_str
|
||||||
|
after_html = ''
|
||||||
|
|
||||||
|
if status.proper.preloadable_poll
|
||||||
|
poll_options_html = status.proper.preloadable_poll.options.map { |o| "[ ] #{o}" }.join('<br />')
|
||||||
|
after_html = "<p>#{poll_options_html}</p>"
|
||||||
|
end
|
||||||
|
|
||||||
|
"#{html}#{after_html}"
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,158 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class TextFormatter
|
||||||
|
include ActionView::Helpers::TextHelper
|
||||||
|
include ERB::Util
|
||||||
|
include RoutingHelper
|
||||||
|
|
||||||
|
URL_PREFIX_REGEX = /\A(https?:\/\/(www\.)?|xmpp:)/.freeze
|
||||||
|
|
||||||
|
DEFAULT_REL = %w(nofollow noopener noreferrer).freeze
|
||||||
|
|
||||||
|
DEFAULT_OPTIONS = {
|
||||||
|
multiline: true,
|
||||||
|
}.freeze
|
||||||
|
|
||||||
|
attr_reader :text, :options
|
||||||
|
|
||||||
|
# @param [String] text
|
||||||
|
# @param [Hash] options
|
||||||
|
# @option options [Boolean] :multiline
|
||||||
|
# @option options [Boolean] :with_domains
|
||||||
|
# @option options [Boolean] :with_rel_me
|
||||||
|
# @option options [Array<Account>] :preloaded_accounts
|
||||||
|
def initialize(text, options = {})
|
||||||
|
@text = text
|
||||||
|
@options = DEFAULT_OPTIONS.merge(options)
|
||||||
|
end
|
||||||
|
|
||||||
|
def entities
|
||||||
|
@entities ||= Extractor.extract_entities_with_indices(text, extract_url_without_protocol: false)
|
||||||
|
end
|
||||||
|
|
||||||
|
def to_s
|
||||||
|
return ''.html_safe if text.blank?
|
||||||
|
|
||||||
|
html = rewrite do |entity|
|
||||||
|
if entity[:url]
|
||||||
|
link_to_url(entity)
|
||||||
|
elsif entity[:hashtag]
|
||||||
|
link_to_hashtag(entity)
|
||||||
|
elsif entity[:screen_name]
|
||||||
|
link_to_mention(entity)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
html = simple_format(html, {}, sanitize: false).delete("\n") if multiline?
|
||||||
|
|
||||||
|
html.html_safe # rubocop:disable Rails/OutputSafety
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def rewrite
|
||||||
|
entities.sort_by! do |entity|
|
||||||
|
entity[:indices].first
|
||||||
|
end
|
||||||
|
|
||||||
|
result = ''.dup
|
||||||
|
|
||||||
|
last_index = entities.reduce(0) do |index, entity|
|
||||||
|
indices = entity[:indices]
|
||||||
|
result << h(text[index...indices.first])
|
||||||
|
result << yield(entity)
|
||||||
|
indices.last
|
||||||
|
end
|
||||||
|
|
||||||
|
result << h(text[last_index..-1])
|
||||||
|
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_to_url(entity)
|
||||||
|
url = Addressable::URI.parse(entity[:url]).to_s
|
||||||
|
rel = with_rel_me? ? (DEFAULT_REL + %w(me)) : DEFAULT_REL
|
||||||
|
|
||||||
|
prefix = url.match(URL_PREFIX_REGEX).to_s
|
||||||
|
display_url = url[prefix.length, 30]
|
||||||
|
suffix = url[prefix.length + 30..-1]
|
||||||
|
cutoff = url[prefix.length..-1].length > 30
|
||||||
|
|
||||||
|
<<~HTML.squish
|
||||||
|
<a href="#{h(url)}" target="_blank" rel="#{rel.join(' ')}"><span class="invisible">#{h(prefix)}</span><span class="#{cutoff ? 'ellipsis' : ''}">#{h(display_url)}</span><span class="invisible">#{h(suffix)}</span></a>
|
||||||
|
HTML
|
||||||
|
rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError
|
||||||
|
h(entity[:url])
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_to_hashtag(entity)
|
||||||
|
hashtag = entity[:hashtag]
|
||||||
|
url = tag_url(hashtag)
|
||||||
|
|
||||||
|
<<~HTML.squish
|
||||||
|
<a href="#{h(url)}" class="mention hashtag" rel="tag">#<span>#{h(hashtag)}</span></a>
|
||||||
|
HTML
|
||||||
|
end
|
||||||
|
|
||||||
|
def link_to_mention(entity)
|
||||||
|
username, domain = entity[:screen_name].split('@')
|
||||||
|
domain = nil if local_domain?(domain)
|
||||||
|
account = nil
|
||||||
|
|
||||||
|
if preloaded_accounts?
|
||||||
|
same_username_hits = 0
|
||||||
|
|
||||||
|
preloaded_accounts.each do |other_account|
|
||||||
|
same_username = other_account.username.casecmp(username).zero?
|
||||||
|
same_domain = other_account.domain.nil? ? domain.nil? : other_account.domain.casecmp(domain)&.zero?
|
||||||
|
|
||||||
|
if same_username && !same_domain
|
||||||
|
same_username_hits += 1
|
||||||
|
elsif same_username && same_domain
|
||||||
|
account = other_account
|
||||||
|
end
|
||||||
|
end
|
||||||
|
else
|
||||||
|
account = entity_cache.mention(username, domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
return "@#{h(entity[:screen_name])}" if account.nil?
|
||||||
|
|
||||||
|
url = ActivityPub::TagManager.instance.url_for(account)
|
||||||
|
display_username = same_username_hits&.positive? || with_domains? ? account.pretty_acct : account.username
|
||||||
|
|
||||||
|
<<~HTML.squish
|
||||||
|
<span class="h-card"><a href="#{h(url)}" class="u-url mention">@<span>#{h(display_username)}</span></a></span>
|
||||||
|
HTML
|
||||||
|
end
|
||||||
|
|
||||||
|
def entity_cache
|
||||||
|
@entity_cache ||= EntityCache.instance
|
||||||
|
end
|
||||||
|
|
||||||
|
def tag_manager
|
||||||
|
@tag_manager ||= TagManager.instance
|
||||||
|
end
|
||||||
|
|
||||||
|
delegate :local_domain?, to: :tag_manager
|
||||||
|
|
||||||
|
def multiline?
|
||||||
|
options[:multiline]
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_domains?
|
||||||
|
options[:with_domains]
|
||||||
|
end
|
||||||
|
|
||||||
|
def with_rel_me?
|
||||||
|
options[:with_rel_me]
|
||||||
|
end
|
||||||
|
|
||||||
|
def preloaded_accounts
|
||||||
|
options[:preloaded_accounts]
|
||||||
|
end
|
||||||
|
|
||||||
|
def preloaded_accounts?
|
||||||
|
preloaded_accounts.present?
|
||||||
|
end
|
||||||
|
end
|
|
@ -5,6 +5,7 @@ class ApplicationMailer < ActionMailer::Base
|
||||||
|
|
||||||
helper :application
|
helper :application
|
||||||
helper :instance
|
helper :instance
|
||||||
|
helper :formatting
|
||||||
|
|
||||||
protected
|
protected
|
||||||
|
|
||||||
|
|
|
@ -132,13 +132,13 @@ class Account < ApplicationRecord
|
||||||
:approved?,
|
:approved?,
|
||||||
:pending?,
|
:pending?,
|
||||||
:disabled?,
|
:disabled?,
|
||||||
|
:unconfirmed?,
|
||||||
:unconfirmed_or_pending?,
|
:unconfirmed_or_pending?,
|
||||||
:role,
|
:role,
|
||||||
:admin?,
|
:admin?,
|
||||||
:moderator?,
|
:moderator?,
|
||||||
:staff?,
|
:staff?,
|
||||||
:locale,
|
:locale,
|
||||||
:hides_network?,
|
|
||||||
:shows_application?,
|
:shows_application?,
|
||||||
to: :user,
|
to: :user,
|
||||||
prefix: true,
|
prefix: true,
|
||||||
|
|
|
@ -0,0 +1,36 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module StatusSnapshotConcern
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
included do
|
||||||
|
has_many :edits, class_name: 'StatusEdit', inverse_of: :status, dependent: :destroy
|
||||||
|
end
|
||||||
|
|
||||||
|
def edited?
|
||||||
|
edited_at.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
def build_snapshot(account_id: nil, at_time: nil, rate_limit: true)
|
||||||
|
# We don't use `edits#new` here to avoid it having saved when the
|
||||||
|
# status is saved, since we want to control that manually
|
||||||
|
|
||||||
|
StatusEdit.new(
|
||||||
|
status_id: id,
|
||||||
|
text: text,
|
||||||
|
spoiler_text: spoiler_text,
|
||||||
|
sensitive: sensitive,
|
||||||
|
ordered_media_attachment_ids: ordered_media_attachment_ids&.dup || media_attachments.pluck(:id),
|
||||||
|
media_descriptions: ordered_media_attachments.map(&:description),
|
||||||
|
poll_options: preloadable_poll&.options&.dup,
|
||||||
|
account_id: account_id || self.account_id,
|
||||||
|
content_type: content_type,
|
||||||
|
created_at: at_time || edited_at,
|
||||||
|
rate_limit: rate_limit
|
||||||
|
)
|
||||||
|
end
|
||||||
|
|
||||||
|
def snapshot!(**options)
|
||||||
|
build_snapshot(**options).save!
|
||||||
|
end
|
||||||
|
end
|
|
@ -185,7 +185,7 @@ class MediaAttachment < ApplicationRecord
|
||||||
remotable_attachment :thumbnail, IMAGE_LIMIT, suppress_errors: true, download_on_assign: false
|
remotable_attachment :thumbnail, IMAGE_LIMIT, suppress_errors: true, download_on_assign: false
|
||||||
|
|
||||||
validates :account, presence: true
|
validates :account, presence: true
|
||||||
validates :description, length: { maximum: MAX_DESCRIPTION_LENGTH }, if: :local?
|
validates :description, length: { maximum: MAX_DESCRIPTION_LENGTH }
|
||||||
validates :file, presence: true, if: :local?
|
validates :file, presence: true, if: :local?
|
||||||
validates :thumbnail, absence: true, if: -> { local? && !audio_or_video? }
|
validates :thumbnail, absence: true, if: -> { local? && !audio_or_video? }
|
||||||
|
|
||||||
|
@ -258,7 +258,6 @@ class MediaAttachment < ApplicationRecord
|
||||||
after_commit :enqueue_processing, on: :create
|
after_commit :enqueue_processing, on: :create
|
||||||
after_commit :reset_parent_cache, on: :update
|
after_commit :reset_parent_cache, on: :update
|
||||||
|
|
||||||
before_create :prepare_description, unless: :local?
|
|
||||||
before_create :set_unknown_type
|
before_create :set_unknown_type
|
||||||
before_create :set_processing
|
before_create :set_processing
|
||||||
|
|
||||||
|
@ -306,10 +305,6 @@ class MediaAttachment < ApplicationRecord
|
||||||
self.type = :unknown if file.blank? && !type_changed?
|
self.type = :unknown if file.blank? && !type_changed?
|
||||||
end
|
end
|
||||||
|
|
||||||
def prepare_description
|
|
||||||
self.description = description.strip[0...MAX_DESCRIPTION_LENGTH] unless description.nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def set_type_and_extension
|
def set_type_and_extension
|
||||||
self.type = begin
|
self.type = begin
|
||||||
if VIDEO_MIME_TYPES.include?(file_content_type)
|
if VIDEO_MIME_TYPES.include?(file_content_type)
|
||||||
|
|
|
@ -37,6 +37,7 @@ class Status < ApplicationRecord
|
||||||
include Paginable
|
include Paginable
|
||||||
include Cacheable
|
include Cacheable
|
||||||
include StatusThreadingConcern
|
include StatusThreadingConcern
|
||||||
|
include StatusSnapshotConcern
|
||||||
include RateLimitable
|
include RateLimitable
|
||||||
|
|
||||||
rate_limit by: :account, family: :statuses
|
rate_limit by: :account, family: :statuses
|
||||||
|
@ -61,8 +62,6 @@ class Status < ApplicationRecord
|
||||||
belongs_to :thread, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :replies, optional: true
|
belongs_to :thread, foreign_key: 'in_reply_to_id', class_name: 'Status', inverse_of: :replies, optional: true
|
||||||
belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblogs, optional: true
|
belongs_to :reblog, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblogs, optional: true
|
||||||
|
|
||||||
has_many :edits, class_name: 'StatusEdit', inverse_of: :status, dependent: :destroy
|
|
||||||
|
|
||||||
has_many :favourites, inverse_of: :status, dependent: :destroy
|
has_many :favourites, inverse_of: :status, dependent: :destroy
|
||||||
has_many :bookmarks, inverse_of: :status, dependent: :destroy
|
has_many :bookmarks, inverse_of: :status, dependent: :destroy
|
||||||
has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy
|
has_many :reblogs, foreign_key: 'reblog_of_id', class_name: 'Status', inverse_of: :reblog, dependent: :destroy
|
||||||
|
@ -217,25 +216,6 @@ class Status < ApplicationRecord
|
||||||
public_visibility? || unlisted_visibility?
|
public_visibility? || unlisted_visibility?
|
||||||
end
|
end
|
||||||
|
|
||||||
def snapshot!(account_id: nil, at_time: nil, rate_limit: true)
|
|
||||||
edits.create!(
|
|
||||||
text: text,
|
|
||||||
spoiler_text: spoiler_text,
|
|
||||||
sensitive: sensitive,
|
|
||||||
ordered_media_attachment_ids: ordered_media_attachment_ids || media_attachments.pluck(:id),
|
|
||||||
media_descriptions: ordered_media_attachments.map(&:description),
|
|
||||||
poll_options: preloadable_poll&.options,
|
|
||||||
account_id: account_id || self.account_id,
|
|
||||||
content_type: content_type,
|
|
||||||
created_at: at_time || edited_at,
|
|
||||||
rate_limit: rate_limit
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
def edited?
|
|
||||||
edited_at.present?
|
|
||||||
end
|
|
||||||
|
|
||||||
alias sign? distributable?
|
alias sign? distributable?
|
||||||
|
|
||||||
def with_media?
|
def with_media?
|
||||||
|
|
|
@ -37,7 +37,7 @@ class Trends::Query
|
||||||
end
|
end
|
||||||
|
|
||||||
def offset!(value)
|
def offset!(value)
|
||||||
@offset = value
|
@offset = value.to_i
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -46,7 +46,7 @@ class Trends::Query
|
||||||
end
|
end
|
||||||
|
|
||||||
def limit!(value)
|
def limit!(value)
|
||||||
@limit = value
|
@limit = value.to_i
|
||||||
self
|
self
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -208,8 +208,12 @@ class User < ApplicationRecord
|
||||||
confirmed? && approved? && !disabled? && !account.suspended? && !account.memorial?
|
confirmed? && approved? && !disabled? && !account.suspended? && !account.memorial?
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def unconfirmed?
|
||||||
|
!confirmed?
|
||||||
|
end
|
||||||
|
|
||||||
def unconfirmed_or_pending?
|
def unconfirmed_or_pending?
|
||||||
!(confirmed? && approved?)
|
unconfirmed? || pending?
|
||||||
end
|
end
|
||||||
|
|
||||||
def inactive_message
|
def inactive_message
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
||||||
include RoutingHelper
|
include RoutingHelper
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
context :security
|
context :security
|
||||||
|
|
||||||
|
@ -102,7 +103,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def summary
|
def summary
|
||||||
object.suspended? ? '' : Formatter.instance.simplified_format(object)
|
object.suspended? ? '' : html_aware_format(object.note, object.local?)
|
||||||
end
|
end
|
||||||
|
|
||||||
def icon
|
def icon
|
||||||
|
@ -185,6 +186,8 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
class Account::FieldSerializer < ActivityPub::Serializer
|
class Account::FieldSerializer < ActivityPub::Serializer
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
attributes :type, :name, :value
|
attributes :type, :name, :value
|
||||||
|
|
||||||
def type
|
def type
|
||||||
|
@ -192,7 +195,7 @@ class ActivityPub::ActorSerializer < ActivityPub::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def value
|
def value
|
||||||
Formatter.instance.format_field(object.account, object.value)
|
html_aware_format(object.value, object.account.local?, with_rel_me: true, with_domains: true, multiline: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
context_extensions :atom_uri, :conversation, :sensitive, :voters_count, :direct_message
|
context_extensions :atom_uri, :conversation, :sensitive, :voters_count, :direct_message
|
||||||
|
|
||||||
attributes :id, :type, :summary,
|
attributes :id, :type, :summary,
|
||||||
|
@ -50,11 +52,11 @@ class ActivityPub::NoteSerializer < ActivityPub::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def content
|
def content
|
||||||
Formatter.instance.format(object)
|
status_content_format(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def content_map
|
def content_map
|
||||||
{ object.language => Formatter.instance.format(object) }
|
{ object.language => content }
|
||||||
end
|
end
|
||||||
|
|
||||||
def replies
|
def replies
|
||||||
|
|
|
@ -2,6 +2,7 @@
|
||||||
|
|
||||||
class REST::AccountSerializer < ActiveModel::Serializer
|
class REST::AccountSerializer < ActiveModel::Serializer
|
||||||
include RoutingHelper
|
include RoutingHelper
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
|
attributes :id, :username, :acct, :display_name, :locked, :bot, :discoverable, :group, :created_at,
|
||||||
:note, :url, :avatar, :avatar_static, :header, :header_static,
|
:note, :url, :avatar, :avatar_static, :header, :header_static,
|
||||||
|
@ -14,10 +15,12 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
||||||
attribute :suspended, if: :suspended?
|
attribute :suspended, if: :suspended?
|
||||||
|
|
||||||
class FieldSerializer < ActiveModel::Serializer
|
class FieldSerializer < ActiveModel::Serializer
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
attributes :name, :value, :verified_at
|
attributes :name, :value, :verified_at
|
||||||
|
|
||||||
def value
|
def value
|
||||||
Formatter.instance.format_field(object.account, object.value)
|
html_aware_format(object.value, object.account.local?, with_rel_me: true, with_domains: true, multiline: false)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -32,7 +35,7 @@ class REST::AccountSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def note
|
def note
|
||||||
object.suspended? ? '' : Formatter.instance.simplified_format(object)
|
object.suspended? ? '' : html_aware_format(object.note, object.local?)
|
||||||
end
|
end
|
||||||
|
|
||||||
def url
|
def url
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class REST::AnnouncementSerializer < ActiveModel::Serializer
|
class REST::AnnouncementSerializer < ActiveModel::Serializer
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
attributes :id, :content, :starts_at, :ends_at, :all_day,
|
attributes :id, :content, :starts_at, :ends_at, :all_day,
|
||||||
:published_at, :updated_at
|
:published_at, :updated_at
|
||||||
|
|
||||||
|
@ -25,7 +27,7 @@ class REST::AnnouncementSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def content
|
def content
|
||||||
Formatter.instance.linkify(object.text)
|
linkify(object.text)
|
||||||
end
|
end
|
||||||
|
|
||||||
def reactions
|
def reactions
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class REST::StatusEditSerializer < ActiveModel::Serializer
|
class REST::StatusEditSerializer < ActiveModel::Serializer
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
has_one :account, serializer: REST::AccountSerializer
|
has_one :account, serializer: REST::AccountSerializer
|
||||||
|
|
||||||
attributes :content, :spoiler_text, :sensitive, :created_at
|
attributes :content, :spoiler_text, :sensitive, :created_at
|
||||||
|
@ -11,7 +13,7 @@ class REST::StatusEditSerializer < ActiveModel::Serializer
|
||||||
attribute :poll, if: -> { object.poll_options.present? }
|
attribute :poll, if: -> { object.poll_options.present? }
|
||||||
|
|
||||||
def content
|
def content
|
||||||
Formatter.instance.format(object)
|
status_content_format(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def poll
|
def poll
|
||||||
|
|
|
@ -1,6 +1,8 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class REST::StatusSerializer < ActiveModel::Serializer
|
class REST::StatusSerializer < ActiveModel::Serializer
|
||||||
|
include FormattingHelper
|
||||||
|
|
||||||
attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id,
|
attributes :id, :created_at, :in_reply_to_id, :in_reply_to_account_id,
|
||||||
:sensitive, :spoiler_text, :visibility, :language,
|
:sensitive, :spoiler_text, :visibility, :language,
|
||||||
:uri, :url, :replies_count, :reblogs_count,
|
:uri, :url, :replies_count, :reblogs_count,
|
||||||
|
@ -73,7 +75,7 @@ class REST::StatusSerializer < ActiveModel::Serializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def content
|
def content
|
||||||
Formatter.instance.format(object)
|
status_content_format(object)
|
||||||
end
|
end
|
||||||
|
|
||||||
def url
|
def url
|
||||||
|
|
|
@ -4,6 +4,8 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
|
||||||
include JsonLdHelper
|
include JsonLdHelper
|
||||||
|
|
||||||
def call(status, json)
|
def call(status, json)
|
||||||
|
raise ArgumentError, 'Status has unsaved changes' if status.changed?
|
||||||
|
|
||||||
@json = json
|
@json = json
|
||||||
@status_parser = ActivityPub::Parser::StatusParser.new(@json)
|
@status_parser = ActivityPub::Parser::StatusParser.new(@json)
|
||||||
@uri = @status_parser.uri
|
@uri = @status_parser.uri
|
||||||
|
@ -17,16 +19,19 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
|
||||||
|
|
||||||
last_edit_date = status.edited_at.presence || status.created_at
|
last_edit_date = status.edited_at.presence || status.created_at
|
||||||
|
|
||||||
|
# Since we rely on tracking of previous changes, ensure clean slate
|
||||||
|
status.clear_changes_information
|
||||||
|
|
||||||
# Only allow processing one create/update per status at a time
|
# Only allow processing one create/update per status at a time
|
||||||
RedisLock.acquire(lock_options) do |lock|
|
RedisLock.acquire(lock_options) do |lock|
|
||||||
if lock.acquired?
|
if lock.acquired?
|
||||||
Status.transaction do
|
Status.transaction do
|
||||||
create_previous_edit!
|
record_previous_edit!
|
||||||
update_media_attachments!
|
update_media_attachments!
|
||||||
update_poll!
|
update_poll!
|
||||||
update_immediate_attributes!
|
update_immediate_attributes!
|
||||||
update_metadata!
|
update_metadata!
|
||||||
create_edit!
|
create_edits!
|
||||||
end
|
end
|
||||||
|
|
||||||
queue_poll_notifications!
|
queue_poll_notifications!
|
||||||
|
@ -216,19 +221,14 @@ class ActivityPub::ProcessStatusUpdateService < BaseService
|
||||||
{ redis: Redis.current, key: "create:#{@uri}", autorelease: 15.minutes.seconds }
|
{ redis: Redis.current, key: "create:#{@uri}", autorelease: 15.minutes.seconds }
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_previous_edit!
|
def record_previous_edit!
|
||||||
# We only need to create a previous edit when no previous edits exist, e.g.
|
@previous_edit = @status.build_snapshot(at_time: @status.created_at, rate_limit: false) if @status.edits.empty?
|
||||||
# when the status has never been edited. For other cases, we always create
|
|
||||||
# an edit, so the step can be skipped
|
|
||||||
|
|
||||||
return if @status.edits.any?
|
|
||||||
|
|
||||||
@status.snapshot!(at_time: @status.created_at, rate_limit: false)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def create_edit!
|
def create_edits!
|
||||||
return unless significant_changes?
|
return unless significant_changes?
|
||||||
|
|
||||||
|
@previous_edit&.save!
|
||||||
@status.snapshot!(account_id: @account.id, rate_limit: false)
|
@status.snapshot!(account_id: @account.id, rate_limit: false)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -134,7 +134,7 @@ class FetchLinkCardService < BaseService
|
||||||
when 'video'
|
when 'video'
|
||||||
@card.width = embed[:width].presence || 0
|
@card.width = embed[:width].presence || 0
|
||||||
@card.height = embed[:height].presence || 0
|
@card.height = embed[:height].presence || 0
|
||||||
@card.html = Formatter.instance.sanitize(embed[:html], Sanitize::Config::MASTODON_OEMBED)
|
@card.html = Sanitize.fragment(embed[:html], Sanitize::Config::MASTODON_OEMBED)
|
||||||
@card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
|
@card.image_remote_url = (url + embed[:thumbnail_url]).to_s if embed[:thumbnail_url].present?
|
||||||
when 'rich'
|
when 'rich'
|
||||||
# Most providers rely on <script> tags, which is a no-no
|
# Most providers rely on <script> tags, which is a no-no
|
||||||
|
|
|
@ -4,6 +4,8 @@ class UpdateStatusService < BaseService
|
||||||
include Redisable
|
include Redisable
|
||||||
include LanguagesHelper
|
include LanguagesHelper
|
||||||
|
|
||||||
|
class NoChangesSubmittedError < StandardError; end
|
||||||
|
|
||||||
# @param [Status] status
|
# @param [Status] status
|
||||||
# @param [Integer] account_id
|
# @param [Integer] account_id
|
||||||
# @param [Hash] options
|
# @param [Hash] options
|
||||||
|
@ -18,6 +20,8 @@ class UpdateStatusService < BaseService
|
||||||
@status = status
|
@status = status
|
||||||
@options = options
|
@options = options
|
||||||
@account_id = account_id
|
@account_id = account_id
|
||||||
|
@media_attachments_changed = false
|
||||||
|
@poll_changed = false
|
||||||
|
|
||||||
Status.transaction do
|
Status.transaction do
|
||||||
create_previous_edit!
|
create_previous_edit!
|
||||||
|
@ -33,18 +37,24 @@ class UpdateStatusService < BaseService
|
||||||
broadcast_updates!
|
broadcast_updates!
|
||||||
|
|
||||||
@status
|
@status
|
||||||
|
rescue NoChangesSubmittedError
|
||||||
|
# For calls that result in no changes, swallow the error
|
||||||
|
# but get back to the original state
|
||||||
|
|
||||||
|
@status.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
private
|
private
|
||||||
|
|
||||||
def update_media_attachments!
|
def update_media_attachments!
|
||||||
previous_media_attachments = @status.media_attachments.to_a
|
previous_media_attachments = @status.ordered_media_attachments.to_a
|
||||||
next_media_attachments = validate_media!
|
next_media_attachments = validate_media!
|
||||||
added_media_attachments = next_media_attachments - previous_media_attachments
|
added_media_attachments = next_media_attachments - previous_media_attachments
|
||||||
|
|
||||||
MediaAttachment.where(id: added_media_attachments.map(&:id)).update_all(status_id: @status.id)
|
MediaAttachment.where(id: added_media_attachments.map(&:id)).update_all(status_id: @status.id)
|
||||||
|
|
||||||
@status.ordered_media_attachment_ids = (@options[:media_ids] || []).map(&:to_i) & next_media_attachments.map(&:id)
|
@status.ordered_media_attachment_ids = (@options[:media_ids] || []).map(&:to_i) & next_media_attachments.map(&:id)
|
||||||
|
@media_attachments_changed = previous_media_attachments.map(&:id) != @status.ordered_media_attachment_ids
|
||||||
@status.media_attachments.reload
|
@status.media_attachments.reload
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -70,20 +80,23 @@ class UpdateStatusService < BaseService
|
||||||
|
|
||||||
# If for some reasons the options were changed, it invalidates all previous
|
# If for some reasons the options were changed, it invalidates all previous
|
||||||
# votes, so we need to remove them
|
# votes, so we need to remove them
|
||||||
poll_changed = true if @options[:poll][:options] != poll.options || ActiveModel::Type::Boolean.new.cast(@options[:poll][:multiple]) != poll.multiple
|
@poll_changed = true if @options[:poll][:options] != poll.options || ActiveModel::Type::Boolean.new.cast(@options[:poll][:multiple]) != poll.multiple
|
||||||
|
|
||||||
poll.options = @options[:poll][:options]
|
poll.options = @options[:poll][:options]
|
||||||
poll.hide_totals = @options[:poll][:hide_totals] || false
|
poll.hide_totals = @options[:poll][:hide_totals] || false
|
||||||
poll.multiple = @options[:poll][:multiple] || false
|
poll.multiple = @options[:poll][:multiple] || false
|
||||||
poll.expires_in = @options[:poll][:expires_in]
|
poll.expires_in = @options[:poll][:expires_in]
|
||||||
poll.reset_votes! if poll_changed
|
poll.reset_votes! if @poll_changed
|
||||||
poll.save!
|
poll.save!
|
||||||
|
|
||||||
@status.poll_id = poll.id
|
@status.poll_id = poll.id
|
||||||
elsif previous_poll.present?
|
elsif previous_poll.present?
|
||||||
previous_poll.destroy
|
previous_poll.destroy
|
||||||
|
@poll_changed = true
|
||||||
@status.poll_id = nil
|
@status.poll_id = nil
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@poll_changed = true if @previous_expires_at != @status.preloadable_poll&.expires_at
|
||||||
end
|
end
|
||||||
|
|
||||||
def update_immediate_attributes!
|
def update_immediate_attributes!
|
||||||
|
@ -92,8 +105,11 @@ class UpdateStatusService < BaseService
|
||||||
@status.sensitive = @options[:sensitive] || @options[:spoiler_text].present? if @options.key?(:sensitive) || @options.key?(:spoiler_text)
|
@status.sensitive = @options[:sensitive] || @options[:spoiler_text].present? if @options.key?(:sensitive) || @options.key?(:spoiler_text)
|
||||||
@status.language = valid_locale_cascade(@options[:language], @status.language, @status.account.user&.preferred_posting_language, I18n.default_locale)
|
@status.language = valid_locale_cascade(@options[:language], @status.language, @status.account.user&.preferred_posting_language, I18n.default_locale)
|
||||||
@status.content_type = @options[:content_type] || @status.content_type
|
@status.content_type = @options[:content_type] || @status.content_type
|
||||||
@status.edited_at = Time.now.utc
|
|
||||||
|
|
||||||
|
# We raise here to rollback the entire transaction
|
||||||
|
raise NoChangesSubmittedError unless significant_changes?
|
||||||
|
|
||||||
|
@status.edited_at = Time.now.utc
|
||||||
@status.save!
|
@status.save!
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -139,4 +155,8 @@ class UpdateStatusService < BaseService
|
||||||
def create_edit!
|
def create_edit!
|
||||||
@status.snapshot!(account_id: @account_id)
|
@status.snapshot!(account_id: @account_id)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def significant_changes?
|
||||||
|
@status.changed? || @poll_changed || @media_attachments_changed
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -5,17 +5,17 @@
|
||||||
.account__header__fields
|
.account__header__fields
|
||||||
- fields.each do |field|
|
- fields.each do |field|
|
||||||
%dl
|
%dl
|
||||||
%dt.emojify{ title: field.name }= Formatter.instance.format_field(account, field.name, custom_emojify: true)
|
%dt.emojify{ title: field.name }= prerender_custom_emojis(h(field.name), account.emojis)
|
||||||
%dd{ title: field.value, class: custom_field_classes(field) }
|
%dd{ title: field.value, class: custom_field_classes(field) }
|
||||||
- if field.verified?
|
- if field.verified?
|
||||||
%span.verified__mark{ title: t('accounts.link_verified_on', date: l(field.verified_at)) }
|
%span.verified__mark{ title: t('accounts.link_verified_on', date: l(field.verified_at)) }
|
||||||
= fa_icon 'check'
|
= fa_icon 'check'
|
||||||
= Formatter.instance.format_field(account, field.value, custom_emojify: true)
|
= prerender_custom_emojis(html_aware_format(field.value, account.local?, with_rel_me: true, with_domains: true, multiline: false), account.emojis)
|
||||||
|
|
||||||
= account_badge(account)
|
= account_badge(account)
|
||||||
|
|
||||||
- if account.note.present?
|
- if account.note.present?
|
||||||
.account__header__content.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)
|
.account__header__content.emojify= prerender_custom_emojis(html_aware_format(account.note, account.local?), account.emojis)
|
||||||
|
|
||||||
.public-account-bio__extra
|
.public-account-bio__extra
|
||||||
= t 'accounts.joined', date: l(account.created_at, format: :month)
|
= t 'accounts.joined', date: l(account.created_at, format: :month)
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
.batch-table__row{ class: [!account.suspended? && account.user_pending? && 'batch-table__row--attention', account.suspended? && 'batch-table__row--muted'] }
|
.batch-table__row{ class: [!account.suspended? && account.user_pending? && 'batch-table__row--attention', (account.suspended? || account.user_unconfirmed?) && 'batch-table__row--muted'] }
|
||||||
%label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
|
%label.batch-table__row__select.batch-table__row__select--aligned.batch-checkbox
|
||||||
= f.check_box :account_ids, { multiple: true, include_hidden: false }, account.id
|
= f.check_box :account_ids, { multiple: true, include_hidden: false }, account.id
|
||||||
.batch-table__row__content.batch-table__row__content--unpadded
|
.batch-table__row__content.batch-table__row__content--unpadded
|
||||||
|
|
|
@ -16,16 +16,16 @@
|
||||||
.account__header__fields
|
.account__header__fields
|
||||||
- fields.each do |field|
|
- fields.each do |field|
|
||||||
%dl
|
%dl
|
||||||
%dt.emojify{ title: field.name }= Formatter.instance.format_field(account, field.name, custom_emojify: true)
|
%dt.emojify{ title: field.name }= prerender_custom_emojis(h(field.name), account.emojis)
|
||||||
%dd{ title: field.value, class: custom_field_classes(field) }
|
%dd{ title: field.value, class: custom_field_classes(field) }
|
||||||
- if field.verified?
|
- if field.verified?
|
||||||
%span.verified__mark{ title: t('accounts.link_verified_on', date: l(field.verified_at)) }
|
%span.verified__mark{ title: t('accounts.link_verified_on', date: l(field.verified_at)) }
|
||||||
= fa_icon 'check'
|
= fa_icon 'check'
|
||||||
= Formatter.instance.format_field(account, field.value, custom_emojify: true)
|
= prerender_custom_emojis(html_aware_format(field.value, account.local?, with_rel_me: true, with_domains: true, multiline: false), account.emojis)
|
||||||
|
|
||||||
- if account.note.present?
|
- if account.note.present?
|
||||||
%div
|
%div
|
||||||
.account__header__content.emojify= Formatter.instance.simplified_format(account, custom_emojify: true)
|
.account__header__content.emojify= prerender_custom_emojis(html_aware_format(account.note, account.local?), account.emojis)
|
||||||
|
|
||||||
.dashboard__counters.admin-account-counters
|
.dashboard__counters.admin-account-counters
|
||||||
%div
|
%div
|
||||||
|
|
|
@ -84,7 +84,8 @@
|
||||||
- else
|
- else
|
||||||
%span.negative-hint
|
%span.negative-hint
|
||||||
= t('admin.instances.availability.failures_recorded', count: @instance.delivery_failure_tracker.days)
|
= t('admin.instances.availability.failures_recorded', count: @instance.delivery_failure_tracker.days)
|
||||||
= link_to t('admin.instances.delivery.clear'), clear_delivery_errors_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post } unless @instance.exhausted_deliveries_days.empty?
|
%span= link_to t('admin.instances.delivery.clear'), clear_delivery_errors_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post } unless @instance.exhausted_deliveries_days.empty?
|
||||||
|
%span= link_to t('admin.instances.delivery.stop'), stop_delivery_admin_instance_path(@instance), data: { confirm: t('admin.accounts.are_you_sure'), method: :post }
|
||||||
|
|
||||||
- if @instance.purgeable?
|
- if @instance.purgeable?
|
||||||
%p= t('admin.instances.purge_description_html')
|
%p= t('admin.instances.purge_description_html')
|
||||||
|
|
|
@ -4,12 +4,12 @@
|
||||||
.batch-table__row__content
|
.batch-table__row__content
|
||||||
.status__content><
|
.status__content><
|
||||||
- if status.proper.spoiler_text.blank?
|
- if status.proper.spoiler_text.blank?
|
||||||
= Formatter.instance.format(status.proper, custom_emojify: true)
|
= prerender_custom_emojis(status_content_format(status.proper), status.proper.emojis)
|
||||||
- else
|
- else
|
||||||
%details<
|
%details<
|
||||||
%summary><
|
%summary><
|
||||||
%strong> Content warning: #{Formatter.instance.format_spoiler(status.proper)}
|
%strong> Content warning: #{prerender_custom_emojis(h(status.proper.spoiler_text), status.proper.emojis)}
|
||||||
= Formatter.instance.format(status.proper, custom_emojify: true)
|
= prerender_custom_emojis(status_content_format(status.proper), status.proper.emojis)
|
||||||
|
|
||||||
- unless status.proper.ordered_media_attachments.empty?
|
- unless status.proper.ordered_media_attachments.empty?
|
||||||
- if status.proper.ordered_media_attachments.first.video?
|
- if status.proper.ordered_media_attachments.first.video?
|
||||||
|
|
|
@ -23,7 +23,7 @@
|
||||||
= fa_icon('lock') if @report.target_account.locked?
|
= fa_icon('lock') if @report.target_account.locked?
|
||||||
- if @report.target_account.note.present?
|
- if @report.target_account.note.present?
|
||||||
.account-card__bio.emojify
|
.account-card__bio.emojify
|
||||||
= Formatter.instance.simplified_format(@report.target_account, custom_emojify: true)
|
= prerender_custom_emojis(html_aware_format(@report.target_account.note, @report.target_account.local?), @report.target_account.emojis)
|
||||||
.account-card__actions
|
.account-card__actions
|
||||||
.account-card__counters
|
.account-card__counters
|
||||||
.account-card__counters__item
|
.account-card__counters__item
|
||||||
|
|
|
@ -8,7 +8,7 @@
|
||||||
|
|
||||||
.dashboard
|
.dashboard
|
||||||
.dashboard__item
|
.dashboard__item
|
||||||
= react_admin_component :counter, measure: 'tag_accounts', start_at: @time_period.first, end_at: @time_period.last, params: { id: @tag.id }, label: t('admin.trends.tags.dashboard.tag_accounts_measure')
|
= react_admin_component :counter, measure: 'tag_accounts', start_at: @time_period.first, end_at: @time_period.last, params: { id: @tag.id }, label: t('admin.trends.tags.dashboard.tag_accounts_measure'), href: tag_url(@tag), target: '_blank'
|
||||||
.dashboard__item
|
.dashboard__item
|
||||||
= react_admin_component :counter, measure: 'tag_uses', start_at: @time_period.first, end_at: @time_period.last, params: { id: @tag.id }, label: t('admin.trends.tags.dashboard.tag_uses_measure')
|
= react_admin_component :counter, measure: 'tag_uses', start_at: @time_period.first, end_at: @time_period.last, params: { id: @tag.id }, label: t('admin.trends.tags.dashboard.tag_uses_measure')
|
||||||
.dashboard__item
|
.dashboard__item
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
= fa_icon('lock') if account.locked?
|
= fa_icon('lock') if account.locked?
|
||||||
- if account.note.present?
|
- if account.note.present?
|
||||||
.account-card__bio.emojify
|
.account-card__bio.emojify
|
||||||
= Formatter.instance.simplified_format(account, custom_emojify: true)
|
= prerender_custom_emojis(html_aware_format(account.note, account.local?), account.emojis)
|
||||||
- else
|
- else
|
||||||
.flex-spacer
|
.flex-spacer
|
||||||
.account-card__actions
|
.account-card__actions
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
%p= t "user_mailer.warning.explanation.#{@strike.action}", instance: Rails.configuration.x.local_domain
|
%p= t "user_mailer.warning.explanation.#{@strike.action}", instance: Rails.configuration.x.local_domain
|
||||||
|
|
||||||
- unless @strike.text.blank?
|
- unless @strike.text.blank?
|
||||||
= Formatter.instance.linkify(@strike.text)
|
= linkify(@strike.text)
|
||||||
|
|
||||||
- if @strike.report && !@strike.report.other?
|
- if @strike.report && !@strike.report.other?
|
||||||
%p
|
%p
|
||||||
|
|
|
@ -28,10 +28,10 @@
|
||||||
- if status.spoiler_text?
|
- if status.spoiler_text?
|
||||||
%div.auto-dir
|
%div.auto-dir
|
||||||
%p
|
%p
|
||||||
= Formatter.instance.format_spoiler(status)
|
= status.spoiler_text
|
||||||
|
|
||||||
%div.auto-dir
|
%div.auto-dir
|
||||||
= Formatter.instance.format(status)
|
= status_content_format(status)
|
||||||
|
|
||||||
- if status.ordered_media_attachments.size > 0
|
- if status.ordered_media_attachments.size > 0
|
||||||
%p
|
%p
|
||||||
|
|
|
@ -3,6 +3,6 @@
|
||||||
> ----
|
> ----
|
||||||
>
|
>
|
||||||
<% end %>
|
<% end %>
|
||||||
> <%= raw word_wrap(Formatter.instance.plaintext(status), break_sequence: "\n> ") %>
|
> <%= raw word_wrap(extract_plain_text(status.text, status.local?), break_sequence: "\n> ") %>
|
||||||
|
|
||||||
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{status.id}") %>
|
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{status.id}") %>
|
||||||
|
|
|
@ -5,7 +5,7 @@
|
||||||
|
|
||||||
* <%= raw t('notification_mailer.digest.mention', name: notification.from_account.pretty_acct) %>
|
* <%= raw t('notification_mailer.digest.mention', name: notification.from_account.pretty_acct) %>
|
||||||
|
|
||||||
<%= raw Formatter.instance.plaintext(notification.target_status) %>
|
<%= raw extract_plain_text(notification.target_status.text, notification.target_status.local?) %>
|
||||||
|
|
||||||
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{notification.target_status.id}") %>
|
<%= raw t('application_mailer.view')%> <%= web_url("statuses/#{notification.target_status.id}") %>
|
||||||
<% end %>
|
<% end %>
|
||||||
|
|
|
@ -18,10 +18,11 @@
|
||||||
.status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
|
.status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
|
||||||
- if status.spoiler_text?
|
- if status.spoiler_text?
|
||||||
%p<
|
%p<
|
||||||
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: prefers_autoplay?)}
|
%span.p-summary> #{prerender_custom_emojis(h(status.spoiler_text), status.emojis)}
|
||||||
%button.status__content__spoiler-link= t('statuses.show_more')
|
%button.status__content__spoiler-link= t('statuses.show_more')
|
||||||
.e-content
|
.e-content
|
||||||
= Formatter.instance.format(status, custom_emojify: true, autoplay: prefers_autoplay?)
|
= prerender_custom_emojis(status_content_format(status), status.emojis)
|
||||||
|
|
||||||
- if status.preloadable_poll
|
- if status.preloadable_poll
|
||||||
= render_poll_component(status)
|
= render_poll_component(status)
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@
|
||||||
%span.poll__number><
|
%span.poll__number><
|
||||||
= "#{percent.round}%"
|
= "#{percent.round}%"
|
||||||
%span.poll__option__text
|
%span.poll__option__text
|
||||||
= Formatter.instance.format_poll_option(status, option, autoplay: prefers_autoplay?)
|
= prerender_custom_emojis(h(option.title), status.emojis)
|
||||||
- if own_votes.include?(index)
|
- if own_votes.include?(index)
|
||||||
%span.poll__voted
|
%span.poll__voted
|
||||||
%i.poll__voted__mark.fa.fa-check
|
%i.poll__voted__mark.fa.fa-check
|
||||||
|
@ -23,7 +23,7 @@
|
||||||
%label.poll__option><
|
%label.poll__option><
|
||||||
%span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}><
|
%span.poll__input{ class: poll.multiple? ? 'checkbox' : nil}><
|
||||||
%span.poll__option__text
|
%span.poll__option__text
|
||||||
= Formatter.instance.format_poll_option(status, option, autoplay: prefers_autoplay?)
|
= prerender_custom_emojis(h(option.title), status.emojis)
|
||||||
.poll__footer
|
.poll__footer
|
||||||
- unless show_results
|
- unless show_results
|
||||||
%button.button.button-secondary{ disabled: true }
|
%button.button.button-secondary{ disabled: true }
|
||||||
|
|
|
@ -30,10 +30,11 @@
|
||||||
.status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
|
.status__content.emojify{ :data => ({ spoiler: current_account&.user&.setting_expand_spoilers ? 'expanded' : 'folded' } if status.spoiler_text?) }<
|
||||||
- if status.spoiler_text?
|
- if status.spoiler_text?
|
||||||
%p<
|
%p<
|
||||||
%span.p-summary> #{Formatter.instance.format_spoiler(status, autoplay: prefers_autoplay?)}
|
%span.p-summary> #{prerender_custom_emojis(h(status.spoiler_text), status.emojis)}
|
||||||
%button.status__content__spoiler-link= t('statuses.show_more')
|
%button.status__content__spoiler-link= t('statuses.show_more')
|
||||||
.e-content<
|
.e-content<
|
||||||
= Formatter.instance.format(status, custom_emojify: true, autoplay: prefers_autoplay?)
|
= prerender_custom_emojis(status_content_format(status), status.emojis)
|
||||||
|
|
||||||
- if status.preloadable_poll
|
- if status.preloadable_poll
|
||||||
= render_poll_component(status)
|
= render_poll_component(status)
|
||||||
|
|
||||||
|
|
|
@ -40,7 +40,7 @@
|
||||||
%p= t "user_mailer.warning.explanation.#{@warning.action}", instance: @instance
|
%p= t "user_mailer.warning.explanation.#{@warning.action}", instance: @instance
|
||||||
|
|
||||||
- unless @warning.text.blank?
|
- unless @warning.text.blank?
|
||||||
= Formatter.instance.linkify(@warning.text)
|
= linkify(@warning.text)
|
||||||
|
|
||||||
- if @warning.report && !@warning.report.other?
|
- if @warning.report && !@warning.report.other?
|
||||||
%p
|
%p
|
||||||
|
|
|
@ -75,30 +75,4 @@ module Twitter::TwitterText
|
||||||
)
|
)
|
||||||
}iox
|
}iox
|
||||||
end
|
end
|
||||||
|
|
||||||
module Extractor
|
|
||||||
# Extracts a list of all XMPP and magnet URIs included in the Toot <tt>text</tt> along
|
|
||||||
# with the indices. If the <tt>text</tt> is <tt>nil</tt> or contains no
|
|
||||||
# XMPP or magnet URIs an empty array will be returned.
|
|
||||||
#
|
|
||||||
# If a block is given then it will be called for each XMPP URI.
|
|
||||||
def extract_extra_uris_with_indices(text, _options = {}) # :yields: uri, start, end
|
|
||||||
return [] unless text && text.index(":")
|
|
||||||
urls = []
|
|
||||||
|
|
||||||
text.to_s.scan(Twitter::TwitterText::Regex[:valid_extended_uri]) do
|
|
||||||
valid_uri_match_data = $~
|
|
||||||
|
|
||||||
start_position = valid_uri_match_data.char_begin(3)
|
|
||||||
end_position = valid_uri_match_data.char_end(3)
|
|
||||||
|
|
||||||
urls << {
|
|
||||||
:url => valid_uri_match_data[3],
|
|
||||||
:indices => [start_position, end_position]
|
|
||||||
}
|
|
||||||
end
|
|
||||||
urls.each{|url| yield url[:url], url[:indices].first, url[:indices].last} if block_given?
|
|
||||||
urls
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -24,7 +24,7 @@ nl:
|
||||||
status:
|
status:
|
||||||
attributes:
|
attributes:
|
||||||
reblog:
|
reblog:
|
||||||
taken: van toot bestaat al
|
taken: van bericht bestaat al
|
||||||
user:
|
user:
|
||||||
attributes:
|
attributes:
|
||||||
email:
|
email:
|
||||||
|
|
|
@ -490,6 +490,7 @@ ca:
|
||||||
other: Intents fallits en %{count} diferents dies.
|
other: Intents fallits en %{count} diferents dies.
|
||||||
no_failures_recorded: Sense errors registrats.
|
no_failures_recorded: Sense errors registrats.
|
||||||
title: Disponibilitat
|
title: Disponibilitat
|
||||||
|
warning: El darrer intent de connectar a aquest servidor no ha tingut èxit
|
||||||
back_to_all: Totes
|
back_to_all: Totes
|
||||||
back_to_limited: Limitades
|
back_to_limited: Limitades
|
||||||
back_to_warning: Avís
|
back_to_warning: Avís
|
||||||
|
|
|
@ -490,6 +490,7 @@ da:
|
||||||
other: Mislykkede forsøg på %{count} forskellige dage.
|
other: Mislykkede forsøg på %{count} forskellige dage.
|
||||||
no_failures_recorded: Ingen fejl noteret.
|
no_failures_recorded: Ingen fejl noteret.
|
||||||
title: Tilgængelighed
|
title: Tilgængelighed
|
||||||
|
warning: Seneste forsøg på at oprette forbindelse til denne server mislykkedes
|
||||||
back_to_all: Alle
|
back_to_all: Alle
|
||||||
back_to_limited: Begrænset
|
back_to_limited: Begrænset
|
||||||
back_to_warning: Advarsel
|
back_to_warning: Advarsel
|
||||||
|
|
|
@ -373,6 +373,7 @@ de:
|
||||||
enable: Aktivieren
|
enable: Aktivieren
|
||||||
enabled: Aktiviert
|
enabled: Aktiviert
|
||||||
enabled_msg: Das Emoji wurde aktiviert
|
enabled_msg: Das Emoji wurde aktiviert
|
||||||
|
image_hint: PNG oder GIF bis %{size}
|
||||||
list: Liste
|
list: Liste
|
||||||
listed: Gelistet
|
listed: Gelistet
|
||||||
new:
|
new:
|
||||||
|
@ -489,6 +490,7 @@ de:
|
||||||
other: Fehlgeschlagener Versuch am %{count}. Tag.
|
other: Fehlgeschlagener Versuch am %{count}. Tag.
|
||||||
no_failures_recorded: Keine Fehler bei der Aufzeichnung.
|
no_failures_recorded: Keine Fehler bei der Aufzeichnung.
|
||||||
title: Verfügbarkeit
|
title: Verfügbarkeit
|
||||||
|
warning: Der letzte Versuch, sich mit diesem Server zu verbinden, war nicht erfolgreich
|
||||||
back_to_all: Alle
|
back_to_all: Alle
|
||||||
back_to_limited: Beschränkt
|
back_to_limited: Beschränkt
|
||||||
back_to_warning: Warnung
|
back_to_warning: Warnung
|
||||||
|
@ -1436,6 +1438,7 @@ de:
|
||||||
disallowed_hashtags:
|
disallowed_hashtags:
|
||||||
one: 'enthält einen verbotenen Hashtag: %{tags}'
|
one: 'enthält einen verbotenen Hashtag: %{tags}'
|
||||||
other: 'enthält verbotene Hashtags: %{tags}'
|
other: 'enthält verbotene Hashtags: %{tags}'
|
||||||
|
edited_at_html: Bearbeitet %{date}
|
||||||
errors:
|
errors:
|
||||||
in_reply_not_found: Der Beitrag, auf den du antworten möchtest, scheint nicht zu existieren.
|
in_reply_not_found: Der Beitrag, auf den du antworten möchtest, scheint nicht zu existieren.
|
||||||
open_in_web: Im Web öffnen
|
open_in_web: Im Web öffnen
|
||||||
|
|
|
@ -73,6 +73,10 @@ id:
|
||||||
index:
|
index:
|
||||||
authorized_at: Diberi hak otorisasi pada %{date}
|
authorized_at: Diberi hak otorisasi pada %{date}
|
||||||
description_html: Ini adalah aplikasi yang dapat mengakses akun Anda menggunakan API. Jika ada aplikasi yang tidak Anda kenal di sini, atau aplikasi yang berperilaku aneh, Anda dapat mencabut hak aksesnya.
|
description_html: Ini adalah aplikasi yang dapat mengakses akun Anda menggunakan API. Jika ada aplikasi yang tidak Anda kenal di sini, atau aplikasi yang berperilaku aneh, Anda dapat mencabut hak aksesnya.
|
||||||
|
last_used_at: Terakhir dipakai pada %{date}
|
||||||
|
never_used: Tidak pernah dipakai
|
||||||
|
scopes: Hak akses
|
||||||
|
superapp: Internal
|
||||||
title: Aplikasi yang anda izinkan
|
title: Aplikasi yang anda izinkan
|
||||||
errors:
|
errors:
|
||||||
messages:
|
messages:
|
||||||
|
@ -108,6 +112,33 @@ id:
|
||||||
authorized_applications:
|
authorized_applications:
|
||||||
destroy:
|
destroy:
|
||||||
notice: Aplikasi dicabut.
|
notice: Aplikasi dicabut.
|
||||||
|
grouped_scopes:
|
||||||
|
access:
|
||||||
|
read: Akses baca-saja
|
||||||
|
read/write: Akses baca dan tulis
|
||||||
|
write: Akses tulis-saja
|
||||||
|
title:
|
||||||
|
accounts: Akun
|
||||||
|
admin/accounts: Administrasi akun
|
||||||
|
admin/all: Semua fungsi administratif
|
||||||
|
admin/reports: Administrasi laporan
|
||||||
|
all: Segalanya
|
||||||
|
blocks: Blokir
|
||||||
|
bookmarks: Markah
|
||||||
|
conversations: Percakapan
|
||||||
|
crypto: Enkripsi end-to-end
|
||||||
|
favourites: Favorit
|
||||||
|
filters: Saringan
|
||||||
|
follow: Hubungan
|
||||||
|
follows: Mengikuti
|
||||||
|
lists: Daftar
|
||||||
|
media: Lampiran media
|
||||||
|
mutes: Bisukan
|
||||||
|
notifications: Notifikasi
|
||||||
|
push: Notifikasi dorong
|
||||||
|
reports: Laporan
|
||||||
|
search: Pencarian
|
||||||
|
statuses: Kiriman
|
||||||
layouts:
|
layouts:
|
||||||
admin:
|
admin:
|
||||||
nav:
|
nav:
|
||||||
|
@ -122,6 +153,7 @@ id:
|
||||||
admin:write: ubah semua data di server
|
admin:write: ubah semua data di server
|
||||||
admin:write:accounts: lakukan aksi moderasi akun
|
admin:write:accounts: lakukan aksi moderasi akun
|
||||||
admin:write:reports: lakukan aksi moderasi laporan
|
admin:write:reports: lakukan aksi moderasi laporan
|
||||||
|
crypto: menggunakan enkripsi end-to-end
|
||||||
follow: mengikuti, blokir, menghapus blokir, dan berhenti mengikuti akun
|
follow: mengikuti, blokir, menghapus blokir, dan berhenti mengikuti akun
|
||||||
push: terima notifikasi dorong
|
push: terima notifikasi dorong
|
||||||
read: membaca data pada akun anda
|
read: membaca data pada akun anda
|
||||||
|
@ -141,6 +173,7 @@ id:
|
||||||
write:accounts: ubah profil Anda
|
write:accounts: ubah profil Anda
|
||||||
write:blocks: blokir akun dan domain
|
write:blocks: blokir akun dan domain
|
||||||
write:bookmarks: status markah
|
write:bookmarks: status markah
|
||||||
|
write:conversations: bisukan dan hapus percakapan
|
||||||
write:favourites: status favorit
|
write:favourites: status favorit
|
||||||
write:filters: buat saringan
|
write:filters: buat saringan
|
||||||
write:follows: ikuti orang
|
write:follows: ikuti orang
|
||||||
|
|
|
@ -130,7 +130,7 @@ ku:
|
||||||
favourites: Bijarte
|
favourites: Bijarte
|
||||||
filters: Parzûn
|
filters: Parzûn
|
||||||
follow: Pêwendî
|
follow: Pêwendî
|
||||||
follows: Şopîner
|
follows: Dişopîne
|
||||||
lists: Rêzok
|
lists: Rêzok
|
||||||
media: Pêvekên medya
|
media: Pêvekên medya
|
||||||
mutes: Bêdengkirin
|
mutes: Bêdengkirin
|
||||||
|
@ -162,7 +162,7 @@ ku:
|
||||||
read:bookmarks: şûnpelên xwe bibîne
|
read:bookmarks: şûnpelên xwe bibîne
|
||||||
read:favourites: bijarteyên xwe bibîne
|
read:favourites: bijarteyên xwe bibîne
|
||||||
read:filters: parzûnûn xwe bibîne
|
read:filters: parzûnûn xwe bibîne
|
||||||
read:follows: şopînerên xwe bibîne
|
read:follows: ên tu dişopînî bibîne
|
||||||
read:lists: rêzoka xwe bibîne
|
read:lists: rêzoka xwe bibîne
|
||||||
read:mutes: ajimêrên bêdeng kirî bibîne
|
read:mutes: ajimêrên bêdeng kirî bibîne
|
||||||
read:notifications: agahdariyên xwe bibîne
|
read:notifications: agahdariyên xwe bibîne
|
||||||
|
|
|
@ -60,6 +60,8 @@ nl:
|
||||||
error:
|
error:
|
||||||
title: Er is een fout opgetreden
|
title: Er is een fout opgetreden
|
||||||
new:
|
new:
|
||||||
|
prompt_html: "%{client_name} heeft toestemming nodig om toegang te krijgen tot jouw account. Het betreft een third-party-toepassing.<strong>Als je dit niet vertrouwt, moet je geen toestemming verlenen.</strong>"
|
||||||
|
review_permissions: Toestemmingen beoordelen
|
||||||
title: Autorisatie vereist
|
title: Autorisatie vereist
|
||||||
show:
|
show:
|
||||||
title: Kopieer deze autorisatiecode en plak het in de toepassing.
|
title: Kopieer deze autorisatiecode en plak het in de toepassing.
|
||||||
|
@ -69,6 +71,11 @@ nl:
|
||||||
confirmations:
|
confirmations:
|
||||||
revoke: Weet je het zeker?
|
revoke: Weet je het zeker?
|
||||||
index:
|
index:
|
||||||
|
authorized_at: Toestemming verleent op %{date}
|
||||||
|
last_used_at: Voor het laatst gebruikt op %{date}
|
||||||
|
never_used: Nooit gebruikt
|
||||||
|
scopes: Toestemmingen
|
||||||
|
superapp: Intern
|
||||||
title: Jouw geautoriseerde toepassingen
|
title: Jouw geautoriseerde toepassingen
|
||||||
errors:
|
errors:
|
||||||
messages:
|
messages:
|
||||||
|
@ -104,6 +111,33 @@ nl:
|
||||||
authorized_applications:
|
authorized_applications:
|
||||||
destroy:
|
destroy:
|
||||||
notice: Toepassing ingetrokken.
|
notice: Toepassing ingetrokken.
|
||||||
|
grouped_scopes:
|
||||||
|
access:
|
||||||
|
read: Alleen leestoegang
|
||||||
|
read/write: Lees- en schrijftoegang
|
||||||
|
write: Alleen schrijftoegang
|
||||||
|
title:
|
||||||
|
accounts: Accounts
|
||||||
|
admin/accounts: Accountbeheer
|
||||||
|
admin/all: Alle beheerfuncties
|
||||||
|
admin/reports: Rapportagebeheer
|
||||||
|
all: Alles
|
||||||
|
blocks: Blokkeren
|
||||||
|
bookmarks: Bladwijzers
|
||||||
|
conversations: Gesprekken
|
||||||
|
crypto: End-to-end-encryptie
|
||||||
|
favourites: Favorieten
|
||||||
|
filters: Filters
|
||||||
|
follow: Relaties
|
||||||
|
follows: Volgend
|
||||||
|
lists: Lijsten
|
||||||
|
media: Mediabijlagen
|
||||||
|
mutes: Negeren
|
||||||
|
notifications: Meldingen
|
||||||
|
push: Pushmeldingen
|
||||||
|
reports: Rapportages
|
||||||
|
search: Zoeken
|
||||||
|
statuses: Berichten
|
||||||
layouts:
|
layouts:
|
||||||
admin:
|
admin:
|
||||||
nav:
|
nav:
|
||||||
|
@ -118,6 +152,7 @@ nl:
|
||||||
admin:write: wijzig alle gegevens op de server
|
admin:write: wijzig alle gegevens op de server
|
||||||
admin:write:accounts: moderatieacties op accounts uitvoeren
|
admin:write:accounts: moderatieacties op accounts uitvoeren
|
||||||
admin:write:reports: moderatieacties op rapportages uitvoeren
|
admin:write:reports: moderatieacties op rapportages uitvoeren
|
||||||
|
crypto: end-to-end-encryptie gebruiken
|
||||||
follow: relaties tussen accounts bewerken
|
follow: relaties tussen accounts bewerken
|
||||||
push: jouw pushmeldingen ontvangen
|
push: jouw pushmeldingen ontvangen
|
||||||
read: alle gegevens van jouw account lezen
|
read: alle gegevens van jouw account lezen
|
||||||
|
@ -130,14 +165,15 @@ nl:
|
||||||
read:lists: jouw lijsten bekijken
|
read:lists: jouw lijsten bekijken
|
||||||
read:mutes: jouw genegeerde gebruikers bekijken
|
read:mutes: jouw genegeerde gebruikers bekijken
|
||||||
read:notifications: jouw meldingen bekijken
|
read:notifications: jouw meldingen bekijken
|
||||||
read:reports: jouw gerapporteerde toots bekijken
|
read:reports: jouw gerapporteerde berichten bekijken
|
||||||
read:search: namens jou zoeken
|
read:search: namens jou zoeken
|
||||||
read:statuses: alle toots bekijken
|
read:statuses: alle berichten bekijken
|
||||||
write: alle gegevens van jouw account bewerken
|
write: alle gegevens van jouw account bewerken
|
||||||
write:accounts: jouw profiel bewerken
|
write:accounts: jouw profiel bewerken
|
||||||
write:blocks: accounts en domeinen blokkeren
|
write:blocks: accounts en domeinen blokkeren
|
||||||
write:bookmarks: toots aan bladwijzers toevoegen
|
write:bookmarks: berichten aan bladwijzers toevoegen
|
||||||
write:favourites: toots als favoriet markeren
|
write:conversations: gespreken negeren en verwijderen
|
||||||
|
write:favourites: berichten als favoriet markeren
|
||||||
write:filters: filters aanmaken
|
write:filters: filters aanmaken
|
||||||
write:follows: mensen volgen
|
write:follows: mensen volgen
|
||||||
write:lists: lijsten aanmaken
|
write:lists: lijsten aanmaken
|
||||||
|
@ -145,4 +181,4 @@ nl:
|
||||||
write:mutes: mensen en gesprekken negeren
|
write:mutes: mensen en gesprekken negeren
|
||||||
write:notifications: meldingen verwijderen
|
write:notifications: meldingen verwijderen
|
||||||
write:reports: andere mensen rapporteren
|
write:reports: andere mensen rapporteren
|
||||||
write:statuses: toots publiceren
|
write:statuses: berichten plaatsen
|
||||||
|
|
|
@ -775,6 +775,11 @@ en:
|
||||||
system_checks:
|
system_checks:
|
||||||
database_schema_check:
|
database_schema_check:
|
||||||
message_html: There are pending database migrations. Please run them to ensure the application behaves as expected
|
message_html: There are pending database migrations. Please run them to ensure the application behaves as expected
|
||||||
|
elasticsearch_running_check:
|
||||||
|
message_html: Could not connect to Elasticsearch. Please check that it is running, or disable full-text search
|
||||||
|
elasticsearch_version_check:
|
||||||
|
message_html: 'Incompatible Elasticsearch version: %{value}'
|
||||||
|
version_comparison: Elasticsearch %{running_version} is running while %{required_version} is required
|
||||||
rules_check:
|
rules_check:
|
||||||
action: Manage server rules
|
action: Manage server rules
|
||||||
message_html: You haven't defined any server rules.
|
message_html: You haven't defined any server rules.
|
||||||
|
|
|
@ -490,6 +490,7 @@ es-AR:
|
||||||
other: Intentos fallidos en %{count} días.
|
other: Intentos fallidos en %{count} días.
|
||||||
no_failures_recorded: No hay fallos en el registro.
|
no_failures_recorded: No hay fallos en el registro.
|
||||||
title: Disponibilidad
|
title: Disponibilidad
|
||||||
|
warning: El último intento de conexión a este servidor no fue exitoso
|
||||||
back_to_all: Todos
|
back_to_all: Todos
|
||||||
back_to_limited: Limitados
|
back_to_limited: Limitados
|
||||||
back_to_warning: Advertencia
|
back_to_warning: Advertencia
|
||||||
|
|
|
@ -490,6 +490,7 @@ es:
|
||||||
other: Intentos fallidos en %{count} días diferentes.
|
other: Intentos fallidos en %{count} días diferentes.
|
||||||
no_failures_recorded: No hay fallos en el registro.
|
no_failures_recorded: No hay fallos en el registro.
|
||||||
title: Disponibilidad
|
title: Disponibilidad
|
||||||
|
warning: El último intento de conexión a este servidor no ha tenido éxito
|
||||||
back_to_all: Todos
|
back_to_all: Todos
|
||||||
back_to_limited: Limitados
|
back_to_limited: Limitados
|
||||||
back_to_warning: Advertencia
|
back_to_warning: Advertencia
|
||||||
|
|
|
@ -490,6 +490,7 @@ gl:
|
||||||
other: Intentos fallidos durante %{count} días distintos.
|
other: Intentos fallidos durante %{count} días distintos.
|
||||||
no_failures_recorded: Non hai fallos rexistrados.
|
no_failures_recorded: Non hai fallos rexistrados.
|
||||||
title: Dispoñibilidade
|
title: Dispoñibilidade
|
||||||
|
warning: Fallou o último intento de conectar con este servidor
|
||||||
back_to_all: Todo
|
back_to_all: Todo
|
||||||
back_to_limited: Limitado
|
back_to_limited: Limitado
|
||||||
back_to_warning: Aviso
|
back_to_warning: Aviso
|
||||||
|
|
|
@ -492,6 +492,7 @@ hu:
|
||||||
other: Sikertelen próbálkozás %{count} különböző napon.
|
other: Sikertelen próbálkozás %{count} különböző napon.
|
||||||
no_failures_recorded: Nem rögzítettünk hibát.
|
no_failures_recorded: Nem rögzítettünk hibát.
|
||||||
title: Elérhetőség
|
title: Elérhetőség
|
||||||
|
warning: Sikertelen volt az utolsó csatlakozási próbálkozás ehhez a szerverhez
|
||||||
back_to_all: Mind
|
back_to_all: Mind
|
||||||
back_to_limited: Korlátozott
|
back_to_limited: Korlátozott
|
||||||
back_to_warning: Figyelmeztetés
|
back_to_warning: Figyelmeztetés
|
||||||
|
|
|
@ -477,6 +477,7 @@ id:
|
||||||
other: Upaya gagal dalam %{count} hari berbeda.
|
other: Upaya gagal dalam %{count} hari berbeda.
|
||||||
no_failures_recorded: Tidak ada kegagalan tercatat.
|
no_failures_recorded: Tidak ada kegagalan tercatat.
|
||||||
title: Ketersediaan
|
title: Ketersediaan
|
||||||
|
warning: Upaya terakhir untuk menyambung ke server ini tidak berhasil
|
||||||
back_to_all: Semua
|
back_to_all: Semua
|
||||||
back_to_limited: Terbatas
|
back_to_limited: Terbatas
|
||||||
back_to_warning: Peringatan
|
back_to_warning: Peringatan
|
||||||
|
|
|
@ -490,6 +490,7 @@ is:
|
||||||
other: Misheppnaðar tilraunir á %{count} mismunandi dögum.
|
other: Misheppnaðar tilraunir á %{count} mismunandi dögum.
|
||||||
no_failures_recorded: Engar misheppnaðar tilraunir á skrá.
|
no_failures_recorded: Engar misheppnaðar tilraunir á skrá.
|
||||||
title: Tiltækileiki
|
title: Tiltækileiki
|
||||||
|
warning: Síðasta tilraun til að tengjast þessum netþjóni mistókst
|
||||||
back_to_all: Allt
|
back_to_all: Allt
|
||||||
back_to_limited: Takmarkað
|
back_to_limited: Takmarkað
|
||||||
back_to_warning: Aðvörun
|
back_to_warning: Aðvörun
|
||||||
|
|
|
@ -490,6 +490,7 @@ it:
|
||||||
other: Tentativo fallito %{count} giorni differenti.
|
other: Tentativo fallito %{count} giorni differenti.
|
||||||
no_failures_recorded: Nessun fallimento registrato.
|
no_failures_recorded: Nessun fallimento registrato.
|
||||||
title: Disponibilità
|
title: Disponibilità
|
||||||
|
warning: L'ultimo tentativo di connessione a questo server non è riuscito
|
||||||
back_to_all: Tutto
|
back_to_all: Tutto
|
||||||
back_to_limited: Limitato
|
back_to_limited: Limitato
|
||||||
back_to_warning: Avviso
|
back_to_warning: Avviso
|
||||||
|
|
|
@ -481,6 +481,7 @@ ko:
|
||||||
other: 실패한 전달 시도 총 %{count}일.
|
other: 실패한 전달 시도 총 %{count}일.
|
||||||
no_failures_recorded: 실패 기록이 없습니다.
|
no_failures_recorded: 실패 기록이 없습니다.
|
||||||
title: 가용성
|
title: 가용성
|
||||||
|
warning: 이 서버에 대한 마지막 연결 시도가 성공적이지 않았습니다
|
||||||
back_to_all: 전체
|
back_to_all: 전체
|
||||||
back_to_limited: 제한됨
|
back_to_limited: 제한됨
|
||||||
back_to_warning: 경고
|
back_to_warning: 경고
|
||||||
|
|
|
@ -133,7 +133,7 @@ ku:
|
||||||
enabled: Çalakkirî
|
enabled: Çalakkirî
|
||||||
enabled_msg: Ajimêra %{username} bi serkeftî hat çalak kirin
|
enabled_msg: Ajimêra %{username} bi serkeftî hat çalak kirin
|
||||||
followers: Şopîner
|
followers: Şopîner
|
||||||
follows: Dişopînê
|
follows: Dişopîne
|
||||||
header: Jormalper
|
header: Jormalper
|
||||||
inbox_url: Peyamên hatî URl
|
inbox_url: Peyamên hatî URl
|
||||||
invite_request_text: Sedemên tevlêbûnê
|
invite_request_text: Sedemên tevlêbûnê
|
||||||
|
@ -492,6 +492,7 @@ ku:
|
||||||
other: Hewldanên têkçûyî di %{count} rojên cuda de.
|
other: Hewldanên têkçûyî di %{count} rojên cuda de.
|
||||||
no_failures_recorded: Di tomarê de têkçûn tune.
|
no_failures_recorded: Di tomarê de têkçûn tune.
|
||||||
title: Berdestbûnî
|
title: Berdestbûnî
|
||||||
|
warning: Hewldana dawî ji bo girêdana bi vê rajekarê re bi ser neket
|
||||||
back_to_all: Hemû
|
back_to_all: Hemû
|
||||||
back_to_limited: Sînorkirî
|
back_to_limited: Sînorkirî
|
||||||
back_to_warning: Hişyarî
|
back_to_warning: Hişyarî
|
||||||
|
@ -543,7 +544,7 @@ ku:
|
||||||
title: Giştî
|
title: Giştî
|
||||||
total_blocked_by_us: Ji aliyê me ve hatiye astengkirin
|
total_blocked_by_us: Ji aliyê me ve hatiye astengkirin
|
||||||
total_followed_by_them: Ji aliyê wan ve hatiye şopandin
|
total_followed_by_them: Ji aliyê wan ve hatiye şopandin
|
||||||
total_followed_by_us: Ji aliyê ve me hate şopandin
|
total_followed_by_us: Ji aliyê me ve hatiye şopandin
|
||||||
total_reported: Giliyên derheqê wan de
|
total_reported: Giliyên derheqê wan de
|
||||||
total_storage: Pêvekên medyayê
|
total_storage: Pêvekên medyayê
|
||||||
totals_time_period_hint_html: Tevahiyên ku li jêr têne xuyakirin daneyên hemû deman dihewîne.
|
totals_time_period_hint_html: Tevahiyên ku li jêr têne xuyakirin daneyên hemû deman dihewîne.
|
||||||
|
@ -975,7 +976,7 @@ ku:
|
||||||
close: An jî, tu dikarî tenê ev çarçoveyê bigirî.
|
close: An jî, tu dikarî tenê ev çarçoveyê bigirî.
|
||||||
return: Profîla vê bikarhênerê nîşan bike
|
return: Profîla vê bikarhênerê nîşan bike
|
||||||
web: Biçe tevneyê
|
web: Biçe tevneyê
|
||||||
title: Bişopîne %{acct}
|
title: "%{acct} bişopîne"
|
||||||
challenge:
|
challenge:
|
||||||
confirm: Bidomîne
|
confirm: Bidomîne
|
||||||
hint_html: "<strong>Nîşe:</strong>Ji bo demjimêreke din em ê pêborîna te careke din ji te nexwazin."
|
hint_html: "<strong>Nîşe:</strong>Ji bo demjimêreke din em ê pêborîna te careke din ji te nexwazin."
|
||||||
|
@ -1247,7 +1248,7 @@ ku:
|
||||||
follow:
|
follow:
|
||||||
body: "%{name} niha te dişopîne!"
|
body: "%{name} niha te dişopîne!"
|
||||||
subject: "%{name} niha te dişopîne"
|
subject: "%{name} niha te dişopîne"
|
||||||
title: Şopînereke nû
|
title: Şopînera nû
|
||||||
follow_request:
|
follow_request:
|
||||||
action: Daxwazên şopandinê bi rê ve bibe
|
action: Daxwazên şopandinê bi rê ve bibe
|
||||||
body: "%{name} daxwaza şopandina te kir"
|
body: "%{name} daxwaza şopandina te kir"
|
||||||
|
@ -1416,7 +1417,7 @@ ku:
|
||||||
notifications: Agahdarî
|
notifications: Agahdarî
|
||||||
preferences: Hilbijarte
|
preferences: Hilbijarte
|
||||||
profile: Profîl
|
profile: Profîl
|
||||||
relationships: Yên tê şopandin û şopîner
|
relationships: Şopandin û şopîner
|
||||||
statuses_cleanup: Bi xweberî ve jêbirina şandiya
|
statuses_cleanup: Bi xweberî ve jêbirina şandiya
|
||||||
strikes: Binpêkirinên çavdêriyê
|
strikes: Binpêkirinên çavdêriyê
|
||||||
two_factor_authentication: Piştrastkirinê du-faktorî
|
two_factor_authentication: Piştrastkirinê du-faktorî
|
||||||
|
|
|
@ -501,6 +501,7 @@ lv:
|
||||||
zero: Neizdevušies mēģinājumi %{count} dienās.
|
zero: Neizdevušies mēģinājumi %{count} dienās.
|
||||||
no_failures_recorded: Nav reģistrētu kļūdu.
|
no_failures_recorded: Nav reģistrētu kļūdu.
|
||||||
title: Pieejamība
|
title: Pieejamība
|
||||||
|
warning: Pēdējais mēģinājums izveidot savienojumu ar šo serveri ir bijis neveiksmīgs
|
||||||
back_to_all: Visas
|
back_to_all: Visas
|
||||||
back_to_limited: Ierobežotās
|
back_to_limited: Ierobežotās
|
||||||
back_to_warning: Brīdinājums
|
back_to_warning: Brīdinājums
|
||||||
|
|
|
@ -1,7 +1,7 @@
|
||||||
---
|
---
|
||||||
nl:
|
nl:
|
||||||
about:
|
about:
|
||||||
about_hashtag_html: Dit zijn openbare toots die getagged zijn met <strong>#%{hashtag}</strong>. Je kunt er op reageren of iets anders mee doen als je op Mastodon (of ergens anders in de fediverse) een account hebt.
|
about_hashtag_html: Dit zijn openbare berichten die getagged zijn met <strong>#%{hashtag}</strong>. Je kunt er op reageren of iets anders mee doen als je op Mastodon (of ergens anders in de fediverse) een account hebt.
|
||||||
about_mastodon_html: Mastodon is een sociaal netwerk dat gebruikt maakt van open webprotocollen en vrije software. Het is net zoals e-mail gedecentraliseerd.
|
about_mastodon_html: Mastodon is een sociaal netwerk dat gebruikt maakt van open webprotocollen en vrije software. Het is net zoals e-mail gedecentraliseerd.
|
||||||
about_this: Over deze server
|
about_this: Over deze server
|
||||||
active_count_after: actief
|
active_count_after: actief
|
||||||
|
@ -31,7 +31,7 @@ nl:
|
||||||
source_code: Broncode
|
source_code: Broncode
|
||||||
status_count_after:
|
status_count_after:
|
||||||
one: toot
|
one: toot
|
||||||
other: toots
|
other: berichten
|
||||||
status_count_before: Zij schreven
|
status_count_before: Zij schreven
|
||||||
tagline: Vrienden volgen en nieuwe ontdekken
|
tagline: Vrienden volgen en nieuwe ontdekken
|
||||||
terms: Gebruiksvoorwaarden
|
terms: Gebruiksvoorwaarden
|
||||||
|
@ -41,7 +41,7 @@ nl:
|
||||||
reason: 'Reden:'
|
reason: 'Reden:'
|
||||||
rejecting_media: 'Mediabestanden van deze server worden niet verwerkt en er worden geen thumbnails getoond. Je moet handmatig naar deze server doorklikken om de mediabestanden te kunnen bekijken:'
|
rejecting_media: 'Mediabestanden van deze server worden niet verwerkt en er worden geen thumbnails getoond. Je moet handmatig naar deze server doorklikken om de mediabestanden te kunnen bekijken:'
|
||||||
rejecting_media_title: Mediabestanden geweigerd
|
rejecting_media_title: Mediabestanden geweigerd
|
||||||
silenced: Toots van deze server worden nergens weergegeven, behalve op jouw eigen starttijdlijn wanneer je het account volgt.
|
silenced: Berichten van deze server worden nergens weergegeven, behalve op jouw eigen starttijdlijn wanneer je het account volgt.
|
||||||
silenced_title: Beperkte servers
|
silenced_title: Beperkte servers
|
||||||
suspended: Je bent niet in staat om iemand van deze server te volgen, en er worden geen gegevens van deze server verwerkt of opgeslagen, en met deze server uitgewisseld.
|
suspended: Je bent niet in staat om iemand van deze server te volgen, en er worden geen gegevens van deze server verwerkt of opgeslagen, en met deze server uitgewisseld.
|
||||||
suspended_title: Opgeschorte servers
|
suspended_title: Opgeschorte servers
|
||||||
|
@ -74,9 +74,9 @@ nl:
|
||||||
following: Je moet dit account wel al volgen, alvorens je het kan aanbevelen
|
following: Je moet dit account wel al volgen, alvorens je het kan aanbevelen
|
||||||
posts:
|
posts:
|
||||||
one: Toot
|
one: Toot
|
||||||
other: Toots
|
other: Berichten
|
||||||
posts_tab_heading: Toots
|
posts_tab_heading: Berichten
|
||||||
posts_with_replies: Toots en reacties
|
posts_with_replies: Berichten en reacties
|
||||||
roles:
|
roles:
|
||||||
admin: Beheerder
|
admin: Beheerder
|
||||||
bot: Bot
|
bot: Bot
|
||||||
|
@ -193,7 +193,7 @@ nl:
|
||||||
targeted_reports: Door anderen gerapporteerd
|
targeted_reports: Door anderen gerapporteerd
|
||||||
silence: Beperken
|
silence: Beperken
|
||||||
silenced: Beperkt
|
silenced: Beperkt
|
||||||
statuses: Toots
|
statuses: Berichten
|
||||||
subscribe: Abonneren
|
subscribe: Abonneren
|
||||||
suspended: Opgeschort
|
suspended: Opgeschort
|
||||||
suspension_irreversible: De gegevens van dit account zijn onomkeerbaar verwijderd. Je kunt het opschorten van dit account ongedaan maken zodat het weer valt te gebruiken, maar de verwijderde gegevens worden hiermee niet hersteld.
|
suspension_irreversible: De gegevens van dit account zijn onomkeerbaar verwijderd. Je kunt het opschorten van dit account ongedaan maken zodat het weer valt te gebruiken, maar de verwijderde gegevens worden hiermee niet hersteld.
|
||||||
|
@ -229,7 +229,7 @@ nl:
|
||||||
destroy_custom_emoji: Lokale emoji verwijderen
|
destroy_custom_emoji: Lokale emoji verwijderen
|
||||||
destroy_domain_allow: Domeingoedkeuring verwijderen
|
destroy_domain_allow: Domeingoedkeuring verwijderen
|
||||||
destroy_domain_block: Domeinblokkade verwijderen
|
destroy_domain_block: Domeinblokkade verwijderen
|
||||||
destroy_email_domain_block: E-maildomeinblokkade verwijderen
|
destroy_email_domain_block: Blokkade van e-maildomein verwijderen
|
||||||
destroy_ip_block: IP-regel verwijderen
|
destroy_ip_block: IP-regel verwijderen
|
||||||
destroy_status: Toot verwijderen
|
destroy_status: Toot verwijderen
|
||||||
destroy_unavailable_domain: Niet beschikbaar domein verwijderen
|
destroy_unavailable_domain: Niet beschikbaar domein verwijderen
|
||||||
|
@ -245,16 +245,16 @@ nl:
|
||||||
reset_password_user: Wachtwoord opnieuw instellen
|
reset_password_user: Wachtwoord opnieuw instellen
|
||||||
resolve_report: Rapportage oplossen
|
resolve_report: Rapportage oplossen
|
||||||
sensitive_account: De media in jouw account als gevoelig markeren
|
sensitive_account: De media in jouw account als gevoelig markeren
|
||||||
silence_account: Account negeren
|
silence_account: Account beperken
|
||||||
suspend_account: Account opschorten
|
suspend_account: Account opschorten
|
||||||
unassigned_report: Rapportage niet langer toewijzen
|
unassigned_report: Rapportage niet langer toewijzen
|
||||||
unsensitive_account: De media in jouw account niet langer als gevoelig markeren
|
unsensitive_account: De media in jouw account niet langer als gevoelig markeren
|
||||||
unsilence_account: Account niet langer negeren
|
unsilence_account: Account niet langer beperken
|
||||||
unsuspend_account: Account niet langer opschorten
|
unsuspend_account: Account niet langer opschorten
|
||||||
update_announcement: Mededeling bijwerken
|
update_announcement: Mededeling bijwerken
|
||||||
update_custom_emoji: Lokale emoji bijwerken
|
update_custom_emoji: Lokale emoji bijwerken
|
||||||
update_domain_block: Domeinblokkade bijwerken
|
update_domain_block: Domeinblokkade bijwerken
|
||||||
update_status: Toot bijwerken
|
update_status: Bericht bijwerken
|
||||||
actions:
|
actions:
|
||||||
assigned_to_self_report_html: "%{name} heeft rapportage %{target} aan zichzelf toegewezen"
|
assigned_to_self_report_html: "%{name} heeft rapportage %{target} aan zichzelf toegewezen"
|
||||||
change_email_user_html: "%{name} veranderde het e-mailadres van gebruiker %{target}"
|
change_email_user_html: "%{name} veranderde het e-mailadres van gebruiker %{target}"
|
||||||
|
@ -274,7 +274,7 @@ nl:
|
||||||
destroy_domain_block_html: Domein %{target} is door %{name} gedeblokkeerd
|
destroy_domain_block_html: Domein %{target} is door %{name} gedeblokkeerd
|
||||||
destroy_email_domain_block_html: "%{name} heeft het e-maildomein %{target} gedeblokkeerd"
|
destroy_email_domain_block_html: "%{name} heeft het e-maildomein %{target} gedeblokkeerd"
|
||||||
destroy_ip_block_html: "%{name} verwijderde regel voor IP %{target}"
|
destroy_ip_block_html: "%{name} verwijderde regel voor IP %{target}"
|
||||||
destroy_status_html: Toot van %{target} is door %{name} verwijderd
|
destroy_status_html: Bericht van %{target} is door %{name} verwijderd
|
||||||
destroy_unavailable_domain_html: "%{name} heeft de bezorging voor domein %{target} hervat"
|
destroy_unavailable_domain_html: "%{name} heeft de bezorging voor domein %{target} hervat"
|
||||||
disable_2fa_user_html: De vereiste tweestapsverificatie voor %{target} is door %{name} uitgeschakeld
|
disable_2fa_user_html: De vereiste tweestapsverificatie voor %{target} is door %{name} uitgeschakeld
|
||||||
disable_custom_emoji_html: Emoji %{target} is door %{name} uitgeschakeld
|
disable_custom_emoji_html: Emoji %{target} is door %{name} uitgeschakeld
|
||||||
|
@ -297,8 +297,8 @@ nl:
|
||||||
update_announcement_html: "%{name} heeft de mededeling %{target} bijgewerkt"
|
update_announcement_html: "%{name} heeft de mededeling %{target} bijgewerkt"
|
||||||
update_custom_emoji_html: Emoji %{target} is door %{name} bijgewerkt
|
update_custom_emoji_html: Emoji %{target} is door %{name} bijgewerkt
|
||||||
update_domain_block_html: "%{name} heeft de domeinblokkade bijgewerkt voor %{target}"
|
update_domain_block_html: "%{name} heeft de domeinblokkade bijgewerkt voor %{target}"
|
||||||
update_status_html: "%{name} heeft de toots van %{target} bijgewerkt"
|
update_status_html: "%{name} heeft de berichten van %{target} bijgewerkt"
|
||||||
deleted_status: "(verwijderde toot}"
|
deleted_status: "(verwijderd bericht}"
|
||||||
empty: Geen logs gevonden.
|
empty: Geen logs gevonden.
|
||||||
filter_by_action: Op actie filteren
|
filter_by_action: Op actie filteren
|
||||||
filter_by_user: Op gebruiker filteren
|
filter_by_user: Op gebruiker filteren
|
||||||
|
@ -461,11 +461,11 @@ nl:
|
||||||
relays:
|
relays:
|
||||||
add_new: Nieuwe relayserver toevoegen
|
add_new: Nieuwe relayserver toevoegen
|
||||||
delete: Verwijderen
|
delete: Verwijderen
|
||||||
description_html: Een <strong>federatierelay</strong> is een tussenliggende server die grote hoeveelheden openbare toots uitwisselt tussen servers die zich hierop hebben geabonneerd. <strong>Het kan kleine en middelgrote servers helpen om content uit de fediverse te ontdekken</strong>, waarvoor anders lokale gebruikers handmatig mensen van externe servers moeten volgen.
|
description_html: Een <strong>federatierelay</strong> is een tussenliggende server die grote hoeveelheden openbare berichten uitwisselt tussen servers die zich hierop hebben geabonneerd. <strong>Het kan kleine en middelgrote servers helpen om content van de fediverse te ontdekken</strong>, waarvoor anders lokale gebruikers handmatig mensen van externe servers moeten volgen.
|
||||||
disable: Uitschakelen
|
disable: Uitschakelen
|
||||||
disabled: Uitgeschakeld
|
disabled: Uitgeschakeld
|
||||||
enable: Inschakelen
|
enable: Inschakelen
|
||||||
enable_hint: Eenmaal ingeschakeld gaat jouw server zich op alle openbare toots van deze relayserver abonneren en stuurt het de openbare toots van jouw server naar de relayserver.
|
enable_hint: Eenmaal ingeschakeld gaat jouw server zich op alle openbare berichten van deze relayserver abonneren en stuurt het de openbare berichten van jouw server naar de relayserver.
|
||||||
enabled: Ingeschakeld
|
enabled: Ingeschakeld
|
||||||
inbox_url: Relay-URL
|
inbox_url: Relay-URL
|
||||||
pending: Aan het wachten op toestemming van de relayserver
|
pending: Aan het wachten op toestemming van de relayserver
|
||||||
|
@ -506,7 +506,7 @@ nl:
|
||||||
reported_by: Gerapporteerd door
|
reported_by: Gerapporteerd door
|
||||||
resolved: Opgelost
|
resolved: Opgelost
|
||||||
resolved_msg: Rapportage succesvol opgelost!
|
resolved_msg: Rapportage succesvol opgelost!
|
||||||
status: Toot
|
status: Bericht
|
||||||
title: Rapportages
|
title: Rapportages
|
||||||
unassign: Niet langer toewijzen
|
unassign: Niet langer toewijzen
|
||||||
unresolved: Onopgelost
|
unresolved: Onopgelost
|
||||||
|
@ -520,7 +520,7 @@ nl:
|
||||||
title: Serverregels
|
title: Serverregels
|
||||||
settings:
|
settings:
|
||||||
activity_api_enabled:
|
activity_api_enabled:
|
||||||
desc_html: Wekelijks overzicht van de hoeveelheid lokale toots, actieve gebruikers en nieuwe registraties
|
desc_html: Wekelijks overzicht van de hoeveelheid lokale berichten, actieve gebruikers en nieuwe registraties
|
||||||
title: Statistieken over gebruikersactiviteit via de API publiceren
|
title: Statistieken over gebruikersactiviteit via de API publiceren
|
||||||
bootstrap_timeline_accounts:
|
bootstrap_timeline_accounts:
|
||||||
desc_html: Meerdere gebruikersnamen met komma's scheiden. Deze accounts worden in ieder geval aan nieuwe gebruikers aanbevolen
|
desc_html: Meerdere gebruikersnamen met komma's scheiden. Deze accounts worden in ieder geval aan nieuwe gebruikers aanbevolen
|
||||||
|
@ -533,7 +533,7 @@ nl:
|
||||||
title: Aangepaste CSS
|
title: Aangepaste CSS
|
||||||
default_noindex:
|
default_noindex:
|
||||||
desc_html: Heeft invloed op alle gebruikers die deze instelling niet zelf hebben veranderd
|
desc_html: Heeft invloed op alle gebruikers die deze instelling niet zelf hebben veranderd
|
||||||
title: Toots van gebruikers standaard niet door zoekmachines laten indexeren
|
title: Berichten van gebruikers standaard niet door zoekmachines laten indexeren
|
||||||
domain_blocks:
|
domain_blocks:
|
||||||
all: Aan iedereen
|
all: Aan iedereen
|
||||||
disabled: Aan niemand
|
disabled: Aan niemand
|
||||||
|
@ -561,7 +561,7 @@ nl:
|
||||||
desc_html: Wordt op de voorpagina weergegeven wanneer registratie van nieuwe accounts is uitgeschakeld<br>En ook hier kan je HTML gebruiken
|
desc_html: Wordt op de voorpagina weergegeven wanneer registratie van nieuwe accounts is uitgeschakeld<br>En ook hier kan je HTML gebruiken
|
||||||
title: Bericht wanneer registratie is uitgeschakeld
|
title: Bericht wanneer registratie is uitgeschakeld
|
||||||
deletion:
|
deletion:
|
||||||
desc_html: Toestaan dat iedereen hun eigen account kan verwijderen
|
desc_html: Toestaan dat iedereen diens eigen account kan verwijderen
|
||||||
title: Verwijderen account toestaan
|
title: Verwijderen account toestaan
|
||||||
min_invite_role:
|
min_invite_role:
|
||||||
disabled: Niemand
|
disabled: Niemand
|
||||||
|
@ -606,7 +606,7 @@ nl:
|
||||||
title: Hashtags toestaan om trending te worden zonder voorafgaande beoordeling
|
title: Hashtags toestaan om trending te worden zonder voorafgaande beoordeling
|
||||||
trends:
|
trends:
|
||||||
desc_html: Eerder beoordeelde hashtags die op dit moment trending zijn openbaar tonen
|
desc_html: Eerder beoordeelde hashtags die op dit moment trending zijn openbaar tonen
|
||||||
title: Trending hashtags
|
title: Trends
|
||||||
site_uploads:
|
site_uploads:
|
||||||
delete: Geüpload bestand verwijderen
|
delete: Geüpload bestand verwijderen
|
||||||
destroyed_msg: Verwijderen website-upload geslaagd!
|
destroyed_msg: Verwijderen website-upload geslaagd!
|
||||||
|
@ -615,8 +615,8 @@ nl:
|
||||||
deleted: Verwijderd
|
deleted: Verwijderd
|
||||||
media:
|
media:
|
||||||
title: Media
|
title: Media
|
||||||
no_status_selected: Er werden geen toots gewijzigd, omdat er geen enkele werd geselecteerd
|
no_status_selected: Er werden geen berichten gewijzigd, omdat er geen enkele werd geselecteerd
|
||||||
title: Toots van account
|
title: Berichten van account
|
||||||
with_media: Met media
|
with_media: Met media
|
||||||
system_checks:
|
system_checks:
|
||||||
database_schema_check:
|
database_schema_check:
|
||||||
|
@ -662,14 +662,14 @@ nl:
|
||||||
guide_link: https://crowdin.com/project/mastodon/nl
|
guide_link: https://crowdin.com/project/mastodon/nl
|
||||||
guide_link_text: Iedereen kan bijdragen.
|
guide_link_text: Iedereen kan bijdragen.
|
||||||
sensitive_content: Gevoelige inhoud
|
sensitive_content: Gevoelige inhoud
|
||||||
toot_layout: Lay-out van toots
|
toot_layout: Lay-out van berichten
|
||||||
application_mailer:
|
application_mailer:
|
||||||
notification_preferences: E-mailvoorkeuren wijzigen
|
notification_preferences: E-mailvoorkeuren wijzigen
|
||||||
salutation: "%{name},"
|
salutation: "%{name},"
|
||||||
settings: 'E-mailvoorkeuren wijzigen: %{link}'
|
settings: 'E-mailvoorkeuren wijzigen: %{link}'
|
||||||
view: 'Bekijk:'
|
view: 'Bekijk:'
|
||||||
view_profile: Profiel bekijken
|
view_profile: Profiel bekijken
|
||||||
view_status: Toot bekijken
|
view_status: Bericht bekijken
|
||||||
applications:
|
applications:
|
||||||
created: Aanmaken toepassing geslaagd
|
created: Aanmaken toepassing geslaagd
|
||||||
destroyed: Verwijderen toepassing geslaagd
|
destroyed: Verwijderen toepassing geslaagd
|
||||||
|
@ -768,8 +768,8 @@ nl:
|
||||||
success_msg: Jouw account is succesvol verwijderd
|
success_msg: Jouw account is succesvol verwijderd
|
||||||
warning:
|
warning:
|
||||||
before: 'Lees deze tekst zorgvuldig voordat je verder gaat:'
|
before: 'Lees deze tekst zorgvuldig voordat je verder gaat:'
|
||||||
caches: Toots en media die op andere servers zijn opgeslagen kunnen daar achterblijven
|
caches: Berichten en media die op andere servers zijn opgeslagen kunnen daar achterblijven
|
||||||
data_removal: Jouw toots en andere gegevens worden permanent verwijderd
|
data_removal: Jouw berichten en andere gegevens worden permanent verwijderd
|
||||||
email_change_html: Je kunt <a href="%{path}">je e-mailadres wijzigen</a> zonder dat je jouw account hoeft te verwijderen
|
email_change_html: Je kunt <a href="%{path}">je e-mailadres wijzigen</a> zonder dat je jouw account hoeft te verwijderen
|
||||||
email_contact_html: Wanneer het nog steeds niet aankomt, kun je voor hulp e-mailen naar <a href="mailto:%{email}">%{email}</a>
|
email_contact_html: Wanneer het nog steeds niet aankomt, kun je voor hulp e-mailen naar <a href="mailto:%{email}">%{email}</a>
|
||||||
email_reconfirmation_html: Wanneer je de bevestigingsmail niet hebt ontvangen, kun je deze <a href="%{path}">opnieuw aanvragen</a>
|
email_reconfirmation_html: Wanneer je de bevestigingsmail niet hebt ontvangen, kun je deze <a href="%{path}">opnieuw aanvragen</a>
|
||||||
|
@ -805,7 +805,7 @@ nl:
|
||||||
archive_takeout:
|
archive_takeout:
|
||||||
date: Datum
|
date: Datum
|
||||||
download: Jouw archief downloaden
|
download: Jouw archief downloaden
|
||||||
hint_html: Je kunt een archief opvragen van jouw <strong>toots en geüploade media</strong>. De geëxporteerde gegevens zijn in het ActivityPub-formaat, dat door hiervoor geschikte software valt uit te lezen. Je kunt elke 7 dagen een kopie van je archief aanvragen.
|
hint_html: Je kunt een archief opvragen van jouw <strong>berichten en geüploade media</strong>. De geëxporteerde gegevens zijn in het ActivityPub-formaat, dat door hiervoor geschikte software valt uit te lezen. Je kunt elke 7 dagen een kopie van je archief aanvragen.
|
||||||
in_progress: Jouw archief wordt samengesteld...
|
in_progress: Jouw archief wordt samengesteld...
|
||||||
request: Jouw archief opvragen
|
request: Jouw archief opvragen
|
||||||
size: Omvang
|
size: Omvang
|
||||||
|
@ -820,7 +820,7 @@ nl:
|
||||||
add_new: Nieuwe toevoegen
|
add_new: Nieuwe toevoegen
|
||||||
errors:
|
errors:
|
||||||
limit: Je hebt al het maximaal aantal hashtags uitgelicht
|
limit: Je hebt al het maximaal aantal hashtags uitgelicht
|
||||||
hint_html: "<strong>Wat zijn uitgelichte hashtags?</strong> Deze worden prominent op jouw openbare profiel getoond en stelt mensen in staat om jouw openbare toots per hashtag te bekijken. Het zijn een goed hulpmiddel om creatieve werkzaamheden of langetermijnprojecten bij te houden."
|
hint_html: "<strong>Wat zijn uitgelichte hashtags?</strong> Deze worden prominent op jouw openbare profiel getoond en stelt mensen in staat om jouw openbare berichten per hashtag te bekijken. Het zijn een goed hulpmiddel om creatieve werkzaamheden of langetermijnprojecten bij te houden."
|
||||||
filters:
|
filters:
|
||||||
contexts:
|
contexts:
|
||||||
account: Profielen
|
account: Profielen
|
||||||
|
@ -901,7 +901,7 @@ nl:
|
||||||
limit: Je hebt het maximaal aantal lijsten bereikt
|
limit: Je hebt het maximaal aantal lijsten bereikt
|
||||||
media_attachments:
|
media_attachments:
|
||||||
validations:
|
validations:
|
||||||
images_and_video: Een video kan niet aan een toot met afbeeldingen worden gekoppeld
|
images_and_video: Een video kan niet aan een bericht met afbeeldingen worden gekoppeld
|
||||||
not_ready: Kan geen bestanden toevoegen die nog niet zijn verwerkt. Probeer het later opnieuw!
|
not_ready: Kan geen bestanden toevoegen die nog niet zijn verwerkt. Probeer het later opnieuw!
|
||||||
too_many: Er kunnen niet meer dan 4 afbeeldingen toegevoegd worden
|
too_many: Er kunnen niet meer dan 4 afbeeldingen toegevoegd worden
|
||||||
migrations:
|
migrations:
|
||||||
|
@ -954,8 +954,8 @@ nl:
|
||||||
other: "%{count} nieuwe meldingen sinds jouw laatste bezoek \U0001F418"
|
other: "%{count} nieuwe meldingen sinds jouw laatste bezoek \U0001F418"
|
||||||
title: Tijdens jouw afwezigheid...
|
title: Tijdens jouw afwezigheid...
|
||||||
favourite:
|
favourite:
|
||||||
body: 'Jouw toot werd door %{name} aan hun favorieten toegevoegd:'
|
body: 'Jouw bericht werd door %{name} aan diens favorieten toegevoegd:'
|
||||||
subject: "%{name} voegde jouw toot als favoriet toe"
|
subject: "%{name} voegde jouw bericht als favoriet toe"
|
||||||
title: Nieuwe favoriet
|
title: Nieuwe favoriet
|
||||||
follow:
|
follow:
|
||||||
body: "%{name} volgt jou nu!"
|
body: "%{name} volgt jou nu!"
|
||||||
|
@ -974,11 +974,11 @@ nl:
|
||||||
poll:
|
poll:
|
||||||
subject: Een poll van %{name} is beëindigd
|
subject: Een poll van %{name} is beëindigd
|
||||||
reblog:
|
reblog:
|
||||||
body: 'Jouw toot werd door %{name} geboost:'
|
body: 'Jouw bericht werd door %{name} geboost:'
|
||||||
subject: "%{name} boostte jouw toot"
|
subject: "%{name} boostte jouw bericht"
|
||||||
title: Nieuwe boost
|
title: Nieuwe boost
|
||||||
status:
|
status:
|
||||||
subject: "%{name} heeft zojuist een toot geplaatst"
|
subject: "%{name} heeft zojuist een bericht geplaatst"
|
||||||
notifications:
|
notifications:
|
||||||
email_events: E-mailmeldingen voor gebeurtenissen
|
email_events: E-mailmeldingen voor gebeurtenissen
|
||||||
email_events_hint: 'Selecteer gebeurtenissen waarvoor je meldingen wilt ontvangen:'
|
email_events_hint: 'Selecteer gebeurtenissen waarvoor je meldingen wilt ontvangen:'
|
||||||
|
@ -997,7 +997,7 @@ nl:
|
||||||
code_hint: Voer de code in die door de authenticatie-app werd gegenereerd
|
code_hint: Voer de code in die door de authenticatie-app werd gegenereerd
|
||||||
description_html: Na het instellen van <strong>tweestapsverificatie</strong> met een authenticatie-app, kun je alleen inloggen als je jouw mobiele telefoon bij je hebt. Hiermee genereer je namelijk de in te voeren toegangscode.
|
description_html: Na het instellen van <strong>tweestapsverificatie</strong> met een authenticatie-app, kun je alleen inloggen als je jouw mobiele telefoon bij je hebt. Hiermee genereer je namelijk de in te voeren toegangscode.
|
||||||
enable: Inschakelen
|
enable: Inschakelen
|
||||||
instructions_html: "<strong>Scan deze QR-code in Google Authenticator of een soortgelijke app op jouw mobiele telefoon</strong>. Van nu af aan genereert deze app toegangscodes die je bij het inloggen moet invoeren."
|
instructions_html: "<strong>Scan deze QR-code in Google Authenticator of een soortgelijke app op jouw mobiele telefoon</strong>. Vanaf nu genereert deze app toegangscodes die je bij het inloggen moet invoeren."
|
||||||
manual_instructions: 'Voor het geval je de QR-code niet kunt scannen en het handmatig moet invoeren, vind je hieronder de geheime code in platte tekst:'
|
manual_instructions: 'Voor het geval je de QR-code niet kunt scannen en het handmatig moet invoeren, vind je hieronder de geheime code in platte tekst:'
|
||||||
setup: Instellen
|
setup: Instellen
|
||||||
wrong_code: De ingevoerde code is ongeldig! Klopt de systeemtijd van de server en die van jouw apparaat?
|
wrong_code: De ingevoerde code is ongeldig! Klopt de systeemtijd van de server en die van jouw apparaat?
|
||||||
|
@ -1053,17 +1053,17 @@ nl:
|
||||||
remote_interaction:
|
remote_interaction:
|
||||||
favourite:
|
favourite:
|
||||||
proceed: Doorgaan met toevoegen aan jouw favorieten
|
proceed: Doorgaan met toevoegen aan jouw favorieten
|
||||||
prompt: 'Je wilt de volgende toot aan jouw favorieten toevoegen:'
|
prompt: 'Je wilt het volgende bericht aan jouw favorieten toevoegen:'
|
||||||
reblog:
|
reblog:
|
||||||
proceed: Doorgaan met boosten
|
proceed: Doorgaan met boosten
|
||||||
prompt: 'Je wilt de volgende toot boosten:'
|
prompt: 'Je wilt het volgende bericht boosten:'
|
||||||
reply:
|
reply:
|
||||||
proceed: Doorgaan met reageren
|
proceed: Doorgaan met reageren
|
||||||
prompt: 'Je wilt op de volgende toot reageren:'
|
prompt: 'Je wilt op het volgende bericht reageren:'
|
||||||
scheduled_statuses:
|
scheduled_statuses:
|
||||||
over_daily_limit: Je hebt de limiet van %{limit} in te plannen toots voor die dag overschreden
|
over_daily_limit: Je hebt de limiet van %{limit} in te plannen berichten voor vandaag overschreden
|
||||||
over_total_limit: Je hebt de limiet van %{limit} in te plannen toots overschreden
|
over_total_limit: Je hebt de limiet van %{limit} in te plannen berichten overschreden
|
||||||
too_soon: De datum voor de ingeplande toot moet in de toekomst liggen
|
too_soon: De datum voor het ingeplande bericht moet in de toekomst liggen
|
||||||
sessions:
|
sessions:
|
||||||
activity: Laatst actief
|
activity: Laatst actief
|
||||||
browser: Webbrowser
|
browser: Webbrowser
|
||||||
|
@ -1093,7 +1093,7 @@ nl:
|
||||||
adobe_air: Adobe Air
|
adobe_air: Adobe Air
|
||||||
android: Android
|
android: Android
|
||||||
blackberry: Blackberry
|
blackberry: Blackberry
|
||||||
chrome_os: ChromeOS
|
chrome_os: Chrome OS
|
||||||
firefox_os: Firefox OS
|
firefox_os: Firefox OS
|
||||||
ios: iOS
|
ios: iOS
|
||||||
linux: Linux
|
linux: Linux
|
||||||
|
@ -1144,12 +1144,12 @@ nl:
|
||||||
one: 'bevatte een niet toegestane hashtag: %{tags}'
|
one: 'bevatte een niet toegestane hashtag: %{tags}'
|
||||||
other: 'bevatte niet toegestane hashtags: %{tags}'
|
other: 'bevatte niet toegestane hashtags: %{tags}'
|
||||||
errors:
|
errors:
|
||||||
in_reply_not_found: De toot waarop je probeert te reageren lijkt niet te bestaan.
|
in_reply_not_found: Het bericht waarop je probeert te reageren lijkt niet te bestaan.
|
||||||
open_in_web: In de webapp openen
|
open_in_web: In de webapp openen
|
||||||
over_character_limit: Limiet van %{max} tekens overschreden
|
over_character_limit: Limiet van %{max} tekens overschreden
|
||||||
pin_errors:
|
pin_errors:
|
||||||
limit: Je hebt het maximaal aantal toots al vastgezet
|
limit: Je hebt het maximaal aantal bericht al vastgemaakt
|
||||||
ownership: Een toot van iemand anders kan niet worden vastgezet
|
ownership: Een bericht van iemand anders kan niet worden vastgemaakt
|
||||||
reblog: Een boost kan niet worden vastgezet
|
reblog: Een boost kan niet worden vastgezet
|
||||||
poll:
|
poll:
|
||||||
total_people:
|
total_people:
|
||||||
|
@ -1174,7 +1174,7 @@ nl:
|
||||||
unlisted: Minder openbaar
|
unlisted: Minder openbaar
|
||||||
unlisted_long: Aan iedereen tonen, maar niet op openbare tijdlijnen
|
unlisted_long: Aan iedereen tonen, maar niet op openbare tijdlijnen
|
||||||
stream_entries:
|
stream_entries:
|
||||||
pinned: Vastgemaakte toot
|
pinned: Vastgemaakt bericht
|
||||||
reblogged: boostte
|
reblogged: boostte
|
||||||
sensitive_content: Gevoelige inhoud
|
sensitive_content: Gevoelige inhoud
|
||||||
tags:
|
tags:
|
||||||
|
@ -1332,7 +1332,7 @@ nl:
|
||||||
otp_lost_help_html: Als je toegang tot beiden kwijt bent geraakt, neem dan contact op via %{email}
|
otp_lost_help_html: Als je toegang tot beiden kwijt bent geraakt, neem dan contact op via %{email}
|
||||||
seamless_external_login: Je bent ingelogd via een externe dienst, daarom zijn wachtwoorden en e-mailinstellingen niet beschikbaar.
|
seamless_external_login: Je bent ingelogd via een externe dienst, daarom zijn wachtwoorden en e-mailinstellingen niet beschikbaar.
|
||||||
signed_in_as: 'Ingelogd als:'
|
signed_in_as: 'Ingelogd als:'
|
||||||
suspicious_sign_in_confirmation: Het lijkt er op dat je nog niet eerder op dit apparaat bent ingelogd, en je bent een tijdje niet ingelogd, dus sturen we een beveiligingscode naar je e-mailadres om te bevestigen dat jij het bent.
|
suspicious_sign_in_confirmation: Het lijkt er op dat je nog niet eerder op dit apparaat bent ingelogd, dus sturen we een beveiligingscode naar jouw e-mailadres om te bevestigen dat jij het bent.
|
||||||
verification:
|
verification:
|
||||||
explanation_html: 'Je kunt <strong>jezelf verifiëren als de eigenaar van de links in de metadata van jouw profiel</strong>. Hiervoor moet op de gelinkte website een link terug naar jouw Mastodonprofiel staan. Deze link <strong>moet</strong> het <code>rel="me"</code>-attribuut bevatten. De omschrijving van de link maakt niet uit. Hier is een voorbeeld:'
|
explanation_html: 'Je kunt <strong>jezelf verifiëren als de eigenaar van de links in de metadata van jouw profiel</strong>. Hiervoor moet op de gelinkte website een link terug naar jouw Mastodonprofiel staan. Deze link <strong>moet</strong> het <code>rel="me"</code>-attribuut bevatten. De omschrijving van de link maakt niet uit. Hier is een voorbeeld:'
|
||||||
verification: Verificatie
|
verification: Verificatie
|
||||||
|
|
|
@ -490,6 +490,7 @@ pt-PT:
|
||||||
other: Tentativas em %{count} dias diferentes.
|
other: Tentativas em %{count} dias diferentes.
|
||||||
no_failures_recorded: Sem falhas registadas.
|
no_failures_recorded: Sem falhas registadas.
|
||||||
title: Disponibilidade
|
title: Disponibilidade
|
||||||
|
warning: A última tentativa de conectar a este servidor não foi bem sucedida
|
||||||
back_to_all: Todas
|
back_to_all: Todas
|
||||||
back_to_limited: Limitadas
|
back_to_limited: Limitadas
|
||||||
back_to_warning: Aviso
|
back_to_warning: Aviso
|
||||||
|
|
|
@ -480,6 +480,7 @@ ru:
|
||||||
availability:
|
availability:
|
||||||
no_failures_recorded: Сбоев в записи нет.
|
no_failures_recorded: Сбоев в записи нет.
|
||||||
title: Доступность
|
title: Доступность
|
||||||
|
warning: Последняя попытка подключения к этому серверу не удалась
|
||||||
back_to_all: Все узлы
|
back_to_all: Все узлы
|
||||||
back_to_limited: Все ограниченные узлы
|
back_to_limited: Все ограниченные узлы
|
||||||
back_to_warning: Все узлы требующие внимания
|
back_to_warning: Все узлы требующие внимания
|
||||||
|
@ -742,6 +743,7 @@ ru:
|
||||||
none: "%{name} отправил(а) предупреждение %{target}"
|
none: "%{name} отправил(а) предупреждение %{target}"
|
||||||
sensitive: "%{name} отметил(а) учетную запись %{target} как деликатную"
|
sensitive: "%{name} отметил(а) учетную запись %{target} как деликатную"
|
||||||
silence: "%{name} ограничил(а) учетную запись %{target}"
|
silence: "%{name} ограничил(а) учетную запись %{target}"
|
||||||
|
appeal_approved: Обжаловано
|
||||||
appeal_pending: Обжалование в обработке
|
appeal_pending: Обжалование в обработке
|
||||||
system_checks:
|
system_checks:
|
||||||
database_schema_check:
|
database_schema_check:
|
||||||
|
@ -806,6 +808,8 @@ ru:
|
||||||
empty: Вы еще не определили пресеты предупреждений.
|
empty: Вы еще не определили пресеты предупреждений.
|
||||||
title: Управление шаблонами предупреждений
|
title: Управление шаблонами предупреждений
|
||||||
admin_mailer:
|
admin_mailer:
|
||||||
|
new_appeal:
|
||||||
|
subject: "%{username} обжалует решение модерации на %{instance}"
|
||||||
new_pending_account:
|
new_pending_account:
|
||||||
body: Ниже указана информация учётной записи. Вы можете одобрить или отклонить заявку.
|
body: Ниже указана информация учётной записи. Вы можете одобрить или отклонить заявку.
|
||||||
subject: Новая учётная запись для рассмотрения на %{instance} (%{username})
|
subject: Новая учётная запись для рассмотрения на %{instance} (%{username})
|
||||||
|
|
|
@ -27,6 +27,8 @@ fa:
|
||||||
scheduled_at: برای انتشار فوری اعلامیه، خالی بگذارید
|
scheduled_at: برای انتشار فوری اعلامیه، خالی بگذارید
|
||||||
starts_at: اختیاری. در صورتی که اعلامیهتان محدود به بازهٔ زمانی خاصی است
|
starts_at: اختیاری. در صورتی که اعلامیهتان محدود به بازهٔ زمانی خاصی است
|
||||||
text: میتوانید مانند یک بوق معمولی بنویسید. یادتان باشد که اعلامیهٔ شما فضای صفحهٔ کاربران را اشغال خواهد کرد
|
text: میتوانید مانند یک بوق معمولی بنویسید. یادتان باشد که اعلامیهٔ شما فضای صفحهٔ کاربران را اشغال خواهد کرد
|
||||||
|
appeal:
|
||||||
|
text: فقط یک بار میتوانید برای اخطار اعتراض کنید
|
||||||
defaults:
|
defaults:
|
||||||
autofollow: کسانی که از راه دعوتنامه عضو میشوند به طور خودکار پیگیر شما خواهند شد
|
autofollow: کسانی که از راه دعوتنامه عضو میشوند به طور خودکار پیگیر شما خواهند شد
|
||||||
avatar: یکی از قالبهای PNG یا GIF یا JPG. بیشترین اندازه %{size}. تصویر به اندازهٔ %{dimensions} پیکسل تبدیل خواهد شد
|
avatar: یکی از قالبهای PNG یا GIF یا JPG. بیشترین اندازه %{size}. تصویر به اندازهٔ %{dimensions} پیکسل تبدیل خواهد شد
|
||||||
|
@ -35,6 +37,7 @@ fa:
|
||||||
current_password: به دلایل امنیتی لطفاً رمز این حساب را وارد کنید
|
current_password: به دلایل امنیتی لطفاً رمز این حساب را وارد کنید
|
||||||
current_username: برای تأیید، لطفاً نام کاربری حساب فعلی را وارد کنید
|
current_username: برای تأیید، لطفاً نام کاربری حساب فعلی را وارد کنید
|
||||||
digest: تنها وقتی فرستاده میشود که مدتی طولانی فعالیتی نداشته باشید و در این مدت برای شما پیغام خصوصیای نوشته شده باشد
|
digest: تنها وقتی فرستاده میشود که مدتی طولانی فعالیتی نداشته باشید و در این مدت برای شما پیغام خصوصیای نوشته شده باشد
|
||||||
|
discoverable: اجازه دهید حسابتان از طریق پیشنهادها، پرطرفدارها و سایر قابلیتها، توسط افراد غریبه قابل کشف باشد
|
||||||
email: به شما ایمیل تأییدی فرستاده خواهد شد
|
email: به شما ایمیل تأییدی فرستاده خواهد شد
|
||||||
fields: شما میتوانید تا چهار مورد را در یک جدول در نمایهٔ خود نمایش دهید
|
fields: شما میتوانید تا چهار مورد را در یک جدول در نمایهٔ خود نمایش دهید
|
||||||
header: یکی از قالبهای PNG یا GIF یا JPG. بیشترین اندازه %{size}. تصویر به اندازهٔ %{dimensions} پیکسل تبدیل خواهد شد
|
header: یکی از قالبهای PNG یا GIF یا JPG. بیشترین اندازه %{size}. تصویر به اندازهٔ %{dimensions} پیکسل تبدیل خواهد شد
|
||||||
|
@ -60,6 +63,7 @@ fa:
|
||||||
domain_allow:
|
domain_allow:
|
||||||
domain: این دامین خواهد توانست دادهها از این سرور را دریافت کند و دادههای از این دامین در اینجا پردازش و ذخیره خواهند شد
|
domain: این دامین خواهد توانست دادهها از این سرور را دریافت کند و دادههای از این دامین در اینجا پردازش و ذخیره خواهند شد
|
||||||
email_domain_block:
|
email_domain_block:
|
||||||
|
domain: این میتواند نام دامنهای باشد که در نشانی رایانامه یا رکورد MX استفاده میشود. پس از ثبت نام بررسی خواهند شد.
|
||||||
with_dns_records: تلاشی برای resolve کردن رکوردهای ساناد دامنهٔ دادهشده انجام شده و نتیجه نیز مسدود خواهد شد
|
with_dns_records: تلاشی برای resolve کردن رکوردهای ساناد دامنهٔ دادهشده انجام شده و نتیجه نیز مسدود خواهد شد
|
||||||
featured_tag:
|
featured_tag:
|
||||||
name: 'شاید بخواهید چنین چیزهایی را به کار ببرید:'
|
name: 'شاید بخواهید چنین چیزهایی را به کار ببرید:'
|
||||||
|
@ -116,6 +120,8 @@ fa:
|
||||||
scheduled_at: زمانبندی انتشار
|
scheduled_at: زمانبندی انتشار
|
||||||
starts_at: آغاز رویداد
|
starts_at: آغاز رویداد
|
||||||
text: اعلامیه
|
text: اعلامیه
|
||||||
|
appeal:
|
||||||
|
text: توضیح دهید که چرا این تصمیم باید معکوس شود
|
||||||
defaults:
|
defaults:
|
||||||
autofollow: دعوت از دیگران برای عضو شدن و پیگیری حساب شما
|
autofollow: دعوت از دیگران برای عضو شدن و پیگیری حساب شما
|
||||||
avatar: تصویر نمایه
|
avatar: تصویر نمایه
|
||||||
|
@ -194,6 +200,7 @@ fa:
|
||||||
sign_up_requires_approval: محدود کردن ثبت نامها
|
sign_up_requires_approval: محدود کردن ثبت نامها
|
||||||
severity: قانون
|
severity: قانون
|
||||||
notification_emails:
|
notification_emails:
|
||||||
|
appeal: شخصی به تصمیم ناظر اعتراض کرد
|
||||||
digest: فرستادن رایانامههای خلاصه
|
digest: فرستادن رایانامههای خلاصه
|
||||||
favourite: وقتی کسی نوشتهٔ شما را پسندید ایمیل بفرست
|
favourite: وقتی کسی نوشتهٔ شما را پسندید ایمیل بفرست
|
||||||
follow: وقتی کسی پیگیر شما شد ایمیل بفرست
|
follow: وقتی کسی پیگیر شما شد ایمیل بفرست
|
||||||
|
@ -201,6 +208,8 @@ fa:
|
||||||
mention: وقتی کسی از شما نام برد ایمیل بفرست
|
mention: وقتی کسی از شما نام برد ایمیل بفرست
|
||||||
pending_account: وقتی حساب تازهای نیاز به بازبینی داشت ایمیل بفرست
|
pending_account: وقتی حساب تازهای نیاز به بازبینی داشت ایمیل بفرست
|
||||||
reblog: وقتی کسی نوشتهٔ شما را بازبوقید ایمیل بفرست
|
reblog: وقتی کسی نوشتهٔ شما را بازبوقید ایمیل بفرست
|
||||||
|
report: گزارش جدیدی فرستاده شد
|
||||||
|
trending_tag: روند جدیدی نیازمند بازبینی است
|
||||||
rule:
|
rule:
|
||||||
text: قانون
|
text: قانون
|
||||||
tag:
|
tag:
|
||||||
|
|
|
@ -144,7 +144,7 @@ ku:
|
||||||
inbox_url: URLya guhêzkera wergirtî
|
inbox_url: URLya guhêzkera wergirtî
|
||||||
irreversible: Li şûna veşartinê jê bibe
|
irreversible: Li şûna veşartinê jê bibe
|
||||||
locale: Zimanê navrûyê
|
locale: Zimanê navrûyê
|
||||||
locked: Ajimêr qefl bike
|
locked: Ajimêr kilît bike
|
||||||
max_uses: Hejmara bikaranîna herî zêde
|
max_uses: Hejmara bikaranîna herî zêde
|
||||||
new_password: Pêborîna nû
|
new_password: Pêborîna nû
|
||||||
note: Jiyanname
|
note: Jiyanname
|
||||||
|
|
|
@ -7,18 +7,18 @@ nl:
|
||||||
account_migration:
|
account_migration:
|
||||||
acct: Vul de gebruikersnaam@domein van het account in, waarnaartoe je wilt verhuizen
|
acct: Vul de gebruikersnaam@domein van het account in, waarnaartoe je wilt verhuizen
|
||||||
account_warning_preset:
|
account_warning_preset:
|
||||||
text: Je kunt voor toots specifieke tekst gebruiken, zoals URL's, hashtags en vermeldingen
|
text: Je kunt specifieke tekst voor berichten gebruiken, zoals URL's, hashtags en vermeldingen
|
||||||
title: Optioneel. Niet zichtbaar voor de ontvanger
|
title: Optioneel. Niet zichtbaar voor de ontvanger
|
||||||
admin_account_action:
|
admin_account_action:
|
||||||
include_statuses: De gebruiker ziet welke toots verantwoordelijk zijn voor de moderatieactie of waarschuwing
|
include_statuses: De gebruiker ziet welke berichten verantwoordelijk zijn voor de moderatieactie of waarschuwing
|
||||||
send_email_notification: De gebruiker ontvangt een uitleg over wat er met hun account is gebeurd
|
send_email_notification: De gebruiker ontvangt een uitleg over wat er met diens account is gebeurd
|
||||||
text_html: Optioneel. Je kunt voor toots specifieke tekst gebruiken. Om tijd te besparen kun je <a href="%{path}">presets voor waarschuwingen toevoegen</a>
|
text_html: Optioneel. Je kunt specifieke tekst voor berichten gebruiken. Om tijd te besparen kun je <a href="%{path}">presets voor waarschuwingen toevoegen</a>
|
||||||
type_html: Kies wat er met <strong>%{acct}</strong> moet gebeuren
|
type_html: Kies wat er met <strong>%{acct}</strong> moet gebeuren
|
||||||
types:
|
types:
|
||||||
disable: Voorkom dat de gebruiker hun account gebruikt, maar verwijder of verberg de inhoud niet.
|
disable: Voorkom dat de gebruiker diens account gebruikt, maar verwijder of verberg de inhoud niet.
|
||||||
none: Gebruik dit om een waarschuwing naar de gebruiker te sturen, zonder dat nog een andere actie wordt uitgevoerd.
|
none: Gebruik dit om een waarschuwing naar de gebruiker te sturen, zonder dat nog een andere actie wordt uitgevoerd.
|
||||||
sensitive: Forceer dat alle mediabijlagen van deze gebruiker als gevoelig worden gemarkeerd.
|
sensitive: Forceer dat alle mediabijlagen van deze gebruiker als gevoelig worden gemarkeerd.
|
||||||
silence: Voorkom dat de gebruiker openbare toots kan versturen, verberg hun toots en meldingen voor mensen die hen niet volgen.
|
silence: Voorkom dat de gebruiker openbare berichten kan versturen, verberg diens berichten en meldingen voor mensen die diegene niet volgen.
|
||||||
suspend: Alle interacties van en met dit account blokkeren en de inhoud verwijderen. Dit kan binnen dertig dagen worden teruggedraaid.
|
suspend: Alle interacties van en met dit account blokkeren en de inhoud verwijderen. Dit kan binnen dertig dagen worden teruggedraaid.
|
||||||
warning_preset_id: Optioneel. Je kunt nog steeds handmatig tekst toevoegen aan het eind van de voorinstelling
|
warning_preset_id: Optioneel. Je kunt nog steeds handmatig tekst toevoegen aan het eind van de voorinstelling
|
||||||
announcement:
|
announcement:
|
||||||
|
@ -26,7 +26,7 @@ nl:
|
||||||
ends_at: Optioneel. De publicatie van de mededeling wordt op dit tijdstip automatisch beëindigd
|
ends_at: Optioneel. De publicatie van de mededeling wordt op dit tijdstip automatisch beëindigd
|
||||||
scheduled_at: Laat leeg om de mededeling meteen te publiceren
|
scheduled_at: Laat leeg om de mededeling meteen te publiceren
|
||||||
starts_at: Optioneel. In het geval dat jouw mededeling aan een bepaald tijdvak is gebonden
|
starts_at: Optioneel. In het geval dat jouw mededeling aan een bepaald tijdvak is gebonden
|
||||||
text: Je kunt voor toots specifieke tekst gebruiken. Let op de ruimte die de mededeling op het scherm van de gebruiker inneemt
|
text: Je kunt specifieke tekst voor berichten gebruiken. Let op de ruimte die de mededeling op het scherm van de gebruiker inneemt
|
||||||
defaults:
|
defaults:
|
||||||
autofollow: Mensen die zich via de uitnodiging hebben geregistreerd, volgen jou automatisch
|
autofollow: Mensen die zich via de uitnodiging hebben geregistreerd, volgen jou automatisch
|
||||||
avatar: PNG, GIF of JPG. Maximaal %{size}. Wordt teruggeschaald naar %{dimensions}px
|
avatar: PNG, GIF of JPG. Maximaal %{size}. Wordt teruggeschaald naar %{dimensions}px
|
||||||
|
@ -39,20 +39,20 @@ nl:
|
||||||
fields: Je kan maximaal 4 items als een tabel op je profiel weergeven
|
fields: Je kan maximaal 4 items als een tabel op je profiel weergeven
|
||||||
header: PNG, GIF of JPG. Maximaal %{size}. Wordt teruggeschaald naar %{dimensions}px
|
header: PNG, GIF of JPG. Maximaal %{size}. Wordt teruggeschaald naar %{dimensions}px
|
||||||
inbox_url: Kopieer de URL van de voorpagina van de relayserver die je wil gebruiken
|
inbox_url: Kopieer de URL van de voorpagina van de relayserver die je wil gebruiken
|
||||||
irreversible: Gefilterde toots verdwijnen onomkeerbaar, zelfs als de filter later wordt verwijderd
|
irreversible: Gefilterde berichten verdwijnen onomkeerbaar, zelfs als de filter later wordt verwijderd
|
||||||
locale: De taal van de gebruikersomgeving, e-mails en pushmeldingen
|
locale: De taal van de gebruikersomgeving, e-mails en pushmeldingen
|
||||||
locked: Door het goedkeuren van volgers handmatig bepalen wie jou mag volgen
|
locked: Door het goedkeuren van volgers handmatig bepalen wie jou mag volgen
|
||||||
password: Gebruik tenminste 8 tekens
|
password: Gebruik tenminste 8 tekens
|
||||||
phrase: Komt overeen ongeacht hoofd-/kleine letters of een inhoudswaarschuwing
|
phrase: Komt overeen ongeacht hoofd-/kleine letters of een inhoudswaarschuwing
|
||||||
scopes: Tot welke API's heeft de toepassing toegang. Wanneer je een toestemming van het bovenste niveau kiest, hoef je geen individuele toestemmingen meer te kiezen.
|
scopes: Tot welke API's heeft de toepassing toegang. Wanneer je een toestemming van het bovenste niveau kiest, hoef je geen individuele toestemmingen meer te kiezen.
|
||||||
setting_aggregate_reblogs: Geen nieuwe boosts tonen voor toots die recentelijk nog zijn geboost (heeft alleen effect op nieuw ontvangen boosts)
|
setting_aggregate_reblogs: Geen nieuwe boosts tonen voor berichten die recentelijk nog zijn geboost (heeft alleen effect op nieuw ontvangen boosts)
|
||||||
setting_default_sensitive: Gevoelige media wordt standaard verborgen en kan met één klik worden getoond
|
setting_default_sensitive: Gevoelige media wordt standaard verborgen en kan met één klik worden getoond
|
||||||
setting_display_media_default: Als gevoelig gemarkeerde media verbergen
|
setting_display_media_default: Als gevoelig gemarkeerde media verbergen
|
||||||
setting_display_media_hide_all: Media altijd verbergen
|
setting_display_media_hide_all: Media altijd verbergen
|
||||||
setting_display_media_show_all: Media altijd tonen
|
setting_display_media_show_all: Media altijd tonen
|
||||||
setting_hide_network: Wie jij volgt en wie jou volgen wordt niet op jouw profiel getoond
|
setting_hide_network: Wie jij volgt en wie jou volgen wordt niet op jouw profiel getoond
|
||||||
setting_noindex: Heeft invloed op jouw openbare profiel en toots
|
setting_noindex: Heeft invloed op jouw openbare profiel en pagina's met berichten
|
||||||
setting_show_application: De toepassing de je gebruikt om te tooten wordt in de gedetailleerde weergave van de toot getoond
|
setting_show_application: De toepassing de je gebruikt om berichten te plaatsen wordt in de gedetailleerde weergave van het bericht getoond
|
||||||
setting_use_blurhash: Wazige kleurovergangen zijn gebaseerd op de kleuren van de verborgen media, waarmee elk detail verdwijnt
|
setting_use_blurhash: Wazige kleurovergangen zijn gebaseerd op de kleuren van de verborgen media, waarmee elk detail verdwijnt
|
||||||
setting_use_pending_items: De tijdlijn wordt bijgewerkt door op het aantal nieuwe items te klikken, in plaats van dat deze automatisch wordt bijgewerkt
|
setting_use_pending_items: De tijdlijn wordt bijgewerkt door op het aantal nieuwe items te klikken, in plaats van dat deze automatisch wordt bijgewerkt
|
||||||
username: Jouw gebruikersnaam is uniek op %{domain}
|
username: Jouw gebruikersnaam is uniek op %{domain}
|
||||||
|
@ -85,7 +85,7 @@ nl:
|
||||||
tag:
|
tag:
|
||||||
name: Je kunt elk woord met een hoofdletter beginnen, om zo bijvoorbeeld de tekst leesbaarder te maken
|
name: Je kunt elk woord met een hoofdletter beginnen, om zo bijvoorbeeld de tekst leesbaarder te maken
|
||||||
user:
|
user:
|
||||||
chosen_languages: Alleen toots in de aangevinkte talen worden op de openbare tijdlijnen getoond
|
chosen_languages: Alleen berichten in de aangevinkte talen worden op de openbare tijdlijnen getoond
|
||||||
labels:
|
labels:
|
||||||
account:
|
account:
|
||||||
fields:
|
fields:
|
||||||
|
@ -99,7 +99,7 @@ nl:
|
||||||
text: Tekst van preset
|
text: Tekst van preset
|
||||||
title: Titel
|
title: Titel
|
||||||
admin_account_action:
|
admin_account_action:
|
||||||
include_statuses: Gerapporteerde toots aan de e-mail toevoegen
|
include_statuses: Gerapporteerde berichten aan de e-mail toevoegen
|
||||||
send_email_notification: Meld dit per e-mail aan de gebruiker
|
send_email_notification: Meld dit per e-mail aan de gebruiker
|
||||||
text: Aangepaste waarschuwing
|
text: Aangepaste waarschuwing
|
||||||
type: Actie
|
type: Actie
|
||||||
|
@ -146,22 +146,22 @@ nl:
|
||||||
setting_advanced_layout: Geavanceerde webomgeving inschakelen
|
setting_advanced_layout: Geavanceerde webomgeving inschakelen
|
||||||
setting_aggregate_reblogs: Boosts in tijdlijnen groeperen
|
setting_aggregate_reblogs: Boosts in tijdlijnen groeperen
|
||||||
setting_auto_play_gif: Speel geanimeerde GIF's automatisch af
|
setting_auto_play_gif: Speel geanimeerde GIF's automatisch af
|
||||||
setting_boost_modal: Vraag voor het boosten van een toot een bevestiging
|
setting_boost_modal: Vraag voor het boosten van een bericht een bevestiging
|
||||||
setting_crop_images: Afbeeldingen bijsnijden tot 16x9 in toots op tijdlijnen
|
setting_crop_images: Afbeeldingen bijsnijden tot 16x9 in berichten op tijdlijnen
|
||||||
setting_default_language: Taal van jouw toots
|
setting_default_language: Taal van jouw berichten
|
||||||
setting_default_privacy: Standaardzichtbaarheid van jouw toots
|
setting_default_privacy: Zichtbaarheid van nieuwe berichten
|
||||||
setting_default_sensitive: Media altijd als gevoelig markeren
|
setting_default_sensitive: Media altijd als gevoelig markeren
|
||||||
setting_delete_modal: Vraag voor het verwijderen van een toot een bevestiging
|
setting_delete_modal: Vraag voor het verwijderen van een bericht een bevestiging
|
||||||
setting_disable_swiping: Swipebewegingen uitschakelen
|
setting_disable_swiping: Swipebewegingen uitschakelen
|
||||||
setting_display_media: Mediaweergave
|
setting_display_media: Mediaweergave
|
||||||
setting_display_media_default: Standaard
|
setting_display_media_default: Standaard
|
||||||
setting_display_media_hide_all: Alles verbergen
|
setting_display_media_hide_all: Alles verbergen
|
||||||
setting_display_media_show_all: Alles tonen
|
setting_display_media_show_all: Alles tonen
|
||||||
setting_expand_spoilers: Altijd toots met inhoudswaarschuwingen uitklappen
|
setting_expand_spoilers: Altijd berichten met inhoudswaarschuwingen uitklappen
|
||||||
setting_hide_network: Jouw volgers en wie je volgt verbergen
|
setting_hide_network: Jouw volgers en wie je volgt verbergen
|
||||||
setting_noindex: Jouw toots niet door zoekmachines laten indexeren
|
setting_noindex: Jouw berichten niet door zoekmachines laten indexeren
|
||||||
setting_reduce_motion: Langzamere animaties
|
setting_reduce_motion: Langzamere animaties
|
||||||
setting_show_application: Toepassing onthullen die je voor het verzenden van toots gebruikt
|
setting_show_application: Toepassing onthullen die je voor het verzenden van berichten gebruikt
|
||||||
setting_system_font_ui: Standaardlettertype van jouw systeem gebruiken
|
setting_system_font_ui: Standaardlettertype van jouw systeem gebruiken
|
||||||
setting_theme: Thema website
|
setting_theme: Thema website
|
||||||
setting_trends: Trends van vandaag tonen
|
setting_trends: Trends van vandaag tonen
|
||||||
|
@ -195,19 +195,19 @@ nl:
|
||||||
severity: Regel
|
severity: Regel
|
||||||
notification_emails:
|
notification_emails:
|
||||||
digest: Periodiek e-mails met een samenvatting versturen
|
digest: Periodiek e-mails met een samenvatting versturen
|
||||||
favourite: Wanneer iemand jouw toot aan hun favorieten heeft toegevoegd
|
favourite: Wanneer iemand jouw bericht aan diens favorieten heeft toegevoegd
|
||||||
follow: Wanneer iemand jou is gaan volgen
|
follow: Wanneer iemand jou is gaan volgen
|
||||||
follow_request: Wanneer iemand jou wil volgen
|
follow_request: Wanneer iemand jou wil volgen
|
||||||
mention: Wanneer iemand jou heeft vermeld
|
mention: Wanneer iemand jou heeft vermeld
|
||||||
pending_account: Wanneer een nieuw account moet worden beoordeeld
|
pending_account: Wanneer een nieuw account moet worden beoordeeld
|
||||||
reblog: Wanneer iemand jouw toot heeft geboost
|
reblog: Wanneer iemand jouw bericht heeft geboost
|
||||||
rule:
|
rule:
|
||||||
text: Regel
|
text: Regel
|
||||||
tag:
|
tag:
|
||||||
listable: Toestaan dat deze hashtag in zoekopdrachten en aanbevelingen te zien valt
|
listable: Toestaan dat deze hashtag in zoekopdrachten en aanbevelingen te zien valt
|
||||||
name: Hashtag
|
name: Hashtag
|
||||||
trendable: Toestaan dat deze hashtag onder trends te zien valt
|
trendable: Toestaan dat deze hashtag onder trends te zien valt
|
||||||
usable: Toestaan dat deze hashtag in toots gebruikt mag worden
|
usable: Toestaan dat deze hashtag in berichten gebruikt mag worden
|
||||||
'no': Nee
|
'no': Nee
|
||||||
recommended: Aanbevolen
|
recommended: Aanbevolen
|
||||||
required:
|
required:
|
||||||
|
|
|
@ -74,7 +74,7 @@ th:
|
||||||
text: นี่จะช่วยให้เราตรวจทานใบสมัครของคุณ
|
text: นี่จะช่วยให้เราตรวจทานใบสมัครของคุณ
|
||||||
ip_block:
|
ip_block:
|
||||||
comment: ไม่จำเป็น จดจำเหตุผลที่คุณเพิ่มกฎนี้
|
comment: ไม่จำเป็น จดจำเหตุผลที่คุณเพิ่มกฎนี้
|
||||||
ip: ป้อนที่อยู่ IPv4 หรือ IPv6 คุณสามารถปิดกั้นทั้งช่วงได้โดยใช้ไวยากรณ์ CIDR ระวังอย่าล็อคตัวเองออก!
|
ip: ป้อนที่อยู่ IPv4 หรือ IPv6 คุณสามารถปิดกั้นทั้งช่วงได้โดยใช้ไวยากรณ์ CIDR ระวังอย่าล็อคตัวคุณเองออก!
|
||||||
severities:
|
severities:
|
||||||
no_access: ปิดกั้นการเข้าถึงทรัพยากรทั้งหมด
|
no_access: ปิดกั้นการเข้าถึงทรัพยากรทั้งหมด
|
||||||
sign_up_requires_approval: การลงทะเบียนใหม่จะต้องมีการอนุมัติของคุณ
|
sign_up_requires_approval: การลงทะเบียนใหม่จะต้องมีการอนุมัติของคุณ
|
||||||
|
|
|
@ -404,6 +404,8 @@ sv:
|
||||||
status: Status
|
status: Status
|
||||||
title: Följ rekommendationer
|
title: Följ rekommendationer
|
||||||
instances:
|
instances:
|
||||||
|
availability:
|
||||||
|
warning: Det senaste försöket att ansluta till denna värddator har misslyckats
|
||||||
back_to_all: Alla
|
back_to_all: Alla
|
||||||
back_to_limited: Begränsat
|
back_to_limited: Begränsat
|
||||||
back_to_warning: Varning
|
back_to_warning: Varning
|
||||||
|
|
|
@ -556,6 +556,9 @@ th:
|
||||||
other: "%{count} หมายเหตุ"
|
other: "%{count} หมายเหตุ"
|
||||||
action_log: รายการบันทึกการตรวจสอบ
|
action_log: รายการบันทึกการตรวจสอบ
|
||||||
action_taken_by: ใช้การกระทำโดย
|
action_taken_by: ใช้การกระทำโดย
|
||||||
|
actions:
|
||||||
|
resolve_description_html: จะไม่ใช้การกระทำกับบัญชีที่รายงาน ไม่มีการบันทึกการดำเนินการ และจะปิดรายงาน
|
||||||
|
actions_description_html: ตัดสินใจว่าการกระทำใดที่จะใช้เพื่อแก้ปัญหารายงานนี้ หากคุณใช้การกระทำที่เป็นการลงโทษกับบัญชีที่รายงาน จะส่งการแจ้งเตือนอีเมลถึงเขา ยกเว้นเมื่อมีการเลือกหมวดหมู่ <strong>สแปม</strong>
|
||||||
are_you_sure: คุณแน่ใจหรือไม่?
|
are_you_sure: คุณแน่ใจหรือไม่?
|
||||||
assign_to_self: มอบหมายให้ฉัน
|
assign_to_self: มอบหมายให้ฉัน
|
||||||
assigned: ผู้ควบคุมที่ได้รับมอบหมาย
|
assigned: ผู้ควบคุมที่ได้รับมอบหมาย
|
||||||
|
@ -883,6 +886,7 @@ th:
|
||||||
confirming: กำลังรอการยืนยันอีเมลให้เสร็จสมบูรณ์
|
confirming: กำลังรอการยืนยันอีเมลให้เสร็จสมบูรณ์
|
||||||
functional: บัญชีของคุณทำงานได้อย่างเต็มที่
|
functional: บัญชีของคุณทำงานได้อย่างเต็มที่
|
||||||
pending: ใบสมัครของคุณกำลังรอดำเนินการตรวจทานโดยพนักงานของเรา นี่อาจใช้เวลาสักครู่ คุณจะได้รับอีเมลหากใบสมัครของคุณได้รับการอนุมัติ
|
pending: ใบสมัครของคุณกำลังรอดำเนินการตรวจทานโดยพนักงานของเรา นี่อาจใช้เวลาสักครู่ คุณจะได้รับอีเมลหากใบสมัครของคุณได้รับการอนุมัติ
|
||||||
|
view_strikes: ดูการดำเนินการที่ผ่านมากับบัญชีของคุณ
|
||||||
too_fast: ส่งแบบฟอร์มเร็วเกินไป ลองอีกครั้ง
|
too_fast: ส่งแบบฟอร์มเร็วเกินไป ลองอีกครั้ง
|
||||||
trouble_logging_in: มีปัญหาในการเข้าสู่ระบบ?
|
trouble_logging_in: มีปัญหาในการเข้าสู่ระบบ?
|
||||||
use_security_key: ใช้กุญแจความปลอดภัย
|
use_security_key: ใช้กุญแจความปลอดภัย
|
||||||
|
@ -954,6 +958,7 @@ th:
|
||||||
submit: ส่งการอุทธรณ์
|
submit: ส่งการอุทธรณ์
|
||||||
associated_report: รายงานที่เกี่ยวข้อง
|
associated_report: รายงานที่เกี่ยวข้อง
|
||||||
created_at: ลงวันที่
|
created_at: ลงวันที่
|
||||||
|
description_html: นี่คือการกระทำที่ใช้กับบัญชีของคุณและคำเตือนที่ส่งถึงคุณโดยพนักงานของ %{instance}
|
||||||
recipient: ส่งถึง
|
recipient: ส่งถึง
|
||||||
status: 'โพสต์ #%{id}'
|
status: 'โพสต์ #%{id}'
|
||||||
title: "%{action} จาก %{date}"
|
title: "%{action} จาก %{date}"
|
||||||
|
@ -1410,9 +1415,11 @@ th:
|
||||||
user_mailer:
|
user_mailer:
|
||||||
appeal_approved:
|
appeal_approved:
|
||||||
action: ไปยังบัญชีของคุณ
|
action: ไปยังบัญชีของคุณ
|
||||||
|
explanation: อนุมัติการอุทธรณ์การดำเนินการกับบัญชีของคุณเมื่อ %{strike_date} ที่คุณได้ส่งเมื่อ %{appeal_date} แล้ว บัญชีของคุณอยู่ในสถานะที่ดีอีกครั้งหนึ่ง
|
||||||
subject: อนุมัติการอุทธรณ์ของคุณจาก %{date} แล้ว
|
subject: อนุมัติการอุทธรณ์ของคุณจาก %{date} แล้ว
|
||||||
title: อนุมัติการอุทธรณ์แล้ว
|
title: อนุมัติการอุทธรณ์แล้ว
|
||||||
appeal_rejected:
|
appeal_rejected:
|
||||||
|
explanation: ปฏิเสธการอุทธรณ์การดำเนินการกับบัญชีของคุณเมื่อ %{strike_date} ที่คุณได้ส่งเมื่อ %{appeal_date} แล้ว
|
||||||
subject: ปฏิเสธการอุทธรณ์ของคุณจาก %{date} แล้ว
|
subject: ปฏิเสธการอุทธรณ์ของคุณจาก %{date} แล้ว
|
||||||
title: ปฏิเสธการอุทธรณ์แล้ว
|
title: ปฏิเสธการอุทธรณ์แล้ว
|
||||||
backup_ready:
|
backup_ready:
|
||||||
|
@ -1431,6 +1438,12 @@ th:
|
||||||
categories:
|
categories:
|
||||||
spam: สแปม
|
spam: สแปม
|
||||||
violation: เนื้อหาละเมิดหลักเกณฑ์ชุมชนดังต่อไปนี้
|
violation: เนื้อหาละเมิดหลักเกณฑ์ชุมชนดังต่อไปนี้
|
||||||
|
explanation:
|
||||||
|
delete_statuses: มีการพบว่าบางโพสต์ของคุณละเมิดหนึ่งหลักเกณฑ์ชุมชนหรือมากกว่าและได้รับการเอาออกโดยผู้ควบคุมของ %{instance} ในเวลาต่อมา
|
||||||
|
disable: คุณไม่สามารถใช้บัญชีของคุณได้อีกต่อไป แต่โปรไฟล์และข้อมูลอื่น ๆ ของคุณยังคงอยู่ในสภาพเดิม คุณสามารถขอข้อมูลสำรองของข้อมูลของคุณ เปลี่ยนการตั้งค่าบัญชี หรือลบบัญชีของคุณ
|
||||||
|
mark_statuses_as_sensitive: ทำเครื่องหมายบางโพสต์ของคุณว่าละเอียดอ่อนโดยผู้ควบคุมของ %{instance} แล้ว นี่หมายความว่าผู้คนจะต้องแตะสื่อในโพสต์ก่อนที่จะแสดงตัวอย่าง คุณสามารถทำเครื่องหมายสื่อว่าละเอียดอ่อนด้วยตัวคุณเองเมื่อโพสต์ในอนาคต
|
||||||
|
sensitive: จากนี้ไป จะทำเครื่องหมายไฟล์สื่อที่อัปโหลดทั้งหมดของคุณว่าละเอียดอ่อนและซ่อนอยู่หลังการคลิกไปยังคำเตือน
|
||||||
|
silence: คุณยังคงสามารถใช้บัญชีของคุณแต่เฉพาะผู้คนที่กำลังติดตามคุณอยู่แล้วเท่านั้นที่จะเห็นโพสต์ของคุณในเซิร์ฟเวอร์นี้ และอาจแยกคุณออกจากคุณลักษณะการค้นพบต่าง ๆ อย่างไรก็ตาม ผู้อื่นอาจยังติดตามคุณด้วยตนเอง
|
||||||
reason: 'เหตุผล:'
|
reason: 'เหตุผล:'
|
||||||
statuses: 'โพสต์ที่อ้างถึง:'
|
statuses: 'โพสต์ที่อ้างถึง:'
|
||||||
subject:
|
subject:
|
||||||
|
|
|
@ -490,6 +490,7 @@ tr:
|
||||||
other: "%{count} farklı gün başarısız girişim."
|
other: "%{count} farklı gün başarısız girişim."
|
||||||
no_failures_recorded: Kayıtlı başarısızlık yok.
|
no_failures_recorded: Kayıtlı başarısızlık yok.
|
||||||
title: Ulaşılabilirlik
|
title: Ulaşılabilirlik
|
||||||
|
warning: Bu sunucuya önceki bağlanma denemesi başarısız olmuştu
|
||||||
back_to_all: Tümü
|
back_to_all: Tümü
|
||||||
back_to_limited: Sınırlı
|
back_to_limited: Sınırlı
|
||||||
back_to_warning: Uyarı
|
back_to_warning: Uyarı
|
||||||
|
|
|
@ -479,6 +479,7 @@ zh-CN:
|
||||||
other: 在 %{count} 天中尝试失败。
|
other: 在 %{count} 天中尝试失败。
|
||||||
no_failures_recorded: 没有失败记录。
|
no_failures_recorded: 没有失败记录。
|
||||||
title: 可用性
|
title: 可用性
|
||||||
|
warning: 上一次连接到此服务器的尝试失败了
|
||||||
back_to_all: 全部
|
back_to_all: 全部
|
||||||
back_to_limited: 受限
|
back_to_limited: 受限
|
||||||
back_to_warning: 警告
|
back_to_warning: 警告
|
||||||
|
|
|
@ -479,6 +479,7 @@ zh-TW:
|
||||||
other: 錯誤嘗試於 %{count} 天。
|
other: 錯誤嘗試於 %{count} 天。
|
||||||
no_failures_recorded: 報告中沒有錯誤。
|
no_failures_recorded: 報告中沒有錯誤。
|
||||||
title: 可用狀態
|
title: 可用狀態
|
||||||
|
warning: 上一次嘗試連線至本伺服器失敗
|
||||||
back_to_all: 所有
|
back_to_all: 所有
|
||||||
back_to_limited: 受限制的
|
back_to_limited: 受限制的
|
||||||
back_to_warning: 警告
|
back_to_warning: 警告
|
||||||
|
|
|
@ -17,7 +17,7 @@ module Mastodon
|
||||||
end
|
end
|
||||||
|
|
||||||
def flags
|
def flags
|
||||||
'rc1'
|
'rc3'
|
||||||
end
|
end
|
||||||
|
|
||||||
def suffix
|
def suffix
|
||||||
|
|
|
@ -0,0 +1,55 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe EmojiFormatter do
|
||||||
|
let!(:emoji) { Fabricate(:custom_emoji, shortcode: 'coolcat') }
|
||||||
|
|
||||||
|
def preformat_text(str)
|
||||||
|
TextFormatter.new(str).to_s
|
||||||
|
end
|
||||||
|
|
||||||
|
describe '#to_s' do
|
||||||
|
subject { described_class.new(text, emojis).to_s }
|
||||||
|
|
||||||
|
let(:emojis) { [emoji] }
|
||||||
|
|
||||||
|
context 'given text that is not marked as html-safe' do
|
||||||
|
let(:text) { 'Foo' }
|
||||||
|
|
||||||
|
it 'raises an argument error' do
|
||||||
|
expect { subject }.to raise_error ArgumentError
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text with an emoji shortcode at the start' do
|
||||||
|
let(:text) { preformat_text(':coolcat: Beep boop') }
|
||||||
|
|
||||||
|
it 'converts the shortcode to an image tag' do
|
||||||
|
is_expected.to match(/<img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text with an emoji shortcode in the middle' do
|
||||||
|
let(:text) { preformat_text('Beep :coolcat: boop') }
|
||||||
|
|
||||||
|
it 'converts the shortcode to an image tag' do
|
||||||
|
is_expected.to match(/Beep <img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text with concatenated emoji shortcodes' do
|
||||||
|
let(:text) { preformat_text(':coolcat::coolcat:') }
|
||||||
|
|
||||||
|
it 'does not touch the shortcodes' do
|
||||||
|
is_expected.to match(/:coolcat::coolcat:/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text with an emoji shortcode at the end' do
|
||||||
|
let(:text) { preformat_text('Beep boop :coolcat:') }
|
||||||
|
|
||||||
|
it 'converts the shortcode to an image tag' do
|
||||||
|
is_expected.to match(/boop <img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -1,638 +0,0 @@
|
||||||
require 'rails_helper'
|
|
||||||
|
|
||||||
RSpec.describe Formatter do
|
|
||||||
let(:local_account) { Fabricate(:account, domain: nil, username: 'alice') }
|
|
||||||
let(:remote_account) { Fabricate(:account, domain: 'remote.test', username: 'bob', url: 'https://remote.test/') }
|
|
||||||
|
|
||||||
shared_examples 'encode and link URLs' do
|
|
||||||
context 'given a stand-alone medium URL' do
|
|
||||||
let(:text) { 'https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a stand-alone google URL' do
|
|
||||||
let(:text) { 'http://google.com' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="http://google.com"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a stand-alone URL with a newer TLD' do
|
|
||||||
let(:text) { 'http://example.gay' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="http://example.gay"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a stand-alone IDN URL' do
|
|
||||||
let(:text) { 'https://nic.みんな/' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://nic.みんな/"'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'has display URL' do
|
|
||||||
is_expected.to include '<span class="">nic.みんな/</span>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with a trailing period' do
|
|
||||||
let(:text) { 'http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona. ' }
|
|
||||||
|
|
||||||
it 'matches the full URL but not the period' do
|
|
||||||
is_expected.to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL enclosed with parentheses' do
|
|
||||||
let(:text) { '(http://google.com/)' }
|
|
||||||
|
|
||||||
it 'matches the full URL but not the parentheses' do
|
|
||||||
is_expected.to include 'href="http://google.com/"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with a trailing exclamation point' do
|
|
||||||
let(:text) { 'http://www.google.com!' }
|
|
||||||
|
|
||||||
it 'matches the full URL but not the exclamation point' do
|
|
||||||
is_expected.to include 'href="http://www.google.com"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with a trailing single quote' do
|
|
||||||
let(:text) { "http://www.google.com'" }
|
|
||||||
|
|
||||||
it 'matches the full URL but not the single quote' do
|
|
||||||
is_expected.to include 'href="http://www.google.com"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with a trailing angle bracket' do
|
|
||||||
let(:text) { 'http://www.google.com>' }
|
|
||||||
|
|
||||||
it 'matches the full URL but not the angle bracket' do
|
|
||||||
is_expected.to include 'href="http://www.google.com"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with a query string' do
|
|
||||||
context 'with escaped unicode character' do
|
|
||||||
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with unicode character' do
|
|
||||||
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓&q=autolink' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&q=autolink"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with unicode character at the end' do
|
|
||||||
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with escaped and not escaped unicode characters' do
|
|
||||||
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink' }
|
|
||||||
|
|
||||||
it 'preserves escaped unicode characters' do
|
|
||||||
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with parentheses in it' do
|
|
||||||
let(:text) { 'https://en.wikipedia.org/wiki/Diaspora_(software)' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL in quotation marks' do
|
|
||||||
let(:text) { '"https://example.com/"' }
|
|
||||||
|
|
||||||
it 'does not match the quotation marks' do
|
|
||||||
is_expected.to include 'href="https://example.com/"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL in angle brackets' do
|
|
||||||
let(:text) { '<https://example.com/>' }
|
|
||||||
|
|
||||||
it 'does not match the angle brackets' do
|
|
||||||
is_expected.to include 'href="https://example.com/"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with Japanese path string' do
|
|
||||||
let(:text) { 'https://ja.wikipedia.org/wiki/日本' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://ja.wikipedia.org/wiki/日本"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with Korean path string' do
|
|
||||||
let(:text) { 'https://ko.wikipedia.org/wiki/대한민국' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://ko.wikipedia.org/wiki/대한민국"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with a full-width space' do
|
|
||||||
let(:text) { 'https://example.com/ abc123' }
|
|
||||||
|
|
||||||
it 'does not match the full-width space' do
|
|
||||||
is_expected.to include 'href="https://example.com/"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL in Japanese quotation marks' do
|
|
||||||
let(:text) { '「[https://example.org/」' }
|
|
||||||
|
|
||||||
it 'does not match the quotation marks' do
|
|
||||||
is_expected.to include 'href="https://example.org/"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with Simplified Chinese path string' do
|
|
||||||
let(:text) { 'https://baike.baidu.com/item/中华人民共和国' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://baike.baidu.com/item/中华人民共和国"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL with Traditional Chinese path string' do
|
|
||||||
let(:text) { 'https://zh.wikipedia.org/wiki/臺灣' }
|
|
||||||
|
|
||||||
it 'matches the full URL' do
|
|
||||||
is_expected.to include 'href="https://zh.wikipedia.org/wiki/臺灣"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL containing unsafe code (XSS attack, visible part)' do
|
|
||||||
let(:text) { %q{http://example.com/b<del>b</del>} }
|
|
||||||
|
|
||||||
it 'does not include the HTML in the URL' do
|
|
||||||
is_expected.to include '"http://example.com/b"'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'escapes the HTML' do
|
|
||||||
is_expected.to include '<del>b</del>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a URL containing unsafe code (XSS attack, invisible part)' do
|
|
||||||
let(:text) { %q{http://example.com/blahblahblahblah/a<script>alert("Hello")</script>} }
|
|
||||||
|
|
||||||
it 'does not include the HTML in the URL' do
|
|
||||||
is_expected.to include '"http://example.com/blahblahblahblah/a"'
|
|
||||||
end
|
|
||||||
|
|
||||||
it 'escapes the HTML' do
|
|
||||||
is_expected.to include '<script>alert("Hello")</script>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given text containing HTML code (script tag)' do
|
|
||||||
let(:text) { '<script>alert("Hello")</script>' }
|
|
||||||
|
|
||||||
it 'escapes the HTML' do
|
|
||||||
is_expected.to include '<p><script>alert("Hello")</script></p>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given text containing HTML (XSS attack)' do
|
|
||||||
let(:text) { %q{<img src="javascript:alert('XSS');">} }
|
|
||||||
|
|
||||||
it 'escapes the HTML' do
|
|
||||||
is_expected.to include '<p><img src="javascript:alert('XSS');"></p>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given an invalid URL' do
|
|
||||||
let(:text) { 'http://www\.google\.com' }
|
|
||||||
|
|
||||||
it 'outputs the raw URL' do
|
|
||||||
is_expected.to eq '<p>http://www\.google\.com</p>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given text containing a hashtag' do
|
|
||||||
let(:text) { '#hashtag' }
|
|
||||||
|
|
||||||
it 'creates a hashtag link' do
|
|
||||||
is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given text containing a hashtag with Unicode chars' do
|
|
||||||
let(:text) { '#hashtagタグ' }
|
|
||||||
|
|
||||||
it 'creates a hashtag link' do
|
|
||||||
is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a stand-alone xmpp: URI' do
|
|
||||||
let(:text) { 'xmpp:user@instance.com' }
|
|
||||||
|
|
||||||
it 'matches the full URI' do
|
|
||||||
is_expected.to include 'href="xmpp:user@instance.com"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a an xmpp: URI with a query-string' do
|
|
||||||
let(:text) { 'please join xmpp:muc@instance.com?join right now' }
|
|
||||||
|
|
||||||
it 'matches the full URI' do
|
|
||||||
is_expected.to include 'href="xmpp:muc@instance.com?join"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given text containing a magnet: URI' do
|
|
||||||
let(:text) { 'wikipedia gives this example of a magnet uri: magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' }
|
|
||||||
|
|
||||||
it 'matches the full URI' do
|
|
||||||
is_expected.to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#format_spoiler' do
|
|
||||||
subject { Formatter.instance.format_spoiler(status) }
|
|
||||||
|
|
||||||
context 'given a post containing plain text' do
|
|
||||||
let(:status) { Fabricate(:status, text: 'text', spoiler_text: 'Secret!', uri: nil) }
|
|
||||||
|
|
||||||
it 'Returns the spoiler text' do
|
|
||||||
is_expected.to eq 'Secret!'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode at the start' do
|
|
||||||
let!(:emoji) { Fabricate(:custom_emoji) }
|
|
||||||
let(:status) { Fabricate(:status, text: 'text', spoiler_text: ':coolcat: Secret!', uri: nil) }
|
|
||||||
let(:text) { ':coolcat: Beep boop' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/<img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#format' do
|
|
||||||
subject { Formatter.instance.format(status) }
|
|
||||||
|
|
||||||
context 'given a post with local status' do
|
|
||||||
context 'given a reblogged post' do
|
|
||||||
let(:reblog) { Fabricate(:status, account: local_account, text: 'Hello world', uri: nil) }
|
|
||||||
let(:status) { Fabricate(:status, reblog: reblog) }
|
|
||||||
|
|
||||||
it 'returns original status with credit to its author' do
|
|
||||||
is_expected.to include 'RT <span class="h-card"><a href="https://cb6e6126.ngrok.io/@alice" class="u-url mention">@<span>alice</span></a></span> Hello world'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post containing plain text' do
|
|
||||||
let(:status) { Fabricate(:status, text: 'text', uri: nil) }
|
|
||||||
|
|
||||||
it 'paragraphizes the text' do
|
|
||||||
is_expected.to eq '<p>text</p>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post containing line feeds' do
|
|
||||||
let(:status) { Fabricate(:status, text: "line\nfeed", uri: nil) }
|
|
||||||
|
|
||||||
it 'removes line feeds' do
|
|
||||||
is_expected.not_to include "\n"
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post containing linkable mentions' do
|
|
||||||
let(:status) { Fabricate(:status, mentions: [ Fabricate(:mention, account: local_account) ], text: '@alice') }
|
|
||||||
|
|
||||||
it 'creates a mention link' do
|
|
||||||
is_expected.to include '<a href="https://cb6e6126.ngrok.io/@alice" class="u-url mention">@<span>alice</span></a></span>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post containing unlinkable mentions' do
|
|
||||||
let(:status) { Fabricate(:status, text: '@alice', uri: nil) }
|
|
||||||
|
|
||||||
it 'does not create a mention link' do
|
|
||||||
is_expected.to include '@alice'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context do
|
|
||||||
let(:content_type) { 'text/plain' }
|
|
||||||
|
|
||||||
subject do
|
|
||||||
status = Fabricate(:status, text: text, content_type: content_type, uri: nil)
|
|
||||||
Formatter.instance.format(status)
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given an invalid URL (invalid port)' do
|
|
||||||
let(:text) { 'https://foo.bar:X/' }
|
|
||||||
let(:content_type) { 'text/markdown' }
|
|
||||||
|
|
||||||
it 'outputs the raw URL' do
|
|
||||||
is_expected.to eq '<p>https://foo.bar:X/</p>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
include_examples 'encode and link URLs'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with custom_emojify option' do
|
|
||||||
let!(:emoji) { Fabricate(:custom_emoji) }
|
|
||||||
let(:status) { Fabricate(:status, account: local_account, text: text) }
|
|
||||||
|
|
||||||
subject { Formatter.instance.format(status, custom_emojify: true) }
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode at the start' do
|
|
||||||
let(:text) { ':coolcat: Beep boop' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/<p><img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode in the middle' do
|
|
||||||
let(:text) { 'Beep :coolcat: boop' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/Beep <img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with concatenated emoji shortcodes' do
|
|
||||||
let(:text) { ':coolcat::coolcat:' }
|
|
||||||
|
|
||||||
it 'does not touch the shortcodes' do
|
|
||||||
is_expected.to match(/:coolcat::coolcat:/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode at the end' do
|
|
||||||
let(:text) { 'Beep boop :coolcat:' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/boop <img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with remote status' do
|
|
||||||
let(:status) { Fabricate(:status, account: remote_account, text: 'Beep boop') }
|
|
||||||
|
|
||||||
it 'reformats the post' do
|
|
||||||
is_expected.to eq 'Beep boop'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with custom_emojify option' do
|
|
||||||
let!(:emoji) { Fabricate(:custom_emoji, domain: remote_account.domain) }
|
|
||||||
let(:status) { Fabricate(:status, account: remote_account, text: text) }
|
|
||||||
|
|
||||||
subject { Formatter.instance.format(status, custom_emojify: true) }
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode at the start' do
|
|
||||||
let(:text) { '<p>:coolcat: Beep boop<br />' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/<p><img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode in the middle' do
|
|
||||||
let(:text) { '<p>Beep :coolcat: boop</p>' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/Beep <img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with concatenated emoji' do
|
|
||||||
let(:text) { '<p>:coolcat::coolcat:</p>' }
|
|
||||||
|
|
||||||
it 'does not touch the shortcodes' do
|
|
||||||
is_expected.to match(/<p>:coolcat::coolcat:<\/p>/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode at the end' do
|
|
||||||
let(:text) { '<p>Beep boop<br />:coolcat:</p>' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/<br><img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#reformat' do
|
|
||||||
subject { Formatter.instance.reformat(text) }
|
|
||||||
|
|
||||||
context 'given a post containing plain text' do
|
|
||||||
let(:text) { 'Beep boop' }
|
|
||||||
|
|
||||||
it 'keeps the plain text' do
|
|
||||||
is_expected.to include 'Beep boop'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post containing script tags' do
|
|
||||||
let(:text) { '<script>alert("Hello")</script>' }
|
|
||||||
|
|
||||||
it 'strips the scripts' do
|
|
||||||
is_expected.to_not include '<script>alert("Hello")</script>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post containing malicious classes' do
|
|
||||||
let(:text) { '<span class="mention status__content__spoiler-link">Show more</span>' }
|
|
||||||
|
|
||||||
it 'strips the malicious classes' do
|
|
||||||
is_expected.to_not include 'status__content__spoiler-link'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#plaintext' do
|
|
||||||
subject { Formatter.instance.plaintext(status) }
|
|
||||||
|
|
||||||
context 'given a post with local status' do
|
|
||||||
let(:status) { Fabricate(:status, text: '<p>a text by a nerd who uses an HTML tag in text</p>', content_type: content_type, uri: nil) }
|
|
||||||
let(:content_type) { 'text/plain' }
|
|
||||||
|
|
||||||
it 'returns the raw text' do
|
|
||||||
is_expected.to eq '<p>a text by a nerd who uses an HTML tag in text</p>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with remote status' do
|
|
||||||
let(:status) { Fabricate(:status, account: remote_account, text: '<script>alert("Hello")</script>') }
|
|
||||||
|
|
||||||
it 'returns tag-stripped text' do
|
|
||||||
is_expected.to eq ''
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#simplified_format' do
|
|
||||||
subject { Formatter.instance.simplified_format(account) }
|
|
||||||
|
|
||||||
context 'given a post with local status' do
|
|
||||||
let(:account) { Fabricate(:account, domain: nil, note: text) }
|
|
||||||
|
|
||||||
context 'given a post containing linkable mentions for local accounts' do
|
|
||||||
let(:text) { '@alice' }
|
|
||||||
|
|
||||||
before { local_account }
|
|
||||||
|
|
||||||
it 'creates a mention link' do
|
|
||||||
is_expected.to eq '<p><span class="h-card"><a href="https://cb6e6126.ngrok.io/@alice" class="u-url mention">@<span>alice</span></a></span></p>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post containing linkable mentions for remote accounts' do
|
|
||||||
let(:text) { '@bob@remote.test' }
|
|
||||||
|
|
||||||
before { remote_account }
|
|
||||||
|
|
||||||
it 'creates a mention link' do
|
|
||||||
is_expected.to eq '<p><span class="h-card"><a href="https://remote.test/" class="u-url mention">@<span>bob</span></a></span></p>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post containing unlinkable mentions' do
|
|
||||||
let(:text) { '@alice' }
|
|
||||||
|
|
||||||
it 'does not create a mention link' do
|
|
||||||
is_expected.to eq '<p>@alice</p>'
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with custom_emojify option' do
|
|
||||||
let!(:emoji) { Fabricate(:custom_emoji) }
|
|
||||||
|
|
||||||
before { account.note = text }
|
|
||||||
subject { Formatter.instance.simplified_format(account, custom_emojify: true) }
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode at the start' do
|
|
||||||
let(:text) { ':coolcat: Beep boop' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/<p><img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode in the middle' do
|
|
||||||
let(:text) { 'Beep :coolcat: boop' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/Beep <img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with concatenated emoji shortcodes' do
|
|
||||||
let(:text) { ':coolcat::coolcat:' }
|
|
||||||
|
|
||||||
it 'does not touch the shortcodes' do
|
|
||||||
is_expected.to match(/:coolcat::coolcat:/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode at the end' do
|
|
||||||
let(:text) { 'Beep boop :coolcat:' }
|
|
||||||
|
|
||||||
it 'converts the shortcode to an image tag' do
|
|
||||||
is_expected.to match(/boop <img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
include_examples 'encode and link URLs'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with remote status' do
|
|
||||||
let(:text) { '<script>alert("Hello")</script>' }
|
|
||||||
let(:account) { Fabricate(:account, domain: 'remote', note: text) }
|
|
||||||
|
|
||||||
it 'reformats' do
|
|
||||||
is_expected.to_not include '<script>alert("Hello")</script>'
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'with custom_emojify option' do
|
|
||||||
let!(:emoji) { Fabricate(:custom_emoji, domain: remote_account.domain) }
|
|
||||||
|
|
||||||
before { remote_account.note = text }
|
|
||||||
|
|
||||||
subject { Formatter.instance.simplified_format(remote_account, custom_emojify: true) }
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode at the start' do
|
|
||||||
let(:text) { '<p>:coolcat: Beep boop<br />' }
|
|
||||||
|
|
||||||
it 'converts shortcode to image tag' do
|
|
||||||
is_expected.to match(/<p><img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode in the middle' do
|
|
||||||
let(:text) { '<p>Beep :coolcat: boop</p>' }
|
|
||||||
|
|
||||||
it 'converts shortcode to image tag' do
|
|
||||||
is_expected.to match(/Beep <img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with concatenated emoji shortcodes' do
|
|
||||||
let(:text) { '<p>:coolcat::coolcat:</p>' }
|
|
||||||
|
|
||||||
it 'does not touch the shortcodes' do
|
|
||||||
is_expected.to match(/<p>:coolcat::coolcat:<\/p>/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
context 'given a post with an emoji shortcode at the end' do
|
|
||||||
let(:text) { '<p>Beep boop<br />:coolcat:</p>' }
|
|
||||||
|
|
||||||
it 'converts shortcode to image tag' do
|
|
||||||
is_expected.to match(/<br><img draggable="false" class="emojione custom-emoji" alt=":coolcat:"/)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
describe '#sanitize' do
|
|
||||||
let(:html) { '<script>alert("Hello")</script>' }
|
|
||||||
|
|
||||||
subject { Formatter.instance.sanitize(html, Sanitize::Config::MASTODON_STRICT) }
|
|
||||||
|
|
||||||
it 'sanitizes' do
|
|
||||||
is_expected.to eq ''
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
|
@ -0,0 +1,44 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe HtmlAwareFormatter do
|
||||||
|
describe '#to_s' do
|
||||||
|
subject { described_class.new(text, local).to_s }
|
||||||
|
|
||||||
|
context 'when local' do
|
||||||
|
let(:local) { true }
|
||||||
|
let(:text) { 'Foo bar' }
|
||||||
|
|
||||||
|
it 'returns formatted text' do
|
||||||
|
is_expected.to eq '<p>Foo bar</p>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'when remote' do
|
||||||
|
let(:local) { false }
|
||||||
|
|
||||||
|
context 'given plain text' do
|
||||||
|
let(:text) { 'Beep boop' }
|
||||||
|
|
||||||
|
it 'keeps the plain text' do
|
||||||
|
is_expected.to include 'Beep boop'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing script tags' do
|
||||||
|
let(:text) { '<script>alert("Hello")</script>' }
|
||||||
|
|
||||||
|
it 'strips the scripts' do
|
||||||
|
is_expected.to_not include '<script>alert("Hello")</script>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing malicious classes' do
|
||||||
|
let(:text) { '<span class="mention status__content__spoiler-link">Show more</span>' }
|
||||||
|
|
||||||
|
it 'strips the malicious classes' do
|
||||||
|
is_expected.to_not include 'status__content__spoiler-link'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -25,6 +25,14 @@ RSpec.describe LinkDetailsExtractor do
|
||||||
expect(subject.canonical_url).to eq 'https://foo.com/article'
|
expect(subject.canonical_url).to eq 'https://foo.com/article'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
context 'when canonical URL is set to "null"' do
|
||||||
|
let(:html) { '<!doctype html><link rel="canonical" href="null" />' }
|
||||||
|
|
||||||
|
it 'ignores the canonical URLs' do
|
||||||
|
expect(subject.canonical_url).to eq original_url
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
context 'when structured data is present' do
|
context 'when structured data is present' do
|
||||||
|
|
|
@ -0,0 +1,24 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe PlainTextFormatter do
|
||||||
|
describe '#to_s' do
|
||||||
|
subject { described_class.new(status.text, status.local?).to_s }
|
||||||
|
|
||||||
|
context 'given a post with local status' do
|
||||||
|
let(:status) { Fabricate(:status, text: '<p>a text by a nerd who uses an HTML tag in text</p>', uri: nil) }
|
||||||
|
|
||||||
|
it 'returns the raw text' do
|
||||||
|
is_expected.to eq '<p>a text by a nerd who uses an HTML tag in text</p>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a post with remote status' do
|
||||||
|
let(:remote_account) { Fabricate(:account, domain: 'remote.test', username: 'bob', url: 'https://remote.test/') }
|
||||||
|
let(:status) { Fabricate(:status, account: remote_account, text: '<p>Hello</p><script>alert("Hello")</script>') }
|
||||||
|
|
||||||
|
it 'returns tag-stripped text' do
|
||||||
|
is_expected.to eq 'Hello'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -0,0 +1,313 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
RSpec.describe TextFormatter do
|
||||||
|
describe '#to_s' do
|
||||||
|
let(:preloaded_accounts) { nil }
|
||||||
|
|
||||||
|
subject { described_class.new(text, preloaded_accounts: preloaded_accounts).to_s }
|
||||||
|
|
||||||
|
context 'given text containing plain text' do
|
||||||
|
let(:text) { 'text' }
|
||||||
|
|
||||||
|
it 'paragraphizes the text' do
|
||||||
|
is_expected.to eq '<p>text</p>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing line feeds' do
|
||||||
|
let(:text) { "line\nfeed" }
|
||||||
|
|
||||||
|
it 'removes line feeds' do
|
||||||
|
is_expected.not_to include "\n"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing linkable mentions' do
|
||||||
|
let(:preloaded_accounts) { [Fabricate(:account, username: 'alice')] }
|
||||||
|
let(:text) { '@alice' }
|
||||||
|
|
||||||
|
it 'creates a mention link' do
|
||||||
|
is_expected.to include '<a href="https://cb6e6126.ngrok.io/@alice" class="u-url mention">@<span>alice</span></a></span>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing unlinkable mentions' do
|
||||||
|
let(:preloaded_accounts) { [] }
|
||||||
|
let(:text) { '@alice' }
|
||||||
|
|
||||||
|
it 'does not create a mention link' do
|
||||||
|
is_expected.to include '@alice'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a stand-alone medium URL' do
|
||||||
|
let(:text) { 'https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://hackernoon.com/the-power-to-build-communities-a-response-to-mark-zuckerberg-3f2cac9148a4"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a stand-alone google URL' do
|
||||||
|
let(:text) { 'http://google.com' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="http://google.com"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a stand-alone URL with a newer TLD' do
|
||||||
|
let(:text) { 'http://example.gay' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="http://example.gay"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a stand-alone IDN URL' do
|
||||||
|
let(:text) { 'https://nic.みんな/' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://nic.みんな/"'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'has display URL' do
|
||||||
|
is_expected.to include '<span class="">nic.みんな/</span>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with a trailing period' do
|
||||||
|
let(:text) { 'http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona. ' }
|
||||||
|
|
||||||
|
it 'matches the full URL but not the period' do
|
||||||
|
is_expected.to include 'href="http://www.mcmansionhell.com/post/156408871451/50-states-of-mcmansion-hell-scottsdale-arizona"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL enclosed with parentheses' do
|
||||||
|
let(:text) { '(http://google.com/)' }
|
||||||
|
|
||||||
|
it 'matches the full URL but not the parentheses' do
|
||||||
|
is_expected.to include 'href="http://google.com/"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with a trailing exclamation point' do
|
||||||
|
let(:text) { 'http://www.google.com!' }
|
||||||
|
|
||||||
|
it 'matches the full URL but not the exclamation point' do
|
||||||
|
is_expected.to include 'href="http://www.google.com"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with a trailing single quote' do
|
||||||
|
let(:text) { "http://www.google.com'" }
|
||||||
|
|
||||||
|
it 'matches the full URL but not the single quote' do
|
||||||
|
is_expected.to include 'href="http://www.google.com"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with a trailing angle bracket' do
|
||||||
|
let(:text) { 'http://www.google.com>' }
|
||||||
|
|
||||||
|
it 'matches the full URL but not the angle bracket' do
|
||||||
|
is_expected.to include 'href="http://www.google.com"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with a query string' do
|
||||||
|
context 'with escaped unicode character' do
|
||||||
|
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&q=autolink"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with unicode character' do
|
||||||
|
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓&q=autolink' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓&q=autolink"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with unicode character at the end' do
|
||||||
|
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=✓' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=✓"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'with escaped and not escaped unicode characters' do
|
||||||
|
let(:text) { 'https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink' }
|
||||||
|
|
||||||
|
it 'preserves escaped unicode characters' do
|
||||||
|
is_expected.to include 'href="https://www.ruby-toolbox.com/search?utf8=%E2%9C%93&utf81=✓&q=autolink"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with parentheses in it' do
|
||||||
|
let(:text) { 'https://en.wikipedia.org/wiki/Diaspora_(software)' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://en.wikipedia.org/wiki/Diaspora_(software)"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL in quotation marks' do
|
||||||
|
let(:text) { '"https://example.com/"' }
|
||||||
|
|
||||||
|
it 'does not match the quotation marks' do
|
||||||
|
is_expected.to include 'href="https://example.com/"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL in angle brackets' do
|
||||||
|
let(:text) { '<https://example.com/>' }
|
||||||
|
|
||||||
|
it 'does not match the angle brackets' do
|
||||||
|
is_expected.to include 'href="https://example.com/"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with Japanese path string' do
|
||||||
|
let(:text) { 'https://ja.wikipedia.org/wiki/日本' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://ja.wikipedia.org/wiki/日本"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with Korean path string' do
|
||||||
|
let(:text) { 'https://ko.wikipedia.org/wiki/대한민국' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://ko.wikipedia.org/wiki/대한민국"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with a full-width space' do
|
||||||
|
let(:text) { 'https://example.com/ abc123' }
|
||||||
|
|
||||||
|
it 'does not match the full-width space' do
|
||||||
|
is_expected.to include 'href="https://example.com/"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL in Japanese quotation marks' do
|
||||||
|
let(:text) { '「[https://example.org/」' }
|
||||||
|
|
||||||
|
it 'does not match the quotation marks' do
|
||||||
|
is_expected.to include 'href="https://example.org/"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with Simplified Chinese path string' do
|
||||||
|
let(:text) { 'https://baike.baidu.com/item/中华人民共和国' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://baike.baidu.com/item/中华人民共和国"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL with Traditional Chinese path string' do
|
||||||
|
let(:text) { 'https://zh.wikipedia.org/wiki/臺灣' }
|
||||||
|
|
||||||
|
it 'matches the full URL' do
|
||||||
|
is_expected.to include 'href="https://zh.wikipedia.org/wiki/臺灣"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL containing unsafe code (XSS attack, visible part)' do
|
||||||
|
let(:text) { %q{http://example.com/b<del>b</del>} }
|
||||||
|
|
||||||
|
it 'does not include the HTML in the URL' do
|
||||||
|
is_expected.to include '"http://example.com/b"'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'escapes the HTML' do
|
||||||
|
is_expected.to include '<del>b</del>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given a URL containing unsafe code (XSS attack, invisible part)' do
|
||||||
|
let(:text) { %q{http://example.com/blahblahblahblah/a<script>alert("Hello")</script>} }
|
||||||
|
|
||||||
|
it 'does not include the HTML in the URL' do
|
||||||
|
is_expected.to include '"http://example.com/blahblahblahblah/a"'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'escapes the HTML' do
|
||||||
|
is_expected.to include '<script>alert("Hello")</script>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing HTML code (script tag)' do
|
||||||
|
let(:text) { '<script>alert("Hello")</script>' }
|
||||||
|
|
||||||
|
it 'escapes the HTML' do
|
||||||
|
is_expected.to include '<p><script>alert("Hello")</script></p>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing HTML (XSS attack)' do
|
||||||
|
let(:text) { %q{<img src="javascript:alert('XSS');">} }
|
||||||
|
|
||||||
|
it 'escapes the HTML' do
|
||||||
|
is_expected.to include '<p><img src="javascript:alert('XSS');"></p>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given an invalid URL' do
|
||||||
|
let(:text) { 'http://www\.google\.com' }
|
||||||
|
|
||||||
|
it 'outputs the raw URL' do
|
||||||
|
is_expected.to eq '<p>http://www\.google\.com</p>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing a hashtag' do
|
||||||
|
let(:text) { '#hashtag' }
|
||||||
|
|
||||||
|
it 'creates a hashtag link' do
|
||||||
|
is_expected.to include '/tags/hashtag" class="mention hashtag" rel="tag">#<span>hashtag</span></a>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing a hashtag with Unicode chars' do
|
||||||
|
let(:text) { '#hashtagタグ' }
|
||||||
|
|
||||||
|
it 'creates a hashtag link' do
|
||||||
|
is_expected.to include '/tags/hashtag%E3%82%BF%E3%82%B0" class="mention hashtag" rel="tag">#<span>hashtagタグ</span></a>'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text with a stand-alone xmpp: URI' do
|
||||||
|
let(:text) { 'xmpp:user@instance.com' }
|
||||||
|
|
||||||
|
it 'matches the full URI' do
|
||||||
|
is_expected.to include 'href="xmpp:user@instance.com"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text with an xmpp: URI with a query-string' do
|
||||||
|
let(:text) { 'please join xmpp:muc@instance.com?join right now' }
|
||||||
|
|
||||||
|
it 'matches the full URI' do
|
||||||
|
is_expected.to include 'href="xmpp:muc@instance.com?join"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
context 'given text containing a magnet: URI' do
|
||||||
|
let(:text) { 'wikipedia gives this example of a magnet uri: magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a' }
|
||||||
|
|
||||||
|
it 'matches the full URI' do
|
||||||
|
is_expected.to include 'href="magnet:?xt=urn:btih:c12fe1c06bba254a9dc9f519b335aa7c1367a88a"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue