Merge branch 'develop' into pag-back

This commit is contained in:
tamaina 2023-08-24 14:54:47 +09:00 committed by GitHub
commit fb5bb950de
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
616 changed files with 5021 additions and 2993 deletions

View file

@ -159,6 +159,9 @@ id: 'aid'
#deliverJobMaxAttempts: 12 #deliverJobMaxAttempts: 12
#inboxJobMaxAttempts: 8 #inboxJobMaxAttempts: 8
# Local address used for outgoing requests
#outgoingAddress: 127.0.0.1
# IP address family used for outgoing request (ipv4, ipv6 or dual) # IP address family used for outgoing request (ipv4, ipv6 or dual)
#outgoingAddressFamily: ipv4 #outgoingAddressFamily: ipv4

View file

@ -14,7 +14,7 @@ jobs:
- run: corepack enable - run: corepack enable
- name: Setup Node.js - name: Setup Node.js
uses: actions/setup-node@v3.7.0 uses: actions/setup-node@v3.8.1
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'

View file

@ -19,7 +19,7 @@ jobs:
with: with:
version: 8 version: 8
run_install: false run_install: false
- uses: actions/setup-node@v3.7.0 - uses: actions/setup-node@v3.8.1
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'
@ -46,7 +46,7 @@ jobs:
with: with:
version: 7 version: 7
run_install: false run_install: false
- uses: actions/setup-node@v3.7.0 - uses: actions/setup-node@v3.8.1
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'
@ -72,7 +72,7 @@ jobs:
with: with:
version: 7 version: 7
run_install: false run_install: false
- uses: actions/setup-node@v3.7.0 - uses: actions/setup-node@v3.8.1
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'

View file

@ -38,7 +38,7 @@ jobs:
version: 8 version: 8
run_install: false run_install: false
- name: Use Node.js 20.x - name: Use Node.js 20.x
uses: actions/setup-node@v3.7.0 uses: actions/setup-node@v3.8.1
with: with:
node-version-file: '.node-version' node-version-file: '.node-version'
cache: 'pnpm' cache: 'pnpm'

View file

@ -38,7 +38,7 @@ jobs:
version: 8 version: 8
run_install: false run_install: false
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.7.0 uses: actions/setup-node@v3.8.1
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'pnpm' cache: 'pnpm'

View file

@ -25,7 +25,7 @@ jobs:
version: 8 version: 8
run_install: false run_install: false
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.7.0 uses: actions/setup-node@v3.8.1
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'pnpm' cache: 'pnpm'
@ -83,7 +83,7 @@ jobs:
version: 7 version: 7
run_install: false run_install: false
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.7.0 uses: actions/setup-node@v3.8.1
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'pnpm' cache: 'pnpm'
@ -101,7 +101,7 @@ jobs:
- name: Cypress install - name: Cypress install
run: pnpm exec cypress install run: pnpm exec cypress install
- name: Cypress run - name: Cypress run
uses: cypress-io/github-action@v5 uses: cypress-io/github-action@v6
with: with:
install: false install: false
start: pnpm start:test start: pnpm start:test

View file

@ -26,7 +26,7 @@ jobs:
- run: corepack enable - run: corepack enable
- name: Setup Node.js ${{ matrix.node-version }} - name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.7.0 uses: actions/setup-node@v3.8.1
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'pnpm' cache: 'pnpm'

View file

@ -28,7 +28,7 @@ jobs:
version: 8 version: 8
run_install: false run_install: false
- name: Use Node.js ${{ matrix.node-version }} - name: Use Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v3.7.0 uses: actions/setup-node@v3.8.1
with: with:
node-version: ${{ matrix.node-version }} node-version: ${{ matrix.node-version }}
cache: 'pnpm' cache: 'pnpm'

View file

@ -12,7 +12,7 @@
--> -->
## 2023.8.0 (unreleased) ## 2023.9.0 (unreleased)
### General ### General
- OAuth 2.0のサポート - OAuth 2.0のサポート
@ -23,20 +23,33 @@
- チャンネルをセンシティブ指定できるようになりました - チャンネルをセンシティブ指定できるようになりました
### Client ### Client
- プロフィールにその人が作ったPlayの一覧出せるように
- メニューのスイッチの動作を改善 - メニューのスイッチの動作を改善
- 絵文字ピッカーの検索の表示件数を100件に増加 - 絵文字ピッカーの検索の表示件数を100件に増加
- 投稿フォームのプレビューの表示状態を記憶するように
- Enhance: ユーザーメニューでスイッチでユーザーリストに追加・削除できるように - Enhance: ユーザーメニューでスイッチでユーザーリストに追加・削除できるように
- Enhance: 自分が押したリアクションのデザインを改善 - Enhance: 自分が押したリアクションのデザインを改善
- Enhance: ノート検索にローカルのみ検索可能なオプションの追加
- Enhance: AiScriptで`LOCALE`として現在の設定言語を取得できるように
- `$[rainbow ]`記法が、動きのあるMFMが無効になっていても使用できるようになりました
- Playの操作を行うAPI TokenをAPIコンソールから発行できるように
- Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正 - Fix: サーバー情報画面(`/instance-info/{domain}`)でブロックができないのを修正
- Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正 - Fix: 未読のお知らせの「わかった」をクリック・タップしてもその場で「わかった」が消えない問題を修正
- Fix: iOSで画面を回転させるとテキストサイズが変わる問題を修正 - Fix: iOSで画面を回転させるとテキストサイズが変わる問題を修正
- Fix: word mute for sub note is not applied
- Fix: タイムラインを下にスクロールしてノート画面に移動して再び戻ったら以前のスクロール位置を失う問題を修正 - Fix: タイムラインを下にスクロールしてノート画面に移動して再び戻ったら以前のスクロール位置を失う問題を修正
- Fix: Misskeyプラグインをインストールする際のAiScriptバージョンのチェックが0.14.0以降に対応していない問題を修正
- Fix: 他のサーバーのユーザーへ「メッセージを送信」した時の初期テキストのメンションが間違っている問題を修正
### Server ### Server
- Fix: ノート検索 `notes/search` にてhostを指定した際に検索結果に反映されるように
- cacheRemoteFilesの初期値はfalseになりました - cacheRemoteFilesの初期値はfalseになりました
- ファイルアップロード時等にファイル名の拡張子を修正する関数(correctFilename)の挙動を改善 - ファイルアップロード時等にファイル名の拡張子を修正する関数(correctFilename)の挙動を改善
- Webhookのペイロードにサーバーのurlが含まれるようになりました
- Fix: 一部のfeatured noteを照会できない問題を修正 - Fix: 一部のfeatured noteを照会できない問題を修正
- Fix: muteがapiからのuser list timeline取得で機能しない問題を修正 - Fix: muteがapiからのuser list timeline取得で機能しない問題を修正
- Fix: ジョブキュー管理画面の認証を回避できる問題を修正
- Fix: 一部のサーバー内部エラーがスタックトレースを返さないように修正
## 13.14.2 ## 13.14.2
@ -51,6 +64,7 @@
### Server ### Server
- Fix: APIのオフセットが壊れていたせいで「もっと見る」でもっと見れない問題を修正 - Fix: APIのオフセットが壊れていたせいで「もっと見る」でもっと見れない問題を修正
- Fix: 外部サーバーの投稿がタイムラインに表示されないことがある問題を修正 - Fix: 外部サーバーの投稿がタイムラインに表示されないことがある問題を修正
- Enhance: Add address bind config option (outgoingAddress)
## 13.14.1 ## 13.14.1

View file

@ -645,6 +645,7 @@ optional: "اختياري"
createNewClip: "أنشئ مِشبكَا جديدًا" createNewClip: "أنشئ مِشبكَا جديدًا"
confirmToUnclipAlreadyClippedNote: "هذه الملاحظة تنتمي للمشبك {name} سلفًا، أتريد حذفها منه⸮" confirmToUnclipAlreadyClippedNote: "هذه الملاحظة تنتمي للمشبك {name} سلفًا، أتريد حذفها منه⸮"
public: "علني" public: "علني"
private: "خاص"
i18nInfo: "يترجم متطوعون ميسكي إلى عدة لغات، يمكنك المساعدة عبر {link}" i18nInfo: "يترجم متطوعون ميسكي إلى عدة لغات، يمكنك المساعدة عبر {link}"
manageAccessTokens: "إدارة رموز الوصول" manageAccessTokens: "إدارة رموز الوصول"
accountInfo: "معلومات الحساب" accountInfo: "معلومات الحساب"
@ -986,6 +987,7 @@ later: "لاحقاً"
goToMisskey: "لميسكي" goToMisskey: "لميسكي"
additionalEmojiDictionary: "قواميس إيموجي إضافية" additionalEmojiDictionary: "قواميس إيموجي إضافية"
installed: "مُثبت" installed: "مُثبت"
icon: "الصورة الرمزية"
_initialAccountSetting: _initialAccountSetting:
accountCreated: "نجح إنشاء حسابك!" accountCreated: "نجح إنشاء حسابك!"
letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي." letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي."
@ -1487,22 +1489,22 @@ _notification:
fileUploaded: "نجح رفع الملف" fileUploaded: "نجح رفع الملف"
youGotMention: "{name} أشار إليك" youGotMention: "{name} أشار إليك"
youGotReply: "ردّ عليك {name}" youGotReply: "ردّ عليك {name}"
youGotQuote: "اقتبس منك {name}" youGotQuote: "اقتبس {name} منشورك"
youRenoted: "إعادت نشر من {name}" youRenoted: "أعاد {name} نشر منشورك"
youWereFollowed: "يتابعك" youWereFollowed: "يتابعك"
youReceivedFollowRequest: "تلقيتَ طلب متابعة" youReceivedFollowRequest: "تلقيتَ طلب متابعة"
yourFollowRequestAccepted: "قُبل طلب المتابعة" yourFollowRequestAccepted: "قُبل طلب المتابعة"
pollEnded: "ظهرت نتائج الاستطلاع" pollEnded: "انتهى الاستطلاع"
unreadAntennaNote: "هوائي {name}" unreadAntennaNote: "هوائي {name}"
_types: _types:
all: "الكل" all: "الكل"
follow: "متابِعون جدد" follow: "متابِعون جدد"
mention: "الإشارات" mention: "الإشارات"
reply: "الردود" reply: "الردود"
renote: "أعد النشر" renote: "أعاد النشر"
quote: "الاقتباسات" quote: "الاقتباسات"
reaction: "التفاعلات" reaction: "التفاعل"
receiveFollowRequest: "طلبات المتابعة المتلقاة" receiveFollowRequest: "طلبات المتابعة"
followRequestAccepted: "طلبات المتابعة المقبولة" followRequestAccepted: "طلبات المتابعة المقبولة"
app: "إشعارات التطبيقات المرتبطة" app: "إشعارات التطبيقات المرتبطة"
_actions: _actions:
@ -1510,26 +1512,26 @@ _notification:
reply: "رد" reply: "رد"
renote: "أعد النشر" renote: "أعد النشر"
_deck: _deck:
alwaysShowMainColumn: "أظهر العمود الرئيسي دائمًا" alwaysShowMainColumn: "أظهر العمود الأساسي دائمًا"
columnAlign: "حاذِ الأعمدة" columnAlign: "محاذاة الأعمدة"
addColumn: "أضف عمودًا" addColumn: "إضافة عمود"
swapLeft: "حرّك لليسار" swapLeft: "التحريك إلى اليسار"
swapRight: "حرّك لليمين" swapRight: "التحريك إلى اليمين"
swapUp: "حرّك لأعلى" swapUp: "التحريك إلى الأعلى"
swapDown: "حرّك لأسفل" swapDown: "التحريك إلى الأسفل"
profile: "الملف الشخصي" profile: "حسابي الشخصي"
_columns: _columns:
main: "الرئيسي" main: "الرئيسية"
widgets: "الودجات" widgets: "التطبيقات المُصغّرة"
notifications: "الإشعارات" notifications: "الإشعارات"
tl: "الخيط الزمني" tl: "الخط الزمني"
antenna: "الهوائيات" antenna: "الهوائيات"
list: "القوائم" list: "القوائم"
channel: "القنوات" channel: "القنوات"
mentions: "الإشارات" mentions: "الإشارات"
direct: "مباشرة" direct: "مباشرة"
_webhookSettings: _webhookSettings:
name: "الإسم" name: "الاسم"
active: فعّل" active: ُفعّل"
_events: _events:
reaction: "عند تلقي تفاعل" reaction: "عند التفاعل"

View file

@ -628,6 +628,7 @@ createNew: "নতুন"
optional: "প্রয়োজনীয় নয়" optional: "প্রয়োজনীয় নয়"
createNewClip: "নতুন ক্লিপ তৈরি করুন" createNewClip: "নতুন ক্লিপ তৈরি করুন"
public: "সর্বজনীন" public: "সর্বজনীন"
private: "ব্যাক্তিগত"
i18nInfo: "Misskey স্বেচ্ছাসেবকদের দ্বারা বিভিন্ন ভাষায় অনুবাদ করা হচ্ছে। আপনি {link} এ গিয়ে অনুবাদে সহযোগিতা করতে পারেন।" i18nInfo: "Misskey স্বেচ্ছাসেবকদের দ্বারা বিভিন্ন ভাষায় অনুবাদ করা হচ্ছে। আপনি {link} এ গিয়ে অনুবাদে সহযোগিতা করতে পারেন।"
manageAccessTokens: "অ্যাক্সেস টোকেন পরিচালনা করুন" manageAccessTokens: "অ্যাক্সেস টোকেন পরিচালনা করুন"
accountInfo: "অ্যাকাউন্টের তথ্য" accountInfo: "অ্যাকাউন্টের তথ্য"
@ -837,6 +838,7 @@ show: "প্রদর্শন"
color: "রং" color: "রং"
horizontal: "পাশে" horizontal: "পাশে"
youFollowing: "অনুসরণ করা হচ্ছে" youFollowing: "অনুসরণ করা হচ্ছে"
icon: "প্রোফাইল ছবি"
_role: _role:
priority: "অগ্রাধিকার" priority: "অগ্রাধিকার"
_priority: _priority:

View file

@ -13,12 +13,14 @@ fetchingAsApObject: "Cercant en el Fediverse..."
ok: "OK" ok: "OK"
gotIt: "Ho he entès!" gotIt: "Ho he entès!"
cancel: "Cancel·lar" cancel: "Cancel·lar"
noThankYou: "No, gràcies"
enterUsername: "Introdueix el teu nom d'usuari" enterUsername: "Introdueix el teu nom d'usuari"
renotedBy: "Impulsat per {usuari}" renotedBy: "Impulsat per {usuari}"
noNotes: "Cap nota" noNotes: "Cap nota"
noNotifications: "Cap notificació" noNotifications: "Cap notificació"
instance: "Servidor" instance: "Servidor"
settings: "Preferències" settings: "Preferències"
notificationSettings: "Paràmetres de notificacions"
basicSettings: "Configuració bàsica" basicSettings: "Configuració bàsica"
otherSettings: "Configuració avançada" otherSettings: "Configuració avançada"
openInWindow: "Obrir en una nova finestra" openInWindow: "Obrir en una nova finestra"
@ -47,8 +49,15 @@ delete: "Elimina"
deleteAndEdit: "Elimina i edita" deleteAndEdit: "Elimina i edita"
deleteAndEditConfirm: "Segur que vols eliminar aquesta publicació i editar-la? Perdràs totes les reaccions, impulsos i respostes." deleteAndEditConfirm: "Segur que vols eliminar aquesta publicació i editar-la? Perdràs totes les reaccions, impulsos i respostes."
addToList: "Afegir a una llista" addToList: "Afegir a una llista"
addToAntenna: "Afegir a l'antena"
sendMessage: "Enviar un missatge" sendMessage: "Enviar un missatge"
copyRSS: "Copiar RSS"
copyUsername: "Copiar nom d'usuari" copyUsername: "Copiar nom d'usuari"
copyUserId: "Copiar ID d'usuari"
copyNoteId: "Copiar ID de nota"
copyFileId: "Copiar ID d'arxiu"
copyFolderId: "Copiar ID de carpeta"
copyProfileUrl: "Copiar URL del perfil"
searchUser: "Cercar un usuari" searchUser: "Cercar un usuari"
reply: "Respondre" reply: "Respondre"
loadMore: "Carregar més" loadMore: "Carregar més"
@ -128,6 +137,7 @@ suspendConfirm: "Estàs segur que vols suspendre aquest compte?"
unsuspendConfirm: "Estàs segur que vols treure la suspensió d'aquest compte?" unsuspendConfirm: "Estàs segur que vols treure la suspensió d'aquest compte?"
selectList: "Tria una llista" selectList: "Tria una llista"
selectAntenna: "Tria una antena" selectAntenna: "Tria una antena"
editAntenna: "Modificar antena"
selectWidget: "Triar un giny" selectWidget: "Triar un giny"
editWidgets: "Editar ginys" editWidgets: "Editar ginys"
editWidgetsExit: "Fet" editWidgetsExit: "Fet"
@ -298,8 +308,10 @@ manageAntennas: "Gestiona les antenes"
antennaSource: "Font de l'antena" antennaSource: "Font de l'antena"
antennaKeywords: "Paraules clau a seguir" antennaKeywords: "Paraules clau a seguir"
antennaExcludeKeywords: "Paraules clau a excloure" antennaExcludeKeywords: "Paraules clau a excloure"
antennaKeywordsDescription: "Separar amb espais per la condició AND o amb salts de línia per la condició OR."
notifyAntenna: "Notifica'm les publicacions noves" notifyAntenna: "Notifica'm les publicacions noves"
withFileAntenna: "Només les publicacions amb fitxers" withFileAntenna: "Només les publicacions amb fitxers"
antennaUsersDescription: "Llistar un nom d'usuari per línia"
notesAndReplies: "Amb respostes" notesAndReplies: "Amb respostes"
silence: "Silencia" silence: "Silencia"
silenceConfirm: "Segur que vols silenciar aquest usuari?" silenceConfirm: "Segur que vols silenciar aquest usuari?"
@ -369,6 +381,9 @@ user: "Usuaris"
global: "Global" global: "Global"
searchByGoogle: "Cercar" searchByGoogle: "Cercar"
file: "Fitxers" file: "Fitxers"
_role:
_options:
antennaMax: "Nombre màxim d'antenes"
_email: _email:
_follow: _follow:
title: "t'ha seguit" title: "t'ha seguit"
@ -385,6 +400,7 @@ _sfx:
antenna: "Antenes" antenna: "Antenes"
_2fa: _2fa:
step2Url: "També pots inserir aquest enllaç i utilitzes una aplicació d'escriptori:" step2Url: "També pots inserir aquest enllaç i utilitzes una aplicació d'escriptori:"
renewTOTPCancel: "No, gràcies"
_antennaSources: _antennaSources:
all: "Totes les publicacions" all: "Totes les publicacions"
homeTimeline: "Publicacions dels usuaris seguits" homeTimeline: "Publicacions dels usuaris seguits"
@ -430,6 +446,7 @@ _pages:
_notification: _notification:
youRenoted: "Impulsat per {name}" youRenoted: "Impulsat per {name}"
youWereFollowed: "t'ha seguit" youWereFollowed: "t'ha seguit"
unreadAntennaNote: "Antena {name}"
_types: _types:
all: "Tots" all: "Tots"
follow: "Seguint" follow: "Seguint"

File diff suppressed because it is too large Load diff

View file

@ -156,6 +156,7 @@ addEmoji: "Emoji hinzufügen"
settingGuide: "Empfohlene Einstellung" settingGuide: "Empfohlene Einstellung"
cacheRemoteFiles: "Dateien von fremden Instanzen im Cache speichern" cacheRemoteFiles: "Dateien von fremden Instanzen im Cache speichern"
cacheRemoteFilesDescription: "Ist diese Einstellung deaktiviert, so werden Dateien fremder Instanzen direkt von dort geladen. Hierdurch wird Speicherplatz auf diesem Server gespart, aber durch fehlende Generierung von Vorschaubildern mehr Bandbreite verwendet." cacheRemoteFilesDescription: "Ist diese Einstellung deaktiviert, so werden Dateien fremder Instanzen direkt von dort geladen. Hierdurch wird Speicherplatz auf diesem Server gespart, aber durch fehlende Generierung von Vorschaubildern mehr Bandbreite verwendet."
youCanCleanRemoteFilesCache: "Klicke auf den 🗑️-Knopf der Dateiverwaltungsansicht, um den Cache zu leeren."
cacheRemoteSensitiveFiles: "Sensitive Dateien von fremden Instanzen im Cache speichern" cacheRemoteSensitiveFiles: "Sensitive Dateien von fremden Instanzen im Cache speichern"
cacheRemoteSensitiveFilesDescription: "Ist diese Einstellung deaktiviert, so werden sensitive Dateien fremder Instanzen direkt von dort ohne Zwischenspeicherung geladen." cacheRemoteSensitiveFilesDescription: "Ist diese Einstellung deaktiviert, so werden sensitive Dateien fremder Instanzen direkt von dort ohne Zwischenspeicherung geladen."
flagAsBot: "Als Bot markieren" flagAsBot: "Als Bot markieren"
@ -230,7 +231,7 @@ noJobs: "Keine Jobs vorhanden"
federating: "Wird föderiert" federating: "Wird föderiert"
blocked: "Blockiert" blocked: "Blockiert"
suspended: "Gesperrt" suspended: "Gesperrt"
all: "Alles" all: "Alle"
subscribing: "Wird abonniert" subscribing: "Wird abonniert"
publishing: "Wird veröffentlicht" publishing: "Wird veröffentlicht"
notResponding: "Antwortet nicht" notResponding: "Antwortet nicht"
@ -540,7 +541,7 @@ chooseEmoji: "Emoji auswählen"
unableToProcess: "Der Vorgang konnte nicht abgeschlossen werden" unableToProcess: "Der Vorgang konnte nicht abgeschlossen werden"
recentUsed: "Vor kurzem verwendet" recentUsed: "Vor kurzem verwendet"
install: "Installieren" install: "Installieren"
uninstall: "Uninstallieren" uninstall: "Deinstallieren"
installedApps: "Authorisierte Anwendungen" installedApps: "Authorisierte Anwendungen"
nothing: "Hier gibt es nichts zu sehen" nothing: "Hier gibt es nichts zu sehen"
installedDate: "Authorisiert am" installedDate: "Authorisiert am"
@ -680,6 +681,7 @@ createNewClip: "Neuen Clip erstellen"
unclip: "Aus Clip entfernen" unclip: "Aus Clip entfernen"
confirmToUnclipAlreadyClippedNote: "Diese Notiz ist bereits im \"{name}\" Clip enthalten. Möchtest du sie aus diesem Clip entfernen?" confirmToUnclipAlreadyClippedNote: "Diese Notiz ist bereits im \"{name}\" Clip enthalten. Möchtest du sie aus diesem Clip entfernen?"
public: "Öffentlich" public: "Öffentlich"
private: "Privat"
i18nInfo: "Misskey wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. Auf {link} kannst du mithelfen." i18nInfo: "Misskey wird durch freiwillige Helfer in viele verschiedene Sprachen übersetzt. Auf {link} kannst du mithelfen."
manageAccessTokens: "Zugriffstokens verwalten" manageAccessTokens: "Zugriffstokens verwalten"
accountInfo: "Benutzerkonto-Informationen" accountInfo: "Benutzerkonto-Informationen"
@ -1094,6 +1096,21 @@ expired: "Abgelaufen"
doYouAgree: "Zustimmen?" doYouAgree: "Zustimmen?"
beSureToReadThisAsItIsImportant: "Lies bitte diese wichtige Informationen." beSureToReadThisAsItIsImportant: "Lies bitte diese wichtige Informationen."
iHaveReadXCarefullyAndAgree: "Ich habe den Text \"{x}\" gelesen und stimme zu." iHaveReadXCarefullyAndAgree: "Ich habe den Text \"{x}\" gelesen und stimme zu."
dialog: "Dialogfeld"
icon: "Symbol"
forYou: "Für dich"
currentAnnouncements: "Aktuelle Ankündigungen"
pastAnnouncements: "Alte Ankündigungen"
youHaveUnreadAnnouncements: "Es gibt neue Ankündigungen."
_announcement:
forExistingUsers: "Nur für existierende Nutzer"
forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
needConfirmationToRead: "Separate Lesebestätigung erfordern"
needConfirmationToReadDescription: "Ist dies aktiviert, so wird beim Markieren dieser Ankündigung als gelesen ein separates Bestätigungsfenster angezeigt. Auch wird sie von der \"Alle als gelesen markieren\"-Funktion ausgenommen."
end: "Ankündigung archivieren"
tooManyActiveAnnouncementDescription: "Zu viele aktive Ankündigungen können die Benutzerfreundlichkeit verschlechtern. Es wird empfohlen, veraltete Ankündigungen zu archivieren."
readConfirmTitle: "Als gelesen markieren?"
readConfirmText: "Dies markiert den Inhalt von \"{title}\" als gelesen."
_initialAccountSetting: _initialAccountSetting:
accountCreated: "Dein Konto wurde erfolgreich erstellt!" accountCreated: "Dein Konto wurde erfolgreich erstellt!"
letsStartAccountSetup: "Lass uns nun dein Konto einrichten." letsStartAccountSetup: "Lass uns nun dein Konto einrichten."
@ -1730,6 +1747,10 @@ _permissions:
"write:gallery": "Deine Galerie bearbeiten" "write:gallery": "Deine Galerie bearbeiten"
"read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge lesen" "read:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge lesen"
"write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge bearbeiten" "write:gallery-likes": "Liste deiner mit \"Gefällt mir\" markierten Galerie-Beiträge bearbeiten"
"read:flash": "Deine Plays lesen"
"write:flash": "Deine Plays bearbeiten oder löschen"
"read:flash-likes": "Liste der Plays, die mir gefallen, lesen"
"write:flash-likes": "Liste der Plays, die mir gefallen, bearbeiten"
_auth: _auth:
shareAccessTitle: "Verteilung von App-Berechtigungen" shareAccessTitle: "Verteilung von App-Berechtigungen"
shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?" shareAccess: "Möchtest du „{name}“ authorisieren, auf dieses Benutzerkonto zugreifen zu können?"

View file

@ -287,6 +287,7 @@ searchByGoogle: "Αναζήτηση"
file: "Αρχεία" file: "Αρχεία"
recommended: "Προτεινόμενα" recommended: "Προτεινόμενα"
cannotUploadBecauseNoFreeSpace: "Το ανέβασμα απέτυχε λόγω ανεπαρκούς Αποθηκευτικού Χώρου" cannotUploadBecauseNoFreeSpace: "Το ανέβασμα απέτυχε λόγω ανεπαρκούς Αποθηκευτικού Χώρου"
icon: "Εικονίδιο"
_email: _email:
_follow: _follow:
title: "Έχετε ένα νέο ακόλουθο" title: "Έχετε ένα νέο ακόλουθο"

View file

@ -74,7 +74,7 @@ import: "Import"
export: "Export" export: "Export"
files: "Files" files: "Files"
download: "Download" download: "Download"
driveFileDeleteConfirm: "Are you sure you want to delete \"{name}\"? It will also vanish from all contents that use it." driveFileDeleteConfirm: "Do you want to remove the file \"{name}\"? Some content using this file will also be removed."
unfollowConfirm: "Are you sure you want to unfollow {name}?" unfollowConfirm: "Are you sure you want to unfollow {name}?"
exportRequested: "You've requested an export. This may take a while. It will be added to your Drive once completed." exportRequested: "You've requested an export. This may take a while. It will be added to your Drive once completed."
importRequested: "You've requested an import. This may take a while." importRequested: "You've requested an import. This may take a while."
@ -156,6 +156,7 @@ addEmoji: "Add an emoji"
settingGuide: "Recommended settings" settingGuide: "Recommended settings"
cacheRemoteFiles: "Cache remote files" cacheRemoteFiles: "Cache remote files"
cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded directly from the remote instance. Disabling this will decrease storage usage, but increase traffic, as thumbnails will not be generated." cacheRemoteFilesDescription: "When this setting is disabled, remote files are loaded directly from the remote instance. Disabling this will decrease storage usage, but increase traffic, as thumbnails will not be generated."
youCanCleanRemoteFilesCache: "You can clear the cache by clicking the 🗑️ button in the file management view."
cacheRemoteSensitiveFiles: "Cache sensitive remote files" cacheRemoteSensitiveFiles: "Cache sensitive remote files"
cacheRemoteSensitiveFilesDescription: "When this setting is disabled, sensitive remote files are loaded directly from the remote instance without caching." cacheRemoteSensitiveFilesDescription: "When this setting is disabled, sensitive remote files are loaded directly from the remote instance without caching."
flagAsBot: "Mark this account as a bot" flagAsBot: "Mark this account as a bot"
@ -680,6 +681,7 @@ createNewClip: "Create new clip"
unclip: "Unclip" unclip: "Unclip"
confirmToUnclipAlreadyClippedNote: "This note is already part of the \"{name}\" clip. Do you want to remove it from this clip instead?" confirmToUnclipAlreadyClippedNote: "This note is already part of the \"{name}\" clip. Do you want to remove it from this clip instead?"
public: "Public" public: "Public"
private: "Private"
i18nInfo: "Misskey is being translated into various languages by volunteers. You can help at {link}." i18nInfo: "Misskey is being translated into various languages by volunteers. You can help at {link}."
manageAccessTokens: "Manage access tokens" manageAccessTokens: "Manage access tokens"
accountInfo: "Account Info" accountInfo: "Account Info"
@ -1094,6 +1096,21 @@ expired: "Expired"
doYouAgree: "Agree?" doYouAgree: "Agree?"
beSureToReadThisAsItIsImportant: "Please read this important information." beSureToReadThisAsItIsImportant: "Please read this important information."
iHaveReadXCarefullyAndAgree: "I have read the text \"{x}\" and agree." iHaveReadXCarefullyAndAgree: "I have read the text \"{x}\" and agree."
dialog: "Dialog"
icon: "Icon"
forYou: "For you"
currentAnnouncements: "Current announcements"
pastAnnouncements: "Past announcements"
youHaveUnreadAnnouncements: "There are unread announcements."
_announcement:
forExistingUsers: "Existing users only"
forExistingUsersDescription: "This announcement will only be shown to users existing at the point of publishment if enabled. If disabled, those newly signing up after it has been posted will also see it."
needConfirmationToRead: "Require separate read confirmation"
needConfirmationToReadDescription: "A separate prompt to confirm marking this announcement as read will be displayed if enabled. This announcement will also be excluded from any \"Mark all as read\" functionality."
end: "Archive announcement"
tooManyActiveAnnouncementDescription: "Having too many active announcements may worsen the user experience. Please consider archiving announcements that have become obsolete."
readConfirmTitle: "Mark as read?"
readConfirmText: "This will mark the contents of \"{title}\" as read."
_initialAccountSetting: _initialAccountSetting:
accountCreated: "Your account was successfully created!" accountCreated: "Your account was successfully created!"
letsStartAccountSetup: "For starters, let's set up your profile." letsStartAccountSetup: "For starters, let's set up your profile."
@ -1718,10 +1735,10 @@ _permissions:
"read:reactions": "View your reactions" "read:reactions": "View your reactions"
"write:reactions": "Edit your reactions" "write:reactions": "Edit your reactions"
"write:votes": "Vote on a poll" "write:votes": "Vote on a poll"
"read:pages": "View your pages" "read:pages": "View your Pages"
"write:pages": "Edit or delete your pages" "write:pages": "Edit or delete your Pages"
"read:page-likes": "View your likes on pages" "read:page-likes": "View list of liked Pages"
"write:page-likes": "Edit your likes on pages" "write:page-likes": "Edit list of liked Pages"
"read:user-groups": "View your user groups" "read:user-groups": "View your user groups"
"write:user-groups": "Edit or delete your user groups" "write:user-groups": "Edit or delete your user groups"
"read:channels": "View your channels" "read:channels": "View your channels"
@ -1730,6 +1747,10 @@ _permissions:
"write:gallery": "Edit your gallery" "write:gallery": "Edit your gallery"
"read:gallery-likes": "View your list of liked gallery posts" "read:gallery-likes": "View your list of liked gallery posts"
"write:gallery-likes": "Edit your list of liked gallery posts" "write:gallery-likes": "Edit your list of liked gallery posts"
"read:flash": "View Play"
"write:flash": "Edit Plays"
"read:flash-likes": "View list of liked Plays"
"write:flash-likes": "Edit list of liked Plays"
_auth: _auth:
shareAccessTitle: "Granting application permissions" shareAccessTitle: "Granting application permissions"
shareAccess: "Would you like to authorize \"{name}\" to access this account?" shareAccess: "Would you like to authorize \"{name}\" to access this account?"

View file

@ -8,10 +8,10 @@ search: "Buscar"
notifications: "Notificaciones" notifications: "Notificaciones"
username: "Nombre de usuario" username: "Nombre de usuario"
password: "Contraseña" password: "Contraseña"
forgotPassword: "Olvidé mi Contraseña" forgotPassword: "Olvidé mi contraseña"
fetchingAsApObject: "Buscando en el fediverso" fetchingAsApObject: "Buscando en el fediverso"
ok: "OK" ok: "OK"
gotIt: "¡Lo tengo!" gotIt: "Entendido"
cancel: "Cancelar" cancel: "Cancelar"
noThankYou: "No gracias" noThankYou: "No gracias"
enterUsername: "Introduce el nombre de usuario" enterUsername: "Introduce el nombre de usuario"
@ -21,7 +21,7 @@ noNotifications: "No hay notificaciones"
instance: "Instancia" instance: "Instancia"
settings: "Configuración" settings: "Configuración"
notificationSettings: "Configurar las notificaciones" notificationSettings: "Configurar las notificaciones"
basicSettings: "Configuración Básica" basicSettings: "Configuración básica"
otherSettings: "Configuración avanzada" otherSettings: "Configuración avanzada"
openInWindow: "Abrir en una ventana" openInWindow: "Abrir en una ventana"
profile: "Perfil" profile: "Perfil"
@ -156,6 +156,7 @@ addEmoji: "Agregar emoji"
settingGuide: "Configuración sugerida" settingGuide: "Configuración sugerida"
cacheRemoteFiles: "Mantener en cache los archivos remotos" cacheRemoteFiles: "Mantener en cache los archivos remotos"
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 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."
youCanCleanRemoteFilesCache: "Puedes vaciar la caché pulsando en el botón 🗑️ en el administrador de archivos."
cacheRemoteSensitiveFiles: "Cachear archivos remotos sensibles" cacheRemoteSensitiveFiles: "Cachear archivos remotos sensibles"
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 son cargador directamente de la instancia origen sin ser cacheados."
flagAsBot: "Esta cuenta es un bot" flagAsBot: "Esta cuenta es un bot"
@ -680,6 +681,7 @@ createNewClip: "Crear clip nuevo"
unclip: "Quitar clip" unclip: "Quitar clip"
confirmToUnclipAlreadyClippedNote: "Esta nota ya está incluida en el clip \"{name}\". ¿Quiere quitar la nota del clip?" confirmToUnclipAlreadyClippedNote: "Esta nota ya está incluida en el clip \"{name}\". ¿Quiere quitar la nota del clip?"
public: "Público" public: "Público"
private: "Privado"
i18nInfo: "Misskey está siendo traducido a varios idiomas gracias a voluntarios. Se puede colaborar traduciendo en {link}" i18nInfo: "Misskey está siendo traducido a varios idiomas gracias a voluntarios. Se puede colaborar traduciendo en {link}"
manageAccessTokens: "Administrar tokens de acceso" manageAccessTokens: "Administrar tokens de acceso"
accountInfo: "Información de la Cuenta" accountInfo: "Información de la Cuenta"
@ -1094,6 +1096,21 @@ expired: "Caducada"
doYouAgree: "¿Está de acuerdo?" doYouAgree: "¿Está de acuerdo?"
beSureToReadThisAsItIsImportant: "Por favor lea esto que es importante" beSureToReadThisAsItIsImportant: "Por favor lea esto que es importante"
iHaveReadXCarefullyAndAgree: "He leído el texto {x} y estoy de acuerdo" iHaveReadXCarefullyAndAgree: "He leído el texto {x} y estoy de acuerdo"
dialog: "Diálogo"
icon: "Avatar"
forYou: "Para ti"
currentAnnouncements: "Anuncios actuales"
pastAnnouncements: "Anuncios anteriores"
youHaveUnreadAnnouncements: "Hay anuncios sin leer"
_announcement:
forExistingUsers: "Solo para usuarios registrados"
forExistingUsersDescription: "Este anuncio solo se mostrará a aquellos usuarios registrados en el momento de su publicación. Si se deshabilita esta opción, aquellos usuarios que se registren tras su publicación también lo verán."
needConfirmationToRead: "Requerir confirmación de lectura aparte"
needConfirmationToReadDescription: "Si se habilita esta opción, se pedirá una confirmación de lectura aparte. Además, este anuncio será excluido de cualquier funcionalidad de \"Marcar todos como leídos\"."
end: "Anuncios archivados"
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?"
readConfirmText: "Esto marcará el contenido de \"{title}\" como leído."
_initialAccountSetting: _initialAccountSetting:
accountCreated: "¡La cuenta ha sido creada!" accountCreated: "¡La cuenta ha sido creada!"
letsStartAccountSetup: "Para empezar, creemos tu perfil." letsStartAccountSetup: "Para empezar, creemos tu perfil."

View file

@ -58,7 +58,7 @@ copyNoteId: "Copier l'identifiant de la note"
copyFileId: "Copier l'identifiant du fichier" copyFileId: "Copier l'identifiant du fichier"
copyFolderId: "Copier l'identifiant du dossier" copyFolderId: "Copier l'identifiant du dossier"
copyProfileUrl: "Copier l'URL du profil" copyProfileUrl: "Copier l'URL du profil"
searchUser: "Chercher un utilisateur" searchUser: "Chercher un·e utilisateur·rice"
reply: "Répondre" reply: "Répondre"
loadMore: "Afficher plus …" loadMore: "Afficher plus …"
showMore: "Voir plus" showMore: "Voir plus"
@ -76,14 +76,14 @@ files: "Fichiers"
download: "Télécharger" download: "Télécharger"
driveFileDeleteConfirm: "Êtes-vous sûr de vouloir supprimer le fichier \"{name}\" ? Les notes liées à ce fichier seront aussi supprimées." driveFileDeleteConfirm: "Êtes-vous sûr de vouloir supprimer le fichier \"{name}\" ? Les notes liées à ce fichier seront aussi supprimées."
unfollowConfirm: "Désirez-vous vous désabonner de {name} ?" unfollowConfirm: "Désirez-vous vous désabonner de {name} ?"
exportRequested: "Vous avez demandé une exportation. Lopération pourrait prendre un peu de temps. Une terminée, le fichier résultant sera ajouté au Drive." exportRequested: "Vous avez demandé une exportation. Lopération pourrait prendre un peu de temps. Une fois terminée, le fichier sera ajouté au Drive."
importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps." importRequested: "Vous avez initié un import. Cela pourrait prendre un peu de temps."
lists: "Listes" lists: "Listes"
noLists: "Vous navez aucune liste" noLists: "Vous navez aucune liste"
note: "Note" note: "Note"
notes: "Notes" notes: "Notes"
following: "Abonnements" following: "Abonnements"
followers: "Abonnés" followers: "Abonné·e·s"
followsYou: "Vous suit" followsYou: "Vous suit"
createList: "Créer une liste" createList: "Créer une liste"
manageLists: "Gérer les listes" manageLists: "Gérer les listes"
@ -138,6 +138,7 @@ unblockConfirm: "Êtes-vous sûr·e de vouloir débloquer ce compte ?"
suspendConfirm: "Êtes-vous sûr·e de vouloir suspendre ce compte ?" suspendConfirm: "Êtes-vous sûr·e de vouloir suspendre ce compte ?"
unsuspendConfirm: "Êtes-vous sûr·e de vouloir annuler la suspension de ce compte ?" unsuspendConfirm: "Êtes-vous sûr·e de vouloir annuler la suspension de ce compte ?"
selectList: "Sélectionner une liste" selectList: "Sélectionner une liste"
editList: "Modifier la liste"
selectChannel: "Sélectionner un canal" selectChannel: "Sélectionner un canal"
selectAntenna: "Sélectionner une antenne" selectAntenna: "Sélectionner une antenne"
editAntenna: "Modifier l'antenne" editAntenna: "Modifier l'antenne"
@ -271,6 +272,7 @@ startMessaging: "Commencer à discuter"
nUsersRead: "Lu par {n} personnes" nUsersRead: "Lu par {n} personnes"
agreeTo: "Jaccepte {0}" agreeTo: "Jaccepte {0}"
agree: "Accepter" agree: "Accepter"
basicNotesBeforeCreateAccount: "Notes importantes"
termsOfService: "Conditions d'utilisation" termsOfService: "Conditions d'utilisation"
start: "Commencer" start: "Commencer"
home: "Principal" home: "Principal"
@ -665,6 +667,7 @@ createNew: "Créer nouveau"
optional: "Facultatif" optional: "Facultatif"
createNewClip: "Créer un nouveau clip" createNewClip: "Créer un nouveau clip"
public: "Public" public: "Public"
private: "Privé"
i18nInfo: "Misskey est traduit dans différentes langues par des bénévoles. Vous pouvez contribuer à {link}." i18nInfo: "Misskey est traduit dans différentes langues par des bénévoles. Vous pouvez contribuer à {link}."
manageAccessTokens: "Gérer les jetons d'accès" manageAccessTokens: "Gérer les jetons d'accès"
accountInfo: " Informations du compte " accountInfo: " Informations du compte "
@ -827,6 +830,8 @@ breakFollow: "Ne plus suivre"
breakFollowConfirm: "Êtes-vous sûr de vouloir vous désabonner?" breakFollowConfirm: "Êtes-vous sûr de vouloir vous désabonner?"
itsOn: "Activé" itsOn: "Activé"
itsOff: "Désactivé" itsOff: "Désactivé"
on: "Activé"
off: "Désactivé"
emailRequiredForSignup: "Une adresse e-mail est nécessaire pour créer un compte" emailRequiredForSignup: "Une adresse e-mail est nécessaire pour créer un compte"
unread: "Non lu" unread: "Non lu"
filter: "Filtre" filter: "Filtre"
@ -942,6 +947,7 @@ preset: "Préréglage"
selectFromPresets: "Sélectionner à partir des préréglages" selectFromPresets: "Sélectionner à partir des préréglages"
thisPostMayBeAnnoying: "Cette note peut gêner d'autres personnes." thisPostMayBeAnnoying: "Cette note peut gêner d'autres personnes."
thisPostMayBeAnnoyingCancel: "Annuler" thisPostMayBeAnnoyingCancel: "Annuler"
internalServerError: "Erreur interne du serveur"
license: "Licence" license: "Licence"
video: "Vidéo" video: "Vidéo"
videos: "Vidéos" videos: "Vidéos"
@ -958,6 +964,9 @@ vertical: "Vertical"
horizontal: "Latéral" horizontal: "Latéral"
serverRules: "Règles du serveur" serverRules: "Règles du serveur"
youFollowing: "Abonné·e" youFollowing: "Abonné·e"
goToMisskey: "Retour vers Misskey"
expirationDate: "Date dexpiration"
icon: "Avatar"
_achievements: _achievements:
_types: _types:
_notes1: _notes1:

View file

@ -133,12 +133,12 @@ renoteMute: "Matikan renote"
renoteUnmute: "Batal mematikan renote" renoteUnmute: "Batal mematikan renote"
block: "Blokir" block: "Blokir"
unblock: "Buka blokir" unblock: "Buka blokir"
suspend: "Bekukan" suspend: "Tangguhkan"
unsuspend: "Buka pembekuan" unsuspend: "Batalkan penangguhan"
blockConfirm: "Apakah kamu yakin ingin memblokir akun ini?" blockConfirm: "Apakah kamu yakin ingin memblokir akun ini?"
unblockConfirm: "Apakah kamu yakin ingin membuka blokir akun ini?" unblockConfirm: "Apakah kamu yakin ingin membuka blokir akun ini?"
suspendConfirm: "Apakah kamu yakin ingin membekukan akun ini?" suspendConfirm: "Apakah kamu yakin ingin menangguhkan akun ini?"
unsuspendConfirm: "Apakah kamu yakin ingin membuka pembekuan akun ini?" unsuspendConfirm: "Apakah kamu yakin ingin membatalkan penangguhan akun ini?"
selectList: "Pilih daftar" selectList: "Pilih daftar"
editList: "Sunting daftar" editList: "Sunting daftar"
selectChannel: "Pilih kanal" selectChannel: "Pilih kanal"
@ -156,6 +156,8 @@ addEmoji: "Tambahkan emoji"
settingGuide: "Pengaturan rekomendasi" settingGuide: "Pengaturan rekomendasi"
cacheRemoteFiles: "Tembolokkan berkas dari instansi luar" cacheRemoteFiles: "Tembolokkan berkas dari instansi luar"
cacheRemoteFilesDescription: "Ketika pengaturan ini dinonaktifkan, berkas dari instansi luar akan dimuat langsung. Menonaktifkan ini akan mengurangi penggunaan penyimpanan peladen, namun dapat menyebabkan peningkatan lalu lintas bandwidth, karena keluku tidak dihasilkan." cacheRemoteFilesDescription: "Ketika pengaturan ini dinonaktifkan, berkas dari instansi luar akan dimuat langsung. Menonaktifkan ini akan mengurangi penggunaan penyimpanan peladen, namun dapat menyebabkan peningkatan lalu lintas bandwidth, karena keluku tidak dihasilkan."
cacheRemoteSensitiveFiles: "Tembolokkan berkas dari instansi luar"
cacheRemoteSensitiveFilesDescription: "Menonaktifkan pengaturan ini menyebabkan berkas sensitif dari instansi luar ditautkan secara langsung, bukan ditembolok."
flagAsBot: "Atur akun ini sebagai Bot" flagAsBot: "Atur akun ini sebagai Bot"
flagAsBotDescription: "Jika akun ini dikendalikan oleh program, tetapkanlah opsi ini. Jika diaktifkan, ini akan berfungsi sebagai tanda bagi pengembang lain untuk mencegah interaksi berantai dengan bot lain dan menyesuaikan sistem internal Misskey untuk memperlakukan akun ini sebagai bot." flagAsBotDescription: "Jika akun ini dikendalikan oleh program, tetapkanlah opsi ini. Jika diaktifkan, ini akan berfungsi sebagai tanda bagi pengembang lain untuk mencegah interaksi berantai dengan bot lain dan menyesuaikan sistem internal Misskey untuk memperlakukan akun ini sebagai bot."
flagAsCat: "Atur akun ini sebagai kucing" flagAsCat: "Atur akun ini sebagai kucing"
@ -227,7 +229,7 @@ noCustomEmojis: "Tidak ada emoji kustom"
noJobs: "Tidak ada kerja" noJobs: "Tidak ada kerja"
federating: "memfederasi" federating: "memfederasi"
blocked: "Diblokir" blocked: "Diblokir"
suspended: "Diberhentikan" suspended: "Ditangguhkan"
all: "Semua" all: "Semua"
subscribing: "Berlangganan" subscribing: "Berlangganan"
publishing: "Sedang menyiarkan langsung" publishing: "Sedang menyiarkan langsung"
@ -391,10 +393,10 @@ withReplies: "Termasuk balasan"
connectedTo: "Akun berikut terhubung" connectedTo: "Akun berikut terhubung"
notesAndReplies: "Catatan dan balasan" notesAndReplies: "Catatan dan balasan"
withFiles: "Media" withFiles: "Media"
silence: "Bungkam" silence: "Senyapkan"
silenceConfirm: "Apakah kamu yakin ingin membungkam pengguna ini?" silenceConfirm: "Apakah kamu yakin ingin menyenyapkan pengguna ini?"
unsilence: "Hapus bungkam" unsilence: "Batalkan senyap"
unsilenceConfirm: "Apakah kamu ingin untuk batal membungkam pengguna ini?" unsilenceConfirm: "Apakah kamu ingin untuk batal menyenyapkan pengguna ini?"
popularUsers: "Pengguna populer" popularUsers: "Pengguna populer"
recentlyUpdatedUsers: "Pengguna dengan aktivitas terkini" recentlyUpdatedUsers: "Pengguna dengan aktivitas terkini"
recentlyRegisteredUsers: "Pengguna baru saja bergabung" recentlyRegisteredUsers: "Pengguna baru saja bergabung"
@ -557,10 +559,10 @@ deleteAllFiles: "Hapus semua berkas"
deleteAllFilesConfirm: "Apakah kamu yakin ingin menghapus semua berkas?" deleteAllFilesConfirm: "Apakah kamu yakin ingin menghapus semua berkas?"
removeAllFollowing: "Batalkan mengikuti semua pengguna" removeAllFollowing: "Batalkan mengikuti semua pengguna"
removeAllFollowingDescription: "Batal mengikuti semua akun dari {host}. Mohon jalankan ini ketika instansi sudah tidak ada lagi." removeAllFollowingDescription: "Batal mengikuti semua akun dari {host}. Mohon jalankan ini ketika instansi sudah tidak ada lagi."
userSuspended: "Pengguna ini telah dibekukan." userSuspended: "Pengguna ini telah ditangguhkan"
userSilenced: "Pengguna ini telah dibungkam." userSilenced: "Pengguna ini telah disenyapkan."
yourAccountSuspendedTitle: "Akun ini dibekukan" yourAccountSuspendedTitle: "Akun ini ditangguhkan"
yourAccountSuspendedDescription: "Akun ini dibekukan karena melanggar ketentuan penggunaan layanan peladen atau semacamnya. Hubungi admin apabila ingin tahu alasan lebih lanjut. Mohon untuk tidak membuat akun baru." yourAccountSuspendedDescription: "Akun ini ditangguhkan karena melanggar ketentuan penggunaan layanan peladen atau semacamnya. Hubungi admin apabila ingin mengetahui alasan lebih lanjut. Mohon untuk tidak membuat akun baru."
tokenRevoked: "Token tidak valid" tokenRevoked: "Token tidak valid"
tokenRevokedDescription: "Token ini telah kedaluwarsa. Mohon masuk lagi." tokenRevokedDescription: "Token ini telah kedaluwarsa. Mohon masuk lagi."
accountDeleted: "Akun telah dihapus" accountDeleted: "Akun telah dihapus"
@ -678,6 +680,7 @@ createNewClip: "Buat klip baru"
unclip: "Batalkan klip" unclip: "Batalkan klip"
confirmToUnclipAlreadyClippedNote: "Catatan ini sudah disertakan di klip \"{name}\". Yakin ingin membatalkan catatan dari klip ini?" confirmToUnclipAlreadyClippedNote: "Catatan ini sudah disertakan di klip \"{name}\". Yakin ingin membatalkan catatan dari klip ini?"
public: "Publik" public: "Publik"
private: "Tersembunyi"
i18nInfo: "Misskey diterjemahkan ke dalam banyak bahasa oleh sukarelawan. Kamu juga dapat ikut membantu menerjemahkannya di {link}." i18nInfo: "Misskey diterjemahkan ke dalam banyak bahasa oleh sukarelawan. Kamu juga dapat ikut membantu menerjemahkannya di {link}."
manageAccessTokens: "Kelola token akses" manageAccessTokens: "Kelola token akses"
accountInfo: "Informasi akun" accountInfo: "Informasi akun"
@ -987,7 +990,7 @@ internalServerErrorDescription: "Peladen sedang mengalami galat tak terduga"
copyErrorInfo: "Salin detil galat" copyErrorInfo: "Salin detil galat"
joinThisServer: "Gabung peladen ini" joinThisServer: "Gabung peladen ini"
exploreOtherServers: "Cari peladen lain" exploreOtherServers: "Cari peladen lain"
letsLookAtTimeline: "LIhat timeline" letsLookAtTimeline: "LIhat lini masa"
disableFederationConfirm: "Matikan federasi?" disableFederationConfirm: "Matikan federasi?"
disableFederationConfirmWarn: "Mematikan federasi tidak membuat kiriman menjadi privat. Umumnya, mematikan federasi tidak diperlukan." disableFederationConfirmWarn: "Mematikan federasi tidak membuat kiriman menjadi privat. Umumnya, mematikan federasi tidak diperlukan."
disableFederationOk: "Matikan federasi" disableFederationOk: "Matikan federasi"
@ -1073,9 +1076,26 @@ enableServerMachineStats: "Tampilkan informasi mesin peladen menjadi publik"
enableIdenticonGeneration: "Nyalakan pembuatan Identicon per pengguna" enableIdenticonGeneration: "Nyalakan pembuatan Identicon per pengguna"
turnOffToImprovePerformance: "Matikan untuk tingkatkan performa." turnOffToImprovePerformance: "Matikan untuk tingkatkan performa."
createInviteCode: "Buat kode undangan" createInviteCode: "Buat kode undangan"
createWithOptions: "Buat dengan opsi"
createCount: "Jumlah undangan"
inviteCodeCreated: "Kode undangan dibuat" inviteCodeCreated: "Kode undangan dibuat"
inviteLimitExceeded: "Kamu telah mencapai jumlah maksimum kode undangan yang dapat dibuat." inviteLimitExceeded: "Kamu telah mencapai jumlah maksimum kode undangan yang dapat dibuat."
createLimitRemaining: "Kode undangan yang dapat dibuat: tersisa {limit}"
inviteLimitResetCycle: "Kamu dapat membuat hingga {limit} kode undangan dalam {time}."
expirationDate: "Tanggal kedaluwarsa" expirationDate: "Tanggal kedaluwarsa"
noExpirationDate: "tidak ada tanggal kedaluwarsa"
inviteCodeUsedAt: "Kode undangan digunakan pada"
registeredUserUsingInviteCode: "Undangan digunakan oleh"
waitingForMailAuth: "Menunggu verifikasi surel"
inviteCodeCreator: "Undangan dibuat oleh"
usedAt: "Digunakan pada"
unused: "Tidak digunakan"
used: "Digunakan"
expired: "Kedaluwarsa"
doYouAgree: "Apa kamu setuju?"
beSureToReadThisAsItIsImportant: "Mohon baca informasi penting berikut."
iHaveReadXCarefullyAndAgree: "Saya telah membaca \"{x}\" dan menyetujui."
icon: "Avatar"
_initialAccountSetting: _initialAccountSetting:
accountCreated: "Akun kamu telah sukses dibuat!" accountCreated: "Akun kamu telah sukses dibuat!"
letsStartAccountSetup: "Untuk pemula, ayo atur profilmu dulu." letsStartAccountSetup: "Untuk pemula, ayo atur profilmu dulu."
@ -1386,6 +1406,9 @@ _role:
ltlAvailable: "Dapat melihat lini masa lokal" ltlAvailable: "Dapat melihat lini masa lokal"
canPublicNote: "Dapat mengirim catatan publik" canPublicNote: "Dapat mengirim catatan publik"
canInvite: "Dapat membuat kode undangan instansi" canInvite: "Dapat membuat kode undangan instansi"
inviteLimit: "Batas jumlah undangan"
inviteLimitCycle: "Interval Penerbitan Kode Undangan"
inviteExpirationTime: "Interval kedaluwarsa undangan"
canManageCustomEmojis: "Dapat mengelola Emoji kustom" canManageCustomEmojis: "Dapat mengelola Emoji kustom"
driveCapacity: "Kapasitas Drive" driveCapacity: "Kapasitas Drive"
alwaysMarkNsfw: "Selalu tandai berkas sebagai NSFW" alwaysMarkNsfw: "Selalu tandai berkas sebagai NSFW"
@ -1448,6 +1471,7 @@ _ad:
back: "Kembali" back: "Kembali"
reduceFrequencyOfThisAd: "Tampilkan iklan ini lebih sedikit" reduceFrequencyOfThisAd: "Tampilkan iklan ini lebih sedikit"
hide: "Jangan tampilkan" hide: "Jangan tampilkan"
timezoneinfo: "Hari dalam satu minggu ditentukan dari zona waktu peladen."
_forgotPassword: _forgotPassword:
enterEmail: "Masukkan alamat surel yang kamu gunakan pada saat mendaftar. Sebuah tautan untuk mengatur ulang kata sandi kamu akan dikirimkan ke alamat surel tersebut." enterEmail: "Masukkan alamat surel yang kamu gunakan pada saat mendaftar. Sebuah tautan untuk mengatur ulang kata sandi kamu akan dikirimkan ke alamat surel tersebut."
ifNoEmail: "Apabila kamu tidak menggunakan surel pada saat pendaftaran, mohon hubungi admin segera." ifNoEmail: "Apabila kamu tidak menggunakan surel pada saat pendaftaran, mohon hubungi admin segera."
@ -1499,6 +1523,10 @@ _aboutMisskey:
donate: "Donasi ke Misskey" donate: "Donasi ke Misskey"
morePatrons: "Kami sangat mengapresiasi dukungan dari banyak penolong lain yang tidak tercantum disini. Terima kasih! 🥰" morePatrons: "Kami sangat mengapresiasi dukungan dari banyak penolong lain yang tidak tercantum disini. Terima kasih! 🥰"
patrons: "Pendukung" patrons: "Pendukung"
_displayOfSensitiveMedia:
respect: "Sembunyikan media yang ditandai sensitif"
ignore: "Tampilkan media yang ditandai sensitif"
force: "Sembunyikan semua media"
_instanceTicker: _instanceTicker:
none: "Jangan tampilkan" none: "Jangan tampilkan"
remote: "Tampilkan untuk pengguna instansi luar" remote: "Tampilkan untuk pengguna instansi luar"
@ -1973,6 +2001,7 @@ _deck:
introduction: "Buat antarmuka sempurna untukmu dengan menata kolom secara bebas!" introduction: "Buat antarmuka sempurna untukmu dengan menata kolom secara bebas!"
introduction2: "Klik \"+\" pada kanan layar untuk menambahkan kolom baru kapanpun yang kamu mau." introduction2: "Klik \"+\" pada kanan layar untuk menambahkan kolom baru kapanpun yang kamu mau."
widgetsIntroduction: "Mohon pilih \"Sunting gawit\" pada menu kolom dan tambahkan gawit." widgetsIntroduction: "Mohon pilih \"Sunting gawit\" pada menu kolom dan tambahkan gawit."
useSimpleUiForNonRootPages: "Gunakan antarmuka sederhana ke halaman yang dituju"
_columns: _columns:
main: "Utama" main: "Utama"
widgets: "Widget" widgets: "Widget"

5
locales/index.d.ts vendored
View file

@ -684,6 +684,7 @@ export interface Locale {
"unclip": string; "unclip": string;
"confirmToUnclipAlreadyClippedNote": string; "confirmToUnclipAlreadyClippedNote": string;
"public": string; "public": string;
"private": string;
"i18nInfo": string; "i18nInfo": string;
"manageAccessTokens": string; "manageAccessTokens": string;
"accountInfo": string; "accountInfo": string;
@ -1867,6 +1868,10 @@ export interface Locale {
"write:gallery": string; "write:gallery": string;
"read:gallery-likes": string; "read:gallery-likes": string;
"write:gallery-likes": string; "write:gallery-likes": string;
"read:flash": string;
"write:flash": string;
"read:flash-likes": string;
"write:flash-likes": string;
}; };
"_auth": { "_auth": {
"shareAccessTitle": string; "shareAccessTitle": string;

View file

@ -21,7 +21,7 @@ noNotifications: "Nessuna notifica"
instance: "Istanza" instance: "Istanza"
settings: "Impostazioni" settings: "Impostazioni"
notificationSettings: "Preferenze di notifica" notificationSettings: "Preferenze di notifica"
basicSettings: "Impostazioni generali" basicSettings: "Impostazioni base"
otherSettings: "Altre impostazioni" otherSettings: "Altre impostazioni"
openInWindow: "Apri in una finestra" openInWindow: "Apri in una finestra"
profile: "Profilo" profile: "Profilo"
@ -65,7 +65,7 @@ showMore: "Espandi"
showLess: "Comprimi" showLess: "Comprimi"
youGotNewFollower: "Ha iniziato a seguirti" youGotNewFollower: "Ha iniziato a seguirti"
receiveFollowRequest: "Hai ricevuto una richiesta di follow" receiveFollowRequest: "Hai ricevuto una richiesta di follow"
followRequestAccepted: "Richiesta di follow accettata" followRequestAccepted: "Ha accettato la tua richiesta di follow"
mention: "Menzioni" mention: "Menzioni"
mentions: "Menzioni" mentions: "Menzioni"
directNotes: "Note dirette" directNotes: "Note dirette"
@ -156,6 +156,7 @@ addEmoji: "Aggiungi un emoji"
settingGuide: "Configurazione suggerita" settingGuide: "Configurazione suggerita"
cacheRemoteFiles: "Memorizza i file remoti nella cache" cacheRemoteFiles: "Memorizza i file remoti nella cache"
cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno linkati direttamente senza essere memorizzati nella cache. Sarà possibile risparmiare spazio di archiviazione sul server, ma il traffico aumenterà in quanto non verranno generate anteprime." cacheRemoteFilesDescription: "Disabilitando questa opzione, i file remoti verranno linkati direttamente senza essere memorizzati nella cache. Sarà possibile risparmiare spazio di archiviazione sul server, ma il traffico aumenterà in quanto non verranno generate anteprime."
youCanCleanRemoteFilesCache: "Puoi svuotare tutta la cache cliccando il bottone 🗑️ nella gestione file"
cacheRemoteSensitiveFiles: "Memorizza nella cache i file sensibili remoti" cacheRemoteSensitiveFiles: "Memorizza nella cache i file sensibili remoti"
cacheRemoteSensitiveFilesDescription: "Disattivando questa opzione, i file sensibili verranno caricati direttamente dall'istanza remota senza essere salvati dal server." cacheRemoteSensitiveFilesDescription: "Disattivando questa opzione, i file sensibili verranno caricati direttamente dall'istanza remota senza essere salvati dal server."
flagAsBot: "Io sono un robot" flagAsBot: "Io sono un robot"
@ -273,10 +274,10 @@ noMoreHistory: "Non c'è più cronologia da visualizzare"
startMessaging: "Nuovo messaggio" startMessaging: "Nuovo messaggio"
nUsersRead: "Letto da {n} persone" nUsersRead: "Letto da {n} persone"
agreeTo: "Sono d'accordo con {0}" agreeTo: "Sono d'accordo con {0}"
agree: "D'accordo" agree: "Accetto"
agreeBelow: "Accetto quanto riportato sotto" agreeBelow: "Accetto quanto riportato sotto"
basicNotesBeforeCreateAccount: "Note importanti" basicNotesBeforeCreateAccount: "Note importanti"
termsOfService: "Informativa Privacy" termsOfService: "Informativa ai sensi degli artt. 13 e 14 del Regolamento UE 2016/679 per la protezione dei dati personali (GDPR)"
start: "Inizia!" start: "Inizia!"
home: "Home" home: "Home"
remoteUserCaution: "Le informazioni potrebbero essere incomplete poiché questo profilo remoto potrebbe non essere completamente federato." remoteUserCaution: "Le informazioni potrebbero essere incomplete poiché questo profilo remoto potrebbe non essere completamente federato."
@ -526,9 +527,9 @@ serverLogs: "Log del server"
deleteAll: "Cancella cronologia" deleteAll: "Cancella cronologia"
showFixedPostForm: "Visualizzare la finestra di pubblicazione in cima alla timeline" showFixedPostForm: "Visualizzare la finestra di pubblicazione in cima alla timeline"
showFixedPostFormInChannel: "Per i canali, mostra il modulo di pubblicazione in cima alla timeline" showFixedPostFormInChannel: "Per i canali, mostra il modulo di pubblicazione in cima alla timeline"
newNoteRecived: "Vedi le nuove note" newNoteRecived: "Nuove note da leggere"
sounds: "Impostazioni suoni" sounds: "Impostazioni suoni"
sound: "Impostazioni suoni" sound: "Suono"
listen: "Ascolta" listen: "Ascolta"
none: "Nessuno" none: "Nessuno"
showInPage: "Visualizza in pagina" showInPage: "Visualizza in pagina"
@ -654,7 +655,7 @@ sample: "Esempio"
abuseReports: "Segnalazioni" abuseReports: "Segnalazioni"
reportAbuse: "Segnalazioni" reportAbuse: "Segnalazioni"
reportAbuseOf: "Segnala {name}" reportAbuseOf: "Segnala {name}"
fillAbuseReportDescription: "Si prega di spiegare il motivo della segnalazione. Se riguarda una nota precisa, si prega di collegare anche l'URL della nota." fillAbuseReportDescription: "Per favore, spiegaci il motivo della segnalazione. Se riguarda una Nota precisa, indica anche l'indirizzo URL."
abuseReported: "La segnalazione è stata inviata. Grazie." abuseReported: "La segnalazione è stata inviata. Grazie."
reporter: "il corrispondente" reporter: "il corrispondente"
reporteeOrigin: "Origine del segnalato" reporteeOrigin: "Origine del segnalato"
@ -680,6 +681,7 @@ createNewClip: "Crea una Clip"
unclip: "Togli Nota dalla Clip" unclip: "Togli Nota dalla Clip"
confirmToUnclipAlreadyClippedNote: "Questa nota è già inclusa in \"{name}\". Si desidera escludere la nota?" confirmToUnclipAlreadyClippedNote: "Questa nota è già inclusa in \"{name}\". Si desidera escludere la nota?"
public: "Pubblica" public: "Pubblica"
private: "Invisibile"
i18nInfo: "Misskey è tradotto in diverse lingue da volontari. Anche tu puoi contribuire su {link}." i18nInfo: "Misskey è tradotto in diverse lingue da volontari. Anche tu puoi contribuire su {link}."
manageAccessTokens: "Gestisci token di accesso" manageAccessTokens: "Gestisci token di accesso"
accountInfo: "Informazioni profilo" accountInfo: "Informazioni profilo"
@ -819,8 +821,8 @@ previewNoteText: "Anteprima del testo"
customCss: "CSS personalizzato" customCss: "CSS personalizzato"
customCssWarn: "Questa impostazione deve essere eseguita da una persona esperta. Una configurazione errata può impedire al client di utilizzare correttamente il sistema." customCssWarn: "Questa impostazione deve essere eseguita da una persona esperta. Una configurazione errata può impedire al client di utilizzare correttamente il sistema."
global: "Federata" global: "Federata"
squareAvatars: "Mostra l'immagine del profilo come quadrato" squareAvatars: "Foto profilo squadrate"
sent: "Inviare" sent: "Inviato"
received: "Ricevuto" received: "Ricevuto"
searchResult: "Risultati della Ricerca" searchResult: "Risultati della Ricerca"
hashtags: "Hashtag" hashtags: "Hashtag"
@ -829,8 +831,8 @@ useBlurEffect: "Utilizza effetto sfocatura"
learnMore: "Più dettagli" learnMore: "Più dettagli"
misskeyUpdated: "Misskey è stato aggiornato!" misskeyUpdated: "Misskey è stato aggiornato!"
whatIsNew: "Visualizza le informazioni sull'aggiornamento" whatIsNew: "Visualizza le informazioni sull'aggiornamento"
translate: "Traduzione" translate: "Traduci"
translatedFrom: "Tradotto da {x}" translatedFrom: "Traduzione da {x}"
accountDeletionInProgress: "È in corso l'eliminazione del profilo" accountDeletionInProgress: "È in corso l'eliminazione del profilo"
usernameInfo: "Un nome per identificare univocamente il tuo profilo sull'istanza. Puoi utilizzare caratteri alfanumerici maiuscoli, minuscoli e il trattino basso (_). Non potrai cambiare nome utente in seguito." usernameInfo: "Un nome per identificare univocamente il tuo profilo sull'istanza. Puoi utilizzare caratteri alfanumerici maiuscoli, minuscoli e il trattino basso (_). Non potrai cambiare nome utente in seguito."
aiChanMode: "Modalità Ai" aiChanMode: "Modalità Ai"
@ -854,11 +856,11 @@ manageAccounts: "Gestisci i profili"
makeReactionsPublic: "Pubblicare la lista delle reazioni." makeReactionsPublic: "Pubblicare la lista delle reazioni."
makeReactionsPublicDescription: "La lista delle reazioni che avete fatto è a disposizione di tutti." makeReactionsPublicDescription: "La lista delle reazioni che avete fatto è a disposizione di tutti."
classic: "Classico" classic: "Classico"
muteThread: "Silenzia la conversazione" muteThread: "Silenzia conversazione"
unmuteThread: "Riattiva la conversazione" unmuteThread: "Riattiva la conversazione"
ffVisibility: "Visibilità delle connessioni" ffVisibility: "Visibilità delle connessioni"
ffVisibilityDescription: "Puoi scegliere a chi mostrare le tue relazioni con altri profili nel fediverso." ffVisibilityDescription: "Puoi scegliere a chi mostrare le tue relazioni con altri profili nel fediverso."
continueThread: "Altri thread." continueThread: "Altre conversazioni"
deleteAccountConfirm: "Così verrà eliminato il profilo. Vuoi procedere?" deleteAccountConfirm: "Così verrà eliminato il profilo. Vuoi procedere?"
incorrectPassword: "La password è errata." incorrectPassword: "La password è errata."
voteConfirm: "Votare per「{choice}」?" voteConfirm: "Votare per「{choice}」?"
@ -1030,8 +1032,8 @@ operationForbidden: "Operazione non consentita"
forceShowAds: "Mostra sempre i banner" forceShowAds: "Mostra sempre i banner"
addMemo: "Aggiungi Memo" addMemo: "Aggiungi Memo"
editMemo: "Modifica Memo" editMemo: "Modifica Memo"
reactionsList: "Elenco delle reazioni" reactionsList: "Chi ha reagito?"
renotesList: "Elenco di Rinota" renotesList: "Chi ha Rinotato?"
notificationDisplay: "Stile delle notifiche" notificationDisplay: "Stile delle notifiche"
leftTop: "In alto a sinistra" leftTop: "In alto a sinistra"
rightTop: "In alto a destra" rightTop: "In alto a destra"
@ -1042,8 +1044,8 @@ vertical: "Verticale"
horizontal: "Laterale" horizontal: "Laterale"
position: "Posizione" position: "Posizione"
serverRules: "Regolamento" serverRules: "Regolamento"
pleaseConfirmBelowBeforeSignup: "Ai sensi del regolamento EU 679/2016 GDPR, autorizzo il trattamento dati personali come descritto nella informativa Privacy." pleaseConfirmBelowBeforeSignup: "Per iscriversi, occorre essere d'accordo con le seguenti condizioni."
pleaseAgreeAllToContinue: "Per continuare, occorre selezionare ed essere d'accordo su tutto." pleaseAgreeAllToContinue: "Occorre accettare tutte le condizioni prima di continuare."
continue: "Continua" continue: "Continua"
preservedUsernames: "Nomi utente riservati" preservedUsernames: "Nomi utente riservati"
preservedUsernamesDescription: "Elenca, uno per linea, i nomi utente che non possono essere registrati durante la creazione del profilo. La restrizione non si applica agli amministratori. Inoltre, i profili già registrati sono esenti." preservedUsernamesDescription: "Elenca, uno per linea, i nomi utente che non possono essere registrati durante la creazione del profilo. La restrizione non si applica agli amministratori. Inoltre, i profili già registrati sono esenti."
@ -1082,7 +1084,7 @@ inviteLimitExceeded: "Hai raggiunto il numero massimo di codici invito generabil
createLimitRemaining: "Inviti generabili: {limit} rimanenti" createLimitRemaining: "Inviti generabili: {limit} rimanenti"
inviteLimitResetCycle: "Alle {time}, il limite verrà ripristinato a {limit}" inviteLimitResetCycle: "Alle {time}, il limite verrà ripristinato a {limit}"
expirationDate: "Scadenza" expirationDate: "Scadenza"
noExpirationDate: "Perpetuo" noExpirationDate: "Senza scadenza"
inviteCodeUsedAt: "Codice di invito usato alle" inviteCodeUsedAt: "Codice di invito usato alle"
registeredUserUsingInviteCode: "Codice di invito usato da" registeredUserUsingInviteCode: "Codice di invito usato da"
waitingForMailAuth: "In attesa della verifica email" waitingForMailAuth: "In attesa della verifica email"
@ -1091,9 +1093,24 @@ usedAt: "Usato alle"
unused: "Inutilizzato" unused: "Inutilizzato"
used: "Utilizzato" used: "Utilizzato"
expired: "Scaduto" expired: "Scaduto"
doYouAgree: "Sei d'accordo?" doYouAgree: "Accetti le condizioni?"
beSureToReadThisAsItIsImportant: "Si prega di leggere attentamente perché è importante." beSureToReadThisAsItIsImportant: "Si prega di leggere attentamente perché è importante."
iHaveReadXCarefullyAndAgree: "Ho letto accuratamente \"{x}\" e sono d'accordo." iHaveReadXCarefullyAndAgree: "Dichiaro di aver letto attentamente \"{x}\" e accettarne le condizioni."
dialog: "Dialogo"
icon: "Foto del profilo"
forYou: "Per te"
currentAnnouncements: "Annunci attuali"
pastAnnouncements: "Annunci precedenti"
youHaveUnreadAnnouncements: "Ci sono Annunci non letti"
_announcement:
forExistingUsers: "Solo ai profili attuali"
forExistingUsersDescription: "L'annuncio sarà visibile solo ai profili esistenti in questo momento. Se disabilitato, sarà visibile anche ai profili che verranno creati dopo la pubblicazione di questo annuncio."
needConfirmationToRead: "Richiede la conferma di lettura"
needConfirmationToReadDescription: "Sarà visualizzata una finestra di dialogo che richiede la conferma di lettura. Inoltre, non è soggetto a conferme di lettura massicce."
end: "Archivia l'annuncio"
tooManyActiveAnnouncementDescription: "L'esperienza delle persone può peggiorare se ci sono troppi annunci attivi. Considera anche l'archiviazione degli annunci conclusi."
readConfirmTitle: "Segnare come già letto?"
readConfirmText: "Hai già letto \"{title}˝?"
_initialAccountSetting: _initialAccountSetting:
accountCreated: "Il tuo profilo è stato creato!" accountCreated: "Il tuo profilo è stato creato!"
letsStartAccountSetup: "Per iniziare, impostiamo il tuo profilo." letsStartAccountSetup: "Per iniziare, impostiamo il tuo profilo."
@ -1489,11 +1506,11 @@ _plugin:
installWarn: "Si prega di installare soltanto estensioni che provengono da fonti affidabili." installWarn: "Si prega di installare soltanto estensioni che provengono da fonti affidabili."
manage: "Gestisci estensioni" manage: "Gestisci estensioni"
_preferencesBackups: _preferencesBackups:
list: "I backup creati." list: "Elenco di impostazioni salvate in precedenza"
saveNew: "Nuovo salvataggio" saveNew: "Nuovo salvataggio"
loadFile: "Importa file" loadFile: "Carica da file"
apply: "Applicabile a questo dispositivo" apply: "Applicabile a questo dispositivo"
save: "Sovrascrivi il file di salvataggio" save: "Sovrascrivi il backup"
inputName: "Inserire il nome del backup." inputName: "Inserire il nome del backup."
cannotSave: "Impossibile salvare." cannotSave: "Impossibile salvare."
nameAlreadyExists: "Il nome del backup \"{name}\" esiste già. Si prega di specificare un nome diverso." nameAlreadyExists: "Il nome del backup \"{name}\" esiste già. Si prega di specificare un nome diverso."
@ -1986,7 +2003,7 @@ _deck:
alwaysShowMainColumn: "Mostra sempre la colonna principale" alwaysShowMainColumn: "Mostra sempre la colonna principale"
columnAlign: "Allineare colonne" columnAlign: "Allineare colonne"
addColumn: "Aggiungi colonna" addColumn: "Aggiungi colonna"
configureColumn: "Impostazioni della colonna." configureColumn: "Impostazioni colonna"
swapLeft: "Sposta a sinistra" swapLeft: "Sposta a sinistra"
swapRight: "Sposta a destra" swapRight: "Sposta a destra"
swapUp: "Sposta in alto" swapUp: "Sposta in alto"

View file

@ -681,6 +681,7 @@ createNewClip: "新しいクリップを作成"
unclip: "クリップ解除" unclip: "クリップ解除"
confirmToUnclipAlreadyClippedNote: "このノートはすでにクリップ「{name}」に含まれています。ノートをこのクリップから除外しますか?" confirmToUnclipAlreadyClippedNote: "このノートはすでにクリップ「{name}」に含まれています。ノートをこのクリップから除外しますか?"
public: "パブリック" public: "パブリック"
private: "非公開"
i18nInfo: "Misskeyは有志によって様々な言語に翻訳されています。{link}で翻訳に協力できます。" i18nInfo: "Misskeyは有志によって様々な言語に翻訳されています。{link}で翻訳に協力できます。"
manageAccessTokens: "アクセストークンの管理" manageAccessTokens: "アクセストークンの管理"
accountInfo: "アカウント情報" accountInfo: "アカウント情報"
@ -1785,6 +1786,10 @@ _permissions:
"write:gallery": "ギャラリーを操作する" "write:gallery": "ギャラリーを操作する"
"read:gallery-likes": "ギャラリーのいいねを見る" "read:gallery-likes": "ギャラリーのいいねを見る"
"write:gallery-likes": "ギャラリーのいいねを操作する" "write:gallery-likes": "ギャラリーのいいねを操作する"
"read:flash": "Playを見る"
"write:flash": "Playを操作する"
"read:flash-likes": "Playのいいねを見る"
"write:flash-likes": "Playのいいねを操作する"
_auth: _auth:
shareAccessTitle: "アプリへのアクセス許可" shareAccessTitle: "アプリへのアクセス許可"

View file

@ -76,7 +76,7 @@ files: "ファイル"
download: "ダウンロード" download: "ダウンロード"
driveFileDeleteConfirm: "ファイル「{name}」をほかしてええか?このファイルを添付したノートも消えてまうで。" driveFileDeleteConfirm: "ファイル「{name}」をほかしてええか?このファイルを添付したノートも消えてまうで。"
unfollowConfirm: "{name}のフォローを解除してもええんか?" unfollowConfirm: "{name}のフォローを解除してもええんか?"
exportRequested: "エクスポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。エクスポート終わったら「ドライブ」に突っ込んどくで。" exportRequested: "エクスポートしてな、って言うたけど、これ多分めっちゃ時間かかるで。エクスポート終わったら「ドライブ」に突っ込んどくで。"
importRequested: "インポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。" importRequested: "インポートしてな、ってリクエストしたけど、これ多分めっちゃ時間かかるで。"
lists: "リスト" lists: "リスト"
noLists: "リストなんてあらへんで" noLists: "リストなんてあらへんで"
@ -156,6 +156,7 @@ addEmoji: "絵文字を追加"
settingGuide: "ええ感じの設定" settingGuide: "ええ感じの設定"
cacheRemoteFiles: "リモートのファイルをキャッシュする" cacheRemoteFiles: "リモートのファイルをキャッシュする"
cacheRemoteFilesDescription: "この設定を切っとったら、リモートファイルをキャッシュせんと直リンクするようになるで。サーバーの容量は節約できるけど、サムネイルを作らんなるから通信量が増えるで。" cacheRemoteFilesDescription: "この設定を切っとったら、リモートファイルをキャッシュせんと直リンクするようになるで。サーバーの容量は節約できるけど、サムネイルを作らんなるから通信量が増えるで。"
youCanCleanRemoteFilesCache: "ファイル管理にある🗑️ボタンでキャッシュ全部ほかすで。"
cacheRemoteSensitiveFiles: "リモートのセンシティブなファイルをキャッシュする" cacheRemoteSensitiveFiles: "リモートのセンシティブなファイルをキャッシュする"
cacheRemoteSensitiveFilesDescription: "この設定を無効にすると、リモートのセンシティブなファイルはキャッシュせず直リンクするようになるで。" cacheRemoteSensitiveFilesDescription: "この設定を無効にすると、リモートのセンシティブなファイルはキャッシュせず直リンクするようになるで。"
flagAsBot: "Botにするで" flagAsBot: "Botにするで"
@ -680,6 +681,7 @@ createNewClip: "新しいクリップを作るで"
unclip: "クリップ解除するで" unclip: "クリップ解除するで"
confirmToUnclipAlreadyClippedNote: "このノートはすでにクリップ「{name}」に含まれとるで。ノートをこのクリップから除外しよか?" confirmToUnclipAlreadyClippedNote: "このノートはすでにクリップ「{name}」に含まれとるで。ノートをこのクリップから除外しよか?"
public: "パブリック" public: "パブリック"
private: "非公開"
i18nInfo: "Misskeyは有志によっていろんな言語に翻訳されとるで。{link}で翻訳に協力したってやー。" i18nInfo: "Misskeyは有志によっていろんな言語に翻訳されとるで。{link}で翻訳に協力したってやー。"
manageAccessTokens: "アクセストークンの管理" manageAccessTokens: "アクセストークンの管理"
accountInfo: "アカウント情報" accountInfo: "アカウント情報"
@ -699,7 +701,7 @@ no: "あかん"
driveFilesCount: "ドライブのファイル数" driveFilesCount: "ドライブのファイル数"
driveUsage: "ドライブ使用量やで" driveUsage: "ドライブ使用量やで"
noCrawle: "クローラーによるインデックスを拒否するで" noCrawle: "クローラーによるインデックスを拒否するで"
noCrawleDescription: "検索エンジンにあんたのユーザーページ、ート、Pagesとかのコンテンツを登録(インデックス)せぇへんように頼むで。" noCrawleDescription: "検索エンジンにあんたのユーザーページ、ート、Pagesとかのコンテンツを登録(インデックス)せんように頼むで。邪魔すんねんやったら帰って〜。"
lockedAccountInfo: "フォローを承認制にしとっても、ノートの公開範囲を「フォロワー」にせぇへん限り、誰でもあんたのノートを見れるで。" lockedAccountInfo: "フォローを承認制にしとっても、ノートの公開範囲を「フォロワー」にせぇへん限り、誰でもあんたのノートを見れるで。"
alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にするで" alwaysMarkSensitive: "デフォルトでメディアを閲覧注意にするで"
loadRawImages: "添付画像のサムネイルをオリジナル画質にするで" loadRawImages: "添付画像のサムネイルをオリジナル画質にするで"
@ -745,7 +747,7 @@ value: "値"
createdAt: "作成した日" createdAt: "作成した日"
updatedAt: "更新日時" updatedAt: "更新日時"
saveConfirm: "保存するで?" saveConfirm: "保存するで?"
deleteConfirm: "ホンマに削除するで?" deleteConfirm: "ホンマにほかすで?"
invalidValue: "有効な値じゃないみたいやで。" invalidValue: "有効な値じゃないみたいやで。"
registry: "レジストリ" registry: "レジストリ"
closeAccount: "アカウントを閉鎖する" closeAccount: "アカウントを閉鎖する"
@ -1070,7 +1072,7 @@ later: "あとで"
goToMisskey: "Misskeyへ" goToMisskey: "Misskeyへ"
additionalEmojiDictionary: "絵文字の追加辞書" additionalEmojiDictionary: "絵文字の追加辞書"
installed: "インストール済み" installed: "インストール済み"
branding: "" branding: "ブランディング"
enableServerMachineStats: "サーバーのマシン情報見せびらかすで" enableServerMachineStats: "サーバーのマシン情報見せびらかすで"
enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする" enableIdenticonGeneration: "ユーザーごとのIdenticon生成を有効にする"
turnOffToImprovePerformance: "オフにしたらえらい軽うなるで。" turnOffToImprovePerformance: "オフにしたらえらい軽うなるで。"
@ -1094,6 +1096,12 @@ expired: "期限切れ"
doYouAgree: "同意するんか?" doYouAgree: "同意するんか?"
beSureToReadThisAsItIsImportant: "重要やから絶対読んでや。" beSureToReadThisAsItIsImportant: "重要やから絶対読んでや。"
iHaveReadXCarefullyAndAgree: "「{x}」の内容をよう読んで、同意するで。" iHaveReadXCarefullyAndAgree: "「{x}」の内容をよう読んで、同意するで。"
dialog: "ダイアログ"
icon: "アイコン"
forYou: "あんたへ"
youHaveUnreadAnnouncements: "あんたまだこのお知らせ読んどらんやろ。"
_announcement:
readConfirmTitle: "既読にしてええんやな?"
_initialAccountSetting: _initialAccountSetting:
accountCreated: "アカウント作り終わったで。" accountCreated: "アカウント作り終わったで。"
letsStartAccountSetup: "アカウントの初期設定をしよか。" letsStartAccountSetup: "アカウントの初期設定をしよか。"
@ -1694,7 +1702,7 @@ _2fa:
removeKeyConfirm: "{name}を消すん?" removeKeyConfirm: "{name}を消すん?"
whyTOTPOnlyRenew: "セキュリティキーが登録されとったら、認証アプリの設定は解除できへんで。" whyTOTPOnlyRenew: "セキュリティキーが登録されとったら、認証アプリの設定は解除できへんで。"
renewTOTP: "認証アプリをもっかい設定" renewTOTP: "認証アプリをもっかい設定"
renewTOTPConfirm: "今までの人称アプリの確認コードは使えんくなるけどええか?" renewTOTPConfirm: "今までの認証アプリの確認コードは使えんくなるけどええか?"
renewTOTPOk: "もっかい設定する" renewTOTPOk: "もっかい設定する"
renewTOTPCancel: "やめとく" renewTOTPCancel: "やめとく"
_permissions: _permissions:

View file

@ -156,6 +156,7 @@ addEmoji: "이모지 추가"
settingGuide: "추천 설정" settingGuide: "추천 설정"
cacheRemoteFiles: "리모트 파일을 캐시" cacheRemoteFiles: "리모트 파일을 캐시"
cacheRemoteFilesDescription: "이 설정을 해지하면 리모트 파일을 캐시하지 않고 해당 파일을 직접 링크하게 됩니다. 그에 따라 서버의 저장 공간을 절약할 수 있지만, 썸네일이 생성되지 않기 때문에 통신량이 증가합니다." cacheRemoteFilesDescription: "이 설정을 해지하면 리모트 파일을 캐시하지 않고 해당 파일을 직접 링크하게 됩니다. 그에 따라 서버의 저장 공간을 절약할 수 있지만, 썸네일이 생성되지 않기 때문에 통신량이 증가합니다."
youCanCleanRemoteFilesCache: "파일 관리 화면의 🗑️ 버튼을 눌러 모든 캐시를 삭제할 수 있습니다."
cacheRemoteSensitiveFiles: "리모트의 민감한 파일을 캐시" cacheRemoteSensitiveFiles: "리모트의 민감한 파일을 캐시"
cacheRemoteSensitiveFilesDescription: "이 설정을 비활성화하면 리모트의 민감한 파일은 캐시하지 않고 리모트에서 직접 가져오도록 합니다." cacheRemoteSensitiveFilesDescription: "이 설정을 비활성화하면 리모트의 민감한 파일은 캐시하지 않고 리모트에서 직접 가져오도록 합니다."
flagAsBot: "나는 봇입니다" flagAsBot: "나는 봇입니다"
@ -329,7 +330,7 @@ watch: "지켜보기"
unwatch: "지켜보기 해제" unwatch: "지켜보기 해제"
accept: "허가" accept: "허가"
reject: "거부" reject: "거부"
normal: "정상" normal: "일반"
instanceName: "서버 이름" instanceName: "서버 이름"
instanceDescription: "서버 소개" instanceDescription: "서버 소개"
maintainerName: "관리자 이름" maintainerName: "관리자 이름"
@ -680,6 +681,7 @@ createNewClip: "새 클립 만들기"
unclip: "클립 해제" unclip: "클립 해제"
confirmToUnclipAlreadyClippedNote: "이 노트는 이미 \"{name}\" 클립에 포함되어 있습니다. 클립을 해제하시겠습니까?" confirmToUnclipAlreadyClippedNote: "이 노트는 이미 \"{name}\" 클립에 포함되어 있습니다. 클립을 해제하시겠습니까?"
public: "공개" public: "공개"
private: "비공개"
i18nInfo: "Misskey는 자원봉사자들에 의해 다양한 언어로 번역되고 있습니다. {link}에서 번역에 참가할 수 있습니다." i18nInfo: "Misskey는 자원봉사자들에 의해 다양한 언어로 번역되고 있습니다. {link}에서 번역에 참가할 수 있습니다."
manageAccessTokens: "액세스 토큰 관리" manageAccessTokens: "액세스 토큰 관리"
accountInfo: "계정 정보" accountInfo: "계정 정보"
@ -1091,6 +1093,24 @@ usedAt: "사용 시각"
unused: "사용되지 않음" unused: "사용되지 않음"
used: "사용됨" used: "사용됨"
expired: "만료됨" expired: "만료됨"
doYouAgree: "동의하십니까?"
beSureToReadThisAsItIsImportant: "중요하므로 반드시 읽어주십시오."
iHaveReadXCarefullyAndAgree: "\"{x}\"의 내용을 읽고 동의합니다."
dialog: "다이얼로그"
icon: "아바타"
forYou: "당신에게"
currentAnnouncements: "현재 공지사항"
pastAnnouncements: "과거 공지사항"
youHaveUnreadAnnouncements: "읽지 않은 공지사항이 있습니다."
_announcement:
forExistingUsers: "기존 유저에게만 알림"
forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다."
needConfirmationToRead: "읽음으로 표시하기 전에 확인하기"
needConfirmationToReadDescription: "활성화하면 이 공지사항을 읽음으로 표시하기 전에 확인 알림창을 띄웁니다. '모두 읽음'의 대상에서도 제외됩니다."
end: "공지에서 내리기"
tooManyActiveAnnouncementDescription: "공지사항이 너무 많을 경우, 사용자 경험에 영향을 끼칠 가능성이 있습니다. 오래된 공지사항은 아카이브하시는 것을 권장드립니다."
readConfirmTitle: "읽음으로 표시합니까?"
readConfirmText: "\"{title}\"을(를) 읽음으로 표시합니다."
_initialAccountSetting: _initialAccountSetting:
accountCreated: "계정 생성이 완료되었습니다!" accountCreated: "계정 생성이 완료되었습니다!"
letsStartAccountSetup: "계정의 초기 설정을 진행합니다." letsStartAccountSetup: "계정의 초기 설정을 진행합니다."

View file

@ -107,7 +107,11 @@ enterEmoji: "ປ້ອນອີໂມຈິ"
renote: "Renote" renote: "Renote"
unrenote: "ເລີກ Renote" unrenote: "ເລີກ Renote"
renoted: "ເກັບບັນທຶກໄວ້" renoted: "ເກັບບັນທຶກໄວ້"
cantRenote: "ໂພສນີ້ບໍ່ສາມາດຖືກບັນທຶກໄວ້ຄືນໃໝ່ໄດ້"
cantReRenote: "ບໍ່ສາມາດບັນທຶກຄືນໃໝ່ໄດ້"
quote: "ລວມຂໍ້ຄວາມອ້າງອີງ" quote: "ລວມຂໍ້ຄວາມອ້າງອີງ"
inChannelRenote: "ຊ່ອງພຽງແຕ່ Renote"
inChannelQuote: "ຊ່ອງເທົ່ານັ້ນ Quote"
pinnedNote: "ບັນທຶກທີ່ປັກໝຸດໄວ້" pinnedNote: "ບັນທຶກທີ່ປັກໝຸດໄວ້"
pinned: "ປັກໝຸດໄປຫາໂປຣໄຟລ໌" pinned: "ປັກໝຸດໄປຫາໂປຣໄຟລ໌"
you: "ເຈົ້າ" you: "ເຈົ້າ"
@ -226,9 +230,12 @@ fromUrl: "ຈາກ URL"
uploadFromUrl: "ອັບໂຫຼດຈາກ URL" uploadFromUrl: "ອັບໂຫຼດຈາກ URL"
uploadFromUrlDescription: "URL ຂອງໄຟລ໌ທີ່ທ່ານຕ້ອງການອັບໂຫລດ" uploadFromUrlDescription: "URL ຂອງໄຟລ໌ທີ່ທ່ານຕ້ອງການອັບໂຫລດ"
uploadFromUrlRequested: "ຮ້ອງຂໍການອັບໂຫລດ" uploadFromUrlRequested: "ຮ້ອງຂໍການອັບໂຫລດ"
explore: "ສຳຫຼວດ"
messageRead: "ອ່ານແລ້ວ" messageRead: "ອ່ານແລ້ວ"
startMessaging: "ເລີ່ມການສົນທະນາໃໝ່" startMessaging: "ເລີ່ມການສົນທະນາໃໝ່"
nUsersRead: "ອ່ານໂດຍ {n}" nUsersRead: "ອ່ານໂດຍ {n}"
agree: "ຍອມຮັບ"
termsOfService: "ເງື່ອນໄຂການບໍລິການ"
start: "ເລີ່ມຕົ້ນນຳໃຊ້ເລີຍ" start: "ເລີ່ມຕົ້ນນຳໃຊ້ເລີຍ"
home: "ໜ້າຫຼັກ" home: "ໜ້າຫຼັກ"
activity: "ກິດຈະກຳ" activity: "ກິດຈະກຳ"
@ -266,6 +273,7 @@ inputNewDescription: "ໃສ່ຄຳບັນຍາຍໃໝ່"
inputNewFolderName: "ໃສ່ຊື່ໂຟນເດີໃໝ່" inputNewFolderName: "ໃສ່ຊື່ໂຟນເດີໃໝ່"
circularReferenceFolder: "ໂຟນເດີປາຍທາງແມ່ນໂຟນເດີຍ່ອຍຂອງໂຟນເດີທີ່ທ່ານຕ້ອງການຍ້າຍ" circularReferenceFolder: "ໂຟນເດີປາຍທາງແມ່ນໂຟນເດີຍ່ອຍຂອງໂຟນເດີທີ່ທ່ານຕ້ອງການຍ້າຍ"
rename: "ປ່ຽນຊື່" rename: "ປ່ຽນຊື່"
doNothing: "ບໍ່ສົນໃຈ"
watch: "ເບິ່ງ" watch: "ເບິ່ງ"
unwatch: "ຢຸດເບິ່ງ" unwatch: "ຢຸດເບິ່ງ"
accept: "ອະນຸຍາດ" accept: "ອະນຸຍາດ"
@ -294,7 +302,14 @@ enableRegistration: "ເປີດໃຊ້ການລົງທະບຽນຜ
invite: "ເຊີນ" invite: "ເຊີນ"
driveCapacityPerLocalAccount: "ຄວາມອາດສາມາດຂັບຕໍ່ຜູ້ໃຊ້ທ້ອງຖິ່ນ" driveCapacityPerLocalAccount: "ຄວາມອາດສາມາດຂັບຕໍ່ຜູ້ໃຊ້ທ້ອງຖິ່ນ"
driveCapacityPerRemoteAccount: "ໄດຣຟ໌ຄວາມອາດສາມາດຕໍ່ຜູ້ໃຊ້ທາງໄກ" driveCapacityPerRemoteAccount: "ໄດຣຟ໌ຄວາມອາດສາມາດຕໍ່ຜູ້ໃຊ້ທາງໄກ"
basicInfo: "ຂໍ້ມຸນເບື້ອງຕົ້ນ"
pinnedNotes: "ບັນທຶກທີ່ປັກໝຸດໄວ້" pinnedNotes: "ບັນທຶກທີ່ປັກໝຸດໄວ້"
hcaptchaSiteKey: "ກະແຈໄຊທ໌"
hcaptchaSecretKey: "ກະແຈລັບ"
recaptcha: "reCAPTCHA"
enableRecaptcha: "ເປີດໃຊ້ງານລີແຄ໋ບຈາ"
recaptchaSiteKey: "ກະແຈໄຊທ໌"
recaptchaSecretKey: "ກະແຈລັບ"
turnstileSiteKey: "ກະແຈໄຊທ໌" turnstileSiteKey: "ກະແຈໄຊທ໌"
turnstileSecretKey: "ກະແຈລັບ" turnstileSecretKey: "ກະແຈລັບ"
name: "ຊື່" name: "ຊື່"
@ -302,24 +317,41 @@ userList: "ລາຍການ"
about: "ກ່ຽວກັບ" about: "ກ່ຽວກັບ"
aboutMisskey: "ກ່ຽວກັບ Misskey" aboutMisskey: "ກ່ຽວກັບ Misskey"
administrator: "ຜູ້ບໍລິຫານ" administrator: "ຜູ້ບໍລິຫານ"
token: "ໂທເຄັນ"
share: "ແບ່ງປັນ" share: "ແບ່ງປັນ"
notFound: "ບໍ່ພົບ" notFound: "ບໍ່ພົບ"
cacheClear: "ລຶບລ້າງແຄສ" cacheClear: "ລຶບລ້າງແຄສ"
help: "ຊ່ວຍເຫຼືອ"
close: "ປິດ"
invites: "ເຊີນ" invites: "ເຊີນ"
members: "ສະມາຊິກ"
transfer: "ໂອນຍ້າຍ"
title: "ຫົວຂໍ້" title: "ຫົວຂໍ້"
text: "ຂໍ້ຄວາມ" text: "ຂໍ້ຄວາມ"
enable: "ເປີດໃຊ້" enable: "ເປີດໃຊ້"
next: "ຕໍ່ໄປ" next: "ຕໍ່ໄປ"
retype: "ເຂົ້າໄປອີກຄັ້ງ"
quoteAttached: "ວົງຢືມ"
invitations: "ເຊີນ" invitations: "ເຊີນ"
unavailable: "ບໍ່​ສາ​ມາດ​ໃຊ້​ໄດ້"
language: "ພາສາ" language: "ພາສາ"
aboutX: "ກ່ຽວກັບ {x}"
emojiStyle: "ຮູບແບບອີໂມຈິ"
native: "ພາ​ສາ​ແມ່" native: "ພາ​ສາ​ແມ່"
noHistory: "​ບໍ່​ມີ​ລາຍ​ການ​ຢູ່​ບ່ອນ​ນີ້"
doing: "ກຳລັງປະມວນຜົນ..."
category: "ຫມວດຫມູ່" category: "ຫມວດຫມູ່"
tags: "ແທ໋ກ" tags: "ແທ໋ກ"
createAccount: "ສ້າງບັນຊີ" createAccount: "ສ້າງບັນຊີ"
existingAccount: "ທີ່ມີຢູ່" existingAccount: "ທີ່ມີຢູ່"
dashboard: "ໜ້າປັດ" dashboard: "ໜ້າປັດ"
local: "ທ້ອງຖິ່ນ" local: "ທ້ອງຖິ່ນ"
numberOfDays: "ຈຳນວນມື້"
objectStorageBucket: "Bucket"
objectStoragePrefix: "Prefix"
objectStorageEndpoint: "Endpoint"
objectStorageRegion: "ພາກ​ພື້ນ" objectStorageRegion: "ພາກ​ພື້ນ"
deleteAll: "ລຶບທັງໝົດ"
sounds: "ສຽງ" sounds: "ສຽງ"
sound: "ສຽງ" sound: "ສຽງ"
none: "ບໍ່ມີ" none: "ບໍ່ມີ"
@ -333,18 +365,40 @@ ascendingOrder: "ນ້ອຍໄປຫາໃຫຍ່"
descendingOrder: "ໃຫຍ່ຫານ້ອຍ" descendingOrder: "ໃຫຍ່ຫານ້ອຍ"
output: "ຜົນຜະລິດ" output: "ຜົນຜະລິດ"
script: "ບົດ​ຄວາມ" script: "ບົດ​ຄວາມ"
menu: "ເມນູ"
rearrange: "ຈັດລຽງຄືນ"
poll: "ການພູນ"
description: "ລາຍລະອຽດ"
author: "ຜູ້ຂຽນ"
manage: "ການຈັດການ"
plugins: "ປລັ໋ກອີນ"
width: "ກວ້າງ"
height: "ຄວາມສູງ"
large: "ໃຫຍ່."
medium: "ປານກາງ"
small: "ເລັກ"
permission: "ການອະນຸຍາດ"
notificationType: "​ປະເພດການ​ແຈ້ງ​ເຕືອນ"
edit: "ແກ້ໄຂ"
email: "ອີເມວ"
smtpHost: "ໂຮດສ" smtpHost: "ໂຮດສ"
smtpUser: "ຊື່ຜູ້ໃຊ້" smtpUser: "ຊື່ຜູ້ໃຊ້"
smtpPass: "ລະຫັດຜ່ານ" smtpPass: "ລະຫັດຜ່ານ"
clearCache: "ລຶບລ້າງແຄສ" clearCache: "ລຶບລ້າງແຄສ"
info: "ກ່ຽວກັບ" info: "ກ່ຽວກັບ"
user: "ຜູ້ໃຊ້ຕ່າງໆ" user: "ຜູ້ໃຊ້ຕ່າງໆ"
administration: "ການຈັດການ"
middle: "ປານກາງ"
searchByGoogle: "ຄົ້ນຫາ" searchByGoogle: "ຄົ້ນຫາ"
file: "ໄຟລ໌" file: "ໄຟລ໌"
_role:
_priority:
middle: "ປານກາງ"
_email: _email:
_follow: _follow:
title: "ໄດ້ຕິດຕາມທ່ານ" title: "ໄດ້ຕິດຕາມທ່ານ"
_theme: _theme:
description: "ລາຍລະອຽດ"
keys: keys:
mention: "ໄດ້ກ່າວມາ" mention: "ໄດ້ກ່າວມາ"
renote: "Renote" renote: "Renote"
@ -383,6 +437,7 @@ _timelines:
home: "ໜ້າຫຼັກ" home: "ໜ້າຫຼັກ"
_play: _play:
script: "ບົດ​ຄວາມ" script: "ບົດ​ຄວາມ"
summary: "ລາຍລະອຽດ"
_pages: _pages:
blocks: blocks:
image: "ຮູບພາບ" image: "ຮູບພາບ"

View file

@ -426,6 +426,7 @@ pushNotificationAlreadySubscribed: "Pushberichtrn al ingeschakeld"
windowMaximize: "Maximaliseren" windowMaximize: "Maximaliseren"
windowRestore: "Herstellen" windowRestore: "Herstellen"
loggedInAsBot: "Momenteel als bot ingelogd" loggedInAsBot: "Momenteel als bot ingelogd"
icon: "Avatar"
_email: _email:
_follow: _follow:
title: "volgde jou" title: "volgde jou"

View file

@ -461,6 +461,7 @@ videos: "Videoer"
continue: "Fortsett" continue: "Fortsett"
youFollowing: "Følger" youFollowing: "Følger"
options: "Alternativ" options: "Alternativ"
icon: "Avatar"
_initialAccountSetting: _initialAccountSetting:
theseSettingsCanEditLater: "Du kan endre disse innstillingene senere." theseSettingsCanEditLater: "Du kan endre disse innstillingene senere."
_achievements: _achievements:

View file

@ -644,6 +644,7 @@ createNewClip: "Utwórz nowy klip"
unclip: "Odczep" unclip: "Odczep"
confirmToUnclipAlreadyClippedNote: "Ten wpis jest już częścią klipu \"{name}\". Czy chcesz ją usunąć z tego klipu?" confirmToUnclipAlreadyClippedNote: "Ten wpis jest już częścią klipu \"{name}\". Czy chcesz ją usunąć z tego klipu?"
public: "Publiczny" public: "Publiczny"
private: "Prywatne"
i18nInfo: "Misskey jest tłumaczone na wiele języków przez wolontariuszy. Możesz pomóc na {link}." i18nInfo: "Misskey jest tłumaczone na wiele języków przez wolontariuszy. Możesz pomóc na {link}."
manageAccessTokens: "Zarządzaj tokenami dostępu" manageAccessTokens: "Zarządzaj tokenami dostępu"
accountInfo: "Informacje o koncie" accountInfo: "Informacje o koncie"
@ -870,6 +871,7 @@ like: "Polub"
show: "Wyświetlanie" show: "Wyświetlanie"
color: "Kolor" color: "Kolor"
youFollowing: "Śledzeni" youFollowing: "Śledzeni"
icon: "Awatar"
_role: _role:
priority: "Priorytet" priority: "Priorytet"
_priority: _priority:
@ -1089,6 +1091,8 @@ _2fa:
step3: "Wprowadź token podany w aplikacji, aby ukończyć konfigurację." step3: "Wprowadź token podany w aplikacji, aby ukończyć konfigurację."
step4: "Od teraz, przy każdej próbie logowania otrzymasz prośbę o token logowania." step4: "Od teraz, przy każdej próbie logowania otrzymasz prośbę o token logowania."
removeKeyConfirm: "Usunąć kopię zapasową {name}?" removeKeyConfirm: "Usunąć kopię zapasową {name}?"
renewTOTPConfirm: "Spowoduje to, że kody weryfikacyjne z poprzedniej aplikacji przestaną działać"
renewTOTPOk: "Rekonfiguruj"
renewTOTPCancel: "Nie teraz" renewTOTPCancel: "Nie teraz"
_permissions: _permissions:
"read:account": "Wyświetl informacje o swoim koncie" "read:account": "Wyświetl informacje o swoim koncie"
@ -1102,8 +1106,10 @@ _permissions:
"read:following": "Wyświetlanie informacji o obserwowanych" "read:following": "Wyświetlanie informacji o obserwowanych"
"write:following": "Obserwowanie lub cofanie obserwacji innych kont" "write:following": "Obserwowanie lub cofanie obserwacji innych kont"
"read:messaging": "Zobacz swoje czaty" "read:messaging": "Zobacz swoje czaty"
"write:messaging": "Tworzenie lub usuwanie wiadomości czatu"
"read:mutes": "Wyświetlanie listy osób, które wyciszyłeś(-aś)" "read:mutes": "Wyświetlanie listy osób, które wyciszyłeś(-aś)"
"write:mutes": "Edycja listy osób, które wyciszyłeś(-aś)" "write:mutes": "Edycja listy osób, które wyciszyłeś(-aś)"
"write:notes": "Tworzenie lub usuwanie wpisów"
"read:notifications": "Wyświetlanie powiadomień" "read:notifications": "Wyświetlanie powiadomień"
"write:notifications": "Działanie na powiadomieniach" "write:notifications": "Działanie na powiadomieniach"
"read:reactions": "Wyświetlanie reakcji" "read:reactions": "Wyświetlanie reakcji"
@ -1119,9 +1125,23 @@ _permissions:
"write:channels": "Edytuj swoje kanały" "write:channels": "Edytuj swoje kanały"
"read:gallery": "Zobacz swoją galerię" "read:gallery": "Zobacz swoją galerię"
"write:gallery": "Edytuj swoją galerię" "write:gallery": "Edytuj swoją galerię"
"read:gallery-likes": "Wyświetlanie listy polubionych postów w galerii"
"write:gallery-likes": "Edytowanie listy polubionych postów w galerii"
_auth: _auth:
shareAccessTitle: "Przyznawanie uprawnień aplikacji"
shareAccess: "Czy chcesz autoryzować „{name}” do dostępu do tego konta?" shareAccess: "Czy chcesz autoryzować „{name}” do dostępu do tego konta?"
shareAccessAsk: "Czy na pewno chcesz zezwolić tej aplikacji na dostęp do Twojego konta?"
permission: "{name} żąda następujących uprawnień"
permissionAsk: "Ta aplikacja wymaga następujących uprawnień:" permissionAsk: "Ta aplikacja wymaga następujących uprawnień:"
pleaseGoBack: "Proszę, wróć do aplikacji"
callback: "Powracanie do aplikacji"
denied: "Odmowa dostępu"
pleaseLogin: "Zaloguj się, aby autoryzować aplikacje."
_antennaSources:
all: "Wszystkie wpisy"
homeTimeline: "Wpisy obserwowanych użytkowników"
users: "Wpisy określonych użytkowników"
userList: "Wpisy z określonej listy użytkowników"
_weekday: _weekday:
sunday: "Niedziela" sunday: "Niedziela"
monday: "Poniedziałek" monday: "Poniedziałek"
@ -1154,8 +1174,10 @@ _widgets:
serverMetric: "Metryka serwera" serverMetric: "Metryka serwera"
aiscript: "Konsola AiScript" aiscript: "Konsola AiScript"
aichan: "Ai" aichan: "Ai"
userList: "Lista użytkowników"
_userList: _userList:
chooseList: "Wybierz listę" chooseList: "Wybierz listę"
clicker: "Clicker"
_cw: _cw:
hide: "Ukryj" hide: "Ukryj"
show: "Załaduj więcej" show: "Załaduj więcej"
@ -1187,10 +1209,16 @@ _visibility:
public: "Publiczny" public: "Publiczny"
publicDescription: "Twój wpis pojawi się w publicznych osiach czasu" publicDescription: "Twój wpis pojawi się w publicznych osiach czasu"
home: "Strona główna" home: "Strona główna"
homeDescription: "Publikuj tylko na głównej osi czasu"
followers: "Obserwujący" followers: "Obserwujący"
followersDescription: "Widoczne tylko dla obserwujących"
specified: "Bezpośredni" specified: "Bezpośredni"
specifiedDescription: "Napisz tylko określonym użytkownikom" specifiedDescription: "Napisz tylko określonym użytkownikom"
disableFederationDescription: "Nie przesyłaj do innych instancji"
_postForm: _postForm:
replyPlaceholder: "Odpowiedz na ten wpis..."
quotePlaceholder: "Zacytuj ten wpis…"
channelPlaceholder: "Publikuj na kanale..."
_placeholders: _placeholders:
a: "Co się dzieje?" a: "Co się dzieje?"
b: "Co się wydarzyło?" b: "Co się wydarzyło?"
@ -1212,17 +1240,29 @@ _profile:
changeBanner: "Zmień baner" changeBanner: "Zmień baner"
_exportOrImport: _exportOrImport:
allNotes: "Wszystkie wpisy" allNotes: "Wszystkie wpisy"
favoritedNotes: "Ulubione wpisy"
followingList: "Obserwowani" followingList: "Obserwowani"
muteList: "Wycisz" muteList: "Wycisz"
blockingList: "Zablokuj" blockingList: "Zablokuj"
userLists: "Listy" userLists: "Listy"
excludeMutingUsers: "Wyklucz wyciszonych użytkowników"
excludeInactiveUsers: "Wyklucz nieaktywnych użytkowników"
_charts: _charts:
federation: "Federacja" federation: "Federacja"
apRequest: "Żądania" apRequest: "Żądania"
usersIncDec: "Różnica w liczbie użytkowników"
usersTotal: "Łącznie # użytkowników" usersTotal: "Łącznie # użytkowników"
activeUsers: "Aktywni użytkownicy" activeUsers: "Aktywni użytkownicy"
notesIncDec: "Różnica w liczbie wpisów"
notesTotal: "Całkowita liczba wpisów"
filesIncDec: "Różnica w liczbie plików"
filesTotal: "Całkowita liczba plików"
storageUsageIncDec: "Różnica w wykorzystaniu pamięci"
storageUsageTotal: "Całkowite wykorzystanie pamięci"
_instanceCharts: _instanceCharts:
requests: "Żądania" requests: "Żądania"
users: "Różnica w liczbie użytkowników"
notes: "Różnica w liczbie wpisów"
notesTotal: "Łącznie # wpisów" notesTotal: "Łącznie # wpisów"
ff: "Różnica w # obserwujących" ff: "Różnica w # obserwujących"
ffTotal: "Łączna liczba # obserwujących" ffTotal: "Łączna liczba # obserwujących"
@ -1347,5 +1387,16 @@ _deck:
mentions: "Wspomnienia" mentions: "Wspomnienia"
direct: "Bezpośredni" direct: "Bezpośredni"
_webhookSettings: _webhookSettings:
createWebhook: "Stwórz Webhook"
name: "Nazwa" name: "Nazwa"
secret: "Sekret"
events: "Uruchomienie Webhooka"
active: "Właczono" active: "Właczono"
_events:
follow: "Po zaobserwowaniu użytkownika"
followed: "Po zostaniu zaobserwowanym"
note: "Po opublikowaniu wpisu"
reply: "Po otrzymaniu odpowiedzi"
renote: "Po udostępnieniu wpisu"
reaction: "Po otrzymaniu reakcji"
mention: "Po zostaniu wspomnianym"

View file

@ -4,7 +4,7 @@ headlineMisskey: "Uma rede ligada por notas"
introMisskey: "Bem-vindo! O Misskey é um serviço de microblog descentralizado de código aberto.\nCrie \"notas\" para compartilhar o que está acontecendo agora ou para se expressar com todos à sua volta 📡\nVocê também pode adicionar rapidamente reações às notas de outras pessoas usando a função \"Reações\" 👍\nVamos explorar um novo mundo 🚀" introMisskey: "Bem-vindo! O Misskey é um serviço de microblog descentralizado de código aberto.\nCrie \"notas\" para compartilhar o que está acontecendo agora ou para se expressar com todos à sua volta 📡\nVocê também pode adicionar rapidamente reações às notas de outras pessoas usando a função \"Reações\" 👍\nVamos explorar um novo mundo 🚀"
poweredByMisskeyDescription: "{name} é um dos servidores da plataforma de código aberto <b>Misskey</b>." poweredByMisskeyDescription: "{name} é um dos servidores da plataforma de código aberto <b>Misskey</b>."
monthAndDay: "{day}/{month}" monthAndDay: "{day}/{month}"
search: "Buscar" search: "Pesquisar"
notifications: "Notificações" notifications: "Notificações"
username: "Nome de usuário" username: "Nome de usuário"
password: "Senha" password: "Senha"
@ -25,23 +25,23 @@ basicSettings: "Configurações básicas"
otherSettings: "Outras configurações" otherSettings: "Outras configurações"
openInWindow: "Abrir em um janela" openInWindow: "Abrir em um janela"
profile: "Perfil" profile: "Perfil"
timeline: "Timeline" timeline: "Linha do tempo"
noAccountDescription: "Este usuário não tem uma descrição." noAccountDescription: "Este usuário não tem uma descrição."
login: "Iniciar sessão" login: "Iniciar sessão"
loggingIn: "Iniciando sessão…" loggingIn: "Iniciando sessão…"
logout: "Sair" logout: "Sair"
signup: "Registrar-se" signup: "Registrar-se"
uploading: "Enviando…" uploading: "Enviando…"
save: "Guardar" save: "Salvar"
users: "Usuários" users: "Usuários"
addUser: "Adicionar usuário" addUser: "Adicionar usuário"
favorite: "Adicionar aos favoritos" favorite: "Adicionar aos favoritos"
favorites: "Adicionar aos favoritos" favorites: "Favoritos"
unfavorite: "Remover dos favoritos" unfavorite: "Remover dos favoritos"
favorited: "Adicionado aos favoritos." favorited: "Adicionado aos favoritos."
alreadyFavorited: "Já adicionado aos favoritos." alreadyFavorited: "Já adicionado aos favoritos."
cantFavorite: "Não foi possível adicionar aos favoritos." cantFavorite: "Não foi possível adicionar aos favoritos."
pin: "Afixar no perfil" pin: "Fixar no perfil"
unpin: "Desafixar do perfil" unpin: "Desafixar do perfil"
copyContent: "Copiar conteúdos" copyContent: "Copiar conteúdos"
copyLink: "Copiar link" copyLink: "Copiar link"
@ -50,11 +50,11 @@ deleteAndEdit: "Excluir e editar"
deleteAndEditConfirm: "Deseja excluir esta nota e editá-la novamente? Todas as reações, compartilhamentos e respostas a esta nota também serão excluídas." deleteAndEditConfirm: "Deseja excluir esta nota e editá-la novamente? Todas as reações, compartilhamentos e respostas a esta nota também serão excluídas."
addToList: "Adicionar a lista" addToList: "Adicionar a lista"
addToAntenna: "Adicionar à antena" addToAntenna: "Adicionar à antena"
sendMessage: "Enviar uma mensagem" sendMessage: "Enviar mensagem"
copyRSS: "Copiar RSS" copyRSS: "Copiar RSS"
copyUsername: "Copiar nome de utilizador" copyUsername: "Copiar nome de utilizador"
copyUserId: "Copiar o ID do utilizador" copyUserId: "Copiar ID do utilizador"
copyNoteId: "Copiar o ID da publicação" copyNoteId: "Copiar ID da publicação"
copyFileId: "Copiar o ID do arquivo" copyFileId: "Copiar o ID do arquivo"
copyFolderId: "Copiar o ID da pasta" copyFolderId: "Copiar o ID da pasta"
copyProfileUrl: "Copiar a URL do perfil" copyProfileUrl: "Copiar a URL do perfil"
@ -89,7 +89,7 @@ createList: "Criar lista"
manageLists: "Gerenciar listas" manageLists: "Gerenciar listas"
error: "Erro" error: "Erro"
somethingHappened: "Ocorreu um erro" somethingHappened: "Ocorreu um erro"
retry: "Tentar novamente" retry: "Tente novamente"
pageLoadError: "Ocorreu um erro ao carregar a página." pageLoadError: "Ocorreu um erro ao carregar a página."
pageLoadErrorDescription: "Isso geralmente acontece devido ao cache do navegador ou da rede. Tente limpar o cache ou aguarde um pouco antes de tentar novamente." pageLoadErrorDescription: "Isso geralmente acontece devido ao cache do navegador ou da rede. Tente limpar o cache ou aguarde um pouco antes de tentar novamente."
serverIsDead: "Não há resposta do servidor. Aguarde um momento e tente novamente." serverIsDead: "Não há resposta do servidor. Aguarde um momento e tente novamente."
@ -156,6 +156,7 @@ addEmoji: "Adicionar um Emoji"
settingGuide: "Guia de configuração" settingGuide: "Guia de configuração"
cacheRemoteFiles: "Cache de arquivos remotos" cacheRemoteFiles: "Cache de arquivos remotos"
cacheRemoteFilesDescription: "Ao desativar esta configuração, os arquivos remotos não serão mais armazenados em cache e serão vinculados diretamente. Isso economizará espaço de armazenamento no servidor, mas os thumbnails não serão gerados, o que pode aumentar o tráfego de dados." cacheRemoteFilesDescription: "Ao desativar esta configuração, os arquivos remotos não serão mais armazenados em cache e serão vinculados diretamente. Isso economizará espaço de armazenamento no servidor, mas os thumbnails não serão gerados, o que pode aumentar o tráfego de dados."
youCanCleanRemoteFilesCache: "Pode excluir todos os caches com o botão 🗑️ de gestão de arquivos."
cacheRemoteSensitiveFiles: "Fazer cache de arquivos remotos sensíveis" cacheRemoteSensitiveFiles: "Fazer cache de arquivos remotos sensíveis"
cacheRemoteSensitiveFilesDescription: "Desativar essa configuração faz com que arquivos remotos sensíveis sejam vinculados diretamente em vez de armazenados em cache." cacheRemoteSensitiveFilesDescription: "Desativar essa configuração faz com que arquivos remotos sensíveis sejam vinculados diretamente em vez de armazenados em cache."
flagAsBot: "Marcar conta como robô" flagAsBot: "Marcar conta como robô"
@ -231,7 +232,7 @@ federating: "Federando"
blocked: "Bloqueado" blocked: "Bloqueado"
suspended: "Suspenso" suspended: "Suspenso"
all: "Todos" all: "Todos"
subscribing: "Subscrito" subscribing: "Inscrito"
publishing: "Publicando" publishing: "Publicando"
notResponding: "Sem resposta" notResponding: "Sem resposta"
instanceFollowing: "Seguir a instância" instanceFollowing: "Seguir a instância"
@ -278,15 +279,15 @@ agreeBelow: "Eu concordo com o seguinte"
basicNotesBeforeCreateAccount: "Observações importantes" basicNotesBeforeCreateAccount: "Observações importantes"
termsOfService: "Termos de Uso" termsOfService: "Termos de Uso"
start: "começar" start: "começar"
home: "casa" home: "Início"
remoteUserCaution: "As informações estão incompletas porque é um utilizador remoto." remoteUserCaution: "As informações estão incompletas porque é um utilizador remoto."
activity: "atividade" activity: "atividade"
images: "imagem" images: "imagem"
image: "imagem" image: "imagem"
birthday: "aniversário" birthday: "Aniversário"
yearsOld: "{age} anos" yearsOld: "{age} anos"
registeredDate: "Data de registro" registeredDate: "Data de registro"
location: "Lugar, colocar" location: "Localização"
theme: "tema" theme: "tema"
themeForLightMode: "Temas usados no modo de luz" themeForLightMode: "Temas usados no modo de luz"
themeForDarkMode: "Temas usados no modo escuro" themeForDarkMode: "Temas usados no modo escuro"
@ -295,7 +296,7 @@ dark: "Escuro"
lightThemes: "Tema claro" lightThemes: "Tema claro"
darkThemes: "Tema escuro" darkThemes: "Tema escuro"
syncDeviceDarkMode: "Sincronize com o modo escuro do dispositivo" syncDeviceDarkMode: "Sincronize com o modo escuro do dispositivo"
drive: "Unidades" drive: "Drive"
fileName: "Nome do Ficheiro" fileName: "Nome do Ficheiro"
selectFile: "Selecione os arquivos" selectFile: "Selecione os arquivos"
selectFiles: "Selecione os arquivos" selectFiles: "Selecione os arquivos"
@ -428,7 +429,7 @@ reduceUiAnimation: "Reduzir a animação da interface do utilizador"
share: "Compartilhar" share: "Compartilhar"
notFound: "Não encontrado" notFound: "Não encontrado"
notFoundDescription: "Não havia página correspondente ao URL especificado." notFoundDescription: "Não havia página correspondente ao URL especificado."
uploadFolder: "Destino de upload padrão" uploadFolder: "Destino de upload padrão"
cacheClear: "Excluir memória transitória" cacheClear: "Excluir memória transitória"
markAsReadAllNotifications: "Marcar todas as notificações como lidas" markAsReadAllNotifications: "Marcar todas as notificações como lidas"
markAsReadAllUnreadNotes: "Marcar todas as postagens como lidas" markAsReadAllUnreadNotes: "Marcar todas as postagens como lidas"
@ -489,7 +490,7 @@ fontSize: "Tamanho do texto"
mediaListWithOneImageAppearance: "Altura da lista de mídias com apenas uma imagem" mediaListWithOneImageAppearance: "Altura da lista de mídias com apenas uma imagem"
limitTo: "Até {x}" limitTo: "Até {x}"
noFollowRequests: "Não há pedidos de seguidor pendentes" noFollowRequests: "Não há pedidos de seguidor pendentes"
openImageInNewTab: "Abrir a imagem numa nova aba" openImageInNewTab: "Abrir a imagem em uma nova aba"
dashboard: "Painel de controle" dashboard: "Painel de controle"
local: "Local" local: "Local"
remote: "Remoto" remote: "Remoto"
@ -601,7 +602,7 @@ useFullReactionPicker: "Usar o seletor de reações completo"
width: "Largura" width: "Largura"
height: "Altura" height: "Altura"
large: "Grande" large: "Grande"
medium: "Média" medium: "Médio"
small: "Pequeno" small: "Pequeno"
generateAccessToken: "Gerar token de acesso" generateAccessToken: "Gerar token de acesso"
permission: "Permissões" permission: "Permissões"
@ -680,6 +681,7 @@ createNewClip: "Criar novo clipe"
unclip: "Remover do clipe" unclip: "Remover do clipe"
confirmToUnclipAlreadyClippedNote: "Esta nota já está incluída no clipe \"{name}\". Você deseja remover a nota deste clipe?" confirmToUnclipAlreadyClippedNote: "Esta nota já está incluída no clipe \"{name}\". Você deseja remover a nota deste clipe?"
public: "Público" public: "Público"
private: "Privado"
i18nInfo: "Misskey é traduzido para várias línguas por voluntários. Você pode ajudar com as traduções em {link}." i18nInfo: "Misskey é traduzido para várias línguas por voluntários. Você pode ajudar com as traduções em {link}."
manageAccessTokens: "Gerenciar tokens de acesso" manageAccessTokens: "Gerenciar tokens de acesso"
accountInfo: "Informações da conta" accountInfo: "Informações da conta"
@ -715,54 +717,208 @@ useSystemFont: "Utilizar a fonte padrão do sistema"
clips: "Clipe" clips: "Clipe"
experimentalFeatures: "Funcionalidades Experimentais" experimentalFeatures: "Funcionalidades Experimentais"
experimental: "Experimental" experimental: "Experimental"
thisIsExperimentalFeature: "Este é um recurso experimental. As funções podem mudar ou pode não funcionar corretamente."
developer: "Programador"
makeExplorable: "Deixe a sua conta mais fácil de encontrar."
makeExplorableDescription: "Se você desativá-lo, outros usuários não poderão encontrar a sua conta na aba Descoberta."
showGapBetweenNotesInTimeline: "Mostrar um espaço entre as notas na linha de tempo"
duplicate: "Duplicar" duplicate: "Duplicar"
left: "Esquerda" left: "Esquerda"
center: "Centralizar"
wide: "Largo" wide: "Largo"
narrow: "Estreito" narrow: "Estreito"
reloadToApplySetting: "As configurações serão refletidas após recarregar a página. Deseja recarregar agora?"
needReloadToApply: "É necessário recarregar a página para refletir as alterações."
showTitlebar: "Exibir barra de título" showTitlebar: "Exibir barra de título"
clearCache: "Limpar o cache" clearCache: "Limpar o cache"
onlineUsersCount: "Pessoas Online"
nUsers: "Usuários"
nNotes: "Notas"
sendErrorReports: "Enviar relatórios de erro"
sendErrorReportsDescription: "Ao ativar essa opção, informações detalhadas de erro serão compartilhadas com o Misskey em caso de problemas, o que pode ajudar a melhorar a qualidade do software. As informações de erro podem incluir a versão do sistema operacional, o tipo de navegador e o sua atividade no Misskey." sendErrorReportsDescription: "Ao ativar essa opção, informações detalhadas de erro serão compartilhadas com o Misskey em caso de problemas, o que pode ajudar a melhorar a qualidade do software. As informações de erro podem incluir a versão do sistema operacional, o tipo de navegador e o sua atividade no Misskey."
myTheme: "Meu tema"
backgroundColor: "Cor de fundo" backgroundColor: "Cor de fundo"
accentColor: "Cor de destaque"
textColor: "Cor do texto"
saveAs: "Salvar como"
advanced: "Avançado" advanced: "Avançado"
advancedSettings: "Configurações avançadas"
value: "Valor" value: "Valor"
createdAt: "Data de criação"
updatedAt: "Última atualização"
saveConfirm: "Deseja salvá-lo?"
deleteConfirm: "Confirma a exclusão?" deleteConfirm: "Confirma a exclusão?"
invalidValue: "Valor inválido"
registry: "Registo"
closeAccount: "Encerrar conta"
currentVersion: "Versão Atual"
latestVersion: "Última versão"
youAreRunningUpToDateClient: "Você está usando a última versão do cliente"
newVersionOfClientAvailable: "Nova versão do cliente disponível"
usageAmount: "Quantidade utilizada"
capacity: "Capacidade" capacity: "Capacidade"
inUse: "Em uso"
editCode: "Editar código"
apply: "Aplicar" apply: "Aplicar"
receiveAnnouncementFromInstance: "Receba as notificações da instância"
emailNotification: "Notificações por e-mail" emailNotification: "Notificações por e-mail"
publish: "Publicar" publish: "Publicar"
inChannelSearch: "Pesquisar no canal"
useReactionPickerForContextMenu: "Clique com o botão direito do mouse para abrir o seletor de reações."
typingUsers: "digitando"
jumpToSpecifiedDate: "Pular para uma data específica"
showingPastTimeline: "Visualizar linha de tempo anterior"
clear: "Limpar"
markAllAsRead: "Marcar todas como lidas"
goBack: "Voltar" goBack: "Voltar"
unlikeConfirm: "Deseja realmente deixar de curtir?" unlikeConfirm: "Deseja realmente deixar de curtir?"
fullView: "Visão completa"
quitFullView: "Sair da visualização completa"
addDescription: "Adicionar descrição"
userPagePinTip: "Notas podem ser mostradas aqui ao clicar em \"Fixar no Perfil\" no menu de notas individuais."
notSpecifiedMentionWarning: "Esta nota menciona usuários que não foram incluídos como recipientes."
info: "Informações" info: "Informações"
userInfo: "Informações do Usuário"
unknown: "Desconhecido" unknown: "Desconhecido"
onlineStatus: "On-line"
hideOnlineStatus: "Ocultar o status on-line."
hideOnlineStatusDescription: "Esconder que está Ativo reduzirá a utilidade de certas funções (como, por exemplo, a Pesquisa)."
online: "Online"
active: "Ativo"
offline: "Inativo"
notRecommended: "Não recomendado"
botProtection: "Proteção contra Bot"
instanceBlocking: "Instâncias bloqueadas"
selectAccount: "Selecionar conta"
switchAccount: "Trocar conta"
enabled: "Ativado" enabled: "Ativado"
disabled: "Desativado" disabled: "Desativado"
quickAction: "Ações rápidas"
user: "Usuários" user: "Usuários"
administration: "Administrar" administration: "Administrar"
accounts: "Contas"
switch: "Trocar"
noMaintainerInformationWarning: "A informação de administrador não foi configurada."
noBotProtectionWarning: "A proteção contra bots não foi configurada."
configure: "Configurar"
postToGallery: "Criar publicação em galeria"
postToHashtag: "Publicar nesta Hashtag"
gallery: "Galeria"
recentPosts: "Notas recentes"
popularPosts: "Notas populares"
shareWithNote: "Compartilhar em Notas"
ads: "Anúncios" ads: "Anúncios"
expiration: "Data limite"
startingperiod: "Data de início"
memo: "Nota"
priority: "Prioridade"
high: "Alto"
middle: "Meio" middle: "Meio"
low: "Baixo"
emailNotConfiguredWarning: "Endereço de e-mail não configurado. " emailNotConfiguredWarning: "Endereço de e-mail não configurado. "
ratio: "Ratio"
previewNoteText: "Visualizar Nota"
customCss: "CSS Personalizado"
customCssWarn: "Esta configuração só deve ser usada se souber o que está fazendo. Valores impróprios podem causar erros no funcionamento do cliente."
global: "Global"
squareAvatars: "Exibir ícones quadrados"
sent: "Enviar" sent: "Enviar"
received: "Recebido"
searchResult: "Pesquisar"
hashtags: "Hashtags"
troubleshooting: "Resolução de problemas"
useBlurEffect: "Usar efeito de desfoque na UI"
learnMore: "Saiba mais"
misskeyUpdated: "Misskey foi atualizado!"
whatIsNew: "Ver atualizações"
translate: "Traduzir" translate: "Traduzir"
translatedFrom: "Traduzido de"
accountDeletionInProgress: "Encerramento de conta em andamento"
usernameInfo: "O nome para identificar exclusivamente a sua conta no servidor. Pode conter letras (az, AZ), números (0~9) e sublinhados (_). O nome de usuário não pode ser alterado posteriormente." usernameInfo: "O nome para identificar exclusivamente a sua conta no servidor. Pode conter letras (az, AZ), números (0~9) e sublinhados (_). O nome de usuário não pode ser alterado posteriormente."
aiChanMode: "Modo AI-chan"
devMode: "Modo de Desenvolvedor"
keepCw: "Manter aviso de conteúdo"
pubSub: "Publicar/Inscrever no perfil"
lastCommunication: "Ultima atualização"
resolved: "Resolvido"
unresolved: "Não resolvido"
breakFollow: "Remover seguidor" breakFollow: "Remover seguidor"
breakFollowConfirm: "Deseja realmente deixar de seguir?" breakFollowConfirm: "Deseja realmente deixar de seguir?"
itsOn: "Ativado"
itsOff: "Desativado"
on: "Ligado" on: "Ligado"
off: "Desligado" off: "Desligado"
emailRequiredForSignup: "Tornar o endereço de e-mail obrigatório durante o cadastro" emailRequiredForSignup: "Tornar o endereço de e-mail obrigatório durante o cadastro"
unread: "Não lido" unread: "Não lido"
filter: "Filtrar"
controlPanel: "Painel de controle"
manageAccounts: "Gerenciar contas"
makeReactionsPublic: "Deixar o histórico de reações em Público"
makeReactionsPublicDescription: "Isto vai deixar o histórico de todas as suas reações visíveis para qualquer um ver."
classic: "Clássico"
muteThread: "Silenciar esta conversa"
unmuteThread: "Desativar silêncio desta conversa"
ffVisibility: "Visibilidade de Seguidos/Seguidores"
ffVisibilityDescription: "Permite configurar quem pode ver quem lhe segue e quem você está seguindo."
continueThread: "Ver mais desta conversa"
deleteAccountConfirm: "Deseja realmente excluir a conta?" deleteAccountConfirm: "Deseja realmente excluir a conta?"
incorrectPassword: "Senha inválida."
voteConfirm: "Deseja confirmar o seu voto em \"{choice}\"?"
hide: "Ocultar" hide: "Ocultar"
useDrawerReactionPickerForMobile: "Mostrar em formato de gaveta" useDrawerReactionPickerForMobile: "Mostrar em formato de gaveta"
welcomeBackWithName: "Bem-vindo de volta, {name}"
clickToFinishEmailVerification: "Clique em [{ok}] para completar a validação do endereço de e-mail." clickToFinishEmailVerification: "Clique em [{ok}] para completar a validação do endereço de e-mail."
overridedDeviceKind: "Sobrepor dispositivo"
smartphone: "Celular"
tablet: "Tablet"
auto: "Automático" auto: "Automático"
searchByGoogle: "Buscar" themeColor: "Cor do tema"
size: "Tamanho"
numberOfColumn: "Número da coluna"
searchByGoogle: "Pesquisar"
instanceDefaultLightTheme: "Tema diurno padrão para toda a instância"
instanceDefaultDarkTheme: "Tema noturno para toda a instância"
instanceDefaultThemeDescription: "Insira o código do tema em formato de objeto."
mutePeriod: "Duração de silenciamento"
period: "Data limite"
indefinitely: "Indefinitivamente"
tenMinutes: "10 minutos"
oneHour: "1 hora"
oneDay: "1 dia"
oneWeek: "1 semana"
oneMonth: "1 mês"
reflectMayTakeTime: "As mudanças podem demorar a aparecer."
failedToFetchAccountInformation: "Não foi possível obter informações da conta"
rateLimitExceeded: "Taxa limite excedido"
cropImage: "Recortar imagem"
cropImageAsk: "Deseja recortar esta imagem?"
cropYes: "Recortar"
cropNo: "Manter deste jeito"
file: "Ficheiros" file: "Ficheiros"
recentNHours: "Últimas {n} horas"
recentNDays: "Últimos {n} dias"
noEmailServerWarning: "Servidor de e-mail não configurado." noEmailServerWarning: "Servidor de e-mail não configurado."
thereIsUnresolvedAbuseReportWarning: "Existem denúncias não resolvidas."
recommended: "Recomendado"
check: "Verificar"
driveCapOverrideLabel: "Altere a capacidade do drive para este usuário" driveCapOverrideLabel: "Altere a capacidade do drive para este usuário"
driveCapOverrideCaption: "Altere a capacidade para o valor padrão informando o valor 0 ou inferior." driveCapOverrideCaption: "Altere a capacidade para o valor padrão informando o valor 0 ou inferior."
requireAdminForView: "Para visualizar, é necessário acessar com uma conta de administrador."
isSystemAccount: "É uma conta criada e gerenciada automaticamente pelo sistema."
typeToConfirm: "Para realizar essa operação, digite {x}."
deleteAccount: "Excluir conta" deleteAccount: "Excluir conta"
document: "Documentação"
numberOfPageCache: "Número de cache de página"
numberOfPageCacheDescription: "Aumentar isso melhora a conveniência, mas também resulta em maior carga e uso de memória."
logoutConfirm: "Gostaria de encerrar a sessão?"
lastActiveDate: "Última data de uso"
statusbar: "Barra de status"
pleaseSelect: "Por favor, selecione."
reverse: "Inversão"
colored: "Colorido"
enableAutoSensitiveDescription: "Quando disponível, a marcação de mídia sensível será automaticamente atribuído ao conteúdo de mídia usando aprendizado de máquina. Mesmo que você desative essa função, em alguns servidores, isso pode ser configurado automaticamente." enableAutoSensitiveDescription: "Quando disponível, a marcação de mídia sensível será automaticamente atribuído ao conteúdo de mídia usando aprendizado de máquina. Mesmo que você desative essa função, em alguns servidores, isso pode ser configurado automaticamente."
activeEmailValidationDescription: "A validação do endereço de e-mail do usuário será realizada de forma mais rigorosa, considerando se é um endereço descartável ou se é possível realizar comunicação efetiva. Se desativado, apenas a validade do formato do endereço será verificada como uma sequência de caracteres." activeEmailValidationDescription: "A validação do endereço de e-mail do usuário será realizada de forma mais rigorosa, considerando se é um endereço descartável ou se é possível realizar comunicação efetiva. Se desativado, apenas a validade do formato do endereço será verificada como uma sequência de caracteres."
account: "Contas"
like: "Curtir" like: "Curtir"
unlike: "Remover curtida" unlike: "Remover curtida"
numberOfLikes: "Número de curtidas" numberOfLikes: "Número de curtidas"
@ -772,6 +928,27 @@ pleaseDonate: "O Misskey é um software gratuito utilizado por {host}. Para que
roles: "Cargos" roles: "Cargos"
role: "Cargo" role: "Cargo"
noRole: "Nenhum cargo" noRole: "Nenhum cargo"
unassign: "Remover"
color: "Cor"
manageCustomEmojis: "Gerenciar Emojis customizados"
youCannotCreateAnymore: "Você atingiu o limite de criação."
cannotPerformTemporary: "Ação temporariamente indisponível"
cannotPerformTemporaryDescription: "Esta ação não pôde ser concluída devido ao excesso de pedidos em sucessão. Tente novamente em alguns momentos."
invalidParamError: "Parâmetros inválidos"
invalidParamErrorDescription: "Parâmetros requisitados inválidos. Isto normalmente acontece devido a um erro, mas também pode ocorrer devido à entrada de valores além do limite ou algo semelhante."
permissionDeniedError: "Operação recusada"
permissionDeniedErrorDescription: "Esta conta não tem permissão para executar esta ação."
preset: "Predefinições"
selectFromPresets: "Escolher de predefinições"
achievements: "Conquistas"
gotInvalidResponseError: "Resposta do servidor inválida"
gotInvalidResponseErrorDescription: "Servidor fora do ar ou em manutenção. Favor tentar mais tarde."
thisPostMayBeAnnoying: "Esta nota pode incomodar outras pessoas."
thisPostMayBeAnnoyingHome: "Postar na linha do tempo inicial"
thisPostMayBeAnnoyingCancel: "Cancelar"
thisPostMayBeAnnoyingIgnore: "Postar mesmo assim"
collapseRenotes: "Ocultar Renotes já visualizadas"
internalServerError: "Erro interno de servidor"
emailNotSupported: "O envio de e-mails não é suportado nesta instância" emailNotSupported: "O envio de e-mails não é suportado nesta instância"
likeOnly: "Apenas curtidas" likeOnly: "Apenas curtidas"
likeOnlyForRemote: "Tudo (somente curtidas remotas)" likeOnlyForRemote: "Tudo (somente curtidas remotas)"
@ -780,6 +957,7 @@ rolesAssignedToMe: "Cargos atribuídos a mim"
unfavoriteConfirm: "Deseja realmente remover dos favoritos?" unfavoriteConfirm: "Deseja realmente remover dos favoritos?"
drivecleaner: "Limpeza do drive" drivecleaner: "Limpeza do drive"
retryAllQueuesConfirmTitle: "Gostaria de tentar novamente agora?" retryAllQueuesConfirmTitle: "Gostaria de tentar novamente agora?"
horizontal: "Exibir painel lateral inteiro"
preservedUsernamesDescription: "Liste os nomes de usuário que deseja reservar, separando-os por quebras de linha. Os nomes de usuário especificados aqui não poderão ser utilizados durante a criação de contas. No entanto, esta restrição não se aplica quando a conta é criada por um administrador. Além disso, as contas que já existem não serão afetadas." preservedUsernamesDescription: "Liste os nomes de usuário que deseja reservar, separando-os por quebras de linha. Os nomes de usuário especificados aqui não poderão ser utilizados durante a criação de contas. No entanto, esta restrição não se aplica quando a conta é criada por um administrador. Além disso, as contas que já existem não serão afetadas."
channelArchiveConfirmTitle: "Deseja realmente arquivar {name}?" channelArchiveConfirmTitle: "Deseja realmente arquivar {name}?"
youFollowing: "Seguindo" youFollowing: "Seguindo"
@ -789,6 +967,7 @@ rolesThatCanBeUsedThisEmojiAsReaction: "Cargos que podem utilizar este emoji com
rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Se nenhum cargo for especificado, qualquer pessoa pode usar este emoji como reação." rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Se nenhum cargo for especificado, qualquer pessoa pode usar este emoji como reação."
rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Estes cargos devem ser públicos." rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Estes cargos devem ser públicos."
waitingForMailAuth: "Verificação de e-mail pendente " waitingForMailAuth: "Verificação de e-mail pendente "
icon: "Avatar"
_initialAccountSetting: _initialAccountSetting:
followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo." followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo."
_accountMigration: _accountMigration:
@ -989,7 +1168,7 @@ _role:
priority: "Prioridade" priority: "Prioridade"
_priority: _priority:
low: "Baixa" low: "Baixa"
middle: "Média" middle: "Médio"
high: "Alta" high: "Alta"
_options: _options:
gtlAvailable: "Visualizar Linha do Tempo Global" gtlAvailable: "Visualizar Linha do Tempo Global"
@ -1038,9 +1217,11 @@ _emailUnavailable:
mx: "O servidor de informado é inválido" mx: "O servidor de informado é inválido"
smtp: "O servidor de e-mail não está respondendo" smtp: "O servidor de e-mail não está respondendo"
_ffVisibility: _ffVisibility:
public: "Publicar" public: "Público"
followers: "Visível apenas para seguidores" followers: "Visível apenas para seguidores"
private: "Privado"
_signup: _signup:
almostThere: "Quase pronto"
emailAddressInfo: "Por favor, insira o seu endereço de e-mail. Ele não será divulgado." emailAddressInfo: "Por favor, insira o seu endereço de e-mail. Ele não será divulgado."
emailSent: "Um e-mail de confirmação foi enviado para o endereço de e-mail fornecido ({email}). Acesse o link fornecido no e-mail para concluir a criação de sua conta." emailSent: "Um e-mail de confirmação foi enviado para o endereço de e-mail fornecido ({email}). Acesse o link fornecido no e-mail para concluir a criação de sua conta."
_accountDelete: _accountDelete:
@ -1052,6 +1233,8 @@ _accountDelete:
inProgress: "A exclusão está em andamento" inProgress: "A exclusão está em andamento"
_ad: _ad:
back: "Voltar" back: "Voltar"
reduceFrequencyOfThisAd: "Diminuir frequência deste anúncio"
hide: "Não exibir anúncios"
_forgotPassword: _forgotPassword:
enterEmail: "Por favor, insira o endereço de e-mail usado no cadastro de sua conta. Um link para redefinição de senha será enviado para esse endereço." enterEmail: "Por favor, insira o endereço de e-mail usado no cadastro de sua conta. Um link para redefinição de senha será enviado para esse endereço."
ifNoEmail: "Caso você não tenha registrado um endereço de e-mail, por favor, entre em contato com o administrador." ifNoEmail: "Caso você não tenha registrado um endereço de e-mail, por favor, entre em contato com o administrador."
@ -1072,8 +1255,16 @@ _preferencesBackups:
_channel: _channel:
featured: "Destaques" featured: "Destaques"
following: "Seguindo" following: "Seguindo"
usersCount: "usuários ativos"
notesCount: "notas"
nameAndDescription: "Nome e descrição"
_menuDisplay:
sideFull: "Exibir painel lateral inteiro"
top: "Exibir barra superior"
hide: "Ocultar"
_theme: _theme:
description: "Descrição" description: "Descrição"
alpha: "Opacidade"
deleteConstantConfirm: "Confirma a exclusão da constante {const}?" deleteConstantConfirm: "Confirma a exclusão da constante {const}?"
keys: keys:
mention: "Menção" mention: "Menção"
@ -1160,7 +1351,7 @@ _poll:
canMultipleVote: "Permitir múltipla seleção" canMultipleVote: "Permitir múltipla seleção"
vote: "Votar em enquetes" vote: "Votar em enquetes"
_visibility: _visibility:
home: "casa" home: "Início"
followers: "Seguidores" followers: "Seguidores"
followersDescription: "Tornar visível apenas para os meus seguidores" followersDescription: "Tornar visível apenas para os meus seguidores"
_profile: _profile:
@ -1175,7 +1366,7 @@ _exportOrImport:
_charts: _charts:
federation: "União" federation: "União"
_timelines: _timelines:
home: "casa" home: "Início"
_play: _play:
new: "Criar Play" new: "Criar Play"
edit: "Editar Play" edit: "Editar Play"
@ -1211,7 +1402,7 @@ _notification:
pollEnded: "Os resultados da enquete agora estão disponíveis" pollEnded: "Os resultados da enquete agora estão disponíveis"
emptyPushNotificationMessage: "As notificações de alerta foram atualizadas" emptyPushNotificationMessage: "As notificações de alerta foram atualizadas"
_types: _types:
all: "Todos" all: "Todas"
follow: "Seguindo" follow: "Seguindo"
mention: "Menção" mention: "Menção"
reply: "Respostas" reply: "Respostas"

View file

@ -630,6 +630,7 @@ sent: "Trimite"
searchByGoogle: "Caută" searchByGoogle: "Caută"
file: "Fișiere" file: "Fișiere"
show: "Arată" show: "Arată"
icon: "Avatar"
_role: _role:
_priority: _priority:
middle: "Mediu" middle: "Mediu"

View file

@ -680,6 +680,7 @@ createNewClip: "Новая подборка"
unclip: "Убрать из подборки" unclip: "Убрать из подборки"
confirmToUnclipAlreadyClippedNote: "Эта заметка уже есть в подборке «{name}». Удалить из этой подборки?" confirmToUnclipAlreadyClippedNote: "Эта заметка уже есть в подборке «{name}». Удалить из этой подборки?"
public: "Общедоступно" public: "Общедоступно"
private: "Показываются только вам"
i18nInfo: "Misskey переводят на разные языки добровольцы со всего света. Ваша помощь тоже пригодится здесь: {link}." i18nInfo: "Misskey переводят на разные языки добровольцы со всего света. Ваша помощь тоже пригодится здесь: {link}."
manageAccessTokens: "Управление токенами доступа" manageAccessTokens: "Управление токенами доступа"
accountInfo: "Сведения об учётной записи" accountInfo: "Сведения об учётной записи"
@ -1023,16 +1024,56 @@ videos: "Видео"
dataSaver: "Экономия трафика" dataSaver: "Экономия трафика"
accountMigration: "Перенести учётную запись" accountMigration: "Перенести учётную запись"
accountMoved: "Учетная запись перенесена" accountMoved: "Учетная запись перенесена"
accountMovedShort: "Эта учётная запись перемещена"
operationForbidden: "Эта операция невозможна." operationForbidden: "Эта операция невозможна."
forceShowAds: "Всегда отображать рекламу"
addMemo: "Добавить заметку" addMemo: "Добавить заметку"
editMemo: "Редактировать заметку" editMemo: "Редактировать заметку"
reactionsList: "Реакции" reactionsList: "Реакции"
renotesList: "Репосты" renotesList: "Репосты"
notificationDisplay: "Отображение уведомления" notificationDisplay: "Отображение уведомления"
leftTop: "Верхний левый угол" leftTop: "Верхний левый угол"
rightTop: "Сверху справа"
leftBottom: "Снизу слева"
rightBottom: "Снизу справа"
vertical: "Вертикальная"
horizontal: "Сбоку" horizontal: "Сбоку"
position: "Позиция"
serverRules: "Правила сервера"
pleaseConfirmBelowBeforeSignup: "Для регистрации на данном сервере, необходимо согласится с нижеследующими положениями."
pleaseAgreeAllToContinue: "Чтобы продолжить, необходимо поставить отметки во всех полях \"согласен\"."
continue: "Продолжить"
preservedUsernames: "Зарезервированные имена пользователей"
preservedUsernamesDescription: "Перечислите зарезервированные имена пользователей, отделяя их строками. Они станут недоступны при создании учётной записи. Это ограничение не применяется при создании учётной записи администраторами. Также, уже существующие учётные записи останутся без изменений."
createNoteFromTheFile: "Создать заметку из этого файла"
archive: "Архив"
channelArchiveConfirmTitle: "Переместить {name} в архив?"
channelArchiveConfirmDescription: "Архивированные каналы перестанут отображаться в списке каналов или результатах поиска. В них также нельзя будет добавлять новые записи."
displayOfNote: "Отображение заметок"
initialAccountSetting: "Настройка профиля"
youFollowing: "Подписки" youFollowing: "Подписки"
preventAiLearning: "Отказаться от использования в машинном обучении (Генеративный ИИ)"
options: "Настройки ролей" options: "Настройки ролей"
specifyUser: "Указанный пользователь"
failedToPreviewUrl: "Предварительный просмотр недоступен"
update: "Обновить"
later: "Позже"
goToMisskey: "К Misskey"
additionalEmojiDictionary: "Дополнительные словари эмодзи"
installed: "Установлено"
branding: "Бренд"
expirationDate: "Дата истечения"
unused: "Неиспользуемый"
expired: "Срок действия приглашения истёк"
doYouAgree: "Согласны?"
icon: "Аватар"
_initialAccountSetting:
accountCreated: "Аккаунт успешно создан!"
letsStartAccountSetup: "Давайте настроим вашу учётную запись."
profileSetting: "Настройки профиля"
privacySetting: "Настройки конфиденциальности"
initialAccountSettingCompleted: "Первоначальная настройка успешно завершена!"
skipAreYouSure: "Пропустить настройку?"
_achievements: _achievements:
earnedAt: "Разблокировано в" earnedAt: "Разблокировано в"
_types: _types:
@ -1899,6 +1940,7 @@ _deck:
channel: "Каналы" channel: "Каналы"
mentions: "Упоминания" mentions: "Упоминания"
direct: "Личное" direct: "Личное"
roleTimeline: "История Ролей"
_dialog: _dialog:
charactersExceeded: "Превышено максимальное количество символов! У вас {current} / из {max}" charactersExceeded: "Превышено максимальное количество символов! У вас {current} / из {max}"
charactersBelow: "Это ниже минимального количества символов! У вас {current} / из {min}" charactersBelow: "Это ниже минимального количества символов! У вас {current} / из {min}"
@ -1906,5 +1948,6 @@ _disabledTimeline:
title: "Лента отключена" title: "Лента отключена"
description: "Ваша текущая роль не позволяет пользоваться этой лентой." description: "Ваша текущая роль не позволяет пользоваться этой лентой."
_webhookSettings: _webhookSettings:
createWebhook: "Создать вебхук"
name: "Название" name: "Название"
active: "Вкл." active: "Вкл."

View file

@ -654,6 +654,7 @@ createNewClip: "Vytvoriť nový klip"
unclip: "Odopnúť" unclip: "Odopnúť"
confirmToUnclipAlreadyClippedNote: "Táto poznámka je už pripnutá ako \"{name}\". Naozaj ju chcete odopnúť?" confirmToUnclipAlreadyClippedNote: "Táto poznámka je už pripnutá ako \"{name}\". Naozaj ju chcete odopnúť?"
public: "Verejné" public: "Verejné"
private: "Súkromné"
i18nInfo: "Misskey je prekladaný do rôznych jazykov dobrovoľníkmi. Pomôcť môžete na {link}." i18nInfo: "Misskey je prekladaný do rôznych jazykov dobrovoľníkmi. Pomôcť môžete na {link}."
manageAccessTokens: "Spravovať prístupové tokeny" manageAccessTokens: "Spravovať prístupové tokeny"
accountInfo: "Informácie o účte" accountInfo: "Informácie o účte"
@ -918,6 +919,7 @@ pleaseDonate: "Misskey je bezplatný softvér, ktorý používa {host}. Prosím,
color: "Farba" color: "Farba"
horizontal: "Strana" horizontal: "Strana"
youFollowing: "Sledované" youFollowing: "Sledované"
icon: "Avatar"
_role: _role:
priority: "Priorita" priority: "Priorita"
_priority: _priority:

View file

@ -485,6 +485,7 @@ windowRestore: "Återställ"
pleaseDonate: "Misskey är en gratis programvara som används på {host}. Donera gärna för att göra utvecklingen ständigt, tack!" pleaseDonate: "Misskey är en gratis programvara som används på {host}. Donera gärna för att göra utvecklingen ständigt, tack!"
resetPasswordConfirm: "Återställ verkligen ditt lösenord?" resetPasswordConfirm: "Återställ verkligen ditt lösenord?"
dataSaver: "Databesparing" dataSaver: "Databesparing"
icon: "Profilbild"
_achievements: _achievements:
_types: _types:
_open3windows: _open3windows:

View file

@ -74,7 +74,7 @@ import: "นำเข้า"
export: "นำออก" export: "นำออก"
files: "ไฟล์" files: "ไฟล์"
download: "ดาวน์โหลด" download: "ดาวน์โหลด"
driveFileDeleteConfirm: "นายแน่ใจแล้วหรอ? ว่าต้องการลบไฟล์ \"{name}\" โน้ตย่อที่แนบมากับไฟล์นี้ก็จะถูกลบด้วยนะ" driveFileDeleteConfirm: "คุณต้องการลบไฟล์ \"{name}\" ใช่หรือไม่? โน้ตย่อที่แนบมากับไฟล์นี้ก็จะถูกลบไปด้วย"
unfollowConfirm: "นายแน่ใจแล้วหรอว่าต้องการเลิกติดตาม {name}?" unfollowConfirm: "นายแน่ใจแล้วหรอว่าต้องการเลิกติดตาม {name}?"
exportRequested: "เมื่อคุณได้ร้องขอการส่งออก อาจจะต้องใช้เวลาสักครู่ และจะถูกเพิ่มในไดรฟ์ของคุณเมื่อเสร็จสิ้นแล้ว" exportRequested: "เมื่อคุณได้ร้องขอการส่งออก อาจจะต้องใช้เวลาสักครู่ และจะถูกเพิ่มในไดรฟ์ของคุณเมื่อเสร็จสิ้นแล้ว"
importRequested: "เมื่อคุณได้ร้องขอการนำเข้า อาจจะต้องใช้เวลาสักครู่นะ" importRequested: "เมื่อคุณได้ร้องขอการนำเข้า อาจจะต้องใช้เวลาสักครู่นะ"
@ -92,8 +92,8 @@ somethingHappened: "อุ๊ย ! มีอะไรบางอย่างผ
retry: "ลองใหม่อีกครั้ง" retry: "ลองใหม่อีกครั้ง"
pageLoadError: "เกิดข้อผิดพลาดในการโหลดหน้านี้" pageLoadError: "เกิดข้อผิดพลาดในการโหลดหน้านี้"
pageLoadErrorDescription: "โดยปกติแล้วมักจะเกิดจากข้อผิดพลาดของเครือข่ายหรือแคชของเบราว์เซอร์ ลองล้างแคชแล้วลองใหม่อีกครั้งหลังจากรอสักครู่ " pageLoadErrorDescription: "โดยปกติแล้วมักจะเกิดจากข้อผิดพลาดของเครือข่ายหรือแคชของเบราว์เซอร์ ลองล้างแคชแล้วลองใหม่อีกครั้งหลังจากรอสักครู่ "
serverIsDead: "เซิร์ฟเวอร์นี้ไม่มีการตอบสนอง ได้โปรดกรุณารอสักครู่แล้วลองใหม่อีกครั้งนะ" serverIsDead: "เซิร์ฟเวอร์นี้ไม่มีการตอบสนอง โปรดกรุณารอสักครู่แล้วลองใหม่อีกครั้ง"
youShouldUpgradeClient: "หากต้องการดูหน้านี้ได้โปรดกรุณา รีเซ็ตเพื่ออัปเดตไคลเอ็นต์ของคุณนะ" youShouldUpgradeClient: "หากต้องการดูหน้านี้ กรุณาโหลดหน้าใหม่เพื่ออัปเดตไคลเอ็นต์ของคุณ"
enterListName: "ใส่ชื่อสำหรับรายการลิสต์" enterListName: "ใส่ชื่อสำหรับรายการลิสต์"
privacy: "ความเป็นส่วนตัว" privacy: "ความเป็นส่วนตัว"
makeFollowManuallyApprove: "ติดตามคำขอที่ต้องได้รับการอนุมัติ" makeFollowManuallyApprove: "ติดตามคำขอที่ต้องได้รับการอนุมัติ"
@ -109,7 +109,7 @@ unrenote: "เลิกรีโน้ต"
renoted: "รีโน้ตแล้ว" renoted: "รีโน้ตแล้ว"
cantRenote: "โพสต์นี้ไม่สามารถรีโน้ตไว้ใหม่ได้นะ" cantRenote: "โพสต์นี้ไม่สามารถรีโน้ตไว้ใหม่ได้นะ"
cantReRenote: "ไม่สามารถรีโน้ตเอาไว้ใหม่ได้นะ" cantReRenote: "ไม่สามารถรีโน้ตเอาไว้ใหม่ได้นะ"
quote: "อ้างคำพูด" quote: "อ้างอิง"
inChannelRenote: "รีโน้ตช่องแชลแนลเท่านั้น" inChannelRenote: "รีโน้ตช่องแชลแนลเท่านั้น"
inChannelQuote: "อ้างช่องเท่านั้น" inChannelQuote: "อ้างช่องเท่านั้น"
pinnedNote: "โน้ตที่ปักหมุดเอาไว้" pinnedNote: "โน้ตที่ปักหมุดเอาไว้"
@ -137,7 +137,7 @@ suspend: "ถูกระงับ"
unsuspend: "ยกเลิกระงับ" unsuspend: "ยกเลิกระงับ"
blockConfirm: "คุณแน่ใจแล้วเหรอ? ว่าต้องการบล็อกบัญชีนี้" blockConfirm: "คุณแน่ใจแล้วเหรอ? ว่าต้องการบล็อกบัญชีนี้"
unblockConfirm: "คุณแน่ใจแล้วเหรอ? ว่าต้องการปลดบล็อคบัญชีนี้" unblockConfirm: "คุณแน่ใจแล้วเหรอ? ว่าต้องการปลดบล็อคบัญชีนี้"
suspendConfirm: "นายแน่ใจแล้วเหรอว่าต้องการระงับบัญชีนี้อ่ะ?" suspendConfirm: "แน่ใจว่าคุณต้องการระงับบัญชีนี้?"
unsuspendConfirm: "นายแน่ใจแล้วหรอ? ว่าต้องการยกเลิกการระงับบัญชีนี้" unsuspendConfirm: "นายแน่ใจแล้วหรอ? ว่าต้องการยกเลิกการระงับบัญชีนี้"
selectList: "เลือกรายการ" selectList: "เลือกรายการ"
editList: "แก้ไขรายการ" editList: "แก้ไขรายการ"
@ -156,6 +156,7 @@ addEmoji: "แทรกอีโมจิ"
settingGuide: "การตั้งค่าที่แนะนำ" settingGuide: "การตั้งค่าที่แนะนำ"
cacheRemoteFiles: "แคชไฟล์ระยะไกล" cacheRemoteFiles: "แคชไฟล์ระยะไกล"
cacheRemoteFilesDescription: "เมื่อปิดใช้งานการตั้งค่านี้ ไฟล์ระยะไกลนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกล แต่กรณีการปิดใช้งานนี้จะช่วยลดปริมาณการใช้พื้นที่จัดเก็บข้อมูล แต่เพิ่มปริมาณการใช้งาน เพราะเนื่องจากจะไม่มีการสร้างภาพขนาดย่อ" cacheRemoteFilesDescription: "เมื่อปิดใช้งานการตั้งค่านี้ ไฟล์ระยะไกลนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกล แต่กรณีการปิดใช้งานนี้จะช่วยลดปริมาณการใช้พื้นที่จัดเก็บข้อมูล แต่เพิ่มปริมาณการใช้งาน เพราะเนื่องจากจะไม่มีการสร้างภาพขนาดย่อ"
youCanCleanRemoteFilesCache: "คุณสามารถล้างแคชได้โดยคลิกที่ปุ่ม 🗑️ ในมุมมองการจัดการไฟล์"
cacheRemoteSensitiveFiles: "ไฟล์ระยะไกลที่มีความละเอียดอ่อนแคช" cacheRemoteSensitiveFiles: "ไฟล์ระยะไกลที่มีความละเอียดอ่อนแคช"
cacheRemoteSensitiveFilesDescription: "เมื่อปิดการใช้งานแล้วการตั้งค่านี้ ไฟล์รีโมตที่มีความละเอียดอ่อนนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกลโดยที่ไม่มีการแคช" cacheRemoteSensitiveFilesDescription: "เมื่อปิดการใช้งานแล้วการตั้งค่านี้ ไฟล์รีโมตที่มีความละเอียดอ่อนนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกลโดยที่ไม่มีการแคช"
flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอท" flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอท"
@ -254,7 +255,7 @@ imageUrl: "url รูปภาพ"
remove: "ลบ" remove: "ลบ"
removed: "ถูกลบไปแล้ว" removed: "ถูกลบไปแล้ว"
removeAreYouSure: "นายแน่ใจจริงหรอว่าต้องการที่จะลบออก \"{x}\"" removeAreYouSure: "นายแน่ใจจริงหรอว่าต้องการที่จะลบออก \"{x}\""
deleteAreYouSure: "นายแน่ใจจริงหรอว่าต้องการที่จะลบออก \"{x}\"" deleteAreYouSure: "ต้องการลบ {x} หรือไม่คะ?"
resetAreYouSure: "รีเซ็ตเลยไหม" resetAreYouSure: "รีเซ็ตเลยไหม"
saved: "บันทึกแล้ว" saved: "บันทึกแล้ว"
messaging: "แชท" messaging: "แชท"
@ -308,8 +309,8 @@ renameFolder: "เปลี่ยนชื่อโฟลเดอร์"
deleteFolder: "ลบโฟลเดอร์" deleteFolder: "ลบโฟลเดอร์"
addFile: "เพิ่มไฟล์" addFile: "เพิ่มไฟล์"
emptyDrive: "ไดรฟ์ของคุณว่างเปล่านะ" emptyDrive: "ไดรฟ์ของคุณว่างเปล่านะ"
emptyFolder: "โฟลเดอร์นี้น่าจะว่างเปล่านะ" emptyFolder: "โฟลเดอร์นี้ว่างเปล่า"
unableToDelete: "ไม่สามารถลบออกได้นะ" unableToDelete: "ไม่สามารถลบออกได้"
inputNewFileName: "ป้อนชื่อไฟล์ใหม่นะ" inputNewFileName: "ป้อนชื่อไฟล์ใหม่นะ"
inputNewDescription: "กรุณาใส่แคปชั่นใหม่" inputNewDescription: "กรุณาใส่แคปชั่นใหม่"
inputNewFolderName: "กรุณาใส่ชื่อโฟลเดอร์ใหม่นะ\n" inputNewFolderName: "กรุณาใส่ชื่อโฟลเดอร์ใหม่นะ\n"
@ -363,7 +364,7 @@ pinnedUsersDescription: "ลิสต์ชื่อผู้ใช้โดย
pinnedPages: "หน้าที่ปักหมุด" pinnedPages: "หน้าที่ปักหมุด"
pinnedPagesDescription: "ป้อนเส้นทางของหน้าที่คุณต้องการตรึงไว้ที่หน้าแรกของอินสแตนซ์นี้ โดยคั่นด้วยตัวแบ่งบรรทัด" pinnedPagesDescription: "ป้อนเส้นทางของหน้าที่คุณต้องการตรึงไว้ที่หน้าแรกของอินสแตนซ์นี้ โดยคั่นด้วยตัวแบ่งบรรทัด"
pinnedClipId: "ID ของคลิปที่จะปักหมุด" pinnedClipId: "ID ของคลิปที่จะปักหมุด"
pinnedNotes: "โน้ตที่ปักหมุดเอาไว้" pinnedNotes: "โน้ตที่ปักหมุดไว้"
hcaptcha: "hCaptcha" hcaptcha: "hCaptcha"
enableHcaptcha: "เปิดใช้ hCaptcha" enableHcaptcha: "เปิดใช้ hCaptcha"
hcaptchaSiteKey: "คีย์ไซต์" hcaptchaSiteKey: "คีย์ไซต์"
@ -404,7 +405,7 @@ recentlyDiscoveredUsers: "ผู้ใช้ที่เพิ่งค้นพ
exploreUsersCount: "มีผู้ใช้ {จำนวน} ราย" exploreUsersCount: "มีผู้ใช้ {จำนวน} ราย"
exploreFediverse: "สำรวจเฟดดิเวิร์ส" exploreFediverse: "สำรวจเฟดดิเวิร์ส"
popularTags: "แท็กยอดนิยม" popularTags: "แท็กยอดนิยม"
userList: "รายการ" userList: "ลิสต์"
about: "เกี่ยวกับ" about: "เกี่ยวกับ"
aboutMisskey: "เกี่ยวกับ Misskey" aboutMisskey: "เกี่ยวกับ Misskey"
administrator: "ผู้ดูแลระบบ" administrator: "ผู้ดูแลระบบ"
@ -444,7 +445,7 @@ text: "ข้อความ"
enable: "เปิดใช้งาน" enable: "เปิดใช้งาน"
next: "ถัด​ไป" next: "ถัด​ไป"
retype: "พิมพ์รหัสอีกครั้ง" retype: "พิมพ์รหัสอีกครั้ง"
noteOf: "โน้ต โดย {ผู้ใช้งาน}" noteOf: "โน้ต โดย {user}"
quoteAttached: "อ้างอิง" quoteAttached: "อ้างอิง"
quoteQuestion: "นายต้องการที่จะอ้างอิงหรอ?" quoteQuestion: "นายต้องการที่จะอ้างอิงหรอ?"
noMessagesYet: "ยังไม่มีข้อความนะ" noMessagesYet: "ยังไม่มีข้อความนะ"
@ -680,6 +681,7 @@ createNewClip: "สร้างคลิปใหม่"
unclip: "ลบคลิป" unclip: "ลบคลิป"
confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งของคลิป \"{name}\" แล้ว คุณต้องการลบออกจากคลิปนี้แทนอย่างงั้นหรอ?" confirmToUnclipAlreadyClippedNote: "โน้ตนี้เป็นส่วนหนึ่งของคลิป \"{name}\" แล้ว คุณต้องการลบออกจากคลิปนี้แทนอย่างงั้นหรอ?"
public: "สาธารณะ" public: "สาธารณะ"
private: "ส่วนตัว"
i18nInfo: "Misskey กำลังได้รับการแปลเป็นภาษาต่างๆ โดยอาสาสมัคร คุณสามารถช่วยเหลือได้ที่ {link}" i18nInfo: "Misskey กำลังได้รับการแปลเป็นภาษาต่างๆ โดยอาสาสมัคร คุณสามารถช่วยเหลือได้ที่ {link}"
manageAccessTokens: "การจัดการโทเค็นการเข้าถึง" manageAccessTokens: "การจัดการโทเค็นการเข้าถึง"
accountInfo: "ข้อมูลบัญชี" accountInfo: "ข้อมูลบัญชี"
@ -957,7 +959,7 @@ show: "แสดงผล"
neverShow: "ไม่ต้องแสดงข้อความนี้อีก" neverShow: "ไม่ต้องแสดงข้อความนี้อีก"
remindMeLater: "ไว้ครั้งหน้าแล้วกัน" remindMeLater: "ไว้ครั้งหน้าแล้วกัน"
didYouLikeMisskey: "คุณเคยชอบ Misskey ไหม?" didYouLikeMisskey: "คุณเคยชอบ Misskey ไหม?"
pleaseDonate: "{host} ใช้ซอฟต์แวร์ฟรี Misskey เราขอขอบคุณการบริจาคของคุณอย่างสูงเพื่อให้การพัฒนา Misskey สามารถดำเนินต่อไปได้นะ!" pleaseDonate: "Misskey เป็นซอฟต์แวร์ฟรีที่ใช้งานโดย {host} เราขอขอบคุณการสนับสนุนของคุณอย่างสูงเพื่อให้การพัฒนา Misskey สามารถดำเนินต่อไปได้!"
roles: "บทบาท" roles: "บทบาท"
role: "บทบาท" role: "บทบาท"
noRole: "ไม่พบบทบาท" noRole: "ไม่พบบทบาท"
@ -1094,6 +1096,16 @@ expired: "หมดอายุแล้ว"
doYouAgree: "ยอมรับมั้ย?" doYouAgree: "ยอมรับมั้ย?"
beSureToReadThisAsItIsImportant: "กรุณาอ่านข้อมูลที่สำคัญอันนี้" beSureToReadThisAsItIsImportant: "กรุณาอ่านข้อมูลที่สำคัญอันนี้"
iHaveReadXCarefullyAndAgree: "ฉันได้อ่านข้อความ \"{x}\" และยินยอม" iHaveReadXCarefullyAndAgree: "ฉันได้อ่านข้อความ \"{x}\" และยินยอม"
dialog: "ไดอะล็อก"
icon: "ไอคอน"
forYou: "สำหรับคุณ"
_announcement:
forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน"
needConfirmationToReadDescription: "ข้อความแจ้งแยก ถ้าหากต้องการเพื่อยืนยันว่ากำลังทำเครื่องหมายประกาศนี้ว่าอ่านแล้วจะแสดงขึ้นถ้าหากเปิดใช้งาน การประกาศนั้นจะไม่รวมอยู่ในฟังก์ชั่นว่า \"ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว\""
end: "ประกาศเก็บถาวร"
tooManyActiveAnnouncementDescription: "การมีประกาศที่ใช้งานมากเกินไปนั้นอาจจะทำให้ประสบการณ์ของผู้ใช้งานนั้นดูแย่ลง โปรดกรุณาพิจารณาการเก็บประกาศที่ล้าสมัยด้วยนะค่ะ"
readConfirmTitle: "ทำเครื่องหมายบอกว่าอ่านแล้วเลยมั้ย?"
readConfirmText: "การดำเนินการนี้จะทำเครื่องหมายเนื้อหาของ \"{title}\" บอกว่าอ่านแล้วนะ"
_initialAccountSetting: _initialAccountSetting:
accountCreated: "คุณได้สร้างบัญชีของคุณสำเร็จเรียบร้อยแล้ว!" accountCreated: "คุณได้สร้างบัญชีของคุณสำเร็จเรียบร้อยแล้ว!"
letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ของคุณกันเถอะ" letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ของคุณกันเถอะ"
@ -1105,7 +1117,7 @@ _initialAccountSetting:
followUsers: "ลองติดตามผู้ใช้บางคนที่คุณอาจจะสนใจเพื่อสร้างไทม์ไลน์ของคุณสิ !" followUsers: "ลองติดตามผู้ใช้บางคนที่คุณอาจจะสนใจเพื่อสร้างไทม์ไลน์ของคุณสิ !"
pushNotificationDescription: "กำลังเปิดใช้งานการแจ้งเตือนแบบพุชจะช่วยให้คุณได้รับการแจ้งเตือนจาก {name} โดยตรงบนอุปกรณ์ของคุณนะ" pushNotificationDescription: "กำลังเปิดใช้งานการแจ้งเตือนแบบพุชจะช่วยให้คุณได้รับการแจ้งเตือนจาก {name} โดยตรงบนอุปกรณ์ของคุณนะ"
initialAccountSettingCompleted: "ตั้งค่าโปรไฟล์เสร็จสมบูรณ์แล้ว!" initialAccountSettingCompleted: "ตั้งค่าโปรไฟล์เสร็จสมบูรณ์แล้ว!"
haveFun: "สนุกกับ {name}!" haveFun: "ขอให้สนุก {name}!"
ifYouNeedLearnMore: "ถ้าหากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีใช้ {ชื่อ} (Misskey) กรุณาไปที่ {link}" ifYouNeedLearnMore: "ถ้าหากคุณต้องการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีใช้ {ชื่อ} (Misskey) กรุณาไปที่ {link}"
skipAreYouSure: "ต้องการข้ามการตั้งค่าโปรไฟล์จริงๆแบบนั้นหรอ?" skipAreYouSure: "ต้องการข้ามการตั้งค่าโปรไฟล์จริงๆแบบนั้นหรอ?"
laterAreYouSure: "ต้องการตั้งค่าโปรไฟล์ในภายหลังจริงๆอย่างงั้นหรอ?" laterAreYouSure: "ต้องการตั้งค่าโปรไฟล์ในภายหลังจริงๆอย่างงั้นหรอ?"
@ -1130,8 +1142,8 @@ _achievements:
earnedAt: "ได้รับเมื่อ" earnedAt: "ได้รับเมื่อ"
_types: _types:
_notes1: _notes1:
title: "เพียงแค่ตั้งค่า msky ของฉัน" title: "just setting up my msky"
description: "โพสต์โน้ตครั้งแรกของคุณ" description: "โพสต์โน้ตแรกของคุณ"
flavor: "ขอให้มีช่วงเวลาที่ดีกับ Misskey นะคะ!" flavor: "ขอให้มีช่วงเวลาที่ดีกับ Misskey นะคะ!"
_notes10: _notes10:
title: "โน้ตบางอย่าง" title: "โน้ตบางอย่าง"
@ -1290,7 +1302,7 @@ _achievements:
_iLoveMisskey: _iLoveMisskey:
title: "ฉันรัก Misskey" title: "ฉันรัก Misskey"
description: "โพสต์ \"I ❤ #Misskey\"" description: "โพสต์ \"I ❤ #Misskey\""
flavor: "ทีมผู้พัฒนา Misskey ได้ขอบคุณสำหรับการสนับสนุนของคุณ!" flavor: "ขอบคุณที่ใช้ Misskey! by ทีมผู้พัฒนา"
_foundTreasure: _foundTreasure:
title: "ล่าสมบัติ" title: "ล่าสมบัติ"
description: "คุณพบสมบัติที่ซ่อนอยู่" description: "คุณพบสมบัติที่ซ่อนอยู่"
@ -1298,7 +1310,7 @@ _achievements:
title: "พักผ่อนสักหน่อย" title: "พักผ่อนสักหน่อย"
description: "ใช้เวลา 30 นาทีบน Misskey" description: "ใช้เวลา 30 นาทีบน Misskey"
_client60min: _client60min:
title: "ไม่พบ \"Miss\" ใน Misskey " title: "ไม่มี \"Miss\" ใน Misskey "
description: "เปิด Misskey ค้างไว้แล้วอย่างน้อย 60 นาที" description: "เปิด Misskey ค้างไว้แล้วอย่างน้อย 60 นาที"
_noteDeletedWithin1min: _noteDeletedWithin1min:
title: "ไม่เป็นไร" title: "ไม่เป็นไร"
@ -1447,7 +1459,7 @@ _sensitiveMediaDetection:
_emailUnavailable: _emailUnavailable:
used: "ที่อยู่อีเมลนี้ได้ถูกใช้ไปแล้ว" used: "ที่อยู่อีเมลนี้ได้ถูกใช้ไปแล้ว"
format: "รูปแบบของที่อยู่อีเมลนี้ไม่ถูกต้อง" format: "รูปแบบของที่อยู่อีเมลนี้ไม่ถูกต้อง"
disposable: "ที่อยู่อีเมลที่ใช้แล้วทิ้งนั้นไม่สามารถใช้ได้" disposable: "ไม่สามารถใช้อีเมลชั่วคราวได้"
mx: "เซิร์ฟเวอร์อีเมลนี้ไม่ถูกต้อง" mx: "เซิร์ฟเวอร์อีเมลนี้ไม่ถูกต้อง"
smtp: "เซิร์ฟเวอร์อีเมลนี้ไม่มีการตอบสนอง" smtp: "เซิร์ฟเวอร์อีเมลนี้ไม่มีการตอบสนอง"
_ffVisibility: _ffVisibility:
@ -1517,9 +1529,9 @@ _aboutMisskey:
contributors: "ผู้สนับสนุนหลัก" contributors: "ผู้สนับสนุนหลัก"
allContributors: "ผู้มีส่วนร่วมทั้งหมด" allContributors: "ผู้มีส่วนร่วมทั้งหมด"
source: "ซอร์สโค้ด" source: "ซอร์สโค้ด"
translation: "รับแปลภาษา Misskey" translation: "แปลภาษา Misskey"
donate: "บริจาคให้กับ Misskey" donate: "บริจาคให้กับ Misskey"
morePatrons: "เราขอขอบคุณสำหรับความช่วยเหลือจากผู้ช่วยอื่นๆ ที่ไม่ได้ระบุไว้ที่นี่นะ ขอขอบคุณ! 🥰" morePatrons: " ขอบคุณทุกท่านที่ร่วมกันช่วยเหลือตลอดมานะคะ 🥰"
patrons: "สมาชิกพันธมิตร" patrons: "สมาชิกพันธมิตร"
_displayOfSensitiveMedia: _displayOfSensitiveMedia:
respect: "ซ่อนสื่อทำเครื่องหมายบอกว่าละเอียดอ่อน" respect: "ซ่อนสื่อทำเครื่องหมายบอกว่าละเอียดอ่อน"
@ -1768,7 +1780,7 @@ _widgets:
photos: "รูปภาพ" photos: "รูปภาพ"
digitalClock: "นาฬิกาดิจิตอล" digitalClock: "นาฬิกาดิจิตอล"
unixClock: "นาฬิกา UNIX" unixClock: "นาฬิกา UNIX"
federation: "สหพันธ์" federation: "Fediration"
instanceCloud: "อินสแตนซ์คลาวด์" instanceCloud: "อินสแตนซ์คลาวด์"
postForm: "แบบฟอร์มการโพสต์" postForm: "แบบฟอร์มการโพสต์"
slideshow: "แสดงภาพนิ่ง" slideshow: "แสดงภาพนิ่ง"
@ -1778,7 +1790,7 @@ _widgets:
serverMetric: "ตัวชี้วัดเซิร์ฟเวอร์" serverMetric: "ตัวชี้วัดเซิร์ฟเวอร์"
aiscript: "AiScript คอนโซล" aiscript: "AiScript คอนโซล"
aiscriptApp: "AiScript แอพ" aiscriptApp: "AiScript แอพ"
aichan: "เอไอ" aichan: "ไอ"
userList: "รายชื่อผู้ใช้" userList: "รายชื่อผู้ใช้"
_userList: _userList:
chooseList: "เลือกรายการ" chooseList: "เลือกรายการ"

View file

@ -259,6 +259,7 @@ messaging: "Mesajlar"
upload: "Yükle" upload: "Yükle"
keepOriginalUploading: "Orijinal görseli koru" keepOriginalUploading: "Orijinal görseli koru"
keepOriginalUploadingDescription: "Orijinal olarak yüklenen görüntüyü olduğu gibi kaydeder. Kapatılırsa, yükleme sırasında web'de görüntülenecek bir sürüm oluşturulur." keepOriginalUploadingDescription: "Orijinal olarak yüklenen görüntüyü olduğu gibi kaydeder. Kapatılırsa, yükleme sırasında web'de görüntülenecek bir sürüm oluşturulur."
fromDrive: "Drive Dosyasından"
fromUrl: "Bağlantıdan" fromUrl: "Bağlantıdan"
uploadFromUrl: "Bağlantıdan yükle" uploadFromUrl: "Bağlantıdan yükle"
uploadFromUrlDescription: "Yüklemek istediğiniz dosyanın bağlantısı" uploadFromUrlDescription: "Yüklemek istediğiniz dosyanın bağlantısı"
@ -305,12 +306,42 @@ renameFolder: "Klasörü Yeniden Adlandır"
deleteFolder: "Klasörü sil" deleteFolder: "Klasörü sil"
addFile: "Dosya ekle" addFile: "Dosya ekle"
emptyDrive: "Sürücü boş" emptyDrive: "Sürücü boş"
emptyFolder: "Bu klasör boş"
unableToDelete: "Silme mümkün değil"
inputNewFileName: "Yeni dosya ismini girin"
inputNewDescription: "Yeni bir başlık gir"
inputNewFolderName: "Yeni klasör ismini girin"
circularReferenceFolder: "Hedef klasör taşınan klasörün bir alt klasörü."
hasChildFilesOrFolders: "Klasör boş olmadığından silinemiyor" hasChildFilesOrFolders: "Klasör boş olmadığından silinemiyor"
copyUrl: "URL'yi kopyala"
rename: "Yeniden adlandır"
avatar: "Avatar"
banner: "Banner"
displayOfSensitiveMedia: "Hassas içerik gösterimi"
whenServerDisconnected: "Sunucu bağlantısı kesildiğinde"
disconnectedFromServer: "Sunucu bağlantısı koptu"
reload: "Yenile"
doNothing: "Bir şey yapma" doNothing: "Bir şey yapma"
reloadConfirm: "Zaman akışı yenilensin mi?" reloadConfirm: "Zaman akışı yenilensin mi?"
watch: "İzle"
unwatch: "İzlemeyi bırak"
accept: "Kabul et"
reject: "Reddet"
normal: "Normal"
instanceName: "Sunucu ismi"
instanceDescription: "Sunucu açıklaması"
maintainerName: "Yönetici ismi" maintainerName: "Yönetici ismi"
maintainerEmail: "Yöneticinin e-postası"
tosUrl: "Hizmet Koşulları Bağlantısı"
thisYear: "Bu yıl"
thisMonth: "Bu ay"
today: "Bugün"
monthX: "{month} ay" monthX: "{month} ay"
pages: "Sayfalar"
integration: "Entegrasyon"
enableRegistration: "Kayıtlara izin ver" enableRegistration: "Kayıtlara izin ver"
basicInfo: "Temel bilgiler"
pinnedUsers: "Sabitlenmiş kullanıcılar"
pinnedNotes: "Sabitlenen" pinnedNotes: "Sabitlenen"
manageAntennas: "Anten ayarları" manageAntennas: "Anten ayarları"
userList: "Listeler" userList: "Listeler"
@ -322,6 +353,7 @@ smtpHost: "Sağlayıcı"
smtpUser: "Kullanıcı Adı" smtpUser: "Kullanıcı Adı"
smtpPass: "Şifre" smtpPass: "Şifre"
notificationSetting: "Bildirim ayarları" notificationSetting: "Bildirim ayarları"
instanceTicker: "Notların sunucu bilgileri"
noCrawleDescription: "Arama motorlarından profilinde, notlarında, sayfalarında vb. dolaşılmamasını ve dizine eklememesini talep et." noCrawleDescription: "Arama motorlarından profilinde, notlarında, sayfalarında vb. dolaşılmamasını ve dizine eklememesini talep et."
clearCache: "Ön belleği temizle" clearCache: "Ön belleği temizle"
onlineUsersCount: "{n} kullanıcı çevrim içi" onlineUsersCount: "{n} kullanıcı çevrim içi"
@ -338,6 +370,7 @@ pushNotificationNotSupported: "Push bildirimleri sunucu veya tarayıcı tarafın
noRole: "Rol bulunamadı" noRole: "Rol bulunamadı"
color: "Renk" color: "Renk"
addMemo: "Kısa not ekle" addMemo: "Kısa not ekle"
icon: "Avatar"
_accountDelete: _accountDelete:
started: "Silme işlemi başlatıldı" started: "Silme işlemi başlatıldı"
_email: _email:

View file

@ -20,6 +20,7 @@ noNotes: "Немає нотаток"
noNotifications: "Немає сповіщень" noNotifications: "Немає сповіщень"
instance: "Інстанс" instance: "Інстанс"
settings: "Налаштування" settings: "Налаштування"
notificationSettings: "Параметри сповіщень"
basicSettings: "Основні налаштування" basicSettings: "Основні налаштування"
otherSettings: "Інші налаштування" otherSettings: "Інші налаштування"
openInWindow: "Відкрити у вікні" openInWindow: "Відкрити у вікні"
@ -48,9 +49,12 @@ delete: "Видалити"
deleteAndEdit: "Видалити й редагувати" deleteAndEdit: "Видалити й редагувати"
deleteAndEditConfirm: "Ви впевнені, що хочете видалити цю нотатку та відредагувати її? Ви втратите всі реакції, поширення та відповіді на неї." deleteAndEditConfirm: "Ви впевнені, що хочете видалити цю нотатку та відредагувати її? Ви втратите всі реакції, поширення та відповіді на неї."
addToList: "Додати до списку" addToList: "Додати до списку"
addToAntenna: "Додати в антени"
sendMessage: "Надіслати повідомлення" sendMessage: "Надіслати повідомлення"
copyRSS: "Скопіювати RSS" copyRSS: "Скопіювати RSS"
copyUsername: "Скопіювати ім’я користувача" copyUsername: "Скопіювати ім’я користувача"
copyUserId: "Копіювати ID користувача"
copyNoteId: "блокнот ID користувача"
searchUser: "Пошук користувачів" searchUser: "Пошук користувачів"
reply: "Відповісти" reply: "Відповісти"
loadMore: "Показати більше" loadMore: "Показати більше"
@ -644,6 +648,7 @@ createNewClip: "Створити нотатку"
unclip: "Незакріплений" unclip: "Незакріплений"
confirmToUnclipAlreadyClippedNote: "Ця нотатка вже включена до кліпу \"{name}\". Ви хочете виключити нотатку з цього кліпу?" confirmToUnclipAlreadyClippedNote: "Ця нотатка вже включена до кліпу \"{name}\". Ви хочете виключити нотатку з цього кліпу?"
public: "Публічний" public: "Публічний"
private: "Приватне"
i18nInfo: "Misskey перекладається на різні мови волонтерами. Ви можете допомогти: {link}" i18nInfo: "Misskey перекладається на різні мови волонтерами. Ви можете допомогти: {link}"
manageAccessTokens: "Керування токенами доступу" manageAccessTokens: "Керування токенами доступу"
accountInfo: "Інформація про акаунт" accountInfo: "Інформація про акаунт"
@ -900,6 +905,7 @@ exploreOtherServers: "Знайти інший сервер"
letsLookAtTimeline: "Перегляд історії" letsLookAtTimeline: "Перегляд історії"
horizontal: "Збоку" horizontal: "Збоку"
youFollowing: "Підписки" youFollowing: "Підписки"
icon: "Аватар"
_achievements: _achievements:
earnedAt: "Відкрито" earnedAt: "Відкрито"
_types: _types:

View file

@ -43,8 +43,8 @@ alreadyFavorited: "allaqachon sevimlilar orasida"
cantFavorite: "sevimlilarga qo'shib bo'lmadi" cantFavorite: "sevimlilarga qo'shib bo'lmadi"
pin: "Profilga qadab qo'yish" pin: "Profilga qadab qo'yish"
unpin: "Profildan olib tashlash" unpin: "Profildan olib tashlash"
copyContent: "kontentni nusxalash" copyContent: "Tarkibini nusxalash"
copyLink: "Havolani nuxalash" copyLink: "Havolani nusxalash"
delete: "O'chirib tashlash" delete: "O'chirib tashlash"
deleteAndEdit: "O'chirish va tahrirlash" deleteAndEdit: "O'chirish va tahrirlash"
deleteAndEditConfirm: "O'chirib, tahrirlamoqchiligingizga ishonchingiz komilmi? Siz bu qaydga tegishli barcha reaktsiyalar va javoblarni yo'qotasiz." deleteAndEditConfirm: "O'chirib, tahrirlamoqchiligingizga ishonchingiz komilmi? Siz bu qaydga tegishli barcha reaktsiyalar va javoblarni yo'qotasiz."
@ -156,6 +156,7 @@ addEmoji: "Emoji qo'shish"
settingGuide: "Tavsiya qilingan sozlamalar" settingGuide: "Tavsiya qilingan sozlamalar"
cacheRemoteFiles: "Tashqi fayllarni keshlash" cacheRemoteFiles: "Tashqi fayllarni keshlash"
cacheRemoteFilesDescription: "Ushbu sozlama o'chirilgan bo'lsa tashqi fayllar bevosita tashqi serverdan yuklanadi. Buni o'chirish ombor ishlatilishini kamaytiradi, lekin traffikni ko'paytiradi, chunki eskizlar generatsiya qilinmaydi." cacheRemoteFilesDescription: "Ushbu sozlama o'chirilgan bo'lsa tashqi fayllar bevosita tashqi serverdan yuklanadi. Buni o'chirish ombor ishlatilishini kamaytiradi, lekin traffikni ko'paytiradi, chunki eskizlar generatsiya qilinmaydi."
youCanCleanRemoteFilesCache: "Fayl menejeridagi 🗑️ tugmasi yordamida barcha keshlarni oʻchirib tashlashingiz mumkin."
cacheRemoteSensitiveFiles: "Tashqi fayllarni keshlash" cacheRemoteSensitiveFiles: "Tashqi fayllarni keshlash"
cacheRemoteSensitiveFilesDescription: "Bu sozlama oʻchiq boʻlsa, \"barcha ko'rishi mumkin bo'lmagan\" fayllar keshlashsiz toʻgʻridan-toʻgʻri masofaviy serverdan yuklanadi." cacheRemoteSensitiveFilesDescription: "Bu sozlama oʻchiq boʻlsa, \"barcha ko'rishi mumkin bo'lmagan\" fayllar keshlashsiz toʻgʻridan-toʻgʻri masofaviy serverdan yuklanadi."
flagAsBot: "Ushbu akkauntni bot sifatida belgilash" flagAsBot: "Ushbu akkauntni bot sifatida belgilash"
@ -246,6 +247,7 @@ newPasswordRetype: "Yangi parolni boshqatdan tering"
attachFile: "Fayl biriktirish" attachFile: "Fayl biriktirish"
more: "Ko'proq!" more: "Ko'proq!"
featured: "ta'kidlash" featured: "ta'kidlash"
usernameOrUserId: "Foydalanuvchi nomi yoki identifikatori"
noSuchUser: "Foydalanuvchi topilmadi" noSuchUser: "Foydalanuvchi topilmadi"
lookup: "So'rov" lookup: "So'rov"
announcements: "Bildirishnomalar" announcements: "Bildirishnomalar"
@ -259,7 +261,10 @@ saved: "Saqlandi"
messaging: "Suhbat" messaging: "Suhbat"
upload: "Yuklash" upload: "Yuklash"
keepOriginalUploading: "Asl rasmni saqlang" keepOriginalUploading: "Asl rasmni saqlang"
keepOriginalUploadingDescription: "Rasmlarni yuklashda asl nusxasini saqlaydi. Agar o'chirilgan bo'lsa, brauzer yuklangandan keyin nashr qilish uchun rasm yaratadi."
fromDrive: "Drive orqali"
fromUrl: "URL dan" fromUrl: "URL dan"
uploadFromUrl: "URL orqali yuklash"
uploadFromUrlDescription: "Yuklamoqchi bo'lgan faylingizga havola" uploadFromUrlDescription: "Yuklamoqchi bo'lgan faylingizga havola"
uploadFromUrlRequested: "yuklab olish so'ralgan" uploadFromUrlRequested: "yuklab olish so'ralgan"
uploadFromUrlMayTakeTime: "Yuklash tugallanishi uchun biroz vaqt ketishi mumkin." uploadFromUrlMayTakeTime: "Yuklash tugallanishi uchun biroz vaqt ketishi mumkin."
@ -275,6 +280,7 @@ basicNotesBeforeCreateAccount: "Muhim qaydlar"
termsOfService: "Foydalanish shartlari" termsOfService: "Foydalanish shartlari"
start: "Boshlash" start: "Boshlash"
home: "Bosh sahifa" home: "Bosh sahifa"
remoteUserCaution: "Bu foydalanuvchi uzoqda bo'lganligi sababli, ko'rsatilgan ma'lumotlar to'liq bo'lmasligi mumkin."
activity: "Faollik" activity: "Faollik"
images: "Rasmlar" images: "Rasmlar"
image: "Rasm" image: "Rasm"
@ -308,11 +314,13 @@ unableToDelete: "O'chirilmadi"
inputNewFileName: "Yangi fayl nomini kiriting" inputNewFileName: "Yangi fayl nomini kiriting"
inputNewDescription: "Iltimos, yangi sarlavha kiriting." inputNewDescription: "Iltimos, yangi sarlavha kiriting."
inputNewFolderName: "Yangi papka nomini kiriting" inputNewFolderName: "Yangi papka nomini kiriting"
circularReferenceFolder: "Belgilangan papka siz ko'chirmoqchi bo'lgan jildning pastki jildidir."
hasChildFilesOrFolders: "Bu papka boʻsh emas va uni oʻchirib boʻlmaydi." hasChildFilesOrFolders: "Bu papka boʻsh emas va uni oʻchirib boʻlmaydi."
copyUrl: "Bog'lamadan nusxa olish" copyUrl: "Bog'lamadan nusxa olish"
rename: "Qayta nomlash" rename: "Qayta nomlash"
avatar: "Avatar" avatar: "Avatar"
banner: "Banner" banner: "Banner"
displayOfSensitiveMedia: "Nozik kontentni ko'rish"
whenServerDisconnected: "server bilan aloqa uzilganda" whenServerDisconnected: "server bilan aloqa uzilganda"
disconnectedFromServer: "Server bilan ulanish uzulib qoldi" disconnectedFromServer: "Server bilan ulanish uzulib qoldi"
reload: "Yangilash" reload: "Yangilash"
@ -340,15 +348,21 @@ connectService: "Ulash"
disconnectService: "Uzish" disconnectService: "Uzish"
enableLocalTimeline: "Mahalliy vaqt mintaqasini yoqing" enableLocalTimeline: "Mahalliy vaqt mintaqasini yoqing"
enableGlobalTimeline: "Global vaqt mintaqasini yoqing" enableGlobalTimeline: "Global vaqt mintaqasini yoqing"
disablingTimelinesInfo: "Administratorlar va Moderatorlar har doim barcha vaqt jadvallariga kirish huquqiga ega bo'ladilar, hatto ular yoqilmagan bo'lsa ham."
registration: "Ro'yxatdan o'tish" registration: "Ro'yxatdan o'tish"
enableRegistration: "Ro'yxatdan o'tishni yoqing" enableRegistration: "Ro'yxatdan o'tishni yoqing"
invite: "Taklif qilish" invite: "Taklif qilish"
driveCapacityPerLocalAccount: "Har bir mahalliy foydalanuvchi uchun disk maydoni"
driveCapacityPerRemoteAccount: "Har bir masofaviy foydalanuvchi uchun disk maydoni"
inMb: "Megabaytlarda" inMb: "Megabaytlarda"
iconUrl: "Ikonkaning URL manzili (masalan: favicon)" iconUrl: "Ikonkaning URL manzili (masalan: favicon)"
bannerUrl: "Banner URLi"
backgroundImageUrl: "Fon rasmi URL manzili" backgroundImageUrl: "Fon rasmi URL manzili"
basicInfo: "Asosiy ma'lumot" basicInfo: "Asosiy ma'lumot"
pinnedUsers: "Qadalgan foydalanuvchilar" pinnedUsers: "Qadalgan foydalanuvchilar"
pinnedUsersDescription: "Har bir qatorga bitta foydalanuvchi nomini kiriting. Bu yerda sanab oʻtilgan foydalanuvchilar “Oʻrganish” yorligʻiga bogʻlanadi."
pinnedPages: "Qadalgan Sahifalar" pinnedPages: "Qadalgan Sahifalar"
pinnedClipId: "Qadalgan xabar IDsi"
pinnedNotes: "Qadalgan qayd" pinnedNotes: "Qadalgan qayd"
hcaptcha: "hCaptcha" hcaptcha: "hCaptcha"
enableHcaptcha: "hCaptchani yoqish" enableHcaptcha: "hCaptchani yoqish"
@ -358,19 +372,37 @@ recaptcha: "reCAPTCHA"
enableRecaptcha: "reCAPTCHA ni yoqish" enableRecaptcha: "reCAPTCHA ni yoqish"
recaptchaSiteKey: "Sayt kaliti" recaptchaSiteKey: "Sayt kaliti"
recaptchaSecretKey: "Maxfiy kalit" recaptchaSecretKey: "Maxfiy kalit"
turnstile: "Turniket"
enableTurnstile: "Turniketni yoqish"
turnstileSiteKey: "Sayt kaliti" turnstileSiteKey: "Sayt kaliti"
turnstileSecretKey: "Maxfiy kalit" turnstileSecretKey: "Maxfiy kalit"
avoidMultiCaptchaConfirm: "\nBir nechta Captcha tizimlaridan foydalanish ular o'rtasida noqulaylik olib kelishi mumkin. Hozirda faol bo'lgan boshqa Captcha tizimlarini o'chirib qo'ymoqchimisiz? Agar siz ularning faol bo'lishini istasangiz, bekor qilish tugmasini bosing."
antennas: "Antennalar" antennas: "Antennalar"
manageAntennas: "Antennalarni boshqarish" manageAntennas: "Antennalarni boshqarish"
name: "Ism" name: "Ism"
antennaSource: "Antenna manbai" antennaSource: "Antenna manbai"
antennaKeywords: "Kalit so'zni qabul qilish" antennaKeywords: "Kalit so'zni qabul qilish"
antennaExcludeKeywords: "Istisno qilingan kalit so'zlar"
antennaKeywordsDescription: "VA sharti uchun bo'shliqlar bilan yoki YOKI sharti uchun qator uzilishlari bilan ajrating."
notifyAntenna: "Yangi qaydlar haqida menga xabar bering" notifyAntenna: "Yangi qaydlar haqida menga xabar bering"
withFileAntenna: "Faqatgina fayli bor qaydlar"
enableServiceworker: "Bildirish nomalarni olish"
antennaUsersDescription: "Har bir foydalunvchi nomini alohida qatorga yozing"
caseSensitive: "Katta-kichik harfni farqlash"
withReplies: "Javob yo'llash"
connectedTo: "Quyidagi akkountlarga ulangan" connectedTo: "Quyidagi akkountlarga ulangan"
silence: "Sukunat" notesAndReplies: "Qaydlar va javoblar"
withFiles: "Fayllar"
silence: "Jim qilish"
silenceConfirm: "Rostdan ham ushbu foydalanuvchini jim qilmoqchimisiz?"
unsilence: "Jim qilishni bekor qilish"
unsilenceConfirm: "Rostdan ham ushbu foydalanuvchini ovozsiz \nqilmoqchimisiz?"
popularUsers: "Mashhur foydalanuvchilar." popularUsers: "Mashhur foydalanuvchilar."
recentlyUpdatedUsers: "Yaqinda ro'yxatdan o'tgan foydalanuvchilar"
recentlyRegisteredUsers: "Yaqinda ro'yxatdan o'tgan foydalanuvchilar" recentlyRegisteredUsers: "Yaqinda ro'yxatdan o'tgan foydalanuvchilar"
recentlyDiscoveredUsers: "Yangi foydalanuvchilar"
exploreUsersCount: "{count} ta foydalanuvchi bor" exploreUsersCount: "{count} ta foydalanuvchi bor"
exploreFediverse: "Fediversni ko'rib chiqing"
popularTags: "Ommabop teglar" popularTags: "Ommabop teglar"
userList: "Ro'yxatlar" userList: "Ro'yxatlar"
about: "Haqida" about: "Haqida"
@ -381,12 +413,25 @@ token: "Tasdiqlash"
totp: "Autentifikatsiya ilovasi" totp: "Autentifikatsiya ilovasi"
totpDescription: "Bir martalik parollarni kiritish uchun autentifikatsiya ilovasidan foydalaning" totpDescription: "Bir martalik parollarni kiritish uchun autentifikatsiya ilovasidan foydalaning"
moderator: "Moderator" moderator: "Moderator"
moderation: "Moderatsiya"
nUsersMentioned: "{n} tomonidan chop etilgan" nUsersMentioned: "{n} tomonidan chop etilgan"
securityKeyAndPasskey: "Xavfsizlik kaliti va maxfiy so'z"
securityKey: "Xavfsizlik kaliti"
lastUsed: "Oxirgi marta foydalanilgan"
lastUsedAt: "Oxirgi marta {t} da foydalanilgan"
unregister: "ro'yxatdan chiqarish"
passwordLessLogin: "Parolsiz kirshni sozlash"
passwordLessLoginDescription: "Parolsiz kirish"
resetPassword: "Parolni tiklash" resetPassword: "Parolni tiklash"
newPasswordIs: "Yangi parolingiz {password}"
reduceUiAnimation: "Interfeysdagi animatsiyani kamaytirish"
share: "Yuborish" share: "Yuborish"
notFound: "Topilmadi" notFound: "Topilmadi"
notFoundDescription: "Ushbu sahifa topilmadi"
uploadFolder: "Jildni yuklash" uploadFolder: "Jildni yuklash"
cacheClear: "Keshni tozalash" cacheClear: "Keshni tozalash"
markAsReadAllNotifications: "Bildirishnomalarni o'qilgan deb belgilash"
markAsReadAllUnreadNotes: "Barch xabarlarni oq'ilgan deb belgilash"
markAsReadAllTalkMessages: "Barcha suhbatlarni o'qilgan deb belgilang" markAsReadAllTalkMessages: "Barcha suhbatlarni o'qilgan deb belgilang"
help: "Yordam" help: "Yordam"
inputMessageHere: "Xabar kiriting" inputMessageHere: "Xabar kiriting"
@ -399,6 +444,11 @@ text: "Matn"
enable: "Yoqish" enable: "Yoqish"
next: "Keyingisi" next: "Keyingisi"
retype: "Qayta kiriting" retype: "Qayta kiriting"
noteOf: "{user} tomonidan joylandi\n"
quoteAttached: "Iqtibos"
quoteQuestion: "Iqtibos sifatida qo'shilsinmi?"
noMessagesYet: "Bu yerda xabarlar yo'q"
newMessageExists: "Yangi xabarlar bor"
onlyOneFileCanBeAttached: "Faqat bitta faylni biriktirish mumkin" onlyOneFileCanBeAttached: "Faqat bitta faylni biriktirish mumkin"
signinRequired: "Davom etishdan oldin ro'yhatdan o'tishingiz yoki tizimga kirishingiz kerak" signinRequired: "Davom etishdan oldin ro'yhatdan o'tishingiz yoki tizimga kirishingiz kerak"
invitations: "Taklif qilish" invitations: "Taklif qilish"
@ -418,10 +468,16 @@ signinWith: "{x} bilan tizimga kirish"
signinFailed: "Tizimga kirishda xatolik yuz berdi. Iltimos, foydalanuvchi nomingiz va parolingizni tekshiring." signinFailed: "Tizimga kirishda xatolik yuz berdi. Iltimos, foydalanuvchi nomingiz va parolingizni tekshiring."
or: "yoki" or: "yoki"
language: "til" language: "til"
uiLanguage: "Interfeys tili"
aboutX: "{x} haqida" aboutX: "{x} haqida"
emojiStyle: "Emoji ko'rinishi"
native: "Mahalliy"
disableDrawer: "Slayd menyusidan foydalanmang"
showNoteActionsOnlyHover: "Eslatma amallarini faqat sichqonchani olib borganda korsatish" showNoteActionsOnlyHover: "Eslatma amallarini faqat sichqonchani olib borganda korsatish"
noHistory: "Tarix yo'q" noHistory: "Tarix yo'q"
signinHistory: "kirish tarixi" signinHistory: "kirish tarixi"
enableAdvancedMfm: "MFMni faollashtirish"
doing: "Bajarilmoqda..."
category: "kategoriya" category: "kategoriya"
tags: "teg" tags: "teg"
docSource: "Ushbu hujjatning manbasi" docSource: "Ushbu hujjatning manbasi"
@ -431,8 +487,10 @@ regenerate: "regeneratsiya"
fontSize: "shrift hajmi" fontSize: "shrift hajmi"
limitTo: "{x} gacha" limitTo: "{x} gacha"
noFollowRequests: "obuna uchun so'rov yo'q" noFollowRequests: "obuna uchun so'rov yo'q"
openImageInNewTab: "Rasmni boshqa oynada ochish"
dashboard: "Boshqaruv paneli" dashboard: "Boshqaruv paneli"
local: "Mahalliy" local: "Mahalliy"
remote: "masofaviy"
total: "Jami" total: "Jami"
weekOverWeekChanges: "Oxirgi haftadagi o'zgarishlar" weekOverWeekChanges: "Oxirgi haftadagi o'zgarishlar"
dayOverDayChanges: "Kecha bo'lgan o'zgarishlar" dayOverDayChanges: "Kecha bo'lgan o'zgarishlar"
@ -444,18 +502,56 @@ promote: "targ'ib qilish"
numberOfDays: "kunlar soni" numberOfDays: "kunlar soni"
hideThisNote: "bu eslatmani yashiring" hideThisNote: "bu eslatmani yashiring"
showFeaturedNotesInTimeline: "Tanlangan qaydlarni Timelineda ko'rsatish" showFeaturedNotesInTimeline: "Tanlangan qaydlarni Timelineda ko'rsatish"
objectStorage: "ob'ektni saqlash"
useObjectStorage: "Ob'ektni saqlashdan foydalaning"
objectStorageBaseUrl: "Asosiy URL" objectStorageBaseUrl: "Asosiy URL"
objectStorageBaseUrlDesc: "Malumot va foydalanish uchun URL. Agar siz CDN yoki proksi-serverdan foydalanayotgan bo'lsangiz, URL manzili, S3: 'https://<bucket>.s3.amazonaws.com', GCS va boshqalar: 'https://storage.googleapis.com/<bucket>'."
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Iltimos, foydalaniladigan xizmatning bucket nomini belgilang."
objectStoragePrefix: "Prefix"
objectStorageEndpoint: "Endpoint"
objectStorageRegion: "Mintaqa" objectStorageRegion: "Mintaqa"
objectStorageRegionDesc: "'xx-east-1' kabi mintaqani belgilang. Agar xizmatingizda mintaqa tushunchasi bo'lmasa, `us-east-1` dan foydalaning. AWS konfiguratsiya fayllari yoki muhit oʻzgaruvchilariga havola qilishda boʻsh qoldiring."
objectStorageUseSSL: "SSL dan foydalaning" objectStorageUseSSL: "SSL dan foydalaning"
objectStorageUseSSLDesc: "API ulanishlari uchun https dan foydalanmasangiz, belgini olib tashlang"
objectStorageUseProxy: "Proksi-serverdan foydalaning"
objectStorageUseProxyDesc: "Proksi-serverdan foydalanishni xohlamasangiz, uni o'chiring"
objectStorageSetPublicRead: "Yuklashda \"public-read\" ni o'rnating"
serverLogs: "Server protokoli"
deleteAll: "Hammasini o'chirib tashlash"
showFixedPostForm: "Taqdim etish shaklini vaqt jadvalining yuqori qismida ko'rsating"
newNoteRecived: "Yangi qaydlar mavjud emas"
sounds: "Tovushlar" sounds: "Tovushlar"
sound: "ovoz" sound: "ovoz"
listen: "Eshitish"
none: "Hechnima" none: "Hechnima"
showInPage: "Sahifada ko'rsatish"
popout: "Oching"
volume: "Ovoz balandligi" volume: "Ovoz balandligi"
details: "Batafsil" details: "Batafsil"
chooseEmoji: "Emojini tanlang"
unableToProcess: "Opertsiya bajarilmadi"
recentUsed: "Oxirgi ishlatilganlar"
install: "Ornatish"
uninstall: "Ochirib tashlash"
installedApps: "O'rnatilgan ilovalar"
nothing: "Hech narsa yo'q"
installedDate: "O'rnatish sanasi"
lastUsedDate: "Oxirgi marta ishlatilgan sana"
state: "Holat"
sort: "saralamoq"
ascendingOrder: "O'sish bo'yicha"
descendingOrder: "Kamayish bo'yicha"
scratchpad: "Qoralama"
output: "Chiqish" output: "Chiqish"
script: "Skript"
disablePagesScript: "AiScriptni sahifalardan o'chirish"
updateRemoteUser: "Masofaviy foydalanuvchi ma'lumotlarini yangilash"
deleteAllFiles: "barcha fayllarni o'chirish" deleteAllFiles: "barcha fayllarni o'chirish"
deleteAllFilesConfirm: "Barcha fayllar oʻchirilsinmi?" deleteAllFilesConfirm: "Barcha fayllar oʻchirilsinmi?"
removeAllFollowing: "Barcha obunalarni o'chirish"
userSuspended: "Bu foydalanuvchi muzlatilgan." userSuspended: "Bu foydalanuvchi muzlatilgan."
userSilenced: "Ushbu foydalanuvchi jim qilingan"
yourAccountSuspendedTitle: "akkaunt muzlatilgan" yourAccountSuspendedTitle: "akkaunt muzlatilgan"
yourAccountSuspendedDescription: "Ushbu akkaunt serverning xizmat ko'rsatish shartlarini buzish kabi sabablarga ko'ra to'xtatilgan. Tafsilotlar uchun administratoringizga murojaat qiling. Iltimos, yangi akkaunt yaratmang." yourAccountSuspendedDescription: "Ushbu akkaunt serverning xizmat ko'rsatish shartlarini buzish kabi sabablarga ko'ra to'xtatilgan. Tafsilotlar uchun administratoringizga murojaat qiling. Iltimos, yangi akkaunt yaratmang."
tokenRevoked: "token yaroqsiz" tokenRevoked: "token yaroqsiz"
@ -465,51 +561,301 @@ accountDeletedDescription: "Bu akkaunt oʻchirildi."
menu: "Menyu" menu: "Menyu"
divider: "Ajratrmoq" divider: "Ajratrmoq"
addItem: "Element qo'shish" addItem: "Element qo'shish"
rearrange: "Qayta saralash"
inboxUrl: "Qabul qilingan xabarlar URL manzili"
serviceworkerInfo: "bildirishnomalar uchun yoqilgan bo'lishi kerak." serviceworkerInfo: "bildirishnomalar uchun yoqilgan bo'lishi kerak."
deletedNote: "Oʻchirilgan post" deletedNote: "Oʻchirilgan post"
visibility: "Ko'rinishi"
poll: "So'ro'vnoma"
useCw: "Kontentni yashirish"
enablePlayer: "Video pleyerni ochish"
disablePlayer: "Video pleyerni yopish"
expandTweet: "Xabarni kengaytirish"
themeEditor: "Rang sxemasi muharriri" themeEditor: "Rang sxemasi muharriri"
description: "tavsif"
describeFile: "sarlavha qo'shing" describeFile: "sarlavha qo'shing"
enterFileDescription: "sarlavha kiriting" enterFileDescription: "sarlavha kiriting"
author: "muallif" author: "muallif"
leaveConfirm: "Sizda saqlanmagan oʻzgarishlar bor. Bekor qilinsinmi?" leaveConfirm: "Sizda saqlanmagan oʻzgarishlar bor. Bekor qilinsinmi?"
manage: "Administratsiya"
plugins: "Kengaytmalar, plaginlar"
preferencesBackups: "Sozlamalarni zahiralash"
useBlurEffectForModal: "Modal uchun xiralashtirish effektidan foydalaning" useBlurEffectForModal: "Modal uchun xiralashtirish effektidan foydalaning"
useFullReactionPicker: "Katta oynada reaksiya tanlash"
width: "kengligi" width: "kengligi"
height: "balandligi" height: "balandligi"
large: "Katta" large: "Katta"
medium: "O'rta"
small: "kichik" small: "kichik"
generateAccessToken: "Kirish tokenini yaratish"
permission: "Ruxsatlar"
enableAll: "Yoqish" enableAll: "Yoqish"
disableAll: "hammasini o'chirib qo'ying" disableAll: "hammasini o'chirib qo'ying"
tokenRequested: "Hisobga kirish"
pluginTokenRequestedDescription: "Bu plagin shu yerda belgilanganlarga qodir bo'ladi"
notificationType: "Bildirishnoma turi"
edit: "Tahrirlash" edit: "Tahrirlash"
emailServer: "Email server"
email: "Email" email: "Email"
emailAddress: "E-pochtangiz:"
smtpConfig: "SMTP server sozlamalari"
smtpHost: "Host" smtpHost: "Host"
smtpPort: "Port"
smtpUser: "Foydalanuvchi nomi" smtpUser: "Foydalanuvchi nomi"
smtpPass: "Parol" smtpPass: "Parol"
testEmail: "Email jo'natmani testlash"
userSaysSomething: "{name} nimadir dedi"
makeActive: "Faol"
display: "Displey"
copy: "Nusxa olish" copy: "Nusxa olish"
metrics: "Metrikalar"
overview: "Umumiy ma'lumot"
logs: "Jurnallar"
delayed: "Kechiktirildi"
database: "Ma'lumotlar bazasi"
channel: "Kanallar"
create: "Yaratish"
notificationSetting: "Bildirishnoma sozlamalari" notificationSetting: "Bildirishnoma sozlamalari"
notificationSettingDesc: "Ko'rsatish uchun bildirishnoma turlarini tanlang."
useGlobalSetting: "Global sozlamalardan foydalanish"
other: "Qoshimcha" other: "Qoshimcha"
regenerateLoginToken: "Kirish tokenini qayta yaratish"
setMultipleBySeparatingWithSpace: "Bo'sh joy qoldirib, bir necha ma'lumot kiritish mumkin"
fileIdOrUrl: "Fayl ID'si yoki URL havolasi"
behavior: "Hatti-harakatlar" behavior: "Hatti-harakatlar"
sample: "Namuna" sample: "Namuna"
abuseReports: "Shikoyatlar"
reportAbuse: "Shikoyat qilish"
reportAbuseOf: "{name} ustidan shikoyat qilish"
abuseReported: "Shikoyatingiz yetkazildi. Ma'lumot uchun rahmat."
reporter: "Shikoyat qiluvchi"
reporteeOrigin: "Xabarning kelib chiqishi"
reporterOrigin: "Xabarchining joylashuvi"
forwardReport: "Xabarni masofadagi serverga yuborish"
forwardReportIsAnonymous: "Sizning yuborayotgan xabaringiz o'z akkountingiz emas balki anonim tarzda qoladi"
send: "Yuborish"
abuseMarkAsResolved: "Yuborilgan xabarni hal qilingan deb belgilash"
openInNewTab: "Yangi tab da ochish"
openInSideView: "Yon panelda ochish"
defaultNavigationBehaviour: "Standart navigatsiya harakati"
editTheseSettingsMayBreakAccount: "Bu sozlamalarni o'zgartirish hisobingizga zarar yetkazishi mumkin."
waitingFor: "{x}ni kutayapman"
random: "Tasodifiy"
system: "Tizim"
switchUi: "Interfeysni almashtirish"
desktop: "Brauzer rejimi"
clip: "Klip"
createNew: "Yangi yaratish"
optional: "Ixtiyoriy"
createNewClip: "Yangi klip yaratish"
unclip: "qirqish\n"
confirmToUnclipAlreadyClippedNote: "Ushbu xat allaqachon \"{name}\" klipga tegishli. Uni ushbu klipdan olib tashlashni xohlaysizmi?"
public: "Ommaviy" public: "Ommaviy"
i18nInfo: "Misskey bir qancha volontyorlar yordamida bir qancha tillarga tarjima qilingan. Ushbu {link} orqali ularga yordam berishingiz mumkin."
manageAccessTokens: "Kirish tokenlarini boshqarish"
accountInfo: "Akkount haqida ma'lumot"
notesCount: "Xatlar soni"
repliesCount: "Yuborilgan javoblar soni"
renotesCount: "Qayta yuborilgan xatlar soni"
repliedCount: "Qabul qilingan javoblar soni"
renotedCount: "Qayta yuborilgan xatlar soni"
followingCount: "Obuna bo'lingan akkountlar soni"
followersCount: "Obunachilar soni"
sentReactionsCount: "Yuborilgan reaksiyalar soni"
receivedReactionsCount: "Qabul qilingan reaksiyalar soni"
pollVotesCount: "Berilgan ovozlar soni"
pollVotedCount: "Qabul qilingan ovozlar soni"
yes: "Ha"
no: "Yo'q"
driveFilesCount: "Diskdagi fayllar soni"
driveUsage: "Ishlatilgan disk joyi"
noCrawleDescription: "Qidiruv tizimlari sizning profilingiz, sahifalaringiz, xatlaringiz va hokazolarni belgilamasligi uchun so'rov yuborish"
lockedAccountInfo: "Xatlaringizni faqatgina obunachilaringizga ko'rsatishni xohlasangiz unda \"Faqat Obunachilar uchun\" xususiyatini yoqishingiz lozim. Bo'lmasa sizning yozgan xatlaringiz hammaga ko'rinadi."
alwaysMarkSensitive: "Avvaldan ta'sirchan kontent deb belgilash"
loadRawImages: "Thumbnaillarsiz Original rasmni yuklash"
disableShowingAnimatedImages: "Animatsiyali rasmlarni ko'rsatmaslik"
verificationEmailSent: "Emailingizga tasdiqlash xabari yuborildi. Iltimos linkda ko'rsatilgan amallarga rioya qiling"
notSet: "Sozlanmagan"
emailVerified: "Elektron pochta manzili tasdiqlandi"
pageLikesCount: "Sahifadagi like'lar soni"
contact: "aloqa uchun manzil"
useSystemFont: "Tizimdagi standart shriftidan foydalaning"
clips: "Klip"
experimentalFeatures: "eksperimental xususiyatlar"
experimental: "eksperimental"
developer: "Dasturchi"
makeExplorable: "Akkauntingizni topishni osonlashtiring"
duplicate: "Dublikat"
left: "Chap(dagi)"
center: "Markaz"
wide: "Keng"
narrow: "Tor"
reloadToApplySetting: "Bu sozlamalar sahifa yangilangandagina kuchga kiradi. Hozir yangilashni istaysizmi?"
needReloadToApply: "Sahifani yangilash talab etiladi."
clearCache: "Keshni tozalash" clearCache: "Keshni tozalash"
onlineUsersCount: "Faol userlar" onlineUsersCount: "Faol userlar"
nUsers: "{n} ta foydalanuvchi"
myTheme: "Mening rang sxemam" myTheme: "Mening rang sxemam"
backgroundColor: "Fon" backgroundColor: "Fon"
accentColor: "Urg'u" accentColor: "Urg'u"
textColor: "Matn" textColor: "Matn"
advanced: "Murakkab"
advancedSettings: "Qo'shimcha sozlashlar"
value: "Qiymati"
createdAt: "Yaratilish vaqti"
updatedAt: "yangilangan sana"
saveConfirm: "O'zgartirishni saqlash?"
deleteConfirm: "o'chirishni tasdiqlash"
invalidValue: "noto'g'ri qiymat"
registry: "ro'yhatga olish"
closeAccount: "hisobni yopish / profilni yopish"
currentVersion: "joriy versiya"
latestVersion: "so'ngi versiya"
youAreRunningUpToDateClient: "siz so'ngi versiyali ilovani ishlatyapsiz"
newVersionOfClientAvailable: "Mijozning yangi versiyasi mavjud."
usageAmount: "foydalanish miqdori"
capacity: "sig'im"
inUse: "allaqachon band"
editCode: "kodni tahrirlash"
apply: "Ilova"
receiveAnnouncementFromInstance: "Serverdan bildirishnomalarni oling"
emailNotification: "E-mail xabarlari"
publish: "Chiqarish"
inChannelSearch: "Kanal qidirish"
useReactionPickerForContextMenu: "kontekst menyusi uchun reaktsiya tanlash vositasidan foydalaning"
typingUsers: "{users} yozmoqda"
jumpToSpecifiedDate: "Muayyan sanaga o'tish"
clear: "aniq"
markAllAsRead: "hammasini o'qilgan deb belgilang"
goBack: "qaytish"
unlikeConfirm: "Un like qilishni xohlaysizmi?"
fullView: "to'liq ko'rish"
quitFullView: "Toʻliq koʻrishdan chiqish"
addDescription: "Tavsif qo'shing"
info: "Haqida" info: "Haqida"
userInfo: "Foydalanuvchi ma'lumotlari"
unknown: "aniq emas"
onlineStatus: "onlayn holat"
hideOnlineStatus: "onlayn holatni yashirish"
hideOnlineStatusDescription: "Onlayn statusingizni yashirish, qidiruv kabi baʼzi funksiyalardan foydalanish imkoniyatini kamaytirishi mumkin."
online: "onlayn"
active: "Aktiv"
offline: "oflayn"
notRecommended: "tavsiya etilmaydi"
selectAccount: "Akkauntni tanlang"
switchAccount: "akkauntni almashtirish"
enabled: "yaroqli"
disabled: "yaroqsiz"
quickAction: "tezkor harakat"
user: "Foydalanuvchilar" user: "Foydalanuvchilar"
administration: "Administratsiya"
accounts: "akkaunt"
switch: "almashtirish"
noBotProtectionWarning: "Bot himoyasi sozlanmagan."
configure: "sozlamoq"
postToGallery: "Yangi galleriya posti"
gallery: "Galereya"
recentPosts: "So'nggi postlar"
popularPosts: "Mashhur postlar"
shareWithNote: "Eslatmani ulashish"
ads: "Reklama"
startingperiod: "Boshlanish davri"
memo: "Eslatma"
priority: "Ustuvorlik"
high: "Yuqori"
middle: "O'rta"
low: "Quyi"
ratio: "nisbat"
previewNoteText: "Razm solish"
customCss: "Maxsus CSS"
global: "Global" global: "Global"
squareAvatars: "Kvadrat avatarkalar" squareAvatars: "Kvadrat avatarkalar"
sent: "Yuborish"
received: "Qabul qilingan"
searchResult: "Qidiruv natijalari"
hashtags: "Hashteglar"
troubleshooting: "Muammolarni bartaraf qilish"
useBlurEffect: "Interfeysda xiralashtiruvchi effektlardan foydalanish"
learnMore: "Batafsilroq"
misskeyUpdated: "Misskey yangilandi!"
whatIsNew: "O'zgarishlarni ko'rish"
translate: "Tarjima qilish"
translatedFrom: "{x} tilidan tarjima qilindi"
devMode: "Dasturchi rejimi"
lastCommunication: "Oxirgi muloqot"
resolved: "Hal qilingan"
unresolved: "Hal qilinmagan"
breakFollow: "Obunachini o'chirish"
breakFollowConfirm: "Obunachini o'chirmoqchimisiz?"
itsOn: "Yoqilgan"
itsOff: "O'chirilgan"
on: "Yoqish"
off: "O'chirish"
emailRequiredForSignup: "Ro'yxatdan o'tish uchun email talab qilish"
unread: "Oʻqilmagan xabarlar"
filter: "Filter"
controlPanel: "Boshqaruv paneli"
manageAccounts: "Hisobni boshqarish"
classic: "Klassik"
hide: "Yashirish"
searchByGoogle: "Izlash" searchByGoogle: "Izlash"
indefinitely: "Hech qachon" indefinitely: "Hech qachon"
file: "Fayllar" file: "Fayllar"
recommended: "Tavsiya qilingan"
check: "Tekshirish"
requireAdminForView: "Ko'rish uchun adminstrator hisobi bilan tizimga kirgan bo'lishingiz kerak."
isSystemAccount: "Ushbu hisob tizim tomonidan avtomatik tarzda yaratilgan va boshqariladi."
typeToConfirm: "Ushbu amalni bajarish uchun {x}ni kiriting"
deleteAccount: "Hisobni o'chirish"
document: "hujjat"
numberOfPageCache: "Sahifa keshlar soni"
logoutConfirm: "Chiqishni xohlaysizmi?"
lastActiveDate: "oxirgi foydalanish sanasi"
statusbar: "Holat paneli"
pleaseSelect: "Iltimos tanlang"
reverse: "Teskari"
colored: "rangli"
refreshInterval: "yangilash oralig'i"
label: "Yorliq" label: "Yorliq"
type: "turi"
speed: "tezlik"
slow: "Sekin"
fast: "Tez"
localOnly: "Faqat mahalliy"
remoteOnly: "faqat masofadan turib"
failedToUpload: "yuklanmadi"
cannotUploadBecauseInappropriate: "Faylni yuklab bo'lmaydi, chunki unda nomaqbul kontent borligi aniqlangan."
cannotUploadBecauseNoFreeSpace: "Yuklab bo'lmadi, chunki diskda bo'sh joy yo'q."
cannotUploadBecauseExceedsFileSizeLimit: "Faylni yuklash mumkin emas, chunki u fayl hajmi chegarasidan oshib ketgan."
beta: "beta"
account: "akkaunt"
show: "Displey"
color: "Rang" color: "Rang"
disableFederationConfirm: "Federatsiyani o'chirmoqchimisiz?"
disableFederationOk: "O'chirish"
emailNotSupported: "Bu server E-pochtalar yuborishni qo'llab-quvvatlamaydi"
postToTheChannel: "Kanalga joylash"
cannotBeChangedLater: "Buni keyinchalik o'zgartirishni iloji yo'q"
likeOnly: "Faqat like'lar"
nonSensitiveOnly: "Xavfsiz rejim"
rolesAssignedToMe: "Mening rollarim"
resetPasswordConfirm: "Qayta parol o'rnatmoqchimisiz?"
sensitiveWords: "Ta'sirchan so'zlar"
icon: "Avatar"
_achievements: _achievements:
_types: _types:
_viewInstanceChart: _viewInstanceChart:
title: "Tahlilchi" title: "Tahlilchi"
_role:
priority: "Ustuvorlik"
_priority:
low: "Quyi"
middle: "O'rta"
high: "Yuqori"
_ffVisibility:
public: "Chiqarish"
_ad: _ad:
back: "qaytish"
hide: "Boshqa ko'rsatilmasin" hide: "Boshqa ko'rsatilmasin"
_email: _email:
_follow: _follow:
@ -566,6 +912,7 @@ _ago:
minutesAgo: "{n} daqiqa oldin" minutesAgo: "{n} daqiqa oldin"
hoursAgo: "{n} soat oldin" hoursAgo: "{n} soat oldin"
daysAgo: "{n} kun oldin" daysAgo: "{n} kun oldin"
invalid: "Hech narsa yo'q"
_2fa: _2fa:
renewTOTPCancel: "Hozir emas" renewTOTPCancel: "Hozir emas"
_permissions: _permissions:

View file

@ -20,6 +20,7 @@ noNotes: "Chưa có bài viết nào."
noNotifications: "Chưa có thông báo" noNotifications: "Chưa có thông báo"
instance: "Máy chủ" instance: "Máy chủ"
settings: "Cài đặt" settings: "Cài đặt"
notificationSettings: "Cài đặt thông báo"
basicSettings: "Thiết lập chung" basicSettings: "Thiết lập chung"
otherSettings: "Thiết lập khác" otherSettings: "Thiết lập khác"
openInWindow: "Mở trong cửa sổ mới" openInWindow: "Mở trong cửa sổ mới"
@ -48,9 +49,15 @@ delete: "Xóa"
deleteAndEdit: "Sửa" deleteAndEdit: "Sửa"
deleteAndEditConfirm: "Bạn có chắc muốn sửa tút này? Những biểu cảm, lượt trả lời và đăng lại sẽ bị mất." deleteAndEditConfirm: "Bạn có chắc muốn sửa tút này? Những biểu cảm, lượt trả lời và đăng lại sẽ bị mất."
addToList: "Thêm vào danh sách" addToList: "Thêm vào danh sách"
addToAntenna: "Thêm vào Ăngten"
sendMessage: "Gửi tin nhắn" sendMessage: "Gửi tin nhắn"
copyRSS: "Sao chép RSS" copyRSS: "Sao chép RSS"
copyUsername: "Chép tên người dùng" copyUsername: "Chép tên người dùng"
copyUserId: "Sao chép ID người dùng"
copyNoteId: "Sao chép ID ghi chú"
copyFileId: "Sao chép ID tập tin"
copyFolderId: "Sao chép ID thư mục"
copyProfileUrl: "Sao chép URL hồ sơ"
searchUser: "Tìm kiếm người dùng" searchUser: "Tìm kiếm người dùng"
reply: "Trả lời" reply: "Trả lời"
loadMore: "Tải thêm" loadMore: "Tải thêm"
@ -122,6 +129,8 @@ unmarkAsSensitive: "Bỏ đánh dấu nhạy cảm"
enterFileName: "Nhập tên tập tin" enterFileName: "Nhập tên tập tin"
mute: "Ẩn" mute: "Ẩn"
unmute: "Bỏ ẩn" unmute: "Bỏ ẩn"
renoteMute: "Mute Renotes"
renoteUnmute: "Unmute Renotes"
block: "Chặn" block: "Chặn"
unblock: "Bỏ chặn" unblock: "Bỏ chặn"
suspend: "Vô hiệu hóa" suspend: "Vô hiệu hóa"
@ -131,8 +140,10 @@ unblockConfirm: "Bạn có chắc muốn bỏ chặn người này?"
suspendConfirm: "Bạn có chắc muốn vô hiệu hóa người này?" suspendConfirm: "Bạn có chắc muốn vô hiệu hóa người này?"
unsuspendConfirm: "Bạn có chắc muốn bỏ vô hiệu hóa người này?" unsuspendConfirm: "Bạn có chắc muốn bỏ vô hiệu hóa người này?"
selectList: "Chọn danh sách" selectList: "Chọn danh sách"
editList: "Chỉnh sửa danh sách"
selectChannel: "Lựa chọn kênh" selectChannel: "Lựa chọn kênh"
selectAntenna: "Chọn một antenna" selectAntenna: "Chọn một antenna"
editAntenna: "Chỉnh sửa Ăngten"
selectWidget: "Chọn tiện ích" selectWidget: "Chọn tiện ích"
editWidgets: "Sửa tiện ích" editWidgets: "Sửa tiện ích"
editWidgetsExit: "Xong" editWidgetsExit: "Xong"
@ -145,6 +156,8 @@ addEmoji: "Thêm emoji"
settingGuide: "Cài đặt đề xuất" settingGuide: "Cài đặt đề xuất"
cacheRemoteFiles: "Tập tin cache từ xa" cacheRemoteFiles: "Tập tin cache từ xa"
cacheRemoteFilesDescription: "Khi tùy chọn này bị tắt, các tập tin từ xa sẽ được tải trực tiếp từ máy chủ khác. Điều này sẽ giúp giảm dung lượng lưu trữ nhưng lại tăng lưu lượng truy cập, vì hình thu nhỏ sẽ không được tạo." cacheRemoteFilesDescription: "Khi tùy chọn này bị tắt, các tập tin từ xa sẽ được tải trực tiếp từ máy chủ khác. Điều này sẽ giúp giảm dung lượng lưu trữ nhưng lại tăng lưu lượng truy cập, vì hình thu nhỏ sẽ không được tạo."
cacheRemoteSensitiveFiles: "Lưu các tập tin nhạy cảm vào bộ nhớ tạm từ xa"
cacheRemoteSensitiveFilesDescription: "Khi bạn tắt tính năng này, các tệp nhạy cảm sẽ được tải trực tiếp từ máy chủ và không được lưu vào bộ nhớ tạm"
flagAsBot: "Đánh dấu đây là tài khoản bot" flagAsBot: "Đánh dấu đây là tài khoản bot"
flagAsBotDescription: "Bật tùy chọn này nếu tài khoản này được kiểm soát bởi một chương trình. Nếu được bật, nó sẽ được đánh dấu để các nhà phát triển khác ngăn chặn chuỗi tương tác vô tận với các bot khác và điều chỉnh hệ thống nội bộ của Misskey để coi tài khoản này như một bot." flagAsBotDescription: "Bật tùy chọn này nếu tài khoản này được kiểm soát bởi một chương trình. Nếu được bật, nó sẽ được đánh dấu để các nhà phát triển khác ngăn chặn chuỗi tương tác vô tận với các bot khác và điều chỉnh hệ thống nội bộ của Misskey để coi tài khoản này như một bot."
flagAsCat: "Chế độ Mèeeeeeeeeeo!!" flagAsCat: "Chế độ Mèeeeeeeeeeo!!"
@ -153,6 +166,7 @@ flagShowTimelineReplies: "Hiện lượt trả lời trong bảng tin"
flagShowTimelineRepliesDescription: "Hiện lượt trả lời của người bạn theo dõi trên tút của những người khác." flagShowTimelineRepliesDescription: "Hiện lượt trả lời của người bạn theo dõi trên tút của những người khác."
autoAcceptFollowed: "Tự động phê duyệt theo dõi từ những người mà bạn đang theo dõi" autoAcceptFollowed: "Tự động phê duyệt theo dõi từ những người mà bạn đang theo dõi"
addAccount: "Thêm tài khoản" addAccount: "Thêm tài khoản"
reloadAccountsList: "Cập nhật danh sách tài khoản"
loginFailed: "Đăng nhập không thành công" loginFailed: "Đăng nhập không thành công"
showOnRemote: "Truy cập trang của người này" showOnRemote: "Truy cập trang của người này"
general: "Tổng quan" general: "Tổng quan"
@ -259,8 +273,10 @@ noMoreHistory: "Không còn gì để đọc"
startMessaging: "Bắt đầu trò chuyện" startMessaging: "Bắt đầu trò chuyện"
nUsersRead: "đọc bởi {n}" nUsersRead: "đọc bởi {n}"
agreeTo: "Tôi đồng ý {0}" agreeTo: "Tôi đồng ý {0}"
agree: "Đồng ý"
agreeBelow: "Đồng ý với nội dung dưới đây" agreeBelow: "Đồng ý với nội dung dưới đây"
basicNotesBeforeCreateAccount: "Những điều cơ bản cần chú ý " basicNotesBeforeCreateAccount: "Những điều cơ bản cần chú ý "
termsOfService: "Điều khoản và Điều kiện"
start: "Bắt đầu" start: "Bắt đầu"
home: "Trang chính" home: "Trang chính"
remoteUserCaution: "Vì người dùng này ở máy chủ khác, thông tin hiển thị có thể không đầy đủ." remoteUserCaution: "Vì người dùng này ở máy chủ khác, thông tin hiển thị có thể không đầy đủ."
@ -303,6 +319,7 @@ copyUrl: "Sao chép URL"
rename: "Đổi tên" rename: "Đổi tên"
avatar: "Ảnh đại diện" avatar: "Ảnh đại diện"
banner: "Ảnh bìa" banner: "Ảnh bìa"
displayOfSensitiveMedia: "Hiển thị nội dung nhạy cảm (NSFW)"
whenServerDisconnected: "Khi mất kết nối tới máy chủ" whenServerDisconnected: "Khi mất kết nối tới máy chủ"
disconnectedFromServer: "Mất kết nối tới máy chủ" disconnectedFromServer: "Mất kết nối tới máy chủ"
reload: "Tải lại" reload: "Tải lại"
@ -456,6 +473,7 @@ aboutX: "Giới thiệu {x}"
emojiStyle: "Kiểu cách Emoji" emojiStyle: "Kiểu cách Emoji"
native: "Bản xứ" native: "Bản xứ"
disableDrawer: "Không dùng menu thanh bên" disableDrawer: "Không dùng menu thanh bên"
showNoteActionsOnlyHover: "Chỉ hiển thị các hành động ghi chú khi di chuột"
noHistory: "Không có dữ liệu" noHistory: "Không có dữ liệu"
signinHistory: "Lịch sử đăng nhập" signinHistory: "Lịch sử đăng nhập"
enableAdvancedMfm: "Xem bài MFM chất lượng cao." enableAdvancedMfm: "Xem bài MFM chất lượng cao."
@ -468,6 +486,7 @@ createAccount: "Tạo tài khoản"
existingAccount: "Tài khoản hiện có" existingAccount: "Tài khoản hiện có"
regenerate: "Tạo lại" regenerate: "Tạo lại"
fontSize: "Cỡ chữ" fontSize: "Cỡ chữ"
limitTo: "Giới hạn tỷ lệ {x}"
noFollowRequests: "Bạn không có yêu cầu theo dõi nào" noFollowRequests: "Bạn không có yêu cầu theo dõi nào"
openImageInNewTab: "Mở ảnh trong tab mới" openImageInNewTab: "Mở ảnh trong tab mới"
dashboard: "Trang chính" dashboard: "Trang chính"
@ -504,6 +523,7 @@ objectStorageSetPublicRead: "Đặt \"public-read\" khi tải lên"
serverLogs: "Nhật ký máy chủ" serverLogs: "Nhật ký máy chủ"
deleteAll: "Xóa tất cả" deleteAll: "Xóa tất cả"
showFixedPostForm: "Hiện khung soạn tút ở phía trên bảng tin" showFixedPostForm: "Hiện khung soạn tút ở phía trên bảng tin"
showFixedPostFormInChannel: "Hiển thị mẫu bài đăng ở phía trên bản tin"
newNoteRecived: "Đã nhận tút mới" newNoteRecived: "Đã nhận tút mới"
sounds: "Âm thanh" sounds: "Âm thanh"
sound: "Âm thanh" sound: "Âm thanh"
@ -541,9 +561,14 @@ userSuspended: "Người này đã bị vô hiệu hóa."
userSilenced: "Người này đã bị ẩn" userSilenced: "Người này đã bị ẩn"
yourAccountSuspendedTitle: "Tài khoản bị vô hiệu hóa" yourAccountSuspendedTitle: "Tài khoản bị vô hiệu hóa"
yourAccountSuspendedDescription: "Tài khoản này đã bị vô hiệu hóa do vi phạm quy tắc máy chủ hoặc điều tương tự. Liên hệ với quản trị viên nếu bạn muốn biết lý do chi tiết hơn. Vui lòng không tạo tài khoản mới." yourAccountSuspendedDescription: "Tài khoản này đã bị vô hiệu hóa do vi phạm quy tắc máy chủ hoặc điều tương tự. Liên hệ với quản trị viên nếu bạn muốn biết lý do chi tiết hơn. Vui lòng không tạo tài khoản mới."
tokenRevoked: "Token đã bị từ chối"
tokenRevokedDescription: "Phiên đăng nhập đã hết hạn. Vui lòng đăng nhập lại."
accountDeleted: "Tài khoản đã bị xóa"
accountDeletedDescription: "Tài khoản này đã bị xóa."
menu: "Menu" menu: "Menu"
divider: "Phân chia" divider: "Phân chia"
addItem: "Thêm mục" addItem: "Thêm mục"
rearrange: "Sắp xếp lại"
relays: "Chuyển tiếp" relays: "Chuyển tiếp"
addRelay: "Thêm chuyển tiếp" addRelay: "Thêm chuyển tiếp"
inboxUrl: "URL Hộp thư đến" inboxUrl: "URL Hộp thư đến"
@ -653,6 +678,7 @@ createNewClip: "Tạo một ghim mới"
unclip: "Bỏ ghim" unclip: "Bỏ ghim"
confirmToUnclipAlreadyClippedNote: "Bài đăng này là một phần của \"{name}\" ghim. Bạn có muốn bỏ khỏi ghim?" confirmToUnclipAlreadyClippedNote: "Bài đăng này là một phần của \"{name}\" ghim. Bạn có muốn bỏ khỏi ghim?"
public: "Công khai" public: "Công khai"
private: "Riêng tư"
i18nInfo: "Misskey đang được các tình nguyện viên dịch sang nhiều thứ tiếng khác nhau. Bạn có thể hỗ trợ tại {link}." i18nInfo: "Misskey đang được các tình nguyện viên dịch sang nhiều thứ tiếng khác nhau. Bạn có thể hỗ trợ tại {link}."
manageAccessTokens: "Tạo mã truy cập" manageAccessTokens: "Tạo mã truy cập"
accountInfo: "Thông tin tài khoản" accountInfo: "Thông tin tài khoản"
@ -687,6 +713,8 @@ contact: "Liên hệ"
useSystemFont: "Dùng phông chữ mặc định của hệ thống" useSystemFont: "Dùng phông chữ mặc định của hệ thống"
clips: "Lưu bài viết" clips: "Lưu bài viết"
experimentalFeatures: "Tính năng thử nghiệm" experimentalFeatures: "Tính năng thử nghiệm"
experimental: "Thử nghiệm"
thisIsExperimentalFeature: "Tính năng này đang trong quá trình thử nghiệm. Tính năng có thể không hoạt động, hoặc đặc tính kỹ thuật có thể bị thay đổi sau này."
developer: "Nhà phát triển" developer: "Nhà phát triển"
makeExplorable: "Không hiện tôi trong \"Khám phá\"" makeExplorable: "Không hiện tôi trong \"Khám phá\""
makeExplorableDescription: "Nếu bạn tắt, tài khoản của bạn sẽ không hiện trong mục \"Khám phá\"." makeExplorableDescription: "Nếu bạn tắt, tài khoản của bạn sẽ không hiện trong mục \"Khám phá\"."
@ -771,6 +799,7 @@ noMaintainerInformationWarning: "Chưa thiết lập thông tin vận hành."
noBotProtectionWarning: "Bảo vệ Bot chưa thiết lập." noBotProtectionWarning: "Bảo vệ Bot chưa thiết lập."
configure: "Thiết lập" configure: "Thiết lập"
postToGallery: "Tạo tút có ảnh" postToGallery: "Tạo tút có ảnh"
postToHashtag: "Đăng bài với hashtag này"
gallery: "Thư viện ảnh" gallery: "Thư viện ảnh"
recentPosts: "Tút gần đây" recentPosts: "Tút gần đây"
popularPosts: "Tút được xem nhiều nhất" popularPosts: "Tút được xem nhiều nhất"
@ -804,6 +833,7 @@ translatedFrom: "Dịch từ {x}"
accountDeletionInProgress: "Đang xử lý việc xóa tài khoản" accountDeletionInProgress: "Đang xử lý việc xóa tài khoản"
usernameInfo: "Bạn có thể sử dụng chữ cái (a ~ z, A ~ Z), chữ số (0 ~ 9) hoặc dấu gạch dưới (_). Tên người dùng không thể thay đổi sau này." usernameInfo: "Bạn có thể sử dụng chữ cái (a ~ z, A ~ Z), chữ số (0 ~ 9) hoặc dấu gạch dưới (_). Tên người dùng không thể thay đổi sau này."
aiChanMode: "Chế độ Ai" aiChanMode: "Chế độ Ai"
devMode: "Chế độ dành cho nhà phát triển"
keepCw: "Giữ cảnh báo nội dung" keepCw: "Giữ cảnh báo nội dung"
pubSub: "Tài khoản Chính/Phụ" pubSub: "Tài khoản Chính/Phụ"
lastCommunication: "Lần giao tiếp cuối" lastCommunication: "Lần giao tiếp cuối"
@ -813,6 +843,8 @@ breakFollow: "Xóa người theo dõi"
breakFollowConfirm: "Bạn bỏ theo dõi tài khoản này không?" breakFollowConfirm: "Bạn bỏ theo dõi tài khoản này không?"
itsOn: "Đã bật" itsOn: "Đã bật"
itsOff: "Đã tắt" itsOff: "Đã tắt"
on: "Bật"
off: "Tắt"
emailRequiredForSignup: "Yêu cầu địa chỉ email khi đăng ký" emailRequiredForSignup: "Yêu cầu địa chỉ email khi đăng ký"
unread: "Chưa đọc" unread: "Chưa đọc"
filter: "Bộ lọc" filter: "Bộ lọc"
@ -857,6 +889,7 @@ failedToFetchAccountInformation: "Không thể lấy thông tin tài khoản"
rateLimitExceeded: "Giới hạn quá mức" rateLimitExceeded: "Giới hạn quá mức"
cropImage: "Cắt hình ảnh" cropImage: "Cắt hình ảnh"
cropImageAsk: "Bạn có muốn cắt ảnh này?" cropImageAsk: "Bạn có muốn cắt ảnh này?"
cropYes: "Cắt"
cropNo: "Để nguyên" cropNo: "Để nguyên"
file: "Tập tin" file: "Tập tin"
recentNHours: "{n}h trước" recentNHours: "{n}h trước"
@ -892,6 +925,7 @@ remoteOnly: "Chỉ máy chủ từ xa"
failedToUpload: "Tải lên thất bại" failedToUpload: "Tải lên thất bại"
cannotUploadBecauseInappropriate: "Không thể tải lên tập tin này vì các phần của tập tin đã được phát hiện có khả năng là NSFW." cannotUploadBecauseInappropriate: "Không thể tải lên tập tin này vì các phần của tập tin đã được phát hiện có khả năng là NSFW."
cannotUploadBecauseNoFreeSpace: "Tải lên không thành công do thiếu dung lượng Drive." cannotUploadBecauseNoFreeSpace: "Tải lên không thành công do thiếu dung lượng Drive."
cannotUploadBecauseExceedsFileSizeLimit: "Không thể tải lên tập tin vì kích thước quá lớn."
beta: "Beta" beta: "Beta"
enableAutoSensitive: "Tự động đánh dấu NSFW" enableAutoSensitive: "Tự động đánh dấu NSFW"
enableAutoSensitiveDescription: "Cho phép tự động phát hiện và đánh dấu media NSFW thông qua học máy, nếu có thể. Ngay cả khi tùy chọn này bị tắt, nó vẫn có thể được bật trên toàn máy chủ." enableAutoSensitiveDescription: "Cho phép tự động phát hiện và đánh dấu media NSFW thông qua học máy, nếu có thể. Ngay cả khi tùy chọn này bị tắt, nó vẫn có thể được bật trên toàn máy chủ."
@ -904,9 +938,11 @@ pushNotification: "Thông báo đẩy"
subscribePushNotification: "Bật thông báo đẩy" subscribePushNotification: "Bật thông báo đẩy"
unsubscribePushNotification: "Tắt thông báo đẩy" unsubscribePushNotification: "Tắt thông báo đẩy"
pushNotificationAlreadySubscribed: "Đang bật thông báo đẩy" pushNotificationAlreadySubscribed: "Đang bật thông báo đẩy"
pushNotificationNotSupported: "Trình duyệt của bạn không hỗ trợ thông báo đẩy."
sendPushNotificationReadMessage: "Xóa thông báo đẩy sau khi đọc thông báo hay tin nhắn" sendPushNotificationReadMessage: "Xóa thông báo đẩy sau khi đọc thông báo hay tin nhắn"
sendPushNotificationReadMessageCaption: "Thông báo như {emptyPushNotificationMessage} sẽ hiển thị trong giây phút. Tiêu tốn pin của máy bạn có thể tăng lên hơn nữa." sendPushNotificationReadMessageCaption: "Thông báo như {emptyPushNotificationMessage} sẽ hiển thị trong giây phút. Tiêu tốn pin của máy bạn có thể tăng lên hơn nữa."
windowMaximize: "Phóng to" windowMaximize: "Phóng to"
windowMinimize: "Thu nhỏ tối đa"
windowRestore: "Khôi phục" windowRestore: "Khôi phục"
caption: "Mô tả" caption: "Mô tả"
loggedInAsBot: "Đang đăng nhập bằng tài khoản Bot" loggedInAsBot: "Đang đăng nhập bằng tài khoản Bot"
@ -923,12 +959,22 @@ didYouLikeMisskey: "Bạn có ưa thích Mískey không?"
pleaseDonate: "Misskey là phần mềm miễn phí mà {host} đang sử dụng. Xin mong bạn quyên góp cho chúng tôi để chúng tôi có thể tiếp tục phát triển dịch vụ này. Xin cảm ơn!!" pleaseDonate: "Misskey là phần mềm miễn phí mà {host} đang sử dụng. Xin mong bạn quyên góp cho chúng tôi để chúng tôi có thể tiếp tục phát triển dịch vụ này. Xin cảm ơn!!"
roles: "Vai trò" roles: "Vai trò"
role: "Vai trò" role: "Vai trò"
noRole: "Bạn chưa được cấp quyền."
normalUser: "Người dùng bình thường" normalUser: "Người dùng bình thường"
undefined: "Chưa xác định" undefined: "Chưa xác định"
assign: "Phân công"
unassign: "Hủy phân công"
color: "Màu sắc" color: "Màu sắc"
manageCustomEmojis: "Quản lý CustomEmoji" manageCustomEmojis: "Quản lý CustomEmoji"
youCannotCreateAnymore: "Bạn đã tới giới hạn tạo."
cannotPerformTemporary: "Tạm thời không sử dụng được" cannotPerformTemporary: "Tạm thời không sử dụng được"
cannotPerformTemporaryDescription: "Tạm thời không sử dụng được vì lần số điều kiện quá giới hạn. Thử lại sau mọt lát nữa." cannotPerformTemporaryDescription: "Tạm thời không sử dụng được vì lần số điều kiện quá giới hạn. Thử lại sau mọt lát nữa."
invalidParamError: "Lỗi tham số"
invalidParamErrorDescription: "Có vấn đề với các tham số được request. Thông thường, đây là do bug, nhưng cũng có thể do bạn đã nhập vào quá nhiều ký tự."
permissionDeniedError: "Thao tác bị từ chối"
permissionDeniedErrorDescription: "Tài khoản này không có đủ quyền hạn để thực hiện thao tác này."
preset: "Mẫu thiết lập"
selectFromPresets: "Chọn từ mẫu"
achievements: "Thành tích" achievements: "Thành tích"
gotInvalidResponseError: "Không nhận được trả lời chủ máy" gotInvalidResponseError: "Không nhận được trả lời chủ máy"
gotInvalidResponseErrorDescription: "Chủ máy có lẻ ngừng hoạt động hoặc bảo trí. Thử lại sau một lát nữa. " gotInvalidResponseErrorDescription: "Chủ máy có lẻ ngừng hoạt động hoặc bảo trí. Thử lại sau một lát nữa. "
@ -945,6 +991,7 @@ exploreOtherServers: "Tìm chủ máy khác"
letsLookAtTimeline: "Thử xem Timeline" letsLookAtTimeline: "Thử xem Timeline"
horizontal: "Thanh bên" horizontal: "Thanh bên"
youFollowing: "Đang theo dõi" youFollowing: "Đang theo dõi"
icon: "Ảnh đại diện"
_achievements: _achievements:
earnedAt: "Ngày thu nhận" earnedAt: "Ngày thu nhận"
_types: _types:
@ -1104,6 +1151,7 @@ _achievements:
_cookieClicked: _cookieClicked:
flavor: "Bạn nhầm phầm mềm chứ?" flavor: "Bạn nhầm phầm mềm chứ?"
_role: _role:
assignTarget: "Phân công"
priority: "Ưu tiên" priority: "Ưu tiên"
_priority: _priority:
low: "Thấp" low: "Thấp"
@ -1346,15 +1394,27 @@ _time:
minute: "phút" minute: "phút"
hour: "giờ" hour: "giờ"
day: "ngày" day: "ngày"
_timelineTutorial:
step4_1: "Bạn có thể thêm \"Reaction\" vào ghi chú"
step4_2: "Khi thêm biểu cảm hãy nhấn dấu \"+\""
_2fa: _2fa:
alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước." alreadyRegistered: "Bạn đã đăng ký thiết bị xác minh 2 bước."
registerTOTP: "Đăng ký ứng dụng xác thực"
passwordToTOTP: "Nhắn mật mã" passwordToTOTP: "Nhắn mật mã"
step1: "Trước tiên, hãy cài đặt một ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị của bạn." step1: "Trước tiên, hãy cài đặt một ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị của bạn."
step2: "Sau đó, quét mã QR hiển thị trên màn hình này." step2: "Sau đó, quét mã QR hiển thị trên màn hình này."
step2Click: "Quét mã QR trên ứng dụng xác thực (Authy, Google authenticator, v.v.)"
step2Url: "Bạn cũng có thể nhập URL này nếu sử dụng một chương trình máy tính:" step2Url: "Bạn cũng có thể nhập URL này nếu sử dụng một chương trình máy tính:"
step3Title: "Nhập mã xác thực"
step3: "Nhập mã token do ứng dụng của bạn cung cấp để hoàn tất thiết lập." step3: "Nhập mã token do ứng dụng của bạn cung cấp để hoàn tất thiết lập."
step4: "Kể từ bây giờ, những lần đăng nhập trong tương lai sẽ yêu cầu mã token đăng nhập đó." step4: "Kể từ bây giờ, những lần đăng nhập trong tương lai sẽ yêu cầu mã token đăng nhập đó."
securityKeyNotSupported: "Trình duyệt của bạn không hỗ trợ khóa bảo mật"
registerTOTPBeforeKey: "Vui lòng thiết lập một ứng dụng xác thực để đăng ký khóa bảo mật hoặc mật khẩu."
securityKeyInfo: "Bên cạnh xác minh bằng vân tay hoặc mã PIN, bạn cũng có thể thiết lập xác minh thông qua khóa bảo mật phần cứng hỗ trợ FIDO2 để bảo mật hơn nữa cho tài khoản của mình." securityKeyInfo: "Bên cạnh xác minh bằng vân tay hoặc mã PIN, bạn cũng có thể thiết lập xác minh thông qua khóa bảo mật phần cứng hỗ trợ FIDO2 để bảo mật hơn nữa cho tài khoản của mình."
chromePasskeyNotSupported: "Mật khẩu Chrome hiện không được hỗ trợ."
registerSecurityKey: "Tạo khóa bảo mật hoặc mã bảo mật"
securityKeyName: "Nhập tên khóa bảo mật"
tapSecurityKey: "Vui lòng làm theo hướng dẫn của trình duyệt để đăng ký mã bảo mật hoặc mã khóa"
removeKey: "Xóa mã bảo mật" removeKey: "Xóa mã bảo mật"
removeKeyConfirm: "Xóa bản sao lưu {name}?" removeKeyConfirm: "Xóa bản sao lưu {name}?"
renewTOTP: "Cài đặt lại ứng dụng xác thực" renewTOTP: "Cài đặt lại ứng dụng xác thực"
@ -1677,5 +1737,11 @@ _dialog:
charactersExceeded: "Bạn nhắn quá giới hạn ký tự!! Hiện nay {current} / giới hạn {max}" charactersExceeded: "Bạn nhắn quá giới hạn ký tự!! Hiện nay {current} / giới hạn {max}"
charactersBelow: "Bạn nhắn quá ít tối thiểu ký tự!! Hiện nay {current} / Tối thiểu {min}" charactersBelow: "Bạn nhắn quá ít tối thiểu ký tự!! Hiện nay {current} / Tối thiểu {min}"
_webhookSettings: _webhookSettings:
createWebhook: "Tạo Webhook"
name: "Tên" name: "Tên"
secret: "Mã bí mật"
events: "Sự kiện Webhook"
active: "Đã bật" active: "Đã bật"
_events:
reaction: "Khi nhận được sự kiện"
mention: "Khi có người nhắc tới bạn"

View file

@ -15,7 +15,7 @@ gotIt: "我明白了"
cancel: "取消" cancel: "取消"
noThankYou: "不用,谢谢" noThankYou: "不用,谢谢"
enterUsername: "输入用户名" enterUsername: "输入用户名"
renotedBy: "由 {user} 转贴" renotedBy: "{user} 转发了"
noNotes: "没有帖文" noNotes: "没有帖文"
noNotifications: "无通知" noNotifications: "无通知"
instance: "服务器" instance: "服务器"
@ -156,11 +156,12 @@ addEmoji: "添加表情符号"
settingGuide: "推荐配置" settingGuide: "推荐配置"
cacheRemoteFiles: "缓存远程文件" cacheRemoteFiles: "缓存远程文件"
cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远程服务器载入。禁用后会减小储存空间需求,但是会增加流量,因为缩略图不会被生成。" cacheRemoteFilesDescription: "当禁用此设定时远程文件将直接从远程服务器载入。禁用后会减小储存空间需求,但是会增加流量,因为缩略图不会被生成。"
youCanCleanRemoteFilesCache: "可以使用文件管理的🗑️按钮来删除所有的缓存。"
cacheRemoteSensitiveFiles: "缓存远程敏感媒体文件" cacheRemoteSensitiveFiles: "缓存远程敏感媒体文件"
cacheRemoteSensitiveFilesDescription: "如果禁用这项设定,远程服务器的敏感媒体将不会被缓存,而是直接链接。" cacheRemoteSensitiveFilesDescription: "如果禁用这项设定,远程服务器的敏感媒体将不会被缓存,而是直接链接。"
flagAsBot: "这是一个机器人账号" flagAsBot: "这是一个机器人账号"
flagAsBotDescription: "如果此账户由程序控制,请启用此项。启用后,此标志可以帮助其他开发人员防止机器人之间产生无限互动的行为,并让 Misskey 的内部系统将此账户识别为机器人。" flagAsBotDescription: "如果此账户由程序控制,请启用此项。启用后,此标志可以帮助其他开发人员防止机器人之间产生无限互动的行为,并让 Misskey 的内部系统将此账户识别为机器人。"
flagAsCat: "将这个账户设定为一只猫" flagAsCat: "喵!!!!!!!!!!!!"
flagAsCatDescription: "如果您想表明此帐户是一只猫,请打开此标志。\n开启后会在您的头像上出现猫耳朵并将你的帖子中的「na」替换为「nya」日文同理。" flagAsCatDescription: "如果您想表明此帐户是一只猫,请打开此标志。\n开启后会在您的头像上出现猫耳朵并将你的帖子中的「na」替换为「nya」日文同理。"
flagShowTimelineReplies: "在时间线上显示帖子的回复" flagShowTimelineReplies: "在时间线上显示帖子的回复"
flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。" flagShowTimelineRepliesDescription: "启用时,时间线除了显示用户的帖子外,还会显示其他用户对帖子的回复。"
@ -507,13 +508,13 @@ showFeaturedNotesInTimeline: "在时间线上显示热门推荐"
objectStorage: "对象存储" objectStorage: "对象存储"
useObjectStorage: "使用对象存储" useObjectStorage: "使用对象存储"
objectStorageBaseUrl: "Base URL" objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "这里是用于引用的 URL如果您正在使用 CDN 或反向代理,请指定其 URL例如S3“https://<bucket>.s3.amazonaws.com”GCS“https://storage.googleapis.com/<bucket>”" objectStorageBaseUrlDesc: "这里是用于参考的 URL如果您正在使用 CDN 或反向代理,请指定其 URL例如 S3“https://<bucket>.s3.amazonaws.com”GCS“https://storage.googleapis.com/<bucket>”"
objectStorageBucket: "存储桶" objectStorageBucket: "存储桶"
objectStorageBucketDesc: "请指定使用的对象存储服务的存储桶名称。" objectStorageBucketDesc: "请指定使用的对象存储服务的存储桶名称。"
objectStoragePrefix: "前缀" objectStoragePrefix: "前缀"
objectStoragePrefixDesc: "文件将存储在此前缀的目录下。" objectStoragePrefixDesc: "文件将存储在此前缀的目录下。"
objectStorageEndpoint: "Endpoint" objectStorageEndpoint: "端点"
objectStorageEndpointDesc: "如果你使用 AWS S3 请留空。否则请根据你使用的服务商的说明来进行设置,指定 Endpoint 形式为“<host>”或“<host>:<port>”。" objectStorageEndpointDesc: "如果你使用 AWS S3 请留空。否则请根据你使用的服务商的说明来进行设置,指定端点形式为“<host>”或“<host>:<port>”。"
objectStorageRegion: "可用区" objectStorageRegion: "可用区"
objectStorageRegionDesc: "指定一个可用区例如“xx-east-1”。 如果您的对象存储服务没有可用区概念请将其留空或填写“us-east-1”。如果引用 AWS 的配置文件或环境变量,则留空。" objectStorageRegionDesc: "指定一个可用区例如“xx-east-1”。 如果您的对象存储服务没有可用区概念请将其留空或填写“us-east-1”。如果引用 AWS 的配置文件或环境变量,则留空。"
objectStorageUseSSL: "使用 SSL" objectStorageUseSSL: "使用 SSL"
@ -680,6 +681,7 @@ createNewClip: "新建便签"
unclip: "移除便签" unclip: "移除便签"
confirmToUnclipAlreadyClippedNote: "本帖已包含在便签 \"{name}\" 里。您想要将本帖从该便签中移除吗?" confirmToUnclipAlreadyClippedNote: "本帖已包含在便签 \"{name}\" 里。您想要将本帖从该便签中移除吗?"
public: "公开" public: "公开"
private: "私密"
i18nInfo: "Misskey 已经被志愿者们翻译成了各种语言。如果你也有兴趣,可以通过 {link} 帮助翻译。" i18nInfo: "Misskey 已经被志愿者们翻译成了各种语言。如果你也有兴趣,可以通过 {link} 帮助翻译。"
manageAccessTokens: "管理 Access Tokens" manageAccessTokens: "管理 Access Tokens"
accountInfo: "账户信息" accountInfo: "账户信息"
@ -1094,6 +1096,21 @@ expired: "已过期"
doYouAgree: "你同意吗?" doYouAgree: "你同意吗?"
beSureToReadThisAsItIsImportant: "请好好阅读,这真的很重要。" beSureToReadThisAsItIsImportant: "请好好阅读,这真的很重要。"
iHaveReadXCarefullyAndAgree: "我已经仔细阅读并同意了「{x}」的内容。" iHaveReadXCarefullyAndAgree: "我已经仔细阅读并同意了「{x}」的内容。"
dialog: "对话框"
icon: "头像"
forYou: "您的"
currentAnnouncements: "现在的公告"
pastAnnouncements: "过去的公告"
youHaveUnreadAnnouncements: "您有未读的公告"
_announcement:
forExistingUsers: "仅限现有用户"
forExistingUsersDescription: "若启用,该公告将仅对创建此公告时存在的用户可见。 如果禁用,则在创建此公告后注册的用户也可以看到该公告。"
needConfirmationToRead: "需要确认才能标记为已读"
needConfirmationToReadDescription: "若启用,则会在标记已读时会显示确认对话框。此外,它也会不受批量已读操作的影响。"
end: "结束公告"
tooManyActiveAnnouncementDescription: "若有大量活动公告,可能会造成用户体验可能下降。请考虑归档已完成的公告。"
readConfirmTitle: "标记为已读?"
readConfirmText: "阅读“{title}”的内容并将其标记为已读。"
_initialAccountSetting: _initialAccountSetting:
accountCreated: "账户创建完成了!" accountCreated: "账户创建完成了!"
letsStartAccountSetup: "来进行帐户的初始设置吧。" letsStartAccountSetup: "来进行帐户的初始设置吧。"

View file

@ -1,9 +1,9 @@
--- ---
_lang_: "繁體中文" _lang_: "繁體中文"
headlineMisskey: "貼文連繫網絡" headlineMisskey: "貼文連繫網絡"
introMisskey: "歡迎Misskey 是一個開源且去中心化的社群網路服務。\n發佈「貼文」向身邊的人分享您的想法!📡\n利用「反應」表達您對貼文的感覺👍\n讓我們一起探索新的世界吧🚀" introMisskey: "歡迎Misskey 是一個開放原始碼且去中心化的社群網路服務。\n發布「貼文」向身邊的人分享您的想法!📡\n利用「反應」表達您對貼文的感覺👍\n讓我們一起探索新的世界吧🚀"
poweredByMisskeyDescription: "{name}是使用開放原始碼平台<b>Misskey</b>的服務之一(稱為 Misskey 伺服器)。\n" poweredByMisskeyDescription: "{name}是開放原始碼平臺 <b>Misskey</b> 的伺服器之一。"
monthAndDay: "{month}月 {day}日" monthAndDay: "{month} 月 {day} 日"
search: "搜尋" search: "搜尋"
notifications: "通知" notifications: "通知"
username: "使用者名稱" username: "使用者名稱"
@ -16,7 +16,7 @@ cancel: "取消"
noThankYou: "現在不要" noThankYou: "現在不要"
enterUsername: "輸入使用者名稱" enterUsername: "輸入使用者名稱"
renotedBy: "{user} 轉發了" renotedBy: "{user} 轉發了"
noNotes: "無貼文" noNotes: "無貼文"
noNotifications: "沒有通知" noNotifications: "沒有通知"
instance: "伺服器" instance: "伺服器"
settings: "設定" settings: "設定"
@ -26,7 +26,7 @@ otherSettings: "其他設定"
openInWindow: "在新視窗開啟" openInWindow: "在新視窗開啟"
profile: "個人檔案" profile: "個人檔案"
timeline: "時間軸" timeline: "時間軸"
noAccountDescription: "此用戶還沒有自我介紹" noAccountDescription: "此使用者尚未自我介紹"
login: "登入" login: "登入"
loggingIn: "登入中" loggingIn: "登入中"
logout: "登出" logout: "登出"
@ -135,8 +135,8 @@ block: "封鎖"
unblock: "解除封鎖" unblock: "解除封鎖"
suspend: "凍結" suspend: "凍結"
unsuspend: "解除凍結" unsuspend: "解除凍結"
blockConfirm: "確定要封鎖此用戶" blockConfirm: "確定要封鎖此使用者嗎"
unblockConfirm: "確定解除封鎖此用戶" unblockConfirm: "確定要解除封鎖此使用者嗎"
suspendConfirm: "確定凍結此帳戶?" suspendConfirm: "確定凍結此帳戶?"
unsuspendConfirm: "確定解凍此帳戶?" unsuspendConfirm: "確定解凍此帳戶?"
selectList: "選擇清單" selectList: "選擇清單"
@ -155,15 +155,16 @@ emojiUrl: "表情符號URL"
addEmoji: "新增表情符號" addEmoji: "新增表情符號"
settingGuide: "推薦設定" settingGuide: "推薦設定"
cacheRemoteFiles: "快取遠端檔案" cacheRemoteFiles: "快取遠端檔案"
cacheRemoteFilesDescription: "禁用此設定會停止遠端檔案的緩存,從而節省儲存空間,但資料會因直接連線從而產生額外連接數據。" cacheRemoteFilesDescription: "禁用此設定會停止建立遠端檔案快取,從而節省伺服器儲存空間,但會因從遠端讀取資料而增加網路數據用量。"
youCanCleanRemoteFilesCache: "按檔案管理的🗑️按鈕,將快取全部刪除。"
cacheRemoteSensitiveFiles: "快取遠端的敏感檔案" cacheRemoteSensitiveFiles: "快取遠端的敏感檔案"
cacheRemoteSensitiveFilesDescription: "若停用這個設定,則不會快取遠端的敏感檔案,而是直接連結。" cacheRemoteSensitiveFilesDescription: "若停用這個設定,則不會快取遠端的敏感檔案,而是直接連結。"
flagAsBot: "此使用者是機器人" flagAsBot: "此使用者是機器人"
flagAsBotDescription: "如果本帳戶是由程式控制請啟用此選項。啟用後會作為標示幫助其他開發者防止機器人之間產生無限互動的行為並會調整Misskey內部系統將本帳戶識別為機器人。" flagAsBotDescription: "標記本帳戶由程式控制,防止其他程式與本帳戶產生無限互動的行為。"
flagAsCat: "此帳戶是一隻貓,喵~~~!!!" flagAsCat: "此帳戶是一隻貓,喵~~~!!!"
flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示" flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆" flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
flagShowTimelineRepliesDescription: "啟用時,時間線除了顯示用戶的貼文以外,還會顯示用戶對其他貼文的回覆。" flagShowTimelineRepliesDescription: "啟用時,時間線除了顯示使用者的貼文以外,還會顯示使用者對其他貼文的回覆。"
autoAcceptFollowed: "自動允許來自追隨中使用者的追隨請求" autoAcceptFollowed: "自動允許來自追隨中使用者的追隨請求"
addAccount: "新增帳戶" addAccount: "新增帳戶"
reloadAccountsList: "更新帳戶清單的資訊" reloadAccountsList: "更新帳戶清單的資訊"
@ -177,12 +178,12 @@ searchWith: "搜尋: {q}"
youHaveNoLists: "你沒有任何清單" youHaveNoLists: "你沒有任何清單"
followConfirm: "你真的要追隨{name}嗎?" followConfirm: "你真的要追隨{name}嗎?"
proxyAccount: "代理帳戶" proxyAccount: "代理帳戶"
proxyAccountDescription: "代理帳戶是在某些情況下充當其他伺服器用戶的帳戶。例如,當使用者將一個來自其他伺服器的帳戶放在列表中時,由於沒有其他使用者追隨該帳戶,該指令不會傳送到該伺服器上,因此會由代理帳戶追隨。" proxyAccountDescription: "代理帳戶是在特定條件下充當遠端追隨者的帳戶。例如,當使用者新增遠端使用者至其列表時,若沒有本地使用者追隨該遠端使用者,則其活動將不會傳送至伺服器,此時便會由代理帳戶代為追隨以解決問題。"
host: "主機" host: "主機"
selectUser: "選取使用者" selectUser: "選取使用者"
recipient: "收件人" recipient: "收件人"
annotation: "註解" annotation: "註解"
federation: "站台聯邦" federation: "聯邦宇宙"
instances: "伺服器" instances: "伺服器"
registeredAt: "初次觀測" registeredAt: "初次觀測"
latestRequestReceivedAt: "上次收到的請求" latestRequestReceivedAt: "上次收到的請求"
@ -197,10 +198,10 @@ operations: "操作"
software: "軟體" software: "軟體"
version: "版本" version: "版本"
metadata: "元資料" metadata: "元資料"
withNFiles: "{n}個檔案" withNFiles: "{n} 個檔案"
monitor: "監視器" monitor: "監視器"
jobQueue: "佇列" jobQueue: "佇列"
cpuAndMemory: "CPU及記憶體用量" cpuAndMemory: "CPU 及記憶體"
network: "網路" network: "網路"
disk: "硬碟" disk: "硬碟"
instanceInfo: "伺服器資訊" instanceInfo: "伺服器資訊"
@ -213,8 +214,8 @@ clearCachedFilesConfirm: "確定要清除所有遠端暫存資料嗎?"
blockedInstances: "已封鎖的伺服器" blockedInstances: "已封鎖的伺服器"
blockedInstancesDescription: "請逐行輸入需要封鎖的伺服器。已封鎖的伺服器將無法與本伺服器進行通訊。" blockedInstancesDescription: "請逐行輸入需要封鎖的伺服器。已封鎖的伺服器將無法與本伺服器進行通訊。"
muteAndBlock: "靜音和封鎖" muteAndBlock: "靜音和封鎖"
mutedUsers: "已靜音用戶" mutedUsers: "被靜音的使用者"
blockedUsers: "已封鎖用戶" blockedUsers: "被封鎖的使用者"
noUsers: "沒有任何使用者" noUsers: "沒有任何使用者"
editProfile: "編輯個人檔案" editProfile: "編輯個人檔案"
noteDeleteConfirm: "確定刪除此貼文嗎?" noteDeleteConfirm: "確定刪除此貼文嗎?"
@ -236,7 +237,7 @@ publishing: "直播中"
notResponding: "沒有回應" notResponding: "沒有回應"
instanceFollowing: "追隨的伺服器" instanceFollowing: "追隨的伺服器"
instanceFollowers: "伺服器的追隨者" instanceFollowers: "伺服器的追隨者"
instanceUsers: "用戶" instanceUsers: "伺服器使用者"
changePassword: "修改密碼" changePassword: "修改密碼"
security: "安全性" security: "安全性"
retypedNotMatch: "兩次輸入不一致。" retypedNotMatch: "兩次輸入不一致。"
@ -246,7 +247,7 @@ newPasswordRetype: "確認密碼"
attachFile: "上傳附件" attachFile: "上傳附件"
more: "更多!" more: "更多!"
featured: "精選" featured: "精選"
usernameOrUserId: "使用者名稱或使用者ID" usernameOrUserId: "使用者名稱或使用者 ID"
noSuchUser: "使用者不存在" noSuchUser: "使用者不存在"
lookup: "查詢" lookup: "查詢"
announcements: "公告" announcements: "公告"
@ -262,16 +263,16 @@ upload: "上傳"
keepOriginalUploading: "保留原圖" keepOriginalUploading: "保留原圖"
keepOriginalUploadingDescription: "上傳圖片時保留原始圖片。關閉時,瀏覽器會在上傳時生成適用於網路傳送的版本。" keepOriginalUploadingDescription: "上傳圖片時保留原始圖片。關閉時,瀏覽器會在上傳時生成適用於網路傳送的版本。"
fromDrive: "從雲端空間" fromDrive: "從雲端空間"
fromUrl: "從URL" fromUrl: "從 URL"
uploadFromUrl: "從網址上傳" uploadFromUrl: "從網址上傳"
uploadFromUrlDescription: "您要上傳的文件的URL" uploadFromUrlDescription: "您要上傳的檔案網址"
uploadFromUrlRequested: "已請求上傳" uploadFromUrlRequested: "已請求上傳"
uploadFromUrlMayTakeTime: "還需要一些時間才能完成上傳。" uploadFromUrlMayTakeTime: "還需要一些時間才能完成上傳。"
explore: "探索" explore: "探索"
messageRead: "已讀" messageRead: "已讀"
noMoreHistory: "沒有更多歷史紀錄" noMoreHistory: "沒有更多歷史紀錄"
startMessaging: "開始聊天" startMessaging: "開始聊天"
nUsersRead: "{n}人已讀" nUsersRead: "{n} 人已讀"
agreeTo: "我同意{0}" agreeTo: "我同意{0}"
agree: "同意" agree: "同意"
agreeBelow: "同意以下內容" agreeBelow: "同意以下內容"
@ -284,7 +285,7 @@ activity: "動態"
images: "圖片" images: "圖片"
image: "圖片" image: "圖片"
birthday: "生日" birthday: "生日"
yearsOld: "{age}歲" yearsOld: "{age} 歲"
registeredDate: "註冊日期" registeredDate: "註冊日期"
location: "位置" location: "位置"
theme: "外觀主題" theme: "外觀主題"
@ -292,8 +293,8 @@ themeForLightMode: "在淺色模式下使用的主題"
themeForDarkMode: "在深色模式下使用的主題" themeForDarkMode: "在深色模式下使用的主題"
light: "淺色" light: "淺色"
dark: "深色" dark: "深色"
lightThemes: "明亮主題" lightThemes: "淺色主題"
darkThemes: "黑暗主題" darkThemes: "深色主題"
syncDeviceDarkMode: "同步至此裝置的深色模式設定" syncDeviceDarkMode: "同步至此裝置的深色模式設定"
drive: "雲端硬碟" drive: "雲端硬碟"
fileName: "檔案名稱" fileName: "檔案名稱"
@ -334,13 +335,13 @@ instanceName: "伺服器名稱"
instanceDescription: "伺服器介紹" instanceDescription: "伺服器介紹"
maintainerName: "管理員名稱" maintainerName: "管理員名稱"
maintainerEmail: "管理員郵箱" maintainerEmail: "管理員郵箱"
tosUrl: "服務條款URL" tosUrl: "服務條款 URL"
thisYear: "本年" thisYear: "本年"
thisMonth: "本月" thisMonth: "本月"
today: "本日" today: "本日"
dayX: "{day}日" dayX: "{day} 日"
monthX: "{month}月" monthX: "{month} 月"
yearX: "{year}年" yearX: "{year} 年"
pages: "頁面" pages: "頁面"
integration: "整合" integration: "整合"
connectService: "已連結" connectService: "已連結"
@ -351,14 +352,14 @@ disablingTimelinesInfo: "為了方便,即使您關閉了時間線功能,管
registration: "註冊" registration: "註冊"
enableRegistration: "開放新使用者註冊" enableRegistration: "開放新使用者註冊"
invite: "邀請" invite: "邀請"
driveCapacityPerLocalAccount: "每個本地用戶的雲端空間大小" driveCapacityPerLocalAccount: "每個本地使用者的雲端硬碟容量"
driveCapacityPerRemoteAccount: "每個非本地用戶的雲端空間大小" driveCapacityPerRemoteAccount: "每個非本地用戶的雲端空間大小"
inMb: "以Mbps為單位" inMb: "以Mbps為單位"
iconUrl: "圖標 URL例如 favicon" iconUrl: "圖標 URL例如 favicon"
bannerUrl: "橫幅圖片URL" bannerUrl: "橫幅圖片URL"
backgroundImageUrl: "背景圖片的來源網址 " backgroundImageUrl: "背景圖片的來源網址 "
basicInfo: "基本資訊" basicInfo: "基本資訊"
pinnedUsers: "置頂用戶" pinnedUsers: "置頂使用者"
pinnedUsersDescription: "在「探索」頁面中使用換行標記想要置頂的使用者。" pinnedUsersDescription: "在「探索」頁面中使用換行標記想要置頂的使用者。"
pinnedPages: "釘選頁面" pinnedPages: "釘選頁面"
pinnedPagesDescription: "輸入要固定至實例首頁的頁面路徑,以換行符分隔。" pinnedPagesDescription: "輸入要固定至實例首頁的頁面路徑,以換行符分隔。"
@ -386,7 +387,7 @@ antennaExcludeKeywords: "排除關鍵字"
antennaKeywordsDescription: "空格代表「以及」AND換行代表「或者」OR" antennaKeywordsDescription: "空格代表「以及」AND換行代表「或者」OR"
notifyAntenna: "通知有新貼文" notifyAntenna: "通知有新貼文"
withFileAntenna: "僅帶有附件的貼文" withFileAntenna: "僅帶有附件的貼文"
enableServiceworker: "啟用 ServiceWorker" enableServiceworker: "啟用瀏覽器的推播通知"
antennaUsersDescription: "填寫使用者名稱,以換行分隔" antennaUsersDescription: "填寫使用者名稱,以換行分隔"
caseSensitive: "區分大小寫" caseSensitive: "區分大小寫"
withReplies: "包含回覆" withReplies: "包含回覆"
@ -394,7 +395,7 @@ connectedTo: "您的帳戶已連接到以下社交帳戶"
notesAndReplies: "貼文與回覆" notesAndReplies: "貼文與回覆"
withFiles: "附件" withFiles: "附件"
silence: "禁言" silence: "禁言"
silenceConfirm: "確定要禁言此帳戶嗎?" silenceConfirm: "確定要禁言此使用者嗎?"
unsilence: "解除禁言" unsilence: "解除禁言"
unsilenceConfirm: "確定要解除禁言嗎?" unsilenceConfirm: "確定要解除禁言嗎?"
popularUsers: "熱門使用者" popularUsers: "熱門使用者"
@ -426,8 +427,8 @@ resetPassword: "重設密碼"
newPasswordIs: "新密碼為「{password}」" newPasswordIs: "新密碼為「{password}」"
reduceUiAnimation: "減少介面的動態視覺" reduceUiAnimation: "減少介面的動態視覺"
share: "分享" share: "分享"
notFound: "找不到" notFound: "查無項目"
notFoundDescription: "找不到該 URL 的頁面" notFoundDescription: "查無此頁"
uploadFolder: "預設上傳資料夾" uploadFolder: "預設上傳資料夾"
cacheClear: "清除快取" cacheClear: "清除快取"
markAsReadAllNotifications: "標記所有通知為已讀" markAsReadAllNotifications: "標記所有通知為已讀"
@ -476,7 +477,7 @@ disableDrawer: "不顯示下拉式選單"
showNoteActionsOnlyHover: "僅在游標停留時顯示貼文的操作選項" showNoteActionsOnlyHover: "僅在游標停留時顯示貼文的操作選項"
noHistory: "沒有歷史紀錄" noHistory: "沒有歷史紀錄"
signinHistory: "登入歷史" signinHistory: "登入歷史"
enableAdvancedMfm: "啟用高級 MFM" enableAdvancedMfm: "啟用進階 MFM"
enableAnimatedMfm: "啟用 MFM 動畫" enableAnimatedMfm: "啟用 MFM 動畫"
doing: "正在進行" doing: "正在進行"
category: "類別" category: "類別"
@ -487,7 +488,7 @@ existingAccount: "現有帳戶"
regenerate: "再次生成" regenerate: "再次生成"
fontSize: "字體大小" fontSize: "字體大小"
mediaListWithOneImageAppearance: "只有一張圖片時的媒體列表高度" mediaListWithOneImageAppearance: "只有一張圖片時的媒體列表高度"
limitTo: "上限為{x}" limitTo: "上限為 {x}"
noFollowRequests: "沒有追隨您的請求" noFollowRequests: "沒有追隨您的請求"
openImageInNewTab: "於新分頁中開啟圖片" openImageInNewTab: "於新分頁中開啟圖片"
dashboard: "儀表板" dashboard: "儀表板"
@ -504,10 +505,10 @@ promote: "推廣"
numberOfDays: "有效天數" numberOfDays: "有效天數"
hideThisNote: "隱藏此貼文" hideThisNote: "隱藏此貼文"
showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦" showFeaturedNotesInTimeline: "在時間軸上顯示熱門推薦"
objectStorage: "Object Storage (物件儲存)" objectStorage: "對象存儲"
useObjectStorage: "使用Object Storage" useObjectStorage: "使用對象存儲"
objectStorageBaseUrl: "Base URL" objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "引用的 URL。如果您使用的是 CDN 或反向代理,請指定其 URL例如 S3https://<bucket>.s3.amazonaws.com、GCShttps://storage.googleapis.com/<bucket>)。" objectStorageBaseUrlDesc: "用於引用的 URL。如果您使用的是 CDN 或反向代理,請指定其 URL例如 S3https://<bucket>.s3.amazonaws.com、GCShttps://storage.googleapis.com/<bucket>)。"
objectStorageBucket: "儲存空間Bucket" objectStorageBucket: "儲存空間Bucket"
objectStorageBucketDesc: "請填寫所用服務的儲存空間Bucket名稱。 " objectStorageBucketDesc: "請填寫所用服務的儲存空間Bucket名稱。 "
objectStoragePrefix: "前綴" objectStoragePrefix: "前綴"
@ -532,7 +533,7 @@ sound: "音效"
listen: "聆聽" listen: "聆聽"
none: "無" none: "無"
showInPage: "在頁面中顯示" showInPage: "在頁面中顯示"
popout: "彈出型窗口" popout: "彈出式視窗"
volume: "音量" volume: "音量"
masterVolume: "主音量" masterVolume: "主音量"
details: "詳細資訊" details: "詳細資訊"
@ -542,7 +543,7 @@ recentUsed: "最近使用"
install: "安裝" install: "安裝"
uninstall: "解除安裝" uninstall: "解除安裝"
installedApps: "已授權的應用程式" installedApps: "已授權的應用程式"
nothing: "未發現" nothing: ""
installedDate: "安裝時間" installedDate: "安裝時間"
lastUsedDate: "最後上線日期" lastUsedDate: "最後上線日期"
state: "狀態" state: "狀態"
@ -550,7 +551,7 @@ sort: "排序"
ascendingOrder: "昇冪" ascendingOrder: "昇冪"
descendingOrder: "降冪" descendingOrder: "降冪"
scratchpad: "暫存記憶體" scratchpad: "暫存記憶體"
scratchpadDescription: "AiScript 控制為 AiScript 的實驗環境。您可以在此編寫、執行和確認程式碼與 Misskey 互動的果。" scratchpadDescription: "AiScript 控制為 AiScript 的實驗環境。您可以在此編寫、執行和確認程式碼與 Misskey 互動的果。"
output: "輸出" output: "輸出"
script: "腳本" script: "腳本"
disablePagesScript: "停用頁面的 AiScript 腳本" disablePagesScript: "停用頁面的 AiScript 腳本"
@ -559,8 +560,8 @@ deleteAllFiles: "刪除所有檔案"
deleteAllFilesConfirm: "要刪除所有檔案嗎?" deleteAllFilesConfirm: "要刪除所有檔案嗎?"
removeAllFollowing: "解除所有追隨" removeAllFollowing: "解除所有追隨"
removeAllFollowingDescription: "解除{host}所有的追隨。在伺服器不再存在時執行。" removeAllFollowingDescription: "解除{host}所有的追隨。在伺服器不再存在時執行。"
userSuspended: "該使用者已被停用" userSuspended: "該使用者已被停用"
userSilenced: "該用戶已被禁言。" userSilenced: "該使用者已被禁言。"
yourAccountSuspendedTitle: "帳戶已被凍結" yourAccountSuspendedTitle: "帳戶已被凍結"
yourAccountSuspendedDescription: "該帳戶已因違反伺服器服務條款或其他原因而被凍結。您可以向管理員查詢更多資訊。請不要建立新帳戶。" yourAccountSuspendedDescription: "該帳戶已因違反伺服器服務條款或其他原因而被凍結。您可以向管理員查詢更多資訊。請不要建立新帳戶。"
tokenRevoked: "權杖無效" tokenRevoked: "權杖無效"
@ -680,7 +681,8 @@ createNewClip: "建立新摘錄"
unclip: "解除摘錄" unclip: "解除摘錄"
confirmToUnclipAlreadyClippedNote: "此貼文已包含在摘錄「{name}」中。 你想將貼文從這個摘錄中排除嗎?" confirmToUnclipAlreadyClippedNote: "此貼文已包含在摘錄「{name}」中。 你想將貼文從這個摘錄中排除嗎?"
public: "公開" public: "公開"
i18nInfo: "Misskey 已被志願者們翻譯成各種語言版本。您可以瀏覽{link}幫助翻譯。" private: "私密"
i18nInfo: "Misskey 已被志願者們翻譯成各種語言版本。您可以瀏覽 {link} 幫助翻譯。"
manageAccessTokens: "管理存取權杖" manageAccessTokens: "管理存取權杖"
accountInfo: "帳戶資訊" accountInfo: "帳戶資訊"
notesCount: "貼文數量" notesCount: "貼文數量"
@ -688,7 +690,7 @@ repliesCount: "回覆數量"
renotesCount: "轉發數量" renotesCount: "轉發數量"
repliedCount: "回覆數量" repliedCount: "回覆數量"
renotedCount: "轉發次數" renotedCount: "轉發次數"
followingCount: "正在追隨的用戶數量" followingCount: "正在追隨的使用者數量"
followersCount: "追隨者數量" followersCount: "追隨者數量"
sentReactionsCount: "反應發送次數" sentReactionsCount: "反應發送次數"
receivedReactionsCount: "收到反應次數" receivedReactionsCount: "收到反應次數"
@ -729,11 +731,11 @@ reloadToApplySetting: "設定將會在頁面重新載入之後生效。要現在
needReloadToApply: "必須重新載入才會生效。" needReloadToApply: "必須重新載入才會生效。"
showTitlebar: "顯示標題列" showTitlebar: "顯示標題列"
clearCache: "清除快取資料" clearCache: "清除快取資料"
onlineUsersCount: "{n}人正在線上" onlineUsersCount: "{n} 人上線"
nUsers: "{n}用戶" nUsers: "{n} 使用者"
nNotes: "{n}貼文" nNotes: "{n} 貼文"
sendErrorReports: "傳送錯誤報告" sendErrorReports: "傳送錯誤報告"
sendErrorReportsDescription: "啟用後問題報告將傳送至開發者以提升軟體品質。問題報告可能包括OS版本,瀏覽器類型,行為歷史記錄等。" sendErrorReportsDescription: "傳送問題報告至開發者以提升軟體品質。問題報告可能包括作業系統版本,瀏覽器類型,行為歷史記錄等。"
myTheme: "我的佈景主題" myTheme: "我的佈景主題"
backgroundColor: "背景" backgroundColor: "背景"
accentColor: "重點色彩" accentColor: "重點色彩"
@ -770,13 +772,13 @@ clear: "清除"
markAllAsRead: "全部標示為已讀" markAllAsRead: "全部標示為已讀"
goBack: "返回" goBack: "返回"
unlikeConfirm: "要取消按讚嗎?" unlikeConfirm: "要取消按讚嗎?"
fullView: "全幕顯示" fullView: "全幕顯示"
quitFullView: "退出全螢幕顯示" quitFullView: "退出全螢幕顯示"
addDescription: "新增描述" addDescription: "新增描述"
userPagePinTip: "在貼文的選單中選擇\"置頂\",即可置頂該貼文至您的個人檔案頁面。" userPagePinTip: "在貼文的選單中選擇「置頂」,即可置頂該貼文至您的個人檔案頁面。"
notSpecifiedMentionWarning: "此貼文有未指定的提及" notSpecifiedMentionWarning: "此貼文有未指定的提及"
info: "資訊" info: "資訊"
userInfo: "用戶資料" userInfo: "使用者資訊"
unknown: "未知" unknown: "未知"
onlineStatus: "上線狀態" onlineStatus: "上線狀態"
hideOnlineStatus: "隱藏上線狀態" hideOnlineStatus: "隱藏上線狀態"
@ -835,7 +837,7 @@ accountDeletionInProgress: "正在刪除帳戶"
usernameInfo: "在伺服器上您的帳戶是唯一的識別名稱。您可以使用字母 (a ~ z, A ~ Z)、數字 (0 ~ 9) 和下底線 (_)。之後帳戶名是不能更改的。" usernameInfo: "在伺服器上您的帳戶是唯一的識別名稱。您可以使用字母 (a ~ z, A ~ Z)、數字 (0 ~ 9) 和下底線 (_)。之後帳戶名是不能更改的。"
aiChanMode: "小藍模式" aiChanMode: "小藍模式"
devMode: "開發者模式" devMode: "開發者模式"
keepCw: "保持 CW" keepCw: "保持隱藏內容"
pubSub: "Pub/Sub 帳戶" pubSub: "Pub/Sub 帳戶"
lastCommunication: "最近的通信" lastCommunication: "最近的通信"
resolved: "已解決" resolved: "已解決"
@ -849,7 +851,7 @@ off: "關閉"
emailRequiredForSignup: "註冊帳戶需要電子郵件地址" emailRequiredForSignup: "註冊帳戶需要電子郵件地址"
unread: "未讀" unread: "未讀"
filter: "篩選" filter: "篩選"
controlPanel: "控制" controlPanel: "控制"
manageAccounts: "管理帳戶" manageAccounts: "管理帳戶"
makeReactionsPublic: "將反應設為公開" makeReactionsPublic: "將反應設為公開"
makeReactionsPublicDescription: "將您做過的反應設為公開可見。" makeReactionsPublicDescription: "將您做過的反應設為公開可見。"
@ -893,8 +895,8 @@ cropImageAsk: "要剪裁圖片嗎?"
cropYes: "裁剪" cropYes: "裁剪"
cropNo: "使用原圖" cropNo: "使用原圖"
file: "檔案" file: "檔案"
recentNHours: "過去{n}小時" recentNHours: "過去 {n} 小時"
recentNDays: "過去{n}天" recentNDays: "過去 {n} 天"
noEmailServerWarning: "尚未設定電子郵件伺服器。" noEmailServerWarning: "尚未設定電子郵件伺服器。"
thereIsUnresolvedAbuseReportWarning: "有尚未處理的檢舉。" thereIsUnresolvedAbuseReportWarning: "有尚未處理的檢舉。"
recommended: "推薦" recommended: "推薦"
@ -930,7 +932,7 @@ cannotUploadBecauseExceedsFileSizeLimit: "由於超過了檔案大小的限制
beta: "測試版" beta: "測試版"
enableAutoSensitive: "自動 NSFW 判定" enableAutoSensitive: "自動 NSFW 判定"
enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷多媒體內容是否需要標記 NSFW。即使關閉此功能也可能會依實例規則而自動啟用。" enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷多媒體內容是否需要標記 NSFW。即使關閉此功能也可能會依實例規則而自動啟用。"
activeEmailValidationDescription: "積極驗證帳戶的電郵地址,以判斷它是否可以通訊。關閉此選項代表只會檢查地址是否符合格式。" activeEmailValidationDescription: "積極驗證使用者的電郵地址,以判斷它是否可以通訊。關閉此選項代表只會檢查地址是否符合格式。"
navbar: "導覽列" navbar: "導覽列"
shuffle: "隨機" shuffle: "隨機"
account: "帳戶" account: "帳戶"
@ -1015,7 +1017,7 @@ drivecleaner: "雲端硬碟清掃器"
retryAllQueuesNow: "立刻重試所有佇列" retryAllQueuesNow: "立刻重試所有佇列"
retryAllQueuesConfirmTitle: "要現在重試嗎?" retryAllQueuesConfirmTitle: "要現在重試嗎?"
retryAllQueuesConfirmText: "伺服器的負荷可能會暫時增加。" retryAllQueuesConfirmText: "伺服器的負荷可能會暫時增加。"
enableChartsForRemoteUser: "生成遠端用戶的圖表" enableChartsForRemoteUser: "生成遠端使用者的圖表"
enableChartsForFederatedInstances: "生成遠端伺服器的圖表" enableChartsForFederatedInstances: "生成遠端伺服器的圖表"
showClipButtonInNoteFooter: "新增摘錄至貼文" showClipButtonInNoteFooter: "新增摘錄至貼文"
largeNoteReactions: "放大顯示貼文反應" largeNoteReactions: "放大顯示貼文反應"
@ -1094,6 +1096,21 @@ expired: "過期"
doYouAgree: "你同意嗎?" doYouAgree: "你同意嗎?"
beSureToReadThisAsItIsImportant: "重要,請務必閱讀。" beSureToReadThisAsItIsImportant: "重要,請務必閱讀。"
iHaveReadXCarefullyAndAgree: "我已仔細閱讀並同意「{x}」的内容。" iHaveReadXCarefullyAndAgree: "我已仔細閱讀並同意「{x}」的内容。"
dialog: "對話方塊"
icon: "圖示"
forYou: "給您"
currentAnnouncements: "最新公告"
pastAnnouncements: "歷史公告"
youHaveUnreadAnnouncements: "有未讀的公告。"
_announcement:
forExistingUsers: "僅限既有的使用者"
forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。"
needConfirmationToRead: "必須確認才能標記為已讀"
needConfirmationToReadDescription: "啟用代表此公告將顯示對話方塊以確認是否標記為已讀,同時不會受「標記所有公告為已讀」功能影響。"
end: "結束公告"
tooManyActiveAnnouncementDescription: "有過多公告可能會影響使用者體驗。請考慮歸檔已結束的公告。"
readConfirmTitle: "標記為已讀嗎?"
readConfirmText: "閱讀「{title}」的內容並標記為已讀。"
_initialAccountSetting: _initialAccountSetting:
accountCreated: "帳戶已建立完成!" accountCreated: "帳戶已建立完成!"
letsStartAccountSetup: "來進行帳戶的初始設定吧。" letsStartAccountSetup: "來進行帳戶的初始設定吧。"
@ -1106,7 +1123,7 @@ _initialAccountSetting:
pushNotificationDescription: "啟用推送通知,就可以在設備上接收{name}的通知。" pushNotificationDescription: "啟用推送通知,就可以在設備上接收{name}的通知。"
initialAccountSettingCompleted: "初始設定完成了!" initialAccountSettingCompleted: "初始設定完成了!"
haveFun: "盡情享受{name}吧!" haveFun: "盡情享受{name}吧!"
ifYouNeedLearnMore: "關於如何使用{name}(Misskey)的詳細資訊,請見{link}。" ifYouNeedLearnMore: "請瀏覽{link}以更瞭解{name}Misskey的使用方法。"
skipAreYouSure: "要略過初始設定嗎?" skipAreYouSure: "要略過初始設定嗎?"
laterAreYouSure: "稍後再重新進行初始設定嗎?" laterAreYouSure: "稍後再重新進行初始設定嗎?"
_serverRules: _serverRules:
@ -1119,7 +1136,7 @@ _accountMigration:
moveTo: "將這個帳戶遷移至新的帳戶" moveTo: "將這個帳戶遷移至新的帳戶"
moveToLabel: "要遷移到的帳戶:" moveToLabel: "要遷移到的帳戶:"
moveCannotBeUndone: "一旦遷移帳戶,就無法取消。" moveCannotBeUndone: "一旦遷移帳戶,就無法取消。"
moveAccountDescription: "這個操作不可撤銷。首先,請確認已在要遷移到的帳戶中為這個帳戶建立了一個別名。建立別名之後,像這樣輸入你要遷移到的帳戶:@person@instance.com" moveAccountDescription: "遷移至新帳戶。\n ・此帳戶的追隨者將自動追隨新帳戶\n ・此帳戶的所有追隨者將被取消追隨\n ・此帳戶不能再發文。\n\n雖然會自動遷移您追隨者但必須手動遷移您追隨的帳戶。請在遷移前匯出此帳戶的「追隨中」名單並在遷移後自行匯入。\n列表名單、靜音名單及封鎖名單也必須如此處理。\n\n此說明適用於本伺服器以及運行 Misskey v13.12.0 或更新版本的其他伺服器;如 Mastodon 等使用 ActivityPub 協定的其他軟體或有不同的處理方式。)"
moveAccountHowTo: "要遷移帳戶,首先要在目標帳戶中為此帳戶建立一個別名。\n 建立別名後,像這樣輸入目標帳戶:@username@server.example.com" moveAccountHowTo: "要遷移帳戶,首先要在目標帳戶中為此帳戶建立一個別名。\n 建立別名後,像這樣輸入目標帳戶:@username@server.example.com"
startMigration: "遷移" startMigration: "遷移"
migrationConfirm: "確定要將這個帳戶遷移至 {account} 嗎?一旦遷移就無法撤銷,也就無法以原來的狀態使用這個帳戶。\n另外請確認在要遷移到的帳戶已經建立了一個別名。" migrationConfirm: "確定要將這個帳戶遷移至 {account} 嗎?一旦遷移就無法撤銷,也就無法以原來的狀態使用這個帳戶。\n另外請確認在要遷移到的帳戶已經建立了一個別名。"
@ -1242,7 +1259,7 @@ _achievements:
title: "有備而來" title: "有備而來"
description: "設定了個人檔案" description: "設定了個人檔案"
_markedAsCat: _markedAsCat:
title: "吾輩乃貓是也" title: "我是貓"
description: "已將帳戶設定為貓" description: "已將帳戶設定為貓"
flavor: "還沒有名字。" flavor: "還沒有名字。"
_following1: _following1:
@ -1264,7 +1281,7 @@ _achievements:
title: "第一個追隨者" title: "第一個追隨者"
description: "第一次被追隨" description: "第一次被追隨"
_followers10: _followers10:
title: "Follow me!" title: "追隨我吧!"
description: "追隨者超過10人了" description: "追隨者超過10人了"
_followers50: _followers50:
title: "成群結隊" title: "成群結隊"
@ -1273,20 +1290,20 @@ _achievements:
title: "熱門人物" title: "熱門人物"
description: "追隨者超過100人了" description: "追隨者超過100人了"
_followers300: _followers300:
title: "請排成一排" title: "請排"
description: "追隨者超過300人了" description: "追隨者超過300人了"
_followers500: _followers500:
title: "基地" title: "基地"
description: "超過五百名追隨者了" description: "超過五百名追隨者了"
_followers1000: _followers1000:
title: "影響者" title: "星光熠熠"
description: "超過一千名追隨者了" description: "超過一千名追隨者了"
_collectAchievements30: _collectAchievements30:
title: "成就收藏家" title: "成就收藏家"
description: "獲得三十個以上的成就" description: "獲得三十個以上的成就"
_viewAchievements3min: _viewAchievements3min:
title: "喜愛成就" title: "成就發燒友"
description: "看成就列表要花三分鐘以上" description: "看著成就列表超過三分鐘"
_iLoveMisskey: _iLoveMisskey:
title: "I Love Misskey" title: "I Love Misskey"
description: "發佈「I ❤ #Misskey」" description: "發佈「I ❤ #Misskey」"
@ -1298,34 +1315,34 @@ _achievements:
title: "休息一下" title: "休息一下"
description: "客戶端啟動已超過30分鐘" description: "客戶端啟動已超過30分鐘"
_client60min: _client60min:
title: "Misskey看太多" title: "Misskey 看太多"
description: "客戶端啟動已超過60分鐘" description: "客戶端啟動已超過60分鐘"
_noteDeletedWithin1min: _noteDeletedWithin1min:
title: "現在沒有了" title: "欲言又止"
description: "發文後1分鐘內刪文" description: "發文後分鐘內刪文"
_postedAtLateNight: _postedAtLateNight:
title: "夜行性" title: "夜貓子"
description: "在深夜發佈貼文" description: "在深夜發佈貼文"
flavor: "該去睡覺了。" flavor: "該去睡覺了。"
_postedAt0min0sec: _postedAt0min0sec:
title: "報時" title: "報時"
description: "在0分0秒發佈貼文" description: "在零分零秒發佈貼文"
flavor: "啵.啵.啵.嗶ー" flavor: "啵、啵、啵、嗶ーー"
_selfQuote: _selfQuote:
title: "自我引用" title: "自我引用"
description: "引用了自己的貼文" description: "引用了自己的貼文"
_htl20npm: _htl20npm:
title: "流動的TL" title: "源源不絕"
description: "在首頁時間軸的流速超過20npm" description: "首頁時間軸在一分鐘內出現超過二十篇貼文"
_viewInstanceChart: _viewInstanceChart:
title: "分析師" title: "分析師"
description: "顯示了實例的圖表" description: "顯示了實例的圖表"
_outputHelloWorldOnScratchpad: _outputHelloWorldOnScratchpad:
title: "Hello world!" title: "Hello, world!"
description: "在暫存記憶體輸出了 hello world" description: "在 AiScript 控制臺輸出了「hello world」"
_open3windows: _open3windows:
title: "多重視窗" title: "多重視窗"
description: "開啟了3個以上的視窗" description: "開啟過三個以上的視窗"
_driveFolderCircularReference: _driveFolderCircularReference:
title: "循環引用" title: "循環引用"
description: "試圖遞迴套入雲端硬碟資料夾" description: "試圖遞迴套入雲端硬碟資料夾"
@ -1337,33 +1354,33 @@ _achievements:
description: "已點擊這裡了" description: "已點擊這裡了"
_justPlainLucky: _justPlainLucky:
title: "只是運氣好" title: "只是運氣好"
description: "每10秒有0.01%的機率獲得" description: "每十秒有二萬分之一0.005%的機率獲得"
_setNameToSyuilo: _setNameToSyuilo:
title: "神的情結" title: "神與您同在"
description: "將名稱設定為 syuilo" description: "將名稱設定為 syuilo"
_passedSinceAccountCreated1: _passedSinceAccountCreated1:
title: "一年" title: "一年"
description: "自建立帳戶開始過了1年" description: "帳戶加入時間已超過一年"
_passedSinceAccountCreated2: _passedSinceAccountCreated2:
title: "二年" title: "二年"
description: "自建立帳戶開始過了2年" description: "帳戶加入時間已超過兩年"
_passedSinceAccountCreated3: _passedSinceAccountCreated3:
title: "三年" title: "三年"
description: "自建立帳戶開始過了3年" description: "帳戶加入時間已超過三年"
_loggedInOnBirthday: _loggedInOnBirthday:
title: "生日快樂" title: "生日快樂"
description: "在生日當天登入了" description: "在生日當天登入了"
_loggedInOnNewYearsDay: _loggedInOnNewYearsDay:
title: "新年快樂" title: "新年快樂"
description: "在元旦當天登入了" description: "在元旦當天登入了"
flavor: "今年也請對敝實例多多指教" flavor: "今年也請您多多指教!"
_cookieClicked: _cookieClicked:
title: "點擊餅乾的遊戲" title: "點擊餅乾的遊戲"
description: "點擊了餅乾" description: "點擊了餅乾"
flavor: "是不是軟體有問題?" flavor: "是不是軟體有問題?"
_brainDiver: _brainDiver:
title: "Brain Driver" title: "Brain Driver"
description: "發佈了Brain Driver的連結" description: "發佈一篇含歌曲《Brain Driver》連結的貼文"
flavor: "Misskey-Misskey La-Tu-Ma" flavor: "Misskey-Misskey La-Tu-Ma"
_role: _role:
new: "建立角色" new: "建立角色"
@ -1387,7 +1404,7 @@ _role:
chooseRoleToAssign: "選擇要指派的角色" chooseRoleToAssign: "選擇要指派的角色"
iconUrl: "圖示的URL" iconUrl: "圖示的URL"
asBadge: "顯示為徽章" asBadge: "顯示為徽章"
descriptionOfAsBadge: "開啟的話,角色圖示會顯示在用戶名旁邊。" descriptionOfAsBadge: "開啟的話,角色圖示會顯示在使用者名稱旁邊。"
isExplorable: "讓使用者更容易找到您" isExplorable: "讓使用者更容易找到您"
descriptionOfIsExplorable: "若開啟則公開角色時間軸。若角色不是公開的,則無法公開時間軸。" descriptionOfIsExplorable: "若開啟則公開角色時間軸。若角色不是公開的,則無法公開時間軸。"
displayOrder: "顯示順序" displayOrder: "顯示順序"
@ -1425,8 +1442,8 @@ _role:
_condition: _condition:
isLocal: "本地使用者" isLocal: "本地使用者"
isRemote: "遠端使用者" isRemote: "遠端使用者"
createdLessThan: "自建立帳戶開始~以內" createdLessThan: "帳戶加入時間不超過"
createdMoreThan: "自建立帳戶開始~經過" createdMoreThan: "帳戶加入時間已超過"
followersLessThanOrEq: "追隨者人數在~以下" followersLessThanOrEq: "追隨者人數在~以下"
followersMoreThanOrEq: "追隨者人數在~以上" followersMoreThanOrEq: "追隨者人數在~以上"
followingLessThanOrEq: "追隨人數在~以下" followingLessThanOrEq: "追隨人數在~以下"
@ -1452,7 +1469,7 @@ _emailUnavailable:
smtp: "郵件伺服器沒有應答" smtp: "郵件伺服器沒有應答"
_ffVisibility: _ffVisibility:
public: "公開" public: "公開"
followers: "只有關注你的用戶能看到" followers: "只有關注您的使用者能看到"
private: "私密" private: "私密"
_signup: _signup:
almostThere: "即將完成" almostThere: "即將完成"
@ -1513,7 +1530,7 @@ _registry:
domain: "域" domain: "域"
createKey: "新增機碼" createKey: "新增機碼"
_aboutMisskey: _aboutMisskey:
about: "Misskey是由syuilo自2014年起開發的開源軟體。" about: "Misskey 是由 syuilo 自 2014 年起開發的開放原始碼軟體。"
contributors: "主要貢獻者" contributors: "主要貢獻者"
allContributors: "全體貢獻人員" allContributors: "全體貢獻人員"
source: "原始碼" source: "原始碼"
@ -1541,8 +1558,8 @@ _channel:
featured: "熱門貼文" featured: "熱門貼文"
owned: "管理中" owned: "管理中"
following: "追隨中" following: "追隨中"
usersCount: "有{n}人參與" usersCount: "有 {n} 人參與"
notesCount: "有{n}個貼文" notesCount: "有 {n} 篇貼文"
nameAndDescription: "名稱與說明" nameAndDescription: "名稱與說明"
nameOnly: "僅名稱" nameOnly: "僅名稱"
_menuDisplay: _menuDisplay:
@ -1560,7 +1577,7 @@ _wordMute:
hard: "硬性靜音" hard: "硬性靜音"
mutedNotes: "已靜音的貼文" mutedNotes: "已靜音的貼文"
_instanceMute: _instanceMute:
instanceMuteDescription: "包括對被靜音實例上的用戶的回覆,被設定的實例上所有貼文及轉發都會被靜音。" instanceMuteDescription: "包括對被靜音實例上的使用者的回覆,被設定的實例上所有貼文及轉發都會被靜音。"
instanceMuteDescription2: "換行以分隔" instanceMuteDescription2: "換行以分隔"
title: "將隱藏被設定的實例貼文。" title: "將隱藏被設定的實例貼文。"
heading: "將實例靜音" heading: "將實例靜音"
@ -1622,9 +1639,9 @@ _theme:
infoFg: "資訊內容" infoFg: "資訊內容"
infoWarnBg: "警告背景" infoWarnBg: "警告背景"
infoWarnFg: "警告文字" infoWarnFg: "警告文字"
cwBg: "CW 按鈕背景" cwBg: "隱藏內容按鈕背景"
cwFg: "CW 按鈕文字" cwFg: "隱藏內容按鈕文字"
cwHoverBg: "CW 按鈕背景(懸浮)" cwHoverBg: "隱藏內容按鈕背景(懸浮)"
toastBg: "通知背景" toastBg: "通知背景"
toastFg: "通知文本" toastFg: "通知文本"
buttonBg: "按鈕背景" buttonBg: "按鈕背景"
@ -1649,14 +1666,14 @@ _sfx:
_ago: _ago:
future: "未來" future: "未來"
justNow: "剛剛" justNow: "剛剛"
secondsAgo: "{n}秒前" secondsAgo: "{n} 秒前"
minutesAgo: "{n}分鐘前" minutesAgo: "{n}分鐘前 "
hoursAgo: "{n}小時前" hoursAgo: "{n} 小時前"
daysAgo: "{n}天前" daysAgo: "{n} 天前"
weeksAgo: "{n}週前" weeksAgo: "{n} 週前"
monthsAgo: "{n}個月前" monthsAgo: "{n} 個月前"
yearsAgo: "{n}年前" yearsAgo: "{n} 年前"
invalid: "未發現" invalid: ""
_time: _time:
second: "秒" second: "秒"
minute: "分鐘" minute: "分鐘"
@ -1700,13 +1717,13 @@ _2fa:
_permissions: _permissions:
"read:account": "查看我的帳戶資訊" "read:account": "查看我的帳戶資訊"
"write:account": "更改我的帳戶資訊" "write:account": "更改我的帳戶資訊"
"read:blocks": "已封鎖用戶名單" "read:blocks": "查看封鎖名單"
"write:blocks": "編輯封鎖用戶名單" "write:blocks": "編輯封鎖名單"
"read:drive": "存取雲端硬碟" "read:drive": "存取雲端硬碟"
"write:drive": "編輯雲端硬碟的檔案" "write:drive": "編輯雲端硬碟的檔案"
"read:favorites": "瀏覽我的最愛" "read:favorites": "瀏覽我的最愛"
"write:favorites": "編輯我的最愛列表" "write:favorites": "編輯我的最愛列表"
"read:following": "查看追隨中的用戶資訊" "read:following": "查看追隨中的使用者資訊"
"write:following": "追隨/解除追隨" "write:following": "追隨/解除追隨"
"read:messaging": "顯示訊息" "read:messaging": "顯示訊息"
"write:messaging": "撰寫或刪除訊息" "write:messaging": "撰寫或刪除訊息"
@ -1730,10 +1747,14 @@ _permissions:
"write:gallery": "操作圖庫" "write:gallery": "操作圖庫"
"read:gallery-likes": "讀取喜歡的圖片" "read:gallery-likes": "讀取喜歡的圖片"
"write:gallery-likes": "操作喜歡的圖片" "write:gallery-likes": "操作喜歡的圖片"
"read:flash": "檢視 Play"
"write:flash": "操作 Play"
"read:flash-likes": "檢視 Play 的讚"
"write:flash-likes": "對 Play 的讚進行操作"
_auth: _auth:
shareAccessTitle: "應用程式的存取權限" shareAccessTitle: "應用程式的存取權限"
shareAccess: "要授權「“{name}”」存取您的帳戶嗎?" shareAccess: "要授權「“{name}”」存取您的帳戶嗎?"
shareAccessAsk: "您確定要授權這個應用程式使用您的帳戶嗎?" shareAccessAsk: "您確定要授權這個應用程式存取您的帳戶嗎?"
permission: "{name}要求以下的權限" permission: "{name}要求以下的權限"
permissionAsk: "此應用程式需要以下權限" permissionAsk: "此應用程式需要以下權限"
pleaseGoBack: "請返回至應用程式" pleaseGoBack: "請返回至應用程式"
@ -1760,7 +1781,7 @@ _widgets:
notifications: "通知" notifications: "通知"
timeline: "時間軸" timeline: "時間軸"
calendar: "行事曆" calendar: "行事曆"
trends: "發燒貼文" trends: "熱門貼文"
clock: "時鐘" clock: "時鐘"
rss: "RSS 閱讀器" rss: "RSS 閱讀器"
rssTicker: "RSS 跑馬燈" rssTicker: "RSS 跑馬燈"
@ -1768,15 +1789,15 @@ _widgets:
photos: "照片" photos: "照片"
digitalClock: "電子時鐘" digitalClock: "電子時鐘"
unixClock: "UNIX 時間" unixClock: "UNIX 時間"
federation: "站台聯邦" federation: "聯邦宇宙"
instanceCloud: "實例雲" instanceCloud: "實例雲"
postForm: "發佈窗口" postForm: "發文視窗"
slideshow: "幻燈片" slideshow: "幻燈片"
button: "按鈕" button: "按鈕"
onlineUsers: "線上的用戶" onlineUsers: "上線使用者"
jobQueue: "佇列" jobQueue: "佇列"
serverMetric: "伺服器指標 " serverMetric: "伺服器指標 "
aiscript: "AiScript 控制" aiscript: "AiScript 控制"
aiscriptApp: "AiScript App" aiscriptApp: "AiScript App"
aichan: "小藍" aichan: "小藍"
userList: "使用者列表" userList: "使用者列表"
@ -1789,8 +1810,8 @@ _cw:
chars: "{count} 個字元" chars: "{count} 個字元"
files: "{count} 個檔案" files: "{count} 個檔案"
_poll: _poll:
noOnlyOneChoice: "至少需要兩個選項。" noOnlyOneChoice: "需要至少兩個選項。"
choiceN: "選 {n}" choiceN: "選 {n}"
noMore: "沒辦法再添加選項了" noMore: "沒辦法再添加選項了"
canMultipleVote: "可以多次投票" canMultipleVote: "可以多次投票"
expiration: "期限" expiration: "期限"
@ -1812,7 +1833,7 @@ _poll:
remainingSeconds: "{s} 秒後截止" remainingSeconds: "{s} 秒後截止"
_visibility: _visibility:
public: "公開" public: "公開"
publicDescription: "發佈給所有帳戶" publicDescription: "發佈給所有使用者"
home: "首頁" home: "首頁"
homeDescription: "僅發布至首頁的時間軸" homeDescription: "僅發布至首頁的時間軸"
followers: "追隨者" followers: "追隨者"
@ -1837,8 +1858,8 @@ _profile:
username: "使用者名稱" username: "使用者名稱"
description: "關於我" description: "關於我"
youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag" youCanIncludeHashtags: "你也可以在「關於我」中加上 #tag"
metadata: "進階資訊" metadata: "附加資訊"
metadataEdit: "編輯進階資訊" metadataEdit: "編輯附加資訊"
metadataDescription: "可以在個人資料中以表格形式顯示其他資訊。" metadataDescription: "可以在個人資料中以表格形式顯示其他資訊。"
metadataLabel: "標籤" metadataLabel: "標籤"
metadataContent: "内容" metadataContent: "内容"
@ -1851,10 +1872,10 @@ _exportOrImport:
muteList: "靜音" muteList: "靜音"
blockingList: "封鎖" blockingList: "封鎖"
userLists: "清單" userLists: "清單"
excludeMutingUsers: "排除被靜音的用戶" excludeMutingUsers: "排除被靜音的使用者"
excludeInactiveUsers: "排除不活躍帳戶" excludeInactiveUsers: "排除不活躍帳戶"
_charts: _charts:
federation: "站台聯邦" federation: "聯邦宇宙"
apRequest: "請求" apRequest: "請求"
usersIncDec: "使用者增減" usersIncDec: "使用者增減"
usersTotal: "使用者合共" usersTotal: "使用者合共"
@ -1895,7 +1916,7 @@ _play:
viewSource: "檢視原始碼" viewSource: "檢視原始碼"
my: "自己的 Play" my: "自己的 Play"
liked: "按讚的 Play" liked: "按讚的 Play"
featured: "人氣" featured: "熱門"
title: "標題" title: "標題"
script: "腳本" script: "腳本"
summary: "描述" summary: "描述"
@ -1917,7 +1938,7 @@ _pages:
unlike: "收回讚好" unlike: "收回讚好"
my: "我的頁面" my: "我的頁面"
liked: "已讚好的頁面" liked: "已讚好的頁面"
featured: "人氣" featured: "熱門"
inspector: "面板檢查" inspector: "面板檢查"
contents: "內容" contents: "內容"
content: "頁面方塊" content: "頁面方塊"

View file

@ -1,6 +1,6 @@
{ {
"name": "misskey", "name": "misskey",
"version": "13.14.2", "version": "2023.9.0-beta.2",
"codename": "nasubi", "codename": "nasubi",
"repository": { "repository": {
"type": "git", "type": "git",

View file

@ -16,5 +16,9 @@ export class addRenoteMuting1665091090561 {
} }
async down(queryRunner) { async down(queryRunner) {
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muterId"`);
await queryRunner.query(`DROP INDEX "IDX_renote_muting_muteeId"`);
await queryRunner.query(`DROP INDEX "IDX_renote_muting_createdAt"`);
await queryRunner.query(`DROP TABLE "renote_muting"`);
} }
} }

View file

@ -0,0 +1,12 @@
export class FixRenoteMuting1690417561185 {
name = 'FixRenoteMuting1690417561185'
async up(queryRunner) {
await queryRunner.query(`DELETE FROM "renote_muting" WHERE "muteeId" NOT IN (SELECT "id" FROM "user")`);
await queryRunner.query(`DELETE FROM "renote_muting" WHERE "muterId" NOT IN (SELECT "id" FROM "user")`);
}
async down(queryRunner) {
}
}

View file

@ -0,0 +1,15 @@
/*
* SPDX-FileCopyrightText: syuilo and other misskey contributors
* SPDX-License-Identifier: AGPL-3.0-only
*/
export class PlayVisibility1689102832143 {
name = 'PlayVisibility1690796169261'
async up(queryRunner) {
await queryRunner.query(`ALTER TABLE "public"."flash" ADD "visibility" character varying(512) DEFAULT 'public'`, undefined);
}
async down(queryRunner) {
await queryRunner.query(`ALTER TABLE "public"."flash" DROP COLUMN "visibility"`, undefined);
}
}

View file

@ -76,6 +76,7 @@ export type Source = {
id: string; id: string;
outgoingAddress?: string;
outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual'; outgoingAddressFamily?: 'ipv4' | 'ipv6' | 'dual';
deliverJobConcurrency?: number; deliverJobConcurrency?: number;

View file

@ -8,7 +8,7 @@ import { IsNull, In, MoreThan, Not } from 'typeorm';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/entities/User.js';
import type { BlockingsRepository, FollowingsRepository, InstancesRepository, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/index.js'; import type { BlockingsRepository, FollowingsRepository, InstancesRepository, MutingsRepository, UserListJoiningsRepository, UsersRepository } from '@/models/index.js';
import type { RelationshipJobData, ThinUser } from '@/queue/types.js'; import type { RelationshipJobData, ThinUser } from '@/queue/types.js';
@ -71,12 +71,12 @@ export class AccountMoveService {
* After delivering Move activity, its local followers unfollow the old account and then follow the new one. * After delivering Move activity, its local followers unfollow the old account and then follow the new one.
*/ */
@bindThis @bindThis
public async moveFromLocal(src: LocalUser, dst: LocalUser | RemoteUser): Promise<unknown> { public async moveFromLocal(src: MiLocalUser, dst: MiLocalUser | MiRemoteUser): Promise<unknown> {
const srcUri = this.userEntityService.getUserUri(src); const srcUri = this.userEntityService.getUserUri(src);
const dstUri = this.userEntityService.getUserUri(dst); const dstUri = this.userEntityService.getUserUri(dst);
// add movedToUri to indicate that the user has moved // add movedToUri to indicate that the user has moved
const update = {} as Partial<LocalUser>; const update = {} as Partial<MiLocalUser>;
update.alsoKnownAs = src.alsoKnownAs?.includes(dstUri) ? src.alsoKnownAs : src.alsoKnownAs?.concat([dstUri]) ?? [dstUri]; update.alsoKnownAs = src.alsoKnownAs?.includes(dstUri) ? src.alsoKnownAs : src.alsoKnownAs?.concat([dstUri]) ?? [dstUri];
update.movedToUri = dstUri; update.movedToUri = dstUri;
update.movedAt = new Date(); update.movedAt = new Date();
@ -114,7 +114,7 @@ export class AccountMoveService {
} }
@bindThis @bindThis
public async postMoveProcess(src: User, dst: User): Promise<void> { public async postMoveProcess(src: MiUser, dst: MiUser): Promise<void> {
// Copy blockings and mutings, and update lists // Copy blockings and mutings, and update lists
try { try {
await Promise.all([ await Promise.all([
@ -213,7 +213,7 @@ export class AccountMoveService {
* @returns Promise<void> * @returns Promise<void>
*/ */
@bindThis @bindThis
public async updateLists(src: ThinUser, dst: User): Promise<void> { public async updateLists(src: ThinUser, dst: MiUser): Promise<void> {
// Return if there is no list to be updated. // Return if there is no list to be updated.
const oldJoinings = await this.userListJoiningsRepository.find({ const oldJoinings = await this.userListJoiningsRepository.find({
where: { where: {
@ -260,7 +260,7 @@ export class AccountMoveService {
} }
@bindThis @bindThis
private async adjustFollowingCounts(localFollowerIds: string[], oldAccount: User): Promise<void> { private async adjustFollowingCounts(localFollowerIds: string[], oldAccount: MiUser): Promise<void> {
if (localFollowerIds.length === 0) return; if (localFollowerIds.length === 0) return;
// Set the old account's following and followers counts to 0. // Set the old account's following and followers counts to 0.
@ -301,11 +301,11 @@ export class AccountMoveService {
*/ */
@bindThis @bindThis
public async validateAlsoKnownAs( public async validateAlsoKnownAs(
dst: LocalUser | RemoteUser, dst: MiLocalUser | MiRemoteUser,
check: (oldUser: LocalUser | RemoteUser | null, newUser: LocalUser | RemoteUser) => boolean | Promise<boolean> = () => true, check: (oldUser: MiLocalUser | MiRemoteUser | null, newUser: MiLocalUser | MiRemoteUser) => boolean | Promise<boolean> = () => true,
instant = false, instant = false,
): Promise<LocalUser | RemoteUser | null> { ): Promise<MiLocalUser | MiRemoteUser | null> {
let resultUser: LocalUser | RemoteUser | null = null; let resultUser: MiLocalUser | MiRemoteUser | null = null;
if (this.userEntityService.isRemoteUser(dst)) { if (this.userEntityService.isRemoteUser(dst)) {
if ((new Date()).getTime() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) { if ((new Date()).getTime() - (dst.lastFetchedAt?.getTime() ?? 0) > 10 * 1000) {

View file

@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { UsersRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
import { RelayService } from '@/core/RelayService.js'; import { RelayService } from '@/core/RelayService.js';
import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js'; import { ApDeliverManagerService } from '@/core/activitypub/ApDeliverManagerService.js';
@ -27,7 +27,7 @@ export class AccountUpdateService {
} }
@bindThis @bindThis
public async publishToFollowers(userId: User['id']) { public async publishToFollowers(userId: MiUser['id']) {
const user = await this.usersRepository.findOneBy({ id: userId }); const user = await this.usersRepository.findOneBy({ id: userId });
if (user == null) throw new Error('user not found'); if (user == null) throw new Error('user not found');

View file

@ -5,7 +5,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import type { UserProfilesRepository } from '@/models/index.js'; import type { UserProfilesRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { NotificationService } from '@/core/NotificationService.js'; import { NotificationService } from '@/core/NotificationService.js';
@ -99,7 +99,7 @@ export class AchievementService {
@bindThis @bindThis
public async create( public async create(
userId: User['id'], userId: MiUser['id'],
type: typeof ACHIEVEMENT_TYPES[number], type: typeof ACHIEVEMENT_TYPES[number],
): Promise<void> { ): Promise<void> {
if (!ACHIEVEMENT_TYPES.includes(type)) return; if (!ACHIEVEMENT_TYPES.includes(type)) return;

View file

@ -6,8 +6,8 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Brackets } from 'typeorm'; import { Brackets } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import type { AnnouncementReadsRepository, AnnouncementsRepository, Announcement, AnnouncementRead } from '@/models/index.js'; import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead } from '@/models/index.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { Packed } from '@/misc/json-schema.js'; import { Packed } from '@/misc/json-schema.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
@ -28,14 +28,14 @@ export class AnnouncementService {
} }
@bindThis @bindThis
public async getReads(userId: User['id']): Promise<AnnouncementRead[]> { public async getReads(userId: MiUser['id']): Promise<MiAnnouncementRead[]> {
return this.announcementReadsRepository.findBy({ return this.announcementReadsRepository.findBy({
userId: userId, userId: userId,
}); });
} }
@bindThis @bindThis
public async getUnreadAnnouncements(user: User): Promise<Announcement[]> { public async getUnreadAnnouncements(user: MiUser): Promise<MiAnnouncement[]> {
const readsQuery = this.announcementReadsRepository.createQueryBuilder('read') const readsQuery = this.announcementReadsRepository.createQueryBuilder('read')
.select('read.announcementId') .select('read.announcementId')
.where('read.userId = :userId', { userId: user.id }); .where('read.userId = :userId', { userId: user.id });
@ -58,7 +58,7 @@ export class AnnouncementService {
} }
@bindThis @bindThis
public async create(values: Partial<Announcement>): Promise<{ raw: Announcement; packed: Packed<'Announcement'> }> { public async create(values: Partial<MiAnnouncement>): Promise<{ raw: MiAnnouncement; packed: Packed<'Announcement'> }> {
const announcement = await this.announcementsRepository.insert({ const announcement = await this.announcementsRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
createdAt: new Date(), createdAt: new Date(),
@ -92,7 +92,7 @@ export class AnnouncementService {
} }
@bindThis @bindThis
public async read(user: User, announcementId: Announcement['id']): Promise<void> { public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> {
try { try {
await this.announcementReadsRepository.insert({ await this.announcementReadsRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
@ -111,10 +111,10 @@ export class AnnouncementService {
@bindThis @bindThis
public async packMany( public async packMany(
announcements: Announcement[], announcements: MiAnnouncement[],
me?: { id: User['id'] } | null | undefined, me?: { id: MiUser['id'] } | null | undefined,
options?: { options?: {
reads?: AnnouncementRead[]; reads?: MiAnnouncementRead[];
}, },
): Promise<Packed<'Announcement'>[]> { ): Promise<Packed<'Announcement'>[]> {
const reads = me ? (options?.reads ?? await this.getReads(me.id)) : []; const reads = me ? (options?.reads ?? await this.getReads(me.id)) : [];

View file

@ -5,9 +5,9 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import type { Antenna } from '@/models/entities/Antenna.js'; import type { MiAntenna } from '@/models/entities/Antenna.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import * as Acct from '@/misc/acct.js'; import * as Acct from '@/misc/acct.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
@ -21,7 +21,7 @@ import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
export class AntennaService implements OnApplicationShutdown { export class AntennaService implements OnApplicationShutdown {
private antennasFetched: boolean; private antennasFetched: boolean;
private antennas: Antenna[]; private antennas: MiAntenna[];
constructor( constructor(
@Inject(DI.redis) @Inject(DI.redis)
@ -76,7 +76,7 @@ export class AntennaService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async addNoteToAntennas(note: Note, noteUser: { id: User['id']; username: string; host: string | null; }): Promise<void> { public async addNoteToAntennas(note: MiNote, noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise<void> {
const antennas = await this.getAntennas(); const antennas = await this.getAntennas();
const antennasWithMatchResult = await Promise.all(antennas.map(antenna => this.checkHitAntenna(antenna, note, noteUser).then(hit => [antenna, hit] as const))); const antennasWithMatchResult = await Promise.all(antennas.map(antenna => this.checkHitAntenna(antenna, note, noteUser).then(hit => [antenna, hit] as const)));
const matchedAntennas = antennasWithMatchResult.filter(([, hit]) => hit).map(([antenna]) => antenna); const matchedAntennas = antennasWithMatchResult.filter(([, hit]) => hit).map(([antenna]) => antenna);
@ -99,7 +99,7 @@ export class AntennaService implements OnApplicationShutdown {
// NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている // NOTE: フォローしているユーザーのノート、リストのユーザーのノート、グループのユーザーのノート指定はパフォーマンス上の理由で無効になっている
@bindThis @bindThis
public async checkHitAntenna(antenna: Antenna, note: (Note | Packed<'Note'>), noteUser: { id: User['id']; username: string; host: string | null; }): Promise<boolean> { public async checkHitAntenna(antenna: MiAntenna, note: (MiNote | Packed<'Note'>), noteUser: { id: MiUser['id']; username: string; host: string | null; }): Promise<boolean> {
if (note.visibility === 'specified') return false; if (note.visibility === 'specified') return false;
if (note.visibility === 'followers') return false; if (note.visibility === 'followers') return false;

View file

@ -5,9 +5,9 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, UserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import type { BlockingsRepository, ChannelFollowingsRepository, FollowingsRepository, MutingsRepository, RenoteMutingsRepository, MiUserProfile, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js'; import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
import type { LocalUser, User } from '@/models/entities/User.js'; import type { MiLocalUser, MiUser } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@ -16,11 +16,11 @@ import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
export class CacheService implements OnApplicationShutdown { export class CacheService implements OnApplicationShutdown {
public userByIdCache: MemoryKVCache<User, User | string>; public userByIdCache: MemoryKVCache<MiUser, MiUser | string>;
public localUserByNativeTokenCache: MemoryKVCache<LocalUser | null, string | null>; public localUserByNativeTokenCache: MemoryKVCache<MiLocalUser | null, string | null>;
public localUserByIdCache: MemoryKVCache<LocalUser>; public localUserByIdCache: MemoryKVCache<MiLocalUser>;
public uriPersonCache: MemoryKVCache<User | null, string | null>; public uriPersonCache: MemoryKVCache<MiUser | null, string | null>;
public userProfileCache: RedisKVCache<UserProfile>; public userProfileCache: RedisKVCache<MiUserProfile>;
public userMutingsCache: RedisKVCache<Set<string>>; public userMutingsCache: RedisKVCache<Set<string>>;
public userBlockingCache: RedisKVCache<Set<string>>; public userBlockingCache: RedisKVCache<Set<string>>;
public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ public userBlockedCache: RedisKVCache<Set<string>>; // NOTE: 「被」Blockキャッシュ
@ -60,14 +60,14 @@ export class CacheService implements OnApplicationShutdown {
) { ) {
//this.onMessage = this.onMessage.bind(this); //this.onMessage = this.onMessage.bind(this);
const localUserByIdCache = new MemoryKVCache<LocalUser>(1000 * 60 * 60 * 6 /* 6h */); const localUserByIdCache = new MemoryKVCache<MiLocalUser>(1000 * 60 * 60 * 6 /* 6h */);
this.localUserByIdCache = localUserByIdCache; this.localUserByIdCache = localUserByIdCache;
// ローカルユーザーならlocalUserByIdCacheにデータを追加し、こちらにはid(文字列)だけを追加する // ローカルユーザーならlocalUserByIdCacheにデータを追加し、こちらにはid(文字列)だけを追加する
const userByIdCache = new MemoryKVCache<User, User | string>(1000 * 60 * 60 * 6 /* 6h */, { const userByIdCache = new MemoryKVCache<MiUser, MiUser | string>(1000 * 60 * 60 * 6 /* 6h */, {
toMapConverter: user => { toMapConverter: user => {
if (user.host === null) { if (user.host === null) {
localUserByIdCache.set(user.id, user as LocalUser); localUserByIdCache.set(user.id, user as MiLocalUser);
return user.id; return user.id;
} }
@ -77,7 +77,7 @@ export class CacheService implements OnApplicationShutdown {
}); });
this.userByIdCache = userByIdCache; this.userByIdCache = userByIdCache;
this.localUserByNativeTokenCache = new MemoryKVCache<LocalUser | null, string | null>(Infinity, { this.localUserByNativeTokenCache = new MemoryKVCache<MiLocalUser | null, string | null>(Infinity, {
toMapConverter: user => { toMapConverter: user => {
if (user === null) return null; if (user === null) return null;
@ -86,7 +86,7 @@ export class CacheService implements OnApplicationShutdown {
}, },
fromMapConverter: id => id === null ? null : localUserByIdCache.get(id), fromMapConverter: id => id === null ? null : localUserByIdCache.get(id),
}); });
this.uriPersonCache = new MemoryKVCache<User | null, string | null>(Infinity, { this.uriPersonCache = new MemoryKVCache<MiUser | null, string | null>(Infinity, {
toMapConverter: user => { toMapConverter: user => {
if (user === null) return null; if (user === null) return null;
@ -96,7 +96,7 @@ export class CacheService implements OnApplicationShutdown {
fromMapConverter: id => id === null ? null : userByIdCache.get(id), fromMapConverter: id => id === null ? null : userByIdCache.get(id),
}); });
this.userProfileCache = new RedisKVCache<UserProfile>(this.redisClient, 'userProfile', { this.userProfileCache = new RedisKVCache<MiUserProfile>(this.redisClient, 'userProfile', {
lifetime: 1000 * 60 * 30, // 30m lifetime: 1000 * 60 * 30, // 30m
memoryCacheLifetime: 1000 * 60, // 1m memoryCacheLifetime: 1000 * 60, // 1m
fetcher: (key) => this.userProfilesRepository.findOneByOrFail({ userId: key }), fetcher: (key) => this.userProfilesRepository.findOneByOrFail({ userId: key }),
@ -178,7 +178,7 @@ export class CacheService implements OnApplicationShutdown {
break; break;
} }
case 'userTokenRegenerated': { case 'userTokenRegenerated': {
const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as LocalUser; const user = await this.usersRepository.findOneByOrFail({ id: body.id }) as MiLocalUser;
this.localUserByNativeTokenCache.delete(body.oldToken); this.localUserByNativeTokenCache.delete(body.oldToken);
this.localUserByNativeTokenCache.set(body.newToken, user); this.localUserByNativeTokenCache.set(body.newToken, user);
break; break;
@ -197,7 +197,7 @@ export class CacheService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public findUserById(userId: User['id']) { public findUserById(userId: MiUser['id']) {
return this.userByIdCache.fetch(userId, () => this.usersRepository.findOneByOrFail({ id: userId })); return this.userByIdCache.fetch(userId, () => this.usersRepository.findOneByOrFail({ id: userId }));
} }

View file

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

View file

@ -10,9 +10,9 @@ import { DI } from '@/di-symbols.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { MiDriveFile } from '@/models/entities/DriveFile.js';
import type { Emoji } from '@/models/entities/Emoji.js'; import type { MiEmoji } from '@/models/entities/Emoji.js';
import type { EmojisRepository, Role } from '@/models/index.js'; import type { EmojisRepository, MiRole } from '@/models/index.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js'; import { MemoryKVCache, RedisSingleCache } from '@/misc/cache.js';
import { UtilityService } from '@/core/UtilityService.js'; import { UtilityService } from '@/core/UtilityService.js';
@ -23,8 +23,8 @@ const parseEmojiStrRegexp = /^(\w+)(?:@([\w.-]+))?$/;
@Injectable() @Injectable()
export class CustomEmojiService implements OnApplicationShutdown { export class CustomEmojiService implements OnApplicationShutdown {
private cache: MemoryKVCache<Emoji | null>; private cache: MemoryKVCache<MiEmoji | null>;
public localEmojisCache: RedisSingleCache<Map<string, Emoji>>; public localEmojisCache: RedisSingleCache<Map<string, MiEmoji>>;
constructor( constructor(
@Inject(DI.redis) @Inject(DI.redis)
@ -38,16 +38,16 @@ export class CustomEmojiService implements OnApplicationShutdown {
private emojiEntityService: EmojiEntityService, private emojiEntityService: EmojiEntityService,
private globalEventService: GlobalEventService, private globalEventService: GlobalEventService,
) { ) {
this.cache = new MemoryKVCache<Emoji | null>(1000 * 60 * 60 * 12); this.cache = new MemoryKVCache<MiEmoji | null>(1000 * 60 * 60 * 12);
this.localEmojisCache = new RedisSingleCache<Map<string, Emoji>>(this.redisClient, 'localEmojis', { this.localEmojisCache = new RedisSingleCache<Map<string, MiEmoji>>(this.redisClient, 'localEmojis', {
lifetime: 1000 * 60 * 30, // 30m lifetime: 1000 * 60 * 30, // 30m
memoryCacheLifetime: 1000 * 60 * 3, // 3m memoryCacheLifetime: 1000 * 60 * 3, // 3m
fetcher: () => this.emojisRepository.find({ where: { host: IsNull() } }).then(emojis => new Map(emojis.map(emoji => [emoji.name, emoji]))), fetcher: () => this.emojisRepository.find({ where: { host: IsNull() } }).then(emojis => new Map(emojis.map(emoji => [emoji.name, emoji]))),
toRedisConverter: (value) => JSON.stringify(Array.from(value.values())), toRedisConverter: (value) => JSON.stringify(Array.from(value.values())),
fromRedisConverter: (value) => { fromRedisConverter: (value) => {
if (!Array.isArray(JSON.parse(value))) return undefined; // 古いバージョンの壊れたキャッシュが残っていることがある(そのうち消す) if (!Array.isArray(JSON.parse(value))) return undefined; // 古いバージョンの壊れたキャッシュが残っていることがある(そのうち消す)
return new Map(JSON.parse(value).map((x: Serialized<Emoji>) => [x.name, { return new Map(JSON.parse(value).map((x: Serialized<MiEmoji>) => [x.name, {
...x, ...x,
updatedAt: x.updatedAt ? new Date(x.updatedAt) : null, updatedAt: x.updatedAt ? new Date(x.updatedAt) : null,
}])); }]));
@ -57,7 +57,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
@bindThis @bindThis
public async add(data: { public async add(data: {
driveFile: DriveFile; driveFile: MiDriveFile;
name: string; name: string;
category: string | null; category: string | null;
aliases: string[]; aliases: string[];
@ -65,8 +65,8 @@ export class CustomEmojiService implements OnApplicationShutdown {
license: string | null; license: string | null;
isSensitive: boolean; isSensitive: boolean;
localOnly: boolean; localOnly: boolean;
roleIdsThatCanBeUsedThisEmojiAsReaction: Role['id'][]; roleIdsThatCanBeUsedThisEmojiAsReaction: MiRole['id'][];
}): Promise<Emoji> { }): Promise<MiEmoji> {
const emoji = await this.emojisRepository.insert({ const emoji = await this.emojisRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
updatedAt: new Date(), updatedAt: new Date(),
@ -95,15 +95,15 @@ export class CustomEmojiService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async update(id: Emoji['id'], data: { public async update(id: MiEmoji['id'], data: {
driveFile?: DriveFile; driveFile?: MiDriveFile;
name?: string; name?: string;
category?: string | null; category?: string | null;
aliases?: string[]; aliases?: string[];
license?: string | null; license?: string | null;
isSensitive?: boolean; isSensitive?: boolean;
localOnly?: boolean; localOnly?: boolean;
roleIdsThatCanBeUsedThisEmojiAsReaction?: Role['id'][]; roleIdsThatCanBeUsedThisEmojiAsReaction?: MiRole['id'][];
}): Promise<void> { }): Promise<void> {
const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() }); const sameNameEmoji = await this.emojisRepository.findOneBy({ name: data.name, host: IsNull() });
@ -143,7 +143,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async addAliasesBulk(ids: Emoji['id'][], aliases: string[]) { public async addAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) {
const emojis = await this.emojisRepository.findBy({ const emojis = await this.emojisRepository.findBy({
id: In(ids), id: In(ids),
}); });
@ -163,7 +163,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async setAliasesBulk(ids: Emoji['id'][], aliases: string[]) { public async setAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) {
await this.emojisRepository.update({ await this.emojisRepository.update({
id: In(ids), id: In(ids),
}, { }, {
@ -179,7 +179,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async removeAliasesBulk(ids: Emoji['id'][], aliases: string[]) { public async removeAliasesBulk(ids: MiEmoji['id'][], aliases: string[]) {
const emojis = await this.emojisRepository.findBy({ const emojis = await this.emojisRepository.findBy({
id: In(ids), id: In(ids),
}); });
@ -199,7 +199,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async setCategoryBulk(ids: Emoji['id'][], category: string | null) { public async setCategoryBulk(ids: MiEmoji['id'][], category: string | null) {
await this.emojisRepository.update({ await this.emojisRepository.update({
id: In(ids), id: In(ids),
}, { }, {
@ -215,7 +215,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async setLicenseBulk(ids: Emoji['id'][], license: string | null) { public async setLicenseBulk(ids: MiEmoji['id'][], license: string | null) {
await this.emojisRepository.update({ await this.emojisRepository.update({
id: In(ids), id: In(ids),
}, { }, {
@ -231,7 +231,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async delete(id: Emoji['id']) { public async delete(id: MiEmoji['id']) {
const emoji = await this.emojisRepository.findOneByOrFail({ id: id }); const emoji = await this.emojisRepository.findOneByOrFail({ id: id });
await this.emojisRepository.delete(emoji.id); await this.emojisRepository.delete(emoji.id);
@ -244,7 +244,7 @@ export class CustomEmojiService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async deleteBulk(ids: Emoji['id'][]) { public async deleteBulk(ids: MiEmoji['id'][]) {
const emojis = await this.emojisRepository.findBy({ const emojis = await this.emojisRepository.findBy({
id: In(ids), id: In(ids),
}); });

View file

@ -14,9 +14,9 @@ import { DI } from '@/di-symbols.js';
import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js'; import type { DriveFilesRepository, UsersRepository, DriveFoldersRepository, UserProfilesRepository } from '@/models/index.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import Logger from '@/logger.js'; import Logger from '@/logger.js';
import type { RemoteUser, User } from '@/models/entities/User.js'; import type { MiRemoteUser, MiUser } from '@/models/entities/User.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { DriveFile } from '@/models/entities/DriveFile.js'; import { MiDriveFile } from '@/models/entities/DriveFile.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
import { FILE_TYPE_BROWSERSAFE } from '@/const.js'; import { FILE_TYPE_BROWSERSAFE } from '@/const.js';
@ -27,7 +27,7 @@ import { VideoProcessingService } from '@/core/VideoProcessingService.js';
import { ImageProcessingService } from '@/core/ImageProcessingService.js'; import { ImageProcessingService } from '@/core/ImageProcessingService.js';
import type { IImage } from '@/core/ImageProcessingService.js'; import type { IImage } from '@/core/ImageProcessingService.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
import type { DriveFolder } from '@/models/entities/DriveFolder.js'; import type { MiDriveFolder } from '@/models/entities/DriveFolder.js';
import { createTemp } from '@/misc/create-temp.js'; import { createTemp } from '@/misc/create-temp.js';
import DriveChart from '@/core/chart/charts/drive.js'; import DriveChart from '@/core/chart/charts/drive.js';
import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js'; import PerUserDriveChart from '@/core/chart/charts/per-user-drive.js';
@ -45,7 +45,7 @@ import { isMimeImage } from '@/misc/is-mime-image.js';
type AddFileArgs = { type AddFileArgs = {
/** User who wish to add file */ /** User who wish to add file */
user: { id: User['id']; host: User['host'] } | null; user: { id: MiUser['id']; host: MiUser['host'] } | null;
/** File path */ /** File path */
path: string; path: string;
/** Name */ /** Name */
@ -73,8 +73,8 @@ type AddFileArgs = {
type UploadFromUrlArgs = { type UploadFromUrlArgs = {
url: string; url: string;
user: { id: User['id']; host: User['host'] } | null; user: { id: MiUser['id']; host: MiUser['host'] } | null;
folderId?: DriveFolder['id'] | null; folderId?: MiDriveFolder['id'] | null;
uri?: string | null; uri?: string | null;
sensitive?: boolean; sensitive?: boolean;
force?: boolean; force?: boolean;
@ -138,7 +138,7 @@ export class DriveService {
* @param size Size for original * @param size Size for original
*/ */
@bindThis @bindThis
private async save(file: DriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<DriveFile> { private async save(file: MiDriveFile, path: string, name: string, type: string, hash: string, size: number): Promise<MiDriveFile> {
// thunbnail, webpublic を必要なら生成 // thunbnail, webpublic を必要なら生成
const alts = await this.generateAlts(path, type, !file.uri); const alts = await this.generateAlts(path, type, !file.uri);
@ -405,7 +405,7 @@ export class DriveService {
// Expire oldest file (without avatar or banner) of remote user // Expire oldest file (without avatar or banner) of remote user
@bindThis @bindThis
private async expireOldFile(user: RemoteUser, driveCapacity: number) { private async expireOldFile(user: MiRemoteUser, driveCapacity: number) {
const q = this.driveFilesRepository.createQueryBuilder('file') const q = this.driveFilesRepository.createQueryBuilder('file')
.where('file.userId = :userId', { userId: user.id }) .where('file.userId = :userId', { userId: user.id })
.andWhere('file.isLink = FALSE'); .andWhere('file.isLink = FALSE');
@ -451,7 +451,7 @@ export class DriveService {
requestIp = null, requestIp = null,
requestHeaders = null, requestHeaders = null,
ext = null, ext = null,
}: AddFileArgs): Promise<DriveFile> { }: AddFileArgs): Promise<MiDriveFile> {
let skipNsfwCheck = false; let skipNsfwCheck = false;
const instance = await this.metaService.fetch(); const instance = await this.metaService.fetch();
const userRoleNSFW = user && (await this.roleService.getUserPolicies(user.id)).alwaysMarkNsfw; const userRoleNSFW = user && (await this.roleService.getUserPolicies(user.id)).alwaysMarkNsfw;
@ -520,7 +520,7 @@ export class DriveService {
if (isLocalUser) { if (isLocalUser) {
throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.'); throw new IdentifiableError('c6244ed2-a39a-4e1c-bf93-f0fbd7764fa6', 'No free space.');
} }
await this.expireOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as RemoteUser, driveCapacity - info.size); await this.expireOldFile(await this.usersRepository.findOneByOrFail({ id: user.id }) as MiRemoteUser, driveCapacity - info.size);
} }
} }
//#endregion //#endregion
@ -558,7 +558,7 @@ export class DriveService {
const folder = await fetchFolder(); const folder = await fetchFolder();
let file = new DriveFile(); let file = new MiDriveFile();
file.id = this.idService.genId(); file.id = this.idService.genId();
file.createdAt = new Date(); file.createdAt = new Date();
file.userId = user ? user.id : null; file.userId = user ? user.id : null;
@ -614,7 +614,7 @@ export class DriveService {
file = await this.driveFilesRepository.findOneBy({ file = await this.driveFilesRepository.findOneBy({
uri: file.uri!, uri: file.uri!,
userId: user ? user.id : IsNull(), userId: user ? user.id : IsNull(),
}) as DriveFile; }) as MiDriveFile;
} else { } else {
this.registerLogger.error(err as Error); this.registerLogger.error(err as Error);
throw err; throw err;
@ -648,7 +648,7 @@ export class DriveService {
} }
@bindThis @bindThis
public async deleteFile(file: DriveFile, isExpired = false) { public async deleteFile(file: MiDriveFile, isExpired = false) {
if (file.storedInternal) { if (file.storedInternal) {
this.internalStorageService.del(file.accessKey!); this.internalStorageService.del(file.accessKey!);
@ -675,7 +675,7 @@ export class DriveService {
} }
@bindThis @bindThis
public async deleteFileSync(file: DriveFile, isExpired = false) { public async deleteFileSync(file: MiDriveFile, isExpired = false) {
if (file.storedInternal) { if (file.storedInternal) {
this.internalStorageService.del(file.accessKey!); this.internalStorageService.del(file.accessKey!);
@ -706,7 +706,7 @@ export class DriveService {
} }
@bindThis @bindThis
private async deletePostProcess(file: DriveFile, isExpired = false) { private async deletePostProcess(file: MiDriveFile, isExpired = false) {
// リモートファイル期限切れ削除後は直リンクにする // リモートファイル期限切れ削除後は直リンクにする
if (isExpired && file.userHost !== null && file.uri != null) { if (isExpired && file.userHost !== null && file.uri != null) {
this.driveFilesRepository.update(file.id, { this.driveFilesRepository.update(file.id, {
@ -769,7 +769,7 @@ export class DriveService {
comment = null, comment = null,
requestIp = null, requestIp = null,
requestHeaders = null, requestHeaders = null,
}: UploadFromUrlArgs): Promise<DriveFile> { }: UploadFromUrlArgs): Promise<MiDriveFile> {
// Create temp file // Create temp file
const [path, cleanup] = await createTemp(); const [path, cleanup] = await createTemp();

View file

@ -6,7 +6,7 @@
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import type { InstancesRepository } from '@/models/index.js'; import type { InstancesRepository } from '@/models/index.js';
import type { Instance } from '@/models/entities/Instance.js'; import type { MiInstance } from '@/models/entities/Instance.js';
import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js'; import { MemoryKVCache, RedisKVCache } from '@/misc/cache.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
@ -15,7 +15,7 @@ import { bindThis } from '@/decorators.js';
@Injectable() @Injectable()
export class FederatedInstanceService implements OnApplicationShutdown { export class FederatedInstanceService implements OnApplicationShutdown {
public federatedInstanceCache: RedisKVCache<Instance | null>; public federatedInstanceCache: RedisKVCache<MiInstance | null>;
constructor( constructor(
@Inject(DI.redis) @Inject(DI.redis)
@ -27,7 +27,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
private utilityService: UtilityService, private utilityService: UtilityService,
private idService: IdService, private idService: IdService,
) { ) {
this.federatedInstanceCache = new RedisKVCache<Instance | null>(this.redisClient, 'federatedInstance', { this.federatedInstanceCache = new RedisKVCache<MiInstance | null>(this.redisClient, 'federatedInstance', {
lifetime: 1000 * 60 * 30, // 30m lifetime: 1000 * 60 * 30, // 30m
memoryCacheLifetime: 1000 * 60 * 3, // 3m memoryCacheLifetime: 1000 * 60 * 3, // 3m
fetcher: (key) => this.instancesRepository.findOneBy({ host: key }), fetcher: (key) => this.instancesRepository.findOneBy({ host: key }),
@ -46,7 +46,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async fetch(host: string): Promise<Instance> { public async fetch(host: string): Promise<MiInstance> {
host = this.utilityService.toPuny(host); host = this.utilityService.toPuny(host);
const cached = await this.federatedInstanceCache.get(host); const cached = await this.federatedInstanceCache.get(host);
@ -70,7 +70,7 @@ export class FederatedInstanceService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async update(id: Instance['id'], data: Partial<Instance>): Promise<void> { public async update(id: MiInstance['id'], data: Partial<MiInstance>): Promise<void> {
const result = await this.instancesRepository.createQueryBuilder().update() const result = await this.instancesRepository.createQueryBuilder().update()
.set(data) .set(data)
.where('id = :id', { id }) .where('id = :id', { id })

View file

@ -8,7 +8,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { JSDOM } from 'jsdom'; import { JSDOM } from 'jsdom';
import tinycolor from 'tinycolor2'; import tinycolor from 'tinycolor2';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import type { Instance } from '@/models/entities/Instance.js'; import type { MiInstance } from '@/models/entities/Instance.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { LoggerService } from '@/core/LoggerService.js'; import { LoggerService } from '@/core/LoggerService.js';
@ -62,7 +62,7 @@ export class FetchInstanceMetadataService {
} }
@bindThis @bindThis
public async fetchInstanceMetadata(instance: Instance, force = false): Promise<void> { public async fetchInstanceMetadata(instance: MiInstance, force = false): Promise<void> {
const host = instance.host; const host = instance.host;
// Acquire mutex to ensure no parallel runs // Acquire mutex to ensure no parallel runs
if (!await this.tryLock(host)) return; if (!await this.tryLock(host)) return;
@ -123,7 +123,7 @@ export class FetchInstanceMetadataService {
} }
@bindThis @bindThis
private async fetchNodeinfo(instance: Instance): Promise<NodeInfo> { private async fetchNodeinfo(instance: MiInstance): Promise<NodeInfo> {
this.logger.info(`Fetching nodeinfo of ${instance.host} ...`); this.logger.info(`Fetching nodeinfo of ${instance.host} ...`);
try { try {
@ -142,10 +142,10 @@ export class FetchInstanceMetadataService {
const links = wellknown.links as any[]; const links = wellknown.links as any[];
const lnik1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0'); const link1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0');
const lnik2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0'); const link2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0');
const lnik2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1'); const link2_1 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.1');
const link = lnik2_1 ?? lnik2_0 ?? lnik1_0; const link = link2_1 ?? link2_0 ?? link1_0;
if (link == null) { if (link == null) {
throw new Error('No nodeinfo link provided'); throw new Error('No nodeinfo link provided');
@ -167,7 +167,7 @@ export class FetchInstanceMetadataService {
} }
@bindThis @bindThis
private async fetchDom(instance: Instance): Promise<DOMWindow['document']> { private async fetchDom(instance: MiInstance): Promise<DOMWindow['document']> {
this.logger.info(`Fetching HTML of ${instance.host} ...`); this.logger.info(`Fetching HTML of ${instance.host} ...`);
const url = 'https://' + instance.host; const url = 'https://' + instance.host;
@ -181,7 +181,7 @@ export class FetchInstanceMetadataService {
} }
@bindThis @bindThis
private async fetchManifest(instance: Instance): Promise<Record<string, unknown> | null> { private async fetchManifest(instance: MiInstance): Promise<Record<string, unknown> | null> {
const url = 'https://' + instance.host; const url = 'https://' + instance.host;
const manifestUrl = url + '/manifest.json'; const manifestUrl = url + '/manifest.json';
@ -192,7 +192,7 @@ export class FetchInstanceMetadataService {
} }
@bindThis @bindThis
private async fetchFaviconUrl(instance: Instance, doc: DOMWindow['document'] | null): Promise<string | null> { private async fetchFaviconUrl(instance: MiInstance, doc: DOMWindow['document'] | null): Promise<string | null> {
const url = 'https://' + instance.host; const url = 'https://' + instance.host;
if (doc) { if (doc) {
@ -218,7 +218,7 @@ export class FetchInstanceMetadataService {
} }
@bindThis @bindThis
private async fetchIconUrl(instance: Instance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> { private async fetchIconUrl(instance: MiInstance, doc: DOMWindow['document'] | null, manifest: Record<string, any> | null): Promise<string | null> {
if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) { if (manifest && manifest.icons && manifest.icons.length > 0 && manifest.icons[0].src) {
const url = 'https://' + instance.host; const url = 'https://' + instance.host;
return (new URL(manifest.icons[0].src, url)).href; return (new URL(manifest.icons[0].src, url)).href;

View file

@ -5,10 +5,10 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import type { UserList } from '@/models/entities/UserList.js'; import type { MiUserList } from '@/models/entities/UserList.js';
import type { Antenna } from '@/models/entities/Antenna.js'; import type { MiAntenna } from '@/models/entities/Antenna.js';
import type { import type {
StreamChannels, StreamChannels,
AdminStreamTypes, AdminStreamTypes,
@ -25,7 +25,7 @@ import type { Packed } from '@/misc/json-schema.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { Role } from '@/models/index.js'; import { MiRole } from '@/models/index.js';
@Injectable() @Injectable()
export class GlobalEventService { export class GlobalEventService {
@ -61,17 +61,17 @@ export class GlobalEventService {
} }
@bindThis @bindThis
public publishMainStream<K extends keyof MainStreamTypes>(userId: User['id'], type: K, value?: MainStreamTypes[K]): void { public publishMainStream<K extends keyof MainStreamTypes>(userId: MiUser['id'], type: K, value?: MainStreamTypes[K]): void {
this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value); this.publish(`mainStream:${userId}`, type, typeof value === 'undefined' ? null : value);
} }
@bindThis @bindThis
public publishDriveStream<K extends keyof DriveStreamTypes>(userId: User['id'], type: K, value?: DriveStreamTypes[K]): void { public publishDriveStream<K extends keyof DriveStreamTypes>(userId: MiUser['id'], type: K, value?: DriveStreamTypes[K]): void {
this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value); this.publish(`driveStream:${userId}`, type, typeof value === 'undefined' ? null : value);
} }
@bindThis @bindThis
public publishNoteStream<K extends keyof NoteStreamTypes>(noteId: Note['id'], type: K, value?: NoteStreamTypes[K]): void { public publishNoteStream<K extends keyof NoteStreamTypes>(noteId: MiNote['id'], type: K, value?: NoteStreamTypes[K]): void {
this.publish(`noteStream:${noteId}`, type, { this.publish(`noteStream:${noteId}`, type, {
id: noteId, id: noteId,
body: value, body: value,
@ -79,17 +79,17 @@ export class GlobalEventService {
} }
@bindThis @bindThis
public publishUserListStream<K extends keyof UserListStreamTypes>(listId: UserList['id'], type: K, value?: UserListStreamTypes[K]): void { public publishUserListStream<K extends keyof UserListStreamTypes>(listId: MiUserList['id'], type: K, value?: UserListStreamTypes[K]): void {
this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value); this.publish(`userListStream:${listId}`, type, typeof value === 'undefined' ? null : value);
} }
@bindThis @bindThis
public publishAntennaStream<K extends keyof AntennaStreamTypes>(antennaId: Antenna['id'], type: K, value?: AntennaStreamTypes[K]): void { public publishAntennaStream<K extends keyof AntennaStreamTypes>(antennaId: MiAntenna['id'], type: K, value?: AntennaStreamTypes[K]): void {
this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value); this.publish(`antennaStream:${antennaId}`, type, typeof value === 'undefined' ? null : value);
} }
@bindThis @bindThis
public publishRoleTimelineStream<K extends keyof RoleTimelineStreamTypes>(roleId: Role['id'], type: K, value?: RoleTimelineStreamTypes[K]): void { public publishRoleTimelineStream<K extends keyof RoleTimelineStreamTypes>(roleId: MiRole['id'], type: K, value?: RoleTimelineStreamTypes[K]): void {
this.publish(`roleTimelineStream:${roleId}`, type, typeof value === 'undefined' ? null : value); this.publish(`roleTimelineStream:${roleId}`, type, typeof value === 'undefined' ? null : value);
} }
@ -99,7 +99,7 @@ export class GlobalEventService {
} }
@bindThis @bindThis
public publishAdminStream<K extends keyof AdminStreamTypes>(userId: User['id'], type: K, value?: AdminStreamTypes[K]): void { public publishAdminStream<K extends keyof AdminStreamTypes>(userId: MiUser['id'], type: K, value?: AdminStreamTypes[K]): void {
this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value); this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value);
} }
} }

View file

@ -5,10 +5,10 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import type { Hashtag } from '@/models/entities/Hashtag.js'; import type { MiHashtag } from '@/models/entities/Hashtag.js';
import type { HashtagsRepository } from '@/models/index.js'; import type { HashtagsRepository } from '@/models/index.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@ -25,14 +25,14 @@ export class HashtagService {
} }
@bindThis @bindThis
public async updateHashtags(user: { id: User['id']; host: User['host']; }, tags: string[]) { public async updateHashtags(user: { id: MiUser['id']; host: MiUser['host']; }, tags: string[]) {
for (const tag of tags) { for (const tag of tags) {
await this.updateHashtag(user, tag); await this.updateHashtag(user, tag);
} }
} }
@bindThis @bindThis
public async updateUsertags(user: User, tags: string[]) { public async updateUsertags(user: MiUser, tags: string[]) {
for (const tag of tags) { for (const tag of tags) {
await this.updateHashtag(user, tag, true, true); await this.updateHashtag(user, tag, true, true);
} }
@ -43,7 +43,7 @@ export class HashtagService {
} }
@bindThis @bindThis
public async updateHashtag(user: { id: User['id']; host: User['host']; }, tag: string, isUserAttached = false, inc = true) { public async updateHashtag(user: { id: MiUser['id']; host: MiUser['host']; }, tag: string, isUserAttached = false, inc = true) {
tag = normalizeForSearch(tag); tag = normalizeForSearch(tag);
const index = await this.hashtagsRepository.findOneBy({ name: tag }); const index = await this.hashtagsRepository.findOneBy({ name: tag });
@ -123,7 +123,7 @@ export class HashtagService {
attachedLocalUsersCount: this.userEntityService.isLocalUser(user) ? 1 : 0, attachedLocalUsersCount: this.userEntityService.isLocalUser(user) ? 1 : 0,
attachedRemoteUserIds: this.userEntityService.isRemoteUser(user) ? [user.id] : [], attachedRemoteUserIds: this.userEntityService.isRemoteUser(user) ? [user.id] : [],
attachedRemoteUsersCount: this.userEntityService.isRemoteUser(user) ? 1 : 0, attachedRemoteUsersCount: this.userEntityService.isRemoteUser(user) ? 1 : 0,
} as Hashtag); } as MiHashtag);
} else { } else {
this.hashtagsRepository.insert({ this.hashtagsRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
@ -140,7 +140,7 @@ export class HashtagService {
attachedLocalUsersCount: 0, attachedLocalUsersCount: 0,
attachedRemoteUserIds: [], attachedRemoteUserIds: [],
attachedRemoteUsersCount: 0, attachedRemoteUsersCount: 0,
} as Hashtag); } as MiHashtag);
} }
} }
} }

View file

@ -53,12 +53,14 @@ export class HttpRequestService {
keepAlive: true, keepAlive: true,
keepAliveMsecs: 30 * 1000, keepAliveMsecs: 30 * 1000,
lookup: cache.lookup as unknown as net.LookupFunction, lookup: cache.lookup as unknown as net.LookupFunction,
localAddress: config.outgoingAddress,
}); });
this.https = new https.Agent({ this.https = new https.Agent({
keepAlive: true, keepAlive: true,
keepAliveMsecs: 30 * 1000, keepAliveMsecs: 30 * 1000,
lookup: cache.lookup as unknown as net.LookupFunction, lookup: cache.lookup as unknown as net.LookupFunction,
localAddress: config.outgoingAddress,
}); });
const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128); const maxSockets = Math.max(256, config.deliverJobConcurrency ?? 128);
@ -71,6 +73,7 @@ export class HttpRequestService {
maxFreeSockets: 256, maxFreeSockets: 256,
scheduling: 'lifo', scheduling: 'lifo',
proxy: config.proxy, proxy: config.proxy,
localAddress: config.outgoingAddress,
}) })
: this.http; : this.http;
@ -82,6 +85,7 @@ export class HttpRequestService {
maxFreeSockets: 256, maxFreeSockets: 256,
scheduling: 'lifo', scheduling: 'lifo',
proxy: config.proxy, proxy: config.proxy,
localAddress: config.outgoingAddress,
}) })
: this.https; : this.https;
} }

View file

@ -5,7 +5,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { IsNull } from 'typeorm'; import { IsNull } from 'typeorm';
import type { LocalUser } from '@/models/entities/User.js'; import type { MiLocalUser } from '@/models/entities/User.js';
import type { UsersRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js';
import { MemorySingleCache } from '@/misc/cache.js'; import { MemorySingleCache } from '@/misc/cache.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
@ -16,7 +16,7 @@ const ACTOR_USERNAME = 'instance.actor' as const;
@Injectable() @Injectable()
export class InstanceActorService { export class InstanceActorService {
private cache: MemorySingleCache<LocalUser>; private cache: MemorySingleCache<MiLocalUser>;
constructor( constructor(
@Inject(DI.usersRepository) @Inject(DI.usersRepository)
@ -24,24 +24,24 @@ export class InstanceActorService {
private createSystemUserService: CreateSystemUserService, private createSystemUserService: CreateSystemUserService,
) { ) {
this.cache = new MemorySingleCache<LocalUser>(Infinity); this.cache = new MemorySingleCache<MiLocalUser>(Infinity);
} }
@bindThis @bindThis
public async getInstanceActor(): Promise<LocalUser> { public async getInstanceActor(): Promise<MiLocalUser> {
const cached = this.cache.get(); const cached = this.cache.get();
if (cached) return cached; if (cached) return cached;
const user = await this.usersRepository.findOneBy({ const user = await this.usersRepository.findOneBy({
host: IsNull(), host: IsNull(),
username: ACTOR_USERNAME, username: ACTOR_USERNAME,
}) as LocalUser | undefined; }) as MiLocalUser | undefined;
if (user) { if (user) {
this.cache.set(user); this.cache.set(user);
return user; return user;
} else { } else {
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as LocalUser; const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME) as MiLocalUser;
this.cache.set(created); this.cache.set(created);
return created; return created;
} }

View file

@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { Meta } from '@/models/entities/Meta.js'; import { MiMeta } from '@/models/entities/Meta.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { StreamMessages } from '@/server/api/stream/types.js'; import { StreamMessages } from '@/server/api/stream/types.js';
@ -15,7 +15,7 @@ import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
export class MetaService implements OnApplicationShutdown { export class MetaService implements OnApplicationShutdown {
private cache: Meta | undefined; private cache: MiMeta | undefined;
private intervalId: NodeJS.Timer; private intervalId: NodeJS.Timer;
constructor( constructor(
@ -59,12 +59,12 @@ export class MetaService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async fetch(noCache = false): Promise<Meta> { public async fetch(noCache = false): Promise<MiMeta> {
if (!noCache && this.cache) return this.cache; if (!noCache && this.cache) return this.cache;
return await this.db.transaction(async transactionalEntityManager => { return await this.db.transaction(async transactionalEntityManager => {
// 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する // 過去のバグでレコードが複数出来てしまっている可能性があるので新しいIDを優先する
const metas = await transactionalEntityManager.find(Meta, { const metas = await transactionalEntityManager.find(MiMeta, {
order: { order: {
id: 'DESC', id: 'DESC',
}, },
@ -79,13 +79,13 @@ export class MetaService implements OnApplicationShutdown {
// metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う // metaが空のときfetchMetaが同時に呼ばれるとここが同時に呼ばれてしまうことがあるのでフェイルセーフなupsertを使う
const saved = await transactionalEntityManager const saved = await transactionalEntityManager
.upsert( .upsert(
Meta, MiMeta,
{ {
id: 'x', id: 'x',
}, },
['id'], ['id'],
) )
.then((x) => transactionalEntityManager.findOneByOrFail(Meta, x.identifiers[0])); .then((x) => transactionalEntityManager.findOneByOrFail(MiMeta, x.identifiers[0]));
this.cache = saved; this.cache = saved;
return saved; return saved;
@ -94,9 +94,9 @@ export class MetaService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async update(data: Partial<Meta>): Promise<Meta> { public async update(data: Partial<MiMeta>): Promise<MiMeta> {
const updated = await this.db.transaction(async transactionalEntityManager => { const updated = await this.db.transaction(async transactionalEntityManager => {
const metas = await transactionalEntityManager.find(Meta, { const metas = await transactionalEntityManager.find(MiMeta, {
order: { order: {
id: 'DESC', id: 'DESC',
}, },
@ -105,9 +105,9 @@ export class MetaService implements OnApplicationShutdown {
const meta = metas[0]; const meta = metas[0];
if (meta) { if (meta) {
await transactionalEntityManager.update(Meta, meta.id, data); await transactionalEntityManager.update(MiMeta, meta.id, data);
const metas = await transactionalEntityManager.find(Meta, { const metas = await transactionalEntityManager.find(MiMeta, {
order: { order: {
id: 'DESC', id: 'DESC',
}, },
@ -115,7 +115,7 @@ export class MetaService implements OnApplicationShutdown {
return metas[0]; return metas[0];
} else { } else {
return await transactionalEntityManager.save(Meta, data); return await transactionalEntityManager.save(MiMeta, data);
} }
}); });

View file

@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { ModerationLogsRepository } from '@/models/index.js'; import type { ModerationLogsRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@ -21,7 +21,7 @@ export class ModerationLogService {
} }
@bindThis @bindThis
public async insertModerationLog(moderator: { id: User['id'] }, type: string, info?: Record<string, any>) { public async insertModerationLog(moderator: { id: MiUser['id'] }, type: string, info?: Record<string, any>) {
await this.moderationLogsRepository.insert({ await this.moderationLogsRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
createdAt: new Date(), createdAt: new Date(),

View file

@ -13,21 +13,21 @@ import { extractMentions } from '@/misc/extract-mentions.js';
import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js'; import { extractCustomEmojisFromMfm } from '@/misc/extract-custom-emojis-from-mfm.js';
import { extractHashtags } from '@/misc/extract-hashtags.js'; import { extractHashtags } from '@/misc/extract-hashtags.js';
import type { IMentionedRemoteUsers } from '@/models/entities/Note.js'; import type { IMentionedRemoteUsers } from '@/models/entities/Note.js';
import { Note } from '@/models/entities/Note.js'; import { MiNote } from '@/models/entities/Note.js';
import type { ChannelsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js'; import type { ChannelsRepository, InstancesRepository, MutedNotesRepository, MutingsRepository, NotesRepository, NoteThreadMutingsRepository, UserProfilesRepository, UsersRepository } from '@/models/index.js';
import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { MiDriveFile } from '@/models/entities/DriveFile.js';
import type { App } from '@/models/entities/App.js'; import type { MiApp } from '@/models/entities/App.js';
import { concat } from '@/misc/prelude/array.js'; import { concat } from '@/misc/prelude/array.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import type { User, LocalUser, RemoteUser } from '@/models/entities/User.js'; import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
import type { IPoll } from '@/models/entities/Poll.js'; import type { IPoll } from '@/models/entities/Poll.js';
import { Poll } from '@/models/entities/Poll.js'; import { MiPoll } from '@/models/entities/Poll.js';
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
import { checkWordMute } from '@/misc/check-word-mute.js'; import { checkWordMute } from '@/misc/check-word-mute.js';
import type { Channel } from '@/models/entities/Channel.js'; import type { MiChannel } from '@/models/entities/Channel.js';
import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import { MemorySingleCache } from '@/misc/cache.js'; import { MemorySingleCache } from '@/misc/cache.js';
import type { UserProfile } from '@/models/entities/UserProfile.js'; import type { MiUserProfile } from '@/models/entities/UserProfile.js';
import { RelayService } from '@/core/RelayService.js'; import { RelayService } from '@/core/RelayService.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
@ -54,23 +54,23 @@ import { RoleService } from '@/core/RoleService.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { SearchService } from '@/core/SearchService.js'; import { SearchService } from '@/core/SearchService.js';
const mutedWordsCache = new MemorySingleCache<{ userId: UserProfile['userId']; mutedWords: UserProfile['mutedWords']; }[]>(1000 * 60 * 5); const mutedWordsCache = new MemorySingleCache<{ userId: MiUserProfile['userId']; mutedWords: MiUserProfile['mutedWords']; }[]>(1000 * 60 * 5);
type NotificationType = 'reply' | 'renote' | 'quote' | 'mention'; type NotificationType = 'reply' | 'renote' | 'quote' | 'mention';
class NotificationManager { class NotificationManager {
private notifier: { id: User['id']; }; private notifier: { id: MiUser['id']; };
private note: Note; private note: MiNote;
private queue: { private queue: {
target: LocalUser['id']; target: MiLocalUser['id'];
reason: NotificationType; reason: NotificationType;
}[]; }[];
constructor( constructor(
private mutingsRepository: MutingsRepository, private mutingsRepository: MutingsRepository,
private notificationService: NotificationService, private notificationService: NotificationService,
notifier: { id: User['id']; }, notifier: { id: MiUser['id']; },
note: Note, note: MiNote,
) { ) {
this.notifier = notifier; this.notifier = notifier;
this.note = note; this.note = note;
@ -78,7 +78,7 @@ class NotificationManager {
} }
@bindThis @bindThis
public push(notifiee: LocalUser['id'], reason: NotificationType) { public push(notifiee: MiLocalUser['id'], reason: NotificationType) {
// 自分自身へは通知しない // 自分自身へは通知しない
if (this.notifier.id === notifiee) return; if (this.notifier.id === notifiee) return;
@ -119,32 +119,32 @@ class NotificationManager {
} }
type MinimumUser = { type MinimumUser = {
id: User['id']; id: MiUser['id'];
host: User['host']; host: MiUser['host'];
username: User['username']; username: MiUser['username'];
uri: User['uri']; uri: MiUser['uri'];
}; };
type Option = { type Option = {
createdAt?: Date | null; createdAt?: Date | null;
name?: string | null; name?: string | null;
text?: string | null; text?: string | null;
reply?: Note | null; reply?: MiNote | null;
renote?: Note | null; renote?: MiNote | null;
files?: DriveFile[] | null; files?: MiDriveFile[] | null;
poll?: IPoll | null; poll?: IPoll | null;
localOnly?: boolean | null; localOnly?: boolean | null;
reactionAcceptance?: Note['reactionAcceptance']; reactionAcceptance?: MiNote['reactionAcceptance'];
cw?: string | null; cw?: string | null;
visibility?: string; visibility?: string;
visibleUsers?: MinimumUser[] | null; visibleUsers?: MinimumUser[] | null;
channel?: Channel | null; channel?: MiChannel | null;
apMentions?: MinimumUser[] | null; apMentions?: MinimumUser[] | null;
apHashtags?: string[] | null; apHashtags?: string[] | null;
apEmojis?: string[] | null; apEmojis?: string[] | null;
uri?: string | null; uri?: string | null;
url?: string | null; url?: string | null;
app?: App | null; app?: MiApp | null;
}; };
@Injectable() @Injectable()
@ -211,12 +211,12 @@ export class NoteCreateService implements OnApplicationShutdown {
@bindThis @bindThis
public async create(user: { public async create(user: {
id: User['id']; id: MiUser['id'];
username: User['username']; username: MiUser['username'];
host: User['host']; host: MiUser['host'];
createdAt: User['createdAt']; createdAt: MiUser['createdAt'];
isBot: User['isBot']; isBot: MiUser['isBot'];
}, data: Option, silent = false): Promise<Note> { }, data: Option, silent = false): Promise<MiNote> {
// チャンネル外にリプライしたら対象のスコープに合わせる // チャンネル外にリプライしたら対象のスコープに合わせる
// (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで) // (クライアントサイドでやっても良い処理だと思うけどとりあえずサーバーサイドで)
if (data.reply && data.channel && data.reply.channelId !== data.channel.id) { if (data.reply && data.channel && data.reply.channelId !== data.channel.id) {
@ -348,8 +348,8 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private async insertNote(user: { id: User['id']; host: User['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) { private async insertNote(user: { id: MiUser['id']; host: MiUser['host']; }, data: Option, tags: string[], emojis: string[], mentionedUsers: MinimumUser[]) {
const insert = new Note({ const insert = new MiNote({
id: this.idService.genId(data.createdAt!), id: this.idService.genId(data.createdAt!),
createdAt: data.createdAt!, createdAt: data.createdAt!,
fileIds: data.files ? data.files.map(file => file.id) : [], fileIds: data.files ? data.files.map(file => file.id) : [],
@ -411,9 +411,9 @@ export class NoteCreateService implements OnApplicationShutdown {
if (insert.hasPoll) { if (insert.hasPoll) {
// Start transaction // Start transaction
await this.db.transaction(async transactionalEntityManager => { await this.db.transaction(async transactionalEntityManager => {
await transactionalEntityManager.insert(Note, insert); await transactionalEntityManager.insert(MiNote, insert);
const poll = new Poll({ const poll = new MiPoll({
noteId: insert.id, noteId: insert.id,
choices: data.poll!.choices, choices: data.poll!.choices,
expiresAt: data.poll!.expiresAt, expiresAt: data.poll!.expiresAt,
@ -424,7 +424,7 @@ export class NoteCreateService implements OnApplicationShutdown {
userHost: user.host, userHost: user.host,
}); });
await transactionalEntityManager.insert(Poll, poll); await transactionalEntityManager.insert(MiPoll, poll);
}); });
} else { } else {
await this.notesRepository.insert(insert); await this.notesRepository.insert(insert);
@ -446,12 +446,12 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private async postNoteCreated(note: Note, user: { private async postNoteCreated(note: MiNote, user: {
id: User['id']; id: MiUser['id'];
username: User['username']; username: MiUser['username'];
host: User['host']; host: MiUser['host'];
createdAt: User['createdAt']; createdAt: MiUser['createdAt'];
isBot: User['isBot']; isBot: MiUser['isBot'];
}, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) { }, data: Option, silent: boolean, tags: string[], mentionedUsers: MinimumUser[]) {
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
@ -625,7 +625,7 @@ export class NoteCreateService implements OnApplicationShutdown {
// メンションされたリモートユーザーに配送 // メンションされたリモートユーザーに配送
for (const u of mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u))) { for (const u of mentionedUsers.filter(u => this.userEntityService.isRemoteUser(u))) {
dm.addDirectRecipe(u as RemoteUser); dm.addDirectRecipe(u as MiRemoteUser);
} }
// 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送 // 投稿がリプライかつ投稿者がローカルユーザーかつリプライ先の投稿の投稿者がリモートユーザーなら配送
@ -703,7 +703,7 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private incRenoteCount(renote: Note) { private incRenoteCount(renote: MiNote) {
this.notesRepository.createQueryBuilder().update() this.notesRepository.createQueryBuilder().update()
.set({ .set({
renoteCount: () => '"renoteCount" + 1', renoteCount: () => '"renoteCount" + 1',
@ -714,7 +714,7 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private async createMentionedEvents(mentionedUsers: MinimumUser[], note: Note, nm: NotificationManager) { private async createMentionedEvents(mentionedUsers: MinimumUser[], note: MiNote, nm: NotificationManager) {
for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) { for (const u of mentionedUsers.filter(u => this.userEntityService.isLocalUser(u))) {
const isThreadMuted = await this.noteThreadMutingsRepository.exist({ const isThreadMuted = await this.noteThreadMutingsRepository.exist({
where: { where: {
@ -746,12 +746,12 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private saveReply(reply: Note, note: Note) { private saveReply(reply: MiNote, note: MiNote) {
this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1); this.notesRepository.increment({ id: reply.id }, 'repliesCount', 1);
} }
@bindThis @bindThis
private async renderNoteOrRenoteActivity(data: Option, note: Note) { private async renderNoteOrRenoteActivity(data: Option, note: MiNote) {
if (data.localOnly) return null; if (data.localOnly) return null;
const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0) const content = data.renote && data.text == null && data.poll == null && (data.files == null || data.files.length === 0)
@ -762,14 +762,14 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private index(note: Note) { private index(note: MiNote) {
if (note.text == null && note.cw == null) return; if (note.text == null && note.cw == null) return;
this.searchService.indexNote(note); this.searchService.indexNote(note);
} }
@bindThis @bindThis
private incNotesCountOfUser(user: { id: User['id']; }) { private incNotesCountOfUser(user: { id: MiUser['id']; }) {
this.usersRepository.createQueryBuilder().update() this.usersRepository.createQueryBuilder().update()
.set({ .set({
updatedAt: new Date(), updatedAt: new Date(),
@ -780,13 +780,13 @@ export class NoteCreateService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private async extractMentionedUsers(user: { host: User['host']; }, tokens: mfm.MfmNode[]): Promise<User[]> { private async extractMentionedUsers(user: { host: MiUser['host']; }, tokens: mfm.MfmNode[]): Promise<MiUser[]> {
if (tokens == null) return []; if (tokens == null) return [];
const mentions = extractMentions(tokens); const mentions = extractMentions(tokens);
let mentionedUsers = (await Promise.all(mentions.map(m => let mentionedUsers = (await Promise.all(mentions.map(m =>
this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null), this.remoteUserResolveService.resolveUser(m.username, m.host ?? user.host).catch(() => null),
))).filter(x => x != null) as User[]; ))).filter(x => x != null) as MiUser[];
// Drop duplicate users // Drop duplicate users
mentionedUsers = mentionedUsers.filter((u, i, self) => mentionedUsers = mentionedUsers.filter((u, i, self) =>

View file

@ -5,8 +5,8 @@
import { Brackets, In } from 'typeorm'; import { Brackets, In } from 'typeorm';
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import type { User, LocalUser, RemoteUser } from '@/models/entities/User.js'; import type { MiUser, MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
import type { Note, IMentionedRemoteUsers } from '@/models/entities/Note.js'; import type { MiNote, IMentionedRemoteUsers } from '@/models/entities/Note.js';
import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js'; import type { InstancesRepository, NotesRepository, UsersRepository } from '@/models/index.js';
import { RelayService } from '@/core/RelayService.js'; import { RelayService } from '@/core/RelayService.js';
import { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
@ -58,7 +58,7 @@ export class NoteDeleteService {
* @param user 稿 * @param user 稿
* @param note 稿 * @param note 稿
*/ */
async delete(user: { id: User['id']; uri: User['uri']; host: User['host']; isBot: User['isBot']; }, note: Note, quiet = false) { async delete(user: { id: MiUser['id']; uri: MiUser['uri']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote, quiet = false) {
const deletedAt = new Date(); const deletedAt = new Date();
const cascadingNotes = await this.findCascadingNotes(note); const cascadingNotes = await this.findCascadingNotes(note);
@ -79,7 +79,7 @@ export class NoteDeleteService {
//#region ローカルの投稿なら削除アクティビティを配送 //#region ローカルの投稿なら削除アクティビティを配送
if (this.userEntityService.isLocalUser(user) && !note.localOnly) { if (this.userEntityService.isLocalUser(user) && !note.localOnly) {
let renote: Note | null = null; let renote: MiNote | null = null;
// if deletd note is renote // if deletd note is renote
if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) { if (note.renoteId && note.text == null && !note.hasPoll && (note.fileIds == null || note.fileIds.length === 0)) {
@ -134,8 +134,8 @@ export class NoteDeleteService {
} }
@bindThis @bindThis
private async findCascadingNotes(note: Note): Promise<Note[]> { private async findCascadingNotes(note: MiNote): Promise<MiNote[]> {
const recursive = async (noteId: string): Promise<Note[]> => { const recursive = async (noteId: string): Promise<MiNote[]> => {
const query = this.notesRepository.createQueryBuilder('note') const query = this.notesRepository.createQueryBuilder('note')
.where('note.replyId = :noteId', { noteId }) .where('note.replyId = :noteId', { noteId })
.orWhere(new Brackets(q => { .orWhere(new Brackets(q => {
@ -151,13 +151,13 @@ export class NoteDeleteService {
].flat(); ].flat();
}; };
const cascadingNotes: Note[] = await recursive(note.id); const cascadingNotes: MiNote[] = await recursive(note.id);
return cascadingNotes; return cascadingNotes;
} }
@bindThis @bindThis
private async getMentionedRemoteUsers(note: Note) { private async getMentionedRemoteUsers(note: MiNote) {
const where = [] as any[]; const where = [] as any[];
// mention / reply / dm // mention / reply / dm
@ -179,11 +179,11 @@ export class NoteDeleteService {
return await this.usersRepository.find({ return await this.usersRepository.find({
where, where,
}) as RemoteUser[]; }) as MiRemoteUser[];
} }
@bindThis @bindThis
private async deliverToConcerned(user: { id: LocalUser['id']; host: null; }, note: Note, content: any) { private async deliverToConcerned(user: { id: MiLocalUser['id']; host: null; }, note: MiNote, content: any) {
this.apDeliverManagerService.deliverToFollowers(user, content); this.apDeliverManagerService.deliverToFollowers(user, content);
this.relayService.deliverToRelays(user, content); this.relayService.deliverToRelays(user, content);
const remoteUsers = await this.getMentionedRemoteUsers(note); const remoteUsers = await this.getMentionedRemoteUsers(note);

View file

@ -7,10 +7,10 @@ import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { NotesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js'; import type { NotesRepository, UserNotePiningsRepository, UsersRepository } from '@/models/index.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import type { UserNotePining } from '@/models/entities/UserNotePining.js'; import type { MiUserNotePining } from '@/models/entities/UserNotePining.js';
import { RelayService } from '@/core/RelayService.js'; import { RelayService } from '@/core/RelayService.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
@ -49,7 +49,7 @@ export class NotePiningService {
* @param noteId * @param noteId
*/ */
@bindThis @bindThis
public async addPinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { public async addPinned(user: { id: MiUser['id']; host: MiUser['host']; }, noteId: MiNote['id']) {
// Fetch pinee // Fetch pinee
const note = await this.notesRepository.findOneBy({ const note = await this.notesRepository.findOneBy({
id: noteId, id: noteId,
@ -75,7 +75,7 @@ export class NotePiningService {
createdAt: new Date(), createdAt: new Date(),
userId: user.id, userId: user.id,
noteId: note.id, noteId: note.id,
} as UserNotePining); } as MiUserNotePining);
// Deliver to remote followers // Deliver to remote followers
if (this.userEntityService.isLocalUser(user)) { if (this.userEntityService.isLocalUser(user)) {
@ -89,7 +89,7 @@ export class NotePiningService {
* @param noteId * @param noteId
*/ */
@bindThis @bindThis
public async removePinned(user: { id: User['id']; host: User['host']; }, noteId: Note['id']) { public async removePinned(user: { id: MiUser['id']; host: MiUser['host']; }, noteId: MiNote['id']) {
// Fetch unpinee // Fetch unpinee
const note = await this.notesRepository.findOneBy({ const note = await this.notesRepository.findOneBy({
id: noteId, id: noteId,
@ -112,7 +112,7 @@ export class NotePiningService {
} }
@bindThis @bindThis
public async deliverPinnedChange(userId: User['id'], noteId: Note['id'], isAddition: boolean) { public async deliverPinnedChange(userId: MiUser['id'], noteId: MiNote['id'], isAddition: boolean) {
const user = await this.usersRepository.findOneBy({ id: userId }); const user = await this.usersRepository.findOneBy({ id: userId });
if (user == null) throw new Error('user not found'); if (user == null) throw new Error('user not found');

View file

@ -7,9 +7,9 @@ import { setTimeout } from 'node:timers/promises';
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { In } from 'typeorm'; import { In } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository } from '@/models/index.js'; import type { NoteUnreadsRepository, MutingsRepository, NoteThreadMutingsRepository } from '@/models/index.js';
@ -35,7 +35,7 @@ export class NoteReadService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async insertNoteUnread(userId: User['id'], note: Note, params: { public async insertNoteUnread(userId: MiUser['id'], note: MiNote, params: {
// NOTE: isSpecifiedがtrueならisMentionedは必ずfalse // NOTE: isSpecifiedがtrueならisMentionedは必ずfalse
isSpecified: boolean; isSpecified: boolean;
isMentioned: boolean; isMentioned: boolean;
@ -84,11 +84,11 @@ export class NoteReadService implements OnApplicationShutdown {
@bindThis @bindThis
public async read( public async read(
userId: User['id'], userId: MiUser['id'],
notes: (Note | Packed<'Note'>)[], notes: (MiNote | Packed<'Note'>)[],
): Promise<void> { ): Promise<void> {
const readMentions: (Note | Packed<'Note'>)[] = []; const readMentions: (MiNote | Packed<'Note'>)[] = [];
const readSpecifiedNotes: (Note | Packed<'Note'>)[] = []; const readSpecifiedNotes: (MiNote | Packed<'Note'>)[] = [];
for (const note of notes) { for (const note of notes) {
if (note.mentions && note.mentions.includes(userId)) { if (note.mentions && note.mentions.includes(userId)) {

View file

@ -9,8 +9,8 @@ import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import { In } from 'typeorm'; import { In } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { UsersRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import type { Notification } from '@/models/entities/Notification.js'; import type { MiNotification } from '@/models/entities/Notification.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import { PushNotificationService } from '@/core/PushNotificationService.js'; import { PushNotificationService } from '@/core/PushNotificationService.js';
@ -39,7 +39,7 @@ export class NotificationService implements OnApplicationShutdown {
@bindThis @bindThis
public async readAllNotification( public async readAllNotification(
userId: User['id'], userId: MiUser['id'],
force = false, force = false,
) { ) {
const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${userId}`); const latestReadNotificationId = await this.redisClient.get(`latestReadNotification:${userId}`);
@ -61,17 +61,17 @@ export class NotificationService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private postReadAllNotifications(userId: User['id']) { private postReadAllNotifications(userId: MiUser['id']) {
this.globalEventService.publishMainStream(userId, 'readAllNotifications'); this.globalEventService.publishMainStream(userId, 'readAllNotifications');
this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined); this.pushNotificationService.pushNotification(userId, 'readAllNotifications', undefined);
} }
@bindThis @bindThis
public async createNotification( public async createNotification(
notifieeId: User['id'], notifieeId: MiUser['id'],
type: Notification['type'], type: MiNotification['type'],
data: Partial<Notification>, data: Partial<MiNotification>,
): Promise<Notification | null> { ): Promise<MiNotification | null> {
const profile = await this.cacheService.userProfileCache.fetch(notifieeId); const profile = await this.cacheService.userProfileCache.fetch(notifieeId);
const isMuted = profile.mutingNotificationTypes.includes(type); const isMuted = profile.mutingNotificationTypes.includes(type);
if (isMuted) return null; if (isMuted) return null;
@ -92,7 +92,7 @@ export class NotificationService implements OnApplicationShutdown {
createdAt: new Date(), createdAt: new Date(),
type: type, type: type,
...data, ...data,
} as Notification; } as MiNotification;
const redisIdPromise = this.redisClient.xadd( const redisIdPromise = this.redisClient.xadd(
`notificationTimeline:${notifieeId}`, `notificationTimeline:${notifieeId}`,
@ -126,7 +126,7 @@ export class NotificationService implements OnApplicationShutdown {
// TODO: locale ファイルをクライアント用とサーバー用で分けたい // TODO: locale ファイルをクライアント用とサーバー用で分けたい
@bindThis @bindThis
private async emailNotificationFollow(userId: User['id'], follower: User) { private async emailNotificationFollow(userId: MiUser['id'], follower: MiUser) {
/* /*
const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return; if (!userProfile.email || !userProfile.emailNotificationTypes.includes('follow')) return;
@ -138,7 +138,7 @@ export class NotificationService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private async emailNotificationReceiveFollowRequest(userId: User['id'], follower: User) { private async emailNotificationReceiveFollowRequest(userId: MiUser['id'], follower: MiUser) {
/* /*
const userProfile = await UserProfiles.findOneByOrFail({ userId: userId }); const userProfile = await UserProfiles.findOneByOrFail({ userId: userId });
if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return; if (!userProfile.email || !userProfile.emailNotificationTypes.includes('receiveFollowRequest')) return;

View file

@ -5,8 +5,8 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository, User } from '@/models/index.js'; import type { NotesRepository, UsersRepository, PollsRepository, PollVotesRepository, MiUser } from '@/models/index.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import { RelayService } from '@/core/RelayService.js'; import { RelayService } from '@/core/RelayService.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
@ -42,7 +42,7 @@ export class PollService {
} }
@bindThis @bindThis
public async vote(user: User, note: Note, choice: number) { public async vote(user: MiUser, note: MiNote, choice: number) {
const poll = await this.pollsRepository.findOneBy({ noteId: note.id }); const poll = await this.pollsRepository.findOneBy({ noteId: note.id });
if (poll == null) throw new Error('poll not found'); if (poll == null) throw new Error('poll not found');
@ -92,7 +92,7 @@ export class PollService {
} }
@bindThis @bindThis
public async deliverQuestionUpdate(noteId: Note['id']) { public async deliverQuestionUpdate(noteId: MiNote['id']) {
const note = await this.notesRepository.findOneBy({ id: noteId }); const note = await this.notesRepository.findOneBy({ id: noteId });
if (note == null) throw new Error('note not found'); if (note == null) throw new Error('note not found');

View file

@ -5,7 +5,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import type { UsersRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js';
import type { LocalUser } from '@/models/entities/User.js'; import type { MiLocalUser } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@ -21,9 +21,9 @@ export class ProxyAccountService {
} }
@bindThis @bindThis
public async fetch(): Promise<LocalUser | null> { public async fetch(): Promise<MiLocalUser | null> {
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
if (meta.proxyAccountId == null) return null; if (meta.proxyAccountId == null) return null;
return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as LocalUser; return await this.usersRepository.findOneByOrFail({ id: meta.proxyAccountId }) as MiLocalUser;
} }
} }

View file

@ -10,7 +10,7 @@ import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type { Packed } from '@/misc/json-schema.js'; import type { Packed } from '@/misc/json-schema.js';
import { getNoteSummary } from '@/misc/get-note-summary.js'; import { getNoteSummary } from '@/misc/get-note-summary.js';
import type { SwSubscription, SwSubscriptionsRepository } from '@/models/index.js'; import type { MiSwSubscription, SwSubscriptionsRepository } from '@/models/index.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { RedisKVCache } from '@/misc/cache.js'; import { RedisKVCache } from '@/misc/cache.js';
@ -48,7 +48,7 @@ function truncateBody<T extends keyof PushNotificationsTypes>(type: T, body: Pus
@Injectable() @Injectable()
export class PushNotificationService implements OnApplicationShutdown { export class PushNotificationService implements OnApplicationShutdown {
private subscriptionsCache: RedisKVCache<SwSubscription[]>; private subscriptionsCache: RedisKVCache<MiSwSubscription[]>;
constructor( constructor(
@Inject(DI.config) @Inject(DI.config)
@ -62,7 +62,7 @@ export class PushNotificationService implements OnApplicationShutdown {
private metaService: MetaService, private metaService: MetaService,
) { ) {
this.subscriptionsCache = new RedisKVCache<SwSubscription[]>(this.redisClient, 'userSwSubscriptions', { this.subscriptionsCache = new RedisKVCache<MiSwSubscription[]>(this.redisClient, 'userSwSubscriptions', {
lifetime: 1000 * 60 * 60 * 1, // 1h lifetime: 1000 * 60 * 60 * 1, // 1h
memoryCacheLifetime: 1000 * 60 * 3, // 3m memoryCacheLifetime: 1000 * 60 * 3, // 3m
fetcher: (key) => this.swSubscriptionsRepository.findBy({ userId: key }), fetcher: (key) => this.swSubscriptionsRepository.findBy({ userId: key }),

View file

@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Brackets, ObjectLiteral } from 'typeorm'; import { Brackets, ObjectLiteral } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/index.js'; import type { UserProfilesRepository, FollowingsRepository, ChannelFollowingsRepository, MutedNotesRepository, BlockingsRepository, NoteThreadMutingsRepository, MutingsRepository, RenoteMutingsRepository } from '@/models/index.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { SelectQueryBuilder } from 'typeorm'; import type { SelectQueryBuilder } from 'typeorm';
@ -69,7 +69,7 @@ export class QueryService {
// ここでいうBlockedは被Blockedの意 // ここでいうBlockedは被Blockedの意
@bindThis @bindThis
public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { public generateBlockedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
.select('blocking.blockerId') .select('blocking.blockerId')
.where('blocking.blockeeId = :blockeeId', { blockeeId: me.id }); .where('blocking.blockeeId = :blockeeId', { blockeeId: me.id });
@ -92,7 +92,7 @@ export class QueryService {
} }
@bindThis @bindThis
public generateBlockQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { public generateBlockQueryForUsers(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking') const blockingQuery = this.blockingsRepository.createQueryBuilder('blocking')
.select('blocking.blockeeId') .select('blocking.blockeeId')
.where('blocking.blockerId = :blockerId', { blockerId: me.id }); .where('blocking.blockerId = :blockerId', { blockerId: me.id });
@ -109,7 +109,7 @@ export class QueryService {
} }
@bindThis @bindThis
public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void { public generateChannelQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void {
if (me == null) { if (me == null) {
q.andWhere('note.channelId IS NULL'); q.andWhere('note.channelId IS NULL');
} else { } else {
@ -131,7 +131,7 @@ export class QueryService {
} }
@bindThis @bindThis
public generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { public generateMutedNoteQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted') const mutedQuery = this.mutedNotesRepository.createQueryBuilder('muted')
.select('muted.noteId') .select('muted.noteId')
.where('muted.userId = :userId', { userId: me.id }); .where('muted.userId = :userId', { userId: me.id });
@ -142,7 +142,7 @@ export class QueryService {
} }
@bindThis @bindThis
public generateMutedNoteThreadQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { public generateMutedNoteThreadQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted') const mutedQuery = this.noteThreadMutingsRepository.createQueryBuilder('threadMuted')
.select('threadMuted.threadId') .select('threadMuted.threadId')
.where('threadMuted.userId = :userId', { userId: me.id }); .where('threadMuted.userId = :userId', { userId: me.id });
@ -157,7 +157,7 @@ export class QueryService {
} }
@bindThis @bindThis
public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: User['id'] }, exclude?: User): void { public generateMutedUserQuery(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }, exclude?: MiUser): void {
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
.select('muting.muteeId') .select('muting.muteeId')
.where('muting.muterId = :muterId', { muterId: me.id }); .where('muting.muterId = :muterId', { muterId: me.id });
@ -202,7 +202,7 @@ export class QueryService {
} }
@bindThis @bindThis
public generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { public generateMutedUserQueryForUsers(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
const mutingQuery = this.mutingsRepository.createQueryBuilder('muting') const mutingQuery = this.mutingsRepository.createQueryBuilder('muting')
.select('muting.muteeId') .select('muting.muteeId')
.where('muting.muterId = :muterId', { muterId: me.id }); .where('muting.muterId = :muterId', { muterId: me.id });
@ -213,7 +213,7 @@ export class QueryService {
} }
@bindThis @bindThis
public generateRepliesQuery(q: SelectQueryBuilder<any>, withReplies: boolean, me?: Pick<User, 'id'> | null): void { public generateRepliesQuery(q: SelectQueryBuilder<any>, withReplies: boolean, me?: Pick<MiUser, 'id'> | null): void {
if (me == null) { if (me == null) {
q.andWhere(new Brackets(qb => { qb q.andWhere(new Brackets(qb => { qb
.where('note.replyId IS NULL') // 返信ではない .where('note.replyId IS NULL') // 返信ではない
@ -239,7 +239,7 @@ export class QueryService {
} }
@bindThis @bindThis
public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: User['id'] } | null): void { public generateVisibilityQuery(q: SelectQueryBuilder<any>, me?: { id: MiUser['id'] } | null): void {
// This code must always be synchronized with the checks in Notes.isVisibleForMe. // This code must always be synchronized with the checks in Notes.isVisibleForMe.
if (me == null) { if (me == null) {
q.andWhere(new Brackets(qb => { qb q.andWhere(new Brackets(qb => { qb
@ -279,7 +279,7 @@ export class QueryService {
} }
@bindThis @bindThis
public generateMutedUserRenotesQueryForNotes(q: SelectQueryBuilder<any>, me: { id: User['id'] }): void { public generateMutedUserRenotesQueryForNotes(q: SelectQueryBuilder<any>, me: { id: MiUser['id'] }): void {
const mutingQuery = this.renoteMutingsRepository.createQueryBuilder('renote_muting') const mutingQuery = this.renoteMutingsRepository.createQueryBuilder('renote_muting')
.select('renote_muting.muteeId') .select('renote_muting.muteeId')
.where('renote_muting.muterId = :muterId', { muterId: me.id }); .where('renote_muting.muterId = :muterId', { muterId: me.id });

View file

@ -6,8 +6,8 @@
import { randomUUID } from 'node:crypto'; import { randomUUID } from 'node:crypto';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import type { IActivity } from '@/core/activitypub/type.js'; import type { IActivity } from '@/core/activitypub/type.js';
import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { MiDriveFile } from '@/models/entities/DriveFile.js';
import type { Webhook, webhookEventTypes } from '@/models/entities/Webhook.js'; import type { MiWebhook, webhookEventTypes } from '@/models/entities/Webhook.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@ -237,7 +237,7 @@ export class QueueService {
} }
@bindThis @bindThis
public createImportFollowingJob(user: ThinUser, fileId: DriveFile['id']) { public createImportFollowingJob(user: ThinUser, fileId: MiDriveFile['id']) {
return this.dbQueue.add('importFollowing', { return this.dbQueue.add('importFollowing', {
user: { id: user.id }, user: { id: user.id },
fileId: fileId, fileId: fileId,
@ -254,7 +254,7 @@ export class QueueService {
} }
@bindThis @bindThis
public createImportMutingJob(user: ThinUser, fileId: DriveFile['id']) { public createImportMutingJob(user: ThinUser, fileId: MiDriveFile['id']) {
return this.dbQueue.add('importMuting', { return this.dbQueue.add('importMuting', {
user: { id: user.id }, user: { id: user.id },
fileId: fileId, fileId: fileId,
@ -265,7 +265,7 @@ export class QueueService {
} }
@bindThis @bindThis
public createImportBlockingJob(user: ThinUser, fileId: DriveFile['id']) { public createImportBlockingJob(user: ThinUser, fileId: MiDriveFile['id']) {
return this.dbQueue.add('importBlocking', { return this.dbQueue.add('importBlocking', {
user: { id: user.id }, user: { id: user.id },
fileId: fileId, fileId: fileId,
@ -298,7 +298,7 @@ export class QueueService {
} }
@bindThis @bindThis
public createImportUserListsJob(user: ThinUser, fileId: DriveFile['id']) { public createImportUserListsJob(user: ThinUser, fileId: MiDriveFile['id']) {
return this.dbQueue.add('importUserLists', { return this.dbQueue.add('importUserLists', {
user: { id: user.id }, user: { id: user.id },
fileId: fileId, fileId: fileId,
@ -309,7 +309,7 @@ export class QueueService {
} }
@bindThis @bindThis
public createImportCustomEmojisJob(user: ThinUser, fileId: DriveFile['id']) { public createImportCustomEmojisJob(user: ThinUser, fileId: MiDriveFile['id']) {
return this.dbQueue.add('importCustomEmojis', { return this.dbQueue.add('importCustomEmojis', {
user: { id: user.id }, user: { id: user.id },
fileId: fileId, fileId: fileId,
@ -412,7 +412,7 @@ export class QueueService {
} }
@bindThis @bindThis
public webhookDeliver(webhook: Webhook, type: typeof webhookEventTypes[number], content: unknown) { public webhookDeliver(webhook: MiWebhook, type: typeof webhookEventTypes[number], content: unknown) {
const data = { const data = {
type, type,
content, content,

View file

@ -7,10 +7,10 @@ import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/index.js'; import type { EmojisRepository, NoteReactionsRepository, UsersRepository, NotesRepository } from '@/models/index.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
import type { RemoteUser, User } from '@/models/entities/User.js'; import type { MiRemoteUser, MiUser } from '@/models/entities/User.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import type { MiNoteReaction } from '@/models/entities/NoteReaction.js';
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import { NotificationService } from '@/core/NotificationService.js'; import { NotificationService } from '@/core/NotificationService.js';
@ -95,7 +95,7 @@ export class ReactionService {
} }
@bindThis @bindThis
public async create(user: { id: User['id']; host: User['host']; isBot: User['isBot'] }, note: Note, _reaction?: string | null) { public async create(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot'] }, note: MiNote, _reaction?: string | null) {
// Check blocking // Check blocking
if (note.userId !== user.id) { if (note.userId !== user.id) {
const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id); const blocked = await this.userBlockingService.checkBlocked(note.userId, user.id);
@ -146,7 +146,7 @@ export class ReactionService {
} }
} }
const record: NoteReaction = { const record: MiNoteReaction = {
id: this.idService.genId(), id: this.idService.genId(),
createdAt: new Date(), createdAt: new Date(),
noteId: note.id, noteId: note.id,
@ -231,7 +231,7 @@ export class ReactionService {
const dm = this.apDeliverManagerService.createDeliverManager(user, content); const dm = this.apDeliverManagerService.createDeliverManager(user, content);
if (note.userHost !== null) { if (note.userHost !== null) {
const reactee = await this.usersRepository.findOneBy({ id: note.userId }); const reactee = await this.usersRepository.findOneBy({ id: note.userId });
dm.addDirectRecipe(reactee as RemoteUser); dm.addDirectRecipe(reactee as MiRemoteUser);
} }
if (['public', 'home', 'followers'].includes(note.visibility)) { if (['public', 'home', 'followers'].includes(note.visibility)) {
@ -239,7 +239,7 @@ export class ReactionService {
} else if (note.visibility === 'specified') { } else if (note.visibility === 'specified') {
const visibleUsers = await Promise.all(note.visibleUserIds.map(id => this.usersRepository.findOneBy({ id }))); const visibleUsers = await Promise.all(note.visibleUserIds.map(id => this.usersRepository.findOneBy({ id })));
for (const u of visibleUsers.filter(u => u && this.userEntityService.isRemoteUser(u))) { for (const u of visibleUsers.filter(u => u && this.userEntityService.isRemoteUser(u))) {
dm.addDirectRecipe(u as RemoteUser); dm.addDirectRecipe(u as MiRemoteUser);
} }
} }
@ -249,7 +249,7 @@ export class ReactionService {
} }
@bindThis @bindThis
public async delete(user: { id: User['id']; host: User['host']; isBot: User['isBot']; }, note: Note) { public async delete(user: { id: MiUser['id']; host: MiUser['host']; isBot: MiUser['isBot']; }, note: MiNote) {
// if already unreacted // if already unreacted
const exist = await this.noteReactionsRepository.findOneBy({ const exist = await this.noteReactionsRepository.findOneBy({
noteId: note.id, noteId: note.id,
@ -289,7 +289,7 @@ export class ReactionService {
const dm = this.apDeliverManagerService.createDeliverManager(user, content); const dm = this.apDeliverManagerService.createDeliverManager(user, content);
if (note.userHost !== null) { if (note.userHost !== null) {
const reactee = await this.usersRepository.findOneBy({ id: note.userId }); const reactee = await this.usersRepository.findOneBy({ id: note.userId });
dm.addDirectRecipe(reactee as RemoteUser); dm.addDirectRecipe(reactee as MiRemoteUser);
} }
dm.addFollowersRecipe(); dm.addFollowersRecipe();
dm.execute(); dm.execute();

View file

@ -5,11 +5,11 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { IsNull } from 'typeorm'; import { IsNull } from 'typeorm';
import type { LocalUser, User } from '@/models/entities/User.js'; import type { MiLocalUser, MiUser } from '@/models/entities/User.js';
import type { RelaysRepository, UsersRepository } from '@/models/index.js'; import type { RelaysRepository, UsersRepository } from '@/models/index.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { MemorySingleCache } from '@/misc/cache.js'; import { MemorySingleCache } from '@/misc/cache.js';
import type { Relay } from '@/models/entities/Relay.js'; import type { MiRelay } from '@/models/entities/Relay.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
import { CreateSystemUserService } from '@/core/CreateSystemUserService.js'; import { CreateSystemUserService } from '@/core/CreateSystemUserService.js';
import { ApRendererService } from '@/core/activitypub/ApRendererService.js'; import { ApRendererService } from '@/core/activitypub/ApRendererService.js';
@ -21,7 +21,7 @@ const ACTOR_USERNAME = 'relay.actor' as const;
@Injectable() @Injectable()
export class RelayService { export class RelayService {
private relaysCache: MemorySingleCache<Relay[]>; private relaysCache: MemorySingleCache<MiRelay[]>;
constructor( constructor(
@Inject(DI.usersRepository) @Inject(DI.usersRepository)
@ -35,24 +35,24 @@ export class RelayService {
private createSystemUserService: CreateSystemUserService, private createSystemUserService: CreateSystemUserService,
private apRendererService: ApRendererService, private apRendererService: ApRendererService,
) { ) {
this.relaysCache = new MemorySingleCache<Relay[]>(1000 * 60 * 10); this.relaysCache = new MemorySingleCache<MiRelay[]>(1000 * 60 * 10);
} }
@bindThis @bindThis
private async getRelayActor(): Promise<LocalUser> { private async getRelayActor(): Promise<MiLocalUser> {
const user = await this.usersRepository.findOneBy({ const user = await this.usersRepository.findOneBy({
host: IsNull(), host: IsNull(),
username: ACTOR_USERNAME, username: ACTOR_USERNAME,
}); });
if (user) return user as LocalUser; if (user) return user as MiLocalUser;
const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME); const created = await this.createSystemUserService.createSystemUser(ACTOR_USERNAME);
return created as LocalUser; return created as MiLocalUser;
} }
@bindThis @bindThis
public async addRelay(inbox: string): Promise<Relay> { public async addRelay(inbox: string): Promise<MiRelay> {
const relay = await this.relaysRepository.insert({ const relay = await this.relaysRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
inbox, inbox,
@ -87,7 +87,7 @@ export class RelayService {
} }
@bindThis @bindThis
public async listRelay(): Promise<Relay[]> { public async listRelay(): Promise<MiRelay[]> {
const relays = await this.relaysRepository.find(); const relays = await this.relaysRepository.find();
return relays; return relays;
} }
@ -111,7 +111,7 @@ export class RelayService {
} }
@bindThis @bindThis
public async deliverToRelays(user: { id: User['id']; host: null; }, activity: any): Promise<void> { public async deliverToRelays(user: { id: MiUser['id']; host: null; }, activity: any): Promise<void> {
if (activity == null) return; if (activity == null) return;
const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({ const relays = await this.relaysCache.fetch(() => this.relaysRepository.findBy({

View file

@ -9,7 +9,7 @@ import chalk from 'chalk';
import { IsNull } from 'typeorm'; import { IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { UsersRepository } from '@/models/index.js'; import type { UsersRepository } from '@/models/index.js';
import type { LocalUser, RemoteUser } from '@/models/entities/User.js'; import type { MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
import { UtilityService } from '@/core/UtilityService.js'; import { UtilityService } from '@/core/UtilityService.js';
@ -40,7 +40,7 @@ export class RemoteUserResolveService {
} }
@bindThis @bindThis
public async resolveUser(username: string, host: string | null): Promise<LocalUser | RemoteUser> { public async resolveUser(username: string, host: string | null): Promise<MiLocalUser | MiRemoteUser> {
const usernameLower = username.toLowerCase(); const usernameLower = username.toLowerCase();
if (host == null) { if (host == null) {
@ -51,7 +51,7 @@ export class RemoteUserResolveService {
} else { } else {
return u; return u;
} }
}) as LocalUser; }) as MiLocalUser;
} }
host = this.utilityService.toPuny(host); host = this.utilityService.toPuny(host);
@ -64,10 +64,10 @@ export class RemoteUserResolveService {
} else { } else {
return u; return u;
} }
}) as LocalUser; }) as MiLocalUser;
} }
const user = await this.usersRepository.findOneBy({ usernameLower, host }) as RemoteUser | null; const user = await this.usersRepository.findOneBy({ usernameLower, host }) as MiRemoteUser | null;
const acctLower = `${usernameLower}@${host}`; const acctLower = `${usernameLower}@${host}`;
@ -86,7 +86,7 @@ export class RemoteUserResolveService {
} else { } else {
return u; return u;
} }
})) as LocalUser; })) as MiLocalUser;
} }
} }
@ -132,7 +132,7 @@ export class RemoteUserResolveService {
if (u == null) { if (u == null) {
throw new Error('user not found'); throw new Error('user not found');
} else { } else {
return u as LocalUser | RemoteUser; return u as MiLocalUser | MiRemoteUser;
} }
}); });
} }

View file

@ -6,9 +6,9 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import { In } from 'typeorm'; import { In } from 'typeorm';
import type { Role, RoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js'; import type { MiRole, MiRoleAssignment, RoleAssignmentsRepository, RolesRepository, UsersRepository } from '@/models/index.js';
import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js'; import { MemoryKVCache, MemorySingleCache } from '@/misc/cache.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
@ -71,8 +71,8 @@ export const DEFAULT_POLICIES: RolePolicies = {
@Injectable() @Injectable()
export class RoleService implements OnApplicationShutdown { export class RoleService implements OnApplicationShutdown {
private rolesCache: MemorySingleCache<Role[]>; private rolesCache: MemorySingleCache<MiRole[]>;
private roleAssignmentByUserIdCache: MemoryKVCache<RoleAssignment[]>; private roleAssignmentByUserIdCache: MemoryKVCache<MiRoleAssignment[]>;
public static AlreadyAssignedError = class extends Error {}; public static AlreadyAssignedError = class extends Error {};
public static NotAssignedError = class extends Error {}; public static NotAssignedError = class extends Error {};
@ -101,8 +101,8 @@ export class RoleService implements OnApplicationShutdown {
) { ) {
//this.onMessage = this.onMessage.bind(this); //this.onMessage = this.onMessage.bind(this);
this.rolesCache = new MemorySingleCache<Role[]>(1000 * 60 * 60 * 1); this.rolesCache = new MemorySingleCache<MiRole[]>(1000 * 60 * 60 * 1);
this.roleAssignmentByUserIdCache = new MemoryKVCache<RoleAssignment[]>(1000 * 60 * 60 * 1); this.roleAssignmentByUserIdCache = new MemoryKVCache<MiRoleAssignment[]>(1000 * 60 * 60 * 1);
this.redisForSub.on('message', this.onMessage); this.redisForSub.on('message', this.onMessage);
} }
@ -173,7 +173,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
private evalCond(user: User, value: RoleCondFormulaValue): boolean { private evalCond(user: MiUser, value: RoleCondFormulaValue): boolean {
try { try {
switch (value.type) { switch (value.type) {
case 'and': { case 'and': {
@ -225,7 +225,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async getUserAssigns(userId: User['id']) { public async getUserAssigns(userId: MiUser['id']) {
const now = Date.now(); const now = Date.now();
let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId })); let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
// 期限切れのロールを除外 // 期限切れのロールを除外
@ -234,7 +234,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async getUserRoles(userId: User['id']) { public async getUserRoles(userId: MiUser['id']) {
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({})); const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
const assigns = await this.getUserAssigns(userId); const assigns = await this.getUserAssigns(userId);
const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id)); const assignedRoles = roles.filter(r => assigns.map(x => x.roleId).includes(r.id));
@ -247,7 +247,7 @@ export class RoleService implements OnApplicationShutdown {
* *
*/ */
@bindThis @bindThis
public async getUserBadgeRoles(userId: User['id']) { public async getUserBadgeRoles(userId: MiUser['id']) {
const now = Date.now(); const now = Date.now();
let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId })); let assigns = await this.roleAssignmentByUserIdCache.fetch(userId, () => this.roleAssignmentsRepository.findBy({ userId }));
// 期限切れのロールを除外 // 期限切れのロールを除外
@ -266,7 +266,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async getUserPolicies(userId: User['id'] | null): Promise<RolePolicies> { public async getUserPolicies(userId: MiUser['id'] | null): Promise<RolePolicies> {
const meta = await this.metaService.fetch(); const meta = await this.metaService.fetch();
const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies }; const basePolicies = { ...DEFAULT_POLICIES, ...meta.policies };
@ -314,19 +314,19 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async isModerator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise<boolean> { public async isModerator(user: { id: MiUser['id']; isRoot: MiUser['isRoot'] } | null): Promise<boolean> {
if (user == null) return false; if (user == null) return false;
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator); return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isModerator || r.isAdministrator);
} }
@bindThis @bindThis
public async isAdministrator(user: { id: User['id']; isRoot: User['isRoot'] } | null): Promise<boolean> { public async isAdministrator(user: { id: MiUser['id']; isRoot: MiUser['isRoot'] } | null): Promise<boolean> {
if (user == null) return false; if (user == null) return false;
return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator); return user.isRoot || (await this.getUserRoles(user.id)).some(r => r.isAdministrator);
} }
@bindThis @bindThis
public async isExplorable(role: { id: Role['id']} | null): Promise<boolean> { public async isExplorable(role: { id: MiRole['id']} | null): Promise<boolean> {
if (role == null) return false; if (role == null) return false;
const check = await this.rolesRepository.findOneBy({ id: role.id }); const check = await this.rolesRepository.findOneBy({ id: role.id });
if (check == null) return false; if (check == null) return false;
@ -334,7 +334,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async getModeratorIds(includeAdmins = true): Promise<User['id'][]> { public async getModeratorIds(includeAdmins = true): Promise<MiUser['id'][]> {
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({})); const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
const moderatorRoles = includeAdmins ? roles.filter(r => r.isModerator || r.isAdministrator) : roles.filter(r => r.isModerator); const moderatorRoles = includeAdmins ? roles.filter(r => r.isModerator || r.isAdministrator) : roles.filter(r => r.isModerator);
const assigns = moderatorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({ const assigns = moderatorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
@ -345,7 +345,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async getModerators(includeAdmins = true): Promise<User[]> { public async getModerators(includeAdmins = true): Promise<MiUser[]> {
const ids = await this.getModeratorIds(includeAdmins); const ids = await this.getModeratorIds(includeAdmins);
const users = ids.length > 0 ? await this.usersRepository.findBy({ const users = ids.length > 0 ? await this.usersRepository.findBy({
id: In(ids), id: In(ids),
@ -354,7 +354,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async getAdministratorIds(): Promise<User['id'][]> { public async getAdministratorIds(): Promise<MiUser['id'][]> {
const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({})); const roles = await this.rolesCache.fetch(() => this.rolesRepository.findBy({}));
const administratorRoles = roles.filter(r => r.isAdministrator); const administratorRoles = roles.filter(r => r.isAdministrator);
const assigns = administratorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({ const assigns = administratorRoles.length > 0 ? await this.roleAssignmentsRepository.findBy({
@ -365,7 +365,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async getAdministrators(): Promise<User[]> { public async getAdministrators(): Promise<MiUser[]> {
const ids = await this.getAdministratorIds(); const ids = await this.getAdministratorIds();
const users = ids.length > 0 ? await this.usersRepository.findBy({ const users = ids.length > 0 ? await this.usersRepository.findBy({
id: In(ids), id: In(ids),
@ -374,7 +374,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async assign(userId: User['id'], roleId: Role['id'], expiresAt: Date | null = null): Promise<void> { public async assign(userId: MiUser['id'], roleId: MiRole['id'], expiresAt: Date | null = null): Promise<void> {
const now = new Date(); const now = new Date();
const existing = await this.roleAssignmentsRepository.findOneBy({ const existing = await this.roleAssignmentsRepository.findOneBy({
@ -409,7 +409,7 @@ export class RoleService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async unassign(userId: User['id'], roleId: Role['id']): Promise<void> { public async unassign(userId: MiUser['id'], roleId: MiRole['id']): Promise<void> {
const now = new Date(); const now = new Date();
const existing = await this.roleAssignmentsRepository.findOneBy({ roleId, userId }); const existing = await this.roleAssignmentsRepository.findOneBy({ roleId, userId });

View file

@ -10,7 +10,7 @@ import { Injectable } from '@nestjs/common';
import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3'; import { DeleteObjectCommand, S3Client } from '@aws-sdk/client-s3';
import { Upload } from '@aws-sdk/lib-storage'; import { Upload } from '@aws-sdk/lib-storage';
import { NodeHttpHandler, NodeHttpHandlerOptions } from '@aws-sdk/node-http-handler'; import { NodeHttpHandler, NodeHttpHandlerOptions } from '@aws-sdk/node-http-handler';
import type { Meta } from '@/models/entities/Meta.js'; import type { MiMeta } from '@/models/entities/Meta.js';
import { HttpRequestService } from '@/core/HttpRequestService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/client-s3'; import type { DeleteObjectCommandInput, PutObjectCommandInput } from '@aws-sdk/client-s3';
@ -23,7 +23,7 @@ export class S3Service {
} }
@bindThis @bindThis
public getS3Client(meta: Meta): S3Client { public getS3Client(meta: MiMeta): S3Client {
const u = meta.objectStorageEndpoint const u = meta.objectStorageEndpoint
? `${meta.objectStorageUseSSL ? 'https' : 'http'}://${meta.objectStorageEndpoint}` ? `${meta.objectStorageUseSSL ? 'https' : 'http'}://${meta.objectStorageEndpoint}`
: `${meta.objectStorageUseSSL ? 'https' : 'http'}://example.net`; // dummy url to select http(s) agent : `${meta.objectStorageUseSSL ? 'https' : 'http'}://example.net`; // dummy url to select http(s) agent
@ -50,7 +50,7 @@ export class S3Service {
} }
@bindThis @bindThis
public async upload(meta: Meta, input: PutObjectCommandInput) { public async upload(meta: MiMeta, input: PutObjectCommandInput) {
const client = this.getS3Client(meta); const client = this.getS3Client(meta);
return new Upload({ return new Upload({
client, client,
@ -62,7 +62,7 @@ export class S3Service {
} }
@bindThis @bindThis
public delete(meta: Meta, input: DeleteObjectCommandInput) { public delete(meta: MiMeta, input: DeleteObjectCommandInput) {
const client = this.getS3Client(meta); const client = this.getS3Client(meta);
return client.send(new DeleteObjectCommand(input)); return client.send(new DeleteObjectCommand(input));
} }

View file

@ -8,8 +8,8 @@ import { In } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { Note } from '@/models/entities/Note.js'; import { MiNote } from '@/models/entities/Note.js';
import { User } from '@/models/index.js'; import { MiUser } from '@/models/index.js';
import type { NotesRepository } from '@/models/index.js'; import type { NotesRepository } from '@/models/index.js';
import { sqlLikeEscape } from '@/misc/sql-like-escape.js'; import { sqlLikeEscape } from '@/misc/sql-like-escape.js';
import { QueryService } from '@/core/QueryService.js'; import { QueryService } from '@/core/QueryService.js';
@ -25,6 +25,8 @@ type Q =
{ op: '<', k: K, v: number } | { op: '<', k: K, v: number } |
{ op: '>=', k: K, v: number } | { op: '>=', k: K, v: number } |
{ op: '<=', k: K, v: number } | { op: '<=', k: K, v: number } |
{ op: 'is null', k: K} |
{ op: 'is not null', k: K} |
{ op: 'and', qs: Q[] } | { op: 'and', qs: Q[] } |
{ op: 'or', qs: Q[] } | { op: 'or', qs: Q[] } |
{ op: 'not', q: Q }; { op: 'not', q: Q };
@ -50,6 +52,8 @@ function compileQuery(q: Q): string {
case '<=': return `(${q.k} <= ${compileValue(q.v)})`; case '<=': return `(${q.k} <= ${compileValue(q.v)})`;
case 'and': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' AND ') })`; case 'and': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' AND ') })`;
case 'or': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' OR ') })`; case 'or': return q.qs.length === 0 ? '' : `(${ q.qs.map(_q => compileQuery(_q)).join(' OR ') })`;
case 'is null': return `(${q.k} IS NULL)`;
case 'is not null': return `(${q.k} IS NOT NULL)`;
case 'not': return `(NOT ${compileQuery(q.q)})`; case 'not': return `(NOT ${compileQuery(q.q)})`;
default: throw new Error('unrecognized query operator'); default: throw new Error('unrecognized query operator');
} }
@ -105,7 +109,7 @@ export class SearchService {
} }
@bindThis @bindThis
public async indexNote(note: Note): Promise<void> { public async indexNote(note: MiNote): Promise<void> {
if (note.text == null && note.cw == null) return; if (note.text == null && note.cw == null) return;
if (!['home', 'public'].includes(note.visibility)) return; if (!['home', 'public'].includes(note.visibility)) return;
@ -141,7 +145,7 @@ export class SearchService {
} }
@bindThis @bindThis
public async unindexNote(note: Note): Promise<void> { public async unindexNote(note: MiNote): Promise<void> {
if (!['home', 'public'].includes(note.visibility)) return; if (!['home', 'public'].includes(note.visibility)) return;
if (this.meilisearch) { if (this.meilisearch) {
@ -150,15 +154,15 @@ export class SearchService {
} }
@bindThis @bindThis
public async searchNote(q: string, me: User | null, opts: { public async searchNote(q: string, me: MiUser | null, opts: {
userId?: Note['userId'] | null; userId?: MiNote['userId'] | null;
channelId?: Note['channelId'] | null; channelId?: MiNote['channelId'] | null;
host?: string | null; host?: string | null;
}, pagination: { }, pagination: {
untilId?: Note['id']; untilId?: MiNote['id'];
sinceId?: Note['id']; sinceId?: MiNote['id'];
limit?: number; limit?: number;
}): Promise<Note[]> { }): Promise<MiNote[]> {
if (this.meilisearch) { if (this.meilisearch) {
const filter: Q = { const filter: Q = {
op: 'and', op: 'and',
@ -170,7 +174,7 @@ export class SearchService {
if (opts.channelId) filter.qs.push({ op: '=', k: 'channelId', v: opts.channelId }); if (opts.channelId) filter.qs.push({ op: '=', k: 'channelId', v: opts.channelId });
if (opts.host) { if (opts.host) {
if (opts.host === '.') { if (opts.host === '.') {
// TODO: Meilisearchが2023/05/07現在値がNULLかどうかのクエリが書けない filter.qs.push({ op: 'is null', k: 'userHost' });
} else { } else {
filter.qs.push({ op: '=', k: 'userHost', v: opts.host }); filter.qs.push({ op: '=', k: 'userHost', v: opts.host });
} }
@ -204,6 +208,14 @@ export class SearchService {
.leftJoinAndSelect('reply.user', 'replyUser') .leftJoinAndSelect('reply.user', 'replyUser')
.leftJoinAndSelect('renote.user', 'renoteUser'); .leftJoinAndSelect('renote.user', 'renoteUser');
if (opts.host) {
if (opts.host === '.') {
query.andWhere('user.host IS NULL');
} else {
query.andWhere('user.host = :host', { host: opts.host });
}
}
this.queryService.generateVisibilityQuery(query, me); this.queryService.generateVisibilityQuery(query, me);
if (me) this.queryService.generateMutedUserQuery(query, me); if (me) this.queryService.generateMutedUserQuery(query, me);
if (me) this.queryService.generateBlockedUserQuery(query, me); if (me) this.queryService.generateBlockedUserQuery(query, me);

View file

@ -9,11 +9,11 @@ import bcrypt from 'bcryptjs';
import { DataSource, IsNull } from 'typeorm'; import { DataSource, IsNull } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { UsedUsernamesRepository, UsersRepository } from '@/models/index.js'; import type { UsedUsernamesRepository, UsersRepository } from '@/models/index.js';
import { User } from '@/models/entities/User.js'; import { MiUser } from '@/models/entities/User.js';
import { UserProfile } from '@/models/entities/UserProfile.js'; import { MiUserProfile } from '@/models/entities/UserProfile.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { UserKeypair } from '@/models/entities/UserKeypair.js'; import { MiUserKeypair } from '@/models/entities/UserKeypair.js';
import { UsedUsername } from '@/models/entities/UsedUsername.js'; import { MiUsedUsername } from '@/models/entities/UsedUsername.js';
import generateUserToken from '@/misc/generate-native-user-token.js'; import generateUserToken from '@/misc/generate-native-user-token.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@ -43,9 +43,9 @@ export class SignupService {
@bindThis @bindThis
public async signup(opts: { public async signup(opts: {
username: User['username']; username: MiUser['username'];
password?: string | null; password?: string | null;
passwordHash?: UserProfile['password'] | null; passwordHash?: MiUserProfile['password'] | null;
host?: string | null; host?: string | null;
ignorePreservedUsernames?: boolean; ignorePreservedUsernames?: boolean;
}) { }) {
@ -108,18 +108,18 @@ export class SignupService {
err ? rej(err) : res([publicKey, privateKey]), err ? rej(err) : res([publicKey, privateKey]),
)); ));
let account!: User; let account!: MiUser;
// Start transaction // Start transaction
await this.db.transaction(async transactionalEntityManager => { await this.db.transaction(async transactionalEntityManager => {
const exist = await transactionalEntityManager.findOneBy(User, { const exist = await transactionalEntityManager.findOneBy(MiUser, {
usernameLower: username.toLowerCase(), usernameLower: username.toLowerCase(),
host: IsNull(), host: IsNull(),
}); });
if (exist) throw new Error(' the username is already used'); if (exist) throw new Error(' the username is already used');
account = await transactionalEntityManager.save(new User({ account = await transactionalEntityManager.save(new MiUser({
id: this.idService.genId(), id: this.idService.genId(),
createdAt: new Date(), createdAt: new Date(),
username: username, username: username,
@ -129,19 +129,19 @@ export class SignupService {
isRoot: isTheFirstUser, isRoot: isTheFirstUser,
})); }));
await transactionalEntityManager.save(new UserKeypair({ await transactionalEntityManager.save(new MiUserKeypair({
publicKey: keyPair[0], publicKey: keyPair[0],
privateKey: keyPair[1], privateKey: keyPair[1],
userId: account.id, userId: account.id,
})); }));
await transactionalEntityManager.save(new UserProfile({ await transactionalEntityManager.save(new MiUserProfile({
userId: account.id, userId: account.id,
autoAcceptFollowed: true, autoAcceptFollowed: true,
password: hash, password: hash,
})); }));
await transactionalEntityManager.save(new UsedUsername({ await transactionalEntityManager.save(new MiUsedUsername({
createdAt: new Date(), createdAt: new Date(),
username: username.toLowerCase(), username: username.toLowerCase(),
})); }));

View file

@ -6,8 +6,8 @@
import { Inject, Injectable, OnModuleInit } from '@nestjs/common'; import { Inject, Injectable, OnModuleInit } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core'; import { ModuleRef } from '@nestjs/core';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import type { Blocking } from '@/models/entities/Blocking.js'; import type { MiBlocking } from '@/models/entities/Blocking.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
@ -58,7 +58,7 @@ export class UserBlockingService implements OnModuleInit {
} }
@bindThis @bindThis
public async block(blocker: User, blockee: User, silent = false) { public async block(blocker: MiUser, blockee: MiUser, silent = false) {
await Promise.all([ await Promise.all([
this.cancelRequest(blocker, blockee, silent), this.cancelRequest(blocker, blockee, silent),
this.cancelRequest(blockee, blocker, silent), this.cancelRequest(blockee, blocker, silent),
@ -74,7 +74,7 @@ export class UserBlockingService implements OnModuleInit {
blockerId: blocker.id, blockerId: blocker.id,
blockee, blockee,
blockeeId: blockee.id, blockeeId: blockee.id,
} as Blocking; } as MiBlocking;
await this.blockingsRepository.insert(blocking); await this.blockingsRepository.insert(blocking);
@ -93,7 +93,7 @@ export class UserBlockingService implements OnModuleInit {
} }
@bindThis @bindThis
private async cancelRequest(follower: User, followee: User, silent = false) { private async cancelRequest(follower: MiUser, followee: MiUser, silent = false) {
const request = await this.followRequestsRepository.findOneBy({ const request = await this.followRequestsRepository.findOneBy({
followeeId: followee.id, followeeId: followee.id,
followerId: follower.id, followerId: follower.id,
@ -143,7 +143,7 @@ export class UserBlockingService implements OnModuleInit {
} }
@bindThis @bindThis
private async removeFromList(listOwner: User, user: User) { private async removeFromList(listOwner: MiUser, user: MiUser) {
const userLists = await this.userListsRepository.findBy({ const userLists = await this.userListsRepository.findBy({
userId: listOwner.id, userId: listOwner.id,
}); });
@ -157,7 +157,7 @@ export class UserBlockingService implements OnModuleInit {
} }
@bindThis @bindThis
public async unblock(blocker: User, blockee: User) { public async unblock(blocker: MiUser, blockee: MiUser) {
const blocking = await this.blockingsRepository.findOneBy({ const blocking = await this.blockingsRepository.findOneBy({
blockerId: blocker.id, blockerId: blocker.id,
blockeeId: blockee.id, blockeeId: blockee.id,
@ -191,7 +191,7 @@ export class UserBlockingService implements OnModuleInit {
} }
@bindThis @bindThis
public async checkBlocked(blockerId: User['id'], blockeeId: User['id']): Promise<boolean> { public async checkBlocked(blockerId: MiUser['id'], blockeeId: MiUser['id']): Promise<boolean> {
return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId); return (await this.cacheService.userBlockingCache.fetch(blockerId)).has(blockeeId);
} }
} }

View file

@ -6,7 +6,7 @@
import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common'; import { Inject, Injectable, OnModuleInit, forwardRef } from '@nestjs/common';
import { ModuleRef } from '@nestjs/core'; import { ModuleRef } from '@nestjs/core';
import { IsNull } from 'typeorm'; import { IsNull } from 'typeorm';
import type { LocalUser, PartialLocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js'; import type { MiLocalUser, MiPartialLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/entities/User.js';
import { IdentifiableError } from '@/misc/identifiable-error.js'; import { IdentifiableError } from '@/misc/identifiable-error.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js'; import PerUserFollowingChart from '@/core/chart/charts/per-user-following.js';
@ -32,16 +32,16 @@ import Logger from '../logger.js';
const logger = new Logger('following/create'); const logger = new Logger('following/create');
type Local = LocalUser | { type Local = MiLocalUser | {
id: LocalUser['id']; id: MiLocalUser['id'];
host: LocalUser['host']; host: MiLocalUser['host'];
uri: LocalUser['uri'] uri: MiLocalUser['uri']
}; };
type Remote = RemoteUser | { type Remote = MiRemoteUser | {
id: RemoteUser['id']; id: MiRemoteUser['id'];
host: RemoteUser['host']; host: MiRemoteUser['host'];
uri: RemoteUser['uri']; uri: MiRemoteUser['uri'];
inbox: RemoteUser['inbox']; inbox: MiRemoteUser['inbox'];
}; };
type Both = Local | Remote; type Both = Local | Remote;
@ -91,11 +91,11 @@ export class UserFollowingService implements OnModuleInit {
} }
@bindThis @bindThis
public async follow(_follower: { id: User['id'] }, _followee: { id: User['id'] }, requestId?: string, silent = false): Promise<void> { public async follow(_follower: { id: MiUser['id'] }, _followee: { id: MiUser['id'] }, requestId?: string, silent = false): Promise<void> {
const [follower, followee] = await Promise.all([ const [follower, followee] = await Promise.all([
this.usersRepository.findOneByOrFail({ id: _follower.id }), this.usersRepository.findOneByOrFail({ id: _follower.id }),
this.usersRepository.findOneByOrFail({ id: _followee.id }), this.usersRepository.findOneByOrFail({ id: _followee.id }),
]) as [LocalUser | RemoteUser, LocalUser | RemoteUser]; ]) as [MiLocalUser | MiRemoteUser, MiLocalUser | MiRemoteUser];
// check blocking // check blocking
const [blocking, blocked] = await Promise.all([ const [blocking, blocked] = await Promise.all([
@ -180,10 +180,10 @@ export class UserFollowingService implements OnModuleInit {
@bindThis @bindThis
private async insertFollowingDoc( private async insertFollowingDoc(
followee: { followee: {
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']
}, },
follower: { follower: {
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox'] id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox']
}, },
silent = false, silent = false,
): Promise<void> { ): Promise<void> {
@ -312,10 +312,10 @@ export class UserFollowingService implements OnModuleInit {
@bindThis @bindThis
public async unfollow( public async unfollow(
follower: { follower: {
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
}, },
followee: { followee: {
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
}, },
silent = false, silent = false,
): Promise<void> { ): Promise<void> {
@ -358,21 +358,21 @@ export class UserFollowingService implements OnModuleInit {
} }
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as PartialLocalUser, followee as PartialRemoteUser), follower)); const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as MiPartialLocalUser, followee as MiPartialRemoteUser), follower));
this.queueService.deliver(follower, content, followee.inbox, false); this.queueService.deliver(follower, content, followee.inbox, false);
} }
if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) { if (this.userEntityService.isLocalUser(followee) && this.userEntityService.isRemoteUser(follower)) {
// local user has null host // local user has null host
const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower as PartialRemoteUser, followee as PartialLocalUser), followee)); const content = this.apRendererService.addContext(this.apRendererService.renderReject(this.apRendererService.renderFollow(follower as MiPartialRemoteUser, followee as MiPartialLocalUser), followee));
this.queueService.deliver(followee, content, follower.inbox, false); this.queueService.deliver(followee, content, follower.inbox, false);
} }
} }
@bindThis @bindThis
private async decrementFollowing( private async decrementFollowing(
follower: User, follower: MiUser,
followee: User, followee: MiUser,
): Promise<void> { ): Promise<void> {
this.globalEventService.publishInternalEvent('unfollow', { followerId: follower.id, followeeId: followee.id }); this.globalEventService.publishInternalEvent('unfollow', { followerId: follower.id, followeeId: followee.id });
@ -444,10 +444,10 @@ export class UserFollowingService implements OnModuleInit {
@bindThis @bindThis
public async createFollowRequest( public async createFollowRequest(
follower: { follower: {
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
}, },
followee: { followee: {
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
}, },
requestId?: string, requestId?: string,
): Promise<void> { ): Promise<void> {
@ -494,7 +494,7 @@ export class UserFollowingService implements OnModuleInit {
} }
if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) { if (this.userEntityService.isLocalUser(follower) && this.userEntityService.isRemoteUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower as PartialLocalUser, followee as PartialRemoteUser, requestId ?? `${this.config.url}/follows/${followRequest.id}`)); const content = this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiPartialLocalUser, followee as MiPartialRemoteUser, requestId ?? `${this.config.url}/follows/${followRequest.id}`));
this.queueService.deliver(follower, content, followee.inbox, false); this.queueService.deliver(follower, content, followee.inbox, false);
} }
} }
@ -502,14 +502,14 @@ export class UserFollowingService implements OnModuleInit {
@bindThis @bindThis
public async cancelFollowRequest( public async cancelFollowRequest(
followee: { followee: {
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox'] id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']
}, },
follower: { follower: {
id: User['id']; host: User['host']; uri: User['host'] id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']
}, },
): Promise<void> { ): Promise<void> {
if (this.userEntityService.isRemoteUser(followee)) { if (this.userEntityService.isRemoteUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as PartialLocalUser | PartialRemoteUser, followee as PartialRemoteUser), follower)); const content = this.apRendererService.addContext(this.apRendererService.renderUndo(this.apRendererService.renderFollow(follower as MiPartialLocalUser | MiPartialRemoteUser, followee as MiPartialRemoteUser), follower));
if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので if (this.userEntityService.isLocalUser(follower)) { // 本来このチェックは不要だけどTSに怒られるので
this.queueService.deliver(follower, content, followee.inbox, false); this.queueService.deliver(follower, content, followee.inbox, false);
@ -540,9 +540,9 @@ export class UserFollowingService implements OnModuleInit {
@bindThis @bindThis
public async acceptFollowRequest( public async acceptFollowRequest(
followee: { followee: {
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
}, },
follower: User, follower: MiUser,
): Promise<void> { ): Promise<void> {
const request = await this.followRequestsRepository.findOneBy({ const request = await this.followRequestsRepository.findOneBy({
followeeId: followee.id, followeeId: followee.id,
@ -556,7 +556,7 @@ export class UserFollowingService implements OnModuleInit {
await this.insertFollowingDoc(followee, follower); await this.insertFollowingDoc(followee, follower);
if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) { if (this.userEntityService.isRemoteUser(follower) && this.userEntityService.isLocalUser(followee)) {
const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as PartialLocalUser, request.requestId!), followee)); const content = this.apRendererService.addContext(this.apRendererService.renderAccept(this.apRendererService.renderFollow(follower, followee as MiPartialLocalUser, request.requestId!), followee));
this.queueService.deliver(followee, content, follower.inbox, false); this.queueService.deliver(followee, content, follower.inbox, false);
} }
@ -568,7 +568,7 @@ export class UserFollowingService implements OnModuleInit {
@bindThis @bindThis
public async acceptAllFollowRequests( public async acceptAllFollowRequests(
user: { user: {
id: User['id']; host: User['host']; uri: User['host']; inbox: User['inbox']; sharedInbox: User['sharedInbox']; id: MiUser['id']; host: MiUser['host']; uri: MiUser['host']; inbox: MiUser['inbox']; sharedInbox: MiUser['sharedInbox'];
}, },
): Promise<void> { ): Promise<void> {
const requests = await this.followRequestsRepository.findBy({ const requests = await this.followRequestsRepository.findBy({

View file

@ -5,16 +5,16 @@
import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common'; import { Inject, Injectable, OnApplicationShutdown } from '@nestjs/common';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import type { UserKeypairsRepository } from '@/models/index.js'; import type { UserKeypairsRepository } from '@/models/index.js';
import { RedisKVCache } from '@/misc/cache.js'; import { RedisKVCache } from '@/misc/cache.js';
import type { UserKeypair } from '@/models/entities/UserKeypair.js'; import type { MiUserKeypair } from '@/models/entities/UserKeypair.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@Injectable() @Injectable()
export class UserKeypairService implements OnApplicationShutdown { export class UserKeypairService implements OnApplicationShutdown {
private cache: RedisKVCache<UserKeypair>; private cache: RedisKVCache<MiUserKeypair>;
constructor( constructor(
@Inject(DI.redis) @Inject(DI.redis)
@ -23,7 +23,7 @@ export class UserKeypairService implements OnApplicationShutdown {
@Inject(DI.userKeypairsRepository) @Inject(DI.userKeypairsRepository)
private userKeypairsRepository: UserKeypairsRepository, private userKeypairsRepository: UserKeypairsRepository,
) { ) {
this.cache = new RedisKVCache<UserKeypair>(this.redisClient, 'userKeypair', { this.cache = new RedisKVCache<MiUserKeypair>(this.redisClient, 'userKeypair', {
lifetime: 1000 * 60 * 60 * 24, // 24h lifetime: 1000 * 60 * 60 * 24, // 24h
memoryCacheLifetime: Infinity, memoryCacheLifetime: Infinity,
fetcher: (key) => this.userKeypairsRepository.findOneByOrFail({ userId: key }), fetcher: (key) => this.userKeypairsRepository.findOneByOrFail({ userId: key }),
@ -33,7 +33,7 @@ export class UserKeypairService implements OnApplicationShutdown {
} }
@bindThis @bindThis
public async getUserKeypair(userId: User['id']): Promise<UserKeypair> { public async getUserKeypair(userId: MiUser['id']): Promise<MiUserKeypair> {
return await this.cache.fetch(userId); return await this.cache.fetch(userId);
} }

View file

@ -5,9 +5,9 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import type { UserListJoiningsRepository } from '@/models/index.js'; import type { UserListJoiningsRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import type { UserList } from '@/models/entities/UserList.js'; import type { MiUserList } from '@/models/entities/UserList.js';
import type { UserListJoining } from '@/models/entities/UserListJoining.js'; import type { MiUserListJoining } from '@/models/entities/UserListJoining.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
@ -35,7 +35,7 @@ export class UserListService {
} }
@bindThis @bindThis
public async push(target: User, list: UserList, me: User) { public async push(target: MiUser, list: MiUserList, me: MiUser) {
const currentCount = await this.userListJoiningsRepository.countBy({ const currentCount = await this.userListJoiningsRepository.countBy({
userListId: list.id, userListId: list.id,
}); });
@ -48,7 +48,7 @@ export class UserListService {
createdAt: new Date(), createdAt: new Date(),
userId: target.id, userId: target.id,
userListId: list.id, userListId: list.id,
} as UserListJoining); } as MiUserListJoining);
this.globalEventService.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target)); this.globalEventService.publishUserListStream(list.id, 'userAdded', await this.userEntityService.pack(target));

View file

@ -5,9 +5,9 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { In } from 'typeorm'; import { In } from 'typeorm';
import type { MutingsRepository, Muting } from '@/models/index.js'; import type { MutingsRepository, MiMuting } from '@/models/index.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { CacheService } from '@/core/CacheService.js'; import { CacheService } from '@/core/CacheService.js';
@ -24,7 +24,7 @@ export class UserMutingService {
} }
@bindThis @bindThis
public async mute(user: User, target: User, expiresAt: Date | null = null): Promise<void> { public async mute(user: MiUser, target: MiUser, expiresAt: Date | null = null): Promise<void> {
await this.mutingsRepository.insert({ await this.mutingsRepository.insert({
id: this.idService.genId(), id: this.idService.genId(),
createdAt: new Date(), createdAt: new Date(),
@ -37,7 +37,7 @@ export class UserMutingService {
} }
@bindThis @bindThis
public async unmute(mutings: Muting[]): Promise<void> { public async unmute(mutings: MiMuting[]): Promise<void> {
if (mutings.length === 0) return; if (mutings.length === 0) return;
await this.mutingsRepository.delete({ await this.mutingsRepository.delete({

View file

@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { Not, IsNull } from 'typeorm'; import { Not, IsNull } from 'typeorm';
import type { FollowingsRepository } from '@/models/index.js'; import type { FollowingsRepository } from '@/models/index.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js'; import { GlobalEventService } from '@/core/GlobalEventService.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
@ -28,7 +28,7 @@ export class UserSuspendService {
} }
@bindThis @bindThis
public async doPostSuspend(user: { id: User['id']; host: User['host'] }): Promise<void> { public async doPostSuspend(user: { id: MiUser['id']; host: MiUser['host'] }): Promise<void> {
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true }); this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: true });
if (this.userEntityService.isLocalUser(user)) { if (this.userEntityService.isLocalUser(user)) {
@ -58,7 +58,7 @@ export class UserSuspendService {
} }
@bindThis @bindThis
public async doPostUnsuspend(user: User): Promise<void> { public async doPostUnsuspend(user: MiUser): Promise<void> {
this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false }); this.globalEventService.publishInternalEvent('userChangeSuspendedState', { id: user.id, isSuspended: false });
if (this.userEntityService.isLocalUser(user)) { if (this.userEntityService.isLocalUser(user)) {

View file

@ -6,7 +6,7 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import * as Redis from 'ioredis'; import * as Redis from 'ioredis';
import type { WebhooksRepository } from '@/models/index.js'; import type { WebhooksRepository } from '@/models/index.js';
import type { Webhook } from '@/models/entities/Webhook.js'; import type { MiWebhook } from '@/models/entities/Webhook.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { StreamMessages } from '@/server/api/stream/types.js'; import { StreamMessages } from '@/server/api/stream/types.js';
@ -15,7 +15,7 @@ import type { OnApplicationShutdown } from '@nestjs/common';
@Injectable() @Injectable()
export class WebhookService implements OnApplicationShutdown { export class WebhookService implements OnApplicationShutdown {
private webhooksFetched = false; private webhooksFetched = false;
private webhooks: Webhook[] = []; private webhooks: MiWebhook[] = [];
constructor( constructor(
@Inject(DI.redisForSub) @Inject(DI.redisForSub)

View file

@ -5,7 +5,7 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import promiseLimit from 'promise-limit'; import promiseLimit from 'promise-limit';
import type { RemoteUser, User } from '@/models/entities/User.js'; import type { MiRemoteUser, MiUser } from '@/models/entities/User.js';
import { concat, unique } from '@/misc/prelude/array.js'; import { concat, unique } from '@/misc/prelude/array.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { getApIds } from './type.js'; import { getApIds } from './type.js';
@ -17,8 +17,8 @@ type Visibility = 'public' | 'home' | 'followers' | 'specified';
type AudienceInfo = { type AudienceInfo = {
visibility: Visibility, visibility: Visibility,
mentionedUsers: User[], mentionedUsers: MiUser[],
visibleUsers: User[], visibleUsers: MiUser[],
}; };
type GroupedAudience = Record<'public' | 'followers' | 'other', string[]>; type GroupedAudience = Record<'public' | 'followers' | 'other', string[]>;
@ -31,16 +31,16 @@ export class ApAudienceService {
} }
@bindThis @bindThis
public async parseAudience(actor: RemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> { public async parseAudience(actor: MiRemoteUser, to?: ApObject, cc?: ApObject, resolver?: Resolver): Promise<AudienceInfo> {
const toGroups = this.groupingAudience(getApIds(to), actor); const toGroups = this.groupingAudience(getApIds(to), actor);
const ccGroups = this.groupingAudience(getApIds(cc), actor); const ccGroups = this.groupingAudience(getApIds(cc), actor);
const others = unique(concat([toGroups.other, ccGroups.other])); const others = unique(concat([toGroups.other, ccGroups.other]));
const limit = promiseLimit<User | null>(2); const limit = promiseLimit<MiUser | null>(2);
const mentionedUsers = (await Promise.all( const mentionedUsers = (await Promise.all(
others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))), others.map(id => limit(() => this.apPersonService.resolvePerson(id, resolver).catch(() => null))),
)).filter((x): x is User => x != null); )).filter((x): x is MiUser => x != null);
if (toGroups.public.length > 0) { if (toGroups.public.length > 0) {
return { return {
@ -74,7 +74,7 @@ export class ApAudienceService {
} }
@bindThis @bindThis
private groupingAudience(ids: string[], actor: RemoteUser): GroupedAudience { private groupingAudience(ids: string[], actor: MiRemoteUser): GroupedAudience {
const groups: GroupedAudience = { const groups: GroupedAudience = {
public: [], public: [],
followers: [], followers: [],
@ -106,7 +106,7 @@ export class ApAudienceService {
} }
@bindThis @bindThis
private isFollowers(id: string, actor: RemoteUser): boolean { private isFollowers(id: string, actor: MiRemoteUser): boolean {
return id === (actor.followersUri ?? `${actor.uri}/followers`); return id === (actor.followersUri ?? `${actor.uri}/followers`);
} }
} }

View file

@ -8,11 +8,11 @@ import { DI } from '@/di-symbols.js';
import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; import type { NotesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import { MemoryKVCache } from '@/misc/cache.js'; import { MemoryKVCache } from '@/misc/cache.js';
import type { UserPublickey } from '@/models/entities/UserPublickey.js'; import type { MiUserPublickey } from '@/models/entities/UserPublickey.js';
import { CacheService } from '@/core/CacheService.js'; import { CacheService } from '@/core/CacheService.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { LocalUser, RemoteUser } from '@/models/entities/User.js'; import { MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
import { getApId } from './type.js'; import { getApId } from './type.js';
import { ApPersonService } from './models/ApPersonService.js'; import { ApPersonService } from './models/ApPersonService.js';
import type { IObject } from './type.js'; import type { IObject } from './type.js';
@ -35,8 +35,8 @@ export type UriParseResult = {
@Injectable() @Injectable()
export class ApDbResolverService implements OnApplicationShutdown { export class ApDbResolverService implements OnApplicationShutdown {
private publicKeyCache: MemoryKVCache<UserPublickey | null>; private publicKeyCache: MemoryKVCache<MiUserPublickey | null>;
private publicKeyByUserIdCache: MemoryKVCache<UserPublickey | null>; private publicKeyByUserIdCache: MemoryKVCache<MiUserPublickey | null>;
constructor( constructor(
@Inject(DI.config) @Inject(DI.config)
@ -54,8 +54,8 @@ export class ApDbResolverService implements OnApplicationShutdown {
private cacheService: CacheService, private cacheService: CacheService,
private apPersonService: ApPersonService, private apPersonService: ApPersonService,
) { ) {
this.publicKeyCache = new MemoryKVCache<UserPublickey | null>(Infinity); this.publicKeyCache = new MemoryKVCache<MiUserPublickey | null>(Infinity);
this.publicKeyByUserIdCache = new MemoryKVCache<UserPublickey | null>(Infinity); this.publicKeyByUserIdCache = new MemoryKVCache<MiUserPublickey | null>(Infinity);
} }
@bindThis @bindThis
@ -78,7 +78,7 @@ export class ApDbResolverService implements OnApplicationShutdown {
* AP Note => Misskey Note in DB * AP Note => Misskey Note in DB
*/ */
@bindThis @bindThis
public async getNoteFromApId(value: string | IObject): Promise<Note | null> { public async getNoteFromApId(value: string | IObject): Promise<MiNote | null> {
const parsed = this.parseUri(value); const parsed = this.parseUri(value);
if (parsed.local) { if (parsed.local) {
@ -98,7 +98,7 @@ export class ApDbResolverService implements OnApplicationShutdown {
* AP Person => Misskey User in DB * AP Person => Misskey User in DB
*/ */
@bindThis @bindThis
public async getUserFromApId(value: string | IObject): Promise<LocalUser | RemoteUser | null> { public async getUserFromApId(value: string | IObject): Promise<MiLocalUser | MiRemoteUser | null> {
const parsed = this.parseUri(value); const parsed = this.parseUri(value);
if (parsed.local) { if (parsed.local) {
@ -107,12 +107,12 @@ export class ApDbResolverService implements OnApplicationShutdown {
return await this.cacheService.userByIdCache.fetchMaybe( return await this.cacheService.userByIdCache.fetchMaybe(
parsed.id, parsed.id,
() => this.usersRepository.findOneBy({ id: parsed.id }).then(x => x ?? undefined), () => this.usersRepository.findOneBy({ id: parsed.id }).then(x => x ?? undefined),
) as LocalUser | undefined ?? null; ) as MiLocalUser | undefined ?? null;
} else { } else {
return await this.cacheService.uriPersonCache.fetch( return await this.cacheService.uriPersonCache.fetch(
parsed.uri, parsed.uri,
() => this.usersRepository.findOneBy({ uri: parsed.uri }), () => this.usersRepository.findOneBy({ uri: parsed.uri }),
) as RemoteUser | null; ) as MiRemoteUser | null;
} }
} }
@ -121,8 +121,8 @@ export class ApDbResolverService implements OnApplicationShutdown {
*/ */
@bindThis @bindThis
public async getAuthUserFromKeyId(keyId: string): Promise<{ public async getAuthUserFromKeyId(keyId: string): Promise<{
user: RemoteUser; user: MiRemoteUser;
key: UserPublickey; key: MiUserPublickey;
} | null> { } | null> {
const key = await this.publicKeyCache.fetch(keyId, async () => { const key = await this.publicKeyCache.fetch(keyId, async () => {
const key = await this.userPublickeysRepository.findOneBy({ const key = await this.userPublickeysRepository.findOneBy({
@ -137,7 +137,7 @@ export class ApDbResolverService implements OnApplicationShutdown {
if (key == null) return null; if (key == null) return null;
return { return {
user: await this.cacheService.findUserById(key.userId) as RemoteUser, user: await this.cacheService.findUserById(key.userId) as MiRemoteUser,
key, key,
}; };
} }
@ -147,10 +147,10 @@ export class ApDbResolverService implements OnApplicationShutdown {
*/ */
@bindThis @bindThis
public async getAuthUserFromApId(uri: string): Promise<{ public async getAuthUserFromApId(uri: string): Promise<{
user: RemoteUser; user: MiRemoteUser;
key: UserPublickey | null; key: MiUserPublickey | null;
} | null> { } | null> {
const user = await this.apPersonService.resolvePerson(uri) as RemoteUser; const user = await this.apPersonService.resolvePerson(uri) as MiRemoteUser;
const key = await this.publicKeyByUserIdCache.fetch( const key = await this.publicKeyByUserIdCache.fetch(
user.id, user.id,

View file

@ -7,7 +7,7 @@ import { Inject, Injectable } from '@nestjs/common';
import { IsNull, Not } from 'typeorm'; import { IsNull, Not } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { FollowingsRepository } from '@/models/index.js'; import type { FollowingsRepository } from '@/models/index.js';
import type { LocalUser, RemoteUser, User } from '@/models/entities/User.js'; import type { MiLocalUser, MiRemoteUser, MiUser } from '@/models/entities/User.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
@ -24,7 +24,7 @@ interface IFollowersRecipe extends IRecipe {
interface IDirectRecipe extends IRecipe { interface IDirectRecipe extends IRecipe {
type: 'Direct'; type: 'Direct';
to: RemoteUser; to: MiRemoteUser;
} }
const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe => const isFollowers = (recipe: IRecipe): recipe is IFollowersRecipe =>
@ -51,7 +51,7 @@ class DeliverManager {
private followingsRepository: FollowingsRepository, private followingsRepository: FollowingsRepository,
private queueService: QueueService, private queueService: QueueService,
actor: { id: User['id']; host: null; }, actor: { id: MiUser['id']; host: null; },
activity: IActivity | null, activity: IActivity | null,
) { ) {
// 型で弾いてはいるが一応ローカルユーザーかチェック // 型で弾いてはいるが一応ローカルユーザーかチェック
@ -82,7 +82,7 @@ class DeliverManager {
* @param to To * @param to To
*/ */
@bindThis @bindThis
public addDirectRecipe(to: RemoteUser): void { public addDirectRecipe(to: MiRemoteUser): void {
const recipe: IDirectRecipe = { const recipe: IDirectRecipe = {
type: 'Direct', type: 'Direct',
to, to,
@ -165,7 +165,7 @@ export class ApDeliverManagerService {
* @param activity Activity * @param activity Activity
*/ */
@bindThis @bindThis
public async deliverToFollowers(actor: { id: LocalUser['id']; host: null; }, activity: IActivity): Promise<void> { public async deliverToFollowers(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity): Promise<void> {
const manager = new DeliverManager( const manager = new DeliverManager(
this.userEntityService, this.userEntityService,
this.followingsRepository, this.followingsRepository,
@ -184,7 +184,7 @@ export class ApDeliverManagerService {
* @param to Target user * @param to Target user
*/ */
@bindThis @bindThis
public async deliverToUser(actor: { id: LocalUser['id']; host: null; }, activity: IActivity, to: RemoteUser): Promise<void> { public async deliverToUser(actor: { id: MiLocalUser['id']; host: null; }, activity: IActivity, to: MiRemoteUser): Promise<void> {
const manager = new DeliverManager( const manager = new DeliverManager(
this.userEntityService, this.userEntityService,
this.followingsRepository, this.followingsRepository,
@ -197,7 +197,7 @@ export class ApDeliverManagerService {
} }
@bindThis @bindThis
public createDeliverManager(actor: { id: User['id']; host: null; }, activity: IActivity | null): DeliverManager { public createDeliverManager(actor: { id: MiUser['id']; host: null; }, activity: IActivity | null): DeliverManager {
return new DeliverManager( return new DeliverManager(
this.userEntityService, this.userEntityService,
this.followingsRepository, this.followingsRepository,

View file

@ -26,7 +26,7 @@ import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { QueueService } from '@/core/QueueService.js'; import { QueueService } from '@/core/QueueService.js';
import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js'; import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserReportsRepository, FollowRequestsRepository } from '@/models/index.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import type { RemoteUser } from '@/models/entities/User.js'; import type { MiRemoteUser } from '@/models/entities/User.js';
import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js'; import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
import { ApNoteService } from './models/ApNoteService.js'; import { ApNoteService } from './models/ApNoteService.js';
import { ApLoggerService } from './ApLoggerService.js'; import { ApLoggerService } from './ApLoggerService.js';
@ -87,7 +87,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
public async performActivity(actor: RemoteUser, activity: IObject): Promise<void> { public async performActivity(actor: MiRemoteUser, activity: IObject): Promise<void> {
if (isCollectionOrOrderedCollection(activity)) { if (isCollectionOrOrderedCollection(activity)) {
const resolver = this.apResolverService.createResolver(); const resolver = this.apResolverService.createResolver();
for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) { for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) {
@ -115,7 +115,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
public async performOneActivity(actor: RemoteUser, activity: IObject): Promise<void> { public async performOneActivity(actor: MiRemoteUser, activity: IObject): Promise<void> {
if (actor.isSuspended) return; if (actor.isSuspended) return;
if (isCreate(activity)) { if (isCreate(activity)) {
@ -152,7 +152,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async follow(actor: RemoteUser, activity: IFollow): Promise<string> { private async follow(actor: MiRemoteUser, activity: IFollow): Promise<string> {
const followee = await this.apDbResolverService.getUserFromApId(activity.object); const followee = await this.apDbResolverService.getUserFromApId(activity.object);
if (followee == null) { if (followee == null) {
@ -169,7 +169,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async like(actor: RemoteUser, activity: ILike): Promise<string> { private async like(actor: MiRemoteUser, activity: ILike): Promise<string> {
const targetUri = getApId(activity.object); const targetUri = getApId(activity.object);
const note = await this.apNoteService.fetchNote(targetUri); const note = await this.apNoteService.fetchNote(targetUri);
@ -187,7 +187,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async accept(actor: RemoteUser, activity: IAccept): Promise<string> { private async accept(actor: MiRemoteUser, activity: IAccept): Promise<string> {
const uri = activity.id ?? activity; const uri = activity.id ?? activity;
this.logger.info(`Accept: ${uri}`); this.logger.info(`Accept: ${uri}`);
@ -205,7 +205,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async acceptFollow(actor: RemoteUser, activity: IFollow): Promise<string> { private async acceptFollow(actor: MiRemoteUser, activity: IFollow): Promise<string> {
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
const follower = await this.apDbResolverService.getUserFromApId(activity.actor); const follower = await this.apDbResolverService.getUserFromApId(activity.actor);
@ -229,7 +229,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async add(actor: RemoteUser, activity: IAdd): Promise<void> { private async add(actor: MiRemoteUser, activity: IAdd): Promise<void> {
if (actor.uri !== activity.actor) { if (actor.uri !== activity.actor) {
throw new Error('invalid actor'); throw new Error('invalid actor');
} }
@ -249,7 +249,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async announce(actor: RemoteUser, activity: IAnnounce): Promise<void> { private async announce(actor: MiRemoteUser, activity: IAnnounce): Promise<void> {
const uri = getApId(activity); const uri = getApId(activity);
this.logger.info(`Announce: ${uri}`); this.logger.info(`Announce: ${uri}`);
@ -260,7 +260,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async announceNote(actor: RemoteUser, activity: IAnnounce, targetUri: string): Promise<void> { private async announceNote(actor: MiRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
const uri = getApId(activity); const uri = getApId(activity);
if (actor.isSuspended) { if (actor.isSuspended) {
@ -320,7 +320,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async block(actor: RemoteUser, activity: IBlock): Promise<string> { private async block(actor: MiRemoteUser, activity: IBlock): Promise<string> {
// ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず // ※ activity.objectにブロック対象があり、それは存在するローカルユーザーのはず
const blockee = await this.apDbResolverService.getUserFromApId(activity.object); const blockee = await this.apDbResolverService.getUserFromApId(activity.object);
@ -338,7 +338,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async create(actor: RemoteUser, activity: ICreate): Promise<void> { private async create(actor: MiRemoteUser, activity: ICreate): Promise<void> {
const uri = getApId(activity); const uri = getApId(activity);
this.logger.info(`Create: ${uri}`); this.logger.info(`Create: ${uri}`);
@ -374,7 +374,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async createNote(resolver: Resolver, actor: RemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> { private async createNote(resolver: Resolver, actor: MiRemoteUser, note: IObject, silent = false, activity?: ICreate): Promise<string> {
const uri = getApId(note); const uri = getApId(note);
if (typeof note === 'object') { if (typeof note === 'object') {
@ -409,7 +409,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async delete(actor: RemoteUser, activity: IDelete): Promise<string> { private async delete(actor: MiRemoteUser, activity: IDelete): Promise<string> {
if (actor.uri !== activity.actor) { if (actor.uri !== activity.actor) {
throw new Error('invalid actor'); throw new Error('invalid actor');
} }
@ -451,7 +451,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async deleteActor(actor: RemoteUser, uri: string): Promise<string> { private async deleteActor(actor: MiRemoteUser, uri: string): Promise<string> {
this.logger.info(`Deleting the Actor: ${uri}`); this.logger.info(`Deleting the Actor: ${uri}`);
if (actor.uri !== uri) { if (actor.uri !== uri) {
@ -475,7 +475,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async deleteNote(actor: RemoteUser, uri: string): Promise<string> { private async deleteNote(actor: MiRemoteUser, uri: string): Promise<string> {
this.logger.info(`Deleting the Note: ${uri}`); this.logger.info(`Deleting the Note: ${uri}`);
const unlock = await this.appLockService.getApLock(uri); const unlock = await this.appLockService.getApLock(uri);
@ -499,7 +499,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async flag(actor: RemoteUser, activity: IFlag): Promise<string> { private async flag(actor: MiRemoteUser, activity: IFlag): Promise<string> {
// objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので // objectは `(User|Note) | (User|Note)[]` だけど、全パターンDBスキーマと対応させられないので
// 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する // 対象ユーザーは一番最初のユーザー として あとはコメントとして格納する
const uris = getApIds(activity.object); const uris = getApIds(activity.object);
@ -527,7 +527,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async reject(actor: RemoteUser, activity: IReject): Promise<string> { private async reject(actor: MiRemoteUser, activity: IReject): Promise<string> {
const uri = activity.id ?? activity; const uri = activity.id ?? activity;
this.logger.info(`Reject: ${uri}`); this.logger.info(`Reject: ${uri}`);
@ -545,7 +545,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async rejectFollow(actor: RemoteUser, activity: IFollow): Promise<string> { private async rejectFollow(actor: MiRemoteUser, activity: IFollow): Promise<string> {
// ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある // ※ activityはこっちから投げたフォローリクエストなので、activity.actorは存在するローカルユーザーである必要がある
const follower = await this.apDbResolverService.getUserFromApId(activity.actor); const follower = await this.apDbResolverService.getUserFromApId(activity.actor);
@ -569,7 +569,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async remove(actor: RemoteUser, activity: IRemove): Promise<void> { private async remove(actor: MiRemoteUser, activity: IRemove): Promise<void> {
if (actor.uri !== activity.actor) { if (actor.uri !== activity.actor) {
throw new Error('invalid actor'); throw new Error('invalid actor');
} }
@ -589,7 +589,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async undo(actor: RemoteUser, activity: IUndo): Promise<string> { private async undo(actor: MiRemoteUser, activity: IUndo): Promise<string> {
if (actor.uri !== activity.actor) { if (actor.uri !== activity.actor) {
throw new Error('invalid actor'); throw new Error('invalid actor');
} }
@ -616,7 +616,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async undoAccept(actor: RemoteUser, activity: IAccept): Promise<string> { private async undoAccept(actor: MiRemoteUser, activity: IAccept): Promise<string> {
const follower = await this.apDbResolverService.getUserFromApId(activity.object); const follower = await this.apDbResolverService.getUserFromApId(activity.object);
if (follower == null) { if (follower == null) {
return 'skip: follower not found'; return 'skip: follower not found';
@ -638,7 +638,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async undoAnnounce(actor: RemoteUser, activity: IAnnounce): Promise<string> { private async undoAnnounce(actor: MiRemoteUser, activity: IAnnounce): Promise<string> {
const uri = getApId(activity); const uri = getApId(activity);
const note = await this.notesRepository.findOneBy({ const note = await this.notesRepository.findOneBy({
@ -653,7 +653,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async undoBlock(actor: RemoteUser, activity: IBlock): Promise<string> { private async undoBlock(actor: MiRemoteUser, activity: IBlock): Promise<string> {
const blockee = await this.apDbResolverService.getUserFromApId(activity.object); const blockee = await this.apDbResolverService.getUserFromApId(activity.object);
if (blockee == null) { if (blockee == null) {
@ -669,7 +669,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async undoFollow(actor: RemoteUser, activity: IFollow): Promise<string> { private async undoFollow(actor: MiRemoteUser, activity: IFollow): Promise<string> {
const followee = await this.apDbResolverService.getUserFromApId(activity.object); const followee = await this.apDbResolverService.getUserFromApId(activity.object);
if (followee == null) { if (followee == null) {
return 'skip: followee not found'; return 'skip: followee not found';
@ -707,7 +707,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async undoLike(actor: RemoteUser, activity: ILike): Promise<string> { private async undoLike(actor: MiRemoteUser, activity: ILike): Promise<string> {
const targetUri = getApId(activity.object); const targetUri = getApId(activity.object);
const note = await this.apNoteService.fetchNote(targetUri); const note = await this.apNoteService.fetchNote(targetUri);
@ -722,7 +722,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async update(actor: RemoteUser, activity: IUpdate): Promise<string> { private async update(actor: MiRemoteUser, activity: IUpdate): Promise<string> {
if (actor.uri !== activity.actor) { if (actor.uri !== activity.actor) {
return 'skip: invalid actor'; return 'skip: invalid actor';
} }
@ -748,7 +748,7 @@ export class ApInboxService {
} }
@bindThis @bindThis
private async move(actor: RemoteUser, activity: IMove): Promise<string> { private async move(actor: MiRemoteUser, activity: IMove): Promise<string> {
// fetch the new and old accounts // fetch the new and old accounts
const targetUri = getApHrefNullable(activity.target); const targetUri = getApHrefNullable(activity.target);
if (!targetUri) return 'skip: invalid activity target'; if (!targetUri) return 'skip: invalid activity target';

View file

@ -6,7 +6,7 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import { MfmService } from '@/core/MfmService.js'; import { MfmService } from '@/core/MfmService.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { extractApHashtagObjects } from './models/tag.js'; import { extractApHashtagObjects } from './models/tag.js';
import type { IObject } from './type.js'; import type { IObject } from './type.js';
@ -25,7 +25,7 @@ export class ApMfmService {
} }
@bindThis @bindThis
public getNoteHtml(note: Note): string | null { public getNoteHtml(note: MiNote): string | null {
if (!note.text) return ''; if (!note.text) return '';
return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers)); return this.mfmService.toHtml(mfm.parse(note.text), JSON.parse(note.mentionedRemoteUsers));
} }

View file

@ -9,20 +9,20 @@ import { In } from 'typeorm';
import * as mfm from 'mfm-js'; import * as mfm from 'mfm-js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type { PartialLocalUser, LocalUser, PartialRemoteUser, RemoteUser, User } from '@/models/entities/User.js'; import type { MiPartialLocalUser, MiLocalUser, MiPartialRemoteUser, MiRemoteUser, MiUser } from '@/models/entities/User.js';
import type { IMentionedRemoteUsers, Note } from '@/models/entities/Note.js'; import type { IMentionedRemoteUsers, MiNote } from '@/models/entities/Note.js';
import type { Blocking } from '@/models/entities/Blocking.js'; import type { MiBlocking } from '@/models/entities/Blocking.js';
import type { Relay } from '@/models/entities/Relay.js'; import type { MiRelay } from '@/models/entities/Relay.js';
import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { MiDriveFile } from '@/models/entities/DriveFile.js';
import type { NoteReaction } from '@/models/entities/NoteReaction.js'; import type { MiNoteReaction } from '@/models/entities/NoteReaction.js';
import type { Emoji } from '@/models/entities/Emoji.js'; import type { MiEmoji } from '@/models/entities/Emoji.js';
import type { Poll } from '@/models/entities/Poll.js'; import type { MiPoll } from '@/models/entities/Poll.js';
import type { PollVote } from '@/models/entities/PollVote.js'; import type { MiPollVote } from '@/models/entities/PollVote.js';
import { UserKeypairService } from '@/core/UserKeypairService.js'; import { UserKeypairService } from '@/core/UserKeypairService.js';
import { MfmService } from '@/core/MfmService.js'; import { MfmService } from '@/core/MfmService.js';
import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js';
import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js'; import { DriveFileEntityService } from '@/core/entities/DriveFileEntityService.js';
import type { UserKeypair } from '@/models/entities/UserKeypair.js'; import type { MiUserKeypair } from '@/models/entities/UserKeypair.js';
import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository } from '@/models/index.js'; import type { UsersRepository, UserProfilesRepository, NotesRepository, DriveFilesRepository, PollsRepository } from '@/models/index.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { CustomEmojiService } from '@/core/CustomEmojiService.js'; import { CustomEmojiService } from '@/core/CustomEmojiService.js';
@ -63,7 +63,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderAccept(object: string | IObject, user: { id: User['id']; host: null }): IAccept { public renderAccept(object: string | IObject, user: { id: MiUser['id']; host: null }): IAccept {
return { return {
type: 'Accept', type: 'Accept',
actor: this.userEntityService.genLocalUserUri(user.id), actor: this.userEntityService.genLocalUserUri(user.id),
@ -72,7 +72,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderAdd(user: LocalUser, target: string | IObject | undefined, object: string | IObject): IAdd { public renderAdd(user: MiLocalUser, target: string | IObject | undefined, object: string | IObject): IAdd {
return { return {
type: 'Add', type: 'Add',
actor: this.userEntityService.genLocalUserUri(user.id), actor: this.userEntityService.genLocalUserUri(user.id),
@ -82,7 +82,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderAnnounce(object: string | IObject, note: Note): IAnnounce { public renderAnnounce(object: string | IObject, note: MiNote): IAnnounce {
const attributedTo = this.userEntityService.genLocalUserUri(note.userId); const attributedTo = this.userEntityService.genLocalUserUri(note.userId);
let to: string[] = []; let to: string[] = [];
@ -118,7 +118,7 @@ export class ApRendererService {
* @param block The block to be rendered. The blockee relation must be loaded. * @param block The block to be rendered. The blockee relation must be loaded.
*/ */
@bindThis @bindThis
public renderBlock(block: Blocking): IBlock { public renderBlock(block: MiBlocking): IBlock {
if (block.blockee?.uri == null) { if (block.blockee?.uri == null) {
throw new Error('renderBlock: missing blockee uri'); throw new Error('renderBlock: missing blockee uri');
} }
@ -132,7 +132,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderCreate(object: IObject, note: Note): ICreate { public renderCreate(object: IObject, note: MiNote): ICreate {
const activity: ICreate = { const activity: ICreate = {
id: `${this.config.url}/notes/${note.id}/activity`, id: `${this.config.url}/notes/${note.id}/activity`,
actor: this.userEntityService.genLocalUserUri(note.userId), actor: this.userEntityService.genLocalUserUri(note.userId),
@ -148,7 +148,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderDelete(object: IObject | string, user: { id: User['id']; host: null }): IDelete { public renderDelete(object: IObject | string, user: { id: MiUser['id']; host: null }): IDelete {
return { return {
type: 'Delete', type: 'Delete',
actor: this.userEntityService.genLocalUserUri(user.id), actor: this.userEntityService.genLocalUserUri(user.id),
@ -158,7 +158,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderDocument(file: DriveFile): IApDocument { public renderDocument(file: MiDriveFile): IApDocument {
return { return {
type: 'Document', type: 'Document',
mediaType: file.webpublicType ?? file.type, mediaType: file.webpublicType ?? file.type,
@ -168,7 +168,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderEmoji(emoji: Emoji): IApEmoji { public renderEmoji(emoji: MiEmoji): IApEmoji {
return { return {
id: `${this.config.url}/emojis/${emoji.name}`, id: `${this.config.url}/emojis/${emoji.name}`,
type: 'Emoji', type: 'Emoji',
@ -185,7 +185,7 @@ export class ApRendererService {
// to anonymise reporters, the reporting actor must be a system user // to anonymise reporters, the reporting actor must be a system user
@bindThis @bindThis
public renderFlag(user: LocalUser, object: IObject | string, content: string): IFlag { public renderFlag(user: MiLocalUser, object: IObject | string, content: string): IFlag {
return { return {
type: 'Flag', type: 'Flag',
actor: this.userEntityService.genLocalUserUri(user.id), actor: this.userEntityService.genLocalUserUri(user.id),
@ -195,7 +195,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderFollowRelay(relay: Relay, relayActor: LocalUser): IFollow { public renderFollowRelay(relay: MiRelay, relayActor: MiLocalUser): IFollow {
return { return {
id: `${this.config.url}/activities/follow-relay/${relay.id}`, id: `${this.config.url}/activities/follow-relay/${relay.id}`,
type: 'Follow', type: 'Follow',
@ -209,15 +209,15 @@ export class ApRendererService {
* @param id Follower|Followee ID * @param id Follower|Followee ID
*/ */
@bindThis @bindThis
public async renderFollowUser(id: User['id']): Promise<string> { public async renderFollowUser(id: MiUser['id']): Promise<string> {
const user = await this.usersRepository.findOneByOrFail({ id: id }) as PartialLocalUser | PartialRemoteUser; const user = await this.usersRepository.findOneByOrFail({ id: id }) as MiPartialLocalUser | MiPartialRemoteUser;
return this.userEntityService.getUserUri(user); return this.userEntityService.getUserUri(user);
} }
@bindThis @bindThis
public renderFollow( public renderFollow(
follower: PartialLocalUser | PartialRemoteUser, follower: MiPartialLocalUser | MiPartialRemoteUser,
followee: PartialLocalUser | PartialRemoteUser, followee: MiPartialLocalUser | MiPartialRemoteUser,
requestId?: string, requestId?: string,
): IFollow { ): IFollow {
return { return {
@ -238,7 +238,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderImage(file: DriveFile): IApImage { public renderImage(file: MiDriveFile): IApImage {
return { return {
type: 'Image', type: 'Image',
url: this.driveFileEntityService.getPublicUrl(file), url: this.driveFileEntityService.getPublicUrl(file),
@ -248,7 +248,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderKey(user: LocalUser, key: UserKeypair, postfix?: string): IKey { public renderKey(user: MiLocalUser, key: MiUserKeypair, postfix?: string): IKey {
return { return {
id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`, id: `${this.config.url}/users/${user.id}${postfix ?? '/publickey'}`,
type: 'Key', type: 'Key',
@ -261,7 +261,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public async renderLike(noteReaction: NoteReaction, note: { uri: string | null }): Promise<ILike> { public async renderLike(noteReaction: MiNoteReaction, note: { uri: string | null }): Promise<ILike> {
const reaction = noteReaction.reaction; const reaction = noteReaction.reaction;
const object: ILike = { const object: ILike = {
@ -284,18 +284,18 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderMention(mention: PartialLocalUser | PartialRemoteUser): IApMention { public renderMention(mention: MiPartialLocalUser | MiPartialRemoteUser): IApMention {
return { return {
type: 'Mention', type: 'Mention',
href: this.userEntityService.getUserUri(mention), href: this.userEntityService.getUserUri(mention),
name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as LocalUser).username}`, name: this.userEntityService.isRemoteUser(mention) ? `@${mention.username}@${mention.host}` : `@${(mention as MiLocalUser).username}`,
}; };
} }
@bindThis @bindThis
public renderMove( public renderMove(
src: PartialLocalUser | PartialRemoteUser, src: MiPartialLocalUser | MiPartialRemoteUser,
dst: PartialLocalUser | PartialRemoteUser, dst: MiPartialLocalUser | MiPartialRemoteUser,
): IMove { ): IMove {
const actor = this.userEntityService.getUserUri(src); const actor = this.userEntityService.getUserUri(src);
const target = this.userEntityService.getUserUri(dst); const target = this.userEntityService.getUserUri(dst);
@ -309,15 +309,15 @@ export class ApRendererService {
} }
@bindThis @bindThis
public async renderNote(note: Note, dive = true): Promise<IPost> { public async renderNote(note: MiNote, dive = true): Promise<IPost> {
const getPromisedFiles = async (ids: string[]): Promise<DriveFile[]> => { const getPromisedFiles = async (ids: string[]): Promise<MiDriveFile[]> => {
if (ids.length === 0) return []; if (ids.length === 0) return [];
const items = await this.driveFilesRepository.findBy({ id: In(ids) }); const items = await this.driveFilesRepository.findBy({ id: In(ids) });
return ids.map(id => items.find(item => item.id === id)).filter((item): item is DriveFile => item != null); return ids.map(id => items.find(item => item.id === id)).filter((item): item is MiDriveFile => item != null);
}; };
let inReplyTo; let inReplyTo;
let inReplyToNote: Note | null; let inReplyToNote: MiNote | null;
if (note.replyId) { if (note.replyId) {
inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId }); inReplyToNote = await this.notesRepository.findOneBy({ id: note.replyId });
@ -376,12 +376,12 @@ export class ApRendererService {
}) : []; }) : [];
const hashtagTags = note.tags.map(tag => this.renderHashtag(tag)); const hashtagTags = note.tags.map(tag => this.renderHashtag(tag));
const mentionTags = mentionedUsers.map(u => this.renderMention(u as LocalUser | RemoteUser)); const mentionTags = mentionedUsers.map(u => this.renderMention(u as MiLocalUser | MiRemoteUser));
const files = await getPromisedFiles(note.fileIds); const files = await getPromisedFiles(note.fileIds);
const text = note.text ?? ''; const text = note.text ?? '';
let poll: Poll | null = null; let poll: MiPoll | null = null;
if (note.hasPoll) { if (note.hasPoll) {
poll = await this.pollsRepository.findOneBy({ noteId: note.id }); poll = await this.pollsRepository.findOneBy({ noteId: note.id });
@ -449,7 +449,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public async renderPerson(user: LocalUser) { public async renderPerson(user: MiLocalUser) {
const id = this.userEntityService.genLocalUserUri(user.id); const id = this.userEntityService.genLocalUserUri(user.id);
const isSystem = user.username.includes('.'); const isSystem = user.username.includes('.');
@ -523,7 +523,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderQuestion(user: { id: User['id'] }, note: Note, poll: Poll): IQuestion { public renderQuestion(user: { id: MiUser['id'] }, note: MiNote, poll: MiPoll): IQuestion {
return { return {
type: 'Question', type: 'Question',
id: `${this.config.url}/questions/${note.id}`, id: `${this.config.url}/questions/${note.id}`,
@ -541,7 +541,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderReject(object: string | IObject, user: { id: User['id'] }): IReject { public renderReject(object: string | IObject, user: { id: MiUser['id'] }): IReject {
return { return {
type: 'Reject', type: 'Reject',
actor: this.userEntityService.genLocalUserUri(user.id), actor: this.userEntityService.genLocalUserUri(user.id),
@ -550,7 +550,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderRemove(user: { id: User['id'] }, target: string | IObject | undefined, object: string | IObject): IRemove { public renderRemove(user: { id: MiUser['id'] }, target: string | IObject | undefined, object: string | IObject): IRemove {
return { return {
type: 'Remove', type: 'Remove',
actor: this.userEntityService.genLocalUserUri(user.id), actor: this.userEntityService.genLocalUserUri(user.id),
@ -568,7 +568,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderUndo(object: string | IObject, user: { id: User['id'] }): IUndo { public renderUndo(object: string | IObject, user: { id: MiUser['id'] }): IUndo {
const id = typeof object !== 'string' && typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined; const id = typeof object !== 'string' && typeof object.id === 'string' && object.id.startsWith(this.config.url) ? `${object.id}/undo` : undefined;
return { return {
@ -581,7 +581,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderUpdate(object: string | IObject, user: { id: User['id'] }): IUpdate { public renderUpdate(object: string | IObject, user: { id: MiUser['id'] }): IUpdate {
return { return {
id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`, id: `${this.config.url}/users/${user.id}#updates/${new Date().getTime()}`,
actor: this.userEntityService.genLocalUserUri(user.id), actor: this.userEntityService.genLocalUserUri(user.id),
@ -593,7 +593,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public renderVote(user: { id: User['id'] }, vote: PollVote, note: Note, poll: Poll, pollOwner: RemoteUser): ICreate { public renderVote(user: { id: MiUser['id'] }, vote: MiPollVote, note: MiNote, poll: MiPoll, pollOwner: MiRemoteUser): ICreate {
return { return {
id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`, id: `${this.config.url}/users/${user.id}#votes/${vote.id}/activity`,
actor: this.userEntityService.genLocalUserUri(user.id), actor: this.userEntityService.genLocalUserUri(user.id),
@ -651,7 +651,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
public async attachLdSignature(activity: any, user: { id: User['id']; host: null; }): Promise<IActivity> { public async attachLdSignature(activity: any, user: { id: MiUser['id']; host: null; }): Promise<IActivity> {
const keypair = await this.userKeypairService.getUserKeypair(user.id); const keypair = await this.userKeypairService.getUserKeypair(user.id);
const ldSignature = this.ldSignatureService.use(); const ldSignature = this.ldSignatureService.use();
@ -710,7 +710,7 @@ export class ApRendererService {
} }
@bindThis @bindThis
private async getEmojis(names: string[]): Promise<Emoji[]> { private async getEmojis(names: string[]): Promise<MiEmoji[]> {
if (names.length === 0) return []; if (names.length === 0) return [];
const allEmojis = await this.customEmojiService.localEmojisCache.fetch(); const allEmojis = await this.customEmojiService.localEmojisCache.fetch();

View file

@ -8,7 +8,7 @@ import { URL } from 'node:url';
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { UserKeypairService } from '@/core/UserKeypairService.js'; import { UserKeypairService } from '@/core/UserKeypairService.js';
import { HttpRequestService } from '@/core/HttpRequestService.js'; import { HttpRequestService } from '@/core/HttpRequestService.js';
import { LoggerService } from '@/core/LoggerService.js'; import { LoggerService } from '@/core/LoggerService.js';
@ -145,7 +145,7 @@ export class ApRequestService {
} }
@bindThis @bindThis
public async signedPost(user: { id: User['id'] }, url: string, object: unknown): Promise<void> { public async signedPost(user: { id: MiUser['id'] }, url: string, object: unknown): Promise<void> {
const body = JSON.stringify(object); const body = JSON.stringify(object);
const keypair = await this.userKeypairService.getUserKeypair(user.id); const keypair = await this.userKeypairService.getUserKeypair(user.id);
@ -174,7 +174,7 @@ export class ApRequestService {
* @param url URL to fetch * @param url URL to fetch
*/ */
@bindThis @bindThis
public async signedGet(url: string, user: { id: User['id'] }): Promise<unknown> { public async signedGet(url: string, user: { id: MiUser['id'] }): Promise<unknown> {
const keypair = await this.userKeypairService.getUserKeypair(user.id); const keypair = await this.userKeypairService.getUserKeypair(user.id);
const req = ApRequestCreator.createSignedGet({ const req = ApRequestCreator.createSignedGet({

View file

@ -4,7 +4,7 @@
*/ */
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import type { LocalUser, RemoteUser } from '@/models/entities/User.js'; import type { MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
import { InstanceActorService } from '@/core/InstanceActorService.js'; import { InstanceActorService } from '@/core/InstanceActorService.js';
import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js'; import type { NotesRepository, PollsRepository, NoteReactionsRepository, UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
@ -23,7 +23,7 @@ import type { IObject, ICollection, IOrderedCollection } from './type.js';
export class Resolver { export class Resolver {
private history: Set<string>; private history: Set<string>;
private user?: LocalUser; private user?: MiLocalUser;
private logger: Logger; private logger: Logger;
constructor( constructor(
@ -134,7 +134,7 @@ export class Resolver {
}); });
case 'users': case 'users':
return this.usersRepository.findOneByOrFail({ id: parsed.id }) return this.usersRepository.findOneByOrFail({ id: parsed.id })
.then(user => this.apRendererService.renderPerson(user as LocalUser)); .then(user => this.apRendererService.renderPerson(user as MiLocalUser));
case 'questions': case 'questions':
// Polls are indexed by the note they are attached to. // Polls are indexed by the note they are attached to.
return Promise.all([ return Promise.all([
@ -152,7 +152,7 @@ export class Resolver {
return Promise.all( return Promise.all(
[parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })), [parsed.id, parsed.rest].map(id => this.usersRepository.findOneByOrFail({ id })),
) )
.then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower as LocalUser | RemoteUser, followee as LocalUser | RemoteUser, url))); .then(([follower, followee]) => this.apRendererService.addContext(this.apRendererService.renderFollow(follower as MiLocalUser | MiRemoteUser, followee as MiLocalUser | MiRemoteUser, url)));
default: default:
throw new Error(`resolveLocal: type ${parsed.type} unhandled`); throw new Error(`resolveLocal: type ${parsed.type} unhandled`);
} }

View file

@ -6,8 +6,8 @@
import { Inject, Injectable } from '@nestjs/common'; import { Inject, Injectable } from '@nestjs/common';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { DriveFilesRepository } from '@/models/index.js'; import type { DriveFilesRepository } from '@/models/index.js';
import type { RemoteUser } from '@/models/entities/User.js'; import type { MiRemoteUser } from '@/models/entities/User.js';
import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { MiDriveFile } from '@/models/entities/DriveFile.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { truncate } from '@/misc/truncate.js'; import { truncate } from '@/misc/truncate.js';
import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/const.js'; import { DB_MAX_IMAGE_COMMENT_LENGTH } from '@/const.js';
@ -39,7 +39,7 @@ export class ApImageService {
* Imageを作成します * Imageを作成します
*/ */
@bindThis @bindThis
public async createImage(actor: RemoteUser, value: string | IObject): Promise<DriveFile> { public async createImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> {
// 投稿者が凍結されていたらスキップ // 投稿者が凍結されていたらスキップ
if (actor.isSuspended) { if (actor.isSuspended) {
throw new Error('actor has been suspended'); throw new Error('actor has been suspended');
@ -90,7 +90,7 @@ export class ApImageService {
* Misskeyに登録しそれを返します * Misskeyに登録しそれを返します
*/ */
@bindThis @bindThis
public async resolveImage(actor: RemoteUser, value: string | IObject): Promise<DriveFile> { public async resolveImage(actor: MiRemoteUser, value: string | IObject): Promise<MiDriveFile> {
// TODO // TODO
// リモートサーバーからフェッチしてきて登録 // リモートサーバーからフェッチしてきて登録

View file

@ -5,7 +5,7 @@
import { Injectable } from '@nestjs/common'; import { Injectable } from '@nestjs/common';
import promiseLimit from 'promise-limit'; import promiseLimit from 'promise-limit';
import type { User } from '@/models/index.js'; import type { MiUser } from '@/models/index.js';
import { toArray, unique } from '@/misc/prelude/array.js'; import { toArray, unique } from '@/misc/prelude/array.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import { isMention } from '../type.js'; import { isMention } from '../type.js';
@ -21,13 +21,13 @@ export class ApMentionService {
} }
@bindThis @bindThis
public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise<User[]> { public async extractApMentions(tags: IObject | IObject[] | null | undefined, resolver: Resolver): Promise<MiUser[]> {
const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href)); const hrefs = unique(this.extractApMentionObjects(tags).map(x => x.href));
const limit = promiseLimit<User | null>(2); const limit = promiseLimit<MiUser | null>(2);
const mentionedUsers = (await Promise.all( const mentionedUsers = (await Promise.all(
hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))), hrefs.map(x => limit(() => this.apPersonService.resolvePerson(x, resolver).catch(() => null))),
)).filter((x): x is User => x != null); )).filter((x): x is MiUser => x != null);
return mentionedUsers; return mentionedUsers;
} }

View file

@ -9,13 +9,13 @@ import { In } from 'typeorm';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { PollsRepository, EmojisRepository } from '@/models/index.js'; import type { PollsRepository, EmojisRepository } from '@/models/index.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type { RemoteUser } from '@/models/entities/User.js'; import type { MiRemoteUser } from '@/models/entities/User.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import { toArray, toSingle, unique } from '@/misc/prelude/array.js'; import { toArray, toSingle, unique } from '@/misc/prelude/array.js';
import type { Emoji } from '@/models/entities/Emoji.js'; import type { MiEmoji } from '@/models/entities/Emoji.js';
import { MetaService } from '@/core/MetaService.js'; import { MetaService } from '@/core/MetaService.js';
import { AppLockService } from '@/core/AppLockService.js'; import { AppLockService } from '@/core/AppLockService.js';
import type { DriveFile } from '@/models/entities/DriveFile.js'; import type { MiDriveFile } from '@/models/entities/DriveFile.js';
import { NoteCreateService } from '@/core/NoteCreateService.js'; import { NoteCreateService } from '@/core/NoteCreateService.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
import { IdService } from '@/core/IdService.js'; import { IdService } from '@/core/IdService.js';
@ -101,7 +101,7 @@ export class ApNoteService {
* Misskeyに対象のNoteが登録されていればそれを返します * Misskeyに対象のNoteが登録されていればそれを返します
*/ */
@bindThis @bindThis
public async fetchNote(object: string | IObject): Promise<Note | null> { public async fetchNote(object: string | IObject): Promise<MiNote | null> {
return await this.apDbResolverService.getNoteFromApId(object); return await this.apDbResolverService.getNoteFromApId(object);
} }
@ -109,7 +109,7 @@ export class ApNoteService {
* Noteを作成します * Noteを作成します
*/ */
@bindThis @bindThis
public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<Note | null> { public async createNote(value: string | IObject, resolver?: Resolver, silent = false): Promise<MiNote | null> {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
if (resolver == null) resolver = this.apResolverService.createResolver(); if (resolver == null) resolver = this.apResolverService.createResolver();
@ -147,7 +147,7 @@ export class ApNoteService {
throw new Error('invalid note.attributedTo: ' + note.attributedTo); throw new Error('invalid note.attributedTo: ' + note.attributedTo);
} }
const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as RemoteUser; const actor = await this.apPersonService.resolvePerson(getOneApId(note.attributedTo), resolver) as MiRemoteUser;
// 投稿者が凍結されていたらスキップ // 投稿者が凍結されていたらスキップ
if (actor.isSuspended) { if (actor.isSuspended) {
@ -172,7 +172,7 @@ export class ApNoteService {
// 添付ファイル // 添付ファイル
// TODO: attachmentは必ずしもImageではない // TODO: attachmentは必ずしもImageではない
// TODO: attachmentは必ずしも配列ではない // TODO: attachmentは必ずしも配列ではない
const limit = promiseLimit<DriveFile>(2); const limit = promiseLimit<MiDriveFile>(2);
const files = (await Promise.all(toArray(note.attachment).map(attach => ( const files = (await Promise.all(toArray(note.attachment).map(attach => (
limit(() => this.apImageService.resolveImage(actor, { limit(() => this.apImageService.resolveImage(actor, {
...attach, ...attach,
@ -181,7 +181,7 @@ export class ApNoteService {
)))); ))));
// リプライ // リプライ
const reply: Note | null = note.inReplyTo const reply: MiNote | null = note.inReplyTo
? await this.resolveNote(note.inReplyTo, { resolver }) ? await this.resolveNote(note.inReplyTo, { resolver })
.then(x => { .then(x => {
if (x == null) { if (x == null) {
@ -198,11 +198,11 @@ export class ApNoteService {
: null; : null;
// 引用 // 引用
let quote: Note | undefined | null = null; let quote: MiNote | undefined | null = null;
if (note._misskey_quote ?? note.quoteUrl) { if (note._misskey_quote ?? note.quoteUrl) {
const tryResolveNote = async (uri: string): Promise< const tryResolveNote = async (uri: string): Promise<
| { status: 'ok'; res: Note } | { status: 'ok'; res: MiNote }
| { status: 'permerror' | 'temperror' } | { status: 'permerror' | 'temperror' }
> => { > => {
if (!/^https?:/.test(uri)) return { status: 'permerror' }; if (!/^https?:/.test(uri)) return { status: 'permerror' };
@ -220,7 +220,7 @@ export class ApNoteService {
const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string')); const uris = unique([note._misskey_quote, note.quoteUrl].filter((x): x is string => typeof x === 'string'));
const results = await Promise.all(uris.map(tryResolveNote)); const results = await Promise.all(uris.map(tryResolveNote));
quote = results.filter((x): x is { status: 'ok', res: Note } => x.status === 'ok').map(x => x.res).at(0); quote = results.filter((x): x is { status: 'ok', res: MiNote } => x.status === 'ok').map(x => x.res).at(0);
if (!quote) { if (!quote) {
if (results.some(x => x.status === 'temperror')) { if (results.some(x => x.status === 'temperror')) {
throw new Error('quote resolve failed'); throw new Error('quote resolve failed');
@ -310,7 +310,7 @@ export class ApNoteService {
* Misskeyに登録しそれを返します * Misskeyに登録しそれを返します
*/ */
@bindThis @bindThis
public async resolveNote(value: string | IObject, options: { sentFrom?: URL, resolver?: Resolver } = {}): Promise<Note | null> { public async resolveNote(value: string | IObject, options: { sentFrom?: URL, resolver?: Resolver } = {}): Promise<MiNote | null> {
const uri = getApId(value); const uri = getApId(value);
// ブロックしていたら中断 // ブロックしていたら中断
@ -342,7 +342,7 @@ export class ApNoteService {
} }
@bindThis @bindThis
public async extractEmojis(tags: IObject | IObject[], host: string): Promise<Emoji[]> { public async extractEmojis(tags: IObject | IObject[], host: string): Promise<MiEmoji[]> {
// eslint-disable-next-line no-param-reassign // eslint-disable-next-line no-param-reassign
host = this.utilityService.toPuny(host); host = this.utilityService.toPuny(host);

View file

@ -10,26 +10,26 @@ import { ModuleRef } from '@nestjs/core';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js'; import type { FollowingsRepository, InstancesRepository, UserProfilesRepository, UserPublickeysRepository, UsersRepository } from '@/models/index.js';
import type { Config } from '@/config.js'; import type { Config } from '@/config.js';
import type { LocalUser, RemoteUser } from '@/models/entities/User.js'; import type { MiLocalUser, MiRemoteUser } from '@/models/entities/User.js';
import { User } from '@/models/entities/User.js'; import { MiUser } from '@/models/entities/User.js';
import { truncate } from '@/misc/truncate.js'; import { truncate } from '@/misc/truncate.js';
import type { CacheService } from '@/core/CacheService.js'; import type { CacheService } from '@/core/CacheService.js';
import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { normalizeForSearch } from '@/misc/normalize-for-search.js';
import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js'; import { isDuplicateKeyValueError } from '@/misc/is-duplicate-key-value-error.js';
import type Logger from '@/logger.js'; import type Logger from '@/logger.js';
import type { Note } from '@/models/entities/Note.js'; import type { MiNote } from '@/models/entities/Note.js';
import type { IdService } from '@/core/IdService.js'; import type { IdService } from '@/core/IdService.js';
import type { MfmService } from '@/core/MfmService.js'; import type { MfmService } from '@/core/MfmService.js';
import { toArray } from '@/misc/prelude/array.js'; import { toArray } from '@/misc/prelude/array.js';
import type { GlobalEventService } from '@/core/GlobalEventService.js'; import type { GlobalEventService } from '@/core/GlobalEventService.js';
import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js'; import type { FederatedInstanceService } from '@/core/FederatedInstanceService.js';
import type { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js'; import type { FetchInstanceMetadataService } from '@/core/FetchInstanceMetadataService.js';
import { UserProfile } from '@/models/entities/UserProfile.js'; import { MiUserProfile } from '@/models/entities/UserProfile.js';
import { UserPublickey } from '@/models/entities/UserPublickey.js'; import { MiUserPublickey } from '@/models/entities/UserPublickey.js';
import type UsersChart from '@/core/chart/charts/users.js'; import type UsersChart from '@/core/chart/charts/users.js';
import type InstanceChart from '@/core/chart/charts/instance.js'; import type InstanceChart from '@/core/chart/charts/instance.js';
import type { HashtagService } from '@/core/HashtagService.js'; import type { HashtagService } from '@/core/HashtagService.js';
import { UserNotePining } from '@/models/entities/UserNotePining.js'; import { MiUserNotePining } from '@/models/entities/UserNotePining.js';
import { StatusError } from '@/misc/status-error.js'; import { StatusError } from '@/misc/status-error.js';
import type { UtilityService } from '@/core/UtilityService.js'; import type { UtilityService } from '@/core/UtilityService.js';
import type { UserEntityService } from '@/core/entities/UserEntityService.js'; import type { UserEntityService } from '@/core/entities/UserEntityService.js';
@ -201,20 +201,20 @@ export class ApPersonService implements OnModuleInit {
* Misskeyに対象のPersonが登録されていればそれを返しnullを返します * Misskeyに対象のPersonが登録されていればそれを返しnullを返します
*/ */
@bindThis @bindThis
public async fetchPerson(uri: string): Promise<LocalUser | RemoteUser | null> { public async fetchPerson(uri: string): Promise<MiLocalUser | MiRemoteUser | null> {
const cached = this.cacheService.uriPersonCache.get(uri) as LocalUser | RemoteUser | null | undefined; const cached = this.cacheService.uriPersonCache.get(uri) as MiLocalUser | MiRemoteUser | null | undefined;
if (cached) return cached; if (cached) return cached;
// URIがこのサーバーを指しているならデータベースからフェッチ // URIがこのサーバーを指しているならデータベースからフェッチ
if (uri.startsWith(`${this.config.url}/`)) { if (uri.startsWith(`${this.config.url}/`)) {
const id = uri.split('/').pop(); const id = uri.split('/').pop();
const u = await this.usersRepository.findOneBy({ id }) as LocalUser | null; const u = await this.usersRepository.findOneBy({ id }) as MiLocalUser | null;
if (u) this.cacheService.uriPersonCache.set(uri, u); if (u) this.cacheService.uriPersonCache.set(uri, u);
return u; return u;
} }
//#region このサーバーに既に登録されていたらそれを返す //#region このサーバーに既に登録されていたらそれを返す
const exist = await this.usersRepository.findOneBy({ uri }) as LocalUser | RemoteUser | null; const exist = await this.usersRepository.findOneBy({ uri }) as MiLocalUser | MiRemoteUser | null;
if (exist) { if (exist) {
this.cacheService.uriPersonCache.set(uri, exist); this.cacheService.uriPersonCache.set(uri, exist);
@ -225,7 +225,7 @@ export class ApPersonService implements OnModuleInit {
return null; return null;
} }
private async resolveAvatarAndBanner(user: RemoteUser, icon: any, image: any): Promise<Pick<RemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> { private async resolveAvatarAndBanner(user: MiRemoteUser, icon: any, image: any): Promise<Pick<MiRemoteUser, 'avatarId' | 'bannerId' | 'avatarUrl' | 'bannerUrl' | 'avatarBlurhash' | 'bannerBlurhash'>> {
const [avatar, banner] = await Promise.all([icon, image].map(img => { const [avatar, banner] = await Promise.all([icon, image].map(img => {
if (img == null) return null; if (img == null) return null;
if (user == null) throw new Error('failed to create user: user is null'); if (user == null) throw new Error('failed to create user: user is null');
@ -246,7 +246,7 @@ export class ApPersonService implements OnModuleInit {
* Personを作成します * Personを作成します
*/ */
@bindThis @bindThis
public async createPerson(uri: string, resolver?: Resolver): Promise<RemoteUser> { public async createPerson(uri: string, resolver?: Resolver): Promise<MiRemoteUser> {
if (typeof uri !== 'string') throw new Error('uri is not string'); if (typeof uri !== 'string') throw new Error('uri is not string');
if (uri.startsWith(this.config.url)) { if (uri.startsWith(this.config.url)) {
@ -280,7 +280,7 @@ export class ApPersonService implements OnModuleInit {
} }
// Create user // Create user
let user: RemoteUser | null = null; let user: MiRemoteUser | null = null;
//#region カスタム絵文字取得 //#region カスタム絵文字取得
const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host) const emojis = await this.apNoteService.extractEmojis(person.tag ?? [], host)
@ -294,7 +294,7 @@ export class ApPersonService implements OnModuleInit {
try { try {
// Start transaction // Start transaction
await this.db.transaction(async transactionalEntityManager => { await this.db.transaction(async transactionalEntityManager => {
user = await transactionalEntityManager.save(new User({ user = await transactionalEntityManager.save(new MiUser({
id: this.idService.genId(), id: this.idService.genId(),
avatarId: null, avatarId: null,
bannerId: null, bannerId: null,
@ -318,9 +318,9 @@ export class ApPersonService implements OnModuleInit {
isBot, isBot,
isCat: (person as any).isCat === true, isCat: (person as any).isCat === true,
emojis, emojis,
})) as RemoteUser; })) as MiRemoteUser;
await transactionalEntityManager.save(new UserProfile({ await transactionalEntityManager.save(new MiUserProfile({
userId: user.id, userId: user.id,
description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null, description: person.summary ? this.apMfmService.htmlToMfm(truncate(person.summary, summaryLength), person.tag) : null,
url, url,
@ -331,7 +331,7 @@ export class ApPersonService implements OnModuleInit {
})); }));
if (person.publicKey) { if (person.publicKey) {
await transactionalEntityManager.save(new UserPublickey({ await transactionalEntityManager.save(new MiUserPublickey({
userId: user.id, userId: user.id,
keyId: person.publicKey.id, keyId: person.publicKey.id,
keyPem: person.publicKey.publicKeyPem, keyPem: person.publicKey.publicKeyPem,
@ -345,7 +345,7 @@ export class ApPersonService implements OnModuleInit {
const u = await this.usersRepository.findOneBy({ uri: person.id }); const u = await this.usersRepository.findOneBy({ uri: person.id });
if (u == null) throw new Error('already registered'); if (u == null) throw new Error('already registered');
user = u as RemoteUser; user = u as MiRemoteUser;
} else { } else {
this.logger.error(e instanceof Error ? e : new Error(e as string)); this.logger.error(e instanceof Error ? e : new Error(e as string));
throw e; throw e;
@ -407,7 +407,7 @@ export class ApPersonService implements OnModuleInit {
if (uri.startsWith(`${this.config.url}/`)) return; if (uri.startsWith(`${this.config.url}/`)) return;
//#region このサーバーに既に登録されているか //#region このサーバーに既に登録されているか
const exist = await this.fetchPerson(uri) as RemoteUser | null; const exist = await this.fetchPerson(uri) as MiRemoteUser | null;
if (exist === null) return; if (exist === null) return;
//#endregion //#endregion
@ -456,7 +456,7 @@ export class ApPersonService implements OnModuleInit {
alsoKnownAs: person.alsoKnownAs ?? null, alsoKnownAs: person.alsoKnownAs ?? null,
isExplorable: person.discoverable, isExplorable: person.discoverable,
...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))), ...(await this.resolveAvatarAndBanner(exist, person.icon, person.image).catch(() => ({}))),
} as Partial<RemoteUser> & Pick<RemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>; } as Partial<MiRemoteUser> & Pick<MiRemoteUser, 'isBot' | 'isCat' | 'isLocked' | 'movedToUri' | 'alsoKnownAs' | 'isExplorable'>;
const moving = ((): boolean => { const moving = ((): boolean => {
// 移行先がない→ある // 移行先がない→ある
@ -542,7 +542,7 @@ export class ApPersonService implements OnModuleInit {
* Misskeyに登録しそれを返します * Misskeyに登録しそれを返します
*/ */
@bindThis @bindThis
public async resolvePerson(uri: string, resolver?: Resolver): Promise<LocalUser | RemoteUser> { public async resolvePerson(uri: string, resolver?: Resolver): Promise<MiLocalUser | MiRemoteUser> {
//#region このサーバーに既に登録されていたらそれを返す //#region このサーバーに既に登録されていたらそれを返す
const exist = await this.fetchPerson(uri); const exist = await this.fetchPerson(uri);
if (exist) return exist; if (exist) return exist;
@ -572,7 +572,7 @@ export class ApPersonService implements OnModuleInit {
} }
@bindThis @bindThis
public async updateFeatured(userId: User['id'], resolver?: Resolver): Promise<void> { public async updateFeatured(userId: MiUser['id'], resolver?: Resolver): Promise<void> {
const user = await this.usersRepository.findOneByOrFail({ id: userId }); const user = await this.usersRepository.findOneByOrFail({ id: userId });
if (!this.userEntityService.isRemoteUser(user)) return; if (!this.userEntityService.isRemoteUser(user)) return;
if (!user.featured) return; if (!user.featured) return;
@ -590,7 +590,7 @@ export class ApPersonService implements OnModuleInit {
const items = await Promise.all(toArray(unresolvedItems).map(x => _resolver.resolve(x))); const items = await Promise.all(toArray(unresolvedItems).map(x => _resolver.resolve(x)));
// Resolve and regist Notes // Resolve and regist Notes
const limit = promiseLimit<Note | null>(2); const limit = promiseLimit<MiNote | null>(2);
const featuredNotes = await Promise.all(items const featuredNotes = await Promise.all(items
.filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも .filter(item => getApType(item) === 'Note') // TODO: Noteでなくてもいいかも
.slice(0, 5) .slice(0, 5)
@ -600,13 +600,13 @@ export class ApPersonService implements OnModuleInit {
})))); }))));
await this.db.transaction(async transactionalEntityManager => { await this.db.transaction(async transactionalEntityManager => {
await transactionalEntityManager.delete(UserNotePining, { userId: user.id }); await transactionalEntityManager.delete(MiUserNotePining, { userId: user.id });
// とりあえずidを別の時間で生成して順番を維持 // とりあえずidを別の時間で生成して順番を維持
let td = 0; let td = 0;
for (const note of featuredNotes.filter((note): note is Note => note != null)) { for (const note of featuredNotes.filter((note): note is MiNote => note != null)) {
td -= 1000; td -= 1000;
transactionalEntityManager.insert(UserNotePining, { transactionalEntityManager.insert(MiUserNotePining, {
id: this.idService.genId(new Date(Date.now() + td)), id: this.idService.genId(new Date(Date.now() + td)),
createdAt: new Date(), createdAt: new Date(),
userId: user.id, userId: user.id,
@ -622,7 +622,7 @@ export class ApPersonService implements OnModuleInit {
* @param movePreventUris URIにsrc.movedToUriが含まれる場合 * @param movePreventUris URIにsrc.movedToUriが含まれる場合
*/ */
@bindThis @bindThis
private async processRemoteMove(src: RemoteUser, movePreventUris: string[] = []): Promise<string> { private async processRemoteMove(src: MiRemoteUser, movePreventUris: string[] = []): Promise<string> {
if (!src.movedToUri) return 'skip: no movedToUri'; if (!src.movedToUri) return 'skip: no movedToUri';
if (src.uri === src.movedToUri) return 'skip: movedTo itself (src)'; // if (src.uri === src.movedToUri) return 'skip: movedTo itself (src)'; //
if (movePreventUris.length > 10) return 'skip: too many moves'; if (movePreventUris.length > 10) return 'skip: too many moves';
@ -632,7 +632,7 @@ export class ApPersonService implements OnModuleInit {
if (dst && this.userEntityService.isLocalUser(dst)) { if (dst && this.userEntityService.isLocalUser(dst)) {
// targetがローカルユーザーだった場合データベースから引っ張ってくる // targetがローカルユーザーだった場合データベースから引っ張ってくる
dst = await this.usersRepository.findOneByOrFail({ uri: src.movedToUri }) as LocalUser; dst = await this.usersRepository.findOneByOrFail({ uri: src.movedToUri }) as MiLocalUser;
} else if (dst) { } else if (dst) {
if (movePreventUris.includes(src.movedToUri)) return 'skip: circular move'; if (movePreventUris.includes(src.movedToUri)) return 'skip: circular move';

View file

@ -6,7 +6,7 @@
import { Injectable, Inject } from '@nestjs/common'; import { Injectable, Inject } from '@nestjs/common';
import { DataSource } from 'typeorm'; import { DataSource } from 'typeorm';
import { AppLockService } from '@/core/AppLockService.js'; import { AppLockService } from '@/core/AppLockService.js';
import type { User } from '@/models/entities/User.js'; import type { MiUser } from '@/models/entities/User.js';
import { DI } from '@/di-symbols.js'; import { DI } from '@/di-symbols.js';
import { bindThis } from '@/decorators.js'; import { bindThis } from '@/decorators.js';
import Chart from '../core.js'; import Chart from '../core.js';
@ -21,9 +21,8 @@ const year = 1000 * 60 * 60 * 24 * 365;
/** /**
* *
*/ */
// eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class ActiveUsersChart extends Chart<typeof schema> { export default class ActiveUsersChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export
constructor( constructor(
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,
@ -43,7 +42,7 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
} }
@bindThis @bindThis
public async read(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> { public async read(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise<void> {
await this.commit({ await this.commit({
'read': [user.id], 'read': [user.id],
'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [], 'registeredWithinWeek': (Date.now() - user.createdAt.getTime() < week) ? [user.id] : [],
@ -56,7 +55,7 @@ export default class ActiveUsersChart extends Chart<typeof schema> {
} }
@bindThis @bindThis
public async write(user: { id: User['id'], host: null, createdAt: User['createdAt'] }): Promise<void> { public async write(user: { id: MiUser['id'], host: null, createdAt: MiUser['createdAt'] }): Promise<void> {
await this.commit({ await this.commit({
'write': [user.id], 'write': [user.id],
}); });

View file

@ -16,9 +16,8 @@ import type { KVs } from '../core.js';
/** /**
* Chart about ActivityPub requests * Chart about ActivityPub requests
*/ */
// eslint-disable-next-line import/no-default-export
@Injectable() @Injectable()
export default class ApRequestChart extends Chart<typeof schema> { export default class ApRequestChart extends Chart<typeof schema> { // eslint-disable-line import/no-default-export
constructor( constructor(
@Inject(DI.db) @Inject(DI.db)
private db: DataSource, private db: DataSource,

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