Merge branch 'master' into skylight
This commit is contained in:
commit
f4ab6b9f2c
4
.gitignore
vendored
4
.gitignore
vendored
|
@ -28,3 +28,7 @@ neo4j/
|
||||||
|
|
||||||
# Ignore Capistrano customizations
|
# Ignore Capistrano customizations
|
||||||
config/deploy/*
|
config/deploy/*
|
||||||
|
|
||||||
|
|
||||||
|
# Ignore IDE files
|
||||||
|
.vscode/
|
||||||
|
|
|
@ -7,7 +7,7 @@ There are three ways in which you can contribute to this repository:
|
||||||
2. By working on the back-end application
|
2. By working on the back-end application
|
||||||
3. By working on the front-end application
|
3. By working on the front-end application
|
||||||
|
|
||||||
Choosing what to work on in a large open source project is not easy. The list of GitHub issues may provide some ideas, but not every feature request has been greenlit. Likewise, not every change or feature that resolves a personal itch will be merged into the main repository. Some communication ahead of time may be wise.
|
Choosing what to work on in a large open source project is not easy. The list of GitHub issues may provide some ideas, but not every feature request has been greenlit. Likewise, not every change or feature that resolves a personal itch will be merged into the main repository. Some communication ahead of time may be wise. If your addition creates a new feature or setting, or otherwise changes how things work in some substantial way, please remember to submit a correlating pull request to document your changes in the [documentation](http://github.com/tootsuite/documentation).
|
||||||
|
|
||||||
Below are the guidelines for working on pull requests:
|
Below are the guidelines for working on pull requests:
|
||||||
|
|
||||||
|
@ -41,3 +41,4 @@ It is expected that you have a working development environment set up (see back-
|
||||||
* If you are introducing new strings, they must be using localization methods
|
* If you are introducing new strings, they must be using localization methods
|
||||||
|
|
||||||
If the JavaScript or CSS assets won't compile due to a syntax error, it's a good sign that the pull request isn't ready for submission yet.
|
If the JavaScript or CSS assets won't compile due to a syntax error, it's a good sign that the pull request isn't ready for submission yet.
|
||||||
|
|
||||||
|
|
59
README.md
59
README.md
|
@ -67,23 +67,52 @@ Consult the example configuration file, `.env.production.sample` for the full li
|
||||||
|
|
||||||
[![](https://images.microbadger.com/badges/version/gargron/mastodon.svg)](https://microbadger.com/images/gargron/mastodon "Get your own version badge on microbadger.com") [![](https://images.microbadger.com/badges/image/gargron/mastodon.svg)](https://microbadger.com/images/gargron/mastodon "Get your own image badge on microbadger.com")
|
[![](https://images.microbadger.com/badges/version/gargron/mastodon.svg)](https://microbadger.com/images/gargron/mastodon "Get your own version badge on microbadger.com") [![](https://images.microbadger.com/badges/image/gargron/mastodon.svg)](https://microbadger.com/images/gargron/mastodon "Get your own image badge on microbadger.com")
|
||||||
|
|
||||||
The project now includes a `Dockerfile` and a `docker-compose.yml` file (which requires at least docker-compose version `1.10.0`). You need to turn `.env.production.sample` into `.env.production` with all the variables set before you can:
|
The project now includes a `Dockerfile` and a `docker-compose.yml` file (which requires at least docker-compose version `1.10.0`).
|
||||||
|
|
||||||
|
Review the settings in `docker-compose.yml`. Note that it is not default to store the postgresql database and redis databases in a persistent storage location,
|
||||||
|
so you may need or want to adjust the settings there.
|
||||||
|
|
||||||
|
Before running the first time, you need to build the images:
|
||||||
|
|
||||||
docker-compose build
|
docker-compose build
|
||||||
|
|
||||||
And finally
|
Then, you need to fill in the `.env.production` file:
|
||||||
|
|
||||||
docker-compose up -d
|
cp .env.production.sample .env.production
|
||||||
|
nano .env.production
|
||||||
|
|
||||||
As usual, the first thing you would need to do would be to run migrations:
|
Do NOT change the `REDIS_*` or `DB_*` settings when running with the default docker configurations.
|
||||||
|
|
||||||
|
You will need to fill in, at least: `LOCAL_DOMAIN`, `LOCAL_HTTPS`, `PAPERCLIP_SECRET`, `SECRET_KEY_BASE`, `OTP_SECRET`, and the `SMTP_*` settings. To generate the `PAPERCLIP_SECRET`, `SECRET_KEY_BASE`, and `OTP_SECRET`, you may use:
|
||||||
|
|
||||||
|
docker-compose run --rm web rake secret
|
||||||
|
|
||||||
|
Do this once for each of those keys, and copy the result into the `.env.production` file in the appropriate field.
|
||||||
|
|
||||||
|
Then you should run the `db:migrate` command to create the database, or migrate it from an older release:
|
||||||
|
|
||||||
docker-compose run --rm web rails db:migrate
|
docker-compose run --rm web rails db:migrate
|
||||||
|
|
||||||
And since the instance running in the container will be running in production mode, you need to pre-compile assets:
|
Then, you will also need to precompile the assets:
|
||||||
|
|
||||||
docker-compose run --rm web rails assets:precompile
|
docker-compose run --rm web rails assets:precompile
|
||||||
|
|
||||||
The container has two volumes, for the assets and for user uploads. The default docker-compose.yml maps them to the repository's `public/assets` and `public/system` directories, you may wish to put them somewhere else. Likewise, the PostgreSQL and Redis images have data containers that you may wish to map somewhere where you know how to find them and back them up.
|
before you can launch the docker image with:
|
||||||
|
|
||||||
|
docker-compose up
|
||||||
|
|
||||||
|
If you wish to run this as a daemon process instead of monitoring it on console, use instead:
|
||||||
|
|
||||||
|
docker-compose up -d
|
||||||
|
|
||||||
|
Then you may login to your new Mastodon instance by browsing to http://localhost:3000/
|
||||||
|
|
||||||
|
Following that, make sure that you read the [production guide](docs/Running-Mastodon/Production-guide.md). You are probably going to want to understand how
|
||||||
|
to configure Nginx to make your Mastodon instance available to the rest of the world.
|
||||||
|
|
||||||
|
The container has two volumes, for the assets and for user uploads, and optionally two more, for the postgresql and redis databases.
|
||||||
|
|
||||||
|
The default docker-compose.yml maps them to the repository's `public/assets` and `public/system` directories, you may wish to put them somewhere else. Likewise, the PostgreSQL and Redis images have data containers that you may wish to map somewhere where you know how to find them and back them up.
|
||||||
|
|
||||||
**Note**: The `--rm` option for docker-compose will remove the container that is created to run a one-off command after it completes. As data is stored in volumes it is not affected by that container clean-up.
|
**Note**: The `--rm` option for docker-compose will remove the container that is created to run a one-off command after it completes. As data is stored in volumes it is not affected by that container clean-up.
|
||||||
|
|
||||||
|
@ -103,17 +132,11 @@ Running any of these tasks via docker-compose would look like this:
|
||||||
|
|
||||||
This approach makes updating to the latest version a real breeze.
|
This approach makes updating to the latest version a real breeze.
|
||||||
|
|
||||||
git pull
|
1. `git pull` to download updates from the repository
|
||||||
|
2. `docker-compose build` to compile the Docker image out of the changed source files
|
||||||
To pull down the updates, re-run
|
3. (optional) `docker-compose run --rm web rails db:migrate` to perform database migrations. Does nothing if your database is up to date
|
||||||
|
4. (optional) `docker-compose run --rm web rails assets:precompile` to compile new JS and CSS assets
|
||||||
docker-compose build
|
5. `docker-compose up -d` to re-create (restart) containers and pick up the changes
|
||||||
|
|
||||||
And finally,
|
|
||||||
|
|
||||||
docker-compose up -d
|
|
||||||
|
|
||||||
Which will re-create the updated containers, leaving databases and data as is. Depending on what files have been updated, you might need to re-run migrations and asset compilation.
|
|
||||||
|
|
||||||
## Deployment without Docker
|
## Deployment without Docker
|
||||||
|
|
||||||
|
@ -129,7 +152,7 @@ Docker is great for quickly trying out software, but it has its drawbacks too. I
|
||||||
|
|
||||||
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
|
[![Deploy](https://www.herokucdn.com/deploy/button.svg)](https://heroku.com/deploy)
|
||||||
|
|
||||||
Mastodon can theoretically run indefinitely on a free [Heroku](https://heroku.com) app. [You can view a guide for deployment on Heroku here.](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Heroku-guide.md)
|
Mastodon can run on [Heroku](https://heroku.com), but it gets expensive and impractical due to how Heroku prices resource usage. [You can view a guide for deployment on Heroku here](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Heroku-guide.md), but you have been warned.
|
||||||
|
|
||||||
## Development with Vagrant
|
## Development with Vagrant
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1 @@
|
||||||
<svg xmlns="http://www.w3.org/2000/svg" width="1000" height="1000" viewBox="0 0 1000 1000">
|
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 1000 1000" height="1000" width="1000"><g fill="#189efc"><path d="M500 0A500 500 0 0 0 0 500a500 500 0 0 0 500 500 500 500 0 0 0 500-500A500 500 0 0 0 500 0zm-2.5 271.1h107.24c-20.56 14.471-27.24 57.064-27.24 78.927v202.145c0 43.726-35.202 78.928-80 78.928s-80-35.202-80-78.928V350.027c0-43.725 35.202-78.927 80-78.927zm-276 48.9c44.798 0 80 35.202 80 78.928v202.144c0 21.863 6.68 64.456 27.24 78.928H221.5c-44.798 0-80-35.202-80-78.928V398.928c0-43.726 35.202-78.928 80-78.928zm550.24 0c44.799 0 80 35.202 80 78.928v202.144c0 43.726-35.201 78.928-80 78.928H664.5c20.56-14.472 27.24-57.065 27.24-78.928V398.928c0-43.726 35.202-78.928 80-78.928z"/><g transform="translate(-2)"><circle cx="223.5" cy="410.5" r="27.5"/><circle cx="223.5" cy="500.5" r="27.5"/><circle cx="223.5" cy="590.5" r="27.5"/></g><g transform="matrix(1 0 0 -1 274 951)"><circle cx="223.5" cy="410.5" r="27.5"/><circle cx="223.5" cy="500.5" r="27.5"/><circle cx="223.5" cy="590.5" r="27.5"/></g><g transform="matrix(-1 0 0 1 995 0)"><circle cx="223.5" cy="410.5" r="27.5"/><circle cx="223.5" cy="500.5" r="27.5"/><circle cx="223.5" cy="590.5" r="27.5"/></g></g></svg>
|
||||||
<path d="M527.194 543.7a28.362 28.362 0 0 0-56.723 0 25.73 25.73 0 0 0 2.67 11.674 26.42 26.42 0 0 0 5.672 8.34 28.2 28.2 0 0 0 40.04 0 31.87 31.87 0 0 0 6.006-8.34 28.8 28.8 0 0 0 2.336-11.674m-48.382-113.413a28.308 28.308 0 1 0 40.04 40.027 37.2 37.2 0 0 0 4.67-5.67 28.092 28.092 0 0 0 3.67-14.343 27.29 27.29 0 0 0-8.34-20.012 28.24 28.24 0 0 0-5.006-4 26.958 26.958 0 0 0-15.015-4.336 27.31 27.31 0 0 0-20.02 8.34m20.02-101.735a28.476 28.476 0 1 0 20.02 8.34 27.31 27.31 0 0 0-20.02-8.34M231.9 573.717a28.18 28.18 0 1 0 8.342 20.012 27.308 27.308 0 0 0-8.342-20.014m-40.04-93.4a28.352 28.352 0 0 0 20.02 48.366 26.958 26.958 0 0 0 15.015-4.336 28.255 28.255 0 0 0 5.005-4 27.29 27.29 0 0 0 8.342-20.013 28.09 28.09 0 0 0-3.67-14.343 37.21 37.21 0 0 0-4.67-5.67 28.2 28.2 0 0 0-40.04 0m40.04-93.4a28.2 28.2 0 0 0-40.04 0 26.425 26.425 0 0 0-5.673 8.34 25.73 25.73 0 0 0-2.67 11.673 28.315 28.315 0 0 0 48.38 20.018 27.29 27.29 0 0 0 8.342-20.012 28.8 28.8 0 0 0-2.336-11.674 31.87 31.87 0 0 0-6.006-8.34m550.55 178.453a28.476 28.476 0 1 0 20.02 8.34 27.31 27.31 0 0 0-20.02-8.34m20.02-85.057a28.2 28.2 0 0 0-40.04 0 37.2 37.2 0 0 0-4.672 5.67 28.092 28.092 0 0 0-3.67 14.343 27.29 27.29 0 0 0 8.342 20.013 28.248 28.248 0 0 0 5.005 4 26.96 26.96 0 0 0 15.015 4.336 28.3 28.3 0 0 0 20.02-48.366m-46.046-85.057a28.8 28.8 0 0 0-2.336 11.673 28.362 28.362 0 0 0 56.723 0 25.73 25.73 0 0 0-2.668-11.674 26.427 26.427 0 0 0-5.672-8.34 28.2 28.2 0 0 0-40.04 0 31.86 31.86 0 0 0-6.007 8.343z" fill="#2b90d9"/>
|
|
||||||
<path d="M853.52 146.764Q707.04 0 499.833 0 292.96 0 146.48 146.764 0 293.2 0 500q0 207.138 146.48 353.57T499.833 1000q207.207 0 353.687-146.43T1000 500q0-206.8-146.48-353.236zM213.547 708.806h-3.337q-43.043 0-73.407-30.02-30.03-30.354-30.03-73.382V395.93v-.666q1.335-41.027 30.03-69.713 30.364-30.35 73.407-30.35t73.073 30.354q29.363 29.02 30.364 70.38V615.41q2.336 55.037 46.713 93.4zM600.6 554.7q-1 41.36-30.364 70.38-30.03 30.353-73.073 30.354t-73.407-30.354q-28.7-28.686-30.03-69.713V345.23q0-43.03 30.03-73.382 30.364-30.02 73.407-30.02h150.15q-44.378 38.36-46.713 93.4zm286.954 50.7q0 43.03-30.03 73.382-30.364 30.02-73.407 30.02h-150.15q44.378-38.36 46.713-93.4v-219.47q1-41.362 30.364-70.38 30.03-30.355 73.073-30.355t73.407 30.354q28.7 28.687 30.03 69.714V605.4z" fill="#2b90d9"/>
|
|
||||||
</svg>
|
|
Before Width: | Height: | Size: 2.3 KiB After Width: | Height: | Size: 1.2 KiB |
|
@ -41,15 +41,18 @@ import Report from '../features/report';
|
||||||
import { IntlProvider, addLocaleData } from 'react-intl';
|
import { IntlProvider, addLocaleData } from 'react-intl';
|
||||||
import en from 'react-intl/locale-data/en';
|
import en from 'react-intl/locale-data/en';
|
||||||
import de from 'react-intl/locale-data/de';
|
import de from 'react-intl/locale-data/de';
|
||||||
import es from 'react-intl/locale-data/es';
|
|
||||||
import fr from 'react-intl/locale-data/fr';
|
|
||||||
import pt from 'react-intl/locale-data/pt';
|
|
||||||
import hu from 'react-intl/locale-data/hu';
|
|
||||||
import uk from 'react-intl/locale-data/uk';
|
|
||||||
import fi from 'react-intl/locale-data/fi';
|
|
||||||
import eo from 'react-intl/locale-data/eo';
|
import eo from 'react-intl/locale-data/eo';
|
||||||
import ru from 'react-intl/locale-data/ru';
|
import es from 'react-intl/locale-data/es';
|
||||||
|
import fi from 'react-intl/locale-data/fi';
|
||||||
|
import fr from 'react-intl/locale-data/fr';
|
||||||
|
import hu from 'react-intl/locale-data/hu';
|
||||||
import ja from 'react-intl/locale-data/ja';
|
import ja from 'react-intl/locale-data/ja';
|
||||||
|
import pt from 'react-intl/locale-data/pt';
|
||||||
|
import no from 'react-intl/locale-data/no';
|
||||||
|
import ru from 'react-intl/locale-data/ru';
|
||||||
|
import uk from 'react-intl/locale-data/uk';
|
||||||
|
import zh from 'react-intl/locale-data/zh';
|
||||||
|
import { localeData as zh_hk } from '../locales/zh-hk';
|
||||||
|
|
||||||
import getMessagesForLocale from '../locales';
|
import getMessagesForLocale from '../locales';
|
||||||
import { hydrateStore } from '../actions/store';
|
import { hydrateStore } from '../actions/store';
|
||||||
|
@ -64,7 +67,22 @@ const browserHistory = useRouterHistory(createBrowserHistory)({
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
||||||
addLocaleData([...en, ...de, ...es, ...fr, ...pt, ...hu, ...uk, ...fi, ...eo, ...ru, ...ja]);
|
addLocaleData([
|
||||||
|
...en,
|
||||||
|
...de,
|
||||||
|
...eo,
|
||||||
|
...es,
|
||||||
|
...fi,
|
||||||
|
...fr,
|
||||||
|
...hu,
|
||||||
|
...ja,
|
||||||
|
...pt,
|
||||||
|
...no,
|
||||||
|
...ru,
|
||||||
|
...uk,
|
||||||
|
...zh,
|
||||||
|
...zh_hk,
|
||||||
|
]);
|
||||||
|
|
||||||
|
|
||||||
const Mastodon = React.createClass({
|
const Mastodon = React.createClass({
|
||||||
|
|
|
@ -43,7 +43,7 @@ const GettingStarted = ({ intl, me }) => {
|
||||||
|
|
||||||
<div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
|
<div className='scrollable optionally-scrollable' style={{ display: 'flex', flexDirection: 'column' }}>
|
||||||
<div className='static-content getting-started'>
|
<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/mastodon/blob/master/docs/Using-Mastodon/Apps.md" target="_blank"><FormattedMessage id='getting_started.apps' defaultMessage='Various apps are available' /></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}. {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>
|
||||||
</div>
|
</div>
|
||||||
</Column>
|
</Column>
|
||||||
|
|
|
@ -5,28 +5,35 @@ const es = {
|
||||||
"status.mention": "Mencionar",
|
"status.mention": "Mencionar",
|
||||||
"status.delete": "Borrar",
|
"status.delete": "Borrar",
|
||||||
"status.reply": "Responder",
|
"status.reply": "Responder",
|
||||||
"status.reblog": "Republicar",
|
"status.reblog": "Retoot",
|
||||||
"status.favourite": "Favorito",
|
"status.favourite": "Favorito",
|
||||||
"status.reblogged_by": "{name} republicado",
|
"status.reblogged_by": "Retooteado por {name}",
|
||||||
|
"status.sensitive_warning": "Contenido sensible",
|
||||||
|
"status.sensitive_toggle": "Click para ver",
|
||||||
|
"status.show_more": "Mostrar más",
|
||||||
|
"status.show_less": "Mostrar menos",
|
||||||
|
"status.open": "Expandir estado",
|
||||||
|
"status.report": "Reportar",
|
||||||
"video_player.toggle_sound": "Act/Desac. sonido",
|
"video_player.toggle_sound": "Act/Desac. sonido",
|
||||||
"account.mention": "Mención",
|
"account.mention": "Mencionar",
|
||||||
"account.edit_profile": "Editar perfil",
|
"account.edit_profile": "Editar perfil",
|
||||||
"account.unblock": "Desbloquear",
|
"account.unblock": "Desbloquear",
|
||||||
"account.unfollow": "Dejar de seguir",
|
"account.unfollow": "Dejar de seguir",
|
||||||
|
"account.mute": "Silenciar",
|
||||||
"account.block": "Bloquear",
|
"account.block": "Bloquear",
|
||||||
"account.follow": "Seguir",
|
"account.follow": "Seguir",
|
||||||
"account.block": "Bloquear",
|
|
||||||
"account.posts": "Publicaciones",
|
"account.posts": "Publicaciones",
|
||||||
"account.follows": "Seguir",
|
"account.follows": "Seguir",
|
||||||
"account.followers": "Seguidores",
|
"account.followers": "Seguidores",
|
||||||
"account.follows_you": "Te sigue",
|
"account.follows_you": "Te sigue",
|
||||||
|
"account.requested": "Esperando aprobación",
|
||||||
"getting_started.heading": "Primeros pasos",
|
"getting_started.heading": "Primeros pasos",
|
||||||
"getting_started.about_addressing": "Puedes seguir a gente si conoces su nombre de usuario y el dominio en el que están registrados, introduciendo algo similar a una dirección de correo electrónico en el formulario en la parte superior de la barra lateral.",
|
"getting_started.about_addressing": "Puedes seguir a gente si conoces su nombre de usuario y el dominio en el que están registrados, introduciendo algo similar a una dirección de correo electrónico en el formulario en la parte superior de la barra lateral.",
|
||||||
"getting_started.about_shortcuts": "Si el usuario que buscas está en el mismo dominio que tú, simplemente funcionará introduciendo el nombre de usuario. La misma regla se aplica para mencionar a usuarios.",
|
"getting_started.about_shortcuts": "Si el usuario que buscas está en el mismo dominio que tú, simplemente funcionará introduciendo el nombre de usuario. La misma regla se aplica para mencionar a usuarios.",
|
||||||
"getting_started.about_developer": "Puedes seguir al desarrollador de este proyecto en Gargron@mastodon.social",
|
"getting_started.open_source_notice": "Mastodon es software libre. Puedes contribuir o reportar errores en {github}. {apps}.",
|
||||||
"column.home": "Inicio",
|
"column.home": "Inicio",
|
||||||
"column.mentions": "Menciones",
|
"column.community": "Historia local",
|
||||||
"column.public": "Historia pública",
|
"column.public": "Historia federada",
|
||||||
"column.notifications": "Notificaciones",
|
"column.notifications": "Notificaciones",
|
||||||
"tabs_bar.compose": "Redactar",
|
"tabs_bar.compose": "Redactar",
|
||||||
"tabs_bar.home": "Inicio",
|
"tabs_bar.home": "Inicio",
|
||||||
|
@ -34,23 +41,47 @@ const es = {
|
||||||
"tabs_bar.public": "Público",
|
"tabs_bar.public": "Público",
|
||||||
"tabs_bar.notifications": "Notificaciones",
|
"tabs_bar.notifications": "Notificaciones",
|
||||||
"compose_form.placeholder": "¿En qué estás pensando?",
|
"compose_form.placeholder": "¿En qué estás pensando?",
|
||||||
"compose_form.publish": "Publicar",
|
"compose_form.publish": "Tootear",
|
||||||
"compose_form.sensitive": "Marcar el contenido como sensible",
|
"compose_form.sensitive": "Marcar contenido como sensible",
|
||||||
"compose_form.unlisted": "Privado",
|
"compose_form.spoiler": "Ocultar texto tras advertencia",
|
||||||
|
"compose_form.spoiler_placeholder": "Advertencia de contenido",
|
||||||
|
"composer_form.private": "Marcar como privado",
|
||||||
|
"composer_form.privacy_disclaimer": "Tu estado se mostrará a los usuarios mencionados en {domains}. Tu estado podrá ser visto en otras instancias, quizás no quieras que tu estado sea visto por otros usuarios.",
|
||||||
|
"compose_form.unlisted": "No mostrar en la historia federada",
|
||||||
"navigation_bar.edit_profile": "Editar perfil",
|
"navigation_bar.edit_profile": "Editar perfil",
|
||||||
"navigation_bar.preferences": "Preferencias",
|
"navigation_bar.preferences": "Preferencias",
|
||||||
"navigation_bar.public_timeline": "Público",
|
"navigation_bar.community_timeline": "Historia local",
|
||||||
|
"navigation_bar.public_timeline": "Historia federada",
|
||||||
|
"navigation_bar.favourites": "Favoritos",
|
||||||
|
"navigation_bar.blocks": "Usuarios bloqueados",
|
||||||
|
"navigation_bar.info": "Información adicional",
|
||||||
"navigation_bar.logout": "Cerrar sesión",
|
"navigation_bar.logout": "Cerrar sesión",
|
||||||
"reply_indicator.cancel": "Cancelar",
|
"reply_indicator.cancel": "Cancelar",
|
||||||
"search.placeholder": "Buscar",
|
"search.placeholder": "Buscar",
|
||||||
"search.account": "Cuenta",
|
"search.account": "Cuenta",
|
||||||
"search.hashtag": "Etiqueta",
|
"search.hashtag": "Etiqueta",
|
||||||
"upload_button.label": "Añadir medio",
|
"upload_button.label": "Subir multimedia",
|
||||||
"upload_form.undo": "Deshacer",
|
"upload_form.undo": "Deshacer",
|
||||||
"notification.follow": "{name} le esta ahora siguiendo",
|
"notification.follow": "{name} te empezó a seguir",
|
||||||
"notification.favourite": "{name} marcó como favorito su estado",
|
"notification.favourite": "{name} marcó tu estado como favorito",
|
||||||
"notification.reblog": "{name} volvió a publicar su estado",
|
"notification.reblog": "{name} ha retooteado tu estado",
|
||||||
"notification.mention": "Fue mencionado por {name}"
|
"notification.mention": "{name} te ha mencionado",
|
||||||
|
"notifications.column_settings.alert": "Notificaciones de escritorio",
|
||||||
|
"notifications.column_settings.show": "Mostrar en columna",
|
||||||
|
"notifications.column_settings.follow": "Nuevos seguidores:",
|
||||||
|
"notifications.column_settings.favourite": "Favoritos:",
|
||||||
|
"notifications.column_settings.mention": "Menciones:",
|
||||||
|
"notifications.column_settings.reblog": "Retoots:",
|
||||||
|
"emoji_button.label": "Insertar emoji",
|
||||||
|
"privacy.public.short": "Público",
|
||||||
|
"privacy.public.long": "Mostrar en la historia federada",
|
||||||
|
"privacy.unlisted.short": "Sin federar",
|
||||||
|
"privacy.unlisted.long": "No mostrar en la historia federada",
|
||||||
|
"privacy.private.short": "Privado",
|
||||||
|
"privacy.private.long": "Sólo mostrar a seguidores",
|
||||||
|
"privacy.direct.short": "Directo",
|
||||||
|
"privacy.direct.long": "Sólo mostrar a los usuarios mencionados",
|
||||||
|
"privacy.change": "Ajustar privacidad"
|
||||||
};
|
};
|
||||||
|
|
||||||
export default es;
|
export default es;
|
||||||
|
|
|
@ -34,7 +34,7 @@ const fr = {
|
||||||
"account.report": "Signaler",
|
"account.report": "Signaler",
|
||||||
"account.disclaimer": "Ce compte est situé sur une autre instance. Les nombres peuvent être plus grands.",
|
"account.disclaimer": "Ce compte est situé sur une autre instance. Les nombres peuvent être plus grands.",
|
||||||
"getting_started.heading": "Pour commencer",
|
"getting_started.heading": "Pour commencer",
|
||||||
"getting_started.about_addressing": "Vous pouvez suivre les statuts de quelqu’un en entrant dans le champs de recherche leur identifiant et le domaine de leur instance, séparés par un @ à la manière d’une adresse courriel.",
|
"getting_started.about_addressing": "Vous pouvez suivre les statuts de quelqu’un en entrant dans le champ de recherche leur identifiant et le domaine de leur instance, séparés par un @ à la manière d’une adresse courriel.",
|
||||||
"getting_started.about_shortcuts": "Si cette personne utilise la même instance que vous, l’identifiant suffit. C’est le même principe pour mentionner quelqu’un dans vos statuts.",
|
"getting_started.about_shortcuts": "Si cette personne utilise la même instance que vous, l’identifiant suffit. C’est le même principe pour mentionner quelqu’un dans vos statuts.",
|
||||||
"getting_started.about_developer": "Pour suivre le développeur de ce projet, c’est Gargron@mastodon.social",
|
"getting_started.about_developer": "Pour suivre le développeur de ce projet, c’est Gargron@mastodon.social",
|
||||||
"getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer et envoyer vos commentaires et rapports de bogues via {github} sur GitHub.",
|
"getting_started.open_source_notice": "Mastodon est un logiciel libre. Vous pouvez contribuer et envoyer vos commentaires et rapports de bogues via {github} sur GitHub.",
|
||||||
|
@ -107,7 +107,7 @@ const fr = {
|
||||||
"privacy.private.short": "Privé",
|
"privacy.private.short": "Privé",
|
||||||
"privacy.private.long": "N’afficher que pour vos abonné⋅e⋅s",
|
"privacy.private.long": "N’afficher que pour vos abonné⋅e⋅s",
|
||||||
"privacy.direct.short": "Direct",
|
"privacy.direct.short": "Direct",
|
||||||
"privacy.direct.long": "N’afficher que pour les personnes mentionné⋅e⋅s",
|
"privacy.direct.long": "N’afficher que pour les personnes mentionnées",
|
||||||
"privacy.change": "Ajuster la confidentialité du message",
|
"privacy.change": "Ajuster la confidentialité du message",
|
||||||
"media_gallery.toggle_visible": "Modifier la visibilité",
|
"media_gallery.toggle_visible": "Modifier la visibilité",
|
||||||
"missing_indicator.label": "Non trouvé",
|
"missing_indicator.label": "Non trouvé",
|
||||||
|
|
|
@ -3,12 +3,14 @@ import de from './de';
|
||||||
import es from './es';
|
import es from './es';
|
||||||
import hu from './hu';
|
import hu from './hu';
|
||||||
import fr from './fr';
|
import fr from './fr';
|
||||||
|
import no from './no';
|
||||||
import pt from './pt';
|
import pt from './pt';
|
||||||
import uk from './uk';
|
import uk from './uk';
|
||||||
import fi from './fi';
|
import fi from './fi';
|
||||||
import eo from './eo';
|
import eo from './eo';
|
||||||
import ru from './ru';
|
import ru from './ru';
|
||||||
import ja from './ja';
|
import ja from './ja';
|
||||||
|
import zh_hk from './zh-hk';
|
||||||
|
|
||||||
|
|
||||||
const locales = {
|
const locales = {
|
||||||
|
@ -17,13 +19,14 @@ const locales = {
|
||||||
es,
|
es,
|
||||||
hu,
|
hu,
|
||||||
fr,
|
fr,
|
||||||
|
no,
|
||||||
pt,
|
pt,
|
||||||
uk,
|
uk,
|
||||||
fi,
|
fi,
|
||||||
eo,
|
eo,
|
||||||
ru,
|
ru,
|
||||||
ja
|
ja,
|
||||||
|
'zh-HK': zh_hk,
|
||||||
};
|
};
|
||||||
|
|
||||||
export default function getMessagesForLocale (locale) {
|
export default function getMessagesForLocale (locale) {
|
||||||
|
|
|
@ -2,42 +2,45 @@ const ja = {
|
||||||
"column_back_button.label": "戻る",
|
"column_back_button.label": "戻る",
|
||||||
"lightbox.close": "閉じる",
|
"lightbox.close": "閉じる",
|
||||||
"loading_indicator.label": "読み込み中...",
|
"loading_indicator.label": "読み込み中...",
|
||||||
"status.mention": "@{name}さんへの返信",
|
"status.mention": "@{name} さんへの返信",
|
||||||
"status.delete": "削除",
|
"status.delete": "削除",
|
||||||
"status.reply": "返信",
|
"status.reply": "返信",
|
||||||
"status.reblog": "ブースト",
|
"status.reblog": "ブースト",
|
||||||
"status.favourite": "お気に入り",
|
"status.favourite": "お気に入り",
|
||||||
"status.reblogged_by": "{name}さんにブーストされました",
|
"status.reblogged_by": "{name} さんにブーストされました",
|
||||||
"status.sensitive_warning": "不適切なコンテンツ",
|
"status.sensitive_warning": "不適切なコンテンツ",
|
||||||
"status.sensitive_toggle": "見るにはクリック",
|
"status.sensitive_toggle": "クリックして表示",
|
||||||
"status.show_more": "もっと見る",
|
"status.show_more": "もっと見る",
|
||||||
"status.show_less": "隠す",
|
"status.show_less": "隠す",
|
||||||
"status.open": "Expand this status",
|
"status.open": "Expand this status",
|
||||||
"status.report": "@{name}さんを報告",
|
"status.report": "@{name} さんを報告",
|
||||||
"video_player.toggle_sound": "音切り替え",
|
"video_player.toggle_sound": "音の切り替え",
|
||||||
"account.mention": "@{name}さんに返信",
|
"account.mention": "@{name} さんに返信",
|
||||||
"account.edit_profile": "プロフィール返信",
|
"account.edit_profile": "プロフィールを編集",
|
||||||
"account.unblock": "@{name}さんのブロックを解除",
|
"account.unblock": "@{name} さんのブロックを解除",
|
||||||
"account.unfollow": "フォロー解除",
|
"account.unfollow": "フォロー解除",
|
||||||
"account.block": "@{name}さんをブロック",
|
"account.block": "@{name} さんをブロック",
|
||||||
|
"account.mute": "ミュート",
|
||||||
|
"account.unmute": "ミュート解除",
|
||||||
"account.follow": "フォロー",
|
"account.follow": "フォロー",
|
||||||
"account.posts": "投稿",
|
"account.posts": "投稿",
|
||||||
"account.follows": "フォロー",
|
"account.follows": "フォロー",
|
||||||
"account.followers": "フォロワー",
|
"account.followers": "フォロワー",
|
||||||
"account.follows_you": "フォロー中",
|
"account.follows_you": "フォローされています",
|
||||||
"account.requested": "承認待ち",
|
"account.requested": "承認待ち",
|
||||||
"getting_started.heading": "スタート",
|
"getting_started.heading": "スタート",
|
||||||
"getting_started.about_addressing": "ドメインとユーザー名を知っているなら検索フォームに入力すればフォローできます。",
|
"getting_started.about_addressing": "ドメインとユーザー名を知っているなら検索フォームに入力すればフォローできます。",
|
||||||
"getting_started.about_shortcuts": "対象のアカウントがあなたと同じドメインのユーザーならばユーザー名のみで検索できます。これは返信のときも一緒です。",
|
"getting_started.about_shortcuts": "対象のアカウントがあなたと同じドメインのユーザーならばユーザー名のみで検索できます。これは返信のときも一緒です。",
|
||||||
"getting_started.open_source_notice": "Mastodon はオープンソースのソフトウェアです。誰でもGitHub({github})から開発に参加したり、問題を報告したりできます。 {apps}",
|
"getting_started.open_source_notice": "Mastodon はオープンソースソフトウェアです。誰でも GitHub({github})から開発に参加したり、問題を報告したりできます。 {apps}",
|
||||||
"column.home": "ホーム",
|
"column.home": "ホーム",
|
||||||
"column.community": "ローカルタイムライン",
|
"column.community": "ローカルタイムライン",
|
||||||
"column.public": "連邦タイムライン",
|
"column.public": "連合タイムライン",
|
||||||
"column.notifications": "通知",
|
"column.notifications": "通知",
|
||||||
"tabs_bar.compose": "Compose",
|
"tabs_bar.compose": "投稿",
|
||||||
"tabs_bar.home": "ホーム",
|
"tabs_bar.home": "ホーム",
|
||||||
"tabs_bar.mentions": "返信",
|
"tabs_bar.mentions": "返信",
|
||||||
"tabs_bar.public": "連邦タイムライン",
|
"tabs_bar.local_timeline": "ローカルTL",
|
||||||
|
"tabs_bar.federated_timeline": "連合TL",
|
||||||
"tabs_bar.notifications": "通知",
|
"tabs_bar.notifications": "通知",
|
||||||
"compose_form.placeholder": "今なにしてる?",
|
"compose_form.placeholder": "今なにしてる?",
|
||||||
"compose_form.publish": "トゥート",
|
"compose_form.publish": "トゥート",
|
||||||
|
@ -46,27 +49,35 @@ const ja = {
|
||||||
"compose_form.private": "非公開にする",
|
"compose_form.private": "非公開にする",
|
||||||
"compose_form.privacy_disclaimer": "あなたの非公開トゥートは返信先のユーザー(at {domains})に公開されます。{domainsCount, plural, one {that server} other {those servers}}を信頼しますか?投稿のプライバシー保護はMastodonサーバー内でのみ有効です。 もし{domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}ならばあなたの投稿のプライバシーは保護されず、ブーストされたり予期しないユーザーに見られる可能性があります。",
|
"compose_form.privacy_disclaimer": "あなたの非公開トゥートは返信先のユーザー(at {domains})に公開されます。{domainsCount, plural, one {that server} other {those servers}}を信頼しますか?投稿のプライバシー保護はMastodonサーバー内でのみ有効です。 もし{domains} {domainsCount, plural, one {is not a Mastodon instance} other {are not Mastodon instances}}ならばあなたの投稿のプライバシーは保護されず、ブーストされたり予期しないユーザーに見られる可能性があります。",
|
||||||
"compose_form.unlisted": "公開タイムラインに表示しない",
|
"compose_form.unlisted": "公開タイムラインに表示しない",
|
||||||
"navigation_bar.edit_profile": "プロフィール編集",
|
"navigation_bar.edit_profile": "プロフィールを編集",
|
||||||
"navigation_bar.preferences": "ユーザー設定",
|
"navigation_bar.preferences": "ユーザー設定",
|
||||||
"navigation_bar.community_timeline": "ローカルタイムライン",
|
"navigation_bar.community_timeline": "ローカルタイムライン",
|
||||||
"navigation_bar.public_timeline": "連邦タイムライン",
|
"navigation_bar.public_timeline": "連合タイムライン",
|
||||||
"navigation_bar.logout": "ログアウト",
|
"navigation_bar.logout": "ログアウト",
|
||||||
|
"navigation_bar.favourites": "お気に入り",
|
||||||
|
"navigation_bar.blocks": "ブロックしたユーザー",
|
||||||
|
"navigation_bar.info": "サーバー情報",
|
||||||
"reply_indicator.cancel": "キャンセル",
|
"reply_indicator.cancel": "キャンセル",
|
||||||
"search.placeholder": "検索",
|
"search.placeholder": "検索",
|
||||||
"search.account": "アカウント",
|
"search.account": "アカウント",
|
||||||
"search.hashtag": "ハッシュタグ",
|
"search.hashtag": "ハッシュタグ",
|
||||||
"upload_button.label": "メディアを追加",
|
"upload_button.label": "メディアを追加",
|
||||||
"upload_form.undo": "やり直す",
|
"upload_form.undo": "やり直す",
|
||||||
"notification.follow": "{name}さんにフォローされました",
|
"notification.follow": "{name} さんにフォローされました",
|
||||||
"notification.favourite": "{name}さんがあなたのトゥートをいいねしました",
|
"notification.favourite": "{name} さんがあなたのトゥートをお気に入りに登録しました",
|
||||||
"notification.reblog": "{name}さんがあなたのトゥートをブーストしました",
|
"notification.reblog": "{name} さんがあなたのトゥートをブーストしました",
|
||||||
"notification.mention": "{name}さんがあなたに返信しました",
|
"notification.mention": "{name} さんがあなたに返信しました",
|
||||||
"notifications.column_settings.alert": "デスクトップ通知",
|
"notifications.column_settings.alert": "デスクトップ通知",
|
||||||
"notifications.column_settings.show": "表示項目",
|
"notifications.column_settings.show": "カラムに表示",
|
||||||
"notifications.column_settings.follow": "新しいフォロワー:",
|
"notifications.column_settings.follow": "新しいフォロワー",
|
||||||
"notifications.column_settings.favourite": "いいね:",
|
"notifications.column_settings.favourite": "お気に入り",
|
||||||
"notifications.column_settings.mention": "返信:",
|
"notifications.column_settings.mention": "返信",
|
||||||
"notifications.column_settings.reblog": "ブースト:",
|
"notifications.column_settings.reblog": "ブースト",
|
||||||
|
"notifications.column_settings.sound": "通知音を再生",
|
||||||
|
"empty_column.home": "まだ誰もフォローしていません。{public}を見に行くか、検索を使って他のユーザーを見つけましょう。",
|
||||||
|
"empty_column.home.public_timeline": "連合タイムライン",
|
||||||
|
"empty_column.notifications": "まだ通知がありません。他の人とふれ合って会話を始めましょう。",
|
||||||
|
"empty_column.public": "ここにはまだ何もありません!公開で何かを投稿したり、他のインスタンスのユーザーをフォローしたりしていっぱいにしましょう!",
|
||||||
};
|
};
|
||||||
|
|
||||||
export default ja;
|
export default ja;
|
||||||
|
|
72
app/assets/javascripts/components/locales/no.jsx
Normal file
72
app/assets/javascripts/components/locales/no.jsx
Normal file
|
@ -0,0 +1,72 @@
|
||||||
|
const no = {
|
||||||
|
"column_back_button.label": "Tilbake",
|
||||||
|
"lightbox.close": "Lukk",
|
||||||
|
"loading_indicator.label": "Laster...",
|
||||||
|
"status.mention": "Nevn @{name}",
|
||||||
|
"status.delete": "Slett",
|
||||||
|
"status.reply": "Svar",
|
||||||
|
"status.reblog": "Reblogg",
|
||||||
|
"status.favourite": "Favoritt",
|
||||||
|
"status.reblogged_by": "{name} reblogget",
|
||||||
|
"status.sensitive_warning": "Sensitivt innhold",
|
||||||
|
"status.sensitive_toggle": "Klikk for å vise",
|
||||||
|
"status.show_more": "Vis mer",
|
||||||
|
"status.show_less": "Vis mindre",
|
||||||
|
"status.open": "Utvid denne statusen",
|
||||||
|
"status.report": "Rapporter @{name}",
|
||||||
|
"video_player.toggle_sound": "Veksle lyd",
|
||||||
|
"account.mention": "Nevn @{name}",
|
||||||
|
"account.edit_profile": "Rediger profil",
|
||||||
|
"account.unblock": "Avblokker @{name}",
|
||||||
|
"account.unfollow": "Avfølg",
|
||||||
|
"account.block": "Blokker @{name}",
|
||||||
|
"account.follow": "Følg",
|
||||||
|
"account.posts": "Poster",
|
||||||
|
"account.follows": "Følginger",
|
||||||
|
"account.followers": "Følgere",
|
||||||
|
"account.follows_you": "Folger deg",
|
||||||
|
"account.requested": "Venter på godkjennelse",
|
||||||
|
"getting_started.heading": "Kom i gang",
|
||||||
|
"getting_started.about_addressing": "Du kan følge noen hvis du vet brukernavnet deres og domenet de er på ved å skrive en e-postadresse inn i søkeskjemaet.",
|
||||||
|
"getting_started.about_shortcuts": "Hvis målbrukeren er på samme domene som deg, vil kun brukernavnet også fungere. Den samme regelen gjelder når man nevner noen i statuser.",
|
||||||
|
"getting_started.open_source_notice": "Mastodon er programvare med fri kildekode. Du kan bidra eller rapportere problemer på GitHub på {github}. {apps}.",
|
||||||
|
"column.home": "Hjem",
|
||||||
|
"column.community": "Lokal tidslinje",
|
||||||
|
"column.public": "Føderert tidslinje",
|
||||||
|
"column.notifications": "Varslinger",
|
||||||
|
"tabs_bar.compose": "Komponer",
|
||||||
|
"tabs_bar.home": "Hjem",
|
||||||
|
"tabs_bar.mentions": "Nevninger",
|
||||||
|
"tabs_bar.public": "Føderert tidslinje",
|
||||||
|
"tabs_bar.notifications": "Varslinger",
|
||||||
|
"compose_form.placeholder": "Hva har du på hjertet?",
|
||||||
|
"compose_form.publish": "Tut",
|
||||||
|
"compose_form.sensitive": "Merk media som følsomt",
|
||||||
|
"compose_form.spoiler": "Skjul tekst bak advarsel",
|
||||||
|
"compose_form.private": "Merk som privat",
|
||||||
|
"compose_form.privacy_disclaimer": "Din private status vil leveres til nevnte brukere på {domains}. Stoler du på {domainsCount, plural, one {den serveren} other {de serverne}}? Synlighet fungerer kun på Mastodon-instanser. Hvis {domains} {domainsCount, plural, one {ike er en Mastodon-instans} other {ikke er Mastodon-instanser}}, vil det ikke indikeres at posten din er privat, og den kan kanskje bli reblogget eller på annen måte bli synlig for uventede mottakere.",
|
||||||
|
"compose_form.unlisted": "Ikke vis på offentlige tidslinjer",
|
||||||
|
"navigation_bar.edit_profile": "Rediger profil",
|
||||||
|
"navigation_bar.preferences": "Preferanser",
|
||||||
|
"navigation_bar.community_timeline": "Lokal tidslinje",
|
||||||
|
"navigation_bar.public_timeline": "Føderert tidslinje",
|
||||||
|
"navigation_bar.logout": "Logg ut",
|
||||||
|
"reply_indicator.cancel": "Avbryt",
|
||||||
|
"search.placeholder": "Søk",
|
||||||
|
"search.account": "Konto",
|
||||||
|
"search.hashtag": "Hashtag",
|
||||||
|
"upload_button.label": "Legg til media",
|
||||||
|
"upload_form.undo": "Angre",
|
||||||
|
"notification.follow": "{name} fulgte deg",
|
||||||
|
"notification.favourite": "{name} likte din status",
|
||||||
|
"notification.reblog": "{name} reblogget din status",
|
||||||
|
"notification.mention": "{name} nevnte deg",
|
||||||
|
"notifications.column_settings.alert": "Skrivebordsvarslinger",
|
||||||
|
"notifications.column_settings.show": "Vis i kolonne",
|
||||||
|
"notifications.column_settings.follow": "Nye følgere:",
|
||||||
|
"notifications.column_settings.favourite": "Favouritter:",
|
||||||
|
"notifications.column_settings.mention": "Nevninger:",
|
||||||
|
"notifications.column_settings.reblog": "Reblogginger:",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default no;
|
113
app/assets/javascripts/components/locales/zh-hk.jsx
Normal file
113
app/assets/javascripts/components/locales/zh-hk.jsx
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
import zh from 'react-intl/locale-data/zh';
|
||||||
|
|
||||||
|
const localeData = zh.reduce(function (acc, localeData) {
|
||||||
|
if (localeData.locale === "zh-Hant-HK") {
|
||||||
|
// rename the locale "zh-Hant-HK" as "zh-HK"
|
||||||
|
// (match the code usually used in Accepted-Language header)
|
||||||
|
acc.push(Object.assign({},
|
||||||
|
localeData,
|
||||||
|
{
|
||||||
|
"locale": "zh-HK",
|
||||||
|
"parentLocale": "zh-Hant-HK",
|
||||||
|
}
|
||||||
|
));
|
||||||
|
}
|
||||||
|
return acc;
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
export { localeData as localeData };
|
||||||
|
|
||||||
|
const zh_hk = {
|
||||||
|
"account.block": "封鎖 @{name}",
|
||||||
|
"account.edit_profile": "修改個人資料",
|
||||||
|
"account.follow": "關注",
|
||||||
|
"account.followers": "關注的人",
|
||||||
|
"account.follows_you": "關注你",
|
||||||
|
"account.follows": "正在關注",
|
||||||
|
"account.mention": "提及 @{name}",
|
||||||
|
"account.posts": "文章",
|
||||||
|
"account.requested": "等候審批",
|
||||||
|
"account.unblock": "解除對 @{name} 的封鎖",
|
||||||
|
"account.unfollow": "取消關注",
|
||||||
|
"column_back_button.label": "先前顯示",
|
||||||
|
"column.community": "本站時間軸",
|
||||||
|
"column.home": "家",
|
||||||
|
"column.notifications": "通知",
|
||||||
|
"column.public": "跨站公共時間軸",
|
||||||
|
"compose_form.placeholder": "你在想甚麼?",
|
||||||
|
"compose_form.privacy_disclaimer": "你的私人文章,將被遞送至你所提及的 {domains} 用戶。你是否信任 {domainsCount, plural, one {這個網站} other {這些網站}}?請留意,文章私隱設定只適用於各 Mastodon 服務站,如果 {domains} {domainsCount, plural, one {不是 Mastodon 服務站} other {之中有些不是 Mastodon 服務站}},對方將無法收到這篇文章的私隱設定,然後可能被轉推給不能預知的用戶閱讀。",
|
||||||
|
"compose_form.private": "標示為「只有關注你的人能看」",
|
||||||
|
"compose_form.publish": "發文",
|
||||||
|
"compose_form.sensitive": "將媒體檔案標示為「敏感內容」",
|
||||||
|
"compose_form.spoiler": "將部份文字藏於警告訊息之後",
|
||||||
|
"compose_form.unlisted": "請勿在公共時間軸顯示",
|
||||||
|
"empty_column.community": "本站時間軸暫時未有內容,快貼文來搶頭香啊!",
|
||||||
|
"empty_column.hashtag": "這個標籤暫時未有內容。",
|
||||||
|
"empty_column.home": "你還沒有關注任何用戶。快看看{public},向其他用戶搭訕吧。",
|
||||||
|
"empty_column.home.public_timeline": "公共時間軸",
|
||||||
|
"empty_column.notifications": "You don't have any notifications yet. Interact with others to start the conversation.",
|
||||||
|
"empty_column.public": "There is nothing here! Write something publicly, or manually follow users from other instances to fill it up.",
|
||||||
|
"getting_started.about_addressing": "只要你知道一位用戶的用戶名稱和域名,你可以用「@用戶名稱@域名」的格式在搜尋欄尋找該用戶。",
|
||||||
|
"getting_started.about_shortcuts": "只要該用戶是在你現在的服務站開立,你可以直接輸入用戶𠱷搜尋。同樣的規則適用於在文章提及別的用戶。",
|
||||||
|
"getting_started.apps": "手機或桌面應用程式",
|
||||||
|
"getting_started.heading": "開始使用",
|
||||||
|
"getting_started.open_source_notice": "Mastodon 是一個開放源碼的軟件。你可以在官方 GitHub ({github}) 貢獻或者回報問題。你亦可透過{apps}閱讀 Mastodon 上的消息。",
|
||||||
|
"home.column_settings.basic": "基本",
|
||||||
|
"home.column_settings.show_reblogs": "顯示被轉推的文章",
|
||||||
|
"home.column_settings.show_replies": "顯示回應文章",
|
||||||
|
"home.column_settings.advanced": "進階",
|
||||||
|
"lightbox.close": "關閉",
|
||||||
|
"loading_indicator.label": "載入中...",
|
||||||
|
"missing_indicator.label": "找不到內容",
|
||||||
|
"navigation_bar.community_timeline": "本站時間軸",
|
||||||
|
"navigation_bar.edit_profile": "修改個人資料",
|
||||||
|
"navigation_bar.logout": "登出",
|
||||||
|
"navigation_bar.preferences": "個人設定",
|
||||||
|
"navigation_bar.public_timeline": "跨站公共時間軸",
|
||||||
|
"notification.favourite": "{name} 喜歡你的文章",
|
||||||
|
"notification.follow": "{name} 開始開始你",
|
||||||
|
"notification.mention": "{name} 提及你",
|
||||||
|
"notification.reblog": "{name} 轉推你的文章",
|
||||||
|
"notifications.column_settings.alert": "顯示桌面通知",
|
||||||
|
"notifications.column_settings.favourite": "喜歡你的文章:",
|
||||||
|
"notifications.column_settings.follow": "關注你:",
|
||||||
|
"notifications.column_settings.mention": "提及你:",
|
||||||
|
"notifications.column_settings.reblog": "轉推你的文章:",
|
||||||
|
"notifications.column_settings.show": "在通知欄顯示",
|
||||||
|
"notifications.column_settings.sound": "播放音效",
|
||||||
|
"reply_indicator.cancel": "取消",
|
||||||
|
"report.target": "Reporting",
|
||||||
|
"search.account": "用戶",
|
||||||
|
"search.hashtag": "標籤",
|
||||||
|
"search.placeholder": "搜尋",
|
||||||
|
"search_results.total": "{count} 項結果",
|
||||||
|
"search.status_by": "按用戶名稱搜尋文章",
|
||||||
|
"status.delete": "刪除",
|
||||||
|
"status.favourite": "喜歡",
|
||||||
|
"status.load_more": "載入更多",
|
||||||
|
"status.media_hidden": "隱藏媒體內容",
|
||||||
|
"status.mention": "提及 @{name}",
|
||||||
|
"status.open": "展開文章",
|
||||||
|
"status.reblog": "轉推",
|
||||||
|
"status.reblogged_by": "{name} 轉推",
|
||||||
|
"status.reply": "回應",
|
||||||
|
"status.report": "舉報 @{name}",
|
||||||
|
"status.sensitive_toggle": "點擊顯示",
|
||||||
|
"status.sensitive_warning": "敏感內容",
|
||||||
|
"status.show_less": "減少顯示",
|
||||||
|
"status.show_more": "顯示更多",
|
||||||
|
"tabs_bar.compose": "撰寫",
|
||||||
|
"tabs_bar.home": "家",
|
||||||
|
"tabs_bar.local_timeline": "本站",
|
||||||
|
"tabs_bar.mentions": "提及",
|
||||||
|
"tabs_bar.notifications": "通知",
|
||||||
|
"tabs_bar.public": "跨站公共時間軸",
|
||||||
|
"tabs_bar.federated_timeline": "跨站",
|
||||||
|
"upload_area.title": "將檔案拖放至此上載",
|
||||||
|
"upload_button.label": "上載媒體檔案",
|
||||||
|
"upload_progress.label": "上載中……",
|
||||||
|
"upload_form.undo": "還原",
|
||||||
|
"video_player.toggle_sound": "開關音效",
|
||||||
|
};
|
||||||
|
|
||||||
|
export default zh_hk;
|
|
@ -76,7 +76,8 @@ function appendMedia(state, media) {
|
||||||
map.update('media_attachments', list => list.push(media));
|
map.update('media_attachments', list => list.push(media));
|
||||||
map.set('is_uploading', false);
|
map.set('is_uploading', false);
|
||||||
map.set('resetFileKey', Math.floor((Math.random() * 0x10000)));
|
map.set('resetFileKey', Math.floor((Math.random() * 0x10000)));
|
||||||
map.update('text', oldText => `${oldText} ${media.get('text_url')}`.trim());
|
map.set('focusDate', new Date());
|
||||||
|
map.update('text', oldText => `${oldText.trim()} ${media.get('text_url')}`.trim() + ' ');
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
@ -14,7 +14,7 @@
|
||||||
}
|
}
|
||||||
|
|
||||||
&:after {
|
&:after {
|
||||||
background: rgba($color8, 0.5);
|
background: linear-gradient(rgba($color8, 0.5), rgba($color8, 0.8));
|
||||||
display: block;
|
display: block;
|
||||||
content: "";
|
content: "";
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
@ -72,7 +72,6 @@
|
||||||
position: relative;
|
position: relative;
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
flex-direction: row;
|
flex-direction: row;
|
||||||
background: rgba(0,0,0,0.5);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.details-counters {
|
.details-counters {
|
||||||
|
@ -388,6 +387,5 @@
|
||||||
.account__header__content {
|
.account__header__content {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
color: $color1;
|
color: $color1;
|
||||||
text-shadow: 0 0 2px $color8;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,305 +4,13 @@
|
||||||
@import 'fonts/montserrat';
|
@import 'fonts/montserrat';
|
||||||
@import 'font-awesome';
|
@import 'font-awesome';
|
||||||
|
|
||||||
/* http://meyerweb.com/eric/tools/css/reset/
|
@import 'reset';
|
||||||
v2.0 | 20110126
|
@import 'basics';
|
||||||
License: none (public domain)
|
@import 'containers';
|
||||||
*/
|
@import 'lists';
|
||||||
|
@import 'footer';
|
||||||
html, body, div, span, applet, object, iframe,
|
@import 'compact_header';
|
||||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
@import 'landing_strip';
|
||||||
a, abbr, acronym, address, big, cite, code,
|
|
||||||
del, dfn, em, img, ins, kbd, q, s, samp,
|
|
||||||
small, strike, strong, sub, sup, tt, var,
|
|
||||||
b, u, i, center,
|
|
||||||
dl, dt, dd, ol, ul, li,
|
|
||||||
fieldset, form, label, legend,
|
|
||||||
table, caption, tbody, tfoot, thead, tr, th, td,
|
|
||||||
article, aside, canvas, details, embed,
|
|
||||||
figure, figcaption, footer, header, hgroup,
|
|
||||||
menu, nav, output, ruby, section, summary,
|
|
||||||
time, mark, audio, video {
|
|
||||||
margin: 0;
|
|
||||||
padding: 0;
|
|
||||||
border: 0;
|
|
||||||
font-size: 100%;
|
|
||||||
font: inherit;
|
|
||||||
vertical-align: baseline;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* HTML5 display-role reset for older browsers */
|
|
||||||
article, aside, details, figcaption, figure,
|
|
||||||
footer, header, hgroup, menu, nav, section {
|
|
||||||
display: block;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
line-height: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
ol, ul {
|
|
||||||
list-style: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote, q {
|
|
||||||
quotes: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
blockquote:before, blockquote:after,
|
|
||||||
q:before, q:after {
|
|
||||||
content: '';
|
|
||||||
content: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
border-spacing: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar {
|
|
||||||
width: 8px;
|
|
||||||
height: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb {
|
|
||||||
background: lighten($color1, 4%);
|
|
||||||
border: 0px none $color5;
|
|
||||||
border-radius: 50px;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:hover {
|
|
||||||
background: lighten($color1, 6%);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-thumb:active {
|
|
||||||
background: lighten($color1, 4%);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track {
|
|
||||||
border: 0px none $color5;
|
|
||||||
border-radius: 0;
|
|
||||||
background: rgba($color8, 0.1);
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track:hover {
|
|
||||||
background: $color1;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-track:active {
|
|
||||||
background: $color1;
|
|
||||||
}
|
|
||||||
|
|
||||||
::-webkit-scrollbar-corner {
|
|
||||||
background: transparent;
|
|
||||||
}
|
|
||||||
|
|
||||||
body {
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
background: $color1 image-url('background-photo.jpeg');
|
|
||||||
background-size: cover;
|
|
||||||
background-attachment: fixed;
|
|
||||||
font-size: 13px;
|
|
||||||
line-height: 18px;
|
|
||||||
font-weight: 400;
|
|
||||||
color: $color5;
|
|
||||||
padding-bottom: 140px;
|
|
||||||
text-rendering: optimizelegibility;
|
|
||||||
font-feature-settings: "kern";
|
|
||||||
text-size-adjust: none;
|
|
||||||
|
|
||||||
&.app-body {
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0;
|
|
||||||
background: $color1;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.embed {
|
|
||||||
background: transparent;
|
|
||||||
margin: 0;
|
|
||||||
|
|
||||||
.container {
|
|
||||||
position: absolute;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
&.admin {
|
|
||||||
background: darken($color1, 4%);
|
|
||||||
position: fixed;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
padding: 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
@media screen and (max-width: 360px) {
|
|
||||||
padding-bottom: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
button:focus {
|
|
||||||
outline: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.app-holder {
|
|
||||||
display: flex;
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container {
|
|
||||||
width: 700px;
|
|
||||||
margin: 0 auto;
|
|
||||||
margin-top: 40px;
|
|
||||||
|
|
||||||
@media screen and (max-width: 700px) {
|
|
||||||
width: 100%;
|
|
||||||
margin: 0;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.logo-container {
|
|
||||||
max-width: 400px;
|
|
||||||
margin: 100px auto;
|
|
||||||
margin-bottom: 0;
|
|
||||||
cursor: default;
|
|
||||||
|
|
||||||
@media screen and (max-width: 360px) {
|
|
||||||
margin: 30px auto;
|
|
||||||
}
|
|
||||||
|
|
||||||
h1 {
|
|
||||||
display: block;
|
|
||||||
text-align: center;
|
|
||||||
color: $color5;
|
|
||||||
font-size: 48px;
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: block;
|
|
||||||
margin: 20px auto;
|
|
||||||
width: 180px;
|
|
||||||
height: 180px;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
outline: 0;
|
|
||||||
|
|
||||||
img {
|
|
||||||
opacity: 0.8;
|
|
||||||
transition: all 0.8s ease;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
img {
|
|
||||||
opacity: 1;
|
|
||||||
transition-duration: 0.2s;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
small {
|
|
||||||
display: block;
|
|
||||||
font-size: 12px;
|
|
||||||
font-weight: 400;
|
|
||||||
font-family: 'Roboto Mono', monospace;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.no-list {
|
|
||||||
list-style: none;
|
|
||||||
|
|
||||||
li {
|
|
||||||
display: inline-block;
|
|
||||||
margin: 0 5px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 30px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: darken($color2, 25%);
|
|
||||||
|
|
||||||
.domain {
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.powered-by {
|
|
||||||
font-weight: 400;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: underline;
|
|
||||||
font-weight: 500;
|
|
||||||
|
|
||||||
&:hover {
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.compact-header {
|
|
||||||
h1 {
|
|
||||||
font-size: 24px;
|
|
||||||
line-height: 28px;
|
|
||||||
color: $color3;
|
|
||||||
overflow: hidden;
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
small {
|
|
||||||
font-weight: 400;
|
|
||||||
color: $color2;
|
|
||||||
}
|
|
||||||
|
|
||||||
img {
|
|
||||||
display: inline-block;
|
|
||||||
margin-bottom: -5px;
|
|
||||||
margin-right: 15px;
|
|
||||||
width: 36px;
|
|
||||||
height: 36px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.landing-strip {
|
|
||||||
background: rgba(darken($color1, 7%), 0.8);
|
|
||||||
color: $color3;
|
|
||||||
font-weight: 400;
|
|
||||||
padding: 14px;
|
|
||||||
border-radius: 4px;
|
|
||||||
margin-bottom: 20px;
|
|
||||||
|
|
||||||
strong, a {
|
|
||||||
font-weight: 500;
|
|
||||||
}
|
|
||||||
|
|
||||||
a {
|
|
||||||
color: inherit;
|
|
||||||
text-decoration: underline;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@import 'forms';
|
@import 'forms';
|
||||||
@import 'accounts';
|
@import 'accounts';
|
||||||
@import 'stream_entries';
|
@import 'stream_entries';
|
||||||
|
|
58
app/assets/stylesheets/basics.scss
Normal file
58
app/assets/stylesheets/basics.scss
Normal file
|
@ -0,0 +1,58 @@
|
||||||
|
body {
|
||||||
|
font-family: 'Roboto', sans-serif;
|
||||||
|
background: $color1 image-url('background-photo.jpeg');
|
||||||
|
background-size: cover;
|
||||||
|
background-attachment: fixed;
|
||||||
|
font-size: 13px;
|
||||||
|
line-height: 18px;
|
||||||
|
font-weight: 400;
|
||||||
|
color: $color5;
|
||||||
|
padding-bottom: 140px;
|
||||||
|
text-rendering: optimizelegibility;
|
||||||
|
font-feature-settings: "kern";
|
||||||
|
text-size-adjust: none;
|
||||||
|
|
||||||
|
&.app-body {
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
background: $color1;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.embed {
|
||||||
|
background: transparent;
|
||||||
|
margin: 0;
|
||||||
|
|
||||||
|
.container {
|
||||||
|
position: absolute;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
&.admin {
|
||||||
|
background: darken($color1, 4%);
|
||||||
|
position: fixed;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
padding: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media screen and (max-width: 360px) {
|
||||||
|
padding-bottom: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
button:focus {
|
||||||
|
outline: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.app-holder {
|
||||||
|
display: flex;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
28
app/assets/stylesheets/compact_header.scss
Normal file
28
app/assets/stylesheets/compact_header.scss
Normal file
|
@ -0,0 +1,28 @@
|
||||||
|
.compact-header {
|
||||||
|
h1 {
|
||||||
|
font-size: 24px;
|
||||||
|
line-height: 28px;
|
||||||
|
color: $color3;
|
||||||
|
overflow: hidden;
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
font-weight: 400;
|
||||||
|
color: $color2;
|
||||||
|
}
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: inline-block;
|
||||||
|
margin-bottom: -5px;
|
||||||
|
margin-right: 15px;
|
||||||
|
width: 36px;
|
||||||
|
height: 36px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -59,7 +59,7 @@
|
||||||
z-index: 2;
|
z-index: 2;
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1025px) {
|
||||||
.column-icon-clear {
|
.column-icon-clear {
|
||||||
top: 10px;
|
top: 10px;
|
||||||
}
|
}
|
||||||
|
@ -857,7 +857,7 @@ a.status__content__spoiler-link {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@media screen and (min-width: 1024px) {
|
@media screen and (min-width: 1025px) {
|
||||||
.columns-area {
|
.columns-area {
|
||||||
padding: 0;
|
padding: 0;
|
||||||
}
|
}
|
||||||
|
|
61
app/assets/stylesheets/containers.scss
Normal file
61
app/assets/stylesheets/containers.scss
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
.container {
|
||||||
|
width: 700px;
|
||||||
|
margin: 0 auto;
|
||||||
|
margin-top: 40px;
|
||||||
|
|
||||||
|
@media screen and (max-width: 700px) {
|
||||||
|
width: 100%;
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.logo-container {
|
||||||
|
max-width: 400px;
|
||||||
|
margin: 100px auto;
|
||||||
|
margin-bottom: 0;
|
||||||
|
cursor: default;
|
||||||
|
|
||||||
|
@media screen and (max-width: 360px) {
|
||||||
|
margin: 30px auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
h1 {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
color: $color5;
|
||||||
|
font-size: 48px;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
img {
|
||||||
|
display: block;
|
||||||
|
margin: 20px auto;
|
||||||
|
width: 180px;
|
||||||
|
height: 180px;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
outline: 0;
|
||||||
|
|
||||||
|
img {
|
||||||
|
opacity: 0.8;
|
||||||
|
transition: all 0.8s ease;
|
||||||
|
}
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
img {
|
||||||
|
opacity: 1;
|
||||||
|
transition-duration: 0.2s;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
small {
|
||||||
|
display: block;
|
||||||
|
font-size: 12px;
|
||||||
|
font-weight: 400;
|
||||||
|
font-family: 'Roboto Mono', monospace;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
29
app/assets/stylesheets/footer.scss
Normal file
29
app/assets/stylesheets/footer.scss
Normal file
|
@ -0,0 +1,29 @@
|
||||||
|
.footer {
|
||||||
|
text-align: center;
|
||||||
|
margin-top: 30px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: darken($color2, 25%);
|
||||||
|
|
||||||
|
.domain {
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.powered-by {
|
||||||
|
font-weight: 400;
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: underline;
|
||||||
|
font-weight: 500;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
17
app/assets/stylesheets/landing_strip.scss
Normal file
17
app/assets/stylesheets/landing_strip.scss
Normal file
|
@ -0,0 +1,17 @@
|
||||||
|
.landing-strip {
|
||||||
|
background: rgba(darken($color1, 7%), 0.8);
|
||||||
|
color: $color3;
|
||||||
|
font-weight: 400;
|
||||||
|
padding: 14px;
|
||||||
|
border-radius: 4px;
|
||||||
|
margin-bottom: 20px;
|
||||||
|
|
||||||
|
strong, a {
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
color: inherit;
|
||||||
|
text-decoration: underline;
|
||||||
|
}
|
||||||
|
}
|
8
app/assets/stylesheets/lists.scss
Normal file
8
app/assets/stylesheets/lists.scss
Normal file
|
@ -0,0 +1,8 @@
|
||||||
|
.no-list {
|
||||||
|
list-style: none;
|
||||||
|
|
||||||
|
li {
|
||||||
|
display: inline-block;
|
||||||
|
margin: 0 5px;
|
||||||
|
}
|
||||||
|
}
|
91
app/assets/stylesheets/reset.scss
Normal file
91
app/assets/stylesheets/reset.scss
Normal file
|
@ -0,0 +1,91 @@
|
||||||
|
/* http://meyerweb.com/eric/tools/css/reset/
|
||||||
|
v2.0 | 20110126
|
||||||
|
License: none (public domain)
|
||||||
|
*/
|
||||||
|
|
||||||
|
html, body, div, span, applet, object, iframe,
|
||||||
|
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||||
|
a, abbr, acronym, address, big, cite, code,
|
||||||
|
del, dfn, em, img, ins, kbd, q, s, samp,
|
||||||
|
small, strike, strong, sub, sup, tt, var,
|
||||||
|
b, u, i, center,
|
||||||
|
dl, dt, dd, ol, ul, li,
|
||||||
|
fieldset, form, label, legend,
|
||||||
|
table, caption, tbody, tfoot, thead, tr, th, td,
|
||||||
|
article, aside, canvas, details, embed,
|
||||||
|
figure, figcaption, footer, header, hgroup,
|
||||||
|
menu, nav, output, ruby, section, summary,
|
||||||
|
time, mark, audio, video {
|
||||||
|
margin: 0;
|
||||||
|
padding: 0;
|
||||||
|
border: 0;
|
||||||
|
font-size: 100%;
|
||||||
|
font: inherit;
|
||||||
|
vertical-align: baseline;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* HTML5 display-role reset for older browsers */
|
||||||
|
article, aside, details, figcaption, figure,
|
||||||
|
footer, header, hgroup, menu, nav, section {
|
||||||
|
display: block;
|
||||||
|
}
|
||||||
|
|
||||||
|
body {
|
||||||
|
line-height: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ol, ul {
|
||||||
|
list-style: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote, q {
|
||||||
|
quotes: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
blockquote:before, blockquote:after,
|
||||||
|
q:before, q:after {
|
||||||
|
content: '';
|
||||||
|
content: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
border-collapse: collapse;
|
||||||
|
border-spacing: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar {
|
||||||
|
width: 8px;
|
||||||
|
height: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb {
|
||||||
|
background: lighten($color1, 4%);
|
||||||
|
border: 0px none $color5;
|
||||||
|
border-radius: 50px;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: lighten($color1, 6%);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-thumb:active {
|
||||||
|
background: lighten($color1, 4%);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track {
|
||||||
|
border: 0px none $color5;
|
||||||
|
border-radius: 0;
|
||||||
|
background: rgba($color8, 0.1);
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track:hover {
|
||||||
|
background: $color1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-track:active {
|
||||||
|
background: $color1;
|
||||||
|
}
|
||||||
|
|
||||||
|
::-webkit-scrollbar-corner {
|
||||||
|
background: transparent;
|
||||||
|
}
|
|
@ -26,7 +26,7 @@ module Localized
|
||||||
end
|
end
|
||||||
|
|
||||||
def default_locale
|
def default_locale
|
||||||
ENV.fetch('DEFAULT_LOCALE') {
|
ENV.fetch('DEFAULT_LOCALE') {
|
||||||
http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
|
http_accept_language.compatible_language_from(I18n.available_locales) || I18n.default_locale
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
23
app/controllers/settings/exports/base_controller.rb
Normal file
23
app/controllers/settings/exports/base_controller.rb
Normal file
|
@ -0,0 +1,23 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Settings
|
||||||
|
module Exports
|
||||||
|
class BaseController < ApplicationController
|
||||||
|
before_action :authenticate_user!
|
||||||
|
|
||||||
|
def index
|
||||||
|
export_data = Export.new(export_accounts).to_csv
|
||||||
|
|
||||||
|
respond_to do |format|
|
||||||
|
format.csv { send_data export_data, filename: export_filename }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def export_filename
|
||||||
|
"#{controller_name}.csv"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -2,15 +2,11 @@
|
||||||
|
|
||||||
module Settings
|
module Settings
|
||||||
module Exports
|
module Exports
|
||||||
class BlockedAccountsController < ApplicationController
|
class BlockedAccountsController < BaseController
|
||||||
before_action :authenticate_user!
|
private
|
||||||
|
|
||||||
def index
|
def export_accounts
|
||||||
export_data = Export.new(current_account.blocking).to_csv
|
current_account.blocking
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.csv { send_data export_data, filename: 'blocking.csv' }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -2,15 +2,11 @@
|
||||||
|
|
||||||
module Settings
|
module Settings
|
||||||
module Exports
|
module Exports
|
||||||
class FollowingAccountsController < ApplicationController
|
class FollowingAccountsController < BaseController
|
||||||
before_action :authenticate_user!
|
private
|
||||||
|
|
||||||
def index
|
def export_accounts
|
||||||
export_data = Export.new(current_account.following).to_csv
|
current_account.following
|
||||||
|
|
||||||
respond_to do |format|
|
|
||||||
format.csv { send_data export_data, filename: 'following.csv' }
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,13 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Settings
|
||||||
|
module Exports
|
||||||
|
class MutedAccountsController < BaseController
|
||||||
|
private
|
||||||
|
|
||||||
|
def export_accounts
|
||||||
|
current_account.muting
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -9,5 +9,6 @@ class Settings::ExportsController < ApplicationController
|
||||||
@total_storage = current_account.media_attachments.sum(:file_file_size)
|
@total_storage = current_account.media_attachments.sum(:file_file_size)
|
||||||
@total_follows = current_account.following.count
|
@total_follows = current_account.following.count
|
||||||
@total_blocks = current_account.blocking.count
|
@total_blocks = current_account.blocking.count
|
||||||
|
@total_mutes = current_account.muting.count
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -27,8 +27,6 @@ class StreamEntriesController < ApplicationController
|
||||||
|
|
||||||
def embed
|
def embed
|
||||||
response.headers['X-Frame-Options'] = 'ALLOWALL'
|
response.headers['X-Frame-Options'] = 'ALLOWALL'
|
||||||
@external_links = true
|
|
||||||
|
|
||||||
return gone if @stream_entry.activity.nil?
|
return gone if @stream_entry.activity.nil?
|
||||||
|
|
||||||
render layout: 'embedded'
|
render layout: 'embedded'
|
||||||
|
|
|
@ -1,7 +1,6 @@
|
||||||
# frozen_string_literal: true
|
# frozen_string_literal: true
|
||||||
|
|
||||||
class XrdController < ApplicationController
|
class XrdController < ApplicationController
|
||||||
before_action :set_default_format_json, only: :webfinger
|
|
||||||
before_action :set_default_format_xml, only: :host_meta
|
before_action :set_default_format_xml, only: :host_meta
|
||||||
|
|
||||||
def host_meta
|
def host_meta
|
||||||
|
@ -31,20 +30,8 @@ class XrdController < ApplicationController
|
||||||
request.format = 'xml' if request.headers['HTTP_ACCEPT'].nil? && params[:format].nil?
|
request.format = 'xml' if request.headers['HTTP_ACCEPT'].nil? && params[:format].nil?
|
||||||
end
|
end
|
||||||
|
|
||||||
def set_default_format_json
|
|
||||||
request.format = 'json' if request.headers['HTTP_ACCEPT'].nil? && params[:format].nil?
|
|
||||||
end
|
|
||||||
|
|
||||||
def username_from_resource
|
def username_from_resource
|
||||||
if resource_param =~ /\Ahttps?:\/\//
|
WebfingerResource.new(resource_param).username
|
||||||
path_params = Rails.application.routes.recognize_path(resource_param)
|
|
||||||
raise ActiveRecord::RecordNotFound unless path_params[:controller] == 'users' && path_params[:action] == 'show'
|
|
||||||
path_params[:username]
|
|
||||||
else
|
|
||||||
username, domain = resource_param.gsub(/\Aacct:/, '').split('@')
|
|
||||||
raise ActiveRecord::RecordNotFound unless TagManager.instance.local_domain?(domain)
|
|
||||||
username
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
def pem_to_magic_key(public_key)
|
def pem_to_magic_key(public_key)
|
||||||
|
|
|
@ -6,10 +6,21 @@ module Admin::AccountsHelper
|
||||||
end
|
end
|
||||||
|
|
||||||
def filter_link_to(text, more_params)
|
def filter_link_to(text, more_params)
|
||||||
link_to text, filter_params(more_params), class: params.merge(more_params).compact == params.compact ? 'selected' : ''
|
new_url = filtered_url_for(more_params)
|
||||||
|
link_to text, new_url, class: filter_link_class(new_url)
|
||||||
end
|
end
|
||||||
|
|
||||||
def table_link_to(icon, text, path, options = {})
|
def table_link_to(icon, text, path, options = {})
|
||||||
link_to safe_join([fa_icon(icon), text]), path, options.merge(class: 'table-action-link')
|
link_to safe_join([fa_icon(icon), text]), path, options.merge(class: 'table-action-link')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def filter_link_class(new_url)
|
||||||
|
filtered_url_for(params) == new_url ? 'selected' : ''
|
||||||
|
end
|
||||||
|
|
||||||
|
def filtered_url_for(params)
|
||||||
|
url_for filter_params(params)
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -6,15 +6,16 @@ module SettingsHelper
|
||||||
de: 'Deutsch',
|
de: 'Deutsch',
|
||||||
es: 'Español',
|
es: 'Español',
|
||||||
eo: 'Esperanto',
|
eo: 'Esperanto',
|
||||||
pt: 'Português',
|
|
||||||
fr: 'Français',
|
fr: 'Français',
|
||||||
hu: 'Magyar',
|
hu: 'Magyar',
|
||||||
uk: 'Українська',
|
no: 'Norsk',
|
||||||
'zh-CN': '简体中文',
|
pt: 'Português',
|
||||||
fi: 'Suomi',
|
fi: 'Suomi',
|
||||||
ru: 'Русский',
|
ru: 'Русский',
|
||||||
|
uk: 'Українська',
|
||||||
ja: '日本語',
|
ja: '日本語',
|
||||||
|
'zh-CN': '简体中文',
|
||||||
|
'zh-HK': '繁體中文(香港)',
|
||||||
}.freeze
|
}.freeze
|
||||||
|
|
||||||
def human_locale(locale)
|
def human_locale(locale)
|
||||||
|
|
|
@ -2,22 +2,40 @@
|
||||||
|
|
||||||
module StreamEntriesHelper
|
module StreamEntriesHelper
|
||||||
def display_name(account)
|
def display_name(account)
|
||||||
account.display_name.blank? ? account.username : account.display_name
|
account.display_name.presence || account.username
|
||||||
|
end
|
||||||
|
|
||||||
|
def stream_link_target
|
||||||
|
embedded_view? ? '_blank' : nil
|
||||||
end
|
end
|
||||||
|
|
||||||
def acct(account)
|
def acct(account)
|
||||||
"@#{account.acct}#{@external_links && account.local? ? "@#{Rails.configuration.x.local_domain}" : ''}"
|
"@#{account.acct}#{embedded_view? && account.local? ? "@#{Rails.configuration.x.local_domain}" : ''}"
|
||||||
end
|
end
|
||||||
|
|
||||||
def entry_classes(status, is_predecessor, is_successor, include_threads)
|
def style_classes(status, is_predecessor, is_successor, include_threads)
|
||||||
classes = ['entry']
|
classes = ['entry']
|
||||||
classes << 'entry-reblog u-repost-of h-cite' if status.reblog?
|
classes << 'entry-predecessor' if is_predecessor
|
||||||
classes << 'entry-predecessor u-in-reply-to h-cite' if is_predecessor
|
classes << 'entry-reblog' if status.reblog?
|
||||||
classes << 'entry-successor u-comment h-cite' if is_successor
|
classes << 'entry-successor' if is_successor
|
||||||
classes << 'entry-center h-entry' if include_threads
|
classes << 'entry-center' if include_threads
|
||||||
classes.join(' ')
|
classes.join(' ')
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def microformats_classes(status, is_direct_parent, is_direct_child)
|
||||||
|
classes = []
|
||||||
|
classes << 'p-in-reply-to' if is_direct_parent
|
||||||
|
classes << 'p-repost-of' if status.reblog? && is_direct_parent
|
||||||
|
classes << 'p-comment' if is_direct_child
|
||||||
|
classes.join(' ')
|
||||||
|
end
|
||||||
|
|
||||||
|
def microformats_h_class(status, is_predecessor, is_successor, include_threads)
|
||||||
|
return 'h-cite' if is_predecessor || status.reblog || is_successor
|
||||||
|
return 'h-entry' unless include_threads
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
def rtl?(text)
|
def rtl?(text)
|
||||||
return false if text.empty?
|
return false if text.empty?
|
||||||
|
|
||||||
|
@ -30,4 +48,10 @@ module StreamEntriesHelper
|
||||||
|
|
||||||
rtl_size / ltr_size > 0.3
|
rtl_size / ltr_size > 0.3
|
||||||
end
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def embedded_view?
|
||||||
|
params[:controller] == 'stream_entries' && params[:action] == 'embed'
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -26,8 +26,8 @@ class AtomSerializer
|
||||||
append_element(author, 'link', nil, rel: :avatar, type: account.avatar_content_type, 'media:width': 120, 'media:height': 120, href: full_asset_url(account.avatar.url(:original)))
|
append_element(author, 'link', nil, rel: :avatar, type: account.avatar_content_type, 'media:width': 120, 'media:height': 120, href: full_asset_url(account.avatar.url(:original)))
|
||||||
append_element(author, 'link', nil, rel: :header, type: account.header_content_type, 'media:width': 700, 'media:height': 335, href: full_asset_url(account.header.url(:original)))
|
append_element(author, 'link', nil, rel: :header, type: account.header_content_type, 'media:width': 700, 'media:height': 335, href: full_asset_url(account.header.url(:original)))
|
||||||
append_element(author, 'poco:preferredUsername', account.username)
|
append_element(author, 'poco:preferredUsername', account.username)
|
||||||
append_element(author, 'poco:displayName', account.display_name) unless account.display_name.blank?
|
append_element(author, 'poco:displayName', account.display_name) if account.display_name?
|
||||||
append_element(author, 'poco:note', Formatter.instance.simplified_format(account).to_str) unless account.note.blank?
|
append_element(author, 'poco:note', Formatter.instance.simplified_format(account).to_str) if account.note?
|
||||||
append_element(author, 'mastodon:scope', account.locked? ? :private : :public)
|
append_element(author, 'mastodon:scope', account.locked? ? :private : :public)
|
||||||
|
|
||||||
author
|
author
|
||||||
|
@ -327,7 +327,7 @@ class AtomSerializer
|
||||||
end
|
end
|
||||||
|
|
||||||
def serialize_status_attributes(entry, status)
|
def serialize_status_attributes(entry, status)
|
||||||
append_element(entry, 'summary', status.spoiler_text) unless status.spoiler_text.blank?
|
append_element(entry, 'summary', status.spoiler_text) if status.spoiler_text?
|
||||||
append_element(entry, 'content', Formatter.instance.format(status.proper).to_str, type: 'html')
|
append_element(entry, 'content', Formatter.instance.format(status.proper).to_str, type: 'html')
|
||||||
|
|
||||||
status.mentions.each do |mentioned|
|
status.mentions.each do |mentioned|
|
||||||
|
|
|
@ -95,6 +95,6 @@ class Formatter
|
||||||
end
|
end
|
||||||
|
|
||||||
def mention_html(match, account)
|
def mention_html(match, account)
|
||||||
"#{match.split('@').first}<a href=\"#{TagManager.instance.url_for(account)}\" class=\"h-card u-url p-nickname mention\">@<span>#{account.username}</span></a>"
|
"#{match.split('@').first}<span class=\"h-card\"><a href=\"#{TagManager.instance.url_for(account)}\" class=\"u-url mention\">@#{account.username}</a></span>"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
66
app/lib/webfinger_resource.rb
Normal file
66
app/lib/webfinger_resource.rb
Normal file
|
@ -0,0 +1,66 @@
|
||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
class WebfingerResource
|
||||||
|
attr_reader :resource
|
||||||
|
|
||||||
|
def initialize(resource)
|
||||||
|
@resource = resource
|
||||||
|
end
|
||||||
|
|
||||||
|
def username
|
||||||
|
case resource
|
||||||
|
when /\Ahttps?/i
|
||||||
|
username_from_url
|
||||||
|
when /\@/
|
||||||
|
username_from_acct
|
||||||
|
else
|
||||||
|
raise(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def username_from_url
|
||||||
|
if account_show_page?
|
||||||
|
path_params[:username]
|
||||||
|
else
|
||||||
|
raise ActiveRecord::RecordNotFound
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def account_show_page?
|
||||||
|
path_params[:controller] == 'accounts' && path_params[:action] == 'show'
|
||||||
|
end
|
||||||
|
|
||||||
|
def path_params
|
||||||
|
Rails.application.routes.recognize_path(resource)
|
||||||
|
end
|
||||||
|
|
||||||
|
def username_from_acct
|
||||||
|
if domain_matches_local?
|
||||||
|
local_username
|
||||||
|
else
|
||||||
|
raise ActiveRecord::RecordNotFound
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def split_acct
|
||||||
|
resource_without_acct_string.split('@')
|
||||||
|
end
|
||||||
|
|
||||||
|
def resource_without_acct_string
|
||||||
|
resource.gsub(/\Aacct:/, '')
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_username
|
||||||
|
split_acct.first
|
||||||
|
end
|
||||||
|
|
||||||
|
def local_domain
|
||||||
|
split_acct.last
|
||||||
|
end
|
||||||
|
|
||||||
|
def domain_matches_local?
|
||||||
|
TagManager.instance.local_domain?(local_domain)
|
||||||
|
end
|
||||||
|
end
|
|
@ -7,9 +7,10 @@ class UserMailer < Devise::Mailer
|
||||||
def confirmation_instructions(user, token, _opts = {})
|
def confirmation_instructions(user, token, _opts = {})
|
||||||
@resource = user
|
@resource = user
|
||||||
@token = token
|
@token = token
|
||||||
|
@instance = Rails.configuration.x.local_domain
|
||||||
|
|
||||||
I18n.with_locale(@resource.locale || I18n.default_locale) do
|
I18n.with_locale(@resource.locale || I18n.default_locale) do
|
||||||
mail to: @resource.unconfirmed_email.blank? ? @resource.email : @resource.unconfirmed_email
|
mail to: @resource.unconfirmed_email.blank? ? @resource.email : @resource.unconfirmed_email, subject: I18n.t('devise.mailer.confirmation_instructions.subject', instance: @instance)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -18,7 +19,7 @@ class UserMailer < Devise::Mailer
|
||||||
@token = token
|
@token = token
|
||||||
|
|
||||||
I18n.with_locale(@resource.locale || I18n.default_locale) do
|
I18n.with_locale(@resource.locale || I18n.default_locale) do
|
||||||
mail to: @resource.email
|
mail to: @resource.email, subject: I18n.t('devise.mailer.reset_password_instructions.subject')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -26,7 +27,7 @@ class UserMailer < Devise::Mailer
|
||||||
@resource = user
|
@resource = user
|
||||||
|
|
||||||
I18n.with_locale(@resource.locale || I18n.default_locale) do
|
I18n.with_locale(@resource.locale || I18n.default_locale) do
|
||||||
mail to: @resource.email
|
mail to: @resource.email, subject: I18n.t('devise.mailer.password_change.subject')
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
class Import < ApplicationRecord
|
class Import < ApplicationRecord
|
||||||
self.inheritance_column = false
|
self.inheritance_column = false
|
||||||
|
|
||||||
enum type: [:following, :blocking]
|
enum type: [:following, :blocking, :muting]
|
||||||
|
|
||||||
belongs_to :account
|
belongs_to :account
|
||||||
|
|
||||||
|
|
|
@ -12,7 +12,7 @@ class AfterBlockService < BaseService
|
||||||
home_key = FeedManager.instance.key(:home, account.id)
|
home_key = FeedManager.instance.key(:home, account.id)
|
||||||
|
|
||||||
redis.pipelined do
|
redis.pipelined do
|
||||||
target_account.statuses.select('id').find_each do |status|
|
target_account.statuses.select('id').reorder(nil).find_each do |status|
|
||||||
redis.zrem(home_key, status.id)
|
redis.zrem(home_key, status.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -34,7 +34,7 @@ class FanOutOnWriteService < BaseService
|
||||||
def deliver_to_followers(status)
|
def deliver_to_followers(status)
|
||||||
Rails.logger.debug "Delivering status #{status.id} to followers"
|
Rails.logger.debug "Delivering status #{status.id} to followers"
|
||||||
|
|
||||||
status.account.followers.where(domain: nil).joins(:user).where('users.current_sign_in_at > ?', 14.days.ago).select(:id).find_each do |follower|
|
status.account.followers.where(domain: nil).joins(:user).where('users.current_sign_in_at > ?', 14.days.ago).select(:id).reorder(nil).find_each do |follower|
|
||||||
FeedInsertWorker.perform_async(status.id, follower.id)
|
FeedInsertWorker.perform_async(status.id, follower.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ class MuteService < BaseService
|
||||||
def clear_home_timeline(account, target_account)
|
def clear_home_timeline(account, target_account)
|
||||||
home_key = FeedManager.instance.key(:home, account.id)
|
home_key = FeedManager.instance.key(:home, account.id)
|
||||||
|
|
||||||
target_account.statuses.select('id').find_each do |status|
|
target_account.statuses.select('id').reorder(nil).find_each do |status|
|
||||||
redis.zrem(home_key, status.id)
|
redis.zrem(home_key, status.id)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -12,7 +12,7 @@ class SuspendAccountService < BaseService
|
||||||
private
|
private
|
||||||
|
|
||||||
def purge_content
|
def purge_content
|
||||||
@account.statuses.find_each do |status|
|
@account.statuses.reorder(nil).find_each do |status|
|
||||||
RemoveStatusService.new.call(status)
|
RemoveStatusService.new.call(status)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
76
app/views/about/terms.no.html.haml
Normal file
76
app/views/about/terms.no.html.haml
Normal file
|
@ -0,0 +1,76 @@
|
||||||
|
- content_for :page_title do
|
||||||
|
#{Rails.configuration.x.local_domain} Personvern og villkår for bruk av nettstedet
|
||||||
|
|
||||||
|
.wrapper
|
||||||
|
%h2 Personvernserklæring
|
||||||
|
|
||||||
|
%h3#collect Hvilke opplysninger samler vi?
|
||||||
|
|
||||||
|
%p Vi samler opplysninger fra deg når du registrerer deg på nettstedet vårt, og vi samler data når du deltar på forumet ved å lese, skrive og evaluere innholdet som deles her.
|
||||||
|
|
||||||
|
%p Når du registrerer deg på nettstedet vårt, kan du bli bedt om å oppgi navnet og e-postadressen din. Imidlertid kan du besøke nettstedet vårt uten å registrere deg. E-postadressen din vil bli bekreftet med en e-post som inneholder en unik lenke. Hvis siden den lenker til, blir besøkt, vet vi at du har kontroll over e-postadressen.
|
||||||
|
|
||||||
|
%p Når du registrerer deg og skriver innlegg, registrerer vi IP-adressen som innlegget stammer fra. Vi kan også oppbevare logger som inkluderer IP-adressen til alle forespørslene sendt til tjeneren vår.
|
||||||
|
|
||||||
|
%h3#use Hva bruker vi opplysningene dine til?
|
||||||
|
|
||||||
|
%p Alle opplysningene vi samler fra deg, kan bli brukt på en av følgende måter:
|
||||||
|
|
||||||
|
%ul
|
||||||
|
%li For å gjøre opplevelsen din mer personlig. Opplysningene dine hjelper oss å svare bedre på dine individuelle behov.
|
||||||
|
%li For å forbedre nettstedet vårt. Vi jobber konstant for å forbedre nettstedets tilbud basert på opplysningene og tilbakemeldingene vi mottar fra deg.
|
||||||
|
%li For å forbedre vår kundeservice. Dine opplysninger hjelper oss å svare mer effektivt på dine forespørsler sendt til kundeservice eller behov om støtte.
|
||||||
|
%li For å sende periodiske e-poster. E-postadressen du oppgir, kan bli brukt til å sende deg informasjon, påminnelser som du ber om ved endringer av emner eller ved svar til brukernavnet ditt, til henvendelser, og/eller andre forspørsler eller andre spørsmål.
|
||||||
|
|
||||||
|
%h3#protect Hvordan sikrer vi opplysningene?
|
||||||
|
|
||||||
|
%p Vi gjennomfører flere sikkerhetstiltak for å holde personopplysningene dine sikre når du skriver inn, lagrer eller henter dem.
|
||||||
|
|
||||||
|
%h3#data-retention Hva er retningslinjene deres for lagring av data?
|
||||||
|
|
||||||
|
%p Vi vil forsøke i god tro å:
|
||||||
|
|
||||||
|
%ul
|
||||||
|
%li Ikke oppbevare tjener-logger som inneholder IP-adressen til alle forespørslene til denne tjeneren i lenger enn i 90 dager.
|
||||||
|
%li Ikke oppbevare IP-adressene forbundet med registrerte brukere og deres innlegg lenger enn i 5 år.
|
||||||
|
|
||||||
|
%h3#cookies Bruker vi informasjonskapsler?
|
||||||
|
|
||||||
|
%p Ja. Informasjonskapsler er små filer som et nettsted eller dets tjenesteleverandør overfører til harddisken på datamaskinen din gjennom nettleseren din (dersom du tillater det). Disse informasjonskapslene gjør det mulig for nettstedet å gjenkjenne nettleseren din og, dersom du har en konto, knytte nettleseren til den.
|
||||||
|
|
||||||
|
%p Vi bruker informasjonskapsler for å forstå og lagre preferansene dine for fremtidige besøk og for å samle aggregatdata om trafikk på og samhandling med nettstedet slik at vi kan tilby bedre opplevelser og verktøy på nettstedet i fremtiden. Vi kan inngå avtaler med tredjeparts tjenesteleverandører for å bistå oss i å forstå besøkerne våres bedre. Disse tjenesteleverandørene har ikke lov til å bruke opplysningene samlet på våres vegne unntatt til å hjelpe oss å gjennomføre og forbedre anliggendet vårt.
|
||||||
|
|
||||||
|
%h3#disclose Gir vi noen opplysninger videre til andre parter?
|
||||||
|
|
||||||
|
%p Vi verken selger, handler med eller overfører på noen annen måte til andre parter dine identifiserbare personopplysninger. Dette inkluderer ikke tredjeparter som har vår tillit og bistår oss i å drive nettstedet, utføre våre anliggender eller yter tjenester til deg, så lenge disse partene samtykker til å behandle disse opplysningene fortrolig. Vi kan også frigi opplysningene dine dersom vi tror at å frigi dem er hensiktsmessig for å overholde loven, håndheve nettstedet retningslinjer eller beskytte våre og andres rettigheter. Imidlertid kan opplysninger som ikke er personlig identifiserbare, bli delt med andre parter for markedsføring, reklame eller annet bruk.
|
||||||
|
|
||||||
|
%h3#third-party Tredjeparts lenker
|
||||||
|
|
||||||
|
%p Av og til, etter skjønn, kan vil inkludere eller tilby tredjeparts produkter eller tjenester på nettstedet vårt. Disse tredjeparts nettstedene har separate og selvstendige personvernerklæringer. Vi bærer derfor intet ansvar eller forpliktelser for innholdet eller aktivitetene til disse nettstedene det lenkes til. Ikke mindre prøver vi å bevare vår eget nettsteds integritet og ønsker enhver tilbakemelding om disse nettstedene velkomne.
|
||||||
|
|
||||||
|
%h3#coppa Overensstemmelse med Children's Online Privacy Protection Act
|
||||||
|
|
||||||
|
%p
|
||||||
|
Nettstedet er rettet mot folk som er minst 13 år gamle. Dersom denne tjeneren er i USA, og du er under 13 år i henhold til kravene i COPPA
|
||||||
|
= surround '(', '),' do
|
||||||
|
= link_to 'Children\'s Online Privacy Protection Act', 'https://en.wikipedia.org/wiki/Children%27s_Online_Privacy_Protection_Act'
|
||||||
|
ikke bruk dette nettstedet.
|
||||||
|
|
||||||
|
%h3#online Personvernerklæring bare for nettet
|
||||||
|
|
||||||
|
%p Denne nett-personvernerklæringen gjelder bare for informasjon samlet gjennom nettstedet vårt og ikke for opplysninger samlet når en er frakoblet.
|
||||||
|
|
||||||
|
%h3#consent Ditt samtykke
|
||||||
|
|
||||||
|
%p Ved å bruke dette nettstedet samtykker du til nettstedets personvernerklæring.
|
||||||
|
|
||||||
|
%h3#changes Endringer i vår personvernerklæring
|
||||||
|
|
||||||
|
%p Dersom vi beslutter å endre personvernerklæringen vår, vil vi publisere disse endringene på denne siden.
|
||||||
|
|
||||||
|
%p Dette dokumentet er lisensiert under CC-BY-SA. De ble sist oppdatert 12. april 2017.
|
||||||
|
|
||||||
|
%p
|
||||||
|
Dokumentet er en adoptert og endret versjon fra
|
||||||
|
= succeed '.' do
|
||||||
|
= link_to 'Discourse privacy policy', 'https://github.com/discourse/discourse'
|
|
@ -13,7 +13,7 @@
|
||||||
%h1.name
|
%h1.name
|
||||||
%span.p-name.emojify= display_name(@account)
|
%span.p-name.emojify= display_name(@account)
|
||||||
%small
|
%small
|
||||||
%span.p-nickname= "@#{@account.username}"
|
%span= "@#{@account.username}"
|
||||||
= fa_icon('lock') if @account.locked?
|
= fa_icon('lock') if @account.locked?
|
||||||
.details
|
.details
|
||||||
.bio
|
.bio
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
%i.fa.fa-check
|
%i.fa.fa-check
|
||||||
%td= distance_of_time_in_words(Time.now, subscription.expires_at)
|
%td= distance_of_time_in_words(Time.now, subscription.expires_at)
|
||||||
%td
|
%td
|
||||||
- if subscription.last_successful_delivery_at.nil?
|
- if subscription.last_successful_delivery_at?
|
||||||
%i.fa.fa-times
|
|
||||||
- else
|
|
||||||
= l subscription.last_successful_delivery_at
|
= l subscription.last_successful_delivery_at
|
||||||
|
- else
|
||||||
|
%i.fa.fa-times
|
||||||
|
|
||||||
= paginate @subscriptions
|
= paginate @subscriptions
|
||||||
|
|
|
@ -1,12 +1,12 @@
|
||||||
- content_for :page_title do
|
- content_for :page_title do
|
||||||
Reports
|
= t('reports.reports')
|
||||||
|
|
||||||
.filters
|
.filters
|
||||||
.filter-subset
|
.filter-subset
|
||||||
%strong Status
|
%strong= t('reports.status')
|
||||||
%ul
|
%ul
|
||||||
%li= filter_link_to 'Unresolved', action_taken: nil
|
%li= filter_link_to t('reports.unresolved'), action_taken: nil
|
||||||
%li= filter_link_to 'Resolved', action_taken: '1'
|
%li= filter_link_to t('reports.resolved'), action_taken: '1'
|
||||||
|
|
||||||
= form_tag do
|
= form_tag do
|
||||||
|
|
||||||
|
@ -14,10 +14,10 @@
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th
|
%th
|
||||||
%th ID
|
%th= t('reports.id')
|
||||||
%th Target
|
%th= t('reports.target')
|
||||||
%th Reported by
|
%th= t('reports.reported_by')
|
||||||
%th Comment
|
%th= t('reports.comment.label')
|
||||||
%th
|
%th
|
||||||
%tbody
|
%tbody
|
||||||
- @reports.each do |report|
|
- @reports.each do |report|
|
||||||
|
@ -27,6 +27,6 @@
|
||||||
%td= link_to report.target_account.acct, admin_account_path(report.target_account.id)
|
%td= link_to report.target_account.acct, admin_account_path(report.target_account.id)
|
||||||
%td= link_to report.account.acct, admin_account_path(report.account.id)
|
%td= link_to report.account.acct, admin_account_path(report.account.id)
|
||||||
%td= truncate(report.comment, length: 30, separator: ' ')
|
%td= truncate(report.comment, length: 30, separator: ' ')
|
||||||
%td= table_link_to 'circle', 'View', admin_report_path(report)
|
%td= table_link_to 'circle', t('reports.view'), admin_report_path(report)
|
||||||
|
|
||||||
= paginate @reports
|
= paginate @reports
|
||||||
|
|
|
@ -1,20 +1,18 @@
|
||||||
- content_for :page_title do
|
- content_for :page_title do
|
||||||
= "Report ##{@report.id}"
|
= t('reports.report', id: @report.id)
|
||||||
|
|
||||||
.report-accounts
|
.report-accounts
|
||||||
.report-accounts__item
|
.report-accounts__item
|
||||||
%strong Reported account:
|
%strong= t('reports.reported_account')
|
||||||
= render partial: 'authorize_follow/card', locals: { account: @report.target_account }
|
= render partial: 'authorize_follow/card', locals: { account: @report.target_account }
|
||||||
.report-accounts__item
|
.report-accounts__item
|
||||||
%strong Reported by:
|
%strong= t('reports.reported_by')
|
||||||
= render partial: 'authorize_follow/card', locals: { account: @report.account }
|
= render partial: 'authorize_follow/card', locals: { account: @report.account }
|
||||||
|
|
||||||
%p
|
%p
|
||||||
%strong Comment:
|
%strong= t('reports.comment.label')
|
||||||
- if @report.comment.blank?
|
\:
|
||||||
None
|
= @report.comment.presence || t('reports.comment.none')
|
||||||
- else
|
|
||||||
= @report.comment
|
|
||||||
|
|
||||||
- unless @statuses.empty?
|
- unless @statuses.empty?
|
||||||
%hr/
|
%hr/
|
||||||
|
@ -24,7 +22,7 @@
|
||||||
.activity-stream.activity-stream-headless
|
.activity-stream.activity-stream-headless
|
||||||
.entry= render partial: 'stream_entries/simple_status', locals: { status: status }
|
.entry= render partial: 'stream_entries/simple_status', locals: { status: status }
|
||||||
.report-status__actions
|
.report-status__actions
|
||||||
= link_to remove_admin_report_path(@report, status_id: status.id), method: :post, class: 'icon-button', style: 'font-size: 24px; width: 24px; height: 24px', title: 'Delete' do
|
= link_to remove_admin_report_path(@report, status_id: status.id), method: :post, class: 'icon-button', style: 'font-size: 24px; width: 24px; height: 24px', title: t('reports.delete') do
|
||||||
= fa_icon 'trash'
|
= fa_icon 'trash'
|
||||||
|
|
||||||
- if !@report.action_taken?
|
- if !@report.action_taken?
|
||||||
|
@ -32,10 +30,10 @@
|
||||||
|
|
||||||
%div{ style: 'overflow: hidden' }
|
%div{ style: 'overflow: hidden' }
|
||||||
%div{ style: 'float: right' }
|
%div{ style: 'float: right' }
|
||||||
= link_to 'Silence account', silence_admin_report_path(@report), method: :post, class: 'button'
|
= link_to t('reports.silence_account'), silence_admin_report_path(@report), method: :post, class: 'button'
|
||||||
= link_to 'Suspend account', suspend_admin_report_path(@report), method: :post, class: 'button'
|
= link_to t('reports.suspend_account'), suspend_admin_report_path(@report), method: :post, class: 'button'
|
||||||
%div{ style: 'float: left' }
|
%div{ style: 'float: left' }
|
||||||
= link_to 'Mark as resolved', resolve_admin_report_path(@report), method: :post, class: 'button'
|
= link_to t('reports.mark_as_resolved'), resolve_admin_report_path(@report), method: :post, class: 'button'
|
||||||
- elsif !@report.action_taken_by_account.nil?
|
- elsif !@report.action_taken_by_account.nil?
|
||||||
%hr/
|
%hr/
|
||||||
|
|
||||||
|
|
|
@ -1,52 +1,40 @@
|
||||||
- content_for :page_title do
|
- content_for :page_title do
|
||||||
Site Settings
|
= t('admin.settings.title')
|
||||||
|
|
||||||
%table.table
|
%table.table
|
||||||
%colgroup
|
%colgroup
|
||||||
%col{ width: '35%' }/
|
%col{ width: '35%' }/
|
||||||
%thead
|
%thead
|
||||||
%tr
|
%tr
|
||||||
%th Setting
|
%th= t('admin.settings.setting')
|
||||||
%th Click to edit
|
%th= t('admin.settings.click_to_edit')
|
||||||
%tbody
|
%tbody
|
||||||
%tr
|
%tr
|
||||||
%td{ rowspan: 2 }
|
%td{ rowspan: 2 }
|
||||||
%strong Contact information
|
%strong= t('admin.settings.contact_information.label')
|
||||||
%td= best_in_place @settings['site_contact_username'], :value, url: admin_setting_path(@settings['site_contact_username']), place_holder: 'Enter a username'
|
%td= best_in_place @settings['site_contact_username'], :value, url: admin_setting_path(@settings['site_contact_username']), place_holder: t('admin.settings.contact_information.username')
|
||||||
%tr
|
%tr
|
||||||
%td= best_in_place @settings['site_contact_email'], :value, url: admin_setting_path(@settings['site_contact_email']), place_holder: 'Enter a public e-mail address'
|
%td= best_in_place @settings['site_contact_email'], :value, url: admin_setting_path(@settings['site_contact_email']), place_holder: t('admin.settings.contact_information.email')
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
%strong Site title
|
%strong= t('admin.settings.site_title')
|
||||||
%td= best_in_place @settings['site_title'], :value, url: admin_setting_path(@settings['site_title'])
|
%td= best_in_place @settings['site_title'], :value, url: admin_setting_path(@settings['site_title'])
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
%strong Site description
|
%strong= t('admin.settings.site_description.title')
|
||||||
%br/
|
%p= t('admin.settings.site_description.desc_html')
|
||||||
Displayed as a paragraph on the frontpage and used as a meta tag.
|
|
||||||
%br/
|
|
||||||
You can use HTML tags, in particular
|
|
||||||
%code= '<a>'
|
|
||||||
and
|
|
||||||
%code= '<em>'
|
|
||||||
%td= best_in_place @settings['site_description'], :value, as: :textarea, url: admin_setting_path(@settings['site_description'])
|
%td= best_in_place @settings['site_description'], :value, as: :textarea, url: admin_setting_path(@settings['site_description'])
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
%strong Extended site description
|
%strong= t('admin.settings.site_description_extended.title')
|
||||||
%br/
|
%p= t('admin.settings.site_description_extended.desc_html')
|
||||||
Displayed on extended information page
|
|
||||||
%br/
|
|
||||||
You can use HTML tags
|
|
||||||
%td= best_in_place @settings['site_extended_description'], :value, as: :textarea, url: admin_setting_path(@settings['site_extended_description'])
|
%td= best_in_place @settings['site_extended_description'], :value, as: :textarea, url: admin_setting_path(@settings['site_extended_description'])
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
%strong Open registration
|
%strong= t('admin.settings.registrations.open.title')
|
||||||
%td= best_in_place @settings['open_registrations'], :value, as: :checkbox, collection: { false: 'Disabled', true: 'Enabled'}, url: admin_setting_path(@settings['open_registrations'])
|
%td= best_in_place @settings['open_registrations'], :value, as: :checkbox, collection: { false: t('admin.settings.registrations.open.disabled'), true: t('admin.settings.registrations.open.enabled')}, url: admin_setting_path(@settings['open_registrations'])
|
||||||
%tr
|
%tr
|
||||||
%td
|
%td
|
||||||
%strong Closed registration message
|
%strong= t('admin.settings.registrations.closed_message.title')
|
||||||
%br/
|
%p= t('admin.settings.registrations.closed_message.desc_html')
|
||||||
Displayed on frontpage when registrations are closed
|
|
||||||
%br/
|
|
||||||
You can use HTML tags
|
|
||||||
%td= best_in_place @settings['closed_registrations_message'], :value, as: :textarea, url: admin_setting_path(@settings['closed_registrations_message'])
|
%td= best_in_place @settings['closed_registrations_message'], :value, as: :textarea, url: admin_setting_path(@settings['closed_registrations_message'])
|
||||||
|
|
|
@ -7,5 +7,5 @@
|
||||||
%strong.emojify= display_name(account)
|
%strong.emojify= display_name(account)
|
||||||
%span= "@#{account.acct}"
|
%span= "@#{account.acct}"
|
||||||
|
|
||||||
- unless account.note.blank?
|
- if account.note?
|
||||||
.account__header__content.emojify= Formatter.instance.simplified_format(account)
|
.account__header__content.emojify= Formatter.instance.simplified_format(account)
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- content_for :page_title do
|
- content_for :page_title do
|
||||||
The page you were looking for doesn't exist
|
= t('errors.404')
|
||||||
|
|
||||||
- content_for :content do
|
- content_for :content do
|
||||||
The page you were looking for doesn't exist
|
= t('errors.404')
|
|
@ -1,5 +1,5 @@
|
||||||
- content_for :page_title do
|
- content_for :page_title do
|
||||||
The page you were looking for doesn't exist anymore
|
= t('errors.410')
|
||||||
|
|
||||||
- content_for :content do
|
- content_for :content do
|
||||||
The page you were looking for doesn't exist anymore
|
= t('errors.410')
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
- content_for :page_title do
|
- content_for :page_title do
|
||||||
Security verification failed
|
= t('errors.422.title')
|
||||||
|
|
||||||
- content_for :content do
|
- content_for :content do
|
||||||
Security verification failed. Are you blocking cookies?
|
= t('errors.422.content')
|
||||||
|
|
|
@ -15,3 +15,7 @@
|
||||||
%th= t('exports.blocks')
|
%th= t('exports.blocks')
|
||||||
%td= @total_blocks
|
%td= @total_blocks
|
||||||
%td= table_link_to 'download', t('exports.csv'), settings_exports_blocks_path(format: :csv)
|
%td= table_link_to 'download', t('exports.csv'), settings_exports_blocks_path(format: :csv)
|
||||||
|
%tr
|
||||||
|
%th= t('exports.mutes')
|
||||||
|
%td= @total_mutes
|
||||||
|
%td= table_link_to 'download', t('exports.csv'), settings_exports_mutes_path(format: :csv)
|
||||||
|
|
|
@ -1,18 +1,19 @@
|
||||||
.detailed-status.light
|
.detailed-status.light
|
||||||
= link_to TagManager.instance.url_for(status.account), class: 'detailed-status__display-name p-author h-card', target: @external_links ? '_blank' : nil, rel: 'noopener' do
|
= link_to TagManager.instance.url_for(status.account), class: 'detailed-status__display-name p-author h-card', target: stream_link_target, rel: 'noopener' do
|
||||||
%div
|
%div
|
||||||
%div.avatar
|
%div.avatar
|
||||||
= image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'u-photo'
|
= image_tag status.account.avatar.url(:original), width: 48, height: 48, alt: '', class: 'u-photo'
|
||||||
%span.display-name
|
%span.display-name
|
||||||
%strong.p-name.emojify= display_name(status.account)
|
%strong.p-name.emojify= display_name(status.account)
|
||||||
%span.p-nickname= acct(status.account)
|
%span= acct(status.account)
|
||||||
|
|
||||||
.status__content.e-content.p-name.emojify<
|
.status__content.p-name.emojify<
|
||||||
- unless status.spoiler_text.blank?
|
- if status.spoiler_text?
|
||||||
%p{ style: 'margin-bottom: 0' }<
|
%p{ style: 'margin-bottom: 0' }<
|
||||||
%span>= "#{status.spoiler_text} "
|
%span.p-summary>= "#{status.spoiler_text} "
|
||||||
%a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
|
%a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
|
||||||
%div{ style: "display: #{status.spoiler_text.blank? ? 'block' : 'none'}; direction: #{rtl?(status.content) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status)
|
%div.e-content{ style: "display: #{status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl?(status.content) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status)
|
||||||
|
|
||||||
|
|
||||||
- unless status.media_attachments.empty?
|
- unless status.media_attachments.empty?
|
||||||
- if status.media_attachments.first.video?
|
- if status.media_attachments.first.video?
|
||||||
|
@ -30,7 +31,7 @@
|
||||||
|
|
||||||
%div.detailed-status__meta
|
%div.detailed-status__meta
|
||||||
%data.dt-published{ value: status.created_at.to_time.iso8601 }
|
%data.dt-published{ value: status.created_at.to_time.iso8601 }
|
||||||
= link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime u-url u-uid', target: @external_links ? '_blank' : nil, rel: 'noopener' do
|
= link_to TagManager.instance.url_for(status), class: 'detailed-status__datetime u-url u-uid', target: stream_link_target, rel: 'noopener' do
|
||||||
%span= l(status.created_at)
|
%span= l(status.created_at)
|
||||||
·
|
·
|
||||||
- if status.application
|
- if status.application
|
||||||
|
@ -49,4 +50,4 @@
|
||||||
|
|
||||||
- if user_signed_in?
|
- if user_signed_in?
|
||||||
·
|
·
|
||||||
= link_to t('statuses.open_in_web'), web_url("statuses/#{status.id}"), class: 'open-in-web-link'
|
= link_to t('statuses.open_in_web'), web_url("statuses/#{status.id}"), class: 'open-in-web-link', target: '_blank'
|
||||||
|
|
|
@ -1,23 +1,23 @@
|
||||||
.status.light
|
.status.light
|
||||||
.status__header
|
.status__header
|
||||||
.status__meta
|
.status__meta
|
||||||
= link_to time_ago_in_words(status.created_at), TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', title: l(status.created_at), target: @external_links ? '_blank' : nil, rel: 'noopener'
|
= link_to time_ago_in_words(status.created_at), TagManager.instance.url_for(status), class: 'status__relative-time u-url u-uid', title: l(status.created_at), target: stream_link_target, rel: 'noopener'
|
||||||
%data.dt-published{ value: status.created_at.to_time.iso8601 }
|
%data.dt-published{ value: status.created_at.to_time.iso8601 }
|
||||||
|
|
||||||
= link_to TagManager.instance.url_for(status.account), class: 'status__display-name p-author h-card', target: @external_links ? '_blank' : nil, rel: 'noopener' do
|
= link_to TagManager.instance.url_for(status.account), class: 'status__display-name p-author h-card', target: stream_link_target, rel: 'noopener' do
|
||||||
.status__avatar
|
.status__avatar
|
||||||
%div
|
%div
|
||||||
= image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo'
|
= image_tag status.account.avatar(:original), width: 48, height: 48, alt: '', class: 'u-photo'
|
||||||
%span.display-name
|
%span.display-name
|
||||||
%strong.p-name.emojify= display_name(status.account)
|
%strong.p-name.emojify= display_name(status.account)
|
||||||
%span.p-nickname= acct(status.account)
|
%span= acct(status.account)
|
||||||
|
|
||||||
.status__content.e-content.p-name.emojify<
|
.status__content.p-name.emojify<
|
||||||
- unless status.spoiler_text.blank?
|
- if status.spoiler_text?
|
||||||
%p{ style: 'margin-bottom: 0' }<
|
%p{ style: 'margin-bottom: 0' }<
|
||||||
%span>= "#{status.spoiler_text} "
|
%span.p-summary>= "#{status.spoiler_text} "
|
||||||
%a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
|
%a.status__content__spoiler-link{ href: '#' }= t('statuses.show_more')
|
||||||
%div{ style: "display: #{status.spoiler_text.blank? ? 'block' : 'none'}; direction: #{rtl?(status.content) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status)
|
%div.e-content{ style: "display: #{status.spoiler_text? ? 'none' : 'block'}; direction: #{rtl?(status.content) ? 'rtl' : 'ltr'}" }= Formatter.instance.format(status)
|
||||||
|
|
||||||
- unless status.media_attachments.empty?
|
- unless status.media_attachments.empty?
|
||||||
.status__attachments
|
.status__attachments
|
||||||
|
|
|
@ -1,12 +1,22 @@
|
||||||
- include_threads ||= false
|
- include_threads ||= false
|
||||||
- is_predecessor ||= false
|
- is_predecessor ||= false
|
||||||
- is_successor ||= false
|
- is_successor ||= false
|
||||||
|
- direct_reply_id ||= false
|
||||||
|
- parent_id ||= false
|
||||||
|
- is_direct_parent = direct_reply_id == status.id
|
||||||
|
- is_direct_child = parent_id == status.in_reply_to_id
|
||||||
|
- parent_id ||= false
|
||||||
- centered ||= include_threads && !is_predecessor && !is_successor
|
- centered ||= include_threads && !is_predecessor && !is_successor
|
||||||
|
- h_class = microformats_h_class(status, is_predecessor, is_successor, include_threads)
|
||||||
|
- style_classes = style_classes(status, is_predecessor, is_successor, include_threads)
|
||||||
|
- mf_classes = microformats_classes(status, is_direct_parent, is_direct_child)
|
||||||
|
- entry_classes = h_class + ' ' + mf_classes + ' ' + style_classes
|
||||||
|
|
||||||
- if status.reply? && include_threads
|
- if status.reply? && include_threads
|
||||||
= render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true }
|
= render partial: 'stream_entries/status', collection: @ancestors, as: :status, locals: { is_predecessor: true, direct_reply_id: status.in_reply_to_id}
|
||||||
|
|
||||||
|
.entry{ class: entry_classes }
|
||||||
|
|
||||||
.entry{ class: entry_classes(status, is_predecessor, is_successor, include_threads) }
|
|
||||||
- if status.reblog?
|
- if status.reblog?
|
||||||
.pre-header
|
.pre-header
|
||||||
%div.pre-header__icon
|
%div.pre-header__icon
|
||||||
|
@ -19,4 +29,4 @@
|
||||||
= render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: status.proper }
|
= render partial: centered ? 'stream_entries/detailed_status' : 'stream_entries/simple_status', locals: { status: status.proper }
|
||||||
|
|
||||||
- if include_threads
|
- if include_threads
|
||||||
= render partial: 'stream_entries/status', collection: @descendants, as: :status, locals: { is_successor: true }
|
= render partial: 'stream_entries/status', collection: @descendants, as: :status, locals: { is_successor: true, parent_id: status.id}
|
||||||
|
|
|
@ -23,5 +23,5 @@
|
||||||
- if !user_signed_in? && !Rails.configuration.x.single_user_mode
|
- if !user_signed_in? && !Rails.configuration.x.single_user_mode
|
||||||
= render partial: 'shared/landing_strip', locals: { account: @stream_entry.account }
|
= render partial: 'shared/landing_strip', locals: { account: @stream_entry.account }
|
||||||
|
|
||||||
.activity-stream.activity-stream-headless
|
.activity-stream.activity-stream-headless.h-entry
|
||||||
= render partial: "stream_entries/#{@type}", locals: { @type.to_sym => @stream_entry.activity, include_threads: true }
|
= render partial: "stream_entries/#{@type}", locals: { @type.to_sym => @stream_entry.activity, include_threads: true }
|
||||||
|
|
|
@ -1,5 +1,12 @@
|
||||||
<p>Welcome <%= @resource.email %>!</p>
|
<p>Welcome <%= @resource.email %> !</p>
|
||||||
|
|
||||||
<p>You can confirm your Mastodon account email through the link below:</p>
|
<p>You just created an account on <%= @instance %>.</p>
|
||||||
|
|
||||||
<p><%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %></p>
|
<p>To confirm your inscription, please click on the following link : <br>
|
||||||
|
<%= link_to 'Confirm my account', confirmation_url(@resource, confirmation_token: @token) %>
|
||||||
|
|
||||||
|
<p>Please also check out our <%= link_to 'terms and conditions', terms_url %>.</p>
|
||||||
|
|
||||||
|
<p>Sincerely,<p>
|
||||||
|
|
||||||
|
<p>The <%= @instance %> team</p>
|
|
@ -1,5 +1,12 @@
|
||||||
Welcome <%= @resource.email %>!
|
Welcome <%= @resource.email %> !
|
||||||
|
|
||||||
You can confirm your Mastodon account email through the link below:
|
You just created an account on <%= @instance %>.
|
||||||
|
|
||||||
|
To confirm your inscription, please click on the following link :
|
||||||
<%= confirmation_url(@resource, confirmation_token: @token) %>
|
<%= confirmation_url(@resource, confirmation_token: @token) %>
|
||||||
|
|
||||||
|
Please also check out our terms and conditions <%= terms_url %>
|
||||||
|
|
||||||
|
Sincerely,
|
||||||
|
|
||||||
|
The <%= @instance %> team
|
|
@ -1,5 +1,14 @@
|
||||||
<p>Bienvenue <%= @resource.email %> !</p>
|
<p>Bonjour <%= @resource.email %> !<p>
|
||||||
|
|
||||||
<p>Vous pouvez confirmer le courriel de votre compte Mastodon en cliquant sur le lien ci-dessous :</p>
|
<p>Vous venez de vous créer un compte sur <%= @instance %> et nous vous en remercions :)</p>
|
||||||
|
|
||||||
<p><%= link_to 'Confirmer mon compte', confirmation_url(@resource, confirmation_token: @token) %></p>
|
<p>Pour confirmer votre inscription, merci de cliquer sur le lien suivant : <br>
|
||||||
|
<%= link_to 'Confirmer mon compte', confirmation_url(@resource, confirmation_token: @token) %></p>
|
||||||
|
|
||||||
|
<p>Après votre première connexion, vous pourrez accéder à la documentation de l'outil.</p>
|
||||||
|
|
||||||
|
<p>Pensez également à jeter un œil à nos <%= link_to 'conditions d\'utilisation', terms_url %>.</p>
|
||||||
|
|
||||||
|
<p>Amicalement,</p>
|
||||||
|
|
||||||
|
<p>L'équipe <%= @instance %></p>
|
|
@ -1,5 +1,14 @@
|
||||||
Bienvenue <%= @resource.email %> !
|
Bonjour <%= @resource.email %> !
|
||||||
|
|
||||||
Vous pouvez confirmer le courriel de votre compte Mastodon en cliquant sur le lien ci-dessous :
|
Vous venez de vous créer un compte sur <%= @instance %> et nous vous en remercions.
|
||||||
|
|
||||||
|
Pour confirmer votre inscription, merci de cliquer sur le lien suivant :
|
||||||
<%= confirmation_url(@resource, confirmation_token: @token) %>
|
<%= confirmation_url(@resource, confirmation_token: @token) %>
|
||||||
|
|
||||||
|
Après votre première connexion, vous pourrez accéder à la documentation de l'outil.
|
||||||
|
|
||||||
|
Pour rappel, nos conditions d'utilisation sont indiquées ici <%= terms_url %>
|
||||||
|
|
||||||
|
Amicalement,
|
||||||
|
|
||||||
|
L'équipe <%= @instance %>
|
|
@ -16,6 +16,8 @@ class ImportWorker
|
||||||
process_blocks
|
process_blocks
|
||||||
when 'following'
|
when 'following'
|
||||||
process_follows
|
process_follows
|
||||||
|
when 'muting'
|
||||||
|
process_mutes
|
||||||
end
|
end
|
||||||
|
|
||||||
@import.destroy
|
@import.destroy
|
||||||
|
@ -35,6 +37,18 @@ class ImportWorker
|
||||||
CSV.new(import_contents).reject(&:blank?)
|
CSV.new(import_contents).reject(&:blank?)
|
||||||
end
|
end
|
||||||
|
|
||||||
|
def process_mutes
|
||||||
|
import_rows.each do |row|
|
||||||
|
begin
|
||||||
|
target_account = FollowRemoteAccountService.new.call(row.first)
|
||||||
|
next if target_account.nil?
|
||||||
|
MuteService.new.call(from_account, target_account)
|
||||||
|
rescue Goldfinger::Error, HTTP::Error, OpenSSL::SSL::SSLError
|
||||||
|
next
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
def process_blocks
|
def process_blocks
|
||||||
import_rows.each do |row|
|
import_rows.each do |row|
|
||||||
begin
|
begin
|
||||||
|
|
|
@ -19,7 +19,7 @@ class Pubsubhubbub::DeliveryWorker
|
||||||
|
|
||||||
headers['User-Agent'] = 'Mastodon/PubSubHubbub'
|
headers['User-Agent'] = 'Mastodon/PubSubHubbub'
|
||||||
headers['Link'] = LinkHeader.new([[api_push_url, [%w(rel hub)]], [account_url(subscription.account, format: :atom), [%w(rel self)]]]).to_s
|
headers['Link'] = LinkHeader.new([[api_push_url, [%w(rel hub)]], [account_url(subscription.account, format: :atom), [%w(rel self)]]]).to_s
|
||||||
headers['X-Hub-Signature'] = signature(subscription.secret, payload) unless subscription.secret.blank?
|
headers['X-Hub-Signature'] = signature(subscription.secret, payload) if subscription.secret?
|
||||||
|
|
||||||
response = HTTP.timeout(:per_operation, write: 50, connect: 20, read: 50)
|
response = HTTP.timeout(:per_operation, write: 50, connect: 20, read: 50)
|
||||||
.headers(headers)
|
.headers(headers)
|
||||||
|
|
|
@ -25,7 +25,22 @@ module Mastodon
|
||||||
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
|
||||||
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
|
||||||
|
|
||||||
config.i18n.available_locales = [:en, :de, :es, :pt, :fr, :hu, :uk, 'zh-CN', :fi, :eo, :ru, :ja]
|
config.i18n.available_locales = [
|
||||||
|
:en,
|
||||||
|
:de,
|
||||||
|
:eo,
|
||||||
|
:es,
|
||||||
|
:fi,
|
||||||
|
:fr,
|
||||||
|
:hu,
|
||||||
|
:ja,
|
||||||
|
:no,
|
||||||
|
:pt,
|
||||||
|
:ru,
|
||||||
|
:uk,
|
||||||
|
'zh-CN',
|
||||||
|
:'zh-HK',
|
||||||
|
]
|
||||||
|
|
||||||
config.i18n.default_locale = :en
|
config.i18n.default_locale = :en
|
||||||
|
|
||||||
|
|
|
@ -2,5 +2,5 @@
|
||||||
|
|
||||||
Rails.application.configure do
|
Rails.application.configure do
|
||||||
config.x.email_domains_blacklist = ENV.fetch('EMAIL_DOMAIN_BLACKLIST') { 'mvrht.com' }
|
config.x.email_domains_blacklist = ENV.fetch('EMAIL_DOMAIN_BLACKLIST') { 'mvrht.com' }
|
||||||
config.x.email_domains_whitelist = ENV.fetch('EMAIL_DOMAIN_WHITELIST') { '' }
|
config.x.email_domains_whitelist = ENV.fetch('EMAIL_DOMAIN_WHITELIST') { '' }
|
||||||
end
|
end
|
||||||
|
|
|
@ -17,7 +17,7 @@ en:
|
||||||
unconfirmed: You have to confirm your email address before continuing.
|
unconfirmed: You have to confirm your email address before continuing.
|
||||||
mailer:
|
mailer:
|
||||||
confirmation_instructions:
|
confirmation_instructions:
|
||||||
subject: 'Mastodon: Confirmation instructions'
|
subject: 'Mastodon: Confirmation instructions for %{instance}'
|
||||||
password_change:
|
password_change:
|
||||||
subject: 'Mastodon: Password changed'
|
subject: 'Mastodon: Password changed'
|
||||||
reset_password_instructions:
|
reset_password_instructions:
|
||||||
|
|
|
@ -17,13 +17,13 @@ fr:
|
||||||
unconfirmed: Vous devez valider votre compte pour continuer.
|
unconfirmed: Vous devez valider votre compte pour continuer.
|
||||||
mailer:
|
mailer:
|
||||||
confirmation_instructions:
|
confirmation_instructions:
|
||||||
subject: Instructions de confirmation
|
subject: "Merci de confirmer votre inscription sur %{instance}"
|
||||||
password_change:
|
password_change:
|
||||||
subject: Votre mot de passe a été modifié avec succés.
|
subject: Votre mot de passe a été modifié avec succés.
|
||||||
reset_password_instructions:
|
reset_password_instructions:
|
||||||
subject: Instructions pour modifier le mot de passe
|
subject: Instructions pour changer votre mot de passe
|
||||||
unlock_instructions:
|
unlock_instructions:
|
||||||
subject: Instructions pour déverrouiller le compte
|
subject: Instructions pour déverrouiller votre compte
|
||||||
omniauth_callbacks:
|
omniauth_callbacks:
|
||||||
failure: 'Nous n''avons pas pu vous authentifier via %{kind} : ''%{reason}''.'
|
failure: 'Nous n''avons pas pu vous authentifier via %{kind} : ''%{reason}''.'
|
||||||
success: Authentifié avec succès via %{kind}.
|
success: Authentifié avec succès via %{kind}.
|
||||||
|
|
61
config/locales/devise.zh-HK.yml
Normal file
61
config/locales/devise.zh-HK.yml
Normal file
|
@ -0,0 +1,61 @@
|
||||||
|
---
|
||||||
|
zh-HK:
|
||||||
|
devise:
|
||||||
|
confirmations:
|
||||||
|
confirmed: 你的電郵地址確認成功
|
||||||
|
send_instructions: 你將會在幾分鐘內收到確認指示電郵,上面有確認你電郵地址的指示。
|
||||||
|
send_paranoid_instructions: 如果你的電郵地址已經存在於我們的資料庫,你將會在幾分鐘內收到電郵,確認你電郵地址的指示。
|
||||||
|
failure:
|
||||||
|
already_authenticated: 你之前已經登入了。
|
||||||
|
inactive: 你的用戶並未啟用。
|
||||||
|
invalid: 不正確的 %{authentication_keys} 或密碼。
|
||||||
|
last_attempt: 若你再一次嘗試失敗,我們將鎖定你的用戶,以察安全。
|
||||||
|
locked: 你的用戶已被鎖定
|
||||||
|
not_found_in_database: 不正確的 %{authentication_keys} 或密碼。
|
||||||
|
timeout: 你的登入階段已經過期,請重新登入以繼續使用。
|
||||||
|
unauthenticated: 你必須先登入或登記,以繼續使用。
|
||||||
|
unconfirmed: 你必須先確認電郵地址,繼續使用。
|
||||||
|
mailer:
|
||||||
|
confirmation_instructions:
|
||||||
|
subject: 'Mastodon: 確認電郵地址'
|
||||||
|
password_change:
|
||||||
|
subject: 'Mastodon: 更改密碼'
|
||||||
|
reset_password_instructions:
|
||||||
|
subject: 'Mastodon: 重設密碼'
|
||||||
|
unlock_instructions:
|
||||||
|
subject: 'Mastodon: 解除用戶鎖定'
|
||||||
|
omniauth_callbacks:
|
||||||
|
failure: 無法以 %{kind} 登入你的用戶,原因是︰「%{reason}」。
|
||||||
|
success: 成功以 %{kind} 登入你的用戶。
|
||||||
|
passwords:
|
||||||
|
no_token: 你必須使用重設密碼電郵內的網址進入本頁。如果你確是使用電郵內的網址,請確認你用了完整的網址。
|
||||||
|
send_instructions: 你將在幾分鐘內收到重設密碼的電郵指示。
|
||||||
|
send_paranoid_instructions: 如果你的電郵地址已經存在於我們的資料庫,你將會在幾分鐘內收到重設密碼的電郵指示。
|
||||||
|
updated: 你的密碼已經更新,你現在正登入本站。
|
||||||
|
updated_not_active: 你的密碼已經更新。
|
||||||
|
registrations:
|
||||||
|
destroyed: 再見了!你的用戶已被取消,希望我們相有相見的機會吧。
|
||||||
|
signed_up: 歡迎你!你的登記已經成功。
|
||||||
|
signed_up_but_inactive: 你的登記已經成功,可是由於你的用戶還被被啟用,暫時還不能讓你登入。
|
||||||
|
signed_up_but_locked: 你的登記已經成功,可是由於你的用戶已被鎖定,我們無法讓你登入。
|
||||||
|
signed_up_but_unconfirmed: 一條確認連結已經電郵到你的郵址。請使用讓連結啟用你的用戶。
|
||||||
|
update_needs_confirmation: 你的用戶已經更新,但我們需要確認你的電郵地址。請打開你的郵箱,使用確認電郵的連結來確認的地郵址。
|
||||||
|
updated: 你的用戶已經成功更新。
|
||||||
|
sessions:
|
||||||
|
already_signed_out: 成功登出。
|
||||||
|
signed_in: 成功登入。
|
||||||
|
signed_out: 成功登出。
|
||||||
|
unlocks:
|
||||||
|
send_instructions: 你將在幾分鐘內收到解除用戶鎖定的電郵指示。
|
||||||
|
send_paranoid_instructions: 如果你的電郵地址已經存在於我們的資料庫,你將在幾分鐘內收到解除用戶鎖定的電郵指示。
|
||||||
|
unlocked: 你的用戶已經解鎖,請登入以繼續。
|
||||||
|
errors:
|
||||||
|
messages:
|
||||||
|
already_confirmed: 先前已經確認,請嘗試登入
|
||||||
|
confirmation_period_expired: 需要在 %{period} 之內確認。請重新申請
|
||||||
|
expired: 已經過期,請重新申請
|
||||||
|
not_found: 找不到
|
||||||
|
not_locked: 並未被鎖定
|
||||||
|
not_saved:
|
||||||
|
one: '1 個錯誤令 %{resource} 被法被儲存︰'
|
||||||
|
other: "%{count} 個錯誤令 %{resource} 被法被儲存︰"
|
|
@ -60,15 +60,15 @@ ja:
|
||||||
title: 認証コード
|
title: 認証コード
|
||||||
authorized_applications:
|
authorized_applications:
|
||||||
buttons:
|
buttons:
|
||||||
revoke: 取り消す
|
revoke: 取消
|
||||||
confirmations:
|
confirmations:
|
||||||
revoke: 本当に取り消しますか?
|
revoke: 本当に取り消しますか?
|
||||||
index:
|
index:
|
||||||
application: アプリケーション
|
application: アプリ名
|
||||||
created_at: 認証済み
|
created_at: 許可した日時
|
||||||
date_format: "%Y年%m月%d日 %H時%M分%S秒"
|
date_format: "%Y年%m月%d日 %H時%M分%S秒"
|
||||||
scopes: アクセス権
|
scopes: アクセス権
|
||||||
title: あなたの認証済みアプリケーション
|
title: 認証済みアプリケーション
|
||||||
errors:
|
errors:
|
||||||
messages:
|
messages:
|
||||||
access_denied: リソースの所有者または認証サーバーが要求を拒否しました。
|
access_denied: リソースの所有者または認証サーバーが要求を拒否しました。
|
||||||
|
@ -83,7 +83,7 @@ ja:
|
||||||
expired: アクセストークンの有効期限が切れています
|
expired: アクセストークンの有効期限が切れています
|
||||||
revoked: アクセストークンは取り消されています。
|
revoked: アクセストークンは取り消されています。
|
||||||
unknown: アクセストークンが無効です。
|
unknown: アクセストークンが無効です。
|
||||||
resource_owner_authenticator_not_configured: Doorkeeper.configure.resource_owner_authenticatorが設定されていないため、リソース所有者の検索に失敗しました。
|
resource_owner_authenticator_not_configured: Doorkeeper.configure.resource_owner_authenticator が設定されていないため、リソース所有者の検索に失敗しました。
|
||||||
server_error: 認証サーバーに予期せぬ例外が発生したため、リクエストを実行できなくなりました。
|
server_error: 認証サーバーに予期せぬ例外が発生したため、リクエストを実行できなくなりました。
|
||||||
temporarily_unavailable: 現在、認証サーバーに一時的な過負荷が掛かっているか、またはメンテナンス中のため、リクエストを処理できません。
|
temporarily_unavailable: 現在、認証サーバーに一時的な過負荷が掛かっているか、またはメンテナンス中のため、リクエストを処理できません。
|
||||||
unauthorized_client: クライアントはこのメゾットで要求を実行する権限がありません。
|
unauthorized_client: クライアントはこのメゾットで要求を実行する権限がありません。
|
||||||
|
|
113
config/locales/doorkeeper.zh-HK.yml
Normal file
113
config/locales/doorkeeper.zh-HK.yml
Normal file
|
@ -0,0 +1,113 @@
|
||||||
|
---
|
||||||
|
zh-HK:
|
||||||
|
activerecord:
|
||||||
|
attributes:
|
||||||
|
doorkeeper/application:
|
||||||
|
name: 名稱
|
||||||
|
redirect_uri: 轉接 URI
|
||||||
|
errors:
|
||||||
|
models:
|
||||||
|
doorkeeper/application:
|
||||||
|
attributes:
|
||||||
|
redirect_uri:
|
||||||
|
fragment_present: 'URI 不可包含 "#fragment" 部份'
|
||||||
|
invalid_uri: 必需有正確的 URI.
|
||||||
|
relative_uri: 必需為絕對 URI.
|
||||||
|
secured_uri: 必需使用有 HTTPS/SSL 加密的 URI.
|
||||||
|
doorkeeper:
|
||||||
|
applications:
|
||||||
|
buttons:
|
||||||
|
authorize: 認證
|
||||||
|
cancel: 取消
|
||||||
|
destroy: 移除
|
||||||
|
edit: 編輯
|
||||||
|
submit: 提交
|
||||||
|
confirmations:
|
||||||
|
destroy: 是否確定?
|
||||||
|
edit:
|
||||||
|
title: 編輯應用程式
|
||||||
|
form:
|
||||||
|
error: 噢!請檢查你表格的錯誤訊息
|
||||||
|
help:
|
||||||
|
native_redirect_uri: 使用 %{native_redirect_uri} 作局部測試
|
||||||
|
redirect_uri: 每行輸入一個 URI
|
||||||
|
scopes: 請用半形空格分開權限範圍 (scope)。留空表示使用預設的權限範圍
|
||||||
|
index:
|
||||||
|
callback_url: 回傳網址
|
||||||
|
name: 名稱
|
||||||
|
new: 新增應用程式
|
||||||
|
title: 你的應用程式
|
||||||
|
new:
|
||||||
|
title: 新增應用程式
|
||||||
|
show:
|
||||||
|
actions: 操作
|
||||||
|
application_id: 應用程式 ID
|
||||||
|
callback_urls: 回傳網址
|
||||||
|
scopes: 權限範圍
|
||||||
|
secret: 密碼
|
||||||
|
title: '應用程式︰ %{name}'
|
||||||
|
authorizations:
|
||||||
|
buttons:
|
||||||
|
authorize: 批准
|
||||||
|
deny: 拒絕
|
||||||
|
error:
|
||||||
|
title: 發生錯誤
|
||||||
|
new:
|
||||||
|
able_to: 要求獲取權限
|
||||||
|
prompt: 應用程式 %{client_name} 要求得到你用戶的部份權限
|
||||||
|
title: 需要用戶授權
|
||||||
|
show:
|
||||||
|
title: 授權代碼
|
||||||
|
authorized_applications:
|
||||||
|
buttons:
|
||||||
|
revoke: 取消授權
|
||||||
|
confirmations:
|
||||||
|
revoke: 是否確定要取消授權?
|
||||||
|
index:
|
||||||
|
application: 應用程式
|
||||||
|
created_at: 授權於
|
||||||
|
date_format: "%Y-%m-%d %H:%M:%S"
|
||||||
|
scopes: 權限範圍
|
||||||
|
title: 已獲你授權的程用程式
|
||||||
|
errors:
|
||||||
|
messages:
|
||||||
|
access_denied: 資源擁有者或授權伺服器不接受請求。
|
||||||
|
credential_flow_not_configured: 資源擁有者密碼認證程序 (Resource Owner Password Credentials flow) 失敗,原因是 Doorkeeper.configure.resource_owner_from_credentials 沒有設定。
|
||||||
|
invalid_client: 用戶程式認證 (Client authentication) 失敗,原因是用戶程式未有登記、沒有指定用戶程式 (client)、或者使用了不支援的認證方法 (method)。
|
||||||
|
invalid_grant: 授權申請 (authorization grant) 不正確、過期、已被取消,或者無法對應授權請求 (authorization request) 內的轉接 URI,或者屬於別的用戶程式。
|
||||||
|
invalid_redirect_uri: 不正確的轉接網址。
|
||||||
|
invalid_request: 請求缺少了必要的參數、包含了不支援的參數、或者其他輸入錯誤。
|
||||||
|
invalid_resource_owner: 資源擁有者的登入資訊錯誤、或者無法找到該資源擁有者。
|
||||||
|
invalid_scope: 請求的權限範圍 (scope) 不正確、未有定義、或者輸入錯誤。
|
||||||
|
invalid_token:
|
||||||
|
expired: access token 已經過期
|
||||||
|
revoked: access token 已被取消
|
||||||
|
unknown: access token 不正確
|
||||||
|
resource_owner_authenticator_not_configured: 無法找到資源擁有者,原因是 Doorkeeper.configure.resource_owner_authenticator 沒有設定。
|
||||||
|
server_error: 認證伺服器遇上未知狀況,令請求無法通過。
|
||||||
|
temporarily_unavailable: 認證伺服器由於臨時負荷過重或者維護,目前未能處理請求。
|
||||||
|
unauthorized_client: 用戶程式無權用此方法 (method) 請行這個請求。
|
||||||
|
unsupported_grant_type: 授權伺服器不支援這個授權類型 (grant type)。
|
||||||
|
unsupported_response_type: 授權伺服器不支援這個回應類型 (response type).
|
||||||
|
flash:
|
||||||
|
applications:
|
||||||
|
create:
|
||||||
|
notice: 已新增應用程式。
|
||||||
|
destroy:
|
||||||
|
notice: 已刪除應用程式。
|
||||||
|
update:
|
||||||
|
notice: 已更新應用程式。
|
||||||
|
authorized_applications:
|
||||||
|
destroy:
|
||||||
|
notice: 已取消應用程式授權。
|
||||||
|
layouts:
|
||||||
|
admin:
|
||||||
|
nav:
|
||||||
|
applications: 應用程式
|
||||||
|
oauth2_provider: OAuth2 供應者
|
||||||
|
application:
|
||||||
|
title: 需要 OAuth 授權
|
||||||
|
scopes:
|
||||||
|
follow: 關注、封鎖、解除封鎖及取消關注用戶
|
||||||
|
read: 閱讀你的用戶資料
|
||||||
|
write: 以你的名義發佈文章
|
|
@ -76,6 +76,7 @@ en:
|
||||||
x_seconds: "%{count}s"
|
x_seconds: "%{count}s"
|
||||||
exports:
|
exports:
|
||||||
blocks: You block
|
blocks: You block
|
||||||
|
mutes: You mute
|
||||||
csv: CSV
|
csv: CSV
|
||||||
follows: You follow
|
follows: You follow
|
||||||
storage: Media storage
|
storage: Media storage
|
||||||
|
@ -92,6 +93,7 @@ en:
|
||||||
types:
|
types:
|
||||||
blocking: Blocking list
|
blocking: Blocking list
|
||||||
following: Following list
|
following: Following list
|
||||||
|
muting: Muting list
|
||||||
upload: Upload
|
upload: Upload
|
||||||
landing_strip_html: <strong>%{name}</strong> is a user on <strong>%{domain}</strong>. You can follow them or interact with them if you have an account anywhere in the fediverse. If you don't, you can <a href="%{sign_up_path}">sign up here</a>.
|
landing_strip_html: <strong>%{name}</strong> is a user on <strong>%{domain}</strong>. You can follow them or interact with them if you have an account anywhere in the fediverse. If you don't, you can <a href="%{sign_up_path}">sign up here</a>.
|
||||||
media_attachments:
|
media_attachments:
|
||||||
|
@ -170,3 +172,54 @@ en:
|
||||||
users:
|
users:
|
||||||
invalid_email: The e-mail address is invalid
|
invalid_email: The e-mail address is invalid
|
||||||
invalid_otp_token: Invalid two-factor code
|
invalid_otp_token: Invalid two-factor code
|
||||||
|
will_paginate:
|
||||||
|
page_gap: "…"
|
||||||
|
errors:
|
||||||
|
404: The page you were looking for doesn't exist.
|
||||||
|
410: The page you were looking for doesn't exist anymore.
|
||||||
|
422:
|
||||||
|
title: Security verification failed
|
||||||
|
content: Security verification failed. Are you blocking cookies?
|
||||||
|
reports:
|
||||||
|
reports: Reports
|
||||||
|
status: Status
|
||||||
|
unresolved: Unresolved
|
||||||
|
resolved: Resolved
|
||||||
|
id: ID
|
||||||
|
target: Target
|
||||||
|
reported_by: Reported by
|
||||||
|
comment:
|
||||||
|
label: Comment
|
||||||
|
none: None
|
||||||
|
view: View
|
||||||
|
report: "Report #%{id}"
|
||||||
|
delete: Delete
|
||||||
|
reported_account: Reported account
|
||||||
|
reported_by: Signalé par
|
||||||
|
silence_account: Silence account
|
||||||
|
suspend_account: Suspend account
|
||||||
|
mark_as_resolved: Mark as resolved
|
||||||
|
admin:
|
||||||
|
settings:
|
||||||
|
title: Site Settings
|
||||||
|
setting: Setting
|
||||||
|
click_to_edit: Click to edit
|
||||||
|
contact_information:
|
||||||
|
label: Contact information
|
||||||
|
username: Enter a username
|
||||||
|
email: Enter a public e-mail address
|
||||||
|
site_title: Site title
|
||||||
|
site_description:
|
||||||
|
title: Site description
|
||||||
|
desc_html: "Displayed as a paragraph on the frontpage and used as a meta tag.<br>You can use HTML tags, in particular <code><a></code> and <code><em></code>."
|
||||||
|
site_description_extended:
|
||||||
|
title: Extended site description
|
||||||
|
desc_html: "Displayed on extended information page<br>You can use HTML tags"
|
||||||
|
registrations:
|
||||||
|
open:
|
||||||
|
title: Open registration
|
||||||
|
enabled: Enabled
|
||||||
|
disabled: Disabled
|
||||||
|
closed_message:
|
||||||
|
title: Closed registration message
|
||||||
|
desc_html: "Displayed on frontpage when registrations are closed<br>You can use HTML tags"
|
||||||
|
|
|
@ -2,52 +2,166 @@
|
||||||
es:
|
es:
|
||||||
about:
|
about:
|
||||||
about_mastodon: Mastodon es un servidor de red social <em>libre y de código abierto</em>. Una alternativa <em>descentralizada</em> a plataformas comerciales, que evita el riesgo de que una única compañía monopolice tu comunicación. Cualquiera puede ejecutar Mastodon y participar sin problemas en la <em>red social</em>.
|
about_mastodon: Mastodon es un servidor de red social <em>libre y de código abierto</em>. Una alternativa <em>descentralizada</em> a plataformas comerciales, que evita el riesgo de que una única compañía monopolice tu comunicación. Cualquiera puede ejecutar Mastodon y participar sin problemas en la <em>red social</em>.
|
||||||
|
about_this: Acerca de esta instancia
|
||||||
|
apps: Apps
|
||||||
|
business_email: 'Correo de negocios:'
|
||||||
|
closed_registrations: Los registros están actualmente cerrados en esta instancia.
|
||||||
|
contact: Contacto
|
||||||
|
description_headline: ¿Qué es %{domain}?
|
||||||
|
domain_count_after: otras instancias
|
||||||
|
domain_count_before: Conectado a
|
||||||
|
features:
|
||||||
|
api: API pública para aplicaciones y servicios
|
||||||
|
blocks: Moderación de contenido
|
||||||
|
characters: 500 caracteres por publicación
|
||||||
|
chronology: Las historias son cronológicas
|
||||||
|
ethics: 'Diseño etico: sin anuncios, sin rastreos'
|
||||||
|
gifv: Videos cortos y GIFV
|
||||||
|
privacy: Configuraciones de privacidad ajustables
|
||||||
|
public: Historia federada
|
||||||
|
features_headline: Lo que distingue a Mastodon
|
||||||
get_started: Comenzar
|
get_started: Comenzar
|
||||||
|
links: Enlaces
|
||||||
|
other_instances: Otras instancias
|
||||||
source_code: Código fuente
|
source_code: Código fuente
|
||||||
terms: Términos de uso
|
status_count_after: estados
|
||||||
|
status_count_before: Que han escrito
|
||||||
|
terms: Términos
|
||||||
|
user_count_after: usuarios registrados
|
||||||
|
user_count_before: Tenemos
|
||||||
accounts:
|
accounts:
|
||||||
follow: Seguir
|
follow: Seguir
|
||||||
followers: Seguidores
|
followers: Seguidores
|
||||||
following: Siguiendo
|
following: Siguiendo
|
||||||
nothing_here: "¡No hay nada aquí!"
|
nothing_here: ¡No hay nada aquí!
|
||||||
people_followed_by: Usuarios a quien %{name} sigue
|
people_followed_by: Usuarios a quien %{name} sigue
|
||||||
people_who_follow: Usuarios que siguen a %{name}
|
people_who_follow: Usuarios que siguen a %{name}
|
||||||
posts: Publicaciones
|
posts: Toots
|
||||||
|
remote_follow: Seguir
|
||||||
unfollow: Dejar de seguir
|
unfollow: Dejar de seguir
|
||||||
application_mailer:
|
application_mailer:
|
||||||
|
settings: 'Cambiar preferencias de correo: %{link}'
|
||||||
signature: Notificaciones de Mastodon desde %{instance}
|
signature: Notificaciones de Mastodon desde %{instance}
|
||||||
|
view: 'Vista:'
|
||||||
|
applications:
|
||||||
|
invalid_url: La URL proporcionada es incorrecta
|
||||||
auth:
|
auth:
|
||||||
change_password: Cambiar contraseña
|
change_password: Cambiar contraseña
|
||||||
didnt_get_confirmation: "¿No recibió instrucciones de confirmación?"
|
didnt_get_confirmation: ¿No recibió el correo de confirmación?
|
||||||
forgot_password: "¿Olvidó su contraseña?"
|
forgot_password: ¿Olvidaste tu contraseña?
|
||||||
login: Iniciar sesión
|
login: Iniciar sesión
|
||||||
|
logout: Cerrar sesión
|
||||||
register: Registrarse
|
register: Registrarse
|
||||||
resend_confirmation: Volver a enviar las instrucciones de confirmación
|
resend_confirmation: Volver a enviar el correo de confirmación
|
||||||
reset_password: Restablecer contraseña
|
reset_password: Restablecer contraseña
|
||||||
set_new_password: Establecer nueva contraseña
|
set_new_password: Establecer nueva contraseña
|
||||||
|
authorize_follow:
|
||||||
|
error: Desafortunadamente, ha ocurrido un error buscando la cuenta remota
|
||||||
|
follow: Seguir
|
||||||
|
prompt_html: 'Tú (<strong>%{self}</strong>) has solicitado seguir:'
|
||||||
|
title: Seguir %{acct}
|
||||||
|
datetime:
|
||||||
|
distance_in_words:
|
||||||
|
about_x_hours: "%{count}h"
|
||||||
|
about_x_months: "%{count}m"
|
||||||
|
about_x_years: "%{count}y"
|
||||||
|
almost_x_years: "%{count}y"
|
||||||
|
half_a_minute: Justo ahora
|
||||||
|
less_than_x_minutes: "%{count}m"
|
||||||
|
less_than_x_seconds: Justo ahora
|
||||||
|
over_x_years: "%{count}y"
|
||||||
|
x_days: "%{count}d"
|
||||||
|
x_minutes: "%{count}m"
|
||||||
|
x_months: "%{count}m"
|
||||||
|
x_seconds: "%{count}s"
|
||||||
|
exports:
|
||||||
|
blocks: Personas que has bloqueado
|
||||||
|
csv: CSV
|
||||||
|
follows: Personas que sigues
|
||||||
|
storage: Almacenamiento
|
||||||
generic:
|
generic:
|
||||||
changes_saved_msg: "¡Cambios guardados con éxito!"
|
changes_saved_msg: ¡Cambios guardados con éxito!
|
||||||
powered_by: powered by %{link}
|
powered_by: powered by %{link}
|
||||||
save_changes: Guardar cambios
|
save_changes: Guardar cambios
|
||||||
validation_errors:
|
validation_errors:
|
||||||
one: "¡Algo no está todavía bien! Por favor, revise el error más abajo"
|
one: ¡Algo no está bien! Por favor, revisa el error
|
||||||
other: "¡Algo no está todavía bien! Por favor, revise %{count} errores más abajo"
|
other: ¡Algo no está bien! Por favor, revise %{count} errores más abajo
|
||||||
|
imports:
|
||||||
|
preface: Puedes importar ciertos datos, como todas las personas que estás siguiendo o bloqueando en tu cuenta en esta instancia, desde archivos exportados de otra instancia.
|
||||||
|
success: Sus datos se han cargado correctamente y serán procesados en brevedad
|
||||||
|
types:
|
||||||
|
blocking: Lista de bloqueados
|
||||||
|
following: Lista de seguidos
|
||||||
|
upload: Cargar
|
||||||
|
landing_strip_html: <strong>%{name}</strong> es un usuario en <strong>%{domain}</strong>. Puedes seguirlo(a) o interactuar con el o ella si tienes una cuenta en cualquier parte del fediverse. Si no tienes una, puedes <a href="%{sign_up_path}">registrar aquí</a>.
|
||||||
notification_mailer:
|
notification_mailer:
|
||||||
|
digest:
|
||||||
|
body: 'Un resumen de lo que te perdiste en %{instance} desde tu última visita el %{since}:'
|
||||||
|
mention: "%{name} te ha mencionado en:"
|
||||||
|
new_followers_summary:
|
||||||
|
one: ¡Hurra!. Alguien más te ha comenzado a seguir
|
||||||
|
other: ¡Genial!. Te han seguido %{count} nuevas personas
|
||||||
|
subject:
|
||||||
|
one: "1 nueva notificación desde tu última visita \U0001F418"
|
||||||
|
other: "%{count} nuevas notificaciones desde tu última visita \U0001F418"
|
||||||
favourite:
|
favourite:
|
||||||
body: 'Su estado fue marcado como favorito por %{name}:'
|
body: 'Tu estado fue marcado como favorito por %{name}:'
|
||||||
subject: "%{name} marcó como favorito su estado"
|
subject: "%{name} marcó como favorito tu estado"
|
||||||
follow:
|
follow:
|
||||||
body: "¡%{name} le está ahora siguiendo!"
|
body: "¡%{name} te está siguiendo!"
|
||||||
subject: "%{name} le esta ahora siguiendo"
|
subject: "%{name} te está siguiendo"
|
||||||
|
follow_request:
|
||||||
|
body: "%{name} ha solicitado seguirte"
|
||||||
|
subject: 'Seguidor pendiente: %{name}'
|
||||||
mention:
|
mention:
|
||||||
body: 'Fue mencionado por %{name} en:'
|
body: 'Fuiste mencionado por %{name} en:'
|
||||||
subject: Fue mencionado por %{name}
|
subject: Fuiste mencionado por %{name}
|
||||||
reblog:
|
reblog:
|
||||||
body: 'Su estado fue vuelto a publicar por %{name}:'
|
body: '%{name} ha retooteado tu estado'
|
||||||
subject: "%{name} volvió a publicar su estado"
|
subject: "%{name} ha retooteado tu estado"
|
||||||
pagination:
|
pagination:
|
||||||
next: Próximo
|
next: Próximo
|
||||||
prev: Anterior
|
prev: Anterior
|
||||||
|
remote_follow:
|
||||||
|
acct: Ingesa el usuario@dominio de la persona que quieres seguir
|
||||||
|
missing_resource: No se pudo encontrar la URL de redirección necesaria para su cuenta.
|
||||||
|
proceed: Proceder a seguir
|
||||||
|
prompt: 'Vas a seguir a:'
|
||||||
settings:
|
settings:
|
||||||
|
authorized_apps: Aplicaciones autorizadas
|
||||||
|
back: Volver al inicio
|
||||||
edit_profile: Editar perfil
|
edit_profile: Editar perfil
|
||||||
|
export: Exportar información
|
||||||
|
import: Importar
|
||||||
preferences: Preferencias
|
preferences: Preferencias
|
||||||
|
settings: Ajustes
|
||||||
|
two_factor_auth: Aute/ción. de dos factores
|
||||||
|
statuses:
|
||||||
|
open_in_web: Abrir en web
|
||||||
|
over_character_limit: Límite de caracteres de %{max} superado
|
||||||
|
show_more: Mostrar más
|
||||||
|
visibilities:
|
||||||
|
private: Sólo mostrar a seguidores
|
||||||
|
public: Público
|
||||||
|
unlisted: Público, pero no mostrar en la historia federada
|
||||||
|
stream_entries:
|
||||||
|
click_to_show: Click para mostrar
|
||||||
|
reblogged: retooteado
|
||||||
|
sensitive_content: Contenido sensible
|
||||||
|
time:
|
||||||
|
formats:
|
||||||
|
default: "%b %d, %Y, %H:%M"
|
||||||
|
two_factor_auth:
|
||||||
|
description_html: Sí habilitas la <strong>autenticación de dos factores</strong>, se requerirá estar en posesión de su teléfono, lo que generará tokens para que usted pueda iniciar sesión.
|
||||||
|
disable: Deshabilitar
|
||||||
|
enable: Habilitar
|
||||||
|
instructions_html: "<strong>Escanea este código QR desde Google Authenticator o una aplicación similar en su teléfono</strong>. Desde ahora, esta aplicación va a generar tokens que tienes que ingresar cuando quieras iniciar sesión."
|
||||||
|
plaintext_secret_html: 'Código en texto plano: <samp>%{secret}</samp>'
|
||||||
|
warning: Sí no puedes configurar una aplicación de autenticación ahora, deberás deshabilitar la autenticación de dos factores o no podrás iniciar sesión.
|
||||||
|
users:
|
||||||
|
invalid_email: La dirección de correo es incorrecta
|
||||||
|
invalid_otp_token: Código de dos factores incorrecto
|
||||||
|
media_attachments:
|
||||||
|
validations:
|
||||||
|
too_many: No se pueden adjuntar más de 4 archivos
|
||||||
|
images_and_video: No se puede adjuntar un video a un estado que ya contenga imágenes
|
||||||
|
|
|
@ -40,7 +40,7 @@ fr:
|
||||||
remote_follow: Suivre à distance
|
remote_follow: Suivre à distance
|
||||||
unfollow: Ne plus suivre
|
unfollow: Ne plus suivre
|
||||||
application_mailer:
|
application_mailer:
|
||||||
settings: 'Changer les préférences courriel : ${link}'
|
settings: 'Changer les préférences courriel : %{link}'
|
||||||
signature: Notifications de Mastodon depuis %{instance}
|
signature: Notifications de Mastodon depuis %{instance}
|
||||||
view: 'Voir :'
|
view: 'Voir :'
|
||||||
applications:
|
applications:
|
||||||
|
@ -167,3 +167,54 @@ fr:
|
||||||
users:
|
users:
|
||||||
invalid_email: L'adresse courriel est invalide
|
invalid_email: L'adresse courriel est invalide
|
||||||
invalid_otp_token: Le code d'authentification à deux facteurs est invalide
|
invalid_otp_token: Le code d'authentification à deux facteurs est invalide
|
||||||
|
will_paginate:
|
||||||
|
page_gap: "…"
|
||||||
|
errors:
|
||||||
|
404: La page que vous recherchez n'existe pas.
|
||||||
|
410: La page que vous recherchez n'existe plus.
|
||||||
|
422:
|
||||||
|
title: Vérification de sécurité échouée
|
||||||
|
content: Vérification de sécurité échouée. Bloquez-vous les cookies ?
|
||||||
|
reports:
|
||||||
|
reports: Signalements
|
||||||
|
status: Statut
|
||||||
|
unresolved: Non résolus
|
||||||
|
resolved: Résolus
|
||||||
|
id: ID
|
||||||
|
target: Cible
|
||||||
|
reported_by: Signalé par
|
||||||
|
comment:
|
||||||
|
label: Commentaire
|
||||||
|
none: Aucun
|
||||||
|
view: Voir
|
||||||
|
report: "Signalement #%{id}"
|
||||||
|
delete: Supprimer
|
||||||
|
reported_account: Compte signalé
|
||||||
|
reported_by: Signalé par
|
||||||
|
silence_account: Rendre le compte muet
|
||||||
|
suspend_account: Suspendre le compte
|
||||||
|
mark_as_resolved: Marqué comme résolu
|
||||||
|
admin:
|
||||||
|
settings:
|
||||||
|
title: Paramètres du site
|
||||||
|
setting: Paramètre
|
||||||
|
click_to_edit: Cliquez pour éditer
|
||||||
|
contact_information:
|
||||||
|
label: Informations de contact
|
||||||
|
username: Entrez un nom d'utilisateur
|
||||||
|
email: Entrez une adresse courriel publique
|
||||||
|
site_title: Titre du site
|
||||||
|
site_description:
|
||||||
|
title: Description du site
|
||||||
|
desc_html: "Affichée sous la forme d'un paragraphe sur la page d'accueil et utilisée comme balise meta.<br>Vous pouvez utiliser des balises HTML, en particulier <code><a></code> et <code><em></code>."
|
||||||
|
site_description_extended:
|
||||||
|
title: Description étendue du site
|
||||||
|
desc_html: "Affichée sur la page d'informations complémentaires du site<br>Vous pouvez utiliser des balises HTML"
|
||||||
|
registrations:
|
||||||
|
open:
|
||||||
|
title: Inscriptions
|
||||||
|
enabled: Activées
|
||||||
|
disabled: Désactivées
|
||||||
|
closed_message:
|
||||||
|
title: Message de fermeture des inscriptions
|
||||||
|
desc_html: "Affiché sur la page d'accueil lorsque les inscriptions sont fermées<br>Vous pouvez utiliser des balises HTML"
|
||||||
|
|
|
@ -1,32 +1,32 @@
|
||||||
---
|
---
|
||||||
ja:
|
ja:
|
||||||
about:
|
about:
|
||||||
about_mastodon: Mastodon は<em>無料でオープンソース</em>のソーシャルネットワークです。 従来のプラットフォームとは違う<em>分散型</em>で、これはあなたの会話が一つの会社によって独占されるのを防ぎます。自分の信頼できるサーバーを選びます— どのサーバーを選んでも、誰とでも会話することができます。 だれでも自分の Mastodon サーバーを作ることができ、<em>シームレスにソーシャルネットワークに参加</em>できます。
|
about_mastodon: Mastodon は<em>自由でオープンソース</em>なソーシャルネットワークです。商用プラットフォームの代替となる<em>分散型</em>を採用し、あなたのやりとりが一つの会社によって独占されるのを防ぎます。信頼できるインスタンスを選択してください — どのインスタンスを選んでも、誰とでもやりとりすることができます。 だれでも自分の Mastodon インスタンスを作ることができ、シームレスに<em>ソーシャルネットワーク</em>に参加できます。
|
||||||
about_this: このサーバーについて
|
about_this: このインスタンスについて
|
||||||
apps: アプリ
|
apps: アプリ
|
||||||
business_email: 'ビジネスメールアドレス:'
|
business_email: 'ビジネスメールアドレス:'
|
||||||
closed_registrations: 現在このサーバーでの新規登録は受け付けていません。
|
closed_registrations: 現在このインスタンスでの新規登録は受け付けていません。
|
||||||
contact: 連絡先
|
contact: 連絡先
|
||||||
description_headline: '%{domain}とは?'
|
description_headline: '%{domain} とは?'
|
||||||
domain_count_after: 個のサーバー
|
domain_count_after: 個のインスタンス
|
||||||
domain_count_before: 接続中
|
domain_count_before: 接続中
|
||||||
features:
|
features:
|
||||||
api: アプリやその他サービスにAPIを公開
|
api: アプリやその他サービスにAPIを公開
|
||||||
blocks: ブロックやミュートの種類は豊富
|
blocks: 豊富なブロックやミュート機能
|
||||||
characters: 1投稿は500文字まで可能
|
characters: 1つの投稿は500文字まで利用可能
|
||||||
chronology: 時系列に沿ったタイムライン
|
chronology: 時系列順のタイムライン
|
||||||
ethics: 広告も行動追跡もなく、プライバシーにも配慮
|
ethics: 広告もトラッキングもありません
|
||||||
gifv: GIFVや短い動画にも対応
|
gifv: GIFVや短い動画にも対応
|
||||||
privacy: 細かく投稿ごとに公開範囲が設定可能
|
privacy: 投稿ごとに公開範囲を細かく設定可能
|
||||||
public: 公開タイムライン
|
public: 公開タイムライン
|
||||||
features_headline: Mastodonの特徴
|
features_headline: Mastodon の特徴
|
||||||
get_started: 始める
|
get_started: 参加する
|
||||||
links: リンク
|
links: リンク
|
||||||
other_instances: 他のサーバー
|
other_instances: 他のインスタンス
|
||||||
source_code: ソースコード
|
source_code: ソースコード
|
||||||
status_count_after: トゥート
|
status_count_after: トゥート
|
||||||
status_count_before: トゥート数
|
status_count_before: トゥート数
|
||||||
terms: 規約
|
terms: プライバシーポリシー
|
||||||
user_count_after: 人
|
user_count_after: 人
|
||||||
user_count_before: ユーザー数
|
user_count_before: ユーザー数
|
||||||
accounts:
|
accounts:
|
||||||
|
@ -34,14 +34,14 @@ ja:
|
||||||
followers: フォロワー
|
followers: フォロワー
|
||||||
following: フォロー中
|
following: フォロー中
|
||||||
nothing_here: 何もありません
|
nothing_here: 何もありません
|
||||||
people_followed_by: '%{name}さんをフォロー中のアカウント'
|
people_followed_by: '%{name} さんをフォロー中のアカウント'
|
||||||
people_who_follow: '%{name}さんがフォロー中のアカウント'
|
people_who_follow: '%{name} さんがフォロー中のアカウント'
|
||||||
posts: 投稿
|
posts: 投稿
|
||||||
remote_follow: リモートフォロー
|
remote_follow: リモートフォロー
|
||||||
unfollow: フォロー解除
|
unfollow: フォロー解除
|
||||||
application_mailer:
|
application_mailer:
|
||||||
settings: 'メール設定の変更: %{link}'
|
settings: 'メール設定の変更: %{link}'
|
||||||
signature: 'Mastodon %{instance}サーバーからの通知'
|
signature: 'Mastodon %{instance} インスタンスからの通知'
|
||||||
view: 'View:'
|
view: 'View:'
|
||||||
applications:
|
applications:
|
||||||
invalid_url: URLが無効です
|
invalid_url: URLが無効です
|
||||||
|
@ -51,15 +51,15 @@ ja:
|
||||||
forgot_password: パスワードをお忘れですか?
|
forgot_password: パスワードをお忘れですか?
|
||||||
login: ログイン
|
login: ログイン
|
||||||
logout: ログアウト
|
logout: ログアウト
|
||||||
register: サインアップ
|
register: 登録する
|
||||||
resend_confirmation: 確認メールを再送する
|
resend_confirmation: 確認メールを再送する
|
||||||
reset_password: パスワード再発行
|
reset_password: パスワードを再発行
|
||||||
set_new_password: 新しいパスワード
|
set_new_password: 新しいパスワード
|
||||||
authorize_follow:
|
authorize_follow:
|
||||||
error: 残念ながら、リモートアカウントにエラーが発生しました。
|
error: 残念ながら、リモートアカウントにエラーが発生しました。
|
||||||
follow: フォロー
|
follow: フォロー
|
||||||
prompt_html: 'あなた (<strong>%{self}</strong>) は以下のアカウントのフォローをリクエストしました:'
|
prompt_html: 'あなた(<strong>%{self}</strong>)は以下のアカウントのフォローをリクエストしました:'
|
||||||
title: '%{acct}をフォロー'
|
title: '%{acct} をフォロー'
|
||||||
datetime:
|
datetime:
|
||||||
distance_in_words:
|
distance_in_words:
|
||||||
about_x_hours: "%{count}時間"
|
about_x_hours: "%{count}時間"
|
||||||
|
@ -87,42 +87,42 @@ ja:
|
||||||
one: エラーが発生しました。以下のエラーを確認してください
|
one: エラーが発生しました。以下のエラーを確認してください
|
||||||
other: エラーが発生しました。以下の%{count}個のエラーを確認してください
|
other: エラーが発生しました。以下の%{count}個のエラーを確認してください
|
||||||
imports:
|
imports:
|
||||||
preface: このサーバーのあなたのアカウントにフォロー、ブロック、などの他のサーバーからエクスポートされたファイルの情報をインポートできます。
|
preface: 他のインスタンスでエクスポートされたファイルから、フォロー/ブロックした情報をこのインスタンス上のアカウントにインポートできます。
|
||||||
success: ファイルは正常にアップロードされ、現在処理中です。しばらくしてから確認してください
|
success: ファイルは正常にアップロードされ、現在処理中です。しばらくしてから確認してください
|
||||||
types:
|
types:
|
||||||
blocking: ブロック中のアカウントリスト
|
blocking: ブロック中のアカウントリスト
|
||||||
following: フォロー中のアカウントリスト
|
following: フォロー中のアカウントリスト
|
||||||
upload: アップロード
|
upload: アップロード
|
||||||
landing_strip_html: <strong>%{name}</strong>さんはサーバー<strong>%{domain}</strong>のユーザーです。アカウントさえ持っていればフォローしたり会話したりできます。もしお持ちでないなら<a href="%{sign_up_path}">こちら</a>からサインアップできます。
|
landing_strip_html: <strong>%{name}</strong> さんはインスタンス <strong>%{domain}</strong> のユーザーです。アカウントさえ持っていればフォローしたり会話したりできます。もしお持ちでないなら <a href="%{sign_up_path}">こちら</a> からサインアップできます。
|
||||||
media_attachments:
|
media_attachments:
|
||||||
validations:
|
validations:
|
||||||
images_and_video: 既に画像が追加されている場合動画を追加することはできません。
|
images_and_video: 既に画像が追加されているため、動画を追加することはできません。
|
||||||
too_many: 追加できるファイルは4つまでです。
|
too_many: 追加できるファイルは4つまでです。
|
||||||
notification_mailer:
|
notification_mailer:
|
||||||
digest:
|
digest:
|
||||||
body: '%{instance}での最後のログインからの出来事:'
|
body: '%{instance} での最後のログインからの出来事:'
|
||||||
mention: "%{name}さんがあなたに返信しました:"
|
mention: "%{name} さんがあなたに返信しました:"
|
||||||
new_followers_summary:
|
new_followers_summary:
|
||||||
one: 新たなフォロワーを獲得しました!
|
one: 新たなフォロワーを獲得しました!
|
||||||
other: '%{count}人の新たなフォロワーを獲得しました!'
|
other: '%{count} 人の新たなフォロワーを獲得しました!'
|
||||||
subject:
|
subject:
|
||||||
one: "新しい1つの通知 \U0001F418"
|
one: "新しい1件の通知 \U0001F418"
|
||||||
other: "新しい%{count}つの通知 \U0001F418"
|
other: "新しい%{count}件の通知 \U0001F418"
|
||||||
favourite:
|
favourite:
|
||||||
body: 'あなたのステータスが%{name}さんにいいねされました:'
|
body: 'あなたのトゥートが %{name} さんにお気に入り登録されました:'
|
||||||
subject: "%{name}さんがあなたのステータスをいいねしました"
|
subject: "%{name} さんがあなたのトゥートをお気に入りに登録しました"
|
||||||
follow:
|
follow:
|
||||||
body: "%{name}さんにフォローされています"
|
body: "%{name} さんにフォローされています"
|
||||||
subject: "%{name}さんにフォローされています"
|
subject: "%{name} さんにフォローされています"
|
||||||
follow_request:
|
follow_request:
|
||||||
body: "%{name}さんがあなたにフォローをリクエストしました。"
|
body: "%{name} さんがあなたにフォローをリクエストしました。"
|
||||||
subject: '%{name}さんからのフォローリクエスト'
|
subject: '%{name} さんからのフォローリクエスト'
|
||||||
mention:
|
mention:
|
||||||
body: '%{name}さんから返信がありました:'
|
body: '%{name} さんから返信がありました:'
|
||||||
subject: '%{name}さんに返信されました'
|
subject: '%{name} さんに返信されました'
|
||||||
reblog:
|
reblog:
|
||||||
body: 'あなたのステータスが%{name}さんにブーストされました:'
|
body: 'あなたのトゥートが %{name} さんにブーストされました:'
|
||||||
subject: "あなたのステータスが%{name}さんにブーストされました"
|
subject: "あなたのトゥートが %{name} さんにブーストされました"
|
||||||
pagination:
|
pagination:
|
||||||
next: 次
|
next: 次
|
||||||
prev: 前
|
prev: 前
|
||||||
|
@ -134,7 +134,7 @@ ja:
|
||||||
settings:
|
settings:
|
||||||
authorized_apps: 認証済みアプリ
|
authorized_apps: 認証済みアプリ
|
||||||
back: 戻る
|
back: 戻る
|
||||||
edit_profile: プロフィール編集
|
edit_profile: プロフィールを編集
|
||||||
export: データのエクスポート
|
export: データのエクスポート
|
||||||
import: データのインポート
|
import: データのインポート
|
||||||
preferences: ユーザー設定
|
preferences: ユーザー設定
|
||||||
|
@ -142,14 +142,14 @@ ja:
|
||||||
two_factor_auth: 二段階認証
|
two_factor_auth: 二段階認証
|
||||||
statuses:
|
statuses:
|
||||||
open_in_web: Webで開く
|
open_in_web: Webで開く
|
||||||
over_character_limit: '%{max}文字までしか入力できません'
|
over_character_limit: '上限は %{max}文字までです'
|
||||||
show_more: もっと見る
|
show_more: もっと見る
|
||||||
visibilities:
|
visibilities:
|
||||||
private: フォロワーだけに見せる
|
private: Private - フォロワーだけに見せる
|
||||||
public: 公開
|
public: Public - 全体に公開する
|
||||||
unlisted: 公開されますが、公開タイムラインには載りません
|
unlisted: Unlisted - トゥートは公開するが、公開タイムラインには表示しない
|
||||||
stream_entries:
|
stream_entries:
|
||||||
click_to_show: 見るにはクリック
|
click_to_show: クリックして表示
|
||||||
reblogged: ブーストされました
|
reblogged: ブーストされました
|
||||||
sensitive_content: 不適切なコンテンツの可能性があります
|
sensitive_content: 不適切なコンテンツの可能性があります
|
||||||
time:
|
time:
|
||||||
|
@ -171,3 +171,31 @@ ja:
|
||||||
invalid_otp_token: 二段階認証コードが間違っています
|
invalid_otp_token: 二段階認証コードが間違っています
|
||||||
will_paginate:
|
will_paginate:
|
||||||
page_gap: "…"
|
page_gap: "…"
|
||||||
|
|
||||||
|
errors:
|
||||||
|
404: お探しのページは見つかりませんでした。
|
||||||
|
410: お探しのページはもう存在しません。
|
||||||
|
422:
|
||||||
|
title: セキュリティ認証に失敗
|
||||||
|
content: セキュリティ認証に失敗しました。Cookieをブロックしていませんか?
|
||||||
|
admin:
|
||||||
|
settings:
|
||||||
|
title: サイト設定
|
||||||
|
setting: 設定
|
||||||
|
click_to_edit: クリックして編集
|
||||||
|
contact_information:
|
||||||
|
label: 連絡先情報
|
||||||
|
username: ユーザー名を入力
|
||||||
|
email: 公開するメールアドレスを入力
|
||||||
|
site_title: サイトのタイトル
|
||||||
|
site_description:
|
||||||
|
title: サイトの説明文
|
||||||
|
desc_html: "トップページへの表示と meta タグに使用されます。<br>HTMLタグ、特に<code><a></code> and <code><em></code>が利用可能です。"
|
||||||
|
site_description_extended:
|
||||||
|
title: サイトの詳細な説明
|
||||||
|
desc_html: "インスタンスについてのページに表示されます。<br>HTMLタグが利用可能です。"
|
||||||
|
registrations:
|
||||||
|
open:
|
||||||
|
title: 新規登録を受け付ける
|
||||||
|
enabled: 有効
|
||||||
|
disabled: 無効
|
||||||
|
|
|
@ -104,8 +104,8 @@
|
||||||
one: "1 ny hendelse siden ditt siste besøk \U0001F418"
|
one: "1 ny hendelse siden ditt siste besøk \U0001F418"
|
||||||
other: "%{count} nye hendelser siden ditt siste besøk \U0001F418"
|
other: "%{count} nye hendelser siden ditt siste besøk \U0001F418"
|
||||||
favourite:
|
favourite:
|
||||||
body: 'Din status ble satt som favoritt av %{name}'
|
body: 'Din status ble likt av %{name}'
|
||||||
subject: "%{name} satte din status som favoritt."
|
subject: "%{name} likte din status."
|
||||||
follow:
|
follow:
|
||||||
body: "%{name} følger deg!"
|
body: "%{name} følger deg!"
|
||||||
subject: "%{name} følger deg"
|
subject: "%{name} følger deg"
|
||||||
|
|
|
@ -1,24 +1,40 @@
|
||||||
---
|
---
|
||||||
es:
|
es:
|
||||||
simple_form:
|
simple_form:
|
||||||
|
hints:
|
||||||
|
defaults:
|
||||||
|
avatar: PNG, GIF o JPG. Máximo 2MB. Será escalado a 120x120px
|
||||||
|
display_name: Máximo 30 caracteres
|
||||||
|
header: PNG, GIF o JPG. Máximo 2MB. Será escalado a 700x335px
|
||||||
|
locked: Requiere que manualmente apruebes seguidores y las publicaciones serán mostradas solamente a tus seguidores
|
||||||
|
note: Máximo 160 caracteres
|
||||||
|
imports:
|
||||||
|
data: Archivo CSV exportado desde otra instancia de Mastodon
|
||||||
labels:
|
labels:
|
||||||
defaults:
|
defaults:
|
||||||
avatar: Avatar
|
avatar: Avatar
|
||||||
confirm_new_password: Confirmar nueva contraseña
|
confirm_new_password: Confirmar nueva contraseña
|
||||||
confirm_password: Confirmar contraseña
|
confirm_password: Confirmar contraseña
|
||||||
current_password: Contraseña actual
|
current_password: Contraseña actual
|
||||||
|
data: Información
|
||||||
display_name: Mostrar nombre
|
display_name: Mostrar nombre
|
||||||
email: Dirección de correo electrónico
|
email: Dirección de correo electrónico
|
||||||
header: Img. cabecera
|
header: Img. cabecera
|
||||||
locale: Idioma
|
locale: Idioma
|
||||||
new_password: Nueva contraseña
|
new_password: Nueva contraseña
|
||||||
note: Biografía
|
note: Biografía
|
||||||
|
otp_attempt: Código de dos factores
|
||||||
password: Contraseña
|
password: Contraseña
|
||||||
|
setting_default_privacy: Privacidad de publicaciones
|
||||||
|
type: Importar tipo
|
||||||
username: Nombre de usuario
|
username: Nombre de usuario
|
||||||
|
interactions:
|
||||||
|
must_be_follower: Bloquear notificaciones de personas que no te siguen
|
||||||
|
must_be_following: Bloquear notificaciones de personas que no sigues
|
||||||
notification_emails:
|
notification_emails:
|
||||||
favourite: Enviar correo electrónico cuando alguien de a favorito en su publicación
|
favourite: Enviar correo electrónico cuando alguien de a favorito en su publicación
|
||||||
follow: Enviar correo electrónico cuando alguien le siga
|
follow: Enviar correo electrónico cuando alguien te siga
|
||||||
mention: Enviar correo electrónico cuando alguien le mencione
|
mention: Enviar correo electrónico cuando alguien te mencione
|
||||||
reblog: Enviar correo electrónico cuando alguien comparta su publicación
|
reblog: Enviar correo electrónico cuando alguien comparta su publicación
|
||||||
'no': 'No'
|
'no': 'No'
|
||||||
required:
|
required:
|
||||||
|
|
|
@ -3,17 +3,17 @@ ja:
|
||||||
simple_form:
|
simple_form:
|
||||||
hints:
|
hints:
|
||||||
defaults:
|
defaults:
|
||||||
avatar: PNGやGIF、JPGは2MBまでです。120x120pxまで縮小されます。
|
avatar: 2MBまでのPNGやGIF、JPGが利用可能です。120x120pxまで縮小されます。
|
||||||
display_name: 名前は30文字まで設定することができます。
|
display_name: 名前は30文字まで設定することができます。
|
||||||
header: PNGやGIF、JPGは2MBまでです。 700x335pxまで縮小されます。
|
header: 2MBまでのPNGやGIF、JPGが利用可能です。 700x335pxまで縮小されます。
|
||||||
locked: フォロワーを手動で承認する必要があります。デフォルトでは投稿範囲はフォロワーまでです。
|
locked: フォロワーを手動で承認する必要があります。デフォルトではトゥートの公開範囲はフォロワーのみです。
|
||||||
note: プロフィールは30文字まで設定することができます。
|
note: プロフィールは160文字まで設定することができます。
|
||||||
imports:
|
imports:
|
||||||
data: CSVファイルからデータをインポートしました。
|
data: 他の Mastodon サーバーからエクスポートしたCSVファイルを選択して下さい
|
||||||
labels:
|
labels:
|
||||||
defaults:
|
defaults:
|
||||||
avatar: アカウント
|
avatar: アイコン
|
||||||
confirm_new_password: 新しいパスワード(確認用)
|
confirm_new_password: 新しいパスワード(確認用)
|
||||||
confirm_password: 新しいパスワード
|
confirm_password: 新しいパスワード
|
||||||
current_password: 現在のパスワード
|
current_password: 現在のパスワード
|
||||||
data: データ
|
data: データ
|
||||||
|
@ -26,19 +26,20 @@ ja:
|
||||||
note: プロフィール
|
note: プロフィール
|
||||||
otp_attempt: 二段階認証コード
|
otp_attempt: 二段階認証コード
|
||||||
password: パスワード
|
password: パスワード
|
||||||
setting_default_privacy: 投稿範囲
|
setting_default_privacy: 投稿の公開範囲
|
||||||
type: インポートするファイルの種類
|
type: インポートする項目
|
||||||
username: ユーザー名
|
username: ユーザー名
|
||||||
|
setting_boost_modal: ブーストする前に確認ダイアログを表示する
|
||||||
interactions:
|
interactions:
|
||||||
must_be_follower: フォロワー以外からの通知をブロック
|
must_be_follower: フォロワー以外からの通知をブロック
|
||||||
must_be_following: フォローしていないユーザーからの通知をブロック
|
must_be_following: フォローしていないユーザーからの通知をブロック
|
||||||
notification_emails:
|
notification_emails:
|
||||||
digest: タイムラインからピックアップしてメールで通知する
|
digest: タイムラインからピックアップしてメールで通知する
|
||||||
favourite: いいねされた時メールで通知する
|
favourite: お気に入りに登録された時にメールで通知する
|
||||||
follow: フォローされた時メールで通知する
|
follow: フォローされた時にメールで通知する
|
||||||
follow_request: フォローリクエストを受けた時メールで通知する
|
follow_request: フォローリクエストを受けた時にメールで通知する
|
||||||
mention: 返信された時メールで通知する
|
mention: 返信が来た時にメールで通知する
|
||||||
reblog: あなたのトゥートがブーストされた時メールで通知する
|
reblog: トゥートがブーストされた時にメールで通知する
|
||||||
'no': 'いいえ'
|
'no': 'いいえ'
|
||||||
required:
|
required:
|
||||||
mark: "*"
|
mark: "*"
|
||||||
|
|
|
@ -34,7 +34,7 @@
|
||||||
must_be_following: Blokker meldinger fra folk du ikke følger
|
must_be_following: Blokker meldinger fra folk du ikke følger
|
||||||
notification_emails:
|
notification_emails:
|
||||||
digest: Send oppsummerings eposter
|
digest: Send oppsummerings eposter
|
||||||
favourite: Send e-post når noen setter din status som favoritt
|
favourite: Send e-post når noen liker din status
|
||||||
follow: Send e-post når noen følger deg
|
follow: Send e-post når noen følger deg
|
||||||
follow_request: Send e-post når noen spør om å få følge deg
|
follow_request: Send e-post når noen spør om å få følge deg
|
||||||
mention: Send e-post når noen nevner deg
|
mention: Send e-post når noen nevner deg
|
||||||
|
|
46
config/locales/simple_form.zh-HK.yml
Normal file
46
config/locales/simple_form.zh-HK.yml
Normal file
|
@ -0,0 +1,46 @@
|
||||||
|
---
|
||||||
|
zh-HK:
|
||||||
|
simple_form:
|
||||||
|
hints:
|
||||||
|
defaults:
|
||||||
|
avatar: 支援 PNG, GIF 或 JPG 圖片,檔案大小上限為 2MB,會被縮裁成 120x120px
|
||||||
|
display_name: 最多 30 個字元
|
||||||
|
header: 支援 PNG, GIF 或 JPG 圖片,檔案大小上限為 2MB,會被縮裁成 700x335px
|
||||||
|
locked: 你必須人手核准每個用戶對你的關注請求,而你的文章私隱會被預設為「只有關注你的人能看」
|
||||||
|
note: 最多 160 個字元
|
||||||
|
imports:
|
||||||
|
data: 自其他服務站匯出的 CSV 檔案
|
||||||
|
labels:
|
||||||
|
defaults:
|
||||||
|
avatar: 個人頭像
|
||||||
|
confirm_new_password: 確認新密碼
|
||||||
|
confirm_password: 確認密碼
|
||||||
|
current_password: 目前密碼
|
||||||
|
data: 資料
|
||||||
|
display_name: 顯示名稱
|
||||||
|
email: 電郵地址
|
||||||
|
header: 個人頁面頂部
|
||||||
|
locale: 語言
|
||||||
|
locked: 將用戶轉為「私人」
|
||||||
|
new_password: 新密碼
|
||||||
|
note: 簡介
|
||||||
|
otp_attempt: 雙重認證碼
|
||||||
|
password: 密碼
|
||||||
|
setting_default_privacy: 文章預設私隱度
|
||||||
|
type: 匯入資料類型
|
||||||
|
username: 用戶名稱
|
||||||
|
interactions:
|
||||||
|
must_be_follower: 隱藏沒有關注你的用戶的通知
|
||||||
|
must_be_following: 隱藏你不關注的用戶的通知
|
||||||
|
notification_emails:
|
||||||
|
digest: 定期電郵摘要
|
||||||
|
favourite: 當有用戶喜歡你的文章時,發電郵通知
|
||||||
|
follow: 當有用戶關注你時,發電郵通知
|
||||||
|
follow_request: 當有用戶要求關注你時,發電郵通知
|
||||||
|
mention: 當有用戶在文章提及你時,發電郵通知
|
||||||
|
reblog: 當有用戶轉推你的文章時,發電郵通知
|
||||||
|
'no': '否'
|
||||||
|
required:
|
||||||
|
mark: "*"
|
||||||
|
text: 必須填寫
|
||||||
|
'yes': '是'
|
172
config/locales/zh-HK.yml
Normal file
172
config/locales/zh-HK.yml
Normal file
|
@ -0,0 +1,172 @@
|
||||||
|
---
|
||||||
|
zh-HK:
|
||||||
|
about:
|
||||||
|
about_mastodon: Mastodon (長毛象)是一個<em>自由、開放源碼</em>的社交網站。它是一個分散式的服務,避免你的通訊被單一商業機構壟斷操控。請你選擇一家你信任的 Mastodon 服務站,在上面建立帳號,然後你就可以和任一 Mastodon 服務站上的用戶互通,享受無縫的<em>社交網絡</em>交流。
|
||||||
|
about_this: 關於本服務站
|
||||||
|
apps: 應用程式
|
||||||
|
business_email: 商業電郵︰
|
||||||
|
closed_registrations: 本服務站暫時停止接受登記。
|
||||||
|
contact: 聯絡
|
||||||
|
description_headline: 關於 %{domain}
|
||||||
|
domain_count_after: 個其他服務站
|
||||||
|
domain_count_before: 已連接至
|
||||||
|
features:
|
||||||
|
api: 開放 API,供各式應用程式及服務連入
|
||||||
|
blocks: 完善的封鎖用戶、靜音功能
|
||||||
|
characters: 每篇文章最多 500 字
|
||||||
|
chronology: 時間軸忠實按時序顯示文章,不作多餘處理
|
||||||
|
ethics: 良心設計︰沒有廣告,不追蹤你的使用行為
|
||||||
|
gifv: 支援顯示 GIFV 短片圖組
|
||||||
|
privacy: 可逐篇文章設定私隱度
|
||||||
|
public: 公共時間軸
|
||||||
|
features_headline: 甚麼讓 Mastodon 與眾不同
|
||||||
|
get_started: 立即登記
|
||||||
|
links: 連結
|
||||||
|
other_instances: 其他服務站
|
||||||
|
source_code: 源代碼
|
||||||
|
status_count_after: 篇文章
|
||||||
|
status_count_before: 他們共發佈了
|
||||||
|
terms: 使用條款
|
||||||
|
user_count_after: 位使用者
|
||||||
|
user_count_before: 這裏共註冊有
|
||||||
|
accounts:
|
||||||
|
follow: 關注
|
||||||
|
followers: 關注者
|
||||||
|
following: 正在關注
|
||||||
|
nothing_here: 暫時未有內容可以顯示
|
||||||
|
people_followed_by: '%{name} 關注的人'
|
||||||
|
people_who_follow: 關注 %{name} 的人
|
||||||
|
posts: 文章
|
||||||
|
remote_follow: 跨站關注
|
||||||
|
unfollow: 取消關注
|
||||||
|
application_mailer:
|
||||||
|
settings: '修改電郵設定︰ %{link}'
|
||||||
|
signature: 來自 %{instance} 的 Mastodon 通知
|
||||||
|
view: '進入瀏覽︰'
|
||||||
|
applications:
|
||||||
|
invalid_url: 所提供的網址不正確
|
||||||
|
auth:
|
||||||
|
change_password: 登入資訊
|
||||||
|
didnt_get_confirmation: 沒有收到確認指示電郵?
|
||||||
|
forgot_password: 忘記了密碼?
|
||||||
|
login: 登入
|
||||||
|
logout: 登出
|
||||||
|
register: 登記
|
||||||
|
resend_confirmation: 重發確認指示電郵
|
||||||
|
reset_password: 重設密碼
|
||||||
|
set_new_password: 設定新密碼
|
||||||
|
authorize_follow:
|
||||||
|
error: 對不起,尋找這個跨站用戶的過程發生錯誤
|
||||||
|
follow: 關注
|
||||||
|
prompt_html: '你 (<strong>%{self}</strong>) 正準備關注︰'
|
||||||
|
title: 關注 %{acct}
|
||||||
|
datetime:
|
||||||
|
distance_in_words:
|
||||||
|
about_x_hours: "%{count}小時前"
|
||||||
|
about_x_months: "%{count}個月前"
|
||||||
|
about_x_years: "%{count}年前"
|
||||||
|
almost_x_years: "接近%{count}年前"
|
||||||
|
half_a_minute: 剛剛
|
||||||
|
less_than_x_minutes: "少於%{count}分鐘前"
|
||||||
|
less_than_x_seconds: 剛剛
|
||||||
|
over_x_years: "%{count}y"
|
||||||
|
x_days: "%{count}日"
|
||||||
|
x_minutes: "%{count}分鐘"
|
||||||
|
x_months: "%{count}個月"
|
||||||
|
x_seconds: "%{count}秒"
|
||||||
|
exports:
|
||||||
|
blocks: 被你封鎖的用戶
|
||||||
|
csv: CSV
|
||||||
|
follows: 你所關注的用戶
|
||||||
|
storage: 媒體容量大小
|
||||||
|
generic:
|
||||||
|
changes_saved_msg: 已成功儲存修改
|
||||||
|
powered_by: 網站由 %{link} 開發
|
||||||
|
save_changes: 儲存修改
|
||||||
|
validation_errors:
|
||||||
|
one: 提交的資料有問題
|
||||||
|
other: 提交的資料有 %{count} 項問題
|
||||||
|
imports:
|
||||||
|
preface: 你可以在此匯入你在其他服務站所匯出的資料檔,包括︰你所關注的用戶,被你封鎖的用戶。
|
||||||
|
success: 你已成功上載資料檔,我們正將資料匯入,請稍候
|
||||||
|
types:
|
||||||
|
blocking: 被你封鎖的用戶名單
|
||||||
|
following: 你所關注的用戶名單
|
||||||
|
upload: 上載
|
||||||
|
landing_strip_html: <strong>%{name}</strong> 是一個在 <strong>%{domain}</strong> 的用戶。只要你有任何 Mastodon 服務站、或者聯盟網站的用戶,便可以跨站關注此站用戶,或者與他們互動。如果你沒有這類用戶,歡迎在<a href="%{sign_up_path}">此處登記</a>。
|
||||||
|
media_attachments:
|
||||||
|
validations:
|
||||||
|
images_and_video: 不能在已有圖片的文章上加入影片
|
||||||
|
too_many: 不可以加入超過 4 個檔案
|
||||||
|
notification_mailer:
|
||||||
|
digest:
|
||||||
|
body: '這是自從你在%{since}使用%{instance}以後,你錯失了的訊息︰'
|
||||||
|
mention: "%{name} 在此提及了你︰"
|
||||||
|
new_followers_summary:
|
||||||
|
one: 你新獲得了 1 位關注者了!恭喜!
|
||||||
|
other: 你新獲得了 %{count} 位關注者了!好厲害!
|
||||||
|
subject:
|
||||||
|
one: "自從上次登入以來,你收到 1 則新的通知 \U0001F418"
|
||||||
|
other: "自從上次登入以來,你收到 %{count} 則新的通知 \U0001F418"
|
||||||
|
favourite:
|
||||||
|
body: '你的文章獲得 %{name} 的喜愛'
|
||||||
|
subject: "%{name} 喜歡你的文章"
|
||||||
|
follow:
|
||||||
|
body: "%{name} 開始關注你!"
|
||||||
|
subject: "%{name} 現正關注你"
|
||||||
|
follow_request:
|
||||||
|
body: "%{name} 要求關注你"
|
||||||
|
subject: '等候關注你的用戶︰ %{name}'
|
||||||
|
mention:
|
||||||
|
body: '%{name} 在文章中提及你︰'
|
||||||
|
subject: '%{name} 在文章中提及你'
|
||||||
|
reblog:
|
||||||
|
body: '你的文章得到 %{name} 的轉推'
|
||||||
|
subject: "%{name} 轉推了你的文章"
|
||||||
|
pagination:
|
||||||
|
next: 下一頁
|
||||||
|
prev: 上一頁
|
||||||
|
truncate: "……"
|
||||||
|
remote_follow:
|
||||||
|
acct: 請輸入你的︰用戶名稱@服務點域名
|
||||||
|
missing_resource: 無法找到你用戶的轉接網址
|
||||||
|
proceed: 下一步
|
||||||
|
prompt: '你希望關注︰'
|
||||||
|
settings:
|
||||||
|
authorized_apps: 授權應用程式
|
||||||
|
back: 回到 Mastodon
|
||||||
|
edit_profile: 修改個人資料
|
||||||
|
export: 匯出
|
||||||
|
import: 匯入
|
||||||
|
preferences: 偏好設定
|
||||||
|
settings: 設定
|
||||||
|
two_factor_auth: 雙重認證
|
||||||
|
statuses:
|
||||||
|
open_in_web: 開啟網頁
|
||||||
|
over_character_limit: 超過了 %{max} 字的限制
|
||||||
|
show_more: 顯示更多
|
||||||
|
visibilities:
|
||||||
|
private: 只有關注你的人能看
|
||||||
|
public: 公開
|
||||||
|
unlisted: 公開,但不在公共時間軸顯示
|
||||||
|
stream_entries:
|
||||||
|
click_to_show: 點擊顯示
|
||||||
|
reblogged: 轉推
|
||||||
|
sensitive_content: 敏感內容
|
||||||
|
time:
|
||||||
|
formats:
|
||||||
|
default: "%Y年%-m月%d日 %H:%M"
|
||||||
|
two_factor_auth:
|
||||||
|
code_hint: 請輸入你認證器產生的代碼,以確認設定
|
||||||
|
description_html: 當你啟用<strong>雙重認證</strong>後,你登入時將需要使你手機、或其他種類認證器產生的代碼。
|
||||||
|
disable: 停用
|
||||||
|
enable: 啟用
|
||||||
|
enabled_success: 已成功啟用雙重認證
|
||||||
|
instructions_html: <strong>請用你手機的認證器應用程式(如 Google Authenticator、Authy),掃描這裏的 QR 圖形碼</strong>。在雙重認證啟用後,你登入時將須要使用此應用程式產生的認證碼。
|
||||||
|
manual_instructions: 如果你無法掃描 QR 圖形碼,請手動輸入這個文字密碼︰
|
||||||
|
setup: 設定
|
||||||
|
warning: 如果你現在無法正確設定你的應用程式,請即「停用」雙重認證,否則日後可能無法登入本站。
|
||||||
|
wrong_code: 你輸入的認證碼並不正確!可能伺服器時間和你手機不一致,請檢查你手機的時鐘,或與本站管理員聯絡。
|
||||||
|
users:
|
||||||
|
invalid_email: 電郵地址格式不正確
|
||||||
|
invalid_otp_token: 雙重認證確認碼不正確
|
|
@ -15,7 +15,7 @@ Rails.application.routes.draw do
|
||||||
end
|
end
|
||||||
|
|
||||||
get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta
|
get '.well-known/host-meta', to: 'xrd#host_meta', as: :host_meta
|
||||||
get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger
|
get '.well-known/webfinger', to: 'xrd#webfinger', as: :webfinger, defaults: { format: 'json' }
|
||||||
|
|
||||||
devise_for :users, path: 'auth', controllers: {
|
devise_for :users, path: 'auth', controllers: {
|
||||||
sessions: 'auth/sessions',
|
sessions: 'auth/sessions',
|
||||||
|
@ -57,6 +57,7 @@ Rails.application.routes.draw do
|
||||||
namespace :exports, constraints: { format: :csv } do
|
namespace :exports, constraints: { format: :csv } do
|
||||||
resources :follows, only: :index, controller: :following_accounts
|
resources :follows, only: :index, controller: :following_accounts
|
||||||
resources :blocks, only: :index, controller: :blocked_accounts
|
resources :blocks, only: :index, controller: :blocked_accounts
|
||||||
|
resources :mutes, only: :index, controller: :muted_accounts
|
||||||
end
|
end
|
||||||
|
|
||||||
resource :two_factor_auth, only: [:show, :new, :create] do
|
resource :two_factor_auth, only: [:show, :new, :create] do
|
||||||
|
|
|
@ -1 +1 @@
|
||||||
[The documentation has moved to its own repository](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Tuning-guide.md)
|
[The documentation has moved to its own repository](https://github.com/tootsuite/documentation/blob/master/Running-Mastodon/Tuning.md)
|
||||||
|
|
|
@ -13,11 +13,12 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
|
||||||
|
|
||||||
describe 'GET #index' do
|
describe 'GET #index' do
|
||||||
before do
|
before do
|
||||||
status = PostStatusService.new.call(user.account, 'Test')
|
first_status = PostStatusService.new.call(user.account, 'Test')
|
||||||
@reblog = ReblogService.new.call(other.account, status)
|
@reblog_of_first_status = ReblogService.new.call(other.account, first_status)
|
||||||
@mention = PostStatusService.new.call(other.account, 'Hello @alice')
|
mentioning_status = PostStatusService.new.call(other.account, 'Hello @alice')
|
||||||
@favourite = FavouriteService.new.call(other.account, status)
|
@mention_from_status = mentioning_status.mentions.first
|
||||||
@follow = FollowService.new.call(other.account, 'alice')
|
@favourite = FavouriteService.new.call(other.account, first_status)
|
||||||
|
@follow = FollowService.new.call(other.account, 'alice')
|
||||||
end
|
end
|
||||||
|
|
||||||
describe 'with no options' do
|
describe 'with no options' do
|
||||||
|
@ -30,19 +31,19 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes reblog' do
|
it 'includes reblog' do
|
||||||
expect(assigns(:notifications).map(&:activity_id)).to include(@reblog.id)
|
expect(assigns(:notifications).map(&:activity)).to include(@reblog_of_first_status)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes mention' do
|
it 'includes mention' do
|
||||||
expect(assigns(:notifications).map(&:activity_id)).to include(@mention.mentions.first.id)
|
expect(assigns(:notifications).map(&:activity)).to include(@mention_from_status)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes favourite' do
|
it 'includes favourite' do
|
||||||
expect(assigns(:notifications).map(&:activity_id)).to include(@favourite.id)
|
expect(assigns(:notifications).map(&:activity)).to include(@favourite)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes follow' do
|
it 'includes follow' do
|
||||||
expect(assigns(:notifications).map(&:activity_id)).to include(@follow.id)
|
expect(assigns(:notifications).map(&:activity)).to include(@follow)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
@ -56,19 +57,19 @@ RSpec.describe Api::V1::NotificationsController, type: :controller do
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes reblog' do
|
it 'includes reblog' do
|
||||||
expect(assigns(:notifications).map(&:activity_id)).to include(@reblog.id)
|
expect(assigns(:notifications).map(&:activity)).to include(@reblog_of_first_status)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'excludes mention' do
|
it 'excludes mention' do
|
||||||
expect(assigns(:notifications).map(&:activity_id)).to_not include(@mention.mentions.first.id)
|
expect(assigns(:notifications).map(&:activity)).to_not include(@mention_from_status)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes favourite' do
|
it 'includes favourite' do
|
||||||
expect(assigns(:notifications).map(&:activity_id)).to include(@favourite.id)
|
expect(assigns(:notifications).map(&:activity)).to include(@favourite)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'includes follow' do
|
it 'includes follow' do
|
||||||
expect(assigns(:notifications).map(&:activity_id)).to include(@follow.id)
|
expect(assigns(:notifications).map(&:activity)).to include(@follow)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ describe Settings::Exports::BlockedAccountsController do
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.content_type).to eq 'text/csv'
|
expect(response.content_type).to eq 'text/csv'
|
||||||
expect(response.headers['Content-Disposition']).to eq 'attachment; filename="blocking.csv"'
|
expect(response.headers['Content-Disposition']).to eq 'attachment; filename="blocked_accounts.csv"'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -11,7 +11,7 @@ describe Settings::Exports::FollowingAccountsController do
|
||||||
|
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
expect(response.content_type).to eq 'text/csv'
|
expect(response.content_type).to eq 'text/csv'
|
||||||
expect(response.headers['Content-Disposition']).to eq 'attachment; filename="following.csv"'
|
expect(response.headers['Content-Disposition']).to eq 'attachment; filename="following_accounts.csv"'
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,17 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe Settings::Exports::MutedAccountsController do
|
||||||
|
before do
|
||||||
|
sign_in Fabricate(:user), scope: :user
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'GET #index' do
|
||||||
|
it 'returns a csv of the muting accounts' do
|
||||||
|
get :index, format: :csv
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response.content_type).to eq 'text/csv'
|
||||||
|
expect(response.headers['Content-Disposition']).to eq 'attachment; filename="muted_accounts.csv"'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
|
@ -17,4 +17,14 @@ RSpec.describe StreamEntriesController, type: :controller do
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
describe 'GET #embed' do
|
||||||
|
it 'returns embedded view of status' do
|
||||||
|
get :embed, params: { account_username: alice.username, id: status.stream_entry.id }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response.headers['X-Frame-Options']).to eq 'ALLOWALL'
|
||||||
|
expect(response).to render_template(layout: 'embedded')
|
||||||
|
end
|
||||||
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -14,12 +14,12 @@ RSpec.describe XrdController, type: :controller do
|
||||||
let(:alice) { Fabricate(:account, username: 'alice') }
|
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||||
|
|
||||||
it 'returns http success when account can be found' do
|
it 'returns http success when account can be found' do
|
||||||
get :webfinger, params: { resource: alice.to_webfinger_s }
|
get :webfinger, params: { resource: alice.to_webfinger_s }, format: :json
|
||||||
expect(response).to have_http_status(:success)
|
expect(response).to have_http_status(:success)
|
||||||
end
|
end
|
||||||
|
|
||||||
it 'returns http not found when account cannot be found' do
|
it 'returns http not found when account cannot be found' do
|
||||||
get :webfinger, params: { resource: 'acct:not@existing.com' }
|
get :webfinger, params: { resource: 'acct:not@existing.com' }, format: :json
|
||||||
expect(response).to have_http_status(:not_found)
|
expect(response).to have_http_status(:not_found)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,30 @@
|
||||||
|
import { expect } from 'chai';
|
||||||
|
import { mount } from 'enzyme';
|
||||||
|
import sinon from 'sinon';
|
||||||
|
|
||||||
|
import Column from '../../../../../../app/assets/javascripts/components/features/ui/components/column';
|
||||||
|
import ColumnHeader from '../../../../../../app/assets/javascripts/components/features/ui/components/column_header';
|
||||||
|
|
||||||
|
describe('<Column />', () => {
|
||||||
|
describe('<ColumnHeader /> click handler', () => {
|
||||||
|
beforeEach(() => {
|
||||||
|
global.requestAnimationFrame = sinon.spy();
|
||||||
|
});
|
||||||
|
|
||||||
|
it('runs the scroll animation if the column contains scrollable content', () => {
|
||||||
|
const wrapper = mount(
|
||||||
|
<Column heading="notifications">
|
||||||
|
<div className="scrollable" />
|
||||||
|
</Column>
|
||||||
|
);
|
||||||
|
wrapper.find(ColumnHeader).simulate('click');
|
||||||
|
expect(global.requestAnimationFrame.called).to.equal(true);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('does not try to scroll if there is no scrollable content', () => {
|
||||||
|
const wrapper = mount(<Column heading="notifications" />);
|
||||||
|
wrapper.find(ColumnHeader).simulate('click');
|
||||||
|
expect(global.requestAnimationFrame.called).to.equal(false);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
88
spec/lib/webfinger_resource_spec.rb
Normal file
88
spec/lib/webfinger_resource_spec.rb
Normal file
|
@ -0,0 +1,88 @@
|
||||||
|
require 'rails_helper'
|
||||||
|
|
||||||
|
describe WebfingerResource do
|
||||||
|
describe '#username' do
|
||||||
|
describe 'with a URL value' do
|
||||||
|
it 'raises with an unrecognized route' do
|
||||||
|
resource = 'https://example.com/users/alice/other'
|
||||||
|
|
||||||
|
expect {
|
||||||
|
WebfingerResource.new(resource).username
|
||||||
|
}.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises with a string that doesnt start with URL' do
|
||||||
|
resource = 'website for http://example.com/users/alice/other'
|
||||||
|
|
||||||
|
expect {
|
||||||
|
WebfingerResource.new(resource).username
|
||||||
|
}.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'finds the username in a valid https route' do
|
||||||
|
resource = 'https://example.com/users/alice'
|
||||||
|
|
||||||
|
result = WebfingerResource.new(resource).username
|
||||||
|
expect(result).to eq 'alice'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'finds the username in a mixed case http route' do
|
||||||
|
resource = 'HTTp://exAMPLEe.com/users/alice'
|
||||||
|
|
||||||
|
result = WebfingerResource.new(resource).username
|
||||||
|
expect(result).to eq 'alice'
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'finds the username in a valid http route' do
|
||||||
|
resource = 'http://example.com/users/alice'
|
||||||
|
|
||||||
|
result = WebfingerResource.new(resource).username
|
||||||
|
expect(result).to eq 'alice'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'with a username and hostname value' do
|
||||||
|
it 'raises on a non-local domain' do
|
||||||
|
resource = 'user@remote-host.com'
|
||||||
|
|
||||||
|
expect {
|
||||||
|
WebfingerResource.new(resource).username
|
||||||
|
}.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'finds username for a local domain' do
|
||||||
|
Rails.configuration.x.local_domain = 'example.com'
|
||||||
|
resource = 'alice@example.com'
|
||||||
|
|
||||||
|
result = WebfingerResource.new(resource).username
|
||||||
|
expect(result).to eq 'alice'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe 'with an acct value' do
|
||||||
|
it 'raises on a non-local domain' do
|
||||||
|
resource = 'acct:user@remote-host.com'
|
||||||
|
|
||||||
|
expect {
|
||||||
|
WebfingerResource.new(resource).username
|
||||||
|
}.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'raises on a nonsense domain' do
|
||||||
|
resource = 'acct:user@remote-host@remote-hostess.remote.local@remote'
|
||||||
|
|
||||||
|
expect {
|
||||||
|
WebfingerResource.new(resource).username
|
||||||
|
}.to raise_error(ActiveRecord::RecordNotFound)
|
||||||
|
end
|
||||||
|
|
||||||
|
it 'finds the username for a local account' do
|
||||||
|
Rails.configuration.x.local_domain = 'example.com'
|
||||||
|
resource = 'acct:alice@example.com'
|
||||||
|
|
||||||
|
result = WebfingerResource.new(resource).username
|
||||||
|
expect(result).to eq 'alice'
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
33
spec/requests/webfinger_request_spec.rb
Normal file
33
spec/requests/webfinger_request_spec.rb
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
require "rails_helper"
|
||||||
|
|
||||||
|
describe "The webfinger route" do
|
||||||
|
let(:alice) { Fabricate(:account, username: 'alice') }
|
||||||
|
|
||||||
|
describe "requested without accepts headers" do
|
||||||
|
it "returns a json response" do
|
||||||
|
get webfinger_url, params: { resource: alice.to_webfinger_s }
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response.content_type).to eq "application/jrd+json"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "requested with html in accepts headers" do
|
||||||
|
it "returns a json response" do
|
||||||
|
headers = { 'HTTP_ACCEPT' => 'text/html' }
|
||||||
|
get webfinger_url, params: { resource: alice.to_webfinger_s }, headers: headers
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response.content_type).to eq "application/jrd+json"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
describe "requested with xml format" do
|
||||||
|
it "returns an xml response" do
|
||||||
|
get webfinger_url(resource: alice.to_webfinger_s, format: :xml)
|
||||||
|
|
||||||
|
expect(response).to have_http_status(:success)
|
||||||
|
expect(response.content_type).to eq "application/xrd+xml"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
Reference in a new issue