gearheads
/
mastodon
Archived
2
0
Fork 0

Gearheads: merge to Mastodon 4.2.0-beta2

gh/dev
Ducky 2023-08-28 00:18:16 +01:00
commit cbd48a29b2
252 changed files with 3630 additions and 2405 deletions

View File

@ -1,31 +1,29 @@
// For more details, see https://aka.ms/devcontainer.json.
{
"name": "Mastodon",
"dockerComposeFile": "docker-compose.yml",
"service": "app",
"workspaceFolder": "/workspaces/${localWorkspaceFolderBasename}",
// Features to add to the dev container. More info: https://containers.dev/features.
"features": {
"ghcr.io/devcontainers/features/sshd:1": {}
},
// Use 'forwardPorts' to make a list of ports inside the container available locally.
// This can be used to network with other containers or the host.
"runServices": ["app", "db", "redis"],
"forwardPorts": [3000, 4000],
// Use 'postCreateCommand' to run commands after the container is created.
"containerEnv": {
"ES_ENABLED": "",
"LIBRE_TRANSLATE_ENDPOINT": ""
},
"onCreateCommand": "git config --global --add safe.directory ${containerWorkspaceFolder}",
"postCreateCommand": ".devcontainer/post-create.sh",
"waitFor": "postCreateCommand",
// Configure tool-specific properties.
"customizations": {
// Configure properties specific to VS Code.
"vscode": {
// Set *default* container specific settings.json values on container create.
"settings": {},
// Add the IDs of extensions you want installed when the container is created.
"extensions": ["EditorConfig.EditorConfig", "webben.browserslist"]
}
}

View File

@ -1,20 +1,21 @@
{
$schema: 'https://docs.renovatebot.com/renovate-schema.json',
extends: [
'config:base',
':dependencyDashboard',
'config:recommended',
':labels(dependencies)',
':maintainLockFilesMonthly', // update non-direct dependencies monthly
':prConcurrentLimit10', // only 10 open PRs at the same time
':prConcurrentLimitNone', // Remove limit for open PRs at any time.
':prHourlyLimit2', // Rate limit PR creation to a maximum of two per hour.
],
stabilityDays: 3, // Wait 3 days after the package has been published before upgrading it
minimumReleaseAge: '3', // Wait 3 days after the package has been published before upgrading it
// packageRules order is important, they are applied from top to bottom and are merged,
// meaning the most important ones must be at the bottom, for example grouping rules
// If we do not want a package to be grouped with others, we need to set its groupName
// to `null` after any other rule set it to something.
dependencyDashboardHeader: 'This issue lists Renovate updates and detected dependencies. Read the [Dependency Dashboard](https://docs.renovatebot.com/key-concepts/dashboard/) docs to learn more. Before approving any upgrade: read the description and comments in the [`renovate.json5` file](https://github.com/mastodon/mastodon/blob/main/.github/renovate.json5).',
packageRules: [
{
// Ignore major version bumps for these node packages
// Require Dependency Dashboard Approval for major version bumps of these node packages
matchManagers: ['npm'],
matchPackageNames: [
'tesseract.js', // Requires code changes
@ -41,10 +42,10 @@
'react-router-dom',
],
matchUpdateTypes: ['major'],
enabled: false,
dependencyDashboardApproval: true,
},
{
// Ignore major version bumps for these Ruby packages
// Require Dependency Dashboard Approval for major version bumps of these Ruby packages
matchManagers: ['bundler'],
matchPackageNames: [
'rack', // Needs to be synced with Rails version
@ -55,7 +56,7 @@
'redis', // Requires manual upgrade and sync with Sidekiq version
],
matchUpdateTypes: ['major'],
enabled: false,
dependencyDashboardApproval: true,
},
{
// Update Github Actions and Docker images weekly
@ -63,25 +64,25 @@
extends: ['schedule:weekly'],
},
{
// Ignore major & minor bumps for the ruby image, this needs to be synced with .ruby-version
// Require Dependency Dashboard Approval for major & minor bumps for the ruby image, this needs to be synced with .ruby-version
matchManagers: ['dockerfile'],
matchPackageNames: ['moritzheiber/ruby-jemalloc'],
matchUpdateTypes: ['minor', 'major'],
enabled: false,
dependencyDashboardApproval: true,
},
{
// Ignore major bump for the node image, this needs to be synced with .nvmrc
// Require Dependency Dashboard Approval for major bumps for the node image, this needs to be synced with .nvmrc
matchManagers: ['dockerfile'],
matchPackageNames: ['node'],
matchUpdateTypes: ['major'],
enabled: false,
dependencyDashboardApproval: true,
},
{
// Ignore major postgres bumps in the docker-compose file, as those break dev environments
// Require Dependency Dashboard Approval for major postgres bumps in the docker-compose file, as those break dev environments
matchManagers: ['docker-compose'],
matchPackageNames: ['postgres'],
matchUpdateTypes: ['major'],
enabled: false,
dependencyDashboardApproval: true,
},
{
// Update devDependencies every week, with one grouped PR

View File

@ -8,6 +8,9 @@ The following changelog entries focus on changes visible to users, administrator
### Added
- **Add “Privacy and reach” tab in profile settings** ([Gargron](https://github.com/mastodon/mastodon/pull/26484), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26508))
This reorganized scattered privacy and reach settings to a single place, as well as improve their wording.
- **Add display of out-of-band hashtags in the web interface** ([Gargron](https://github.com/mastodon/mastodon/pull/26492), [arbolitoloco1](https://github.com/mastodon/mastodon/pull/26497), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26506), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26525))
- **Add role badges to the web interface** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25649), [Gargron](https://github.com/mastodon/mastodon/pull/26281))
- **Add ability to pick domains to forward reports to using the `forward_to_domains` parameter in `POST /api/v1/reports`** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25866))
The `forward_to_domains` REST API parameter is a list of strings. If it is empty or omitted, the previous behavior is maintained.
@ -23,8 +26,18 @@ The following changelog entries focus on changes visible to users, administrator
- **Add optional hCaptcha support** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25019), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25057), [Gargron](https://github.com/mastodon/mastodon/pull/25395), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26388))
- **Add lines to threads in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/24549), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24677), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24696), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24711), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24714), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24713), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24715), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24800), [teeerevor](https://github.com/mastodon/mastodon/pull/25706), [renchap](https://github.com/mastodon/mastodon/pull/25807))
- **Add new onboarding flow to web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/24619), [Gargron](https://github.com/mastodon/mastodon/pull/24646), [Gargron](https://github.com/mastodon/mastodon/pull/24705), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/24872), [ThisIsMissEm](https://github.com/mastodon/mastodon/pull/24883), [Gargron](https://github.com/mastodon/mastodon/pull/24954), [stevenjlm](https://github.com/mastodon/mastodon/pull/24959), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25010), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25275), [Gargron](https://github.com/mastodon/mastodon/pull/25559), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25561))
- **Add `S3_DISABLE_CHECKSUM_MODE` environment variable for compatibility with some S3-compatible providers** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26435))
- **Add auto-refresh of accounts we get new messages/edits of** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26510))
- **Add Elasticsearch cluster health check and indexes mismatch check to dashboard** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26448))
- Add support for `indexable` attribute on remote actors ([Gargron](https://github.com/mastodon/mastodon/pull/26485))
- Add `DELETE /api/v1/profile/avatar` and `DELETE /api/v1/profile/header` to the REST API ([danielmbrasil](https://github.com/mastodon/mastodon/pull/25124), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26573))
- Add `ES_PRESET` option to customize numbers of shards and replicas ([Gargron](https://github.com/mastodon/mastodon/pull/26483), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26489))
This can have a value of `single_node_cluster` (default), `small_cluster` (uses one replica) or `large_cluster` (uses one replica and a higher number of shards).
- Add missing `instances` option to `tootctl search deploy` ([tribela](https://github.com/mastodon/mastodon/pull/26461))
- Add `CACHE_BUSTER_HTTP_METHOD` environment variable ([renchap](https://github.com/mastodon/mastodon/pull/26528), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26542))
- Add support for `DB_PASS` when using `DATABASE_URL` ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26295))
- Add `GET /api/v1/instance/languages` to REST API ([danielmbrasil](https://github.com/mastodon/mastodon/pull/24443))
- Add primary key to `preview_cards_statuses` join table ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25243), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26384))
- Add primary key to `preview_cards_statuses` join table ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25243), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26384), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26447))
- Add client-side timeout on resend confirmation button ([Gargron](https://github.com/mastodon/mastodon/pull/26300))
- Add published date and author to news on the explore screen in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26155))
- Add `lang` attribute to various UI components ([c960657](https://github.com/mastodon/mastodon/pull/23869), [c960657](https://github.com/mastodon/mastodon/pull/23891), [c960657](https://github.com/mastodon/mastodon/pull/26111), [c960657](https://github.com/mastodon/mastodon/pull/26149))
@ -43,7 +56,7 @@ The following changelog entries focus on changes visible to users, administrator
- Add unsubscribe link and headers to e-mails ([Gargron](https://github.com/mastodon/mastodon/pull/25378), [c960657](https://github.com/mastodon/mastodon/pull/26085))
- Add logging of websocket send errors ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/25280))
- Add time zone preference ([Gargron](https://github.com/mastodon/mastodon/pull/25342), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26025))
- Add `legal` as report category ([Gargron](https://github.com/mastodon/mastodon/pull/23941), [renchap](https://github.com/mastodon/mastodon/pull/25400))
- Add `legal` as report category ([Gargron](https://github.com/mastodon/mastodon/pull/23941), [renchap](https://github.com/mastodon/mastodon/pull/25400), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26509))
- Add `data-nosnippet` so Google doesn't use trending posts in snippets for `/` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25279))
- Add card with who invited you to join when displaying rules on sign-up ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23475))
- Add missing primary keys to `accounts_tags` and `statuses_tags` ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25210))
@ -80,11 +93,12 @@ The following changelog entries focus on changes visible to users, administrator
### Changed
- **Change hashtags to be displayed separately when they are the last line of a post** ([renchap](https://github.com/mastodon/mastodon/pull/26499))
- **Change reblogs to be excluded from "Posts and replies" tab in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/26302))
- **Change interaction modal in web interface** ([Gargron, ClearlyClaire](https://github.com/mastodon/mastodon/pull/26075), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26269), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26268), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26267))
- **Change interaction modal in web interface** ([Gargron, ClearlyClaire](https://github.com/mastodon/mastodon/pull/26075), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26269), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26268), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26267), [mgmn](https://github.com/mastodon/mastodon/pull/26459))
- **Change design of link previews in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/26136), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26151), [Gargron](https://github.com/mastodon/mastodon/pull/26153), [Gargron](https://github.com/mastodon/mastodon/pull/26250), [Gargron](https://github.com/mastodon/mastodon/pull/26287), [Gargron](https://github.com/mastodon/mastodon/pull/26286), [c960657](https://github.com/mastodon/mastodon/pull/26184))
- **Change "direct message" nomenclature to "private mention" in web UI** ([Gargron](https://github.com/mastodon/mastodon/pull/24248))
- **Change translation feature to cover Content Warnings, poll options and media descriptions** ([c960657](https://github.com/mastodon/mastodon/pull/24175), [S-H-GAMELINKS](https://github.com/mastodon/mastodon/pull/25251), [c960657](https://github.com/mastodon/mastodon/pull/26168))
- **Change translation feature to cover Content Warnings, poll options and media descriptions** ([c960657](https://github.com/mastodon/mastodon/pull/24175), [S-H-GAMELINKS](https://github.com/mastodon/mastodon/pull/25251), [c960657](https://github.com/mastodon/mastodon/pull/26168), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26452))
- **Change account search to match by text when opted-in** ([jsgoldstein](https://github.com/mastodon/mastodon/pull/25599), [Gargron](https://github.com/mastodon/mastodon/pull/26378))
- **Change import feature to be clearer, less error-prone and more reliable** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/21054), [mgmn](https://github.com/mastodon/mastodon/pull/24874))
- **Change local and federated timelines to be in a single “Live feeds” column** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/25641), [Gargron](https://github.com/mastodon/mastodon/pull/25683), [mgmn](https://github.com/mastodon/mastodon/pull/25694), [Plastikmensch](https://github.com/mastodon/mastodon/pull/26247))
@ -97,7 +111,9 @@ The following changelog entries focus on changes visible to users, administrator
- **Change replica support to native Rails adapter** ([krainboltgreene](https://github.com/mastodon/mastodon/pull/25693), [Gargron](https://github.com/mastodon/mastodon/pull/25849), [Gargron](https://github.com/mastodon/mastodon/pull/25874), [Gargron](https://github.com/mastodon/mastodon/pull/25851), [Gargron](https://github.com/mastodon/mastodon/pull/25977), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26074), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26326), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26386))
This is a breaking change, dropping `makara` support, and requiring you to update your database configuration if you are using replicas.
To tell Mastodon to use a read replica, you can either set the `REPLICA_DB_NAME` environment variable (along with `REPLICA_DB_USER`, `REPLICA_DB_PASS`, `REPLICA_DB_HOST`, and `REPLICA_DB_PORT`, if they differ from the primary database), or the `REPLICA_DATABASE_URL` environment variable if your configuration is based on `DATABASE_URL`.
- Change header of hashtag timelines in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26362))
- Change follow recommendation materialized view to be faster in most cases ([renchap, ClearlyClaire](https://github.com/mastodon/mastodon/pull/26545))
- Change `robots.txt` to block GPTBot ([Foritus](https://github.com/mastodon/mastodon/pull/26396))
- Change header of hashtag timelines in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/26362), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26416))
- Change streaming `/metrics` to include additional metrics ([ThisIsMissEm](https://github.com/mastodon/mastodon/pull/26299))
- Change indexing frequency from 5 minutes to 1 minute, add locks to schedulers ([Gargron](https://github.com/mastodon/mastodon/pull/26304))
- Change column link to add a better keyboard focus indicator ([teeerevor](https://github.com/mastodon/mastodon/pull/26278))
@ -114,7 +130,7 @@ The following changelog entries focus on changes visible to users, administrator
- Change header backgrounds to use fewer different colors in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25577))
- Change files to be deleted in batches instead of one-by-one ([Gargron](https://github.com/mastodon/mastodon/pull/23302), [S-H-GAMELINKS](https://github.com/mastodon/mastodon/pull/25586), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25587))
- Change emoji picker icon ([iparr](https://github.com/mastodon/mastodon/pull/25479))
- Change edit profile page ([Gargron](https://github.com/mastodon/mastodon/pull/25413))
- Change edit profile page ([Gargron](https://github.com/mastodon/mastodon/pull/25413), [c960657](https://github.com/mastodon/mastodon/pull/26538))
- Change "bot" label to "automated" ([Gargron](https://github.com/mastodon/mastodon/pull/25356))
- Change design of dropdowns in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25107))
- Change wording of “Content cache retention period” setting to highlight destructive implications ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/23261))
@ -172,6 +188,9 @@ The following changelog entries focus on changes visible to users, administrator
- **Fix being unable to load past a full page of filtered posts in Home timeline** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/24930))
- **Fix log-in flow when involving both OAuth and external authentication** ([CSDUMMI](https://github.com/mastodon/mastodon/pull/24073))
- **Fix broken links in account gallery** ([c960657](https://github.com/mastodon/mastodon/pull/24218))
- **Fix blocking subdomains of an already-blocked domain** ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26392))
- Fix uploading of video files for which `ffprobe` reports `0/0` average framerate ([NicolaiSoeborg](https://github.com/mastodon/mastodon/pull/26500))
- Fix cached posts including stale stats ([ClearlyClaire](https://github.com/mastodon/mastodon/pull/26409))
- Fix adding column with default value taking longer on Postgres >= 11 ([Gargron](https://github.com/mastodon/mastodon/pull/26375))
- Fix light theme select option for hashtags ([teeerevor](https://github.com/mastodon/mastodon/pull/26311))
- Fix AVIF attachments ([c960657](https://github.com/mastodon/mastodon/pull/26264))
@ -189,7 +208,7 @@ The following changelog entries focus on changes visible to users, administrator
- Fix for "follows you" indicator in light web UI not readable ([vmstan](https://github.com/mastodon/mastodon/pull/25993))
- Fix incorrect line break between icon and number of reposts & favourites ([edent](https://github.com/mastodon/mastodon/pull/26004))
- Fix sounds not being loaded from assets host ([Signez](https://github.com/mastodon/mastodon/pull/25931))
- Fix buttons showing inconsistent styles ([teeerevor](https://github.com/mastodon/mastodon/pull/25903), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25965), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26341))
- Fix buttons showing inconsistent styles ([teeerevor](https://github.com/mastodon/mastodon/pull/25903), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25965), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26341), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/26482))
- Fix trend calculation working on too many items at a time ([Gargron](https://github.com/mastodon/mastodon/pull/25835))
- Fix dropdowns being disabled for logged out users in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25714), [ClearlyClaire](https://github.com/mastodon/mastodon/pull/25964))
- Fix explore page being inaccessible when opted-out of trends in web UI ([Gargron](https://github.com/mastodon/mastodon/pull/25716))

View File

@ -59,7 +59,7 @@ gem 'httplog', '~> 1.6.2'
gem 'idn-ruby', require: 'idn'
gem 'kaminari', '~> 1.2'
gem 'link_header', '~> 0.0'
gem 'mime-types', '~> 3.4.1', require: 'mime/types/columnar'
gem 'mime-types', '~> 3.5.0', require: 'mime/types/columnar'
gem 'nokogiri', '~> 1.15'
gem 'nsa', github: 'jhawthorn/nsa', ref: 'e020fcc3a54d993ab45b7194d89ab720296c111b'
gem 'oj', '~> 3.14'
@ -186,9 +186,7 @@ group :development, :test do
gem 'ruby-prof', require: false
gem 'stackprof', require: false
gem 'test-prof'
end
group :development, :test do
# RSpec runner for rails
gem 'rspec-rails', '~> 6.0'
end

View File

@ -39,47 +39,47 @@ GIT
GEM
remote: https://rubygems.org/
specs:
actioncable (7.0.6)
actionpack (= 7.0.6)
activesupport (= 7.0.6)
actioncable (7.0.7)
actionpack (= 7.0.7)
activesupport (= 7.0.7)
nio4r (~> 2.0)
websocket-driver (>= 0.6.1)
actionmailbox (7.0.6)
actionpack (= 7.0.6)
activejob (= 7.0.6)
activerecord (= 7.0.6)
activestorage (= 7.0.6)
activesupport (= 7.0.6)
actionmailbox (7.0.7)
actionpack (= 7.0.7)
activejob (= 7.0.7)
activerecord (= 7.0.7)
activestorage (= 7.0.7)
activesupport (= 7.0.7)
mail (>= 2.7.1)
net-imap
net-pop
net-smtp
actionmailer (7.0.6)
actionpack (= 7.0.6)
actionview (= 7.0.6)
activejob (= 7.0.6)
activesupport (= 7.0.6)
actionmailer (7.0.7)
actionpack (= 7.0.7)
actionview (= 7.0.7)
activejob (= 7.0.7)
activesupport (= 7.0.7)
mail (~> 2.5, >= 2.5.4)
net-imap
net-pop
net-smtp
rails-dom-testing (~> 2.0)
actionpack (7.0.6)
actionview (= 7.0.6)
activesupport (= 7.0.6)
actionpack (7.0.7)
actionview (= 7.0.7)
activesupport (= 7.0.7)
rack (~> 2.0, >= 2.2.4)
rack-test (>= 0.6.3)
rails-dom-testing (~> 2.0)
rails-html-sanitizer (~> 1.0, >= 1.2.0)
actiontext (7.0.6)
actionpack (= 7.0.6)
activerecord (= 7.0.6)
activestorage (= 7.0.6)
activesupport (= 7.0.6)
actiontext (7.0.7)
actionpack (= 7.0.7)
activerecord (= 7.0.7)
activestorage (= 7.0.7)
activesupport (= 7.0.7)
globalid (>= 0.6.0)
nokogiri (>= 1.8.5)
actionview (7.0.6)
activesupport (= 7.0.6)
actionview (7.0.7)
activesupport (= 7.0.7)
builder (~> 3.1)
erubi (~> 1.4)
rails-dom-testing (~> 2.0)
@ -89,22 +89,22 @@ GEM
activemodel (>= 4.1, < 7.1)
case_transform (>= 0.2)
jsonapi-renderer (>= 0.1.1.beta1, < 0.3)
activejob (7.0.6)
activesupport (= 7.0.6)
activejob (7.0.7)
activesupport (= 7.0.7)
globalid (>= 0.3.6)
activemodel (7.0.6)
activesupport (= 7.0.6)
activerecord (7.0.6)
activemodel (= 7.0.6)
activesupport (= 7.0.6)
activestorage (7.0.6)
actionpack (= 7.0.6)
activejob (= 7.0.6)
activerecord (= 7.0.6)
activesupport (= 7.0.6)
activemodel (7.0.7)
activesupport (= 7.0.7)
activerecord (7.0.7)
activemodel (= 7.0.7)
activesupport (= 7.0.7)
activestorage (7.0.7)
actionpack (= 7.0.7)
activejob (= 7.0.7)
activerecord (= 7.0.7)
activesupport (= 7.0.7)
marcel (~> 1.0)
mini_mime (>= 1.1.0)
activesupport (7.0.6)
activesupport (7.0.7)
concurrent-ruby (~> 1.0, >= 1.0.2)
i18n (>= 1.6, < 2)
minitest (>= 5.1)
@ -125,7 +125,7 @@ GEM
awrence (1.2.1)
aws-eventstream (1.2.0)
aws-partitions (1.793.0)
aws-sdk-core (3.180.0)
aws-sdk-core (3.180.3)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.651.0)
aws-sigv4 (~> 1.5)
@ -133,7 +133,7 @@ GEM
aws-sdk-kms (1.71.0)
aws-sdk-core (~> 3, >= 3.177.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.132.0)
aws-sdk-s3 (1.132.1)
aws-sdk-core (~> 3, >= 3.179.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.6)
@ -269,7 +269,7 @@ GEM
tzinfo
excon (0.100.0)
fabrication (2.30.0)
faker (3.2.0)
faker (3.2.1)
i18n (>= 1.8.11, < 2)
faraday (1.10.3)
faraday-em_http (~> 1.0)
@ -332,7 +332,7 @@ GEM
activesupport (>= 5.1)
haml (>= 4.0.6)
railties (>= 5.1)
haml_lint (0.49.2)
haml_lint (0.49.3)
haml (>= 4.0, < 6.2)
parallel (~> 1.10)
rainbow
@ -451,10 +451,10 @@ GEM
hashie (~> 5.0)
memory_profiler (1.0.1)
method_source (1.0.0)
mime-types (3.4.1)
mime-types (3.5.0)
mime-types-data (~> 3.2015)
mime-types-data (3.2023.0218.1)
mini_mime (1.1.2)
mime-types-data (3.2023.0808)
mini_mime (1.1.5)
mini_portile2 (2.8.4)
minitest (5.19.0)
msgpack (1.7.1)
@ -464,7 +464,7 @@ GEM
uri
net-http-persistent (4.0.2)
connection_pool (~> 2.2)
net-imap (0.3.6)
net-imap (0.3.7)
date
net-protocol
net-ldap (0.18.0)
@ -478,7 +478,7 @@ GEM
net-protocol
net-ssh (7.1.0)
nio4r (2.5.9)
nokogiri (1.15.3)
nokogiri (1.15.4)
mini_portile2 (~> 2.8.2)
racc (~> 1.4)
oj (3.15.0)
@ -532,7 +532,7 @@ GEM
premailer (~> 1.7, >= 1.7.9)
private_address_check (0.5.0)
public_suffix (5.0.3)
puma (6.3.0)
puma (6.3.1)
nio4r (~> 2.0)
pundit (2.3.0)
activesupport (>= 3.0.0)
@ -555,20 +555,20 @@ GEM
rack
rack-test (2.1.0)
rack (>= 1.3)
rails (7.0.6)
actioncable (= 7.0.6)
actionmailbox (= 7.0.6)
actionmailer (= 7.0.6)
actionpack (= 7.0.6)
actiontext (= 7.0.6)
actionview (= 7.0.6)
activejob (= 7.0.6)
activemodel (= 7.0.6)
activerecord (= 7.0.6)
activestorage (= 7.0.6)
activesupport (= 7.0.6)
rails (7.0.7)
actioncable (= 7.0.7)
actionmailbox (= 7.0.7)
actionmailer (= 7.0.7)
actionpack (= 7.0.7)
actiontext (= 7.0.7)
actionview (= 7.0.7)
activejob (= 7.0.7)
activemodel (= 7.0.7)
activerecord (= 7.0.7)
activestorage (= 7.0.7)
activesupport (= 7.0.7)
bundler (>= 1.15.0)
railties (= 7.0.6)
railties (= 7.0.7)
rails-controller-testing (1.0.5)
actionpack (>= 5.0.1.rc1)
actionview (>= 5.0.1.rc1)
@ -583,9 +583,9 @@ GEM
rails-i18n (7.0.7)
i18n (>= 0.7, < 2)
railties (>= 6.0.0, < 8)
railties (7.0.6)
actionpack (= 7.0.6)
activesupport (= 7.0.6)
railties (7.0.7)
actionpack (= 7.0.7)
activesupport (= 7.0.7)
method_source
rake (>= 12.2)
thor (~> 1.0)
@ -801,7 +801,7 @@ GEM
railties (>= 5.2)
semantic_range (>= 2.3.0)
websocket (1.2.9)
websocket-driver (0.7.5)
websocket-driver (0.7.6)
websocket-extensions (>= 0.1.0)
websocket-extensions (0.1.5)
wisper (2.0.1)
@ -874,7 +874,7 @@ DEPENDENCIES
mario-redis-lock (~> 1.2)
md-paperclip-azure (~> 2.2)
memory_profiler
mime-types (~> 3.4.1)
mime-types (~> 3.5.0)
net-http (~> 0.3.2)
net-ldap (~> 0.18)
nokogiri (~> 1.15)

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class AccountsIndex < Chewy::Index
settings index: { refresh_interval: '30s' }, analysis: {
settings index: index_preset(refresh_interval: '30s'), analysis: {
filter: {
english_stop: {
type: 'stop',

View File

@ -1,12 +1,12 @@
# frozen_string_literal: true
class InstancesIndex < Chewy::Index
settings index: { refresh_interval: '30s' }
settings index: index_preset(refresh_interval: '30s')
index_scope ::Instance.searchable
root date_detection: false do
field :domain, type: 'text', index_prefixes: { min_chars: 1 }
field :domain, type: 'text', index_prefixes: { min_chars: 1, max_chars: 5 }
field :accounts_count, type: 'long'
end
end

View File

@ -3,7 +3,7 @@
class StatusesIndex < Chewy::Index
include FormattingHelper
settings index: { refresh_interval: '30s' }, analysis: {
settings index: index_preset(refresh_interval: '30s', number_of_shards: 5), analysis: {
filter: {
english_stop: {
type: 'stop',

View File

@ -1,7 +1,7 @@
# frozen_string_literal: true
class TagsIndex < Chewy::Index
settings index: { refresh_interval: '30s' }, analysis: {
settings index: index_preset(refresh_interval: '30s'), analysis: {
analyzer: {
content: {
tokenizer: 'keyword',

View File

@ -40,7 +40,7 @@ module Admin
end
# Allow transparently upgrading a domain block
if existing_domain_block.present?
if existing_domain_block.present? && existing_domain_block.domain == TagManager.instance.normalize_domain(@domain_block.domain.strip)
@domain_block = existing_domain_block
@domain_block.assign_attributes(resource_params)
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class Api::V1::Profile::AvatarsController < Api::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
before_action :require_user!
def destroy
@account = current_account
UpdateAccountService.new.call(@account, { avatar: nil }, raise_error: true)
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
render json: @account, serializer: REST::CredentialAccountSerializer
end
end

View File

@ -0,0 +1,13 @@
# frozen_string_literal: true
class Api::V1::Profile::HeadersController < Api::BaseController
before_action -> { doorkeeper_authorize! :write, :'write:accounts' }
before_action :require_user!
def destroy
@account = current_account
UpdateAccountService.new.call(@account, { header: nil }, raise_error: true)
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
render json: @account, serializer: REST::CredentialAccountSerializer
end
end

View File

@ -0,0 +1,27 @@
# frozen_string_literal: true
class Settings::PrivacyController < Settings::BaseController
before_action :set_account
def show; end
def update
if UpdateAccountService.new.call(@account, account_params.except(:settings))
current_user.update!(settings_attributes: account_params[:settings])
ActivityPub::UpdateDistributionWorker.perform_async(@account.id)
redirect_to settings_privacy_path, notice: I18n.t('generic.changes_saved_msg')
else
render :show
end
end
private
def account_params
params.require(:account).permit(:discoverable, :unlocked, :show_collections, settings: UserSettings.keys)
end
def set_account
@account = current_account
end
end

View File

@ -20,7 +20,7 @@ class Settings::ProfilesController < Settings::BaseController
private
def account_params
params.require(:account).permit(:display_name, :note, :avatar, :header, :locked, :bot, :discoverable, :hide_collections, fields_attributes: [:name, :value])
params.require(:account).permit(:display_name, :note, :avatar, :header, :bot, fields_attributes: [:name, :value])
end
def set_account

View File

@ -20,6 +20,7 @@ module ContextHelper
focal_point: { 'toot' => 'http://joinmastodon.org/ns#', 'focalPoint' => { '@container' => '@list', '@id' => 'toot:focalPoint' } },
blurhash: { 'toot' => 'http://joinmastodon.org/ns#', 'blurhash' => 'toot:blurhash' },
discoverable: { 'toot' => 'http://joinmastodon.org/ns#', 'discoverable' => 'toot:discoverable' },
indexable: { 'toot' => 'http://joinmastodon.org/ns#', 'indexable' => 'toot:indexable' },
voters_count: { 'toot' => 'http://joinmastodon.org/ns#', 'votersCount' => 'toot:votersCount' },
olm: {
'toot' => 'http://joinmastodon.org/ns#', 'Device' => 'toot:Device', 'Ed25519Signature' => 'toot:Ed25519Signature', 'Ed25519Key' => 'toot:Ed25519Key', 'Curve25519Key' => 'toot:Curve25519Key', 'EncryptedMessage' => 'toot:EncryptedMessage', 'publicKeyBase64' => 'toot:publicKeyBase64', 'deviceId' => 'toot:deviceId',

View File

@ -76,7 +76,10 @@ export function normalizeStatus(status, normalOldStatus) {
normalStatus.spoilerHtml = normalOldStatus.get('spoilerHtml');
normalStatus.spoiler_text = normalOldStatus.get('spoiler_text');
normalStatus.hidden = normalOldStatus.get('hidden');
normalStatus.translation = normalOldStatus.get('translation');
if (normalOldStatus.get('translation')) {
normalStatus.translation = normalOldStatus.get('translation');
}
} else {
// If the status has a CW but no contents, treat the CW as if it were the
// status' contents, to avoid having a CW toggle with seemingly no effect.

View File

@ -0,0 +1,184 @@
import { fromJS } from 'immutable';
import type { StatusLike } from '../hashtag_bar';
import { computeHashtagBarForStatus } from '../hashtag_bar';
function createStatus(
content: string,
hashtags: string[],
hasMedia = false,
spoilerText?: string,
) {
return fromJS({
tags: hashtags.map((name) => ({ name })),
contentHtml: content,
media_attachments: hasMedia ? ['fakeMedia'] : [],
spoiler_text: spoilerText,
}) as unknown as StatusLike; // need to force the type here, as it is not properly defined
}
describe('computeHashtagBarForStatus', () => {
it('does nothing when there are no tags', () => {
const status = createStatus('<p>Simple text</p>', []);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual([]);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p>Simple text</p>"`,
);
});
it('displays out of band hashtags in the bar', () => {
const status = createStatus(
'<p>Simple text <a href="test">#hashtag</a></p>',
['hashtag', 'test'],
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual(['test']);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p>Simple text <a href="test">#hashtag</a></p>"`,
);
});
it('extract tags from the last line', () => {
const status = createStatus(
'<p>Simple text</p><p><a href="test">#hashtag</a></p>',
['hashtag'],
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual(['hashtag']);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p>Simple text</p>"`,
);
});
it('does not include tags from content', () => {
const status = createStatus(
'<p>Simple text with a <a href="test">#hashtag</a></p><p><a href="test">#hashtag</a></p>',
['hashtag'],
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual([]);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p>Simple text with a <a href="test">#hashtag</a></p>"`,
);
});
it('works with one line status and hashtags', () => {
const status = createStatus(
'<p><a href="test">#test</a>. And another <a href="test">#hashtag</a></p>',
['hashtag', 'test'],
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual([]);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p><a href="test">#test</a>. And another <a href="test">#hashtag</a></p>"`,
);
});
it('de-duplicate accentuated characters with case differences', () => {
const status = createStatus(
'<p>Text</p><p><a href="test">#éaa</a> <a href="test">#Éaa</a></p>',
['éaa'],
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual(['Éaa']);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p>Text</p>"`,
);
});
it('does not display in bar a hashtag in content with a case difference', () => {
const status = createStatus(
'<p>Text <a href="test">#Éaa</a></p><p><a href="test">#éaa</a></p>',
['éaa'],
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual([]);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p>Text <a href="test">#Éaa</a></p>"`,
);
});
it('does not modify a status with a line of hashtags only', () => {
const status = createStatus(
'<p><a href="test">#test</a> <a href="test">#hashtag</a></p>',
['test', 'hashtag'],
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual([]);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p><a href="test">#test</a> <a href="test">#hashtag</a></p>"`,
);
});
it('puts the hashtags in the bar if a status content has hashtags in the only line and has a media', () => {
const status = createStatus(
'<p>This is my content! <a href="test">#hashtag</a></p>',
['hashtag'],
true,
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual([]);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p>This is my content! <a href="test">#hashtag</a></p>"`,
);
});
it('puts the hashtags in the bar if a status content is only hashtags and has a media', () => {
const status = createStatus(
'<p><a href="test">#test</a> <a href="test">#hashtag</a></p>',
['test', 'hashtag'],
true,
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual(['test', 'hashtag']);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(`""`);
});
it('does not use the hashtag bar if the status content is only hashtags, has a CW and a media', () => {
const status = createStatus(
'<p><a href="test">#test</a> <a href="test">#hashtag</a></p>',
['test', 'hashtag'],
true,
'My CW text',
);
const { hashtagsInBar, statusContentProps } =
computeHashtagBarForStatus(status);
expect(hashtagsInBar).toEqual([]);
expect(statusContentProps.statusContent).toMatchInlineSnapshot(
`"<p><a href="test">#test</a> <a href="test">#hashtag</a></p>"`,
);
});
});

View File

@ -8,6 +8,7 @@ import classNames from 'classnames';
import api from 'mastodon/api';
const messages = defineMessages({
legal: { id: 'report.categories.legal', defaultMessage: 'Legal' },
other: { id: 'report.categories.other', defaultMessage: 'Other' },
spam: { id: 'report.categories.spam', defaultMessage: 'Spam' },
violation: { id: 'report.categories.violation', defaultMessage: 'Content violates one or more server rules' },
@ -150,6 +151,7 @@ class ReportReasonSelector extends PureComponent {
return (
<div className='report-reason-selector'>
<Category id='other' text={intl.formatMessage(messages.other)} selected={category === 'other'} onSelect={this.handleSelect} disabled={disabled} />
<Category id='legal' text={intl.formatMessage(messages.legal)} selected={category === 'legal'} onSelect={this.handleSelect} disabled={disabled} />
<Category id='spam' text={intl.formatMessage(messages.spam)} selected={category === 'spam'} onSelect={this.handleSelect} disabled={disabled} />
<Category id='violation' text={intl.formatMessage(messages.violation)} selected={category === 'violation'} onSelect={this.handleSelect} disabled={disabled}>
{rules.map(rule => <Rule key={rule.id} id={rule.id} text={rule.text} selected={rule_ids.includes(rule.id)} onToggle={this.handleToggle} disabled={disabled} />)}

View File

@ -0,0 +1,222 @@
import { useState, useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import { Link } from 'react-router-dom';
import type { List, Record } from 'immutable';
import { groupBy, minBy } from 'lodash';
import { getStatusContent } from './status_content';
// About two lines on desktop
const VISIBLE_HASHTAGS = 7;
// Those types are not correct, they need to be replaced once this part of the state is typed
export type TagLike = Record<{ name: string }>;
export type StatusLike = Record<{
tags: List<TagLike>;
contentHTML: string;
media_attachments: List<unknown>;
spoiler_text?: string;
}>;
function normalizeHashtag(hashtag: string) {
if (hashtag && hashtag.startsWith('#')) return hashtag.slice(1);
else return hashtag;
}
function isNodeLinkHashtag(element: Node): element is HTMLLinkElement {
return (
element instanceof HTMLAnchorElement &&
// it may be a <a> starting with a hashtag
(element.textContent?.[0] === '#' ||
// or a #<a>
element.previousSibling?.textContent?.[
element.previousSibling.textContent.length - 1
] === '#')
);
}
/**
* Removes duplicates from an hashtag list, case-insensitive, keeping only the best one
* "Best" here is defined by the one with the more casing difference (ie, the most camel-cased one)
* @param hashtags The list of hashtags
* @returns The input hashtags, but with only 1 occurence of each (case-insensitive)
*/
function uniqueHashtagsWithCaseHandling(hashtags: string[]) {
const groups = groupBy(hashtags, (tag) =>
tag.normalize('NFKD').toLowerCase(),
);
return Object.values(groups).map((tags) => {
if (tags.length === 1) return tags[0];
// The best match is the one where we have the less difference between upper and lower case letter count
const best = minBy(tags, (tag) => {
const upperCase = Array.from(tag).reduce(
(acc, char) => (acc += char.toUpperCase() === char ? 1 : 0),
0,
);
const lowerCase = tag.length - upperCase;
return Math.abs(lowerCase - upperCase);
});
return best ?? tags[0];
});
}
// Create the collator once, this is much more efficient
const collator = new Intl.Collator(undefined, { sensitivity: 'accent' });
function localeAwareInclude(collection: string[], value: string) {
return collection.find((item) => collator.compare(item, value) === 0);
}
// We use an intermediate function here to make it easier to test
export function computeHashtagBarForStatus(status: StatusLike): {
statusContentProps: { statusContent: string };
hashtagsInBar: string[];
} {
let statusContent = getStatusContent(status);
const tagNames = status
.get('tags')
.map((tag) => tag.get('name'))
.toJS();
// this is returned if we stop the processing early, it does not change what is displayed
const defaultResult = {
statusContentProps: { statusContent },
hashtagsInBar: [],
};
// return early if this status does not have any tags
if (tagNames.length === 0) return defaultResult;
const template = document.createElement('template');
template.innerHTML = statusContent.trim();
const lastChild = template.content.lastChild;
if (!lastChild) return defaultResult;
template.content.removeChild(lastChild);
const contentWithoutLastLine = template;
// First, try to parse
const contentHashtags = Array.from(
contentWithoutLastLine.content.querySelectorAll<HTMLLinkElement>('a[href]'),
).reduce<string[]>((result, link) => {
if (isNodeLinkHashtag(link)) {
if (link.textContent) result.push(normalizeHashtag(link.textContent));
}
return result;
}, []);
// Now we parse the last line, and try to see if it only contains hashtags
const lastLineHashtags: string[] = [];
// try to see if the last line is only hashtags
let onlyHashtags = true;
Array.from(lastChild.childNodes).forEach((node) => {
if (isNodeLinkHashtag(node) && node.textContent) {
const normalized = normalizeHashtag(node.textContent);
if (!localeAwareInclude(tagNames, normalized)) {
// stop here, this is not a real hashtag, so consider it as text
onlyHashtags = false;
return;
}
if (!localeAwareInclude(contentHashtags, normalized))
// only add it if it does not appear in the rest of the content
lastLineHashtags.push(normalized);
} else if (node.nodeType !== Node.TEXT_NODE || node.nodeValue?.trim()) {
// not a space
onlyHashtags = false;
}
});
const hashtagsInBar = tagNames.filter(
(tag) =>
// the tag does not appear at all in the status content, it is an out-of-band tag
!localeAwareInclude(contentHashtags, tag) &&
!localeAwareInclude(lastLineHashtags, tag),
);
const isOnlyOneLine = contentWithoutLastLine.content.childElementCount === 0;
const hasMedia = status.get('media_attachments').size > 0;
const hasSpoiler = !!status.get('spoiler_text');
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition -- due to https://github.com/microsoft/TypeScript/issues/9998
if (onlyHashtags && ((hasMedia && !hasSpoiler) || !isOnlyOneLine)) {
// if the last line only contains hashtags, and we either:
// - have other content in the status
// - dont have other content, but a media and no CW. If it has a CW, then we do not remove the content to avoid having an empty content behind the CW button
statusContent = contentWithoutLastLine.innerHTML;
// and add the tags to the bar
hashtagsInBar.push(...lastLineHashtags);
}
return {
statusContentProps: { statusContent },
hashtagsInBar: uniqueHashtagsWithCaseHandling(hashtagsInBar),
};
}
/**
* This function will process a status to, at the same time (avoiding parsing it twice):
* - build the HashtagBar for this status
* - remove the last-line hashtags from the status content
* @param status The status to process
* @returns Props to be passed to the <StatusContent> component, and the hashtagBar to render
*/
export function getHashtagBarForStatus(status: StatusLike) {
const { statusContentProps, hashtagsInBar } =
computeHashtagBarForStatus(status);
return {
statusContentProps,
hashtagBar: <HashtagBar hashtags={hashtagsInBar} />,
};
}
const HashtagBar: React.FC<{
hashtags: string[];
}> = ({ hashtags }) => {
const [expanded, setExpanded] = useState(false);
const handleClick = useCallback(() => {
setExpanded(true);
}, []);
if (hashtags.length === 0) {
return null;
}
const revealedHashtags = expanded
? hashtags
: hashtags.slice(0, VISIBLE_HASHTAGS - 1);
return (
<div className='hashtag-bar'>
{revealedHashtags.map((hashtag) => (
<Link key={hashtag} to={`/tags/${hashtag}`}>
#{hashtag}
</Link>
))}
{!expanded && hashtags.length > VISIBLE_HASHTAGS && (
<button className='link-button' onClick={handleClick}>
<FormattedMessage
id='hashtags.and_other'
defaultMessage='…and {count, plural, other {# more}}'
values={{ count: hashtags.length - VISIBLE_HASHTAGS }}
/>
</button>
)}
</div>
);
};

View File

@ -22,6 +22,7 @@ import { displayMedia } from '../initial_state';
import { Avatar } from './avatar';
import { AvatarOverlay } from './avatar_overlay';
import { DisplayName } from './display_name';
import { getHashtagBarForStatus } from './hashtag_bar';
import { RelativeTimestamp } from './relative_timestamp';
import StatusActionBar from './status_action_bar';
import StatusContent from './status_content';
@ -544,6 +545,8 @@ class Status extends ImmutablePureComponent {
const visibilityIcon = visibilityIconInfo[status.get('visibility')];
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
return (
<HotKeys handlers={handlers}>
<div className={classNames('status__wrapper', `status__wrapper-${status.get('visibility')}`, { 'status__wrapper-reply': !!status.get('in_reply_to_id'), unread, focusable: !this.props.muted })} tabIndex={this.props.muted ? null : 0} data-featured={featured ? 'true' : null} aria-label={textForScreenReader(intl, status, rebloggedByText)} ref={this.handleRef}>
@ -576,10 +579,13 @@ class Status extends ImmutablePureComponent {
onTranslate={this.handleTranslate}
collapsible
onCollapsedToggle={this.handleCollapsedToggle}
{...statusContentProps}
/>
{media}
{hashtagBar}
<StatusActionBar scrollKey={scrollKey} status={status} account={account} onFilter={matchedFilters ? this.handleFilterClick : null} {...other} />
</div>
</div>

View File

@ -15,6 +15,15 @@ import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_s
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
/**
*
* @param {any} status
* @returns {string}
*/
export function getStatusContent(status) {
return status.getIn(['translation', 'contentHtml']) || status.get('contentHtml');
}
class TranslateButton extends PureComponent {
static propTypes = {
@ -65,6 +74,7 @@ class StatusContent extends PureComponent {
static propTypes = {
status: ImmutablePropTypes.map.isRequired,
statusContent: PropTypes.string,
expanded: PropTypes.bool,
onExpandedToggle: PropTypes.func,
onTranslate: PropTypes.func,
@ -225,7 +235,7 @@ class StatusContent extends PureComponent {
};
render () {
const { status, intl } = this.props;
const { status, intl, statusContent } = this.props;
const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden;
const renderReadMore = this.props.onClick && status.get('collapsed');
@ -233,7 +243,7 @@ class StatusContent extends PureComponent {
const targetLanguages = this.props.languages?.get(status.get('language') || 'und');
const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale);
const content = { __html: status.getIn(['translation', 'contentHtml']) || status.get('contentHtml') };
const content = { __html: statusContent ?? getStatusContent(status) };
const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') };
const language = status.getIn(['translation', 'language']) || status.get('language');
const classNames = classnames('status__content', {

View File

@ -265,9 +265,9 @@ class Header extends ImmutablePureComponent {
if (signedIn && !account.get('relationship')) { // Wait until the relationship is loaded
actionBtn = '';
} else if (account.getIn(['relationship', 'requested'])) {
actionBtn = <Button className={classNames({ 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
actionBtn = <Button text={intl.formatMessage(messages.cancel_follow_request)} title={intl.formatMessage(messages.requested)} onClick={this.props.onFollow} />;
} else if (!account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames({ 'button--destructive': account.getIn(['relationship', 'following']), 'button--with-bell': bellBtn !== '' })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={signedIn ? this.props.onFollow : this.props.onInteractionModal} />;
actionBtn = <Button disabled={account.getIn(['relationship', 'blocked_by'])} className={classNames({ 'button--destructive': account.getIn(['relationship', 'following']) })} text={intl.formatMessage(account.getIn(['relationship', 'following']) ? messages.unfollow : messages.follow)} onClick={signedIn ? this.props.onFollow : this.props.onInteractionModal} />;
} else if (account.getIn(['relationship', 'blocking'])) {
actionBtn = <Button text={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.props.onBlock} />;
}

View File

@ -203,7 +203,7 @@ class HashtagTimeline extends PureComponent {
</ColumnHeader>
<StatusListContainer
prepend={<HashtagHeader tag={tag} disabled={!signedIn} onClick={this.handleFollow} />}
prepend={pinned ? null : <HashtagHeader tag={tag} disabled={!signedIn} onClick={this.handleFollow} />}
alwaysPrepend
trackScroll={!pinned}
scrollKey={`hashtag_timeline-${columnId}`}

View File

@ -21,12 +21,16 @@ const messages = defineMessages({
const mapStateToProps = (state, { accountId }) => ({
displayNameHtml: state.getIn(['accounts', accountId, 'display_name_html']),
signupUrl: state.getIn(['server', 'server', 'registrations', 'url'], null) || '/auth/sign_up',
});
const mapDispatchToProps = (dispatch) => ({
onSignupClick() {
dispatch(closeModal());
dispatch(openModal('CLOSED_REGISTRATIONS'));
dispatch(closeModal({
modalType: undefined,
ignoreFocus: false,
}));
dispatch(openModal({ modalType: 'CLOSED_REGISTRATIONS' }));
},
});
@ -294,6 +298,7 @@ class InteractionModal extends React.PureComponent {
url: PropTypes.string,
type: PropTypes.oneOf(['reply', 'reblog', 'favourite', 'follow']),
onSignupClick: PropTypes.func.isRequired,
signupUrl: PropTypes.string.isRequired,
};
handleSignupClick = () => {
@ -301,7 +306,7 @@ class InteractionModal extends React.PureComponent {
};
render () {
const { url, type, displayNameHtml } = this.props;
const { url, type, displayNameHtml, signupUrl } = this.props;
const name = <bdi dangerouslySetInnerHTML={{ __html: displayNameHtml }} />;
@ -340,7 +345,7 @@ class InteractionModal extends React.PureComponent {
);
} else if (registrationsOpen) {
signupButton = (
<a href='/auth/sign_up' className='link-button'>
<a href={signupUrl} className='link-button'>
<FormattedMessage id='sign_in_banner.create_account' defaultMessage='Create account' />
</a>
);

View File

@ -10,6 +10,7 @@ import ImmutablePureComponent from 'react-immutable-pure-component';
import { AnimatedNumber } from 'mastodon/components/animated_number';
import EditedTimestamp from 'mastodon/components/edited_timestamp';
import { getHashtagBarForStatus } from 'mastodon/components/hashtag_bar';
import { Icon } from 'mastodon/components/icon';
import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder';
@ -291,6 +292,8 @@ class DetailedStatus extends ImmutablePureComponent {
);
}
const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status);
return (
<div style={outerStyle}>
<div ref={this.setRef} className={classNames('detailed-status', { compact })}>
@ -310,10 +313,13 @@ class DetailedStatus extends ImmutablePureComponent {
expanded={!status.get('hidden')}
onExpandedToggle={this.handleExpandedToggle}
onTranslate={this.handleTranslate}
{...statusContentProps}
/>
{media}
{hashtagBar}
<div className='detailed-status__meta'>
<a className='detailed-status__datetime' href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} target='_blank' rel='noopener noreferrer'>
<FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />

View File

@ -13,7 +13,7 @@
"about.rules": "قواعد الخادم",
"account.account_note_header": "مُلاحظة",
"account.add_or_remove_from_list": "الإضافة أو الإزالة من القائمة",
"account.badges.bot": "بوت",
"account.badges.bot": "آلي",
"account.badges.group": "فريق",
"account.block": "احجب @{name}",
"account.block_domain": "حظر اسم النِّطاق {domain}",
@ -181,6 +181,7 @@
"confirmations.mute.explanation": "هذا سيخفي المنشورات عنهم وتلك المشار فيها إليهم، لكنه سيسمح لهم برؤية منشوراتك ومتابعتك.",
"confirmations.mute.message": "هل أنت متأكد أنك تريد كتم {name} ؟",
"confirmations.redraft.confirm": "إزالة وإعادة الصياغة",
"confirmations.redraft.message": "هل أنت متأكد من أنك تريد حذف هذا المنشور و إعادة صياغته؟ سوف تفقد جميع الإعجابات و الترقيات أما الردود المتصلة به فستُصبِح يتيمة.",
"confirmations.reply.confirm": "رد",
"confirmations.reply.message": "الرد في الحين سوف يُعيد كتابة الرسالة التي أنت بصدد كتابتها. متأكد من أنك تريد المواصلة؟",
"confirmations.unfollow.confirm": "إلغاء المتابعة",
@ -200,6 +201,7 @@
"dismissable_banner.community_timeline": "هذه هي أحدث المشاركات العامة من الأشخاص الذين تُستضاف حساباتهم على {domain}.",
"dismissable_banner.dismiss": "رفض",
"dismissable_banner.explore_links": "هذه القصص الإخبارية يتحدث عنها حاليًا أشخاص على هذا الخادم وكذا على الخوادم الأخرى للشبكة اللامركزية.",
"dismissable_banner.explore_statuses": "هذه هي المنشورات الرائجة على الشبكات الاجتماعيّة اليوم. تظهر المنشورات التي أعيد مشاركتها وحازت على مفضّلات أكثر في مرتبة عليا.",
"dismissable_banner.explore_tags": "هذه الوسوم تكتسب جذب اهتمام الناس حاليًا على هذا الخادم وكذا على الخوادم الأخرى للشبكة اللامركزية.",
"dismissable_banner.public_timeline": "هذه هي أحدث المنشورات العامة من الناس على الشبكة الاجتماعية التي يتبعها الناس على {domain}.",
"embed.instructions": "يمكنكم إدماج هذا المنشور على موقعكم الإلكتروني عن طريق نسخ الشفرة أدناه.",
@ -228,6 +230,8 @@
"empty_column.direct": "لم يتم الإشارة إليك بشكل خاص بعد. عندما تتلقى أو ترسل إشارة، سيتم عرضها هنا.",
"empty_column.domain_blocks": "ليس هناك نطاقات تم حجبها بعد.",
"empty_column.explore_statuses": "ليس هناك ما هو متداوَل الآن. عد في وقت لاحق!",
"empty_column.favourited_statuses": "ليس لديك أية منشورات مفضلة بعد. عندما ستقوم بالإعجاب بواحدة، ستظهر هنا.",
"empty_column.favourites": "لم يقم أي أحد بالإعجاب بهذا المنشور بعد. عندما يقوم أحدهم بذلك سوف يظهر هنا.",
"empty_column.follow_requests": "ليس عندك أي طلب للمتابعة بعد. سوف تظهر طلباتك هنا إن قمت بتلقي البعض منها.",
"empty_column.followed_tags": "لم تُتابع أي وسم بعدُ. ستظهر الوسوم هنا حينما تفعل ذلك.",
"empty_column.hashtag": "ليس هناك بعدُ أي محتوى ذو علاقة بهذا الوسم.",
@ -298,14 +302,22 @@
"home.column_settings.basic": "الأساسية",
"home.column_settings.show_reblogs": "اعرض الترقيات",
"home.column_settings.show_replies": "اعرض الردود",
"home.explore_prompt.body": "سوف يحتوي خيط أخبارك الرئيسي على مزيج من المشاركات من الوسوم التي اخترت متابعتها، والأشخاص الذين اخترت متابعتهم، والمنشورات التي قاموا بدعمها. ومع ذلك، إن كانت تبدو الأمور هادئة جدا، ماذا لو:",
"home.explore_prompt.title": "هذا مقرك الرئيسي داخل ماستدون.",
"home.hide_announcements": "إخفاء الإعلانات",
"home.show_announcements": "إظهار الإعلانات",
"interaction_modal.description.favourite": "بفضل حساب على ماستدون، يمكنك إضافة هذا المنشور إلى مفضلتك لإبلاغ الناشر عن تقديرك وكذا للاحتفاظ بالمنشور إلى وقت لاحق.",
"interaction_modal.description.follow": "مع حساب في ماستدون، يمكنك متابعة {name} وتلقي منشوراته على خيطك الرئيس.",
"interaction_modal.description.reblog": "مع حساب في ماستدون، يمكنك تعزيز هذا المنشور ومشاركته مع مُتابِعيك.",
"interaction_modal.description.reply": "مع حساب في ماستدون، يمكنك الرد على هذا المنشور.",
"interaction_modal.login.action": "خذني إلى خادمي",
"interaction_modal.login.prompt": "نطاق الخادم الخاص بك، على سبيل المثال mastodon.social",
"interaction_modal.no_account_yet": "ليست على ماستدون بعد؟",
"interaction_modal.on_another_server": "على خادم مختلف",
"interaction_modal.on_this_server": "على هذا الخادم",
"interaction_modal.sign_in": "لم تقم بتسجيل الدخول إلى هذا الخادم. أين هو مستضاف حسابك؟",
"interaction_modal.sign_in_hint": "تلميح: هذا هو الموقع الذي سجّلت عن طريقه. إن لم تتذكّر/ين اسم الموقع، يمكنك البحث عن الرسالة الترحيبيّة في بريدك الالكتروني. يمكنك أيضاً استخدام إسم المستخدم/ـة الكامل! (مثلاً: @Mastadon@mastadon.social)",
"interaction_modal.title.favourite": "إضافة منشور {name} إلى المفضلة",
"interaction_modal.title.follow": "اتبع {name}",
"interaction_modal.title.reblog": "مشاركة منشور {name}",
"interaction_modal.title.reply": "الرد على منشور {name}",
@ -321,6 +333,8 @@
"keyboard_shortcuts.direct": "to open direct messages column",
"keyboard_shortcuts.down": "للانتقال إلى أسفل القائمة",
"keyboard_shortcuts.enter": "لفتح المنشور",
"keyboard_shortcuts.favourite": "لإضافة المنشور إلى المفضلة",
"keyboard_shortcuts.favourites": "لفتح قائمة المفضلات",
"keyboard_shortcuts.federated": "لفتح الخيط الزمني الفديرالي",
"keyboard_shortcuts.heading": "Keyboard Shortcuts",
"keyboard_shortcuts.home": "لفتح الخيط الرئيسي",
@ -351,6 +365,7 @@
"lightbox.previous": "العودة",
"limited_account_hint.action": "إظهار الملف التعريفي على أي حال",
"limited_account_hint.title": "تم إخفاء هذا الملف الشخصي من قبل مشرفي {domain}.",
"link_preview.author": "مِن {name}",
"lists.account.add": "أضف إلى القائمة",
"lists.account.remove": "احذف من القائمة",
"lists.delete": "احذف القائمة",
@ -373,6 +388,7 @@
"mute_modal.hide_notifications": "هل تود إخفاء الإخطارات القادمة من هذا المستخدم ؟",
"mute_modal.indefinite": "إلى أجل غير مسمى",
"navigation_bar.about": "عن",
"navigation_bar.advanced_interface": "افتحه في واجهة الويب المتقدمة",
"navigation_bar.blocks": "الحسابات المحجوبة",
"navigation_bar.bookmarks": "الفواصل المرجعية",
"navigation_bar.community_timeline": "الخيط المحلي",
@ -382,6 +398,7 @@
"navigation_bar.domain_blocks": "النطاقات المحظورة",
"navigation_bar.edit_profile": "عدّل الملف التعريفي",
"navigation_bar.explore": "استكشف",
"navigation_bar.favourites": "المفضلة",
"navigation_bar.filters": "الكلمات المكتومة",
"navigation_bar.follow_requests": "طلبات المتابعة",
"navigation_bar.followed_tags": "الوسوم المتابَعة",
@ -398,6 +415,7 @@
"not_signed_in_indicator.not_signed_in": "تحتاج إلى تسجيل الدخول للوصول إلى هذا المصدر.",
"notification.admin.report": "{name} أبلغ عن {target}",
"notification.admin.sign_up": "أنشأ {name} حسابًا",
"notification.favourite": "أضاف {name} منشورك إلى مفضلته",
"notification.follow": "{name} يتابعك",
"notification.follow_request": "لقد طلب {name} متابعتك",
"notification.mention": "{name} ذكرك",
@ -411,6 +429,7 @@
"notifications.column_settings.admin.report": "التقارير الجديدة:",
"notifications.column_settings.admin.sign_up": "التسجيلات الجديدة:",
"notifications.column_settings.alert": "إشعارات سطح المكتب",
"notifications.column_settings.favourite": "المفضلة:",
"notifications.column_settings.filter_bar.advanced": "اعرض كافة الفئات",
"notifications.column_settings.filter_bar.category": "شريط الفلترة السريعة",
"notifications.column_settings.filter_bar.show_bar": "إظهار شريط التصفية",
@ -428,6 +447,7 @@
"notifications.column_settings.update": "التعديلات:",
"notifications.filter.all": "الكل",
"notifications.filter.boosts": "الترقيات",
"notifications.filter.favourites": "المفضلة",
"notifications.filter.follows": "يتابِع",
"notifications.filter.mentions": "الإشارات",
"notifications.filter.polls": "نتائج استطلاع الرأي",
@ -578,6 +598,8 @@
"server_banner.server_stats": "إحصائيات الخادم:",
"sign_in_banner.create_account": "أنشئ حسابًا",
"sign_in_banner.sign_in": "تسجيل الدخول",
"sign_in_banner.sso_redirect": "تسجيل الدخول أو إنشاء حساب",
"sign_in_banner.text": "قم بالولوج بحسابك لمتابعة الصفحات الشخصية أو الوسوم، أو لإضافة المنشورات إلى المفضلة ومشاركتها والرد عليها أو التفاعل بواسطة حسابك المتواجد على خادم مختلف.",
"status.admin_account": "افتح الواجهة الإدارية لـ @{name}",
"status.admin_domain": "فتح واجهة الإشراف لـ {domain}",
"status.admin_status": "افتح هذا المنشور على واجهة الإشراف",
@ -594,6 +616,7 @@
"status.edited": "عُدّل في {date}",
"status.edited_x_times": "عُدّل {count, plural, zero {} one {مرةً واحدة} two {مرّتان} few {{count} مرات} many {{count} مرة} other {{count} مرة}}",
"status.embed": "إدماج",
"status.favourite": "فضّل",
"status.filter": "تصفية هذه الرسالة",
"status.filtered": "مُصفّى",
"status.hide": "إخفاء المنشور",

View File

@ -212,6 +212,7 @@
"hashtag.column_header.tag_mode.none": "ensin {additional}",
"hashtag.column_settings.select.no_options_message": "Nun s'atopó nenguna suxerencia",
"hashtag.column_settings.tag_toggle": "Include additional tags in this column",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} participante} other {{counter} participantes}}",
"hashtag.follow": "Siguir a la etiqueta",
"hashtag.unfollow": "Dexar de siguir a la etiqueta",
"home.column_settings.basic": "Configuración básica",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Любы",
"hashtag.column_settings.tag_mode.none": "Нічога з пералічанага",
"hashtag.column_settings.tag_toggle": "Уключыць дадатковыя тэгі для гэтай калонкі",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} удзельнік} few {{counter} удзельніка} many {{counter} удзельнікаў} other {{counter} удзельніка}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} допіс} few {{counter} допісы} many {{counter} допісаў} other {{counter} допісу}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} допіс} few {{counter} допісы} many {{counter} допісаў} other {{counter} допісу}} за сёння",
"hashtag.follow": "Падпісацца на хэштэг",
"hashtag.unfollow": "Адпісацца ад хэштэга",
"home.actions.go_to_explore": "Паглядзіце, што ў трэндзе",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Qualsevol daquests",
"hashtag.column_settings.tag_mode.none": "Cap daquests",
"hashtag.column_settings.tag_toggle": "Inclou etiquetes addicionals per a aquesta columna",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} participant} other {{counter} participants}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} tut} other {{counter} tuts}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} tut} other {{counter} tuts}} avui",
"hashtag.follow": "Segueix l'etiqueta",
"hashtag.unfollow": "Deixa de seguir l'etiqueta",
"home.actions.go_to_explore": "Mira què és tendència",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Nogle af disse",
"hashtag.column_settings.tag_mode.none": "Ingen af disse",
"hashtag.column_settings.tag_toggle": "Inkludér ekstra tags for denne kolonne",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} deltager} other {{counter} deltagere}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} indlæg} other {{counter} indlæg}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} indlæg} other {{counter} indlæg}} i dag",
"hashtag.follow": "Følg hashtag",
"hashtag.unfollow": "Stop med at følge hashtag",
"home.actions.go_to_explore": "Se, hvad som trender",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Eines von diesen",
"hashtag.column_settings.tag_mode.none": "Keines von diesen",
"hashtag.column_settings.tag_toggle": "Zusätzliche Hashtags dieser Spalte hinzufügen",
"hashtag.counter_by_accounts": "{count, plural, one{{counter} Beteiligte*r} other{{counter} Beteiligte}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} Beitrag} other {{counter} Beiträge}} heute",
"hashtag.follow": "Hashtag folgen",
"hashtag.unfollow": "Hashtag entfolgen",
"home.actions.go_to_explore": "Trends ansehen",
@ -547,7 +550,7 @@
"report.reasons.dislike": "Das gefällt mir nicht",
"report.reasons.dislike_description": "Das ist etwas, das du nicht sehen möchtest",
"report.reasons.legal": "Das ist illegal",
"report.reasons.legal_description": "Du bist davon überzeugt, dass es gegen die Gesetze deines Landes oder des Landes des Servers verstößt",
"report.reasons.legal_description": "Du glaubst, dass es gegen die Gesetze deines Landes oder des Landes des Servers verstößt",
"report.reasons.other": "Es ist etwas anderes",
"report.reasons.other_description": "Der Vorfall passt zu keiner dieser Kategorien",
"report.reasons.spam": "Das ist Spam",

View File

@ -235,7 +235,7 @@
"empty_column.follow_requests": "You don't have any follow requests yet. When you receive one, it will show up here.",
"empty_column.followed_tags": "You have not followed any hashtags yet. When you do, they will show up here.",
"empty_column.hashtag": "There is nothing in this hashtag yet.",
"empty_column.home": "Your home timeline is empty! Follow more people to fill it up. {suggestions}",
"empty_column.home": "Your home timeline is empty! Follow more people to fill it up.",
"empty_column.list": "There is nothing in this list yet. When members of this list post new statuses, they will appear here.",
"empty_column.lists": "You don't have any lists yet. When you create one, it will show up here.",
"empty_column.mutes": "You haven't muted any users yet.",
@ -295,6 +295,7 @@
"hashtag.column_settings.tag_mode.any": "Any of these",
"hashtag.column_settings.tag_mode.none": "None of these",
"hashtag.column_settings.tag_toggle": "Include additional tags in this column",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} Following} other {{counter} Following}}",
"hashtag.follow": "Follow hashtag",
"hashtag.unfollow": "Unfollow hashtag",
"home.actions.go_to_explore": "See what's trending",

View File

@ -300,6 +300,7 @@
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} post} other {{counter} posts}} today",
"hashtag.follow": "Follow hashtag",
"hashtag.unfollow": "Unfollow hashtag",
"hashtags.and_other": "…and {count, plural, other {# more}}",
"home.actions.go_to_explore": "See what's trending",
"home.actions.go_to_suggestions": "Find people to follow",
"home.column_settings.basic": "Basic",
@ -532,6 +533,7 @@
"reply_indicator.cancel": "Cancel",
"report.block": "Block",
"report.block_explanation": "You will not see their posts. They will not be able to see your posts or follow you. They will be able to tell that they are blocked.",
"report.categories.legal": "Legal",
"report.categories.other": "Other",
"report.categories.spam": "Spam",
"report.categories.violation": "Content violates one or more server rules",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Cualquiera de estas",
"hashtag.column_settings.tag_mode.none": "Ninguna de estas",
"hashtag.column_settings.tag_toggle": "Incluir etiquetas adicionales para esta columna",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} participante} other {{counter} participantes}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} mensaje} other {{counter} mensajes}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} mensaje} other {{counter} mensajes}} hoy",
"hashtag.follow": "Seguir etiqueta",
"hashtag.unfollow": "Dejar de seguir etiqueta",
"home.actions.go_to_explore": "Mirá qué está en tendencia",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Cualquiera de estos",
"hashtag.column_settings.tag_mode.none": "Ninguno de estos",
"hashtag.column_settings.tag_toggle": "Incluye etiquetas adicionales para esta columna",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} participante} other {{counter} participantes}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}} hoy",
"hashtag.follow": "Seguir etiqueta",
"hashtag.unfollow": "Dejar de seguir etiqueta",
"home.actions.go_to_explore": "Ver tendencias",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Cualquiera de estos",
"hashtag.column_settings.tag_mode.none": "Ninguno de estos",
"hashtag.column_settings.tag_toggle": "Incluir etiquetas adicionales en esta columna",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} participante} other {{counter} participantes}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} publicación} other {{counter} publicaciones}} hoy",
"hashtag.follow": "Seguir etiqueta",
"hashtag.unfollow": "Dejar de seguir etiqueta",
"home.actions.go_to_explore": "Ver tendencias",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Mõni neist",
"hashtag.column_settings.tag_mode.none": "Mitte ükski neist",
"hashtag.column_settings.tag_toggle": "Kaasa lisamärked selle tulba jaoks",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} osalejaga} other {{counter} osalejaga}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} postitusega} other {{counter} postitusega}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} postitust} other {{counter} postitust}} täna",
"hashtag.follow": "Jälgi silti",
"hashtag.unfollow": "Lõpeta sildi jälgimine",
"home.actions.go_to_explore": "Vaata, mis on populaarne",

View File

@ -292,6 +292,9 @@
"hashtag.column_settings.tag_mode.any": "Hautako edozein",
"hashtag.column_settings.tag_mode.none": "Hauetako bat ere ez",
"hashtag.column_settings.tag_toggle": "Include additional tags in this column",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} parte-hartzaile} other {{counter} parte-hartzaile}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} argitalpen} other {{counter} argitalpen}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} argitalpen} other {{counter} argitalpen}} gaur",
"hashtag.follow": "Jarraitu traola",
"hashtag.unfollow": "Utzi traola jarraitzeari",
"home.actions.go_to_explore": "Ikusi zer dagoen pil-pilean",

View File

@ -81,7 +81,7 @@
"admin.impact_report.instance_follows": "Seuraajat, jotka heidän käyttäjänsä menettäisivät",
"admin.impact_report.title": "Vaikutusten yhteenveto",
"alert.rate_limited.message": "Yritä uudestaan {retry_time, time, medium} jälkeen.",
"alert.rate_limited.title": "Määrää rajoitettu",
"alert.rate_limited.title": "Pyyntömäärää rajoitettu",
"alert.unexpected.message": "Tapahtui odottamaton virhe.",
"alert.unexpected.title": "Hups!",
"announcement.announcement": "Ilmoitus",
@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Mikä tahansa näistä",
"hashtag.column_settings.tag_mode.none": "Ei mitään näistä",
"hashtag.column_settings.tag_toggle": "Sisällytä lisätunnisteet tähän sarakkeeseen",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} osallistuja} other {{counter} osallistujaa}}",
"hashtag.counter_by_uses": "{count, plural, one{{counter} julkaisu} other {{counter} julkaisua}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} julkaisu} other {{counter} julkaisua}} tänään",
"hashtag.follow": "Seuraa aihetunnistetta",
"hashtag.unfollow": "Lopeta aihetunnisteen seuraaminen",
"home.actions.go_to_explore": "Katso, mikä on suosittua",
@ -420,7 +423,7 @@
"notification.follow_request": "{name} haluaa seurata sinua",
"notification.mention": "{name} mainitsi sinut",
"notification.own_poll": "Äänestyksesi on päättynyt",
"notification.poll": "Kysely, johon osallistuit, on päättynyt",
"notification.poll": "Äänestys, johon osallistuit, on päättynyt",
"notification.reblog": "{name} tehosti viestiäsi",
"notification.status": "{name} julkaisi juuri viestin",
"notification.update": "{name} muokkasi viestiä",
@ -598,7 +601,7 @@
"server_banner.server_stats": "Palvelimen tilastot:",
"sign_in_banner.create_account": "Luo tili",
"sign_in_banner.sign_in": "Kirjaudu",
"sign_in_banner.sso_redirect": "Kirjaudu sisään tai rekisteröidy",
"sign_in_banner.sso_redirect": "Kirjaudu tai rekisteröidy",
"sign_in_banner.text": "Kirjaudu sisään seurataksesi profiileja tai aihetunnisteita, merkitäksesi julkaisuja suosikeiksi, julkaistaksesi sekä vastataksesi julkaisuihin. Voit vuorovaikuttaa myös eri palvelimella sijaitsevalta tililtäsi.",
"status.admin_account": "Avaa moderaattorinäkymä tilistä @{name}",
"status.admin_domain": "Avaa palvelimen {domain} moderointitoiminnot",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Okkurt av hesum",
"hashtag.column_settings.tag_mode.none": "Einki av hesum",
"hashtag.column_settings.tag_toggle": "Legg frámerki afturat hesum teigi",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} luttakari} other {{counter} luttakarar}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} postur} other {{counter} postar}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} postur} other {{counter} postar}} í dag",
"hashtag.follow": "Fylg frámerki",
"hashtag.unfollow": "Gevst at fylgja frámerki",
"home.actions.go_to_explore": "Sí rákið",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Au moins un de ceux-ci",
"hashtag.column_settings.tag_mode.none": "Aucun de ceux-ci",
"hashtag.column_settings.tag_toggle": "Inclure des hashtags additionnels pour cette colonne",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} participant} other {{counter} participants}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} message} other {{counter} messages}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} message} other {{counter} messages}} aujourdhui",
"hashtag.follow": "Suivre ce hashtag",
"hashtag.unfollow": "Ne plus suivre ce hashtag",
"home.actions.go_to_explore": "Voir les tendances",

View File

@ -38,7 +38,7 @@
"account.following_counter": "{count, plural, one {{counter} Abonnement} other {{counter} Abonnements}}",
"account.follows.empty": "Cet·te utilisateur·rice ne suit personne pour linstant.",
"account.follows_you": "Vous suit",
"account.go_to_profile": "Voir le profil",
"account.go_to_profile": "Aller au profil",
"account.hide_reblogs": "Masquer les partages de @{name}",
"account.in_memoriam": "En mémoire de.",
"account.joined_short": "Ici depuis",
@ -50,9 +50,9 @@
"account.moved_to": "{name} a indiqué que son nouveau compte est maintenant :",
"account.mute": "Masquer @{name}",
"account.mute_notifications_short": "Désactiver les alertes",
"account.mute_short": "Mettre en sourdine",
"account.mute_short": "Masquer",
"account.muted": "Masqué·e",
"account.no_bio": "Aucune description enregistrée.",
"account.no_bio": "Aucune description fournie.",
"account.open_original_page": "Ouvrir la page d'origine",
"account.posts": "Messages",
"account.posts_with_replies": "Messages et réponses",
@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Au moins un de ces éléments",
"hashtag.column_settings.tag_mode.none": "Aucun de ces éléments",
"hashtag.column_settings.tag_toggle": "Inclure des hashtags additionnels pour cette colonne",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} participant} other {{counter} participants}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} message} other {{counter} messages}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} message} other {{counter} messages}} aujourdhui",
"hashtag.follow": "Suivre le hashtag",
"hashtag.unfollow": "Ne plus suivre le hashtag",
"home.actions.go_to_explore": "Voir les tendances",

View File

@ -13,14 +13,14 @@
"about.rules": "Riaghailtean an fhrithealaiche",
"account.account_note_header": "Nòta",
"account.add_or_remove_from_list": "Cuir ris no thoir air falbh o na liostaichean",
"account.badges.bot": "Bot",
"account.badges.bot": "Fèin-obrachail",
"account.badges.group": "Buidheann",
"account.block": "Bac @{name}",
"account.block_domain": "Bac an àrainn {domain}",
"account.block_short": "Bac",
"account.blocked": "Ga bhacadh",
"account.browse_more_on_origin_server": "Rùraich barrachd dheth air a phròifil thùsail",
"account.cancel_follow_request": "Cuir d iarrtas leantainn dhan dàrna taobh",
"account.cancel_follow_request": "Sguir dhen leantainn",
"account.direct": "Thoir iomradh air @{name} gu prìobhaideach",
"account.disable_notifications": "Na cuir brath thugam tuilleadh nuair a chuireas @{name} post ris",
"account.domain_blocked": "Chaidh an àrainn a bhacadh",
@ -113,7 +113,8 @@
"column.direct": "Iomraidhean prìobhaideach",
"column.directory": "Rùraich sna pròifilean",
"column.domain_blocks": "Àrainnean bacte",
"column.firehose": "Inbhirean beòtha",
"column.favourites": "Annsachdan",
"column.firehose": "An saoghal poblach",
"column.follow_requests": "Iarrtasan leantainn",
"column.home": "Dachaigh",
"column.lists": "Liostaichean",
@ -134,6 +135,8 @@
"community.column_settings.remote_only": "Feadhainn chèin a-mhàin",
"compose.language.change": "Atharraich an cànan",
"compose.language.search": "Lorg cànan…",
"compose.published.body": "Chaidh am post fhoillseachadh.",
"compose.published.open": "Fosgail",
"compose_form.direct_message_warning_learn_more": "Barrachd fiosrachaidh",
"compose_form.encryption_warning": "Chan eil crioptachadh ceann gu ceann air postaichean Mhastodon. Na co-roinn fiosrachadh dìomhair idir le Mastodon.",
"compose_form.hashtag_warning": "Cha nochd am post seo fon taga hais o nach eil e poblach. Cha ghabh ach postaichean poblach a lorg a-rèir an tagaichean hais.",
@ -147,7 +150,7 @@
"compose_form.poll.switch_to_multiple": "Atharraich an cunntas-bheachd ach an gabh iomadh roghainn a thaghadh",
"compose_form.poll.switch_to_single": "Atharraich an cunntas-bheachd gus nach gabh ach aon roghainn a thaghadh",
"compose_form.publish": "Foillsich",
"compose_form.publish_form": "Foillsich",
"compose_form.publish_form": "Post ùr",
"compose_form.publish_loud": "{publish}!",
"compose_form.save_changes": "Sàbhail na h-atharraichean",
"compose_form.sensitive.hide": "{count, plural, one {Cuir comharra gu bheil am meadhan frionasach} two {Cuir comharra gu bheil na meadhanan frionasach} few {Cuir comharra gu bheil na meadhanan frionasach} other {Cuir comharra gu bheil na meadhanan frionasach}}",
@ -178,6 +181,7 @@
"confirmations.mute.explanation": "Cuiridh seo na postaichean uapa s na postaichean a bheir iomradh orra am falach ach chì iad-san na postaichean agad fhathast is faodaidh iad gad leantainn.",
"confirmations.mute.message": "A bheil thu cinnteach gu bheil thu airson {name} a mhùchadh?",
"confirmations.redraft.confirm": "Sguab às ⁊ dèan dreachd ùr",
"confirmations.redraft.message": "A bheil thu cinnteach gu bheil thu airson am post seo a sguabadh às agus dreachd ùr a thòiseachadh? Caillidh tu gach annsachd is brosnachadh air agus thèid freagairtean dhan phost thùsail nan dìlleachdanan.",
"confirmations.reply.confirm": "Freagair",
"confirmations.reply.message": "Ma bheir thu freagairt an-dràsta, thèid seo a sgrìobhadh thairis air an teachdaireachd a tha thu a sgrìobhadh an-dràsta. A bheil thu cinnteach gu bheil thu airson leantainn air adhart?",
"confirmations.unfollow.confirm": "Na lean tuilleadh",
@ -196,8 +200,9 @@
"disabled_account_banner.text": "Tha an cunntas {disabledAccount} agad à comas aig an àm seo.",
"dismissable_banner.community_timeline": "Seo na postaichean poblach as ùire o dhaoine aig a bheil cunntas air {domain}.",
"dismissable_banner.dismiss": "Leig seachad",
"dismissable_banner.explore_links": "Seo na naidheachdan air a bhithear a bruidhinn an-dràsta fhèin air an fhrithealaiche seo is frithealaichean eile dhen lìonra sgaoilte.",
"dismissable_banner.explore_tags": "Tha fèill air na tagaichean hais seo a fàs an-dràsta fhèin air an fhrithealaich seo is frithealaichean eile dhen lìonra sgaoilte.",
"dismissable_banner.explore_links": "Seo na cinn-naidheachd a tha gan co-roinneadh as trice thar an lìona shòisealta an-diugh. Gheibh naidheachdan nas ùire a tha gan co-roinneadh le daoine eadar-dhealaichte rangachadh nas àirde.",
"dismissable_banner.explore_statuses": "Tha fèill air na postaichean seo a fàs thar an lìona shòisealta an-diugh. Gheibh postaichean nas ùire le barrachd brosnaichean is annsachdan rangachadh nas àirde.",
"dismissable_banner.explore_tags": "Tha fèill air na tagaichean hais seo a fàs air an fhrithealaiche seo is frithealaichean eile dhen lìonra sgaoilte an-diugh. Gheibh tagaichean hais a tha gan cleachdadh le daoine eadar-dhealaichte rangachadh nas àirde.",
"dismissable_banner.public_timeline": "Seo na postaichean poblach as ùire o dhaoine air an lìonra sòisealta tha gan leantainn le daoine air {domain}.",
"embed.instructions": "Leabaich am post seo san làrach-lìn agad is tu a dèanamh lethbhreac dhen chòd gu h-ìosal.",
"embed.preview": "Seo an coltas a bhios air:",
@ -225,10 +230,12 @@
"empty_column.direct": "Chan eil iomradh prìobhaideach agad fhathast. Nuair a chuireas no a gheibh thu tè, nochdaidh i an-seo.",
"empty_column.domain_blocks": "Cha deach àrainn sam bith a bhacadh fhathast.",
"empty_column.explore_statuses": "Chan eil dad a treandadh an-dràsta fhèin. Thoir sùil a-rithist an ceann greis!",
"empty_column.favourited_statuses": "Chan eil annsachd air post agad fhathast. Nuair a nì thu annsachd de dhfhear, nochdaidh e an-seo.",
"empty_column.favourites": "Chan deach am post seo a bhrosnachadh le duine sam bith fhathast. Nuair a bhrosnaicheas cuideigin e, nochdaidh iad an-seo.",
"empty_column.follow_requests": "Chan eil iarrtas leantainn agad fhathast. Nuair a gheibh thu fear, nochdaidh e an-seo.",
"empty_column.followed_tags": "Cha do lean thu taga hais sam bith fhathast. Nuair a leanas tu, nochdaidh iad an-seo.",
"empty_column.hashtag": "Chan eil dad san taga hais seo fhathast.",
"empty_column.home": "Tha loidhne-ama na dachaigh agad falamh! Lean barrachd dhaoine gus a lìonadh. {suggestions}",
"empty_column.home": "Tha loidhne-ama na dachaigh agad falamh! Lean barrachd dhaoine gus a lìonadh.",
"empty_column.list": "Chan eil dad air an liosta seo fhathast. Nuair a phostaicheas buill a tha air an liosta seo postaichean ùra, nochdaidh iad an-seo.",
"empty_column.lists": "Chan eil liosta agad fhathast. Nuair chruthaicheas tu tè, nochdaidh i an-seo.",
"empty_column.mutes": "Cha do mhùch thu cleachdaiche sam bith fhathast.",
@ -288,21 +295,32 @@
"hashtag.column_settings.tag_mode.any": "Gin sam bith dhiubh",
"hashtag.column_settings.tag_mode.none": "Às aonais gin sam bith dhiubh",
"hashtag.column_settings.tag_toggle": "Gabh a-steach barrachd tagaichean sa cholbh seo",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} chom-pàirtiche} two {{counter} chom-pàirtiche} few {{counter} com-pàirtiche} other {{counter} com-pàirtiche}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} phost} two {{counter} phost} few {{counter} postaichean} other {{counter} post}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} phost} two {{counter} phost} few { postaichean} other { post}} an-diugh",
"hashtag.follow": "Lean an taga hais",
"hashtag.unfollow": "Na lean an taga hais tuilleadh",
"home.actions.go_to_explore": "Faic na tha a treandadh",
"home.actions.go_to_suggestions": "Lorg daoine a leanas tu",
"home.actions.go_to_suggestions": "Lorg daoine gus an leantainn",
"home.column_settings.basic": "Bunasach",
"home.column_settings.show_reblogs": "Seall na brosnachaidhean",
"home.column_settings.show_replies": "Seall na freagairtean",
"home.explore_prompt.body": "Bidh measgachadh de phostaichean o na tagaichean hais a leanas tu, na daoine a leanas tu is na postaichean a bhrosnaicheas iad air do dhachaigh. Ma tha cùisean ro shàmhach dhut, seo nas urrainn dhut a dhèanamh:",
"home.explore_prompt.title": "Seo do dhachaigh am broinn Mastodon.",
"home.hide_announcements": "Falaich na brathan-fios",
"home.show_announcements": "Seall na brathan-fios",
"interaction_modal.description.favourite": "Le cunntas air Mastodon, s urrainn dhut am post seo a chur ris na h-annsachdan airson innse dhan ùghdar gu bheil e a còrdadh dhut s a shàbhaladh do uaireigin eile.",
"interaction_modal.description.follow": "Le cunntas air Mastodon, s urrainn dhut {name} a leantainn ach am faigh thu na postaichean aca nad dhachaigh.",
"interaction_modal.description.reblog": "Le cunntas air Mastodon, s urrainn dhut am post seo a bhrosnachadh gus a cho-roinneadh leis an luchd-leantainn agad fhèin.",
"interaction_modal.description.reply": "Le cunntas air Mastodon, s urrainn dhut freagairt a chur dhan phost seo.",
"interaction_modal.login.action": "Thoir dhachaigh mi",
"interaction_modal.login.prompt": "Àrainn-lìn an fhrithealaiche dachaigh agad, can ailbhean.co-shaoghal.net",
"interaction_modal.no_account_yet": "Nach eil thu air Mastodon?",
"interaction_modal.on_another_server": "Air frithealaiche eile",
"interaction_modal.on_this_server": "Air an frithealaiche seo",
"interaction_modal.sign_in": "Cha deach do chlàradh a-steach air an fhrithealaiche seo. Càit a bheil an cunntas agad ga òstadh?",
"interaction_modal.sign_in_hint": "Gliocas: Seo an làrach-lìn far an do chlàraich thu. Mur eil cuimhne agad dè bh ann, thoir sùil air a phost-d fàilteachaidh sa bhogsa a-steach agad. S urrainn dhut an t-ainm-cleachdaiche slàn agad a chur a-steach cuideachd! (can @mise@ ailbhean.co-shaoghal.net)",
"interaction_modal.title.favourite": "Cuir am post aig {name} ris na h-annsachdan",
"interaction_modal.title.follow": "Lean {name}",
"interaction_modal.title.reblog": "Brosnaich am post aig {name}",
"interaction_modal.title.reply": "Freagair dhan phost aig {name}",
@ -315,9 +333,11 @@
"keyboard_shortcuts.column": "Cuir am fòcas air colbh",
"keyboard_shortcuts.compose": "Cuir am fòcas air raon teacsa an sgrìobhaidh",
"keyboard_shortcuts.description": "Tuairisgeul",
"keyboard_shortcuts.direct": "to open direct messages column",
"keyboard_shortcuts.direct": "a dhfhosgladh colbh nan iomraidhean prìobhaideach",
"keyboard_shortcuts.down": "Gluais sìos air an liosta",
"keyboard_shortcuts.enter": "Fosgail post",
"keyboard_shortcuts.favourite": "Cuir am post ris na h-annsachdan",
"keyboard_shortcuts.favourites": "Fosgail liosta nan annsachdan",
"keyboard_shortcuts.federated": "Fosgail an loidhne-ama cho-naisgte",
"keyboard_shortcuts.heading": "Ath-ghoiridean a mheur-chlàir",
"keyboard_shortcuts.home": "Fosgail loidhne-ama na dachaigh",
@ -348,6 +368,7 @@
"lightbox.previous": "Air ais",
"limited_account_hint.action": "Seall a phròifil co-dhiù",
"limited_account_hint.title": "Chaidh a phròifil seo fhalach le maoir {domain}.",
"link_preview.author": "Le {name}",
"lists.account.add": "Cuir ris an liosta",
"lists.account.remove": "Thoir air falbh on liosta",
"lists.delete": "Sguab às an liosta",
@ -359,7 +380,7 @@
"lists.replies_policy.followed": "Cleachdaiche sam bith a leanas mi",
"lists.replies_policy.list": "Buill na liosta",
"lists.replies_policy.none": "Na seall idir",
"lists.replies_policy.title": "Seall na freagairtean gu:",
"lists.replies_policy.title": "Seall freagairtean do:",
"lists.search": "Lorg am measg nan daoine a leanas tu",
"lists.subheading": "Na liostaichean agad",
"load_pending": "{count, plural, one {# nì ùr} two {# nì ùr} few {# nithean ùra} other {# nì ùr}}",
@ -370,6 +391,7 @@
"mute_modal.hide_notifications": "A bheil thu airson na brathan fhalach on chleachdaiche seo?",
"mute_modal.indefinite": "Gun chrìoch",
"navigation_bar.about": "Mu dhèidhinn",
"navigation_bar.advanced_interface": "Fosgail san eadar-aghaidh-lìn adhartach",
"navigation_bar.blocks": "Cleachdaichean bacte",
"navigation_bar.bookmarks": "Comharran-lìn",
"navigation_bar.community_timeline": "Loidhne-ama ionadail",
@ -379,6 +401,7 @@
"navigation_bar.domain_blocks": "Àrainnean bacte",
"navigation_bar.edit_profile": "Deasaich a phròifil",
"navigation_bar.explore": "Rùraich",
"navigation_bar.favourites": "Annsachdan",
"navigation_bar.filters": "Faclan mùchte",
"navigation_bar.follow_requests": "Iarrtasan leantainn",
"navigation_bar.followed_tags": "Tagaichean hais gan leantainn",
@ -395,6 +418,7 @@
"not_signed_in_indicator.not_signed_in": "Feumaidh tu clàradh a-steach mus fhaigh thu cothrom air a ghoireas seo.",
"notification.admin.report": "Rinn {name} gearan mu {target}",
"notification.admin.sign_up": "Chlàraich {name}",
"notification.favourite": "Is annsa le {name} am post agad",
"notification.follow": "Tha {name} gad leantainn a-nis",
"notification.follow_request": "Dhiarr {name} gad leantainn",
"notification.mention": "Thug {name} iomradh ort",
@ -408,6 +432,7 @@
"notifications.column_settings.admin.report": "Gearanan ùra:",
"notifications.column_settings.admin.sign_up": "Clàraidhean ùra:",
"notifications.column_settings.alert": "Brathan deasga",
"notifications.column_settings.favourite": "Annsachdan:",
"notifications.column_settings.filter_bar.advanced": "Seall a h-uile roinn-seòrsa",
"notifications.column_settings.filter_bar.category": "Bàr-criathraidh luath",
"notifications.column_settings.filter_bar.show_bar": "Seall am bàr-criathraidh",
@ -425,6 +450,7 @@
"notifications.column_settings.update": "Deasachaidhean:",
"notifications.filter.all": "Na h-uile",
"notifications.filter.boosts": "Brosnachaidhean",
"notifications.filter.favourites": "Annsachdan",
"notifications.filter.follows": "A leantainn",
"notifications.filter.mentions": "Iomraidhean",
"notifications.filter.polls": "Toraidhean cunntais-bheachd",
@ -440,12 +466,12 @@
"notifications_permission_banner.title": "Na caill dad gu bràth tuilleadh",
"onboarding.action.back": "Air ais leam",
"onboarding.actions.back": "Air ais leam",
"onboarding.actions.go_to_explore": "See what's trending",
"onboarding.actions.go_to_home": "Go to your home feed",
"onboarding.actions.go_to_explore": "Thoir dha na treandaichean mi",
"onboarding.actions.go_to_home": "Thoir dhachaigh mi",
"onboarding.compose.template": "Shin thu, a #Mhastodon!",
"onboarding.follows.empty": "Gu mì-fhortanach, chan urrainn dhuinn toradh a shealltainn an-dràsta. Feuch gleus an luirg no duilleag an rùrachaidh airson daoine ri leantainn a lorg no feuch ris a-rithist an ceann tamaill.",
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Popular on Mastodon",
"onboarding.follows.title": "Cuir dreach pearsanta air do dhachaigh",
"onboarding.share.lead": "Innis do dhaoine mar a gheibh iad grèim ort air Mastodon!",
"onboarding.share.message": "Is mise {username} air #Mastodon! Thig gam leantainn air {url}",
"onboarding.share.next_steps": "Ceuman eile as urrainn dhut gabhail:",
@ -454,13 +480,13 @@
"onboarding.start.skip": "Want to skip right ahead?",
"onboarding.start.title": "Rinn thu a chùis air!",
"onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.",
"onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
"onboarding.steps.follow_people.title": "Cuir dreach pearsanta air do dhachaigh",
"onboarding.steps.publish_status.body": "Say hello to the world.",
"onboarding.steps.publish_status.title": "Dèan a chiad phost agad",
"onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.",
"onboarding.steps.setup_profile.title": "Customize your profile",
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
"onboarding.steps.share_profile.title": "Share your profile",
"onboarding.steps.setup_profile.title": "Gnàthaich a phròifil agad",
"onboarding.steps.share_profile.body": "Leig fios dha do charaidean mar a gheibh iad lorg ort air Mastodon",
"onboarding.steps.share_profile.title": "Co-roinn a phròifil Mastodon agad",
"onboarding.tips.2fa": "<strong>An robh fios agad?</strong> S urrainn dhut an cunntas agad a dhìon is tu a suidheachadh dearbhadh dà-cheumnach ann an roghainnean a chunntais agad. Obraichidh e le aplacaid dearbhaidh dhà-cheumnaich sam bith a thogras tu gun fheum air àireamh fòn!",
"onboarding.tips.accounts_from_other_servers": "<strong>An robh fios agad?</strong> On a tha Mastodon sgaoilte, tachraidh tu air pròifilean a tha gan òstadh air frithealaichean eile. S urrainn dhut bruidhinn riutha gun chnap-starra co-dhiù! S e ainm an fhrithealaiche a tha san dàrna leth dhen ainm-chleachdaiche aca!",
"onboarding.tips.migration": "<strong>An robh fios agad?</strong> Ma thig an latha nach eil thu toilichte le {domain} mar an fhrithealaiche agad tuilleadh, s urrainn dhut imrich gu frithealaiche Mastodon eile gun a bhith a call an luchd-leantainn agad. S urrainn dhut fiù frithealaiche agad fhèin òstadh!",
@ -575,6 +601,8 @@
"server_banner.server_stats": "Stadastaireachd an fhrithealaiche:",
"sign_in_banner.create_account": "Cruthaich cunntas",
"sign_in_banner.sign_in": "Clàraich a-steach",
"sign_in_banner.sso_redirect": "Clàraich a-steach no clàraich leinn",
"sign_in_banner.text": "Clàraich a-steach a leantainn phròifilean no thagaichean hais, a cur postaichean ris na h-annsachdan s gan co-roinneadh is freagairt dhaibh. S urrainn dhut gnìomh a ghabhail le cunntas o fhrithealaiche eile cuideachd.",
"status.admin_account": "Fosgail eadar-aghaidh na maorsainneachd dha @{name}",
"status.admin_domain": "Fosgail eadar-aghaidh na maorsainneachd dha {domain}",
"status.admin_status": "Fosgail am post seo ann an eadar-aghaidh na maorsainneachd",
@ -591,12 +619,15 @@
"status.edited": "Air a dheasachadh {date}",
"status.edited_x_times": "Chaidh a dheasachadh {count, plural, one {{counter} turas} two {{counter} thuras} few {{counter} tursan} other {{counter} turas}}",
"status.embed": "Leabaich",
"status.favourite": "Cuir ris na h-annsachdan",
"status.filter": "Criathraich am post seo",
"status.filtered": "Criathraichte",
"status.hide": "Falaich am post",
"status.history.created": "Chruthaich {name} {date} e",
"status.history.edited": "Dheasaich {name} {date} e",
"status.load_more": "Luchdaich barrachd dheth",
"status.media.open": "Dèan briogadh gus fhosgladh",
"status.media.show": "Dèan briogadh gus a shealltainn",
"status.media_hidden": "Meadhan falaichte",
"status.mention": "Thoir iomradh air @{name}",
"status.more": "Barrachd",
@ -624,9 +655,10 @@
"status.show_more": "Seall barrachd dheth",
"status.show_more_all": "Seall barrachd dhen a h-uile",
"status.show_original": "Seall an tionndadh tùsail",
"status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}",
"status.title.with_attachments": "Phostaich {user} {attachmentCount, plural, one {{attachmentCount} cheanglachan} two {{attachmentCount} cheanglachan} few {{attachmentCount} ceanglachain} other {{attachmentCount} ceanglachan}}",
"status.translate": "Eadar-theangaich",
"status.translated_from_with": "Air eadar-theangachadh o {lang} le {provider}",
"status.uncached_media_warning": "Chan eil ro-shealladh ri fhaighinn",
"status.unmute_conversation": "Dì-mhùch an còmhradh",
"status.unpin": "Dì-phrìnich on phròifil",
"subscribed_languages.lead": "Cha nochd ach na postaichean sna cànanan a thagh thu air loidhnichean-ama na dachaigh s nan liostaichean às dèidh an atharrachaidh seo. Na tagh gin ma tha thu airson na postaichean uile fhaighinn ge b e dè an cànan.",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Calquera destes",
"hashtag.column_settings.tag_mode.none": "Ningún destes",
"hashtag.column_settings.tag_toggle": "Incluír cancelos adicionais para esta columna",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} participante} other {{counter} participantes}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} publicación} other {{counter} publicacións}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} publicación} other {{counter} publicacións}} hoxe",
"hashtag.follow": "Seguir cancelo",
"hashtag.unfollow": "Deixar de seguir cancelo",
"home.actions.go_to_explore": "Mira do que se está a falar",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "לפחות אחד מאלה",
"hashtag.column_settings.tag_mode.none": "אף אחד מאלה",
"hashtag.column_settings.tag_toggle": "כלול תגיות נוספות בטור זה",
"hashtag.counter_by_accounts": "{count, plural,one{{count} משתתף.ת}other{{count} משתתפיםות}}",
"hashtag.counter_by_uses": "{count, plural, one {הודעה אחת} two {הודעותיים} many {{count} הודעות} other {{count} הודעות}}",
"hashtag.counter_by_uses_today": "{count, plural, one {הודעה אחת} two {הודעותיים} many {{count} הודעות} other {{count} הודעות}} היום",
"hashtag.follow": "מעקב אחר תגית",
"hashtag.unfollow": "ביטול מעקב אחר תגית",
"home.actions.go_to_explore": "הצגת מגמות",

View File

@ -114,7 +114,7 @@
"column.directory": "Profilok böngészése",
"column.domain_blocks": "Letiltott tartománynevek",
"column.favourites": "Kedvencek",
"column.firehose": "Élő hírfolyamok",
"column.firehose": "Hírfolyamok",
"column.follow_requests": "Követési kérelmek",
"column.home": "Kezdőlap",
"column.lists": "Listák",
@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Bármelyik",
"hashtag.column_settings.tag_mode.none": "Egyik sem",
"hashtag.column_settings.tag_toggle": "További címkék felvétele ehhez az oszlophoz",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} résztvevő} other {{counter} résztvevő}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} bejegyzés} other {{counter} bejegyzés}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} bejegyzés} other {{counter} bejegyzés}} ma",
"hashtag.follow": "Hashtag követése",
"hashtag.unfollow": "Hashtag követésének megszüntetése",
"home.actions.go_to_explore": "Felkapottak megtekintése",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Hvað sem er af þessu",
"hashtag.column_settings.tag_mode.none": "Ekkert af þessu",
"hashtag.column_settings.tag_toggle": "Taka með viðbótarmerki fyrir þennan dálk",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} þátttakandi} other {{counter} þátttakendur}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} færsla} other {{counter} færslur}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} færsla} other {{counter} færslur}} í dag",
"hashtag.follow": "Fylgjast með myllumerki",
"hashtag.unfollow": "Hætta að fylgjast með myllumerki",
"home.actions.go_to_explore": "Sjáðu hvað er í umræðunni",
@ -302,6 +305,7 @@
"home.column_settings.basic": "Einfalt",
"home.column_settings.show_reblogs": "Sýna endurbirtingar",
"home.column_settings.show_replies": "Birta svör",
"home.explore_prompt.body": "Heimastreymið þitt verður með blöndu af færslum úr myllumerkjunum sem þú hefur valið að fylgja, færslum frá fólki sem þú hefur valið að fylgja og færslum sem þau endurbirta. Ef þér finnst þetta allt of kyrrlátt, gætirðu viljað:",
"home.explore_prompt.title": "Þetta er bækistöð þín innan Loðfílsins.",
"home.hide_announcements": "Fela auglýsingar",
"home.show_announcements": "Birta auglýsingar",
@ -315,6 +319,7 @@
"interaction_modal.on_another_server": "Á öðrum netþjóni",
"interaction_modal.on_this_server": "Á þessum netþjóni",
"interaction_modal.sign_in": "Þú ert ekki skráð/ur inn á þennan netþjón. Hvar er aðgangurinn þinn hýstur?",
"interaction_modal.sign_in_hint": "Ábending: Það er vefsvæðið þar sem þú skráðir þig. Ef þú manst ekki hvar, geturðu leitað að kynningarpóstinum í pósthólfinu þínu. Þú getur líka sett inn fullt notandanafn þitt (t.d. @Mastodon@mastodon.social)",
"interaction_modal.title.favourite": "Setja færsluna frá {name} í eftirlæti",
"interaction_modal.title.follow": "Fylgjast með {name}",
"interaction_modal.title.reblog": "Endurbirta færsluna frá {name}",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Uno o più di questi",
"hashtag.column_settings.tag_mode.none": "Nessuno di questi",
"hashtag.column_settings.tag_toggle": "Includi i tag aggiuntivi per questa colonna",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} partecipante} other {{counter} partecipanti}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} post} other {{counter} post}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} post} other {{counter} post}} oggi",
"hashtag.follow": "Segui l'hashtag",
"hashtag.unfollow": "Smetti di seguire l'hashtag",
"home.actions.go_to_explore": "Scopri cosa sia di tendenza",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "いずれかを含む",
"hashtag.column_settings.tag_mode.none": "これらを除く",
"hashtag.column_settings.tag_toggle": "このカラムに追加のタグを含める",
"hashtag.counter_by_accounts": "{count, plural, other {{counter}人投稿}}",
"hashtag.counter_by_uses": "{count, plural, other {{counter}件}}",
"hashtag.counter_by_uses_today": "今日{count, plural, other {{counter}件}}",
"hashtag.follow": "ハッシュタグをフォローする",
"hashtag.unfollow": "ハッシュタグのフォローを解除",
"home.actions.go_to_explore": "話題をさがす",

View File

@ -7,6 +7,7 @@
"account.badges.group": "Agraw",
"account.block": "Seḥbes @{name}",
"account.block_domain": "Ffer kra i d-yekkan seg {domain}",
"account.block_short": "Sewḥel",
"account.blocked": "Yettusewḥel",
"account.browse_more_on_origin_server": "Snirem ugar deg umeɣnu aneẓli",
"account.cancel_follow_request": "Withdraw follow request",
@ -28,6 +29,7 @@
"account.media": "Timidyatin",
"account.mention": "Bder-d @{name}",
"account.mute": "Sgugem @{name}",
"account.mute_short": "Sgugem",
"account.muted": "Yettwasgugem",
"account.open_original_page": "Ldi asebter anasli",
"account.posts": "Tisuffaɣ",
@ -70,6 +72,7 @@
"column.community": "Tasuddemt tadigant",
"column.directory": "Inig deg imaɣnuten",
"column.domain_blocks": "Taɣulin yeffren",
"column.favourites": "Imenyafen",
"column.follow_requests": "Isuturen n teḍfeṛt",
"column.home": "Agejdan",
"column.lists": "Tibdarin",
@ -90,6 +93,7 @@
"community.column_settings.remote_only": "Anmeggag kan",
"compose.language.change": "Beddel tutlayt",
"compose.language.search": "Nadi tutlayin …",
"compose.published.open": "Ldi",
"compose_form.direct_message_warning_learn_more": "Issin ugar",
"compose_form.encryption_warning": "Posts on Mastodon are not end-to-end encrypted. Do not share any dangerous information over Mastodon.",
"compose_form.hashtag_warning": "This post won't be listed under any hashtag as it is unlisted. Only public posts can be searched by hashtag.",
@ -177,6 +181,7 @@
"errors.unexpected_crash.copy_stacktrace": "Nɣel stacktrace ɣef wafus",
"errors.unexpected_crash.report_issue": "Mmel ugur",
"explore.search_results": "Igemmaḍ n unadi",
"explore.suggested_follows": "Imdanen",
"explore.title": "Snirem",
"explore.trending_links": "Isallen",
"explore.trending_statuses": "Tisuffiɣin",
@ -184,6 +189,8 @@
"filter_modal.added.settings_link": "asebter n yiɣewwaṛen",
"filter_modal.select_filter.prompt_new": "Taggayt tamaynutt : {name}",
"filter_modal.select_filter.search": "Nadi neɣ snulfu-d",
"firehose.all": "Akk",
"firehose.local": "Deg uqeddac-ayi",
"follow_request.authorize": "Ssireg",
"follow_request.reject": "Agi",
"footer.about": "Γef",
@ -252,6 +259,7 @@
"lightbox.expand": "Simeɣer tamnaḍt n uskan n tugna",
"lightbox.next": "Γer zdat",
"lightbox.previous": "Γer deffir",
"link_preview.author": "S-ɣur {name}",
"lists.account.add": "Rnu ɣer tebdart",
"lists.account.remove": "Kkes seg tebdart",
"lists.delete": "Kkes tabdart",
@ -280,6 +288,7 @@
"navigation_bar.domain_blocks": "Tiɣula yeffren",
"navigation_bar.edit_profile": "Ẓreg amaɣnu",
"navigation_bar.explore": "Snirem",
"navigation_bar.favourites": "Imenyafen",
"navigation_bar.filters": "Awalen i yettwasgugmen",
"navigation_bar.follow_requests": "Isuturen n teḍfeṛt",
"navigation_bar.follows_and_followers": "Imeḍfaṛen akked wid i teṭṭafaṛeḍ",
@ -303,6 +312,7 @@
"notifications.clear": "Sfeḍ tilɣa",
"notifications.clear_confirmation": "Tebɣiḍ s tidet ad tekkseḍ akk tilɣa-inek·em i lebda?",
"notifications.column_settings.alert": "Tilɣa n tnarit",
"notifications.column_settings.favourite": "Imenyafen:",
"notifications.column_settings.filter_bar.advanced": "Ssken-d meṛṛa tiggayin",
"notifications.column_settings.filter_bar.category": "Iri n usizdeg uzrib",
"notifications.column_settings.follow": "Imeḍfaṛen imaynuten:",
@ -316,6 +326,7 @@
"notifications.column_settings.status": "Tiẓenẓunin timaynutin:",
"notifications.filter.all": "Akk",
"notifications.filter.boosts": "Seǧhed",
"notifications.filter.favourites": "Imenyafen",
"notifications.filter.follows": "Yeṭafaṛ",
"notifications.filter.mentions": "Abdar",
"notifications.filter.polls": "Igemmaḍ n usenqed",
@ -328,6 +339,7 @@
"notifications_permission_banner.title": "Ur zeggel acemma",
"onboarding.actions.go_to_explore": "See what's trending",
"onboarding.actions.go_to_home": "Go to your home feed",
"onboarding.compose.template": "Azul a #Mastodon!",
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Popular on Mastodon",
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
@ -396,6 +408,7 @@
"server_banner.learn_more": "Issin ugar",
"sign_in_banner.create_account": "Snulfu-d amiḍan",
"sign_in_banner.sign_in": "Qqen",
"sign_in_banner.sso_redirect": "Qqen neɣ jerred",
"status.admin_status": "Open this status in the moderation interface",
"status.block": "Seḥbes @{name}",
"status.bookmark": "Creḍ",

View File

@ -37,7 +37,7 @@
"account.following": "팔로잉",
"account.following_counter": "{counter} 팔로잉",
"account.follows.empty": "이 사용자는 아직 아무도 팔로우하고 있지 않습니다.",
"account.follows_you": " 팔로우합니다",
"account.follows_you": "나를 팔로우합니다",
"account.go_to_profile": "프로필로 이동",
"account.hide_reblogs": "@{name}의 부스트를 숨기기",
"account.in_memoriam": "고인의 계정입니다.",
@ -201,7 +201,7 @@
"dismissable_banner.community_timeline": "여기 있는 것들은 계정이 {domain}에 있는 사람들의 최근 공개 게시물들입니다.",
"dismissable_banner.dismiss": "지우기",
"dismissable_banner.explore_links": "이 소식들은 오늘 소셜 웹에서 가장 많이 공유된 내용들입니다. 새 소식을 더 많은 사람들이 공유할수록 높은 순위가 됩니다.",
"dismissable_banner.explore_statuses": "오늘 소셜 웹에서 호응을 얻고 있는게시물입니다. 부스트와 좋아요를 받는 새로운 게시물이 높은 순위가 됩니다",
"dismissable_banner.explore_statuses": "오늘 소셜 웹에서 호응을 얻고 있는 게시물이에요. 부스트와 좋아요를 받는 새로운 게시물이 높은 순위가 돼요.",
"dismissable_banner.explore_tags": "이 해시태그들은 이 서버와 분산화된 네트워크의 다른 서버에서 사람들의 인기를 끌고 있는 것들입니다.",
"dismissable_banner.public_timeline": "이것들은 {domain}에 있는 사람들이 팔로우한 사람들의 최신 게시물들입니다.",
"embed.instructions": "아래의 코드를 복사하여 대화를 원하는 곳으로 공유하세요.",
@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "어느것이든",
"hashtag.column_settings.tag_mode.none": "이것들을 제외하고",
"hashtag.column_settings.tag_toggle": "추가 해시태그를 이 컬럼에 추가합니다",
"hashtag.counter_by_accounts": "{count, plural, other {{counter} 명의 참여자}}",
"hashtag.counter_by_uses": "{count, plural, other {{counter} 개의 게시물}}",
"hashtag.counter_by_uses_today": "오늘 {count, plural, other {{counter} 개의 게시물}}",
"hashtag.follow": "해시태그 팔로우",
"hashtag.unfollow": "해시태그 팔로우 해제",
"home.actions.go_to_explore": "무엇이 유행인지 보기",
@ -303,7 +306,7 @@
"home.column_settings.show_reblogs": "부스트 표시",
"home.column_settings.show_replies": "답글 표시",
"home.explore_prompt.body": "홈 피드에는 내가 팔로우한 해시태그 그리고 팔로우한 사람과 부스트가 함께 나타나요. 너무 고요하게 느껴진다면, 다음 것들을 살펴볼 수 있어요:",
"home.explore_prompt.title": "이것은 마스토돈에 있는 내 본거지입니다.",
"home.explore_prompt.title": "여기가 Mastodon 이용의 본거지예요.",
"home.hide_announcements": "공지사항 숨기기",
"home.show_announcements": "공지사항 보기",
"interaction_modal.description.favourite": "마스토돈 계정을 통해, 게시물을 좋아하는 것으로 작성자에게 호의를 표하고 나중에 보기 위해 저장할 수 있습니다.",
@ -317,7 +320,7 @@
"interaction_modal.on_this_server": "이 서버에서",
"interaction_modal.sign_in": "이 서버에 로그인되어 있지 않습니다. 계정이 어디에 속해 있나요?",
"interaction_modal.sign_in_hint": "팁: 여러분이 가입한 사이트입니다. 만약 기억이 나지 않는다면 가입환영 이메일을 찾아보는 것도 좋습니다. 전체 사용자이름(예: @mastodon@mastodon.social)을 넣어도 됩니다!",
"interaction_modal.title.favourite": "{name} 님의 게시물을 좋아하기",
"interaction_modal.title.favourite": "{name} 님의 게시물을 좋아하기",
"interaction_modal.title.follow": "{name} 님을 팔로우",
"interaction_modal.title.reblog": "{name} 님의 게시물을 부스트",
"interaction_modal.title.reply": "{name} 님의 게시물에 답글",
@ -620,7 +623,7 @@
"status.filter": "이 게시물을 필터",
"status.filtered": "필터로 걸러짐",
"status.hide": "게시물 숨기기",
"status.history.created": "{name} 님이 {date}에 생성함",
"status.history.created": "{name} 님이 {date}에 처음 게시함",
"status.history.edited": "{name} 님이 {date}에 수정함",
"status.load_more": "더 보기",
"status.media.open": "클릭하여 열기",

View File

@ -17,6 +17,7 @@
"account.badges.group": "Grupa",
"account.block": "Bloķēt @{name}",
"account.block_domain": "Bloķēt domēnu {domain}",
"account.block_short": "Bloķēt",
"account.blocked": "Bloķēts",
"account.browse_more_on_origin_server": "Pārlūkot vairāk sākotnējā profilā",
"account.cancel_follow_request": "Atsaukt sekošanas pieprasījumu",
@ -48,7 +49,9 @@
"account.mention": "Pieminēt @{name}",
"account.moved_to": "{name} norādīja, ka viņu jaunais konts tagad ir:",
"account.mute": "Apklusināt @{name}",
"account.mute_short": "Apklusināt",
"account.muted": "Apklusināts",
"account.no_bio": "Apraksts nav sniegts.",
"account.open_original_page": "Atvērt oriģinālo lapu",
"account.posts": "Ieraksti",
"account.posts_with_replies": "Ieraksti un atbildes",
@ -104,6 +107,7 @@
"column.direct": "Privāti pieminēti",
"column.directory": "Pārlūkot profilus",
"column.domain_blocks": "Bloķētie domēni",
"column.favourites": "Iecienīti",
"column.follow_requests": "Sekošanas pieprasījumi",
"column.home": "Sākums",
"column.lists": "Saraksti",
@ -124,6 +128,7 @@
"community.column_settings.remote_only": "Tikai attālinātie",
"compose.language.change": "Mainīt valodu",
"compose.language.search": "Meklēt valodas...",
"compose.published.open": "Atvērt",
"compose_form.direct_message_warning_learn_more": "Uzzināt vairāk",
"compose_form.encryption_warning": "Ziņas vietnē Mastodon nav pilnībā šifrētas. Nedalies ar sensitīvu informāciju caur Mastodon.",
"compose_form.hashtag_warning": "Šī ziņa netiks norādīta zem nevienas atsauces, jo tā nav publiska. Tikai publiskās ziņās var meklēt pēc atsauces.",
@ -251,6 +256,7 @@
"filter_modal.select_filter.subtitle": "Izmanto esošu kategoriju vai izveido jaunu",
"filter_modal.select_filter.title": "Filtrēt šo ziņu",
"filter_modal.title.status": "Filtrēt ziņu",
"firehose.remote": "Citi serveri",
"follow_request.authorize": "Autorizēt",
"follow_request.reject": "Noraidīt",
"follow_requests.unlocked_explanation": "Lai gan tavs konts nav bloķēts, {domain} darbinieki iedomājās, ka, iespējams, vēlēsies pārskatīt pieprasījumus no šiem kontiem.",
@ -452,6 +458,7 @@
"picture_in_picture.restore": "Novietot atpakaļ",
"poll.closed": "Pabeigta",
"poll.refresh": "Atsvaidzināt",
"poll.reveal": "Skatīt rezultātus",
"poll.total_people": "{count, plural, zero {# cilvēku} one {# persona} other {# cilvēki}}",
"poll.total_votes": "{count, plural, zero {# balsojumu} one {# balsojums} other {# balsojumi}}",
"poll.vote": "Balsot",

View File

@ -21,6 +21,7 @@
"account.blocked": "Disekat",
"account.browse_more_on_origin_server": "Layari selebihnya di profil asal",
"account.cancel_follow_request": "Menarik balik permintaan mengikut",
"account.direct": "Sebut secara persendirian @{name}",
"account.disable_notifications": "Berhenti maklumkan saya apabila @{name} mengirim hantaran",
"account.domain_blocked": "Domain disekat",
"account.edit_profile": "Sunting profil",
@ -39,6 +40,7 @@
"account.follows_you": "Mengikuti anda",
"account.go_to_profile": "Pergi ke profil",
"account.hide_reblogs": "Sembunyikan galakan daripada @{name}",
"account.in_memoriam": "Dalam Memoriam.",
"account.joined_short": "Menyertai",
"account.languages": "Tukar bahasa yang dilanggan",
"account.link_verified_on": "Pemilikan pautan ini telah disemak pada {date}",
@ -47,7 +49,10 @@
"account.mention": "Sebut @{name}",
"account.moved_to": "{name} telah menandakan bahawa akaun baru mereka sekarang ialah:",
"account.mute": "Bisukan @{name}",
"account.mute_notifications_short": "Redam pemberitahuan",
"account.mute_short": "Redam",
"account.muted": "Dibisukan",
"account.no_bio": "Tiada penerangan diberikan.",
"account.open_original_page": "Buka halaman asal",
"account.posts": "Hantaran",
"account.posts_with_replies": "Hantaran dan balasan",
@ -70,6 +75,8 @@
"admin.dashboard.retention.average": "Purata",
"admin.dashboard.retention.cohort": "Bulan pendaftaran",
"admin.dashboard.retention.cohort_size": "Pengguna baru",
"admin.impact_report.instance_accounts": "Profil akaun ini akan dipadamkan",
"admin.impact_report.title": "Ringkasan kesan",
"alert.rate_limited.message": "Sila cuba semula selepas {retry_time, time, medium}.",
"alert.rate_limited.title": "Kadar terhad",
"alert.unexpected.message": "Berlaku ralat di luar jangkaan.",
@ -102,6 +109,7 @@
"column.community": "Garis masa tempatan",
"column.directory": "Layari profil",
"column.domain_blocks": "Domain disekat",
"column.favourites": "Kegemaran",
"column.follow_requests": "Permintaan ikutan",
"column.home": "Laman Utama",
"column.lists": "Senarai",
@ -122,6 +130,8 @@
"community.column_settings.remote_only": "Jauh sahaja",
"compose.language.change": "Tukar bahasa",
"compose.language.search": "Cari bahasa...",
"compose.published.body": "Pos telah diterbitkan.",
"compose.published.open": "Buka",
"compose_form.direct_message_warning_learn_more": "Ketahui lebih lanjut",
"compose_form.encryption_warning": "Hantaran pada Mastodon tidak disulitkan hujung ke hujung. Jangan berkongsi sebarang maklumat sensitif melalui Mastodon.",
"compose_form.hashtag_warning": "Hantaran ini tidak akan disenaraikan di bawah mana-mana tanda pagar kerana ia tidak tersenarai. Hanya hantaran awam sahaja boleh dicari menggunakan tanda pagar.",
@ -158,12 +168,14 @@
"confirmations.discard_edit_media.message": "Anda belum menyimpan perubahan pada penerangan atau pratonton media. Anda ingin membuangnya?",
"confirmations.domain_block.confirm": "Sekat keseluruhan domain",
"confirmations.domain_block.message": "Adakah anda betul-betul, sungguh-sungguh pasti anda ingin menyekat keseluruhan {domain}? Selalunya, beberapa sekatan atau pembisuan tersasar sudah memadai dan lebih diutamakan. Anda tidak akan nampak kandungan daripada domain tersebut di mana-mana garis masa awam mahupun pemberitahuan anda. Pengikut anda daripada domain tersebut juga akan dibuang.",
"confirmations.edit.confirm": "Sunting",
"confirmations.logout.confirm": "Log keluar",
"confirmations.logout.message": "Adakah anda pasti anda ingin log keluar?",
"confirmations.mute.confirm": "Bisukan",
"confirmations.mute.explanation": "Ini akan menyembunyikan hantaran daripada mereka dan juga hantaran yang menyebut mereka, tetapi ia masih membenarkan mereka melihat hantaran anda dan mengikuti anda.",
"confirmations.mute.message": "Adakah anda pasti anda ingin membisukan {name}?",
"confirmations.redraft.confirm": "Padam & rangka semula",
"confirmations.redraft.message": "Adakah anda pasti anda ingin memadam pos ini dan merangkanya semula? Kegemaran dan galakan akan hilang, dan balasan ke pos asal akan menjadi yatim.",
"confirmations.reply.confirm": "Balas",
"confirmations.reply.message": "Membalas sekarang akan menulis ganti mesej yang anda sedang karang. Adakah anda pasti anda ingin teruskan?",
"confirmations.unfollow.confirm": "Nyahikut",
@ -173,6 +185,7 @@
"conversation.open": "Lihat perbualan",
"conversation.with": "Dengan {names}",
"copypaste.copied": "Disalin",
"copypaste.copy_to_clipboard": "Salin ke papan klip",
"directory.federated": "Dari fediverse yang diketahui",
"directory.local": "Dari {domain} sahaja",
"directory.new_arrivals": "Ketibaan baharu",
@ -182,7 +195,9 @@
"dismissable_banner.community_timeline": "Inilah hantaran awam terkini daripada orang yang akaun dihos oleh {domain}.",
"dismissable_banner.dismiss": "Ketepikan",
"dismissable_banner.explore_links": "Berita-berita ini sedang dibualkan oleh orang di pelayar ini dan pelayar lain dalam rangkaian terpencar sekarang.",
"dismissable_banner.explore_statuses": "Ini adalah pos dari seluruh web sosial yang semakin menarik perhatian hari ini. Pos baharu dengan lebih banyak rangsangan dan kegemaran diberi kedudukan lebih tinggi.",
"dismissable_banner.explore_tags": "Tanda-tanda pagar ini daripada pelayar ini dan pelayar lain dalam rangkaian terpencar sedang hangat pada pelayar ini sekarang.",
"dismissable_banner.public_timeline": "Ini ialah pos awam terbaharu daripada orang di web sosial yang diikuti oleh orang di {domain}.",
"embed.instructions": "Benam hantaran ini di laman sesawang anda dengan menyalin kod berikut.",
"embed.preview": "Begini rupanya nanti:",
"emoji_button.activity": "Aktiviti",
@ -224,6 +239,7 @@
"errors.unexpected_crash.copy_stacktrace": "Salin surih tindanan ke papan keratan",
"errors.unexpected_crash.report_issue": "Laporkan masalah",
"explore.search_results": "Hasil carian",
"explore.suggested_follows": "Orang",
"explore.title": "Terokai",
"explore.trending_links": "Baru",
"explore.trending_statuses": "Hantar",
@ -244,6 +260,9 @@
"filter_modal.select_filter.subtitle": "Gunakan kumpulan yang sedia ada atau mencipta kumpulan baru",
"filter_modal.select_filter.title": "Tapiskan hantaran ini",
"filter_modal.title.status": "Tapiskan sesuatu hantaran",
"firehose.all": "Semua",
"firehose.local": "Server ini",
"firehose.remote": "Server lain",
"follow_request.authorize": "Benarkan",
"follow_request.reject": "Tolak",
"follow_requests.unlocked_explanation": "Walaupun akaun anda tidak dikunci, kakitangan {domain} merasakan anda mungkin ingin menyemak permintaan ikutan daripada akaun ini secara manual.",
@ -269,14 +288,19 @@
"hashtag.column_settings.tag_toggle": "Sertakan tag tambahan untuk lajur ini",
"hashtag.follow": "Ikuti hashtag",
"hashtag.unfollow": "Nyahikut tanda pagar",
"home.actions.go_to_explore": "Lihat apa yand sedang tren",
"home.actions.go_to_suggestions": "Cari orang untuk diikuti",
"home.column_settings.basic": "Asas",
"home.column_settings.show_reblogs": "Tunjukkan galakan",
"home.column_settings.show_replies": "Tunjukkan balasan",
"home.explore_prompt.title": "Ini adalah pusat operasi anda dalam Mastodon.",
"home.hide_announcements": "Sembunyikan pengumuman",
"home.show_announcements": "Tunjukkan pengumuman",
"interaction_modal.description.follow": "Dengan akaun pada Mastodon, anda boleh mengikut {name} untuk menerima hantaran mereka di suapan rumah anda.",
"interaction_modal.description.reblog": "Dengan akaun pada Mastodon, anda boleh menggalakkan hantaran ini untuk dikongsi dengan pengikut anda.",
"interaction_modal.description.reply": "Dengan akaun pada Mastodon, anda boleh membalas kepada hantaran ini.",
"interaction_modal.login.action": "Bawa saya pulang rumah",
"interaction_modal.no_account_yet": "Tak di Mastadon?",
"interaction_modal.on_another_server": "Di pelayan lain",
"interaction_modal.on_this_server": "Pada pelayan ini",
"interaction_modal.title.follow": "Ikuti {name}",
@ -294,6 +318,7 @@
"keyboard_shortcuts.direct": "to open direct messages column",
"keyboard_shortcuts.down": "to move down in the list",
"keyboard_shortcuts.enter": "Buka hantaran",
"keyboard_shortcuts.favourites": "Buka senarai kegemaran",
"keyboard_shortcuts.federated": "to open federated timeline",
"keyboard_shortcuts.heading": "Pintasan papan kekunci",
"keyboard_shortcuts.home": "to open home timeline",
@ -324,6 +349,7 @@
"lightbox.previous": "Sebelumnya",
"limited_account_hint.action": "Paparkan profil",
"limited_account_hint.title": "Profil ini telah disembunyikan oleh moderator {domain}.",
"link_preview.author": "Dengan {name}",
"lists.account.add": "Tambah ke senarai",
"lists.account.remove": "Buang daripada senarai",
"lists.delete": "Padam senarai",
@ -353,6 +379,7 @@
"navigation_bar.domain_blocks": "Domain disekat",
"navigation_bar.edit_profile": "Sunting profil",
"navigation_bar.explore": "Teroka",
"navigation_bar.favourites": "Kegemaran",
"navigation_bar.filters": "Perkataan yang dibisukan",
"navigation_bar.follow_requests": "Permintaan ikutan",
"navigation_bar.followed_tags": "Ikuti hashtag",
@ -382,6 +409,7 @@
"notifications.column_settings.admin.report": "Laporan baru:",
"notifications.column_settings.admin.sign_up": "Pendaftaran baru:",
"notifications.column_settings.alert": "Pemberitahuan atas meja",
"notifications.column_settings.favourite": "Kegemaran:",
"notifications.column_settings.filter_bar.advanced": "Papar semua kategori",
"notifications.column_settings.filter_bar.category": "Bar penapis pantas",
"notifications.column_settings.filter_bar.show_bar": "Paparkan bar penapis",
@ -399,6 +427,7 @@
"notifications.column_settings.update": "Suntingan:",
"notifications.filter.all": "Semua",
"notifications.filter.boosts": "Galakan",
"notifications.filter.favourites": "Kegemaran",
"notifications.filter.follows": "Ikutan",
"notifications.filter.mentions": "Sebutan",
"notifications.filter.polls": "Keputusan undian",
@ -412,22 +441,36 @@
"notifications_permission_banner.enable": "Dayakan pemberitahuan atas meja",
"notifications_permission_banner.how_to_control": "Untuk mendapat pemberitahuan ketika Mastodon tidak dibuka, dayakan pemberitahuan atas meja. Anda boleh mengawal jenis interaksi mana yang menjana pemberitahuan atas meja melalui butang {icon} di atas setelah ia didayakan.",
"notifications_permission_banner.title": "Jangan terlepas apa-apa",
"onboarding.action.back": "Bawa saya kembali",
"onboarding.actions.back": "Bawa saya kembali",
"onboarding.actions.go_to_explore": "See what's trending",
"onboarding.actions.go_to_home": "Go to your home feed",
"onboarding.compose.template": "Hello #Mastodon!",
"onboarding.follows.lead": "You curate your own home feed. The more people you follow, the more active and interesting it will be. These profiles may be a good starting point—you can always unfollow them later!",
"onboarding.follows.title": "Popular on Mastodon",
"onboarding.share.next_steps": "Langkah seterusnya yang mungkin:",
"onboarding.share.title": "Berkongsi profil anda",
"onboarding.start.lead": "Your new Mastodon account is ready to go. Here's how you can make the most of it:",
"onboarding.start.skip": "Want to skip right ahead?",
"onboarding.start.title": "Anda telah berjaya!",
"onboarding.steps.follow_people.body": "You curate your own feed. Lets fill it with interesting people.",
"onboarding.steps.follow_people.title": "Follow {count, plural, one {one person} other {# people}}",
"onboarding.steps.publish_status.body": "Say hello to the world.",
"onboarding.steps.publish_status.title": "Buat pos pertama anda",
"onboarding.steps.setup_profile.body": "Others are more likely to interact with you with a filled out profile.",
"onboarding.steps.setup_profile.title": "Customize your profile",
"onboarding.steps.share_profile.body": "Let your friends know how to find you on Mastodon!",
"onboarding.steps.share_profile.title": "Share your profile",
"onboarding.tips.2fa": "<strong>Tahukah anda?</strong> Anda boleh melindungi akaun anda dengan menyediakan pengesahan dua faktor dalam tetapan akaun anda. Ia berfungsi dengan mana-mana aplikasi TOTP pilihan anda, tiada nombor telefon diperlukan!",
"onboarding.tips.accounts_from_other_servers": "<strong>Tahukah anda?</strong> Memandangkan Mastodon tidak berpusat, beberapa profil yang anda temui akan dihoskan pada server selain anda. Namun anda boleh berinteraksi dengan mereka dengan lancar! Server mereka berada di separuh kedua nama pengguna mereka!",
"onboarding.tips.migration": "<strong>Tahukah anda?</strong> Jika anda rasa {domain} bukan pilihan server yang bagus untuk anda pada masa hadapan, anda boleh beralih ke server Mastodon yang lain tanpa kehilangan pengikut anda. Anda juga boleh mengehoskan server anda sendiri!",
"onboarding.tips.verification": "<strong>Tahukah anda?</strong> Anda boleh mengesahkan akaun anda dengan meletakkan pautan ke profil Mastodon anda pada tapak web anda sendiri dan menambah tapak web pada profil anda. Tiada bayaran atau dokumen diperlukan!",
"password_confirmation.exceeds_maxlength": "Pengesahan kata laluan melebihi panjang kata laluan maksimum",
"password_confirmation.mismatching": "Pengesahan kata laluan tidak sepadan",
"picture_in_picture.restore": "Letak semula",
"poll.closed": "Ditutup",
"poll.refresh": "Muat semula",
"poll.reveal": "Hasil carian",
"poll.total_people": "{count, plural, other {# orang}}",
"poll.total_votes": "{count, plural, other {# undian}}",
"poll.vote": "Undi",
@ -480,6 +523,8 @@
"report.placeholder": "Ulasan tambahan",
"report.reasons.dislike": "Saya tidak suka",
"report.reasons.dislike_description": "Inilah sesuatu yang anda tidak ingin lihat",
"report.reasons.legal": "Ia haram",
"report.reasons.legal_description": "Anda percaya ia melanggar undang-undang negara anda atau negara server",
"report.reasons.other": "Inilah sesuatu yang lain",
"report.reasons.other_description": "Isu ini tidak sesuai untuk kategori lain",
"report.reasons.spam": "Inilah spam",
@ -499,12 +544,22 @@
"report.unfollow": "Nyahikut @{name}",
"report.unfollow_explanation": "Anda sedang mengikuti akaun ini. Untuk memadam siaran mereka daripada suapan berita anda, nyahikutkan mereka.",
"report_notification.attached_statuses": "{count, plural, other {{count} hantaran}} dilampirkan",
"report_notification.categories.legal": "Sah",
"report_notification.categories.other": "Lain-lain",
"report_notification.categories.spam": "Spam",
"report_notification.categories.violation": "Langgaran peraturan",
"report_notification.open": "Buka laporan",
"search.no_recent_searches": "Tiada carian terkini",
"search.placeholder": "Cari",
"search.quick_action.account_search": "Pos sepadan {x}",
"search.quick_action.go_to_account": "Pergi ke profil {x}",
"search.quick_action.go_to_hashtag": "Pergi ke hashtag {x}",
"search.quick_action.open_url": "Buka URL dalam Mastadon",
"search.quick_action.status_search": "Pos sepadan {x}",
"search.search_or_paste": "Cari atau tampal URL",
"search_popout.quick_actions": "Tindakan pantas",
"search_popout.recent": "Carian terkini",
"search_results.accounts": "Profil",
"search_results.all": "Semua",
"search_results.hashtags": "Tanda pagar",
"search_results.nothing_found": "Tidak dapat menemui apa-apa untuk istilah carian tersebut",
@ -520,6 +575,7 @@
"server_banner.server_stats": "Statistik pelayan:",
"sign_in_banner.create_account": "Cipta akaun",
"sign_in_banner.sign_in": "Daftar masuk",
"sign_in_banner.sso_redirect": "Log masuk atau mendaftar",
"status.admin_account": "Buka antara muka penyederhanaan untuk @{name}",
"status.admin_domain": "antara muka penyederhanaan",
"status.admin_status": "Buka hantaran ini dalam antara muka penyederhanaan",
@ -534,11 +590,15 @@
"status.edited": "Disunting {date}",
"status.edited_x_times": "Disunting {count, plural, other {{count} kali}}",
"status.embed": "Benaman",
"status.favourite": "Kegemaran",
"status.filter": "Tapiskan hantaran ini",
"status.filtered": "Ditapis",
"status.hide": "Sembunyikan pos",
"status.history.created": "{name} mencipta pada {date}",
"status.history.edited": "{name} menyunting pada {date}",
"status.load_more": "Muatkan lagi",
"status.media.open": "Klik untuk membuka",
"status.media.show": "Klik untuk menunjukkan",
"status.media_hidden": "Media disembunyikan",
"status.mention": "Sebut @{name}",
"status.more": "Lagi",
@ -569,6 +629,7 @@
"status.title.with_attachments": "{user} posted {attachmentCount, plural, one {an attachment} other {# attachments}}",
"status.translate": "Menterjemah",
"status.translated_from_with": "Diterjemah daripada {lang} dengan {provider}",
"status.uncached_media_warning": "Pratonton tidak tersedia",
"status.unmute_conversation": "Nyahbisukan perbualan",
"status.unpin": "Nyahsemat daripada profil",
"subscribed_languages.lead": "Hanya hantaran dalam bahasa-bahasa terpilih akan dipaparkan pada garis masa rumah dan senarai selepas perubahan. Pilih tiada untuk menerima hantaran dalam semua bahasa.",
@ -616,6 +677,7 @@
"upload_modal.preview_label": "Pratonton ({ratio})",
"upload_progress.label": "Memuat naik...",
"upload_progress.processing": "Memproses…",
"username.taken": "Nama pengguna tersebut sudah digunakan. Sila cuba lagi",
"video.close": "Tutup video",
"video.download": "Muat turun fail",
"video.exit_fullscreen": "Keluar skrin penuh",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Een van deze",
"hashtag.column_settings.tag_mode.none": "Geen van deze",
"hashtag.column_settings.tag_toggle": "Additionele tags aan deze kolom toevoegen",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} deelnemer} other {{counter} deelnemers}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} bericht} other {{counter} berichten}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} bericht} other {{counter} berichten}} vandaag",
"hashtag.follow": "Hashtag volgen",
"hashtag.unfollow": "Hashtag ontvolgen",
"home.actions.go_to_explore": "De huidige trends bekijken",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Kva som helst av desse",
"hashtag.column_settings.tag_mode.none": "Ingen av desse",
"hashtag.column_settings.tag_toggle": "Inkluder fleire emneord for denne kolonna",
"hashtag.counter_by_accounts": "{count, plural,one {{counter} deltakar} other {{counter} deltakarar}}",
"hashtag.counter_by_uses": "{count, plural,one {{counter} innlegg} other {{counter} innlegg}}",
"hashtag.counter_by_uses_today": "{count, plural,one {{counter} innlegg} other {{counter} innlegg}} i dag",
"hashtag.follow": "Fylg emneknagg",
"hashtag.unfollow": "Slutt å fylgje emneknaggen",
"home.actions.go_to_explore": "Sjå kva som er populært",

View File

@ -13,7 +13,7 @@
"about.rules": "Regler for serveren",
"account.account_note_header": "Notat",
"account.add_or_remove_from_list": "Legg til eller fjern fra lister",
"account.badges.bot": "Robot",
"account.badges.bot": "Automatisert",
"account.badges.group": "Gruppe",
"account.block": "Blokker @{name}",
"account.block_domain": "Blokker domenet {domain}",
@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Enhver av disse",
"hashtag.column_settings.tag_mode.none": "Ingen av disse",
"hashtag.column_settings.tag_toggle": "Include additional tags in this column",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} deltaker} other {{counter} deltakere}}",
"hashtag.counter_by_uses": "{count, plural, one {ett innlegg} other {{counter} innlegg}}",
"hashtag.counter_by_uses_today": "{count, plural, one {ett innlegg} other {{counter} innlegg}} i dag",
"hashtag.follow": "Følg emneknagg",
"hashtag.unfollow": "Slutt å følge emneknagg",
"home.actions.go_to_explore": "Se hva som er populært",
@ -302,6 +305,7 @@
"home.column_settings.basic": "Enkelt",
"home.column_settings.show_reblogs": "Vis fremhevinger",
"home.column_settings.show_replies": "Vis svar",
"home.explore_prompt.body": "Tidslinjen din inneholder en blanding av innlegg fra emneknagger du har valgt å følge, personene du har valgt å følge, og innleggene de fremhever. Hvis det føles for stille, kan det være lurt å:",
"home.explore_prompt.title": "Dette er hjemmet ditt i Mastodon.",
"home.hide_announcements": "Skjul kunngjøring",
"home.show_announcements": "Vis kunngjøring",
@ -309,8 +313,13 @@
"interaction_modal.description.follow": "Med en konto på Mastodon, kan du følge {name} for å få innleggene deres i tidslinjen din.",
"interaction_modal.description.reblog": "Med en konto på Mastodon, kan du fremheve dette innlegget for å dele det med dine egne følgere.",
"interaction_modal.description.reply": "Med en konto på Mastodon, kan du svare på dette innlegget.",
"interaction_modal.login.action": "Ta meg hjem",
"interaction_modal.login.prompt": "Domenet til serveren din, eks. mastodon.social",
"interaction_modal.no_account_yet": "Ennå ikke på Mastodon?",
"interaction_modal.on_another_server": "På en annen server",
"interaction_modal.on_this_server": "På denne serveren",
"interaction_modal.sign_in": "Du er ikke logget inn på denne serveren. Hvor har du kontoen din?",
"interaction_modal.sign_in_hint": "Tips: Det er på nettstedet der du registrerte deg. Hvis du ikke husker det, kan du se etter velkomst e-posten i innboksen. Du kan også skrive inn hele brukernavnet ditt! (eks. @Mastodon@mastodon.social)",
"interaction_modal.title.favourite": "Favorittmarker innlegget til {name}",
"interaction_modal.title.follow": "Følg {name}",
"interaction_modal.title.reblog": "Fremhev {name} sitt innlegg",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Dowolne",
"hashtag.column_settings.tag_mode.none": "Żadne",
"hashtag.column_settings.tag_toggle": "Include additional tags in this column",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} uczestnik} few {{counter} uczestnicy} many {{counter} uczestników} other {{counter} uczestników}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} wpis} few {{counter} wpisy} many {{counter} wpisów} other {{counter} wpisów}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} wpis} few {{counter} wpisy} many {{counter} wpisów} other {{counter} wpisów}} z dzisiaj",
"hashtag.follow": "Obserwuj hasztag",
"hashtag.unfollow": "Przestań obserwować hashtag",
"home.actions.go_to_explore": "Zobacz, co jest teraz popularne",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Qualquer destes",
"hashtag.column_settings.tag_mode.none": "Nenhum destes",
"hashtag.column_settings.tag_toggle": "Incluir etiquetas adicionais para esta coluna",
"hashtag.counter_by_accounts": "{count, plural,one {{counter} participante} other {{counter} participantes}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} publicação} other {{counter} publicações}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} publicação} other {{counter} publicações}} hoje",
"hashtag.follow": "Seguir #etiqueta",
"hashtag.unfollow": "Deixar de seguir #etiqueta",
"home.actions.go_to_explore": "Veja as tendências atuais",
@ -598,6 +601,7 @@
"server_banner.server_stats": "Estatísticas do servidor:",
"sign_in_banner.create_account": "Criar conta",
"sign_in_banner.sign_in": "Iniciar sessão",
"sign_in_banner.sso_redirect": "Iniciar sessão ou inscrever-se",
"sign_in_banner.text": "Inicie sessão para seguir perfis ou hashtags, assinalar como favorito, partilhar ou responder a publicações. Pode ainda interagir através da sua conta noutro servidor.",
"status.admin_account": "Abrir a interface de moderação para @{name}",
"status.admin_domain": "Abrir interface de moderação para {domain}",

View File

@ -358,6 +358,7 @@
"lightbox.previous": "Predchádzajúci",
"limited_account_hint.action": "Ukáž profil aj tak",
"limited_account_hint.title": "Tento profil bol skrytý moderátormi stránky {domain}.",
"link_preview.author": "Podľa {name}",
"lists.account.add": "Pridaj do zoznamu",
"lists.account.remove": "Odober zo zoznamu",
"lists.delete": "Vymaž list",
@ -574,6 +575,7 @@
"server_banner.server_stats": "Serverové štatistiky:",
"sign_in_banner.create_account": "Vytvor účet",
"sign_in_banner.sign_in": "Prihlás sa",
"sign_in_banner.sso_redirect": "Prihlás sa, alebo zaregistruj",
"status.admin_account": "Otvor moderovacie rozhranie užívateľa @{name}",
"status.admin_status": "Otvor tento príspevok v moderovacom rozhraní",
"status.block": "Blokuj @{name}",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Bilo koje od ovih",
"hashtag.column_settings.tag_mode.none": "Nijedan od ovih",
"hashtag.column_settings.tag_toggle": "Uključi dodatne oznake za ovu kolonu",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} učesnik} few {{counter} učesnika} other {{counter} učesnika}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} objava} few {{counter} objave} other {{counter} objava}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} objava} few {{counter} objave} other {{counter} objava}} danas",
"hashtag.follow": "Zaprati heš oznaku",
"hashtag.unfollow": "Otprati heš oznaku",
"home.actions.go_to_explore": "Pogledaj šta je u trendu",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Било које од ових",
"hashtag.column_settings.tag_mode.none": "Ниједан од ових",
"hashtag.column_settings.tag_toggle": "Укључи додатне ознаке за ову колону",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} учесник} few {{counter} учесника} other {{counter} учесника}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} објава} few {{counter} објаве} other {{counter} објава}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} објава} few {{counter} објаве} other {{counter} објава}} данас",
"hashtag.follow": "Запрати хеш ознаку",
"hashtag.unfollow": "Отпрати хеш ознаку",
"home.actions.go_to_explore": "Погледај шта је у тренду",

View File

@ -292,6 +292,9 @@
"hashtag.column_settings.tag_mode.any": "Någon av dessa",
"hashtag.column_settings.tag_mode.none": "Ingen av dessa",
"hashtag.column_settings.tag_toggle": "Include additional tags in this column",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} deltagare} other {{counter} deltagare}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} inlägg} other {{counter} inlägg}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} inlägg} other {{counter} inlägg}} i dag",
"hashtag.follow": "Följ hashtagg",
"hashtag.unfollow": "Avfölj hashtagg",
"home.actions.go_to_suggestions": "Hitta personer att följa",
@ -365,7 +368,7 @@
"lists.replies_policy.title": "Visa svar till:",
"lists.search": "Sök bland personer du följer",
"lists.subheading": "Dina listor",
"load_pending": "{count, plural, other {# objekt}}",
"load_pending": "{count, plural, one {# nytt objekt} other {# nya objekt}}",
"loading_indicator.label": "Laddar...",
"media_gallery.toggle_visible": "Växla synlighet",
"moved_to_account_banner.text": "Ditt konto {disabledAccount} är för närvarande inaktiverat eftersom du flyttat till {movedToAccount}.",
@ -468,8 +471,8 @@
"poll.closed": "Stängd",
"poll.refresh": "Ladda om",
"poll.reveal": "Visa resultat",
"poll.total_people": "{persons, plural, one {# person} other {# personer}}",
"poll.total_votes": "{count, plural, one {1 röst} other {# röster}}",
"poll.total_people": "{count, plural, one {# person} other {# personer}}",
"poll.total_votes": "{count, plural, one {# röst} other {# röster}}",
"poll.vote": "Rösta",
"poll.voted": "Du röstade för detta svar",
"poll.votes": "{votes, plural, one {# röst} other {# röster}}",
@ -560,7 +563,7 @@
"search_results.statuses": "Inlägg",
"search_results.statuses_fts_disabled": "Att söka efter inlägg baserat på innehåll är inte aktiverat på denna Mastodon-server.",
"search_results.title": "Sök efter {q}",
"search_results.total": "{count, number} {count, plural, one {result} other {results}}",
"search_results.total": "{count, number} {count, plural, one {resultat} other {resultat}}",
"server_banner.about_active_users": "Personer som använt denna server de senaste 30 dagarna (månatligt aktiva användare)",
"server_banner.active_users": "aktiva användare",
"server_banner.administered_by": "Administrerad av:",
@ -632,10 +635,10 @@
"tabs_bar.home": "Hem",
"tabs_bar.notifications": "Aviseringar",
"time_remaining.days": "{number, plural, one {# dag} other {# dagar}} kvar",
"time_remaining.hours": "{hours, plural, one {# timme} other {# timmar}} kvar",
"time_remaining.minutes": "{minutes, plural, one {1 minut} other {# minuter}} kvar",
"time_remaining.hours": "{number, plural, one {# timme} other {# timmar}} kvar",
"time_remaining.minutes": "{number, plural, one {# minut} other {# minuter}} kvar",
"time_remaining.moments": "Återstående tillfällen",
"time_remaining.seconds": "{hours, plural, one {# sekund} other {# sekunder}} kvar",
"time_remaining.seconds": "{number, plural, one {# sekund} other {# sekunder}} kvar",
"timeline_hint.remote_resource_not_displayed": "{resource} från andra servrar visas inte.",
"timeline_hint.resources.followers": "Följare",
"timeline_hint.resources.follows": "Följer",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "ใดก็ตามนี้",
"hashtag.column_settings.tag_mode.none": "ไม่ใช่ทั้งหมดนี้",
"hashtag.column_settings.tag_toggle": "รวมแท็กเพิ่มเติมสำหรับคอลัมน์นี้",
"hashtag.counter_by_accounts": "{count, plural, other {{counter} ผู้มีส่วนร่วม}}",
"hashtag.counter_by_uses": "{count, plural, other {{counter} โพสต์}}",
"hashtag.counter_by_uses_today": "{count, plural, other {{counter} โพสต์}}วันนี้",
"hashtag.follow": "ติดตามแฮชแท็ก",
"hashtag.unfollow": "เลิกติดตามแฮชแท็ก",
"home.actions.go_to_explore": "ดูสิ่งที่กำลังนิยม",

View File

@ -200,8 +200,8 @@
"disabled_account_banner.text": "{disabledAccount} hesabınız şu an devre dışı.",
"dismissable_banner.community_timeline": "Bunlar, {domain} sunucusunda hesabı olanların yakın zamandaki herkese açık gönderileridir.",
"dismissable_banner.dismiss": "Yoksay",
"dismissable_banner.explore_links": "Bu haberler, merkeziyetsiz ağın bu ve diğer sunucularındaki insanlar tarafından şimdilerde konuşuluyor.",
"dismissable_banner.explore_statuses": "Merkeziyetsiz ağın bu ve diğer sunucularındaki bu gönderiler, mevcut sunucuda şimdilerde ilgi çekiyorlar.",
"dismissable_banner.explore_links": "Bunlar şimdilerde sosyal ağlarda en çok paylaşılan haberler. Farklı kişilerin yayınladığı daha yeni haberler daha üst sıralarda yer alır.",
"dismissable_banner.explore_statuses": "Bunlar, sosyal ağ genelinde bugün ilgi gören gönderiler. Daha çok yinelenen ve favorilenen yeni gönderiler daha üst sıralarda yer alır.",
"dismissable_banner.explore_tags": "Bu etiketler, merkeziyetsiz ağda bulunan bu ve diğer sunuculardaki insanların şimdilerde ilgisini çekiyor.",
"dismissable_banner.public_timeline": "Bunlar, {domain} üzerindeki insanların, sosyal ağ da takip ettiği insanlarca gönderilen en son ve herkese açık gönderilerdir.",
"embed.instructions": "Aşağıdaki kodu kopyalayarak bu durumu sitenize gömün.",
@ -227,7 +227,7 @@
"empty_column.blocks": "Henüz herhangi bir kullanıcıyı engellemedin.",
"empty_column.bookmarked_statuses": "Henüz yer imine eklediğin toot yok. Bir tanesi yer imine eklendiğinde burada görünür.",
"empty_column.community": "Yerel zaman çizelgesi boş. Daha fazla eğlence için herkese açık bir gönderi paylaşın!",
"empty_column.direct": "Henüz doğrudan değinmeniz yok. Bir tane gönderdiğinizde veya aldığınızda burada listelenecekler.",
"empty_column.direct": "Henüz doğrudan değinmeniz yok. Bir tane gönderdiğinizde veya aldığınızda burada listelenecek.",
"empty_column.domain_blocks": "Henüz engellenmiş bir alan adı yok.",
"empty_column.explore_statuses": "Şu an öne çıkan birşey yok. Daha sonra tekrar bakın!",
"empty_column.favourited_statuses": "Henüz bir gönderiyi favorilerinize eklememişsiniz. Bir gönderiyi favorilerinize eklediğinizde burada görünecek.",
@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Herhangi biri",
"hashtag.column_settings.tag_mode.none": "Bunların hiçbiri",
"hashtag.column_settings.tag_toggle": "Bu sütundaki ek etiketleri içer",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} katılımcı} other {{counter} katılımcı}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} gönderi} other {{counter} gönderi}}",
"hashtag.counter_by_uses_today": "bugün {count, plural, one {{counter} gönderi} other {{counter} gönderi}}",
"hashtag.follow": "Etiketi takip et",
"hashtag.unfollow": "Etiketi takibi bırak",
"home.actions.go_to_explore": "Öne çıkanları gör",
@ -415,7 +418,7 @@
"not_signed_in_indicator.not_signed_in": "Bu kaynağa erişmek için oturum açmanız gerekir.",
"notification.admin.report": "{name}, {target} kişisini bildirdi",
"notification.admin.sign_up": "{name} kaydoldu",
"notification.favourite": "{name} gönderinizden hoşlandı",
"notification.favourite": "{name} gönderinizi beğendi",
"notification.follow": "{name} seni takip etti",
"notification.follow_request": "{name} size takip isteği gönderdi",
"notification.mention": "{name} senden bahsetti",
@ -579,7 +582,7 @@
"search.quick_action.go_to_hashtag": "Etikete git {x}",
"search.quick_action.open_url": "Bağlantıyı Mastodon'da Aç",
"search.quick_action.status_search": "Eşleşen gönderiler {x}",
"search.search_or_paste": "Ara veya Bağlantıyı yapıştır",
"search.search_or_paste": "Ara veya bağlantıyı yapıştır",
"search_popout.quick_actions": "Hızlı eylemler",
"search_popout.recent": "Son aramalar",
"search_results.accounts": "Profiller",
@ -607,7 +610,7 @@
"status.bookmark": "Yer işareti ekle",
"status.cancel_reblog_private": "Yeniden paylaşımı geri al",
"status.cannot_reblog": "Bu gönderi yeniden paylaşılamaz",
"status.copy": "Bağlantı durumunu kopyala",
"status.copy": "Gönderi bağlantısını kopyala",
"status.delete": "Sil",
"status.detailed_status": "Ayrıntılı sohbet görünümü",
"status.direct": "@{name} kullanıcısına özelden değin",
@ -640,7 +643,7 @@
"status.reblogs.empty": "Henüz hiç kimse bu Gönderiyi Yeniden Paylaşmadı. Herhangi bir kullanıcı yeniden paylaştığında burada görüntülenecek.",
"status.redraft": "Sil,Düzenle ve Yeniden paylaş",
"status.remove_bookmark": "Yer işaretini kaldır",
"status.replied_to": "{name} kullanıcısına yanıt verildi",
"status.replied_to": "{name} kullanıcısına yanıt verdi",
"status.reply": "Yanıtla",
"status.replyAll": "Konuyu yanıtla",
"status.report": "@{name} adlı kişiyi bildir",

View File

@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "Який-небудь зі списку",
"hashtag.column_settings.tag_mode.none": "Жоден зі списку",
"hashtag.column_settings.tag_toggle": "Додати додаткові теґи до цього стовпчика",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} учасник} few {{counter} учасники} many {{counter} учасників} other {{counter} учасник}}",
"hashtag.counter_by_uses": "{count, plural, one {{counter} допис} few {{counter} дописи} many {{counter} дописів} other {{counter} допис}}",
"hashtag.counter_by_uses_today": "{count, plural, one {{counter} допис} few {{counter} дописи} many {{counter} дописів} other {{counter} допис}} сьогодні",
"hashtag.follow": "Стежити за хештегом",
"hashtag.unfollow": "Не стежити за хештегом",
"home.actions.go_to_explore": "Переглянути тенденції",

View File

@ -12,7 +12,7 @@
"about.powered_by": "Mạng xã hội liên hợp {mastodon}",
"about.rules": "Nội quy máy chủ",
"account.account_note_header": "Ghi chú",
"account.add_or_remove_from_list": "Thêm hoặc xóa khỏi danh sách",
"account.add_or_remove_from_list": "Thêm vào danh sách",
"account.badges.bot": "Bot",
"account.badges.group": "Nhóm",
"account.block": "Chặn @{name}",
@ -29,7 +29,7 @@
"account.endorse": "Tôn vinh người này",
"account.featured_tags.last_status_at": "Tút gần nhất {date}",
"account.featured_tags.last_status_never": "Chưa có tút",
"account.featured_tags.title": "Hashtag {name} thường dùng",
"account.featured_tags.title": "Hashtag của {name}",
"account.follow": "Theo dõi",
"account.followers": "Người theo dõi",
"account.followers.empty": "Chưa có người theo dõi nào.",
@ -104,8 +104,8 @@
"closed_registrations.other_server_instructions": "Vì Mastodon liên hợp nên bạn có thể tạo tài khoản trên máy chủ khác và vẫn tương tác với máy chủ này.",
"closed_registrations_modal.description": "{domain} hiện tắt đăng ký, nhưng hãy lưu ý rằng bạn không cần một tài khoản riêng trên {domain} để sử dụng Mastodon.",
"closed_registrations_modal.find_another_server": "Tìm máy chủ khác",
"closed_registrations_modal.preamble": "Mastodon liên hợp, vì vậy bất kể bạn tạo tài khoản ở đâu, bạn sẽ có thể theo dõi và tương tác với bất kỳ ai trên máy chủ này. Bạn thậm chí có thể tự mở máy chủ!",
"closed_registrations_modal.title": "Đăng ký trên Mastodon",
"closed_registrations_modal.preamble": "Mastodon liên hợp nên bất kể bạn tạo tài khoản ở đâu, bạn cũng sẽ có thể theo dõi và tương tác với mọi người trên máy chủ này. Bạn thậm chí có thể tự mở máy chủ!",
"closed_registrations_modal.title": "Đăng ký Mastodon",
"column.about": "Giới thiệu",
"column.blocks": "Người đã chặn",
"column.bookmarks": "Đã lưu",
@ -114,9 +114,9 @@
"column.directory": "Tìm người cùng sở thích",
"column.domain_blocks": "Máy chủ đã chặn",
"column.favourites": "Lượt thích",
"column.firehose": "Bản tin trực tiếp",
"column.firehose": "Bảng tin",
"column.follow_requests": "Yêu cầu theo dõi",
"column.home": "Bảng tin",
"column.home": "Trang chính",
"column.lists": "Danh sách",
"column.mutes": "Người đã ẩn",
"column.notifications": "Thông báo",
@ -131,7 +131,7 @@
"column_header.unpin": "Không ghim",
"column_subheading.settings": "Cài đặt",
"community.column_settings.local_only": "Chỉ máy chủ của bạn",
"community.column_settings.media_only": "Chỉ xem media",
"community.column_settings.media_only": "Chỉ hiện tút có media",
"community.column_settings.remote_only": "Chỉ người ở máy chủ khác",
"compose.language.change": "Chọn ngôn ngữ tút",
"compose.language.search": "Tìm ngôn ngữ...",
@ -185,7 +185,7 @@
"confirmations.reply.confirm": "Trả lời",
"confirmations.reply.message": "Nội dung bạn đang soạn thảo sẽ bị ghi đè, bạn có tiếp tục?",
"confirmations.unfollow.confirm": "Bỏ theo dõi",
"confirmations.unfollow.message": "Bạn thật sự muốn ngưng theo dõi {name}?",
"confirmations.unfollow.message": "Bạn thật sự muốn bỏ theo dõi {name}?",
"conversation.delete": "Xóa tin nhắn này",
"conversation.mark_as_read": "Đánh dấu là đã đọc",
"conversation.open": "Xem toàn bộ tin nhắn",
@ -198,12 +198,12 @@
"directory.recently_active": "Hoạt động gần đây",
"disabled_account_banner.account_settings": "Cài đặt tài khoản",
"disabled_account_banner.text": "Tài khoản {disabledAccount} của bạn hiện không khả dụng.",
"dismissable_banner.community_timeline": "Những tút gần đây của những người có tài khoản thuộc máy chủ {domain}.",
"dismissable_banner.community_timeline": "Đây là những tút công khai gần đây trong mạng liên hợp của máy chủ {domain}.",
"dismissable_banner.dismiss": "Bỏ qua",
"dismissable_banner.explore_links": "Những sự kiện đang được thảo luận nhiều trên máy chủ này và những máy chủ khác thuộc mạng liên hợp của nó.",
"dismissable_banner.explore_statuses": "Những tút đang phổ biến trên máy chủ này và mạng liên hợp của nó.",
"dismissable_banner.explore_tags": "Những hashtag đang được sử dụng nhiều trên máy chủ này và những máy chủ khác thuộc mạng liên hợp của nó.",
"dismissable_banner.public_timeline": "Đây là những tút công khai gần đây nhất của những người trong mạng liên hợp của {domain}.",
"dismissable_banner.explore_links": "Đây là những liên kết đang được chia sẻ nhiều trong mạng liên hợp của máy chủ này.",
"dismissable_banner.explore_statuses": "Đây là những tút đang phổ biến trong mạng liên hợp của máy chủ này.",
"dismissable_banner.explore_tags": "Đây là những hashtag đang được sử dụng nhiều trong mạng liên hợp của máy chủ này.",
"dismissable_banner.public_timeline": "Đây là những tút công khai gần đây trong mạng liên hợp của máy chủ {domain}.",
"embed.instructions": "Sao chép đoạn mã dưới đây và chèn vào trang web của bạn.",
"embed.preview": "Nó sẽ hiển thị như vầy:",
"emoji_button.activity": "Hoạt động",
@ -235,11 +235,11 @@
"empty_column.follow_requests": "Bạn chưa có yêu cầu theo dõi nào.",
"empty_column.followed_tags": "Bạn chưa theo dõi hashtag nào. Khi bạn theo dõi, chúng sẽ hiện lên ở đây.",
"empty_column.hashtag": "Chưa có tút nào dùng hashtag này.",
"empty_column.home": "Bảng tin của bạn đang trống! Hãy theo dõi nhiều người hơn. {suggestions}",
"empty_column.home": "Trang chính của bạn đang trống! Hãy theo dõi nhiều người hơn để lấp đầy.",
"empty_column.list": "Chưa có tút. Khi những người trong danh sách này đăng tút mới, chúng sẽ xuất hiện ở đây.",
"empty_column.lists": "Bạn chưa tạo danh sách nào.",
"empty_column.mutes": "Bạn chưa ẩn bất kỳ ai.",
"empty_column.notifications": "Bạn chưa có thông báo nào. Hãy thử theo dõi hoặc nhắn riêng cho một ai đó.",
"empty_column.notifications": "Bạn chưa có thông báo nào. Hãy thử theo dõi hoặc nhắn riêng cho ai đó.",
"empty_column.public": "Trống trơn! Bạn hãy viết gì đó hoặc bắt đầu theo dõi những người khác",
"error.unexpected_crash.explanation": "Trang này có thể không hiển thị chính xác do lỗi lập trình Mastodon hoặc vấn đề tương thích trình duyệt.",
"error.unexpected_crash.explanation_addons": "Trang này không thể hiển thị do xung khắc với add-on của trình duyệt hoặc công cụ tự động dịch ngôn ngữ.",
@ -295,25 +295,28 @@
"hashtag.column_settings.tag_mode.any": "Một phần",
"hashtag.column_settings.tag_mode.none": "Không chọn",
"hashtag.column_settings.tag_toggle": "Bao gồm thêm hashtag cho cột này",
"hashtag.counter_by_accounts": "{count, plural, other {{counter} người dùng}}",
"hashtag.counter_by_uses": "{count, plural, other {{counter} tút}}",
"hashtag.counter_by_uses_today": "{count, plural, other {{counter} tút}} hôm nay",
"hashtag.follow": "Theo dõi hashtag",
"hashtag.unfollow": "Ngưng theo dõi hashtag",
"hashtag.unfollow": "Bỏ theo dõi hashtag",
"home.actions.go_to_explore": "Khám phá xu hướng",
"home.actions.go_to_suggestions": "Đề xuất theo dõi",
"home.actions.go_to_suggestions": "Tìm người theo dõi",
"home.column_settings.basic": "Tùy chỉnh",
"home.column_settings.show_reblogs": "Hiện những lượt đăng lại",
"home.column_settings.show_replies": "Hiện những tút dạng trả lời",
"home.explore_prompt.body": "Bảng tin của bạn sẽ có sự kết hợp của các tút gắn hashtag mà bạn đã chọn để theo dõi, những người bạn đã chọn để theo dõi và các bài đăng lại từ họ. Lúc này có vẻ hơi trống, sao bạn không:",
"home.explore_prompt.body": "Bảng tin của bạn sẽ bao gồm các tút có hashtag bạn theo dõi, những người bạn theo dõi và các tút mà họ đăng lại. Lúc này có vẻ hơi trống, sao bạn không:",
"home.explore_prompt.title": "Đây là ngôi nhà Mastodon của bạn.",
"home.hide_announcements": "Ẩn thông báo máy chủ",
"home.show_announcements": "Hiện thông báo máy chủ",
"home.show_announcements": "Xem thông báo máy chủ",
"interaction_modal.description.favourite": "Với tài khoản Mastodon, bạn có thể cho người đăng biết bạn thích tút này và lưu lại tút.",
"interaction_modal.description.follow": "Với tài khoản Mastodon, bạn có thể theo dõi {name} để nhận những tút của họ trên bảng tin của mình.",
"interaction_modal.description.follow": "Với tài khoản Mastodon, bạn có thể theo dõi {name} để tút của họ hiện trên bảng tin của mình.",
"interaction_modal.description.reblog": "Với tài khoản Mastodon, bạn có thể đăng lại tút này để chia sẻ nó với những người đang theo dõi bạn.",
"interaction_modal.description.reply": "Với tài khoản Mastodon, bạn có thể bình luận tút này.",
"interaction_modal.description.reply": "Với tài khoản Mastodon, bạn có thể trả lời tút này.",
"interaction_modal.login.action": "Đăng nhập ngay",
"interaction_modal.login.prompt": "Địa chỉ máy chủ của bạn, vd: mastodon.social",
"interaction_modal.no_account_yet": "Chưa có tài khoản Mastodon?",
"interaction_modal.on_another_server": "Trên một máy chủ khác",
"interaction_modal.on_another_server": "Trên máy chủ khác",
"interaction_modal.on_this_server": "Trên máy chủ này",
"interaction_modal.sign_in": "Bạn chưa đăng nhập. Bạn đã có tài khoản ở máy chủ khác?",
"interaction_modal.sign_in_hint": "Mẹo: Đó là trang web nơi bạn đã đăng ký. Nếu không nhớ, lục lại trong email của bạn. Bạn cũng có thể nhập địa chỉ Mastodon đầy đủ! (vd: @Mastodon@mastodon.social)",
@ -336,8 +339,8 @@
"keyboard_shortcuts.favourite": "Thích tút",
"keyboard_shortcuts.favourites": "Mở lượt thích",
"keyboard_shortcuts.federated": "mở mạng liên hợp",
"keyboard_shortcuts.heading": "Các phím tắt",
"keyboard_shortcuts.home": "mở bảng tin",
"keyboard_shortcuts.heading": "Danh sách phím tắt",
"keyboard_shortcuts.home": "mở trang chính",
"keyboard_shortcuts.hotkey": "Phím tắt",
"keyboard_shortcuts.legend": "hiện bảng hướng dẫn này",
"keyboard_shortcuts.local": "mở máy chủ của bạn",
@ -464,26 +467,26 @@
"onboarding.action.back": "Quay lại",
"onboarding.actions.back": "Quay lại",
"onboarding.actions.go_to_explore": "Xem những gì đang thịnh hành",
"onboarding.actions.go_to_home": i đến bảng tin",
"onboarding.actions.go_to_home": ến trang chính",
"onboarding.compose.template": "Xin chào #Mastodon!",
"onboarding.follows.empty": "Không có kết quả có thể được hiển thị lúc này. Bạn có thể thử sử dụng tính năng tìm kiếm hoặc duyệt qua trang khám phá để tìm những người theo dõi hoặc thử lại sau.",
"onboarding.follows.lead": "Bạn quản lý bảng tin của riêng bạn. Bạn càng theo dõi nhiều người, nó sẽ càng sôi động và thú vị. Những hồ sơ này có thể là một điểm khởi đầu tốt—bạn luôn có thể hủy theo dõi họ sau!",
"onboarding.follows.lead": "Bạn quản lý bảng tin của riêng bạn. Bạn càng theo dõi nhiều người, nó sẽ càng sôi động và thú vị. Để bắt đầu, đây là vài gợi ý:",
"onboarding.follows.title": "Thịnh hành trên Mastodon",
"onboarding.share.lead": "Hãy cho mọi người biết làm thế nào họ có thể tìm thấy bạn trên Mastodon!",
"onboarding.share.message": "Tôi là {username} trên #Mastodon! Hãy theo dõi tôi tại {url}",
"onboarding.share.next_steps": "Các bước kế tiếp:",
"onboarding.share.title": "Chia sẻ hồ sơ của bạn",
"onboarding.share.title": "Chia sẻ hồ sơ",
"onboarding.start.lead": "Tài khoản Mastodon mới của bạn đã sẵn sàng hoạt động. Đây là cách bạn có thể tận dụng tối đa nó:",
"onboarding.start.skip": "Muốn bỏ qua luôn?",
"onboarding.start.title": "Xong rồi bạn!",
"onboarding.steps.follow_people.body": "Bạn quản lý bảng tin của riêng bạn. Hãy lấp đầy nó với những người thú vị.",
"onboarding.steps.follow_people.title": "Theo dõi {count, plural, other {# người}}",
"onboarding.steps.publish_status.body": "Gửi lời chào tới thế giới mới.",
"onboarding.steps.publish_status.title": "Đăng tút đầu tiên của bạn",
"onboarding.steps.setup_profile.body": "Những người khác có nhiều khả năng tương tác với bạn hơn nếu hồ sơ bạn được điền đầy đủ thông tin.",
"onboarding.steps.follow_people.body": "Theo dõi những người thú vị trên Mastodon.",
"onboarding.steps.follow_people.title": "Cá nhân hóa trang chính",
"onboarding.steps.publish_status.body": "Chào cộng đồng bằng lời nói, ảnh hoặc video {emoji}",
"onboarding.steps.publish_status.title": "Đăng tút đầu tiên",
"onboarding.steps.setup_profile.body": "Tạo sự tương tác bằng một hồ sơ hoàn chỉnh.",
"onboarding.steps.setup_profile.title": "Tùy biến hồ sơ",
"onboarding.steps.share_profile.body": "Hãy để bạn bè của bạn biết cách tìm thấy bạn trên Mastodon!",
"onboarding.steps.share_profile.title": "Chia sẻ hồ sơ của bạn",
"onboarding.steps.share_profile.title": "Chia sẻ hồ sơ Mastodon của bạn",
"onboarding.tips.2fa": "<strong>Bạn có biết?</strong> Bạn có thể bảo mật tài khoản của mình bằng cách thiết lập xác thực hai yếu tố trong cài đặt tài khoản của mình. Nó hoạt động với bất kỳ ứng dụng OTP nào bạn chọn, không cần số điện thoại!",
"onboarding.tips.accounts_from_other_servers": "<strong>Bạn có biết?</strong> Vì Mastodon liên hợp, một số hồ sơ bạn gặp sẽ được lưu trữ trên các máy chủ không giống bạn. Tuy nhiên, bạn có thể tương tác với họ một cách liền mạch! Máy chủ của họ nằm ở nửa sau tên người dùng của họ!",
"onboarding.tips.migration": "<strong>Bạn có biết?</strong> Nếu bạn thấy {domain} không phải là lựa chọn máy chủ tuyệt vời cho bạn trong tương lai, bạn có thể chuyển sang máy chủ Mastodon khác mà không bị mất người theo dõi. Bạn thậm chí có thể lưu trữ máy chủ của riêng bạn!",
@ -491,7 +494,7 @@
"password_confirmation.exceeds_maxlength": "Mật khẩu vượt quá độ dài mật khẩu tối đa",
"password_confirmation.mismatching": "Mật khẩu không trùng khớp",
"picture_in_picture.restore": "Hiển thị bình thường",
"poll.closed": "Kết thúc",
"poll.closed": "Đóng",
"poll.refresh": "Làm mới",
"poll.reveal": "Xem kết quả",
"poll.total_people": "{count, plural, other {# người bình chọn}}",
@ -501,7 +504,7 @@
"poll.votes": "{votes, plural, other {# lượt bình chọn}}",
"poll_button.add_poll": "Tạo bình chọn",
"poll_button.remove_poll": "Hủy cuộc bình chọn",
"privacy.change": "Thay đổi quyền riêng tư",
"privacy.change": "Chọn kiểu tút",
"privacy.direct.long": "Chỉ người được nhắc đến mới thấy",
"privacy.direct.short": "Nhắn riêng",
"privacy.private.long": "Dành riêng cho người theo dõi",
@ -514,7 +517,7 @@
"privacy_policy.title": "Chính sách bảo mật",
"refresh": "Làm mới",
"regeneration_indicator.label": "Đang tải…",
"regeneration_indicator.sublabel": "Bảng tin của bạn đang được cập nhật!",
"regeneration_indicator.sublabel": "Trang chính của bạn đang được cập nhật!",
"relative_time.days": "{number} ngày",
"relative_time.full.days": "{number, plural, other {# ngày}}",
"relative_time.full.hours": "{number, plural, other {# giờ}}",
@ -534,8 +537,8 @@
"report.categories.violation": "Vi phạm nội quy máy chủ",
"report.category.subtitle": "Chọn lý do phù hợp nhất:",
"report.category.title": "{type} này có vấn đề gì?",
"report.category.title_account": "người này",
"report.category.title_status": "tút",
"report.category.title_account": "Người",
"report.category.title_status": "Tút",
"report.close": "Xong",
"report.comment.title": "Có điều gì mà chúng tôi cần biết không?",
"report.forward": "Chuyển đến {target}",
@ -564,8 +567,8 @@
"report.thanks.take_action_actionable": "Trong lúc chờ chúng tôi xem xét, bạn có thể áp dụng hành động với @{name}:",
"report.thanks.title": "Không muốn xem thứ này?",
"report.thanks.title_actionable": "Cảm ơn đã báo cáo, chúng tôi sẽ xem xét kỹ.",
"report.unfollow": "Ngưng theo dõi @{name}",
"report.unfollow_explanation": "Bạn đang theo dõi người này. Để không thấy tút của họ trong bảng tin nữa, hãy ngưng theo dõi.",
"report.unfollow": "Bỏ theo dõi @{name}",
"report.unfollow_explanation": "Bạn đang theo dõi người này. Để không thấy tút của họ trên trang chính nữa, hãy bỏ theo dõi.",
"report_notification.attached_statuses": "{count, plural, other {{count} tút}} đính kèm",
"report_notification.categories.legal": "Pháp lý",
"report_notification.categories.other": "Khác",
@ -574,18 +577,18 @@
"report_notification.open": "Mở báo cáo",
"search.no_recent_searches": "Không có tìm kiếm gần đây",
"search.placeholder": "Tìm kiếm",
"search.quick_action.account_search": "Người trùng khớp {x}",
"search.quick_action.account_search": "Người có tên {x}",
"search.quick_action.go_to_account": "Xem trang {x}",
"search.quick_action.go_to_hashtag": "Xem hashtag {x}",
"search.quick_action.open_url": "Mở liên kết trong Mastodon",
"search.quick_action.status_search": "Tút trùng khớp {x}",
"search.quick_action.status_search": "Tút nhắc đến {x}",
"search.search_or_paste": "Tìm kiếm hoặc nhập URL",
"search_popout.quick_actions": "Thao tác nhanh",
"search_popout.recent": "Tìm kiếm gần đây",
"search_results.accounts": "Mọi người",
"search_results.all": "Toàn bộ",
"search_results.hashtags": "Hashtags",
"search_results.nothing_found": "Không tìm thấy kết quả trùng khớp",
"search_results.hashtags": "Hashtag",
"search_results.nothing_found": "Không tìm thấy ",
"search_results.statuses": "Tút",
"search_results.statuses_fts_disabled": "Máy chủ của bạn không bật tính năng tìm kiếm tút.",
"search_results.title": "Tìm kiếm {q}",
@ -620,14 +623,14 @@
"status.filter": "Lọc tút này",
"status.filtered": "Bộ lọc",
"status.hide": "Ẩn tút",
"status.history.created": "{name} tạo vào {date}",
"status.history.edited": "{name} sửa vào {date}",
"status.history.created": "{name} đăng {date} trước",
"status.history.edited": "{name} đã sửa {date} trước",
"status.load_more": "Tải thêm",
"status.media.open": "Nhấn để mở",
"status.media.show": "Nhấn để xem",
"status.media_hidden": "Đã ẩn",
"status.mention": "Nhắc đến @{name}",
"status.more": "Thêm",
"status.more": "Xem thêm",
"status.mute": "Ẩn @{name}",
"status.mute_conversation": "Không quan tâm nữa",
"status.open": "Đọc tút",
@ -655,7 +658,7 @@
"status.title.with_attachments": "{user} đã đăng {attachmentCount, plural, other {{attachmentCount} đính kèm}}",
"status.translate": "Dịch tút",
"status.translated_from_with": "Dịch từ {lang} bằng {provider}",
"status.uncached_media_warning": "Xem trước không sẵn có",
"status.uncached_media_warning": "Không bản xem trước",
"status.unmute_conversation": "Quan tâm",
"status.unpin": "Bỏ ghim trên hồ sơ",
"subscribed_languages.lead": "Chỉ các tút đăng bằng các ngôn ngữ đã chọn mới được xuất hiện trên bảng tin của bạn. Không chọn gì cả để đọc tút đăng bằng mọi ngôn ngữ.",
@ -663,7 +666,7 @@
"subscribed_languages.target": "Đổi ngôn ngữ mong muốn cho {target}",
"suggestions.dismiss": "Tắt đề xuất",
"suggestions.header": "Có thể bạn quan tâm…",
"tabs_bar.home": "Bảng tin",
"tabs_bar.home": "Trang chính",
"tabs_bar.notifications": "Thông báo",
"time_remaining.days": "{number, plural, other {# ngày}}",
"time_remaining.hours": "{number, plural, other {# giờ}}",

View File

@ -33,9 +33,9 @@
"account.follow": "跟隨",
"account.followers": "跟隨者",
"account.followers.empty": "尚未有人跟隨這位使用者。",
"account.followers_counter": "被 {count, plural,one {{counter} 人}other {{counter} 人}} 跟隨",
"account.followers_counter": "被 {count, plural,one {{counter} 人}other {{counter} 人}}跟隨",
"account.following": "跟隨中",
"account.following_counter": "正在跟隨 {count, plural,one {{counter}}other {{counter} 人}}",
"account.following_counter": "正在跟隨 {count, plural, one {{counter}} other {{counter} 人}}",
"account.follows.empty": "這位使用者尚未跟隨任何人。",
"account.follows_you": "跟隨了您",
"account.go_to_profile": "前往個人檔案",
@ -295,6 +295,9 @@
"hashtag.column_settings.tag_mode.any": "任一",
"hashtag.column_settings.tag_mode.none": "全不",
"hashtag.column_settings.tag_toggle": "將額外標籤加入到這個欄位",
"hashtag.counter_by_accounts": "{count, plural, one {{counter} 名} other {{counter} 名}}參與者",
"hashtag.counter_by_uses": "{count, plural, one {{counter} 則} other {{counter} 則}}嘟文",
"hashtag.counter_by_uses_today": "本日有 {count, plural, one {{counter} 則} other {{counter} 則}}嘟文",
"hashtag.follow": "追蹤主題標籤",
"hashtag.unfollow": "取消追蹤主題標籤",
"home.actions.go_to_explore": "看看發生什麼新鮮事",

View File

@ -65,287 +65,276 @@ function loaded() {
};
};
ready(() => {
const locale = document.documentElement.lang;
const locale = document.documentElement.lang;
const dateTimeFormat = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
});
const dateTimeFormat = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'long',
day: 'numeric',
hour: 'numeric',
minute: 'numeric',
});
const dateFormat = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
timeFormat: false,
});
const dateFormat = new Intl.DateTimeFormat(locale, {
year: 'numeric',
month: 'short',
day: 'numeric',
timeFormat: false,
});
const timeFormat = new Intl.DateTimeFormat(locale, {
timeStyle: 'short',
hour12: false,
});
const timeFormat = new Intl.DateTimeFormat(locale, {
timeStyle: 'short',
hour12: false,
});
const formatMessage = ({ id, defaultMessage }, values) => {
const messageFormat = new IntlMessageFormat(localeData[id] || defaultMessage, locale);
return messageFormat.format(values);
};
const formatMessage = ({ id, defaultMessage }, values) => {
const messageFormat = new IntlMessageFormat(localeData[id] || defaultMessage, locale);
return messageFormat.format(values);
};
[].forEach.call(document.querySelectorAll('.emojify'), (content) => {
content.innerHTML = emojify(content.innerHTML);
});
[].forEach.call(document.querySelectorAll('.emojify'), (content) => {
content.innerHTML = emojify(content.innerHTML);
});
[].forEach.call(document.querySelectorAll('time.formatted'), (content) => {
const datetime = new Date(content.getAttribute('datetime'));
const formattedDate = dateTimeFormat.format(datetime);
[].forEach.call(document.querySelectorAll('time.formatted'), (content) => {
const datetime = new Date(content.getAttribute('datetime'));
const formattedDate = dateTimeFormat.format(datetime);
content.title = formattedDate;
content.textContent = formattedDate;
});
content.title = formattedDate;
content.textContent = formattedDate;
});
const isToday = date => {
const today = new Date();
const isToday = date => {
const today = new Date();
return date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear();
};
const todayFormat = new IntlMessageFormat(localeData['relative_format.today'] || 'Today at {time}', locale);
return date.getDate() === today.getDate() &&
date.getMonth() === today.getMonth() &&
date.getFullYear() === today.getFullYear();
};
const todayFormat = new IntlMessageFormat(localeData['relative_format.today'] || 'Today at {time}', locale);
[].forEach.call(document.querySelectorAll('time.relative-formatted'), (content) => {
const datetime = new Date(content.getAttribute('datetime'));
[].forEach.call(document.querySelectorAll('time.relative-formatted'), (content) => {
const datetime = new Date(content.getAttribute('datetime'));
let formattedContent;
let formattedContent;
if (isToday(datetime)) {
const formattedTime = timeFormat.format(datetime);
if (isToday(datetime)) {
const formattedTime = timeFormat.format(datetime);
formattedContent = todayFormat.format({ time: formattedTime });
} else {
formattedContent = dateFormat.format(datetime);
}
formattedContent = todayFormat.format({ time: formattedTime });
} else {
formattedContent = dateFormat.format(datetime);
}
content.title = formattedContent;
content.textContent = formattedContent;
});
content.title = formattedContent;
content.textContent = formattedContent;
});
[].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
const datetime = new Date(content.getAttribute('datetime'));
const now = new Date();
[].forEach.call(document.querySelectorAll('time.time-ago'), (content) => {
const datetime = new Date(content.getAttribute('datetime'));
const now = new Date();
const timeGiven = content.getAttribute('datetime').includes('T');
content.title = timeGiven ? dateTimeFormat.format(datetime) : dateFormat.format(datetime);
content.textContent = timeAgoString({
formatMessage,
formatDate: (date, options) => (new Intl.DateTimeFormat(locale, options)).format(date),
}, datetime, now, now.getFullYear(), timeGiven);
});
const timeGiven = content.getAttribute('datetime').includes('T');
content.title = timeGiven ? dateTimeFormat.format(datetime) : dateFormat.format(datetime);
content.textContent = timeAgoString({
formatMessage,
formatDate: (date, options) => (new Intl.DateTimeFormat(locale, options)).format(date),
}, datetime, now, now.getFullYear(), timeGiven);
});
const reactComponents = document.querySelectorAll('[data-component]');
const reactComponents = document.querySelectorAll('[data-component]');
if (reactComponents.length > 0) {
import(/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container')
.then(({ default: MediaContainer }) => {
[].forEach.call(reactComponents, (component) => {
[].forEach.call(component.children, (child) => {
component.removeChild(child);
});
if (reactComponents.length > 0) {
import(/* webpackChunkName: "containers/media_container" */ '../mastodon/containers/media_container')
.then(({ default: MediaContainer }) => {
[].forEach.call(reactComponents, (component) => {
[].forEach.call(component.children, (child) => {
component.removeChild(child);
});
const content = document.createElement('div');
const root = createRoot(content);
root.render(<MediaContainer locale={locale} components={reactComponents} />);
document.body.appendChild(content);
scrollToDetailedStatus();
})
.catch(error => {
console.error(error);
scrollToDetailedStatus();
});
} else {
scrollToDetailedStatus();
}
delegate(document, '#user_account_attributes_username', 'input', throttle(() => {
const username = document.getElementById('user_account_attributes_username');
const content = document.createElement('div');
if (username.value && username.value.length > 0) {
axios.get('/api/v1/accounts/lookup', { params: { acct: username.value } }).then(() => {
username.setCustomValidity(formatMessage(messages.usernameTaken));
}).catch(() => {
username.setCustomValidity('');
});
} else {
const root = createRoot(content);
root.render(<MediaContainer locale={locale} components={reactComponents} />);
document.body.appendChild(content);
scrollToDetailedStatus();
})
.catch(error => {
console.error(error);
scrollToDetailedStatus();
});
} else {
scrollToDetailedStatus();
}
delegate(document, '#user_account_attributes_username', 'input', throttle(() => {
const username = document.getElementById('user_account_attributes_username');
if (username.value && username.value.length > 0) {
axios.get('/api/v1/accounts/lookup', { params: { acct: username.value } }).then(() => {
username.setCustomValidity(formatMessage(messages.usernameTaken));
}).catch(() => {
username.setCustomValidity('');
}
}, 500, { leading: false, trailing: true }));
delegate(document, '#user_password,#user_password_confirmation', 'input', () => {
const password = document.getElementById('user_password');
const confirmation = document.getElementById('user_password_confirmation');
if (!confirmation) return;
if (confirmation.value && confirmation.value.length > password.maxLength) {
confirmation.setCustomValidity(formatMessage(messages.passwordExceedsLength));
} else if (password.value && password.value !== confirmation.value) {
confirmation.setCustomValidity(formatMessage(messages.passwordDoesNotMatch));
} else {
confirmation.setCustomValidity('');
}
});
delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original'));
delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
delegate(document, '.status__content__spoiler-link', 'click', function() {
const statusEl = this.parentNode.parentNode;
if (statusEl.dataset.spoiler === 'expanded') {
statusEl.dataset.spoiler = 'folded';
this.textContent = (new IntlMessageFormat(localeData['status.show_more'] || 'Show more', locale)).format();
} else {
statusEl.dataset.spoiler = 'expanded';
this.textContent = (new IntlMessageFormat(localeData['status.show_less'] || 'Show less', locale)).format();
}
return false;
});
[].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => {
const statusEl = spoilerLink.parentNode.parentNode;
const message = (statusEl.dataset.spoiler === 'expanded') ? (localeData['status.show_less'] || 'Show less') : (localeData['status.show_more'] || 'Show more');
spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format();
});
});
delegate(document, '#account_display_name', 'input', ({ target }) => {
const name = document.querySelector('.card .display-name strong');
if (name) {
if (target.value) {
name.innerHTML = emojify(escapeTextContentForBrowser(target.value));
} else {
name.textContent = target.dataset.default;
}
}
});
delegate(document, '#account_avatar', 'change', ({ target }) => {
const avatar = document.querySelector('.card .avatar img');
const [file] = target.files || [];
const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
avatar.src = url;
});
const getProfileAvatarAnimationHandler = (swapTo) => {
//animate avatar gifs on the profile page when moused over
return ({ target }) => {
const swapSrc = target.getAttribute(swapTo);
//only change the img source if autoplay is off and the image src is actually different
if(target.getAttribute('data-autoplay') !== 'true' && target.src !== swapSrc) {
target.src = swapSrc;
}
};
};
delegate(document, 'img#profile_page_avatar', 'mouseover', getProfileAvatarAnimationHandler('data-original'));
delegate(document, 'img#profile_page_avatar', 'mouseout', getProfileAvatarAnimationHandler('data-static'));
delegate(document, '#account_header', 'change', ({ target }) => {
const header = document.querySelector('.card .card__img img');
const [file] = target.files || [];
const url = file ? URL.createObjectURL(file) : header.dataset.originalSrc;
header.src = url;
});
delegate(document, '#account_locked', 'change', ({ target }) => {
const lock = document.querySelector('.card .display-name i');
if (lock) {
if (target.checked) {
delete lock.dataset.hidden;
} else {
lock.dataset.hidden = 'true';
}
}
});
delegate(document, '.input-copy input', 'click', ({ target }) => {
target.focus();
target.select();
target.setSelectionRange(0, target.value.length);
});
delegate(document, '.input-copy button', 'click', ({ target }) => {
const input = target.parentNode.querySelector('.input-copy__wrapper input');
const oldReadOnly = input.readonly;
input.readonly = false;
input.focus();
input.select();
input.setSelectionRange(0, input.value.length);
try {
if (document.execCommand('copy')) {
input.blur();
target.parentNode.classList.add('copied');
setTimeout(() => {
target.parentNode.classList.remove('copied');
}, 700);
}
} catch (err) {
console.error(err);
}
input.readonly = oldReadOnly;
});
const toggleSidebar = () => {
const sidebar = document.querySelector('.sidebar ul');
const toggleButton = document.querySelector('.sidebar__toggle__icon');
if (sidebar.classList.contains('visible')) {
document.body.style.overflow = null;
toggleButton.setAttribute('aria-expanded', 'false');
});
} else {
document.body.style.overflow = 'hidden';
toggleButton.setAttribute('aria-expanded', 'true');
username.setCustomValidity('');
}
}, 500, { leading: false, trailing: true }));
toggleButton.classList.toggle('active');
sidebar.classList.toggle('visible');
};
delegate(document, '#user_password,#user_password_confirmation', 'input', () => {
const password = document.getElementById('user_password');
const confirmation = document.getElementById('user_password_confirmation');
if (!confirmation) return;
delegate(document, '.sidebar__toggle__icon', 'click', () => {
toggleSidebar();
});
delegate(document, '.sidebar__toggle__icon', 'keydown', e => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
toggleSidebar();
if (confirmation.value && confirmation.value.length > password.maxLength) {
confirmation.setCustomValidity(formatMessage(messages.passwordExceedsLength));
} else if (password.value && password.value !== confirmation.value) {
confirmation.setCustomValidity(formatMessage(messages.passwordDoesNotMatch));
} else {
confirmation.setCustomValidity('');
}
});
// Empty the honeypot fields in JS in case something like an extension
// automatically filled them.
delegate(document, '#registration_new_user,#new_user', 'submit', () => {
['user_website', 'user_confirm_password', 'registration_user_website', 'registration_user_confirm_password'].forEach(id => {
const field = document.getElementById(id);
if (field) {
field.value = '';
}
});
delegate(document, '.custom-emoji', 'mouseover', getEmojiAnimationHandler('data-original'));
delegate(document, '.custom-emoji', 'mouseout', getEmojiAnimationHandler('data-static'));
delegate(document, '.status__content__spoiler-link', 'click', function() {
const statusEl = this.parentNode.parentNode;
if (statusEl.dataset.spoiler === 'expanded') {
statusEl.dataset.spoiler = 'folded';
this.textContent = (new IntlMessageFormat(localeData['status.show_more'] || 'Show more', locale)).format();
} else {
statusEl.dataset.spoiler = 'expanded';
this.textContent = (new IntlMessageFormat(localeData['status.show_less'] || 'Show less', locale)).format();
}
return false;
});
[].forEach.call(document.querySelectorAll('.status__content__spoiler-link'), (spoilerLink) => {
const statusEl = spoilerLink.parentNode.parentNode;
const message = (statusEl.dataset.spoiler === 'expanded') ? (localeData['status.show_less'] || 'Show less') : (localeData['status.show_more'] || 'Show more');
spoilerLink.textContent = (new IntlMessageFormat(message, locale)).format();
});
}
delegate(document, '#account_display_name', 'input', ({ target }) => {
const name = document.querySelector('.card .display-name strong');
if (name) {
if (target.value) {
name.innerHTML = emojify(escapeTextContentForBrowser(target.value));
} else {
name.textContent = target.dataset.default;
}
}
});
delegate(document, '#edit_profile input[type=file]', 'change', ({ target }) => {
const avatar = document.getElementById(target.id + '-preview');
const [file] = target.files || [];
const url = file ? URL.createObjectURL(file) : avatar.dataset.originalSrc;
avatar.src = url;
});
const getProfileAvatarAnimationHandler = (swapTo) => {
//animate avatar gifs on the profile page when moused over
return ({ target }) => {
const swapSrc = target.getAttribute(swapTo);
//only change the img source if autoplay is off and the image src is actually different
if(target.getAttribute('data-autoplay') !== 'true' && target.src !== swapSrc) {
target.src = swapSrc;
}
};
};
delegate(document, 'img#profile_page_avatar', 'mouseover', getProfileAvatarAnimationHandler('data-original'));
delegate(document, 'img#profile_page_avatar', 'mouseout', getProfileAvatarAnimationHandler('data-static'));
delegate(document, '#account_locked', 'change', ({ target }) => {
const lock = document.querySelector('.card .display-name i');
if (lock) {
if (target.checked) {
delete lock.dataset.hidden;
} else {
lock.dataset.hidden = 'true';
}
}
});
delegate(document, '.input-copy input', 'click', ({ target }) => {
target.focus();
target.select();
target.setSelectionRange(0, target.value.length);
});
delegate(document, '.input-copy button', 'click', ({ target }) => {
const input = target.parentNode.querySelector('.input-copy__wrapper input');
const oldReadOnly = input.readonly;
input.readonly = false;
input.focus();
input.select();
input.setSelectionRange(0, input.value.length);
try {
if (document.execCommand('copy')) {
input.blur();
target.parentNode.classList.add('copied');
setTimeout(() => {
target.parentNode.classList.remove('copied');
}, 700);
}
} catch (err) {
console.error(err);
}
input.readonly = oldReadOnly;
});
const toggleSidebar = () => {
const sidebar = document.querySelector('.sidebar ul');
const toggleButton = document.querySelector('.sidebar__toggle__icon');
if (sidebar.classList.contains('visible')) {
document.body.style.overflow = null;
toggleButton.setAttribute('aria-expanded', 'false');
} else {
document.body.style.overflow = 'hidden';
toggleButton.setAttribute('aria-expanded', 'true');
}
toggleButton.classList.toggle('active');
sidebar.classList.toggle('visible');
};
delegate(document, '.sidebar__toggle__icon', 'click', () => {
toggleSidebar();
});
delegate(document, '.sidebar__toggle__icon', 'keydown', e => {
if (e.key === ' ' || e.key === 'Enter') {
e.preventDefault();
toggleSidebar();
}
});
// Empty the honeypot fields in JS in case something like an extension
// automatically filled them.
delegate(document, '#registration_new_user,#new_user', 'submit', () => {
['user_website', 'user_confirm_password', 'registration_user_website', 'registration_user_confirm_password'].forEach(id => {
const field = document.getElementById(id);
if (field) {
field.value = '';
}
});
});
function main() {
ready(loaded);

View File

@ -170,11 +170,6 @@
display: block;
width: 100%;
}
.layout-multiple-columns &.button--with-bell {
font-size: 12px;
padding: 0 8px;
}
}
.column__wrapper {
@ -1116,7 +1111,8 @@ body > [data-popper-placement] {
.audio-player,
.attachment-list,
.picture-in-picture-placeholder,
.status-card {
.status-card,
.hashtag-bar {
margin-inline-start: $thread-margin;
width: calc(100% - ($thread-margin));
}
@ -9261,3 +9257,26 @@ noscript {
}
}
}
.hashtag-bar {
margin-top: 16px;
display: flex;
flex-wrap: wrap;
font-size: 14px;
gap: 4px;
a {
display: inline-flex;
border-radius: 4px;
background: rgba($highlight-text-color, 0.2);
color: $highlight-text-color;
padding: 0.4em 0.6em;
text-decoration: none;
&:hover,
&:focus,
&:active {
background: rgba($highlight-text-color, 0.3);
}
}
}

View File

@ -309,9 +309,19 @@ code {
border-radius: 4px;
background: url('images/void.png');
&[src$='missing.png'] {
visibility: hidden;
}
&:last-child {
margin-bottom: 0;
}
&#account_avatar-preview {
width: 90px;
height: 90px;
object-fit: cover;
}
}
}

View File

@ -4,6 +4,8 @@ class ActivityPub::Activity::Create < ActivityPub::Activity
include FormattingHelper
def perform
@account.schedule_refresh_if_stale!
dereference_object!
case @object['type']

View File

@ -2,6 +2,8 @@
class ActivityPub::Activity::Update < ActivityPub::Activity
def perform
@account.schedule_refresh_if_stale!
dereference_object!
if equals_or_includes_any?(@object['type'], %w(Application Group Organization Person Service))

View File

@ -1,6 +1,13 @@
# frozen_string_literal: true
class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
INDEXES = [
InstancesIndex,
AccountsIndex,
TagsIndex,
StatusesIndex,
].freeze
def skip?
!current_user.can?(:view_devops)
end
@ -8,11 +15,15 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
def pass?
return true unless Chewy.enabled?
running_version.present? && compatible_version?
running_version.present? && compatible_version? && cluster_health['status'] == 'green' && indexes_match? && preset_matches?
rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error
false
end
def message
if running_version.present?
if running_version.blank?
Admin::SystemCheck::Message.new(:elasticsearch_running_check)
elsif !compatible_version?
Admin::SystemCheck::Message.new(
:elasticsearch_version_check,
I18n.t(
@ -21,13 +32,32 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
required_version: required_version
)
)
elsif !indexes_match?
Admin::SystemCheck::Message.new(
:elasticsearch_index_mismatch,
mismatched_indexes.join(' ')
)
elsif cluster_health['status'] == 'red'
Admin::SystemCheck::Message.new(:elasticsearch_health_red)
elsif cluster_health['number_of_nodes'] < 2 && es_preset != 'single_node_cluster'
Admin::SystemCheck::Message.new(:elasticsearch_preset_single_node, nil, 'https://docs.joinmastodon.org/admin/optional/elasticsearch/#scaling')
elsif Chewy.client.indices.get_settings['chewy_specifications'].dig('settings', 'index', 'number_of_replicas')&.to_i&.positive? && es_preset == 'single_node_cluster'
Admin::SystemCheck::Message.new(:elasticsearch_reset_chewy)
elsif cluster_health['status'] == 'yellow'
Admin::SystemCheck::Message.new(:elasticsearch_health_yellow)
else
Admin::SystemCheck::Message.new(:elasticsearch_running_check)
Admin::SystemCheck::Message.new(:elasticsearch_preset, nil, 'https://docs.joinmastodon.org/admin/optional/elasticsearch/#scaling')
end
rescue Faraday::ConnectionFailed, Elasticsearch::Transport::Transport::Error
Admin::SystemCheck::Message.new(:elasticsearch_running_check)
end
private
def cluster_health
@cluster_health ||= Chewy.client.cluster.health
end
def running_version
@running_version ||= begin
Chewy.client.info['version']['number']
@ -49,5 +79,30 @@ class Admin::SystemCheck::ElasticsearchCheck < Admin::SystemCheck::BaseCheck
Gem::Version.new(running_version) >= Gem::Version.new(required_version) ||
Gem::Version.new(compatible_wire_version) >= Gem::Version.new(required_version)
rescue ArgumentError
false
end
def mismatched_indexes
@mismatched_indexes ||= INDEXES.filter_map do |klass|
klass.index_name if Chewy.client.indices.get_mapping[klass.index_name]&.deep_symbolize_keys != klass.mappings_hash
end
end
def indexes_match?
mismatched_indexes.empty?
end
def es_preset
ENV.fetch('ES_PRESET', 'single_node_cluster')
end
def preset_matches?
case es_preset
when 'single_node_cluster'
cluster_health['number_of_nodes'] == 1
else
cluster_health['number_of_nodes'] > 1
end
end
end

View File

@ -2,8 +2,14 @@
class CacheBuster
def initialize(options = {})
@secret_header = options[:secret_header] || 'Secret-Header'
@secret = options[:secret] || 'True'
ActiveSupport::Deprecation.warn('Default values for the cache buster secret header name and values will be removed in Mastodon 4.3. Please set them explicitely if you rely on those.') unless options[:http_method] || (options[:secret] && options[:secret_header])
@secret_header = options[:secret_header] ||
(options[:http_method] ? nil : 'Secret-Header')
@secret = options[:secret] ||
(options[:http_method] ? nil : 'True')
@http_method = options[:http_method] || 'GET'
end
def bust(url)
@ -21,8 +27,9 @@ class CacheBuster
end
def build_request(url, http_client)
Request.new(:get, url, http_client: http_client).tap do |request|
request.add_headers(@secret_header => @secret)
end
request = Request.new(@http_method.downcase.to_sym, url, http_client: http_client)
request.add_headers(@secret_header => @secret) if @secret_header.present? && @secret && !@secret.empty?
request
end
end

View File

@ -117,7 +117,7 @@ class Request
def perform
begin
response = http_client.public_send(@verb, @url.to_s, @options.merge(headers: headers))
response = http_client.request(@verb, @url.to_s, @options.merge(headers: headers))
rescue => e
raise e.class, "#{e.message} on #{@url}", e.backtrace[0]
end

View File

@ -43,6 +43,9 @@ class VideoMetadataExtractor
@height = video_stream[:height]
@frame_rate = video_stream[:avg_frame_rate] == '0/0' ? nil : Rational(video_stream[:avg_frame_rate])
@r_frame_rate = video_stream[:r_frame_rate] == '0/0' ? nil : Rational(video_stream[:r_frame_rate])
# For some video streams the frame_rate reported by `ffprobe` will be 0/0, but for these streams we
# should use `r_frame_rate` instead. Video screencast generated by Gnome Screencast have this issue.
@frame_rate ||= @r_frame_rate
end
if (audio_stream = audio_streams.first)

View File

@ -50,6 +50,7 @@
# trendable :boolean
# reviewed_at :datetime
# requested_review_at :datetime
# indexable :boolean default(FALSE), not null
#
class Account < ApplicationRecord
@ -62,6 +63,8 @@ class Account < ApplicationRecord
trust_level
)
BACKGROUND_REFRESH_INTERVAL = 1.week.freeze
USERNAME_RE = /[a-z0-9_]+([a-z0-9_.-]+[a-z0-9_]+)?/i
MENTION_RE = %r{(?<=^|[^/[:word:]])@((#{USERNAME_RE})(?:@[[:word:].-]+[[:word:]]+)?)}i
URL_PREFIX_RE = %r{\Ahttp(s?)://[^/]+}
@ -208,6 +211,12 @@ class Account < ApplicationRecord
last_webfingered_at.nil? || last_webfingered_at <= 1.day.ago
end
def schedule_refresh_if_stale!
return unless last_webfingered_at.present? && last_webfingered_at <= BACKGROUND_REFRESH_INTERVAL.ago
AccountRefreshWorker.perform_in(rand(6.hours.to_i), id)
end
def refresh!
ResolveAccountService.new.call(acct) unless local?
end
@ -441,8 +450,21 @@ class Account < ApplicationRecord
EntityCache.instance.mention(username, domain)
end
end
def inverse_alias(key, original_key)
define_method("#{key}=") do |value|
public_send("#{original_key}=", !ActiveModel::Type::Boolean.new.cast(value))
end
define_method(key) do
!public_send(original_key)
end
end
end
inverse_alias :show_collections, :hide_collections
inverse_alias :unlocked, :locked
def emojis
@emojis ||= CustomEmoji.from_text(emojifiable_text, domain)
end

View File

@ -2,7 +2,7 @@
# == Schema Information
#
# Table name: follow_recommendations
# Table name: global_follow_recommendations
#
# account_id :bigint(8) primary key
# rank :decimal(, )
@ -11,6 +11,7 @@
class FollowRecommendation < ApplicationRecord
self.primary_key = :account_id
self.table_name = :global_follow_recommendations
belongs_to :account_summary, foreign_key: :account_id, inverse_of: false
belongs_to :account

View File

@ -48,7 +48,10 @@ class Report < ApplicationRecord
validate :validate_rule_ids
# entries here needs to be kept in sync with app/javascript/mastodon/features/notifications/components/report.jsx
# entries here need to be kept in sync with the front-end:
# - app/javascript/mastodon/features/notifications/components/report.jsx
# - app/javascript/mastodon/features/report/category.jsx
# - app/javascript/mastodon/components/admin/ReportReasonSelector.jsx
enum category: {
other: 0,
spam: 1_000,

View File

@ -367,13 +367,25 @@ class Status < ApplicationRecord
account_ids.uniq!
status_ids = cached_items.map { |item| item.reblog? ? item.reblog_of_id : item.id }.uniq
return if account_ids.empty?
accounts = Account.where(id: account_ids).includes(:account_stat, :user).index_by(&:id)
status_stats = StatusStat.where(status_id: status_ids).index_by(&:status_id)
cached_items.each do |item|
item.account = accounts[item.account_id]
item.reblog.account = accounts[item.reblog.account_id] if item.reblog?
if item.reblog?
status_stat = status_stats[item.reblog.id]
item.reblog.status_stat = status_stat if status_stat.present?
else
status_stat = status_stats[item.id]
item.status_stat = status_stat if status_stat.present?
end
end
end

View File

@ -16,6 +16,8 @@ class UserSettings
setting :default_sensitive, default: false
setting :default_privacy, default: nil, in: %w(public unlisted private)
setting_inverse_alias :indexable, :noindex
namespace :web do
setting :advanced_layout, default: false
setting :trends, default: true
@ -55,31 +57,26 @@ class UserSettings
end
def [](key)
key = key.to_sym
definition = self.class.definition_for(key)
raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key)
raise KeyError, "Undefined setting: #{key}" if definition.nil?
if @original_hash.key?(key)
@original_hash[key]
else
self.class.definition_for(key).default_value
end
definition.value_for(key, @original_hash[definition.key])
end
def []=(key, value)
key = key.to_sym
definition = self.class.definition_for(key)
raise KeyError, "Undefined setting: #{key}" unless self.class.definition_for?(key)
raise KeyError, "Undefined setting: #{key}" if definition.nil?
setting_definition = self.class.definition_for(key)
typecast_value = setting_definition.type_cast(value)
typecast_value = definition.type_cast(value)
raise ArgumentError, "Invalid value for setting #{key}: #{typecast_value}" if setting_definition.in.present? && setting_definition.in.exclude?(typecast_value)
raise ArgumentError, "Invalid value for setting #{definition.key}: #{typecast_value}" if definition.in.present? && definition.in.exclude?(typecast_value)
if typecast_value.nil?
@original_hash.delete(key)
@original_hash.delete(definition.key)
else
@original_hash[key] = typecast_value
@original_hash[definition.key] = definition.value_for(key, typecast_value)
end
end

View File

@ -10,6 +10,10 @@ module UserSettings::DSL
end
end
def setting_inverse_alias(key, original_key)
@definitions[key] = @definitions[original_key].inverse_of(key)
end
def namespace(key, &block)
@definitions ||= {}

View File

@ -10,6 +10,27 @@ class UserSettings::Setting
@in = options[:in]
end
def inverse_of(name)
@inverse_of = name.to_sym
self
end
def value_for(name, original_value)
value = begin
if original_value.nil?
default_value
else
original_value
end
end
if !@inverse_of.nil? && @inverse_of == name.to_sym
!value
else
value
end
end
def default_value
if @default_value.respond_to?(:call)
@default_value.call

View File

@ -115,6 +115,7 @@ class ActivityPub::ProcessAccountService < BaseService
@account.fields = property_values || {}
@account.also_known_as = as_array(@json['alsoKnownAs'] || []).map { |item| value_or_id(item) }
@account.discoverable = @json['discoverable'] || false
@account.indexable = @json['indexable'] || false
end
def set_fetchable_key!

View File

@ -8,9 +8,6 @@
= render 'shared/error_messages', object: current_user
= f.simple_fields_for :settings, current_user.settings do |ff|
.fields-group
= ff.input :noindex, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_noindex'), hint: I18n.t('simple_form.hints.defaults.setting_noindex')
.fields-group
= ff.input :aggregate_reblogs, wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_aggregate_reblogs'), hint: I18n.t('simple_form.hints.defaults.setting_aggregate_reblogs')
@ -26,9 +23,6 @@
.fields-group
= ff.input :default_sensitive, wrapper: :with_label, label: I18n.t('simple_form.labels.defaults.setting_default_sensitive'), hint: I18n.t('simple_form.hints.defaults.setting_default_sensitive')
.fields-group
= ff.input :show_application, wrapper: :with_label, recommended: true, label: I18n.t('simple_form.labels.defaults.setting_show_application'), hint: I18n.t('simple_form.hints.defaults.setting_show_application')
%h4= t 'preferences.public_timelines'
.fields-group

View File

@ -0,0 +1,43 @@
- content_for :page_title do
= t('privacy.title')
- content_for :heading do
%h2= t('settings.profile')
= render partial: 'settings/shared/profile_navigation'
= simple_form_for @account, url: settings_privacy_path, html: { method: :put } do |f|
= render 'shared/error_messages', object: @account
%p.lead= t('privacy.hint_html')
%h4= t('privacy.reach')
%p.lead= t('privacy.reach_hint_html')
.fields-group
= f.input :discoverable, as: :boolean, wrapper: :with_label, recommended: true
.fields-group
= f.input :unlocked, as: :boolean, wrapper: :with_label
%h4= t('privacy.search')
%p.lead= t('privacy.search_hint_html')
= f.simple_fields_for :settings, current_user.settings do |ff|
.fields-group
= ff.input :indexable, wrapper: :with_label
%h4= t('privacy.privacy')
%p.lead= t('privacy.privacy_hint_html')
.fields-group
= f.input :show_collections, as: :boolean, wrapper: :with_label
= f.simple_fields_for :settings, current_user.settings do |ff|
.fields-group
= ff.input :show_application, wrapper: :with_label
.actions
= f.button :button, t('generic.save_changes'), type: :submit

View File

@ -35,10 +35,10 @@
.fields-group
= f.input :avatar, wrapper: :with_block_label, input_html: { accept: AccountAvatar::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.avatar', dimensions: '400x400', size: number_to_human_size(AccountAvatar::LIMIT))
- if @account.avatar.present?
.fields-row__column.fields-row__column-6
.fields-group
= image_tag @account.avatar.url, class: 'fields-group__thumbnail', width: 90, height: 90
.fields-row__column.fields-row__column-6
.fields-group
= image_tag @account.avatar.url, class: 'fields-group__thumbnail', id: 'account_avatar-preview'
- if @account.avatar.present?
= link_to settings_profile_picture_path('avatar'), data: { method: :delete }, class: 'link-button link-button--destructive' do
= fa_icon 'trash fw'
= t('generic.delete')
@ -48,25 +48,14 @@
.fields-group
= f.input :header, wrapper: :with_block_label, input_html: { accept: AccountHeader::IMAGE_MIME_TYPES.join(',') }, hint: t('simple_form.hints.defaults.header', dimensions: '1500x500', size: number_to_human_size(AccountHeader::LIMIT))
- if @account.header.present?
.fields-row__column.fields-row__column-6
.fields-group
= image_tag @account.header.url, class: 'fields-group__thumbnail'
.fields-row__column.fields-row__column-6
.fields-group
= image_tag @account.header.url, class: 'fields-group__thumbnail', id: 'account_header-preview'
- if @account.header.present?
= link_to settings_profile_picture_path('header'), data: { method: :delete }, class: 'link-button link-button--destructive' do
= fa_icon 'trash fw'
= t('generic.delete')
%h4= t('edit_profile.safety_and_privacy')
.fields-group
= f.input :discoverable, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.discoverable'), recommended: true
.fields-group
= f.input :locked, as: :boolean, wrapper: :with_label, hint: t('simple_form.hints.defaults.locked')
.fields-group
= f.input :hide_collections, as: :boolean, wrapper: :with_label, label: t('simple_form.labels.defaults.setting_hide_network'), hint: t('simple_form.hints.defaults.setting_hide_network')
%h4= t('edit_profile.other')
.fields-group

View File

@ -2,5 +2,6 @@
= render_navigation renderer: :links do |primary|
:ruby
primary.item :profile, safe_join([fa_icon('user fw'), t('settings.edit_profile')]), settings_profile_path
primary.item :privacy, safe_join([fa_icon('lock fw'), t('privacy.title')]), settings_privacy_path
primary.item :verification, safe_join([fa_icon('check fw'), t('verification.verification')]), settings_verification_path
primary.item :featured_tags, safe_join([fa_icon('hashtag fw'), t('settings.featured_tags')]), settings_featured_tags_path

View File

@ -0,0 +1,14 @@
# frozen_string_literal: true
class AccountRefreshWorker
include Sidekiq::Worker
sidekiq_options queue: 'pull', retry: 3, dead: false, lock: :until_executed, lock_ttl: 1.day.to_i
def perform(account_id)
account = Account.find_by(id: account_id)
return if account.nil? || account.last_webfingered_at > Account::BACKGROUND_REFRESH_INTERVAL.ago
ResolveAccountService.new.call(account)
end
end

View File

@ -41,6 +41,8 @@ require_relative '../lib/mastodon/rack_middleware'
require_relative '../lib/public_file_server_middleware'
require_relative '../lib/devise/two_factor_ldap_authenticatable'
require_relative '../lib/devise/two_factor_pam_authenticatable'
require_relative '../lib/chewy/settings_extensions'
require_relative '../lib/chewy/index_extensions'
require_relative '../lib/chewy/strategy/mastodon'
require_relative '../lib/chewy/strategy/bypass_with_warning'
require_relative '../lib/webpacker/manifest_extensions'
@ -49,6 +51,7 @@ require_relative '../lib/rails/engine_extensions'
require_relative '../lib/active_record/database_tasks_extensions'
require_relative '../lib/active_record/batches'
require_relative '../lib/simple_navigation/item_extensions'
require_relative '../lib/http_extensions'
Dotenv::Railtie.load

View File

@ -6,5 +6,6 @@ Rails.application.configure do
config.x.cache_buster = {
secret_header: ENV['CACHE_BUSTER_SECRET_HEADER'],
secret: ENV['CACHE_BUSTER_SECRET'],
http_method: ENV['CACHE_BUSTER_HTTP_METHOD'] || 'GET',
}
end

View File

@ -15,6 +15,9 @@ Chewy.settings = {
journal: false,
user: user,
password: password,
index: {
number_of_replicas: ['single_node_cluster', nil].include?(ENV['ES_PRESET'].presence) ? 0 : 1,
},
}
# We use our own async strategy even outside the request-response
@ -25,14 +28,6 @@ Chewy.root_strategy = :bypass_with_warning if Rails.env.production?
Chewy.request_strategy = :mastodon
Chewy.use_after_commit_callbacks = false
module Chewy
class << self
def enabled?
settings[:enabled]
end
end
end
# Elasticsearch uses Faraday internally. Faraday interprets the
# http_proxy env variable by default which leads to issues when
# Mastodon is run with hidden services enabled, because

View File

@ -90,13 +90,19 @@ if ENV['S3_ENABLED'] == 'true'
# Some S3-compatible providers might not actually be compatible with some APIs
# used by kt-paperclip, see https://github.com/mastodon/mastodon/issues/16822
if ENV['S3_FORCE_SINGLE_REQUEST'] == 'true'
# and https://github.com/mastodon/mastodon/issues/26394
if ENV['S3_FORCE_SINGLE_REQUEST'] == 'true' || ENV['S3_DISABLE_CHECKSUM_MODE'] == 'true'
module Paperclip
module Storage
module S3Extensions
def copy_to_local_file(style, local_dest_path)
log("copying #{path(style)} to local file #{local_dest_path}")
s3_object(style).download_file(local_dest_path, { mode: 'single_request' })
options = {}
options[:mode] = 'single_request' if ENV['S3_FORCE_SINGLE_REQUEST'] == 'true'
options[:checksum_mode] = 'DISABLED' if ENV['S3_DISABLE_CHECKSUM_MODE'] == 'true'
s3_object(style).download_file(local_dest_path, options)
rescue Aws::Errors::ServiceError => e
warn("#{e} - cannot copy #{path(style)} to local file #{local_dest_path}")
false

View File

@ -53,3 +53,7 @@ ms:
position:
elevated: tidak boleh lebih tinggi daripada peranan semasa anda
own_role: tidak boleh diubah dengan peranan semasa anda
webhook:
attributes:
events:
invalid_permissions: tidak boleh memasukkan acara yang anda tidak mempunyai hak untuk melakukannya

View File

@ -399,6 +399,12 @@ ar:
confirm_suspension:
cancel: إلغاء
confirm: علّق الحساب
permanent_action: لن يستعيد رفع الحظر أي بيانات أو علاقات.
preamble_html: أنت على وشك تعليق <strong>%{domain}</strong> وجميع نطاقاته الفرعيّة.
remove_all_data: ستُحذف كل المحتوى والوسائط وبيانات حسابات هذا النطاق من خادومك.
stop_communication: سيتوقّف خادومك عن التواصل مع كل هذه الخادومات.
title: تأكيد حظر نطاق %{domain}
undo_relationships: سيتم التراجع عن أي علاقات متابعة ما بين الحسابات على هذه الخادومات وخادومك.
created_msg: إنّ حجب النطاق حيز التشغيل
destroyed_msg: تم إلغاء الحجب المفروض على النطاق
domain: النطاق
@ -779,6 +785,7 @@ ar:
approved: طلب الموافقة لازم عند إنشاء حساب
none: لا أحد يمكنه إنشاء حساب
open: يمكن للجميع إنشاء حساب
title: إعدادات الخادم
site_uploads:
delete: احذف الملف الذي تم تحميله
destroyed_msg: تم حذف التحميل مِن الموقع بنجاح!
@ -1039,6 +1046,7 @@ ar:
back: العودة
invited_by: 'يمكنك الانضمام إلى %{domain} بفضل الدعوة التي تلقيتها من:'
preamble: يتم تعيين هذه القوانين وفرضها من قبل مشرفي %{domain}.
preamble_invited: قبل المتابعة، يرجى قراءة القواعد الأساسية التي وضعها مشرفو %{domain}.
title: بعض القواعد الأساسية.
title_invited: لقد تلقيت دعوة.
security: الأمان
@ -1142,8 +1150,8 @@ ar:
domain_validator:
invalid_domain: ليس بإسم نطاق صالح
edit_profile:
basic_information: معلومات أساسية
other: أخرى
safety_and_privacy: الأمان والخصوصية
errors:
'400': الطلب الذي قدمته غير صالح أو أنّ شكله غير سليم.
'403': ليس لك الصلاحيات الكافية لعرض هذه الصفحة.
@ -1264,12 +1272,17 @@ ar:
overwrite: إعادة الكتابة
overwrite_long: استبدال التسجيلات الحالية بالجديدة
preface: بإمكانك استيراد بيانات قد قُمتَ بتصديرها مِن مثيل خادم آخَر، كقوائم المستخدِمين الذين كنتَ تتابِعهم أو قُمتَ بحظرهم.
states:
finished: تم
in_progress: قيد الإنجاز
success: تم تحميل بياناتك بنجاح وسيتم معالجتها في الوقت المناسب
type: نوع الاستيراد
types:
blocking: قائمة المحظورين
bookmarks: الفواصل المرجعية
domain_blocking: قائمة النطاقات المحظورة
following: قائمة المستخدمين المتبوعين
lists: القوائم
muting: قائمة الكتم
upload: تحميل
invites:
@ -1315,6 +1328,7 @@ ar:
mail_subscriptions:
unsubscribe:
complete: إلغاء الاشتراك
title: إلغاء الاشتراك
media_attachments:
validations:
images_and_video: ليس بالإمكان إرفاق فيديو في منشور يحتوي مسبقا على صور
@ -1430,6 +1444,7 @@ ar:
expired: لقد انتهى استطلاع الرأي
invalid_choice: خيار التصويت الذي قُمتَ يتحديده غير موجود
over_character_limit: لا يمكن أن يكون أطول من %{max} حرف لكل واحد
self_vote: لا يمكنك التصويت في استطلاعاتك الخاصة
too_few_options: يجب أن يحتوي على أكثر من عنصر واحد
too_many_options: لا يمكنه أن يحتوي أكثر مِن %{max} عناصر
preferences:
@ -1748,12 +1763,15 @@ ar:
title: أهلاً بك، %{name}!
users:
follow_limit_reached: لا يمكنك متابعة أكثر مِن %{limit} أشخاص
go_to_sso_account_settings: انتقل إلى إعدادات حساب مزود الهوية الخاص بك
invalid_otp_token: رمز المصادقة بخطوتين غير صالح
otp_lost_help_html: إن فقدتَهُما ، يمكنك الاتصال بـ %{email}
seamless_external_login: لقد قمت بتسجيل الدخول عبر خدمة خارجية، إنّ إعدادات الكلمة السرية و البريد الإلكتروني غير متوفرة.
signed_in_as: 'تم تسجيل دخولك بصفة:'
verification:
here_is_how: إليك كيف ذلك
verification: التحقق
verified_links: روابطك التي تم التحقق منها
webauthn_credentials:
add: إضافة مفتاح أمان جديد
create:

View File

@ -528,8 +528,6 @@ ast:
your_appeal_approved: Aprobóse la to apellación
your_appeal_pending: Unviesti una apellación
your_appeal_rejected: Refugóse la to apellación
edit_profile:
safety_and_privacy: Seguranza ya privacidá
errors:
'400': La solicitú qu'unviesti nun yera válida o yera incorreuta.
'403': Nun tienes permisu pa ver esta páxina.

View File

@ -1176,7 +1176,6 @@ be:
basic_information: Асноўная інфармацыя
hint_html: "<strong>Наладзьце тое, што людзі будуць бачыць у вашым профілі і побач з вашымі паведамленнямі.</strong> Іншыя людзі з большай верагоднасцю будуць сачыць і ўзаемадзейнічаць з вамі, калі ў вас ёсць запоўнены профіль і фота профілю."
other: Іншае
safety_and_privacy: Бяспека і прыватнасць
errors:
'400': Запыт, які вы адправілі, памылковы або няправільны.
'403': У вас няма дазволу на прагляд гэтай старонкі.

View File

@ -1139,7 +1139,6 @@ bg:
basic_information: Основна информация
hint_html: "<strong>Персонализирайте какво хората виждат в обществения ви профил и до публикациите ви.</strong> Другите хора са по-склонни да ви последват и да взаимодействат с вас, когато имате попълнен профил и снимка на профила."
other: Друго
safety_and_privacy: Безопасност и поверителност
errors:
'400': Подадохте невалидна или деформирана заявка.
'403': Нямате позволение да разгледате тази страница.

View File

@ -1140,7 +1140,6 @@ ca:
basic_information: Informació bàsica
hint_html: "<strong>Personalitza el que la gent veu en el teu perfil públic i a prop dels teus tuts..</strong> És més probable que altres persones et segueixin i interaccionin amb tu quan tens emplenat el teu perfil i amb la teva imatge."
other: Altres
safety_and_privacy: Seguretat i privacitat
errors:
'400': La sol·licitud que vas emetre no era vàlida o no era correcta.
'403': No tens permís per a veure aquesta pàgina.

View File

@ -1212,7 +1212,6 @@ cy:
basic_information: Gwybodaeth Sylfaenol
hint_html: "<strong>Addaswch yr hyn y mae pobl yn ei weld ar eich proffil cyhoeddus ac wrth ymyl eich postiadau.</strong> Mae pobl eraill yn fwy tebygol o'ch dilyn yn ôl a rhyngweithio â chi pan fydd gennych broffil wedi'i lenwi a llun proffil."
other: Arall
safety_and_privacy: Diogelwch a phreifatrwydd
errors:
'400': Roedd y cais a gyflwynwyd gennych yn annilys neu wedi'i gamffurfio.
'403': Nid oes gennych ganiatâd i weld y dudalen hon.

Some files were not shown because too many files have changed in this diff Show More