Refactor Web::PushSubscription, remove welcome message (#4524)

* Refactor Web::PushSubscription, remove welcome message

* Add missing helper

* Use locale of the receiver on push notifications (#4519)

* Remove unused translations

* Fix dir on notifications
This commit is contained in:
Eugen Rochko authored and GitHub committed 2017-09-01 13:35:23 +02:00
1 parent 504737e860
commit 74437c6bff
13 files changed
+205 -252

No files matched your search

@@ -31,8 +31,8 @@ const notify = options =>
const group = cloneNotification(notifications[0]);
group.title = formatGroupTitle(group.data.message, group.data.count + 1);
group.body = `${options.title}\n${group.body}`;
group.data = { ...group.data, count: group.data.count + 1 };
group.body = `${options.title}\n${group.body}`;
group.data = { ...group.data, count: group.data.count + 1 };
return self.registration.showNotification(group.title, group);
}
@@ -43,18 +43,18 @@ const notify = options =>
const handlePush = (event) => {
const options = event.data.json();
options.body = options.data.nsfw || options.data.content;
options.image = options.image || undefined; // Null results in a network request (404)
options.body = options.data.nsfw || options.data.content;
options.dir = options.data.dir;
options.image = options.image || undefined; // Null results in a network request (404)
options.timestamp = options.timestamp && new Date(options.timestamp);
const expandAction = options.data.actions.find(action => action.todo === 'expand');
if (expandAction) {
options.actions = [expandAction];
options.hiddenActions = options.data.actions.filter(action => action !== expandAction);
options.actions = [expandAction];
options.hiddenActions = options.data.actions.filter(action => action !== expandAction);
options.data.hiddenImage = options.image;
options.image = undefined;
options.image = undefined;
} else {
options.actions = options.data.actions;
}
@@ -75,8 +75,8 @@ const cloneNotification = (notification) => {
const expandNotification = (notification) => {
const nextNotification = cloneNotification(notification);
nextNotification.body = notification.data.content;
nextNotification.image = notification.data.hiddenImage;
nextNotification.body = notification.data.content;
nextNotification.image = notification.data.hiddenImage;
nextNotification.actions = notification.data.actions.filter(action => action.todo !== 'expand');
return self.registration.showNotification(nextNotification.title, nextNotification);
@@ -105,8 +105,7 @@ const openUrl = url =>
const webClients = clientList.filter(client => /\/web\//.test(client.url));
if (webClients.length !== 0) {
const client = findBestClient(webClients);
const client = findBestClient(webClients);
const { pathname } = new URL(url);
if (pathname.startsWith('/web/')) {
@@ -126,8 +125,7 @@ const openUrl = url =>
});
const removeActionFromNotification = (notification, action) => {
const actions = notification.actions.filter(act => act.action !== action.action);
const actions = notification.actions.filter(act => act.action !== action.action);
const nextNotification = cloneNotification(notification);
nextNotification.actions = actions;
+1
View File
@@ -25,6 +25,7 @@
#
class SessionActivation < ApplicationRecord
belongs_to :user, inverse_of: :session_activations, required: true
belongs_to :access_token, class_name: 'Doorkeeper::AccessToken', dependent: :destroy
belongs_to :web_push_subscription, class_name: 'Web::PushSubscription', dependent: :destroy
+21 -139
View File
@@ -13,59 +13,14 @@
#
require 'webpush'
require_relative '../../models/setting'
class Web::PushSubscription < ApplicationRecord
include RoutingHelper
include StreamEntriesHelper
include ActionView::Helpers::TranslationHelper
include ActionView::Helpers::SanitizeHelper
has_one :session_activation
before_create :send_welcome_notification
def push(notification)
name = display_name notification.from_account
title = title_str(name, notification)
body = body_str notification
dir = dir_str body
url = url_str notification
image = image_str notification
actions = actions_arr notification
access_token = actions.empty? ? nil : find_or_create_access_token(notification).token
nsfw = notification.target_status.nil? || notification.target_status.spoiler_text.empty? ? nil : notification.target_status.spoiler_text
# TODO: Make sure that the payload does not exceed 4KB - Webpush::PayloadTooLarge
Webpush.payload_send(
message: JSON.generate(
title: title,
dir: dir,
image: image,
badge: full_asset_url('badge.png', skip_pipeline: true),
tag: notification.id,
timestamp: notification.created_at,
icon: notification.from_account.avatar_static_url,
data: {
content: decoder.decode(strip_tags(body)),
nsfw: nsfw.nil? ? nil : decoder.decode(strip_tags(nsfw)),
url: url,
actions: actions,
access_token: access_token,
message: translate('push_notifications.group.title'), # Do not pass count, will be formatted in the ServiceWorker
}
),
endpoint: endpoint,
p256dh: key_p256dh,
auth: key_auth,
vapid: {
subject: "mailto:#{Setting.site_contact_email}",
private_key: Rails.configuration.x.vapid_private_key,
public_key: Rails.configuration.x.vapid_public_key,
},
ttl: 40 * 60 * 60 # 48 hours
)
I18n.with_locale(session_activation.user.locale || I18n.default_locale) do
push_payload(message_from(notification), 48.hours.seconds)
end
end
def pushable?(notification)
@@ -73,120 +28,47 @@ class Web::PushSubscription < ApplicationRecord
end
def as_payload
payload = {
id: id,
endpoint: endpoint,
}
payload = { id: id, endpoint: endpoint }
payload[:alerts] = data['alerts'] if data && data.key?('alerts')
payload
end
def access_token
find_or_create_access_token.token
end
private
def title_str(name, notification)
case notification.type
when :mention then translate('push_notifications.mention.title', name: name)
when :follow then translate('push_notifications.follow.title', name: name)
when :favourite then translate('push_notifications.favourite.title', name: name)
when :reblog then translate('push_notifications.reblog.title', name: name)
end
end
def push_payload(message, ttl = 5.minutes.seconds)
# TODO: Make sure that the payload does not
# exceed 4KB - Webpush::PayloadTooLarge
def body_str(notification)
case notification.type
when :mention then notification.target_status.text
when :follow then notification.from_account.note
when :favourite then notification.target_status.text
when :reblog then notification.target_status.text
end
end
def url_str(notification)
case notification.type
when :mention then web_url("statuses/#{notification.target_status.id}")
when :follow then web_url("accounts/#{notification.from_account.id}")
when :favourite then web_url("statuses/#{notification.target_status.id}")
when :reblog then web_url("statuses/#{notification.target_status.id}")
end
end
def actions_arr(notification)
actions =
case notification.type
when :mention then [
{
title: translate('push_notifications.mention.action_favourite'),
icon: full_asset_url('web-push-icon_favourite.png', skip_pipeline: true),
todo: 'request',
method: 'POST',
action: "/api/v1/statuses/#{notification.target_status.id}/favourite",
},
]
else []
end
should_hide = notification.type.equal?(:mention) && !notification.target_status.nil? && (notification.target_status.sensitive || !notification.target_status.spoiler_text.empty?)
can_boost = notification.type.equal?(:mention) && !notification.target_status.nil? && !notification.target_status.hidden?
if should_hide
actions.insert(0, title: translate('push_notifications.mention.action_expand'), icon: full_asset_url('web-push-icon_expand.png', skip_pipeline: true), todo: 'expand', action: 'expand')
end
if can_boost
actions << { title: translate('push_notifications.mention.action_boost'), icon: full_asset_url('web-push-icon_reblog.png', skip_pipeline: true), todo: 'request', method: 'POST', action: "/api/v1/statuses/#{notification.target_status.id}/reblog" }
end
actions
end
def image_str(notification)
return nil if notification.target_status.nil? || notification.target_status.media_attachments.empty?
full_asset_url(notification.target_status.media_attachments.first.file.url(:small))
end
def dir_str(body)
rtl?(body) ? 'rtl' : 'ltr'
end
def send_welcome_notification
Webpush.payload_send(
message: JSON.generate(
title: translate('push_notifications.subscribed.title'),
icon: full_asset_url('android-chrome-192x192.png', skip_pipeline: true),
badge: full_asset_url('badge.png', skip_pipeline: true),
data: {
content: translate('push_notifications.subscribed.body'),
actions: [],
url: web_url('notifications'),
message: translate('push_notifications.group.title'), # Do not pass count, will be formatted in the ServiceWorker
}
),
message: Oj.dump(message),
endpoint: endpoint,
p256dh: key_p256dh,
auth: key_auth,
ttl: ttl,
vapid: {
subject: "mailto:#{Setting.site_contact_email}",
private_key: Rails.configuration.x.vapid_private_key,
public_key: Rails.configuration.x.vapid_public_key,
},
ttl: 5 * 60 # 5 minutes
}
)
end
def find_or_create_access_token(notification)
def message_from(notification)
serializable_resource = ActiveModelSerializers::SerializableResource.new(notification, serializer: Web::NotificationSerializer, scope: self, scope_name: :current_push_subscription)
serializable_resource.as_json
end
def find_or_create_access_token
Doorkeeper::AccessToken.find_or_create_for(
Doorkeeper::Application.find_by(superapp: true),
notification.account.user.id,
session_activation.user_id,
Doorkeeper::OAuth::Scopes.from_string('read write follow'),
Doorkeeper.configuration.access_token_expires_in,
Doorkeeper.configuration.refresh_token_enabled?
)
end
def decoder
@decoder ||= HTMLEntities.new
end
end
@@ -0,0 +1,168 @@
# frozen_string_literal: true
class Web::NotificationSerializer < ActiveModel::Serializer
include StreamEntriesHelper
class DataSerializer < ActiveModel::Serializer
include RoutingHelper
include StreamEntriesHelper
include ActionView::Helpers::SanitizeHelper
attributes :content, :nsfw, :url, :actions,
:access_token, :message
def content
decoder.decode(strip_tags(body))
end
def dir
rtl?(body) ? 'rtl' : 'ltr'
end
def nsfw
return if object.target_status.nil?
object.target_status.spoiler_text.presence
end
def url
case object.type
when :mention
web_url("statuses/#{object.target_status.id}")
when :follow
web_url("accounts/#{object.from_account.id}")
when :favourite
web_url("statuses/#{object.target_status.id}")
when :reblog
web_url("statuses/#{object.target_status.id}")
end
end
def actions
return @actions if defined?(@actions)
@actions = []
if object.type == :mention
@actions << expand_action if collapsed?
@actions << favourite_action
@actions << reblog_action if rebloggable?
end
@actions
end
def access_token
return if actions.empty?
current_push_subscription.access_token
end
def message
I18n.t('push_notifications.group.title')
end
private
def body
case object.type
when :mention
object.target_status.text
when :follow
object.from_account.note
when :favourite
object.target_status.text
when :reblog
object.target_status.text
end
end
def decoder
@decoder ||= HTMLEntities.new
end
def expand_action
{
title: I18n.t('push_notifications.mention.action_expand'),
icon: full_asset_url('web-push-icon_expand.png', skip_pipeline: true),
todo: 'expand',
action: 'expand',
}
end
def favourite_action
{
title: I18n.t('push_notifications.mention.action_favourite'),
icon: full_asset_url('web-push-icon_favourite.png', skip_pipeline: true),
todo: 'request',
method: 'POST',
action: "/api/v1/statuses/#{object.target_status.id}/favourite",
}
end
def reblog_action
{
title: I18n.t('push_notifications.mention.action_boost'),
icon: full_asset_url('web-push-icon_reblog.png', skip_pipeline: true),
todo: 'request',
method: 'POST',
action: "/api/v1/statuses/#{object.target_status.id}/reblog",
}
end
def collapsed?
!object.target_status.nil? && (object.target_status.sensitive? || object.target_status.spoiler_text.present?)
end
def rebloggable?
!object.target_status.nil? && !object.target_status.hidden?
end
end
attributes :title, :dir, :image, :badge, :tag,
:timestamp, :icon
has_one :data
def title
case object.type
when :mention
I18n.t('push_notifications.mention.title', name: name)
when :follow
I18n.t('push_notifications.follow.title', name: name)
when :favourite
I18n.t('push_notifications.favourite.title', name: name)
when :reblog
I18n.t('push_notifications.reblog.title', name: name)
end
end
def image
return if object.target_status.nil? || object.target_status.media_attachments.empty?
full_asset_url(object.target_status.media_attachments.first.file.url(:small))
end
def badge
full_asset_url('badge.png', skip_pipeline: true)
end
def tag
object.id
end
def timestamp
object.created_at
end
def icon
object.from_account.avatar_static_url
end
def data
object
end
private
def name
display_name(object.from_account)
end
end
+3 -73
View File
@@ -21,7 +21,7 @@ ca:
real_conversation_body: Amb 500 caràcters a la teva disposició i suport per a continguts granulars i avisos multimèdia, pots expressar-te de la manera que vulguis.
real_conversation_title: Construït per a converses reals
within_reach_body: Diverses aplicacions per a iOS, Android i altres plataformes gràcies a un ecosistema API amable amb el desenvolupador, et permet mantenir-te al dia amb els teus amics en qualsevol lloc..
within_reach_title: Sempre a l'abast
within_reach_title: Sempre a l'abast
find_another_instance: Troba altres instàncies
generic_description: "%{domain} és un servidor a la xarxa"
hosted_on: Mastodon allotjat a %{domain}
@@ -215,7 +215,7 @@ ca:
body: "%{reporter} ha informat de %{target}"
subject: Nou informe per a %{instance} (#%{id})
application_mailer:
salutation: '%{name},'
salutation: "%{name},"
settings: 'Canviar preferències de correu: %{link}'
signature: Notificacions de Mastodon desde %{instance}
view: 'Vista:'
@@ -358,9 +358,6 @@ ca:
title: "%{name} t'ha mencionat"
reblog:
title: "%{name} t'ha retootejat"
subscribed:
body: Ara pots rebre notificacions push.
title: Subscripció registrada
remote_follow:
acct: Escriu el usuari@domini de la persona que vols seguir
missing_resource: No s'ha pogut trobar la URL de redirecció necessaria per el compte.
@@ -432,74 +429,7 @@ ca:
reblogged: retooteado
sensitive_content: Contingut sensible
terms:
body_html: |
<h2>Política de privacitat</h2>
<h3 id="collect">Quina informació recollim?</h3>
<p>Recopilem informació teva quan et registres en aquesta instància i recopilem dades quan participes en el fòrum llegint, escrivint i avaluant el contingut aquí compartit.</p>
<p>En registrar-te en aquesta instància, se't pot demanar que introduexisu el teu nom i l'adreça de correu electrònic. També pots visitar el nostre lloc sense registrar-te. La teva adreça de correu electrònic es verificarà mitjançant un correu electrònic que conté un enllaç únic. Si es visita aquest enllaç, sabem que controles l'adreça de correu electrònic.</p>
<p>Quan es registra i publica, registrem l'adreça IP de la qual es va originar la publicació. També podrem conservar els registres del servidor que inclouen l'adreça IP de cada sol·licitud al nostre servidor.</p>
<h3 id="use">Per a què utilitzem la teva informació?</h3>
<p>Qualsevol de la informació que recopilem de tu pot utilitzar-se d'una de les maneres següents:</p>
<ul>
<li>Per a personalitzar la teva experiència &mdash; la teva informació ens ajuda a respondre millor a les teves necessitats individuals.</li>
<li>Per millorar el nostre lloc &mdash; ens esforcem contínuament per millorar les nostres ofertes de llocs basats en la informació i els comentaris que rebem de tu.</li>
<li>Per millorar el servei al client &mdash; la teva informació ens ajuda a respondre més eficaçment a les teves sol·licituds de servei al client i a les necessitats de suport.</li>
<li>Per enviar correus electrònics periòdics &mdash; l'adreça electrònica que proporcionis es pot utilitzar per enviar-te informació, notificacions que sol·licitis sobre canvis en temes o en resposta al teu nom d'usuari, respondre a les consultes i/o altres sol·licituds o preguntes.</li>
</ul>
<h3 id="protect">Com protegim la teva informació?</h3>
<p>Implementem diverses mesures de seguretat per mantenir la seguretat de la teva informació personal quan introdueixes, envies o accedeixes a la teva informació personal.</p>
<h3 id="data-retention">Quina és la nostre política de retenció de dades?</h3>
<p>Farem un esforç de bona fe per a:</p>
<ul>
<li>Conserva els registres de servidor que continguin l'adreça IP de totes les sol·licituds a aquest servidor no més de 90 dies.</li>
<li>Conserva les adreces IP associades als usuaris registrats i les seves publicacions no més de 5 anys.</li>
</ul>
<h3 id="cookies">Utilitzem galetes?</h3>
<p>Sí. Les cookies són fitxers petits que un lloc o el proveïdor de serveis transfereix al disc dur del vostre ordinador a través del navegador web (si ho permet). Aquestes galetes permeten al lloc reconèixer el vostre navegador i, si teniu un compte registrat, associar-lo al vostre compte registrat.</p>
<p>Utilitzem cookies per comprendre i desar les vostres preferències per a futures visites i compilar dades agregades sobre el trànsit del lloc i la interacció del lloc, de manera que podrem oferir millors experiències i eines del lloc en el futur. Podem contractar amb proveïdors de serveis de tercers per ajudar-nos a comprendre millor els visitants del nostre lloc. Aquests proveïdors de serveis no estan autoritzats a utilitzar la informació recollida en nom nostre, excepte per ajudar-nos a dur a terme i millorar el nostre negoci.</p>
<h3 id="disclose">Publiquem informació al exterior?</h3>
<p>No venem, comercialitzem ni transmetem a tercers la vostra informació d'identificació personal. Això no inclou tercers de confiança que ens ajudin a operar el nostre lloc, a dur a terme el nostre negoci o a fer-ho, sempre que aquestes parts acceptin mantenir confidencial aquesta informació. També podem publicar la vostra informació quan creiem que l'alliberament és apropiat per complir amb la llei, fer complir les polítiques del nostre lloc o protegir els nostres drets o altres drets, propietat o seguretat. No obstant això, la informació de visitant que no sigui personalment identificable es pot proporcionar a altres parts per a la comercialització, la publicitat o altres usos.</p>
<h3 id="third-party">Vincles de tercers</h3>
<p>De tant en tant, segons el nostre criteri, podem incloure o oferir productes o serveis de tercers al nostre lloc. Aquests llocs de tercers tenen polítiques de privadesa separades i independents. Per tant, no tenim responsabilitat ni responsabilitat civil pel contingut i les activitats d'aquests llocs enllaçats. No obstant això, busquem protegir la integritat del nostre lloc i donem la benvinguda a qualsevol comentari sobre aquests llocs.</p>
<h3 id="coppa">Compliment de la Llei de protecció de la privacitat en línia dels nens</h3>
<p>El nostre lloc, productes i serveis estan dirigits a persones que tenen almenys 13 anys. Si aquest servidor es troba als EUA, i teniu menys de 13 anys, segons els requisits de COPPA (<a href="https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act">Children's Online Privacy Protection Act</a>) no feu servir aquest lloc.</p>
<h3 id="online">Només la política de privacitat en línia</h3>
<p>Aquesta política de privacitat en línia només s'aplica a la informació recopilada a través del nostre lloc i no a la informació recopilada fora de línia.</p>
<h3 id="consent">El vostre consentiment</h3>
<p>En utilitzar el nostre lloc, accepta la política de privadesa del nostre lloc web.</p>
<h3 id="changes">Canvis a la nostra política de privacitat</h3>
<p>Si decidim canviar la nostra política de privadesa, publicarem aquests canvis en aquesta pàgina.</p>
<p>Aquest document és CC-BY-SA. Es va actualitzar per última vegada el 31 de maig de 2013.</p>
<p>Originalment adaptat a la <a href="https://github.com/discourse/discourse">política de privadesa del Discurs</a>.</p>
body_html: "<h2>Política de privacitat</h2>\n\n<h3 id=\"collect\">Quina informació recollim?</h3>\n\n<p>Recopilem informació teva quan et registres en aquesta instància i recopilem dades quan participes en el fòrum llegint, escrivint i avaluant el contingut aquí compartit.</p>\n\n<p>En registrar-te en aquesta instància, se't pot demanar que introduexisu el teu nom i l'adreça de correu electrònic. També pots visitar el nostre lloc sense registrar-te. La teva adreça de correu electrònic es verificarà mitjançant un correu electrònic que conté un enllaç únic. Si es visita aquest enllaç, sabem que controles l'adreça de correu electrònic.</p>\n\n<p>Quan es registra i publica, registrem l'adreça IP de la qual es va originar la publicació. També podrem conservar els registres del servidor que inclouen l'adreça IP de cada sol·licitud al nostre servidor.</p>\n\n<h3 id=\"use\">Per a què utilitzem la teva informació?</h3>\n\n<p>Qualsevol de la informació que recopilem de tu pot utilitzar-se d'una de les maneres següents:</p>\n\n<ul>\n <li>Per a personalitzar la teva experiència &mdash; la teva informació ens ajuda a respondre millor a les teves necessitats individuals.</li>\n <li>Per millorar el nostre lloc &mdash; ens esforcem contínuament per millorar les nostres ofertes de llocs basats en la informació i els comentaris que rebem de tu.</li>\n <li>Per millorar el servei al client &mdash; la teva informació ens ajuda a respondre més eficaçment a les teves sol·licituds de servei al client i a les necessitats de suport.</li>\n <li>Per enviar correus electrònics periòdics &mdash; l'adreça electrònica que proporcionis es pot utilitzar per enviar-te informació, notificacions que sol·licitis sobre canvis en temes o en resposta al teu nom d'usuari, respondre a les consultes i/o altres sol·licituds o preguntes.</li>\n</ul>\n\n<h3 id=\"protect\">Com protegim la teva informació?</h3>\n\n<p>Implementem diverses mesures de seguretat per mantenir la seguretat de la teva informació personal quan introdueixes, envies o accedeixes a la teva informació personal.</p>\n\n<h3 id=\"data-retention\">Quina és la nostre política de retenció de dades?</h3>\n\n<p>Farem un esforç de bona fe per a:</p>\n\n<ul>\n <li>Conserva els registres de servidor que continguin l'adreça IP de totes les sol·licituds a aquest servidor no més de 90 dies.</li>\n <li>Conserva les adreces IP associades als usuaris registrats i les seves publicacions no més de 5 anys.</li>\n</ul>\n\n<h3 id=\"cookies\">Utilitzem galetes?</h3>\n\n<p>Sí. Les cookies són fitxers petits que un lloc o el proveïdor de serveis transfereix al disc dur del vostre ordinador a través del navegador web (si ho permet). Aquestes galetes permeten al lloc reconèixer el vostre navegador i, si teniu un compte registrat, associar-lo al vostre compte registrat.</p>\n\n<p>Utilitzem cookies per comprendre i desar les vostres preferències per a futures visites i compilar dades agregades sobre el trànsit del lloc i la interacció del lloc, de manera que podrem oferir millors experiències i eines del lloc en el futur. Podem contractar amb proveïdors de serveis de tercers per ajudar-nos a comprendre millor els visitants del nostre lloc. Aquests proveïdors de serveis no estan autoritzats a utilitzar la informació recollida en nom nostre, excepte per ajudar-nos a dur a terme i millorar el nostre negoci.</p>\n\n<h3 id=\"disclose\">Publiquem informació al exterior?</h3>\n\n<p>No venem, comercialitzem ni transmetem a tercers la vostra informació d'identificació personal. Això no inclou tercers de confiança que ens ajudin a operar el nostre lloc, a dur a terme el nostre negoci o a fer-ho, sempre que aquestes parts acceptin mantenir confidencial aquesta informació. També podem publicar la vostra informació quan creiem que l'alliberament és apropiat per complir amb la llei, fer complir les polítiques del nostre lloc o protegir els nostres drets o altres drets, propietat o seguretat. No obstant això, la informació de visitant que no sigui personalment identificable es pot proporcionar a altres parts per a la comercialització, la publicitat o altres usos.</p> \n\n<h3 id=\"third-party\">Vincles de tercers</h3>\n\n<p>De tant en tant, segons el nostre criteri, podem incloure o oferir productes o serveis de tercers al nostre lloc. Aquests llocs de tercers tenen polítiques de privadesa separades i independents. Per tant, no tenim responsabilitat ni responsabilitat civil pel contingut i les activitats d'aquests llocs enllaçats. No obstant això, busquem protegir la integritat del nostre lloc i donem la benvinguda a qualsevol comentari sobre aquests llocs.</p>\n\n<h3 id=\"coppa\">Compliment de la Llei de protecció de la privacitat en línia dels nens</h3>\n\n<p>El nostre lloc, productes i serveis estan dirigits a persones que tenen almenys 13 anys. Si aquest servidor es troba als EUA, i teniu menys de 13 anys, segons els requisits Line truncated
title: "%{instance} Condicions del servei i política de privadesa"
time:
formats:
-3
View File
@@ -382,9 +382,6 @@ en:
title: "%{name} mentioned you"
reblog:
title: "%{name} boosted your status"
subscribed:
body: You can now receive push notifications.
title: Subscription registered!
remote_follow:
acct: Enter your username@domain you want to follow from
missing_resource: Could not find the required redirect URL for your account
-3
View File
@@ -357,9 +357,6 @@ fa:
title: "%{name} از شما نام برد"
reblog:
title: "%{name} نوشتهٔ شما را بازبوقید"
subscribed:
body: از این به بعد سرور می‌تواندبه شما اعلان‌های تازه بفرستد .
title: عضویت ثبت شد!
remote_follow:
acct: نشانی حساب username@domain خود را این‌جا بنویسید
missing_resource: نشانی اینترنتی برای رسیدن به حساب شما پیدا نشد
-3
View File
@@ -353,9 +353,6 @@ fr:
title: "%{name} vous a mentionné·e"
reblog:
title: "%{name} a partagé⋅e votre statut"
subscribed:
body: Vous pouvez désormais recevoir des notifications push.
title: Abonnements aux notifications push
remote_follow:
acct: Entrez votre pseudo@instance depuis lequel vous voulez suivre ce⋅tte utilisateur⋅rice
missing_resource: L’URL de redirection n’a pas pu être trouvée
-3
View File
@@ -381,9 +381,6 @@ ja:
title: "%{name} さんから返信がありました"
reblog:
title: あなたのトゥートが %{name} さんにブーストされました
subscribed:
body: あなたはプッシュ通知を受け取ることが出来ます
title: Subscription が登録されました
remote_follow:
acct: あなたの ユーザー名@ドメイン を入力してください
missing_resource: リダイレクト先が見つかりませんでした
-3
View File
@@ -353,9 +353,6 @@ nl:
title: "%{name} vermeldde jou"
reblog:
title: "%{name} boostte jouw toot"
subscribed:
body: Je kan nu pushmeldingen ontvangen.
title: Aanmelding bevestigd!
remote_follow:
acct: Geef jouw account@domein.tld op waarvandaan je wilt volgen
missing_resource: Kon vereiste doorverwijzings-URL voor jouw account niet vinden
-3
View File
@@ -448,9 +448,6 @@ oc:
title: "%{name} vos a mencionat"
reblog:
title: "%{name} a partejat vòstre estatut"
subscribed:
body: Podètz ara recebre las notificacions push.
title: Abonament enregistrat !
remote_follow:
acct: Picatz vòstre utilizaire@instància que cal utilizar per sègre aqueste utilizaire
missing_resource: URL de redireccion pas trobada
-5
View File
@@ -224,8 +224,6 @@ pl:
settings: 'Zmień ustawienia powiadamiania: %{link}'
signature: Powiadomienie Mastodona z instancji %{instance}
view: 'Zobacz:'
applications:
invalid_url: Ten URL jest nieprawidłowy
applications:
created: Pomyślnie utworzono aplikację
destroyed: Pomyślnie usunięto aplikację
@@ -375,9 +373,6 @@ pl:
title: "%{name} wspomniał o Tobie"
reblog:
title: "%{name} podbił Twój status"
subscribed:
body: Otrzymujesz teraz powiadomienia push.
title: Zarejestrowano subskrypcję!
remote_follow:
acct: Podaj swój adres (nazwa@domena), z którego chcesz śledzić
missing_resource: Nie udało się znaleźć adresu przekierowania z Twojej domeny
-3
View File
@@ -278,9 +278,6 @@ ru:
title: Вас упомянул(а) %{name}
reblog:
title: "%{name} продвинул(а) Ваш статус"
subscribed:
body: Теперь Вы можете получать push-уведомления.
title: Подписка зарегистрирована!
remote_follow:
acct: Введите username@domain, откуда Вы хотите подписаться
missing_resource: Поиск требуемого перенаправления URL для Вашего аккаунта завершился неудачей