Compare commits

..

8 Commits

Author SHA1 Message Date
syuilo
b796aacf7f Merge branch 'develop' 2022-07-16 23:53:24 +09:00
syuilo
ff24811676 Merge branch 'develop' 2022-07-16 18:21:44 +09:00
syuilo
4c8a1867f0 Merge branch 'develop' 2022-07-15 22:45:13 +09:00
syuilo
bce48dfee9 Merge branch 'develop' 2022-07-13 21:59:47 +09:00
syuilo
c20311b8a7 Merge branch 'develop' 2022-07-09 18:32:55 +09:00
syuilo
fb14ac50b8 Merge branch 'develop' 2022-07-08 17:34:53 +09:00
syuilo
84d984bd31 Merge branch 'develop' 2022-07-07 21:23:03 +09:00
syuilo
1bc856c451 Merge pull request #8821 from misskey-dev/develop
Release: 12.111.1
2022-06-13 00:41:09 +09:00
1729 changed files with 63989 additions and 80437 deletions

View File

@@ -138,8 +138,8 @@ id: 'aid'
# Proxy remote files (default: false)
#proxyRemoteFiles: true
# Sign to ActivityPub GET request (default: true)
signToActivityPubGet: true
# Sign to ActivityPub GET request (default: false)
#signToActivityPubGet: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'

View File

@@ -10,15 +10,6 @@ db/
docker-compose.yml
elasticsearch/
node_modules/
packages/*/node_modules
redis/
files/
misskey-assets/
fluent-emojis/
.pnp.*
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions

View File

@@ -10,7 +10,6 @@ assignees: ''
<!--
Thanks for reporting!
First, in order to avoid duplicate Issues, please search to see if the problem you found has already been reported.
Also, If you are NOT owner/admin of server, PLEASE DONT REPORT SERVER SPECIFIC ISSUES TO HERE! (e.g. feature XXX is not working in misskey.example) Please try with another misskey servers, and if your issue is only reproducible with specific server, contact your server's owner/admin first.
-->
## 💡 Summary

View File

@@ -16,7 +16,7 @@ updates:
interval: daily
open-pull-requests-limit: 0
- package-ecosystem: npm
directory: "/packages/frontend"
directory: "/packages/client"
schedule:
interval: daily
open-pull-requests-limit: 0

2
.github/labeler.yml vendored
View File

@@ -2,7 +2,7 @@
- packages/backend/**/*
'🖥Client':
- packages/frontend/**/*
- packages/client/**/*
'🧪Test':
- cypress/**/*

View File

@@ -4,12 +4,12 @@ url: 'http://misskey.local'
port: 61812
db:
host: 127.0.0.1
host: localhost
port: 54312
db: test-misskey
user: postgres
pass: ''
redis:
host: 127.0.0.1
host: localhost
port: 56312
id: aid

View File

@@ -18,13 +18,6 @@ jobs:
uses: docker/metadata-action@v3
with:
images: misskey/misskey
tags: |
type=edge
type=ref,event=pr
type=ref,event=branch
type=semver,pattern={{version}}
type=semver,pattern={{major}}.{{minor}}
type=semver,pattern={{major}}
- name: Log in to Docker Hub
uses: docker/login-action@v1
with:

View File

@@ -8,39 +8,32 @@ on:
pull_request:
jobs:
yarn_install:
backend:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
submodules: true
- uses: actions/setup-node@v3.2.0
- uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'yarn'
- run: corepack enable
- run: yarn install --immutable
cache-dependency-path: |
packages/backend/yarn.lock
- run: yarn install
- run: yarn --cwd ./packages/backend lint
lint:
needs: [yarn_install]
client:
runs-on: ubuntu-latest
continue-on-error: true
strategy:
matrix:
workspace:
- backend
- frontend
- sw
steps:
- uses: actions/checkout@v2
with:
fetch-depth: 0
submodules: true
- uses: actions/setup-node@v3.2.0
- uses: actions/setup-node@v3
with:
node-version: 18.x
cache: 'yarn'
- run: corepack enable
- run: yarn install --immutable
- run: yarn workspace ${{ matrix.workspace }} run lint
cache-dependency-path: |
packages/client/yarn.lock
- run: yarn install
- run: yarn --cwd ./packages/client lint

View File

@@ -8,7 +8,7 @@ on:
pull_request:
jobs:
jest:
mocha:
runs-on: ubuntu-latest
strategy:
@@ -23,7 +23,6 @@ jobs:
env:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
YARN_CHECKSUM_BEHAVIOR: update
redis:
image: redis:6
ports:
@@ -34,12 +33,15 @@ jobs:
with:
submodules: true
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.2.0
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
- run: corepack enable
- run: yarn install --immutable
cache-dependency-path: |
packages/backend/yarn.lock
packages/client/yarn.lock
- name: Install dependencies
run: yarn install
- name: Check yarn.lock
run: git diff --exit-code yarn.lock
- name: Copy Configure
@@ -47,12 +49,7 @@ jobs:
- name: Build
run: yarn build
- name: Test
run: yarn jest-and-coverage
- name: Upload Coverage
uses: codecov/codecov-action@v3
with:
token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/coverage/coverage-final.json
run: yarn mocha
e2e:
runs-on: ubuntu-latest
@@ -87,14 +84,17 @@ jobs:
#- uses: browser-actions/setup-firefox@latest
# if: ${{ matrix.browser == 'firefox' }}
- name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.2.0
uses: actions/setup-node@v3
with:
node-version: ${{ matrix.node-version }}
cache: 'yarn'
- run: corepack enable
- run: yarn install --immutable
env:
YARN_CHECKSUM_BEHAVIOR: update
cache-dependency-path: |
packages/backend/yarn.lock
packages/client/yarn.lock
- name: Install dependencies
run: yarn install
- name: Check yarn.lock
run: git diff --exit-code yarn.lock
- name: Copy Configure
run: cp .github/misskey/test.yml .config
- name: Build
@@ -106,7 +106,7 @@ jobs:
uses: cypress-io/github-action@v4
with:
install: false
start: yarn start:test
start: npm run start:test
wait-on: 'http://localhost:61812'
headless: false
browser: ${{ matrix.browser }}

14
.gitignore vendored
View File

@@ -9,24 +9,10 @@
node_modules
report.*.json
# Yarn
.yarn/*
!.yarn/patches
!.yarn/plugins
!.yarn/releases
!.yarn/sdks
!.yarn/versions
packages/frontend/.yarn/cache
packages/backend/.yarn/cache
packages/sw/.yarn/cache
# Cypress
cypress/screenshots
cypress/videos
# Coverage
coverage
# config
/.config/*
!/.config/example.yml

3
.gitmodules vendored
View File

@@ -1,6 +1,3 @@
[submodule "misskey-assets"]
path = misskey-assets
url = https://github.com/misskey-dev/assets.git
[submodule "fluent-emojis"]
path = fluent-emojis
url = https://github.com/misskey-dev/emojis.git

View File

@@ -1 +1 @@
v18.12.1
v16.15.0

2
.npmrc Normal file
View File

@@ -0,0 +1,2 @@
save-exact = true
package-lock = false

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@@ -1,42 +0,0 @@
httpTimeout: 600000
nmHoistingLimits: none
nodeLinker: pnpm
packageExtensions:
"@bull-board/api@*":
peerDependencies:
"@bull-board/ui": "*"
"@tensorflow/tfjs@*":
dependencies:
long: "*"
chartjs-adapter-date-fns@*:
peerDependencies:
date-fns: "*"
consolidate@*:
dependencies:
ejs: "*"
# these are needed to extend fastify types
"@fastify/accepts@*":
peerDependencies:
fastify: "*"
"@fastify/cookie@*":
peerDependencies:
fastify: "*"
"@fastify/static@*":
peerDependencies:
fastify: "*"
"@fastify/view@*":
peerDependencies:
fastify: "*"
plugins:
- path: .yarn/plugins/@yarnpkg/plugin-interactive-tools.cjs
spec: "@yarnpkg/plugin-interactive-tools"
- path: .yarn/plugins/@yarnpkg/plugin-workspace-tools.cjs
spec: "@yarnpkg/plugin-workspace-tools"
progressBarStyle: patrick
yarnPath: .yarn/releases/yarn-3.3.0.cjs

View File

@@ -1,141 +1,14 @@
<!--
## 13.x.x (unreleased)
## 12.x.x (unreleased)
### Improvements
### Bugfixes
-
-
You should also include the user name that made the change.
-->
## 13.0.0 (unreleased)
### Changes
- Node.js 18.x or later is required
- Elasticsearchのサポートが削除されました
- 代わりに今後任意の検索プロバイダを設定できる仕組みを構想しています。その仕組みを使えば今まで通りElasticsearchも利用できます
- ノートのウォッチ機能が削除されました
- Migrate to Yarn Berry (v3.2.1) @ThatOneCalculator
- You may have to `yarn run clean-all`, `sudo corepack enable` and `yarn set version berry` before running `yarn install` if you're still on yarn classic
- 新たに動的なPagesを作ることはできなくなりました
- 代わりに今後AiScriptを用いてより柔軟に動的なコンテンツを作成できるMisskey Play機能の実装を予定しています。
- iOS15以下のデバイスはサポートされなくなりました
- API: カスタム絵文字エンティティに`url`プロパティが含まれなくなりました
- 絵文字画像を表示するには、`<instance host>/emoji/<emoji name>.webp`にリクエストすると画像が返ります。
- e.g. `https://p1.a9z.dev/emoji/misskey.webp`
- remote: `https://p1.a9z.dev/emoji/syuilo_birth_present@mk.f72u.net.webp`
- API: `user`および`note`エンティティに`emojis`プロパティが含まれなくなりました
- API: `user`エンティティに`avatarColor`および`bannerColor`プロパティが含まれなくなりました
### Improvements
- Push notification of Antenna note @tamaina
- AVIF support @tamaina
- Add Cloudflare Turnstile CAPTCHA support @CyberRex0
- Introduce retention-rate aggregation @syuilo
- Make possible to export favorited notes @syuilo
- Add per user pv chart @syuilo
- Server: signToActivityPubGet is set to true by default @syuilo
- Server: improve syslog performance @syuilo
- Server: improve note scoring for featured notes @CyberRex0
- Server: delete outdated notifications regularly to improve db performance @syuilo
- Server: delete outdated hard-mutes regularly to improve db performance @syuilo
- Server: delete outdated notes of antenna regularly to improve db performance @syuilo
- Client: use tabler-icons instead of fontawesome to better design @syuilo
- Client: Add new gabber kick sounds (thanks for noizenecio)
- Client: Add link to user RSS feed in profile menu @ssmucny
- Client: Compress non-animated PNG files @saschanaz
- Client: YouTube window player @sim1222
- Client: enhance dashboard of control panel @syuilo
- Client: Vite is upgraded to v4 @syuilo, @tamaina
- Client: HMR is available while yarn dev @tamaina
- Client: Make widgets of universal/classic sync between devices @tamaina
- Client: Implement the button to subscribe push notification @tamaina
- Client: Implement the toggle to or not to close push notifications when notifications or messages are read @tamaina
- Client: show Unicode emoji tooltip with its name in MkReactionsViewer.reaction @saschanaz
- Client: OpenSearch support @SoniEx2 @chaoticryptidz
- Client: add user list widget @syuilo
- Client: add heatmap of daily active users to about page @syuilo
- Client: introduce fluent emoji @syuilo
- Client: show bot warning on screen when logged in as bot account @syuilo
- Client: improve overall performance of client @syuilo
- Client: ui tweaks @syuilo
### Bugfixes
- Server: 引用内の文章がnyaizeされてしまう問題を修正 @kabo2468
- Server: Bug fix for Pinned Users lookup on instance @squidicuzz
- Server: Fix peers API returning suspended instances @ineffyble
- Server: trim long text of note from ap @syuilo
- Server: Ap inboxの最大ペイロードサイズを64kbに制限 @syuilo
- Server: アンテナの作成数上限を追加 @syuilo
- Client: case insensitive emoji search @saschanaz
- Client: InAppウィンドウが操作できなくなることがあるのを修正 @tamaina
- Client: use proxied image for instance icon @syuilo
- Client: Webhookの編集画面で、内容を保存することができない問題を修正 @m-hayabusa
- Client: update emoji picker immediately on all input @saschanaz
- Client: チャートのツールチップが画面に残ることがあるのを修正 @syuilo
- Client: fix wrong link in tutorial @syuilo
## 12.119.1 (2022/12/03)
### Bugfixes
- Server: Mitigate AP reference chain DoS vector @skehmatics
## 12.119.0 (2022/09/10)
### Improvements
- Client: Add following badge to user preview popup @nvisser
- Client: mobile twitter url can be used as widget @caipira113
- Client: Improve clock widget @syuilo
### Bugfixes
- マイグレーションに失敗する問題を修正
- Server: 他人の通知を既読にできる可能性があるのを修正 @syuilo
- Client: アクセストークン管理画面、アカウント管理画面表示できないのを修正 @futchitwo
## 12.118.1 (2022/08/08)
### Bugfixes
- Client: can not show some setting pages @syuilo
## 12.118.0 (2022/08/07)
### Improvements
- Client: 設定のバックアップ/リストア機能
- Client: Add vi-VN language support
- Client: Add unix time widget @syuilo
### Bugfixes
- Server: リモートユーザーを正しくブロックできるように修正する @xianonn
- Client: 一度作ったwebhookの設定画面を開こうとするとページがフリーズする @syuilo
- Client: MiAuth認証ページが機能していない @syuilo
- Client: 一部のアプリからファイルを投稿フォームへドロップできない場合がある問題を修正 @m-hayabusa
## 12.117.1 (2022/07/19)
### Improvements
- Client: UIのブラッシュアップ @syuilo
### Bugfixes
- Server: ファイルのアップロードに失敗することがある問題を修正 @acid-chicken
- Client: リアクションピッカーがアプリ内ウィンドウの後ろに表示されてしまう問題を修正 @syuilo
- Client: ユーザー情報の取得の再試行を修正 @xianonn
- Client: MFMチートシートの挙動を修正 @syuilo
- Client: 「インスタンスからのお知らせを受け取る」の設定を変更できない問題を修正 @syuilo
## 12.117.0 (2022/07/18)
### Improvements
- Client: ウィンドウを最大化できるように @syuilo
- Client: Shiftキーを押した状態でリンクをクリックするとアプリ内ウィンドウで開くように @syuilo
- Client: デッキを使用している際、Ctrlキーを押した状態でリンクをクリックするとページ遷移を強制できるように @syuilo
- Client: UIのブラッシュアップ @syuilo
## 12.116.1 (2022/07/17)
### Bugfixes
- Client: デッキUI時に ページで表示 ボタンが機能しない問題を修正 @syuilo
- Error During Migration Run to 12.111.x
## 12.116.0 (2022/07/16)
### Improvements
@@ -435,7 +308,7 @@ same as 12.112.0
## 12.104.0 (2022/02/09)
### Note
ビルドする前に`yarn clean`を実行してください。
ビルドする前に`npm run clean`を実行してください。
このリリースはマイグレーションの規模が大きいため、インスタンスによってはマイグレーションに時間がかかる可能性があります。
マイグレーションが終わらない場合は、チャートの情報はリセットされてしまいますが`__chart__`で始まるテーブルの**レコード**を全て削除(テーブル自体は消さないでください)してから再度試す方法もあります。

View File

@@ -44,7 +44,7 @@ Thank you for your PR! Before creating a PR, please check the following:
- Check if there are any documents that need to be created or updated due to this change.
- If you have added a feature or fixed a bug, please add a test case if possible.
- Please make sure that tests and Lint are passed in advance.
- You can run it with `yarn test` and `yarn lint`. [See more info](#testing)
- You can run it with `npm run test` and `npm run lint`. [See more info](#testing)
- If this PR includes UI changes, please attach a screenshot in the text.
Thanks for your cooperation 🤗
@@ -99,17 +99,9 @@ If your language is not listed in Crowdin, please open an issue.
![Crowdin](https://d322cqt584bo4o.cloudfront.net/misskey/localized.svg)
## Development
During development, it is useful to use the
```
yarn dev
```
command.
- Server-side source files and automatically builds them if they are modified. Automatically start the server process(es).
- Vite HMR (just the `vite` command) is available. The behavior may be different from production.
- Service Worker is watched by esbuild.
During development, it is useful to use the `npm run dev` command.
This command monitors the server-side and client-side source files and automatically builds them if they are modified.
In addition, it will also automatically start the Misskey server process.
## Testing
- Test codes are located in [`/test`](/test).
@@ -117,22 +109,22 @@ command.
### Run test
Create a config file.
```
cp .github/misskey/test.yml .config/
cp test/test.yml .config/
```
Prepare DB/Redis for testing.
```
docker-compose -f packages/backend/test/docker-compose.yml up
docker-compose -f test/docker-compose.yml up
```
Alternatively, prepare an empty (data can be erased) DB and edit `.config/test.yml`.
Run all test.
```
yarn test
npm run test
```
#### Run specify test
```
yarn jest -- foo.ts
npx cross-env TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT="./test/tsconfig.json" npx mocha test/foo.ts --require ts-node/register
```
### e2e tests
@@ -148,34 +140,6 @@ Misskey uses Vue(v3) as its front-end framework.
- **When creating a new component, please use the Composition API (with [setup sugar](https://v3.vuejs.org/api/sfc-script-setup.html) and [ref sugar](https://github.com/vuejs/rfcs/discussions/369)) instead of the Options API.**
- Some of the existing components are implemented in the Options API, but it is an old implementation. Refactors that migrate those components to the Composition API are also welcome.
## nirax
niraxは、Misskeyで使用しているオリジナルのフロントエンドルーティングシステムです。
**vue-routerから影響を多大に受けているので、まずはvue-routerについて学ぶことをお勧めします。**
### ルート定義
ルート定義は、以下の形式のオブジェクトの配列です。
``` ts
{
name?: string;
path: string;
component: Component;
query?: Record<string, string>;
loginRequired?: boolean;
hash?: string;
globalCacheKey?: string;
children?: RouteDef[];
}
```
> **Warning**
> 現状、ルートは定義された順に評価されます。
> たとえば、`/foo/:id`ルート定義の次に`/foo/bar`ルート定義がされていた場合、後者がマッチすることはありません。
### 複数のルーター
vue-routerとの最大の違いは、niraxは複数のルーターが存在することを許可している点です。
これにより、アプリ内ウィンドウでブラウザとは個別にルーティングすることなどが可能になります。
## Notes
### How to resolve conflictions occurred at yarn.lock?
@@ -265,7 +229,7 @@ MongoDBは`null`で返してきてたので、その感覚で`if (x === null)`
### Migration作成方法
packages/backendで:
```sh
yarn dlx typeorm migration:generate -d ormconfig.js -o <migration name>
npx typeorm migration:generate -d ormconfig.js -o <migration name>
```
- 生成後、ファイルをmigration下に移してください

View File

@@ -1,45 +1,32 @@
FROM node:18.12.1-bullseye AS builder
FROM node:16.15.1-bullseye AS builder
ARG NODE_ENV=production
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
build-essential
WORKDIR /misskey
COPY [".yarnrc.yml", "package.json", "yarn.lock", "./"]
COPY [".yarn", "./.yarn"]
COPY ["scripts", "./scripts"]
COPY ["packages/backend/package.json", "./packages/backend/"]
COPY ["packages/frontend/package.json", "./packages/frontend/"]
COPY ["packages/sw/package.json", "./packages/sw/"]
RUN yarn install --immutable
COPY . ./
RUN apt-get update
RUN apt-get install -y build-essential
RUN git submodule update --init
RUN yarn install
RUN yarn build
RUN rm -rf .git
FROM node:18.12.1-bullseye-slim AS runner
FROM node:16.15.1-bullseye-slim AS runner
WORKDIR /misskey
RUN apt-get update \
&& apt-get install -y --no-install-recommends \
ffmpeg tini \
&& apt-get -y clean \
&& rm -rf /var/lib/apt/lists/*
RUN apt-get update
RUN apt-get install -y ffmpeg tini
COPY --from=builder /misskey/.yarn/install-state.gz ./.yarn/install-state.gz
COPY --from=builder /misskey/node_modules ./node_modules
COPY --from=builder /misskey/built ./built
COPY --from=builder /misskey/packages/backend/node_modules ./packages/backend/node_modules
COPY --from=builder /misskey/packages/backend/built ./packages/backend/built
COPY --from=builder /misskey/packages/frontend/node_modules ./packages/frontend/node_modules
COPY --from=builder /misskey/packages/client/node_modules ./packages/client/node_modules
COPY . ./
ENV NODE_ENV=production
ENTRYPOINT ["/usr/bin/tini", "--"]
CMD ["yarn", "run", "migrateandstart"]
CMD ["npm", "run", "migrateandstart"]

View File

@@ -6,20 +6,15 @@ Also, the later tasks are more indefinite and are subject to change as developme
This is the phase we are at now. We need to make a high-maintenance environment that can withstand future development.
- Make the number of type errors zero (backend)
- Probably need to switch some libraries to others that make it difficult to reduce type errors
- e.g. koa to fastify https://github.com/misskey-dev/misskey/issues/7537
- Probably need to switch some libraries to others that make it difficult to reduce type errors
- e.g. koa to fastify https://github.com/misskey-dev/misskey/issues/7537
- Improve CI
- Fix tests
- mocha, jest, etc. do not support the combination of `TypeScript + ESM + Path alias`, and the tests currently do not work.
- Fix random test failures - https://github.com/misskey-dev/misskey/issues/7985 and https://github.com/misskey-dev/misskey/issues/7986
- Add more tests
- May need to implement a mechanism that allows for DI
- https://github.com/misskey-dev/misskey/pull/9085
- Measure coverage
- https://github.com/misskey-dev/misskey/pull/9081
- Fix tests
- mocha, jest, etc. do not support the combination of `TypeScript + ESM + Path alias`, and the tests currently do not work.
- Fix random test failures - https://github.com/misskey-dev/misskey/issues/7985 and https://github.com/misskey-dev/misskey/issues/7986
- Add more tests
- May need to implement a mechanism that allows for DI
- Improve documentation
- Refactoring
- Extract the logic of each endpoint definition into a service and just call it
## (2) Improve functionality
Once Phase 1 is complete and an environment conducive to the development of a stable system is in place, the implementation of new functions can begin gradually.

Binary file not shown.

Before

Width:  |  Height:  |  Size: 41 KiB

View File

@@ -154,8 +154,8 @@ id: "aid"
# Media Proxy
#mediaProxy: https://example.com/proxy
# Sign to ActivityPub GET request (default: true)
signToActivityPubGet: true
# Sign to ActivityPub GET request (default: false)
#signToActivityPubGet: true
#allowedPrivateNetworks: [
# '127.0.0.1/32'

View File

@@ -28,7 +28,7 @@ Cypress.Commands.add('resetState', () => {
cy.window(win => {
win.indexedDB.deleteDatabase('keyval-store');
});
cy.request('POST', '/api/reset-db', {}).as('reset');
cy.request('POST', '/api/reset-db').as('reset');
cy.get('@reset').its('status').should('equal', 204);
cy.reload(true);
});

Submodule fluent-emojis deleted from cae981eb4c

View File

@@ -15,21 +15,21 @@ gulp.task('copy:backend:views', () =>
gulp.src('./packages/backend/src/server/web/views/**/*').pipe(gulp.dest('./packages/backend/built/server/web/views'))
);
gulp.task('copy:frontend:fonts', () =>
gulp.src('./packages/frontend/node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/_frontend_dist_/fonts/'))
gulp.task('copy:client:fonts', () =>
gulp.src('./packages/client/node_modules/three/examples/fonts/**/*').pipe(gulp.dest('./built/_client_dist_/fonts/'))
);
gulp.task('copy:frontend:tabler-icons', () =>
gulp.src('./packages/frontend/node_modules/@tabler/icons/iconfont/**/*').pipe(gulp.dest('./built/_frontend_dist_/tabler-icons/'))
gulp.task('copy:client:fontawesome', () =>
gulp.src('./packages/client/node_modules/@fortawesome/fontawesome-free/**/*').pipe(gulp.dest('./built/_client_dist_/fontawesome/'))
);
gulp.task('copy:frontend:locales', cb => {
fs.mkdirSync('./built/_frontend_dist_/locales', { recursive: true });
gulp.task('copy:client:locales', cb => {
fs.mkdirSync('./built/_client_dist_/locales', { recursive: true });
const v = { '_version_': meta.version };
for (const [lang, locale] of Object.entries(locales)) {
fs.writeFileSync(`./built/_frontend_dist_/locales/${lang}.${meta.version}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8');
fs.writeFileSync(`./built/_client_dist_/locales/${lang}.${meta.version}.json`, JSON.stringify({ ...locale, ...v }), 'utf-8');
}
cb();
@@ -53,7 +53,7 @@ gulp.task('build:backend:style', () => {
});
gulp.task('build', gulp.parallel(
'copy:frontend:locales', 'copy:backend:views', 'build:backend:script', 'build:backend:style', 'copy:frontend:fonts', 'copy:frontend:tabler-icons'
'copy:client:locales', 'copy:backend:views', 'build:backend:script', 'build:backend:style', 'copy:client:fonts', 'copy:client:fontawesome'
));
gulp.task('default', gulp.task('build'));

View File

@@ -12,7 +12,6 @@ fetchingAsApObject: "جارٍ جلبه مِن الفديفرس…"
ok: " حسناً"
gotIt: "فهِمت"
cancel: " إلغاء"
noThankYou: "ليس اﻵن"
enterUsername: "أدخِل إسم مسخدم"
renotedBy: "أعاد نشرها {user}"
noNotes: "لم يُعثر على أية ملاحظات"
@@ -53,7 +52,6 @@ searchUser: "ابحث عن مستخدمين"
reply: "رد"
loadMore: "عرض المزيد"
showMore: "عرض المزيد"
showLess: "اغلق"
youGotNewFollower: "يتابعك"
receiveFollowRequest: "تلقيت طلب متابعة"
followRequestAccepted: "قُبل طلب المتابعة"
@@ -203,7 +201,6 @@ done: "تمّ"
processing: "المعالجة جارية"
preview: "معاينة"
default: "افتراضي"
defaultValueIs: "الافتراضي: {value}"
noCustomEmojis: "ليس هناك إيموجي"
noJobs: "لا توجد مهام"
federating: "الفديرالية جارية"
@@ -345,8 +342,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "تمكين reCAPTCHA"
recaptchaSiteKey: "مفتاح الموقع"
recaptchaSecretKey: "المفتاح السري"
turnstileSiteKey: "مفتاح الموقع"
turnstileSecretKey: "المفتاح السري"
avoidMultiCaptchaConfirm: "يمكن أن يتسبب استخدام عدة خدمات لكلمات التحقق في حدوث تداخل. هل ترغب في إلغاء تنشيط الخدمات الأخرى؟ يمكنك ترك هذه الخدمات نشطة بالضغط على \"ألغ\"."
antennas: "الهوائيات"
manageAntennas: "إدارة الهوائيات"
@@ -447,6 +442,7 @@ language: "اللغة"
uiLanguage: "لغة واجهة المستخدم"
groupInvited: "دُعيت إلى فريقٍ"
aboutX: "عن {x}"
useOsNativeEmojis: "استخدم الإيموجي الخاصة بنظام التشغيل"
youHaveNoGroups: "لا تمتلك أية فِرَق"
joinOrCreateGroup: "احصل على دعوة لفريق أو أنشئ واحدًا."
noHistory: "السجل فارغ"
@@ -489,7 +485,6 @@ deleteAll: "حذف الكل"
showFixedPostForm: "أظهر نموذج الكتابة في أعلى الصفحة"
newNoteRecived: "هناك ملاحظات جديدة"
sounds: "الرنات"
sound: "الرنات"
listen: "استمع"
none: "لا شيء"
showInPage: "اعرض في الصفحة"
@@ -1131,8 +1126,6 @@ _widgets:
onlineUsers: "المتّصلون"
jobQueue: "قائمة الانتظار"
serverMetric: "إحصائيات الخادم"
_userList:
chooseList: "اختر قائمة"
_cw:
hide: "إخفاء"
show: "عرض المزيد"
@@ -1262,6 +1255,8 @@ _pages:
eyeCatchingImageRemove: "احذف صورة مصغّرة"
chooseBlock: "إضافة كتلة"
selectType: "اختر النوع"
enterVariableName: "أدخل اسم المتغيّر"
variableNameIsAlreadyUsed: "هذا الاسم محجوز"
contentBlocks: "المحتوى"
inputBlocks: "مُدخل"
specialBlocks: "خاص"
@@ -1271,11 +1266,225 @@ _pages:
section: "قسم"
image: "الصور"
button: "زرّ"
_if:
variable: "متغيّر"
post: "أنشئ ملاحظة"
_post:
text: "المحتوى"
textInput: "مُدخل نصي"
_textInput:
name: "اسم المتغير"
text: "العنوان"
default: "القيمة الافتراضية"
textareaInput: "مدخل نصي متعدد الأسطر"
_textareaInput:
name: "اسم المتغير"
text: "العنوان"
default: "القيمة الافتراضية"
numberInput: "مُدخل رقمي"
_numberInput:
name: "اسم المتغير"
text: "العنوان"
default: "القيمة الافتراضية"
_canvas:
width: "العُرض"
height: "الإرتفاع"
note: "ملاحظة مضمّنة"
_note:
id: "معرّف الملاحظة"
idDescription: "كبديل يمكنك إدخال رابك الملاحظة هنا"
detailed: "عرض مفصّل"
switch: "بدّل"
_switch:
name: "اسم المتغير"
text: "العنوان"
default: "القيمة الافتراضية"
counter: "العداد"
_counter:
name: "اسم المتغير"
text: "العنوان"
inc: "زِد"
_button:
text: "العنوان"
colored: "ملوّن"
action: "الإجراء عند ضغط الزّر"
_action:
dialog: "أظهر مربع حوار"
_dialog:
content: "المحتوى"
resetRandom: "صفِّر البذرة"
pushEvent: "أرسل حدثًا"
_pushEvent:
event: "اسم الحدث"
message: "إظهار رسالة عند التفعيل"
variable: "أرسل المتغيّر"
no-variable: "لا شيء"
_callAiScript:
functionName: "اسم الدالة"
radioButton: "الخيار "
_radioButton:
name: "اسم المتغير"
title: "العنوان"
values: "قائمة الخيارات (كل خيار في سطر لوحده)"
default: "القيمة الافتراضية"
script:
categories:
logical: "عمليّة منطقيّة"
operation: "حساب"
comparison: "مقارنة"
random: "عشوائي"
value: "القيم"
fn: "دوال"
text: "إجراءات على النصوص"
convert: "تحويل"
list: "القوائم"
blocks:
text: "نص"
textList: "قائمة نصية"
_textList:
info: "اجعل كل مدخل في سطر لوحده"
strLen: "طول النص"
_strLen:
arg1: "نص"
strPick: "استخرج محرفًا"
_strPick:
arg1: "نص"
arg2: "موضع المحرف"
strReplace: "استبدال النّص"
_strReplace:
arg1: "نص"
arg2: "استُبدِل بـ"
arg3: "استُبدِل بـ"
strReverse: "اقلب النص"
_strReverse:
arg1: "نص"
_join:
arg1: "القوائم"
arg2: "فاصل"
add: "إضافة"
_add:
arg1: "أ"
arg2: "ب"
subtract: "اطرح"
_subtract:
arg1: "أ"
arg2: "ب"
multiply: "اضرب"
_multiply:
arg1: "أ"
arg2: "ب"
divide: "اقسم"
_divide:
arg1: "أ"
arg2: "ب"
mod: "الباقي"
_mod:
arg1: "أ"
arg2: "ب"
round: "تقريب عدد عشري"
_round:
arg1: "رقم"
eq: "أ و ب متساويان"
_eq:
arg1: "أ"
arg2: "ب"
notEq: "أ و ب مختلفان"
_notEq:
arg1: "أ"
arg2: "ب"
and: "أ و ب"
_and:
arg1: "أ"
arg2: "ب"
or: "أ أو ب"
_or:
arg1: "أ"
arg2: "ب"
lt: "أ أصغر من ب"
_lt:
arg1: "أ"
arg2: "ب"
gt: "أ أكبر من ب"
_gt:
arg1: "أ"
arg2: "ب"
ltEq: "أ أصغر من أو يساوي ب"
_ltEq:
arg1: "أ"
arg2: "ب"
gtEq: "أ أكبر من أو يساوي ب"
_gtEq:
arg1: "أ"
arg2: "ب"
if: "فرع"
random: "عشوائي"
rannum: "رقم عشوائي"
_rannum:
arg1: "أدنى قيمة"
arg2: "أقصى قيمة"
randomPick: "اختر عشوائيًا من القائمة"
_randomPick:
arg1: "القوائم"
dailyRandom: "عشوائي (يتغير مرة يوميًا لكل مستخدم)"
dailyRannum: "رقم عشوائي (يتغير مرة يوميًا لكل مستخدم)"
_dailyRannum:
arg1: "أدنى قيمة"
arg2: "أقصى قيمة"
dailyRandomPick: "اختيار عشوائي من قائمة (يتغير مرة يوميًا لكل مستخدم)"
_dailyRandomPick:
arg1: "القوائم"
seedRandom: "عشوائي (عبر بذرة)"
_seedRandom:
arg1: "البذرة"
seedRannum: "رقم عشوائي (عبر بذرة)"
_seedRannum:
arg1: "البذرة"
arg2: "أدنى قيمة"
arg3: "أقصى قيمة"
seedRandomPick: "اختيار عشوائي من القائمة (عبر بذرة)"
_seedRandomPick:
arg1: "البذرة"
arg2: "القوائم"
DRPWPM: "اختيار عشوائي من قائمة الاحتمالات (تتغير مرة يوميًا لكل مستخدم)"
_DRPWPM:
arg1: "قائمة نصية"
pick: "اختر من القائمة"
_pick:
arg1: "القوائم"
arg2: "الموضع"
listLen: "طول القائمة"
_listLen:
arg1: "القوائم"
number: "رقم"
stringToNumber: "حوّل نصًا إلى رقم"
_stringToNumber:
arg1: "نص"
numberToString: "حوّل رقمًا إلى نص"
_numberToString:
arg1: "رقم"
_splitStrByLine:
arg1: "نص"
ref: "متغيّر"
aiScriptVar: "متغيّر AiScript"
fn: "دالة"
_fn:
slots: "خانات"
arg1: "المُخرج"
for: "حلقة تكرار"
_for:
arg1: "عدد مرات التكرار"
arg2: "الإجراء"
typeError: "الخانة {slot} تقبل \"{expect}\" لكن القيمة المعطاة هي \"{actual}\"!"
thereIsEmptySlot: "الخانة {slot} فارغة!"
types:
string: "نص"
number: "رقم"
array: "القوائم"
stringArray: "قائمة نصية"
emptySlot: "خانة فارغة"
enviromentVariables: "متغيرات البيئة"
pageVariables: "متغيرات الصفحة"
argVariables: "خانة إدخال"
_relayStatus:
requesting: "مُعلّق"
accepted: "مقبول"

View File

@@ -52,7 +52,6 @@ searchUser: "ব্যবহারকারী খুঁজুন..."
reply: "জবাব"
loadMore: "আরও দেখুন"
showMore: "আরও দেখুন"
showLess: "বন্ধ"
youGotNewFollower: "আপনাকে অনুসরণ করছে"
receiveFollowRequest: "অনুসরণ করার জন্য অনুরোধ পাওয়া গেছে"
followRequestAccepted: "অনুসরণ করার অনুরোধ গৃহীত হয়েছে"
@@ -347,8 +346,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHA চালু করুন"
recaptchaSiteKey: "সাইট কী"
recaptchaSecretKey: "সিক্রেট কী"
turnstileSiteKey: "সাইট কী"
turnstileSecretKey: "সিক্রেট কী"
avoidMultiCaptchaConfirm: "একাধিক Captcha ব্যবহার করলে তারা পরস্পরের কাজে বাধা দিতে পারে। আপনি কি অন্যান্য Captcha নিষ্ক্রিয় করতে চান? আপনি 'বাতিল' ক্লিক করার মাধ্যমে একাধিক Captcha চালু রাখতে পারেন।"
antennas: "অ্যান্টেনা"
manageAntennas: "অ্যান্টেনা ব্যবস্থাপনা"
@@ -450,6 +447,7 @@ language: "ভাষা"
uiLanguage: "UI এর ভাষা"
groupInvited: "আপনি একটি গ্রুপে আমন্ত্রিত হয়েছেন"
aboutX: "{x} সম্পর্কে"
useOsNativeEmojis: "অপারেটিং সিস্টেমের নেটিভ ইমোজি ব্যবহার করুন"
disableDrawer: "ড্রয়ার মেনু প্রদর্শন করবেন না"
youHaveNoGroups: "আপনার কোন গ্রুপ নেই "
joinOrCreateGroup: "একটি বিদ্যমান গ্রুপের আমন্ত্রণ পান বা একটি নতুন গ্রুপ তৈরি করুন৷"
@@ -502,7 +500,6 @@ deleteAll: "সব মুছুন"
showFixedPostForm: "টাইমলাইনের শীর্ষে পোস্ট করার ফর্মটি দেখান"
newNoteRecived: "নতুন নোট আছে"
sounds: "শব্দ"
sound: "শব্দ"
listen: "শুনুন"
none: "কিছুই না"
showInPage: "পেজে দেখান"
@@ -1218,8 +1215,6 @@ _widgets:
serverMetric: "সার্ভার মেট্রিক্স"
aiscript: "AiScript কনসোল"
aichan: "আই চান"
_userList:
chooseList: "লিস্ট নির্বাচন করুন"
_cw:
hide: "লুকান"
show: "আরও দেখুন"
@@ -1355,6 +1350,8 @@ _pages:
eyeCatchingImageRemove: "থাম্বনেইল সরান"
chooseBlock: "ব্লক যোগ করুন"
selectType: "ধরন নির্বাচন করুন"
enterVariableName: "চলকের নাম লিখুন"
variableNameIsAlreadyUsed: "চলকের নামটি ইতিপূর্বে ব্যাবহৃত হয়েছে"
contentBlocks: "বিষয়বস্তু"
inputBlocks: "ইনপুট"
specialBlocks: "বিশেষ"
@@ -1364,11 +1361,249 @@ _pages:
section: "বিভাগ"
image: "ছবি"
button: "বাটন"
if: "যদি"
_if:
variable: "চলকগুলি"
post: "নোট লিখুন"
_post:
text: "বিষয়বস্তু"
attachCanvasImage: "ক্যানভাস ছবিসহ পোস্ট করুন"
canvasId: "ক্যানভাস ID"
textInput: "টেক্সট ইনপুট"
_textInput:
name: "চলকের নাম"
text: "শিরোনাম"
default: "ডিফল্ট মান"
textareaInput: "একাধিক লাইনের টেক্সট ইনপুট"
_textareaInput:
name: "চলকের নাম"
text: "শিরোনাম"
default: "ডিফল্ট মান"
numberInput: "সংখ্যা ইনপুট"
_numberInput:
name: "চলকের নাম"
text: "শিরোনাম"
default: "ডিফল্ট মান"
canvas: "ক্যানভাস"
_canvas:
id: "ক্যানভাস ID"
width: "প্রস্থ"
height: "উচ্চতা"
note: "এম্বেড নোট"
_note:
id: "নোট ID"
idDescription: "আপনি এর বদলে নোটের URL পেস্ট করতে পারেন."
detailed: "বিস্তারিত দেখুন"
switch: "সুইচ"
_switch:
name: "চলকের নাম"
text: "শিরোনাম"
default: "ডিফল্ট মান"
counter: "কাউন্টার"
_counter:
name: "চলকের নাম"
text: "শিরোনাম"
inc: "এভাবে মান বাড়ান"
_button:
text: "শিরোনাম"
colored: "রঙ্গিন"
action: "বাটনে ক্লিক করলে যা হবে"
_action:
dialog: "ডায়ালগ দেখান "
_dialog:
content: "বিষয়বস্তু"
resetRandom: "র‍্যানডম সিড রিসেট করুন"
pushEvent: "ইভেন্ট পাঠান"
_pushEvent:
event: "ইভেন্টের নাম"
message: "চালু হলে প্রদর্শনের জন্য বার্তা"
variable: "পাঠানো চলক"
no-variable: "কিছুই না"
callAiScript: "AiScript চালান"
_callAiScript:
functionName: "ফাংশনের নাম"
radioButton: "বহুনির্বাচনী"
_radioButton:
name: "চলকের নাম"
title: "শিরোনাম"
values: "বিকল্পগুলিকে আলাদা লাইনে লিখুন"
default: "ডিফল্ট মান"
script:
categories:
flow: "নিয়ন্ত্রণ"
logical: "লজিক্যাল অপারেশন"
operation: "হিসাব-নিকাশ"
comparison: "তুলনা"
random: "র‍্যান্ডম"
value: "মান"
fn: "ফাংশন"
text: "টেক্সট ম্যানিপুলেশন"
convert: "রুপান্তর"
list: "লিস্ট"
blocks:
text: "লেখা"
multiLineText: "লেখা (একাধিক লাইন)"
textList: "লেখার লিস্ট"
_textList:
info: "প্রতিটি এন্ট্রিকে আলাদা লাইনে লিখুন"
strLen: "লেখার দৈর্ঘ্য"
_strLen:
arg1: "লেখা"
strPick: "অক্ষর বের করে আনুন"
_strPick:
arg1: "লেখা"
arg2: "অক্ষরের অবস্থান"
strReplace: "লেখা প্রতিস্থাপন"
_strReplace:
arg1: "লেখা"
arg2: "যে লেখা প্রতিস্থাপন করা হবে"
arg3: "যা দ্বারা প্রতিস্থাপন করা হবে"
strReverse: "লেখা উল্টান"
_strReverse:
arg1: "লেখা"
join: "লেখা যুক্ত করুন"
_join:
arg1: "লিস্ট"
arg2: "বিভাজক"
add: "যোগ"
_add:
arg1: "A"
arg2: "B"
subtract: "বিয়োগ"
_subtract:
arg1: "A"
arg2: "B"
multiply: "গুন"
_multiply:
arg1: "A"
arg2: "B"
divide: "ভাগ"
_divide:
arg1: "A"
arg2: "B"
mod: "ভাগশেষ"
_mod:
arg1: "A"
arg2: "B"
round: "দশমিক রাউন্ড করুন"
_round:
arg1: "সংখ্যা"
eq: "A ও B সমান"
_eq:
arg1: "A"
arg2: "B"
notEq: "A ও B সমান না"
_notEq:
arg1: "A"
arg2: "B"
and: "A এবং B"
_and:
arg1: "A"
arg2: "B"
or: "A অথবা B"
_or:
arg1: "A"
arg2: "B"
lt: "< A , B হতে কম"
_lt:
arg1: "A"
arg2: "B"
gt: "> A , B হতে বেশী"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A , B হতে কম বা সমান"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A , B হতে বেশী বা সমান"
_gtEq:
arg1: "A"
arg2: "B"
if: "যদি"
_if:
arg1: "যদি"
arg2: "তাহলে"
arg3: "তাছাড়া"
not: "না"
_not:
arg1: "না"
random: "র‍্যান্ডম"
_random:
arg1: "সম্ভাব্যতা"
rannum: "র‍্যানডম সংখ্যা"
_rannum:
arg1: "ন্যূনতম মান"
arg2: "সর্বোচ্চ মান"
randomPick: "তালিকা থেকে দৈবচয়ন করুন"
_randomPick:
arg1: "লিস্ট"
dailyRandom: "র‍্যান্ডম সংখ্যা (প্রতিটি ব্যবহারকারীর জন্য প্রতিদিন পরিবর্তীত হয়)"
_dailyRandom:
arg1: "সম্ভাব্যতা"
dailyRannum: "র‍্যান্ডম সংখ্যা (প্রতিটি ব্যবহারকারীর জন্য প্রতিদিন পরিবর্তীত হয়)"
_dailyRannum:
arg1: "ন্যূনতম মান"
arg2: "সর্বোচ্চ মান"
dailyRandomPick: "তালিকা থেকে এলোমেলোভাবে নির্বাচন করুন (প্রতিটি ব্যবহারকারীর জন্য প্রতিদিন পরিবর্তীত হয়)"
_dailyRandomPick:
arg1: "লিস্ট"
seedRandom: "র‍্যানডম (সীড দ্বারা)"
_seedRandom:
arg1: "সীড"
arg2: "সম্ভাব্যতা"
seedRannum: "র‍্যানডম সংখ্যা (সীড দ্বারা)"
_seedRannum:
arg1: "সীড"
arg2: "ন্যূনতম মান"
arg3: "সর্বোচ্চ মান"
seedRandomPick: "তালিকা থেকে দৈবচয়ন করুন (সীড দ্বারা)"
_seedRandomPick:
arg1: "সীড"
arg2: "লিস্ট"
DRPWPM: "সম্ভাব্যতা সহ একটি তালিকা থেকে এলোমেলোভাবে নির্বাচন করুন (প্রতিটি ব্যবহারকারীর জন্য প্রতিদিন)"
_DRPWPM:
arg1: "লেখার লিস্ট"
pick: "তালিকা থেকে নির্বাচন করুন"
_pick:
arg1: "লিস্ট"
arg2: "অবস্থান"
listLen: "লিস্টের দৈর্ঘ্য পান"
_listLen:
arg1: "লিস্ট"
number: "সংখ্যা"
stringToNumber: "পাঠ্য থেকে সংখ্যা"
_stringToNumber:
arg1: "লেখা"
numberToString: "সংখ্যা থেকে পাঠ্য"
_numberToString:
arg1: "সংখ্যা"
splitStrByLine: "পাঠ্যকে লাইনে বিভক্ত করুন"
_splitStrByLine:
arg1: "লেখা"
ref: "চলক"
aiScriptVar: "AiScript চলক"
fn: "ফাংশন"
_fn:
slots: "স্লটগুলি"
slots-info: "প্রতিটি স্লটকে আলাদা লাইনে লিখুন"
arg1: "আউটপুট"
for: "for-লুপ"
_for:
arg1: "কতবার চলবে"
arg2: "অ্যাকশন"
typeError: "স্লট {slot}, {expect} ধরনের মান গ্রহণ করে, কিন্তু {actual} ধরনের মান দেওয়া হয়েছে!"
thereIsEmptySlot: "স্লট {slot} খালি!"
types:
string: "লেখা"
number: "সংখ্যা"
boolean: "ফ্ল্যাগ"
array: "লিস্ট"
stringArray: "লেখার লিস্ট"
emptySlot: "খালি স্লট"
enviromentVariables: "এনভাইরনমেন্ট ভ্যারিয়েবল"
pageVariables: "পেজের চলক"
argVariables: "ইনপুটের জায়গা"
_relayStatus:
requesting: "অপেক্ষমান"
accepted: "অনুমোদিত"
@@ -1410,7 +1645,6 @@ _deck:
alwaysShowMainColumn: "সর্বদা মেইন কলাম দেখান"
columnAlign: "কলাম সাজান"
addColumn: "কলাম যুক্ত করুন"
configureColumn: "কলাম সেটিংস"
swapLeft: "বামে সরান"
swapRight: "ডানে সরান"
swapUp: "উপরে উঠান"

View File

@@ -2,7 +2,6 @@
_lang_: "Català"
headlineMisskey: "Una xarxa connectada per notes"
introMisskey: "Benvingut! Misskey és un servei de microblogging descentralitzat de codi obert.\nCrea \"notes\" per compartir els teus pensaments amb tots els que t'envolten. 📡\nAmb \"reaccions\", també pots expressar ràpidament els teus sentiments sobre les notes de tothom. 👍\nExplorem un món nou! 🚀"
poweredByMisskeyDescription: "{name} És un del serveis (anomenats instàncies de Misskey) que utilitzen la plataforma de codi obert <b>Misskey</b>."
monthAndDay: "{day}/{month}"
search: "Cercar"
notifications: "Notificacions"
@@ -14,10 +13,10 @@ ok: "OK"
gotIt: "Ho he entès!"
cancel: "Cancel·lar"
enterUsername: "Introdueix el teu nom d'usuari"
renotedBy: "Impulsat per {usuari}"
renotedBy: "Resignat per {usuari}"
noNotes: "Cap nota"
noNotifications: "Cap notificació"
instance: "Servidor"
instance: "Instàncies"
settings: "Preferències"
basicSettings: "Configuració bàsica"
otherSettings: "Configuració avançada"
@@ -30,7 +29,7 @@ loggingIn: "Identificant-se"
logout: "Tancar la sessió"
signup: "Registrar-se"
uploading: "Pujant..."
save: "Desa"
save: "Desar"
users: "Usuaris"
addUser: "Afegir un usuari"
favorite: "Afegir a preferits"
@@ -43,17 +42,16 @@ pin: "Fixar al perfil"
unpin: "Para de fixar del perfil"
copyContent: "Copiar el contingut"
copyLink: "Copiar l'enllaç"
delete: "Elimina"
deleteAndEdit: "Elimina i edita"
deleteAndEditConfirm: "Segur que vols eliminar aquesta publicació i editar-la? Perdràs totes les reaccions, impulsos i respostes."
delete: "Eliminar"
deleteAndEdit: "Esborrar i editar"
deleteAndEditConfirm: "Estàs segur que vols suprimir aquesta nota i editar-la? Perdràs totes les reaccions, notes i respostes."
addToList: "Afegir a una llista"
sendMessage: "Enviar un missatge"
copyUsername: "Copiar nom d'usuari"
searchUser: "Cercar un usuari"
searchUser: "Cercar usuaris"
reply: "Respondre"
loadMore: "Carregar més"
showMore: "Veure més"
showLess: "Mostra menys"
youGotNewFollower: "t'ha seguit"
receiveFollowRequest: "Sol·licitud de seguiment rebuda"
followRequestAccepted: "Sol·licitud de seguiment acceptada"
@@ -62,7 +60,7 @@ mentions: "Mencions"
directNotes: "Notes directes"
importAndExport: "Importar / Exportar"
import: "Importar"
export: "Exporta"
export: "Exportar"
files: "Fitxers"
download: "Baixar"
driveFileDeleteConfirm: "Estàs segur que vols suprimir el fitxer \"{name}\"? Les notes associades a aquest fitxer adjunt també se suprimiran."
@@ -95,12 +93,12 @@ followRequests: "Sol·licituds de seguiment"
unfollow: "Deixar de seguir"
followRequestPending: "Sol·licituds de seguiment pendents"
enterEmoji: "Introduir un emoji"
renote: "Impulsa"
unrenote: "Anul·la l'impuls"
renoted: "S'ha impulsat"
cantRenote: "No es pot impulsar aquesta publicació"
cantReRenote: "No es pot impulsar l'impuls."
quote: "Cita"
renote: "Renotar"
unrenote: "Anul·lar renota"
renoted: "Renotat."
cantRenote: "Aquesta publicació no pot ser renotada."
cantReRenote: "Impossible renotar una renota."
quote: "Citar"
pinnedNote: "Nota fixada"
pinned: "Fixar al perfil"
you: "Tu"
@@ -121,255 +119,14 @@ block: "Bloqueja"
unblock: "Desbloqueja"
suspend: "Suspèn"
unsuspend: "Deixa de suspendre"
blockConfirm: "Vols bloquejar?"
unblockConfirm: "Vols desbloquejar-lo?"
suspendConfirm: "Estàs segur que vols suspendre aquest compte?"
unsuspendConfirm: "Estàs segur que vols treure la suspensió d'aquest compte?"
selectList: "Tria una llista"
selectAntenna: "Tria una antena"
selectWidget: "Triar un giny"
editWidgets: "Editar ginys"
editWidgetsExit: "Fet"
customEmojis: "Emojis personalitzats"
emoji: "Emoji"
emojis: "Emoji"
emojiName: "Nom del emoji"
emojiUrl: "URL del emoji"
addEmoji: "Afegeix un emoji"
settingGuide: "Configuració recomanada"
cacheRemoteFiles: "Emmagatzemar fitxers remots"
cacheRemoteFilesDescription: "Quan aquesta opció està desactivada, els fitxers remots es carreguen directament des del servidor remot. Si desactiveu això, es reduirà l'ús d'emmagatzematge, però augmentarà el trànsit, ja que no es generaran miniatures."
flagAsBot: "Marca aquest compte com a bot"
flagAsBotDescription: "Marca aquest compte com a bot"
flagAsCat: "Marca aquest compte com a gat"
flagAsCatDescription: "Activeu aquesta opció per marcar aquest compte com a gat."
flagShowTimelineReplies: "Mostra les respostes a la línia de temps"
flagShowTimelineRepliesDescription: "Mostra les respostes a la línia de temps"
autoAcceptFollowed: "Aprova automàticament les sol·licituds de seguiment dels usuaris que segueixes"
addAccount: "Afegeix un compte"
loginFailed: "S'ha produït un error al accedir."
showOnRemote: "Navega més en el perfil original"
general: "General"
wallpaper: "Fons de Pantalla"
setWallpaper: "Defineix el fons de pantalla"
removeWallpaper: "Elimina el fons de pantalla"
searchWith: "Cerca: {q}"
youHaveNoLists: "No tens cap llista"
followConfirm: "Estàs segur que vols deixar de seguir {name}?"
proxyAccount: "Compte de proxy"
proxyAccountDescription: "Un compte proxy és un compte que actua com a seguidor remot per als usuaris en determinades condicions. Per exemple, quan un usuari afegeix un usuari remot a la llista, l'activitat de l'usuari remot no es lliurarà al servidor si cap usuari local segueix aquest usuari, de manera que el compte proxy el seguirà."
host: "Amfitrió"
selectUser: "Selecciona usuari/a"
recipient: "Destinatari"
annotation: "Comentaris"
federation: "Federació"
instances: "Servidors"
registeredAt: "Registrat a"
latestRequestSentAt: "Darrera petició enviada"
latestRequestReceivedAt: "Última petició rebuda"
latestStatus: "Últim estat"
storageUsage: "Emmagatzematge utilitzat"
charts: "Gràfics"
perHour: "Per hora"
perDay: "Per dia"
stopActivityDelivery: "Deixa d'enviar activitats"
blockThisInstance: "Deixa d'enviar activitats"
operations: "Accions"
software: "Programari"
version: "Versió"
metadata: "Metadades"
withNFiles: "{n} fitxer(s)"
monitor: "Monitor"
jobQueue: "Cua de tasques"
cpuAndMemory: "CPU i memòria"
network: "Xarxa"
disk: "Disc"
instanceInfo: "Informació del fitxer d'instal·lació"
statistics: "Estadístiques"
clearQueue: "Esborrar la cua"
clearQueueConfirmTitle: "Esteu segur que voleu esborrar la cua?"
clearQueueConfirmText: "Les notes no lliurades que quedin a la cua no es federaran. Normalment aquesta operació no és necessària."
clearCachedFiles: "Esborra la memòria cau"
clearCachedFilesConfirm: "Segur que voleu eliminar tots els fitxers de la memòria cau?"
blockedInstances: "Instàncies bloquejades"
muteAndBlock: "Silencia i bloca"
mutedUsers: "Usuaris silenciats"
blockedUsers: "Usuaris bloquejats"
noUsers: "No hi ha usuaris"
editProfile: "Edita el perfil"
noteDeleteConfirm: "Segur que voleu eliminar aquesta publicació?"
pinLimitExceeded: "No podeu fixar més publicacions"
intro: "La instal·lació de Misskey ha acabat! Crea un usuari d'administrador."
done: "Fet"
processing: "S'està processant..."
preview: "Vista prèvia"
default: "Per defecte"
defaultValueIs: "Per defecte: {value}"
noCustomEmojis: "Cap emoji personalitzat"
federating: "Federant"
blocked: "Bloquejat"
suspended: "Suspés"
publishing: "S'està publicant"
notResponding: "Sense resposta"
instanceFollowing: "Seguits del servidor"
instanceFollowers: "Seguidors del servidor"
instanceUsers: "Usuaris del servidor"
changePassword: "Canvia la contrasenya"
security: "Seguretat"
currentPassword: "Contrasenya actual"
newPassword: "Contrasenya nova"
newPasswordRetype: "Contrasenya nou (repeteix-la)"
attachFile: "Adjunta fitxers"
more: "Més"
featured: "Destacat"
usernameOrUserId: "Nom o ID d'usuari"
noSuchUser: "No s'ha trobat l'usuari"
lookup: "Cerca"
announcements: "Anuncis"
imageUrl: "URL de la imatge"
instances: "Instàncies"
remove: "Eliminar"
removed: "Eliminat"
removeAreYouSure: "Segur que voleu retirar «{x}»?"
deleteAreYouSure: "Segur que voleu retirar «{x}»?"
resetAreYouSure: "Segur que voleu restablir-ho?"
saved: "S'ha desat"
messaging: "Xat"
upload: "Puja"
start: "Comença"
home: "Inici"
activity: "Activitat"
images: "Imatges"
birthday: "Aniversari"
yearsOld: "{age} anys"
registeredDate: "Data de registre"
location: "Ubicació"
theme: "Tema"
themeForLightMode: "Tema del mode clar"
themeForDarkMode: "Tema del mode fosc"
light: "Clar"
dark: "Fosc"
lightThemes: "Temes clars"
darkThemes: "Temes foscos"
syncDeviceDarkMode: "Sincronitza el mode fosc amb la configuració del dispositiu"
renameFile: "Canvia el nom del fitxer"
folderName: "Nom de la carpeta"
createFolder: "Crea una carpeta"
renameFolder: "Canvia el nom de la carpeta"
deleteFolder: "Elimina la carpeta"
addFile: "Afegeix un fitxer"
emptyFolder: "La carpeta està buida"
unableToDelete: "No es pot eliminar"
copyUrl: "Copia l'URL"
rename: "Canvia el nom"
nsfw: "NSFW"
reload: "Actualitza"
doNothing: "Ignora"
accept: "Accepta"
normal: "Nomal"
instanceName: "Nom del servidor"
instanceDescription: "Descripció del servidor"
maintainerName: "Nom de l'administrador"
maintainerEmail: "Correu electrònic de l'administrador"
tosUrl: "URL de les Condicions d'ús"
thisYear: "Enguany"
thisMonth: "Aquest mes"
today: "Avui"
dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Pàgines"
integration: "Integració"
connectService: "Connecta"
disconnectService: "Desconnecta"
enableLocalTimeline: "Activa la línia de temps local"
enableGlobalTimeline: "Activa la línia de temps global"
registration: "Registre"
invite: "Convida"
basicInfo: "Informació bàsica"
pinnedUsers: "Usuaris fixats"
pinnedNotes: "Nota fixada"
turnstile: "Turnstile"
enableTurnstile: "Activar Turnstile"
turnstileSiteKey: "Clau del lloc"
turnstileSecretKey: "Clau secreta"
antennas: "Antena"
manageAntennas: "Gestiona les antenes"
antennaSource: "Font de l'antena"
antennaKeywords: "Paraules clau a seguir"
antennaExcludeKeywords: "Paraules clau a excloure"
notifyAntenna: "Notifica'm les publicacions noves"
withFileAntenna: "Només les publicacions amb fitxers"
notesAndReplies: "Amb respostes"
silence: "Silencia"
silenceConfirm: "Segur que vols silenciar aquest usuari?"
unsilence: "Deixa de silenciar"
unsilenceConfirm: "Segur que vols deixar de silenciar aquest usuari?"
popularUsers: "Usuaris populars"
recentlyUpdatedUsers: "Usuaris actius fa poc"
recentlyRegisteredUsers: "Usuaris nous"
recentlyDiscoveredUsers: "Usuaris descoberts fa poc"
exploreUsersCount: "Hi ha {count} usuaris"
exploreFediverse: "Explora el fedivers"
popularTags: "Etiquetes populars"
userList: "Llistes"
about: "Informació"
aboutMisskey: "Quant a Misskey"
administrator: "Administrador/a"
twoStepAuthentication: "Verificació en dos passos"
moderator: "Moderador/a"
moderation: "Moderació"
nUsersMentioned: "{n} usuaris mencionats"
securityKey: "Clau de seguretat"
securityKeyName: "Nom de la clau"
registerSecurityKey: "Registra la clau de seguretat"
unregister: "Cancel·la el registre"
passwordLessLogin: "Inici de sessió sense contrasenya"
resetPassword: "Restableix la contrasenya"
newPasswordIs: "La contrasenya nova és «{password}»"
reduceUiAnimation: "Redueix les animacions de la interfície"
share: "Comparteix"
notFound: "No s'ha trobat"
markAsReadAllUnreadNotes: "Marca-ho tot com a llegit"
help: "Ajuda"
invites: "Convida"
next: "Següent"
noteOf: "Publicació de: {user}"
inviteToGroup: "Convida'l al grup"
invitations: "Convida"
tags: "Etiquetes"
docSource: "Font del document"
createAccount: "Crea un compte"
existingAccount: "Compte existent"
regenerate: "Regenera"
fontSize: "Mida del text"
noFollowRequests: "No tens sol·licituds de seguiment"
dashboard: "Panell de control"
local: "Local"
remote: "Remot"
total: "Total"
appearance: "Aparença"
clientSettings: "Configuració del client"
accountSettings: "Configuració del compte"
hideThisNote: "Amaga la publicació"
showFeaturedNotesInTimeline: "Mostra publicacions destacades en la línia de temps"
newNoteRecived: "Hi ha publicacions noves"
installedDate: "Data d'instal·lació"
state: "Estat"
sort: "Ordena"
ascendingOrder: "Ascendent"
descendingOrder: "Descendent"
deletedNote: "Publicacions eliminades"
invisibleNote: "Publicacions amagades"
smtpHost: "Amfitrió"
smtpUser: "Nom d'usuari"
smtpPass: "Contrasenya"
renotesCount: "Impulsos fets"
renotedCount: "Impulsos rebuts"
clearCache: "Esborra la memòria cau"
showingPastTimeline: "Estàs veient una línia de temps antiga"
info: "Informació"
user: "Usuaris"
global: "Global"
searchByGoogle: "Cercar"
file: "Fitxers"
_email:
@@ -378,10 +135,7 @@ _email:
_mfm:
mention: "Menció"
quote: "Citar"
emoji: "Emojis personalitzats"
search: "Cercar"
_instanceMute:
instanceMuteDescription: "Silencia tots els impulsos dels servidors seleccionats, també els usuaris que responen a altres d'un servidor silenciat."
_theme:
keys:
mention: "Menció"
@@ -389,82 +143,55 @@ _theme:
_sfx:
note: "Notes"
notification: "Notificacions"
chat: "Xat"
antenna: "Antenes"
_2fa:
step2Url: "També pots inserir aquest enllaç i utilitzes una aplicació d'escriptori:"
_antennaSources:
all: "Totes les publicacions"
homeTimeline: "Publicacions dels usuaris seguits"
users: "Publicacions d'usuaris específics"
userList: "Publicacions d'una llista d'usuaris"
userGroup: "Publicacions d'usuaris d'un grup"
_widgets:
notifications: "Notificacions"
timeline: "Línia de temps"
activity: "Activitat"
federation: "Federació"
jobQueue: "Cua de tasques"
_userList:
chooseList: "Tria una llista"
_cw:
show: "Carregar més"
_visibility:
home: "Inici"
followers: "Seguidors"
_profile:
username: "Nom d'usuari"
_exportOrImport:
allNotes: "Totes les publicacions"
followingList: "Seguint"
muteList: "Silencia"
blockingList: "Bloqueja"
userLists: "Llistes"
_charts:
federation: "Federació"
_timelines:
home: "Inici"
local: "Local"
social: "Social"
global: "Global"
_pages:
contents: "Contingut"
blocks:
image: "Imatges"
_note:
id: "ID de la publicació"
detailed: "Mostra els detalls"
script:
categories:
list: "Llistes"
blocks:
_join:
arg1: "Llistes"
_randomPick:
arg1: "Llistes"
_dailyRandomPick:
arg1: "Llistes"
_seedRandomPick:
arg2: "Llistes"
_pick:
arg1: "Llistes"
_listLen:
arg1: "Llistes"
types:
array: "Llistes"
_notification:
youRenoted: "Impulsat per {name}"
youWereFollowed: "t'ha seguit"
_types:
all: "Tots"
follow: "Seguint"
mention: "Menció"
renote: "Renotar"
quote: "Citar"
reaction: "Reaccions"
_actions:
followBack: "t'ha seguit també"
reply: "Respondre"
renote: "Renotar"
_deck:
columnAlign: "Alinea les columnes"
addColumn: "Afig una columna"
swapLeft: "Mou a lesquerra"
swapRight: "Mou a la dreta"
swapUp: "Mou cap amunt"
swapDown: "Mou cap avall"
popRight: "Col·loca a la dreta"
profile: "Perfil"
newProfile: "Perfil nou"
deleteProfile: "Elimina el perfil"
_columns:
main: "Principal"
widgets: "Ginys"
notifications: "Notificacions"
tl: "Línia de temps"
antenna: "Antena"
list: "Llistes"
mentions: "Mencions"
direct: "Publicacions directes"

View File

@@ -52,7 +52,6 @@ searchUser: "Vyhledat uživatele"
reply: "Odpovědět"
loadMore: "Zobrazit více"
showMore: "Zobrazit více"
showLess: "Zavřít"
youGotNewFollower: "Máte nového následovníka"
receiveFollowRequest: "Žádost o sledování přijata"
followRequestAccepted: "Žádost o sledování přijata"
@@ -206,7 +205,6 @@ instanceFollowers: "Následovníci na instanci"
instanceUsers: "Uživatelé této instance"
changePassword: "Změnit heslo"
security: "Zabezpečení"
retypedNotMatch: "Zadané údaje se neshodují."
currentPassword: "Současné heslo"
newPassword: "Nové heslo"
newPasswordRetype: "Nové heslo (znovu)"
@@ -269,7 +267,6 @@ addFile: "Přidat soubor"
emptyFolder: "Tato složka je prázdná"
unableToDelete: "Nelze smazat"
inputNewFileName: "Zadejte nový název"
inputNewFolderName: "Zadejte název nové složky"
copyUrl: "Kopírovat URL"
rename: "Přejmenovat"
avatar: "Avatar"
@@ -312,14 +309,10 @@ pinnedUsers: "Připnutí uživatelé"
pinnedNotes: "Připnutá poznámka"
hcaptcha: "hCaptcha"
enableHcaptcha: "Aktivovat hCaptchu"
hcaptchaSiteKey: "Klíč stránky"
hcaptchaSecretKey: "Tajný Klíč (Secret Key)"
recaptcha: "reCAPTCHA"
enableRecaptcha: "Zapnout ReCAPTCHu"
recaptchaSiteKey: "Klíč stránky"
recaptchaSecretKey: "Tajný Klíč (Secret Key)"
turnstileSiteKey: "Klíč stránky"
turnstileSecretKey: "Tajný Klíč (Secret Key)"
antennas: "Antény"
manageAntennas: "Spravovat Antény"
name: "Jméno"
@@ -327,10 +320,6 @@ antennaSource: "Zdroj Antény"
enableServiceworker: "Povolit ServiceWorker"
caseSensitive: "Rozlišuje malá a velká písmena"
connectedTo: "Následující účty jsou připojeny"
notesAndReplies: "Poznámky a odpovědi"
withFiles: "Včetně souborů"
popularUsers: "Populární uživatelé"
recentlyUpdatedUsers: "Nedávno aktívni uživatelé"
popularTags: "Populární tagy"
userList: "Seznamy"
about: "Informace"
@@ -375,14 +364,10 @@ next: "Další"
retype: "Zadejte znovu"
noteOf: "{user} poznámky"
inviteToGroup: "Pozvat do skupiny"
quoteAttached: "Citace"
quoteQuestion: "Přiložit jako citaci?"
noMessagesYet: "Zatím tu nejsou žádné zprávy"
newMessageExists: "Máte novou zprávu"
onlyOneFileCanBeAttached: "Ke zprávě můžete přiložit jenom jeden soubor"
signinRequired: "Přihlašte se, prosím"
invitations: "Pozvat"
invitationCode: "Kód pozvánky"
checking: "Ověřuji"
available: "K dispozici"
unavailable: "Není k dispozici"
@@ -396,12 +381,12 @@ passwordMatched: "Hesla se schodují"
passwordNotMatched: "Hesla se neschodují"
signinWith: "Přihlásit se s {x}"
signinFailed: "Nelze se přihlásit. Zkontrolujte prosím své uživatelské jméno a heslo."
tapSecurityKey: "Ťukněte na bezpečnostní klíč"
or: "Nebo"
language: "Jazyk"
uiLanguage: "Jazyk uživatelského rozhraní"
groupInvited: "Pozvat do skupiny"
aboutX: "O {x}"
useOsNativeEmojis: "Použití nativních emoji operačního systému"
youHaveNoGroups: "Nemáte žádné skupiny"
joinOrCreateGroup: "Můžete požádat o pozvání do stávající skupiny nebo vytvořit novou."
noHistory: "Žádná historie"
@@ -425,20 +410,9 @@ accountSettings: "Nastavení účtu"
promotion: "Propagace"
promote: "Propagovat"
numberOfDays: "Počet dní"
objectStorageBaseUrl: "Base URL"
objectStorageBucket: "Bucket"
objectStoragePrefix: "Předpona"
objectStorageEndpoint: "Endpoint"
objectStorageRegion: "Región"
objectStorageUseSSL: "Použít SSL"
deleteAll: "Smazat vše"
showFixedPostForm: "Zobrazit formulář pro nové příspěvky nad časovou osou"
listen: "Poslouchat"
showInPage: "Zobrazit na stránce"
popout: "Pop-out"
volume: "Hlasitost"
masterVolume: "Celková hlasitost"
details: "Detaily"
chooseEmoji: "Vybrat emotikon"
unableToProcess: "Operace nebyla dokončena."
recentUsed: "Naposledy použité"
@@ -459,20 +433,13 @@ deleteAllFiles: "Smazat všechny soubory"
deleteAllFilesConfirm: "Jste si jistí že chcete smazat všechny soubory?"
userSuspended: "Tomuto uživateli byl pozastaven účet."
menu: "Menu"
divider: "Dělící čára"
addItem: "Přidat položku"
relays: "Relay"
addRelay: "Přidat Relay"
inboxUrl: "Inbox URL"
deletedNote: "Odstraněné příspěvky"
invisibleNote: "Skryté příspěvky"
description: "Popis"
author: "Autor"
manage: "Administrace"
width: "Šířka"
height: "Výška"
large: "Velké"
medium: "Střední"
small: "Malé"
generateAccessToken: "Vygenerovat přístupový token"
permission: "Oprávnění"
@@ -490,16 +457,11 @@ smtpPort: "Port"
smtpUser: "Uživatelské jméno"
smtpPass: "Heslo"
smtpSecureInfo: "Toto vypněte pokud používáte STARTTLS"
testEmail: "Otestovat doručení emailů"
makeActive: "Aktivovat"
display: "Zobrazit"
copy: "Kopírovat"
metrics: "Metriky"
overview: "Shrnutí"
logs: "Logy"
delayed: "Prodleva"
database: "Databáze"
channel: "Kanály"
create: "Vytvořit"
notificationSetting: "Nastavení oznámení"
useGlobalSetting: "Použít globální nastavení"
@@ -507,278 +469,79 @@ other: "Ostatní"
fileIdOrUrl: "ID nebo URL souboru"
behavior: "Chování"
sample: "Ukázka"
send: "Odeslat"
openInNewTab: "Otevřít v nové kartě"
random: "Náhodně"
system: "Systém"
desktop: "Plocha"
clip: "Oříznout"
createNew: "Vytvořit nový"
optional: "Volitelné"
yes: "Ano"
no: "Ne"
notSet: "Není nastaveno"
emailVerified: "Váš e-mail byl ověřen"
contact: "Kontakt"
useSystemFont: "Použít výchozí font systému"
clips: "Oříznout"
experimentalFeatures: "Experimentální funkce"
developer: "Vývojář"
duplicate: "Duplikovat"
left: "Vlevo"
center: "Uprostřed"
wide: "Široké"
narrow: "Úzké"
clearCache: "Vyprázdnit mezipaměť"
nUsers: "{n} užívatelů"
nNotes: "{n} poznámek"
myTheme: "Moje vzhledy"
backgroundColor: "Pozadí"
accentColor: "Akcent"
textColor: "Barva textu"
saveAs: "Uložit jako…"
advanced: "Pokročilé"
value: "Hodnota"
createdAt: "Vytvořeno"
updatedAt: "Upraveno"
saveConfirm: "Uložit změny?"
deleteConfirm: "Opravdu smazat?"
invalidValue: "Neplatná hodnota."
registry: "Registr"
info: "Informace"
unknown: "Neznámý"
onlineStatus: "Online status"
hideOnlineStatus: "Skrýt Váš online status"
hideOnlineStatusDescription: "Skrytí vašeho online stavu může snížit funkcionalitu některých funkcí, například vyhledávání."
online: "Online"
active: "Aktivní"
offline: "Offline"
notRecommended: "Nedoporučuje se"
botProtection: "Bot ochrana"
instanceBlocking: "Blokované instance"
selectAccount: "Vybrat účet"
switchAccount: "Přepnout účet"
enabled: "Zapnuto"
disabled: "Vypnuto"
quickAction: "Rychlé akce"
user: "Uživatelé"
administration: "Administrace"
accounts: "Účty"
switch: "Přepnout"
configure: "Nastavit"
gallery: "Galerie"
recentPosts: "Poslední příspěvky"
ads: "Reklamy"
memo: "Memo"
priority: "Priorita"
high: "Vysoká"
middle: "Střední"
low: "Nízká"
emailNotConfiguredWarning: "E-mailová adresa není nastavena."
ratio: "Poměr"
global: "Globální"
sent: "Odeslat"
hashtags: "Hashtagy"
troubleshooting: "Poradce při potížích"
whatIsNew: "Zobrazit změny"
translate: "Přeložit"
hide: "Skrýt"
smartphone: "Telefon"
tablet: "Tablet"
auto: "Auto"
size: "Velikost"
numberOfColumn: "Počet sloupců"
searchByGoogle: "Vyhledávání"
indefinitely: "Navždy"
tenMinutes: "10 minut"
oneHour: "1 hodina"
oneDay: "1 den"
oneWeek: "1 týden"
reflectMayTakeTime: "Může trvat nějakou dobu, než se projeví změny."
cropImage: "Oříznout obrázek"
file: "Soubor(ů)"
recentNHours: "Posledních {n} hodin"
recentNDays: "Posledních {n} dnů"
recommended: "Doporučeno"
deleteAccount: "Odstranit účet"
document: "Dokumentace"
logoutConfirm: "Opravdu se chcete odhlásit?"
pleaseSelect: "Vybrat možnost"
reverse: "Otočit"
colored: "Barevné"
type: "Typ"
speed: "Rychlost"
slow: "Pomalá"
fast: "Rychlá"
account: "Účty"
_ad:
back: "Zpět"
_gallery:
my: "Moje galerie"
_email:
_follow:
title: "Máte nového následovníka"
_plugin:
install: "Instalovat plugin"
manage: "Správce pluginů"
_preferencesBackups:
list: "Vytvořit backup"
loadFile: "Načíst ze souboru"
save: "Uložit změny"
_registry:
scope: "Rozsah"
key: "Klíč"
keys: "Klíče"
domain: "Doména"
createKey: "Vytvořit klíč"
_aboutMisskey:
allContributors: "Všichni přispěvatelé"
source: "Zdrojový kód"
_mfm:
mention: "Zmínění"
hashtag: "Hashtag"
link: "Odkaz"
bold: "Tučně"
quote: "Citovat"
emoji: "Vlastní emoji"
search: "Vyhledávání"
flip: "Otočit"
tada: "Animace (tadá)"
blur: "Rozmazání"
font: "Font"
rainbow: "Duha"
_channel:
featured: "Trendy"
_menuDisplay:
top: "Nahoru"
hide: "Skrýt"
_theme:
install: "Nainstalovat vzhled"
manage: "Správa vzhledů"
code: "Kód vzhledu"
description: "Popis"
installedThemes: "Nainstalované vzhledy"
constant: "Konstanta"
defaultValue: "Výchozí hodnota"
color: "Barva"
key: "Klíč"
func: "Funkce "
keys:
shadow: "Stín"
header: "Nadpis"
link: "Odkaz"
hashtag: "Hashtag"
mention: "Zmínění"
renote: "Přeposlat"
divider: "Dělící čára"
_sfx:
note: "Poznámky"
notification: "Oznámení"
chat: "Zprávy"
_ago:
future: "Budoucí"
justNow: "Teď"
_time:
second: "Sekund"
minute: "Minut"
hour: "Hodin"
_2fa:
registerDevice: "Přidat zařízení"
registerKey: "Přidat bezpečnostní klíč"
_weekday:
sunday: "Neděle"
monday: "Pondělí"
tuesday: "Úterý"
wednesday: "Středa"
thursday: "Čtvrtek"
friday: "Pátek"
saturday: "Sobota"
_widgets:
notifications: "Oznámení"
timeline: "Časová osa"
calendar: "Kalendář"
trends: "Trendy"
clock: "Hodiny"
rss: "RSS čtečka"
activity: "Aktivita"
photos: "Fotky"
digitalClock: "Digitální hodiny"
federation: "Federace"
slideshow: "Prezentace"
button: "Tlačítko"
onlineUsers: "Online uživatelé"
jobQueue: "Fronta úloh"
aiscript: "AiScript conzole"
aichan: "Ai"
_userList:
chooseList: "Vybrat seznam"
_cw:
hide: "Skrýt"
show: "Zobrazit více"
_poll:
noMore: "Více už přidat nemůžete"
infinite: "Nikdy"
deadlineDate: "Datum ukončení"
deadlineTime: "Hodin"
duration: "Trvání"
_visibility:
home: "Domů"
followers: "Sledující"
_postForm:
_placeholders:
f: "Čekám, až něco napíšete..."
_profile:
name: "Jméno"
username: "Uživatelské jméno"
description: "O mně"
youCanIncludeHashtags: "V popisku o Vás můžete použít i hastagy."
metadata: "Doplňující informace"
metadataContent: "Obsah"
_exportOrImport:
allNotes: "Všechny poznámky"
followingList: "Sledovaní"
muteList: "Ztlumit"
blockingList: "Zablokovat"
userLists: "Seznamy"
_charts:
federation: "Federace"
apRequest: "Požadavek"
usersTotal: "Celkem uživatelů"
activeUsers: "Aktivní uživatelé"
notesTotal: "Celkový počet poznámek"
_timelines:
home: "Domů"
global: "Globální"
_pages:
newPage: "Vytvořit novou stránku"
editPage: "Upravit stránku"
created: "Stránka byla úspěšně vytvořena"
updated: "Stránka byla úspěšně aktualizována"
deleted: "Stránka byla úspěšně smazána"
pageSetting: "Nastavení stránky"
invalidNameText: "Ujistěte se že jméno stránky je vyplněno"
contents: "Obsah"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
chooseBlock: "Přidat blok"
selectType: "Vyberte typ"
contentBlocks: "Obsah"
inputBlocks: "Vstup"
specialBlocks: "Speciální"
blocks:
text: "Text"
textarea: "Textové pole"
section: "Sekce"
image: "Obrázky"
button: "Tlačítko"
script:
categories:
list: "Seznamy"
blocks:
_join:
arg1: "Seznamy"
_randomPick:
arg1: "Seznamy"
_dailyRandomPick:
arg1: "Seznamy"
_seedRandomPick:
arg2: "Seznamy"
_pick:
arg1: "Seznamy"
_listLen:
arg1: "Seznamy"
types:
array: "Seznamy"
_notification:
youWereFollowed: "Máte nového následovníka"
youWereInvitedToGroup: "Pozvat do skupiny"
_types:
all: "Vše"
follow: "Sledovaní"
mention: "Zmínění"
reply: "Odpovědi"
renote: "Přeposlat"
quote: "Citovat"
reaction: "Reakce"

View File

@@ -2,7 +2,6 @@
_lang_: "Deutsch"
headlineMisskey: "Ein durch Notizen verbundenes Netzwerk"
introMisskey: "Willkommen! Misskey ist eine dezentralisierte Open-Source Microblogging-Platform.\nVerfasse „Notizen“ um mitzuteilen, was gerade passiert oder um Ereignisse mit anderen zu teilen. 📡\nMit „Reaktionen“ kannst du außerdem schnell deine Gefühle über Notizen anderer Benutzer zum Ausdruck bringen. 👍\nEine neue Welt wartet auf dich! 🚀"
poweredByMisskeyDescription: "{name} ist einer der durch die Open-Source-Plattform <b>Misskey</b> betriebenen Dienste (meist als \"Misskey-Instanz\" bezeichnet)."
monthAndDay: "{day}.{month}."
search: "Suchen"
notifications: "Benachrichtigungen"
@@ -13,7 +12,6 @@ fetchingAsApObject: "Wird aus dem Fediverse angefragt …"
ok: "OK"
gotIt: "Verstanden!"
cancel: "Abbrechen"
noThankYou: "Nein, danke"
enterUsername: "Benutzername eingeben"
renotedBy: "Renote von {user}"
noNotes: "Keine Notizen gefunden"
@@ -49,13 +47,11 @@ deleteAndEdit: "Löschen und Bearbeiten"
deleteAndEditConfirm: "Möchtest du diese Notiz wirklich löschen und bearbeiten? Alle Reaktionen, Renotes und Antworten dieser Notiz werden verloren gehen."
addToList: "Zu Liste hinzufügen"
sendMessage: "Nachricht senden"
copyRSS: "RSS kopieren"
copyUsername: "Benutzernamen kopieren"
searchUser: "Nach einem Benutzer suchen"
reply: "Antworten"
loadMore: "Mehr laden"
showMore: "Mehr anzeigen"
showLess: "Schließen"
youGotNewFollower: "ist dir gefolgt"
receiveFollowRequest: "Follow-Anfrage erhalten"
followRequestAccepted: "Follow-Anfrage akzeptiert"
@@ -351,10 +347,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHA aktivieren"
recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret key"
turnstile: "Turnstile"
enableTurnstile: "Turnstile aktivieren"
turnstileSiteKey: "Site key"
turnstileSecretKey: "Secret key"
avoidMultiCaptchaConfirm: "Das Verwenden von mehreren Captcha-Systemen kann zu Störungen führen. Sollen die anderen Systeme deaktiviert werden? Durch Abbrechen können mehrere Systeme aktiviert bleiben."
antennas: "Antennen"
manageAntennas: "Antennen verwalten"
@@ -457,8 +449,7 @@ language: "Sprache"
uiLanguage: "Sprache der Benutzeroberfläche"
groupInvited: "Du wurdest in eine Gruppe eingeladen"
aboutX: "Über {x}"
emojiStyle: "Emoji-Stil"
native: "Nativ"
useOsNativeEmojis: "Eingebaute Emojis des Betriebssystems benutzen"
disableDrawer: "Keine ausfahrbaren Menüs verwenden"
youHaveNoGroups: "Keine Gruppen vorhanden"
joinOrCreateGroup: "Lass dich zu einer Gruppe einladen oder erstelle deine eigene."
@@ -504,14 +495,13 @@ objectStorageRegionDesc: "Gib eine Region wie z.B. „xx-east-1“ an. Falls dei
objectStorageUseSSL: "SSL verwenden"
objectStorageUseSSLDesc: "Deaktiviere dies, falls du für API-Verbindungen kein HTTPS verwenden wirst"
objectStorageUseProxy: "Über Proxy verbinden"
objectStorageUseProxyDesc: "Deaktiviere dies, falls du für Verbindungen zur API keinen Proxy verwenden wirst"
objectStorageUseProxyDesc: "Deaktiviere dies, falls du keinen Proxy für den Objektspeicher verwenden wirst"
objectStorageSetPublicRead: "Bei Upload auf \"public-read\" stellen"
serverLogs: "Serverprotokolle"
deleteAll: "Alle löschen"
showFixedPostForm: "Bereich zum Schreiben neuer Notizen am Anfang der Chronik anzeigen"
newNoteRecived: "Es gibt neue Notizen"
sounds: "Töne"
sound: "Töne"
listen: "Anhören"
none: "Nichts"
showInPage: "In einer Seite anzeigen"
@@ -571,7 +561,6 @@ author: "Autor"
leaveConfirm: "Es gibt unspeicherte Änderungen. Möchtest du diese verwerfen?"
manage: "Verwaltung"
plugins: "Plugins"
preferencesBackups: "Einstellungsbackups"
deck: "Deck"
undeck: "Deck verlassen"
useBlurEffectForModal: "Weichzeichnungseffekt für Modals verwenden"
@@ -717,7 +706,6 @@ accentColor: "Akzentfarbe"
textColor: "Textfarbe"
saveAs: "Speichern als …"
advanced: "Fortgeschritten"
advancedSettings: "Erweiterte Einstellungen"
value: "Wert"
createdAt: "Erstellt am"
updatedAt: "Zuletzt geändert am"
@@ -874,7 +862,7 @@ requireAdminForView: "Melde dich mit einem Administratorkonto an, um dies einzus
isSystemAccount: "Ein Benutzerkonto, dass durch das System erstellt und automatisch kontrolliert wird."
typeToConfirm: "Bitte gib zur Bestätigung {x} ein"
deleteAccount: "Benutzerkonto löschen"
document: "Dokumentation"
document: "Dokument"
numberOfPageCache: "Seitencachegröße"
numberOfPageCacheDescription: "Das Erhöhen dieses Caches führt zu einer angenehmerern Benutzererfahrung, erhöht aber Serverlast und Arbeitsspeicherauslastung."
logoutConfirm: "Wirklich abmelden?"
@@ -902,20 +890,6 @@ activeEmailValidationDescription: "Aktivert strengere Überprüfung von E-Mail-A
navbar: "Navigationsleiste"
shuffle: "Mischen"
account: "Benutzerkonto"
move: "Verschieben"
pushNotification: "Push-Benachrichtigungen"
subscribePushNotification: "Push-Benachrichtigungen aktivieren"
unsubscribePushNotification: "Push-Benachrichtigungen deaktivieren"
pushNotificationAlreadySubscribed: "Push-Benachrichtigungen sind bereits aktiviert"
pushNotificationNotSupported: "Entweder dein Browser oder deine Instanz unterstützt Push-Benachrichtigungen nicht"
sendPushNotificationReadMessage: "Push-Benachrichtigungen löschen, sobald die relevanten Benachrichtigungen oder Nachrichten gelesen wurden"
sendPushNotificationReadMessageCaption: "Eine Push-Benachrichtigungen mit dem Inhalt \"{emptyPushNotificationMessage}\" wird kurz eingeblendet. Dies kann gegebenenfalls den Batterieverbrauch deines Gerätes erhöhen."
windowMaximize: "Maximieren"
windowRestore: "Wiederherstellen"
caption: "Beschreibung"
loggedInAsBot: "Momentan als Bot angemeldet"
tools: "Werkzeuge"
cannotLoad: "Kann nicht geladen werden"
_sensitiveMediaDetection:
description: "Ermöglicht eine Erleichterung der Servermoderation durch die automatische Erkennungen von NSFW-Medien unter Verwendung von Machine Learning. Hierdurch wird die Serverlast etwas erhöht."
sensitivity: "Erkennungssensitivität"
@@ -966,24 +940,6 @@ _plugin:
install: "Plugins installieren"
installWarn: "Installiere bitte nur vertrauenswürdige Plugins."
manage: "Plugins verwalten"
_preferencesBackups:
list: "Erstellte Backups"
saveNew: "Neu erstellen"
loadFile: "Von Datei laden"
apply: "Auf dieses Gerät anwenden"
save: "Speichern"
inputName: "Gib einen Namen für dieses Backup ein"
cannotSave: "Speichern fehlgeschlagen"
nameAlreadyExists: "Es existiert bereits ein Backup unter dem Namen \"{name}\". Bitte gib einen anderen Namen ein."
applyConfirm: "Wirklich das Backup \"{name}\" auf dieses Gerät anwenden? Bestehende Einstellungen darauf werden überschrieben."
saveConfirm: "Als {name} speichern?"
deleteConfirm: "Das Backup {name} löschen?"
renameConfirm: "Soll dieses Backup von \"{old}\" zu \"{new}\" umbenannt werden?"
noBackups: "Keine Backups existieren. Backups können über \"Neu erstellen\" erstelllt werden."
createdAt: "Erstellt am: {date} {time}"
updatedAt: "Aktualisiert am: {date} {time}"
cannotLoad: "Laden fehlgeschlagen"
invalidFile: "Ungültiges Dateiformat."
_registry:
scope: "Scope"
key: "Schlüssel"
@@ -1067,8 +1023,6 @@ _mfm:
sparkleDescription: "Verleiht Inhalt einen glitzernden Partikeleffekt."
rotate: "Drehen"
rotateDescription: "Dreht den Inhalt um einen angegebenen Winkel."
plain: "Schlicht"
plainDescription: "Deaktiviert jegliche MFM-Syntax, die sich innerhalb dieses MFM-Effekts befindet."
_instanceTicker:
none: "Nie anzeigen"
remote: "Für Benutzer fremder Instanzen anzeigen"
@@ -1226,9 +1180,6 @@ _tutorial:
step7_1: "Glückwunsch! Du hast die Einführung in die Verwendung von Misskey abgeschlossen."
step7_2: "Wenn du mehr über Misskey lernen möchtest, schau dich im {help}-Bereich um."
step7_3: "Und nun, viel Spaß mit Misskey! 🚀"
step8_1: "Möchtest du abschließend Push-Benachrichtigungen aktivieren?"
step8_2: "Push-Benachrichtigungen erlauben es dir, über Reaktionen, Follows oder Erwähnungen usw. zu erfahren, auch wenn Misskey zu dieser Zeit nicht geöffnet ist."
step8_3: "Diese Einstellung kannst du jederzeit ändern."
_2fa:
alreadyRegistered: "Du hast bereits ein Gerät für Zwei-Faktor-Authentifizierung registriert."
registerDevice: "Neues Gerät registrieren"
@@ -1305,7 +1256,6 @@ _widgets:
activity: "Aktivität"
photos: "Fotos"
digitalClock: "Digitaluhr"
unixClock: "UNIX-Uhr"
federation: "Föderation"
instanceCloud: "Instanzwolke"
postForm: "Notizfenster"
@@ -1316,9 +1266,6 @@ _widgets:
serverMetric: "Servermetriken"
aiscript: "AiScript-Konsole"
aichan: "Ai"
userList: "Benutzerliste"
_userList:
chooseList: "Liste auswählen"
_cw:
hide: "Inhalt verbergen"
show: "Inhalt anzeigen"
@@ -1382,7 +1329,6 @@ _profile:
changeBanner: "Banner ändern"
_exportOrImport:
allNotes: "Alle Notizen"
favoritedNotes: "Als Favorit markierte Notizen"
followingList: "Gefolgte Benutzer"
muteList: "Stummschaltungen"
blockingList: "Blockierungen"
@@ -1455,6 +1401,8 @@ _pages:
eyeCatchingImageRemove: "Vorschaubild entfernen"
chooseBlock: "Block hinzufügen"
selectType: "Typ auswählen"
enterVariableName: "Gib einen Variablennamen ein"
variableNameIsAlreadyUsed: "Dieser Name wird bereits von einer anderen Variable verwendet"
contentBlocks: "Inhalt"
inputBlocks: "Eingabe"
specialBlocks: "Spezial"
@@ -1464,11 +1412,249 @@ _pages:
section: "Abschnitt"
image: "Bild"
button: "Knopf"
if: "Falls"
_if:
variable: "Variable"
post: "Notizfenster"
_post:
text: "Inhalt"
attachCanvasImage: "Leinwandbild anfügen"
canvasId: "Leinwand-ID"
textInput: "Texteingabe"
_textInput:
name: "Variablenname"
text: "Titel"
default: "Standardwert"
textareaInput: "Mehrzeiliges Texteingabefeld"
_textareaInput:
name: "Variablenname"
text: "Titel"
default: "Standardwert"
numberInput: "Zahleneingabe"
_numberInput:
name: "Variablenname"
text: "Titel"
default: "Standardwert"
canvas: "Leinwand"
_canvas:
id: "Leinwand-ID"
width: "Breite"
height: "Höhe"
note: "Eingebettete Notiz"
_note:
id: "Notiz-ID"
idDescription: "Du kannst alternativ auch die Notiz-URL angeben."
detailed: "Detailierte Ansicht"
switch: "Fallunterscheidung"
_switch:
name: "Variablenname"
text: "Titel"
default: "Standardwert"
counter: "Zähler"
_counter:
name: "Variablenname"
text: "Titel"
inc: "Schrittgröße"
_button:
text: "Titel"
colored: "Farbig"
action: "Aktion, die bei Knopfdruck ausgeführt wird"
_action:
dialog: "Dialogfenster anzeigen"
_dialog:
content: "Inhalt"
resetRandom: "Zufallswert zurücksetzen"
pushEvent: "Ein Event senden"
_pushEvent:
event: "Eventname"
message: "Nachricht, die bei Auslösung des Events angezeigt werden soll"
variable: "Variable, die gesendet werden soll"
no-variable: "Keine"
callAiScript: "AiScript ausführen"
_callAiScript:
functionName: "Funktionsname"
radioButton: "Optionsfeld"
_radioButton:
name: "Variablenname"
title: "Titel"
values: "Durch Zeilenümbrüche getrennte Auswahlmöglichkeiten"
default: "Standardwert"
script:
categories:
flow: "Steuerung"
logical: "Logische Operationen"
operation: "Berechnungen"
comparison: "Vergleiche"
random: "Zufällig"
value: "Werte"
fn: "Funktionen"
text: "Textoperationen"
convert: "Konvertierungen"
list: "Listen"
blocks:
text: "Text"
multiLineText: "Text (Mehrzeilig)"
textList: "Textliste"
_textList:
info: "Trenne jeden Eintrag mit einem Zeilenumbruch"
strLen: "Textlänge"
_strLen:
arg1: "Text"
strPick: "Text extrahieren"
_strPick:
arg1: "Text"
arg2: "Textposition"
strReplace: "Textersetzung"
_strReplace:
arg1: "Text"
arg2: "Zu ersetzender Text"
arg3: "Ersetzen mit"
strReverse: "Text umkehren"
_strReverse:
arg1: "Text"
join: "Text zusammenfügen"
_join:
arg1: "Liste"
arg2: "Trennzeichen"
add: "Addieren"
_add:
arg1: "A"
arg2: "B"
subtract: "Subtrahieren"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Multiplizieren"
_multiply:
arg1: "A"
arg2: "B"
divide: "Teilen"
_divide:
arg1: "A"
arg2: "B"
mod: "Rest"
_mod:
arg1: "A"
arg2: "B"
round: "Rundung von Dezimalstellen"
_round:
arg1: "Nummer"
eq: "A und B sind gleich"
_eq:
arg1: "A"
arg2: "B"
notEq: "A und B sind nicht gleich"
_notEq:
arg1: "A"
arg2: "B"
and: "A UND B"
_and:
arg1: "A"
arg2: "B"
or: "A ODER B"
_or:
arg1: "A"
arg2: "B"
lt: "< A ist kleiner als B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A ist größer als B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A ist kleiner als oder gleich B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A ist größer als oder gleich B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Kondition"
_if:
arg1: "Falls"
arg2: "Wenn wahr"
arg3: "Sonst"
not: "NICHT"
_not:
arg1: "NICHT"
random: "Zufällig"
_random:
arg1: "Warscheinlichkeit"
rannum: "Zufallsnummer"
_rannum:
arg1: "Minimum"
arg2: "Maximum"
randomPick: "Zufallswahl aus Liste"
_randomPick:
arg1: "Liste"
dailyRandom: "Zufällig (Pro Nutzer jeden Tag verschieden)"
_dailyRandom:
arg1: "Warscheinlichkeit"
dailyRannum: "Zufallsnummer (Pro Nutzer jeden Tag verschieden)"
_dailyRannum:
arg1: "Minimum"
arg2: "Maximum"
dailyRandomPick: "Zufallsauswahl aus einer Liste (Pro Nutzer jeden Tag verschieden)"
_dailyRandomPick:
arg1: "Liste"
seedRandom: "Zufällig (mit Startwert / Seed)"
_seedRandom:
arg1: "Startwert / Seed"
arg2: "Warscheinlichkeit"
seedRannum: "Zufallsnummer (mit Startwert / Seed)"
_seedRannum:
arg1: "Startwert / Seed"
arg2: "Minimum"
arg3: "Maximum"
seedRandomPick: "Zufallsauswahl aus Liste (mit Startwert / Seed)"
_seedRandomPick:
arg1: "Startwert / Seed"
arg2: "Liste"
DRPWPM: "Zufallsauswahl aus gewichteter Liste (Pro Nutzer jeden Tag verschieden)"
_DRPWPM:
arg1: "Textliste"
pick: "Aus einer Liste wählen"
_pick:
arg1: "Liste"
arg2: "Position"
listLen: "Listenlänge abrufen"
_listLen:
arg1: "Liste"
number: "Nummer"
stringToNumber: "Text zu Nummer"
_stringToNumber:
arg1: "Text"
numberToString: "Nummer zu Text"
_numberToString:
arg1: "Nummer"
splitStrByLine: "Text nach Zeilenumbrüchen aufteilen"
_splitStrByLine:
arg1: "Text"
ref: "Variable"
aiScriptVar: "AiScript Variable"
fn: "Funktion"
_fn:
slots: "Slots"
slots-info: "Trenne jeden Slot mit einem Zeilenumbruch"
arg1: "Ausgabe"
for: "for-Schleife"
_for:
arg1: "Anzahl der Schleifendurchläufe"
arg2: "Aktion"
typeError: "Slot {slot} akzeptiert Werte vom Typ „{expect}“, aber es wurde ein „{actual}“ Wert angegeben!"
thereIsEmptySlot: "Slot {slot} ist leer!"
types:
string: "Text"
number: "Nummer"
boolean: "Wahrheitswert"
array: "Liste"
stringArray: "Textliste"
emptySlot: "Leerer Slot"
enviromentVariables: "Umgebungsvariable"
pageVariables: "Seitenelemente"
argVariables: "Eingabeslots"
_relayStatus:
requesting: "Ausstehend"
accepted: "Akzeptiert"
@@ -1487,7 +1673,6 @@ _notification:
yourFollowRequestAccepted: "Deine Follow-Anfrage wurde akzeptiert"
youWereInvitedToGroup: "{userName} hat dich in eine Gruppe eingeladen"
pollEnded: "Umfrageergebnisse sind verfügbar"
unreadAntennaNote: "Antenne {name}"
emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert"
_types:
all: "Alle"
@@ -1511,7 +1696,6 @@ _deck:
alwaysShowMainColumn: "Hauptspalte immer zeigen"
columnAlign: "Spaltenausrichtung"
addColumn: "Spalte hinzufügen"
configureColumn: "Spalteneinstellungen"
swapLeft: "Mit linker Spalte tauschen"
swapRight: "Mit rechter Spalte tauschen"
swapUp: "Mit oberer Spalte tauschen"

View File

@@ -1,406 +0,0 @@
---
_lang_: "Ελληνικά"
monthAndDay: "{μήνας}/{ημέρα}"
search: "Αναζήτηση"
notifications: "Ειδοποιήσεις"
username: "Όνομα μέλους"
password: "Κωδικός πρόσβασης"
forgotPassword: "Ξέχασα τον κωδικό πρόσβασης"
fetchingAsApObject: "Μαζεύοντας από το Fediverse..."
ok: "Εντάξει"
gotIt: "Τό'πιασα!"
cancel: "Ακύρωση"
enterUsername: "Εισάγετε το όνομα μέλους"
renotedBy: "Κοινοποιήθηκε από {user}"
noNotes: "Δεν υπάρχουν σημειώματα"
noNotifications: "Δεν υπάρχουν ειδοποιήσεις"
settings: "Ρυθμίσεις"
basicSettings: "Βασικές ρυθμίσεις"
otherSettings: "Άλλες ρυθμίσεις"
openInWindow: "Άνοιγμα σε παράθυρο"
profile: "Προφίλ"
timeline: "Χρονολόγιο"
noAccountDescription: "Αυτό το μέλος δεν έχει γράψει βιογραφικό ακόμη."
login: "Σύνδεση"
loggingIn: "Συνδέεστε"
logout: "Αποσύνδεση"
signup: "Δημιουργία λογαριασμού"
uploading: "Ανέβασμα..."
save: "Αποθήκευση"
users: "Μέλη"
addUser: "Προσθήκη μέλους"
favorite: "Προσθήκη στα αγαπημένα"
favorites: "Αγαπημένα"
unfavorite: "Αφαίρεση από αγαπημένα"
favorited: "Προστέθηκε στα αγαπημένα."
alreadyFavorited: "Έχει ήδη προστεθεί στα αγαπημένα."
cantFavorite: "Αδυναμία προσθήκης στα αγαπημένα."
pin: "Καρφίτσωμα στο προφίλ"
unpin: "Ξεκαρφίτσωμα από το προφίλ"
copyContent: "Αντιγραφή περιεχομένων"
copyLink: "Αντιγραφή συνδέσμου"
delete: "Διαγραφή"
deleteAndEdit: "Διαγραφή και επεξεργασία"
deleteAndEditConfirm: "Σίγουρα θέλετε να διαγράψετε αυτό το σημείωμα και να το επεξεργαστείτε; Θα χάσετε όλες τις αντιδράσεις, κοινοποιήσεις και απαντήσεις σε αυτό."
addToList: "Προσθήκη στη λίστα"
sendMessage: "Αποστολή μηνύματος"
copyUsername: "Αντιγραφή ονόματος μέλους"
searchUser: "Αναζήτηση μέλους"
reply: "Απάντηση"
loadMore: "Φόρτωσε περισσότερα"
showMore: "Δείξε περισσότερα"
showLess: "Κλείσιμο"
youGotNewFollower: "σε ακολούθησε"
receiveFollowRequest: "Λάβατε αίτημα ακολούθησης"
followRequestAccepted: "Το αίτημα ακολούθησης έγινε δεκτό"
mention: "Επισήμανση"
mentions: "Επισημάνσεις"
directNotes: "Απευθείας σημειώματα"
importAndExport: "Εισαγωγή / Εξαγωγή"
import: "Εισαγωγή"
export: "Εξαγωγή"
files: "Αρχεία"
download: "Λήψη"
driveFileDeleteConfirm: "Θέλετε σίγουρα να διαγράψετε το αρχείο \"{name}\"; Τα σημειώματα με αυτό το συνημμένο αρχείο επίσης θα διαγραφούν."
unfollowConfirm: "Θέλετε σίγουρα να σταματήσετε να ακολουθείτε το μέλος {name};"
exportRequested: "Ζητήσατε μία εξαγωγή. Αυτό μπορεί να πάρει κάποιον χρόνο. Επίσης θα προστεθεί στον Δίσκο σας μόλις ολοκληρωθεί."
importRequested: "Ζητήσατε μία εισαγωγή. Αυτό μπορεί να πάρει κάποιον χρόνο."
lists: "Λίστες"
noLists: "Δεν έχετε λίστες"
note: "Σημείωμα"
notes: "Σημειώματα"
following: "Ακολουθεί"
followers: "Ακολουθούν"
followsYou: "Σε ακολουθεί"
createList: "Δημιουργία λίστας"
manageLists: "Διαχείριση λιστών"
error: "Σφάλμα"
somethingHappened: "Προέκυψε ένα σφάλμα"
retry: "Προσπάθεια ξανά"
pageLoadError: "Ένα σφάλμα προέκυψε φορτώνοντας τη σελίδα."
pageLoadErrorDescription: "Αυτό κανονικά προκαλείται από σφάλματα δικτύου ή από την προσωρινή μνήμη του προγράμματος περιήγησης. Δοκιμάστε να σβήσετε την προσωρινή μνήμη (cache) και ξαναδοκιμάστε μετά από λίγο."
serverIsDead: "Αυτός ο server δεν αποκρίνεται. Παρακαλώ περιμέντε λίγο και δοκιμάστε ξανά."
youShouldUpgradeClient: "Για να δείτε αυτή τη σελίδα, παρακαλώ επαναφορτώστε για να ενημερωθεί το πρόγραμμα."
enterListName: "Πληκτρολογήστε ένα όνομα για τη λίστα"
privacy: "Ιδιωτικότητα"
makeFollowManuallyApprove: "Τα αιτήματα ακολούθησης χρειάζονται έγκριση"
defaultNoteVisibility: "Προεπιλεγμένη ορατότητα"
follow: "Ακολουθήστε"
followRequest: "Στείλτε αίτημα ακολούθησης"
followRequests: "Αιτήματα ακολούθησης"
unfollow: "Να μην ακολουθώ"
followRequestPending: "Το αίτημα ακολούθησης εκκρεμεί"
enterEmoji: "Εισάγετε ένα emoji"
renote: "Κοινοποίηση σημειώματος"
unrenote: "Ακύρωση κοινοποίησης"
renoted: "Κοινοποιήθηκε."
cantRenote: "Αυτή η δημοσίευση δεν μπορεί να κοινοποιηθεί."
cantReRenote: "Μία κοινοποίηση δεν μπορεί να κοινοποιηθεί."
quote: "Παράθεση"
pinnedNote: "Καρφιτσωμένο σημείωμα"
pinned: "Καρφίτσωμα στο προφίλ"
you: "Εσύ"
clickToShow: "Κάντε κλικ για εμφάνιση"
add: "Προσθέστε"
reaction: "Αντιδράσεις"
reactionSetting: "Αντιδράσεις για εμφάνιση στην επιλογή αντίδρασης"
reactionSettingDescription2: "Σύρετε για να αλλάξετε τη σειρά, κάντε κλικ για να διαγράψετε, πατήστε \"+\" για να προσθέσετε."
rememberNoteVisibility: "Θυμήσου τις ρυθμίσεις ορατότητας σημειώματος"
attachCancel: "Διαγραφή αρχείου"
enterFileName: "Πληκτρολογήστε όνομα αρχείου"
mute: "Σίγαση"
unmute: "Άρση σίγασης"
block: "Μπλοκάρισμα"
unblock: "Άρση μπλοκαρίσματος"
suspend: "Αποβολή"
unsuspend: "Άρση αποβολής"
blockConfirm: "Θέλετε σίγουρα να μπλοκάρετε αυτόν τον λογαριασμό;"
unblockConfirm: "Θέλετε σίγουρα να ξεμπλοκάρετε αυτόν τον λογαριασμό;"
suspendConfirm: "Θέλετε σίγουρα να αποβάλλετε αυτόν τον λογαριασμό;"
unsuspendConfirm: "Θέλετε σίγουρα να άρετε την αποβολή αυτού του λογαριασμού;"
selectList: "Επιλέξτε μία λίστα"
selectAntenna: "Επιλέξτε μία αντένα"
selectWidget: "Επιλέξτε ένα μαραφέτι"
editWidgets: "Επεξεργασία μαραφετίων"
editWidgetsExit: "Ολοκληρώθηκε"
customEmojis: "Επιπλέον emoji"
emojiName: "Όνομα emoji"
addEmoji: "Προσθήκη emoji"
settingGuide: "Συνιστώμενες ρυθμίσεις"
flagAsBot: "Αυτός ο λογαριασμός είναι bot"
flagAsCat: "Αυτός ο λογαριασμός είναι γάτα"
flagShowTimelineReplies: "Εμφάνιση απαντήσεων στο χρονολόγιο"
addAccount: "Προσθήκη λογαριασμού"
general: "Γενικές"
wallpaper: "Ταπετσαρία"
setWallpaper: "Ορισμός ταπετσαρίας"
removeWallpaper: "Διαγραφή ταπετσαρίας"
searchWith: "Αναζήτηση: {q}"
youHaveNoLists: "Δεν έχετε λίστες"
followConfirm: "Θέλετε σίγουρα να ακολουθήσετε τον λογαριασμό {name};"
host: "Φιλοξενεί"
selectUser: "Επιλέξτε ένα μέλος"
recipient: "Αποδέκτης-τρια"
annotation: "Σχόλια"
federation: "Ομοσπονδία"
storageUsage: "Χρήση χώρου"
version: "Έκδοση"
metadata: "Μεταδεδομένα"
network: "Δίκτυο"
disk: "Δίσκος"
instanceInfo: "Πληροφορίες του instance"
statistics: "Στατιστικά"
clearQueue: "Εκκαθάριση ουράς"
clearQueueConfirmTitle: "Θέλετε να διαγράψετε την ουρά;"
clearCachedFiles: "Εκκαθάριση προσωρινής μνήμης"
done: "Ολοκληρώθηκε"
attachFile: "Επισύναψη αρχείων"
more: "Περισσότερα!"
noSuchUser: "Το μέλος δεν βρέθηκε"
announcements: "Ανακοινώσεις"
imageUrl: "URL εικόνας"
remove: "Διαγραφή"
removed: "Η διαγραφή ολοκληρώθηκε επιτυχώς"
saved: "Αποθηκεύτηκε"
messaging: "Συνομιλία"
upload: "Ανεβάστε"
fromDrive: "Από τον Αποθηκευτικό Χώρο"
fromUrl: "Από URL"
uploadFromUrl: "Ανεβάστε από URL"
explore: "Εξερευνήστε"
messageRead: "Διαβάστηκε"
startMessaging: "Ξεκινήστε μία συνομιλία"
nUsersRead: "διαβάστηκε από {n}"
tos: "Όροι χρήσης"
start: "Ας αρχίσουμε"
home: "Κεντρικό"
activity: "Δραστηριότητα"
images: "Εικόνες"
birthday: "Γενέθλια"
registeredDate: "Έγινε μέλος στις"
location: "Τοποθεσία"
theme: "Θέματα"
light: "Ανοιχτόχρωμο"
dark: "Σκούρο"
drive: "Αποθηκευτικός Χώρος"
fileName: "Όνομα αρχείου"
selectFile: "Επιλέξτε ένα αρχείο"
selectFiles: "Επιλέξτε αρχεία"
selectFolder: "Επιλέξτε φάκελο"
selectFolders: "Επιλέξτε φακέλους"
renameFile: "Μετονομασία αρχείου"
addFile: "Προσθήκη αρχείου"
emptyDrive: "Ο Αποθηκευτικός Χώρος σας είναι άδειος"
copyUrl: "Αντιγραφή URL"
rename: "Αλλαγή ονόματος"
avatar: "Εικονίδιο"
banner: "Πανό"
reload: "Ανανέωση"
doNothing: "Αγνόηση"
watch: "Παρακολούθηση"
unwatch: "Τέλος παρακολούθησης"
accept: "Αποδοχή"
reject: "Απόρριψη"
normal: "Κανονικό"
instanceName: "Όνομα instance"
thisYear: "Έτος"
thisMonth: "Μήνας"
today: "Σήμερα"
dayX: "{day}"
pages: "Σελίδες"
connectService: "Σύνδεση"
disconnectService: "Αποσύνδεση"
registration: "Εγγραφή"
pinnedPages: "Καρφιτσωμένες Σελίδες"
pinnedNotes: "Καρφιτσωμένα σημειώματα"
antennas: "Αντένες"
manageAntennas: "Διαχείριση αντενών"
name: "Όνομα"
antennaSource: "Πηγή αντένας"
antennaKeywords: "Λέξεις-κλειδιά για παρακολούθηση"
antennaExcludeKeywords: "Λέξεις-κλειδιά για αποκλεισμό"
notifyAntenna: "Ειδοποίηση για νέα σημειώματα"
withFileAntenna: "Μόνο σημειώματα με αρχεία"
caseSensitive: "Διάκριση Πεζών-Κεφαλαίων"
popularTags: "Δημοφιλείς ετικέτες"
userList: "Λίστες"
about: "Πληροφορίες"
moderator: "Συντονιστής"
moderation: "Συντονισμός"
cacheClear: "Εκκαθάριση προσωρινής μνήμης"
markAsReadAllNotifications: "Όλες οι ειδοποιήσεις διαβάστηκαν"
group: "Ομάδα"
groups: "Ομάδες"
createGroup: "Δημιουργία ομάδας"
ownedGroups: "Οι ομάδες σας"
groupName: "Όνομα ομάδας"
members: "Μέλη"
transfer: "Μεταφορά"
messagingWithUser: "Ιδιωτική συνομιλία"
messagingWithGroup: "Ομαδική συνομιλία"
title: "Τίτλος"
text: "Κείμενο"
enable: "Ενεργοποίηση"
next: "Επόμενο"
noteOf: "Σημείωμα από {user}"
inviteToGroup: "Πρόσκληση στην ομάδα"
quoteAttached: "Παράθεση"
signinRequired: "Παρακαλούμε δημιουργήστε λογαριασμό ή συνδεθείτε πριν συνεχίσετε"
category: "Κατηγορία"
tags: "Ετικέτες"
createAccount: "Δημιουργία λογαριασμού"
local: "Τοπικό"
remote: "Απομακρυσμένo"
total: "Σύνολο"
appearance: "Εμφάνιση"
accountSettings: "Ρυθμίσεις λογαριασμού"
sounds: "Ήχοι"
sound: "Ήχοι"
listen: "Ακρόαση"
showInPage: "Εμφάνιση στη σελίδα"
volume: "Ένταση"
masterVolume: "Κύρια ένταση"
details: "Λεπτομέρειες"
install: "Εγκατάσταση"
uninstall: "Κατάργηση εγκατάστασης"
manage: "Διαχείριση"
smtpHost: "Φιλοξενεί"
smtpUser: "Όνομα μέλους"
smtpPass: "Κωδικός πρόσβασης"
notificationSetting: "Ρυθμίσεις ειδοποιήσεων"
notificationSettingDesc: "Επιλέξτε τους τύπους ειδοποιήσεων που εμφανίζονται"
switchUi: "Αλλαγή UI"
clip: "Κλιπ"
driveFilesCount: "Αριθμός αρχείων Αποθηκευτικού Χώρου"
driveUsage: "Χρήση Αποθηκευτικού Χώρου"
noteFavoritesCount: "Αριθμός αγαπημένων σημειωμάτων"
clips: "Κλιπ"
clearCache: "Εκκαθάριση προσωρινής μνήμης"
emailNotification: "Ειδοποιήσεις μέσω mail"
inChannelSearch: "Αναζήτηση στο κανάλι"
info: "Πληροφορίες"
notRecommended: "Δεν προτείνεται"
switchAccount: "Αλλαγή λογαριασμού"
user: "Μέλη"
administration: "Διαχείριση"
switch: "Εναλλαγή"
gallery: "Γκαλερί"
global: "Παγκόσμιο"
searchResult: "Αποτελέσματα αναζήτησης"
learnMore: "Μάθετε περισσότερα"
controlPanel: "Πίνακας ελέγχου"
manageAccounts: "Διαχείριση Λογαριασμών"
searchByGoogle: "Αναζήτηση"
file: "Αρχεία"
recommended: "Προτεινόμενα"
cannotUploadBecauseNoFreeSpace: "Το ανέβασμα απέτυχε λόγω ανεπαρκούς Αποθηκευτικού Χώρου"
_email:
_follow:
title: "Έχετε ένα νέο ακόλουθο"
_mfm:
mention: "Επισήμανση"
quote: "Παράθεση"
emoji: "Επιπλέον emoji"
search: "Αναζήτηση"
_channel:
featured: "Δημοφιλή"
_theme:
keys:
panel: "Πίνακας"
mention: "Επισήμανση"
renote: "Κοινοποίηση σημειώματος"
_sfx:
note: "Σημειώματα"
notification: "Ειδοποιήσεις"
chat: "Συνομιλία"
chatBg: "Συνομιλία (Παρασκήνιο)"
antenna: "Αντένες"
channel: "Ειδοποιήσεις καναλιών"
_ago:
future: "Μελλοντικό"
justNow: "Μόλις τώρα"
secondsAgo: "{n} δευτερόλεπτο(α) πριν"
minutesAgo: "{n} λεπτό(ά) πριν"
hoursAgo: "{n} ώρα(ες) πριν"
daysAgo: "{n} μέρα(ες) πριν"
weeksAgo: "{n} εβδομάδα(ες) πριν"
monthsAgo: "{n} μήνα(ες) πριν"
yearsAgo: "{n} έτος(η) πριν"
_permissions:
"write:drive": "Επεξεργαστείτε ή διαγράψτε τα αρχεία και τους φακέλους του Αποθηκευτικού Χώρου σας"
"read:favorites": "Δείτε τη λίστα των αγαπημένων σας"
"write:favorites": "Επεξεργαστείτε τη λίστα των αγαπημένων σας"
"read:messaging": "Δείτε τις συνομιλίες σας"
"write:messaging": "Γράψτε ή διαγράψτε μηνύματα συνομιλίας"
"read:notifications": "Δείτε τις ειδοποιήσεις σας"
"write:notifications": "Διαχειριστείτε τις ειδοποιήσεις σας"
"read:pages": "Δείτε τις Σελίδες σας"
"write:pages": "Επεξεργαστείτε ή διαγράψτε τις σελίδες σας"
_antennaSources:
all: "Όλα τα σημειώματα"
homeTimeline: "Σημειώματα από μέλη που ακολουθείτε"
users: "Σημειώματα από συγκεκριμένα μέλη"
userList: "Σημειώματα από καθορισμένη λίστα μελών"
userGroup: "Σημειώματα από μέλη καθορισμένης ομάδας"
_widgets:
notifications: "Ειδοποιήσεις"
timeline: "Χρονολόγιο"
calendar: "Ημερολόγιο"
trends: "Δημοφιλή"
clock: "Ρολόι"
activity: "Δραστηριότητα"
photos: "Φωτογραφίες"
digitalClock: "Ψηφιακό ρολόι"
federation: "Ομοσπονδία"
postForm: "Φόρμα δημοσίευσης"
button: "Κουμπί"
onlineUsers: "Συνδεδεμένα μέλη"
_userList:
chooseList: "Επιλέξτε μία λίστα"
_cw:
show: "Δείτε περισσότερα"
_visibility:
home: "Κεντρικό"
homeDescription: "Δημοσίευση στο κεντρικό χρονολόγιο μόνο"
followers: "Ακολουθούν"
_profile:
name: "Όνομα"
username: "Όνομα μέλους"
_exportOrImport:
allNotes: "Όλα τα σημειώματα"
followingList: "Ακολουθεί"
muteList: "Μέλη σε σίγαση"
blockingList: "Μπλοκαρισμένα μέλη"
userLists: "Λίστες"
_charts:
federation: "Ομοσπονδία"
_timelines:
home: "Κεντρικό"
local: "Τοπικό"
social: "Κοινωνικό"
global: "Παγκόσμιο"
_pages:
viewPage: "Δείτε τις Σελίδες σας"
blocks:
image: "Εικόνες"
_notification:
youGotMessagingMessageFromUser: "{name} σάς έστειλε ένα μήνυμα συνομιλίας"
youWereFollowed: "σε ακολούθησε"
_types:
follow: "Νέοι ακόλουθοι"
mention: "Επισήμανση"
renote: "Κοινοποίηση σημειώματος"
quote: "Παράθεση"
reaction: "Αντιδράσεις"
_actions:
reply: "Απάντηση"
renote: "Κοινοποίηση σημειώματος"
_deck:
widgetsIntroduction: "Παρακαλούμε επιλέξτε \"Επεξεργασία μαραφετίων\" στο μενού και προσθέστε μαραφέτι."
_columns:
widgets: "Μαραφέτια"
notifications: "Ειδοποιήσεις"
tl: "Χρονολόγιο"
antenna: "Αντένες"
list: "Λίστα"
mentions: "Επισημάνσεις"

View File

@@ -2,7 +2,6 @@
_lang_: "English"
headlineMisskey: "A network connected by notes"
introMisskey: "Welcome! Misskey is an open source, decentralized microblogging service.\nCreate \"notes\" to share your thoughts with everyone around you. 📡\nWith \"reactions\", you can also quickly express your feelings about everyone's notes. 👍\nLet's explore a new world! 🚀"
poweredByMisskeyDescription: "{name} is one of the services powered by the open source platform <b>Misskey</b> (referred to as a \"Misskey instance\")."
monthAndDay: "{month}/{day}"
search: "Search"
notifications: "Notifications"
@@ -13,7 +12,6 @@ fetchingAsApObject: "Fetching from the Fediverse..."
ok: "OK"
gotIt: "Got it!"
cancel: "Cancel"
noThankYou: "Not now"
enterUsername: "Enter username"
renotedBy: "Renoted by {user}"
noNotes: "No notes"
@@ -49,13 +47,11 @@ deleteAndEdit: "Delete and edit"
deleteAndEditConfirm: "Are you sure you want to delete this note and edit it? You will lose all reactions, renotes and replies to it."
addToList: "Add to list"
sendMessage: "Send a message"
copyRSS: "Copy RSS"
copyUsername: "Copy username"
searchUser: "Search for a user"
reply: "Reply"
loadMore: "Load more"
showMore: "Show more"
showLess: "Close"
youGotNewFollower: "followed you"
receiveFollowRequest: "Follow request received"
followRequestAccepted: "Follow request accepted"
@@ -83,7 +79,7 @@ manageLists: "Manage lists"
error: "Error"
somethingHappened: "An error has occurred"
retry: "Retry"
pageLoadError: "An error occurred while loading the page."
pageLoadError: "An error occurred loading the page."
pageLoadErrorDescription: "This is normally caused by network errors or the browser's cache. Try clearing the cache and then try again after waiting a little while."
serverIsDead: "This server is not responding. Please wait for a while and try again."
youShouldUpgradeClient: "To view this page, please refresh to update your client."
@@ -218,7 +214,7 @@ subscribing: "Subscribing"
publishing: "Publishing"
notResponding: "Not responding"
instanceFollowing: "Following on instance"
instanceFollowers: "Instance followers"
instanceFollowers: "Followers of instance"
instanceUsers: "Users of this instance"
changePassword: "Change password"
security: "Security"
@@ -351,10 +347,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Enable reCAPTCHA"
recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret key"
turnstile: "Turnstile"
enableTurnstile: "Enable Turnstile"
turnstileSiteKey: "Site key"
turnstileSecretKey: "Secret key"
avoidMultiCaptchaConfirm: "Using multiple Captcha systems may cause interference between them. Would you like to disable the other Captcha systems currently active? If you would like them to stay enabled, press cancel."
antennas: "Antennas"
manageAntennas: "Manage Antennas"
@@ -457,8 +449,7 @@ language: "Language"
uiLanguage: "User interface language"
groupInvited: "You've been invited to a group"
aboutX: "About {x}"
emojiStyle: "Emoji style"
native: "Native"
useOsNativeEmojis: "Use OS native Emoji"
disableDrawer: "Don't use drawer-style menus"
youHaveNoGroups: "You have no groups"
joinOrCreateGroup: "Get invited to a group or create your own."
@@ -511,7 +502,6 @@ deleteAll: "Delete all"
showFixedPostForm: "Display the posting form at the top of the timeline"
newNoteRecived: "There are new notes"
sounds: "Sounds"
sound: "Sounds"
listen: "Listen"
none: "None"
showInPage: "Show in page"
@@ -571,7 +561,6 @@ author: "Author"
leaveConfirm: "There are unsaved changes. Do you want to discard them?"
manage: "Management"
plugins: "Plugins"
preferencesBackups: "Preference backups"
deck: "Deck"
undeck: "Leave Deck"
useBlurEffectForModal: "Use blur effect for modals"
@@ -717,7 +706,6 @@ accentColor: "Accent color"
textColor: "Text color"
saveAs: "Save as..."
advanced: "Advanced"
advancedSettings: "Advanced settings"
value: "Value"
createdAt: "Created at"
updatedAt: "Updated at"
@@ -874,7 +862,7 @@ requireAdminForView: "You must log in with an administrator account to view this
isSystemAccount: "An account created and automatically operated by the system."
typeToConfirm: "Please enter {x} to confirm"
deleteAccount: "Delete account"
document: "Documentation"
document: "Document"
numberOfPageCache: "Number of cached pages"
numberOfPageCacheDescription: "Increasing this number will improve convenience for users but cause more server load as well as more memory to be used."
logoutConfirm: "Really log out?"
@@ -902,20 +890,6 @@ activeEmailValidationDescription: "Enables stricter validation of email addresse
navbar: "Navigation bar"
shuffle: "Shuffle"
account: "Account"
move: "Move"
pushNotification: "Push notifications"
subscribePushNotification: "Enable push notifications"
unsubscribePushNotification: "Disable push notifications"
pushNotificationAlreadySubscribed: "Push notifications are already enabled"
pushNotificationNotSupported: "Your browser or instance does not support push notifications"
sendPushNotificationReadMessage: "Delete push notifications once the relevant notifications or messages have been read"
sendPushNotificationReadMessageCaption: "A notification containing the text \"{emptyPushNotificationMessage}\" will be displayed for a short time. This may increase the battery usage of your device, if applicable."
windowMaximize: "Maximize"
windowRestore: "Restore"
caption: "Caption"
loggedInAsBot: "Currently logged in as bot"
tools: "Tools"
cannotLoad: "Unable to load"
_sensitiveMediaDetection:
description: "Reduces the effort of server moderation through automatically recognizing NSFW media via Machine Learning. This will slightly increase the load on the server."
sensitivity: "Detection sensitivity"
@@ -966,24 +940,6 @@ _plugin:
install: "Install plugins"
installWarn: "Please do not install untrustworthy plugins."
manage: "Manage plugins"
_preferencesBackups:
list: "Created backups"
saveNew: "Save new backup"
loadFile: "Load from file"
apply: "Apply to this device"
save: "Save changes"
inputName: "Please enter a name for this backup"
cannotSave: "Saving failed"
nameAlreadyExists: "A backup called \"{name}\" already exists. Please enter a different name."
applyConfirm: "Do you really want to apply the \"{name}\" backup to this device? Existing settings of this device will be overwritten."
saveConfirm: "Save backup as {name}?"
deleteConfirm: "Delete the {name} backup?"
renameConfirm: "Rename this backup from \"{old}\" to \"{new}\"?"
noBackups: "No backups exist. You may backup your client settings on this server by using \"Create new backup\"."
createdAt: "Created at: {date} {time}"
updatedAt: "Updated at: {date} {time}"
cannotLoad: "Loading failed"
invalidFile: "Invalid file format"
_registry:
scope: "Scope"
key: "Key"
@@ -1067,8 +1023,6 @@ _mfm:
sparkleDescription: "Gives content a sparkling particle effect."
rotate: "Rotate"
rotateDescription: "Turns content by a specified angle."
plain: "Plain"
plainDescription: "Deactivates the effects of all MFM contained within this MFM effect."
_instanceTicker:
none: "Never show"
remote: "Show for remote users"
@@ -1191,13 +1145,13 @@ _sfx:
_ago:
future: "Future"
justNow: "Just now"
secondsAgo: "{n}s ago"
minutesAgo: "{n}m ago"
hoursAgo: "{n}h ago"
daysAgo: "{n}d ago"
weeksAgo: "{n}w ago"
monthsAgo: "{n}mo ago"
yearsAgo: "{n}y ago"
secondsAgo: "{n} second(s) ago"
minutesAgo: "{n} minute(s) ago"
hoursAgo: "{n} hour(s) ago"
daysAgo: "{n} day(s) ago"
weeksAgo: "{n} week(s) ago"
monthsAgo: "{n} month(s) ago"
yearsAgo: "{n} year(s) ago"
_time:
second: "Second(s)"
minute: "Minute(s)"
@@ -1225,10 +1179,7 @@ _tutorial:
step6_3: "To attach a \"reaction\", press the \"+\" mark on another user's note and choose an emoji you'd like to react with."
step7_1: "Congratulations! You have now finished Misskey's basic tutorial."
step7_2: "If you would like to learn more about Misskey, try the {help} section."
step7_3: "Now then, have fun with Misskey! 🚀"
step8_1: "Lastly, would you like to enable push notifications?"
step8_2: "Enabling these will allow you to receive notifications for mentions, reactions, follows, etc. even when Misskey is not opened."
step8_3: "You can always change this setting later."
step7_3: "Now then, good luck and have fun with Misskey! 🚀"
_2fa:
alreadyRegistered: "You have already registered a 2-factor authentication device."
registerDevice: "Register a new device"
@@ -1305,7 +1256,6 @@ _widgets:
activity: "Activity"
photos: "Photos"
digitalClock: "Digital clock"
unixClock: "UNIX clock"
federation: "Federation"
instanceCloud: "Instance cloud"
postForm: "Posting form"
@@ -1316,9 +1266,6 @@ _widgets:
serverMetric: "Server metrics"
aiscript: "AiScript console"
aichan: "Ai"
userList: "User list"
_userList:
chooseList: "Select a list"
_cw:
hide: "Hide"
show: "Show content"
@@ -1382,7 +1329,6 @@ _profile:
changeBanner: "Change banner"
_exportOrImport:
allNotes: "All notes"
favoritedNotes: "Favorite notes"
followingList: "Followed users"
muteList: "Muted users"
blockingList: "Blocked users"
@@ -1455,6 +1401,8 @@ _pages:
eyeCatchingImageRemove: "Delete thumbnail"
chooseBlock: "Add a block"
selectType: "Select a type"
enterVariableName: "Enter a variable name"
variableNameIsAlreadyUsed: "This variable name is already in use"
contentBlocks: "Content"
inputBlocks: "Input"
specialBlocks: "Special"
@@ -1464,11 +1412,249 @@ _pages:
section: "Section"
image: "Images"
button: "Button"
if: "If"
_if:
variable: "Variable"
post: "Posting form"
_post:
text: "Content"
attachCanvasImage: "Attach canvas image"
canvasId: "Canvas ID"
textInput: "Text input"
_textInput:
name: "Variable name"
text: "Title"
default: "Default value"
textareaInput: "Multiline text input"
_textareaInput:
name: "Variable name"
text: "Title"
default: "Default value"
numberInput: "Numeric input"
_numberInput:
name: "Variable name"
text: "Title"
default: "Default value"
canvas: "Canvas"
_canvas:
id: "Canvas ID"
width: "Width"
height: "Height"
note: "Embedded note"
_note:
id: "Note ID"
idDescription: "You can alternatively paste the note URL here."
detailed: "Detailed view"
switch: "Switch"
_switch:
name: "Variable name"
text: "Title"
default: "Default value"
counter: "Counter"
_counter:
name: "Variable name"
text: "Title"
inc: "Step"
_button:
text: "Title"
colored: "Colored"
action: "Behavior when the button is pressed"
_action:
dialog: "Show a dialog"
_dialog:
content: "Content"
resetRandom: "Reset the random seed"
pushEvent: "Send an event"
_pushEvent:
event: "Event name"
message: "Message to display when activated"
variable: "Variable to send"
no-variable: "None"
callAiScript: "Invoke AiScript"
_callAiScript:
functionName: "Function name"
radioButton: "Choice"
_radioButton:
name: "Variable name"
title: "Title"
values: "List of choices separated by line breaks"
default: "Default value"
script:
categories:
flow: "Flow control"
logical: "Logical operation"
operation: "Computation"
comparison: "Comparison"
random: "Random"
value: "Values"
fn: "Functions"
text: "Text operations"
convert: "Transformations"
list: "Lists"
blocks:
text: "Text"
multiLineText: "Text (multiline)"
textList: "Text list"
_textList:
info: "Separate each entry with a line break"
strLen: "Text length"
_strLen:
arg1: "Text"
strPick: "Extract string"
_strPick:
arg1: "Text"
arg2: "String location"
strReplace: "Replacement string"
_strReplace:
arg1: "Text"
arg2: "Text to be replaced"
arg3: "Replace with"
strReverse: "Flip text"
_strReverse:
arg1: "Text"
join: "Text concatenation"
_join:
arg1: "Lists"
arg2: "Separator"
add: "Add"
_add:
arg1: "A"
arg2: "B"
subtract: "Subtract"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Multiply"
_multiply:
arg1: "A"
arg2: "B"
divide: "Divide"
_divide:
arg1: "A"
arg2: "B"
mod: "Remainder"
_mod:
arg1: "A"
arg2: "B"
round: "Decimal rounding"
_round:
arg1: "Number"
eq: "A and B are equal"
_eq:
arg1: "A"
arg2: "B"
notEq: "A and B are different"
_notEq:
arg1: "A"
arg2: "B"
and: "A AND B"
_and:
arg1: "A"
arg2: "B"
or: "A OR B"
_or:
arg1: "A"
arg2: "B"
lt: "< A is less than B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A is larger than B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A is less than or equal to B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A is greater than or equal to B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Branch"
_if:
arg1: "If"
arg2: "Then"
arg3: "Else"
not: "NOT"
_not:
arg1: "NOT"
random: "Random"
_random:
arg1: "Probability"
rannum: "Random number"
_rannum:
arg1: "Minimum value"
arg2: "Maximum value"
randomPick: "Randomly choose from list"
_randomPick:
arg1: "List"
dailyRandom: "Random (Changes once a day for each user)"
_dailyRandom:
arg1: "Probability"
dailyRannum: "Random number (Changes once a day for each user)"
_dailyRannum:
arg1: "Minimum value"
arg2: "Maximum value"
dailyRandomPick: "Randomly choose from a list (Changes once a day for each user)"
_dailyRandomPick:
arg1: "List"
seedRandom: "Random (with seed)"
_seedRandom:
arg1: "Seed"
arg2: "Probability"
seedRannum: "Random number (with seed)"
_seedRannum:
arg1: "Seed"
arg2: "Minimum value"
arg3: "Maximum value"
seedRandomPick: "Randomly choose from list (with seed)"
_seedRandomPick:
arg1: "Seed"
arg2: "List"
DRPWPM: "Randomly choose from weighted list (Changes once a day for each user)"
_DRPWPM:
arg1: "Text list"
pick: "Select from list"
_pick:
arg1: "List"
arg2: "Position"
listLen: "Get length of list"
_listLen:
arg1: "List"
number: "Number"
stringToNumber: "Text to number"
_stringToNumber:
arg1: "Text"
numberToString: "Number to text"
_numberToString:
arg1: "Number"
splitStrByLine: "Split text by line breaks"
_splitStrByLine:
arg1: "Text"
ref: "Variable"
aiScriptVar: "AiScript Variable"
fn: "Function"
_fn:
slots: "Slots"
slots-info: "Separate each slot with a line break"
arg1: "Output"
for: "for-Loop"
_for:
arg1: "Number of times to repeat"
arg2: "Action"
typeError: "Slot {slot} accepts values of type \"{expect}\", but the provided value is of type \"{actual}\"!"
thereIsEmptySlot: "Slot {slot} is empty!"
types:
string: "Text"
number: "Number"
boolean: "Flag"
array: "List"
stringArray: "Text list"
emptySlot: "Empty slot"
enviromentVariables: "Environment variables"
pageVariables: "Page variables"
argVariables: "Input slots"
_relayStatus:
requesting: "Pending"
accepted: "Accepted"
@@ -1487,7 +1673,6 @@ _notification:
yourFollowRequestAccepted: "Your follow request was accepted"
youWereInvitedToGroup: "{userName} invited you to a group"
pollEnded: "Poll results have become available"
unreadAntennaNote: "Antenna {name}"
emptyPushNotificationMessage: "Push notifications have been updated"
_types:
all: "All"
@@ -1511,7 +1696,6 @@ _deck:
alwaysShowMainColumn: "Always show main column"
columnAlign: "Align columns"
addColumn: "Add column"
configureColumn: "Column settings"
swapLeft: "Swap with the left column"
swapRight: "Swap with the right column"
swapUp: "Swap with the above column"

View File

@@ -1,8 +1,7 @@
---
_lang_: "Español"
headlineMisskey: "Red conectada por notas"
introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto.\nEscribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos 👍\n¡Exploremos juntos un nuevo mundo! 🚀"
poweredByMisskeyDescription: "{name} es uno de los servicios (también llamado instancia) que usa la plataforma de código abierto <b>Misskey</b>"
introMisskey: "¡Bienvenido/a! Misskey es un servicio de microblogging descentralizado de código abierto.\nEscribe \"notas\" para compartir lo que te ocurre ahora o para contar sobre ti a todos 📡\nCon la función de \"reacciones\", puedes también añadir una reacción rápida a las notas de todos 👍\nExplora un nuevo mundo 🚀"
monthAndDay: "{day}/{month}"
search: "Buscar"
notifications: "Notificaciones"
@@ -11,7 +10,7 @@ password: "Contraseña"
forgotPassword: "Olvidé mi Contraseña"
fetchingAsApObject: "Buscando en el fediverso"
ok: "OK"
gotIt: "¡Lo tengo!"
gotIt: "Entendido"
cancel: "Cancelar"
enterUsername: "Introduce el nombre de usuario"
renotedBy: "Renotado por {user}"
@@ -23,37 +22,36 @@ basicSettings: "Configuración Básica"
otherSettings: "Configuración avanzada"
openInWindow: "Abrir en una ventana"
profile: "Perfil"
timeline: "Línea de tiempo"
noAccountDescription: "Este usuario no ha escrito su biografía aún"
timeline: "Linea de tiempo"
noAccountDescription: "Este usuario no tiene una descripción"
login: "Iniciar sesión"
loggingIn: "Iniciando sesión"
logout: "Cerrar sesión"
signup: "Registrarse"
uploading: "Cargando..."
uploading: "Cargando"
save: "Guardar"
users: "Usuarios"
addUser: "Agregar usuario"
favorite: "Añadir a favoritos"
favorite: "Favorito"
favorites: "Favoritos"
unfavorite: "Quitar de favoritos"
favorited: "Añadido a favoritos."
favorited: "Añadido a favoritos"
alreadyFavorited: "Ya había sido añadido a favoritos"
cantFavorite: "No se puede añadir a favoritos."
pin: "Fijar al perfil"
cantFavorite: "No fue añadido a favoritos"
pin: "Fijar"
unpin: "Desfijar"
copyContent: "Copiar contenido"
copyLink: "Copiar enlace"
delete: "Borrar"
deleteAndEdit: "Borrar y editar"
deleteAndEditConfirm: "¿Estás seguro de que quieres borrar esta nota y editarla? Perderás todas las reacciones, renotas y respuestas."
deleteAndEditConfirm: "¿Quieres borrar y editar este nota? Las reacciones, renotes, respuestas y todo desaparecerán."
addToList: "Agregar a lista"
sendMessage: "Enviar un mensaje"
sendMessage: "Énviar mensaje"
copyUsername: "Copiar nombre de usuario"
searchUser: "Buscar un usuario"
searchUser: "Búsqueda de usuarios"
reply: "Responder"
loadMore: "Ver más"
showMore: "Ver más"
showLess: "Cerrar"
youGotNewFollower: "te ha seguido"
receiveFollowRequest: "Recibiste una solicitud de seguimiento"
followRequestAccepted: "La solicitud de seguimiento fue aceptada"
@@ -89,11 +87,11 @@ enterListName: "Ingrese nombre de lista"
privacy: "Privacidad"
makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento"
defaultNoteVisibility: "Visibilidad por defecto"
follow: "Seguir"
followRequest: "Enviar solicitud de seguimiento"
follow: "Sigue"
followRequest: "Solicitud de seguimiento"
followRequests: "Solicitudes de seguimiento"
unfollow: "Dejar de seguir"
followRequestPending: "Solicitudes de seguimiento pendiente"
followRequestPending: "Solicitudes de seguimiento pendientes"
enterEmoji: "Ingresar emojis"
renote: "Renotar"
unrenote: "Quitar renota"
@@ -102,7 +100,7 @@ cantRenote: "No se puede renotar este post"
cantReRenote: "No se puede renotar una renota"
quote: "Citar"
pinnedNote: "Nota fijada"
pinned: "Fijar al perfil"
pinned: "Fijar"
you: "Tú"
clickToShow: "Click para ver"
sensitive: "Marcado como sensible"
@@ -205,7 +203,6 @@ done: "Terminado"
processing: "Procesando"
preview: "Vista previa"
default: "Predeterminado"
defaultValueIs: "Predeterminado"
noCustomEmojis: "No hay emojis personalizados"
noJobs: "No hay trabajos"
federating: "Federando"
@@ -349,10 +346,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "activar reCAPTCHA"
recaptchaSiteKey: "Clave del sitio"
recaptchaSecretKey: "Clave secreta"
turnstile: "Turnstile"
enableTurnstile: "Habilitar Turnstile"
turnstileSiteKey: "Clave del sitio"
turnstileSecretKey: "Clave secreta"
avoidMultiCaptchaConfirm: "El uso de múltiples Captchas puede causar interferencia. ¿Desea desactivar el otro Captcha? Puede dejar múltiples Captchas habilitadas presionando cancelar."
antennas: "Antenas"
manageAntennas: "Administrar antenas"
@@ -388,7 +381,6 @@ administrator: "Administrador"
token: "Token"
twoStepAuthentication: "Autenticación de dos factores"
moderator: "Moderador"
moderation: "Moderación"
nUsersMentioned: "{n} usuarios mencionados"
securityKey: "Clave de seguridad"
securityKeyName: "Nombre de la Clave"
@@ -455,6 +447,7 @@ language: "Idioma"
uiLanguage: "Idioma de visualización de la interfaz"
groupInvited: "Invitado al grupo"
aboutX: "Acerca de {x}"
useOsNativeEmojis: "Usa los emojis nativos de la plataforma"
disableDrawer: "No mostrar los menús en cajones"
youHaveNoGroups: "Sin grupos"
joinOrCreateGroup: "Obtenga una invitación para unirse al grupos o puede crear su propio grupo."
@@ -507,7 +500,6 @@ deleteAll: "Eliminar todos"
showFixedPostForm: "Mostrar el formulario de las entradas encima de la línea de tiempo"
newNoteRecived: "Tienes una nota nuevo"
sounds: "Sonidos"
sound: "Sonidos"
listen: "Escuchar"
none: "Ninguna"
showInPage: "Mostrar en la página"
@@ -567,7 +559,6 @@ author: "Autor"
leaveConfirm: "Hay modificaciones sin guardar. ¿Desea descartarlas?"
manage: "Administrar"
plugins: "Plugins"
preferencesBackups: "Respaldo de preferencias"
deck: "Deck"
undeck: "Quitar deck"
useBlurEffectForModal: "Usar efecto borroso en modales"
@@ -863,9 +854,6 @@ noEmailServerWarning: "No se ha configurado un servidor de correo electrónico."
thereIsUnresolvedAbuseReportWarning: "Hay reportes sin resolver"
recommended: "Recomendado"
check: "Verificar"
driveCapOverrideLabel: "Cambiar la capacidad de la unidad para este usuario"
driveCapOverrideCaption: "Restablecer la capacidad a su predeterminado ingresando un valor de 0 o menos"
requireAdminForView: "Necesitas iniciar sesión como administrador para ver esto."
isSystemAccount: "Cuenta creada y operada automáticamente por el sistema"
typeToConfirm: "Ingrese {x} para confirmar"
deleteAccount: "Borrar cuenta"
@@ -873,39 +861,11 @@ document: "Documento"
numberOfPageCache: "Cantidad de páginas cacheadas"
numberOfPageCacheDescription: "Al aumentar el número mejora la conveniencia pero tambien puede aumentar la carga y la memoria a usarse"
logoutConfirm: "¿Cerrar sesión?"
lastActiveDate: "Utilizado por última vez el"
statusbar: "Barra de estado"
pleaseSelect: "Selecciona una opción"
reverse: "Echar de un capirotazo"
colored: "Color"
refreshInterval: "Intervalo de actualización"
label: "Etiqueta"
type: "Tipo"
speed: "Velocidad"
slow: "Lento"
fast: "Rápido"
sensitiveMediaDetection: "Detección de contenido NSFW"
localOnly: "Solo local"
remoteOnly: "Sólo remoto"
failedToUpload: "La subida falló"
cannotUploadBecauseInappropriate: "Este archivo no se puede subir debido a que algunas partes han sido detectadas comoNSFW."
cannotUploadBecauseNoFreeSpace: "La subida falló debido a falta de espacio libre en la unidad del usuario."
beta: "Beta"
enableAutoSensitive: "Marcar automáticamente contenido NSFW"
enableAutoSensitiveDescription: "Permite la detección y marcado automático de contenido NSFW usando 'Machine Learning' cuando sea posible. Incluso si esta opción está desactivada, puede ser activado para toda la instancia."
activeEmailValidationDescription: "Habilita la validación estricta de direcciones de correo electrónico, lo cual incluye la revisión de direcciones desechables y si se puede comunicar con éstas. Cuando está deshabilitado, sólo el formato de la dirección es validado."
navbar: "Barra de navegación"
shuffle: "Aleatorio"
account: "Cuentas"
move: "Mover"
_sensitiveMediaDetection:
description: "Reduce el esfuerzo de la moderación el el servidor a través del reconocimiento automático de contenido NSFW usando 'Machine Learning'. Esto puede incrementar ligeramente la carga en el servidor."
sensitivity: "Sensibilidad de detección"
sensitivityDescription: "Reducir la sensibilidad puede acarrear a varios falsos positivos, mientras que incrementarla puede reducir las detecciones (falsos negativos)."
setSensitiveFlagAutomatically: "Marcar como NSFW"
setSensitiveFlagAutomaticallyDescription: "Los resultados de la detección interna pueden ser retenidos incluso si la opción está desactivada."
analyzeVideos: "Habilitar el análisis de videos"
analyzeVideosDescription: "Analizar videos en adición a las imágenes. Esto puede incrementar ligeramente la carga del servidor."
_emailUnavailable:
used: "Ya fue usado"
format: "Formato no válido."
@@ -948,24 +908,6 @@ _plugin:
install: "Instalar plugins"
installWarn: "Por favor no instale plugins que no son de confianza"
manage: "Gestionar plugins"
_preferencesBackups:
list: "Respaldos creados"
saveNew: "Guardar nuevo respaldo"
loadFile: "Cargar desde archivo"
apply: "Aplicar a este dispositivo"
save: "Guardar cambios"
inputName: "Por favor, ingresa un nombre para este respaldo"
cannotSave: "Fallo al guardar"
nameAlreadyExists: "Un respaldo llamado \"{name}\" ya existe. Por favor ingresa un nombre diferente"
applyConfirm: "¿Realmente quieres aplicar los cambios desde el archivo \"{name}\" a este dispositivo? Las configuraciones existentes serán sobreescritas. "
saveConfirm: "¿Guardar respaldo como \"{name}\"?"
deleteConfirm: "¿Borrar el respaldo \"{name}\"?"
renameConfirm: "¿Renombrar este respaldo de \"{old}\" a \"{new}\"?"
noBackups: "No existen respaldos. Deberás respaldar las configuraciones del cliente en este servidor usando \"Crear nuevo respaldo\""
createdAt: "Creado: {date} {time}"
updatedAt: "Actualizado: {date} {time}"
cannotLoad: "La carga falló"
invalidFile: "Formato de archivo inválido"
_registry:
scope: "Alcance"
key: "Clave"
@@ -1049,8 +991,6 @@ _mfm:
sparkleDescription: "Aplica un efecto de partículas parpadeantes"
rotate: "Rotar"
rotateDescription: "Rota el contenido a un ángulo especificado."
plain: "Plano"
plainDescription: "Desactiva los efectos de todo el contenido MFM con este efecto MFM."
_instanceTicker:
none: "No mostrar"
remote: "Mostrar a usuarios remotos"
@@ -1280,11 +1220,9 @@ _widgets:
trends: "Tendencias"
clock: "Reloj"
rss: "Lector RSS"
rssTicker: "Ticker-RSS"
activity: "Actividad"
photos: "Fotos"
digitalClock: "Reloj digital"
unixClock: "Reloj UNIX"
federation: "Federación"
instanceCloud: "Nube de palabras de la instancia"
postForm: "Formulario"
@@ -1295,8 +1233,6 @@ _widgets:
serverMetric: "Estadísticas del servidor"
aiscript: "Consola de AiScript"
aichan: "indigo"
_userList:
chooseList: "Seleccione una lista"
_cw:
hide: "Ocultar"
show: "Ver más"
@@ -1432,6 +1368,8 @@ _pages:
eyeCatchingImageRemove: "Borrar imagen llamativa"
chooseBlock: "Agregar bloque"
selectType: "Elegir tipo"
enterVariableName: "Ingrese el nombre de la variable"
variableNameIsAlreadyUsed: "El nombre de la variable ya está en uso"
contentBlocks: "Contenido"
inputBlocks: "Entrada"
specialBlocks: "Especial"
@@ -1441,11 +1379,249 @@ _pages:
section: "Sección"
image: "Imagen"
button: "Botón"
if: "si"
_if:
variable: "Variable"
post: "Formulario"
_post:
text: "Contenido"
attachCanvasImage: "Nota con lienzo como imagen"
canvasId: "Lienzo ID"
textInput: "Entrada de texto"
_textInput:
name: "Nombre de variable"
text: "Título"
default: "Valor predeterminado"
textareaInput: "Entrada de texto en múltiples lineas"
_textareaInput:
name: "Nombre de variable"
text: "Título"
default: "Valor predeterminado"
numberInput: "Entrada numérica"
_numberInput:
name: "Nombre de variable"
text: "Título"
default: "Valor predeterminado"
canvas: "Lienzo"
_canvas:
id: "Lienzo ID"
width: "Ancho"
height: "Altura"
note: "Nota embebida"
_note:
id: "Id de la nota"
idDescription: "Pega la URL de la nota para configurarla"
detailed: "Ver Detalles"
switch: "Interruptor"
_switch:
name: "Nombre de variable"
text: "Título"
default: "Valor predeterminado"
counter: "Contador"
_counter:
name: "Nombre de variable"
text: "Título"
inc: "Aumentar cantidad"
_button:
text: "Título"
colored: "Color"
action: "Acción al presionar el botón"
_action:
dialog: "Mostrar cuadro de diálogo"
_dialog:
content: "Contenido"
resetRandom: "Resetear número aleatorio"
pushEvent: "Enviar evento"
_pushEvent:
event: "Nombre del evento"
message: "Mensaje mostrado al apretar"
variable: "Variable a enviar"
no-variable: "Ninguna"
callAiScript: "Invocar AiScript"
_callAiScript:
functionName: "Nombre de la función"
radioButton: "Botón de opción"
_radioButton:
name: "Nombre de variable"
title: "Título"
values: "Opciones separadas por una nueva linea"
default: "Valor predeterminado"
script:
categories:
flow: "Control de flujo"
logical: "Operación lógica"
operation: "Cálculo"
comparison: "Comparar"
random: "Aleatorio"
value: "Valores"
fn: "funciones"
text: "Manejo de texto"
convert: "Conversion"
list: "Listas"
blocks:
text: "Texto"
multiLineText: "Texto (multilinea)"
textList: "Lista de texto"
_textList:
info: "Separe cada texto con una linea nueva"
strLen: "Largo del texto"
_strLen:
arg1: "Texto"
strPick: "Extraer caracteres"
_strPick:
arg1: "Texto"
arg2: "Posición del caracter"
strReplace: "Sustituir texto"
_strReplace:
arg1: "Texto"
arg2: "Texto a reemplazar"
arg3: "Texto reemplazado"
strReverse: "Invertir texto"
_strReverse:
arg1: "Texto"
join: "Concatenar texto"
_join:
arg1: "Listas"
arg2: "Separador"
add: "Suma"
_add:
arg1: "A"
arg2: "B"
subtract: "Resta"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Multiplicación"
_multiply:
arg1: "A"
arg2: "B"
divide: "División"
_divide:
arg1: "A"
arg2: "B"
mod: "Resto"
_mod:
arg1: "A"
arg2: "B"
round: "Redondear decimales"
_round:
arg1: "Número"
eq: "A y B son iguales"
_eq:
arg1: "A"
arg2: "B"
notEq: "A y B son distintos"
_notEq:
arg1: "A"
arg2: "B"
and: "A y B"
_and:
arg1: "A"
arg2: "B"
or: "A o B"
_or:
arg1: "A"
arg2: "B"
lt: "< A es menor que B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A es mayor que B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A es menor o igual que B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A es mayor o igual que B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Si"
_if:
arg1: "si"
arg2: "Entonces"
arg3: "Si no"
not: "Negación"
_not:
arg1: "Negación"
random: "Aleatorio"
_random:
arg1: "probabilidad"
rannum: "Número aleatorio"
_rannum:
arg1: "Mínimo"
arg2: "Máximo"
randomPick: "Elegir aleatoriamente de la lista"
_randomPick:
arg1: "Listas"
dailyRandom: "Aleatorio (Diariamente para cada usuario)"
_dailyRandom:
arg1: "probabilidad"
dailyRannum: "Número aleatorio (Diariamente para cada usuario)"
_dailyRannum:
arg1: "Mínimo"
arg2: "Máximo"
dailyRandomPick: "Elegir aleatoriamente de la lista (Diariamente para cada usuario)"
_dailyRandomPick:
arg1: "Listas"
seedRandom: "Aleatorio (semilla)"
_seedRandom:
arg1: "Semilla"
arg2: "probabilidad"
seedRannum: "Número aleatorio (semilla)"
_seedRannum:
arg1: "Semilla"
arg2: "Mínimo"
arg3: "Máximo"
seedRandomPick: "Elegir aleatoriamente de la lista (semilla)"
_seedRandomPick:
arg1: "Semilla"
arg2: "Listas"
DRPWPM: "Elegir aleatoriamente de la lista ponderada (Diariamente para cada usuario)"
_DRPWPM:
arg1: "Lista de texto"
pick: "Elegir de la lista"
_pick:
arg1: "Listas"
arg2: "Posición"
listLen: "Obtener largo de la lista"
_listLen:
arg1: "Listas"
number: "Número"
stringToNumber: "De texto a número"
_stringToNumber:
arg1: "Texto"
numberToString: "De número a texto"
_numberToString:
arg1: "Número"
splitStrByLine: "Separar texto en lineas"
_splitStrByLine:
arg1: "Texto"
ref: "Variables"
aiScriptVar: "Variable de AiScript"
fn: "funciones"
_fn:
slots: "Slots"
slots-info: "Separe cada uno de los slots con una linea nueva"
arg1: "Salida"
for: "Repetir"
_for:
arg1: "Cantidad de repeticiones"
arg2: "Acción"
typeError: "El slot {slot} acepta el tipo {expect} pero fue ingresado el tipo {actual}"
thereIsEmptySlot: "El slot {slot} está vacío"
types:
string: "Texto"
number: "Número"
boolean: "Booleano"
array: "Listas"
stringArray: "Lista de texto"
emptySlot: "Slot vacío"
enviromentVariables: "Variables de entorno"
pageVariables: "Items de la página"
argVariables: "Slot de entrada"
_relayStatus:
requesting: "Pendiente"
accepted: "Aceptar"
@@ -1487,7 +1663,6 @@ _deck:
alwaysShowMainColumn: "Siempre mostrar la columna principal"
columnAlign: "Alinear columnas"
addColumn: "Agregar columna"
configureColumn: "Ajustes de columna"
swapLeft: "Mover a la izquierda"
swapRight: "Mover a la derecha"
swapUp: "Mover arriba"
@@ -1495,11 +1670,6 @@ _deck:
stackLeft: "Apilar a la izquierda"
popRight: "Sacar a la derecha"
profile: "Perfil"
newProfile: "Nuevo perfil"
deleteProfile: "Eliminar perfil"
introduction: "¡Crea la interfaz perfecta para tí organizando las columnas libremente!"
introduction2: "Presiona en la + de la derecha de la pantalla para añadir nuevas columnas donde quieras."
widgetsIntroduction: "Por favor selecciona \"Editar Widgets\" en el menú columna y agrega un widget."
_columns:
main: "Principal"
widgets: "Widgets"

View File

@@ -52,7 +52,6 @@ searchUser: "Chercher un·e utilisateur·rice"
reply: "Répondre"
loadMore: "Afficher plus …"
showMore: "Afficher plus …"
showLess: "Fermer"
youGotNewFollower: "Vous suit"
receiveFollowRequest: "Demande dabonnement reçue"
followRequestAccepted: "La demande dabonnement a été acceptée"
@@ -203,7 +202,6 @@ done: "Terminé"
processing: "Traitement en cours"
preview: "Aperçu"
default: "Par défaut"
defaultValueIs: "Par défaut : {value}"
noCustomEmojis: "Il n'y a pas démoji"
noJobs: "Il ny a aucune tâche planifiée"
federating: "En cours de fédération"
@@ -239,7 +237,6 @@ saved: "Enregistré"
messaging: "Discuter"
upload: "Téléverser"
keepOriginalUploading: "Garder limage dorigine"
keepOriginalUploadingDescription: "Conserve la version originale lors du téléchargement d'images. S'il est désactivé, le navigateur génère l'image pour la publication web lors du téléchargement."
fromDrive: "Depuis le Drive"
fromUrl: "Depuis une URL"
uploadFromUrl: "Téléverser via une URL"
@@ -347,10 +344,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Activer reCAPTCHA"
recaptchaSiteKey: "Clé du site"
recaptchaSecretKey: "Clé secrète"
turnstile: "Tourniquet"
enableTurnstile: "Activer le tourniquet"
turnstileSiteKey: "Clé du site"
turnstileSecretKey: "Clé secrète"
avoidMultiCaptchaConfirm: "Lutilisation de plusieurs Captchas peut provoquer des interférences. Souhaitez-vous désactiver lautre Captcha ? Vous pouvez laisser plusieurs Captcha activés en appuyant sur Annuler."
antennas: "Antennes"
manageAntennas: "Gestion des antennes"
@@ -386,7 +379,6 @@ administrator: "Administrateur"
token: "Jeton"
twoStepAuthentication: "Authentification à deux facteurs"
moderator: "Modérateur·rice·s"
moderation: "Modérations"
nUsersMentioned: "{n} utilisateur·rice·s mentionné·e·s"
securityKey: "Clé de sécurité"
securityKeyName: "Nom de la clé"
@@ -453,7 +445,7 @@ language: "Langue"
uiLanguage: "Langue daffichage de linterface"
groupInvited: "Invité au groupe"
aboutX: "À propos de {x}"
disableDrawer: "Les menus ne s'affichent pas dans le tiroir"
useOsNativeEmojis: "Utiliser les émojis natifs du système"
youHaveNoGroups: "Vous navez aucun groupe"
joinOrCreateGroup: "Vous pouvez être invité·e à rejoindre des groupes existants ou créer votre propre nouveau groupe."
noHistory: "Pas d'historique"
@@ -505,7 +497,6 @@ deleteAll: "Supprimer tout"
showFixedPostForm: "Afficher le formulaire de publication en haut du fil d'actualité"
newNoteRecived: "Voir les nouvelles notes"
sounds: "Sons"
sound: "Sons"
listen: "Écouter"
none: "Rien"
showInPage: "Afficher dans la page"
@@ -565,7 +556,6 @@ author: "Auteur·rice"
leaveConfirm: "Vous avez des modifications non-sauvegardées. Voulez-vous les ignorer ?"
manage: "Gestion"
plugins: "Extensions"
preferencesBackups: "Sauvegarder les paramètres"
deck: "Deck"
undeck: "Quitter le deck"
useBlurEffectForModal: "Utiliser un effet de flou pour les modals"
@@ -632,7 +622,6 @@ reporter: "Signalé par"
reporteeOrigin: "Origine du signalement"
reporterOrigin: "Signalé par"
forwardReport: "Transférer le signalement à linstance distante"
forwardReportIsAnonymous: "L'instance distante ne sera pas en mesure de voir vos informations et apparaîtra comme un compte anonyme du système."
send: "Envoyer"
abuseMarkAsResolved: "Marquer le signalement comme résolu"
openInNewTab: "Ouvrir dans un nouvel onglet"
@@ -846,59 +835,15 @@ tenMinutes: "10 minutes"
oneHour: "1 heure"
oneDay: "1 jour"
oneWeek: "1 semaine"
reflectMayTakeTime: "Cela peut prendre un certain temps avant que cela ne se termine."
failedToFetchAccountInformation: "Impossible de récupérer les informations du compte."
rateLimitExceeded: "Limite de taux dépassée"
cropImage: "Recadrer l'image"
cropImageAsk: "Voulez-vous recadrer cette image ?"
file: "Fichiers"
noEmailServerWarning: "Serveur de courrier non configuré."
thereIsUnresolvedAbuseReportWarning: "Il ny a aucun rapport non résolu."
recommended: "Recommandé"
check: "Vérifier"
driveCapOverrideLabel: "Modifier la capacité de stockage du drive de cet·te utilisateur·rice"
driveCapOverrideCaption: "Si une valeur inférieure à 0 est spécifiée, elle est annulée."
requireAdminForView: "Vous devez être connecté avec un compte administrateur pour les visualiser."
isSystemAccount: "Ces comptes sont automatiquement créés et gérés par le système."
typeToConfirm: "Pour effectuer cette opération, tapez {x}"
deleteAccount: "Supprimer le compte"
document: "Documentation"
numberOfPageCache: "Nombre de pages en cache"
numberOfPageCacheDescription: "Plus de confort, mais aussi plus de poids et d'utilisation de la mémoire."
logoutConfirm: "Se déconnecter?"
lastActiveDate: "Dernière utilisation"
statusbar: "Barre détat"
pleaseSelect: "Choisir une option"
reverse: "Inverser"
colored: "Coloré"
refreshInterval: "Intervalle de mise à jour"
label: "Étiquette"
type: "Type"
speed: "Vitesse"
slow: "Lente"
fast: "Rapide"
sensitiveMediaDetection: "Détection des médias sensibles"
localOnly: "Local seulement"
remoteOnly: "Distant uniquement"
failedToUpload: "Échec du transfert"
cannotUploadBecauseInappropriate: "Impossible de télécharger le document car il a été déterminé qu'il pouvait contenir un contenu inapproprié."
cannotUploadBecauseNoFreeSpace: "Impossible de télécharger en raison d'un manque d'espace libre sur le disque.\n"
beta: "Bêta"
enableAutoSensitive: "Détermination automatique de NSFW"
enableAutoSensitiveDescription: "S'il est disponible, le drapeau NSFW est automatiquement défini sur le média en utilisant l'apprentissage automatique. Même si cette fonction est désactivée, elle peut être réglée automatiquement dans certains cas."
activeEmailValidationDescription: "Valide l'adresse électronique d'un utilisateur de manière plus agressive en déterminant s'il s'agit d'une adresse électronique jetable et si l'on peut effectivement communiquer avec elle. Si cette option n'est pas cochée, l'adresse électronique n'est vérifiée que sous forme de chaîne de caractères."
navbar: "Barre de navigation"
shuffle: "Lecture aléatoire"
account: "Comptes"
move: "Déplacer"
_sensitiveMediaDetection:
description: "L'apprentissage automatique peut être utilisé pour détecter automatiquement les médias sensibles à modérer. La sollicitation des serveurs augmente légèrement."
sensitivity: "Sensibilité de la détection"
sensitivityDescription: "Une sensibilité plus faible réduit les faux positifs. Une sensibilité plus élevée réduit les omissions (faux négatifs)."
setSensitiveFlagAutomatically: "Définir le drapeau NSFW."
setSensitiveFlagAutomaticallyDescription: "Même si ce paramètre est désactivé, le résultat de la décision est conservé en interne."
analyzeVideos: "Activer lanalyse de vidéos"
analyzeVideosDescription: "Veillez à ce que les vidéos soient analysées en plus des images fixes. La sollicitation des serveurs augmentera légèrement."
_emailUnavailable:
used: "Non disponible"
format: "Le format de cette adresse de courriel est invalide"
@@ -941,24 +886,6 @@ _plugin:
install: "Installation de plugin"
installWarn: "Ninstallez que des extensions provenant de sources de confiance."
manage: "Gestion des plugins"
_preferencesBackups:
list: "Sauvegardes créées"
saveNew: "Nouvelle sauvegarde"
loadFile: "Importer à partir dun fichier"
apply: "Appliquer à cet appareil"
save: "Sauvegarder les changements"
inputName: "Entrez le nom de la sauvegarde"
cannotSave: "Impossible de sauvegarder"
nameAlreadyExists: "Le nom de sauvegarde \"{name}\" existe déjà. Veuillez spécifier un autre nom."
applyConfirm: "Voulez-vous appliquer la sauvegarde '{name}' au dispositif actuel ? La configuration actuelle de l'appareil sera perdue."
saveConfirm: "Voulez-vous écraser {name} ?"
deleteConfirm: "Voulez-vous supprimer {name} ?"
renameConfirm: "Voulez-vous remplacer \"{old}\" par \"{new}\" ?"
noBackups: "Aucune sauvegarde n'est disponible. L'option \"Nouvelle sauvegarde\" vous permet de sauvegarder la configuration actuelle du client sur le serveur."
createdAt: "Créé : {date} {time}"
updatedAt: "Mis à jour : {date} {time}"
cannotLoad: "Échec du chargement"
invalidFile: "Format de fichier non valide"
_registry:
scope: "Portée"
key: "Clé "
@@ -1041,9 +968,6 @@ _mfm:
sparkle: "Paillettes"
sparkleDescription: "Ajoute un effet scintillant au contenu."
rotate: "Pivoter"
rotateDescription: "Faire pivoter à un angle spécifié."
plain: "Vu texte non formaté"
plainDescription: "Désactive toute la syntaxe interne."
_instanceTicker:
none: "Cacher "
remote: "Montrer pour les utilisateur·ice·s distant·e·s"
@@ -1077,7 +1001,6 @@ _wordMute:
hard: "Strict"
mutedNotes: "Notes filtrées"
_instanceMute:
instanceMuteDescription: "Met en sourdine toutes les notes et renotes de l'instance configurée, y compris les réponses aux utilisateurs de l'instance muette."
instanceMuteDescription2: "Séparer avec de nouvelles lignes"
title: "Masque les notes venant des instances listées."
heading: "Instances à mettre en sourdine"
@@ -1273,11 +1196,9 @@ _widgets:
trends: "Tendances"
clock: "Horloge"
rss: "Lecteur de flux RSS"
rssTicker: "Filtre RSS"
activity: "Activité"
photos: "Photos"
digitalClock: "Horloge numérique"
unixClock: "Horloge UNIX"
federation: "Fédération"
postForm: "Formulaire de publication"
slideshow: "Diaporama"
@@ -1287,8 +1208,6 @@ _widgets:
serverMetric: "Statistiques du serveur"
aiscript: "Console AiScript"
aichan: "Ai"
_userList:
chooseList: "Sélectionner une liste"
_cw:
hide: "Masquer"
show: "Afficher plus …"
@@ -1424,6 +1343,8 @@ _pages:
eyeCatchingImageRemove: "Supprimer l'image attractive"
chooseBlock: "Ajouter un bloc"
selectType: "Choisir un type"
enterVariableName: "Veuillez entrer un nom pour votre variable"
variableNameIsAlreadyUsed: "Ce nom de variable est déjà utilisé"
contentBlocks: "Contenu"
inputBlocks: "Blocs d'entrée"
specialBlocks: "Spécial"
@@ -1433,11 +1354,249 @@ _pages:
section: "Section"
image: "Images"
button: "Bouton"
if: "Si"
_if:
variable: "Variables"
post: "Formulaire de publication"
_post:
text: "Contenu"
attachCanvasImage: "Publier avec Toile comme image"
canvasId: "Toile ID"
textInput: "Entrée textuelle"
_textInput:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
textareaInput: "Entrée textuelle multi-ligne"
_textareaInput:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
numberInput: "Entrée numérique"
_numberInput:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
canvas: "Toile"
_canvas:
id: "Toile ID"
width: "Largeur"
height: "Hauteur"
note: "Note intégrée"
_note:
id: "Identifiant de la note"
idDescription: "Pour configurer la note, vous pouvez aussi coller ici l'URL correspondante."
detailed: "Afficher les détails"
switch: "Interrupteur"
_switch:
name: "Nom de la variable"
text: "Titre"
default: "Valeur par défaut"
counter: "Compteur"
_counter:
name: "Nom de la variable"
text: "Titre"
inc: "Augmenter de"
_button:
text: "Titre"
colored: "Coloré"
action: "Opération à effectuer lorsque le bouton est pressé"
_action:
dialog: "Afficher une fenêtre de dialogue"
_dialog:
content: "Contenu"
resetRandom: "Réinitialiser un nombre aléatoire"
pushEvent: "Envoyer un évènement"
_pushEvent:
event: "Nom de lévènement"
message: "Message à afficher lorsquil est activé"
variable: "Variable à envoyer"
no-variable: "Rien"
callAiScript: "Appeler AiScript"
_callAiScript:
functionName: "Nom de la fonction"
radioButton: "Choix"
_radioButton:
name: "Nom de la variable"
title: "Titre"
values: "Liste des choix (un par ligne)"
default: "Valeur par défaut"
script:
categories:
flow: "Contrôle"
logical: "Opération logique"
operation: "Calculer"
comparison: "Comparer"
random: "Aléatoire"
value: "Valeur"
fn: "Fonction"
text: "Manipulation de texte"
convert: "Convertir"
list: "Listes"
blocks:
text: "Texte"
multiLineText: "Texte (multi-ligne)"
textList: "Liste de texte"
_textList:
info: "Veuillez séparer chaque entrée avec un saut de ligne"
strLen: "Longueur du texte"
_strLen:
arg1: "Texte"
strPick: "Extraire un caractère"
_strPick:
arg1: "Texte"
arg2: "Position du joueur"
strReplace: "Remplacement de texte"
_strReplace:
arg1: "Texte"
arg2: "Avant le remplacement"
arg3: "Après le remplacement"
strReverse: "Inverser le texte"
_strReverse:
arg1: "Texte"
join: "Concaténer du texte"
_join:
arg1: "Listes"
arg2: "Séparateur"
add: "Ajouter"
_add:
arg1: "A"
arg2: "B"
subtract: "Soustraire"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Multiplier par"
_multiply:
arg1: "A"
arg2: "B"
divide: "Diviser par"
_divide:
arg1: "A"
arg2: "B"
mod: "Reste"
_mod:
arg1: "A"
arg2: "B"
round: "Arrondir les décimales"
_round:
arg1: "Numérique"
eq: "A et B sont égaux"
_eq:
arg1: "A"
arg2: "B"
notEq: "A et B sont différents"
_notEq:
arg1: "A"
arg2: "B"
and: "A et B"
_and:
arg1: "A"
arg2: "B"
or: "A ou B"
_or:
arg1: "A"
arg2: "B"
lt: "A est inférieur à B"
_lt:
arg1: "A"
arg2: "B"
gt: "A est supérieur à B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "A est inférieur ou égal à B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: "A est supérieur ou égal à B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Branche"
_if:
arg1: "Si"
arg2: "Si"
arg3: "Sinon"
not: "Nier"
_not:
arg1: "Nier"
random: "Aléatoire"
_random:
arg1: "Probabilité"
rannum: "Nombre aléatoire"
_rannum:
arg1: "Minimum"
arg2: "Maximum"
randomPick: "Sélectionner au hasard dans la liste"
_randomPick:
arg1: "Listes"
dailyRandom: "Aléatoire (Quotidien pour chaque utilisateur)"
_dailyRandom:
arg1: "Probabilité"
dailyRannum: "Numéros aléatoires (Quotidien pour chaque utilisateur)"
_dailyRannum:
arg1: "Minimum"
arg2: "Maximum"
dailyRandomPick: "Sélectionné au hasard dans la liste (Quotidien pour chaque utilisateur)"
_dailyRandomPick:
arg1: "Listes"
seedRandom: "Aléatoire (graine)"
_seedRandom:
arg1: "Graine"
arg2: "Probabilité"
seedRannum: "Nombre aléatoire (Graine)"
_seedRannum:
arg1: "Graine"
arg2: "Minimum"
arg3: "Maximum"
seedRandomPick: "Sélectionné au hasard dans la liste (graine)"
_seedRandomPick:
arg1: "Graine"
arg2: "Listes"
DRPWPM: "Sélectionné au hasard dans une liste de probabilités (Quotidien pour chaque utilisateur)"
_DRPWPM:
arg1: "Liste de texte"
pick: "Sélectionner dans la liste"
_pick:
arg1: "Listes"
arg2: "Position"
listLen: "Longueur de la liste"
_listLen:
arg1: "Listes"
number: "Numérique"
stringToNumber: "Convertir du texte en numérique"
_stringToNumber:
arg1: "Texte"
numberToString: "Convertir du numérique en texte"
_numberToString:
arg1: "Numérique"
splitStrByLine: "Séparer le texte par des sauts de lignes"
_splitStrByLine:
arg1: "Texte"
ref: "Variables"
aiScriptVar: "Variable d'AiScript"
fn: "Fonction"
_fn:
slots: "Slots"
slots-info: "Veuillez insérer un seul slot par ligne"
arg1: "Sortie"
for: "Répéter"
_for:
arg1: "Compter"
arg2: "Action"
typeError: "Le slot {slot} accepte \"{expect}\" mais a \"{actual}\" !"
thereIsEmptySlot: "Slot {slot} est vide !"
types:
string: "Texte"
number: "Numérique"
boolean: "Marqueur"
array: "Listes"
stringArray: "Liste de texte"
emptySlot: "Slot vide"
enviromentVariables: "Variables d'environnement"
pageVariables: "Élément de page"
argVariables: "Entrée slot"
_relayStatus:
requesting: "En attente"
accepted: "Accepté"
@@ -1466,7 +1625,6 @@ _notification:
quote: "Citations"
reaction: "Réactions"
pollVote: "Votes dans des sondages"
pollEnded: "Sondages se cloturant"
receiveFollowRequest: "Demande d'abonnement reçue"
followRequestAccepted: "Demande d'abonnement acceptée"
groupInvited: "Invitation à un groupe"
@@ -1479,7 +1637,6 @@ _deck:
alwaysShowMainColumn: "Toujours afficher la colonne principale"
columnAlign: "Aligner les colonnes"
addColumn: "Ajouter une colonne"
configureColumn: "Configuration de la colonne"
swapLeft: "Déplacer à gauche"
swapRight: "Déplacer à droite"
swapUp: "Déplacer vers le haut"
@@ -1487,9 +1644,6 @@ _deck:
stackLeft: "Empiler à gauche"
popRight: "Extraire à droite"
profile: "Profil"
newProfile: "Nouveau profil"
deleteProfile: "Supprimer le profil"
introduction: "Créez linterface parfaite qui vous sied en arrangeant librement les colonnes !"
_columns:
main: "Principale"
widgets: "Widgets"

View File

@@ -52,7 +52,6 @@ searchUser: "Cari pengguna"
reply: "Balas"
loadMore: "Selebihnya"
showMore: "Selebihnya"
showLess: "Tutup"
youGotNewFollower: "Mengikuti kamu"
receiveFollowRequest: "Ingin mengikuti kamu"
followRequestAccepted: "Permintaan mengikuti telah disetujui"
@@ -347,8 +346,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Nyalakan reCAPTCHA"
recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret Key"
turnstileSiteKey: "Site key"
turnstileSecretKey: "Secret Key"
avoidMultiCaptchaConfirm: "Menggunakan banyak Captcha dapat menyebabkan gangguan. Apakah kamu ingin untuk menonaktifkan Captcha yang lain? Kamu dapat membiarkan fitur ini tetap aktif dengan menekan tombol batal."
antennas: "Antena"
manageAntennas: "Pengelola Antena"
@@ -450,6 +447,7 @@ language: "Bahasa"
uiLanguage: "Bahasa antarmuka pengguna"
groupInvited: "Telah diundang ke grup"
aboutX: "Tentang {x}"
useOsNativeEmojis: "Gunakan Emoji bawaan sistem operasi"
disableDrawer: "Jangan gunakan menu bergaya laci"
youHaveNoGroups: "Kamu tidak memiliki grup"
joinOrCreateGroup: "Bergabunglah dengan grup atau kamu dapat membuat grupmu sendiri."
@@ -502,7 +500,6 @@ deleteAll: "Hapus semua"
showFixedPostForm: "Tampilkan form posting di atas linimasa."
newNoteRecived: "Kamu mendapat catatan baru"
sounds: "Bunyi"
sound: "Bunyi"
listen: "Dengarkan"
none: "Tidak ada"
showInPage: "Tampilkan di halaman"
@@ -1222,8 +1219,6 @@ _widgets:
serverMetric: "Statistik peladen"
aiscript: "Konsol AiScript"
aichan: "Ai"
_userList:
chooseList: "Pilih daftar"
_cw:
hide: "Sembunyikan"
show: "Lihat konten"
@@ -1359,6 +1354,8 @@ _pages:
eyeCatchingImageRemove: "Hapus gambar yang menarik"
chooseBlock: "Tambahkan blokir"
selectType: "Pilih jenis"
enterVariableName: "Mohon masukkan nama untuk variabel kamu"
variableNameIsAlreadyUsed: "Nama ini sudah digunakan oleh variabel lain"
contentBlocks: "Konten"
inputBlocks: "Masukan"
specialBlocks: "Khusus"
@@ -1368,11 +1365,249 @@ _pages:
section: "Bagian"
image: "Gambar"
button: "Tombol"
if: "Jika"
_if:
variable: "Variabel"
post: "Buat catatan"
_post:
text: "Isi"
attachCanvasImage: "Posting dengan kanvas sebagai gambar"
canvasId: "ID Kanvas"
textInput: "Masukan teks"
_textInput:
name: "Nama variabel"
text: "Judul"
default: "Nilai bawaan"
textareaInput: "Masukan teks multibaris"
_textareaInput:
name: "Nama variabel"
text: "Judul"
default: "Nilai bawaan"
numberInput: "Masukan angka"
_numberInput:
name: "Nama variabel"
text: "Judul"
default: "Nilai bawaan"
canvas: "Kanvas"
_canvas:
id: "ID Kanvas"
width: "Lebar"
height: "Tinggi"
note: "Catatan yang ditanam"
_note:
id: "ID Catatan"
idDescription: "Kamu dapat menyetel ini dengan menempelkan tautan URL Catatan."
detailed: "Tampilan rincian"
switch: "Beralih"
_switch:
name: "Nama variabel"
text: "Judul"
default: "Nilai bawaan"
counter: "Penghitung"
_counter:
name: "Nama variabel"
text: "Judul"
inc: "Meningkat dengan"
_button:
text: "Judul"
colored: "Diwarnai"
action: "Operasi akan dimulai ketika tombol ditekan"
_action:
dialog: "Tampilkan dialog"
_dialog:
content: "Isi"
resetRandom: "Atur ulang benih acak"
pushEvent: "Kirim event"
_pushEvent:
event: "Nama event"
message: "Pesan yang tampil ketika diaktifkan"
variable: "Variable untuk kirim"
no-variable: "Tidak ada"
callAiScript: "Panggil AiScript"
_callAiScript:
functionName: "Nama fungsi"
radioButton: "Pilihan"
_radioButton:
name: "Nama variabel"
title: "Judul"
values: "Daftar pilihan (dipisahkan dengan garis baru)"
default: "Nilai bawaan"
script:
categories:
flow: "Arus kendali"
logical: "Operasi logis"
operation: "Menghitung"
comparison: "Membandingkan"
random: "Acak"
value: "Nilai"
fn: "Fungsi"
text: "Operasi teks"
convert: "Mengubah"
list: "Daftar"
blocks:
text: "Teks"
multiLineText: "Teks (multibaris)"
textList: "Daftar teks"
_textList:
info: "Pisahkan setiap entri dengan baris baru"
strLen: "Panjang teks"
_strLen:
arg1: "Teks"
strPick: "Ekstrak karakter"
_strPick:
arg1: "Teks"
arg2: "Lokasi karakter"
strReplace: "Penggantian teks"
_strReplace:
arg1: "Teks"
arg2: "Teks yang akan diganti"
arg3: "Diganti dengan"
strReverse: "Balikkan teks"
_strReverse:
arg1: "Teks"
join: "Rangkaian teks"
_join:
arg1: "Daftar"
arg2: "Pemisah"
add: "Tambah"
_add:
arg1: "A"
arg2: "B"
subtract: "Kurangi"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Kali"
_multiply:
arg1: "A"
arg2: "B"
divide: "Bagi"
_divide:
arg1: "A"
arg2: "B"
mod: "Sisa"
_mod:
arg1: "A"
arg2: "B"
round: "Bulat desimal"
_round:
arg1: "Angka"
eq: "A dan B adalah sama"
_eq:
arg1: "A"
arg2: "B"
notEq: "A dan B adalah berbeda"
_notEq:
arg1: "A"
arg2: "B"
and: "A DAN B"
_and:
arg1: "A"
arg2: "B"
or: "A ATAU B"
_or:
arg1: "A"
arg2: "B"
lt: "< A ikurang dari B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A lebih dari B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A kurang dari sama dengan B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A lebih dari sama dengan B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Cabang"
_if:
arg1: "Jika"
arg2: "Jika benar"
arg3: "Jika salah"
not: "BUKAN"
_not:
arg1: "NOT"
random: "Acak"
_random:
arg1: "Probabilitas"
rannum: "Angka acak"
_rannum:
arg1: "Nilai minimum"
arg2: "Nilai maksimum"
randomPick: "Pilih secara acak dari daftar"
_randomPick:
arg1: "Daftar"
dailyRandom: "Acak (bertahan sehari)"
_dailyRandom:
arg1: "Probabilitas"
dailyRannum: "Angka acak (bertahan sehari)"
_dailyRannum:
arg1: "Nilai minimum"
arg2: "Nilai maksimum"
dailyRandomPick: "Pilih secara acak dari daftar (bertahan sehari)"
_dailyRandomPick:
arg1: "Daftar"
seedRandom: "Acak (dengan seed)"
_seedRandom:
arg1: "Seed"
arg2: "Probabilitas"
seedRannum: "Angka acak (dengan seed)"
_seedRannum:
arg1: "Seed"
arg2: "Nilai minimum"
arg3: "Nilai maksimum"
seedRandomPick: "Pilih secara acak dari daftar (dengan seed)"
_seedRandomPick:
arg1: "Seed"
arg2: "Daftar"
DRPWPM: "Pilih secara acak dari daftar berbobot (bertahan sehari)"
_DRPWPM:
arg1: "Daftar teks"
pick: "Pilih dari daftar"
_pick:
arg1: "Daftar"
arg2: "Posisi"
listLen: "Dapatkan panjangnya dari daftar"
_listLen:
arg1: "Daftar"
number: "Angka"
stringToNumber: "Teks ke angka"
_stringToNumber:
arg1: "Teks"
numberToString: "Angka ke teks"
_numberToString:
arg1: "Angka"
splitStrByLine: "Pisahkan teks dengan baris baru"
_splitStrByLine:
arg1: "Teks"
ref: "Variabel"
aiScriptVar: "Variabel AiScript"
fn: "Fungsi"
_fn:
slots: "Slot"
slots-info: "Pisahkan setiap slot dengan baris baru"
arg1: "Keluaran"
for: "Ulangi"
_for:
arg1: "Jumlah angka untuk diulangi"
arg2: "Aksi"
typeError: "Slot {slot} menerima tipe \"{expect}\", sayangnya nilai yang disediakan adalah \"{actual}\"!"
thereIsEmptySlot: "Slot {slot} kosong!"
types:
string: "Teks"
number: "Angka"
boolean: "Markah"
array: "Daftar"
stringArray: "Daftar teks"
emptySlot: "Slot kosong"
enviromentVariables: "Variabel Lingkungan"
pageVariables: "Elemen halaman"
argVariables: "Masukan slot"
_relayStatus:
requesting: "Menunggu"
accepted: "Disetujui"

View File

@@ -36,7 +36,6 @@ const languages = [
'sk-SK',
'ug-CN',
'uk-UA',
'vi-VN',
'zh-CN',
'zh-TW',
];

File diff suppressed because it is too large Load Diff

View File

@@ -2,7 +2,6 @@ _lang_: "日本語"
headlineMisskey: "ノートでつながるネットワーク"
introMisskey: "ようこそMisskeyは、オープンソースの分散型マイクロブログサービスです。\n「ート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡\n「リアクション」機能で、皆のートに素早く反応を追加することもできます👍\n新しい世界を探検しよう🚀"
poweredByMisskeyDescription: "{name}は、オープンソースのプラットフォーム<b>Misskey</b>を使ったサービス(Misskeyインスタンスと呼ばれます)のひとつです。"
monthAndDay: "{month}月 {day}日"
search: "検索"
notifications: "通知"
@@ -13,7 +12,6 @@ fetchingAsApObject: "連合に照会中"
ok: "OK"
gotIt: "わかった"
cancel: "キャンセル"
noThankYou: "やめておく"
enterUsername: "ユーザー名を入力"
renotedBy: "{user}がRenote"
noNotes: "ノートはありません"
@@ -49,13 +47,11 @@ deleteAndEdit: "削除して編集"
deleteAndEditConfirm: "このートを削除してもう一度編集しますかこのートへのリアクション、Renote、返信も全て削除されます。"
addToList: "リストに追加"
sendMessage: "メッセージを送信"
copyRSS: "RSSをコピー"
copyUsername: "ユーザー名をコピー"
searchUser: "ユーザーを検索"
reply: "返信"
loadMore: "もっと見る"
showMore: "もっと見る"
showLess: "閉じる"
youGotNewFollower: "フォローされました"
receiveFollowRequest: "フォローリクエストされました"
followRequestAccepted: "フォローが承認されました"
@@ -143,8 +139,8 @@ cacheRemoteFiles: "リモートのファイルをキャッシュする"
cacheRemoteFilesDescription: "この設定を無効にすると、リモートファイルをキャッシュせず直リンクするようになります。サーバーのストレージを節約できますが、サムネイルが生成されないので通信量が増加します。"
flagAsBot: "Botとして設定"
flagAsBotDescription: "このアカウントがプログラムによって運用される場合は、このフラグをオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Misskeyのシステム上での扱いがBotに合ったものになります。"
flagAsCat: "にゃああああああああああああああ!!!!!!!!!!!!"
flagAsCatDescription: "にゃにゃにゃ??"
flagAsCat: "Catとして設定"
flagAsCatDescription: "このアカウントが猫であることを示す場合は、このフラグをオンにします。"
flagShowTimelineReplies: "タイムラインにノートへの返信を表示する"
flagShowTimelineRepliesDescription: "オンにすると、タイムラインにユーザーのノート以外にもそのユーザーの他のノートへの返信を表示します。"
autoAcceptFollowed: "フォロー中ユーザーからのフォロリクを自動承認"
@@ -351,10 +347,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHAを有効にする"
recaptchaSiteKey: "サイトキー"
recaptchaSecretKey: "シークレットキー"
turnstile: "Turnstile"
enableTurnstile: "Turnstileを有効にする"
turnstileSiteKey: "サイトキー"
turnstileSecretKey: "シークレットキー"
avoidMultiCaptchaConfirm: "複数のCaptchaを使用すると干渉を起こす可能性があります。他のCaptchaを無効にしますかキャンセルして複数のCaptchaを有効化したままにすることも可能です。"
antennas: "アンテナ"
manageAntennas: "アンテナの管理"
@@ -457,8 +449,7 @@ language: "言語"
uiLanguage: "UIの表示言語"
groupInvited: "グループに招待されました"
aboutX: "{x}について"
emojiStyle: "絵文字のスタイル"
native: "ネイティブ"
useOsNativeEmojis: "OSネイティブの絵文字を使用"
disableDrawer: "メニューをドロワーで表示しない"
youHaveNoGroups: "グループがありません"
joinOrCreateGroup: "既存のグループに招待してもらうか、新しくグループを作成してください。"
@@ -511,7 +502,6 @@ deleteAll: "全て削除"
showFixedPostForm: "タイムライン上部に投稿フォームを表示する"
newNoteRecived: "新しいノートがあります"
sounds: "サウンド"
sound: "サウンド"
listen: "聴く"
none: "なし"
showInPage: "ページで表示"
@@ -571,7 +561,6 @@ author: "作者"
leaveConfirm: "未保存の変更があります。破棄しますか?"
manage: "管理"
plugins: "プラグイン"
preferencesBackups: "設定のバックアップ"
deck: "デッキ"
undeck: "デッキ解除"
useBlurEffectForModal: "モーダルにぼかし効果を使用"
@@ -717,7 +706,6 @@ accentColor: "アクセント"
textColor: "文字"
saveAs: "名前を付けて保存"
advanced: "高度"
advancedSettings: "高度な設定"
value: "値"
createdAt: "作成日時"
updatedAt: "更新日時"
@@ -902,21 +890,6 @@ activeEmailValidationDescription: "ユーザーのメールアドレスのバリ
navbar: "ナビゲーションバー"
shuffle: "シャッフル"
account: "アカウント"
move: "移動"
pushNotification: "プッシュ通知"
subscribePushNotification: "プッシュ通知を有効化"
unsubscribePushNotification: "プッシュ通知を停止する"
pushNotificationAlreadySubscribed: "プッシュ通知は有効です"
pushNotificationNotSupported: "ブラウザかインスタンスがプッシュ通知に非対応"
sendPushNotificationReadMessage: "通知やメッセージが既読になったらプッシュ通知を削除する"
sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」という通知が一瞬表示されるようになります。端末の電池消費量が増加する可能性があります。"
windowMaximize: "最大化"
windowRestore: "元に戻す"
caption: "キャプション"
loggedInAsBot: "Botアカウントでログイン中"
tools: "ツール"
cannotLoad: "読み込めません"
numberOfProfileView: "プロフィール表示回数"
_sensitiveMediaDetection:
description: "機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。"
@@ -978,25 +951,6 @@ _plugin:
installWarn: "信頼できないプラグインはインストールしないでください。"
manage: "プラグインの管理"
_preferencesBackups:
list: "作成したバックアップ"
saveNew: "新規保存"
loadFile: "ファイルを読み込み"
apply: "このデバイスに適用"
save: "上書き保存"
inputName: "バックアップ名を入力"
cannotSave: "保存できません"
nameAlreadyExists: "バックアップ名「{name}」は既に存在します。違う名前を指定してください。"
applyConfirm: "バックアップ「{name}」を現在のデバイスに適用しますか?現在のデバイス設定は失われます。"
saveConfirm: "{name}に上書き保存しますか?"
deleteConfirm: "{name}を削除しますか?"
renameConfirm: "「{old}」を「{new}」に変更しますか?"
noBackups: "バックアップはありません。「新規保存」で現在のクライアント設定をサーバーに保存できます。"
createdAt: "作成日時: {date} {time}"
updatedAt: "更新日時: {date} {time}"
cannotLoad: "読み込みできません"
invalidFile: "ファイル形式が違います。"
_registry:
scope: "スコープ"
key: "キー"
@@ -1083,8 +1037,6 @@ _mfm:
sparkleDescription: "キラキラしたパーティクルのエフェクトを追加します。"
rotate: "回転"
rotateDescription: "指定した角度で回転させます。"
plain: "プレーン"
plainDescription: "内側の構文を全て無効にします。"
_instanceTicker:
none: "表示しない"
@@ -1254,9 +1206,6 @@ _tutorial:
step7_1: "これで、Misskeyの基本的な使い方の説明は終わりました。お疲れ様でした。"
step7_2: "もっとMisskeyについて知りたいときは、{help}を見てみてください。"
step7_3: "では、Misskeyをお楽しみください🚀"
step8_1: "最後に、プッシュ通知を有効化してみませんか?"
step8_2: "プッシュ通知を受け取ることで、Misskeyを開いていない時にもリアクションやフォロー、メンションなどに気づけます。"
step8_3: "通知の設定は後から変更できます。"
_2fa:
alreadyRegistered: "既に設定は完了しています。"
@@ -1339,7 +1288,6 @@ _widgets:
activity: "アクティビティ"
photos: "フォト"
digitalClock: "デジタル時計"
unixClock: "UNIX時計"
federation: "連合"
instanceCloud: "インスタンスクラウド"
postForm: "投稿フォーム"
@@ -1350,9 +1298,6 @@ _widgets:
serverMetric: "サーバーメトリクス"
aiscript: "AiScriptコンソール"
aichan: "藍"
userList: "ユーザーリスト"
_userList:
chooseList: "リストを選択"
_cw:
hide: "隠す"
@@ -1422,7 +1367,6 @@ _profile:
_exportOrImport:
allNotes: "全てのノート"
favoritedNotes: "お気に入りにしたノート"
followingList: "フォロー"
muteList: "ミュート"
blockingList: "ブロック"
@@ -1499,6 +1443,8 @@ _pages:
eyeCatchingImageRemove: "アイキャッチ画像を削除"
chooseBlock: "ブロックを追加"
selectType: "種類を選択"
enterVariableName: "変数名を決めてください"
variableNameIsAlreadyUsed: "その変数名は既に使われています"
contentBlocks: "コンテンツ"
inputBlocks: "入力"
specialBlocks: "特殊"
@@ -1509,12 +1455,261 @@ _pages:
image: "画像"
button: "ボタン"
if: "もし"
_if:
variable: "変数"
post: "投稿フォーム"
_post:
text: "内容"
attachCanvasImage: "キャンバスの画像を添付する"
canvasId: "キャンバスID"
textInput: "テキスト入力"
_textInput:
name: "変数名"
text: "タイトル"
default: "デフォルト値"
textareaInput: "複数行テキスト入力"
_textareaInput:
name: "変数名"
text: "タイトル"
default: "デフォルト値"
numberInput: "数値入力"
_numberInput:
name: "変数名"
text: "タイトル"
default: "デフォルト値"
canvas: "キャンバス"
_canvas:
id: "キャンバスID"
width: "幅"
height: "高さ"
note: "ノート埋め込み"
_note:
id: "ートID"
idDescription: "ートURLをペーストして設定することもできます。"
detailed: "詳細な表示"
switch: "スイッチ"
_switch:
name: "変数名"
text: "タイトル"
default: "デフォルト値"
counter: "カウンター"
_counter:
name: "変数名"
text: "タイトル"
inc: "増加値"
_button:
text: "タイトル"
colored: "色付き"
action: "ボタンを押したときの動作"
_action:
dialog: "ダイアログを表示する"
_dialog:
content: "内容"
resetRandom: "乱数をリセット"
pushEvent: "イベントを送信させる"
_pushEvent:
event: "イベント名"
message: "押したときに表示するメッセージ"
variable: "送信する変数"
no-variable: "なし"
callAiScript: "AiScript呼び出し"
_callAiScript:
functionName: "関数名"
radioButton: "選択肢"
_radioButton:
name: "変数名"
title: "タイトル"
values: "改行で区切った選択肢"
default: "デフォルト値"
script:
categories:
flow: "制御"
logical: "論理演算"
operation: "計算"
comparison: "比較"
random: "ランダム"
value: "値"
fn: "関数"
text: "テキスト操作"
convert: "変換"
list: "リスト"
blocks:
text: "テキスト"
multiLineText: "テキスト(複数行)"
textList: "テキストのリスト"
_textList:
info: "ひとつひとつを改行で区切ってください"
strLen: "テキストの長さ"
_strLen:
arg1: "テキスト"
strPick: "文字取り出し"
_strPick:
arg1: "テキスト"
arg2: "文字の位置"
strReplace: "テキスト置き換え"
_strReplace:
arg1: "テキスト"
arg2: "置き換え前"
arg3: "置き換え後"
strReverse: "テキストを反転"
_strReverse:
arg1: "テキスト"
join: "テキストを連結"
_join:
arg1: "リスト"
arg2: "区切り"
add: "足す"
_add:
arg1: "A"
arg2: "B"
subtract: "引く"
_subtract:
arg1: "A"
arg2: "B"
multiply: "掛ける"
_multiply:
arg1: "A"
arg2: "B"
divide: "割る"
_divide:
arg1: "A"
arg2: "B"
mod: "割った余り"
_mod:
arg1: "A"
arg2: "B"
round: "小数を丸める"
_round:
arg1: "数値"
eq: "AとBが同じ"
_eq:
arg1: "A"
arg2: "B"
notEq: "AとBが異なる"
_notEq:
arg1: "A"
arg2: "B"
and: "AかつB"
_and:
arg1: "A"
arg2: "B"
or: "AまたはB"
_or:
arg1: "A"
arg2: "B"
lt: "< AがBより小さい"
_lt:
arg1: "A"
arg2: "B"
gt: "> AがBより大きい"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= AがBと同じか小さい"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= AがBと同じか大きい"
_gtEq:
arg1: "A"
arg2: "B"
if: "分岐"
_if:
arg1: "もし"
arg2: "なら"
arg3: "そうでなければ"
not: "否定"
_not:
arg1: "否定"
random: "ランダム"
_random:
arg1: "確率"
rannum: "乱数"
_rannum:
arg1: "最小"
arg2: "最大"
randomPick: "リストからランダムに選択"
_randomPick:
arg1: "リスト"
dailyRandom: "ランダム (ユーザーごとに日替わり)"
_dailyRandom:
arg1: "確率"
dailyRannum: "乱数 (ユーザーごとに日替わり)"
_dailyRannum:
arg1: "最小"
arg2: "最大"
dailyRandomPick: "リストからランダムに選択 (ユーザーごとに日替わり)"
_dailyRandomPick:
arg1: "リスト"
seedRandom: "ランダム (シード)"
_seedRandom:
arg1: "シード"
arg2: "確率"
seedRannum: "乱数 (シード)"
_seedRannum:
arg1: "シード"
arg2: "最小"
arg3: "最大"
seedRandomPick: "リストからランダムに選択 (シード)"
_seedRandomPick:
arg1: "シード"
arg2: "リスト"
DRPWPM: "確率付きリストからランダムに選択 (ユーザーごとに日替わり)"
_DRPWPM:
arg1: "テキストのリスト"
pick: "リストから選択"
_pick:
arg1: "リスト"
arg2: "位置"
listLen: "リストの長さを取得"
_listLen:
arg1: "リスト"
number: "数値"
stringToNumber: "テキストを数値に"
_stringToNumber:
arg1: "テキスト"
numberToString: "数値をテキストに"
_numberToString:
arg1: "数値"
splitStrByLine: "テキストを行で分割"
_splitStrByLine:
arg1: "テキスト"
ref: "変数"
aiScriptVar: "AiScript変数"
fn: "関数"
_fn:
slots: "スロット"
slots-info: "スロットひとつひとつを改行で区切ってください"
arg1: "出力"
for: "繰り返し"
_for:
arg1: "回数"
arg2: "処理"
typeError: "スロット{slot}は\"{expect}\"を受け付けますが、\"{actual}\"が入れられています!"
thereIsEmptySlot: "スロット{slot}が空です!"
types:
string: "テキスト"
number: "数値"
boolean: "フラグ"
array: "リスト"
stringArray: "テキストのリスト"
emptySlot: "空のスロット"
enviromentVariables: "環境変数"
pageVariables: "ページ要素"
argVariables: "入力スロット"
_relayStatus:
requesting: "承認待ち"
accepted: "承認済み"
@@ -1534,7 +1729,6 @@ _notification:
yourFollowRequestAccepted: "フォローリクエストが承認されました"
youWereInvitedToGroup: "{userName}があなたをグループに招待しました"
pollEnded: "アンケートの結果が出ました"
unreadAntennaNote: "アンテナ {name}"
emptyPushNotificationMessage: "プッシュ通知の更新をしました"
_types:
@@ -1561,12 +1755,11 @@ _deck:
alwaysShowMainColumn: "常にメインカラムを表示"
columnAlign: "カラムの寄せ"
addColumn: "カラムを追加"
configureColumn: "カラムの設定"
swapLeft: "左に移動"
swapRight: "右に移動"
swapUp: "上に移動"
swapDown: "下に移動"
stackLeft: "左にスタック"
stackLeft: "左に重ねる"
popRight: "右に出す"
profile: "プロファイル"
newProfile: "新規プロファイル"

File diff suppressed because it is too large Load Diff

View File

@@ -74,8 +74,6 @@ _permissions:
"write:account": "Ẓreg talɣut n umiḍan-ik·im"
_widgets:
notifications: "Ilɣuyen"
_userList:
chooseList: "Fren tabdart"
_cw:
show: "Wali ugar"
_visibility:
@@ -97,6 +95,24 @@ _pages:
contentBlocks: "Agbur"
inputBlocks: "Anekcum"
specialBlocks: "Uzzig"
script:
categories:
list: "Tibdarin"
blocks:
_join:
arg1: "Tibdarin"
_randomPick:
arg1: "Tibdarin"
_dailyRandomPick:
arg1: "Tibdarin"
_seedRandomPick:
arg2: "Tibdarin"
_pick:
arg1: "Tibdarin"
_listLen:
arg1: "Tibdarin"
types:
array: "Tibdarin"
_notification:
youWereFollowed: "Yeṭṭafaṛ-ik·em-id"
_types:

View File

@@ -1,8 +1,7 @@
---
_lang_: "한국어"
headlineMisskey: "노트로 연결되는 네트워크"
introMisskey: "환영합니다! Misskey는 오픈 소스 분산형 마이크로 블로그 서비스입니다.\n'노트'를 작성해서 지금 일어나고 있는 일을 공유하거나, 당신만의 이야기를 모두에게 발신하세요📡\n'리액션' 기능으로 친구의 노트에 총알같이 반응을 추가할 수도 있습니다👍\n새로운 세계를 탐험해 보세요🚀"
poweredByMisskeyDescription: "{name}은(는) 오픈소스 플랫폼<b>Misskey</b>를 사용한 서비스(Misskey 인스턴스라고 불립니다) 중 하나입니다."
introMisskey: "환영합니다! Misskey 는 오픈 소스 분산형 마이크로 블로그 서비스입니다.\n\"노트\" 를 작성해서, 지금 일어나고 있는 일을 공유하거나, 당신만의 이야기를 모두에게 발신하세요📡\n\"리액션\" 기능으로, 친구의 노트에 총알같이 반응을 추가할 수도 있습니다👍\n새로운 세계를 탐험해 보세요🚀"
monthAndDay: "{month}월 {day}일"
search: "검색"
notifications: "알림"
@@ -10,12 +9,11 @@ username: "유저명"
password: "비밀번호"
forgotPassword: "비밀번호 재설정"
fetchingAsApObject: "연합에서 조회 중"
ok: "확인"
ok: "OK"
gotIt: "알겠어요"
cancel: "취소"
noThankYou: "나중에"
enterUsername: "유저명 입력"
renotedBy: "{user}님의 리노트"
renotedBy: "{user}님이 Renote"
noNotes: "노트가 없습니다"
noNotifications: "표시할 알림이 없습니다"
instance: "인스턴스"
@@ -39,7 +37,7 @@ favorites: "즐겨찾기"
unfavorite: "즐겨찾기에서 제거"
favorited: "즐겨찾기에 등록했습니다"
alreadyFavorited: "이미 즐겨찾기에 등록되어 있습니다"
cantFavorite: "즐겨찾기에 등록하지 못했습니다."
cantFavorite: "즐겨찾기에 등록하지 못했습니다"
pin: "프로필에 고정"
unpin: "프로필에서 고정 해제"
copyContent: "내용 복사"
@@ -49,13 +47,11 @@ deleteAndEdit: "삭제 후 편집"
deleteAndEditConfirm: "이 노트를 삭제한 뒤 다시 편집하시겠습니까? 이 노트에 대한 리액션, 리노트, 답글 또한 모두 삭제됩니다."
addToList: "리스트에 추가"
sendMessage: "메시지 보내기"
copyRSS: "RSS 복사"
copyUsername: "유저명 복사"
searchUser: "사용자 검색"
reply: "답글"
loadMore: "더 보기"
showMore: "더 보기"
showLess: "닫기"
youGotNewFollower: "새로운 팔로워가 있습니다"
receiveFollowRequest: "새로운 팔로우 요청이 있습니다"
followRequestAccepted: "팔로우가 수락되었습니다"
@@ -97,11 +93,11 @@ followRequests: "팔로우 요청"
unfollow: "팔로우 해제"
followRequestPending: "팔로우 허가 대기중"
enterEmoji: "이모지 입력"
renote: "리노트"
unrenote: "리노트 취소"
renoted: "리노트했습니다"
cantRenote: "이 게시물은 리노트 할 수 없습니다."
cantReRenote: "리노트를 리노트 할 수 없습니다."
renote: "Renote"
unrenote: "Renote 취소"
renoted: "Renote 하였습니다"
cantRenote: "이 게시물은 Renote할 수 없습니다."
cantReRenote: "Renote를 Renote할 수 없습니다."
quote: "인용"
pinnedNote: "고정해놓은 노트"
pinned: "프로필에 고정"
@@ -227,7 +223,7 @@ currentPassword: "현재 비밀번호"
newPassword: "새 비밀번호"
newPasswordRetype: "새 비밀번호 (재입력)"
attachFile: "파일 첨부"
more: "더보기"
more: "더보기!"
featured: "하이라이트"
usernameOrUserId: "유저명이나 ID"
noSuchUser: "유저를 찾을 수 없습니다"
@@ -351,10 +347,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHA 활성화"
recaptchaSiteKey: "사이트 키"
recaptchaSecretKey: "시크릿 키"
turnstile: "Turnstile"
enableTurnstile: "Turnstile 활성화"
turnstileSiteKey: "사이트 키"
turnstileSecretKey: "시크릿 키"
avoidMultiCaptchaConfirm: "여러 Captcha를 사용하는 경우 간섭이 발생할 가능성이 있습니다. 다른 Captcha를 비활성화하시겠습니까? 취소를 눌러 여러 Captcha를 활성화한 상태로 두는 것도 가능합니다."
antennas: "안테나"
manageAntennas: "안테나 관리"
@@ -457,8 +449,7 @@ language: "언어"
uiLanguage: "UI 표시 언어"
groupInvited: "그룹에 초대되었습니다"
aboutX: "{x}에 대하여"
emojiStyle: "이모지 스타일"
native: "네이티브"
useOsNativeEmojis: "OS 기본 이모지를 사용"
disableDrawer: "드로어 메뉴를 사용하지 않기"
youHaveNoGroups: "그룹이 없습니다"
joinOrCreateGroup: "다른 그룹의 초대를 받거나, 직접 새 그룹을 만들어 보세요."
@@ -511,7 +502,6 @@ deleteAll: "모두 삭제"
showFixedPostForm: "타임라인 상단에 글 작성란을 표시"
newNoteRecived: "새 노트가 있습니다"
sounds: "소리"
sound: "소리"
listen: "듣기"
none: "없음"
showInPage: "페이지로 보기"
@@ -571,7 +561,6 @@ author: "작성자"
leaveConfirm: "저장하지 않은 변경사항이 있습니다. 취소하시겠습니까?"
manage: "관리"
plugins: "플러그인"
preferencesBackups: "환경설정 백업"
deck: "덱"
undeck: "덱 해제"
useBlurEffectForModal: "모달에 흐림 효과 사용"
@@ -622,7 +611,7 @@ create: "생성"
notificationSetting: "알림 설정"
notificationSettingDesc: "표시할 알림의 종류를 선택해 주세요."
useGlobalSetting: "글로벌 설정을 사용하기"
useGlobalSettingDesc: "활성화하면 계정의 알림 설정이 적용니다. 비활성화하면 개별적으로 설정할 수 있게 됩니다."
useGlobalSettingDesc: "활성화하면 계정의 알림 설정이 적용니다. 비활성화하면 개별적으로 설정할 수 있게 됩니다."
other: "기타"
regenerateLoginToken: "로그인 토큰을 재생성"
regenerateLoginTokenDescription: "로그인할 때 사용되는 내부 토큰을 재생성합니다. 일반적으로 이 작업을 실행할 필요는 없습니다. 이 기능을 사용하면 이 계정으로 로그인한 모든 기기에서 로그아웃됩니다."
@@ -717,7 +706,6 @@ accentColor: "강조 색상"
textColor: "문자 색"
saveAs: "다른 이름으로 저장"
advanced: "고급"
advancedSettings: "고급 설정"
value: "값"
createdAt: "생성된 날짜"
updatedAt: "수정한 날짜"
@@ -879,7 +867,6 @@ numberOfPageCache: "페이지 캐시 수"
numberOfPageCacheDescription: "숫자가 클 수록 편리성이 높아지지만, 시스템 자원과 메모리를 더 많이 사용합니다."
logoutConfirm: "로그아웃 하시겠습니까?"
lastActiveDate: "마지막 이용"
statusbar: "상태바"
pleaseSelect: "선택해 주세요"
reverse: "플립"
colored: "색 입히기"
@@ -899,23 +886,7 @@ beta: "베타"
enableAutoSensitive: "자동 NSFW 탐지"
enableAutoSensitiveDescription: "이용 가능할 경우 기계학습을 통해 자동으로 미디어 NSFW를 설정합니다. 이 기능을 해제하더라도, 인스턴스 정책에 따라 자동으로 설정될 수 있습니다."
activeEmailValidationDescription: "유저가 입력한 메일 주소가 일회용 메일인지, 실제로 통신할 수 있는 지 엄격하게 검사합니다. 해제할 경우 이메일 형식에 대해서만 검사합니다."
navbar: "내비게이션 바"
shuffle: "셔플"
account: "계정"
move: "이동"
pushNotification: "푸시 알림"
subscribePushNotification: "푸시 알림 켜기"
unsubscribePushNotification: "푸시 알림 끄기"
pushNotificationAlreadySubscribed: "푸시 알림이 이미 켜져 있습니다"
pushNotificationNotSupported: "브라우저나 인스턴스에서 푸시 알림이 지원되지 않습니다"
sendPushNotificationReadMessage: "푸시 알림이니 메시지를 읽으면 푸시 알림을 삭제합니다"
sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」이라는 알림이 잠깐 표시됩니다. 기기의 전력 소비량이 증가할 수 있습니다."
windowMaximize: "최대화"
windowRestore: "복구"
caption: "캡션"
loggedInAsBot: "봇 계정으로 로그인중"
tools: "도구"
cannotLoad: "불러오지 못했습니다"
_sensitiveMediaDetection:
description: "기계학습을 통해 자동으로 민감한 미디어를 탐지하여, 모더레이션에 참고할 수 있도록 합니다. 서버의 부하를 약간 증가시킵니다."
sensitivity: "탐지 민감도"
@@ -966,24 +937,6 @@ _plugin:
install: "플러그인 설치"
installWarn: "신뢰할 수 없는 플러그인은 설치하지 않는 것이 좋습니다."
manage: "플러그인 관리"
_preferencesBackups:
list: "생성한 백업"
saveNew: "새 백업 만들기"
loadFile: "파일 가져오기"
apply: "이 기기에 적용"
save: "현재 설정으로 덮어쓰기"
inputName: "백업 이름을 입력하세요"
cannotSave: "저장하지 못했습니다"
nameAlreadyExists: "\"{name}\" 백업이 이미 존재합니다. 다른 이름을 설정하여 주십시오."
applyConfirm: "\"{name}\" 백업을 현재 기기에 적용하시겠습니까? 현재 설정은 덮어 씌워집니다."
saveConfirm: "{name} 을 덮어쓰시겠습니까?"
deleteConfirm: "{name} 을(를) 삭제하시겠습니까?"
renameConfirm: "\"{old}\" 백업을 \"{new}\"(으)로 바꾸시겠습니까?"
noBackups: "저장된 백업이 없습니다. \"새 백업 만들기\"를 눌러 현재 클라이언트 설정을 서버에 백업할 수 있습니다."
createdAt: "생성 날짜: {date} {time}"
updatedAt: "갱신 날짜: {date} {time}"
cannotLoad: "가져오기에 실패했습니다"
invalidFile: "파일 형식이 올바르지 않습니다."
_registry:
scope: "범위"
key: "키"
@@ -1067,8 +1020,6 @@ _mfm:
sparkleDescription: "반짝이는 파티클 효과를 추가합니다."
rotate: "회전"
rotateDescription: "지정한 각도로 회전시킵니다."
plain: "평문"
plainDescription: "안에 있는 MFM 구문을 모두 무시하고 평문으로 표시합니다."
_instanceTicker:
none: "보이지 않음"
remote: "리모트 유저에게만 보이기"
@@ -1154,7 +1105,7 @@ _theme:
hashtag: "해시태그"
mention: "멘션"
mentionMe: "나에게 보낸 멘션"
renote: "리노트"
renote: "Renote"
modalBg: "모달 배경"
divider: "구분선"
scrollbarHandle: "스크롤바 핸들"
@@ -1226,9 +1177,6 @@ _tutorial:
step7_1: "이것으로 Misskey의 기본 튜토리얼을 마치겠습니다. 수고하셨습니다!"
step7_2: "Misskey에 대해 더 알고 싶으시다면 {help}를 참고해 주세요."
step7_3: "그럼 Misskey를 즐기세요! 🚀"
step8_1: "마지막으로, 푸시 알림을 활성화해 보지 않으실래요?"
step8_2: "푸시 알림을 활성화하면, Misskey를 열지 않았을 때에도 리액션이나 팔로우, 멘션 등을 확인할 수 있습니다."
step8_3: "알림 설정은 나중에도 변경할 수 있습니다."
_2fa:
alreadyRegistered: "이미 설정이 완료되었습니다."
registerDevice: "디바이스 등록"
@@ -1301,11 +1249,9 @@ _widgets:
trends: "트렌드"
clock: "시계"
rss: "RSS 리더"
rssTicker: "RSS Ticker"
activity: "활동"
photos: "사진"
digitalClock: "디지털 시계"
unixClock: "UNIX 시계"
federation: "연합"
instanceCloud: "인스턴스 구름"
postForm: "글 입력란"
@@ -1316,9 +1262,6 @@ _widgets:
serverMetric: "서버 통계"
aiscript: "AiScript 콘솔"
aichan: "아이"
userList: "사용자 목록"
_userList:
chooseList: "리스트 선택"
_cw:
hide: "숨기기"
show: "더 보기"
@@ -1454,6 +1397,8 @@ _pages:
eyeCatchingImageRemove: "아이캐치 이미지를 삭제"
chooseBlock: "블록 추가"
selectType: "종류 선택"
enterVariableName: "변수명을 지정해주세요"
variableNameIsAlreadyUsed: "해당 변수명은 이미 사용중입니다"
contentBlocks: "콘텐츠"
inputBlocks: "입력"
specialBlocks: "특수"
@@ -1463,11 +1408,249 @@ _pages:
section: "섹션"
image: "이미지"
button: "버튼"
if: "조건문"
_if:
variable: "변수"
post: "글 입력란"
_post:
text: "내용"
attachCanvasImage: "캔버스의 이미지와 함께 게시하기"
canvasId: "캔버스 ID"
textInput: "텍스트 입력"
_textInput:
name: "변수명"
text: "제목"
default: "기본값"
textareaInput: "여러 줄 텍스트 입력"
_textareaInput:
name: "변수명"
text: "제목"
default: "기본값"
numberInput: "수치 입력"
_numberInput:
name: "변수명"
text: "제목"
default: "기본값"
canvas: "캔버스"
_canvas:
id: "캔버스 ID"
width: "폭"
height: "높이"
note: "노트필기"
_note:
id: "노트 ID"
idDescription: "노트 URL을 붙여넣어 설정할 수도 있습니다."
detailed: "세부 정보 보기"
switch: "스위치"
_switch:
name: "변수명"
text: "제목"
default: "기본값"
counter: "카운터"
_counter:
name: "변수명"
text: "제목"
inc: "증가치"
_button:
text: "제목"
colored: "색 입히기"
action: "버튼을 눌렀을 때의 동작"
_action:
dialog: "대화상자를 표시"
_dialog:
content: "내용"
resetRandom: "난수를 초기화"
pushEvent: "이벤트 보내기"
_pushEvent:
event: "이벤트 이름"
message: "눌렀을 때 표시할 페이지"
variable: "보낼 변수"
no-variable: "없음"
callAiScript: "AiScript 호출"
_callAiScript:
functionName: "함수명"
radioButton: "선택지"
_radioButton:
name: "변수명"
title: "제목"
values: "줄바꿈으로 구분된 선택지"
default: "기본값"
script:
categories:
flow: "흐름 제어"
logical: "논리 연산"
operation: "계산"
comparison: "비교"
random: "랜덤"
value: "값"
fn: "함수"
text: "텍스트 조작"
convert: "변환"
list: "리스트"
blocks:
text: "텍스트"
multiLineText: "텍스트 (여러 줄)"
textList: "텍스트 목록"
_textList:
info: "각각을 줄바꿈으로 구분해주세요"
strLen: "텍스트의 길이"
_strLen:
arg1: "텍스트"
strPick: "문자 추출"
_strPick:
arg1: "텍스트"
arg2: "문자 위치"
strReplace: "텍스트 대체"
_strReplace:
arg1: "텍스트"
arg2: "대체될 텍스트"
arg3: "대체할 텍스트"
strReverse: "텍스트 뒤집기"
_strReverse:
arg1: "텍스트"
join: "텍스트 합치기"
_join:
arg1: "리스트"
arg2: "구분자"
add: "더하기"
_add:
arg1: "A"
arg2: "B"
subtract: "빼기"
_subtract:
arg1: "A"
arg2: "B"
multiply: "곱하기"
_multiply:
arg1: "A"
arg2: "B"
divide: "나누기"
_divide:
arg1: "A"
arg2: "B"
mod: "나눈 나머지"
_mod:
arg1: "A"
arg2: "B"
round: "소수점을 반올림"
_round:
arg1: "수치"
eq: "A와 B가 동일"
_eq:
arg1: "A"
arg2: "B"
notEq: "A와 B가 다름"
_notEq:
arg1: "A"
arg2: "B"
and: "A와 B가 둘 다 참"
_and:
arg1: "A"
arg2: "B"
or: "A, B중 하나 이상이 참"
_or:
arg1: "A"
arg2: "B"
lt: "< A가 B보다 작음"
_lt:
arg1: "A"
arg2: "B"
gt: "> A가 B보다 큼"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A가 B보다 작거나 같음"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A가 B보다 크거나 같음"
_gtEq:
arg1: "A"
arg2: "B"
if: "분기"
_if:
arg1: "조건문"
arg2: "참일 경우"
arg3: "거짓일 경우"
not: "부정"
_not:
arg1: "부정"
random: "랜덤"
_random:
arg1: "확률"
rannum: "난수"
_rannum:
arg1: "최솟값"
arg2: "최댓값"
randomPick: "목록에서 임의로 선택"
_randomPick:
arg1: "리스트"
dailyRandom: "랜덤 (하루동안 결과 유지)"
_dailyRandom:
arg1: "확률"
dailyRannum: "난수 (하루동안 결과 유지)"
_dailyRannum:
arg1: "최솟값"
arg2: "최댓값"
dailyRandomPick: "목록에서 임의로 선택 (하루동안 결과 유지)"
_dailyRandomPick:
arg1: "리스트"
seedRandom: "무작위 (시드)"
_seedRandom:
arg1: "시드"
arg2: "확률"
seedRannum: "난수 (시드)"
_seedRannum:
arg1: "시드"
arg2: "최솟값"
arg3: "최댓값"
seedRandomPick: "목록에서 무작위로 선택 (시드)"
_seedRandomPick:
arg1: "시드"
arg2: "리스트"
DRPWPM: "확률형 목록에서 임의로 선택 (하루동안 결과 유지)"
_DRPWPM:
arg1: "텍스트 목록"
pick: "목록에서 선택"
_pick:
arg1: "리스트"
arg2: "위치"
listLen: "리스트의 길이 가져오기"
_listLen:
arg1: "리스트"
number: "수치"
stringToNumber: "텍스트를 수치로"
_stringToNumber:
arg1: "텍스트"
numberToString: "수치를 텍스트로"
_numberToString:
arg1: "수치"
splitStrByLine: "텍스트를 행 단위로 분할"
_splitStrByLine:
arg1: "텍스트"
ref: "변수"
aiScriptVar: "AiScript 변수"
fn: "함수"
_fn:
slots: "슬롯"
slots-info: "각 슬롯을 줄바꿈으로 구분하여 주세요"
arg1: "출력"
for: "반복"
_for:
arg1: "횟수"
arg2: "처리"
typeError: "슬롯 {slot}은 \"{expect}\"를 사용할 수 있지만 \"{actual}이 들어있습니다!"
thereIsEmptySlot: "슬롯 {slot}이(가) 비었습니다!"
types:
string: "텍스트"
number: "수치"
boolean: "플래그"
array: "리스트"
stringArray: "텍스트 목록"
emptySlot: "빈 슬롯"
enviromentVariables: "환경 변수"
pageVariables: "페이지 요소"
argVariables: "입력 슬롯"
_relayStatus:
requesting: "대기 중"
accepted: "승인됨"
@@ -1486,14 +1669,13 @@ _notification:
yourFollowRequestAccepted: "팔로우 요청이 수락되었습니다"
youWereInvitedToGroup: "그룹에 초대되었습니다"
pollEnded: "투표 결과가 발표되었습니다"
unreadAntennaNote: "안테나 {name}"
emptyPushNotificationMessage: "푸시 알림이 갱신되었습니다"
_types:
all: "전부"
follow: "팔로잉"
mention: "멘션"
reply: "답글"
renote: "리노트"
renote: "Renote"
quote: "인용"
reaction: "리액션"
pollVote: "투표 참여"
@@ -1505,12 +1687,11 @@ _notification:
_actions:
followBack: "팔로우"
reply: "답글"
renote: "리노트"
renote: "Renote"
_deck:
alwaysShowMainColumn: "메인 칼럼 항상 표시"
columnAlign: "칼럼 정렬"
addColumn: "칼럼 추가"
configureColumn: "칼럼 설정"
swapLeft: "왼쪽으로 이동"
swapRight: "오른쪽으로 이동"
swapUp: "위로 이동"
@@ -1518,8 +1699,6 @@ _deck:
stackLeft: "왼쪽에 쌓기"
popRight: "오른쪽으로 빼기"
profile: "프로파일"
newProfile: "새 프로파일"
deleteProfile: "프로파일 삭제"
introduction: "칼럼을 조합해서 나만의 인터페이스를 구성해 보아요!"
introduction2: "나중에라도 화면 우측의 + 버튼을 눌러 새 칼럼을 추가할 수 있습니다."
widgetsIntroduction: "칼럼 메뉴의 \"위젯 편집\"에서 위젯을 추가해 주세요"

View File

@@ -2,7 +2,6 @@
_lang_: "Nederlands"
headlineMisskey: "Netwerk verbonden door notities"
introMisskey: "Welkom! Misskey is een open source, gedecentraliseerde microblogdienst.\nMaak \"notities\" om je gedachten te delen met iedereen om je heen. 📡\nMet \"reacties\" kun je ook snel je mening geven over berichten van anderen. 👍\nLaten we een nieuwe wereld verkennen! 🚀"
poweredByMisskeyDescription: "{name} is één van de services die door het open source platform <b>Misskey</b> wordt geleverd (het wordt ook wel een \"Misskey server genmoemd\")."
monthAndDay: "{day} {month}"
search: "Zoeken"
notifications: "Meldingen"
@@ -13,7 +12,6 @@ fetchingAsApObject: "Ophalen vanuit de Fediverse"
ok: "Ok"
gotIt: "Begrepen"
cancel: "Annuleren"
noThankYou: "Nee, bedankt"
enterUsername: "Voer een gebruikersnaam in"
renotedBy: "Hergedeeld door {user}"
noNotes: "Geen notities"
@@ -54,7 +52,6 @@ searchUser: "Zoeken een gebruiker"
reply: "Antwoord"
loadMore: "Laad meer"
showMore: "Toon meer"
showLess: "Sluiten"
youGotNewFollower: "volgde jou"
receiveFollowRequest: "Volgverzoek ontvangen"
followRequestAccepted: "Volgverzoek geaccepteerd"
@@ -109,7 +106,6 @@ clickToShow: "Klik om te bekijken"
sensitive: "NSFW"
add: "Toevoegen"
reaction: "Reacties"
reactionSetting: "Reacties die in de reactie-selector worden getoond"
reactionSettingDescription2: "Sleep om opnieuw te ordenen, Klik om te verwijderen, Druk op \"+\" om toe te voegen"
rememberNoteVisibility: "Vergeet niet de notitie zichtbaarheidsinstellingen"
attachCancel: "Verwijder bijlage"
@@ -126,19 +122,6 @@ blockConfirm: "Weet je zeker dat je dit account wil blokkeren?"
unblockConfirm: "Ben je zeker dat je deze account wil blokkeren?"
suspendConfirm: "Ben je zeker dat je deze account wil suspenderen?"
unsuspendConfirm: "Ben je zeker dat je deze account wil opnieuw aanstellen?"
selectList: "Kies een lijst."
selectAntenna: "Kies een antenne"
selectWidget: "Kies een widget"
editWidgets: "Bewerk widgets"
editWidgetsExit: "Klaar"
customEmojis: "Maatwerk emoji"
emoji: "Emoji"
emojis: "Emoji"
emojiName: "Naam emoji"
emojiUrl: "URL emoji"
addEmoji: "Toevoegen emoji"
settingGuide: "Aanbevolen instellingen"
cacheRemoteFiles: "Externe bestanden cachen"
flagAsBot: "Markeer dit account als een robot."
flagAsBotDescription: "Als dit account van een programma wordt beheerd, zet deze vlag aan. Het aanzetten helpt andere ontwikkelaars om bijvoorbeeld onbedoelde feedback loops te doorbreken of om Misskey meer geschikt te maken."
flagAsCat: "Markeer dit account als een kat."
@@ -205,7 +188,6 @@ done: "Klaar"
processing: "Bezig met verwerken"
preview: "Voorbeeld"
default: "Standaard"
defaultValueIs: "Standaard: {value}"
noCustomEmojis: "Er zijn geen emojis"
noJobs: "Er zijn geen taken"
federating: "Federeren"
@@ -288,10 +270,6 @@ emptyDrive: "Jouw Drive is leeg."
emptyFolder: "Deze map is leeg"
unableToDelete: "Kan niet worden verwijderd"
inputNewFileName: "Voer een nieuwe naam in"
inputNewDescription: "Voer hier het onderschrift in"
inputNewFolderName: "Naam invoeren voor nieuwe map"
circularReferenceFolder: "De bestemmingsmap is een submap van de map die je wilt verplaatsen."
hasChildFilesOrFolders: "Omdat deze map niet leeg is, kan die niet worden verwijderd."
copyUrl: "URL kopiëren"
rename: "Hernoemen"
avatar: "Avatar"
@@ -299,88 +277,12 @@ banner: "Banner"
nsfw: "NSFW"
whenServerDisconnected: "Wanneer de verbinding met de server wordt onderbroken"
disconnectedFromServer: "Verbinding met de server onderbroken."
reload: "Verversen"
doNothing: "Negeren"
reloadConfirm: "Weet je zeker dat je je tijdlijn wil verversen?"
watch: "Volgen"
unwatch: "Niet meer volgen"
accept: "Accepteren"
reject: "Weigeren"
normal: "Normaal"
instanceName: "Naam van de server"
instanceDescription: "Beschrijving van de server"
maintainerName: "Onderhouder"
maintainerEmail: "E-mailadres beheerder"
tosUrl: "URL gebruiksvoorwaarden"
thisYear: "Jaar"
thisMonth: "Maand"
today: "Vandaag"
dayX: "{day}"
monthX: "{month}"
yearX: "{year}"
pages: "Pagina's"
integration: "Integraties"
connectService: "Verbinden"
disconnectService: "Verbinding verbreken"
enableLocalTimeline: "Inschakelen lokale tijdlijn"
enableGlobalTimeline: "Inschakelen globale tijdlijn "
disablingTimelinesInfo: "Beheerders en moderators hebben altijd toegang tot alle tijdlijnen, ook als ze niet actief zijn."
registration: "Registreren"
enableRegistration: "Inschakelen registratie nieuwe gebruikers "
invite: "Uitnodigen"
driveCapacityPerLocalAccount: "Opslagruimte per lokale gebruiker"
driveCapacityPerRemoteAccount: "Opslagruimte per externe gebruiker"
inMb: "in megabytes"
iconUrl: "Pictogram URL"
bannerUrl: "Banner URL"
backgroundImageUrl: "URL afbeelding"
basicInfo: "Basisinformatie"
pinnedUsers: "Vastgeprikte gebruikers"
pinnedPages: "Vastgeprikte pagina's"
pinnedNotes: "Vastgemaakte notitie"
hcaptcha: "hCaptcha"
enableHcaptcha: "Inschakelen hCaptcha"
hcaptchaSiteKey: "Site sleutel"
hcaptchaSecretKey: "Geheime sleutel"
recaptcha: "reCAPTCHA"
enableRecaptcha: "Inschakelen reCAPTCHA"
recaptchaSiteKey: "Site sleutel"
recaptchaSecretKey: "Geheime sleutel"
turnstile: "Tourniquet"
enableTurnstile: "Inschakelen tourniquet"
turnstileSiteKey: "Site sleutel"
turnstileSecretKey: "Geheime sleutel"
antennas: "Antennes"
manageAntennas: "Antennes beheren"
name: "Naam"
antennaSource: "Bron antenne"
antennaKeywords: "Sleutelwoorden"
antennaExcludeKeywords: "Blokkeerwoorden"
withReplies: "Antwoorden toevoegen"
connectedTo: "De volgende accounts zijn verbonden"
notesAndReplies: "Berichten en reacties"
withFiles: "Bestanden toevoegen"
silence: "Dempen"
silenceConfirm: "Weet je zeker dat je deze gebruiker wil dempen?"
unsilence: "Dempen uitschakelen"
unsilenceConfirm: "Weet je zeker dat je deze gebruiker niet meer wil dempen?"
popularUsers: "Populaire gebruikers"
recentlyUpdatedUsers: "Recent actieve gebruikers"
recentlyRegisteredUsers: "Recent geregistreerde gebruikers"
recentlyDiscoveredUsers: "Nieuw ontdekte gebruikers "
exploreUsersCount: "Er zijn {count} gebruikers"
exploreFediverse: "Ontdek de Fediverse"
popularTags: "Populaire tags"
userList: "Lijsten"
about: "Over"
aboutMisskey: "Over Misskey"
administrator: "Beheerder"
token: "Token"
twoStepAuthentication: "Tweestapsverificatie"
moderator: "Moderator"
moderation: "Moderatie"
nUsersMentioned: "Vermeld door {n} gebruikers"
securityKey: "Beveiligingssleutel"
securityKeyName: "Sleutelnaam"
registerSecurityKey: "Zekerheids-Sleutel registreren"
lastUsed: "Laatst gebruikt"
@@ -391,24 +293,11 @@ newPasswordIs: "Het nieuwe wachtwoord is „{password}”."
reduceUiAnimation: "Verminder beweging in de UI"
share: "Delen"
notFound: "Niet gevonden"
uploadFolder: "Standaardmap voor uploaden"
cacheClear: "Cache verwijderen"
markAsReadAllNotifications: "Markeer alle meldingen als gelezen"
markAsReadAllUnreadNotes: "Markeer alle berichten als gelezen"
markAsReadAllTalkMessages: "Markeer alle berichten als gelezen"
help: "Help"
inputMessageHere: "Voer hier je bericht in"
close: "Sluiten"
group: "Groep"
groups: "Groepen"
invites: "Uitnodigen"
invitations: "Uitnodigen"
sound: "Geluid"
smtpHost: "Server"
smtpUser: "Gebruikersnaam"
smtpPass: "Wachtwoord"
clearCache: "Cache opschonen"
info: "Over"
user: "Gebruikers"
muteThread: "Discussies dempen "
unmuteThread: "Dempen van discussie ongedaan maken"
@@ -417,20 +306,12 @@ searchByGoogle: "Zoeken"
cropImage: "Afbeelding bijsnijden"
cropImageAsk: "Bijsnijdengevraagd"
file: "Bestanden"
pushNotification: "Pushberichten"
subscribePushNotification: "Push meldingen inschakelen"
unsubscribePushNotification: "Pushberichten uitschakelen"
pushNotificationAlreadySubscribed: "Pushberichtrn al ingeschakeld"
windowMaximize: "Maximaliseren"
windowRestore: "Herstellen"
loggedInAsBot: "Momenteel als bot ingelogd"
_email:
_follow:
title: "volgde jou"
_mfm:
mention: "Vermelding"
quote: "Quote"
emoji: "Maatwerk emoji"
search: "Zoeken"
_theme:
keys:
@@ -446,15 +327,12 @@ _widgets:
activity: "Activiteit"
federation: "Federatie"
jobQueue: "Job Queue"
_userList:
chooseList: "Kies een lijst."
_cw:
show: "Laad meer"
_visibility:
home: "Startpagina"
followers: "Volgers"
_profile:
name: "Naam"
username: "Gebruikersnaam"
_exportOrImport:
followingList: "Volgend"
@@ -470,9 +348,26 @@ _timelines:
_pages:
blocks:
image: "Afbeeldingen"
script:
categories:
list: "Lijsten"
blocks:
_join:
arg1: "Lijsten"
_randomPick:
arg1: "Lijsten"
_dailyRandomPick:
arg1: "Lijsten"
_seedRandomPick:
arg2: "Lijsten"
_pick:
arg1: "Lijsten"
_listLen:
arg1: "Lijsten"
types:
array: "Lijsten"
_notification:
youWereFollowed: "volgde jou"
unreadAntennaNote: "Antenne {name}"
_types:
follow: "Volgend"
mention: "Vermelding"
@@ -486,6 +381,5 @@ _deck:
_columns:
notifications: "Meldingen"
tl: "Tijdlijn"
antenna: "Antennes"
list: "Lijsten"
mentions: "Vermeldingen"

View File

@@ -1,8 +1,7 @@
---
_lang_: "Polski"
_lang_: "język polski"
headlineMisskey: "Sieć połączona wpisami"
introMisskey: "Misskey jest serwisem mikroblogowym typu open source.\nMisskey to opensource'owy serwis mikroblogowy, w którym możesz tworzyć \"notatki\", aby dzielić się tym, co się dzieje i opowiadać wszystkim o sobie.\nMożesz również użyć funkcji \"Reakcje\", aby szybko dodać własne reakcje do notatek innych użytkowników👍.\nOdkrywaj nowy świat🚀!"
poweredByMisskeyDescription: "{name} jest jedną z usług działającą na otwartoźródłowej platformie <b>Misskey</b> (określana jako \"instancja Misskey\")."
monthAndDay: "{month}-{day}"
search: "Szukaj"
notifications: "Powiadomienia"
@@ -13,7 +12,6 @@ fetchingAsApObject: "Pobieranie z Fediwersum…"
ok: "OK"
gotIt: "Rozumiem!"
cancel: "Anuluj"
noThankYou: "Nie teraz"
enterUsername: "Wprowadź nazwę użytkownika"
renotedBy: "Udostępniono przez {user}"
noNotes: "Brak wpisów"
@@ -49,13 +47,11 @@ deleteAndEdit: "Usuń i edytuj"
deleteAndEditConfirm: "Czy na pewno chcesz usunąć ten wpis i zedytować go? Utracisz wszystkie reakcje, udostępnienia i odpowiedzi do tego wpisu."
addToList: "Dodaj do listy"
sendMessage: "Wyślij wiadomość"
copyRSS: "Kopiuj RSS"
copyUsername: "Kopiuj nazwę użytkownika"
searchUser: "Wyszukiwanie użytkowników"
reply: "Odpowiedz"
loadMore: "Załaduj więcej"
showMore: "Załaduj więcej"
showLess: "Zamknij"
youGotNewFollower: "Zaobserwował(a) Cię"
receiveFollowRequest: "Otrzymano prośbę o możliwość obserwacji"
followRequestAccepted: "Zaakceptowano prośbę o możliwość obserwacji"
@@ -91,7 +87,7 @@ enterListName: "Nazwa listy"
privacy: "Prywatność"
makeFollowManuallyApprove: "Prośby o możliwość obserwacji wymagają zatwierdzenia"
defaultNoteVisibility: "Domyślna widoczność"
follow: "Obserwuj"
follow: "Obserwowani"
followRequest: "Poproś o możliwość obserwacji"
followRequests: "Prośby o możliwość obserwacji"
unfollow: "Przestań obserwować"
@@ -130,7 +126,7 @@ unsuspendConfirm: "Czy na pewno chcesz cofnąć zawieszenie tego konta?"
selectList: "Wybierz listę"
selectAntenna: "Wybierz Antennę"
selectWidget: "Wybierz widżet"
editWidgets: "Edytuj widżety"
editWidgets: "Edytuj widżet"
editWidgetsExit: "Gotowe"
customEmojis: "Niestandardowe emoji"
emoji: "Emoji"
@@ -145,7 +141,6 @@ flagAsBot: "To konto jest botem"
flagAsBotDescription: "Jeżeli ten kanał jest kontrolowany przez jakiś program, ustaw tę opcję. Jeżeli włączona, będzie działać jako flaga informująca innych programistów, aby zapobiegać nieskończonej interakcji z różnymi botami i dostosowywać wewnętrzne systemy Misskey, traktując konto jako bota."
flagAsCat: "To konto jest kotem"
flagAsCatDescription: "Przełącz tę opcję, aby konto było oznaczone jako kot."
flagShowTimelineReplies: "Pokazuj odpowiedzi na osi czasu"
autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od użytkowników, których obserwujesz"
addAccount: "Dodaj konto"
loginFailed: "Nie udało się zalogować"
@@ -205,7 +200,6 @@ done: "Gotowe"
processing: "Przetwarzanie"
preview: "Podgląd"
default: "Domyślne"
defaultValueIs: "Domyślne: {value}"
noCustomEmojis: "Brak emoji"
noJobs: "Brak zadań"
federating: "Federowanie"
@@ -240,7 +234,6 @@ resetAreYouSure: "Czy na pewno chcesz zresetować?"
saved: "Zapisano"
messaging: "Wiadomości"
upload: "Wyślij"
keepOriginalUploading: "Zachowaj oryginalny obraz"
fromDrive: "Z dysku"
fromUrl: "Z adresu URL"
uploadFromUrl: "Wyślij z adresu URL"
@@ -348,10 +341,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Włącz reCAPTCHA"
recaptchaSiteKey: "Klucz strony"
recaptchaSecretKey: "Tajny klucz"
turnstile: "Turnstile"
enableTurnstile: "Włącz Turnstile"
turnstileSiteKey: "Klucz strony"
turnstileSecretKey: "Tajny klucz"
avoidMultiCaptchaConfirm: "Używanie wielu Captchy może spowodować zakłócenia. Czy chcesz wyłączyć inną Captchę? Możesz zostawić wiele jednocześnie, klikając Anuluj."
antennas: "Anteny"
manageAntennas: "Zarządzaj Antenami"
@@ -387,7 +376,6 @@ administrator: "Admin"
token: "Token"
twoStepAuthentication: "Uwierzytelnianie dwuskładnikowe"
moderator: "Moderator"
moderation: "Moderacja"
nUsersMentioned: "{n} wspomnianych użytkowników"
securityKey: "Klucz bezpieczeństwa"
securityKeyName: "Nazwa klucza"
@@ -454,15 +442,12 @@ language: "Język"
uiLanguage: "Język wyświetlania UI"
groupInvited: "Zaproszony(-a) do grupy"
aboutX: "O {x}"
emojiStyle: "Styl emoji"
native: "Natywny"
disableDrawer: "Nie używaj menu w stylu szuflady"
useOsNativeEmojis: "Używaj natywnych Emoji systemu"
youHaveNoGroups: "Nie masz żadnych grup"
joinOrCreateGroup: "Uzyskaj zaproszenie do dołączenia do grupy lub utwórz własną grupę."
noHistory: "Brak historii"
signinHistory: "Historia logowania"
disableAnimatedMfm: "Wyłącz MFM z animacją"
doing: "Przetwarzanie..."
category: "Kategoria"
tags: "Tagi"
docSource: "Źródło tego dokumentu"
@@ -506,7 +491,6 @@ deleteAll: "Usuń wszystkie"
showFixedPostForm: "Wyświetlaj formularz tworzenia wpisu w górnej części osi czasu"
newNoteRecived: "Masz nowy wpis"
sounds: "Dźwięk"
sound: "Dźwięki"
listen: "Słuchaj"
none: "Brak"
showInPage: "Pokaż na stronie"
@@ -538,9 +522,6 @@ deleteAllFilesConfirm: "Czy na pewno chcesz usunąć wszystkie pliki?"
removeAllFollowingDescription: "Przestań obserwować wszystkie konta z {host}. Wykonaj to, jeżeli instancja już nie istnieje."
userSuspended: "To konto zostało zawieszone."
userSilenced: "Ten użytkownik został wyciszony."
yourAccountSuspendedTitle: "To konto jest zawieszone"
yourAccountSuspendedDescription: "To konto zostało zawieszone z powodu złamania regulaminu serwera lub innych podobnych. Skontaktuj się z administratorem, jeśli chciałbyś poznać bardziej szczegółowy powód. Proszę nie zakładać nowego konta."
menu: "Menu"
divider: "Rozdzielacz"
addItem: "Dodaj element"
relays: "Przekaźniki"
@@ -559,7 +540,7 @@ disablePlayer: "Zamknij odtwarzacz wideo"
expandTweet: "Rozwiń tweet"
themeEditor: "Edytor motywu"
description: "Opis"
describeFile: "Dodaj podpis"
describeFile: "dodaj podpis"
enterFileDescription: "Wprowadź napis"
author: "Autor"
leaveConfirm: "Są niezapisane zmiany. Czy chcesz je odrzucić?"
@@ -596,7 +577,6 @@ emptyToDisableSmtpAuth: "Pozostaw adres e-mail i hasło puste, aby wyłączyć w
smtpSecureInfo: "Wyłącz, jeżeli używasz STARTTLS"
testEmail: "Przetestuj dostarczanie wiadomości e-mail"
wordMute: "Wyciszenie słowa"
instanceMute: "Wyciszone instancje"
userSaysSomething: "{name} powiedział(-a) coś"
makeActive: "Aktywuj"
display: "Wyświetlanie"
@@ -626,7 +606,6 @@ fillAbuseReportDescription: "Wypełnij szczegóły zgłoszenia. Jeżeli dotyczy
abuseReported: "Twoje zgłoszenie zostało wysłane. Dziękujemy."
reporteeOrigin: "Pochodzenie zgłoszonego"
reporterOrigin: "Pochodzenie zgłaszającego"
forwardReport: "Przekaż zgłoszenie do innej instancji"
send: "Wyślij"
abuseMarkAsResolved: "Oznacz zgłoszenie jako rozwiązane"
openInNewTab: "Otwórz w nowej karcie"
@@ -639,12 +618,8 @@ random: "Losowe"
system: "System"
switchUi: "Przełącz interfejs użytkownika"
desktop: "Pulpit"
clip: "Klip"
createNew: "Utwórz nowy"
optional: "Nieobowiązkowe"
createNewClip: "Utwórz nowy klip"
unclip: "Odczep"
confirmToUnclipAlreadyClippedNote: "Ten wpis jest już częścią klipu \"{name}\". Czy chcesz ją usunąć z tego klipu?"
public: "Publiczny"
i18nInfo: "Misskey jest tłumaczone na wiele języków przez wolontariuszy. Możesz pomóc na {link}."
manageAccessTokens: "Zarządzaj tokenami dostępu"
@@ -678,7 +653,6 @@ pageLikesCount: "Liczba otrzymanych polubień stron"
pageLikedCount: "Liczba polubionych stron"
contact: "Kontakt"
useSystemFont: "Używaj domyślnej czcionki systemu"
clips: "Klipy"
experimentalFeatures: "Eksperymentalne funkcje"
developer: "Programista"
makeExplorable: "Pokazuj konto na stronie „Eksploruj”"
@@ -750,7 +724,6 @@ notRecommended: "Nie zalecane"
botProtection: "Zabezpieczenie przed botami"
instanceBlocking: "Zablokowane instancje"
selectAccount: "Wybierz konto"
switchAccount: "Przełącz konto"
enabled: "Właczono"
disabled: "Wyłączono"
quickAction: "Szybkie działania"
@@ -782,120 +755,22 @@ global: "Globalna"
squareAvatars: "Wyświetlaj kwadratowe awatary"
sent: "Wyślij"
received: "Otrzymane"
searchResult: "Wyniki wyszukiwania"
hashtags: "Hashtag"
troubleshooting: "Rozwiązywanie problemów"
useBlurEffect: "Użyj efektów rozmycia w UI"
learnMore: "Dowiedz się więcej"
misskeyUpdated: "Misskey zostało zaktualizowane!"
whatIsNew: "Pokaż zmiany"
translate: "Przetłumacz"
translatedFrom: "Przetłumaczone z {x}"
accountDeletionInProgress: "Trwa usuwanie konta"
usernameInfo: "Nazwa, która identyfikuje Twoje konto spośród innych na tym serwerze. Możesz użyć alfabetu (a~z, A~Z), cyfr (0~9) lub podkreślników (_). Nazwy użytkownika nie mogą być później zmieniane."
aiChanMode: "Tryb Ai"
keepCw: "Zostaw ostrzeżenia o zawartości"
pubSub: "Konta Pub/Sub"
resolved: "Rozwiązane"
unresolved: "Nierozwiązane"
breakFollow: "Usuń obserwującego"
itsOn: "Włączone"
itsOff: "Wyłączone"
unread: "Nieodczytane"
filter: "Filtr"
controlPanel: "Panel sterowania"
manageAccounts: "Zarządzaj kontami"
makeReactionsPublic: "Ustawić historię reakcji jako publiczną"
makeReactionsPublicDescription: "To spowoduje, że lista wszystkich Twoich dotychczasowych reakcji będzie publicznie widoczna."
classic: "Klasyczny"
muteThread: "Wycisz wątek"
unmuteThread: "Wyłącz wyciszenie wątku"
ffVisibility: "Widoczność obserwowanych/obserwujących"
ffVisibilityDescription: "Pozwala skonfigurować, kto może zobaczyć, kogo obserwujesz i kto Cię obserwuje."
continueThread: "Pokaż kontynuację wątku"
deleteAccountConfirm: "Spowoduje to nieodwracalne usunięcie Twojego konta. Kontynuować?"
incorrectPassword: "Nieprawidłowe hasło."
voteConfirm: "Potwierdzić swój głos na \"{choice}\"?"
hide: "Ukryj"
leaveGroup: "Opuść grupę"
leaveGroupConfirm: "Czy na pewno chcesz opuścić \"{name}\"?"
useDrawerReactionPickerForMobile: "Wyświetlaj wybornik reakcji jako szufladę na urządzeniach mobilnych"
welcomeBackWithName: "Witaj z powrotem, {name}"
clickToFinishEmailVerification: "Kliknij [{ok}], aby zakończyć weryfikację e-mail."
overridedDeviceKind: "Typ urządzenia"
smartphone: "Smartfon"
tablet: "Tablet"
auto: "Automatycznie"
size: "Rozmiar"
numberOfColumn: "Liczba kolumn"
searchByGoogle: "Szukaj"
indefinitely: "Nigdy"
tenMinutes: "10 minut"
oneHour: "1 godzina"
oneDay: "1 dzień"
oneWeek: "1 tydzień"
file: "Pliki"
recommended: "Zalecane"
check: "Zweryfikuj"
deleteAccount: "Usuń konto"
document: "Dokumentacja"
numberOfPageCache: "Ilość stron w cache"
logoutConfirm: "Czy na pewno chcesz się wylogować?"
lastActiveDate: "Ostatnio użyte w"
statusbar: "Pasek stanu"
pleaseSelect: "Wybierz opcję"
reverse: "Odwróć"
colored: "Kolorowe"
label: "Etykieta"
type: "Typ"
speed: "Prędkość"
localOnly: "Lokalne tylko"
failedToUpload: "Przesyłanie nie powiodło się"
cannotUploadBecauseInappropriate: "Nie można przesłać tego pliku, ponieważ jego części zostały wykryte jako potencjalnie nieodpowiednie."
cannotUploadBecauseNoFreeSpace: "Przesyłanie nie powiodło się z powodu braku miejsca na dysku."
beta: "Beta"
enableAutoSensitive: "Automatyczne oznaczanie NSFW"
enableAutoSensitiveDescription: "Umożliwia automatyczne wykrywanie i oznaczanie zawartości NSFW za pomocą uczenia maszynowego. Nawet jeśli ta opcja jest wyłączona, może być włączona w całej instancji."
navbar: "Pasek nawigacyjny"
account: "Konta"
move: "Przenieś"
pushNotification: "Powiadomienia"
subscribePushNotification: "Włącz powiadomienia"
unsubscribePushNotification: "Wyłącz powiadomienia push"
pushNotificationAlreadySubscribed: "Powiadomienia push są włączone"
pushNotificationNotSupported: "Przeglądarka lub instancja nie obsługuje powiadomień push"
sendPushNotificationReadMessage: "Usuń powiadomienia push po przeczytaniu powiadomień i wiadomości."
sendPushNotificationReadMessageCaption: "Chwilowo pojawi się powiadomienie \"{emptyPushNotificationMessage}\". Może wzrosnąć zużycie baterii urządzenia."
loggedInAsBot: "Jesteś obecnie zalogowany/a jako bot"
_sensitiveMediaDetection:
description: "Zmniejsza wysiłek związany z moderacją serwera dzięki automatycznemu rozpoznawaniu zawartości NSFW za pomocą uczenia maszynowego. To nieznacznie zwiększy obciążenie serwera."
setSensitiveFlagAutomatically: "Oznacz jako NSFW"
_emailUnavailable:
used: "Ten adres e-mail jest już używany"
format: "Format tego adresu e-mail jest nieprawidłowy"
disposable: "Nie można używać jednorazowych adresów e-mail"
mx: "Ten serwer e-mail jest nieprawidłowy"
smtp: "Ten serwer e-mail nie odpowiada"
_ffVisibility:
public: "Publiczne"
followers: "Widoczne tylko dla obserwujących"
private: "Prywatne"
_signup:
almostThere: "Prawie na miejscu"
emailAddressInfo: "Podaj swój adres e-mail. Nie zostanie on upubliczniony."
emailSent: "E-mail z potwierdzeniem został wysłany na Twój adres e-mail ({email}). Kliknij dołączony link, aby dokończyć tworzenie konta."
_accountDelete:
accountDelete: "Usuń konto"
mayTakeTime: "Ponieważ usuwanie konta jest procesem wymagającym dużej ilości zasobów, jego ukończenie może zająć trochę czasu, w zależności od ilości utworzonej zawartości i liczby przesłanych plików."
sendEmail: "Po zakończeniu usuwania konta na adres e-mail zarejestrowany na tym koncie zostanie wysłana wiadomość e-mail."
requestAccountDelete: "Poproś o usunięcie konta"
started: "Usuwanie się rozpoczęło."
inProgress: "Usuwanie jest obecnie w toku"
public: "Publikuj"
_ad:
back: "Wróć"
reduceFrequencyOfThisAd: "Pokazuj tę reklamę rzadziej"
_forgotPassword:
enterEmail: "Wpisz adres e-mail użyty do rejestracji. Zostanie do niego wysłany link, za pomocą którego możesz zresetować hasło."
ifNoEmail: "Jeżeli nie podano adresu e-mail podczas rejestracji, skontaktuj się z administratorem zamiast tego."
contactAdmin: "Jeżeli Twoja instancja nie obsługuje adresów e-mail, skontaktuj się zamiast tego z administratorem, aby zresetować hasło."
_gallery:
@@ -912,23 +787,6 @@ _plugin:
install: "Zainstaluj wtyczki"
installWarn: "Nie instaluj niezaufanych wtyczek."
manage: "Zarządzanie wtyczkami"
_preferencesBackups:
list: "Utworzone kopie zapasowe"
saveNew: "Zapisz nową kopię zapasową"
loadFile: "Załaduj z pliku"
apply: "Zastosuj do tego urządzenia"
save: "Zapisz zmiany"
inputName: "Proszę podać nazwę dla tej kopii zapasowej"
cannotSave: "Zapisanie nie powiodło się"
nameAlreadyExists: "Kopia zapasowa o nazwie \"{name}\" już istnieje. Proszę podać inną nazwę."
applyConfirm: "Czy na pewno chcesz zastosować kopię zapasową \"{name}\" na tym urządzeniu? Istniejące ustawienia tego urządzenia zostaną nadpisane."
saveConfirm: "Zapisać kopię zapasową jako {name}?"
deleteConfirm: "Usunąć kopię zapasową {name}?"
renameConfirm: "Zmienić nazwę kopii zapasowej z \"{old}\" na \"{new}\"?"
createdAt: "Utworzony w: {date} {time}"
updatedAt: "Zaktualizowano w: {date} {time}"
cannotLoad: "Ładowanie nie powiodło się"
invalidFile: "Nieprawidłowy format pliku"
_registry:
scope: "Zakres"
key: "Klucz"
@@ -963,13 +821,10 @@ _mfm:
bold: "Pogrubienie"
boldDescription: "Wyróżnia litery pogrubiając je."
small: "Małe"
smallDescription: "Wyświetla treść jako małą i cienką."
center: "Wyśrodkowanie"
centerDescription: "Wyśrodkowuje zawartość."
inlineCode: "Kod (w wierszu)"
blockCode: "Kod (blok)"
blockCodeDescription: "Wyświetla kod z podświetlaną składnią składający się z wielu linii."
blockMath: "Matematyka (Blok)"
quote: "Cytuj"
quoteDescription: "Wyświetla treść jako cytat."
emoji: "Niestandardowe emoji"
@@ -978,20 +833,6 @@ _mfm:
searchDescription: "Wyświetla pole wyszukiwania z wcześniej wpisanym tekstem."
flip: "Odwróć"
flipDescription: "Przerzuca treść poziomo lub pionowo."
jelly: "Animacja (Galaretka)"
jellyDescription: "Nadaje treści galaretowatą animację."
tada: "Animation (Tada)"
tadaDescription: "Nadaje treści animację podobną do \"Tada!\"."
jump: "Animacja (Skok)"
jumpDescription: "Nadaje treści animację skakania."
bounce: "Animacja (Odbijanie)"
bounceDescription: "Nadaje treści animację odbijania się."
shake: "Animacja (Wstrząsanie)"
shakeDescription: "Nadaje treści animację wstrząsania."
twitch: "Animacja (Drganie)"
twitchDescription: "Nadaje treści mocno drgającą animację."
spin: "Animacja (Obrót)"
spinDescription: "Nadaje treści animację obracania."
x2: "Duże"
x2Description: "Czyni treść większą."
x3: "Bardzo duże"
@@ -999,17 +840,9 @@ _mfm:
x4: "Ogromne"
x4Description: "Czyni treść jeszcze większą niż jeszcze większa."
blur: "Rozmycie"
blurDescription: "Rozmywa treść. Zostanie wyraźnie wyświetlona po najechaniu."
font: "Czcionka"
fontDescription: "Wybiera czcionkę do wyświetlania treści."
rainbow: "Tęcza"
rainbowDescription: "Sprawia, że zawartość pojawia się w kolorach tęczy."
sparkle: "Blask"
sparkleDescription: "Nadaje zawartości efekt lśniącego brokatu."
rotate: "Obróć"
rotateDescription: "Obraca zawartość o określony kąt."
plain: "Zwyczajny"
plainDescription: "Wyłącza efekty wszystkich MFM zawartych w tym efekcie MFM."
_instanceTicker:
none: "Nigdy nie pokazuj"
remote: "Pokaż dla zdalnych użytkowników"
@@ -1029,7 +862,6 @@ _channel:
usersCount: "{n} uczestnicy"
notesCount: "{n} wpisy"
_menuDisplay:
top: "Góra"
hide: "Ukryj"
_wordMute:
muteWords: "Słowo do wyciszenia"
@@ -1037,9 +869,6 @@ _wordMute:
soft: "Łagodny"
hard: "Twardy"
mutedNotes: "Wyciszone wpisy"
_instanceMute:
title: "Ukrywa wpisy z wymienionych instancji."
heading: "Lista instancji do wyciszenia"
_theme:
explore: "Przeglądaj motywy"
install: "Zainstaluj motyw"
@@ -1120,7 +949,6 @@ _sfx:
notification: "Powiadomienia"
chat: "Wiadomości"
chatBg: "Rozmowy (tło)"
antenna: "Anteny"
channel: "Powiadomienia kanału"
_ago:
future: "W przyszłości"
@@ -1140,33 +968,12 @@ _time:
_tutorial:
title: "Jak korzystać z Misskey"
step1_1: "Witaj!"
step1_2: "Ta strona nazywa się „oś czasu”. Pokazuje chronologicznie uporządkowane wpisy osób, które „śledzisz”."
step1_3: "Twoja oś czasu jest jeszcze pusta, ponieważ nie opublikowałeś(-aś) jeszcze żadnych wpisów i nie obserwujesz jeszcze nikogo."
step2_1: "Ukończmy konfigurację profilu zanim utworzymy wpis lub zaczniemy kogoś obserwować."
step2_2: "Podanie pewnych informacji o tym, kim jesteś, ułatwi innym określenie, czy chcą widzieć Twoje wpisy lub Cię obserwować."
step3_1: "Zakończyłeś(-aś) konfigurację profilu?"
step3_2: "Następnie spróbujmy opublikować wpis. Możesz to zrobić, naciskając przycisk z ikoną ołówka na ekranie."
step3_3: "Wypełnij pole i kliknij przycisk w prawym górnym rogu by wysłać post."
step3_4: "Nie masz nic do powiedzenia? Spróbuj \"ustawiam swój misskey\"!"
step4_1: "Zakończyłeś publikowanie pierwszego wpisu?"
step4_2: "Hurra! Teraz Twój pierwszy wpis powinien być wyświetlany na Twojej osi czasu."
step5_1: "Teraz spróbujmy ożywić Twoją oś czasu, przez zaobserwowanie innych ludzi."
step5_2: "{featured} pokaże Ci popularne wpisy na tej instancji. {explore} pozwoli Ci znaleźć popularnych użytkowników. Spróbuj znaleźć tam osoby, które chcesz obserwować!"
step5_3: "Aby obserwować innych użytkowników, kliknij ich ikonę i naciśnij przycisk \"Obserwuj\" na ich profilu."
step5_4: "Jeśli inny użytkownik ma ikonę kłódki obok swojej nazwy, może minąć trochę czasu, zanim ten użytkownik ręcznie zatwierdzi Twoją prośbę o obserwowanie."
step6_1: "Powinieneś teraz widzieć wpisy innych użytkowników na swojej osi czasu."
step6_2: "Możesz także umieścić „reakcje” na wpisach innych osób, aby szybko na nie odpowiedzieć."
step6_3: "Aby dodać \"reakcję\", naciśnij znak \"+\" na wpisie innego użytkownika i wybierz emotikonę, którą chcesz zareagować."
step7_1: "Gratulacje! Ukończyłeś podstawowy samouczek Misskey."
step7_2: "Jeśli chcesz dowiedzieć się więcej o Misskey, wypróbuj sekcję {help}."
step7_3: "A teraz powodzenia i baw się dobrze z Misskey! 🚀"
step8_1: "Na sam koniec, czy nie chciał(a)byś włączyć powiadomień push?"
step8_2: "Włączenie tej opcji pozwoli ci otrzymywać powiadomienia o reakcjach, śledzeniach i wzmiankach nawet wtedy, gdy Misskey nie będzie otwarty."
step8_3: "Ustawienia powiadomień można zmienić później."
_2fa:
alreadyRegistered: "Zarejestrowałeś już urządzenie do uwierzytelniania dwuskładnikowego."
registerDevice: "Zarejestruj nowe urządzenie"
registerKey: "Zarejestruj klucz bezpieczeństwa"
step1: "Najpierw, zainstaluj aplikację uwierzytelniającą (taką jak {a} lub {b}) na swoim urządzeniu."
step2: "Następnie, zeskanuje kod QR z ekranu."
step3: "Wprowadź token podany w aplikacji, aby ukończyć konfigurację."
@@ -1182,7 +989,6 @@ _permissions:
"write:favorites": "Edycja Twojej listy ulubionych."
"read:following": "Wyświetlanie informacji o obserwowanych"
"write:following": "Obserwowanie lub cofanie obserwacji innych kont"
"read:messaging": "Zobacz swoje czaty"
"read:mutes": "Wyświetlanie listy osób, które wyciszyłeś(-aś)"
"write:mutes": "Edycja listy osób, które wyciszyłeś(-aś)"
"read:notifications": "Wyświetlanie powiadomień"
@@ -1196,10 +1002,6 @@ _permissions:
"write:page-likes": "Edycja polubień na stronach"
"read:user-groups": "Wyświetlanie grup użytkownika"
"write:user-groups": "Edycja lub usuwanie grup użytkownika"
"read:channels": "Zobacz swoje kanały"
"write:channels": "Edytuj swoje kanały"
"read:gallery": "Zobacz swoją galerię"
"write:gallery": "Edytuj swoją galerię"
_auth:
shareAccess: "Czy chcesz autoryzować „{name}” do dostępu do tego konta?"
permissionAsk: "Ta aplikacja wymaga następujących uprawnień:"
@@ -1218,23 +1020,12 @@ _widgets:
calendar: "Kalendarz"
trends: "Na czasie"
clock: "Zegar"
rss: "Czytnik RSS"
activity: "Aktywność"
photos: "Zdjęcia"
digitalClock: "Zegar cyfrowy"
unixClock: "Zegar UNIX"
federation: "Federacja"
instanceCloud: "Chmura instancji"
postForm: "Formularz tworzenia wpisu"
slideshow: "Pokaz slajdów"
postForm: "Utwórz wpis"
button: "Przycisk"
onlineUsers: "Użytkownicy online"
jobQueue: "Kolejka zadań"
serverMetric: "Metryka serwera"
aiscript: "Konsola AiScript"
aichan: "Ai"
_userList:
chooseList: "Wybierz listę"
_cw:
hide: "Ukryj"
show: "Załaduj więcej"
@@ -1349,6 +1140,8 @@ _pages:
eyeCatchingImageRemove: "Usuń przyciągające wzrok zdjęcie"
chooseBlock: "Dodaj blok"
selectType: "Wybierz typ"
enterVariableName: "Wprowadź nazwę dla swojej zmiennej"
variableNameIsAlreadyUsed: "Ta nazwa jest już używana przez inną zmienną"
contentBlocks: "Zawartość"
inputBlocks: "Wejście"
specialBlocks: "Specjalne"
@@ -1358,11 +1151,230 @@ _pages:
section: "Sekcja"
image: "Zdjęcia"
button: "Przycisk"
if: "Jeżeli"
_if:
variable: "Zmienna"
post: "Utwórz wpis"
_post:
text: "Treść"
textInput: "Pole tekstowe"
_textInput:
name: "Nazwa zmiennej"
text: "Tytuł"
default: "Domyślna wartość"
textareaInput: "Pole tekstowe na wiele wierszy"
_textareaInput:
name: "Nazwa zmiennej"
text: "Tytuł"
default: "Domyślna wartość"
numberInput: "Pole na liczbę"
_numberInput:
name: "Nazwa zmiennej"
text: "Tytuł"
default: "Domyślna wartość"
_canvas:
width: "Szerokość"
height: "Wysokość"
note: "Osadzony wpis"
_note:
id: "ID wpisu"
idDescription: "Możesz też wkleić adres URL wpisu, aby go ustawić."
detailed: "Szczegółowy widok"
switch: "Przełącznik"
_switch:
name: "Nazwa zmiennej"
text: "Tytuł"
default: "Domyślna wartość"
counter: "Licznik"
_counter:
name: "Nazwa zmiennej"
text: "Tytuł"
inc: "Zwiększ o"
_button:
text: "Tytuł"
colored: "Kolorowe"
action: "Działanie wykonywane przy naciśnięciu przycisku"
_action:
dialog: "Pokazuj okno dialogowe"
_dialog:
content: "Treść"
resetRandom: "Resetuj losowe ziarno"
pushEvent: "Wyślij zdarzenie"
_pushEvent:
event: "Nazwa zdarzenia"
message: "Wiadomość do wyświetlenia po aktywowaniu"
variable: "Zmienna do wysłania"
no-variable: "Brak"
callAiScript: "Wywołaj AiScript"
_callAiScript:
functionName: "Nazwa funkcji"
radioButton: "Wybór"
_radioButton:
name: "Nazwa zmiennej"
title: "Tytuł"
values: "Lista wyborów (oddzielonych znakiem nowego wiersza)"
default: "Domyślna wartość"
script:
categories:
flow: "Kontrola przepływu"
logical: "Operacje logiczne"
operation: "Obliczanie"
comparison: "Porównanie"
random: "Losowe"
value: "Wartość"
fn: "Funkcje"
text: "Działania na tekście"
convert: "Transformacja"
list: "Listy"
blocks:
text: "Tekst"
multiLineText: "Tekst (w wielu wierszach)"
_textList:
info: "Oddziel każdy wpis znakiem nowego wiersza"
strLen: "Długość tekstu"
_strLen:
arg1: "Tekst"
_strPick:
arg1: "Tekst"
arg2: "Położenie znaku"
strReplace: "Zamiana tekstu"
_strReplace:
arg1: "Tekst"
arg2: "Tekst do zamiany"
arg3: "Zamieniono z"
_strReverse:
arg1: "Tekst"
_join:
arg1: "Listy"
arg2: "Odstęp"
add: "Dodaj"
_add:
arg1: "A"
arg2: "B"
subtract: "Odejmij"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Pomnóż"
_multiply:
arg1: "A"
arg2: "B"
divide: "Podziel"
_divide:
arg1: "A"
arg2: "B"
mod: "Reszta"
_mod:
arg1: "A"
arg2: "B"
_round:
arg1: "Liczba"
eq: "A i B są sobie równe"
_eq:
arg1: "A"
arg2: "B"
notEq: "A i B różnią się"
_notEq:
arg1: "A"
arg2: "B"
and: "A I B"
_and:
arg1: "A"
arg2: "B"
or: "A LUB B"
_or:
arg1: "A"
arg2: "B"
lt: "< A jest mniejsze niż B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A jest większe od B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A jest mniejsze lub równe B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A jest większe lub równe B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Warunek"
_if:
arg1: "Jeżeli"
arg2: "Jeżeli prawda"
not: "NIE"
_not:
arg1: "NIE"
random: "Losowe"
_random:
arg1: "Prawdopodobieństwo"
rannum: "Losowa liczba"
_rannum:
arg1: "Minimalna wartość"
arg2: "Maksymalna wartość"
randomPick: "Wybierz losowo z listy"
_randomPick:
arg1: "Listy"
dailyRandom: "Losowo (zostaje na dzień)"
_dailyRandom:
arg1: "Prawdopodobieństwo"
dailyRannum: "Losowa liczba (zostaje na dzień)"
_dailyRannum:
arg1: "Minimalna wartość"
arg2: "Maksymalna wartość"
dailyRandomPick: "Wybierz losowo z listy (zostaje na dzień)"
_dailyRandomPick:
arg1: "Listy"
seedRandom: "Losowo (z ziarnem)"
_seedRandom:
arg1: "Ziarno"
arg2: "Prawdopodobieństwo"
seedRannum: "Losowa liczba (z ziarnem)"
_seedRannum:
arg1: "Ziarno"
arg2: "Minimalna wartość"
arg3: "Maksymalna wartość"
seedRandomPick: "Wybierz losowo z listy (z ziarnem)"
_seedRandomPick:
arg1: "Ziarno"
arg2: "Listy"
DRPWPM: "Wybierz losowo z ważonej listy (zostaje na dzień)"
pick: "Wybierz z listy"
_pick:
arg1: "Listy"
arg2: "Położenie"
listLen: "Uzyskaj długość listy"
_listLen:
arg1: "Listy"
number: "Liczba"
stringToNumber: "Tekst na liczbę"
_stringToNumber:
arg1: "Tekst"
numberToString: "Liczba na tekst"
_numberToString:
arg1: "Liczba"
splitStrByLine: "Rozdziel tekst znakami nowej linii"
_splitStrByLine:
arg1: "Tekst"
ref: "Zmienne"
aiScriptVar: "Zmienna AiScript"
fn: "Funkcje"
_fn:
arg1: "Wyjście"
for: "Powtórzenie"
_for:
arg1: "Liczba powtórzeń"
arg2: "Działanie"
types:
string: "Tekst"
number: "Liczba"
boolean: "Flaguj"
array: "Listy"
enviromentVariables: "Zmienna środowiskowa"
pageVariables: "Element strony"
_relayStatus:
requesting: "Oczekujące"
accepted: "Zaakceptowano"
@@ -1380,12 +1392,9 @@ _notification:
youReceivedFollowRequest: "Otrzymałeś(-aś) prośbę o możliwość obserwacji"
yourFollowRequestAccepted: "Twoja prośba o możliwość obserwacji została przyjęta"
youWereInvitedToGroup: "Zaproszony(-a) do grupy"
pollEnded: "Wyniki ankiety stały się dostępne"
unreadAntennaNote: "Antena {name}"
emptyPushNotificationMessage: "Powiadomienia push zostały zaktualizowane"
_types:
all: "Wszystkie"
follow: "Nowi obserwujący"
follow: "Obserwowani"
mention: "Wspomnij"
reply: "Odpowiedzi"
renote: "Udostępnij"
@@ -1397,14 +1406,12 @@ _notification:
groupInvited: "Zaproszono do grup"
app: "Powiadomienia z aplikacji"
_actions:
followBack: "zaobserwował cię z powrotem"
reply: "Odpowiedz"
renote: "Udostępnij"
_deck:
alwaysShowMainColumn: "Zawsze pokazuj główną kolumnę"
columnAlign: "Wyrównaj kolumny"
addColumn: "Dodaj kolumnę"
configureColumn: "Ustawienia kolumny"
swapLeft: "Przesuń w lewo"
swapRight: "Przesuń w prawo"
swapUp: "Zamień z powyższym"
@@ -1412,9 +1419,6 @@ _deck:
stackLeft: "Przypnij do lewej"
popRight: "Odepnij w prawo"
profile: "Profil"
newProfile: "Nowy profil"
deleteProfile: "Usuń profil"
widgetsIntroduction: "Wybierz \"Edytuj widżety\" w menu kolumny i dodaj widżet."
_columns:
main: "Główna"
widgets: "Widżety"

View File

@@ -7,7 +7,7 @@ search: "Buscar"
notifications: "Notificações"
username: "Nome de usuário"
password: "Senha"
forgotPassword: "Esqueci-me da senha"
forgotPassword: "Esqueci a senha"
fetchingAsApObject: "Buscando no Fediverso"
ok: "OK"
gotIt: "Entendi"
@@ -52,7 +52,6 @@ searchUser: "Pesquisar utilizador"
reply: "Responder"
loadMore: "Carregar mais"
showMore: "Ver mais"
showLess: "Fechar"
youGotNewFollower: "Você tem um novo seguidor"
receiveFollowRequest: "Pedido de seguimento recebido"
followRequestAccepted: "Pedido de seguir aceito"
@@ -347,8 +346,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Habilitar reCAPTCHA"
recaptchaSiteKey: "Chave do sítio web"
recaptchaSecretKey: "Chave secreta"
turnstileSiteKey: "Chave do sítio web"
turnstileSecretKey: "Chave secreta"
avoidMultiCaptchaConfirm: "O uso de vários captchas pode causar interferência. Deseja desativar outros captchas? Você também pode cancelar e deixar vários captchas ativados."
antennas: "Antenas"
manageAntennas: "Gestão de antena"
@@ -455,7 +452,6 @@ deleteAll: "Apagar Tudo"
showFixedPostForm: "Exibir o formulário de postagem na parte superior da linha do tempo"
newNoteRecived: "Nova nota recebida"
sounds: "Sons"
sound: "Sons"
listen: "Ouvir"
none: "Nenhum"
showInPage: "Ver na página"
@@ -494,8 +490,6 @@ _widgets:
activity: "atividade"
federation: "União"
jobQueue: "Fila de trabalhos"
_userList:
chooseList: "Escolhe uma lista"
_cw:
show: "Carregar mais"
_visibility:
@@ -516,6 +510,171 @@ _timelines:
_pages:
blocks:
image: "imagem"
_button:
_action:
_pushEvent:
event: "Nome do evento"
message: "Mostrar mensagem quando ativado"
variable: "Variável a mandar"
no-variable: "Nenhum"
callAiScript: "Invocar AiScript"
_callAiScript:
functionName: "Nome da função"
radioButton: "Escolha"
_radioButton:
values: "Lista de escolhas separadas por quebras de texto"
script:
categories:
logical: "Operação lógica"
operation: "Cálculos"
comparison: "Comparação"
list: "Listas"
blocks:
_strReplace:
arg2: "Texto que irá ser substituído"
arg3: "Substituir com"
strReverse: "Virar texto"
join: "Sequência de texto"
_join:
arg1: "Listas"
arg2: "Separador"
add: "Somar"
_add:
arg1: "A"
arg2: "B"
subtract: "Subtrair"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Multiplicar"
_multiply:
arg1: "A"
arg2: "B"
divide: "Dividir"
_divide:
arg1: "A"
arg2: "B"
mod: "O resto de"
_mod:
arg1: "A"
arg2: "B"
round: "Arredondar decimal"
_round:
arg1: "Numérico"
eq: "A e B são iguais"
_eq:
arg1: "A"
arg2: "B"
notEq: "A e B são diferentes"
_notEq:
arg1: "A"
arg2: "B"
and: "A e B"
_and:
arg1: "A"
arg2: "B"
or: "A OU B"
_or:
arg1: "A"
arg2: "B"
lt: "< A é menor do que B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A é maior do que B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A é maior ou igual a B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A é maior ou igual a B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Galho"
_if:
arg1: "Se"
arg2: "Então"
arg3: "Se não"
not: "NÃO"
_not:
arg1: "NÃO"
random: "Aleatório"
_random:
arg1: "Probabilidade"
rannum: "Numeral aleatório"
_rannum:
arg1: "Valor mínimo"
arg2: "Valor máximo"
randomPick: "Escolher aleatoriamente de uma lista"
_randomPick:
arg1: "Listas"
dailyRandom: "Aleatório (Muda uma vez por dia para cada usuário)"
_dailyRandom:
arg1: "Probabilidade"
dailyRannum: "Numeral aleatório (Muda uma vez por dia para cada usuário)"
_dailyRannum:
arg1: "Valor mínimo"
arg2: "Valor máximo"
dailyRandomPick: "Escolher aleatoriamente de uma lista (Muda uma vez por dia para cada usuário)"
_dailyRandomPick:
arg1: "Listas"
seedRandom: "Aleatório (com semente)"
_seedRandom:
arg1: "Semente"
arg2: "Probabilidade"
seedRannum: "Número aleatório (com semente)"
_seedRannum:
arg1: "Semente"
arg2: "Valor mínimo"
arg3: "Valor máximo"
seedRandomPick: "Escolher aleatoriamente de uma lista (com uma semente)"
_seedRandomPick:
arg1: "Semente"
arg2: "Listas"
DRPWPM: "Escolher aleatoriamente de uma lista ponderada (Muda uma vez por dia para cada usuário)"
_DRPWPM:
arg1: "Lista de texto"
pick: "Escolhe a partir da lista"
_pick:
arg1: "Listas"
arg2: "Posição"
listLen: "Pegar comprimento da lista"
_listLen:
arg1: "Listas"
number: "Numérico"
stringToNumber: "Texto para numérico"
_stringToNumber:
arg1: "Texto"
numberToString: "Numérico para texto"
_numberToString:
arg1: "Numérico"
splitStrByLine: "Dividir texto por quebras"
_splitStrByLine:
arg1: "Texto"
ref: "Variável"
aiScriptVar: "Variável AiScript"
fn: "Função"
_fn:
slots: "Espaços"
slots-info: "Separar cada espaço com uma quebra de texto"
arg1: "Resultado"
for: "Repetição 'for'"
_for:
arg1: "Número de repetições"
arg2: "Ação"
typeError: "Espaço {slot} aceita valores de tipo \"{expect}\", mas o valor dado é do tipo \"{actual}\"!"
thereIsEmptySlot: "O espaço {slot} está vazio!"
types:
string: "Texto"
number: "Numérico"
array: "Listas"
stringArray: "Lista de texto"
emptySlot: "Espaço vazio"
enviromentVariables: "Variáveis de ambiente"
pageVariables: "Variáveis de página"
_relayStatus:
requesting: "Pendente"
accepted: "Aprovado"

View File

@@ -52,7 +52,6 @@ searchUser: "Caută un utilizator"
reply: "Răspunde"
loadMore: "Incarcă mai mult"
showMore: "Arată mai mult"
showLess: "Închide"
youGotNewFollower: "te-a urmărit"
receiveFollowRequest: "Cerere de urmărire primită"
followRequestAccepted: "Cerere de urmărire acceptată"
@@ -347,8 +346,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Activează reCAPTCHA"
recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret key"
turnstileSiteKey: "Site key"
turnstileSecretKey: "Secret key"
avoidMultiCaptchaConfirm: "Folosirea mai multor sisteme Captcha poate cauza interferență între acestea. Ai dori să dezactivezi alte sisteme Captcha acum active? Dacă preferi să rămână activate, apasă Anulare."
antennas: "Antene"
manageAntennas: "Gestionează Antenele"
@@ -450,6 +447,7 @@ language: "Limbă"
uiLanguage: "Limba interfeței"
groupInvited: "Ai fost invitat într-un grup"
aboutX: "Despre {x}"
useOsNativeEmojis: "Folosește emojiuri native OS-ului"
disableDrawer: "Nu folosi meniuri în stil sertar"
youHaveNoGroups: "Nu ai niciun grup"
joinOrCreateGroup: "Primește o invitație într-un grup sau creează unul nou."
@@ -502,7 +500,6 @@ deleteAll: "Șterge tot"
showFixedPostForm: "Arată caseta de postare în vârful cronologie"
newNoteRecived: "Sunt note noi"
sounds: "Sunete"
sound: "Sunete"
listen: "Ascultă"
none: "Nimic"
showInPage: "Arată în pagină"
@@ -672,8 +669,6 @@ _widgets:
activity: "Activitate"
federation: "Federație"
jobQueue: "coada de job-uri"
_userList:
chooseList: "Selectează o listă"
_cw:
show: "Incarcă mai mult"
_visibility:
@@ -694,6 +689,24 @@ _timelines:
_pages:
blocks:
image: "Imagini"
script:
categories:
list: "Liste"
blocks:
_join:
arg1: "Liste"
_randomPick:
arg1: "Liste"
_dailyRandomPick:
arg1: "Liste"
_seedRandomPick:
arg2: "Liste"
_pick:
arg1: "Liste"
_listLen:
arg1: "Liste"
types:
array: "Liste"
_notification:
youWereFollowed: "te-a urmărit"
youWereInvitedToGroup: "Ai fost invitat într-un grup"

View File

@@ -52,7 +52,6 @@ searchUser: "Поиск людей"
reply: "Ответить"
loadMore: "Показать еще"
showMore: "Показать еще"
showLess: "Закрыть"
youGotNewFollower: "Новый подписчик"
receiveFollowRequest: "Получен запрос на подписку"
followRequestAccepted: "Запрос на подписку принят"
@@ -348,8 +347,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Включить reCAPTCHA"
recaptchaSiteKey: "Ключ сайта"
recaptchaSecretKey: "Секретный ключ"
turnstileSiteKey: "Ключ сайта"
turnstileSecretKey: "Секретный ключ"
avoidMultiCaptchaConfirm: "Несколько способов проверки могут мешать друг другу. Подтвердите, если хотите отключить другие способы. Или нажмите «Отмена», чтобы оставить их включёнными."
antennas: "Антенны"
manageAntennas: "Настройки антенн"
@@ -452,6 +449,7 @@ language: "Язык"
uiLanguage: "Язык интерфейса"
groupInvited: "Приглашение в группу"
aboutX: "Описание {x}"
useOsNativeEmojis: "Использовать эмодзи операционной системы"
disableDrawer: "Не использовать выдвижные меню"
youHaveNoGroups: "У вас нет ни одной группы"
joinOrCreateGroup: "Получайте приглашения в группы или создавайте свои собственные"
@@ -504,7 +502,6 @@ deleteAll: "Удалить всё"
showFixedPostForm: "Показывать поле для ввода новой заметки наверху ленты"
newNoteRecived: "Появилась новая заметка"
sounds: "Звуки"
sound: "Звуки"
listen: "Слушать"
none: "Ничего"
showInPage: "Показать страницу"
@@ -564,7 +561,6 @@ author: "Автор"
leaveConfirm: "Вы не сохранили изменения. Хотите выйти и потерять их?"
manage: "Управление"
plugins: "Расширения"
preferencesBackups: "Резервная копия"
deck: "Пульт"
undeck: "Покинуть пульт"
useBlurEffectForModal: "Размывка под формой поверх всего"
@@ -627,7 +623,6 @@ reportAbuse: "Жалоба"
reportAbuseOf: "Пожаловаться на пользователя {name}"
fillAbuseReportDescription: "Опишите, пожалуйста, причину жалобы подробнее. Если речь о конкретной заметке, будьте добры приложить ссылку на неё."
abuseReported: "Жалоба отправлена. Большое спасибо за информацию."
reporter: "Сообщивший"
reporteeOrigin: "О ком сообщено"
reporterOrigin: "Кто сообщил"
forwardReport: "Перенаправление отчета на инстант."
@@ -648,8 +643,6 @@ clip: "Подборка"
createNew: "Новый документ"
optional: "Необязательно"
createNewClip: "Новая подборка"
unclip: "Убрать из подборки"
confirmToUnclipAlreadyClippedNote: "Эта заметка уже есть в подборке «{name}». Удалить из этой подборки?"
public: "Общедоступно"
i18nInfo: "Misskey переводят на разные языки добровольцы со всего света. Ваша помощь тоже пригодится здесь: {link}."
manageAccessTokens: "Управление токенами доступа"
@@ -840,21 +833,11 @@ numberOfColumn: "Количество столбцов"
searchByGoogle: "Поиск"
instanceDefaultLightTheme: "Светлая тема по умолчанию"
instanceDefaultDarkTheme: "Темная тема по умолчанию"
mutePeriod: "Продолжительность скрытия"
indefinitely: "вечно"
tenMinutes: "10 минут"
oneHour: "1 час"
oneDay: "1 день"
oneWeek: "1 неделя"
cropImage: "Кадрирование"
cropImageAsk: "Нужно ли кадрировать изображение?"
file: "Файлы"
recentNHours: "Последние {n} ч"
recentNDays: "Последние {n} сут"
recommended: "Рекомендуем"
check: "Проверить"
driveCapOverrideLabel: "Изменение лимита дискового пространства для этого пользователя"
deleteAccount: "Удаление учётной записи"
reverse: "Переворот"
colored: "Выделена цветом"
label: "Метка"
@@ -863,8 +846,6 @@ beta: "Бета"
enableAutoSensitive: "Автоматическое определение NSFW"
enableAutoSensitiveDescription: "Если доступно, используйте машинное обучение для автоматической установки флага NSFW на носителе. Даже если эта функция отключена, она может быть установлена ​​автоматически в зависимости от инстанта."
account: "Учётные записи"
windowMaximize: "Развернуть"
windowRestore: "Восстановить"
_sensitiveMediaDetection:
description: "Машинное обучение может быть использовано для автоматического обнаружения чувствительных медиа для модерации. Нагрузка на сервер увеличивается незначительно."
setSensitiveFlagAutomatically: "Установить флаг NSFW"
@@ -1012,9 +993,9 @@ _channel:
usersCount: "Участников: {n}"
notesCount: "Заметок: {n}"
_menuDisplay:
sideFull: "Сбоку"
sideIcon: "Сбоку (только значки)"
top: "Сверху"
sideFull: "Сторона"
sideIcon: "Сторона (иконки)"
top: "Вверх"
hide: "Спрятать"
_wordMute:
muteWords: "Скрыть слово"
@@ -1026,7 +1007,6 @@ _wordMute:
hard: "Жёсткий"
mutedNotes: "Скрытые заметки"
_instanceMute:
title: "Скрывает заметки с заданных инстансов."
heading: "Список заглушенных инстансов"
_theme:
explore: "Обзор"
@@ -1133,7 +1113,7 @@ _tutorial:
step2_1: "Давайте, заполним профиль, прежде чем начать писать заметки и подписываться на других."
step2_2: "То, что вы расскажете в профиле, поможет лучше вас узнать, а значит, многим будет легче присоединиться — вы скорее получите новых подписчиков и читателей."
step3_1: "Успешно заполнили профиль?"
step3_2: "Что ж, теперь самое время опубликовать заметку. Если нажать вверху страницы на изображение карандаша, появится форма для текста."
step3_2: "Что ж, теперь самое время опубликуовать заметку. Если нажать вверху страницы на изображение карандаша, появится форма для текста."
step3_3: "Напишите в неё, что хотите, и нажмите на кнопку в правом верхнем углу."
step3_4: "Ничего не приходит в голову? Как насчёт: «Я новенький, пока осваиваюсь в Misskey»?"
step4_1: "С написанием первой заметки покончено?"
@@ -1231,8 +1211,6 @@ _widgets:
serverMetric: "Показатели сервера"
aiscript: "Консоль AiScript"
aichan: "Ай"
_userList:
chooseList: "Выберите список"
_cw:
hide: "Спрятать"
show: "Показать еще"
@@ -1368,6 +1346,8 @@ _pages:
eyeCatchingImageRemove: "Убрать картинку для привлечения внимания"
chooseBlock: "Добавить блок"
selectType: "Выберите вид"
enterVariableName: "Ведите имя переменной"
variableNameIsAlreadyUsed: "Это имя уже есть у другой переменной"
contentBlocks: "Содержательные"
inputBlocks: "Для ввода"
specialBlocks: "Особые"
@@ -1377,11 +1357,249 @@ _pages:
section: "Раздел"
image: "Изображения"
button: "Кнопка"
if: "Условный"
_if:
variable: "Переменная"
post: "Создание заметки"
_post:
text: "Текст"
attachCanvasImage: "Прикрепить изображение с холста"
canvasId: "Метка холста"
textInput: "Поле ввода текста"
_textInput:
name: "Имя переменной"
text: "Подпись"
default: "Исходное содержимое"
textareaInput: "Многострочное поле ввода текста"
_textareaInput:
name: "Имя переменной"
text: "Подпись"
default: "Исходное содержимое"
numberInput: "Поле для ввода числа"
_numberInput:
name: "Имя переменной"
text: "Подпись"
default: "Исходное значение"
canvas: "Холст"
_canvas:
id: "Метка холста"
width: "Ширина"
height: "Высота"
note: "Встроенная заметка"
_note:
id: "Идентификатор заметки"
idDescription: "Можно также вставить ссылку на заметку."
detailed: "Подробный вид"
switch: "Выключатель"
_switch:
name: "Имя переменной"
text: "Подпись"
default: "Исходное содержимое"
counter: "Кнопка со счётчиком"
_counter:
name: "Имя переменной"
text: "Надпись"
inc: "Увеличивать на"
_button:
text: "Надпись"
colored: "Выделена цветом"
action: "Действие по нажатию"
_action:
dialog: "Показать всплывающий текст"
_dialog:
content: "Всплывающий текст"
resetRandom: "Сброс генератора случайности"
pushEvent: "Вызвать событие"
_pushEvent:
event: "Имя события"
message: "Сообщение при нажатии"
variable: "Передать переменную с событием"
no-variable: "нет"
callAiScript: "Вызвать AiScript"
_callAiScript:
functionName: "Имя функции"
radioButton: "Кнопка-переключатель"
_radioButton:
name: "Имя переменной"
title: "Заголовок"
values: "Значения"
default: "Исходное значение"
script:
categories:
flow: "Управление исполнением"
logical: "Логические"
operation: "Арифметические"
comparison: "Сравнение"
random: "Случайные"
value: "Значения"
fn: "Функции"
text: "Текстовые"
convert: "Преобразование"
list: "Список"
blocks:
text: "Строка текста"
multiLineText: "Многострочный текст"
textList: "Список строк текста"
_textList:
info: "Пишите каждый пункт с новой строки"
strLen: "Длина текста"
_strLen:
arg1: "Текст"
strPick: "Взять знак из текста"
_strPick:
arg1: "Текст"
arg2: "Позиция знака"
strReplace: "Замена текста"
_strReplace:
arg1: "Текст, в котором заменять"
arg2: "Заменяемый текст"
arg3: "Менять на"
strReverse: "В обратном порядке"
_strReverse:
arg1: "Текст"
join: "Объединение"
_join:
arg1: "Списки"
arg2: "Разделитель"
add: "Добавить"
_add:
arg1: "A"
arg2: "B"
subtract: "Вычитание"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Умножение"
_multiply:
arg1: "A"
arg2: "B"
divide: "Деление"
_divide:
arg1: "A"
arg2: "B"
mod: "Остаток от деления"
_mod:
arg1: "A"
arg2: "B"
round: "Округление до целого"
_round:
arg1: "Число"
eq: "A равно B"
_eq:
arg1: "А"
arg2: "B"
notEq: "A не равно B"
_notEq:
arg1: "A"
arg2: "B"
and: "A и B"
_and:
arg1: "A"
arg2: "B"
or: "A или B"
_or:
arg1: "A"
arg2: "B"
lt: "A < B (меньше)"
_lt:
arg1: "A"
arg2: "B"
gt: "A > B (больше)"
_gt:
arg1: "A"
arg2: "B"
ltEq: "A ⩽ B (меньше или равно)"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: "A ⩾ B (больше или равно)"
_gtEq:
arg1: "A"
arg2: "B"
if: "Условный"
_if:
arg1: "Условие"
arg2: "Если правда"
arg3: "Если ложь"
not: "Отрицание"
_not:
arg1: "Условие"
random: "Случайность"
_random:
arg1: "Вероятность"
rannum: "Случайное число"
_rannum:
arg1: "Минимум"
arg2: "Максимум"
randomPick: "Случайный выбор из списка"
_randomPick:
arg1: "Списки"
dailyRandom: "Случайность (на день для пользователя)"
_dailyRandom:
arg1: "Вероятность"
dailyRannum: "Случайное число (на день для пользователя)"
_dailyRannum:
arg1: "Минимум"
arg2: "Максимум"
dailyRandomPick: "Случайный выбор из списка (на день для пользователя)"
_dailyRandomPick:
arg1: "Списки"
seedRandom: "Псевдослучайность (заданная зерном)"
_seedRandom:
arg1: "Зерно"
arg2: "Вероятность"
seedRannum: "Псевдослучайное число (заданное зерном)"
_seedRannum:
arg1: "Зерно"
arg2: "Минимум"
arg3: "Максимум"
seedRandomPick: "Псевдослучайный выбор из списка (заданный зерном)"
_seedRandomPick:
arg1: "Зерно"
arg2: "Списки"
DRPWPM: "Случайный выбор из взвешенного списка (на день для пользователя)"
_DRPWPM:
arg1: "Список строк текста"
pick: "Выбор из списка"
_pick:
arg1: "Списки"
arg2: "Индекс"
listLen: "Количество элементов в списке"
_listLen:
arg1: "Списки"
number: "Число"
stringToNumber: "Число из текста"
_stringToNumber:
arg1: "Текст"
numberToString: "Число в текст"
_numberToString:
arg1: "Число"
splitStrByLine: "Разделение текста на строки"
_splitStrByLine:
arg1: "Текст"
ref: "Переменная"
aiScriptVar: "Переменная AiScript"
fn: "Свои функции"
_fn:
slots: "Аргументы"
slots-info: "Напишите имя каждого аргумента с новой строки"
arg1: "Формула"
for: "Цикл"
_for:
arg1: "Количество повторений"
arg2: "Действие"
typeError: "Аргумент {slot} должен быть иметь тип «{expect}», а передали «{actual}»!"
thereIsEmptySlot: "Аргумент {slot} не заполнен!"
types:
string: "Текст"
number: "Число"
boolean: "Логический"
array: "Списки"
stringArray: "Список строк текста"
emptySlot: "Пустой аргумент"
enviromentVariables: "Переменная окружения"
pageVariables: "Элемент страницы"
argVariables: "Аргументы"
_relayStatus:
requesting: "В ожидании одобрения"
accepted: "Одобрено."
@@ -1419,7 +1637,6 @@ _deck:
alwaysShowMainColumn: "Всегда показывать главную колонку"
columnAlign: "Выравнивание колонок"
addColumn: "Добавить колонку"
configureColumn: "Настройки колонок"
swapLeft: "Переставить левее"
swapRight: "Переставить правее"
swapUp: "Переставить выше"

View File

@@ -2,7 +2,6 @@
_lang_: "Slovenčina"
headlineMisskey: "Sieť prepojená poznámkami"
introMisskey: "Vitajte! Misskey je otvorená a decentralizovaná mikroblogovacia služba.\n\"Poznámkami\" môžete zdieľať svoje myšlienky so všetkými okolo. 📡\nPomocou \"reakcií\" môžete rýchlo vyjadri svoje pocity o každého poznámkach. 👍\nPoďte objavovať svet! 🚀"
poweredByMisskeyDescription: "{name} je jedným zo serverov využívajúcich open source platformu <b>Misskey</b> (nazývaných Misskey inštancia)."
monthAndDay: "{day}. {month}."
search: "Hľadať"
notifications: "Oznámenia"
@@ -13,7 +12,6 @@ fetchingAsApObject: "Načítam údaje z Fediverzu"
ok: "OK"
gotIt: "Rozumiem!"
cancel: "Zrušiť"
noThankYou: "Nie, ďakujem"
enterUsername: "Zadajte meno používateľa"
renotedBy: "{user} preposlal/a"
noNotes: "Žiadne poznámky"
@@ -49,13 +47,11 @@ deleteAndEdit: "Odstrániť a upraviť"
deleteAndEditConfirm: "Naozaj chcete odstrániť túto poznámku a upraviť ju? Stratíte tým všetky reakcie a odpovede na ňu."
addToList: "Pridať do zoznamu"
sendMessage: "Odoslať správu"
copyRSS: "Kopírovať RSS"
copyUsername: "Kopírovať meno používateľa"
searchUser: "Hľadať používateľov"
reply: "Odpovedať"
loadMore: "Zobraziť viac"
showMore: "Zobraziť viac"
showLess: "Zavrieť"
youGotNewFollower: "Máte nového sledujúceho"
receiveFollowRequest: "Žiadosť o sledovanie prijatá"
followRequestAccepted: "Žiadosť o sledovanie akceptovaná"
@@ -351,10 +347,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Zapnúť ReCAPTCHA"
recaptchaSiteKey: "Site key"
recaptchaSecretKey: "Secret key"
turnstile: "Turnstile"
enableTurnstile: "Povoliť turnstile"
turnstileSiteKey: "Site key"
turnstileSecretKey: "Secret key"
avoidMultiCaptchaConfirm: "Použitie viacerých Captcha systémov môže sposobiť problémy. Chcete radšej vypnúť ostatné Captcha systémy? Môžete ich povoliť viaceré stlačení Zrušiť."
antennas: "Antény"
manageAntennas: "Spravovať antény"
@@ -457,8 +449,7 @@ language: "Jazyk"
uiLanguage: "Jazyk používateľského prostredia"
groupInvited: "Pozvať do skupiny"
aboutX: "O {x}"
emojiStyle: "Štýl emoji"
native: "Natívne"
useOsNativeEmojis: "Používať natívne emoji z OS"
disableDrawer: "Nepoužívať šuflíkové menu"
youHaveNoGroups: "Nemáte žiadne skupiny"
joinOrCreateGroup: "Požiadajte o pozvanie do existujúcej skupiny alebo vytvorte novú."
@@ -511,7 +502,6 @@ deleteAll: "Odstrániť všetko"
showFixedPostForm: "Zobraziť formulár na nové príspevky nad časovou osou"
newNoteRecived: "Sú nové poznámky"
sounds: "Zvuky"
sound: "Zvuky"
listen: "Počúvať"
none: "Žiadne"
showInPage: "Zobraziť v stránke"
@@ -571,7 +561,6 @@ author: "Autor"
leaveConfirm: "Máte neuložené zmeny. Chcete ich zahodiť?"
manage: "Administrácia"
plugins: "Pluginy"
preferencesBackups: "Zálohy nastavení"
deck: "Deck"
useBlurEffectForModal: "Použiť efekt rozmazania na okná"
useFullReactionPicker: "Použiť plnú veľkosť výberu reakcií"
@@ -716,7 +705,6 @@ accentColor: "Akcent"
textColor: "Text"
saveAs: "Uložiť ako..."
advanced: "Rozšírené"
advancedSettings: "Rozšírené nastavenia"
value: "Hodnoty"
createdAt: "Vytvorené"
updatedAt: "Upravené"
@@ -868,7 +856,6 @@ thereIsUnresolvedAbuseReportWarning: "Existuje nevyriešené nahlásenie zneuži
recommended: "Odporúčané"
driveCapOverrideLabel: "Zmena limitu úložiska pre tohoto používateľa"
driveCapOverrideCaption: "Ak je zadaná hodnota menšia alebo rovná 0, zruší sa."
requireAdminForView: "Na zobrazenie sa musíte prihlásiť pod administrátorským účtom."
isSystemAccount: "Tieto účty automaticky vytvoril a spravuje systém."
typeToConfirm: "Ak chcete vykonať túto operáciu, napíšte {x}"
deleteAccount: "Vymazať účet"
@@ -897,21 +884,7 @@ enableAutoSensitive: "Automatická detekcia NSFW"
enableAutoSensitiveDescription: "Ak je zapnuté, príznak NSFW sa na médiách automaticky nastaví pomocou strojového učenia. Aj keď je táto funkcia vypnutá, v niektorých prípadoch sa môže nastaviť automaticky."
activeEmailValidationDescription: "Dôkladnejšie overí e-mailovú adresu používateľa tým, že zistí, či ide o vyradenú e-mailovú adresu a či sa s ňou dá skutočne komunikovať. Ak nie je začiarknuté, e-mailová adresa sa kontroluje len ako text."
navbar: "Navigačný panel"
shuffle: "Zamiešať"
account: "Účty"
move: "Pohyb"
pushNotification: "Push notifikácie"
subscribePushNotification: "Push notifikácie zapnuté"
unsubscribePushNotification: "Vypnúť push notifikácie"
pushNotificationAlreadySubscribed: "Push notifikácie sú zapnuté"
pushNotificationNotSupported: "Prehliadač alebo server nepodporujú push notifikácie"
sendPushNotificationReadMessage: "Odstrániť push notifikácie po ich prečítaní"
sendPushNotificationReadMessageCaption: "Na chvíľu sa zobrazí oznámenie \"{emptyPushNotificationMessage}\". Môže to zvýšiť spotrebu batérie zariadenia."
windowMaximize: "Maximalizovať"
windowRestore: "Obnoviť"
caption: "Nadpis"
tools: "Nástroje"
cannotLoad: "Nedá sa načítať."
_sensitiveMediaDetection:
description: "Strojové učenie sa použije na automatickú detekciu citlivých médií na účely ich moderovania. Mierne sa zvýši zaťaženie servera."
sensitivity: "Citlivosť detekcie"
@@ -962,24 +935,6 @@ _plugin:
install: "Inštalova pluginy"
installWarn: "Prosím neinštalujte nedôveryhodné pluginy."
manage: "Spravovanie pluginov"
_preferencesBackups:
list: "Vytvorené zálohy"
saveNew: "Uložiť novú"
loadFile: "Nahrať súbor"
apply: "Použiť na toto zariadenie"
save: "Uložiť"
inputName: "Názov zálohy"
cannotSave: "Nedá sa uložiť"
nameAlreadyExists: "Záloha s názvom \"{name}\" už existuje. Zadajte iný názov."
applyConfirm: "Chcete použiť zálohu '{name}' na aktuálne zariadenie? Aktuálne nastavenia zariadenia sa stratia."
saveConfirm: "Chcete prepísať {name}?"
deleteConfirm: "Naozaj chcete odstrániť \"{name}\"?"
renameConfirm: "Chcete zmeniť \"{old}\" na \"{new}\"?"
noBackups: "Nie je k dispozícii žiadna záloha. \"Uložiť novú\" umožňuje uložiť aktuálnu konfiguráciu zariadenia na server."
createdAt: "Dátum vytvorenia: {date} {time}"
updatedAt: "Dátum úpravy: {date} {time}"
cannotLoad: "Nedá sa nahrať"
invalidFile: "Neplatný formát súboru"
_registry:
scope: "Oblasť"
key: "Kľúč"
@@ -1063,8 +1018,6 @@ _mfm:
sparkleDescription: "Obsahu dodá trblietajúci efekt."
rotate: "Otáčať"
rotateDescription: "Otočí obsah o určitý uhol."
plain: "Obyčajné"
plainDescription: "Bez akejkoľvej syntaxe"
_instanceTicker:
none: "Nikdy nezobrazovať"
remote: "Zobraziť pre vzdialených používateľov"
@@ -1222,9 +1175,6 @@ _tutorial:
step7_1: "Gralujeme! Dokončili ste základného sprievodcu Misskey."
step7_2: "Ak sa chcete naučiť viac o Misskey, skúste sekciu {help}."
step7_3: "A teraz, veľa šťastia, bavte sa s Misskey! 🚀"
step8_1: "A nakoniec, prečo si neaktivovať push oznámenia?"
step8_2: "Vďaka push notifikáciám sa dozviete o reakciách, sledovaniach a zmienkach, aj keď Misskey nie je otvorené."
step8_3: "Nastavenia notifikácií môžete neskôr zmeniť."
_2fa:
alreadyRegistered: "Už ste zaregistrovali 2-faktorové autentifikačné zariadenie."
registerDevice: "Registrovať nové zariadenie"
@@ -1301,7 +1251,6 @@ _widgets:
activity: "Aktivita"
photos: "Fotky"
digitalClock: "Digitálne hodiny"
unixClock: "UNIX čas"
federation: "Federácia"
instanceCloud: "Cloud serverov"
postForm: "Napísať poznámku"
@@ -1312,8 +1261,6 @@ _widgets:
serverMetric: "Metriky servera"
aiscript: "Konzola AiScript"
aichan: "Ai"
_userList:
chooseList: "Vyberte zoznam"
_cw:
hide: "Skryť"
show: "Zobraziť viac"
@@ -1449,6 +1396,8 @@ _pages:
eyeCatchingImageRemove: "Odstrániť miniatúru"
chooseBlock: "Pridať blok"
selectType: "Vyberte typ"
enterVariableName: "Zadajte meno premennej"
variableNameIsAlreadyUsed: "Meno premennej s už používa"
contentBlocks: "Obsah"
inputBlocks: "Vstup"
specialBlocks: "Špeciálne"
@@ -1458,11 +1407,249 @@ _pages:
section: "Sekcia"
image: "Obrázky"
button: "Tlačidlo"
if: "Ak"
_if:
variable: "Premenné"
post: "Napísať poznámku"
_post:
text: "Obsah"
attachCanvasImage: "Príspevok s obrázkom na plátne"
canvasId: "ID plátna"
textInput: "Textový vstup"
_textInput:
name: "Meno premennej"
text: "Nadpis"
default: "Predvolená hodnota"
textareaInput: "Viacriadkový textový vstup"
_textareaInput:
name: "Meno premennej"
text: "Nadpis"
default: "Predvolená hodnota"
numberInput: "Číselný vstup"
_numberInput:
name: "Meno premennej"
text: "Nadpis"
default: "Predvolená hodnota"
canvas: "Plátno"
_canvas:
id: "ID plátna"
width: "Šírka"
height: "Výška"
note: "Vložená poznámka"
_note:
id: "ID poznámky"
idDescription: "Alebo môžete vložiť URL poznámky sem"
detailed: "Podrobný pohľad"
switch: "Prepnúť"
_switch:
name: "Meno premennej"
text: "Nadpis"
default: "Predvolená hodnota"
counter: "Počítadlo"
_counter:
name: "Meno premennej"
text: "Nadpis"
inc: "Pripočítať"
_button:
text: "Nadpis"
colored: "Farebné"
action: "Operácia po stlačení tlačidla"
_action:
dialog: "Zobraziť dialóg"
_dialog:
content: "Obsah"
resetRandom: "Resetovať zdroj náhodnosti"
pushEvent: "Poslať udalosť"
_pushEvent:
event: "Názov udalosti"
message: "Zobrazená správa po aktivácii"
variable: "Odoslaná premenná"
no-variable: "Žiadne"
callAiScript: "Spustiť AiScript"
_callAiScript:
functionName: "Názov funkcie"
radioButton: "Možnosť"
_radioButton:
name: "Meno premennej"
title: "Nadpis"
values: "Zoznam možností oddelené novými riadkami"
default: "Predvolená hodnota"
script:
categories:
flow: "Riadenie behu"
logical: "Logická operácia"
operation: "Výpočet"
comparison: "Porovnanie"
random: "Náhodné"
value: "Hodnoty"
fn: "Funkcie"
text: "Textové operácie"
convert: "Transformácie"
list: "Zoznamy"
blocks:
text: "Text"
multiLineText: "Text (viacriadkový)"
textList: "Zoznam textov"
_textList:
info: "Oddeľte každú položku novým riadkom"
strLen: "Dĺžka textu"
_strLen:
arg1: "Text"
strPick: "Vybrať znak"
_strPick:
arg1: "Text"
arg2: "Pozícia znaku"
strReplace: "Náhradný text"
_strReplace:
arg1: "Text"
arg2: "Nahradený text"
arg3: "Nahradiť s"
strReverse: "Otočiť text"
_strReverse:
arg1: "Text"
join: "Spojiť texty"
_join:
arg1: "Zoznamy"
arg2: "Oddeľovač"
add: "Pridať"
_add:
arg1: "A"
arg2: "B"
subtract: "Odčítať"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Násobiť"
_multiply:
arg1: "A"
arg2: "B"
divide: "Deliť"
_divide:
arg1: "A"
arg2: "B"
mod: "Zvyšok po delení"
_mod:
arg1: "A"
arg2: "B"
round: "Zaokrúhliť"
_round:
arg1: "Číslo"
eq: "A a B sa rovnajú"
_eq:
arg1: "A"
arg2: "B"
notEq: "A a B sa nerovnajú"
_notEq:
arg1: "A"
arg2: "B"
and: "A a zároveň B"
_and:
arg1: "A"
arg2: "B"
or: "A alebo B"
_or:
arg1: "A"
arg2: "B"
lt: "< A je menšie ako B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A je väčšie ako B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A je menšie alebo rovné B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A je väčšie alebo rovné B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Vetva"
_if:
arg1: "Ak"
arg2: "Potom"
arg3: "Inak"
not: "Opak"
_not:
arg1: "Opak"
random: "Náhodné"
_random:
arg1: "Pravdepodobnosť"
rannum: "Náhodné číslo"
_rannum:
arg1: "Minimálna hodnota"
arg2: "Maximálna hodnota"
randomPick: "Náhodný výber zo zoznamu"
_randomPick:
arg1: "Zoznam"
dailyRandom: "Náhodne (zmení sa raz denne pre každého používateľa)"
_dailyRandom:
arg1: "Pravdepodobnosť"
dailyRannum: "Náhodné číslo (Mení sa denne pre každého používateľa)"
_dailyRannum:
arg1: "Minimálna hodnota"
arg2: "Maximálna hodnota"
dailyRandomPick: "Náhodný výber zo zoznamu (Mení sa denne pre každého používateľa)"
_dailyRandomPick:
arg1: "Zoznam"
seedRandom: "Náhodne (so seedom)"
_seedRandom:
arg1: "Seed"
arg2: "Pravdepodobnosť"
seedRannum: "Náhodné číslo (so seedom)"
_seedRannum:
arg1: "Seed"
arg2: "Minimálna hodnota"
arg3: "Maximálna hodnota"
seedRandomPick: "Náhodný výber zo zoznamu (so seedom)"
_seedRandomPick:
arg1: "Seed"
arg2: "Zoznam"
DRPWPM: "Náhodný výber z váženého zoznamu (Mení sa denne pre každého používateľa)"
_DRPWPM:
arg1: "Zoznam textov"
pick: "Vybrať zo zoznamu"
_pick:
arg1: "Zoznam"
arg2: "Pozícia"
listLen: "Získať dĺžku zoznamu"
_listLen:
arg1: "Zoznam"
number: "Číslo"
stringToNumber: "Text na číslo"
_stringToNumber:
arg1: "Text"
numberToString: "Číslo na text"
_numberToString:
arg1: "Číslo"
splitStrByLine: "Rozdelí text po riadkoch"
_splitStrByLine:
arg1: "Text"
ref: "Premenné"
aiScriptVar: "AiScript premenná"
fn: "Funkcie"
_fn:
slots: "Sloty"
slots-info: "Oddeľte každý slot novým riadkom"
arg1: "Výstup"
for: "For cyklus"
_for:
arg1: "Počet opakovaní"
arg2: "Akcia"
typeError: "Slot {slot} akceptuje hodnoty typu \"{expect}\", ale dodaná hodnota je typu \"{actual}\"!"
thereIsEmptySlot: "Slot {slot} je prázdny!"
types:
string: "Text"
number: "Číslo"
boolean: "Boolean"
array: "Zoznamy"
stringArray: "Zoznam textov"
emptySlot: "Prázdny slot"
enviromentVariables: "Premenné prostredia"
pageVariables: "Premenné stránky"
argVariables: "Vstupné sloty"
_relayStatus:
requesting: "Čaká sa"
accepted: "Akceptované"
@@ -1481,7 +1668,6 @@ _notification:
yourFollowRequestAccepted: "Vaša žiadosť o sledovanie bola prijatá"
youWereInvitedToGroup: "Pozvať do skupiny"
pollEnded: "Výsledky hlasovania sú k dispozícii."
unreadAntennaNote: "Anténa {name}"
emptyPushNotificationMessage: "Push notifikácie aktualizované"
_types:
all: "Všetky"
@@ -1505,7 +1691,6 @@ _deck:
alwaysShowMainColumn: "Vždy zobraziť v hlavnom stĺpci"
columnAlign: "Zarovnať stĺpce"
addColumn: "Pridať stĺpec"
configureColumn: "Nastavenie stĺpcov"
swapLeft: "Vymeniť vľavo"
swapRight: "Vymeniť vpravo"
swapUp: "Vymeniť hore"

View File

@@ -203,7 +203,6 @@ done: "Klar"
processing: "Bearbetar..."
preview: "Förhandsvisning"
default: "Standard"
defaultValueIs: "Standard: {value}"
noCustomEmojis: "Det finns ingen emoji"
noJobs: "Det finns inga jobb"
federating: "Federerar"
@@ -270,8 +269,6 @@ _widgets:
timeline: "Tidslinje"
federation: "Federation"
jobQueue: "Jobbkö"
_userList:
chooseList: "Välj lista"
_cw:
show: "Ladda mer"
_visibility:
@@ -285,6 +282,25 @@ _exportOrImport:
userLists: "Listor"
_charts:
federation: "Federation"
_pages:
script:
categories:
list: "Listor"
blocks:
_join:
arg1: "Listor"
_randomPick:
arg1: "Listor"
_dailyRandomPick:
arg1: "Listor"
_seedRandomPick:
arg2: "Listor"
_pick:
arg1: "Listor"
_listLen:
arg1: "Listor"
types:
array: "Listor"
_notification:
youWereFollowed: "följde dig"
_types:

View File

@@ -2,7 +2,6 @@
_lang_: "ภาษาไทย"
headlineMisskey: "เชื่อมต่อเครือข่ายโดยโน้ต"
introMisskey: "ยินดีต้อนรับจ้าาา! Misskey เป็นบริการไมโครบล็อกโอเพ่นซอร์ส แบบการกระจายอำนาจ\nสร้าง \"โน้ต\" เพื่อแบ่งปันความคิดของคุณกับทุกคนรอบตัวคุณกันเถอะ 📡\nด้วยการ \"รีแอคชั่นผู้คน\" คุณยังสามารถแสดงความรู้สึกของคุณเกี่ยวกับบันทึกของทุกคนได้อย่างรวดเร็ว 👍\n\nแล้วมาท่องสำรวจโลกใบใหม่กันเถอะ! 🚀"
poweredByMisskeyDescription: "{name} เป็นส่วนหนึ่งในบริการที่ถูกขับเคลื่อนโดยแพลตฟอร์มโอเพ่นซอร์ส <b>Misskey</b> (เรียกว่า \"อินสแตนซ์ Misskey\")"
monthAndDay: "{เดือน}/{วัน}"
search: "ค้นหา"
notifications: "การเเจ้งเตือน"
@@ -10,10 +9,9 @@ username: "ชื่อผู้ใช้"
password: "รหัสผ่าน"
forgotPassword: "ลืมรหัสผ่าน?"
fetchingAsApObject: "กำลังดึงข้อมูล จาก เฟดิเวิร์ส..."
ok: "โอเค"
ok: "ตกลง"
gotIt: "เข้าใจแล้ว !"
cancel: "ยกเลิก"
noThankYou: "ไม่เป็นไร"
enterUsername: "ใส่ชื่อผู้ใช้"
renotedBy: "รีโน้ตโดย {ผู้ใช้}"
noNotes: "ไม่มีโน้ต"
@@ -49,13 +47,11 @@ deleteAndEdit: "ลบและแก้ไข"
deleteAndEditConfirm: "นายแน่ใจแล้วเหรอ? ว่าต้องการลบโน้ตนี้และแก้ไข คุณอาจจะสูญเสียการโต้ตอบ, โน้ต, และการตอบกลับทั้งหมดได้นะ"
addToList: "เพิ่มในลิสต์"
sendMessage: "ส่งข้อความ"
copyRSS: "คัดลอก RSS"
copyUsername: "คัดลอกชื่อผู้ใช้"
searchUser: "ค้นหาผู้ใช้งาน"
reply: "ตอบกลับ"
loadMore: "โหลดเพิ่มเติม"
showMore: "แสดงเพิ่มเติม"
showLess: "ปิด"
youGotNewFollower: "ได้ติดตามคุณ"
receiveFollowRequest: "คำขอผู้ติดตามที่ได้รับ"
followRequestAccepted: "ผู้ติดตามได้ตอบรับคำขอร้องของคุณแล้ว"
@@ -63,8 +59,8 @@ mention: "กล่าวถึง"
mentions: "พูดถึง"
directNotes: "ไดเร็คโน้ต"
importAndExport: "นำเข้า / ส่งออก"
import: "นำเข้า"
export: "นำออก"
import: "การนำเข้า"
export: "การนำออก"
files: "ไฟล์"
download: "ดาวน์โหลด"
driveFileDeleteConfirm: "นายแน่ใจแล้วหรอ? ว่าต้องการลบไฟล์ \"{name}\" โน้ตย่อที่แนบมากับไฟล์นี้ก็จะถูกลบด้วยนะ"
@@ -74,7 +70,7 @@ importRequested: "เมื่อคุณได้ร้องขอการ
lists: "รายการ"
noLists: "คุณไม่มีลิสต์ใดๆนะ"
note: "ตัวโน้ต"
notes: "ตัวโน้ต"
notes: "หมายเหตุ"
following: "กำลังติดตาม"
followers: "ผู้ติดตาม"
followsYou: "ติดตามคุณ"
@@ -93,7 +89,7 @@ makeFollowManuallyApprove: "ติดตามคำขอที่ต้อง
defaultNoteVisibility: "การมองเห็นที่เป็นค่าเริ่มต้น"
follow: "กำลังติดตาม"
followRequest: "ส่งคำขอติดตาม"
followRequests: "ส่งคำขอติดตาม"
followRequests: "ติดตามการร้องขอ"
unfollow: "เลิกติดตาม"
followRequestPending: "กำลังรอดำเนินการร้องขอติดตาม"
enterEmoji: "ใส่อีโมจิ"
@@ -118,7 +114,7 @@ markAsSensitive: "ทำเครื่องหมายว่าละเอ
unmarkAsSensitive: "ยกเลิกทำเครื่องหมายเป็น NSFW"
enterFileName: "พิมพ์ชื่อไฟล์"
mute: "ปิดเสียง"
unmute: "ยกเลิกการปิดเสียง"
unmute: "ไม่ปิดเสียง"
block: "บล็อค"
unblock: "เลิกปิดกั้น"
suspend: "ถูกระงับ"
@@ -127,7 +123,7 @@ blockConfirm: "คุณแน่ใจแล้วเหรอ? ว่าต้
unblockConfirm: "คุณแน่ใจแล้วเหรอ? ว่าต้องการปลดบล็อคบัญชีนี้"
suspendConfirm: "นายแน่ใจแล้วเหรอว่าต้องการระงับบัญชีนี้อ่ะ?"
unsuspendConfirm: "นายแน่ใจแล้วหรอ? ว่าต้องการยกเลิกการระงับบัญชีนี้"
selectList: "เลือกรายการ"
selectList: "เลือกรายการ (Automatic Translation)"
selectAntenna: "เลือกเสาอากาศ"
selectWidget: "เลือกวิดเจ็ต"
editWidgets: "แก้ไขวิดเจ็ต"
@@ -164,7 +160,7 @@ host: "โฮสต์"
selectUser: "เลือกผู้ใช้งาน"
recipient: "ผู้รับ"
annotation: "ความคิดเห็น"
federation: "เฟดิเวิร์ส"
federation: "สหพันธ์"
instances: "ตัวอย่าง"
registeredAt: "จดทะเบียนที่"
latestRequestSentAt: "ส่งคำขอล่าสุดไปแล้ว"
@@ -228,10 +224,10 @@ newPassword: "รหัสผ่านใหม่"
newPasswordRetype: "ใส่รหัสผ่านใหม่อีกครั้ง"
attachFile: "แนบไฟล์"
more: "เพิ่มเติม!"
featured: "ไฮไลท์"
featured: "เป็นจุดเด่น"
usernameOrUserId: "ชื่อผู้ใช้หรือรหัสผู้ใช้งาน"
noSuchUser: "ไม่มีผู้ใช้นี้อยู่ในระบบ"
lookup: "การค้นหา"
lookup: "ค้นหา"
announcements: "ประกาศ"
imageUrl: "url รูปภาพ"
remove: "ลบ"
@@ -351,10 +347,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "เปิดใช้ reCAPTCHA"
recaptchaSiteKey: "คีย์ไซต์"
recaptchaSecretKey: "คีย์ลับ"
turnstile: "เทิร์น'สไทล"
enableTurnstile: "เปิดใช้งาน เทิร์น'สไทล"
turnstileSiteKey: "คีย์ไซต์"
turnstileSecretKey: "คีย์ลับ"
avoidMultiCaptchaConfirm: "การใช้ระบบ Captcha หลายระบบอาจทำให้เกิดการรบกวนหรืออาจจะเกิดข้อผิดพลาดได้ หากต้องการที่จะปิดการใช้งานระบบ Captcha อื่น ๆ แนะนำให้ปิดตัวอื่นๆก่อน ถ้าหากคุณต้องการให้เปิดใช้งานต่อไป ให้ กด ยกเลิก"
antennas: "เสาอากาศ"
manageAntennas: "จัดการเสาอากาศ"
@@ -457,8 +449,7 @@ language: "ภาษา"
uiLanguage: "ภาษาอินเทอร์เฟซผู้ใช้งาน"
groupInvited: "คุณได้รับเชิญให้เข้าร่วมกลุ่ม"
aboutX: "เกี่ยวกับ {x}"
emojiStyle: "สไตล์อิโมจิ"
native: "ภาษาแม่"
useOsNativeEmojis: "ใช้อีโมจิ OS แบบดั้งเดิม"
disableDrawer: "อย่าใช้ลิ้นชักสไตล์เมนู"
youHaveNoGroups: "คุณยังไม่มีกลุ่ม"
joinOrCreateGroup: "รับเชิญเข้าร่วมกลุ่มหรือสร้างกลุ่มของคุณเองเลยนะ"
@@ -511,7 +502,6 @@ deleteAll: "ลบทั้งหมด"
showFixedPostForm: "แสดงแบบฟอร์มการโพสต์ที่ด้านบนสุดของไทม์ไลน์"
newNoteRecived: "มีโน้ตใหม่"
sounds: "เสียง"
sound: "เสียง"
listen: "ฟัง"
none: "ไม่มี"
showInPage: "แสดงในเพจ"
@@ -571,7 +561,6 @@ author: "ผู้เขียน"
leaveConfirm: "คุณมีการเปลี่ยนแปลงที่ไม่ได้บันทึกนะ นายต้องการทิ้งการเปลี่ยนแปลงเหล่านั้นหรอ?"
manage: "การจัดการ"
plugins: "ปลั๊กอิน"
preferencesBackups: "ตั้งค่าการสำรองข้อมูล"
deck: "เด็ค"
undeck: "ออกจากเด็ค"
useBlurEffectForModal: "ใช้เอฟเฟกต์เบลอสำหรับโมดอล"
@@ -608,7 +597,7 @@ wordMute: "ปิดเสียงคำ"
regexpError: "ข้อผิดพลาดของนิพจน์ทั่วไป"
regexpErrorDescription: "เกิดข้อผิดพลาดในนิพจน์ทั่วไปในบรรทัดที่ {line} ของการปิดเสียงคำ {tab} ของคุณ:"
instanceMute: "ปิดเสียง อินสแตนซ์"
userSaysSomething: "{name} พูดอะไรบางอย่าง"
userSaysSomething: "{ชื่อ} พูดอะไรบางอย่าง"
makeActive: "เปิดใช้งาน"
display: "แสดงผล"
copy: "คัดลอก"
@@ -717,7 +706,6 @@ accentColor: "รูปแบบสี"
textColor: "สีข้อความ"
saveAs: "บันทึกเป็น..."
advanced: "ขั้นสูง"
advancedSettings: "การตั้งค่าขั้นสูง"
value: "ค่า"
createdAt: "สร้างเมื่อ"
updatedAt: "อัพเดทล่าสุด"
@@ -847,689 +835,89 @@ themeColor: "อินสแตนซ์ Ticker Color"
size: "ขนาด"
numberOfColumn: "จำนวนคอลัมน์"
searchByGoogle: "ค้นหา"
instanceDefaultLightTheme: "ธีมสว่างค่าเริ่มต้นสำหรับอินสแตนซ์"
instanceDefaultDarkTheme: "ธีมมืดค่าเริ่มต้นอินสแตนซ์"
instanceDefaultThemeDescription: "ป้อนรหัสธีมในรูปแบบออบเจ็กต์"
mutePeriod: "ระยะเวลาปิดเสียง"
indefinitely: "ตลอดไป"
tenMinutes: "10 นาที"
oneHour: "1 ชั่วโมง"
oneDay: "1 วัน"
oneWeek: "1 สัปดาห์"
reflectMayTakeTime: "อาจจำเป็นต้องใช้เวลาสักระยะหนึ่งจึงจะเห็นแสดงผลได้นะ"
failedToFetchAccountInformation: "ไม่สามารถเรียกดึงข้อมูลบัญชีได้"
rateLimitExceeded: "เกินขีดจำกัดอัตรา"
cropImage: "ครอบตัดรูปภาพ"
cropImageAsk: "คุณต้องการครอบตัดรูปภาพนี้อย่างงั้นหรือ?"
file: "ไฟล์"
recentNHours: "ล่าสุด {n} ชั่วโมงที่แล้ว"
recentNDays: "ล่าสุด {n} วันที่แล้ว"
noEmailServerWarning: "ไม่ได้กำหนดค่าเซิร์ฟเวอร์อีเมลนี้"
thereIsUnresolvedAbuseReportWarning: "มีรายงานที่ยังไม่ได้แก้ไข"
recommended: "แนะนำ"
check: "ตรวจสอบ"
driveCapOverrideLabel: "เปลี่ยนความจุของไดรฟ์สำหรับผู้ใช้รายนี้"
driveCapOverrideCaption: "รีเซ็ตความจุเป็นค่าเริ่มต้นโดยการป้อนค่าเป็น 0 หรือ ต่ำกว่า"
requireAdminForView: "คุณจำเป็นต้องเข้าสู่ระบบด้วยบัญชีผู้ดูแลระบบเพื่อเข้าดูสิ่งนี้"
isSystemAccount: "บัญชีที่ถูกสร้างมานั้น และถูกดำเนินการโดยอัตโนมัติด้วยระบบ"
typeToConfirm: "โปรดป้อน {x} เพื่อยืนยัน"
deleteAccount: "ลบบัญชี"
document: "เอกสาร"
numberOfPageCache: "จำนวนหน้าเพจที่แคช"
numberOfPageCacheDescription: "การเพิ่มจำนวนนี้จะช่วยเพิ่มความสะดวกให้กับผู้ใช้งาน แต่จะทำให้เซิร์ฟเวอร์โหลดมากขึ้นและต้องใช้หน่วยความจำมากขึ้นอีกด้วย"
logoutConfirm: "คุณแน่ใจว่าต้องการออกจากระบบ?"
lastActiveDate: "ใช้งานล่าสุดที่"
statusbar: "ไอคอนบนแถบสถานะ"
pleaseSelect: "ตัวเลือก"
reverse: "ย้อนกลับ"
colored: "สี"
refreshInterval: "รอบการอัพเดต"
label: "ป้ายชื่อ"
type: "รูปแบบ"
speed: "ความเร็ว"
slow: "ช้า"
fast: "เร็ว"
sensitiveMediaDetection: "การตรวจจับของสื่อ NSFW"
localOnly: "เฉพาะท้องถิ่น"
remoteOnly: "รีโมทเท่านั้น"
failedToUpload: "การอัปโหลดล้มเหลว"
cannotUploadBecauseInappropriate: "ไม่สามารถอัปโหลดไฟล์นี้ได้เนื่องจากระบบตรวจพบบางส่วนของไฟล์ว่านี้อาจจะเป็น NSFW"
cannotUploadBecauseNoFreeSpace: "การอัปโหลดนั้นล้มเหลวเนื่องจากไม่มีความจุของไดรฟ์"
beta: "เบต้า"
enableAutoSensitive: "ทำเครื่องหมาย NSFW อัตโนมัติ"
enableAutoSensitiveDescription: "อนุญาตให้ตรวจหาและทำเครื่องหมายสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่องหากเป็นไปได้ แม้ว่าตัวเลือกนี้จะถูกปิดใช้งาน แต่ก็สามารถเปิดใช้งานได้ทั้งอินสแตนซ์นี้"
activeEmailValidationDescription: "เปิดใช้งานการตรวจสอบที่อยู่อีเมลให้มีความเข้มงวดยิ่งขึ้น ซึ่งอาจจะรวมไปถึงการตรวจสอบที่อยู่อีเมล์ที่ใช้แล้วทิ้งและโดยให้พิจารณาว่าสามารถสื่อสารด้วยได้หรือไม่ เมื่อไม่เลือกระบบจะตรวจสอบเฉพาะรูปแบบของอีเมลเท่านั้น"
navbar: "แถบนำทาง"
shuffle: "สลับ"
account: "บัญชีผู้ใช้"
move: "ย้าย"
pushNotification: "การแจ้งเตือนแบบพุช"
subscribePushNotification: "เปิดการแจ้งเตือนแบบพุช"
unsubscribePushNotification: "ปิดการแจ้งเตือนแบบพุช"
pushNotificationAlreadySubscribed: "การแจ้งเตือนแบบพุชได้เปิดใช้งานแล้ว"
pushNotificationNotSupported: "เบราว์เซอร์หรืออินสแตนซ์ของคุณนั้นไม่รองรับการแจ้งเตือนแบบพุช"
sendPushNotificationReadMessage: "ลบการแจ้งเตือนแบบพุชเมื่ออ่านการแจ้งเตือนหรือข้อความที่เกี่ยวข้องแล้ว"
sendPushNotificationReadMessageCaption: "การแจ้งเตือนที่มีข้อความ \"{emptyPushNotificationMessage}\" จะแสดงขึ้นมาในช่วงระยะเวลาสั้นๆ การดำเนินการนี้อาจทำให้เพิ่มการใช้งานแบตเตอรี่ของอุปกรณ์ถ้าหากมีนะ"
windowMaximize: "ขยายใหญ่สุดแล้ว"
windowRestore: "เลิกทำ"
caption: "รายละเอียด"
loggedInAsBot: "ล็อกอินเป็นบอตอยู่ในขณะนี้"
tools: "เครื่องมือ"
cannotLoad: "ไม่สามารถโหลดได้"
_sensitiveMediaDetection:
description: "ลดความพยายามในการดูแลเซิร์ฟเวอร์ผ่านการจดจำสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่อง การทำสิ่งนี้อาจจะเพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย"
sensitivity: "การตรวจจับความไว"
sensitivityDescription: "การลดความไวนั้นจะนำไปสู่การตรวจจับที่ผิดพลาดน้อยลง (ผลบวกที่ผิดพลาด) แต่ในขณะที่การเพิ่มนั้นจะนำไปสู่การตรวจหาที่พลาดน้อยลง (ผลลบเท็จ)"
setSensitiveFlagAutomatically: "ทำเครื่องหมายว่าเป็น NSFW"
setSensitiveFlagAutomaticallyDescription: "ผลลัพธ์ของการตรวจจับภายในนั้นจะยังคงอยู่ ถึงแม้ว่าจะปิดตัวเลือกนี้"
analyzeVideos: "เปิดใช้งานวิเคราะห์ของวิดีโอ"
analyzeVideosDescription: "การวิเคราะห์วิดีโอนอกเหนือจากรูปภาพนั้น การทำสิ่งนี้จะทำให้เพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย"
_emailUnavailable:
used: "ที่อยู่อีเมลนี้ได้ถูกใช้ไปแล้ว"
format: "รูปแบบของที่อยู่อีเมลนี้ไม่ถูกต้อง"
disposable: "ที่อยู่อีเมลที่ใช้แล้วทิ้งนั้นไม่สามารถใช้ได้"
mx: "เซิร์ฟเวอร์อีเมลนี้ไม่ถูกต้อง"
smtp: "เซิร์ฟเวอร์อีเมลนี้ไม่มีการตอบสนอง"
_ffVisibility:
public: "เผยแพร่"
followers: "ปรากฏให้แก่ผู้ติดตามเท่านั้น"
private: "ส่วนตัว"
_signup:
almostThere: "เกือบจะมี"
emailAddressInfo: "โปรดกรอกอีเมลของคุณ มันจะไม่เปิดเผยต่อสาธารณะ"
emailSent: "เราได้ส่งอีเมลยืนยันไปยังที่อยู่อีเมลของคุณแล้วนะ ({email}) โปรดคลิกลิงก์ที่รวมไว้เพื่อสร้างบัญชีให้เสร็จสิ้น"
_accountDelete:
accountDelete: "ลบบัญชีผู้ใช้"
mayTakeTime: "เนื่องจากการลบบัญชีนี้จะเป็นกระบวนการที่ต้องใช้ทรัพยากรมาก จึงอาจจะต้องใช้เวลาสักครู่ถึงจะเสร็จสมบูรณ์ ทั้งนี้ขึ้นอยู่กับจำนวนเนื้อหาที่คุณสร้างและจำนวนไฟล์ที่คุณอัปโหลดนะ"
sendEmail: "เมื่อการลบบัญชีนี้เสร็จสิ้น เราอาจจะส่งอีเมลไปยังที่อยู่อีเมลของคุณที่เคยลงทะเบียนไว้กับบัญชีนี้นะ"
requestAccountDelete: "ร้องขอให้ลบบัญชี"
started: "การลบได้เริ่มต้นขึ้น"
inProgress: "ปัจจุบันกำลังดำเนินการลบอยู่"
_ad:
back: "ย้อนกลับ"
reduceFrequencyOfThisAd: "แสดงโฆษณานี้ให้น้อยลง"
_forgotPassword:
enterEmail: "ป้อนที่อยู่อีเมลที่คุณเคยใช้ในการลงทะเบียนไว้ ลิงก์ที่คุณสามารถรีเซ็ตรหัสผ่านได้นั้นจะถูกส่งไปนะ"
ifNoEmail: "ถ้าหากคุณไม่ได้ใช้อีเมลระหว่างการลงทะเบียน กรุณาติดต่อผู้ดูแลระบบอินสแตนซ์แทนนะ"
contactAdmin: "อินสแตนซ์นี้ไม่รองรับการใช้งานที่อยู่อีเมลนี้ กรุณาติดต่อผู้ดูแลระบบอินสแตนซ์เพื่อรีเซ็ตรหัสผ่านของคุณแทน"
_gallery:
my: "แกลลอรี่ของฉัน"
liked: "โพสต์ที่ถูกใจ"
like: "ชื่นชอบ"
unlike: "ลบไลค์"
_email:
_follow:
title: "ได้ติดตามคุณ"
_receiveFollowRequest:
title: "คุณได้รับคำขอติดตาม"
_plugin:
install: "ติดตั้งปลั๊กอิน"
installWarn: "กรุณาอย่าติดตั้งปลั๊กอินที่ไม่น่าเชื่อถือนะคะ"
manage: "จัดการปลั๊กอิน"
_preferencesBackups:
list: "สร้างการสำรองข้อมูล"
saveNew: "บันทึกใหม่"
loadFile: "โหลดจากไฟล์"
apply: "นำไปใช้กับอุปกรณ์นี้"
save: "บันทึก"
inputName: "กรุณาป้อนชื่อสำหรับข้อมูลสำรองนี้"
cannotSave: "การบันทึกล้มเหลว"
nameAlreadyExists: "มีข้อมูลสำรองชื่อ \"{name}\" นี้อยู่แล้ว กรุณาป้อนชื่ออื่นนะ"
applyConfirm: "คุณต้องการใช้ข้อมูลสำรอง \"{name}\" กับอุปกรณ์นี้อย่างงั้นจริงหรอ การตั้งค่าที่มีอยู่ของอุปกรณ์นี้จะถูกเขียนทับนะ"
saveConfirm: "บันทึกข้อมูลสำรองเป็น {name} มั้ย?"
deleteConfirm: "ลบข้อมูลสำรอง {name} มั้ย?"
renameConfirm: "เปลี่ยนชื่อข้อมูลสำรองนี้จาก \"{old}\" เป็น \"{new}\" หรือป่าว"
noBackups: "ไม่มีข้อมูลสำรองนะ คุณสามารถสำรองข้อมูลการตั้งค่าไคลเอนต์ของคุณบนเซิร์ฟเวอร์นี้โดยใช้ \"สร้างการสำรองข้อมูลใหม่\"ได้นะ"
createdAt: "สร้างเมื่อ: {date} {time}"
updatedAt: "อัปเดตเมื่อ: {date} {time}"
cannotLoad: "การโหลดล้มเหลว"
invalidFile: "รูปแบบไฟล์ไม่ถูกต้องนะ"
_registry:
scope: "สโคป"
key: "คีย์"
keys: "คีย์"
domain: "โดเมน"
createKey: "สร้างคีย์"
_aboutMisskey:
about: "Misskey เป็นซอฟต์แวร์โอเพ่นซอร์สที่ถูกพัฒนาโดย Syuilo ตั้งแต่ปี 2014"
contributors: "ผู้สนับสนุนหลัก"
allContributors: "ผู้มีส่วนร่วมทั้งหมด"
source: "ซอร์สโค้ด"
translation: "รับแปลภาษา Misskey"
donate: "บริจาคให้กับ Misskey"
morePatrons: "เราขอขอบคุณสำหรับความช่วยเหลือจากผู้ช่วยอื่นๆ ที่ไม่ได้ระบุไว้ที่นี่นะ ขอขอบคุณ! 🥰"
patrons: "สมาชิกพันธมิตร"
_nsfw:
respect: "ซ่อนสื่อ NSFW"
ignore: "อย่าซ่อนสื่อ NSFW"
force: "ซ่อนสื่อทั้งหมด"
_mfm:
cheatSheet: "โค้ด MFM Cheat Sheet"
intro: "MFM เป็นภาษามาร์กอัปพิเศษเฉพาะของ Misskey ที่สามารถใช้ได้ในหลายที่ คุณยังสามารถดูรายการไวยากรณ์ MFM ที่มีอยู่ทั้งหมดได้ที่นี่นะ"
dummy: "Misskey ขยายโลกของ Fediverse"
mention: "กล่าวถึง"
mentionDescription: "คุณสามารถระบุผู้ใช้โดยใช้ At-Symbol และชื่อผู้ใช้ได้นะ"
hashtag: "แฮชแท็ก"
hashtagDescription: "คุณสามารถระบุชื่อแฮชแท็กได้โดยใช้เครื่องหมายตัวเลขและข้อความได้นะ"
url: "URL"
urlDescription: "สามารถแสดง URL ได้นะ"
link: "ลิงก์"
linkDescription: "เจาะจงเฉพาะ ส่วนของข้อความที่สามารถแสดงเป็น URL ได้"
bold: "ตัวหนา"
boldDescription: "ไฮไลท์ตัวอักษรโดยทำให้หนาขึ้น"
small: "ขนาดเล็ก"
smallDescription: "แสดงผลเนื้อหาขนาดเล็กและบาง"
center: "เซ็นเตอร์"
centerDescription: "แสดงผลเนื้อหาเป็นศูนย์กลาง"
inlineCode: "โค้ด (อินไลน์)"
inlineCodeDescription: "แสดงผลการเน้นไวยากรณ์แบบอินไลน์สำหรับโค้ด (โปรแกรม)"
blockCode: "โค้ด (บล็อก)"
blockCodeDescription: "แสดงผลการเน้นไวยากรณ์สำหรับโค้ดหลายบรรทัด (โปรแกรม) ในบล็อก"
inlineMath: "คณิต (อินไลน์)"
inlineMathDescription: "แสดงผลสูตรคณิต (KaTeX) ในบรรทัด"
blockMath: "คณิต (บล็อก)"
blockMathDescription: "แสดงผลสูตรคณิตหลายบรรทัด (KaTeX) ในบล็อก"
quote: "อ้างคำพูด"
quoteDescription: "แสดงผลเนื้อหาเป็นใบเสนอราคา"
emoji: "กำหนดอีโมจิเอง"
emojiDescription: "โดยล้อมรอบชื่ออีโมจิที่กำหนดเองด้วยเครื่องหมายทวิภาค จะสามารถแสดงผลอีโมจิที่กำหนดเองได้"
search: "ค้นหา"
searchDescription: "แสดงผลกล่องค้นหาพร้อมกับข้อความที่ป้อนไว้ล่วงหน้า"
flip: "พลิก"
flipDescription: "พลิกเนื้อหาในแนวนอนหรือแนวตั้ง"
jelly: "แอนิเมชั่น (เยลลี่)"
jellyDescription: "ให้เนื้อหาเป็นแอนิเมชั่นเหมือนเยลลี่"
tada: "แอนิเมชั่น (ธาดา)"
tadaDescription: "ให้เนื้อหาเป็นแอนิเมชั่นเหมือน \"ทาด้า!\""
jump: "อนิเมชั่น (กระโดด)"
jumpDescription: "ให้เนื้อหามีภาพเคลื่อนไหวแบบกระโดด"
bounce: "อนิเมชั่น (เด้ง)"
bounceDescription: "ให้เนื้อหามีอนิเมชั่นเด้ง"
shake: "อนิเมชั่น (เขย่า)"
shakeDescription: "ให้เนื้อหามีภาพเคลื่อนไหวสั่น"
twitch: "แอนิเมชั่น (Twitch)"
twitchDescription: "ให้เนื้อหามีแอนิเมชั่นกระตุกอย่างแรง"
spin: "แอนิเมชั่น (สปิน)"
spinDescription: "ให้เนื้อหาเป็นภาพเคลื่อนไหวแบบหมุน"
x2: "ขนาดใหญ่"
x2Description: "แสดงเนื้อหาที่ใหญ่ขึ้น"
x3: "ใหญ่มาก"
x3Description: "แสดงเนื้อหาอีเว้นท์ที่ใหญ่ขึ้น"
x4: "ใหญ่อย่างไม่น่าเชื่อ"
x4Description: "แสดงผลเนื้อหาที่ใหญ่กว่าใหญ่กว่าขนาดใหญ่"
blur: "เบลอ"
blurDescription: "เบลอเนื้อหา จะแสดงผลอย่างชัดเจนต่อเมื่อวางเมาส์เหนือ"
font: "ตัวอักษร"
fontDescription: "ตั้งค่าตัวอักษรเพื่อแสดงเนื้อหาใน"
rainbow: "สายรุ้ง"
rainbowDescription: "ทำให้เนื้อหานั้นปรากฏเป็นสีรุ้ง"
sparkle: "กลิตเตอร์"
sparkleDescription: "ให้เนื้อหานั้นมีเอฟเฟกต์แบบอนุภาคประกาย"
rotate: "หมุนหน้าจอ"
rotateDescription: "เปลี่ยนเนื้อหาตามด้วยมุมที่ระบุไว้"
plain: "เรียบง่าย"
plainDescription: "ปิดการใช้งานเอฟเฟกต์ของ MFM ทั้งหมดที่มีอยู่ในเอฟเฟกต์ MFM นี้"
_instanceTicker:
none: "ไม่ต้องแสดง"
remote: "แสดงสำหรับผู้ใช้ระยะไกล"
always: "แสดงเสมอ"
_serverDisconnectedBehavior:
reload: "โหลดใหม่โดยอัตโนมัติ"
dialog: "แสดงกล่องโต้ตอบคำเตือน"
quiet: "แสดงคำเตือนที่ไม่เป็นการรบกวน"
_channel:
create: "สร้างแชนแนลใหม่"
edit: "แก้ไขแชนแนล"
setBanner: "เซตแบนเนอร์"
removeBanner: "ลบแบนเนอร์"
featured: "เทรนด์"
owned: "เจ้าของ"
following: "ติดตามแล้ว"
usersCount: "{n} ผู้เข้าร่วม"
notesCount: "{n} โน้ต"
_menuDisplay:
sideFull: "ด้านข้าง"
sideIcon: "ด้านข้าง (ไอคอน)"
top: "ท็อป"
hide: "ซ่อน"
_wordMute:
muteWords: "ปิดเสียงคำ"
muteWordsDescription: "คั่นด้วยช่องว่างสำหรับเงื่อนไข AND หรือด้วยการขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR นะ"
muteWordsDescription2: "ล้อมรอบคีย์เวิร์ดด้วยเครื่องหมายทับเพื่อใช้นิพจน์ทั่วไป"
softDescription: "ซ่อนโน้ตให้ตรงตามเงื่อนไขที่ตั้งไว้จากไทม์ไลน์"
hardDescription: "ป้องกันไม่ให้โน้ตย่อที่ตรงตามเงื่อนไขที่ตั้งไว้ไม่ให้ถูกเพิ่มลงในไทม์ไลน์ นอกจากนี้ โน้ตเหล่านี้จะไม่ถูกเพิ่มลงในไทม์ไลน์แม้ว่าจะมีการเปลี่ยนแปลงเงื่อนไขยังไงก็ตาม"
soft: "ซอฟ"
hard: "ยาก"
mutedNotes: "ปิดเสียงโน้ต"
_instanceMute:
instanceMuteDescription: "การดำเนินการนี้จะปิดเสียง\"โน้ต/รีโน้ต\"จากอินสแตนซ์ที่อยู่ในรายการ รวมถึงบันทึกของผู้ใช้ที่ตอบกลับผู้ใช้จากอินสแตนซ์ที่ปิดเสียง"
instanceMuteDescription2: "คั่นด้วยการขึ้นบรรทัดใหม่"
title: "ซ่อนโน้ตจากอินสแตนซ์ที่มีอยู่ในรายการ"
heading: "รายชื่ออินสแตนซ์ที่ถูกปิดเสียง"
_theme:
explore: "สำรวจธีม"
install: "ติดตั้งธีม"
manage: "จัดการธีม"
code: "โค้ดธีม"
description: "รายละเอียด"
installed: "{name} ได้รับการติดตั้ง"
installedThemes: "ธีมที่ติดตั้ง"
builtinThemes: "ธีมในตัว"
alreadyInstalled: "ธีมนี้ได้รับการติดตั้งแล้ว"
invalid: "รูปแบบของธีมนี้ไม่ถูกต้องนะ"
make: "ทำธีม"
base: "ฐาน"
addConstant: "เพิ่มค่าคงที่"
constant: "ตัวแปร"
defaultValue: "ค่าเริ่มต้น"
color: "สี"
refProp: "อ้างอิงคุณสมบัติ"
refConst: "อ้างอิงค่าคงที่"
key: "คีย์"
func: "ฟังก์ชัน"
funcKind: "ประเภทฟังก์ชัน"
argument: "อากิวเม้นต์"
basedProp: "ทรัพย์สินอ้างอิง"
alpha: "ความทึบแสง"
darken: "มืดลง"
lighten: "สว่าง"
inputConstantName: "ป้อนชื่อสำหรับค่าคงที่นี้"
importInfo: "ถ้าหากต้องการป้อนโค้ดที่นี่ คุณยังสามารถนำเข้าไปยังโปรแกรมแก้ไขธีมได้"
deleteConstantConfirm: "คุณต้องการลบค่าคงที่ {const} หรือป่าว?"
keys:
accent: "เน้น"
bg: "ภาพพื้นหลัง"
fg: "ข้อความ"
focus: "โฟกัส"
indicator: "ตัวบ่งชี้"
panel: "แผงควบคุม"
shadow: "เงา"
header: "ส่วนหัว"
navBg: "พื้นหลังแถบด้านข้าง"
navFg: "ข้อความแถบด้านข้าง"
navHoverFg: "ข้อความแถบด้านข้าง (โฮเวอร์)"
navActive: "ข้อความแถบด้านข้าง (ใช้งานอยู่)"
navIndicator: "ตัวระบุแถบด้านข้าง"
link: "ลิงก์"
hashtag: "แฮชแท็ก"
mention: "กล่าวถึง"
mentionMe: "ได้กล่าวถึง (ฉัน)"
renote: "รีโน้ต"
modalBg: "พื้นหลังโมดอล"
divider: "ตัวแบ่ง"
scrollbarHandle: "ที่จับแถบเลื่อน"
scrollbarHandleHover: "ที่จับแถบเลื่อน (โฮเวอร์)"
dateLabelFg: "ข้อความกำกับป้ายวันที่"
infoBg: "ข้อมูลพื้นหลัง"
infoFg: "ข้อความข้อมูล"
infoWarnBg: "คำเตือนพื้นหลัง"
infoWarnFg: "คำเตือนข้อความ"
cwBg: "ปุ่ม CW พื้นหลัง"
cwFg: "ปุ่ม CW ข้อความ"
cwHoverBg: "ปุ่ม CW พื้นหลัง (โฮเวอร์)"
toastBg: "ประวัติการแจ้งเตือน"
toastFg: "ข้อความแจ้งเตือน"
buttonBg: "ปุ่มพื้นหลัง"
buttonHoverBg: "ปุ่มพื้นหลัง (โฮเวอร์)"
inputBorder: "เส้นขอบของช่องป้อนข้อมูล"
listItemHoverBg: "รายการไอเทมพื้นหลัง (โฮเวอร์)"
driveFolderBg: "พื้นหลังโฟลเดอร์ไดรฟ์"
wallpaperOverlay: "วอลล์เปเปอร์ซ้อนทับ"
badge: "ตรา"
messageBg: "พื้นหลังแชท"
accentDarken: "เน้น (มืด)"
accentLighten: "เน้น (สว่าง)"
fgHighlighted: "ข้อความที่ไฮไลต์"
_sfx:
note: "หมายเหตุ"
noteMy: "โน้ตของตัวเอง"
notification: "การเเจ้งเตือน"
chat: "แชท"
chatBg: "แชท (พื้นหลัง)"
antenna: "เสาอากาศ"
channel: "การแจ้งเตือนช่อง"
_ago:
future: "อนาคต"
justNow: "เมื่อกี๊นี้"
secondsAgo: "{n} วินาทีที่แล้ว"
minutesAgo: "{n} นาทีที่แล้ว"
hoursAgo: "{n} ชั่วโมงที่แล้ว"
daysAgo: "{n} วันที่ผ่านมา"
weeksAgo: "{n} สัปดาห์ที่แล้ว"
monthsAgo: "{n} เดือนที่แล้ว"
yearsAgo: "{n} ปีที่ผ่านมา"
_time:
second: "วินาที"
minute: "นาที"
hour: "ชั่วโมง"
day: "วัน"
_tutorial:
title: "วิธีการใช้งาน Misskey"
step1_1: "ยินดีต้อนรับค่ะ!"
step1_2: "หน้านี้เรียกว่า \"ไทม์ไลน์\" มันจะแสดง \"โน้ตย่อ\" ที่เรียงลำดับตามลำดับเวลาของคนที่คุณ \"ติดตาม\""
step1_3: "ไทม์ไลน์ของคุณนั้นว่างเปล่า เนื่องจากคุณยังไม่ได้โพสต์โน้ตย่อหรือไม่ได้ติดตามใครเลย"
step2_1: "มาตั้งค่าโปรไฟล์ของคุณให้เสร็จก่อนเขียนโน้ตย่อหรือติดตามใครก็ได้"
step2_2: "การให้ข้อมูลบางอย่างเกี่ยวกับตัวคุณนั้น จะทำให้ผู้อื่นทราบว่าต้องการดูโน้ตย่อของคุณหรือติดตามคุณได้ง่ายขึ้น"
step3_1: "ตั้งค่าโปรไฟล์ของคุณเสร็จแล้ว?"
step3_2: "จากนั้นลองโพสต์โน้ตกันต่อไป คุณสามารถทำได้โดยกดปุ่มที่มีไอคอนดินสอบนหน้าจอนะ"
step3_3: "กรอกโมดอลแล้วกดปุ่มด้านบนขวาเพื่อโพสต์"
step3_4: "ไม่มีอะไรจะพูดงั้นหรอ ลอง \"เพียงแค่ตั้งค่าว่า Misskey ของฉัน\"!"
step4_1: "เสร็จสิ้นการโพสต์โน้ตย่อแรกของคุณแล้วอย่างงั้นหรอ?"
step4_2: "ไชโย! ตอนนี้โน้ตย่อแรกของคุณได้ปรากฏบนไทม์ไลน์ของคุณแล้วนะ"
step5_1: "ตอนนี้ มาลองทำไทม์ไลน์เพิ่มเติมของคุณให้ดูมีชีวิตชีวามากขึ้นโดยการติดตามคนอื่น"
step5_2: "{featured} จะแสดงโน้ตยอดนิยมให้คุณเห็นในกรณีนี้ {explore} จะช่วยให้คุณค้นหาผู้ใช้ยอดนิยมได้ ลองหาคนที่คุณต้องการติดตามที่นั่นสิ!"
step5_3: "หากต้องการติดตามผู้ใช้รายอื่น ให้คลิกที่ไอคอนและกดปุ่ม \"ติดตาม\" บนโปรไฟล์ของพวกเขาได้เลยจ้า"
step5_4: "หากผู้ใช้รายอื่นมีไอคอนแม่กุญแจที่อยู่ข้างชื่อ อาจต้องใช้เวลาสักระยะกว่าที่ผู้ใช้รายนั้นจะอนุมัติคำขอติดตามของคุณ"
step6_1: "คุณสามารถเห็นโน้ตย่อของผู้ใช้รายอื่นบนไทม์ไลน์ของคุณได้แล้วตอนนี้"
step6_2: "คุณยังสามารถใส่ \"ปฏิกิริยา\" ลงในโน้ตของคนอื่นเพื่อตอบกลับได้อย่างรวดเร็ว"
step6_3: "หากต้องการแนบ \"ปฏิกิริยา\" ให้กดเครื่องหมาย \"+\" ในโน้ตของผู้ใช้รายอื่นแล้วเลือกอีโมจิที่คุณต้องการโต้ตอบด้วย"
step7_1: "ยินดีด้วยนะ! คุณได้เสร็จสิ้นการกวดวิชาพื้นฐานของ Misskey แล้ว"
step7_2: "ถ้าหากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับ Misskey ให้ลองใช้ส่วน {help}"
step7_3: "ตอนนี้ ถ้าอย่างนั้นก็ขอให้โชคดีและสนุกกับ Misskey! 🚀"
step8_1: "สุดท้ายนี้นายต้องการเปิดใช้งานการแจ้งเตือนแบบพุชหรือป่าว?"
step8_2: "การเปิดใช้งานสิ่งเหล่านี้ จะช่วยให้คุณนั้นได้รับการแจ้งเตือนสำหรับการกล่าวถึง การแสดงรีแอคชั่น การติดตาม ฯลฯ เป็นต้น ถึงแม้ว่าจะไม่ได้เปิด Misskey ก็ตาม"
step8_3: "คุณสามารถเปลี่ยนการตั้งค่านี้ในภายหลังได้ตลอดเวลานะ"
_2fa:
alreadyRegistered: "คุณได้ลงทะเบียนอุปกรณ์ยืนยันตัวตนแบบ 2 ชั้นแล้ว"
registerDevice: "ลงทะเบียนอุปกรณ์ใหม่"
registerKey: "ลงทะเบียนรหัสความปลอดภัย"
step1: "ขั้นตอนแรก ติดตั้งแอปยืนยันตัวตน (เช่น {a} หรือ {b}) บนอุปกรณ์ของคุณ"
step2: "จากนั้นสแกนรหัส QR ที่แสดงบนหน้าจอนี้"
step2Url: "คุณยังสามารถป้อนบน URL นี้หากคุณใช้โปรแกรมเดสก์ท็อป:"
step3: "ป้อนโทเค็นที่แอปของคุณให้มาเพื่อเสร็จสิ้นการตั้งค่า"
step4: "นับจากนี้เป็นต้นไปการพยายามเข้าสู่ระบบในอนาคตนั้น อาจจะต้องขอโทเค็นในการเข้าสู่ระบบดังกล่าว"
securityKeyInfo: "นอกจากนี้การตรวจสอบความถูกต้องด้วยลายนิ้วมือหรือ PIN แล้ว คุณยังสามารถตั้งค่าการตรวจสอบสิทธิ์ผ่านคีย์ความปลอดภัยของฮาร์ดแวร์ที่รองรับ FIDO2 เพื่อเพิ่มความปลอดภัยให้กับบัญชีของคุณ"
_permissions:
"read:account": "ดูข้อมูลบัญชีของคุณ"
"write:account": "แก้ไขข้อมูลบัญชีของคุณ"
"read:blocks": "ดูรายชื่อผู้ใช้ที่ถูกบล็อกของคุณ"
"write:blocks": "แก้ไขรายชื่อผู้ใช้ที่ถูกบล็อกของคุณ"
"read:drive": "เข้าถึงไฟล์และโฟลเดอร์ในไดรฟ์ของคุณ"
"write:drive": "แก้ไขหรือลบไฟล์และโฟลเดอร์ในไดรฟ์ของคุณ"
"read:favorites": "ดูรายการโปรด"
"write:favorites": "แก้ไขรายการโปรด"
"read:following": "ดูข้อมูลว่าใครที่คุณติดตาม"
"write:following": "ติดตามหรือเลิกติดตามบัญชีอื่น"
"read:messaging": "ดูแชทของคุณ"
"write:messaging": "เขียนหรือลบข้อความแชท"
"read:mutes": "ดูรายชื่อผู้ใช้ที่ปิดเสียงของคุณ"
"write:mutes": "แก้ไขรายชื่อผู้ใช้ที่ถูกปิดเสียง"
"write:notes": "เขียนหรือลบโน้ต"
"read:notifications": "ดูการแจ้งเตือนของคุณ"
"write:notifications": "จัดการแจ้งเตือนของคุณ"
"read:reactions": "ดูปฏิกิริยาของคุณ"
"write:reactions": "แก้ไขปฏิกิริยาของคุณ"
"write:votes": "โหวตบนสำรวจความคิดเห็น"
"read:pages": "ดูหน้า"
"write:pages": "แก้ไขหรือลบเพจของคุณ"
"read:page-likes": "ดูไลค์ของคุณบนเพจ"
"write:page-likes": "แก้ไขการถูกใจของคุณบนเพจ"
"read:user-groups": "ดูกลุ่มผู้ใช้ของคุณ"
"write:user-groups": "แก้ไขหรือลบกลุ่มผู้ใช้ของคุณ"
"read:channels": "ดูแชนแนลของคุณ"
"write:channels": "แก้ไขแชนแนลของคุณ"
"read:gallery": "ดูแกลเลอรี่"
"write:gallery": "แก้ไขแกลเลอรี่ของคุณ"
"read:gallery-likes": "ดูรายการโพสต์ในแกลเลอรีที่ชอบของคุณ"
"write:gallery-likes": "แก้ไขรายการโพสต์ในแกลเลอรีที่ชอบของคุณ"
_auth:
shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?"
shareAccessAsk: "คุณแน่ใจแล้วจริงๆหรอว่าต้องการอนุญาตให้แอปพลิเคชันนี้เข้าถึงบัญชีของคุณแน่ใจแล้วหรอ?"
permissionAsk: "แอปพลิเคชันนี้ขอสิทธิ์ดังต่อไปนี้"
pleaseGoBack: "กรุณากลับไปที่แอปพลิเคชัน"
callback: "กำลังกลับไปที่แอปพลิเคชัน"
denied: "ปฏิเสธการเข้าใช้"
_antennaSources:
all: "โน้ตทั้งหมด"
homeTimeline: "โน้ตจากผู้ใช้ที่ติดตาม"
users: "โน้ตจากผู้ใช้ที่เฉพาะเจาะจง"
userList: "โน้ตจากรายชื่อผู้ใช้ที่ระบุ"
userGroup: "โน้ตจากผู้ใช้ในกลุ่มที่ระบุ"
_weekday:
sunday: "วันอาทิตย์"
monday: "วันจันทร์"
tuesday: "วันอังคาร"
wednesday: "วันพุธ"
thursday: "วันพฤหัสบดี"
friday: "วันศุกร์"
saturday: "วันเสาร์"
_widgets:
memo: "โน้ตแปะ"
notifications: "การเเจ้งเตือน"
timeline: "ไทม์ไลน์"
calendar: "ปฏิทิน"
trends: "กำลังมาแรง"
clock: "นาฬิกา"
rss: "โปรแกรมอ่าน RSS"
rssTicker: "RSS-ทิกเกอร์"
activity: "กิจกรรม"
photos: "รูปภาพ"
digitalClock: "นาฬิกาดิจิตอล"
unixClock: "นาฬิกา UNIX"
federation: "สหพันธ์"
instanceCloud: "อินสแตนซ์คลาวด์"
postForm: "แบบฟอร์มการโพสต์"
slideshow: "แสดงภาพนิ่ง"
button: "ปุ่ม"
onlineUsers: "ผู้ใช้ที่ออนไลน์"
jobQueue: "คิวงาน"
serverMetric: "ตัวชี้วัดเซิร์ฟเวอร์"
aiscript: "AiScript คอนโซล"
aichan: "เอไอ"
userList: "รายชื่อผู้ใช้"
_userList:
chooseList: "เลือกรายการ"
_cw:
hide: "ซ่อน"
show: "โหลดเพิ่มเติม"
chars: "{count} ตัวอักษร"
files: "{count} ไฟล์"
_poll:
noOnlyOneChoice: "จำเป็นต้องมีอย่างน้อยสองตัวเลือก"
choiceN: "ตัวเลือก {n}"
noMore: "คุณไม่สามารถเพิ่มตัวเลือกอื่นได้"
canMultipleVote: "สามารถตอบได้หลายคำตอบ"
expiration: "สิ้นสุดการสำรวจความคิดเห็น"
infinite: "ไม่ต้องเลย"
at: "จบที่..."
after: "สิ้นสุดหลัง..."
deadlineDate: "วันสิ้นสุด"
deadlineTime: "ชั่วโมง"
duration: "ระยะเวลา"
votesCount: "{n} คะแนนเสียง"
totalVotes: "{n} คะแนนเสียงทั้งหมด"
vote: "โหวต"
showResult: "ดูผลลัพธ์"
voted: "โหวตแล้ว"
closed: "สิ้นสุดแล้ว"
remainingDays: "{d} วัน(s) {h} ชั่วโมง(s) ที่เหลืออยู่"
remainingHours: "{h} ชั่วโมง(s) {m} นาที(s) ที่เหลืออยู่"
remainingMinutes: "{m} นาที(s) {s} วินาที(s) ที่เหลืออยู่"
remainingSeconds: "{s} นาที(s) ที่เหลืออยู่"
_visibility:
public: "สาธารณะ"
publicDescription: "โน้ตของคุณจะปรากฏแก่ผู้ใช้ทุกคน"
home: "หน้าแรก"
homeDescription: "โพสลงไทม์ไลน์ที่บ้านเท่านั้น"
followers: "ผู้ติดตาม"
followersDescription: "ทำให้ผู้ติดตามนั้นมองเห็นแค่คุณเท่านั้น"
specified: "ไดเร็ค"
specifiedDescription: "ทำให้มองเห็นได้เฉพาะผู้ใช้ที่ระบุเท่านั้น"
localOnly: "เฉพาะท้องถิ่น"
localOnlyDescription: "ผู้ใช้ระยะไกลนั้นไม่สามารถมองเห็นได้"
_postForm:
replyPlaceholder: "ตอบกลับโน้ตนี้..."
quotePlaceholder: "อ้างโน้ตนี้..."
channelPlaceholder: "โพสต์ลงช่อง..."
_placeholders:
a: "คุณเป็นอะไรไปหรอ?"
b: "เกิดอะไรขึ้นรอบตัวคุณ?"
c: "คุณกำลังคิดอะไรอยู่?"
d: "คุณต้องการจะพูดอะไร?"
e: "เริ่มเขียน..."
f: "กำลังรอให้คุณเขียน..."
_profile:
name: "ชื่อ"
username: "ชื่อผู้ใช้"
description: "ประวัติ"
youCanIncludeHashtags: "คุณยังสามารถใส่แฮชแท็กในประวัติของคุณได้นะ"
metadata: "ข้อมูลเพิ่มเติม"
metadataEdit: "แก้ไขข้อมูลเพิ่มเติม"
metadataDescription: "ใช้สิ่งเหล่านี้ คุณสามารถแสดงฟิลด์ข้อมูลเพิ่มเติมในโปรไฟล์ของคุณ"
metadataLabel: "ป้ายชื่อ"
metadataContent: "เนื้อหา"
changeAvatar: "เปลี่ยนอวาตาร์"
changeBanner: "เปลี่ยนแบนเนอร์"
_exportOrImport:
allNotes: "โน้ตทั้งหมด"
favoritedNotes: "บันทึกที่ชื่นชอบ"
followingList: "กำลังติดตาม"
muteList: "ปิดเสียง"
blockingList: "บล็อค"
userLists: "รายการ"
excludeMutingUsers: "ยกเว้นผู้ใช้ที่ปิดเสียง"
excludeInactiveUsers: "ยกเว้นผู้ใช้ที่ไม่ได้ใช้งาน"
_charts:
federation: "สหพันธ์"
apRequest: "คำขอ"
usersIncDec: "ความแตกต่างของจำนวนผู้ใช้งาน"
usersTotal: "จำนวนผู้ใช้งานทั้งหมด"
activeUsers: "จำนวนผู้ใช้งานที่ยังมีความเคลื่อนไหวอยู่"
notesIncDec: "ความแตกต่างของจำนวนโน้ต"
localNotesIncDec: "ความแตกต่างของจำนวนโน้ตท้องถิ่น"
remoteNotesIncDec: "ความแตกต่างของจำนวนโน้ตระยะไกล"
notesTotal: "จำนวนโน้ตทั้งหมด"
filesIncDec: "ความแตกต่างของจำนวนไฟล์"
filesTotal: "จำนวนไฟล์ทั้งหมด"
storageUsageIncDec: "ความแตกต่างในการใช้พื้นที่เก็บข้อมูล"
storageUsageTotal: "การใช้พื้นที่เก็บข้อมูลทั้งหมด"
_instanceCharts:
requests: "คำขอ"
users: "ความแตกต่างของจำนวนผู้ใช้งาน"
usersTotal: "จำนวนผู้ใช้งานสะสม"
notes: "ความแตกต่างของจำนวนโน้ต"
notesTotal: "จำนวนโน้ตสะสม"
ff: "ความแตกต่างของจำนวนผู้ใช้ที่ติดตาม / ผู้ติดตาม"
ffTotal: "จำนวนผู้ใช้งานที่ติดตามสะสม / ผู้ติดตาม"
cacheSize: "ความแตกต่างในขนาดของแคช"
cacheSizeTotal: "ขนาดแคชรวมที่สะสม"
files: "ความแตกต่างของจำนวนไฟล์"
filesTotal: "จำนวนไฟล์สะสม"
_timelines:
home: "หน้าแรก"
local: "ในพื้นที่"
social: "โซเชี่ยล"
global: "ทั่วโลก"
_pages:
newPage: "สร้างหน้าเพจใหม่"
editPage: "แก้ไขหน้าเพจ"
readPage: "กำลังดูแหล่งที่มาของเพจนี้"
created: "สร้างหน้าเพจสำเร็จเรียบร้อยแล้ว"
updated: "แก้ไขหน้าเพจสำเร็จเรียบร้อยแล้ว"
deleted: "ลบหน้าเพจสำเร็จเรียบร้อยแล้ว"
pageSetting: "การตั้งค่าหน้า"
nameAlreadyExists: "URL ของหน้าที่ระบุนั้นมีอยู่แล้ว"
invalidNameTitle: "URL ของหน้าที่ระบุนั้นไม่ถูกต้อง"
invalidNameText: "ตรวจสอบให้แน่ใจนะว่าชื่อหน้าไม่ว่างเปล่า"
editThisPage: "แก้ไขเพจนี้"
viewSource: "ดูต้นฉบับ"
viewPage: "ดูหน้า"
like: "ถูกใจ"
unlike: "ลบไลค์"
my: "หน้าเพจของฉัน"
liked: "หน้าเพจที่ถูกใจ"
featured: "เป็นที่นิยม"
inspector: "ตัวตรวจสอบ"
contents: "เนื้อหา"
content: "บล็อคหน้าเพจ"
variables: "ตัวแปร"
title: "หัวข้อ"
url: "URL ของหน้า"
summary: "สรุปเพจ"
alignCenter: "เซ็นเตอร์"
hideTitleWhenPinned: "ซ่อนชื่อหน้าเพจเมื่อปักหมุดไว้ที่โปรไฟล์"
font: "ตัวอักษร"
fontSerif: "Serif"
fontSansSerif: "Sans Serif"
eyeCatchingImageSet: "ตั้งค่าภาพขนาดย่อ"
eyeCatchingImageRemove: "ลบภาพขนาดย่อ"
chooseBlock: "เพิ่มบล็อค"
selectType: "เลือกชนิด"
contentBlocks: "เนื้อหา"
inputBlocks: "อินพุต"
specialBlocks: "พิเศษ"
blocks:
text: "ข้อความ"
textarea: "พื้นที่ข้อความ"
section: "ประเภท"
image: "รูปภาพ"
button: "ปุ่ม"
note: "โน้ตที่ฝังตัว"
_note:
id: "โน้ต ID"
idDescription: "คุณสามารถจะวาง URL ของโน้ตที่นี่ก็ได้นะ"
detailed: "มุมมองโดยละเอียด"
_relayStatus:
requesting: "กำลังรอการยืนยัน"
accepted: "ได้รับการอนุมัติ"
rejected: "ถูกปฏิเสธ"
script:
categories:
list: "รายการ"
blocks:
_join:
arg1: "รายการ"
_randomPick:
arg1: "รายการ"
_dailyRandomPick:
arg1: "รายการ"
_seedRandomPick:
arg2: "รายการ"
_pick:
arg1: "รายการ"
_listLen:
arg1: "รายการ"
types:
array: "รายการ"
_notification:
fileUploaded: "ไฟล์ถูกอัพโหลดแล้วน่ะ"
youGotMention: "{name} กล่าวถึงคุณ"
youGotReply: "{name} ตอบกลับถึงคุณ"
youGotQuote: "{name} อ้างถึงคุณ"
youRenoted: "รีโน้ตจาก {name}"
youGotPoll: "{name} โหวตบนแบบสำรวจความคิดเห็นของคุณ"
youGotMessagingMessageFromUser: "{name} ได้ส่งข้อความแชทถึงคุณ"
youGotMessagingMessageFromGroup: "ข้อความแชทถูกส่งไปยัง {name} กลุ่ม"
youWereFollowed: "ได้ติดตามคุณ"
youReceivedFollowRequest: "คุณมีคำขอติดตามใหม่น่ะ"
yourFollowRequestAccepted: "คำขอติดตามของคุณได้รับการยอมรับแล้วน่ะ"
youWereInvitedToGroup: "{userName} ได้เชิญคุณเข้ากลุ่ม"
pollEnded: "โพลสำรวจความคิดเห็นผลลัพธ์มีพร้อมใช้งาน"
unreadAntennaNote: "เสาอากาศ {name}"
emptyPushNotificationMessage: "การแจ้งเตือนแบบพุชได้รับการอัพเดทแล้ว"
_types:
all: "ทั้งหมด"
follow: "กำลังติดตาม"
mention: "กล่าวถึง"
reply: "ตอบกลับ"
renote: "รีโน้ต"
quote: "อ้างคำพูด"
reaction: "รีแอคชั่น"
pollVote: "จำนวนโหวตที่ได้รับ"
pollEnded: "โพลนี้สิ้นสุดลงแล้ว"
receiveFollowRequest: "ได้รับคำขอติดตาม\n"
followRequestAccepted: "ยอมรับคำขอติดตาม"
groupInvited: "ได้รับคำเชิญเข้ากลุ่ม"
app: "การแจ้งเตือนจากแอปที่มีลิงก์"
_actions:
followBack: "ติดตามกลับด้วย"
reply: "ตอบกลับ"
renote: "รีโน้ต"
_deck:
alwaysShowMainColumn: "แสดงคอลัมน์หลักเสมอ"
columnAlign: "จัดแนวคอลัมน์"
addColumn: "เพิ่มคอลัมน์"
configureColumn: "ตั้งค่าคอลัมน์"
swapLeft: "ขยับไปทางซ้าย"
swapRight: "ขยับไปทางขวา"
swapUp: "เลื่อนขึ้น"
swapDown: "เลื่อนลง"
stackLeft: "กองกับคอลัมน์ด้านซ้าย"
popRight: "ป๊อปคอลัมน์ไปทางขวา"
profile: "โปรไฟล์"
newProfile: "โปรไฟล์ใหม่"
deleteProfile: "ลบโปรไฟล์"
introduction: "สร้างอินเทอร์เฟซที่สมบูรณ์แบบสำหรับคุณโดยจัดเรียงคอลัมน์ได้อย่างอิสระ!"
introduction2: "คลิกที่เครื่องหมาย + ทางขวาของหน้าจอเพื่อเพิ่มคอลัมน์ใหม่ทุกครั้งที่คุณต้องการ"
widgetsIntroduction: "กรุณาเลือก \"แก้ไขวิดเจ็ต\" ในเมนูคอลัมน์และเพิ่มวิดเจ็ต"
_columns:
main: "หลัก"
widgets: "วิดเจ็ต"
notifications: "การเเจ้งเตือน"
tl: "ไทม์ไลน์"
antenna: "เสาอากาศ"
list: "รายการ"
mentions: "พูดถึง"
direct: "ไดเร็ค"

View File

@@ -1,8 +1,7 @@
---
_lang_: "Українська"
headlineMisskey: "Мережа, з’єднана нотатками"
headlineMisskey: "Мережа об'єднана записами"
introMisskey: "Ласкаво просимо! Misskey - децентралізована служба мікроблогів з відкритим кодом.\nСтворюйте \"нотатки\", щоб поділитися тим, що відбувається, і розповісти всім про себе 📡\nЗа допомогою \"реакцій\" ви також можете швидко висловити свої почуття щодо нотаток інших 👍\nДосліджуймо новий світ! 🚀"
poweredByMisskeyDescription: "{name} є одним із сервісів (які називаються інстансами Misskey), що використовують платформу з відкритим вихідним кодом <b>Misskey</b>."
monthAndDay: "{month}/{day}"
search: "Пошук"
notifications: "Сповіщення"
@@ -13,7 +12,6 @@ fetchingAsApObject: "Отримуємо з федіверсу..."
ok: "OK"
gotIt: "Зрозуміло!"
cancel: "Скасувати"
noThankYou: "Не зараз"
enterUsername: "Введіть ім'я користувача"
renotedBy: "Поширено {user}"
noNotes: "Немає нотаток"
@@ -54,7 +52,6 @@ searchUser: "Пошук користувачів"
reply: "Відповісти"
loadMore: "Показати більше"
showMore: "Показати більше"
showLess: "Закрити"
youGotNewFollower: "Новий підписник"
receiveFollowRequest: "Отримано запит на підписку"
followRequestAccepted: "Підписка прийнята"
@@ -206,7 +203,6 @@ done: "Готово"
processing: "Обробка"
preview: "Попередній перегляд"
default: "За умовчанням"
defaultValueIs: "За промовчанням: {value}"
noCustomEmojis: "Немає нетипових емоджі"
noJobs: "Немає завдань"
federating: "Федерується"
@@ -350,10 +346,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Увімкнути reCAPTCHA"
recaptchaSiteKey: "Ключ сайту"
recaptchaSecretKey: "Секретний ключ"
turnstile: "Турнікет"
enableTurnstile: "Увімкнути турнікет"
turnstileSiteKey: "Ключ сайту"
turnstileSecretKey: "Секретний ключ"
avoidMultiCaptchaConfirm: "Використання кількох систем Captcha може спричинити перешкоди між ними. Бажаєте вимкнути інші активні системи Captcha? Якщо ви хочете, щоб вони залишалися ввімкненими, натисніть «Скасувати»."
antennas: "Антени"
manageAntennas: "Налаштування антен"
@@ -364,7 +356,7 @@ antennaExcludeKeywords: "Винятки"
antennaKeywordsDescription: "Розділення ключових слів пробілами для \"І\" або з нової лінійки для \"АБО\""
notifyAntenna: "Сповіщати про нові нотатки"
withFileAntenna: "Тільки нотатки з вкладеними файлами"
enableServiceworker: "Увімкнути ServiceWorker"
enableServiceworker: "Ввімкнути ServiceWorker"
antennaUsersDescription: "Список імя користувачів в стопчик"
caseSensitive: "З урахуванням регістру"
withReplies: "Включаючи відповіді"
@@ -389,7 +381,6 @@ administrator: "Адмін"
token: "Токен"
twoStepAuthentication: "Двохфакторна аутентифікація"
moderator: "Модератор"
moderation: "Модерація"
nUsersMentioned: "Згадали: {n}"
securityKey: "Ключ захисту"
securityKeyName: "Назва ключа"
@@ -456,6 +447,7 @@ language: "Мова"
uiLanguage: "Мова інтерфейсу"
groupInvited: "Запрошення до групи"
aboutX: "Про {x}"
useOsNativeEmojis: "Використовувати емодзі ОС"
disableDrawer: "Не використовувати висувні меню"
youHaveNoGroups: "Немає груп"
joinOrCreateGroup: "Отримуйте запрошення до груп або створюйте свої власні групи."
@@ -508,7 +500,6 @@ deleteAll: "Видалити все"
showFixedPostForm: "Показати форму запису над стрічкою новин."
newNoteRecived: "Є нові нотатки"
sounds: "Звуки"
sound: "Звуки"
listen: "Слухати"
none: "Відсутній"
showInPage: "Показати на сторінці"
@@ -568,7 +559,6 @@ author: "Автор"
leaveConfirm: "Зміни не збережені. Ви дійсно хочете скасувати зміни?"
manage: "Управління"
plugins: "Плагіни"
preferencesBackups: "Бекап налаштувань"
deck: "Дек"
undeck: "Залишити Дек"
useBlurEffectForModal: "Ефект розмиття під модальними діалогами"
@@ -653,8 +643,6 @@ clip: "Добірка"
createNew: "Створити новий"
optional: "Необов'язково"
createNewClip: "Створити нотатку"
unclip: "Незакріплений"
confirmToUnclipAlreadyClippedNote: "Ця нотатка вже включена до кліпу \"{name}\". Ви хочете виключити нотатку з цього кліпу?"
public: "Публічний"
i18nInfo: "Misskey перекладається на різні мови волонтерами. Ви можете допомогти: {link}"
manageAccessTokens: "Керування токенами доступу"
@@ -737,214 +725,33 @@ publish: "Опублікувати"
inChannelSearch: "Пошук за каналом"
useReactionPickerForContextMenu: "Відкривати палітру реакцій правою кнопкою"
typingUsers: "Стук клавіш. Це {users}…"
jumpToSpecifiedDate: "Перейти до конкретної дати"
showingPastTimeline: "Відображення минулих часових шкал."
clear: "Очистити"
markAllAsRead: "Позначити всі як прочитані"
goBack: "Назад"
unlikeConfirm: "Бажаєте відписатися від подібних?"
fullView: "Повний перегляд"
quitFullView: "Повний перегляд"
addDescription: "Додатковий опис."
userPagePinTip: "Ви можете зберегти відображені тут нотатки, вибравши \"Закріпити\" в меню окремих нотаток."
notSpecifiedMentionWarning: "Згадки, не включені до пункту призначення"
info: "Інформація"
userInfo: "Інформація про користувача"
unknown: "Невідомо"
onlineStatus: "Онлайн статус"
hideOnlineStatus: "Приховати онлайн статус."
online: "Онлайн"
active: "Активовано"
offline: "Офлайн"
notRecommended: "Не рекомендовано"
botProtection: "Захист від ботів"
instanceBlocking: "Заблоковані інстанси"
selectAccount: "Виберіть акаунт"
switchAccount: "Змінити акаунт"
enabled: "Увімкнено"
disabled: "Вимкнено"
quickAction: "Швидкі дії"
user: "Користувачі"
administration: "Управління"
accounts: "Акаунти"
switch: "Перемкнути"
noMaintainerInformationWarning: "Інформація про адміністраторів не налаштована"
noBotProtectionWarning: "Захист від ботів не налаштовано"
configure: "Налаштувати"
postToGallery: "Допис у галерею"
gallery: "Галерея"
recentPosts: "Нещодавні дописи"
popularPosts: "Популярні дописи"
shareWithNote: "Поділитися нотаткою"
ads: "Реклама"
expiration: "Опитування закінчується"
memo: "Примітка"
priority: "Пріоритет"
high: "Високий"
middle: "Середній"
low: "Низький"
emailNotConfiguredWarning: "Email адреса не вказана"
ratio: "Співвідношення"
previewNoteText: "Показати передогляд"
customCss: "Власний CSS"
global: "Глобальна"
squareAvatars: "Квадратні аватарки"
sent: "Відправити"
received: "Отримано"
searchResult: "Результати пошуку"
hashtags: "Хештеґ"
troubleshooting: "Усунення проблем"
useBlurEffect: "Ефекти розмиття в інтерфейсі"
learnMore: "Докладніше"
misskeyUpdated: "Misskey оновлено!"
whatIsNew: "Показати зміни"
translate: "Переклад"
translatedFrom: "Переклад з {x}"
accountDeletionInProgress: "Наразі триває видалення акаунту"
aiChanMode: "Режим Ai"
keepCw: "Зберігати попередження щодо вмісту"
pubSub: "Акаунти Pub/Sub"
lastCommunication: "Останній зв'язок"
resolved: "Вирішено"
unresolved: "Не вирішено"
breakFollow: "Видалити підписника"
itsOn: "Увімкнено"
itsOff: "Вимкнено"
emailRequiredForSignup: "Вимагати email адресу для реєстрації"
unread: "Непрочитане"
filter: "Фільтр"
controlPanel: "Панель керування"
manageAccounts: "Керування акаунтом"
makeReactionsPublic: "Зробити історію реакцій публічною"
makeReactionsPublicDescription: "Це зробить список усіх ваших попередніх реакцій загальнодоступним."
classic: "Класичний"
muteThread: "Приглушити тред"
unmuteThread: "Скасувати глушіння"
ffVisibility: "Видимість підписок/підписників"
continueThread: "Показати продовження треду"
deleteAccountConfirm: "Це незворотно видалить ваш акаунт. Продовжити?"
incorrectPassword: "Неправильний пароль."
voteConfirm: "Підтверджуєте свій голос за \"{choice}\"?"
hide: "Сховати"
leaveGroup: "Залишити групу"
leaveGroupConfirm: "Залишити \"{name}\"?"
welcomeBackWithName: "З поверненням, {name}!"
clickToFinishEmailVerification: "Натисніть [{ok}], щоб завершити перевірку email."
overridedDeviceKind: "Тип пристрою"
smartphone: "Смартфон"
tablet: "Планшет"
auto: "Автоматично"
themeColor: "Колір теми"
size: "Розмір"
numberOfColumn: "Кількість стовпців"
searchByGoogle: "Пошук"
instanceDefaultLightTheme: "Світла тема за промовчанням"
instanceDefaultDarkTheme: "Темна тема за промовчанням"
mutePeriod: "Тривалість приховування"
indefinitely: "Ніколи"
tenMinutes: "10 хвилин"
oneHour: "1 година"
oneDay: "1 день"
oneWeek: "1 тиждень"
reflectMayTakeTime: "Може знадобитися деякий час для відображення"
failedToFetchAccountInformation: "Не вдалося отримати інформацію про акаунт"
rateLimitExceeded: "Ліміт швидкості перевищено"
cropImage: "Кадрування"
cropImageAsk: "Бажаєте кадрувати це зображення?"
file: "Файли"
recentNHours: "Останні {n} годин"
recentNDays: "Останні {n} днів"
noEmailServerWarning: "Email сервер не налаштовано."
recommended: "Рекомендоване"
check: "Перевірити"
driveCapOverrideLabel: "Змінити ємність диска для цього користувача"
driveCapOverrideCaption: "Для скасування вкажіть 0 або менше."
requireAdminForView: "Для перегляду ви повинні увійти в акаунт адміністратора."
typeToConfirm: "Введіть {x} для підтвердження"
deleteAccount: "Видалення акаунту"
document: "Документація"
numberOfPageCache: "Кількість кешованих сторінок"
logoutConfirm: "Справді вийти?"
lastActiveDate: "Останнє використання"
statusbar: "Рядок стану"
pleaseSelect: "Виберіть будь ласка"
reverse: "Перевернути"
colored: "Кольоровий"
refreshInterval: "Інтервал оновлення"
label: "Назва"
type: "Тип"
speed: "Швидкість"
slow: "Повільно"
fast: "Швидко"
sensitiveMediaDetection: "Виявлення NSFW"
localOnly: "Локально"
remoteOnly: "Тільки віддаленi"
failedToUpload: "Збій завантаження"
cannotUploadBecauseNoFreeSpace: "Помилка завантаження через брак місця на Диску."
beta: "Бета"
enableAutoSensitive: "Автоматичне маркування NSFW"
navbar: "Рядок навігації"
shuffle: "Перемішати"
account: "Акаунти"
move: "Пересунути"
pushNotification: "Push сповіщення"
subscribePushNotification: "Увімкнути push-сповіщення"
unsubscribePushNotification: "Вимкнути push-сповіщення"
windowMaximize: "Розгорнути"
windowRestore: "Відновити"
caption: "Підпис"
_sensitiveMediaDetection:
sensitivity: "Чутливість детектування"
setSensitiveFlagAutomatically: "Позначити як NSFW"
analyzeVideos: "Увімкнути аналіз відео"
_emailUnavailable:
used: "Ця email адреса вже використовується"
format: "Невірний формат"
disposable: "Одноразові email-адреси використовувати не можна"
mx: "Цей email сервер недійсний"
smtp: "Цей email-сервер не відповідає"
_ffVisibility:
public: "Опублікувати"
followers: "Видно лише підписникам"
private: "Приватне"
_signup:
almostThere: "Майже готово"
emailAddressInfo: "Будь ласка, введіть вашу email-адресу. Вона не буде оприлюднена."
_accountDelete:
accountDelete: "Видалити акаунт"
requestAccountDelete: "Запит на видалення акаунту"
started: "Видалення розпочато."
inProgress: "Наразі триває видалення"
_ad:
back: "Назад"
reduceFrequencyOfThisAd: "Показувати цю рекламу менше"
_gallery:
my: "Моя галерея"
liked: "Вподобане"
like: "Вподобати"
unlike: "Не вподобати"
_email:
_follow:
title: "Новий підписник"
_receiveFollowRequest:
title: "Отримано запит на підписку"
_plugin:
install: "Встановити плагін"
installWarn: "Будь ласка, не встановлюйте плагінів, яким ви не довіряєте."
manage: "Керування плагінами"
_preferencesBackups:
list: "Створені бекапи"
saveNew: "Зберегти як новий"
loadFile: "Завантажити з файлу"
apply: "Застосувати до цього пристрою"
save: "Зберегти"
cannotSave: "Збереження не вдалося"
createdAt: "Створено: {date} {time}"
updatedAt: "Оновлено: {date} {time}"
cannotLoad: "Не вдалося завантажити"
invalidFile: "Невірний формат файлу"
_registry:
scope: "Область дії"
key: "Ключ"
keys: "Ключі"
domain: "Домен"
@@ -1003,9 +810,7 @@ _mfm:
jump: "Анімація (стрибки)"
jumpDescription: "Показує стрибаючу анімацію"
bounce: "Анімація (пружина)"
bounceDescription: "Надає вмісту стрибаючу анімацію."
shake: "Анімація (Shake)"
shakeDescription: "Надає вмісту тремтливу анімацію."
twitch: "Анімація (Twitch)"
spin: "Анімація (Spin)"
x2: "Великий"
@@ -1019,8 +824,6 @@ _mfm:
font: "Шрифт"
fontDescription: "Встановлює шрифт для контенту."
rotate: "Обертати"
plain: "Звичайний"
plainDescription: "Деактивує всі ефекти MFM, що містяться в цьому ефекті MFM."
_instanceTicker:
none: "Не відображати"
remote: "Відображати для віддалених користувачів"
@@ -1039,9 +842,6 @@ _channel:
usersCount: "{n} учасників"
notesCount: "{n} дописів"
_menuDisplay:
sideFull: "Збоку"
sideIcon: "Збоку (значки)"
top: "Зверху"
hide: "Сховати"
_wordMute:
muteWords: "Заглушені слова"
@@ -1052,10 +852,6 @@ _wordMute:
soft: "М'яко"
hard: "Жорстко"
mutedNotes: "Заблоковані нотатки"
_instanceMute:
instanceMuteDescription2: "Розділяйте новими рядками"
title: "Приховує нотатки з перелічених інстансів."
heading: "Список заглушених інстансів"
_theme:
explore: "Оглянути теми"
install: "Встановити тему"
@@ -1069,14 +865,8 @@ _theme:
invalid: "Неправильний формат теми"
make: "Створити тему"
base: "Основа"
defaultValue: "Значення за промовчанням"
color: "Колір"
key: "Ключ"
defaultValue: "Значення за замовчуванням"
func: "Функції"
funcKind: "Тип функції"
argument: "Аргумент"
alpha: "Непрозорість"
darken: "Затемнення"
lighten: "Яскравість"
inputConstantName: "Введіть назву константи"
importInfo: "Вставляючи сюди код теми, ви можете добавити її до редактору тем"
@@ -1162,7 +952,7 @@ _tutorial:
step4_1: "Ви розмістили свій перший запис?"
step4_2: "Ура! Ваш перший запис відображається на вашій стрічці подій."
step5_1: "Настав час оживити вашу стрічку подій підписавшись на інших користувачів."
step5_2: "{explore} допоможе вам знайти цікавих людей та підписатися на них."
step5_2: "{featured} показує популярні записи , а {explore} популярних користувачів з цього інстансу. Спробуйте підписатись на користувача, який вам сподобався!"
step5_3: "Щоб підписатись на інших користувачів, нажміть на їхнє зображення, а потім на кнопку \"підписатись\"."
step5_4: "Якщо користувач має замок при імені, то йому потрібно буде вручну підтвердити вашу заявку на підписку."
step6_1: "Тепер ви повинні бачити записи інших користувачів на вашій стрічці подій."
@@ -1171,17 +961,8 @@ _tutorial:
step7_1: "Вітаю! Ви пройшли ознайомлення з Misskey."
step7_2: "Якщо ви хочете більше дізнатись про Misskey, зайдіть в розділ {help}."
step7_3: "Насолоджуйтесь Misskey! 🚀"
step8_1: "Наостанку, чи бажаєте ви ввімкнути push-сповіщення?"
step8_3: "Ви завжди можете змінити цей параметр пізніше."
_2fa:
alreadyRegistered: "Двофакторна автентифікація вже налаштована."
registerDevice: "Зареєструвати новий пристрій"
registerKey: "Зареєструвати новий ключ безпеки"
step1: "Спершу встановіть на свій пристрій програму автентифікації (наприклад {a} або {b})."
step2: "Потім відскануйте QR-код, який відображається на цьому екрані."
step2Url: "Ви також можете ввести цю URL-адресу, якщо використовуєте програму для ПК:"
step3: "Щоб завершити налаштування, введіть токен, наданий вашою програмою."
step4: "Відтепер будь-які майбутні спроби входу вимагатимуть такого токена."
_permissions:
"read:account": "Переглядати дані профілю"
"write:account": "Змінити дані акаунту"
@@ -1199,7 +980,6 @@ _permissions:
"write:mutes": "Змінювати список ігнорованих"
"write:notes": "Писати і видаляти нотатки"
"read:notifications": "Переглядати сповіщення"
"write:notifications": "Керування сповіщеннями"
"read:reactions": "Переглядати реакції"
"write:reactions": "Змінювати реакції"
"write:votes": "Голосувати в опитуваннях"
@@ -1211,7 +991,6 @@ _permissions:
"write:user-groups": "Змінювати групи користувача"
"read:channels": "Переглядати канали"
"write:channels": "Змінювати канали"
"read:gallery": "Перегляд галереї"
_auth:
shareAccess: "Ви хочете надати \"{name}\" доступ до цього акаунту?"
shareAccessAsk: "Ви впевнені, що хочете надати цій програмі доступ до вашого акаунту?"
@@ -1238,9 +1017,7 @@ _widgets:
activity: "Активність"
photos: "Фото"
digitalClock: "Цифровий годинник"
unixClock: "Unix-годинник"
federation: "Федіверс"
instanceCloud: "Хмара інстансів"
postForm: "Створення нотатки"
slideshow: "Слайд-шоу"
button: "Кнопка"
@@ -1248,10 +1025,6 @@ _widgets:
jobQueue: "Черга завдань"
serverMetric: "Показники сервера "
aiscript: "Консоль AiScript"
aichan: "Ai"
userList: "Список користувачів"
_userList:
chooseList: "Виберіть список"
_cw:
hide: "Сховати"
show: "Показати більше"
@@ -1319,23 +1092,16 @@ _exportOrImport:
muteList: "Ігнорувати"
blockingList: "Заблокувати"
userLists: "Списки"
excludeMutingUsers: "Виключити ігнорованих користувачів"
excludeInactiveUsers: "Виключити неактивних користувачів"
_charts:
federation: "Федіверс"
apRequest: "Запити"
usersIncDec: "Зміни кількості користувачів"
usersTotal: "Загальна кількість користувачів"
activeUsers: "Активні користувачі"
notesIncDec: "Зміни кількості нотаток"
localNotesIncDec: "Зміни кількості локальних нотаток"
remoteNotesIncDec: "Зміни кількості віддалених нотаток"
notesTotal: "Загальна кількість нотаток"
filesIncDec: "Зміни кількості файлів"
filesTotal: "Загальна кількість файлів"
_instanceCharts:
requests: "Запити"
users: "Зміни кількості користувачів"
usersTotal: "Сумарна кількість користувачів"
notes: "Різниця кількості зроблених записів"
notesTotal: "Сумарна кількість нотаток"
@@ -1384,6 +1150,8 @@ _pages:
eyeCatchingImageRemove: "Видалити привабливе зображення"
chooseBlock: "Додати блок"
selectType: "Виберіть тип"
enterVariableName: "Введіть назву для змінної"
variableNameIsAlreadyUsed: "Ця назва вже використовується іншою змінною"
contentBlocks: "Контент"
inputBlocks: "Ввід"
specialBlocks: "Особливе"
@@ -1393,11 +1161,248 @@ _pages:
section: "Розділ"
image: "Зображення"
button: "Кнопка"
if: "Якщо"
_if:
variable: "Змінні"
post: "Створення нотатки"
_post:
text: "Вміст"
canvasId: "Ідентифікатор полотна"
textInput: "Введення тексту"
_textInput:
name: "Ім'я змінної"
text: "Назва"
default: "Значення за замовчуванням"
textareaInput: "Багаторядкове введення тексту"
_textareaInput:
name: "Ім'я змінної"
text: "Назва"
default: "Значення за замовчуванням"
numberInput: "Числове введення"
_numberInput:
name: "Ім'я змінної"
text: "Назва"
default: "Значення за замовчуванням"
canvas: "Полотно"
_canvas:
id: "Ідентифікатор полотна"
width: "Ширина"
height: "Висота"
note: "Вбудована нотатка"
_note:
id: "Ідентифікатор нотатки"
idDescription: "Також можна вказати посилання на нотатку"
detailed: "Детальний вигляд"
switch: "Перемикач"
_switch:
name: "Ім'я змінної"
text: "Назва"
default: "Значення за замовчуванням"
counter: "Лічильник"
_counter:
name: "Ім'я змінної"
text: "Назва"
inc: "Збільшити на"
_button:
text: "Напис"
colored: "Кольоровий"
action: "Дія кнопки"
_action:
dialog: "Показати повідомлення"
_dialog:
content: "Вміст"
resetRandom: "Скидання генератора випадковості"
pushEvent: "Надіслати подію"
_pushEvent:
event: "Назві події"
message: "Повідомлення для відображення при активації"
variable: "Змінна для надсилання"
no-variable: "Відсутньо"
callAiScript: "Виклик AiScript"
_callAiScript:
functionName: "Ім'я функції"
radioButton: "Вибір"
_radioButton:
name: "Ім'я змінної"
title: "Напис"
values: "Варіанти, розділені розривами рядків"
default: "Значення за замовчуванням"
script:
categories:
flow: "Керування потоком"
logical: "Логічні операції"
operation: "Обчислення"
comparison: "Порівняння"
random: "Випадковість"
value: "Значення"
fn: "Функції"
text: "Дії з текстом"
convert: "Перетворення"
list: "Списки"
blocks:
text: "Текст"
multiLineText: "Текст (багаторядковий)"
textList: "Текстовий список"
_textList:
info: "Використовувати новий рядок як роздільник для вводу"
strLen: "Довжина тексту"
_strLen:
arg1: "Текст"
strPick: "Вибрати символ"
_strPick:
arg1: "Текст"
arg2: "Розташування символу"
strReplace: "Заміна тексту"
_strReplace:
arg1: "Текст"
arg2: "Текст, який потрібно замінити"
arg3: "Заміняти на"
strReverse: "Перевернути текст"
_strReverse:
arg1: "Текст"
join: "Конкатенація тексту"
_join:
arg1: "Списки"
arg2: "Розділювач"
add: "Додати"
_add:
arg1: "A"
arg2: "B"
subtract: "Відняти"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Помножити"
_multiply:
arg1: "A"
arg2: "B"
divide: "Поділити"
_divide:
arg1: "A"
arg2: "B"
mod: "Остача"
_mod:
arg1: "A"
arg2: "B"
round: "Десяткове округлення"
_round:
arg1: "Число"
eq: "A дорівнює B"
_eq:
arg1: "A"
arg2: "B"
notEq: "A не дорівнює B"
_notEq:
arg1: "A"
arg2: "B"
and: "А І Б"
_and:
arg1: "A"
arg2: "B"
or: "A АБО B"
_or:
arg1: "A"
arg2: "B"
lt: "< A менше, ніж B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A більше, ніж B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A менше або дорівнює B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A більше або дорівнює B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Умова"
_if:
arg1: "Якщо"
arg2: "Якщо так"
arg3: "Якщо ні"
not: "НЕ"
_not:
arg1: "НЕ"
random: "Випадково"
_random:
arg1: "Імовірність"
rannum: "Випадкове число"
_rannum:
arg1: "Мінімальне значення"
arg2: "Максимальне значення"
randomPick: "Випадковий вибір зі списку"
_randomPick:
arg1: "Списки"
dailyRandom: "Випадково (триває добу)"
_dailyRandom:
arg1: "Імовірність"
dailyRannum: "Випадкове число (триває добу)"
_dailyRannum:
arg1: "Мінімальне значення"
arg2: "Максимальне значення"
dailyRandomPick: "Випадково вибрати зі списку (триває добу)"
_dailyRandomPick:
arg1: "Списки"
seedRandom: "Випадковість (з насінням)"
_seedRandom:
arg1: "Насіння"
arg2: "Імовірність"
seedRannum: "Випадкове число (з насінням)"
_seedRannum:
arg1: "Насіння"
arg2: "Мінімальне значення"
arg3: "Максимальне значення"
seedRandomPick: "Випадково вибрати зі списку (з насінням)"
_seedRandomPick:
arg1: "Насіння"
arg2: "Списки"
DRPWPM: "Випадково вибрати зі зваженого списку (триває добу)"
_DRPWPM:
arg1: "Текстовий список"
pick: "Вибір зі списку"
_pick:
arg1: "Списки"
arg2: "Позиція"
listLen: "Отримати довжину списку"
_listLen:
arg1: "Списки"
number: "Число"
stringToNumber: "Текст на число"
_stringToNumber:
arg1: "Текст"
numberToString: "Число на текст"
_numberToString:
arg1: "Число"
splitStrByLine: "Розбиття тексту на рядки"
_splitStrByLine:
arg1: "Текст"
ref: "Змінні"
aiScriptVar: "Змінна AiScript"
fn: "Функції"
_fn:
slots: "Паз"
slots-info: "Використовувати нову лінію як роздільник пазів"
arg1: "Вивід"
for: "Повторення"
_for:
arg1: "Кількість повторень"
arg2: "Дія"
typeError: "Паз {slot} приймає \"{expect}\" тип, але надана змінна має тип \"{actual}\"!"
thereIsEmptySlot: "Паз {slot} пустий!"
types:
string: "Текст"
number: "Число"
boolean: "Прапорець"
array: "Списки"
stringArray: "Текстовий список"
emptySlot: "Пустий паз"
enviromentVariables: "Змінні середовища"
pageVariables: "Елемент сторінки"
argVariables: "Стрічка вводу"
_relayStatus:
requesting: "Очікує затвердження"
accepted: "Затверджено"
@@ -1442,10 +1447,6 @@ _deck:
stackLeft: "У стовпчик вліво"
popRight: "Витягнути вправо"
profile: "Обліковий запис"
newProfile: "Новий профіль"
deleteProfile: "Видалити профіль"
introduction: "Створіть для себе ідеальний інтерфейс, вільно розташувавши стовпці!"
widgetsIntroduction: "Будь ласка, виберіть «Редагувати віджети» в меню стовпців і додайте віджет."
_columns:
main: "Головна"
widgets: "Віджети"

View File

@@ -52,7 +52,6 @@ searchUser: "Tìm kiếm người dùng"
reply: "Trả lời"
loadMore: "Tải thêm"
showMore: "Xem thêm"
showLess: "Đóng"
youGotNewFollower: "đã theo dõi bạn"
receiveFollowRequest: "Đã yêu cầu theo dõi"
followRequestAccepted: "Đã chấp nhận yêu cầu theo dõi"
@@ -348,8 +347,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "Bật reCAPTCHA"
recaptchaSiteKey: "Khóa của trang"
recaptchaSecretKey: "Khóa bí mật"
turnstileSiteKey: "Khóa của trang"
turnstileSecretKey: "Khóa bí mật"
avoidMultiCaptchaConfirm: "Dùng nhiều hệ thống Captcha có thể gây nhiễu giữa chúng. Bạn có muốn tắt các hệ thống Captcha khác hiện đang hoạt động không? Nếu bạn muốn chúng tiếp tục được bật, hãy nhấn hủy."
antennas: "Trạm phát sóng"
manageAntennas: "Quản lý trạm phát sóng"
@@ -452,6 +449,7 @@ language: "Ngôn ngữ"
uiLanguage: "Ngôn ngữ giao diện"
groupInvited: "Bạn đã được mời tham gia nhóm"
aboutX: "Giới thiệu {x}"
useOsNativeEmojis: "Dùng emoji hệ thống"
disableDrawer: "Không dùng menu thanh bên"
youHaveNoGroups: "Không có nhóm nào"
joinOrCreateGroup: "Tham gia hoặc tạo một nhóm mới."
@@ -504,7 +502,6 @@ deleteAll: "Xóa tất cả"
showFixedPostForm: "Hiện khung soạn tút ở phía trên bảng tin"
newNoteRecived: "Đã nhận tút mới"
sounds: "Âm thanh"
sound: "Âm thanh"
listen: "Nghe"
none: "Không"
showInPage: "Hiện trong trang"
@@ -564,7 +561,6 @@ author: "Tác giả"
leaveConfirm: "Có những thay đổi chưa được lưu. Bạn có muốn bỏ chúng không?"
manage: "Quản lý"
plugins: "Plugin"
preferencesBackups: "Sao lưu thiết lập"
deck: "Deck"
undeck: "Bỏ Deck"
useBlurEffectForModal: "Sử dụng hiệu ứng mờ cho các hộp thoại"
@@ -894,7 +890,6 @@ activeEmailValidationDescription: "Cho phép xác minh địa chỉ email chặt
navbar: "Thanh điều hướng"
shuffle: "Xáo trộn"
account: "Tài khoản của bạn"
move: "Di chuyển"
_sensitiveMediaDetection:
description: "Giảm nỗ lực kiểm duyệt máy chủ thông qua việc tự động nhận dạng media NSFW thông qua học máy. Điều này sẽ làm tăng một chút áp lực trên máy chủ."
sensitivity: "Phát hiện nhạy cảm"
@@ -945,24 +940,6 @@ _plugin:
install: "Cài đặt tiện ích"
installWarn: "Vui lòng không cài đặt những tiện ích đáng ngờ."
manage: "Quản lý plugin"
_preferencesBackups:
list: "Tạo sao lưu"
saveNew: "Lưu bản sao lưu"
loadFile: "Nhập tập tin"
apply: "Áp dụng lên thiết bị này"
save: "Lưu thay đổi"
inputName: "Nhập tên bản sao lưu"
cannotSave: "Không thể lưu"
nameAlreadyExists: "Bản sao lưu \"{name}\" đã tồn tại. Xin nhập tên khác."
applyConfirm: "Bạn có chắc muốn áp dụng bản sao lưu \"{name}\" cho thiết bị này? Thiết lập hiện tại sẽ bị ghi đè."
saveConfirm: "Lưu bản sao lưu {name}?"
deleteConfirm: "Xóa bản sao lưu {name}?"
renameConfirm: "Đổi tên bản sao lưu \"{old}\" thành \"{new}\"?"
noBackups: "Chưa có bản sao lưu. Bạn có thể sao lưu thiết lập trên máy chủ này bằng cách sử dụng \"Tạo sao lưu\"."
createdAt: "Tạo vào: {time} {date}"
updatedAt: "Cập nhật: {time} {date}"
cannotLoad: "Tải thất bại"
invalidFile: "Sai định dạng tập tin"
_registry:
scope: "Phạm vi"
key: "Mã"
@@ -1046,8 +1023,6 @@ _mfm:
sparkleDescription: "Làm cho nội dung hiệu ứng hạt lấp lánh."
rotate: "Xoay"
rotateDescription: "Xoay nội dung theo một góc cụ thể."
plain: "Đơn giản"
plainDescription: "Vô hiệu hóa mọi hiệu ứng MFM chứa trong hiệu ứng MFM này."
_instanceTicker:
none: "Không hiển thị"
remote: "Hiện cho người dùng từ máy chủ khác"
@@ -1281,7 +1256,6 @@ _widgets:
activity: "Hoạt động"
photos: "Kho ảnh"
digitalClock: "Đồng hồ số"
unixClock: "Đồng hồ UNIX"
federation: "Liên hợp"
instanceCloud: "Instance cloud"
postForm: "Mẫu đăng"
@@ -1292,8 +1266,6 @@ _widgets:
serverMetric: "Thống kê máy chủ"
aiscript: "AiScript console"
aichan: "Ai"
_userList:
chooseList: "Chọn danh sách"
_cw:
hide: "Ẩn"
show: "Tải thêm"
@@ -1429,6 +1401,8 @@ _pages:
eyeCatchingImageRemove: "Xóa ảnh thu nhỏ"
chooseBlock: "Thêm khối"
selectType: "Chọn kiểu"
enterVariableName: "Nhập tên một biến thể"
variableNameIsAlreadyUsed: "Tên biến thể này đã được sử dụng"
contentBlocks: "Nội dung"
inputBlocks: "Nhập"
specialBlocks: "Đặc biệt"
@@ -1438,11 +1412,249 @@ _pages:
section: "Mục "
image: "Hình ảnh"
button: "Nút"
if: "Nếu"
_if:
variable: "Biến thể"
post: "Mẫu đăng"
_post:
text: "Nội dung"
attachCanvasImage: "Đính kèm hình canva"
canvasId: "ID Canva"
textInput: "Văn bản đầu vào"
_textInput:
name: "Tên biến thể"
text: "Tựa đề"
default: "Giá trị mặc định"
textareaInput: "Văn bản nhiều dòng đầu vào"
_textareaInput:
name: "Tên biến thể"
text: "Tựa đề"
default: "Giá trị mặc định"
numberInput: "Đầu vào số"
_numberInput:
name: "Tên biến thể"
text: "Tựa đề"
default: "Giá trị mặc định"
canvas: "Canva"
_canvas:
id: "ID Canva"
width: "Chiều rộng"
height: "Chiều cao"
note: "Tút đã nhúng"
_note:
id: "ID tút"
idDescription: "Ngoài ra, bạn có thể dán URL tút vào đây."
detailed: "Xem chi tiết"
switch: "Chuyển đổi"
_switch:
name: "Tên biến thể"
text: "Tựa đề"
default: "Giá trị mặc định"
counter: "Bộ đếm"
_counter:
name: "Tên biến thể"
text: "Tựa đề"
inc: "Bước"
_button:
text: "Tựa đề"
colored: "Với màu"
action: "Thao tác khi nhấn nút"
_action:
dialog: "Hiện hộp thoại"
_dialog:
content: "Nội dung"
resetRandom: "Đặt lại seed ngẫu nhiên"
pushEvent: "Gửi một sự kiện"
_pushEvent:
event: "Tên sự kiện"
message: "Tin nhắn hiển thị khi kích hoạt"
variable: "Biển thể để gửi"
no-variable: "Không"
callAiScript: "Gọi AiScript"
_callAiScript:
functionName: "Tên tính năng"
radioButton: "Lựa chọn"
_radioButton:
name: "Tên biến thể"
title: "Tựa đề"
values: "Phân tách các mục bằng cách xuống dòng"
default: "Giá trị mặc định"
script:
categories:
flow: "Điều khiển"
logical: "Hoạt động logic"
operation: "Tính toán"
comparison: "So sánh"
random: "Ngẫu nhiên"
value: "Giá trị"
fn: "Tính năng"
text: "Tác vụ văn bản"
convert: "Chuyển đổi"
list: "Danh sách"
blocks:
text: "Văn bản"
multiLineText: "Văn bản (nhiều dòng)"
textList: "Văn bản liệt kê"
_textList:
info: "Phân tách mục bằng cách xuống dòng"
strLen: "Độ dài văn bản"
_strLen:
arg1: "Văn bản"
strPick: "Trích xuất chuỗi"
_strPick:
arg1: "Văn bản"
arg2: "Vị trí chuỗi"
strReplace: "Thay thế chuỗi"
_strReplace:
arg1: "Nội dung"
arg2: "Văn bản thay thế"
arg3: "Thay thế bằng"
strReverse: "Lật văn bản"
_strReverse:
arg1: "Văn bản"
join: "Nối văn bản"
_join:
arg1: "Danh sách"
arg2: "Phân cách"
add: "Cộng"
_add:
arg1: "A"
arg2: "B"
subtract: "Trừ"
_subtract:
arg1: "A"
arg2: "B"
multiply: "Nhân"
_multiply:
arg1: "A"
arg2: "B"
divide: "Chia"
_divide:
arg1: "A"
arg2: "B"
mod: "Phần còn lại"
_mod:
arg1: "A"
arg2: "B"
round: "Làm tròn thập phân"
_round:
arg1: "Số"
eq: "A và B bằng nhau"
_eq:
arg1: "A"
arg2: "B"
notEq: "A và B khác nhau"
_notEq:
arg1: "A"
arg2: "B"
and: "A VÀ B"
_and:
arg1: "A"
arg2: "B"
or: "A HOẶC B"
_or:
arg1: "A"
arg2: "B"
lt: "< A nhỏ hơn B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A lớn hơn B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A nhỏ hơn hoặc bằng B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A lớn hơn hoặc bằng B"
_gtEq:
arg1: "A"
arg2: "B"
if: "Nhánh"
_if:
arg1: "Nếu"
arg2: "Sau đó"
arg3: "Khác"
not: "KHÔNG"
_not:
arg1: "KHÔNG"
random: "Ngẫu nhiên"
_random:
arg1: "Xác suất"
rannum: "Số ngẫu nhiên"
_rannum:
arg1: "Giá trị tối thiểu"
arg2: "Giá trị tối đa"
randomPick: "Chọn ngẫu nhiên từ danh sách"
_randomPick:
arg1: "Danh sách"
dailyRandom: "Ngẫu nhiên (Đổi mỗi người một lần mỗi ngày)"
_dailyRandom:
arg1: "Xác suất"
dailyRannum: "Số ngẫu nhiên (Đổi mỗi người một lần mỗi ngày)"
_dailyRannum:
arg1: "Giá trị tối thiểu"
arg2: "Giá trị tối đa"
dailyRandomPick: "Chọn ngẫu nhiên từ một danh sách (Đổi mỗi người một lần mỗi ngày)"
_dailyRandomPick:
arg1: "Danh sách"
seedRandom: "Ngẫu nhiên (với seed)"
_seedRandom:
arg1: "Seed"
arg2: "Xác suất"
seedRannum: "Số ngẫu nhiên (với seed)"
_seedRannum:
arg1: "Seed"
arg2: "Giá trị tối thiểu"
arg3: "Giá trị tối đa"
seedRandomPick: "Chọn ngẫu nhiên từ danh sách (với seed)"
_seedRandomPick:
arg1: "Seed"
arg2: "Danh sách"
DRPWPM: "Chọn ngẫu nhiên từ danh sách nặng (Đổi mỗi người một lần mỗi ngày)"
_DRPWPM:
arg1: "Văn bản liệt kê"
pick: "Chọn từ danh sách"
_pick:
arg1: "Danh sách"
arg2: "Vị trí"
listLen: "Lấy độ dài danh sách"
_listLen:
arg1: "Danh sách"
number: "Số"
stringToNumber: "Chữ thành số"
_stringToNumber:
arg1: "Văn bản"
numberToString: "Số thành chữ"
_numberToString:
arg1: "Số"
splitStrByLine: "Phân cách văn bản bằng cách xuống dòng"
_splitStrByLine:
arg1: "Văn bản"
ref: "Biến thể"
aiScriptVar: "Biển thể AiScript"
fn: "Tính năng"
_fn:
slots: "Chỗ"
slots-info: "Phân cách chỗ bằng cách xuống dòng"
arg1: "Đầu ra"
for: "để-Lặp lại"
_for:
arg1: "Số lần lặp lại"
arg2: "Hành động"
typeError: "Chỗ {slot} chấp nhận các giá trị thuộc loại \"{expect}\", nhưng giá trị được cung cấp thuộc loại \"{actual}\"!"
thereIsEmptySlot: "Chỗ {slot} đang trống!"
types:
string: "Văn bản"
number: "Số"
boolean: "Cờ"
array: "Danh sách"
stringArray: "Văn bản liệt kê"
emptySlot: "Chỗ trống"
enviromentVariables: "Biến môi trường"
pageVariables: "Biến trang"
argVariables: "Đầu vào chỗ"
_relayStatus:
requesting: "Đang chờ"
accepted: "Đã duyệt"
@@ -1484,7 +1696,6 @@ _deck:
alwaysShowMainColumn: "Luôn hiện cột chính"
columnAlign: "Căn cột"
addColumn: "Thêm cột"
configureColumn: "Cài đặt cột"
swapLeft: "Hoán đổi với cột bên trái"
swapRight: "Hoán đổi với cột bên phải"
swapUp: "Hoán đổi với cột trên"

View File

@@ -2,21 +2,19 @@
_lang_: "中文(简体)"
headlineMisskey: "通过帖子连接在一起的网络"
introMisskey: "欢迎Misskey是一个开源的、去中心化的“微博客”服务。\n通过编写「帖文」来和大家分享你的以及你周围的事情吧📡\n通过「回应」功能可以让你快速地对大家的帖文表达反馈👍\n来探索新的世界吧🚀"
poweredByMisskeyDescription: "{name} 由开源平台 <b>Misskey</b> 驱动(也被称为 Misskey 实例)"
monthAndDay: "{month}月 {day}日"
search: "搜索"
notifications: "通知"
username: "用户名"
password: "密码"
forgotPassword: "忘记密码"
fetchingAsApObject: "在联邦宇宙查询中..."
fetchingAsApObject: "在联邦宇宙查询中..."
ok: "OK"
gotIt: "我明白了"
cancel: "取消"
noThankYou: "不用"
enterUsername: "输入用户名"
renotedBy: "由 {user} 转贴"
noNotes: "没有帖"
noNotes: "没有帖"
noNotifications: "无通知"
instance: "实例"
settings: "设置"
@@ -49,13 +47,11 @@ deleteAndEdit: "删除并编辑"
deleteAndEditConfirm: "要删除此帖并再次编辑吗?对此帖的所有回应、转发和回复也将被删除。"
addToList: "添加至列表"
sendMessage: "发送"
copyRSS: "复制RSS"
copyUsername: "复制用户名"
searchUser: "搜索用户"
reply: "回复"
loadMore: "查看更多"
showMore: "查看更多"
showLess: "关闭"
youGotNewFollower: "你有新的关注者"
receiveFollowRequest: "您收到了关注请求"
followRequestAccepted: "您的关注请求被通过了"
@@ -144,7 +140,7 @@ cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远
flagAsBot: "这是一个机器人账号"
flagAsBotDescription: "如果此帐户由程序控制请启用此项。启用后此标志可以帮助其他开发人员防止机器人之间产生无限互动的行为并让Misskey的内部系统将此帐户识别为机器人。"
flagAsCat: "将这个账户设定为一只猫"
flagAsCatDescription: "如果您想表明此帐户是一只猫,请打开此标志。\n开启后会在您的头像上出现猫耳朵并将你的帖子中的「na」替换为「nya」日文同理。"
flagAsCatDescription: "如果您想表明此帐户是一只猫,请打开此标志。"
flagShowTimelineReplies: "在时间线上显示帖子的回复"
flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。"
autoAcceptFollowed: "自动允许关注者的关注"
@@ -215,7 +211,7 @@ blocked: "已拉黑"
suspended: "停止推流"
all: "全部"
subscribing: "已订阅"
publishing: "投递中"
publishing: "直播中"
notResponding: "没有响应"
instanceFollowing: "关注实例"
instanceFollowers: "关注实例"
@@ -255,7 +251,7 @@ messageRead: "已读"
noMoreHistory: "没有更多的历史记录"
startMessaging: "添加聊天"
nUsersRead: "{n}人已读"
agreeTo: "勾选则表示已阅读并同意{0}"
agreeTo: "{0}勾选则表示已阅读并同意"
tos: "服务条款"
start: "开始"
home: "首页"
@@ -346,15 +342,11 @@ pinnedNotes: "已置顶的帖子"
hcaptcha: "hCaptcha"
enableHcaptcha: "启用 hCaptcha"
hcaptchaSiteKey: "网站密钥"
hcaptchaSecretKey: "hCaptcha 密钥(SecretKey)"
hcaptchaSecretKey: "密钥"
recaptcha: "reCAPTCHA"
enableRecaptcha: "启用 reCAPTCHA\n(请注意, 此功能在中国大陆不可用. 如果启用, 可能导致无法正常使用登录或注册等功能)"
recaptchaSiteKey: "网站密钥"
recaptchaSecretKey: "reCAPTCHA 密钥"
turnstile: "Turnstile"
enableTurnstile: "启用Turnstile"
turnstileSiteKey: "网站密钥"
turnstileSecretKey: "Turnstile 密钥(SecretKey)"
avoidMultiCaptchaConfirm: "使用多种验证方式可能会造成干扰,您要禁用其他验证方式吗?您可以按“取消”按钮,仍然保持启用多种验证方式。"
antennas: "天线"
manageAntennas: "天线管理"
@@ -457,8 +449,7 @@ language: "语言"
uiLanguage: "显示语言"
groupInvited: "您有新的群组邀请"
aboutX: "关于 {x}"
emojiStyle: "emoji 的样式"
native: "原生"
useOsNativeEmojis: "使用系统的原生表情符号"
disableDrawer: "不显示抽屉菜单"
youHaveNoGroups: "没有群组"
joinOrCreateGroup: "请加入一个现有的群组,或者创建新群组。"
@@ -492,13 +483,13 @@ showFeaturedNotesInTimeline: "在时间线上显示热门推荐"
objectStorage: "对象存储"
useObjectStorage: "使用对象存储"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "用于引用的URL。如果您正在使用CDN或反向代理请指定其URL例如S3“https://<bucket>.s3.amazonaws.com”GCS“https://storage.googleapis.com/<bucket>”"
objectStorageBaseUrlDesc: "URL前缀用于构造URL到对象媒体的引用如果您使用的是CDN或反向代理请指定其URL否则请根据您使用的服务指定可公开访问的地址。例如“https://<bucket>.s3.amazonaws.com”用于AWS S3“https://storage.googleapis.com/<bucket>”用于GCS"
objectStorageBucket: "存储桶"
objectStorageBucketDesc: "请指定使用的对象存储服务的存储桶名称。"
objectStoragePrefix: "前缀"
objectStoragePrefixDesc: "文件将存储在此前缀的目录下。"
objectStorageEndpoint: "端点"
objectStorageEndpointDesc: "如果你使用AWS S3请留空。否则请根据你使用的服务商的说明来进行设置,指定端点形式为“<host>”或“<host>:<port>”。"
objectStorageEndpointDesc: "如果你希望使用AWS S3请留空。否则请根据你使用的服务来进行设置指定端点形式为“<host>”或“<host>:<port>”。"
objectStorageRegion: "可用区"
objectStorageRegionDesc: "指定一个可用区例如“xx-east-1”。 如果您的对象存储服务没有可用区概念请将其留空或填写“us-east-1”。"
objectStorageUseSSL: "使用SSL"
@@ -511,7 +502,6 @@ deleteAll: "全部删除"
showFixedPostForm: "在时间线顶部显示发帖框"
newNoteRecived: "有新的帖子"
sounds: "提示音"
sound: "提示音"
listen: "试听"
none: "无"
showInPage: "在页面中显示"
@@ -558,7 +548,7 @@ deletedNote: "已删除的帖子"
invisibleNote: "隐藏的帖子"
enableInfiniteScroll: "启用自动滚动页面模式"
visibility: "可见性"
poll: "投票"
poll: "调查问卷"
useCw: "隐藏内容"
enablePlayer: "打开播放器"
disablePlayer: "关闭播放器"
@@ -571,7 +561,6 @@ author: "作者"
leaveConfirm: "存在未保存的更改。要放弃更改吗?"
manage: "管理"
plugins: "插件"
preferencesBackups: "备份设置"
deck: "Deck"
undeck: "取消Deck"
useBlurEffectForModal: "对话框使用模糊效果"
@@ -608,12 +597,12 @@ wordMute: "文字屏蔽"
regexpError: "正则表达式错误"
regexpErrorDescription: "{tab} 屏蔽文字的第 {line} 行的正则表达式有错误:"
instanceMute: "实例的屏蔽"
userSaysSomething: "{name}说了什么,但是被您屏蔽了"
userSaysSomething: "{name}说了什么"
makeActive: "启用"
display: "显示"
copy: "复制"
metrics: "指标"
overview: "概览"
metrics: "服务器监控"
overview: "服务器概况"
logs: "日志"
delayed: "滞后"
database: "数据库"
@@ -677,7 +666,7 @@ yes: "是"
no: "否"
driveFilesCount: "网盘的文件数"
driveUsage: "网盘的空间用量"
noCrawle: "要求搜索引擎不索引该用户"
noCrawle: "要求搜索引擎不索引该站点"
noCrawleDescription: "要求搜索引擎不要收录(索引)您的用户页面,帖子,页面等。"
lockedAccountInfo: "即使通过了关注请求,只要您不将帖子可见范围设置成“关注者”,任何人都可以看到您的帖子。"
alwaysMarkSensitive: "默认将媒体文件标记为敏感内容"
@@ -717,7 +706,6 @@ accentColor: "强调色"
textColor: "文本"
saveAs: "另存为"
advanced: "高级"
advancedSettings: "高级设置"
value: "值"
createdAt: "创建日期"
updatedAt: "更新时间"
@@ -757,7 +745,7 @@ userInfo: "用户信息"
unknown: "未知"
onlineStatus: "在线状态"
hideOnlineStatus: "隐藏在线状态"
hideOnlineStatusDescription: "隐藏在线状态后,可能会降低搜索等功能的便利性。"
hideOnlineStatusDescription: "隐藏在线状态后,可能会降低例如搜索等功能的便利性。"
online: "在线"
active: "活动"
offline: "离线"
@@ -858,7 +846,6 @@ oneDay: "1天"
oneWeek: "1周"
reflectMayTakeTime: "可能需要一些时间才能体现出效果。"
failedToFetchAccountInformation: "获取账户信息失败"
rateLimitExceeded: "已超過速率限制"
cropImage: "剪裁图像"
cropImageAsk: "是否要裁剪图像?"
file: "文件"
@@ -868,7 +855,6 @@ noEmailServerWarning: "电子邮件服务器未设置。"
thereIsUnresolvedAbuseReportWarning: "有未解决的报告"
recommended: "推荐"
check: "检查"
driveCapOverrideLabel: "變更此用戶的雲端硬碟容量上限"
driveCapOverrideCaption: "设定为 0 以下则会解除此限制。"
requireAdminForView: "需要使用管理员账户登录才能查看。"
isSystemAccount: "该账号由系统自动创建和管理。"
@@ -876,7 +862,7 @@ typeToConfirm: "输入 {x} 以确认操作。"
deleteAccount: "删除账户"
document: "文档"
numberOfPageCache: "缓存页数"
numberOfPageCacheDescription: "设置较高的值会更方便用户,但设备的负载和内存使用量会增加。"
numberOfPageCacheDescription: "设置较高的值会更方便用户,但服务器负载和内存使用量会增加。"
logoutConfirm: "是否确认登出?"
lastActiveDate: "最后活跃时间"
statusbar: "状态栏"
@@ -897,25 +883,9 @@ cannotUploadBecauseInappropriate: "因为可能含有不适宜的内容,无法
cannotUploadBecauseNoFreeSpace: "因为已无可用空间,无法上传。"
beta: "测试"
enableAutoSensitive: "自动 NSFW 识别"
enableAutoSensitiveDescription: "如果可用,请使用机器学习在媒体上自动设置 NSFW 标志。即使关闭此功能,也可能会根据实例自动设置。"
activeEmailValidationDescription: "开启用户的电子邮件地址验证,判断它是一次性的电子邮件地址,还是可以实际通信的地址。关闭时,则只检查字符串是否正确。"
navbar: "导航栏"
shuffle: "随机"
account: "账户"
move: "移动"
pushNotification: "推送通知"
subscribePushNotification: "启用推送通知消息"
unsubscribePushNotification: "停用推送通知消息"
pushNotificationAlreadySubscribed: "推送通知消息已启用"
pushNotificationNotSupported: "浏览器或实例不支持推送通知消息"
sendPushNotificationReadMessage: "删除已读推送通知消息"
sendPushNotificationReadMessageCaption: "“{emptyPushNotificationMessage}”的通知消息将会显示。您终端设备的电池消耗可能会增加。"
windowMaximize: "最大化"
windowRestore: "还原"
caption: "标题"
loggedInAsBot: "已登录的Bot"
tools: "工具"
cannotLoad: "无法加载"
_sensitiveMediaDetection:
description: "可以使用机器学习技术自动检测敏感媒体,以便进行审核。服务器负载将略微增加。"
sensitivity: "检测敏感度"
@@ -966,24 +936,6 @@ _plugin:
install: "安装插件"
installWarn: "请不要安装不可信的插件。"
manage: "管理插件..."
_preferencesBackups:
list: "已创建的备份"
saveNew: "另存为"
loadFile: "导入文件"
apply: "应用于本设备"
save: "覆盖存档"
inputName: "请输入备份的名称"
cannotSave: "无法保存"
nameAlreadyExists: "备份名称\"{name}\"已经存在,请指定其他名称。"
applyConfirm: "您是否要将备份\"{name}\"应用到当前设备上?当前设备现有配置将被丢弃。"
saveConfirm: "您确定要覆盖保存 {name} 吗?"
deleteConfirm: "您确定要删除 {name} 吗?"
renameConfirm: "您确定要把“{old}”改为“{new}”吗?"
noBackups: "当前没有备份,“另存为”允许您在服务器上保存当前客户端的配置。"
createdAt: "创建日期:{date} {time}"
updatedAt: "更新日期:{date} {time}"
cannotLoad: "无法加载"
invalidFile: "无效的的文件格式。"
_registry:
scope: "范围"
key: "主要"
@@ -1067,8 +1019,6 @@ _mfm:
sparkleDescription: "添加发光粒子效果。"
rotate: "旋转"
rotateDescription: "旋转指定的角度。"
plain: "简洁"
plainDescription: "禁用所有内部语法。"
_instanceTicker:
none: "不显示"
remote: "仅远程用户"
@@ -1226,9 +1176,6 @@ _tutorial:
step7_1: "对Misskey基本操作的简单介绍就到此结束了。 辛苦了!"
step7_2: "如果你想了解更多有关Misskey的信息请参见{help}。"
step7_3: "接下来享受Misskey带来的乐趣吧🚀"
step8_1: "最后,您想要启用推送通知消息吗?"
step8_2: "通过接收推送通知消息,即使没开 Misskey您也可以收到回应、关注、提及等的消息。"
step8_3: "您也可以稍后再更改通知设置。"
_2fa:
alreadyRegistered: "此设备已被注册"
registerDevice: "注册设备"
@@ -1301,11 +1248,9 @@ _widgets:
trends: "趋势"
clock: "时钟"
rss: "RSS阅读器"
rssTicker: "RSS Ticker"
activity: "活动"
photos: "照片"
digitalClock: "数字时钟"
unixClock: "UNIX时钟"
federation: "联邦宇宙"
instanceCloud: "实例云"
postForm: "投稿窗口"
@@ -1313,12 +1258,9 @@ _widgets:
button: "按钮"
onlineUsers: "在线用户"
jobQueue: "作业队列"
serverMetric: "服务器指标"
serverMetric: "服务器监控"
aiscript: "AiScript控制台"
aichan: "小蓝"
userList: "用户列表"
_userList:
chooseList: "选择列表"
_cw:
hide: "隐藏"
show: "查看更多"
@@ -1454,6 +1396,8 @@ _pages:
eyeCatchingImageRemove: "删除封面图片"
chooseBlock: "添加块"
selectType: "选择类型"
enterVariableName: "请输入变量名"
variableNameIsAlreadyUsed: "变量名已使用"
contentBlocks: "内容"
inputBlocks: "输入"
specialBlocks: "特殊"
@@ -1463,11 +1407,249 @@ _pages:
section: "章节"
image: "图片"
button: "按钮"
if: "如果"
_if:
variable: "变量"
post: "投稿窗口"
_post:
text: "内容"
attachCanvasImage: "附加画布图像"
canvasId: "画布ID"
textInput: "文本输入"
_textInput:
name: "变量名"
text: "标题"
default: "默认值"
textareaInput: "多行文本输入"
_textareaInput:
name: "变量名"
text: "标题"
default: "默认值"
numberInput: "输入数值"
_numberInput:
name: "变量名"
text: "标题"
default: "默认值"
canvas: "画布"
_canvas:
id: "画布ID"
width: "宽度"
height: "高度"
note: "嵌入的帖子"
_note:
id: "帖子ID"
idDescription: "您也可以通过粘贴帖子的URL来进行设置。"
detailed: "显示详细信息"
switch: "开关"
_switch:
name: "变量名"
text: "标题"
default: "默认值"
counter: "计数器"
_counter:
name: "变量名"
text: "标题"
inc: "增加值"
_button:
text: "标题"
colored: "彩色"
action: "按下按钮时的行为"
_action:
dialog: "显示对话框"
_dialog:
content: "内容"
resetRandom: "重置随机值"
pushEvent: "发送事件"
_pushEvent:
event: "事件名称"
message: "按下时显示的消息"
variable: "发送的变量"
no-variable: "空"
callAiScript: "调用AiScript"
_callAiScript:
functionName: "函数名"
radioButton: "选择项"
_radioButton:
name: "变量名"
title: "标题"
values: "使用换行区分的选择项"
default: "默认值"
script:
categories:
flow: "控制"
logical: "逻辑运算"
operation: "计算"
comparison: "比较"
random: "随机"
value: "值"
fn: "函数"
text: "文本操作"
convert: "转换"
list: "列表"
blocks:
text: "文本"
multiLineText: "文本 (多行)"
textList: "文本列表"
_textList:
info: "请使用换行符分隔每行"
strLen: "文本长度"
_strLen:
arg1: "文本"
strPick: "提取字符"
_strPick:
arg1: "文本"
arg2: "字符位置"
strReplace: "替换文本"
_strReplace:
arg1: "文本"
arg2: "替换之前"
arg3: "替换之后"
strReverse: "文本反向"
_strReverse:
arg1: "文本"
join: "合并文本"
_join:
arg1: "列表"
arg2: "分隔符"
add: "加"
_add:
arg1: "A"
arg2: "B"
subtract: "减"
_subtract:
arg1: "A"
arg2: "B"
multiply: "乘"
_multiply:
arg1: "A"
arg2: "B"
divide: "除"
_divide:
arg1: "A"
arg2: "B"
mod: "取模(MOD)"
_mod:
arg1: "A"
arg2: "B"
round: "四舍五入"
_round:
arg1: "数值"
eq: "A和B相等"
_eq:
arg1: "A"
arg2: "B"
notEq: "A和B不等"
_notEq:
arg1: "A"
arg2: "B"
and: "A和B"
_and:
arg1: "A"
arg2: "B"
or: "A或B"
_or:
arg1: "A"
arg2: "B"
lt: "< A小于B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A大于B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A小于等于B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A大于等于B"
_gtEq:
arg1: "A"
arg2: "B"
if: "分支"
_if:
arg1: "如果"
arg2: "如果"
arg3: "否则"
not: "否"
_not:
arg1: "否"
random: "随机"
_random:
arg1: "概率"
rannum: "随机数"
_rannum:
arg1: "最小值"
arg2: "最大值"
randomPick: "从列表中随机选择"
_randomPick:
arg1: "列表"
dailyRandom: "随机(每个用户每日)"
_dailyRandom:
arg1: "概率"
dailyRannum: "随机数(每个用户每日)"
_dailyRannum:
arg1: "最小值"
arg2: "最大值"
dailyRandomPick: "从列表中随机选择(每个用户每日)"
_dailyRandomPick:
arg1: "列表"
seedRandom: "随机 (种子)"
_seedRandom:
arg1: "种子"
arg2: "概率"
seedRannum: "随机数(种子)"
_seedRannum:
arg1: "种子"
arg2: "最小值"
arg3: "最大值"
seedRandomPick: "从列表中随机选择 (种子)"
_seedRandomPick:
arg1: "种子"
arg2: "列表"
DRPWPM: "从概率列表中随机选择(每用户每天)"
_DRPWPM:
arg1: "文本列表"
pick: "从列表中选择"
_pick:
arg1: "列表"
arg2: "位置"
listLen: "获取列表长度"
_listLen:
arg1: "列表"
number: "数值"
stringToNumber: "文本到数字"
_stringToNumber:
arg1: "文本"
numberToString: "数字到文本"
_numberToString:
arg1: "数值"
splitStrByLine: "将文本按行拆分"
_splitStrByLine:
arg1: "文本"
ref: "变量"
aiScriptVar: "AiScript变量"
fn: "函数"
_fn:
slots: "槽函数"
slots-info: "请使用换行符分隔每个槽函数"
arg1: "输出"
for: "重复"
_for:
arg1: "次数"
arg2: "处理"
typeError: "槽函数{slot}需要传入“{expect}”,但是实际传入为“{actual}”!"
thereIsEmptySlot: "槽函数{slot}为空!"
types:
string: "文字"
number: "数值"
boolean: "Flag"
array: "列表"
stringArray: "文本列表"
emptySlot: "空白槽函数"
enviromentVariables: "环境变量"
pageVariables: "页面元素"
argVariables: "输入变量"
_relayStatus:
requesting: "待批准"
accepted: "已批准"
@@ -1486,7 +1668,6 @@ _notification:
yourFollowRequestAccepted: "您的关注请求已通过"
youWereInvitedToGroup: "您有新的群组邀请"
pollEnded: "问卷调查结果已生成。"
unreadAntennaNote: "天线 {name}"
emptyPushNotificationMessage: "推送通知已更新"
_types:
all: "全部"
@@ -1510,19 +1691,13 @@ _deck:
alwaysShowMainColumn: "总是显示主列"
columnAlign: "列对齐"
addColumn: "添加列"
configureColumn: "列设置"
swapLeft: "向左移动"
swapRight: "向右移动"
swapUp: "向上移动"
swapDown: "向下移动"
stackLeft: "向左折叠"
popRight: "向右弹出"
profile: "配置文件"
newProfile: "新建配置文件"
deleteProfile: "删除配置文件"
introduction: "将各列进行组合以创建您自己的界面!"
introduction2: "您可以随时通过屏幕右侧的 + 来添加列"
widgetsIntroduction: "从列菜单中,选择“小工具编辑”来添加小工具"
profile: "个人资料"
_columns:
main: "主列"
widgets: "小工具"

View File

@@ -2,7 +2,6 @@
_lang_: "繁體中文"
headlineMisskey: "貼文連繫網路"
introMisskey: "歡迎! Misskey是一個開放原始碼且去中心化的社群網路。\n透過「貼文」分享周邊新鮮事並告訴其他人您的想法📡\n透過「反應」功能對大家的貼文表達情感👍\n一起來探索這個新的世界吧🚀"
poweredByMisskeyDescription: "{name}是使用開放原始碼平台<b>Misskey</b>的服務之一(稱為 Misskey 實例)。\n"
monthAndDay: "{month}月 {day}日"
search: "搜尋"
notifications: "通知"
@@ -13,7 +12,6 @@ fetchingAsApObject: "從聯邦宇宙取得中..."
ok: "OK"
gotIt: "知道了"
cancel: "取消"
noThankYou: "現在不要"
enterUsername: "輸入使用者名稱"
renotedBy: "{user} 轉傳了"
noNotes: "無貼文。"
@@ -49,13 +47,11 @@ deleteAndEdit: "刪除並編輯"
deleteAndEditConfirm: "要刪除並再次編輯嗎?此貼文的所有情感、轉發和回覆也將會消失。"
addToList: "加入至清單"
sendMessage: "發送訊息"
copyRSS: "複製RSS"
copyUsername: "複製使用者名稱"
searchUser: "搜尋使用者"
reply: "回覆"
loadMore: "載入更多"
showMore: "載入更多"
showLess: "關閉"
youGotNewFollower: "您有新的追隨者"
receiveFollowRequest: "您有新的追隨請求"
followRequestAccepted: "追隨請求已接受"
@@ -159,7 +155,7 @@ searchWith: "搜尋: {q}"
youHaveNoLists: "你沒有任何清單"
followConfirm: "你真的要追隨{name}嗎?"
proxyAccount: "代理帳戶"
proxyAccountDescription: "代理帳戶是在某些情況下充當其他伺服器用戶的帳戶。例如,當使用者將一個來自其他伺服器的帳戶放在列表中時,由於沒有其他使用者追蹤該帳戶,該指令不會傳送到該伺服器上,因此會由代理帳戶追蹤。"
proxyAccountDescription: "代理帳戶是在某些情況下充當其他伺服器用戶的帳戶。例如,當使用者將一個來自其他伺服器的帳戶放在列表中時,由於沒有其他使用者關注該帳戶,該指令不會傳送到該伺服器上,因此會由代理帳戶關注。"
host: "主機"
selectUser: "選取使用者"
recipient: "收件人"
@@ -338,7 +334,7 @@ bannerUrl: "橫幅圖像URL"
backgroundImageUrl: "背景圖片的來源網址 "
basicInfo: "基本資訊"
pinnedUsers: "置頂用戶"
pinnedUsersDescription: "在「探索」頁面中使用換行標記想要置頂的使用者。"
pinnedUsersDescription: "在「發現」頁面中使用換行標記想要置頂的使用者。"
pinnedPages: "釘選頁面"
pinnedPagesDescription: "輸入要固定至實例首頁的頁面路徑,以換行符分隔。"
pinnedClipId: "置頂的摘錄ID"
@@ -351,10 +347,6 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "啟用 reCAPTCHA"
recaptchaSiteKey: "網站金鑰"
recaptchaSecretKey: "金鑰"
turnstile: "Turnstile"
enableTurnstile: "啟用 Turnstile"
turnstileSiteKey: "網站金鑰"
turnstileSecretKey: "金鑰"
avoidMultiCaptchaConfirm: "使用多種驗證方式可能會造成干擾,您要關閉其他驗證方式嗎?您可以按“取消”保留多種驗證方式。"
antennas: "天線"
manageAntennas: "管理天線"
@@ -457,8 +449,7 @@ language: "語言"
uiLanguage: "介面語言"
groupInvited: "您有新的群組邀請"
aboutX: "關於{x}"
emojiStyle: "表情符號的風格"
native: "原生"
useOsNativeEmojis: "使用OS原生表情符號"
disableDrawer: "不顯示下拉式選單"
youHaveNoGroups: "找不到群組"
joinOrCreateGroup: "請加入現有群組,或創建新群組。"
@@ -511,7 +502,6 @@ deleteAll: "刪除所有記錄"
showFixedPostForm: "於時間軸頁頂顯示「發送貼文」方框"
newNoteRecived: "發現新的貼文"
sounds: "音效"
sound: "音效"
listen: "聆聽"
none: "無"
showInPage: "在頁面中顯示"
@@ -571,7 +561,6 @@ author: "作者"
leaveConfirm: "有未保存的更改。要放棄嗎?"
manage: "管理"
plugins: "外掛"
preferencesBackups: "備份設定檔"
deck: "多欄模式"
undeck: "取消多欄模式"
useBlurEffectForModal: "在模態框使用模糊效果"
@@ -694,8 +683,8 @@ useSystemFont: "使用系統預設的字型"
clips: "摘錄"
experimentalFeatures: "實驗中的功能"
developer: "開發者"
makeExplorable: "使自己的帳戶能夠在探索頁面中顯示"
makeExplorableDescription: "如果關閉,帳戶將不會被顯示在「探索」頁面中。"
makeExplorable: "使自己的帳戶能夠在探索頁面中顯示"
makeExplorableDescription: "如果關閉,帳戶將不會被顯示在\"探索\"頁面中。"
showGapBetweenNotesInTimeline: "分開顯示時間線上的貼文。"
duplicate: "複製"
left: "左"
@@ -717,7 +706,6 @@ accentColor: "重點色彩"
textColor: "文字"
saveAs: "另存為..."
advanced: "進階"
advancedSettings: "進階設定"
value: "數值"
createdAt: "建立於"
updatedAt: "最後更新"
@@ -893,30 +881,14 @@ sensitiveMediaDetection: "敏感性媒體的檢測"
localOnly: "僅限本地"
remoteOnly: "僅限遠端"
failedToUpload: "上傳失敗"
cannotUploadBecauseInappropriate: "由於判定可能包含不適當的內容,因此無法上。"
cannotUploadBecauseNoFreeSpace: "由於雲端硬碟沒有可用空間,因此無法上傳。"
cannotUploadBecauseInappropriate: "由於判定可能包含不適當的內容,因此無法上。"
cannotUploadBecauseNoFreeSpace: "由於雲端硬碟沒有可用空間,因此無法上船>"
beta: "Beta"
enableAutoSensitive: "自動NSFW判定"
enableAutoSensitiveDescription: "如果可用,請利用機器學習在媒體上自動設置 NSFW 旗標。 即使關閉此功能,依實例而定也可能會自動設置。"
activeEmailValidationDescription: "積極地驗證用戶的電子郵件地址,判斷它是否為免洗地址,或者它是否可以通信。 若關閉,則只會檢查字元是否正確。"
navbar: "導覽列"
shuffle: "隨機"
account: "帳戶"
move: "移動 "
pushNotification: "推播通知"
subscribePushNotification: "啟用推播通知"
unsubscribePushNotification: "停止推播通知"
pushNotificationAlreadySubscribed: "推播通知啟用中"
pushNotificationNotSupported: "瀏覽器或實例不支援推播通知"
sendPushNotificationReadMessage: "通知與訊息如果已讀的話,就將推播通知刪除"
sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」通知將立刻顯示。可能會增加設備的電池消耗。"
windowMaximize: "最大化"
windowRestore: "復原"
caption: "標題"
loggedInAsBot: "以機器人帳號登入中"
tools: "工具"
cannotLoad: "無法載入"
numberOfProfileView: "個人檔案檢視次數"
_sensitiveMediaDetection:
description: "您可以使用機器學習自動檢測敏感媒體並將其用於審核。 伺服器的負荷會稍微增加。"
sensitivity: "檢測敏感度"
@@ -941,7 +913,7 @@ _signup:
emailSent: "已將確認郵件發送至您輸入的電子郵件地址 ({email})。請開啟電子郵件中的連結以完成帳戶創建。"
_accountDelete:
accountDelete: "刪除帳戶"
mayTakeTime: "刪除帳戶的處理負荷較大,如果帳戶產生的內容數量上的檔案數量較多的話,就需要花费一段時間才能完成。"
mayTakeTime: "刪除帳戶的處理負荷較大,如果帳戶產生的內容數量上的檔案數量較多的話,就需要花费一段時間才能完成。"
sendEmail: "帳戶删除完成後,將向註冊地電子郵件地址發送通知。"
requestAccountDelete: "刪除帳戶請求"
started: "已開始刪除作業。"
@@ -967,24 +939,6 @@ _plugin:
install: "安裝外掛組件"
installWarn: "請不要安裝來源不明的外掛組件。"
manage: "管理外掛"
_preferencesBackups:
list: "已備份的設定檔"
saveNew: "另存新檔"
loadFile: "讀取檔案"
apply: "套用在此裝置"
save: "覆蓋存檔"
inputName: "輸入備份檔名稱"
cannotSave: "無法儲存"
nameAlreadyExists: "備份檔名稱「{name}」已經存在。請指定不同的名稱。"
applyConfirm: "將備份檔「{name}」套用在現在的裝置嗎?現在的裝置設定將會消失。"
saveConfirm: "要覆蓋存檔{name}嗎?"
deleteConfirm: "要刪除{name}嗎?"
renameConfirm: "要將「{old}」變更為「{new}」嗎?"
noBackups: "沒有備份檔。您可以用「另存新檔」將現在的客戶端設定儲存在伺服器上。"
createdAt: "建立日期:{date} {time}"
updatedAt: "更新日期:{date} {time}"
cannotLoad: "無法讀取"
invalidFile: "檔案形式錯誤。"
_registry:
scope: "範圍"
key: "機碼"
@@ -1068,8 +1022,6 @@ _mfm:
sparkleDescription: "添加閃閃發光的粒子效果。"
rotate: "旋轉"
rotateDescription: "以指定的角度旋轉。"
plain: "簡潔"
plainDescription: "停用全部的內部語法。"
_instanceTicker:
none: "隱藏"
remote: "向遠端使用者顯示"
@@ -1227,9 +1179,6 @@ _tutorial:
step7_1: "以上為Misskey的基本操作說明教學在此告一段落。辛苦了。"
step7_2: "歡迎到{help}來瞭解更多Misskey相關介紹。"
step7_3: "那麼祝您在Misskey玩的開心~ 🚀"
step8_1: "最後,要不要試試看啟用推播通知呢?"
step8_2: "透過接收推播通知即使沒有打開Misskey您也會知道反應、追隨與提及的情況。"
step8_3: "通知的設定可以在之後變更。"
_2fa:
alreadyRegistered: "此設備已經被註冊過了"
registerDevice: "註冊裝置"
@@ -1306,7 +1255,6 @@ _widgets:
activity: "動態"
photos: "照片"
digitalClock: "電子時鐘"
unixClock: "UNIX時間"
federation: "聯邦宇宙"
instanceCloud: "實例雲"
postForm: "發佈窗口"
@@ -1317,9 +1265,6 @@ _widgets:
serverMetric: "服務器指標 "
aiscript: "AiScript控制台"
aichan: "小藍"
userList: "使用者列表"
_userList:
chooseList: "選擇清單"
_cw:
hide: "隱藏"
show: "瀏覽更多"
@@ -1383,7 +1328,6 @@ _profile:
changeBanner: "變更橫幅圖像"
_exportOrImport:
allNotes: "所有貼文"
favoritedNotes: "「我的最愛」貼文"
followingList: "追隨中"
muteList: "靜音"
blockingList: "封鎖"
@@ -1456,6 +1400,8 @@ _pages:
eyeCatchingImageRemove: "刪除封面影像"
chooseBlock: "新增方塊"
selectType: "選擇類型"
enterVariableName: "請輸入變數名稱"
variableNameIsAlreadyUsed: "變數名稱已被佔用"
contentBlocks: "內容"
inputBlocks: "輸入"
specialBlocks: "特殊"
@@ -1465,11 +1411,249 @@ _pages:
section: "區段"
image: "圖片"
button: "按鈕"
if: "如果"
_if:
variable: "變數"
post: "發佈窗口"
_post:
text: "内容"
attachCanvasImage: "附加相簿圖像 "
canvasId: "畫布ID"
textInput: "插入字串"
_textInput:
name: "變數名稱"
text: "標題"
default: "預設值"
textareaInput: "多行文字输入"
_textareaInput:
name: "變數名稱"
text: "標題"
default: "預設值"
numberInput: "輸入數值"
_numberInput:
name: "變數名稱"
text: "標題"
default: "預設值"
canvas: "畫布"
_canvas:
id: "畫布ID"
width: "寬度"
height: "高度"
note: "嵌式貼文"
_note:
id: "貼文ID"
idDescription: "您也可以粘貼筆記 URL 並進行設置。 "
detailed: "顯示詳細內容"
switch: "開關"
_switch:
name: "變數名稱"
text: "標題"
default: "預設值"
counter: "計數器"
_counter:
name: "變數名稱"
text: "標題"
inc: "増加値"
_button:
text: "標題"
colored: "彩色"
action: "按下按鈕後發生的行為"
_action:
dialog: "顯示對話框 "
_dialog:
content: "内容"
resetRandom: "重設亂數"
pushEvent: "發送事件"
_pushEvent:
event: "事件名稱"
message: "按下時顯示的消息 "
variable: "要發送的變數"
no-variable: "沒有"
callAiScript: "調用AiScript"
_callAiScript:
functionName: "函數名稱"
radioButton: "選項"
_radioButton:
name: "變數名稱"
title: "標題"
values: "由換行符分隔的選項"
default: "預設值"
script:
categories:
flow: "控制"
logical: "邏輯運算"
operation: "計算"
comparison: "對比"
random: "隨機"
value: "數值 "
fn: "函数"
text: "文本操作"
convert: "轉換"
list: "清單"
blocks:
text: "字串"
multiLineText: "字串(多行)"
textList: "字串串列"
_textList:
info: "請分開每個換行符 "
strLen: "字串長度"
_strLen:
arg1: "字串"
strPick: "提取字元"
_strPick:
arg1: "字串"
arg2: "字元位置"
strReplace: "替換字串"
_strReplace:
arg1: "字串"
arg2: "替換前"
arg3: "替換後"
strReverse: "倒轉字串"
_strReverse:
arg1: "字串"
join: "合併字串"
_join:
arg1: "清單"
arg2: "分隔字元"
add: "加"
_add:
arg1: "A"
arg2: "B"
subtract: "减去"
_subtract:
arg1: "A"
arg2: "B"
multiply: "乘"
_multiply:
arg1: "A"
arg2: "B"
divide: "除"
_divide:
arg1: "A"
arg2: "B"
mod: "餘數"
_mod:
arg1: "A"
arg2: "B"
round: "四舍五入"
_round:
arg1: "數值"
eq: "A和B相等"
_eq:
arg1: "A"
arg2: "B"
notEq: "A和B不等"
_notEq:
arg1: "A"
arg2: "B"
and: "A和B"
_and:
arg1: "A"
arg2: "B"
or: "A或B"
_or:
arg1: "A"
arg2: "B"
lt: "< A小於B"
_lt:
arg1: "A"
arg2: "B"
gt: "> A大於B"
_gt:
arg1: "A"
arg2: "B"
ltEq: "<= A小於或等於B"
_ltEq:
arg1: "A"
arg2: "B"
gtEq: ">= A大於或等於B"
_gtEq:
arg1: "A"
arg2: "B"
if: "分支"
_if:
arg1: "如果"
arg2: "如果"
arg3: "除此以外 "
not: "否"
_not:
arg1: "否"
random: "隨機"
_random:
arg1: "機率"
rannum: "亂數"
_rannum:
arg1: "下限"
arg2: "上限"
randomPick: "從列表中隨機選擇 "
_randomPick:
arg1: "清單"
dailyRandom: "隨機(使用者每日變化 )"
_dailyRandom:
arg1: "機率"
dailyRannum: "亂數(使用者每日變化)"
_dailyRannum:
arg1: "下限"
arg2: "上限"
dailyRandomPick: "從列表中隨機選擇(使用者每日變化 "
_dailyRandomPick:
arg1: "清單"
seedRandom: "隨機抽選種子碼"
_seedRandom:
arg1: "種子"
arg2: "機率"
seedRannum: "亂數 (種子)"
_seedRannum:
arg1: "種子"
arg2: "最小值"
arg3: "最大值"
seedRandomPick: "從列表中隨機選擇 (種子)"
_seedRandomPick:
arg1: "種子"
arg2: "清單"
DRPWPM: "从機率列表中隨機選擇(每個用户每天)"
_DRPWPM:
arg1: "字串串列"
pick: "從清單中選取"
_pick:
arg1: "清單"
arg2: "位置"
listLen: "取得清單長度"
_listLen:
arg1: "清單"
number: "數值"
stringToNumber: "將字串轉換至數値"
_stringToNumber:
arg1: "字串"
numberToString: "將數値轉換至字串"
_numberToString:
arg1: "數值"
splitStrByLine: "於換行時分割字串"
_splitStrByLine:
arg1: "字串"
ref: "變數"
aiScriptVar: "AiScript的變數"
fn: "函数"
_fn:
slots: "欄位"
slots-info: "用換行符分隔每個欄位"
arg1: "輸出"
for: "重複 "
_for:
arg1: "重複次數"
arg2: "處理"
typeError: "槽參數{slot}需要傳入“{expect}”,但是實際傳入為“{actual}”!"
thereIsEmptySlot: "參數{slot}是空的!"
types:
string: "字串"
number: "数值"
boolean: "標記"
array: "清單"
stringArray: "字串列表"
emptySlot: "空欄位"
enviromentVariables: "環境變數"
pageVariables: "頁面元素"
argVariables: "輸入欄位"
_relayStatus:
requesting: "等待核准"
accepted: "已通過核准"
@@ -1488,7 +1672,6 @@ _notification:
yourFollowRequestAccepted: "您的追隨請求已通過"
youWereInvitedToGroup: "您有新的群組邀請"
pollEnded: "問卷調查已產生結果"
unreadAntennaNote: "天線 {name}"
emptyPushNotificationMessage: "推送通知已更新"
_types:
all: "全部 "
@@ -1512,7 +1695,6 @@ _deck:
alwaysShowMainColumn: "總是顯示主欄"
columnAlign: "對齊欄位"
addColumn: "新增欄位"
configureColumn: "欄位的設定"
swapLeft: "向左移動"
swapRight: "向右移動"
swapUp: "往上移動"

View File

@@ -1,64 +1,50 @@
{
"name": "misskey",
"version": "13.0.0-beta.16",
"version": "12.116.0",
"codename": "indigo",
"repository": {
"type": "git",
"url": "https://github.com/misskey-dev/misskey.git"
},
"packageManager": "yarn@3.3.0",
"workspaces": [
"packages/frontend",
"packages/backend",
"packages/sw"
],
"private": true,
"scripts": {
"build-pre": "node ./scripts/build-pre.js",
"build": "yarn build-pre && yarn workspaces foreach run build && yarn run gulp",
"start": "cd packages/backend && node ./built/boot/index.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node ./built/boot/index.js",
"init": "yarn migrate",
"migrate": "cd packages/backend && yarn run typeorm migration:run -d ormconfig.js",
"migrateandstart": "yarn migrate && yarn start",
"postinstall": "node ./scripts/install-packages.js",
"build": "node ./scripts/build.js",
"start": "cd packages/backend && node --experimental-json-modules ./built/index.js",
"start:test": "cd packages/backend && cross-env NODE_ENV=test node --experimental-json-modules ./built/index.js",
"init": "npm run migrate",
"migrate": "cd packages/backend && npx typeorm migration:run -d ormconfig.js",
"migrateandstart": "npm run migrate && npm run start",
"gulp": "gulp build",
"watch": "yarn dev",
"watch": "npm run dev",
"dev": "node ./scripts/dev.js",
"lint": "yarn workspaces foreach run lint",
"lint": "node ./scripts/lint.js",
"cy:open": "cypress open --browser --e2e --config-file=cypress.config.ts",
"cy:run": "cypress run",
"e2e": "start-server-and-test start:test http://localhost:61812 cy:run",
"jest": "cd packages/backend && cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand",
"jest-and-coverage": "cd packages/backend && cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand",
"test": "yarn jest",
"test-and-coverage": "yarn jest-and-coverage",
"mocha": "cd packages/backend && cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" npx mocha",
"test": "npm run mocha",
"format": "gulp format",
"clean": "node ./scripts/clean.js",
"clean-all": "node ./scripts/clean-all.js",
"cleanall": "yarn clean-all"
},
"resolutions": {
"chokidar": "^3.3.1",
"lodash": "^4.17.21"
"cleanall": "npm run clean-all"
},
"dependencies": {
"execa": "5.1.1",
"gulp": "4.0.2",
"gulp-cssnano": "2.1.3",
"gulp-rename": "2.0.0",
"gulp-replace": "1.1.4",
"gulp-replace": "1.1.3",
"gulp-terser": "2.1.0",
"js-yaml": "4.1.0"
},
"devDependencies": {
"@types/gulp": "4.0.10",
"@types/gulp": "4.0.9",
"@types/gulp-rename": "2.0.1",
"@typescript-eslint/eslint-plugin": "5.47.1",
"@typescript-eslint/parser": "5.47.1",
"@typescript-eslint/parser": "5.30.6",
"cross-env": "7.0.3",
"cypress": "12.2.0",
"eslint": "^8.30.0",
"start-server-and-test": "1.15.2",
"typescript": "4.9.4"
"cypress": "10.3.0",
"start-server-and-test": "1.14.0",
"typescript": "4.7.4"
}
}

View File

@@ -1,3 +0,0 @@
{
"tsConfig": "./tsconfig.json"
}

View File

@@ -0,0 +1,10 @@
{
"extension": ["ts","js","cjs","mjs"],
"node-option": [
"experimental-specifier-resolution=node",
"loader=./test/loader.js"
],
"slow": 1000,
"timeout": 30000,
"exit": true
}

2
packages/backend/.npmrc Normal file
View File

@@ -0,0 +1,2 @@
save-exact = true
package-lock = false

View File

@@ -1,15 +0,0 @@
{
"$schema": "https://json.schemastore.org/swcrc",
"jsc": {
"parser": {
"syntax": "typescript",
"dynamicImport": true,
"decorators": true
},
"transform": {
"legacyDecorator": true,
"decoratorMetadata": true
}
},
"minify": false
}

1
packages/backend/.yarnrc Normal file
View File

@@ -0,0 +1 @@
network-timeout 600000

View File

@@ -1,2 +0,0 @@
# Misskey Backend
![](../../assets/backend.png)

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 KiB

View File

@@ -1,14 +0,0 @@
// https://github.com/facebook/jest/issues/12270#issuecomment-1194746382
const nativeModule = require('node:module');
function resolver(module, options) {
const { basedir, defaultResolver } = options;
try {
return defaultResolver(module, options);
} catch (error) {
return nativeModule.createRequire(basedir).resolve(module);
}
}
module.exports = resolver;

View File

@@ -1,202 +0,0 @@
/*
* For a detailed explanation regarding each configuration property and type check, visit:
* https://jestjs.io/docs/en/configuration.html
*/
module.exports = {
// All imported modules in your tests should be mocked automatically
// automock: false,
// Stop running tests after `n` failures
// bail: 0,
// The directory where Jest should store its cached dependency information
// cacheDirectory: "C:\\Users\\ai\\AppData\\Local\\Temp\\jest",
// Automatically clear mock calls and instances between every test
// clearMocks: false,
// Indicates whether the coverage information should be collected while executing the test
// collectCoverage: false,
// An array of glob patterns indicating a set of files for which coverage information should be collected
collectCoverageFrom: ['src/**/*.ts'],
// The directory where Jest should output its coverage files
coverageDirectory: "coverage",
// An array of regexp pattern strings used to skip coverage collection
// coveragePathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// Indicates which provider should be used to instrument code for coverage
coverageProvider: "v8",
// A list of reporter names that Jest uses when writing coverage reports
// coverageReporters: [
// "json",
// "text",
// "lcov",
// "clover"
// ],
// An object that configures minimum threshold enforcement for coverage results
// coverageThreshold: undefined,
// A path to a custom dependency extractor
// dependencyExtractor: undefined,
// Make calling deprecated APIs throw helpful error messages
// errorOnDeprecated: false,
// Force coverage collection from ignored files using an array of glob patterns
// forceCoverageMatch: [],
// A path to a module which exports an async function that is triggered once before all test suites
// globalSetup: undefined,
// A path to a module which exports an async function that is triggered once after all test suites
// globalTeardown: undefined,
// A set of global variables that need to be available in all test environments
globals: {
},
// The maximum amount of workers used to run your tests. Can be specified as % or a number. E.g. maxWorkers: 10% will use 10% of your CPU amount + 1 as the maximum worker number. maxWorkers: 2 will use a maximum of 2 workers.
// maxWorkers: "50%",
// An array of directory names to be searched recursively up from the requiring module's location
// moduleDirectories: [
// "node_modules"
// ],
// An array of file extensions your modules use
// moduleFileExtensions: [
// "js",
// "json",
// "jsx",
// "ts",
// "tsx",
// "node"
// ],
// A map from regular expressions to module names or to arrays of module names that allow to stub out resources with a single module
moduleNameMapper: {
"^@/(.*?).js": "<rootDir>/src/$1.ts",
'^(\\.{1,2}/.*)\\.js$': '$1',
},
// An array of regexp pattern strings, matched against all module paths before considered 'visible' to the module loader
// modulePathIgnorePatterns: [],
// Activates notifications for test results
// notify: false,
// An enum that specifies notification mode. Requires { notify: true }
// notifyMode: "failure-change",
// A preset that is used as a base for Jest's configuration
//preset: "ts-jest/presets/js-with-ts-esm",
// Run tests from one or more projects
// projects: undefined,
// Use this configuration option to add custom reporters to Jest
// reporters: undefined,
// Automatically reset mock state between every test
// resetMocks: false,
// Reset the module registry before running each individual test
// resetModules: false,
// A path to a custom resolver
resolver: './jest-resolver.cjs',
// Automatically restore mock state between every test
restoreMocks: true,
// The root directory that Jest should scan for tests and modules within
// rootDir: undefined,
// A list of paths to directories that Jest should use to search for files in
roots: [
"<rootDir>"
],
// Allows you to use a custom runner instead of Jest's default test runner
// runner: "jest-runner",
// The paths to modules that run some code to configure or set up the testing environment before each test
// setupFiles: [],
// A list of paths to modules that run some code to configure or set up the testing framework before each test
// setupFilesAfterEnv: [],
// The number of seconds after which a test is considered as slow and reported as such in the results.
// slowTestThreshold: 5,
// A list of paths to snapshot serializer modules Jest should use for snapshot testing
// snapshotSerializers: [],
// The test environment that will be used for testing
testEnvironment: "node",
// Options that will be passed to the testEnvironment
// testEnvironmentOptions: {},
// Adds a location field to test results
// testLocationInResults: false,
// The glob patterns Jest uses to detect test files
testMatch: [
"<rootDir>/test/unit/**/*.ts",
//"<rootDir>/test/e2e/**/*.ts"
],
// An array of regexp pattern strings that are matched against all test paths, matched tests are skipped
// testPathIgnorePatterns: [
// "\\\\node_modules\\\\"
// ],
// The regexp pattern or array of patterns that Jest uses to detect test files
// testRegex: [],
// This option allows the use of a custom results processor
// testResultsProcessor: undefined,
// This option allows use of a custom test runner
// testRunner: "jasmine2",
// This option sets the URL for the jsdom environment. It is reflected in properties such as location.href
// testURL: "http://localhost",
// Setting this value to "fake" allows the use of fake timers for functions such as "setTimeout"
// timers: "real",
// A map from regular expressions to paths to transformers
transform: {
"^.+\\.(t|j)sx?$": ["@swc/jest"],
},
// An array of regexp pattern strings that are matched against all source file paths, matched files will skip transformation
// transformIgnorePatterns: [
// "\\\\node_modules\\\\",
// "\\.pnp\\.[^\\\\]+$"
// ],
// An array of regexp pattern strings that are matched against all modules before the module loader will automatically return a mock for them
// unmockedModulePathPatterns: undefined,
// Indicates whether each individual test should be reported during the run
// verbose: undefined,
// An array of regexp patterns that are matched against all source file paths before re-running tests in watch mode
// watchPathIgnorePatterns: [],
// Whether to use watchman for file crawling
// watchman: true,
extensionsToTreatAsEsm: ['.ts'],
};

View File

@@ -30,9 +30,9 @@ export class foreignKeyReports1651224615271 {
queryRunner.query(`CREATE INDEX "IDX_f22169eb10657bded6d875ac8f" ON "note" ("channelId")`),
//queryRunner.query(`CREATE INDEX "IDX_a9021cc2e1feb5f72d3db6e9f5" ON "abuse_user_report" ("targetUserId")`),
//queryRunner.query(`DELETE FROM "abuse_user_report" WHERE "targetUserId" NOT IN (SELECT "id" FROM "user")`).then(() => {
// queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
//}),
queryRunner.query(`DELETE FROM "abuse_user_report" WHERE "targetUserId" NOT IN (SELECT "id" FROM "user")`).then(() => {
queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}),
queryRunner.query(`ALTER TABLE "poll" ADD CONSTRAINT "UQ_da851e06d0dfe2ef397d8b1bf1b" UNIQUE ("noteId")`),
queryRunner.query(`ALTER TABLE "user_keypair" ADD CONSTRAINT "UQ_f4853eb41ab722fe05f81cedeb6" UNIQUE ("userId")`),

View File

@@ -1,15 +0,0 @@
export class turnstile1664694635394 {
name = 'turnstile1664694635394'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "enableTurnstile" boolean NOT NULL DEFAULT false`);
await queryRunner.query(`ALTER TABLE "meta" ADD "turnstileSiteKey" character varying(64)`);
await queryRunner.query(`ALTER TABLE "meta" ADD "turnstileSecretKey" character varying(64)`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "turnstileSecretKey"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "turnstileSiteKey"`);
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableTurnstile"`);
}
}

View File

@@ -1,11 +0,0 @@
export class whetherPushNotifyToSendReadMessage1669138716634 {
name = 'whetherPushNotifyToSendReadMessage1669138716634'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "sw_subscription" ADD "sendReadMessage" boolean NOT NULL DEFAULT false`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "sw_subscription" DROP COLUMN "sendReadMessage"`);
}
}

View File

@@ -1,13 +0,0 @@
export class RetentionAggregation1671924750884 {
name = 'RetentionAggregation1671924750884'
async up(queryRunner) {
await queryRunner.query(`CREATE TABLE "retention_aggregation" ("id" character varying(32) NOT NULL, "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL, "userIds" character varying(32) array NOT NULL, "data" jsonb NOT NULL DEFAULT '{}', CONSTRAINT "PK_22aad3e8640b15fb3b90ee02d18" PRIMARY KEY ("id")); COMMENT ON COLUMN "retention_aggregation"."createdAt" IS 'The created date of the Note.'`);
await queryRunner.query(`CREATE INDEX "IDX_09f4e5b9e4a2f268d3e284e4b3" ON "retention_aggregation" ("createdAt") `);
}
async down(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_09f4e5b9e4a2f268d3e284e4b3"`);
await queryRunner.query(`DROP TABLE "retention_aggregation"`);
}
}

View File

@@ -1,15 +0,0 @@
export class RetentionAggregation21671926422832 {
name = 'RetentionAggregation21671926422832'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "retention_aggregation" ADD "updatedAt" TIMESTAMP WITH TIME ZONE NOT NULL`);
await queryRunner.query(`COMMENT ON COLUMN "retention_aggregation"."updatedAt" IS 'The updated date of the GalleryPost.'`);
await queryRunner.query(`ALTER TABLE "retention_aggregation" ADD "usersCount" integer NOT NULL`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "retention_aggregation" DROP COLUMN "usersCount"`);
await queryRunner.query(`COMMENT ON COLUMN "retention_aggregation"."updatedAt" IS 'The updated date of the GalleryPost.'`);
await queryRunner.query(`ALTER TABLE "retention_aggregation" DROP COLUMN "updatedAt"`);
}
}

View File

@@ -1,17 +0,0 @@
export class PerUserPvChart1672562400597 {
name = 'PerUserPvChart1672562400597'
async up(queryRunner) {
await queryRunner.query(`CREATE TABLE "__chart__per_user_pv" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "unique_temp___upv_user" character varying array NOT NULL DEFAULT '{}', "___upv_user" smallint NOT NULL DEFAULT '0', "___pv_user" smallint NOT NULL DEFAULT '0', "unique_temp___upv_visitor" character varying array NOT NULL DEFAULT '{}', "___upv_visitor" smallint NOT NULL DEFAULT '0', "___pv_visitor" smallint NOT NULL DEFAULT '0', CONSTRAINT "UQ_f2a56da57921ca8439f45c1d95f" UNIQUE ("date", "group"), CONSTRAINT "PK_3c938a24f0203b5bd13fab51059" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f2a56da57921ca8439f45c1d95" ON "__chart__per_user_pv" ("date", "group") `);
await queryRunner.query(`CREATE TABLE "__chart_day__per_user_pv" ("id" SERIAL NOT NULL, "date" integer NOT NULL, "group" character varying(128) NOT NULL, "unique_temp___upv_user" character varying array NOT NULL DEFAULT '{}', "___upv_user" smallint NOT NULL DEFAULT '0', "___pv_user" smallint NOT NULL DEFAULT '0', "unique_temp___upv_visitor" character varying array NOT NULL DEFAULT '{}', "___upv_visitor" smallint NOT NULL DEFAULT '0', "___pv_visitor" smallint NOT NULL DEFAULT '0', CONSTRAINT "UQ_f221e45cfac5bea0ce0f3149fbb" UNIQUE ("date", "group"), CONSTRAINT "PK_0085d7542f6772e99b9dcfb0a9c" PRIMARY KEY ("id"))`);
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_f221e45cfac5bea0ce0f3149fb" ON "__chart_day__per_user_pv" ("date", "group") `);
}
async down(queryRunner) {
await queryRunner.query(`DROP INDEX "public"."IDX_f221e45cfac5bea0ce0f3149fb"`);
await queryRunner.query(`DROP TABLE "__chart_day__per_user_pv"`);
await queryRunner.query(`DROP INDEX "public"."IDX_f2a56da57921ca8439f45c1d95"`);
await queryRunner.query(`DROP TABLE "__chart__per_user_pv"`);
}
}

View File

@@ -1,8 +1,6 @@
import { DataSource } from 'typeorm';
import { loadConfig } from './built/config.js';
import { entities } from './built/postgre.js';
const config = loadConfig();
import config from './built/config/index.js';
import { entities } from './built/db/postgre.js';
export default new DataSource({
type: 'postgres',

View File

@@ -1,186 +1,182 @@
{
"name": "backend",
"main": "./index.js",
"private": true,
"type": "module",
"scripts": {
"start": "node ./built/index.js",
"start:test": "NODE_ENV=test node ./built/index.js",
"migrate": "typeorm migration:run -d ormconfig.js",
"build": "tsc -p tsconfig.json || echo done. && tsc-alias -p tsconfig.json",
"watch": "node watch.mjs",
"lint": "tsc --noEmit && eslint --quiet \"src/**/*.ts\"",
"jest": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --forceExit --runInBand",
"jest-and-coverage": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --coverage --forceExit --runInBand",
"jest-clear": "cross-env NODE_ENV=test node --experimental-vm-modules --experimental-import-meta-resolve node_modules/jest/bin/jest.js --clearCache",
"test": "yarn jest",
"test-and-coverage": "yarn jest-and-coverage"
"lint": "eslint --quiet \"src/**/*.ts\"",
"mocha": "cross-env NODE_ENV=test TS_NODE_FILES=true TS_NODE_TRANSPILE_ONLY=true TS_NODE_PROJECT=\"./test/tsconfig.json\" mocha",
"test": "npm run mocha"
},
"resolutions": {
"chokidar": "^3.3.1",
"lodash": "^4.17.21"
},
"optionalDependencies": {
"@tensorflow/tfjs": "^4.1.0",
"@tensorflow/tfjs-node": "4.1.0"
"@tensorflow/tfjs-node": "3.18.0"
},
"dependencies": {
"@bull-board/api": "^4.9.0",
"@bull-board/fastify": "^4.9.0",
"@bull-board/ui": "^4.9.0",
"@bull-board/koa": "4.0.0",
"@discordapp/twemoji": "14.0.2",
"@fastify/accepts": "4.1.0",
"@fastify/cookie": "^8.3.0",
"@fastify/cors": "8.2.0",
"@fastify/http-proxy": "^8.4.0",
"@fastify/multipart": "7.3.0",
"@fastify/static": "6.6.0",
"@fastify/view": "7.3.0",
"@nestjs/common": "9.2.1",
"@nestjs/core": "9.2.1",
"@nestjs/testing": "9.2.1",
"@peertube/http-signature": "1.7.0",
"@sinonjs/fake-timers": "10.0.2",
"@elastic/elasticsearch": "7.11.0",
"@koa/cors": "3.1.0",
"@koa/multer": "3.0.0",
"@koa/router": "9.0.1",
"@peertube/http-signature": "1.6.0",
"@sinonjs/fake-timers": "9.1.2",
"@syuilo/aiscript": "0.11.1",
"accepts": "^1.3.8",
"ajv": "8.11.2",
"abort-controller": "3.0.0",
"ajv": "8.11.0",
"archiver": "5.3.1",
"autobind-decorator": "2.4.0",
"autwh": "0.1.0",
"aws-sdk": "2.1272.0",
"aws-sdk": "2.1165.0",
"bcryptjs": "2.4.3",
"blurhash": "2.0.4",
"bull": "4.10.2",
"cacheable-lookup": "6.1.0",
"blurhash": "1.1.5",
"bull": "4.8.4",
"cacheable-lookup": "6.0.4",
"cbor": "8.1.0",
"chalk": "5.2.0",
"chalk": "5.0.1",
"chalk-template": "0.4.0",
"chokidar": "3.5.3",
"cli-highlight": "2.1.11",
"color-convert": "2.0.1",
"content-disposition": "0.5.4",
"date-fns": "2.29.3",
"date-fns": "2.28.0",
"deep-email-validator": "0.1.21",
"escape-regexp": "0.0.1",
"fastify": "4.10.2",
"feed": "4.2.2",
"file-type": "18.0.0",
"file-type": "17.1.2",
"fluent-ffmpeg": "2.1.2",
"form-data": "^4.0.0",
"got": "12.5.3",
"hpagent": "1.2.0",
"got": "12.1.0",
"hpagent": "0.1.2",
"ioredis": "4.28.5",
"ip-cidr": "3.0.11",
"ip-cidr": "3.0.10",
"is-svg": "4.3.2",
"js-yaml": "4.1.0",
"jsdom": "20.0.3",
"json5": "2.2.2",
"jsdom": "20.0.0",
"json5": "2.2.1",
"json5-loader": "4.0.1",
"jsonld": "8.1.0",
"jsrsasign": "10.6.1",
"mfm-js": "0.23.0",
"jsonld": "6.0.0",
"jsrsasign": "10.5.25",
"koa": "2.13.4",
"koa-bodyparser": "4.3.0",
"koa-favicon": "2.1.0",
"koa-json-body": "5.3.0",
"koa-logger": "3.2.1",
"koa-mount": "4.0.0",
"koa-send": "5.0.1",
"koa-slow": "2.1.0",
"koa-views": "7.0.2",
"mfm-js": "0.23.0-canary.1",
"mime-types": "2.1.35",
"misskey-js": "0.0.14",
"mocha": "10.0.0",
"ms": "3.0.0-canary.1",
"multer": "1.4.4",
"nested-property": "4.0.0",
"node-fetch": "3.3.0",
"nodemailer": "6.8.0",
"nsfwjs": "2.4.2",
"oauth": "^0.10.0",
"node-fetch": "3.2.8",
"nodemailer": "6.7.7",
"nsfwjs": "2.4.1",
"os-utils": "0.0.14",
"parse5": "7.1.2",
"pg": "8.8.0",
"private-ip": "3.0.0",
"parse5": "7.0.0",
"pg": "8.7.3",
"private-ip": "2.3.3",
"probe-image-size": "7.2.3",
"promise-limit": "2.7.0",
"pug": "3.0.2",
"punycode": "2.1.1",
"pureimage": "0.3.15",
"qrcode": "1.5.1",
"pureimage": "0.3.14",
"qrcode": "1.5.0",
"random-seed": "0.3.0",
"ratelimiter": "3.4.1",
"re2": "1.18.0",
"re2": "1.17.7",
"redis-lock": "0.1.4",
"reflect-metadata": "0.1.13",
"rename": "1.0.4",
"require-all": "3.0.0",
"rndstr": "1.0.0",
"rss-parser": "3.12.0",
"rxjs": "7.8.0",
"s-age": "1.1.2",
"sanitize-html": "2.8.1",
"seedrandom": "^3.0.5",
"semver": "7.3.8",
"sharp": "0.31.3",
"sanitize-html": "2.7.0",
"semver": "7.3.7",
"sharp": "0.29.3",
"speakeasy": "2.0.0",
"strict-event-emitter-types": "2.0.0",
"stringz": "2.1.0",
"style-loader": "3.3.1",
"summaly": "2.7.0",
"syslog-pro": "git+https://github.com/misskey-dev/SyslogPro#0.2.9-misskey.2",
"systeminformation": "5.16.9",
"tinycolor2": "1.5.1",
"syslog-pro": "1.0.0",
"systeminformation": "5.12.0",
"tinycolor2": "1.4.2",
"tmp": "0.2.1",
"tsc-alias": "1.8.2",
"tsconfig-paths": "4.1.1",
"ts-loader": "9.3.1",
"ts-node": "10.8.2",
"tsc-alias": "1.6.11",
"tsconfig-paths": "4.0.0",
"twemoji-parser": "14.0.0",
"typeorm": "0.3.11",
"typeorm": "0.3.7",
"ulid": "2.3.0",
"unzipper": "0.10.11",
"uuid": "9.0.0",
"vary": "1.1.2",
"uuid": "8.3.2",
"web-push": "3.5.0",
"websocket": "1.0.34",
"ws": "8.11.0",
"ws": "8.8.0",
"xev": "3.0.2"
},
"devDependencies": {
"@redocly/openapi-core": "1.0.0-beta.117",
"@swc/core": "1.3.24",
"@swc/jest": "0.2.24",
"@types/accepts": "1.3.5",
"@types/archiver": "5.3.1",
"@redocly/openapi-core": "1.0.0-beta.97",
"@types/bcryptjs": "2.4.2",
"@types/bull": "4.10.0",
"@types/bull": "3.15.8",
"@types/cbor": "6.0.0",
"@types/color-convert": "^2.0.0",
"@types/content-disposition": "^0.5.5",
"@types/escape-regexp": "0.0.1",
"@types/fluent-ffmpeg": "2.1.20",
"@types/ioredis": "4.28.10",
"@types/jest": "29.2.4",
"@types/is-url": "1.2.30",
"@types/js-yaml": "4.0.5",
"@types/jsdom": "20.0.1",
"@types/jsonld": "1.5.8",
"@types/jsrsasign": "10.5.4",
"@types/mime-types": "2.1.1",
"@types/node": "18.11.18",
"@types/jsdom": "16.2.14",
"@types/jsonld": "1.5.6",
"@types/jsrsasign": "10.5.1",
"@types/koa": "2.13.5",
"@types/koa-bodyparser": "4.3.7",
"@types/koa-cors": "0.0.2",
"@types/koa-favicon": "2.0.21",
"@types/koa-logger": "3.1.2",
"@types/koa-mount": "4.0.1",
"@types/koa-send": "4.1.3",
"@types/koa-views": "7.0.0",
"@types/koa__cors": "3.1.1",
"@types/koa__multer": "2.0.4",
"@types/koa__router": "8.0.11",
"@types/mocha": "9.1.1",
"@types/node": "18.0.3",
"@types/node-fetch": "3.0.3",
"@types/nodemailer": "6.4.7",
"@types/nodemailer": "6.4.4",
"@types/oauth": "0.9.1",
"@types/pg": "8.6.6",
"@types/pug": "2.0.6",
"@types/punycode": "2.1.0",
"@types/qrcode": "1.5.0",
"@types/qrcode": "1.4.2",
"@types/random-seed": "0.3.3",
"@types/ratelimiter": "3.4.4",
"@types/ratelimiter": "3.4.3",
"@types/redis": "4.0.11",
"@types/rename": "1.0.4",
"@types/sanitize-html": "2.8.0",
"@types/semver": "7.3.13",
"@types/sharp": "0.31.0",
"@types/sanitize-html": "2.6.2",
"@types/semver": "7.3.10",
"@types/sharp": "0.30.4",
"@types/sinonjs__fake-timers": "8.1.2",
"@types/speakeasy": "2.0.7",
"@types/syslog-pro": "^1.0.0",
"@types/tinycolor2": "1.4.3",
"@types/tmp": "0.2.3",
"@types/unzipper": "0.10.5",
"@types/uuid": "9.0.0",
"@types/vary": "1.1.0",
"@types/uuid": "8.3.4",
"@types/web-push": "3.3.2",
"@types/websocket": "1.0.5",
"@types/ws": "8.5.3",
"@typescript-eslint/eslint-plugin": "5.47.1",
"@typescript-eslint/parser": "5.47.1",
"@typescript-eslint/eslint-plugin": "5.30.6",
"@typescript-eslint/parser": "5.30.6",
"cross-env": "7.0.3",
"eslint": "8.30.0",
"eslint": "8.19.0",
"eslint-plugin-import": "2.26.0",
"execa": "6.1.0",
"jest": "29.3.1",
"jest-mock": "^29.3.1",
"typescript": "4.9.4"
"typescript": "4.7.4"
}
}

View File

@@ -1,5 +1,5 @@
declare module '@peertube/http-signature' {
import type { IncomingMessage, ClientRequest } from 'node:http';
import { IncomingMessage, ClientRequest } from 'node:http';
interface ISignature {
keyId: string;

View File

@@ -0,0 +1,15 @@
declare module 'koa-json-body' {
import { Middleware } from 'koa';
interface IKoaJsonBodyOptions {
strict: boolean;
limit: string;
fallback: boolean;
}
function koaJsonBody(opt?: IKoaJsonBodyOptions): Middleware;
namespace koaJsonBody {} // Hack
export = koaJsonBody;
}

View File

@@ -0,0 +1,14 @@
declare module 'koa-slow' {
import { Middleware } from 'koa';
interface ISlowOptions {
url?: RegExp;
delay?: number;
}
function slow(options?: ISlowOptions): Middleware;
namespace slow {} // Hack
export = slow;
}

View File

@@ -1,5 +1,5 @@
declare module 'probe-image-size' {
import type { ReadStream } from 'node:fs';
import { ReadStream } from 'node:fs';
type ProbeOptions = {
retries: 1;

View File

@@ -1,66 +0,0 @@
import { Global, Inject, Module } from '@nestjs/common';
import Redis from 'ioredis';
import { DataSource } from 'typeorm';
import { createRedisConnection } from '@/redis.js';
import { DI } from './di-symbols.js';
import { loadConfig } from './config.js';
import { createPostgreDataSource } from './postgre.js';
import { RepositoryModule } from './models/RepositoryModule.js';
import type { Provider, OnApplicationShutdown } from '@nestjs/common';
const config = loadConfig();
const $config: Provider = {
provide: DI.config,
useValue: config,
};
const $db: Provider = {
provide: DI.db,
useFactory: async (config) => {
const db = createPostgreDataSource(config);
return await db.initialize();
},
inject: [DI.config],
};
const $redis: Provider = {
provide: DI.redis,
useFactory: (config) => {
const redisClient = createRedisConnection(config);
return redisClient;
},
inject: [DI.config],
};
const $redisSubscriber: Provider = {
provide: DI.redisSubscriber,
useFactory: (config) => {
const redisSubscriber = createRedisConnection(config);
redisSubscriber.subscribe(config.host);
return redisSubscriber;
},
inject: [DI.config],
};
@Global()
@Module({
imports: [RepositoryModule],
providers: [$config, $db, $redis, $redisSubscriber],
exports: [$config, $db, $redis, $redisSubscriber, RepositoryModule],
})
export class GlobalModule implements OnApplicationShutdown {
constructor(
@Inject(DI.db) private db: DataSource,
@Inject(DI.redis) private redisClient: Redis.Redis,
@Inject(DI.redisSubscriber) private redisSubscriber: Redis.Redis,
) {}
async onApplicationShutdown(signal: string): Promise<void> {
await Promise.all([
this.db.destroy(),
this.redisClient.disconnect(),
this.redisSubscriber.disconnect(),
]);
}
}

View File

@@ -1,49 +0,0 @@
import { LoggerService } from '@nestjs/common';
import Logger from '@/logger.js';
const logger = new Logger('core', 'cyan');
const nestLogger = logger.createSubLogger('nest', 'green', false);
export class NestLogger implements LoggerService {
/**
* Write a 'log' level log.
*/
log(message: any, ...optionalParams: any[]) {
const ctx = optionalParams[0];
nestLogger.info(ctx + ': ' + message);
}
/**
* Write an 'error' level log.
*/
error(message: any, ...optionalParams: any[]) {
const ctx = optionalParams[0];
nestLogger.error(ctx + ': ' + message);
}
/**
* Write a 'warn' level log.
*/
warn(message: any, ...optionalParams: any[]) {
const ctx = optionalParams[0];
nestLogger.warn(ctx + ': ' + message);
}
/**
* Write a 'debug' level log.
*/
debug?(message: any, ...optionalParams: any[]) {
if (process.env.NODE_ENV === 'production') return;
const ctx = optionalParams[0];
nestLogger.debug(ctx + ': ' + message);
}
/**
* Write a 'verbose' level log.
*/
verbose?(message: any, ...optionalParams: any[]) {
if (process.env.NODE_ENV === 'production') return;
const ctx = optionalParams[0];
nestLogger.debug(ctx + ': ' + message);
}
}

View File

@@ -1,13 +0,0 @@
import { Module } from '@nestjs/common';
import { ServerModule } from '@/server/ServerModule.js';
import { GlobalModule } from '@/GlobalModule.js';
import { QueueProcessorModule } from '@/queue/QueueProcessorModule.js';
@Module({
imports: [
GlobalModule,
ServerModule,
QueueProcessorModule,
],
})
export class RootModule {}

View File

@@ -1,28 +1,44 @@
/**
* Misskey Entry Point!
*/
import cluster from 'node:cluster';
import { EventEmitter } from 'node:events';
import chalk from 'chalk';
import Xev from 'xev';
import Logger from '@/logger.js';
import Logger from '@/services/logger.js';
import { envOption } from '../env.js';
// for typeorm
import 'reflect-metadata';
import { masterMain } from './master.js';
import { workerMain } from './worker.js';
import 'reflect-metadata';
process.title = `Misskey (${cluster.isPrimary ? 'master' : 'worker'})`;
Error.stackTraceLimit = Infinity;
EventEmitter.defaultMaxListeners = 128;
const logger = new Logger('core', 'cyan');
const clusterLogger = logger.createSubLogger('cluster', 'orange', false);
const ev = new Xev();
/**
* Init process
*/
export default async function() {
process.title = `Misskey (${cluster.isPrimary ? 'master' : 'worker'})`;
if (cluster.isPrimary || envOption.disableClustering) {
await masterMain();
if (cluster.isPrimary) {
ev.mount();
}
}
if (cluster.isWorker || envOption.disableClustering) {
await workerMain();
}
// ユニットテスト時にMisskeyが子プロセスで起動された時のため
// それ以外のときは process.send は使えないので弾く
if (process.send) {
process.send('ok');
}
}
//#region Events
// Listen new workers
@@ -52,7 +68,6 @@ if (!envOption.quiet) {
process.on('uncaughtException', err => {
try {
logger.error(err);
console.trace(err);
} catch { }
});
@@ -62,21 +77,3 @@ process.on('exit', code => {
});
//#endregion
if (cluster.isPrimary || envOption.disableClustering) {
await masterMain();
if (cluster.isPrimary) {
ev.mount();
}
}
if (cluster.isWorker || envOption.disableClustering) {
await workerMain();
}
// ユニットテスト時にMisskeyが子プロセスで起動された時のため
// それ以外のときは process.send は使えないので弾く
if (process.send) {
process.send('ok');
}

View File

@@ -6,18 +6,14 @@ import cluster from 'node:cluster';
import chalk from 'chalk';
import chalkTemplate from 'chalk-template';
import semver from 'semver';
import { NestFactory } from '@nestjs/core';
import Logger from '@/logger.js';
import { loadConfig } from '@/config.js';
import type { Config } from '@/config.js';
import { lessThan } from '@/misc/prelude/array.js';
import { showMachineInfo } from '@/misc/show-machine-info.js';
import { DaemonModule } from '@/daemons/DaemonModule.js';
import { JanitorService } from '@/daemons/JanitorService.js';
import { QueueStatsService } from '@/daemons/QueueStatsService.js';
import { ServerStatsService } from '@/daemons/ServerStatsService.js';
import { NestLogger } from '@/NestLogger.js';
import Logger from '@/services/logger.js';
import loadConfig from '@/config/load.js';
import { Config } from '@/config/types.js';
import { lessThan } from '@/prelude/array.js';
import { envOption } from '../env.js';
import { showMachineInfo } from '@/misc/show-machine-info.js';
import { db, initDb } from '../db/postgre.js';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
@@ -64,7 +60,7 @@ export async function masterMain() {
await showMachineInfo(bootLogger);
showNodejsVersion();
config = loadConfigBoot();
//await connectDb();
await connectDb();
} catch (e) {
bootLogger.error('Fatal error occurred during initialization', null, true);
process.exit(1);
@@ -79,13 +75,9 @@ export async function masterMain() {
bootLogger.succ(`Now listening on port ${config.port} on ${config.url}`, null, true);
if (!envOption.noDaemons) {
const daemons = await NestFactory.createApplicationContext(DaemonModule, {
logger: new NestLogger(),
});
daemons.enableShutdownHooks();
daemons.get(JanitorService).start();
daemons.get(QueueStatsService).start();
daemons.get(ServerStatsService).start();
import('../daemons/server-stats.js').then(x => x.default());
import('../daemons/queue-stats.js').then(x => x.default());
import('../daemons/janitor.js').then(x => x.default());
}
}
@@ -122,7 +114,8 @@ function loadConfigBoot(): Config {
if (typeof exception === 'string') {
configLogger.error(exception);
process.exit(1);
} else if ((exception as any).code === 'ENOENT') {
}
if (exception.code === 'ENOENT') {
configLogger.error('Configuration file not found', null, true);
process.exit(1);
}
@@ -134,7 +127,6 @@ function loadConfigBoot(): Config {
return config;
}
/*
async function connectDb(): Promise<void> {
const dbLogger = bootLogger.createSubLogger('db');
@@ -144,15 +136,14 @@ async function connectDb(): Promise<void> {
await initDb();
const v = await db.query('SHOW server_version').then(x => x[0].server_version);
dbLogger.succ(`Connected: v${v}`);
} catch (err) {
} catch (e) {
dbLogger.error('Cannot connect', null, true);
dbLogger.error(err);
dbLogger.error(e);
process.exit(1);
}
}
*/
async function spawnWorkers(limit = 1) {
async function spawnWorkers(limit: number = 1) {
const workers = Math.min(limit, os.cpus().length);
bootLogger.info(`Starting ${workers} worker${workers === 1 ? '' : 's'}...`);
await Promise.all([...Array(workers)].map(spawnWorker));
@@ -164,7 +155,7 @@ function spawnWorker(): Promise<void> {
const worker = cluster.fork();
worker.on('message', message => {
if (message === 'listenFailed') {
bootLogger.error('The server Listen failed due to the previous error.');
bootLogger.error(`The server Listen failed due to the previous error.`);
process.exit(1);
}
if (message !== 'ready') return;

View File

@@ -1,32 +1,17 @@
import cluster from 'node:cluster';
import { NestFactory } from '@nestjs/core';
import { envOption } from '@/env.js';
import { ChartManagementService } from '@/core/chart/ChartManagementService.js';
import { ServerService } from '@/server/ServerService.js';
import { QueueProcessorService } from '@/queue/QueueProcessorService.js';
import { NestLogger } from '@/NestLogger.js';
import { RootModule } from '../RootModule.js';
import { initDb } from '../db/postgre.js';
/**
* Init worker process
*/
export async function workerMain() {
const app = await NestFactory.createApplicationContext(RootModule, {
logger: new NestLogger(),
});
app.enableShutdownHooks();
await initDb();
// start server
const serverService = app.get(ServerService);
serverService.launch();
await import('../server/index.js').then(x => x.default());
// start job queue
if (!envOption.onlyServer) {
const queueProcessorService = app.get(QueueProcessorService);
queueProcessorService.start();
}
app.get(ChartManagementService).run();
import('../queue/index.js').then(x => x.default());
if (cluster.isWorker) {
// Send a 'ready' message to parent process

View File

@@ -1,154 +0,0 @@
/**
* Config loader
*/
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import * as yaml from 'js-yaml';
/**
* ユーザーが設定する必要のある情報
*/
export type Source = {
repository_url?: string;
feedback_url?: string;
url: string;
port: number;
disableHsts?: boolean;
db: {
host: string;
port: number;
db: string;
user: string;
pass: string;
disableCache?: boolean;
extra?: { [x: string]: string };
};
redis: {
host: string;
port: number;
family?: number;
pass: string;
db?: number;
prefix?: string;
};
elasticsearch: {
host: string;
port: number;
ssl?: boolean;
user?: string;
pass?: string;
index?: string;
};
proxy?: string;
proxySmtp?: string;
proxyBypassHosts?: string[];
allowedPrivateNetworks?: string[];
maxFileSize?: number;
accesslog?: string;
clusterLimit?: number;
id: string;
outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual';
deliverJobConcurrency?: number;
inboxJobConcurrency?: number;
deliverJobPerSec?: number;
inboxJobPerSec?: number;
deliverJobMaxAttempts?: number;
inboxJobMaxAttempts?: number;
syslog: {
host: string;
port: number;
};
mediaProxy?: string;
proxyRemoteFiles?: boolean;
signToActivityPubGet?: boolean;
};
/**
* Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報
*/
export type Mixin = {
version: string;
host: string;
hostname: string;
scheme: string;
wsScheme: string;
apiUrl: string;
wsUrl: string;
authUrl: string;
driveUrl: string;
userAgent: string;
clientEntry: string;
clientManifestExists: boolean;
};
export type Config = Source & Mixin;
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
/**
* Path of configuration directory
*/
const dir = `${_dirname}/../../../.config`;
/**
* Path of configuration file
*/
const path = process.env.NODE_ENV === 'test'
? `${dir}/test.yml`
: `${dir}/default.yml`;
export function loadConfig() {
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../built/meta.json`, 'utf-8'));
const clientManifestExists = fs.existsSync(_dirname + '/../../../built/_vite_/manifest.json')
const clientManifest = clientManifestExists ?
JSON.parse(fs.readFileSync(`${_dirname}/../../../built/_vite_/manifest.json`, 'utf-8'))
: { 'src/init.ts': { file: 'src/init.ts' } };
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
const mixin = {} as Mixin;
const url = tryCreateUrl(config.url);
config.url = url.origin;
config.port = config.port ?? parseInt(process.env.PORT ?? '', 10);
mixin.version = meta.version;
mixin.host = url.host;
mixin.hostname = url.hostname;
mixin.scheme = url.protocol.replace(/:$/, '');
mixin.wsScheme = mixin.scheme.replace('http', 'ws');
mixin.wsUrl = `${mixin.wsScheme}://${mixin.host}`;
mixin.apiUrl = `${mixin.scheme}://${mixin.host}/api`;
mixin.authUrl = `${mixin.scheme}://${mixin.host}/auth`;
mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`;
mixin.userAgent = `Misskey/${meta.version} (${config.url})`;
mixin.clientEntry = clientManifest['src/init.ts'];
mixin.clientManifestExists = clientManifestExists;
if (!config.redis.prefix) config.redis.prefix = mixin.host;
return Object.assign(config, mixin);
}
function tryCreateUrl(url: string) {
try {
return new URL(url);
} catch (e) {
throw `url="${url}" is not a valid URL.`;
}
}

View File

@@ -0,0 +1,3 @@
import load from './load.js';
export default load();

View File

@@ -0,0 +1,62 @@
/**
* Config loader
*/
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import * as yaml from 'js-yaml';
import { Source, Mixin } from './types.js';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
/**
* Path of configuration directory
*/
const dir = `${_dirname}/../../../../.config`;
/**
* Path of configuration file
*/
const path = process.env.NODE_ENV === 'test'
? `${dir}/test.yml`
: `${dir}/default.yml`;
export default function load() {
const meta = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/meta.json`, 'utf-8'));
const clientManifest = JSON.parse(fs.readFileSync(`${_dirname}/../../../../built/_client_dist_/manifest.json`, 'utf-8'));
const config = yaml.load(fs.readFileSync(path, 'utf-8')) as Source;
const mixin = {} as Mixin;
const url = tryCreateUrl(config.url);
config.url = url.origin;
config.port = config.port || parseInt(process.env.PORT || '', 10);
mixin.version = meta.version;
mixin.host = url.host;
mixin.hostname = url.hostname;
mixin.scheme = url.protocol.replace(/:$/, '');
mixin.wsScheme = mixin.scheme.replace('http', 'ws');
mixin.wsUrl = `${mixin.wsScheme}://${mixin.host}`;
mixin.apiUrl = `${mixin.scheme}://${mixin.host}/api`;
mixin.authUrl = `${mixin.scheme}://${mixin.host}/auth`;
mixin.driveUrl = `${mixin.scheme}://${mixin.host}/files`;
mixin.userAgent = `Misskey/${meta.version} (${config.url})`;
mixin.clientEntry = clientManifest['src/init.ts'];
if (!config.redis.prefix) config.redis.prefix = mixin.host;
return Object.assign(config, mixin);
}
function tryCreateUrl(url: string) {
try {
return new URL(url);
} catch (e) {
throw `url="${url}" is not a valid URL.`;
}
}

View File

@@ -0,0 +1,87 @@
/**
* ユーザーが設定する必要のある情報
*/
export type Source = {
repository_url?: string;
feedback_url?: string;
url: string;
port: number;
disableHsts?: boolean;
db: {
host: string;
port: number;
db: string;
user: string;
pass: string;
disableCache?: boolean;
extra?: { [x: string]: string };
};
redis: {
host: string;
port: number;
family?: number;
pass: string;
db?: number;
prefix?: string;
};
elasticsearch: {
host: string;
port: number;
ssl?: boolean;
user?: string;
pass?: string;
index?: string;
};
proxy?: string;
proxySmtp?: string;
proxyBypassHosts?: string[];
allowedPrivateNetworks?: string[];
maxFileSize?: number;
accesslog?: string;
clusterLimit?: number;
id: string;
outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual';
deliverJobConcurrency?: number;
inboxJobConcurrency?: number;
deliverJobPerSec?: number;
inboxJobPerSec?: number;
deliverJobMaxAttempts?: number;
inboxJobMaxAttempts?: number;
syslog: {
host: string;
port: number;
};
mediaProxy?: string;
proxyRemoteFiles?: boolean;
signToActivityPubGet?: boolean;
};
/**
* Misskeyが自動的に(ユーザーが設定した情報から推論して)設定する情報
*/
export type Mixin = {
version: string;
host: string;
hostname: string;
scheme: string;
wsScheme: string;
apiUrl: string;
wsUrl: string;
authUrl: string;
driveUrl: string;
userAgent: string;
clientEntry: string;
};
export type Config = Source & Mixin;

View File

@@ -3,22 +3,6 @@ export const MAX_NOTE_TEXT_LENGTH = 3000;
export const USER_ONLINE_THRESHOLD = 1000 * 60 * 10; // 10min
export const USER_ACTIVE_THRESHOLD = 1000 * 60 * 60 * 24 * 3; // 3days
//#region hard limits
// If you change DB_* values, you must also change the DB schema.
/**
* Maximum note text length that can be stored in DB.
* Surrogate pairs count as one
*/
export const DB_MAX_NOTE_TEXT_LENGTH = 8192;
/**
* Maximum image description length that can be stored in DB.
* Surrogate pairs count as one
*/
export const DB_MAX_IMAGE_COMMENT_LENGTH = 512;
//#endregion
// ブラウザで直接表示することを許可するファイルの種類のリスト
// ここに含まれないものは application/octet-stream としてレスポンスされる
// SVGはXSSを生むので許可しない
@@ -28,7 +12,6 @@ export const FILE_TYPE_BROWSERSAFE = [
'image/gif',
'image/jpeg',
'image/webp',
'image/avif',
'image/apng',
'image/bmp',
'image/tiff',

View File

@@ -1,40 +0,0 @@
import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import type { UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js';
import type { User } from '@/models/entities/User.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { RelayService } from '@/core/RelayService.js';
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js';
@Injectable()
export class AccountUpdateService {
constructor(
@Inject(DI.config)
private config: Config,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
private userEntityService: UserEntityService,
private apRendererService: ApRendererService,
private apDeliverManagerService: ApDeliverManagerService,
private relayService: RelayService,
) {
}
@bindThis
public async publishToFollowers(userId: User['id']) {
const user = await this.usersRepository.findOneBy({ id: userId });
if (user == null) throw new Error('user not found');
// フォロワーがリモートユーザーかつ投稿者がローカルユーザーならUpdateを配信
if (this.userEntityService.isLocalUser(user)) {
const content = this.apRendererService.renderActivity(this.apRendererService.renderUpdate(await this.apRendererService.renderPerson(user), user));
this.apDeliverManagerService.deliverToFollowers(user, content);
this.relayService.deliverToRelays(user, content);
}
}
}

View File

@@ -1,63 +0,0 @@
import * as fs from 'node:fs';
import { fileURLToPath } from 'node:url';
import { dirname } from 'node:path';
import { Inject, Injectable } from '@nestjs/common';
import * as nsfw from 'nsfwjs';
import si from 'systeminformation';
import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
const _filename = fileURLToPath(import.meta.url);
const _dirname = dirname(_filename);
const REQUIRED_CPU_FLAGS = ['avx2', 'fma'];
let isSupportedCpu: undefined | boolean = undefined;
@Injectable()
export class AiService {
private model: nsfw.NSFWJS;
constructor(
@Inject(DI.config)
private config: Config,
) {
}
@bindThis
public async detectSensitive(path: string): Promise<nsfw.predictionType[] | null> {
try {
if (isSupportedCpu === undefined) {
const cpuFlags = await this.getCpuFlags();
isSupportedCpu = REQUIRED_CPU_FLAGS.every(required => cpuFlags.includes(required));
}
if (!isSupportedCpu) {
console.error('This CPU cannot use TensorFlow.');
return null;
}
const tf = await import('@tensorflow/tfjs-node');
if (this.model == null) this.model = await nsfw.load(`file://${_dirname}/../../nsfw-model/`, { size: 299 });
const buffer = await fs.promises.readFile(path);
const image = await tf.node.decodeImage(buffer, 3) as any;
try {
const predictions = await this.model.classify(image);
return predictions;
} finally {
image.dispose();
}
} catch (err) {
console.error(err);
return null;
}
}
@bindThis
private async getCpuFlags(): Promise<string[]> {
const str = await si.cpuFlags();
return str.split(/\s+/);
}
}

View File

@@ -1,244 +0,0 @@
import { Inject, Injectable } from '@nestjs/common';
import Redis from 'ioredis';
import type { Antenna } from '@/models/entities/Antenna.js';
import type { Note } from '@/models/entities/Note.js';
import type { User } from '@/models/entities/User.js';
import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
import { AntennaEntityService } from '@/core/entities/AntennaEntityService.js';
import { IdService } from '@/core/IdService.js';
import { isUserRelated } from '@/misc/is-user-related.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { PushNotificationService } from '@/core/PushNotificationService.js';
import * as Acct from '@/misc/acct.js';
import { Cache } from '@/misc/cache.js';
import type { Packed } from '@/misc/schema.js';
import { DI } from '@/di-symbols.js';
import type { MutingsRepository, BlockingsRepository, NotesRepository, AntennaNotesRepository, AntennasRepository, UserGroupJoiningsRepository, UserListJoiningsRepository } from '@/models/index.js';
import { UtilityService } from '@/core/UtilityService.js';
import type { OnApplicationShutdown } from '@nestjs/common';
import { bindThis } from '@/decorators.js';
@Injectable()
export class AntennaService implements OnApplicationShutdown {
private antennasFetched: boolean;
private antennas: Antenna[];
private blockingCache: Cache<User['id'][]>;
constructor(
@Inject(DI.redisSubscriber)
private redisSubscriber: Redis.Redis,
@Inject(DI.mutingsRepository)
private mutingsRepository: MutingsRepository,
@Inject(DI.blockingsRepository)
private blockingsRepository: BlockingsRepository,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
@Inject(DI.antennaNotesRepository)
private antennaNotesRepository: AntennaNotesRepository,
@Inject(DI.antennasRepository)
private antennasRepository: AntennasRepository,
@Inject(DI.userGroupJoiningsRepository)
private userGroupJoiningsRepository: UserGroupJoiningsRepository,
@Inject(DI.userListJoiningsRepository)
private userListJoiningsRepository: UserListJoiningsRepository,
private utilityService: UtilityService,
private idService: IdService,
private globalEventServie: GlobalEventService,
private pushNotificationService: PushNotificationService,
private noteEntityService: NoteEntityService,
private antennaEntityService: AntennaEntityService,
) {
this.antennasFetched = false;
this.antennas = [];
this.blockingCache = new Cache<User['id'][]>(1000 * 60 * 5);
this.redisSubscriber.on('message', this.onRedisMessage);
}
@bindThis
public onApplicationShutdown(signal?: string | undefined) {
this.redisSubscriber.off('message', this.onRedisMessage);
}
@bindThis
private async onRedisMessage(_: string, data: string): Promise<void> {
const obj = JSON.parse(data);
if (obj.channel === 'internal') {
const { type, body } = obj.message;
switch (type) {
case 'antennaCreated':
this.antennas.push(body);
break;
case 'antennaUpdated':
this.antennas[this.antennas.findIndex(a => a.id === body.id)] = body;
break;
case 'antennaDeleted':
this.antennas = this.antennas.filter(a => a.id !== body.id);
break;
default:
break;
}
}
}
@bindThis
public async addNoteToAntenna(antenna: Antenna, note: Note, noteUser: { id: User['id']; }): Promise<void> {
// 通知しない設定になっているか、自分自身の投稿なら既読にする
const read = !antenna.notify || (antenna.userId === noteUser.id);
this.antennaNotesRepository.insert({
id: this.idService.genId(),
antennaId: antenna.id,
noteId: note.id,
read: read,
});
this.globalEventServie.publishAntennaStream(antenna.id, 'note', note);
if (!read) {
const mutings = await this.mutingsRepository.find({
where: {
muterId: antenna.userId,
},
select: ['muteeId'],
});
// Copy
const _note: Note = {
...note,
};
if (note.replyId != null) {
_note.reply = await this.notesRepository.findOneByOrFail({ id: note.replyId });
}
if (note.renoteId != null) {
_note.renote = await this.notesRepository.findOneByOrFail({ id: note.renoteId });
}
if (isUserRelated(_note, new Set<string>(mutings.map(x => x.muteeId)))) {
return;
}
// 2秒経っても既読にならなかったら通知
setTimeout(async () => {
const unread = await this.antennaNotesRepository.findOneBy({ antennaId: antenna.id, read: false });
if (unread) {
this.globalEventServie.publishMainStream(antenna.userId, 'unreadAntenna', antenna);
this.pushNotificationService.pushNotification(antenna.userId, 'unreadAntennaNote', {
antenna: { id: antenna.id, name: antenna.name },
note: await this.noteEntityService.pack(note)
});
}
}, 2000);
}
}
// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている
/**
* noteUserFollowers / antennaUserFollowing はどちらか一方が指定されていればよい
*/
@bindThis
public async checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }, noteUserFollowers?: User['id'][], antennaUserFollowing?: User['id'][]): Promise<boolean> {
if (note.visibility === 'specified') return false;
// アンテナ作成者がノート作成者にブロックされていたらスキップ
const blockings = await this.blockingCache.fetch(noteUser.id, () => this.blockingsRepository.findBy({ blockerId: noteUser.id }).then(res => res.map(x => x.blockeeId)));
if (blockings.some(blocking => blocking === antenna.userId)) return false;
if (note.visibility === 'followers') {
if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
}
if (!antenna.withReplies && note.replyId != null) return false;
if (antenna.src === 'home') {
if (noteUserFollowers && !noteUserFollowers.includes(antenna.userId)) return false;
if (antennaUserFollowing && !antennaUserFollowing.includes(note.userId)) return false;
} else if (antenna.src === 'list') {
const listUsers = (await this.userListJoiningsRepository.findBy({
userListId: antenna.userListId!,
})).map(x => x.userId);
if (!listUsers.includes(note.userId)) return false;
} else if (antenna.src === 'group') {
const joining = await this.userGroupJoiningsRepository.findOneByOrFail({ id: antenna.userGroupJoiningId! });
const groupUsers = (await this.userGroupJoiningsRepository.findBy({
userGroupId: joining.userGroupId,
})).map(x => x.userId);
if (!groupUsers.includes(note.userId)) return false;
} else if (antenna.src === 'users') {
const accts = antenna.users.map(x => {
const { username, host } = Acct.parse(x);
return this.utilityService.getFullApAccount(username, host).toLowerCase();
});
if (!accts.includes(this.utilityService.getFullApAccount(noteUser.username, noteUser.host).toLowerCase())) return false;
}
const keywords = antenna.keywords
// Clean up
.map(xs => xs.filter(x => x !== ''))
.filter(xs => xs.length > 0);
if (keywords.length > 0) {
if (note.text == null) return false;
const matched = keywords.some(and =>
and.every(keyword =>
antenna.caseSensitive
? note.text!.includes(keyword)
: note.text!.toLowerCase().includes(keyword.toLowerCase()),
));
if (!matched) return false;
}
const excludeKeywords = antenna.excludeKeywords
// Clean up
.map(xs => xs.filter(x => x !== ''))
.filter(xs => xs.length > 0);
if (excludeKeywords.length > 0) {
if (note.text == null) return false;
const matched = excludeKeywords.some(and =>
and.every(keyword =>
antenna.caseSensitive
? note.text!.includes(keyword)
: note.text!.toLowerCase().includes(keyword.toLowerCase()),
));
if (matched) return false;
}
if (antenna.withFile) {
if (note.fileIds && note.fileIds.length === 0) return false;
}
// TODO: eval expression
return true;
}
@bindThis
public async getAntennas() {
if (!this.antennasFetched) {
this.antennas = await this.antennasRepository.find();
this.antennasFetched = true;
}
return this.antennas;
}
}

View File

@@ -1,44 +0,0 @@
import { promisify } from 'node:util';
import { Inject, Injectable } from '@nestjs/common';
import redisLock from 'redis-lock';
import Redis from 'ioredis';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
/**
* Retry delay (ms) for lock acquisition
*/
const retryDelay = 100;
@Injectable()
export class AppLockService {
private lock: (key: string, timeout?: number) => Promise<() => void>;
constructor(
@Inject(DI.redis)
private redisClient: Redis.Redis,
) {
this.lock = promisify(redisLock(this.redisClient, retryDelay));
}
/**
* Get AP Object lock
* @param uri AP object ID
* @param timeout Lock timeout (ms), The timeout releases previous lock.
* @returns Unlock function
*/
@bindThis
public getApLock(uri: string, timeout = 30 * 1000): Promise<() => void> {
return this.lock(`ap-object:${uri}`, timeout);
}
@bindThis
public getFetchInstanceMetadataLock(host: string, timeout = 30 * 1000): Promise<() => void> {
return this.lock(`instance:${host}`, timeout);
}
@bindThis
public getChartInsertLock(lockKey: string, timeout = 30 * 1000): Promise<() => void> {
return this.lock(`chart-insert:${lockKey}`, timeout);
}
}

View File

@@ -1,98 +0,0 @@
import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js';
import type { UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { bindThis } from '@/decorators.js';
type CaptchaResponse = {
success: boolean;
'error-codes'?: string[];
};
@Injectable()
export class CaptchaService {
constructor(
@Inject(DI.config)
private config: Config,
private httpRequestService: HttpRequestService,
) {
}
@bindThis
private async getCaptchaResponse(url: string, secret: string, response: string): Promise<CaptchaResponse> {
const params = new URLSearchParams({
secret,
response,
});
const res = await fetch(url, {
method: 'POST',
body: params,
headers: {
'User-Agent': this.config.userAgent,
},
// TODO
//timeout: 10 * 1000,
agent: (url, bypassProxy) => this.httpRequestService.getAgentByUrl(url, bypassProxy),
}).catch(err => {
throw `${err.message ?? err}`;
});
if (!res.ok) {
throw `${res.status}`;
}
return await res.json() as CaptchaResponse;
}
@bindThis
public async verifyRecaptcha(secret: string, response: string | null | undefined): Promise<void> {
if (response == null) {
throw 'recaptcha-failed: no response provided';
}
const result = await this.getCaptchaResponse('https://www.recaptcha.net/recaptcha/api/siteverify', secret, response).catch(err => {
throw `recaptcha-request-failed: ${err}`;
});
if (result.success !== true) {
const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : '';
throw `recaptcha-failed: ${errorCodes}`;
}
}
@bindThis
public async verifyHcaptcha(secret: string, response: string | null | undefined): Promise<void> {
if (response == null) {
throw 'hcaptcha-failed: no response provided';
}
const result = await this.getCaptchaResponse('https://hcaptcha.com/siteverify', secret, response).catch(err => {
throw `hcaptcha-request-failed: ${err}`;
});
if (result.success !== true) {
const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : '';
throw `hcaptcha-failed: ${errorCodes}`;
}
}
@bindThis
public async verifyTurnstile(secret: string, response: string | null | undefined): Promise<void> {
if (response == null) {
throw 'turnstile-failed: no response provided';
}
const result = await this.getCaptchaResponse('https://challenges.cloudflare.com/turnstile/v0/siteverify', secret, response).catch(err => {
throw `turnstile-request-failed: ${err}`;
});
if (result.success !== true) {
const errorCodes = result['error-codes'] ? result['error-codes'].join(', ') : '';
throw `turnstile-failed: ${errorCodes}`;
}
}
}

View File

@@ -1,712 +0,0 @@
import { Module } from '@nestjs/common';
import { DI } from '../di-symbols.js';
import { AccountUpdateService } from './AccountUpdateService.js';
import { AiService } from './AiService.js';
import { AntennaService } from './AntennaService.js';
import { AppLockService } from './AppLockService.js';
import { CaptchaService } from './CaptchaService.js';
import { CreateNotificationService } from './CreateNotificationService.js';
import { CreateSystemUserService } from './CreateSystemUserService.js';
import { CustomEmojiService } from './CustomEmojiService.js';
import { DeleteAccountService } from './DeleteAccountService.js';
import { DownloadService } from './DownloadService.js';
import { DriveService } from './DriveService.js';
import { EmailService } from './EmailService.js';
import { FederatedInstanceService } from './FederatedInstanceService.js';
import { FetchInstanceMetadataService } from './FetchInstanceMetadataService.js';
import { GlobalEventService } from './GlobalEventService.js';
import { HashtagService } from './HashtagService.js';
import { HttpRequestService } from './HttpRequestService.js';
import { IdService } from './IdService.js';
import { ImageProcessingService } from './ImageProcessingService.js';
import { InstanceActorService } from './InstanceActorService.js';
import { InternalStorageService } from './InternalStorageService.js';
import { MessagingService } from './MessagingService.js';
import { MetaService } from './MetaService.js';
import { MfmService } from './MfmService.js';
import { ModerationLogService } from './ModerationLogService.js';
import { NoteCreateService } from './NoteCreateService.js';
import { NoteDeleteService } from './NoteDeleteService.js';
import { NotePiningService } from './NotePiningService.js';
import { NoteReadService } from './NoteReadService.js';
import { NotificationService } from './NotificationService.js';
import { PollService } from './PollService.js';
import { PushNotificationService } from './PushNotificationService.js';
import { QueryService } from './QueryService.js';
import { ReactionService } from './ReactionService.js';
import { RelayService } from './RelayService.js';
import { S3Service } from './S3Service.js';
import { SignupService } from './SignupService.js';
import { TwoFactorAuthenticationService } from './TwoFactorAuthenticationService.js';
import { UserBlockingService } from './UserBlockingService.js';
import { UserCacheService } from './UserCacheService.js';
import { UserFollowingService } from './UserFollowingService.js';
import { UserKeypairStoreService } from './UserKeypairStoreService.js';
import { UserListService } from './UserListService.js';
import { UserMutingService } from './UserMutingService.js';
import { UserSuspendService } from './UserSuspendService.js';
import { VideoProcessingService } from './VideoProcessingService.js';
import { WebhookService } from './WebhookService.js';
import { ProxyAccountService } from './ProxyAccountService.js';
import { UtilityService } from './UtilityService.js';
import { FileInfoService } from './FileInfoService.js';
import { ChartLoggerService } from './chart/ChartLoggerService.js';
import FederationChart from './chart/charts/federation.js';
import NotesChart from './chart/charts/notes.js';
import UsersChart from './chart/charts/users.js';
import ActiveUsersChart from './chart/charts/active-users.js';
import InstanceChart from './chart/charts/instance.js';
import PerUserNotesChart from './chart/charts/per-user-notes.js';
import PerUserPvChart from './chart/charts/per-user-pv.js';
import DriveChart from './chart/charts/drive.js';
import PerUserReactionsChart from './chart/charts/per-user-reactions.js';
import HashtagChart from './chart/charts/hashtag.js';
import PerUserFollowingChart from './chart/charts/per-user-following.js';
import PerUserDriveChart from './chart/charts/per-user-drive.js';
import ApRequestChart from './chart/charts/ap-request.js';
import { ChartManagementService } from './chart/ChartManagementService.js';
import { AbuseUserReportEntityService } from './entities/AbuseUserReportEntityService.js';
import { AntennaEntityService } from './entities/AntennaEntityService.js';
import { AppEntityService } from './entities/AppEntityService.js';
import { AuthSessionEntityService } from './entities/AuthSessionEntityService.js';
import { BlockingEntityService } from './entities/BlockingEntityService.js';
import { ChannelEntityService } from './entities/ChannelEntityService.js';
import { ClipEntityService } from './entities/ClipEntityService.js';
import { DriveFileEntityService } from './entities/DriveFileEntityService.js';
import { DriveFolderEntityService } from './entities/DriveFolderEntityService.js';
import { EmojiEntityService } from './entities/EmojiEntityService.js';
import { FollowingEntityService } from './entities/FollowingEntityService.js';
import { FollowRequestEntityService } from './entities/FollowRequestEntityService.js';
import { GalleryLikeEntityService } from './entities/GalleryLikeEntityService.js';
import { GalleryPostEntityService } from './entities/GalleryPostEntityService.js';
import { HashtagEntityService } from './entities/HashtagEntityService.js';
import { InstanceEntityService } from './entities/InstanceEntityService.js';
import { MessagingMessageEntityService } from './entities/MessagingMessageEntityService.js';
import { ModerationLogEntityService } from './entities/ModerationLogEntityService.js';
import { MutingEntityService } from './entities/MutingEntityService.js';
import { NoteEntityService } from './entities/NoteEntityService.js';
import { NoteFavoriteEntityService } from './entities/NoteFavoriteEntityService.js';
import { NoteReactionEntityService } from './entities/NoteReactionEntityService.js';
import { NotificationEntityService } from './entities/NotificationEntityService.js';
import { PageEntityService } from './entities/PageEntityService.js';
import { PageLikeEntityService } from './entities/PageLikeEntityService.js';
import { SigninEntityService } from './entities/SigninEntityService.js';
import { UserEntityService } from './entities/UserEntityService.js';
import { UserGroupEntityService } from './entities/UserGroupEntityService.js';
import { UserGroupInvitationEntityService } from './entities/UserGroupInvitationEntityService.js';
import { UserListEntityService } from './entities/UserListEntityService.js';
import { ApAudienceService } from './activitypub/ApAudienceService.js';
import { ApDbResolverService } from './activitypub/ApDbResolverService.js';
import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js';
import { ApInboxService } from './activitypub/ApInboxService.js';
import { ApLoggerService } from './activitypub/ApLoggerService.js';
import { ApMfmService } from './activitypub/ApMfmService.js';
import { ApRendererService } from './activitypub/ApRendererService.js';
import { ApRequestService } from './activitypub/ApRequestService.js';
import { ApResolverService } from './activitypub/ApResolverService.js';
import { LdSignatureService } from './activitypub/LdSignatureService.js';
import { RemoteLoggerService } from './RemoteLoggerService.js';
import { RemoteUserResolveService } from './RemoteUserResolveService.js';
import { WebfingerService } from './WebfingerService.js';
import { ApImageService } from './activitypub/models/ApImageService.js';
import { ApMentionService } from './activitypub/models/ApMentionService.js';
import { ApNoteService } from './activitypub/models/ApNoteService.js';
import { ApPersonService } from './activitypub/models/ApPersonService.js';
import { ApQuestionService } from './activitypub/models/ApQuestionService.js';
import { QueueModule } from './QueueModule.js';
import { QueueService } from './QueueService.js';
import { LoggerService } from './LoggerService.js';
import type { Provider } from '@nestjs/common';
//#region 文字列ベースでのinjection用(循環参照対応のため)
const $LoggerService: Provider = { provide: 'LoggerService', useExisting: LoggerService };
const $AccountUpdateService: Provider = { provide: 'AccountUpdateService', useExisting: AccountUpdateService };
const $AiService: Provider = { provide: 'AiService', useExisting: AiService };
const $AntennaService: Provider = { provide: 'AntennaService', useExisting: AntennaService };
const $AppLockService: Provider = { provide: 'AppLockService', useExisting: AppLockService };
const $CaptchaService: Provider = { provide: 'CaptchaService', useExisting: CaptchaService };
const $CreateNotificationService: Provider = { provide: 'CreateNotificationService', useExisting: CreateNotificationService };
const $CreateSystemUserService: Provider = { provide: 'CreateSystemUserService', useExisting: CreateSystemUserService };
const $CustomEmojiService: Provider = { provide: 'CustomEmojiService', useExisting: CustomEmojiService };
const $DeleteAccountService: Provider = { provide: 'DeleteAccountService', useExisting: DeleteAccountService };
const $DownloadService: Provider = { provide: 'DownloadService', useExisting: DownloadService };
const $DriveService: Provider = { provide: 'DriveService', useExisting: DriveService };
const $EmailService: Provider = { provide: 'EmailService', useExisting: EmailService };
const $FederatedInstanceService: Provider = { provide: 'FederatedInstanceService', useExisting: FederatedInstanceService };
const $FetchInstanceMetadataService: Provider = { provide: 'FetchInstanceMetadataService', useExisting: FetchInstanceMetadataService };
const $GlobalEventService: Provider = { provide: 'GlobalEventService', useExisting: GlobalEventService };
const $HashtagService: Provider = { provide: 'HashtagService', useExisting: HashtagService };
const $HttpRequestService: Provider = { provide: 'HttpRequestService', useExisting: HttpRequestService };
const $IdService: Provider = { provide: 'IdService', useExisting: IdService };
const $ImageProcessingService: Provider = { provide: 'ImageProcessingService', useExisting: ImageProcessingService };
const $InstanceActorService: Provider = { provide: 'InstanceActorService', useExisting: InstanceActorService };
const $InternalStorageService: Provider = { provide: 'InternalStorageService', useExisting: InternalStorageService };
const $MessagingService: Provider = { provide: 'MessagingService', useExisting: MessagingService };
const $MetaService: Provider = { provide: 'MetaService', useExisting: MetaService };
const $MfmService: Provider = { provide: 'MfmService', useExisting: MfmService };
const $ModerationLogService: Provider = { provide: 'ModerationLogService', useExisting: ModerationLogService };
const $NoteCreateService: Provider = { provide: 'NoteCreateService', useExisting: NoteCreateService };
const $NoteDeleteService: Provider = { provide: 'NoteDeleteService', useExisting: NoteDeleteService };
const $NotePiningService: Provider = { provide: 'NotePiningService', useExisting: NotePiningService };
const $NoteReadService: Provider = { provide: 'NoteReadService', useExisting: NoteReadService };
const $NotificationService: Provider = { provide: 'NotificationService', useExisting: NotificationService };
const $PollService: Provider = { provide: 'PollService', useExisting: PollService };
const $ProxyAccountService: Provider = { provide: 'ProxyAccountService', useExisting: ProxyAccountService };
const $PushNotificationService: Provider = { provide: 'PushNotificationService', useExisting: PushNotificationService };
const $QueryService: Provider = { provide: 'QueryService', useExisting: QueryService };
const $ReactionService: Provider = { provide: 'ReactionService', useExisting: ReactionService };
const $RelayService: Provider = { provide: 'RelayService', useExisting: RelayService };
const $S3Service: Provider = { provide: 'S3Service', useExisting: S3Service };
const $SignupService: Provider = { provide: 'SignupService', useExisting: SignupService };
const $TwoFactorAuthenticationService: Provider = { provide: 'TwoFactorAuthenticationService', useExisting: TwoFactorAuthenticationService };
const $UserBlockingService: Provider = { provide: 'UserBlockingService', useExisting: UserBlockingService };
const $UserCacheService: Provider = { provide: 'UserCacheService', useExisting: UserCacheService };
const $UserFollowingService: Provider = { provide: 'UserFollowingService', useExisting: UserFollowingService };
const $UserKeypairStoreService: Provider = { provide: 'UserKeypairStoreService', useExisting: UserKeypairStoreService };
const $UserListService: Provider = { provide: 'UserListService', useExisting: UserListService };
const $UserMutingService: Provider = { provide: 'UserMutingService', useExisting: UserMutingService };
const $UserSuspendService: Provider = { provide: 'UserSuspendService', useExisting: UserSuspendService };
const $VideoProcessingService: Provider = { provide: 'VideoProcessingService', useExisting: VideoProcessingService };
const $WebhookService: Provider = { provide: 'WebhookService', useExisting: WebhookService };
const $UtilityService: Provider = { provide: 'UtilityService', useExisting: UtilityService };
const $FileInfoService: Provider = { provide: 'FileInfoService', useExisting: FileInfoService };
const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService };
const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart };
const $NotesChart: Provider = { provide: 'NotesChart', useExisting: NotesChart };
const $UsersChart: Provider = { provide: 'UsersChart', useExisting: UsersChart };
const $ActiveUsersChart: Provider = { provide: 'ActiveUsersChart', useExisting: ActiveUsersChart };
const $InstanceChart: Provider = { provide: 'InstanceChart', useExisting: InstanceChart };
const $PerUserNotesChart: Provider = { provide: 'PerUserNotesChart', useExisting: PerUserNotesChart };
const $PerUserPvChart: Provider = { provide: 'PerUserPvChart', useExisting: PerUserPvChart };
const $DriveChart: Provider = { provide: 'DriveChart', useExisting: DriveChart };
const $PerUserReactionsChart: Provider = { provide: 'PerUserReactionsChart', useExisting: PerUserReactionsChart };
const $HashtagChart: Provider = { provide: 'HashtagChart', useExisting: HashtagChart };
const $PerUserFollowingChart: Provider = { provide: 'PerUserFollowingChart', useExisting: PerUserFollowingChart };
const $PerUserDriveChart: Provider = { provide: 'PerUserDriveChart', useExisting: PerUserDriveChart };
const $ApRequestChart: Provider = { provide: 'ApRequestChart', useExisting: ApRequestChart };
const $ChartManagementService: Provider = { provide: 'ChartManagementService', useExisting: ChartManagementService };
const $AbuseUserReportEntityService: Provider = { provide: 'AbuseUserReportEntityService', useExisting: AbuseUserReportEntityService };
const $AntennaEntityService: Provider = { provide: 'AntennaEntityService', useExisting: AntennaEntityService };
const $AppEntityService: Provider = { provide: 'AppEntityService', useExisting: AppEntityService };
const $AuthSessionEntityService: Provider = { provide: 'AuthSessionEntityService', useExisting: AuthSessionEntityService };
const $BlockingEntityService: Provider = { provide: 'BlockingEntityService', useExisting: BlockingEntityService };
const $ChannelEntityService: Provider = { provide: 'ChannelEntityService', useExisting: ChannelEntityService };
const $ClipEntityService: Provider = { provide: 'ClipEntityService', useExisting: ClipEntityService };
const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useExisting: DriveFileEntityService };
const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useExisting: DriveFolderEntityService };
const $EmojiEntityService: Provider = { provide: 'EmojiEntityService', useExisting: EmojiEntityService };
const $FollowingEntityService: Provider = { provide: 'FollowingEntityService', useExisting: FollowingEntityService };
const $FollowRequestEntityService: Provider = { provide: 'FollowRequestEntityService', useExisting: FollowRequestEntityService };
const $GalleryLikeEntityService: Provider = { provide: 'GalleryLikeEntityService', useExisting: GalleryLikeEntityService };
const $GalleryPostEntityService: Provider = { provide: 'GalleryPostEntityService', useExisting: GalleryPostEntityService };
const $HashtagEntityService: Provider = { provide: 'HashtagEntityService', useExisting: HashtagEntityService };
const $InstanceEntityService: Provider = { provide: 'InstanceEntityService', useExisting: InstanceEntityService };
const $MessagingMessageEntityService: Provider = { provide: 'MessagingMessageEntityService', useExisting: MessagingMessageEntityService };
const $ModerationLogEntityService: Provider = { provide: 'ModerationLogEntityService', useExisting: ModerationLogEntityService };
const $MutingEntityService: Provider = { provide: 'MutingEntityService', useExisting: MutingEntityService };
const $NoteEntityService: Provider = { provide: 'NoteEntityService', useExisting: NoteEntityService };
const $NoteFavoriteEntityService: Provider = { provide: 'NoteFavoriteEntityService', useExisting: NoteFavoriteEntityService };
const $NoteReactionEntityService: Provider = { provide: 'NoteReactionEntityService', useExisting: NoteReactionEntityService };
const $NotificationEntityService: Provider = { provide: 'NotificationEntityService', useExisting: NotificationEntityService };
const $PageEntityService: Provider = { provide: 'PageEntityService', useExisting: PageEntityService };
const $PageLikeEntityService: Provider = { provide: 'PageLikeEntityService', useExisting: PageLikeEntityService };
const $SigninEntityService: Provider = { provide: 'SigninEntityService', useExisting: SigninEntityService };
const $UserEntityService: Provider = { provide: 'UserEntityService', useExisting: UserEntityService };
const $UserGroupEntityService: Provider = { provide: 'UserGroupEntityService', useExisting: UserGroupEntityService };
const $UserGroupInvitationEntityService: Provider = { provide: 'UserGroupInvitationEntityService', useExisting: UserGroupInvitationEntityService };
const $UserListEntityService: Provider = { provide: 'UserListEntityService', useExisting: UserListEntityService };
const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService };
const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService };
const $ApDeliverManagerService: Provider = { provide: 'ApDeliverManagerService', useExisting: ApDeliverManagerService };
const $ApInboxService: Provider = { provide: 'ApInboxService', useExisting: ApInboxService };
const $ApLoggerService: Provider = { provide: 'ApLoggerService', useExisting: ApLoggerService };
const $ApMfmService: Provider = { provide: 'ApMfmService', useExisting: ApMfmService };
const $ApRendererService: Provider = { provide: 'ApRendererService', useExisting: ApRendererService };
const $ApRequestService: Provider = { provide: 'ApRequestService', useExisting: ApRequestService };
const $ApResolverService: Provider = { provide: 'ApResolverService', useExisting: ApResolverService };
const $LdSignatureService: Provider = { provide: 'LdSignatureService', useExisting: LdSignatureService };
const $RemoteLoggerService: Provider = { provide: 'RemoteLoggerService', useExisting: RemoteLoggerService };
const $RemoteUserResolveService: Provider = { provide: 'RemoteUserResolveService', useExisting: RemoteUserResolveService };
const $WebfingerService: Provider = { provide: 'WebfingerService', useExisting: WebfingerService };
const $ApImageService: Provider = { provide: 'ApImageService', useExisting: ApImageService };
const $ApMentionService: Provider = { provide: 'ApMentionService', useExisting: ApMentionService };
const $ApNoteService: Provider = { provide: 'ApNoteService', useExisting: ApNoteService };
const $ApPersonService: Provider = { provide: 'ApPersonService', useExisting: ApPersonService };
const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting: ApQuestionService };
//#endregion
@Module({
imports: [
QueueModule,
],
providers: [
LoggerService,
AccountUpdateService,
AiService,
AntennaService,
AppLockService,
CaptchaService,
CreateNotificationService,
CreateSystemUserService,
CustomEmojiService,
DeleteAccountService,
DownloadService,
DriveService,
EmailService,
FederatedInstanceService,
FetchInstanceMetadataService,
GlobalEventService,
HashtagService,
HttpRequestService,
IdService,
ImageProcessingService,
InstanceActorService,
InternalStorageService,
MessagingService,
MetaService,
MfmService,
ModerationLogService,
NoteCreateService,
NoteDeleteService,
NotePiningService,
NoteReadService,
NotificationService,
PollService,
ProxyAccountService,
PushNotificationService,
QueryService,
ReactionService,
RelayService,
S3Service,
SignupService,
TwoFactorAuthenticationService,
UserBlockingService,
UserCacheService,
UserFollowingService,
UserKeypairStoreService,
UserListService,
UserMutingService,
UserSuspendService,
VideoProcessingService,
WebhookService,
UtilityService,
FileInfoService,
ChartLoggerService,
FederationChart,
NotesChart,
UsersChart,
ActiveUsersChart,
InstanceChart,
PerUserNotesChart,
PerUserPvChart,
DriveChart,
PerUserReactionsChart,
HashtagChart,
PerUserFollowingChart,
PerUserDriveChart,
ApRequestChart,
ChartManagementService,
AbuseUserReportEntityService,
AntennaEntityService,
AppEntityService,
AuthSessionEntityService,
BlockingEntityService,
ChannelEntityService,
ClipEntityService,
DriveFileEntityService,
DriveFolderEntityService,
EmojiEntityService,
FollowingEntityService,
FollowRequestEntityService,
GalleryLikeEntityService,
GalleryPostEntityService,
HashtagEntityService,
InstanceEntityService,
MessagingMessageEntityService,
ModerationLogEntityService,
MutingEntityService,
NoteEntityService,
NoteFavoriteEntityService,
NoteReactionEntityService,
NotificationEntityService,
PageEntityService,
PageLikeEntityService,
SigninEntityService,
UserEntityService,
UserGroupEntityService,
UserGroupInvitationEntityService,
UserListEntityService,
ApAudienceService,
ApDbResolverService,
ApDeliverManagerService,
ApInboxService,
ApLoggerService,
ApMfmService,
ApRendererService,
ApRequestService,
ApResolverService,
LdSignatureService,
RemoteLoggerService,
RemoteUserResolveService,
WebfingerService,
ApImageService,
ApMentionService,
ApNoteService,
ApPersonService,
ApQuestionService,
QueueService,
//#region 文字列ベースでのinjection用(循環参照対応のため)
$LoggerService,
$AccountUpdateService,
$AiService,
$AntennaService,
$AppLockService,
$CaptchaService,
$CreateNotificationService,
$CreateSystemUserService,
$CustomEmojiService,
$DeleteAccountService,
$DownloadService,
$DriveService,
$EmailService,
$FederatedInstanceService,
$FetchInstanceMetadataService,
$GlobalEventService,
$HashtagService,
$HttpRequestService,
$IdService,
$ImageProcessingService,
$InstanceActorService,
$InternalStorageService,
$MessagingService,
$MetaService,
$MfmService,
$ModerationLogService,
$NoteCreateService,
$NoteDeleteService,
$NotePiningService,
$NoteReadService,
$NotificationService,
$PollService,
$ProxyAccountService,
$PushNotificationService,
$QueryService,
$ReactionService,
$RelayService,
$S3Service,
$SignupService,
$TwoFactorAuthenticationService,
$UserBlockingService,
$UserCacheService,
$UserFollowingService,
$UserKeypairStoreService,
$UserListService,
$UserMutingService,
$UserSuspendService,
$VideoProcessingService,
$WebhookService,
$UtilityService,
$FileInfoService,
$ChartLoggerService,
$FederationChart,
$NotesChart,
$UsersChart,
$ActiveUsersChart,
$InstanceChart,
$PerUserNotesChart,
$PerUserPvChart,
$DriveChart,
$PerUserReactionsChart,
$HashtagChart,
$PerUserFollowingChart,
$PerUserDriveChart,
$ApRequestChart,
$ChartManagementService,
$AbuseUserReportEntityService,
$AntennaEntityService,
$AppEntityService,
$AuthSessionEntityService,
$BlockingEntityService,
$ChannelEntityService,
$ClipEntityService,
$DriveFileEntityService,
$DriveFolderEntityService,
$EmojiEntityService,
$FollowingEntityService,
$FollowRequestEntityService,
$GalleryLikeEntityService,
$GalleryPostEntityService,
$HashtagEntityService,
$InstanceEntityService,
$MessagingMessageEntityService,
$ModerationLogEntityService,
$MutingEntityService,
$NoteEntityService,
$NoteFavoriteEntityService,
$NoteReactionEntityService,
$NotificationEntityService,
$PageEntityService,
$PageLikeEntityService,
$SigninEntityService,
$UserEntityService,
$UserGroupEntityService,
$UserGroupInvitationEntityService,
$UserListEntityService,
$ApAudienceService,
$ApDbResolverService,
$ApDeliverManagerService,
$ApInboxService,
$ApLoggerService,
$ApMfmService,
$ApRendererService,
$ApRequestService,
$ApResolverService,
$LdSignatureService,
$RemoteLoggerService,
$RemoteUserResolveService,
$WebfingerService,
$ApImageService,
$ApMentionService,
$ApNoteService,
$ApPersonService,
$ApQuestionService,
//#endregion
],
exports: [
QueueModule,
LoggerService,
AccountUpdateService,
AiService,
AntennaService,
AppLockService,
CaptchaService,
CreateNotificationService,
CreateSystemUserService,
CustomEmojiService,
DeleteAccountService,
DownloadService,
DriveService,
EmailService,
FederatedInstanceService,
FetchInstanceMetadataService,
GlobalEventService,
HashtagService,
HttpRequestService,
IdService,
ImageProcessingService,
InstanceActorService,
InternalStorageService,
MessagingService,
MetaService,
MfmService,
ModerationLogService,
NoteCreateService,
NoteDeleteService,
NotePiningService,
NoteReadService,
NotificationService,
PollService,
ProxyAccountService,
PushNotificationService,
QueryService,
ReactionService,
RelayService,
S3Service,
SignupService,
TwoFactorAuthenticationService,
UserBlockingService,
UserCacheService,
UserFollowingService,
UserKeypairStoreService,
UserListService,
UserMutingService,
UserSuspendService,
VideoProcessingService,
WebhookService,
UtilityService,
FileInfoService,
FederationChart,
NotesChart,
UsersChart,
ActiveUsersChart,
InstanceChart,
PerUserNotesChart,
PerUserPvChart,
DriveChart,
PerUserReactionsChart,
HashtagChart,
PerUserFollowingChart,
PerUserDriveChart,
ApRequestChart,
ChartManagementService,
AbuseUserReportEntityService,
AntennaEntityService,
AppEntityService,
AuthSessionEntityService,
BlockingEntityService,
ChannelEntityService,
ClipEntityService,
DriveFileEntityService,
DriveFolderEntityService,
EmojiEntityService,
FollowingEntityService,
FollowRequestEntityService,
GalleryLikeEntityService,
GalleryPostEntityService,
HashtagEntityService,
InstanceEntityService,
MessagingMessageEntityService,
ModerationLogEntityService,
MutingEntityService,
NoteEntityService,
NoteFavoriteEntityService,
NoteReactionEntityService,
NotificationEntityService,
PageEntityService,
PageLikeEntityService,
SigninEntityService,
UserEntityService,
UserGroupEntityService,
UserGroupInvitationEntityService,
UserListEntityService,
ApAudienceService,
ApDbResolverService,
ApDeliverManagerService,
ApInboxService,
ApLoggerService,
ApMfmService,
ApRendererService,
ApRequestService,
ApResolverService,
LdSignatureService,
RemoteLoggerService,
RemoteUserResolveService,
WebfingerService,
ApImageService,
ApMentionService,
ApNoteService,
ApPersonService,
ApQuestionService,
QueueService,
//#region 文字列ベースでのinjection用(循環参照対応のため)
$LoggerService,
$AccountUpdateService,
$AiService,
$AntennaService,
$AppLockService,
$CaptchaService,
$CreateNotificationService,
$CreateSystemUserService,
$CustomEmojiService,
$DeleteAccountService,
$DownloadService,
$DriveService,
$EmailService,
$FederatedInstanceService,
$FetchInstanceMetadataService,
$GlobalEventService,
$HashtagService,
$HttpRequestService,
$IdService,
$ImageProcessingService,
$InstanceActorService,
$InternalStorageService,
$MessagingService,
$MetaService,
$MfmService,
$ModerationLogService,
$NoteCreateService,
$NoteDeleteService,
$NotePiningService,
$NoteReadService,
$NotificationService,
$PollService,
$ProxyAccountService,
$PushNotificationService,
$QueryService,
$ReactionService,
$RelayService,
$S3Service,
$SignupService,
$TwoFactorAuthenticationService,
$UserBlockingService,
$UserCacheService,
$UserFollowingService,
$UserKeypairStoreService,
$UserListService,
$UserMutingService,
$UserSuspendService,
$VideoProcessingService,
$WebhookService,
$UtilityService,
$FileInfoService,
$FederationChart,
$NotesChart,
$UsersChart,
$ActiveUsersChart,
$InstanceChart,
$PerUserNotesChart,
$PerUserPvChart,
$DriveChart,
$PerUserReactionsChart,
$HashtagChart,
$PerUserFollowingChart,
$PerUserDriveChart,
$ApRequestChart,
$ChartManagementService,
$AbuseUserReportEntityService,
$AntennaEntityService,
$AppEntityService,
$AuthSessionEntityService,
$BlockingEntityService,
$ChannelEntityService,
$ClipEntityService,
$DriveFileEntityService,
$DriveFolderEntityService,
$EmojiEntityService,
$FollowingEntityService,
$FollowRequestEntityService,
$GalleryLikeEntityService,
$GalleryPostEntityService,
$HashtagEntityService,
$InstanceEntityService,
$MessagingMessageEntityService,
$ModerationLogEntityService,
$MutingEntityService,
$NoteEntityService,
$NoteFavoriteEntityService,
$NoteReactionEntityService,
$NotificationEntityService,
$PageEntityService,
$PageLikeEntityService,
$SigninEntityService,
$UserEntityService,
$UserGroupEntityService,
$UserGroupInvitationEntityService,
$UserListEntityService,
$ApAudienceService,
$ApDbResolverService,
$ApDeliverManagerService,
$ApInboxService,
$ApLoggerService,
$ApMfmService,
$ApRendererService,
$ApRequestService,
$ApResolverService,
$LdSignatureService,
$RemoteLoggerService,
$RemoteUserResolveService,
$WebfingerService,
$ApImageService,
$ApMentionService,
$ApNoteService,
$ApPersonService,
$ApQuestionService,
//#endregion
],
})
export class CoreModule {}

View File

@@ -1,118 +0,0 @@
import { Inject, Injectable } from '@nestjs/common';
import type { MutingsRepository, NotificationsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js';
import type { Notification } from '@/models/entities/Notification.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { IdService } from '@/core/IdService.js';
import { DI } from '@/di-symbols.js';
import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js';
import { PushNotificationService } from '@/core/PushNotificationService.js';
import { bindThis } from '@/decorators.js';
@Injectable()
export class CreateNotificationService {
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
@Inject(DI.notificationsRepository)
private notificationsRepository: NotificationsRepository,
@Inject(DI.mutingsRepository)
private mutingsRepository: MutingsRepository,
private notificationEntityService: NotificationEntityService,
private idService: IdService,
private globalEventServie: GlobalEventService,
private pushNotificationService: PushNotificationService,
) {
}
@bindThis
public async createNotification(
notifieeId: User['id'],
type: Notification['type'],
data: Partial<Notification>,
): Promise<Notification | null> {
if (data.notifierId && (notifieeId === data.notifierId)) {
return null;
}
const profile = await this.userProfilesRepository.findOneBy({ userId: notifieeId });
const isMuted = profile?.mutingNotificationTypes.includes(type);
// Create notification
const notification = await this.notificationsRepository.insert({
id: this.idService.genId(),
createdAt: new Date(),
notifieeId: notifieeId,
type: type,
// 相手がこの通知をミュートしているようなら、既読を予めつけておく
isRead: isMuted,
...data,
} as Partial<Notification>)
.then(x => this.notificationsRepository.findOneByOrFail(x.identifiers[0]));
const packed = await this.notificationEntityService.pack(notification, {});
// Publish notification event
this.globalEventServie.publishMainStream(notifieeId, 'notification', packed);
// 2秒経っても(今回作成した)通知が既読にならなかったら「未読の通知がありますよ」イベントを発行する
setTimeout(async () => {
const fresh = await this.notificationsRepository.findOneBy({ id: notification.id });
if (fresh == null) return; // 既に削除されているかもしれない
if (fresh.isRead) return;
//#region ただしミュートしているユーザーからの通知なら無視
const mutings = await this.mutingsRepository.findBy({
muterId: notifieeId,
});
if (data.notifierId && mutings.map(m => m.muteeId).includes(data.notifierId)) {
return;
}
//#endregion
this.globalEventServie.publishMainStream(notifieeId, 'unreadNotification', packed);
this.pushNotificationService.pushNotification(notifieeId, 'notification', packed);
if (type === 'follow') this.emailNotificationFollow(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! }));
if (type === 'receiveFollowRequest') this.emailNotificationReceiveFollowRequest(notifieeId, await this.usersRepository.findOneByOrFail({ id: data.notifierId! }));
}, 2000);
return notification;
}
// TODO
//const locales = await import('../../../../locales/index.js');
// TODO: locale ファイルをクライアント用とサーバー用で分けたい
@bindThis
private async emailNotificationFollow(userId: User['id'], follower: User) {
/*
const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return;
const locale = locales[userProfile.lang ?? 'ja-JP'];
const i18n = new I18n(locale);
// TODO: render user information html
sendEmail(userProfile.email, i18n.t('_email._follow.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`);
*/
}
@bindThis
private async emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) {
/*
const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return;
const locale = locales[userProfile.lang ?? 'ja-JP'];
const i18n = new I18n(locale);
// TODO: render user information html
sendEmail(userProfile.email, i18n.t('_email._receiveFollowRequest.title'), `${follower.name} (@${Acct.toString(follower)})`, `${follower.name} (@${Acct.toString(follower)})`);
*/
}
}

View File

@@ -1,82 +0,0 @@
import { Inject, Injectable } from '@nestjs/common';
import bcrypt from 'bcryptjs';
import { v4 as uuid } from 'uuid';
import { IsNull, DataSource } from 'typeorm';
import { genRsaKeyPair } from '@/misc/gen-key-pair.js';
import { User } from '@/models/entities/User.js';
import { UserProfile } from '@/models/entities/UserProfile.js';
import { IdService } from '@/core/IdService.js';
import { UserKeypair } from '@/models/entities/UserKeypair.js';
import { UsedUsername } from '@/models/entities/UsedUsername.js';
import { DI } from '@/di-symbols.js';
import generateNativeUserToken from '@/misc/generate-native-user-token.js';
import { bindThis } from '@/decorators.js';
@Injectable()
export class CreateSystemUserService {
constructor(
@Inject(DI.db)
private db: DataSource,
private idService: IdService,
) {
}
@bindThis
public async createSystemUser(username: string): Promise<User> {
const password = uuid();
// Generate hash of password
const salt = await bcrypt.genSalt(8);
const hash = await bcrypt.hash(password, salt);
// Generate secret
const secret = generateNativeUserToken();
const keyPair = await genRsaKeyPair(4096);
let account!: User;
// Start transaction
await this.db.transaction(async transactionalEntityManager => {
const exist = await transactionalEntityManager.findOneBy(User, {
usernameLower: username.toLowerCase(),
host: IsNull(),
});
if (exist) throw new Error('the user is already exists');
account = await transactionalEntityManager.insert(User, {
id: this.idService.genId(),
createdAt: new Date(),
username: username,
usernameLower: username.toLowerCase(),
host: null,
token: secret,
isAdmin: false,
isLocked: true,
isExplorable: false,
isBot: true,
}).then(x => transactionalEntityManager.findOneByOrFail(User, x.identifiers[0]));
await transactionalEntityManager.insert(UserKeypair, {
publicKey: keyPair.publicKey,
privateKey: keyPair.privateKey,
userId: account.id,
});
await transactionalEntityManager.insert(UserProfile, {
userId: account.id,
autoAcceptFollowed: false,
password: hash,
});
await transactionalEntityManager.insert(UsedUsername, {
createdAt: new Date(),
username: username.toLowerCase(),
});
});
return account;
}
}

View File

@@ -1,47 +0,0 @@
import { Inject, Injectable } from '@nestjs/common';
import { DataSource, In, IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js';
import { IdService } from '@/core/IdService.js';
import type { DriveFile } from '@/models/entities/DriveFile.js';
import type { Emoji } from '@/models/entities/Emoji.js';
import type { EmojisRepository } from '@/models/index.js';
import { bindThis } from '@/decorators.js';
@Injectable()
export class CustomEmojiService {
constructor(
@Inject(DI.db)
private db: DataSource,
@Inject(DI.emojisRepository)
private emojisRepository: EmojisRepository,
private idService: IdService,
) {
}
@bindThis
public async add(data: {
driveFile: DriveFile;
name: string;
category: string | null;
aliases: string[];
host: string | null;
}): Promise<Emoji> {
const emoji = await this.emojisRepository.insert({
id: this.idService.genId(),
updatedAt: new Date(),
name: data.name,
category: data.category,
host: data.host,
aliases: data.aliases,
originalUrl: data.driveFile.url,
publicUrl: data.driveFile.webpublicUrl ?? data.driveFile.url,
type: data.driveFile.webpublicType ?? data.driveFile.type,
}).then(x => this.emojisRepository.findOneByOrFail(x.identifiers[0]));
await this.db.queryResultCache!.remove(['meta_emojis']);
return emoji;
}
}

View File

@@ -1,40 +0,0 @@
import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository } from '@/models/index.js';
import { QueueService } from '@/core/QueueService.js';
import { UserSuspendService } from '@/core/UserSuspendService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js';
@Injectable()
export class DeleteAccountService {
constructor(
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
private userSuspendService: UserSuspendService,
private queueService: QueueService,
private globalEventServie: GlobalEventService,
) {
}
@bindThis
public async deleteAccount(user: {
id: string;
host: string | null;
}): Promise<void> {
// 物理削除する前にDelete activityを送信する
await this.userSuspendService.doPostSuspend(user).catch(e => {});
this.queueService.createDeleteAccountJob(user, {
soft: false,
});
await this.usersRepository.update(user.id, {
isDeleted: true,
});
// Terminate streaming
this.globalEventServie.publishUserEvent(user.id, 'terminate', {});
}
}

View File

@@ -1,129 +0,0 @@
import * as fs from 'node:fs';
import * as stream from 'node:stream';
import * as util from 'node:util';
import { Inject, Injectable } from '@nestjs/common';
import IPCIDR from 'ip-cidr';
import PrivateIp from 'private-ip';
import got, * as Got from 'got';
import chalk from 'chalk';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import { HttpRequestService } from '@/core/HttpRequestService.js';
import { createTemp } from '@/misc/create-temp.js';
import { StatusError } from '@/misc/status-error.js';
import { LoggerService } from '@/core/LoggerService.js';
import type Logger from '@/logger.js';
const pipeline = util.promisify(stream.pipeline);
import { bindThis } from '@/decorators.js';
@Injectable()
export class DownloadService {
private logger: Logger;
constructor(
@Inject(DI.config)
private config: Config,
private httpRequestService: HttpRequestService,
private loggerService: LoggerService,
) {
this.logger = this.loggerService.getLogger('download');
}
@bindThis
public async downloadUrl(url: string, path: string): Promise<void> {
this.logger.info(`Downloading ${chalk.cyan(url)} to ${chalk.cyanBright(path)} ...`);
const timeout = 30 * 1000;
const operationTimeout = 60 * 1000;
const maxSize = this.config.maxFileSize ?? 262144000;
const req = got.stream(url, {
headers: {
'User-Agent': this.config.userAgent,
},
timeout: {
lookup: timeout,
connect: timeout,
secureConnect: timeout,
socket: timeout, // read timeout
response: timeout,
send: timeout,
request: operationTimeout, // whole operation timeout
},
agent: {
http: this.httpRequestService.httpAgent,
https: this.httpRequestService.httpsAgent,
},
http2: false, // default
retry: {
limit: 0,
},
}).on('response', (res: Got.Response) => {
if ((process.env.NODE_ENV === 'production' || process.env.NODE_ENV === 'test') && !this.config.proxy && res.ip) {
if (this.isPrivateIp(res.ip)) {
this.logger.warn(`Blocked address: ${res.ip}`);
req.destroy();
}
}
const contentLength = res.headers['content-length'];
if (contentLength != null) {
const size = Number(contentLength);
if (size > maxSize) {
this.logger.warn(`maxSize exceeded (${size} > ${maxSize}) on response`);
req.destroy();
}
}
}).on('downloadProgress', (progress: Got.Progress) => {
if (progress.transferred > maxSize) {
this.logger.warn(`maxSize exceeded (${progress.transferred} > ${maxSize}) on downloadProgress`);
req.destroy();
}
});
try {
await pipeline(req, fs.createWriteStream(path));
} catch (e) {
if (e instanceof Got.HTTPError) {
throw new StatusError(`${e.response.statusCode} ${e.response.statusMessage}`, e.response.statusCode, e.response.statusMessage);
} else {
throw e;
}
}
this.logger.succ(`Download finished: ${chalk.cyan(url)}`);
}
@bindThis
public async downloadTextFile(url: string): Promise<string> {
// Create temp file
const [path, cleanup] = await createTemp();
this.logger.info(`text file: Temp file is ${path}`);
try {
// write content at URL to temp file
await this.downloadUrl(url, path);
const text = await util.promisify(fs.readFile)(path, 'utf8');
return text;
} finally {
cleanup();
}
}
@bindThis
private isPrivateIp(ip: string): boolean {
for (const net of this.config.allowedPrivateNetworks ?? []) {
const cidr = new IPCIDR(net);
if (cidr.contains(ip)) {
return false;
}
}
return PrivateIp(ip);
}
}

View File

@@ -1,752 +0,0 @@
import * as fs from 'node:fs';
import { Inject, Injectable } from '@nestjs/common';
import { v4 as uuid } from 'uuid';
import sharp from 'sharp';
import { IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js';
import type { Config } from '@/config.js';
import Logger from '@/logger.js';
import type { IRemoteUser, User } from '@/models/entities/User.js';
import { MetaService } from '@/core/MetaService.js';
import { DriveFile } from '@/models/entities/DriveFile.js';
import { IdService } from '@/core/IdService.js';
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
import { IdentifiableError } from '@/misc/identifiable-error.js';
import { contentDisposition } from '@/misc/content-disposition.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { VideoProcessingService } from '@/core/VideoProcessingService.js';
import { ImageProcessingService } from '@/core/ImageProcessingService.js';
import type { IImage } from '@/core/ImageProcessingService.js';
import { QueueService } from '@/core/QueueService.js';
import type { DriveFolder } from '@/models/entities/DriveFolder.js';
import { createTemp } from '@/misc/create-temp.js';
import DriveChart from '@/core/chart/charts/drive.js';
import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js';
import InstanceChart from '@/core/chart/charts/instance.js';
import { DownloadService } from '@/core/DownloadService.js';
import { S3Service } from '@/core/S3Service.js';
import { InternalStorageService } from '@/core/InternalStorageService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { FileInfoService } from '@/core/FileInfoService.js';
import { bindThis } from '@/decorators.js';
import type S3 from 'aws-sdk/clients/s3.js';
type AddFileArgs = {
/** User who wish to add file */
user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null;
/** File path */
path: string;
/** Name */
name?: string | null;
/** Comment */
comment?: string | null;
/** Folder ID */
folderId?: any;
/** If set to true, forcibly upload the file even if there is a file with the same hash. */
force?: boolean;
/** Do not save file to local */
isLink?: boolean;
/** URL of source (URLからアップロードされた場合(ローカル/リモート)の元URL) */
url?: string | null;
/** URL of source (リモートインスタンスのURLからアップロードされた場合の元URL) */
uri?: string | null;
/** Mark file as sensitive */
sensitive?: boolean | null;
requestIp?: string | null;
requestHeaders?: Record<string, string> | null;
};
type UploadFromUrlArgs = {
url: string;
user: { id: User['id']; host: User['host']; driveCapacityOverrideMb: User['driveCapacityOverrideMb'] } | null;
folderId?: DriveFolder['id'] | null;
uri?: string | null;
sensitive?: boolean;
force?: boolean;
isLink?: boolean;
comment?: string | null;
requestIp?: string | null;
requestHeaders?: Record<string, string> | null;
};
@Injectable()
export class DriveService {
private registerLogger: Logger;
private downloaderLogger: Logger;
constructor(
@Inject(DI.config)
private config: Config,
@Inject(DI.usersRepository)
private usersRepository: UsersRepository,
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
@Inject(DI.driveFilesRepository)
private driveFilesRepository: DriveFilesRepository,
@Inject(DI.driveFoldersRepository)
private driveFoldersRepository: DriveFoldersRepository,
private fileInfoService: FileInfoService,
private userEntityService: UserEntityService,
private driveFileEntityService: DriveFileEntityService,
private idService: IdService,
private metaService: MetaService,
private downloadService: DownloadService,
private internalStorageService: InternalStorageService,
private s3Service: S3Service,
private imageProcessingService: ImageProcessingService,
private videoProcessingService: VideoProcessingService,
private globalEventService: GlobalEventService,
private queueService: QueueService,
private driveChart: DriveChart,
private perUserDriveChart: PerUserDriveChart,
private instanceChart: InstanceChart,
) {
const logger = new Logger('drive', 'blue');
this.registerLogger = logger.createSubLogger('register', 'yellow');
this.downloaderLogger = logger.createSubLogger('downloader');
}
/***
* Save file
* @param path Path for original
* @param name Name for original
* @param type Content-Type for original
* @param hash Hash for original
* @param size Size for original
*/
@bindThis
private async save(file: DriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<DriveFile> {
// thunbnail, webpublic を必要なら生成
const alts = await this.generateAlts(path, type, !file.uri);
const meta = await this.metaService.fetch();
if (meta.useObjectStorage) {
//#region ObjectStorage params
let [ext] = (name.match(/\.([a-zA-Z0-9_-]+)$/) ?? ['']);
if (ext === '') {
if (type === 'image/jpeg') ext = '.jpg';
if (type === 'image/png') ext = '.png';
if (type === 'image/webp') ext = '.webp';
if (type === 'image/avif') ext = '.avif';
if (type === 'image/apng') ext = '.apng';
if (type === 'image/vnd.mozilla.apng') ext = '.apng';
}
// 拡張子からContent-Typeを設定してそうな挙動を示すオブジェクトストレージ (upcloud?) も存在するので、
// 許可されているファイル形式でしか拡張子をつけない
if (!FILE_TYPE_BROWSERSAFE.includes(type)) {
ext = '';
}
const baseUrl = meta.objectStorageBaseUrl
?? `${ meta.objectStorageUseSSL ? 'https' : 'http' }://${ meta.objectStorageEndpoint }${ meta.objectStoragePort ? `:${meta.objectStoragePort}` : '' }/${ meta.objectStorageBucket }`;
// for original
const key = `${meta.objectStoragePrefix}/${uuid()}${ext}`;
const url = `${ baseUrl }/${ key }`;
// for alts
let webpublicKey: string | null = null;
let webpublicUrl: string | null = null;
let thumbnailKey: string | null = null;
let thumbnailUrl: string | null = null;
//#endregion
//#region Uploads
this.registerLogger.info(`uploading original: ${key}`);
const uploads = [
this.upload(key, fs.createReadStream(path), type, name),
];
if (alts.webpublic) {
webpublicKey = `${meta.objectStoragePrefix}/webpublic-${uuid()}.${alts.webpublic.ext}`;
webpublicUrl = `${ baseUrl }/${ webpublicKey }`;
this.registerLogger.info(`uploading webpublic: ${webpublicKey}`);
uploads.push(this.upload(webpublicKey, alts.webpublic.data, alts.webpublic.type, name));
}
if (alts.thumbnail) {
thumbnailKey = `${meta.objectStoragePrefix}/thumbnail-${uuid()}.${alts.thumbnail.ext}`;
thumbnailUrl = `${ baseUrl }/${ thumbnailKey }`;
this.registerLogger.info(`uploading thumbnail: ${thumbnailKey}`);
uploads.push(this.upload(thumbnailKey, alts.thumbnail.data, alts.thumbnail.type));
}
await Promise.all(uploads);
//#endregion
file.url = url;
file.thumbnailUrl = thumbnailUrl;
file.webpublicUrl = webpublicUrl;
file.accessKey = key;
file.thumbnailAccessKey = thumbnailKey;
file.webpublicAccessKey = webpublicKey;
file.webpublicType = alts.webpublic?.type ?? null;
file.name = name;
file.type = type;
file.md5 = hash;
file.size = size;
file.storedInternal = false;
return await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0]));
} else { // use internal storage
const accessKey = uuid();
const thumbnailAccessKey = 'thumbnail-' + uuid();
const webpublicAccessKey = 'webpublic-' + uuid();
const url = this.internalStorageService.saveFromPath(accessKey, path);
let thumbnailUrl: string | null = null;
let webpublicUrl: string | null = null;
if (alts.thumbnail) {
thumbnailUrl = this.internalStorageService.saveFromBuffer(thumbnailAccessKey, alts.thumbnail.data);
this.registerLogger.info(`thumbnail stored: ${thumbnailAccessKey}`);
}
if (alts.webpublic) {
webpublicUrl = this.internalStorageService.saveFromBuffer(webpublicAccessKey, alts.webpublic.data);
this.registerLogger.info(`web stored: ${webpublicAccessKey}`);
}
file.storedInternal = true;
file.url = url;
file.thumbnailUrl = thumbnailUrl;
file.webpublicUrl = webpublicUrl;
file.accessKey = accessKey;
file.thumbnailAccessKey = thumbnailAccessKey;
file.webpublicAccessKey = webpublicAccessKey;
file.webpublicType = alts.webpublic?.type ?? null;
file.name = name;
file.type = type;
file.md5 = hash;
file.size = size;
return await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0]));
}
}
/**
* Generate webpublic, thumbnail, etc
* @param path Path for original
* @param type Content-Type for original
* @param generateWeb Generate webpublic or not
*/
@bindThis
public async generateAlts(path: string, type: string, generateWeb: boolean) {
if (type.startsWith('video/')) {
try {
const thumbnail = await this.videoProcessingService.generateVideoThumbnail(path);
return {
webpublic: null,
thumbnail,
};
} catch (err) {
this.registerLogger.warn(`GenerateVideoThumbnail failed: ${err}`);
return {
webpublic: null,
thumbnail: null,
};
}
}
if (!['image/jpeg', 'image/png', 'image/webp', 'image/avif', 'image/svg+xml'].includes(type)) {
this.registerLogger.debug('web image and thumbnail not created (not an required file)');
return {
webpublic: null,
thumbnail: null,
};
}
let img: sharp.Sharp | null = null;
let satisfyWebpublic: boolean;
try {
img = sharp(path);
const metadata = await img.metadata();
const isAnimated = metadata.pages && metadata.pages > 1;
// skip animated
if (isAnimated) {
return {
webpublic: null,
thumbnail: null,
};
}
satisfyWebpublic = !!(
type !== 'image/svg+xml' && type !== 'image/webp' && type !== 'image/avif' &&
!(metadata.exif ?? metadata.iptc ?? metadata.xmp ?? metadata.tifftagPhotoshop) &&
metadata.width && metadata.width <= 2048 &&
metadata.height && metadata.height <= 2048
);
} catch (err) {
this.registerLogger.warn(`sharp failed: ${err}`);
return {
webpublic: null,
thumbnail: null,
};
}
// #region webpublic
let webpublic: IImage | null = null;
if (generateWeb && !satisfyWebpublic) {
this.registerLogger.info('creating web image');
try {
if (['image/jpeg', 'image/webp', 'image/avif'].includes(type)) {
webpublic = await this.imageProcessingService.convertSharpToJpeg(img, 2048, 2048);
} else if (['image/png'].includes(type)) {
webpublic = await this.imageProcessingService.convertSharpToPng(img, 2048, 2048);
} else if (['image/svg+xml'].includes(type)) {
webpublic = await this.imageProcessingService.convertSharpToPng(img, 2048, 2048);
} else {
this.registerLogger.debug('web image not created (not an required image)');
}
} catch (err) {
this.registerLogger.warn('web image not created (an error occured)', err as Error);
}
} else {
if (satisfyWebpublic) this.registerLogger.info('web image not created (original satisfies webpublic)');
else this.registerLogger.info('web image not created (from remote)');
}
// #endregion webpublic
// #region thumbnail
let thumbnail: IImage | null = null;
try {
if (['image/jpeg', 'image/webp', 'image/avif', 'image/png', 'image/svg+xml'].includes(type)) {
thumbnail = await this.imageProcessingService.convertSharpToWebp(img, 498, 280);
} else {
this.registerLogger.debug('thumbnail not created (not an required file)');
}
} catch (err) {
this.registerLogger.warn('thumbnail not created (an error occured)', err as Error);
}
// #endregion thumbnail
return {
webpublic,
thumbnail,
};
}
/**
* Upload to ObjectStorage
*/
@bindThis
private async upload(key: string, stream: fs.ReadStream | Buffer, type: string, filename?: string) {
if (type === 'image/apng') type = 'image/png';
if (!FILE_TYPE_BROWSERSAFE.includes(type)) type = 'application/octet-stream';
const meta = await this.metaService.fetch();
const params = {
Bucket: meta.objectStorageBucket,
Key: key,
Body: stream,
ContentType: type,
CacheControl: 'max-age=31536000, immutable',
} as S3.PutObjectRequest;
if (filename) params.ContentDisposition = contentDisposition('inline', filename);
if (meta.objectStorageSetPublicRead) params.ACL = 'public-read';
const s3 = this.s3Service.getS3(meta);
const upload = s3.upload(params, {
partSize: s3.endpoint.hostname === 'storage.googleapis.com' ? 500 * 1024 * 1024 : 8 * 1024 * 1024,
});
const result = await upload.promise();
if (result) this.registerLogger.debug(`Uploaded: ${result.Bucket}/${result.Key} => ${result.Location}`);
}
@bindThis
private async deleteOldFile(user: IRemoteUser) {
const q = this.driveFilesRepository.createQueryBuilder('file')
.where('file.userId = :userId', { userId: user.id })
.andWhere('file.isLink = FALSE');
if (user.avatarId) {
q.andWhere('file.id != :avatarId', { avatarId: user.avatarId });
}
if (user.bannerId) {
q.andWhere('file.id != :bannerId', { bannerId: user.bannerId });
}
q.orderBy('file.id', 'ASC');
const oldFile = await q.getOne();
if (oldFile) {
this.deleteFile(oldFile, true);
}
}
/**
* Add file to drive
*
*/
@bindThis
public async addFile({
user,
path,
name = null,
comment = null,
folderId = null,
force = false,
isLink = false,
url = null,
uri = null,
sensitive = null,
requestIp = null,
requestHeaders = null,
}: AddFileArgs): Promise<DriveFile> {
let skipNsfwCheck = false;
const instance = await this.metaService.fetch();
if (user == null) skipNsfwCheck = true;
if (instance.sensitiveMediaDetection === 'none') skipNsfwCheck = true;
if (user && instance.sensitiveMediaDetection === 'local' && this.userEntityService.isRemoteUser(user)) skipNsfwCheck = true;
if (user && instance.sensitiveMediaDetection === 'remote' && this.userEntityService.isLocalUser(user)) skipNsfwCheck = true;
const info = await this.fileInfoService.getFileInfo(path, {
skipSensitiveDetection: skipNsfwCheck,
sensitiveThreshold: // 感度が高いほどしきい値は低くすることになる
instance.sensitiveMediaDetectionSensitivity === 'veryHigh' ? 0.1 :
instance.sensitiveMediaDetectionSensitivity === 'high' ? 0.3 :
instance.sensitiveMediaDetectionSensitivity === 'low' ? 0.7 :
instance.sensitiveMediaDetectionSensitivity === 'veryLow' ? 0.9 :
0.5,
sensitiveThresholdForPorn: 0.75,
enableSensitiveMediaDetectionForVideos: instance.enableSensitiveMediaDetectionForVideos,
});
this.registerLogger.info(`${JSON.stringify(info)}`);
// 現状 false positive が多すぎて実用に耐えない
//if (info.porn && instance.disallowUploadWhenPredictedAsPorn) {
// throw new IdentifiableError('282f77bf-5816-4f72-9264-aa14d8261a21', 'Detected as porn.');
//}
// detect name
const detectedName = name ?? (info.type.ext ? `untitled.${info.type.ext}` : 'untitled');
if (user && !force) {
// Check if there is a file with the same hash
const much = await this.driveFilesRepository.findOneBy({
md5: info.md5,
userId: user.id,
});
if (much) {
this.registerLogger.info(`file with same hash is found: ${much.id}`);
return much;
}
}
//#region Check drive usage
if (user && !isLink) {
const usage = await this.driveFileEntityService.calcDriveUsageOf(user);
const u = await this.usersRepository.findOneBy({ id: user.id });
const instance = await this.metaService.fetch();
let driveCapacity = 1024 * 1024 * (this.userEntityService.isLocalUser(user) ? instance.localDriveCapacityMb : instance.remoteDriveCapacityMb);
if (this.userEntityService.isLocalUser(user) && u?.driveCapacityOverrideMb != null) {
driveCapacity = 1024 * 1024 * u.driveCapacityOverrideMb;
this.registerLogger.debug('drive capacity override applied');
this.registerLogger.debug(`overrideCap: ${driveCapacity}bytes, usage: ${usage}bytes, u+s: ${usage + info.size}bytes`);
}
this.registerLogger.debug(`drive usage is ${usage} (max: ${driveCapacity})`);
// If usage limit exceeded
if (usage + info.size > driveCapacity) {
if (this.userEntityService.isLocalUser(user)) {
throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.');
} else {
// (アバターまたはバナーを含まず)最も古いファイルを削除する
this.deleteOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as IRemoteUser);
}
}
}
//#endregion
const fetchFolder = async () => {
if (!folderId) {
return null;
}
const driveFolder = await this.driveFoldersRepository.findOneBy({
id: folderId,
userId: user ? user.id : IsNull(),
});
if (driveFolder == null) throw new Error('folder-not-found');
return driveFolder;
};
const properties: {
width?: number;
height?: number;
orientation?: number;
} = {};
if (info.width) {
properties['width'] = info.width;
properties['height'] = info.height;
}
if (info.orientation != null) {
properties['orientation'] = info.orientation;
}
const profile = user ? await this.userProfilesRepository.findOneBy({ userId: user.id }) : null;
const folder = await fetchFolder();
let file = new DriveFile();
file.id = this.idService.genId();
file.createdAt = new Date();
file.userId = user ? user.id : null;
file.userHost = user ? user.host : null;
file.folderId = folder !== null ? folder.id : null;
file.comment = comment;
file.properties = properties;
file.blurhash = info.blurhash ?? null;
file.isLink = isLink;
file.requestIp = requestIp;
file.requestHeaders = requestHeaders;
file.maybeSensitive = info.sensitive;
file.maybePorn = info.porn;
file.isSensitive = user
? this.userEntityService.isLocalUser(user) && profile!.alwaysMarkNsfw ? true :
(sensitive !== null && sensitive !== undefined)
? sensitive
: false
: false;
if (info.sensitive && profile!.autoSensitive) file.isSensitive = true;
if (info.sensitive && instance.setSensitiveFlagAutomatically) file.isSensitive = true;
if (url !== null) {
file.src = url;
if (isLink) {
file.url = url;
// ローカルプロキシ用
file.accessKey = uuid();
file.thumbnailAccessKey = 'thumbnail-' + uuid();
file.webpublicAccessKey = 'webpublic-' + uuid();
}
}
if (uri !== null) {
file.uri = uri;
}
if (isLink) {
try {
file.size = 0;
file.md5 = info.md5;
file.name = detectedName;
file.type = info.type.mime;
file.storedInternal = false;
file = await this.driveFilesRepository.insert(file).then(x => this.driveFilesRepository.findOneByOrFail(x.identifiers[0]));
} catch (err) {
// duplicate key error (when already registered)
if (isDuplicateKeyValueError(err)) {
this.registerLogger.info(`already registered ${file.uri}`);
file = await this.driveFilesRepository.findOneBy({
uri: file.uri!,
userId: user ? user.id : IsNull(),
}) as DriveFile;
} else {
this.registerLogger.error(err as Error);
throw err;
}
}
} else {
file = await (this.save(file, path, detectedName, info.type.mime, info.md5, info.size));
}
this.registerLogger.succ(`drive file has been created ${file.id}`);
if (user) {
this.driveFileEntityService.pack(file, { self: true }).then(packedFile => {
// Publish driveFileCreated event
this.globalEventService.publishMainStream(user.id, 'driveFileCreated', packedFile);
this.globalEventService.publishDriveStream(user.id, 'fileCreated', packedFile);
});
}
// 統計を更新
this.driveChart.update(file, true);
this.perUserDriveChart.update(file, true);
if (file.userHost !== null) {
this.instanceChart.updateDrive(file, true);
}
return file;
}
@bindThis
public async deleteFile(file: DriveFile, isExpired = false) {
if (file.storedInternal) {
this.internalStorageService.del(file.accessKey!);
if (file.thumbnailUrl) {
this.internalStorageService.del(file.thumbnailAccessKey!);
}
if (file.webpublicUrl) {
this.internalStorageService.del(file.webpublicAccessKey!);
}
} else if (!file.isLink) {
this.queueService.createDeleteObjectStorageFileJob(file.accessKey!);
if (file.thumbnailUrl) {
this.queueService.createDeleteObjectStorageFileJob(file.thumbnailAccessKey!);
}
if (file.webpublicUrl) {
this.queueService.createDeleteObjectStorageFileJob(file.webpublicAccessKey!);
}
}
this.deletePostProcess(file, isExpired);
}
@bindThis
public async deleteFileSync(file: DriveFile, isExpired = false) {
if (file.storedInternal) {
this.internalStorageService.del(file.accessKey!);
if (file.thumbnailUrl) {
this.internalStorageService.del(file.thumbnailAccessKey!);
}
if (file.webpublicUrl) {
this.internalStorageService.del(file.webpublicAccessKey!);
}
} else if (!file.isLink) {
const promises = [];
promises.push(this.deleteObjectStorageFile(file.accessKey!));
if (file.thumbnailUrl) {
promises.push(this.deleteObjectStorageFile(file.thumbnailAccessKey!));
}
if (file.webpublicUrl) {
promises.push(this.deleteObjectStorageFile(file.webpublicAccessKey!));
}
await Promise.all(promises);
}
this.deletePostProcess(file, isExpired);
}
@bindThis
private async deletePostProcess(file: DriveFile, isExpired = false) {
// リモートファイル期限切れ削除後は直リンクにする
if (isExpired && file.userHost !== null && file.uri != null) {
this.driveFilesRepository.update(file.id, {
isLink: true,
url: file.uri,
thumbnailUrl: null,
webpublicUrl: null,
storedInternal: false,
// ローカルプロキシ用
accessKey: uuid(),
thumbnailAccessKey: 'thumbnail-' + uuid(),
webpublicAccessKey: 'webpublic-' + uuid(),
});
} else {
this.driveFilesRepository.delete(file.id);
}
// 統計を更新
this.driveChart.update(file, false);
this.perUserDriveChart.update(file, false);
if (file.userHost !== null) {
this.instanceChart.updateDrive(file, false);
}
}
@bindThis
public async deleteObjectStorageFile(key: string) {
const meta = await this.metaService.fetch();
const s3 = this.s3Service.getS3(meta);
await s3.deleteObject({
Bucket: meta.objectStorageBucket!,
Key: key,
}).promise();
}
@bindThis
public async uploadFromUrl({
url,
user,
folderId = null,
uri = null,
sensitive = false,
force = false,
isLink = false,
comment = null,
requestIp = null,
requestHeaders = null,
}: UploadFromUrlArgs): Promise<DriveFile> {
let name = new URL(url).pathname.split('/').pop() ?? null;
if (name == null || !this.driveFileEntityService.validateFileName(name)) {
name = null;
}
// If the comment is same as the name, skip comment
// (image.name is passed in when receiving attachment)
if (comment !== null && name === comment) {
comment = null;
}
// Create temp file
const [path, cleanup] = await createTemp();
try {
// write content at URL to temp file
await this.downloadService.downloadUrl(url, path);
const driveFile = await this.addFile({ user, path, name, comment, folderId, force, isLink, url, uri, sensitive, requestIp, requestHeaders });
this.downloaderLogger.succ(`Got: ${driveFile.id}`);
return driveFile!;
} catch (err) {
this.downloaderLogger.error(`Failed to create drive file: ${err}`, {
url: url,
e: err,
});
throw err;
} finally {
cleanup();
}
}
}

View File

@@ -1,180 +0,0 @@
import * as nodemailer from 'nodemailer';
import { Inject, Injectable } from '@nestjs/common';
import { validate as validateEmail } from 'deep-email-validator';
import { MetaService } from '@/core/MetaService.js';
import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js';
import type Logger from '@/logger.js';
import type { UserProfilesRepository } from '@/models/index.js';
import { LoggerService } from '@/core/LoggerService.js';
import { bindThis } from '@/decorators.js';
@Injectable()
export class EmailService {
private logger: Logger;
constructor(
@Inject(DI.config)
private config: Config,
@Inject(DI.userProfilesRepository)
private userProfilesRepository: UserProfilesRepository,
private metaService: MetaService,
private loggerService: LoggerService,
) {
this.logger = this.loggerService.getLogger('email');
}
@bindThis
public async sendEmail(to: string, subject: string, html: string, text: string) {
const meta = await this.metaService.fetch(true);
const iconUrl = `${this.config.url}/static-assets/mi-white.png`;
const emailSettingUrl = `${this.config.url}/settings/email`;
const enableAuth = meta.smtpUser != null && meta.smtpUser !== '';
const transporter = nodemailer.createTransport({
host: meta.smtpHost,
port: meta.smtpPort,
secure: meta.smtpSecure,
ignoreTLS: !enableAuth,
proxy: this.config.proxySmtp,
auth: enableAuth ? {
user: meta.smtpUser,
pass: meta.smtpPass,
} : undefined,
} as any);
try {
// TODO: htmlサニタイズ
const info = await transporter.sendMail({
from: meta.email!,
to: to,
subject: subject,
text: text,
html: `<!doctype html>
<html>
<head>
<meta charset="utf-8">
<title>${ subject }</title>
<style>
html {
background: #eee;
}
body {
padding: 16px;
margin: 0;
font-family: sans-serif;
font-size: 14px;
}
a {
text-decoration: none;
color: #86b300;
}
a:hover {
text-decoration: underline;
}
main {
max-width: 500px;
margin: 0 auto;
background: #fff;
color: #555;
}
main > header {
padding: 32px;
background: #86b300;
}
main > header > img {
max-width: 128px;
max-height: 28px;
vertical-align: bottom;
}
main > article {
padding: 32px;
}
main > article > h1 {
margin: 0 0 1em 0;
}
main > footer {
padding: 32px;
border-top: solid 1px #eee;
}
nav {
box-sizing: border-box;
max-width: 500px;
margin: 16px auto 0 auto;
padding: 0 32px;
}
nav > a {
color: #888;
}
</style>
</head>
<body>
<main>
<header>
<img src="${ meta.logoImageUrl ?? meta.iconUrl ?? iconUrl }"/>
</header>
<article>
<h1>${ subject }</h1>
<div>${ html }</div>
</article>
<footer>
<a href="${ emailSettingUrl }">${ 'Email setting' }</a>
</footer>
</main>
<nav>
<a href="${ this.config.url }">${ this.config.host }</a>
</nav>
</body>
</html>`,
});
this.logger.info(`Message sent: ${info.messageId}`);
} catch (err) {
this.logger.error(err as Error);
throw err;
}
}
@bindThis
public async validateEmailForAccount(emailAddress: string): Promise<{
available: boolean;
reason: null | 'used' | 'format' | 'disposable' | 'mx' | 'smtp';
}> {
const meta = await this.metaService.fetch();
const exist = await this.userProfilesRepository.countBy({
emailVerified: true,
email: emailAddress,
});
const validated = meta.enableActiveEmailValidation ? await validateEmail({
email: emailAddress,
validateRegex: true,
validateMx: true,
validateTypo: false, // TLDを見ているみたいだけどclubとか弾かれるので
validateDisposable: true, // 捨てアドかどうかチェック
validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので
}) : { valid: true, reason: null };
const available = exist === 0 && validated.valid;
return {
available,
reason: available ? null :
exist !== 0 ? 'used' :
validated.reason === 'regex' ? 'format' :
validated.reason === 'disposable' ? 'disposable' :
validated.reason === 'mx' ? 'mx' :
validated.reason === 'smtp' ? 'smtp' :
null,
};
}
}

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