Merge branch 'develop' into refactor-unify-rolepolicyeditor

This commit is contained in:
kakkokari-gtyih 2025-08-02 14:35:43 +09:00
commit 766fa7fbc4
240 changed files with 7777 additions and 4706 deletions

View File

@ -13,3 +13,7 @@ trim_trailing_whitespace = false
[*.{yml,yaml}] [*.{yml,yaml}]
indent_style = space indent_style = space
[packages/backend/migration/*.js]
indent_style = space
indent_size = 4

View File

@ -1 +1 @@
20.10.0 22.15.0

View File

@ -15,3 +15,5 @@ redis:
host: 127.0.0.1 host: 127.0.0.1
port: 56312 port: 56312
id: aidx id: aidx
proxyRemoteFiles: true

View File

@ -109,6 +109,7 @@ jobs:
name: E2E tests (backend) name: E2E tests (backend)
runs-on: ubuntu-latest runs-on: ubuntu-latest
strategy: strategy:
fail-fast: false
matrix: matrix:
node-version-file: node-version-file:
- .node-version - .node-version
@ -152,3 +153,47 @@ jobs:
with: with:
token: ${{ secrets.CODECOV_TOKEN }} token: ${{ secrets.CODECOV_TOKEN }}
files: ./packages/backend/coverage/coverage-final.json files: ./packages/backend/coverage/coverage-final.json
migration:
name: Migration tests (backend)
runs-on: ubuntu-latest
strategy:
matrix:
node-version-file:
- .node-version
#- .github/min.node-version
services:
postgres:
image: postgres:15
ports:
- 54312:5432
env:
POSTGRES_DB: test-misskey
POSTGRES_HOST_AUTH_METHOD: trust
steps:
- uses: actions/checkout@v4.2.2
with:
submodules: true
- name: Setup pnpm
uses: pnpm/action-setup@v4.1.0
- name: Get current date
id: current-date
run: echo "today=$(date +'%Y-%m-%d')" >> $GITHUB_OUTPUT
- name: Use Node.js
uses: actions/setup-node@v4.4.0
with:
node-version-file: ${{ matrix.node-version-file }}
cache: 'pnpm'
- run: pnpm i --frozen-lockfile
- name: Check pnpm-lock.yaml
run: git diff --exit-code pnpm-lock.yaml
- name: Copy Configure
run: cp .github/misskey/test.yml .config
- name: Build
run: pnpm build
- name: Run migrations
run: MISSKEY_CONFIG_YML=test.yml pnpm --filter backend migrate
- name: Check no migrations are remaining
run: MISSKEY_CONFIG_YML=test.yml pnpm --filter backend check-migrations

View File

@ -1,12 +1,54 @@
## 2025.8.0
### Note
- サポートされるNode.jsの最小バージョンが**22.15.0**になりました
### General
- ノートを削除した際、関連するノートが同時に削除されないようになりました
- APIで、「replyIdが存在しているのにreplyがnull」や「renoteIdが存在しているのにrenoteがnull」であるという、今までにはなかったパターンが表れることになります
- 定期的に参照されていない古いリモートの投稿を削除する機能が実装されました(コントロールパネル→パフォーマンス→Remote Notes Cleaning)
- 既存のサーバーでは**デフォルトでオフ**、新規サーバーでは**デフォルトでオン**になります
- データベースの肥大化を防止することが可能です
- 既存のサーバーで当機能を有効化した場合は、処理量が多くなるため、一時的にストレージ使用量が増加する可能性があります。
- 増加量を抑えるには、最大処理継続時間をデフォルトより短くしてください。
- サーバーの初期設定が完了するまでは連合がオンにならないようになりました
- 日本語における公開範囲名称の「ダイレクト」が「指名」に改称されました
- mfm.jsをアップデートしました
- Enhance: Unicode 15.1 および 16.0 に収録されている絵文字に対応
- Enhance: acctに `.` が入っているユーザーのメンションに対応
- Fix: Unicode絵文字に隣接する異体字セレクタ`U+FE0F`)が絵文字として認識される問題を修正
### Client
- Feat: セーフモード
- プラグイン・テーマ・カスタムCSSの使用でクライアントの起動に問題が発生した際に、これらを無効にして起動できます
- 以下の方法でセーフモードを起動できます
- `g` キーを連打する
- URLに`?safemode=true`を付ける
- PWAのショートカットで Safemode を選択して起動する
- Fix: 一部の設定検索結果が存在しないパスになる問題を修正
(Cherry-picked from https://activitypub.software/TransFem-org/Sharkey/-/merge_requests/1171)
- Fix: テーマエディタが動作しない問題を修正
### Server
- Enhance: ノートの削除処理の効率化
- Enhance: 全体的なパフォーマンスの向上
## 2025.7.0 ## 2025.7.0
### Note
- Node.jsの最小バージョンを20.10.0から20.18.1に引き上げました
- なお、特に必要がない限りNode.jsは推奨バージョンであるv22を使用するようにしてください
### General ### General
- Feat: ノートの下書き機能 - Feat: ノートの下書き機能
- Feat: クリップ内でノートを検索できるように - Feat: クリップ内でノートを検索できるように
- Feat: Playを検索できるように - Feat: Playを検索できるように
- Feat: モデレーションにおいて、特定のドライブファイルを添付しているチャットメッセージを一覧できるように - Feat: モデレーションにおいて、特定のドライブファイルを添付しているチャットメッセージを一覧できるように
- Enhance: ウォーターマーク機能をロールで制御可能に
### Client ### Client
- Note: 「自動でもっと見る」オプションは無効になっています
- Feat: モデログを検索できるように - Feat: モデログを検索できるように
- Enhance: 設定の自動バックアップをオンにした直後に自動バックアップするように - Enhance: 設定の自動バックアップをオンにした直後に自動バックアップするように
- Enhance: ファイルアップロード前にキャプション設定を行えるように - Enhance: ファイルアップロード前にキャプション設定を行えるように
@ -19,6 +61,8 @@
- Fix: プラグインをアンインストールしてもセーブデータが残る問題を修正 - Fix: プラグインをアンインストールしてもセーブデータが残る問題を修正
- Fix: 数時間後Misskeyのタブに戻った際に、タブがスロットリングされている間の更新アニメーションを延々見せ続けられる問題を修正 - Fix: 数時間後Misskeyのタブに戻った際に、タブがスロットリングされている間の更新アニメーションを延々見せ続けられる問題を修正
- Fix: 非ログイン時のハイライトートの画像がCWの有無を考慮せず表示される問題を修正 - Fix: 非ログイン時のハイライトートの画像がCWの有無を考慮せず表示される問題を修正
- Fix: レンジ選択・ドロップダウンにて、操作を無効にすべきところで無効にならない問題を修正
- Fix: Pull to refreshが有効なときに横スクロールができない問題を修正
### Server ### Server
- Enhance: sinceId/untilIdが指定可能なエンドポイントにおいて、sinceDate/untilDateも指定可能に - Enhance: sinceId/untilIdが指定可能なエンドポイントにおいて、sinceDate/untilDateも指定可能に

View File

@ -18,6 +18,7 @@ WORKDIR /misskey
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
COPY --link ["scripts", "./scripts"] COPY --link ["scripts", "./scripts"]
COPY --link ["patches", "./patches"]
COPY --link ["packages/backend/package.json", "./packages/backend/"] COPY --link ["packages/backend/package.json", "./packages/backend/"]
COPY --link ["packages/frontend-shared/package.json", "./packages/frontend-shared/"] COPY --link ["packages/frontend-shared/package.json", "./packages/frontend-shared/"]
COPY --link ["packages/frontend/package.json", "./packages/frontend/"] COPY --link ["packages/frontend/package.json", "./packages/frontend/"]
@ -53,6 +54,7 @@ WORKDIR /misskey
COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"] COPY --link ["pnpm-lock.yaml", "pnpm-workspace.yaml", "package.json", "./"]
COPY --link ["scripts", "./scripts"] COPY --link ["scripts", "./scripts"]
COPY --link ["patches", "./patches"]
COPY --link ["packages/backend/package.json", "./packages/backend/"] COPY --link ["packages/backend/package.json", "./packages/backend/"]
COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"] COPY --link ["packages/misskey-js/package.json", "./packages/misskey-js/"]
COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"] COPY --link ["packages/misskey-reversi/package.json", "./packages/misskey-reversi/"]

View File

@ -78,6 +78,8 @@ describe('After setup instance', () => {
cy.get('[data-cy-signup-password] input').type('alice1234'); cy.get('[data-cy-signup-password] input').type('alice1234');
cy.get('[data-cy-signup-submit]').should('be.disabled'); cy.get('[data-cy-signup-submit]').should('be.disabled');
cy.get('[data-cy-signup-password-retype] input').type('alice1234'); cy.get('[data-cy-signup-password-retype] input').type('alice1234');
cy.get('[data-cy-signup-submit]').should('be.disabled');
cy.get('[data-cy-signup-invitation-code] input').type('test-invitation-code');
cy.get('[data-cy-signup-submit]').should('not.be.disabled'); cy.get('[data-cy-signup-submit]').should('not.be.disabled');
cy.get('[data-cy-signup-submit]').click(); cy.get('[data-cy-signup-submit]').click();

View File

@ -1008,6 +1008,8 @@ lastNDays: "آخر {n} أيام"
surrender: "ألغِ" surrender: "ألغِ"
postForm: "أنشئ ملاحظة" postForm: "أنشئ ملاحظة"
information: "عن" information: "عن"
inMinutes: "د"
inDays: "ي"
_chat: _chat:
invitations: "دعوة" invitations: "دعوة"
noHistory: "السجل فارغ" noHistory: "السجل فارغ"

View File

@ -848,6 +848,8 @@ sourceCode: "সোর্স কোড"
flip: "উল্টান" flip: "উল্টান"
postForm: "নোট লিখুন" postForm: "নোট লিখুন"
information: "আপনার সম্পর্কে" information: "আপনার সম্পর্কে"
inMinutes: "মিনিট"
inDays: "দিন"
_chat: _chat:
invitations: "আমন্ত্রণ" invitations: "আমন্ত্রণ"
noHistory: "কোনো ইতিহাস নেই" noHistory: "কোনো ইতিহাস নেই"

View File

@ -896,7 +896,7 @@ searchResult: "Resultats de la cerca"
hashtags: "Etiquetes" hashtags: "Etiquetes"
troubleshooting: "Solucionar problemes" troubleshooting: "Solucionar problemes"
useBlurEffect: "Fes servir efectes de desenfocament a la interfície" useBlurEffect: "Fes servir efectes de desenfocament a la interfície"
learnMore: "Saber més " learnMore: "Saber-ne més "
misskeyUpdated: "Misskey s'ha actualitzat " misskeyUpdated: "Misskey s'ha actualitzat "
whatIsNew: "Mostra canvis" whatIsNew: "Mostra canvis"
translate: "Traduir " translate: "Traduir "
@ -1368,6 +1368,8 @@ redisplayAllTips: "Torna ha mostrat tots els trucs i consells"
hideAllTips: "Amagar tots els trucs i consells" hideAllTips: "Amagar tots els trucs i consells"
defaultImageCompressionLevel: "Nivell de comprensió de la imatge per defecte" defaultImageCompressionLevel: "Nivell de comprensió de la imatge per defecte"
defaultImageCompressionLevel_description: "Baixa, conserva la qualitat de la imatge però la mida de l'arxiu és més gran. <br>Alta, redueix la mida de l'arxiu però també la qualitat de la imatge." defaultImageCompressionLevel_description: "Baixa, conserva la qualitat de la imatge però la mida de l'arxiu és més gran. <br>Alta, redueix la mida de l'arxiu però també la qualitat de la imatge."
inMinutes: "Minut(s)"
inDays: "Di(a)(es)"
_order: _order:
newest: "Més recent" newest: "Més recent"
oldest: "Cronològic" oldest: "Cronològic"
@ -1998,6 +2000,7 @@ _role:
uploadableFileTypes_caption: "Especifica el tipus MIME. Es poden especificar diferents tipus MIME separats amb una nova línia, i es poden especificar comodins amb asteriscs (*). (Per exemple: image/*)" uploadableFileTypes_caption: "Especifica el tipus MIME. Es poden especificar diferents tipus MIME separats amb una nova línia, i es poden especificar comodins amb asteriscs (*). (Per exemple: image/*)"
uploadableFileTypes_caption2: "Pot que no sigui possible determinar el tipus MIME d'alguns arxius. Per permetre aquests tipus d'arxius afegeix {x} a les especificacions." uploadableFileTypes_caption2: "Pot que no sigui possible determinar el tipus MIME d'alguns arxius. Per permetre aquests tipus d'arxius afegeix {x} a les especificacions."
noteDraftLimit: "Nombre possible d'esborranys de notes al servidor" noteDraftLimit: "Nombre possible d'esborranys de notes al servidor"
watermarkAvailable: "Pots fer servir la marca d'aigua"
_condition: _condition:
roleAssignedTo: "Assignat a rols manuals" roleAssignedTo: "Assignat a rols manuals"
isLocal: "Usuari local" isLocal: "Usuari local"
@ -2806,6 +2809,7 @@ _fileViewer:
url: "URL" url: "URL"
uploadedAt: "Pujat el" uploadedAt: "Pujat el"
attachedNotes: "Notes amb aquest fitxer" attachedNotes: "Notes amb aquest fitxer"
usage: "Ús "
thisPageCanBeSeenFromTheAuthor: "Aquesta pàgina només la pot veure l'usuari que ha pujat aquest fitxer." thisPageCanBeSeenFromTheAuthor: "Aquesta pàgina només la pot veure l'usuari que ha pujat aquest fitxer."
_externalResourceInstaller: _externalResourceInstaller:
title: "Instal·lar des d'un lloc extern" title: "Instal·lar des d'un lloc extern"

View File

@ -1107,6 +1107,8 @@ lastNDays: "Posledních {n} dnů"
surrender: "Zrušit" surrender: "Zrušit"
postForm: "Formulář pro odeslání" postForm: "Formulář pro odeslání"
information: "Informace" information: "Informace"
inMinutes: "Minut"
inDays: "Dnů"
_chat: _chat:
invitations: "Pozvat" invitations: "Pozvat"
noHistory: "Žádná historie" noHistory: "Žádná historie"

View File

@ -1313,6 +1313,7 @@ availableRoles: "Verfügbare Rollen"
acknowledgeNotesAndEnable: "Schalten Sie dies erst ein, wenn Sie die Vorsichtsmaßnahmen verstanden haben." 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." 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." federationDisabled: "Föderation ist auf diesem Server deaktiviert. Es ist nicht möglich, mit Benutzern auf anderen Servern zu interagieren."
draft: "Entwurf"
confirmOnReact: "Reagieren bestätigen" confirmOnReact: "Reagieren bestätigen"
reactAreYouSure: "Willst du eine \"{emoji}\"-Reaktion hinzufügen?" reactAreYouSure: "Willst du eine \"{emoji}\"-Reaktion hinzufügen?"
markAsSensitiveConfirm: "Möchtest du dieses Medium als sensibel kennzeichnen?" markAsSensitiveConfirm: "Möchtest du dieses Medium als sensibel kennzeichnen?"
@ -1367,6 +1368,11 @@ redisplayAllTips: "Alle „Tipps und Tricks“ wieder anzeigen"
hideAllTips: "Alle „Tipps und Tricks“ ausblenden" hideAllTips: "Alle „Tipps und Tricks“ ausblenden"
defaultImageCompressionLevel: "Standard-Bildkomprimierungsstufe" defaultImageCompressionLevel: "Standard-Bildkomprimierungsstufe"
defaultImageCompressionLevel_description: "Ein niedrigerer Wert erhält die Bildqualität, erhöht aber die Dateigröße. <br>Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität." defaultImageCompressionLevel_description: "Ein niedrigerer Wert erhält die Bildqualität, erhöht aber die Dateigröße. <br>Höhere Werte reduzieren die Dateigröße, verringern aber die Bildqualität."
inMinutes: "Minute(n)"
inDays: "Tag(en)"
_order:
newest: "Neueste zuerst"
oldest: "Älteste zuerst"
_chat: _chat:
noMessagesYet: "Noch keine Nachrichten" noMessagesYet: "Noch keine Nachrichten"
newMessage: "Neue Nachricht" newMessage: "Neue Nachricht"
@ -1993,6 +1999,8 @@ _role:
uploadableFileTypes: "Hochladbare Dateitypen" uploadableFileTypes: "Hochladbare Dateitypen"
uploadableFileTypes_caption: "Gibt die zulässigen MIME-/Dateitypen an. Mehrere MIME-Typen können durch einen Zeilenumbruch getrennt angegeben werden, und Platzhalter können mit einem Sternchen (*) angegeben werden. (z. B. image/*)" uploadableFileTypes_caption: "Gibt die zulässigen MIME-/Dateitypen an. Mehrere MIME-Typen können durch einen Zeilenumbruch getrennt angegeben werden, und Platzhalter können mit einem Sternchen (*) angegeben werden. (z. B. image/*)"
uploadableFileTypes_caption2: "Bei manchen Dateien ist es nicht möglich, den Typ zu bestimmen. Um solche Dateien zuzulassen, füge {x} der Spezifikation hinzu." uploadableFileTypes_caption2: "Bei manchen Dateien ist es nicht möglich, den Typ zu bestimmen. Um solche Dateien zuzulassen, füge {x} der Spezifikation hinzu."
noteDraftLimit: "Anzahl der möglichen Entwürfe für serverseitige Notizen"
watermarkAvailable: "Kann die Wasserzeichenfunktion verwenden"
_condition: _condition:
roleAssignedTo: "Manuellen Rollen zugewiesen" roleAssignedTo: "Manuellen Rollen zugewiesen"
isLocal: "Lokaler Benutzer" isLocal: "Lokaler Benutzer"
@ -2152,6 +2160,7 @@ _theme:
install: "Farbschemata installieren" install: "Farbschemata installieren"
manage: "Farbschemaverwaltung" manage: "Farbschemaverwaltung"
code: "Farbschemencode" code: "Farbschemencode"
copyThemeCode: "Farbschemencode kopieren"
description: "Beschreibung" description: "Beschreibung"
installed: "{name} wurde installiert" installed: "{name} wurde installiert"
installedThemes: "Installierte Farbschemata" installedThemes: "Installierte Farbschemata"
@ -3103,6 +3112,7 @@ _serverSetupWizard:
text2: "Wir würden uns über deine Unterstützung freuen, damit wir dieses Projekt auch in Zukunft weiterentwickeln können." text2: "Wir würden uns über deine Unterstützung freuen, damit wir dieses Projekt auch in Zukunft weiterentwickeln können."
text3: "Für Unterstützer gibt es auch besondere Vorteile!" text3: "Für Unterstützer gibt es auch besondere Vorteile!"
_uploader: _uploader:
editImage: "Bild bearbeiten"
compressedToX: "Komprimiert zu {x}" compressedToX: "Komprimiert zu {x}"
savedXPercent: "{x}% gespart" savedXPercent: "{x}% gespart"
abortConfirm: "Einige Dateien wurden nicht hochgeladen. Möchtest du den Vorgang abbrechen?" abortConfirm: "Einige Dateien wurden nicht hochgeladen. Möchtest du den Vorgang abbrechen?"
@ -3141,6 +3151,12 @@ _watermarkEditor:
stripeWidth: "Linienbreite" stripeWidth: "Linienbreite"
stripeFrequency: "Linienanzahl" stripeFrequency: "Linienanzahl"
angle: "Winkel" angle: "Winkel"
polkadot: "Punktmuster"
polkadotMainDotOpacity: "Deckkraft des Hauptpunktes"
polkadotMainDotRadius: "Größe des Hauptpunktes"
polkadotSubDotOpacity: "Deckkraft des Unterpunktes"
polkadotSubDotRadius: "Größe des Unterpunktes"
polkadotSubDotDivisions: "Anzahl der Unterpunkte"
_imageEffector: _imageEffector:
title: "Effekte" title: "Effekte"
addEffect: "Effekte hinzufügen" addEffect: "Effekte hinzufügen"
@ -3156,5 +3172,18 @@ _imageEffector:
colorClampAdvanced: "Farbkomprimierung (erweitert)" colorClampAdvanced: "Farbkomprimierung (erweitert)"
distort: "Verzerrung" distort: "Verzerrung"
stripe: "Streifen" stripe: "Streifen"
polkadot: "Punktmuster"
drafts: "Entwurf"
_drafts: _drafts:
select: "Entwurf auswählen"
cannotCreateDraftAnymore: "Die Anzahl der Entwürfe, die erstellt werden können, wurde überschritten."
cannotCreateDraft: "Mit diesem Inhalt kann kein Entwurf erstellt werden."
delete: "Entwurf löschen"
deleteAreYouSure: "Entwurf löschen?"
noDrafts: "Keine Entwürfe"
replyTo: "Antwort an {user}"
quoteOf: "Zitat von {user}s Notiz"
saveToDraft: "Als Entwurf speichern"
restoreFromDraft: "Aus Entwurf wiederherstellen"
restore: "Wiederherstellen" restore: "Wiederherstellen"
listDrafts: "Liste der Entwürfe"

View File

@ -1302,7 +1302,7 @@ passkeyVerificationSucceededButPasswordlessLoginDisabled: "Passkey verification
messageToFollower: "Message to followers" messageToFollower: "Message to followers"
target: "Target" target: "Target"
testCaptchaWarning: "This function is intended for CAPTCHA testing purposes.\n<strong>Do not use in a production environment.</strong>" testCaptchaWarning: "This function is intended for CAPTCHA testing purposes.\n<strong>Do not use in a production environment.</strong>"
prohibitedWordsForNameOfUser: "Prohibited words for user names" prohibitedWordsForNameOfUser: "Prohibited words for usernames"
prohibitedWordsForNameOfUserDescription: "If any of the strings in this list are included in the user's name, the name will be denied. Users with moderator privileges are not affected by this restriction." prohibitedWordsForNameOfUserDescription: "If any of the strings in this list are included in the user's name, the name will be denied. Users with moderator privileges are not affected by this restriction."
yourNameContainsProhibitedWords: "Your name contains prohibited words" yourNameContainsProhibitedWords: "Your name contains prohibited words"
yourNameContainsProhibitedWordsDescription: "If you wish to use this name, please contact your server administrator." yourNameContainsProhibitedWordsDescription: "If you wish to use this name, please contact your server administrator."
@ -1367,7 +1367,9 @@ tip: "Tips & Tricks"
redisplayAllTips: "Show all “Tips & Tricks” again" redisplayAllTips: "Show all “Tips & Tricks” again"
hideAllTips: "Hide all \"Tips & Tricks\"" hideAllTips: "Hide all \"Tips & Tricks\""
defaultImageCompressionLevel: "Default image compression level" defaultImageCompressionLevel: "Default image compression level"
defaultImageCompressionLevel_description: "High, reduces the file size but also the image quality. <br>High, reduces the file size but also the image quality." defaultImageCompressionLevel_description: "Lower level preserves image quality but increases file size.<br>Higher level reduce file size, but reduce image quality."
inMinutes: "Minute(s)"
inDays: "Day(s)"
_order: _order:
newest: "Newest First" newest: "Newest First"
oldest: "Oldest First" oldest: "Oldest First"
@ -1998,6 +2000,7 @@ _role:
uploadableFileTypes_caption: "Specifies the allowed MIME/file types. Multiple MIME types can be specified by separating them with a new line, and wildcards can be specified with an asterisk (*). (e.g., image/*)" uploadableFileTypes_caption: "Specifies the allowed MIME/file types. Multiple MIME types can be specified by separating them with a new line, and wildcards can be specified with an asterisk (*). (e.g., image/*)"
uploadableFileTypes_caption2: "Some files types might fail to be detected. To allow such files, add {x} to the specification." uploadableFileTypes_caption2: "Some files types might fail to be detected. To allow such files, add {x} to the specification."
noteDraftLimit: "Number of possible drafts of server notes" noteDraftLimit: "Number of possible drafts of server notes"
watermarkAvailable: "Availability of watermark function"
_condition: _condition:
roleAssignedTo: "Assigned to manual roles" roleAssignedTo: "Assigned to manual roles"
isLocal: "Local user" isLocal: "Local user"
@ -2806,6 +2809,7 @@ _fileViewer:
url: "URL" url: "URL"
uploadedAt: "Uploaded at" uploadedAt: "Uploaded at"
attachedNotes: "Attached notes" attachedNotes: "Attached notes"
usage: "Used"
thisPageCanBeSeenFromTheAuthor: "This page can only be seen by the user who uploaded this file." thisPageCanBeSeenFromTheAuthor: "This page can only be seen by the user who uploaded this file."
_externalResourceInstaller: _externalResourceInstaller:
title: "Install from external site" title: "Install from external site"
@ -3182,6 +3186,7 @@ drafts: "Drafts"
_drafts: _drafts:
select: "Select Draft" select: "Select Draft"
cannotCreateDraftAnymore: "The number of drafts that can be created has been exceeded." cannotCreateDraftAnymore: "The number of drafts that can be created has been exceeded."
cannotCreateDraft: "You cannot create a draft with this content."
delete: "Delete Draft" delete: "Delete Draft"
deleteAreYouSure: "Delete draft?" deleteAreYouSure: "Delete draft?"
noDrafts: "No drafts" noDrafts: "No drafts"

View File

@ -61,7 +61,7 @@ copyRSS: "Copiar RSS"
copyUsername: "Copiar nombre de usuario" copyUsername: "Copiar nombre de usuario"
copyUserId: "Copiar ID del usuario" copyUserId: "Copiar ID del usuario"
copyNoteId: "Copiar ID de la nota" copyNoteId: "Copiar ID de la nota"
copyFileId: "Copiar ID del archivo" copyFileId: "Copiar ID de archivo"
copyFolderId: "Copiar ID de carpeta" copyFolderId: "Copiar ID de carpeta"
copyProfileUrl: "Copiar la URL del perfil" copyProfileUrl: "Copiar la URL del perfil"
searchUser: "Buscar un usuario" searchUser: "Buscar un usuario"
@ -83,10 +83,10 @@ files: "Archivos"
download: "Descargar" download: "Descargar"
driveFileDeleteConfirm: "¿Desea borrar el archivo \"{name}\"? Las notas que tengan este archivo como adjunto serán eliminadas" driveFileDeleteConfirm: "¿Desea borrar el archivo \"{name}\"? Las notas que tengan este archivo como adjunto serán eliminadas"
unfollowConfirm: "¿Desea dejar de seguir a {name}?" unfollowConfirm: "¿Desea dejar de seguir a {name}?"
exportRequested: "Se ha solicitado la exportación. Puede tomar un tiempo. Cuando termine la exportación, se añadirá en el drive" exportRequested: "Has solicitado la exportación. Puede llevar un tiempo. Cuando termine la exportación, se añadirá al drive"
importRequested: "Se ha solicitado la importación. Puede tomar un tiempo." importRequested: "Has solicitado la importación. Puede llevar un tiempo."
lists: "Listas" lists: "Listas"
noLists: "No tiene listas" noLists: "No tienes ninguna lista"
note: "Notas" note: "Notas"
notes: "Notas" notes: "Notas"
following: "Siguiendo" following: "Siguiendo"
@ -99,9 +99,9 @@ somethingHappened: "Ocurrió un error"
retry: "Reintentar" retry: "Reintentar"
pageLoadError: "Error al leer la página" pageLoadError: "Error al leer la página"
pageLoadErrorDescription: "Normalmente es debido a la red o al caché del navegador. Por favor limpie el caché o intente más tarde." pageLoadErrorDescription: "Normalmente es debido a la red o al caché del navegador. Por favor limpie el caché o intente más tarde."
serverIsDead: "No hay respuesta del servidor. Espere un momento y vuelva a intentarlo." serverIsDead: "No hay respuesta del servidor. Espera un momento y vuelve a intentarlo."
youShouldUpgradeClient: "Para ver esta página, por favor refrezca el navegador y utiliza una versión más reciente del cliente." youShouldUpgradeClient: "Para ver esta página, recarga el navegador para actualizar el cliente."
enterListName: "Ingrese nombre de lista" enterListName: "Introduce un nombre para la lista"
privacy: "Privacidad" privacy: "Privacidad"
makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento" makeFollowManuallyApprove: "Aprobar manualmente las solicitudes de seguimiento"
defaultNoteVisibility: "Visibilidad por defecto" defaultNoteVisibility: "Visibilidad por defecto"
@ -134,28 +134,28 @@ emojiPicker: "Selector de emojis"
pinnedEmojisForReactionSettingDescription: "Puedes seleccionar reacciones para fijarlos en el selector" pinnedEmojisForReactionSettingDescription: "Puedes seleccionar reacciones para fijarlos en el selector"
pinnedEmojisSettingDescription: "Puedes seleccionar emojis para fijarlos en el selector" pinnedEmojisSettingDescription: "Puedes seleccionar emojis para fijarlos en el selector"
emojiPickerDisplay: "Mostrar el selector de emojis" emojiPickerDisplay: "Mostrar el selector de emojis"
overwriteFromPinnedEmojisForReaction: "Sobreescribir las reacciones fijadas" overwriteFromPinnedEmojisForReaction: "Sobreescribir los ajustes de reacciones"
overwriteFromPinnedEmojis: "Sobreescribir los emojis fijados" overwriteFromPinnedEmojis: "Sobreescribir los ajustes generales"
reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir." reactionSettingDescription2: "Arrastra para reordenar, click para borrar, pulsa \"+\" para añadir."
rememberNoteVisibility: "Recordar visibilidad" rememberNoteVisibility: "Recordar visibilidad"
attachCancel: "Quitar adjunto" attachCancel: "Quitar adjunto"
deleteFile: "Archivo eliminado" deleteFile: "Eliminar archivo"
markAsSensitive: "Marcar como sensible" markAsSensitive: "Marcar como sensible"
unmarkAsSensitive: "Desmarcar como sensible" unmarkAsSensitive: "Desmarcar como sensible"
enterFileName: "Ingrese el nombre del archivo" enterFileName: "Introduce el nombre del archivo"
mute: "Silenciar" mute: "Silenciar"
unmute: "Dejar de silenciar" unmute: "Dejar de silenciar"
renoteMute: "Silenciar renota" renoteMute: "Silenciar renota"
renoteUnmute: "Desilenciar renota" renoteUnmute: "Desilenciar renota"
block: "Bloquear" block: "Bloquear"
unblock: "Dejar de bloquear" unblock: "Desbloquear"
suspend: "Suspender" suspend: "Suspender"
unsuspend: "Dejar de suspender" unsuspend: "Dejar de suspender"
blockConfirm: "¿Quiere bloquear esta cuenta?" blockConfirm: "¿Quieres bloquear esta cuenta?"
unblockConfirm: "¿Quiere dejar de bloquear esta cuenta?" unblockConfirm: "¿Quieres desbloquear esta cuenta?"
suspendConfirm: "¿Quiere suspender esta cuenta?" suspendConfirm: "¿Quieres suspender esta cuenta?"
unsuspendConfirm: "¿Quiere dejar de suspender esta cuenta?" unsuspendConfirm: "¿Quieres dejar de suspender esta cuenta?"
selectList: "Seleccione una lista" selectList: "Selecciona una lista"
editList: "Editar lista" editList: "Editar lista"
selectChannel: "Seleccionar canal" selectChannel: "Seleccionar canal"
selectAntenna: "Seleccionar antena" selectAntenna: "Seleccionar antena"
@ -163,55 +163,55 @@ editAntenna: "Editar antena"
createAntenna: "Crear una antena" createAntenna: "Crear una antena"
selectWidget: "Seleccionar widget" selectWidget: "Seleccionar widget"
editWidgets: "Editar widgets" editWidgets: "Editar widgets"
editWidgetsExit: "Terminar edición" editWidgetsExit: "Hecho"
customEmojis: "Emojis personalizados" customEmojis: "Emojis personalizados"
emoji: "Emoji" emoji: "Emoji"
emojis: "Emoji" emojis: "Emojis"
emojiName: "Nombre del emoji" emojiName: "Nombre del emoji"
emojiUrl: "URL de la imagen del emoji" emojiUrl: "URL del emoji"
addEmoji: "Agregar emoji" addEmoji: "Añadir emoji"
settingGuide: "Configuración sugerida" settingGuide: "Configuración recomendada"
cacheRemoteFiles: "Mantener en cache los archivos remotos" cacheRemoteFiles: "Mantener los archivos remotos en caché"
cacheRemoteFilesDescription: "Si desactiva esta configuración, Los archivos remotos se cargarán desde el link directo sin usar la caché. Con eso se puede ahorrar almacenamiento del servidor, pero eso aumentará el tráfico al no crear miniaturas." cacheRemoteFilesDescription: "Si desactivas esta configuración, los archivos remotos se cargarán directamente de los servidores remotos. Desactivar esto reducirá el uso de almacenamiento, pero incrementará el uso de tráfico, ya que no se generarán miniaturas."
youCanCleanRemoteFilesCache: "Puedes vaciar la caché pulsando en el botón 🗑️ en el administrador de archivos." youCanCleanRemoteFilesCache: "Puedes vaciar la caché pulsando en el botón 🗑️ en el administrador de archivos."
cacheRemoteSensitiveFiles: "Cachear archivos remotos sensibles" cacheRemoteSensitiveFiles: "Mantener los archivos remotos sensibles en caché"
cacheRemoteSensitiveFilesDescription: "Cuando esta opción está desactivada, los archivos remotos sensibles son cargador directamente de la instancia origen sin ser cacheados." cacheRemoteSensitiveFilesDescription: "Cuando esta opción está desactivada, los archivos remotos sensibles se cargarán directamente desde los servidores remotos."
flagAsBot: "Esta cuenta es un bot" flagAsBot: "Esta cuenta es un bot"
flagAsBotDescription: "En caso de que esta cuenta fuera usada por un programa, active esta opción. Al hacerlo, esta opción servirá para otros desarrolladores para evitar cadenas infinitas de reacciones, y ajustará los sistemas internos de Misskey para que trate a esta cuenta como un bot." flagAsBotDescription: "Activa esta opción si la cuenta es utilizada por un programa. Si se activa, actuará como una etiqueta para otros desarrolladores para prevenir cadenas eternas de interacción con otros bots, y ajustará los sistemas internos de Misskey para tratar esta cuenta de manera acorde."
flagAsCat: "Esta cuenta es un gato" flagAsCat: "Marcar esta cuenta como gato"
flagAsCatDescription: "En caso de que declare que esta cuenta es de un gato, active esta opción." flagAsCatDescription: "Activa esta opción para marcar esta cuenta como un gato."
flagShowTimelineReplies: "Mostrar respuestas a las notas en la biografía" flagShowTimelineReplies: "Mostrar respuestas en la línea de tiempo"
flagShowTimelineRepliesDescription: "Cuando se marca, la línea de tiempo muestra respuestas a otras notas además de las notas del usuario" flagShowTimelineRepliesDescription: "Muestra respuestas de los usuarios a las notas de otros usuarios en la línea de tiempo al activar esta opción."
autoAcceptFollowed: "Aceptar automáticamente las solicitudes de seguimiento de los usuarios que sigues" autoAcceptFollowed: "Aceptar automáticamente las solicitudes de seguimiento de los usuarios que sigues"
addAccount: "Agregar Cuenta" addAccount: "Agregar cuenta"
reloadAccountsList: "Recargar lista de cuentas" reloadAccountsList: "Recargar lista de cuentas"
loginFailed: "Error al iniciar sesión." loginFailed: "Error al iniciar sesión."
showOnRemote: "Ver en una instancia remota" showOnRemote: "Ver en instancia remota"
continueOnRemote: "Ver en una instancia remota" continueOnRemote: "Continuar en una instancia remota"
chooseServerOnMisskeyHub: "Elegir un servidor en Misskey Hub" chooseServerOnMisskeyHub: "Elegir un servidor en Misskey Hub"
specifyServerHost: "Especifica una instancia directamente" specifyServerHost: "Especifica una instancia directamente"
inputHostName: "Introduzca el dominio" inputHostName: "Introduce el dominio"
general: "General" general: "General"
wallpaper: "Fondo de pantalla" wallpaper: "Fondo de pantalla"
setWallpaper: "Establecer fondo de pantalla" setWallpaper: "Establecer fondo de pantalla"
removeWallpaper: "Quitar fondo de pantalla" removeWallpaper: "Quitar fondo de pantalla"
searchWith: "Buscar: {q}" searchWith: "Buscar: {q}"
youHaveNoLists: "No tienes listas" youHaveNoLists: "No tienes ninguna lista"
followConfirm: "¿Desea seguir a {name}?" followConfirm: "¿Quieres seguir a {name}?"
proxyAccount: "Cuenta proxy" proxyAccount: "Cuenta proxy"
proxyAccountDescription: "Una cuenta proxy es una cuenta que actúa como un seguidor remoto de un usuario bajo ciertas condiciones. Por ejemplo, cuando un usuario añade un usuario remoto a una lista, si ningún usuario local sigue al usuario agregado a la lista, la instancia no puede obtener su actividad. Así que la cuenta proxy sigue al usuario añadido a la lista" proxyAccountDescription: "Una cuenta proxy es una cuenta que actúa como un seguidor remoto de un usuario bajo ciertas condiciones. Por ejemplo, cuando un usuario añade un usuario remoto a una lista, si ningún usuario local sigue al usuario agregado a la lista, la instancia no puede obtener su actividad, así que la cuenta proxy sigue al usuario añadido a la lista"
host: "Host" host: "Instancia"
selectSelf: "Elígete a ti mismo" selectSelf: "Elígete a ti mismo"
selectUser: "Elegir usuario" selectUser: "Elegir usuario"
recipient: "Recipiente" recipient: "Receptor"
annotation: "Anotación" annotation: "Anotación"
federation: "Federación" federation: "Federación"
instances: "Instancia" instances: "Instancias"
registeredAt: "Registrado en" registeredAt: "Registrado en"
latestRequestReceivedAt: "Ultimo pedido recibido" latestRequestReceivedAt: "Última petición recibida"
latestStatus: "Último status" latestStatus: "Último estado"
storageUsage: "Almacenamiento usado" storageUsage: "Almacenamiento usado"
charts: "Chat" charts: "Métricas"
perHour: "por hora" perHour: "por hora"
perDay: "por día" perDay: "por día"
stopActivityDelivery: "Dejar de enviar actividades" stopActivityDelivery: "Dejar de enviar actividades"
@ -226,40 +226,40 @@ metadata: "Metadatos"
withNFiles: "{n} archivos" withNFiles: "{n} archivos"
monitor: "Monitor" monitor: "Monitor"
jobQueue: "Cola de trabajos" jobQueue: "Cola de trabajos"
cpuAndMemory: "CPU y Memoria" cpuAndMemory: "CPU y memoria"
network: "Red" network: "Red"
disk: "Disco" disk: "Disco"
instanceInfo: "información de la instancia" instanceInfo: "Información de la instancia"
statistics: "Estadísticas" statistics: "Estadísticas"
clearQueue: "Limpiar cola" clearQueue: "Limpiar cola"
clearQueueConfirmTitle: "¿Desea limpiar la cola?" clearQueueConfirmTitle: "¿Quieres limpiar la cola?"
clearQueueConfirmText: "Las notas aún no entregadas no se federarán. Normalmente no se necesita ejecutar esta operación" clearQueueConfirmText: "Las notas aún no entregadas no se federarán. Normalmente no se necesita ejecutar esta operación"
clearCachedFiles: "Limpiar caché" clearCachedFiles: "Limpiar caché"
clearCachedFilesConfirm: "¿Desea borrar todos los archivos remotos cacheados?" clearCachedFilesConfirm: "¿Quieres borrar todos los archivos remotos en caché?"
blockedInstances: "Instancias bloqueadas" blockedInstances: "Instancias bloqueadas"
blockedInstancesDescription: "Seleccione los hosts de las instancias que desea bloquear, separadas por una linea nueva. Las instancias bloqueadas no podrán comunicarse con esta instancia." blockedInstancesDescription: "La lista de los dominios de las instancias que quieres bloquear, separadas por una linea nueva. Las instancias bloqueadas no podrán comunicarse con esta instancia."
silencedInstances: "Instancias silenciadas" silencedInstances: "Instancias silenciadas"
silencedInstancesDescription: "Listar los hostname de las instancias que quieres silenciar. Todas las cuentas de las instancias listadas serán tratadas como silenciadas, solo podrán hacer peticiones de seguimiento, y no podrán mencionar cuentas locales si no las siguen. Esto no afecta a las instancias bloqueadas." silencedInstancesDescription: "La lista de los dominios de las instancias que quieres silenciar. Todas las cuentas de las instancias listadas serán tratadas como silenciadas, solo podrán hacer peticiones de seguimiento, y no podrán mencionar cuentas locales si no las siguen. Esto no afecta a las instancias bloqueadas."
mediaSilencedInstances: "Servidores silenciados (Multimedia)" mediaSilencedInstances: "Servidores con multimedia silenciada"
mediaSilencedInstancesDescription: "Listar las instancias que quieres silenciar. Todas las cuentas de las instancias listadas serán tratadas como silenciadas, solo podrán hacer peticiones de seguimiento, y no podrán mencionar cuentas locales si no las siguen. Esto no afecta a las instancias bloqueadas." mediaSilencedInstancesDescription: "La lista de los dominios de las instancias cuya multimedia quieres silenciar. Todas las cuentas que pertenezcan a estas instancias serán marcadas como sensibles, y no podrán usar sus emojis personalizados. Esto no afectará a las instancias bloqueadas"
federationAllowedHosts: "Servidores federados" federationAllowedHosts: "Servidores federados"
federationAllowedHostsDescription: "Establezca los nombres de los servidores que pueden federarse, separados por una nueva línea." federationAllowedHostsDescription: "La lista de los dominios de las instancias cuya federación está permitida, separadas por saltos de línea."
muteAndBlock: "Silenciar y bloquear" muteAndBlock: "Silenciar y bloquear"
mutedUsers: "Usuarios silenciados" mutedUsers: "Usuarios silenciados"
blockedUsers: "Usuarios bloqueados" blockedUsers: "Usuarios bloqueados"
noUsers: "No hay usuarios" noUsers: "No hay usuarios"
editProfile: "Editar perfil" editProfile: "Editar perfil"
noteDeleteConfirm: "¿Desea borrar esta nota?" noteDeleteConfirm: "¿Quieres borrar esta nota?"
pinLimitExceeded: "Ya no se pueden fijar más posts" pinLimitExceeded: "Ya no se pueden fijar más notas"
done: "Terminado" done: "Terminado"
processing: "Procesando" processing: "Procesando..."
preview: "Vista previa" preview: "Vista previa"
default: "Predeterminado" default: "Predeterminado"
defaultValueIs: "Por defecto: {value}" defaultValueIs: "Por defecto: {value}"
noCustomEmojis: "No hay emojis personalizados" noCustomEmojis: "No hay emojis personalizados"
noJobs: "No hay trabajos" noJobs: "No hay trabajos"
federating: "Federando" federating: "Federando"
blocked: "Bloqueando" blocked: "Bloqueado"
suspended: "Suspendido" suspended: "Suspendido"
all: "Todo" all: "Todo"
subscribing: "Suscribiendo" subscribing: "Suscribiendo"
@ -280,8 +280,8 @@ featured: "Destacados"
usernameOrUserId: "Nombre o ID del usuario" usernameOrUserId: "Nombre o ID del usuario"
noSuchUser: "No se encuentra el usuario" noSuchUser: "No se encuentra el usuario"
lookup: "Búsqueda" lookup: "Búsqueda"
announcements: "Anuncios" announcements: "Avisos"
imageUrl: "URL de la imágen" imageUrl: "URL de la imagen."
remove: "Borrar" remove: "Borrar"
removed: "Borrado" removed: "Borrado"
removeAreYouSure: "¿Desea borrar \"{x}\"?" removeAreYouSure: "¿Desea borrar \"{x}\"?"
@ -842,7 +842,7 @@ unlikeConfirm: "¿Quitar como favorito?"
fullView: "Vista completa" fullView: "Vista completa"
quitFullView: "quitar vista completa" quitFullView: "quitar vista completa"
addDescription: "Agregar descripción" addDescription: "Agregar descripción"
userPagePinTip: "Puede mantener sus notas visibles aquí seleccionando Pin en el menú de notas individuales" userPagePinTip: "Puede mantener sus notas visibles aquí seleccionando 'Fijar al perfil' en el menú de notas individuales"
notSpecifiedMentionWarning: "Algunas menciones no están incluidas en el destino" notSpecifiedMentionWarning: "Algunas menciones no están incluidas en el destino"
info: "Información" info: "Información"
userInfo: "Información del usuario" userInfo: "Información del usuario"
@ -877,7 +877,7 @@ popularPosts: "Más vistos"
shareWithNote: "Compartir con una nota" shareWithNote: "Compartir con una nota"
ads: "Anuncios" ads: "Anuncios"
expiration: "Termina el" expiration: "Termina el"
startingperiod: "periodo de inicio" startingperiod: "Comienzo"
memo: "Notas" memo: "Notas"
priority: "Prioridad" priority: "Prioridad"
high: "Alta" high: "Alta"
@ -1143,7 +1143,7 @@ channelArchiveConfirmTitle: "¿Seguro de archivar {name}?"
channelArchiveConfirmDescription: "Un canal archivado no aparecerá en la lista de canales ni en los resultados. Las nuevas publicaciones tampoco serán añadidas." channelArchiveConfirmDescription: "Un canal archivado no aparecerá en la lista de canales ni en los resultados. Las nuevas publicaciones tampoco serán añadidas."
thisChannelArchived: "El canal ha sido archivado." thisChannelArchived: "El canal ha sido archivado."
displayOfNote: "Mostrar notas" displayOfNote: "Mostrar notas"
initialAccountSetting: "Configración inicial de su cuenta\nか\nConfigración de inicio" initialAccountSetting: "Configración inicial de su cuenta"
youFollowing: "Siguiendo" youFollowing: "Siguiendo"
preventAiLearning: "Rechazar el uso en el Aprendizaje de Máquinas. (IA Generativa)" preventAiLearning: "Rechazar el uso en el Aprendizaje de Máquinas. (IA Generativa)"
preventAiLearningDescription: "Pedirle a las arañas (crawlers) no usar los textos publicados o imágenes en el aprendizaje automático (IA Predictiva / Generativa). Ésto se logra añadiendo una marca respuesta HTML con la cadena \"noai\" al cantenido. Una prevención total no podría lograrse sólo usando ésta marca, ya que puede ser simplemente ignorada." preventAiLearningDescription: "Pedirle a las arañas (crawlers) no usar los textos publicados o imágenes en el aprendizaje automático (IA Predictiva / Generativa). Ésto se logra añadiendo una marca respuesta HTML con la cadena \"noai\" al cantenido. Una prevención total no podría lograrse sólo usando ésta marca, ya que puede ser simplemente ignorada."
@ -1358,8 +1358,8 @@ advice: "Consejos"
realtimeMode: "Modo en tiempo real" realtimeMode: "Modo en tiempo real"
turnItOn: "Activar" turnItOn: "Activar"
turnItOff: "Desactivar" turnItOff: "Desactivar"
emojiMute: "Silenciar emojis" emojiMute: "Silenciar emoji"
emojiUnmute: "No Silenciar emojis" emojiUnmute: "No silenciar emoji"
muteX: "Silenciar {x}" muteX: "Silenciar {x}"
unmuteX: "Dejar de silenciar {x}" unmuteX: "Dejar de silenciar {x}"
abort: "Abortar" abort: "Abortar"
@ -1368,6 +1368,8 @@ redisplayAllTips: "Volver a mostrar todos \"Trucos y consejos\""
hideAllTips: "Ocultar todos los \"Trucos y consejos\"" hideAllTips: "Ocultar todos los \"Trucos y consejos\""
defaultImageCompressionLevel: "Nivel de compresión de la imagen por defecto" defaultImageCompressionLevel: "Nivel de compresión de la imagen por defecto"
defaultImageCompressionLevel_description: "Baja, conserva la calidad de la imagen pero la medida del archivo es más grande. <br>Alta, reduce la medida del archivo pero también la calidad de la imagen." defaultImageCompressionLevel_description: "Baja, conserva la calidad de la imagen pero la medida del archivo es más grande. <br>Alta, reduce la medida del archivo pero también la calidad de la imagen."
inMinutes: "Minutos"
inDays: "Días"
_order: _order:
newest: "Los más recientes primero" newest: "Los más recientes primero"
oldest: "Los más antiguos primero" oldest: "Los más antiguos primero"
@ -1530,7 +1532,7 @@ _announcement:
tooManyActiveAnnouncementDescription: "Tener demasiados anuncios activos empeora la experiencia de usuario. Por favor, considera archivar aquellos anuncios que hayan quedado obsoletos." tooManyActiveAnnouncementDescription: "Tener demasiados anuncios activos empeora la experiencia de usuario. Por favor, considera archivar aquellos anuncios que hayan quedado obsoletos."
readConfirmTitle: "¿Marcar como leído?" readConfirmTitle: "¿Marcar como leído?"
readConfirmText: "Esto marcará el contenido de \"{title}\" como leído." readConfirmText: "Esto marcará el contenido de \"{title}\" como leído."
shouldNotBeUsedToPresentPermanentInfo: "Dado que puede impactar en la experiencia de usuario de forma significativa, es recomendable usar notificaciones en el flujo de información en vez de información persistente." shouldNotBeUsedToPresentPermanentInfo: "Se recomienda utilizar los avisos para publicar información que requiera inmediatez, en lugar de hacerlo constantemente, ya que esto perjudica especialmente la UX de los nuevos usuarios."
dialogAnnouncementUxWarn: "Mostrar dos o más notificaciones en formato diálogo a la vez puede impactar en la experiencia de usuario de forma significativa, úsalos con cuidado." dialogAnnouncementUxWarn: "Mostrar dos o más notificaciones en formato diálogo a la vez puede impactar en la experiencia de usuario de forma significativa, úsalos con cuidado."
silence: "Silenciar notificaciones" silence: "Silenciar notificaciones"
silenceDescription: "Si lo activas, no enviarás notificación sobre este anuncio y el usuario no tendrá que leerlo." silenceDescription: "Si lo activas, no enviarás notificación sobre este anuncio y el usuario no tendrá que leerlo."
@ -1998,6 +2000,7 @@ _role:
uploadableFileTypes_caption: "Especifica los tipos MIME/archivos permitidos. Se pueden especificar varios tipos MIME separándolos con una nueva línea, y se pueden especificar comodines con un asterisco (*). (por ejemplo, image/*)" uploadableFileTypes_caption: "Especifica los tipos MIME/archivos permitidos. Se pueden especificar varios tipos MIME separándolos con una nueva línea, y se pueden especificar comodines con un asterisco (*). (por ejemplo, image/*)"
uploadableFileTypes_caption2: "Es posible que no se detecten algunos tipos de archivos. Para permitir estos archivos, añade {x} a la especificación." uploadableFileTypes_caption2: "Es posible que no se detecten algunos tipos de archivos. Para permitir estos archivos, añade {x} a la especificación."
noteDraftLimit: "Número de posibles borradores de notas del servidor" noteDraftLimit: "Número de posibles borradores de notas del servidor"
watermarkAvailable: "Disponibilidad de la función de marca de agua"
_condition: _condition:
roleAssignedTo: "Asignado a roles manuales" roleAssignedTo: "Asignado a roles manuales"
isLocal: "Usuario local" isLocal: "Usuario local"
@ -2806,6 +2809,7 @@ _fileViewer:
url: "URL" url: "URL"
uploadedAt: "Subido el" uploadedAt: "Subido el"
attachedNotes: "Notas adjuntas" attachedNotes: "Notas adjuntas"
usage: "Utilizado"
thisPageCanBeSeenFromTheAuthor: "Esta página solo puede ser vista por el autor." thisPageCanBeSeenFromTheAuthor: "Esta página solo puede ser vista por el autor."
_externalResourceInstaller: _externalResourceInstaller:
title: "Instalar desde sitio externo" title: "Instalar desde sitio externo"
@ -3119,7 +3123,7 @@ _uploader:
tip: "El archivo aún no se ha cargado, por lo que este cuadro de diálogo te permite confirmar, renombrar, comprimir y recortar el archivo antes de cargarlo. Cuando esté listo, puedes iniciar la carga pulsando el botón \"Cargar\"." tip: "El archivo aún no se ha cargado, por lo que este cuadro de diálogo te permite confirmar, renombrar, comprimir y recortar el archivo antes de cargarlo. Cuando esté listo, puedes iniciar la carga pulsando el botón \"Cargar\"."
_clientPerformanceIssueTip: _clientPerformanceIssueTip:
title: "Si crees que el consumo de batería es demasiado alto" title: "Si crees que el consumo de batería es demasiado alto"
makeSureDisabledAdBlocker: "Por favor, desactive el bloqueador de publicidad." makeSureDisabledAdBlocker: "Por favor, desactiva el bloqueador de publicidad."
makeSureDisabledAdBlocker_description: "Los bloqueadores de anuncios pueden afectar al rendimiento. Asegúrate de que no están activados en tu sistema o en las funciones/extensiones de tu navegador." makeSureDisabledAdBlocker_description: "Los bloqueadores de anuncios pueden afectar al rendimiento. Asegúrate de que no están activados en tu sistema o en las funciones/extensiones de tu navegador."
makeSureDisabledCustomCss: "Desactiva el CSS personalizado" makeSureDisabledCustomCss: "Desactiva el CSS personalizado"
makeSureDisabledCustomCss_description: "Anular estilos puede afectar al rendimiento. Asegúrate de que el CSS personalizado o las extensiones que sobrescriben estilos no están activados." makeSureDisabledCustomCss_description: "Anular estilos puede afectar al rendimiento. Asegúrate de que el CSS personalizado o las extensiones que sobrescriben estilos no están activados."

View File

@ -1272,6 +1272,8 @@ pleaseSelectAccount: "Sélectionner un compte"
availableRoles: "Rôles disponibles" availableRoles: "Rôles disponibles"
postForm: "Formulaire de publication" postForm: "Formulaire de publication"
information: "Informations" information: "Informations"
inMinutes: "min"
inDays: "j"
_chat: _chat:
invitations: "Inviter" invitations: "Inviter"
noHistory: "Pas d'historique" noHistory: "Pas d'historique"

View File

@ -1263,6 +1263,8 @@ thereAreNChanges: "Ada {n} perubahan"
prohibitedWordsForNameOfUser: "Kata yang dilarang untuk nama pengguna" prohibitedWordsForNameOfUser: "Kata yang dilarang untuk nama pengguna"
postForm: "Buat catatan" postForm: "Buat catatan"
information: "Informasi" information: "Informasi"
inMinutes: "menit"
inDays: "hari"
_chat: _chat:
invitations: "Undang" invitations: "Undang"
noHistory: "Tidak ada riwayat" noHistory: "Tidak ada riwayat"

84
locales/index.d.ts vendored
View File

@ -315,11 +315,11 @@ export interface Locale extends ILocale {
*/ */
"mention": string; "mention": string;
/** /**
* *
*/ */
"mentions": string; "mentions": string;
/** /**
* 稿 *
*/ */
"directNotes": string; "directNotes": string;
/** /**
@ -2567,11 +2567,11 @@ export interface Locale extends ILocale {
*/ */
"serviceworkerInfo": string; "serviceworkerInfo": string;
/** /**
* 稿 *
*/ */
"deletedNote": string; "deletedNote": string;
/** /**
* 稿 *
*/ */
"invisibleNote": string; "invisibleNote": string;
/** /**
@ -5231,7 +5231,7 @@ export interface Locale extends ILocale {
*/ */
"prohibitedWordsForNameOfUser": string; "prohibitedWordsForNameOfUser": string;
/** /**
* * (username)
*/ */
"prohibitedWordsForNameOfUserDescription": string; "prohibitedWordsForNameOfUserDescription": string;
/** /**
@ -5493,6 +5493,30 @@ export interface Locale extends ILocale {
* <br> * <br>
*/ */
"defaultImageCompressionLevel_description": string; "defaultImageCompressionLevel_description": string;
/**
*
*/
"inMinutes": string;
/**
*
*/
"inDays": string;
/**
*
*/
"safeModeEnabled": string;
/**
*
*/
"pluginsAreDisabledBecauseSafeMode": string;
/**
* CSSは適用されていません
*/
"customCssIsDisabledBecauseSafeMode": string;
/**
* 使
*/
"themeIsDefaultBecauseSafeMode": string;
"_order": { "_order": {
/** /**
* *
@ -6329,7 +6353,7 @@ export interface Locale extends ILocale {
*/ */
"followers": string; "followers": string;
/** /**
* 使 *
*/ */
"direct": string; "direct": string;
/** /**
@ -6337,7 +6361,7 @@ export interface Locale extends ILocale {
*/ */
"doNotSendConfidencialOnDirect1": string; "doNotSendConfidencialOnDirect1": string;
/** /**
* 稿稿 * 稿
*/ */
"doNotSendConfidencialOnDirect2": string; "doNotSendConfidencialOnDirect2": string;
/** /**
@ -6486,6 +6510,22 @@ export interface Locale extends ILocale {
* Redisのメモリ使用量は増加します * Redisのメモリ使用量は増加します
*/ */
"reactionsBufferingDescription": string; "reactionsBufferingDescription": string;
/**
* 稿
*/
"remoteNotesCleaning": string;
/**
* 稿
*/
"remoteNotesCleaning_description": string;
/**
*
*/
"remoteNotesCleaningMaxProcessingDuration": string;
/**
*
*/
"remoteNotesCleaningExpiryDaysForEachNotes": string;
/** /**
* URL * URL
*/ */
@ -6558,6 +6598,14 @@ export interface Locale extends ILocale {
* *
*/ */
"userGeneratedContentsVisibilityForVisitor_description2": string; "userGeneratedContentsVisibilityForVisitor_description2": string;
/**
*
*/
"restartServerSetupWizardConfirm_title": string;
/**
*
*/
"restartServerSetupWizardConfirm_text": string;
"_userGeneratedContentsVisibilityForVisitor": { "_userGeneratedContentsVisibilityForVisitor": {
/** /**
* *
@ -7795,6 +7843,10 @@ export interface Locale extends ILocale {
* *
*/ */
"noteDraftLimit": string; "noteDraftLimit": string;
/**
* 使
*/
"watermarkAvailable": string;
}; };
"_condition": { "_condition": {
/** /**
@ -9589,7 +9641,7 @@ export interface Locale extends ILocale {
*/ */
"followersDescription": string; "followersDescription": string;
/** /**
* *
*/ */
"specified": string; "specified": string;
/** /**
@ -10478,11 +10530,11 @@ export interface Locale extends ILocale {
*/ */
"channel": string; "channel": string;
/** /**
* *
*/ */
"mentions": string; "mentions": string;
/** /**
* *
*/ */
"direct": string; "direct": string;
/** /**
@ -11803,6 +11855,10 @@ export interface Locale extends ILocale {
* *
*/ */
"otherOption3": string; "otherOption3": string;
/**
* Misskeyをセーフモードで起動
*/
"otherOption4": string;
}; };
"_search": { "_search": {
/** /**
@ -11939,6 +11995,14 @@ export interface Locale extends ILocale {
* *
*/ */
"youCanConfigureMoreFederationSettingsLater": string; "youCanConfigureMoreFederationSettingsLater": string;
/**
*
*/
"remoteContentsCleaning": string;
/**
*
*/
"remoteContentsCleaning_description": string;
/** /**
* *
*/ */

View File

@ -1313,6 +1313,7 @@ availableRoles: "Ruoli disponibili"
acknowledgeNotesAndEnable: "Attivare dopo averne compreso il comportamento." acknowledgeNotesAndEnable: "Attivare dopo averne compreso il comportamento."
federationSpecified: "Questo server è federato solo con istanze specifiche del Fediverso. Puoi interagire solo con quelle scelte dall'amministrazione." federationSpecified: "Questo server è federato solo con istanze specifiche del Fediverso. Puoi interagire solo con quelle scelte dall'amministrazione."
federationDisabled: "Questo server ha la federazione disabilitata. Non puoi interagire con profili provenienti da altri server." federationDisabled: "Questo server ha la federazione disabilitata. Non puoi interagire con profili provenienti da altri server."
draft: "Bozza"
confirmOnReact: "Confermare le reazioni" confirmOnReact: "Confermare le reazioni"
reactAreYouSure: "Vuoi davvero reagire con {emoji} ?" reactAreYouSure: "Vuoi davvero reagire con {emoji} ?"
markAsSensitiveConfirm: "Vuoi davvero indicare questo contenuto multimediale come esplicito?" markAsSensitiveConfirm: "Vuoi davvero indicare questo contenuto multimediale come esplicito?"
@ -1367,6 +1368,11 @@ redisplayAllTips: "Mostra tutti i suggerimenti"
hideAllTips: "Nascondi tutti i suggerimenti" hideAllTips: "Nascondi tutti i suggerimenti"
defaultImageCompressionLevel: "Livello predefinito di compressione immagini" defaultImageCompressionLevel: "Livello predefinito di compressione immagini"
defaultImageCompressionLevel_description: "La compressione diminuisce la qualità dell'immagine, poca compressione mantiene alta qualità delle immagini. Aumentandola, si riducono le dimensioni del file, a discapito della qualità dell'immagine." defaultImageCompressionLevel_description: "La compressione diminuisce la qualità dell'immagine, poca compressione mantiene alta qualità delle immagini. Aumentandola, si riducono le dimensioni del file, a discapito della qualità dell'immagine."
inMinutes: "min"
inDays: "giorni"
_order:
newest: "Prima i più recenti"
oldest: "Meno recenti prima"
_chat: _chat:
noMessagesYet: "Ancora nessun messaggio" noMessagesYet: "Ancora nessun messaggio"
newMessage: "Nuovo messaggio" newMessage: "Nuovo messaggio"
@ -1993,6 +1999,8 @@ _role:
uploadableFileTypes: "Tipi di file caricabili" uploadableFileTypes: "Tipi di file caricabili"
uploadableFileTypes_caption: "Specifica il tipo MIME. Puoi specificare più valori separandoli andando a capo, oppure indicare caratteri jolly con un asterisco (*). Ad esempio: image/*" uploadableFileTypes_caption: "Specifica il tipo MIME. Puoi specificare più valori separandoli andando a capo, oppure indicare caratteri jolly con un asterisco (*). Ad esempio: image/*"
uploadableFileTypes_caption2: "A seconda del file, il tipo potrebbe non essere determinato. Se si desidera consentire tali file, aggiungere {x} alla specifica." uploadableFileTypes_caption2: "A seconda del file, il tipo potrebbe non essere determinato. Se si desidera consentire tali file, aggiungere {x} alla specifica."
noteDraftLimit: "Numero massimo di Note in bozza, lato server"
watermarkAvailable: "Disponibilità della funzione filigrana"
_condition: _condition:
roleAssignedTo: "Assegnato a ruoli manualmente" roleAssignedTo: "Assegnato a ruoli manualmente"
isLocal: "Profilo locale" isLocal: "Profilo locale"
@ -2152,6 +2160,7 @@ _theme:
install: "Installa un tema" install: "Installa un tema"
manage: "Gestione dei temi" manage: "Gestione dei temi"
code: "Codice tema" code: "Codice tema"
copyThemeCode: "Copia il codice del Tema"
description: "Descrizione" description: "Descrizione"
installed: "{name} è installato" installed: "{name} è installato"
installedThemes: "Temi installati" installedThemes: "Temi installati"
@ -2800,6 +2809,7 @@ _fileViewer:
url: "URL" url: "URL"
uploadedAt: "Caricato il" uploadedAt: "Caricato il"
attachedNotes: "Note a cui è allegato" attachedNotes: "Note a cui è allegato"
usage: "In uso"
thisPageCanBeSeenFromTheAuthor: "Questa pagina può essere vista solo da chi ha caricato il file." thisPageCanBeSeenFromTheAuthor: "Questa pagina può essere vista solo da chi ha caricato il file."
_externalResourceInstaller: _externalResourceInstaller:
title: "Installa da sito esterno" title: "Installa da sito esterno"
@ -3103,6 +3113,7 @@ _serverSetupWizard:
text2: "Se puoi, ti preghiamo di prendere in considerazione l'idea di fare una donazione, così potremo continuare a sviluppare." text2: "Se puoi, ti preghiamo di prendere in considerazione l'idea di fare una donazione, così potremo continuare a sviluppare."
text3: "Sono previsti anche dei vantaggi speciali per i sostenitori!" text3: "Sono previsti anche dei vantaggi speciali per i sostenitori!"
_uploader: _uploader:
editImage: "Modifica immagine"
compressedToX: "Compresso in {x}" compressedToX: "Compresso in {x}"
savedXPercent: "{x}% risparmiati" savedXPercent: "{x}% risparmiati"
abortConfirm: "Alcuni file non sono stati caricati. Vuoi annullare l'operazione?" abortConfirm: "Alcuni file non sono stati caricati. Vuoi annullare l'operazione?"
@ -3169,5 +3180,20 @@ _imageEffector:
stripe: "Strisce" stripe: "Strisce"
polkadot: "A pallini" polkadot: "A pallini"
checker: "revisore" checker: "revisore"
blockNoise: "Attenua rumore"
tearing: "Strappa immagine"
drafts: "Bozza"
_drafts: _drafts:
select: "Selezionare bozza"
cannotCreateDraftAnymore: "Hai superato il numero massimo di bozze ammissibili."
cannotCreateDraft: "Impossibile creare una bozza di questo contenuto."
delete: "Elimina bozza"
deleteAreYouSure: "Vuoi davvero eliminare la bozza?"
noDrafts: "Non c'è nessuna bozza."
replyTo: "Rispondere a {user}"
quoteOf: "Citare la nota di {user}"
postTo: "Inserire in {channel}"
saveToDraft: "Salva come bozza"
restoreFromDraft: "Recuperare dalle bozze"
restore: "Ripristina" restore: "Ripristina"
listDrafts: "Elenco bozze"

View File

@ -74,8 +74,8 @@ youGotNewFollower: "フォローされました"
receiveFollowRequest: "フォローリクエストされました" receiveFollowRequest: "フォローリクエストされました"
followRequestAccepted: "フォローが承認されました" followRequestAccepted: "フォローが承認されました"
mention: "メンション" mention: "メンション"
mentions: "あなた宛て" mentions: "メンション"
directNotes: "ダイレクト投稿" directNotes: "指名"
importAndExport: "インポートとエクスポート" importAndExport: "インポートとエクスポート"
import: "インポート" import: "インポート"
export: "エクスポート" export: "エクスポート"
@ -637,8 +637,8 @@ addRelay: "リレーの追加"
inboxUrl: "inboxのURL" inboxUrl: "inboxのURL"
addedRelays: "追加済みのリレー" addedRelays: "追加済みのリレー"
serviceworkerInfo: "プッシュ通知を行うには有効にする必要があります。" serviceworkerInfo: "プッシュ通知を行うには有効にする必要があります。"
deletedNote: "削除された投稿" deletedNote: "削除されたノート"
invisibleNote: "非公開の投稿" invisibleNote: "非公開のノート"
enableInfiniteScroll: "自動でもっと見る" enableInfiniteScroll: "自動でもっと見る"
visibility: "公開範囲" visibility: "公開範囲"
poll: "アンケート" poll: "アンケート"
@ -1303,7 +1303,7 @@ messageToFollower: "フォロワーへのメッセージ"
target: "対象" target: "対象"
testCaptchaWarning: "CAPTCHAのテストを目的とした機能です。<strong>本番環境で使用しないでください。</strong>" testCaptchaWarning: "CAPTCHAのテストを目的とした機能です。<strong>本番環境で使用しないでください。</strong>"
prohibitedWordsForNameOfUser: "禁止ワード(ユーザーの名前)" prohibitedWordsForNameOfUser: "禁止ワード(ユーザーの名前)"
prohibitedWordsForNameOfUserDescription: "このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。" prohibitedWordsForNameOfUserDescription: "このリストに含まれる文字列がユーザーの名前に含まれる場合、ユーザーの名前の変更を拒否します。モデレーター権限を持つユーザーはこの制限の影響を受けません。ユーザー名(username)に対しても全て小文字に置き換えて検査します。"
yourNameContainsProhibitedWords: "変更しようとした名前に禁止された文字列が含まれています" yourNameContainsProhibitedWords: "変更しようとした名前に禁止された文字列が含まれています"
yourNameContainsProhibitedWordsDescription: "名前に禁止されている文字列が含まれています。この名前を使用したい場合は、サーバー管理者にお問い合わせください。" yourNameContainsProhibitedWordsDescription: "名前に禁止されている文字列が含まれています。この名前を使用したい場合は、サーバー管理者にお問い合わせください。"
thisContentsAreMarkedAsSigninRequiredByAuthor: "投稿者により、表示にはログインが必要と設定されています" thisContentsAreMarkedAsSigninRequiredByAuthor: "投稿者により、表示にはログインが必要と設定されています"
@ -1368,6 +1368,12 @@ redisplayAllTips: "全ての「ヒントとコツ」を再表示"
hideAllTips: "全ての「ヒントとコツ」を非表示" hideAllTips: "全ての「ヒントとコツ」を非表示"
defaultImageCompressionLevel: "デフォルトの画像圧縮度" defaultImageCompressionLevel: "デフォルトの画像圧縮度"
defaultImageCompressionLevel_description: "低くすると画質を保てますが、ファイルサイズは増加します。<br>高くするとファイルサイズを減らせますが、画質は低下します。" defaultImageCompressionLevel_description: "低くすると画質を保てますが、ファイルサイズは増加します。<br>高くするとファイルサイズを減らせますが、画質は低下します。"
inMinutes: "分"
inDays: "日"
safeModeEnabled: "セーフモードが有効です"
pluginsAreDisabledBecauseSafeMode: "セーフモードが有効なため、プラグインはすべて無効化されています。"
customCssIsDisabledBecauseSafeMode: "セーフモードが有効なため、カスタムCSSは適用されていません。"
themeIsDefaultBecauseSafeMode: "セーフモードが有効な間はデフォルトのテーマが使用されます。セーフモードをオフにすると元に戻ります。"
_order: _order:
newest: "新しい順" newest: "新しい順"
@ -1603,9 +1609,9 @@ _initialTutorial:
public: "すべてのユーザーに公開。" public: "すべてのユーザーに公開。"
home: "ホームタイムラインのみに公開。フォロワー・プロフィールを見に来た人・リノートから、他のユーザーも見ることができます。" home: "ホームタイムラインのみに公開。フォロワー・プロフィールを見に来た人・リノートから、他のユーザーも見ることができます。"
followers: "フォロワーにのみ公開。本人以外がリノートすることはできず、またフォロワー以外は閲覧できません。" followers: "フォロワーにのみ公開。本人以外がリノートすることはできず、またフォロワー以外は閲覧できません。"
direct: "指定したユーザーにのみ公開され、また相手に通知が入ります。ダイレクトメッセージのかわりにお使いいただけます。" direct: "指定したユーザーにのみ公開され、また相手に通知が入ります。"
doNotSendConfidencialOnDirect1: "機密情報は送信する際は注意してください。" doNotSendConfidencialOnDirect1: "機密情報は送信する際は注意してください。"
doNotSendConfidencialOnDirect2: "送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーにダイレクト投稿を送信する場合は、機密情報の扱いに注意が必要です。" doNotSendConfidencialOnDirect2: "送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーが含まれる限定公開のノートを作成する際は、機密情報の扱いに注意が必要です。"
localOnly: "他のサーバーに投稿を連合しません。上記の公開範囲に関わらず、他のサーバーのユーザーは、この設定がついたノートを直接閲覧することができなくなります。" localOnly: "他のサーバーに投稿を連合しません。上記の公開範囲に関わらず、他のサーバーのユーザーは、この設定がついたノートを直接閲覧することができなくなります。"
_cw: _cw:
title: "内容を隠すCW" title: "内容を隠すCW"
@ -1649,6 +1655,10 @@ _serverSettings:
fanoutTimelineDbFallback: "データベースへのフォールバック" fanoutTimelineDbFallback: "データベースへのフォールバック"
fanoutTimelineDbFallbackDescription: "有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。" fanoutTimelineDbFallbackDescription: "有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。"
reactionsBufferingDescription: "有効にすると、リアクション作成時のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。" reactionsBufferingDescription: "有効にすると、リアクション作成時のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。"
remoteNotesCleaning: "リモート投稿の自動クリーニング"
remoteNotesCleaning_description: "有効にすると、参照されていない古いリモートの投稿を定期的にクリーンアップしてデータベースの肥大化を抑制します。"
remoteNotesCleaningMaxProcessingDuration: "最大クリーニング処理継続時間"
remoteNotesCleaningExpiryDaysForEachNotes: "最低ノート保持日数"
inquiryUrl: "問い合わせ先URL" inquiryUrl: "問い合わせ先URL"
inquiryUrlDescription: "サーバー運営者へのお問い合わせフォームのURLや、運営者の連絡先等が記載されたWebページのURLを指定します。" inquiryUrlDescription: "サーバー運営者へのお問い合わせフォームのURLや、運営者の連絡先等が記載されたWebページのURLを指定します。"
openRegistration: "アカウントの作成をオープンにする" openRegistration: "アカウントの作成をオープンにする"
@ -1667,6 +1677,8 @@ _serverSettings:
userGeneratedContentsVisibilityForVisitor: "非利用者に対するユーザー作成コンテンツの公開範囲" userGeneratedContentsVisibilityForVisitor: "非利用者に対するユーザー作成コンテンツの公開範囲"
userGeneratedContentsVisibilityForVisitor_description: "モデレーションが行き届きにくい不適切なリモートコンテンツなどが、自サーバー経由で図らずもインターネットに公開されてしまうことによるトラブル防止などに役立ちます。" userGeneratedContentsVisibilityForVisitor_description: "モデレーションが行き届きにくい不適切なリモートコンテンツなどが、自サーバー経由で図らずもインターネットに公開されてしまうことによるトラブル防止などに役立ちます。"
userGeneratedContentsVisibilityForVisitor_description2: "サーバーで受信したリモートのコンテンツを含め、サーバー内の全てのコンテンツを無条件でインターネットに公開することはリスクが伴います。特に、分散型の特性を知らない閲覧者にとっては、リモートのコンテンツであってもサーバー内で作成されたコンテンツであると誤って認識してしまう可能性があるため、注意が必要です。" userGeneratedContentsVisibilityForVisitor_description2: "サーバーで受信したリモートのコンテンツを含め、サーバー内の全てのコンテンツを無条件でインターネットに公開することはリスクが伴います。特に、分散型の特性を知らない閲覧者にとっては、リモートのコンテンツであってもサーバー内で作成されたコンテンツであると誤って認識してしまう可能性があるため、注意が必要です。"
restartServerSetupWizardConfirm_title: "サーバーの初期設定ウィザードをやり直しますか?"
restartServerSetupWizardConfirm_text: "現在の一部の設定はリセットされます。"
_userGeneratedContentsVisibilityForVisitor: _userGeneratedContentsVisibilityForVisitor:
all: "全て公開" all: "全て公開"
@ -2019,6 +2031,7 @@ _role:
uploadableFileTypes_caption: "MIMEタイプを指定します。改行で区切って複数指定できるほか、アスタリスク(*)でワイルドカード指定できます。(例: image/*)" uploadableFileTypes_caption: "MIMEタイプを指定します。改行で区切って複数指定できるほか、アスタリスク(*)でワイルドカード指定できます。(例: image/*)"
uploadableFileTypes_caption2: "ファイルによっては種別を判定できないことがあります。そのようなファイルを許可する場合は {x} を指定に追加してください。" uploadableFileTypes_caption2: "ファイルによっては種別を判定できないことがあります。そのようなファイルを許可する場合は {x} を指定に追加してください。"
noteDraftLimit: "サーバーサイドのノートの下書きの作成可能数" noteDraftLimit: "サーバーサイドのノートの下書きの作成可能数"
watermarkAvailable: "ウォーターマーク機能の使用可否"
_condition: _condition:
roleAssignedTo: "マニュアルロールにアサイン済み" roleAssignedTo: "マニュアルロールにアサイン済み"
isLocal: "ローカルユーザー" isLocal: "ローカルユーザー"
@ -2523,7 +2536,7 @@ _visibility:
homeDescription: "ホームタイムラインのみに公開" homeDescription: "ホームタイムラインのみに公開"
followers: "フォロワー" followers: "フォロワー"
followersDescription: "自分のフォロワーのみに公開" followersDescription: "自分のフォロワーのみに公開"
specified: "ダイレクト" specified: "指名"
specifiedDescription: "指定したユーザーのみに公開" specifiedDescription: "指定したユーザーのみに公開"
disableFederation: "連合なし" disableFederation: "連合なし"
disableFederationDescription: "他サーバーへの配信を行いません" disableFederationDescription: "他サーバーへの配信を行いません"
@ -2769,8 +2782,8 @@ _deck:
antenna: "アンテナ" antenna: "アンテナ"
list: "リスト" list: "リスト"
channel: "チャンネル" channel: "チャンネル"
mentions: "あなた宛て" mentions: "メンション"
direct: "ダイレクト" direct: "指名"
roleTimeline: "ロールタイムライン" roleTimeline: "ロールタイムライン"
chat: "チャット" chat: "チャット"
@ -3155,6 +3168,7 @@ _bootErrors:
otherOption1: "クライアント設定とキャッシュを削除" otherOption1: "クライアント設定とキャッシュを削除"
otherOption2: "簡易クライアントを起動" otherOption2: "簡易クライアントを起動"
otherOption3: "修復ツールを起動" otherOption3: "修復ツールを起動"
otherOption4: "Misskeyをセーフモードで起動"
_search: _search:
searchScopeAll: "全て" searchScopeAll: "全て"
@ -3193,6 +3207,8 @@ _serverSetupWizard:
doYouConnectToFediverse_description1: "分散型サーバーで構成されるネットワーク(Fediverse)に接続すると、他のサーバーと相互にコンテンツのやり取りが可能です。" doYouConnectToFediverse_description1: "分散型サーバーで構成されるネットワーク(Fediverse)に接続すると、他のサーバーと相互にコンテンツのやり取りが可能です。"
doYouConnectToFediverse_description2: "Fediverseと接続することは「連合」とも呼ばれます。" doYouConnectToFediverse_description2: "Fediverseと接続することは「連合」とも呼ばれます。"
youCanConfigureMoreFederationSettingsLater: "連合可能なサーバーの指定など、高度な設定も後ほど可能です。" youCanConfigureMoreFederationSettingsLater: "連合可能なサーバーの指定など、高度な設定も後ほど可能です。"
remoteContentsCleaning: "受信コンテンツの自動クリーニング"
remoteContentsCleaning_description: "連合を行うと、継続して多くのコンテンツを受信します。自動クリーニングを有効にすると、参照されていない古くなったコンテンツを自動でサーバーから削除し、ストレージを節約できます。"
adminInfo: "管理者情報" adminInfo: "管理者情報"
adminInfo_description: "問い合わせを受け付けるために使用される管理者情報を設定します。" adminInfo_description: "問い合わせを受け付けるために使用される管理者情報を設定します。"
adminInfo_mustBeFilled: "オープンサーバー、または連合がオンの場合は必ず入力が必要です。" adminInfo_mustBeFilled: "オープンサーバー、または連合がオンの場合は必ず入力が必要です。"

View File

@ -300,6 +300,7 @@ uploadFromUrlMayTakeTime: "アップロード終わるんにちょい時間か
explore: "みつける" explore: "みつける"
messageRead: "もう読んだ" messageRead: "もう読んだ"
noMoreHistory: "これより昔のんはあらへんで" noMoreHistory: "これより昔のんはあらへんで"
startChat: "チャットを始めよか"
nUsersRead: "{n}人が読んでもうた" nUsersRead: "{n}人が読んでもうた"
agreeTo: "{0}に同意したで" agreeTo: "{0}に同意したで"
agree: "せやな" agree: "せやな"
@ -324,6 +325,7 @@ dark: "ダーク"
lightThemes: "デイゲーム" lightThemes: "デイゲーム"
darkThemes: "ナイトゲーム" darkThemes: "ナイトゲーム"
syncDeviceDarkMode: "デバイスのダークモードと一緒にする" syncDeviceDarkMode: "デバイスのダークモードと一緒にする"
switchDarkModeManuallyWhenSyncEnabledConfirm: "「{x}」がオンになってるで。同期をオフにして手動でモードを切り替えることにします?"
drive: "ドライブ" drive: "ドライブ"
fileName: "ファイル名" fileName: "ファイル名"
selectFile: "ファイル選んでや" selectFile: "ファイル選んでや"
@ -422,6 +424,7 @@ antennaExcludeBots: "Botアカウントを除外"
antennaKeywordsDescription: "スペースで区切ったるとAND指定で、改行で区切ったるとOR指定や" antennaKeywordsDescription: "スペースで区切ったるとAND指定で、改行で区切ったるとOR指定や"
notifyAntenna: "新しいノートを通知すんで" notifyAntenna: "新しいノートを通知すんで"
withFileAntenna: "なんか添付されたノートだけ" withFileAntenna: "なんか添付されたノートだけ"
excludeNotesInSensitiveChannel: "センシティブなチャンネルのノートは入れんとくわ"
enableServiceworker: "ブラウザにプッシュ通知が行くようにする" enableServiceworker: "ブラウザにプッシュ通知が行くようにする"
antennaUsersDescription: "ユーザー名を改行で区切ったってな" antennaUsersDescription: "ユーザー名を改行で区切ったってな"
caseSensitive: "大文字と小文字は別もんや" caseSensitive: "大文字と小文字は別もんや"
@ -693,6 +696,7 @@ userSaysSomethingAbout: "{name}が「{word}」についてなんか言うてた
makeActive: "使うで" makeActive: "使うで"
display: "表示" display: "表示"
copy: "コピー" copy: "コピー"
copiedToClipboard: "クリップボードにコピーされたで"
metrics: "メトリクス" metrics: "メトリクス"
overview: "概要" overview: "概要"
logs: "ログ" logs: "ログ"
@ -787,6 +791,7 @@ wide: "広い"
narrow: "狭い" narrow: "狭い"
reloadToApplySetting: "設定はページリロード後に反映されるで。今リロードしとくか?" reloadToApplySetting: "設定はページリロード後に反映されるで。今リロードしとくか?"
needReloadToApply: "反映には再起動せなあかんで" needReloadToApply: "反映には再起動せなあかんで"
needToRestartServerToApply: "反映にはサーバーを再起動せなあかんのよ。"
showTitlebar: "タイトルバーを見せる" showTitlebar: "タイトルバーを見せる"
clearCache: "キャッシュをほかす" clearCache: "キャッシュをほかす"
onlineUsersCount: "{n}人が起きとるで" onlineUsersCount: "{n}人が起きとるで"
@ -974,6 +979,7 @@ document: "ドキュメント"
numberOfPageCache: "ページ、どんだけキャッシュすんの?" numberOfPageCache: "ページ、どんだけキャッシュすんの?"
numberOfPageCacheDescription: "増やすと使いやすくなるけど、負荷とメモリ使用量が増えてくで。一長一短やな。" numberOfPageCacheDescription: "増やすと使いやすくなるけど、負荷とメモリ使用量が増えてくで。一長一短やな。"
logoutConfirm: "ログアウトしまっか?" logoutConfirm: "ログアウトしまっか?"
logoutWillClearClientData: "ログアウトするとクライアントの設定情報がブラウザから消されてまうで。再ログイン時に設定情報を復元できるようにするためには、設定の自動バックアップを有効にするとええで。"
lastActiveDate: "最後に使った日時" lastActiveDate: "最後に使った日時"
statusbar: "ステータスバー" statusbar: "ステータスバー"
pleaseSelect: "選んだってやー" pleaseSelect: "選んだってやー"
@ -992,6 +998,7 @@ failedToUpload: "アップロードに失敗してもうたわ…"
cannotUploadBecauseInappropriate: "きわどい内容を含むかもしれへんって言われたからアップロードできへんわ。" cannotUploadBecauseInappropriate: "きわどい内容を含むかもしれへんって言われたからアップロードできへんわ。"
cannotUploadBecauseNoFreeSpace: "ドライブがもうパンパンやからアップロードできへんわ。" cannotUploadBecauseNoFreeSpace: "ドライブがもうパンパンやからアップロードできへんわ。"
cannotUploadBecauseExceedsFileSizeLimit: "ファイルが思うたよりも大きいさかいアップロードできへんでこれ。" cannotUploadBecauseExceedsFileSizeLimit: "ファイルが思うたよりも大きいさかいアップロードできへんでこれ。"
cannotUploadBecauseUnallowedFileType: "許可されてへんファイル種別やからアップロードできへんっぽい。"
beta: "ベータ" beta: "ベータ"
enableAutoSensitive: "自動できわどいか判断する" enableAutoSensitive: "自動できわどいか判断する"
enableAutoSensitiveDescription: "使える時は、機械学習を使って自動でメディアにNSFWフラグを設定するで。この機能をオフにしても、サーバーによっては自動で設定されることがあるで。" enableAutoSensitiveDescription: "使える時は、機械学習を使って自動でメディアにNSFWフラグを設定するで。この機能をオフにしても、サーバーによっては自動で設定されることがあるで。"
@ -1304,11 +1311,37 @@ federationSpecified: "このサーバーはホワイトリスト連合で運用
federationDisabled: "このサーバーは連合が無効化されてるで。他のサーバーのユーザーとやり取りすることはできひんで。" federationDisabled: "このサーバーは連合が無効化されてるで。他のサーバーのユーザーとやり取りすることはできひんで。"
confirmOnReact: "ツッコむときに確認とる" confirmOnReact: "ツッコむときに確認とる"
reactAreYouSure: "\" {emoji} \" でツッコむ?" reactAreYouSure: "\" {emoji} \" でツッコむ?"
markAsSensitiveConfirm: "このメディアをきわどい扱いしときますか?"
unmarkAsSensitiveConfirm: "このメディアはやっぱきわどくなかったってことでええんか?"
noName: "名前はあらへんで"
preferenceSyncConflictTitle: "サーバーに設定値があるみたいやわ"
preferenceSyncConflictText: "同期が有効にされた設定項目は設定値をサーバーに保存するねんけど、この設定項目はサーバーに保存されたやつがあるみたいやわ。どないするん?"
preferenceSyncConflictChoiceMerge: "ガッチャンコしよか"
preferenceSyncConflictChoiceCancel: "同期の有効化はやめとくわ"
postForm: "投稿フォーム" postForm: "投稿フォーム"
information: "情報" information: "情報"
migrateOldSettings: "旧設定情報をお引っ越し"
migrateOldSettings_description: "通常これは自動で行われるはずなんやけど、なんかの理由で上手く移行できへんかったときは手動で移行処理をポチっとできるで。今の設定情報は上書きされるで。"
settingsMigrating: "設定を移行しとるで。ちょっと待っとってな... (後で、設定→その他→旧設定情報を移行 で手動で移行することもできるで)"
driveAboutTip: "ドライブでは、今までアップロードしたファイルがずらーっと表示されるで。<br>\nートにファイルをもっかいのっけたり、あとで投稿するファイルをその辺に置いとくこともできるねん。<br>\n<b>ファイルをほかすと、前にそのファイルをのっけた全部の場所(ノート、ページ、アバター、バナー等)からも見えんくなるから気いつけてな。</b><br>\nフォルダを作って整理することもできるで。"
turnItOn: "オンにしとこ"
turnItOff: "オフでええわ"
emojiUnmute: "絵文字ミュートやめたる"
unmuteX: "{x}のミュートやめたる"
redisplayAllTips: "全部の「ヒントとコツ」をもっかい見して"
hideAllTips: "「ヒントとコツ」は全部表示せんでええ"
defaultImageCompressionLevel_description: "低くすると画質は保てるんやけど、ファイルサイズが増えるで。<br>高くするとファイルサイズは減らせるんやけど、画質が落ちるで。"
inMinutes: "分"
inDays: "日"
_chat: _chat:
noMessagesYet: "まだメッセージはあらへんで"
individualChat_description: "特定のユーザーと一対一でチャットができるで。"
roomChat_description: "複数人でチャットできるで。\nあと、個人チャットを許可してへんユーザーとでも、相手がええって言うならチャットできるで。"
inviteUserToChat: "ユーザーを招待してチャットを始めてみ"
invitations: "来てや" invitations: "来てや"
noInvitations: "招待はあらへんで"
noHistory: "履歴はないわ。" noHistory: "履歴はないわ。"
noRooms: "ルームはあらへんで"
members: "メンバーはん" members: "メンバーはん"
home: "ホーム" home: "ホーム"
send: "送信" send: "送信"
@ -2617,7 +2650,7 @@ _externalResourceInstaller:
_errors: _errors:
_invalidParams: _invalidParams:
title: "" title: ""
description: "" description: "外部サイトからデータを持ってくるのに欲しい情報が足らへんみたいやわ。URLは合っとる"
_resourceTypeNotSupported: _resourceTypeNotSupported:
title: "" title: ""
description: "" description: ""
@ -2648,7 +2681,7 @@ _dataSaver:
title: "アイコンの絵" title: "アイコンの絵"
description: "アイコン画像のアニメが止まるで。普通の画像よりもデータ量がでかいから、もっと通信量を節約できるねん。" description: "アイコン画像のアニメが止まるで。普通の画像よりもデータ量がでかいから、もっと通信量を節約できるねん。"
_code: _code:
title: "コードハイライト" title: "コードハイライトは表示せんでええ"
description: "MFMとかでコードハイライト記法が使われてるとき、タップするまで読み込まれへんくなるで。コードハイライトではハイライトする言語ごとにその決めてるファイルを読む必要はあんねんな。けどな、それは自動で読み込まれなくなるから、通信量を少なくできることができるねん。" description: "MFMとかでコードハイライト記法が使われてるとき、タップするまで読み込まれへんくなるで。コードハイライトではハイライトする言語ごとにその決めてるファイルを読む必要はあんねんな。けどな、それは自動で読み込まれなくなるから、通信量を少なくできることができるねん。"
_hemisphere: _hemisphere:
N: "北半球" N: "北半球"
@ -2858,3 +2891,8 @@ _watermarkEditor:
image: "画像" image: "画像"
advanced: "高度" advanced: "高度"
angle: "角度" angle: "角度"
_imageEffector:
discardChangesConfirm: "変更をせんで終わるか?"
_drafts:
deleteAreYouSure: "下書きをほかしてもええか?"
noDrafts: "下書きはあらへん"

View File

@ -1368,6 +1368,8 @@ redisplayAllTips: "모든 '팁과 유용한 정보'를 재표시"
hideAllTips: "모든 '팁과 유용한 정보'를 비표시" hideAllTips: "모든 '팁과 유용한 정보'를 비표시"
defaultImageCompressionLevel: "기본 이미지 압축 정도" defaultImageCompressionLevel: "기본 이미지 압축 정도"
defaultImageCompressionLevel_description: "낮추면 화질을 유지합니다만 파일 크기는 증가합니다. <br>높이면 파일 크기를 줄일 수 있습니다만 화질은 저하됩니다." defaultImageCompressionLevel_description: "낮추면 화질을 유지합니다만 파일 크기는 증가합니다. <br>높이면 파일 크기를 줄일 수 있습니다만 화질은 저하됩니다."
inMinutes: "분"
inDays: "일"
_order: _order:
newest: "최신 순" newest: "최신 순"
oldest: "오래된 순" oldest: "오래된 순"
@ -1998,6 +2000,7 @@ _role:
uploadableFileTypes_caption: "MIME 유형을 " uploadableFileTypes_caption: "MIME 유형을 "
uploadableFileTypes_caption2: "파일에 따라서는 유형을 검사하지 못하는 경우가 있습니다. 그러한 파일을 허가하는 경우에는 {x}를 지정으로 추가해주십시오." uploadableFileTypes_caption2: "파일에 따라서는 유형을 검사하지 못하는 경우가 있습니다. 그러한 파일을 허가하는 경우에는 {x}를 지정으로 추가해주십시오."
noteDraftLimit: "서버측 노트 초안 작성 가능 수" noteDraftLimit: "서버측 노트 초안 작성 가능 수"
watermarkAvailable: "워터마크 기능의 사용 여부"
_condition: _condition:
roleAssignedTo: "수동 역할에 이미 할당됨" roleAssignedTo: "수동 역할에 이미 할당됨"
isLocal: "로컬 유저" isLocal: "로컬 유저"
@ -2806,6 +2809,7 @@ _fileViewer:
url: "URL" url: "URL"
uploadedAt: "업로드 날짜" uploadedAt: "업로드 날짜"
attachedNotes: "첨부된 노트" attachedNotes: "첨부된 노트"
usage: "이용"
thisPageCanBeSeenFromTheAuthor: "이 페이지는 파일 소유자만 열람할 수 있습니다" thisPageCanBeSeenFromTheAuthor: "이 페이지는 파일 소유자만 열람할 수 있습니다"
_externalResourceInstaller: _externalResourceInstaller:
title: "외부 사이트로부터 설치" title: "외부 사이트로부터 설치"
@ -3182,6 +3186,7 @@ drafts: "초안"
_drafts: _drafts:
select: "초안 선택" select: "초안 선택"
cannotCreateDraftAnymore: "초안 작성 가능 수를 초과했습니다." cannotCreateDraftAnymore: "초안 작성 가능 수를 초과했습니다."
cannotCreateDraft: "이 내용으로는 초안을 작성할 수 없습니다. "
delete: "초안 삭제\n" delete: "초안 삭제\n"
deleteAreYouSure: "초안을 삭제하시겠습니까?" deleteAreYouSure: "초안을 삭제하시겠습니까?"
noDrafts: "초안 없음\n" noDrafts: "초안 없음\n"

View File

@ -461,6 +461,8 @@ replies: "Svar"
renotes: "Renote" renotes: "Renote"
surrender: "Avbryt" surrender: "Avbryt"
information: "Informasjon" information: "Informasjon"
inMinutes: "Minutter"
inDays: "Dager"
_chat: _chat:
invitations: "Inviter" invitations: "Inviter"
members: "Medlemmer" members: "Medlemmer"

View File

@ -1040,6 +1040,8 @@ surrender: "Odrzuć"
gameRetry: "Spróbuj ponownie" gameRetry: "Spróbuj ponownie"
postForm: "Formularz tworzenia wpisu" postForm: "Formularz tworzenia wpisu"
information: "Informacje" information: "Informacje"
inMinutes: "minuta"
inDays: "dzień"
_chat: _chat:
invitations: "Zaproś" invitations: "Zaproś"
noHistory: "Brak historii" noHistory: "Brak historii"

View File

@ -446,7 +446,7 @@ exploreUsersCount: "Há um utilizador de {count}"
exploreFediverse: "Explorar Fediverse" exploreFediverse: "Explorar Fediverse"
popularTags: "Tags populares" popularTags: "Tags populares"
userList: "Listas" userList: "Listas"
about: "Informações" about: "Sobre"
aboutMisskey: "Sobre Misskey" aboutMisskey: "Sobre Misskey"
administrator: "Administrador" administrator: "Administrador"
token: "Símbolo" token: "Símbolo"
@ -1339,7 +1339,7 @@ paste: "Colar"
emojiPalette: "Paleta de emojis" emojiPalette: "Paleta de emojis"
postForm: "Campo de postagem" postForm: "Campo de postagem"
textCount: "Contagem de caracteres" textCount: "Contagem de caracteres"
information: "Informações" information: "Sobre"
chat: "Conversas" chat: "Conversas"
migrateOldSettings: "Migrar configurações antigas de cliente" migrateOldSettings: "Migrar configurações antigas de cliente"
migrateOldSettings_description: "Isso deve ser feito automaticamente. Caso o processo de migração tenha falhado, você pode acioná-lo manualmente. As informações atuais de migração serão substituídas." migrateOldSettings_description: "Isso deve ser feito automaticamente. Caso o processo de migração tenha falhado, você pode acioná-lo manualmente. As informações atuais de migração serão substituídas."
@ -1368,6 +1368,8 @@ redisplayAllTips: "Mostrar todas as \"Dicas e Truques\" novamente"
hideAllTips: "Ocultas todas as \"Dicas e Truques\"" hideAllTips: "Ocultas todas as \"Dicas e Truques\""
defaultImageCompressionLevel: "Nível de compressão de imagem padrão" defaultImageCompressionLevel: "Nível de compressão de imagem padrão"
defaultImageCompressionLevel_description: "Alto, reduz o tamanho do arquivo mas, também, a qualidade da imagem.<br>Alto, reduz o tamanho do arquivo mas, também, a qualidade da imagem." defaultImageCompressionLevel_description: "Alto, reduz o tamanho do arquivo mas, também, a qualidade da imagem.<br>Alto, reduz o tamanho do arquivo mas, também, a qualidade da imagem."
inMinutes: "Minuto(s)"
inDays: "Dia(s)"
_order: _order:
newest: "Priorizar Mais Novos" newest: "Priorizar Mais Novos"
oldest: "Priorizar Mais Antigos" oldest: "Priorizar Mais Antigos"
@ -1998,6 +2000,7 @@ _role:
uploadableFileTypes_caption: "Especifica tipos MIME permitidos. Múltiplos tipos MIME podem ser especificados separando-os por linha. Curingas podem ser especificados com um asterisco (*). (exemplo, image/*)" uploadableFileTypes_caption: "Especifica tipos MIME permitidos. Múltiplos tipos MIME podem ser especificados separando-os por linha. Curingas podem ser especificados com um asterisco (*). (exemplo, image/*)"
uploadableFileTypes_caption2: "Alguns tipos de arquivos podem não ser detectados. Para permiti-los, adicione {x} à especificação." uploadableFileTypes_caption2: "Alguns tipos de arquivos podem não ser detectados. Para permiti-los, adicione {x} à especificação."
noteDraftLimit: "Limite de rascunhos possíveis" noteDraftLimit: "Limite de rascunhos possíveis"
watermarkAvailable: "Disponibilidade da função de marca d'água"
_condition: _condition:
roleAssignedTo: "Atribuído a cargos manuais" roleAssignedTo: "Atribuído a cargos manuais"
isLocal: "Usuário local" isLocal: "Usuário local"
@ -2806,6 +2809,7 @@ _fileViewer:
url: "URL" url: "URL"
uploadedAt: "Adicionado em" uploadedAt: "Adicionado em"
attachedNotes: "Notas anexadas" attachedNotes: "Notas anexadas"
usage: "Usado"
thisPageCanBeSeenFromTheAuthor: "Essa página só pode ser vista pelo usuário que enviou esse arquivo." thisPageCanBeSeenFromTheAuthor: "Essa página só pode ser vista pelo usuário que enviou esse arquivo."
_externalResourceInstaller: _externalResourceInstaller:
title: "Instalar de site externo" title: "Instalar de site externo"
@ -3182,5 +3186,14 @@ drafts: "Rascunhos"
_drafts: _drafts:
select: "Selecionar Rascunho" select: "Selecionar Rascunho"
cannotCreateDraftAnymore: "O número máximo de rascunhos foi excedido." cannotCreateDraftAnymore: "O número máximo de rascunhos foi excedido."
cannotCreateDraft: "Você não pode criar um rascunho com esse conteúdo."
delete: "Excluir Rascunho" delete: "Excluir Rascunho"
deleteAreYouSure: "Excluir rascunho?"
noDrafts: "Sem rascunhos"
replyTo: "Resposta a {user}"
quoteOf: "Citação à nota de {user}"
postTo: "Publicando em {channel}"
saveToDraft: "Salvar como Rascunho"
restoreFromDraft: "Restaurar de Rascunho"
restore: "Redefinir" restore: "Redefinir"
listDrafts: "Lista de Rascunhos"

View File

@ -2,7 +2,7 @@
_lang_: "Русский" _lang_: "Русский"
headlineMisskey: "Сеть, сплетённая из заметок" headlineMisskey: "Сеть, сплетённая из заметок"
introMisskey: "Добро пожаловать! Misskey — это децентрализованный сервис микроблогов с открытым исходным кодом.\nПишите «заметки» — делитесь со всеми происходящим вокруг или рассказывайте о себе 📡\nСтавьте «реакции» — выражайте свои чувства и эмоции от заметок других 👍\nОткройте для себя новый мир 🚀" introMisskey: "Добро пожаловать! Misskey — это децентрализованный сервис микроблогов с открытым исходным кодом.\nПишите «заметки» — делитесь со всеми происходящим вокруг или рассказывайте о себе 📡\nСтавьте «реакции» — выражайте свои чувства и эмоции от заметок других 👍\nОткройте для себя новый мир 🚀"
poweredByMisskeyDescription: "{name} сервис на платформе с открытым исходным кодом <b>Misskey</b>, называемый экземпляром Misskey." poweredByMisskeyDescription: "{name} один из инстансов (также называемый экземпляром Misskey), использующий платформу с открытым исходным кодом <b>Misskey</b>."
monthAndDay: "{day}.{month}" monthAndDay: "{day}.{month}"
search: "Поиск" search: "Поиск"
reset: "Сброс" reset: "Сброс"
@ -82,7 +82,7 @@ export: "Экспорт"
files: "Файлы" files: "Файлы"
download: "Скачать" download: "Скачать"
driveFileDeleteConfirm: "Удалить файл «{name}»? Заметки с ним также будут удалены." driveFileDeleteConfirm: "Удалить файл «{name}»? Заметки с ним также будут удалены."
unfollowConfirm: "Удалить из подписок пользователя {name}?" unfollowConfirm: "Отписаться от {name} ?"
exportRequested: "Вы запросили экспорт. Это может занять некоторое время. Результат будет добавлен на «Диск»." exportRequested: "Вы запросили экспорт. Это может занять некоторое время. Результат будет добавлен на «Диск»."
importRequested: "Вы запросили импорт. Это может занять некоторое время." importRequested: "Вы запросили импорт. Это может занять некоторое время."
lists: "Списки" lists: "Списки"
@ -298,6 +298,7 @@ uploadFromUrl: "Загрузить по ссылке"
uploadFromUrlDescription: "Ссылка на файл, который хотите загрузить" uploadFromUrlDescription: "Ссылка на файл, который хотите загрузить"
uploadFromUrlRequested: "Загрузка выбранного" uploadFromUrlRequested: "Загрузка выбранного"
uploadFromUrlMayTakeTime: "Загрузка может занять некоторое время." uploadFromUrlMayTakeTime: "Загрузка может занять некоторое время."
uploadNFiles: "Загрузить {n} файл"
explore: "Обзор" explore: "Обзор"
messageRead: "Прочитали" messageRead: "Прочитали"
noMoreHistory: "История закончилась" noMoreHistory: "История закончилась"
@ -575,8 +576,10 @@ showFixedPostForm: "Показывать поле для ввода новой
showFixedPostFormInChannel: "Показывать поле для ввода новой заметки наверху ленты (каналы)" showFixedPostFormInChannel: "Показывать поле для ввода новой заметки наверху ленты (каналы)"
withRepliesByDefaultForNewlyFollowed: "По умолчанию включайте ответы новых пользователей, на которых вы подписались, во временную шкалу" withRepliesByDefaultForNewlyFollowed: "По умолчанию включайте ответы новых пользователей, на которых вы подписались, во временную шкалу"
newNoteRecived: "Появилась новая заметка" newNoteRecived: "Появилась новая заметка"
newNote: "Новая заметка"
sounds: "Звуки" sounds: "Звуки"
sound: "Звуки" sound: "Звуки"
notificationSoundSettings: "Настройки звука уведомлений"
listen: "Слушать" listen: "Слушать"
none: "Ничего" none: "Ничего"
showInPage: "Показать страницу" showInPage: "Показать страницу"
@ -791,6 +794,7 @@ wide: "Толстый"
narrow: "Тонкий" narrow: "Тонкий"
reloadToApplySetting: "Это настройка вступает в силу при загрузке страницы. Перезагрузить сейчас?" reloadToApplySetting: "Это настройка вступает в силу при загрузке страницы. Перезагрузить сейчас?"
needReloadToApply: "Изменения вступят в силу после перезагрузки страницы." needReloadToApply: "Изменения вступят в силу после перезагрузки страницы."
needToRestartServerToApply: "Для вступления изменений в силу необходимо перезапустить сервер."
showTitlebar: "Показать заголовок" showTitlebar: "Показать заголовок"
clearCache: "Очистить кэш" clearCache: "Очистить кэш"
onlineUsersCount: "Пользователей сейчас в сети: {n}" onlineUsersCount: "Пользователей сейчас в сети: {n}"
@ -1176,13 +1180,25 @@ unused: "Неиспользованное"
used: "Использован" used: "Использован"
expired: "Срок действия приглашения истёк" expired: "Срок действия приглашения истёк"
doYouAgree: "Согласны?" doYouAgree: "Согласны?"
beSureToReadThisAsItIsImportant: "Это важно, поэтому, пожалуйста, прочтите это."
iHaveReadXCarefullyAndAgree: "Я прочитал(а) и согласен(сна) с условиями \"{x}"
dialog: "Диалог"
icon: "Аватар" icon: "Аватар"
currentAnnouncements: "Текущие новости"
pastAnnouncements: "Предыдущие новости"
youHaveUnreadAnnouncements: "У вас есть непрочитанные уведомления"
replies: "Ответы" replies: "Ответы"
renotes: "Репост" renotes: "Репост"
loadReplies: "Показать ответы" loadReplies: "Показать ответы"
loadConversation: "Загрузить беседу"
pinnedList: "Закреплённый список" pinnedList: "Закреплённый список"
keepScreenOn: "Держать экран включённым" keepScreenOn: "Держать экран включённым"
unnotifyNotes: "Отписаться от сообщений"
authentication: "Аутентификация"
authenticationRequiredToContinue: "Пожалуйста, пройдите аутентификацию, чтобы продолжить"
dateAndTime: "Дата и время"
showRenotes: "Показывать репосты" showRenotes: "Показывать репосты"
edited: "Изменено"
mutualFollow: "Взаимные подписки" mutualFollow: "Взаимные подписки"
followingOrFollower: "Подписки или подписчики" followingOrFollower: "Подписки или подписчики"
fileAttachedOnly: "Только заметки с файлами" fileAttachedOnly: "Только заметки с файлами"
@ -1193,30 +1209,71 @@ sourceCode: "Исходный код"
sourceCodeIsNotYetProvided: "Исходный код пока не доступен. Свяжитесь с администратором, чтобы исправить эту проблему." sourceCodeIsNotYetProvided: "Исходный код пока не доступен. Свяжитесь с администратором, чтобы исправить эту проблему."
repositoryUrl: "Ссылка на репозиторий" repositoryUrl: "Ссылка на репозиторий"
repositoryUrlDescription: "Если вы используете Misskey как есть (без изменений в исходном коде), введите https://github.com/misskey-dev/misskey" repositoryUrlDescription: "Если вы используете Misskey как есть (без изменений в исходном коде), введите https://github.com/misskey-dev/misskey"
feedback: "Обратная связь"
privacyPolicy: "Политика Конфиденциальности" privacyPolicy: "Политика Конфиденциальности"
privacyPolicyUrl: "Ссылка на Политику Конфиденциальности" privacyPolicyUrl: "Ссылка на Политику Конфиденциальности"
tosAndPrivacyPolicy: "Условия использования и политика конфиденциальности"
avatarDecorations: "Украшения для аватара"
attach: "Прикрепить" attach: "Прикрепить"
angle: "Угол" angle: "Угол"
flip: "Переворот" flip: "Переворот"
showAvatarDecorations: "Показать украшения для аватара"
pullDownToRefresh: "Опустите что бы обновить"
useGroupedNotifications: "Отображать уведомления сгруппировано" useGroupedNotifications: "Отображать уведомления сгруппировано"
signupPendingError: "Возникла проблема с подтверждением вашего адреса электронной почты. Возможно, срок действия ссылки истёк."
cwNotationRequired: "Если включена опция «Скрыть содержимое», необходимо написать аннотацию."
doReaction: "Добавить реакцию" doReaction: "Добавить реакцию"
code: "Код" code: "Код"
reloadRequiredToApplySettings: "Для применения настроек необходима обновить страницу."
remainingN: "Остаётся: {n}" remainingN: "Остаётся: {n}"
overwriteContentConfirm: "Текущее содержимое будет перезаписано. Вы уверены?"
seasonalScreenEffect: "Эффект времени года на экране" seasonalScreenEffect: "Эффект времени года на экране"
decorate: "Украсить" decorate: "Украсить"
addMfmFunction: "Добавить MFM" addMfmFunction: "Добавить MFM"
bubbleGame: "BubbleGame"
sfx: "Звуковые эффекты"
soundWillBePlayed: "Будет воспроизведен звук"
showReplay: "Показать повтор"
endReplay: "Конец повтора"
lastNDays: "Последние {n} сут" lastNDays: "Последние {n} сут"
hemisphere: "Место проживания" hemisphere: "Место проживания"
userSaysSomethingSensitive: "Сообщение, содержит конфиденциальные файлы от {name}"
enableHorizontalSwipe: "Смахните в сторону, чтобы сменить вкладки" enableHorizontalSwipe: "Смахните в сторону, чтобы сменить вкладки"
surrender: "Этот пост не может быть отменен." surrender: "Этот пост не может быть отменен."
gameRetry: "Повторить попытку"
notUsePleaseLeaveBlank: "Если не используется, оставьте пустым"
useNativeUIForVideoAudioPlayer: "Использовать интерфейс браузера при проигрывании видео и звука" useNativeUIForVideoAudioPlayer: "Использовать интерфейс браузера при проигрывании видео и звука"
keepOriginalFilename: "Сохранять исходное имя файла" keepOriginalFilename: "Сохранять исходное имя файла"
keepOriginalFilenameDescription: "Если вы выключите данную настройку, имена файлов будут автоматически заменены случайной строкой при загрузке." keepOriginalFilenameDescription: "Если вы выключите данную настройку, имена файлов будут автоматически заменены случайной строкой при загрузке."
alwaysConfirmFollow: "Всегда подтверждать подписку" alwaysConfirmFollow: "Всегда подтверждать подписку"
inquiry: "Связаться" inquiry: "Связаться"
fromX: "Из {x}"
genEmbedCode: "Сгенерировать код для "
noteOfThisUser: "Список заметок этого пользователя"
clipNoteLimitExceeded: "К этому клипу больше нельзя добавить заметки"
performance: "Производительность"
modified: "Изменено"
signinWithPasskey: "Войдите в систему, используя свой пароль"
unknownWebAuthnKey: "Не известный ключ "
passkeyVerificationFailed: "Ошибка проверка ключа доступа "
messageToFollower: "Сообщение подписчикам" messageToFollower: "Сообщение подписчикам"
testCaptchaWarning: "Эта функция предназначена для тестирования CAPTCHA. <strong>Не использовать это в рабочей среде</strong>"
prohibitedWordsForNameOfUser: "Запрещенные слова (имя пользователя)"
prohibitedWordsForNameOfUserDescription: "Если имя пользователя содержит строку из этого списка, изменение имени пользователя будет запрещено. На пользователей с правами модератора это ограничение не распространяется. Имена пользователей также проверяются путём замены всех букв в нижнем регистре"
yourNameContainsProhibitedWords: "Имя, которое вы пытаетесь изменить, содержит запрещенную строку символов"
yourNameContainsProhibitedWordsDescription: "Имя содержит запрещённую строку символов. Если вы хотите использовать это имя, обратитесь к администратору сервера"
thisContentsAreMarkedAsSigninRequiredByAuthor: "Автор сообщения установил требование в виде авторизации для просмотра"
lockdown: "Доступ ограничен"
pleaseSelectAccount: "Выберите свой аккаунт"
availableRoles: "Доступные роли"
federationDisabled: "Федерация отключена для этого сервера. Вы не можете взаимодействовать с пользователями на других серверах."
draft: "Черновик"
markAsSensitiveConfirm: "Отметить контент как чувствительный?"
resetToDefaultValue: "Сбросить настройки до стандартных"
postForm: "Форма отправки" postForm: "Форма отправки"
information: "Описание" information: "Описание"
inMinutes: "мин"
inDays: "сут"
_chat: _chat:
invitations: "Пригласить" invitations: "Пригласить"
noHistory: "История пока пуста" noHistory: "История пока пуста"
@ -2200,3 +2257,4 @@ _watermarkEditor:
image: "Изображения" image: "Изображения"
advanced: "Для продвинутых" advanced: "Для продвинутых"
angle: "Угол" angle: "Угол"
drafts: "Черновик"

View File

@ -913,6 +913,8 @@ flip: "Preklopiť"
lastNDays: "Posledných {n} dní" lastNDays: "Posledných {n} dní"
postForm: "Napísať poznámku" postForm: "Napísať poznámku"
information: "Informácie" information: "Informácie"
inMinutes: "min"
inDays: "dní"
_chat: _chat:
invitations: "Pozvať" invitations: "Pozvať"
noHistory: "Žiadna história" noHistory: "Žiadna história"

View File

@ -146,7 +146,7 @@ enterFileName: "พิมพ์ชื่อไฟล์"
mute: "ปิดเสียง" mute: "ปิดเสียง"
unmute: "ยกเลิกการปิดเสียง" unmute: "ยกเลิกการปิดเสียง"
renoteMute: "ปิดเสียงรีโน้ต" renoteMute: "ปิดเสียงรีโน้ต"
renoteUnmute: "เปิดเสียง รีโน้ต" renoteUnmute: "เลิกปิดเสียงรีโน้ต"
block: "บล็อก" block: "บล็อก"
unblock: "เลิกบล็อก" unblock: "เลิกบล็อก"
suspend: "ระงับ" suspend: "ระงับ"
@ -242,8 +242,8 @@ silencedInstances: "ปิดปากเซิร์ฟเวอร์นี้
silencedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการปิดปาก คั่นด้วยการขึ้นบรรทัดใหม่, บัญชีทั้งหมดของเซิร์ฟเวอร์ดังกล่าวจะถือว่าถูกปิดปากเช่นกัน ทำได้เฉพาะคำขอติดตามเท่านั้น และไม่สามารถกล่าวถึงบัญชีในเซิร์ฟเวอร์นี้ได้หากไม่ได้ถูกติดตามกลับ | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก" silencedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการปิดปาก คั่นด้วยการขึ้นบรรทัดใหม่, บัญชีทั้งหมดของเซิร์ฟเวอร์ดังกล่าวจะถือว่าถูกปิดปากเช่นกัน ทำได้เฉพาะคำขอติดตามเท่านั้น และไม่สามารถกล่าวถึงบัญชีในเซิร์ฟเวอร์นี้ได้หากไม่ได้ถูกติดตามกลับ | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก"
mediaSilencedInstances: "เซิร์ฟเวอร์ที่ถูกปิดปากสื่อ" mediaSilencedInstances: "เซิร์ฟเวอร์ที่ถูกปิดปากสื่อ"
mediaSilencedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการปิดปากสื่อ คั่นด้วยการขึ้นบรรทัดใหม่, ไฟล์ที่ถูกส่งจากบัญชีของเซิร์ฟเวอร์ดังกล่าวจะถือว่าถูกปิดปาก แล้วจะถูกติดเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน และเอโมจิแบบกำหนดเองก็จะใช้ไม่ได้ด้วย | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก" mediaSilencedInstancesDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่ต้องการปิดปากสื่อ คั่นด้วยการขึ้นบรรทัดใหม่, ไฟล์ที่ถูกส่งจากบัญชีของเซิร์ฟเวอร์ดังกล่าวจะถือว่าถูกปิดปาก แล้วจะถูกติดเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน และเอโมจิแบบกำหนดเองก็จะใช้ไม่ได้ด้วย | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก"
federationAllowedHosts: "เซิร์ฟเวอร์ที่เปิดให้บริการแบบเฟเดอเรชั่น" federationAllowedHosts: "เซิร์ฟเวอร์ที่อนุญาตให้เชื่อมกับสหพันธ์"
federationAllowedHostsDescription: "ระบุชื่อโฮสต์ของเซิร์ฟเวอร์ที่คุณต้องการอนุญาตให้เชื่อมต่อแบบเฟเดอเรชั่น โดยต้องเว้นวรรคแต่ละบรรทัด" federationAllowedHostsDescription: "ระบุโฮสต์ของเซิร์ฟเวอร์ที่อนุญาตให้เชื่อมกับสหพันธ์ โดยแยกแต่ละรายการด้วยบรรทัดใหม่"
muteAndBlock: "ปิดเสียงและบล็อก" muteAndBlock: "ปิดเสียงและบล็อก"
mutedUsers: "ผู้ใช้ที่ถูกปิดเสียง" mutedUsers: "ผู้ใช้ที่ถูกปิดเสียง"
blockedUsers: "ผู้ใช้ที่ถูกบล็อก" blockedUsers: "ผู้ใช้ที่ถูกบล็อก"
@ -298,9 +298,11 @@ uploadFromUrl: "อัปโหลดจาก URL"
uploadFromUrlDescription: "URL ของไฟล์ที่คุณต้องการอัปโหลด" uploadFromUrlDescription: "URL ของไฟล์ที่คุณต้องการอัปโหลด"
uploadFromUrlRequested: "ร้องขอการอัปโหลดแล้ว" uploadFromUrlRequested: "ร้องขอการอัปโหลดแล้ว"
uploadFromUrlMayTakeTime: "การอัปโหลดอาจใช้เวลาสักครู่จึงจะเสร็จสมบูรณ์" uploadFromUrlMayTakeTime: "การอัปโหลดอาจใช้เวลาสักครู่จึงจะเสร็จสมบูรณ์"
uploadNFiles: "อัปโหลด {n} ไฟล์"
explore: "สำรวจ" explore: "สำรวจ"
messageRead: "อ่านแล้ว" messageRead: "อ่านแล้ว"
noMoreHistory: "ไม่มีประวัติเพิ่มเติม" noMoreHistory: "ไม่มีประวัติเพิ่มเติม"
startChat: "เริ่มแชต"
nUsersRead: "อ่านโดย {n}" nUsersRead: "อ่านโดย {n}"
agreeTo: "ฉันยอมรับ {0}" agreeTo: "ฉันยอมรับ {0}"
agree: "ยอมรับ" agree: "ยอมรับ"
@ -325,6 +327,7 @@ dark: "มืด"
lightThemes: "ธีมสว่าง" lightThemes: "ธีมสว่าง"
darkThemes: "ธีมมืด" darkThemes: "ธีมมืด"
syncDeviceDarkMode: "ซิงค์โหมดมืดกับการตั้งค่าอุปกรณ์ของคุณ" syncDeviceDarkMode: "ซิงค์โหมดมืดกับการตั้งค่าอุปกรณ์ของคุณ"
switchDarkModeManuallyWhenSyncEnabledConfirm: "“{x}” เปิดอยู่ ต้องการปิดการซิงค์และสลับโหมดด้วยตนเองหรือไม่?"
drive: "ไดรฟ์" drive: "ไดรฟ์"
fileName: "ชื่อไฟล์" fileName: "ชื่อไฟล์"
selectFile: "เลือกไฟล์" selectFile: "เลือกไฟล์"
@ -365,7 +368,7 @@ reject: "ปฏิเสธ"
normal: "ปกติ" normal: "ปกติ"
instanceName: "ชื่อเซิร์ฟเวอร์" instanceName: "ชื่อเซิร์ฟเวอร์"
instanceDescription: "คำอธิบายแนะนำเซิร์ฟเวอร์" instanceDescription: "คำอธิบายแนะนำเซิร์ฟเวอร์"
maintainerName: "ผู้ดูแล" maintainerName: "ชื่อผู้ดูแลระบบ"
maintainerEmail: "อีเมลผู้ดูแลระบบ" maintainerEmail: "อีเมลผู้ดูแลระบบ"
tosUrl: "URL เงื่อนไขการให้บริการ" tosUrl: "URL เงื่อนไขการให้บริการ"
thisYear: "ปีนี้" thisYear: "ปีนี้"
@ -423,6 +426,7 @@ antennaExcludeBots: "ยกเว้นบัญชีบอต"
antennaKeywordsDescription: "คั่นด้วยเว้นวรรคสำหรับเงื่อนไข AND, หรือขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR" antennaKeywordsDescription: "คั่นด้วยเว้นวรรคสำหรับเงื่อนไข AND, หรือขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR"
notifyAntenna: "แจ้งเตือนเกี่ยวกับโน้ตใหม่" notifyAntenna: "แจ้งเตือนเกี่ยวกับโน้ตใหม่"
withFileAntenna: "เฉพาะโน้ตที่มีไฟล์" withFileAntenna: "เฉพาะโน้ตที่มีไฟล์"
excludeNotesInSensitiveChannel: "ไม่รวมโน้ตจากช่องเนื้อหาละเอียดอ่อน"
enableServiceworker: "เปิดใช้งานการแจ้งเตือนแบบพุชไปยังเบราว์เซอร์ของคุณ" enableServiceworker: "เปิดใช้งานการแจ้งเตือนแบบพุชไปยังเบราว์เซอร์ของคุณ"
antennaUsersDescription: "ระบุหนึ่งชื่อผู้ใช้ต่อบรรทัด" antennaUsersDescription: "ระบุหนึ่งชื่อผู้ใช้ต่อบรรทัด"
caseSensitive: "อักษรพิมพ์ใหญ่-พิมพ์เล็กความหมายต่างกัน" caseSensitive: "อักษรพิมพ์ใหญ่-พิมพ์เล็กความหมายต่างกัน"
@ -453,17 +457,17 @@ totpDescription: "ใช้แอปยืนยันตัวตนเพื
moderator: "ผู้ควบคุม" moderator: "ผู้ควบคุม"
moderation: "การกลั่นกรอง" moderation: "การกลั่นกรอง"
moderationNote: "โน้ตการกลั่นกรอง" moderationNote: "โน้ตการกลั่นกรอง"
moderationNoteDescription: "คุณสามารถใส่โน้ตส่วนตัวที่เฉพาะผู้ดูแลระบบเท่านั้นที่สามารถเข้าถึงได้" moderationNoteDescription: "สามารถจดเมโมที่จะแบ่งปันเฉพาะระหว่างผู้ควบคุมได้"
addModerationNote: "เพิ่มโน้ตการกลั่นกรอง" addModerationNote: "เพิ่มโน้ตการกลั่นกรอง"
moderationLogs: "ปูมการควบคุมดูแล" moderationLogs: "ปูมการควบคุมดูแล"
nUsersMentioned: "กล่าวถึงโดยผู้ใช้ {n} ราย" nUsersMentioned: "กล่าวถึงโดยผู้ใช้ {n} ราย"
securityKeyAndPasskey: "ความปลอดภัยและรหัสผ่าน" securityKeyAndPasskey: "Security key และ Passkey"
securityKey: "กุญแจความปลอดภัย" securityKey: "Security Key"
lastUsed: "ใช้ล่าสุด" lastUsed: "ใช้ล่าสุด"
lastUsedAt: "ใช้งานครั้งล่าสุด: {t}" lastUsedAt: "ใช้งานครั้งล่าสุด: {t}"
unregister: "เลิกติดตาม" unregister: "เลิกติดตาม"
passwordLessLogin: "เข้าสู่ระบบแบบไม่ใช้รหัสผ่าน" passwordLessLogin: "เข้าสู่ระบบแบบไม่ใช้รหัสผ่าน"
passwordLessLoginDescription: "อนุญาตให้เข้าสู่ระบบโดยไม่ต้องใช้รหัสผ่านโดยใช้รหัสรักษาความปลอดภัยหรือรหัสผ่านเท่านั้น" passwordLessLoginDescription: "เข้าสู่ระบบโดยไม่ใช้รหัสผ่าน โดยใช้เฉพาะ Security Key หรือ Passkey เท่านั้น"
resetPassword: "รีเซ็ตรหัสผ่าน" resetPassword: "รีเซ็ตรหัสผ่าน"
newPasswordIs: "รหัสผ่านใหม่คือ “{password}”" newPasswordIs: "รหัสผ่านใหม่คือ “{password}”"
reduceUiAnimation: "ลดภาพเคลื่อนไหว UI" reduceUiAnimation: "ลดภาพเคลื่อนไหว UI"
@ -573,8 +577,10 @@ showFixedPostForm: "แสดงแบบฟอร์มการโพสต์
showFixedPostFormInChannel: "แสดงแบบฟอร์มการโพสต์ที่ด้านบนของไทม์ไลน์ (ช่อง)" showFixedPostFormInChannel: "แสดงแบบฟอร์มการโพสต์ที่ด้านบนของไทม์ไลน์ (ช่อง)"
withRepliesByDefaultForNewlyFollowed: "แสดงการตอบกลับจากผู้ใช้ที่คุณเพิ่งติดตามลงไทม์ไลน์ตามค่าเริ่มต้น" withRepliesByDefaultForNewlyFollowed: "แสดงการตอบกลับจากผู้ใช้ที่คุณเพิ่งติดตามลงไทม์ไลน์ตามค่าเริ่มต้น"
newNoteRecived: "มีโน้ตใหม่" newNoteRecived: "มีโน้ตใหม่"
newNote: "โน้ตใหม่"
sounds: "เสียง" sounds: "เสียง"
sound: "เสียง" sound: "เสียง"
notificationSoundSettings: "ตั้งค่าเสียงแจ้งเตือน"
listen: "ฟัง" listen: "ฟัง"
none: "ไม่มี" none: "ไม่มี"
showInPage: "แสดงในเพจ" showInPage: "แสดงในเพจ"
@ -606,8 +612,8 @@ output: "เอาท์พุต"
script: "สคริปต์" script: "สคริปต์"
disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ" disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ"
updateRemoteUser: "อัปเดตข้อมูลผู้ใช้งานระยะไกล" updateRemoteUser: "อัปเดตข้อมูลผู้ใช้งานระยะไกล"
unsetUserAvatar: "เลิกตั้งอวตาร" unsetUserAvatar: "เลิกตั้งไอคอน"
unsetUserAvatarConfirm: "ต้องการเลิกตั้งอวตารใข่ไหม?" unsetUserAvatarConfirm: "ต้องการเลิกตั้งไอคอนประจำตัวหรือไม่?"
unsetUserBanner: "เลิกตั้งแบนเนอร์" unsetUserBanner: "เลิกตั้งแบนเนอร์"
unsetUserBannerConfirm: "ต้องการเลิกตั้งแบนเนอร์?" unsetUserBannerConfirm: "ต้องการเลิกตั้งแบนเนอร์?"
deleteAllFiles: "ลบไฟล์ทั้งหมด" deleteAllFiles: "ลบไฟล์ทั้งหมด"
@ -682,13 +688,15 @@ smtpSecure: "ใช้โดยนัย SSL/TLS สำหรับการเ
smtpSecureInfo: "ปิดสิ่งนี้เมื่อใช้ STARTTLS" smtpSecureInfo: "ปิดสิ่งนี้เมื่อใช้ STARTTLS"
testEmail: "ทดสอบการส่งอีเมล" testEmail: "ทดสอบการส่งอีเมล"
wordMute: "ปิดเสียงคำ" wordMute: "ปิดเสียงคำ"
wordMuteDescription: "ย่อโน้ตที่มีวลีที่ระบุ สามารถดูโน้ตที่ย่อแล้วได้โดยคลิกที่โน้ตเหล่านั้น"
hardWordMute: "ปิดเสียงคำแบบแข็งโป๊ก" hardWordMute: "ปิดเสียงคำแบบแข็งโป๊ก"
hardWordMuteDescription: "ซ่อนหมายเหตุที่มีวลีที่ระบุ ต่างจากการปิดเสียงคำ โน้ตต่างๆ จะถูกซ่อนไว้อย่างสมบูรณ์" showMutedWord: "แสดงคำที่ถูกปิดเสียง"
hardWordMuteDescription: "จะซ่อนโน้ตที่มีคำที่ระบุไว้ ซึ่งไม่เหมือนการปิดเสียงคำ ในกรณีนี้โน้ตจะไม่แสดงเลย"
regexpError: "เกิดข้อผิดพลาดใน regular expression" regexpError: "เกิดข้อผิดพลาดใน regular expression"
regexpErrorDescription: "เกิดข้อผิดพลาดใน regular expression บรรทัดที่ {line} ของการปิดเสียงคำ {tab} :" regexpErrorDescription: "เกิดข้อผิดพลาดใน regular expression บรรทัดที่ {line} ของการปิดเสียงคำ {tab} :"
instanceMute: "ปิดเสียงเซิร์ฟเวอร์" instanceMute: "ปิดเสียงเซิร์ฟเวอร์"
userSaysSomething: "{name} พูดอะไรบางอย่าง" userSaysSomething: "{name} พูดอะไรบางอย่าง"
userSaysSomethingAbout: "{name} พูดอะไรบางอย่างเกี่ยวกับ \"{word}\"" userSaysSomethingAbout: "{name} พูดบางอย่างเกี่ยวกับ “{word}”"
makeActive: "เปิดใช้งาน" makeActive: "เปิดใช้งาน"
display: "แสดงผล" display: "แสดงผล"
copy: "คัดลอก" copy: "คัดลอก"
@ -758,7 +766,7 @@ yes: "ใช่"
no: "ไม่" no: "ไม่"
driveFilesCount: "จำนวนไฟล์ไดรฟ์" driveFilesCount: "จำนวนไฟล์ไดรฟ์"
driveUsage: "การใช้พื้นที่ไดรฟ์" driveUsage: "การใช้พื้นที่ไดรฟ์"
noCrawle: "ปฏิเสธการจัดทำดัชนีของโปรแกรมรวบรวมข้อมูล" noCrawle: "ปฏิเสธการจัดทำดัชนีของ Crawler (โปรแกรมรวบรวมข้อมูล)"
noCrawleDescription: "ขอให้เครื่องมือค้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ โน้ต หน้าเพจ ฯลฯ" noCrawleDescription: "ขอให้เครื่องมือค้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ โน้ต หน้าเพจ ฯลฯ"
lockedAccountInfo: "แม้ว่าการอนุมัติการติดตามถูกเปิดใช้งานอยู่ทุกคนก็ยังคงสามารถเห็นโน้ตของคุณได้ เว้นแต่ว่าคุณจะเปลี่ยนการเปิดเผยโน้ตของคุณเป็น “เฉพาะผู้ติดตาม”" lockedAccountInfo: "แม้ว่าการอนุมัติการติดตามถูกเปิดใช้งานอยู่ทุกคนก็ยังคงสามารถเห็นโน้ตของคุณได้ เว้นแต่ว่าคุณจะเปลี่ยนการเปิดเผยโน้ตของคุณเป็น “เฉพาะผู้ติดตาม”"
alwaysMarkSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนเป็นค่าเริ่มต้น" alwaysMarkSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนเป็นค่าเริ่มต้น"
@ -881,7 +889,7 @@ previewNoteText: "แสดงตัวอย่าง"
customCss: "CSS ที่กำหนดเอง" customCss: "CSS ที่กำหนดเอง"
customCssWarn: "ควรใช้การตั้งค่านี้เฉพาะต่อเมื่อคุณรู้มันใช้ทำอะไร การตั้งค่าที่ไม่เหมาะสมอาจทำให้ไคลเอ็นต์ไม่สามารถใช้งานได้อย่างถูกต้อง" customCssWarn: "ควรใช้การตั้งค่านี้เฉพาะต่อเมื่อคุณรู้มันใช้ทำอะไร การตั้งค่าที่ไม่เหมาะสมอาจทำให้ไคลเอ็นต์ไม่สามารถใช้งานได้อย่างถูกต้อง"
global: "ทั่วโลก" global: "ทั่วโลก"
squareAvatars: "แสดงผลอวตารเป็นสี่เหลี่ยม" squareAvatars: "แสดงไอคอนประจำตัวเป็นสี่เหลี่ยม"
sent: "ส่ง" sent: "ส่ง"
received: "ได้รับแล้ว" received: "ได้รับแล้ว"
searchResult: "ผลการค้นหา" searchResult: "ผลการค้นหา"
@ -948,6 +956,9 @@ oneHour: "1 ชั่วโมง"
oneDay: "1 วัน" oneDay: "1 วัน"
oneWeek: "1 สัปดาห์" oneWeek: "1 สัปดาห์"
oneMonth: "หนึ่งเดือน" oneMonth: "หนึ่งเดือน"
threeMonths: "3 เดือน"
oneYear: "1 ปี"
threeDays: "3 วัน"
reflectMayTakeTime: "อาจจำเป็นต้องใช้เวลาสักระยะหนึ่งจึงจะเห็นแสดงผลได้นะ" reflectMayTakeTime: "อาจจำเป็นต้องใช้เวลาสักระยะหนึ่งจึงจะเห็นแสดงผลได้นะ"
failedToFetchAccountInformation: "ไม่สามารถเรียกดึงข้อมูลบัญชีได้" failedToFetchAccountInformation: "ไม่สามารถเรียกดึงข้อมูลบัญชีได้"
rateLimitExceeded: "เกินขีดจำกัดอัตรา" rateLimitExceeded: "เกินขีดจำกัดอัตรา"
@ -972,6 +983,7 @@ document: "เอกสาร"
numberOfPageCache: "จำนวนหน้าเพจที่แคช" numberOfPageCache: "จำนวนหน้าเพจที่แคช"
numberOfPageCacheDescription: "การเพิ่มจำนวนนี้จะช่วยเพิ่มความสะดวกให้กับผู้ใช้งาน แต่จะทำให้เซิร์ฟเวอร์โหลดมากขึ้นและต้องใช้หน่วยความจำมากขึ้นอีกด้วย" numberOfPageCacheDescription: "การเพิ่มจำนวนนี้จะช่วยเพิ่มความสะดวกให้กับผู้ใช้งาน แต่จะทำให้เซิร์ฟเวอร์โหลดมากขึ้นและต้องใช้หน่วยความจำมากขึ้นอีกด้วย"
logoutConfirm: "ต้องการออกจากระบบใช่ไหม?" logoutConfirm: "ต้องการออกจากระบบใช่ไหม?"
logoutWillClearClientData: "เมื่อออกจากระบบ ข้อมูลการตั้งค่าของไคลเอนต์จะถูกลบออกจากเบราว์เซอร์ เพื่อให้สามารถกู้คืนข้อมูลการตั้งค่าได้เมื่อกลับมาเข้าสู่ระบบอีกครั้ง โปรดเปิดใช้งานการสำรองข้อมูลการตั้งค่าอัตโนมัติ"
lastActiveDate: "ใช้งานล่าสุดเมื่อ" lastActiveDate: "ใช้งานล่าสุดเมื่อ"
statusbar: "แถบสถานะ" statusbar: "แถบสถานะ"
pleaseSelect: "ตัวเลือก" pleaseSelect: "ตัวเลือก"
@ -990,6 +1002,7 @@ failedToUpload: "การอัปโหลดล้มเหลว"
cannotUploadBecauseInappropriate: "ไม่สามารถอัปโหลดไฟล์นี้ได้เนื่องจากระบบตรวจพบบางส่วนของไฟล์ว่านี้อาจจะเป็น NSFW" cannotUploadBecauseInappropriate: "ไม่สามารถอัปโหลดไฟล์นี้ได้เนื่องจากระบบตรวจพบบางส่วนของไฟล์ว่านี้อาจจะเป็น NSFW"
cannotUploadBecauseNoFreeSpace: "ไม่สามารถอัปโหลดได้เนื่องจากไม่มีพื้นที่ว่างในไดรฟ์เหลือแล้ว" cannotUploadBecauseNoFreeSpace: "ไม่สามารถอัปโหลดได้เนื่องจากไม่มีพื้นที่ว่างในไดรฟ์เหลือแล้ว"
cannotUploadBecauseExceedsFileSizeLimit: "ไม่สามารถอัปโหลดไฟล์นี้ได้แล้วเนื่องจากเกินขีดจำกัดของขนาดไฟล์แล้ว" cannotUploadBecauseExceedsFileSizeLimit: "ไม่สามารถอัปโหลดไฟล์นี้ได้แล้วเนื่องจากเกินขีดจำกัดของขนาดไฟล์แล้ว"
cannotUploadBecauseUnallowedFileType: "ไม่สามารถอัปโหลดได้เนื่องจากเป็นชนิดไฟล์ที่ไม่ได้รับอนุญาต"
beta: "เบต้า" beta: "เบต้า"
enableAutoSensitive: "ทำเครื่องหมายว่ามีเนื้อหาที่ละเอียดอ่อนโดยอัตโนมัติ" enableAutoSensitive: "ทำเครื่องหมายว่ามีเนื้อหาที่ละเอียดอ่อนโดยอัตโนมัติ"
enableAutoSensitiveDescription: "อนุญาตให้ตรวจหาและทำเครื่องหมายสื่อว่ามีเนื้อหาโดยละเอียดอ่อนโดยอัตโนมัติ ผ่าน Machine Learning หากเป็นไปได้ แม้ว่าคุณจะปิดคุณสมบัตินี้ ก็อาจถูกตั้งค่าโดยอัตโนมัติ ทั้งนี้ขึ้นอยู่กับเซิร์ฟเวอร์" enableAutoSensitiveDescription: "อนุญาตให้ตรวจหาและทำเครื่องหมายสื่อว่ามีเนื้อหาโดยละเอียดอ่อนโดยอัตโนมัติ ผ่าน Machine Learning หากเป็นไปได้ แม้ว่าคุณจะปิดคุณสมบัตินี้ ก็อาจถูกตั้งค่าโดยอัตโนมัติ ทั้งนี้ขึ้นอยู่กับเซิร์ฟเวอร์"
@ -1009,7 +1022,7 @@ windowMaximize: "ขยายใหญ่สุด"
windowMinimize: "ย่อเล็กที่สุด" windowMinimize: "ย่อเล็กที่สุด"
windowRestore: "เลิกทำ" windowRestore: "เลิกทำ"
caption: "คำอธิบาย" caption: "คำอธิบาย"
loggedInAsBot: "ล็อกอินเป็นบอตอยู่ในขณะนี้" loggedInAsBot: "เข้าสู่ระบบเป็นบอตอยู่ในขณะนี้"
tools: "เครื่องมือ" tools: "เครื่องมือ"
cannotLoad: "ไม่สามารถโหลดได้" cannotLoad: "ไม่สามารถโหลดได้"
numberOfProfileView: "มุมมองโปรไฟล์" numberOfProfileView: "มุมมองโปรไฟล์"
@ -1058,7 +1071,7 @@ exploreOtherServers: "มองหาเซิร์ฟเวอร์อื่
letsLookAtTimeline: "มาดูไทม์ไลน์กัน" letsLookAtTimeline: "มาดูไทม์ไลน์กัน"
disableFederationConfirm: "ปิดใช้งานสหพันธ์เลยใช่ไหม?" disableFederationConfirm: "ปิดใช้งานสหพันธ์เลยใช่ไหม?"
disableFederationConfirmWarn: "โพสต์จะยังคงเป็นสาธารณะต่อไป เว้นแต่จะตั้งค่าเป็นอย่างอื่น" disableFederationConfirmWarn: "โพสต์จะยังคงเป็นสาธารณะต่อไป เว้นแต่จะตั้งค่าเป็นอย่างอื่น"
disableFederationOk: "ปิดการใช้งาน" disableFederationOk: "ปิดการใช้งานสหพันธ์"
invitationRequiredToRegister: "เซิร์ฟเวอร์นี้เป็นแบบรับเชิญ เฉพาะผู้มีรหัสเชิญเท่านั้นถึงสามารถลงทะเบียนได้" invitationRequiredToRegister: "เซิร์ฟเวอร์นี้เป็นแบบรับเชิญ เฉพาะผู้มีรหัสเชิญเท่านั้นถึงสามารถลงทะเบียนได้"
emailNotSupported: "เซิร์ฟเวอร์นี้ไม่รองรับการส่งอีเมล" emailNotSupported: "เซิร์ฟเวอร์นี้ไม่รองรับการส่งอีเมล"
postToTheChannel: "โพสต์ลงช่อง" postToTheChannel: "โพสต์ลงช่อง"
@ -1088,7 +1101,7 @@ retryAllQueuesConfirmTitle: "ลองใหม่ทั้งหมดจริ
retryAllQueuesConfirmText: "สิ่งนี้จะเพิ่มการโหลดเซิร์ฟเวอร์ชั่วคราวนะ" retryAllQueuesConfirmText: "สิ่งนี้จะเพิ่มการโหลดเซิร์ฟเวอร์ชั่วคราวนะ"
enableChartsForRemoteUser: "สร้างแผนภูมิข้อมูลผู้ใช้ระยะไกล" enableChartsForRemoteUser: "สร้างแผนภูมิข้อมูลผู้ใช้ระยะไกล"
enableChartsForFederatedInstances: "สร้างแผนภูมิของเซิร์ฟเวอร์ระยะไกล" enableChartsForFederatedInstances: "สร้างแผนภูมิของเซิร์ฟเวอร์ระยะไกล"
enableStatsForFederatedInstances: "ดึงข้อมูลสถิติจากเซิร์ฟเวอร์ที่อยู่ห่างไกล" enableStatsForFederatedInstances: "ดึงข้อมูลจากเซิร์ฟเวอร์ระยะไกล"
showClipButtonInNoteFooter: "เพิ่ม “คลิป” ไปยังเมนูสั่งการของโน้ต" showClipButtonInNoteFooter: "เพิ่ม “คลิป” ไปยังเมนูสั่งการของโน้ต"
reactionsDisplaySize: "ขนาดของรีแอคชั่น" reactionsDisplaySize: "ขนาดของรีแอคชั่น"
limitWidthOfReaction: "จำกัดความกว้างสูงสุดของรีแอคชั่นและแสดงให้เล็กลง" limitWidthOfReaction: "จำกัดความกว้างสูงสุดของรีแอคชั่นและแสดงให้เล็กลง"
@ -1219,13 +1232,13 @@ impressumDescription: "การติดป้ายกำกับ (Impressum)
privacyPolicy: "นโยบายความเป็นส่วนตัว" privacyPolicy: "นโยบายความเป็นส่วนตัว"
privacyPolicyUrl: "URL นโยบายความเป็นส่วนตัว" privacyPolicyUrl: "URL นโยบายความเป็นส่วนตัว"
tosAndPrivacyPolicy: "เงื่อนไขในการให้บริการและนโยบายความเป็นส่วนตัว" tosAndPrivacyPolicy: "เงื่อนไขในการให้บริการและนโยบายความเป็นส่วนตัว"
avatarDecorations: "การตกแต่งอวตาร" avatarDecorations: "ของตกแต่งไอคอน"
attach: "แนบ" attach: "แนบ"
detach: "นำออก" detach: "นำออก"
detachAll: "เอาออกทั้งหมด" detachAll: "เอาออกทั้งหมด"
angle: "แองเกิล" angle: "แองเกิล"
flip: "พลิก" flip: "พลิก"
showAvatarDecorations: "แสดงตกแต่งอวตาร" showAvatarDecorations: "แสดงของตกแต่งไอคอน"
releaseToRefresh: "ปล่อยเพื่อรีเฟรช" releaseToRefresh: "ปล่อยเพื่อรีเฟรช"
refreshing: "กำลังรีเฟรช..." refreshing: "กำลังรีเฟรช..."
pullDownToRefresh: "ดึงลงเพื่อรีเฟรช" pullDownToRefresh: "ดึงลงเพื่อรีเฟรช"
@ -1281,51 +1294,210 @@ clipNoteLimitExceeded: "ไม่สามารถเพิ่มโน้ต
performance: "ประสิทธิภาพ​" performance: "ประสิทธิภาพ​"
modified: "แก้ไข" modified: "แก้ไข"
discard: "ละทิ้ง" discard: "ละทิ้ง"
thereAreNChanges: "มีอยู่ {n} เปลี่ยนแปลง(s)" thereAreNChanges: "มีการเปลี่ยนแปลง {n} รายการ"
signinWithPasskey: "ลงชื่อเข้าใช้ด้วย Passkey" signinWithPasskey: "ลงชื่อเข้าใช้ด้วย Passkey"
unknownWebAuthnKey: "พาสคีย์ไม่ถูกต้องค่ะ" unknownWebAuthnKey: "เป็น Passkey ที่ยังไม่ได้ลงทะเบียน"
passkeyVerificationFailed: "การยืนยันกุญแจดิจิทัลไม่สำเร็จค่ะ" passkeyVerificationFailed: "การยืนยัน Passkey ล้มเหลว"
passkeyVerificationSucceededButPasswordlessLoginDisabled: "การยืนยันพาสคีย์สำเร็จแล้ว แต่การลงชื่อเข้าใช้แบบไม่ต้องใส่รหัสผ่านถูกปิดใช้งานแล้ว" passkeyVerificationSucceededButPasswordlessLoginDisabled: "การยืนยัน Passkey สำเร็จ แต่การเข้าสู่ระบบแบบไม่ใช้รหัสผ่านถูกปิดใช้งานอยู่"
messageToFollower: "ข้อความถึงผู้ติดตาม" messageToFollower: "ข้อความถึงผู้ติดตาม"
target: "เป้า" target: "เป้า"
testCaptchaWarning: "ฟังก์ชันนี้มีไว้สำหรับทดสอบ CAPTCHA เท่านั้น\n<strong>ห้ามนำไปใช้ในระบบจริงโดยเด็ดขาด</strong>" testCaptchaWarning: "ฟังก์ชันนี้มีไว้สำหรับทดสอบ CAPTCHA เท่านั้น\n<strong>ห้ามนำไปใช้ในระบบจริงโดยเด็ดขาด</strong>"
prohibitedWordsForNameOfUser: "คำนี้ไม่สามารถใช้เป็นชื่อผู้ใช้ได้" prohibitedWordsForNameOfUser: "คำนี้ไม่สามารถใช้เป็นชื่อผู้ใช้ได้"
prohibitedWordsForNameOfUserDescription: "หากมีสตริงใดๆ ในรายการนี้ปรากฏอยู่ในชื่อของผู้ใช้ ชื่อนั้นจะถูกปฏิเสธ ผู้ใช้ที่มีสิทธิ์แต่ผู้ดูแลระบบนั้นจะไม่ได้รับผลกระทบใดๆจากข้อจำกัดนี้ค่ะ" prohibitedWordsForNameOfUserDescription: "จะไม่อนุญาตให้เปลี่ยนชื่อผู้ใช้หากชื่อของผู้ใช้มีข้อความที่อยู่ในรายการนี้ แต่ผู้ใช้ที่มีสิทธิ์เป็นผู้ควบคุมจะไม่ได้รับผลกระทบจากข้อจำกัดนี้"
yourNameContainsProhibitedWords: "ชื่อของคุณนั้นมีคำที่ต้องห้าม" yourNameContainsProhibitedWords: "ชื่อของคุณนั้นมีคำที่ต้องห้าม"
yourNameContainsProhibitedWordsDescription: "ถ้าหากคุณต้องการใช้ชื่อนี้ กรุณาติดต่อผู้ดูแลระบบของเซิร์ฟเวอร์นะค่ะ" yourNameContainsProhibitedWordsDescription: "ถ้าหากคุณต้องการใช้ชื่อนี้ กรุณาติดต่อผู้ดูแลระบบของเซิร์ฟเวอร์นะค่ะ"
federationDisabled: "เซิร์ฟเวอร์นี้ปิดการใช้งานการรวมกลุ่ม คุณไม่สามารถโต้ตอบกับผู้ใช้บนเซิร์ฟเวอร์อื่นได้" thisContentsAreMarkedAsSigninRequiredByAuthor: "ผู้โพสต์ได้ตั้งค่าว่าต้องเข้าสู่ระบบจึงจะสามารถดูได้"
reactAreYouSure: "คุณต้องการที่จะตอบสนองต่อ \" {emoji}\" หรือไม่?" lockdown: "ล็อกดาวน์"
markAsSensitiveConfirm: "คุณต้องการทำเครื่องหมายสื่อนี้ว่าละเอียดอ่อนหรือไม่?" pleaseSelectAccount: "โปรดเลือกบัญชี"
unmarkAsSensitiveConfirm: "คุณต้องการลบการกำหนดความไวของสื่อนี้หรือไม่?" availableRoles: "บทบาทที่ใช้ได้"
acknowledgeNotesAndEnable: "เปิดใช้งานหลังจากที่เข้าใจข้อควรระวังแล้ว"
federationSpecified: "เซิร์ฟเวอร์นี้ดำเนินงานในระบบกลุ่มไวท์ลิสต์ ไม่สามารถติดต่อกับเซิร์ฟเวอร์อื่นที่ไม่ได้รับอนุญาตจากผู้ดูแลระบบได้"
federationDisabled: "เซิร์ฟเวอร์นี้ปิดใช้งานสหพันธ์ ไม่สามารถติดต่อหรือแลกเปลี่ยนข้อมูลกับผู้ใช้จากเซิร์ฟเวอร์อื่นได้"
draft: "ร่าง"
confirmOnReact: "ยืนยันเมื่อทำการรีแอคชั่น"
reactAreYouSure: "ต้องการใส่รีแอคชั่นด้วย \"{emoji}\" หรือไม่?"
markAsSensitiveConfirm: "ต้องการตั้งค่าสื่อนี้ว่าเป็นเนื้อหาละเอียดอ่อนหรือไม่?"
unmarkAsSensitiveConfirm: "ต้องการยกเลิกการระบุว่าสื่อนี้มีเนื้อหาละเอียดอ่อนหรือไม่?"
preferences: "การตั้งค่าสภาพแวดล้อม" preferences: "การตั้งค่าสภาพแวดล้อม"
accessibility: "การช่วยการเข้าถึง"
preferencesProfile: "โปรไฟล์การกำหนดค่า" preferencesProfile: "โปรไฟล์การกำหนดค่า"
copyPreferenceId: "คัดลือก ID การตั้งค่า"
resetToDefaultValue: "คืนค่าเป็นค่าเริ่มต้น"
overrideByAccount: "เขียนทับด้วยบัญชี"
untitled: "ไม่มีชื่อ"
noName: "ไม่มีชื่อ"
skip: "ข้าม"
restore: "กู้คืน"
syncBetweenDevices: "ซิงค์ระหว่างอุปกรณ์"
preferenceSyncConflictTitle: "การตั้งค่ามีอยู่บนเซิร์ฟเวอร์" preferenceSyncConflictTitle: "การตั้งค่ามีอยู่บนเซิร์ฟเวอร์"
preferenceSyncConflictText: "รายการการตั้งค่าที่เปิดใช้งานการซิงโครไนซ์จะจัดเก็บค่าไว้บนเซิร์ฟเวอร์ และพบค่าที่จัดเก็บบนเซิร์ฟเวอร์สำหรับรายการการตั้งค่านี้ คุณต้องการทำอย่างไร?" preferenceSyncConflictText: "การตั้งค่าที่เปิดใช้งานการซิงค์จะบันทึกค่าลงในเซิร์ฟเวอร์ อย่างไรก็ดี พบว่ามีค่าการตั้งค่านี้ที่เคยบันทึกไว้ในเซิร์ฟเวอร์แล้ว ต้องการดำเนินการอย่างไร?"
preferenceSyncConflictChoiceMerge: "รวมเข้าด้วยกัน"
preferenceSyncConflictChoiceServer: "เขียนทับด้วยค่าการตั้งค่าเซิร์ฟเวอร์"
preferenceSyncConflictChoiceDevice: "เขียนทับด้วยค่าการตั้งค่าอุปกรณ์"
preferenceSyncConflictChoiceCancel: "ยกเลิกการเปิดใช้งานการซิงค์"
paste: "วาง"
emojiPalette: "จานสีเอโมจิ"
postForm: "แบบฟอร์มการโพสต์" postForm: "แบบฟอร์มการโพสต์"
textCount: "จำนวนอักขระ"
information: "เกี่ยวกับ" information: "เกี่ยวกับ"
chat: "แชต"
migrateOldSettings: "ย้ายข้อมูลการตั้งค่าเก่า"
migrateOldSettings_description: "โดยปกติจะทำโดยอัตโนมัติ แต่หากด้วยเหตุผลบางประการที่ไม่สามารถย้ายได้สำเร็จ สามารถสั่งย้ายด้วยตนเองได้ การตั้งค่าปัจจุบันจะถูกเขียนทับ"
compress: "บีบอัด"
right: "ขวา" right: "ขวา"
bottom: "ภายใต้" bottom: "ภายใต้"
top: "บน"
embed: "ฝัง"
settingsMigrating: "กำลังย้ายการตั้งค่า กรุณารอสักครู่... (สามารถย้ายด้วยตนเองภายหลังได้ที่ การตั้งค่า → อื่นๆ → ย้ายข้อมูลการตั้งค่าเก่า)"
readonly: "อ่านได้อย่างเดียว"
goToDeck: "กลับไปยังเด็ค"
federationJobs: "งานสหพันธ์"
driveAboutTip: "ในไดรฟ์จะแสดงรายการไฟล์ที่เคยอัปโหลดไว้ก่อนหน้า<br>\nสามารถนำมาใช้ซ้ำเมื่อแนบไฟล์ในโน้ต หรือตั้งค่าให้อัปโหลดไฟล์ล่วงหน้าเพื่อนำไปโพสต์ทีหลังได้<br>\n<b>โปรดระวัง เมื่อลบไฟล์ ไฟล์นั้นจะไม่แสดงในทุกที่ที่เคยใช้ไฟล์นี้ (โน้ต, หน้าเพจ, อวตาร, แบนเนอร์ ฯลฯ)</b><br>\nสามารถสร้างโฟลเดอร์เพื่อจัดระเบียบได้"
scrollToClose: "เลื่อนเพื่อปิด"
advice: "คำแนะนำ"
realtimeMode: "โหมดเรียลไทม์"
turnItOn: "เปิดใช้งาน"
turnItOff: "ปิดใช้งาน"
emojiMute: "ปิดเสียงเอโมจิ"
emojiUnmute: "เลิกปิดเสียงเอโมจิ"
muteX: "ปิดเสียง {x}"
unmuteX: "เลิกปิดเสียง {x}"
abort: "หยุดและยกเลิก"
tip: "คำแนะนำและเคล็ดลับ"
redisplayAllTips: "แสดงคำแนะนำและเคล็ดลับทั้งหมดอีกครั้ง"
hideAllTips: "ซ่อนคำแนะนำและเคล็ดลับทั้งหมด"
defaultImageCompressionLevel: "ความละเอียดเริ่มต้นสำหรับการบีบอัดภาพ"
defaultImageCompressionLevel_description: "หากตั้งค่าต่ำ จะรักษาคุณภาพภาพได้ดีขึ้นแต่ขนาดไฟล์จะเพิ่มขึ้น<br>หากตั้งค่าสูง จะลดขนาดไฟล์ได้ แต่คุณภาพภาพจะลดลง"
inMinutes: "นาที"
inDays: "วัน"
_order:
newest: "เรียงจากใหม่ไปเก่า"
oldest: "เรียงจากเก่าไปใหม่"
_chat: _chat:
noMessagesYet: "ยังไม่มีข้อความ"
newMessage: "ข้อความใหม่"
individualChat: "แชตส่วนตัว"
individualChat_description: "สามารถแชตแบบตัวต่อตัวกับผู้ใช้ที่ระบุไว้ได้"
roomChat: "ห้องแชต"
roomChat_description: "สามารถแชตแบบกลุ่มหลายคนได้\nและสามารถแชตกับผู้ใช้ที่ไม่ได้อนุญาตแชตส่วนตัวได้ หากอีกฝ่ายยอมรับ"
createRoom: "สร้างห้อง"
inviteUserToChat: "เชิญผู้ใช้และเริ่มแชตได้เลย"
yourRooms: "ห้องที่สร้างไว้"
joiningRooms: "ห้องที่เข้าร่วมอยู่"
invitations: "คำเชิญ" invitations: "คำเชิญ"
noInvitations: "ไม่มีคำเชิญ"
history: "ประวัติ"
noHistory: "ไม่มีประวัติ" noHistory: "ไม่มีประวัติ"
noRooms: "ไม่มีห้อง"
inviteUser: "เชิญผู้ใช้"
sentInvitations: "คำเชิญที่ส่งไปแล้ว"
join: "เข้าร่วม"
ignore: "ไม่สนใจ"
leave: "ออกจากห้อง"
members: "สมาชิก" members: "สมาชิก"
searchMessages: "ค้นหาข้อความ"
home: "หน้าหลัก" home: "หน้าหลัก"
send: "ส่ง" send: "ส่ง"
newline: "ขึ้นบรรทัดใหม่"
muteThisRoom: "ปิดเสียงห้องนี้"
deleteRoom: "ลบห้อง"
chatNotAvailableForThisAccountOrServer: "แชตไม่ได้เปิดใช้งานบนเซิร์ฟเวอร์นี้ หรือบัญชีนี้"
chatIsReadOnlyForThisAccountOrServer: "แชตบนเซิร์ฟเวอร์นี้ หรือบัญชีนี้ เป็นแบบอ่านอย่างเดียว ไม่สามารถส่งข้อความใหม่ สร้างหรือเข้าร่วมห้องแชตได้"
chatNotAvailableInOtherAccount: "บัญชีคู่สนทนาไม่สามารถใช้ฟังก์ชันแชตได้"
cannotChatWithTheUser: "ไม่สามารถเริ่มแชตกับผู้ใช้นี้ได้"
cannotChatWithTheUser_description: "แชตใช้งานไม่ได้ หรือคู่สนทนายังไม่ได้เปิดแชต"
youAreNotAMemberOfThisRoomButInvited: "คุณไม่ได้เป็นผู้เข้าร่วมห้องนี้ แต่มีคำเชิญส่งมา หากต้องการเข้าร่วม กรุณายืนยันคำเชิญ"
doYouAcceptInvitation: "ต้องการยอมรับคำเชิญหรือไม่?"
chatWithThisUser: "แชตเลย"
thisUserAllowsChatOnlyFromFollowers: "ผู้ใช้นี้รับแชตเฉพาะจากผู้ติดตามเท่านั้น"
thisUserAllowsChatOnlyFromFollowing: "ผู้ใช้นี้รับแชตเฉพาะจากผู้ที่เขาติดตามเท่านั้น"
thisUserAllowsChatOnlyFromMutualFollowing: "ผู้ใช้นี้รับแชตเฉพาะจากผู้ที่ติดตามซึ่งกันและกันทั้งสองฝ่ายเท่านั้น"
thisUserNotAllowedChatAnyone: "ผู้ใช้นี้ไม่รับแชตจากใครเลย"
chatAllowedUsers: "ผู้ที่อนุญาตให้แชตด้วย"
chatAllowedUsers_note: "ไม่ว่าจะตั้งค่ายังไง คุณยังสามารถแชตกับคนที่คุณส่งข้อความไปหาได้"
_chatAllowedUsers:
everyone: "ใครก็ได้หมด"
followers: "เฉพาะผู้ติดตามเท่านั้น"
following: "เฉพาะผู้ที่ตัวเองติดตามเท่านั้น"
mutual: "เฉพาะผู้ใช้ที่ติดตามซึ่งกันและกันทั้งสองฝ่ายเท่านั้น"
none: "ไม่อนุญาตให้ใครเลย"
_emojiPalette:
palettes: "จานสี"
enableSyncBetweenDevicesForPalettes: "เปิดใช้งานการซิงค์จานสีระหว่างอุปกรณ์"
paletteForMain: "จานสีหลักที่ใช้"
paletteForReaction: "จานสีที่ใช้ในการรีแอคชั่น"
_settings: _settings:
driveBanner: "สามารถจัดการและตั้งค่าไดรฟ์ ตรวจสอบการใช้งาน และตั้งค่าการอัปโหลดไฟล์ได้"
pluginBanner: "สามารถขยายความสามารถของไคลเอนต์ด้วยปลั๊กอินได้ ติดตั้ง ตั้งค่า และจัดการปลั๊กอินแต่ละตัวได้"
notificationsBanner: "สามารถตั้งค่าประเภทและขอบเขตของการแจ้งเตือนที่รับจากเซิร์ฟเวอร์ รวมถึงการแจ้งเตือนแบบพุช"
api: "API"
webhook: "Webhook" webhook: "Webhook"
serviceConnection: "การเชื่อมต่อกับบริการ"
serviceConnectionBanner: "สามารถจัดการและตั้งค่า Access Token และ Webhook เพื่อเชื่อมต่อกับแอปหรือบริการภายนอกได้"
accountData: "ข้อมูลบัญชี"
accountDataBanner: "สามารถจัดการข้อมูลบัญชีได้โดยส่งออกหรือนำเข้าไฟล์เก็บถาวร"
muteAndBlockBanner: "สามารถตั้งค่าการซ่อนเนื้อหา และจำกัดการกระทำจากผู้ใช้เฉพาะรายได้"
accessibilityBanner: "สามารถปรับแต่งรูปลักษณ์และพฤติกรรมของไคลเอนต์เพื่อให้เหมาะกับการใช้งานของตนเองมากขึ้น"
privacyBanner: "สามารถตั้งค่าความเป็นส่วนตัวของบัญชี เช่น ขอบเขตการเผยแพร่เนื้อหา ความสามารถในการค้นหา และการอนุมัติผู้ติดตาม"
securityBanner: "สามารถตั้งค่าความปลอดภัยของบัญชี เช่น รหัสผ่าน วิธีการเข้าสู่ระบบ แอปยืนยันตัวตน Passkey เป็นต้น"
preferencesBanner: "คุณสามารถกำหนดค่าพฤติกรรมโดยรวมของไคลเอนต์ได้ตามความต้องการของคุณ" preferencesBanner: "คุณสามารถกำหนดค่าพฤติกรรมโดยรวมของไคลเอนต์ได้ตามความต้องการของคุณ"
appearanceBanner: "สามารถตั้งค่ารูปลักษณ์และวิธีการแสดงผลของไคลเอนต์ตามความชอบได้"
soundsBanner: "สามารถตั้งค่าเสียงที่จะเล่นบนไคลเอนต์ได้"
timelineAndNote: "ไทม์ไลน์และโน้ต"
makeEveryTextElementsSelectable: "อนุญาตให้เลือกข้อความทั้งหมดได้"
makeEveryTextElementsSelectable_description: "หากเปิดใช้งาน อาจทำให้ความสะดวกในการใช้งานลดลงในบางสถานการณ์"
useStickyIcons: "ทำให้ไอคอนเคลื่อนตามการเลื่อน"
enableHighQualityImagePlaceholders: "แสดงภาพตัวแทนคุณภาพสูง"
uiAnimations: "ภาพเคลื่อนไหวของ UI"
showNavbarSubButtons: "แสดงปุ่มรองบนแถบนำทาง"
ifOn: "เมื่อเปิดใช้งาน"
ifOff: "เมื่อปิดใช้งาน"
enableSyncThemesBetweenDevices: "ซิงค์ธีมที่ติดตั้งระหว่างอุปกรณ์"
enablePullToRefresh: "ดึงเพื่ออัปเดต"
enablePullToRefresh_description: "สำหรับเมาส์ ให้กดปุ่มล้อกลางค้างไว้แล้วลาก"
realtimeMode_description: "เชื่อมต่อกับเซิร์ฟเวอร์และอัปเดตเนื้อหาแบบเรียลไทม์ อาจทำให้ใช้ปริมาณข้อมูลและแบตเตอรี่มากขึ้นได้"
contentsUpdateFrequency: "ความถี่ในการดึงข้อมูลเนื้อหา"
contentsUpdateFrequency_description: "ยิ่งตั้งค่าสูง เนื้อหาจะอัปเดตแบบเรียลไทม์มากขึ้น แต่ประสิทธิภาพอาจลดลง และการใช้ข้อมูลกับแบตเตอรี่จะเพิ่มมากขึ้น"
contentsUpdateFrequency_description2: "เมื่อโหมดเรียลไทม์เปิดอยู่ เนื้อหาจะอัปเดตแบบเรียลไทม์โดยไม่ขึ้นกับการตั้งค่านี้"
showUrlPreview: "แสดงตัวอย่าง URL"
showAvailableReactionsFirstInNote: "แสดงรีแอคชั่นที่ใช้ได้ไว้หน้าสุด"
_chat:
showSenderName: "แสดงชื่อผู้ส่ง"
sendOnEnter: "กด Enter เพื่อส่ง"
_preferencesProfile:
profileName: "ชื่อโปรไฟล์"
profileNameDescription: "กรุณาตั้งชื่อเพื่อระบุอุปกรณ์นี้"
profileNameDescription2: "เช่น: “คอมเครื่องหลัก”, “มือถือ” ฯลฯ"
manageProfiles: "จัดการโปรไฟล์"
_preferencesBackup:
autoBackup: "สำรองโดยอัตโนมัติ"
restoreFromBackup: "คืนค่าจากข้อมูลสำรอง"
noBackupsFoundTitle: "ไม่พบข้อมูลสำรอง"
noBackupsFoundDescription: "ไม่พบข้อมูลสำรองที่สร้างโดยอัตโนมัติ แต่หากมีข้อมูลสำรองที่บันทึกด้วยตนเอง สามารถนำเข้ามาเพื่อกู้คืนได้"
selectBackupToRestore: "กรุณาเลือกข้อมูลสำรองที่ต้องการกู้คืน"
youNeedToNameYourProfileToEnableAutoBackup: "จำเป็นต้องตั้งชื่อโปรไฟล์ก่อนจึงจะเปิดใช้งานการสำรองข้อมูลอัตโนมัติได้"
autoPreferencesBackupIsNotEnabledForThisDevice: "ยังไม่ได้เปิดใช้งานการสำรองข้อมูลอัตโนมัติบนอุปกรณ์นี้"
backupFound: "พบข้อมูลสำรองของการตั้งค่าแล้ว"
_accountSettings: _accountSettings:
requireSigninToViewContents: "ต้องเข้าสู่ระบบเพื่อดูเนื้อหา" requireSigninToViewContents: "ต้องเข้าสู่ระบบเพื่อดูเนื้อหา"
requireSigninToViewContentsDescription1: "ต้องเข้าสู่ระบบเพื่อดูบันทึกและเนื้อหาอื่น ๆ ทั้งหมดที่คุณสร้าง คาดว่าจะมีประสิทธิผลในการป้องกันไม่ให้ข้อมูลถูกเก็บรวบรวมโดยโปรแกรมรวบรวมข้อมูล" requireSigninToViewContentsDescription1: "กำหนดให้ต้องเข้าสู่ระบบก่อนจึงจะสามารถดูโน้ตหรือเนื้อหาทั้งหมดที่สร้างไว้ได้ ซึ่งช่วยป้องกันไม่ให้ข้อมูลถูกเก็บโดยบอตหรือ Crawler (โปรแกรมรวบรวมข้อมูล)"
requireSigninToViewContentsDescription2: "นอกจากนี้ จะไม่สามารถดูจากเซิร์ฟเวอร์ที่ไม่รองรับการดูตัวอย่าง URL (OGP), การฝังในหน้าเว็บ หรือการอ้างอิงหมายเหตุได้" requireSigninToViewContentsDescription2: "จะไม่สามารถแสดงผลจากเซิร์ฟเวอร์ที่ไม่รองรับการแสดงตัวอย่าง URL (OGP), การฝังในหน้าเว็บ, หรือการอ้างอิงโน้ตได้"
requireSigninToViewContentsDescription3: "เนื้อหาที่ถูกรวมเข้ากับเซิร์ฟเวอร์ระยะไกลอาจไม่อยู่ภายใต้ข้อจำกัดเหล่านี้" requireSigninToViewContentsDescription3: "เนื้อหาที่ถูกรวมผ่านสหพันธ์จากเซิร์ฟเวอร์ระยะไกลอาจไม่อยู่ภายใต้ข้อจำกัดเหล่านี้"
makeNotesFollowersOnlyBefore: "แสดงโน้ตเก่าเฉพาะกับผู้ติดตามเท่านั้น"
makeNotesFollowersOnlyBeforeDescription: "ขณะที่เปิดฟังก์ชันนี้ โน้ตที่เก่ากว่าหรือเลยเวลาที่กำหนดจะแสดงเฉพาะกับผู้ติดตามเท่านั้น หากปิดใช้งาน สถานะการเปิดเผยจะกลับไปเป็นแบบเดิม"
makeNotesHiddenBefore: "ทำให้โน้ตเก่าทั้งหมดเป็นแบบส่วนตัว"
makeNotesHiddenBeforeDescription: "ขณะที่เปิดฟังก์ชันนี้ โน้ตที่เก่ากว่าหรือเลยเวลาที่กำหนดจะแสดงเฉพาะกับตนเอง (กลายเป็นแบบส่วนตัว) หากปิดใช้งาน สถานะการเปิดเผยจะกลับไปเป็นแบบเดิม"
mayNotEffectForFederatedNotes: "โน้ตที่ถูกรวมผ่านสหพันธ์จากเซิร์ฟเวอร์ระยะไกลอาจไม่ได้รับผลจากการตั้งค่านี้"
mayNotEffectSomeSituations: "ข้อจำกัดเหล่านี้เป็นเพียงการกรองเบื้องต้น ในบางกรณี เช่น การดูจากเซิร์ฟเวอร์อื่นหรือในระหว่างการตรวจสอบโดยผู้ดูแล อาจไม่สามารถใช้งานได้"
notesHavePassedSpecifiedPeriod: "โน้ตที่เลยเวลาที่กำหนดไว้แล้ว"
notesOlderThanSpecifiedDateAndTime: "โน้ตก่อนเวลาที่กำหนดไว้"
_abuseUserReport: _abuseUserReport:
forward: "ส่ง​ต่อ" forward: "ส่ง​ต่อ"
forwardDescription: "ส่งรายงานไปยังเซิร์ฟเวอร์ระยะไกลโดยใช้บัญชีระบบที่ไม่ระบุตัวตน" forwardDescription: "ส่งรายงานไปยังเซิร์ฟเวอร์ระยะไกลโดยใช้บัญชีระบบที่ไม่ระบุตัวตน"
resolve: "แก้ไข" resolve: "แก้ไข"
accept: "ยอมรับ" accept: "ยอมรับ"
reject: "ปฏิเสธ" reject: "ปฏิเสธ"
resolveTutorial: "ถ้าหากรายงานนี้มีเนื้อหาถูกต้อง ให้เลือก \"ยอมรับ\" เพื่อปิดเคสกรณีนี้โดยถือว่าได้รับการแก้ไขแล้ว\nถ้าหากเนื้อหาในรายงานนี้นั้นไม่ถูกต้อง ให้เลือก \"ปฏิเสธ\" เพื่อปิดเคสกรณีนี้โดยถือว่าไม่ได้รับการแก้ไข" resolveTutorial: "ให้เลือก “ยอมรับ” หากรายงานนี้มีเนื้อหาชอบธรรม เพื่อทำเครื่องหมายว่ากรณีนี้ได้รับการแก้ไขในทางบวก\nให้เลือก “ปฏิเสธ” หากรายงานนี้มีเนื้อหาไม่สมเหตุผล เพื่อทำเครื่องหมายว่ากรณีนี้ได้รับการแก้ไขในทางลบ"
_delivery: _delivery:
status: "สถานะการจัดส่ง" status: "สถานะการจัดส่ง"
stop: "ระงับการส่ง" stop: "ระงับการส่ง"
@ -1335,6 +1507,7 @@ _delivery:
manuallySuspended: "หยุดชั่วคราวด้วยตนเอง" manuallySuspended: "หยุดชั่วคราวด้วยตนเอง"
goneSuspended: "เซิร์ฟเวอร์ถูกระงับเนื่องจากมีการลบเซิร์ฟเวอร์นี้" goneSuspended: "เซิร์ฟเวอร์ถูกระงับเนื่องจากมีการลบเซิร์ฟเวอร์นี้"
autoSuspendedForNotResponding: "เซิร์ฟเวอร์ถูกระงับเนื่องจากไม่ตอบสนอง" autoSuspendedForNotResponding: "เซิร์ฟเวอร์ถูกระงับเนื่องจากไม่ตอบสนอง"
softwareSuspended: "หยุดให้บริการ เนื่องจากเป็นซอฟต์แวร์ที่ถูกระงับการเผยแพร่"
_bubbleGame: _bubbleGame:
howToPlay: "วิธีเล่น" howToPlay: "วิธีเล่น"
hold: "ถือไว้" hold: "ถือไว้"
@ -1449,7 +1622,7 @@ _timelineDescription:
_serverRules: _serverRules:
description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ" description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ"
_serverSettings: _serverSettings:
iconUrl: "URL ไอคอน" iconUrl: "URL ของไอคอน"
appIconDescription: "ระบุไอคอนที่จะใช้เมื่อ {host} แสดงเป็นแอป" appIconDescription: "ระบุไอคอนที่จะใช้เมื่อ {host} แสดงเป็นแอป"
appIconUsageExample: "ตัวอย่างเช่น เมื่อถูกเพิ่มเป็น PWA หรือบุ๊กมาร์กบนหน้าจอหลักในสมาร์ทโฟน" appIconUsageExample: "ตัวอย่างเช่น เมื่อถูกเพิ่มเป็น PWA หรือบุ๊กมาร์กบนหน้าจอหลักในสมาร์ทโฟน"
appIconStyleRecommendation: "เนื่องจากอาจถูกครอบตัดเป็นสี่เหลี่ยมหรือวงกลม จึงแนะนำให้ใช้ภาพที่เผื่อพื้นที่รอบๆ ตัวโลโก้ไอคอนไว้" appIconStyleRecommendation: "เนื่องจากอาจถูกครอบตัดเป็นสี่เหลี่ยมหรือวงกลม จึงแนะนำให้ใช้ภาพที่เผื่อพื้นที่รอบๆ ตัวโลโก้ไอคอนไว้"
@ -1463,7 +1636,26 @@ _serverSettings:
reactionsBufferingDescription: "เมื่อเปิดใช้งานฟังก์ชันนี้ก็จะช่วยลด latency ในการสร้างปฏิกิริยา แต่อาจจะส่งผลให้ memory footprint ของ Redis เพิ่มขึ้นนะ" reactionsBufferingDescription: "เมื่อเปิดใช้งานฟังก์ชันนี้ก็จะช่วยลด latency ในการสร้างปฏิกิริยา แต่อาจจะส่งผลให้ memory footprint ของ Redis เพิ่มขึ้นนะ"
inquiryUrl: "URL สำหรับการติดต่อสอบถาม" inquiryUrl: "URL สำหรับการติดต่อสอบถาม"
inquiryUrlDescription: "ระบุ URL ของหน้าเว็บที่มีแบบฟอร์มสำหรับติดต่อผู้ดูแลเซิร์ฟเวอร์ หรือข้อมูลการติดต่อของผู้ดูแลเซิร์ฟเวอร์" inquiryUrlDescription: "ระบุ URL ของหน้าเว็บที่มีแบบฟอร์มสำหรับติดต่อผู้ดูแลเซิร์ฟเวอร์ หรือข้อมูลการติดต่อของผู้ดูแลเซิร์ฟเวอร์"
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "ถ้าหากไม่มีการตรวจสอบจากผู้ดูแลระบบหรือไม่มีความเคลื่อนไหวมาเป็นระยะเวลาหนึ่ง ระบบจะทำการปิดใช้งานฟังก์ชันนี้โดยอัตโนมัติ เพื่อลดความเสี่ยงในการถูกโจมตีด้วยสแปมและอื่นๆ" openRegistration: "เปิดให้สร้างบัญชีได้"
openRegistrationWarning: "การเปิดให้ลงทะเบียนมีความเสี่ยง แนะนำให้เปิดใช้งานเฉพาะในกรณีที่สามารถตรวจสอบเซิร์ฟเวอร์อย่างสม่ำเสมอและมีระบบรับมือกับปัญหาได้ทันท่วงที"
thisSettingWillAutomaticallyOffWhenModeratorsInactive: "หากไม่พบกิจกรรมของผู้ควบคุมในช่วงระยะเวลาหนึ่ง การตั้งค่านี้จะถูกปิดโดยอัตโนมัติเพื่อป้องกันสแปม"
deliverSuspendedSoftware: "ซอฟต์แวร์ที่หยุดการเผยแพร่"
deliverSuspendedSoftwareDescription: "เนื่องจากเหตุผลด้านช่องโหว่ เป็นต้น สามารถหยุดการแจกจ่ายโดยระบุชื่อซอฟต์แวร์ของเซิร์ฟเวอร์และช่วงของเวอร์ชันได้ ข้อมูลเวอร์ชันนี้เป็นข้อมูลที่เซิร์ฟเวอร์ให้มา จึงไม่สามารถรับประกันความน่าเชื่อถือได้ สามารถใช้การระบุช่วงเวอร์ชันแบบ semver ได้ แต่ถ้าระบุเป็น >= 2024.3.1 จะไม่รวมเวอร์ชันแบบกำหนดเอง เช่น 2024.3.1-custom.0 จึงแนะนำให้ระบุเป็น >= 2024.3.1-0 ซึ่งเป็นการระบุแบบ prerelease"
singleUserMode: "โหมดผู้ใช้คนเดียว"
singleUserMode_description: "หากมีเพียงตัวเองคนเดียวที่ใช้เซิร์ฟเวอร์นี้ การเปิดใช้งานโหมดนี้จะช่วยปรับการทำงานให้เหมาะสมที่สุด"
signToActivityPubGet: "ลงนามในคำขอ GET"
signToActivityPubGet_description: "โดยปกติควรเปิดใช้งาน แต่หากพบปัญหาเกี่ยวกับการสื่อสารในสหพันธ์ การปิดใช้งานอาจช่วยแก้ไขได้ แต่ในบางกรณี เซิร์ฟเวอร์อาจไม่สามารถสื่อสารได้เลยหากปิดใช้งานนี้"
proxyRemoteFiles: "พร็อกซีไฟล์ระยะไกล"
proxyRemoteFiles_description: "เมื่อเปิดใช้งาน จะทำหน้าที่เป็นพร็อกซีสำหรับไฟล์จากระยะไกล ช่วยในการสร้างภาพขนาดย่อและปกป้องความเป็นส่วนตัวของผู้ใช้"
allowExternalApRedirect: "อนุญาตการเปลี่ยนเส้นทางการสืบค้นผ่าน ActivityPub"
allowExternalApRedirect_description: "เมื่อเปิดใช้งาน จะอนุญาตให้เซิร์ฟเวอร์อื่นสืบค้นเนื้อหาของบุคคลที่สามผ่านเซิร์ฟเวอร์นี้ได้ แต่มีความเสี่ยงที่อาจเกิดการปลอมแปลงเนื้อหา"
userGeneratedContentsVisibilityForVisitor: "ขอบเขตการเปิดเผยเนื้อหาที่ผู้ใช้สร้างต่อบุคคลที่ไม่ได้เข้าร่วม (แขก)"
userGeneratedContentsVisibilityForVisitor_description: "ช่วยป้องกันปัญหาที่อาจเกิดขึ้นจากเนื้อหาระยะไกลที่ไม่เหมาะสม ซึ่งอาจถูกเผยแพร่ออกสู่อินเทอร์เน็ตโดยไม่ตั้งใจผ่านเซิร์ฟเวอร์ของตนเอง โดยเฉพาะในกรณีที่การดูแลควบคุมไม่ทั่วถึง"
userGeneratedContentsVisibilityForVisitor_description2: "การเปิดเผยเนื้อหาทั้งหมดในเซิร์ฟเวอร์รวมทั้งเนื้อหาที่รับมาจากระยะไกลสู่สาธารณะบนอินเทอร์เน็ตโดยไม่มีข้อจำกัดใดๆ มีความเสี่ยงโดยเฉพาะอย่างยิ่งสำหรับผู้ชมที่ไม่เข้าใจลักษณะของระบบแบบกระจาย อาจทำให้เกิดความเข้าใจผิดคิดว่าเนื้อหาที่มาจากระยะไกลนั้นเป็นเนื้อหาที่สร้างขึ้นภายในเซิร์ฟเวอร์นี้ จึงควรใช้ความระมัดระวังอย่างมาก"
_userGeneratedContentsVisibilityForVisitor:
all: "ทั้งหมดสาธารณะ"
localOnly: "เผยแพร่เป็นสาธารณะเฉพาะเนื้อหาท้องถิ่น เนื้อหาระยะไกลให้เป็นส่วนตัว"
none: "ทั้งหมดส่วนตัว"
_accountMigration: _accountMigration:
moveFrom: "ย้ายจากบัญชีอื่นมาที่บัญชีนี้" moveFrom: "ย้ายจากบัญชีอื่นมาที่บัญชีนี้"
moveFromSub: "สร้างนามแฝงไปยังบัญชีอื่น" moveFromSub: "สร้างนามแฝงไปยังบัญชีอื่น"
@ -1753,13 +1945,15 @@ _role:
baseRole: "แม่แบบบทบาท" baseRole: "แม่แบบบทบาท"
useBaseValue: "ใช้ตามแม่แบบบทบาท" useBaseValue: "ใช้ตามแม่แบบบทบาท"
chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด" chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด"
iconUrl: "URL ไอคอน" iconUrl: "URL ของไอคอน"
asBadge: "แสดงเป็นตรา" asBadge: "แสดงเป็นตรา"
descriptionOfAsBadge: "เมื่อเปิดใช้งาน ไอคอนบทบาทจะปรากฏถัดจากชื่อผู้ใช้" descriptionOfAsBadge: "หากเปิดใช้งาน จะมีไอคอนของบทบาท แสดงถัดจากชื่อผู้ใช้"
isExplorable: "ค้นหาผู้ใช้ได้ง่ายขึ้นโดยดูจากบทบาท" isExplorable: "ค้นหาผู้ใช้ได้ง่ายขึ้นโดยดูจากบทบาท"
descriptionOfIsExplorable: "เมื่อเปิดใช้งาน ไทมไลน์บทบาทนี้และสมาชิกที่มีบทบาทนี้จะเปิดเผยเป็นสาธารณะ" descriptionOfIsExplorable: "เมื่อเปิดใช้งาน ไทมไลน์บทบาทนี้และสมาชิกที่มีบทบาทนี้จะเปิดเผยเป็นสาธารณะ"
displayOrder: "ลำดับการแสดงผล" displayOrder: "ลำดับการแสดงผล"
descriptionOfDisplayOrder: "เลขที่สูงกว่าจะแสดงบน UI ก่อน" descriptionOfDisplayOrder: "เลขที่สูงกว่าจะแสดงบน UI ก่อน"
preserveAssignmentOnMoveAccount: "โอนสถานะการมอบหมายไปยังบัญชีที่ย้ายไป"
preserveAssignmentOnMoveAccount_description: "เมื่อเปิดใช้งาน บัญชีที่ได้รับบทบาทนี้เมื่อถูกย้ายไปบัญชีใหม่ บทบาทนี้จะถูกถ่ายทอดไปยังบัญชีปลายทางด้วย"
canEditMembersByModerator: "อนุญาตให้ผู้ควบคุมแก้ไขสมาชิก" canEditMembersByModerator: "อนุญาตให้ผู้ควบคุมแก้ไขสมาชิก"
descriptionOfCanEditMembersByModerator: "เมื่อเปิดใช้ นอกเหนือจากผู้ควบคุมและผู้ดูแลระบบแล้ว จะสามารถเพิ่มถอนบทบาทนี้แก่ผู้ใช้ได้ แต่เมื่อปิดใช้ จะมีเฉพาะผู้ดูแลระบบเท่านั้นที่จะสามารถดำเนินการได้" descriptionOfCanEditMembersByModerator: "เมื่อเปิดใช้ นอกเหนือจากผู้ควบคุมและผู้ดูแลระบบแล้ว จะสามารถเพิ่มถอนบทบาทนี้แก่ผู้ใช้ได้ แต่เมื่อปิดใช้ จะมีเฉพาะผู้ดูแลระบบเท่านั้นที่จะสามารถดำเนินการได้"
priority: "ลำดับความสำคัญ" priority: "ลำดับความสำคัญ"
@ -1779,8 +1973,9 @@ _role:
canManageCustomEmojis: "จัดการเอโมจิที่กำหนดเอง" canManageCustomEmojis: "จัดการเอโมจิที่กำหนดเอง"
canManageAvatarDecorations: "จัดการตกแต่งอวตาร" canManageAvatarDecorations: "จัดการตกแต่งอวตาร"
driveCapacity: "ความจุของไดรฟ์" driveCapacity: "ความจุของไดรฟ์"
maxFileSize: "ขนาดไฟล์สูงสุดที่สามารถอัปโหลดได้"
alwaysMarkNsfw: "ทำเครื่องหมายไฟล์ว่าเป็น NSFW เสมอ" alwaysMarkNsfw: "ทำเครื่องหมายไฟล์ว่าเป็น NSFW เสมอ"
canUpdateBioMedia: "อนุญาตให้ปรับปรุงไอคอนและแบนเนอร์" canUpdateBioMedia: "อนุญาตให้เปลี่ยนไอคอนประจำตัวและแบนเนอร์"
pinMax: "จํานวนสูงสุดของโน้ตที่ปักหมุดไว้" pinMax: "จํานวนสูงสุดของโน้ตที่ปักหมุดไว้"
antennaMax: "จำนวนสูงสุดของเสาอากาศ" antennaMax: "จำนวนสูงสุดของเสาอากาศ"
wordMuteMax: "จำนวนอักขระสูงสุดที่อนุญาตในการปิดเสียงคำ" wordMuteMax: "จำนวนอักขระสูงสุดที่อนุญาตในการปิดเสียงคำ"
@ -1794,12 +1989,18 @@ _role:
canHideAds: "ซ่อนโฆษณา" canHideAds: "ซ่อนโฆษณา"
canSearchNotes: "การใช้การค้นหาโน้ต" canSearchNotes: "การใช้การค้นหาโน้ต"
canUseTranslator: "การใช้งานแปล" canUseTranslator: "การใช้งานแปล"
avatarDecorationLimit: "จำนวนการตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้" avatarDecorationLimit: "จำนวนของตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้"
canImportAntennas: "อนุญาตให้นำเข้าเสาอากาศ" canImportAntennas: "อนุญาตให้นำเข้าเสาอากาศ"
canImportBlocking: "อนุญาตให้นำเข้าการบล็อก" canImportBlocking: "อนุญาตให้นำเข้าการบล็อก"
canImportFollowing: "อนุญาตให้นำเข้ารายการต่อไปนี้" canImportFollowing: "อนุญาตให้นำเข้ารายการต่อไปนี้"
canImportMuting: "อนุญาตให้นำเข้าการปิดกั้น" canImportMuting: "อนุญาตให้นำเข้าการปิดเสียง"
canImportUserLists: "อนุญาตให้นำเข้ารายการ" canImportUserLists: "อนุญาตให้นำเข้ารายการ"
chatAvailability: "อนุญาตให้แชต"
uploadableFileTypes: "ประเภทไฟล์ที่สามารถอัปโหลดได้"
uploadableFileTypes_caption: "สามารถระบุ MIME type ได้ โดยใช้การขึ้นบรรทัดใหม่เพื่อแยกหลายรายการ และสามารถใช้ดอกจัน (*) เพื่อระบุแบบไวลด์การ์ดได้ (เช่น: image/*)"
uploadableFileTypes_caption2: "ไฟล์บางประเภทอาจไม่สามารถระบุชนิดได้ หากต้องการอนุญาตไฟล์ลักษณะนั้น กรุณาเพิ่ม {x} ลงในรายการที่อนุญาต"
noteDraftLimit: "จำนวนโน้ตฉบับร่างที่สามารถสร้างได้บนฝั่งเซิร์ฟเวอร์"
watermarkAvailable: "มีฟังก์ชั่นลายน้ำให้เลือกใช้"
_condition: _condition:
roleAssignedTo: "มอบหมายให้มีบทบาทแบบทำมือ" roleAssignedTo: "มอบหมายให้มีบทบาทแบบทำมือ"
isLocal: "ผู้ใช้ท้องถิ่น" isLocal: "ผู้ใช้ท้องถิ่น"
@ -1959,10 +2160,12 @@ _theme:
install: "ติดตั้งธีม" install: "ติดตั้งธีม"
manage: "จัดการธีม" manage: "จัดการธีม"
code: "โค้ดธีม" code: "โค้ดธีม"
description: "รายละเอียด" copyThemeCode: "คัดลอกรหัสธีม"
description: "คำอธิบาย"
installed: "{name} ได้รับการติดตั้ง" installed: "{name} ได้รับการติดตั้ง"
installedThemes: "ธีมที่ติดตั้ง" installedThemes: "ธีมที่ติดตั้ง"
builtinThemes: "ธีมในตัว" builtinThemes: "ธีมในตัว"
instanceTheme: "ธีมของเซิร์ฟเวอร์"
alreadyInstalled: "ธีมนี้ได้รับการติดตั้งแล้ว" alreadyInstalled: "ธีมนี้ได้รับการติดตั้งแล้ว"
invalid: "รูปแบบของธีมนี้ไม่ถูกต้องนะ" invalid: "รูปแบบของธีมนี้ไม่ถูกต้องนะ"
make: "ทำธีม" make: "ทำธีม"
@ -1990,7 +2193,7 @@ _theme:
fg: "ข้อความ" fg: "ข้อความ"
focus: "โฟกัส" focus: "โฟกัส"
indicator: "ตัวบ่งชี้" indicator: "ตัวบ่งชี้"
panel: "แผงควบคุม" panel: "แผง"
shadow: "เงา" shadow: "เงา"
header: "ส่วนหัว" header: "ส่วนหัว"
navBg: "พื้นหลังแถบด้านข้าง" navBg: "พื้นหลังแถบด้านข้าง"
@ -2000,7 +2203,7 @@ _theme:
link: "ลิงก์" link: "ลิงก์"
hashtag: "แฮชแท็ก" hashtag: "แฮชแท็ก"
mention: "กล่าวถึง" mention: "กล่าวถึง"
mentionMe: "ได้กล่าวถึง (ฉัน)" mentionMe: "ได้กล่าวถึงคุณ"
renote: "รีโน้ต" renote: "รีโน้ต"
modalBg: "พื้นหลังโมดอล" modalBg: "พื้นหลังโมดอล"
divider: "ตัวแบ่ง" divider: "ตัวแบ่ง"
@ -2024,6 +2227,7 @@ _sfx:
noteMy: "โน้ตของตัวเอง" noteMy: "โน้ตของตัวเอง"
notification: "การเเจ้งเตือน" notification: "การเเจ้งเตือน"
reaction: "เมื่อเลือกรีแอคชั่น" reaction: "เมื่อเลือกรีแอคชั่น"
chatMessage: "ข้อความของแชต"
_soundSettings: _soundSettings:
driveFile: "ใช้เสียงจากไดรฟ์" driveFile: "ใช้เสียงจากไดรฟ์"
driveFileWarn: "เลือกไฟล์ในไดรฟ์ของคุณ" driveFileWarn: "เลือกไฟล์ในไดรฟ์ของคุณ"
@ -2066,15 +2270,15 @@ _2fa:
step3: "ป้อนโทเค็นที่แอปของคุณให้มาเพื่อเสร็จสิ้นการตั้งค่า" step3: "ป้อนโทเค็นที่แอปของคุณให้มาเพื่อเสร็จสิ้นการตั้งค่า"
setupCompleted: "ตั้งค่าสำเร็จแล้ว" setupCompleted: "ตั้งค่าสำเร็จแล้ว"
step4: "นับจากนี้เป็นต้นไปการพยายามเข้าสู่ระบบในอนาคตนั้น อาจจะต้องขอโทเค็นในการเข้าสู่ระบบดังกล่าว" step4: "นับจากนี้เป็นต้นไปการพยายามเข้าสู่ระบบในอนาคตนั้น อาจจะต้องขอโทเค็นในการเข้าสู่ระบบดังกล่าว"
securityKeyNotSupported: "เบราว์เซอร์ของคุณไม่รองรับคีย์ความปลอดภัยนะ" securityKeyNotSupported: "เว็บเบราว์เซอร์ที่ใช้งานอยู่ไม่รองรับ Security Key"
registerTOTPBeforeKey: "กรุณาตั้งค่าแอปยืนยันตัวตนเพื่อลงทะเบียนรหัสความปลอดภัยหรือรหัสผ่าน" registerTOTPBeforeKey: "ก่อนลงทะเบียน Security Key หรือ Passkey กรุณาตั้งค่าแอปยืนยันตัวตนก่อน"
securityKeyInfo: "นอกจากนี้การตรวจสอบความถูกต้องด้วยลายนิ้วมือหรือ PIN แล้ว คุณยังสามารถตั้งค่าการตรวจสอบสิทธิ์ผ่านคีย์ความปลอดภัยของฮาร์ดแวร์ที่รองรับ FIDO2 เพื่อเพิ่มความปลอดภัยให้กับบัญชีของคุณ" securityKeyInfo: "ลงทะเบียนกุญแจที่มาจาก WebAuthn เช่น Security Key แบบฮาร์ดแวร์ที่รองรับ FIDO2 การยืนยันตัวตนด้วยชีวมิติหรือ PIN บนอุปกรณ์ และ Passkey"
registerSecurityKey: "ลงทะเบียนรหัสความปลอดภัยหรือรหัสผ่าน" registerSecurityKey: "ลงทะเบียน Security Key หรือ Passkey"
securityKeyName: "ป้อนชื่อคีย์" securityKeyName: "ป้อนชื่อคีย์"
tapSecurityKey: "กรุณาทำตามเบราว์เซอร์ของคุณเพื่อลงทะเบียนรหัสความปลอดภัยหรือรหัสผ่าน" tapSecurityKey: "กรุณาทำตามคำแนะนำของเบราว์เซอร์เพื่อลงทะเบียน Security Key หรือ Passkey"
removeKey: "ลบคีย์ความปลอดภัยออก" removeKey: "ลบ Security Key ออก"
removeKeyConfirm: "ลบข้อมูลสำรอง {name} มั้ย?" removeKeyConfirm: "ลบข้อมูลสำรอง {name} มั้ย?"
whyTOTPOnlyRenew: "ไม่สามารถลบแอปตัวรับรองความถูกต้องได้ตราบใดที่มีการลงทะเบียนคีย์ความปลอดภัยไว้แล้ว" whyTOTPOnlyRenew: "ไม่สามารถลบแอปตัวรับรองความถูกต้องได้ตราบใดที่ยังมีการลงทะเบียน Security Key อยู่"
renewTOTP: "ตั้งค่าแอปยืนยันตัวตน" renewTOTP: "ตั้งค่าแอปยืนยันตัวตน"
renewTOTPConfirm: "วิธีการแบบนี้จะทําให้รหัสยืนยันจากแอพก่อนหน้าของคุณหยุดทํางานเลยนะ" renewTOTPConfirm: "วิธีการแบบนี้จะทําให้รหัสยืนยันจากแอพก่อนหน้าของคุณหยุดทํางานเลยนะ"
renewTOTPOk: "ตั้งค่าคอนฟิกใหม่" renewTOTPOk: "ตั้งค่าคอนฟิกใหม่"
@ -2171,6 +2375,7 @@ _permissions:
"read:federation": "รับข้อมูลเกี่ยวกับสหพันธ์" "read:federation": "รับข้อมูลเกี่ยวกับสหพันธ์"
"write:report-abuse": "รายงานการละเมิด" "write:report-abuse": "รายงานการละเมิด"
"write:chat": "เขียนหรือลบข้อความแชท" "write:chat": "เขียนหรือลบข้อความแชท"
"read:chat": "อ่านแชต"
_auth: _auth:
shareAccessTitle: "การให้สิทธิ์แอปพลิเคชัน" shareAccessTitle: "การให้สิทธิ์แอปพลิเคชัน"
shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?" shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?"
@ -2179,8 +2384,11 @@ _auth:
permissionAsk: "แอปพลิเคชันนี้ขอสิทธิ์ดังต่อไปนี้" permissionAsk: "แอปพลิเคชันนี้ขอสิทธิ์ดังต่อไปนี้"
pleaseGoBack: "กรุณากลับไปที่แอปพลิเคชัน" pleaseGoBack: "กรุณากลับไปที่แอปพลิเคชัน"
callback: "กำลังกลับไปที่แอปพลิเคชัน" callback: "กำลังกลับไปที่แอปพลิเคชัน"
accepted: "การเข้าถึงได้รับอนุญาต"
denied: "ปฏิเสธการเข้าใช้" denied: "ปฏิเสธการเข้าใช้"
scopeUser: "กำลังดำเนินการในฐานะผู้ใช้ต่อไปนี้"
pleaseLogin: "กรุณาเข้าสู่ระบบเพื่ออนุมัติแอปพลิเคชัน" pleaseLogin: "กรุณาเข้าสู่ระบบเพื่ออนุมัติแอปพลิเคชัน"
byClickingYouWillBeRedirectedToThisUrl: "หากอนุญาตการเข้าถึง ระบบจะเปลี่ยนเส้นทางไปยัง URL ด้านล่างโดยอัตโนมัติ"
_antennaSources: _antennaSources:
all: "โน้ตทั้งหมด" all: "โน้ตทั้งหมด"
homeTimeline: "โน้ตจากผู้ใช้ที่ติดตาม" homeTimeline: "โน้ตจากผู้ใช้ที่ติดตาม"
@ -2226,6 +2434,7 @@ _widgets:
chooseList: "เลือกรายชื่อ" chooseList: "เลือกรายชื่อ"
clicker: "คลิกเกอร์" clicker: "คลิกเกอร์"
birthdayFollowings: "วันเกิดผู้ใช้ในวันนี้" birthdayFollowings: "วันเกิดผู้ใช้ในวันนี้"
chat: "แชต"
_cw: _cw:
hide: "ซ่อน" hide: "ซ่อน"
show: "โหลดเพิ่มเติม" show: "โหลดเพิ่มเติม"
@ -2265,6 +2474,8 @@ _visibility:
disableFederation: "การปิดใช้งานสหพันธ์" disableFederation: "การปิดใช้งานสหพันธ์"
disableFederationDescription: "อย่าส่งข้อมูลไปยังเซิร์ฟเวอร์อื่น" disableFederationDescription: "อย่าส่งข้อมูลไปยังเซิร์ฟเวอร์อื่น"
_postForm: _postForm:
quitInspiteOfThereAreUnuploadedFilesConfirm: "มีไฟล์ที่ยังไม่ได้อัปโหลด ต้องการละทิ้งและปิดฟอร์มหรือไม่?"
uploaderTip: "ไฟล์ยังไม่ได้อัปโหลด สามารถตั้งค่าต่างๆ ได้จากเมนูของไฟล์ เช่น การเปลี่ยนชื่อ การครอปรูป การใส่ลายน้ำ และการบีบอัด ไฟล์จะถูกอัปโหลดโดยอัตโนมัติเมื่อโพสต์โน้ต"
replyPlaceholder: "ตอบกลับโน้ตนี้..." replyPlaceholder: "ตอบกลับโน้ตนี้..."
quotePlaceholder: "อ้างโน้ตนี้..." quotePlaceholder: "อ้างโน้ตนี้..."
channelPlaceholder: "โพสต์ลงช่อง..." channelPlaceholder: "โพสต์ลงช่อง..."
@ -2285,7 +2496,7 @@ _profile:
metadataDescription: "ใช้สิ่งเหล่านี้ คุณสามารถแสดงฟิลด์ข้อมูลเพิ่มเติมในโปรไฟล์ของคุณ" metadataDescription: "ใช้สิ่งเหล่านี้ คุณสามารถแสดงฟิลด์ข้อมูลเพิ่มเติมในโปรไฟล์ของคุณ"
metadataLabel: "ป้ายชื่อ" metadataLabel: "ป้ายชื่อ"
metadataContent: "เนื้อหา" metadataContent: "เนื้อหา"
changeAvatar: "เปลี่ยนอวาตาร์" changeAvatar: "เปลี่ยนไอคอนประจำตัว"
changeBanner: "เปลี่ยนแบนเนอร์" changeBanner: "เปลี่ยนแบนเนอร์"
verifiedLinkDescription: "หากป้อน URL ที่มีลิงก์ไปยังโปรไฟล์ของคุณ ไอคอนการยืนยันความเป็นเจ้าของจะแสดงถัดจากฟิลด์นั้น ๆ" verifiedLinkDescription: "หากป้อน URL ที่มีลิงก์ไปยังโปรไฟล์ของคุณ ไอคอนการยืนยันความเป็นเจ้าของจะแสดงถัดจากฟิลด์นั้น ๆ"
avatarDecorationMax: "คุณสามารถเพิ่มการตกแต่งได้สูงสุด {max}" avatarDecorationMax: "คุณสามารถเพิ่มการตกแต่งได้สูงสุด {max}"
@ -2298,7 +2509,7 @@ _exportOrImport:
clips: "คลิป" clips: "คลิป"
followingList: "กำลังติดตาม" followingList: "กำลังติดตาม"
muteList: "ปิดเสียง" muteList: "ปิดเสียง"
blockingList: "บล็อ" blockingList: "บล็อ"
userLists: "รายชื่อ" userLists: "รายชื่อ"
excludeMutingUsers: "ยกเว้นผู้ใช้ที่ปิดเสียง" excludeMutingUsers: "ยกเว้นผู้ใช้ที่ปิดเสียง"
excludeInactiveUsers: "ยกเว้นผู้ใช้ที่ไม่ได้ใช้งาน" excludeInactiveUsers: "ยกเว้นผู้ใช้ที่ไม่ได้ใช้งาน"
@ -2368,7 +2579,7 @@ _pages:
featured: "เป็นที่นิยม" featured: "เป็นที่นิยม"
inspector: "ตัวตรวจสอบ" inspector: "ตัวตรวจสอบ"
contents: "เนื้อหา" contents: "เนื้อหา"
content: "บล็อหน้าเพจ" content: "บล็อหน้าเพจ"
variables: "ตัวแปร" variables: "ตัวแปร"
title: "หัวข้อ" title: "หัวข้อ"
url: "URL ของหน้า" url: "URL ของหน้า"
@ -2380,7 +2591,7 @@ _pages:
fontSansSerif: "Sans Serif" fontSansSerif: "Sans Serif"
eyeCatchingImageSet: "ตั้งค่าภาพขนาดย่อ" eyeCatchingImageSet: "ตั้งค่าภาพขนาดย่อ"
eyeCatchingImageRemove: "ลบภาพขนาดย่อ" eyeCatchingImageRemove: "ลบภาพขนาดย่อ"
chooseBlock: "เพิ่มบล็อ" chooseBlock: "เพิ่มบล็อ"
enterSectionTitle: "ป้อนชื่อหัวข้อ" enterSectionTitle: "ป้อนชื่อหัวข้อ"
selectType: "เลือกชนิด" selectType: "เลือกชนิด"
contentBlocks: "เนื้อหา" contentBlocks: "เนื้อหา"
@ -2416,6 +2627,7 @@ _notification:
newNote: "โพสต์ใหม่" newNote: "โพสต์ใหม่"
unreadAntennaNote: "เสาอากาศ {name}" unreadAntennaNote: "เสาอากาศ {name}"
roleAssigned: "ได้รับบทบาท" roleAssigned: "ได้รับบทบาท"
chatRoomInvitationReceived: "ได้รับคำเชิญเข้าร่วมห้องแชต"
emptyPushNotificationMessage: "อัปเดตการแจ้งเตือนแบบพุชแล้ว" emptyPushNotificationMessage: "อัปเดตการแจ้งเตือนแบบพุชแล้ว"
achievementEarned: "รับความสำเร็จ" achievementEarned: "รับความสำเร็จ"
testNotification: "ทดสอบการแจ้งเตือน" testNotification: "ทดสอบการแจ้งเตือน"
@ -2428,7 +2640,9 @@ _notification:
followedBySomeUsers: "มีผู้ติดตาม {n} ราย" followedBySomeUsers: "มีผู้ติดตาม {n} ราย"
flushNotification: "ล้างประวัติการแจ้งเตือน" flushNotification: "ล้างประวัติการแจ้งเตือน"
exportOfXCompleted: "การดำเนินการส่งออก {x} ได้เสร็จสิ้นลงแล้ว" exportOfXCompleted: "การดำเนินการส่งออก {x} ได้เสร็จสิ้นลงแล้ว"
login: "มีคนล็อกอิน" login: "มีการเข้าสู่ระบบ"
createToken: "สร้างโทเค็นการเข้าถึงแล้ว"
createTokenDescription: "หากไม่ทราบสาเหตุของคำเชิญ กรุณาลบโทเค็นการเข้าถึงผ่านทาง “{text}”"
_types: _types:
all: "ทั้งหมด" all: "ทั้งหมด"
note: "โน้ตใหม่" note: "โน้ตใหม่"
@ -2442,9 +2656,11 @@ _notification:
receiveFollowRequest: "ได้รับคำร้องขอติดตาม" receiveFollowRequest: "ได้รับคำร้องขอติดตาม"
followRequestAccepted: "อนุมัติให้ติดตามแล้ว" followRequestAccepted: "อนุมัติให้ติดตามแล้ว"
roleAssigned: "ให้บทบาท" roleAssigned: "ให้บทบาท"
chatRoomInvitationReceived: "เชิญเข้าห้องแชต"
achievementEarned: "ปลดล็อกความสำเร็จแล้ว" achievementEarned: "ปลดล็อกความสำเร็จแล้ว"
exportCompleted: "กระบวนการส่งออกข้อมูลได้เสร็จสิ้นสมบูรณ์แล้ว" exportCompleted: "กระบวนการส่งออกข้อมูลได้เสร็จสิ้นสมบูรณ์แล้ว"
login: "เข้าสู่ระบบ" login: "เข้าสู่ระบบ"
createToken: "สร้างโทเค็นการเข้าถึง"
test: "ทดสอบระบบแจ้งเตือน" test: "ทดสอบระบบแจ้งเตือน"
app: "การแจ้งเตือนจากแอปที่มีลิงก์" app: "การแจ้งเตือนจากแอปที่มีลิงก์"
_actions: _actions:
@ -2454,6 +2670,9 @@ _notification:
_deck: _deck:
alwaysShowMainColumn: "แสดงคอลัมน์หลักเสมอ" alwaysShowMainColumn: "แสดงคอลัมน์หลักเสมอ"
columnAlign: "จัดแนวคอลัมน์" columnAlign: "จัดแนวคอลัมน์"
columnGap: "ช่องห่างระว่างคอลัมน์"
deckMenuPosition: "ตำแหน่งเมนูเด็ค"
navbarPosition: "ตำแหน่งของแถบนำทาง"
addColumn: "เพิ่มคอลัมน์" addColumn: "เพิ่มคอลัมน์"
newNoteNotificationSettings: "ตั้งค่าการแจ้งเตือนเมื่อมีโน้ตใหม่" newNoteNotificationSettings: "ตั้งค่าการแจ้งเตือนเมื่อมีโน้ตใหม่"
configureColumn: "ตั้งค่าคอลัมน์" configureColumn: "ตั้งค่าคอลัมน์"
@ -2472,6 +2691,7 @@ _deck:
useSimpleUiForNonRootPages: "แสดง UI ของ Root Page อย่างง่าย " useSimpleUiForNonRootPages: "แสดง UI ของ Root Page อย่างง่าย "
usedAsMinWidthWhenFlexible: "ความกว้างขั้นต่ำนั้นจะถูกใช้งานสำหรับสิ่งนี้เมื่อเปิดใช้งานตัวเลือก \"ปรับความกว้างอัตโนมัติ\" หากเลือกเปิดใช้งานแล้ว" usedAsMinWidthWhenFlexible: "ความกว้างขั้นต่ำนั้นจะถูกใช้งานสำหรับสิ่งนี้เมื่อเปิดใช้งานตัวเลือก \"ปรับความกว้างอัตโนมัติ\" หากเลือกเปิดใช้งานแล้ว"
flexible: "ปรับความกว้างอัตโนมัติ" flexible: "ปรับความกว้างอัตโนมัติ"
enableSyncBetweenDevicesForProfiles: "เปิดใช้งานการซิงค์ข้อมูลโปรไฟล์ระหว่างอุปกรณ์"
_columns: _columns:
main: "หลัก" main: "หลัก"
widgets: "วิดเจ็ต" widgets: "วิดเจ็ต"
@ -2483,6 +2703,7 @@ _deck:
mentions: "กล่าวถึงคุณ" mentions: "กล่าวถึงคุณ"
direct: "ไดเร็กต์" direct: "ไดเร็กต์"
roleTimeline: "บทบาทไทม์ไลน์" roleTimeline: "บทบาทไทม์ไลน์"
chat: "แชต"
_dialog: _dialog:
charactersExceeded: "คุณกำลังมีตัวอักขระเกินขีดจำกัดสูงสุดแล้วนะ! ปัจจุบันอยู่ที่ {current} จาก {max}" charactersExceeded: "คุณกำลังมีตัวอักขระเกินขีดจำกัดสูงสุดแล้วนะ! ปัจจุบันอยู่ที่ {current} จาก {max}"
charactersBelow: "คุณกำลังใช้อักขระต่ำกว่าขีดจำกัดขั้นต่ำเลยนะ! ปัจจุบันอยู่ที่ {current} จาก {min}" charactersBelow: "คุณกำลังใช้อักขระต่ำกว่าขีดจำกัดขั้นต่ำเลยนะ! ปัจจุบันอยู่ที่ {current} จาก {min}"
@ -2511,8 +2732,8 @@ _webhookSettings:
abuseReport: "เมื่อมีการรายงานจากผู้ใช้" abuseReport: "เมื่อมีการรายงานจากผู้ใช้"
abuseReportResolved: "เมื่อมีการจัดการกับการรายงานจากผู้ใช้" abuseReportResolved: "เมื่อมีการจัดการกับการรายงานจากผู้ใช้"
userCreated: "เมื่อผู้ใช้ถูกสร้างขึ้น" userCreated: "เมื่อผู้ใช้ถูกสร้างขึ้น"
inactiveModeratorsWarning: "เมื่อผู้ดูแลระบบไม่ได้ใช้งานมานานระยะหนึ่ง" inactiveModeratorsWarning: "เมื่อผู้ควบคุมไม่มีความเคลื่อนไหวในช่วงระยะเวลาหนึ่ง"
inactiveModeratorsInvitationOnlyChanged: "เมื่อผู้ดูแลระบบที่ไม่ได้ใช้งานมานาน และเซิร์ฟเวอร์เปลี่ยนเป็นแบบเชิญเข้าร่วมเท่านั้น" inactiveModeratorsInvitationOnlyChanged: "เมื่อผู้ควบคุมไม่มีความเคลื่อนไหวในช่วงระยะเวลาหนึ่ง ระบบจะเปลี่ยนเป็นแบบใช้คำเชิญโดยอัตโนมัติ"
deleteConfirm: "ต้องการลบ Webhook ใช่ไหม?" deleteConfirm: "ต้องการลบ Webhook ใช่ไหม?"
testRemarks: "คลิกปุ่มทางด้านขวาของสวิตช์เพื่อส่ง Webhook ทดสอบที่มีข้อมูลจำลอง" testRemarks: "คลิกปุ่มทางด้านขวาของสวิตช์เพื่อส่ง Webhook ทดสอบที่มีข้อมูลจำลอง"
_abuseReport: _abuseReport:
@ -2564,10 +2785,10 @@ _moderationLogTypes:
createAd: "สร้างโฆษณาแล้ว" createAd: "สร้างโฆษณาแล้ว"
deleteAd: "ลบโฆษณาออกแล้ว" deleteAd: "ลบโฆษณาออกแล้ว"
updateAd: "อัปเดตโฆษณาแล้ว" updateAd: "อัปเดตโฆษณาแล้ว"
createAvatarDecoration: "สร้างการตกแต่งไอคอนแล้ว" createAvatarDecoration: "สร้างของตกแต่งไอคอนแล้ว"
updateAvatarDecoration: "อัปเดตการตกแต่งไอคอนแล้ว" updateAvatarDecoration: "อัปเดตของตกแต่งไอคอนแล้ว"
deleteAvatarDecoration: "ลบการตกแต่งไอคอนแล้ว" deleteAvatarDecoration: "ลบของตกแต่งไอคอนแล้ว"
unsetUserAvatar: "ลบไอคอนผู้ใช้" unsetUserAvatar: "เลิกตั้งไอคอนประจำตัวแล้ว"
unsetUserBanner: "ลบแบนเนอร์ผู้ใช้" unsetUserBanner: "ลบแบนเนอร์ผู้ใช้"
createSystemWebhook: "สร้าง SystemWebhook" createSystemWebhook: "สร้าง SystemWebhook"
updateSystemWebhook: "อัปเดต SystemWebhook" updateSystemWebhook: "อัปเดต SystemWebhook"
@ -2579,6 +2800,8 @@ _moderationLogTypes:
deletePage: "เพจถูกลบออกไปแล้ว" deletePage: "เพจถูกลบออกไปแล้ว"
deleteFlash: "Play ถูกลบออกไปแล้ว" deleteFlash: "Play ถูกลบออกไปแล้ว"
deleteGalleryPost: "โพสต์แกลเลอรี่ถูกลบออกแล้ว" deleteGalleryPost: "โพสต์แกลเลอรี่ถูกลบออกแล้ว"
deleteChatRoom: "ลบห้องแชต"
updateProxyAccountDescription: "อัปเดตคำอธิบายของบัญชีพร็อกซี"
_fileViewer: _fileViewer:
title: "รายละเอียดไฟล์" title: "รายละเอียดไฟล์"
type: "ประเภทไฟล์" type: "ประเภทไฟล์"
@ -2586,6 +2809,7 @@ _fileViewer:
url: "URL" url: "URL"
uploadedAt: "วันที่เข้าร่วม" uploadedAt: "วันที่เข้าร่วม"
attachedNotes: "โน้ตที่แนบมาด้วย" attachedNotes: "โน้ตที่แนบมาด้วย"
usage: "ใช้แล้ว"
thisPageCanBeSeenFromTheAuthor: "หน้าเพจนี้จะสามารถปรากฏได้โดยผู้ใช้ที่อัปโหลดไฟล์นี้เท่านั้น" thisPageCanBeSeenFromTheAuthor: "หน้าเพจนี้จะสามารถปรากฏได้โดยผู้ใช้ที่อัปโหลดไฟล์นี้เท่านั้น"
_externalResourceInstaller: _externalResourceInstaller:
title: "ติดตั้งจากไซต์ภายนอก" title: "ติดตั้งจากไซต์ภายนอก"
@ -2631,8 +2855,14 @@ _dataSaver:
title: "โหลดสื่อ" title: "โหลดสื่อ"
description: "กันไม่ให้ภาพและวิดีโอโหลดโดยอัตโนมัติ แตะรูปภาพ/วิดีโอที่ซ่อนอยู่เพื่อโหลด" description: "กันไม่ให้ภาพและวิดีโอโหลดโดยอัตโนมัติ แตะรูปภาพ/วิดีโอที่ซ่อนอยู่เพื่อโหลด"
_avatar: _avatar:
title: "รูปไอคอน" title: "ปิดใช้งานภาพเคลื่อนไหวของไอคอนประจำตัว"
description: "ระงับการเคลื่อนไหวของภาพไอคอน ภาพเคลื่อนไหวอาจมีขนาดไฟล์ใหญ่กว่าภาพปกติ ดังนั้นจึงสามารถช่วยในการลดการใช้ข้อมูล" description: "ภาพเคลื่อนไหวของไอคอนประจำตัวจะหยุดทำงาน ภาพแบบเคลื่อนไหวมักมีขนาดไฟล์ใหญ่กว่าภาพปกติ จึงช่วยลดปริมาณการใช้ข้อมูลได้มากขึ้น"
_urlPreviewThumbnail:
title: "ซ่อนภาพขนาดย่อของการแสดงตัวอย่าง URL"
description: "ภาพขนาดย่อของการตัวอย่าง URL จะไม่ถูกโหลดอีกต่อไป"
_disableUrlPreview:
title: "ปิดการใช้งานแสดงตัวอย่าง URL"
description: "ปิดฟังก์ชันแสดงตัวอย่าง URL แตกต่างจากการซ่อนเพียงภาพขนาดย่อ ฟังก์ชันนี้จะช่วยลดการโหลดข้อมูลจากลิงก์ปลายทางทั้งหมด"
_code: _code:
title: "ไฮไลต์โค้ด" title: "ไฮไลต์โค้ด"
description: "หากใช้สัญลักษณ์ไฮไลต์โค้ดใน MFM ฯลฯ สัญลักษณ์เหล่านั้นจะไม่โหลดจนกว่าจะแตะ การไฮไลต์ไวยากรณ์(syntax)จำเป็นต้องดาวน์โหลดไฟล์คำจำกัดความของไฮไลต์สำหรับแต่ละภาษา ดังนั้นการปิดใช้งานการโหลดไฟล์เหล่านี้โดยอัตโนมัติจึงคาดว่าจะช่วยลดปริมาณข้อมูลการสื่อสารได้" description: "หากใช้สัญลักษณ์ไฮไลต์โค้ดใน MFM ฯลฯ สัญลักษณ์เหล่านั้นจะไม่โหลดจนกว่าจะแตะ การไฮไลต์ไวยากรณ์(syntax)จำเป็นต้องดาวน์โหลดไฟล์คำจำกัดความของไฮไลต์สำหรับแต่ละภาษา ดังนั้นการปิดใช้งานการโหลดไฟล์เหล่านี้โดยอัตโนมัติจึงคาดว่าจะช่วยลดปริมาณข้อมูลการสื่อสารได้"
@ -2683,13 +2913,15 @@ _reversi:
allowIrregularRules: "อนุญาตกฎที่ไม่ปรกติ (โหมดฟรีทุกอย่าง)" allowIrregularRules: "อนุญาตกฎที่ไม่ปรกติ (โหมดฟรีทุกอย่าง)"
disallowIrregularRules: "ไม่อนุญาตกฎที่ไม่ปรกติ" disallowIrregularRules: "ไม่อนุญาตกฎที่ไม่ปรกติ"
showBoardLabels: "แสดงหมายเลขแถว/คอลัมน์บนกระดาน" showBoardLabels: "แสดงหมายเลขแถว/คอลัมน์บนกระดาน"
useAvatarAsStone: "ใช้รูปอวตารเป็นหมาก" useAvatarAsStone: "ใช้ไอคอนประจำตัวเป็นหมาก"
_offlineScreen: _offlineScreen:
title: "ออฟไลน์ - ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้" title: "ออฟไลน์ - ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
header: "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้" header: "ไม่สามารถเชื่อมต่อกับเซิร์ฟเวอร์ได้"
_urlPreviewSetting: _urlPreviewSetting:
title: "การตั้งค่าการแสดงตัวอย่าง URL" title: "การตั้งค่าการแสดงตัวอย่าง URL"
enable: "เปิดใช้งานการแสดงตัวอย่าง URL" enable: "เปิดใช้งานการแสดงตัวอย่าง URL"
allowRedirect: "อนุญาตการเปลี่ยนเส้นทางไปยังปลายทางของการแสดงตัวอย่าง"
allowRedirectDescription: "ตั้งค่าว่าจะติดตามลิงก์ที่เปลี่ยนเส้นทาง (redirect) เพื่อแสดงตัวอย่างหรือไม่ เมื่อมีการป้อน URL ที่มีการเปลี่ยนเส้นทาง หากปิดการใช้งาน จะช่วยประหยัดทรัพยากรของเซิร์ฟเวอร์ แต่จะไม่สามารถแสดงเนื้อหาจากปลายทางที่เปลี่ยนเส้นทางได้"
timeout: "เวลาจำกัดในการโหลดตัวอย่าง URL (ms)" timeout: "เวลาจำกัดในการโหลดตัวอย่าง URL (ms)"
timeoutDescription: "หากเวลาที่ใช้ในการโหลดเกินค่านี้ จะไม่มีการสร้างการแสดงตัวอย่าง" timeoutDescription: "หากเวลาที่ใช้ในการโหลดเกินค่านี้ จะไม่มีการสร้างการแสดงตัวอย่าง"
maximumContentLength: "ค่าสูงสุดของ Content-Length (byte)" maximumContentLength: "ค่าสูงสุดของ Content-Length (byte)"
@ -2710,6 +2942,62 @@ _contextMenu:
app: "แอปพลิเคชัน" app: "แอปพลิเคชัน"
appWithShift: "แอปฟลิเคชันด้วยปุ่มยกแคร่ (Shift)" appWithShift: "แอปฟลิเคชันด้วยปุ่มยกแคร่ (Shift)"
native: "UI ของเบราว์เซอร์" native: "UI ของเบราว์เซอร์"
_gridComponent:
_error:
requiredValue: "ค่านี้จำเป็นต้องกรอก"
columnTypeNotSupport: "การตรวจสอบค่าด้วย regex รองรับเฉพาะคอลัมน์ที่เป็น type:text"
patternNotMatch: "ค่านี้ไม่ตรงกับรูปแบบ {pattern}"
notUnique: "ค่านี้ต้องไม่ซ้ำกับค่าที่มีอยู่"
_roleSelectDialog:
notSelected: "ยังไม่มีการเลือก"
_customEmojisManager:
_gridCommon:
copySelectionRows: "คัดลอกแถวที่เลือกไว้"
copySelectionRanges: "คัดลือกที่เลือกไว้"
deleteSelectionRows: "ลบแถวที่เลือกไว้"
deleteSelectionRanges: "ล้างค่าช่วงที่เลือก"
searchSettings: "ตั้งค่าการค้นหา"
searchSettingCaption: "ตั้งค่าเงื่อนไขการค้นหาอย่างละเอียด"
searchLimit: "จำนวนรายการที่แสดง"
sortOrder: "ลำดับการเรียง"
registrationLogs: "ปูมการลงทะเบียน"
registrationLogsCaption: "จะแสดงปูมเมื่อมีการอัปเดตหรือลบเอโมจิ หากดำเนินการอัปเดต/ลบ หรือเปลี่ยนหน้า/รีโหลด หน้านี้ ปูมจะหายไป"
alertEmojisRegisterFailedDescription: "การอัปเดตหรือลบเอโมจิล้มเหลว กรุณาตรวจสอบรายละเอียดในปูมการลงทะเบียน"
_logs:
showSuccessLogSwitch: "แสดงปูมที่สำเร็จ"
failureLogNothing: "ไม่มีปูมความล้มเหลว"
logNothing: "ไม่มีปูม"
_remote:
selectionRowDetail: "รายละเอียดของแถวที่เลือก"
importSelectionRows: "นำเข้าแถวที่เลือก"
importSelectionRangesRows: "นำเข้าแถวในช่วงที่เลือก"
importEmojisButton: "นำเข้าเอโมจิที่ทำเครื่องหมายไว้"
confirmImportEmojisTitle: "นำเข้าเอโมจิ"
confirmImportEmojisDescription: "จะนำเข้าเอโมจิ {count} รายการที่ได้รับจากระยะไกล ทั้งนี้โปรดระมัดระวังเรื่องสิทธิ์การใช้งานเอโมจิ ดำเนินการหรือไม่?"
_local:
tabTitleList: "รายการเอโมจิที่ลงทะเบียนไว้แล้ว"
tabTitleRegister: "ลงทะเบียนเอโมจิ"
_list:
emojisNothing: "ยังไม่มีเอโมจิที่ลงทะเบียนไว้"
markAsDeleteTargetRows: "กำหนดแถวที่เลือกให้เป็นรายการสำหรับลบ"
markAsDeleteTargetRanges: "กำหนดช่วงแถวที่เลือกให้เป็นรายการสำหรับลบ"
alertUpdateEmojisNothingDescription: "ไม่มีการเปลี่ยนแปลงเอโมจิ"
alertDeleteEmojisNothingDescription: "ไม่มีเอโมจิที่อยู่ในรายการสำหรับลบ"
confirmMovePage: "ต้องการเปลี่ยนหน้าหรือไม่?"
confirmChangeView: "ต้องการเปลี่ยนการแสดงผลหรือไม่?"
confirmUpdateEmojisDescription: "จะอัปเดตเอโมจิ {count} รายการ ดำเนินการหรือไม่?"
confirmDeleteEmojisDescription: "จะลบเอโมจิที่ถูกทำเครื่องหมายไว้ {count} รายการ ดำเนินการหรือไม่?"
confirmResetDescription: "การเปลี่ยนแปลงทั้งหมดที่ทำมาจะถูกรีเซ็ต"
confirmMovePageDesciption: "มีการเปลี่ยนแปลงเอโมจิในหน้านี้ หากเปลี่ยนหน้าโดยไม่บันทึก การเปลี่ยนแปลงทั้งหมดจะถูกละทิ้ง"
dialogSelectRoleTitle: "ค้นหาบทบาทที่ตั้งค่าไว้ด้วยเอโมจิ"
_register:
uploadSettingTitle: "ตั้งค่าการอัปโหลด"
uploadSettingDescription: "สามารถกำหนดพฤติกรรมขณะอัปโหลดเอโมจิจากหน้าจอนี้ได้"
directoryToCategoryLabel: "ป้อนชื่อไดเรกทอรีเป็น \"category\""
directoryToCategoryCaption: "เมื่อทำการลากและวางไดเรกทอรี ชื่อจะถูกป้อนเป็น \"category\""
confirmRegisterEmojisDescription: "จะลงทะเบียนเอโมจิที่แสดงในรายการเป็นเอโมจิแบบกำหนดเองใหม่\nดำเนินการต่อหรือไม่? (เพื่อหลีกเลี่ยงภาระโหลดหนัก ระบบจะสามารถลงทะเบียนอีโมจิได้สูงสุด {count} รายการต่อครั้ง)"
confirmClearEmojisDescription: "ต้องการยกเลิกการแก้ไขและล้างรายการเอโมจิที่แสดงอยู่หรือไม่?"
confirmUploadEmojisDescription: "จะอัปโหลดไฟล์ {count} รายการที่ลากและวางไปยังไดรฟ์ ดำเนินการหรือไม่?"
_embedCodeGen: _embedCodeGen:
title: "ปรับแต่งโค้ดฝัง" title: "ปรับแต่งโค้ดฝัง"
header: "แสดงส่วนหัว" header: "แสดงส่วนหัว"
@ -2724,15 +3012,137 @@ _embedCodeGen:
generateCode: "สร้างโค้ดสำหรับการฝัง" generateCode: "สร้างโค้ดสำหรับการฝัง"
codeGenerated: "รหัสถูกสร้างขึ้นแล้ว" codeGenerated: "รหัสถูกสร้างขึ้นแล้ว"
codeGeneratedDescription: "นำโค้ดที่สร้างแล้วไปวางในเว็บไซต์ของคุณเพื่อฝังเนื้อหา" codeGeneratedDescription: "นำโค้ดที่สร้างแล้วไปวางในเว็บไซต์ของคุณเพื่อฝังเนื้อหา"
_selfXssPrevention:
warning: "คำเตือน"
title: "“ข้อความที่บอกให้วางบางอย่างในหน้าจอนี้” ทั้งหมดเป็นการหลอกลวง"
description1: "ถ้าวางบางอย่างที่นี่ อาจทำให้ผู้ไม่หวังดีเข้าควบคุมบัญชี หรือขโมยข้อมูลส่วนตัวได้"
description2: "ถ้าไม่เข้าใจอย่างชัดเจนว่าสิ่งที่กำลังจะวางคืออะไร %cให้หยุดการทำงานทันทีแล้วปิดหน้าต่างนี้"
description3: "ดูรายละเอียดเพิ่มเติมได้ที่นี่: {link}"
_followRequest:
recieved: "คำขอที่ได้รับ"
sent: "คำที่ส่งไป"
_remoteLookupErrors: _remoteLookupErrors:
_federationNotAllowed:
title: "ไม่สามารถสื่อสารกับเซิร์ฟเวอร์นี้ได้"
description: "การสื่อสารกับเซิร์ฟเวอร์นี้อาจถูกปิดใช้งาน หรือเซิร์ฟเวอร์นี้อาจจะได้บล็อกคุณ หรือคุณอาจจะได้บล็อกเซิร์ฟเวอร์นี้อยู่\nกรุณาติดต่อผู้ดูแลระบบเซิร์ฟเวอร์เพื่อสอบถามรายละเอียดเพิ่มเติม"
_uriInvalid:
title: "URI ไม่ถูกต้อง"
description: "มีปัญหาเกี่ยวกับ URI ที่ป้อน โปรดตรวจสอบว่าไม่มีอักขระที่ไม่สามารถใช้กับ URI"
_requestFailed:
title: "การร้องขอล้มเหลว"
description: "การสื่อสารกับเซิร์ฟเวอร์นี้ล้มเหลว เซิร์ฟเวอร์ปลายทางอาจล่ม หรืออาจป้อน URI ที่ไม่ถูกต้องหรือไม่มีอยู่"
_responseInvalid:
title: "ข้อมูลตอบสนองกลับไม่ถูกต้อง"
description: "สามารถเชื่อมต่อกับเซิร์ฟเวอร์นี้ได้ แต่ข้อมูลที่ได้รับไม่ถูกต้อง หากกำลังดึงข้อมูลจากเซิร์ฟเวอร์บุคคลที่สาม โปรดใช้ URI ที่สามารถดึงข้อมูลได้จากเซิร์ฟเวอร์ต้นทางโดยตรง"
_noSuchObject: _noSuchObject:
title: "ไม่พบหน้าที่ต้องการ" title: "ไม่พบหน้าที่ต้องการ"
description: "ไม่พบทรัพยากรที่ร้องขอ กรุณาตรวจสอบ URI อีกครั้ง"
_captcha:
verify: "กรุณาผ่าน CAPTCHA"
testSiteKeyMessage: "สามารถดูตัวอย่างได้โดยป้อนค่าทดสอบใน site key และ secret key\nดูรายละเอียดเพิ่มเติมได้ที่หน้าด้านล่างนี้"
_error:
_requestFailed:
title: "การร้องขอ CAPTCHA ล้มเหลว"
text: "โปรดลองใหม่ภายหลัง หรือ ตรวจสอบการตั้งค่าอีกครั้ง"
_verificationFailed:
title: "การยืนยัน CAPTCHA ล้มเหลว"
text: "กรุณาตรวจสอบอีกครั้งว่าการตั้งค่าถูกต้องหรือไม่"
_unknown:
title: "CAPTCHA เกิดข้อผิดพลาด"
text: "เกิดข้อผิดพลาดที่ไม่คาดคิด"
_bootErrors:
title: "การโหลดล้มเหลว"
serverError: "หากปัญหายังคงอยู่แม้ว่าจะรอสักครู่แล้วโหลดหน้าใหม่อีกครั้ง โปรดติดต่อผู้ดูแลระบบเซิร์ฟเวอร์พร้อมรหัสข้อผิดพลาดต่อไปนี้"
solution: "สิ่งต่อไปนี้อาจช่วยแก้ไขปัญหาได้"
solution1: "อัปเดตเบราว์เซอร์และระบบปฏิบัติการเป็นรุ่นล่าสุด"
solution2: "ปิดใช้งานตัวบล็อกโฆษณา"
solution3: "ล้างแคชเบราว์เซอร์"
solution4: "(Tor Browser) ตั้งค่า dom.webaudio.enabled เป็น true"
otherOption: "ตัวเลือกเพิ่มเติม"
otherOption1: "ลบการตั้งค่าและแคชของไคลเอนต์"
otherOption2: "เริ่มใช้งานไคลเอนต์แบบง่าย"
otherOption3: "เปิดเครื่องมือซ่อมแซม"
_search: _search:
searchScopeAll: "ทั้งหมด" searchScopeAll: "ทั้งหมด"
searchScopeLocal: "ท้องถิ่น" searchScopeLocal: "ท้องถิ่น"
searchScopeServer: "ระบุเซิร์ฟเวอร์"
searchScopeUser: "ผู้ใช้เฉพาะ" searchScopeUser: "ผู้ใช้เฉพาะ"
pleaseEnterServerHost: "กรุณากรอกโฮสต์ของเซิร์ฟเวอร์"
pleaseSelectUser: "กรุณาเลือกผู้ใช้"
serverHostPlaceholder: "ตัวอย่าง: misskey.example.com"
_serverSetupWizard:
installCompleted: "การติดตั้ง Misskey เสร็จสมบูรณ์แล้ว!"
firstCreateAccount: "ขั้นแรก ให้สร้างบัญชีผู้ดูแลระบบ"
accountCreated: "บัญชีผู้ดูแลระบบถูกสร้างขึ้นแล้ว!"
serverSetting: "การตั้งค่าเซิร์ฟเวอร์"
youCanEasilyConfigureOptimalServerSettingsWithThisWizard: "สามารถตั้งค่าเซิร์ฟเวอร์ได้อย่างง่ายดายด้วยวิซาร์ดนี้"
settingsYouMakeHereCanBeChangedLater: "สามารถเปลี่ยนแปลงการตั้งค่าเหล่านี้ในภายหลังได้"
howWillYouUseMisskey: "ต้องการใช้ Misskey อย่างไร?"
_use:
single: "เซิร์ฟเวอร์คนเดียว"
single_description: "ใช้งานเป็นเซิร์ฟเวอร์ส่วนตัวสำหรับตัวเองคนเดียว"
single_youCanCreateMultipleAccounts: "แม้จะใช้งานเป็นเซิร์ฟเวอร์ส่วนตัวสำหรับคนเดียว ก็สามารถสร้างบัญชีผู้ใช้หลายบัญชีได้ตามความจำเป็น"
group: "เซิร์ฟเวอร์กลุ่ม"
group_description: "เชิญผู้ใช้ที่เชื่อถือได้ มาเข้าร่วมใช้งานแบบหลายคน"
open: "เซิร์ฟเวอร์สาธารณะ"
open_description: "เปิดรับผู้ใช้จำนวนมากแบบไม่จำกัด"
openServerAdvice: "การเปิดรับผู้ใช้จำนวนมากมีความเสี่ยง ควรบริหารจัดการด้วยระบบดูแลที่เข้มงวดเพื่อรับมือกับปัญหาที่อาจเกิดขึ้น"
openServerAntiSpamAdvice: "เพื่อป้องกันไม่ให้เซิร์ฟเวอร์ของตนกลายเป็นแหล่งส่งสแปม ควรเปิดใช้งานฟีเจอร์ป้องกันบอต เช่น reCAPTCHA และใส่ใจเรื่องความปลอดภัยอย่างเคร่งครัด"
howManyUsersDoYouExpect: "คาดว่าจะมีผู้ใช้งานประมาณกี่คน?"
_scale:
small: "น้อยกว่า 100 คน (ขนาดเล็ก)"
medium: "เกิน 100 คน แต่น้อยกว่า 1000 คน (ขนาดกลาง)"
large: "เกิน 1000 คน (ขนาดใหญ่)"
largeScaleServerAdvice: "เซิร์ฟเวอร์ขนาดใหญ่อาจต้องการความรู้ด้านโครงสร้างพื้นฐานขั้นสูง เช่น การบาลานซ์โหลด หรือการทำสำเนาฐานข้อมูล"
doYouConnectToFediverse: "เชื่อมต่อกับ Fediverse หรือไม่?"
doYouConnectToFediverse_description1: "หากเชื่อมต่อกับเครือข่ายที่ประกอบด้วยเซิร์ฟเวอร์แบบกระจาย (Fediverse) จะสามารถแลกเปลี่ยนเนื้อหากับเซิร์ฟเวอร์อื่นๆ ได้"
doYouConnectToFediverse_description2: "การเชื่อมต่อกับ Fediverse เรียกว่า “สหพันธ์”"
youCanConfigureMoreFederationSettingsLater: "หลังจากนี้ยังสามารถตั้งค่าแบบขั้นสูง เช่น การกำหนดเซิร์ฟเวอร์ที่อนุญาตให้สหพันธ์ต่อกันได้เพิ่มเติม"
adminInfo: "ข้อมูลผู้ดูแลระบ"
adminInfo_description: "ตั้งค่าข้อมูลผู้ดูแลระบบที่จะใช้รับคำถามและติดต่อ"
adminInfo_mustBeFilled: "หากเปิดใช้เซิร์ฟเวอร์สาธารณะ หรือเปิดใช้งานสหพันธ์ จะต้องกรอกข้อมูลนี้"
followingSettingsAreRecommended: "แนะนำให้ตั้งค่าตามด้านล่างนี้"
applyTheseSettings: "ใช้การตั้งค่านี้"
skipSettings: "ข้ามการตั้งค่า"
settingsCompleted: "การตั้งค่าเสร็จสมบูรณ์แล้ว!"
settingsCompleted_description: "ขอบคุณที่สละเวลามาตั้งค่า ตอนนี้เซิร์ฟเวอร์พร้อมใช้งานได้ทันที"
settingsCompleted_description2: "การตั้งค่าเซิร์ฟเวอร์อย่างละเอียดสามารถทำได้จาก “แผงควบคุม”"
donationRequest: "คำขอรับบริจาค"
_donationRequest:
text1: "Misskey เป็นซอฟต์แวร์ฟรีที่พัฒนาโดยอาสาสมัคร"
text2: "เพื่อให้การพัฒนางานนี้สามารถดำเนินต่อไปได้ในอนาคต หากไม่เป็นการรบกวน รบกวนพิจารณาร่วมสมทบทุนด้วยนะคะ"
text3: "นอกจากนี้ยังมีสิทธิพิเศษสำหรับผู้สนับสนุนอีกด้วยค่ะ"
_uploader:
editImage: "แก้ไขรูปภาพ"
compressedToX: "บีบอัดเป็น {x}"
savedXPercent: "ประหยัดไป {x}%"
abortConfirm: "มีไฟล์ที่ยังไม่ได้อัปโหลด ต้องการยกเลิกหรือไม่?"
doneConfirm: "มีไฟล์ที่ยังไม่ได้อัปโหลด ต้องการดำเนินการให้เสร็จสิ้นหรือไม่?"
maxFileSizeIsX: "ขนาดไฟล์สูงสุดที่สามารถอัปโหลดได้คือ {x}"
allowedTypes: "ประเภทไฟล์ที่สามารถอัปโหลดได้"
tip: "ยังไม่มีไฟล์ถูกอัปโหลด สามารถ ตรวจสอบ ลบชื่อไฟล์ บีบอัด หรือครอปตัดภาพ ก่อนอัปโหลดได้ในหน้านี้ เมื่อพร้อมแล้วให้กดปุ่ม “อัปโหลด” เพื่อเริ่มการอัปโหลด"
_clientPerformanceIssueTip:
title: "หากรู้สึกว่าแบตเตอรี่หมดเร็ว"
makeSureDisabledAdBlocker: "โปรดปิดการใช้งานตัวบล็อกโฆษณา"
makeSureDisabledAdBlocker_description: "ตัวบล็อกโฆษณาอาจส่งผลต่อประสิทธิภาพ โปรดตรวจสอบว่าไม่ได้เปิดใช้งานผ่านฟังก์ชันของระบบปฏิบัติการ เบราว์เซอร์ หรือส่วนเสริมใดๆ"
makeSureDisabledCustomCss: "โปรดปิดการใช้งาน CSS แบบกำหนดเอง"
makeSureDisabledCustomCss_description: "การเขียนทับสไตล์อาจส่งผลต่อประสิทธิภาพ โปรดตรวจสอบว่าไม่มี CSS แบบกำหนดเองหรือส่วนเสริมที่แก้ไขสไตล์เปิดใช้งานอยู่"
makeSureDisabledAddons: "โปรดปิดการใช้งานส่วนเสริม"
makeSureDisabledAddons_description: "ส่วนเสริมบางตัวอาจรบกวนการทำงานของไคลเอนต์และทำให้ประสิทธิภาพลดลง กรุณาลองปิดส่วนเสริมในเบราว์เซอร์แล้วตรวจสอบอีกครั้ง"
_clip:
tip: "คลิปเป็นฟังก์ชันที่สามารถรวมโน้ตเข้าด้วยกัน"
_userLists:
tip: "สามารถสร้างรายชื่อที่มีผู้ใช้ใดก็ได้ เมื่อสร้างแล้ว รายชื่อนั้นจะแสดงเป็นไทม์ไลน์ได้"
watermark: "ลายน้ำ"
defaultPreset: "พรีเซ็ตเริ่มต้น"
_watermarkEditor: _watermarkEditor:
tip: "สามารถเพิ่มลายน้ำ เช่น ข้อมูลเครดิต ลงในภาพได้"
quitWithoutSaveConfirm: "ต้องการออกโดยไม่บันทึกหรือไม่?"
driveFileTypeWarn: "ไม่รองรับไฟล์นี้" driveFileTypeWarn: "ไม่รองรับไฟล์นี้"
driveFileTypeWarnDescription: "กรุณาเลือกไฟล์ภาพ"
title: "แก้ไขลายน้ำ"
cover: "ซ้อนทับทั่วทั้งพื้นที่"
repeat: "ปูให้เต็มพื้นที่"
opacity: "ความทึบแสง" opacity: "ความทึบแสง"
scale: "ขนาด" scale: "ขนาด"
text: "ข้อความ" text: "ข้อความ"
@ -2740,4 +3150,50 @@ _watermarkEditor:
type: "รูปแบบ" type: "รูปแบบ"
image: "รูปภาพ" image: "รูปภาพ"
advanced: "ขั้นสูง" advanced: "ขั้นสูง"
stripe: "ริ้ว"
stripeWidth: "ความกว้างเส้น"
stripeFrequency: "จำนวนเส้น"
angle: "แองเกิล" angle: "แองเกิล"
polkadot: "ลายจุด"
checker: "ช่องตาราง"
polkadotMainDotOpacity: "ความทึบของจุดหลัก"
polkadotMainDotRadius: "ขนาดของจุดหลัก"
polkadotSubDotOpacity: "ความทึบของจุดรอง"
polkadotSubDotRadius: "ขนาดของจุดรอง"
polkadotSubDotDivisions: "จำนวนจุดรอง"
_imageEffector:
title: "เอฟเฟกต์"
addEffect: "เพิ่มเอฟเฟกต์"
discardChangesConfirm: "ต้องการทิ้งการเปลี่ยนแปลงแล้วออกหรือไม่?"
_fxs:
chromaticAberration: "ความคลาดสี"
glitch: "กลิตช์"
mirror: "กระจก"
invert: "กลับสี"
grayscale: "ขาวดำเทา"
colorAdjust: "ปรับแก้สี"
colorClamp: "บีบอัดสี"
colorClampAdvanced: "บีบอัดสี (ขั้นสูง)"
distort: "บิดเบี้ยว"
threshold: "สองสี"
zoomLines: "เส้นความเข้มข้น"
stripe: "ริ้ว"
polkadot: "ลายจุด"
checker: "ช่องตาราง"
blockNoise: "บล็อกที่มีการรบกวน"
tearing: "ฉีกขาด"
drafts: "ร่าง"
_drafts:
select: "เลือกฉบับร่าง"
cannotCreateDraftAnymore: "ถึงจำนวนจำกัดที่ฉบับร่างที่สามารถสร้างได้แล้ว"
cannotCreateDraft: "ไม่สามารถสร้างฉบับร่างด้วยเนื้อหานี้ได้"
delete: "ลบฉบับร่าง"
deleteAreYouSure: "ต้องการลบฉบับร่างหรือไม่?"
noDrafts: "ไม่มีฉบับร่าง"
replyTo: "ตอบกลับ {user}"
quoteOf: "อ้างอิงถึงโน้ตของ {user}"
postTo: "โพสต์ไปยัง {channel}"
saveToDraft: "บันทึกเป็นฉบับร่าง"
restoreFromDraft: "คืนค่าจากฉบับร่าง"
restore: "กู้คืน"
listDrafts: "รายการฉบับร่าง"

View File

@ -8,6 +8,9 @@ search: "Пошук"
notifications: "Сповіщення" notifications: "Сповіщення"
username: "Ім'я користувача" username: "Ім'я користувача"
password: "Пароль" password: "Пароль"
initialPasswordForSetup: "Початковий пароль для налаштування"
initialPasswordIsIncorrect: "Початковий пароль для налаштування неправильний"
initialPasswordForSetupDescription: "Використайте пароль, вказаний у конфігураційному файлі, якщо ви встановлювали Misskey власноруч.\nЯкщо використовуєте сервіси хостингу Misskey, використайте наданий пароль.\nЯкщо ви не маєте паролю, лишіть порожнім щоб продовжити. "
forgotPassword: "Я забув пароль" forgotPassword: "Я забув пароль"
fetchingAsApObject: "Отримуємо з федіверсу..." fetchingAsApObject: "Отримуємо з федіверсу..."
ok: "OK" ok: "OK"
@ -45,6 +48,7 @@ pin: "Закріпити"
unpin: "Відкріпити" unpin: "Відкріпити"
copyContent: "Скопіювати контент" copyContent: "Скопіювати контент"
copyLink: "Скопіювати посилання" copyLink: "Скопіювати посилання"
copyRemoteLink: "Копіювати віддалене посилання"
delete: "Видалити" delete: "Видалити"
deleteAndEdit: "Видалити й редагувати" deleteAndEdit: "Видалити й редагувати"
deleteAndEditConfirm: "Ви впевнені, що хочете видалити цю нотатку та відредагувати її? Ви втратите всі реакції, поширення та відповіді на неї." deleteAndEditConfirm: "Ви впевнені, що хочете видалити цю нотатку та відредагувати її? Ви втратите всі реакції, поширення та відповіді на неї."
@ -57,6 +61,7 @@ copyUserId: "Копіювати ID користувача"
copyNoteId: "блокнот ID користувача" copyNoteId: "блокнот ID користувача"
copyFileId: "Скопіювати ідентифікатор файлу." copyFileId: "Скопіювати ідентифікатор файлу."
searchUser: "Пошук користувачів" searchUser: "Пошук користувачів"
searchThisUsersNotes: "Пошук нотаток користувача"
reply: "Відповісти" reply: "Відповісти"
loadMore: "Показати більше" loadMore: "Показати більше"
showMore: "Показати більше" showMore: "Показати більше"
@ -105,9 +110,11 @@ enterEmoji: "Введіть емодзі"
renote: "Поширити" renote: "Поширити"
unrenote: "Відміна поширення" unrenote: "Відміна поширення"
renoted: "Поширити запис." renoted: "Поширити запис."
renotedToX: "Поширено до {name}"
cantRenote: "Неможливо поширити." cantRenote: "Неможливо поширити."
cantReRenote: "Поширення не можливо поширити." cantReRenote: "Поширення не можливо поширити."
quote: "Цитата" quote: "Цитата"
inChannelRenote: "Поширено у канал"
pinnedNote: "Закріплений запис" pinnedNote: "Закріплений запис"
pinned: "Закріпити" pinned: "Закріпити"
you: "Ви" you: "Ви"
@ -116,6 +123,7 @@ sensitive: "NSFW"
add: "Додати" add: "Додати"
reaction: "Реакції" reaction: "Реакції"
reactions: "Реакції" reactions: "Реакції"
emojiPicker: "Вибір реакції"
reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб видалити, Натиснути \"+\" щоб додати." reactionSettingDescription2: "Перемістити щоб змінити порядок, Клацнути мишою щоб видалити, Натиснути \"+\" щоб додати."
rememberNoteVisibility: "Пам’ятати параметри видимісті" rememberNoteVisibility: "Пам’ятати параметри видимісті"
attachCancel: "Видалити вкладення" attachCancel: "Видалити вкладення"
@ -289,7 +297,9 @@ folderName: "Ім'я теки"
createFolder: "Створити теку" createFolder: "Створити теку"
renameFolder: "Перейменувати теку" renameFolder: "Перейменувати теку"
deleteFolder: "Видалити теку" deleteFolder: "Видалити теку"
folder: "Тека"
addFile: "Додати файл" addFile: "Додати файл"
showFile: "Показати файл"
emptyDrive: "Диск порожній" emptyDrive: "Диск порожній"
emptyFolder: "Тека порожня" emptyFolder: "Тека порожня"
unableToDelete: "Видалення неможливе" unableToDelete: "Видалення неможливе"
@ -302,6 +312,7 @@ copyUrl: "Копіювати URL"
rename: "Перейменувати" rename: "Перейменувати"
avatar: "Аватар" avatar: "Аватар"
banner: "Банер" banner: "Банер"
displayOfSensitiveMedia: "Показ чутливого медіа"
whenServerDisconnected: "Коли зв’язок із сервером втрачено" whenServerDisconnected: "Коли зв’язок із сервером втрачено"
disconnectedFromServer: "Зв’язок із сервером було перервано" disconnectedFromServer: "Зв’язок із сервером було перервано"
reload: "Оновити" reload: "Оновити"
@ -348,8 +359,11 @@ hcaptcha: "hCaptcha"
enableHcaptcha: "Увімкнути hCaptcha" enableHcaptcha: "Увімкнути hCaptcha"
hcaptchaSiteKey: "Ключ сайту" hcaptchaSiteKey: "Ключ сайту"
hcaptchaSecretKey: "Секретний ключ" hcaptchaSecretKey: "Секретний ключ"
mcaptcha: "MCaptcha"
enableMcaptcha: "Увімкнути MCaptcha"
mcaptchaSiteKey: "Ключ сайту" mcaptchaSiteKey: "Ключ сайту"
mcaptchaSecretKey: "Секретний ключ" mcaptchaSecretKey: "Секретний ключ"
mcaptchaInstanceUrl: "Посилання на сервер MCaptcha"
recaptcha: "reCAPTCHA" recaptcha: "reCAPTCHA"
enableRecaptcha: "Увімкнути reCAPTCHA" enableRecaptcha: "Увімкнути reCAPTCHA"
recaptchaSiteKey: "Ключ сайту" recaptchaSiteKey: "Ключ сайту"
@ -905,6 +919,8 @@ flip: "Перевернути"
lastNDays: "Останні {n} днів" lastNDays: "Останні {n} днів"
postForm: "Створення нотатки" postForm: "Створення нотатки"
information: "Інформація" information: "Інформація"
inMinutes: "х"
inDays: "д"
_chat: _chat:
invitations: "Запросити" invitations: "Запросити"
noHistory: "Історія порожня" noHistory: "Історія порожня"

View File

@ -1221,6 +1221,8 @@ information: "Giới thiệu"
chat: "Trò chuyện" chat: "Trò chuyện"
migrateOldSettings: "Di chuyển cài đặt cũ" migrateOldSettings: "Di chuyển cài đặt cũ"
migrateOldSettings_description: "Thông thường, quá trình này diễn ra tự động, nhưng nếu vì lý do nào đó mà quá trình di chuyển không thành công, bạn có thể kích hoạt thủ công quy trình di chuyển, quá trình này sẽ ghi đè lên thông tin cấu hình hiện tại của bạn." migrateOldSettings_description: "Thông thường, quá trình này diễn ra tự động, nhưng nếu vì lý do nào đó mà quá trình di chuyển không thành công, bạn có thể kích hoạt thủ công quy trình di chuyển, quá trình này sẽ ghi đè lên thông tin cấu hình hiện tại của bạn."
inMinutes: "phút"
inDays: "ngày"
_chat: _chat:
invitations: "Mời" invitations: "Mời"
noHistory: "Không có dữ liệu" noHistory: "Không có dữ liệu"

View File

@ -1318,7 +1318,7 @@ confirmOnReact: "发送回应前需要确认"
reactAreYouSure: "要用「{emoji}」进行回应吗?" reactAreYouSure: "要用「{emoji}」进行回应吗?"
markAsSensitiveConfirm: "要将此媒体标记为敏感吗?" markAsSensitiveConfirm: "要将此媒体标记为敏感吗?"
unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?" unmarkAsSensitiveConfirm: "要将此媒体解除敏感标记吗?"
preferences: "设置" preferences: "偏好设置"
accessibility: "辅助功能" accessibility: "辅助功能"
preferencesProfile: "设置的配置" preferencesProfile: "设置的配置"
copyPreferenceId: "复制设置 ID" copyPreferenceId: "复制设置 ID"
@ -1368,6 +1368,8 @@ redisplayAllTips: "重新显示所有的提示和技巧"
hideAllTips: "隐藏所有的提示和技巧" hideAllTips: "隐藏所有的提示和技巧"
defaultImageCompressionLevel: "默认图像压缩等级" defaultImageCompressionLevel: "默认图像压缩等级"
defaultImageCompressionLevel_description: "较低的等级可以保持画质,但会增加文件大小。<br>较高的等级可以减少文件大小,但相对应的画质将会降低。" defaultImageCompressionLevel_description: "较低的等级可以保持画质,但会增加文件大小。<br>较高的等级可以减少文件大小,但相对应的画质将会降低。"
inMinutes: "分"
inDays: "日"
_order: _order:
newest: "从新到旧" newest: "从新到旧"
oldest: "从旧到新" oldest: "从旧到新"
@ -1927,7 +1929,7 @@ _role:
name: "角色名称" name: "角色名称"
description: "角色描述" description: "角色描述"
permission: "角色权限" permission: "角色权限"
descriptionOfPermission: "<b>监察员</b>可以执行基本地审核操作。\n<b>管理员</b>可以更改服务器的所有设置。" descriptionOfPermission: "<b>监察员</b>可以执行基本的审核操作。\n<b>管理员</b>可以更改实例的所有设置。"
assignTarget: "授权对象" assignTarget: "授权对象"
descriptionOfAssignTarget: "<b>手动</b>指手动选择谁被包括在这个角色中。\n<b>符合条件</b>指设置条件以自动包括符合条件的用户。" descriptionOfAssignTarget: "<b>手动</b>指手动选择谁被包括在这个角色中。\n<b>符合条件</b>指设置条件以自动包括符合条件的用户。"
manual: "手动" manual: "手动"
@ -1998,6 +2000,7 @@ _role:
uploadableFileTypes_caption: "指定 MIME 类型。可用换行指定多个类型,也可以用星号(*)作为通配符。(如 image/*" uploadableFileTypes_caption: "指定 MIME 类型。可用换行指定多个类型,也可以用星号(*)作为通配符。(如 image/*"
uploadableFileTypes_caption2: "文件根据文件的不同,可能无法判断其类型。若要允许此类文件,请在指定中添加 {x}。" uploadableFileTypes_caption2: "文件根据文件的不同,可能无法判断其类型。若要允许此类文件,请在指定中添加 {x}。"
noteDraftLimit: "可在服务器上创建多少草稿" noteDraftLimit: "可在服务器上创建多少草稿"
watermarkAvailable: "能否使用水印功能"
_condition: _condition:
roleAssignedTo: "已分配给手动角色" roleAssignedTo: "已分配给手动角色"
isLocal: "是本地用户" isLocal: "是本地用户"
@ -2806,6 +2809,7 @@ _fileViewer:
url: "URL" url: "URL"
uploadedAt: "添加日期" uploadedAt: "添加日期"
attachedNotes: "附加到的帖子" attachedNotes: "附加到的帖子"
usage: "使用"
thisPageCanBeSeenFromTheAuthor: "此页只能被该文件的上传者查看。" thisPageCanBeSeenFromTheAuthor: "此页只能被该文件的上传者查看。"
_externalResourceInstaller: _externalResourceInstaller:
title: "从外部站点安装" title: "从外部站点安装"

View File

@ -638,7 +638,7 @@ inboxUrl: "收件夾 URL"
addedRelays: "已加入的中繼器" addedRelays: "已加入的中繼器"
serviceworkerInfo: "如要使用推播通知,需要啟用此選項並設定金鑰。" serviceworkerInfo: "如要使用推播通知,需要啟用此選項並設定金鑰。"
deletedNote: "已刪除的貼文" deletedNote: "已刪除的貼文"
invisibleNote: "私貼文" invisibleNote: "私密的貼文"
enableInfiniteScroll: "啟用自動滾動頁面模式" enableInfiniteScroll: "啟用自動滾動頁面模式"
visibility: "可見性" visibility: "可見性"
poll: "票選活動" poll: "票選活動"
@ -1368,6 +1368,8 @@ redisplayAllTips: "重新顯示所有「提示與技巧」"
hideAllTips: "隱藏所有「提示與技巧」" hideAllTips: "隱藏所有「提示與技巧」"
defaultImageCompressionLevel: "預設的影像壓縮程度" defaultImageCompressionLevel: "預設的影像壓縮程度"
defaultImageCompressionLevel_description: "低的話可以保留畫質,但是會增加檔案的大小。<br>高的話可以減少檔案大小,但是會降低畫質。" defaultImageCompressionLevel_description: "低的話可以保留畫質,但是會增加檔案的大小。<br>高的話可以減少檔案大小,但是會降低畫質。"
inMinutes: "分鐘"
inDays: "日"
_order: _order:
newest: "最新的在前" newest: "最新的在前"
oldest: "最舊的在前" oldest: "最舊的在前"
@ -1998,6 +2000,7 @@ _role:
uploadableFileTypes_caption: "請指定 MIME 類型。可以用換行區隔多個類型,也可以使用星號(*作為萬用字元進行指定。例如image/*\n" uploadableFileTypes_caption: "請指定 MIME 類型。可以用換行區隔多個類型,也可以使用星號(*作為萬用字元進行指定。例如image/*\n"
uploadableFileTypes_caption2: "有些檔案可能無法判斷其類型。若要允許這類檔案,請在指定中加入 {x}。" uploadableFileTypes_caption2: "有些檔案可能無法判斷其類型。若要允許這類檔案,請在指定中加入 {x}。"
noteDraftLimit: "伺服器端可建立的貼文草稿數量上限\n" noteDraftLimit: "伺服器端可建立的貼文草稿數量上限\n"
watermarkAvailable: "浮水印功能是否可用"
_condition: _condition:
roleAssignedTo: "手動指派角色完成" roleAssignedTo: "手動指派角色完成"
isLocal: "本地使用者" isLocal: "本地使用者"
@ -2806,6 +2809,7 @@ _fileViewer:
url: "URL" url: "URL"
uploadedAt: "加入日期" uploadedAt: "加入日期"
attachedNotes: "含有附件的貼文" attachedNotes: "含有附件的貼文"
usage: "使用情況"
thisPageCanBeSeenFromTheAuthor: "本頁面僅限上傳了這個檔案的使用者可以檢視。" thisPageCanBeSeenFromTheAuthor: "本頁面僅限上傳了這個檔案的使用者可以檢視。"
_externalResourceInstaller: _externalResourceInstaller:
title: "從外部網站安裝" title: "從外部網站安裝"

View File

@ -1,12 +1,12 @@
{ {
"name": "misskey", "name": "misskey",
"version": "2025.7.0-beta.0", "version": "2025.8.0-alpha.3",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",
"url": "https://github.com/misskey-dev/misskey.git" "url": "https://github.com/misskey-dev/misskey.git"
}, },
"packageManager": "pnpm@10.12.1", "packageManager": "pnpm@10.13.1",
"workspaces": [ "workspaces": [
"packages/frontend-shared", "packages/frontend-shared",
"packages/frontend", "packages/frontend",
@ -52,29 +52,29 @@
"lodash": "4.17.21" "lodash": "4.17.21"
}, },
"dependencies": { "dependencies": {
"cssnano": "7.0.7", "cssnano": "7.1.0",
"esbuild": "0.25.5", "esbuild": "0.25.6",
"execa": "9.6.0", "execa": "9.6.0",
"fast-glob": "3.3.3", "fast-glob": "3.3.3",
"glob": "11.0.2", "glob": "11.0.3",
"ignore-walk": "7.0.0", "ignore-walk": "7.0.0",
"js-yaml": "4.1.0", "js-yaml": "4.1.0",
"postcss": "8.5.4", "postcss": "8.5.6",
"tar": "7.4.3", "tar": "7.4.3",
"terser": "5.42.0", "terser": "5.43.1",
"typescript": "5.8.3" "typescript": "5.8.3"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/eslint-plugin": "2.1.0", "@misskey-dev/eslint-plugin": "2.1.0",
"@types/node": "22.15.31", "@types/node": "22.16.4",
"@typescript-eslint/eslint-plugin": "8.34.0", "@typescript-eslint/eslint-plugin": "8.37.0",
"@typescript-eslint/parser": "8.34.0", "@typescript-eslint/parser": "8.37.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "14.4.1", "cypress": "14.5.2",
"eslint": "9.28.0", "eslint": "9.31.0",
"globals": "16.2.0", "globals": "16.3.0",
"ncp": "2.0.0", "ncp": "2.0.0",
"pnpm": "10.12.1", "pnpm": "10.13.1",
"start-server-and-test": "2.0.12" "start-server-and-test": "2.0.12"
}, },
"optionalDependencies": { "optionalDependencies": {
@ -83,6 +83,9 @@
"pnpm": { "pnpm": {
"overrides": { "overrides": {
"@aiscript-dev/aiscript-languageserver": "-" "@aiscript-dev/aiscript-languageserver": "-"
},
"patchedDependencies": {
"typeorm": "patches/typeorm.patch"
} }
} }
} }

View File

@ -0,0 +1,24 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class NoActionOnDraftRelation1752502434151 {
name = 'NoActionOnDraftRelation1752502434151'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_CHANNEL_ID"`);
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_USER_ID"`);
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_RENOTE_ID"`);
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_NOTE_DRAFT_REPLY_ID"`);
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_e4983f28b4b18b03491536052f5" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "note_draft" DROP CONSTRAINT "FK_e4983f28b4b18b03491536052f5"`);
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_REPLY_ID" FOREIGN KEY ("replyId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_RENOTE_ID" FOREIGN KEY ("renoteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_USER_ID" FOREIGN KEY ("userId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note_draft" ADD CONSTRAINT "FK_NOTE_DRAFT_CHANNEL_ID" FOREIGN KEY ("channelId") REFERENCES "channel"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}

View File

@ -0,0 +1,79 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class MigrationCleanup1752509043847 {
name = 'MigrationCleanup1752509043847'
async up(queryRunner) {
// 1745378064470-composite-note-index.js created a index ON "note" ("userId", "id" DESC) as IDX_724b311e6f883751f261ebe378 but should be named IDX_a6f649630f55af3888e5a42919
await queryRunner.query(`ALTER INDEX "IDX_724b311e6f883751f261ebe378" RENAME TO "IDX_a6f649630f55af3888e5a42919"`);
// 1713656541000-abuse-report-notification.js generated system_webhook with hand-written SQL with CURRENT_TIMESTAMP as the default value, but its representation in TypeORM is `now()`
// see https://github.com/typeorm/typeorm/blob/f351757a15b9d2bd9d4222c69dcfd2316f46b5d1/src/driver/postgres/PostgresDriver.ts#L1575
await queryRunner.query(`ALTER TABLE "system_webhook" ALTER COLUMN "updatedAt" SET DEFAULT now()`);
// 1702718871541-ffVisibility.js defined a enum type "user_profile_followersVisibility_enum" but it should be "user_profile_followersvisibility_enum" (lowercase 'v') in typeorm
await queryRunner.query(`ALTER TYPE "public"."user_profile_followersVisibility_enum" RENAME TO "user_profile_followersvisibility_enum"`);
// 1713656541000-abuse-report-notification.js generated abuse_report_notification_recipient with hand-written SQL with CURRENT_TIMESTAMP as the default value, but its representation in TypeORM is `now()`
await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "updatedAt" SET DEFAULT now()`);
// 1690796169261-play-visibility.js added visibility column to flash table but it forgot to set NOT NULL constraint
await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "visibility" SET NOT NULL`);
// 1736686850345-createNoteDraft.js created note_draft with hand-written SQL but several types and comments are not correctly defined
await queryRunner.query(`CREATE TYPE "public"."note_draft_visibility_enum" AS ENUM('public', 'home', 'followers', 'specified')`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "id" SET DATA TYPE character varying(32)`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "replyId" SET DATA TYPE character varying(32)`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "renoteId" SET DATA TYPE character varying(32)`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "userId" SET DATA TYPE character varying(32)`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "channelId" SET DATA TYPE character varying(32)`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" SET DATA TYPE character varying(32) array`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" SET DATA TYPE character varying(32) array`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibility" SET DATA TYPE "public"."note_draft_visibility_enum" USING visibility::note_draft_visibility_enum`);
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."replyId" IS 'The ID of reply target.'`);
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."renoteId" IS 'The ID of renote target.'`);
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."userId" IS 'The ID of author.'`);
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."channelId" IS 'The ID of source channel.'`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "hasPoll" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollChoices" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollMultiple" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "localOnly" SET NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" SET NOT NULL`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "localOnly" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollMultiple" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "pollChoices" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "hasPoll" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" DROP NOT NULL`);
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."channelId" IS NULL`);
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."userId" IS 'The ID of author.'`);
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."renoteId" IS NULL`);
await queryRunner.query(`COMMENT ON COLUMN "note_draft"."replyId" IS NULL`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibility" SET DATA TYPE varchar`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "visibleUserIds" SET DATA TYPE varchar[]`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "fileIds" SET DATA TYPE varchar[]`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "channelId" SET DATA TYPE varchar`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "userId" SET DATA TYPE varchar`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "renoteId" SET DATA TYPE varchar`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "replyId" SET DATA TYPE varchar`);
await queryRunner.query(`ALTER TABLE "note_draft" ALTER COLUMN "id" SET DATA TYPE varchar`);
await queryRunner.query(`DROP TYPE "public"."note_draft_visibility_enum"`);
await queryRunner.query(`ALTER TABLE "flash" ALTER COLUMN "visibility" DROP NOT NULL`);
await queryRunner.query(`ALTER TABLE "abuse_report_notification_recipient" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP`);
await queryRunner.query(`ALTER TYPE "public"."user_profile_followersvisibility_enum" RENAME TO "user_profile_followersVisibility_enum"`);
await queryRunner.query(`ALTER TABLE "system_webhook" ALTER COLUMN "updatedAt" SET DEFAULT CURRENT_TIMESTAMP`);
await queryRunner.query(`ALTER INDEX "IDX_a6f649630f55af3888e5a42919" RENAME TO "IDX_724b311e6f883751f261ebe378"`);
}
}

View File

@ -0,0 +1,20 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class RemoteNotesCleaning1753863104203 {
name = 'RemoteNotesCleaning1753863104203'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ADD "enableRemoteNotesCleaning" boolean NOT NULL DEFAULT false`);
await queryRunner.query('ALTER TABLE "meta" ADD "remoteNotesCleaningMaxProcessingDurationInMinutes" integer NOT NULL DEFAULT \'60\'');
await queryRunner.query('ALTER TABLE "meta" ADD "remoteNotesCleaningExpiryDaysForEachNotes" integer NOT NULL DEFAULT \'90\'');
}
async down(queryRunner) {
await queryRunner.query('ALTER TABLE "meta" DROP COLUMN "remoteNotesCleaningExpiryDaysForEachNotes"');
await queryRunner.query('ALTER TABLE "meta" DROP COLUMN "remoteNotesCleaningMaxProcessingDurationInMinutes"');
await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "enableRemoteNotesCleaning"`);
}
}

View File

@ -0,0 +1,18 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class RemoveNoteConstraints1753868431598 {
name = 'RemoveNoteConstraints1753868431598'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_52ccc804d7c69037d558bac4c96"`);
await queryRunner.query(`ALTER TABLE "note" DROP CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5"`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_17cb3553c700a4985dff5a30ff5" FOREIGN KEY ("replyId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
await queryRunner.query(`ALTER TABLE "note" ADD CONSTRAINT "FK_52ccc804d7c69037d558bac4c96" FOREIGN KEY ("renoteId") REFERENCES "note"("id") ON DELETE CASCADE ON UPDATE NO ACTION`);
}
}

View File

@ -0,0 +1,16 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class TweakDefaultFederationSettings1754019326356 {
name = 'TweakDefaultFederationSettings1754019326356'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "federation" SET DEFAULT 'none'`);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "meta" ALTER COLUMN "federation" SET DEFAULT 'all'`);
}
}

View File

@ -4,7 +4,7 @@
"private": true, "private": true,
"type": "module", "type": "module",
"engines": { "engines": {
"node": "^20.10.0 || ^22.0.0" "node": "^22.15.0"
}, },
"scripts": { "scripts": {
"start": "node ./built/boot/entry.js", "start": "node ./built/boot/entry.js",
@ -33,6 +33,7 @@
"test:fed": "pnpm jest:fed", "test:fed": "pnpm jest:fed",
"test-and-coverage": "pnpm jest-and-coverage", "test-and-coverage": "pnpm jest-and-coverage",
"test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e", "test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e",
"check-migrations": "node scripts/check_migrations_clean.js",
"generate-api-json": "node ./scripts/generate_api_json.js" "generate-api-json": "node ./scripts/generate_api_json.js"
}, },
"optionalDependencies": { "optionalDependencies": {
@ -69,7 +70,7 @@
"dependencies": { "dependencies": {
"@aws-sdk/client-s3": "3.826.0", "@aws-sdk/client-s3": "3.826.0",
"@aws-sdk/lib-storage": "3.826.0", "@aws-sdk/lib-storage": "3.826.0",
"@discordapp/twemoji": "15.1.0", "@discordapp/twemoji": "16.0.1",
"@fastify/accepts": "5.0.2", "@fastify/accepts": "5.0.2",
"@fastify/cookie": "11.0.2", "@fastify/cookie": "11.0.2",
"@fastify/cors": "10.1.0", "@fastify/cors": "10.1.0",
@ -92,7 +93,7 @@
"@smithy/node-http-handler": "2.5.0", "@smithy/node-http-handler": "2.5.0",
"@swc/cli": "0.7.7", "@swc/cli": "0.7.7",
"@swc/core": "1.12.0", "@swc/core": "1.12.0",
"@twemoji/parser": "15.1.1", "@twemoji/parser": "16.0.0",
"@types/redis-info": "3.0.3", "@types/redis-info": "3.0.3",
"accepts": "1.3.8", "accepts": "1.3.8",
"ajv": "8.17.1", "ajv": "8.17.1",
@ -134,7 +135,7 @@
"jsrsasign": "11.1.0", "jsrsasign": "11.1.0",
"juice": "11.0.1", "juice": "11.0.1",
"meilisearch": "0.51.0", "meilisearch": "0.51.0",
"mfm-js": "0.24.0", "mfm-js": "0.25.0",
"microformats-parser": "2.0.3", "microformats-parser": "2.0.3",
"mime-types": "2.1.35", "mime-types": "2.1.35",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",

View File

@ -0,0 +1,26 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
// This script checks if the database migrations has been generated correctly.
import dataSource from '../ormconfig.js';
await dataSource.initialize();
const sqlInMemory = await dataSource.driver.createSchemaBuilder().log();
if (sqlInMemory.upQueries.length > 0 || sqlInMemory.downQueries.length > 0) {
console.error('There are several pending migrations. Please make sure you have generated the migrations correctly, or configured entities class correctly.');
for (const query of sqlInMemory.upQueries) {
console.error(`- ${query.query}`);
}
for (const query of sqlInMemory.downQueries) {
console.error(`- ${query.query}`);
}
process.exit(1);
} else {
console.log('All migrations are clean.');
process.exit(0);
}

View File

@ -20,6 +20,8 @@ import { CacheService } from '@/core/CacheService.js';
import { isReply } from '@/misc/is-reply.js'; import { isReply } from '@/misc/is-reply.js';
import { isInstanceMuted } from '@/misc/is-instance-muted.js'; import { isInstanceMuted } from '@/misc/is-instance-muted.js';
type NoteFilter = (note: MiNote) => boolean;
type TimelineOptions = { type TimelineOptions = {
untilId: string | null, untilId: string | null,
sinceId: string | null, sinceId: string | null,
@ -28,7 +30,7 @@ type TimelineOptions = {
me?: { id: MiUser['id'] } | undefined | null, me?: { id: MiUser['id'] } | undefined | null,
useDbFallback: boolean, useDbFallback: boolean,
redisTimelines: FanoutTimelineName[], redisTimelines: FanoutTimelineName[],
noteFilter?: (note: MiNote) => boolean, noteFilter?: NoteFilter,
alwaysIncludeMyNotes?: boolean; alwaysIncludeMyNotes?: boolean;
ignoreAuthorFromBlock?: boolean; ignoreAuthorFromBlock?: boolean;
ignoreAuthorFromMute?: boolean; ignoreAuthorFromMute?: boolean;
@ -79,7 +81,7 @@ export class FanoutTimelineEndpointService {
const shouldFallbackToDb = noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId; const shouldFallbackToDb = noteIds.length === 0 || ps.sinceId != null && ps.sinceId < oldestNoteId;
if (!shouldFallbackToDb) { if (!shouldFallbackToDb) {
let filter = ps.noteFilter ?? (_note => true); let filter = ps.noteFilter ?? (_note => true) as NoteFilter;
if (ps.alwaysIncludeMyNotes && ps.me) { if (ps.alwaysIncludeMyNotes && ps.me) {
const me = ps.me; const me = ps.me;
@ -145,15 +147,11 @@ export class FanoutTimelineEndpointService {
{ {
const parentFilter = filter; const parentFilter = filter;
filter = (note) => { filter = (note) => {
const noteJoined = note as MiNote & {
renoteUser: MiUser | null;
replyUser: MiUser | null;
};
if (!ps.ignoreAuthorFromUserSuspension) { if (!ps.ignoreAuthorFromUserSuspension) {
if (note.user!.isSuspended) return false; if (note.user!.isSuspended) return false;
} }
if (note.userId !== note.renoteUserId && noteJoined.renoteUser?.isSuspended) return false; if (note.userId !== note.renoteUserId && note.renote?.user?.isSuspended) return false;
if (note.userId !== note.replyUserId && noteJoined.replyUser?.isSuspended) return false; if (note.userId !== note.replyUserId && note.reply?.user?.isSuspended) return false;
return parentFilter(note); return parentFilter(note);
}; };
@ -200,7 +198,7 @@ export class FanoutTimelineEndpointService {
return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit); return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit);
} }
private async getAndFilterFromDb(noteIds: string[], noteFilter: (note: MiNote) => boolean, idCompare: (a: string, b: string) => number): Promise<MiNote[]> { private async getAndFilterFromDb(noteIds: string[], noteFilter: NoteFilter, idCompare: (a: string, b: string) => number): Promise<MiNote[]> {
const query = this.notesRepository.createQueryBuilder('note') const query = this.notesRepository.createQueryBuilder('note')
.where('note.id IN (:...noteIds)', { noteIds: noteIds }) .where('note.id IN (:...noteIds)', { noteIds: noteIds })
.innerJoinAndSelect('note.user', 'user') .innerJoinAndSelect('note.user', 'user')

View File

@ -421,7 +421,7 @@ export class NoteCreateService implements OnApplicationShutdown {
emojis, emojis,
userId: user.id, userId: user.id,
localOnly: data.localOnly!, localOnly: data.localOnly!,
reactionAcceptance: data.reactionAcceptance, reactionAcceptance: data.reactionAcceptance ?? null,
visibility: data.visibility as any, visibility: data.visibility as any,
visibleUserIds: data.visibility === 'specified' visibleUserIds: data.visibility === 'specified'
? data.visibleUsers ? data.visibleUsers
@ -483,7 +483,11 @@ export class NoteCreateService implements OnApplicationShutdown {
await this.notesRepository.insert(insert); await this.notesRepository.insert(insert);
} }
return insert; return {
...insert,
reply: data.reply ?? null,
renote: data.renote ?? null,
};
} catch (e) { } catch (e) {
// duplicate key error // duplicate key error
if (isDuplicateKeyValueError(e)) { if (isDuplicateKeyValueError(e)) {

View File

@ -62,7 +62,6 @@ export class NoteDeleteService {
*/ */
async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false, deleter?: MiUser) { async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false, deleter?: MiUser) {
const deletedAt = new Date(); const deletedAt = new Date();
const cascadingNotes = await this.findCascadingNotes(note);
if (note.replyId) { if (note.replyId) {
await this.notesRepository.decrement({ id: note.replyId }, 'repliesCount', 1); await this.notesRepository.decrement({ id: note.replyId }, 'repliesCount', 1);
@ -90,15 +89,6 @@ export class NoteDeleteService {
this.deliverToConcerned(user, note, content); this.deliverToConcerned(user, note, content);
} }
// also deliver delete activity to cascaded notes
const federatedLocalCascadingNotes = (cascadingNotes).filter(note => !note.localOnly && note.userHost == null); // filter out local-only notes
for (const cascadingNote of federatedLocalCascadingNotes) {
if (!cascadingNote.user) continue;
if (!this.userEntityService.isLocalUser(cascadingNote.user)) continue;
const content = this.apRendererService.addContext(this.apRendererService.renderDelete(this.apRendererService.renderTombstone(`${this.config.url}/notes/${cascadingNote.id}`), cascadingNote.user));
this.deliverToConcerned(cascadingNote.user, cascadingNote, content);
}
//#endregion //#endregion
this.notesChart.update(note, false); this.notesChart.update(note, false);
@ -118,9 +108,6 @@ export class NoteDeleteService {
} }
} }
for (const cascadingNote of cascadingNotes) {
this.searchService.unindexNote(cascadingNote);
}
this.searchService.unindexNote(note); this.searchService.unindexNote(note);
await this.notesRepository.delete({ await this.notesRepository.delete({
@ -140,29 +127,6 @@ export class NoteDeleteService {
} }
} }
@bindThis
private async findCascadingNotes(note: MiNote): Promise<MiNote[]> {
const recursive = async (noteId: string): Promise<MiNote[]> => {
const query = this.notesRepository.createQueryBuilder('note')
.where('note.replyId = :noteId', { noteId })
.orWhere(new Brackets(q => {
q.where('note.renoteId = :noteId', { noteId })
.andWhere('note.text IS NOT NULL');
}))
.leftJoinAndSelect('note.user', 'user');
const replies = await query.getMany();
return [
replies,
...await Promise.all(replies.map(reply => recursive(reply.id))),
].flat();
};
const cascadingNotes: MiNote[] = await recursive(note.id);
return cascadingNotes;
}
@bindThis @bindThis
private async getMentionedRemoteUsers(note: MiNote) { private async getMentionedRemoteUsers(note: MiNote) {
const where = [] as any[]; const where = [] as any[];

View File

@ -360,7 +360,7 @@ export class QueryService {
public generateSuspendedUserQueryForNote(q: SelectQueryBuilder<any>, excludeAuthor?: boolean): void { public generateSuspendedUserQueryForNote(q: SelectQueryBuilder<any>, excludeAuthor?: boolean): void {
if (excludeAuthor) { if (excludeAuthor) {
const brakets = (user: string) => new Brackets(qb => qb const brakets = (user: string) => new Brackets(qb => qb
.where(`note.${user}Id IS NULL`) .where(`${user}.id IS NULL`) // そもそもreplyやrenoteではない、もしくはleftjoinなどでuserが存在しなかった場合を考慮
.orWhere(`user.id = ${user}.id`) .orWhere(`user.id = ${user}.id`)
.orWhere(`${user}.isSuspended = FALSE`)); .orWhere(`${user}.isSuspended = FALSE`));
q q
@ -368,7 +368,7 @@ export class QueryService {
.andWhere(brakets('renoteUser')); .andWhere(brakets('renoteUser'));
} else { } else {
const brakets = (user: string) => new Brackets(qb => qb const brakets = (user: string) => new Brackets(qb => qb
.where(`note.${user}Id IS NULL`) .where(`${user}.id IS NULL`) // そもそもreplyやrenoteではない、もしくはleftjoinなどでuserが存在しなかった場合を考慮
.orWhere(`${user}.isSuspended = FALSE`)); .orWhere(`${user}.isSuspended = FALSE`));
q q
.andWhere('user.isSuspended = FALSE') .andWhere('user.isSuspended = FALSE')

View File

@ -17,6 +17,7 @@ import { bindThis } from '@/decorators.js';
import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js'; import type { Antenna } from '@/server/api/endpoints/i/import-antennas.js';
import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js'; import { ApRequestCreator } from '@/core/activitypub/ApRequestService.js';
import { type SystemWebhookPayload } from '@/core/SystemWebhookService.js'; import { type SystemWebhookPayload } from '@/core/SystemWebhookService.js';
import type { Packed } from '@/misc/json-schema.js';
import { type UserWebhookPayload } from './UserWebhookService.js'; import { type UserWebhookPayload } from './UserWebhookService.js';
import type { import type {
DbJobData, DbJobData,
@ -39,7 +40,6 @@ import type {
} from './QueueModule.js'; } from './QueueModule.js';
import type httpSignature from '@peertube/http-signature'; import type httpSignature from '@peertube/http-signature';
import type * as Bull from 'bullmq'; import type * as Bull from 'bullmq';
import type { Packed } from '@/misc/json-schema.js';
export const QUEUE_TYPES = [ export const QUEUE_TYPES = [
'system', 'system',
@ -53,6 +53,37 @@ export const QUEUE_TYPES = [
'systemWebhookDeliver', 'systemWebhookDeliver',
] as const; ] as const;
const REPEATABLE_SYSTEM_JOB_DEF = [{
name: 'tickCharts',
pattern: '55 * * * *',
}, {
name: 'resyncCharts',
pattern: '0 0 * * *',
}, {
name: 'cleanCharts',
pattern: '0 0 * * *',
}, {
name: 'aggregateRetention',
pattern: '0 0 * * *',
}, {
name: 'clean',
pattern: '0 0 * * *',
}, {
name: 'checkExpiredMutings',
pattern: '*/5 * * * *',
}, {
name: 'bakeBufferedReactions',
pattern: '0 0 * * *',
}, {
name: 'checkModeratorsActivity',
// 毎時30分に起動
pattern: '30 * * * *',
}, {
name: 'cleanRemoteNotes',
// 毎日午前4時に起動(最も人の少ない時間帯)
pattern: '0 4 * * *',
}];
@Injectable() @Injectable()
export class QueueService { export class QueueService {
constructor( constructor(
@ -69,61 +100,30 @@ export class QueueService {
@Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue, @Inject('queue:userWebhookDeliver') public userWebhookDeliverQueue: UserWebhookDeliverQueue,
@Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue, @Inject('queue:systemWebhookDeliver') public systemWebhookDeliverQueue: SystemWebhookDeliverQueue,
) { ) {
this.systemQueue.add('tickCharts', { for (const def of REPEATABLE_SYSTEM_JOB_DEF) {
}, { this.systemQueue.upsertJobScheduler(def.name, {
repeat: { pattern: '55 * * * *' }, pattern: def.pattern,
removeOnComplete: 10, }, {
removeOnFail: 30, name: def.name,
}); opts: {
// 期限ではなくcountで設定したいが、ジョブごとではなくキュー全体でカウントされるため、高頻度で実行されるジョブによって低頻度で実行されるジョブのログが消えることになる
removeOnComplete: {
age: 3600 * 24 * 7, // keep up to 7 days
},
removeOnFail: {
age: 3600 * 24 * 7, // keep up to 7 days
},
},
});
}
this.systemQueue.add('resyncCharts', { // 古いバージョンで作成され現在使われなくなったrepeatableジョブをクリーンアップ
}, { this.systemQueue.getJobSchedulers().then(schedulers => {
repeat: { pattern: '0 0 * * *' }, for (const scheduler of schedulers) {
removeOnComplete: 10, if (!REPEATABLE_SYSTEM_JOB_DEF.some(def => def.name === scheduler.key)) {
removeOnFail: 30, this.systemQueue.removeJobScheduler(scheduler.key);
}); }
}
this.systemQueue.add('cleanCharts', {
}, {
repeat: { pattern: '0 0 * * *' },
removeOnComplete: 10,
removeOnFail: 30,
});
this.systemQueue.add('aggregateRetention', {
}, {
repeat: { pattern: '0 0 * * *' },
removeOnComplete: 10,
removeOnFail: 30,
});
this.systemQueue.add('clean', {
}, {
repeat: { pattern: '0 0 * * *' },
removeOnComplete: 10,
removeOnFail: 30,
});
this.systemQueue.add('checkExpiredMutings', {
}, {
repeat: { pattern: '*/5 * * * *' },
removeOnComplete: 10,
removeOnFail: 30,
});
this.systemQueue.add('bakeBufferedReactions', {
}, {
repeat: { pattern: '0 0 * * *' },
removeOnComplete: 10,
removeOnFail: 30,
});
this.systemQueue.add('checkModeratorsActivity', {
}, {
// 毎時30分に起動
repeat: { pattern: '30 * * * *' },
removeOnComplete: 10,
removeOnFail: 30,
}); });
} }
@ -810,6 +810,13 @@ export class QueueService {
} }
} }
@bindThis
public async queueGetJobLogs(queueType: typeof QUEUE_TYPES[number], jobId: string) {
const queue = this.getQueue(queueType);
const result = await queue.getJobLogs(jobId);
return result.logs;
}
@bindThis @bindThis
public async queueGetJobs(queueType: typeof QUEUE_TYPES[number], jobTypes: JobType[], search?: string) { public async queueGetJobs(queueType: typeof QUEUE_TYPES[number], jobTypes: JobType[], search?: string) {
const RETURN_LIMIT = 100; const RETURN_LIMIT = 100;

View File

@ -67,6 +67,7 @@ export type RolePolicies = {
chatAvailability: 'available' | 'readonly' | 'unavailable'; chatAvailability: 'available' | 'readonly' | 'unavailable';
uploadableFileTypes: string[]; uploadableFileTypes: string[];
noteDraftLimit: number; noteDraftLimit: number;
watermarkAvailable: boolean;
}; };
export const DEFAULT_POLICIES: RolePolicies = { export const DEFAULT_POLICIES: RolePolicies = {
@ -111,6 +112,7 @@ export const DEFAULT_POLICIES: RolePolicies = {
'audio/*', 'audio/*',
], ],
noteDraftLimit: 10, noteDraftLimit: 10,
watermarkAvailable: true,
}; };
@Injectable() @Injectable()
@ -433,6 +435,7 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit {
return [...set]; return [...set];
}), }),
noteDraftLimit: calc('noteDraftLimit', vs => Math.max(...vs)), noteDraftLimit: calc('noteDraftLimit', vs => Math.max(...vs)),
watermarkAvailable: calc('watermarkAvailable', vs => vs.some(v => v === true)),
}; };
} }

View File

@ -227,9 +227,9 @@ export class SearchService {
if (opts.host) { if (opts.host) {
if (opts.host === '.') { if (opts.host === '.') {
query.andWhere('user.host IS NULL'); query.andWhere('note.userHost IS NULL');
} else { } else {
query.andWhere('user.host = :host', { host: opts.host }); query.andWhere('note.userHost = :host', { host: opts.host });
} }
} }

View File

@ -93,6 +93,11 @@ export class SignupService {
if (isPreserved) { if (isPreserved) {
throw new Error('USED_USERNAME'); throw new Error('USED_USERNAME');
} }
const hasProhibitedWords = this.utilityService.isKeyWordIncluded(username.toLowerCase(), this.meta.prohibitedWordsForNameOfUser);
if (hasProhibitedWords) {
throw new Error('USED_USERNAME');
}
} }
const keyPair = await new Promise<string[]>((res, rej) => const keyPair = await new Promise<string[]>((res, rej) =>

View File

@ -5,6 +5,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core'; import { ModuleRef } from '@nestjs/core';
import { EntityNotFoundError } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import { awaitAll } from '@/misc/prelude/await-all.js'; import { awaitAll } from '@/misc/prelude/await-all.js';
@ -90,6 +91,17 @@ export class NoteDraftEntityService implements OnModuleInit {
const packedFiles = options?._hint_?.packedFiles; const packedFiles = options?._hint_?.packedFiles;
const packedUsers = options?._hint_?.packedUsers; const packedUsers = options?._hint_?.packedUsers;
async function nullIfEntityNotFound<T>(promise: Promise<T>): Promise<T | null> {
try {
return await promise;
} catch (err) {
if (err instanceof EntityNotFoundError) {
return null;
}
throw err;
}
}
const packed: Packed<'NoteDraft'> = await awaitAll({ const packed: Packed<'NoteDraft'> = await awaitAll({
id: noteDraft.id, id: noteDraft.id,
createdAt: this.idService.parse(noteDraft.id).date.toISOString(), createdAt: this.idService.parse(noteDraft.id).date.toISOString(),
@ -117,15 +129,15 @@ export class NoteDraftEntityService implements OnModuleInit {
} : undefined, } : undefined,
...(opts.detail ? { ...(opts.detail ? {
reply: noteDraft.replyId ? this.noteEntityService.pack(noteDraft.replyId, me, { reply: noteDraft.replyId ? nullIfEntityNotFound(this.noteEntityService.pack(noteDraft.replyId, me, {
detail: false, detail: false,
skipHide: opts.skipHide, skipHide: opts.skipHide,
}) : undefined, })) : undefined,
renote: noteDraft.renoteId ? this.noteEntityService.pack(noteDraft.renoteId, me, { renote: noteDraft.renoteId ? nullIfEntityNotFound(this.noteEntityService.pack(noteDraft.renoteId, me, {
detail: true, detail: true,
skipHide: opts.skipHide, skipHide: opts.skipHide,
}) : undefined, })) : undefined,
poll: noteDraft.hasPoll ? { poll: noteDraft.hasPoll ? {
choices: noteDraft.pollChoices, choices: noteDraft.pollChoices,

View File

@ -4,7 +4,7 @@
*/ */
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm'; import { EntityNotFoundError, In } from 'typeorm';
import { ModuleRef } from '@nestjs/core'; import { ModuleRef } from '@nestjs/core';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
@ -46,6 +46,17 @@ function getAppearNoteIds(notes: MiNote[]): Set<string> {
return appearNoteIds; return appearNoteIds;
} }
async function nullIfEntityNotFound<T>(promise: Promise<T>): Promise<T | null> {
try {
return await promise;
} catch (err) {
if (err instanceof EntityNotFoundError) {
return null;
}
throw err;
}
}
@Injectable() @Injectable()
export class NoteEntityService implements OnModuleInit { export class NoteEntityService implements OnModuleInit {
private userEntityService: UserEntityService; private userEntityService: UserEntityService;
@ -436,19 +447,21 @@ export class NoteEntityService implements OnModuleInit {
...(opts.detail ? { ...(opts.detail ? {
clippedCount: note.clippedCount, clippedCount: note.clippedCount,
reply: note.replyId ? this.pack(note.reply ?? note.replyId, me, { // そもそもJOINしていない場合はundefined、JOINしたけど存在していなかった場合はnullで区別される
reply: (note.replyId && note.reply === null) ? null : note.replyId ? nullIfEntityNotFound(this.pack(note.reply ?? note.replyId, me, {
detail: false, detail: false,
skipHide: opts.skipHide, skipHide: opts.skipHide,
withReactionAndUserPairCache: opts.withReactionAndUserPairCache, withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
_hint_: options?._hint_, _hint_: options?._hint_,
}) : undefined, })) : undefined,
renote: note.renoteId ? this.pack(note.renote ?? note.renoteId, me, { // そもそもJOINしていない場合はundefined、JOINしたけど存在していなかった場合はnullで区別される
renote: (note.renoteId && note.renote === null) ? null : note.renoteId ? nullIfEntityNotFound(this.pack(note.renote ?? note.renoteId, me, {
detail: true, detail: true,
skipHide: opts.skipHide, skipHide: opts.skipHide,
withReactionAndUserPairCache: opts.withReactionAndUserPairCache, withReactionAndUserPairCache: opts.withReactionAndUserPairCache,
_hint_: options?._hint_, _hint_: options?._hint_,
}) : undefined, })) : undefined,
poll: note.hasPoll ? this.populatePoll(note, meId) : undefined, poll: note.hasPoll ? this.populatePoll(note, meId) : undefined,
@ -591,7 +604,7 @@ export class NoteEntityService implements OnModuleInit {
private findNoteOrFail(id: string): Promise<MiNote> { private findNoteOrFail(id: string): Promise<MiNote> {
return this.notesRepository.findOneOrFail({ return this.notesRepository.findOneOrFail({
where: { id }, where: { id },
relations: ['user'], relations: ['user', 'renote', 'reply'],
}); });
} }

File diff suppressed because one or more lines are too long

View File

@ -22,7 +22,7 @@ export class MiAbuseReportNotificationRecipient {
/** /**
* . * .
*/ */
@Index() @Index('IDX_abuse_report_notification_recipient_isActive')
@Column('boolean', { @Column('boolean', {
default: true, default: true,
}) })
@ -47,7 +47,7 @@ export class MiAbuseReportNotificationRecipient {
/** /**
* . * .
*/ */
@Index() @Index('IDX_abuse_report_notification_recipient_method')
@Column('varchar', { @Column('varchar', {
length: 64, length: 64,
}) })
@ -56,10 +56,11 @@ export class MiAbuseReportNotificationRecipient {
/** /**
* ID. * ID.
*/ */
@Index() @Index('IDX_abuse_report_notification_recipient_userId')
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
default: null,
}) })
public userId: MiUser['id'] | null; public userId: MiUser['id'] | null;
@ -75,17 +76,20 @@ export class MiAbuseReportNotificationRecipient {
/** /**
* . * .
*/ */
@ManyToOne(type => MiUserProfile, {}) @ManyToOne(type => MiUserProfile, {
onDelete: 'CASCADE',
})
@JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' }) @JoinColumn({ name: 'userId', referencedColumnName: 'userId', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_userId2' })
public userProfile: MiUserProfile | null; public userProfile: MiUserProfile | null;
/** /**
* WebhookId. * WebhookId.
*/ */
@Index() @Index('IDX_abuse_report_notification_recipient_systemWebhookId')
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
default: null,
}) })
public systemWebhookId: string | null; public systemWebhookId: string | null;
@ -95,6 +99,6 @@ export class MiAbuseReportNotificationRecipient {
@ManyToOne(type => MiSystemWebhook, { @ManyToOne(type => MiSystemWebhook, {
onDelete: 'CASCADE', onDelete: 'CASCADE',
}) })
@JoinColumn() @JoinColumn({ name: 'systemWebhookId', referencedColumnName: 'id', foreignKeyConstraintName: 'FK_abuse_report_notification_recipient_systemWebhookId' })
public systemWebhook: MiSystemWebhook | null; public systemWebhook: MiSystemWebhook | null;
} }

View File

@ -8,6 +8,7 @@ import { id } from './util/id.js';
@Entity('emoji') @Entity('emoji')
@Index(['name', 'host'], { unique: true }) @Index(['name', 'host'], { unique: true })
@Index('IDX_EMOJI_ROLE_IDS', { synchronize: false }) // GIN for roleIdsThatCanBeUsedThisEmojiAsReaction in production
export class MiEmoji { export class MiEmoji {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@ -32,6 +33,7 @@ export class MiEmoji {
@Column('varchar', { @Column('varchar', {
length: 128, nullable: true, length: 128, nullable: true,
}) })
@Index('IDX_EMOJI_CATEGORY')
public category: string | null; public category: string | null;
@Column('varchar', { @Column('varchar', {

View File

@ -59,7 +59,7 @@ export class MiMeta {
public maintainerEmail: string | null; public maintainerEmail: string | null;
@Column('boolean', { @Column('boolean', {
default: false, default: true,
}) })
public disableRegistration: boolean; public disableRegistration: boolean;
@ -570,7 +570,7 @@ export class MiMeta {
public bannedEmailDomains: string[]; public bannedEmailDomains: string[];
@Column('varchar', { @Column('varchar', {
length: 1024, array: true, default: '{ "admin", "administrator", "root", "system", "maintainer", "host", "mod", "moderator", "owner", "superuser", "staff", "auth", "i", "me", "everyone", "all", "mention", "mentions", "example", "user", "users", "account", "accounts", "official", "help", "helps", "support", "supports", "info", "information", "informations", "announce", "announces", "announcement", "announcements", "notice", "notification", "notifications", "dev", "developer", "developers", "tech", "misskey" }', length: 1024, array: true, default: ['admin', 'administrator', 'root', 'system', 'maintainer', 'host', 'mod', 'moderator', 'owner', 'superuser', 'staff', 'auth', 'i', 'me', 'everyone', 'all', 'mention', 'mentions', 'example', 'user', 'users', 'account', 'accounts', 'official', 'help', 'helps', 'support', 'supports', 'info', 'information', 'informations', 'announce', 'announces', 'announcement', 'announcements', 'notice', 'notification', 'notifications', 'dev', 'developer', 'developers', 'tech', 'misskey'],
}) })
public preservedUsernames: string[]; public preservedUsernames: string[];
@ -635,7 +635,7 @@ export class MiMeta {
public urlPreviewMaximumContentLength: number; public urlPreviewMaximumContentLength: number;
@Column('boolean', { @Column('boolean', {
default: true, default: false,
}) })
public urlPreviewRequireContentLength: boolean; public urlPreviewRequireContentLength: boolean;
@ -648,12 +648,13 @@ export class MiMeta {
@Column('varchar', { @Column('varchar', {
length: 1024, length: 1024,
nullable: true, nullable: true,
default: null,
}) })
public urlPreviewUserAgent: string | null; public urlPreviewUserAgent: string | null;
@Column('varchar', { @Column('varchar', {
length: 128, length: 128,
default: 'all', default: 'none',
}) })
public federation: 'all' | 'specified' | 'none'; public federation: 'all' | 'specified' | 'none';
@ -700,6 +701,21 @@ export class MiMeta {
default: true, default: true,
}) })
public allowExternalApRedirect: boolean; public allowExternalApRedirect: boolean;
@Column('boolean', {
default: false,
})
public enableRemoteNotesCleaning: boolean;
@Column('integer', {
default: 60, // minutes
})
public remoteNotesCleaningMaxProcessingDurationInMinutes: number;
@Column('integer', {
default: 90, // days
})
public remoteNotesCleaningExpiryDaysForEachNotes: number;
} }
export type SoftwareSuspension = { export type SoftwareSuspension = {

View File

@ -20,7 +20,8 @@ import type { MiDriveFile } from './DriveFile.js';
// You should not use `@Index({ concurrent: true })` decorator because database initialization for test will fail // You should not use `@Index({ concurrent: true })` decorator because database initialization for test will fail
// because it will always run CREATE INDEX in transaction based on decorators. // because it will always run CREATE INDEX in transaction based on decorators.
// Not appending `{ concurrent: true }` to `@Index` will not cause any problem in production, // Not appending `{ concurrent: true }` to `@Index` will not cause any problem in production,
@Index(['userId', 'id'])
@Index(['userId', 'id']) // Note: this index is ("userId", "id" DESC) in production, but not in test.
@Entity('note') @Entity('note')
export class MiNote { export class MiNote {
@PrimaryColumn(id()) @PrimaryColumn(id())
@ -35,7 +36,7 @@ export class MiNote {
public replyId: MiNote['id'] | null; public replyId: MiNote['id'] | null;
@ManyToOne(type => MiNote, { @ManyToOne(type => MiNote, {
onDelete: 'CASCADE', createForeignKeyConstraints: false,
}) })
@JoinColumn() @JoinColumn()
public reply: MiNote | null; public reply: MiNote | null;
@ -49,7 +50,7 @@ export class MiNote {
public renoteId: MiNote['id'] | null; public renoteId: MiNote['id'] | null;
@ManyToOne(type => MiNote, { @ManyToOne(type => MiNote, {
onDelete: 'CASCADE', createForeignKeyConstraints: false,
}) })
@JoinColumn() @JoinColumn()
public renote: MiNote | null; public renote: MiNote | null;

View File

@ -12,11 +12,13 @@ import { MiNote } from './Note.js';
import type { MiDriveFile } from './DriveFile.js'; import type { MiDriveFile } from './DriveFile.js';
@Entity('note_draft') @Entity('note_draft')
@Index('IDX_NOTE_DRAFT_FILE_IDS', { synchronize: false }) // GIN for fileIds in production
@Index('IDX_NOTE_DRAFT_VISIBLE_USER_IDS', { synchronize: false }) // GIN for visibleUserIds in production
export class MiNoteDraft { export class MiNoteDraft {
@PrimaryColumn(id()) @PrimaryColumn(id())
public id: string; public id: string;
@Index() @Index('IDX_NOTE_DRAFT_REPLY_ID')
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
@ -24,13 +26,14 @@ export class MiNoteDraft {
}) })
public replyId: MiNote['id'] | null; public replyId: MiNote['id'] | null;
// There is a possibility that replyId is not null but reply is null when the reply note is deleted.
@ManyToOne(type => MiNote, { @ManyToOne(type => MiNote, {
onDelete: 'CASCADE', createForeignKeyConstraints: false,
}) })
@JoinColumn() @JoinColumn()
public reply: MiNote | null; public reply: MiNote | null;
@Index() @Index('IDX_NOTE_DRAFT_RENOTE_ID')
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
@ -38,8 +41,9 @@ export class MiNoteDraft {
}) })
public renoteId: MiNote['id'] | null; public renoteId: MiNote['id'] | null;
// There is a possibility that renoteId is not null but renote is null when the renote note is deleted.
@ManyToOne(type => MiNote, { @ManyToOne(type => MiNote, {
onDelete: 'CASCADE', createForeignKeyConstraints: false,
}) })
@JoinColumn() @JoinColumn()
public renote: MiNote | null; public renote: MiNote | null;
@ -55,7 +59,7 @@ export class MiNoteDraft {
}) })
public cw: string | null; public cw: string | null;
@Index() @Index('IDX_NOTE_DRAFT_USER_ID')
@Column({ @Column({
...id(), ...id(),
comment: 'The ID of author.', comment: 'The ID of author.',
@ -106,7 +110,7 @@ export class MiNoteDraft {
}) })
public hashtag: string | null; public hashtag: string | null;
@Index() @Index('IDX_NOTE_DRAFT_CHANNEL_ID')
@Column({ @Column({
...id(), ...id(),
nullable: true, nullable: true,
@ -114,8 +118,10 @@ export class MiNoteDraft {
}) })
public channelId: MiChannel['id'] | null; public channelId: MiChannel['id'] | null;
// There is a possibility that channelId is not null but channel is null when the channel is deleted.
// (deleting channel is not implemented so it's not happening now but may happen in the future)
@ManyToOne(type => MiChannel, { @ManyToOne(type => MiChannel, {
onDelete: 'CASCADE', createForeignKeyConstraints: false,
}) })
@JoinColumn() @JoinColumn()
public channel: MiChannel | null; public channel: MiChannel | null;

View File

@ -29,7 +29,7 @@ export class MiUserProfile {
}) })
public location: string | null; public location: string | null;
@Index() // Note: There's index named IDX_de22cd2b445eee31ae51cdbe99 for SUBSTR("birthday", 6, 5)
@Column('char', { @Column('char', {
length: 10, nullable: true, length: 10, nullable: true,
comment: 'The birthday (YYYY-MM-DD) of the User.', comment: 'The birthday (YYYY-MM-DD) of the User.',

View File

@ -51,11 +51,13 @@ export const packedNoteDraftSchema = {
type: 'object', type: 'object',
optional: true, nullable: true, optional: true, nullable: true,
ref: 'Note', ref: 'Note',
description: 'The reply target note contents if exists. If the reply target has been deleted since the draft was created, this will be null while replyId is not null.',
}, },
renote: { renote: {
type: 'object', type: 'object',
optional: true, nullable: true, optional: true, nullable: true,
ref: 'Note', ref: 'Note',
description: 'The renote target note contents if exists. If the renote target has been deleted since the draft was created, this will be null while renoteId is not null.',
}, },
visibility: { visibility: {
type: 'string', type: 'string',

View File

@ -313,6 +313,10 @@ export const packedRolePoliciesSchema = {
type: 'integer', type: 'integer',
optional: false, nullable: false, optional: false, nullable: false,
}, },
watermarkAvailable: {
type: 'boolean',
optional: false, nullable: false,
},
}, },
} as const; } as const;

View File

@ -6,7 +6,6 @@
import { Module } from '@nestjs/common'; import { Module } from '@nestjs/common';
import { CoreModule } from '@/core/CoreModule.js'; import { CoreModule } from '@/core/CoreModule.js';
import { GlobalModule } from '@/GlobalModule.js'; import { GlobalModule } from '@/GlobalModule.js';
import { CheckModeratorsActivityProcessorService } from '@/queue/processors/CheckModeratorsActivityProcessorService.js';
import { QueueLoggerService } from './QueueLoggerService.js'; import { QueueLoggerService } from './QueueLoggerService.js';
import { QueueProcessorService } from './QueueProcessorService.js'; import { QueueProcessorService } from './QueueProcessorService.js';
import { DeliverProcessorService } from './processors/DeliverProcessorService.js'; import { DeliverProcessorService } from './processors/DeliverProcessorService.js';
@ -18,6 +17,8 @@ import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMu
import { BakeBufferedReactionsProcessorService } from './processors/BakeBufferedReactionsProcessorService.js'; import { BakeBufferedReactionsProcessorService } from './processors/BakeBufferedReactionsProcessorService.js';
import { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js'; import { CleanChartsProcessorService } from './processors/CleanChartsProcessorService.js';
import { CleanProcessorService } from './processors/CleanProcessorService.js'; import { CleanProcessorService } from './processors/CleanProcessorService.js';
import { CheckModeratorsActivityProcessorService } from './processors/CheckModeratorsActivityProcessorService.js';
import { CleanRemoteNotesProcessorService } from './processors/CleanRemoteNotesProcessorService.js';
import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js'; import { CleanRemoteFilesProcessorService } from './processors/CleanRemoteFilesProcessorService.js';
import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js'; import { DeleteAccountProcessorService } from './processors/DeleteAccountProcessorService.js';
import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js'; import { DeleteDriveFilesProcessorService } from './processors/DeleteDriveFilesProcessorService.js';
@ -83,6 +84,7 @@ import { RelationshipProcessorService } from './processors/RelationshipProcessor
AggregateRetentionProcessorService, AggregateRetentionProcessorService,
CheckExpiredMutingsProcessorService, CheckExpiredMutingsProcessorService,
CheckModeratorsActivityProcessorService, CheckModeratorsActivityProcessorService,
CleanRemoteNotesProcessorService,
QueueProcessorService, QueueProcessorService,
], ],
exports: [ exports: [

View File

@ -43,6 +43,7 @@ import { CheckExpiredMutingsProcessorService } from './processors/CheckExpiredMu
import { BakeBufferedReactionsProcessorService } from './processors/BakeBufferedReactionsProcessorService.js'; import { BakeBufferedReactionsProcessorService } from './processors/BakeBufferedReactionsProcessorService.js';
import { CleanProcessorService } from './processors/CleanProcessorService.js'; import { CleanProcessorService } from './processors/CleanProcessorService.js';
import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js'; import { AggregateRetentionProcessorService } from './processors/AggregateRetentionProcessorService.js';
import { CleanRemoteNotesProcessorService } from './processors/CleanRemoteNotesProcessorService.js';
import { QueueLoggerService } from './QueueLoggerService.js'; import { QueueLoggerService } from './QueueLoggerService.js';
import { QUEUE, baseWorkerOptions } from './const.js'; import { QUEUE, baseWorkerOptions } from './const.js';
@ -123,6 +124,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
private bakeBufferedReactionsProcessorService: BakeBufferedReactionsProcessorService, private bakeBufferedReactionsProcessorService: BakeBufferedReactionsProcessorService,
private checkModeratorsActivityProcessorService: CheckModeratorsActivityProcessorService, private checkModeratorsActivityProcessorService: CheckModeratorsActivityProcessorService,
private cleanProcessorService: CleanProcessorService, private cleanProcessorService: CleanProcessorService,
private cleanRemoteNotesProcessorService: CleanRemoteNotesProcessorService,
) { ) {
this.logger = this.queueLoggerService.logger; this.logger = this.queueLoggerService.logger;
@ -164,6 +166,7 @@ export class QueueProcessorService implements OnApplicationShutdown {
case 'bakeBufferedReactions': return this.bakeBufferedReactionsProcessorService.process(); case 'bakeBufferedReactions': return this.bakeBufferedReactionsProcessorService.process();
case 'checkModeratorsActivity': return this.checkModeratorsActivityProcessorService.process(); case 'checkModeratorsActivity': return this.checkModeratorsActivityProcessorService.process();
case 'clean': return this.cleanProcessorService.process(); case 'clean': return this.cleanProcessorService.process();
case 'cleanRemoteNotes': return this.cleanRemoteNotesProcessorService.process(job);
default: throw new Error(`unrecognized job type ${job.name} for system`); default: throw new Error(`unrecognized job type ${job.name} for system`);
} }
}; };

View File

@ -0,0 +1,174 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { setTimeout } from 'node:timers/promises';
import { Inject, Injectable } from '@nestjs/common';
import { And, In, IsNull, LessThan, MoreThan, Not } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { MiMeta, MiNote, NoteFavoritesRepository, NotesRepository, UserNotePiningsRepository } from '@/models/_.js';
import type Logger from '@/logger.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
import { QueueLoggerService } from '../QueueLoggerService.js';
import type * as Bull from 'bullmq';
@Injectable()
export class CleanRemoteNotesProcessorService {
private logger: Logger;
constructor(
@Inject(DI.meta)
private meta: MiMeta,
@Inject(DI.notesRepository)
private notesRepository: NotesRepository,
@Inject(DI.noteFavoritesRepository)
private noteFavoritesRepository: NoteFavoritesRepository,
@Inject(DI.userNotePiningsRepository)
private userNotePiningsRepository: UserNotePiningsRepository,
private idService: IdService,
private queueLoggerService: QueueLoggerService,
) {
this.logger = this.queueLoggerService.logger.createSubLogger('clean-remote-notes');
}
@bindThis
public async process(job: Bull.Job<Record<string, unknown>>): Promise<{
deletedCount: number;
oldest: number | null;
newest: number | null;
skipped?: boolean;
}> {
if (!this.meta.enableRemoteNotesCleaning) {
this.logger.info('Remote notes cleaning is disabled, skipping...');
return {
deletedCount: 0,
oldest: null,
newest: null,
skipped: true,
};
}
this.logger.info('cleaning remote notes...');
const maxDuration = this.meta.remoteNotesCleaningMaxProcessingDurationInMinutes * 60 * 1000; // Convert minutes to milliseconds
const startAt = Date.now();
const MAX_NOTE_COUNT_PER_QUERY = 50;
const stats = {
deletedCount: 0,
oldest: null as number | null,
newest: null as number | null,
};
let cursor: MiNote['id'] = this.idService.gen(Date.now() - (1000 * 60 * 60 * 24 * this.meta.remoteNotesCleaningExpiryDaysForEachNotes));
while (true) {
const batchBeginAt = Date.now();
let notes: Pick<MiNote, 'id'>[] = await this.notesRepository.find({
where: {
id: LessThan(cursor),
userHost: Not(IsNull()),
clippedCount: 0,
renoteCount: 0,
},
take: MAX_NOTE_COUNT_PER_QUERY,
order: {
// 新しい順
// https://github.com/misskey-dev/misskey/pull/16292#issuecomment-3139376314
id: -1,
},
select: ['id'],
});
const fetchedCount = notes.length;
for (const note of notes) {
if (note.id < cursor) {
cursor = note.id;
}
}
const pinings = notes.length === 0 ? [] : await this.userNotePiningsRepository.find({
where: {
noteId: In(notes.map(note => note.id)),
},
select: ['noteId'],
});
notes = notes.filter(note => {
return !pinings.some(pining => pining.noteId === note.id);
});
const favorites = notes.length === 0 ? [] : await this.noteFavoritesRepository.find({
where: {
noteId: In(notes.map(note => note.id)),
},
select: ['noteId'],
});
notes = notes.filter(note => {
return !favorites.some(favorite => favorite.noteId === note.id);
});
const replies = notes.length === 0 ? [] : await this.notesRepository.find({
where: {
replyId: In(notes.map(note => note.id)),
userHost: IsNull(),
},
select: ['replyId'],
});
notes = notes.filter(note => {
return !replies.some(reply => reply.replyId === note.id);
});
if (notes.length > 0) {
await this.notesRepository.delete(notes.map(note => note.id));
for (const note of notes) {
const t = this.idService.parse(note.id).date.getTime();
if (stats.oldest === null || t < stats.oldest) {
stats.oldest = t;
}
if (stats.newest === null || t > stats.newest) {
stats.newest = t;
}
}
stats.deletedCount += notes.length;
}
job.log(`Deleted ${notes.length} of ${fetchedCount}; ${Date.now() - batchBeginAt}ms`);
const elapsed = Date.now() - startAt;
if (elapsed >= maxDuration) {
this.logger.info(`Reached maximum duration of ${maxDuration}ms, stopping...`);
job.log('Reached maximum duration, stopping cleaning.');
job.updateProgress(100);
break;
}
job.updateProgress((elapsed / maxDuration) * 100);
await setTimeout(1000 * 5); // Wait a moment to avoid overwhelming the db
}
this.logger.succ('cleaning of remote notes completed.');
return {
deletedCount: stats.deletedCount,
oldest: stats.oldest,
newest: stats.newest,
skipped: false,
};
}
}

View File

@ -40,8 +40,8 @@ export class GetterService {
} }
@bindThis @bindThis
public async getNoteWithUser(noteId: MiNote['id']) { public async getNoteWithRelations(noteId: MiNote['id']) {
const note = await this.notesRepository.findOne({ where: { id: noteId }, relations: ['user'] }); const note = await this.notesRepository.findOne({ where: { id: noteId }, relations: ['user', 'reply', 'renote', 'reply.user', 'renote.user'] });
if (note == null) { if (note == null) {
throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.'); throw new IdentifiableError('9725d0ce-ba28-4dde-95a7-2cbb2c15de24', 'No such note.');

View File

@ -129,7 +129,8 @@ export class SignupApiService {
let ticket: MiRegistrationTicket | null = null; let ticket: MiRegistrationTicket | null = null;
if (this.meta.disableRegistration) { // テスト時はこの機構は障害となるため無効にする
if (process.env.NODE_ENV !== 'test' && this.meta.disableRegistration) {
if (invitationCode == null || typeof invitationCode !== 'string') { if (invitationCode == null || typeof invitationCode !== 'string') {
reply.code(400); reply.code(400);
return; return;

View File

@ -70,6 +70,7 @@ export * as 'admin/queue/inbox-delayed' from './endpoints/admin/queue/inbox-dela
export * as 'admin/queue/retry-job' from './endpoints/admin/queue/retry-job.js'; export * as 'admin/queue/retry-job' from './endpoints/admin/queue/retry-job.js';
export * as 'admin/queue/remove-job' from './endpoints/admin/queue/remove-job.js'; export * as 'admin/queue/remove-job' from './endpoints/admin/queue/remove-job.js';
export * as 'admin/queue/show-job' from './endpoints/admin/queue/show-job.js'; export * as 'admin/queue/show-job' from './endpoints/admin/queue/show-job.js';
export * as 'admin/queue/show-job-logs' from './endpoints/admin/queue/show-job-logs.js';
export * as 'admin/queue/promote-jobs' from './endpoints/admin/queue/promote-jobs.js'; export * as 'admin/queue/promote-jobs' from './endpoints/admin/queue/promote-jobs.js';
export * as 'admin/queue/jobs' from './endpoints/admin/queue/jobs.js'; export * as 'admin/queue/jobs' from './endpoints/admin/queue/jobs.js';
export * as 'admin/queue/stats' from './endpoints/admin/queue/stats.js'; export * as 'admin/queue/stats' from './endpoints/admin/queue/stats.js';

View File

@ -571,6 +571,18 @@ export const meta = {
type: 'boolean', type: 'boolean',
optional: false, nullable: false, optional: false, nullable: false,
}, },
enableRemoteNotesCleaning: {
type: 'boolean',
optional: false, nullable: false,
},
remoteNotesCleaningExpiryDaysForEachNotes: {
type: 'number',
optional: false, nullable: false,
},
remoteNotesCleaningMaxProcessingDurationInMinutes: {
type: 'number',
optional: false, nullable: false,
},
}, },
}, },
} as const; } as const;
@ -722,6 +734,9 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
proxyRemoteFiles: instance.proxyRemoteFiles, proxyRemoteFiles: instance.proxyRemoteFiles,
signToActivityPubGet: instance.signToActivityPubGet, signToActivityPubGet: instance.signToActivityPubGet,
allowExternalApRedirect: instance.allowExternalApRedirect, allowExternalApRedirect: instance.allowExternalApRedirect,
enableRemoteNotesCleaning: instance.enableRemoteNotesCleaning,
remoteNotesCleaningExpiryDaysForEachNotes: instance.remoteNotesCleaningExpiryDaysForEachNotes,
remoteNotesCleaningMaxProcessingDurationInMinutes: instance.remoteNotesCleaningMaxProcessingDurationInMinutes,
}; };
}); });
} }

View File

@ -0,0 +1,45 @@
/*
* SPDX-FileCopyrightText: syuilo and misskey-project
* SPDX-License-Identifier: AGPL-3.0-only
*/
import { Injectable } from '@nestjs/common';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QUEUE_TYPES, QueueService } from '@/core/QueueService.js';
export const meta = {
tags: ['admin'],
requireCredential: true,
requireModerator: true,
kind: 'read:admin:queue',
res: {
type: 'array',
optional: false, nullable: false,
items: {
optional: false, nullable: false,
type: 'string',
},
},
} as const;
export const paramDef = {
type: 'object',
properties: {
queue: { type: 'string', enum: QUEUE_TYPES },
jobId: { type: 'string' },
},
required: ['queue', 'jobId'],
} as const;
@Injectable()
export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
constructor(
private queueService: QueueService,
) {
super(meta, paramDef, async (ps, me) => {
return this.queueService.queueGetJobLogs(ps.queue, ps.jobId);
});
}
}

View File

@ -205,6 +205,9 @@ export const paramDef = {
proxyRemoteFiles: { type: 'boolean' }, proxyRemoteFiles: { type: 'boolean' },
signToActivityPubGet: { type: 'boolean' }, signToActivityPubGet: { type: 'boolean' },
allowExternalApRedirect: { type: 'boolean' }, allowExternalApRedirect: { type: 'boolean' },
enableRemoteNotesCleaning: { type: 'boolean' },
remoteNotesCleaningExpiryDaysForEachNotes: { type: 'number' },
remoteNotesCleaningMaxProcessingDurationInMinutes: { type: 'number' },
}, },
required: [], required: [],
} as const; } as const;
@ -723,6 +726,18 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.allowExternalApRedirect = ps.allowExternalApRedirect; set.allowExternalApRedirect = ps.allowExternalApRedirect;
} }
if (ps.enableRemoteNotesCleaning !== undefined) {
set.enableRemoteNotesCleaning = ps.enableRemoteNotesCleaning;
}
if (ps.remoteNotesCleaningExpiryDaysForEachNotes !== undefined) {
set.remoteNotesCleaningExpiryDaysForEachNotes = ps.remoteNotesCleaningExpiryDaysForEachNotes;
}
if (ps.remoteNotesCleaningMaxProcessingDurationInMinutes !== undefined) {
set.remoteNotesCleaningMaxProcessingDurationInMinutes = ps.remoteNotesCleaningMaxProcessingDurationInMinutes;
}
const before = await this.metaService.fetch(true); const before = await this.metaService.fetch(true);
await this.metaService.update(set); await this.metaService.update(set);

View File

@ -269,7 +269,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
let renote: MiNote | null = null; let renote: MiNote | null = null;
if (ps.renoteId != null) { if (ps.renoteId != null) {
// Fetch renote to note // Fetch renote to note
renote = await this.notesRepository.findOneBy({ id: ps.renoteId }); renote = await this.notesRepository.findOne({
where: { id: ps.renoteId },
relations: ['user', 'renote', 'reply'],
});
if (renote == null) { if (renote == null) {
throw new ApiError(meta.errors.noSuchRenoteTarget); throw new ApiError(meta.errors.noSuchRenoteTarget);
@ -315,7 +318,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
let reply: MiNote | null = null; let reply: MiNote | null = null;
if (ps.replyId != null) { if (ps.replyId != null) {
// Fetch reply // Fetch reply
reply = await this.notesRepository.findOneBy({ id: ps.replyId }); reply = await this.notesRepository.findOne({
where: { id: ps.replyId },
relations: ['user'],
});
if (reply == null) { if (reply == null) {
throw new ApiError(meta.errors.noSuchReplyTarget); throw new ApiError(meta.errors.noSuchReplyTarget);

View File

@ -55,7 +55,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private getterService: GetterService, private getterService: GetterService,
) { ) {
super(meta, paramDef, async (ps, me) => { super(meta, paramDef, async (ps, me) => {
const note = await this.getterService.getNoteWithUser(ps.noteId).catch(err => { const note = await this.getterService.getNoteWithRelations(ps.noteId).catch(err => {
if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote); if (err.id === '9725d0ce-ba28-4dde-95a7-2cbb2c15de24') throw new ApiError(meta.errors.noSuchNote);
throw err; throw err;
}); });

View File

@ -237,7 +237,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
} }
if (ps.withRenotes === false) { if (ps.withRenotes === false) {
query.andWhere('note.renoteId IS NULL'); query.andWhere(new Brackets(qb => {
qb.orWhere('note.renoteId IS NULL');
qb.orWhere(new Brackets(qb => {
qb.orWhere('note.text IS NOT NULL');
qb.orWhere('note.fileIds != \'{}\'');
}));
}));
} }
//#endregion //#endregion

View File

@ -188,6 +188,10 @@ export class ClientServerService {
'url': 'url', 'url': 'url',
}, },
}, },
'shortcuts': [{
'name': 'Safemode',
'url': '/?safemode=true',
}],
}; };
manifest = { manifest = {
@ -580,7 +584,7 @@ export class ClientServerService {
id: request.params.note, id: request.params.note,
visibility: In(['public', 'home']), visibility: In(['public', 'home']),
}, },
relations: ['user'], relations: ['user', 'reply', 'renote'],
}); });
if ( if (
@ -821,8 +825,11 @@ export class ClientServerService {
fastify.get<{ Params: { note: string; } }>('/embed/notes/:note', async (request, reply) => { fastify.get<{ Params: { note: string; } }>('/embed/notes/:note', async (request, reply) => {
reply.removeHeader('X-Frame-Options'); reply.removeHeader('X-Frame-Options');
const note = await this.notesRepository.findOneBy({ const note = await this.notesRepository.findOne({
id: request.params.note, where: {
id: request.params.note,
},
relations: ['user', 'reply', 'renote'],
}); });
if (note == null) return; if (note == null) return;

View File

@ -94,23 +94,37 @@
} }
//#endregion //#endregion
//#region Theme let isSafeMode = (localStorage.getItem('isSafeMode') === 'true');
const theme = localStorage.getItem('theme');
if (theme) {
for (const [k, v] of Object.entries(JSON.parse(theme))) {
document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
// HTMLの theme-color 適用 if (!isSafeMode) {
if (k === 'htmlThemeColor') { const urlParams = new URLSearchParams(window.location.search);
for (const tag of document.head.children) {
if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') { if (urlParams.has('safemode') && urlParams.get('safemode') === 'true') {
tag.setAttribute('content', v); localStorage.setItem('isSafeMode', 'true');
break; isSafeMode = true;
}
}
//#region Theme
if (!isSafeMode) {
const theme = localStorage.getItem('theme');
if (theme) {
for (const [k, v] of Object.entries(JSON.parse(theme))) {
document.documentElement.style.setProperty(`--MI_THEME-${k}`, v.toString());
// HTMLの theme-color 適用
if (k === 'htmlThemeColor') {
for (const tag of document.head.children) {
if (tag.tagName === 'META' && tag.getAttribute('name') === 'theme-color') {
tag.setAttribute('content', v);
break;
}
} }
} }
} }
} }
} }
const colorScheme = localStorage.getItem('colorScheme'); const colorScheme = localStorage.getItem('colorScheme');
if (colorScheme) { if (colorScheme) {
document.documentElement.style.setProperty('color-scheme', colorScheme); document.documentElement.style.setProperty('color-scheme', colorScheme);
@ -127,11 +141,13 @@
document.documentElement.classList.add('useSystemFont'); document.documentElement.classList.add('useSystemFont');
} }
const customCss = localStorage.getItem('customCss'); if (!isSafeMode) {
if (customCss && customCss.length > 0) { const customCss = localStorage.getItem('customCss');
const style = document.createElement('style'); if (customCss && customCss.length > 0) {
style.innerHTML = customCss; const style = document.createElement('style');
document.head.appendChild(style); style.innerHTML = customCss;
document.head.appendChild(style);
}
} }
async function addStyle(styleText) { async function addStyle(styleText) {
@ -159,9 +175,13 @@
otherOption1: 'Clear preferences and cache', otherOption1: 'Clear preferences and cache',
otherOption2: 'Start the simple client', otherOption2: 'Start the simple client',
otherOption3: 'Start the repair tool', otherOption3: 'Start the repair tool',
otherOption4: 'Start Misskey in safe mode',
}, locale?._bootErrors || {}); }, locale?._bootErrors || {});
const reload = locale?.reload || 'Reload'; const reload = locale?.reload || 'Reload';
const safeModeUrl = new URL(window.location.href);
safeModeUrl.searchParams.set('safemode', 'true');
let errorsElement = document.getElementById('errors'); let errorsElement = document.getElementById('errors');
if (!errorsElement) { if (!errorsElement) {
@ -182,6 +202,12 @@
<p>${messages.solution4}</p> <p>${messages.solution4}</p>
<details style="color: #86b300;"> <details style="color: #86b300;">
<summary>${messages.otherOption}</summary> <summary>${messages.otherOption}</summary>
<a href="${safeModeUrl}">
<button class="button-small">
<span class="button-label-small">${messages.otherOption4}</span>
</button>
</a>
<br>
<a href="/flush"> <a href="/flush">
<button class="button-small"> <button class="button-small">
<span class="button-label-small">${messages.otherOption1}</span> <span class="button-label-small">${messages.otherOption1}</span>

View File

@ -34,5 +34,11 @@
"text": "text", "text": "text",
"url": "url" "url": "url"
} }
} },
"shortcuts": [
{
"name": "Safemode",
"url": "/?safemode=true"
}
]
} }

View File

@ -74,6 +74,10 @@ services:
source: ../../../pnpm-workspace.yaml source: ../../../pnpm-workspace.yaml
target: /misskey/pnpm-workspace.yaml target: /misskey/pnpm-workspace.yaml
read_only: true read_only: true
- type: bind
source: ../../../patches
target: /misskey/patches
read_only: true
- type: bind - type: bind
source: ./certificates/rootCA.crt source: ./certificates/rootCA.crt
target: /usr/local/share/ca-certificates/rootCA.crt target: /usr/local/share/ca-certificates/rootCA.crt

View File

@ -74,6 +74,10 @@ services:
source: ../../../pnpm-workspace.yaml source: ../../../pnpm-workspace.yaml
target: /misskey/pnpm-workspace.yaml target: /misskey/pnpm-workspace.yaml
read_only: true read_only: true
- type: bind
source: ../../../patches
target: /misskey/patches
read_only: true
- type: bind - type: bind
source: ./certificates/rootCA.crt source: ./certificates/rootCA.crt
target: /usr/local/share/ca-certificates/rootCA.crt target: /usr/local/share/ca-certificates/rootCA.crt
@ -118,6 +122,10 @@ services:
source: ../../../pnpm-workspace.yaml source: ../../../pnpm-workspace.yaml
target: /misskey/pnpm-workspace.yaml target: /misskey/pnpm-workspace.yaml
read_only: true read_only: true
- type: bind
source: ../../../patches
target: /misskey/patches
read_only: true
working_dir: /misskey working_dir: /misskey
command: > command: >
bash -c " bash -c "

View File

@ -63,7 +63,6 @@ describe('Note', () => {
deepStrictEqualWithExcludedFields(note, resolvedNote, [ deepStrictEqualWithExcludedFields(note, resolvedNote, [
'id', 'id',
'emojis', 'emojis',
'reactionAcceptance',
'replyId', 'replyId',
'reply', 'reply',
'userId', 'userId',
@ -105,7 +104,6 @@ describe('Note', () => {
deepStrictEqualWithExcludedFields(note, resolvedNote, [ deepStrictEqualWithExcludedFields(note, resolvedNote, [
'id', 'id',
'emojis', 'emojis',
'reactionAcceptance',
'renoteId', 'renoteId',
'renote', 'renote',
'userId', 'userId',

View File

@ -79,6 +79,9 @@ async function createAdmin(host: Host): Promise<Misskey.entities.SignupResponse
rateLimitFactor: 0 as never, rateLimitFactor: 0 as never,
}, },
}, res.token); }, res.token);
await client.request('admin/update-meta', {
federation: 'all',
}, res.token);
return res; return res;
}).catch(err => { }).catch(err => {
if (err.info.e.message === 'access denied') return undefined; if (err.info.e.message === 'access denied') return undefined;

View File

@ -673,7 +673,6 @@ describe('アンテナ', () => {
assert.deepStrictEqual(response, expected); assert.deepStrictEqual(response, expected);
}); });
test.skip('が取得でき、日付指定のPaginationに一貫性があること', async () => { }); test.skip('が取得でき、日付指定のPaginationに一貫性があること', async () => { });
test.each([ test.each([
{ label: 'ID指定', offsetBy: 'id' }, { label: 'ID指定', offsetBy: 'id' },

View File

@ -24,6 +24,7 @@ describe('Endpoints', () => {
bob = await signup({ username: 'bob' }); bob = await signup({ username: 'bob' });
carol = await signup({ username: 'carol' }); carol = await signup({ username: 'carol' });
dave = await signup({ username: 'dave' }); dave = await signup({ username: 'dave' });
await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
}, 1000 * 60 * 2); }, 1000 * 60 * 2);
describe('signup', () => { describe('signup', () => {

View File

@ -6,7 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js'; import { api, channel, clip, galleryPost, page, play, post, signup, simpleGet, uploadFile } from '../utils.js';
import type { SimpleGetResponse } from '../utils.js'; import type { SimpleGetResponse } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
@ -78,6 +78,7 @@ describe('Webリソース', () => {
beforeAll(async () => { beforeAll(async () => {
alice = await signup({ username: 'alice' }); alice = await signup({ username: 'alice' });
await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
aliceUploadedFile = (await uploadFile(alice)).body; aliceUploadedFile = (await uploadFile(alice)).body;
alicesPost = await post(alice, { alicesPost = await post(alice, {
text: 'test', text: 'test',

View File

@ -16,6 +16,7 @@ describe('FF visibility', () => {
beforeAll(async () => { beforeAll(async () => {
alice = await signup({ username: 'alice' }); alice = await signup({ username: 'alice' });
bob = await signup({ username: 'bob' }); bob = await signup({ username: 'bob' });
await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
}, 1000 * 60 * 2); }, 1000 * 60 * 2);
test('followingVisibility, followersVisibility がともに public なユーザーのフォロー/フォロワーを誰でも見れる', async () => { test('followingVisibility, followersVisibility がともに public なユーザーのフォロー/フォロワーを誰でも見れる', async () => {

File diff suppressed because it is too large Load Diff

View File

@ -6,7 +6,7 @@
process.env.NODE_ENV = 'test'; process.env.NODE_ENV = 'test';
import * as assert from 'assert'; import * as assert from 'assert';
import { host, origin, relativeFetch, signup } from '../utils.js'; import { api, host, origin, relativeFetch, signup } from '../utils.js';
import type * as misskey from 'misskey-js'; import type * as misskey from 'misskey-js';
describe('.well-known', () => { describe('.well-known', () => {
@ -14,6 +14,7 @@ describe('.well-known', () => {
beforeAll(async () => { beforeAll(async () => {
alice = await signup({ username: 'alice' }); alice = await signup({ username: 'alice' });
await api('admin/update-meta', { federation: 'all' }, alice as misskey.entities.SignupResponse);
}, 1000 * 60 * 2); }, 1000 * 60 * 2);
test('nodeinfo', async () => { test('nodeinfo', async () => {

View File

@ -10,62 +10,62 @@
"lint": "pnpm typecheck && pnpm eslint" "lint": "pnpm typecheck && pnpm eslint"
}, },
"dependencies": { "dependencies": {
"@discordapp/twemoji": "15.1.0", "@discordapp/twemoji": "16.0.1",
"@rollup/plugin-json": "6.1.0", "@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.2", "@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.1.4", "@rollup/pluginutils": "5.2.0",
"@twemoji/parser": "15.1.1", "@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "5.2.4", "@vitejs/plugin-vue": "5.2.4",
"@vue/compiler-sfc": "3.5.16", "@vue/compiler-sfc": "3.5.17",
"astring": "1.9.0", "astring": "1.9.0",
"buraha": "0.0.1", "buraha": "0.0.1",
"estree-walker": "3.0.3", "estree-walker": "3.0.3",
"icons-subsetter": "workspace:*", "icons-subsetter": "workspace:*",
"frontend-shared": "workspace:*", "frontend-shared": "workspace:*",
"json5": "2.2.3", "json5": "2.2.3",
"mfm-js": "0.24.0", "mfm-js": "0.25.0",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"punycode.js": "2.3.1", "punycode.js": "2.3.1",
"rollup": "4.42.0", "rollup": "4.45.1",
"sass": "1.89.2", "sass": "1.89.2",
"shiki": "3.6.0", "shiki": "3.8.0",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tsc-alias": "1.8.16", "tsc-alias": "1.8.16",
"tsconfig-paths": "4.2.0", "tsconfig-paths": "4.2.0",
"typescript": "5.8.3", "typescript": "5.8.3",
"uuid": "11.1.0", "uuid": "11.1.0",
"vite": "6.3.5", "vite": "6.3.5",
"vue": "3.5.16" "vue": "3.5.17"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/summaly": "5.2.1", "@misskey-dev/summaly": "5.2.2",
"@tabler/icons-webfont": "3.34.0", "@tabler/icons-webfont": "3.34.0",
"@testing-library/vue": "8.1.0", "@testing-library/vue": "8.1.0",
"@types/estree": "1.0.8", "@types/estree": "1.0.8",
"@types/micromatch": "4.0.9", "@types/micromatch": "4.0.9",
"@types/node": "22.15.31", "@types/node": "22.16.4",
"@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.34.0", "@typescript-eslint/eslint-plugin": "8.37.0",
"@typescript-eslint/parser": "8.34.0", "@typescript-eslint/parser": "8.37.0",
"@vitest/coverage-v8": "3.2.3", "@vitest/coverage-v8": "3.2.4",
"@vue/runtime-core": "3.5.16", "@vue/runtime-core": "3.5.17",
"acorn": "8.15.0", "acorn": "8.15.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"eslint-plugin-import": "2.31.0", "eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.2.0", "eslint-plugin-vue": "10.3.0",
"fast-glob": "3.3.3", "fast-glob": "3.3.3",
"happy-dom": "17.6.3", "happy-dom": "17.6.3",
"intersection-observer": "0.12.2", "intersection-observer": "0.12.2",
"micromatch": "4.0.8", "micromatch": "4.0.8",
"msw": "2.10.2", "msw": "2.10.4",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"prettier": "3.5.3", "prettier": "3.6.2",
"start-server-and-test": "2.0.12", "start-server-and-test": "2.0.12",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vue-component-type-helpers": "2.2.10", "vue-component-type-helpers": "2.2.12",
"vue-eslint-parser": "10.1.3", "vue-eslint-parser": "10.2.0",
"vue-tsc": "2.2.10" "vue-tsc": "2.2.12"
} }
} }

View File

@ -18,5 +18,5 @@ onmessage = (event) => {
render(event.data.hash, canvas); render(event.data.hash, canvas);
const bitmap = canvas.transferToImageBitmap(); const bitmap = canvas.transferToImageBitmap();
postMessage({ id: event.data.id, bitmap }); postMessage({ id: event.data.id, bitmap }, [bitmap]);
}; };

View File

@ -23,6 +23,7 @@ export const version = _VERSION_;
export const instanceName = (siteName === 'Misskey' || siteName == null) ? host : siteName; export const instanceName = (siteName === 'Misskey' || siteName == null) ? host : siteName;
export const ui = localStorage.getItem('ui'); export const ui = localStorage.getItem('ui');
export const debug = localStorage.getItem('debug') === 'true'; export const debug = localStorage.getItem('debug') === 'true';
export const isSafeMode = localStorage.getItem('isSafeMode') === 'true';
export function updateLocale(newLocale: Locale): void { export function updateLocale(newLocale: Locale): void {
locale = newLocale; locale = newLocale;

View File

@ -112,6 +112,7 @@ export const ROLE_POLICIES = [
'chatAvailability', 'chatAvailability',
'uploadableFileTypes', 'uploadableFileTypes',
'noteDraftLimit', 'noteDraftLimit',
'watermarkAvailable',
] as const; ] as const;
export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime']; export const MFM_TAGS = ['tada', 'jelly', 'twitch', 'shake', 'spin', 'jump', 'bounce', 'flip', 'x2', 'x3', 'x4', 'scale', 'position', 'fg', 'bg', 'border', 'font', 'blur', 'rainbow', 'sparkle', 'rotate', 'ruby', 'unixtime'];

View File

@ -44,6 +44,8 @@
["😑", "expressionless", 0], ["😑", "expressionless", 0],
["😒", "unamused", 0], ["😒", "unamused", 0],
["🙄", "roll_eyes", 0], ["🙄", "roll_eyes", 0],
["🙂‍↔️", "head_shaking_horizontally", 0],
["🙂‍↕️", "head_shaking_vertically", 0],
["🤔", "thinking", 0], ["🤔", "thinking", 0],
["🤥", "lying_face", 0], ["🤥", "lying_face", 0],
["🤭", "hand_over_mouth", 0], ["🤭", "hand_over_mouth", 0],
@ -76,6 +78,7 @@
["😥", "disappointed_relieved", 0], ["😥", "disappointed_relieved", 0],
["🤤", "drooling_face", 0], ["🤤", "drooling_face", 0],
["😪", "sleepy", 0], ["😪", "sleepy", 0],
["🫩", "face_with_bags_under_eyes", 0],
["😓", "sweat", 0], ["😓", "sweat", 0],
["🥵", "hot", 0], ["🥵", "hot", 0],
["🥶", "cold", 0], ["🥶", "cold", 0],
@ -92,6 +95,7 @@
["🥴", "woozy", 0], ["🥴", "woozy", 0],
["🥱", "yawning", 0], ["🥱", "yawning", 0],
["😴", "sleeping", 0], ["😴", "sleeping", 0],
["👁️‍🗨️", "eye_in_speech_bubble", 0],
["💤", "zzz", 0], ["💤", "zzz", 0],
["😶‍🌫️", "face_in_clouds", 0], ["😶‍🌫️", "face_in_clouds", 0],
["😮‍💨", "face_exhaling", 0], ["😮‍💨", "face_exhaling", 0],
@ -185,6 +189,7 @@
["👤", "bust_in_silhouette", 1], ["👤", "bust_in_silhouette", 1],
["👥", "busts_in_silhouette", 1], ["👥", "busts_in_silhouette", 1],
["🗣", "speaking_head", 1], ["🗣", "speaking_head", 1],
["🫆", "fingerprint", 1],
["👶", "baby", 1], ["👶", "baby", 1],
["🧒", "child", 1], ["🧒", "child", 1],
["👦", "boy", 1], ["👦", "boy", 1],
@ -198,8 +203,9 @@
["🧑‍🦰", "red_hair", 1], ["🧑‍🦰", "red_hair", 1],
["👩‍🦰", "red_hair_woman", 1], ["👩‍🦰", "red_hair_woman", 1],
["👨‍🦰", "red_hair_man", 1], ["👨‍🦰", "red_hair_man", 1],
["👱", "blonde_person", 1],
["👱‍♀️", "blonde_woman", 1], ["👱‍♀️", "blonde_woman", 1],
["👱", "blonde_man", 1], ["👱‍♂️", "blonde_man", 1],
["🧑‍🦳", "white_hair", 1], ["🧑‍🦳", "white_hair", 1],
["👩‍🦳", "white_hair_woman", 1], ["👩‍🦳", "white_hair_woman", 1],
["👨‍🦳", "white_hair_man", 1], ["👨‍🦳", "white_hair_man", 1],
@ -207,21 +213,28 @@
["👩‍🦲", "bald_woman", 1], ["👩‍🦲", "bald_woman", 1],
["👨‍🦲", "bald_man", 1], ["👨‍🦲", "bald_man", 1],
["🧔", "bearded_person", 1], ["🧔", "bearded_person", 1],
["🧔‍♀️", "beard_woman", 1],
["🧔‍♂️", "beard_man", 1],
["🧓", "older_adult", 1], ["🧓", "older_adult", 1],
["👴", "older_man", 1], ["👴", "older_man", 1],
["👵", "older_woman", 1], ["👵", "older_woman", 1],
["👲", "man_with_gua_pi_mao", 1], ["👲", "man_with_gua_pi_mao", 1],
["🧕", "woman_with_headscarf", 1], ["🧕", "woman_with_headscarf", 1],
["👳", "person_with_turban", 1],
["👳‍♀️", "woman_with_turban", 1], ["👳‍♀️", "woman_with_turban", 1],
["👳", "man_with_turban", 1], ["👳‍♂️", "man_with_turban", 1],
["👮", "police_officer", 1],
["👮‍♀️", "policewoman", 1], ["👮‍♀️", "policewoman", 1],
["👮", "policeman", 1], ["👮‍♂️", "policeman", 1],
["👷", "construction_worker", 1],
["👷‍♀️", "construction_worker_woman", 1], ["👷‍♀️", "construction_worker_woman", 1],
["👷", "construction_worker_man", 1], ["👷‍♂️", "construction_worker_man", 1],
["💂", "guard", 1],
["💂‍♀️", "guardswoman", 1], ["💂‍♀️", "guardswoman", 1],
["💂", "guardsman", 1], ["💂‍♂️", "guardsman", 1],
["🕵", "detective", 1],
["🕵️‍♀️", "female_detective", 1], ["🕵️‍♀️", "female_detective", 1],
["🕵", "male_detective", 1], ["🕵️‍♂️", "male_detective", 1],
["🧑‍⚕️", "health_worker", 1], ["🧑‍⚕️", "health_worker", 1],
["👩‍⚕️", "woman_health_worker", 1], ["👩‍⚕️", "woman_health_worker", 1],
["👨‍⚕️", "man_health_worker", 1], ["👨‍⚕️", "man_health_worker", 1],
@ -270,26 +283,35 @@
["🧑‍⚖️", "judge", 1], ["🧑‍⚖️", "judge", 1],
["👩‍⚖️", "woman_judge", 1], ["👩‍⚖️", "woman_judge", 1],
["👨‍⚖️", "man_judge", 1], ["👨‍⚖️", "man_judge", 1],
["🦸", "superhero", 1],
["🦸‍♀️", "woman_superhero", 1], ["🦸‍♀️", "woman_superhero", 1],
["🦸‍♂️", "man_superhero", 1], ["🦸‍♂️", "man_superhero", 1],
["🦹", "supervillain", 1],
["🦹‍♀️", "woman_supervillain", 1], ["🦹‍♀️", "woman_supervillain", 1],
["🦹‍♂️", "man_supervillain", 1], ["🦹‍♂️", "man_supervillain", 1],
["🤶", "mrs_claus", 1], ["🤶", "mrs_claus", 1],
["🧑‍🎄", "mx_claus", 1], ["🧑‍🎄", "mx_claus", 1],
["🎅", "santa", 1], ["🎅", "santa", 1],
["🥷", "ninja", 1], ["🥷", "ninja", 1],
["🧙", "mage", 1],
["🧙‍♀️", "sorceress", 1], ["🧙‍♀️", "sorceress", 1],
["🧙‍♂️", "wizard", 1], ["🧙‍♂️", "wizard", 1],
["🧝", "elf", 1],
["🧝‍♀️", "woman_elf", 1], ["🧝‍♀️", "woman_elf", 1],
["🧝‍♂️", "man_elf", 1], ["🧝‍♂️", "man_elf", 1],
["🧛", "vampire", 1],
["🧛‍♀️", "woman_vampire", 1], ["🧛‍♀️", "woman_vampire", 1],
["🧛‍♂️", "man_vampire", 1], ["🧛‍♂️", "man_vampire", 1],
["🧟", "zombie", 1],
["🧟‍♀️", "woman_zombie", 1], ["🧟‍♀️", "woman_zombie", 1],
["🧟‍♂️", "man_zombie", 1], ["🧟‍♂️", "man_zombie", 1],
["🧞", "genie", 1],
["🧞‍♀️", "woman_genie", 1], ["🧞‍♀️", "woman_genie", 1],
["🧞‍♂️", "man_genie", 1], ["🧞‍♂️", "man_genie", 1],
["🧜", "merperson", 1],
["🧜‍♀️", "mermaid", 1], ["🧜‍♀️", "mermaid", 1],
["🧜‍♂️", "merman", 1], ["🧜‍♂️", "merman", 1],
["🧚", "fairy", 1],
["🧚‍♀️", "woman_fairy", 1], ["🧚‍♀️", "woman_fairy", 1],
["🧚‍♂️", "man_fairy", 1], ["🧚‍♂️", "man_fairy", 1],
["👼", "angel", 1], ["👼", "angel", 1],
@ -305,68 +327,108 @@
["👸", "princess", 1], ["👸", "princess", 1],
["🤴", "prince", 1], ["🤴", "prince", 1],
["👰", "person_with_veil", 1], ["👰", "person_with_veil", 1],
["👰", "bride_with_veil", 1], ["👰‍♀️", "bride_with_veil", 1],
["👰‍♂️", "man_with_veil", 1],
["🤵", "person_in_tuxedo", 1], ["🤵", "person_in_tuxedo", 1],
["🤵", "man_in_tuxedo", 1], ["🤵‍♀️", "woman_in_tuxedo", 1],
["🤵‍♂️", "man_in_tuxedo", 1],
["🏃", "running_person", 1],
["🏃‍➡️", "running_person_facing_right", 1],
["🏃‍♀️", "running_woman", 1], ["🏃‍♀️", "running_woman", 1],
["🏃", "running_man", 1], ["🏃‍♀️‍➡️", "running_woman_facing_right", 1],
["🏃‍♂️", "running_man", 1],
["🏃‍♂️‍➡️", "running_man_facing_right", 1],
["🚶", "walking_person", 1],
["🚶‍➡️", "walking_person_facing_right", 1],
["🚶‍♀️", "walking_woman", 1], ["🚶‍♀️", "walking_woman", 1],
["🚶", "walking_man", 1], ["🚶‍♀️‍➡️", "walking_woman_facing_right", 1],
["🚶‍♂️", "walking_man", 1],
["🚶‍♂️‍➡️", "walking_man_facing_right", 1],
["💃", "dancer", 1], ["💃", "dancer", 1],
["🕺", "man_dancing", 1], ["🕺", "man_dancing", 1],
["👯", "dancing_women", 1], ["👯", "dancing_people", 1],
["👯‍♀️", "dancing_women", 1],
["👯‍♂️", "dancing_men", 1], ["👯‍♂️", "dancing_men", 1],
["👫", "couple", 1], ["👫", "couple", 1],
["🧑‍🤝‍🧑", "people_holding_hands", 1], ["🧑‍🤝‍🧑", "people_holding_hands", 1],
["👬", "two_men_holding_hands", 1], ["👬", "two_men_holding_hands", 1],
["👭", "two_women_holding_hands", 1], ["👭", "two_women_holding_hands", 1],
["🫂", "people_hugging", 1], ["🫂", "people_hugging", 1],
["🙇", "bowing_person", 1],
["🙇‍♀️", "bowing_woman", 1], ["🙇‍♀️", "bowing_woman", 1],
["🙇", "bowing_man", 1], ["🙇‍♂️", "bowing_man", 1],
["🤦", "person_facepalming", 1],
["🤦‍♂️", "man_facepalming", 1], ["🤦‍♂️", "man_facepalming", 1],
["🤦‍♀️", "woman_facepalming", 1], ["🤦‍♀️", "woman_facepalming", 1],
["🤷", "woman_shrugging", 1], ["🤷", "person_shrugging", 1],
["🤷‍♀️", "woman_shrugging", 1],
["🤷‍♂️", "man_shrugging", 1], ["🤷‍♂️", "man_shrugging", 1],
["💁", "tipping_hand_woman", 1], ["💁", "tipping_hand_person", 1],
["💁‍♀️", "tipping_hand_woman", 1],
["💁‍♂️", "tipping_hand_man", 1], ["💁‍♂️", "tipping_hand_man", 1],
["🙅", "no_good_woman", 1], ["🙅", "no_good_person", 1],
["🙅‍♀️", "no_good_woman", 1],
["🙅‍♂️", "no_good_man", 1], ["🙅‍♂️", "no_good_man", 1],
["🙆", "ok_woman", 1], ["🙆", "ok_person", 1],
["🙆‍♀️", "ok_woman", 1],
["🙆‍♂️", "ok_man", 1], ["🙆‍♂️", "ok_man", 1],
["🙋", "raising_hand_woman", 1], ["🙋", "raising_hand_person", 1],
["🙋‍♀️", "raising_hand_woman", 1],
["🙋‍♂️", "raising_hand_man", 1], ["🙋‍♂️", "raising_hand_man", 1],
["🙎", "pouting_woman", 1], ["🙎", "pouting_person", 1],
["🙎‍♀️", "pouting_woman", 1],
["🙎‍♂️", "pouting_man", 1], ["🙎‍♂️", "pouting_man", 1],
["🙍", "frowning_woman", 1], ["🙍", "frowning_person", 1],
["🙍‍♀️", "frowning_woman", 1],
["🙍‍♂️", "frowning_man", 1], ["🙍‍♂️", "frowning_man", 1],
["💇", "haircut_woman", 1], ["💇", "haircut_person", 1],
["💇‍♀️", "haircut_woman", 1],
["💇‍♂️", "haircut_man", 1], ["💇‍♂️", "haircut_man", 1],
["💆", "massage_woman", 1], ["💆", "massage_person", 1],
["💆‍♀️", "massage_woman", 1],
["💆‍♂️", "massage_man", 1], ["💆‍♂️", "massage_man", 1],
["🧖", "person_in_steamy_room", 1],
["🧖‍♀️", "woman_in_steamy_room", 1], ["🧖‍♀️", "woman_in_steamy_room", 1],
["🧖‍♂️", "man_in_steamy_room", 1], ["🧖‍♂️", "man_in_steamy_room", 1],
["🧏", "person_deaf", 1],
["🧏‍♀️", "woman_deaf", 1], ["🧏‍♀️", "woman_deaf", 1],
["🧏‍♂️", "man_deaf", 1], ["🧏‍♂️", "man_deaf", 1],
["🧍", "person_standing", 1],
["🧍‍♀️", "woman_standing", 1], ["🧍‍♀️", "woman_standing", 1],
["🧍‍♂️", "man_standing", 1], ["🧍‍♂️", "man_standing", 1],
["🧎", "person_kneeling", 1],
["🧎‍➡️", "person_kneeling_facing_right", 1],
["🧎‍♀️", "woman_kneeling", 1], ["🧎‍♀️", "woman_kneeling", 1],
["🧎‍♀️‍➡️", "woman_kneeling_facing_right", 1],
["🧎‍♂️", "man_kneeling", 1], ["🧎‍♂️", "man_kneeling", 1],
["🧎‍♂️‍➡️", "man_kneeling_facing_right", 1],
["🧑‍🦯", "person_with_probing_cane", 1], ["🧑‍🦯", "person_with_probing_cane", 1],
["🧑‍🦯‍➡️", "person_with_probing_cane_facing_right", 1],
["👩‍🦯", "woman_with_probing_cane", 1], ["👩‍🦯", "woman_with_probing_cane", 1],
["👩‍🦯‍➡️", "woman_with_probing_cane_facing_right", 1],
["👨‍🦯", "man_with_probing_cane", 1], ["👨‍🦯", "man_with_probing_cane", 1],
["👨‍🦯‍➡️", "man_with_probing_cane_facing_right", 1],
["🧑‍🦼", "person_in_motorized_wheelchair", 1], ["🧑‍🦼", "person_in_motorized_wheelchair", 1],
["🧑‍🦼‍➡️", "person_in_motorized_wheelchair_facing_right", 1],
["👩‍🦼", "woman_in_motorized_wheelchair", 1], ["👩‍🦼", "woman_in_motorized_wheelchair", 1],
["👩‍🦼‍➡️", "woman_in_motorized_wheelchair_facing_right", 1],
["👨‍🦼", "man_in_motorized_wheelchair", 1], ["👨‍🦼", "man_in_motorized_wheelchair", 1],
["👨‍🦼‍➡️", "man_in_motorized_wheelchair_facing_right", 1],
["🧑‍🦽", "person_in_manual_wheelchair", 1], ["🧑‍🦽", "person_in_manual_wheelchair", 1],
["🧑‍🦽‍➡️", "person_in_manual_wheelchair_facing_right", 1],
["👩‍🦽", "woman_in_manual_wheelchair", 1], ["👩‍🦽", "woman_in_manual_wheelchair", 1],
["👩‍🦽‍➡️", "woman_in_manual_wheelchair_facing_right", 1],
["👨‍🦽", "man_in_manual_wheelchair", 1], ["👨‍🦽", "man_in_manual_wheelchair", 1],
["💑", "couple_with_heart_woman_man", 1], ["👨‍🦽‍➡️", "man_in_manual_wheelchair_facing_right", 1],
["💑", "couple_with_heart", 1],
["👩‍❤️‍👨", "couple_with_heart_woman_man", 1],
["👩‍❤️‍👩", "couple_with_heart_woman_woman", 1], ["👩‍❤️‍👩", "couple_with_heart_woman_woman", 1],
["👨‍❤️‍👨", "couple_with_heart_man_man", 1], ["👨‍❤️‍👨", "couple_with_heart_man_man", 1],
["💏", "couplekiss_man_woman", 1], ["💏", "couplekiss", 1],
["👩‍❤️‍💋‍👨", "couplekiss_woman_man", 1],
["👩‍❤️‍💋‍👩", "couplekiss_woman_woman", 1], ["👩‍❤️‍💋‍👩", "couplekiss_woman_woman", 1],
["👨‍❤️‍💋‍👨", "couplekiss_man_man", 1], ["👨‍❤️‍💋‍👨", "couplekiss_man_man", 1],
["👪", "family_man_woman_boy", 1], ["👨‍👩‍👦", "family_man_woman_boy", 1],
["👨‍👩‍👧", "family_man_woman_girl", 1], ["👨‍👩‍👧", "family_man_woman_girl", 1],
["👨‍👩‍👧‍👦", "family_man_woman_girl_boy", 1], ["👨‍👩‍👧‍👦", "family_man_woman_girl_boy", 1],
["👨‍👩‍👦‍👦", "family_man_woman_boy_boy", 1], ["👨‍👩‍👦‍👦", "family_man_woman_boy_boy", 1],
@ -391,6 +453,11 @@
["👨‍👧‍👦", "family_man_girl_boy", 1], ["👨‍👧‍👦", "family_man_girl_boy", 1],
["👨‍👦‍👦", "family_man_boy_boy", 1], ["👨‍👦‍👦", "family_man_boy_boy", 1],
["👨‍👧‍👧", "family_man_girl_girl", 1], ["👨‍👧‍👧", "family_man_girl_girl", 1],
["👪", "family", 1],
["🧑‍🧑‍🧒", "family_adult_adult_child", 1],
["🧑‍🧑‍🧒‍🧒", "family_adult_adult_child_child", 1],
["🧑‍🧒", "family_adult_child", 1],
["🧑‍🧒‍🧒", "family_adult_child_child", 1],
["🧶", "yarn", 1], ["🧶", "yarn", 1],
["🧵", "thread", 1], ["🧵", "thread", 1],
["🧥", "coat", 1], ["🧥", "coat", 1],
@ -475,6 +542,7 @@
["🐦‍⬛", "black_bird", 2], ["🐦‍⬛", "black_bird", 2],
["🦅", "eagle", 2], ["🦅", "eagle", 2],
["🦉", "owl", 2], ["🦉", "owl", 2],
["🐦‍🔥", "phoenix", 2],
["🦇", "bat", 2], ["🦇", "bat", 2],
["🐺", "wolf", 2], ["🐺", "wolf", 2],
["🐗", "boar", 2], ["🐗", "boar", 2],
@ -575,6 +643,7 @@
["🌿", "herb", 2], ["🌿", "herb", 2],
["☘", "shamrock", 2], ["☘", "shamrock", 2],
["🍀", "four_leaf_clover", 2], ["🍀", "four_leaf_clover", 2],
["🪾", "leafless_tree", 2],
["🎍", "bamboo", 2], ["🎍", "bamboo", 2],
["🎋", "tanabata_tree", 2], ["🎋", "tanabata_tree", 2],
["🍃", "leaves", 2], ["🍃", "leaves", 2],
@ -648,6 +717,7 @@
["🪸", "coral", 2], ["🪸", "coral", 2],
["🪹", "empty_nest", 2], ["🪹", "empty_nest", 2],
["🪺", "nest_with_eggs", 2], ["🪺", "nest_with_eggs", 2],
["🍋‍🟩", "lime", 3],
["🍏", "green_apple", 3], ["🍏", "green_apple", 3],
["🍎", "apple", 3], ["🍎", "apple", 3],
["🍐", "pear", 3], ["🍐", "pear", 3],
@ -667,6 +737,8 @@
["🥑", "avocado", 3], ["🥑", "avocado", 3],
["🫛", "pea_pod", 3], ["🫛", "pea_pod", 3],
["🥦", "broccoli", 3], ["🥦", "broccoli", 3],
["🍄‍🟫", "brown_mushroom", 3],
["🫜", "root_vegetable", 3],
["🍅", "tomato", 3], ["🍅", "tomato", 3],
["🍆", "eggplant", 3], ["🍆", "eggplant", 3],
["🥒", "cucumber", 3], ["🥒", "cucumber", 3],
@ -786,8 +858,9 @@
["🥏", "flying_disc", 4], ["🥏", "flying_disc", 4],
["🎱", "8ball", 4], ["🎱", "8ball", 4],
["⛳", "golf", 4], ["⛳", "golf", 4],
["🏌", "golfing_person", 4],
["🏌️‍♀️", "golfing_woman", 4], ["🏌️‍♀️", "golfing_woman", 4],
["🏌", "golfing_man", 4], ["🏌️‍♂️", "golfing_man", 4],
["🏓", "ping_pong", 4], ["🏓", "ping_pong", 4],
["🏸", "badminton", 4], ["🏸", "badminton", 4],
["🥅", "goal_net", 4], ["🥅", "goal_net", 4],
@ -799,10 +872,13 @@
["⛷", "skier", 4], ["⛷", "skier", 4],
["🏂", "snowboarder", 4], ["🏂", "snowboarder", 4],
["🤺", "person_fencing", 4], ["🤺", "person_fencing", 4],
["🤼", "people_wrestling", 4],
["🤼‍♀️", "women_wrestling", 4], ["🤼‍♀️", "women_wrestling", 4],
["🤼‍♂️", "men_wrestling", 4], ["🤼‍♂️", "men_wrestling", 4],
["🤸", "person_cartwheeling", 4],
["🤸‍♀️", "woman_cartwheeling", 4], ["🤸‍♀️", "woman_cartwheeling", 4],
["🤸‍♂️", "man_cartwheeling", 4], ["🤸‍♂️", "man_cartwheeling", 4],
["🤾", "person_playing_handball", 4],
["🤾‍♀️", "woman_playing_handball", 4], ["🤾‍♀️", "woman_playing_handball", 4],
["🤾‍♂️", "man_playing_handball", 4], ["🤾‍♂️", "man_playing_handball", 4],
["⛸", "ice_skate", 4], ["⛸", "ice_skate", 4],
@ -813,27 +889,37 @@
["🎣", "fishing_pole_and_fish", 4], ["🎣", "fishing_pole_and_fish", 4],
["🥊", "boxing_glove", 4], ["🥊", "boxing_glove", 4],
["🥋", "martial_arts_uniform", 4], ["🥋", "martial_arts_uniform", 4],
["🚣", "rowing_person", 4],
["🚣‍♀️", "rowing_woman", 4], ["🚣‍♀️", "rowing_woman", 4],
["🚣", "rowing_man", 4], ["🚣‍♂️", "rowing_man", 4],
["🧗", "climbing_person", 4],
["🧗‍♀️", "climbing_woman", 4], ["🧗‍♀️", "climbing_woman", 4],
["🧗‍♂️", "climbing_man", 4], ["🧗‍♂️", "climbing_man", 4],
["🏊", "swimming_person", 4],
["🏊‍♀️", "swimming_woman", 4], ["🏊‍♀️", "swimming_woman", 4],
["🏊", "swimming_man", 4], ["🏊‍♂️", "swimming_man", 4],
["🤽", "person_playing_water_polo", 4],
["🤽‍♀️", "woman_playing_water_polo", 4], ["🤽‍♀️", "woman_playing_water_polo", 4],
["🤽‍♂️", "man_playing_water_polo", 4], ["🤽‍♂️", "man_playing_water_polo", 4],
["🧘", "person_in_lotus_position", 4],
["🧘‍♀️", "woman_in_lotus_position", 4], ["🧘‍♀️", "woman_in_lotus_position", 4],
["🧘‍♂️", "man_in_lotus_position", 4], ["🧘‍♂️", "man_in_lotus_position", 4],
["🏄", "surfing_person", 4],
["🏄‍♀️", "surfing_woman", 4], ["🏄‍♀️", "surfing_woman", 4],
["🏄", "surfing_man", 4], ["🏄‍♂️", "surfing_man", 4],
["🛀", "bath", 4], ["🛀", "bath", 4],
["⛹", "basketball_person", 4],
["⛹️‍♀️", "basketball_woman", 4], ["⛹️‍♀️", "basketball_woman", 4],
["⛹", "basketball_man", 4], ["⛹️‍♂️", "basketball_man", 4],
["🏋", "weight_lifting_person", 4],
["🏋️‍♀️", "weight_lifting_woman", 4], ["🏋️‍♀️", "weight_lifting_woman", 4],
["🏋", "weight_lifting_man", 4], ["🏋️‍♂️", "weight_lifting_man", 4],
["🚴", "biking_person", 4],
["🚴‍♀️", "biking_woman", 4], ["🚴‍♀️", "biking_woman", 4],
["🚴", "biking_man", 4], ["🚴‍♂️", "biking_man", 4],
["🚵", "mountain_biking_person", 4],
["🚵‍♀️", "mountain_biking_woman", 4], ["🚵‍♀️", "mountain_biking_woman", 4],
["🚵", "mountain_biking_man", 4], ["🚵‍♂️", "mountain_biking_man", 4],
["🏇", "horse_racing", 4], ["🏇", "horse_racing", 4],
["🤿", "diving_mask", 4], ["🤿", "diving_mask", 4],
["🪀", "yo_yo", 4], ["🪀", "yo_yo", 4],
@ -856,6 +942,7 @@
["🎭", "performing_arts", 4], ["🎭", "performing_arts", 4],
["🎨", "art", 4], ["🎨", "art", 4],
["🎪", "circus_tent", 4], ["🎪", "circus_tent", 4],
["🤹", "person_juggling", 4],
["🤹‍♀️", "woman_juggling", 4], ["🤹‍♀️", "woman_juggling", 4],
["🤹‍♂️", "man_juggling", 4], ["🤹‍♂️", "man_juggling", 4],
["🎤", "microphone", 4], ["🎤", "microphone", 4],
@ -872,6 +959,7 @@
["🪕", "banjo", 4], ["🪕", "banjo", 4],
["🪗", "accordion", 4], ["🪗", "accordion", 4],
["🪘", "long_drum", 4], ["🪘", "long_drum", 4],
["🪉", "harp", 4],
["🎬", "clapper", 4], ["🎬", "clapper", 4],
["🎮", "video_game", 4], ["🎮", "video_game", 4],
["👾", "space_invader", 4], ["👾", "space_invader", 4],
@ -1076,8 +1164,10 @@
["🪙", "coin", 6], ["🪙", "coin", 6],
["💳", "credit_card", 6], ["💳", "credit_card", 6],
["🪪", "identification_card", 6], ["🪪", "identification_card", 6],
["🥾", "hiking_boot", 6],
["💎", "gem", 6], ["💎", "gem", 6],
["⚖", "balance_scale", 6], ["⚖", "balance_scale", 6],
["⛓️‍💥", "broken_chain", 6],
["🧰", "toolbox", 6], ["🧰", "toolbox", 6],
["🔧", "wrench", 6], ["🔧", "wrench", 6],
["🔨", "hammer", 6], ["🔨", "hammer", 6],
@ -1093,6 +1183,7 @@
["🪛", "screwdriver", 6], ["🪛", "screwdriver", 6],
["🪝", "hook", 6], ["🪝", "hook", 6],
["🪜", "ladder", 6], ["🪜", "ladder", 6],
["🪏", "shovel", 6],
["🧱", "brick", 6], ["🧱", "brick", 6],
["⛓", "chains", 6], ["⛓", "chains", 6],
["🧲", "magnet", 6], ["🧲", "magnet", 6],
@ -1304,6 +1395,8 @@
["♓", "pisces", 7], ["♓", "pisces", 7],
["🆔", "id", 7], ["🆔", "id", 7],
["⚛", "atom_symbol", 7], ["⚛", "atom_symbol", 7],
["♀️", "female_sign", 7],
["♂️", "male_sign", 7],
["⚧️", "transgender_symbol", 7], ["⚧️", "transgender_symbol", 7],
["🈳", "u7a7a", 7], ["🈳", "u7a7a", 7],
["🈹", "u5272", 7], ["🈹", "u5272", 7],
@ -1463,9 +1556,11 @@
["♾", "infinity", 7], ["♾", "infinity", 7],
["💲", "heavy_dollar_sign", 7], ["💲", "heavy_dollar_sign", 7],
["💱", "currency_exchange", 7], ["💱", "currency_exchange", 7],
["⚕️", "medical_symbol", 7],
["©️", "copyright", 7], ["©️", "copyright", 7],
["®️", "registered", 7], ["®️", "registered", 7],
["™️", "tm", 7], ["™️", "tm", 7],
["🫟", "splatter", 7],
["🔚", "end", 7], ["🔚", "end", 7],
["🔙", "back", 7], ["🔙", "back", 7],
["🔛", "on", 7], ["🔛", "on", 7],
@ -1576,6 +1671,7 @@
["🇧🇲", "bermuda", 8], ["🇧🇲", "bermuda", 8],
["🇧🇹", "bhutan", 8], ["🇧🇹", "bhutan", 8],
["🇧🇴", "bolivia", 8], ["🇧🇴", "bolivia", 8],
["🇧🇻", "bouvet_island", 8],
["🇧🇶", "caribbean_netherlands", 8], ["🇧🇶", "caribbean_netherlands", 8],
["🇧🇦", "bosnia_herzegovina", 8], ["🇧🇦", "bosnia_herzegovina", 8],
["🇧🇼", "botswana", 8], ["🇧🇼", "botswana", 8],
@ -1593,10 +1689,12 @@
["🇮🇨", "canary_islands", 8], ["🇮🇨", "canary_islands", 8],
["🇰🇾", "cayman_islands", 8], ["🇰🇾", "cayman_islands", 8],
["🇨🇫", "central_african_republic", 8], ["🇨🇫", "central_african_republic", 8],
["🇪🇦", "ceuta_melilla", 8],
["🇹🇩", "chad", 8], ["🇹🇩", "chad", 8],
["🇨🇱", "chile", 8], ["🇨🇱", "chile", 8],
["🇨🇳", "cn", 8], ["🇨🇳", "cn", 8],
["🇨🇽", "christmas_island", 8], ["🇨🇽", "christmas_island", 8],
["🇨🇵", "clipperton_island", 8],
["🇨🇨", "cocos_islands", 8], ["🇨🇨", "cocos_islands", 8],
["🇨🇴", "colombia", 8], ["🇨🇴", "colombia", 8],
["🇰🇲", "comoros", 8], ["🇰🇲", "comoros", 8],
@ -1610,6 +1708,7 @@
["🇨🇾", "cyprus", 8], ["🇨🇾", "cyprus", 8],
["🇨🇿", "czech_republic", 8], ["🇨🇿", "czech_republic", 8],
["🇩🇰", "denmark", 8], ["🇩🇰", "denmark", 8],
["🇩🇬", "diego_garcia", 8],
["🇩🇯", "djibouti", 8], ["🇩🇯", "djibouti", 8],
["🇩🇲", "dominica", 8], ["🇩🇲", "dominica", 8],
["🇩🇴", "dominican_republic", 8], ["🇩🇴", "dominican_republic", 8],
@ -1646,6 +1745,7 @@
["🇬🇼", "guinea_bissau", 8], ["🇬🇼", "guinea_bissau", 8],
["🇬🇾", "guyana", 8], ["🇬🇾", "guyana", 8],
["🇭🇹", "haiti", 8], ["🇭🇹", "haiti", 8],
["🇭🇲", "heard_mcdonald_islands", 8],
["🇭🇳", "honduras", 8], ["🇭🇳", "honduras", 8],
["🇭🇰", "hong_kong", 8], ["🇭🇰", "hong_kong", 8],
["🇭🇺", "hungary", 8], ["🇭🇺", "hungary", 8],
@ -1733,10 +1833,12 @@
["🇷🇴", "romania", 8], ["🇷🇴", "romania", 8],
["🇷🇺", "ru", 8], ["🇷🇺", "ru", 8],
["🇷🇼", "rwanda", 8], ["🇷🇼", "rwanda", 8],
["🇨🇶", "sark", 8],
["🇧🇱", "st_barthelemy", 8], ["🇧🇱", "st_barthelemy", 8],
["🇸🇭", "st_helena", 8], ["🇸🇭", "st_helena", 8],
["🇰🇳", "st_kitts_nevis", 8], ["🇰🇳", "st_kitts_nevis", 8],
["🇱🇨", "st_lucia", 8], ["🇱🇨", "st_lucia", 8],
["🇲🇫", "st_martin", 8],
["🇵🇲", "st_pierre_miquelon", 8], ["🇵🇲", "st_pierre_miquelon", 8],
["🇻🇨", "st_vincent_grenadines", 8], ["🇻🇨", "st_vincent_grenadines", 8],
["🇼🇸", "samoa", 8], ["🇼🇸", "samoa", 8],
@ -1762,6 +1864,7 @@
["🇸🇩", "sudan", 8], ["🇸🇩", "sudan", 8],
["🇸🇷", "suriname", 8], ["🇸🇷", "suriname", 8],
["🇸🇿", "swaziland", 8], ["🇸🇿", "swaziland", 8],
["🇸🇯", "svalbard_jan_mayen", 8],
["🇸🇪", "sweden", 8], ["🇸🇪", "sweden", 8],
["🇨🇭", "switzerland", 8], ["🇨🇭", "switzerland", 8],
["🇸🇾", "syria", 8], ["🇸🇾", "syria", 8],
@ -1788,6 +1891,7 @@
["🏴󠁧󠁢󠁳󠁣󠁴󠁿", "scotland", 8], ["🏴󠁧󠁢󠁳󠁣󠁴󠁿", "scotland", 8],
["🏴󠁧󠁢󠁷󠁬󠁳󠁿", "wales", 8], ["🏴󠁧󠁢󠁷󠁬󠁳󠁿", "wales", 8],
["🇺🇸", "us", 8], ["🇺🇸", "us", 8],
["🇺🇲", "us_outlying_islands", 8],
["🇻🇮", "us_virgin_islands", 8], ["🇻🇮", "us_virgin_islands", 8],
["🇺🇾", "uruguay", 8], ["🇺🇾", "uruguay", 8],
["🇺🇿", "uzbekistan", 8], ["🇺🇿", "uzbekistan", 8],

View File

@ -21,20 +21,20 @@
"lint": "pnpm typecheck && pnpm eslint" "lint": "pnpm typecheck && pnpm eslint"
}, },
"devDependencies": { "devDependencies": {
"@types/node": "22.15.31", "@types/node": "22.16.4",
"@typescript-eslint/eslint-plugin": "8.34.0", "@typescript-eslint/eslint-plugin": "8.37.0",
"@typescript-eslint/parser": "8.34.0", "@typescript-eslint/parser": "8.37.0",
"esbuild": "0.25.5", "esbuild": "0.25.6",
"eslint-plugin-vue": "10.2.0", "eslint-plugin-vue": "10.3.0",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"typescript": "5.8.3", "typescript": "5.8.3",
"vue-eslint-parser": "10.1.3" "vue-eslint-parser": "10.2.0"
}, },
"files": [ "files": [
"js-built" "js-built"
], ],
"dependencies": { "dependencies": {
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"vue": "3.5.16" "vue": "3.5.17"
} }
} }

View File

@ -34,7 +34,7 @@ export const commonHandlers = [
}), }),
http.get('/twemoji/:codepoints.svg', async ({ params }) => { http.get('/twemoji/:codepoints.svg', async ({ params }) => {
const { codepoints } = params; const { codepoints } = params;
const value = await fetch(`https://unpkg.com/@discordapp/twemoji@15.0.2/dist/svg/${codepoints}.svg`).then((response) => response.blob()); const value = await fetch(`https://unpkg.com/@discordapp/twemoji@16.0.1/dist/svg/${codepoints}.svg`).then((response) => response.blob());
return new HttpResponse(value, { return new HttpResponse(value, {
headers: { headers: {
'Content-Type': 'image/svg+xml', 'Content-Type': 'image/svg+xml',

View File

@ -17,25 +17,25 @@
}, },
"dependencies": { "dependencies": {
"@analytics/google-analytics": "1.1.0", "@analytics/google-analytics": "1.1.0",
"@discordapp/twemoji": "15.1.0", "@discordapp/twemoji": "16.0.1",
"@github/webauthn-json": "2.1.1", "@github/webauthn-json": "2.1.1",
"@mcaptcha/vanilla-glue": "0.1.0-alpha-3", "@mcaptcha/vanilla-glue": "0.1.0-alpha-3",
"@misskey-dev/browser-image-resizer": "2024.1.0", "@misskey-dev/browser-image-resizer": "2024.1.0",
"@rollup/plugin-json": "6.1.0", "@rollup/plugin-json": "6.1.0",
"@rollup/plugin-replace": "6.0.2", "@rollup/plugin-replace": "6.0.2",
"@rollup/pluginutils": "5.1.4", "@rollup/pluginutils": "5.2.0",
"@sentry/vue": "9.27.0", "@sentry/vue": "9.39.0",
"@syuilo/aiscript": "0.19.0", "@syuilo/aiscript": "0.19.0",
"@twemoji/parser": "15.1.1", "@twemoji/parser": "16.0.0",
"@vitejs/plugin-vue": "5.2.4", "@vitejs/plugin-vue": "5.2.4",
"@vue/compiler-sfc": "3.5.16", "@vue/compiler-sfc": "3.5.17",
"aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15", "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.15",
"analytics": "0.8.16", "analytics": "0.8.16",
"astring": "1.9.0", "astring": "1.9.0",
"broadcast-channel": "7.1.0", "broadcast-channel": "7.1.0",
"buraha": "0.0.1", "buraha": "0.0.1",
"canvas-confetti": "1.9.3", "canvas-confetti": "1.9.3",
"chart.js": "4.4.9", "chart.js": "4.5.0",
"chartjs-adapter-date-fns": "3.0.0", "chartjs-adapter-date-fns": "3.0.0",
"chartjs-chart-matrix": "2.1.1", "chartjs-chart-matrix": "2.1.1",
"chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-gradient": "0.6.1",
@ -54,19 +54,19 @@
"json5": "2.2.3", "json5": "2.2.3",
"magic-string": "0.30.17", "magic-string": "0.30.17",
"matter-js": "0.20.0", "matter-js": "0.20.0",
"mfm-js": "0.24.0", "mfm-js": "0.25.0",
"misskey-bubble-game": "workspace:*", "misskey-bubble-game": "workspace:*",
"misskey-js": "workspace:*", "misskey-js": "workspace:*",
"misskey-reversi": "workspace:*", "misskey-reversi": "workspace:*",
"photoswipe": "5.4.4", "photoswipe": "5.4.4",
"punycode.js": "2.3.1", "punycode.js": "2.3.1",
"rollup": "4.42.0", "rollup": "4.45.1",
"sanitize-html": "2.17.0", "sanitize-html": "2.17.0",
"sass": "1.89.2", "sass": "1.89.2",
"shiki": "3.6.0", "shiki": "3.8.0",
"strict-event-emitter-types": "2.0.0", "strict-event-emitter-types": "2.0.0",
"textarea-caret": "3.1.0", "textarea-caret": "3.1.0",
"three": "0.177.0", "three": "0.178.0",
"throttle-debounce": "5.0.2", "throttle-debounce": "5.0.2",
"tinycolor2": "1.6.0", "tinycolor2": "1.6.0",
"tsc-alias": "1.8.16", "tsc-alias": "1.8.16",
@ -74,12 +74,12 @@
"typescript": "5.8.3", "typescript": "5.8.3",
"v-code-diff": "1.13.1", "v-code-diff": "1.13.1",
"vite": "6.3.5", "vite": "6.3.5",
"vue": "3.5.16", "vue": "3.5.17",
"vuedraggable": "next", "vuedraggable": "next",
"wanakana": "5.3.1" "wanakana": "5.3.1"
}, },
"devDependencies": { "devDependencies": {
"@misskey-dev/summaly": "5.2.1", "@misskey-dev/summaly": "5.2.2",
"@storybook/addon-actions": "8.6.14", "@storybook/addon-actions": "8.6.14",
"@storybook/addon-essentials": "8.6.14", "@storybook/addon-essentials": "8.6.14",
"@storybook/addon-interactions": "8.6.14", "@storybook/addon-interactions": "8.6.14",
@ -104,32 +104,32 @@
"@types/estree": "1.0.8", "@types/estree": "1.0.8",
"@types/matter-js": "0.19.8", "@types/matter-js": "0.19.8",
"@types/micromatch": "4.0.9", "@types/micromatch": "4.0.9",
"@types/node": "22.15.31", "@types/node": "22.16.4",
"@types/punycode.js": "npm:@types/punycode@2.1.4", "@types/punycode.js": "npm:@types/punycode@2.1.4",
"@types/sanitize-html": "2.16.0", "@types/sanitize-html": "2.16.0",
"@types/seedrandom": "3.0.8", "@types/seedrandom": "3.0.8",
"@types/throttle-debounce": "5.0.2", "@types/throttle-debounce": "5.0.2",
"@types/tinycolor2": "1.4.6", "@types/tinycolor2": "1.4.6",
"@types/ws": "8.18.1", "@types/ws": "8.18.1",
"@typescript-eslint/eslint-plugin": "8.34.0", "@typescript-eslint/eslint-plugin": "8.37.0",
"@typescript-eslint/parser": "8.34.0", "@typescript-eslint/parser": "8.37.0",
"@vitest/coverage-v8": "3.2.3", "@vitest/coverage-v8": "3.2.4",
"@vue/compiler-core": "3.5.16", "@vue/compiler-core": "3.5.17",
"@vue/runtime-core": "3.5.16", "@vue/runtime-core": "3.5.17",
"acorn": "8.15.0", "acorn": "8.15.0",
"cross-env": "7.0.3", "cross-env": "7.0.3",
"cypress": "14.4.1", "cypress": "14.5.2",
"eslint-plugin-import": "2.31.0", "eslint-plugin-import": "2.32.0",
"eslint-plugin-vue": "10.2.0", "eslint-plugin-vue": "10.3.0",
"fast-glob": "3.3.3", "fast-glob": "3.3.3",
"happy-dom": "17.6.3", "happy-dom": "17.6.3",
"intersection-observer": "0.12.2", "intersection-observer": "0.12.2",
"micromatch": "4.0.8", "micromatch": "4.0.8",
"minimatch": "10.0.1", "minimatch": "10.0.3",
"msw": "2.10.2", "msw": "2.10.4",
"msw-storybook-addon": "2.0.5", "msw-storybook-addon": "2.0.5",
"nodemon": "3.1.10", "nodemon": "3.1.10",
"prettier": "3.5.3", "prettier": "3.6.2",
"react": "19.1.0", "react": "19.1.0",
"react-dom": "19.1.0", "react-dom": "19.1.0",
"seedrandom": "3.0.5", "seedrandom": "3.0.5",
@ -137,10 +137,10 @@
"storybook": "8.6.14", "storybook": "8.6.14",
"storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme",
"vite-plugin-turbosnap": "1.0.3", "vite-plugin-turbosnap": "1.0.3",
"vitest": "3.2.3", "vitest": "3.2.4",
"vitest-fetch-mock": "0.4.5", "vitest-fetch-mock": "0.4.5",
"vue-component-type-helpers": "2.2.10", "vue-component-type-helpers": "2.2.12",
"vue-eslint-parser": "10.1.3", "vue-eslint-parser": "10.2.0",
"vue-tsc": "2.2.10" "vue-tsc": "2.2.12"
} }
} }

View File

@ -5,7 +5,7 @@
import { computed, watch, version as vueVersion } from 'vue'; import { computed, watch, version as vueVersion } from 'vue';
import { compareVersions } from 'compare-versions'; import { compareVersions } from 'compare-versions';
import { version, lang, updateLocale, locale, apiUrl } from '@@/js/config.js'; import { version, lang, updateLocale, locale, apiUrl, isSafeMode } from '@@/js/config.js';
import defaultLightTheme from '@@/themes/l-light.json5'; import defaultLightTheme from '@@/themes/l-light.json5';
import defaultDarkTheme from '@@/themes/d-green-lime.json5'; import defaultDarkTheme from '@@/themes/d-green-lime.json5';
import type { App } from 'vue'; import type { App } from 'vue';
@ -168,28 +168,35 @@ export async function common(createVue: () => Promise<App<Element>>) {
// NOTE: この処理は必ずクライアント更新チェック処理より後に来ること(テーマ再構築のため) // NOTE: この処理は必ずクライアント更新チェック処理より後に来ること(テーマ再構築のため)
watch(store.r.darkMode, (darkMode) => { watch(store.r.darkMode, (darkMode) => {
applyTheme(darkMode const theme = (() => {
? (prefer.s.darkTheme ?? defaultDarkTheme) if (darkMode) {
: (prefer.s.lightTheme ?? defaultLightTheme), return isSafeMode ? defaultDarkTheme : (prefer.s.darkTheme ?? defaultDarkTheme);
); } else {
}, { immediate: miLocalStorage.getItem('theme') == null }); return isSafeMode ? defaultLightTheme : (prefer.s.lightTheme ?? defaultLightTheme);
}
})();
applyTheme(theme);
}, { immediate: isSafeMode || miLocalStorage.getItem('theme') == null });
window.document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light'; window.document.documentElement.dataset.colorScheme = store.s.darkMode ? 'dark' : 'light';
const darkTheme = prefer.model('darkTheme'); if (!isSafeMode) {
const lightTheme = prefer.model('lightTheme'); const darkTheme = prefer.model('darkTheme');
const lightTheme = prefer.model('lightTheme');
watch(darkTheme, (theme) => { watch(darkTheme, (theme) => {
if (store.s.darkMode) { if (store.s.darkMode) {
applyTheme(theme ?? defaultDarkTheme); applyTheme(theme ?? defaultDarkTheme);
} }
}); });
watch(lightTheme, (theme) => { watch(lightTheme, (theme) => {
if (!store.s.darkMode) { if (!store.s.darkMode) {
applyTheme(theme ?? defaultLightTheme); applyTheme(theme ?? defaultLightTheme);
} }
}); });
}
//#region Sync dark mode //#region Sync dark mode
if (prefer.s.syncDeviceDarkMode) { if (prefer.s.syncDeviceDarkMode) {
@ -203,17 +210,19 @@ export async function common(createVue: () => Promise<App<Element>>) {
}); });
//#endregion //#endregion
if (prefer.s.darkTheme && store.s.darkMode) { if (!isSafeMode) {
if (miLocalStorage.getItem('themeId') !== prefer.s.darkTheme.id) applyTheme(prefer.s.darkTheme); if (prefer.s.darkTheme && store.s.darkMode) {
} else if (prefer.s.lightTheme && !store.s.darkMode) { if (miLocalStorage.getItem('themeId') !== prefer.s.darkTheme.id) applyTheme(prefer.s.darkTheme);
if (miLocalStorage.getItem('themeId') !== prefer.s.lightTheme.id) applyTheme(prefer.s.lightTheme); } else if (prefer.s.lightTheme && !store.s.darkMode) {
} if (miLocalStorage.getItem('themeId') !== prefer.s.lightTheme.id) applyTheme(prefer.s.lightTheme);
}
fetchInstanceMetaPromise.then(() => { fetchInstanceMetaPromise.then(() => {
// TODO: instance.defaultLightTheme/instance.defaultDarkThemeが不正な形式だった場合のケア // TODO: instance.defaultLightTheme/instance.defaultDarkThemeが不正な形式だった場合のケア
if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.commit('lightTheme', JSON.parse(instance.defaultLightTheme)); if (prefer.s.lightTheme == null && instance.defaultLightTheme != null) prefer.commit('lightTheme', JSON.parse(instance.defaultLightTheme));
if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.commit('darkTheme', JSON.parse(instance.defaultDarkTheme)); if (prefer.s.darkTheme == null && instance.defaultDarkTheme != null) prefer.commit('darkTheme', JSON.parse(instance.defaultDarkTheme));
}); });
}
watch(prefer.r.overridedDeviceKind, (kind) => { watch(prefer.r.overridedDeviceKind, (kind) => {
updateDeviceKind(kind); updateDeviceKind(kind);

View File

@ -28,8 +28,8 @@ import { addCustomEmoji, removeCustomEmojis, updateCustomEmojis } from '@/custom
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { launchPlugins } from '@/plugin.js'; import { launchPlugins } from '@/plugin.js';
import { updateCurrentAccountPartial } from '@/accounts.js'; import { updateCurrentAccountPartial } from '@/accounts.js';
import { signout } from '@/signout.js';
import { migrateOldSettings } from '@/pref-migrate.js'; import { migrateOldSettings } from '@/pref-migrate.js';
import { unisonReload } from '@/utility/unison-reload.js';
export async function mainBoot() { export async function mainBoot() {
const { isClientUpdated, lastVersion } = await common(async () => { const { isClientUpdated, lastVersion } = await common(async () => {
@ -391,6 +391,8 @@ export async function mainBoot() {
} }
// shortcut // shortcut
let safemodeRequestCount = 0;
let safemodeRequestTimer: number | null = null;
const keymap = { const keymap = {
'p|n': () => { 'p|n': () => {
if ($i == null) return; if ($i == null) return;
@ -402,6 +404,24 @@ export async function mainBoot() {
's': () => { 's': () => {
mainRouter.push('/search'); mainRouter.push('/search');
}, },
'g': {
callback: () => {
// mを5回押すとセーフモードに入る
safemodeRequestCount++;
if (safemodeRequestCount >= 5) {
miLocalStorage.setItem('isSafeMode', 'true');
unisonReload();
} else {
if (safemodeRequestTimer != null) {
window.clearTimeout(safemodeRequestTimer);
}
safemodeRequestTimer = window.setTimeout(() => {
safemodeRequestCount = 0;
}, 300);
}
},
allowRepeat: true,
}
} as const satisfies Keymap; } as const satisfies Keymap;
window.document.addEventListener('keydown', makeHotkey(keymap), { passive: false }); window.document.addEventListener('keydown', makeHotkey(keymap), { passive: false });

View File

@ -145,7 +145,7 @@ import { claimAchievement } from '@/utility/achievements.js';
import { prefer } from '@/preferences.js'; import { prefer } from '@/preferences.js';
import { chooseFileFromPcAndUpload, selectDriveFolder } from '@/utility/drive.js'; import { chooseFileFromPcAndUpload, selectDriveFolder } from '@/utility/drive.js';
import { store } from '@/store.js'; import { store } from '@/store.js';
import { isSeparatorNeeded, getSeparatorInfo, makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js'; import { makeDateGroupedTimelineComputedRef } from '@/utility/timeline-date-separate.js';
import { globalEvents, useGlobalEvent } from '@/events.js'; import { globalEvents, useGlobalEvent } from '@/events.js';
import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js'; import { checkDragDataType, getDragData, setDragData } from '@/drag-and-drop.js';
import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js'; import { getDriveFileMenu } from '@/utility/get-drive-file-menu.js';

View File

@ -495,7 +495,7 @@ function done(query?: string): boolean | void {
function settings() { function settings() {
emit('esc'); emit('esc');
router.push('settings/emoji-palette'); router.push('/settings/emoji-palette');
} }
onMounted(() => { onMounted(() => {
@ -684,13 +684,8 @@ defineExpose({
height: 100%; height: 100%;
overflow-y: auto; overflow-y: auto;
overflow-x: hidden; overflow-x: hidden;
scrollbar-width: none; scrollbar-width: none;
&::-webkit-scrollbar {
display: none;
}
> .group { > .group {
&:not(.index) { &:not(.index) {
padding: 4px 0 8px 0; padding: 4px 0 8px 0;

View File

@ -52,15 +52,20 @@ import TestWebGL2 from '@/workers/test-webgl2?worker';
import { WorkerMultiDispatch } from '@@/js/worker-multi-dispatch.js'; import { WorkerMultiDispatch } from '@@/js/worker-multi-dispatch.js';
import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurhash.js'; import { extractAvgColorFromBlurhash } from '@@/js/extract-avg-color-from-blurhash.js';
// Web Worker
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-expect-error
const isTest = (import.meta.env.MODE === 'test' || window.Cypress != null);
const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => { const canvasPromise = new Promise<WorkerMultiDispatch | HTMLCanvasElement>(resolve => {
// Web Worker if (isTest) {
if (import.meta.env.MODE === 'test') {
const canvas = window.document.createElement('canvas'); const canvas = window.document.createElement('canvas');
canvas.width = 64; canvas.width = 64;
canvas.height = 64; canvas.height = 64;
resolve(canvas); resolve(canvas);
return; return;
} }
const testWorker = new TestWebGL2(); const testWorker = new TestWebGL2();
testWorker.addEventListener('message', event => { testWorker.addEventListener('message', event => {
if (event.data.result) { if (event.data.result) {
@ -189,7 +194,7 @@ function drawAvg() {
} }
async function draw() { async function draw() {
if (import.meta.env.MODE === 'test' && props.hash == null) return; if (isTest && props.hash == null) return;
drawAvg(); drawAvg();

View File

@ -15,7 +15,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@contextmenu.stop @contextmenu.stop
@keydown.stop @keydown.stop
> >
<button v-if="hide" :class="$style.hidden" @click="show"> <button v-if="hide" :class="$style.hidden" @click="reveal">
<div :class="$style.hiddenTextWrapper"> <div :class="$style.hiddenTextWrapper">
<b v-if="audio.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ prefer.s.dataSaver.media ? ` (${i18n.ts.audio}${audio.size ? ' ' + bytes(audio.size) : ''})` : '' }}</b> <b v-if="audio.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ prefer.s.dataSaver.media ? ` (${i18n.ts.audio}${audio.size ? ' ' + bytes(audio.size) : ''})` : '' }}</b>
<b v-else style="display: block;"><i class="ti ti-music"></i> {{ prefer.s.dataSaver.media && audio.size ? bytes(audio.size) : i18n.ts.audio }}</b> <b v-else style="display: block;"><i class="ti ti-music"></i> {{ prefer.s.dataSaver.media && audio.size ? bytes(audio.size) : i18n.ts.audio }}</b>
@ -157,7 +157,7 @@ const audioEl = useTemplateRef('audioEl');
// eslint-disable-next-line vue/no-setup-props-reactivity-loss // eslint-disable-next-line vue/no-setup-props-reactivity-loss
const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.audio.isSensitive && prefer.s.nsfw !== 'ignore')); const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.audio.isSensitive && prefer.s.nsfw !== 'ignore'));
async function show() { async function reveal() {
if (props.audio.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) { if (props.audio.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'question', type: 'question',

View File

@ -6,7 +6,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<template> <template>
<div :class="$style.root"> <div :class="$style.root">
<MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/> <MkMediaAudio v-if="media.type.startsWith('audio') && media.type !== 'audio/midi'" :audio="media"/>
<div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="show"> <div v-else-if="media.isSensitive && hide" :class="$style.sensitive" @click="reveal">
<span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span> <span style="font-size: 1.6em;"><i class="ti ti-alert-triangle"></i></span>
<b>{{ i18n.ts.sensitive }}</b> <b>{{ i18n.ts.sensitive }}</b>
<span>{{ i18n.ts.clickToShow }}</span> <span>{{ i18n.ts.clickToShow }}</span>
@ -37,7 +37,7 @@ const props = defineProps<{
const hide = ref(true); const hide = ref(true);
async function show() { async function reveal() {
if (props.media.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) { if (props.media.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'question', type: 'question',

View File

@ -4,7 +4,7 @@ SPDX-License-Identifier: AGPL-3.0-only
--> -->
<template> <template>
<div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @click="onclick"> <div :class="[hide ? $style.hidden : $style.visible, (image.isSensitive && prefer.s.highlightSensitiveMedia) && $style.sensitive]" @click="reveal">
<component <component
:is="disableImageLink ? 'div' : 'a'" :is="disableImageLink ? 'div' : 'a'"
v-bind="disableImageLink ? { v-bind="disableImageLink ? {
@ -96,10 +96,10 @@ const url = computed(() => (props.raw || prefer.s.loadRawImages)
? props.image.url ? props.image.url
: prefer.s.disableShowingAnimatedImages : prefer.s.disableShowingAnimatedImages
? getStaticImageUrl(props.image.url) ? getStaticImageUrl(props.image.url)
: props.image.thumbnailUrl, : props.image.thumbnailUrl!,
); );
async function onclick(ev: MouseEvent) { async function reveal(ev: MouseEvent) {
if (!props.controls) { if (!props.controls) {
return; return;
} }

View File

@ -19,7 +19,7 @@ SPDX-License-Identifier: AGPL-3.0-only
@contextmenu.stop @contextmenu.stop
@keydown.stop @keydown.stop
> >
<button v-if="hide" :class="$style.hidden" @click="show"> <button v-if="hide" :class="$style.hidden" @click="reveal">
<div :class="$style.hiddenTextWrapper"> <div :class="$style.hiddenTextWrapper">
<b v-if="video.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ prefer.s.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b> <b v-if="video.isSensitive" style="display: block;"><i class="ti ti-eye-exclamation"></i> {{ i18n.ts.sensitive }}{{ prefer.s.dataSaver.media ? ` (${i18n.ts.video}${video.size ? ' ' + bytes(video.size) : ''})` : '' }}</b>
<b v-else style="display: block;"><i class="ti ti-movie"></i> {{ prefer.s.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b> <b v-else style="display: block;"><i class="ti ti-movie"></i> {{ prefer.s.dataSaver.media && video.size ? bytes(video.size) : i18n.ts.video }}</b>
@ -178,7 +178,7 @@ function hasFocus() {
// eslint-disable-next-line vue/no-setup-props-reactivity-loss // eslint-disable-next-line vue/no-setup-props-reactivity-loss
const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.video.isSensitive && prefer.s.nsfw !== 'ignore')); const hide = ref((prefer.s.nsfw === 'force' || prefer.s.dataSaver.media) ? true : (props.video.isSensitive && prefer.s.nsfw !== 'ignore'));
async function show() { async function reveal() {
if (props.video.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) { if (props.video.isSensitive && prefer.s.confirmWhenRevealingSensitiveMedia) {
const { canceled } = await os.confirm({ const { canceled } = await os.confirm({
type: 'question', type: 'question',

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