Replace sprockets/browserify with Webpack (#2617)

* Replace browserify with webpack

* Add react-intl-translations-manager

* Do not minify in development, add offline-plugin for ServiceWorker background cache updates

* Adjust tests and dependencies

* Fix production deployments

* Fix tests

* More optimizations

* Improve travis cache for npm stuff

* Re-run travis

* Add back support for custom.scss as before

* Remove offline-plugin and babili

* Fix issue with Immutable.List().unshift(...values) not working as expected

* Make travis load schema instead of running all migrations in sequence

* Fix missing React import in WarningContainer. Optimize rendering performance by using ImmutablePureComponent instead of
React.PureComponent. ImmutablePureComponent uses Immutable.is() to compare props. Replace dynamic callback bindings in
<UI />

* Add react definitions to places that use JSX

* Add Procfile.dev for running rails, webpack and streaming API at the same time
This commit is contained in:
Eugen Rochko 2017-05-03 02:04:16 +02:00 committed by GitHub
parent 26bc591572
commit f5bf5ebb82
343 changed files with 5299 additions and 2081 deletions

View file

@ -1,7 +1,25 @@
{
"presets": ["es2015", "react"],
"presets": [
"es2015",
"react",
[
"env",
{
"loose": true,
"modules": false
}
]
],
"plugins": [
"transform-react-jsx-source",
"transform-react-jsx-self",
"transform-decorators-legacy",
"transform-object-rest-spread"
"transform-object-rest-spread",
[
"react-intl",
{
"messagesDir": "./build/messages"
}
]
]
}

1
.foreman Normal file
View file

@ -0,0 +1 @@
procfile: Procfile.dev

4
.gitignore vendored
View file

@ -22,7 +22,7 @@ public/assets
.env
.env.production
node_modules/
neo4j/
build/
# Ignore Vagrant files
.vagrant/
@ -43,3 +43,5 @@ redis
# Ignore vim files
*~
*.swp
/public/packs
/node_modules

4
.postcssrc.yml Normal file
View file

@ -0,0 +1,4 @@
plugins:
postcss-smart-import: {}
precss: {}
autoprefixer: {}

View file

@ -1,9 +1,7 @@
language: ruby
cache:
bundler: true
yarn: true
directories:
- node_modules
yarn: false
dist: trusty
sudo: false
@ -42,7 +40,8 @@ install:
- yarn install
before_script:
- bundle exec rails db:create db:migrate
- bundle exec rails db:create db:schema:load
- bundle exec rails assets:precompile
script:
- bundle exec rspec

View file

@ -10,8 +10,6 @@ EXPOSE 3000 4000
WORKDIR /mastodon
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/repositories \
&& BUILD_DEPS=" \
postgresql-dev \
@ -23,6 +21,7 @@ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/reposit
$BUILD_DEPS \
nodejs@edge \
nodejs-npm@edge \
git \
libpq \
libxml2 \
libxslt \
@ -31,14 +30,14 @@ RUN echo "@edge https://nl.alpinelinux.org/alpine/edge/main" >> /etc/apk/reposit
imagemagick@edge \
ca-certificates \
&& npm install -g npm@3 && npm install -g yarn \
&& bundle install --deployment --without test development \
&& yarn --ignore-optional \
&& yarn cache clean \
&& npm -g cache clean \
&& update-ca-certificates \
&& apk del $BUILD_DEPS \
&& rm -rf /tmp/* /var/cache/apk/*
COPY Gemfile Gemfile.lock package.json yarn.lock /mastodon/
RUN bundle install --deployment --without test development \
&& yarn --ignore-optional --pure-lockfile
COPY . /mastodon
VOLUME /mastodon/public/system /mastodon/public/assets
VOLUME /mastodon/public/system /mastodon/public/assets /mastodon/public/packs

34
Gemfile
View file

@ -5,22 +5,19 @@ ruby '>= 2.3.0', '< 2.5.0'
gem 'pkg-config'
gem 'rails', '~> 5.0.2'
gem 'sass-rails', '~> 5.0'
gem 'uglifier', '>= 1.3.0'
gem 'jquery-rails'
gem 'puma'
gem 'rails', '~> 5.0.2'
gem 'uglifier', '>= 1.3.0'
gem 'hamlit-rails'
gem 'pg'
gem 'pghero'
gem 'dotenv-rails'
gem 'font-awesome-rails'
gem 'best_in_place', '~> 3.0.1'
gem 'aws-sdk', '>= 2.0'
gem 'paperclip', '~> 5.1'
gem 'paperclip-av-transcoder'
gem 'aws-sdk', '>= 2.0'
gem 'addressable'
gem 'devise'
@ -58,18 +55,18 @@ gem 'sprockets-rails', require: 'sprockets/railtie'
gem 'statsd-instrument'
gem 'twitter-text'
gem 'tzinfo-data'
gem 'webpacker', '~>1.2'
gem 'whatlanguage'
# For some reason the view specs start failing without this
gem 'react-rails'
gem 'browserify-rails'
gem 'autoprefixer-rails'
group :development, :test do
gem 'rspec-rails'
gem 'pry-rails'
gem 'fuubar'
gem 'fabrication'
gem 'fuubar'
gem 'i18n-tasks', '~> 0.9.6'
gem 'pry-rails'
gem 'rspec-rails'
end
group :test do
@ -83,24 +80,23 @@ group :test do
end
group :development do
gem 'rubocop', '0.46.0', require: false
gem 'better_errors'
gem 'binding_of_caller'
gem 'letter_opener'
gem 'letter_opener_web'
gem 'bullet'
gem 'active_record_query_trace'
gem 'annotate'
gem 'better_errors'
gem 'binding_of_caller'
gem 'bullet'
gem 'letter_opener'
gem 'letter_opener_web'
gem 'rubocop', '0.46.0', require: false
gem 'capistrano', '3.8.0'
gem 'capistrano-rails'
gem 'capistrano-rbenv'
gem 'capistrano-yarn'
gem 'capistrano-faster-assets', '~> 1.0'
end
group :production do
gem 'lograge'
gem 'rails_12factor'
gem 'redis-rails'
gem 'lograge'
end

View file

@ -43,15 +43,13 @@ GEM
public_suffix (~> 2.0, >= 2.0.2)
airbrussh (1.2.0)
sshkit (>= 1.6.1, != 1.7.0)
annotate (2.7.1)
activerecord (>= 3.2, < 6.0)
rake (>= 10.4, < 12.0)
annotate (2.6.5)
activerecord (>= 2.3.0)
rake (>= 0.8.7)
arel (7.1.4)
ast (2.3.0)
attr_encrypted (3.0.3)
encryptor (~> 3.0.0)
autoprefixer-rails (6.7.7.2)
execjs
av (0.9.0)
cocaine (~> 0.5.3)
aws-sdk (2.9.12)
@ -76,10 +74,6 @@ GEM
rack (>= 0.9.0)
binding_of_caller (0.7.2)
debug_inspector (>= 0.0.1)
browserify-rails (4.1.0)
addressable (>= 2.4.0)
railties (>= 4.0.0, < 5.1)
sprockets (>= 3.6.0)
builder (3.2.3)
bullet (5.5.1)
activesupport (>= 3.0.0)
@ -92,8 +86,6 @@ GEM
capistrano-bundler (1.2.0)
capistrano (~> 3.1)
sshkit (~> 1.2)
capistrano-faster-assets (1.0.2)
capistrano (>= 3.1)
capistrano-rails (1.2.3)
capistrano (~> 3.1)
capistrano-bundler (~> 1.1)
@ -161,8 +153,6 @@ GEM
faker (1.7.3)
i18n (~> 0.5)
fast_blank (1.0.0)
font-awesome-rails (4.7.0.1)
railties (>= 3.2, < 5.1)
fuubar (2.2.0)
rspec-core (~> 3.0)
ruby-progressbar (~> 1.4)
@ -210,10 +200,6 @@ GEM
rainbow (~> 2.2)
terminal-table (>= 1.5.1)
jmespath (1.3.1)
jquery-rails (4.3.1)
rails-dom-testing (>= 1, < 3)
railties (>= 4.2.0)
thor (>= 0.14, < 2.0)
json (2.1.0)
kaminari (1.0.1)
activesupport (>= 4.1.0)
@ -257,6 +243,7 @@ GEM
mimemagic (0.3.2)
mini_portile2 (2.1.0)
minitest (5.10.1)
multi_json (1.12.1)
net-scp (1.2.1)
net-ssh (>= 2.6.5)
net-ssh (4.1.0)
@ -348,8 +335,8 @@ GEM
thor (>= 0.18.1, < 2.0)
rainbow (2.2.2)
rake
rake (11.3.0)
react-rails (1.11.0)
rake (12.0.0)
react-rails (2.1.0)
babel-transpiler (>= 0.7.0)
connection_pool
execjs
@ -410,13 +397,6 @@ GEM
crass (~> 1.0.2)
nokogiri (>= 1.4.4)
nokogumbo (~> 1.4.1)
sass (3.4.23)
sass-rails (5.0.6)
railties (>= 4.0.0, < 6)
sass (~> 3.1)
sprockets (>= 2.8, < 4.0)
sprockets-rails (>= 2.0, < 4.0)
tilt (>= 1.1, < 3)
sidekiq (4.2.10)
concurrent-ruby (~> 1.0)
connection_pool (~> 2.2, >= 2.2.0)
@ -473,6 +453,10 @@ GEM
addressable (>= 2.3.6)
crack (>= 0.3.2)
hashdiff
webpacker (1.2)
activesupport (>= 4.2)
multi_json (~> 1.2)
railties (>= 4.2)
websocket-driver (0.6.5)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.2)
@ -487,15 +471,12 @@ DEPENDENCIES
active_record_query_trace
addressable
annotate
autoprefixer-rails
aws-sdk (>= 2.0)
best_in_place (~> 3.0.1)
better_errors
binding_of_caller
browserify-rails
bullet
capistrano (= 3.8.0)
capistrano-faster-assets (~> 1.0)
capistrano-rails
capistrano-rbenv
capistrano-yarn
@ -507,7 +488,6 @@ DEPENDENCIES
fabrication
faker
fast_blank
font-awesome-rails
fuubar
goldfinger
hamlit-rails
@ -517,7 +497,6 @@ DEPENDENCIES
http_accept_language
httplog
i18n-tasks (~> 0.9.6)
jquery-rails
kaminari
letter_opener
letter_opener_web
@ -554,7 +533,6 @@ DEPENDENCIES
rubocop (= 0.46.0)
ruby-oembed
sanitize
sass-rails (~> 5.0)
sidekiq
sidekiq-unique-jobs
simple-navigation
@ -566,6 +544,7 @@ DEPENDENCIES
tzinfo-data
uglifier (>= 1.3.0)
webmock
webpacker (~> 1.2)
whatlanguage
RUBY VERSION

3
Procfile.dev Normal file
View file

@ -0,0 +1,3 @@
web: bundle exec rails s -p 3000
stream: yarn run start
webpack: ./bin/webpack-dev-server

View file

@ -1,15 +0,0 @@
// This is a manifest file that'll be compiled into application.js, which will include all the files
// listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
// compiled file.
//
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
// about supported directives.
//
//= require jquery2
//= require jquery_ujs
//= require components

View file

@ -1,9 +0,0 @@
//= require jquery2
//= require jquery_ujs
//= require extras
//= require best_in_place
//= require local_time
$(function () {
$(".best_in_place").best_in_place();
});

View file

@ -1,15 +0,0 @@
//= require_self
//= require react_ujs
window.React = require('react');
window.ReactDOM = require('react-dom');
window.Perf = require('react-addons-perf');
if (!window.Intl) {
require('intl');
require('intl/locale-data/jsonp/en.js');
}
//= require_tree ./components
window.Mastodon = require('./components/containers/mastodon');

View file

@ -1,16 +0,0 @@
import Avatar from '../../../components/avatar';
import DisplayName from '../../../components/display_name';
import ImmutablePropTypes from 'react-immutable-proptypes';
const AutosuggestAccount = ({ account }) => (
<div className='autosuggest-account'>
<div className='autosuggest-account-icon'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={18} /></div>
<DisplayName account={account} />
</div>
);
AutosuggestAccount.propTypes = {
account: ImmutablePropTypes.map.isRequired
};
export default AutosuggestAccount;

View file

@ -1,15 +0,0 @@
import { FormattedMessage } from 'react-intl';
import DisplayName from '../../../components/display_name';
import ImmutablePropTypes from 'react-immutable-proptypes';
const AutosuggestStatus = ({ status }) => (
<div className='autosuggest-status'>
<FormattedMessage id='search.status_by' defaultMessage='Status by {name}' values={{ name: <strong>@{status.getIn(['account', 'acct'])}</strong> }} />
</div>
);
AutosuggestStatus.propTypes = {
status: ImmutablePropTypes.map.isRequired
};
export default AutosuggestStatus;

View file

@ -1,44 +0,0 @@
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
import Permalink from '../../../components/permalink';
import Avatar from '../../../components/avatar';
import DisplayName from '../../../components/display_name';
import emojify from '../../../emoji';
import IconButton from '../../../components/icon_button';
import { defineMessages, injectIntl } from 'react-intl';
const messages = defineMessages({
authorize: { id: 'follow_request.authorize', defaultMessage: 'Authorize' },
reject: { id: 'follow_request.reject', defaultMessage: 'Reject' }
});
const AccountAuthorize = ({ intl, account, onAuthorize, onReject }) => {
const content = { __html: emojify(account.get('note')) };
return (
<div className='account-authorize__wrapper'>
<div className='account-authorize'>
<Permalink href={account.get('url')} to={`/accounts/${account.get('id')}`} className='detailed-status__display-name'>
<div className='account-authorize__avatar'><Avatar src={account.get('avatar')} staticSrc={account.get('avatar_static')} size={48} /></div>
<DisplayName account={account} />
</Permalink>
<div className='account__header__content' dangerouslySetInnerHTML={content} />
</div>
<div className='account--panel'>
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div>
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div>
</div>
</div>
)
};
AccountAuthorize.propTypes = {
account: ImmutablePropTypes.map.isRequired,
onAuthorize: PropTypes.func.isRequired,
onReject: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired
};
export default injectIntl(AccountAuthorize);

View file

@ -1,66 +0,0 @@
import Column from '../ui/components/column';
import ColumnLink from '../ui/components/column_link';
import ColumnSubheading from '../ui/components/column_subheading';
import { Link } from 'react-router';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux';
import PropTypes from 'prop-types';
import ImmutablePropTypes from 'react-immutable-proptypes';
const messages = defineMessages({
heading: { id: 'getting_started.heading', defaultMessage: 'Getting started' },
public_timeline: { id: 'navigation_bar.public_timeline', defaultMessage: 'Federated timeline' },
navigation_subheading: { id: 'column_subheading.navigation', defaultMessage: 'Navigation'},
settings_subheading: { id: 'column_subheading.settings', defaultMessage: 'Settings'},
community_timeline: { id: 'navigation_bar.community_timeline', defaultMessage: 'Local timeline' },
preferences: { id: 'navigation_bar.preferences', defaultMessage: 'Preferences' },
follow_requests: { id: 'navigation_bar.follow_requests', defaultMessage: 'Follow requests' },
sign_out: { id: 'navigation_bar.logout', defaultMessage: 'Logout' },
favourites: { id: 'navigation_bar.favourites', defaultMessage: 'Favourites' },
blocks: { id: 'navigation_bar.blocks', defaultMessage: 'Blocked users' },
mutes: { id: 'navigation_bar.mutes', defaultMessage: 'Muted users' },
info: { id: 'navigation_bar.info', defaultMessage: 'Extended information' }
});
const mapStateToProps = state => ({
me: state.getIn(['accounts', state.getIn(['meta', 'me'])])
});
const GettingStarted = ({ intl, me }) => {
let followRequests = '';
if (me.get('locked')) {
followRequests = <ColumnLink icon='users' text={intl.formatMessage(messages.follow_requests)} to='/follow_requests' />;
}
return (
<Column icon='asterisk' heading={intl.formatMessage(messages.heading)} hideHeadingOnMobile={true}>
<div className='getting-started__wrapper'>
<ColumnSubheading text={intl.formatMessage(messages.navigation_subheading)}/>
<ColumnLink icon='users' hideOnMobile={true} text={intl.formatMessage(messages.community_timeline)} to='/timelines/public/local' />
<ColumnLink icon='globe' hideOnMobile={true} text={intl.formatMessage(messages.public_timeline)} to='/timelines/public' />
<ColumnLink icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />
{followRequests}
<ColumnLink icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />
<ColumnLink icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />
<ColumnSubheading text={intl.formatMessage(messages.settings_subheading)}/>
<ColumnLink icon='book' text={intl.formatMessage(messages.info)} href='/about/more' />
<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='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
<div className='static-content getting-started'>
<p><FormattedMessage id='getting_started.open_source_notice' defaultMessage='Mastodon is open source software. You can contribute or report issues on GitHub at {github}. {apps}.' values={{ github: <a href="https://github.com/tootsuite/mastodon" target="_blank">tootsuite/mastodon</a>, apps: <a href="https://github.com/tootsuite/documentation/blob/master/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></a> }} /></p>
</div>
</div>
</Column>
);
};
GettingStarted.propTypes = {
intl: PropTypes.object.isRequired,
me: ImmutablePropTypes.map.isRequired
};
export default connect(mapStateToProps)(injectIntl(GettingStarted));

View file

@ -1,68 +0,0 @@
const bg = {
"column_back_button.label": "Назад",
"lightbox.close": "Затвори",
"loading_indicator.label": "Зареждане...",
"status.mention": "Споменаване",
"status.delete": "Изтриване",
"status.reply": "Отговор",
"status.reblog": "Споделяне",
"status.favourite": "Предпочитани",
"status.reblogged_by": "{name} сподели",
"status.sensitive_warning": "Деликатно съдържание",
"status.sensitive_toggle": "Покажи",
"video_player.toggle_sound": "Звук",
"account.mention": "Споменаване",
"account.edit_profile": "Редактирай профила си",
"account.unblock": "Не блокирай",
"account.unfollow": "Не следвай",
"account.block": "Блокирай",
"account.follow": "Последвай",
"account.posts": "Публикации",
"account.follows": "Следвам",
"account.followers": "Последователи",
"account.follows_you": "Твой последовател",
"account.requested": "В очакване на одобрение",
"getting_started.heading": "Първи стъпки",
"getting_started.about_addressing": "Можеш да последваш потребител, ако знаеш потребителското му име и домейна, на който се намира, като в полето за търсене ги въведеш по този начин: име@домейн",
"getting_started.about_shortcuts": "Ако с търсения потребител се намирате на един и същ домейн, достатъчно е да въведеш само името. Същото важи и за споменаване на хора в публикации.",
"getting_started.about_developer": "Можеш да потърсиш разработчика на този проект като: Gargron@mastodon.social",
"getting_started.open_source_notice": "Mastodon е софтуер с отворен код. Можеш да помогнеш или да докладваш за проблеми в Github: {github}.",
"column.home": "Начало",
"column.mentions": "Споменавания",
"column.public": "Публичен канал",
"column.notifications": "Известия",
"tabs_bar.compose": "Съставяне",
"tabs_bar.home": "Начало",
"tabs_bar.mentions": "Споменавания",
"tabs_bar.public": "Публичен канал",
"tabs_bar.notifications": "Известия",
"compose_form.placeholder": "Какво си мислиш?",
"compose_form.publish": "Раздумай",
"compose_form.sensitive": "Отбележи съдържанието като деликатно",
"compose_form.spoiler": "Скрий текста зад предупреждение",
"compose_form.private": "Отбележи като поверително",
"compose_form.privacy_disclaimer": "Поверителни публикации ще бъдат изпратени до споменатите потребители на {domains}. Доверяваш ли се на {domainsCount, plural, one {that server} other {those servers}}, че няма да издаде твоята публикация?",
"compose_form.unlisted": "Не показвай в публичния канал",
"navigation_bar.edit_profile": "Редактирай профил",
"navigation_bar.preferences": "Предпочитания",
"navigation_bar.public_timeline": "Публичен канал",
"navigation_bar.logout": "Излизане",
"reply_indicator.cancel": "Отказ",
"search.placeholder": "Търсене",
"search.account": "Акаунт",
"search.hashtag": "Хаштаг",
"upload_button.label": "Добави медия",
"upload_form.undo": "Отмяна",
"notification.follow": "{name} те последва",
"notification.favourite": "{name} хареса твоята публикация",
"notification.reblog": "{name} сподели твоята публикация",
"notification.mention": "{name} те спомена",
"notifications.column_settings.alert": "Десктоп известия",
"notifications.column_settings.show": "Покажи в колона",
"notifications.column_settings.follow": "Нови последователи:",
"notifications.column_settings.favourite": "Предпочитани:",
"notifications.column_settings.mention": "Споменавания:",
"notifications.column_settings.reblog": "Споделяния:",
};
export default bg;

View file

@ -1,68 +0,0 @@
const eo = {
"column_back_button.label": "Reveni",
"lightbox.close": "Fermi",
"loading_indicator.label": "Ŝarĝanta...",
"status.mention": "Mencii @{name}",
"status.delete": "Forigi",
"status.reply": "Respondi",
"status.reblog": "Diskonigi",
"status.favourite": "Favori",
"status.reblogged_by": "{name} diskonigita",
"status.sensitive_warning": "Tikla enhavo",
"status.sensitive_toggle": "Alklaki por vidi",
"video_player.toggle_sound": "Aktivigi sonojn",
"account.mention": "Mencii @{name}",
"account.edit_profile": "Redakti la profilon",
"account.unblock": "Malbloki @{name}",
"account.unfollow": "Malsekvi",
"account.block": "Bloki @{name}",
"account.follow": "Sekvi",
"account.posts": "Mesaĝoj",
"account.follows": "Sekvatoj",
"account.followers": "Sekvantoj",
"account.follows_you": "Sekvas vin",
"account.requested": "Atendas aprobon",
"getting_started.heading": "Por komenci",
"getting_started.about_addressing": "Vi povas sekvi homojn se vi konas la uzantnomon kaj domajnon tajpinte retpoŝtecan adreson en la serĉilon.",
"getting_started.about_shortcuts": "Se la celita uzanto troviĝas en la sama domajno de vi, uzi nur la uzantnomon sufiĉos. La sama regulo validas por mencii aliajn uzantojn en mesaĝo.",
"getting_started.open_source_notice": "Mastodon estas malfermitkoda programo. Vi povas kontribui aŭ raporti problemojn en github je {github}. {apps}.",
"column.home": "Hejmo",
"column.community": "Loka tempolinio",
"column.public": "Fratara tempolinio",
"column.notifications": "Sciigoj",
"tabs_bar.compose": "Ekskribi",
"tabs_bar.home": "Hejmo",
"tabs_bar.mentions": "Sciigoj",
"tabs_bar.public": "Fratara tempolinio",
"tabs_bar.notifications": "Sciigoj",
"compose_form.placeholder": "Pri kio vi pensas?",
"compose_form.publish": "Hup",
"compose_form.sensitive": "Marki ke la enhavo estas tikla",
"compose_form.spoiler": "Kaŝi la tekston malantaŭ averto",
"compose_form.private": "Marki ke la enhavo estas privata",
"compose_form.privacy_disclaimer": "Via privata mesaĝo estos sendita nur al menciitaj uzantoj en {domains}. Ĉu vi fidas {domainsCount, plural, one {tiun servilon} other {tiujn servilojn}}? Mesaĝa privateco funkcias nur en aperaĵoj de Mastodon. Se {domains} {domainsCount, plural, one {ne estas aperaĵo de Mastodon} other {ne estas aperaĵoj de Mastodon}}, estos neniu indiko ke via mesaĝo estas privata, kaj ĝi povus esti diskonigita aŭ videbligita al necelitaj ricevantoj.",
"compose_form.unlisted": "Ne afiŝi en publikaj tempolinioj",
"navigation_bar.edit_profile": "Redakti la profilon",
"navigation_bar.preferences": "Preferoj",
"navigation_bar.community_timeline": "Loka tempolinio",
"navigation_bar.public_timeline": "Fratara tempolinio",
"navigation_bar.logout": "Elsaluti",
"reply_indicator.cancel": "Rezigni",
"search.placeholder": "Serĉi",
"search.account": "Konto",
"search.hashtag": "Kradvorto",
"upload_button.label": "Aldoni enhavaĵon",
"upload_form.undo": "Malfari",
"notification.follow": "{name} sekvis vin",
"notification.favourite": "{name} favoris vian mesaĝon",
"notification.reblog": "{name} diskonigis vian mesaĝon",
"notification.mention": "{name} menciis vin",
"notifications.column_settings.alert": "Retumilaj atentigoj",
"notifications.column_settings.show": "Montri en kolono",
"notifications.column_settings.follow": "Novaj sekvantoj:",
"notifications.column_settings.favourite": "Favoroj:",
"notifications.column_settings.mention": "Mencioj:",
"notifications.column_settings.reblog": "Diskonigoj:",
};
export default eo;