diff --git a/.env.production.sample b/.env.production.sample index 91fcce6ac..3f0edd72f 100644 --- a/.env.production.sample +++ b/.env.production.sample @@ -11,10 +11,11 @@ DB_PASS= DB_PORT=5432 # Federation -# Note: Changing LOCAL_DOMAIN or LOCAL_HTTPS at a later time will cause unwanted side effects. +# Note: Changing LOCAL_DOMAIN at a later time will cause unwanted side effects, including breaking all existing federation. # LOCAL_DOMAIN should *NOT* contain the protocol part of the domain e.g https://example.com. LOCAL_DOMAIN=example.com -LOCAL_HTTPS=true + +# Changing LOCAL_HTTPS in production is no longer supported. (Mastodon will always serve https:// links) # Use this only if you need to run mastodon on a different domain than the one used for federation. # You can read more about this option on https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Serving_a_different_domain.md diff --git a/Gemfile b/Gemfile index f6acb431a..a5bf2daff 100644 --- a/Gemfile +++ b/Gemfile @@ -28,7 +28,7 @@ gem 'browser' gem 'charlock_holmes', '~> 0.7.5' gem 'iso-639' gem 'cld3', '~> 3.2.0' -gem 'devise', '~> 4.2' +gem 'devise', '~> 4.3' gem 'devise-two-factor', '~> 3.0' gem 'doorkeeper', '~> 4.2' gem 'fast_blank', '~> 1.0' @@ -58,6 +58,7 @@ gem 'redis', '~> 3.3', require: ['redis', 'redis/connection/hiredis'] gem 'mario-redis-lock', '~> 1.2', require: 'redis_lock' gem 'rqrcode', '~> 0.10' gem 'ruby-oembed', '~> 0.12', require: 'oembed' +gem 'ruby-progressbar', '~> 1.4' gem 'sanitize', '~> 4.4' gem 'sidekiq', '~> 5.0' gem 'sidekiq-scheduler', '~> 2.1' diff --git a/Gemfile.lock b/Gemfile.lock index febfb8561..f3887b2b8 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -299,13 +299,11 @@ GEM sidekiq (>= 3.5.0) statsd-ruby (~> 1.2.0) oj (3.3.9) - openssl (2.0.6) orm_adapter (0.5.0) - ostatus2 (2.0.1) + ostatus2 (2.0.2) addressable (~> 2.4) http (~> 2.0) nokogiri (~> 1.6) - openssl (~> 2.0) ox (2.8.2) paperclip (5.1.0) activemodel (>= 4.2.0) @@ -561,7 +559,7 @@ DEPENDENCIES charlock_holmes (~> 0.7.5) cld3 (~> 3.2.0) climate_control (~> 0.2) - devise (~> 4.2) + devise (~> 4.3) devise-two-factor (~> 3.0) doorkeeper (~> 4.2) dotenv-rails (~> 2.2) @@ -621,6 +619,7 @@ DEPENDENCIES rspec-sidekiq (~> 3.0) rubocop ruby-oembed (~> 0.12) + ruby-progressbar (~> 1.4) sanitize (~> 4.4) scss_lint (~> 0.55) sidekiq (~> 5.0) @@ -643,4 +642,4 @@ RUBY VERSION ruby 2.4.2p198 BUNDLED WITH - 1.16.0 + 1.16.1 diff --git a/app/controllers/accounts_controller.rb b/app/controllers/accounts_controller.rb index 75915b337..69fd20e27 100644 --- a/app/controllers/accounts_controller.rb +++ b/app/controllers/accounts_controller.rb @@ -2,7 +2,8 @@ class AccountsController < ApplicationController include AccountControllerConcern - include SignatureVerification + + before_action :set_cache_headers def show respond_to do |format| @@ -26,10 +27,11 @@ class AccountsController < ApplicationController end format.json do - render json: @account, - serializer: ActivityPub::ActorSerializer, - adapter: ActivityPub::Adapter, - content_type: 'application/activity+json' + skip_session! + + render_cached_json(['activitypub', 'actor', @account.cache_key], content_type: 'application/activity+json') do + ActiveModelSerializers::SerializableResource.new(@account, serializer: ActivityPub::ActorSerializer, adapter: ActivityPub::Adapter) + end end end end diff --git a/app/controllers/activitypub/follows_controller.rb b/app/controllers/activitypub/follows_controller.rb new file mode 100644 index 000000000..038bcbabc --- /dev/null +++ b/app/controllers/activitypub/follows_controller.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class ActivityPub::FollowsController < Api::BaseController + include SignatureVerification + + def show + render json: follow_request, + serializer: ActivityPub::FollowSerializer, + adapter: ActivityPub::Adapter, + content_type: 'application/activity+json' + end + + private + + def follow_request + FollowRequest.includes(:account).references(:account).find_by!( + id: params.require(:id), + accounts: { domain: nil, username: params.require(:account_username) }, + target_account: signed_request_account + ) + end +end diff --git a/app/controllers/admin/custom_emojis_controller.rb b/app/controllers/admin/custom_emojis_controller.rb index ccab03de4..d61bafdf0 100644 --- a/app/controllers/admin/custom_emojis_controller.rb +++ b/app/controllers/admin/custom_emojis_controller.rb @@ -3,6 +3,7 @@ module Admin class CustomEmojisController < BaseController before_action :set_custom_emoji, except: [:index, :new, :create] + before_action :set_filter_params def index authorize :custom_emoji, :index? @@ -32,23 +33,26 @@ module Admin if @custom_emoji.update(resource_params) log_action :update, @custom_emoji - redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.updated_msg') + flash[:notice] = I18n.t('admin.custom_emojis.updated_msg') else - redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.update_failed_msg') + flash[:alert] = I18n.t('admin.custom_emojis.update_failed_msg') end + redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) end def destroy authorize @custom_emoji, :destroy? @custom_emoji.destroy! log_action :destroy, @custom_emoji - redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.destroyed_msg') + flash[:notice] = I18n.t('admin.custom_emojis.destroyed_msg') + redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) end def copy authorize @custom_emoji, :copy? - emoji = CustomEmoji.find_or_initialize_by(domain: nil, shortcode: @custom_emoji.shortcode) + emoji = CustomEmoji.find_or_initialize_by(domain: nil, + shortcode: @custom_emoji.shortcode) emoji.image = @custom_emoji.image if emoji.save @@ -58,21 +62,23 @@ module Admin flash[:alert] = I18n.t('admin.custom_emojis.copy_failed_msg') end - redirect_to admin_custom_emojis_path(page: params[:page]) + redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) end def enable authorize @custom_emoji, :enable? @custom_emoji.update!(disabled: false) log_action :enable, @custom_emoji - redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.enabled_msg') + flash[:notice] = I18n.t('admin.custom_emojis.enabled_msg') + redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) end def disable authorize @custom_emoji, :disable? @custom_emoji.update!(disabled: true) log_action :disable, @custom_emoji - redirect_to admin_custom_emojis_path, notice: I18n.t('admin.custom_emojis.disabled_msg') + flash[:notice] = I18n.t('admin.custom_emojis.disabled_msg') + redirect_to admin_custom_emojis_path(page: params[:page], **@filter_params) end private @@ -81,6 +87,10 @@ module Admin @custom_emoji = CustomEmoji.find(params[:id]) end + def set_filter_params + @filter_params = filter_params.to_hash.symbolize_keys + end + def resource_params params.require(:custom_emoji).permit(:shortcode, :image, :visible_in_picker) end diff --git a/app/controllers/admin/settings_controller.rb b/app/controllers/admin/settings_controller.rb index eed5fb6b5..487282dc3 100644 --- a/app/controllers/admin/settings_controller.rb +++ b/app/controllers/admin/settings_controller.rb @@ -17,6 +17,8 @@ module Admin bootstrap_timeline_accounts thumbnail min_invite_role + activity_api_enabled + peers_api_enabled ).freeze BOOLEAN_SETTINGS = %w( @@ -24,6 +26,8 @@ module Admin open_deletion timeline_preview show_staff_badge + activity_api_enabled + peers_api_enabled ).freeze UPLOAD_SETTINGS = %w( diff --git a/app/controllers/api/v1/instances/activity_controller.rb b/app/controllers/api/v1/instances/activity_controller.rb new file mode 100644 index 000000000..e14e0aee8 --- /dev/null +++ b/app/controllers/api/v1/instances/activity_controller.rb @@ -0,0 +1,36 @@ +# frozen_string_literal: true + +class Api::V1::Instances::ActivityController < Api::BaseController + before_action :require_enabled_api! + + respond_to :json + + def show + render_cached_json('api:v1:instances:activity:show', expires_in: 1.day) { activity } + end + + private + + def activity + weeks = [] + + 12.times do |i| + day = i.weeks.ago.to_date + week_id = day.cweek + week = Date.commercial(day.cwyear, week_id) + + weeks << { + week: week.to_time.to_i.to_s, + statuses: Redis.current.get("activity:statuses:local:#{week_id}") || '0', + logins: Redis.current.pfcount("activity:logins:#{week_id}").to_s, + registrations: Redis.current.get("activity:accounts:local:#{week_id}") || '0', + } + end + + weeks + end + + def require_enabled_api! + head 404 unless Setting.activity_api_enabled + end +end diff --git a/app/controllers/api/v1/instances/peers_controller.rb b/app/controllers/api/v1/instances/peers_controller.rb new file mode 100644 index 000000000..2070c487d --- /dev/null +++ b/app/controllers/api/v1/instances/peers_controller.rb @@ -0,0 +1,17 @@ +# frozen_string_literal: true + +class Api::V1::Instances::PeersController < Api::BaseController + before_action :require_enabled_api! + + respond_to :json + + def index + render_cached_json('api:v1:instances:peers:index', expires_in: 1.day) { Account.remote.domains } + end + + private + + def require_enabled_api! + head 404 unless Setting.peers_api_enabled + end +end diff --git a/app/controllers/application_controller.rb b/app/controllers/application_controller.rb index a213302cb..f59f2725b 100644 --- a/app/controllers/application_controller.rb +++ b/app/controllers/application_controller.rb @@ -121,4 +121,26 @@ class ApplicationController < ActionController::Base end end end + + def render_cached_json(cache_key, **options) + options[:expires_in] ||= 3.minutes + options[:public] ||= true + cache_key = cache_key.join(':') if cache_key.is_a?(Enumerable) + content_type = options.delete(:content_type) || 'application/json' + + data = Rails.cache.fetch(cache_key, { raw: true }.merge(options)) do + yield.to_json + end + + expires_in options[:expires_in], public: options[:public] + render json: data, content_type: content_type + end + + def set_cache_headers + response.headers['Vary'] = 'Accept' + end + + def skip_session! + request.session_options[:skip] = true + end end diff --git a/app/controllers/auth/confirmations_controller.rb b/app/controllers/auth/confirmations_controller.rb index d5e8e58ed..2fdb281f4 100644 --- a/app/controllers/auth/confirmations_controller.rb +++ b/app/controllers/auth/confirmations_controller.rb @@ -2,10 +2,4 @@ class Auth::ConfirmationsController < Devise::ConfirmationsController layout 'auth' - - def show - super do |user| - BootstrapTimelineWorker.perform_async(user.account_id) if user.errors.empty? - end - end end diff --git a/app/controllers/auth/registrations_controller.rb b/app/controllers/auth/registrations_controller.rb index da0b6512f..b8ff4e54f 100644 --- a/app/controllers/auth/registrations_controller.rb +++ b/app/controllers/auth/registrations_controller.rb @@ -37,6 +37,10 @@ class Auth::RegistrationsController < Devise::RegistrationsController new_user_session_path end + def after_update_path_for(_resource) + edit_user_registration_path + end + def check_enabled_registrations redirect_to root_path if single_user_mode? || !allowed_registrations? end diff --git a/app/controllers/authorize_follows_controller.rb b/app/controllers/authorize_follows_controller.rb index 78b564183..7afe664d1 100644 --- a/app/controllers/authorize_follows_controller.rb +++ b/app/controllers/authorize_follows_controller.rb @@ -4,6 +4,7 @@ class AuthorizeFollowsController < ApplicationController layout 'modal' before_action :authenticate_user! + before_action :set_body_classes def show @account = located_account || render(:error) @@ -58,4 +59,8 @@ class AuthorizeFollowsController < ApplicationController def acct_params params.fetch(:acct, '') end + + def set_body_classes + @body_classes = 'modal-layout' + end end diff --git a/app/controllers/concerns/user_tracking_concern.rb b/app/controllers/concerns/user_tracking_concern.rb index 8663c3086..1e3132941 100644 --- a/app/controllers/concerns/user_tracking_concern.rb +++ b/app/controllers/concerns/user_tracking_concern.rb @@ -17,6 +17,7 @@ module UserTrackingConcern # Mark as signed-in today current_user.update_tracked_fields!(request) + ActivityTracker.record('activity:logins', current_user.id) # Regenerate feed if needed regenerate_feed! if user_needs_feed_update? diff --git a/app/controllers/emojis_controller.rb b/app/controllers/emojis_controller.rb index a82b9340b..c9725ccc0 100644 --- a/app/controllers/emojis_controller.rb +++ b/app/controllers/emojis_controller.rb @@ -2,14 +2,16 @@ class EmojisController < ApplicationController before_action :set_emoji + before_action :set_cache_headers def show respond_to do |format| format.json do - render json: @emoji, - serializer: ActivityPub::EmojiSerializer, - adapter: ActivityPub::Adapter, - content_type: 'application/activity+json' + skip_session! + + render_cached_json(['activitypub', 'emoji', @emoji.cache_key], content_type: 'application/activity+json') do + ActiveModelSerializers::SerializableResource.new(@emoji, serializer: ActivityPub::EmojiSerializer, adapter: ActivityPub::Adapter) + end end end end diff --git a/app/controllers/remote_follow_controller.rb b/app/controllers/remote_follow_controller.rb index 48b026aa5..3b988e08d 100644 --- a/app/controllers/remote_follow_controller.rb +++ b/app/controllers/remote_follow_controller.rb @@ -38,4 +38,8 @@ class RemoteFollowController < ApplicationController def suspended_account? @account.suspended? end + + def set_body_classes + @body_classes = 'modal-layout' + end end diff --git a/app/controllers/shares_controller.rb b/app/controllers/shares_controller.rb index 994742c3d..fc2469dea 100644 --- a/app/controllers/shares_controller.rb +++ b/app/controllers/shares_controller.rb @@ -25,6 +25,6 @@ class SharesController < ApplicationController end def set_body_classes - @body_classes = 'compose-standalone' + @body_classes = 'modal-layout compose-standalone' end end diff --git a/app/controllers/statuses_controller.rb b/app/controllers/statuses_controller.rb index e8a360fb5..367ea34e7 100644 --- a/app/controllers/statuses_controller.rb +++ b/app/controllers/statuses_controller.rb @@ -10,6 +10,7 @@ class StatusesController < ApplicationController before_action :set_link_headers before_action :check_account_suspension before_action :redirect_to_original, only: [:show] + before_action :set_cache_headers def show respond_to do |format| @@ -21,19 +22,21 @@ class StatusesController < ApplicationController end format.json do - render json: @status, - serializer: ActivityPub::NoteSerializer, - adapter: ActivityPub::Adapter, - content_type: 'application/activity+json' + skip_session! unless @stream_entry.hidden? + + render_cached_json(['activitypub', 'note', @status.cache_key], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do + ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::NoteSerializer, adapter: ActivityPub::Adapter) + end end end end def activity - render json: @status, - serializer: ActivityPub::ActivitySerializer, - adapter: ActivityPub::Adapter, - content_type: 'application/activity+json' + skip_session! + + render_cached_json(['activitypub', 'activity', @status.cache_key], content_type: 'application/activity+json', public: !@stream_entry.hidden?) do + ActiveModelSerializers::SerializableResource.new(@status, serializer: ActivityPub::ActivitySerializer, adapter: ActivityPub::Adapter) + end end def embed diff --git a/app/controllers/well_known/host_meta_controller.rb b/app/controllers/well_known/host_meta_controller.rb index 40f96eaa2..5fb70288a 100644 --- a/app/controllers/well_known/host_meta_controller.rb +++ b/app/controllers/well_known/host_meta_controller.rb @@ -1,15 +1,19 @@ # frozen_string_literal: true module WellKnown - class HostMetaController < ApplicationController + class HostMetaController < ActionController::Base include RoutingHelper + before_action { response.headers['Vary'] = 'Accept' } + def show @webfinger_template = "#{webfinger_url}?resource={uri}" respond_to do |format| format.xml { render content_type: 'application/xrd+xml' } end + + expires_in(3.days, public: true) end end end diff --git a/app/controllers/well_known/webfinger_controller.rb b/app/controllers/well_known/webfinger_controller.rb index 5cc606808..28654b61d 100644 --- a/app/controllers/well_known/webfinger_controller.rb +++ b/app/controllers/well_known/webfinger_controller.rb @@ -1,9 +1,11 @@ # frozen_string_literal: true module WellKnown - class WebfingerController < ApplicationController + class WebfingerController < ActionController::Base include RoutingHelper + before_action { response.headers['Vary'] = 'Accept' } + def show @account = Account.find_local!(username_from_resource) @@ -16,6 +18,8 @@ module WellKnown render content_type: 'application/xrd+xml' end end + + expires_in(3.days, public: true) rescue ActiveRecord::RecordNotFound head 404 end diff --git a/app/helpers/admin/action_logs_helper.rb b/app/helpers/admin/action_logs_helper.rb index e85243e57..4475034a5 100644 --- a/app/helpers/admin/action_logs_helper.rb +++ b/app/helpers/admin/action_logs_helper.rb @@ -34,7 +34,7 @@ module Admin::ActionLogsHelper link_to attributes['domain'], "https://#{attributes['domain']}" when 'Status' tmp_status = Status.new(attributes) - link_to tmp_status.account.acct, TagManager.instance.url_for(tmp_status) + link_to tmp_status.account&.acct || "##{tmp_status.account_id}", TagManager.instance.url_for(tmp_status) end end diff --git a/app/helpers/routing_helper.rb b/app/helpers/routing_helper.rb index 11894a895..998b7566f 100644 --- a/app/helpers/routing_helper.rb +++ b/app/helpers/routing_helper.rb @@ -4,6 +4,7 @@ module RoutingHelper extend ActiveSupport::Concern include Rails.application.routes.url_helpers include ActionView::Helpers::AssetTagHelper + include Webpacker::Helper included do def default_url_options @@ -17,6 +18,10 @@ module RoutingHelper URI.join(root_url, source).to_s end + def full_pack_url(source, **options) + full_asset_url(asset_pack_path(source, options)) + end + private def use_storage? diff --git a/app/helpers/settings_helper.rb b/app/helpers/settings_helper.rb index 1d4cb8a57..a63eb5e43 100644 --- a/app/helpers/settings_helper.rb +++ b/app/helpers/settings_helper.rb @@ -28,6 +28,9 @@ module SettingsHelper pt: 'Português', 'pt-BR': 'Português do Brasil', ru: 'Русский', + sk: 'Slovensky', + sr: 'Српски', + 'sr-Latn': 'Srpski (latinica)', sv: 'Svenska', th: 'ภาษาไทย', tr: 'Türkçe', diff --git a/app/javascript/images/mastodon-getting-started.png b/app/javascript/images/mastodon-ui.png similarity index 56% rename from app/javascript/images/mastodon-getting-started.png rename to app/javascript/images/mastodon-ui.png index e05dd493f..a1fb642a0 100644 Binary files a/app/javascript/images/mastodon-getting-started.png and b/app/javascript/images/mastodon-ui.png differ diff --git a/app/javascript/images/wave-compose-standalone.png b/app/javascript/images/wave-compose-standalone.png new file mode 100644 index 000000000..287ee639b Binary files /dev/null and b/app/javascript/images/wave-compose-standalone.png differ diff --git a/app/javascript/images/wave-drawer.png b/app/javascript/images/wave-drawer.png new file mode 100644 index 000000000..ca9f9e1d8 Binary files /dev/null and b/app/javascript/images/wave-drawer.png differ diff --git a/app/javascript/images/wave-modal.png b/app/javascript/images/wave-modal.png new file mode 100644 index 000000000..88818a6d7 Binary files /dev/null and b/app/javascript/images/wave-modal.png differ diff --git a/app/javascript/mastodon/actions/notifications.js b/app/javascript/mastodon/actions/notifications.js index b24ac8b73..502690045 100644 --- a/app/javascript/mastodon/actions/notifications.js +++ b/app/javascript/mastodon/actions/notifications.js @@ -31,7 +31,7 @@ const fetchRelatedRelationships = (dispatch, notifications) => { const unescapeHTML = (html) => { const wrapper = document.createElement('div'); - html = html.replace(/
|
|\n/, ' '); + html = html.replace(/
|
|\n/g, ' '); wrapper.innerHTML = html; return wrapper.textContent; }; diff --git a/app/javascript/mastodon/actions/push_notifications.js b/app/javascript/mastodon/actions/push_notifications.js deleted file mode 100644 index de06385f9..000000000 --- a/app/javascript/mastodon/actions/push_notifications.js +++ /dev/null @@ -1,57 +0,0 @@ -import axios from 'axios'; -import { pushNotificationsSetting } from '../settings'; - -export const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT'; -export const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION'; -export const CLEAR_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_CLEAR_SUBSCRIPTION'; -export const ALERTS_CHANGE = 'PUSH_NOTIFICATIONS_ALERTS_CHANGE'; - -export function setBrowserSupport (value) { - return { - type: SET_BROWSER_SUPPORT, - value, - }; -} - -export function setSubscription (subscription) { - return { - type: SET_SUBSCRIPTION, - subscription, - }; -} - -export function clearSubscription () { - return { - type: CLEAR_SUBSCRIPTION, - }; -} - -export function changeAlerts(key, value) { - return dispatch => { - dispatch({ - type: ALERTS_CHANGE, - key, - value, - }); - - dispatch(saveSettings()); - }; -} - -export function saveSettings() { - return (_, getState) => { - const state = getState().get('push_notifications'); - const subscription = state.get('subscription'); - const alerts = state.get('alerts'); - const data = { alerts }; - - axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, { - data, - }).then(() => { - const me = getState().getIn(['meta', 'me']); - if (me) { - pushNotificationsSetting.set(me, data); - } - }); - }; -} diff --git a/app/javascript/mastodon/actions/push_notifications/index.js b/app/javascript/mastodon/actions/push_notifications/index.js new file mode 100644 index 000000000..2ffec500a --- /dev/null +++ b/app/javascript/mastodon/actions/push_notifications/index.js @@ -0,0 +1,23 @@ +import { + SET_BROWSER_SUPPORT, + SET_SUBSCRIPTION, + CLEAR_SUBSCRIPTION, + SET_ALERTS, + setAlerts, +} from './setter'; +import { register, saveSettings } from './registerer'; + +export { + SET_BROWSER_SUPPORT, + SET_SUBSCRIPTION, + CLEAR_SUBSCRIPTION, + SET_ALERTS, + register, +}; + +export function changeAlerts(path, value) { + return dispatch => { + dispatch(setAlerts(path, value)); + dispatch(saveSettings()); + }; +} diff --git a/app/javascript/mastodon/actions/push_notifications/registerer.js b/app/javascript/mastodon/actions/push_notifications/registerer.js new file mode 100644 index 000000000..1d040bc8c --- /dev/null +++ b/app/javascript/mastodon/actions/push_notifications/registerer.js @@ -0,0 +1,149 @@ +import axios from 'axios'; +import { pushNotificationsSetting } from '../../settings'; +import { setBrowserSupport, setSubscription, clearSubscription } from './setter'; + +// Taken from https://www.npmjs.com/package/web-push +const urlBase64ToUint8Array = (base64String) => { + const padding = '='.repeat((4 - base64String.length % 4) % 4); + const base64 = (base64String + padding) + .replace(/\-/g, '+') + .replace(/_/g, '/'); + + const rawData = window.atob(base64); + const outputArray = new Uint8Array(rawData.length); + + for (let i = 0; i < rawData.length; ++i) { + outputArray[i] = rawData.charCodeAt(i); + } + return outputArray; +}; + +const getApplicationServerKey = () => document.querySelector('[name="applicationServerKey"]').getAttribute('content'); + +const getRegistration = () => navigator.serviceWorker.ready; + +const getPushSubscription = (registration) => + registration.pushManager.getSubscription() + .then(subscription => ({ registration, subscription })); + +const subscribe = (registration) => + registration.pushManager.subscribe({ + userVisibleOnly: true, + applicationServerKey: urlBase64ToUint8Array(getApplicationServerKey()), + }); + +const unsubscribe = ({ registration, subscription }) => + subscription ? subscription.unsubscribe().then(() => registration) : registration; + +const sendSubscriptionToBackend = (subscription, me) => { + const params = { subscription }; + + if (me) { + const data = pushNotificationsSetting.get(me); + if (data) { + params.data = data; + } + } + + return axios.post('/api/web/push_subscriptions', params).then(response => response.data); +}; + +// Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload +const supportsPushNotifications = ('serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype); + +export function register () { + return (dispatch, getState) => { + dispatch(setBrowserSupport(supportsPushNotifications)); + const me = getState().getIn(['meta', 'me']); + + if (me && !pushNotificationsSetting.get(me)) { + const alerts = getState().getIn(['push_notifications', 'alerts']); + if (alerts) { + pushNotificationsSetting.set(me, { alerts: alerts }); + } + } + + if (supportsPushNotifications) { + if (!getApplicationServerKey()) { + console.error('The VAPID public key is not set. You will not be able to receive Web Push Notifications.'); + return; + } + + getRegistration() + .then(getPushSubscription) + .then(({ registration, subscription }) => { + if (subscription !== null) { + // We have a subscription, check if it is still valid + const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey)).toString(); + const subscriptionServerKey = urlBase64ToUint8Array(getApplicationServerKey()).toString(); + const serverEndpoint = getState().getIn(['push_notifications', 'subscription', 'endpoint']); + + // If the VAPID public key did not change and the endpoint corresponds + // to the endpoint saved in the backend, the subscription is valid + if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint) { + return subscription; + } else { + // Something went wrong, try to subscribe again + return unsubscribe({ registration, subscription }).then(subscribe).then( + subscription => sendSubscriptionToBackend(subscription, me)); + } + } + + // No subscription, try to subscribe + return subscribe(registration).then( + subscription => sendSubscriptionToBackend(subscription, me)); + }) + .then(subscription => { + // If we got a PushSubscription (and not a subscription object from the backend) + // it means that the backend subscription is valid (and was set during hydration) + if (!(subscription instanceof PushSubscription)) { + dispatch(setSubscription(subscription)); + if (me) { + pushNotificationsSetting.set(me, { alerts: subscription.alerts }); + } + } + }) + .catch(error => { + if (error.code === 20 && error.name === 'AbortError') { + console.warn('Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.'); + } else if (error.code === 5 && error.name === 'InvalidCharacterError') { + console.error('The VAPID public key seems to be invalid:', getApplicationServerKey()); + } + + // Clear alerts and hide UI settings + dispatch(clearSubscription()); + if (me) { + pushNotificationsSetting.remove(me); + } + + try { + getRegistration() + .then(getPushSubscription) + .then(unsubscribe); + } catch (e) { + + } + }); + } else { + console.warn('Your browser does not support Web Push Notifications.'); + } + }; +} + +export function saveSettings() { + return (_, getState) => { + const state = getState().get('push_notifications'); + const subscription = state.get('subscription'); + const alerts = state.get('alerts'); + const data = { alerts }; + + axios.put(`/api/web/push_subscriptions/${subscription.get('id')}`, { + data, + }).then(() => { + const me = getState().getIn(['meta', 'me']); + if (me) { + pushNotificationsSetting.set(me, data); + } + }); + }; +} diff --git a/app/javascript/mastodon/actions/push_notifications/setter.js b/app/javascript/mastodon/actions/push_notifications/setter.js new file mode 100644 index 000000000..5561766bf --- /dev/null +++ b/app/javascript/mastodon/actions/push_notifications/setter.js @@ -0,0 +1,34 @@ +export const SET_BROWSER_SUPPORT = 'PUSH_NOTIFICATIONS_SET_BROWSER_SUPPORT'; +export const SET_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_SET_SUBSCRIPTION'; +export const CLEAR_SUBSCRIPTION = 'PUSH_NOTIFICATIONS_CLEAR_SUBSCRIPTION'; +export const SET_ALERTS = 'PUSH_NOTIFICATIONS_SET_ALERTS'; + +export function setBrowserSupport (value) { + return { + type: SET_BROWSER_SUPPORT, + value, + }; +} + +export function setSubscription (subscription) { + return { + type: SET_SUBSCRIPTION, + subscription, + }; +} + +export function clearSubscription () { + return { + type: CLEAR_SUBSCRIPTION, + }; +} + +export function setAlerts (path, value) { + return dispatch => { + dispatch({ + type: SET_ALERTS, + path, + value, + }); + }; +} diff --git a/app/javascript/mastodon/actions/settings.js b/app/javascript/mastodon/actions/settings.js index 79adca18c..aeef43527 100644 --- a/app/javascript/mastodon/actions/settings.js +++ b/app/javascript/mastodon/actions/settings.js @@ -4,11 +4,11 @@ import { debounce } from 'lodash'; export const SETTING_CHANGE = 'SETTING_CHANGE'; export const SETTING_SAVE = 'SETTING_SAVE'; -export function changeSetting(key, value) { +export function changeSetting(path, value) { return dispatch => { dispatch({ type: SETTING_CHANGE, - key, + path, value, }); @@ -21,7 +21,7 @@ const debouncedSave = debounce((dispatch, getState) => { return; } - const data = getState().get('settings').filter((_, key) => key !== 'saved').toJS(); + const data = getState().get('settings').filter((_, path) => path !== 'saved').toJS(); axios.put('/api/web/settings', { data }).then(() => dispatch({ type: SETTING_SAVE })); }, 5000, { trailing: true }); diff --git a/app/javascript/mastodon/components/account.js b/app/javascript/mastodon/components/account.js index b0479db4f..81459731c 100644 --- a/app/javascript/mastodon/components/account.js +++ b/app/javascript/mastodon/components/account.js @@ -27,6 +27,7 @@ export default class Account extends ImmutablePureComponent { onFollow: PropTypes.func.isRequired, onBlock: PropTypes.func.isRequired, onMute: PropTypes.func.isRequired, + onMuteNotifications: PropTypes.func.isRequired, intl: PropTypes.object.isRequired, hidden: PropTypes.bool, }; diff --git a/app/javascript/mastodon/features/compose/containers/warning_container.js b/app/javascript/mastodon/features/compose/containers/warning_container.js index d34471a3e..b9f280958 100644 --- a/app/javascript/mastodon/features/compose/containers/warning_container.js +++ b/app/javascript/mastodon/features/compose/containers/warning_container.js @@ -5,20 +5,27 @@ import PropTypes from 'prop-types'; import { FormattedMessage } from 'react-intl'; import { me } from '../../../initial_state'; +const APPROX_HASHTAG_RE = /(?:^|[^\/\)\w])#(\S+)/i; + const mapStateToProps = state => ({ needsLockWarning: state.getIn(['compose', 'privacy']) === 'private' && !state.getIn(['accounts', me, 'locked']), + hashtagWarning: state.getIn(['compose', 'privacy']) !== 'public' && APPROX_HASHTAG_RE.test(state.getIn(['compose', 'text'])), }); -const WarningWrapper = ({ needsLockWarning }) => { +const WarningWrapper = ({ needsLockWarning, hashtagWarning }) => { if (needsLockWarning) { return }} />} />; } + if (hashtagWarning) { + return } />; + } return null; }; WarningWrapper.propTypes = { needsLockWarning: PropTypes.bool, + hashtagWarning: PropTypes.bool, }; export default connect(mapStateToProps)(WarningWrapper); diff --git a/app/javascript/mastodon/features/compose/index.js b/app/javascript/mastodon/features/compose/index.js index 0c66585c9..c3e936ab9 100644 --- a/app/javascript/mastodon/features/compose/index.js +++ b/app/javascript/mastodon/features/compose/index.js @@ -94,6 +94,7 @@ export default class Compose extends React.PureComponent {
+
diff --git a/app/javascript/mastodon/features/getting_started/index.js b/app/javascript/mastodon/features/getting_started/index.js index 4b4c02bcc..ee789e180 100644 --- a/app/javascript/mastodon/features/getting_started/index.js +++ b/app/javascript/mastodon/features/getting_started/index.js @@ -48,7 +48,7 @@ export default class GettingStarted extends ImmutablePureComponent { render () { const { intl, myAccount, columns, multiColumn } = this.props; - let navItems = []; + const navItems = []; if (multiColumn) { if (!columns.find(item => item.get('id') === 'HOME')) { @@ -68,21 +68,20 @@ export default class GettingStarted extends ImmutablePureComponent { } } - navItems = navItems.concat([ + navItems.push( , - , - , - ]); + + ); if (myAccount.get('locked')) { navItems.push(); } - navItems = navItems.concat([ - , - , - , - ]); + if (multiColumn) { + navItems.push(); + } + + navItems.push(); return ( @@ -90,24 +89,24 @@ export default class GettingStarted extends ImmutablePureComponent { {navItems} - + + +
-
-
-

- -

-

- tootsuite/mastodon }} - /> -

-
+
+

+ +

+

+ tootsuite/mastodon }} + /> +

); diff --git a/app/javascript/mastodon/features/home_timeline/components/column_settings.js b/app/javascript/mastodon/features/home_timeline/components/column_settings.js index 43172bd25..0c0c4fa36 100644 --- a/app/javascript/mastodon/features/home_timeline/components/column_settings.js +++ b/app/javascript/mastodon/features/home_timeline/components/column_settings.js @@ -27,11 +27,11 @@ export default class ColumnSettings extends React.PureComponent {
- } /> + } />
- } /> + } />
diff --git a/app/javascript/mastodon/features/keyboard_shortcuts/index.js b/app/javascript/mastodon/features/keyboard_shortcuts/index.js index 22991fcba..8531e03c0 100644 --- a/app/javascript/mastodon/features/keyboard_shortcuts/index.js +++ b/app/javascript/mastodon/features/keyboard_shortcuts/index.js @@ -33,59 +33,59 @@ export default class KeyboardShortcuts extends ImmutablePureComponent { - r + r - m + m - f + f - b + b - enter + enter - up + up - down + down - 1-9 + 1-9 - n + n - alt+n + alt+n - backspace + backspace - s + s - esc + esc - ? + ? diff --git a/app/javascript/mastodon/features/notifications/components/column_settings.js b/app/javascript/mastodon/features/notifications/components/column_settings.js index 88a29d4d3..d9638aaf3 100644 --- a/app/javascript/mastodon/features/notifications/components/column_settings.js +++ b/app/javascript/mastodon/features/notifications/components/column_settings.js @@ -11,12 +11,11 @@ export default class ColumnSettings extends React.PureComponent { settings: ImmutablePropTypes.map.isRequired, pushSettings: ImmutablePropTypes.map.isRequired, onChange: PropTypes.func.isRequired, - onSave: PropTypes.func.isRequired, onClear: PropTypes.func.isRequired, }; - onPushChange = (key, checked) => { - this.props.onChange(['push', ...key], checked); + onPushChange = (path, checked) => { + this.props.onChange(['push', ...path], checked); } render () { @@ -40,10 +39,10 @@ export default class ColumnSettings extends React.PureComponent {
- - {showPushSettings && } - - + + {showPushSettings && } + +
@@ -51,10 +50,10 @@ export default class ColumnSettings extends React.PureComponent {
- - {showPushSettings && } - - + + {showPushSettings && } + +
@@ -62,10 +61,10 @@ export default class ColumnSettings extends React.PureComponent {
- - {showPushSettings && } - - + + {showPushSettings && } + +
@@ -73,10 +72,10 @@ export default class ColumnSettings extends React.PureComponent {
- - {showPushSettings && } - - + + {showPushSettings && } + +
diff --git a/app/javascript/mastodon/features/notifications/components/setting_toggle.js b/app/javascript/mastodon/features/notifications/components/setting_toggle.js index 281359d2a..ac2211e48 100644 --- a/app/javascript/mastodon/features/notifications/components/setting_toggle.js +++ b/app/javascript/mastodon/features/notifications/components/setting_toggle.js @@ -8,23 +8,23 @@ export default class SettingToggle extends React.PureComponent { static propTypes = { prefix: PropTypes.string, settings: ImmutablePropTypes.map.isRequired, - settingKey: PropTypes.array.isRequired, + settingPath: PropTypes.array.isRequired, label: PropTypes.node.isRequired, meta: PropTypes.node, onChange: PropTypes.func.isRequired, } onChange = ({ target }) => { - this.props.onChange(this.props.settingKey, target.checked); + this.props.onChange(this.props.settingPath, target.checked); } render () { - const { prefix, settings, settingKey, label, meta } = this.props; - const id = ['setting-toggle', prefix, ...settingKey].filter(Boolean).join('-'); + const { prefix, settings, settingPath, label, meta } = this.props; + const id = ['setting-toggle', prefix, ...settingPath].filter(Boolean).join('-'); return (
- + {meta && {meta}}
diff --git a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js index d4ead7881..e9cef0a7b 100644 --- a/app/javascript/mastodon/features/notifications/containers/column_settings_container.js +++ b/app/javascript/mastodon/features/notifications/containers/column_settings_container.js @@ -1,9 +1,9 @@ import { connect } from 'react-redux'; import { defineMessages, injectIntl } from 'react-intl'; import ColumnSettings from '../components/column_settings'; -import { changeSetting, saveSettings } from '../../../actions/settings'; +import { changeSetting } from '../../../actions/settings'; import { clearNotifications } from '../../../actions/notifications'; -import { changeAlerts as changePushNotifications, saveSettings as savePushNotificationSettings } from '../../../actions/push_notifications'; +import { changeAlerts as changePushNotifications } from '../../../actions/push_notifications'; import { openModal } from '../../../actions/modal'; const messages = defineMessages({ @@ -18,19 +18,14 @@ const mapStateToProps = state => ({ const mapDispatchToProps = (dispatch, { intl }) => ({ - onChange (key, checked) { - if (key[0] === 'push') { - dispatch(changePushNotifications(key.slice(1), checked)); + onChange (path, checked) { + if (path[0] === 'push') { + dispatch(changePushNotifications(path.slice(1), checked)); } else { - dispatch(changeSetting(['notifications', ...key], checked)); + dispatch(changeSetting(['notifications', ...path], checked)); } }, - onSave () { - dispatch(saveSettings()); - dispatch(savePushNotificationSettings()); - }, - onClear () { dispatch(openModal('CONFIRM', { message: intl.formatMessage(messages.clearMessage), diff --git a/app/javascript/mastodon/features/status/components/action_bar.js b/app/javascript/mastodon/features/status/components/action_bar.js index 99834df6c..13cc10c9c 100644 --- a/app/javascript/mastodon/features/status/components/action_bar.js +++ b/app/javascript/mastodon/features/status/components/action_bar.js @@ -13,6 +13,10 @@ const messages = defineMessages({ reblog: { id: 'status.reblog', defaultMessage: 'Boost' }, cannot_reblog: { id: 'status.cannot_reblog', defaultMessage: 'This post cannot be boosted' }, favourite: { id: 'status.favourite', defaultMessage: 'Favourite' }, + mute: { id: 'status.mute', defaultMessage: 'Mute @{name}' }, + muteConversation: { id: 'status.mute_conversation', defaultMessage: 'Mute conversation' }, + unmuteConversation: { id: 'status.unmute_conversation', defaultMessage: 'Unmute conversation' }, + block: { id: 'status.block', defaultMessage: 'Block @{name}' }, report: { id: 'status.report', defaultMessage: 'Report @{name}' }, share: { id: 'status.share', defaultMessage: 'Share' }, pin: { id: 'status.pin', defaultMessage: 'Pin on profile' }, @@ -34,6 +38,9 @@ export default class ActionBar extends React.PureComponent { onFavourite: PropTypes.func.isRequired, onDelete: PropTypes.func.isRequired, onMention: PropTypes.func.isRequired, + onMute: PropTypes.func, + onMuteConversation: PropTypes.func, + onBlock: PropTypes.func, onReport: PropTypes.func, onPin: PropTypes.func, onEmbed: PropTypes.func, @@ -60,6 +67,18 @@ export default class ActionBar extends React.PureComponent { this.props.onMention(this.props.status.get('account'), this.context.router.history); } + handleMuteClick = () => { + this.props.onMute(this.props.status.get('account')); + } + + handleConversationMuteClick = () => { + this.props.onMuteConversation(this.props.status); + } + + handleBlockClick = () => { + this.props.onBlock(this.props.status.get('account')); + } + handleReport = () => { this.props.onReport(this.props.status); } @@ -83,6 +102,7 @@ export default class ActionBar extends React.PureComponent { const { status, intl } = this.props; const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); + const mutingConversation = status.get('muted'); let menu = []; @@ -95,10 +115,15 @@ export default class ActionBar extends React.PureComponent { menu.push({ text: intl.formatMessage(status.get('pinned') ? messages.unpin : messages.pin), action: this.handlePinClick }); } + menu.push(null); + menu.push({ text: intl.formatMessage(mutingConversation ? messages.unmuteConversation : messages.muteConversation), action: this.handleConversationMuteClick }); + menu.push(null); menu.push({ text: intl.formatMessage(messages.delete), action: this.handleDeleteClick }); } else { menu.push({ text: intl.formatMessage(messages.mention, { name: status.getIn(['account', 'username']) }), action: this.handleMentionClick }); menu.push(null); + menu.push({ text: intl.formatMessage(messages.mute, { name: status.getIn(['account', 'username']) }), action: this.handleMuteClick }); + menu.push({ text: intl.formatMessage(messages.block, { name: status.getIn(['account', 'username']) }), action: this.handleBlockClick }); menu.push({ text: intl.formatMessage(messages.report, { name: status.getIn(['account', 'username']) }), action: this.handleReport }); } diff --git a/app/javascript/mastodon/features/status/index.js b/app/javascript/mastodon/features/status/index.js index cc28ff5fc..73ea9321d 100644 --- a/app/javascript/mastodon/features/status/index.js +++ b/app/javascript/mastodon/features/status/index.js @@ -20,14 +20,16 @@ import { replyCompose, mentionCompose, } from '../../actions/compose'; -import { deleteStatus } from '../../actions/statuses'; +import { blockAccount } from '../../actions/accounts'; +import { muteStatus, unmuteStatus, deleteStatus } from '../../actions/statuses'; +import { initMuteModal } from '../../actions/mutes'; import { initReport } from '../../actions/reports'; import { makeGetStatus } from '../../selectors'; import { ScrollContainer } from 'react-router-scroll-4'; import ColumnBackButton from '../../components/column_back_button'; import StatusContainer from '../../containers/status_container'; import { openModal } from '../../actions/modal'; -import { defineMessages, injectIntl } from 'react-intl'; +import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import ImmutablePureComponent from 'react-immutable-pure-component'; import { HotKeys } from 'react-hotkeys'; import { boostModal, deleteModal } from '../../initial_state'; @@ -36,6 +38,7 @@ import { attachFullscreenListener, detachFullscreenListener, isFullscreen } from const messages = defineMessages({ deleteConfirm: { id: 'confirmations.delete.confirm', defaultMessage: 'Delete' }, deleteMessage: { id: 'confirmations.delete.message', defaultMessage: 'Are you sure you want to delete this status?' }, + blockConfirm: { id: 'confirmations.block.confirm', defaultMessage: 'Block' }, }); const makeMapStateToProps = () => { @@ -148,6 +151,28 @@ export default class Status extends ImmutablePureComponent { this.props.dispatch(openModal('VIDEO', { media, time })); } + handleMuteClick = (account) => { + this.props.dispatch(initMuteModal(account)); + } + + handleConversationMuteClick = (status) => { + if (status.get('muted')) { + this.props.dispatch(unmuteStatus(status.get('id'))); + } else { + this.props.dispatch(muteStatus(status.get('id'))); + } + } + + handleBlockClick = (account) => { + const { dispatch, intl } = this.props; + + dispatch(openModal('CONFIRM', { + message: @{account.get('acct')} }} />, + confirm: intl.formatMessage(messages.blockConfirm), + onConfirm: () => dispatch(blockAccount(account.get('id'))), + })); + } + handleReport = (status) => { this.props.dispatch(initReport(status.get('account'), status)); } @@ -321,6 +346,9 @@ export default class Status extends ImmutablePureComponent { onReblog={this.handleReblogClick} onDelete={this.handleDeleteClick} onMention={this.handleMentionClick} + onMute={this.handleMuteClick} + onMuteConversation={this.handleConversationMuteClick} + onBlock={this.handleBlockClick} onReport={this.handleReport} onPin={this.handlePin} onEmbed={this.handleEmbed} diff --git a/app/javascript/mastodon/features/ui/components/column_link.js b/app/javascript/mastodon/features/ui/components/column_link.js index 5425219c4..a90616213 100644 --- a/app/javascript/mastodon/features/ui/components/column_link.js +++ b/app/javascript/mastodon/features/ui/components/column_link.js @@ -26,7 +26,6 @@ ColumnLink.propTypes = { to: PropTypes.string, href: PropTypes.string, method: PropTypes.string, - hideOnMobile: PropTypes.bool, }; export default ColumnLink; diff --git a/app/javascript/mastodon/locales/ar.json b/app/javascript/mastodon/locales/ar.json index d699a69df..f1bb465d9 100644 --- a/app/javascript/mastodon/locales/ar.json +++ b/app/javascript/mastodon/locales/ar.json @@ -50,6 +50,7 @@ "column_header.unpin": "فك التدبيس", "column_subheading.navigation": "التصفح", "column_subheading.settings": "الإعدادات", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "حسابك ليس {locked}. يمكن لأي شخص متابعتك و عرض المنشورات.", "compose_form.lock_disclaimer.lock": "مقفل", "compose_form.placeholder": "فيمَ تفكّر؟", @@ -213,6 +214,7 @@ "search_popout.tips.user": "مستخدِم", "search_results.total": "{count, number} {count, plural, one {result} و {results}}", "standalone.public_title": "نظرة على ...", + "status.block": "Block @{name}", "status.cannot_reblog": "تعذرت ترقية هذا المنشور", "status.delete": "إحذف", "status.embed": "إدماج", @@ -221,6 +223,7 @@ "status.media_hidden": "الصورة مستترة", "status.mention": "أذكُر @{name}", "status.more": "المزيد", + "status.mute": "Mute @{name}", "status.mute_conversation": "كتم المحادثة", "status.open": "وسع هذه المشاركة", "status.pin": "تدبيس على الملف الشخصي", diff --git a/app/javascript/mastodon/locales/bg.json b/app/javascript/mastodon/locales/bg.json index 1c04b3bfa..c0a24dacb 100644 --- a/app/javascript/mastodon/locales/bg.json +++ b/app/javascript/mastodon/locales/bg.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", "compose_form.placeholder": "Какво си мислиш?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Изтриване", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Media hidden", "status.mention": "Споменаване", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", "status.open": "Expand this status", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/ca.json b/app/javascript/mastodon/locales/ca.json index 62d85a5e1..3d2fe2839 100644 --- a/app/javascript/mastodon/locales/ca.json +++ b/app/javascript/mastodon/locales/ca.json @@ -50,6 +50,7 @@ "column_header.unpin": "Deslligar", "column_subheading.navigation": "Navegació", "column_subheading.settings": "Configuració", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "El teu compte no està bloquejat {locked}. Tothom pot seguir-te i veure els teus missatges a seguidors.", "compose_form.lock_disclaimer.lock": "bloquejat", "compose_form.placeholder": "En què estàs pensant?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "usuari", "search_results.total": "{count, number} {count, plural, un {result} altres {results}}", "standalone.public_title": "Una mirada a l'interior ...", + "status.block": "Block @{name}", "status.cannot_reblog": "Aquesta publicació no pot ser retootejada", "status.delete": "Esborrar", "status.embed": "Incrustar", @@ -221,6 +223,7 @@ "status.media_hidden": "Multimèdia amagat", "status.mention": "Esmentar @{name}", "status.more": "Més", + "status.mute": "Mute @{name}", "status.mute_conversation": "Silenciar conversació", "status.open": "Ampliar aquest estat", "status.pin": "Fixat en el perfil", diff --git a/app/javascript/mastodon/locales/de.json b/app/javascript/mastodon/locales/de.json index 6354f18b6..9b6c857e4 100644 --- a/app/javascript/mastodon/locales/de.json +++ b/app/javascript/mastodon/locales/de.json @@ -50,6 +50,7 @@ "column_header.unpin": "Lösen", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Einstellungen", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Dein Profil ist nicht {locked}. Wer dir folgen will, kann das jederzeit tun und dann auch deine privaten Beiträge sehen.", "compose_form.lock_disclaimer.lock": "gesperrt", "compose_form.placeholder": "Worüber möchtest du schreiben?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {Ergebnis} other {Ergebnisse}}", "standalone.public_title": "Ein kleiner Einblick …", + "status.block": "Block @{name}", "status.cannot_reblog": "Dieser Beitrag kann nicht geteilt werden", "status.delete": "Löschen", "status.embed": "Einbetten", @@ -221,6 +223,7 @@ "status.media_hidden": "Medien versteckt", "status.mention": "@{name} erwähnen", "status.more": "Mehr", + "status.mute": "Mute @{name}", "status.mute_conversation": "Thread stummschalten", "status.open": "Diesen Beitrag öffnen", "status.pin": "Im Profil anheften", diff --git a/app/javascript/mastodon/locales/defaultMessages.json b/app/javascript/mastodon/locales/defaultMessages.json index bb82cf5f5..797054d57 100644 --- a/app/javascript/mastodon/locales/defaultMessages.json +++ b/app/javascript/mastodon/locales/defaultMessages.json @@ -727,6 +727,10 @@ { "defaultMessage": "locked", "id": "compose_form.lock_disclaimer.lock" + }, + { + "defaultMessage": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "id": "compose_form.hashtag_warning" } ], "path": "app/javascript/mastodon/features/compose/containers/warning_container.json" @@ -1053,7 +1057,7 @@ "id": "lists.delete" }, { - "defaultMessage": "There is nothing in this list yet.", + "defaultMessage": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", "id": "empty_column.list" } ], @@ -1244,6 +1248,22 @@ "defaultMessage": "Favourite", "id": "status.favourite" }, + { + "defaultMessage": "Mute @{name}", + "id": "status.mute" + }, + { + "defaultMessage": "Mute conversation", + "id": "status.mute_conversation" + }, + { + "defaultMessage": "Unmute conversation", + "id": "status.unmute_conversation" + }, + { + "defaultMessage": "Block @{name}", + "id": "status.block" + }, { "defaultMessage": "Report @{name}", "id": "status.report" @@ -1276,6 +1296,14 @@ { "defaultMessage": "Are you sure you want to delete this status?", "id": "confirmations.delete.message" + }, + { + "defaultMessage": "Block", + "id": "confirmations.block.confirm" + }, + { + "defaultMessage": "Are you sure you want to block {name}?", + "id": "confirmations.block.message" } ], "path": "app/javascript/mastodon/features/status/index.json" diff --git a/app/javascript/mastodon/locales/en.json b/app/javascript/mastodon/locales/en.json index 5c39bd682..d214fe85f 100644 --- a/app/javascript/mastodon/locales/en.json +++ b/app/javascript/mastodon/locales/en.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", "compose_form.placeholder": "What is on your mind?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Delete", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Media hidden", "status.mention": "Mention @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", "status.open": "Expand this status", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/eo.json b/app/javascript/mastodon/locales/eo.json index 9e66c379f..eab8c09a6 100644 --- a/app/javascript/mastodon/locales/eo.json +++ b/app/javascript/mastodon/locales/eo.json @@ -50,6 +50,7 @@ "column_header.unpin": "Depingli", "column_subheading.navigation": "Navigado", "column_subheading.settings": "Agordoj", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Via konta ne estas ŝlosita. Iu ajn povas sekvi vin por vidi viajn privatajn pepojn.", "compose_form.lock_disclaimer.lock": "ŝlosita", "compose_form.placeholder": "Pri kio vi pensas?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "uzanto", "search_results.total": "{count, number} {count, plural, one {rezultato} other {rezultatoj}}", "standalone.public_title": "Rigardeti…", + "status.block": "Block @{name}", "status.cannot_reblog": "Tiun publikaĵon oni ne povas diskonigi", "status.delete": "Forigi", "status.embed": "Enmeti", @@ -221,6 +223,7 @@ "status.media_hidden": "Sonbildaĵo kaŝita", "status.mention": "Mencii @{name}", "status.more": "Pli", + "status.mute": "Mute @{name}", "status.mute_conversation": "Silentigi konversacion", "status.open": "Disfaldi statkonigon", "status.pin": "Pingli al la profilo", diff --git a/app/javascript/mastodon/locales/es.json b/app/javascript/mastodon/locales/es.json index 6122a79ab..8a8110b1e 100644 --- a/app/javascript/mastodon/locales/es.json +++ b/app/javascript/mastodon/locales/es.json @@ -50,6 +50,7 @@ "column_header.unpin": "Dejar de fijar", "column_subheading.navigation": "Navegación", "column_subheading.settings": "Ajustes", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Tu cuenta no está bloqueada. Todos pueden seguirte para ver tus toots solo para seguidores.", "compose_form.lock_disclaimer.lock": "bloqueado", "compose_form.placeholder": "¿En qué estás pensando?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "usuario", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "standalone.public_title": "Un pequeño vistazo...", + "status.block": "Block @{name}", "status.cannot_reblog": "Este toot no puede retootearse", "status.delete": "Borrar", "status.embed": "Incrustado", @@ -221,6 +223,7 @@ "status.media_hidden": "Contenido multimedia oculto", "status.mention": "Mencionar", "status.more": "Más", + "status.mute": "Mute @{name}", "status.mute_conversation": "Silenciar conversación", "status.open": "Expandir estado", "status.pin": "Fijar", diff --git a/app/javascript/mastodon/locales/fa.json b/app/javascript/mastodon/locales/fa.json index 75057a7dd..f6c6f5ced 100644 --- a/app/javascript/mastodon/locales/fa.json +++ b/app/javascript/mastodon/locales/fa.json @@ -50,6 +50,7 @@ "column_header.unpin": "رهاکردن", "column_subheading.navigation": "گشت و گذار", "column_subheading.settings": "تنظیمات", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "حساب شما {locked} نیست. هر کسی می‌تواند پیگیر شما شود و نوشته‌های ویژهٔ پیگیران شما را ببیند.", "compose_form.lock_disclaimer.lock": "قفل", "compose_form.placeholder": "تازه چه خبر؟", @@ -213,6 +214,7 @@ "search_popout.tips.user": "کاربر", "search_results.total": "{count, number} {count, plural, one {نتیجه} other {نتیجه}}", "standalone.public_title": "نگاهی به کاربران این سرور...", + "status.block": "Block @{name}", "status.cannot_reblog": "این نوشته را نمی‌شود بازبوقید", "status.delete": "پاک‌کردن", "status.embed": "جاگذاری", @@ -221,6 +223,7 @@ "status.media_hidden": "تصویر پنهان شده", "status.mention": "نام‌بردن از @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "بی‌صداکردن گفتگو", "status.open": "این نوشته را باز کن", "status.pin": "نوشتهٔ ثابت نمایه", diff --git a/app/javascript/mastodon/locales/fi.json b/app/javascript/mastodon/locales/fi.json index 4ddc1cca7..74ab699c4 100644 --- a/app/javascript/mastodon/locales/fi.json +++ b/app/javascript/mastodon/locales/fi.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", "compose_form.placeholder": "Mitä sinulla on mielessä?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Poista", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Media hidden", "status.mention": "Mainitse @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", "status.open": "Expand this status", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/fr.json b/app/javascript/mastodon/locales/fr.json index ecfff87c8..e77107fc5 100644 --- a/app/javascript/mastodon/locales/fr.json +++ b/app/javascript/mastodon/locales/fr.json @@ -50,6 +50,7 @@ "column_header.unpin": "Retirer", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Paramètres", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Votre compte n’est pas {locked}. Tout le monde peut vous suivre et voir vos pouets privés.", "compose_form.lock_disclaimer.lock": "verrouillé", "compose_form.placeholder": "Qu’avez-vous en tête ?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "utilisateur⋅ice", "search_results.total": "{count, number} {count, plural, one {résultat} other {résultats}}", "standalone.public_title": "Jeter un coup d’œil…", + "status.block": "Block @{name}", "status.cannot_reblog": "Cette publication ne peut être boostée", "status.delete": "Effacer", "status.embed": "Intégrer", @@ -221,6 +223,7 @@ "status.media_hidden": "Média caché", "status.mention": "Mentionner", "status.more": "Plus", + "status.mute": "Mute @{name}", "status.mute_conversation": "Masquer la conversation", "status.open": "Déplier ce statut", "status.pin": "Épingler sur le profil", diff --git a/app/javascript/mastodon/locales/gl.json b/app/javascript/mastodon/locales/gl.json index 6398daa11..523dcc924 100644 --- a/app/javascript/mastodon/locales/gl.json +++ b/app/javascript/mastodon/locales/gl.json @@ -50,6 +50,7 @@ "column_header.unpin": "Soltar", "column_subheading.navigation": "Navegación", "column_subheading.settings": "Axustes", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "A súa conta non está {locked}. Calquera pode seguila para ver as súas mensaxes só-para-seguidoras.", "compose_form.lock_disclaimer.lock": "bloqueado", "compose_form.placeholder": "A qué andas?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "usuaria", "search_results.total": "{count, number} {count,plural,one {result} outros {results}}", "standalone.public_title": "Ollada dentro...", + "status.block": "Block @{name}", "status.cannot_reblog": "Esta mensaxe non pode ser promocionada", "status.delete": "Eliminar", "status.embed": "Incrustar", @@ -221,6 +223,7 @@ "status.media_hidden": "Medios ocultos", "status.mention": "Mencionar @{name}", "status.more": "Máis", + "status.mute": "Mute @{name}", "status.mute_conversation": "Acalar conversa", "status.open": "Expandir este estado", "status.pin": "Fixar no perfil", diff --git a/app/javascript/mastodon/locales/he.json b/app/javascript/mastodon/locales/he.json index 5444c8e34..2eb186173 100644 --- a/app/javascript/mastodon/locales/he.json +++ b/app/javascript/mastodon/locales/he.json @@ -50,6 +50,7 @@ "column_header.unpin": "שחרור קיבוע", "column_subheading.navigation": "ניווט", "column_subheading.settings": "אפשרויות", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "חשבונך אינו {locked}. כל אחד יוכל לעקוב אחריך כדי לקרוא את הודעותיך המיועדות לעוקבים בלבד.", "compose_form.lock_disclaimer.lock": "נעול", "compose_form.placeholder": "מה עובר לך בראש?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "משתמש(ת)", "search_results.total": "{count, number} {count, plural, one {תוצאה} other {תוצאות}}", "standalone.public_title": "הצצה פנימה...", + "status.block": "Block @{name}", "status.cannot_reblog": "לא ניתן להדהד הודעה זו", "status.delete": "מחיקה", "status.embed": "הטמעה", @@ -221,6 +223,7 @@ "status.media_hidden": "מדיה מוסתרת", "status.mention": "פניה אל @{name}", "status.more": "עוד", + "status.mute": "Mute @{name}", "status.mute_conversation": "השתקת שיחה", "status.open": "הרחבת הודעה", "status.pin": "לקבע באודות", diff --git a/app/javascript/mastodon/locales/hr.json b/app/javascript/mastodon/locales/hr.json index f70c66223..00dea67f7 100644 --- a/app/javascript/mastodon/locales/hr.json +++ b/app/javascript/mastodon/locales/hr.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigacija", "column_subheading.settings": "Postavke", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Tvoj račun nije {locked}. Svatko te može slijediti kako bi vidio postove namijenjene samo tvojim sljedbenicima.", "compose_form.lock_disclaimer.lock": "zaključan", "compose_form.placeholder": "Što ti je na umu?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "Ovaj post ne može biti boostan", "status.delete": "Obriši", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Sakriven media sadržaj", "status.mention": "Spomeni @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Utišaj razgovor", "status.open": "Proširi ovaj status", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/hu.json b/app/javascript/mastodon/locales/hu.json index 7cb816fe9..e1048519b 100644 --- a/app/javascript/mastodon/locales/hu.json +++ b/app/javascript/mastodon/locales/hu.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", "compose_form.placeholder": "Mire gondolsz?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Törlés", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Media hidden", "status.mention": "Említés", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", "status.open": "Expand this status", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/id.json b/app/javascript/mastodon/locales/id.json index 429b77182..0942bc33c 100644 --- a/app/javascript/mastodon/locales/id.json +++ b/app/javascript/mastodon/locales/id.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigasi", "column_subheading.settings": "Pengaturan", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Akun anda tidak {locked}. Semua orang dapat mengikuti anda untuk melihat postingan khusus untuk pengikut anda.", "compose_form.lock_disclaimer.lock": "dikunci", "compose_form.placeholder": "Apa yang ada di pikiran anda?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count} {count, plural, one {hasil} other {hasil}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Hapus", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Media disembunyikan", "status.mention": "Balasan @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", "status.open": "Tampilkan status ini", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/io.json b/app/javascript/mastodon/locales/io.json index 3e5c8edb9..cfd8e299f 100644 --- a/app/javascript/mastodon/locales/io.json +++ b/app/javascript/mastodon/locales/io.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", "compose_form.placeholder": "Quo esas en tua spirito?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {rezulto} other {rezulti}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Efacar", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Kontenajo celita", "status.mention": "Mencionar @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", "status.open": "Detaligar ca mesajo", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/it.json b/app/javascript/mastodon/locales/it.json index e2ad1632a..e14fa410c 100644 --- a/app/javascript/mastodon/locales/it.json +++ b/app/javascript/mastodon/locales/it.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", "compose_form.placeholder": "A cosa stai pensando?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count} {count, plural, one {risultato} other {risultati}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Elimina", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Allegato nascosto", "status.mention": "Nomina @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", "status.open": "Espandi questo post", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/ja.json b/app/javascript/mastodon/locales/ja.json index 2d4ec531c..e7f15691c 100644 --- a/app/javascript/mastodon/locales/ja.json +++ b/app/javascript/mastodon/locales/ja.json @@ -50,6 +50,7 @@ "column_header.unpin": "ピン留めを外す", "column_subheading.navigation": "ナビゲーション", "column_subheading.settings": "設定", + "compose_form.hashtag_warning": "このトゥートは未収載なのでハッシュタグの一覧に表示されません。公開トゥートだけがハッシュタグで検索できます。", "compose_form.lock_disclaimer": "あなたのアカウントは{locked}になっていません。誰でもあなたをフォローすることができ、フォロワー限定の投稿を見ることができます。", "compose_form.lock_disclaimer.lock": "非公開", "compose_form.placeholder": "今なにしてる?", @@ -91,7 +92,7 @@ "empty_column.hashtag": "このハッシュタグはまだ使われていません。", "empty_column.home": "まだ誰もフォローしていません。{public}を見に行くか、検索を使って他のユーザーを見つけましょう。", "empty_column.home.public_timeline": "連合タイムライン", - "empty_column.list": "このリストにはまだなにもありません。", + "empty_column.list": "このリストにはまだなにもありません。このリストのメンバーが新しいトゥートをするとここに表示されます。", "empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。", "empty_column.public": "ここにはまだ何もありません! 公開で何かを投稿したり、他のインスタンスのユーザーをフォローしたりしていっぱいにしましょう", "follow_request.authorize": "許可", @@ -213,6 +214,7 @@ "search_popout.tips.user": "ユーザー", "search_results.total": "{count, number}件の結果", "standalone.public_title": "今こんな話をしています...", + "status.block": "@{name}をブロック", "status.cannot_reblog": "この投稿はブーストできません", "status.delete": "削除", "status.embed": "埋め込み", @@ -221,6 +223,7 @@ "status.media_hidden": "非表示のメディア", "status.mention": "返信", "status.more": "もっと見る", + "status.mute": "@{name}をミュート", "status.mute_conversation": "会話をミュート", "status.open": "詳細を表示", "status.pin": "プロフィールに固定表示", diff --git a/app/javascript/mastodon/locales/ko.json b/app/javascript/mastodon/locales/ko.json index 472a52a99..321e3ce47 100644 --- a/app/javascript/mastodon/locales/ko.json +++ b/app/javascript/mastodon/locales/ko.json @@ -1,55 +1,56 @@ { "account.block": "차단", "account.block_domain": "{domain} 전체를 숨김", - "account.disclaimer_full": "Information below may reflect the user's profile incompletely.", + "account.disclaimer_full": "여기 있는 정보는 유저의 프로파일을 정확히 반영하지 못 할 수도 있습니다.", "account.edit_profile": "프로필 편집", "account.follow": "팔로우", "account.followers": "팔로워", "account.follows": "팔로우", "account.follows_you": "날 팔로우합니다", - "account.hide_reblogs": "Hide boosts from @{name}", + "account.hide_reblogs": "@{name}의 부스트를 숨기기", "account.media": "미디어", "account.mention": "답장", - "account.moved_to": "{name} has moved to:", + "account.moved_to": "{name}는 계정을 이동했습니다:", "account.mute": "뮤트", - "account.mute_notifications": "Mute notifications from @{name}", + "account.mute_notifications": "@{name}의 알림을 뮤트", "account.posts": "포스트", "account.report": "신고", "account.requested": "승인 대기 중", - "account.share": "Share @{name}'s profile", - "account.show_reblogs": "Show boosts from @{name}", + "account.share": "@{name}의 프로파일 공유", + "account.show_reblogs": "@{name}의 부스트 보기", "account.unblock": "차단 해제", "account.unblock_domain": "{domain} 숨김 해제", "account.unfollow": "팔로우 해제", "account.unmute": "뮤트 해제", - "account.unmute_notifications": "Unmute notifications from @{name}", + "account.unmute_notifications": "@{name}의 알림 뮤트 해제", "account.view_full_profile": "전체 프로필 보기", "boost_modal.combo": "다음부터 {combo}를 누르면 이 과정을 건너뛸 수 있습니다.", "bundle_column_error.body": "Something went wrong while loading this component.", - "bundle_column_error.retry": "Try again", - "bundle_column_error.title": "Network error", - "bundle_modal_error.close": "Close", + "bundle_column_error.retry": "다시 시도", + "bundle_column_error.title": "네트워크 에러", + "bundle_modal_error.close": "닫기", "bundle_modal_error.message": "Something went wrong while loading this component.", - "bundle_modal_error.retry": "Try again", + "bundle_modal_error.retry": "다시 시도", "column.blocks": "차단 중인 사용자", "column.community": "로컬 타임라인", "column.favourites": "즐겨찾기", "column.follow_requests": "팔로우 요청", "column.home": "홈", - "column.lists": "Lists", + "column.lists": "리스트", "column.mutes": "뮤트 중인 사용자", "column.notifications": "알림", "column.pins": "고정된 툿", "column.public": "연합 타임라인", "column_back_button.label": "돌아가기", - "column_header.hide_settings": "Hide settings", - "column_header.moveLeft_settings": "Move column to the left", - "column_header.moveRight_settings": "Move column to the right", + "column_header.hide_settings": "설정 숨기기", + "column_header.moveLeft_settings": "왼쪽으로 이동", + "column_header.moveRight_settings": "오른쪽으로 이동", "column_header.pin": "고정하기", - "column_header.show_settings": "Show settings", + "column_header.show_settings": "설정 보이기", "column_header.unpin": "고정 해제", "column_subheading.navigation": "내비게이션", "column_subheading.settings": "설정", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "이 계정은 {locked}로 설정 되어 있지 않습니다. 누구나 이 계정을 팔로우 할 수 있으며, 팔로워 공개의 포스팅을 볼 수 있습니다.", "compose_form.lock_disclaimer.lock": "비공개", "compose_form.placeholder": "지금 무엇을 하고 있나요?", @@ -63,35 +64,35 @@ "confirmations.block.message": "정말로 {name}를 차단하시겠습니까?", "confirmations.delete.confirm": "삭제", "confirmations.delete.message": "정말로 삭제하시겠습니까?", - "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.delete_list.confirm": "삭제", + "confirmations.delete_list.message": "정말로 이 리스트를 삭제하시겠습니까?", "confirmations.domain_block.confirm": "도메인 전체를 숨김", "confirmations.domain_block.message": "정말로 {domain} 전체를 숨기시겠습니까? 대부분의 경우 개별 차단이나 뮤트로 충분합니다.", "confirmations.mute.confirm": "뮤트", "confirmations.mute.message": "정말로 {name}를 뮤트하시겠습니까?", - "confirmations.unfollow.confirm": "Unfollow", - "confirmations.unfollow.message": "Are you sure you want to unfollow {name}?", + "confirmations.unfollow.confirm": "언팔로우", + "confirmations.unfollow.message": "정말로 {name}를 언팔로우하시겠습니까?", "embed.instructions": "아래의 코드를 복사하여 대화를 원하는 곳으로 공유하세요.", "embed.preview": "다음과 같이 표시됩니다:", "emoji_button.activity": "활동", - "emoji_button.custom": "Custom", + "emoji_button.custom": "커스텀", "emoji_button.flags": "국기", "emoji_button.food": "음식", "emoji_button.label": "emoji를 추가", "emoji_button.nature": "자연", - "emoji_button.not_found": "No emojos!! (╯°□°)╯︵ ┻━┻", + "emoji_button.not_found": "없어!! (╯°□°)╯︵ ┻━┻", "emoji_button.objects": "물건", "emoji_button.people": "사람들", - "emoji_button.recent": "Frequently used", + "emoji_button.recent": "자주 사용 됨", "emoji_button.search": "검색...", - "emoji_button.search_results": "Search results", + "emoji_button.search_results": "검색 결과", "emoji_button.symbols": "기호", "emoji_button.travel": "여행과 장소", "empty_column.community": "로컬 타임라인에 아무 것도 없습니다. 아무거나 적어 보세요!", "empty_column.hashtag": "이 해시태그는 아직 사용되지 않았습니다.", "empty_column.home": "아직 아무도 팔로우 하고 있지 않습니다. {public}를 보러 가거나, 검색하여 다른 사용자를 찾아 보세요.", "empty_column.home.public_timeline": "연합 타임라인", - "empty_column.list": "There is nothing in this list yet.", + "empty_column.list": "리스트에 아직 아무 것도 없습니다.", "empty_column.notifications": "아직 알림이 없습니다. 다른 사람과 대화를 시작해 보세요!", "empty_column.public": "여기엔 아직 아무 것도 없습니다! 공개적으로 무언가 포스팅하거나, 다른 인스턴스 유저를 팔로우 해서 가득 채워보세요!", "follow_request.authorize": "허가", @@ -107,46 +108,46 @@ "home.column_settings.show_reblogs": "부스트 표시", "home.column_settings.show_replies": "답글 표시", "home.settings": "컬럼 설정", - "keyboard_shortcuts.back": "to navigate back", - "keyboard_shortcuts.boost": "to boost", - "keyboard_shortcuts.column": "to focus a status in one of the columns", - "keyboard_shortcuts.compose": "to focus the compose textarea", - "keyboard_shortcuts.description": "Description", - "keyboard_shortcuts.down": "to move down in the list", - "keyboard_shortcuts.enter": "to open status", - "keyboard_shortcuts.favourite": "to favourite", - "keyboard_shortcuts.heading": "Keyboard Shortcuts", - "keyboard_shortcuts.hotkey": "Hotkey", - "keyboard_shortcuts.legend": "to display this legend", - "keyboard_shortcuts.mention": "to mention author", - "keyboard_shortcuts.reply": "to reply", - "keyboard_shortcuts.search": "to focus search", - "keyboard_shortcuts.toot": "to start a brand new toot", - "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", - "keyboard_shortcuts.up": "to move up in the list", + "keyboard_shortcuts.back": "뒤로가기", + "keyboard_shortcuts.boost": "부스트", + "keyboard_shortcuts.column": "해당 열에 포커스", + "keyboard_shortcuts.compose": "작성창으로 포커스", + "keyboard_shortcuts.description": "설명", + "keyboard_shortcuts.down": "리스트에서 아래로 이동", + "keyboard_shortcuts.enter": "열기", + "keyboard_shortcuts.favourite": "관심글 지정", + "keyboard_shortcuts.heading": "키보드 단축키", + "keyboard_shortcuts.hotkey": "핫키", + "keyboard_shortcuts.legend": "이 도움말 표시", + "keyboard_shortcuts.mention": "멘션", + "keyboard_shortcuts.reply": "답장", + "keyboard_shortcuts.search": "검색창에 포커스", + "keyboard_shortcuts.toot": "새 툿 작성", + "keyboard_shortcuts.unfocus": "작성창에서 포커스 해제", + "keyboard_shortcuts.up": "리스트에서 위로 이동", "lightbox.close": "닫기", - "lightbox.next": "Next", - "lightbox.previous": "Previous", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", - "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", + "lightbox.next": "다음", + "lightbox.previous": "이전", + "lists.account.add": "리스트에 추가", + "lists.account.remove": "리스트에서 제거", + "lists.delete": "리스트 삭제", + "lists.edit": "리스트 편집", + "lists.new.create": "리스트 추가", + "lists.new.title_placeholder": "새 리스트의 이름", + "lists.search": "팔로우 중인 사람들 중에서 찾기", "lists.subheading": "Your lists", "loading_indicator.label": "불러오는 중...", "media_gallery.toggle_visible": "표시 전환", "missing_indicator.label": "찾을 수 없습니다", - "mute_modal.hide_notifications": "Hide notifications from this user?", + "mute_modal.hide_notifications": "이 사용자로부터의 알림을 뮤트하시겠습니까?", "navigation_bar.blocks": "차단한 사용자", "navigation_bar.community_timeline": "로컬 타임라인", "navigation_bar.edit_profile": "프로필 편집", "navigation_bar.favourites": "즐겨찾기", "navigation_bar.follow_requests": "팔로우 요청", "navigation_bar.info": "이 인스턴스에 대해서", - "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts", - "navigation_bar.lists": "Lists", + "navigation_bar.keyboard_shortcuts": "키보드 단축키", + "navigation_bar.lists": "리스트", "navigation_bar.logout": "로그아웃", "navigation_bar.mutes": "뮤트 중인 사용자", "navigation_bar.pins": "고정된 툿", @@ -162,8 +163,8 @@ "notifications.column_settings.favourite": "즐겨찾기", "notifications.column_settings.follow": "새 팔로워", "notifications.column_settings.mention": "답글", - "notifications.column_settings.push": "Push notifications", - "notifications.column_settings.push_meta": "This device", + "notifications.column_settings.push": "푸시 알림", + "notifications.column_settings.push_meta": "이 장치", "notifications.column_settings.reblog": "부스트", "notifications.column_settings.show": "컬럼에 표시", "notifications.column_settings.sound": "효과음 재생", @@ -213,6 +214,7 @@ "search_popout.tips.user": "유저", "search_results.total": "{count, number}건의 결과", "standalone.public_title": "A look inside...", + "status.block": "@{name} 차단", "status.cannot_reblog": "이 포스트는 부스트 할 수 없습니다", "status.delete": "삭제", "status.embed": "공유하기", @@ -220,7 +222,8 @@ "status.load_more": "더 보기", "status.media_hidden": "미디어 숨겨짐", "status.mention": "답장", - "status.more": "More", + "status.more": "자세히", + "status.mute": "@{name} 뮤트", "status.mute_conversation": "이 대화를 뮤트", "status.open": "상세 정보 표시", "status.pin": "고정", @@ -231,7 +234,7 @@ "status.report": "신고", "status.sensitive_toggle": "클릭해서 표시하기", "status.sensitive_warning": "민감한 미디어", - "status.share": "Share", + "status.share": "공유", "status.show_less": "숨기기", "status.show_more": "더 보기", "status.unmute_conversation": "이 대화의 뮤트 해제하기", @@ -241,19 +244,19 @@ "tabs_bar.home": "홈", "tabs_bar.local_timeline": "로컬", "tabs_bar.notifications": "알림", - "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "ui.beforeunload": "지금 나가면 저장되지 않은 항목을 잃게 됩니다.", "upload_area.title": "드래그 & 드롭으로 업로드", "upload_button.label": "미디어 추가", "upload_form.description": "Describe for the visually impaired", "upload_form.undo": "재시도", "upload_progress.label": "업로드 중...", - "video.close": "Close video", - "video.exit_fullscreen": "Exit full screen", - "video.expand": "Expand video", - "video.fullscreen": "Full screen", - "video.hide": "Hide video", - "video.mute": "Mute sound", - "video.pause": "Pause", - "video.play": "Play", - "video.unmute": "Unmute sound" + "video.close": "동영상 닫기", + "video.exit_fullscreen": "전체화면 나가기", + "video.expand": "동영상 확장", + "video.fullscreen": "전체화면", + "video.hide": "동영상 숨기기", + "video.mute": "음소거", + "video.pause": "일시정지", + "video.play": "재생", + "video.unmute": "음소거 해제" } diff --git a/app/javascript/mastodon/locales/nl.json b/app/javascript/mastodon/locales/nl.json index e154d1ab2..f85cc75c5 100644 --- a/app/javascript/mastodon/locales/nl.json +++ b/app/javascript/mastodon/locales/nl.json @@ -50,6 +50,7 @@ "column_header.unpin": "Losmaken", "column_subheading.navigation": "Navigatie", "column_subheading.settings": "Instellingen", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Jouw account is niet {locked}. Iedereen kan jou volgen en toots zien die je alleen aan volgers hebt gericht.", "compose_form.lock_disclaimer.lock": "besloten", "compose_form.placeholder": "Wat wil je kwijt?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "gebruiker", "search_results.total": "{count, number} {count, plural, one {resultaat} other {resultaten}}", "standalone.public_title": "Een kijkje binnenin...", + "status.block": "Block @{name}", "status.cannot_reblog": "Deze toot kan niet geboost worden", "status.delete": "Verwijderen", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Media verborgen", "status.mention": "Vermeld @{name}", "status.more": "Meer", + "status.mute": "Mute @{name}", "status.mute_conversation": "Negeer conversatie", "status.open": "Toot volledig tonen", "status.pin": "Aan profielpagina vastmaken", diff --git a/app/javascript/mastodon/locales/no.json b/app/javascript/mastodon/locales/no.json index bf2b6259a..5fbc51ff3 100644 --- a/app/javascript/mastodon/locales/no.json +++ b/app/javascript/mastodon/locales/no.json @@ -50,6 +50,7 @@ "column_header.unpin": "Løsne", "column_subheading.navigation": "Navigasjon", "column_subheading.settings": "Innstillinger", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Din konto er ikke {locked}. Hvem som helst kan følge deg og se dine private poster.", "compose_form.lock_disclaimer.lock": "låst", "compose_form.placeholder": "Hva har du på hjertet?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {resultat} other {resultater}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "Denne posten kan ikke fremheves", "status.delete": "Slett", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Media skjult", "status.mention": "Nevn @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Demp samtale", "status.open": "Utvid denne statusen", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/oc.json b/app/javascript/mastodon/locales/oc.json index 0d1f7c971..6ebd40f5b 100644 --- a/app/javascript/mastodon/locales/oc.json +++ b/app/javascript/mastodon/locales/oc.json @@ -50,6 +50,7 @@ "column_header.unpin": "Despenjar", "column_subheading.navigation": "Navigacion", "column_subheading.settings": "Paramètres", + "compose_form.hashtag_warning": "Aqueste tut serà pas ligat a cap etiqueta estant qu’es pas listat. Òm pas cercar que los tuts publics per etiqueta.", "compose_form.lock_disclaimer": "Vòstre compte es pas {locked}. Tot lo mond pòt vos sègre e veire los estatuts reservats als seguidors.", "compose_form.lock_disclaimer.lock": "clavat", "compose_form.placeholder": "A de qué pensatz ?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "utilizaire", "search_results.total": "{count, number} {count, plural, one {resultat} other {resultats}}", "standalone.public_title": "Una ulhada dedins…", + "status.block": "Blocar @{name}", "status.cannot_reblog": "Aqueste estatut pòt pas èsser partejat", "status.delete": "Escafar", "status.embed": "Embarcar", @@ -221,6 +223,7 @@ "status.media_hidden": "Mèdia rescondut", "status.mention": "Mencionar", "status.more": "Mai", + "status.mute": "Rescondre @{name}", "status.mute_conversation": "Rescondre la conversacion", "status.open": "Desplegar aqueste estatut", "status.pin": "Penjar al perfil", diff --git a/app/javascript/mastodon/locales/pl.json b/app/javascript/mastodon/locales/pl.json index 9295ab937..334178e03 100644 --- a/app/javascript/mastodon/locales/pl.json +++ b/app/javascript/mastodon/locales/pl.json @@ -50,6 +50,7 @@ "column_header.unpin": "Cofnij przypięcie", "column_subheading.navigation": "Nawigacja", "column_subheading.settings": "Ustawienia", + "compose_form.hashtag_warning": "Ten wpis nie będzie widoczny pod podanymi hashtagami, ponieważ jest oznaczony jako niewidoczny. Tylko publiczne wpisy mogą zostać znalezione z użyciem hashtagów.", "compose_form.lock_disclaimer": "Twoje konto nie jest {locked}. Każdy, kto Cię śledzi, może wyświetlać Twoje wpisy przeznaczone tylko dla śledzących.", "compose_form.lock_disclaimer.lock": "zablokowane", "compose_form.placeholder": "Co Ci chodzi po głowie?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "użytkownik", "search_results.total": "{count, number} {count, plural, one {wynik} few {wyniki} many {wyników} more {wyników}}", "standalone.public_title": "Spojrzenie w głąb…", + "status.block": "Zablokuj @{name}", "status.cannot_reblog": "Ten wpis nie może zostać podbity", "status.delete": "Usuń", "status.embed": "Osadź", @@ -221,6 +223,7 @@ "status.media_hidden": "Zawartość multimedialna ukryta", "status.mention": "Wspomnij o @{name}", "status.more": "Więcej", + "status.mute": "Wycisz @{name}", "status.mute_conversation": "Wycisz konwersację", "status.open": "Rozszerz ten wpis", "status.pin": "Przypnij do profilu", diff --git a/app/javascript/mastodon/locales/pt-BR.json b/app/javascript/mastodon/locales/pt-BR.json index 70632846c..bc6ae928d 100644 --- a/app/javascript/mastodon/locales/pt-BR.json +++ b/app/javascript/mastodon/locales/pt-BR.json @@ -50,6 +50,7 @@ "column_header.unpin": "Desafixar", "column_subheading.navigation": "Navegação", "column_subheading.settings": "Configurações", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "A sua conta não está {locked}. Qualquer pessoa pode te seguir e visualizar postagens direcionadas a apenas seguidores.", "compose_form.lock_disclaimer.lock": "trancada", "compose_form.placeholder": "No que você está pensando?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "usuário", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "standalone.public_title": "Dê uma espiada...", + "status.block": "Block @{name}", "status.cannot_reblog": "Esta postagem não pode ser compartilhada", "status.delete": "Excluir", "status.embed": "Incorporar", @@ -221,6 +223,7 @@ "status.media_hidden": "Mídia escondida", "status.mention": "Mencionar @{name}", "status.more": "Mais", + "status.mute": "Mute @{name}", "status.mute_conversation": "Silenciar conversa", "status.open": "Expandir", "status.pin": "Fixar no perfil", diff --git a/app/javascript/mastodon/locales/pt.json b/app/javascript/mastodon/locales/pt.json index 15d5deb93..f9db2ad08 100644 --- a/app/javascript/mastodon/locales/pt.json +++ b/app/javascript/mastodon/locales/pt.json @@ -50,6 +50,7 @@ "column_header.unpin": "Remover fixar", "column_subheading.navigation": "Navegação", "column_subheading.settings": "Preferências", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "A tua conta não está {locked}. Qualquer pessoa pode seguir-te e ver as publicações direcionadas apenas a seguidores.", "compose_form.lock_disclaimer.lock": "bloqueada", "compose_form.placeholder": "Em que estás a pensar?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "utilizador", "search_results.total": "{count, number} {count, plural, one {resultado} other {resultados}}", "standalone.public_title": "Espreitar lá dentro...", + "status.block": "Block @{name}", "status.cannot_reblog": "Este post não pode ser partilhado", "status.delete": "Eliminar", "status.embed": "Incorporar", @@ -221,6 +223,7 @@ "status.media_hidden": "Media escondida", "status.mention": "Mencionar @{name}", "status.more": "Mais", + "status.mute": "Mute @{name}", "status.mute_conversation": "Silenciar conversa", "status.open": "Expandir", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/ru.json b/app/javascript/mastodon/locales/ru.json index e9925b675..0fec70df0 100644 --- a/app/javascript/mastodon/locales/ru.json +++ b/app/javascript/mastodon/locales/ru.json @@ -50,6 +50,7 @@ "column_header.unpin": "Открепить", "column_subheading.navigation": "Навигация", "column_subheading.settings": "Настройки", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Ваш аккаунт не {locked}. Любой человек может подписаться на Вас и просматривать посты для подписчиков.", "compose_form.lock_disclaimer.lock": "закрыт", "compose_form.placeholder": "О чем Вы думаете?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "пользователь", "search_results.total": "{count, number} {count, plural, one {результат} few {результата} many {результатов} other {результатов}}", "standalone.public_title": "Прямо сейчас", + "status.block": "Block @{name}", "status.cannot_reblog": "Этот статус не может быть продвинут", "status.delete": "Удалить", "status.embed": "Встроить", @@ -221,6 +223,7 @@ "status.media_hidden": "Медиаконтент скрыт", "status.mention": "Упомянуть @{name}", "status.more": "Больше", + "status.mute": "Mute @{name}", "status.mute_conversation": "Заглушить тред", "status.open": "Развернуть статус", "status.pin": "Закрепить в профиле", diff --git a/app/javascript/mastodon/locales/sk.json b/app/javascript/mastodon/locales/sk.json new file mode 100644 index 000000000..41ebfbd0e --- /dev/null +++ b/app/javascript/mastodon/locales/sk.json @@ -0,0 +1,262 @@ +{ + "account.block": "Blokovať @{name}", + "account.block_domain": "Blokovať všetko z {domain}", + "account.disclaimer_full": "Inofrmácie nižšie nemusia reflektovať použivateľský účet kompletne.", + "account.edit_profile": "Upraviť profil", + "account.follow": "Sledovať", + "account.followers": "Sledujúci", + "account.follows": "Sledovaní", + "account.follows_you": "Sleduje teba", + "account.hide_reblogs": "Hide boosts from @{name}", + "account.media": "Média", + "account.mention": "Napísať @{name}", + "account.moved_to": "{name} has moved to:", + "account.mute": "Ignorovať @{name}", + "account.mute_notifications": "Mute notifications from @{name}", + "account.posts": "Správ", + "account.report": "Nahlásiť @{name}", + "account.requested": "Čaká na schválenie. Klikni na zrušenie žiadosti", + "account.share": "Zdieľať @{name} profil", + "account.show_reblogs": "Show boosts from @{name}", + "account.unblock": "Odblokovať @{name}", + "account.unblock_domain": "Prestať blokovať {domain}", + "account.unfollow": "Prestať nasledovať", + "account.unmute": "Prestať ignorovať @{name}", + "account.unmute_notifications": "Unmute notifications from @{name}", + "account.view_full_profile": "Pozri celý profil", + "boost_modal.combo": "Nabudúce môžeš kliknúť {combo} a preskočiť", + "bundle_column_error.body": "Nastala chyba pri načítaní tohto komponentu.", + "bundle_column_error.retry": "Skús znova", + "bundle_column_error.title": "Chyba siete", + "bundle_modal_error.close": "Zatvoriť", + "bundle_modal_error.message": "Nastala chyba pri načítaní tohto komponentu.", + "bundle_modal_error.retry": "Skúsiť znova", + "column.blocks": "Blokovaní používatelia", + "column.community": "Lokálna časová os", + "column.favourites": "Obľúbené", + "column.follow_requests": "Žiadosti", + "column.home": "Moja časová os", + "column.lists": "Lists", + "column.mutes": "Ignorovaní používatelia", + "column.notifications": "Notifikácie", + "column.pins": "Pripnuté toots", + "column.public": "Federovaná časová os", + "column_back_button.label": "Späť", + "column_header.hide_settings": "Skryť nastavenia", + "column_header.moveLeft_settings": "Presunúť stĺpec doľava", + "column_header.moveRight_settings": "Presunúť stĺpec doprava", + "column_header.pin": "Pripnúť", + "column_header.show_settings": "Ukázať nastavenia", + "column_header.unpin": "Odopnúť", + "column_subheading.navigation": "Navigácia", + "column_subheading.settings": "Nastavenia", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.lock_disclaimer": "Tvoj účet nie je zamknutý. Ktokoľvek ťa môže nasledovať a vidieť tvoje správy pre sledujúcich.", + "compose_form.lock_disclaimer.lock": "zamknutý", + "compose_form.placeholder": "Čo máš na mysli?", + "compose_form.publish": "Toot", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive": "Označ súbor ako chúlostivý", + "compose_form.spoiler": "Skryť text za varovanie", + "compose_form.spoiler_placeholder": "Napíš sem tvoje varovanie", + "confirmation_modal.cancel": "Zrušiť", + "confirmations.block.confirm": "Blokovať", + "confirmations.block.message": "Naozaj chceš blokovať {name}?", + "confirmations.delete.confirm": "Zmazať", + "confirmations.delete.message": "Naozaj chceš zmazať túto správu?", + "confirmations.delete_list.confirm": "Delete", + "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.domain_block.confirm": "Skryť celú doménu", + "confirmations.domain_block.message": "Si si naozaj istý, že chceš blokovať celú {domain}? Vo väčšine prípadov stačí blokovať alebo ignorovať daných používateľov.", + "confirmations.mute.confirm": "Ignoruj", + "confirmations.mute.message": "Naozaj chceš ignorovať {name}?", + "confirmations.unfollow.confirm": "Nesledovať", + "confirmations.unfollow.message": "Naozaj chceš prestať sledovať {name}?", + "embed.instructions": "Skopíruj kód nižšie a ridaj tento status na tvoju web stránku.", + "embed.preview": "Tu je ukážka ako to bude vyzerať:", + "emoji_button.activity": "Aktivity", + "emoji_button.custom": "Vlastné", + "emoji_button.flags": "Vlajky", + "emoji_button.food": "Jedlá a nápoje", + "emoji_button.label": "Vlož emoji", + "emoji_button.nature": "Zvieratká", + "emoji_button.not_found": "Nenájdené", + "emoji_button.objects": "Predmety", + "emoji_button.people": "Ľudia", + "emoji_button.recent": "Často používané", + "emoji_button.search": "Hľadaj...", + "emoji_button.search_results": "Nájdené", + "emoji_button.symbols": "Symboly", + "emoji_button.travel": "Cestovanie a miesta", + "empty_column.community": "Lokálna časová os je prázdna. Napíš niečo aby sa to začalo hýbať!", + "empty_column.hashtag": "Ešte nič nie je v tomto hashtag-u.", + "empty_column.home": "Ešte nesleduješ nikoho. Pre začiatok pozri {public} alebo použi vyhľadávanie aby si našiel ostatných používateľov.", + "empty_column.home.public_timeline": "verejnú časovú os", + "empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.", + "empty_column.notifications": "Nemáš žiadne notifikácie. Napíš niekomu, nasleduj niekoho alebo komunikuj s ostatnými.", + "empty_column.public": "Ešte tu nič nie je. Napíš niečo verejne alebo začni sledovať používateľov z iných Mastodon serverov aby tu niečo bolo", + "follow_request.authorize": "Potvrdiť", + "follow_request.reject": "Odmietnúť", + "getting_started.appsshort": "Aplikácie", + "getting_started.faq": "FAQ", + "getting_started.heading": "Začíname", + "getting_started.open_source_notice": "Mastodon má otvorený kód. Reportovať chyby alebo prispievať vlastným kódom môžeš na GitHube v {github}.", + "getting_started.userguide": "Používateľská príručka", + "home.column_settings.advanced": "Rozšírené", + "home.column_settings.basic": "Základné", + "home.column_settings.filter_regex": "Filtrovať použitím regulárnych výrazov", + "home.column_settings.show_reblogs": "Zobraziť boosts", + "home.column_settings.show_replies": "Zobraziť odpovede", + "home.settings": "Nastavenia stĺpcov", + "keyboard_shortcuts.back": "to navigate back", + "keyboard_shortcuts.boost": "to boost", + "keyboard_shortcuts.column": "to focus a status in one of the columns", + "keyboard_shortcuts.compose": "to focus the compose textarea", + "keyboard_shortcuts.description": "Description", + "keyboard_shortcuts.down": "to move down in the list", + "keyboard_shortcuts.enter": "to open status", + "keyboard_shortcuts.favourite": "to favourite", + "keyboard_shortcuts.heading": "Keyboard Shortcuts", + "keyboard_shortcuts.hotkey": "Hotkey", + "keyboard_shortcuts.legend": "to display this legend", + "keyboard_shortcuts.mention": "to mention author", + "keyboard_shortcuts.reply": "to reply", + "keyboard_shortcuts.search": "to focus search", + "keyboard_shortcuts.toot": "to start a brand new toot", + "keyboard_shortcuts.unfocus": "to un-focus compose textarea/search", + "keyboard_shortcuts.up": "to move up in the list", + "lightbox.close": "Zavrieť", + "lightbox.next": "Ďalší", + "lightbox.previous": "Predchádzajúci", + "lists.account.add": "Add to list", + "lists.account.remove": "Remove from list", + "lists.delete": "Delete list", + "lists.edit": "Edit list", + "lists.new.create": "Add list", + "lists.new.title_placeholder": "New list title", + "lists.search": "Search among people you follow", + "lists.subheading": "Your lists", + "loading_indicator.label": "Nahrávam...", + "media_gallery.toggle_visible": "Zapnúť/Vypnúť viditeľnosť", + "missing_indicator.label": "Nenájdené", + "mute_modal.hide_notifications": "Hide notifications from this user?", + "navigation_bar.blocks": "Blokovaní používatelia", + "navigation_bar.community_timeline": "Lokálna časová os", + "navigation_bar.edit_profile": "Upraviť profil", + "navigation_bar.favourites": "Obľúbené", + "navigation_bar.follow_requests": "Žiadosti", + "navigation_bar.info": "O tomto Mastodon serveri", + "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts", + "navigation_bar.lists": "Lists", + "navigation_bar.logout": "Odhlásiť", + "navigation_bar.mutes": "Ignorovaní používatelia", + "navigation_bar.pins": "Pripnuté toots", + "navigation_bar.preferences": "Možnosti", + "navigation_bar.public_timeline": "Federovaná časová os", + "notification.favourite": "{name} sa páči tvoj status", + "notification.follow": "{name} ťa začal(a) sledovať", + "notification.mention": "{name} ťa zmienil", + "notification.reblog": "{name} re-tootol tvoj status", + "notifications.clear": "Vymazať notifikácie", + "notifications.clear_confirmation": "Naozaj chceš vymazať všetky tvoje notifikácie?", + "notifications.column_settings.alert": "Bublinové notifikácie", + "notifications.column_settings.favourite": "Obľúbené:", + "notifications.column_settings.follow": "Nový nasledujúci:", + "notifications.column_settings.mention": "Zmienenia:", + "notifications.column_settings.push": "Push notifikácie", + "notifications.column_settings.push_meta": "Toto zariadenie", + "notifications.column_settings.reblog": "Re-toots:", + "notifications.column_settings.show": "Zobraziť v stĺpci", + "notifications.column_settings.sound": "Prehrať zvuk", + "onboarding.done": "Koniec", + "onboarding.next": "Ďalej", + "onboarding.page_five.public_timelines": "Lokálna časová os zobrazuje verejné správy od všetkých na {domain}. Federovaná časová os zobrazuje verejné správy od všetkých ľudí ktoré {domain} nasleduje. Tieto sú takzvané Verejné Časové Osi, výborná možnosť ako nájsť a spoznať nových ľudí.", + "onboarding.page_four.home": "Domovská časová os zobrazí správy od ľudí ktorých sleduješ.", + "onboarding.page_four.notifications": "Stĺpec s notifikáciami zobrazí keď budeš s niekým komunikovať.", + "onboarding.page_one.federation": "Mastodon je sieť nezávislých serverov spojením ktorých vzniká jedna veľká federovaná sociálna sieť.", + "onboarding.page_one.handle": "Ty si na {domain}, takže tvoje celý nickname je {handle}", + "onboarding.page_one.welcome": "Vitajte v Mastodon!", + "onboarding.page_six.admin": "Správca tohto servera je {admin}.", + "onboarding.page_six.almost_done": "Takmer hotovo...", + "onboarding.page_six.appetoot": "Bon Appetoot!", + "onboarding.page_six.apps_available": "Aplikácie {apps} sú dostupné na pre iOS, Android and ďalšie platformy.", + "onboarding.page_six.github": "Mastodon je free open-source software. Chyby, nové funkcie alebo prispievať svojím kódom mǒžeš na {github}.", + "onboarding.page_six.guidelines": "pravidlá komunity", + "onboarding.page_six.read_guidelines": "Prosím prečítajte si {domain} pravidlá {guidelines}!", + "onboarding.page_six.various_app": "mobilné applikácie", + "onboarding.page_three.profile": "Uprav svoj profile a zmeň svoj avatar, bio a meno ktoré bude zobrazené. V nastaveniach nájdeš ďalšie možnosti.", + "onboarding.page_three.search": "Použi vyhľadávacie políčko na nájdenie ľudí a hashtagov, ako napríklad {slovensko}, {slovakia} alebo {pivo}. Na nájdenie človeka ktorý je registrovaný na inom Mastodon serveri použi jeho celý nickname.", + "onboarding.page_two.compose": "Správy píš zo stĺpca na komponovanie. Môžeš nahrávať obrázky, meniť nastavenia súkromia správ a pridávať varovania ikonkami nižšie.", + "onboarding.skip": "Preskočiť", + "privacy.change": "Zmeň viditeľnosť statusu", + "privacy.direct.long": "Pošli priamo iba spomenutým používateľom", + "privacy.direct.short": "Súkromne", + "privacy.private.long": "Pošli iba sledujúcim", + "privacy.private.short": "Iba sledujúci", + "privacy.public.long": "Pošli všetkým", + "privacy.public.short": "Verejne", + "privacy.unlisted.long": "Neposielať verejne", + "privacy.unlisted.short": "Nie je v zozname", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "now", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Zrušiť", + "report.placeholder": "Ďalšie komentáre", + "report.submit": "Poslať", + "report.target": "Reportovať {target}", + "search.placeholder": "Hľadaj", + "search_popout.search_format": "Advanced search format", + "search_popout.tips.hashtag": "hashtag", + "search_popout.tips.status": "status", + "search_popout.tips.text": "Simple text returns matching display names, usernames and hashtags", + "search_popout.tips.user": "user", + "search_results.total": "{count, number} nájdených", + "standalone.public_title": "Čo tam nájdeš...", + "status.block": "Block @{name}", + "status.cannot_reblog": "Tento príspevok nemôže byť re-tootnutý", + "status.delete": "Zmazať", + "status.embed": "Embed", + "status.favourite": "Páči sa mi", + "status.load_more": "Zobraziť viac", + "status.media_hidden": "Skryté médiá", + "status.mention": "Napísať @{name}", + "status.more": "More", + "status.mute": "Mute @{name}", + "status.mute_conversation": "Ignorovať konverzáciu", + "status.open": "Otvoriť", + "status.pin": "Pripnúť na profil", + "status.reblog": "Re-toot", + "status.reblogged_by": "{name} re-tootol", + "status.reply": "Odpovedať", + "status.replyAll": "Odpovedať všetkým", + "status.report": "Nahlásiť @{name}", + "status.sensitive_toggle": "Klikni pre zobrazenie", + "status.sensitive_warning": "Chúlostivý obsah", + "status.share": "Zdieľať", + "status.show_less": "Zobraziť menej", + "status.show_more": "Zobraziť viac", + "status.unmute_conversation": "Prestať ignorovať konverzáciu", + "status.unpin": "Odopnúť z profilu", + "tabs_bar.compose": "Napísať", + "tabs_bar.federated_timeline": "Federovaná", + "tabs_bar.home": "Domov", + "tabs_bar.local_timeline": "Local", + "tabs_bar.notifications": "Notifikácie", + "ui.beforeunload": "Your draft will be lost if you leave Mastodon.", + "upload_area.title": "Ťahaj a pusti pre nahratie", + "upload_button.label": "Pridať", + "upload_form.description": "Describe for the visually impaired", + "upload_form.undo": "Späť", + "upload_progress.label": "Nahrávam...", + "video.close": "Zavrieť video", + "video.exit_fullscreen": "Vpnúť zobrazenie na celú obrazovku", + "video.expand": "Zväčšiť video", + "video.fullscreen": "Zapnúť zobrazenie na celú obrazovku", + "video.hide": "Skryť video", + "video.mute": "Vypnúť zvuk", + "video.pause": "Pauza", + "video.play": "Prehrať", + "video.unmute": "Zapnúť zvuk" +} diff --git a/app/javascript/mastodon/locales/sr-Latn.json b/app/javascript/mastodon/locales/sr-Latn.json new file mode 100644 index 000000000..88631e332 --- /dev/null +++ b/app/javascript/mastodon/locales/sr-Latn.json @@ -0,0 +1,262 @@ +{ + "account.block": "Blokiraj korisnika @{name}", + "account.block_domain": "Sakrij sve sa domena {domain}", + "account.disclaimer_full": "Navedene informacije možda ne odslikavaju korisnički profil u potpunosti.", + "account.edit_profile": "Izmeni profil", + "account.follow": "Zaprati", + "account.followers": "Pratioca", + "account.follows": "Prati", + "account.follows_you": "Prati Vas", + "account.hide_reblogs": "Sakrij podrške koje daje korisnika @{name}", + "account.media": "Mediji", + "account.mention": "Pomeni korisnika @{name}", + "account.moved_to": "{name} se pomerio na:", + "account.mute": "Ućutkaj korisnika @{name}", + "account.mute_notifications": "Isključi obaveštenja od korisnika @{name}", + "account.posts": "Statusa", + "account.report": "Prijavi @{name}", + "account.requested": "Čekam odobrenje. Kliknite da poništite zahtev za praćenje", + "account.share": "Podeli profil korisnika @{name}", + "account.show_reblogs": "Prikaži podrške od korisnika @{name}", + "account.unblock": "Odblokiraj korisnika @{name}", + "account.unblock_domain": "Odblokiraj domen {domain}", + "account.unfollow": "Otprati", + "account.unmute": "Ukloni ućutkavanje korisniku @{name}", + "account.unmute_notifications": "Uključi nazad obaveštenja od korisnika @{name}", + "account.view_full_profile": "Vidi ceo profil", + "boost_modal.combo": "Možete pritisnuti {combo} da preskočite ovo sledeći put", + "bundle_column_error.body": "Nešto je pošlo po zlu prilikom učitavanja ove komponente.", + "bundle_column_error.retry": "Pokušajte ponovo", + "bundle_column_error.title": "Mrežna greška", + "bundle_modal_error.close": "Zatvori", + "bundle_modal_error.message": "Nešto nije bilo u redu pri učitavanju ove komponente.", + "bundle_modal_error.retry": "Pokušajte ponovo", + "column.blocks": "Blokirani korisnici", + "column.community": "Lokalna lajna", + "column.favourites": "Omiljeni", + "column.follow_requests": "Zahtevi za praćenje", + "column.home": "Početna", + "column.lists": "Liste", + "column.mutes": "Ućutkani korisnici", + "column.notifications": "Obaveštenja", + "column.pins": "Prikačeni tutovi", + "column.public": "Federisana lajna", + "column_back_button.label": "Nazad", + "column_header.hide_settings": "Sakrij postavke", + "column_header.moveLeft_settings": "Pomeri kolonu ulevo", + "column_header.moveRight_settings": "Pomeri kolonu udesno", + "column_header.pin": "Prikači", + "column_header.show_settings": "Prikaži postavke", + "column_header.unpin": "Otkači", + "column_subheading.navigation": "Navigacija", + "column_subheading.settings": "Postavke", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.lock_disclaimer": "Vaš nalog nije {locked}. Svako može da Vas zaprati i da vidi objave namenjene samo Vašim pratiocima.", + "compose_form.lock_disclaimer.lock": "zaključan", + "compose_form.placeholder": "Šta Vam je na umu?", + "compose_form.publish": "Tutni", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive": "Obeleži multimediju kao osetljivu", + "compose_form.spoiler": "Sakrij tekst ispod upozorenja", + "compose_form.spoiler_placeholder": "Ovde upišite upozorenje", + "confirmation_modal.cancel": "Poništi", + "confirmations.block.confirm": "Blokiraj", + "confirmations.block.message": "Da li ste sigurni da želite da blokirate korisnika {name}?", + "confirmations.delete.confirm": "Obriši", + "confirmations.delete.message": "Da li ste sigurni da želite obrišete ovaj status?", + "confirmations.delete_list.confirm": "Obriši", + "confirmations.delete_list.message": "Da li ste sigurni da želite da bespovratno obrišete ovu listu?", + "confirmations.domain_block.confirm": "Sakrij ceo domen", + "confirmations.domain_block.message": "Da li ste stvarno, stvarno sigurno da želite da blokirate ceo domen {domain}? U većini slučajeva, par dobrih blokiranja ili ućutkavanja su dovoljna i preporučljiva.", + "confirmations.mute.confirm": "Ućutkaj", + "confirmations.mute.message": "Da li stvarno želite da ućutkate korisnika {name}?", + "confirmations.unfollow.confirm": "Otprati", + "confirmations.unfollow.message": "Da li ste sigurni da želite da otpratite korisnika {name}?", + "embed.instructions": "Ugradi ovaj status na Vaš veb sajt kopiranjem koda ispod.", + "embed.preview": "Ovako će da izgleda:", + "emoji_button.activity": "Aktivnost", + "emoji_button.custom": "Proizvoljno", + "emoji_button.flags": "Zastave", + "emoji_button.food": "Hrana & piće", + "emoji_button.label": "Ubaci smajli", + "emoji_button.nature": "Priroda", + "emoji_button.not_found": "Nema smajlija!! (╯°□°)╯︵ ┻━┻", + "emoji_button.objects": "Objekti", + "emoji_button.people": "Ljudi", + "emoji_button.recent": "Najčešće korišćeni", + "emoji_button.search": "Pretraga...", + "emoji_button.search_results": "Rezultati pretrage", + "emoji_button.symbols": "Simboli", + "emoji_button.travel": "Putovanja & mesta", + "empty_column.community": "Lokalna lajna je prazna. Napišite nešto javno da lajna produva!", + "empty_column.hashtag": "Trenutno nema ništa na ovom heštegu.", + "empty_column.home": "Vaša lajna je prazna! Posetite {public} ili koristite pretragu da počnete i upoznajete nove ljude.", + "empty_column.home.public_timeline": "javna lajna", + "empty_column.list": "U ovoj listi još nema ničega. Kada članovi liste objave nove statuse, oni će se pojavljivati ovde.", + "empty_column.notifications": "Trenutno nemate obaveštenja. Družite se malo da započnete razgovore.", + "empty_column.public": "Ovde nema ničega! Napišite nešto javno, ili nađite korisnike sa drugih instanci koje ćete zapratiti da popunite ovu prazninu", + "follow_request.authorize": "Odobri", + "follow_request.reject": "Odbij", + "getting_started.appsshort": "Aplikacije", + "getting_started.faq": "ČPP", + "getting_started.heading": "Da počnete", + "getting_started.open_source_notice": "Mastodont je softver otvorenog koda. Možete mu doprineti ili prijaviti probleme preko GitHub-a na {github}.", + "getting_started.userguide": "Korisničko uputstvo", + "home.column_settings.advanced": "Napredno", + "home.column_settings.basic": "Osnovno", + "home.column_settings.filter_regex": "Filtriraj regularnim izrazima", + "home.column_settings.show_reblogs": "Prikaži i podržavanja", + "home.column_settings.show_replies": "Prikaži odgovore", + "home.settings": "Postavke kolone", + "keyboard_shortcuts.back": "da odete nazad", + "keyboard_shortcuts.boost": "da podržite", + "keyboard_shortcuts.column": "da se prebacite na status u jednoj od kolona", + "keyboard_shortcuts.compose": "da se prebacite na pisanje novog tuta", + "keyboard_shortcuts.description": "Opis", + "keyboard_shortcuts.down": "da se pomerite na dole u listi", + "keyboard_shortcuts.enter": "da otvorite status", + "keyboard_shortcuts.favourite": "da označite kao omiljeno", + "keyboard_shortcuts.heading": "Prečice na tastaturi", + "keyboard_shortcuts.hotkey": "Prečica", + "keyboard_shortcuts.legend": "da prikažete ovaj podsetnik", + "keyboard_shortcuts.mention": "da pomenete autora", + "keyboard_shortcuts.reply": "da odgovorite", + "keyboard_shortcuts.search": "da se prebacite na pretragu", + "keyboard_shortcuts.toot": "da započnete skroz novi tut", + "keyboard_shortcuts.unfocus": "da ne budete više na pretrazi/pravljenju novog tuta", + "keyboard_shortcuts.up": "da se pomerite na gore u listi", + "lightbox.close": "Zatvori", + "lightbox.next": "Sledeći", + "lightbox.previous": "Prethodni", + "lists.account.add": "Dodaj na listu", + "lists.account.remove": "Ukloni sa liste", + "lists.delete": "Obriši listu", + "lists.edit": "Izmeni listu", + "lists.new.create": "Dodaj listu", + "lists.new.title_placeholder": "Naslov nove liste", + "lists.search": "Pretraži među ljudima koje pratite", + "lists.subheading": "Vaše liste", + "loading_indicator.label": "Učitavam...", + "media_gallery.toggle_visible": "Uključi/isključi vidljivost", + "missing_indicator.label": "Nije pronađeno", + "mute_modal.hide_notifications": "Sakrij obaveštenja od ovog korisnika?", + "navigation_bar.blocks": "Blokirani korisnici", + "navigation_bar.community_timeline": "Lokalna lajna", + "navigation_bar.edit_profile": "Izmeni profil", + "navigation_bar.favourites": "Omiljeni", + "navigation_bar.follow_requests": "Zahtevi za praćenje", + "navigation_bar.info": "O ovoj instanci", + "navigation_bar.keyboard_shortcuts": "Prečice na tastaturi", + "navigation_bar.lists": "Liste", + "navigation_bar.logout": "Odjava", + "navigation_bar.mutes": "Ućutkani korisnici", + "navigation_bar.pins": "Prikačeni tutovi", + "navigation_bar.preferences": "Podešavanja", + "navigation_bar.public_timeline": "Federisana lajna", + "notification.favourite": "{name} je stavio Vaš status kao omiljeni", + "notification.follow": "{name} Vas je zapratio", + "notification.mention": "{name} Vas je pomenuo", + "notification.reblog": "{name} je podržao(la) Vaš status", + "notifications.clear": "Očisti obaveštenja", + "notifications.clear_confirmation": "Da li ste sigurno da trajno želite da očistite Vaša obaveštenja?", + "notifications.column_settings.alert": "Obaveštenja na radnoj površini", + "notifications.column_settings.favourite": "Omiljeni:", + "notifications.column_settings.follow": "Novi pratioci:", + "notifications.column_settings.mention": "Pominjanja:", + "notifications.column_settings.push": "Guraj obaveštenja", + "notifications.column_settings.push_meta": "Ovaj uređaj", + "notifications.column_settings.reblog": "Podrški:", + "notifications.column_settings.show": "Prikaži u koloni", + "notifications.column_settings.sound": "Puštaj zvuk", + "onboarding.done": "Gotovo", + "onboarding.next": "Sledeće", + "onboarding.page_five.public_timelines": "Lokalna lajna prikazuje sve javne statuse od svih na domenu {domain}. Federisana lajna prikazuje javne statuse od svih ljudi koje prate korisnici sa domena {domain}. Ovo su javne lajne, sjajan način da otkrijete nove ljude.", + "onboarding.page_four.home": "Početna lajna prikazuje statuse ljudi koje Vi pratite.", + "onboarding.page_four.notifications": "Kolona sa obaveštenjima Vam prikazuje kada neko priča sa Vama.", + "onboarding.page_one.federation": "Mastodont je mreža nezavisnih servera koji se uvezuju da naprave jednu veću društvenu mrežu. Ove servere zovemo instancama.", + "onboarding.page_one.handle": "Vi ste na domenu {domain}, pa je Vaša puna identifikacija {handle}", + "onboarding.page_one.welcome": "Dobrodošli na Mastodont!", + "onboarding.page_six.admin": "Administrator Vaše instance je {admin}.", + "onboarding.page_six.almost_done": "Još malo, pa gotovo...", + "onboarding.page_six.appetoot": "Prijatutno!", + "onboarding.page_six.apps_available": "Postoje {apps} dostupne za iOS, Android i druge platforme.", + "onboarding.page_six.github": "Mastodont je slobodan softver otvorenog koda. Možete prijavljivati greške, potraživati nove funckionalnosti, ili učestvujući u programiranju. Naš izvorni kod je ovde: {github}.", + "onboarding.page_six.guidelines": "smernice zajednice", + "onboarding.page_six.read_guidelines": "Pročitejte {guidelines} domena {domain}!", + "onboarding.page_six.various_app": "mobilne aplikacije", + "onboarding.page_three.profile": "Izmenite profil da promenite avatar, biografiju i ime za prikaz. Tamo ćete naći i ostala podešavanja.", + "onboarding.page_three.search": "Korisite pretragu da nađete ljude i gledate heštegove, kao što su {illustration} i {introductions}. Da nađete osobu koja nije na ovoj instanci, koristite njenu punu identifikaciju.", + "onboarding.page_two.compose": "Pišite statuse iz prve kolone. Možete otpremati slike, menjati podešavanja privatnosti, i dodavati upozorenja za osetljiv sadržaj preko ikonica ispod.", + "onboarding.skip": "Preskoči", + "privacy.change": "Podesi status privatnosti", + "privacy.direct.long": "Objavi samo korisnicima koji su pomenuti", + "privacy.direct.short": "Direktno", + "privacy.private.long": "Objavi samo pratiocima", + "privacy.private.short": "Samo za pratioce", + "privacy.public.long": "Objavi na javnoj lajni", + "privacy.public.short": "Javno", + "privacy.unlisted.long": "Ne objavljuj na javnim lajnama", + "privacy.unlisted.short": "Neizlistano", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "sada", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Poništi", + "report.placeholder": "Dodatni komentari", + "report.submit": "Pošalji", + "report.target": "Prijavljujem {target}", + "search.placeholder": "Pretraga", + "search_popout.search_format": "Napredni format pretrage", + "search_popout.tips.hashtag": "hešteg", + "search_popout.tips.status": "status", + "search_popout.tips.text": "Traženjem običnog teksta ćete dobiti sva pronađena imena, sva korisnička imena i sve nađene heštegove", + "search_popout.tips.user": "korisnik", + "search_results.total": "{count, number} {count, plural, one {rezultat} few {rezultata} other {rezultata}}", + "standalone.public_title": "Pogled iznutra...", + "status.block": "Block @{name}", + "status.cannot_reblog": "Ovaj status ne može da se podrži", + "status.delete": "Obriši", + "status.embed": "Ugradi na sajt", + "status.favourite": "Omiljeno", + "status.load_more": "Učitaj još", + "status.media_hidden": "Multimedija sakrivena", + "status.mention": "Pomeni korisnika @{name}", + "status.more": "Još", + "status.mute": "Mute @{name}", + "status.mute_conversation": "Ućutkaj prepisku", + "status.open": "Proširi ovaj status", + "status.pin": "Prikači na profil", + "status.reblog": "Podrži", + "status.reblogged_by": "{name} podržao(la)", + "status.reply": "Odgovori", + "status.replyAll": "Odgovori na diskusiju", + "status.report": "Prijavi korisnika @{name}", + "status.sensitive_toggle": "Kliknite da vidite", + "status.sensitive_warning": "Osetljiv sadržaj", + "status.share": "Podeli", + "status.show_less": "Prikaži manje", + "status.show_more": "Prikaži više", + "status.unmute_conversation": "Uključi prepisku", + "status.unpin": "Otkači sa profila", + "tabs_bar.compose": "Napiši", + "tabs_bar.federated_timeline": "Federisano", + "tabs_bar.home": "Početna", + "tabs_bar.local_timeline": "Lokalno", + "tabs_bar.notifications": "Obaveštenja", + "ui.beforeunload": "Ako napustite Mastodont, izgubićete napisani nacrt.", + "upload_area.title": "Prevucite ovde da otpremite", + "upload_button.label": "Dodaj multimediju", + "upload_form.description": "Opiši za slabovide osobe", + "upload_form.undo": "Opozovi", + "upload_progress.label": "Otpremam...", + "video.close": "Zatvori video", + "video.exit_fullscreen": "Napusti ceo ekran", + "video.expand": "Proširi video", + "video.fullscreen": "Ceo ekran", + "video.hide": "Sakrij video", + "video.mute": "Ugasi zvuk", + "video.pause": "Pauziraj", + "video.play": "Pusti", + "video.unmute": "Vrati zvuk" +} diff --git a/app/javascript/mastodon/locales/sr.json b/app/javascript/mastodon/locales/sr.json new file mode 100644 index 000000000..e65c02ab7 --- /dev/null +++ b/app/javascript/mastodon/locales/sr.json @@ -0,0 +1,262 @@ +{ + "account.block": "Блокирај корисника @{name}", + "account.block_domain": "Сакриј све са домена {domain}", + "account.disclaimer_full": "Наведене информације можда не одсликавају кориснички профил у потпуности.", + "account.edit_profile": "Измени профил", + "account.follow": "Запрати", + "account.followers": "Пратиоца", + "account.follows": "Прати", + "account.follows_you": "Прати Вас", + "account.hide_reblogs": "Сакриј подршке које даје корисника @{name}", + "account.media": "Медији", + "account.mention": "Помени корисника @{name}", + "account.moved_to": "{name} се померио на:", + "account.mute": "Ућуткај корисника @{name}", + "account.mute_notifications": "Искључи обавештења од корисника @{name}", + "account.posts": "Статуса", + "account.report": "Пријави @{name}", + "account.requested": "Чекам одобрење. Кликните да поништите захтев за праћење", + "account.share": "Подели профил корисника @{name}", + "account.show_reblogs": "Прикажи подршке од корисника @{name}", + "account.unblock": "Одблокирај корисника @{name}", + "account.unblock_domain": "Одблокирај домен {domain}", + "account.unfollow": "Отпрати", + "account.unmute": "Уклони ућуткавање кориснику @{name}", + "account.unmute_notifications": "Укључи назад обавештења од корисника @{name}", + "account.view_full_profile": "Види цео профил", + "boost_modal.combo": "Можете притиснути {combo} да прескочите ово следећи пут", + "bundle_column_error.body": "Нешто је пошло по злу приликом учитавања ове компоненте.", + "bundle_column_error.retry": "Покушајте поново", + "bundle_column_error.title": "Мрежна грешка", + "bundle_modal_error.close": "Затвори", + "bundle_modal_error.message": "Нешто није било у реду при учитавању ове компоненте.", + "bundle_modal_error.retry": "Покушајте поново", + "column.blocks": "Блокирани корисници", + "column.community": "Локална лајна", + "column.favourites": "Омиљени", + "column.follow_requests": "Захтеви за праћење", + "column.home": "Почетна", + "column.lists": "Листе", + "column.mutes": "Ућуткани корисници", + "column.notifications": "Обавештења", + "column.pins": "Прикачени тутови", + "column.public": "Федерисана лајна", + "column_back_button.label": "Назад", + "column_header.hide_settings": "Сакриј поставке", + "column_header.moveLeft_settings": "Помери колону улево", + "column_header.moveRight_settings": "Помери колону удесно", + "column_header.pin": "Прикачи", + "column_header.show_settings": "Прикажи поставке", + "column_header.unpin": "Откачи", + "column_subheading.navigation": "Навигација", + "column_subheading.settings": "Поставке", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", + "compose_form.lock_disclaimer": "Ваш налог није {locked}. Свако може да Вас запрати и да види објаве намењене само Вашим пратиоцима.", + "compose_form.lock_disclaimer.lock": "закључан", + "compose_form.placeholder": "Шта Вам је на уму?", + "compose_form.publish": "Тутни", + "compose_form.publish_loud": "{publish}!", + "compose_form.sensitive": "Обележи мултимедију као осетљиву", + "compose_form.spoiler": "Сакриј текст испод упозорења", + "compose_form.spoiler_placeholder": "Овде упишите упозорење", + "confirmation_modal.cancel": "Поништи", + "confirmations.block.confirm": "Блокирај", + "confirmations.block.message": "Да ли сте сигурни да желите да блокирате корисника {name}?", + "confirmations.delete.confirm": "Обриши", + "confirmations.delete.message": "Да ли сте сигурни да желите обришете овај статус?", + "confirmations.delete_list.confirm": "Обриши", + "confirmations.delete_list.message": "Да ли сте сигурни да желите да бесповратно обришете ову листу?", + "confirmations.domain_block.confirm": "Сакриј цео домен", + "confirmations.domain_block.message": "Да ли сте стварно, стварно сигурно да желите да блокирате цео домен {domain}? У већини случајева, пар добрих блокирања или ућуткавања су довољна и препоручљива.", + "confirmations.mute.confirm": "Ућуткај", + "confirmations.mute.message": "Да ли стварно желите да ућуткате корисника {name}?", + "confirmations.unfollow.confirm": "Отпрати", + "confirmations.unfollow.message": "Да ли сте сигурни да желите да отпратите корисника {name}?", + "embed.instructions": "Угради овај статус на Ваш веб сајт копирањем кода испод.", + "embed.preview": "Овако ће да изгледа:", + "emoji_button.activity": "Активност", + "emoji_button.custom": "Произвољно", + "emoji_button.flags": "Заставе", + "emoji_button.food": "Храна & пиће", + "emoji_button.label": "Убаци смајли", + "emoji_button.nature": "Природа", + "emoji_button.not_found": "Нема смајлија!! (╯°□°)╯︵ ┻━┻", + "emoji_button.objects": "Објекти", + "emoji_button.people": "Људи", + "emoji_button.recent": "Најчешће коришћени", + "emoji_button.search": "Претрага...", + "emoji_button.search_results": "Резултати претраге", + "emoji_button.symbols": "Симболи", + "emoji_button.travel": "Путовања & места", + "empty_column.community": "Локална лајна је празна. Напишите нешто јавно да лајна продува!", + "empty_column.hashtag": "Тренутно нема ништа на овом хештегу.", + "empty_column.home": "Ваша лајна је празна! Посетите {public} или користите претрагу да почнете и упознајете нове људе.", + "empty_column.home.public_timeline": "јавна лајна", + "empty_column.list": "У овој листи још нема ничега. Када чланови листе објаве нове статусе, они ће се појављивати овде.", + "empty_column.notifications": "Тренутно немате обавештења. Дружите се мало да започнете разговоре.", + "empty_column.public": "Овде нема ничега! Напишите нешто јавно, или нађите кориснике са других инстанци које ћете запратити да попуните ову празнину", + "follow_request.authorize": "Одобри", + "follow_request.reject": "Одбиј", + "getting_started.appsshort": "Апликације", + "getting_started.faq": "ЧПП", + "getting_started.heading": "Да почнете", + "getting_started.open_source_notice": "Мастoдонт је софтвер отвореног кода. Можете му допринети или пријавити проблеме преко GitHub-а на {github}.", + "getting_started.userguide": "Корисничко упутство", + "home.column_settings.advanced": "Напредно", + "home.column_settings.basic": "Основно", + "home.column_settings.filter_regex": "Филтрирај регуларним изразима", + "home.column_settings.show_reblogs": "Прикажи и подржавања", + "home.column_settings.show_replies": "Прикажи одговоре", + "home.settings": "Поставке колоне", + "keyboard_shortcuts.back": "да одете назад", + "keyboard_shortcuts.boost": "да подржите", + "keyboard_shortcuts.column": "да се пребаците на статус у једној од колона", + "keyboard_shortcuts.compose": "да се пребаците на писање новог тута", + "keyboard_shortcuts.description": "Опис", + "keyboard_shortcuts.down": "да се померите на доле у листи", + "keyboard_shortcuts.enter": "да отворите статус", + "keyboard_shortcuts.favourite": "да означите као омиљено", + "keyboard_shortcuts.heading": "Пречице на тастатури", + "keyboard_shortcuts.hotkey": "Пречица", + "keyboard_shortcuts.legend": "да прикажете овај подсетник", + "keyboard_shortcuts.mention": "да поменете аутора", + "keyboard_shortcuts.reply": "да одговорите", + "keyboard_shortcuts.search": "да се пребаците на претрагу", + "keyboard_shortcuts.toot": "да започнете скроз нови тут", + "keyboard_shortcuts.unfocus": "да не будете више на претрази/прављењу новог тута", + "keyboard_shortcuts.up": "да се померите на горе у листи", + "lightbox.close": "Затвори", + "lightbox.next": "Следећи", + "lightbox.previous": "Претходни", + "lists.account.add": "Додај на листу", + "lists.account.remove": "Уклони са листе", + "lists.delete": "Обриши листу", + "lists.edit": "Измени листу", + "lists.new.create": "Додај листу", + "lists.new.title_placeholder": "Наслов нове листе", + "lists.search": "Претражи међу људима које пратите", + "lists.subheading": "Ваше листе", + "loading_indicator.label": "Учитавам...", + "media_gallery.toggle_visible": "Укључи/искључи видљивост", + "missing_indicator.label": "Није пронађено", + "mute_modal.hide_notifications": "Сакриј обавештења од овог корисника?", + "navigation_bar.blocks": "Блокирани корисници", + "navigation_bar.community_timeline": "Локална лајна", + "navigation_bar.edit_profile": "Измени профил", + "navigation_bar.favourites": "Омиљени", + "navigation_bar.follow_requests": "Захтеви за праћење", + "navigation_bar.info": "О овој инстанци", + "navigation_bar.keyboard_shortcuts": "Пречице на тастатури", + "navigation_bar.lists": "Листе", + "navigation_bar.logout": "Одјава", + "navigation_bar.mutes": "Ућуткани корисници", + "navigation_bar.pins": "Прикачени тутови", + "navigation_bar.preferences": "Подешавања", + "navigation_bar.public_timeline": "Федерисана лајна", + "notification.favourite": "{name} је ставио Ваш статус као омиљени", + "notification.follow": "{name} Вас је запратио", + "notification.mention": "{name} Вас је поменуо", + "notification.reblog": "{name} је подржао(ла) Ваш статус", + "notifications.clear": "Очисти обавештења", + "notifications.clear_confirmation": "Да ли сте сигурно да трајно желите да очистите Ваша обавештења?", + "notifications.column_settings.alert": "Обавештења на радној површини", + "notifications.column_settings.favourite": "Омиљени:", + "notifications.column_settings.follow": "Нови пратиоци:", + "notifications.column_settings.mention": "Помињања:", + "notifications.column_settings.push": "Гурај обавештења", + "notifications.column_settings.push_meta": "Овај уређај", + "notifications.column_settings.reblog": "Подршки:", + "notifications.column_settings.show": "Прикажи у колони", + "notifications.column_settings.sound": "Пуштај звук", + "onboarding.done": "Готово", + "onboarding.next": "Следеће", + "onboarding.page_five.public_timelines": "Локална лајна приказује све јавне статусе од свих на домену {domain}. Федерисана лајна приказује јавне статусе од свих људи које прате корисници са домена {domain}. Ово су јавне лајне, сјајан начин да откријете нове људе.", + "onboarding.page_four.home": "Почетна лајна приказује статусе људи које Ви пратите.", + "onboarding.page_four.notifications": "Колона са обавештењима Вам приказује када неко прича са Вама.", + "onboarding.page_one.federation": "Мастодонт је мрежа независних сервера који се увезују да направе једну већу друштвену мрежу. Ове сервере зовемо инстанцама.", + "onboarding.page_one.handle": "Ви сте на домену {domain}, па је Ваша пуна идентификација {handle}", + "onboarding.page_one.welcome": "Добродошли на Мастодонт!", + "onboarding.page_six.admin": "Администратор Ваше инстанце је {admin}.", + "onboarding.page_six.almost_done": "Још мало, па готово...", + "onboarding.page_six.appetoot": "Пријатутно!", + "onboarding.page_six.apps_available": "Постоје {apps} доступне за iOS, Андроид и друге платформе.", + "onboarding.page_six.github": "Мастодонт је слободан софтвер отвореног кода. Можете пријављивати грешке, потраживати нове фунцкионалности, или учествујући у програмирању. Наш изворни код је овде: {github}.", + "onboarding.page_six.guidelines": "смернице заједнице", + "onboarding.page_six.read_guidelines": "Прочитејте {guidelines} домена {domain}!", + "onboarding.page_six.various_app": "мобилне апликације", + "onboarding.page_three.profile": "Измените профил да промените аватар, биографију и име за приказ. Тамо ћете наћи и остала подешавања.", + "onboarding.page_three.search": "Корисите претрагу да нађете људе и гледате хештегове, као што су {illustration} и {introductions}. Да нађете особу која није на овој инстанци, користите њену пуну идентификацију.", + "onboarding.page_two.compose": "Пишите статусе из прве колоне. Можете отпремати слике, мењати подешавања приватности, и додавати упозорења за осетљив садржај преко иконица испод.", + "onboarding.skip": "Прескочи", + "privacy.change": "Подеси статус приватности", + "privacy.direct.long": "Објави само корисницима који су поменути", + "privacy.direct.short": "Директно", + "privacy.private.long": "Објави само пратиоцима", + "privacy.private.short": "Само за пратиоце", + "privacy.public.long": "Објави на јавној лајни", + "privacy.public.short": "Јавно", + "privacy.unlisted.long": "Не објављуј на јавним лајнама", + "privacy.unlisted.short": "Неизлистано", + "relative_time.days": "{number}d", + "relative_time.hours": "{number}h", + "relative_time.just_now": "сада", + "relative_time.minutes": "{number}m", + "relative_time.seconds": "{number}s", + "reply_indicator.cancel": "Поништи", + "report.placeholder": "Додатни коментари", + "report.submit": "Пошаљи", + "report.target": "Пријављујем {target}", + "search.placeholder": "Претрага", + "search_popout.search_format": "Напредни формат претраге", + "search_popout.tips.hashtag": "хештег", + "search_popout.tips.status": "статус", + "search_popout.tips.text": "Тражењем обичног текста ћете добити сва пронађена имена, сва корисничка имена и све нађене хештегове", + "search_popout.tips.user": "корисник", + "search_results.total": "{count, number} {count, plural, one {резултат} few {резултата} other {резултата}}", + "standalone.public_title": "Поглед изнутра...", + "status.block": "Block @{name}", + "status.cannot_reblog": "Овај статус не може да се подржи", + "status.delete": "Обриши", + "status.embed": "Угради на сајт", + "status.favourite": "Омиљено", + "status.load_more": "Учитај још", + "status.media_hidden": "Мултимедија сакривена", + "status.mention": "Помени корисника @{name}", + "status.more": "Још", + "status.mute": "Mute @{name}", + "status.mute_conversation": "Ућуткај преписку", + "status.open": "Прошири овај статус", + "status.pin": "Прикачи на профил", + "status.reblog": "Подржи", + "status.reblogged_by": "{name} подржао(ла)", + "status.reply": "Одговори", + "status.replyAll": "Одговори на дискусију", + "status.report": "Пријави корисника @{name}", + "status.sensitive_toggle": "Кликните да видите", + "status.sensitive_warning": "Осетљив садржај", + "status.share": "Подели", + "status.show_less": "Прикажи мање", + "status.show_more": "Прикажи више", + "status.unmute_conversation": "Укључи преписку", + "status.unpin": "Откачи са профила", + "tabs_bar.compose": "Напиши", + "tabs_bar.federated_timeline": "Федерисано", + "tabs_bar.home": "Почетна", + "tabs_bar.local_timeline": "Локално", + "tabs_bar.notifications": "Обавештења", + "ui.beforeunload": "Ако напустите Мастодонт, изгубићете написани нацрт.", + "upload_area.title": "Превуците овде да отпремите", + "upload_button.label": "Додај мултимедију", + "upload_form.description": "Опиши за слабовиде особе", + "upload_form.undo": "Опозови", + "upload_progress.label": "Отпремам...", + "video.close": "Затвори видео", + "video.exit_fullscreen": "Напусти цео екран", + "video.expand": "Прошири видео", + "video.fullscreen": "Цео екран", + "video.hide": "Сакриј видео", + "video.mute": "Угаси звук", + "video.pause": "Паузирај", + "video.play": "Пусти", + "video.unmute": "Врати звук" +} diff --git a/app/javascript/mastodon/locales/sv.json b/app/javascript/mastodon/locales/sv.json index 9d9646509..edfa9b8c2 100644 --- a/app/javascript/mastodon/locales/sv.json +++ b/app/javascript/mastodon/locales/sv.json @@ -50,6 +50,7 @@ "column_header.unpin": "Ångra fäst", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Inställningar", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Ditt konto är inte {locked}. Vemsomhelst kan följa dig och även se dina inlägg skrivna för endast dina följare.", "compose_form.lock_disclaimer.lock": "låst", "compose_form.placeholder": "Vad funderar du på?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "användare", "search_results.total": "{count, number} {count, plural, ett {result} andra {results}}", "standalone.public_title": "En titt inuti...", + "status.block": "Block @{name}", "status.cannot_reblog": "Detta inlägg kan inte knuffas", "status.delete": "Ta bort", "status.embed": "Bädda in", @@ -221,6 +223,7 @@ "status.media_hidden": "Media dold", "status.mention": "Omnämn @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Tysta konversation", "status.open": "Utvidga denna status", "status.pin": "Fäst i profil", diff --git a/app/javascript/mastodon/locales/th.json b/app/javascript/mastodon/locales/th.json index cc18a6096..06323ebfc 100644 --- a/app/javascript/mastodon/locales/th.json +++ b/app/javascript/mastodon/locales/th.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigation", "column_subheading.settings": "Settings", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Your account is not {locked}. Anyone can follow you to view your follower-only posts.", "compose_form.lock_disclaimer.lock": "locked", "compose_form.placeholder": "What is on your mind?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {result} other {results}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "This post cannot be boosted", "status.delete": "Delete", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Media hidden", "status.mention": "Mention @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", "status.open": "Expand this status", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/tr.json b/app/javascript/mastodon/locales/tr.json index c51f3e417..ce6434ca6 100644 --- a/app/javascript/mastodon/locales/tr.json +++ b/app/javascript/mastodon/locales/tr.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Navigasyon", "column_subheading.settings": "Ayarlar", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Hesabınız {locked} değil. Sadece takipçilerle paylaştığınız gönderileri görebilmek için sizi herhangi bir kullanıcı takip edebilir.", "compose_form.lock_disclaimer.lock": "kilitli", "compose_form.placeholder": "Ne düşünüyorsun?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {sonuç} other {sonuçlar}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "Bu gönderi boost edilemez", "status.delete": "Sil", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Gizli görsel", "status.mention": "Bahset @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Mute conversation", "status.open": "Bu gönderiyi genişlet", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/uk.json b/app/javascript/mastodon/locales/uk.json index 86c0ce76d..46d22ac83 100644 --- a/app/javascript/mastodon/locales/uk.json +++ b/app/javascript/mastodon/locales/uk.json @@ -50,6 +50,7 @@ "column_header.unpin": "Unpin", "column_subheading.navigation": "Навігація", "column_subheading.settings": "Налаштування", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "Ваш акаунт не {locked}. Кожен може підписатися на Вас та бачити Ваші приватні пости.", "compose_form.lock_disclaimer.lock": "приватний", "compose_form.placeholder": "Що у Вас на думці?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} {count, plural, one {результат} few {результати} many {результатів} other {результатів}}", "standalone.public_title": "A look inside...", + "status.block": "Block @{name}", "status.cannot_reblog": "Цей допис не може бути передмухнутий", "status.delete": "Видалити", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "Медіаконтент приховано", "status.mention": "Згадати", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "Заглушити діалог", "status.open": "Розгорнути допис", "status.pin": "Pin on profile", diff --git a/app/javascript/mastodon/locales/whitelist_sk.json b/app/javascript/mastodon/locales/whitelist_sk.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_sk.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/whitelist_sr-Latn.json b/app/javascript/mastodon/locales/whitelist_sr-Latn.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_sr-Latn.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/whitelist_sr.json b/app/javascript/mastodon/locales/whitelist_sr.json new file mode 100644 index 000000000..0d4f101c7 --- /dev/null +++ b/app/javascript/mastodon/locales/whitelist_sr.json @@ -0,0 +1,2 @@ +[ +] diff --git a/app/javascript/mastodon/locales/zh-CN.json b/app/javascript/mastodon/locales/zh-CN.json index 9be6a9f73..ce16dcd8e 100644 --- a/app/javascript/mastodon/locales/zh-CN.json +++ b/app/javascript/mastodon/locales/zh-CN.json @@ -50,6 +50,7 @@ "column_header.unpin": "取消固定", "column_subheading.navigation": "导航", "column_subheading.settings": "设置", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "你的帐户没有{locked}。任何人都可以在关注你后立即查看仅关注者可见的嘟文。", "compose_form.lock_disclaimer.lock": "开启保护", "compose_form.placeholder": "在想啥?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "用户", "search_results.total": "共 {count, number} 个结果", "standalone.public_title": "大家都在干啥?", + "status.block": "Block @{name}", "status.cannot_reblog": "无法转嘟这条嘟文", "status.delete": "删除", "status.embed": "嵌入", @@ -221,6 +223,7 @@ "status.media_hidden": "隐藏媒体内容", "status.mention": "提及 @{name}", "status.more": "更多", + "status.mute": "Mute @{name}", "status.mute_conversation": "隐藏此对话", "status.open": "展开嘟文", "status.pin": "在个人资料页面置顶", diff --git a/app/javascript/mastodon/locales/zh-HK.json b/app/javascript/mastodon/locales/zh-HK.json index 15a68c915..7745da622 100644 --- a/app/javascript/mastodon/locales/zh-HK.json +++ b/app/javascript/mastodon/locales/zh-HK.json @@ -50,6 +50,7 @@ "column_header.unpin": "取下", "column_subheading.navigation": "瀏覽", "column_subheading.settings": "設定", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "你的用戶狀態為「{locked}」,任何人都能立即關注你,然後看到「只有關注者能看」的文章。", "compose_form.lock_disclaimer.lock": "公共", "compose_form.placeholder": "你在想甚麼?", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} 項結果", "standalone.public_title": "站點一瞥…", + "status.block": "Block @{name}", "status.cannot_reblog": "這篇文章無法被轉推", "status.delete": "刪除", "status.embed": "鑲嵌", @@ -221,6 +223,7 @@ "status.media_hidden": "隱藏媒體內容", "status.mention": "提及 @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "靜音對話", "status.open": "展開文章", "status.pin": "置頂到資料頁", diff --git a/app/javascript/mastodon/locales/zh-TW.json b/app/javascript/mastodon/locales/zh-TW.json index 1bdc883a8..65b174ab5 100644 --- a/app/javascript/mastodon/locales/zh-TW.json +++ b/app/javascript/mastodon/locales/zh-TW.json @@ -50,6 +50,7 @@ "column_header.unpin": "取下", "column_subheading.navigation": "瀏覽", "column_subheading.settings": "設定", + "compose_form.hashtag_warning": "This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag.", "compose_form.lock_disclaimer": "你的帳號沒有{locked}。任何人都可以關注你,看到發給關注者的貼文。", "compose_form.lock_disclaimer.lock": "上鎖", "compose_form.placeholder": "在想些什麼?", @@ -63,8 +64,8 @@ "confirmations.block.message": "你確定要封鎖 {name} ?", "confirmations.delete.confirm": "刪除", "confirmations.delete.message": "你確定要刪除這個狀態?", - "confirmations.delete_list.confirm": "Delete", - "confirmations.delete_list.message": "Are you sure you want to permanently delete this list?", + "confirmations.delete_list.confirm": "刪除", + "confirmations.delete_list.message": "確定要永久性地刪除這個名單嗎?", "confirmations.domain_block.confirm": "隱藏整個網域", "confirmations.domain_block.message": "你真的真的確定要隱藏整個 {domain} ?多數情況下,比較推薦封鎖或消音幾個特定目標就好。", "confirmations.mute.confirm": "消音", @@ -127,14 +128,14 @@ "lightbox.close": "關閉", "lightbox.next": "繼續", "lightbox.previous": "回退", - "lists.account.add": "Add to list", - "lists.account.remove": "Remove from list", - "lists.delete": "Delete list", - "lists.edit": "Edit list", - "lists.new.create": "Add list", - "lists.new.title_placeholder": "New list title", - "lists.search": "Search among people you follow", - "lists.subheading": "Your lists", + "lists.account.add": "加到名單裡", + "lists.account.remove": "從名單中移除", + "lists.delete": "刪除名單", + "lists.edit": "修改名單", + "lists.new.create": "新增名單", + "lists.new.title_placeholder": "名單名稱", + "lists.search": "搜尋您關注的使用者", + "lists.subheading": "您的名單", "loading_indicator.label": "讀取中...", "media_gallery.toggle_visible": "切換可見性", "missing_indicator.label": "找不到", @@ -145,8 +146,8 @@ "navigation_bar.favourites": "最愛", "navigation_bar.follow_requests": "關注請求", "navigation_bar.info": "關於本站", - "navigation_bar.keyboard_shortcuts": "Keyboard shortcuts", - "navigation_bar.lists": "Lists", + "navigation_bar.keyboard_shortcuts": "快速鍵", + "navigation_bar.lists": "名單", "navigation_bar.logout": "登出", "navigation_bar.mutes": "消音的使用者", "navigation_bar.pins": "置頂貼文", @@ -213,6 +214,7 @@ "search_popout.tips.user": "user", "search_results.total": "{count, number} 項結果", "standalone.public_title": "站點一瞥…", + "status.block": "Block @{name}", "status.cannot_reblog": "此貼文無法轉推", "status.delete": "刪除", "status.embed": "Embed", @@ -221,6 +223,7 @@ "status.media_hidden": "媒體已隱藏", "status.mention": "提到 @{name}", "status.more": "More", + "status.mute": "Mute @{name}", "status.mute_conversation": "消音對話", "status.open": "展開這個狀態", "status.pin": "置頂到個人資訊頁", diff --git a/app/javascript/mastodon/main.js b/app/javascript/mastodon/main.js index 23b6b04fa..5d73caa10 100644 --- a/app/javascript/mastodon/main.js +++ b/app/javascript/mastodon/main.js @@ -1,5 +1,5 @@ -import * as WebPushSubscription from './web_push_subscription'; -import Mastodon from './containers/mastodon'; +import * as registerPushNotifications from './actions/push_notifications'; +import { default as Mastodon, store } from './containers/mastodon'; import React from 'react'; import ReactDOM from 'react-dom'; import ready from './ready'; @@ -25,7 +25,7 @@ function main() { if (process.env.NODE_ENV === 'production') { // avoid offline in dev mode because it's harder to debug require('offline-plugin/runtime').install(); - WebPushSubscription.register(); + store.dispatch(registerPushNotifications.register()); } perf.stop('main()'); }); diff --git a/app/javascript/mastodon/reducers/push_notifications.js b/app/javascript/mastodon/reducers/push_notifications.js index 31a40d246..85628c6b1 100644 --- a/app/javascript/mastodon/reducers/push_notifications.js +++ b/app/javascript/mastodon/reducers/push_notifications.js @@ -1,5 +1,5 @@ import { STORE_HYDRATE } from '../actions/store'; -import { SET_BROWSER_SUPPORT, SET_SUBSCRIPTION, CLEAR_SUBSCRIPTION, ALERTS_CHANGE } from '../actions/push_notifications'; +import { SET_BROWSER_SUPPORT, SET_SUBSCRIPTION, CLEAR_SUBSCRIPTION, SET_ALERTS } from '../actions/push_notifications'; import Immutable from 'immutable'; const initialState = Immutable.Map({ @@ -43,8 +43,8 @@ export default function push_subscriptions(state = initialState, action) { return state.set('browserSupport', action.value); case CLEAR_SUBSCRIPTION: return initialState; - case ALERTS_CHANGE: - return state.setIn(action.key, action.value); + case SET_ALERTS: + return state.setIn(action.path, action.value); default: return state; } diff --git a/app/javascript/mastodon/reducers/settings.js b/app/javascript/mastodon/reducers/settings.js index 5817cf49b..390b2a13a 100644 --- a/app/javascript/mastodon/reducers/settings.js +++ b/app/javascript/mastodon/reducers/settings.js @@ -93,7 +93,7 @@ export default function settings(state = initialState, action) { return hydrate(state, action.state.get('settings')); case SETTING_CHANGE: return state - .setIn(action.key, action.value) + .setIn(action.path, action.value) .set('saved', false); case COLUMN_ADD: return state diff --git a/app/javascript/mastodon/web_push_subscription.js b/app/javascript/mastodon/web_push_subscription.js deleted file mode 100644 index 17aca4060..000000000 --- a/app/javascript/mastodon/web_push_subscription.js +++ /dev/null @@ -1,129 +0,0 @@ -import axios from 'axios'; -import { store } from './containers/mastodon'; -import { setBrowserSupport, setSubscription, clearSubscription } from './actions/push_notifications'; -import { pushNotificationsSetting } from './settings'; - -// Taken from https://www.npmjs.com/package/web-push -const urlBase64ToUint8Array = (base64String) => { - const padding = '='.repeat((4 - base64String.length % 4) % 4); - const base64 = (base64String + padding) - .replace(/\-/g, '+') - .replace(/_/g, '/'); - - const rawData = window.atob(base64); - const outputArray = new Uint8Array(rawData.length); - - for (let i = 0; i < rawData.length; ++i) { - outputArray[i] = rawData.charCodeAt(i); - } - return outputArray; -}; - -const getApplicationServerKey = () => document.querySelector('[name="applicationServerKey"]').getAttribute('content'); - -const getRegistration = () => navigator.serviceWorker.ready; - -const getPushSubscription = (registration) => - registration.pushManager.getSubscription() - .then(subscription => ({ registration, subscription })); - -const subscribe = (registration) => - registration.pushManager.subscribe({ - userVisibleOnly: true, - applicationServerKey: urlBase64ToUint8Array(getApplicationServerKey()), - }); - -const unsubscribe = ({ registration, subscription }) => - subscription ? subscription.unsubscribe().then(() => registration) : registration; - -const sendSubscriptionToBackend = (subscription) => { - const params = { subscription }; - - const me = store.getState().getIn(['meta', 'me']); - if (me) { - const data = pushNotificationsSetting.get(me); - if (data) { - params.data = data; - } - } - - return axios.post('/api/web/push_subscriptions', params).then(response => response.data); -}; - -// Last one checks for payload support: https://web-push-book.gauntface.com/chapter-06/01-non-standards-browsers/#no-payload -const supportsPushNotifications = ('serviceWorker' in navigator && 'PushManager' in window && 'getKey' in PushSubscription.prototype); - -export function register () { - store.dispatch(setBrowserSupport(supportsPushNotifications)); - const me = store.getState().getIn(['meta', 'me']); - - if (me && !pushNotificationsSetting.get(me)) { - const alerts = store.getState().getIn(['push_notifications', 'alerts']); - if (alerts) { - pushNotificationsSetting.set(me, { alerts: alerts }); - } - } - - if (supportsPushNotifications) { - if (!getApplicationServerKey()) { - console.error('The VAPID public key is not set. You will not be able to receive Web Push Notifications.'); - return; - } - - getRegistration() - .then(getPushSubscription) - .then(({ registration, subscription }) => { - if (subscription !== null) { - // We have a subscription, check if it is still valid - const currentServerKey = (new Uint8Array(subscription.options.applicationServerKey)).toString(); - const subscriptionServerKey = urlBase64ToUint8Array(getApplicationServerKey()).toString(); - const serverEndpoint = store.getState().getIn(['push_notifications', 'subscription', 'endpoint']); - - // If the VAPID public key did not change and the endpoint corresponds - // to the endpoint saved in the backend, the subscription is valid - if (subscriptionServerKey === currentServerKey && subscription.endpoint === serverEndpoint) { - return subscription; - } else { - // Something went wrong, try to subscribe again - return unsubscribe({ registration, subscription }).then(subscribe).then(sendSubscriptionToBackend); - } - } - - // No subscription, try to subscribe - return subscribe(registration).then(sendSubscriptionToBackend); - }) - .then(subscription => { - // If we got a PushSubscription (and not a subscription object from the backend) - // it means that the backend subscription is valid (and was set during hydration) - if (!(subscription instanceof PushSubscription)) { - store.dispatch(setSubscription(subscription)); - if (me) { - pushNotificationsSetting.set(me, { alerts: subscription.alerts }); - } - } - }) - .catch(error => { - if (error.code === 20 && error.name === 'AbortError') { - console.warn('Your browser supports Web Push Notifications, but does not seem to implement the VAPID protocol.'); - } else if (error.code === 5 && error.name === 'InvalidCharacterError') { - console.error('The VAPID public key seems to be invalid:', getApplicationServerKey()); - } - - // Clear alerts and hide UI settings - store.dispatch(clearSubscription()); - if (me) { - pushNotificationsSetting.remove(me); - } - - try { - getRegistration() - .then(getPushSubscription) - .then(unsubscribe); - } catch (e) { - - } - }); - } else { - console.warn('Your browser does not support Web Push Notifications.'); - } -} diff --git a/app/javascript/styles/application.scss b/app/javascript/styles/application.scss index 44aa10564..fd6665f65 100644 --- a/app/javascript/styles/application.scss +++ b/app/javascript/styles/application.scss @@ -6,6 +6,7 @@ @import 'mastodon/reset'; @import 'mastodon/basics'; +@import 'mastodon/modal'; @import 'mastodon/containers'; @import 'mastodon/lists'; @import 'mastodon/footer'; diff --git a/app/javascript/styles/mastodon/admin.scss b/app/javascript/styles/mastodon/admin.scss index bddea557b..0c343e1df 100644 --- a/app/javascript/styles/mastodon/admin.scss +++ b/app/javascript/styles/mastodon/admin.scss @@ -398,10 +398,12 @@ } } + &__content { + max-width: calc(100% - 90px); + } + &__title { - overflow: hidden; - text-overflow: ellipsis; - white-space: nowrap; + word-wrap: break-word; } &__timestamp { @@ -415,7 +417,7 @@ color: $ui-primary-color; font-family: 'mastodon-font-monospace', monospace; font-size: 12px; - white-space: nowrap; + word-wrap: break-word; min-height: 20px; } diff --git a/app/javascript/styles/mastodon/components.scss b/app/javascript/styles/mastodon/components.scss index db07c3dfa..dec489e9a 100644 --- a/app/javascript/styles/mastodon/components.scss +++ b/app/javascript/styles/mastodon/components.scss @@ -214,6 +214,7 @@ .dropdown-menu { position: absolute; + transform-origin: 50% 0; } .dropdown--active .icon-button { @@ -1757,7 +1758,7 @@ position: absolute; top: 0; left: 0; - background: lighten($ui-base-color, 13%); + background: lighten($ui-base-color, 13%) url('../images/wave-drawer.png') no-repeat bottom / 100% auto; box-sizing: border-box; padding: 0; display: flex; @@ -1770,6 +1771,11 @@ &.darker { background: $ui-base-color; } + + > .mastodon { + background: url('../images/mastodon-ui.png') no-repeat left bottom / contain; + flex: 1; + } } .pseudo-drawer { @@ -2066,20 +2072,18 @@ cursor: default; } +.getting-started__wrapper, +.getting_started { + background: $ui-base-color; +} + .getting-started__wrapper { position: relative; overflow-y: auto; } -.getting-started__footer { - display: flex; - flex-direction: column; -} - .getting-started { - box-sizing: border-box; - padding-bottom: 235px; - background: url('../images/mastodon-getting-started.png') no-repeat 0 100%; + background: $ui-base-color; flex: 1 0 auto; p { @@ -2104,7 +2108,7 @@ padding: 0 10px 8px; } - code { + kbd { display: inline-block; padding: 3px 5px; background-color: lighten($ui-base-color, 8%); @@ -2148,7 +2152,8 @@ @import 'boost'; -button.icon-button i.fa-retweet { +.no-reduce-motion button.icon-button i.fa-retweet { + background-position: 0 0; height: 19px; transition: background-position 0.9s steps(10); @@ -2159,13 +2164,23 @@ button.icon-button i.fa-retweet { &::before { display: none !important; } + } -button.icon-button.active i.fa-retweet { +.no-reduce-motion button.icon-button.active i.fa-retweet { transition-duration: 0.9s; background-position: 0 100%; } +.reduce-motion button.icon-button i.fa-retweet { + color: $ui-base-lighter-color; + transition: color 100ms ease-in; +} + +.reduce-motion button.icon-button.active i.fa-retweet { + color: $ui-highlight-color; +} + .status-card { display: flex; cursor: pointer; @@ -2943,6 +2958,7 @@ button.icon-button.active i.fa-retweet { border-radius: 4px; margin-left: 40px; overflow: hidden; + transform-origin: 50% 0; } .privacy-dropdown__option { diff --git a/app/javascript/styles/mastodon/modal.scss b/app/javascript/styles/mastodon/modal.scss new file mode 100644 index 000000000..310dcb924 --- /dev/null +++ b/app/javascript/styles/mastodon/modal.scss @@ -0,0 +1,20 @@ +.modal-layout { + background: $ui-base-color url('../images/wave-modal.png') repeat-x bottom fixed; + display: flex; + flex-direction: column; + height: 100vh; + padding: 0; +} + +.modal-layout__mastodon { + display: flex; + flex: 1; + flex-direction: column; + justify-content: flex-end; + + > * { + flex: 1; + max-height: 235px; + background: url('../images/mastodon-ui.png') no-repeat left bottom / contain; + } +} diff --git a/app/lib/activity_tracker.rb b/app/lib/activity_tracker.rb new file mode 100644 index 000000000..5b4972674 --- /dev/null +++ b/app/lib/activity_tracker.rb @@ -0,0 +1,31 @@ +# frozen_string_literal: true + +class ActivityTracker + EXPIRE_AFTER = 90.days.seconds + + class << self + def increment(prefix) + key = [prefix, current_week].join(':') + + redis.incrby(key, 1) + redis.expire(key, EXPIRE_AFTER) + end + + def record(prefix, value) + key = [prefix, current_week].join(':') + + redis.pfadd(key, value) + redis.expire(key, EXPIRE_AFTER) + end + + private + + def redis + Redis.current + end + + def current_week + Time.zone.today.cweek + end + end +end diff --git a/app/lib/activitypub/activity/accept.rb b/app/lib/activitypub/activity/accept.rb index bd90c9019..d0082483c 100644 --- a/app/lib/activitypub/activity/accept.rb +++ b/app/lib/activitypub/activity/accept.rb @@ -2,16 +2,18 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity def perform - case @object['type'] - when 'Follow' - accept_follow + if @object.respond_to?(:[]) && + @object['type'] == 'Follow' && @object['actor'].present? + accept_follow_from @object['actor'] + else + accept_follow_object @object end end private - def accept_follow - target_account = account_from_uri(target_uri) + def accept_follow_from(actor) + target_account = account_from_uri(value_or_id(actor)) return if target_account.nil? || !target_account.local? @@ -19,7 +21,8 @@ class ActivityPub::Activity::Accept < ActivityPub::Activity follow_request&.authorize! end - def target_uri - @target_uri ||= value_or_id(@object['actor']) + def accept_follow_object(object) + follow_request = ActivityPub::TagManager.instance.uri_to_resource(value_or_id(object), FollowRequest) + follow_request&.authorize! end end diff --git a/app/lib/activitypub/activity/delete.rb b/app/lib/activitypub/activity/delete.rb index d0fb49342..5fa60a81c 100644 --- a/app/lib/activitypub/activity/delete.rb +++ b/app/lib/activitypub/activity/delete.rb @@ -13,6 +13,7 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity def delete_person SuspendAccountService.new.call(@account) + @account.destroy! end def delete_note diff --git a/app/lib/activitypub/tag_manager.rb b/app/lib/activitypub/tag_manager.rb index 0708713e6..1c35e1672 100644 --- a/app/lib/activitypub/tag_manager.rb +++ b/app/lib/activitypub/tag_manager.rb @@ -28,6 +28,8 @@ class ActivityPub::TagManager return target.uri if target.respond_to?(:local?) && !target.local? case target.object_type + when :follow + account_follow_url(target.account.username, target) when :person account_url(target) when :note, :comment, :activity @@ -97,6 +99,12 @@ class ActivityPub::TagManager case klass.name when 'Account' klass.find_local(uri_to_local_id(uri, :username)) + when 'FollowRequest' + params = Rails.application.routes.recognize_path(uri) + klass.joins(:account).find_by!( + accounts: { domain: nil, username: params[:account_username] }, + id: params[:id] + ) else StatusFinder.new(uri).status end diff --git a/app/lib/formatter.rb b/app/lib/formatter.rb index f5bf64cc7..8c0f8cebc 100644 --- a/app/lib/formatter.rb +++ b/app/lib/formatter.rb @@ -171,10 +171,10 @@ class Formatter end def link_to_url(entity) - normalized_url = Addressable::URI.parse(entity[:url]).normalize - html_attrs = { target: '_blank', rel: 'nofollow noopener' } + url = Addressable::URI.parse(entity[:url]) + html_attrs = { target: '_blank', rel: 'nofollow noopener' } - Twitter::Autolink.send(:link_to_text, entity, link_html(entity[:url]), normalized_url, html_attrs) + Twitter::Autolink.send(:link_to_text, entity, link_html(entity[:url]), url, html_attrs) rescue Addressable::URI::InvalidURIError, IDN::Idna::IdnaError encode(entity[:url]) end diff --git a/app/lib/provider_discovery.rb b/app/lib/provider_discovery.rb index 04ba38101..5732e4fcb 100644 --- a/app/lib/provider_discovery.rb +++ b/app/lib/provider_discovery.rb @@ -29,7 +29,7 @@ class ProviderDiscovery < OEmbed::ProviderDiscovery end if format.nil? || format == :xml - provider_endpoint ||= html.at_xpath('//link[@type="application/xml+oembed"]')&.attribute('href')&.value + provider_endpoint ||= html.at_xpath('//link[@type="text/xml+oembed"]')&.attribute('href')&.value format ||= :xml if provider_endpoint end diff --git a/app/lib/sanitize_config.rb b/app/lib/sanitize_config.rb index f09288fcd..c2b466924 100644 --- a/app/lib/sanitize_config.rb +++ b/app/lib/sanitize_config.rb @@ -6,14 +6,14 @@ class Sanitize CLASS_WHITELIST_TRANSFORMER = lambda do |env| node = env[:node] - class_list = node['class']&.split(' ') + class_list = node['class']&.split(/[\t\n\f\r ]/) return unless class_list class_list.keep_if do |e| - return true if e =~ /^(h|p|u|dt|e)-/ # microformats classes - return true if e =~ /^(mention|hashtag)$/ # semantic classes - return true if e =~ /^(ellipsis|invisible)$/ # link formatting classes + next true if e =~ /^(h|p|u|dt|e)-/ # microformats classes + next true if e =~ /^(mention|hashtag)$/ # semantic classes + next true if e =~ /^(ellipsis|invisible)$/ # link formatting classes end node['class'] = class_list.join(' ') diff --git a/app/mailers/user_mailer.rb b/app/mailers/user_mailer.rb index 5a062dc25..7821be32b 100644 --- a/app/mailers/user_mailer.rb +++ b/app/mailers/user_mailer.rb @@ -13,7 +13,9 @@ class UserMailer < Devise::Mailer return if @resource.disabled? I18n.with_locale(@resource.locale || I18n.default_locale) do - mail to: @resource.unconfirmed_email.blank? ? @resource.email : @resource.unconfirmed_email, subject: I18n.t('devise.mailer.confirmation_instructions.subject', instance: @instance) + mail to: @resource.unconfirmed_email.blank? ? @resource.email : @resource.unconfirmed_email, + subject: I18n.t(@resource.pending_reconfirmation? ? 'devise.mailer.reconfirmation_instructions.subject' : 'devise.mailer.confirmation_instructions.subject', instance: @instance), + template_name: @resource.pending_reconfirmation? ? 'reconfirmation_instructions' : 'confirmation_instructions' end end @@ -39,4 +41,15 @@ class UserMailer < Devise::Mailer mail to: @resource.email, subject: I18n.t('devise.mailer.password_change.subject') end end + + def email_changed(user, **) + @resource = user + @instance = Rails.configuration.x.local_domain + + return if @resource.disabled? + + I18n.with_locale(@resource.locale || I18n.default_locale) do + mail to: @resource.email, subject: I18n.t('devise.mailer.email_changed.subject') + end + end end diff --git a/app/models/follow_request.rb b/app/models/follow_request.rb index ebf6959ce..33e5fec12 100644 --- a/app/models/follow_request.rb +++ b/app/models/follow_request.rb @@ -21,6 +21,10 @@ class FollowRequest < ApplicationRecord validates :account_id, uniqueness: { scope: :target_account_id } + def object_type + :follow + end + def authorize! account.follow!(target_account, reblogs: show_reblogs) MergeWorker.perform_async(target_account.id, account.id) diff --git a/app/models/form/admin_settings.rb b/app/models/form/admin_settings.rb index c1d2cf420..dd629279c 100644 --- a/app/models/form/admin_settings.rb +++ b/app/models/form/admin_settings.rb @@ -30,6 +30,10 @@ class Form::AdminSettings :bootstrap_timeline_accounts=, :min_invite_role, :min_invite_role=, + :activity_api_enabled, + :activity_api_enabled=, + :peers_api_enabled, + :peers_api_enabled=, to: Setting ) end diff --git a/app/models/status.rb b/app/models/status.rb index 8579ff9e4..00dcec624 100644 --- a/app/models/status.rb +++ b/app/models/status.rb @@ -135,6 +135,7 @@ class Status < ApplicationRecord end after_create_commit :store_uri, if: :local? + after_create_commit :update_statistics, if: :local? around_create Mastodon::Snowflake::Callbacks @@ -308,4 +309,9 @@ class Status < ApplicationRecord def set_local self.local = account.local? end + + def update_statistics + return unless public_visibility? || unlisted_visibility? + ActivityTracker.increment('activity:statuses:local') + end end diff --git a/app/models/user.rb b/app/models/user.rb index 578622fdf..9459db7fe 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -41,12 +41,15 @@ class User < ApplicationRecord ACTIVE_DURATION = 14.days - devise :registerable, :recoverable, - :rememberable, :trackable, :validatable, :confirmable, - :two_factor_authenticatable, :two_factor_backupable, - otp_secret_encryption_key: ENV['OTP_SECRET'], + devise :two_factor_authenticatable, + otp_secret_encryption_key: ENV['OTP_SECRET'] + + devise :two_factor_backupable, otp_number_of_backup_codes: 10 + devise :registerable, :recoverable, :rememberable, :trackable, :validatable, + :confirmable + belongs_to :account, inverse_of: :user, required: true belongs_to :invite, counter_cache: :uses accepts_nested_attributes_for :account @@ -122,9 +125,19 @@ class User < ApplicationRecord update!(disabled: false) end + def confirm + new_user = !confirmed? + + super + update_statistics! if new_user + end + def confirm! + new_user = !confirmed? + skip_confirmation! save! + update_statistics! if new_user end def promote! @@ -202,4 +215,9 @@ class User < ApplicationRecord def sanitize_languages filtered_languages.reject!(&:blank?) end + + def update_statistics! + BootstrapTimelineWorker.perform_async(account_id) + ActivityTracker.increment('activity:accounts:local') + end end diff --git a/app/serializers/activitypub/delete_actor_serializer.rb b/app/serializers/activitypub/delete_actor_serializer.rb new file mode 100644 index 000000000..dfea9db4a --- /dev/null +++ b/app/serializers/activitypub/delete_actor_serializer.rb @@ -0,0 +1,22 @@ +# frozen_string_literal: true + +class ActivityPub::DeleteActorSerializer < ActiveModel::Serializer + attributes :id, :type, :actor + attribute :virtual_object, key: :object + + def id + [ActivityPub::TagManager.instance.uri_for(object), '#delete'].join + end + + def type + 'Delete' + end + + def actor + ActivityPub::TagManager.instance.uri_for(object) + end + + def virtual_object + actor + end +end diff --git a/app/serializers/activitypub/follow_serializer.rb b/app/serializers/activitypub/follow_serializer.rb index 86c9992fe..eecd64701 100644 --- a/app/serializers/activitypub/follow_serializer.rb +++ b/app/serializers/activitypub/follow_serializer.rb @@ -1,11 +1,12 @@ # frozen_string_literal: true class ActivityPub::FollowSerializer < ActiveModel::Serializer - attributes :id, :type, :actor + attributes :type, :actor + attribute :id, if: :dereferencable? attribute :virtual_object, key: :object def id - [ActivityPub::TagManager.instance.uri_for(object.account), '#follows/', object.id].join + ActivityPub::TagManager.instance.uri_for(object) end def type @@ -19,4 +20,8 @@ class ActivityPub::FollowSerializer < ActiveModel::Serializer def virtual_object ActivityPub::TagManager.instance.uri_for(object.target_account) end + + def dereferencable? + object.respond_to?(:object_type) + end end diff --git a/app/serializers/rest/instance_serializer.rb b/app/serializers/rest/instance_serializer.rb index 2898011fd..ae1dbe6b5 100644 --- a/app/serializers/rest/instance_serializer.rb +++ b/app/serializers/rest/instance_serializer.rb @@ -27,7 +27,7 @@ class REST::InstanceSerializer < ActiveModel::Serializer end def thumbnail - full_asset_url(instance_presenter.thumbnail.file.url) if instance_presenter.thumbnail + instance_presenter.thumbnail ? full_asset_url(instance_presenter.thumbnail.file.url) : full_pack_url('preview.jpg') end def stats diff --git a/app/services/activitypub/process_account_service.rb b/app/services/activitypub/process_account_service.rb index 06ca75563..0fbf18c00 100644 --- a/app/services/activitypub/process_account_service.rb +++ b/app/services/activitypub/process_account_service.rb @@ -74,7 +74,7 @@ class ActivityPub::ProcessAccountService < BaseService @account.statuses_count = outbox_total_items if outbox_total_items.present? @account.following_count = following_total_items if following_total_items.present? @account.followers_count = followers_total_items if followers_total_items.present? - @account.moved_to_account = moved_account if @json['movedTo'].present? + @account.moved_to_account = @json['movedTo'].present? ? moved_account : nil end def after_protocol_change! diff --git a/app/services/batched_remove_status_service.rb b/app/services/batched_remove_status_service.rb index 6b6b0c418..e2763c2b9 100644 --- a/app/services/batched_remove_status_service.rb +++ b/app/services/batched_remove_status_service.rb @@ -17,9 +17,7 @@ class BatchedRemoveStatusService < BaseService @stream_entry_batches = [] @salmon_batches = [] - @activity_json_batches = [] @json_payloads = statuses.map { |s| [s.id, Oj.dump(event: :delete, payload: s.id.to_s)] }.to_h - @activity_json = {} @activity_xml = {} # Ensure that rendered XML reflects destroyed state @@ -32,10 +30,7 @@ class BatchedRemoveStatusService < BaseService unpush_from_home_timelines(account, account_statuses) unpush_from_list_timelines(account, account_statuses) - if account.local? - batch_stream_entries(account, account_statuses) - batch_activity_json(account, account_statuses) - end + batch_stream_entries(account, account_statuses) if account.local? end # Cannot be batched @@ -46,7 +41,6 @@ class BatchedRemoveStatusService < BaseService Pubsubhubbub::RawDistributionWorker.push_bulk(@stream_entry_batches) { |batch| batch } NotificationWorker.push_bulk(@salmon_batches) { |batch| batch } - ActivityPub::DeliveryWorker.push_bulk(@activity_json_batches) { |batch| batch } end private @@ -57,22 +51,6 @@ class BatchedRemoveStatusService < BaseService end end - def batch_activity_json(account, statuses) - account.followers.inboxes.each do |inbox_url| - statuses.each do |status| - @activity_json_batches << [build_json(status), account.id, inbox_url] - end - end - - statuses.each do |status| - other_recipients = (status.mentions + status.reblogs).map(&:account).reject(&:local?).select(&:activitypub?).uniq(&:id) - - other_recipients.each do |target_account| - @activity_json_batches << [build_json(status), account.id, target_account.inbox_url] - end - end - end - def unpush_from_home_timelines(account, statuses) recipients = account.followers.local.to_a @@ -123,23 +101,9 @@ class BatchedRemoveStatusService < BaseService Redis.current end - def build_json(status) - return @activity_json[status.id] if @activity_json.key?(status.id) - - @activity_json[status.id] = sign_json(status, ActiveModelSerializers::SerializableResource.new( - status, - serializer: status.reblog? ? ActivityPub::UndoAnnounceSerializer : ActivityPub::DeleteSerializer, - adapter: ActivityPub::Adapter - ).as_json) - end - def build_xml(stream_entry) return @activity_xml[stream_entry.id] if @activity_xml.key?(stream_entry.id) @activity_xml[stream_entry.id] = stream_entry_to_xml(stream_entry) end - - def sign_json(status, json) - Oj.dump(ActivityPub::LinkedDataSignature.new(json).sign!(status.account)) - end end diff --git a/app/services/fetch_atom_service.rb b/app/services/fetch_atom_service.rb index 1c47a22da..c07859845 100644 --- a/app/services/fetch_atom_service.rb +++ b/app/services/fetch_atom_service.rb @@ -46,11 +46,13 @@ class FetchAtomService < BaseService json = body_to_json(@response.to_s) if supported_context?(json) && json['type'] == 'Person' && json['inbox'].present? [json['id'], { prefetched_body: @response.to_s, id: true }, :activitypub] + elsif supported_context?(json) && json['type'] == 'Note' + [json['id'], { prefetched_body: @response.to_s, id: true }, :activitypub] else @unsupported_activity = true nil end - elsif @response['Link'] && !terminal + elsif @response['Link'] && !terminal && link_header.find_link(%w(rel alternate)) process_headers elsif @response.mime_type == 'text/html' && !terminal process_html @@ -70,8 +72,6 @@ class FetchAtomService < BaseService end def process_headers - link_header = LinkHeader.parse(@response['Link'].is_a?(Array) ? @response['Link'].first : @response['Link']) - json_link = link_header.find_link(%w(rel alternate), %w(type application/activity+json)) || link_header.find_link(%w(rel alternate), ['type', 'application/ld+json; profile="https://www.w3.org/ns/activitystreams"']) atom_link = link_header.find_link(%w(rel alternate), %w(type application/atom+xml)) @@ -80,4 +80,8 @@ class FetchAtomService < BaseService result end + + def link_header + @link_header ||= LinkHeader.parse(@response['Link'].is_a?(Array) ? @response['Link'].first : @response['Link']) + end end diff --git a/app/services/process_mentions_service.rb b/app/services/process_mentions_service.rb index e12721c46..46401f298 100644 --- a/app/services/process_mentions_service.rb +++ b/app/services/process_mentions_service.rb @@ -11,18 +11,20 @@ class ProcessMentionsService < BaseService return unless status.local? status.text = status.text.gsub(Account::MENTION_RE) do |match| - begin - mentioned_account = resolve_remote_account_service.call($1) - rescue Goldfinger::Error, HTTP::Error - mentioned_account = nil + username, domain = $1.split('@') + mentioned_account = Account.find_remote(username, domain) + + if mention_undeliverable?(status, mentioned_account) + begin + mentioned_account = resolve_remote_account_service.call($1) + rescue Goldfinger::Error, HTTP::Error + mentioned_account = nil + end end - if mentioned_account.nil? - username, domain = $1.split('@') - mentioned_account = Account.find_remote(username, domain) - end + mentioned_account ||= Account.find_remote(username, domain) - next match if mentioned_account.nil? || (!mentioned_account.local? && mentioned_account.ostatus? && status.stream_entry.hidden?) + next match if mention_undeliverable?(status, mentioned_account) mentioned_account.mentions.where(status: status).first_or_create(status: status) "@#{mentioned_account.acct}" @@ -37,6 +39,10 @@ class ProcessMentionsService < BaseService private + def mention_undeliverable?(status, mentioned_account) + mentioned_account.nil? || (!mentioned_account.local? && mentioned_account.ostatus? && status.stream_entry.hidden?) + end + def create_notification(status, mention) mentioned_account = mention.account diff --git a/app/services/suspend_account_service.rb b/app/services/suspend_account_service.rb index 958b28cdc..56fa2d8dd 100644 --- a/app/services/suspend_account_service.rb +++ b/app/services/suspend_account_service.rb @@ -22,6 +22,8 @@ class SuspendAccountService < BaseService end def purge_content! + ActivityPub::RawDistributionWorker.perform_async(delete_actor_json, @account.id) if @account.local? + @account.statuses.reorder(nil).find_in_batches do |statuses| BatchedRemoveStatusService.new.call(statuses) end @@ -54,4 +56,14 @@ class SuspendAccountService < BaseService def destroy_all(association) association.in_batches.destroy_all end + + def delete_actor_json + payload = ActiveModelSerializers::SerializableResource.new( + @account, + serializer: ActivityPub::DeleteActorSerializer, + adapter: ActivityPub::Adapter + ).as_json + + Oj.dump(ActivityPub::LinkedDataSignature.new(payload).sign!(@account)) + end end diff --git a/app/views/accounts/_header.html.haml b/app/views/accounts/_header.html.haml index d4081af64..3800bd837 100644 --- a/app/views/accounts/_header.html.haml +++ b/app/views/accounts/_header.html.haml @@ -8,7 +8,7 @@ = fa_icon 'user-times' = t('accounts.unfollow') - else - = link_to account_follow_path(account), data: { method: :post }, class: 'icon-button' do + = link_to account_follows_path(account), data: { method: :post }, class: 'icon-button' do = fa_icon 'user-plus' = t('accounts.follow') - elsif !user_signed_in? diff --git a/app/views/admin/accounts/_account.html.haml b/app/views/admin/accounts/_account.html.haml index 598f6cddd..dfa7c5649 100644 --- a/app/views/admin/accounts/_account.html.haml +++ b/app/views/admin/accounts/_account.html.haml @@ -6,7 +6,10 @@ = link_to account.domain, admin_accounts_path(by_domain: account.domain) %td - if account.local? - = t("admin.accounts.roles.#{account.user&.role}") + - if account.user.nil? + = t("admin.accounts.moderation.suspended") + - else + = t("admin.accounts.roles.#{account.user.role}") - else = account.protocol.humanize %td diff --git a/app/views/admin/custom_emojis/_custom_emoji.html.haml b/app/views/admin/custom_emojis/_custom_emoji.html.haml index f7fd2538c..fbaa9a174 100644 --- a/app/views/admin/custom_emojis/_custom_emoji.html.haml +++ b/app/views/admin/custom_emojis/_custom_emoji.html.haml @@ -11,18 +11,18 @@ %td - if custom_emoji.local? - if custom_emoji.visible_in_picker - = table_link_to 'eye', t('admin.custom_emojis.listed'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: false }), method: :patch + = table_link_to 'eye', t('admin.custom_emojis.listed'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: false }, page: params[:page], **@filter_params), method: :patch - else - = table_link_to 'eye-slash', t('admin.custom_emojis.unlisted'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: true }), method: :patch + = table_link_to 'eye-slash', t('admin.custom_emojis.unlisted'), admin_custom_emoji_path(custom_emoji, custom_emoji: { visible_in_picker: true }, page: params[:page], **@filter_params), method: :patch - else - if custom_emoji.local_counterpart.present? - = link_to safe_join([custom_emoji_tag(custom_emoji.local_counterpart), t('admin.custom_emojis.overwrite')]), copy_admin_custom_emoji_path(custom_emoji, page: params[:page]), method: :post, class: 'table-action-link' + = link_to safe_join([custom_emoji_tag(custom_emoji.local_counterpart), t('admin.custom_emojis.overwrite')]), copy_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post, class: 'table-action-link' - else - = table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji, page: params[:page]), method: :post + = table_link_to 'copy', t('admin.custom_emojis.copy'), copy_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post %td - if custom_emoji.disabled? - = table_link_to 'power-off', t('admin.custom_emojis.enable'), enable_admin_custom_emoji_path(custom_emoji), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } + = table_link_to 'power-off', t('admin.custom_emojis.enable'), enable_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } - else - = table_link_to 'power-off', t('admin.custom_emojis.disable'), disable_admin_custom_emoji_path(custom_emoji), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } + = table_link_to 'power-off', t('admin.custom_emojis.disable'), disable_admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :post, data: { confirm: t('admin.accounts.are_you_sure') } %td - = table_link_to 'times', t('admin.custom_emojis.delete'), admin_custom_emoji_path(custom_emoji), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } + = table_link_to 'times', t('admin.custom_emojis.delete'), admin_custom_emoji_path(custom_emoji, page: params[:page], **@filter_params), method: :delete, data: { confirm: t('admin.accounts.are_you_sure') } diff --git a/app/views/admin/custom_emojis/index.html.haml b/app/views/admin/custom_emojis/index.html.haml index 89ea3a6fe..3a119276c 100644 --- a/app/views/admin/custom_emojis/index.html.haml +++ b/app/views/admin/custom_emojis/index.html.haml @@ -29,7 +29,7 @@ .actions %button= t('admin.accounts.search') - = link_to t('admin.accounts.reset'), admin_accounts_path, class: 'button negative' + = link_to t('admin.accounts.reset'), admin_custom_emojis_path, class: 'button negative' .table-wrapper %table.table diff --git a/app/views/admin/settings/edit.html.haml b/app/views/admin/settings/edit.html.haml index c7c25f528..4f9115ed2 100644 --- a/app/views/admin/settings/edit.html.haml +++ b/app/views/admin/settings/edit.html.haml @@ -46,5 +46,13 @@ .fields-group = f.input :bootstrap_timeline_accounts, wrapper: :with_block_label, label: t('admin.settings.bootstrap_timeline_accounts.title'), hint: t('admin.settings.bootstrap_timeline_accounts.desc_html') + %hr/ + + .fields-group + = f.input :activity_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.activity_api_enabled.title'), hint: t('admin.settings.activity_api_enabled.desc_html') + + .fields-group + = f.input :peers_api_enabled, as: :boolean, wrapper: :with_label, label: t('admin.settings.peers_api_enabled.title'), hint: t('admin.settings.peers_api_enabled.desc_html') + .actions = f.button :button, t('generic.save_changes'), type: :submit diff --git a/app/views/layouts/application.html.haml b/app/views/layouts/application.html.haml index ee995c987..f38c59165 100755 --- a/app/views/layouts/application.html.haml +++ b/app/views/layouts/application.html.haml @@ -2,8 +2,7 @@ %html{ lang: I18n.locale } %head %meta{ charset: 'utf-8' }/ - %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }/ - %meta{ 'http-equiv' => 'X-UA-Compatible', content: 'IE=edge' }/ + %meta{ name: 'viewport', content: 'width=device-width, initial-scale=1' }/ %link{ rel: 'icon', href: favicon_path, type: 'image/x-icon' }/ %link{ rel: 'apple-touch-icon', sizes: '180x180', href: '/apple-touch-icon.png' }/ %link{ rel: 'mask-icon', href: '/mask-icon.svg', color: '#2B90D9' }/ @@ -28,6 +27,7 @@ - body_classes ||= @body_classes || '' - body_classes += ' system-font' if current_account&.user&.setting_system_font_ui + - body_classes += current_account&.user&.setting_reduce_motion ? ' reduce-motion' : ' no-reduce-motion' %body{ class: add_rtl_body_class(body_classes) } = content_for?(:content) ? yield(:content) : yield diff --git a/app/views/layouts/modal.html.haml b/app/views/layouts/modal.html.haml index a819e098d..d01b6b6c5 100644 --- a/app/views/layouts/modal.html.haml +++ b/app/views/layouts/modal.html.haml @@ -12,5 +12,7 @@ = fa_icon 'sign-out' .container= yield + .modal-layout__mastodon + %div = render template: 'layouts/application' diff --git a/app/views/user_mailer/confirmation_instructions.oc.html.erb b/app/views/user_mailer/confirmation_instructions.oc.html.erb index 7a16db67a..5657e40d4 100644 --- a/app/views/user_mailer/confirmation_instructions.oc.html.erb +++ b/app/views/user_mailer/confirmation_instructions.oc.html.erb @@ -7,7 +7,7 @@

Aprèp vòstra primièra connexion, poiretz accedir a la documentacion de l’aisina.

-

Pensatz tanben de gaitar nòstras <%= link_to 'conditions d\'utilisation', terms_url %>.

+

Pensatz tanben de gaitar nòstres <%= link_to 'tèrmes e condicions d\'utilizacion', terms_url %>.

Amistosament,

diff --git a/app/views/user_mailer/confirmation_instructions.oc.text.erb b/app/views/user_mailer/confirmation_instructions.oc.text.erb index bf2acfec1..fe04fe3d0 100644 --- a/app/views/user_mailer/confirmation_instructions.oc.text.erb +++ b/app/views/user_mailer/confirmation_instructions.oc.text.erb @@ -7,7 +7,7 @@ er confirmar vòstra inscripcion, mercés de clicar sul ligam seguent :  Aprèp vòstra primièra connexion, poiretz accedir a la documentacion de l’aisina. -Pensatz tanben de gaitar nòstras <%= link_to 'conditions d\'utilisation', terms_url %>. +Pensatz tanben de gaitar nòstres <%= link_to 'tèrmes e condicions d\'utilizacion', terms_url %>. Amistosament, diff --git a/app/views/user_mailer/confirmation_instructions.pl.html.erb b/app/views/user_mailer/confirmation_instructions.pl.html.erb index 973950184..2285b5c6e 100644 --- a/app/views/user_mailer/confirmation_instructions.pl.html.erb +++ b/app/views/user_mailer/confirmation_instructions.pl.html.erb @@ -1,4 +1,4 @@ -

Witaj, <%= @resource.email %> !

+

Witaj, <%= @resource.email %>!

Właśnie utworzyłeś konto na instancji <%= @instance %>.

diff --git a/app/views/user_mailer/confirmation_instructions.pl.text.erb b/app/views/user_mailer/confirmation_instructions.pl.text.erb index 9640d8ddd..f20082e16 100644 --- a/app/views/user_mailer/confirmation_instructions.pl.text.erb +++ b/app/views/user_mailer/confirmation_instructions.pl.text.erb @@ -1,4 +1,4 @@ -Witaj, <%= @resource.email %> ! +Witaj, <%= @resource.email %>! Właśnie utworzyłeś konto na instancji <%= @instance %>. diff --git a/app/views/user_mailer/confirmation_instructions.sr-Latn.html.erb b/app/views/user_mailer/confirmation_instructions.sr-Latn.html.erb new file mode 100644 index 000000000..a16008250 --- /dev/null +++ b/app/views/user_mailer/confirmation_instructions.sr-Latn.html.erb @@ -0,0 +1,15 @@ +

Dobrodošao <%= @resource.email %> !

+ +

Upravo ste napravili nalog na instanci <%= @instance %>.

+ +

Da potvrdite Vašu registraciju, molimo Vas kliknite na sledeći link:
+<%= link_to 'Potvrdi moj nalog', confirmation_url(@resource, confirmation_token: @token) %>

+ +

Ako link iznad ne radi, kopirajte i nalepite ovu adresu u adresnu traku:
+<%= confirmation_url(@resource, confirmation_token: @token) %> + +

Takođe pogledajte i <%= link_to 'pravila i uslove korišćenja', terms_url %>.

+ +

S poštovanjem,

+ +

<%= @instance %> tim

diff --git a/app/views/user_mailer/confirmation_instructions.sr-Latn.text.erb b/app/views/user_mailer/confirmation_instructions.sr-Latn.text.erb new file mode 100644 index 000000000..60fe9db0d --- /dev/null +++ b/app/views/user_mailer/confirmation_instructions.sr-Latn.text.erb @@ -0,0 +1,12 @@ +Dobrodošao <%= @resource.email %> ! + +Upravo ste napravili nalog na instanci <%= @instance %>. + +Da potvrdite Vašu registraciju, molimo Vas kliknite na sledeći link: +<%= confirmation_url(@resource, confirmation_token: @token) %> + +Takođe pogledajte i pravila i uslove korišćenja <%= terms_url %> + +S poštovanjem, + +<%= @instance %> tim diff --git a/app/views/user_mailer/confirmation_instructions.sr.html.erb b/app/views/user_mailer/confirmation_instructions.sr.html.erb new file mode 100644 index 000000000..09203cc9a --- /dev/null +++ b/app/views/user_mailer/confirmation_instructions.sr.html.erb @@ -0,0 +1,15 @@ +

Добродошао <%= @resource.email %> !

+ +

Управо сте направили налог на инстанци <%= @instance %>.

+ +

Да потврдите Вашу регистрацију, молимо Вас кликните на следећи линк:
+<%= link_to 'Потврди мој налог', confirmation_url(@resource, confirmation_token: @token) %>

+ +

Ако линк изнад не ради, копирајте и налепите ову адресу у адресну траку:
+<%= confirmation_url(@resource, confirmation_token: @token) %> + +

Такође погледајте и <%= link_to 'правила и услове коришћења', terms_url %>.

+ +

С поштовањем,

+ +

<%= @instance %> тим

diff --git a/app/views/user_mailer/confirmation_instructions.sr.text.erb b/app/views/user_mailer/confirmation_instructions.sr.text.erb new file mode 100644 index 000000000..e7cb7e188 --- /dev/null +++ b/app/views/user_mailer/confirmation_instructions.sr.text.erb @@ -0,0 +1,12 @@ +Добродошао <%= @resource.email %> ! + +Управо сте направили налог на инстанци <%= @instance %>. + +Да потврдите Вашу регистрацију, молимо Вас кликните на следећи линк: +<%= confirmation_url(@resource, confirmation_token: @token) %> + +Такође погледајте и правила и услове коришћења <%= terms_url %> + +С поштовањем, + +<%= @instance %> тим diff --git a/app/views/user_mailer/email_changed.en.html.erb b/app/views/user_mailer/email_changed.en.html.erb new file mode 100644 index 000000000..c10680086 --- /dev/null +++ b/app/views/user_mailer/email_changed.en.html.erb @@ -0,0 +1,15 @@ +

Hello <%= @resource.email %>!

+ +<% if @resource&.unconfirmed_email? %> +

We're contacting you to notify you that the email you use on <%= @instance %> is being changed to <%= @resource.unconfirmed_email %>.

+<% else %> +

We're contacting you to notify you that the email you use on <%= @instance %> has been changed to <%= @resource.email %>.

+<% end %> + +

+ If you did not change your email, it is likely that someone has gained access to your account. Please change your password immediately or contact the instance admin if you're locked out of your account. +

+ +

Sincerely,

+ +

The <%= @instance %> team

diff --git a/app/views/user_mailer/email_changed.en.text.erb b/app/views/user_mailer/email_changed.en.text.erb new file mode 100644 index 000000000..971972461 --- /dev/null +++ b/app/views/user_mailer/email_changed.en.text.erb @@ -0,0 +1,13 @@ +Hello <%= @resource.email %>! + +<% if @resource&.unconfirmed_email? %> +We're contacting you to notify you that the email you use on <%= @instance %> is being changed to <%= @resource.unconfirmed_email %>. +<% else %> +We're contacting you to notify you that the email you use on <%= @instance %> has been changed to <%= @resource.email %>. +<% end %> + +If you did not change your email, it is likely that someone has gained access to your account. Please change your password immediately or contact the instance admin if you're locked out of your account. + +Sincerely, + +The <%= @instance %> team diff --git a/app/views/user_mailer/email_changed.ja.html.erb b/app/views/user_mailer/email_changed.ja.html.erb new file mode 100644 index 000000000..c66f409c6 --- /dev/null +++ b/app/views/user_mailer/email_changed.ja.html.erb @@ -0,0 +1,13 @@ +

こんにちは<%= @resource.email %>さん

+ +<% if @resource&.unconfirmed_email? %> +

<%= @instance %>で使っているメールアドレスが<%= @resource.unconfirmed_email %>に変更されようとしています。

+<% else %> +

<%= @instance %>で使っているメールアドレスが<%= @resource.email %>に変更されました。

+<% end %> + +

+ メールアドレスを変更した覚えがない場合、誰かがあなたのアカウントにアクセスしたおそれがあります。すぐにパスワードを変更するか、アカウントにアクセスできない場合はインスタンスの管理者に連絡してください。 +

+ +

<%= @instance %>チームより

diff --git a/app/views/user_mailer/email_changed.ja.text.erb b/app/views/user_mailer/email_changed.ja.text.erb new file mode 100644 index 000000000..33ee6d10b --- /dev/null +++ b/app/views/user_mailer/email_changed.ja.text.erb @@ -0,0 +1,11 @@ +Hello <%= @resource.email %>! + +<% if @resource&.unconfirmed_email? %> +<%= @instance %>で使っているメールアドレスが<%= @resource.unconfirmed_email %>に変更されようとしています。 +<% else %> +<%= @instance %>で使っているメールアドレスが<%= @resource.email %>に変更されました。 +<% end %> + +メールアドレスを変更した覚えがない場合、誰かがあなたのアカウントにアクセスしたおそれがあります。すぐにパスワードを変更するか、アカウントにアクセスできない場合はインスタンスの管理者に連絡してください。 + +<%= @instance %>チームより diff --git a/app/views/user_mailer/email_changed.oc.html.erb b/app/views/user_mailer/email_changed.oc.html.erb new file mode 100644 index 000000000..0f4c891dc --- /dev/null +++ b/app/views/user_mailer/email_changed.oc.html.erb @@ -0,0 +1,15 @@ +

Bonjorn <%= @resource.email %> !

+ +<% if @resource&.unconfirmed_email? %> +

Vos contactem per vos senhalar que l’adreça qu’utilizatz per <%= @instance %> es cambiada per aquesta d’aquí <%= @resource.unconfirmed_email %>.

+<% else %> +

Vos contactem per vos senhalar que l’adreça qu’utilizatz per <%= @instance %> es cambiada per aquesta d’aquí <%= @resource.email %>.

+<% end %> + +

+ S’avètz pas demandat aqueste cambiament d’adreça, poiriá arribar que qualqu’un mai aguèsse agut accès a vòstre compte. Mercés de cambiar sulpic vòstre senhal o de contactar vòstre administrator d’instància se l’accès a vòstre compte vos es barrat. +

+ +

Amistosament,

+ +

La còla <%= @instance %>

diff --git a/app/views/user_mailer/email_changed.oc.text.erb b/app/views/user_mailer/email_changed.oc.text.erb new file mode 100644 index 000000000..2305ef834 --- /dev/null +++ b/app/views/user_mailer/email_changed.oc.text.erb @@ -0,0 +1,13 @@ +Bonjorn <%= @resource.email %> ! + +<% if @resource&.unconfirmed_email? %> +Vos contactem per vos senhalar que l’adreça qu’utilizatz per <%= @instance %> es cambiada per aquesta d’aquí <%= @resource.unconfirmed_email %>. +<% else %> +Vos contactem per vos senhalar que l’adreça qu’utilizatz per <%= @instance %> es cambiada per aquesta d’aquí <%= @resource.email %>. +<% end %> + +S’avètz pas demandat aqueste cambiament d’adreça, poiriá arribar que qualqu’un mai aguèsse agut accès a vòstre compte. Mercés de cambiar sulpic vòstre senhal o de contactar vòstre administrator d’instància se l’accès a vòstre compte vos es barrat. + +Amistosament, + +La còla <%= @instance %> diff --git a/app/views/user_mailer/email_changed.pl.html.erb b/app/views/user_mailer/email_changed.pl.html.erb new file mode 100644 index 000000000..9ed122b0f --- /dev/null +++ b/app/views/user_mailer/email_changed.pl.html.erb @@ -0,0 +1,15 @@ +

Witaj, <%= @resource.email %>!

+ +<% if @resource&.unconfirmed_email? %> +

Informujemy, że e-mail używany przez Ciebie na <%= @instance %> został zmieniony na <%= @resource.unconfirmed_email %>.

+<% else %> +

Informujemy, że e-mail używany przez Ciebie na <%= @instance %> został zmieniony na <%= @resource.email %>.

+<% end %> + +

+ Jeżeli to nie Ty, prawdopodobnie ktoś uzyskał dostęp do Twojego konta. Zalecana jest natychmiastowa zmiana hasła lub skontaktowanie się z administratorem, jeżeli nie masz dostępu do swojego konta. +

+ +

Z pozdrowieniami,

+ +

Zespół <%= @instance %>

diff --git a/app/views/user_mailer/email_changed.pl.text.erb b/app/views/user_mailer/email_changed.pl.text.erb new file mode 100644 index 000000000..134a79e95 --- /dev/null +++ b/app/views/user_mailer/email_changed.pl.text.erb @@ -0,0 +1,13 @@ +Witaj, <%= @resource.email %>! + +<% if @resource&.unconfirmed_email? %> +Informujemy, że e-mail używany przez Ciebie na <%= @instance %> został zmieniony na <%= @resource.unconfirmed_email %>. +<% else %> +Informujemy, że e-mail używany przez Ciebie na <%= @instance %> został zmieniony na <%= @resource.email %>. +<% end %> + +Jeżeli to nie Ty, prawdopodobnie ktoś uzyskał dostęp do Twojego konta. Zalecana jest natychmiastowa zmiana hasła lub skontaktowanie się z administratorem, jeżeli nie masz dostępu do swojego konta. + +Z pozdrowieniami, + +Zespół <%= @instance %> diff --git a/app/views/user_mailer/password_change.sr-Latn.html.erb b/app/views/user_mailer/password_change.sr-Latn.html.erb new file mode 100644 index 000000000..ab4e23bdf --- /dev/null +++ b/app/views/user_mailer/password_change.sr-Latn.html.erb @@ -0,0 +1,3 @@ +

Zdravo <%= @resource.email %>!

+ +

Želimo samo da Vas obavestimo da je Vaša lozinka na Mastodont instanci <%= @instance %> promenjena.

diff --git a/app/views/user_mailer/password_change.sr-Latn.text.erb b/app/views/user_mailer/password_change.sr-Latn.text.erb new file mode 100644 index 000000000..6e0666d8d --- /dev/null +++ b/app/views/user_mailer/password_change.sr-Latn.text.erb @@ -0,0 +1,3 @@ +Zdravo <%= @resource.email %>! + +Želimo samo da Vas obavestimo da je Vaša lozinka na Mastodont instanci <%= @instance %> promenjena. diff --git a/app/views/user_mailer/password_change.sr.html.erb b/app/views/user_mailer/password_change.sr.html.erb new file mode 100644 index 000000000..4bb61b74f --- /dev/null +++ b/app/views/user_mailer/password_change.sr.html.erb @@ -0,0 +1,3 @@ +

Здраво <%= @resource.email %>!

+ +

Желимо само да Вас обавестимо да је Ваша лозинка на Мастодонт инстанци <%= @instance %> промењена.

diff --git a/app/views/user_mailer/password_change.sr.text.erb b/app/views/user_mailer/password_change.sr.text.erb new file mode 100644 index 000000000..9082201c0 --- /dev/null +++ b/app/views/user_mailer/password_change.sr.text.erb @@ -0,0 +1,3 @@ +Здраво <%= @resource.email %>! + +Желимо само да Вас обавестимо да је Ваша лозинка на Мастодонт инстанци <%= @instance %> промењена. diff --git a/app/views/user_mailer/reconfirmation_instructions.en.html.erb b/app/views/user_mailer/reconfirmation_instructions.en.html.erb new file mode 100644 index 000000000..31866a3c8 --- /dev/null +++ b/app/views/user_mailer/reconfirmation_instructions.en.html.erb @@ -0,0 +1,15 @@ +

Hello <%= @resource.unconfirmed_email %>!

+ +

You requested a change to the email address you use on <%= @instance %>.

+ +

To confirm your new email, please click on the following link:
+<%= link_to 'Confirm my email address', confirmation_url(@resource, confirmation_token: @token) %>

+ +

If the above link did not work, copy and paste this URL into your address bar:
+<%= confirmation_url(@resource, confirmation_token: @token) %> + +

Please also check out our <%= link_to 'terms and conditions', terms_url %>.

+ +

Sincerely,

+ +

The <%= @instance %> team

diff --git a/app/views/user_mailer/reconfirmation_instructions.en.text.erb b/app/views/user_mailer/reconfirmation_instructions.en.text.erb new file mode 100644 index 000000000..c1c735b3a --- /dev/null +++ b/app/views/user_mailer/reconfirmation_instructions.en.text.erb @@ -0,0 +1,12 @@ +Hello <%= @resource.unconfirmed_email %>! + +You requested a change to the email address you use on <%= @instance %>. + +To confirm your new email, please click on the following link: +<%= confirmation_url(@resource, confirmation_token: @token) %> + +Please also check out our terms and conditions <%= terms_url %> + +Sincerely, + +The <%= @instance %> team diff --git a/app/views/user_mailer/reconfirmation_instructions.ja.html.erb b/app/views/user_mailer/reconfirmation_instructions.ja.html.erb new file mode 100644 index 000000000..caa53032a --- /dev/null +++ b/app/views/user_mailer/reconfirmation_instructions.ja.html.erb @@ -0,0 +1,13 @@ +

こんにちは<%= @resource.unconfirmed_email %>さん

+ +

<%= @instance %>で使っているメールアドレスの変更をあなたがリクエストしました。

+ +

新しいメールアドレスを確認するには次のリンクをクリックしてください:
+<%= link_to 'わたしのメールアドレスを確認する', confirmation_url(@resource, confirmation_token: @token) %>

+ +

上記のリンクがうまくいかなかった場合はこのURLをコピーしてアドレスバーに貼り付けてください:
+<%= confirmation_url(@resource, confirmation_token: @token) %> + +

また<%= link_to '利用規約', terms_url %>もご確認ください。

+ +

<%= @instance %>チームより

diff --git a/app/views/user_mailer/reconfirmation_instructions.ja.text.erb b/app/views/user_mailer/reconfirmation_instructions.ja.text.erb new file mode 100644 index 000000000..5326e4512 --- /dev/null +++ b/app/views/user_mailer/reconfirmation_instructions.ja.text.erb @@ -0,0 +1,10 @@ +こんにちは<%= @resource.unconfirmed_email %>さん + +<%= @instance %>で使っているメールアドレスの変更をあなたがリクエストしました。 + +新しいメールアドレスを確認するには次のリンクをクリックしてください: +<%= confirmation_url(@resource, confirmation_token: @token) %> + +また利用規約もご確認ください <%= terms_url %> + +<%= @instance %>チームより diff --git a/app/views/user_mailer/reconfirmation_instructions.oc.html.erb b/app/views/user_mailer/reconfirmation_instructions.oc.html.erb new file mode 100644 index 000000000..d5404e49c --- /dev/null +++ b/app/views/user_mailer/reconfirmation_instructions.oc.html.erb @@ -0,0 +1,15 @@ +

Bonjorn <%= @resource.unconfirmed_email %> !

+ +

Avètz demandat a cambiar vòstra adreça de corrièl qu’utilizatz per <%= @instance %>.

+ +

Per confirmar vòstra novèla adreça, mercés de clicar lo ligam seguent :
+<%= link_to 'Confirmar mon adreça', confirmation_url(@resource, confirmation_token: @token) %>

+ +

Se lo ligam al dessús fonciona pas, copiatz e pegatz aquesta URL a la barra d’adreça :
+<%= confirmation_url(@resource, confirmation_token: @token) %> + +

Mercés de gaitar tanben nòstres <%= link_to 'terms and conditions', terms_url %>.

+ +

Amistosament,

+ +

La còla <%= @instance %>

diff --git a/app/views/user_mailer/reconfirmation_instructions.oc.text.erb b/app/views/user_mailer/reconfirmation_instructions.oc.text.erb new file mode 100644 index 000000000..6f174bb3e --- /dev/null +++ b/app/views/user_mailer/reconfirmation_instructions.oc.text.erb @@ -0,0 +1,12 @@ +Bonjorn <%= @resource.unconfirmed_email %> ! + +Avètz demandat a cambiar vòstra adreça de corrièl qu’utilizatz per <%= @instance %>. + +Per confirmar vòstra novèla adreça, mercés de clicar lo ligam seguent : +<%= confirmation_url(@resource, confirmation_token: @token) %> + +Mercés tanben de gaitar nòstres <%= link_to 'terms and conditions', terms_url %>. + +Amistosament, + +La còla <%= @instance %> diff --git a/app/views/user_mailer/reconfirmation_instructions.pl.html.erb b/app/views/user_mailer/reconfirmation_instructions.pl.html.erb new file mode 100644 index 000000000..57cdc42e1 --- /dev/null +++ b/app/views/user_mailer/reconfirmation_instructions.pl.html.erb @@ -0,0 +1,15 @@ +

Witaj, <%= @resource.unconfirmed_email %>!

+ +

Dokonano próby zmiany adresu e-mail, którego używasz na <%= @instance %>.

+ +

Aby potwierdzić posiadanie tego adresu e-mail, kliknij na poniższy odnośnik:
+<%= link_to 'Potwierdź mój adres e-mail', confirmation_url(@resource, confirmation_token: @token) %>

+ +

Jeżeli ten odnośnik nie działa, wklej następujący adres w pasek adresu Twojej przeglądarki:
+<%= confirmation_url(@resource, confirmation_token: @token) %> + +

Pamiętaj o przeczytaniu naszych <%= link_to 'zasad użytkowania', terms_url %>.

+ +

Z pozdrowieniami,

+ +

Zespół <%= @instance %>

diff --git a/app/views/user_mailer/reconfirmation_instructions.pl.text.erb b/app/views/user_mailer/reconfirmation_instructions.pl.text.erb new file mode 100644 index 000000000..032718f81 --- /dev/null +++ b/app/views/user_mailer/reconfirmation_instructions.pl.text.erb @@ -0,0 +1,12 @@ +Witaj, <%= @resource.unconfirmed_email %>! + +Dokonano próby zmiany adresu e-mail, którego używasz na <%= @instance %>. + +Aby potwierdzić posiadanie tego adresu e-mail, kliknij na poniższy odnośnik: +<%= confirmation_url(@resource, confirmation_token: @token) %> + +Pamiętaj o przeczytaniu naszych zasad użytkowania: <%= terms_url %> + +Z pozdrowieniami, + +Zespół <%= @instance %> diff --git a/app/views/user_mailer/reset_password_instructions.sr-Latn.html.erb b/app/views/user_mailer/reset_password_instructions.sr-Latn.html.erb new file mode 100644 index 000000000..7dede16b2 --- /dev/null +++ b/app/views/user_mailer/reset_password_instructions.sr-Latn.html.erb @@ -0,0 +1,8 @@ +

Zdravo <%= @resource.email %>!

+ +

Neko je zatražio link za promenu lozinke na instanci <%= @instance %>. Ovo možete uraditi klikom na link ispod.

+ +

<%= link_to 'Promeni moju lozinku', edit_password_url(@resource, reset_password_token: @token) %>

+ +

Ignorišite ovu poruku, ako niste Vi bili ti koji ste zatražili promenu lozinke.

+

Lozinka se neće promeniti sve dok ne kliknete link iznad i ne napravite novu lozinku.

diff --git a/app/views/user_mailer/reset_password_instructions.sr-Latn.text.erb b/app/views/user_mailer/reset_password_instructions.sr-Latn.text.erb new file mode 100644 index 000000000..31707dee1 --- /dev/null +++ b/app/views/user_mailer/reset_password_instructions.sr-Latn.text.erb @@ -0,0 +1,8 @@ +Zdravo <%= @resource.email %>! + +Neko je zatražio link za promenu lozinke na instanci <%= @instance %>. Ovo možete uraditi preko linka ispod. + +<%= edit_password_url(@resource, reset_password_token: @token) %> + +Ignorišite ovu poruku, ako niste Vi bili ti koji ste zatražili promenu lozinke. +Lozinka se neće promeniti sve dok ne kliknete link iznad i ne napravite novu lozinku. diff --git a/app/views/user_mailer/reset_password_instructions.sr.html.erb b/app/views/user_mailer/reset_password_instructions.sr.html.erb new file mode 100644 index 000000000..be8d0c3ed --- /dev/null +++ b/app/views/user_mailer/reset_password_instructions.sr.html.erb @@ -0,0 +1,8 @@ +

Здраво <%= @resource.email %>!

+ +

Неко је затражио линк за промену лозинке на инстанци <%= @instance %>. Ово можете урадити кликом на линк испод.

+ +

<%= link_to 'Промени моју лозинку', edit_password_url(@resource, reset_password_token: @token) %>

+ +

Игноришите ову поруку, ако нисте Ви били ти који сте затражили промену лозинке.

+

Лозинка се неће променити све док не кликнете линк изнад и не направите нову лозинку.

diff --git a/app/views/user_mailer/reset_password_instructions.sr.text.erb b/app/views/user_mailer/reset_password_instructions.sr.text.erb new file mode 100644 index 000000000..86ea32b05 --- /dev/null +++ b/app/views/user_mailer/reset_password_instructions.sr.text.erb @@ -0,0 +1,8 @@ +Здраво <%= @resource.email %>! + +Неко је затражио линк за промену лозинке на инстанци <%= @instance %>. Ово можете урадити преко линка испод. + +<%= edit_password_url(@resource, reset_password_token: @token) %> + +Игноришите ову поруку, ако нисте Ви били ти који сте затражили промену лозинке. +Лозинка се неће променити све док не кликнете линк изнад и не направите нову лозинку. diff --git a/app/workers/pubsubhubbub/subscribe_worker.rb b/app/workers/pubsubhubbub/subscribe_worker.rb index e350973e1..2e176d1c1 100644 --- a/app/workers/pubsubhubbub/subscribe_worker.rb +++ b/app/workers/pubsubhubbub/subscribe_worker.rb @@ -20,7 +20,7 @@ class Pubsubhubbub::SubscribeWorker sidekiq_retries_exhausted do |msg, _e| account = Account.find(msg['args'].first) - logger.error "PuSH subscription attempts for #{account.acct} exhausted. Unsubscribing" + Sidekiq.logger.error "PuSH subscription attempts for #{account.acct} exhausted. Unsubscribing" ::UnsubscribeService.new.call(account) end diff --git a/config/application.rb b/config/application.rb index 1a53ab6e9..dc488ea8a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -55,6 +55,9 @@ module Mastodon :pt, :'pt-BR', :ru, + :sk, + :sr, + :'sr-Latn', :sv, :th, :tr, diff --git a/config/initializers/devise.rb b/config/initializers/devise.rb index 64c4e12ff..07912c28b 100644 --- a/config/initializers/devise.rb +++ b/config/initializers/devise.rb @@ -137,6 +137,9 @@ Devise.setup do |config| # Setup a pepper to generate the encrypted password. # config.pepper = '104d16705f794923e77c5e5167b52452d00646dc952a2d30b541c24086e647012c7b9625f253c51912e455981e503446772973d5f1638631196c819d7137fad4' + # Send a notification to the original email when the user's email is changed. + config.send_email_changed_notification = true + # Send a notification email when the user's password is changed config.send_password_change_notification = true @@ -160,7 +163,7 @@ Devise.setup do |config| # initial account confirmation) to be applied. Requires additional unconfirmed_email # db field (see migrations). Until confirmed, new email is stored in # unconfirmed_email column, and copied to email column on successful confirmation. - config.reconfirmable = false + config.reconfirmable = true # Defines which key will be used when confirming an account # config.confirmation_keys = [:email] diff --git a/config/initializers/ostatus.rb b/config/initializers/ostatus.rb index bb8591f74..5773b7290 100644 --- a/config/initializers/ostatus.rb +++ b/config/initializers/ostatus.rb @@ -3,11 +3,12 @@ port = ENV.fetch('PORT') { 3000 } host = ENV.fetch('LOCAL_DOMAIN') { "localhost:#{port}" } web_host = ENV.fetch('WEB_DOMAIN') { host } -https = ENV['LOCAL_HTTPS'] == 'true' alternate_domains = ENV.fetch('ALTERNATE_DOMAINS') { '' } Rails.application.configure do + https = Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true' + config.x.local_domain = host config.x.web_domain = web_host config.x.use_https = https diff --git a/config/initializers/session_store.rb b/config/initializers/session_store.rb index ef61543a8..3dc0edd6f 100644 --- a/config/initializers/session_store.rb +++ b/config/initializers/session_store.rb @@ -1,3 +1,3 @@ # Be sure to restart your server when you modify this file. -Rails.application.config.session_store :cookie_store, key: '_mastodon_session', secure: (ENV['LOCAL_HTTPS'] == 'true') +Rails.application.config.session_store :cookie_store, key: '_mastodon_session', secure: (Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true') diff --git a/config/locales/activerecord.sk.yml b/config/locales/activerecord.sk.yml new file mode 100644 index 000000000..9ae71fa9a --- /dev/null +++ b/config/locales/activerecord.sk.yml @@ -0,0 +1,13 @@ +--- +sk: + activerecord: + errors: + models: + account: + attributes: + username: + invalid: iba písmená, číslice a podčiarkovníky + status: + attributes: + reblog: + taken: status už existuje diff --git a/config/locales/activerecord.sr-Latn.yml b/config/locales/activerecord.sr-Latn.yml new file mode 100644 index 000000000..1527a0d8c --- /dev/null +++ b/config/locales/activerecord.sr-Latn.yml @@ -0,0 +1,13 @@ +--- +sr-Latn: + activerecord: + errors: + models: + account: + attributes: + username: + invalid: samo slova, brojevi i donje crte + status: + attributes: + reblog: + taken: statusa već postoji diff --git a/config/locales/activerecord.sr.yml b/config/locales/activerecord.sr.yml new file mode 100644 index 000000000..b4d929634 --- /dev/null +++ b/config/locales/activerecord.sr.yml @@ -0,0 +1,13 @@ +--- +sr: + activerecord: + errors: + models: + account: + attributes: + username: + invalid: само слова, бројеви и доње црте + status: + attributes: + reblog: + taken: статуса већ постоји diff --git a/config/locales/devise.en.yml b/config/locales/devise.en.yml index 586c5349d..c5ae583ff 100644 --- a/config/locales/devise.en.yml +++ b/config/locales/devise.en.yml @@ -18,8 +18,12 @@ en: mailer: confirmation_instructions: subject: 'Mastodon: Confirmation instructions for %{instance}' + email_changed: + subject: 'Mastodon: Email changed' password_change: subject: 'Mastodon: Password changed' + reconfirmation_instructions: + subject: 'Mastodon: Confirm email for %{instance}' reset_password_instructions: subject: 'Mastodon: Reset password instructions' unlock_instructions: diff --git a/config/locales/devise.ja.yml b/config/locales/devise.ja.yml index 1a46b80b5..118186877 100644 --- a/config/locales/devise.ja.yml +++ b/config/locales/devise.ja.yml @@ -18,8 +18,12 @@ ja: mailer: confirmation_instructions: subject: 'Mastodon: メールアドレスの確認' + email_changed: + subject: 'Mastodon: メールアドレスの変更' password_change: subject: 'Mastodon: パスワードが変更されました' + reconfirmation_instructions: + subject: 'Mastodon: %{instance}のメールを確認する' reset_password_instructions: subject: 'Mastodon: パスワード再発行' unlock_instructions: diff --git a/config/locales/devise.oc.yml b/config/locales/devise.oc.yml index 266f87373..de87ac1e2 100644 --- a/config/locales/devise.oc.yml +++ b/config/locales/devise.oc.yml @@ -18,8 +18,12 @@ oc: mailer: confirmation_instructions: subject: Mercés de confirmar vòstra inscripcion sus %{instance} + email_changed: + subject: 'Mastodon : corrièl cambiat' password_change: subject: Mastodon : senhal cambiat + reconfirmation_instructions: + subject: 'Mastodon : Confirmatz l’adreça per %{instance}' reset_password_instructions: subject: Mastodon : instruccions per reïnicializar lo senhal unlock_instructions: diff --git a/config/locales/devise.pl.yml b/config/locales/devise.pl.yml index 4b1eb2c60..6a2960463 100644 --- a/config/locales/devise.pl.yml +++ b/config/locales/devise.pl.yml @@ -18,8 +18,12 @@ pl: mailer: confirmation_instructions: subject: 'Mastodon: Instrukcje weryfikacji adresu e-mail' + email_changed: + subject: 'Mastodon: Zmieniono adres e-mail' password_change: - subject: 'Mastodon: Hasło zmienione' + subject: 'Mastodon: Zmieniono hasło' + reconfirmation_instructions: + subject: 'Mastodon: Potwierdź adres e-mail na &{instance}' reset_password_instructions: subject: 'Mastodon: Instrukcje ustawienia nowego hasła' unlock_instructions: diff --git a/config/locales/devise.sk.yml b/config/locales/devise.sk.yml new file mode 100644 index 000000000..a4b92886d --- /dev/null +++ b/config/locales/devise.sk.yml @@ -0,0 +1,61 @@ +--- +sk: + devise: + confirmations: + confirmed: Váš účet bol úspešne overený. + send_instructions: O niekoľko minút obdržíte email s inštrukciami ako potvrdiť váš účet. + send_paranoid_instructions: Ak sa váš email nachádza v našej databáze, obdržíte email s inštrukciami ako potvrdiť váš účet. + failure: + already_authenticated: Už ste prihlásený. + inactive: Váš účet ešte nebol aktivovaný. + invalid: Nesprávny %{authentication_keys} alebo heslo. + last_attempt: Máte posledný pokus pred zamknutím vašeho účtu. + locked: Váš účet je zamknutý. + not_found_in_database: Nesprávny %{authentication_keys} alebo heslo. + timeout: Vaša session vypršala. Na pokračovanie sa prosím znovu prihláste. + unauthenticated: Pred pokračovaním sa musíte zaregistrovať alebo prihlásiť. + unconfirmed: Pred pokračovaním musíte potvrdiť svoj email. + mailer: + confirmation_instructions: + subject: 'Mastodon: Potvrdzovacie inštrukcie pre %{instance}' + password_change: + subject: 'Mastodon: Heslo bolo zmenené' + reset_password_instructions: + subject: 'Mastodon: Inštrukcie pre obnovu hesla' + unlock_instructions: + subject: 'Mastodon: Inštrukcie pre odomknutie účtu' + omniauth_callbacks: + failure: Nebolo možné vás autentifikovať z %{kind} z dôvodu "%{reason}". + success: Úspešne autentifikovaný z účtu %{kind}. + passwords: + no_token: Túto stránku nemôžete navštíviť pokiaľ neprichádzate z emailu s inštrukciami na obnovu hesla. Pokiaľ prichádzate z tohto emailu, prosím uistite sa že ste použili celú URL z emailu. + send_instructions: Ak zadaný email existuje v našej databáze tak o niekoľko minút obdržíte email s inštrukciami ako nastaviť nové heslo. + send_paranoid_instructions: Ak zadaný email existuje v našej databáze, obdržíte odkaz na obnovu hesla na svoj email. + updated: Vaše heslo bolo úspešne zmenené. Teraz ste prihlásený. + updated_not_active: Vaše heslo bolo úspešne zmenené. + registrations: + destroyed: Dovidenia! Váš účet bol úspešne zrušený. Dúfame že sa opäť niekedy zastavíte. + signed_up: Vitajte! Vaša registrácia bola úspešná. + signed_up_but_inactive: Registrácia bola úspešná. Avšak, účet ešte nebol aktivovaný, takže vás nemôžeme prihlásiť. + signed_up_but_locked: Prihlasovanie úspešné. Avšak, účet je zablokovaný, takže vás nemôžeme prihlásiť. + signed_up_but_unconfirmed: Správa s odkazom potvrdzujúcim registráciu bola poslaná na váš email. Pre aktváciu účtu, kliknite na daný odkaz. + update_needs_confirmation: Účet bol úspešne zmenený ale ešte potrebujeme overiť vašu novú emailovú adresu. Pre overenie prosím kliknite na link v správe ktorú sme vám poslali na email. + updated: Váš účet bol úspešne aktualizovaný. + sessions: + already_signed_out: Odhlásenie bolo úspešné. + signed_in: Prihlásenie úspešné. + signed_out: Odhlásenie úspešné. + unlocks: + send_instructions: O niekoľko minút obdržíte email s inštrukciami ako nastaviť nové heslo. + send_paranoid_instructions: Ak váš účet existuje, o niekoľko minút obdržíte email s inštrukciami ako ho odomknúť. + unlocked: Váš účet bol úspešne odomknutý. Prosím prihláste sa. + errors: + messages: + already_confirmed: bol už potvrdený, skúste sa prihlásiť + confirmation_period_expired: musí byť potvrdený do %{period}, prosím požiadajte o nový + expired: expiroval, prosím, vyžiadajte si nový + not_found: nenájdený + not_locked: nebol uzamknutý + not_saved: + one: "%{resource} nebol uložený kôli chybe:" + other: "%{resource} nebol uložený kôli %{count} chybám:" diff --git a/config/locales/devise.sr-Latn.yml b/config/locales/devise.sr-Latn.yml new file mode 100644 index 000000000..21ddbd726 --- /dev/null +++ b/config/locales/devise.sr-Latn.yml @@ -0,0 +1,63 @@ +--- +sr-Latn: + devise: + confirmations: + confirmed: Adresa Vaše e-pošte je uspešno potvrđena. + send_instructions: U roku od nekoliko minuta primićete e-poštu sa uputstvom za potvrdu Vašeg naloga. Molimo proverite i spam fasciklu ako niste primili poruku. + send_paranoid_instructions: Ukoliko se adresa Vaše e-pošte nalazi u našoj bazi, u roku od nekoliko minuta primićete poruku sa uputstvom kako da potvrdite Vaš nalog. Molimo proverite i spam fasciklu ako niste primili poruku. + failure: + already_authenticated: Već ste prijavljeni. + inactive: Vaš nalog još nije aktiviran. + invalid: Neispravan %{authentication_keys} ili lozinka. + last_attempt: Imate još jedan pokušaj pre nego što Vaš nalog bude zaključan. + locked: Vaš nalog je zaključan. + not_found_in_database: Neispravan %{authentication_keys} ili lozinka. + timeout: Vreme trajanja Vaše sesije je isteklo. Za nastavak prijavite se ponovo. + unauthenticated: Za nastavak se morate prijaviti ili registrovati. + unconfirmed: Pre nastavka morate potvrditi svoj nalog. + mailer: + confirmation_instructions: + subject: 'Mastodont: Uputstvo za potvrdu korisničkog naloga na instanci %{instance}' + password_change: + subject: 'Mastodont: Lozinka promenjena' + reset_password_instructions: + subject: 'Mastodont: Uputstvo za resetovanje lozinke' + unlock_instructions: + subject: 'Mastodont: Uputstvo za otključavanje korisničkog naloga' + omniauth_callbacks: + failure: Nismo u mogućnosti autorizovati Vas sa %{kind} nalogom zbog "%{reason}". + success: Uspešna autorizacija sa %{kind} naloga. + passwords: + no_token: Ne možete pristupiti ovoj stranici ako niste pratili vezu u imejlu za resetovanje lozinke. Ukoliko ste pratili vezu za resetovanje lozinke u poruci, molimo Vas da proverite da li ste koristili punu adresu. + send_instructions: U roku od nekoliko minuta primitićete poruku sa uputstvom za promenu Vaše lozinke. Molimo proverite i spam fasciklu ako niste primili poruku. + send_paranoid_instructions: Ukoliko se adresa Vaše e-pošte nalazi u našoj bazi, u roku od nekoliko minuta primićete poruku sa uputstvom za promenu Vaše lozinke. Molimo proverite i spam fasciklu ako niste primili poruku. + updated: Vaša lozinka je uspešno promenjena. Sada ste prijavljeni. + updated_not_active: Vaša lozinka nije uspešno promenjena. + registrations: + destroyed: Ćao! Vaš nalog je uspešno obrisan. Nadamo se da ćete se uskoro vratiti. + signed_up: Dobrodošli! Uspešno ste se registrovali. + signed_up_but_inactive: Uspešno ste se registrovali. Nažalost ne možete se prijaviti zato što Vaš nalog još nije aktiviran. + signed_up_but_locked: Uspešno ste se registrovali. Nažalost ne možete se prijaviti zato što je Vaš nalog zaključan. + signed_up_but_unconfirmed: Poruka za potvrdu Vašeg naloga je poslata na Vašu imejl adresu. Kliknite na vezu u imejlu da potvrdite svoj nalog. Molimo proverite i spam fasciklu ako niste primili poruku. + update_needs_confirmation: Uspešno ste ažurirali svoj nalog, ali treba da potvrdimo novu adresu Vaše e-pošte. Molimo Vas da proverite e-poštu i pratite link za potvrdu nove adrese Vaše e-pošte. + updated: Vaš nalog je uspešno ažuriran. + sessions: + already_signed_out: Uspešno ste se odjavili. + signed_in: Uspešno ste se prijavili. + signed_out: Uspešno ste se odjavili. + unlocks: + send_instructions: U roku od nekoliko minuta primićete imejl sa uputstvom za otključavanje Vašeg naloga. Molimo proverite i spam fasciklu ako niste primili poruku. + send_paranoid_instructions: koliko se adresa Vaše e-pošte nalazi u našoj bazi, u roku od nekoliko minuta primićete poruku sa uputstvom kako da otključate Vaš nalog. Molimo proverite i spam fasciklu ako niste primili poruku. + unlocked: Vaš nalog je uspešno otključan. Molimo Vas da se prijavite da biste nastavili. + errors: + messages: + already_confirmed: je već potvrđen, molimo Vas da se prijavite + confirmation_period_expired: je trebao biti potvrđen tokom perioda %{period}, molimo Vas da zatražite novi + expired: je istekao, molimo Vas da zatražite novi + not_found: nije pronađeno + not_locked: nije zaključan + not_saved: + few: "%{count} greške sprečavaju %{resource}a:" + many: "%{count} grešaka sprečavaju %{resource}a:" + one: '1 greška sprečava %{resource}a:' + other: "%{count} grešaka sprečavaju %{resource}a:" diff --git a/config/locales/devise.sr.yml b/config/locales/devise.sr.yml new file mode 100644 index 000000000..9d1359695 --- /dev/null +++ b/config/locales/devise.sr.yml @@ -0,0 +1,63 @@ +--- +sr: + devise: + confirmations: + confirmed: Адреса Ваше е-поште је успешно потврђена. + send_instructions: У року од неколико минута примићете е-пошту са упутством за потврду Вашег налога. Молимо проверите и спам фасциклу ако нисте примили поруку. + send_paranoid_instructions: Уколико се адреса Ваше е-поште налази у нашој бази, у року од неколико минута примићете поруку са упутством како да потврдите Ваш налог. Молимо проверите и спам фасциклу ако нисте примили поруку. + failure: + already_authenticated: Већ сте пријављени. + inactive: Ваш налог још није активиран. + invalid: Неисправан %{authentication_keys} или лозинка. + last_attempt: Имате још један покушај пре него што Ваш налог буде закључан. + locked: Ваш налог је закључан. + not_found_in_database: Неисправан %{authentication_keys} или лозинка. + timeout: Време трајања Ваше сесије је истекло. За наставак пријавите се поново. + unauthenticated: За наставак се морате пријавити или регистровати. + unconfirmed: Пре наставка морате потврдити свој налог. + mailer: + confirmation_instructions: + subject: 'Мастодонт: Упутство за потврду корисничког налога на инстанци %{instance}' + password_change: + subject: 'Мастодонт: Лозинка промењена' + reset_password_instructions: + subject: 'Мастодонт: Упутство за ресетовање лозинке' + unlock_instructions: + subject: 'Мастодонт: Упутство за откључавање корисничког налога' + omniauth_callbacks: + failure: Нисмо у могућности ауторизовати Вас са %{kind} налогом због "%{reason}". + success: Успешна ауторизација са %{kind} налога. + passwords: + no_token: Не можете приступити овој страници ако нисте пратили везу у имејлу за ресетовање лозинке. Уколико сте пратили везу за ресетовање лозинке у поруци, молимо Вас да проверите да ли сте користили пуну адресу. + send_instructions: У року од неколико минута примитићете поруку са упутством за промену Ваше лозинке. Молимо проверите и спам фасциклу ако нисте примили поруку. + send_paranoid_instructions: Уколико се адреса Ваше е-поште налази у нашој бази, у року од неколико минута примићете поруку са упутством за промену Ваше лозинке. Молимо проверите и спам фасциклу ако нисте примили поруку. + updated: Ваша лозинка је успешно промењена. Сада сте пријављени. + updated_not_active: Ваша лозинка није успешно промењена. + registrations: + destroyed: Ћао! Ваш налог је успешно обрисан. Надамо се да ћете се ускоро вратити. + signed_up: Добродошли! Успешно сте се регистровали. + signed_up_but_inactive: Успешно сте се регистровали. Нажалост не можете се пријавити зато што Ваш налог још није активиран. + signed_up_but_locked: Успешно сте се регистровали. Нажалост не можете се пријавити зато што је Ваш налог закључан. + signed_up_but_unconfirmed: Порука за потврду Вашег налога је послата на Вашу имејл адресу. Кликните на везу у имејлу да потврдите свој налог. Молимо проверите и спам фасциклу ако нисте примили поруку. + update_needs_confirmation: Uспешно сте ажурирали свој налог, али треба да потврдимо нову адресу Ваше е-поште. Молимо Вас да проверите е-пошту и пратите линк за потврду нове адресе Ваше е-поште. + updated: Ваш налог је успешно ажуриран. + sessions: + already_signed_out: Успешно сте се одјавили. + signed_in: Успешно сте се пријавили. + signed_out: Успешно сте се одјавили. + unlocks: + send_instructions: У року од неколико минута примићете имејл са упутством за откључавање Вашег налога. Молимо проверите и спам фасциклу ако нисте примили поруку. + send_paranoid_instructions: колико се адреса Ваше е-поште налази у нашој бази, у року од неколико минута примићете поруку са упутством како да откључате Ваш налог. Молимо проверите и спам фасциклу ако нисте примили поруку. + unlocked: Ваш налог је успешно откључан. Молимо Вас да се пријавите да бисте наставили. + errors: + messages: + already_confirmed: је већ потврђен, молимо Вас да се пријавите + confirmation_period_expired: је требао бити потврђен током периода %{period}, молимо Вас да затражите нови + expired: је истекао, молимо Вас да затражите нови + not_found: није пронађено + not_locked: није закључан + not_saved: + few: "%{count} грешке спречавају %{resource}a:" + many: "%{count} грешака спречавају %{resource}a:" + one: '1 грешка спречава %{resource}а:' + other: "%{count} грешака спречавају %{resource}a:" diff --git a/config/locales/doorkeeper.sk.yml b/config/locales/doorkeeper.sk.yml new file mode 100644 index 000000000..f33f0a9e9 --- /dev/null +++ b/config/locales/doorkeeper.sk.yml @@ -0,0 +1,119 @@ +--- +sk: + activerecord: + attributes: + doorkeeper/application: + name: Názov aplikácie + redirect_uri: Presmerovacia URI + scopes: Pôsobnosť + website: Webstránka aplikácie + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: nesmie obsahovať fragment. + invalid_uri: musí byť platná URI. + relative_uri: musí byť absolútna URI. + secured_uri: musí byť HTTPS/SSL URI. + doorkeeper: + applications: + buttons: + authorize: Autorizovať + cancel: Zrušiť + destroy: Zničiť + edit: Upraviť + submit: Poslať + confirmations: + destroy: Ste si istý? + edit: + title: Upraviť aplikáciu + form: + error: No teda! Pozrite formulár pre prípadné chyby + help: + native_redirect_uri: Použite %{native_redirect_uri} pre lokálne testy + redirect_uri: Iba jedna URI na riadok + scopes: Rozsahy oddeľujte medzerami. Nechajte prázdne pre štandardné rozsahy. + index: + application: Aplikácia + callback_url: Návratová URL + delete: Zmazať + name: Názov + new: Nová aplikácia + scopes: Rozsahy + show: Ukázať + title: Vaše aplikácie + new: + title: Nová aplikácia + show: + actions: Akcie + application_id: Kľúč klienta + callback_urls: Návratové URL adresy + scopes: Rozsahy + secret: Tajné slovo klienta + title: 'Aplikácia: %{name}' + authorizations: + buttons: + authorize: Autorizovať + deny: Zamietnuť + error: + title: Nastala chyba + new: + able_to: Bude môcť + prompt: Aplikácia %{client_name} žiada prístup k vašemu účtu + title: Je potrebná autorizácia + show: + title: Skopírujte tento autorizačný kód a vložte ho do aplikácie. + authorized_applications: + buttons: + revoke: Zrušiť oprávnenie + confirmations: + revoke: Ste si istý? + index: + application: Aplikácia + created_at: Autorizované + date_format: "%Y-%m-%d %H:%M:%S" + scopes: Rozsahy + title: Vaše autorizované aplikácie + errors: + messages: + access_denied: Prístup zamietnutý. + credential_flow_not_configured: Resource Owner Password Credentials zlyhal lebo Doorkeeper.configure.resource_owner_from_credentials nebol nakonfigurovaný. + invalid_client: Overenie klienta zlyhalo. Neznámy klient, chýbajú údaje o klientovi alebo nepodporovaná metóda overovania. + invalid_grant: Poslané oprávnenie je neplatné, expirované, zrušené, nesúhlasí s presmerovacou URI použitou v autorizačnej požiadavke alebo bolo vydané niekomu inému. + invalid_redirect_uri: Presmerovacia URI je neplatná. + invalid_request: Požiadavke chýba povinný parameter alebo obsahuje nepodporovanú hodnotu niektorého parametra alebo je nejako inak poškodená. + invalid_resource_owner: Uvedené prihlasovacie údaje sú neplatné alebo nenájdené + invalid_scope: Požadovaný rozsah je neplatný, neznámy alebo poškodený. + invalid_token: + expired: Prístupový token expiroval + revoked: Prístupový token bol odňatý + unknown: Prístupový token je neplatný + resource_owner_authenticator_not_configured: Resource Owner zlyhal pretože Doorkeeper.configure.resource_owner_authenticator nebol nakonfigurovaný. + server_error: Nastala neočakávaná chyba na autorizačnom serveri ktorá zabránila vykonať požiadavku. + temporarily_unavailable: Autorizačný server Vás teraz nemôže obslúžiť pretože prebieha údržba alebo je dočasne preťažený. + unauthorized_client: Klient nie je autorizovaný vykonať túto požiadavku touto metódou. + unsupported_grant_type: Tento typ oprávnenia nie je podporovaný autorizačným serverom. + unsupported_response_type: Autorizačný server nepodporuje typ tejto odpovede. + flash: + applications: + create: + notice: Aplikácia vytvorená. + destroy: + notice: Aplikácia zmazaná. + update: + notice: Aplikácia aktualizovaná. + authorized_applications: + destroy: + notice: Oprávnenia aplikácie zrušené. + layouts: + admin: + nav: + applications: Aplikácie + oauth2_provider: Poskytovateľ OAuth2 + application: + title: Požadovaná OAuth autorizácia + scopes: + follow: sledovať, blokovať, povoliť a zušiť sledovanie účtov + read: prezrieť váš účet + write: poslať vo vašom mene diff --git a/config/locales/doorkeeper.sr-Latn.yml b/config/locales/doorkeeper.sr-Latn.yml new file mode 100644 index 000000000..8e96f8b93 --- /dev/null +++ b/config/locales/doorkeeper.sr-Latn.yml @@ -0,0 +1,119 @@ +--- +sr-Latn: + activerecord: + attributes: + doorkeeper/application: + name: Ime aplikacije + redirect_uri: Adresa za preusmeravanje + scopes: Opseg važenja + website: Veb sajt aplikacije + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: ne može da sadrži fragment. + invalid_uri: mora biti ispravan URI. + relative_uri: mora biti apsolutni URI. + secured_uri: mora biti HTTPS/SSL URI. + doorkeeper: + applications: + buttons: + authorize: Autorizuj + cancel: Poništi + destroy: Uništi + edit: Izmeni + submit: Pošalji + confirmations: + destroy: Da li ste sigurni? + edit: + title: Izmeni aplikaciju + form: + error: Ops! Proverite formular za eventualne greške + help: + native_redirect_uri: Koristite %{native_redirect_uri} za lokalno testiranje + redirect_uri: Koristite jednu liniju po URI-ju + scopes: Odvojite opsege važenja sa belinama. Ostavite prazno za podrazumevane opsege važenja. + index: + application: Aplikacija + callback_url: Adresa za povratni poziv + delete: Obriši + name: Ime + new: Nova aplikacija + scopes: Opsezi važenja + show: Prikaži + title: Vaše aplikacije + new: + title: Nova aplikacija + show: + actions: Akcije + application_id: Klijentski ključ + callback_urls: Adrese za povratne pozive + scopes: Opseg važenja + secret: Klijentska tajna + title: 'Aplikacija: %{name}' + authorizations: + buttons: + authorize: Autorizuj se + deny: Odbij + error: + title: Dogodila se greška + new: + able_to: Biće u mogućnosti da + prompt: Aplikacija %{client_name} zahteva pristup Vašem nalogu + title: Potrebna autorizacija + show: + title: Kopirajte ovaj autorizacioni kod i nalepite ga u aplikaciju. + authorized_applications: + buttons: + revoke: Opozovi + confirmations: + revoke: Da li ste sigurni? + index: + application: Aplikacija + created_at: Autorizovana + date_format: "%d.%m.%Y %H:%M:%S" + scopes: Opsezi važenja + title: Vaše autorizovane aplikacije + errors: + messages: + access_denied: Vlasnik resursa ili autorizacioni server su odbili zahtev. + credential_flow_not_configured: Tok Resource Owner Password Credentials nije uspeo pošto je Doorkeeper.configure.resource_owner_from_credentials neiskonfigurisan. + invalid_client: Klijentska identifikacija nije uspela zbog nepoznatog klijenta, zato što klijent nije uključio identifikaciju ili zato što je iskorišćen nepodržani identifikacioni metod. + invalid_grant: Zadata identifikaciona dozvola je neispravna, istekla, opozvana, ne poklapa se sa adresom preusmeravanja ili je izdata nekog drugom klijentu. + invalid_redirect_uri: Uključena adresa preusmeravanja nije ispravna. + invalid_request: Obavezni parametar fali u zahtevu, zahtev uključuje nepodržanu vrednost parametra ili je parametar na neki drugi način pogrešan. + invalid_resource_owner: Zadati kredencijali vlasnika resursa nisu ispravni ili vlasnik resursa ne može biti nađen + invalid_scope: Zahtevani opseg važenja nije ispravan, nepoznat je ili je na neki drugi način pogrešan. + invalid_token: + expired: Pristupni token je istekao + revoked: Pristupni token je opozvan + unknown: Pristupni token nije ispravan + resource_owner_authenticator_not_configured: Greška u pronalaženju vlasnika resursa pošto Doorkeeper.configure.resource_owner_authenticator nije konfigurisan. + server_error: Identifikacioni server je naišao na neočekivanu situaciju zbog koje nije ispunio upućeni zahtev. + temporarily_unavailable: Identifikacioni server trenutno ne može da obradi zahtev jer je privremeno preopterećen ili je u režimu održavanja. + unauthorized_client: Klijent nije ovlašćen da izvrši ovaj zahtev ovim metodom. + unsupported_grant_type: Tip autorizacione dozvole nije podržan od strane autorizacionog servera. + unsupported_response_type: Autorizacioni server ne podržava ovaj tip odgovora. + flash: + applications: + create: + notice: Aplikacija napravljena. + destroy: + notice: Aplikacija obrisana. + update: + notice: Aplikacija ažurirana. + authorized_applications: + destroy: + notice: Aplikacija opozvana. + layouts: + admin: + nav: + applications: Aplikacije + oauth2_provider: OAuth2 provajder + application: + title: OAuth autorizacija potrebna + scopes: + follow: prati, blokira, odblokira i otprati naloge + read: čita podatke Vašeg naloga + write: objavljuje statuse u Vaše ime diff --git a/config/locales/doorkeeper.sr.yml b/config/locales/doorkeeper.sr.yml new file mode 100644 index 000000000..723c02d09 --- /dev/null +++ b/config/locales/doorkeeper.sr.yml @@ -0,0 +1,119 @@ +--- +sr: + activerecord: + attributes: + doorkeeper/application: + name: Име апликације + redirect_uri: Адреса за преусмеравање + scopes: Опсег важења + website: Веб сајт апликације + errors: + models: + doorkeeper/application: + attributes: + redirect_uri: + fragment_present: не може да садржи фрагмент. + invalid_uri: мора бити исправан URI. + relative_uri: мора бити апсолутни URI. + secured_uri: мора бити HTTPS/SSL URI. + doorkeeper: + applications: + buttons: + authorize: Ауторизуј + cancel: Поништи + destroy: Уништи + edit: Измени + submit: Пошаљи + confirmations: + destroy: Да ли сте сигурни? + edit: + title: Измени апликацију + form: + error: Опс! Проверите формулар за евентуалне грешке + help: + native_redirect_uri: Користите %{native_redirect_uri} за локално тестирање + redirect_uri: Користите једну линију по URI-ју + scopes: Одвојите опсеге важења са белинама. Оставите празно за подразумеване опсеге важења. + index: + application: Апликација + callback_url: Адреса за повратни позив + delete: Обриши + name: Име + new: Нова апликација + scopes: Опсези важења + show: Прикажи + title: Ваше апликације + new: + title: Нова апликација + show: + actions: Акције + application_id: Клијентски кључ + callback_urls: Адресе за повратне позиве + scopes: Опсег важења + secret: Клијентска тајна + title: 'Апликација: %{name}' + authorizations: + buttons: + authorize: Ауторизуј се + deny: Одбиј + error: + title: Догодила се грешка + new: + able_to: Биће у могућности да + prompt: Апликација %{client_name} захтева приступ Вашем налогу + title: Потребна ауторизација + show: + title: Копирајте овај ауторизациони код и налепите га у апликацију. + authorized_applications: + buttons: + revoke: Опозови + confirmations: + revoke: Да ли сте сигурни? + index: + application: Апликација + created_at: Ауторизована + date_format: "%d.%m.%Y %H:%M:%S" + scopes: Опсези важења + title: Ваше ауторизоване апликације + errors: + messages: + access_denied: Власник ресурса или ауторизациони сервер су одбили захтев. + credential_flow_not_configured: Ток Resource Owner Password Credentials није успео пошто је Doorkeeper.configure.resource_owner_from_credentials неисконфигурисан. + invalid_client: Клијентска идентификација није успела због непознатог клијента, зато што клијент није укључио идентификацију или зато што је искоришћен неподржани идентификациони метод. + invalid_grant: Задата идентификациона дозвола је неисправна, истекла, опозвана, не поклапа се са адресом преусмеравања или је издата неког другом клијенту. + invalid_redirect_uri: Укључена адреса преусмеравања није исправна. + invalid_request: Обавезни параметар фали у захтеву, захтев укључује неподржану вредност параметра или је параметар на неки други начин погрешан. + invalid_resource_owner: Задати креденцијали власника ресурса нису исправни или власник ресурса не може бити нађен + invalid_scope: Захтевани опсег важења није исправан, непознат је или је на неки други начин погрешан. + invalid_token: + expired: Приступни токен је истекао + revoked: Приступни токен је опозван + unknown: Приступни токен није исправан + resource_owner_authenticator_not_configured: Грешка у проналажењу власника ресурса пошто Doorkeeper.configure.resource_owner_authenticator није конфигурисан. + server_error: Идентификациони сервер је наишао на неочекивану ситуацију због које није испунио упућени захтев. + temporarily_unavailable: Идентификациони сервер тренутно не може да обради захтев јер је привремено преоптерећен или је у режиму одржавања. + unauthorized_client: Клијент није овлашћен да изврши овај захтев овим методом. + unsupported_grant_type: Тип ауторизационе дозволе није подржан од стране ауторизационог сервера. + unsupported_response_type: Ауторизациони сервер не подржава овај тип одговора. + flash: + applications: + create: + notice: Апликација направљена. + destroy: + notice: Апликација обрисана. + update: + notice: Апликација ажурирана. + authorized_applications: + destroy: + notice: Апликација опозвана. + layouts: + admin: + nav: + applications: Апликације + oauth2_provider: OAuth2 провајдер + application: + title: OAuth ауторизација потребна + scopes: + follow: прати, блокира, одблокира и отпрати налоге + read: чита податке Вашег налога + write: објављује статусе у Ваше име diff --git a/config/locales/en.yml b/config/locales/en.yml index 92eca1ddd..f18437241 100644 --- a/config/locales/en.yml +++ b/config/locales/en.yml @@ -265,12 +265,18 @@ en: unresolved: Unresolved view: View settings: + activity_api_enabled: + desc_html: Counts of locally posted statuses, active users, and new registrations in weekly buckets + title: Publish aggregate statistics about user activity bootstrap_timeline_accounts: desc_html: Separate multiple usernames by comma. Only local and unlocked accounts will work. Default when empty is all local admins. title: Default follows for new users contact_information: email: Business e-mail username: Contact username + peers_api_enabled: + desc_html: Domain names this instance has encountered in the fediverse + title: Publish list of discovered instances registrations: closed_message: desc_html: Displayed on frontpage when registrations are closed. You can use HTML tags diff --git a/config/locales/ja.yml b/config/locales/ja.yml index debf68d1d..f3f23b8f6 100644 --- a/config/locales/ja.yml +++ b/config/locales/ja.yml @@ -116,6 +116,7 @@ ja: roles: admin: 管理者 moderator: モデレーター + staff: スタッフ user: ユーザー salmon_url: Salmon URL search: 検索 @@ -160,6 +161,7 @@ ja: update_status: "%{name} さんが %{target} さんの投稿を更新しました" title: 操作履歴 custom_emojis: + by_domain: ドメイン copied_msg: 絵文字のコピーをローカルに作成しました copy: コピー copy_failed_msg: 絵文字のコピーをローカルに作成できませんでした @@ -263,12 +265,18 @@ ja: unresolved: 未解決 view: 表示 settings: + activity_api_enabled: + desc_html: ローカルに投稿されたトゥート数、アクティブなユーザー数、週ごとの新規登録者数 + title: ユーザーアクティビティに関する統計を公開する bootstrap_timeline_accounts: desc_html: 複数のユーザー名はコンマで区切ります。ローカルの公開アカウントのみ有効です。指定しない場合は管理者がデフォルトで指定されます。 title: 新規ユーザーが自動フォローするアカウント contact_information: email: ビジネスメールアドレス username: 連絡先のユーザー名 + peers_api_enabled: + desc_html: 連合内でこのインスタンスが遭遇したドメインの名前 + title: 接続しているインスタンスのリストを公開する registrations: closed_message: desc_html: 新規登録を停止しているときにフロントページに表示されます。HTMLタグが使えます diff --git a/config/locales/ko.yml b/config/locales/ko.yml index 333bcc689..9e3505a72 100644 --- a/config/locales/ko.yml +++ b/config/locales/ko.yml @@ -64,6 +64,7 @@ ko: by_domain: 도메인 confirm: 확인 confirmed: 확인됨 + demote: 모더레이터 강등 disable: 비활성화 disable_two_factor_authentication: 2단계 인증을 비활성화 disabled: 비활성화된 @@ -86,6 +87,7 @@ ko: title: 위치 login_status: 로그인 상태 media_attachments: 첨부된 미디어 + memorialize: 메모리엄으로 전환 moderation: all: 전체 silenced: 침묵 중 @@ -102,6 +104,7 @@ ko: outbox_url: Outbox URL perform_full_suspension: 완전히 정지시키기 profile_url: 프로필 URL + promote: 모더레이터로 승급 protocol: Protocol public: 전체 공개 push_subscription_expires: PuSH 구독 기간 만료 @@ -112,6 +115,8 @@ ko: role: 권한 roles: admin: 관리자 + moderator: 모더레이터 + staff: 스태프 user: 사용자 salmon_url: Salmon URL search: 검색 @@ -129,7 +134,34 @@ ko: unsubscribe: 구독 해제 username: 아이디 web: Web + action_logs: + actions: + confirm_user: "%{name}이 %{target}의 이메일 주소를 컨펌했습니다" + create_custom_emoji: "%{name}이 새로운 에모지 %{target}를 추가했습니다" + create_domain_block: "%{name}이 도메인 %{target}를 차단했습니다" + create_email_domain_block: "%{name}이 이메일 도메인 %{target}를 차단했습니다" + demote_user: "%{name}이 %{target}을 강등했습니다" + destroy_domain_block: "%{name}이 도메인 %{target}의 차단을 해제했습니다" + destroy_email_domain_block: "%{name}이 이메일 도메인 %{target}을 화이트리스트에 넣었습니다" + destroy_status: "%{name}이 %{target}의 툿을 삭제했습니다" + disable_2fa_user: "%{name}이 %{target}의 2FA를 비활성화 했습니다" + disable_custom_emoji: "%{name}이 에모지 %{target}를 비활성화 했습니다" + disable_user: "%{name}이 %{target}의 로그인을 비활성화 했습니다" + enable_custom_emoji: "%{name}이 에모지 %{target}를 활성화 했습니다" + enable_user: "%{name}이 %{target}의 로그인을 활성화 했습니다" + memorialize_account: "%{name}이 %{target}의 계정을 메모리엄으로 전환했습니다" + promote_user: "%{name}이 %{target}를 승급시켰습니다" + reset_password_user: "%{name}이 %{target}의 암호를 초기화했습니다" + resolve_report: "%{name}이 %{target} 신고를 처리됨으로 변경하였습니다" + silence_account: "%{name}이 %{target}의 계정을 뮤트시켰습니다" + suspend_account: "%{name}이 %{target}의 계정을 정지시켰습니다 " + unsilence_account: "%{name}이 %{target}에 대한 뮤트를 해제했습니다" + unsuspend_account: "%{name}이 %{target}에 대한 정지를 해제했습니다" + update_custom_emoji: "%{name}이 에모지 %{target}를 업데이트 했습니다" + update_status: "%{name}이 %{target}의 상태를 업데이트 했습니다" + title: 감사 기록 custom_emojis: + by_domain: 도메인 copied_msg: 성공적으로 에모지의 로컬 복사본을 생성했습니다 copy: 복사 copy_failed_msg: 에모지의 로컬 복사본을 만드는 데 실패하였습니다 @@ -142,11 +174,16 @@ ko: enable: 활성화 enabled_msg: 성공적으로 활성화하였습니다 image_hint: 50KB 이하의 PNG + listed: 목록에 실림 new: title: 새 커스텀 에모지 추가 + overwrite: 덮어쓰기 shortcode: 짧은 코드 shortcode_hint: 최소 2글자, 영문자, 숫자, _만 사용 가능 title: 커스텀 에모지 + unlisted: 목록에 없음 + update_failed_msg: 에모지를 업데이트 할 수 없습니다 + updated_msg: 에모지가 성공적으로 업데이트 되었습니다! upload: 업로드 domain_blocks: add_new: 추가하기 @@ -196,6 +233,13 @@ ko: reset: 리셋 search: 검색 title: 알려진 인스턴스들 + invites: + filter: + all: 모두 + available: 사용가능 + expired: 만료됨 + title: 필터 + title: 초대 reports: action_taken_by: 신고 처리자 are_you_sure: 정말로 실행하시겠습니까? @@ -221,12 +265,18 @@ ko: unresolved: 미해결 view: 표시 settings: + activity_api_enabled: + desc_html: 주별 로컬에 게시 된 글, 활성 사용자 및 새로운 가입자 수 + title: 유저 활동에 대한 통계 발행 bootstrap_timeline_accounts: desc_html: 콤마로 여러 유저명을 구분. 로컬의 잠기지 않은 계정만 가능합니다. 비워 둘 경우 모든 로컬 관리자가 기본으로 사용 됩니다. title: 새 유저가 팔로우 할 계정들 contact_information: email: 공개할 메일 주소를 입력 username: 아이디를 입력 + peers_api_enabled: + desc_html: 이 인스턴스가 페디버스에서 만났던 도메인 네임들 + title: 발견 된 인스턴스들의 리스트 발행 registrations: closed_message: desc_html: 신규 등록을 받지 않을 때 프론트 페이지에 표시됩니다.
HTML 태그를 사용할 수 있습니다. @@ -234,9 +284,15 @@ ko: deletion: desc_html: 유저가 자신의 계정을 삭제할 수 있도록 설정합니다. title: 계정 삭제를 허가함 + min_invite_role: + disabled: 아무도 못 하게 + title: 초대링크를 만들 수 있는 권한 open: desc_html: 유저가 자신의 계정을 생성할 수 있도록 설정합니다. title: 신규 계정 등록을 받음 + show_staff_badge: + desc_html: 유저 페이지에 스태프 배지를 표시합니다. + title: 스태프 배지 표시 site_description: desc_html: 탑 페이지와 meta 태그에 사용됩니다.
HTML 태그, 예를 들어<a> 태그와 <em> 태그를 사용할 수 있습니다. title: 사이트 설명 @@ -303,6 +359,8 @@ ko: invalid_reset_password_token: 비밀번호 리셋 토큰이 올바르지 못하거나 기간이 만료되었습니다. 다시 요청해주세요. login: 로그인 logout: 로그아웃 + migrate_account: 계정 옮기기 + migrate_account_html: 이 계정을 다른 계정으로 리디렉션 하길 원하는 경우 여기에서 설정할 수 있습니다. register: 등록하기 resend_confirmation: 확인 메일을 다시 보내기 reset_password: 비밀번호 재설정 @@ -384,12 +442,43 @@ ko: following: 팔로우 중인 계정 목록 muting: 뮤트 중인 계정 목록 upload: 업로드 + in_memoriam_html: 메모리엄에 있음. + invites: + delete: 비활성화 + expired: 만료됨 + expires_in: + '1800': 30 분 + '21600': 6 시간 + '3600': 1 시간 + '43200': 12 시간 + '86400': 하루 + expires_in_prompt: 영원히 + generate: 생성 + max_uses: + one: 일회용 + other: "%{count} 회" + max_uses_prompt: 제한 없음 + prompt: 이 인스턴스에 대한 초대 링크를 만들고 공유합니다 + table: + expires_at: 만료 + uses: 사용됨 + title: 초대 landing_strip_html: "%{name} 님은 %{link_to_root_path} 인스턴스의 사용자입니다. 계정을 가지고 있다면 팔로우 하거나 대화할 수 있습니다." landing_strip_signup_html: 아직 계정이 없다면 여기서 등록할 수 있습니다. + lists: + errors: + limit: 리스트 최대치에 도달했습니다 media_attachments: validations: images_and_video: 이미 사진이 첨부되어 있으므로 동영상을 첨부할 수 없습니다. too_many: 최대 4개까지 첨부할 수 있습니다. + migrations: + acct: 새 계정의 username@domain + currently_redirecting: '당신의 프로파일은 여기로 리디렉션 됩니다:' + proceed: 저장 + updated_msg: 계정 이동 설정이 저장되었습니다! + moderation: + title: 모더레이션 notification_mailer: digest: body: "%{instance} 에서 마지막 로그인 뒤로 일어난 일:" @@ -502,6 +591,7 @@ ko: export: 데이터 내보내기 followers: 신뢰 중인 인스턴스 import: 데이터 가져오기 + migrate: 계정 이동 notifications: 알림 preferences: 사용자 설정 settings: 설정 @@ -516,6 +606,7 @@ ko: private: 비공개 툿은 고정될 수 없습니다. reblog: 부스트는 고정될 수 없습니다. show_more: 더 보기 + title: '%{name}: "%{quote}"' visibilities: private: 비공개 private_long: 팔로워에게만 공개됩니다 @@ -530,6 +621,8 @@ ko: sensitive_content: 민감한 컨텐츠 terms: title: "%{instance} 이용약관과 개인정보 취급 방침" + themes: + default: Mastodon time: formats: default: "%Y년 %m월 %d일 %H:%M" diff --git a/config/locales/oc.yml b/config/locales/oc.yml index 97d20bdf8..40387de70 100644 --- a/config/locales/oc.yml +++ b/config/locales/oc.yml @@ -265,12 +265,18 @@ oc: unresolved: Pas resolguts view: Veire settings: + activity_api_enabled: + desc_html: Nombre d’estatuts publicats, utilizaires actius e novèlas inscripcions en rapòrt setmanièr + title: Publica las estatisticas totalas de l’activitat dels utilizaires bootstrap_timeline_accounts: desc_html: Separatz los noms d’utilizaire amb de virgula. Pas que los comptes locals e pas clavats foncionaràn. Se lo camp es void los admins seràn selecionats. - title: Per defaut los nouvenguts sègon + title: Per defaut los nòuvenguts sègon contact_information: email: Picatz una adreça de corrièl username: Picatz un nom d’utilizaire + peers_api_enabled: + desc_html: Noms de domeni qu’aquesta instància a trobats pel fediverse + title: Publica la lista de las instàncias conegudas registrations: closed_message: desc_html: Afichat sus las pagina d’acuèlh quand las inscripcions son tampadas.
Podètz utilizar de balisas HTML diff --git a/config/locales/pl.yml b/config/locales/pl.yml index 2206e551a..94bd0a592 100644 --- a/config/locales/pl.yml +++ b/config/locales/pl.yml @@ -160,6 +160,7 @@ pl: update_status: "%{name} zaktualizował wpis użytkownika %{target}" title: Dziennik działań administracyjnych custom_emojis: + by_domain: Według domeny copied_msg: Pomyślnie utworzono lokalną kopię emoji copy: Kopiuj copy_failed_msg: Nie udało się utworzyć lokalnej kopii emoji @@ -264,12 +265,18 @@ pl: unresolved: Nierozwiązane view: Wyświetl settings: + activity_api_enabled: + desc_html: Liczy publikowane lokalnie wpisy, aktywnych użytkowników i nowe rejestracje w ciągu danego tygodnia + title: Publikuj zbiorowe statystyki o aktywności użytkowników bootstrap_timeline_accounts: desc_html: Oddzielaj nazwy użytkowników przecinkami. Działa tylko dla niezablokowanych kont w obrębie instancji. Jeżeli puste, zostaną użyte konta administratorów instancji. title: Domyślnie obserwowani użytkownicy contact_information: email: Służbowy adres e-mail username: Nazwa użytkownika do kontaktu + peers_api_enabled: + desc_html: Nazwy domen, z którymi ta instancja wchodziła w interakcje + title: Publikuj listę znanych instancji registrations: closed_message: desc_html: Wyświetlana na stronie głównej, gdy możliwość otwarej rejestracji nie jest dostępna. Możesz korzystać z tagów HTML @@ -472,7 +479,7 @@ pl: acct: nazwa@domena nowego konta currently_redirecting: 'Obecnie Twoje konto przekierowuje do:' proceed: Zapisz - updated_msg: Pomyślnie zaktualizowano ustawienia i migracji Twojego konta! + updated_msg: Pomyślnie zaktualizowano ustawienia migracji Twojego konta! moderation: title: Moderacja notification_mailer: @@ -606,6 +613,7 @@ pl: private: Nie możesz przypiąć niepublicznego wpisu reblog: Nie możesz przypiąć podbicia wpisu show_more: Pokaż więcej + title: '%{name}: "%{quote}"' visibilities: private: Tylko dla śledzących private_long: Widoczne tylko dla osób, które Cię śledzą diff --git a/config/locales/simple_form.ko.yml b/config/locales/simple_form.ko.yml index e2a678121..48306f418 100644 --- a/config/locales/simple_form.ko.yml +++ b/config/locales/simple_form.ko.yml @@ -4,6 +4,7 @@ ko: hints: defaults: avatar: PNG, GIF 혹은 JPG. 최대 2MB. 120x120px로 다운스케일 됨 + digest: 오랫동안 활동하지 않았을 때 받은 멘션들에 대한 요약 받기. display_name: one: 1 글자 남음 other: %{count} 글자 남음 @@ -29,9 +30,12 @@ ko: data: 데이터 display_name: 표시되는 이름 email: 이메일 주소 + expires_in: 만료시각 + filtered_languages: 숨긴 언어들 header: 헤더 locale: 언어 locked: 계정 잠금 + max_uses: 사용 횟수 제한 new_password: 새로운 비밀번호 입력 note: 자기소개 otp_attempt: 2단계 인증 코드 @@ -42,6 +46,7 @@ ko: setting_default_sensitive: 미디어를 언제나 민감한 컨텐츠로 설정 setting_delete_modal: 툿 삭제 전 확인 창을 표시 setting_noindex: 검색엔진의 인덱싱을 거절 + setting_reduce_motion: 애니메이션 줄이기 setting_system_font_ui: 시스템의 초기 설정 폰트를 사용 setting_theme: 사이트 테마 setting_unfollow_modal: 언팔로우 전 언팔로우 확인 표시 @@ -51,6 +56,7 @@ ko: interactions: must_be_follower: 나를 팔로우 하지 않는 사람에게서 온 알림을 차단 must_be_following: 내가 팔로우 하지 않는 사람에게서 온 알림을 차단 + must_be_following_dm: 내가 팔로우 하지 않은 사람에게서 오는 다이렉트메시지를 차단 notification_emails: digest: 요약 이메일 보내기 favourite: 누군가 내 상태를 즐겨찾기로 등록했을 때 이메일 보내기 diff --git a/config/locales/simple_form.sk.yml b/config/locales/simple_form.sk.yml new file mode 100644 index 000000000..25265c73a --- /dev/null +++ b/config/locales/simple_form.sk.yml @@ -0,0 +1,67 @@ +--- +sk: + simple_form: + hints: + defaults: + avatar: PNG, GIF alebo JPG. Maximálne 2MB. Bude zmenšený na 120x120px + digest: Sumár príspevkov ktoré ste dostali v dobe svojej neprítomnosti ak ste sa dlhší čas neprihlásili + display_name: + one: Ostáva vám 1 znak + other: Ostáva vám %{count} znakov + header: PNG, GIF alebo JPG. Maximálne 2MB. Bude zmenšený na 700x335px + locked: Budete musieť manuálne schváliť sledujúcich + note: + one: Ostáva vám 1 znak + other: Ostáva vám %{count}znakov + setting_noindex: Vaše príspevky a profil sa nebude dať nájsť vyhľadávaním + setting_theme: Ovplyvní ako bude Mastodon vyzerať pri prihlásení z hociktorého zariadenia. + imports: + data: CSV súbor vyexportovaný z inej Mastodon inštancie + sessions: + otp: Vložte 2FA kód z telefónu alebo použite jeden z vašich obnovovacích kódov. + user: + filtered_languages: Zaškrtnuté jazyky vám nebudú zobrazené vo verejnej časovej osi + labels: + defaults: + avatar: Avatar + confirm_new_password: Opäť vaše nové heslo pre potvrdenie + confirm_password: Potvrďte heslo + current_password: Súčasné heslo + data: Dáta + display_name: Meno + email: Emailová adresa + filtered_languages: Filtrované jazyky + header: Obrázok v hlavičke + locale: Jazyk + locked: Zamknúť účet + new_password: Nové heslo + note: O vás + otp_attempt: Dvoj-faktorový (2FA) kód + password: Heslo + setting_auto_play_gif: Automaticky prehrávať animované GIFy + setting_boost_modal: Zobrazovať potvrdzovacie okno pred re-toot + setting_default_privacy: Nastavenie súkromia príspevkov + setting_default_sensitive: Označiť každý obrázok/video/súbor ako chúlostivý + setting_delete_modal: Zobrazovať potvrdzovacie okno pred zmazaním toot-u + setting_noindex: Nezaradzovať vaše príspevky do indexácie pre vyhľadávanie + setting_system_font_ui: Použiť štandardný systémový font + setting_theme: Vzhľad + setting_unfollow_modal: Zobrazovať potvrdzovacie okno pred skončením sledovania iného používateľa + severity: Závažnosť + type: Typ importu + username: Používateľské meno + interactions: + must_be_follower: Blokovať notifikácie pod používateľov, ktorí vás nesledujú + must_be_following: Blokovať notifikácie od ľudí ktorý vás nesledujú + notification_emails: + digest: Posielať súhrnné emaily + favourite: Poslať email ak niekto označí váš príspevok ako obľúbený + follow: Poslať email ak vás niekto začne sledovať + follow_request: Poslať email ak vám niekto pošle žiadosť o sledovanie + mention: Poslať email ak vás niekto spomenie v svojom príspevku + reblog: Poslať email ak niekto re-tootne váš príspevok + 'no': Nie + required: + mark: "*" + text: povinné + 'yes': Áno diff --git a/config/locales/simple_form.sr-Latn.yml b/config/locales/simple_form.sr-Latn.yml new file mode 100644 index 000000000..d811377c3 --- /dev/null +++ b/config/locales/simple_form.sr-Latn.yml @@ -0,0 +1,75 @@ +--- +sr-Latn: + simple_form: + hints: + defaults: + avatar: PNG, GIF ili JPG. Najviše 2MB. Biće smanjena na 120x120px + digest: Poslato posle dužeg perioda neaktivnosti sa pregledom svih bitnih stvari koje ste dobili dok ste bili odsutni + display_name: + few: %{count} karaktera preostala + many: %{count} karaktera preostalo + one: 1 karakter preostao + other: %{count} karaktera preostalo + header: PNG, GIF ili JPG. Najviše 2MB. Biće smanjena na 700x335px + locked: Zahteva da pojedinačno odobrite pratioce + note: + few: %{count} karaktera preostal + many: %{count} karaktera preostalo + one: 1 karakter preostao + other: %{count} karaktera preostalo + setting_noindex: Utiče na Vaš javni profil i statusne strane + setting_theme: Utiče kako će Mastodont izgledati kada ste prijavljeni sa bilo kog uređaja. + imports: + data: CSV fajl izvezen sa druge Mastodont instance + sessions: + otp: Unesite dvofaktorski kod sa Vašeg telefona ili koristite jedan od kodova za oporavak. + user: + filtered_languages: Označeni jezici će za Vas biti isfiltrirani sa javnih lajni + labels: + defaults: + avatar: Avatar + confirm_new_password: Potvrdite novu lozinku + confirm_password: Potvrdite lozinku + current_password: Trenutna lozinka + data: Podaci + display_name: Ime za prikaz + email: Adresa e-pošte + expires_in: Ističe nakon + filtered_languages: Filtrirani jezici + header: Zaglavlje + locale: Jezik + locked: Zaključaj nalog + max_uses: Maksimalni broj korišćenja + new_password: Nova lozinka + note: Biografija + otp_attempt: Dvofaktorski kod + password: Lozinka + setting_auto_play_gif: Automatski puštaj animirane GIF-ove + setting_boost_modal: Prikaži dijalog za potvrdu pre davanja podrške + setting_default_privacy: Privatnost objava + setting_default_sensitive: Uvek označi multimediju kao osetljivu + setting_delete_modal: Prikaži dijalog za potvrdu pre brisanja tuta + setting_noindex: Odjavi se od indeksiranja search engine-a + setting_reduce_motion: Smanji pokrete u animacijama + setting_system_font_ui: Koristi sistemski font + setting_theme: Tema sajta + setting_unfollow_modal: Prikaži dijalog za potvrdu pre nego što otpratite nekoga + severity: Oštrina + type: Tip uvoza + username: Korisničko ime + interactions: + must_be_follower: Blokiraj obaveštenja od korisnika koji me ne prate + must_be_following: Blokiraj obaveštenja od ljudi koje ne pratim + must_be_following_dm: Blokiraj direktne poruke od ljudi koje ne pratim + notification_emails: + digest: Šalji e-poštu sa sažetkom + favourite: Šalji e-poštu kada neko stavi da mu je Vaš status omiljen + follow: Šalji e-poštu kada Vas neko zaprati + follow_request: Šalji e-poštu kada neko zatraži da Vas zaprati + mention: Šalji e-poštu kada Vas neko pomene + reblog: Šalji e-poštu kada neko podrži Vaš status + 'no': Ne + required: + mark: "*" + text: obavezno + 'yes': Da diff --git a/config/locales/simple_form.sr.yml b/config/locales/simple_form.sr.yml new file mode 100644 index 000000000..4e3599e5f --- /dev/null +++ b/config/locales/simple_form.sr.yml @@ -0,0 +1,75 @@ +--- +sr: + simple_form: + hints: + defaults: + avatar: PNG, GIF или JPG. Највише 2MB. Биће смањена на 120x120px + digest: Послато после дужег периода неактивности са прегледом свих битних ствари које сте добили док сте били одсутни + display_name: + few: %{count} карактера преостала + many: %{count} карактера преостало + one: 1 карактер преостао + other: %{count} карактера преостало + header: PNG, GIF или JPG. Највише 2MB. Биће смањена на 700x335px + locked: Захтева да појединачно одобрите пратиоце + note: + few: %{count} карактера преостал + many: %{count} карактера преостало + one: 1 карактер преостао + other: %{count} карактера преостало + setting_noindex: Утиче на Ваш јавни профил и статусне стране + setting_theme: Утиче како ће Мастодонт изгледати када сте пријављени са било ког уређаја. + imports: + data: CSV фајл извезен са друге Мастодонт инстанце + sessions: + otp: Унесите двофакторски код са Вашег телефона или користите један од кодова за опоравак. + user: + filtered_languages: Означени језици ће за Вас бити исфилтрирани са јавних лајни + labels: + defaults: + avatar: Аватар + confirm_new_password: Потврдите нову лозинку + confirm_password: Потврдите лозинку + current_password: Тренутна лозинка + data: Подаци + display_name: Име за приказ + email: Адреса е-поште + expires_in: Истиче након + filtered_languages: Филтрирани језици + header: Заглавље + locale: Језик + locked: Закључај налог + max_uses: Максимални број коришћења + new_password: Нова лозинка + note: Биографија + otp_attempt: Двофакторски код + password: Лозинка + setting_auto_play_gif: Аутоматски пуштај анимиране GIF-ове + setting_boost_modal: Прикажи дијалог за потврду пре давања подршке + setting_default_privacy: Приватност објава + setting_default_sensitive: Увек означи мултимедију као осетљиву + setting_delete_modal: Прикажи дијалог за потврду пре брисања тута + setting_noindex: Одјави се од индексирања search engine-а + setting_reduce_motion: Смањи покрете у анимацијама + setting_system_font_ui: Користи системски фонт + setting_theme: Тема сајта + setting_unfollow_modal: Прикажи дијалог за потврду пре него што отпратите некога + severity: Оштрина + type: Тип увоза + username: Корисничко име + interactions: + must_be_follower: Блокирај обавештења од корисника који ме не прате + must_be_following: Блокирај обавештења од људи које не пратим + must_be_following_dm: Блокирај директне поруке од људи које не пратим + notification_emails: + digest: Шаљи е-пошту са сажетком + favourite: Шаљи е-пошту када неко стави да му је Ваш статус омиљен + follow: Шаљи е-пошту када Вас неко запрати + follow_request: Шаљи е-пошту када неко затражи да Вас запрати + mention: Шаљи е-пошту када Вас неко помене + reblog: Шаљи е-пошту када неко подржи Ваш статус + 'no': Не + required: + mark: "*" + text: обавезно + 'yes': Да diff --git a/config/locales/simple_form.zh-TW.yml b/config/locales/simple_form.zh-TW.yml index c82f07e2d..b5edaea72 100644 --- a/config/locales/simple_form.zh-TW.yml +++ b/config/locales/simple_form.zh-TW.yml @@ -21,7 +21,7 @@ zh-TW: data: 資料 display_name: 顯示名稱 email: 電子信箱 - filtered_languages: 封鎖下面语言的文章 + filtered_languages: 封鎖下面語言的文章 header: 個人頁面頂部 locale: 語言 locked: 將帳號轉為「私密」 @@ -29,7 +29,16 @@ zh-TW: note: 簡介 otp_attempt: 雙因子驗證碼 password: 密碼 + setting_auto_play_gif: 自動播放 GIFs + setting_boost_modal: 轉推前跳出確認視窗 setting_default_privacy: 文章預設隱私度 + setting_default_sensitive: 預設我的內容為敏感內容 + setting_delete_modal: 刪推前跳出確認視窗 + setting_noindex: 不被搜尋引擎檢索 + setting_reduce_motion: 減低動畫效果 + setting_system_font_ui: 使用系統預設字體 + setting_theme: 網站主題 + setting_unfollow_modal: 取消關注前跳出確認視窗 type: 匯入資料類型 username: 使用者名稱 interactions: diff --git a/config/locales/sk.yml b/config/locales/sk.yml new file mode 100644 index 000000000..d3ae96dcd --- /dev/null +++ b/config/locales/sk.yml @@ -0,0 +1,144 @@ +--- +sk: + about: + about_mastodon_html: Mastodon je sociálna sieť založená na otvorených webových protokoloch. Jej zrojový kód je otvorený a je decentralizovaná podobne ako email. + about_this: Info + closed_registrations: Registrácie sú momentálne uzatvorené. Avšak, existujú ďalšie Mastodon inštancie kde si môžete založiť účet a získať prístup do tejto siete od nich. + contact: Kontakt + contact_missing: Chýba + contact_unavailable: Neuvedené + description_headline: Čo je %{domain}? + domain_count_after: ďalším inštanciám + domain_count_before: Pripojený k + extended_description_html: | +

Pravidlá

+

Žiadne zatiaľ nie sú

+ features: + humane_approach_body: Poučený z chýb iných sociálnych sietí, Mastodon sa snaží bojovať s nekalým používaním siete vytváraním etických možností. + humane_approach_title: Ľudskejší prístup + not_a_product_body: Mastodon nie je komerčná sieť. Žiadne reklamy, žiadne dolovanie dát, žiadne múry. Žiadna centrálna autorita. + not_a_product_title: Si človek, nie produkt + real_conversation_body: S 500 znakmi a podporou pre varovania pri obrázkoch a videách sa môžeš vyjadriť tak ako budeš chcieť. + real_conversation_title: Vytvorený pre reálnu konverzáciu + within_reach_body: Viacero aplikácií pre iOS, Android a iné platformy, ktoré vďaka jednoduchému API ekosystému vám dovoľujú byť online so svojimi priateľmi kdekoľvek. + within_reach_title: Stále v dosahu + find_another_instance: Nájdi inú inštanciu + generic_description: "%{domain} je jeden server v sieti" + hosted_on: Mastodon hostovaný na %{domain} + learn_more: Dozvedieť sa viac + other_instances: Zoznam ďalších inštancií + source_code: Zdrojový kód + status_count_after: príspevkov + status_count_before: Ktorí napísali + user_count_after: používateľov + user_count_before: Domov pre + what_is_mastodon: Čo je Mastodon? + accounts: + follow: Sledovať + followers: Sledujúci + following: Sleduje + media: Médiá + nothing_here: Nič tu nie je! + people_followed_by: Ľudia, ktorých %{name} sleduje + people_who_follow: Ľudia sledujúci %{name} + posts: Tooty + posts_with_replies: Tooty s odpoveďami + remote_follow: Vzdialené sledovanie + reserved_username: Prihlasovacie meno je rezervované + roles: + admin: Admin + unfollow: Prestať sledovať + admin: + accounts: + are_you_sure: Ste si istý? + confirm: Potvrdiť + confirmed: Potvrdený + disable_two_factor_authentication: Zakázať 2FA + display_name: Zobraziť meno + domain: Doména + edit: Upraviť + email: Email + feed_url: URL časovej osi + followers: Sledujúci + followers_url: URL sledujúcich + follows: Sledovaní + inbox_url: URL prijatých správ + ip: IP + location: + all: Všetko + local: Lokálne + remote: Federované + title: Lokácia + media_attachments: Prílohy + moderation: + all: Všetko + silenced: Umlčané + suspended: Suspendované + title: Moderácia + most_recent_activity: Posledná aktivita + most_recent_ip: Posledná IP + not_subscribed: Nezaregistrované + order: + alphabetic: Abecedne + most_recent: Podľa času + title: Zoradiť + outbox_url: URL poslaných + perform_full_suspension: Suspendovať + profile_url: URL profilu + protocol: Protokol + public: Verejná os + push_subscription_expires: PuSH odoberanie expiruje + redownload: Obnoviť avatar + reset: Reset + reset_password: Obnoviť heslo + resubscribe: Znovu odoberať + salmon_url: Salmon URL + search: Hľadať + shared_inbox_url: URL zdieľanej schránky + show: + created_reports: Reportované týmto používateľom + report: report + targeted_reports: Nahlásenia pre tento účet + silence: Stíšiť + statuses: Príspevky + subscribe: Odoberať + title: Účty + undo_silenced: Zrušiť stíšenie + undo_suspension: Zrušiť suspendáciu + unsubscribe: Prestať odoberať + username: Používateľske meno + web: Web + custom_emojis: + created_msg: Emoji úspešne vytvorené! + delete: Zmazať + destroyed_msg: Emojo úspešne zničený! + emoji: Emoji + image_hint: PNG do 50KB + new: + title: Pridať vlastný emoji + shortcode: Skratka + shortcode_hint: Aspoň 2 znaky, povolené sú alfanumerické alebo podčiarkovník + title: Vlastné emoji + upload: Nahrať + domain_blocks: + add_new: Pridať nový + created_msg: Doména je v procese blokovania + destroyed_msg: Blokovanie domény bolo zrušené + domain: Doména + new: + create: Blokovať doménu + hint: Blokovanie domény stále dovolí vytvárať nové účty v databáze, ale tieto budú automaticky moderované. + severity: + noop: Nič + silence: Stíšiť + suspend: Suspendovať + title: Nové blokovanie domény + reject_media: Odmietať súbory s obrázkami alebo videami + severities: + noop: Nič + silence: Stíšiť + suspend: Suspendovať + severity: Závažnosť + settings: + authorized_apps: Autorizované aplikácie + back: Naspäť na stránku diff --git a/config/locales/sr-Latn.yml b/config/locales/sr-Latn.yml new file mode 100644 index 000000000..964a82d64 --- /dev/null +++ b/config/locales/sr-Latn.yml @@ -0,0 +1,723 @@ +--- +sr-Latn: + about: + about_hashtag_html: Ovo su javni statusi tagovani sa #%{hashtag}. Možete odgovarati na njih ako imate nalog bilo gde u fediversu. + about_mastodon_html: Mastodont je društvena mreža bazirana na otvorenim protokolima i slobodnom softveru otvorenog koda. Decentralizovana je kao što je decentralizovana e-pošta. + about_this: O instanci + closed_registrations: Registracije su trenutno zatvorene na ovoj instanci. Ipak! Možete naći drugu instancu na kojoj ćete napraviti nalog i odatle dobiti pristup istoj ovoj mreži. + contact: Kontakt + contact_missing: Nije postavljeno + contact_unavailable: N/A + description_headline: Šta je %{domain}? + domain_count_after: ostale instance + domain_count_before: Povezan na + extended_description_html: | +

Dobro mesto za pravila

+

Prošireni opis koji još nije postavljen.

+ features: + humane_approach_body: Učeći od grešaka sa ostalih mreža, a da bi se borio protiv zloupotreba na društvenim mrežama, Mastodont pokušava da pravi što etičkije odluke prilikom razvoja. + humane_approach_title: Humaniji pristup + not_a_product_body: Mastodont nije komercijalna mreža. Nema reklama, nema skupljanja privatnih podataka, nema zaštićenih delova. Nema centralnog autoriteta. + not_a_product_title: Vi ste osoba, ne proizvod + real_conversation_body: Sa 500 karaktera na raspolaganju i podrškom za granularniji sadržaj i upozorenja na osetljiviji sadržaj, možete se izraziti kako god želite. + real_conversation_title: Pravljen za pravi razgovor + within_reach_body: Više aplikacija za iOS, Android, kao i druge platforme zahvaljujući ekosistemu dobrih API-ja će Vam omogućiti da ostanete u kontaktu sa prijateljima svuda. + within_reach_title: Uvek u kontaktu + find_another_instance: Nađite drugu instancu + generic_description: "%{domain} je server na mreži" + hosted_on: Mastodont hostovan na %{domain} + learn_more: Saznajte više + other_instances: Lista instanci + source_code: Izvorni kod + status_count_after: statusa + status_count_before: Koji su napisali + user_count_after: korisnika + user_count_before: Dom za + what_is_mastodon: Šta je Mastodont? + accounts: + follow: Follow + followers: Followers + following: Following + media: Multimedija + moved_html: "%{name} je pomeren na %{new_profile_link}:" + nothing_here: Ovde nema ništa! + people_followed_by: Ljudi koje %{name} prati + people_who_follow: Ljudi koji prate %{name} + posts: Tutovi + posts_with_replies: Tutovi i odgovori + remote_follow: Udaljena praćenja + reserved_username: Korisničko ime je rezervisano + roles: + admin: Administrator + moderator: Moderator + unfollow: Otprati + admin: + account_moderation_notes: + account: Moderator + create: Napravi + created_at: Datum + created_msg: Moderatorska beleška uspešno napravljena! + delete: Obriši + destroyed_msg: Moderatorska beleška uspešno obrisana! + accounts: + are_you_sure: Da li ste sigurni? + by_domain: Domen + confirm: Potvrdi + confirmed: Potvrđeno + demote: Ražaluj + disable: Isključi + disable_two_factor_authentication: Isključi 2FA + disabled: Isključena + display_name: Prikazano ime + domain: Domen + edit: Izmeni + email: E-pošta + enable: Uključi + enabled: Uključeno + feed_url: Adresa dovoda + followers: Pratioci + followers_url: Adresa pratioca + follows: Praćeni + inbox_url: Adresa sandučeta + ip: IP + location: + all: Sve + local: Lokalne + remote: Udaljene + title: Lokacija + login_status: Status prijave + media_attachments: Multimedijalni prilozi + memorialize: Prebaci u in memoriam + moderation: + all: Svi + silenced: Ućutkani + suspended: Suspendovani + title: Moderacija + moderation_notes: Moderatorske beleške + most_recent_activity: Najskorija aktivnost + most_recent_ip: Najskorija IP adresa + not_subscribed: Nije pretplaćen + order: + alphabetic: Abecedni + most_recent: Najskoriji + title: Redosled + outbox_url: Odlazno sanduče + perform_full_suspension: Izvrši kompletno isključenje + profile_url: Adresa profila + promote: Unapredi + protocol: Protokol + public: Javno + push_subscription_expires: PuSH subscription expires + redownload: Osveži avatar + reset: Resetuj + reset_password: Resetuj lozinku + resubscribe: Ponovo se pretplati + role: Ovlašćenja + roles: + admin: Administrator + moderator: Moderator + staff: Osoblje + user: Korisnik + salmon_url: Salmon adresa + search: Pretraga + shared_inbox_url: Adresa deljenog sandučeta + show: + created_reports: Prijave koje je napravio ovaj nalog + report: prijava + targeted_reports: Prijave napravljene o ovom nalogu + silence: Ućutkaj + statuses: Statusi + subscribe: Pretplati se + title: Nalozi + undo_silenced: Ukini ćutanje + undo_suspension: Ukini suspenziju + unsubscribe: Ukini pretplatu + username: Korisničko ime + web: Veb + action_logs: + actions: + confirm_user: "%{name} je potvrdio adresu e-pošte korisnika %{target}" + create_custom_emoji: "%{name} je otpremio novi emotikon %{target}" + create_domain_block: "%{name} je blokirao domen %{target}" + create_email_domain_block: "%{name} je stavio na crnu listu domen e-pošte %{target}" + demote_user: "%{name} je ražalovao korisnika %{target}" + destroy_domain_block: "%{name} je odblokirao domen %{target}" + destroy_email_domain_block: "%{name} je stavio na belu listu domen e-pošte %{target}" + destroy_status: "%{name} je uklonio status korisnika %{target}" + disable_2fa_user: "%{name} je isključio obaveznu dvofaktorsku identifikaciju za korisnika %{target}" + disable_custom_emoji: "%{name} je onemogućio emotikon %{target}" + disable_user: "%{name} je onemogućio prijavljivanje korisniku %{target}" + enable_custom_emoji: "%{name} je omogućio emotikon %{target}" + enable_user: "%{name} je omogućio prijavljivanje za korisnika %{target}" + memorialize_account: "%{name} je pretvorio stranu naloga %{target} kao in memoriam stranu" + promote_user: "%{name} je unapredio korisnika %{target}" + reset_password_user: "%{name} je resetovao lozinku korisniku %{target}" + resolve_report: "%{name} je odbacio prijavu %{target}" + silence_account: "%{name} je ućutkao nalog %{target}" + suspend_account: "%{name} je suspendovao nalog %{target}" + unsilence_account: "%{name} je ukinuo ćutanje nalogu %{target}" + unsuspend_account: "%{name} je ukinuo suspenziju nalogu %{target}" + update_custom_emoji: "%{name} je izmenio emotikon %{target}" + update_status: "%{name} je izmenio status korisnika %{target}" + title: Zapisnik + custom_emojis: + by_domain: Domen + copied_msg: Uspešno napravljena lokalna kopija emotikona + copy: Kopiraj + copy_failed_msg: Ne mogu da napravim lokalnu kopiju tog emotikona + created_msg: Emotikon uspešno napravljen! + delete: Obriši + destroyed_msg: Emotikon uspešno obrisan! + disable: Onemogući + disabled_msg: Emotikon uspešno onemogućen + emoji: Emotikon + enable: Omogući + enabled_msg: Emotikon uspešno omogućen + image_hint: PNG do 50KB + listed: Izlistan + new: + title: Dodaj novi proizvoljni emotikon + overwrite: Prepiši + shortcode: Prečica + shortcode_hint: Najmanje 2 karaktera, dozvoljeni su samo slova, brojevi i donje crte + title: Proizvoljni emotikoni + unlisted: Neizlistan + update_failed_msg: Ne mogu da ažuriram ovaj emotikon + updated_msg: emotikon uspešno ažuriran! + upload: Otpremi + domain_blocks: + add_new: Dodaj novi + created_msg: Blokiranje domena se obrađuje + destroyed_msg: Blokiranje domena je opozvano + domain: Domen + new: + create: Napravi blokadu + hint: Blokiranje domena neće sprečiti pravljenje naloga u bazi, ali će retroaktivno i automatski primeniti određene moderatorske metode nad tim nalozima. + severity: + desc_html: "Ućutkavanje će sve statuse ovog naloga učiniti nevidiljivim za sve, osim za one koji nalog već prate. Suspenzija će ukloniti sav sadržaj naloga, svu multimediju, i profilne podatke. Koristite Ništa ako samo želite da odbacite multimedijalne fajlove." + noop: Ništa + silence: Ućutkavanje + suspend: Suspenzija + title: Novo blokiranje domena + reject_media: Odbaci multimediju + reject_media_hint: Uklanja lokalno uskladištene multimedijske fajlove i odbija da ih skida na dalje. Nebitno je za suspenziju. + severities: + noop: Ništa + silence: Ućutkavanje + suspend: Suspenzija + severity: Oštrina + show: + affected_accounts: + few: Utiče na %{count} naloga u bazi + many: Utiče na %{count} naloga u bazi + one: Utiče na jedan nalog u bazi + other: Utiče na %{count} naloga u bazi + retroactive: + silence: Ugasi ućutkivanje za sve postojeće naloge sa ovog domena + suspend: Ugasi suspenzije za sve postojeće naloge sa ovog domena + title: Poništi blokadu domena za domen %{domain} + undo: Poništi + title: Blokade domena + undo: Poništi + email_domain_blocks: + add_new: Dodaj novuAdd new + created_msg: Uspešno dodao domen e-pošte na crnu listu + delete: Ukloni + destroyed_msg: Uspešno uklonjen domen e-pošte sa crne liste + domain: Domen + new: + create: Dodaj domen + title: Nova stavka u crnoj listi e-pošti + title: Crna lista adresa e-pošte + instances: + account_count: Poznati nalozi + domain_name: Domen + reset: Resetuj + search: Pretraga + title: Poznate instance + invites: + filter: + all: Sve + available: Aktivne + expired: Istekle + title: Filter + title: Pozivnice + reports: + action_taken_by: Akciju izveo + are_you_sure: Da li ste sigurni? + comment: + label: Komentar + none: Ništa + delete: Obriši + id: ID + mark_as_resolved: Označi kao rešen + nsfw: + 'false': Otkrij medijske priloge + 'true': Sakrij medijske priloge + report: 'Prijava #%{id}' + report_contents: Sadržaj + reported_account: Prijavljeni nalog + reported_by: Prijavio + resolved: Rešeni + silence_account: Ućutkaj nalog + status: Status + suspend_account: Suspenduj nalog + target: Cilj + title: Prijave + unresolved: Nerešeni + view: Pogledaj + settings: + bootstrap_timeline_accounts: + desc_html: Odvojite više korisničkih imena zarezom. Radi samo za lokalne i otključane naloge. Ako je prazno, onda se odnosi na sve lokalne administratore. + title: Nalozi za automatsko zapraćivanje za nove korisnike + contact_information: + email: Poslovna e-pošta + username: Kontakt korisničko ime + registrations: + closed_message: + desc_html: Prikazuje se na glavnoj strani kada je instanca zatvorena za registracije. Možete koristiti HTML tagove + title: Poruka o zatvorenoj registraciji + deletion: + desc_html: Dozvoli svima da mogu da obrišu svoj nalog + title: Otvori brisanje naloga + min_invite_role: + disabled: Niko + title: Samo preko pozivnice + open: + desc_html: Dozvoli svakome da kreira nalog + title: Otvorena registracija + show_staff_badge: + desc_html: Prikaži bedž osoblja na korisničkoj strani + title: Prikaži bedž osoblja + site_description: + desc_html: Uvodni pasus na naslovnoj strani i u meta HTML tagovima. Možete koristiti HTML tagove, konkretno <a> i <em>. + title: Opis instance + site_description_extended: + desc_html: Dobro mesto za vaš kod ponašanja, pravila, smernice i druge stvari po kojima se Vaša instanca razlikuje. Možete koristiti HTML tagove + title: Proizvoljne dodatne informacije + site_terms: + desc_html: Možete pisati Vašu politiku privatnosti, uslove korišćenja i ostale legalne stvari. Možete koristiti HTML tagove + title: Proizvoljni uslovi korišćenja + site_title: Ime instance + thumbnail: + desc_html: Koristi se za preglede kroz OpenGraph i API. Preporučuje se 1200x630px + title: Sličica instance + timeline_preview: + desc_html: Prikaži javnu lajnu na početnoj strani + title: Pregled lajne + title: Postavke sajta + statuses: + back_to_account: Nazad na stranu naloga + batch: + delete: Obriši + nsfw_off: NSFW isključen + nsfw_on: NSFW uključen + execute: Izvrši + failed_to_execute: Neuspelo izvršavanje + media: + hide: Sakrij multimediju + show: Prikaži multimediju + title: Multimedija + no_media: Bez multimedije + title: Statusi naloga + with_media: Sa multimedijom + subscriptions: + callback_url: Callback URL + confirmed: Potvrđeno + expires_in: Ističe za + last_delivery: Poslednja dostava + title: WebSub + topic: Topic + title: Administracija + admin_mailer: + new_report: + body: "%{reporter} je prijavio %{target}" + subject: Nova prijava za %{instance} (#%{id}) + application_mailer: + salutation: "%{name}," + settings: 'Promeni podešavanja e-pošte: %{link}' + signature: Mastodont obaveštenje sa instance %{instance} + view: 'Pogledaj:' + applications: + created: Aplikacija uspešno napravljena + destroyed: Aplikacija uspešno obrisana + invalid_url: Data adresa nije ispravna + regenerate_token: Rekreiraj pristupni token + token_regenerated: Pristupni token uspešno rekreiran + warning: Oprezno sa ovim podacima. Nikad je ne delite ni sa kim! + your_token: Vaš pristupni token + auth: + agreement_html: Pristupanjem instanci se slažete sa pravilima instance i uslovima korišćenja. + change_password: Bezbednost + delete_account: Obriši nalog + delete_account_html: Ako želite da obrišete Vaš nalog, možete nastaviti ovde. Bićete upitani da potvrdite. + didnt_get_confirmation: Niste dobili poruku sa uputstvima za potvrdu naloga? + forgot_password: Zaboravili ste lozinku? + invalid_reset_password_token: Token za resetovanje lozinke je neispravan ili je istekao. Zatražite novi. + login: Prijavi se + logout: Odjava + migrate_account: Pomeri u drugi nalog + migrate_account_html: Ako želite da preusmerite ovaj nalog na neki drugi, možete to podesiti ovde. + register: Registruj se + resend_confirmation: Pošalji poruku sa uputstvima o potvrdi naloga ponovo + reset_password: Resetuj lozinku + set_new_password: Postavi novu lozinku + authorize_follow: + error: Nažalost, desila se greška pri traženju udaljenog naloga + follow: Zaprati + follow_request: 'Poslali ste zahtev za praćenjen za:' + following: 'Sjajno! Sada pratite:' + post_follow: + close: Ili možete zatvoriti ovaj prozor. + return: Vrati se na profil ovog korisnika + web: Idi na veb + title: Zaprati %{acct} + datetime: + distance_in_words: + about_x_hours: "%{count}h" + about_x_months: "%{count}mesec" + about_x_years: "%{count}god" + almost_x_years: "%{count}god" + half_a_minute: Upravo sad + less_than_x_minutes: "%{count}m" + less_than_x_seconds: Upravo sad + over_x_years: "%{count}god" + x_days: "%{count}d" + x_minutes: "%{count}m" + x_months: "%{count}mesec" + x_seconds: "%{count}s" + deletes: + bad_password_msg: Dobar pokušaj, hakeri! Neispravna lozinka + confirm_password: Unesite trenutnu lozinku da bismo proverili Vaš identitet + description_html: Ovo će trajno, bespovratno ukloniti sadržaj sa Vašef naloga i deaktivirati ga. Vaše korisničko ime će ostati rezervisano da se spreči da se neko ne predstavlja kao Vi sutra. + proceed: Obriši nalog + success_msg: Vaš nalog je uspešno obrisan + warning_html: Garantovano je samo brisanje sadržaja sa ove instance. Sadržaj koji je deljen dalje će verovatno da ostavi neke tragove. Nedostupni i ugašeni serveri, kao i serveri koji su odjavljeni od primanja statusa od Vas, neće ažurirati svoje baze. + warning_title: Dostupnost rasejanog sadržaja + errors: + '403': Nemate dozvola da vidite ovu stranu. + '404': Strana koju ste tražili ne postoji. + '410': Strana koju ste tražili više ne postoji. + '422': + content: Security verification failed. Are you blocking cookies? + title: Security verification failed + '429': Uspored + '500': + content: Izvinjavamo se, nešto je pošlo po zlu sa ove strane. + title: Strana nije ispravna + noscript_html: Da biste koristili Mastodont veb aplikaciju, omogućite JavaScript. U suprotnom, probajte neku od originalnih aplikacija za Mastodont za Vašu platformu. + exports: + blocks: Blokirali ste + csv: CSV + follows: Pratite + mutes: Ućutkali ste + storage: Multimedijalno skladište + followers: + domain: Domen + explanation_html: Ako želite da osigurate privatnost Vaših statusa, morate biti svesni ko Vas prati. Vaši privatni statusi se šalju na sve instance na kojima imate pratioce. Možda želite da ih pregledate i da uklonite one pratioce na onim instancama za koje nemate poverenja da će poštovati Vašu privatnost. + followers_count: Broj pratilaca + lock_link: Zaključajte nalog + purge: Ukloni iz pratioca + success: + few: U procesu blokiranja pratioca sa %{count} domena... + many: U procesu blokiranja pratioca sa %{count} domena... + one: U procesu blokiranja pratioca sa jednog domena... + other: U procesu blokiranja pratioca sa %{count} domena... + true_privacy_html: Zapamtite da se prava privatnost može postići samo šifrovanjem sa kraja na kraj. + unlocked_warning_html: Svako može da Vas zaprati da odmah vidi Vaše privatne statuse. %{lock_link} da biste pregledali i odbacili pratioce. + unlocked_warning_title: Vaš nalog nije zaključan + generic: + changes_saved_msg: Izmene uspešno sačuvane! + powered_by: omogućio %{link} + save_changes: Snimi izmene + validation_errors: + few: Nešto nije baš kako treba! Pregledajte %{count} greške ispod + many: Nešto nije baš kako treba! Pregledajte %{count} grešaka ispod + one: Nešto nije baš kako treba! Pregledajte greške ispod + other: Nešto nije baš kako treba! Pregledajte %{count} grešaka ispod + imports: + preface: Možete uvesti podatke koje ste izvezli sa druge instance, kao što su liste ljudi koje ste pratili ili blokirali. + success: Vaši podaci su uspešno otpremljeni i biće obrađeni uskoro + types: + blocking: Lista blokiranja + following: Lista pratilaca + muting: Lista ućutkanih + upload: Otpremi + in_memoriam_html: In Memoriam. + invites: + delete: Deaktiviraj + expired: Isteklo + expires_in: + '1800': 30 minuta + '21600': 6 sati + '3600': 1 sad + '43200': 12 sati + '86400': 1 dan + expires_in_prompt: Nikad + generate: Generiši + max_uses: + few: "%{count} korišćenja" + many: "%{count} korišćenja" + one: 1 korišćenje + other: "%{count} korišćenja" + max_uses_prompt: Bez ograničenja + prompt: Generiši i podeli linkove sa drugima da im odobrite pristup ovoj instanci + table: + expires_at: Ističe + uses: Korišćenja + title: Pozovi ljude + landing_strip_html: "%{name} je korisnik na %{link_to_root_path}. Možete ga zapratiti ili komunicirati sa njim ako imte nalog bilo gde u fediversu." + landing_strip_signup_html: Ako nemate, možete se registrovati ovde. + lists: + errors: + limit: Dostigli ste limit broja listi + media_attachments: + validations: + images_and_video: Ne može da se prikači video na status koji već ima slike + too_many: Ne može se prikačiti više od 4 fajla + migrations: + acct: korisnik@domen novog naloga + currently_redirecting: 'Profil Vam je podešen da preusmerava na :' + proceed: Sačuvaj + updated_msg: Prebacivanje postavki Vašeg naloga uspešno izmenjeno! + moderation: + title: Moderacija + notification_mailer: + digest: + body: 'Evo kratak pregled šta ste propustili na instanci %{instance} od poslednje posete od %{since}:' + mention: "%{name} Vas je pomenuo u:" + new_followers_summary: + few: Dobili ste %{count} nova pratioca! Sjajno! + many: Dobili ste %{count} novih pratioca! Sjajno! + one: Dobili ste jednog novog pratioca! Jeee! + other: Dobili ste %{count} novih pratioca! Sjajno! + subject: + few: "%{count} nova obaveštenja od poslednje posete \U0001F418" + many: "%{count} novih obaveštenja od poslednje posete \U0001F418" + one: "1 novo obaveštenje od poslednje posete \U0001F418" + other: "%{count} novih obaveštenja od poslednje posete \U0001F418" + favourite: + body: "%{name} je postavio kao omiljen Vaš status:" + subject: "%{name} je postavio kao omiljen Vaš status" + follow: + body: "%{name} Vas je zapratio!" + subject: "%{name} Vas je zapratio" + follow_request: + body: "%{name} je zatražio da Vas zaprati" + subject: 'Pratioci na čekanju: %{name}' + mention: + body: "%{name} Vas je pomenuo u:" + subject: "%{name} Vas je pomenuo" + reblog: + body: "%{name} Vam je podržao(la) status:" + subject: "%{name} je podržao(la) Vaš status" + number: + human: + decimal_units: + format: "%n%u" + units: + billion: B + million: M + quadrillion: Q + thousand: K + trillion: T + unit: '' + pagination: + next: Sledeći + prev: Prethodni + truncate: "…" + preferences: + languages: Jezici + other: Ostali + publishing: Objavljivanje + web: Veb + push_notifications: + favourite: + title: "%{name} je stavio Vaš status za omiljeni" + follow: + title: "%{name} Vas je zapratio" + group: + title: "%{count} obaveštenja" + mention: + action_boost: Podrži + action_expand: Prikaži još + action_favourite: Omiljeni + title: "%{name} Vas je pomenuo" + reblog: + title: "%{name} je podržao(la) Vaš status" + remote_follow: + acct: Unesite Vaš korisnik@domen sa koga želite da pratite + missing_resource: Ne mogu da nađem zahtevanu adresu preusmeravanja za Vaš nalog + proceed: Nastavite da zapratite + prompt: 'Zapratite će:' + sessions: + activity: Poslednja aktivnost + browser: Veb čitač + browsers: + alipay: Alipay + blackberry: Blekberi + chrome: Hrom + edge: Microsoft Edge + firefox: Firefox + generic: Nepoznati veb čitač + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Opera + phantom_js: PhantomJS + qq: QQ Browser + safari: Safari + uc_browser: UCBrowser + weibo: Weibo + current_session: Trenutna sesija + description: "%{browser} sa %{platform}" + explanation: Ovo su trenutno prijavljeni veb čitači na Vaš Mastodont nalog. + ip: IP + platforms: + adobe_air: Adobe Air-a + android: Androida + blackberry: Blekberija + chrome_os: Hrom OS-a + firefox_os: Fajerfoks OS-a + ios: iOS + linux: Linuksa + mac: Mac-a + other: nepoznate platforme + windows: Vindouza + windows_mobile: Vindouz mobilnog + windows_phone: Vindouz telefona + revoke: Opozovi + revoke_success: Sesija uspešno opozvana + title: Sesije + settings: + authorized_apps: Autorizovane aplikacije + back: Nazad na Mastodonta + delete: Brisanje naloga + development: Razvoj + edit_profile: Izmena profila + export: Izvoz podataka + followers: Autorizovani pratioci + import: Uvoz + migrate: Prebacivanje naloga + notifications: Obaveštenja + preferences: Podešavanja + settings: Postavke + two_factor_authentication: Dvofaktorska identifikacija + your_apps: Vaše aplikacije + statuses: + open_in_web: Otvori u vebu + over_character_limit: ograničenje od %{max} karaktera prekoračeno + pin_errors: + limit: Već imate prikačen najveći broj tutova + ownership: Tuđi tutovi ne mogu da se prikače + private: Tutovi koji nisu javni ne mogu da se prikače + reblog: Podrška ne može da se prikači + show_more: Prikaži još + title: '%{name}: "%{quote}"' + visibilities: + private: Samo pratioci + private_long: Samo prikaži pratiocima + public: Javno + public_long: Svako može da vidi + unlisted: Neizlistano + unlisted_long: Svako može da vidi, ali nije izlistano na javnim lajnama + stream_entries: + click_to_show: Klikni da vidiš + pinned: Prikačeni tut + reblogged: podržano + sensitive_content: Osetljiv sadržaj + terms: + body_html: | +

Privacy Policy

+ +

What information do we collect?

+ +

We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here.

+ +

When registering on our site, you may be asked to enter your name and e-mail address. You may, however, visit our site without registering. Your e-mail address will be verified by an email containing a unique link. If that link is visited, we know that you control the e-mail address.

+ +

When registered and posting, we record the IP address that the post originated from. We also may retain server logs which include the IP address of every request to our server.

+ +

What do we use your information for?

+ +

Any of the information we collect from you may be used in one of the following ways:

+ +
    +
  • To personalize your experience — your information helps us to better respond to your individual needs.
  • +
  • To improve our site — we continually strive to improve our site offerings based on the information and feedback we receive from you.
  • +
  • To improve customer service — your information helps us to more effectively respond to your customer service requests and support needs.
  • +
  • To send periodic emails — The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions.
  • +
+ +

How do we protect your information?

+ +

We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information.

+ +

What is your data retention policy?

+ +

We will make a good faith effort to:

+ +
    +
  • Retain server logs containing the IP address of all requests to this server no more than 90 days.
  • +
  • Retain the IP addresses associated with registered users and their posts no more than 5 years.
  • +
+ +

Do we use cookies?

+ +

Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.

+ +

We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.

+ +

Do we disclose any information to outside parties?

+ +

We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.

+ +

Third party links

+ +

Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.

+ +

Children's Online Privacy Protection Act Compliance

+ +

Our site, products and services are all directed to people who are at least 13 years old. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA (Children's Online Privacy Protection Act) do not use this site.

+ +

Online Privacy Policy Only

+ +

This online privacy policy applies only to information collected through our site and not to information collected offline.

+ + + +

By using our site, you consent to our web site privacy policy.

+ +

Changes to our Privacy Policy

+ +

If we decide to change our privacy policy, we will post those changes on this page.

+ +

This document is CC-BY-SA. It was last updated May 31, 2013.

+ +

Originally adapted from the Discourse privacy policy.

+ title: Uslovi korišćenja i politika privatnosti instance %{instance} + themes: + default: Mastodont + time: + formats: + default: "%b %d, %Y, %H:%M" + two_factor_authentication: + code_hint: Unesite kod sa Vaše aplikacije za proveru identiteta da potvrdite + description_html: Ako uključite dvofaktorsku identifikaciju, moraćete da imate telefon sa sobom da biste mogli da se prijavite. Telefon će onda generisati tokene za Vašu prijavu. + disable: Isključi + enable: Uključi + enabled: Dvofaktorska identifikacija je uključena + enabled_success: Dvofaktorska identifikacija je uspešno uključena + generate_recovery_codes: Generiši kodove za oporavak + instructions_html: "Skenirajte ovaj QR kod u Google Authenticator ili nekoj sličnoj TOTP aplikaciji na Vašem telefonu. Od sada, ta aplikacija će Vam generisati tokene koje morate uneti da biste se prijavili." + lost_recovery_codes: Kodovi za oporavak Vam omogućavaju da povratite pristup nalogu ako izgubite telefon. Ako izgubite kodove za oporavak, možete ih regenerisati ovde. Od tog trenutka, stari kodovi za oporavak više ne važe. + manual_instructions: 'Ukoliko ne možete da skenirate QR kod i morate ga unesete ručno, evo je ogoljena šifra:' + recovery_codes: Napravite rezervu kodova za oporavak + recovery_codes_regenerated: Kodovi za oporavak uspešno regenerisani + recovery_instructions_html: Ako ikada izgubite pristup telefonu, možete iskoristiti kodove za oporavak date ispod da povratite pristup nalogu. Držite kodove za oporavak na sigurnom. Na primer, odštampajte ih i čuvajte ih sa ostalim važnim dokumentima. + setup: Nameštanje + wrong_code: Uneseni kod nije ispravan! Da li su vremena na serveru i na uređaju ispravna? + users: + invalid_email: Adresa e-pošte nije ispravna + invalid_otp_token: Neispravni dvofaktorski kod + signed_in_as: 'Prijavljen kao:' diff --git a/config/locales/sr.yml b/config/locales/sr.yml new file mode 100644 index 000000000..57ccf2008 --- /dev/null +++ b/config/locales/sr.yml @@ -0,0 +1,723 @@ +--- +sr: + about: + about_hashtag_html: Ово су јавни статуси таговани са #%{hashtag}. Можете одговарати на њих ако имате налог било где у федиверсу. + about_mastodon_html: Мастодонт је друштвена мрежа базирана на отвореним протоколима и слободном софтверу отвореног кода. Децентрализована је као што је децентрализована е-пошта. + about_this: О инстанци + closed_registrations: Регистрације су тренутно затворене на овој инстанци. Ипак! Можете наћи другу инстанцу на којој ћете направити налог и одатле добити приступ истој овој мрежи. + contact: Контакт + contact_missing: Није постављено + contact_unavailable: N/A + description_headline: Шта је %{domain}? + domain_count_after: остале инстанце + domain_count_before: Повезан на + extended_description_html: | +

Добро место за правила

+

Проширени опис који још није постављен.

+ features: + humane_approach_body: Учећи од грешака са осталих мрежа, а да би се борио против злоупотреба на друштвеним мрежама, Мастодонт покушава да прави што етичкије одлуке приликом развоја. + humane_approach_title: Хуманији приступ + not_a_product_body: Мастодонт није комерцијална мрежа. Нема реклама, нема скупљања приватних података, нема заштићених делова. Нема централног ауторитета. + not_a_product_title: Ви сте особа, не производ + real_conversation_body: Са 500 карактера на располагању и подршком за грануларнији садржај и упозорења на осетљивији садржај, можете се изразити како год желите. + real_conversation_title: Прављен за прави разговор + within_reach_body: Више апликација за iOS, Андроид, као и друге платформе захваљујући екосистему добрих API-ја ће Вам омогућити да останете у контакту са пријатељима свуда. + within_reach_title: Увек у контакту + find_another_instance: Нађите другу инстанцу + generic_description: "%{domain} је сервер на мрежи" + hosted_on: Мастодонт хостован на %{domain} + learn_more: Сазнајте више + other_instances: Листа инстанци + source_code: Изворни код + status_count_after: статуса + status_count_before: Који су написали + user_count_after: корисника + user_count_before: Дом за + what_is_mastodon: Шта је Мастодонт? + accounts: + follow: Follow + followers: Followers + following: Following + media: Мултимедија + moved_html: "%{name} је померен на %{new_profile_link}:" + nothing_here: Овде нема ништа! + people_followed_by: Људи које %{name} прати + people_who_follow: Људи који прате %{name} + posts: Тутови + posts_with_replies: Тутови и одговори + remote_follow: Удаљена праћења + reserved_username: Корисничко име је резервисано + roles: + admin: Администратор + moderator: Модератор + unfollow: Отпрати + admin: + account_moderation_notes: + account: Модератор + create: Направи + created_at: Датум + created_msg: Модераторска белешка успешно направљена! + delete: Обриши + destroyed_msg: Модераторска белешка успешно обрисана! + accounts: + are_you_sure: Да ли сте сигурни? + by_domain: Домен + confirm: Потврди + confirmed: Потврђено + demote: Ражалуј + disable: Искључи + disable_two_factor_authentication: Искључи 2FA + disabled: Искључена + display_name: Приказано име + domain: Домен + edit: Измени + email: Е-пошта + enable: Укључи + enabled: Укључено + feed_url: Адреса довода + followers: Пратиоци + followers_url: Адреса пратиоца + follows: Праћени + inbox_url: Адреса сандучета + ip: IP + location: + all: Све + local: Локалне + remote: Удаљене + title: Локација + login_status: Статус пријаве + media_attachments: Мултимедијални прилози + memorialize: Пребаци у in memoriam + moderation: + all: Сви + silenced: Ућуткани + suspended: Суспендовани + title: Модерација + moderation_notes: Модераторске белешке + most_recent_activity: Најскорија активност + most_recent_ip: Најскорија IP адреса + not_subscribed: Није претплаћен + order: + alphabetic: Абецедни + most_recent: Најскорији + title: Редослед + outbox_url: Одлазно сандуче + perform_full_suspension: Изврши комплетно искључење + profile_url: Адреса профила + promote: Унапреди + protocol: Протокол + public: Јавно + push_subscription_expires: PuSH subscription expires + redownload: Освежи аватар + reset: Ресетуј + reset_password: Ресетуј лозинку + resubscribe: Поново се претплати + role: Овлашћења + roles: + admin: Администратор + moderator: Модератор + staff: Особље + user: Корисник + salmon_url: Salmon адреса + search: Претрага + shared_inbox_url: Адреса дељеног сандучета + show: + created_reports: Пријаве које је направио овај налог + report: пријава + targeted_reports: Пријаве направљене о овом налогу + silence: Ућуткај + statuses: Статуси + subscribe: Претплати се + title: Налози + undo_silenced: Укини ћутање + undo_suspension: Укини суспензију + unsubscribe: Укини претплату + username: Корисничко име + web: Веб + action_logs: + actions: + confirm_user: "%{name} је потврдио адресу е-поште корисника %{target}" + create_custom_emoji: "%{name} је отпремио нови емотикон %{target}" + create_domain_block: "%{name} је блокирао домен %{target}" + create_email_domain_block: "%{name} је ставио на црну листу домен е-поште %{target}" + demote_user: "%{name} је ражаловао корисника %{target}" + destroy_domain_block: "%{name} је одблокирао домен %{target}" + destroy_email_domain_block: "%{name} је ставио на белу листу домен е-поште %{target}" + destroy_status: "%{name} је уклонио статус корисника %{target}" + disable_2fa_user: "%{name} је искључио обавезну двофакторску идентификацију за корисника %{target}" + disable_custom_emoji: "%{name} је онемогућио емотикон %{target}" + disable_user: "%{name} је онемогућио пријављивање кориснику %{target}" + enable_custom_emoji: "%{name} је омогућио емотикон %{target}" + enable_user: "%{name} је омогућио пријављивање за корисника %{target}" + memorialize_account: "%{name} је претворио страну налога %{target} као in memoriam страну" + promote_user: "%{name} је унапредио корисника %{target}" + reset_password_user: "%{name} је ресетовао лозинку кориснику %{target}" + resolve_report: "%{name} је одбацио пријаву %{target}" + silence_account: "%{name} је ућуткао налог %{target}" + suspend_account: "%{name} је суспендовао налог %{target}" + unsilence_account: "%{name} је укинуо ћутање налогу %{target}" + unsuspend_account: "%{name} је укинуо суспензију налогу %{target}" + update_custom_emoji: "%{name} је изменио емотикон %{target}" + update_status: "%{name} је изменио статус корисника %{target}" + title: Записник + custom_emojis: + by_domain: Домен + copied_msg: Успешно направљена локална копија емотикона + copy: Копирај + copy_failed_msg: Не могу да направим локалну копију тог емотикона + created_msg: Емотикон успешно направљен! + delete: Обриши + destroyed_msg: Емотикон успешно обрисан! + disable: Онемогући + disabled_msg: Емотикон успешно онемогућен + emoji: Емотикон + enable: Омогући + enabled_msg: Емотикон успешно омогућен + image_hint: PNG до 50KB + listed: Излистан + new: + title: Додај нови произвољни емотикон + overwrite: Препиши + shortcode: Пречица + shortcode_hint: Најмање 2 карактера, дозвољени су само слова, бројеви и доње црте + title: Произвољни емотикони + unlisted: Неизлистан + update_failed_msg: Не могу да ажурирам овај емотикон + updated_msg: емотикон успешно ажуриран! + upload: Отпреми + domain_blocks: + add_new: Додај нови + created_msg: Блокирање домена се обрађује + destroyed_msg: Блокирање домена је опозвано + domain: Домен + new: + create: Направи блокаду + hint: Блокирање домена неће спречити прављење налога у бази, али ће ретроактивно и аутоматски применити одређене модераторске методе над тим налозима. + severity: + desc_html: "Ућуткавање ће све статусе овог налога учинити невидиљивим за све, осим за оне који налог већ прате. Суспензија ће уклонити сав садржај налога, сву мултимедију, и профилне податке. Користите Ништа ако само желите да одбаците мултимедијалне фајлове." + noop: Ништа + silence: Ућуткавање + suspend: Суспензија + title: Ново блокирање домена + reject_media: Одбаци мултимедију + reject_media_hint: Уклања локално ускладиштене мултимедијске фајлове и одбија да их скида на даље. Небитно је за суспензију. + severities: + noop: Ништа + silence: Ућуткавање + suspend: Суспензија + severity: Оштрина + show: + affected_accounts: + few: Утиче на %{count} налога у бази + many: Утиче на %{count} налога у бази + one: Утиче на један налог у бази + other: Утиче на %{count} налога у бази + retroactive: + silence: Угаси ућуткивање за све постојеће налоге са овог домена + suspend: Угаси суспензије за све постојеће налоге са овог домена + title: Поништи блокаду домена за домен %{domain} + undo: Поништи + title: Блокаде домена + undo: Поништи + email_domain_blocks: + add_new: Додај новуAdd new + created_msg: Успешно додао домен е-поште на црну листу + delete: Уклони + destroyed_msg: Успешно уклоњен домен е-поште са црне листе + domain: Домен + new: + create: Додај домен + title: Нова ставка у црној листи е-пошти + title: Црна листа адреса е-поште + instances: + account_count: Познати налози + domain_name: Домен + reset: Ресетуј + search: Претрага + title: Познате инстанце + invites: + filter: + all: Све + available: Активне + expired: Истекле + title: Филтер + title: Позивнице + reports: + action_taken_by: Акцију извео + are_you_sure: Да ли сте сигурни? + comment: + label: Коментар + none: Ништа + delete: Обриши + id: ID + mark_as_resolved: Означи као решен + nsfw: + 'false': Откриј медијске прилоге + 'true': Сакриј медијске прилоге + report: 'Пријава #%{id}' + report_contents: Садржај + reported_account: Пријављени налог + reported_by: Пријавио + resolved: Решени + silence_account: Ућуткај налог + status: Статус + suspend_account: Суспендуј налог + target: Циљ + title: Пријаве + unresolved: Нерешени + view: Погледај + settings: + bootstrap_timeline_accounts: + desc_html: Одвојите више корисничких имена зарезом. Ради само за локалне и откључане налоге. Ако је празно, онда се односи на све локалне администраторе. + title: Налози за аутоматско запраћивање за нове кориснике + contact_information: + email: Пословна е-пошта + username: Контакт корисничко име + registrations: + closed_message: + desc_html: Приказује се на главној страни када је инстанца затворена за регистрације. Можете користити HTML тагове + title: Порука о затвореној регистрацији + deletion: + desc_html: Дозволи свима да могу да обришу свој налог + title: Отвори брисање налога + min_invite_role: + disabled: Нико + title: Само преко позивнице + open: + desc_html: Дозволи свакоме да креира налог + title: Отворена регистрација + show_staff_badge: + desc_html: Прикажи беџ особља на корисничкој страни + title: Прикажи беџ особља + site_description: + desc_html: Уводни пасус на насловној страни и у meta HTML таговима. Можете користити HTML тагове, конкретно <a> и <em>. + title: Опис инстанце + site_description_extended: + desc_html: Добро место за ваш код понашања, правила, смернице и друге ствари по којима се Ваша инстанца разликује. Можете користити HTML тагове + title: Произвољне додатне информације + site_terms: + desc_html: Можете писати Вашу политику приватности, услове коришћења и остале легалне ствари. Можете користити HTML тагове + title: Произвољни услови коришћења + site_title: Име инстанце + thumbnail: + desc_html: Користи се за прегледе кроз OpenGraph и API. Препоручује се 1200x630px + title: Сличица инстанце + timeline_preview: + desc_html: Прикажи јавну лајну на почетној страни + title: Преглед лајне + title: Поставке сајта + statuses: + back_to_account: Назад на страну налога + batch: + delete: Обриши + nsfw_off: NSFW искључен + nsfw_on: NSFW укључен + execute: Изврши + failed_to_execute: Неуспело извршавање + media: + hide: Сакриј мултимедију + show: Прикажи мултимедију + title: Мултимедија + no_media: Без мултимедије + title: Статуси налога + with_media: Са мултимедијом + subscriptions: + callback_url: Callback URL + confirmed: Потврђено + expires_in: Истиче за + last_delivery: Последња достава + title: WebSub + topic: Topic + title: Администрација + admin_mailer: + new_report: + body: "%{reporter} је пријавио %{target}" + subject: Нова пријава за %{instance} (#%{id}) + application_mailer: + salutation: "%{name}," + settings: 'Промени подешавања е-поште: %{link}' + signature: Мастодонт обавештење са инстанце %{instance} + view: 'Погледај:' + applications: + created: Апликација успешно направљена + destroyed: Апликација успешно обрисана + invalid_url: Дата адреса није исправна + regenerate_token: Рекреирај приступни токен + token_regenerated: Приступни токен успешно рекреиран + warning: Опрезно са овим подацима. Никад је не делите ни са ким! + your_token: Ваш приступни токен + auth: + agreement_html: Приступањем инстанци се слажете са правилима инстанце и условима коришћења. + change_password: Безбедност + delete_account: Обриши налог + delete_account_html: Ако желите да обришете Ваш налог, можете наставити овде. Бићете упитани да потврдите. + didnt_get_confirmation: Нисте добили поруку са упутствима за потврду налога? + forgot_password: Заборавили сте лозинку? + invalid_reset_password_token: Токен за ресетовање лозинке је неисправан или је истекао. Затражите нови. + login: Пријави се + logout: Одјава + migrate_account: Помери у други налог + migrate_account_html: Ако желите да преусмерите овај налог на неки други, можете то подесити овде. + register: Региструј се + resend_confirmation: Пошаљи поруку са упутствима о потврди налога поново + reset_password: Ресетуј лозинку + set_new_password: Постави нову лозинку + authorize_follow: + error: Нажалост, десила се грешка при тражењу удаљеног налога + follow: Запрати + follow_request: 'Послали сте захтев за праћењен за:' + following: 'Сјајно! Сада пратите:' + post_follow: + close: Или можете затворити овај прозор. + return: Врати се на профил овог корисника + web: Иди на веб + title: Запрати %{acct} + datetime: + distance_in_words: + about_x_hours: "%{count}h" + about_x_months: "%{count}месец" + about_x_years: "%{count}год" + almost_x_years: "%{count}год" + half_a_minute: Управо сад + less_than_x_minutes: "%{count}m" + less_than_x_seconds: Управо сад + over_x_years: "%{count}год" + x_days: "%{count}д" + x_minutes: "%{count}m" + x_months: "%{count}месец" + x_seconds: "%{count}s" + deletes: + bad_password_msg: Добар покушај, хакери! Неисправна лозинка + confirm_password: Унесите тренутну лозинку да бисмо проверили Ваш идентитет + description_html: Ово ће трајно, бесповратно уклонити садржај са Вашеф налога и деактивирати га. Ваше корисничко име ће остати резервисано да се спречи да се неко не представља као Ви сутра. + proceed: Обриши налог + success_msg: Ваш налог је успешно обрисан + warning_html: Гарантовано је само брисање садржаја са ове инстанце. Садржај који је дељен даље ће вероватно да остави неке трагове. Недоступни и угашени сервери, као и сервери који су одјављени од примања статуса од Вас, неће ажурирати своје базе. + warning_title: Доступност расејаног садржаја + errors: + '403': Немате дозвола да видите ову страну. + '404': Страна коју сте тражили не постоји. + '410': Страна коју сте тражили више не постоји. + '422': + content: Security verification failed. Are you blocking cookies? + title: Security verification failed + '429': Успоред + '500': + content: Извињавамо се, нешто је пошло по злу са ове стране. + title: Страна није исправна + noscript_html: Да бисте користили Мастодонт веб апликацију, омогућите JavaScript. У супротном, пробајте неку од оригиналних апликација за Мастодонт за Вашу платформу. + exports: + blocks: Блокирали сте + csv: CSV + follows: Пратите + mutes: Ућуткали сте + storage: Мултимедијално складиште + followers: + domain: Домен + explanation_html: Ако желите да осигурате приватност Ваших статуса, морате бити свесни ко Вас прати. Ваши приватни статуси се шаљу на све инстанце на којима имате пратиоце. Можда желите да их прегледате и да уклоните оне пратиоце на оним инстанцама за које немате поверења да ће поштовати Вашу приватност. + followers_count: Број пратилаца + lock_link: Закључајте налог + purge: Уклони из пратиоца + success: + few: У процесу блокирања пратиоца са %{count} домена... + many: У процесу блокирања пратиоца са %{count} домена... + one: У процесу блокирања пратиоца са једног домена... + other: У процесу блокирања пратиоца са %{count} домена... + true_privacy_html: Запамтите да се права приватност може постићи само шифровањем са краја на крај. + unlocked_warning_html: Свако може да Вас запрати да одмах види Ваше приватне статусе. %{lock_link} да бисте прегледали и одбацили пратиоце. + unlocked_warning_title: Ваш налог није закључан + generic: + changes_saved_msg: Измене успешно сачуване! + powered_by: омогућио %{link} + save_changes: Сними измене + validation_errors: + few: Нешто није баш како треба! Прегледајте %{count} грешке испод + many: Нешто није баш како треба! Прегледајте %{count} грешака испод + one: Нешто није баш како треба! Прегледајте грешке испод + other: Нешто није баш како треба! Прегледајте %{count} грешака испод + imports: + preface: Можете увести податке које сте извезли са друге инстанце, као што су листе људи које сте пратили или блокирали. + success: Ваши подаци су успешно отпремљени и биће обрађени ускоро + types: + blocking: Листа блокирања + following: Листа пратилаца + muting: Листа ућутканих + upload: Отпреми + in_memoriam_html: In Memoriam. + invites: + delete: Деактивирај + expired: Истекло + expires_in: + '1800': 30 минута + '21600': 6 сати + '3600': 1 сад + '43200': 12 сати + '86400': 1 дан + expires_in_prompt: Никад + generate: Генериши + max_uses: + few: "%{count} коришћења" + many: "%{count} коришћења" + one: 1 коришћење + other: "%{count} коришћења" + max_uses_prompt: Без ограничења + prompt: Генериши и подели линкове са другима да им одобрите приступ овој инстанци + table: + expires_at: Истиче + uses: Коришћења + title: Позови људе + landing_strip_html: "%{name} је корисник на %{link_to_root_path}. Можете га запратити или комуницирати са њим ако имте налог било где у федиверсу." + landing_strip_signup_html: Ако немате, можете се регистровати овде. + lists: + errors: + limit: Достигли сте лимит броја листи + media_attachments: + validations: + images_and_video: Не може да се прикачи видео на статус који већ има слике + too_many: Не може се прикачити више од 4 фајла + migrations: + acct: корисник@домен новог налога + currently_redirecting: 'Профил Вам је подешен да преусмерава на :' + proceed: Сачувај + updated_msg: Пребацивање поставки Вашег налога успешно измењено! + moderation: + title: Модерација + notification_mailer: + digest: + body: 'Ево кратак преглед шта сте пропустили на инстанци %{instance} од последње посете од %{since}:' + mention: "%{name} Вас је поменуо у:" + new_followers_summary: + few: Добили сте %{count} нова пратиоца! Сјајно! + many: Добили сте %{count} нових пратиоца! Сјајно! + one: Добили сте једног новог пратиоца! Јеее! + other: Добили сте %{count} нових пратиоца! Сјајно! + subject: + few: "%{count} нова обавештења од последње посете \U0001F418" + many: "%{count} нових обавештења од последње посете \U0001F418" + one: "1 ново обавештење од последње посете \U0001F418" + other: "%{count} нових обавештења од последње посете \U0001F418" + favourite: + body: "%{name} је поставио као омиљен Ваш статус:" + subject: "%{name} је поставио као омиљен Ваш статус" + follow: + body: "%{name} Вас је запратио!" + subject: "%{name} Вас је запратио" + follow_request: + body: "%{name} је затражио да Вас запрати" + subject: 'Пратиоци на чекању: %{name}' + mention: + body: "%{name} Вас је поменуо у:" + subject: "%{name} Вас је поменуо" + reblog: + body: "%{name} Вам је подржао(ла) статус:" + subject: "%{name} је подржао(ла) Ваш статус" + number: + human: + decimal_units: + format: "%n%u" + units: + billion: B + million: M + quadrillion: Q + thousand: K + trillion: T + unit: '' + pagination: + next: Следећи + prev: Претходни + truncate: "…" + preferences: + languages: Језици + other: Остали + publishing: Објављивање + web: Веб + push_notifications: + favourite: + title: "%{name} је ставио Ваш статус за омиљени" + follow: + title: "%{name} Вас је запратио" + group: + title: "%{count} обавештења" + mention: + action_boost: Подржи + action_expand: Прикажи још + action_favourite: Омиљени + title: "%{name} Вас је поменуо" + reblog: + title: "%{name} је подржао(ла) Ваш статус" + remote_follow: + acct: Унесите Ваш корисник@домен са кога желите да пратите + missing_resource: Не могу да нађем захтевану адресу преусмеравања за Ваш налог + proceed: Наставите да запратите + prompt: 'Запратите ће:' + sessions: + activity: Последња активност + browser: Веб читач + browsers: + alipay: Alipay + blackberry: Блекбери + chrome: Хром + edge: Microsoft Edge + firefox: Firefox + generic: Непознати веб читач + ie: Internet Explorer + micro_messenger: MicroMessenger + nokia: Nokia S40 Ovi Browser + opera: Опера + phantom_js: PhantomJS + qq: QQ Browser + safari: Сафари + uc_browser: UCBrowser + weibo: Weibo + current_session: Тренутна сесија + description: "%{browser} са %{platform}" + explanation: Ово су тренутно пријављени веб читачи на Ваш Мастодонт налог. + ip: IP + platforms: + adobe_air: Adobe Air-а + android: Андроида + blackberry: Блекберија + chrome_os: Хром ОС-а + firefox_os: Фајерфокс ОС-а + ios: iOS + linux: Линукса + mac: Mac-а + other: непознате платформе + windows: Виндоуза + windows_mobile: Виндоуз мобилног + windows_phone: Виндоуз телефона + revoke: Опозови + revoke_success: Сесија успешно опозвана + title: Сесије + settings: + authorized_apps: Ауторизоване апликације + back: Назад на Мастодонта + delete: Брисање налога + development: Развој + edit_profile: Измена профила + export: Извоз података + followers: Ауторизовани пратиоци + import: Увоз + migrate: Пребацивање налога + notifications: Обавештења + preferences: Подешавања + settings: Поставке + two_factor_authentication: Двофакторска идентификација + your_apps: Ваше апликације + statuses: + open_in_web: Отвори у вебу + over_character_limit: ограничење од %{max} карактера прекорачено + pin_errors: + limit: Већ имате прикачен највећи број тутова + ownership: Туђи тутови не могу да се прикаче + private: Тутови који нису јавни не могу да се прикаче + reblog: Подршка не може да се прикачи + show_more: Прикажи још + title: '%{name}: "%{quote}"' + visibilities: + private: Само пратиоци + private_long: Само прикажи пратиоцима + public: Јавно + public_long: Свако може да види + unlisted: Неизлистано + unlisted_long: Свако може да види, али није излистано на јавним лајнама + stream_entries: + click_to_show: Кликни да видиш + pinned: Прикачени тут + reblogged: подржано + sensitive_content: Осетљив садржај + terms: + body_html: | +

Privacy Policy

+ +

What information do we collect?

+ +

We collect information from you when you register on our site and gather data when you participate in the forum by reading, writing, and evaluating the content shared here.

+ +

When registering on our site, you may be asked to enter your name and e-mail address. You may, however, visit our site without registering. Your e-mail address will be verified by an email containing a unique link. If that link is visited, we know that you control the e-mail address.

+ +

When registered and posting, we record the IP address that the post originated from. We also may retain server logs which include the IP address of every request to our server.

+ +

What do we use your information for?

+ +

Any of the information we collect from you may be used in one of the following ways:

+ +
    +
  • To personalize your experience — your information helps us to better respond to your individual needs.
  • +
  • To improve our site — we continually strive to improve our site offerings based on the information and feedback we receive from you.
  • +
  • To improve customer service — your information helps us to more effectively respond to your customer service requests and support needs.
  • +
  • To send periodic emails — The email address you provide may be used to send you information, notifications that you request about changes to topics or in response to your user name, respond to inquiries, and/or other requests or questions.
  • +
+ +

How do we protect your information?

+ +

We implement a variety of security measures to maintain the safety of your personal information when you enter, submit, or access your personal information.

+ +

What is your data retention policy?

+ +

We will make a good faith effort to:

+ +
    +
  • Retain server logs containing the IP address of all requests to this server no more than 90 days.
  • +
  • Retain the IP addresses associated with registered users and their posts no more than 5 years.
  • +
+ +

Do we use cookies?

+ +

Yes. Cookies are small files that a site or its service provider transfers to your computer's hard drive through your Web browser (if you allow). These cookies enable the site to recognize your browser and, if you have a registered account, associate it with your registered account.

+ +

We use cookies to understand and save your preferences for future visits and compile aggregate data about site traffic and site interaction so that we can offer better site experiences and tools in the future. We may contract with third-party service providers to assist us in better understanding our site visitors. These service providers are not permitted to use the information collected on our behalf except to help us conduct and improve our business.

+ +

Do we disclose any information to outside parties?

+ +

We do not sell, trade, or otherwise transfer to outside parties your personally identifiable information. This does not include trusted third parties who assist us in operating our site, conducting our business, or servicing you, so long as those parties agree to keep this information confidential. We may also release your information when we believe release is appropriate to comply with the law, enforce our site policies, or protect ours or others rights, property, or safety. However, non-personally identifiable visitor information may be provided to other parties for marketing, advertising, or other uses.

+ +

Third party links

+ +

Occasionally, at our discretion, we may include or offer third party products or services on our site. These third party sites have separate and independent privacy policies. We therefore have no responsibility or liability for the content and activities of these linked sites. Nonetheless, we seek to protect the integrity of our site and welcome any feedback about these sites.

+ +

Children's Online Privacy Protection Act Compliance

+ +

Our site, products and services are all directed to people who are at least 13 years old. If this server is in the USA, and you are under the age of 13, per the requirements of COPPA (Children's Online Privacy Protection Act) do not use this site.

+ +

Online Privacy Policy Only

+ +

This online privacy policy applies only to information collected through our site and not to information collected offline.

+ + + +

By using our site, you consent to our web site privacy policy.

+ +

Changes to our Privacy Policy

+ +

If we decide to change our privacy policy, we will post those changes on this page.

+ +

This document is CC-BY-SA. It was last updated May 31, 2013.

+ +

Originally adapted from the Discourse privacy policy.

+ title: Услови коришћења и политика приватности инстанце %{instance} + themes: + default: Мастодонт + time: + formats: + default: "%b %d, %Y, %H:%M" + two_factor_authentication: + code_hint: Унесите код са Ваше апликације за проверу идентитета да потврдите + description_html: Ако укључите двофакторску идентификацију, мораћете да имате телефон са собом да бисте могли да се пријавите. Телефон ће онда генерисати токене за Вашу пријаву. + disable: Искључи + enable: Укључи + enabled: Двофакторска идентификација је укључена + enabled_success: Двофакторска идентификација је успешно укључена + generate_recovery_codes: Генериши кодове за опоравак + instructions_html: "Скенирајте овај QR код у Google Authenticator или некој сличној TOTP апликацији на Вашем телефону. Од сада, та апликација ће Вам генерисати токене које морате унети да бисте се пријавили." + lost_recovery_codes: Кодови за опоравак Вам омогућавају да повратите приступ налогу ако изгубите телефон. Ако изгубите кодове за опоравак, можете их регенерисати овде. Од тог тренутка, стари кодови за опоравак више не важе. + manual_instructions: 'Уколико не можете да скенирате QR код и морате га унесете ручно, ево је огољена шифра:' + recovery_codes: Направите резерву кодова за опоравак + recovery_codes_regenerated: Кодови за опоравак успешно регенерисани + recovery_instructions_html: Ако икада изгубите приступ телефону, можете искористити кодове за опоравак дате испод да повратите приступ налогу. Држите кодове за опоравак на сигурном. На пример, одштампајте их и чувајте их са осталим важним документима. + setup: Намештање + wrong_code: Унесени код није исправан! Да ли су времена на серверу и на уређају исправна? + users: + invalid_email: Адреса е-поште није исправна + invalid_otp_token: Неисправни двофакторски код + signed_in_as: 'Пријављен као:' diff --git a/config/locales/zh-TW.yml b/config/locales/zh-TW.yml index 7a66a64ca..e73dbf9cc 100644 --- a/config/locales/zh-TW.yml +++ b/config/locales/zh-TW.yml @@ -55,7 +55,7 @@ zh-TW: perform_full_suspension: 進行停權 profile_url: 個人檔案網址 public: 公開 - push_subscription_expires: PuSH 訂閱逾期 + push_subscription_expires: 推播訂閱過期 salmon_url: Salmon URL silence: 靜音 statuses: 狀態 @@ -133,12 +133,14 @@ zh-TW: forgot_password: 忘記密碼? login: 登入 logout: 登出 + migrate_account: 轉移到另一個帳號 + migrate_account_html: 想要將這個帳號指向另一個帳號可到到這裡設定。 register: 註冊 resend_confirmation: 重寄驗證信 reset_password: 重設密碼 set_new_password: 設定新密碼 authorize_follow: - error: 對不起,尋找這個跨站使用者的過程發生錯誤 + error: 對不起,搜尋遠端使用者出現錯誤 follow: 關注 title: 關注 %{acct} datetime: @@ -165,7 +167,16 @@ zh-TW: blocks: 您封鎖的使用者 csv: CSV follows: 您關注的使用者 + mutes: 您靜音的使用者 storage: 儲存空間大小 + followers: + domain: 網域 + explanation_html: 為確保個人隱私,您必須知道有哪些使用者正關注你。您的私密內容會被發送到所有您有被關注的服務站上。如果您不信任這些服務站的管理者,您可以選擇檢查或刪除您的關注者。 + followers_count: 關注者數 + lock_link: 鎖住你的帳號 + purge: 移除關注者 + unlocked_warning_html: 所有人都可以關注並檢索你的隱藏狀態。%{lock_link}以檢查或拒絕關注。 + unlocked_warning_title: 你的帳號是公開的 generic: changes_saved_msg: 已成功儲存修改 powered_by: 網站由 %{link} 開發 @@ -179,6 +190,7 @@ zh-TW: types: blocking: 您封鎖的使用者名單 following: 您關注的使用者名單 + muting: 您靜音的使用者名單 upload: 上傳 landing_strip_html: "%{name} 是一個在 %{link_to_root_path} 的使用者。只要您有任何 Mastodon 服務站、或者聯盟網站的帳號,便可以跨站關注此站使用者,或者與他們互動。" landing_strip_signup_html: 如果您沒有這些帳號,歡迎在這裡註冊。 @@ -231,15 +243,26 @@ zh-TW: missing_resource: 無法找到資源 proceed: 下一步 prompt: 您希望關注︰ + sessions: + activity: 最近活動 + browser: 瀏覽器 + current_session: 目前的 session + description: "%{platform} 上的 %{browser}" + explanation: 這些是現在正登入於你的 Mastodon 帳號的瀏覽器。 + revoke: 取消 + revoke_success: Session 取消成功。 settings: authorized_apps: 已授權應用程式 back: 回到 Mastodon + development: 開發 edit_profile: 修改個人資料 export: 匯出 + followers: 授權追蹤者 import: 匯入 + notifications: 通知 preferences: 偏好設定 settings: 設定 - two_factor_authentication: 雙因子認證 + two_factor_authentication: 兩階段認證 statuses: open_in_web: 以網頁開啟 over_character_limit: 超過了 %{max} 字的限制 @@ -257,14 +280,14 @@ zh-TW: default: "%Y年%-m月%d日 %H:%M" two_factor_authentication: code_hint: 請輸入您認證器產生的代碼,以進行認證 - description_html: 當您啟用雙因子認證後,您登入時將需要使您手機、或其他種類認證器產生的代碼。 + description_html: 啟用兩階段認證後,登入時將需要使手機、或其他種類認證器產生的代碼。 disable: 停用 enable: 啟用 - enabled_success: 已成功啟用雙因子認證 - instructions_html: "請用您手機的認證器應用程式(如 Google Authenticator、Authy),掃描這裡的 QR 圖形碼。在雙因子認證啟用後,您登入時將須要使用此應用程式產生的認證碼。" + enabled_success: 已成功啟用兩階段認證 + instructions_html: "請用您手機的認證器應用程式(如 Google Authenticator、Authy),掃描這裡的 QR 圖形碼。在兩階段認證啟用後,您登入時將須要使用此應用程式產生的認證碼。" manual_instructions: 如果您無法掃描 QR 圖形碼,請手動輸入︰ setup: 設定 wrong_code: 您輸入的認證碼並不正確!可能伺服器時間和您手機不一致,請檢查您手機的時間,或與本站管理員聯絡。 users: invalid_email: 信箱地址格式不正確 - invalid_otp_token: 雙因子認證碼不正確 + invalid_otp_token: 兩階段認證碼不正確 diff --git a/config/routes.rb b/config/routes.rb index 467849c03..21ad0a93f 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -54,7 +54,8 @@ Rails.application.routes.draw do resources :followers, only: [:index], controller: :follower_accounts resources :following, only: [:index], controller: :following_accounts - resource :follow, only: [:create], controller: :account_follow + resources :follows, only: [:show], module: :activitypub + resource :follow, only: [:create], controller: :account_follow, as: :follows resource :unfollow, only: [:create], controller: :account_unfollow resource :outbox, only: [:show], module: :activitypub resource :inbox, only: [:create], module: :activitypub @@ -241,7 +242,11 @@ Rails.application.routes.draw do resources :apps, only: [:create] - resource :instance, only: [:show] + resource :instance, only: [:show] do + resources :peers, only: [:index], controller: 'instances/peers' + resource :activity, only: [:show], controller: 'instances/activity' + end + resource :domain_blocks, only: [:show, :create, :destroy] resources :follow_requests, only: [:index] do diff --git a/config/settings.yml b/config/settings.yml index f03a32e50..4a2519464 100644 --- a/config/settings.yml +++ b/config/settings.yml @@ -47,7 +47,8 @@ defaults: &defaults - webmaster - administrator bootstrap_timeline_accounts: '' - + activity_api_enabled: true + peers_api_enabled: true development: <<: *defaults diff --git a/config/webpack/translationRunner.js b/config/webpack/translationRunner.js index d616c7839..e6543fbb7 100644 --- a/config/webpack/translationRunner.js +++ b/config/webpack/translationRunner.js @@ -2,7 +2,7 @@ const fs = require('fs'); const path = require('path'); const { default: manageTranslations } = require('react-intl-translations-manager'); -const RFC5646_REGEXP = /^[a-z]{2,3}(?:|-[A-Z]+)$/; +const RFC5646_REGEXP = /^[a-z]{2,3}(?:-(?:x|[A-Za-z]{2,4}))*$/; const rootDirectory = path.resolve(__dirname, '..', '..'); const translationsDirectory = path.resolve(rootDirectory, 'app', 'javascript', 'mastodon', 'locales'); diff --git a/db/migrate/20171129172043_add_index_on_stream_entries.rb b/db/migrate/20171129172043_add_index_on_stream_entries.rb index 478530c7f..181c4f288 100644 --- a/db/migrate/20171129172043_add_index_on_stream_entries.rb +++ b/db/migrate/20171129172043_add_index_on_stream_entries.rb @@ -1,6 +1,7 @@ class AddIndexOnStreamEntries < ActiveRecord::Migration[5.1] + disable_ddl_transaction! + def change - commit_db_transaction add_index :stream_entries, [:account_id, :activity_type, :id], algorithm: :concurrently remove_index :stream_entries, name: :index_stream_entries_on_account_id end diff --git a/db/migrate/20171226094803_more_faster_index_on_notifications.rb b/db/migrate/20171226094803_more_faster_index_on_notifications.rb new file mode 100644 index 000000000..0273a4e7c --- /dev/null +++ b/db/migrate/20171226094803_more_faster_index_on_notifications.rb @@ -0,0 +1,8 @@ +class MoreFasterIndexOnNotifications < ActiveRecord::Migration[5.1] + disable_ddl_transaction! + + def change + add_index :notifications, [:account_id, :id], order: { id: :desc }, algorithm: :concurrently + remove_index :notifications, name: :index_notifications_on_id_and_account_id_and_activity_type + end +end diff --git a/db/schema.rb b/db/schema.rb index 08337a4e1..136d596d1 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -10,7 +10,7 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20171212195226) do +ActiveRecord::Schema.define(version: 20171226094803) do # These are extensions that must be enabled in order to support this database enable_extension "plpgsql" @@ -260,8 +260,8 @@ ActiveRecord::Schema.define(version: 20171212195226) do t.bigint "account_id" t.bigint "from_account_id" t.index ["account_id", "activity_id", "activity_type"], name: "account_activity", unique: true + t.index ["account_id", "id"], name: "index_notifications_on_account_id_and_id", order: { id: :desc } t.index ["activity_id", "activity_type"], name: "index_notifications_on_activity_id_and_activity_type" - t.index ["id", "account_id", "activity_type"], name: "index_notifications_on_id_and_account_id_and_activity_type", order: { id: :desc } end create_table "oauth_access_grants", force: :cascade do |t| diff --git a/lib/mastodon/version.rb b/lib/mastodon/version.rb index efd133f29..bd23ab1d1 100644 --- a/lib/mastodon/version.rb +++ b/lib/mastodon/version.rb @@ -13,11 +13,11 @@ module Mastodon end def patch - 0 + 2 end def pre - '' + nil end def flags diff --git a/lib/tasks/mastodon.rake b/lib/tasks/mastodon.rake index 0f2cc536a..33969d470 100644 --- a/lib/tasks/mastodon.rake +++ b/lib/tasks/mastodon.rake @@ -1,5 +1,8 @@ # frozen_string_literal: true +require 'optparse' +require 'colorize' + namespace :mastodon do desc 'Execute daily tasks (deprecated)' task :daily do @@ -338,5 +341,75 @@ namespace :mastodon do PreviewCard.where(embed_url: '', type: :photo).delete_all LinkCrawlWorker.push_bulk status_ids end + + desc 'Check every known remote account and delete those that no longer exist in origin' + task purge_removed_accounts: :environment do + prepare_for_options! + + options = {} + + OptionParser.new do |opts| + opts.banner = 'Usage: rails mastodon:maintenance:purge_removed_accounts [options]' + + opts.on('-f', '--force', 'Remove all encountered accounts without asking for confirmation') do + options[:force] = true + end + + opts.on('-h', '--help', 'Display this message') do + puts opts + exit + end + end.parse! + + disable_log_stdout! + + total = Account.remote.where(protocol: :activitypub).count + progress_bar = ProgressBar.create(total: total, format: '%c/%C |%w>%i| %e') + + Account.remote.where(protocol: :activitypub).partitioned.find_each do |account| + progress_bar.increment + + begin + res = Request.new(:head, account.uri).perform + rescue StandardError + # This could happen due to network timeout, DNS timeout, wrong SSL cert, etc, + # which should probably not lead to perceiving the account as deleted, so + # just skip till next time + next + end + + if [404, 410].include?(res.code) + if options[:force] + account.destroy + else + progress_bar.pause + progress_bar.clear + print "\nIt seems like #{account.acct} no longer exists. Purge the account from the database? [Y/n]: ".colorize(:yellow) + confirm = STDIN.gets.chomp + puts '' + progress_bar.resume + + if confirm.casecmp('n').zero? + next + else + account.destroy + end + end + end + end + end end end + +def disable_log_stdout! + dev_null = Logger.new('/dev/null') + + Rails.logger = dev_null + ActiveRecord::Base.logger = dev_null + HttpLog.configuration.logger = dev_null + Paperclip.options[:log] = false +end + +def prepare_for_options! + 2.times { ARGV.shift } +end diff --git a/package.json b/package.json index ef4edcbcf..4c5dceabb 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,9 @@ { "name": "mastodon", "license": "AGPL-3.0", + "engines": { + "node": ">=6" + }, "scripts": { "postversion": "git push --tags", "build:development": "cross-env RAILS_ENV=development ./bin/webpack", diff --git a/spec/controllers/activitypub/follows_controller_spec.rb b/spec/controllers/activitypub/follows_controller_spec.rb new file mode 100644 index 000000000..6026cd353 --- /dev/null +++ b/spec/controllers/activitypub/follows_controller_spec.rb @@ -0,0 +1,43 @@ +# frozen_string_literal: true + +require 'rails_helper' + +describe ActivityPub::FollowsController, type: :controller do + let(:follow_request) { Fabricate(:follow_request, account: account) } + + render_views + + context 'with local account' do + let(:account) { Fabricate(:account, domain: nil) } + + it 'returns follow request' do + signed_request = Request.new(:get, account_follow_url(account, follow_request)) + signed_request.on_behalf_of(follow_request.target_account) + request.headers.merge! signed_request.headers + + get :show, params: { id: follow_request, account_username: account.username } + + expect(body_as_json[:id]).to eq ActivityPub::TagManager.instance.uri_for(follow_request) + expect(response).to have_http_status :success + end + + it 'returns http 404 without signature' do + get :show, params: { id: follow_request, account_username: account.username } + expect(response).to have_http_status 404 + end + end + + context 'with remote account' do + let(:account) { Fabricate(:account, domain: Faker::Internet.domain_name) } + + it 'returns http 404' do + signed_request = Request.new(:get, account_follow_url(account, follow_request)) + signed_request.on_behalf_of(follow_request.target_account) + request.headers.merge! signed_request.headers + + get :show, params: { id: follow_request, account_username: account.username } + + expect(response).to have_http_status 404 + end + end +end diff --git a/spec/controllers/auth/confirmations_controller_spec.rb b/spec/controllers/auth/confirmations_controller_spec.rb index 2ec36c060..80a06c43a 100644 --- a/spec/controllers/auth/confirmations_controller_spec.rb +++ b/spec/controllers/auth/confirmations_controller_spec.rb @@ -12,20 +12,40 @@ describe Auth::ConfirmationsController, type: :controller do end describe 'GET #show' do - let!(:user) { Fabricate(:user, confirmation_token: 'foobar', confirmed_at: nil) } + context 'when user is unconfirmed' do + let!(:user) { Fabricate(:user, confirmation_token: 'foobar', confirmed_at: nil) } - before do - allow(BootstrapTimelineWorker).to receive(:perform_async) - @request.env['devise.mapping'] = Devise.mappings[:user] - get :show, params: { confirmation_token: 'foobar' } + before do + allow(BootstrapTimelineWorker).to receive(:perform_async) + @request.env['devise.mapping'] = Devise.mappings[:user] + get :show, params: { confirmation_token: 'foobar' } + end + + it 'redirects to login' do + expect(response).to redirect_to(new_user_session_path) + end + + it 'queues up bootstrapping of home timeline' do + expect(BootstrapTimelineWorker).to have_received(:perform_async).with(user.account_id) + end end - it 'redirects to login' do - expect(response).to redirect_to(new_user_session_path) - end + context 'when user is updating email' do + let!(:user) { Fabricate(:user, confirmation_token: 'foobar', unconfirmed_email: 'new-email@example.com') } - it 'queues up bootstrapping of home timeline' do - expect(BootstrapTimelineWorker).to have_received(:perform_async).with(user.account_id) + before do + allow(BootstrapTimelineWorker).to receive(:perform_async) + @request.env['devise.mapping'] = Devise.mappings[:user] + get :show, params: { confirmation_token: 'foobar' } + end + + it 'redirects to login' do + expect(response).to redirect_to(new_user_session_path) + end + + it 'does not queue up bootstrapping of home timeline' do + expect(BootstrapTimelineWorker).to_not have_received(:perform_async) + end end end end diff --git a/spec/fixtures/requests/oembed_json_xml.html b/spec/fixtures/requests/oembed_json_xml.html index b5fc9bed0..8afd8e997 100644 --- a/spec/fixtures/requests/oembed_json_xml.html +++ b/spec/fixtures/requests/oembed_json_xml.html @@ -1,8 +1,14 @@ + - + diff --git a/spec/fixtures/requests/oembed_xml.html b/spec/fixtures/requests/oembed_xml.html index 5d7633e71..bdfcca170 100644 --- a/spec/fixtures/requests/oembed_xml.html +++ b/spec/fixtures/requests/oembed_xml.html @@ -1,7 +1,13 @@ - + + diff --git a/spec/lib/activitypub/activity/accept_spec.rb b/spec/lib/activitypub/activity/accept_spec.rb index 6503c83e3..9f43be35d 100644 --- a/spec/lib/activitypub/activity/accept_spec.rb +++ b/spec/lib/activitypub/activity/accept_spec.rb @@ -3,36 +3,49 @@ require 'rails_helper' RSpec.describe ActivityPub::Activity::Accept do let(:sender) { Fabricate(:account) } let(:recipient) { Fabricate(:account) } - - let(:json) do - { - '@context': 'https://www.w3.org/ns/activitystreams', - id: 'foo', - type: 'Accept', - actor: ActivityPub::TagManager.instance.uri_for(sender), - object: { - id: 'bar', - type: 'Follow', - actor: ActivityPub::TagManager.instance.uri_for(recipient), - object: ActivityPub::TagManager.instance.uri_for(sender), - }, - }.with_indifferent_access - end + let!(:follow_request) { Fabricate(:follow_request, account: recipient, target_account: sender) } describe '#perform' do subject { described_class.new(json, sender) } before do - Fabricate(:follow_request, account: recipient, target_account: sender) subject.perform end - it 'creates a follow relationship' do - expect(recipient.following?(sender)).to be true + context 'with concerete object representation' do + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Accept', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: { + type: 'Follow', + actor: ActivityPub::TagManager.instance.uri_for(recipient), + object: ActivityPub::TagManager.instance.uri_for(sender), + }, + }.with_indifferent_access + end + + it 'creates a follow relationship' do + expect(recipient.following?(sender)).to be true + end end - it 'removes the follow request' do - expect(recipient.requested?(sender)).to be false + context 'with object represented by id' do + let(:json) do + { + '@context': 'https://www.w3.org/ns/activitystreams', + id: 'foo', + type: 'Accept', + actor: ActivityPub::TagManager.instance.uri_for(sender), + object: ActivityPub::TagManager.instance.uri_for(follow_request), + }.with_indifferent_access + end + + it 'creates a follow relationship' do + expect(recipient.following?(sender)).to be true + end end end end diff --git a/spec/lib/formatter_spec.rb b/spec/lib/formatter_spec.rb index 71b6b78d2..67fbfe92d 100644 --- a/spec/lib/formatter_spec.rb +++ b/spec/lib/formatter_spec.rb @@ -17,7 +17,7 @@ RSpec.describe Formatter do let(:text) { 'http://google.com' } it 'has valid URL' do - is_expected.to include 'href="http://google.com/"' + is_expected.to include 'href="http://google.com"' end end @@ -25,7 +25,7 @@ RSpec.describe Formatter do let(:text) { 'https://nic.みんな/' } it 'has valid URL' do - is_expected.to include 'href="https://nic.xn--q9jyb4c/"' + is_expected.to include 'href="https://nic.みんな/"' end it 'has display URL' do @@ -53,7 +53,7 @@ RSpec.describe Formatter do let(:text) { 'http://www.google.com!' } it 'has valid URL' do - is_expected.to include 'href="http://www.google.com/"' + is_expected.to include 'href="http://www.google.com"' end end @@ -61,7 +61,7 @@ RSpec.describe Formatter do let(:text) { "http://www.google.com'" } it 'has valid URL' do - is_expected.to include 'href="http://www.google.com/"' + is_expected.to include 'href="http://www.google.com"' end end @@ -69,7 +69,7 @@ RSpec.describe Formatter do let(:text) { 'http://www.google.com>' } it 'has valid URL' do - is_expected.to include 'href="http://www.google.com/"' + is_expected.to include 'href="http://www.google.com"' end end @@ -93,7 +93,7 @@ RSpec.describe Formatter do let(:text) { 'https://ja.wikipedia.org/wiki/日本' } it 'has valid URL' do - is_expected.to include 'href="https://ja.wikipedia.org/wiki/%E6%97%A5%E6%9C%AC"' + is_expected.to include 'href="https://ja.wikipedia.org/wiki/日本"' end end @@ -101,7 +101,7 @@ RSpec.describe Formatter do let(:text) { 'https://ko.wikipedia.org/wiki/대한민국' } it 'has valid URL' do - is_expected.to include 'href="https://ko.wikipedia.org/wiki/%EB%8C%80%ED%95%9C%EB%AF%BC%EA%B5%AD"' + is_expected.to include 'href="https://ko.wikipedia.org/wiki/대한민국"' end end @@ -109,7 +109,7 @@ RSpec.describe Formatter do let(:text) { 'https://baike.baidu.com/item/中华人民共和国' } it 'has valid URL' do - is_expected.to include 'href="https://baike.baidu.com/item/%E4%B8%AD%E5%8D%8E%E4%BA%BA%E6%B0%91%E5%85%B1%E5%92%8C%E5%9B%BD"' + is_expected.to include 'href="https://baike.baidu.com/item/中华人民共和国"' end end @@ -117,7 +117,7 @@ RSpec.describe Formatter do let(:text) { 'https://zh.wikipedia.org/wiki/臺灣' } it 'has valid URL' do - is_expected.to include 'href="https://zh.wikipedia.org/wiki/%E8%87%BA%E7%81%A3"' + is_expected.to include 'href="https://zh.wikipedia.org/wiki/臺灣"' end end @@ -332,7 +332,7 @@ RSpec.describe Formatter do end context 'contains malicious classes' do - let(:text) { 'Show more' } + let(:text) { 'Show more' } it 'strips malicious classes' do is_expected.to_not include 'status__content__spoiler-link' diff --git a/spec/mailers/user_mailer_spec.rb b/spec/mailers/user_mailer_spec.rb index 1f6d44015..9f17993e0 100644 --- a/spec/mailers/user_mailer_spec.rb +++ b/spec/mailers/user_mailer_spec.rb @@ -33,6 +33,20 @@ describe UserMailer, type: :mailer do instance: Rails.configuration.x.local_domain end + describe 'reconfirmation_instructions' do + let(:mail) { UserMailer.confirmation_instructions(receiver, 'spec') } + + it 'renders reconfirmation instructions' do + receiver.update!(email: 'new-email@example.com', locale: nil) + expect(mail.body.encoded).to include 'new-email@example.com' + expect(mail.body.encoded).to include 'spec' + expect(mail.body.encoded).to include Rails.configuration.x.local_domain + expect(mail.subject).to eq I18n.t('devise.mailer.reconfirmation_instructions.subject', + instance: Rails.configuration.x.local_domain, + locale: I18n.default_locale) + end + end + describe 'reset_password_instructions' do let(:mail) { UserMailer.reset_password_instructions(receiver, 'spec') } @@ -57,4 +71,16 @@ describe UserMailer, type: :mailer do include_examples 'localized subject', 'devise.mailer.password_change.subject' end + + describe 'email_changed' do + let(:mail) { UserMailer.email_changed(receiver) } + + it 'renders email change notification' do + receiver.update!(locale: nil) + expect(mail.body.encoded).to include receiver.email + end + + include_examples 'localized subject', + 'devise.mailer.email_changed.subject' + end end diff --git a/spec/models/follow_request_spec.rb b/spec/models/follow_request_spec.rb index 59893a14f..7e10631a3 100644 --- a/spec/models/follow_request_spec.rb +++ b/spec/models/follow_request_spec.rb @@ -27,4 +27,12 @@ RSpec.describe FollowRequest, type: :model do expect(follow_request.account.muting_reblogs?(target)).to be true end end + + describe '#object_type' do + let(:follow_request) { Fabricate(:follow_request) } + + it 'equals to :follow' do + expect(follow_request.object_type).to eq :follow + end + end end diff --git a/spec/models/user_spec.rb b/spec/models/user_spec.rb index 5ed7ed88b..8171c939a 100644 --- a/spec/models/user_spec.rb +++ b/spec/models/user_spec.rb @@ -148,6 +148,14 @@ RSpec.describe User, type: :model do end end + describe '#confirm' do + it 'sets email to unconfirmed_email' do + user = Fabricate.build(:user, confirmed_at: Time.now.utc, unconfirmed_email: 'new-email@example.com') + user.confirm + expect(user.email).to eq 'new-email@example.com' + end + end + describe '#disable_two_factor!' do it 'saves false for otp_required_for_login' do user = Fabricate.build(:user, otp_required_for_login: true) diff --git a/spec/rails_helper.rb b/spec/rails_helper.rb index 4f7399505..67c6b9205 100644 --- a/spec/rails_helper.rb +++ b/spec/rails_helper.rb @@ -46,7 +46,7 @@ RSpec.configure do |config| config.include ActiveSupport::Testing::TimeHelpers config.before :each, type: :feature do - https = ENV['LOCAL_HTTPS'] == 'true' + https = Rails.env.production? || ENV['LOCAL_HTTPS'] == 'true' Capybara.app_host = "http#{https ? 's' : ''}://#{ENV.fetch('LOCAL_DOMAIN')}" end