Merge tag 'v2.1.2' into kosmos
This commit is contained in:
commit
42cea8a076
@ -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
|
||||
|
3
Gemfile
3
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'
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
22
app/controllers/activitypub/follows_controller.rb
Normal file
22
app/controllers/activitypub/follows_controller.rb
Normal file
@ -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
|
@ -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
|
||||
|
@ -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(
|
||||
|
36
app/controllers/api/v1/instances/activity_controller.rb
Normal file
36
app/controllers/api/v1/instances/activity_controller.rb
Normal file
@ -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
|
17
app/controllers/api/v1/instances/peers_controller.rb
Normal file
17
app/controllers/api/v1/instances/peers_controller.rb
Normal file
@ -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
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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?
|
||||
|
@ -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
|
||||
|
@ -38,4 +38,8 @@ class RemoteFollowController < ApplicationController
|
||||
def suspended_account?
|
||||
@account.suspended?
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'modal-layout'
|
||||
end
|
||||
end
|
||||
|
@ -25,6 +25,6 @@ class SharesController < ApplicationController
|
||||
end
|
||||
|
||||
def set_body_classes
|
||||
@body_classes = 'compose-standalone'
|
||||
@body_classes = 'modal-layout compose-standalone'
|
||||
end
|
||||
end
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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?
|
||||
|
@ -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',
|
||||
|
Binary file not shown.
Before Width: | Height: | Size: 34 KiB After Width: | Height: | Size: 32 KiB |
BIN
app/javascript/images/wave-compose-standalone.png
Normal file
BIN
app/javascript/images/wave-compose-standalone.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.8 KiB |
BIN
app/javascript/images/wave-drawer.png
Normal file
BIN
app/javascript/images/wave-drawer.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 3.2 KiB |
BIN
app/javascript/images/wave-modal.png
Normal file
BIN
app/javascript/images/wave-modal.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 5.1 KiB |
@ -31,7 +31,7 @@ const fetchRelatedRelationships = (dispatch, notifications) => {
|
||||
|
||||
const unescapeHTML = (html) => {
|
||||
const wrapper = document.createElement('div');
|
||||
html = html.replace(/<br \/>|<br>|\n/, ' ');
|
||||
html = html.replace(/<br \/>|<br>|\n/g, ' ');
|
||||
wrapper.innerHTML = html;
|
||||
return wrapper.textContent;
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
23
app/javascript/mastodon/actions/push_notifications/index.js
Normal file
23
app/javascript/mastodon/actions/push_notifications/index.js
Normal file
@ -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());
|
||||
};
|
||||
}
|
149
app/javascript/mastodon/actions/push_notifications/registerer.js
Normal file
149
app/javascript/mastodon/actions/push_notifications/registerer.js
Normal file
@ -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);
|
||||
}
|
||||
});
|
||||
};
|
||||
}
|
34
app/javascript/mastodon/actions/push_notifications/setter.js
Normal file
34
app/javascript/mastodon/actions/push_notifications/setter.js
Normal file
@ -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,
|
||||
});
|
||||
};
|
||||
}
|
@ -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 });
|
||||
|
@ -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,
|
||||
};
|
||||
|
@ -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 <Warning message={<FormattedMessage id='compose_form.lock_disclaimer' defaultMessage='Your account is not {locked}. Anyone can follow you to view your follower-only posts.' values={{ locked: <a href='/settings/profile'><FormattedMessage id='compose_form.lock_disclaimer.lock' defaultMessage='locked' /></a> }} />} />;
|
||||
}
|
||||
if (hashtagWarning) {
|
||||
return <Warning message={<FormattedMessage id='compose_form.hashtag_warning' defaultMessage="This toot won't be listed under any hashtag as it is unlisted. Only public toots can be searched by hashtag." />} />;
|
||||
}
|
||||
|
||||
return null;
|
||||
};
|
||||
|
||||
WarningWrapper.propTypes = {
|
||||
needsLockWarning: PropTypes.bool,
|
||||
hashtagWarning: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default connect(mapStateToProps)(WarningWrapper);
|
||||
|
@ -94,6 +94,7 @@ export default class Compose extends React.PureComponent {
|
||||
<div className='drawer__inner' onFocus={this.onFocus}>
|
||||
<NavigationContainer onClose={this.onBlur} />
|
||||
<ComposeFormContainer />
|
||||
<div className='mastodon' />
|
||||
</div>
|
||||
|
||||
<Motion defaultStyle={{ x: -100 }} style={{ x: spring(showSearch ? 0 : -100, { stiffness: 210, damping: 20 }) }}>
|
||||
|
@ -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(
|
||||
<ColumnLink key='4' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />,
|
||||
<ColumnLink key='5' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />,
|
||||
<ColumnLink key='9' icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' />,
|
||||
]);
|
||||
<ColumnLink key='5' icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' />
|
||||
);
|
||||
|
||||
if (myAccount.get('locked')) {
|
||||
navItems.push(<ColumnLink key='6' icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />);
|
||||
}
|
||||
|
||||
navItems = navItems.concat([
|
||||
<ColumnLink key='7' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />,
|
||||
<ColumnLink key='8' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />,
|
||||
<ColumnLink key='10' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' hideOnMobile />,
|
||||
]);
|
||||
if (multiColumn) {
|
||||
navItems.push(<ColumnLink key='7' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />);
|
||||
}
|
||||
|
||||
navItems.push(<ColumnLink key='8' icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />);
|
||||
|
||||
return (
|
||||
<Column icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile>
|
||||
@ -90,24 +89,24 @@ export default class GettingStarted extends ImmutablePureComponent {
|
||||
<ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)} />
|
||||
{navItems}
|
||||
<ColumnSubheading text={intl.formatMessage(messages.settings_subheading)} />
|
||||
<ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
|
||||
<ColumnLink icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />
|
||||
<ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
|
||||
<ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
|
||||
<ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />
|
||||
<ColumnLink icon='sign-out' text={intl.formatMessage(messages.sign_out)} href='/auth/sign_out' method='delete' />
|
||||
</div>
|
||||
|
||||
<div className='getting-started__footer scrollable optionally-scrollable'>
|
||||
<div className='static-content getting-started'>
|
||||
<p>
|
||||
<a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/FAQ.md' rel='noopener' target='_blank'><FormattedMessage id='getting_started.faq' defaultMessage='FAQ' /></a> • <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/User-guide.md' rel='noopener' target='_blank'><FormattedMessage id='getting_started.userguide' defaultMessage='User Guide' /></a> • <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md' rel='noopener' target='_blank'><FormattedMessage id='getting_started.appsshort' defaultMessage='Apps' /></a>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='getting_started.open_source_notice'
|
||||
defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.'
|
||||
values={{ github: <a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>tootsuite/mastodon</a> }}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
<div className='static-content getting-started'>
|
||||
<p>
|
||||
<a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/FAQ.md' rel='noopener' target='_blank'><FormattedMessage id='getting_started.faq' defaultMessage='FAQ' /></a> • <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/User-guide.md' rel='noopener' target='_blank'><FormattedMessage id='getting_started.userguide' defaultMessage='User Guide' /></a> • <a href='https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md' rel='noopener' target='_blank'><FormattedMessage id='getting_started.appsshort' defaultMessage='Apps' /></a>
|
||||
</p>
|
||||
<p>
|
||||
<FormattedMessage
|
||||
id='getting_started.open_source_notice'
|
||||
defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}.'
|
||||
values={{ github: <a href='https://github.com/tootsuite/mastodon' rel='noopener' target='_blank'>tootsuite/mastodon</a> }}
|
||||
/>
|
||||
</p>
|
||||
</div>
|
||||
</Column>
|
||||
);
|
||||
|
@ -27,11 +27,11 @@ export default class ColumnSettings extends React.PureComponent {
|
||||
<span className='column-settings__section'><FormattedMessage id='home.column_settings.basic' defaultMessage='Basic' /></span>
|
||||
|
||||
<div className='column-settings__row'>
|
||||
<SettingToggle prefix='home_timeline' settings={settings} settingKey={['shows', 'reblog']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_reblogs' defaultMessage='Show boosts' />} />
|
||||
<SettingToggle prefix='home_timeline' settings={settings} settingPath={['shows', 'reblog']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_reblogs' defaultMessage='Show boosts' />} />
|
||||
</div>
|
||||
|
||||
<div className='column-settings__row'>
|
||||
<SettingToggle prefix='home_timeline' settings={settings} settingKey={['shows', 'reply']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_replies' defaultMessage='Show replies' />} />
|
||||
<SettingToggle prefix='home_timeline' settings={settings} settingPath={['shows', 'reply']} onChange={onChange} label={<FormattedMessage id='home.column_settings.show_replies' defaultMessage='Show replies' />} />
|
||||
</div>
|
||||
|
||||
<span className='column-settings__section'><FormattedMessage id='home.column_settings.advanced' defaultMessage='Advanced' /></span>
|
||||
|
@ -33,59 +33,59 @@ export default class KeyboardShortcuts extends ImmutablePureComponent {
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><code>r</code></td>
|
||||
<td><kbd>r</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.reply' defaultMessage='to reply' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>m</code></td>
|
||||
<td><kbd>m</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.mention' defaultMessage='to mention author' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>f</code></td>
|
||||
<td><kbd>f</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.favourite' defaultMessage='to favourite' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>b</code></td>
|
||||
<td><kbd>b</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.boost' defaultMessage='to boost' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>enter</code></td>
|
||||
<td><kbd>enter</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.enter' defaultMessage='to open status' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>up</code></td>
|
||||
<td><kbd>up</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.up' defaultMessage='to move up in the list' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>down</code></td>
|
||||
<td><kbd>down</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.down' defaultMessage='to move down in the list' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>1</code>-<code>9</code></td>
|
||||
<td><kbd>1</kbd>-<kbd>9</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.column' defaultMessage='to focus a status in one of the columns' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>n</code></td>
|
||||
<td><kbd>n</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.compose' defaultMessage='to focus the compose textarea' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>alt</code>+<code>n</code></td>
|
||||
<td><kbd>alt</kbd>+<kbd>n</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.toot' defaultMessage='to start a brand new toot' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>backspace</code></td>
|
||||
<td><kbd>backspace</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.back' defaultMessage='to navigate back' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>s</code></td>
|
||||
<td><kbd>s</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.search' defaultMessage='to focus search' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>esc</code></td>
|
||||
<td><kbd>esc</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.unfocus' defaultMessage='to un-focus compose textarea/search' /></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td><code>?</code></td>
|
||||
<td><kbd>?</kbd></td>
|
||||
<td><FormattedMessage id='keyboard_shortcuts.legend' defaultMessage='to display this legend' /></td>
|
||||
</tr>
|
||||
</tbody>
|
||||
|
@ -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 {
|
||||
<span id='notifications-follow' className='column-settings__section'><FormattedMessage id='notifications.column_settings.follow' defaultMessage='New followers:' /></span>
|
||||
|
||||
<div className='column-settings__row'>
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'follow']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'follow']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'follow']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'follow']} onChange={onChange} label={soundStr} />
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'follow']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'follow']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'follow']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'follow']} onChange={onChange} label={soundStr} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -51,10 +50,10 @@ export default class ColumnSettings extends React.PureComponent {
|
||||
<span id='notifications-favourite' className='column-settings__section'><FormattedMessage id='notifications.column_settings.favourite' defaultMessage='Favourites:' /></span>
|
||||
|
||||
<div className='column-settings__row'>
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'favourite']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'favourite']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'favourite']} onChange={onChange} label={soundStr} />
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'favourite']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'favourite']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'favourite']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'favourite']} onChange={onChange} label={soundStr} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -62,10 +61,10 @@ export default class ColumnSettings extends React.PureComponent {
|
||||
<span id='notifications-mention' className='column-settings__section'><FormattedMessage id='notifications.column_settings.mention' defaultMessage='Mentions:' /></span>
|
||||
|
||||
<div className='column-settings__row'>
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'mention']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'mention']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'mention']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'mention']} onChange={onChange} label={soundStr} />
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'mention']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'mention']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'mention']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'mention']} onChange={onChange} label={soundStr} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@ -73,10 +72,10 @@ export default class ColumnSettings extends React.PureComponent {
|
||||
<span id='notifications-reblog' className='column-settings__section'><FormattedMessage id='notifications.column_settings.reblog' defaultMessage='Boosts:' /></span>
|
||||
|
||||
<div className='column-settings__row'>
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingKey={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingKey={['alerts', 'reblog']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingKey={['shows', 'reblog']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingKey={['sounds', 'reblog']} onChange={onChange} label={soundStr} />
|
||||
<SettingToggle prefix='notifications_desktop' settings={settings} settingPath={['alerts', 'reblog']} onChange={onChange} label={alertStr} />
|
||||
{showPushSettings && <SettingToggle prefix='notifications_push' settings={pushSettings} settingPath={['alerts', 'reblog']} meta={pushMeta} onChange={this.onPushChange} label={pushStr} />}
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['shows', 'reblog']} onChange={onChange} label={showStr} />
|
||||
<SettingToggle prefix='notifications' settings={settings} settingPath={['sounds', 'reblog']} onChange={onChange} label={soundStr} />
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
@ -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 (
|
||||
<div className='setting-toggle'>
|
||||
<Toggle id={id} checked={settings.getIn(settingKey)} onChange={this.onChange} onKeyDown={this.onKeyDown} />
|
||||
<Toggle id={id} checked={settings.getIn(settingPath)} onChange={this.onChange} onKeyDown={this.onKeyDown} />
|
||||
<label htmlFor={id} className='setting-toggle__label'>{label}</label>
|
||||
{meta && <span className='setting-meta__label'>{meta}</span>}
|
||||
</div>
|
||||
|
@ -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),
|
||||
|
@ -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 });
|
||||
}
|
||||
|
||||
|
@ -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: <FormattedMessage id='confirmations.block.message' defaultMessage='Are you sure you want to block {name}?' values={{ name: <strong>@{account.get('acct')}</strong> }} />,
|
||||
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}
|
||||
|
@ -26,7 +26,6 @@ ColumnLink.propTypes = {
|
||||
to: PropTypes.string,
|
||||
href: PropTypes.string,
|
||||
method: PropTypes.string,
|
||||
hideOnMobile: PropTypes.bool,
|
||||
};
|
||||
|
||||
export default ColumnLink;
|
||||
|
@ -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": "تدبيس على الملف الشخصي",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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"
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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": "نوشتهٔ ثابت نمایه",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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": "לקבע באודות",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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": "プロフィールに固定表示",
|
||||
|
@ -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": "음소거 해제"
|
||||
}
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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": "Закрепить в профиле",
|
||||
|
262
app/javascript/mastodon/locales/sk.json
Normal file
262
app/javascript/mastodon/locales/sk.json
Normal file
@ -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"
|
||||
}
|
262
app/javascript/mastodon/locales/sr-Latn.json
Normal file
262
app/javascript/mastodon/locales/sr-Latn.json
Normal file
@ -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"
|
||||
}
|
262
app/javascript/mastodon/locales/sr.json
Normal file
262
app/javascript/mastodon/locales/sr.json
Normal file
@ -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": "Врати звук"
|
||||
}
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
@ -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",
|
||||
|
2
app/javascript/mastodon/locales/whitelist_sk.json
Normal file
2
app/javascript/mastodon/locales/whitelist_sk.json
Normal file
@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
2
app/javascript/mastodon/locales/whitelist_sr-Latn.json
Normal file
2
app/javascript/mastodon/locales/whitelist_sr-Latn.json
Normal file
@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
2
app/javascript/mastodon/locales/whitelist_sr.json
Normal file
2
app/javascript/mastodon/locales/whitelist_sr.json
Normal file
@ -0,0 +1,2 @@
|
||||
[
|
||||
]
|
@ -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": "在个人资料页面置顶",
|
||||
|
@ -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": "置頂到資料頁",
|
||||
|
@ -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": "置頂到個人資訊頁",
|
||||
|
@ -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()');
|
||||
});
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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.');
|
||||
}
|
||||
}
|
@ -6,6 +6,7 @@
|
||||
|
||||
@import 'mastodon/reset';
|
||||
@import 'mastodon/basics';
|
||||
@import 'mastodon/modal';
|
||||
@import 'mastodon/containers';
|
||||
@import 'mastodon/lists';
|
||||
@import 'mastodon/footer';
|
||||
|
@ -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;
|
||||
}
|
||||
|
||||
|
@ -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 {
|
||||
|
20
app/javascript/styles/mastodon/modal.scss
Normal file
20
app/javascript/styles/mastodon/modal.scss
Normal file
@ -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;
|
||||
}
|
||||
}
|
31
app/lib/activity_tracker.rb
Normal file
31
app/lib/activity_tracker.rb
Normal file
@ -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
|
@ -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
|
||||
|
@ -13,6 +13,7 @@ class ActivityPub::Activity::Delete < ActivityPub::Activity
|
||||
|
||||
def delete_person
|
||||
SuspendAccountService.new.call(@account)
|
||||
@account.destroy!
|
||||
end
|
||||
|
||||
def delete_note
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
||||
|
@ -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(' ')
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user