commit
3c3982464f
|
@ -165,6 +165,11 @@ id: 'aidx'
|
|||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
#sentryForFrontend:
|
||||
# vueIntegration:
|
||||
# tracingOptions:
|
||||
# trackComponents: true
|
||||
# browserTracingIntegration:
|
||||
# replayIntegration:
|
||||
# options:
|
||||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
|
|
|
@ -177,6 +177,11 @@ id: 'aidx'
|
|||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
#sentryForFrontend:
|
||||
# vueIntegration:
|
||||
# tracingOptions:
|
||||
# trackComponents: true
|
||||
# browserTracingIntegration:
|
||||
# replayIntegration:
|
||||
# options:
|
||||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
|
|
|
@ -259,6 +259,11 @@ id: 'aidx'
|
|||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
#sentryForFrontend:
|
||||
# vueIntegration:
|
||||
# tracingOptions:
|
||||
# trackComponents: true
|
||||
# browserTracingIntegration:
|
||||
# replayIntegration:
|
||||
# options:
|
||||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
|
|
|
@ -152,6 +152,11 @@ id: 'aidx'
|
|||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
#sentryForFrontend:
|
||||
# vueIntegration:
|
||||
# tracingOptions:
|
||||
# trackComponents: true
|
||||
# browserTracingIntegration:
|
||||
# replayIntegration:
|
||||
# options:
|
||||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
|
|
|
@ -24,9 +24,6 @@ updates:
|
|||
aws-sdk:
|
||||
patterns:
|
||||
- "@aws-sdk/*"
|
||||
bull-board:
|
||||
patterns:
|
||||
- "@bull-board/*"
|
||||
nestjs:
|
||||
patterns:
|
||||
- "@nestjs/*"
|
||||
|
|
|
@ -22,7 +22,7 @@ jobs:
|
|||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -14,7 +14,7 @@ jobs:
|
|||
- name: Checkout head
|
||||
uses: actions/checkout@v4.2.2
|
||||
- name: Setup Node.js
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ jobs:
|
|||
|
||||
- name: setup node
|
||||
id: setup-node
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: pnpm
|
||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- uses: actions/setup-node@v4.2.0
|
||||
- uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
@ -69,13 +69,13 @@ jobs:
|
|||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- uses: actions/setup-node@v4.2.0
|
||||
- uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
- run: pnpm i --frozen-lockfile
|
||||
- name: Restore eslint cache
|
||||
uses: actions/cache@v4.2.2
|
||||
uses: actions/cache@v4.2.3
|
||||
with:
|
||||
path: ${{ env.eslint-cache-path }}
|
||||
key: eslint-${{ env.eslint-cache-version }}-${{ matrix.workspace }}-${{ hashFiles('**/pnpm-lock.yaml') }}-${{ github.ref_name }}-${{ github.sha }}
|
||||
|
@ -99,7 +99,7 @@ jobs:
|
|||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- uses: actions/setup-node@v4.2.0
|
||||
- uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -20,7 +20,7 @@ jobs:
|
|||
submodules: true
|
||||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- uses: actions/setup-node@v4.2.0
|
||||
- uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -5,7 +5,6 @@ on:
|
|||
branches:
|
||||
- master
|
||||
- develop
|
||||
- dev/storybook8 # for testing
|
||||
pull_request_target:
|
||||
branches-ignore:
|
||||
# Since pull requests targets master mostly is the "develop" branch.
|
||||
|
@ -15,8 +14,12 @@ on:
|
|||
|
||||
jobs:
|
||||
build:
|
||||
# chromatic is not likely to be available for fork repositories, so we disable for fork repositories.
|
||||
if: github.repository == 'misskey-dev/misskey'
|
||||
# Chromatic is not likely to be available for fork repositories, so we disable for fork repositories.
|
||||
# Neither Dependabot nor Renovate will change the actual behavior for components.
|
||||
if: >-
|
||||
github.repository == 'misskey-dev/misskey' &&
|
||||
startsWith(github.head_ref, 'refs/heads/dependabot/') != true &&
|
||||
startsWith(github.head_ref, 'refs/heads/renovate/') != true
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
|
@ -43,15 +46,15 @@ jobs:
|
|||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- name: Use Node.js 20.x
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version-file: '.node-version'
|
||||
cache: 'pnpm'
|
||||
- run: pnpm i --frozen-lockfile
|
||||
- name: Check pnpm-lock.yaml
|
||||
run: git diff --exit-code pnpm-lock.yaml
|
||||
- name: Build misskey-js
|
||||
run: pnpm --filter misskey-js build
|
||||
- name: Build dependent packages
|
||||
run: pnpm -F misskey-js -F misskey-bubble-game -F misskey-reversi build
|
||||
- name: Build storybook
|
||||
run: pnpm --filter frontend build-storybook
|
||||
- name: Publish to Chromatic
|
||||
|
|
|
@ -62,7 +62,7 @@ jobs:
|
|||
fi
|
||||
done
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
@ -109,7 +109,7 @@ jobs:
|
|||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -44,7 +44,7 @@ jobs:
|
|||
fi
|
||||
done
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -38,7 +38,7 @@ jobs:
|
|||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
@ -93,7 +93,7 @@ jobs:
|
|||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -33,7 +33,7 @@ jobs:
|
|||
uses: pnpm/action-setup@v4.1.0
|
||||
|
||||
- name: Setup Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -26,7 +26,7 @@ jobs:
|
|||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
|
|
@ -27,7 +27,7 @@ jobs:
|
|||
- name: Setup pnpm
|
||||
uses: pnpm/action-setup@v4.1.0
|
||||
- name: Use Node.js ${{ matrix.node-version }}
|
||||
uses: actions/setup-node@v4.2.0
|
||||
uses: actions/setup-node@v4.3.0
|
||||
with:
|
||||
node-version: ${{ matrix.node-version }}
|
||||
cache: 'pnpm'
|
||||
|
|
79
CHANGELOG.md
79
CHANGELOG.md
|
@ -1,3 +1,82 @@
|
|||
## 2025.4.0
|
||||
|
||||
### General
|
||||
- Feat: チャット(ダイレクトメッセージ)がリニューアルして復活しました
|
||||
- 既存のDM機能よりも便利で効率的な実装になっています
|
||||
- チャットを受け付ける相手を制限可能です
|
||||
- 誰でも / フォローユーザーのみ / フォロワーのみ / 相互のみ / 受け付けない から選択できます
|
||||
- 自分からメッセージを送った相手とは上記の設定に関わらずチャット可能です
|
||||
- チャット機能を開放するかどうかをロールで制御可能です
|
||||
- ルームを作成して、複数人でのチャットも可能です
|
||||
- 過去自分が送ったメッセージ・自分に送られたメッセージの検索が可能です
|
||||
- 参加中のルームをミュートして通知が来ないように設定可能です
|
||||
- メッセージにはリアクションも可能です
|
||||
- 現在、リモートユーザーがチャットを受け付ける設定になっているかどうかを取得する術がないため、ローカルユーザー間でのみ利用可能です
|
||||
- Feat: アカウントの移行時に古いアカウントからあたらしいアカウントにロールをコピーできるようになりました。
|
||||
- 管理者がロールの設定でマイグレーション時にコピーするかを指定できるようになります。
|
||||
- Enhance: セキュリティを強化するため、ジョブキューのダッシュボード(bull-board)統合が削除されました。
|
||||
- Misskeyネイティブでダッシュボードを実装予定です
|
||||
- Enhance: フロントエンドのエラートラッキングができるように
|
||||
- `.config/default.yml`中の項目`sentryForFrontend`を適宜設定してください。
|
||||
- 外部サービスであるSentryへエラー情報が送信されます。ご利用の地域の法令に従い、適切なプライバシーポリシーを策定の上で運用してください。
|
||||
- Enhance: ミュートしているユーザーをユーザー検索の結果から除外するように
|
||||
- Enhance: アンテナでセンシティブなチャンネルのノートを除外できるように `#14177`
|
||||
- Fix: 通知のページネーションで2つ以上読み込めなくなることがある問題を修正
|
||||
|
||||
### Client
|
||||
- Feat: 設定の管理が強化されました
|
||||
- 内部処理が一新され、安定性とパフォーマンスが向上しました
|
||||
- 全てのクライアント設定がエクスポート(バックアップ)/インポート対象に含まれるようになりました
|
||||
- プラグイン、テーマ、クライアントに追加されたすべてのアカウント情報も含まれるようになりました
|
||||
- 自動で設定データをサーバーにバックアップできるように
|
||||
- 設定→設定のプロファイル→自動バックアップ で有効にできます
|
||||
- 新しいデバイスからログインしたり、ブラウザから設定データが消えてしまったときに自動で復元されます(復元をスキップすることも可能)
|
||||
- 任意の設定項目をデバイス間で同期できるように
|
||||
- 設定項目の「...」メニュー→「デバイス間で同期」
|
||||
- 同期をオンにした際にサーバーに保存された値とローカルの値が競合する場合はどちらを優先するか選択できます
|
||||
- 任意の設定項目を初期値にリセットできるように
|
||||
- 設定項目の「...」メニュー→「初期値にリセット」
|
||||
- アカウントごとに設定値が分離される設定とそうでないクライアント設定が混在していた(かつ分離するかどうかを設定不可だった)のを、基本的に一律でクライアント全体に適用されるようにし、個別でアカウントごとに異なる設定を行えるように
|
||||
- 設定項目の「...」メニュー→「アカウントで上書き」をオンにすることで、設定値をそのアカウントでだけ適用するようにできます
|
||||
- ログアウトすると設定データもブラウザから消去されるようになりプライバシーが向上しました
|
||||
- 再度ログインすればサーバーのバックアップから設定データを復元可能です
|
||||
- エクスポートした設定データを他のサーバーでインポートして適用すること(設定の持ち運び)が可能になりました
|
||||
- 設定情報の移行は自動で行われますが、何らかの理由で失敗した場合、設定→その他→旧設定情報を移行 で再試行可能です
|
||||
- 過去に作成されたバックアップデータとは現在互換性がありませんのでご注意ください
|
||||
- Feat: 画面を重ねて表示するオプションを実装(実験的)
|
||||
- 設定 → その他 → 実験的機能 → Enable stacking router view
|
||||
- Enhance: プラグインの管理が強化されました
|
||||
- インストール/アンインストール/設定の変更時にリロード不要になりました
|
||||
- Enhance: ログアウト時、ブラウザに保存されたWebクライアントのデータを全て消去するように
|
||||
- Enhance: デッキUIでカラム間のマージンを設定できるように
|
||||
- Enhance: デッキUIでデッキメニューの位置を設定できるように
|
||||
- Enhance: デッキUIでナビゲーションバーの位置を設定できるように
|
||||
- Enhance: アイコンのスクロール追従を無効化してパフォーマンス向上できるように
|
||||
- Enhance: CWの注釈テキストが入力されていない場合, Postボタンを非アクティブに
|
||||
- Enhance: CWを無効にした場合, 注釈テキストが最大入力文字数を超えていても投稿できるように
|
||||
- Enhance: テーマ設定画面のデザインを改善
|
||||
- Enhance: 投稿フォームの設定メニューを改良
|
||||
- 投稿フォームをリセットできるように
|
||||
- 文字数カウントを復活
|
||||
- Enhance: 2段階認証時のリカバリーコードのファイル名にサーバーURLを含めるように
|
||||
- Enhance: 全体的なブラッシュアップ
|
||||
- Enhance 全体的なパフォーマンス向上
|
||||
- Enhance: ファイルのアップロードでデフォルトで圧縮するかどうかのオプションが廃止され、アップロード時に圧縮するかどうかを選択するようになりました
|
||||
- 画像データの貼り付け、ドロップ時は圧縮されるようになりました
|
||||
- Fix: 読み込み直後にスクロールしようとすると途中で止まる場合があるのを修正
|
||||
- Fix: テーマ切り替え時に一部の色が変わらない問題を修正
|
||||
- Fix: iPadOSでdeck uiをマウスカーソルによってスクロールできない問題を修正
|
||||
- NOTE: 構造上クラシックUIを新しいデザインシステムに移行することが困難なため、クラシックUIが削除されました
|
||||
- デッキUIでカラムを中央寄せにし、メインカラムの左右にウィジェットカラムを配置し、ナビゲーションバーを上部に表示することである程度クラシックUIを再現できます
|
||||
|
||||
### Server
|
||||
- Enhance 全体的なパフォーマンス向上
|
||||
- Fix: プロフィール追加情報で無効なURLに入力された場合に照会エラーを出るのを修正
|
||||
- Fix: ActivityPubリクエストURLチェック実装は仕様に従っていないのを修正
|
||||
- Fix: 連合無しモードでも外部から照会可能だった問題を修正
|
||||
- Fix: テスト用WebHookのペイロードの`emojis`パラメータが実際のものと異なる問題を修正
|
||||
- Fix: 非ログインでタイムラインのストリームに接続した際、表示にログイン必須のノートが流れる場合がある問題を修正
|
||||
|
||||
## 2025.3.1
|
||||
|
||||
### General
|
||||
|
|
|
@ -273,7 +273,6 @@ niraxは、Misskeyで使用しているオリジナルのフロントエンド
|
|||
query?: Record<string, string>;
|
||||
loginRequired?: boolean;
|
||||
hash?: string;
|
||||
globalCacheKey?: string;
|
||||
children?: RouteDef[];
|
||||
}
|
||||
```
|
||||
|
|
Binary file not shown.
Before Width: | Height: | Size: 94 KiB |
Binary file not shown.
Before Width: | Height: | Size: 317 KiB |
Binary file not shown.
Before Width: | Height: | Size: 24 KiB |
Binary file not shown.
Before Width: | Height: | Size: 95 KiB |
Binary file not shown.
Before Width: | Height: | Size: 238 KiB |
Binary file not shown.
Before Width: | Height: | Size: 148 KiB |
|
@ -173,6 +173,11 @@ id: "aidx"
|
|||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
#sentryForFrontend:
|
||||
# vueIntegration:
|
||||
# tracingOptions:
|
||||
# trackComponents: true
|
||||
# browserTracingIntegration:
|
||||
# replayIntegration:
|
||||
# options:
|
||||
# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
|
||||
|
||||
|
|
|
@ -251,7 +251,6 @@ removeAreYouSure: "متأكد من أنك تريد حذف {x}؟"
|
|||
deleteAreYouSure: "متأكد من أنك تريد حذف {x}؟"
|
||||
resetAreYouSure: "هل تريد إعادة التعيين؟"
|
||||
saved: "حُفظ"
|
||||
messaging: "المحادثة"
|
||||
upload: "ارفع"
|
||||
keepOriginalUploading: "ابق الصورة الأصلية"
|
||||
keepOriginalUploadingDescription: "يحفظ الصور المرفوعة على حالتها الأصلية، وان عطّل ستولد نسخة مخصصة من الصورة."
|
||||
|
@ -264,7 +263,6 @@ uploadFromUrlMayTakeTime: "سيستغرق بعض الوقت لاتمام الر
|
|||
explore: "استكشاف"
|
||||
messageRead: "مقروءة"
|
||||
noMoreHistory: "لا يوجد المزيد من التاريخ"
|
||||
startMessaging: "ابدأ محادثة"
|
||||
nUsersRead: "قرأه {n}"
|
||||
agreeTo: "اوافق على {0}"
|
||||
agree: "أقبل"
|
||||
|
@ -436,8 +434,6 @@ retype: "أعد الكتابة"
|
|||
noteOf: "ملاحظات {user}"
|
||||
quoteAttached: "اِقتُبسَ"
|
||||
quoteQuestion: "أتريد تضمينها كاقتباس"
|
||||
noMessagesYet: "ليس هناك رسائل بعد"
|
||||
newMessageExists: "لقد تلقيت رسالة جديدة"
|
||||
onlyOneFileCanBeAttached: "يمكنك إرفاق ملف واحد بالرسالة"
|
||||
signinRequired: "رجاءً لِج"
|
||||
invitations: "دعوة"
|
||||
|
@ -1012,6 +1008,14 @@ sourceCode: "الشفرة المصدرية"
|
|||
flip: "اقلب"
|
||||
lastNDays: "آخر {n} أيام"
|
||||
surrender: "ألغِ"
|
||||
postForm: "أنشئ ملاحظة"
|
||||
information: "عن"
|
||||
_chat:
|
||||
invitations: "دعوة"
|
||||
noHistory: "السجل فارغ"
|
||||
members: "الأعضاء"
|
||||
home: "الرئيسي"
|
||||
send: "أرسل"
|
||||
_delivery:
|
||||
stop: "مُعلّق"
|
||||
_initialAccountSetting:
|
||||
|
@ -1236,7 +1240,6 @@ _theme:
|
|||
shadow: "الظل"
|
||||
navBg: "خلفية الشريط الجانبي"
|
||||
navFg: "نص الشريط الجانبي"
|
||||
navHoverFg: "نص الشريط الجانبي (عند التمرير فوقه)"
|
||||
link: "رابط"
|
||||
hashtag: "وسم"
|
||||
mention: "أشر الى"
|
||||
|
@ -1311,6 +1314,7 @@ _permissions:
|
|||
"read:gallery": "اعرض المعرض"
|
||||
"write:gallery": "عدّل المعرض"
|
||||
"read:gallery-likes": "يعرض ما أعجبك من مشاركات المعرض"
|
||||
"write:chat": "اكتب أو احذف رسائل محادثة"
|
||||
_auth:
|
||||
shareAccess: "أتريد التفويض لـ \"{name}\" بالوصول لحسابك؟"
|
||||
shareAccessAsk: "هل تخول لهذا التطبيق الوصول لحسابك؟"
|
||||
|
|
|
@ -252,7 +252,6 @@ removeAreYouSure: "আপনি কি \"{x}\" সরানোর ব্যা
|
|||
deleteAreYouSure: "আপনি কি \"{x}\" সরানোর ব্যাপারে নিশ্চিত?"
|
||||
resetAreYouSure: "রিসেট করার ব্যাপারে নিশ্চিত?"
|
||||
saved: "সংরক্ষিত হয়েছে"
|
||||
messaging: "চ্যাট"
|
||||
upload: "আপলোড"
|
||||
keepOriginalUploading: "আসল ছবি রাখুন"
|
||||
keepOriginalUploadingDescription: "ছবিটি আপলোড করার সময় আসল সংস্করণটি রাখুন। অপশনটি বন্ধ থাকলে, আপলোডের সময় ওয়েব প্রকাশনার জন্য ছবি ব্রাউজারে তৈরি করা হবে।"
|
||||
|
@ -265,7 +264,6 @@ uploadFromUrlMayTakeTime: "URL হতে আপলোড হতে কিছু
|
|||
explore: "ঘুরে দেখুন"
|
||||
messageRead: "পড়া"
|
||||
noMoreHistory: "আর কোন ইতিহাস নেই"
|
||||
startMessaging: "চ্যাট শুরু করুন"
|
||||
nUsersRead: "{n} জন পড়েছেন"
|
||||
agreeTo: "{0} এর প্রতি আমি সম্মত"
|
||||
start: "শুরু করুন"
|
||||
|
@ -427,8 +425,6 @@ retype: "পুনঃ প্রবেশ"
|
|||
noteOf: "{user} এর নোট"
|
||||
quoteAttached: "উদ্ধৃত"
|
||||
quoteQuestion: "উদ্ধৃতি হিসাবে সংযুক্ত করবেন?"
|
||||
noMessagesYet: "কোন মেসেজ নেই"
|
||||
newMessageExists: "নতুন মেসেজ পেয়েছেন"
|
||||
onlyOneFileCanBeAttached: "আপনি মেসেজের সাথে সর্বোচ্চ একটি ফাইল যুক্ত করতে পারবেন"
|
||||
signinRequired: "দয়া করে লগ ইন করুন"
|
||||
invitations: "আমন্ত্রণ"
|
||||
|
@ -852,6 +848,14 @@ replies: "জবাব"
|
|||
renotes: "রিনোট"
|
||||
sourceCode: "সোর্স কোড"
|
||||
flip: "উল্টান"
|
||||
postForm: "নোট লিখুন"
|
||||
information: "আপনার সম্পর্কে"
|
||||
_chat:
|
||||
invitations: "আমন্ত্রণ"
|
||||
noHistory: "কোনো ইতিহাস নেই"
|
||||
members: "সদস্যবৃন্দ"
|
||||
home: "মূল পাতা"
|
||||
send: "পাঠান"
|
||||
_delivery:
|
||||
stop: "স্থগিত করা হয়েছে"
|
||||
_type:
|
||||
|
@ -994,7 +998,6 @@ _theme:
|
|||
header: "হেডার"
|
||||
navBg: "সাইডবারের পটভূমি"
|
||||
navFg: "সাইডবারের পাঠ্য"
|
||||
navHoverFg: "সাইডবারের পাঠ্য (হভার)"
|
||||
navActive: "সাইডবারের পাঠ্য (অ্যাকটিভ)"
|
||||
navIndicator: "সাইডবারের ইনডিকেটর"
|
||||
link: "লিংক"
|
||||
|
@ -1017,11 +1020,8 @@ _theme:
|
|||
buttonHoverBg: "বাটনের পটভূমি (হভার)"
|
||||
inputBorder: "ইনপুট ফিল্ডের বর্ডার"
|
||||
driveFolderBg: "ড্রাইভ ফোল্ডারের পটভূমি"
|
||||
wallpaperOverlay: "ওয়ালপেপার ওভারলে"
|
||||
badge: "ব্যাজ"
|
||||
messageBg: "চ্যাটের পটভূমি"
|
||||
accentDarken: "অ্যাকসেন্ট (গাঢ়)"
|
||||
accentLighten: "অ্যাকসেন্ট (হাল্কা)"
|
||||
fgHighlighted: "হাইলাইট করা পাঠ্য"
|
||||
_sfx:
|
||||
note: "নোটগুলি"
|
||||
|
@ -1084,6 +1084,7 @@ _permissions:
|
|||
"write:gallery": "গ্যালারী সম্পাদনা করুন"
|
||||
"read:gallery-likes": "গ্যালারীর পছন্দগুলি দেখুন"
|
||||
"write:gallery-likes": "গ্যালারীর পছন্দগুলি সম্পাদনা করুন"
|
||||
"write:chat": "চ্যাটগুলি সম্পাদনা করুন"
|
||||
_auth:
|
||||
shareAccess: "\"{name}\" কে অ্যাকাউন্টের অ্যাক্সেস দিবেন?"
|
||||
shareAccessAsk: "অ্যাপ্লিকেশনটিকে অ্যাকাউন্টের অ্যাক্সেস দিবেন?"
|
||||
|
|
|
@ -289,7 +289,6 @@ deleteAreYouSure: "Segur que vols esborrar «{x}»?"
|
|||
resetAreYouSure: "Segur que vols restablir-ho?"
|
||||
areYouSure: "Estàs segur?"
|
||||
saved: "S'ha desat"
|
||||
messaging: "Xat"
|
||||
upload: "Puja"
|
||||
keepOriginalUploading: "Guarda la imatge original"
|
||||
keepOriginalUploadingDescription: "Guarda la imatge pujada sense modificar. Si està desactivat, es generarà una versió per visualitzar a la web en pujar la imatge."
|
||||
|
@ -302,7 +301,7 @@ uploadFromUrlMayTakeTime: "La càrrega des de l'enllaç pot trigar un temps"
|
|||
explore: "Explora"
|
||||
messageRead: "Vist"
|
||||
noMoreHistory: "No hi ha res més per veure"
|
||||
startMessaging: "Comença a xatejar"
|
||||
startChat: "Comença a xatejar "
|
||||
nUsersRead: "Vist per {n}"
|
||||
agreeTo: "Accepto que {0}"
|
||||
agree: "Hi estic d'acord"
|
||||
|
@ -425,6 +424,7 @@ antennaExcludeBots: "Exclou els bots"
|
|||
antennaKeywordsDescription: "Separar amb espais per la condició AND o amb salts de línia per la condició OR."
|
||||
notifyAntenna: "Notifica'm les publicacions noves"
|
||||
withFileAntenna: "Només les publicacions amb fitxers"
|
||||
excludeNotesInSensitiveChannel: "Excloure notes a canals sensibles"
|
||||
enableServiceworker: "Activar les notificacions al navegador"
|
||||
antennaUsersDescription: "Llistar un nom d'usuari per línia"
|
||||
caseSensitive: "Sensible a majúscules i minúscules "
|
||||
|
@ -491,8 +491,6 @@ noteOf: "Publicació de: {user}"
|
|||
quoteAttached: "Frase adjunta"
|
||||
quoteQuestion: "Vols annexar-la com a cita?"
|
||||
attachAsFileQuestion: "El text copiat és massa llarg. Vols adjuntar-lo com un fitxer de text?"
|
||||
noMessagesYet: "Encara no hi ha missatges"
|
||||
newMessageExists: "Has rebut un nou missatge"
|
||||
onlyOneFileCanBeAttached: "Només pots adjuntar un fitxer a un missatge"
|
||||
signinRequired: "Si us plau, Registra't o inicia la sessió abans de continuar"
|
||||
signinOrContinueOnRemote: "Per continuar necessites moure el teu servidor o registrar-te / iniciar sessió en aquest servidor."
|
||||
|
@ -539,7 +537,7 @@ mediaListWithOneImageAppearance: "Altura de la llista de fitxers amb una única
|
|||
limitTo: "Limita a {x}"
|
||||
noFollowRequests: "No tens sol·licituds de seguiment"
|
||||
openImageInNewTab: "Obre imatges a una nova pestanya"
|
||||
dashboard: "Taulell de control"
|
||||
dashboard: "Tauler de control"
|
||||
local: "Local"
|
||||
remote: "Remot"
|
||||
total: "Total"
|
||||
|
@ -654,7 +652,7 @@ manage: "Administració"
|
|||
plugins: "Extensions"
|
||||
preferencesBackups: "Configuracions de les Còpies de seguretat"
|
||||
deck: "Escriptori"
|
||||
undeck: "Tanca l'escriptori"
|
||||
undeck: "Tanca el tauler"
|
||||
useBlurEffectForModal: "Utilitzar l'efecte de difuminació a modals"
|
||||
useFullReactionPicker: "Utilitza el cercador de reaccions d'escala sencera"
|
||||
width: "Amplada"
|
||||
|
@ -698,6 +696,7 @@ userSaysSomethingAbout: "{name} està parlant sobre \"{word}\""
|
|||
makeActive: "Activar"
|
||||
display: "Veure"
|
||||
copy: "Copiar"
|
||||
copiedToClipboard: "Copiat al porta papers"
|
||||
metrics: "Mètriques"
|
||||
overview: "Visió General"
|
||||
logs: "Registres"
|
||||
|
@ -916,7 +915,7 @@ off: "Desactivar"
|
|||
emailRequiredForSignup: "Demanar correu electrònic per registrar-se "
|
||||
unread: "Sense llegir"
|
||||
filter: "Filtrar"
|
||||
controlPanel: "Taulell de control"
|
||||
controlPanel: "Tauler de control"
|
||||
manageAccounts: "Gestionar comptes"
|
||||
makeReactionsPublic: "Reaccions públiques "
|
||||
makeReactionsPublicDescription: "Això fa que totes les teves reaccions siguin visibles públicament "
|
||||
|
@ -1130,7 +1129,7 @@ pleaseAgreeAllToContinue: "Has d'acceptar tots els camps de dalt per poder conti
|
|||
continue: "Continuar"
|
||||
preservedUsernames: "Noms d'usuaris reservats"
|
||||
preservedUsernamesDescription: "Llistat de noms d'usuaris que no es poden fer servir separats per salts de linia. Aquests noms d'usuaris no estaran disponibles quan es creï un compte d'usuari normal, però els administradors els poden fer servir per crear comptes manualment. Per altre banda els comptes ja creats amb aquests noms d'usuari no es veure'n afectats."
|
||||
createNoteFromTheFile: "Compon una nota des d'aquest fitxer"
|
||||
createNoteFromTheFile: "Escriu una nota incloent aquest fitxer"
|
||||
archive: "Arxiu"
|
||||
archived: "Arxivat"
|
||||
unarchive: "Desarxivar"
|
||||
|
@ -1139,7 +1138,7 @@ channelArchiveConfirmDescription: "Un Canal arxivat no apareixerà a la llista d
|
|||
thisChannelArchived: "Aquest Canal ha sigut arxivat."
|
||||
displayOfNote: "Mostrar notes"
|
||||
initialAccountSetting: "Configuració del perfil"
|
||||
youFollowing: "Seguint"
|
||||
youFollowing: "Segueixes "
|
||||
preventAiLearning: "Descartar l'ús d'aprenentatge automàtic (IA Generativa)"
|
||||
preventAiLearningDescription: "Demanar els indexadors no fer servir els texts, imatges, etc. en cap conjunt de dades per alimentar l'aprenentatge automàtic (IA Predictiva/ Generativa). Això s'aconsegueix afegint la etiqueta \"noai\" com a resposta HTML al contingut corresponent. Prevenir aquest ús totalment pot ser que no sigui aconseguit, ja que molts indexadors poden obviar aquesta etiqueta."
|
||||
options: "Opcions"
|
||||
|
@ -1190,7 +1189,7 @@ pastAnnouncements: "Informes passats"
|
|||
youHaveUnreadAnnouncements: "Tens informes per llegir."
|
||||
useSecurityKey: "Segueix les instruccions del teu navegador O dispositiu per fer servir el teu passkey."
|
||||
replies: "Respostes"
|
||||
renotes: "Impulsar"
|
||||
renotes: "Impulsos"
|
||||
loadReplies: "Mostrar les respostes"
|
||||
loadConversation: "Mostrar la conversació "
|
||||
pinnedList: "Llista fixada"
|
||||
|
@ -1313,6 +1312,130 @@ confirmOnReact: "Confirmar en reaccionar"
|
|||
reactAreYouSure: "Vols reaccionar amb \"{emoji}\"?"
|
||||
markAsSensitiveConfirm: "Vols marcar aquest contingut com a sensible?"
|
||||
unmarkAsSensitiveConfirm: "Vols deixar de marcar com a sensible aquest contingut?"
|
||||
preferences: "Preferències "
|
||||
accessibility: "Accessibilitat "
|
||||
preferencesProfile: "Perfil de configuració "
|
||||
copyPreferenceId: "Copiar l'ID de la configuració "
|
||||
resetToDefaultValue: "Restaura al valor per defecte "
|
||||
overrideByAccount: "Anul·lar per compte"
|
||||
untitled: "Sense títol "
|
||||
noName: "No hi ha un nom disponible "
|
||||
skip: "Ometre "
|
||||
restore: "Restaurar "
|
||||
syncBetweenDevices: "Sincronització entre dispositius"
|
||||
preferenceSyncConflictTitle: "Els valors de la configuració ja existeixen al dispositiu"
|
||||
preferenceSyncConflictText: "Un element de la configuració amb sincronització activada desa els seus valors al servidor, però s'ha trobat un valor a la configuració desat al servidor per aquest element de la configuració. Quin valor us sobreescriure?"
|
||||
preferenceSyncConflictChoiceServer: "Valors de configuració del servidor"
|
||||
preferenceSyncConflictChoiceDevice: "Punts d'ajustos del dispositiu "
|
||||
preferenceSyncConflictChoiceCancel: "Cancel·lar l'activació de la sincronització "
|
||||
paste: "Pegar"
|
||||
emojiPalette: "Calaix d'emojis"
|
||||
postForm: "Formulari de publicació"
|
||||
textCount: "Nombre de caràcters "
|
||||
information: "Informació"
|
||||
chat: "Xat"
|
||||
migrateOldSettings: "Migració de la configuració antiga "
|
||||
migrateOldSettings_description: "Normalment això es fa automàticament, però si la transició no es fa, el procés es pot iniciar manualment. S'esborrarà la configuració actual."
|
||||
compress: "Comprimir "
|
||||
right: "Dreta"
|
||||
bottom: "A baix "
|
||||
top: "A dalt "
|
||||
embed: "Incrustar"
|
||||
settingsMigrating: "Estem fent la migració de la teva configuració. Si us plau espera un moment... (També pots fer la migració més tard i manualment anant a Preferències → Altres configuracions → Migrar configuració antiga)"
|
||||
readonly: "Només lectura"
|
||||
goToDeck: "Tornar al tauler"
|
||||
_chat:
|
||||
noMessagesYet: "Encara no tens missatges "
|
||||
newMessage: "Missatge nou"
|
||||
individualChat: "Xat individual "
|
||||
individualChat_description: "Pots mantenir converses individuals amb usuaris concrets."
|
||||
roomChat: "Sala de xat"
|
||||
roomChat_description: "Pots xatejar amb diverses persones.\nTambé pots xatejar amb usuaris que no poden fer xats privats, si ells accepten."
|
||||
createRoom: "Crear una sala"
|
||||
inviteUserToChat: "Invita usuaris per començar a xatejar"
|
||||
yourRooms: "Sales creades"
|
||||
joiningRooms: "Sales a les quals participes"
|
||||
invitations: "Convida"
|
||||
noInvitations: "No tens cap invitació "
|
||||
history: "Historial de converses "
|
||||
noHistory: "No hi ha un registre previ"
|
||||
noRooms: "No hi ha habitacions"
|
||||
inviteUser: "Invitar usuaris"
|
||||
sentInvitations: "Enviar invitacions"
|
||||
join: "Afegir-se "
|
||||
ignore: "Ignorar "
|
||||
leave: "Marxar"
|
||||
members: "Membres"
|
||||
searchMessages: "Buscar missatges "
|
||||
home: "Inici"
|
||||
send: "Envia"
|
||||
newline: "Línia nova "
|
||||
muteThisRoom: "Silenciar aquesta sala"
|
||||
deleteRoom: "Esborrar la sala"
|
||||
chatNotAvailableForThisAccountOrServer: "El xat no està disponible per aquest servidor o aquest compte."
|
||||
chatIsReadOnlyForThisAccountOrServer: "El xat és només de lectura en aquest servidor o compte. No es poden escriure nous missatges ni crear o unir-se a sales de xat."
|
||||
chatNotAvailableInOtherAccount: "La funció de xat es troba desactivada al compte de l'altre usuari."
|
||||
cannotChatWithTheUser: "No pots xatejar amb aquest usuari"
|
||||
cannotChatWithTheUser_description: "El xat està desactivat o l'altra part encara no l'ha obert."
|
||||
chatWithThisUser: "Xateja amb aquest usuari"
|
||||
thisUserAllowsChatOnlyFromFollowers: "Aquest usuari només accepta xats d'usuaris que el segueixen."
|
||||
thisUserAllowsChatOnlyFromFollowing: "Aquest usuari només accepta xats d'usuaris que segueix."
|
||||
thisUserAllowsChatOnlyFromMutualFollowing: "Aquest usuari només accepta xats d'usuaris que segueixes i et segueixen."
|
||||
thisUserNotAllowedChatAnyone: "Aquest usuari no accepta xats de ningú."
|
||||
chatAllowedUsers: "Usuaris que poden xatejar"
|
||||
chatAllowedUsers_note: "Pots xatejar amb qualsevol usuari a qui hagis enviat un missatge de xat, independentment d'aquesta configuració."
|
||||
_chatAllowedUsers:
|
||||
everyone: "Tothom"
|
||||
followers: "Només els teus seguidors"
|
||||
following: "Només usuaris als que segueixes"
|
||||
mutual: "Només seguidors mutus"
|
||||
none: "Ningú "
|
||||
_emojiPalette:
|
||||
palettes: "Calaixos d'emojis"
|
||||
enableSyncBetweenDevicesForPalettes: "Activa la sincronització dels calaixos d'emojis entre dispositius"
|
||||
paletteForMain: "Calaix d'emojis principal"
|
||||
paletteForReaction: "Calaix d'emojis per reaccions"
|
||||
_settings:
|
||||
driveBanner: "Pots gestionar i configurar el Disc, comprovar el seu ús i establir una configuració per a la càrrega d'arxius."
|
||||
pluginBanner: "Els complements poden fer-se servir per ampliar les funcionalitats del client. Els complements poden instal·lar-se, configurar-se individualment i gestionar-se."
|
||||
notificationsBanner: "Pots configurar el tipus i l'abast de les notificacions que es rebran del servidor, també les notificacions emergents."
|
||||
api: "API"
|
||||
webhook: "Webhook"
|
||||
serviceConnection: "Relació entre serveis"
|
||||
serviceConnectionBanner: "Pots configurar i gestionar tokens d'accés i webhooks per integrar serveis i aplicacions externes."
|
||||
accountData: "Dades del compte"
|
||||
accountDataBanner: "Exportació/Importació i gestió d'arxius amb dades del compte."
|
||||
muteAndBlockBanner: "Pots configurar i gestionar els continguts que desitges amagar i restringir les accions de determinats usuaris."
|
||||
accessibilityBanner: "Els clients poden personalitzar-se i configurar-se per un ús òptim en funció de la seva visió i comportament."
|
||||
privacyBanner: "Pots establir la configuració de privacitat del compte, com el grau de visibilitat del teu contingut, la facilitat per trobar-ho i si es pot aprovar els seguidors."
|
||||
securityBanner: "Configura les opcions relacionades amb la seguretat del teu compte com ara contrasenyes, mètodes per iniciar sessió, aplicacions d'autentificació i claus d'accés."
|
||||
preferencesBanner: "Pots configurar el comportament general del client segons les teves preferències."
|
||||
appearanceBanner: "Pots configurar les preferències relacionades amb la visualització i l'aspecte del client segons el teu parer."
|
||||
soundsBanner: "Configuració dels sons que reproduirà el client."
|
||||
timelineAndNote: "Línia de temps i nota"
|
||||
makeEveryTextElementsSelectable: "Fes que tots els elements del text siguin seleccionables"
|
||||
makeEveryTextElementsSelectable_description: "L'activació pot reduir la usabilitat en determinades ocasions."
|
||||
useStickyIcons: "Utilitza icones fixes"
|
||||
showNavbarSubButtons: "Mostrar sub botons a la barra de navegació "
|
||||
ifOn: "Quan s'encén "
|
||||
ifOff: "Quan s'apaga "
|
||||
enableSyncThemesBetweenDevices: "Sincronitzar els temes instal·lats entre dispositius"
|
||||
_chat:
|
||||
showSenderName: "Mostrar el nom del remitent"
|
||||
sendOnEnter: "Introdueix per enviar"
|
||||
_preferencesProfile:
|
||||
profileName: "Nom del perfil"
|
||||
profileNameDescription: "Estableix un nom que identifiqui aquest dispositiu."
|
||||
profileNameDescription2: "Per exemple: \"PC Principal\", \"Smartphone\", etc"
|
||||
_preferencesBackup:
|
||||
autoBackup: "Còpia de seguretat automàtica "
|
||||
restoreFromBackup: "Restaurar des d'una còpia de seguretat"
|
||||
noBackupsFoundTitle: "No s'ha trobat cap còpia de seguretat"
|
||||
noBackupsFoundDescription: "No s'han trobat còpies de seguretat creades automàticament, però si has desat, manualment, un arxiu de còpia de seguretat, pots importar-lo i carregar-lo."
|
||||
selectBackupToRestore: "Seleccionar la còpia de seguretat que vols restaurar"
|
||||
youNeedToNameYourProfileToEnableAutoBackup: "Has de posar-li un nom al teu perfil per poder activar les còpies de seguretat automàtiques."
|
||||
autoPreferencesBackupIsNotEnabledForThisDevice: "La còpia de seguretat automàtica no es troba activada en aquest dispositiu."
|
||||
backupFound: "Còpia de seguretat de la configuració trobada"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "És obligatori l'inici de sessió per poder veure el contingut"
|
||||
requireSigninToViewContentsDescription1: "Es requereix l'inici de sessió per poder veure totes les notes i el contingut que has creat. Amb això esperem evitar que els rastrejadors recopilin informació."
|
||||
|
@ -1323,6 +1446,7 @@ _accountSettings:
|
|||
makeNotesHiddenBefore: "Fes que les notes antigues siguin privades"
|
||||
makeNotesHiddenBeforeDescription: "Mentres aquesta funció estigui activada les notes que hagin superat una data i hora fixada o hagi passat el temps establert només seran visibles per a tu. Si la desactives es restablirà també l'estat públic de les notes."
|
||||
mayNotEffectForFederatedNotes: "Això pot ser que no afecti les notes federades."
|
||||
mayNotEffectSomeSituations: "Aquestes restriccions són simplificades. Pot ser que no s'apliquin en determinades situacions, com quan es modera o visualitza un servidor remot."
|
||||
notesHavePassedSpecifiedPeriod: "Notes publicades durant un període de temps especificat."
|
||||
notesOlderThanSpecifiedDateAndTime: "Notes més antigues de la data i temps especificat "
|
||||
_abuseUserReport:
|
||||
|
@ -1768,6 +1892,8 @@ _role:
|
|||
descriptionOfIsExplorable: "La línia de temps d'aquest rol i la llista d'usuaris seran públics si s'activa."
|
||||
displayOrder: "Posició "
|
||||
descriptionOfDisplayOrder: "Com més gran és el número, més dalt la seva posició a la interfície."
|
||||
preserveAssignmentOnMoveAccount: "L'estat de l'assignació també es trasllada amb el compte migrat"
|
||||
preserveAssignmentOnMoveAccount_description: "Si s'activa quan es migra un compte amb aquest rol, el compte migrat també heretarà aquest rol."
|
||||
canEditMembersByModerator: "Permetre que els moderadors editin la llista d'usuaris en aquest rol"
|
||||
descriptionOfCanEditMembersByModerator: "Quan s'activa, els moderadors, així com els administradors, podran afegir i treure usuaris d'aquest rol. Si es troba desactivat, només els administradors poden assignar usuaris."
|
||||
priority: "Prioritat"
|
||||
|
@ -1808,6 +1934,7 @@ _role:
|
|||
canImportFollowing: "Autoritza la importació de seguidors"
|
||||
canImportMuting: "Autoritza la importació de silenciats"
|
||||
canImportUserLists: "Autoritza la importació de llistes d'usuaris "
|
||||
chatAvailability: "Es permet xatejar"
|
||||
_condition:
|
||||
roleAssignedTo: "Assignat a rols manuals"
|
||||
isLocal: "Usuari local"
|
||||
|
@ -1971,6 +2098,7 @@ _theme:
|
|||
installed: "{name} Instal·lat "
|
||||
installedThemes: "Temes instal·lats "
|
||||
builtinThemes: "Temes integrats"
|
||||
instanceTheme: "Tema de la instància "
|
||||
alreadyInstalled: "Aquest tema ja es troba instal·lat "
|
||||
invalid: "El format d'aquest tema no és correcte"
|
||||
make: "Crear un tema"
|
||||
|
@ -1998,19 +2126,18 @@ _theme:
|
|||
fg: "Text"
|
||||
focus: "Enfocament"
|
||||
indicator: "Indicador"
|
||||
panel: "Taulell "
|
||||
panel: "Tauler"
|
||||
shadow: "Ombra"
|
||||
header: "Capçalera"
|
||||
navBg: "Fons de la barra lateral"
|
||||
navFg: "Text de la barra lateral"
|
||||
navHoverFg: "Text barra lateral (en passar per sobre)"
|
||||
navActive: "Text barra lateral (actiu)"
|
||||
navIndicator: "Indicador barra lateral"
|
||||
link: "Enllaç"
|
||||
hashtag: "Etiqueta"
|
||||
mention: "Menció"
|
||||
mentionMe: "Mencions (jo)"
|
||||
renote: "Renotar"
|
||||
renote: "Impulsar"
|
||||
modalBg: "Fons del modal"
|
||||
divider: "Divisor"
|
||||
scrollbarHandle: "Maneta de la barra de desplaçament"
|
||||
|
@ -2026,17 +2153,15 @@ _theme:
|
|||
buttonHoverBg: "Fons botó (en passar-hi per sobre)"
|
||||
inputBorder: "Contorn del cap d'introducció "
|
||||
driveFolderBg: "Fons de la carpeta Disc"
|
||||
wallpaperOverlay: "Superposició del fons de pantalla "
|
||||
badge: "Insígnia "
|
||||
messageBg: "Fons del xat"
|
||||
accentDarken: "Accent (fosc)"
|
||||
accentLighten: "Accent (clar)"
|
||||
fgHighlighted: "Text ressaltat"
|
||||
_sfx:
|
||||
note: "Notes"
|
||||
noteMy: "Nota (per mi)"
|
||||
notification: "Notificacions"
|
||||
reaction: "Quan se selecciona una reacció "
|
||||
chatMessage: "Missatges del xat"
|
||||
_soundSettings:
|
||||
driveFile: "Fer servir un fitxer d'àudio del disc"
|
||||
driveFileWarn: "Seleccionar un fitxer d'àudio del disc"
|
||||
|
@ -2183,6 +2308,8 @@ _permissions:
|
|||
"read:clip-favorite": "Veure clips favorits"
|
||||
"read:federation": "Veure dades de federació"
|
||||
"write:report-abuse": "Informar d'un abús"
|
||||
"write:chat": "Crear o esborrar missatges de xat"
|
||||
"read:chat": "Explorar xats"
|
||||
_auth:
|
||||
shareAccessTitle: "Concedeix permisos a l'aplicació"
|
||||
shareAccess: "Vols que {name} pugui accedir al vostre compte?"
|
||||
|
@ -2431,6 +2558,7 @@ _notification:
|
|||
newNote: "Nota nova"
|
||||
unreadAntennaNote: "Antena {name}"
|
||||
roleAssigned: "Rol assignat "
|
||||
chatRoomInvitationReceived: "T'han invitat a una sala de xat"
|
||||
emptyPushNotificationMessage: "Les notificacions han sigut actualitzades"
|
||||
achievementEarned: "Aconseguiment desblocat"
|
||||
testNotification: "Notificació de prova"
|
||||
|
@ -2452,26 +2580,31 @@ _notification:
|
|||
follow: "Segueix-me"
|
||||
mention: "Menció"
|
||||
reply: "Respostes"
|
||||
renote: "Impulsar"
|
||||
renote: "Impulsos"
|
||||
quote: "Citar"
|
||||
reaction: "Reaccions"
|
||||
pollEnded: "Enquesta terminada"
|
||||
receiveFollowRequest: "Rebuda una petició de seguiment"
|
||||
followRequestAccepted: "Petició de seguiment acceptada"
|
||||
roleAssigned: "Rol donat"
|
||||
chatRoomInvitationReceived: "Invitat a la sala de xat"
|
||||
achievementEarned: "Assoliment desbloquejat"
|
||||
exportCompleted: "Exportació completada"
|
||||
login: "Iniciar sessió"
|
||||
createToken: "Creació de tokens d'accés "
|
||||
test: "Prova la notificació"
|
||||
app: "Notificacions d'aplicacions"
|
||||
_actions:
|
||||
followBack: "També et segueix"
|
||||
reply: "Respondre"
|
||||
renote: "Impulsos"
|
||||
renote: "Impulsar"
|
||||
_deck:
|
||||
alwaysShowMainColumn: "Mostrar sempre la columna principal"
|
||||
columnAlign: "Alinea les columnes"
|
||||
addColumn: "Afig una columna"
|
||||
columnGap: "Espai entre columnes"
|
||||
deckMenuPosition: "Posició del menú del tauler"
|
||||
navbarPosition: "Posició de la barra de navegació "
|
||||
addColumn: "Afegeix una columna"
|
||||
newNoteNotificationSettings: "Configuració de notificacions per a notes noves"
|
||||
configureColumn: "Configuració de columnes"
|
||||
swapLeft: "Mou a l’esquerra"
|
||||
|
@ -2489,6 +2622,7 @@ _deck:
|
|||
useSimpleUiForNonRootPages: "Usa una interfície senzilla per a les pàgines navegades"
|
||||
usedAsMinWidthWhenFlexible: "L'amplada mínima es farà servir quan \"Ajust automàtic de l'amplada\" estigui activat"
|
||||
flexible: "Ajust automàtic de l'amplada"
|
||||
enableSyncBetweenDevicesForProfiles: "Activar la sincronització de la informació de perfils de dispositiu a dispositiu"
|
||||
_columns:
|
||||
main: "Principal"
|
||||
widgets: "Ginys"
|
||||
|
@ -2596,6 +2730,7 @@ _moderationLogTypes:
|
|||
deletePage: "Esborrar la pàgina"
|
||||
deleteFlash: "Esborrar el guió"
|
||||
deleteGalleryPost: "Esborrar la publicació de la galeria"
|
||||
deleteChatRoom: "Esborra la sala de xat"
|
||||
updateProxyAccountDescription: "Actualitzar descripció del compte proxy"
|
||||
_fileViewer:
|
||||
title: "Detall del fitxer"
|
||||
|
@ -2610,10 +2745,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "Assegura't que qui distribueix aquest recurs és fiable abans d'instal·lar-ho."
|
||||
_plugin:
|
||||
title: "Vols instal·lar aquest afegit?"
|
||||
metaTitle: "Informació de l'afegit "
|
||||
_theme:
|
||||
title: "Vols instal·lar aquest tema?"
|
||||
metaTitle: "Informació del tema"
|
||||
_meta:
|
||||
base: "Paleta de colors base"
|
||||
_vendorInfo:
|
||||
|
@ -2666,7 +2799,7 @@ _hemisphere:
|
|||
_reversi:
|
||||
reversi: "Reversi"
|
||||
gameSettings: "Opcions del joc"
|
||||
chooseBoard: "Escull un taulell"
|
||||
chooseBoard: "Escull un tauler"
|
||||
blackOrWhite: "Negres/Blanques"
|
||||
blackIs: "{name} juga amb negres "
|
||||
rules: "Regles"
|
||||
|
|
|
@ -9,6 +9,9 @@ reset: "Obnovit"
|
|||
notifications: "Oznámení"
|
||||
username: "Uživatelské jméno"
|
||||
password: "Heslo"
|
||||
initialPasswordForSetup: "Počáteční heslo pro nastavení"
|
||||
initialPasswordIsIncorrect: "Počáteční heslo pro nastavení je nesprávné"
|
||||
initialPasswordForSetupDescription: "Použijte heslo, které jste nastavili v konfiguračním souboru, pokud jste Misskey instalovali ručně.\nPokud užíváte Misskey hostovací službu, použijte poskytnuté heslo.\nPokud jste heslo nenastavovali, zanechte prázdné."
|
||||
forgotPassword: "Zapomenuté heslo"
|
||||
fetchingAsApObject: "Načítám data z Fediversu..."
|
||||
ok: "Potvrdit"
|
||||
|
@ -46,6 +49,8 @@ pin: "Připnout"
|
|||
unpin: "Odepnout"
|
||||
copyContent: "Zkopírovat obsah"
|
||||
copyLink: "Kopírovat odkaz"
|
||||
copyRemoteLink: "Zkoprírovat vzdálený odkaz"
|
||||
copyLinkRenote: "Zkopírovat odkaz renotu"
|
||||
delete: "Smazat"
|
||||
deleteAndEdit: "Smazat a upravit"
|
||||
deleteAndEditConfirm: "Jste si jistí že chcete smazat tuto poznámku a editovat ji? Ztratíte tím všechny reakce, sdílení a odpovědi na ni."
|
||||
|
@ -169,6 +174,9 @@ addAccount: "Přidat účet"
|
|||
reloadAccountsList: "Obnovit list účtů"
|
||||
loginFailed: "Přihlášení se nezdařilo."
|
||||
showOnRemote: "Více na původním profilu"
|
||||
continueOnRemote: "Pokračujte na původní profil"
|
||||
chooseServerOnMisskeyHub: "Vyberete si server z Misskey Hubu"
|
||||
inputHostName: "Zadejte doménu"
|
||||
general: "Obecně"
|
||||
wallpaper: "Obrázek na pozadí"
|
||||
setWallpaper: "Nastavení obrázku na pozadí"
|
||||
|
@ -193,6 +201,7 @@ perHour: "za hodinu"
|
|||
perDay: "za den"
|
||||
stopActivityDelivery: "Přestat zasílat aktivitu"
|
||||
blockThisInstance: "Blokovat tuto instanci"
|
||||
silenceThisInstance: "Utišit tuto instanci"
|
||||
operations: "Operace"
|
||||
software: "Software"
|
||||
version: "Verze"
|
||||
|
@ -257,7 +266,6 @@ removeAreYouSure: "Jste si jistí že chcete smazat \"{x}\"?"
|
|||
deleteAreYouSure: "Jste si jistí že chcete smazat \"{x}\"?"
|
||||
resetAreYouSure: "Opravdu resetovat?"
|
||||
saved: "Uloženo"
|
||||
messaging: "Zprávy"
|
||||
upload: "Nahrát soubory"
|
||||
keepOriginalUploading: "Ponechat originální obrázek"
|
||||
keepOriginalUploadingDescription: "Uloží původní nahraný obrázek jak je. Pokud je to vypnuté, vygeneruje se zobrazení verze na webu při nahrátí."
|
||||
|
@ -270,7 +278,6 @@ uploadFromUrlMayTakeTime: "Může trvat nějakou dobu, dokud nebude dokončeno n
|
|||
explore: "Objevovat"
|
||||
messageRead: "Přečtené"
|
||||
noMoreHistory: "To je vše"
|
||||
startMessaging: "Zahájit chat"
|
||||
nUsersRead: "přečteno {n} uživateli"
|
||||
agreeTo: "Souhlasím s {0}"
|
||||
agree: "Souhlasím"
|
||||
|
@ -449,8 +456,6 @@ retype: "Zadejte znovu"
|
|||
noteOf: "{user} poznámky"
|
||||
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"
|
||||
|
@ -474,6 +479,8 @@ uiLanguage: "Jazyk uživatelského rozhraní"
|
|||
aboutX: "O {x}"
|
||||
emojiStyle: "Styl emoji"
|
||||
native: "Výchozí"
|
||||
style: "Vzhled"
|
||||
popup: "Vyskakovací okno"
|
||||
showNoteActionsOnlyHover: "Zobrazit akce poznámky jenom při naběhnutí myši"
|
||||
noHistory: "Žádná historie"
|
||||
signinHistory: "Historie přihlášení"
|
||||
|
@ -536,6 +543,7 @@ showInPage: "Zobrazit na stránce"
|
|||
popout: "Pop-out"
|
||||
volume: "Hlasitost"
|
||||
masterVolume: "Celková hlasitost"
|
||||
notUseSound: "Zakázat zvuk"
|
||||
details: "Detaily"
|
||||
chooseEmoji: "Vybrat emotikon"
|
||||
unableToProcess: "Operace nebyla dokončena."
|
||||
|
@ -1098,6 +1106,14 @@ sourceCode: "Zdrojový kód"
|
|||
flip: "Otočit"
|
||||
lastNDays: "Posledních {n} dnů"
|
||||
surrender: "Zrušit"
|
||||
postForm: "Formulář pro odeslání"
|
||||
information: "Informace"
|
||||
_chat:
|
||||
invitations: "Pozvat"
|
||||
noHistory: "Žádná historie"
|
||||
members: "Členové"
|
||||
home: "Domů"
|
||||
send: "Odeslat"
|
||||
_delivery:
|
||||
stop: "Suspendováno"
|
||||
_type:
|
||||
|
@ -1610,7 +1626,6 @@ _theme:
|
|||
header: "Nadpis"
|
||||
navBg: "Pozadí postranního panelu"
|
||||
navFg: "Text na postranním panelu"
|
||||
navHoverFg: "Text na postranním panelu (Hover)"
|
||||
navActive: "Text na postranním panelu (Aktivní)"
|
||||
navIndicator: "Indikátor na postranním panelu"
|
||||
link: "Odkaz"
|
||||
|
@ -1633,11 +1648,8 @@ _theme:
|
|||
buttonHoverBg: "Pozadí tlačítka (Hover)"
|
||||
inputBorder: "Ohraničení vstupního pole"
|
||||
driveFolderBg: "Pozadí složky disku"
|
||||
wallpaperOverlay: "Překrytí tapety"
|
||||
badge: "Odznak"
|
||||
messageBg: "Pozadí chatu"
|
||||
accentDarken: "Akcent (Ztmavený)"
|
||||
accentLighten: "Akcent (Zesvětlený)"
|
||||
fgHighlighted: "Zvýrazněný text"
|
||||
_sfx:
|
||||
note: "Poznámky"
|
||||
|
@ -1713,6 +1725,7 @@ _permissions:
|
|||
"write:gallery": "Upravit galerii"
|
||||
"read:gallery-likes": "Zobrazit seznam to se mi líbí příspěvků v galerii"
|
||||
"write:gallery-likes": "Upravit seznam to se mi líbí příspěvků v galerii"
|
||||
"write:chat": "Sestavit nebo mazat zprávy chatu"
|
||||
_auth:
|
||||
shareAccessTitle: "Udělovat oprávnění k aplikacím"
|
||||
shareAccess: "Chcete autorizovat \"{name}\" pro přístup k tomuto účtu?"
|
||||
|
|
|
@ -49,7 +49,7 @@ pin: "An dein Profil anheften"
|
|||
unpin: "Von deinem Profil lösen"
|
||||
copyContent: "Inhalt kopieren"
|
||||
copyLink: "Link kopieren"
|
||||
copyRemoteLink: "Renote-Link kopieren"
|
||||
copyRemoteLink: "Remote-Link kopieren"
|
||||
copyLinkRenote: "Renote-Link kopieren"
|
||||
delete: "Löschen"
|
||||
deleteAndEdit: "Löschen und Bearbeiten"
|
||||
|
@ -289,7 +289,6 @@ deleteAreYouSure: "Möchtest du „{x}“ wirklich löschen?"
|
|||
resetAreYouSure: "Wirklich zurücksetzen?"
|
||||
areYouSure: "Bist du sicher?"
|
||||
saved: "Erfolgreich gespeichert"
|
||||
messaging: "Chat"
|
||||
upload: "Hochladen"
|
||||
keepOriginalUploading: "Originalbild speichern"
|
||||
keepOriginalUploadingDescription: "Speichert das Originalbild so, wie es ist. Ist dies deaktiviert, wird eine Version zum Anzeigen im Internet generiert."
|
||||
|
@ -302,7 +301,7 @@ uploadFromUrlMayTakeTime: "Es kann eine Weile dauern, bis das Hochladen abgeschl
|
|||
explore: "Erkunden"
|
||||
messageRead: "Gelesen"
|
||||
noMoreHistory: "Kein weiterer Verlauf vorhanden"
|
||||
startMessaging: "Neuen Chat erstellen"
|
||||
startChat: "Chat starten"
|
||||
nUsersRead: "Von {n} Benutzern gelesen"
|
||||
agreeTo: "Ich stimme {0} zu"
|
||||
agree: "Zustimmen"
|
||||
|
@ -425,6 +424,7 @@ antennaExcludeBots: "Bot-Accounts ausschließen"
|
|||
antennaKeywordsDescription: "Zum Nutzen einer \"UND\"-Verknüpfung Einträge mit Leerzeichen trennen, zum Nutzen einer \"ODER\"-Verknüpfung Einträge mit einem Zeilenumbruch trennen"
|
||||
notifyAntenna: "Über neue Notizen benachrichtigen"
|
||||
withFileAntenna: "Nur Notizen mit Dateien"
|
||||
excludeNotesInSensitiveChannel: "Schließe Notizen von sensitive Kanäle aus"
|
||||
enableServiceworker: "Push-Benachrichtigungen im Browser aktivieren"
|
||||
antennaUsersDescription: "Benutzernamen getrennt durch Zeilenumbrüche angeben"
|
||||
caseSensitive: "Groß-/Kleinschreibung unterscheiden"
|
||||
|
@ -491,8 +491,6 @@ noteOf: "Notiz von {user}"
|
|||
quoteAttached: "Zitat"
|
||||
quoteQuestion: "Als Zitat anhängen?"
|
||||
attachAsFileQuestion: "Der Text in der Zwischenablage ist lang. Möchtest du ihn als Textdatei anhängen?"
|
||||
noMessagesYet: "Noch keine Nachrichten vorhanden"
|
||||
newMessageExists: "Du hast eine neue Nachricht"
|
||||
onlyOneFileCanBeAttached: "Es kann pro Nachricht nur eine Datei angehängt werden"
|
||||
signinRequired: "Bitte registriere oder melde dich an, um fortzufahren"
|
||||
signinOrContinueOnRemote: "Um fortzufahren, gehe zu deiner Instanz oder registriere bzw. melde dich an dieser Instanz an. "
|
||||
|
@ -698,6 +696,7 @@ userSaysSomethingAbout: "{name} sagt etwas über '{word}'"
|
|||
makeActive: "Aktivieren"
|
||||
display: "Anzeigeart"
|
||||
copy: "Kopieren"
|
||||
copiedToClipboard: "In die Zwischenablage kopiert"
|
||||
metrics: "Metriken"
|
||||
overview: "Übersicht"
|
||||
logs: "Protokolle"
|
||||
|
@ -863,7 +862,7 @@ administration: "Verwaltung"
|
|||
accounts: "Benutzerkonten"
|
||||
switch: "Wechseln"
|
||||
noMaintainerInformationWarning: "Betreiberinformationen sind nicht konfiguriert."
|
||||
noInquiryUrlWarning: "Keine gültige URL."
|
||||
noInquiryUrlWarning: "Keine gültige Kontakt-URL."
|
||||
noBotProtectionWarning: "Schutz vor Bots ist nicht konfiguriert."
|
||||
configure: "Konfigurieren"
|
||||
postToGallery: "Neuen Galeriebeitrag erstellen"
|
||||
|
@ -964,8 +963,8 @@ cropImageAsk: "Möchtest du das Bild zuschneiden?"
|
|||
cropYes: "Zuschneiden"
|
||||
cropNo: "Unbearbeitet verwenden"
|
||||
file: "Datei"
|
||||
recentNHours: "Letzten {n} Stunden"
|
||||
recentNDays: "Letzten {n} Tage"
|
||||
recentNHours: "Letzte {n} Stunden"
|
||||
recentNDays: "Letzte {n} Tage"
|
||||
noEmailServerWarning: "Es ist kein Email-Server konfiguriert."
|
||||
thereIsUnresolvedAbuseReportWarning: "Es liegen ungelöste Meldungen vor."
|
||||
recommended: "Empfehlung"
|
||||
|
@ -973,7 +972,7 @@ check: "Check"
|
|||
driveCapOverrideLabel: "Die Drive-Kapazität dieses Nutzers verändern"
|
||||
driveCapOverrideCaption: "Gib einen Wert von 0 oder weniger ein, um die Kapazität auf den Standard zurückzusetzen."
|
||||
requireAdminForView: "Melde dich mit einem Administratorkonto an, um dies einzusehen."
|
||||
isSystemAccount: "Ein Benutzerkonto, dass durch das System erstellt und automatisch kontrolliert wird."
|
||||
isSystemAccount: "Ein Benutzerkonto, das durch das System erstellt und automatisch verwaltet wird."
|
||||
typeToConfirm: "Bitte gib zur Bestätigung {x} ein"
|
||||
deleteAccount: "Benutzerkonto löschen"
|
||||
document: "Dokumentation"
|
||||
|
@ -1144,7 +1143,7 @@ preventAiLearning: "Verwendung in machinellem Lernen (Generative bzw. Prediktive
|
|||
preventAiLearningDescription: "Fordert Crawler auf, gepostetes Text- oder Bildmaterial usw. nicht in Datensätzen für maschinelles Lernen (Generative bzw. Prediktive AI/KI) zu verwenden. Dies wird durch das Hinzufügen einer \"noai\"-Flag in der HTML-Antwort des jeweiligen Inhalts erreicht. Da diese Flag jedoch ignoriert werden kann, ist eine vollständige Verhinderung hierdurch nicht möglich."
|
||||
options: "Optionen"
|
||||
specifyUser: "Spezifischer Benutzer"
|
||||
lookupConfirm: "Zustimmen?"
|
||||
lookupConfirm: "Bist du sicher, dass du das nachschlagen möchtest?"
|
||||
openTagPageConfirm: "Hashtag Seite wirklich öffnen?"
|
||||
specifyHost: "Host"
|
||||
failedToPreviewUrl: "Vorschau nicht anzeigbar"
|
||||
|
@ -1259,7 +1258,7 @@ replaying: "Aufzeichnung"
|
|||
endReplay: "Aufzeichnung verlassen"
|
||||
copyReplayData: "Aufzeichnung kopieren"
|
||||
ranking: "Rangliste"
|
||||
lastNDays: "Letzten {n} Tage"
|
||||
lastNDays: "Letzte {n} Tage"
|
||||
backToTitle: "Zurück zum Startbildschirm"
|
||||
hemisphere: "Hemisphäre"
|
||||
withSensitive: "Zeige \"sensitive Inhalte\" an"
|
||||
|
@ -1306,17 +1305,149 @@ thisContentsAreMarkedAsSigninRequiredByAuthor: "Logge dich ein, um weitere Inhal
|
|||
lockdown: "Sperren"
|
||||
pleaseSelectAccount: "Bitte Konto auswählen"
|
||||
availableRoles: "Verfügbare Rollen"
|
||||
acknowledgeNotesAndEnable: "Schalten Sie dies erst ein, wenn Sie die Vorsichtsmaßnahmen verstanden haben."
|
||||
federationSpecified: "Dieser Server arbeitet mit Whitelist-Föderation. Er kann nicht mit anderen als den vom Administrator angegebenen Servern interagieren."
|
||||
federationDisabled: "Föderation ist auf diesem Server deaktiviert. Es ist nicht möglich, mit Benutzern auf anderen Servern zu interagieren."
|
||||
confirmOnReact: "Reagieren bestätigen"
|
||||
reactAreYouSure: "Willst du eine \"{emoji}\"-Reaktion hinzufügen?"
|
||||
markAsSensitiveConfirm: "Möchtest du dieses Medium als sensibel kennzeichnen?"
|
||||
unmarkAsSensitiveConfirm: "Möchtest du die Kennzeichnung dieses Mediums als sensibel aufheben?"
|
||||
preferences: "Einstellungen"
|
||||
accessibility: "Eingabehilfe"
|
||||
preferencesProfile: "Einstellungsprofil"
|
||||
copyPreferenceId: "Kopiere die Einstellungs-ID"
|
||||
resetToDefaultValue: "Auf Standard zurücksetzen"
|
||||
overrideByAccount: "Überschreibung durch das Konto"
|
||||
untitled: "Unbenannt"
|
||||
noName: "Kein Name"
|
||||
skip: "Überspringen"
|
||||
restore: "Wiederherstellen"
|
||||
syncBetweenDevices: "Zwischen Geräten synchronisieren"
|
||||
preferenceSyncConflictTitle: "Der konfigurierte Wert ist auf dem Server bereits vorhanden."
|
||||
preferenceSyncConflictText: "Die Einstellungen mit aktivierter Synchronisierung werden ihre Werte auf dem Server speichern. Es gibt jedoch bereits Werte auf dem Server. Welche Einstellungswerte sollen überschrieben werden?"
|
||||
preferenceSyncConflictChoiceServer: "Konfigurierte Werte auf dem Server"
|
||||
preferenceSyncConflictChoiceDevice: "Konfigurierte Werte auf dem Gerät"
|
||||
preferenceSyncConflictChoiceCancel: "Einrichten der Synchronisierung abbrechen"
|
||||
paste: "Einfügen"
|
||||
emojiPalette: "Emoji-Palette"
|
||||
postForm: "Notizfenster"
|
||||
textCount: "Zeichenanzahl"
|
||||
information: "Über"
|
||||
chat: "Chat"
|
||||
migrateOldSettings: "Alte Client-Einstellungen migrieren"
|
||||
migrateOldSettings_description: "Dies sollte normalerweise automatisch geschehen, aber wenn die Migration aus irgendeinem Grund nicht erfolgreich war, kannst du den Migrationsprozess selbst manuell auslösen. Die aktuellen Konfigurationsinformationen werden dabei überschrieben."
|
||||
compress: "Komprimieren"
|
||||
right: "Rechts"
|
||||
bottom: "Unten"
|
||||
top: "Oben"
|
||||
embed: "Einbetten"
|
||||
settingsMigrating: "Ihre Einstellungen werden gerade migriert, Bitte warten Sie einen Moment... (Sie können die Einstellungen später auch manuell migrieren, indem Sie zu Einstellungen → Sonstiges → Alte Einstellungen migrieren gehen)"
|
||||
readonly: "Nur Lesezugriff"
|
||||
goToDeck: "Zurück zum Deck"
|
||||
_chat:
|
||||
noMessagesYet: "Noch keine Nachrichten"
|
||||
newMessage: "Neue Nachricht"
|
||||
individualChat: "Privater Chat"
|
||||
individualChat_description: "Führe einen privaten Chat mit einer anderen Person."
|
||||
roomChat: "Chatraum"
|
||||
roomChat_description: "Ein Chat-Raum, an dem mehrere Personen teilnehmen können.\nDu kannst auch Personen einladen, die keine privaten Chats zulassen, wenn sie die Einladung annehmen."
|
||||
createRoom: "Raum erstellen"
|
||||
inviteUserToChat: "Lade Benutzer ein, um mit dem Chatten zu beginnen"
|
||||
yourRooms: "Erstellte Räume"
|
||||
joiningRooms: "Raum beitreten"
|
||||
invitations: "Einladen"
|
||||
noInvitations: "Keine Einladungen"
|
||||
history: "Verlauf"
|
||||
noHistory: "Kein Verlauf gefunden"
|
||||
noRooms: "Keine Räume gefunden"
|
||||
inviteUser: "Benutzer einladen"
|
||||
sentInvitations: "Verschickte Einladungen"
|
||||
join: "Beitreten"
|
||||
ignore: "Ignorieren"
|
||||
leave: "Raum verlassen"
|
||||
members: "Mitglieder"
|
||||
searchMessages: "Nachrichten suchen"
|
||||
home: "Startseite"
|
||||
send: "Senden"
|
||||
newline: "Neue Zeile"
|
||||
muteThisRoom: "Raum stummschalten"
|
||||
deleteRoom: "Raum löschen"
|
||||
chatNotAvailableForThisAccountOrServer: "Der Chat ist auf diesem Server oder für dieses Konto nicht aktiviert."
|
||||
chatIsReadOnlyForThisAccountOrServer: "Der Chat ist auf dieser Instanz oder diesem Konto nur zum Lesen freigegeben. Es ist nicht möglich, neue Nachrichten zu schreiben oder Chaträume zu erstellen oder zu betreten."
|
||||
chatNotAvailableInOtherAccount: "Die Chatfunktion wurde vom anderen Benutzer deaktiviert."
|
||||
cannotChatWithTheUser: "Starten eines Chats mit diesem Benutzer nicht möglich"
|
||||
cannotChatWithTheUser_description: "Der Chat ist entweder nicht verfügbar oder die andere Seite hat den Chat nicht aktiviert."
|
||||
chatWithThisUser: "Mit dem Benutzer chatten"
|
||||
thisUserAllowsChatOnlyFromFollowers: "Dieser Benutzer nimmt nur Chats von Followern an."
|
||||
thisUserAllowsChatOnlyFromFollowing: "Dieser Benutzer nimmt nur Chats von Benutzern an, denen er folgt."
|
||||
thisUserAllowsChatOnlyFromMutualFollowing: "Dieser Benutzer akzeptiert nur Chats von Benutzern, die sich gegenseitig folgen."
|
||||
thisUserNotAllowedChatAnyone: "Dieser Benutzer nimmt keine Chats von anderen Benutzern an."
|
||||
chatAllowedUsers: "Wem das Chatten erlaubt werden soll"
|
||||
chatAllowedUsers_note: "Du kannst unabhängig von dieser Einstellung mit allen Personen chatten, denen du eine Chat-Nachricht gesendet hast."
|
||||
_chatAllowedUsers:
|
||||
everyone: "Jeder"
|
||||
followers: "Nur deine Follower"
|
||||
following: "Nur Benutzer, denen du folgst"
|
||||
mutual: "Nur Benutzer, die sich gegenseitig folgen"
|
||||
none: "Niemand"
|
||||
_emojiPalette:
|
||||
palettes: "Palette"
|
||||
enableSyncBetweenDevicesForPalettes: "Synchronisierung der Paletten zwischen Geräten aktivieren"
|
||||
paletteForMain: "Hauptpalette"
|
||||
paletteForReaction: "Reaktions-Palette"
|
||||
_settings:
|
||||
driveBanner: "Du kannst den Drive verwalten und konfigurieren, die Auslastung überprüfen und Einstellungen für das Hochladen von Dateien vornehmen."
|
||||
pluginBanner: "Du kannst die Funktionen des Clients mit Plugins erweitern. Plugins können installiert, individuell konfiguriert und verwaltet werden."
|
||||
notificationsBanner: "Sie können die Arten und den Umfang der Benachrichtigungen vom Server und der Push- Mitteilungen konfigurieren."
|
||||
api: "API"
|
||||
webhook: "Webhook"
|
||||
serviceConnection: "Integrierte Dienste"
|
||||
serviceConnectionBanner: "Du kannst Zugriffstoken und Webhooks für die Integration mit externen Anwendungen und Diensten verwalten und konfigurieren."
|
||||
accountData: "Kontodaten"
|
||||
accountDataBanner: "Export/Import und Verwaltung von Kontodatenarchiven."
|
||||
muteAndBlockBanner: "Du kannst Einstellungen konfigurieren und verwalten, um Inhalte auszublenden und Aktionen für bestimmte Benutzer zu beschränken."
|
||||
accessibilityBanner: "Die Clients können personalisiert und für eine optimale Nutzung im Hinblick auf ihre Darstellung und ihr Verhalten eingerichtet werden."
|
||||
privacyBanner: "Du kannst Einstellungen für die Privatsphäre deines Kontos vornehmen, z. B. inwieweit Inhalte veröffentlicht werden, wie leicht sie zu finden sind und ob Follower genehmigt werden müssen."
|
||||
securityBanner: "Du kannst Einstellungen für die Kontosicherheit konfigurieren, z. B. Passwörter, Anmeldemethoden, Authentifizierungs-Apps und Passkeys."
|
||||
preferencesBanner: "Sie können das Gesamtverhalten des Clients nach Ihren Wünschen konfigurieren."
|
||||
appearanceBanner: "Du kannst das Erscheinungsbild und die Anzeigeeinstellungen für den Client nach deinen Wünschen konfigurieren."
|
||||
soundsBanner: "Du kannst die Einstellungen für die Wiedergabe von Klängen im Client konfigurieren."
|
||||
timelineAndNote: "Chroniken und Notizen"
|
||||
makeEveryTextElementsSelectable: "Alle Textelemente auswählbar machen"
|
||||
makeEveryTextElementsSelectable_description: "Die Aktivierung kann in manchen Situationen die Benutzerfreundlichkeit beeinträchtigen."
|
||||
useStickyIcons: "Icons beim Scrollen folgen lassen"
|
||||
showNavbarSubButtons: "Unterschaltflächen in der Navigationsleiste anzeigen"
|
||||
ifOn: "Wenn eingeschaltet"
|
||||
ifOff: "Wenn ausgeschaltet"
|
||||
enableSyncThemesBetweenDevices: "Synchronisierung von installierten Themen auf verschiedenen Endgeräten"
|
||||
_chat:
|
||||
showSenderName: "Name des Absenders anzeigen"
|
||||
sendOnEnter: "Eingabetaste sendet Nachricht"
|
||||
_preferencesProfile:
|
||||
profileName: "Profilname"
|
||||
profileNameDescription: "Lege einen Namen fest, der dieses Gerät identifiziert."
|
||||
profileNameDescription2: "Beispiel: \"Haupt-PC\", \"Smartphone\""
|
||||
_preferencesBackup:
|
||||
autoBackup: "Automatische Sicherung"
|
||||
restoreFromBackup: "Wiederherstellen aus der Sicherung"
|
||||
noBackupsFoundTitle: "Keine Sicherungen gefunden"
|
||||
noBackupsFoundDescription: "Es wurden keine automatisch erstellten Sicherungen gefunden, aber wenn du eine Sicherungsdatei manuell gespeichert hast, kannst du diese importieren und wiederherstellen."
|
||||
selectBackupToRestore: "Wähle die wiederherzustellende Sicherung"
|
||||
youNeedToNameYourProfileToEnableAutoBackup: "Um die automatische Sicherung zu aktivieren, müssen Profilnamen festgelegt werden."
|
||||
autoPreferencesBackupIsNotEnabledForThisDevice: "Die automatische Sicherung der Einstellungen ist auf diesem Gerät nicht aktiviert."
|
||||
backupFound: "Konfigurationssicherung gefunden."
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "Anmeldung erfordern, um Inhalte anzuzeigen"
|
||||
requireSigninToViewContentsDescription1: "Erfordere eine Anmeldung, um alle Notizen und andere Inhalte anzuzeigen, die du erstellt hast. Dadurch wird verhindert, dass Crawler deine Informationen sammeln."
|
||||
requireSigninToViewContentsDescription2: "Der Inhalt wird nicht in URL-Vorschauen (OGP), eingebettet in Webseiten oder auf Servern, die keine Zitate unterstützen, angezeigt."
|
||||
requireSigninToViewContentsDescription3: "Diese Einschränkungen gelten möglicherweise nicht für föderierte Inhalte von anderen Servern."
|
||||
makeNotesFollowersOnlyBefore: "Macht frühere Notizen nur für Follower sichtbar"
|
||||
makeNotesFollowersOnlyBeforeDescription: "Solange diese Funktion aktiviert ist, sind Notizen, die nach dem eingestellten Datum und der eingestellten Zeit liegen oder die eingestellte Zeit abgelaufen ist, nur für Follower sichtbar. Bei Deaktivierung wird auch der öffentliche Status der Notiz wiederhergestellt."
|
||||
makeNotesHiddenBefore: "Frühere Notizen privat machen"
|
||||
makeNotesHiddenBeforeDescription: ""
|
||||
mayNotEffectForFederatedNotes: "Dies hat möglicherweise keine Auswirkungen auf Notizen, die an andere Server föderiert werden."
|
||||
mayNotEffectSomeSituations: "Diese Einschränkungen sind vereinfacht. Sie gelten möglicherweise nicht in allen Situationen, z. B. bei der Anzeige auf einem fremden Server oder während der Moderation."
|
||||
notesHavePassedSpecifiedPeriod: "Notizen die nach der folgenden Zeit veröffentlicht worden"
|
||||
notesOlderThanSpecifiedDateAndTime: "Notizen vor einem bestimmtem Datum und Uhrzeit"
|
||||
_abuseUserReport:
|
||||
forward: "Weiterleiten"
|
||||
|
@ -1324,11 +1455,16 @@ _abuseUserReport:
|
|||
resolve: "lösen"
|
||||
accept: "Akzeptieren"
|
||||
reject: "Ablehnen"
|
||||
resolveTutorial: "Wenn der Inhalt der Meldung rechtmäßig ist, wähle „Akzeptieren“, um sie als gelöst zu markieren.\nWenn der Inhalt der Meldung unzulässig ist, wähle „Ablehnen“, um sie zu ignorieren."
|
||||
_delivery:
|
||||
status: "Auslieferungsstatus"
|
||||
stop: "Gesperrt"
|
||||
resume: "Zustellung wieder fortsetzen"
|
||||
_type:
|
||||
none: "Wird veröffentlicht"
|
||||
manuallySuspended: "Manuell gesperrt"
|
||||
goneSuspended: "Gesperrt wegen Löschung des Servers"
|
||||
autoSuspendedForNotResponding: "Gesperrt, weil der Server nicht antwortet"
|
||||
_bubbleGame:
|
||||
howToPlay: "Wie man spielt"
|
||||
hold: "Halten"
|
||||
|
@ -1338,6 +1474,8 @@ _bubbleGame:
|
|||
highScore: "Höchstpunktzahl"
|
||||
maxChain: "Maximale Anzahl an Verkettungen"
|
||||
yen: "{yen} Yen"
|
||||
estimatedQty: "{qty} Stück"
|
||||
scoreSweets: "{onigiriQtyWithUnit} Onigiri"
|
||||
_howToPlay:
|
||||
section1: "Passe die Position an und lasse das Objekt in das Spielfeld fallen."
|
||||
section2: "Wenn sich zwei Objekte der gleichen Art berühren, verwandeln sie sich in ein anderes Objekt und du bekommst Punkte."
|
||||
|
@ -1395,15 +1533,21 @@ _initialTutorial:
|
|||
reactDone: "Du kannst eine Reaktion zurücknehmen, indem du auf den '-' Button drückst."
|
||||
_timeline:
|
||||
title: "So funktionieren die Chroniken"
|
||||
description1: "Misskey stellt mehrere Chroniken bereit (einige können je nach den Richtlinien des Servers nicht verfügbar sein)."
|
||||
home: "Du kannst Beiträge von den Konten sehen, denen du folgst."
|
||||
local: "Du kannst Beiträge aller Benutzer auf diesem Server sehen."
|
||||
social: "Notizen von der Startseite und der lokalen Chronik werden angezeigt."
|
||||
global: "Du kannst Notizen von allen föderierten Servern sehen."
|
||||
description2: "Du kannst jederzeit am oberen Rand des Bildschirms zwischen den jeweiligen Chroniken wechseln."
|
||||
description3: "Darüber hinaus gibt es Listen-Chroniken und Kanal-Chroniken. Weitere Einzelheiten findest du unter {link}."
|
||||
_postNote:
|
||||
title: "Optionen bei Abschicken einer Notiz"
|
||||
description1: "Wenn du eine Notiz auf Misskey veröffentlichst, stehen dir verschiedene Optionen zur Verfügung. Die Oberfläche sieht folgendermaßen aus."
|
||||
_visibility:
|
||||
description: "Du kannst einschränken, wer deine Notiz sehen kann."
|
||||
public: "Deine Notiz wird für alle Nutzer sichtbar sein."
|
||||
home: "Nur auf der Startseite sichtbar. Kann von Followern, Profilbesuchern und durch Renotes gesehen werden."
|
||||
followers: "Nur für Follower sichtbar. Nur Follower können es sehen und niemand sonst, und es kann nicht von anderen gerenoted werden."
|
||||
direct: "Die Notiz wird nur für den angegebenen Benutzer veröffentlicht und der Empfänger wird benachrichtigt. Kann anstelle von Direktnachrichten verwendet werden."
|
||||
doNotSendConfidencialOnDirect1: "Sei vorsichtig, wenn du sensible Informationen verschickst!"
|
||||
doNotSendConfidencialOnDirect2: "Die Administratoren des Servers können den Inhalt der Notiz sehen. Sei vorsichtig mit sensiblen Informationen, wenn du Direktnachrichten an Benutzer auf nicht vertrauenswürdigen Servern sendest."
|
||||
|
@ -1414,8 +1558,10 @@ _initialTutorial:
|
|||
_exampleNote:
|
||||
cw: "Das wird dich bestimmt hungrig machen!"
|
||||
note: "Ich hatte gerade einen Donut mit Schokoladenüberzug 🍩😋"
|
||||
useCases: "Dient zur Kennzeichnung von Notizen, wie sie in den Serverrichtlinien vorgeschrieben sind, oder zur eigenen Festlegung von Spoiler-Beiträgen oder sensiblem Text."
|
||||
_howToMakeAttachmentsSensitive:
|
||||
title: "Wie markiert man Anhänge als sensibel?"
|
||||
description: "Markiere Anhänge als sensibel, die aufgrund von den Serverregeln nicht sichtbar sein sollen."
|
||||
tryThisFile: "Versuche, das angehängte Bild als sensibel zu markieren!"
|
||||
_exampleNote:
|
||||
note: "Ups, ich habe es vergeigt, den Natto-Deckel zu öffnen..."
|
||||
|
@ -1426,7 +1572,9 @@ _initialTutorial:
|
|||
title: "Du hast das Tutorial abgeschlossen! 🎉"
|
||||
description: "Die hier beschriebenen Funktionen sind nur ein kleiner Teil dessen, was Misskey zu bieten hat; um mehr darüber zu erfahren, wie du Misskey benutzen kannst, besuche bitte {link}."
|
||||
_timelineDescription:
|
||||
home: "In der Startseiten-Chronik kannst du Notizen von Konten sehen, denen du folgst."
|
||||
local: "In der lokalen Chronik siehst du Notizen von allen Benutzern auf diesem Server."
|
||||
social: "Die soziale Chronik zeigt Notizen von der Startseite und der lokalen Chronik."
|
||||
global: "In der globalen Chronik siehst du Notizen von allen föderierten Servern."
|
||||
_serverRules:
|
||||
description: "Eine Reihe von Regeln, die vor der Registrierung angezeigt werden. Eine Zusammenfassung der Nutzungsbedingungen anzuzeigen ist empfohlen."
|
||||
|
@ -1443,6 +1591,9 @@ _serverSettings:
|
|||
fanoutTimelineDbFallback: "Auf die Datenbank zurückfallen"
|
||||
fanoutTimelineDbFallbackDescription: "Ist diese Option aktiviert, wird die Chronik auf zusätzliche Abfragen in der Datenbank zurückgreifen, wenn sich die Chronik nicht im Cache befindet. Eine Deaktivierung führt zu geringerer Serverlast, aber schränkt den Zeitraum der abrufbaren Chronik ein. "
|
||||
reactionsBufferingDescription: "Wenn diese Option aktiviert ist, kann sie die Leistung beim Erstellen von Reaktionen erheblich verbessern und die Belastung der Datenbank verringern. Allerdings steigt die Speichernutzung von Redis."
|
||||
inquiryUrl: "Kontakt-URL"
|
||||
inquiryUrlDescription: "Gib eine URL für das Kontaktformular der Serverbetreiber oder eine Webseite an, die Kontaktinformationen enthält."
|
||||
openRegistration: "Registrierung von Konten aktivieren"
|
||||
openRegistrationWarning: "Das Aktivieren von Registrierungen ist riskant. Es wird empfohlen, sie nur dann zu aktivieren, wenn der Server ständig überwacht wird und im Falle eines Problems sofort reagiert werden kann."
|
||||
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "Wenn über einen bestimmten Zeitraum keine Moderatorenaktivität festgestellt wird, wird diese Einstellung automatisch deaktiviert, um Spam zu verhindern."
|
||||
_accountMigration:
|
||||
|
@ -1707,8 +1858,11 @@ _achievements:
|
|||
description: "Tutorial abgeschlossen"
|
||||
_bubbleGameExplodingHead:
|
||||
title: "🤯"
|
||||
description: "Das größte Objekt im Bubble Game"
|
||||
_bubbleGameDoubleExplodingHead:
|
||||
title: "Doppel🤯"
|
||||
description: "Zwei der größten Objekte im Bubble Game zur gleichen Zeit"
|
||||
flavor: "Eine Lunchbox kann man auch mit etwas mehr 🤯 🤯 füllen"
|
||||
_role:
|
||||
new: "Rolle erstellen"
|
||||
edit: "Rolle bearbeiten"
|
||||
|
@ -1738,6 +1892,8 @@ _role:
|
|||
descriptionOfIsExplorable: "Ist dies aktiviert, so ist die Chronik dieser Rolle, sowie eine Liste der Benutzer mit dieser Rolle, frei zugänglich."
|
||||
displayOrder: "Position"
|
||||
descriptionOfDisplayOrder: "Je höher die Nummer, desto höher die UI-Position."
|
||||
preserveAssignmentOnMoveAccount: "Rolle übertragbar machen"
|
||||
preserveAssignmentOnMoveAccount_description: "Wenn diese Option aktiviert ist, wird diese Rolle bei der Migration mit übertragen."
|
||||
canEditMembersByModerator: "Moderatoren können Benutzern diese Rolle zuweisen"
|
||||
descriptionOfCanEditMembersByModerator: "Wenn aktiviert, so können Moderatoren und Adminstratoren anderen Benutzern diese Rolle zuweisen bzw. diese Zuweisung aufheben. Wenn deaktiviert, so ist es nur Administratoren möglich, Zuweisungen dieser Rolle zu verwalten."
|
||||
priority: "Priorität"
|
||||
|
@ -1758,6 +1914,7 @@ _role:
|
|||
canManageAvatarDecorations: "Profilbilddekorationen verwalten"
|
||||
driveCapacity: "Drive-Kapazität"
|
||||
alwaysMarkNsfw: "Dateien immer als NSFW markieren"
|
||||
canUpdateBioMedia: "Kann ein Profil- oder ein Bannerbild bearbeiten"
|
||||
pinMax: "Maximale Anzahl an angehefteten Notizen"
|
||||
antennaMax: "Maximale Anzahl an Antennen"
|
||||
wordMuteMax: "Maximale Zeichenlänge für Wortstummschaltungen"
|
||||
|
@ -1773,11 +1930,20 @@ _role:
|
|||
canUseTranslator: "Verwendung des Übersetzers"
|
||||
avatarDecorationLimit: "Maximale Anzahl an Profilbilddekorationen, die angebracht werden können"
|
||||
canImportAntennas: "Importieren von Antennen erlauben"
|
||||
canImportBlocking: "Importieren von Blockierungen zulassen"
|
||||
canImportFollowing: "Importieren von Gefolgten zulassen"
|
||||
canImportMuting: "Importieren von Stummgeschalteten zulassen"
|
||||
canImportUserLists: "Importieren von Listen erlauben"
|
||||
chatAvailability: "Chatten erlauben"
|
||||
_condition:
|
||||
roleAssignedTo: "Manuellen Rollen zugewiesen"
|
||||
isLocal: "Lokaler Benutzer"
|
||||
isRemote: "Benutzer fremder Instanz"
|
||||
isCat: "Katzen-Benutzer"
|
||||
isBot: "Bot-Benutzer"
|
||||
isSuspended: "Gesperrter Benutzer"
|
||||
isLocked: "Private Konten"
|
||||
isExplorable: "Benutzer, die ihr Konto im \"Erkunden\"-Bereich sichtbar machen"
|
||||
createdLessThan: "Kontoerstellung liegt weniger als X zurück"
|
||||
createdMoreThan: "Kontoerstellung liegt mehr als X zurück"
|
||||
followersLessThanOrEq: "Hat X oder weniger Follower"
|
||||
|
@ -1932,6 +2098,7 @@ _theme:
|
|||
installed: "{name} wurde installiert"
|
||||
installedThemes: "Installierte Farbschemata"
|
||||
builtinThemes: "Eingebaute Farbschemata"
|
||||
instanceTheme: "Server-Thema"
|
||||
alreadyInstalled: "Dieses Farbschema ist bereits installiert"
|
||||
invalid: "Der Code dieses Farbschemas ist ungültig"
|
||||
make: "Farbschema erstellen"
|
||||
|
@ -1964,7 +2131,6 @@ _theme:
|
|||
header: "Kopfzeile"
|
||||
navBg: "Hintergrund der Seitenleiste"
|
||||
navFg: "Text der Seitenleiste"
|
||||
navHoverFg: "Text der Seitenleiste (Mouseover)"
|
||||
navActive: "Text der Seitenleiste (Aktiv)"
|
||||
navIndicator: "Indikator der Seitenleiste"
|
||||
link: "Link"
|
||||
|
@ -1987,17 +2153,15 @@ _theme:
|
|||
buttonHoverBg: "Hintergrund von Schaltflächen (Mouseover)"
|
||||
inputBorder: "Rahmen von Eingabefeldern"
|
||||
driveFolderBg: "Hintergrund von Drive-Ordnern"
|
||||
wallpaperOverlay: "Hintergrundbild-Overlay"
|
||||
badge: "Wappen"
|
||||
messageBg: "Hintergrund von Chats"
|
||||
accentDarken: "Akzent (Verdunkelt)"
|
||||
accentLighten: "Akzent (Erhellt)"
|
||||
fgHighlighted: "Hervorgehobener Text"
|
||||
_sfx:
|
||||
note: "Notizen"
|
||||
noteMy: "Meine Notizen"
|
||||
notification: "Benachrichtigungen"
|
||||
reaction: "Auswählen einer Reaktion"
|
||||
chatMessage: "Chat-Nachrichten"
|
||||
_soundSettings:
|
||||
driveFile: "Audiodatei aus dem Drive verwenden"
|
||||
driveFileWarn: "Wähle eine Audiodatei aus dem Drive"
|
||||
|
@ -2021,6 +2185,10 @@ _timeIn:
|
|||
seconds: "In {n}s"
|
||||
minutes: "In {n} Min."
|
||||
hours: "In {n} Std."
|
||||
days: "In {n} Tagen"
|
||||
weeks: "In {n} Wochen"
|
||||
months: "In {n} Monaten"
|
||||
years: "In {n} Jahren"
|
||||
_time:
|
||||
second: "Sekunde(n)"
|
||||
minute: "Minute(n)"
|
||||
|
@ -2054,6 +2222,7 @@ _2fa:
|
|||
backupCodesDescription: "Verwende diese Codes, falls du nicht mehr auf deine App zur Zweifaktorauthentifizierung zugreifen kannst. Jeder Code kann nur einmal verwendet werden. Bewahre sie an einem sicheren Ort auf."
|
||||
backupCodeUsedWarning: "Ein Backup-Code wurde verwendet. Falls du den Zugriff zu deiner Zweifaktorauthentifizierungsapp verloren hast, konfiguriere diese bitte möglichst bald erneut."
|
||||
backupCodesExhaustedWarning: "Alle Backup-Codes wurden verwendet. Falls du den Zugang zu deiner Zweifaktorauthentifizierungsapp verlierst, wirst du dich nicht mehr in dieses Konto einloggen können. Bitte konfiguriere diese App erneut."
|
||||
moreDetailedGuideHere: "Hier ist eine ausführliche Anleitung"
|
||||
_permissions:
|
||||
"read:account": "Deine Benutzerkontoinformationen lesen"
|
||||
"write:account": "Deine Benutzerkontoinformationen bearbeiten"
|
||||
|
@ -2091,6 +2260,7 @@ _permissions:
|
|||
"write:flash": "Deine Plays bearbeiten oder löschen"
|
||||
"read:flash-likes": "Liste der Plays, die mir gefallen, lesen"
|
||||
"write:flash-likes": "Liste der Plays, die mir gefallen, bearbeiten"
|
||||
"read:admin:abuse-user-reports": "Meldungen von Benutzern ansehen"
|
||||
"write:admin:delete-account": "Benutzerkonto löschen"
|
||||
"write:admin:delete-all-files-of-a-user": "Alle Dateien eines Benutzers löschen"
|
||||
"read:admin:index-stats": "Statistiken zu Datenbankindizes einsehen"
|
||||
|
@ -2098,10 +2268,17 @@ _permissions:
|
|||
"read:admin:user-ips": "IP-Adressen von Benutzern anzeigen"
|
||||
"read:admin:meta": "Metadaten der Instanz einsehen"
|
||||
"write:admin:reset-password": "Benutzerpasswort zurücksetzen"
|
||||
"write:admin:resolve-abuse-user-report": "Meldungen von Benutzern lösen"
|
||||
"write:admin:send-email": "E-Mail versenden"
|
||||
"read:admin:server-info": "Serverinformationen anzeigen"
|
||||
"read:admin:show-moderation-log": "Moderationsprotokoll einsehen"
|
||||
"read:admin:show-user": "Private Benutzerinformationen einsehen"
|
||||
"write:admin:suspend-user": "Benutzer sperren"
|
||||
"write:admin:unset-user-avatar": "Benutzer-Profilbild entfernen"
|
||||
"write:admin:unset-user-banner": "Benutzer-Banner entfernen"
|
||||
"write:admin:unsuspend-user": "Benutzer entsperren"
|
||||
"write:admin:meta": "Metadaten der Instanz verwalten"
|
||||
"write:admin:user-note": "Moderationsvermerke verwalten"
|
||||
"write:admin:roles": "Rollen verwalten"
|
||||
"read:admin:roles": "Rollen anzeigen"
|
||||
"write:admin:relays": "Relays verwalten"
|
||||
|
@ -2112,12 +2289,27 @@ _permissions:
|
|||
"read:admin:announcements": "Ankündigungen einsehen"
|
||||
"write:admin:avatar-decorations": "Kann Avatar-Dekorationen verwalten"
|
||||
"read:admin:avatar-decorations": "Avatar-Dekorationen ansehen"
|
||||
"write:admin:federation": "Informationen über Föderationen bearbeiten oder löschen"
|
||||
"write:admin:account": "Benutzerkonten verwalten"
|
||||
"read:admin:account": "Benutzerkonten anzeigen"
|
||||
"write:admin:emoji": "Emojis verwalten"
|
||||
"read:admin:emoji": "Emojis anzeigen"
|
||||
"write:admin:queue": "Job-Warteschlange verwalten"
|
||||
"read:admin:queue": "Job-Warteschlange anzeigen"
|
||||
"write:admin:promo": "Moderationsnotiz hinzufügen"
|
||||
"write:admin:drive": "Benutzer-Drive verwalten"
|
||||
"read:admin:drive": "Benutzer-Drive ansehen"
|
||||
"read:admin:stream": "Verwendung der Websocket-API für Administratoren"
|
||||
"write:admin:ad": "Werbung verwalten"
|
||||
"read:admin:ad": "Werbung ansehen"
|
||||
"write:invite-codes": "Einladungscodes erstellen"
|
||||
"read:invite-codes": "Einladungscodes anzeigen"
|
||||
"write:clip-favorite": "Clip-Likes bearbeiten oder löschen"
|
||||
"read:clip-favorite": "Clip-Likes ansehen"
|
||||
"read:federation": "Informationen zur Föderation einsehen"
|
||||
"write:report-abuse": "Verstöße melden"
|
||||
"write:chat": "Chats bedienen"
|
||||
"read:chat": "Chats durchsuchen"
|
||||
_auth:
|
||||
shareAccessTitle: "Verteilung von App-Berechtigungen"
|
||||
shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?"
|
||||
|
@ -2128,7 +2320,9 @@ _auth:
|
|||
callback: "Es wird zur Anwendung zurückgekehrt"
|
||||
accepted: "Zugriff gewährt"
|
||||
denied: "Zugriff verweigert"
|
||||
scopeUser: "Als folgender Benutzer agieren"
|
||||
pleaseLogin: "Bitte logge dich ein, um Apps zu authorisieren."
|
||||
byClickingYouWillBeRedirectedToThisUrl: "Wenn der Zugang gewährt wird, wirst du automatisch zu folgender URL weitergeleitet"
|
||||
_antennaSources:
|
||||
all: "Alle Notizen"
|
||||
homeTimeline: "Notizen von Benutzern, denen gefolgt wird"
|
||||
|
@ -2239,6 +2433,7 @@ _profile:
|
|||
avatarDecorationMax: "Du kannst bis zu {max} Dekorationen hinzufügen."
|
||||
followedMessage: "Nachricht, wenn dir jemand folgt"
|
||||
followedMessageDescription: "Du kannst eine kurze Nachricht festlegen, die dem Empfänger angezeigt wird, wenn er dir folgt."
|
||||
followedMessageDescriptionForLockedAccount: "Wenn Folgeanfragen deine Genehmigung brauchen, wird dies beim Genehmigen einer Anfrage angezeigt."
|
||||
_exportOrImport:
|
||||
allNotes: "Alle Notizen"
|
||||
favoritedNotes: "Als Favorit markierte Notizen"
|
||||
|
@ -2296,6 +2491,7 @@ _play:
|
|||
title: "Titel"
|
||||
script: "Skript"
|
||||
summary: "Beschreibung"
|
||||
visibilityDescription: "Wenn du die Sichtbarkeit auf Privat stellst, wird der Play nicht auf deinem Profil sichtbar sein, aber jeder, der die URL hat, kann ihn trotzdem aufrufen."
|
||||
_pages:
|
||||
newPage: "Seite erstellen"
|
||||
editPage: "Seite bearbeiten"
|
||||
|
@ -2327,6 +2523,7 @@ _pages:
|
|||
eyeCatchingImageSet: "Vorschaubild festlegen"
|
||||
eyeCatchingImageRemove: "Vorschaubild entfernen"
|
||||
chooseBlock: "Block hinzufügen"
|
||||
enterSectionTitle: "Titel des Abschnitts eingeben"
|
||||
selectType: "Typ auswählen"
|
||||
contentBlocks: "Inhalt"
|
||||
inputBlocks: "Eingabe"
|
||||
|
@ -2337,6 +2534,8 @@ _pages:
|
|||
section: "Abschnitt"
|
||||
image: "Bild"
|
||||
button: "Knopf"
|
||||
dynamic: "Dynamische Bausteine"
|
||||
dynamicDescription: "Dieser Baustein wurde abgeschafft. Bitte verwende von nun an {play}."
|
||||
note: "Eingebettete Notiz"
|
||||
_note:
|
||||
id: "Notiz-ID"
|
||||
|
@ -2359,6 +2558,7 @@ _notification:
|
|||
newNote: "Neue Notiz"
|
||||
unreadAntennaNote: "Antenne {name}"
|
||||
roleAssigned: "Rolle zugewiesen"
|
||||
chatRoomInvitationReceived: "Du wurdest in einen Chatraum eingeladen"
|
||||
emptyPushNotificationMessage: "Push-Benachrichtigungen wurden aktualisiert"
|
||||
achievementEarned: "Errungenschaft freigeschaltet"
|
||||
testNotification: "Testbenachrichtigung"
|
||||
|
@ -2372,6 +2572,8 @@ _notification:
|
|||
flushNotification: "Benachrichtigungen löschen"
|
||||
exportOfXCompleted: "Der Export von {x} ist abgeschlossen"
|
||||
login: "Neue Anmeldung erfolgt"
|
||||
createToken: "Ein Zugangstoken wurde erstellt"
|
||||
createTokenDescription: "Wenn Sie keine Ahnung haben, löschen Sie das Zugriffstoken über \"{text}\""
|
||||
_types:
|
||||
all: "Alle"
|
||||
note: "Neue Notizen"
|
||||
|
@ -2385,9 +2587,11 @@ _notification:
|
|||
receiveFollowRequest: "Erhaltene Follow-Anfragen"
|
||||
followRequestAccepted: "Akzeptierte Follow-Anfragen"
|
||||
roleAssigned: "Rolle zugewiesen"
|
||||
chatRoomInvitationReceived: "Einladungen zum Chatraum"
|
||||
achievementEarned: "Errungenschaft freigeschaltet"
|
||||
exportCompleted: "Der Export ist abgeschlossen"
|
||||
login: "Anmeldung"
|
||||
createToken: "Erstellung von Zugriffstokens"
|
||||
test: "Test-Benachrichtigungen"
|
||||
app: "Benachrichtigungen von Apps"
|
||||
_actions:
|
||||
|
@ -2397,6 +2601,9 @@ _notification:
|
|||
_deck:
|
||||
alwaysShowMainColumn: "Hauptspalte immer zeigen"
|
||||
columnAlign: "Spaltenausrichtung"
|
||||
columnGap: "Spaltenabstand"
|
||||
deckMenuPosition: "Position des Deck-Menüs"
|
||||
navbarPosition: "Position der Navigationsleiste"
|
||||
addColumn: "Spalte hinzufügen"
|
||||
newNoteNotificationSettings: "Benachrichtigungseinstellungen für neue Notizen"
|
||||
configureColumn: "Spalteneinstellungen"
|
||||
|
@ -2415,6 +2622,7 @@ _deck:
|
|||
useSimpleUiForNonRootPages: "Simple Benutzeroberfläche für navigierte Seiten verwenden"
|
||||
usedAsMinWidthWhenFlexible: "Ist \"Automatische Breitenanpassung\" aktiviert, wird hierfür die minimale Breite verwendet"
|
||||
flexible: "Automatische Breitenanpassung"
|
||||
enableSyncBetweenDevicesForProfiles: "Aktivieren der Synchronisierung von Profilinformationen zwischen Geräten"
|
||||
_columns:
|
||||
main: "Hauptspalte"
|
||||
widgets: "Widgets"
|
||||
|
@ -2450,7 +2658,14 @@ _webhookSettings:
|
|||
renote: "Wenn du ein Renote erhältst"
|
||||
reaction: "Wenn du eine Reaktion erhältst"
|
||||
mention: "Wenn du erwähnt wirst"
|
||||
_systemEvents:
|
||||
abuseReport: "Wenn eine neue Meldung eingeht"
|
||||
abuseReportResolved: "Wenn eine Meldung gelöst wird"
|
||||
userCreated: "Beim Anlegen eines Benutzers"
|
||||
inactiveModeratorsWarning: "Wenn Moderatoren für eine gewisse Zeit inaktiv sind"
|
||||
inactiveModeratorsInvitationOnlyChanged: "Wenn ein Moderator über einen gewissen Zeitraum inaktiv war und der Server auf Einladungsbasis umgestellt wird"
|
||||
deleteConfirm: "Bist du sicher, dass du den Webhook löschen willst?"
|
||||
testRemarks: "Klicke auf die Schaltfläche rechts neben dem Schalter, um einen Test-Webhook mit Dummy-Daten zu senden."
|
||||
_abuseReport:
|
||||
_notificationRecipient:
|
||||
createRecipient: "Meldungsempfänger hinzufügen"
|
||||
|
@ -2490,9 +2705,12 @@ _moderationLogTypes:
|
|||
resetPassword: "Passwort zurückgesetzt"
|
||||
suspendRemoteInstance: "Fremde Instanz gesperrt"
|
||||
unsuspendRemoteInstance: "Fremde Instanz entsperrt"
|
||||
updateRemoteInstanceNote: "Aktualisierung der Moderationshinweise für fremde Server."
|
||||
markSensitiveDriveFile: "Datei als sensitiv markiert"
|
||||
unmarkSensitiveDriveFile: "Datei als nicht sensitiv markiert"
|
||||
resolveAbuseReport: "Meldung bearbeitet"
|
||||
forwardAbuseReport: "Meldung weitergeleitet"
|
||||
updateAbuseReportNote: "Moderationsnotiz einer Meldung aktualisiert"
|
||||
createInvitation: "Einladung erstellt"
|
||||
createAd: "Werbung erstellt"
|
||||
deleteAd: "Werbung gelöscht"
|
||||
|
@ -2505,9 +2723,15 @@ _moderationLogTypes:
|
|||
createSystemWebhook: "System-Webhook erstellt"
|
||||
updateSystemWebhook: "System-Webhook aktualisiert"
|
||||
deleteSystemWebhook: "System-Webhook gelöscht"
|
||||
createAbuseReportNotificationRecipient: "Empfänger für Meldungen erstellt"
|
||||
updateAbuseReportNotificationRecipient: "Empfänger für Meldungen aktualisiert"
|
||||
deleteAbuseReportNotificationRecipient: "Empfänger für Meldungen entfernt"
|
||||
deleteAccount: "Benutzerkonto gelöscht"
|
||||
deletePage: "Seite gelöscht"
|
||||
deleteFlash: "Play gelöscht"
|
||||
deleteGalleryPost: "Galeriebeitrag gelöscht"
|
||||
deleteChatRoom: "Chatraum gelöscht"
|
||||
updateProxyAccountDescription: "Beschreibung des Proxy-Benutzerkontos aktualisiert"
|
||||
_fileViewer:
|
||||
title: "Dateiinformationen"
|
||||
type: "Dateityp"
|
||||
|
@ -2521,10 +2745,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "Überprüfe vor Installation die Vertrauenswürdigkeit des Vertreibers."
|
||||
_plugin:
|
||||
title: "Möchtest du dieses Plugin installieren?"
|
||||
metaTitle: "Plugininformation"
|
||||
_theme:
|
||||
title: "Möchten du dieses Farbschema installieren?"
|
||||
metaTitle: "Farbschemainfo"
|
||||
_meta:
|
||||
base: "Farbschemavorlage"
|
||||
_vendorInfo:
|
||||
|
@ -2557,16 +2779,67 @@ _externalResourceInstaller:
|
|||
_themeInstallFailed:
|
||||
title: "Das Farbschema konnte nicht installiert werden"
|
||||
description: "Während der Installation des Farbschemas ist ein Problem aufgetreten. Bitte versuche es erneut. Detaillierte Fehlerinformationen können über die Javascript-Konsole abgerufen werden."
|
||||
_dataSaver:
|
||||
_media:
|
||||
title: "Laden von Medien verhindern"
|
||||
description: "Verhindert, dass Bilder/Videos automatisch geladen werden. Ausgeblendete Bilder/Videos werden geladen, wenn du auf sie tippst."
|
||||
_avatar:
|
||||
title: "Animierte Profilbilder deaktivieren"
|
||||
description: "Die Animation von Profilbildern wird angehalten. Da animierte Bilder eine größere Dateigröße haben können als normale Bilder, kann dies den Datenverkehr weiter reduzieren."
|
||||
_urlPreview:
|
||||
title: "URL-Vorschaubilder ausblenden"
|
||||
description: "URL-Vorschaubilder werden nicht mehr geladen."
|
||||
_code:
|
||||
title: "Code-Hervorhebungen ausblenden"
|
||||
description: "Wenn Code-Hervorhebungen in MFM usw. verwendet werden, werden sie erst geladen, wenn sie angetippt werden. Die Syntaxhervorhebung erfordert das Herunterladen der Definitionsdateien für jede Programmiersprache. Es ist daher zu erwarten, dass die Deaktivierung des automatischen Ladens dieser Dateien die Menge des Datenverkehrs reduziert."
|
||||
_hemisphere:
|
||||
N: "Nördliche Erdhalbkugel"
|
||||
S: "Südliche Erdhalbkugel"
|
||||
caption: "Wird in einigen Client-Einstellungen zur Bestimmung der Jahreszeit verwendet."
|
||||
_reversi:
|
||||
reversi: "Reversi"
|
||||
gameSettings: "Spieleinstellungen"
|
||||
chooseBoard: "Spielbrett auswählen"
|
||||
blackOrWhite: "Schwarz/Weiß"
|
||||
blackIs: "{name} spielt Schwarz"
|
||||
rules: "Regeln"
|
||||
thisGameIsStartedSoon: "Das Spiel wird in Kürze beginnen"
|
||||
waitingForOther: "Warte auf den Zug des Gegenspielers"
|
||||
waitingForMe: "Warte auf deinen Zug"
|
||||
waitingBoth: "Mach dich bereit"
|
||||
ready: "Bereit"
|
||||
cancelReady: "Nicht bereit"
|
||||
opponentTurn: "Dein Gegner ist an der Reihe"
|
||||
myTurn: "Du bist am Zug"
|
||||
turnOf: "{name} ist am Zug"
|
||||
pastTurnOf: "Zug von {name}"
|
||||
surrender: "Aufgeben"
|
||||
surrendered: "Aufgegeben"
|
||||
timeout: "Zeit abgelaufen"
|
||||
drawn: "Unentschieden"
|
||||
won: "{name} hat gewonnen"
|
||||
black: "Schwarz"
|
||||
white: "Weiß"
|
||||
total: "Gesamt"
|
||||
turnCount: " Zug {count}"
|
||||
myGames: "Meine Runden"
|
||||
allGames: "Alle Runden"
|
||||
ended: "Beendet"
|
||||
playing: "Partie läuft"
|
||||
isLlotheo: "Der mit weniger Steinen gewinnt (Llotheo)"
|
||||
loopedMap: "Wiederholendes Spielbrett"
|
||||
canPutEverywhere: "Steine können überall platziert werden"
|
||||
timeLimitForEachTurn: "Zeitlimit eines Zugs"
|
||||
freeMatch: "Freies Spiel"
|
||||
lookingForPlayer: "Gegner werden gesucht..."
|
||||
gameCanceled: "Das Spiel wurde abgesagt."
|
||||
shareToTlTheGameWhenStart: "Spiel in der Chronik teilen, wenn es gestartet wurde"
|
||||
iStartedAGame: "Das Spiel hat begonnen! #MisskeyReversi"
|
||||
opponentHasSettingsChanged: "Der Gegner hat seine Einstellungen geändert."
|
||||
allowIrregularRules: "Irreguläre Regeln (völlig frei)"
|
||||
disallowIrregularRules: "Keine irregulären Regeln"
|
||||
showBoardLabels: "Anzeige der Zeilen- und Spaltennummern am Spielbrett"
|
||||
useAvatarAsStone: "Steine in Benutzeravatare umwandeln"
|
||||
_offlineScreen:
|
||||
title: "Offline - keine Verbindung zum Server möglich"
|
||||
header: "Verbindung zum Server nicht möglich"
|
||||
|
@ -2580,14 +2853,79 @@ _urlPreviewSetting:
|
|||
requireContentLength: "Vorschau nur generieren, wenn Content-Length verfügbar ist"
|
||||
requireContentLengthDescription: "Wenn der Server keine Content-Length zurückgibt, wird keine Vorschau erzeugt."
|
||||
userAgent: "User-Agent"
|
||||
userAgentDescription: "Legt den User-Agent fest, der beim Abrufen der Vorschau verwendet werden soll. Bleibt er leer, wird der Standard-User-Agent verwendet."
|
||||
summaryProxy: "Proxy-Endpunkte, die Vorschaubilder erzeugen"
|
||||
summaryProxyDescription: "Generierung von Vorschaubildern mit Summaly Proxy anstelle von Misskey selbst."
|
||||
summaryProxyDescription2: "Die folgenden Parameter werden als Abfrage-Strings mit dem Proxy verknüpft. Wenn der Proxy sie nicht unterstützt, werden die Werte ignoriert."
|
||||
_mediaControls:
|
||||
pip: "Bild-in-Bild"
|
||||
playbackRate: "Wiedergabegeschwindigkeit"
|
||||
loop: "Endloswiedergabe"
|
||||
_contextMenu:
|
||||
title: "Kontextmenü"
|
||||
app: "Anwendung"
|
||||
appWithShift: "Anwendung per Umschalttaste"
|
||||
native: "Natives Browsermenü"
|
||||
_gridComponent:
|
||||
_error:
|
||||
requiredValue: "Dieser Wert ist ein Pflichtfeld"
|
||||
columnTypeNotSupport: "Die Validierung regulärer Ausdrücke wird nur für Spalten vom Typ \"Text\" unterstützt."
|
||||
patternNotMatch: "Dieser Wert stimmt nicht mit dem Schema in {pattern} überein"
|
||||
notUnique: "Dieser Wert muss eindeutig sein"
|
||||
_roleSelectDialog:
|
||||
notSelected: "Nicht ausgewählt"
|
||||
_customEmojisManager:
|
||||
_gridCommon:
|
||||
copySelectionRows: "Ausgewählte Zeilen kopieren"
|
||||
copySelectionRanges: "Auswahl kopieren"
|
||||
deleteSelectionRows: "Ausgewählte Zeilen löschen"
|
||||
deleteSelectionRanges: "Zeilen in der Auswahl löschen"
|
||||
searchSettings: "Sucheinstellungen"
|
||||
searchSettingCaption: "Detaillierte Suchkriterien festlegen."
|
||||
searchLimit: "Anzahl der Ergebnisse"
|
||||
sortOrder: "Sortierung"
|
||||
registrationLogs: "Registrierungsprotokoll"
|
||||
registrationLogsCaption: "Protokolle werden beim Aktualisieren oder Löschen von Emojis angezeigt. Sie verschwinden nach dem Aktualisieren oder Löschen, dem Wechsel zu einer neuen Seite oder dem Neuladen."
|
||||
alertEmojisRegisterFailedDescription: "Emoji konnte nicht aktualisiert oder gelöscht werden. Bitte prüfe das Registrierungsprotokoll für Details."
|
||||
_logs:
|
||||
showSuccessLogSwitch: "Erfolgsprotokoll zeigen"
|
||||
failureLogNothing: "Es gibt kein Fehlerprotokoll."
|
||||
logNothing: "Keine Protokoll-Einträge."
|
||||
_remote:
|
||||
selectionRowDetail: "Details der ausgewählten Zeile"
|
||||
importSelectionRows: "Ausgewählte Zeilen importieren"
|
||||
importSelectionRangesRows: "Zeilen in der Auswahl importieren"
|
||||
importEmojisButton: "Ausgewählte Emojis importieren"
|
||||
confirmImportEmojisTitle: "Emojis importieren"
|
||||
confirmImportEmojisDescription: "Importiere {count} Emoji(s), die von entfernten Server empfangen wurden. Bitte achte genau auf die Lizenz der Emojis. Bist du sicher, dass du fortfahren möchtest?"
|
||||
_local:
|
||||
tabTitleList: "Hinzugefügte Emojis"
|
||||
tabTitleRegister: "Emojis hinzufügen"
|
||||
_list:
|
||||
emojisNothing: "Es wurden keine Emojis hinzugefügt."
|
||||
markAsDeleteTargetRows: "Ausgewählte Zeilen als zu löschendes Element markieren"
|
||||
markAsDeleteTargetRanges: "Zeilen in der Auswahl als zu löschendes Element markieren"
|
||||
alertUpdateEmojisNothingDescription: "Es wurden keine Emojis geändert."
|
||||
alertDeleteEmojisNothingDescription: "Es gibt keine zu löschenden Emojis."
|
||||
confirmMovePage: "Möchten Sie die Seiten verschieben?"
|
||||
confirmChangeView: "Möchten Sie die Darstellung wechseln?"
|
||||
confirmUpdateEmojisDescription: "Aktualisiere {count} Emoji(s). Willst du fortfahren?"
|
||||
confirmDeleteEmojisDescription: "Lösche {count} ausgewählte Emoji(s). Willst du fortfahren?"
|
||||
confirmResetDescription: "Alle bisher vorgenommenen Änderungen werden zurückgesetzt."
|
||||
confirmMovePageDesciption: "An den Emojis auf dieser Seite wurden Änderungen vorgenommen.\nWenn du die Seite verlässt, ohne zu speichern, werden alle auf dieser Seite vorgenommenen Änderungen verworfen."
|
||||
dialogSelectRoleTitle: "Suche nach dem Rollensatz in Emojis"
|
||||
_register:
|
||||
uploadSettingTitle: "Upload-Einstellungen"
|
||||
uploadSettingDescription: "Hier kannst du das Verhalten beim Hochladen von Emojis konfigurieren."
|
||||
directoryToCategoryLabel: "Gib den Namen des Verzeichnisses in das Feld „Kategorie“ ein"
|
||||
directoryToCategoryCaption: "Wenn du ein Verzeichnis ziehst und ablegst, gib den Verzeichnisnamen in das Feld „Kategorie“ ein."
|
||||
emojiInputAreaCaption: "Wählen Sie die Emojis aus, die Sie mit einer der folgenden Methoden speichern möchten."
|
||||
emojiInputAreaList1: "Ziehe Bilddateien oder Verzeichnisse per Drag-and-drop in diesen Rahmen"
|
||||
emojiInputAreaList2: "Klicke auf diesen Link, um von deinem PC aus zu wählen"
|
||||
emojiInputAreaList3: "Klicke auf diesen Link, um vom Drive aus zu wählen"
|
||||
confirmRegisterEmojisDescription: "Füge die in der Liste aufgeführten Emojis als neue benutzerdefinierte Emojis hinzu. Bist du sicher? (Um eine Überlastung zu vermeiden, können nur {count} Emoji(s) in einem Vorgang hinzugefügt werden)"
|
||||
confirmClearEmojisDescription: "Verwerfe die Bearbeitungen und lösche die Emojis aus der Liste. Bist du sicher, dass du fortfahren möchtest?"
|
||||
confirmUploadEmojisDescription: "Lade die {count} abgelegte(n) Datei(en) in das Drive hoch. Bist du sicher, dass du fortfahren möchtest?"
|
||||
_embedCodeGen:
|
||||
title: "Einbettungscode anpassen"
|
||||
header: "Kopfzeile anzeigen"
|
||||
|
@ -2595,6 +2933,9 @@ _embedCodeGen:
|
|||
maxHeight: "Maximale Höhe"
|
||||
maxHeightDescription: "Der Wert 0 deaktiviert die Einstellung der maximalen Höhe. Gib einen Wert an, um zu verhindern, dass das Widget weiterhin vertikal vergrößert wird."
|
||||
maxHeightWarn: "Die Begrenzung der maximalen Höhe ist deaktiviert (0). Wenn dies nicht beabsichtigt war, setze die maximale Höhe auf einen Wert fest."
|
||||
previewIsNotActual: "Die Anzeige weicht von der tatsächlichen Einbettung ab, da sie den auf dem Vorschaufenster angezeigten Bereich überschreitet."
|
||||
rounded: "Ecken abrunden"
|
||||
border: "Dem äußeren Rand einen Rahmen hinzufügen"
|
||||
applyToPreview: "Auf die Vorschau anwenden"
|
||||
generateCode: "Einbettungscode generieren"
|
||||
codeGenerated: "Der Code wurde generiert"
|
||||
|
@ -2603,7 +2944,11 @@ _selfXssPrevention:
|
|||
warning: "WARNUNG"
|
||||
title: "„Füge in diesen Bereich etwas ein“ ist eine Betrugsmasche."
|
||||
description1: "Wenn du hier etwas einfügst, könnte ein böswilliger Benutzer dein Konto übernehmen oder deine persönlichen Daten stehlen."
|
||||
description2: "Wenn du das nicht genau verstehst, was du einfügst, %csolltest du die Eingabe abbrechen und das Fenster schließen."
|
||||
description3: "Weitere Informationen findest du hier. {link}"
|
||||
_followRequest:
|
||||
recieved: "Anfrage erhalten"
|
||||
sent: "Anfrage gesendet"
|
||||
_remoteLookupErrors:
|
||||
_federationNotAllowed:
|
||||
title: "Kommunikation mit diesem Server nicht möglich"
|
||||
|
@ -2611,10 +2956,45 @@ _remoteLookupErrors:
|
|||
_uriInvalid:
|
||||
title: "URI ist fehlerhaft"
|
||||
description: "Es gibt ein Problem mit der von dir eingegebenen URI. Bitte prüfe, ob du Zeichen eingegeben hast, die in der URI nicht verwendet werden können."
|
||||
_requestFailed:
|
||||
title: "Anfrage fehlgeschlagen"
|
||||
description: "Die Kommunikation mit diesem Server ist fehlgeschlagen. Der Server ist möglicherweise nicht erreichbar. Bitte vergewissere dich auch, dass du keine ungültige oder nicht existierende URI eingegeben hast."
|
||||
_responseInvalid:
|
||||
title: "Die Antwort ist ungültig"
|
||||
description: "Die Kommunikation mit dem Server war erfolgreich, aber die erhaltenen Daten waren nicht korrekt. Wenn du Remote-Inhalte über einen Server eines Dritten abfragst, verwende bitte erneut eine URI, die vom Ursprungsserver abgerufen werden kann."
|
||||
_noSuchObject:
|
||||
title: "Nicht gefunden"
|
||||
description: "Die angeforderte Ressource konnte nicht gefunden werden, bitte überprüfe die URI erneut."
|
||||
_captcha:
|
||||
verify: "Bitte beantworte das CAPTCHA"
|
||||
testSiteKeyMessage: "Du kannst die Vorschau prüfen, indem du die Testwerte für den Site- und Secret-Key eingibst. Weitere Informationen findest du auf der folgenden Seite."
|
||||
_error:
|
||||
_requestFailed:
|
||||
title: "CAPTCHA-Anfrage fehlgeschlagen."
|
||||
text: "Bitte probiere es später noch einmal oder überprüfe die Einstellungen erneut."
|
||||
_verificationFailed:
|
||||
title: "CAPTCHA-Prüfung fehlgeschlagen"
|
||||
text: "Bitte überprüfe nochmals, ob die Einstellungen korrekt sind."
|
||||
_unknown:
|
||||
title: "CAPTCHA-Fehler"
|
||||
text: "Es ist ein unerwarteter Fehler aufgetreten."
|
||||
_bootErrors:
|
||||
title: "Laden fehlgeschlagen"
|
||||
serverError: "Wenn das Problem nach kurzem Warten und erneutem Laden immer noch nicht behoben ist, wende dich bitte an den Serveradministrator und gib die folgende Fehler-ID an."
|
||||
solution: "Folgendes könnte das Problem lösen."
|
||||
solution1: "Aktualisiere deinen Browser und dein Betriebssystem auf die neueste Version"
|
||||
solution2: "Deaktiviere den Werbeblocker"
|
||||
solution3: "Leere den Browser-Cache"
|
||||
solution4: "(Tor Browser) Setze dom.webaudio.enabled auf true"
|
||||
otherOption: "Weitere Optionen"
|
||||
otherOption1: "Client-Einstellungen und Cache löschen"
|
||||
otherOption2: "Einfachen Client starten"
|
||||
otherOption3: "Starte das Reparaturwerkzeug"
|
||||
_search:
|
||||
searchScopeAll: "Alle"
|
||||
searchScopeLocal: "Lokal"
|
||||
searchScopeServer: "Bestimmter Server"
|
||||
searchScopeUser: "Spezifischer Benutzer"
|
||||
pleaseEnterServerHost: "Gib den Server-Host ein"
|
||||
pleaseSelectUser: "Benutzer auswählen"
|
||||
serverHostPlaceholder: "Beispiel: misskey.example.com"
|
||||
|
|
|
@ -162,14 +162,12 @@ imageUrl: "URL εικόνας"
|
|||
remove: "Διαγραφή"
|
||||
removed: "Η διαγραφή ολοκληρώθηκε επιτυχώς"
|
||||
saved: "Αποθηκεύτηκε"
|
||||
messaging: "Συνομιλία"
|
||||
upload: "Ανεβάστε"
|
||||
fromDrive: "Από τον Αποθηκευτικό Χώρο"
|
||||
fromUrl: "Από URL"
|
||||
uploadFromUrl: "Ανεβάστε από URL"
|
||||
explore: "Εξερευνήστε"
|
||||
messageRead: "Διαβάστηκε"
|
||||
startMessaging: "Ξεκινήστε μία συνομιλία"
|
||||
nUsersRead: "διαβάστηκε από {n}"
|
||||
start: "Ας αρχίσουμε"
|
||||
home: "Κεντρικό"
|
||||
|
@ -288,6 +286,11 @@ cannotUploadBecauseNoFreeSpace: "Το ανέβασμα απέτυχε λόγω
|
|||
icon: "Εικονίδιο"
|
||||
replies: "Απάντηση"
|
||||
renotes: "Κοινοποίηση σημειώματος"
|
||||
postForm: "Φόρμα δημοσίευσης"
|
||||
information: "Πληροφορίες"
|
||||
_chat:
|
||||
members: "Μέλη"
|
||||
home: "Κεντρικό"
|
||||
_email:
|
||||
_follow:
|
||||
title: "Έχετε ένα νέο ακόλουθο"
|
||||
|
@ -321,6 +324,7 @@ _permissions:
|
|||
"write:notifications": "Διαχειριστείτε τις ειδοποιήσεις σας"
|
||||
"read:pages": "Δείτε τις Σελίδες σας"
|
||||
"write:pages": "Επεξεργαστείτε ή διαγράψτε τις σελίδες σας"
|
||||
"write:chat": "Γράψτε ή διαγράψτε μηνύματα συνομιλίας"
|
||||
_antennaSources:
|
||||
all: "Όλα τα σημειώματα"
|
||||
homeTimeline: "Σημειώματα από μέλη που ακολουθείτε"
|
||||
|
|
|
@ -289,7 +289,6 @@ deleteAreYouSure: "Are you sure that you want to delete \"{x}\"?"
|
|||
resetAreYouSure: "Really reset?"
|
||||
areYouSure: "Are you sure?"
|
||||
saved: "Saved"
|
||||
messaging: "Chat"
|
||||
upload: "Upload"
|
||||
keepOriginalUploading: "Keep original image"
|
||||
keepOriginalUploadingDescription: "Saves the originally uploaded image as-is. If turned off, a version to display on the web will be generated on upload."
|
||||
|
@ -302,7 +301,7 @@ uploadFromUrlMayTakeTime: "It may take some time until the upload is complete."
|
|||
explore: "Explore"
|
||||
messageRead: "Read"
|
||||
noMoreHistory: "There is no further history"
|
||||
startMessaging: "Start a new chat"
|
||||
startChat: "Start chat"
|
||||
nUsersRead: "read by {n}"
|
||||
agreeTo: "I agree to {0}"
|
||||
agree: "Agree"
|
||||
|
@ -346,7 +345,7 @@ emptyDrive: "Your Drive is empty"
|
|||
emptyFolder: "This folder is empty"
|
||||
unableToDelete: "Unable to delete"
|
||||
inputNewFileName: "Enter a new filename"
|
||||
inputNewDescription: "Enter new caption"
|
||||
inputNewDescription: "Enter new alt text"
|
||||
inputNewFolderName: "Enter a new folder name"
|
||||
circularReferenceFolder: "The destination folder is a subfolder of the folder you wish to move."
|
||||
hasChildFilesOrFolders: "Since this folder is not empty, it can not be deleted."
|
||||
|
@ -425,6 +424,7 @@ antennaExcludeBots: "Exclude bot accounts"
|
|||
antennaKeywordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition."
|
||||
notifyAntenna: "Notify about new notes"
|
||||
withFileAntenna: "Only notes with files"
|
||||
excludeNotesInSensitiveChannel: "Exclude notes from sensitive channels"
|
||||
enableServiceworker: "Enable Push-Notifications for your Browser"
|
||||
antennaUsersDescription: "List one username per line"
|
||||
caseSensitive: "Case sensitive"
|
||||
|
@ -491,8 +491,6 @@ noteOf: "Note by {user}"
|
|||
quoteAttached: "Quote"
|
||||
quoteQuestion: "Append as quote?"
|
||||
attachAsFileQuestion: "The text in clipboard is long. Would you want to attach it as text file?"
|
||||
noMessagesYet: "No messages yet"
|
||||
newMessageExists: "There are new messages"
|
||||
onlyOneFileCanBeAttached: "You can only attach one file to a message"
|
||||
signinRequired: "Please register or sign in before continuing"
|
||||
signinOrContinueOnRemote: "To continue, you need to move your server or sign up / log in to this server."
|
||||
|
@ -646,8 +644,8 @@ disablePlayer: "Close video player"
|
|||
expandTweet: "Expand post"
|
||||
themeEditor: "Theme editor"
|
||||
description: "Description"
|
||||
describeFile: "Add caption"
|
||||
enterFileDescription: "Enter caption"
|
||||
describeFile: "Add alt text"
|
||||
enterFileDescription: "Enter alt text"
|
||||
author: "Author"
|
||||
leaveConfirm: "There are unsaved changes. Do you want to discard them?"
|
||||
manage: "Management"
|
||||
|
@ -698,6 +696,7 @@ userSaysSomethingAbout: "{name} said something about \"{word}\""
|
|||
makeActive: "Activate"
|
||||
display: "Display"
|
||||
copy: "Copy"
|
||||
copiedToClipboard: "Copied to clipboard"
|
||||
metrics: "Metrics"
|
||||
overview: "Overview"
|
||||
logs: "Logs"
|
||||
|
@ -1016,7 +1015,7 @@ sendPushNotificationReadMessageCaption: "This may increase the power consumption
|
|||
windowMaximize: "Maximize"
|
||||
windowMinimize: "Minimize"
|
||||
windowRestore: "Restore"
|
||||
caption: "Caption"
|
||||
caption: "Alt text"
|
||||
loggedInAsBot: "Currently logged in as bot"
|
||||
tools: "Tools"
|
||||
cannotLoad: "Unable to load"
|
||||
|
@ -1313,6 +1312,130 @@ confirmOnReact: "Confirm when reacting"
|
|||
reactAreYouSure: "Would you like to add a \"{emoji}\" reaction?"
|
||||
markAsSensitiveConfirm: "Do you want to set this media as sensitive?"
|
||||
unmarkAsSensitiveConfirm: "Do you want to remove the sensitive designation for this media?"
|
||||
preferences: "Preferences"
|
||||
accessibility: "Accessibility"
|
||||
preferencesProfile: "Preferences profile"
|
||||
copyPreferenceId: "Copy the preference ID"
|
||||
resetToDefaultValue: "Revert to default"
|
||||
overrideByAccount: "Override by the account"
|
||||
untitled: "Untitled"
|
||||
noName: "No name"
|
||||
skip: "Skip"
|
||||
restore: "Restore"
|
||||
syncBetweenDevices: "Sync between devices"
|
||||
preferenceSyncConflictTitle: "The configured value exists on the server."
|
||||
preferenceSyncConflictText: "The sync enabled settings will save their values to the server. However, there are existing values on the server. Which set of values would you like to overwrite?"
|
||||
preferenceSyncConflictChoiceServer: "Configured value on server"
|
||||
preferenceSyncConflictChoiceDevice: "Configured value on device"
|
||||
preferenceSyncConflictChoiceCancel: "Cancel enabling sync"
|
||||
paste: "Paste"
|
||||
emojiPalette: "Emoji palette"
|
||||
postForm: "Posting form"
|
||||
textCount: "Character count"
|
||||
information: "About"
|
||||
chat: "Chat"
|
||||
migrateOldSettings: "Migrate old client settings"
|
||||
migrateOldSettings_description: "This should be done automatically but if for some reason the migration was not successful, you can trigger the migration process yourself manually. The current configuration information will be overwritten."
|
||||
compress: "Compress"
|
||||
right: "Right"
|
||||
bottom: "Bottom"
|
||||
top: "Top"
|
||||
embed: "Embed"
|
||||
settingsMigrating: "Settings are being migrated, please wait a moment... (You can also migrate manually later by going to Settings→Others→Migrate old settings)"
|
||||
readonly: "Read only"
|
||||
goToDeck: "Return to Deck"
|
||||
_chat:
|
||||
noMessagesYet: "No messages yet"
|
||||
newMessage: "New message"
|
||||
individualChat: "Private Chat"
|
||||
individualChat_description: "Have a private chat with another person."
|
||||
roomChat: "Room Chat"
|
||||
roomChat_description: "A chat room which can have multiple people.\nYou can also invite people who don't allow private chats if they accept the invite."
|
||||
createRoom: "Create Room"
|
||||
inviteUserToChat: "Invite users to start chatting"
|
||||
yourRooms: "Created rooms"
|
||||
joiningRooms: "Joined rooms"
|
||||
invitations: "Invite"
|
||||
noInvitations: "No invitations"
|
||||
history: "History"
|
||||
noHistory: "No history available"
|
||||
noRooms: "No rooms found"
|
||||
inviteUser: "Invite Users"
|
||||
sentInvitations: "Sent Invites"
|
||||
join: "Join"
|
||||
ignore: "Ignore"
|
||||
leave: "Leave room"
|
||||
members: "Members"
|
||||
searchMessages: "Search messages"
|
||||
home: "Home"
|
||||
send: "Send"
|
||||
newline: "New line"
|
||||
muteThisRoom: "Mute room"
|
||||
deleteRoom: "Delete room"
|
||||
chatNotAvailableForThisAccountOrServer: "Chat is not enabled on this server or for this account."
|
||||
chatIsReadOnlyForThisAccountOrServer: "Chat is read-only on this instance or this account. You cannot write new messages or create/join chat rooms."
|
||||
chatNotAvailableInOtherAccount: "The chat function is disabled for the other user."
|
||||
cannotChatWithTheUser: "Cannot start a chat with this user"
|
||||
cannotChatWithTheUser_description: "Chat is either unavailable or the other party has not enabled chat."
|
||||
chatWithThisUser: "Chat with user"
|
||||
thisUserAllowsChatOnlyFromFollowers: "This user accepts chats from followers only."
|
||||
thisUserAllowsChatOnlyFromFollowing: "This user accepts chats only from users they follow."
|
||||
thisUserAllowsChatOnlyFromMutualFollowing: "This user only accepts chats from users who are mutual followers."
|
||||
thisUserNotAllowedChatAnyone: "This user is not accepting chats from anyone."
|
||||
chatAllowedUsers: "Who to allow chatting with"
|
||||
chatAllowedUsers_note: "You can chat with anyone to whom you have sent a chat message regardless of this setting."
|
||||
_chatAllowedUsers:
|
||||
everyone: "Everyone"
|
||||
followers: "Only your followers"
|
||||
following: "Only users you are following"
|
||||
mutual: "Mutual followers only"
|
||||
none: "Nobody"
|
||||
_emojiPalette:
|
||||
palettes: "Palette"
|
||||
enableSyncBetweenDevicesForPalettes: "Enable palette sync between devices"
|
||||
paletteForMain: "Main palette"
|
||||
paletteForReaction: "Reaction palette"
|
||||
_settings:
|
||||
driveBanner: "You can manage and configure the drive, check usage, and configure file upload settings."
|
||||
pluginBanner: "You can extend client features with plugins. You can install plugins, configure and manage individually."
|
||||
notificationsBanner: "You can configure the types and range of notifications from the server and push notifications."
|
||||
api: "API"
|
||||
webhook: "Webhook"
|
||||
serviceConnection: "Service integration"
|
||||
serviceConnectionBanner: "Manage and configure access tokens and Webhooks to integrate with external apps or services."
|
||||
accountData: "Account data"
|
||||
accountDataBanner: "Export and import to manage account data."
|
||||
muteAndBlockBanner: "You can configure and manage settings to hide content and restrict actions from specific users."
|
||||
accessibilityBanner: "You can personalize the client's visuals and behavior, and configure settings to optimize usage."
|
||||
privacyBanner: "You can configure settings related to account privacy, such as content visibility, discoverability, and follow approval."
|
||||
securityBanner: "You can configure settings related to account security, such as password, login methods, authentication apps, and Passkeys."
|
||||
preferencesBanner: "You can configure the overall behavior of the client according to your preferences."
|
||||
appearanceBanner: "You can configure the appearance and display settings for the client according to your preferences."
|
||||
soundsBanner: "You can configure the sound settings for playback in the client."
|
||||
timelineAndNote: "Timeline and note"
|
||||
makeEveryTextElementsSelectable: "Make all text elements selectable"
|
||||
makeEveryTextElementsSelectable_description: "Enabling this may reduce usability in some situations."
|
||||
useStickyIcons: "Make icons follow while scrolling"
|
||||
showNavbarSubButtons: "Show sub-buttons on the navigation bar"
|
||||
ifOn: "When turned on"
|
||||
ifOff: "When turned off"
|
||||
enableSyncThemesBetweenDevices: "Synchronize installed themes across devices"
|
||||
_chat:
|
||||
showSenderName: "Show sender's name"
|
||||
sendOnEnter: "Press Enter to send"
|
||||
_preferencesProfile:
|
||||
profileName: "Profile name"
|
||||
profileNameDescription: "Set a name that identifies this device."
|
||||
profileNameDescription2: "Example: \"Main PC\", \"Smartphone\""
|
||||
_preferencesBackup:
|
||||
autoBackup: "Auto backup"
|
||||
restoreFromBackup: "Restore from backup"
|
||||
noBackupsFoundTitle: "No backups found"
|
||||
noBackupsFoundDescription: "No auto-created backups were found, but if you have manually saved a backup file, you can import and restore it."
|
||||
selectBackupToRestore: "Select a backup to restore"
|
||||
youNeedToNameYourProfileToEnableAutoBackup: "A profile name must be set to enable auto backup."
|
||||
autoPreferencesBackupIsNotEnabledForThisDevice: "Settings auto backup is not enabled on this device."
|
||||
backupFound: "Settings backup is found"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "Require sign-in to view contents"
|
||||
requireSigninToViewContentsDescription1: "Require login to view all notes and other content you have created. This will have the effect of preventing crawlers from collecting your information."
|
||||
|
@ -1323,6 +1446,7 @@ _accountSettings:
|
|||
makeNotesHiddenBefore: "Make past notes private"
|
||||
makeNotesHiddenBeforeDescription: "While this feature is enabled, notes that are past the set date and time or have been visible only to you. When it is deactivated, the note publication status will also be restored."
|
||||
mayNotEffectForFederatedNotes: "Notes federated to a remote server may not be affected."
|
||||
mayNotEffectSomeSituations: "These restrictions are simplified. They may not apply in some situations, such as when viewing on a remote server or during moderation."
|
||||
notesHavePassedSpecifiedPeriod: "Note that the specified time has passed"
|
||||
notesOlderThanSpecifiedDateAndTime: "Notes before the specified date and time"
|
||||
_abuseUserReport:
|
||||
|
@ -1331,7 +1455,7 @@ _abuseUserReport:
|
|||
resolve: "Resolve"
|
||||
accept: "Accept"
|
||||
reject: "Reject"
|
||||
resolveTutorial: "If the report is legitimate in content, select \"Accept\" to mark the case as resolved in the affirmative.\nIf the content of the report is not legitimate, select \"Reject\" to mark the case as resolved in the negative."
|
||||
resolveTutorial: "If the report's content is legitimate, select \"Accept\" to mark it as resolved.\nIf the report's content is illegitimate, select \"Reject\" to ignore it."
|
||||
_delivery:
|
||||
status: "Delivery status"
|
||||
stop: "Suspended"
|
||||
|
@ -1768,6 +1892,8 @@ _role:
|
|||
descriptionOfIsExplorable: "This role's timeline and the list of users with this will be made public if enabled."
|
||||
displayOrder: "Position"
|
||||
descriptionOfDisplayOrder: "The higher the number, the higher its UI position."
|
||||
preserveAssignmentOnMoveAccount: "Preserve role assignment during migration"
|
||||
preserveAssignmentOnMoveAccount_description: "When turned on, this role will be carried over to the destination account when an account with this role is migrated."
|
||||
canEditMembersByModerator: "Allow moderators to edit the list of members for this role"
|
||||
descriptionOfCanEditMembersByModerator: "When turned on, moderators as well as administrators will be able to assign and unassign users to this role. When turned off, only administrators will be able to assign users."
|
||||
priority: "Priority"
|
||||
|
@ -1808,6 +1934,7 @@ _role:
|
|||
canImportFollowing: "Allow importing following"
|
||||
canImportMuting: "Allow importing muting"
|
||||
canImportUserLists: "Allow importing lists"
|
||||
chatAvailability: "Allow Chat"
|
||||
_condition:
|
||||
roleAssignedTo: "Assigned to manual roles"
|
||||
isLocal: "Local user"
|
||||
|
@ -1971,6 +2098,7 @@ _theme:
|
|||
installed: "{name} has been installed"
|
||||
installedThemes: "Installed themes"
|
||||
builtinThemes: "Built-in themes"
|
||||
instanceTheme: "Server theme"
|
||||
alreadyInstalled: "This theme is already installed"
|
||||
invalid: "The format of this theme is invalid"
|
||||
make: "Make a theme"
|
||||
|
@ -2003,7 +2131,6 @@ _theme:
|
|||
header: "Header"
|
||||
navBg: "Sidebar background"
|
||||
navFg: "Sidebar text"
|
||||
navHoverFg: "Sidebar text (Hover)"
|
||||
navActive: "Sidebar text (Active)"
|
||||
navIndicator: "Sidebar indicator"
|
||||
link: "Link"
|
||||
|
@ -2026,17 +2153,15 @@ _theme:
|
|||
buttonHoverBg: "Button background (Hover)"
|
||||
inputBorder: "Input field border"
|
||||
driveFolderBg: "Drive folder background"
|
||||
wallpaperOverlay: "Wallpaper overlay"
|
||||
badge: "Badge"
|
||||
messageBg: "Chat background"
|
||||
accentDarken: "Accent (Darkened)"
|
||||
accentLighten: "Accent (Lightened)"
|
||||
fgHighlighted: "Highlighted Text"
|
||||
_sfx:
|
||||
note: "New note"
|
||||
noteMy: "Own note"
|
||||
notification: "Notifications"
|
||||
reaction: "On choosing a reaction"
|
||||
chatMessage: "Chat Messages"
|
||||
_soundSettings:
|
||||
driveFile: "Use an audio file in Drive."
|
||||
driveFileWarn: "Select an audio file from Drive."
|
||||
|
@ -2183,6 +2308,8 @@ _permissions:
|
|||
"read:clip-favorite": "View favorited clips"
|
||||
"read:federation": "Get federation data"
|
||||
"write:report-abuse": "Report violation"
|
||||
"write:chat": "Compose or delete chat messages"
|
||||
"read:chat": "Browse Chat"
|
||||
_auth:
|
||||
shareAccessTitle: "Granting application permissions"
|
||||
shareAccess: "Would you like to authorize \"{name}\" to access this account?"
|
||||
|
@ -2431,6 +2558,7 @@ _notification:
|
|||
newNote: "New note"
|
||||
unreadAntennaNote: "Antenna {name}"
|
||||
roleAssigned: "Role given"
|
||||
chatRoomInvitationReceived: "You have been invited to a chat room"
|
||||
emptyPushNotificationMessage: "Push notifications have been updated"
|
||||
achievementEarned: "Achievement unlocked"
|
||||
testNotification: "Test notification"
|
||||
|
@ -2459,9 +2587,11 @@ _notification:
|
|||
receiveFollowRequest: "Received follow requests"
|
||||
followRequestAccepted: "Accepted follow requests"
|
||||
roleAssigned: "Role given"
|
||||
chatRoomInvitationReceived: "Invited to chat room"
|
||||
achievementEarned: "Achievement unlocked"
|
||||
exportCompleted: "The export has been completed"
|
||||
login: "Sign In"
|
||||
createToken: "Create access token"
|
||||
test: "Notification test"
|
||||
app: "Notifications from linked apps"
|
||||
_actions:
|
||||
|
@ -2471,6 +2601,9 @@ _notification:
|
|||
_deck:
|
||||
alwaysShowMainColumn: "Always show main column"
|
||||
columnAlign: "Align columns"
|
||||
columnGap: "Margin between columns"
|
||||
deckMenuPosition: "Deck menu position"
|
||||
navbarPosition: "Navigation bar position"
|
||||
addColumn: "Add column"
|
||||
newNoteNotificationSettings: "Notification setting for new notes"
|
||||
configureColumn: "Column settings"
|
||||
|
@ -2484,11 +2617,12 @@ _deck:
|
|||
newProfile: "New profile"
|
||||
deleteProfile: "Delete profile"
|
||||
introduction: "Create the perfect interface for you by arranging columns freely!"
|
||||
introduction2: "Click on the + on the right of the screen to add new colums whenever you want."
|
||||
introduction2: "Click on the + on the right of the screen to add new columns whenever you want."
|
||||
widgetsIntroduction: "Please select \"Edit widgets\" in the column menu and add a widget."
|
||||
useSimpleUiForNonRootPages: "Use simple UI for navigated pages"
|
||||
usedAsMinWidthWhenFlexible: "Minimum width will be used for this when the \"Auto-adjust width\" option is enabled"
|
||||
flexible: "Auto-adjust width"
|
||||
enableSyncBetweenDevicesForProfiles: "Enable profile information sync between devices"
|
||||
_columns:
|
||||
main: "Main"
|
||||
widgets: "Widgets"
|
||||
|
@ -2534,7 +2668,7 @@ _webhookSettings:
|
|||
testRemarks: "Click the button to the right of the switch to send a test Webhook with dummy data."
|
||||
_abuseReport:
|
||||
_notificationRecipient:
|
||||
createRecipient: "Add a recipient for reports"
|
||||
createRecipient: "Add recipient for reports"
|
||||
modifyRecipient: "Edit a recipient for reports"
|
||||
recipientType: "Notification type"
|
||||
_recipientType:
|
||||
|
@ -2596,6 +2730,7 @@ _moderationLogTypes:
|
|||
deletePage: "Page deleted"
|
||||
deleteFlash: "Play deleted"
|
||||
deleteGalleryPost: "Gallery post deleted"
|
||||
deleteChatRoom: "Deleted Chat Room"
|
||||
updateProxyAccountDescription: "Update the description of the proxy account"
|
||||
_fileViewer:
|
||||
title: "File details"
|
||||
|
@ -2610,10 +2745,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "Make sure the distributor of this resource is trustworthy before installation."
|
||||
_plugin:
|
||||
title: "Do you want to install this plugin?"
|
||||
metaTitle: "Plugin information"
|
||||
_theme:
|
||||
title: "Do you want to install this theme?"
|
||||
metaTitle: "Theme information"
|
||||
_meta:
|
||||
base: "Base color scheme"
|
||||
_vendorInfo:
|
||||
|
@ -2744,7 +2877,7 @@ _roleSelectDialog:
|
|||
_customEmojisManager:
|
||||
_gridCommon:
|
||||
copySelectionRows: "Copy selected rows"
|
||||
copySelectionRanges: "Copy selected ranges"
|
||||
copySelectionRanges: "Copy selection"
|
||||
deleteSelectionRows: "Delete selected rows"
|
||||
deleteSelectionRanges: "Delete rows in the selection"
|
||||
searchSettings: "Search settings"
|
||||
|
@ -2766,7 +2899,7 @@ _customEmojisManager:
|
|||
confirmImportEmojisTitle: "Import Emojis"
|
||||
confirmImportEmojisDescription: "Import {count} Emoji(s) received from the remote server. Please pay close attention to the license of the Emoji. Are you sure to continue?"
|
||||
_local:
|
||||
tabTitleList: "List of registered Emojis"
|
||||
tabTitleList: "Registered emojis"
|
||||
tabTitleRegister: "Emoji registration"
|
||||
_list:
|
||||
emojisNothing: "There are no registered Emojis."
|
||||
|
@ -2814,8 +2947,8 @@ _selfXssPrevention:
|
|||
description2: "If you do not understand exactly what you are trying to paste, %cstop working right now and close this window."
|
||||
description3: "For more information, please refer to this. {link}"
|
||||
_followRequest:
|
||||
recieved: "Received application"
|
||||
sent: "Sent application"
|
||||
recieved: "Received request"
|
||||
sent: "Sent request"
|
||||
_remoteLookupErrors:
|
||||
_federationNotAllowed:
|
||||
title: "Unable to communicate with this server"
|
||||
|
|
|
@ -289,7 +289,6 @@ deleteAreYouSure: "¿Desea borrar \"{x}\"?"
|
|||
resetAreYouSure: "¿Desea reestablecer?"
|
||||
areYouSure: "¿Estás conforme?"
|
||||
saved: "Guardado"
|
||||
messaging: "Chat"
|
||||
upload: "Subir"
|
||||
keepOriginalUploading: "Mantener la imagen original"
|
||||
keepOriginalUploadingDescription: "Mantener la versión original al cargar imágenes. Si está desactivado, el navegador generará imágenes para la publicación web en el momento de recargar la página"
|
||||
|
@ -302,7 +301,6 @@ uploadFromUrlMayTakeTime: "Subir el fichero puede tardar un tiempo."
|
|||
explore: "Explorar"
|
||||
messageRead: "Ya leído"
|
||||
noMoreHistory: "El historial se ha acabado"
|
||||
startMessaging: "Iniciar chat"
|
||||
nUsersRead: "Leído por {n} personas"
|
||||
agreeTo: "De acuerdo con {0}"
|
||||
agree: "De acuerdo."
|
||||
|
@ -491,8 +489,6 @@ noteOf: "Notas de {user}"
|
|||
quoteAttached: "Cita añadida"
|
||||
quoteQuestion: "¿Quiere añadir una cita?"
|
||||
attachAsFileQuestion: "El texto del portapapeles es demasiado grande ¿Desea adjuntarlo como archivo de texto?"
|
||||
noMessagesYet: "Aún no hay chat"
|
||||
newMessageExists: "Tienes un mensaje nuevo"
|
||||
onlyOneFileCanBeAttached: "Solo se puede añadir un archivo al mensaje"
|
||||
signinRequired: "Iniciar sesión"
|
||||
signinOrContinueOnRemote: "Para continuar, tendrá que ir a su servidor o registrarse e iniciar sesión en este servidor"
|
||||
|
@ -1299,6 +1295,17 @@ messageToFollower: "Mensaje a seguidores"
|
|||
target: "Para"
|
||||
federationSpecified: "Este servidor opera en una federación de listas blancas. No puede interactuar con otros servidores que no sean los especificados por el administrador."
|
||||
federationDisabled: "La federación está desactivada en este servidor. No puede interactuar con usuarios de otros servidores"
|
||||
preferences: "Preferencias"
|
||||
postForm: "Formulario"
|
||||
information: "Información"
|
||||
_chat:
|
||||
invitations: "Invitar"
|
||||
noHistory: "No hay datos en el historial"
|
||||
members: "Miembros"
|
||||
home: "Inicio"
|
||||
send: "Enviar"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "Se requiere iniciar sesión para ver el contenido"
|
||||
requireSigninToViewContentsDescription1: "Requiere iniciar sesión para ver todas las notas y otros contenidos que hayas creado. Se espera que esto evite que los rastreadores recopilen información."
|
||||
|
@ -1958,7 +1965,6 @@ _theme:
|
|||
header: "Cabezal"
|
||||
navBg: "Fondo de la barra lateral"
|
||||
navFg: "Texto de la barra lateral"
|
||||
navHoverFg: "Texto de la barra lateral (hover)"
|
||||
navActive: "Texto de la barra lateral (activo)"
|
||||
navIndicator: "Indicador de la barra lateral"
|
||||
link: "Vínculo"
|
||||
|
@ -1981,11 +1987,8 @@ _theme:
|
|||
buttonHoverBg: "Fondo de botón (hover)"
|
||||
inputBorder: "Borde de los campos de entrada"
|
||||
driveFolderBg: "Fondo de capeta del drive"
|
||||
wallpaperOverlay: "Transparencia del fondo de pantalla"
|
||||
badge: "Medalla"
|
||||
messageBg: "Fondo de chat"
|
||||
accentDarken: "Acento (oscuro)"
|
||||
accentLighten: "Acento (claro)"
|
||||
fgHighlighted: "Texto resaltado"
|
||||
_sfx:
|
||||
note: "Notas"
|
||||
|
@ -2137,6 +2140,7 @@ _permissions:
|
|||
"read:clip-favorite": "Ver los clips que me gustan"
|
||||
"read:federation": "Ver instancias federadas"
|
||||
"write:report-abuse": "Crear reportes de usuario"
|
||||
"write:chat": "Administrar chat"
|
||||
_auth:
|
||||
shareAccessTitle: "Permisos de la aplicación"
|
||||
shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?"
|
||||
|
@ -2518,10 +2522,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "Asegúrate de que el distribuidor de este recurso es de confianza antes de proceder a la instalación."
|
||||
_plugin:
|
||||
title: "¿Quieres instalar este plugin?"
|
||||
metaTitle: "Información del plugin"
|
||||
_theme:
|
||||
title: "¿Quieres instalar este tema?"
|
||||
metaTitle: "Información del tema"
|
||||
_meta:
|
||||
base: "Esquema de color base"
|
||||
_vendorInfo:
|
||||
|
|
|
@ -277,7 +277,6 @@ deleteAreYouSure: "Êtes-vous sûr·e de vouloir supprimer « {x} » ?"
|
|||
resetAreYouSure: "Voulez-vous réinitialiser ?"
|
||||
areYouSure: "Êtes-vous sûr·e ?"
|
||||
saved: "Enregistré"
|
||||
messaging: "Discuter"
|
||||
upload: "Téléverser"
|
||||
keepOriginalUploading: "Garder l’image d’origine"
|
||||
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."
|
||||
|
@ -290,7 +289,6 @@ uploadFromUrlMayTakeTime: "Le téléversement de votre fichier peut prendre un c
|
|||
explore: "Découvrir"
|
||||
messageRead: "Lu"
|
||||
noMoreHistory: "Il n’y a plus d’historique"
|
||||
startMessaging: "Commencer à discuter"
|
||||
nUsersRead: "Lu par {n} personnes"
|
||||
agreeTo: "J’accepte {0}"
|
||||
agree: "Accepter"
|
||||
|
@ -477,8 +475,6 @@ retype: "Confirmation"
|
|||
noteOf: "Notes de {user}"
|
||||
quoteAttached: "Avec citation"
|
||||
quoteQuestion: "Souhaitez-vous ajouter une citation ?"
|
||||
noMessagesYet: "Pas encore de discussion"
|
||||
newMessageExists: "Vous avez un nouveau message"
|
||||
onlyOneFileCanBeAttached: "Vous ne pouvez joindre qu’un seul fichier au message"
|
||||
signinRequired: "Veuillez vous connecter"
|
||||
invitations: "Invitations"
|
||||
|
@ -1277,6 +1273,14 @@ prohibitedWordsForNameOfUser: "Mots interdits pour les noms d'utilisateur·rices
|
|||
lockdown: "Verrouiller"
|
||||
pleaseSelectAccount: "Sélectionner un compte"
|
||||
availableRoles: "Rôles disponibles"
|
||||
postForm: "Formulaire de publication"
|
||||
information: "Informations"
|
||||
_chat:
|
||||
invitations: "Inviter"
|
||||
noHistory: "Pas d'historique"
|
||||
members: "Membres"
|
||||
home: "Principal"
|
||||
send: "Envoyer"
|
||||
_abuseUserReport:
|
||||
forward: "Transférer"
|
||||
forwardDescription: "Transférer le signalement vers une instance distante en tant qu'anonyme."
|
||||
|
@ -1812,7 +1816,6 @@ _theme:
|
|||
header: "Entête"
|
||||
navBg: "Fond de la barre latérale"
|
||||
navFg: "Texte de la barre latérale"
|
||||
navHoverFg: "Texte de la barre latérale (survolé)"
|
||||
navActive: "Texte de la barre latérale (actif)"
|
||||
navIndicator: "Indicateur de barre latérale"
|
||||
link: "Lien"
|
||||
|
@ -1835,11 +1838,8 @@ _theme:
|
|||
buttonHoverBg: "Arrière-plan du bouton (survolé)"
|
||||
inputBorder: "Cadre de la zone de texte"
|
||||
driveFolderBg: "Arrière-plan du dossier de disque"
|
||||
wallpaperOverlay: "Superposition de fond d'écran"
|
||||
badge: "Badge"
|
||||
messageBg: "Arrière plan de la discussion"
|
||||
accentDarken: "Plus sombre"
|
||||
accentLighten: "Plus clair"
|
||||
fgHighlighted: "Texte mis en évidence"
|
||||
_sfx:
|
||||
note: "Nouvelle note"
|
||||
|
@ -1949,6 +1949,7 @@ _permissions:
|
|||
"write:admin:unsuspend-user": "Lever la suspension d'un utilisateur"
|
||||
"write:admin:meta": "Gérer les métadonnées de l'instance"
|
||||
"write:admin:roles": "Gérer les rôles"
|
||||
"write:chat": "Gérer les discussions"
|
||||
_auth:
|
||||
shareAccess: "Autoriser \"{name}\" à accéder à votre compte ?"
|
||||
shareAccessAsk: "Voulez-vous vraiment autoriser cette application à accéder à votre compte?"
|
||||
|
@ -2294,10 +2295,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "Veuillez confirmer que le distributeur est fiable avant l'installation."
|
||||
_plugin:
|
||||
title: "Voulez-vous installer cette extension ?"
|
||||
metaTitle: "Informations sur l'extension"
|
||||
_theme:
|
||||
title: "Voulez-vous installer ce thème ?"
|
||||
metaTitle: "Informations sur le thème"
|
||||
_meta:
|
||||
base: "Palette de couleurs de base"
|
||||
_vendorInfo:
|
||||
|
|
|
@ -280,7 +280,6 @@ deleteAreYouSure: "Apakah kamu yakin ingin menghapus \"{x}\"?"
|
|||
resetAreYouSure: "Yakin mau atur ulang?"
|
||||
areYouSure: "Apakah kamu yakin?"
|
||||
saved: "Telah disimpan"
|
||||
messaging: "Pesan"
|
||||
upload: "Unggah"
|
||||
keepOriginalUploading: "Simpan gambar asli"
|
||||
keepOriginalUploadingDescription: "Simpan gambar yang diunggah sebagaimana gambar aslinya. Bila dimatikan, versi tampilan web akan dihasilkan pada saat diunggah."
|
||||
|
@ -293,7 +292,6 @@ uploadFromUrlMayTakeTime: "Membutuhkan beberapa waktu hingga pengunggahan selesa
|
|||
explore: "Jelajahi"
|
||||
messageRead: "Telah dibaca"
|
||||
noMoreHistory: "Tidak ada sejarah lagi"
|
||||
startMessaging: "Mulai mengirim pesan"
|
||||
nUsersRead: "Dibaca oleh {n}"
|
||||
agreeTo: "Saya setuju kepada {0}"
|
||||
agree: "Setuju"
|
||||
|
@ -481,8 +479,6 @@ noteOf: "Catatan milik {user}"
|
|||
quoteAttached: "Dikutip"
|
||||
quoteQuestion: "Apakah kamu ingin menambahkan kutipan?"
|
||||
attachAsFileQuestion: "Teks dalam papan klip terlalu panjang. Apakah kamu ingin melampirkannya sebagai berkas teks?"
|
||||
noMessagesYet: "Tidak ada pesan"
|
||||
newMessageExists: "Kamu mendapatkan pesan baru"
|
||||
onlyOneFileCanBeAttached: "Kamu hanya dapat melampirkan satu berkas ke dalam pesan"
|
||||
signinRequired: "Silahkan login"
|
||||
invitations: "Undangan"
|
||||
|
@ -1261,6 +1257,16 @@ performance: "Kinerja"
|
|||
modified: "Diubah"
|
||||
thereAreNChanges: "Ada {n} perubahan"
|
||||
prohibitedWordsForNameOfUser: "Kata yang dilarang untuk nama pengguna"
|
||||
postForm: "Buat catatan"
|
||||
information: "Informasi"
|
||||
_chat:
|
||||
invitations: "Undang"
|
||||
noHistory: "Tidak ada riwayat"
|
||||
members: "Anggota"
|
||||
home: "Beranda"
|
||||
send: "Kirim"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_abuseUserReport:
|
||||
accept: "Setuju"
|
||||
reject: "Tolak"
|
||||
|
@ -1925,7 +1931,6 @@ _theme:
|
|||
header: "Header"
|
||||
navBg: "Latar belakang bilah samping"
|
||||
navFg: "Teks bilah samping"
|
||||
navHoverFg: "Teks bilah samping (Mengambang)"
|
||||
navActive: "Teks bilah samping (Aktif)"
|
||||
navIndicator: "Indikator bilah samping"
|
||||
link: "Tautan"
|
||||
|
@ -1948,11 +1953,8 @@ _theme:
|
|||
buttonHoverBg: "Latar belakang tombol (Mengambang)"
|
||||
inputBorder: "Batas bidang masukan"
|
||||
driveFolderBg: "Latar belakang folder drive"
|
||||
wallpaperOverlay: "Lapisan wallpaper"
|
||||
badge: "Lencana"
|
||||
messageBg: "Latar belakang obrolan"
|
||||
accentDarken: "Aksen (Gelap)"
|
||||
accentLighten: "Aksen (Terang)"
|
||||
fgHighlighted: "Teks yang disorot"
|
||||
_sfx:
|
||||
note: "Catatan"
|
||||
|
@ -2105,6 +2107,7 @@ _permissions:
|
|||
"read:clip-favorite": "Lihat klip yang difavoritkan"
|
||||
"read:federation": "Mendapatkan data federasi"
|
||||
"write:report-abuse": "Melaporkan pelanggaran"
|
||||
"write:chat": "Buat atau hapus obrolan"
|
||||
_auth:
|
||||
shareAccessTitle: "Mendapatkan ijin akses aplikasi"
|
||||
shareAccess: "Apakah kamu ingin mengijinkan \"{name}\" untuk mengakses akun ini?"
|
||||
|
@ -2489,10 +2492,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "Pastikan sumber dari sumber daya ini terpercaya sebelum melakukan pemasangan."
|
||||
_plugin:
|
||||
title: "Apakah kamu ingin memasang plugin ini?"
|
||||
metaTitle: "Informasi plugin"
|
||||
_theme:
|
||||
title: "Apakah kamu ingin memasang tema ini?"
|
||||
metaTitle: "Informasi tema"
|
||||
_meta:
|
||||
base: "Skema warna dasar"
|
||||
_vendorInfo:
|
||||
|
|
|
@ -1174,10 +1174,6 @@ export interface Locale extends ILocale {
|
|||
* 保存しました
|
||||
*/
|
||||
"saved": string;
|
||||
/**
|
||||
* チャット
|
||||
*/
|
||||
"messaging": string;
|
||||
/**
|
||||
* アップロード
|
||||
*/
|
||||
|
@ -1227,9 +1223,9 @@ export interface Locale extends ILocale {
|
|||
*/
|
||||
"noMoreHistory": string;
|
||||
/**
|
||||
* チャットを開始
|
||||
* チャットを始める
|
||||
*/
|
||||
"startMessaging": string;
|
||||
"startChat": string;
|
||||
/**
|
||||
* {n}人が読みました
|
||||
*/
|
||||
|
@ -1718,6 +1714,10 @@ export interface Locale extends ILocale {
|
|||
* ファイルが添付されたノートのみ
|
||||
*/
|
||||
"withFileAntenna": string;
|
||||
/**
|
||||
* センシティブなチャンネルのノートを除外
|
||||
*/
|
||||
"excludeNotesInSensitiveChannel": string;
|
||||
/**
|
||||
* ブラウザへのプッシュ通知を有効にする
|
||||
*/
|
||||
|
@ -1982,14 +1982,6 @@ export interface Locale extends ILocale {
|
|||
* クリップボードのテキストが長いです。テキストファイルとして添付しますか?
|
||||
*/
|
||||
"attachAsFileQuestion": string;
|
||||
/**
|
||||
* まだチャットはありません
|
||||
*/
|
||||
"noMessagesYet": string;
|
||||
/**
|
||||
* 新しいメッセージがあります
|
||||
*/
|
||||
"newMessageExists": string;
|
||||
/**
|
||||
* メッセージに添付できるファイルはひとつです
|
||||
*/
|
||||
|
@ -2810,6 +2802,10 @@ export interface Locale extends ILocale {
|
|||
* コピー
|
||||
*/
|
||||
"copy": string;
|
||||
/**
|
||||
* クリップボードにコピーされました
|
||||
*/
|
||||
"copiedToClipboard": string;
|
||||
/**
|
||||
* メトリクス
|
||||
*/
|
||||
|
@ -5278,6 +5274,481 @@ export interface Locale extends ILocale {
|
|||
* アクセシビリティ
|
||||
*/
|
||||
"accessibility": string;
|
||||
/**
|
||||
* 設定のプロファイル
|
||||
*/
|
||||
"preferencesProfile": string;
|
||||
/**
|
||||
* 設定IDをコピー
|
||||
*/
|
||||
"copyPreferenceId": string;
|
||||
/**
|
||||
* 初期値に戻す
|
||||
*/
|
||||
"resetToDefaultValue": string;
|
||||
/**
|
||||
* アカウントで上書き
|
||||
*/
|
||||
"overrideByAccount": string;
|
||||
/**
|
||||
* 無題
|
||||
*/
|
||||
"untitled": string;
|
||||
/**
|
||||
* 名前はありません
|
||||
*/
|
||||
"noName": string;
|
||||
/**
|
||||
* スキップ
|
||||
*/
|
||||
"skip": string;
|
||||
/**
|
||||
* 復元
|
||||
*/
|
||||
"restore": string;
|
||||
/**
|
||||
* デバイス間で同期
|
||||
*/
|
||||
"syncBetweenDevices": string;
|
||||
/**
|
||||
* サーバーに設定値が存在します
|
||||
*/
|
||||
"preferenceSyncConflictTitle": string;
|
||||
/**
|
||||
* 同期が有効にされた設定項目は設定値をサーバーに保存しますが、この設定項目のサーバーに保存された設定値が見つかりました。どちらの設定値で上書きしますか?
|
||||
*/
|
||||
"preferenceSyncConflictText": string;
|
||||
/**
|
||||
* サーバーの設定値
|
||||
*/
|
||||
"preferenceSyncConflictChoiceServer": string;
|
||||
/**
|
||||
* デバイスの設定値
|
||||
*/
|
||||
"preferenceSyncConflictChoiceDevice": string;
|
||||
/**
|
||||
* 同期の有効化をキャンセル
|
||||
*/
|
||||
"preferenceSyncConflictChoiceCancel": string;
|
||||
/**
|
||||
* ペースト
|
||||
*/
|
||||
"paste": string;
|
||||
/**
|
||||
* 絵文字パレット
|
||||
*/
|
||||
"emojiPalette": string;
|
||||
/**
|
||||
* 投稿フォーム
|
||||
*/
|
||||
"postForm": string;
|
||||
/**
|
||||
* 文字数
|
||||
*/
|
||||
"textCount": string;
|
||||
/**
|
||||
* 情報
|
||||
*/
|
||||
"information": string;
|
||||
/**
|
||||
* チャット
|
||||
*/
|
||||
"chat": string;
|
||||
/**
|
||||
* 旧設定情報を移行
|
||||
*/
|
||||
"migrateOldSettings": string;
|
||||
/**
|
||||
* 通常これは自動で行われていますが、何らかの理由により上手く移行されなかった場合は手動で移行処理をトリガーできます。現在の設定情報は上書きされます。
|
||||
*/
|
||||
"migrateOldSettings_description": string;
|
||||
/**
|
||||
* 圧縮
|
||||
*/
|
||||
"compress": string;
|
||||
/**
|
||||
* 右
|
||||
*/
|
||||
"right": string;
|
||||
/**
|
||||
* 下
|
||||
*/
|
||||
"bottom": string;
|
||||
/**
|
||||
* 上
|
||||
*/
|
||||
"top": string;
|
||||
/**
|
||||
* 埋め込み
|
||||
*/
|
||||
"embed": string;
|
||||
/**
|
||||
* 設定を移行しています。しばらくお待ちください... (後ほど、設定→その他→旧設定情報を移行 で手動で移行することもできます)
|
||||
*/
|
||||
"settingsMigrating": string;
|
||||
/**
|
||||
* 読み取り専用
|
||||
*/
|
||||
"readonly": string;
|
||||
/**
|
||||
* デッキへ戻る
|
||||
*/
|
||||
"goToDeck": string;
|
||||
"_chat": {
|
||||
/**
|
||||
* まだメッセージはありません
|
||||
*/
|
||||
"noMessagesYet": string;
|
||||
/**
|
||||
* 新しいメッセージ
|
||||
*/
|
||||
"newMessage": string;
|
||||
/**
|
||||
* 個人チャット
|
||||
*/
|
||||
"individualChat": string;
|
||||
/**
|
||||
* 特定ユーザーとの一対一のチャットができます。
|
||||
*/
|
||||
"individualChat_description": string;
|
||||
/**
|
||||
* ルームチャット
|
||||
*/
|
||||
"roomChat": string;
|
||||
/**
|
||||
* 複数人でのチャットができます。
|
||||
* また、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。
|
||||
*/
|
||||
"roomChat_description": string;
|
||||
/**
|
||||
* ルームを作成
|
||||
*/
|
||||
"createRoom": string;
|
||||
/**
|
||||
* ユーザーを招待してチャットを始めましょう
|
||||
*/
|
||||
"inviteUserToChat": string;
|
||||
/**
|
||||
* 作成したルーム
|
||||
*/
|
||||
"yourRooms": string;
|
||||
/**
|
||||
* 参加中のルーム
|
||||
*/
|
||||
"joiningRooms": string;
|
||||
/**
|
||||
* 招待
|
||||
*/
|
||||
"invitations": string;
|
||||
/**
|
||||
* 招待はありません
|
||||
*/
|
||||
"noInvitations": string;
|
||||
/**
|
||||
* 履歴
|
||||
*/
|
||||
"history": string;
|
||||
/**
|
||||
* 履歴はありません
|
||||
*/
|
||||
"noHistory": string;
|
||||
/**
|
||||
* ルームはありません
|
||||
*/
|
||||
"noRooms": string;
|
||||
/**
|
||||
* ユーザーを招待
|
||||
*/
|
||||
"inviteUser": string;
|
||||
/**
|
||||
* 送信した招待
|
||||
*/
|
||||
"sentInvitations": string;
|
||||
/**
|
||||
* 参加
|
||||
*/
|
||||
"join": string;
|
||||
/**
|
||||
* 無視
|
||||
*/
|
||||
"ignore": string;
|
||||
/**
|
||||
* ルームから退出
|
||||
*/
|
||||
"leave": string;
|
||||
/**
|
||||
* メンバー
|
||||
*/
|
||||
"members": string;
|
||||
/**
|
||||
* メッセージを検索
|
||||
*/
|
||||
"searchMessages": string;
|
||||
/**
|
||||
* ホーム
|
||||
*/
|
||||
"home": string;
|
||||
/**
|
||||
* 送信
|
||||
*/
|
||||
"send": string;
|
||||
/**
|
||||
* 改行
|
||||
*/
|
||||
"newline": string;
|
||||
/**
|
||||
* このルームをミュート
|
||||
*/
|
||||
"muteThisRoom": string;
|
||||
/**
|
||||
* ルームを削除
|
||||
*/
|
||||
"deleteRoom": string;
|
||||
/**
|
||||
* このサーバー、またはこのアカウントでチャットは有効化されていません。
|
||||
*/
|
||||
"chatNotAvailableForThisAccountOrServer": string;
|
||||
/**
|
||||
* このサーバー、またはこのアカウントでチャットは読み取り専用となっています。新たに書き込んだり、チャットルームを作成・参加したりすることはできません。
|
||||
*/
|
||||
"chatIsReadOnlyForThisAccountOrServer": string;
|
||||
/**
|
||||
* 相手のアカウントでチャット機能が使えない状態になっています。
|
||||
*/
|
||||
"chatNotAvailableInOtherAccount": string;
|
||||
/**
|
||||
* このユーザーとのチャットを開始できません
|
||||
*/
|
||||
"cannotChatWithTheUser": string;
|
||||
/**
|
||||
* チャットが使えない状態になっているか、相手がチャットを開放していません。
|
||||
*/
|
||||
"cannotChatWithTheUser_description": string;
|
||||
/**
|
||||
* チャットする
|
||||
*/
|
||||
"chatWithThisUser": string;
|
||||
/**
|
||||
* このユーザーはフォロワーからのみチャットを受け付けています。
|
||||
*/
|
||||
"thisUserAllowsChatOnlyFromFollowers": string;
|
||||
/**
|
||||
* このユーザーは、このユーザーがフォローしているユーザーからのみチャットを受け付けています。
|
||||
*/
|
||||
"thisUserAllowsChatOnlyFromFollowing": string;
|
||||
/**
|
||||
* このユーザーは相互フォローのユーザーからのみチャットを受け付けています。
|
||||
*/
|
||||
"thisUserAllowsChatOnlyFromMutualFollowing": string;
|
||||
/**
|
||||
* このユーザーは誰からもチャットを受け付けていません。
|
||||
*/
|
||||
"thisUserNotAllowedChatAnyone": string;
|
||||
/**
|
||||
* チャットを許可する相手
|
||||
*/
|
||||
"chatAllowedUsers": string;
|
||||
/**
|
||||
* 自分からチャットメッセージを送った相手とはこの設定に関わらずチャットが可能です。
|
||||
*/
|
||||
"chatAllowedUsers_note": string;
|
||||
"_chatAllowedUsers": {
|
||||
/**
|
||||
* 誰でも
|
||||
*/
|
||||
"everyone": string;
|
||||
/**
|
||||
* 自分のフォロワーのみ
|
||||
*/
|
||||
"followers": string;
|
||||
/**
|
||||
* 自分がフォローしているユーザーのみ
|
||||
*/
|
||||
"following": string;
|
||||
/**
|
||||
* 相互フォローのユーザーのみ
|
||||
*/
|
||||
"mutual": string;
|
||||
/**
|
||||
* 誰も許可しない
|
||||
*/
|
||||
"none": string;
|
||||
};
|
||||
};
|
||||
"_emojiPalette": {
|
||||
/**
|
||||
* パレット
|
||||
*/
|
||||
"palettes": string;
|
||||
/**
|
||||
* パレットのデバイス間同期を有効にする
|
||||
*/
|
||||
"enableSyncBetweenDevicesForPalettes": string;
|
||||
/**
|
||||
* メインで使用するパレット
|
||||
*/
|
||||
"paletteForMain": string;
|
||||
/**
|
||||
* リアクションで使用するパレット
|
||||
*/
|
||||
"paletteForReaction": string;
|
||||
};
|
||||
"_settings": {
|
||||
/**
|
||||
* ドライブの管理と設定、使用量の確認、ファイルをアップロードする際の設定を行えます。
|
||||
*/
|
||||
"driveBanner": string;
|
||||
/**
|
||||
* プラグインを利用するとクライアントの機能を拡張することができます。プラグインのインストール、個別の設定と管理が行えます。
|
||||
*/
|
||||
"pluginBanner": string;
|
||||
/**
|
||||
* サーバーからの受信する通知の種類と範囲や、プッシュ通知の設定が行えます。
|
||||
*/
|
||||
"notificationsBanner": string;
|
||||
/**
|
||||
* API
|
||||
*/
|
||||
"api": string;
|
||||
/**
|
||||
* Webhook
|
||||
*/
|
||||
"webhook": string;
|
||||
/**
|
||||
* サービス連携
|
||||
*/
|
||||
"serviceConnection": string;
|
||||
/**
|
||||
* 外部のアプリ・サービスと連携するためのアクセストークンやWebhookの管理と設定が行えます。
|
||||
*/
|
||||
"serviceConnectionBanner": string;
|
||||
/**
|
||||
* アカウントのデータ
|
||||
*/
|
||||
"accountData": string;
|
||||
/**
|
||||
* アカウントデータのアーカイブをエクスポート/インポートして管理できます。
|
||||
*/
|
||||
"accountDataBanner": string;
|
||||
/**
|
||||
* 非表示にするコンテンツの設定や、特定のユーザーからのアクションを制限する設定と管理を行えます。
|
||||
*/
|
||||
"muteAndBlockBanner": string;
|
||||
/**
|
||||
* クライアントの視覚や動作に関するパーソナライズを行い、より最適に使用できるように設定できます。
|
||||
*/
|
||||
"accessibilityBanner": string;
|
||||
/**
|
||||
* コンテンツの公開範囲、見つけやすさ、フォローの承認制などアカウントのプライバシーに関する設定を行えます。
|
||||
*/
|
||||
"privacyBanner": string;
|
||||
/**
|
||||
* パスワード、ログイン方法、認証アプリ、パスキーなどアカウントのセキュリティに関する設定を行えます。
|
||||
*/
|
||||
"securityBanner": string;
|
||||
/**
|
||||
* 好みに応じた、クライアントの全体的な動作の設定が行えます。
|
||||
*/
|
||||
"preferencesBanner": string;
|
||||
/**
|
||||
* 好みに応じた、クライアントの見た目・表示方法に関する設定が行えます。
|
||||
*/
|
||||
"appearanceBanner": string;
|
||||
/**
|
||||
* クライアントで再生するサウンドの設定が行えます。
|
||||
*/
|
||||
"soundsBanner": string;
|
||||
/**
|
||||
* タイムラインとノート
|
||||
*/
|
||||
"timelineAndNote": string;
|
||||
/**
|
||||
* 全てのテキスト要素を選択可能にする
|
||||
*/
|
||||
"makeEveryTextElementsSelectable": string;
|
||||
/**
|
||||
* 有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。
|
||||
*/
|
||||
"makeEveryTextElementsSelectable_description": string;
|
||||
/**
|
||||
* アイコンをスクロールに追従させる
|
||||
*/
|
||||
"useStickyIcons": string;
|
||||
/**
|
||||
* ナビゲーションバーに副ボタンを表示
|
||||
*/
|
||||
"showNavbarSubButtons": string;
|
||||
/**
|
||||
* オンのとき
|
||||
*/
|
||||
"ifOn": string;
|
||||
/**
|
||||
* オフのとき
|
||||
*/
|
||||
"ifOff": string;
|
||||
/**
|
||||
* デバイス間でインストールしたテーマを同期
|
||||
*/
|
||||
"enableSyncThemesBetweenDevices": string;
|
||||
"_chat": {
|
||||
/**
|
||||
* 送信者の名前を表示
|
||||
*/
|
||||
"showSenderName": string;
|
||||
/**
|
||||
* Enterで送信
|
||||
*/
|
||||
"sendOnEnter": string;
|
||||
};
|
||||
};
|
||||
"_preferencesProfile": {
|
||||
/**
|
||||
* プロファイル名
|
||||
*/
|
||||
"profileName": string;
|
||||
/**
|
||||
* このデバイスを識別する名前を設定してください。
|
||||
*/
|
||||
"profileNameDescription": string;
|
||||
/**
|
||||
* 例: 「メインPC」、「スマホ」など
|
||||
*/
|
||||
"profileNameDescription2": string;
|
||||
};
|
||||
"_preferencesBackup": {
|
||||
/**
|
||||
* 自動バックアップ
|
||||
*/
|
||||
"autoBackup": string;
|
||||
/**
|
||||
* バックアップから復元
|
||||
*/
|
||||
"restoreFromBackup": string;
|
||||
/**
|
||||
* バックアップが見つかりませんでした
|
||||
*/
|
||||
"noBackupsFoundTitle": string;
|
||||
/**
|
||||
* 自動で作成されたバックアップは見つかりませんでしたが、バックアップファイルを手動で保存している場合、それをインポートして復元することはできます。
|
||||
*/
|
||||
"noBackupsFoundDescription": string;
|
||||
/**
|
||||
* 復元するバックアップを選択してください
|
||||
*/
|
||||
"selectBackupToRestore": string;
|
||||
/**
|
||||
* 自動バックアップを有効にするにはプロファイル名の設定が必要です。
|
||||
*/
|
||||
"youNeedToNameYourProfileToEnableAutoBackup": string;
|
||||
/**
|
||||
* このデバイスで設定の自動バックアップは有効になっていません。
|
||||
*/
|
||||
"autoPreferencesBackupIsNotEnabledForThisDevice": string;
|
||||
/**
|
||||
* 設定のバックアップが見つかりました
|
||||
*/
|
||||
"backupFound": string;
|
||||
};
|
||||
"_accountSettings": {
|
||||
/**
|
||||
* コンテンツの表示にログインを必須にする
|
||||
|
@ -5315,6 +5786,10 @@ export interface Locale extends ILocale {
|
|||
* リモートサーバーに連合されたノートには効果が及ばない場合があります。
|
||||
*/
|
||||
"mayNotEffectForFederatedNotes": string;
|
||||
/**
|
||||
* これらの制限は簡易的なものです。リモートサーバーでの閲覧やモデレーション時など、一部のシチュエーションでは適用されない場合があります。
|
||||
*/
|
||||
"mayNotEffectSomeSituations": string;
|
||||
/**
|
||||
* 指定した時間を経過しているノート
|
||||
*/
|
||||
|
@ -6902,6 +7377,14 @@ export interface Locale extends ILocale {
|
|||
* 数値が大きいほどUI上で先頭に表示されます。
|
||||
*/
|
||||
"descriptionOfDisplayOrder": string;
|
||||
/**
|
||||
* アサイン状態を移行先アカウントにも引き継ぐ
|
||||
*/
|
||||
"preserveAssignmentOnMoveAccount": string;
|
||||
/**
|
||||
* オンにすると、このロールが付与されたアカウントが移行された際に、移行先アカウントにもこのロールが引き継がれるようになります。
|
||||
*/
|
||||
"preserveAssignmentOnMoveAccount_description": string;
|
||||
/**
|
||||
* モデレーターのメンバー編集を許可
|
||||
*/
|
||||
|
@ -7057,6 +7540,10 @@ export interface Locale extends ILocale {
|
|||
* リストのインポートを許可
|
||||
*/
|
||||
"canImportUserLists": string;
|
||||
/**
|
||||
* チャットを許可
|
||||
*/
|
||||
"chatAvailability": string;
|
||||
};
|
||||
"_condition": {
|
||||
/**
|
||||
|
@ -7662,6 +8149,10 @@ export interface Locale extends ILocale {
|
|||
* 標準のテーマ
|
||||
*/
|
||||
"builtinThemes": string;
|
||||
/**
|
||||
* サーバーのテーマ
|
||||
*/
|
||||
"instanceTheme": string;
|
||||
/**
|
||||
* そのテーマは既にインストールされています
|
||||
*/
|
||||
|
@ -7780,23 +8271,19 @@ export interface Locale extends ILocale {
|
|||
*/
|
||||
"header": string;
|
||||
/**
|
||||
* サイドバーの背景
|
||||
* ナビゲーションバーの背景
|
||||
*/
|
||||
"navBg": string;
|
||||
/**
|
||||
* サイドバーの文字
|
||||
* ナビゲーションバーの文字
|
||||
*/
|
||||
"navFg": string;
|
||||
/**
|
||||
* サイドバー文字(ホバー)
|
||||
*/
|
||||
"navHoverFg": string;
|
||||
/**
|
||||
* サイドバー文字(アクティブ)
|
||||
* ナビゲーションバー文字(アクティブ)
|
||||
*/
|
||||
"navActive": string;
|
||||
/**
|
||||
* サイドバーのインジケーター
|
||||
* ナビゲーションバーのインジケーター
|
||||
*/
|
||||
"navIndicator": string;
|
||||
/**
|
||||
|
@ -7816,7 +8303,7 @@ export interface Locale extends ILocale {
|
|||
*/
|
||||
"mentionMe": string;
|
||||
/**
|
||||
* Renote
|
||||
* リノート
|
||||
*/
|
||||
"renote": string;
|
||||
/**
|
||||
|
@ -7879,10 +8366,6 @@ export interface Locale extends ILocale {
|
|||
* ドライブフォルダーの背景
|
||||
*/
|
||||
"driveFolderBg": string;
|
||||
/**
|
||||
* 壁紙のオーバーレイ
|
||||
*/
|
||||
"wallpaperOverlay": string;
|
||||
/**
|
||||
* バッジ
|
||||
*/
|
||||
|
@ -7891,14 +8374,6 @@ export interface Locale extends ILocale {
|
|||
* チャットの背景
|
||||
*/
|
||||
"messageBg": string;
|
||||
/**
|
||||
* アクセント (暗め)
|
||||
*/
|
||||
"accentDarken": string;
|
||||
/**
|
||||
* アクセント (明るめ)
|
||||
*/
|
||||
"accentLighten": string;
|
||||
/**
|
||||
* 強調された文字
|
||||
*/
|
||||
|
@ -7922,6 +8397,10 @@ export interface Locale extends ILocale {
|
|||
* リアクション選択時
|
||||
*/
|
||||
"reaction": string;
|
||||
/**
|
||||
* チャットのメッセージ
|
||||
*/
|
||||
"chatMessage": string;
|
||||
};
|
||||
"_soundSettings": {
|
||||
/**
|
||||
|
@ -8494,6 +8973,14 @@ export interface Locale extends ILocale {
|
|||
* 違反を報告する
|
||||
*/
|
||||
"write:report-abuse": string;
|
||||
/**
|
||||
* チャットを操作する
|
||||
*/
|
||||
"write:chat": string;
|
||||
/**
|
||||
* チャットを閲覧する
|
||||
*/
|
||||
"read:chat": string;
|
||||
};
|
||||
"_auth": {
|
||||
/**
|
||||
|
@ -9444,6 +9931,10 @@ export interface Locale extends ILocale {
|
|||
* ロールが付与されました
|
||||
*/
|
||||
"roleAssigned": string;
|
||||
/**
|
||||
* チャットルームへ招待されました
|
||||
*/
|
||||
"chatRoomInvitationReceived": string;
|
||||
/**
|
||||
* プッシュ通知の更新をしました
|
||||
*/
|
||||
|
@ -9553,6 +10044,10 @@ export interface Locale extends ILocale {
|
|||
* ロールが付与された
|
||||
*/
|
||||
"roleAssigned": string;
|
||||
/**
|
||||
* チャットルームへ招待された
|
||||
*/
|
||||
"chatRoomInvitationReceived": string;
|
||||
/**
|
||||
* 実績の獲得
|
||||
*/
|
||||
|
@ -9565,6 +10060,10 @@ export interface Locale extends ILocale {
|
|||
* ログイン
|
||||
*/
|
||||
"login": string;
|
||||
/**
|
||||
* アクセストークンの作成
|
||||
*/
|
||||
"createToken": string;
|
||||
/**
|
||||
* 通知のテスト
|
||||
*/
|
||||
|
@ -9598,6 +10097,18 @@ export interface Locale extends ILocale {
|
|||
* カラムの寄せ
|
||||
*/
|
||||
"columnAlign": string;
|
||||
/**
|
||||
* カラム間のマージン
|
||||
*/
|
||||
"columnGap": string;
|
||||
/**
|
||||
* デッキメニューの位置
|
||||
*/
|
||||
"deckMenuPosition": string;
|
||||
/**
|
||||
* ナビゲーションバーの位置
|
||||
*/
|
||||
"navbarPosition": string;
|
||||
/**
|
||||
* カラムを追加
|
||||
*/
|
||||
|
@ -9651,7 +10162,7 @@ export interface Locale extends ILocale {
|
|||
*/
|
||||
"introduction": string;
|
||||
/**
|
||||
* 画面の右にある + を押して、いつでもカラムを追加できます。
|
||||
* カラムを追加するには、画面の + をクリックします。
|
||||
*/
|
||||
"introduction2": string;
|
||||
/**
|
||||
|
@ -9670,6 +10181,10 @@ export interface Locale extends ILocale {
|
|||
* 幅を自動調整
|
||||
*/
|
||||
"flexible": string;
|
||||
/**
|
||||
* プロファイル情報のデバイス間同期を有効にする
|
||||
*/
|
||||
"enableSyncBetweenDevicesForProfiles": string;
|
||||
"_columns": {
|
||||
/**
|
||||
* メイン
|
||||
|
@ -10074,6 +10589,10 @@ export interface Locale extends ILocale {
|
|||
* ギャラリーの投稿を削除
|
||||
*/
|
||||
"deleteGalleryPost": string;
|
||||
/**
|
||||
* チャットルームを削除
|
||||
*/
|
||||
"deleteChatRoom": string;
|
||||
/**
|
||||
* プロキシアカウントの説明を更新
|
||||
*/
|
||||
|
@ -10123,20 +10642,12 @@ export interface Locale extends ILocale {
|
|||
* このプラグインをインストールしますか?
|
||||
*/
|
||||
"title": string;
|
||||
/**
|
||||
* プラグイン情報
|
||||
*/
|
||||
"metaTitle": string;
|
||||
};
|
||||
"_theme": {
|
||||
/**
|
||||
* このテーマをインストールしますか?
|
||||
*/
|
||||
"title": string;
|
||||
/**
|
||||
* テーマ情報
|
||||
*/
|
||||
"metaTitle": string;
|
||||
};
|
||||
"_meta": {
|
||||
/**
|
||||
|
|
|
@ -289,7 +289,6 @@ deleteAreYouSure: "Vuoi davvero eliminare \"{x}\"?"
|
|||
resetAreYouSure: "Ripristinare?"
|
||||
areYouSure: "Confermi?"
|
||||
saved: "Salvato"
|
||||
messaging: "Messaggi"
|
||||
upload: "Carica"
|
||||
keepOriginalUploading: "Conservare l'immagine originale."
|
||||
keepOriginalUploadingDescription: "Conserva la versione originale quando si caricano le immagini. Se è disattivato, il browser genera l'immagine per la pubblicazione sul Web durante il caricamento."
|
||||
|
@ -302,7 +301,7 @@ uploadFromUrlMayTakeTime: "Il caricamento del file può richiedere tempo."
|
|||
explore: "Esplora"
|
||||
messageRead: "Visualizzato"
|
||||
noMoreHistory: "Non c'è più cronologia da visualizzare"
|
||||
startMessaging: "Nuovo messaggio"
|
||||
startChat: "Inizia a chattare"
|
||||
nUsersRead: "Letto da {n} persone"
|
||||
agreeTo: "Sono d'accordo con {0}"
|
||||
agree: "Accetto"
|
||||
|
@ -491,8 +490,6 @@ noteOf: "Note di {user}"
|
|||
quoteAttached: "Citazione allegata"
|
||||
quoteQuestion: "Vuoi aggiungere una citazione?"
|
||||
attachAsFileQuestion: "Il testo copiato eccede le dimensioni, vuoi allegarlo?"
|
||||
noMessagesYet: "Ancora nessuna chat"
|
||||
newMessageExists: "Hai ricevuto un nuovo messaggio"
|
||||
onlyOneFileCanBeAttached: "È possibile allegare al messaggio soltanto uno file"
|
||||
signinRequired: "Occorre avere un profilo registrato su questa istanza"
|
||||
signinOrContinueOnRemote: "Per continuare, devi accedere alla tua istanza o registrarti su questa e poi accedere"
|
||||
|
@ -525,7 +522,7 @@ showNoteActionsOnlyHover: "Mostra le azioni delle Note solo al passaggio del mou
|
|||
showReactionsCount: "Visualizza il numero di reazioni su una nota"
|
||||
noHistory: "Nessuna cronologia"
|
||||
signinHistory: "Storico degli accessi al profilo"
|
||||
enableAdvancedMfm: "Attiva MFM avanzati"
|
||||
enableAdvancedMfm: "Attivare i Misskey Flavoured Markdown (MFM) avanzati"
|
||||
enableAnimatedMfm: "Attiva MFM animati"
|
||||
doing: "In corso..."
|
||||
category: "Categoria"
|
||||
|
@ -606,9 +603,9 @@ scratchpad: "ScratchPad"
|
|||
scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScript. È possibile scrivere, eseguire e confermare i risultati dell'interazione del codice con Misskey."
|
||||
uiInspector: "UI Inspector"
|
||||
uiInspectorDescription: "Puoi visualizzare un elenco di elementi UI presenti in memoria. I componenti dell'interfaccia utente vengono generati dalle funzioni Ui:C:."
|
||||
output: "Uscita"
|
||||
output: "Output"
|
||||
script: "Script"
|
||||
disablePagesScript: "Disabilita AiScript nelle pagine"
|
||||
disablePagesScript: "Disabilitare AiScript nelle pagine"
|
||||
updateRemoteUser: "Aggiorna dati dal profilo remoto"
|
||||
unsetUserAvatar: "Rimozione foto profilo"
|
||||
unsetUserAvatarConfirm: "Vuoi davvero rimuovere la foto profilo?"
|
||||
|
@ -666,7 +663,7 @@ generateAccessToken: "Genera token di accesso"
|
|||
permission: "Autorizzazioni "
|
||||
adminPermission: "Privilegi amministrativi"
|
||||
enableAll: "Abilita tutto"
|
||||
disableAll: "Disabilita tutto"
|
||||
disableAll: "Disabilitare tutto"
|
||||
tokenRequested: "Autorizza accesso al profilo"
|
||||
pluginTokenRequestedDescription: "Il plugin potrà utilizzare le autorizzazioni impostate qui."
|
||||
notificationType: "Tipo di notifiche"
|
||||
|
@ -698,6 +695,7 @@ userSaysSomethingAbout: "{name} ha Notato a riguardo di \"{word}\""
|
|||
makeActive: "Attiva"
|
||||
display: "Visualizza"
|
||||
copy: "Copia"
|
||||
copiedToClipboard: "Copiato negli appunti"
|
||||
metrics: "Statistiche"
|
||||
overview: "Anteprima"
|
||||
logs: "Log"
|
||||
|
@ -766,9 +764,9 @@ driveUsage: "Utilizzazione del Drive"
|
|||
noCrawle: "Rifiuta l'indicizzazione dai robot."
|
||||
noCrawleDescription: "Richiedi che i motori di ricerca non indicizzino la tua pagina di profilo, le tue note, pagine, ecc."
|
||||
lockedAccountInfo: "A meno che non imposti la visibilità delle tue note su \"Solo ai follower\", le tue note sono visibili da tutti, anche se hai configurato l'account per confermare manualmente le richieste di follow."
|
||||
alwaysMarkSensitive: "Segnare gli allegati come espliciti come opzione predefinita"
|
||||
alwaysMarkSensitive: "Segnare automaticamente come espliciti gli allegati"
|
||||
loadRawImages: "Visualizza le intere immagini allegate invece delle miniature."
|
||||
disableShowingAnimatedImages: "Disabilita le immagini animate"
|
||||
disableShowingAnimatedImages: "Disabilitare le immagini animate"
|
||||
highlightSensitiveMedia: "Evidenzia i media espliciti"
|
||||
verificationEmailSent: "Una mail di verifica è stata inviata. Si prega di accedere al collegamento per compiere la verifica."
|
||||
notSet: "Non impostato"
|
||||
|
@ -973,7 +971,7 @@ check: "Verifica"
|
|||
driveCapOverrideLabel: "Modificare la capienza del Drive per questo profilo"
|
||||
driveCapOverrideCaption: "Se viene specificato meno di 0, viene annullato."
|
||||
requireAdminForView: "Per visualizzarli, è necessario aver effettuato l'accesso con un profilo amministratore."
|
||||
isSystemAccount: "Questi profili vengono creati e gestiti automaticamente dal sistema"
|
||||
isSystemAccount: "Si tratta di un profilo creato e gestito automaticamente dal sistema."
|
||||
typeToConfirm: "Digita {x} per continuare"
|
||||
deleteAccount: "Eliminazione profilo"
|
||||
document: "Documentazione"
|
||||
|
@ -1090,7 +1088,7 @@ notesSearchNotAvailable: "Non è possibile cercare tra le Note."
|
|||
license: "Licenza"
|
||||
unfavoriteConfirm: "Vuoi davvero rimuovere la preferenza?"
|
||||
myClips: "Le mie Clip"
|
||||
drivecleaner: "Drive cleaner"
|
||||
drivecleaner: "Pulizia del Drive"
|
||||
retryAllQueuesNow: "Ritenta di consumare tutte le code"
|
||||
retryAllQueuesConfirmTitle: "Vuoi ritentare adesso?"
|
||||
retryAllQueuesConfirmText: "Potrebbe sovraccaricare il server temporaneamente."
|
||||
|
@ -1194,7 +1192,7 @@ renotes: "Rinota"
|
|||
loadReplies: "Leggi le risposte"
|
||||
loadConversation: "Leggi la conversazione"
|
||||
pinnedList: "Elenco in primo piano"
|
||||
keepScreenOn: "Mantieni lo schermo acceso"
|
||||
keepScreenOn: "Mantenere lo schermo acceso"
|
||||
verifiedLink: "Abbiamo confermato la validità di questo collegamento"
|
||||
notifyNotes: "Notifica nuove Note"
|
||||
unnotifyNotes: "Interrompi le notifiche di nuove Note"
|
||||
|
@ -1236,7 +1234,7 @@ flip: "Inverti"
|
|||
showAvatarDecorations: "Mostra decorazione della foto profilo"
|
||||
releaseToRefresh: "Rilascia per aggiornare"
|
||||
refreshing: "Aggiornamento..."
|
||||
pullDownToRefresh: "Trascina per aggiornare"
|
||||
pullDownToRefresh: "Trascinare per aggiornare"
|
||||
disableStreamingTimeline: "Disabilitare gli aggiornamenti della TL in tempo reale"
|
||||
useGroupedNotifications: "Mostra le notifiche raggruppate"
|
||||
signupPendingError: "Si è verificato un problema durante la verifica del tuo indirizzo email. Potrebbe essere scaduto il collegamento temporaneo."
|
||||
|
@ -1264,7 +1262,7 @@ backToTitle: "Torna al titolo"
|
|||
hemisphere: "Geolocalizzazione"
|
||||
withSensitive: "Mostra le Note con allegati espliciti"
|
||||
userSaysSomethingSensitive: "Note da {name} con allegati espliciti"
|
||||
enableHorizontalSwipe: "Trascina per invertire i tab"
|
||||
enableHorizontalSwipe: "Trascinare per invertire le colonne"
|
||||
loading: "Caricamento"
|
||||
surrender: "Annulla"
|
||||
gameRetry: "Riprova"
|
||||
|
@ -1315,6 +1313,124 @@ markAsSensitiveConfirm: "Vuoi davvero indicare questo contenuto multimediale com
|
|||
unmarkAsSensitiveConfirm: "Vuoi davvero indicare come non esplicito il contenuto multimediale?"
|
||||
preferences: "Preferenze"
|
||||
accessibility: "Accessibilità"
|
||||
preferencesProfile: "Profilo preferenze"
|
||||
copyPreferenceId: "Copia ID preferenze"
|
||||
resetToDefaultValue: "Ripristina a predefinito"
|
||||
overrideByAccount: "Sovrascrivere col profilo"
|
||||
untitled: "Senza titolo"
|
||||
noName: "Senza nome"
|
||||
skip: "Salta"
|
||||
restore: "Ripristina"
|
||||
syncBetweenDevices: "Sincronizzazione tra i dispositivi"
|
||||
preferenceSyncConflictTitle: "Sul server esiste già il valore impostato"
|
||||
preferenceSyncConflictText: "Le impostazione sincronizzata salverà il valore sul server. Però, bada che esiste già un valore sul server. Quale vorresti sovrascrivere?"
|
||||
preferenceSyncConflictChoiceServer: "Valore del server"
|
||||
preferenceSyncConflictChoiceDevice: "Valore del dispositivo"
|
||||
preferenceSyncConflictChoiceCancel: "Annulla la sincronizzazione"
|
||||
paste: "Incolla"
|
||||
emojiPalette: "Tavolozza emoji"
|
||||
postForm: "Finestra di pubblicazione"
|
||||
textCount: "Il numero di caratteri"
|
||||
information: "Informazioni"
|
||||
chat: "Chat"
|
||||
migrateOldSettings: "Migrare le vecchie impostazioni"
|
||||
migrateOldSettings_description: "Di solito, viene fatto automaticamente. Se per qualche motivo non fossero migrate con successo, è possibile avviare il processo di migrazione manualmente, sovrascrivendo le configurazioni attuali."
|
||||
compress: "Comprimi"
|
||||
right: "Destra"
|
||||
bottom: "Sotto"
|
||||
top: "Sopra"
|
||||
embed: "Incorporare"
|
||||
_chat:
|
||||
noMessagesYet: "Ancora nessun messaggio"
|
||||
newMessage: "Nuovo messaggio"
|
||||
individualChat: "Chat individuale"
|
||||
individualChat_description: "Puoi chattare con una persona specifica."
|
||||
roomChat: "Stanza di chat"
|
||||
roomChat_description: "Puoi chattare con più persone.\nInoltre, anche le persone che non consentono chat personalizzate possono chattare se gli altri accettano."
|
||||
createRoom: "Crea stanza"
|
||||
inviteUserToChat: "Invita a chattare altre persone"
|
||||
yourRooms: "Le tue stanze"
|
||||
joiningRooms: "Stanze a cui partecipi"
|
||||
invitations: "Invita"
|
||||
noInvitations: "Nessun invito"
|
||||
history: "Cronologia"
|
||||
noHistory: "Nessuna cronologia"
|
||||
noRooms: "Nessuna stanza"
|
||||
inviteUser: "Invita"
|
||||
sentInvitations: "Inviti spediti"
|
||||
join: "Entra"
|
||||
ignore: "Ignora"
|
||||
leave: "Esci"
|
||||
members: "Membri"
|
||||
searchMessages: "Cerca messaggi"
|
||||
home: "Home"
|
||||
send: "Inviare"
|
||||
newline: "Nuova riga"
|
||||
muteThisRoom: "Silenzia stanza"
|
||||
deleteRoom: "Elimina stanza"
|
||||
chatNotAvailableForThisAccountOrServer: "Questo server, o questo profilo ha disabilitato la chat."
|
||||
chatNotAvailableInOtherAccount: "La chat non è disponibile nel profilo dell'altra persona."
|
||||
cannotChatWithTheUser: "Impossibile chattare con questa persona"
|
||||
cannotChatWithTheUser_description: "La chat potrebbe non essere disponibile, oppure l'altra persona potrebbe non esserlo."
|
||||
chatWithThisUser: "Chatta con questa persona"
|
||||
thisUserAllowsChatOnlyFromFollowers: "Questa persona permette di chattare soltanto i propri Follower."
|
||||
thisUserAllowsChatOnlyFromFollowing: "Questa persona permette di chattare soltanto ai suoi Follow."
|
||||
thisUserAllowsChatOnlyFromMutualFollowing: "Questa persona permette di chattare solo a relazioni reciproche."
|
||||
thisUserNotAllowedChatAnyone: "Questa persona non permette di chattare a nessuno."
|
||||
chatAllowedUsers: "Persone ammesse alla chat"
|
||||
chatAllowedUsers_note: "Puoi chattare con le persone a cui hai già inviato un messaggio, indipendentemente da questa impostazione."
|
||||
_chatAllowedUsers:
|
||||
everyone: "Chiunque"
|
||||
followers: "Solo i tuoi Follower"
|
||||
following: "Solo i tuoi Follow"
|
||||
mutual: "Solo relazioni reciproche"
|
||||
none: "Nessuno"
|
||||
_emojiPalette:
|
||||
palettes: "Tavolozza"
|
||||
enableSyncBetweenDevicesForPalettes: "Attiva la sincronizzazione tra dispositivi"
|
||||
paletteForMain: "Tavolozza principale"
|
||||
paletteForReaction: "Tavolozza per reazioni"
|
||||
_settings:
|
||||
driveBanner: "Permette di gestire e configurare il Drive, controllare il consumo di spazio e configurare il caricamento dei file."
|
||||
pluginBanner: "Consentono di migliorare le funzionalità. Le estensioni si possono configurare e gestire singolarmente."
|
||||
notificationsBanner: "Puoi impostare il tipo di notifiche da ricevere dal server e anche le notifiche push."
|
||||
api: "API"
|
||||
webhook: "Webhook"
|
||||
serviceConnection: "Integrazione servizi"
|
||||
serviceConnectionBanner: "Puoi gestire i codici di accesso e i Webhook per collegare App o servizi esterni."
|
||||
accountData: "Dati del profilo"
|
||||
accountDataBanner: "Puoi gestire i dati del tuo profilo, esportando e importando."
|
||||
muteAndBlockBanner: "Puoi configurare la visibiltà dei contenuti e limitare le attività provenienti da profili specifici."
|
||||
accessibilityBanner: "Puoi personalizzare e migliorare la lettura sul tuo dispositivo in modo che sia più chiaro e reattivo."
|
||||
privacyBanner: "Puoi configurare la privacy del tuo profilo, come la visibilità delle Note, la visibilità del profilo nelle ricerche e l'approvazione delle relazioni tra profili."
|
||||
securityBanner: "Puoi gestire la sicurezza del tuo account, la password, i modi di accesso, la generazione di codici OTP per accesso multi fattore (MFA/2FA) e la passkey."
|
||||
preferencesBanner: "Puoi personalizzare il comportamento del tuo dispositivo."
|
||||
appearanceBanner: "Puoi personalizzare l'aspetto nel dispositivo, in base alle tue preferenze."
|
||||
soundsBanner: "Puoi personalizzare i suoni emessi dagli eventi sul tuo dispositivo."
|
||||
timelineAndNote: "Note e Timeline"
|
||||
makeEveryTextElementsSelectable: "Imposta ogni elemento come selezionabile"
|
||||
makeEveryTextElementsSelectable_description: "Potrebbe ridurre l'usabilità in alcune situazioni."
|
||||
useStickyIcons: "Fissa le icone durante lo scorrimento"
|
||||
showNavbarSubButtons: "Mostra i pulsanti secondari nella barra di navigazione"
|
||||
ifOn: "Quando attivato"
|
||||
ifOff: "Quando disattivato"
|
||||
enableSyncThemesBetweenDevices: "Sincronizzare il tema tra i dispositivi"
|
||||
_chat:
|
||||
showSenderName: "Mostra il nome del mittente"
|
||||
sendOnEnter: "Invio spedisce"
|
||||
_preferencesProfile:
|
||||
profileName: "Nome del profilo"
|
||||
profileNameDescription: "Impostare il nome che indentifica questo dispositivo."
|
||||
profileNameDescription2: "Es: \"PC principale\" o \"Cellulare\""
|
||||
_preferencesBackup:
|
||||
autoBackup: "Backup automatico"
|
||||
restoreFromBackup: "Ripristinare da backup"
|
||||
noBackupsFoundTitle: "Nessun backup trovato"
|
||||
noBackupsFoundDescription: "Impossibile trovare un backup creato automaticamente. Se se hai salvato il file di backup manualmente, puoi importarlo e ripristinarlo."
|
||||
selectBackupToRestore: "Seleziona un backup da ripristinare"
|
||||
youNeedToNameYourProfileToEnableAutoBackup: "Per abilitare i backup automatici, è necessario indicare il nome del profilo."
|
||||
autoPreferencesBackupIsNotEnabledForThisDevice: "Su questo dispositivo non è stato attivato il backup automatico delle preferenze."
|
||||
backupFound: "Esiste il Backup delle preferenze"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "Per vedere il contenuto, è necessaria l'iscrizione"
|
||||
requireSigninToViewContentsDescription1: "Richiedere l'iscrizione per visualizzare tutte le Note e gli altri contenuti che hai creato. Probabilmente l'effetto è impedire la raccolta di informazioni da parte dei bot crawler."
|
||||
|
@ -1325,6 +1441,7 @@ _accountSettings:
|
|||
makeNotesHiddenBefore: "Nascondi le Note pubblicate in precedenza"
|
||||
makeNotesHiddenBeforeDescription: "Mentre questa funzione è abilitata, le Note antecedenti al momento impostato, saranno visibili soltanto a te (private). Disabilitandola nuovamente, verrà ripristinata anche la visibilità pubblica della Nota."
|
||||
mayNotEffectForFederatedNotes: "Le Note già federate su server remoti potrebbero non essere modificate."
|
||||
mayNotEffectSomeSituations: "Queste restrizioni sono semplificate. In alcuni casi, potrebbero anche non avvenire. Ad esempio visionando un server remoto o durante la moderazione."
|
||||
notesHavePassedSpecifiedPeriod: "Note antecedenti al periodo specificato"
|
||||
notesOlderThanSpecifiedDateAndTime: "Note antecedenti al momento specificato"
|
||||
_abuseUserReport:
|
||||
|
@ -1770,6 +1887,8 @@ _role:
|
|||
descriptionOfIsExplorable: "Selezionandolo, la timeline del ruolo diventerà accessibile pubblicamente. Tranne se il ruolo non è pubblico."
|
||||
displayOrder: "Ordine di visualizzazione"
|
||||
descriptionOfDisplayOrder: "I valori più alti vengono visualizzati per primi"
|
||||
preserveAssignmentOnMoveAccount: "Mantenere l'assegnazione alla migrazione del profilo"
|
||||
preserveAssignmentOnMoveAccount_description: "Attivando, il ruolo verrà portato sul profilo destinatario, durante la migrazione."
|
||||
canEditMembersByModerator: "Anche i Moderatori assegnano profili a questo ruolo"
|
||||
descriptionOfCanEditMembersByModerator: "Se disattivo, potranno farlo solamente gli Amministratori."
|
||||
priority: "Priorità"
|
||||
|
@ -1973,6 +2092,7 @@ _theme:
|
|||
installed: "{name} è installato"
|
||||
installedThemes: "Temi installati"
|
||||
builtinThemes: "Temi integrati"
|
||||
instanceTheme: "Tema dell'istanza"
|
||||
alreadyInstalled: "Questo tema è già installato"
|
||||
invalid: "Il formato tema non è valido"
|
||||
make: "Crea un tema"
|
||||
|
@ -2005,14 +2125,13 @@ _theme:
|
|||
header: "Intestazione"
|
||||
navBg: "Sfondo della barra laterale"
|
||||
navFg: "Testo della barra laterale"
|
||||
navHoverFg: "Testo della barra laterale (al passaggio del mouse)"
|
||||
navActive: "Testo della barra laterale (attivo)"
|
||||
navIndicator: "Indicatore di barra laterale"
|
||||
link: "Link"
|
||||
hashtag: "Hashtag"
|
||||
mention: "Menzioni"
|
||||
mentionMe: "Menzioni (di me)"
|
||||
renote: "Rinota"
|
||||
renote: "Renota"
|
||||
modalBg: "Sfondo modale."
|
||||
divider: "Interruzione di linea"
|
||||
scrollbarHandle: "Maniglie della barra di scorrimento"
|
||||
|
@ -2028,17 +2147,15 @@ _theme:
|
|||
buttonHoverBg: "Sfondo del pulsante (sorvolato)"
|
||||
inputBorder: "Inquadra casella di testo"
|
||||
driveFolderBg: "Sfondo della cartella di disco"
|
||||
wallpaperOverlay: "Sovrapposizione dello sfondo"
|
||||
badge: "Distintivo"
|
||||
messageBg: "Sfondo della chat"
|
||||
accentDarken: "Temi (scuri)"
|
||||
accentLighten: "Temi (luminosi)"
|
||||
fgHighlighted: "Testo in evidenza."
|
||||
_sfx:
|
||||
note: "Nota"
|
||||
noteMy: "Mia nota"
|
||||
notification: "Notifiche"
|
||||
reaction: "Quando seleziono una reazione"
|
||||
chatMessage: "Messaggio di chat"
|
||||
_soundSettings:
|
||||
driveFile: "Suoni del Drive"
|
||||
driveFileWarn: "Seleziona file dal dispositivo"
|
||||
|
@ -2185,6 +2302,8 @@ _permissions:
|
|||
"read:clip-favorite": "Vedere Clip preferite"
|
||||
"read:federation": "Vedere la federazione"
|
||||
"write:report-abuse": "Inviare segnalazioni"
|
||||
"write:chat": "Gestire la chat"
|
||||
"read:chat": "Visualizzare le chat"
|
||||
_auth:
|
||||
shareAccessTitle: "Permessi dell'applicazione"
|
||||
shareAccess: "Vuoi autorizzare {name} ad accedere al tuo profilo?"
|
||||
|
@ -2433,6 +2552,7 @@ _notification:
|
|||
newNote: "Nuove Note"
|
||||
unreadAntennaNote: "Antenna {name}"
|
||||
roleAssigned: "Ruolo assegnato"
|
||||
chatRoomInvitationReceived: "Invito in una stanza di chat"
|
||||
emptyPushNotificationMessage: "Le notifiche push sono state aggiornate."
|
||||
achievementEarned: "Obiettivo raggiunto"
|
||||
testNotification: "Provare la notifica"
|
||||
|
@ -2461,9 +2581,11 @@ _notification:
|
|||
receiveFollowRequest: "Richieste di follow in arrivo"
|
||||
followRequestAccepted: "Richieste di follow accettate"
|
||||
roleAssigned: "Ruolo concesso"
|
||||
chatRoomInvitationReceived: "Invito in una stanza di chat"
|
||||
achievementEarned: "Risultato raggiunto"
|
||||
exportCompleted: "Esportazione completata"
|
||||
login: "Accessi"
|
||||
createToken: "Creare un token di accesso"
|
||||
test: "Notifiche di test"
|
||||
app: "Notifiche da applicazioni"
|
||||
_actions:
|
||||
|
@ -2473,6 +2595,9 @@ _notification:
|
|||
_deck:
|
||||
alwaysShowMainColumn: "Mostra sempre la colonna principale"
|
||||
columnAlign: "Allineare colonne"
|
||||
columnGap: "Margine tra le colonne"
|
||||
deckMenuPosition: "Posizione del menu Deck"
|
||||
navbarPosition: "Posizione barra di navigazione"
|
||||
addColumn: "Aggiungi colonna"
|
||||
newNoteNotificationSettings: "Preferenze per le notifiche di nuove Note"
|
||||
configureColumn: "Impostazioni colonna"
|
||||
|
@ -2491,6 +2616,7 @@ _deck:
|
|||
useSimpleUiForNonRootPages: "Visualizza sotto pagine con interfaccia web semplice"
|
||||
usedAsMinWidthWhenFlexible: "Se \"larghezza flessibile\" è abilitato, questa diventa la larghezza minima"
|
||||
flexible: "Larghezza flessibile"
|
||||
enableSyncBetweenDevicesForProfiles: "Abilita la sincronizzazione delle informazioni profilo tra dispositivi"
|
||||
_columns:
|
||||
main: "Principale"
|
||||
widgets: "Riquadri"
|
||||
|
@ -2509,8 +2635,8 @@ _disabledTimeline:
|
|||
title: "Timeline disabilitata"
|
||||
description: "Il ruolo in cui sei non ti permette di leggere questa timeline"
|
||||
_drivecleaner:
|
||||
orderBySizeDesc: "Dal più grande al più piccolo"
|
||||
orderByCreatedAtAsc: "Dal più vecchio al più recente"
|
||||
orderBySizeDesc: "Dal file più grosso al più piccolo"
|
||||
orderByCreatedAtAsc: "Dal file più vecchio al più recente"
|
||||
_webhookSettings:
|
||||
createWebhook: "Creazione Webhook"
|
||||
modifyWebhook: "Modifica Webhook"
|
||||
|
@ -2598,6 +2724,7 @@ _moderationLogTypes:
|
|||
deletePage: "Pagina eliminata"
|
||||
deleteFlash: "Play eliminato"
|
||||
deleteGalleryPost: "Eliminazione pubblicazione nella Galleria"
|
||||
deleteChatRoom: "Elimina chat"
|
||||
updateProxyAccountDescription: "Aggiornata la descrizione del profilo proxy"
|
||||
_fileViewer:
|
||||
title: "Dettagli del file"
|
||||
|
@ -2612,10 +2739,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "Prima di installare, assicurati che la fonte sia affidabile."
|
||||
_plugin:
|
||||
title: "Vuoi davvero installare questo componente aggiuntivo?"
|
||||
metaTitle: "Informazioni sul componente aggiuntivo"
|
||||
_theme:
|
||||
title: "Vuoi davvero installare questa variazione grafica?"
|
||||
metaTitle: "Informazioni sulla variazione grafica"
|
||||
_meta:
|
||||
base: "Combinazione base di colori"
|
||||
_vendorInfo:
|
||||
|
@ -2789,7 +2914,7 @@ _customEmojisManager:
|
|||
directoryToCategoryLabel: "Inseriscile in una cartella omonima alla categoria"
|
||||
directoryToCategoryCaption: "Crea il campo categoria in base alla cartella."
|
||||
emojiInputAreaCaption: "Seleziona l'emoji da registrare utilizzando uno dei metodi."
|
||||
emojiInputAreaList1: "Trascina una immagine o una cartella in quest'area"
|
||||
emojiInputAreaList1: "Trascinare una immagine o una cartella in quest'area"
|
||||
emojiInputAreaList2: "Clicca per scegliere file dal tuo dispositivo"
|
||||
emojiInputAreaList3: "Clicca per selezionare dal Drive"
|
||||
confirmRegisterEmojisDescription: "Registrazione delle emoji elencate come nuove emoji personalizzate. Vuoi davvero procedere? (Per evitare sovraccarichi, puoi registrare al massimo {count} emoji per volta)"
|
||||
|
|
|
@ -289,7 +289,6 @@ deleteAreYouSure: "「{x}」を削除しますか?"
|
|||
resetAreYouSure: "リセットしますか?"
|
||||
areYouSure: "よろしいですか?"
|
||||
saved: "保存しました"
|
||||
messaging: "チャット"
|
||||
upload: "アップロード"
|
||||
keepOriginalUploading: "オリジナル画像を保持"
|
||||
keepOriginalUploadingDescription: "画像をアップロードする時にオリジナル版を保持します。オフにするとアップロード時にブラウザでWeb公開用画像を生成します。"
|
||||
|
@ -302,7 +301,7 @@ uploadFromUrlMayTakeTime: "アップロードが完了するまで時間がか
|
|||
explore: "みつける"
|
||||
messageRead: "既読"
|
||||
noMoreHistory: "これより過去の履歴はありません"
|
||||
startMessaging: "チャットを開始"
|
||||
startChat: "チャットを始める"
|
||||
nUsersRead: "{n}人が読みました"
|
||||
agreeTo: "{0}に同意"
|
||||
agree: "同意する"
|
||||
|
@ -425,6 +424,7 @@ antennaExcludeBots: "Botアカウントを除外"
|
|||
antennaKeywordsDescription: "スペースで区切るとAND指定になり、改行で区切るとOR指定になります"
|
||||
notifyAntenna: "新しいノートを通知する"
|
||||
withFileAntenna: "ファイルが添付されたノートのみ"
|
||||
excludeNotesInSensitiveChannel: "センシティブなチャンネルのノートを除外"
|
||||
enableServiceworker: "ブラウザへのプッシュ通知を有効にする"
|
||||
antennaUsersDescription: "ユーザー名を改行で区切って指定します"
|
||||
caseSensitive: "大文字小文字を区別する"
|
||||
|
@ -491,8 +491,6 @@ noteOf: "{user}のノート"
|
|||
quoteAttached: "引用付き"
|
||||
quoteQuestion: "引用として添付しますか?"
|
||||
attachAsFileQuestion: "クリップボードのテキストが長いです。テキストファイルとして添付しますか?"
|
||||
noMessagesYet: "まだチャットはありません"
|
||||
newMessageExists: "新しいメッセージがあります"
|
||||
onlyOneFileCanBeAttached: "メッセージに添付できるファイルはひとつです"
|
||||
signinRequired: "続行する前に、登録またはログインが必要です"
|
||||
signinOrContinueOnRemote: "続行するには、お使いのサーバーに移動するか、このサーバーに登録・ログインする必要があります"
|
||||
|
@ -698,6 +696,7 @@ userSaysSomethingAbout: "{name}が「{word}」について何かを言いまし
|
|||
makeActive: "アクティブにする"
|
||||
display: "表示"
|
||||
copy: "コピー"
|
||||
copiedToClipboard: "クリップボードにコピーされました"
|
||||
metrics: "メトリクス"
|
||||
overview: "概要"
|
||||
logs: "ログ"
|
||||
|
@ -1315,6 +1314,134 @@ markAsSensitiveConfirm: "このメディアをセンシティブとして設定
|
|||
unmarkAsSensitiveConfirm: "このメディアのセンシティブ指定を解除しますか?"
|
||||
preferences: "環境設定"
|
||||
accessibility: "アクセシビリティ"
|
||||
preferencesProfile: "設定のプロファイル"
|
||||
copyPreferenceId: "設定IDをコピー"
|
||||
resetToDefaultValue: "初期値に戻す"
|
||||
overrideByAccount: "アカウントで上書き"
|
||||
untitled: "無題"
|
||||
noName: "名前はありません"
|
||||
skip: "スキップ"
|
||||
restore: "復元"
|
||||
syncBetweenDevices: "デバイス間で同期"
|
||||
preferenceSyncConflictTitle: "サーバーに設定値が存在します"
|
||||
preferenceSyncConflictText: "同期が有効にされた設定項目は設定値をサーバーに保存しますが、この設定項目のサーバーに保存された設定値が見つかりました。どちらの設定値で上書きしますか?"
|
||||
preferenceSyncConflictChoiceServer: "サーバーの設定値"
|
||||
preferenceSyncConflictChoiceDevice: "デバイスの設定値"
|
||||
preferenceSyncConflictChoiceCancel: "同期の有効化をキャンセル"
|
||||
paste: "ペースト"
|
||||
emojiPalette: "絵文字パレット"
|
||||
postForm: "投稿フォーム"
|
||||
textCount: "文字数"
|
||||
information: "情報"
|
||||
chat: "チャット"
|
||||
migrateOldSettings: "旧設定情報を移行"
|
||||
migrateOldSettings_description: "通常これは自動で行われていますが、何らかの理由により上手く移行されなかった場合は手動で移行処理をトリガーできます。現在の設定情報は上書きされます。"
|
||||
compress: "圧縮"
|
||||
right: "右"
|
||||
bottom: "下"
|
||||
top: "上"
|
||||
embed: "埋め込み"
|
||||
settingsMigrating: "設定を移行しています。しばらくお待ちください... (後ほど、設定→その他→旧設定情報を移行 で手動で移行することもできます)"
|
||||
readonly: "読み取り専用"
|
||||
goToDeck: "デッキへ戻る"
|
||||
|
||||
_chat:
|
||||
noMessagesYet: "まだメッセージはありません"
|
||||
newMessage: "新しいメッセージ"
|
||||
individualChat: "個人チャット"
|
||||
individualChat_description: "特定ユーザーとの一対一のチャットができます。"
|
||||
roomChat: "ルームチャット"
|
||||
roomChat_description: "複数人でのチャットができます。\nまた、個人チャットを許可していないユーザーとでも、相手が受け入れればチャットができます。"
|
||||
createRoom: "ルームを作成"
|
||||
inviteUserToChat: "ユーザーを招待してチャットを始めましょう"
|
||||
yourRooms: "作成したルーム"
|
||||
joiningRooms: "参加中のルーム"
|
||||
invitations: "招待"
|
||||
noInvitations: "招待はありません"
|
||||
history: "履歴"
|
||||
noHistory: "履歴はありません"
|
||||
noRooms: "ルームはありません"
|
||||
inviteUser: "ユーザーを招待"
|
||||
sentInvitations: "送信した招待"
|
||||
join: "参加"
|
||||
ignore: "無視"
|
||||
leave: "ルームから退出"
|
||||
members: "メンバー"
|
||||
searchMessages: "メッセージを検索"
|
||||
home: "ホーム"
|
||||
send: "送信"
|
||||
newline: "改行"
|
||||
muteThisRoom: "このルームをミュート"
|
||||
deleteRoom: "ルームを削除"
|
||||
chatNotAvailableForThisAccountOrServer: "このサーバー、またはこのアカウントでチャットは有効化されていません。"
|
||||
chatIsReadOnlyForThisAccountOrServer: "このサーバー、またはこのアカウントでチャットは読み取り専用となっています。新たに書き込んだり、チャットルームを作成・参加したりすることはできません。"
|
||||
chatNotAvailableInOtherAccount: "相手のアカウントでチャット機能が使えない状態になっています。"
|
||||
cannotChatWithTheUser: "このユーザーとのチャットを開始できません"
|
||||
cannotChatWithTheUser_description: "チャットが使えない状態になっているか、相手がチャットを開放していません。"
|
||||
chatWithThisUser: "チャットする"
|
||||
thisUserAllowsChatOnlyFromFollowers: "このユーザーはフォロワーからのみチャットを受け付けています。"
|
||||
thisUserAllowsChatOnlyFromFollowing: "このユーザーは、このユーザーがフォローしているユーザーからのみチャットを受け付けています。"
|
||||
thisUserAllowsChatOnlyFromMutualFollowing: "このユーザーは相互フォローのユーザーからのみチャットを受け付けています。"
|
||||
thisUserNotAllowedChatAnyone: "このユーザーは誰からもチャットを受け付けていません。"
|
||||
chatAllowedUsers: "チャットを許可する相手"
|
||||
chatAllowedUsers_note: "自分からチャットメッセージを送った相手とはこの設定に関わらずチャットが可能です。"
|
||||
_chatAllowedUsers:
|
||||
everyone: "誰でも"
|
||||
followers: "自分のフォロワーのみ"
|
||||
following: "自分がフォローしているユーザーのみ"
|
||||
mutual: "相互フォローのユーザーのみ"
|
||||
none: "誰も許可しない"
|
||||
|
||||
_emojiPalette:
|
||||
palettes: "パレット"
|
||||
enableSyncBetweenDevicesForPalettes: "パレットのデバイス間同期を有効にする"
|
||||
paletteForMain: "メインで使用するパレット"
|
||||
paletteForReaction: "リアクションで使用するパレット"
|
||||
|
||||
_settings:
|
||||
driveBanner: "ドライブの管理と設定、使用量の確認、ファイルをアップロードする際の設定を行えます。"
|
||||
pluginBanner: "プラグインを利用するとクライアントの機能を拡張することができます。プラグインのインストール、個別の設定と管理が行えます。"
|
||||
notificationsBanner: "サーバーからの受信する通知の種類と範囲や、プッシュ通知の設定が行えます。"
|
||||
api: "API"
|
||||
webhook: "Webhook"
|
||||
serviceConnection: "サービス連携"
|
||||
serviceConnectionBanner: "外部のアプリ・サービスと連携するためのアクセストークンやWebhookの管理と設定が行えます。"
|
||||
accountData: "アカウントのデータ"
|
||||
accountDataBanner: "アカウントデータのアーカイブをエクスポート/インポートして管理できます。"
|
||||
muteAndBlockBanner: "非表示にするコンテンツの設定や、特定のユーザーからのアクションを制限する設定と管理を行えます。"
|
||||
accessibilityBanner: "クライアントの視覚や動作に関するパーソナライズを行い、より最適に使用できるように設定できます。"
|
||||
privacyBanner: "コンテンツの公開範囲、見つけやすさ、フォローの承認制などアカウントのプライバシーに関する設定を行えます。"
|
||||
securityBanner: "パスワード、ログイン方法、認証アプリ、パスキーなどアカウントのセキュリティに関する設定を行えます。"
|
||||
preferencesBanner: "好みに応じた、クライアントの全体的な動作の設定が行えます。"
|
||||
appearanceBanner: "好みに応じた、クライアントの見た目・表示方法に関する設定が行えます。"
|
||||
soundsBanner: "クライアントで再生するサウンドの設定が行えます。"
|
||||
timelineAndNote: "タイムラインとノート"
|
||||
makeEveryTextElementsSelectable: "全てのテキスト要素を選択可能にする"
|
||||
makeEveryTextElementsSelectable_description: "有効にすると、一部のシチュエーションでのユーザビリティが低下する場合があります。"
|
||||
useStickyIcons: "アイコンをスクロールに追従させる"
|
||||
showNavbarSubButtons: "ナビゲーションバーに副ボタンを表示"
|
||||
ifOn: "オンのとき"
|
||||
ifOff: "オフのとき"
|
||||
enableSyncThemesBetweenDevices: "デバイス間でインストールしたテーマを同期"
|
||||
|
||||
_chat:
|
||||
showSenderName: "送信者の名前を表示"
|
||||
sendOnEnter: "Enterで送信"
|
||||
|
||||
_preferencesProfile:
|
||||
profileName: "プロファイル名"
|
||||
profileNameDescription: "このデバイスを識別する名前を設定してください。"
|
||||
profileNameDescription2: "例: 「メインPC」、「スマホ」など"
|
||||
|
||||
_preferencesBackup:
|
||||
autoBackup: "自動バックアップ"
|
||||
restoreFromBackup: "バックアップから復元"
|
||||
noBackupsFoundTitle: "バックアップが見つかりませんでした"
|
||||
noBackupsFoundDescription: "自動で作成されたバックアップは見つかりませんでしたが、バックアップファイルを手動で保存している場合、それをインポートして復元することはできます。"
|
||||
selectBackupToRestore: "復元するバックアップを選択してください"
|
||||
youNeedToNameYourProfileToEnableAutoBackup: "自動バックアップを有効にするにはプロファイル名の設定が必要です。"
|
||||
autoPreferencesBackupIsNotEnabledForThisDevice: "このデバイスで設定の自動バックアップは有効になっていません。"
|
||||
backupFound: "設定のバックアップが見つかりました"
|
||||
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "コンテンツの表示にログインを必須にする"
|
||||
|
@ -1326,6 +1453,7 @@ _accountSettings:
|
|||
makeNotesHiddenBefore: "過去のノートを非公開化する"
|
||||
makeNotesHiddenBeforeDescription: "この機能が有効になっている間、設定された日時より過去、または設定された時間を経過しているノートが自分のみ表示可能(非公開化)になります。無効に戻すと、ノートの公開状態も元に戻ります。"
|
||||
mayNotEffectForFederatedNotes: "リモートサーバーに連合されたノートには効果が及ばない場合があります。"
|
||||
mayNotEffectSomeSituations: "これらの制限は簡易的なものです。リモートサーバーでの閲覧やモデレーション時など、一部のシチュエーションでは適用されない場合があります。"
|
||||
notesHavePassedSpecifiedPeriod: "指定した時間を経過しているノート"
|
||||
notesOlderThanSpecifiedDateAndTime: "指定した日時より前のノート"
|
||||
|
||||
|
@ -1783,6 +1911,8 @@ _role:
|
|||
descriptionOfIsExplorable: "オンにすると、「みつける」でメンバー一覧が公開されるほか、ロールのタイムラインが利用可能になります。"
|
||||
displayOrder: "表示順"
|
||||
descriptionOfDisplayOrder: "数値が大きいほどUI上で先頭に表示されます。"
|
||||
preserveAssignmentOnMoveAccount: "アサイン状態を移行先アカウントにも引き継ぐ"
|
||||
preserveAssignmentOnMoveAccount_description: "オンにすると、このロールが付与されたアカウントが移行された際に、移行先アカウントにもこのロールが引き継がれるようになります。"
|
||||
canEditMembersByModerator: "モデレーターのメンバー編集を許可"
|
||||
descriptionOfCanEditMembersByModerator: "オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになります。オフにすると管理者のみが行えます。"
|
||||
priority: "優先度"
|
||||
|
@ -1823,6 +1953,7 @@ _role:
|
|||
canImportFollowing: "フォローのインポートを許可"
|
||||
canImportMuting: "ミュートのインポートを許可"
|
||||
canImportUserLists: "リストのインポートを許可"
|
||||
chatAvailability: "チャットを許可"
|
||||
_condition:
|
||||
roleAssignedTo: "マニュアルロールにアサイン済み"
|
||||
isLocal: "ローカルユーザー"
|
||||
|
@ -2007,6 +2138,7 @@ _theme:
|
|||
installed: "{name}をインストールしました"
|
||||
installedThemes: "インストールされたテーマ"
|
||||
builtinThemes: "標準のテーマ"
|
||||
instanceTheme: "サーバーのテーマ"
|
||||
alreadyInstalled: "そのテーマは既にインストールされています"
|
||||
invalid: "テーマの形式が間違っています"
|
||||
make: "テーマを作る"
|
||||
|
@ -2038,16 +2170,15 @@ _theme:
|
|||
panel: "パネル"
|
||||
shadow: "影"
|
||||
header: "ヘッダー"
|
||||
navBg: "サイドバーの背景"
|
||||
navFg: "サイドバーの文字"
|
||||
navHoverFg: "サイドバー文字(ホバー)"
|
||||
navActive: "サイドバー文字(アクティブ)"
|
||||
navIndicator: "サイドバーのインジケーター"
|
||||
navBg: "ナビゲーションバーの背景"
|
||||
navFg: "ナビゲーションバーの文字"
|
||||
navActive: "ナビゲーションバー文字(アクティブ)"
|
||||
navIndicator: "ナビゲーションバーのインジケーター"
|
||||
link: "リンク"
|
||||
hashtag: "ハッシュタグ"
|
||||
mention: "メンション"
|
||||
mentionMe: "あなた宛てメンション"
|
||||
renote: "Renote"
|
||||
renote: "リノート"
|
||||
modalBg: "モーダルの背景"
|
||||
divider: "分割線"
|
||||
scrollbarHandle: "スクロールバーの取っ手"
|
||||
|
@ -2063,11 +2194,8 @@ _theme:
|
|||
buttonHoverBg: "ボタンの背景 (ホバー)"
|
||||
inputBorder: "入力ボックスの縁取り"
|
||||
driveFolderBg: "ドライブフォルダーの背景"
|
||||
wallpaperOverlay: "壁紙のオーバーレイ"
|
||||
badge: "バッジ"
|
||||
messageBg: "チャットの背景"
|
||||
accentDarken: "アクセント (暗め)"
|
||||
accentLighten: "アクセント (明るめ)"
|
||||
fgHighlighted: "強調された文字"
|
||||
|
||||
_sfx:
|
||||
|
@ -2075,6 +2203,7 @@ _sfx:
|
|||
noteMy: "ノート(自分)"
|
||||
notification: "通知"
|
||||
reaction: "リアクション選択時"
|
||||
chatMessage: "チャットのメッセージ"
|
||||
|
||||
_soundSettings:
|
||||
driveFile: "ドライブの音声を使用"
|
||||
|
@ -2227,6 +2356,8 @@ _permissions:
|
|||
"read:clip-favorite": "クリップのいいねを見る"
|
||||
"read:federation": "連合に関する情報を取得する"
|
||||
"write:report-abuse": "違反を報告する"
|
||||
"write:chat": "チャットを操作する"
|
||||
"read:chat": "チャットを閲覧する"
|
||||
|
||||
_auth:
|
||||
shareAccessTitle: "アプリへのアクセス許可"
|
||||
|
@ -2493,6 +2624,7 @@ _notification:
|
|||
newNote: "新しい投稿"
|
||||
unreadAntennaNote: "アンテナ {name}"
|
||||
roleAssigned: "ロールが付与されました"
|
||||
chatRoomInvitationReceived: "チャットルームへ招待されました"
|
||||
emptyPushNotificationMessage: "プッシュ通知の更新をしました"
|
||||
achievementEarned: "実績を獲得"
|
||||
testNotification: "通知テスト"
|
||||
|
@ -2522,9 +2654,11 @@ _notification:
|
|||
receiveFollowRequest: "フォロー申請を受け取った"
|
||||
followRequestAccepted: "フォローが受理された"
|
||||
roleAssigned: "ロールが付与された"
|
||||
chatRoomInvitationReceived: "チャットルームへ招待された"
|
||||
achievementEarned: "実績の獲得"
|
||||
exportCompleted: "エクスポートが完了した"
|
||||
login: "ログイン"
|
||||
createToken: "アクセストークンの作成"
|
||||
test: "通知のテスト"
|
||||
app: "連携アプリからの通知"
|
||||
|
||||
|
@ -2536,6 +2670,9 @@ _notification:
|
|||
_deck:
|
||||
alwaysShowMainColumn: "常にメインカラムを表示"
|
||||
columnAlign: "カラムの寄せ"
|
||||
columnGap: "カラム間のマージン"
|
||||
deckMenuPosition: "デッキメニューの位置"
|
||||
navbarPosition: "ナビゲーションバーの位置"
|
||||
addColumn: "カラムを追加"
|
||||
newNoteNotificationSettings: "新着ノート通知の設定"
|
||||
configureColumn: "カラムの設定"
|
||||
|
@ -2549,11 +2686,12 @@ _deck:
|
|||
newProfile: "新規プロファイル"
|
||||
deleteProfile: "プロファイルを削除"
|
||||
introduction: "カラムを組み合わせて自分だけのインターフェイスを作りましょう!"
|
||||
introduction2: "画面の右にある + を押して、いつでもカラムを追加できます。"
|
||||
introduction2: "カラムを追加するには、画面の + をクリックします。"
|
||||
widgetsIntroduction: "カラムのメニューから、「ウィジェットの編集」を選択してウィジェットを追加してください"
|
||||
useSimpleUiForNonRootPages: "非ルートページは簡易UIで表示"
|
||||
usedAsMinWidthWhenFlexible: "「幅を自動調整」が有効の場合、これが幅の最小値となります"
|
||||
flexible: "幅を自動調整"
|
||||
enableSyncBetweenDevicesForProfiles: "プロファイル情報のデバイス間同期を有効にする"
|
||||
|
||||
_columns:
|
||||
main: "メイン"
|
||||
|
@ -2668,6 +2806,7 @@ _moderationLogTypes:
|
|||
deletePage: "ページを削除"
|
||||
deleteFlash: "Playを削除"
|
||||
deleteGalleryPost: "ギャラリーの投稿を削除"
|
||||
deleteChatRoom: "チャットルームを削除"
|
||||
updateProxyAccountDescription: "プロキシアカウントの説明を更新"
|
||||
|
||||
_fileViewer:
|
||||
|
@ -2684,10 +2823,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "配布元が信頼できるかを確認した上でインストールしてください。"
|
||||
_plugin:
|
||||
title: "このプラグインをインストールしますか?"
|
||||
metaTitle: "プラグイン情報"
|
||||
_theme:
|
||||
title: "このテーマをインストールしますか?"
|
||||
metaTitle: "テーマ情報"
|
||||
_meta:
|
||||
base: "基本のカラースキーム"
|
||||
_vendorInfo:
|
||||
|
|
|
@ -289,7 +289,6 @@ deleteAreYouSure: "「{x}」はほかしてええか?"
|
|||
resetAreYouSure: "リセットしてええん?"
|
||||
areYouSure: "いいん?"
|
||||
saved: "保存したで!"
|
||||
messaging: "チャット"
|
||||
upload: "アップロード"
|
||||
keepOriginalUploading: "オリジナル画像のまんま"
|
||||
keepOriginalUploadingDescription: "画像を上げるときにオリジナル版のまんまにするで。オフにしたら、上げたときにブラウザでWeb公開用の画像を生成するで。 "
|
||||
|
@ -302,7 +301,6 @@ uploadFromUrlMayTakeTime: "アップロード終わるんにちょい時間か
|
|||
explore: "みつける"
|
||||
messageRead: "もう読んだ"
|
||||
noMoreHistory: "これより昔のんはあらへんで"
|
||||
startMessaging: "チャットやるで"
|
||||
nUsersRead: "{n}人が読んでもうた"
|
||||
agreeTo: "{0}に同意したで"
|
||||
agree: "せやな"
|
||||
|
@ -491,8 +489,6 @@ noteOf: "{user}はんのノート"
|
|||
quoteAttached: "引用付いとるで"
|
||||
quoteQuestion: "引用として添付してもええか?"
|
||||
attachAsFileQuestion: "クリップボードのテキストが長すぎるからテキストファイルとして添付してもええか?"
|
||||
noMessagesYet: "まだチャットはあらへんで"
|
||||
newMessageExists: "新しいメッセージがきたで"
|
||||
onlyOneFileCanBeAttached: "ごめんな、メッセージに添付できるファイルはひとつだけなんよ。"
|
||||
signinRequired: "ログインしてくれへん?"
|
||||
signinOrContinueOnRemote: "続行するには、お使いのサーバーに移動するか、このサーバーに登録・ログインする必要があるで"
|
||||
|
@ -1311,6 +1307,16 @@ federationSpecified: "このサーバーはホワイトリスト連合で運用
|
|||
federationDisabled: "このサーバーは連合が無効化されてるで。他のサーバーのユーザーとやり取りすることはできひんで。"
|
||||
confirmOnReact: "ツッコむときに確認とる"
|
||||
reactAreYouSure: "\" {emoji} \" でツッコむ?"
|
||||
postForm: "投稿フォーム"
|
||||
information: "情報"
|
||||
_chat:
|
||||
invitations: "来てや"
|
||||
noHistory: "履歴はないわ。"
|
||||
members: "メンバーはん"
|
||||
home: "ホーム"
|
||||
send: "送信"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "ログインしてもらってからコンテンツ見てもらう"
|
||||
requireSigninToViewContentsDescription1: "あなたが作成した全部のノートとかのコンテンツを見れるようにするのにログインがいるようにするで。クローラーにいろいろ収集されるんを防げるかもしれん。"
|
||||
|
@ -2001,7 +2007,6 @@ _theme:
|
|||
header: "ヘッダー"
|
||||
navBg: "サイドバーの背景"
|
||||
navFg: "サイドバーの文字"
|
||||
navHoverFg: "サイドバー文字(ホバー)"
|
||||
navActive: "サイドバー文字(アクティブ)"
|
||||
navIndicator: "サイドバーのインジケーター"
|
||||
link: "リンク"
|
||||
|
@ -2024,11 +2029,8 @@ _theme:
|
|||
buttonHoverBg: "ボタンの背景 (ホバー)"
|
||||
inputBorder: "入力ボックスの縁取り"
|
||||
driveFolderBg: "ドライブフォルダーの背景"
|
||||
wallpaperOverlay: "壁紙のオーバーレイ"
|
||||
badge: "バッジ"
|
||||
messageBg: "チャットの背景"
|
||||
accentDarken: "アクセント (暗め)"
|
||||
accentLighten: "アクセント (明るめ)"
|
||||
fgHighlighted: "強調されとる文字"
|
||||
_sfx:
|
||||
note: "ノート"
|
||||
|
@ -2181,6 +2183,7 @@ _permissions:
|
|||
"read:clip-favorite": "クリップのいいね見る"
|
||||
"read:federation": "連合の情報取得"
|
||||
"write:report-abuse": "違反報告"
|
||||
"write:chat": "チャットを操作するで"
|
||||
_auth:
|
||||
shareAccessTitle: "アプリへのアクセス許してやったらどうや"
|
||||
shareAccess: "「{name}」がアカウントにアクセスすることを許可してええか?"
|
||||
|
@ -2607,10 +2610,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "配ってるとこが信頼できるか確認した上でインストールしてな。"
|
||||
_plugin:
|
||||
title: "このプラグイン、インストールする?"
|
||||
metaTitle: "プラグイン情報"
|
||||
_theme:
|
||||
title: "このテーマインストールする?"
|
||||
metaTitle: "テーマ情報"
|
||||
_meta:
|
||||
base: ""
|
||||
_vendorInfo:
|
||||
|
|
|
@ -263,7 +263,6 @@ deleteAreYouSure: "‘{x}’(얼)럴 뭉캡니꺼?"
|
|||
resetAreYouSure: "아시로 데돌립니꺼?"
|
||||
areYouSure: "갠찮십니꺼?"
|
||||
saved: "저장햇십니다"
|
||||
messaging: "대화"
|
||||
upload: "올리기"
|
||||
keepOriginalUploading: "온본 두기"
|
||||
keepOriginalUploadingDescription: "이미지럴 올릴 때 온본얼 고대로 둡니다. 꺼모 올릴 때 브라우저서 웹 공개 이미지럴 맨겁니다."
|
||||
|
@ -276,7 +275,6 @@ uploadFromUrlMayTakeTime: "올리기가 껕날라먼 시간이 쪼매 걸릴 깁
|
|||
explore: "살펴보기"
|
||||
messageRead: "이럿어예"
|
||||
noMoreHistory: "요카마 옛날 기록이 어ᇝ십니다"
|
||||
startMessaging: "대화하기"
|
||||
nUsersRead: "{n}멩이 이럿십니다"
|
||||
agreeTo: "{0}에 동이하기"
|
||||
agree: "동이합니다"
|
||||
|
@ -457,8 +455,6 @@ retype: "다시 서기"
|
|||
noteOf: "{user}님으 노트"
|
||||
quoteAttached: "따옴"
|
||||
quoteQuestion: "따와가 작성하겠십니까?"
|
||||
noMessagesYet: "아직 대화가 없십니다"
|
||||
newMessageExists: "새 메시지가 있십니다"
|
||||
onlyOneFileCanBeAttached: "메시지엔 파일 하나까제밖에 몬 넣십니다"
|
||||
invitations: "초대하기"
|
||||
invitationCode: "초대장"
|
||||
|
@ -655,6 +651,12 @@ replies: "답하기"
|
|||
renotes: "리노트"
|
||||
attach: "옇기"
|
||||
surrender: "아이예"
|
||||
information: "정보"
|
||||
_chat:
|
||||
invitations: "초대하기"
|
||||
noHistory: "기록이 없십니다"
|
||||
members: "구성원"
|
||||
home: "덜머리"
|
||||
_delivery:
|
||||
stop: "고만 보내예"
|
||||
_type:
|
||||
|
@ -745,6 +747,7 @@ _theme:
|
|||
description: "설멩"
|
||||
keys:
|
||||
mention: "멘션"
|
||||
renote: "리노트"
|
||||
_sfx:
|
||||
note: "새 노트"
|
||||
notification: "알림"
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -223,7 +223,6 @@ remove: "ລຶບ"
|
|||
removed: "ລຶບແລ້ວ"
|
||||
resetAreYouSure: "ຣີເຊັດບໍ?"
|
||||
saved: "ບັນທຶກແລ້ວ"
|
||||
messaging: "ແຊັຕ"
|
||||
upload: "ອັບໂຫຼດ"
|
||||
keepOriginalUploading: "ຮັກສາຮູບພາບຕົ້ນສະບັບ"
|
||||
fromDrive: "ຈາກ Drive"
|
||||
|
@ -233,7 +232,6 @@ uploadFromUrlDescription: "URL ຂອງໄຟລ໌ທີ່ທ່ານຕ້
|
|||
uploadFromUrlRequested: "ຮ້ອງຂໍການອັບໂຫລດແລ້ວ"
|
||||
explore: "ສຳຫຼວດ"
|
||||
messageRead: "ອ່ານແລ້ວ"
|
||||
startMessaging: "ເລີ່ມການສົນທະນາໃໝ່"
|
||||
nUsersRead: "ອ່ານໂດຍ {n}"
|
||||
agree: "ຍອມຮັບ"
|
||||
termsOfService: "ເງື່ອນໄຂການບໍລິການ"
|
||||
|
@ -394,6 +392,12 @@ searchByGoogle: "ຄົ້ນຫາ"
|
|||
file: "ໄຟລ໌"
|
||||
replies: "ຕອບກັບ"
|
||||
renotes: "Renote"
|
||||
information: "ກ່ຽວກັບ"
|
||||
_chat:
|
||||
invitations: "ເຊີນ"
|
||||
noHistory: "ບໍ່ມີປະຫວັດ"
|
||||
members: "ສະມາຊິກ"
|
||||
home: "ໜ້າຫຼັກ"
|
||||
_delivery:
|
||||
stop: "ໂຈະ"
|
||||
_type:
|
||||
|
|
|
@ -5,6 +5,7 @@ introMisskey: "Welkom! Misskey is een open source, gedecentraliseerde microblogd
|
|||
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"
|
||||
reset: "Herstellen"
|
||||
notifications: "Meldingen"
|
||||
username: "Gebruikersnaam"
|
||||
password: "Wachtwoord"
|
||||
|
@ -48,6 +49,7 @@ pin: "Vastmaken aan profielpagina"
|
|||
unpin: "Losmaken van profielpagina"
|
||||
copyContent: "Kopiëren inhoud"
|
||||
copyLink: "Kopiëren link"
|
||||
copyRemoteLink: "Remote-link kopiëren"
|
||||
copyLinkRenote: ""
|
||||
delete: "Verwijderen"
|
||||
deleteAndEdit: "Verwijderen en bewerken"
|
||||
|
@ -63,6 +65,7 @@ copyFileId: "Kopieer veld ID"
|
|||
copyFolderId: "Kopieer folder ID"
|
||||
copyProfileUrl: "Kopieer profiel URL"
|
||||
searchUser: "Zoeken een gebruiker"
|
||||
searchThisUsersNotes: "Notities van deze gebruiker doorzoeken"
|
||||
reply: "Antwoord"
|
||||
loadMore: "Laad meer"
|
||||
showMore: "Toon meer"
|
||||
|
@ -129,9 +132,12 @@ emojiPicker: "Emoji kiezer"
|
|||
pinnedEmojisForReactionSettingDescription: "Kies de emojis die als eerste getoond worden tijdens het reageren"
|
||||
pinnedEmojisSettingDescription: "Kies de emojis die als eerste getoond worden tijdens het reageren"
|
||||
emojiPickerDisplay: "Emoji kiezer weergave"
|
||||
overwriteFromPinnedEmojisForReaction: "Overschrijven met reactieinstellingen"
|
||||
overwriteFromPinnedEmojis: "Overschrijven met algemene instellingen"
|
||||
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"
|
||||
deleteFile: "Bestand verwijderen"
|
||||
markAsSensitive: "Markeren als NSFW"
|
||||
unmarkAsSensitive: "Geen NSFW"
|
||||
enterFileName: "Invoeren bestandsnaam"
|
||||
|
@ -147,6 +153,7 @@ 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"
|
||||
createAntenna: "Antenne aanmaken"
|
||||
selectWidget: "Kies een widget"
|
||||
editWidgets: "Bewerk widgets"
|
||||
editWidgetsExit: "Klaar"
|
||||
|
@ -158,6 +165,7 @@ emojiUrl: "URL emoji"
|
|||
addEmoji: "Toevoegen emoji"
|
||||
settingGuide: "Aanbevolen instellingen"
|
||||
cacheRemoteFiles: "Externe bestanden cachen"
|
||||
cacheRemoteFilesDescription: "Als deze instelling uitgeschakeld is worden bestanden altijd direct van remote servers geladen. Hiermee wordt opslagruimte bespaard, maar doordat er geen thumbnails worden gegenereerd, zal netwerkverkeer toenemen."
|
||||
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."
|
||||
|
@ -168,6 +176,10 @@ autoAcceptFollowed: "Accepteer verzoeken om jezelf te volgen vanzelf als je de v
|
|||
addAccount: "Account toevoegen"
|
||||
loginFailed: "Aanmelding mislukt."
|
||||
showOnRemote: "Toon op de externe instantie."
|
||||
continueOnRemote: "Verder op remote server"
|
||||
chooseServerOnMisskeyHub: "Kies een server van de Misskey Hub"
|
||||
specifyServerHost: "Serverhost uitkiezen"
|
||||
inputHostName: "Domein invullen"
|
||||
general: "Algemeen"
|
||||
wallpaper: "Achtergrond"
|
||||
setWallpaper: "Achtergrond instellen"
|
||||
|
@ -178,6 +190,7 @@ followConfirm: "Weet je zeker dat je {name} wilt volgen?"
|
|||
proxyAccount: "Proxy account"
|
||||
proxyAccountDescription: "Een proxy-account is een account dat onder bepaalde voorwaarden fungeert als externe volger voor gebruikers. Als een gebruiker bijvoorbeeld een externe gebruiker aan de lijst toevoegt, wordt de activiteit van de externe gebruiker niet aan de server geleverd als geen lokale gebruiker die gebruiker volgt, dus het proxy-account volgt in plaats daarvan."
|
||||
host: "Server"
|
||||
selectSelf: "Mezelf kiezen"
|
||||
selectUser: "Kies een gebruiker"
|
||||
recipient: "Ontvanger"
|
||||
annotation: "Reacties"
|
||||
|
@ -192,6 +205,7 @@ perHour: "Per uur"
|
|||
perDay: "Per dag"
|
||||
stopActivityDelivery: "Stop met versturen activiteiten"
|
||||
blockThisInstance: "Blokkeer deze server"
|
||||
mediaSilenceThisInstance: "Media van deze server dempen"
|
||||
operations: "Verwerkingen"
|
||||
software: "Software"
|
||||
version: "Versie"
|
||||
|
@ -211,6 +225,11 @@ clearCachedFiles: "Cache opschonen"
|
|||
clearCachedFilesConfirm: "Weet je zeker dat je alle externe bestanden in de cache wilt verwijderen?"
|
||||
blockedInstances: "Geblokkeerde servers"
|
||||
blockedInstancesDescription: "Maak een lijst van de servers die moeten worden geblokkeerd, gescheiden door regeleinden. Geblokkeerde servers kunnen niet meer communiceren met deze server."
|
||||
silencedInstancesDescription: "Geef de hostnamen van de servers die je wil dempen op, elk op hun eigen regel. Alle accounts die bij de opgegeven servers horen worden als gedempt behandeld, kunnen alleen maar volgverzoeken maken, en kunnen lokale accounts niet vermelden als ze niet gevolgd worden. Geblokkeerde servers worden hier niet door beïnvloed."
|
||||
mediaSilencedInstances: "Media-gedempte servers"
|
||||
mediaSilencedInstancesDescription: "Geef de hostnamen van de servers die je wil media-dempen op, elk op hun eigen regel. Alle accounts die bij de opgegeven servers horen worden als gedempt behandeld, en kunnen geen eigen emojis gebruiken. Geblokkeerde servers worden hier niet door beïnvloed."
|
||||
federationAllowedHosts: "Servers die mogen federeren "
|
||||
federationAllowedHostsDescription: "Geef de hostnamen van de servers die mogen federeren op, elk op hun eigen regel."
|
||||
muteAndBlock: "Gedempt en geblokkeerd"
|
||||
mutedUsers: "Gedempte gebruikers"
|
||||
blockedUsers: "Geblokkeerde gebruikers"
|
||||
|
@ -255,8 +274,8 @@ removed: "Succesvol verwijderd"
|
|||
removeAreYouSure: "Weet je zeker dat je \"{x}\" wil verwijderen?"
|
||||
deleteAreYouSure: "Weet je zeker dat je \"{x}\" wil verwijderen?"
|
||||
resetAreYouSure: "Resetten?"
|
||||
areYouSure: "Weet je het zeker?"
|
||||
saved: "Opgeslagen"
|
||||
messaging: "Chat"
|
||||
upload: "Uploaden"
|
||||
keepOriginalUploading: "Origineel beeld behouden."
|
||||
keepOriginalUploadingDescription: "Bewaar de originele versie bij het uploaden van afbeeldingen. Indien uitgeschakeld, wordt bij het uploaden een alternatieve versie voor webpublicatie genereert."
|
||||
|
@ -269,7 +288,7 @@ uploadFromUrlMayTakeTime: "Het kan even duren voordat het uploaden voltooid is."
|
|||
explore: "Verkennen"
|
||||
messageRead: "Lezen"
|
||||
noMoreHistory: "Er is geen verdere geschiedenis"
|
||||
startMessaging: "Start een gesprek"
|
||||
startChat: "Chat starten"
|
||||
nUsersRead: "gelezen door {n}"
|
||||
agreeTo: "Ik stem in met {0}"
|
||||
start: "Aan de slag"
|
||||
|
@ -296,12 +315,15 @@ selectFile: "Kies een bestand"
|
|||
selectFiles: "Selecteer bestanden"
|
||||
selectFolder: "Kies een map"
|
||||
selectFolders: "Kies mappen"
|
||||
fileNotSelected: "Geen bestand geselecteerd"
|
||||
renameFile: "Wijzig bestandsnaam"
|
||||
folderName: "Mapnaam"
|
||||
createFolder: "Map aanmaken"
|
||||
renameFolder: "Map hernoemen"
|
||||
deleteFolder: "Map verwijderen"
|
||||
folder: "Map"
|
||||
addFile: "Bestand toevoegen"
|
||||
showFile: "Bestanden weergeven"
|
||||
emptyDrive: "Jouw Drive is leeg."
|
||||
emptyFolder: "Deze map is leeg"
|
||||
unableToDelete: "Kan niet worden verwijderd"
|
||||
|
@ -357,8 +379,11 @@ hcaptcha: "hCaptcha"
|
|||
enableHcaptcha: "Inschakelen hCaptcha"
|
||||
hcaptchaSiteKey: "Site sleutel"
|
||||
hcaptchaSecretKey: "Geheime sleutel"
|
||||
mcaptcha: "mCaptcha"
|
||||
enableMcaptcha: "mCaptcha activeren"
|
||||
mcaptchaSiteKey: "Site sleutel"
|
||||
mcaptchaSecretKey: "Geheime sleutel"
|
||||
mcaptchaInstanceUrl: "mCaptcha server-URL"
|
||||
recaptcha: "reCAPTCHA"
|
||||
enableRecaptcha: "Inschakelen reCAPTCHA"
|
||||
recaptchaSiteKey: "Site sleutel"
|
||||
|
@ -373,6 +398,7 @@ name: "Naam"
|
|||
antennaSource: "Bron antenne"
|
||||
antennaKeywords: "Sleutelwoorden"
|
||||
antennaExcludeKeywords: "Blokkeerwoorden"
|
||||
antennaExcludeBots: "Bot-accounts uitsluiten"
|
||||
withReplies: "Antwoorden toevoegen"
|
||||
connectedTo: "De volgende accounts zijn verbonden"
|
||||
notesAndReplies: "Berichten en reacties"
|
||||
|
@ -423,7 +449,13 @@ retype: "Opnieuw invoeren"
|
|||
noteOf: "Notitie van {user}"
|
||||
quoteAttached: "Citaat"
|
||||
quoteQuestion: "Toevoegen als citaat?"
|
||||
signinOrContinueOnRemote: "Ga naar je eigen instantie of registreer je/log in op deze server om door te gaan."
|
||||
invitations: "Uitnodigen"
|
||||
menuStyle: "Menustijl"
|
||||
style: "Stijl"
|
||||
drawer: "Lade"
|
||||
popup: "Pop-up"
|
||||
showReactionsCount: "Zie het aantal reacties op notities"
|
||||
dashboard: "Overzicht"
|
||||
local: "Lokaal"
|
||||
remote: "Remote"
|
||||
|
@ -439,16 +471,41 @@ numberOfDays: "Aantal dagen"
|
|||
hideThisNote: "Verberg deze notitie"
|
||||
showFeaturedNotesInTimeline: "Laat featured notities in tijdlijn zien"
|
||||
sound: "Geluid"
|
||||
notUseSound: "Geluid uitschakelen"
|
||||
useSoundOnlyWhenActive: "Geluid alleen inschakelen wanneer Misskey actief is"
|
||||
uiInspector: "UI-inspecteur"
|
||||
unsetUserAvatar: "Avatar verwijderen"
|
||||
unsetUserAvatarConfirm: "Weet je zeker dat je je avatar wil verwijderen?"
|
||||
unsetUserBanner: "Banner verwijderen"
|
||||
unsetUserBannerConfirm: "Weet je zeker dat je je banner wil verwijderen?"
|
||||
expandTweet: "Notitie uitklappen"
|
||||
adminPermission: "Administratorrechten"
|
||||
smtpHost: "Server"
|
||||
smtpUser: "Gebruikersnaam"
|
||||
smtpPass: "Wachtwoord"
|
||||
wordMuteDescription: "Minimaliseert notities die het gespecificeerde woord of zin bevatten. Geminimaliseerde notities kunnen worden weergegeven door er op te klikken."
|
||||
hardWordMute: "Harde woorddemping"
|
||||
showMutedWord: "Gedempte woorden weergeven"
|
||||
hardWordMuteDescription: "Verbert notities die het gespecificeerde woord of zin bevatten. In tegenstelling tot woorddemping wordt de notitie volledig verborgen."
|
||||
userSaysSomethingAbout: "{name} zei iets over '{word}'"
|
||||
copiedToClipboard: "Naar het klembord gekopieerd"
|
||||
theKeywordWhenSearchingForCustomEmoji: "Dit is het keyword dat gebruikt wordt bij het zoeken naar eigen emojis."
|
||||
fillAbuseReportDescription: "Vul s.v.p. de details in over deze melding. Geef, als het over een specifieke notitie gaat, ook de URL op."
|
||||
reloadToApplySetting: "Deze instelling gaat pas in nadat de pagina herladen is. Nu herladen?"
|
||||
clearCache: "Cache opschonen"
|
||||
info: "Over"
|
||||
user: "Gebruikers"
|
||||
noInquiryUrlWarning: "Contact-URL niet opgegeven"
|
||||
muteThread: "Discussies dempen "
|
||||
unmuteThread: "Dempen van discussie ongedaan maken"
|
||||
followingVisibility: "Zichtbaarheid van gevolgden"
|
||||
followersVisibility: "Zichtbaarheid van volgers"
|
||||
incorrectTotp: "Het eenmalige wachtwoord is incorrect of verlopen"
|
||||
hide: "Verbergen"
|
||||
searchByGoogle: "Zoeken"
|
||||
threeMonths: "3 maanden"
|
||||
oneYear: "1 jaar"
|
||||
threeDays: "3 dagen"
|
||||
cropImage: "Afbeelding bijsnijden"
|
||||
cropImageAsk: "Bijsnijdengevraagd"
|
||||
file: "Bestanden"
|
||||
|
@ -459,9 +516,33 @@ pushNotificationAlreadySubscribed: "Pushberichtrn al ingeschakeld"
|
|||
windowMaximize: "Maximaliseren"
|
||||
windowRestore: "Herstellen"
|
||||
loggedInAsBot: "Momenteel als bot ingelogd"
|
||||
correspondingSourceIsAvailable: "De bijbehorende broncode is beschikbaar bij {anchor}"
|
||||
invalidParamErrorDescription: "De aanvraagparameters zijn ongeldig. Dit komt meestal door een bug, maar kan ook omdat de invoer te lang is of iets dergelijks."
|
||||
collapseRenotes: "Renotes die je al gezien hebt, inklappen"
|
||||
collapseRenotesDescription: "Klapt notities in waar je al op gereageerd hebt of die je al gerenotet hebt."
|
||||
prohibitedWords: "Verboden woorden"
|
||||
prohibitedWordsDescription: "Activeert een foutmelding als er geprobeerd wordt een notitie met de ingestelde woorden te plaatsen. Meerdere woorden kunnen worden ingesteld, elk op hun eigen regel."
|
||||
hiddenTags: "Verborgen hashtags"
|
||||
hiddenTagsDescription: "Selecteer tags die niet worden weergegeven in de trends. Meerdere tags kunnen worden geregistreerd, elk op hun eigen regel."
|
||||
enableStatsForFederatedInstances: "Statistieken van remote servers ontvangen"
|
||||
limitWidthOfReaction: "Limiteert de maximale breedte van reacties en geef ze verkleind weer"
|
||||
audio: "Audio"
|
||||
audioFiles: "Audio"
|
||||
archived: "Gearchiveerd"
|
||||
unarchive: "Dearchiveren"
|
||||
lookupConfirm: "Weet je zeker dat je dit wil opzoeken?"
|
||||
openTagPageConfirm: "Wil je deze hashtagpagina openen?"
|
||||
specifyHost: "Specificeer host"
|
||||
icon: "Avatar"
|
||||
replies: "Antwoord"
|
||||
renotes: "Herdelen"
|
||||
followingOrFollower: "Gevolgd of volger"
|
||||
confirmShowRepliesAll: "Dit is een onomkeerbare operatie. Weet je zeker dat reacties op anderen van iedereen die je volgt, wil weergeven in je tijdlijn?"
|
||||
information: "Over"
|
||||
_chat:
|
||||
invitations: "Uitnodigen"
|
||||
members: "Leden"
|
||||
home: "Startpagina"
|
||||
_delivery:
|
||||
stop: "Opgeschort"
|
||||
_type:
|
||||
|
|
|
@ -299,8 +299,6 @@ text: "Tekst"
|
|||
next: "Neste"
|
||||
retype: "Gjenta"
|
||||
quoteAttached: "Sitat"
|
||||
noMessagesYet: "Ingen meldinger ennå"
|
||||
newMessageExists: "Det er nye meldinger"
|
||||
onlyOneFileCanBeAttached: "Du kan bare legge ved én fil i en melding"
|
||||
invitations: "Inviter"
|
||||
available: "Tilgjengelig"
|
||||
|
@ -463,6 +461,12 @@ icon: "Avatar"
|
|||
replies: "Svar"
|
||||
renotes: "Renote"
|
||||
surrender: "Avbryt"
|
||||
information: "Informasjon"
|
||||
_chat:
|
||||
invitations: "Inviter"
|
||||
members: "Medlemmer"
|
||||
home: "Hjem"
|
||||
send: "Send"
|
||||
_delivery:
|
||||
stop: "Suspendert"
|
||||
_initialAccountSetting:
|
||||
|
|
|
@ -269,7 +269,6 @@ deleteAreYouSure: "Czy na pewno chcesz usunąć „{x}”?"
|
|||
resetAreYouSure: "Czy na pewno chcesz zresetować?"
|
||||
areYouSure: "Na pewno?"
|
||||
saved: "Zapisano"
|
||||
messaging: "Wiadomości"
|
||||
upload: "Wyślij"
|
||||
keepOriginalUploading: "Zachowaj oryginalny obraz"
|
||||
keepOriginalUploadingDescription: "Zapisuje oryginalnie przesłany obraz w niezmienionej postaci. Jeśli ta opcja jest wyłączona, po przesłaniu zostanie wygenerowana wersja do wyświetlenia w Internecie."
|
||||
|
@ -282,7 +281,6 @@ uploadFromUrlMayTakeTime: "Wysyłanie może chwilę potrwać."
|
|||
explore: "Eksploruj"
|
||||
messageRead: "Przeczytano"
|
||||
noMoreHistory: "Nie ma dalszej historii"
|
||||
startMessaging: "Rozpocznij czat"
|
||||
nUsersRead: "przeczytano przez {n}"
|
||||
agreeTo: "Wyrażam zgodę na {0}"
|
||||
agree: "Zatwierdź"
|
||||
|
@ -466,8 +464,6 @@ retype: "Wprowadź ponownie"
|
|||
noteOf: "Wpisy {user}"
|
||||
quoteAttached: "Zacytowano"
|
||||
quoteQuestion: "Czy na pewno chcesz umieścić cytat?"
|
||||
noMessagesYet: "Nie napisano jeszcze wiadomości"
|
||||
newMessageExists: "Masz nową wiadomość"
|
||||
onlyOneFileCanBeAttached: "Możesz załączyć tylko jeden plik do wiadomości"
|
||||
signinRequired: "Proszę się zalogować"
|
||||
invitations: "Zaproś"
|
||||
|
@ -1044,6 +1040,14 @@ flip: "Odwróć"
|
|||
lastNDays: "W ciągu ostatnich {n} dni"
|
||||
surrender: "Odrzuć"
|
||||
gameRetry: "Spróbuj ponownie"
|
||||
postForm: "Formularz tworzenia wpisu"
|
||||
information: "Informacje"
|
||||
_chat:
|
||||
invitations: "Zaproś"
|
||||
noHistory: "Brak historii"
|
||||
members: "Członkowie"
|
||||
home: "Strona główna"
|
||||
send: "Wyślij"
|
||||
_delivery:
|
||||
stop: "Zawieszono"
|
||||
_type:
|
||||
|
@ -1208,7 +1212,6 @@ _theme:
|
|||
header: "Nagłówek"
|
||||
navBg: "Tło paska bocznego"
|
||||
navFg: "Tekst paska bocznego"
|
||||
navHoverFg: "Tekst paska bocznego (zbliżenie)"
|
||||
navActive: "Tekst paska bocznego (aktywny)"
|
||||
navIndicator: "Wskaźnik paska bocznego"
|
||||
link: "Odnośnik"
|
||||
|
@ -1231,11 +1234,8 @@ _theme:
|
|||
buttonHoverBg: "Tło przycisku (po najechaniu)"
|
||||
inputBorder: "Obramowanie pola wejścia"
|
||||
driveFolderBg: "Tło folderu na dysku"
|
||||
wallpaperOverlay: "Nakładka tapety"
|
||||
badge: "Odznaka"
|
||||
messageBg: "Tło czatu"
|
||||
accentDarken: "Akcent (ciemniejszy)"
|
||||
accentLighten: "Akcent (jaśniejszy)"
|
||||
fgHighlighted: "Wyróżniony tekst"
|
||||
_sfx:
|
||||
note: "Wpisy"
|
||||
|
@ -1300,6 +1300,7 @@ _permissions:
|
|||
"write:gallery": "Edytuj swoją galerię"
|
||||
"read:gallery-likes": "Wyświetlanie listy polubionych postów w galerii"
|
||||
"write:gallery-likes": "Edytowanie listy polubionych postów w galerii"
|
||||
"write:chat": "Tworzenie lub usuwanie wiadomości czatu"
|
||||
_auth:
|
||||
shareAccessTitle: "Przyznawanie uprawnień aplikacji"
|
||||
shareAccess: "Czy chcesz autoryzować „{name}” do dostępu do tego konta?"
|
||||
|
|
|
@ -287,7 +287,6 @@ deleteAreYouSure: "Deseja excluir \"{x}\"?"
|
|||
resetAreYouSure: "Deseja reiniciar?"
|
||||
areYouSure: "Tem certeza?"
|
||||
saved: "Salvo"
|
||||
messaging: "Chat"
|
||||
upload: "Fazer upload"
|
||||
keepOriginalUploading: "Manter a imagem original"
|
||||
keepOriginalUploadingDescription: "Ao fazer o upload de uma imagem, ela será mantida em sua versão original. Caso desative esta opção, o navegador irá gerar uma versão da imagem otimizada para publicação na web durante o upload."
|
||||
|
@ -300,7 +299,6 @@ uploadFromUrlMayTakeTime: "Pode levar algum tempo para que o upload seja conclu
|
|||
explore: "Explorar"
|
||||
messageRead: "Lida"
|
||||
noMoreHistory: "Não existe histórico anterior"
|
||||
startMessaging: "Iniciar conversação"
|
||||
nUsersRead: "{n} pessoas leram"
|
||||
agreeTo: "Eu concordo com {0}"
|
||||
agree: "Concordar"
|
||||
|
@ -489,8 +487,6 @@ noteOf: "Publicação de {user}"
|
|||
quoteAttached: "Com citação"
|
||||
quoteQuestion: "Anexar como citação?"
|
||||
attachAsFileQuestion: "O texto na área de transferência é muito longo. Você gostaria de anexá-lo como um arquivo de texto?"
|
||||
noMessagesYet: "Sem conversas até o momento"
|
||||
newMessageExists: "Há uma nova mensagem"
|
||||
onlyOneFileCanBeAttached: "Apenas um arquivo pode ser anexado a uma mensagem"
|
||||
signinRequired: "É necessário se inscrever ou fazer login antes de continuar"
|
||||
signinOrContinueOnRemote: "Para continuar, você precisa mover o seu servidor ou entrar/cadastrar-se nesse servidor."
|
||||
|
@ -1301,6 +1297,16 @@ lockdown: "Lockdown"
|
|||
pleaseSelectAccount: "Selecione uma conta"
|
||||
availableRoles: "Cargos disponíveis"
|
||||
acknowledgeNotesAndEnable: "Ative após compreender as precauções."
|
||||
postForm: "Campo de postagem"
|
||||
information: "Informações"
|
||||
_chat:
|
||||
invitations: "Convidar"
|
||||
noHistory: "Ainda não há histórico"
|
||||
members: "Membros"
|
||||
home: "Início"
|
||||
send: "Enviar"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "Exigir cadastro para ver o conteúdo"
|
||||
requireSigninToViewContentsDescription1: "Exigir cadastro para ver todas as notas e outro conteúdo que você criou. Isso previne 'crawlers' de coletar os seus dados."
|
||||
|
@ -1991,7 +1997,6 @@ _theme:
|
|||
header: "Cabeçalho"
|
||||
navBg: "Plano de fundo da barra lateral"
|
||||
navFg: "Texto da barra lateral"
|
||||
navHoverFg: "Texto da coluna lateral (Selecionado)"
|
||||
navActive: "Texto da coluna lateral (Ativa)"
|
||||
navIndicator: "Indicador da coluna lateral"
|
||||
link: "Link"
|
||||
|
@ -2014,11 +2019,8 @@ _theme:
|
|||
buttonHoverBg: "Plano de fundo de botão (Selecionado)"
|
||||
inputBorder: "Borda de campo digitável"
|
||||
driveFolderBg: "Plano de fundo da pasta no Drive"
|
||||
wallpaperOverlay: "Sobreposição do papel de parede."
|
||||
badge: "Emblema"
|
||||
messageBg: "Plano de fundo do chat"
|
||||
accentDarken: "Cor de destaque (Escurecida)"
|
||||
accentLighten: "Cor de destaque (Esclarecida)"
|
||||
fgHighlighted: "Texto Destacado"
|
||||
_sfx:
|
||||
note: "Posts"
|
||||
|
@ -2171,6 +2173,7 @@ _permissions:
|
|||
"read:clip-favorite": "Ver Clipes favoritados"
|
||||
"read:federation": "Ver dados de federação"
|
||||
"write:report-abuse": "Reportar violação"
|
||||
"write:chat": "Compor ou editar mensagens de chat"
|
||||
_auth:
|
||||
shareAccessTitle: "Conceder permissões do aplicativo"
|
||||
shareAccess: "Você gostaria de autorizar \"{name}\" para acessar essa conta?"
|
||||
|
@ -2595,10 +2598,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "Tenha certeza de que o distribuidor desse recurso é confiável antes da instalação."
|
||||
_plugin:
|
||||
title: "Deseja instalar esse plugin?"
|
||||
metaTitle: "Informações do plugin"
|
||||
_theme:
|
||||
title: "Deseja instalar esse tema?"
|
||||
metaTitle: "Informações do tema"
|
||||
_meta:
|
||||
base: "Paleta de cores base"
|
||||
_vendorInfo:
|
||||
|
|
|
@ -254,7 +254,6 @@ removeAreYouSure: "Ești sigur că vrei să înlături {x}?"
|
|||
deleteAreYouSure: "Ești sigur că vrei să ștergi {x}?"
|
||||
resetAreYouSure: "Sigur vrei să resetezi?"
|
||||
saved: "Salvat"
|
||||
messaging: "Chat"
|
||||
upload: "Încarcă"
|
||||
keepOriginalUploading: "Păstrează imaginea originală"
|
||||
keepOriginalUploadingDescription: "Salvează imaginea originala încărcată fără modificări. Dacă e oprită, o versiune pentru afișarea pe web va fi generată la încărcare."
|
||||
|
@ -267,7 +266,6 @@ uploadFromUrlMayTakeTime: "S-ar putea să ia puțin până se finalizează înc
|
|||
explore: "Explorează"
|
||||
messageRead: "Citit"
|
||||
noMoreHistory: "Nu există mai mult istoric"
|
||||
startMessaging: "Începe un chat nou"
|
||||
nUsersRead: "citit de {n}"
|
||||
agreeTo: "Sunt de acord cu {0}"
|
||||
start: "Să începem"
|
||||
|
@ -429,8 +427,6 @@ retype: "Introdu din nou"
|
|||
noteOf: "Notă de {user}"
|
||||
quoteAttached: "Citat"
|
||||
quoteQuestion: "Vrei să adaugi ca citat?"
|
||||
noMessagesYet: "Niciun mesaj încă"
|
||||
newMessageExists: "Ai mesaje noi"
|
||||
onlyOneFileCanBeAttached: "Poți atașa un singur fișier la un mesaj"
|
||||
signinRequired: "Te rog autentifică-te"
|
||||
invitations: "Invită"
|
||||
|
@ -646,6 +642,13 @@ show: "Arată"
|
|||
icon: "Avatar"
|
||||
replies: "Răspunde"
|
||||
renotes: "Re-notează"
|
||||
information: "Despre"
|
||||
_chat:
|
||||
invitations: "Invită"
|
||||
noHistory: "Nu există istoric"
|
||||
members: "Membri"
|
||||
home: "Acasă"
|
||||
send: "Trimite"
|
||||
_delivery:
|
||||
stop: "Suspendat"
|
||||
_type:
|
||||
|
|
|
@ -282,7 +282,6 @@ deleteAreYouSure: "Хотите удалить «{x}»?"
|
|||
resetAreYouSure: "На самом деле сбросить?"
|
||||
areYouSure: "Вы уверены?"
|
||||
saved: "Сохранено"
|
||||
messaging: "Сообщения"
|
||||
upload: "Загрузить"
|
||||
keepOriginalUploading: "Сохранить исходное изображение"
|
||||
keepOriginalUploadingDescription: "Сохраняет исходную версию при загрузке изображений. Если выключить, то при загрузке браузер генерирует изображение для публикации."
|
||||
|
@ -295,7 +294,6 @@ uploadFromUrlMayTakeTime: "Загрузка может занять некото
|
|||
explore: "Обзор"
|
||||
messageRead: "Прочитали"
|
||||
noMoreHistory: "История закончилась"
|
||||
startMessaging: "Начать общение"
|
||||
nUsersRead: "Прочитали {n}"
|
||||
agreeTo: "Я соглашаюсь с {0}"
|
||||
agree: "Согласен"
|
||||
|
@ -482,8 +480,6 @@ noteOf: "Что пишет {user}"
|
|||
quoteAttached: "Цитата"
|
||||
quoteQuestion: "Хотите добавить цитату?"
|
||||
attachAsFileQuestion: "Текста в буфере обмена слишком много. Прикрепить как текстовый файл?"
|
||||
noMessagesYet: "Пока ни одного сообщения"
|
||||
newMessageExists: "Новое сообщение"
|
||||
onlyOneFileCanBeAttached: "К сообщению можно прикрепить только один файл"
|
||||
signinRequired: "Пожалуйста, войдите"
|
||||
signinOrContinueOnRemote: "Чтобы продолжить, вам необходимо войти в аккаунт на своём сервере или зарегистрироваться / войти в аккаунт на этом."
|
||||
|
@ -1181,6 +1177,16 @@ keepOriginalFilenameDescription: "Если вы выключите данную
|
|||
alwaysConfirmFollow: "Всегда подтверждать подписку"
|
||||
inquiry: "Связаться"
|
||||
messageToFollower: "Сообщение подписчикам"
|
||||
postForm: "Форма отправки"
|
||||
information: "Описание"
|
||||
_chat:
|
||||
invitations: "Пригласить"
|
||||
noHistory: "История пока пуста"
|
||||
members: "Участники"
|
||||
home: "Главная"
|
||||
send: "Отправить"
|
||||
_settings:
|
||||
webhook: "Вебхук"
|
||||
_delivery:
|
||||
stop: "Заморожено"
|
||||
_type:
|
||||
|
@ -1683,7 +1689,6 @@ _theme:
|
|||
header: "Заголовок"
|
||||
navBg: "Фон боковой панели"
|
||||
navFg: "Текст на боковой панели"
|
||||
navHoverFg: "Текст на боковой панели (под указателем)"
|
||||
navActive: "Текст на боковой панели (активирован)"
|
||||
navIndicator: "Индикатор на боковой панели"
|
||||
link: "Ссылка"
|
||||
|
@ -1706,11 +1711,8 @@ _theme:
|
|||
buttonHoverBg: "Текст кнопки"
|
||||
inputBorder: "Рамка поля ввода"
|
||||
driveFolderBg: "Фон папки «Диска»"
|
||||
wallpaperOverlay: "Слой обоев"
|
||||
badge: "Значок"
|
||||
messageBg: "Фон беседы"
|
||||
accentDarken: "Фон (затемнённый)"
|
||||
accentLighten: "Фон (осветлённый)"
|
||||
fgHighlighted: "Подсвеченный текст"
|
||||
_sfx:
|
||||
note: "Заметки"
|
||||
|
@ -1799,6 +1801,7 @@ _permissions:
|
|||
"read:gallery-likes": "Просмотр списка понравившегося в галерее"
|
||||
"write:gallery-likes": "Изменение списка понравившегося в галерее"
|
||||
"write:admin:reset-password": "Сбросить пароль пользователю"
|
||||
"write:chat": "Писать и удалять сообщения"
|
||||
_auth:
|
||||
shareAccessTitle: "Разрешения для приложений"
|
||||
shareAccess: "Дать доступ для «{name}» к вашей учётной записи?"
|
||||
|
|
|
@ -242,7 +242,6 @@ removeAreYouSure: "Naozaj chcete odstrániť \"{x}\"?"
|
|||
deleteAreYouSure: "Naozaj chcete odstrániť \"{x}\"?"
|
||||
resetAreYouSure: "Naozaj resetovať?"
|
||||
saved: "Uložené"
|
||||
messaging: "Chat"
|
||||
upload: "Nahrať súbor"
|
||||
keepOriginalUploading: "Zachovať pôvodný obrázok"
|
||||
keepOriginalUploadingDescription: "Uloží pôvodný obrázok ako je. Ak je vypnuté, verzia pre web sa vygeneruje pri nahratí."
|
||||
|
@ -255,7 +254,6 @@ uploadFromUrlMayTakeTime: "Nahrávanie môže nejaký čas trvať."
|
|||
explore: "Objavovať"
|
||||
messageRead: "Prečítané"
|
||||
noMoreHistory: "To je všetko"
|
||||
startMessaging: "Začať chat"
|
||||
nUsersRead: "prečítané {n} používateľmi"
|
||||
agreeTo: "Súhlasím s {0}"
|
||||
agreeBelow: "Súhlasím s nasledovným"
|
||||
|
@ -428,8 +426,6 @@ retype: "Zadajte znovu"
|
|||
noteOf: "Poznámky používateľa {user}"
|
||||
quoteAttached: "Citované"
|
||||
quoteQuestion: "Pripojiť ako citát?"
|
||||
noMessagesYet: "Zatiaľ žiadne správy"
|
||||
newMessageExists: "Máte novú správu"
|
||||
onlyOneFileCanBeAttached: "Ku správe môžete priložiť len jeden súbor"
|
||||
signinRequired: "Prihláste sa, prosím!"
|
||||
invitations: "Pozvať"
|
||||
|
@ -917,6 +913,14 @@ renotes: "Preposlať"
|
|||
sourceCode: "Zdrojový kód"
|
||||
flip: "Preklopiť"
|
||||
lastNDays: "Posledných {n} dní"
|
||||
postForm: "Napísať poznámku"
|
||||
information: "Informácie"
|
||||
_chat:
|
||||
invitations: "Pozvať"
|
||||
noHistory: "Žiadna história"
|
||||
members: "Členovia"
|
||||
home: "Domov"
|
||||
send: "Poslať"
|
||||
_delivery:
|
||||
stop: "Zmrazené"
|
||||
_type:
|
||||
|
@ -1085,7 +1089,6 @@ _theme:
|
|||
header: "Hlavička"
|
||||
navBg: "Pozadie bočného panela"
|
||||
navFg: "Text bočného panela"
|
||||
navHoverFg: "Text bočného panela (pod kurzorom)"
|
||||
navActive: "Text bočného panela (aktívny)"
|
||||
navIndicator: "Indikátor bočného panela"
|
||||
link: "Odkaz"
|
||||
|
@ -1108,11 +1111,8 @@ _theme:
|
|||
buttonHoverBg: "Pozadie tlačidla (pod kurzorom)"
|
||||
inputBorder: "Okraj vstupného poľa"
|
||||
driveFolderBg: "Pozadie priečinu disku"
|
||||
wallpaperOverlay: "Vrstvenie pozadia"
|
||||
badge: "Odznak"
|
||||
messageBg: "Pozadie chatu"
|
||||
accentDarken: "Akcent (stmavené)"
|
||||
accentLighten: "Akcent (zosvetlené)"
|
||||
fgHighlighted: "Zvýraznený text"
|
||||
_sfx:
|
||||
note: "Poznámky"
|
||||
|
@ -1176,6 +1176,7 @@ _permissions:
|
|||
"write:gallery": "Upravovať vašu galériu"
|
||||
"read:gallery-likes": "Vidieť zoznam obľúbených príspevkov z galérie"
|
||||
"write:gallery-likes": "Upraviť zoznam obľúbených príspevov z galérie"
|
||||
"write:chat": "Písať alebo odstraňovať správy v chate"
|
||||
_auth:
|
||||
shareAccess: "Prajete si povoliť \"{name}\", aby mal prístup k tomuto účtu?"
|
||||
shareAccessAsk: "Naozaj chcete povoliť tejto aplikácii prístup k tomuto účtu?"
|
||||
|
|
|
@ -249,7 +249,6 @@ removeAreYouSure: "Är du säker att du vill radera \"{x}\"?"
|
|||
deleteAreYouSure: "Är du säker att du vill radera \"{x}\"?"
|
||||
resetAreYouSure: "Vill du återställa?"
|
||||
saved: "Sparad"
|
||||
messaging: "Chatt"
|
||||
upload: "Ladda upp"
|
||||
keepOriginalUploading: "Behåll originalbild"
|
||||
keepOriginalUploadingDescription: "Sparar den originellt uppladdade bilden i sitt i befintliga skick. Om avstängd, kommer en webbversion bli genererad vid uppladdning."
|
||||
|
@ -262,7 +261,6 @@ uploadFromUrlMayTakeTime: "Det kan ta tid tills att uppladdningen blir klar."
|
|||
explore: "Utforska"
|
||||
messageRead: "Läs"
|
||||
noMoreHistory: "Det finns ingen mer historik"
|
||||
startMessaging: "Starta en chatt"
|
||||
nUsersRead: "läst av {n}"
|
||||
agreeTo: "Jag accepterar {0}"
|
||||
agree: "Överens"
|
||||
|
@ -394,7 +392,6 @@ text: "Text"
|
|||
enable: "Aktivera"
|
||||
next: "Nästa"
|
||||
retype: "Ange igen"
|
||||
noMessagesYet: "Inga meddelanden än"
|
||||
invitations: "Inbjudan"
|
||||
invitationCode: "Inbjudningskod"
|
||||
available: "Tillgängligt"
|
||||
|
@ -562,6 +559,12 @@ inquiry: "Kontakt"
|
|||
tryAgain: "Försök igen senare"
|
||||
signinWithPasskey: "Logga in med nyckel"
|
||||
unknownWebAuthnKey: "Okänd nyckel"
|
||||
information: "Om"
|
||||
_chat:
|
||||
invitations: "Inbjudan"
|
||||
members: "Medlemmar"
|
||||
home: "Hem"
|
||||
send: "Skicka"
|
||||
_delivery:
|
||||
stop: "Suspenderad"
|
||||
_type:
|
||||
|
|
|
@ -287,7 +287,6 @@ deleteAreYouSure: "ต้องการลบ “{x}” ใช่ไหม?"
|
|||
resetAreYouSure: "รีเซ็ตเลยไหม?"
|
||||
areYouSure: "แน่ใจแล้วใช่ไหมคะ?"
|
||||
saved: "บันทึกแล้ว"
|
||||
messaging: "แชท"
|
||||
upload: "อัปโหลด"
|
||||
keepOriginalUploading: "เก็บภาพต้นฉบับ"
|
||||
keepOriginalUploadingDescription: "เก็บภาพต้นฉบับไว้เมื่ออัปโหลดภาพ หากปิด รูปภาพสำหรับการเผยแพร่ทางเว็บจะถูกสร้างขึ้นในเบราว์เซอร์เมื่อทำการอัปโหลด"
|
||||
|
@ -300,7 +299,6 @@ uploadFromUrlMayTakeTime: "การอัปโหลดอาจใช้เ
|
|||
explore: "สำรวจ"
|
||||
messageRead: "อ่านแล้ว"
|
||||
noMoreHistory: "ไม่มีประวัติเพิ่มเติม"
|
||||
startMessaging: "เริ่มการสนทนา"
|
||||
nUsersRead: "อ่านโดย {n}"
|
||||
agreeTo: "ฉันยอมรับ {0}"
|
||||
agree: "ยอมรับ"
|
||||
|
@ -489,8 +487,6 @@ noteOf: "โน้ตของ {user}"
|
|||
quoteAttached: "อ้างอิง"
|
||||
quoteQuestion: "ต้องการที่จะแนบมันเพื่ออ้างอิงใช่ไหม?"
|
||||
attachAsFileQuestion: "ข้อความในคลิปบอร์ดยาวเกินไป คุณต้องการแนบเป็นไฟล์ข้อความหรือไม่?"
|
||||
noMessagesYet: "ยังไม่มีข้อความ"
|
||||
newMessageExists: "คุณมีข้อความใหม่"
|
||||
onlyOneFileCanBeAttached: "สามารถแนบไฟล์ได้เพียงไฟล์เดียวต่อ 1 ข้อความ"
|
||||
signinRequired: "ก่อนดำเนินการต่อ กรุณาลงทะเบียนหรือเข้าสู่ระบบ"
|
||||
signinOrContinueOnRemote: "เพื่อดำเนินการต่อได้ คุณต้องไปที่เซิร์ฟเวอร์ที่คุณใช้งานอยู่ หรือลงทะเบียน/เข้าสู่ระบบเซิร์ฟเวอร์นี้"
|
||||
|
@ -1292,6 +1288,16 @@ prohibitedWordsForNameOfUser: "คำนี้ไม่สามารถใช
|
|||
prohibitedWordsForNameOfUserDescription: "หากมีสตริงใดๆ ในรายการนี้ปรากฏอยู่ในชื่อของผู้ใช้ ชื่อนั้นจะถูกปฏิเสธ ผู้ใช้ที่มีสิทธิ์แต่ผู้ดูแลระบบนั้นจะไม่ได้รับผลกระทบใดๆจากข้อจำกัดนี้ค่ะ"
|
||||
yourNameContainsProhibitedWords: "ชื่อของคุณนั้นมีคำที่ต้องห้าม"
|
||||
yourNameContainsProhibitedWordsDescription: "ถ้าหากคุณต้องการใช้ชื่อนี้ กรุณาติดต่อผู้ดูแลระบบของเซิร์ฟเวอร์นะค่ะ"
|
||||
postForm: "แบบฟอร์มการโพสต์"
|
||||
information: "เกี่ยวกับ"
|
||||
_chat:
|
||||
invitations: "คำเชิญ"
|
||||
noHistory: "ไม่มีประวัติ"
|
||||
members: "สมาชิก"
|
||||
home: "หน้าหลัก"
|
||||
send: "ส่ง"
|
||||
_settings:
|
||||
webhook: "Webhook"
|
||||
_abuseUserReport:
|
||||
forward: "ส่งต่อ"
|
||||
forwardDescription: "ส่งรายงานไปยังเซิร์ฟเวอร์ระยะไกลโดยใช้บัญชีระบบที่ไม่ระบุตัวตน"
|
||||
|
@ -1968,7 +1974,6 @@ _theme:
|
|||
header: "ส่วนหัว"
|
||||
navBg: "พื้นหลังแถบด้านข้าง"
|
||||
navFg: "ข้อความแถบด้านข้าง"
|
||||
navHoverFg: "ข้อความแถบด้านข้าง (โฮเวอร์)"
|
||||
navActive: "ข้อความแถบด้านข้าง (ใช้งานอยู่)"
|
||||
navIndicator: "ตัวระบุแถบด้านข้าง"
|
||||
link: "ลิงก์"
|
||||
|
@ -1991,11 +1996,8 @@ _theme:
|
|||
buttonHoverBg: "ปุ่มพื้นหลัง (โฮเวอร์)"
|
||||
inputBorder: "เส้นขอบของช่องป้อนข้อมูล"
|
||||
driveFolderBg: "พื้นหลังโฟลเดอร์ไดรฟ์"
|
||||
wallpaperOverlay: "วอลล์เปเปอร์ซ้อนทับ"
|
||||
badge: "ตรา"
|
||||
messageBg: "พื้นหลังแชท"
|
||||
accentDarken: "สีหลัก (มืด)"
|
||||
accentLighten: "สีหลัก (สว่าง)"
|
||||
fgHighlighted: "ข้อความที่ไฮไลต์"
|
||||
_sfx:
|
||||
note: "โน้ต"
|
||||
|
@ -2148,6 +2150,7 @@ _permissions:
|
|||
"read:clip-favorite": "ดูคลิปที่ถูกใจ"
|
||||
"read:federation": "รับข้อมูลเกี่ยวกับสหพันธ์"
|
||||
"write:report-abuse": "รายงานการละเมิด"
|
||||
"write:chat": "เขียนหรือลบข้อความแชท"
|
||||
_auth:
|
||||
shareAccessTitle: "การให้สิทธิ์แอปพลิเคชัน"
|
||||
shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?"
|
||||
|
@ -2569,10 +2572,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "โปรดตรวจสอบให้แน่ใจว่าแหล่งแจกหน่ายมีความน่าเชื่อถือก่อนทำการติดตั้ง"
|
||||
_plugin:
|
||||
title: "ต้องการติดตั้งปลั๊กอินนี้ใช่ไหม?"
|
||||
metaTitle: "ข้อมูลส่วนเสริม"
|
||||
_theme:
|
||||
title: "ต้องการติดตั้งธีมนี้ใช่ไหม?"
|
||||
metaTitle: "ข้อมูลธีม"
|
||||
_meta:
|
||||
base: "โทนสีพื้นฐาน"
|
||||
_vendorInfo:
|
||||
|
|
|
@ -261,7 +261,6 @@ removeAreYouSure: "\"{x}\" silmek istediğinizden emin misiniz?"
|
|||
deleteAreYouSure: "\"{x}\" silmek istediğinizden emin misiniz?"
|
||||
resetAreYouSure: "Sıfırlansın mı?"
|
||||
saved: "Kaydedildi"
|
||||
messaging: "Mesajlar"
|
||||
upload: "Yükle"
|
||||
keepOriginalUploading: "Orijinal görseli koru"
|
||||
keepOriginalUploadingDescription: "Orijinal olarak yüklenen görüntüyü olduğu gibi kaydeder. Kapatılırsa, yükleme sırasında web'de görüntülenecek bir sürüm oluşturulur."
|
||||
|
@ -274,7 +273,6 @@ uploadFromUrlMayTakeTime: "Yüklemenin tamamlanması biraz süre alabilir."
|
|||
explore: "Keşfet"
|
||||
messageRead: "Okundu"
|
||||
noMoreHistory: "Bundan öncesi yok"
|
||||
startMessaging: "Yeni bir sohbet başlat"
|
||||
nUsersRead: "{n} kişi okudu"
|
||||
agreeTo: "Kabul Ediyorum: {0}"
|
||||
agree: "Kabul Et"
|
||||
|
@ -351,7 +349,6 @@ pinnedNotes: "Sabitlenen"
|
|||
manageAntennas: "Anten ayarları"
|
||||
userList: "Listeler"
|
||||
resetPassword: "Şifre sıfırlama"
|
||||
noMessagesYet: "Şimdilik mesaj yok"
|
||||
details: "Detaylar"
|
||||
deck: "Güverte"
|
||||
smtpHost: "Sağlayıcı"
|
||||
|
@ -378,6 +375,8 @@ addMemo: "Kısa not ekle"
|
|||
icon: "Avatar"
|
||||
replies: "yanıt"
|
||||
renotes: "vazgeçme"
|
||||
_chat:
|
||||
home: "Ana sayfa"
|
||||
_delivery:
|
||||
stop: "Askıya alınmış"
|
||||
_type:
|
||||
|
|
|
@ -246,7 +246,6 @@ removeAreYouSure: "Ви впевнені, що хочете видалити \"{
|
|||
deleteAreYouSure: "Ви впевнені, що хочете видалити \"{x}\"?"
|
||||
resetAreYouSure: "Справді скинути?"
|
||||
saved: "Збережено"
|
||||
messaging: "Чати"
|
||||
upload: "Завантажити"
|
||||
keepOriginalUploading: "Зберегти оригінальне зображення"
|
||||
keepOriginalUploadingDescription: "Зберігає початково завантажене зображення як є. Якщо вимкнено, версія для відображення в Інтернеті буде створена під час завантаження."
|
||||
|
@ -259,7 +258,6 @@ uploadFromUrlMayTakeTime: "Завантаження може зайняти де
|
|||
explore: "Огляд"
|
||||
messageRead: "Прочитано"
|
||||
noMoreHistory: "Подальшої історії немає"
|
||||
startMessaging: "Розпочати діалог"
|
||||
nUsersRead: "Прочитали {n}"
|
||||
agreeTo: "Я погоджуюсь з {0}"
|
||||
agreeBelow: "Я погоджуюся з наведеним нижче"
|
||||
|
@ -427,8 +425,6 @@ retype: "Введіть ще раз"
|
|||
noteOf: "Нотатка {user}"
|
||||
quoteAttached: "Цитата"
|
||||
quoteQuestion: "Ви хочете додати цитату?"
|
||||
noMessagesYet: "Ще немає повідомлень"
|
||||
newMessageExists: "Є нові повідомлення"
|
||||
onlyOneFileCanBeAttached: "До повідомлення можна вкласти лише один файл"
|
||||
signinRequired: "Будь ласка, авторизуйтесь"
|
||||
invitations: "Запрошення"
|
||||
|
@ -909,6 +905,14 @@ renotes: "Поширити"
|
|||
sourceCode: "Вихідний код"
|
||||
flip: "Перевернути"
|
||||
lastNDays: "Останні {n} днів"
|
||||
postForm: "Створення нотатки"
|
||||
information: "Інформація"
|
||||
_chat:
|
||||
invitations: "Запросити"
|
||||
noHistory: "Історія порожня"
|
||||
members: "Учасники"
|
||||
home: "Домівка"
|
||||
send: "Відправити"
|
||||
_delivery:
|
||||
stop: "Призупинено"
|
||||
_type:
|
||||
|
@ -1279,7 +1283,6 @@ _theme:
|
|||
header: "Заголовок"
|
||||
navBg: "Фон бокової панелі"
|
||||
navFg: "Текст бокової панелі"
|
||||
navHoverFg: "Текст бокової панелі (під курсором)"
|
||||
navActive: "Текст бокової панелі (активне)"
|
||||
navIndicator: "Індикатор бокової панелі"
|
||||
link: "Посилання"
|
||||
|
@ -1302,11 +1305,8 @@ _theme:
|
|||
buttonHoverBg: "Фон кнопки (при наведенні)"
|
||||
inputBorder: "Край поля вводу"
|
||||
driveFolderBg: "Фон папки на диску"
|
||||
wallpaperOverlay: "Накладання шпалер"
|
||||
badge: "Значок"
|
||||
messageBg: "Фон переписки"
|
||||
accentDarken: "Акцент (Затемлений)"
|
||||
accentLighten: "Акцент (Освітлений)"
|
||||
fgHighlighted: "Виділений текст"
|
||||
_sfx:
|
||||
note: "Нотатки"
|
||||
|
@ -1365,6 +1365,7 @@ _permissions:
|
|||
"read:channels": "Переглядати канали"
|
||||
"write:channels": "Змінювати канали"
|
||||
"read:gallery": "Перегляд галереї"
|
||||
"write:chat": "Створювати та видаляти повідомлення"
|
||||
_auth:
|
||||
shareAccess: "Ви хочете надати \"{name}\" доступ до цього акаунту?"
|
||||
shareAccessAsk: "Ви впевнені, що хочете надати цій програмі доступ до вашого акаунту?"
|
||||
|
|
|
@ -257,7 +257,6 @@ removeAreYouSure: "“{x}”ni olib tashlamoqchi ekanligingizga ishonchingiz kom
|
|||
deleteAreYouSure: "“{x}”ni chindan ham yo'q qilmoqchimisiz?"
|
||||
resetAreYouSure: "Haqiqatan ham qayta tiklansinmi?"
|
||||
saved: "Saqlandi"
|
||||
messaging: "Suhbat"
|
||||
upload: "Yuklash"
|
||||
keepOriginalUploading: "Asl rasmni saqlang"
|
||||
keepOriginalUploadingDescription: "Rasmlarni yuklashda asl nusxasini saqlaydi. Agar o'chirilgan bo'lsa, brauzer yuklangandan keyin nashr qilish uchun rasm yaratadi."
|
||||
|
@ -270,7 +269,6 @@ uploadFromUrlMayTakeTime: "Yuklash tugallanishi uchun biroz vaqt ketishi mumkin.
|
|||
explore: "Ko'rib chiqish"
|
||||
messageRead: "O‘qildi"
|
||||
noMoreHistory: "Buning ortida hech qanday hikoya yo'q"
|
||||
startMessaging: "Yangi suhbatni boshlash"
|
||||
nUsersRead: "{n} tomonidan o'qildi"
|
||||
agreeTo: "Men {0} ga roziman"
|
||||
agree: "Rozi bo'lish"
|
||||
|
@ -445,8 +443,6 @@ retype: "Qayta kiriting"
|
|||
noteOf: "{user} tomonidan joylandi\n"
|
||||
quoteAttached: "Iqtibos"
|
||||
quoteQuestion: "Iqtibos sifatida qo'shilsinmi?"
|
||||
noMessagesYet: "Bu yerda xabarlar yo'q"
|
||||
newMessageExists: "Yangi xabarlar bor"
|
||||
onlyOneFileCanBeAttached: "Faqat bitta faylni biriktirish mumkin"
|
||||
signinRequired: "Davom etishdan oldin ro'yhatdan o'tishingiz yoki tizimga kirishingiz kerak"
|
||||
invitations: "Taklif qilish"
|
||||
|
@ -841,6 +837,13 @@ icon: "Avatar"
|
|||
replies: "Javob berish"
|
||||
renotes: "Qayta qayd etish"
|
||||
flip: "Teskari"
|
||||
information: "Haqida"
|
||||
_chat:
|
||||
invitations: "Taklif qilish"
|
||||
noHistory: "Tarix yo'q"
|
||||
members: "A'zolar"
|
||||
home: "Bosh sahifa"
|
||||
send: "Yuborish"
|
||||
_delivery:
|
||||
stop: "To'xtatilgan"
|
||||
_type:
|
||||
|
@ -904,8 +907,6 @@ _theme:
|
|||
mention: "Murojat"
|
||||
renote: "Qayta qayd etish"
|
||||
divider: "Ajratrmoq"
|
||||
accentDarken: "Urg'u (Qoraytirilgan)"
|
||||
accentLighten: "Urg'u (Yoritilgan)"
|
||||
fgHighlighted: "Belgilangan matn"
|
||||
_sfx:
|
||||
note: "Qaydlar"
|
||||
|
|
|
@ -264,7 +264,6 @@ deleteAreYouSure: "Bạn có chắc muốn xóa \"{x}\"?"
|
|||
resetAreYouSure: "Bạn có chắc muốn đặt lại?"
|
||||
areYouSure: "Bạn chắc chứ?"
|
||||
saved: "Đã lưu"
|
||||
messaging: "Trò chuyện"
|
||||
upload: "Tải lên"
|
||||
keepOriginalUploading: "Giữ hình ảnh gốc"
|
||||
keepOriginalUploadingDescription: "Giữ nguyên như hình ảnh được tải lên ban đầu. Nếu tắt, một phiên bản để hiển thị trên web sẽ được tạo khi tải lên."
|
||||
|
@ -277,7 +276,6 @@ uploadFromUrlMayTakeTime: "Sẽ mất một khoảng thời gian để tải lê
|
|||
explore: "Khám phá"
|
||||
messageRead: "Đã đọc"
|
||||
noMoreHistory: "Không còn gì để đọc"
|
||||
startMessaging: "Bắt đầu trò chuyện"
|
||||
nUsersRead: "đọc bởi {n}"
|
||||
agreeTo: "Tôi đồng ý {0}"
|
||||
agree: "Đồng ý"
|
||||
|
@ -463,8 +461,6 @@ noteOf: "Tút của {user}"
|
|||
quoteAttached: "Trích dẫn"
|
||||
quoteQuestion: "Trích dẫn lại?"
|
||||
attachAsFileQuestion: "Văn bản ở trong bộ nhớ tạm rất dài. Bạn có muốn đăng nó dưới dạng một tệp văn bản không?"
|
||||
noMessagesYet: "Chưa có tin nhắn"
|
||||
newMessageExists: "Bạn có tin nhắn mới"
|
||||
onlyOneFileCanBeAttached: "Bạn chỉ có thể đính kèm một tập tin"
|
||||
signinRequired: "Vui lòng đăng nhập"
|
||||
invitations: "Mời"
|
||||
|
@ -1119,6 +1115,14 @@ pullDownToRefresh: "Kéo xuống để làm mới"
|
|||
cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải có chú thích."
|
||||
lastNDays: "{n} ngày trước"
|
||||
surrender: "Từ chối"
|
||||
postForm: "Mẫu đăng"
|
||||
information: "Giới thiệu"
|
||||
_chat:
|
||||
invitations: "Mời"
|
||||
noHistory: "Không có dữ liệu"
|
||||
members: "Thành viên"
|
||||
home: "Trang chính"
|
||||
send: "Gửi"
|
||||
_delivery:
|
||||
stop: "Đã vô hiệu hóa"
|
||||
_type:
|
||||
|
@ -1526,7 +1530,6 @@ _theme:
|
|||
header: "Ảnh bìa"
|
||||
navBg: "Nền thanh bên"
|
||||
navFg: "Chữ thanh bên"
|
||||
navHoverFg: "Chữ thanh bên (Khi chạm)"
|
||||
navActive: "Chữ thanh bên (Khi chọn)"
|
||||
navIndicator: "Chỉ báo thanh bên"
|
||||
link: "Đường dẫn"
|
||||
|
@ -1549,11 +1552,8 @@ _theme:
|
|||
buttonHoverBg: "Nền nút (Chạm)"
|
||||
inputBorder: "Đường viền khung soạn thảo"
|
||||
driveFolderBg: "Nền thư mục Ổ đĩa"
|
||||
wallpaperOverlay: "Lớp phủ hình nền"
|
||||
badge: "Huy hiệu"
|
||||
messageBg: "Nền chat"
|
||||
accentDarken: "Màu phụ (Tối)"
|
||||
accentLighten: "Màu phụ (Sáng)"
|
||||
fgHighlighted: "Chữ nổi bật"
|
||||
_sfx:
|
||||
note: "Tút"
|
||||
|
@ -1628,6 +1628,7 @@ _permissions:
|
|||
"write:gallery": "Sửa kho ảnh của tôi"
|
||||
"read:gallery-likes": "Xem danh sách các tút đã thích trong thư viện của tôi"
|
||||
"write:gallery-likes": "Sửa danh sách các tút đã thích trong thư viện của tôi"
|
||||
"write:chat": "Soạn hoặc xóa tin nhắn"
|
||||
_auth:
|
||||
shareAccessTitle: "Cho phép truy cập app"
|
||||
shareAccess: "Bạn có muốn cho phép \"{name}\" truy cập vào tài khoản này không?"
|
||||
|
|
|
@ -289,7 +289,6 @@ deleteAreYouSure: "要删掉「{x}」吗?"
|
|||
resetAreYouSure: "恢复默认设置?"
|
||||
areYouSure: "你确定吗?"
|
||||
saved: "已保存"
|
||||
messaging: "聊天"
|
||||
upload: "本地上传"
|
||||
keepOriginalUploading: "保留原图"
|
||||
keepOriginalUploadingDescription: "上传图片时保留原始图片。关闭时,浏览器会在上传时生成一张用于web发布的图片。"
|
||||
|
@ -302,7 +301,7 @@ uploadFromUrlMayTakeTime: "上传可能需要一些时间完成。"
|
|||
explore: "发现"
|
||||
messageRead: "已读"
|
||||
noMoreHistory: "没有更多的历史记录"
|
||||
startMessaging: "添加聊天"
|
||||
startChat: "开始聊天"
|
||||
nUsersRead: "{n} 人已读"
|
||||
agreeTo: "勾选则表示已阅读并同意 {0}"
|
||||
agree: "同意"
|
||||
|
@ -425,6 +424,7 @@ antennaExcludeBots: "排除机器人账户"
|
|||
antennaKeywordsDescription: "AND 条件用空格分隔,OR 条件用换行符分隔。"
|
||||
notifyAntenna: "开启通知"
|
||||
withFileAntenna: "仅带有附件的帖子"
|
||||
excludeNotesInSensitiveChannel: "排除敏感频道内的帖子"
|
||||
enableServiceworker: "启用 ServiceWorker"
|
||||
antennaUsersDescription: "指定用户名,一行一个"
|
||||
caseSensitive: "区分大小写"
|
||||
|
@ -491,8 +491,6 @@ noteOf: "{user} 的帖子"
|
|||
quoteAttached: "已引用"
|
||||
quoteQuestion: "是否引用此链接内容?"
|
||||
attachAsFileQuestion: "剪贴板内的文字过长。要转换为文本文件并添加吗?"
|
||||
noMessagesYet: "现在没有新的聊天"
|
||||
newMessageExists: "新信息"
|
||||
onlyOneFileCanBeAttached: "只能添加一个附件"
|
||||
signinRequired: "请先登录"
|
||||
signinOrContinueOnRemote: "若要继续,需要转到您所使用的实例,或者在此服务器上注册或登录。"
|
||||
|
@ -698,6 +696,7 @@ userSaysSomethingAbout: "{name} 说了关于「{word}」的什么"
|
|||
makeActive: "启用"
|
||||
display: "显示"
|
||||
copy: "复制"
|
||||
copiedToClipboard: "已复制到剪贴板"
|
||||
metrics: "指标"
|
||||
overview: "概览"
|
||||
logs: "日志"
|
||||
|
@ -746,7 +745,7 @@ confirmToUnclipAlreadyClippedNote: "本帖已包含在便签 \"{name}\" 里。
|
|||
public: "公开"
|
||||
private: "私密"
|
||||
i18nInfo: "Misskey 已经被志愿者们翻译成了各种语言。如果你也有兴趣,可以通过 {link} 帮助翻译。"
|
||||
manageAccessTokens: "管理 Access Tokens"
|
||||
manageAccessTokens: "管理访问令牌"
|
||||
accountInfo: "账户信息"
|
||||
notesCount: "帖子数量"
|
||||
repliesCount: "回复数量"
|
||||
|
@ -1315,6 +1314,127 @@ markAsSensitiveConfirm: "要将此媒体标记为敏感吗?"
|
|||
unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?"
|
||||
preferences: "设置"
|
||||
accessibility: "辅助功能"
|
||||
preferencesProfile: "设置的配置"
|
||||
copyPreferenceId: "复制设置 ID"
|
||||
resetToDefaultValue: "重置为默认值"
|
||||
overrideByAccount: "用账户覆盖"
|
||||
untitled: "未命名"
|
||||
noName: "没有名字"
|
||||
skip: "跳过"
|
||||
restore: "恢复"
|
||||
syncBetweenDevices: "设备间同步"
|
||||
preferenceSyncConflictTitle: "服务器上已存在设定值"
|
||||
preferenceSyncConflictText: "服务器上已有此设置的设定值。要覆盖哪个设定值?"
|
||||
preferenceSyncConflictChoiceServer: "服务器上的设定值"
|
||||
preferenceSyncConflictChoiceDevice: "设备上的设定值"
|
||||
preferenceSyncConflictChoiceCancel: "取消同步"
|
||||
paste: "粘贴"
|
||||
emojiPalette: "表情符号调色板"
|
||||
postForm: "投稿窗口"
|
||||
textCount: "字数"
|
||||
information: "关于"
|
||||
chat: "聊天"
|
||||
migrateOldSettings: "迁移旧设置信息"
|
||||
migrateOldSettings_description: "通常设置信息将自动迁移。但如果由于某种原因迁移不成功,则可以手动触发迁移过程。当前的配置信息将被覆盖。"
|
||||
compress: "压缩"
|
||||
right: "右"
|
||||
bottom: "下"
|
||||
top: "上"
|
||||
embed: "嵌入"
|
||||
settingsMigrating: "正在迁移设置,请稍候。(之后也可以在设置 → 其它 → 迁移旧设置来手动迁移)"
|
||||
readonly: "只读"
|
||||
goToDeck: "返回至 Deck"
|
||||
_chat:
|
||||
noMessagesYet: "还没有消息"
|
||||
newMessage: "新消息"
|
||||
individualChat: "私聊"
|
||||
individualChat_description: "可以与特定用户进行一对一聊天。"
|
||||
roomChat: "群聊"
|
||||
roomChat_description: "可以进行多人聊天。\n就算用户未允许私聊,只要接受了邀请,仍可以聊天。"
|
||||
createRoom: "创建房间"
|
||||
inviteUserToChat: "邀请用户来开始聊天"
|
||||
yourRooms: "已创建的房间"
|
||||
joiningRooms: "已加入的房间"
|
||||
invitations: "邀请"
|
||||
noInvitations: "没有邀请"
|
||||
history: "历史"
|
||||
noHistory: "没有历史记录"
|
||||
noRooms: "没有房间"
|
||||
inviteUser: "邀请用户"
|
||||
sentInvitations: "已发送的邀请"
|
||||
join: "加入"
|
||||
ignore: "忽略"
|
||||
leave: "退出房间"
|
||||
members: "成员"
|
||||
searchMessages: "搜索消息"
|
||||
home: "首页"
|
||||
send: "发送"
|
||||
newline: "换行"
|
||||
muteThisRoom: "静音此房间"
|
||||
deleteRoom: "删除房间"
|
||||
chatNotAvailableForThisAccountOrServer: "此服务器或者账户还未开启聊天功能。"
|
||||
chatIsReadOnlyForThisAccountOrServer: "此服务器或者账户内的聊天为只读。无法发布新信息或创建及加入群聊。"
|
||||
chatNotAvailableInOtherAccount: "对方账户目前处于无法使用聊天的状态。"
|
||||
cannotChatWithTheUser: "无法与此用户聊天"
|
||||
cannotChatWithTheUser_description: "可能现在无法使用聊天,或者对方未开启聊天。"
|
||||
chatWithThisUser: "聊天"
|
||||
thisUserAllowsChatOnlyFromFollowers: "此用户仅接受关注者发起的聊天。"
|
||||
thisUserAllowsChatOnlyFromFollowing: "此用户仅接受关注的人发起的聊天。"
|
||||
thisUserAllowsChatOnlyFromMutualFollowing: "此用户仅接受互相关注的人发起的聊天。"
|
||||
thisUserNotAllowedChatAnyone: "此用户不接受任何人发起的聊天。"
|
||||
chatAllowedUsers: "谁可以发起聊天"
|
||||
chatAllowedUsers_note: "主动发起聊天时,对方将不受此设置限制。"
|
||||
_chatAllowedUsers:
|
||||
everyone: "任何人"
|
||||
followers: "仅关注者"
|
||||
following: "仅关注的人"
|
||||
mutual: "仅相互关注"
|
||||
none: "没有人"
|
||||
_emojiPalette:
|
||||
palettes: "调色板"
|
||||
enableSyncBetweenDevicesForPalettes: "启用调色板的设备间同步"
|
||||
paletteForMain: "主调色板"
|
||||
paletteForReaction: "回应用调色板"
|
||||
_settings:
|
||||
driveBanner: "可在此管理和设置网盘、确认使用量及配置上传文件的设置。"
|
||||
pluginBanner: "使用插件可以扩展客户端的功能。可以在此安装、单独管理插件。"
|
||||
notificationsBanner: "可在此设置从服务器接收的通知的种类和范围,以及推送通知的设置。"
|
||||
api: "API"
|
||||
webhook: "Webhook"
|
||||
serviceConnection: "连接服务"
|
||||
serviceConnectionBanner: "可在此管理用于连接外部应用或服务的访问令牌及 Webhook。"
|
||||
accountData: "账户数据"
|
||||
accountDataBanner: "可在此导入或导出帐户数据的存档。"
|
||||
muteAndBlockBanner: "可在此设置隐藏内容,或限制指定用户能进行的操作。"
|
||||
accessibilityBanner: "可在此设置客户端的显示及动态效果等辅助设置。"
|
||||
privacyBanner: "可在此设置如内容可见性、可发现性、批准关注请求等账户隐私设置。"
|
||||
securityBanner: "可在此设置如密码、登入方式、验证器、Passkey 等账户安全性设置。"
|
||||
preferencesBanner: "可在此设置客户端的整体运作行为。"
|
||||
appearanceBanner: "可在此设置客户端的外观及显示方式。"
|
||||
soundsBanner: "可在此设置客户端播放的声音。"
|
||||
timelineAndNote: "时间线和帖子"
|
||||
makeEveryTextElementsSelectable: "使所有的文字均可选择"
|
||||
makeEveryTextElementsSelectable_description: "若开启,在某些情况下可能降低用户体验。"
|
||||
useStickyIcons: "使图标跟随滚动"
|
||||
showNavbarSubButtons: "在导航栏中显示副按钮"
|
||||
ifOn: "启用时"
|
||||
ifOff: "关闭时"
|
||||
_chat:
|
||||
showSenderName: "显示发送者的名字"
|
||||
sendOnEnter: "回车键发送"
|
||||
_preferencesProfile:
|
||||
profileName: "配置名"
|
||||
profileNameDescription: "请指定用于识别此设备的名称"
|
||||
profileNameDescription2: "如「PC」、「手机」等"
|
||||
_preferencesBackup:
|
||||
autoBackup: "自动备份"
|
||||
restoreFromBackup: "从备份恢复"
|
||||
noBackupsFoundTitle: "没有找到备份"
|
||||
noBackupsFoundDescription: "没有找到自动备份。若有手动保存备份文件,可将其导入来恢复。"
|
||||
selectBackupToRestore: "请选择要恢复的备份"
|
||||
youNeedToNameYourProfileToEnableAutoBackup: "需指定配置名以开启自动备份。"
|
||||
autoPreferencesBackupIsNotEnabledForThisDevice: "此设备未开启自动备份"
|
||||
backupFound: "已找到备份"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "需要登录才能显示内容"
|
||||
requireSigninToViewContentsDescription1: "您发布的所有帖子将变成需要登入后才会显示。有望防止爬虫收集各种信息。"
|
||||
|
@ -1325,6 +1445,7 @@ _accountSettings:
|
|||
makeNotesHiddenBefore: "将过去的帖子设为私密"
|
||||
makeNotesHiddenBeforeDescription: "开启此设定时,超过设定的时间或日期后,帖子将变为仅自己可见。关闭后帖子的公开状态将恢复成原本的设定。"
|
||||
mayNotEffectForFederatedNotes: "与远程服务器联合的帖子在远端可能会没有效果。"
|
||||
mayNotEffectSomeSituations: "此限制功能非常简单,在与远程服务器联合等情形时可能不适用。"
|
||||
notesHavePassedSpecifiedPeriod: "超过指定时间的帖子"
|
||||
notesOlderThanSpecifiedDateAndTime: "指定日期前的帖子"
|
||||
_abuseUserReport:
|
||||
|
@ -1770,6 +1891,8 @@ _role:
|
|||
descriptionOfIsExplorable: "打开后将公开角色时间线。如果角色不是公开的,就无法公开时间线。"
|
||||
displayOrder: "显示顺序"
|
||||
descriptionOfDisplayOrder: "数字越大,显示位置越靠前。"
|
||||
preserveAssignmentOnMoveAccount: "将分配状态继承到目标账户"
|
||||
preserveAssignmentOnMoveAccount_description: "启用后,当迁移具有该角色的账户时,目标账户也会继承该角色。"
|
||||
canEditMembersByModerator: "允许监察员编辑成员"
|
||||
descriptionOfCanEditMembersByModerator: "如果选中,监察员和管理员都能够为用户分配/取消分配角色。如果未选中,则只有管理员可以执行此操作。"
|
||||
priority: "优先级"
|
||||
|
@ -1810,6 +1933,7 @@ _role:
|
|||
canImportFollowing: "允许导入关注列表"
|
||||
canImportMuting: "允许导入隐藏列表"
|
||||
canImportUserLists: "允许导入用户列表"
|
||||
chatAvailability: "允许聊天"
|
||||
_condition:
|
||||
roleAssignedTo: "已分配给手动角色"
|
||||
isLocal: "是本地用户"
|
||||
|
@ -1973,6 +2097,7 @@ _theme:
|
|||
installed: "{name} 已安装"
|
||||
installedThemes: "已安装的主题"
|
||||
builtinThemes: "标准主题"
|
||||
instanceTheme: "服务器主题"
|
||||
alreadyInstalled: "此主题已经安装"
|
||||
invalid: "主题格式错误"
|
||||
make: "制作主题"
|
||||
|
@ -2005,7 +2130,6 @@ _theme:
|
|||
header: "顶栏"
|
||||
navBg: "侧边栏背景"
|
||||
navFg: "侧栏文本"
|
||||
navHoverFg: "侧栏文本(悬停)"
|
||||
navActive: "侧栏文本(活动)"
|
||||
navIndicator: "侧栏标记"
|
||||
link: "链接"
|
||||
|
@ -2028,17 +2152,15 @@ _theme:
|
|||
buttonHoverBg: "按钮背景(悬停)"
|
||||
inputBorder: "输入框边框"
|
||||
driveFolderBg: "网盘的文件夹背景"
|
||||
wallpaperOverlay: "壁纸叠加层"
|
||||
badge: "徽章"
|
||||
messageBg: "聊天背景"
|
||||
accentDarken: "强调色(深)"
|
||||
accentLighten: "强调色(浅)"
|
||||
fgHighlighted: "高亮显示文本"
|
||||
_sfx:
|
||||
note: "帖子"
|
||||
noteMy: "我的帖子"
|
||||
notification: "通知"
|
||||
reaction: "选择回应时"
|
||||
chatMessage: "聊天信息"
|
||||
_soundSettings:
|
||||
driveFile: "使用网盘内的音频"
|
||||
driveFileWarn: "选择网盘上的文件"
|
||||
|
@ -2185,6 +2307,8 @@ _permissions:
|
|||
"read:clip-favorite": "查看便签的点赞"
|
||||
"read:federation": "查看联合相关信息"
|
||||
"write:report-abuse": "举报用户"
|
||||
"write:chat": "撰写或删除消息"
|
||||
"read:chat": "查看聊天"
|
||||
_auth:
|
||||
shareAccessTitle: "应用程序授权许可"
|
||||
shareAccess: "您要授权允许 “{name}” 访问您的帐户吗?"
|
||||
|
@ -2433,6 +2557,7 @@ _notification:
|
|||
newNote: "新的帖子"
|
||||
unreadAntennaNote: "天线 {name}"
|
||||
roleAssigned: "授予的角色"
|
||||
chatRoomInvitationReceived: "受邀加入聊天室"
|
||||
emptyPushNotificationMessage: "推送通知已更新"
|
||||
achievementEarned: "获得成就"
|
||||
testNotification: "测试通知"
|
||||
|
@ -2461,9 +2586,11 @@ _notification:
|
|||
receiveFollowRequest: "收到关注请求"
|
||||
followRequestAccepted: "关注请求已通过"
|
||||
roleAssigned: "授予的角色"
|
||||
chatRoomInvitationReceived: "受邀加入聊天室"
|
||||
achievementEarned: "取得的成就"
|
||||
exportCompleted: "已完成导出"
|
||||
login: "登录"
|
||||
createToken: "创建访问令牌"
|
||||
test: "测试通知"
|
||||
app: "关联应用的通知"
|
||||
_actions:
|
||||
|
@ -2473,6 +2600,9 @@ _notification:
|
|||
_deck:
|
||||
alwaysShowMainColumn: "总是显示主列"
|
||||
columnAlign: "列对齐"
|
||||
columnGap: "列间距"
|
||||
deckMenuPosition: "Deck 菜单位置"
|
||||
navbarPosition: "导航栏位置"
|
||||
addColumn: "添加列"
|
||||
newNoteNotificationSettings: "新帖子通知设定"
|
||||
configureColumn: "列设置"
|
||||
|
@ -2486,11 +2616,12 @@ _deck:
|
|||
newProfile: "新建配置文件"
|
||||
deleteProfile: "删除配置文件"
|
||||
introduction: "将各列进行组合以创建您自己的界面!"
|
||||
introduction2: "您可以随时通过屏幕右侧的 + 来添加列"
|
||||
introduction2: "可以随时通过屏幕右侧的 + 来添加列"
|
||||
widgetsIntroduction: "从列菜单中,选择“小工具编辑”来添加小工具"
|
||||
useSimpleUiForNonRootPages: "用简易UI表示非根页面"
|
||||
usedAsMinWidthWhenFlexible: "「自适应宽度」被启用的时候,这就是最小的宽度"
|
||||
flexible: "自适应宽度"
|
||||
enableSyncBetweenDevicesForProfiles: "启用个人资料信息跨设备同步"
|
||||
_columns:
|
||||
main: "主列"
|
||||
widgets: "小工具"
|
||||
|
@ -2598,7 +2729,8 @@ _moderationLogTypes:
|
|||
deletePage: "删除了页面"
|
||||
deleteFlash: "删除了 Play"
|
||||
deleteGalleryPost: "删除了图库稿件"
|
||||
updateProxyAccountDescription: "更新代理账户的说明"
|
||||
deleteChatRoom: "删除聊天室"
|
||||
updateProxyAccountDescription: "更新代理账户的简介"
|
||||
_fileViewer:
|
||||
title: "文件信息"
|
||||
type: "文件类型"
|
||||
|
@ -2612,10 +2744,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "请在安装前确保来源可靠"
|
||||
_plugin:
|
||||
title: "要安装此插件吗?"
|
||||
metaTitle: "插件信息"
|
||||
_theme:
|
||||
title: "要安装此主题吗?"
|
||||
metaTitle: "主题信息"
|
||||
_meta:
|
||||
base: "基本配色方案"
|
||||
_vendorInfo:
|
||||
|
|
|
@ -103,7 +103,7 @@ serverIsDead: "伺服器沒有回應。請稍等片刻再試。"
|
|||
youShouldUpgradeClient: "請重新載入以使用新版客戶端顯示此頁面。"
|
||||
enterListName: "輸入清單名稱"
|
||||
privacy: "隱私"
|
||||
makeFollowManuallyApprove: "手動審核追隨請求"
|
||||
makeFollowManuallyApprove: "追隨需要核准"
|
||||
defaultNoteVisibility: "預設可見性"
|
||||
follow: "追隨"
|
||||
followRequest: "追隨請求"
|
||||
|
@ -289,7 +289,6 @@ deleteAreYouSure: "確定要刪掉「{x}」嗎?"
|
|||
resetAreYouSure: "確定要重設嗎?"
|
||||
areYouSure: "是否確定?"
|
||||
saved: "已儲存"
|
||||
messaging: "聊天"
|
||||
upload: "上傳"
|
||||
keepOriginalUploading: "保留原圖"
|
||||
keepOriginalUploadingDescription: "上傳圖片時保留原始圖片。關閉時,瀏覽器會在上傳時生成適用於網路傳送的版本。"
|
||||
|
@ -302,7 +301,7 @@ uploadFromUrlMayTakeTime: "還需要一些時間才能完成上傳。"
|
|||
explore: "探索"
|
||||
messageRead: "已讀"
|
||||
noMoreHistory: "沒有更多歷史紀錄"
|
||||
startMessaging: "開始聊天"
|
||||
startChat: "開始聊天"
|
||||
nUsersRead: "{n} 人已讀"
|
||||
agreeTo: "我同意{0}"
|
||||
agree: "同意"
|
||||
|
@ -459,13 +458,13 @@ moderationNoteDescription: "您可以編寫僅在審查員之間共用的註解
|
|||
addModerationNote: "新增管理筆記"
|
||||
moderationLogs: "管理日誌"
|
||||
nUsersMentioned: "被 {n} 個人提及"
|
||||
securityKeyAndPasskey: "安全金鑰、Passkey"
|
||||
securityKeyAndPasskey: "安全金鑰、通行金鑰"
|
||||
securityKey: "安全金鑰"
|
||||
lastUsed: "上次使用"
|
||||
lastUsedAt: "上次使用:{t}"
|
||||
unregister: "註銷"
|
||||
passwordLessLogin: "無密碼登入"
|
||||
passwordLessLoginDescription: "不使用密碼,以安全金鑰或 Passkey 登入"
|
||||
passwordLessLoginDescription: "不使用密碼,以安全金鑰或通行金鑰登入"
|
||||
resetPassword: "重設密碼"
|
||||
newPasswordIs: "新密碼為「{password}」"
|
||||
reduceUiAnimation: "減少介面的動態視覺"
|
||||
|
@ -491,8 +490,6 @@ noteOf: "{user}的貼文"
|
|||
quoteAttached: "引用"
|
||||
quoteQuestion: "是否要引用?"
|
||||
attachAsFileQuestion: "剪貼簿的文字較長。請問是否要將其以文字檔的方式附加呢?"
|
||||
noMessagesYet: "沒有訊息"
|
||||
newMessageExists: "有新的訊息"
|
||||
onlyOneFileCanBeAttached: "只能加入一個附件"
|
||||
signinRequired: "請先登入"
|
||||
signinOrContinueOnRemote: "若要繼續,需前往您所在的伺服器,或者註冊並登入此伺服器"
|
||||
|
@ -698,6 +695,7 @@ userSaysSomethingAbout: "{name} 說了一些關於「{word}」的話"
|
|||
makeActive: "啟用"
|
||||
display: "檢視"
|
||||
copy: "複製"
|
||||
copiedToClipboard: "已複製到剪貼簿"
|
||||
metrics: "指標"
|
||||
overview: "概覽"
|
||||
logs: "日誌"
|
||||
|
@ -765,7 +763,7 @@ driveFilesCount: "雲端硬碟檔案數量"
|
|||
driveUsage: "雲端硬碟使用量"
|
||||
noCrawle: "拒絕搜尋引擎索引"
|
||||
noCrawleDescription: "要求網路搜尋引擎不要索引你的個人資料頁、貼文及頁面等。"
|
||||
lockedAccountInfo: "即使你通過了追隨者請求,除非你將貼文的可見性設定為 「追隨者」,否則任何人都能看見你的貼文。"
|
||||
lockedAccountInfo: "即使追隨需要核准,除非你將貼文的可見性設定為 「追隨者」,否則任何人都能看見你的貼文。"
|
||||
alwaysMarkSensitive: "預設標記檔案為敏感內容"
|
||||
loadRawImages: "以原始圖檔顯示附件圖檔的縮圖"
|
||||
disableShowingAnimatedImages: "不播放動態圖檔"
|
||||
|
@ -1188,7 +1186,7 @@ forYou: "給您"
|
|||
currentAnnouncements: "最新公告"
|
||||
pastAnnouncements: "歷史公告"
|
||||
youHaveUnreadAnnouncements: "有未讀的公告。"
|
||||
useSecurityKey: "請按照瀏覽器或裝置上的說明來使用安全金鑰或 Passkey。"
|
||||
useSecurityKey: "請按照瀏覽器或裝置上的說明來使用安全金鑰或通行金鑰。"
|
||||
replies: "回覆"
|
||||
renotes: "轉發"
|
||||
loadReplies: "閱覽回覆"
|
||||
|
@ -1205,7 +1203,7 @@ showRenotes: "顯示其他人的轉發貼文"
|
|||
edited: "已編輯"
|
||||
notificationRecieveConfig: "接受通知的設定"
|
||||
mutualFollow: "互相追隨"
|
||||
followingOrFollower: "追隨中或者追隨者"
|
||||
followingOrFollower: "追隨中或追隨者"
|
||||
fileAttachedOnly: "只顯示包含附件的貼文"
|
||||
showRepliesToOthersInTimeline: "在時間軸上顯示給其他人的回覆"
|
||||
hideRepliesToOthersInTimeline: "在時間軸上隱藏給其他人的回覆"
|
||||
|
@ -1291,10 +1289,10 @@ performance: "性能"
|
|||
modified: "已變更"
|
||||
discard: "取消"
|
||||
thereAreNChanges: "有 {n} 處的變更"
|
||||
signinWithPasskey: "使用密碼金鑰登入"
|
||||
unknownWebAuthnKey: "未註冊的金鑰。"
|
||||
passkeyVerificationFailed: "驗證金鑰失敗。"
|
||||
passkeyVerificationSucceededButPasswordlessLoginDisabled: "雖然驗證金鑰成功,但是無密碼登入的方式是停用的。"
|
||||
signinWithPasskey: "使用通行金鑰登入"
|
||||
unknownWebAuthnKey: "未註冊的通行金鑰。"
|
||||
passkeyVerificationFailed: "驗證通行金鑰失敗。"
|
||||
passkeyVerificationSucceededButPasswordlessLoginDisabled: "雖然驗證通行金鑰成功,但是無密碼登入的方式是停用的。"
|
||||
messageToFollower: "給追隨者的訊息"
|
||||
target: "目標 "
|
||||
testCaptchaWarning: "此功能用於 CAPTCHA 的測試。<strong>請勿在正式環境中使用。</strong>"
|
||||
|
@ -1315,16 +1313,138 @@ markAsSensitiveConfirm: "要將這個媒體設定為敏感嗎?"
|
|||
unmarkAsSensitiveConfirm: "要解除這個媒體的敏感設定嗎?"
|
||||
preferences: "環境設定"
|
||||
accessibility: "輔助工具"
|
||||
preferencesProfile: "設定檔案"
|
||||
copyPreferenceId: "複製設定 ID"
|
||||
resetToDefaultValue: "還原成預設值"
|
||||
overrideByAccount: "覆寫帳號"
|
||||
untitled: "無標題"
|
||||
noName: "沒有名稱"
|
||||
skip: "跳過"
|
||||
restore: "還原"
|
||||
syncBetweenDevices: "裝置之間的同步化"
|
||||
preferenceSyncConflictTitle: "伺服器上存在設定值"
|
||||
preferenceSyncConflictText: "已啟用同步的設定項目會將設定值儲存至伺服器,並已找到該設定項目在伺服器上儲存的設定值。請選擇要使用哪個設定值進行覆寫。"
|
||||
preferenceSyncConflictChoiceServer: "伺服器設定值"
|
||||
preferenceSyncConflictChoiceDevice: "裝置的設定值"
|
||||
preferenceSyncConflictChoiceCancel: "取消啟用同步"
|
||||
paste: "貼上"
|
||||
emojiPalette: "表情符號調色盤"
|
||||
postForm: "發文視窗"
|
||||
textCount: "字數"
|
||||
information: "關於"
|
||||
chat: "聊天"
|
||||
migrateOldSettings: "遷移舊設定資訊"
|
||||
migrateOldSettings_description: "通常情況下,這會自動進行,但若因某些原因未能順利遷移,您可以手動觸發遷移處理。請注意,當前的設定資訊將會被覆寫。"
|
||||
compress: "壓縮"
|
||||
right: "右"
|
||||
bottom: "下"
|
||||
top: "上"
|
||||
embed: "嵌入"
|
||||
settingsMigrating: "正在移轉設定。請稍候……(之後也可以到「設定 → 其他 → 舊設定資訊移轉」中手動進行移轉)"
|
||||
readonly: "唯讀"
|
||||
_chat:
|
||||
noMessagesYet: "尚無訊息"
|
||||
newMessage: "新訊息"
|
||||
individualChat: "ㄧ對一聊天室"
|
||||
individualChat_description: "可以與特定使用者進行一對一的聊天。"
|
||||
roomChat: "多人聊天室"
|
||||
roomChat_description: "可以進行多人聊天。\n此外,即使是未允許個人聊天的使用者,只要對方接受,也可以進行聊天。"
|
||||
createRoom: "建立聊天室"
|
||||
inviteUserToChat: "邀請使用者開始聊天"
|
||||
yourRooms: "已建立的聊天室"
|
||||
joiningRooms: "已加入的聊天室"
|
||||
invitations: "邀請"
|
||||
noInvitations: "沒有邀請"
|
||||
history: "歷史紀錄"
|
||||
noHistory: "沒有歷史紀錄"
|
||||
noRooms: "沒有可用的聊天室"
|
||||
inviteUser: "邀請使用者"
|
||||
sentInvitations: "已傳送的邀請"
|
||||
join: "加入"
|
||||
ignore: "忽視"
|
||||
leave: "退出聊天室"
|
||||
members: "成員"
|
||||
searchMessages: "搜尋聊天訊息"
|
||||
home: "首頁"
|
||||
send: "發送"
|
||||
newline: "換行"
|
||||
muteThisRoom: "此聊天室已靜音"
|
||||
deleteRoom: "刪除聊天室"
|
||||
chatNotAvailableForThisAccountOrServer: "這個伺服器或這個帳號的聊天功能尚未啟用。"
|
||||
chatIsReadOnlyForThisAccountOrServer: "在此伺服器或此帳戶上的聊天是唯讀的。您無法發布新訊息、建立或加入聊天室。"
|
||||
chatNotAvailableInOtherAccount: "對方的帳號無法使用聊天功能。"
|
||||
cannotChatWithTheUser: "無法與此使用者聊天"
|
||||
cannotChatWithTheUser_description: "聊天功能目前無法使用,或對方尚未開放聊天功能。"
|
||||
chatWithThisUser: "聊天"
|
||||
thisUserAllowsChatOnlyFromFollowers: "此使用者僅接受來自追隨者的聊天訊息。"
|
||||
thisUserAllowsChatOnlyFromFollowing: "此使用者僅接受自己追隨的使用者傳送聊天訊息。"
|
||||
thisUserAllowsChatOnlyFromMutualFollowing: "此使用者只接受互相追隨的使用者傳送聊天訊息。"
|
||||
thisUserNotAllowedChatAnyone: "此使用者不接受來自任何人的聊天訊息。"
|
||||
chatAllowedUsers: "允許聊天的對象"
|
||||
chatAllowedUsers_note: "無論此設定為何,您仍可與自己曾發送過聊天訊息的對象進行聊天。"
|
||||
_chatAllowedUsers:
|
||||
everyone: "任何人"
|
||||
followers: "追隨自己的使用者"
|
||||
following: "只有您追隨的使用者"
|
||||
mutual: "互相追隨"
|
||||
none: "無"
|
||||
_emojiPalette:
|
||||
palettes: "調色盤"
|
||||
enableSyncBetweenDevicesForPalettes: "啟用裝置與裝置之間的調色盤同步化"
|
||||
paletteForMain: "主要使用的調色盤"
|
||||
paletteForReaction: "反應用的調色盤"
|
||||
_settings:
|
||||
driveBanner: "您可以管理和設定雲端硬碟、確認使用量,以及調整上傳檔案時的設定。"
|
||||
pluginBanner: "可使用外掛擴充用戶端的功能。您可以安裝外掛,實施個別的設定與管理。"
|
||||
notificationsBanner: "您可以設定從伺服器接收通知的類型和範圍,以及推送通知。"
|
||||
api: "API"
|
||||
webhook: "Webhook"
|
||||
serviceConnection: "服務整合"
|
||||
serviceConnectionBanner: "您可以管理和設定存取權杖與 Webhooks,以便與外部應用程式和服務整合。"
|
||||
accountData: "帳戶資料"
|
||||
accountDataBanner: "您可以管理帳戶資料的匯出 / 匯入。"
|
||||
muteAndBlockBanner: "您可以設定和管理要隱藏的內容,並限制特定使用者的行動。"
|
||||
accessibilityBanner: "可針對客戶端的視覺和行為進行個人化設定,以達到更佳的使用效果。"
|
||||
privacyBanner: "您可以調整帳戶的隱私設定,例如內容的可見性、尋找內容的容易程度,以及追隨是否需要核准。"
|
||||
securityBanner: "您可以設定與帳戶安全性相關的設定,例如密碼、登入方式、驗證應用程式和通行金鑰。"
|
||||
preferencesBanner: "您可以根據喜好設定用戶端的整體行為。"
|
||||
appearanceBanner: "您可以根據喜好設定與用戶端外觀和顯示方式相關的設定。"
|
||||
soundsBanner: "您可以調整用戶端播放的聲音設定。"
|
||||
timelineAndNote: "時間軸及貼文"
|
||||
makeEveryTextElementsSelectable: "允許選取所有文字"
|
||||
makeEveryTextElementsSelectable_description: "啟用此功能後,可能會在某些情境下降低可用性。"
|
||||
useStickyIcons: "使大頭貼跟隨捲動"
|
||||
showNavbarSubButtons: "在導覽列顯示輔助按鈕"
|
||||
ifOn: "開啟時"
|
||||
ifOff: "關閉時"
|
||||
enableSyncThemesBetweenDevices: "在裝置之間同步已安裝的主題"
|
||||
_chat:
|
||||
showSenderName: "顯示發送者的名稱"
|
||||
sendOnEnter: "按下 Enter 發送訊息"
|
||||
_preferencesProfile:
|
||||
profileName: "設定檔案名稱"
|
||||
profileNameDescription: "設定一個名稱來識別此裝置。"
|
||||
profileNameDescription2: "例如:「主要個人電腦」、「智慧型手機」等"
|
||||
_preferencesBackup:
|
||||
autoBackup: "自動備份"
|
||||
restoreFromBackup: "從備份還原"
|
||||
noBackupsFoundTitle: "找不到備份檔"
|
||||
noBackupsFoundDescription: "沒有找到自動建立的備份,但如果您手動儲存了備份檔案,則可以匯入並還原。"
|
||||
selectBackupToRestore: "選擇要還原的備份"
|
||||
youNeedToNameYourProfileToEnableAutoBackup: "要啟用自動備份,必須設定檔案名稱。"
|
||||
autoPreferencesBackupIsNotEnabledForThisDevice: "此裝置未啟用自動備份設定。"
|
||||
backupFound: "找到設定的備份"
|
||||
_accountSettings:
|
||||
requireSigninToViewContents: "須登入以顯示內容"
|
||||
requireSigninToViewContentsDescription1: "必須登入才會顯示您建立的貼文等內容。可望有效防止資訊被爬蟲蒐集。"
|
||||
requireSigninToViewContentsDescription2: "來自不支援 URL 預覽 (OGP)、 網頁嵌入和引用貼文的伺服器,也將停止顯示。"
|
||||
requireSigninToViewContentsDescription2: "針對您貼文的 URL 預覽 (OGP) 與網頁嵌入功能將會無法使用。而不支援引用貼文的伺服器,也將停止顯示。"
|
||||
requireSigninToViewContentsDescription3: "這些限制可能不適用於被聯邦發送至遠端伺服器的內容。"
|
||||
makeNotesFollowersOnlyBefore: "讓過去的貼文僅對追隨者顯示"
|
||||
makeNotesFollowersOnlyBeforeDescription: "啟用此功能後,超過設定的日期和時間或超過設定時間的貼文將僅對追隨者顯示。 如果您再次停用它,貼文的公開狀態也會恢復原狀。"
|
||||
makeNotesHiddenBefore: "隱藏過去的貼文"
|
||||
makeNotesHiddenBeforeDescription: "啟用此功能後,超過設定的日期和時間或超過設定時間的貼文將僅對自己顯示(私密化)。 如果您再次停用它,貼文的公開狀態也會恢復原狀。"
|
||||
mayNotEffectForFederatedNotes: "聯邦發送至遠端伺服器的貼文可能會不受影響。"
|
||||
mayNotEffectSomeSituations: "這些限制已經簡化。它們可能不適用於某些情況,例如在遠端伺服器上檢視或管理時。"
|
||||
notesHavePassedSpecifiedPeriod: "早於指定時間的貼文"
|
||||
notesOlderThanSpecifiedDateAndTime: "指定時間和日期之前的貼文"
|
||||
_abuseUserReport:
|
||||
|
@ -1770,6 +1890,8 @@ _role:
|
|||
descriptionOfIsExplorable: "若開啟則公開角色時間軸。若角色不是公開的,則無法公開時間軸。"
|
||||
displayOrder: "顯示順序"
|
||||
descriptionOfDisplayOrder: "數字越大,顯示在UI上的越上面。"
|
||||
preserveAssignmentOnMoveAccount: "將指派狀態承接至轉移後的帳戶"
|
||||
preserveAssignmentOnMoveAccount_description: "開啟此選項後,當具備此角色的帳戶被移轉時,該角色也會承接至轉移後的帳戶。"
|
||||
canEditMembersByModerator: "允許編輯審查員的成員"
|
||||
descriptionOfCanEditMembersByModerator: "如果開啟,管理員與審查員都可以為使用者指派/解除指派該角色。如果關閉,則只有管理員可以執行。"
|
||||
priority: "優先級"
|
||||
|
@ -1810,6 +1932,7 @@ _role:
|
|||
canImportFollowing: "允許匯入追隨名單"
|
||||
canImportMuting: "允許匯入靜音名單"
|
||||
canImportUserLists: "允許匯入清單"
|
||||
chatAvailability: "允許聊天"
|
||||
_condition:
|
||||
roleAssignedTo: "手動指派角色完成"
|
||||
isLocal: "本地使用者"
|
||||
|
@ -1973,6 +2096,7 @@ _theme:
|
|||
installed: "{name}已安裝"
|
||||
installedThemes: "已經安裝的佈景主題"
|
||||
builtinThemes: "標準佈景主題"
|
||||
instanceTheme: "伺服器的主題"
|
||||
alreadyInstalled: "已安裝此佈景主題"
|
||||
invalid: "佈景主題格式錯誤"
|
||||
make: "製作佈景主題"
|
||||
|
@ -2005,7 +2129,6 @@ _theme:
|
|||
header: "標題"
|
||||
navBg: "側邊欄的背景 "
|
||||
navFg: "側邊欄的文字"
|
||||
navHoverFg: "側邊欄文字(懸浮) "
|
||||
navActive: "側邊欄文字(活動)"
|
||||
navIndicator: "側邊欄指示符"
|
||||
link: "連結"
|
||||
|
@ -2028,17 +2151,15 @@ _theme:
|
|||
buttonHoverBg: "按鈕背景 (漂浮)"
|
||||
inputBorder: "輸入框邊框"
|
||||
driveFolderBg: "雲端硬碟文件夾背景"
|
||||
wallpaperOverlay: "壁紙覆蓋層"
|
||||
badge: "徽章"
|
||||
messageBg: "私訊背景"
|
||||
accentDarken: "強調色(黑暗)"
|
||||
accentLighten: "強調色(明亮)"
|
||||
fgHighlighted: "突顯文字"
|
||||
_sfx:
|
||||
note: "貼文"
|
||||
noteMy: "我的貼文"
|
||||
notification: "通知"
|
||||
reaction: "選擇反應時"
|
||||
chatMessage: "聊天訊息"
|
||||
_soundSettings:
|
||||
driveFile: "使用雲端硬碟的音效檔案"
|
||||
driveFileWarn: "請選擇雲端硬碟中的檔案"
|
||||
|
@ -2082,11 +2203,11 @@ _2fa:
|
|||
setupCompleted: "設定完成"
|
||||
step4: "從現在開始,任何登入操作都將要求您提供權杖。"
|
||||
securityKeyNotSupported: "您的瀏覽器不支援安全金鑰。"
|
||||
registerTOTPBeforeKey: "如要註冊安全金鑰或 Passkey,請先設定驗證應用程式。"
|
||||
securityKeyInfo: "您可以設定使用支援 FIDO2 的硬體安全金鑰,以及裝置上的生物辨識、PIN 碼和密碼等來登入。"
|
||||
registerSecurityKey: "註冊安全金鑰或 Passkey"
|
||||
registerTOTPBeforeKey: "如要註冊安全金鑰或通行金鑰,請先設定驗證應用程式。"
|
||||
securityKeyInfo: "註冊 WebAuthn 衍生的金鑰,例如支援 FIDO2 的硬體安全金鑰、裝置生物識別、PIN 鎖和通行金鑰。"
|
||||
registerSecurityKey: "註冊安全金鑰或通行金鑰"
|
||||
securityKeyName: "輸入金鑰名稱"
|
||||
tapSecurityKey: "按照瀏覽器的說明註冊安全金鑰或 Passkey。"
|
||||
tapSecurityKey: "按照瀏覽器的說明註冊安全金鑰或通行金鑰。"
|
||||
removeKey: "刪除安全金鑰"
|
||||
removeKeyConfirm: "要刪除{name}嗎?"
|
||||
whyTOTPOnlyRenew: "如果註冊了安全金鑰,則無法解除驗證應用程式的設定。"
|
||||
|
@ -2185,6 +2306,8 @@ _permissions:
|
|||
"read:clip-favorite": "查看摘錄的讚"
|
||||
"read:federation": "查看站台聯邦的相關資訊"
|
||||
"write:report-abuse": "檢舉違規行為"
|
||||
"write:chat": "撰寫或刪除訊息"
|
||||
"read:chat": "查看聊天訊息"
|
||||
_auth:
|
||||
shareAccessTitle: "應用程式的存取權限"
|
||||
shareAccess: "要授權「“{name}”」存取您的帳戶嗎?"
|
||||
|
@ -2308,7 +2431,7 @@ _profile:
|
|||
avatarDecorationMax: "最多可以設置 {max} 個裝飾。"
|
||||
followedMessage: "被追隨時的訊息"
|
||||
followedMessageDescription: "可以設定被追隨時顯示給對方的訊息。"
|
||||
followedMessageDescriptionForLockedAccount: "如果追隨是需要審核的話,在允許追隨請求之後顯示。"
|
||||
followedMessageDescriptionForLockedAccount: "如果追隨需要核准的話,將在通過追隨請求之後顯示。"
|
||||
_exportOrImport:
|
||||
allNotes: "所有貼文"
|
||||
favoritedNotes: "「我的最愛」貼文"
|
||||
|
@ -2428,11 +2551,12 @@ _notification:
|
|||
youRenoted: "{name} 轉發了你的貼文"
|
||||
youWereFollowed: "您有新的追隨者"
|
||||
youReceivedFollowRequest: "您有新的追隨請求"
|
||||
yourFollowRequestAccepted: "您的追隨請求已通過"
|
||||
yourFollowRequestAccepted: "您的追隨請求已被核准"
|
||||
pollEnded: "問卷調查已產生結果"
|
||||
newNote: "新的貼文"
|
||||
unreadAntennaNote: "天線 {name}"
|
||||
roleAssigned: "已授予角色"
|
||||
chatRoomInvitationReceived: "您被邀請加入聊天室"
|
||||
emptyPushNotificationMessage: "推送通知已更新"
|
||||
achievementEarned: "獲得成就"
|
||||
testNotification: "通知測試"
|
||||
|
@ -2461,9 +2585,11 @@ _notification:
|
|||
receiveFollowRequest: "已收到追隨請求"
|
||||
followRequestAccepted: "追隨請求已接受"
|
||||
roleAssigned: "已授予角色"
|
||||
chatRoomInvitationReceived: "已被邀請加入聊天室"
|
||||
achievementEarned: "獲得成就"
|
||||
exportCompleted: "已完成匯出。"
|
||||
login: "登入"
|
||||
createToken: "建立存取權杖"
|
||||
test: "通知測試"
|
||||
app: "應用程式通知"
|
||||
_actions:
|
||||
|
@ -2473,6 +2599,9 @@ _notification:
|
|||
_deck:
|
||||
alwaysShowMainColumn: "總是顯示主欄"
|
||||
columnAlign: "對齊欄位"
|
||||
columnGap: "欄與欄之間的邊距"
|
||||
deckMenuPosition: "多欄模式的選單位置"
|
||||
navbarPosition: "導覽列位置"
|
||||
addColumn: "新增欄位"
|
||||
newNoteNotificationSettings: "新貼文通知的設定"
|
||||
configureColumn: "欄位的設定"
|
||||
|
@ -2491,6 +2620,7 @@ _deck:
|
|||
useSimpleUiForNonRootPages: "用簡易介面顯示非根頁面"
|
||||
usedAsMinWidthWhenFlexible: "如果啟用「自動調整寬度」,此為最小寬度"
|
||||
flexible: "自動調整寬度"
|
||||
enableSyncBetweenDevicesForProfiles: "啟用裝置與裝置之間的設定檔資料同步化"
|
||||
_columns:
|
||||
main: "主列"
|
||||
widgets: "小工具"
|
||||
|
@ -2598,6 +2728,7 @@ _moderationLogTypes:
|
|||
deletePage: "刪除頁面"
|
||||
deleteFlash: "刪除 Play"
|
||||
deleteGalleryPost: "刪除相簿的貼文"
|
||||
deleteChatRoom: "刪除聊天室"
|
||||
updateProxyAccountDescription: "更新代理帳戶的說明"
|
||||
_fileViewer:
|
||||
title: "檔案詳細資訊"
|
||||
|
@ -2612,10 +2743,8 @@ _externalResourceInstaller:
|
|||
checkVendorBeforeInstall: "安裝前請確認提供者是可信賴的。"
|
||||
_plugin:
|
||||
title: "要安裝此外掛嘛?"
|
||||
metaTitle: "外掛資訊"
|
||||
_theme:
|
||||
title: "要安裝此佈景主題嗎?"
|
||||
metaTitle: "佈景主題資訊"
|
||||
_meta:
|
||||
base: "基本配色方案"
|
||||
_vendorInfo:
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "misskey",
|
||||
"version": "2025.3.1",
|
||||
"version": "2025.4.0",
|
||||
"codename": "nasubi",
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
@ -65,12 +65,12 @@
|
|||
},
|
||||
"devDependencies": {
|
||||
"@misskey-dev/eslint-plugin": "2.1.0",
|
||||
"@types/node": "22.13.9",
|
||||
"@types/node": "22.13.10",
|
||||
"@typescript-eslint/eslint-plugin": "8.26.0",
|
||||
"@typescript-eslint/parser": "8.26.0",
|
||||
"cross-env": "7.0.3",
|
||||
"cypress": "14.1.0",
|
||||
"eslint": "9.21.0",
|
||||
"eslint": "9.22.0",
|
||||
"globals": "16.0.0",
|
||||
"ncp": "2.0.0",
|
||||
"pnpm": "10.6.1",
|
||||
|
@ -84,7 +84,8 @@
|
|||
"@aiscript-dev/aiscript-languageserver": "-"
|
||||
},
|
||||
"patchedDependencies": {
|
||||
"re2": "scripts/dependency-patches/re2.patch"
|
||||
"re2": "scripts/dependency-patches/re2.patch",
|
||||
"vite": "scripts/dependency-patches/vite.patch"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class AddAntennaHideNotesInSensitiveChannel1736230492103 {
|
||||
name = 'AddAntennaHideNotesInSensitiveChannel1736230492103'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "antenna" ADD "hideNotesInSensitiveChannel" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "hideNotesInSensitiveChannel"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,46 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class Chat1742203321812 {
|
||||
name = 'Chat1742203321812'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`CREATE TABLE "chat_room" ("id" character varying(32) NOT NULL, "name" character varying(256) NOT NULL, "ownerId" character varying(32) NOT NULL, CONSTRAINT "PK_8aa3a52cf74c96469f0ef9fbe3e" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_f0d8ad64243fa2ca2800da0dfd" ON "chat_room" ("ownerId") `);
|
||||
await queryRunner.query(`CREATE TABLE "chat_message" ("id" character varying(32) NOT NULL, "fromUserId" character varying(32) NOT NULL, "toUserId" character varying(32), "toRoomId" character varying(32), "text" character varying(4096), "uri" character varying(512), "reads" character varying(32) array NOT NULL DEFAULT '{}', "fileId" character varying(32), "reactions" character varying(1024) array NOT NULL DEFAULT '{}', CONSTRAINT "PK_3cc0d85193aade457d3077dd06b" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_79a26e7a4d9afa5e4fc05f134e" ON "chat_message" ("fromUserId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_25e097b51d7622c249452c6f75" ON "chat_message" ("toUserId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_f006b8a76efd1abf9f221c175c" ON "chat_message" ("toRoomId") `);
|
||||
await queryRunner.query(`CREATE TABLE "chat_room_membership" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "roomId" character varying(32) NOT NULL, CONSTRAINT "PK_2bd59c741e571b283c048beb69a" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_d99c5279460fb77ef58c596ce5" ON "chat_room_membership" ("userId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_c25143ebab714e930aeca1c0e8" ON "chat_room_membership" ("roomId") `);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room" ADD CONSTRAINT "FK_f0d8ad64243fa2ca2800da0dfd6" FOREIGN KEY ("ownerId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" ADD CONSTRAINT "FK_79a26e7a4d9afa5e4fc05f134ed" FOREIGN KEY ("fromUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" ADD CONSTRAINT "FK_25e097b51d7622c249452c6f757" FOREIGN KEY ("toUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" ADD CONSTRAINT "FK_f006b8a76efd1abf9f221c175ce" FOREIGN KEY ("toRoomId") REFERENCES "chat_room"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" ADD CONSTRAINT "FK_fd0f9a4879430239715ad4f8e2a" FOREIGN KEY ("fileId") REFERENCES "drive_file"("id") ON DELETE SET NULL ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_membership" ADD CONSTRAINT "FK_d99c5279460fb77ef58c596ce51" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_membership" ADD CONSTRAINT "FK_c25143ebab714e930aeca1c0e8d" FOREIGN KEY ("roomId") REFERENCES "chat_room"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_membership" DROP CONSTRAINT "FK_c25143ebab714e930aeca1c0e8d"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_membership" DROP CONSTRAINT "FK_d99c5279460fb77ef58c596ce51"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" DROP CONSTRAINT "FK_fd0f9a4879430239715ad4f8e2a"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" DROP CONSTRAINT "FK_f006b8a76efd1abf9f221c175ce"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" DROP CONSTRAINT "FK_25e097b51d7622c249452c6f757"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_message" DROP CONSTRAINT "FK_79a26e7a4d9afa5e4fc05f134ed"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room" DROP CONSTRAINT "FK_f0d8ad64243fa2ca2800da0dfd6"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_c25143ebab714e930aeca1c0e8"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_d99c5279460fb77ef58c596ce5"`);
|
||||
await queryRunner.query(`DROP TABLE "chat_room_membership"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_f006b8a76efd1abf9f221c175c"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_25e097b51d7622c249452c6f75"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_79a26e7a4d9afa5e4fc05f134e"`);
|
||||
await queryRunner.query(`DROP TABLE "chat_message"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_f0d8ad64243fa2ca2800da0dfd"`);
|
||||
await queryRunner.query(`DROP TABLE "chat_room"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,18 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class Chat21742608337548 {
|
||||
name = 'Chat21742608337548'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "user" ADD "chatScope" character varying(128) NOT NULL DEFAULT 'mutual'`);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_185b6b5afa707b5d36d1ce3144" ON "chat_room_membership" ("userId", "roomId") `);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_185b6b5afa707b5d36d1ce3144"`);
|
||||
await queryRunner.query(`ALTER TABLE "user" DROP COLUMN "chatScope"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class Chat31742617546147 {
|
||||
name = 'Chat31742617546147'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`CREATE TABLE "chat_approval" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "otherId" character varying(32) NOT NULL, CONSTRAINT "PK_fbbb95d60acf5c85388345b5f5d" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_530257863e1381a7f2f1d3282f" ON "chat_approval" ("userId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_b1d46037f23d170da5c05fdf75" ON "chat_approval" ("otherId") `);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_12c4768a2f706fc267f2078903" ON "chat_approval" ("userId", "otherId") `);
|
||||
await queryRunner.query(`ALTER TABLE "chat_approval" ADD CONSTRAINT "FK_530257863e1381a7f2f1d3282fe" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_approval" ADD CONSTRAINT "FK_b1d46037f23d170da5c05fdf755" FOREIGN KEY ("otherId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "chat_approval" DROP CONSTRAINT "FK_b1d46037f23d170da5c05fdf755"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_approval" DROP CONSTRAINT "FK_530257863e1381a7f2f1d3282fe"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_12c4768a2f706fc267f2078903"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_b1d46037f23d170da5c05fdf75"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_530257863e1381a7f2f1d3282f"`);
|
||||
await queryRunner.query(`DROP TABLE "chat_approval"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,26 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class Chat41742707840715 {
|
||||
name = 'Chat41742707840715'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`CREATE TABLE "chat_room_invitation" ("id" character varying(32) NOT NULL, "userId" character varying(32) NOT NULL, "roomId" character varying(32) NOT NULL, CONSTRAINT "PK_9d489521a312dd28225672de2dc" PRIMARY KEY ("id"))`);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_8552bb38e7ed038c5bdd398a38" ON "chat_room_invitation" ("userId") `);
|
||||
await queryRunner.query(`CREATE INDEX "IDX_5f265075b215fc390a57523b12" ON "chat_room_invitation" ("roomId") `);
|
||||
await queryRunner.query(`CREATE UNIQUE INDEX "IDX_044f2a7962b8ee5bbfaa02e8a3" ON "chat_room_invitation" ("userId", "roomId") `);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_invitation" ADD CONSTRAINT "FK_8552bb38e7ed038c5bdd398a384" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_invitation" ADD CONSTRAINT "FK_5f265075b215fc390a57523b12a" FOREIGN KEY ("roomId") REFERENCES "chat_room"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_invitation" DROP CONSTRAINT "FK_5f265075b215fc390a57523b12a"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_invitation" DROP CONSTRAINT "FK_8552bb38e7ed038c5bdd398a384"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_044f2a7962b8ee5bbfaa02e8a3"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_5f265075b215fc390a57523b12"`);
|
||||
await queryRunner.query(`DROP INDEX "public"."IDX_8552bb38e7ed038c5bdd398a38"`);
|
||||
await queryRunner.query(`DROP TABLE "chat_room_invitation"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class Chat51742721896936 {
|
||||
name = 'Chat51742721896936'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_invitation" ADD "ignored" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_invitation" DROP COLUMN "ignored"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class Chat61742795111958 {
|
||||
name = 'Chat61742795111958'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "chat_room" ADD "description" character varying(2048) NOT NULL DEFAULT ''`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room" ADD "isArchived" boolean NOT NULL DEFAULT false`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_membership" ADD "isMuted" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "chat_room_membership" DROP COLUMN "isMuted"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room" DROP COLUMN "isArchived"`);
|
||||
await queryRunner.query(`ALTER TABLE "chat_room" DROP COLUMN "description"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class RoleCopyOnMoveAccount1743558299182 {
|
||||
name = 'RoleCopyOnMoveAccount1743558299182'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "role" ADD "preserveAssignmentOnMoveAccount" boolean NOT NULL DEFAULT false`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "role" DROP COLUMN "preserveAssignmentOnMoveAccount"`);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
export class ExcludeNotesInSensitiveChannel1744075766000 {
|
||||
name = 'ExcludeNotesInSensitiveChannel1744075766000'
|
||||
|
||||
async up(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "antenna" RENAME COLUMN "hideNotesInSensitiveChannel" TO "excludeNotesInSensitiveChannel"`);
|
||||
}
|
||||
|
||||
async down(queryRunner) {
|
||||
await queryRunner.query(`ALTER TABLE "antenna" RENAME COLUMN "excludeNotesInSensitiveChannel" TO "hideNotesInSensitiveChannel"`);
|
||||
}
|
||||
}
|
|
@ -37,17 +37,17 @@
|
|||
},
|
||||
"optionalDependencies": {
|
||||
"@swc/core-android-arm64": "1.3.11",
|
||||
"@swc/core-darwin-arm64": "1.10.16",
|
||||
"@swc/core-darwin-x64": "1.10.16",
|
||||
"@swc/core-darwin-arm64": "1.11.11",
|
||||
"@swc/core-darwin-x64": "1.11.11",
|
||||
"@swc/core-freebsd-x64": "1.3.11",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.10.16",
|
||||
"@swc/core-linux-arm64-gnu": "1.10.16",
|
||||
"@swc/core-linux-arm64-musl": "1.10.16",
|
||||
"@swc/core-linux-x64-gnu": "1.10.16",
|
||||
"@swc/core-linux-x64-musl": "1.10.16",
|
||||
"@swc/core-win32-arm64-msvc": "1.10.16",
|
||||
"@swc/core-win32-ia32-msvc": "1.10.16",
|
||||
"@swc/core-win32-x64-msvc": "1.10.16",
|
||||
"@swc/core-linux-arm-gnueabihf": "1.11.11",
|
||||
"@swc/core-linux-arm64-gnu": "1.11.11",
|
||||
"@swc/core-linux-arm64-musl": "1.11.11",
|
||||
"@swc/core-linux-x64-gnu": "1.11.11",
|
||||
"@swc/core-linux-x64-musl": "1.11.11",
|
||||
"@swc/core-win32-arm64-msvc": "1.11.11",
|
||||
"@swc/core-win32-ia32-msvc": "1.11.11",
|
||||
"@swc/core-win32-x64-msvc": "1.11.11",
|
||||
"@tensorflow/tfjs": "4.22.0",
|
||||
"@tensorflow/tfjs-node": "4.22.0",
|
||||
"bufferutil": "4.0.9",
|
||||
|
@ -67,26 +67,23 @@
|
|||
"utf-8-validate": "6.0.5"
|
||||
},
|
||||
"dependencies": {
|
||||
"@aws-sdk/client-s3": "3.749.0",
|
||||
"@aws-sdk/lib-storage": "3.749.0",
|
||||
"@bull-board/api": "6.7.7",
|
||||
"@bull-board/fastify": "6.7.7",
|
||||
"@bull-board/ui": "6.7.7",
|
||||
"@aws-sdk/client-s3": "3.772.0",
|
||||
"@aws-sdk/lib-storage": "3.772.0",
|
||||
"@discordapp/twemoji": "15.1.0",
|
||||
"@fastify/accepts": "5.0.2",
|
||||
"@fastify/cookie": "11.0.2",
|
||||
"@fastify/cors": "10.0.2",
|
||||
"@fastify/cors": "10.1.0",
|
||||
"@fastify/express": "4.0.2",
|
||||
"@fastify/http-proxy": "10.0.2",
|
||||
"@fastify/multipart": "9.0.3",
|
||||
"@fastify/static": "8.1.0",
|
||||
"@fastify/static": "8.1.1",
|
||||
"@fastify/view": "10.0.2",
|
||||
"@misskey-dev/sharp-read-bmp": "1.2.0",
|
||||
"@misskey-dev/summaly": "5.2.0",
|
||||
"@napi-rs/canvas": "0.1.67",
|
||||
"@nestjs/common": "11.0.9",
|
||||
"@nestjs/core": "11.0.9",
|
||||
"@nestjs/testing": "11.0.9",
|
||||
"@napi-rs/canvas": "0.1.68",
|
||||
"@nestjs/common": "11.0.12",
|
||||
"@nestjs/core": "11.0.12",
|
||||
"@nestjs/testing": "11.0.12",
|
||||
"@peertube/http-signature": "1.7.0",
|
||||
"@sentry/node": "8.55.0",
|
||||
"@sentry/profiling-node": "8.55.0",
|
||||
|
@ -94,7 +91,7 @@
|
|||
"@sinonjs/fake-timers": "11.3.1",
|
||||
"@smithy/node-http-handler": "2.5.0",
|
||||
"@swc/cli": "0.6.0",
|
||||
"@swc/core": "1.10.16",
|
||||
"@swc/core": "1.11.11",
|
||||
"@twemoji/parser": "15.1.1",
|
||||
"accepts": "1.3.8",
|
||||
"ajv": "8.17.1",
|
||||
|
@ -103,7 +100,7 @@
|
|||
"bcryptjs": "2.4.3",
|
||||
"blurhash": "2.0.5",
|
||||
"body-parser": "1.20.3",
|
||||
"bullmq": "5.41.1",
|
||||
"bullmq": "5.44.1",
|
||||
"cacheable-lookup": "7.0.0",
|
||||
"cbor": "9.0.2",
|
||||
"chalk": "5.4.1",
|
||||
|
@ -125,7 +122,7 @@
|
|||
"hpagent": "1.2.0",
|
||||
"htmlescape": "1.1.1",
|
||||
"http-link-header": "1.1.3",
|
||||
"ioredis": "5.5.0",
|
||||
"ioredis": "5.6.0",
|
||||
"ip-cidr": "4.0.2",
|
||||
"ipaddr.js": "2.2.0",
|
||||
"is-svg": "5.1.0",
|
||||
|
@ -134,26 +131,26 @@
|
|||
"json5": "2.2.3",
|
||||
"jsonld": "8.3.3",
|
||||
"jsrsasign": "11.1.0",
|
||||
"juice": "11.0.0",
|
||||
"meilisearch": "0.48.2",
|
||||
"juice": "11.0.1",
|
||||
"meilisearch": "0.49.0",
|
||||
"mfm-js": "0.24.0",
|
||||
"microformats-parser": "2.0.2",
|
||||
"mime-types": "2.1.35",
|
||||
"misskey-js": "workspace:*",
|
||||
"misskey-reversi": "workspace:*",
|
||||
"ms": "3.0.0-canary.1",
|
||||
"nanoid": "5.1.0",
|
||||
"nanoid": "5.1.5",
|
||||
"nested-property": "4.0.0",
|
||||
"node-fetch": "3.3.2",
|
||||
"nodemailer": "6.10.0",
|
||||
"nsfwjs": "4.2.0",
|
||||
"oauth": "0.10.0",
|
||||
"oauth": "0.10.2",
|
||||
"oauth2orize": "1.12.0",
|
||||
"oauth2orize-pkce": "0.1.2",
|
||||
"os-utils": "0.0.14",
|
||||
"otpauth": "9.3.6",
|
||||
"parse5": "7.2.1",
|
||||
"pg": "8.13.3",
|
||||
"pg": "8.14.1",
|
||||
"pkce-challenge": "4.1.0",
|
||||
"probe-image-size": "7.2.3",
|
||||
"promise-limit": "2.7.0",
|
||||
|
@ -166,8 +163,8 @@
|
|||
"reflect-metadata": "0.2.2",
|
||||
"rename": "1.0.4",
|
||||
"rss-parser": "3.13.0",
|
||||
"rxjs": "7.8.1",
|
||||
"sanitize-html": "2.14.0",
|
||||
"rxjs": "7.8.2",
|
||||
"sanitize-html": "2.15.0",
|
||||
"secure-json-parse": "3.0.2",
|
||||
"sharp": "0.33.5",
|
||||
"slacc": "0.0.10",
|
||||
|
@ -176,19 +173,20 @@
|
|||
"systeminformation": "5.25.11",
|
||||
"tinycolor2": "1.6.0",
|
||||
"tmp": "0.2.3",
|
||||
"tsc-alias": "1.8.10",
|
||||
"tsc-alias": "1.8.11",
|
||||
"tsconfig-paths": "4.2.0",
|
||||
"typeorm": "0.3.20",
|
||||
"typescript": "5.7.3",
|
||||
"ulid": "2.3.0",
|
||||
"typeorm": "0.3.21",
|
||||
"typescript": "5.8.2",
|
||||
"ulid": "2.4.0",
|
||||
"vary": "1.1.2",
|
||||
"web-push": "3.6.7",
|
||||
"ws": "8.18.0",
|
||||
"ws": "8.18.1",
|
||||
"xev": "3.0.2"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@jest/globals": "29.7.0",
|
||||
"@nestjs/platform-express": "10.4.15",
|
||||
"@sentry/vue": "9.8.0",
|
||||
"@simplewebauthn/types": "12.0.0",
|
||||
"@swc/jest": "0.2.37",
|
||||
"@types/accepts": "1.3.7",
|
||||
|
@ -207,7 +205,7 @@
|
|||
"@types/jsrsasign": "10.5.15",
|
||||
"@types/mime-types": "2.1.4",
|
||||
"@types/ms": "0.7.34",
|
||||
"@types/node": "22.13.4",
|
||||
"@types/node": "22.13.10",
|
||||
"@types/nodemailer": "6.4.17",
|
||||
"@types/oauth": "0.9.6",
|
||||
"@types/oauth2orize": "1.11.5",
|
||||
|
@ -226,9 +224,9 @@
|
|||
"@types/tmp": "0.2.6",
|
||||
"@types/vary": "1.1.3",
|
||||
"@types/web-push": "3.6.4",
|
||||
"@types/ws": "8.5.14",
|
||||
"@typescript-eslint/eslint-plugin": "8.24.0",
|
||||
"@typescript-eslint/parser": "8.24.0",
|
||||
"@types/ws": "8.18.0",
|
||||
"@typescript-eslint/eslint-plugin": "8.27.0",
|
||||
"@typescript-eslint/parser": "8.27.0",
|
||||
"aws-sdk-client-mock": "4.1.0",
|
||||
"cross-env": "7.0.3",
|
||||
"eslint-plugin-import": "2.31.0",
|
||||
|
|
|
@ -7,7 +7,8 @@ import * as fs from 'node:fs';
|
|||
import { fileURLToPath } from 'node:url';
|
||||
import { dirname, resolve } from 'node:path';
|
||||
import * as yaml from 'js-yaml';
|
||||
import * as Sentry from '@sentry/node';
|
||||
import type * as Sentry from '@sentry/node';
|
||||
import type * as SentryVue from '@sentry/vue';
|
||||
import type { RedisOptions } from 'ioredis';
|
||||
|
||||
type RedisOptionsSource = Partial<RedisOptions> & {
|
||||
|
@ -62,7 +63,12 @@ type Source = {
|
|||
scope?: 'local' | 'global' | string[];
|
||||
};
|
||||
sentryForBackend?: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; };
|
||||
sentryForFrontend?: { options: Partial<Sentry.NodeOptions> };
|
||||
sentryForFrontend?: {
|
||||
options: Partial<SentryVue.BrowserOptions> & { dsn: string };
|
||||
vueIntegration?: SentryVue.VueIntegrationOptions | null;
|
||||
browserTracingIntegration?: Parameters<typeof SentryVue.browserTracingIntegration>[0] | null;
|
||||
replayIntegration?: Parameters<typeof SentryVue.replayIntegration>[0] | null;
|
||||
};
|
||||
|
||||
publishTarballInsteadOfProvideRepositoryUrl?: boolean;
|
||||
|
||||
|
@ -198,7 +204,12 @@ export type Config = {
|
|||
redisForTimelines: RedisOptions & RedisOptionsSource;
|
||||
redisForReactions: RedisOptions & RedisOptionsSource;
|
||||
sentryForBackend: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; } | undefined;
|
||||
sentryForFrontend: { options: Partial<Sentry.NodeOptions> } | undefined;
|
||||
sentryForFrontend: {
|
||||
options: Partial<SentryVue.BrowserOptions> & { dsn: string };
|
||||
vueIntegration?: SentryVue.VueIntegrationOptions | null;
|
||||
browserTracingIntegration?: Parameters<typeof SentryVue.browserTracingIntegration>[0] | null;
|
||||
replayIntegration?: Parameters<typeof SentryVue.replayIntegration>[0] | null;
|
||||
} | undefined;
|
||||
perChannelMaxNoteCacheCount: number;
|
||||
perUserNotificationsMaxCount: number;
|
||||
deactivateAntennaThreshold: number;
|
||||
|
|
|
@ -24,6 +24,7 @@ import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
|
|||
import InstanceChart from '@/core/chart/charts/instance.js';
|
||||
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
|
||||
import { SystemAccountService } from '@/core/SystemAccountService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
|
||||
@Injectable()
|
||||
export class AccountMoveService {
|
||||
|
@ -61,6 +62,7 @@ export class AccountMoveService {
|
|||
private relayService: RelayService,
|
||||
private queueService: QueueService,
|
||||
private systemAccountService: SystemAccountService,
|
||||
private roleService: RoleService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -119,6 +121,7 @@ export class AccountMoveService {
|
|||
await Promise.all([
|
||||
this.copyBlocking(src, dst),
|
||||
this.copyMutings(src, dst),
|
||||
this.copyRoles(src, dst),
|
||||
this.updateLists(src, dst),
|
||||
]);
|
||||
} catch {
|
||||
|
@ -201,6 +204,32 @@ export class AccountMoveService {
|
|||
await this.mutingsRepository.insert(arrayToInsert);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async copyRoles(src: ThinUser, dst: ThinUser): Promise<void> {
|
||||
// Insert new roles with the same values except userId
|
||||
// role service may have cache for roles so retrieve roles from service
|
||||
const [oldRoleAssignments, roles] = await Promise.all([
|
||||
this.roleService.getUserAssigns(src.id),
|
||||
this.roleService.getRoles(),
|
||||
]);
|
||||
|
||||
if (oldRoleAssignments.length === 0) return;
|
||||
|
||||
// No promise all since the only async operation is writing to the database
|
||||
for (const oldRoleAssignment of oldRoleAssignments) {
|
||||
const role = roles.find(x => x.id === oldRoleAssignment.roleId);
|
||||
if (role == null) continue; // Very unlikely however removing role may cause this case
|
||||
if (!role.preserveAssignmentOnMoveAccount) continue;
|
||||
|
||||
try {
|
||||
await this.roleService.assign(dst.id, role.id, oldRoleAssignment.expiresAt);
|
||||
} catch (e) {
|
||||
if (e instanceof RoleService.AlreadyAssignedError) continue;
|
||||
throw e;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Update lists while moving accounts.
|
||||
* - No removal of the old account from the lists
|
||||
|
|
|
@ -114,6 +114,8 @@ export class AntennaService implements OnApplicationShutdown {
|
|||
if (note.visibility === 'specified') return false;
|
||||
if (note.visibility === 'followers') return false;
|
||||
|
||||
if (antenna.excludeNotesInSensitiveChannel && note.channel?.isSensitive) return false;
|
||||
|
||||
if (antenna.excludeBots && noteUser.isBot) return false;
|
||||
|
||||
if (antenna.localOnly && noteUser.host != null) return false;
|
||||
|
|
|
@ -0,0 +1,925 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import * as Redis from 'ioredis';
|
||||
import { Brackets } from 'typeorm';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { ChatEntityService } from '@/core/entities/ChatEntityService.js';
|
||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||
import { PushNotificationService } from '@/core/PushNotificationService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import type { ChatApprovalsRepository, ChatMessagesRepository, ChatRoomInvitationsRepository, ChatRoomMembershipsRepository, ChatRoomsRepository, MiChatMessage, MiChatRoom, MiChatRoomMembership, MiDriveFile, MiUser, MutingsRepository, UsersRepository } from '@/models/_.js';
|
||||
import { UserBlockingService } from '@/core/UserBlockingService.js';
|
||||
import { QueryService } from '@/core/QueryService.js';
|
||||
import { RoleService } from '@/core/RoleService.js';
|
||||
import { UserFollowingService } from '@/core/UserFollowingService.js';
|
||||
import { MiChatRoomInvitation } from '@/models/ChatRoomInvitation.js';
|
||||
import { Packed } from '@/misc/json-schema.js';
|
||||
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { emojiRegex } from '@/misc/emoji-regex.js';
|
||||
import { NotificationService } from '@/core/NotificationService.js';
|
||||
import { ModerationLogService } from '@/core/ModerationLogService.js';
|
||||
|
||||
const MAX_ROOM_MEMBERS = 30;
|
||||
const MAX_REACTIONS_PER_MESSAGE = 100;
|
||||
const isCustomEmojiRegexp = /^:([\w+-]+)(?:@\.)?:$/;
|
||||
|
||||
// TODO: ReactionServiceのやつと共通化
|
||||
function normalizeEmojiString(x: string) {
|
||||
const match = emojiRegex.exec(x);
|
||||
if (match) {
|
||||
// 合字を含む1つの絵文字
|
||||
const unicode = match[0];
|
||||
|
||||
// 異体字セレクタ除去
|
||||
return unicode.match('\u200d') ? unicode : unicode.replace(/\ufe0f/g, '');
|
||||
} else {
|
||||
throw new Error('invalid emoji');
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class ChatService {
|
||||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
|
||||
@Inject(DI.redis)
|
||||
private redisClient: Redis.Redis,
|
||||
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
@Inject(DI.chatMessagesRepository)
|
||||
private chatMessagesRepository: ChatMessagesRepository,
|
||||
|
||||
@Inject(DI.chatApprovalsRepository)
|
||||
private chatApprovalsRepository: ChatApprovalsRepository,
|
||||
|
||||
@Inject(DI.chatRoomsRepository)
|
||||
private chatRoomsRepository: ChatRoomsRepository,
|
||||
|
||||
@Inject(DI.chatRoomInvitationsRepository)
|
||||
private chatRoomInvitationsRepository: ChatRoomInvitationsRepository,
|
||||
|
||||
@Inject(DI.chatRoomMembershipsRepository)
|
||||
private chatRoomMembershipsRepository: ChatRoomMembershipsRepository,
|
||||
|
||||
@Inject(DI.mutingsRepository)
|
||||
private mutingsRepository: MutingsRepository,
|
||||
|
||||
private userEntityService: UserEntityService,
|
||||
private chatEntityService: ChatEntityService,
|
||||
private idService: IdService,
|
||||
private globalEventService: GlobalEventService,
|
||||
private apRendererService: ApRendererService,
|
||||
private queueService: QueueService,
|
||||
private pushNotificationService: PushNotificationService,
|
||||
private notificationService: NotificationService,
|
||||
private userBlockingService: UserBlockingService,
|
||||
private queryService: QueryService,
|
||||
private roleService: RoleService,
|
||||
private userFollowingService: UserFollowingService,
|
||||
private customEmojiService: CustomEmojiService,
|
||||
private moderationLogService: ModerationLogService,
|
||||
) {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getChatAvailability(userId: MiUser['id']): Promise<{ read: boolean; write: boolean; }> {
|
||||
const policies = await this.roleService.getUserPolicies(userId);
|
||||
|
||||
switch (policies.chatAvailability) {
|
||||
case 'available':
|
||||
return {
|
||||
read: true,
|
||||
write: true,
|
||||
};
|
||||
case 'readonly':
|
||||
return {
|
||||
read: true,
|
||||
write: false,
|
||||
};
|
||||
case 'unavailable':
|
||||
return {
|
||||
read: false,
|
||||
write: false,
|
||||
};
|
||||
default:
|
||||
throw new Error('invalid chat availability (unreachable)');
|
||||
}
|
||||
}
|
||||
|
||||
/** getChatAvailabilityの糖衣。主にAPI呼び出し時に走らせて、権限的に問題ない場合はそのまま続行する */
|
||||
@bindThis
|
||||
public async checkChatAvailability(userId: MiUser['id'], permission: 'read' | 'write') {
|
||||
const policy = await this.getChatAvailability(userId);
|
||||
if (policy[permission] === false) {
|
||||
throw new Error('ROLE_PERMISSION_DENIED');
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async createMessageToUser(fromUser: { id: MiUser['id']; host: MiUser['host']; }, toUser: MiUser, params: {
|
||||
text?: string | null;
|
||||
file?: MiDriveFile | null;
|
||||
uri?: string | null;
|
||||
}): Promise<Packed<'ChatMessageLiteFor1on1'>> {
|
||||
if (fromUser.id === toUser.id) {
|
||||
throw new Error('yourself');
|
||||
}
|
||||
|
||||
const approvals = await this.chatApprovalsRepository.createQueryBuilder('approval')
|
||||
.where(new Brackets(qb => { // 自分が相手を許可しているか
|
||||
qb.where('approval.userId = :fromUserId', { fromUserId: fromUser.id })
|
||||
.andWhere('approval.otherId = :toUserId', { toUserId: toUser.id });
|
||||
}))
|
||||
.orWhere(new Brackets(qb => { // 相手が自分を許可しているか
|
||||
qb.where('approval.userId = :toUserId', { toUserId: toUser.id })
|
||||
.andWhere('approval.otherId = :fromUserId', { fromUserId: fromUser.id });
|
||||
}))
|
||||
.take(2)
|
||||
.getMany();
|
||||
|
||||
const otherApprovedMe = approvals.some(approval => approval.userId === toUser.id);
|
||||
const iApprovedOther = approvals.some(approval => approval.userId === fromUser.id);
|
||||
|
||||
if (!otherApprovedMe) {
|
||||
if (toUser.chatScope === 'none') {
|
||||
throw new Error('recipient is cannot chat (none)');
|
||||
} else if (toUser.chatScope === 'followers') {
|
||||
const isFollower = await this.userFollowingService.isFollowing(fromUser.id, toUser.id);
|
||||
if (!isFollower) {
|
||||
throw new Error('recipient is cannot chat (followers)');
|
||||
}
|
||||
} else if (toUser.chatScope === 'following') {
|
||||
const isFollowing = await this.userFollowingService.isFollowing(toUser.id, fromUser.id);
|
||||
if (!isFollowing) {
|
||||
throw new Error('recipient is cannot chat (following)');
|
||||
}
|
||||
} else if (toUser.chatScope === 'mutual') {
|
||||
const isMutual = await this.userFollowingService.isMutual(fromUser.id, toUser.id);
|
||||
if (!isMutual) {
|
||||
throw new Error('recipient is cannot chat (mutual)');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!(await this.getChatAvailability(toUser.id)).write) {
|
||||
throw new Error('recipient is cannot chat (policy)');
|
||||
}
|
||||
|
||||
const blocked = await this.userBlockingService.checkBlocked(toUser.id, fromUser.id);
|
||||
if (blocked) {
|
||||
throw new Error('blocked');
|
||||
}
|
||||
|
||||
const message = {
|
||||
id: this.idService.gen(),
|
||||
fromUserId: fromUser.id,
|
||||
toUserId: toUser.id,
|
||||
text: params.text ? params.text.trim() : null,
|
||||
fileId: params.file ? params.file.id : null,
|
||||
reads: [],
|
||||
uri: params.uri ?? null,
|
||||
} satisfies Partial<MiChatMessage>;
|
||||
|
||||
const inserted = await this.chatMessagesRepository.insertOne(message);
|
||||
|
||||
// 相手を許可しておく
|
||||
if (!iApprovedOther) {
|
||||
this.chatApprovalsRepository.insertOne({
|
||||
id: this.idService.gen(),
|
||||
userId: fromUser.id,
|
||||
otherId: toUser.id,
|
||||
});
|
||||
}
|
||||
|
||||
const packedMessage = await this.chatEntityService.packMessageLiteFor1on1(inserted);
|
||||
|
||||
if (this.userEntityService.isLocalUser(toUser)) {
|
||||
const redisPipeline = this.redisClient.pipeline();
|
||||
redisPipeline.set(`newUserChatMessageExists:${toUser.id}:${fromUser.id}`, message.id);
|
||||
redisPipeline.sadd(`newChatMessagesExists:${toUser.id}`, `user:${fromUser.id}`);
|
||||
redisPipeline.exec();
|
||||
}
|
||||
|
||||
if (this.userEntityService.isLocalUser(fromUser)) {
|
||||
// 自分のストリーム
|
||||
this.globalEventService.publishChatUserStream(fromUser.id, toUser.id, 'message', packedMessage);
|
||||
}
|
||||
|
||||
if (this.userEntityService.isLocalUser(toUser)) {
|
||||
// 相手のストリーム
|
||||
this.globalEventService.publishChatUserStream(toUser.id, fromUser.id, 'message', packedMessage);
|
||||
}
|
||||
|
||||
// 3秒経っても既読にならなかったらイベント発行
|
||||
if (this.userEntityService.isLocalUser(toUser)) {
|
||||
setTimeout(async () => {
|
||||
const marker = await this.redisClient.get(`newUserChatMessageExists:${toUser.id}:${fromUser.id}`);
|
||||
|
||||
if (marker == null) return; // 既読
|
||||
|
||||
const packedMessageForTo = await this.chatEntityService.packMessageDetailed(inserted, toUser);
|
||||
this.globalEventService.publishMainStream(toUser.id, 'newChatMessage', packedMessageForTo);
|
||||
//this.pushNotificationService.pushNotification(toUser.id, 'newChatMessage', packedMessageForTo);
|
||||
}, 3000);
|
||||
}
|
||||
|
||||
return packedMessage;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async createMessageToRoom(fromUser: { id: MiUser['id']; host: MiUser['host']; }, toRoom: MiChatRoom, params: {
|
||||
text?: string | null;
|
||||
file?: MiDriveFile | null;
|
||||
uri?: string | null;
|
||||
}): Promise<Packed<'ChatMessageLiteForRoom'>> {
|
||||
const memberships = (await this.chatRoomMembershipsRepository.findBy({ roomId: toRoom.id })).map(m => ({
|
||||
userId: m.userId,
|
||||
isMuted: m.isMuted,
|
||||
})).concat({ // ownerはmembershipレコードを作らないため
|
||||
userId: toRoom.ownerId,
|
||||
isMuted: false,
|
||||
});
|
||||
|
||||
if (!memberships.some(member => member.userId === fromUser.id)) {
|
||||
throw new Error('you are not a member of the room');
|
||||
}
|
||||
|
||||
const membershipsOtherThanMe = memberships.filter(member => member.userId !== fromUser.id);
|
||||
|
||||
const message = {
|
||||
id: this.idService.gen(),
|
||||
fromUserId: fromUser.id,
|
||||
toRoomId: toRoom.id,
|
||||
text: params.text ? params.text.trim() : null,
|
||||
fileId: params.file ? params.file.id : null,
|
||||
reads: [],
|
||||
uri: params.uri ?? null,
|
||||
} satisfies Partial<MiChatMessage>;
|
||||
|
||||
const inserted = await this.chatMessagesRepository.insertOne(message);
|
||||
|
||||
const packedMessage = await this.chatEntityService.packMessageLiteForRoom(inserted);
|
||||
|
||||
this.globalEventService.publishChatRoomStream(toRoom.id, 'message', packedMessage);
|
||||
|
||||
const redisPipeline = this.redisClient.pipeline();
|
||||
for (const membership of membershipsOtherThanMe) {
|
||||
if (membership.isMuted) continue;
|
||||
|
||||
redisPipeline.set(`newRoomChatMessageExists:${membership.userId}:${toRoom.id}`, message.id);
|
||||
redisPipeline.sadd(`newChatMessagesExists:${membership.userId}`, `room:${toRoom.id}`);
|
||||
}
|
||||
redisPipeline.exec();
|
||||
|
||||
// 3秒経っても既読にならなかったらイベント発行
|
||||
setTimeout(async () => {
|
||||
const redisPipeline = this.redisClient.pipeline();
|
||||
for (const membership of membershipsOtherThanMe) {
|
||||
redisPipeline.get(`newRoomChatMessageExists:${membership.userId}:${toRoom.id}`);
|
||||
}
|
||||
const markers = await redisPipeline.exec();
|
||||
if (markers == null) throw new Error('redis error');
|
||||
|
||||
if (markers.every(marker => marker[1] == null)) return;
|
||||
|
||||
const packedMessageForTo = await this.chatEntityService.packMessageDetailed(inserted);
|
||||
|
||||
for (let i = 0; i < membershipsOtherThanMe.length; i++) {
|
||||
const marker = markers[i][1];
|
||||
if (marker == null) continue;
|
||||
|
||||
this.globalEventService.publishMainStream(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo);
|
||||
//this.pushNotificationService.pushNotification(membershipsOtherThanMe[i].userId, 'newChatMessage', packedMessageForTo);
|
||||
}
|
||||
}, 3000);
|
||||
|
||||
return packedMessage;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async readUserChatMessage(
|
||||
readerId: MiUser['id'],
|
||||
senderId: MiUser['id'],
|
||||
): Promise<void> {
|
||||
const redisPipeline = this.redisClient.pipeline();
|
||||
redisPipeline.del(`newUserChatMessageExists:${readerId}:${senderId}`);
|
||||
redisPipeline.srem(`newChatMessagesExists:${readerId}`, `user:${senderId}`);
|
||||
await redisPipeline.exec();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async readRoomChatMessage(
|
||||
readerId: MiUser['id'],
|
||||
roomId: MiChatRoom['id'],
|
||||
): Promise<void> {
|
||||
const redisPipeline = this.redisClient.pipeline();
|
||||
redisPipeline.del(`newRoomChatMessageExists:${readerId}:${roomId}`);
|
||||
redisPipeline.srem(`newChatMessagesExists:${readerId}`, `room:${roomId}`);
|
||||
await redisPipeline.exec();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public findMessageById(messageId: MiChatMessage['id']) {
|
||||
return this.chatMessagesRepository.findOneBy({ id: messageId });
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public findMyMessageById(userId: MiUser['id'], messageId: MiChatMessage['id']) {
|
||||
return this.chatMessagesRepository.findOneBy({ id: messageId, fromUserId: userId });
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async hasPermissionToViewRoomTimeline(meId: MiUser['id'], room: MiChatRoom) {
|
||||
if (await this.isRoomMember(room, meId)) {
|
||||
return true;
|
||||
} else {
|
||||
const iAmModerator = await this.roleService.isModerator({ id: meId });
|
||||
if (iAmModerator) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async deleteMessage(message: MiChatMessage) {
|
||||
await this.chatMessagesRepository.delete(message.id);
|
||||
|
||||
if (message.toUserId) {
|
||||
const [fromUser, toUser] = await Promise.all([
|
||||
this.usersRepository.findOneByOrFail({ id: message.fromUserId }),
|
||||
this.usersRepository.findOneByOrFail({ id: message.toUserId }),
|
||||
]);
|
||||
|
||||
if (this.userEntityService.isLocalUser(fromUser)) this.globalEventService.publishChatUserStream(message.fromUserId, message.toUserId, 'deleted', message.id);
|
||||
if (this.userEntityService.isLocalUser(toUser)) this.globalEventService.publishChatUserStream(message.toUserId, message.fromUserId, 'deleted', message.id);
|
||||
|
||||
if (this.userEntityService.isLocalUser(fromUser) && this.userEntityService.isRemoteUser(toUser)) {
|
||||
//const activity = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${message.id}`), fromUser));
|
||||
//this.queueService.deliver(fromUser, activity, toUser.inbox);
|
||||
}
|
||||
} else if (message.toRoomId) {
|
||||
this.globalEventService.publishChatRoomStream(message.toRoomId, 'deleted', message.id);
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async userTimeline(meId: MiUser['id'], otherId: MiUser['id'], limit: number, sinceId?: MiChatMessage['id'] | null, untilId?: MiChatMessage['id'] | null) {
|
||||
const query = this.queryService.makePaginationQuery(this.chatMessagesRepository.createQueryBuilder('message'), sinceId, untilId)
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where(new Brackets(qb => {
|
||||
qb
|
||||
.where('message.fromUserId = :meId')
|
||||
.andWhere('message.toUserId = :otherId');
|
||||
}))
|
||||
.orWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('message.fromUserId = :otherId')
|
||||
.andWhere('message.toUserId = :meId');
|
||||
}));
|
||||
}))
|
||||
.setParameter('meId', meId)
|
||||
.setParameter('otherId', otherId);
|
||||
|
||||
const messages = await query.take(limit).getMany();
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async roomTimeline(roomId: MiChatRoom['id'], limit: number, sinceId?: MiChatMessage['id'] | null, untilId?: MiChatMessage['id'] | null) {
|
||||
const query = this.queryService.makePaginationQuery(this.chatMessagesRepository.createQueryBuilder('message'), sinceId, untilId)
|
||||
.andWhere('message.toRoomId = :roomId', { roomId })
|
||||
.leftJoinAndSelect('message.file', 'file')
|
||||
.leftJoinAndSelect('message.fromUser', 'fromUser');
|
||||
|
||||
const messages = await query.take(limit).getMany();
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async userHistory(meId: MiUser['id'], limit: number): Promise<MiChatMessage[]> {
|
||||
const history: MiChatMessage[] = [];
|
||||
|
||||
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
|
||||
.select('muting.muteeId')
|
||||
.where('muting.muterId = :muterId', { muterId: meId });
|
||||
|
||||
for (let i = 0; i < limit; i++) {
|
||||
const found = history.map(m => (m.fromUserId === meId) ? m.toUserId! : m.fromUserId!);
|
||||
|
||||
const query = this.chatMessagesRepository.createQueryBuilder('message')
|
||||
.orderBy('message.id', 'DESC')
|
||||
.where(new Brackets(qb => {
|
||||
qb
|
||||
.where('message.fromUserId = :meId', { meId: meId })
|
||||
.orWhere('message.toUserId = :meId', { meId: meId });
|
||||
}))
|
||||
.andWhere('message.toRoomId IS NULL')
|
||||
.andWhere(`message.fromUserId NOT IN (${ mutingQuery.getQuery() })`)
|
||||
.andWhere(`message.toUserId NOT IN (${ mutingQuery.getQuery() })`);
|
||||
|
||||
if (found.length > 0) {
|
||||
query.andWhere('message.fromUserId NOT IN (:...found)', { found: found });
|
||||
query.andWhere('message.toUserId NOT IN (:...found)', { found: found });
|
||||
}
|
||||
|
||||
query.setParameters(mutingQuery.getParameters());
|
||||
|
||||
const message = await query.getOne();
|
||||
|
||||
if (message) {
|
||||
history.push(message);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async roomHistory(meId: MiUser['id'], limit: number): Promise<MiChatMessage[]> {
|
||||
// TODO: 一回のクエリにまとめられるかも
|
||||
const [memberRoomIds, ownedRoomIds] = await Promise.all([
|
||||
this.chatRoomMembershipsRepository.findBy({
|
||||
userId: meId,
|
||||
}).then(xs => xs.map(x => x.roomId)),
|
||||
this.chatRoomsRepository.findBy({
|
||||
ownerId: meId,
|
||||
}).then(xs => xs.map(x => x.id)),
|
||||
]);
|
||||
|
||||
const roomIds = memberRoomIds.concat(ownedRoomIds);
|
||||
|
||||
if (memberRoomIds.length === 0 && ownedRoomIds.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const history: MiChatMessage[] = [];
|
||||
|
||||
for (let i = 0; i < limit; i++) {
|
||||
const found = history.map(m => m.toRoomId!);
|
||||
|
||||
const query = this.chatMessagesRepository.createQueryBuilder('message')
|
||||
.orderBy('message.id', 'DESC')
|
||||
.where('message.toRoomId IN (:...roomIds)', { roomIds });
|
||||
|
||||
if (found.length > 0) {
|
||||
query.andWhere('message.toRoomId NOT IN (:...found)', { found: found });
|
||||
}
|
||||
|
||||
const message = await query.getOne();
|
||||
|
||||
if (message) {
|
||||
history.push(message);
|
||||
} else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return history;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getUserReadStateMap(userId: MiUser['id'], otherIds: MiUser['id'][]) {
|
||||
const readStateMap: Record<MiUser['id'], boolean> = {};
|
||||
|
||||
const redisPipeline = this.redisClient.pipeline();
|
||||
|
||||
for (const otherId of otherIds) {
|
||||
redisPipeline.get(`newUserChatMessageExists:${userId}:${otherId}`);
|
||||
}
|
||||
|
||||
const markers = await redisPipeline.exec();
|
||||
if (markers == null) throw new Error('redis error');
|
||||
|
||||
for (let i = 0; i < otherIds.length; i++) {
|
||||
const marker = markers[i][1];
|
||||
readStateMap[otherIds[i]] = marker == null;
|
||||
}
|
||||
|
||||
return readStateMap;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getRoomReadStateMap(userId: MiUser['id'], roomIds: MiChatRoom['id'][]) {
|
||||
const readStateMap: Record<MiChatRoom['id'], boolean> = {};
|
||||
|
||||
const redisPipeline = this.redisClient.pipeline();
|
||||
|
||||
for (const roomId of roomIds) {
|
||||
redisPipeline.get(`newRoomChatMessageExists:${userId}:${roomId}`);
|
||||
}
|
||||
|
||||
const markers = await redisPipeline.exec();
|
||||
if (markers == null) throw new Error('redis error');
|
||||
|
||||
for (let i = 0; i < roomIds.length; i++) {
|
||||
const marker = markers[i][1];
|
||||
readStateMap[roomIds[i]] = marker == null;
|
||||
}
|
||||
|
||||
return readStateMap;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async hasUnreadMessages(userId: MiUser['id']) {
|
||||
const card = await this.redisClient.scard(`newChatMessagesExists:${userId}`);
|
||||
return card > 0;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async createRoom(owner: MiUser, params: Partial<{
|
||||
name: string;
|
||||
description: string;
|
||||
}>) {
|
||||
const room = {
|
||||
id: this.idService.gen(),
|
||||
name: params.name,
|
||||
description: params.description,
|
||||
ownerId: owner.id,
|
||||
} satisfies Partial<MiChatRoom>;
|
||||
|
||||
const created = await this.chatRoomsRepository.insertOne(room);
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async hasPermissionToDeleteRoom(meId: MiUser['id'], room: MiChatRoom) {
|
||||
if (room.ownerId === meId) {
|
||||
return true;
|
||||
}
|
||||
|
||||
const iAmModerator = await this.roleService.isModerator({ id: meId });
|
||||
if (iAmModerator) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async deleteRoom(room: MiChatRoom, deleter?: MiUser) {
|
||||
await this.chatRoomsRepository.delete(room.id);
|
||||
|
||||
if (deleter) {
|
||||
const deleterIsModerator = await this.roleService.isModerator(deleter);
|
||||
|
||||
if (deleterIsModerator) {
|
||||
this.moderationLogService.log(deleter, 'deleteChatRoom', {
|
||||
roomId: room.id,
|
||||
room: room,
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async findMyRoomById(ownerId: MiUser['id'], roomId: MiChatRoom['id']) {
|
||||
return this.chatRoomsRepository.findOneBy({ id: roomId, ownerId: ownerId });
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async findRoomById(roomId: MiChatRoom['id']) {
|
||||
return this.chatRoomsRepository.findOne({ where: { id: roomId }, relations: ['owner'] });
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async isRoomMember(room: MiChatRoom, userId: MiUser['id']) {
|
||||
if (room.ownerId === userId) return true;
|
||||
const membership = await this.chatRoomMembershipsRepository.findOneBy({ roomId: room.id, userId });
|
||||
return membership != null;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async createRoomInvitation(inviterId: MiUser['id'], roomId: MiChatRoom['id'], inviteeId: MiUser['id']) {
|
||||
if (inviterId === inviteeId) {
|
||||
throw new Error('yourself');
|
||||
}
|
||||
|
||||
const room = await this.chatRoomsRepository.findOneByOrFail({ id: roomId, ownerId: inviterId });
|
||||
|
||||
if (await this.isRoomMember(room, inviteeId)) {
|
||||
throw new Error('already member');
|
||||
}
|
||||
|
||||
const existingInvitation = await this.chatRoomInvitationsRepository.findOneBy({ roomId, userId: inviteeId });
|
||||
if (existingInvitation) {
|
||||
throw new Error('already invited');
|
||||
}
|
||||
|
||||
const membershipsCount = await this.chatRoomMembershipsRepository.countBy({ roomId });
|
||||
if (membershipsCount >= MAX_ROOM_MEMBERS) {
|
||||
throw new Error('room is full');
|
||||
}
|
||||
|
||||
// TODO: cehck block
|
||||
|
||||
const invitation = {
|
||||
id: this.idService.gen(),
|
||||
roomId: room.id,
|
||||
userId: inviteeId,
|
||||
} satisfies Partial<MiChatRoomInvitation>;
|
||||
|
||||
const created = await this.chatRoomInvitationsRepository.insertOne(invitation);
|
||||
|
||||
this.notificationService.createNotification(inviteeId, 'chatRoomInvitationReceived', {
|
||||
invitationId: invitation.id,
|
||||
}, inviterId);
|
||||
|
||||
return created;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getSentRoomInvitationsWithPagination(roomId: MiChatRoom['id'], limit: number, sinceId?: MiChatRoomInvitation['id'] | null, untilId?: MiChatRoomInvitation['id'] | null) {
|
||||
const query = this.queryService.makePaginationQuery(this.chatRoomInvitationsRepository.createQueryBuilder('invitation'), sinceId, untilId)
|
||||
.andWhere('invitation.roomId = :roomId', { roomId });
|
||||
|
||||
const invitations = await query.take(limit).getMany();
|
||||
|
||||
return invitations;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getOwnedRoomsWithPagination(ownerId: MiUser['id'], limit: number, sinceId?: MiChatRoom['id'] | null, untilId?: MiChatRoom['id'] | null) {
|
||||
const query = this.queryService.makePaginationQuery(this.chatRoomsRepository.createQueryBuilder('room'), sinceId, untilId)
|
||||
.andWhere('room.ownerId = :ownerId', { ownerId });
|
||||
|
||||
const rooms = await query.take(limit).getMany();
|
||||
|
||||
return rooms;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getReceivedRoomInvitationsWithPagination(userId: MiUser['id'], limit: number, sinceId?: MiChatRoomInvitation['id'] | null, untilId?: MiChatRoomInvitation['id'] | null) {
|
||||
const query = this.queryService.makePaginationQuery(this.chatRoomInvitationsRepository.createQueryBuilder('invitation'), sinceId, untilId)
|
||||
.andWhere('invitation.userId = :userId', { userId })
|
||||
.andWhere('invitation.ignored = FALSE');
|
||||
|
||||
const invitations = await query.take(limit).getMany();
|
||||
|
||||
return invitations;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async joinToRoom(userId: MiUser['id'], roomId: MiChatRoom['id']) {
|
||||
const invitation = await this.chatRoomInvitationsRepository.findOneByOrFail({ roomId, userId });
|
||||
|
||||
const membershipsCount = await this.chatRoomMembershipsRepository.countBy({ roomId });
|
||||
if (membershipsCount >= MAX_ROOM_MEMBERS) {
|
||||
throw new Error('room is full');
|
||||
}
|
||||
|
||||
const membership = {
|
||||
id: this.idService.gen(),
|
||||
roomId: roomId,
|
||||
userId: userId,
|
||||
} satisfies Partial<MiChatRoomMembership>;
|
||||
|
||||
// TODO: transaction
|
||||
await this.chatRoomMembershipsRepository.insertOne(membership);
|
||||
await this.chatRoomInvitationsRepository.delete(invitation.id);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async ignoreRoomInvitation(userId: MiUser['id'], roomId: MiChatRoom['id']) {
|
||||
const invitation = await this.chatRoomInvitationsRepository.findOneByOrFail({ roomId, userId });
|
||||
await this.chatRoomInvitationsRepository.update(invitation.id, { ignored: true });
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async leaveRoom(userId: MiUser['id'], roomId: MiChatRoom['id']) {
|
||||
const membership = await this.chatRoomMembershipsRepository.findOneByOrFail({ roomId, userId });
|
||||
await this.chatRoomMembershipsRepository.delete(membership.id);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async muteRoom(userId: MiUser['id'], roomId: MiChatRoom['id'], mute: boolean) {
|
||||
const membership = await this.chatRoomMembershipsRepository.findOneByOrFail({ roomId, userId });
|
||||
await this.chatRoomMembershipsRepository.update(membership.id, { isMuted: mute });
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async updateRoom(room: MiChatRoom, params: {
|
||||
name?: string;
|
||||
description?: string;
|
||||
}): Promise<MiChatRoom> {
|
||||
return this.chatRoomsRepository.createQueryBuilder().update()
|
||||
.set(params)
|
||||
.where('id = :id', { id: room.id })
|
||||
.returning('*')
|
||||
.execute()
|
||||
.then((response) => {
|
||||
return response.raw[0];
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getRoomMembershipsWithPagination(roomId: MiChatRoom['id'], limit: number, sinceId?: MiChatRoomMembership['id'] | null, untilId?: MiChatRoomMembership['id'] | null) {
|
||||
const query = this.queryService.makePaginationQuery(this.chatRoomMembershipsRepository.createQueryBuilder('membership'), sinceId, untilId)
|
||||
.andWhere('membership.roomId = :roomId', { roomId });
|
||||
|
||||
const memberships = await query.take(limit).getMany();
|
||||
|
||||
return memberships;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async searchMessages(meId: MiUser['id'], query: string, limit: number, params: {
|
||||
userId?: MiUser['id'] | null;
|
||||
roomId?: MiChatRoom['id'] | null;
|
||||
}) {
|
||||
const q = this.chatMessagesRepository.createQueryBuilder('message');
|
||||
|
||||
if (params.userId) {
|
||||
q.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where(new Brackets(qb => {
|
||||
qb
|
||||
.where('message.fromUserId = :meId')
|
||||
.andWhere('message.toUserId = :otherId');
|
||||
}))
|
||||
.orWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('message.fromUserId = :otherId')
|
||||
.andWhere('message.toUserId = :meId');
|
||||
}));
|
||||
}))
|
||||
.setParameter('meId', meId)
|
||||
.setParameter('otherId', params.userId);
|
||||
} else if (params.roomId) {
|
||||
q.where('message.toRoomId = :roomId', { roomId: params.roomId });
|
||||
} else {
|
||||
const membershipsQuery = this.chatRoomMembershipsRepository.createQueryBuilder('membership')
|
||||
.select('membership.roomId')
|
||||
.where('membership.userId = :meId', { meId: meId });
|
||||
|
||||
const ownedRoomsQuery = this.chatRoomsRepository.createQueryBuilder('room')
|
||||
.select('room.id')
|
||||
.where('room.ownerId = :meId', { meId });
|
||||
|
||||
q.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('message.fromUserId = :meId')
|
||||
.orWhere('message.toUserId = :meId')
|
||||
.orWhere(`message.toRoomId IN (${membershipsQuery.getQuery()})`)
|
||||
.orWhere(`message.toRoomId IN (${ownedRoomsQuery.getQuery()})`);
|
||||
}));
|
||||
|
||||
q.setParameters(membershipsQuery.getParameters());
|
||||
q.setParameters(ownedRoomsQuery.getParameters());
|
||||
}
|
||||
|
||||
q.andWhere('LOWER(message.text) LIKE :q', { q: `%${ sqlLikeEscape(query.toLowerCase()) }%` });
|
||||
|
||||
q.leftJoinAndSelect('message.file', 'file');
|
||||
q.leftJoinAndSelect('message.fromUser', 'fromUser');
|
||||
q.leftJoinAndSelect('message.toUser', 'toUser');
|
||||
q.leftJoinAndSelect('message.toRoom', 'toRoom');
|
||||
q.leftJoinAndSelect('toRoom.owner', 'toRoomOwner');
|
||||
|
||||
const messages = await q.orderBy('message.id', 'DESC').take(limit).getMany();
|
||||
|
||||
return messages;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async react(messageId: MiChatMessage['id'], userId: MiUser['id'], reaction_: string) {
|
||||
let reaction;
|
||||
|
||||
const custom = reaction_.match(isCustomEmojiRegexp);
|
||||
|
||||
if (custom == null) {
|
||||
reaction = normalizeEmojiString(reaction_);
|
||||
} else {
|
||||
const name = custom[1];
|
||||
const emoji = (await this.customEmojiService.localEmojisCache.fetch()).get(name);
|
||||
|
||||
if (emoji == null) {
|
||||
throw new Error('no such emoji');
|
||||
} else {
|
||||
reaction = `:${name}:`;
|
||||
}
|
||||
}
|
||||
|
||||
const message = await this.chatMessagesRepository.findOneByOrFail({ id: messageId });
|
||||
|
||||
if (message.fromUserId === userId) {
|
||||
throw new Error('cannot react to own message');
|
||||
}
|
||||
|
||||
if (message.toRoomId === null && message.toUserId !== userId) {
|
||||
throw new Error('cannot react to others message');
|
||||
}
|
||||
|
||||
if (message.reactions.length >= MAX_REACTIONS_PER_MESSAGE) {
|
||||
throw new Error('too many reactions');
|
||||
}
|
||||
|
||||
const room = message.toRoomId ? await this.chatRoomsRepository.findOneByOrFail({ id: message.toRoomId }) : null;
|
||||
|
||||
if (room) {
|
||||
if (!await this.isRoomMember(room, userId)) {
|
||||
throw new Error('cannot react to others message');
|
||||
}
|
||||
}
|
||||
|
||||
await this.chatMessagesRepository.createQueryBuilder().update()
|
||||
.set({
|
||||
reactions: () => `array_append("reactions", '${userId}/${reaction}')`,
|
||||
})
|
||||
.where('id = :id', { id: message.id })
|
||||
.execute();
|
||||
|
||||
if (room) {
|
||||
this.globalEventService.publishChatRoomStream(room.id, 'react', {
|
||||
messageId: message.id,
|
||||
user: await this.userEntityService.pack(userId),
|
||||
reaction,
|
||||
});
|
||||
} else {
|
||||
this.globalEventService.publishChatUserStream(message.fromUserId, message.toUserId!, 'react', {
|
||||
messageId: message.id,
|
||||
reaction,
|
||||
});
|
||||
this.globalEventService.publishChatUserStream(message.toUserId!, message.fromUserId, 'react', {
|
||||
messageId: message.id,
|
||||
reaction,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async unreact(messageId: MiChatMessage['id'], userId: MiUser['id'], reaction_: string) {
|
||||
let reaction;
|
||||
|
||||
const custom = reaction_.match(isCustomEmojiRegexp);
|
||||
|
||||
if (custom == null) {
|
||||
reaction = normalizeEmojiString(reaction_);
|
||||
} else { // 削除されたカスタム絵文字のリアクションを削除したいかもしれないので絵文字の存在チェックはする必要なし
|
||||
const name = custom[1];
|
||||
reaction = `:${name}:`;
|
||||
}
|
||||
|
||||
// NOTE: 自分のリアクションを(あれば)削除するだけなので諸々の権限チェックは必要なし
|
||||
|
||||
const message = await this.chatMessagesRepository.findOneByOrFail({ id: messageId });
|
||||
|
||||
const room = message.toRoomId ? await this.chatRoomsRepository.findOneByOrFail({ id: message.toRoomId }) : null;
|
||||
|
||||
await this.chatMessagesRepository.createQueryBuilder().update()
|
||||
.set({
|
||||
reactions: () => `array_remove("reactions", '${userId}/${reaction}')`,
|
||||
})
|
||||
.where('id = :id', { id: message.id })
|
||||
.execute();
|
||||
|
||||
// TODO: 実際に削除が行われたときのみイベントを発行する
|
||||
|
||||
if (room) {
|
||||
this.globalEventService.publishChatRoomStream(room.id, 'unreact', {
|
||||
messageId: message.id,
|
||||
user: await this.userEntityService.pack(userId),
|
||||
reaction,
|
||||
});
|
||||
} else {
|
||||
this.globalEventService.publishChatUserStream(message.fromUserId, message.toUserId!, 'unreact', {
|
||||
messageId: message.id,
|
||||
reaction,
|
||||
});
|
||||
this.globalEventService.publishChatUserStream(message.toUserId!, message.fromUserId, 'unreact', {
|
||||
messageId: message.id,
|
||||
reaction,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getMyMemberships(userId: MiUser['id'], limit: number, sinceId?: MiChatRoomMembership['id'] | null, untilId?: MiChatRoomMembership['id'] | null) {
|
||||
const query = this.queryService.makePaginationQuery(this.chatRoomMembershipsRepository.createQueryBuilder('membership'), sinceId, untilId)
|
||||
.andWhere('membership.userId = :userId', { userId });
|
||||
|
||||
const memberships = await query.take(limit).getMany();
|
||||
|
||||
return memberships;
|
||||
}
|
||||
}
|
|
@ -44,7 +44,6 @@ 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';
|
||||
|
@ -75,6 +74,7 @@ import { ClipService } from './ClipService.js';
|
|||
import { FeaturedService } from './FeaturedService.js';
|
||||
import { FanoutTimelineService } from './FanoutTimelineService.js';
|
||||
import { ChannelFollowingService } from './ChannelFollowingService.js';
|
||||
import { ChatService } from './ChatService.js';
|
||||
import { RegistryApiService } from './RegistryApiService.js';
|
||||
import { ReversiService } from './ReversiService.js';
|
||||
|
||||
|
@ -100,6 +100,7 @@ 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 { ChatEntityService } from './entities/ChatEntityService.js';
|
||||
import { ClipEntityService } from './entities/ClipEntityService.js';
|
||||
import { DriveFileEntityService } from './entities/DriveFileEntityService.js';
|
||||
import { DriveFolderEntityService } from './entities/DriveFolderEntityService.js';
|
||||
|
@ -184,7 +185,6 @@ const $ModerationLogService: Provider = { provide: 'ModerationLogService', useEx
|
|||
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 $SystemAccountService: Provider = { provide: 'SystemAccountService', useExisting: SystemAccountService };
|
||||
|
@ -221,6 +221,7 @@ const $FeaturedService: Provider = { provide: 'FeaturedService', useExisting: Fe
|
|||
const $FanoutTimelineService: Provider = { provide: 'FanoutTimelineService', useExisting: FanoutTimelineService };
|
||||
const $FanoutTimelineEndpointService: Provider = { provide: 'FanoutTimelineEndpointService', useExisting: FanoutTimelineEndpointService };
|
||||
const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService };
|
||||
const $ChatService: Provider = { provide: 'ChatService', useExisting: ChatService };
|
||||
const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService };
|
||||
const $ReversiService: Provider = { provide: 'ReversiService', useExisting: ReversiService };
|
||||
|
||||
|
@ -247,6 +248,7 @@ const $AppEntityService: Provider = { provide: 'AppEntityService', useExisting:
|
|||
const $AuthSessionEntityService: Provider = { provide: 'AuthSessionEntityService', useExisting: AuthSessionEntityService };
|
||||
const $BlockingEntityService: Provider = { provide: 'BlockingEntityService', useExisting: BlockingEntityService };
|
||||
const $ChannelEntityService: Provider = { provide: 'ChannelEntityService', useExisting: ChannelEntityService };
|
||||
const $ChatEntityService: Provider = { provide: 'ChatEntityService', useExisting: ChatEntityService };
|
||||
const $ClipEntityService: Provider = { provide: 'ClipEntityService', useExisting: ClipEntityService };
|
||||
const $DriveFileEntityService: Provider = { provide: 'DriveFileEntityService', useExisting: DriveFileEntityService };
|
||||
const $DriveFolderEntityService: Provider = { provide: 'DriveFolderEntityService', useExisting: DriveFolderEntityService };
|
||||
|
@ -333,7 +335,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
NoteCreateService,
|
||||
NoteDeleteService,
|
||||
NotePiningService,
|
||||
NoteReadService,
|
||||
NotificationService,
|
||||
PollService,
|
||||
SystemAccountService,
|
||||
|
@ -370,6 +371,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
FanoutTimelineService,
|
||||
FanoutTimelineEndpointService,
|
||||
ChannelFollowingService,
|
||||
ChatService,
|
||||
RegistryApiService,
|
||||
ReversiService,
|
||||
|
||||
|
@ -396,6 +398,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
AuthSessionEntityService,
|
||||
BlockingEntityService,
|
||||
ChannelEntityService,
|
||||
ChatEntityService,
|
||||
ClipEntityService,
|
||||
DriveFileEntityService,
|
||||
DriveFolderEntityService,
|
||||
|
@ -478,7 +481,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$NoteCreateService,
|
||||
$NoteDeleteService,
|
||||
$NotePiningService,
|
||||
$NoteReadService,
|
||||
$NotificationService,
|
||||
$PollService,
|
||||
$SystemAccountService,
|
||||
|
@ -515,6 +517,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$FanoutTimelineService,
|
||||
$FanoutTimelineEndpointService,
|
||||
$ChannelFollowingService,
|
||||
$ChatService,
|
||||
$RegistryApiService,
|
||||
$ReversiService,
|
||||
|
||||
|
@ -541,6 +544,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$AuthSessionEntityService,
|
||||
$BlockingEntityService,
|
||||
$ChannelEntityService,
|
||||
$ChatEntityService,
|
||||
$ClipEntityService,
|
||||
$DriveFileEntityService,
|
||||
$DriveFolderEntityService,
|
||||
|
@ -624,7 +628,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
NoteCreateService,
|
||||
NoteDeleteService,
|
||||
NotePiningService,
|
||||
NoteReadService,
|
||||
NotificationService,
|
||||
PollService,
|
||||
SystemAccountService,
|
||||
|
@ -661,6 +664,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
FanoutTimelineService,
|
||||
FanoutTimelineEndpointService,
|
||||
ChannelFollowingService,
|
||||
ChatService,
|
||||
RegistryApiService,
|
||||
ReversiService,
|
||||
|
||||
|
@ -686,6 +690,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
AuthSessionEntityService,
|
||||
BlockingEntityService,
|
||||
ChannelEntityService,
|
||||
ChatEntityService,
|
||||
ClipEntityService,
|
||||
DriveFileEntityService,
|
||||
DriveFolderEntityService,
|
||||
|
@ -768,7 +773,6 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$NoteCreateService,
|
||||
$NoteDeleteService,
|
||||
$NotePiningService,
|
||||
$NoteReadService,
|
||||
$NotificationService,
|
||||
$PollService,
|
||||
$SystemAccountService,
|
||||
|
@ -804,6 +808,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$FanoutTimelineService,
|
||||
$FanoutTimelineEndpointService,
|
||||
$ChannelFollowingService,
|
||||
$ChatService,
|
||||
$RegistryApiService,
|
||||
$ReversiService,
|
||||
|
||||
|
@ -829,6 +834,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
|
|||
$AuthSessionEntityService,
|
||||
$BlockingEntityService,
|
||||
$ChannelEntityService,
|
||||
$ChatEntityService,
|
||||
$ClipEntityService,
|
||||
$DriveFileEntityService,
|
||||
$DriveFolderEntityService,
|
||||
|
|
|
@ -20,7 +20,7 @@ import type { MiPage } from '@/models/Page.js';
|
|||
import type { MiWebhook } from '@/models/Webhook.js';
|
||||
import type { MiSystemWebhook } from '@/models/SystemWebhook.js';
|
||||
import type { MiMeta } from '@/models/Meta.js';
|
||||
import { MiAvatarDecoration, MiReversiGame, MiRole, MiRoleAssignment } from '@/models/_.js';
|
||||
import { MiAvatarDecoration, MiChatMessage, MiChatRoom, MiReversiGame, MiRole, MiRoleAssignment } from '@/models/_.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
|
@ -72,12 +72,8 @@ export interface MainEventTypes {
|
|||
readAllNotifications: undefined;
|
||||
notificationFlushed: undefined;
|
||||
unreadNotification: Packed<'Notification'>;
|
||||
unreadMention: MiNote['id'];
|
||||
readAllUnreadMentions: undefined;
|
||||
unreadSpecifiedNote: MiNote['id'];
|
||||
readAllUnreadSpecifiedNotes: undefined;
|
||||
readAllAntennas: undefined;
|
||||
unreadAntenna: MiAntenna;
|
||||
newChatMessage: Packed<'ChatMessage'>;
|
||||
readAllAnnouncements: undefined;
|
||||
myTokenRegenerated: undefined;
|
||||
signin: {
|
||||
|
@ -163,6 +159,21 @@ export interface AdminEventTypes {
|
|||
};
|
||||
}
|
||||
|
||||
export interface ChatEventTypes {
|
||||
message: Packed<'ChatMessageLite'>;
|
||||
deleted: Packed<'ChatMessageLite'>['id'];
|
||||
react: {
|
||||
reaction: string;
|
||||
user?: Packed<'UserLite'>;
|
||||
messageId: MiChatMessage['id'];
|
||||
};
|
||||
unreact: {
|
||||
reaction: string;
|
||||
user?: Packed<'UserLite'>;
|
||||
messageId: MiChatMessage['id'];
|
||||
};
|
||||
}
|
||||
|
||||
export interface ReversiEventTypes {
|
||||
matched: {
|
||||
game: Packed<'ReversiGameDetailed'>;
|
||||
|
@ -202,7 +213,7 @@ export interface ReversiGameEventTypes {
|
|||
type Events<T extends object> = { [K in keyof T]: { type: K; body: T[K]; } };
|
||||
type EventUnionFromDictionary<
|
||||
T extends object,
|
||||
U = Events<T>
|
||||
U = Events<T>,
|
||||
> = U[keyof U];
|
||||
|
||||
type SerializedAll<T> = {
|
||||
|
@ -295,6 +306,14 @@ export type GlobalEvents = {
|
|||
name: 'notesStream';
|
||||
payload: Serialized<Packed<'Note'>>;
|
||||
};
|
||||
chatUser: {
|
||||
name: `chatUserStream:${MiUser['id']}-${MiUser['id']}`;
|
||||
payload: EventTypesToEventPayload<ChatEventTypes>;
|
||||
};
|
||||
chatRoom: {
|
||||
name: `chatRoomStream:${MiChatRoom['id']}`;
|
||||
payload: EventTypesToEventPayload<ChatEventTypes>;
|
||||
};
|
||||
reversi: {
|
||||
name: `reversiStream:${MiUser['id']}`;
|
||||
payload: EventTypesToEventPayload<ReversiEventTypes>;
|
||||
|
@ -393,6 +412,16 @@ export class GlobalEventService {
|
|||
this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public publishChatUserStream<K extends keyof ChatEventTypes>(fromUserId: MiUser['id'], toUserId: MiUser['id'], type: K, value?: ChatEventTypes[K]): void {
|
||||
this.publish(`chatUserStream:${fromUserId}-${toUserId}`, type, typeof value === 'undefined' ? null : value);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public publishChatRoomStream<K extends keyof ChatEventTypes>(toRoomId: MiChatRoom['id'], type: K, value?: ChatEventTypes[K]): void {
|
||||
this.publish(`chatRoomStream:${toRoomId}`, type, typeof value === 'undefined' ? null : value);
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public publishReversiStream<K extends keyof ReversiEventTypes>(userId: MiUser['id'], type: K, value?: ReversiEventTypes[K]): void {
|
||||
this.publish(`reversiStream:${userId}`, type, typeof value === 'undefined' ? null : value);
|
||||
|
|
|
@ -16,7 +16,7 @@ import type { Config } from '@/config.js';
|
|||
import { StatusError } from '@/misc/status-error.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
|
||||
import { assertActivityMatchesUrls, FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
|
||||
import { assertActivityMatchesUrl, FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
|
||||
import type { IObject } from '@/core/activitypub/type.js';
|
||||
import type { Response } from 'node-fetch';
|
||||
import type { URL } from 'node:url';
|
||||
|
@ -265,7 +265,7 @@ export class HttpRequestService {
|
|||
const finalUrl = res.url; // redirects may have been involved
|
||||
const activity = await res.json() as IObject;
|
||||
|
||||
assertActivityMatchesUrls(url, activity, [finalUrl], allowSoftfail);
|
||||
assertActivityMatchesUrl(url, activity, finalUrl, allowSoftfail);
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
|
|
@ -7,13 +7,13 @@ import { Inject, Injectable } from '@nestjs/common';
|
|||
import { ulid } from 'ulid';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { genAid, isSafeAidT, parseAid } from '@/misc/id/aid.js';
|
||||
import { genAidx, isSafeAidxT, parseAidx } from '@/misc/id/aidx.js';
|
||||
import { genMeid, isSafeMeidT, parseMeid } from '@/misc/id/meid.js';
|
||||
import { genMeidg, isSafeMeidgT, parseMeidg } from '@/misc/id/meidg.js';
|
||||
import { genObjectId, isSafeObjectIdT, parseObjectId } from '@/misc/id/object-id.js';
|
||||
import { genAid, isSafeAidT, parseAid, parseAidFull } from '@/misc/id/aid.js';
|
||||
import { genAidx, isSafeAidxT, parseAidx, parseAidxFull } from '@/misc/id/aidx.js';
|
||||
import { genMeid, isSafeMeidT, parseMeid, parseMeidFull } from '@/misc/id/meid.js';
|
||||
import { genMeidg, isSafeMeidgT, parseMeidg, parseMeidgFull } from '@/misc/id/meidg.js';
|
||||
import { genObjectId, isSafeObjectIdT, parseObjectId, parseObjectIdFull } from '@/misc/id/object-id.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { parseUlid } from '@/misc/id/ulid.js';
|
||||
import { parseUlid, parseUlidFull } from '@/misc/id/ulid.js';
|
||||
|
||||
@Injectable()
|
||||
export class IdService {
|
||||
|
@ -70,4 +70,18 @@ export class IdService {
|
|||
default: throw new Error('unrecognized id generation method');
|
||||
}
|
||||
}
|
||||
|
||||
// Note: additional is at most 64 bits
|
||||
@bindThis
|
||||
public parseFull(id: string): { date: number; additional: bigint; } {
|
||||
switch (this.method) {
|
||||
case 'aid': return parseAidFull(id);
|
||||
case 'aidx': return parseAidxFull(id);
|
||||
case 'objectid': return parseObjectIdFull(id);
|
||||
case 'meid': return parseMeidFull(id);
|
||||
case 'meidg': return parseMeidgFull(id);
|
||||
case 'ulid': return parseUlidFull(id);
|
||||
default: throw new Error('unrecognized id generation method');
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -42,7 +42,6 @@ import { NoteEntityService } from '@/core/entities/NoteEntityService.js';
|
|||
import { UserEntityService } from '@/core/entities/UserEntityService.js';
|
||||
import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
|
||||
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
|
||||
import { NoteReadService } from '@/core/NoteReadService.js';
|
||||
import { RemoteUserResolveService } from '@/core/RemoteUserResolveService.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { DB_MAX_NOTE_TEXT_LENGTH } from '@/const.js';
|
||||
|
@ -199,7 +198,6 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||
private globalEventService: GlobalEventService,
|
||||
private queueService: QueueService,
|
||||
private fanoutTimelineService: FanoutTimelineService,
|
||||
private noteReadService: NoteReadService,
|
||||
private notificationService: NotificationService,
|
||||
private relayService: RelayService,
|
||||
private federatedInstanceService: FederatedInstanceService,
|
||||
|
@ -534,7 +532,10 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||
|
||||
this.pushToTl(note, user);
|
||||
|
||||
this.antennaService.addNoteToAntennas(note, user);
|
||||
this.antennaService.addNoteToAntennas({
|
||||
...note,
|
||||
channel: data.channel ?? null,
|
||||
}, user);
|
||||
|
||||
if (data.reply) {
|
||||
this.saveReply(data.reply, note);
|
||||
|
@ -582,31 +583,6 @@ export class NoteCreateService implements OnApplicationShutdown {
|
|||
if (!silent) {
|
||||
if (this.userEntityService.isLocalUser(user)) this.activeUsersChart.write(user);
|
||||
|
||||
// 未読通知を作成
|
||||
if (data.visibility === 'specified') {
|
||||
if (data.visibleUsers == null) throw new Error('invalid param');
|
||||
|
||||
for (const u of data.visibleUsers) {
|
||||
// ローカルユーザーのみ
|
||||
if (!this.userEntityService.isLocalUser(u)) continue;
|
||||
|
||||
this.noteReadService.insertNoteUnread(u.id, note, {
|
||||
isSpecified: true,
|
||||
isMentioned: false,
|
||||
});
|
||||
}
|
||||
} else {
|
||||
for (const u of mentionedUsers) {
|
||||
// ローカルユーザーのみ
|
||||
if (!this.userEntityService.isLocalUser(u)) continue;
|
||||
|
||||
this.noteReadService.insertNoteUnread(u.id, note, {
|
||||
isSpecified: false,
|
||||
isMentioned: true,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
// Pack the note
|
||||
const noteObj = await this.noteEntityService.pack(note, null, { skipHide: true, withReactionAndUserPairCache: true });
|
||||
|
||||
|
|
|
@ -1,143 +0,0 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { setTimeout } from 'node:timers/promises';
|
||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||
import { In } from 'typeorm';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import type { MiNote } from '@/models/Note.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { GlobalEventService } from '@/core/GlobalEventService.js';
|
||||
import type { NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository } from '@/models/_.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { trackPromise } from '@/misc/promise-tracker.js';
|
||||
|
||||
@Injectable()
|
||||
export class NoteReadService implements OnApplicationShutdown {
|
||||
#shutdownController = new AbortController();
|
||||
|
||||
constructor(
|
||||
@Inject(DI.noteUnreadsRepository)
|
||||
private noteUnreadsRepository: NoteUnreadsRepository,
|
||||
|
||||
@Inject(DI.mutingsRepository)
|
||||
private mutingsRepository: MutingsRepository,
|
||||
|
||||
@Inject(DI.noteThreadMutingsRepository)
|
||||
private noteThreadMutingsRepository: NoteThreadMutingsRepository,
|
||||
|
||||
private idService: IdService,
|
||||
private globalEventService: GlobalEventService,
|
||||
) {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async insertNoteUnread(userId: MiUser['id'], note: MiNote, params: {
|
||||
// NOTE: isSpecifiedがtrueならisMentionedは必ずfalse
|
||||
isSpecified: boolean;
|
||||
isMentioned: boolean;
|
||||
}): Promise<void> {
|
||||
//#region ミュートしているなら無視
|
||||
const mute = await this.mutingsRepository.findBy({
|
||||
muterId: userId,
|
||||
});
|
||||
if (mute.map(m => m.muteeId).includes(note.userId)) return;
|
||||
//#endregion
|
||||
|
||||
// スレッドミュート
|
||||
const isThreadMuted = await this.noteThreadMutingsRepository.exists({
|
||||
where: {
|
||||
userId: userId,
|
||||
threadId: note.threadId ?? note.id,
|
||||
},
|
||||
});
|
||||
if (isThreadMuted) return;
|
||||
|
||||
const unread = {
|
||||
id: this.idService.gen(),
|
||||
noteId: note.id,
|
||||
userId: userId,
|
||||
isSpecified: params.isSpecified,
|
||||
isMentioned: params.isMentioned,
|
||||
noteUserId: note.userId,
|
||||
};
|
||||
|
||||
await this.noteUnreadsRepository.insert(unread);
|
||||
|
||||
// 2秒経っても既読にならなかったら「未読の投稿がありますよ」イベントを発行する
|
||||
setTimeout(2000, 'unread note', { signal: this.#shutdownController.signal }).then(async () => {
|
||||
const exist = await this.noteUnreadsRepository.exists({ where: { id: unread.id } });
|
||||
|
||||
if (!exist) return;
|
||||
|
||||
if (params.isMentioned) {
|
||||
this.globalEventService.publishMainStream(userId, 'unreadMention', note.id);
|
||||
}
|
||||
if (params.isSpecified) {
|
||||
this.globalEventService.publishMainStream(userId, 'unreadSpecifiedNote', note.id);
|
||||
}
|
||||
}, () => { /* aborted, ignore it */ });
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async read(
|
||||
userId: MiUser['id'],
|
||||
notes: (MiNote | Packed<'Note'>)[],
|
||||
): Promise<void> {
|
||||
if (notes.length === 0) return;
|
||||
|
||||
const noteIds = new Set<MiNote['id']>();
|
||||
|
||||
for (const note of notes) {
|
||||
if (note.mentions && note.mentions.includes(userId)) {
|
||||
noteIds.add(note.id);
|
||||
} else if (note.visibleUserIds && note.visibleUserIds.includes(userId)) {
|
||||
noteIds.add(note.id);
|
||||
}
|
||||
}
|
||||
|
||||
if (noteIds.size === 0) return;
|
||||
|
||||
// Remove the record
|
||||
await this.noteUnreadsRepository.delete({
|
||||
userId: userId,
|
||||
noteId: In(Array.from(noteIds)),
|
||||
});
|
||||
|
||||
// TODO: ↓まとめてクエリしたい
|
||||
|
||||
trackPromise(this.noteUnreadsRepository.countBy({
|
||||
userId: userId,
|
||||
isMentioned: true,
|
||||
}).then(mentionsCount => {
|
||||
if (mentionsCount === 0) {
|
||||
// 全て既読になったイベントを発行
|
||||
this.globalEventService.publishMainStream(userId, 'readAllUnreadMentions');
|
||||
}
|
||||
}));
|
||||
|
||||
trackPromise(this.noteUnreadsRepository.countBy({
|
||||
userId: userId,
|
||||
isSpecified: true,
|
||||
}).then(specifiedCount => {
|
||||
if (specifiedCount === 0) {
|
||||
// 全て既読になったイベントを発行
|
||||
this.globalEventService.publishMainStream(userId, 'readAllUnreadSpecifiedNotes');
|
||||
}
|
||||
}));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public dispose(): void {
|
||||
this.#shutdownController.abort();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public onApplicationShutdown(signal?: string | undefined): void {
|
||||
this.dispose();
|
||||
}
|
||||
}
|
|
@ -7,6 +7,7 @@ import { setTimeout } from 'node:timers/promises';
|
|||
import * as Redis from 'ioredis';
|
||||
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
|
||||
import { In } from 'typeorm';
|
||||
import { ReplyError } from 'ioredis';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { UsersRepository } from '@/models/_.js';
|
||||
import type { MiUser } from '@/models/User.js';
|
||||
|
@ -19,7 +20,7 @@ import { IdService } from '@/core/IdService.js';
|
|||
import { CacheService } from '@/core/CacheService.js';
|
||||
import type { Config } from '@/config.js';
|
||||
import { UserListService } from '@/core/UserListService.js';
|
||||
import type { FilterUnionByProperty } from '@/types.js';
|
||||
import { FilterUnionByProperty, groupedNotificationTypes, obsoleteNotificationTypes } from '@/types.js';
|
||||
import { trackPromise } from '@/misc/promise-tracker.js';
|
||||
|
||||
@Injectable()
|
||||
|
@ -145,21 +146,36 @@ export class NotificationService implements OnApplicationShutdown {
|
|||
}
|
||||
}
|
||||
|
||||
const notification = {
|
||||
id: this.idService.gen(),
|
||||
createdAt: new Date(),
|
||||
type: type,
|
||||
...(notifierId ? {
|
||||
notifierId,
|
||||
} : {}),
|
||||
...data,
|
||||
} as any as FilterUnionByProperty<MiNotification, 'type', T>;
|
||||
const createdAt = new Date();
|
||||
let notification: FilterUnionByProperty<MiNotification, 'type', T>;
|
||||
let redisId: string;
|
||||
|
||||
const redisIdPromise = this.redisClient.xadd(
|
||||
`notificationTimeline:${notifieeId}`,
|
||||
'MAXLEN', '~', this.config.perUserNotificationsMaxCount.toString(),
|
||||
'*',
|
||||
'data', JSON.stringify(notification));
|
||||
do {
|
||||
notification = {
|
||||
id: this.idService.gen(),
|
||||
createdAt,
|
||||
type: type,
|
||||
...(notifierId ? {
|
||||
notifierId,
|
||||
} : {}),
|
||||
...data,
|
||||
} as unknown as FilterUnionByProperty<MiNotification, 'type', T>;
|
||||
|
||||
try {
|
||||
redisId = (await this.redisClient.xadd(
|
||||
`notificationTimeline:${notifieeId}`,
|
||||
'MAXLEN', '~', this.config.perUserNotificationsMaxCount.toString(),
|
||||
this.toXListId(notification.id),
|
||||
'data', JSON.stringify(notification)))!;
|
||||
} catch (e) {
|
||||
// The ID specified in XADD is equal or smaller than the target stream top item で失敗することがあるのでリトライ
|
||||
if (e instanceof ReplyError) continue;
|
||||
throw e;
|
||||
}
|
||||
|
||||
break;
|
||||
// eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
|
||||
} while (true);
|
||||
|
||||
const packed = await this.notificationEntityService.pack(notification, notifieeId, {});
|
||||
|
||||
|
@ -173,7 +189,7 @@ export class NotificationService implements OnApplicationShutdown {
|
|||
const interval = notification.type === 'test' ? 0 : 2000;
|
||||
setTimeout(interval, 'unread notification', { signal: this.#shutdownController.signal }).then(async () => {
|
||||
const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${notifieeId}`);
|
||||
if (latestReadNotificationId && (latestReadNotificationId >= (await redisIdPromise)!)) return;
|
||||
if (latestReadNotificationId && (latestReadNotificationId >= redisId)) return;
|
||||
|
||||
this.globalEventService.publishMainStream(notifieeId, 'unreadNotification', packed);
|
||||
this.pushNotificationService.pushNotification(notifieeId, 'notification', packed);
|
||||
|
@ -228,6 +244,79 @@ export class NotificationService implements OnApplicationShutdown {
|
|||
this.#shutdownController.abort();
|
||||
}
|
||||
|
||||
private toXListId(id: string): string {
|
||||
const { date, additional } = this.idService.parseFull(id);
|
||||
return date.toString() + '-' + additional.toString();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async getNotifications(
|
||||
userId: MiUser['id'],
|
||||
{
|
||||
sinceId,
|
||||
untilId,
|
||||
limit = 20,
|
||||
includeTypes,
|
||||
excludeTypes,
|
||||
}: {
|
||||
sinceId?: string,
|
||||
untilId?: string,
|
||||
limit?: number,
|
||||
// any extra types are allowed, those are no-op
|
||||
includeTypes?: (MiNotification['type'] | string)[],
|
||||
excludeTypes?: (MiNotification['type'] | string)[],
|
||||
},
|
||||
): Promise<MiNotification[]> {
|
||||
let sinceTime = sinceId ? this.toXListId(sinceId) : null;
|
||||
let untilTime = untilId ? this.toXListId(untilId) : null;
|
||||
|
||||
let notifications: MiNotification[];
|
||||
for (;;) {
|
||||
let notificationsRes: [id: string, fields: string[]][];
|
||||
|
||||
// sinceidのみの場合は古い順、そうでない場合は新しい順。 QueryService.makePaginationQueryも参照
|
||||
if (sinceTime && !untilTime) {
|
||||
notificationsRes = await this.redisClient.xrange(
|
||||
`notificationTimeline:${userId}`,
|
||||
'(' + sinceTime,
|
||||
'+',
|
||||
'COUNT', limit);
|
||||
} else {
|
||||
notificationsRes = await this.redisClient.xrevrange(
|
||||
`notificationTimeline:${userId}`,
|
||||
untilTime ? '(' + untilTime : '+',
|
||||
sinceTime ? '(' + sinceTime : '-',
|
||||
'COUNT', limit);
|
||||
}
|
||||
|
||||
if (notificationsRes.length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
notifications = notificationsRes.map(x => JSON.parse(x[1][1])) as MiNotification[];
|
||||
|
||||
if (includeTypes && includeTypes.length > 0) {
|
||||
notifications = notifications.filter(notification => includeTypes.includes(notification.type));
|
||||
} else if (excludeTypes && excludeTypes.length > 0) {
|
||||
notifications = notifications.filter(notification => !excludeTypes.includes(notification.type));
|
||||
}
|
||||
|
||||
if (notifications.length !== 0) {
|
||||
// 通知が1件以上ある場合は返す
|
||||
break;
|
||||
}
|
||||
|
||||
// フィルタしたことで通知が0件になった場合、次のページを取得する
|
||||
if (sinceId && !untilId) {
|
||||
sinceTime = notificationsRes[notificationsRes.length - 1][0];
|
||||
} else {
|
||||
untilTime = notificationsRes[notificationsRes.length - 1][0];
|
||||
}
|
||||
}
|
||||
|
||||
return notifications;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public onApplicationShutdown(signal?: string | undefined): void {
|
||||
this.dispose();
|
||||
|
|
|
@ -69,7 +69,7 @@ export class QueryService {
|
|||
|
||||
// ここでいうBlockedは被Blockedの意
|
||||
@bindThis
|
||||
public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
|
||||
public generateBlockedUserQueryForNotes(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
|
||||
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
|
||||
.select('blocking.blockerId')
|
||||
.where('blocking.blockeeId = :blockeeId', { blockeeId: me.id });
|
||||
|
@ -127,7 +127,7 @@ export class QueryService {
|
|||
}
|
||||
|
||||
@bindThis
|
||||
public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void {
|
||||
public generateMutedUserQueryForNotes(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }, exclude?: { id: MiUser['id'] }): void {
|
||||
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
|
||||
.select('muting.muteeId')
|
||||
.where('muting.muterId = :muterId', { muterId: me.id });
|
||||
|
|
|
@ -63,6 +63,7 @@ export type RolePolicies = {
|
|||
canImportFollowing: boolean;
|
||||
canImportMuting: boolean;
|
||||
canImportUserLists: boolean;
|
||||
chatAvailability: 'available' | 'readonly' | 'unavailable';
|
||||
};
|
||||
|
||||
export const DEFAULT_POLICIES: RolePolicies = {
|
||||
|
@ -97,6 +98,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
|
|||
canImportFollowing: true,
|
||||
canImportMuting: true,
|
||||
canImportUserLists: true,
|
||||
chatAvailability: 'available',
|
||||
};
|
||||
|
||||
@Injectable()
|
||||
|
@ -368,6 +370,12 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
|||
return aggregate(policies.map(policy => policy.useDefault ? basePolicies[name] : policy.value));
|
||||
}
|
||||
|
||||
function aggregateChatAvailability(vs: RolePolicies['chatAvailability'][]) {
|
||||
if (vs.some(v => v === 'available')) return 'available';
|
||||
if (vs.some(v => v === 'readonly')) return 'readonly';
|
||||
return 'unavailable';
|
||||
}
|
||||
|
||||
return {
|
||||
gtlAvailable: calc('gtlAvailable', vs => vs.some(v => v === true)),
|
||||
ltlAvailable: calc('ltlAvailable', vs => vs.some(v => v === true)),
|
||||
|
@ -400,6 +408,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
|||
canImportFollowing: calc('canImportFollowing', vs => vs.some(v => v === true)),
|
||||
canImportMuting: calc('canImportMuting', vs => vs.some(v => v === true)),
|
||||
canImportUserLists: calc('canImportUserLists', vs => vs.some(v => v === true)),
|
||||
chatAvailability: calc('chatAvailability', aggregateChatAvailability),
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -627,6 +636,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
|
|||
isModerator: values.isModerator,
|
||||
isExplorable: values.isExplorable,
|
||||
asBadge: values.asBadge,
|
||||
preserveAssignmentOnMoveAccount: values.preserveAssignmentOnMoveAccount,
|
||||
canEditMembersByModerator: values.canEditMembersByModerator,
|
||||
displayOrder: values.displayOrder,
|
||||
policies: values.policies,
|
||||
|
|
|
@ -234,8 +234,8 @@ export class SearchService {
|
|||
}
|
||||
|
||||
this.queryService.generateVisibilityQuery(query, me);
|
||||
if (me) this.queryService.generateMutedUserQuery(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQuery(query, me);
|
||||
if (me) this.queryService.generateMutedUserQueryForNotes(query, me);
|
||||
if (me) this.queryService.generateBlockedUserQueryForNotes(query, me);
|
||||
|
||||
return query.limit(pagination.limit).getMany();
|
||||
}
|
||||
|
|
|
@ -5,7 +5,7 @@
|
|||
|
||||
import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
|
||||
import { ModuleRef } from '@nestjs/core';
|
||||
import { IsNull } from 'typeorm';
|
||||
import { Brackets, IsNull } from 'typeorm';
|
||||
import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/User.js';
|
||||
import { IdentifiableError } from '@/misc/identifiable-error.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
|
@ -736,4 +736,30 @@ export class UserFollowingService implements OnModuleInit {
|
|||
.where('following.followerId = :followerId', { followerId: userId })
|
||||
.getMany();
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public isFollowing(followerId: MiUser['id'], followeeId: MiUser['id']) {
|
||||
return this.followingsRepository.exists({
|
||||
where: {
|
||||
followerId,
|
||||
followeeId,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async isMutual(aUserId: MiUser['id'], bUserId: MiUser['id']) {
|
||||
const count = await this.followingsRepository.createQueryBuilder('following')
|
||||
.where(new Brackets(qb => {
|
||||
qb.where('following.followerId = :aUserId', { aUserId })
|
||||
.andWhere('following.followeeId = :bUserId', { bUserId });
|
||||
}))
|
||||
.orWhere(new Brackets(qb => {
|
||||
qb.where('following.followerId = :bUserId', { bUserId })
|
||||
.andWhere('following.followeeId = :aUserId', { aUserId });
|
||||
}))
|
||||
.getCount();
|
||||
|
||||
return count === 2;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,7 @@
|
|||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { Brackets, SelectQueryBuilder } from 'typeorm';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import { type FollowingsRepository, MiUser, type UsersRepository } from '@/models/_.js';
|
||||
import { type FollowingsRepository, MiUser, type MutingsRepository, type UserProfilesRepository, type UsersRepository } from '@/models/_.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
|
||||
import type { Config } from '@/config.js';
|
||||
|
@ -22,10 +22,19 @@ export class UserSearchService {
|
|||
constructor(
|
||||
@Inject(DI.config)
|
||||
private config: Config,
|
||||
|
||||
@Inject(DI.usersRepository)
|
||||
private usersRepository: UsersRepository,
|
||||
|
||||
@Inject(DI.userProfilesRepository)
|
||||
private userProfilesRepository: UserProfilesRepository,
|
||||
|
||||
@Inject(DI.followingsRepository)
|
||||
private followingsRepository: FollowingsRepository,
|
||||
|
||||
@Inject(DI.mutingsRepository)
|
||||
private mutingsRepository: MutingsRepository,
|
||||
|
||||
private userEntityService: UserEntityService,
|
||||
) {
|
||||
}
|
||||
|
@ -58,7 +67,7 @@ export class UserSearchService {
|
|||
* @see {@link UserSearchService#buildSearchUserNoLoginQueries}
|
||||
*/
|
||||
@bindThis
|
||||
public async search(
|
||||
public async searchByUsernameAndHost(
|
||||
params: {
|
||||
username?: string | null,
|
||||
host?: string | null,
|
||||
|
@ -202,4 +211,91 @@ export class UserSearchService {
|
|||
|
||||
return userQuery;
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async search(query: string, meId: MiUser['id'] | null, options: Partial<{
|
||||
limit: number;
|
||||
offset: number;
|
||||
origin: 'local' | 'remote' | 'combined';
|
||||
}> = {}) {
|
||||
const activeThreshold = new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)); // 30日
|
||||
|
||||
const isUsername = query.startsWith('@') && !query.includes(' ') && query.indexOf('@', 1) === -1;
|
||||
|
||||
let users: MiUser[] = [];
|
||||
|
||||
const mutingQuery = meId == null ? null : this.mutingsRepository.createQueryBuilder('muting')
|
||||
.select('muting.muteeId')
|
||||
.where('muting.muterId = :muterId', { muterId: meId });
|
||||
|
||||
const nameQuery = this.usersRepository.createQueryBuilder('user')
|
||||
.where(new Brackets(qb => {
|
||||
qb.where('user.name ILIKE :query', { query: '%' + sqlLikeEscape(query) + '%' });
|
||||
|
||||
if (isUsername) {
|
||||
qb.orWhere('user.usernameLower LIKE :username', { username: sqlLikeEscape(query.replace('@', '').toLowerCase()) + '%' });
|
||||
} else if (this.userEntityService.validateLocalUsername(query)) { // Also search username if it qualifies as username
|
||||
qb.orWhere('user.usernameLower LIKE :username', { username: '%' + sqlLikeEscape(query.toLowerCase()) + '%' });
|
||||
}
|
||||
}))
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('user.updatedAt IS NULL')
|
||||
.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
|
||||
}))
|
||||
.andWhere('user.isSuspended = FALSE');
|
||||
|
||||
if (mutingQuery) {
|
||||
nameQuery.andWhere(`user.id NOT IN (${mutingQuery.getQuery()})`);
|
||||
nameQuery.setParameters(mutingQuery.getParameters());
|
||||
}
|
||||
|
||||
if (options.origin === 'local') {
|
||||
nameQuery.andWhere('user.host IS NULL');
|
||||
} else if (options.origin === 'remote') {
|
||||
nameQuery.andWhere('user.host IS NOT NULL');
|
||||
}
|
||||
|
||||
users = await nameQuery
|
||||
.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
|
||||
.limit(options.limit)
|
||||
.offset(options.offset)
|
||||
.getMany();
|
||||
|
||||
if (users.length < (options.limit ?? 30)) {
|
||||
const profQuery = this.userProfilesRepository.createQueryBuilder('prof')
|
||||
.select('prof.userId')
|
||||
.where('prof.description ILIKE :query', { query: '%' + sqlLikeEscape(query) + '%' });
|
||||
|
||||
if (mutingQuery) {
|
||||
profQuery.andWhere(`prof.userId NOT IN (${mutingQuery.getQuery()})`);
|
||||
profQuery.setParameters(mutingQuery.getParameters());
|
||||
}
|
||||
|
||||
if (options.origin === 'local') {
|
||||
profQuery.andWhere('prof.userHost IS NULL');
|
||||
} else if (options.origin === 'remote') {
|
||||
profQuery.andWhere('prof.userHost IS NOT NULL');
|
||||
}
|
||||
|
||||
const userQuery = this.usersRepository.createQueryBuilder('user')
|
||||
.where(`user.id IN (${ profQuery.getQuery() })`)
|
||||
.andWhere(new Brackets(qb => {
|
||||
qb
|
||||
.where('user.updatedAt IS NULL')
|
||||
.orWhere('user.updatedAt > :activeThreshold', { activeThreshold: activeThreshold });
|
||||
}))
|
||||
.andWhere('user.isSuspended = FALSE')
|
||||
.setParameters(profQuery.getParameters());
|
||||
|
||||
users = users.concat(await userQuery
|
||||
.orderBy('user.updatedAt', 'DESC', 'NULLS LAST')
|
||||
.limit(options.limit)
|
||||
.offset(options.offset)
|
||||
.getMany(),
|
||||
);
|
||||
}
|
||||
|
||||
return users;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,42 +7,16 @@ import { Injectable } from '@nestjs/common';
|
|||
import { MiAbuseUserReport, MiNote, MiUser, MiWebhook } from '@/models/_.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { MiSystemWebhook, type SystemWebhookEventType } from '@/models/SystemWebhook.js';
|
||||
import { AbuseReportPayload, SystemWebhookPayload, SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { Packed } from '@/misc/json-schema.js';
|
||||
import { type AbuseReportPayload, SystemWebhookPayload, SystemWebhookService } from '@/core/SystemWebhookService.js';
|
||||
import { type Packed } from '@/misc/json-schema.js';
|
||||
import { type WebhookEventTypes } from '@/models/Webhook.js';
|
||||
import { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { type UserWebhookPayload, UserWebhookService } from '@/core/UserWebhookService.js';
|
||||
import { QueueService } from '@/core/QueueService.js';
|
||||
import { ModeratorInactivityRemainingTime } from '@/queue/processors/CheckModeratorsActivityProcessorService.js';
|
||||
|
||||
const oneDayMillis = 24 * 60 * 60 * 1000;
|
||||
|
||||
function generateAbuseReport(override?: Partial<MiAbuseUserReport>): AbuseReportPayload {
|
||||
const result: MiAbuseUserReport = {
|
||||
id: 'dummy-abuse-report1',
|
||||
targetUserId: 'dummy-target-user',
|
||||
targetUser: null,
|
||||
reporterId: 'dummy-reporter-user',
|
||||
reporter: null,
|
||||
assigneeId: null,
|
||||
assignee: null,
|
||||
resolved: false,
|
||||
forwarded: false,
|
||||
comment: 'This is a dummy report for testing purposes.',
|
||||
targetUserHost: null,
|
||||
reporterHost: null,
|
||||
resolvedAs: null,
|
||||
moderationNote: 'foo',
|
||||
...override,
|
||||
};
|
||||
|
||||
return {
|
||||
...result,
|
||||
targetUser: result.targetUser ? toPackedUserLite(result.targetUser) : null,
|
||||
reporter: result.reporter ? toPackedUserLite(result.reporter) : null,
|
||||
assignee: result.assignee ? toPackedUserLite(result.assignee) : null,
|
||||
};
|
||||
}
|
||||
|
||||
function generateDummyUser(override?: Partial<MiUser>): MiUser {
|
||||
return {
|
||||
id: 'dummy-user-1',
|
||||
|
@ -79,6 +53,7 @@ function generateDummyUser(override?: Partial<MiUser>): MiUser {
|
|||
requireSigninToViewContents: false,
|
||||
makeNotesFollowersOnlyBefore: null,
|
||||
makeNotesHiddenBefore: null,
|
||||
chatScope: 'mutual',
|
||||
emojis: [],
|
||||
score: 0,
|
||||
host: null,
|
||||
|
@ -134,124 +109,6 @@ function generateDummyNote(override?: Partial<MiNote>): MiNote {
|
|||
};
|
||||
}
|
||||
|
||||
function toPackedNote(note: MiNote, detail = true, override?: Packed<'Note'>): Packed<'Note'> {
|
||||
return {
|
||||
id: note.id,
|
||||
createdAt: new Date().toISOString(),
|
||||
deletedAt: null,
|
||||
text: note.text,
|
||||
cw: note.cw,
|
||||
userId: note.userId,
|
||||
user: toPackedUserLite(note.user ?? generateDummyUser()),
|
||||
replyId: note.replyId,
|
||||
renoteId: note.renoteId,
|
||||
isHidden: false,
|
||||
visibility: note.visibility,
|
||||
mentions: note.mentions,
|
||||
visibleUserIds: note.visibleUserIds,
|
||||
fileIds: note.fileIds,
|
||||
files: [],
|
||||
tags: note.tags,
|
||||
poll: null,
|
||||
emojis: note.emojis,
|
||||
channelId: note.channelId,
|
||||
channel: note.channel,
|
||||
localOnly: note.localOnly,
|
||||
reactionAcceptance: note.reactionAcceptance,
|
||||
reactionEmojis: {},
|
||||
reactions: {},
|
||||
reactionCount: 0,
|
||||
renoteCount: note.renoteCount,
|
||||
repliesCount: note.repliesCount,
|
||||
uri: note.uri ?? undefined,
|
||||
url: note.url ?? undefined,
|
||||
reactionAndUserPairCache: note.reactionAndUserPairCache,
|
||||
...(detail ? {
|
||||
clippedCount: note.clippedCount,
|
||||
reply: note.reply ? toPackedNote(note.reply, false) : null,
|
||||
renote: note.renote ? toPackedNote(note.renote, true) : null,
|
||||
myReaction: null,
|
||||
} : {}),
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
function toPackedUserLite(user: MiUser, override?: Packed<'UserLite'>): Packed<'UserLite'> {
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
host: user.host,
|
||||
avatarUrl: user.avatarUrl,
|
||||
avatarBlurhash: user.avatarBlurhash,
|
||||
avatarDecorations: user.avatarDecorations.map(it => ({
|
||||
id: it.id,
|
||||
angle: it.angle,
|
||||
flipH: it.flipH,
|
||||
url: 'https://example.com/dummy-image001.png',
|
||||
offsetX: it.offsetX,
|
||||
offsetY: it.offsetY,
|
||||
})),
|
||||
isBot: user.isBot,
|
||||
isCat: user.isCat,
|
||||
emojis: user.emojis,
|
||||
onlineStatus: 'active',
|
||||
badgeRoles: [],
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
function toPackedUserDetailedNotMe(user: MiUser, override?: Packed<'UserDetailedNotMe'>): Packed<'UserDetailedNotMe'> {
|
||||
return {
|
||||
...toPackedUserLite(user),
|
||||
url: null,
|
||||
uri: null,
|
||||
movedTo: null,
|
||||
alsoKnownAs: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: user.updatedAt?.toISOString() ?? null,
|
||||
lastFetchedAt: user.lastFetchedAt?.toISOString() ?? null,
|
||||
bannerUrl: user.bannerUrl,
|
||||
bannerBlurhash: user.bannerBlurhash,
|
||||
isLocked: user.isLocked,
|
||||
isSilenced: false,
|
||||
isSuspended: user.isSuspended,
|
||||
description: null,
|
||||
location: null,
|
||||
birthday: null,
|
||||
lang: null,
|
||||
fields: [],
|
||||
verifiedLinks: [],
|
||||
followersCount: user.followersCount,
|
||||
followingCount: user.followingCount,
|
||||
notesCount: user.notesCount,
|
||||
pinnedNoteIds: [],
|
||||
pinnedNotes: [],
|
||||
pinnedPageId: null,
|
||||
pinnedPage: null,
|
||||
publicReactions: true,
|
||||
followersVisibility: 'public',
|
||||
followingVisibility: 'public',
|
||||
twoFactorEnabled: false,
|
||||
usePasswordLessLogin: false,
|
||||
securityKeys: false,
|
||||
roles: [],
|
||||
memo: null,
|
||||
moderationNote: undefined,
|
||||
isFollowing: false,
|
||||
isFollowed: false,
|
||||
hasPendingFollowRequestFromYou: false,
|
||||
hasPendingFollowRequestToYou: false,
|
||||
isBlocking: false,
|
||||
isBlocked: false,
|
||||
isMuted: false,
|
||||
isRenoteMuted: false,
|
||||
notify: 'none',
|
||||
withReplies: true,
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
const dummyUser1 = generateDummyUser();
|
||||
const dummyUser2 = generateDummyUser({
|
||||
id: 'dummy-user-2',
|
||||
|
@ -284,6 +141,7 @@ export class WebhookTestService {
|
|||
};
|
||||
|
||||
constructor(
|
||||
private customEmojiService: CustomEmojiService,
|
||||
private userWebhookService: UserWebhookService,
|
||||
private systemWebhookService: SystemWebhookService,
|
||||
private queueService: QueueService,
|
||||
|
@ -354,31 +212,31 @@ export class WebhookTestService {
|
|||
|
||||
switch (params.type) {
|
||||
case 'note': {
|
||||
send('note', { note: toPackedNote(dummyNote1) });
|
||||
send('note', { note: await this.toPackedNote(dummyNote1) });
|
||||
break;
|
||||
}
|
||||
case 'reply': {
|
||||
send('reply', { note: toPackedNote(dummyReply1) });
|
||||
send('reply', { note: await this.toPackedNote(dummyReply1) });
|
||||
break;
|
||||
}
|
||||
case 'renote': {
|
||||
send('renote', { note: toPackedNote(dummyRenote1) });
|
||||
send('renote', { note: await this.toPackedNote(dummyRenote1) });
|
||||
break;
|
||||
}
|
||||
case 'mention': {
|
||||
send('mention', { note: toPackedNote(dummyMention1) });
|
||||
send('mention', { note: await this.toPackedNote(dummyMention1) });
|
||||
break;
|
||||
}
|
||||
case 'follow': {
|
||||
send('follow', { user: toPackedUserDetailedNotMe(dummyUser1) });
|
||||
send('follow', { user: await this.toPackedUserDetailedNotMe(dummyUser1) });
|
||||
break;
|
||||
}
|
||||
case 'followed': {
|
||||
send('followed', { user: toPackedUserLite(dummyUser2) });
|
||||
send('followed', { user: await this.toPackedUserLite(dummyUser2) });
|
||||
break;
|
||||
}
|
||||
case 'unfollow': {
|
||||
send('unfollow', { user: toPackedUserDetailedNotMe(dummyUser3) });
|
||||
send('unfollow', { user: await this.toPackedUserDetailedNotMe(dummyUser3) });
|
||||
break;
|
||||
}
|
||||
// まだ実装されていない (#9485)
|
||||
|
@ -427,7 +285,7 @@ export class WebhookTestService {
|
|||
|
||||
switch (params.type) {
|
||||
case 'abuseReport': {
|
||||
send('abuseReport', generateAbuseReport({
|
||||
send('abuseReport', await this.generateAbuseReport({
|
||||
targetUserId: dummyUser1.id,
|
||||
targetUser: dummyUser1,
|
||||
reporterId: dummyUser2.id,
|
||||
|
@ -436,7 +294,7 @@ export class WebhookTestService {
|
|||
break;
|
||||
}
|
||||
case 'abuseReportResolved': {
|
||||
send('abuseReportResolved', generateAbuseReport({
|
||||
send('abuseReportResolved', await this.generateAbuseReport({
|
||||
targetUserId: dummyUser1.id,
|
||||
targetUser: dummyUser1,
|
||||
reporterId: dummyUser2.id,
|
||||
|
@ -448,7 +306,7 @@ export class WebhookTestService {
|
|||
break;
|
||||
}
|
||||
case 'userCreated': {
|
||||
send('userCreated', toPackedUserLite(dummyUser1));
|
||||
send('userCreated', await this.toPackedUserLite(dummyUser1));
|
||||
break;
|
||||
}
|
||||
case 'inactiveModeratorsWarning': {
|
||||
|
@ -474,4 +332,155 @@ export class WebhookTestService {
|
|||
}
|
||||
}
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async generateAbuseReport(override?: Partial<MiAbuseUserReport>): Promise<AbuseReportPayload> {
|
||||
const result: MiAbuseUserReport = {
|
||||
id: 'dummy-abuse-report1',
|
||||
targetUserId: 'dummy-target-user',
|
||||
targetUser: null,
|
||||
reporterId: 'dummy-reporter-user',
|
||||
reporter: null,
|
||||
assigneeId: null,
|
||||
assignee: null,
|
||||
resolved: false,
|
||||
forwarded: false,
|
||||
comment: 'This is a dummy report for testing purposes.',
|
||||
targetUserHost: null,
|
||||
reporterHost: null,
|
||||
resolvedAs: null,
|
||||
moderationNote: 'foo',
|
||||
...override,
|
||||
};
|
||||
|
||||
return {
|
||||
...result,
|
||||
targetUser: result.targetUser ? await this.toPackedUserLite(result.targetUser) : null,
|
||||
reporter: result.reporter ? await this.toPackedUserLite(result.reporter) : null,
|
||||
assignee: result.assignee ? await this.toPackedUserLite(result.assignee) : null,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async toPackedNote(note: MiNote, detail = true, override?: Packed<'Note'>): Promise<Packed<'Note'>> {
|
||||
return {
|
||||
id: note.id,
|
||||
createdAt: new Date().toISOString(),
|
||||
deletedAt: null,
|
||||
text: note.text,
|
||||
cw: note.cw,
|
||||
userId: note.userId,
|
||||
user: await this.toPackedUserLite(note.user ?? generateDummyUser()),
|
||||
replyId: note.replyId,
|
||||
renoteId: note.renoteId,
|
||||
isHidden: false,
|
||||
visibility: note.visibility,
|
||||
mentions: note.mentions,
|
||||
visibleUserIds: note.visibleUserIds,
|
||||
fileIds: note.fileIds,
|
||||
files: [],
|
||||
tags: note.tags,
|
||||
poll: null,
|
||||
emojis: await this.customEmojiService.populateEmojis(note.emojis, note.userHost),
|
||||
channelId: note.channelId,
|
||||
channel: note.channel,
|
||||
localOnly: note.localOnly,
|
||||
reactionAcceptance: note.reactionAcceptance,
|
||||
reactionEmojis: {},
|
||||
reactions: {},
|
||||
reactionCount: 0,
|
||||
renoteCount: note.renoteCount,
|
||||
repliesCount: note.repliesCount,
|
||||
uri: note.uri ?? undefined,
|
||||
url: note.url ?? undefined,
|
||||
reactionAndUserPairCache: note.reactionAndUserPairCache,
|
||||
...(detail ? {
|
||||
clippedCount: note.clippedCount,
|
||||
reply: note.reply ? await this.toPackedNote(note.reply, false) : null,
|
||||
renote: note.renote ? await this.toPackedNote(note.renote, true) : null,
|
||||
myReaction: null,
|
||||
} : {}),
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async toPackedUserLite(user: MiUser, override?: Packed<'UserLite'>): Promise<Packed<'UserLite'>> {
|
||||
return {
|
||||
id: user.id,
|
||||
name: user.name,
|
||||
username: user.username,
|
||||
host: user.host,
|
||||
avatarUrl: user.avatarUrl,
|
||||
avatarBlurhash: user.avatarBlurhash,
|
||||
avatarDecorations: user.avatarDecorations.map(it => ({
|
||||
id: it.id,
|
||||
angle: it.angle,
|
||||
flipH: it.flipH,
|
||||
url: 'https://example.com/dummy-image001.png',
|
||||
offsetX: it.offsetX,
|
||||
offsetY: it.offsetY,
|
||||
})),
|
||||
isBot: user.isBot,
|
||||
isCat: user.isCat,
|
||||
emojis: await this.customEmojiService.populateEmojis(user.emojis, user.host),
|
||||
onlineStatus: 'active',
|
||||
badgeRoles: [],
|
||||
...override,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
private async toPackedUserDetailedNotMe(user: MiUser, override?: Packed<'UserDetailedNotMe'>): Promise<Packed<'UserDetailedNotMe'>> {
|
||||
return {
|
||||
...await this.toPackedUserLite(user),
|
||||
url: null,
|
||||
uri: null,
|
||||
movedTo: null,
|
||||
alsoKnownAs: [],
|
||||
createdAt: new Date().toISOString(),
|
||||
updatedAt: user.updatedAt?.toISOString() ?? null,
|
||||
lastFetchedAt: user.lastFetchedAt?.toISOString() ?? null,
|
||||
bannerUrl: user.bannerUrl,
|
||||
bannerBlurhash: user.bannerBlurhash,
|
||||
isLocked: user.isLocked,
|
||||
isSilenced: false,
|
||||
isSuspended: user.isSuspended,
|
||||
description: null,
|
||||
location: null,
|
||||
birthday: null,
|
||||
lang: null,
|
||||
fields: [],
|
||||
verifiedLinks: [],
|
||||
followersCount: user.followersCount,
|
||||
followingCount: user.followingCount,
|
||||
notesCount: user.notesCount,
|
||||
pinnedNoteIds: [],
|
||||
pinnedNotes: [],
|
||||
pinnedPageId: null,
|
||||
pinnedPage: null,
|
||||
publicReactions: true,
|
||||
followersVisibility: 'public',
|
||||
followingVisibility: 'public',
|
||||
chatScope: 'mutual',
|
||||
canChat: true,
|
||||
twoFactorEnabled: false,
|
||||
usePasswordLessLogin: false,
|
||||
securityKeys: false,
|
||||
roles: [],
|
||||
memo: null,
|
||||
moderationNote: undefined,
|
||||
isFollowing: false,
|
||||
isFollowed: false,
|
||||
hasPendingFollowRequestFromYou: false,
|
||||
hasPendingFollowRequestToYou: false,
|
||||
isBlocking: false,
|
||||
isBlocked: false,
|
||||
isMuted: false,
|
||||
isRenoteMuted: false,
|
||||
notify: 'none',
|
||||
withReplies: true,
|
||||
...override,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -499,11 +499,28 @@ export class ApRendererService {
|
|||
this.userProfilesRepository.findOneByOrFail({ userId: user.id }),
|
||||
]);
|
||||
|
||||
const tryRewriteUrl = (maybeUrl: string) => {
|
||||
const urlSafeRegex = /^(?:http[s]?:\/\/.)?(?:www\.)?[-a-zA-Z0-9@%._\+~#=]{2,256}\.[a-z]{2,6}\b(?:[-a-zA-Z0-9@:%_\+.~#?&\/\/=]*)/;
|
||||
try {
|
||||
const match = maybeUrl.match(urlSafeRegex);
|
||||
if (!match) {
|
||||
return maybeUrl;
|
||||
}
|
||||
const urlPart = match[0];
|
||||
const urlPartParsed = new URL(urlPart);
|
||||
const restPart = maybeUrl.slice(match[0].length);
|
||||
|
||||
return `<a href="${urlPartParsed.href}" rel="me nofollow noopener" target="_blank">${urlPart}</a>${restPart}`;
|
||||
} catch (e) {
|
||||
return maybeUrl;
|
||||
}
|
||||
};
|
||||
|
||||
const attachment = profile.fields.map(field => ({
|
||||
type: 'PropertyValue',
|
||||
name: field.name,
|
||||
value: (field.value.startsWith('http://') || field.value.startsWith('https://'))
|
||||
? `<a href="${new URL(field.value).href}" rel="me nofollow noopener" target="_blank">${new URL(field.value).href}</a>`
|
||||
? tryRewriteUrl(field.value)
|
||||
: field.value,
|
||||
}));
|
||||
|
||||
|
|
|
@ -17,7 +17,7 @@ import { LoggerService } from '@/core/LoggerService.js';
|
|||
import { bindThis } from '@/decorators.js';
|
||||
import type Logger from '@/logger.js';
|
||||
import { validateContentTypeSetAsActivityPub } from '@/core/activitypub/misc/validator.js';
|
||||
import { assertActivityMatchesUrls, FetchAllowSoftFailMask as FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
|
||||
import { assertActivityMatchesUrl, FetchAllowSoftFailMask as FetchAllowSoftFailMask } from '@/core/activitypub/misc/check-against-url.js';
|
||||
import type { IObject } from './type.js';
|
||||
|
||||
type Request = {
|
||||
|
@ -258,7 +258,7 @@ export class ApRequestService {
|
|||
const finalUrl = res.url; // redirects may have been involved
|
||||
const activity = await res.json() as IObject;
|
||||
|
||||
assertActivityMatchesUrls(url, activity, [finalUrl], allowSoftfail);
|
||||
assertActivityMatchesUrl(url, activity, finalUrl, allowSoftfail);
|
||||
|
||||
return activity;
|
||||
}
|
||||
|
|
|
@ -75,7 +75,7 @@ function normalizeSynonymousSubdomain(url: URL | string): URL {
|
|||
return new URL(urlParsed.toString().replace(host, normalizedHost));
|
||||
}
|
||||
|
||||
export function assertActivityMatchesUrls(requestUrl: string | URL, activity: IObject, candidateUrls: (string | URL)[], allowSoftfail: FetchAllowSoftFailMask): FetchAllowSoftFailMask {
|
||||
export function assertActivityMatchesUrl(requestUrl: string | URL, activity: IObject, finalUrl: string | URL, allowSoftfail: FetchAllowSoftFailMask): FetchAllowSoftFailMask {
|
||||
// must have a unique identifier to verify authority
|
||||
if (!activity.id) {
|
||||
throw new Error('bad Activity: missing id field');
|
||||
|
@ -95,26 +95,32 @@ export function assertActivityMatchesUrls(requestUrl: string | URL, activity: IO
|
|||
const requestUrlParsed = normalizeSynonymousSubdomain(requestUrl);
|
||||
const idParsed = normalizeSynonymousSubdomain(activity.id);
|
||||
|
||||
const candidateUrlsParsed = candidateUrls.map(it => normalizeSynonymousSubdomain(it));
|
||||
const finalUrlParsed = normalizeSynonymousSubdomain(finalUrl);
|
||||
|
||||
// mastodon sends activities with hash in the URL
|
||||
// currently it only happens with likes, deletes etc.
|
||||
// but object ID never has hash
|
||||
requestUrlParsed.hash = '';
|
||||
finalUrlParsed.hash = '';
|
||||
|
||||
const requestUrlSecure = requestUrlParsed.protocol === 'https:';
|
||||
const finalUrlSecure = candidateUrlsParsed.every(it => it.protocol === 'https:');
|
||||
const finalUrlSecure = finalUrlParsed.protocol === 'https:';
|
||||
if (requestUrlSecure && !finalUrlSecure) {
|
||||
throw new Error(`bad Activity: id(${activity.id}) is not allowed to have http:// in the url`);
|
||||
}
|
||||
|
||||
// Compare final URL to the ID
|
||||
if (!candidateUrlsParsed.some(it => it.href === idParsed.href)) {
|
||||
requireSoftfail(FetchAllowSoftFailMask.NonCanonicalId, `bad Activity: id(${activity.id}) does not match response url(${candidateUrlsParsed.map(it => it.toString())})`);
|
||||
if (finalUrlParsed.href !== idParsed.href) {
|
||||
requireSoftfail(FetchAllowSoftFailMask.NonCanonicalId, `bad Activity: id(${activity.id}) does not match response url(${finalUrlParsed.toString()})`);
|
||||
|
||||
// at lease host need to match exactly (ActivityPub requirement)
|
||||
if (!candidateUrlsParsed.some(it => idParsed.host === it.host)) {
|
||||
throw new Error(`bad Activity: id(${activity.id}) does not match response host(${candidateUrlsParsed.map(it => it.host)})`);
|
||||
if (idParsed.host !== finalUrlParsed.host) {
|
||||
throw new Error(`bad Activity: id(${activity.id}) does not match response host(${finalUrlParsed.host})`);
|
||||
}
|
||||
}
|
||||
|
||||
// Compare request URL to the ID
|
||||
if (!requestUrlParsed.href.includes(idParsed.href)) {
|
||||
if (requestUrlParsed.href !== idParsed.href) {
|
||||
requireSoftfail(FetchAllowSoftFailMask.NonCanonicalId, `bad Activity: id(${activity.id}) does not match request url(${requestUrlParsed.toString()})`);
|
||||
|
||||
// if cross-origin lookup is allowed, we can accept some variation between the original request URL to the final object ID (but not between the final URL and the object ID)
|
||||
|
|
|
@ -41,6 +41,7 @@ export class AntennaEntityService {
|
|||
excludeBots: antenna.excludeBots,
|
||||
withReplies: antenna.withReplies,
|
||||
withFile: antenna.withFile,
|
||||
excludeNotesInSensitiveChannel: antenna.excludeNotesInSensitiveChannel,
|
||||
isActive: antenna.isActive,
|
||||
hasUnreadNote: false, // TODO
|
||||
notify: false, // 後方互換性のため
|
||||
|
|
|
@ -0,0 +1,376 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
import { Inject, Injectable } from '@nestjs/common';
|
||||
import { DI } from '@/di-symbols.js';
|
||||
import type { MiUser, ChatMessagesRepository, MiChatMessage, ChatRoomsRepository, MiChatRoom, MiChatRoomInvitation, ChatRoomInvitationsRepository, MiChatRoomMembership, ChatRoomMembershipsRepository } from '@/models/_.js';
|
||||
import { awaitAll } from '@/misc/prelude/await-all.js';
|
||||
import type { Packed } from '@/misc/json-schema.js';
|
||||
import type { } from '@/models/Blocking.js';
|
||||
import { bindThis } from '@/decorators.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { UserEntityService } from './UserEntityService.js';
|
||||
import { DriveFileEntityService } from './DriveFileEntityService.js';
|
||||
import { In } from 'typeorm';
|
||||
|
||||
@Injectable()
|
||||
export class ChatEntityService {
|
||||
constructor(
|
||||
@Inject(DI.chatMessagesRepository)
|
||||
private chatMessagesRepository: ChatMessagesRepository,
|
||||
|
||||
@Inject(DI.chatRoomsRepository)
|
||||
private chatRoomsRepository: ChatRoomsRepository,
|
||||
|
||||
@Inject(DI.chatRoomInvitationsRepository)
|
||||
private chatRoomInvitationsRepository: ChatRoomInvitationsRepository,
|
||||
|
||||
@Inject(DI.chatRoomMembershipsRepository)
|
||||
private chatRoomMembershipsRepository: ChatRoomMembershipsRepository,
|
||||
|
||||
private userEntityService: UserEntityService,
|
||||
private driveFileEntityService: DriveFileEntityService,
|
||||
private idService: IdService,
|
||||
) {
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMessageDetailed(
|
||||
src: MiChatMessage['id'] | MiChatMessage,
|
||||
me?: { id: MiUser['id'] },
|
||||
options?: {
|
||||
_hint_?: {
|
||||
packedFiles?: Map<MiChatMessage['fileId'], Packed<'DriveFile'> | null>;
|
||||
packedUsers?: Map<MiChatMessage['id'], Packed<'UserLite'>>;
|
||||
packedRooms?: Map<MiChatMessage['toRoomId'], Packed<'ChatRoom'> | null>;
|
||||
};
|
||||
},
|
||||
): Promise<Packed<'ChatMessage'>> {
|
||||
const packedUsers = options?._hint_?.packedUsers;
|
||||
const packedFiles = options?._hint_?.packedFiles;
|
||||
const packedRooms = options?._hint_?.packedRooms;
|
||||
|
||||
const message = typeof src === 'object' ? src : await this.chatMessagesRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const reactions: { user: Packed<'UserLite'>; reaction: string; }[] = [];
|
||||
|
||||
for (const record of message.reactions) {
|
||||
const [userId, reaction] = record.split('/');
|
||||
reactions.push({
|
||||
user: packedUsers?.get(userId) ?? await this.userEntityService.pack(userId),
|
||||
reaction,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: message.id,
|
||||
createdAt: this.idService.parse(message.id).date.toISOString(),
|
||||
text: message.text,
|
||||
fromUserId: message.fromUserId,
|
||||
fromUser: packedUsers?.get(message.fromUserId) ?? await this.userEntityService.pack(message.fromUser ?? message.fromUserId, me),
|
||||
toUserId: message.toUserId,
|
||||
toUser: message.toUserId ? (packedUsers?.get(message.toUserId) ?? await this.userEntityService.pack(message.toUser ?? message.toUserId, me)) : undefined,
|
||||
toRoomId: message.toRoomId,
|
||||
toRoom: message.toRoomId ? (packedRooms?.get(message.toRoomId) ?? await this.packRoom(message.toRoom ?? message.toRoomId, me)) : undefined,
|
||||
fileId: message.fileId,
|
||||
file: message.fileId ? (packedFiles?.get(message.fileId) ?? await this.driveFileEntityService.pack(message.file ?? message.fileId)) : null,
|
||||
reactions,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMessagesDetailed(
|
||||
messages: MiChatMessage[],
|
||||
me: { id: MiUser['id'] },
|
||||
) {
|
||||
if (messages.length === 0) return [];
|
||||
|
||||
const excludeMe = (x: MiUser | string) => {
|
||||
if (typeof x === 'string') {
|
||||
return x !== me.id;
|
||||
} else {
|
||||
return x.id !== me.id;
|
||||
}
|
||||
};
|
||||
|
||||
const users = [
|
||||
...messages.map((m) => m.fromUser ?? m.fromUserId).filter(excludeMe),
|
||||
...messages.map((m) => m.toUser ?? m.toUserId).filter(x => x != null).filter(excludeMe),
|
||||
];
|
||||
|
||||
const reactedUserIds = messages.flatMap(x => x.reactions.map(r => r.split('/')[0]));
|
||||
|
||||
for (const reactedUserId of reactedUserIds) {
|
||||
if (!users.some(x => typeof x === 'string' ? x === reactedUserId : x.id === reactedUserId)) {
|
||||
users.push(reactedUserId);
|
||||
}
|
||||
}
|
||||
|
||||
const [packedUsers, packedFiles, packedRooms] = await Promise.all([
|
||||
this.userEntityService.packMany(users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u]))),
|
||||
this.driveFileEntityService.packMany(messages.map(m => m.file).filter(x => x != null))
|
||||
.then(files => new Map(files.map(f => [f.id, f]))),
|
||||
this.packRooms(messages.map(m => m.toRoom ?? m.toRoomId).filter(x => x != null), me)
|
||||
.then(rooms => new Map(rooms.map(r => [r.id, r]))),
|
||||
]);
|
||||
|
||||
return Promise.all(messages.map(message => this.packMessageDetailed(message, me, { _hint_: { packedUsers, packedFiles, packedRooms } })));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMessageLiteFor1on1(
|
||||
src: MiChatMessage['id'] | MiChatMessage,
|
||||
options?: {
|
||||
_hint_?: {
|
||||
packedFiles: Map<MiChatMessage['fileId'], Packed<'DriveFile'> | null>;
|
||||
};
|
||||
},
|
||||
): Promise<Packed<'ChatMessageLiteFor1on1'>> {
|
||||
const packedFiles = options?._hint_?.packedFiles;
|
||||
|
||||
const message = typeof src === 'object' ? src : await this.chatMessagesRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const reactions: { reaction: string; }[] = [];
|
||||
|
||||
for (const record of message.reactions) {
|
||||
const [userId, reaction] = record.split('/');
|
||||
reactions.push({
|
||||
reaction,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: message.id,
|
||||
createdAt: this.idService.parse(message.id).date.toISOString(),
|
||||
text: message.text,
|
||||
fromUserId: message.fromUserId,
|
||||
toUserId: message.toUserId!,
|
||||
fileId: message.fileId,
|
||||
file: message.fileId ? (packedFiles?.get(message.fileId) ?? await this.driveFileEntityService.pack(message.file ?? message.fileId)) : null,
|
||||
reactions,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMessagesLiteFor1on1(
|
||||
messages: MiChatMessage[],
|
||||
) {
|
||||
if (messages.length === 0) return [];
|
||||
|
||||
const [packedFiles] = await Promise.all([
|
||||
this.driveFileEntityService.packMany(messages.map(m => m.file).filter(x => x != null))
|
||||
.then(files => new Map(files.map(f => [f.id, f]))),
|
||||
]);
|
||||
|
||||
return Promise.all(messages.map(message => this.packMessageLiteFor1on1(message, { _hint_: { packedFiles } })));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMessageLiteForRoom(
|
||||
src: MiChatMessage['id'] | MiChatMessage,
|
||||
options?: {
|
||||
_hint_?: {
|
||||
packedFiles: Map<MiChatMessage['fileId'], Packed<'DriveFile'> | null>;
|
||||
packedUsers: Map<MiUser['id'], Packed<'UserLite'>>;
|
||||
};
|
||||
},
|
||||
): Promise<Packed<'ChatMessageLiteForRoom'>> {
|
||||
const packedFiles = options?._hint_?.packedFiles;
|
||||
const packedUsers = options?._hint_?.packedUsers;
|
||||
|
||||
const message = typeof src === 'object' ? src : await this.chatMessagesRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const reactions: { user: Packed<'UserLite'>; reaction: string; }[] = [];
|
||||
|
||||
for (const record of message.reactions) {
|
||||
const [userId, reaction] = record.split('/');
|
||||
reactions.push({
|
||||
user: packedUsers?.get(userId) ?? await this.userEntityService.pack(userId),
|
||||
reaction,
|
||||
});
|
||||
}
|
||||
|
||||
return {
|
||||
id: message.id,
|
||||
createdAt: this.idService.parse(message.id).date.toISOString(),
|
||||
text: message.text,
|
||||
fromUserId: message.fromUserId,
|
||||
fromUser: packedUsers?.get(message.fromUserId) ?? await this.userEntityService.pack(message.fromUser ?? message.fromUserId),
|
||||
toRoomId: message.toRoomId!,
|
||||
fileId: message.fileId,
|
||||
file: message.fileId ? (packedFiles?.get(message.fileId) ?? await this.driveFileEntityService.pack(message.file ?? message.fileId)) : null,
|
||||
reactions,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packMessagesLiteForRoom(
|
||||
messages: MiChatMessage[],
|
||||
) {
|
||||
if (messages.length === 0) return [];
|
||||
|
||||
const users = messages.map(x => x.fromUser ?? x.fromUserId);
|
||||
const reactedUserIds = messages.flatMap(x => x.reactions.map(r => r.split('/')[0]));
|
||||
|
||||
for (const reactedUserId of reactedUserIds) {
|
||||
if (!users.some(x => typeof x === 'string' ? x === reactedUserId : x.id === reactedUserId)) {
|
||||
users.push(reactedUserId);
|
||||
}
|
||||
}
|
||||
|
||||
const [packedUsers, packedFiles] = await Promise.all([
|
||||
this.userEntityService.packMany(users)
|
||||
.then(users => new Map(users.map(u => [u.id, u]))),
|
||||
this.driveFileEntityService.packMany(messages.map(m => m.file).filter(x => x != null))
|
||||
.then(files => new Map(files.map(f => [f.id, f]))),
|
||||
]);
|
||||
|
||||
return Promise.all(messages.map(message => this.packMessageLiteForRoom(message, { _hint_: { packedFiles, packedUsers } })));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packRoom(
|
||||
src: MiChatRoom['id'] | MiChatRoom,
|
||||
me?: { id: MiUser['id'] },
|
||||
options?: {
|
||||
_hint_?: {
|
||||
packedOwners: Map<MiChatRoom['id'], Packed<'UserLite'>>;
|
||||
memberships?: Map<MiChatRoom['id'], MiChatRoomMembership | null | undefined>;
|
||||
};
|
||||
},
|
||||
): Promise<Packed<'ChatRoom'>> {
|
||||
const room = typeof src === 'object' ? src : await this.chatRoomsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const membership = me && me.id !== room.ownerId ? (options?._hint_?.memberships?.get(room.id) ?? await this.chatRoomMembershipsRepository.findOneBy({ roomId: room.id, userId: me.id })) : null;
|
||||
|
||||
return {
|
||||
id: room.id,
|
||||
createdAt: this.idService.parse(room.id).date.toISOString(),
|
||||
name: room.name,
|
||||
description: room.description,
|
||||
ownerId: room.ownerId,
|
||||
owner: options?._hint_?.packedOwners.get(room.ownerId) ?? await this.userEntityService.pack(room.owner ?? room.ownerId, me),
|
||||
isMuted: membership != null ? membership.isMuted : false,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packRooms(
|
||||
rooms: (MiChatRoom | MiChatRoom['id'])[],
|
||||
me: { id: MiUser['id'] },
|
||||
) {
|
||||
if (rooms.length === 0) return [];
|
||||
|
||||
const _rooms = rooms.filter((room): room is MiChatRoom => typeof room !== 'string');
|
||||
if (_rooms.length !== rooms.length) {
|
||||
_rooms.push(
|
||||
...await this.chatRoomsRepository.find({
|
||||
where: {
|
||||
id: In(rooms.filter((room): room is string => typeof room === 'string')),
|
||||
},
|
||||
relations: ['owner'],
|
||||
}),
|
||||
);
|
||||
}
|
||||
|
||||
const owners = _rooms.map(x => x.owner ?? x.ownerId);
|
||||
|
||||
const [packedOwners, memberships] = await Promise.all([
|
||||
this.userEntityService.packMany(owners, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u]))),
|
||||
this.chatRoomMembershipsRepository.find({
|
||||
where: {
|
||||
roomId: In(_rooms.map(x => x.id)),
|
||||
userId: me.id,
|
||||
},
|
||||
}).then(memberships => new Map(_rooms.map(r => [r.id, memberships.find(m => m.roomId === r.id)]))),
|
||||
]);
|
||||
|
||||
return Promise.all(_rooms.map(room => this.packRoom(room, me, { _hint_: { packedOwners, memberships } })));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packRoomInvitation(
|
||||
src: MiChatRoomInvitation['id'] | MiChatRoomInvitation,
|
||||
me: { id: MiUser['id'] },
|
||||
options?: {
|
||||
_hint_?: {
|
||||
packedRooms: Map<MiChatRoomInvitation['roomId'], Packed<'ChatRoom'>>;
|
||||
packedUsers: Map<MiChatRoomInvitation['id'], Packed<'UserLite'>>;
|
||||
};
|
||||
},
|
||||
): Promise<Packed<'ChatRoomInvitation'>> {
|
||||
const invitation = typeof src === 'object' ? src : await this.chatRoomInvitationsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
return {
|
||||
id: invitation.id,
|
||||
createdAt: this.idService.parse(invitation.id).date.toISOString(),
|
||||
roomId: invitation.roomId,
|
||||
room: options?._hint_?.packedRooms.get(invitation.roomId) ?? await this.packRoom(invitation.room ?? invitation.roomId, me),
|
||||
userId: invitation.userId,
|
||||
user: options?._hint_?.packedUsers.get(invitation.userId) ?? await this.userEntityService.pack(invitation.user ?? invitation.userId, me),
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packRoomInvitations(
|
||||
invitations: MiChatRoomInvitation[],
|
||||
me: { id: MiUser['id'] },
|
||||
) {
|
||||
if (invitations.length === 0) return [];
|
||||
|
||||
return Promise.all(invitations.map(invitation => this.packRoomInvitation(invitation, me)));
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packRoomMembership(
|
||||
src: MiChatRoomMembership['id'] | MiChatRoomMembership,
|
||||
me: { id: MiUser['id'] },
|
||||
options?: {
|
||||
populateUser?: boolean;
|
||||
populateRoom?: boolean;
|
||||
_hint_?: {
|
||||
packedRooms: Map<MiChatRoomMembership['roomId'], Packed<'ChatRoom'>>;
|
||||
packedUsers: Map<MiChatRoomMembership['id'], Packed<'UserLite'>>;
|
||||
};
|
||||
},
|
||||
): Promise<Packed<'ChatRoomMembership'>> {
|
||||
const membership = typeof src === 'object' ? src : await this.chatRoomMembershipsRepository.findOneByOrFail({ id: src });
|
||||
|
||||
return {
|
||||
id: membership.id,
|
||||
createdAt: this.idService.parse(membership.id).date.toISOString(),
|
||||
userId: membership.userId,
|
||||
user: options?.populateUser ? (options._hint_?.packedUsers.get(membership.userId) ?? await this.userEntityService.pack(membership.user ?? membership.userId, me)) : undefined,
|
||||
roomId: membership.roomId,
|
||||
room: options?.populateRoom ? (options._hint_?.packedRooms.get(membership.roomId) ?? await this.packRoom(membership.room ?? membership.roomId, me)) : undefined,
|
||||
};
|
||||
}
|
||||
|
||||
@bindThis
|
||||
public async packRoomMemberships(
|
||||
memberships: MiChatRoomMembership[],
|
||||
me: { id: MiUser['id'] },
|
||||
options: {
|
||||
populateUser?: boolean;
|
||||
populateRoom?: boolean;
|
||||
} = {},
|
||||
) {
|
||||
if (memberships.length === 0) return [];
|
||||
|
||||
const users = memberships.map(x => x.user ?? x.userId);
|
||||
const rooms = memberships.map(x => x.room ?? x.roomId);
|
||||
|
||||
const [packedUsers, packedRooms] = await Promise.all([
|
||||
this.userEntityService.packMany(users, me)
|
||||
.then(users => new Map(users.map(u => [u.id, u]))),
|
||||
this.packRooms(rooms, me)
|
||||
.then(rooms => new Map(rooms.map(r => [r.id, r]))),
|
||||
]);
|
||||
|
||||
return Promise.all(memberships.map(membership => this.packRoomMembership(membership, me, { ...options, _hint_: { packedUsers, packedRooms } })));
|
||||
}
|
||||
}
|
|
@ -127,6 +127,7 @@ export class MetaEntityService {
|
|||
|
||||
policies: { ...DEFAULT_POLICIES, ...instance.policies },
|
||||
|
||||
sentryForFrontend: this.config.sentryForFrontend ?? null,
|
||||
mediaProxy: this.config.mediaProxy,
|
||||
enableUrlPreview: instance.urlPreviewEnabled,
|
||||
noteSearchableScope: (this.config.meilisearch == null || this.config.meilisearch.scope !== 'local') ? 'global' : 'local',
|
||||
|
|
|
@ -16,6 +16,7 @@ import { bindThis } from '@/decorators.js';
|
|||
import { FilterUnionByProperty, groupedNotificationTypes } from '@/types.js';
|
||||
import { CacheService } from '@/core/CacheService.js';
|
||||
import { RoleEntityService } from './RoleEntityService.js';
|
||||
import { ChatEntityService } from './ChatEntityService.js';
|
||||
import type { OnModuleInit } from '@nestjs/common';
|
||||
import type { UserEntityService } from './UserEntityService.js';
|
||||
import type { NoteEntityService } from './NoteEntityService.js';
|
||||
|
@ -27,6 +28,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
private userEntityService: UserEntityService;
|
||||
private noteEntityService: NoteEntityService;
|
||||
private roleEntityService: RoleEntityService;
|
||||
private chatEntityService: ChatEntityService;
|
||||
|
||||
constructor(
|
||||
private moduleRef: ModuleRef,
|
||||
|
@ -41,9 +43,6 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
private followRequestsRepository: FollowRequestsRepository,
|
||||
|
||||
private cacheService: CacheService,
|
||||
|
||||
//private userEntityService: UserEntityService,
|
||||
//private noteEntityService: NoteEntityService,
|
||||
) {
|
||||
}
|
||||
|
||||
|
@ -51,6 +50,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
this.userEntityService = this.moduleRef.get('UserEntityService');
|
||||
this.noteEntityService = this.moduleRef.get('NoteEntityService');
|
||||
this.roleEntityService = this.moduleRef.get('RoleEntityService');
|
||||
this.chatEntityService = this.moduleRef.get('ChatEntityService');
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -59,7 +59,6 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
async #packInternal <T extends MiNotification | MiGroupedNotification> (
|
||||
src: T,
|
||||
meId: MiUser['id'],
|
||||
|
||||
options: {
|
||||
checkValidNotifier?: boolean;
|
||||
},
|
||||
|
@ -92,7 +91,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
// if the user has been deleted, don't show this notification
|
||||
if (needsUser && !userIfNeed) return null;
|
||||
|
||||
// #region Grouped notifications
|
||||
//#region Grouped notifications
|
||||
if (notification.type === 'reaction:grouped') {
|
||||
const reactions = (await Promise.all(notification.reactions.map(async reaction => {
|
||||
const user = hint?.packedUsers != null
|
||||
|
@ -137,7 +136,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
users,
|
||||
});
|
||||
}
|
||||
// #endregion
|
||||
//#endregion
|
||||
|
||||
const needsRole = notification.type === 'roleAssigned';
|
||||
const role = needsRole ? await this.roleEntityService.pack(notification.roleId) : undefined;
|
||||
|
@ -146,6 +145,13 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
return null;
|
||||
}
|
||||
|
||||
const needsChatRoomInvitation = notification.type === 'chatRoomInvitationReceived';
|
||||
const chatRoomInvitation = needsChatRoomInvitation ? await this.chatEntityService.packRoomInvitation(notification.invitationId, { id: meId }).catch(() => null) : undefined;
|
||||
// if the invitation has been deleted, don't show this notification
|
||||
if (needsChatRoomInvitation && !chatRoomInvitation) {
|
||||
return null;
|
||||
}
|
||||
|
||||
return await awaitAll({
|
||||
id: notification.id,
|
||||
createdAt: new Date(notification.createdAt).toISOString(),
|
||||
|
@ -159,6 +165,9 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
...(notification.type === 'roleAssigned' ? {
|
||||
role: role,
|
||||
} : {}),
|
||||
...(notification.type === 'chatRoomInvitationReceived' ? {
|
||||
invitation: chatRoomInvitation,
|
||||
} : {}),
|
||||
...(notification.type === 'followRequestAccepted' ? {
|
||||
message: notification.message,
|
||||
} : {}),
|
||||
|
@ -236,7 +245,7 @@ export class NotificationEntityService implements OnModuleInit {
|
|||
public async pack(
|
||||
src: MiNotification | MiGroupedNotification,
|
||||
meId: MiUser['id'],
|
||||
|
||||
|
||||
options: {
|
||||
checkValidNotifier?: boolean;
|
||||
},
|
||||
|
|
|
@ -13,6 +13,7 @@ import type { MiRole } from '@/models/Role.js';
|
|||
import { bindThis } from '@/decorators.js';
|
||||
import { DEFAULT_POLICIES } from '@/core/RoleService.js';
|
||||
import { IdService } from '@/core/IdService.js';
|
||||
import { Packed } from '@/misc/json-schema.js';
|
||||
|
||||
@Injectable()
|
||||
export class RoleEntityService {
|
||||
|
@ -31,7 +32,7 @@ export class RoleEntityService {
|
|||
public async pack(
|
||||
src: MiRole['id'] | MiRole,
|
||||
me?: { id: MiUser['id'] } | null | undefined,
|
||||
) {
|
||||
): Promise<Packed<'Role'>> {
|
||||
const role = typeof src === 'object' ? src : await this.rolesRepository.findOneByOrFail({ id: src });
|
||||
|
||||
const assignedCount = await this.roleAssignmentsRepository.createQueryBuilder('assign')
|
||||
|
@ -67,6 +68,7 @@ export class RoleEntityService {
|
|||
isModerator: role.isModerator,
|
||||
isExplorable: role.isExplorable,
|
||||
asBadge: role.asBadge,
|
||||
preserveAssignmentOnMoveAccount: role.preserveAssignmentOnMoveAccount,
|
||||
canEditMembersByModerator: role.canEditMembersByModerator,
|
||||
displayOrder: role.displayOrder,
|
||||
policies: policies,
|
||||
|
|
|
@ -32,7 +32,6 @@ import type {
|
|||
MiUserNotePining,
|
||||
MiUserProfile,
|
||||
MutingsRepository,
|
||||
NoteUnreadsRepository,
|
||||
RenoteMutingsRepository,
|
||||
UserMemoRepository,
|
||||
UserNotePiningsRepository,
|
||||
|
@ -48,9 +47,9 @@ import { IdService } from '@/core/IdService.js';
|
|||
import type { AnnouncementService } from '@/core/AnnouncementService.js';
|
||||
import type { CustomEmojiService } from '@/core/CustomEmojiService.js';
|
||||
import { AvatarDecorationService } from '@/core/AvatarDecorationService.js';
|
||||
import { ChatService } from '@/core/ChatService.js';
|
||||
import type { OnModuleInit } from '@nestjs/common';
|
||||
import type { NoteEntityService } from './NoteEntityService.js';
|
||||
import type { DriveFileEntityService } from './DriveFileEntityService.js';
|
||||
import type { PageEntityService } from './PageEntityService.js';
|
||||
|
||||
const Ajv = _Ajv.default;
|
||||
|
@ -94,6 +93,7 @@ export class UserEntityService implements OnModuleInit {
|
|||
private federatedInstanceService: FederatedInstanceService;
|
||||
private idService: IdService;
|
||||
private avatarDecorationService: AvatarDecorationService;
|
||||
private chatService: ChatService;
|
||||
|
||||
constructor(
|
||||
private moduleRef: ModuleRef,
|
||||
|
@ -128,9 +128,6 @@ export class UserEntityService implements OnModuleInit {
|
|||
@Inject(DI.renoteMutingsRepository)
|
||||
private renoteMutingsRepository: RenoteMutingsRepository,
|
||||
|
||||
@Inject(DI.noteUnreadsRepository)
|
||||
private noteUnreadsRepository: NoteUnreadsRepository,
|
||||
|
||||
@Inject(DI.userNotePiningsRepository)
|
||||
private userNotePiningsRepository: UserNotePiningsRepository,
|
||||
|
||||
|
@ -152,6 +149,7 @@ export class UserEntityService implements OnModuleInit {
|
|||
this.federatedInstanceService = this.moduleRef.get('FederatedInstanceService');
|
||||
this.idService = this.moduleRef.get('IdService');
|
||||
this.avatarDecorationService = this.moduleRef.get('AvatarDecorationService');
|
||||
this.chatService = this.moduleRef.get('ChatService');
|
||||
}
|
||||
|
||||
//#region Validators
|
||||
|
@ -558,6 +556,8 @@ export class UserEntityService implements OnModuleInit {
|
|||
publicReactions: this.isLocalUser(user) ? profile!.publicReactions : false, // https://github.com/misskey-dev/misskey/issues/12964
|
||||
followersVisibility: profile!.followersVisibility,
|
||||
followingVisibility: profile!.followingVisibility,
|
||||
chatScope: user.chatScope,
|
||||
canChat: this.roleService.getUserPolicies(user.id).then(r => r.chatAvailability === 'available'),
|
||||
roles: this.roleService.getUserRoles(user.id).then(roles => roles.filter(role => role.isPublic).sort((a, b) => b.displayOrder - a.displayOrder).map(role => ({
|
||||
id: role.id,
|
||||
name: role.name,
|
||||
|
@ -598,14 +598,9 @@ export class UserEntityService implements OnModuleInit {
|
|||
isDeleted: user.isDeleted,
|
||||
twoFactorBackupCodesStock: profile?.twoFactorBackupSecret?.length === 5 ? 'full' : (profile?.twoFactorBackupSecret?.length ?? 0) > 0 ? 'partial' : 'none',
|
||||
hideOnlineStatus: user.hideOnlineStatus,
|
||||
hasUnreadSpecifiedNotes: this.noteUnreadsRepository.count({
|
||||
where: { userId: user.id, isSpecified: true },
|
||||
take: 1,
|
||||
}).then(count => count > 0),
|
||||
hasUnreadMentions: this.noteUnreadsRepository.count({
|
||||
where: { userId: user.id, isMentioned: true },
|
||||
take: 1,
|
||||
}).then(count => count > 0),
|
||||
hasUnreadSpecifiedNotes: false, // 後方互換性のため
|
||||
hasUnreadMentions: false, // 後方互換性のため
|
||||
hasUnreadChatMessages: this.chatService.hasUnreadMessages(user.id),
|
||||
hasUnreadAnnouncement: unreadAnnouncements!.length > 0,
|
||||
unreadAnnouncements,
|
||||
hasUnreadAntenna: this.getHasUnreadAntenna(user.id),
|
||||
|
|
|
@ -24,7 +24,6 @@ export const DI = {
|
|||
noteFavoritesRepository: Symbol('noteFavoritesRepository'),
|
||||
noteThreadMutingsRepository: Symbol('noteThreadMutingsRepository'),
|
||||
noteReactionsRepository: Symbol('noteReactionsRepository'),
|
||||
noteUnreadsRepository: Symbol('noteUnreadsRepository'),
|
||||
pollsRepository: Symbol('pollsRepository'),
|
||||
pollVotesRepository: Symbol('pollVotesRepository'),
|
||||
userProfilesRepository: Symbol('userProfilesRepository'),
|
||||
|
@ -83,6 +82,11 @@ export const DI = {
|
|||
flashsRepository: Symbol('flashsRepository'),
|
||||
flashLikesRepository: Symbol('flashLikesRepository'),
|
||||
userMemosRepository: Symbol('userMemosRepository'),
|
||||
chatMessagesRepository: Symbol('chatMessagesRepository'),
|
||||
chatApprovalsRepository: Symbol('chatApprovalsRepository'),
|
||||
chatRoomsRepository: Symbol('chatRoomsRepository'),
|
||||
chatRoomMembershipsRepository: Symbol('chatRoomMembershipsRepository'),
|
||||
chatRoomInvitationsRepository: Symbol('chatRoomInvitationsRepository'),
|
||||
bubbleGameRecordsRepository: Symbol('bubbleGameRecordsRepository'),
|
||||
reversiGamesRepository: Symbol('reversiGamesRepository'),
|
||||
//#endregion
|
||||
|
|
|
@ -4,6 +4,7 @@
|
|||
*/
|
||||
|
||||
import * as fs from 'node:fs/promises';
|
||||
import { WritableStream } from 'node:stream/web';
|
||||
import type { PathLike } from 'node:fs';
|
||||
|
||||
/**
|
||||
|
|
|
@ -0,0 +1,40 @@
|
|||
/*
|
||||
* SPDX-FileCopyrightText: syuilo and misskey-project
|
||||
* SPDX-License-Identifier: AGPL-3.0-only
|
||||
*/
|
||||
|
||||
function parseBigIntChunked(str: string, base: number, chunkSize: number, powerOfChunkSize: bigint): bigint {
|
||||
const chunks = [];
|
||||
while (str.length > 0) {
|
||||
chunks.unshift(str.slice(-chunkSize));
|
||||
str = str.slice(0, -chunkSize);
|
||||
}
|
||||
let result = 0n;
|
||||
for (const chunk of chunks) {
|
||||
result *= powerOfChunkSize;
|
||||
const int = parseInt(chunk, base);
|
||||
if (Number.isNaN(int)) {
|
||||
throw new Error('Invalid base36 string');
|
||||
}
|
||||
result += BigInt(int);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
export function parseBigInt36(str: string): bigint {
|
||||
// log_36(Number.MAX_SAFE_INTEGER) => 10.251599391715352
|
||||
// so we process 10 chars at once
|
||||
return parseBigIntChunked(str, 36, 10, 36n ** 10n);
|
||||
}
|
||||
|
||||
export function parseBigInt16(str: string): bigint {
|
||||
// log_16(Number.MAX_SAFE_INTEGER) => 13.25
|
||||
// so we process 13 chars at once
|
||||
return parseBigIntChunked(str, 16, 13, 16n ** 13n);
|
||||
}
|
||||
|
||||
export function parseBigInt32(str: string): bigint {
|
||||
// log_32(Number.MAX_SAFE_INTEGER) => 10.6
|
||||
// so we process 10 chars at once
|
||||
return parseBigIntChunked(str, 32, 10, 32n ** 10n);
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue