diff --git a/.forgejo/workflows/lint.yml b/.forgejo/workflows/lint.yml index e77c3e7a7..edbfa980d 100644 --- a/.forgejo/workflows/lint.yml +++ b/.forgejo/workflows/lint.yml @@ -88,4 +88,8 @@ jobs: - run: pnpm i --frozen-lockfile - run: pnpm --filter misskey-js run build if: ${{ matrix.workspace == 'backend' }} + - run: pnpm --filter misskey-reversi run build + if: ${{ matrix.workspace == 'backend' }} + - run: pnpm --filter misskey-bubble-game run build + if: ${{ matrix.workspace == 'backend' }} - run: pnpm --filter ${{ matrix.workspace }} run typecheck diff --git a/.gitignore b/.gitignore index 216b5548e..2b6a5c1eb 100644 --- a/.gitignore +++ b/.gitignore @@ -58,6 +58,7 @@ api-docs.json ormconfig.json temp /packages/frontend/src/**/*.stories.ts +tsdoc-metadata.json # Sharkey /packages/megalodon/lib diff --git a/CHANGELOG.md b/CHANGELOG.md index 1d2c98ad2..cff0fc07c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,27 +1,70 @@ + + ## 202x.x.x (Unreleased) +### Note +- 外部サイトからプラグインをインストールする場合のパスが`/install-extentions`から`/install-extensions`に変わります。現時点では以前のパスも利用できますが、非推奨です。 + ### General - Feat: [mCaptcha](https://github.com/mCaptcha/mCaptcha)のサポートを追加 - Fix: リストライムラインの「リノートを表示」が正しく機能しない問題を修正 +- Feat: Add support for TrueMail ### Client - Feat: 新しいゲームを追加 +- Feat: 音声・映像プレイヤーを追加 +- Feat: 絵文字の詳細ダイアログを追加 +- Feat: 枠線をつけるMFM`$[border.width=1,style=solid,color=fff,radius=0 ...]`を追加 + - デフォルトで枠線からはみ出る部分が隠されるようにしました。初期と同じ挙動にするには`$[border.noclip`が必要です +- Feat: スワイプでタブを切り替えられるように +- Enhance: MFM等のコードブロックに全文コピー用のボタンを追加 - Enhance: ハッシュタグ入力時に、本文の末尾の行に何も書かれていない場合は新たにスペースを追加しないように - Enhance: チャンネルノートのピン留めをノートのメニューからできるように - Enhance: 管理者の場合はAPI tokenの発行画面で管理機能に関する権限を付与できるように - Enhance: AiScriptを0.17.0に更新 [CHANGELOG](https://github.com/aiscript-dev/aiscript/blob/bb89d132b633a622d3cb0eff0d0cc7e476c0cfdd/CHANGELOG.md) - 配列の範囲外・非整数のインデックスへの代入が完全禁止になるので注意 - Enhance: 絵文字ピッカー・オートコンプリートで、完全一致した絵文字を優先的に表示するように +- Enhance: Playの説明欄にMFMを使えるように +- Enhance: チャンネルノートの場合は詳細ページからその前後のノートを見れるように +- Enhance: 季節に応じた画面の演出を南半球でも利用できるように +- Enhance: タイムラインフィルターの設定をすべて保持できるように + - 今までの「TLに他の人への返信を含める」設定は一旦リセットされます +- Enhance: タイムラインフィルターに「センシティブなファイルを含むノートを表示」を追加 +- Enhance: ノート作成画面のファイル添付メニューから直接ファイルを削除できるように +- Enhance: MFMの属性でオートコンプリートが使用できるように #12735 +- Enhance: 絵文字編集ダイアログをモーダルではなくウィンドウで表示するように - Fix: ネイティブモードの絵文字がモノクロにならないように - Fix: v2023.12.0で追加された「モデレーターがユーザーのアイコンもしくはバナー画像を未設定状態にできる機能」が管理画面上で正しく表示されていない問題を修正 - Fix: AiScriptの`readline`関数が不正な値を返すことがある問題のv2023.12.0時点での修正がPlay以外に適用されていないのを修正 - Fix: v2023.12.1で追加された`$[clickable ...]`および`onClickEv`が正しく機能していないのを修正 +- Enhance: ページ遷移時にPlayerを閉じるように ### Server - Enhance: 連合先のレートリミットに引っかかった際にリトライするようになりました - Enhance: ActivityPub Deliver queueでBodyを事前処理するように (#12916) - Enhance: クリップをエクスポートできるように +- Enhance: `/files`のファイルに対してHTTP Rangeリクエストを行えるように +- Enhance: `api.json`のOpenAPI Specificationを3.1.0に更新 - Fix: `drive/files/update`でファイル名のバリデーションが機能していない問題を修正 +- Fix: `notes/create`で、`text`が空白文字のみで構成されているか`null`であって、かつ`text`だけであるリクエストに対するレスポンスが400になるように変更 +- Fix: `notes/create`で、`text`が空白文字のみで構成されていてかつリノート、ファイルまたは投票を含んでいるリクエストに対するレスポンスの`text`が`""`から`null`になるように変更 +- Fix: ipv4とipv6の両方が利用可能な環境でallowedPrivateNetworksが設定されていた場合プライベートipの検証ができていなかった問題を修正 +- Fix: properly handle cc followers + +### Service Worker +- Enhance: オフライン表示のデザインを改善・多言語対応 ## 2023.12.2 diff --git a/Dockerfile b/Dockerfile index 2f2861960..8ad4bbbfb 100644 --- a/Dockerfile +++ b/Dockerfile @@ -43,6 +43,10 @@ COPY --from=build /sharkey/packages/megalodon/lib ./packages/megalodon/lib COPY --from=build /sharkey/packages/megalodon/node_modules ./packages/megalodon/node_modules COPY --from=build /sharkey/packages/misskey-js/built ./packages/misskey-js/built COPY --from=build /sharkey/packages/misskey-js/node_modules ./packages/misskey-js/node_modules +COPY --from=build /sharkey/packages/misskey-reversi/built ./packages/misskey-reversi/built +COPY --from=build /sharkey/packages/misskey-reversi/node_modules ./packages/misskey-reversi/node_modules +COPY --from=build /sharkey/packages/misskey-bubble-game/built ./packages/misskey-bubble-game/built +COPY --from=build /sharkey/packages/misskey-bubble-game/node_modules ./packages/misskey-bubble-game/node_modules COPY --from=build /sharkey/fluent-emojis ./fluent-emojis COPY --from=build /sharkey/tossface-emojis/dist ./tossface-emojis/dist COPY --from=build /sharkey/sharkey-assets ./packages/frontend/assets @@ -56,6 +60,8 @@ COPY packages/backend/migration ./packages/backend/migration COPY packages/backend/assets ./packages/backend/assets COPY packages/megalodon/package.json ./packages/megalodon/package.json COPY packages/misskey-js/package.json ./packages/misskey-js/package.json +COPY packages/misskey-reversi/package.json ./packages/misskey-reversi/package.json +COPY packages/misskey-bubble-game/package.json ./packages/misskey-bubble-game/package.json ENV NODE_ENV=production RUN corepack enable diff --git a/cypress/e2e/basic.cy.js b/cypress/e2e/basic.cy.js index 5ab07c748..d20b28c49 100644 --- a/cypress/e2e/basic.cy.js +++ b/cypress/e2e/basic.cy.js @@ -161,11 +161,13 @@ describe('After user signed in', () => { }); it('successfully loads', () => { - cy.get('[data-cy-user-setup-continue]').should('be.visible'); + // 表示に時間がかかるのでデフォルト秒数だとタイムアウトする + cy.get('[data-cy-user-setup-continue]', { timeout: 12000 }).should('be.visible'); }); it('account setup wizard', () => { - cy.get('[data-cy-user-setup-continue]').click(); + // 表示に時間がかかるのでデフォルト秒数だとタイムアウトする + cy.get('[data-cy-user-setup-continue]', { timeout: 12000 }).click(); cy.get('[data-cy-user-setup-user-name] input').type('ありす'); cy.get('[data-cy-user-setup-user-description] textarea').type('ほげ'); @@ -202,7 +204,8 @@ describe('After user setup', () => { cy.login('alice', 'alice1234'); // アカウント初期設定ウィザード - cy.get('[data-cy-user-setup] [data-cy-modal-window-close]').click(); + // 表示に時間がかかるのでデフォルト秒数だとタイムアウトする + cy.get('[data-cy-user-setup] [data-cy-modal-window-close]', { timeout: 12000 }).click(); cy.get('[data-cy-modal-dialog-ok]').click(); }); diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml index 0a7d86cc8..47f131032 100644 --- a/locales/ar-SA.yml +++ b/locales/ar-SA.yml @@ -360,6 +360,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "فعّل hCaptcha" hcaptchaSiteKey: "مفتاح الموقع" hcaptchaSecretKey: "المفتاح السري" +mcaptchaSiteKey: "مفتاح الموقع" +mcaptchaSecretKey: "المفتاح السري" recaptcha: "reCAPTCHA" enableRecaptcha: "تمكين reCAPTCHA" recaptchaSiteKey: "مفتاح الموقع" @@ -1010,6 +1012,7 @@ icon: "الصورة الرمزية" replies: "رد" renotes: "أعد النشر" flip: "اقلب" +lastNDays: "آخر {n} أيام" _initialAccountSetting: accountCreated: "نجح إنشاء حسابك!" letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي." @@ -1414,6 +1417,7 @@ _profile: _exportOrImport: allNotes: "كل الملاحظات" favoritedNotes: " الملاحظات المفضلة" + clips: "مِشبك" followingList: "المتابَعون" muteList: "المستخدمون المكتومون" blockingList: "المستخدمون المحجوبون" @@ -1561,3 +1565,6 @@ _moderationLogTypes: suspend: "علِق" resetPassword: "أعد تعيين كلمتك السرية" createInvitation: "ولِّد دعوة" +_reversi: + total: "المجموع" + diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml index 77ba3f030..42edad1fd 100644 --- a/locales/bn-BD.yml +++ b/locales/bn-BD.yml @@ -357,6 +357,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptcha চালু করুন" hcaptchaSiteKey: "সাইট কী" hcaptchaSecretKey: "সিক্রেট কী" +mcaptchaSiteKey: "সাইট কী" +mcaptchaSecretKey: "সিক্রেট কী" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA চালু করুন" recaptchaSiteKey: "সাইট কী" @@ -1190,6 +1192,7 @@ _profile: changeBanner: "ব্যানার পরিবর্তন করুন" _exportOrImport: allNotes: "সকল নোট" + clips: "ক্লিপ" followingList: "অনুসরণ করা হচ্ছে" muteList: "মিউট" blockingList: "ব্লক" @@ -1341,3 +1344,6 @@ _webhookSettings: _moderationLogTypes: suspend: "স্থগিত করা" resetPassword: "পাসওয়ার্ড রিসেট করুন" +_reversi: + total: "মোট" + diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml index ba1e44256..59594f85f 100644 --- a/locales/ca-ES.yml +++ b/locales/ca-ES.yml @@ -336,8 +336,12 @@ whenServerDisconnected: "Quan es perdi la connexió al servidor" disconnectedFromServer: "Desconnectat pel servidor" reload: "Actualitza" doNothing: "Ignora" -accept: "Accepta" -normal: "Nomal" +reloadConfirm: "Vols recarregar?" +watch: "Veure" +unwatch: "Deixar de veure" +accept: "Acceptar" +reject: "Denegar" +normal: "Normal" instanceName: "Nom del servidor" instanceDescription: "Descripció del servidor" maintainerName: "Nom de l'administrador" @@ -355,25 +359,56 @@ connectService: "Connecta" disconnectService: "Desconnecta" enableLocalTimeline: "Activa la línia de temps local" enableGlobalTimeline: "Activa la línia de temps global" +disablingTimelinesInfo: "Fins i tot si aquestes línies de temps són desactivades, els administradors i els moderadors poden continuar visualitzant per conveniència." registration: "Registre" +enableRegistration: "Permet els registres d'usuaris" invite: "Convida" +driveCapacityPerLocalAccount: "Capacitat del disc per usuaris locals" +driveCapacityPerRemoteAccount: "Capacitat del disc per usuaris remots" +inMb: "En megabytes" +bannerUrl: "Adreça URL del bàner" +backgroundImageUrl: "Adreça URL de la imatge de fons" basicInfo: "Informació bàsica" pinnedUsers: "Usuaris fixats" +pinnedUsersDescription: "Llista d'usuaris, separats per salts de línia, que seran fixats a la pestanya \"Explorar\"." +pinnedPages: "Pàgines fixades" +pinnedPagesDescription: "Escriu els camins de les pàgines que vols fixar a la pàgina d'inici d'aquesta instància. Separades per salts de línia." +pinnedClipId: "ID del retall fixat" pinnedNotes: "Nota fixada" +hcaptcha: "hCaptcha" +enableHcaptcha: "Activar hCaptcha" +hcaptchaSiteKey: "Clau del lloc" +hcaptchaSecretKey: "Clau secreta" +mcaptcha: "mCaptcha" +enableMcaptcha: "Activar mCaptcha" +mcaptchaSiteKey: "Clau del lloc" +mcaptchaSecretKey: "Clau secreta" +mcaptchaInstanceUrl: "Adreça URL del servidor mCaptcha" +recaptcha: "reCAPTCHA" +enableRecaptcha: "Activar reCAPTCHA" +recaptchaSiteKey: "Clau del lloc" +recaptchaSecretKey: "Clau secreta" turnstile: "Turnstile" enableTurnstile: "Activar Turnstile" turnstileSiteKey: "Clau del lloc" turnstileSecretKey: "Clau secreta" +avoidMultiCaptchaConfirm: "Fer servir diferents sistemes de Captcha a la vegada pot causar problemes entre ells. Vols desactivar els altres sistemes de Captcha activats? Si els vols mantenir actius fes clic a cancel·lar." antennas: "Antena" manageAntennas: "Gestiona les antenes" +name: "Nom" antennaSource: "Font de l'antena" antennaKeywords: "Paraules clau a seguir" 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" withFileAntenna: "Només les publicacions amb fitxers" +enableServiceworker: "Activar les notificacions al navegador" antennaUsersDescription: "Llistar un nom d'usuari per línia" +caseSensitive: "Sensible a majúscules i minúscules " +withReplies: "Inclou respostes" +connectedTo: "Aquests comptes hi són connectats" notesAndReplies: "Amb respostes" +withFiles: "Incloure arxius" silence: "Silencia" silenceConfirm: "Segur que vols silenciar aquest usuari?" unsilence: "Deixa de silenciar" @@ -389,20 +424,40 @@ userList: "Llistes" about: "Informació" aboutMisskey: "Quant a Misskey" administrator: "Administrador/a" +token: "Codi de verificació" +2fa: "Autenticació de doble factor" +setupOf2fa: "Configurar l'autenticació de doble factor" +totp: "Aplicació d'autenticació" +totpDescription: "Escriu una contrasenya d'un sol us fent servir l'aplicació d'autenticació" moderator: "Moderador/a" moderation: "Moderació" +moderationNote: "Nota de moderació " +addModerationNote: "Afegir una nota de moderació " +moderationLogs: "Registre de moderació " nUsersMentioned: "{n} usuaris mencionats" +securityKeyAndPasskey: "Clau de seguretat / Clau de pas" securityKey: "Clau de seguretat" +lastUsed: "Fet servir per última vegada" +lastUsedAt: "Fet servir per última vegada: {t}" unregister: "Cancel·la el registre" passwordLessLogin: "Inici de sessió sense contrasenya" +passwordLessLoginDescription: "Permet l'inici de sessió sense contrasenya fent servir només una Clau de seguretat/Clau de pas" resetPassword: "Restableix la contrasenya" newPasswordIs: "La contrasenya nova és «{password}»" reduceUiAnimation: "Redueix les animacions de la interfície" share: "Comparteix" notFound: "No s'ha trobat" +notFoundDescription: "No es troba cap pàgina que correspongui a aquesta adreça" +uploadFolder: "Carpeta per defecte per pujades" +markAsReadAllNotifications: "Marca totes les notificacions com a llegides" markAsReadAllUnreadNotes: "Marca-ho tot com a llegit" +markAsReadAllTalkMessages: "Marcar tots els missatges com llegits" help: "Ajuda" +inputMessageHere: "Escriu aquí el teu missatge " +close: "Tancar" invites: "Convida" +members: "Membres" +transfer: "Transferir" title: "Títol" text: "Text" enable: "Habilita" @@ -472,12 +527,62 @@ objectStorage: "Emmagatzematge d'objectes\n" useObjectStorage: "Utilitzar l'emmagatzematge d'objectes" objectStorageBaseUrl: "Base d'enllaç" objectStorageBaseUrlDesc: "Prefix d'enllaç utilitzat per a fer referencia als fitxers. Especifica l'enllaç del teu CDN o Proxy si n'estàs utilitzant qualsevol, en cas contrari, especifica l'enllaç al que es pot accedir públicament segons la guia de servei que vosté utilitza.\nPer l'ús d'S3 utilitza 'https://.s3.amazonaws.com' I per a GCS o serveis equivalents utilitza 'https://storage.googleapis.com/'." +objectStorageBucket: "Dipòsit " +objectStorageBucketDesc: "Escriu el nom del dipòsit que fas servir al teu proveïdor d'emmagatzematge " +objectStoragePrefix: "Prefix" +objectStoragePrefixDesc: "Els fitxers es deixaren a directoris amb aquest prefix" +objectStorageEndpoint: "Endpoint" +objectStorageEndpointDesc: "Deixa'l buit si fas servir AWS S3, si no és així específica un punt d'entrada com '' o ':', depenent del servei que facis servir." +objectStorageRegion: "Regió " +objectStorageRegionDesc: "Especifica una regió com 'xx-east-1'. Si el teu servei no diferència regions has de posar 'us-east-1'. Deixa'l buit si fas servir variables d'entorn o un arxiu de configuració d'AWS." +objectStorageUseSSL: "Fes servir SSL" +objectStorageUseSSLDesc: "Desactiva'l si no tens pensat fer servir HTTPS per les connexions de l'API" +objectStorageUseProxy: "Connectar-se mitjançant un Proxy" +objectStorageUseProxyDesc: "Desactiva'l si no faràs servir un Proxy per les connexions de l'API" +objectStorageSetPublicRead: "Configurar les pujades com públiques " +s3ForcePathStyleDesc: "Si s3ForcePathStyle es troba activat el nom del dipòsit s'ha d'incloure a l'adreça URL en comtes del nom del host. Potser que necessitis activar-ho quan facis servir, per exemple, Minio a un servidor propi." +serverLogs: "Registres del servidor" +deleteAll: "Esborrar tot" +showFixedPostForm: "Mostrar el formulari per escriure a l'inici de la línia de temps" +showFixedPostFormInChannel: "Mostrar el formulari d'escriptura al principi de la línia de temps (Canals)" +withRepliesByDefaultForNewlyFollowed: "Inclou les respostes d'usuaris nous seguits a la línia de temps per defecte." newNoteRecived: "Hi ha publicacions noves" +sounds: "Sons" +sound: "So" +listen: "Escoltar" +none: "Res" +showInPage: "Mostrar a la pàgina " +popout: "Finestra emergent" +volume: "Volum" +masterVolume: "Volum principal" +notUseSound: "Sense so" +useSoundOnlyWhenActive: "Reproduir sons només quan Misskey estigui actiu" +details: "Detalls" +chooseEmoji: "Tria un emoji" +unableToProcess: "L'operació no pot ser completada " +recentUsed: "Utilitzat recentment" +install: "Instal·lació " +uninstall: "Desinstal·lar " +installedApps: "Aplicacions autoritzades " +nothing: "No hi ha res per veure aquí " installedDate: "Data d'instal·lació" +lastUsedDate: "Utilitzat per última vegada" state: "Estat" sort: "Ordena" ascendingOrder: "Ascendent" descendingOrder: "Descendent" +scratchpad: "Bloc de proves" +scratchpadDescription: "El bloc de proves proporciona un entorn experimental per AiScript. Pot escriure i verificar els resultats que interactuen amb Misskey." +output: "Sortida" +script: "Script" +disablePagesScript: "Desactivar AiScript a les pàgines " +updateRemoteUser: "Actualitzar la informació de l'usuari remot" +unsetUserAvatar: "Desactivar l'avatar " +unsetUserAvatarConfirm: "Segur que vols desactivar l'avatar?" +unsetUserBanner: "Desactivar el bàner " +unsetUserBannerConfirm: "Segur que vols desactivar el bàner?" +deleteAllFiles: "Esborrar tots els arxius" +deleteAllFilesConfirm: "Segur que vols esborrar tots els arxius?" removeAllFollowing: "Deixar de seguir tots els usuaris seguits" removeAllFollowingDescription: "El fet d'executar això, et farà deixar de seguir a tots els usuaris de {host}. Si us plau, executa això si l'amfitrió, per exemple, ja no existeix." userSuspended: "Aquest usuari ha sigut suspès" @@ -526,31 +631,543 @@ medium: "Mitjà" small: "Petit" generateAccessToken: "Genera codi d'accés" permission: "Permisos" +adminPermission: "Permisos d'administrador " enableAll: "Habilita tot" disableAll: "Deshabilita tot" tokenRequested: "Donar accés al compte" +pluginTokenRequestedDescription: "Aquest connector podrà fer servir tots els permisos configurats aquí." +notificationType: "Tipus de notificació " +edit: "Editar" +emailServer: "Servidor de correu electrònic " +enableEmail: "Activar l'enviament de correus electrònics " +emailConfigInfo: "Es fa servir per confirmar el teu correu quan et registres o oblides la contrasenya " +email: "Correu electrònic" +emailAddress: "Adreça de correu electrònic" +smtpConfig: "Configuració del servidor SMTP" smtpHost: "Amfitrió" +smtpPort: "Port" smtpUser: "Nom d'usuari" smtpPass: "Contrasenya" +emptyToDisableSmtpAuth: "No omplis el nom d'usuari i la contrasenya si vols deshabilitar l'autenticació SMTP" +smtpSecure: "Fes servir SSL/TLS per connexions SMTP" +smtpSecureInfo: "Desactiva això quan facis servir connexions STARTTLS" +testEmail: "Prova l'enviament de correu " +wordMute: "Silenciar paraules " +hardWordMute: "Silenciar paraules fortes" +regexpError: "Error de l'expressió regular " +regexpErrorDescription: "S'ha produït un error a l'expressió regular a la línia {line} de les paraules silenciades {tab}:" +instanceMute: "Silenciar servidor" +userSaysSomething: "{name} n'ha dit alguna cosa" +makeActive: "Activar" +display: "Veure" +copy: "Copiar" +metrics: "Mètriques" +overview: "Visió General" +logs: "Registres" +delayed: "Endarrerits " +database: "Bases de dades" +channel: "Canals" +create: "Crear" +notificationSetting: "Paràmetres de notificacions" +notificationSettingDesc: "Selecciona els tipus de notificacions que es mostraran" +useGlobalSetting: "Fer servir la configuració global" +useGlobalSettingDesc: "Si s'activa, es farà servir la configuració de notificacions del teu comte. Si no s'activa es poden fer configuracions individuals." +other: "Altre" +regenerateLoginToken: "Regenerar clau de seguretat d'inici de sessió" +regenerateLoginTokenDescription: "Regenera la clau de seguretat que es fa servir internament durant l'inici de sessió. Normalment aquesta acció no és necessària. Si es regenera es tancarà la sessió a tots els dispositius amb una sessió activa." +theKeywordWhenSearchingForCustomEmoji: "Cercar un emoji personalitzat " +setMultipleBySeparatingWithSpace: "Separa múltiples entrades amb un espai" +fileIdOrUrl: "ID de l'arxiu o URL" +behavior: "Comportament" +sample: "Mostrar" +abuseReports: "Denúncies " +reportAbuse: "Denuncia un abús " +reportAbuseRenote: "Denuncia una renota" +reportAbuseOf: "Denuncia a {name}" +fillAbuseReportDescription: "Omple els detalls sobre aquesta denúncia. Si la denúncia és sobre una nota en concret inclou l'adreça URL." +abuseReported: "La teva denúncia s'ha enviat. Moltes gràcies." +reporter: "Denunciant " +reporteeOrigin: "Origen de la denúncia " +reporterOrigin: "Origen del denunciant" +forwardReport: "Transferir la denúncia a una instància remota" +forwardReportIsAnonymous: "En comptes del teu compte, es farà servir un compte anònim com a denunciat a la instància remota." +send: "Enviar" +abuseMarkAsResolved: "Marcar la denúncia com a resolta" +openInNewTab: "Obre a una pestanya nova" +openInSideView: "Obre a una vista lateral" +defaultNavigationBehaviour: "Navegació per defecte" +editTheseSettingsMayBreakAccount: "Editar aquestes opcions pot deixar inoperatiu el teu compte" +instanceTicker: "Informació de notes de la instància " +waitingFor: "Esperant {x}" +random: "Aleatori " +system: "Sistema" +switchUi: "Canviar interfície d'usuari " +desktop: "Escriptori" +clip: "Retalls" +createNew: "Crear" +optional: "Opcional" +createNewClip: "Crear un nou Retall" +unclip: "Treure Retall" +confirmToUnclipAlreadyClippedNote: "Aquesta nota ja és inclosa al Retall \"{name}\". Vols treure-la d'aquest retall?" +public: "Públic " +private: "Privat" +i18nInfo: "Misskey està sent traduït a diferents idiomes per voluntaris. Pots ajudar aquí {link}." +manageAccessTokens: "Administrar claus de seguretat d'accés " +accountInfo: "Informació del compte" +notesCount: "Comptador de notes" +repliesCount: "Nombre de respostes" renotesCount: "Impulsos fets" +repliedCount: "Nombre de respostes rebudes" renotedCount: "Impulsos rebuts" +followingCount: "Nombre de comptes seguits" +followersCount: "Nombre de seguidors" +sentReactionsCount: "Nombre de reaccions enviades" +receivedReactionsCount: "Nombre de reaccions rebudes" +pollVotesCount: "Nombre de vots enviats a enquestes" +pollVotedCount: "Nombre de vots rebuts a les enquestes" +yes: "Sí " +no: "No" +driveFilesCount: "Nombre de fitxers al Disc" +driveUsage: "Utilització de l'espai del Disc" +noCrawle: "Rebutjar la indexació dels buscadors" +noCrawleDescription: "No permetis que els buscadors indexin el teu perfil, notes, pàgines, etc." +lockedAccountInfo: "Tret que establiu la visibilitat de la nota a \"Només seguidors\", les vostres notes seran visibles per qualsevol persona, fins i tot si heu d'aprovar els seguidors manualment" +alwaysMarkSensitive: "Marcar com a sensible per defecte" +loadRawImages: "Carregar les imatges originals en comptes de miniatures " +disableShowingAnimatedImages: "No reproduir imatges animades" +highlightSensitiveMedia: "Ressalta els medis marcats com a sensibles" +verificationEmailSent: "S'ha enviat un correu electrònic de verificació. Fes clic a l'enllaç per completar la verificació." +notSet: "Sense definir" +emailVerified: "El correu electrònic s'ha verificat" +noteFavoritesCount: "Nombre de notes favorites " +pageLikesCount: "Nombre de Pàgines que t'agraden " +pageLikedCount: "Nombre d'agraïments rebuts a les Pàgines " +contact: "Contacte" +useSystemFont: "Fes servir la font per defecte del sistema" +clips: "Retalls" +experimentalFeatures: "Característiques experimentals" +experimental: "Experimental" +thisIsExperimentalFeature: "Aquesta és una característica experimental. La seva funcionalitat pot canviar, i pot ser que no funcioni degudament." +developer: "Programador" +makeExplorable: "Fes que el compte sigui visible a la secció \"Explorar\"" +makeExplorableDescription: "Si desactives aquesta opció, el teu compte no sortirà a la secció \"Explorar\"" +showGapBetweenNotesInTimeline: "Mostra una separació entre els articles a la línia de temps" +duplicate: "Duplicat" +left: "Esquerra" +center: "Centre" +wide: "Gran" +narrow: "Estret" +reloadToApplySetting: "Aquest ajust només s'aplicarà després de recarregar la pàgina. Vols fer-ho ara?" +needReloadToApply: "Es requereix recarregar per reflectir aquesta opció " +showTitlebar: "Mostra la barra del títol " clearCache: "Esborra la memòria cau" +onlineUsersCount: "{n} Usuaris es troben en línia " +nUsers: "{n} Usuaris" +nNotes: "{n} Notes" +sendErrorReports: "Enviar informes d'error " +sendErrorReportsDescription: "Quan s'activa, es compartirà amb Misskey informació detallada de l'error quan es trobi un problema això farà pujar la qualitat de Misskey.\nAixò inclourà informació com la versió del SO que fas servir, el navegador web que fas servir, la teva activitat a Misskey, etc." +myTheme: "El meu tema" +backgroundColor: "Color de fons" +accentColor: "Color principal" +textColor: "Color del text" +saveAs: "Desar com..." +advanced: "Avançat" +advancedSettings: "Configuració avançada" +value: "Valor" +createdAt: "Creat el" +updatedAt: "Actualitzat el" +saveConfirm: "Desar canvis?" +deleteConfirm: "Segur que vols esborrar?" +invalidValue: "Valor invàlid." +registry: "Registre " +closeAccount: "Tancar el compte" +currentVersion: "Versió actual" +latestVersion: "Versió nova" +youAreRunningUpToDateClient: "Ja estàs fent servir la versió més recent del client." +newVersionOfClientAvailable: "Tens disponible una versió del client més recent." +usageAmount: "Ús " +capacity: "Capacitat" +inUse: "Fet servir" +editCode: "Editar el codi" +apply: "Aplicar" +receiveAnnouncementFromInstance: "Rep notificacions d'aquesta instància " +emailNotification: "Notificacions per correu electrònic " +publish: "Publicar" +inChannelSearch: "Cerca al canal" +useReactionPickerForContextMenu: "Fes clic al botó dret del ratolí per obrir el menú de reaccions" +typingUsers: "{users} està/estàn Escrivint " +jumpToSpecifiedDate: "Ves a una data concreta" showingPastTimeline: "Estàs veient una línia de temps antiga" +clear: "Tornar" +markAllAsRead: "Marcar tot com llegit" +goBack: "Tornar" +unlikeConfirm: "Vols esborrar el teu m'agrada?" +fullView: "Vista completa." +quitFullView: "Sortir de la vista completa" +addDescription: "Afegeix una descripció " +userPagePinTip: "Podeu seleccionar \"Fixar al perfil\" del menú de notes individuals per mostrar les notes aquí." +notSpecifiedMentionWarning: "Aquesta nota esmenta usuaris que no es troben com a destinataris" info: "Informació" +userInfo: "Informació de l'usuari" +unknown: "Desconegut" +onlineStatus: "Connectat" +hideOnlineStatus: "Ocultar l'estat de connexió" +hideOnlineStatusDescription: "Ocultant el teu estat de connexió redueix les funcionalitats d'algunes funcions com la cerca." +online: "Connectat" +active: "Actiu" +offline: "Desconnectat" +notRecommended: "No recomanat" +botProtection: "Protecció contra bots" +instanceBlocking: "Instàncies blocades/silenciades" +selectAccount: "Seleccionar un compte" +switchAccount: "Canviar de compte" +enabled: "Activat" +disabled: "Desactivat" +quickAction: "Accions ràpides" user: "Usuaris" administration: "Administració" +accounts: "Comptes" +switch: "Canvia" +noMaintainerInformationWarning: "La informació de l'administrador no s'ha configurat" +noBotProtectionWarning: "La protecció contra bots no s'ha configurat." +configure: "Configurar" +postToGallery: "Crear una nova publicació a la galeria" +postToHashtag: "Pública a aquesta etiqueta" +gallery: "Galeria" +recentPosts: "Articles recents" +popularPosts: "Articles populars" +shareWithNote: "Comparteix amb una nota" +ads: "Anuncis" +expiration: "" +startingperiod: "Inici" +memo: "Recordatori" +priority: "Prioritat" +high: "Alta" middle: "Mitjà" +low: "Baixa" +emailNotConfiguredWarning: "Adreça de correu electrònic" +ratio: "Proporció" +previewNoteText: "Mostrar vista prèvia" +customCss: "CSS personalitzat" +customCssWarn: "Aquesta configuració només hauries de configurar-la si saps que fas. Si poses valors inadequats pots fer que el client deixi de funcionar correctament." global: "Global" +squareAvatars: "Mostrar avatars quadrats" +sent: "Enviar" +received: "Rebut" +searchResult: "Resultats de la cerca" +hashtags: "Etiquetes" +troubleshooting: "Solucionar problemes" +useBlurEffect: "Fes servir efectes de desenfocament a la interfície" +learnMore: "Saber més " +misskeyUpdated: "Misskey s'ha actualitzat " +whatIsNew: "Mostra canvis" +translate: "Traduir " +translatedFrom: "Traduït del {x}" +accountDeletionInProgress: "S'està produint l'eliminació del compte" +usernameInfo: "Un nom que identifiqui el teu compte d'altres en aquest servidor. Pots fer servir lletres (a~z, A~Z), números (0~9) i guions baixos (_). Els noms d'usuari no es poden canviar després." +aiChanMode: "Mode IA" +devMode: "Mode desenvolupador" +keepCw: "Mantenir els avisos de contingut" +pubSub: "Comptes Pub/Sub" +lastCommunication: "Última comunicació " +resolved: "Resolt" +unresolved: "Sense resoldre" +breakFollow: "Deixar de seguir" +breakFollowConfirm: "Vols deixar de seguir?" +itsOn: "Activat" +itsOff: "Desactivat" +on: "Activar" +off: "Desactivar" +emailRequiredForSignup: "Demanar correu electrònic per registrar-se " +unread: "Sense llegir" +filter: "Filtrar" +controlPanel: "Panel de control" +manageAccounts: "Gestionar comptes" +makeReactionsPublic: "Reaccions públiques " +makeReactionsPublicDescription: "Això fa que totes les teves reaccions siguin visibles públicament " +classic: "Clàssic " +muteThread: "Silenciar el fil" +unmuteThread: "Deixar de silenciar el fil" +followingVisibility: "Visibilitat dels seguiments" +followersVisibility: "Visibilitat dels seguidors" +continueThread: "Veure la continuació del fil" +deleteAccountConfirm: "Això eliminarà el teu compte irreversiblement. Procedir?" +incorrectPassword: "Contrasenya incorrecta." +voteConfirm: "Confirma el teu vot \"{choice}\"" +hide: "Amagar" +useDrawerReactionPickerForMobile: "Mostrar el selector de reaccions com un calaix al mòbil " +welcomeBackWithName: "Benvingut de nou, {name}" +clickToFinishEmailVerification: "Si us plau, fes clic a [{ok}] per completar la verificació per correu electrònic " +overridedDeviceKind: "Tipus de dispositiu" +smartphone: "Telèfon intel·ligent" +tablet: "Tauleta" +auto: "Automàtic " +themeColor: "Color del tema" +size: "Mida" +numberOfColumn: "Nombre de columnes" searchByGoogle: "Cercar" +instanceDefaultLightTheme: "Tema clar per defecte de tota la instància " +instanceDefaultDarkTheme: "Tema fosc per defecte de tota la instància " +instanceDefaultThemeDescription: "Introdueix el codi del tema en format d'objecte" +mutePeriod: "Duració del silenci" +period: "Límit de temps" +indefinitely: "Permanent" +tenMinutes: "10 minuts" +oneHour: "1 hora" +oneDay: "Un dia" +oneWeek: "Una setmana" +oneMonth: "Un mes" +reflectMayTakeTime: "Això pot trigar una estona a tenir efecte" +failedToFetchAccountInformation: "No es pot obtenir la informació del compte" +rateLimitExceeded: "S'ha arribat al màxim de peticions" +cropImage: "Retalla la imatge" +cropImageAsk: "Vols retallar la imatge?" +cropYes: "Retallar" +cropNo: "Fer servir tal qual" file: "Fitxers" +recentNHours: "Últimes {n} hores" +recentNDays: "Últims {n} dies" +noEmailServerWarning: "Correu electrònic del servidor sense configurar" +thereIsUnresolvedAbuseReportWarning: "Hi ha informes sense solucionar." +recommended: "Recomanat" +check: "Verificar" +driveCapOverrideLabel: "Canvia la capacitat del Disc per aquest usuari" +driveCapOverrideCaption: "Restableix la mida original posant un valor de 0 o menys." +requireAdminForView: "Has de ser administrador per poder veure això." +isSystemAccount: "Un compte creat i operat automàticament pel sistema." +typeToConfirm: "Si us plau, escriu {x} per confirmar" +deleteAccount: "Esborrar el compte" +document: "Documentació" +numberOfPageCache: "Nombre de pàgines a la memòria cau" +numberOfPageCacheDescription: "Incrementant aquest nombre farà que millori l'experiència de l'usuari, però es farà servir més memòria al dispositiu de l'usuari." +logoutConfirm: "Vols sortir?" +lastActiveDate: "Fet servir per última vegada" +statusbar: "Barra d'estat" +pleaseSelect: "Selecciona una opció" +reverse: "Invertir" +colored: "Colorit" +refreshInterval: "Interval d'actualització " +label: "Etiqueta" +type: "Tipus" +speed: "Velocitat" +slow: "Lent" +fast: "Ràpid " +sensitiveMediaDetection: "Detecció de contingut sensible" +localOnly: "Només local" +remoteOnly: "Només remot" +failedToUpload: "Ha fallat la pujada" +cannotUploadBecauseInappropriate: "Aquest fitxer no es pot pujar perquè s'ha trobat que algunes parts són inapropiades." +cannotUploadBecauseNoFreeSpace: "Ha fallat la pujada del fitxer perquè no hi ha capacitat al Disc." +cannotUploadBecauseExceedsFileSizeLimit: "Aquest fitxer no es pot pujar perquè supera la mida permesa." +beta: "Proves" +enableAutoSensitive: "Marcar com a sensible automàticament " +enableAutoSensitiveDescription: "Permet la detecció i el marcat automàtic dels mitjans sensibles fent servir aprenentatge automàtic quan sigui possible. Si aquesta opció es troba desactivada potser que estigui activada per a tota la instància. " +activeEmailValidationDescription: "Activa la validació estricta de comptes de correu electrònic, inclou la validació d'adreces d'un sol ús i si es possible comunicar-se amb aquestes. Quan es troba desactivada només es vàlida el format del correu electrònic." +navbar: "Barra de navegació " +shuffle: "Aleatori" +account: "Compte" +move: "Mou" +pushNotification: "Enviament de notificacions" +subscribePushNotification: "Activar l'enviament de notificacions" +unsubscribePushNotification: "Desactivar l'enviament de notificacions" +pushNotificationAlreadySubscribed: "L'enviament de notificacions ja és activat" +pushNotificationNotSupported: "El teu navegador o la teva instància no suporta l'enviament de notificacions " +sendPushNotificationReadMessage: "Esborrar les notificacions enviades quan s'hagin llegit" +sendPushNotificationReadMessageCaption: "Això pot fer que el teu dispositiu consumeixi més bateria" +windowMaximize: "Maximitzar " +windowMinimize: "Minimitzar" +windowRestore: "Restaurar" +caption: "Llegenda" +loggedInAsBot: "Identificat com a bot" +tools: "Eines" +cannotLoad: "No es pot carregar" +numberOfProfileView: "Visualitzacions del perfil" +like: "M'agrada " +unlike: "Treure m'agrada " +numberOfLikes: "M'agraden " +show: "Veure" +neverShow: "No mostrar més " +remindMeLater: "Recorda-m'ho més tard" +didYouLikeMisskey: "T'està agradant Misskey?" +pleaseDonate: "A {host} fem servir el software lliure Misskey. Considera fer un donatiu a Misskey perquè pugui continuar el seu desenvolupament!" +roles: "Rols" +role: "Rols" +noRole: "No s'han trobat rols" +normalUser: "Usuari normal" +undefined: "Sense definir" +assign: "Assignar " +unassign: "Treure" +color: "Color" +manageCustomEmojis: "Gestiona els emojis personalitzats" +manageAvatarDecorations: "Gestiona les decoracions dels avatars " +youCannotCreateAnymore: "Has arribat al màxim de creacions" +cannotPerformTemporary: "Temporalment no disponible" +cannotPerformTemporaryDescription: "Aquesta acció no es pot dur a terme temporalment per arribar al seu límit d'execució. Pots esperar una mica i tornar-ho a intentar." +invalidParamError: "Paràmetres incorrectes " +invalidParamErrorDescription: "Els paràmetres demanats no són correctes. Normalment això es deu a un error, però també pot ser a alguna entrada excedint els límits o similar." +permissionDeniedError: "Operació no permesa " +permissionDeniedErrorDescription: "Aquest compte no té suficients permisos per dur a terme aquesta acció " +preset: "Predefinit" +selectFromPresets: "Escull des dels predefinits" +achievements: "Assoliments" +gotInvalidResponseError: "Resposta del servidor invàlida " +gotInvalidResponseErrorDescription: "No es pot contactar amb el servidor o potser es troba fora de línia per manteniment. Provar-ho de nou més tard." +thisPostMayBeAnnoying: "Aquesta nota pot ser molesta per algú." +thisPostMayBeAnnoyingHome: "Publicar a la línia de temps d'Inici" +thisPostMayBeAnnoyingCancel: "Cancel·lar " +thisPostMayBeAnnoyingIgnore: "Publicar de totes maneres" +collapseRenotes: "Col·lapsar les renotes que ja has vist" +internalServerError: "Error intern del servidor" +internalServerErrorDescription: "El servidor ha fallat de manera inexplicable." +copyErrorInfo: "Copiar la informació de l'error " +joinThisServer: "Registra't en aquesta instància " +exploreOtherServers: "Cerca una altra instància " +letsLookAtTimeline: "Dona una ullada a la línia de temps" +disableFederationConfirm: "Vols treure la federació?" +disableFederationConfirmWarn: "Fins i tot traient la federació, les publicacions continuaren sent públiques, a no ser que es digui el contrari. Normalment no has de tocar això." +disableFederationOk: "Desactivar" +invitationRequiredToRegister: "Aquesta instància només permet el registre per invitació. Per registrar-te has d'introduir el codi d'invitació." +emailNotSupported: "Aquesta instància no suporta l'enviament de correus electrònics " +postToTheChannel: "Publicar a un Canal" +cannotBeChangedLater: "Això ja no es podrà canviar." +reactionAcceptance: "Acceptació de reaccions " +likeOnly: "Només m'agraden " +likeOnlyForRemote: "Tot (només m'agraden d'instàncies remotes)" +nonSensitiveOnly: "Només sense contingut sensible" +nonSensitiveOnlyForLocalLikeOnlyForRemote: "Només contingut no sensible (Només m'agraden d'instàncies remotes)" +rolesAssignedToMe: "Rols assignats " +resetPasswordConfirm: "Vols canviar la teva contrasenya?" +sensitiveWords: "Paraules sensibles" +sensitiveWordsDescription: "La visibilitat de totes les notes que continguin qualsevol de les paraules configurades seran, automàticament, afegides a \"Inici\". Pots llistar diferents paraules separant les per línies noves." +myClips: "Els meus retalls" +drivecleaner: "Netejador de Disc" +retryAllQueuesNow: "Prova de nou d'executar totes les cues" +retryAllQueuesConfirmTitle: "Tornar a intentar-ho tot?" +retryAllQueuesConfirmText: "Això farà que la càrrega del servidor augmenti temporalment." +enableChartsForRemoteUser: "Generar gràfiques d'usuaris remots" +enableChartsForFederatedInstances: "Generar gràfiques d'instàncies remotes" +showClipButtonInNoteFooter: "Afegir \"Retall\" al menú d'acció de la nota" +reactionsDisplaySize: "Mida de les reaccions" +accountMoved: "Aquest usuari té un compte nou:" +accountMovedShort: "Aquest compte ha sigut migrat" +operationForbidden: "Operació no permesa " +forceShowAds: "Mostra els anuncis sempre " +addMemo: "Afegir recordatori" +editMemo: "Editar recordatori" +reactionsList: "Reaccions" +renotesList: "Impulsos" +notificationDisplay: "Notificacions" +leftTop: "Dalt a l'esquerra " +rightTop: "Dalt a la dreta " +leftBottom: "A baix a l'esquerra" +rightBottom: "A baix a la dreta" +stackAxis: "Apilar en direcció " +vertical: "Vertical" +horizontal: "Horitzontal " +position: "Posició " +serverRules: "Regles del servidor" +pleaseConfirmBelowBeforeSignup: "Per obrir un compte en aquest servidor, has de llegir i acceptar el següent." +pleaseAgreeAllToContinue: "Has d'acceptar tots els camps de dalt per poder continuar." +continue: "Continuar" +preservedUsernames: "Noms d'usuaris reservats" +preservedUsernamesDescription: "Llistat de noms d'usuaris que no es poden fer servir separats per salts de linia. Aquests noms d'usuaris no estaran disponibles quan es creï un compte d'usuari normal, però els administradors els poden fer servir per crear comptes manualment. Per altre banda els comptes ja creats amb aquests noms d'usuari no es veure'n afectats." +createNoteFromTheFile: "Compon una nota des d'aquest fitxer" +archive: "Arxiu" +channelArchiveConfirmTitle: "Vols arxivar {name}?" +channelArchiveConfirmDescription: "Un Canal arxivat no apareixerà a la llista de canals o als resultats de cerca. Tampoc es poden afegir noves entrades." +thisChannelArchived: "Aquest Canal ha sigut arxivat." +displayOfNote: "Mostrar notes" +initialAccountSetting: "Configuració del perfil" +youFollowing: "Seguit" +preventAiLearning: "Descartar l'ús d'aprenentatge automàtic (IA Generativa)" +preventAiLearningDescription: "Demanar els indexadors no fer servir els texts, imatges, etc. en cap conjunt de dades per alimentar l'aprenentatge automàtic (IA Predictiva/ Generativa). Això s'aconsegueix afegint la etiqueta \"noai\" com a resposta HTML al contingut corresponent. Prevenir aquest ús totalment pot ser que no sigui aconseguit, ja que molts indexadors poden obviar aquesta etiqueta." +options: "Opcions" +specifyUser: "Especificar usuari" +failedToPreviewUrl: "Vista prèvia no disponible" +update: "Actualitzar" +rolesThatCanBeUsedThisEmojiAsReaction: "Rols que poden fer servir aquest emoji com a reacció " +rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "Si cap rol es especificat tothom ho pot fer servir" +rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "Aquests rols han de ser públics " +cancelReactionConfirm: "Vols esborrar la teva reacció?" +changeReactionConfirm: "Vols canviar la teva reacció?" +later: "Més tard" +goToMisskey: "Ves a Misskey" +additionalEmojiDictionary: "Diccionari d'emojis adicionals" +installed: "Instal·lats " +branding: "Marca" +enableServerMachineStats: "Publicar estadístiques del maquinari del servidor" +enableIdenticonGeneration: "Activar la generació d'icones d'identificació " +turnOffToImprovePerformance: "Desactivant aquesta opció es pot millorar el rendiment." icon: "Icona" replies: "Respostes" renotes: "Impulsa" +externalServices: "Serveis externs" +impressum: "Impressum" +impressumUrl: "Adreça URL impressum" +impressumDescription: "A països, com Alemanya, la inclusió de la informació de contacte de l'operador (un Impressum) és requereix de manera legal per llocs comercials." +privacyPolicy: "Política de privacitat" +privacyPolicyUrl: "Adreça URL de la política de privacitat" +tosAndPrivacyPolicy: "Termes d'ús i política de privacitat" +avatarDecorations: "Decoracions dels avatars" +attach: "Adjuntar" +detach: "Eliminar" +detachAll: "Treure tot" +angle: "Angle" +flip: "Girar" +showAvatarDecorations: "Mostrar les decoracions dels avatars" +releaseToRefresh: "Deixar anar per actualitzar" +refreshing: "Recarregant..." +pullDownToRefresh: "Llisca cap a baix per recarregar" +disableStreamingTimeline: "Desactivar l'actualització en temps real de les línies de temps" +useGroupedNotifications: "Mostrar les notificacions agrupades " +signupPendingError: "Hi ha hagut un problema verificant l'adreça de correu electrònic. L'enllaç pot haver caducat." +cwNotationRequired: "Si està activat \"Amagar contingut\" s'ha d'escriure una descripció " +doReaction: "Afegeix una reacció " +code: "Codi" +reloadRequiredToApplySettings: "És necessari recarregar la pàgina per aplicar els canvis." +remainingN: "Queden: {n}" +overwriteContentConfirm: "Vols substituir el contingut actual?" +seasonalScreenEffect: "Efectes de pantalla segons les estacions" +decorate: "Decorar" +addMfmFunction: "Afegeix funcions MFM" +enableQuickAddMfmFunction: "Activar accés ràpid per afegir funcions MFM" +lastNDays: "Últims {n} dies" +_announcement: + forExistingUsers: "Anunci per usuaris registrats" + forExistingUsersDescription: "Aquest avís només es mostrarà als usuaris existents fins al moment de la publicació. Si no també es mostrarà als usuaris que es registrin després de la publicació." + needConfirmationToRead: "Es necessita confirmació de lectura de la notificació " + needConfirmationToReadDescription: "Si s'activa es mostrarà un diàleg per confirmar la lectura d'aquesta notificació. A més aquesta notificació serà exclosa de qualsevol funcionalitat com \"Marcar tot com a llegit\"." + end: "Final de la notificació " + tooManyActiveAnnouncementDescription: "Tenir massa notificacions actives pot empitjorar l'experiència de l'usuari. Considera finalitzar els anuncis que siguin antics." + readConfirmTitle: "Marcar com llegida?" + readConfirmText: "Això marcarà el contingut de \"{title}\" com llegit." + shouldNotBeUsedToPresentPermanentInfo: "Ja que l'ús de notificacions pot impactar l'experiència dels nous usuaris, és recomanable fer servir les notificacions amb el flux d'informació en comptes de fer-les servir en un únic bloc." + dialogAnnouncementUxWarn: "Tenir dues o més notificacions amb l'estil de finestres pot impactar l'experiència de l'usuari, és per això que és recomana fer-lo servir amb cura." + silence: "Sense notificacions" + silenceDescription: "Activant aquesta opció la notificació no es mostrarà ni l'usuari l'haurà de llegir." +_initialAccountSetting: + accountCreated: "S'ha completat la creació del compte!" + letsStartAccountSetup: "Posem ràpidament la configuració inicial del compte." + letsFillYourProfile: "Comencem establint el teu perfil." + profileSetting: "Configuració del perfil" + privacySetting: "Configuració de seguretat" + theseSettingsCanEditLater: "Aquests ajustos es poden canviar més tard." + youCanEditMoreSettingsInSettingsPageLater: "A més d'això, es poden fer diferents configuracions a través de la pàgina de configuració. Assegureu-vos de comprovar-ho més tard." _role: + assignTarget: "Assignar " + priority: "Prioritat" _priority: + low: "Baixa" middle: "Mitjà" + high: "Alta" _options: + canManageCustomEmojis: "Gestiona els emojis personalitzats" + canManageAvatarDecorations: "Gestiona les decoracions dels avatars " antennaMax: "Nombre màxim d'antenes" +_ffVisibility: + public: "Publicar" +_ad: + back: "Tornar" _email: _follow: title: "t'ha seguit" @@ -589,9 +1206,11 @@ _visibility: home: "Inici" followers: "Seguidors" _profile: + name: "Nom" username: "Nom d'usuari" _exportOrImport: allNotes: "Totes les publicacions" + clips: "Retalls" followingList: "Seguint" muteList: "Silencia" blockingList: "Bloqueja" @@ -604,6 +1223,7 @@ _timelines: social: "Social" global: "Global" _play: + script: "Script" summary: "Descripció" _pages: contents: "Contingut" @@ -645,8 +1265,15 @@ _deck: tl: "Línia de temps" antenna: "Antena" list: "Llistes" + channel: "Canals" mentions: "Mencions" direct: "Publicacions directes" +_webhookSettings: + name: "Nom" + active: "Activat" _moderationLogTypes: suspend: "Suspèn" resetPassword: "Restableix la contrasenya" +_reversi: + total: "Total" + diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml index afe613677..241e4cfc7 100644 --- a/locales/cs-CZ.yml +++ b/locales/cs-CZ.yml @@ -366,6 +366,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Aktivovat hCaptchu" hcaptchaSiteKey: "Klíč stránky" hcaptchaSecretKey: "Tajný Klíč (Secret Key)" +mcaptchaSiteKey: "Klíč stránky" +mcaptchaSecretKey: "Tajný Klíč (Secret Key)" recaptcha: "reCAPTCHA" enableRecaptcha: "Zapnout ReCAPTCHu" recaptchaSiteKey: "Klíč stránky" @@ -1093,6 +1095,7 @@ icon: "Avatar" replies: "Odpovědi" renotes: "Přeposlat" flip: "Otočit" +lastNDays: "Posledních {n} dnů" _initialAccountSetting: accountCreated: "Váš účet byl úspěšně vytvořen!" letsStartAccountSetup: "Pro začátek si nastavte svůj profil." @@ -1825,6 +1828,7 @@ _profile: _exportOrImport: allNotes: "Všechny poznámky" favoritedNotes: "Oblíbené poznámky" + clips: "Oříznout" followingList: "Sledovaní" muteList: "Ztlumit" blockingList: "Zablokovat" @@ -2016,3 +2020,6 @@ _moderationLogTypes: suspend: "Zmrazit" resetPassword: "Resetovat heslo" createInvitation: "Vygenerovat pozvánku" +_reversi: + total: "Celkem" + diff --git a/locales/da-DK.yml b/locales/da-DK.yml index 08c15ed09..d1fbec9f6 100644 --- a/locales/da-DK.yml +++ b/locales/da-DK.yml @@ -1,2 +1,3 @@ --- _lang_: "Dansk" + diff --git a/locales/de-DE.yml b/locales/de-DE.yml index fd25ba028..0cbd3db0a 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -121,9 +121,12 @@ sensitive: "Sensibel" add: "Hinzufügen" reaction: "Reaktionen" reactions: "Reaktionen" +emojiPicker: "Emoji auswählen" +pinnedEmojisForReactionSettingDescription: "Wähle die Emojis aus, um sie an zu pinnen" reactionSettingDescription2: "Ziehe um Anzuordnen, klicke um zu löschen, drücke „+“ um hinzuzufügen" rememberNoteVisibility: "Notizsichtbarkeit merken" attachCancel: "Anhang entfernen" +deleteFile: "Datei gelöscht" markAsSensitive: "Als sensibel markieren" unmarkAsSensitive: "Als nicht sensibel markieren" enterFileName: "Dateinamen eingeben" @@ -372,6 +375,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptcha aktivieren" hcaptchaSiteKey: "Site key" hcaptchaSecretKey: "Secret key" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret key" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA aktivieren" recaptchaSiteKey: "Site key" @@ -619,6 +624,7 @@ medium: "Mittel" small: "Klein" generateAccessToken: "Zugriffstoken generieren" permission: "Berechtigungen" +adminPermission: "Administratorberechtigung" enableAll: "Alle aktivieren" disableAll: "Alle deaktivieren" tokenRequested: "Zugriff zum Benutzerkonto gewähren" @@ -1168,6 +1174,7 @@ signupPendingError: "Beim Überprüfen der Mailadresse ist etwas schiefgelaufen. cwNotationRequired: "Ist \"Inhaltswarnung verwenden\" aktiviert, muss eine Beschreibung gegeben werden." doReaction: "Reagieren" code: "Code" +lastNDays: "Letzten {n} Tage" _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." @@ -1955,6 +1962,7 @@ _profile: _exportOrImport: allNotes: "Alle Notizen" favoritedNotes: "Als Favorit markierte Notizen" + clips: "Clip erstellen" followingList: "Gefolgte Benutzer" muteList: "Stummschaltungen" blockingList: "Blockierungen" @@ -2237,3 +2245,6 @@ _externalResourceInstaller: _themeInstallFailed: title: "Das Farbschema konnte nicht installiert werden" description: "Während der Installation des Farbschemas ist ein Problem aufgetreten. Bitte versuche es erneut. Detaillierte Fehlerinformationen können über die Javascript-Konsole abgerufen werden." +_reversi: + total: "Gesamt" + diff --git a/locales/el-GR.yml b/locales/el-GR.yml index 30a52b726..bb5639a74 100644 --- a/locales/el-GR.yml +++ b/locales/el-GR.yml @@ -356,6 +356,7 @@ _profile: username: "Όνομα μέλους" _exportOrImport: allNotes: "Όλα τα σημειώματα" + clips: "Κλιπ" followingList: "Ακολουθεί" muteList: "Μέλη σε σίγαση" blockingList: "Μπλοκαρισμένα μέλη" @@ -395,3 +396,6 @@ _webhookSettings: name: "Όνομα" _moderationLogTypes: suspend: "Αποβολή" +_reversi: + total: "Σύνολο" + diff --git a/locales/en-US.yml b/locales/en-US.yml index 7a28ce109..6c1cfc994 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -134,6 +134,7 @@ overwriteFromPinnedEmojis: "Override from general settings" reactionSettingDescription2: "Drag to reorder, click to delete, press \"+\" to add." rememberNoteVisibility: "Remember note visibility settings" attachCancel: "Remove attachment" +deleteFile: "File deleted" markAsSensitive: "Mark as sensitive" unmarkAsSensitive: "Unmark as sensitive" enterFileName: "Enter filename" @@ -390,6 +391,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Enable hCaptcha" hcaptchaSiteKey: "Site key" hcaptchaSecretKey: "Secret key" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret key" recaptcha: "reCAPTCHA" enableRecaptcha: "Enable reCAPTCHA" recaptchaSiteKey: "Site key" @@ -1226,6 +1229,10 @@ seasonalScreenEffect: "Seasonal screen effects" decorate: "Decorate" addMfmFunction: "Add MFM" enableQuickAddMfmFunction: "Show advanced MFM picker" +bubbleGame: "Bubble Game" +sfx: "Sound Effects" +replay: "Replay" +lastNDays: "Last {n} days" _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." @@ -1596,6 +1603,10 @@ _achievements: _tutorialCompleted: title: "Sharkey Elementary Course Diploma" description: "Tutorial completed" + _bubbleGameExplodingHead: + title: "🤯" + _bubbleGameDoubleExplodingHead: + title: "Double🤯" _role: new: "New role" edit: "Edit role" @@ -2160,6 +2171,7 @@ _profile: _exportOrImport: allNotes: "All notes" favoritedNotes: "Favorite notes" + clips: "Clip" followingList: "Followed users" muteList: "Muted users" blockingList: "Blocked users" @@ -2547,3 +2559,6 @@ _dataSaver: _code: title: "Code highlighting" description: "If code highlighting notations are used in MFM, etc., they will not load until tapped. Syntax highlighting requires downloading the highlight definition files for each programming language. Therefore, disabling the automatic loading of these files is expected to reduce the amount of communication data." +_reversi: + total: "Total" + diff --git a/locales/es-ES.yml b/locales/es-ES.yml index c269cc4d7..e8e9761d2 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "Sobreescribir los emojis fijados" reactionSettingDescription2: "Arrastre para reordenar, click para borrar, apriete la tecla + para añadir." rememberNoteVisibility: "Recordar visibilidad" attachCancel: "Quitar adjunto" +deleteFile: "Archivo eliminado" markAsSensitive: "Marcar como sensible" unmarkAsSensitive: "Desmarcar como sensible" enterFileName: "Ingrese el nombre del archivo" @@ -379,6 +380,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Habilitar hCaptcha" hcaptchaSiteKey: "Clave del sitio" hcaptchaSecretKey: "Clave secreta" +mcaptcha: "mCaptcha" +enableMcaptcha: "Activar mCaptcha" +mcaptchaSiteKey: "Clave del sitio" +mcaptchaSecretKey: "Clave secreta" +mcaptchaInstanceUrl: "URL del servidor mCaptcha" recaptcha: "reCAPTCHA" enableRecaptcha: "activar reCAPTCHA" recaptchaSiteKey: "Clave del sitio" @@ -626,6 +632,7 @@ medium: "Mediano" small: "Pequeño" generateAccessToken: "Generar token de acceso" permission: "Permisos" +adminPermission: "Permiso de administrador" enableAll: "Activar todo" disableAll: "Desactivar todo" tokenRequested: "Permiso de acceso a la cuenta" @@ -669,6 +676,7 @@ useGlobalSettingDesc: "Al activarse, se usará la configuración de notificacion other: "Otro" regenerateLoginToken: "Regenerar token de login" regenerateLoginTokenDescription: "Regenerar el token usado internamente durante el login. No siempre es necesario hacerlo. Al hacerlo de nuevo, se deslogueará en todos los dispositivos." +theKeywordWhenSearchingForCustomEmoji: "Palabra clave para buscar el emoji personalizado." setMultipleBySeparatingWithSpace: "Puedes añadir mas de uno, separado por espacios." fileIdOrUrl: "Id del archivo o URL" behavior: "Comportamiento" @@ -1181,6 +1189,18 @@ remainingN: "Faltan: {n}" overwriteContentConfirm: "¿Quieres sustituir todo el contenido actual?" seasonalScreenEffect: "Efectos de pantalla asociados a estaciones" decorate: "Decorar" +addMfmFunction: "Añadir función MFM" +enableQuickAddMfmFunction: "Activar acceso rápido para añadir funciones MFM" +bubbleGame: "Bubble Game" +sfx: "Efectos de sonido" +soundWillBePlayed: "Se reproducirán efector sonoros" +showReplay: "Ver reproducción" +replay: "Reproducir" +replaying: "Reproduciendo" +ranking: "Clasificación" +lastNDays: "Últimos {n} días" +_bubbleGame: + howToPlay: "Cómo jugar" _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." @@ -1551,6 +1571,10 @@ _achievements: _tutorialCompleted: title: "Diploma del Curso Básico de Misskey" description: "Tutorial completado" + _bubbleGameExplodingHead: + title: "🤯" + _bubbleGameDoubleExplodingHead: + title: "Doble 🤯" _role: new: "Crear rol" edit: "Editar rol" @@ -1933,6 +1957,54 @@ _permissions: "write:flash": "Editar Plays" "read:flash-likes": "Ver los Play que me gustan" "write:flash-likes": "Editar lista de Play que me gustan" + "read:admin:abuse-user-reports": "Ver reportes de usuarios" + "write:admin:delete-account": "Eliminar cuentas de usuario" + "write:admin:delete-all-files-of-a-user": "Eliminar todos los archivos de un usuario" + "read:admin:index-stats": "Ver datos indexados" + "read:admin:user-ips": "Ver dirección IP de usuario" + "read:admin:meta": "Ver metadatos de la instancia" + "write:admin:reset-password": "Restablecer contraseñas de usuario" + "write:admin:resolve-abuse-user-report": "Resolución de reportes de usuario" + "write:admin:send-email": "Enviar email" + "read:admin:server-info": "Ver información del servidor" + "read:admin:show-moderation-log": "Ver log de moderación" + "read:admin:show-user": "Ver información privada de usuario" + "read:admin:show-users": "Ver información privada de usuario" + "write:admin:suspend-user": "Suspender cuentas de usuario" + "write:admin:unset-user-avatar": "Quitar avatares de usuario" + "write:admin:unset-user-banner": "Quitar banner de usuarios" + "write:admin:unsuspend-user": "Quitar suspensión de cuentas de usuario" + "write:admin:meta": "Edición de metadatos de la instancia" + "write:admin:user-note": "Moderación de notas" + "write:admin:roles": "Edición de roles de usuario" + "read:admin:roles": "Ver roles de usuario" + "write:admin:relays": "Edición de relays" + "read:admin:relays": "Ver relays" + "write:admin:invite-codes": "Edición de códigos de invitación" + "read:admin:invite-codes": "Ver códigos de invitación" + "write:admin:announcements": "Edición de anuncios" + "read:admin:announcements": "Ver anuncios" + "write:admin:avatar-decorations": "Edición de decoración de avatares" + "read:admin:avatar-decorations": "Ver decoraciones de avatar" + "write:admin:federation": "Edición de federación de instancias" + "write:admin:account": "Edición de cuentas de usuario" + "read:admin:account": "Ver cuentas de usuario" + "write:admin:emoji": "Edición de emojis" + "read:admin:emoji": "Ver emojis" + "write:admin:queue": "Edición de cola de tareas" + "read:admin:queue": "Ver cola de tareas" + "write:admin:promo": "Edición de promociones" + "write:admin:drive": "Edición de Drive de usuarios" + "read:admin:drive": "Ver Drive de usuarios" + "read:admin:stream": "Usar la API de Websocket para administradores" + "write:admin:ad": "Edición de anuncios" + "read:admin:ad": "Ver anuncios" + "write:invite-codes": "Crear códigos de invitación" + "read:invite-codes": "Ver códigos de invitación" + "write:clip-favorite": "Marcar me gusta en clips" + "read:clip-favorite": "Ver los clips que me gustan" + "read:federation": "Ver instancias federadas" + "write:report-abuse": "Crear reportes de usuario" _auth: shareAccessTitle: "Permisos de la aplicación" shareAccess: "¿Desea permitir el acceso a la cuenta \"{name}\"?" @@ -2055,6 +2127,7 @@ _profile: _exportOrImport: allNotes: "Todas las notas" favoritedNotes: "Notas favoritas" + clips: "Clip" followingList: "Siguiendo" muteList: "Silenciados" blockingList: "Bloqueados" @@ -2354,3 +2427,6 @@ _dataSaver: _code: title: "Resaltar código" description: "Si se usa resaltado de código en MFM, etc., no se cargará hasta pulsar en ello. El resaltado de sintaxis requiere la descarga de archivos de definición para cada lenguaje de programación. Debido a esto, al deshabilitar la carga automática de estos archivos reducirás el consumo de datos." +_reversi: + total: "Total" + diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml index ac9e94a01..c38616d22 100644 --- a/locales/fr-FR.yml +++ b/locales/fr-FR.yml @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "Remplacer par les émojis épinglés globalement" reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser « + » pour ajouter." rememberNoteVisibility: "Se souvenir de la visibilité des notes" attachCancel: "Supprimer le fichier attaché" +deleteFile: "Fichier supprimé" markAsSensitive: "Marquer comme sensible" unmarkAsSensitive: "Supprimer le marquage comme sensible" enterFileName: "Entrer le nom du fichier" @@ -168,7 +169,7 @@ cacheRemoteSensitiveFilesDescription: "Si vous désactivez ce paramètre, les fi flagAsBot: "Ce compte est un robot" flagAsBotDescription: "Si ce compte est géré de manière automatisée, choisissez cette option. Si elle est activée, elle agira comme un marqueur pour les autres développeurs afin d'éviter des chaînes d'interaction sans fin avec d'autres robots et d'ajuster les systèmes internes de Misskey pour traiter ce compte comme un robot." flagAsCat: "Ce compte est un chat" -flagAsCatDescription: "Activer l'option \" Je suis un chat \" pour ce compte." +flagAsCatDescription: "Miaou miaou miaou ?" flagShowTimelineReplies: "Afficher les réponses dans le fil" flagShowTimelineRepliesDescription: "Affiche les réponses des utilisateurs aux notes des autres utilisateurs dans la timeline si cette option est activée." autoAcceptFollowed: "Accepter automatiquement les demandes d’abonnement venant d’utilisateur·rice·s que vous suivez" @@ -379,6 +380,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Activer hCaptcha" hcaptchaSiteKey: "Clé du site" hcaptchaSecretKey: "Clé secrète" +mcaptchaSiteKey: "Clé du site" +mcaptchaSecretKey: "Clé secrète" recaptcha: "reCAPTCHA" enableRecaptcha: "Activer reCAPTCHA" recaptchaSiteKey: "Clé du site" @@ -696,7 +699,7 @@ system: "Système" switchUi: "Modifier l'interface utilisateur" desktop: "Bureau" clip: "Clip" -createNew: "Créer nouveau" +createNew: "Créer" optional: "Facultatif" createNewClip: "Créer un nouveau clip" unclip: "Supprimer le clip" @@ -1081,12 +1084,24 @@ specifyUser: "Spécifier l'utilisateur·rice" failedToPreviewUrl: "Aperçu d'URL échoué" update: "Mettre à jour" rolesThatCanBeUsedThisEmojiAsReaction: "Rôles qui peuvent utiliser cet émoji comme réaction" +cancelReactionConfirm: "Supprimez la réaction ?" later: "Plus tard" goToMisskey: "Retour vers Misskey" additionalEmojiDictionary: "Dictionnaires d'émojis additionnels" installed: "Installé" branding: "Image de marque" +enableServerMachineStats: "Publier les statistiques du matériel du serveur" +enableIdenticonGeneration: "Générer les identicons des utilisateurs" +turnOffToImprovePerformance: "Désactiver peut améliorer la performance." +createInviteCode: "Créer un code d'invitation" +createWithOptions: "Options" +createCount: "Quantité à créer" +inviteCodeCreated: "Code d'invitation créé" +inviteLimitExceeded: "Vous avez atteint la limite de codes d'invitation que vous pouvez générer." expirationDate: "Date d’expiration" +noExpirationDate: "Ne pas expirer" +inviteCodeUsedAt: "Code d'invitation utilisé à" +registeredUserUsingInviteCode: "Code d'invitation utilisé par" waitingForMailAuth: "En attente de la vérification de l'adresse courriel" inviteCodeCreator: "Créateur·rice de ce code d'invitation" usedAt: "Utilisé le" @@ -1106,6 +1121,7 @@ loadReplies: "Inclure les réponses" loadConversation: "Afficher la conversation" pinnedList: "Liste épinglée" notifyNotes: "Notifier à propos des nouvelles notes" +unnotifyNotes: "Ne pas notifier pour la publication des notes" authentication: "Authentification" authenticationRequiredToContinue: "Veuillez vous authentifier pour continuer" dateAndTime: "Date et heure" @@ -1147,7 +1163,9 @@ remainingN: "Restants : {n}" overwriteContentConfirm: "Voulez-vous remplacer le contenu actuel ?" seasonalScreenEffect: "Effet d'écran saisonnier" decorate: "Décorer" +lastNDays: "Derniers {n} jours" _announcement: + forExistingUsers: "Pour les utilisateurs existants seulement" readConfirmTitle: "Marquer comme lu ?" shouldNotBeUsedToPresentPermanentInfo: "Puisque cela pourrait nuire considérablement à l'expérience utilisateur pour les nouveaux utilisateurs, il est recommandé d'utiliser les annonces pour afficher des informations temporaires plutôt que des informations persistantes." dialogAnnouncementUxWarn: "Avoir deux ou plus annonces de style dialogue en même temps pourrait nuire considérablement à l'expérience utilisateur. Veuillez les utiliser avec caution." @@ -1242,6 +1260,7 @@ _accountMigration: startMigration: "Migrer" movedTo: "Compte vers lequel vous migrez :" _achievements: + earnedAt: "Date d'obtention" _types: _notes1: title: "Je viens tout juste de configurer mon shonk" @@ -1799,6 +1818,7 @@ _profile: avatarDecorationMax: "Vous pouvez mettre au plus {max} décorations d'avatar." _exportOrImport: allNotes: "Toutes les notes" + clips: "Clip" followingList: "Abonnements" muteList: "Comptes masqués" blockingList: "Comptes bloqués" @@ -2063,3 +2083,6 @@ _dataSaver: _code: title: "Mise en évidence du code" description: "Si la notation de mise en évidence du code est utilisée, par exemple dans la MFM, elle ne sera pas chargée tant qu'elle n'aura pas été tapée. La mise en évidence du code nécessite le chargement du fichier de définition de chaque langue à mettre en évidence, mais comme ces fichiers ne sont plus chargés automatiquement, on peut s'attendre à une réduction du trafic de données." +_reversi: + total: "Total" + diff --git a/locales/generateDTS.js b/locales/generateDTS.js index d3afdd6e1..49807144e 100644 --- a/locales/generateDTS.js +++ b/locales/generateDTS.js @@ -6,54 +6,176 @@ import ts from 'typescript'; const __filename = fileURLToPath(import.meta.url); const __dirname = dirname(__filename); +const parameterRegExp = /\{(\w+)\}/g; + +function createMemberType(item) { + if (typeof item !== 'string') { + return ts.factory.createTypeLiteralNode(createMembers(item)); + } + const parameters = Array.from( + item.matchAll(parameterRegExp), + ([, parameter]) => parameter, + ); + return parameters.length + ? ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('ParameterizedString'), + [ + ts.factory.createUnionTypeNode( + parameters.map((parameter) => + ts.factory.createStringLiteral(parameter), + ), + ), + ], + ) + : ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword); +} function createMembers(record) { - return Object.entries(record) - .map(([k, v]) => ts.factory.createPropertySignature( + return Object.entries(record).map(([k, v]) => { + const node = ts.factory.createPropertySignature( undefined, ts.factory.createStringLiteral(k), undefined, - typeof v === 'string' - ? ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword) - : ts.factory.createTypeLiteralNode(createMembers(v)), - )); + createMemberType(v), + ); + if (typeof v === 'string') { + ts.addSyntheticLeadingComment( + node, + ts.SyntaxKind.MultiLineCommentTrivia, + `* + * ${v.replace(/\n/g, '\n * ')} + `, + true, + ); + } + return node; + }); } export default function generateDTS() { const locale = yaml.load(fs.readFileSync(`${__dirname}/ja-JP.yml`, 'utf-8')); const members = createMembers(locale); const elements = [ + ts.factory.createVariableStatement( + [ts.factory.createToken(ts.SyntaxKind.DeclareKeyword)], + ts.factory.createVariableDeclarationList( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier('kParameters'), + undefined, + ts.factory.createTypeOperatorNode( + ts.SyntaxKind.UniqueKeyword, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.SymbolKeyword), + ), + undefined, + ), + ], + ts.NodeFlags.Const, + ), + ), + ts.factory.createInterfaceDeclaration( + [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], + ts.factory.createIdentifier('ParameterizedString'), + [ + ts.factory.createTypeParameterDeclaration( + undefined, + ts.factory.createIdentifier('T'), + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ), + ], + undefined, + [ + ts.factory.createPropertySignature( + undefined, + ts.factory.createComputedPropertyName( + ts.factory.createIdentifier('kParameters'), + ), + undefined, + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('T'), + undefined, + ), + ), + ], + ), + ts.factory.createInterfaceDeclaration( + [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], + ts.factory.createIdentifier('ILocale'), + undefined, + undefined, + [ + ts.factory.createIndexSignature( + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + ts.factory.createIdentifier('_'), + undefined, + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + undefined, + ), + ], + ts.factory.createUnionTypeNode([ + ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('ParameterizedString'), + ), + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('ILocale'), + undefined, + ), + ]), + ), + ], + ), ts.factory.createInterfaceDeclaration( [ts.factory.createToken(ts.SyntaxKind.ExportKeyword)], ts.factory.createIdentifier('Locale'), undefined, - undefined, + [ + ts.factory.createHeritageClause(ts.SyntaxKind.ExtendsKeyword, [ + ts.factory.createExpressionWithTypeArguments( + ts.factory.createIdentifier('ILocale'), + undefined, + ), + ]), + ], members, ), ts.factory.createVariableStatement( [ts.factory.createToken(ts.SyntaxKind.DeclareKeyword)], ts.factory.createVariableDeclarationList( - [ts.factory.createVariableDeclaration( - ts.factory.createIdentifier('locales'), - undefined, - ts.factory.createTypeLiteralNode([ts.factory.createIndexSignature( + [ + ts.factory.createVariableDeclaration( + ts.factory.createIdentifier('locales'), undefined, - [ts.factory.createParameterDeclaration( - undefined, - undefined, - ts.factory.createIdentifier('lang'), - undefined, - ts.factory.createKeywordTypeNode(ts.SyntaxKind.StringKeyword), - undefined, - )], - ts.factory.createTypeReferenceNode( - ts.factory.createIdentifier('Locale'), - undefined, - ), - )]), - undefined, - )], - ts.NodeFlags.Const | ts.NodeFlags.Ambient | ts.NodeFlags.ContextFlags, + ts.factory.createTypeLiteralNode([ + ts.factory.createIndexSignature( + undefined, + [ + ts.factory.createParameterDeclaration( + undefined, + undefined, + ts.factory.createIdentifier('lang'), + undefined, + ts.factory.createKeywordTypeNode( + ts.SyntaxKind.StringKeyword, + ), + undefined, + ), + ], + ts.factory.createTypeReferenceNode( + ts.factory.createIdentifier('Locale'), + undefined, + ), + ), + ]), + undefined, + ), + ], + ts.NodeFlags.Const, ), ), ts.factory.createFunctionDeclaration( @@ -70,16 +192,39 @@ export default function generateDTS() { ), ts.factory.createExportDefault(ts.factory.createIdentifier('locales')), ]; - const printed = ts.createPrinter({ - newLine: ts.NewLineKind.LineFeed, - }).printList( - ts.ListFormat.MultiLine, - ts.factory.createNodeArray(elements), - ts.createSourceFile('index.d.ts', '', ts.ScriptTarget.ESNext, true, ts.ScriptKind.TS), + ts.addSyntheticLeadingComment( + elements[0], + ts.SyntaxKind.MultiLineCommentTrivia, + ' eslint-disable ', + true, ); + ts.addSyntheticLeadingComment( + elements[0], + ts.SyntaxKind.SingleLineCommentTrivia, + ' This file is generated by locales/generateDTS.js', + true, + ); + ts.addSyntheticLeadingComment( + elements[0], + ts.SyntaxKind.SingleLineCommentTrivia, + ' Do not edit this file directly.', + true, + ); + const printed = ts + .createPrinter({ + newLine: ts.NewLineKind.LineFeed, + }) + .printList( + ts.ListFormat.MultiLine, + ts.factory.createNodeArray(elements), + ts.createSourceFile( + 'index.d.ts', + '', + ts.ScriptTarget.ESNext, + true, + ts.ScriptKind.TS, + ), + ); - fs.writeFileSync(`${__dirname}/index.d.ts`, `/* eslint-disable */ -// This file is generated by locales/generateDTS.js -// Do not edit this file directly. -${printed}`, 'utf-8'); + fs.writeFileSync(`${__dirname}/index.d.ts`, printed, 'utf-8'); } diff --git a/locales/hr-HR.yml b/locales/hr-HR.yml index 9cfebdd01..881aa8464 100644 --- a/locales/hr-HR.yml +++ b/locales/hr-HR.yml @@ -3,3 +3,4 @@ _lang_: "japanski" ok: "OK" gotIt: "Razumijem" cancel: "otkazati" + diff --git a/locales/ht-HT.yml b/locales/ht-HT.yml index e3595c79b..1698c9f28 100644 --- a/locales/ht-HT.yml +++ b/locales/ht-HT.yml @@ -16,3 +16,4 @@ _2fa: renewTOTPCancel: "Sispann" _widgets: profile: "pwofil" + diff --git a/locales/hu-HU.yml b/locales/hu-HU.yml index 023a91494..2f7006484 100644 --- a/locales/hu-HU.yml +++ b/locales/hu-HU.yml @@ -102,3 +102,4 @@ _deck: _columns: notifications: "Értesítések" tl: "Idővonal" + diff --git a/locales/id-ID.yml b/locales/id-ID.yml index 00844550f..4093dee6d 100644 --- a/locales/id-ID.yml +++ b/locales/id-ID.yml @@ -125,9 +125,12 @@ emojiPicker: "Emoji Picker" pinnedEmojisForReactionSettingDescription: "Atur sematan emoji pada reaksi" pinnedEmojisSettingDescription: "Atur sematan emoji pada masukan emoji" emojiPickerDisplay: "Tampilan Emoji Picker" +overwriteFromPinnedEmojisForReaction: "Timpa dari pengaturan reaksi" +overwriteFromPinnedEmojis: "Timpa dari pengaturan umum" reactionSettingDescription2: "Geser untuk memindah urutan emoji, klik untuk menghapus, tekan \"+\" untuk menambahkan" rememberNoteVisibility: "Ingat pengaturan visibilitas catatan" attachCancel: "Hapus lampiran" +deleteFile: "Berkas dihapus" markAsSensitive: "Tandai sebagai konten sensitif" unmarkAsSensitive: "Hapus tanda konten sensitif" enterFileName: "Masukkan nama berkas" @@ -377,6 +380,9 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Nyalakan hCaptcha" hcaptchaSiteKey: "Site Key" hcaptchaSecretKey: "Secret Key" +mcaptcha: "mCaptcha" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret Key" recaptcha: "reCAPTCHA" enableRecaptcha: "Nyalakan reCAPTCHA" recaptchaSiteKey: "Site key" @@ -667,6 +673,7 @@ useGlobalSettingDesc: "Jika dinyalakan, setelan notifikasi akun kamu akan diguna other: "Lainnya" regenerateLoginToken: "Perbarui token login" regenerateLoginTokenDescription: "Perbarui token yang digunakan secara internal saat login. Normalnya aksi ini tidak diperlukan. Jika diperbarui, semua perangkat akan dilogout." +theKeywordWhenSearchingForCustomEmoji: "Kata kunci ini digunakan untuk mencari emoji kustom yang dicari." setMultipleBySeparatingWithSpace: "Kamu dapat menyetel banyak dengan memisahkannya menggunakan spasi." fileIdOrUrl: "File-ID atau URL" behavior: "Perilaku" @@ -879,6 +886,8 @@ makeReactionsPublicDescription: "Pengaturan ini akan membuat daftar dari semua r classic: "Klasik" muteThread: "Bisukan thread" unmuteThread: "Suarakan thread" +followingVisibility: "Visibilitas mengikuti" +followersVisibility: "Visibilitas pengikut" continueThread: "Lihat lanjutan thread" deleteAccountConfirm: "Akun akan dihapus. Apakah kamu yakin?" incorrectPassword: "Kata sandi salah." @@ -1174,7 +1183,17 @@ doReaction: "Tambahkan reaksi" code: "Kode" reloadRequiredToApplySettings: "Muat ulang diperlukan untuk menerapkan pengaturan." remainingN: "Sisa : {n}" +overwriteContentConfirm: "Apakah kamu yakin untuk menimpa konten saat ini?" +seasonalScreenEffect: "Efek layar musiman" decorate: "Dekor" +addMfmFunction: "Tambahkan dekorasi" +enableQuickAddMfmFunction: "Tampilkan pemilih MFM tingkat lanjut" +bubbleGame: "Bubble Game" +sfx: "Efek Suara" +lastNDays: "{n} hari terakhir" +backToTitle: "Ke Judul" +_bubbleGame: + howToPlay: "Cara bermain" _announcement: forExistingUsers: "Hanya pengguna yang telah ada" forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya." @@ -1184,7 +1203,10 @@ _announcement: tooManyActiveAnnouncementDescription: "Terlalu banyak pengumuman dapat memperburuk pengalaman pengguna. Mohon pertimbangkan untuk mengarsipkan pengumuman yang sudah usang/tidak relevan." readConfirmTitle: "Tandai telah dibaca?" readConfirmText: "Aksi ini akan menandai konten dari \"{title}\" telah dibaca." + shouldNotBeUsedToPresentPermanentInfo: "Karena dapat berdampak pada pengalaman pengguna untuk pengguna baru, sangat direkomendasikan untuk menggunakan notifikasi secara mengalir daripada tetap." + dialogAnnouncementUxWarn: "Memiliki dua atau lebih gaya dialog notifikasi secara bersamaan dapat berdampak signifikan pada pengalaman pengguna, mohon untuk menggunakannya dengan hati-hati." silence: "Tiada notifikasi" + silenceDescription: "Apabila diaktifkan, notifikasi dari pengumuman ini akan dilewatkan dan pengguna tidak perlu membacanya." _initialAccountSetting: accountCreated: "Akun kamu telah sukses dibuat!" letsStartAccountSetup: "Untuk pemula, ayo atur profilmu dulu." @@ -1197,6 +1219,7 @@ _initialAccountSetting: pushNotificationDescription: "Menyalakan notifikasi dorong akan membuatmu menerima notifikasi dari {name} secara langsung ke perangkatmu." initialAccountSettingCompleted: "Pengaturan profil selesai!" haveFun: "Selamat menikmati, {name}!" + youCanContinueTutorial: "Kamu dapat menjutkan ke tutorial dalam bagaimana menggunakan {name} (Misskey) atau kamu dapat keluar dari pemasangan ini dan langsung menggunakannya segera." startTutorial: "Mulai Tutorial" skipAreYouSure: "Yakin melewati atur profil?" laterAreYouSure: "Yakin banget untuk atur profil nanti?" @@ -1210,6 +1233,10 @@ _initialTutorial: description: "Di sini kamu dapat mempelajari dasar-dasar dari penggunaan Misskey dan fitur-fiturnya." _note: title: "Apa itu Catatan?" + description: "Postingan di Misskey disebut sebagai 'Catatan'. Catatan ditampilkan secara kronologis pada lini masa dan dimutakhirkan secara real-time." + reply: "Klik pada tombol ini untuk membalas ke sebuah pesan. Bisa juga untuk membalas ke sebuah balasan dan melanjutkannya seperti percakapan selayaknya utas." + renote: "Kamu dapat membagikan catatan ke lini masa milikmu. Kamu juga dapat mengutipnya dengan komentarmu." + reaction: "Kamu dapat menambahkan reaksi ke Catatan. Detil lebih lanjut akan dijelaskan di halaman berikutnya." _reaction: title: "Apa itu Reaksi?" _timeline: @@ -1774,6 +1801,14 @@ _sfx: notification: "Notifikasi" antenna: "Penerimaan Antenna" channel: "Notifikasi Kanal" + reaction: "Ketika memilih reaksi" +_soundSettings: + driveFile: "Menggunakan berkas audio dalam Drive" + driveFileWarn: "Pilih berkas audio dari Drive" + driveFileTypeWarn: "Berkas ini tidak didukung" + driveFileTypeWarnDescription: "Pilih berkas audio" + driveFileDurationWarn: "Audio ini terlalu panjang" + driveFileDurationWarnDescription: "Audio panjang dapat mengganggu penggunaan Misskey. Masih ingin melanjutkan?" _ago: future: "Masa depan" justNow: "Baru saja" @@ -1785,6 +1820,14 @@ _ago: monthsAgo: "{n} bulan lalu" yearsAgo: "{n} tahun lalu" invalid: "Tidak ada sama sekali disini" +_timeIn: + seconds: "dalam {n} detik" + minutes: "dalam {n} menit" + hours: "dalam {n} jam" + days: "dalam {n} hari" + weeks: "dalam {n} minggu" + months: "dalam {n} bulan" + years: "dalam {n} tahun" _time: second: "detik" minute: "menit" @@ -1972,9 +2015,11 @@ _profile: changeAvatar: "Ubah avatar" changeBanner: "Ubah header" verifiedLinkDescription: "Dengan memasukkan URL yang mengandung tautan ke profil kamu di sini, ikon verifikasi kepemilikan dapat ditampilkan di sebelah kolom ini." + avatarDecorationMax: "Dapat ditambahkan hingga {max} dekorasi." _exportOrImport: allNotes: "Semua catatan" favoritedNotes: "Catatan favorit" + clips: "Klip" followingList: "Ikuti" muteList: "Bisukan" blockingList: "Blokir" @@ -2093,12 +2138,16 @@ _notification: pollEnded: "Hasil Kuesioner telah keluar" newNote: "Catatan baru" unreadAntennaNote: "Antena {name}" + roleAssigned: "Peran Diberikan" emptyPushNotificationMessage: "Pembaruan notifikasi dorong" achievementEarned: "Pencapaian didapatkan" testNotification: "Tes notifikasi" checkNotificationBehavior: "Cek tampilan notifikasi" sendTestNotification: "Kirim tes notifikasi" notificationWillBeDisplayedLikeThis: "Notifikasi akan terlihat seperti ini" + reactedBySomeUsers: "{n} orang memberikan reaksi" + renotedBySomeUsers: "{n} orang telah merenote" + followedBySomeUsers: "{n} orang telah mengikuti" _types: all: "Semua" note: "Catatan baru" @@ -2111,6 +2160,7 @@ _notification: pollEnded: "Jajak pendapat berakhir" receiveFollowRequest: "Permintaan mengikuti diterima" followRequestAccepted: "Permintaan mengikuti disetujui" + roleAssigned: "Peran Diberikan" achievementEarned: "Pencapaian didapatkan" app: "Notifikasi dari aplikasi tertaut" _actions: @@ -2202,6 +2252,11 @@ _moderationLogTypes: createAd: "Iklan telah dibuat" deleteAd: "Iklan telah dihapus" updateAd: "Iklan telah diperbaharui" + createAvatarDecoration: "Buat dekorasi avatar" + updateAvatarDecoration: "Perbarui dekorasi avatar" + deleteAvatarDecoration: "Hapus dekorasi avatar" + unsetUserAvatar: "Hapus avatar pengguna" + unsetUserBanner: "Hapus banner pengguna" _fileViewer: title: "Rincian berkas" type: "Jenis berkas" @@ -2210,3 +2265,60 @@ _fileViewer: uploadedAt: "Diunggah pada" attachedNotes: "Catatan yang dilampirkan" thisPageCanBeSeenFromTheAuthor: "Halaman ini hanya dapat dilihat oleh pengguna yang mengunggah bekas ini." +_externalResourceInstaller: + title: "Pasang dari situs eksternal" + checkVendorBeforeInstall: "Pastikan sumber dari sumber daya ini terpercaya sebelum melakukan pemasangan." + _plugin: + title: "Apakah kamu ingin memasang plugin ini?" + metaTitle: "Informasi plugin" + _theme: + title: "Apakah kamu ingin memasang tema ini?" + metaTitle: "Informasi tema" + _meta: + base: "Skema warna dasar" + _vendorInfo: + title: "Informasi sumber" + endpoint: "Referensi Endpoint" + hashVerify: "Verifikasi hash" + _errors: + _invalidParams: + title: "Parameter tidak valid" + description: "Tidak cukup informasi untuk memuat data dari situs eksternal. Mohon konfirmasi kembali URL yang dimasukkan." + _resourceTypeNotSupported: + title: "Sumber daya eksternal ini tidak didukung" + description: "Tipe sumber daya eksternal ini tidak didukung. Mohon kontak administrator dari situs tersebut." + _failedToFetch: + title: "Gagal memuat data" + fetchErrorDescription: "Kesalahan terjadi ketika menghubungkan dengan situs eksternal. Jika percobaan kembali tidak dapat memperbaiki masalah ini, mohon hubungi administrator dari situs tersebut." + parseErrorDescription: "Kesalahan terjadi dalam memproses data yang dimuat dari situs eksternal. Mohon hubungi administrator dari situs tersebut." + _hashUnmatched: + title: "Verifikasi data gagal" + description: "Kesalahan terjadi dalam memverifikasi integritas data yang diambil. Sebagai pencegahan keamanan, pemasangan tidak dapat dilanjutkan. Mohon hubungi administrator dari situs tersebut." + _pluginParseFailed: + title: "Kesalahan AiScript" + description: "Data yang diminta telah diambil dengan sukses, namun kesalahan terjadi ketika AiScript melakukan parsing. Mohon hubungi pembuat plugin. Detil kesalahan dapat dilihat pada konsol Javascript." + _pluginInstallFailed: + title: "Pemasangan plugin gagal" + description: "Kesalahan terjadi ketika pemasangan plugin. Mohon coba lagi. Detil kesalahan dapat dilihat pada konsol Javascript." + _themeParseFailed: + title: "Parsing tema gagal" + description: "Data yang diminta telah diambil dengan sukses, namun kesalahan terjadi ketika tema melakukan parsing. Mohon hubungi pembuat tema. Detil kesalahan dapat dilihat pada konsol Javascript." + _themeInstallFailed: + title: "Pemasangan tema gagal" + description: "Kesalahan terjadi ketika pemasangan tema. Mohon coba lagi. Detil kesalahan dapat dilihat pada konsol Javascript." +_dataSaver: + _media: + title: "Memuat media" + description: "Mencegah gambar/video dimuat secara otomatis. Menyembunyikan gambar/video dan akan dimuat ketika diketuk." + _avatar: + title: "Gambar avatar" + description: "Hentikan animasi gambar avatar. Gambar animasi dapat berukuran lebih besar dari gambar biasa, berpotensi pada pengurangan lalu lintas data lebih jauh." + _urlPreview: + title: "Gambar kecil URL pratinjau" + description: "Gambar kecil URL pratinjau tidak akan dimuat lagi." + _code: + title: "Penyorotan kode" + description: "Jika notasi penyorotan kode digunakan di MFM, dll. Fungsi tersebut tidak akan dimuat apabila tidak diketuk. Penyorotan sintaks membutuhkan pengunduhan berkas definisi penyorotan untuk setiap bahasa pemrograman. Oleh sebab itu, menonaktifkan pemuatan otomatis dari berkas ini dilakukan untuk mengurangi jumlah komunikasi data." +_reversi: + total: "Jumlah" + diff --git a/locales/index.d.ts b/locales/index.d.ts index e36ff1983..87c50a5ea 100644 --- a/locales/index.d.ts +++ b/locales/index.d.ts @@ -1,2696 +1,9862 @@ /* eslint-disable */ // This file is generated by locales/generateDTS.js // Do not edit this file directly. -export interface Locale { +declare const kParameters: unique symbol; +export interface ParameterizedString { + [kParameters]: T; +} +export interface ILocale { + [_: string]: string | ParameterizedString | ILocale; +} +export interface Locale extends ILocale { + /** + * 日本語 + */ "_lang_": string; + /** + * ノートでつながるネットワーク + */ "headlineMisskey": string; + /** + * ようこそ!Sharkeyは、オープンソースの分散型マイクロブログサービスです。 + * 「ノート」を作成して、いま起こっていることを共有したり、あなたについて皆に発信しよう📡 + * 「リアクション」機能で、皆のノートに素早く反応を追加することもできます👍 + * 新しい世界を探検しよう🚀 + */ "introMisskey": string; - "poweredByMisskeyDescription": string; - "monthAndDay": string; + /** + * {name}は、オープンソースのプラットフォームSharkeyのサーバーのひとつです。 + */ + "poweredByMisskeyDescription": ParameterizedString<"name">; + /** + * {month}月 {day}日 + */ + "monthAndDay": ParameterizedString<"month" | "day">; + /** + * 検索 + */ "search": string; + /** + * 通知 + */ "notifications": string; + /** + * ユーザー名 + */ "username": string; + /** + * パスワード + */ "password": string; + /** + * パスワードを忘れた + */ "forgotPassword": string; + /** + * 連合に照会中 + */ "fetchingAsApObject": string; + /** + * OK + */ "ok": string; + /** + * わかった + */ "gotIt": string; + /** + * キャンセル + */ "cancel": string; + /** + * やめておく + */ "noThankYou": string; + /** + * ユーザー名を入力 + */ "enterUsername": string; - "renotedBy": string; + /** + * {user}がブースト + */ + "renotedBy": ParameterizedString<"user">; + /** + * ノートはありません + */ "noNotes": string; + /** + * 通知はありません + */ "noNotifications": string; + /** + * サーバー + */ "instance": string; + /** + * 設定 + */ "settings": string; + /** + * 通知の設定 + */ "notificationSettings": string; + /** + * 基本設定 + */ "basicSettings": string; + /** + * その他の設定 + */ "otherSettings": string; + /** + * ウィンドウで開く + */ "openInWindow": string; + /** + * プロフィール + */ "profile": string; + /** + * タイムライン + */ "timeline": string; + /** + * 自己紹介はありません + */ "noAccountDescription": string; + /** + * ログイン + */ "login": string; + /** + * ログイン中 + */ "loggingIn": string; + /** + * ログアウト + */ "logout": string; + /** + * 新規登録 + */ "signup": string; + /** + * アップロード中 + */ "uploading": string; + /** + * 保存 + */ "save": string; + /** + * ユーザー + */ "users": string; + /** + * 承認 + */ "approvals": string; + /** + * ユーザーを追加 + */ "addUser": string; + /** + * お気に入り + */ "favorite": string; + /** + * お気に入り + */ "favorites": string; + /** + * お気に入り解除 + */ "unfavorite": string; + /** + * お気に入りに登録しました。 + */ "favorited": string; + /** + * 既にお気に入りに登録されています。 + */ "alreadyFavorited": string; + /** + * お気に入りに登録できませんでした。 + */ "cantFavorite": string; + /** + * ピン留め + */ "pin": string; + /** + * ピン留め解除 + */ "unpin": string; + /** + * 内容をコピー + */ "copyContent": string; + /** + * リンクをコピー + */ "copyLink": string; + /** + * ブーストのリンクをコピー + */ "copyLinkRenote": string; + /** + * 削除 + */ "delete": string; + /** + * 削除して編集 + */ "deleteAndEdit": string; + /** + * このノートを削除してもう一度編集しますか?このノートへのリアクション、ブースト、返信も全て削除されます。 + */ "deleteAndEditConfirm": string; + /** + * リストに追加 + */ "addToList": string; + /** + * アンテナに追加 + */ "addToAntenna": string; + /** + * メッセージを送信 + */ "sendMessage": string; + /** + * RSSをコピー + */ "copyRSS": string; + /** + * ユーザー名をコピー + */ "copyUsername": string; + /** + * リモートプロフィールを開く + */ "openRemoteProfile": string; + /** + * ユーザーIDをコピー + */ "copyUserId": string; + /** + * ノートIDをコピー + */ "copyNoteId": string; + /** + * ファイルIDをコピー + */ "copyFileId": string; + /** + * フォルダーIDをコピー + */ "copyFolderId": string; + /** + * プロフィールURLをコピー + */ "copyProfileUrl": string; + /** + * ユーザーを検索 + */ "searchUser": string; + /** + * 返信 + */ "reply": string; + /** + * もっと見る + */ "loadMore": string; + /** + * もっと見る + */ "showMore": string; + /** + * 閉じる + */ "showLess": string; + /** + * フォローされました + */ "youGotNewFollower": string; + /** + * フォローリクエストされました + */ "receiveFollowRequest": string; + /** + * フォローが承認されました + */ "followRequestAccepted": string; + /** + * メンション + */ "mention": string; + /** + * あなた宛て + */ "mentions": string; + /** + * ダイレクト投稿 + */ "directNotes": string; + /** + * インポートとエクスポート + */ "importAndExport": string; + /** + * インポート + */ "import": string; + /** + * エクスポート + */ "export": string; + /** + * ファイル + */ "files": string; + /** + * ダウンロード + */ "download": string; - "driveFileDeleteConfirm": string; - "unfollowConfirm": string; + /** + * ファイル「{name}」を削除しますか?このファイルを使用した一部のコンテンツも削除されます。 + */ + "driveFileDeleteConfirm": ParameterizedString<"name">; + /** + * {name}のフォローを解除しますか? + */ + "unfollowConfirm": ParameterizedString<"name">; + /** + * エクスポートをリクエストしました。これには時間がかかる場合があります。エクスポートが終わると、「ドライブ」に追加されます。 + */ "exportRequested": string; + /** + * インポートをリクエストしました。これには時間がかかる場合があります。 + */ "importRequested": string; + /** + * リスト + */ "lists": string; + /** + * リストはありません + */ "noLists": string; + /** + * ノート + */ "note": string; + /** + * ノート + */ "notes": string; + /** + * フォロー + */ "following": string; + /** + * フォロワー + */ "followers": string; + /** + * フォローされています + */ "followsYou": string; + /** + * リスト作成 + */ "createList": string; + /** + * リストの管理 + */ "manageLists": string; + /** + * エラー + */ "error": string; + /** + * 問題が発生しました + */ "somethingHappened": string; + /** + * 再試行 + */ "retry": string; + /** + * ページの読み込みに失敗しました。 + */ "pageLoadError": string; + /** + * これは通常、ネットワークまたはブラウザキャッシュが原因です。キャッシュをクリアするか、しばらく待ってから再度試してください。 + */ "pageLoadErrorDescription": string; + /** + * サーバーの応答がありません。しばらく待ってから再度試してください。 + */ "serverIsDead": string; + /** + * このページを表示するためには、リロードして新しいバージョンのクライアントをご利用ください。 + */ "youShouldUpgradeClient": string; + /** + * リスト名を入力 + */ "enterListName": string; + /** + * プライバシー + */ "privacy": string; + /** + * フォローを承認制にする + */ "makeFollowManuallyApprove": string; + /** + * デフォルトの公開範囲 + */ "defaultNoteVisibility": string; + /** + * フォロー + */ "follow": string; + /** + * フォロー申請 + */ "followRequest": string; + /** + * フォロー申請 + */ "followRequests": string; + /** + * フォロー解除 + */ "unfollow": string; + /** + * フォロー許可待ち + */ "followRequestPending": string; + /** + * 絵文字を入力 + */ "enterEmoji": string; + /** + * ブースト + */ "renote": string; + /** + * ブースト解除 + */ "unrenote": string; + /** + * ブーストしました。 + */ "renoted": string; + /** + * 引用。 + */ "quoted": string; + /** + * ブースト解除しました。 + */ "rmboost": string; + /** + * この投稿はブーストできません。 + */ "cantRenote": string; + /** + * ブーストをブーストすることはできません。 + */ "cantReRenote": string; + /** + * 引用 + */ "quote": string; + /** + * チャンネル内ブースト + */ "inChannelRenote": string; + /** + * チャンネル内引用 + */ "inChannelQuote": string; + /** + * ピン留めされたノート + */ "pinnedNote": string; + /** + * ピン留め + */ "pinned": string; + /** + * あなた + */ "you": string; + /** + * クリックして表示 + */ "clickToShow": string; + /** + * センシティブ + */ "sensitive": string; + /** + * 追加 + */ "add": string; + /** + * リアクション + */ "reaction": string; + /** + * リアクション + */ "reactions": string; + /** + * 絵文字ピッカー + */ "emojiPicker": string; + /** + * リアクション時にピン留め表示する絵文字を設定できます + */ "pinnedEmojisForReactionSettingDescription": string; + /** + * 絵文字入力時にピン留め表示する絵文字を設定できます + */ "pinnedEmojisSettingDescription": string; + /** + * ピッカーの表示 + */ "emojiPickerDisplay": string; + /** + * リアクション設定から上書きする + */ "overwriteFromPinnedEmojisForReaction": string; + /** + * 全般設定から上書きする + */ "overwriteFromPinnedEmojis": string; + /** + * ドラッグして並び替え、クリックして削除、+を押して追加します。 + */ "reactionSettingDescription2": string; + /** + * 公開範囲を記憶する + */ "rememberNoteVisibility": string; + /** + * 添付取り消し + */ "attachCancel": string; + /** + * ファイルを削除 + */ + "deleteFile": string; + /** + * センシティブとして設定 + */ "markAsSensitive": string; + /** + * センシティブを解除する + */ "unmarkAsSensitive": string; + /** + * ファイル名を入力 + */ "enterFileName": string; + /** + * ミュート + */ "mute": string; + /** + * ミュート解除 + */ "unmute": string; + /** + * ブーストをミュート + */ "renoteMute": string; + /** + * ブーストのミュートを解除 + */ "renoteUnmute": string; + /** + * ブロック + */ "block": string; + /** + * ブロック解除 + */ "unblock": string; + /** + * ユーザーのすべてのメディアをNSFWとしてマークする + */ "markAsNSFW": string; + /** + * 凍結 + */ "suspend": string; + /** + * 解凍 + */ "unsuspend": string; + /** + * ブロックしますか? + */ "blockConfirm": string; + /** + * ブロック解除しますか? + */ "unblockConfirm": string; + /** + * このアカウントからのすべてのメディアをNSFWとしてマークしてもよろしいですか? + */ "nsfwConfirm": string; + /** + * このアカウントのすべてのメディアをNSFWとしてマーク解除してもよろしいですか? + */ "unNsfwConfirm": string; + /** + * 凍結しますか? + */ "suspendConfirm": string; + /** + * このアカウントを承認してもよろしいですか? + */ "approveConfirm": string; + /** + * 解凍しますか? + */ "unsuspendConfirm": string; + /** + * リストを選択 + */ "selectList": string; + /** + * リストを編集 + */ "editList": string; + /** + * チャンネルを選択 + */ "selectChannel": string; + /** + * アンテナを選択 + */ "selectAntenna": string; + /** + * アンテナを編集 + */ "editAntenna": string; + /** + * ウィジェットを選択 + */ "selectWidget": string; + /** + * ウィジェットを編集 + */ "editWidgets": string; + /** + * 編集を終了 + */ "editWidgetsExit": string; + /** + * カスタム絵文字 + */ "customEmojis": string; + /** + * 絵文字 + */ "emoji": string; + /** + * 絵文字 + */ "emojis": string; + /** + * 絵文字名 + */ "emojiName": string; + /** + * 絵文字画像URL + */ "emojiUrl": string; + /** + * 絵文字を追加 + */ "addEmoji": string; + /** + * おすすめ設定 + */ "settingGuide": string; + /** + * リモートのファイルをキャッシュする + */ "cacheRemoteFiles": string; + /** + * この設定を有効にすると、リモートファイルをこのサーバーのストレージにキャッシュするようになります。画像の表示が高速になりますが、サーバーのストレージを多く消費します。リモートユーザーがどれほどキャッシュを保持するかは、ロールによるドライブ容量制限によって決定されます。この制限を超えた場合、古いファイルからキャッシュが削除されリンクになります。この設定が無効の場合、リモートのファイルを最初からリンクとして保持しますが、画像のサムネイル生成やユーザーのプライバシー保護のために、default.ymlでproxyRemoteFilesをtrueにすることをお勧めします。 + */ "cacheRemoteFilesDescription": string; + /** + * ファイル管理の🗑️ボタンで全てのキャッシュを削除できます。 + */ "youCanCleanRemoteFilesCache": string; + /** + * リモートのセンシティブなファイルをキャッシュする + */ "cacheRemoteSensitiveFiles": string; + /** + * この設定を無効にすると、リモートのセンシティブなファイルはキャッシュせず直リンクするようになります。 + */ "cacheRemoteSensitiveFilesDescription": string; + /** + * Botとして設定 + */ "flagAsBot": string; + /** + * このアカウントがプログラムによって運用される場合は、このフラグをオンにします。オンにすると、反応の連鎖を防ぐためのフラグとして他の開発者に役立ったり、Sharkeyのシステム上での扱いがBotに合ったものになります。 + */ "flagAsBotDescription": string; + /** + * にゃああああああああああああああ!!!!!!!!!!!! + */ "flagAsCat": string; + /** + * にゃにゃにゃ?? + */ "flagAsCatDescription": string; + /** + * 猫語で話す + */ "flagSpeakAsCat": string; + /** + * 有効にすると、あなたの投稿の 「な」を「にゃ」にします。 + */ "flagSpeakAsCatDescription": string; + /** + * タイムラインにノートへの返信を表示する + */ "flagShowTimelineReplies": string; + /** + * オンにすると、タイムラインにユーザーのノート以外にもそのユーザーの他のノートへの返信を表示します。 + */ "flagShowTimelineRepliesDescription": string; + /** + * フォロー中ユーザーからのフォロリクを自動承認 + */ "autoAcceptFollowed": string; + /** + * アカウントを追加 + */ "addAccount": string; + /** + * アカウントリストの情報を更新 + */ "reloadAccountsList": string; + /** + * ログインに失敗しました + */ "loginFailed": string; + /** + * リモートで表示 + */ "showOnRemote": string; + /** + * 全般 + */ "general": string; + /** + * 壁紙 + */ "wallpaper": string; + /** + * 壁紙を設定 + */ "setWallpaper": string; + /** + * 壁紙を削除 + */ "removeWallpaper": string; - "searchWith": string; + /** + * 検索: {q} + */ + "searchWith": ParameterizedString<"q">; + /** + * リストがありません + */ "youHaveNoLists": string; - "followConfirm": string; + /** + * {name}をフォローしますか? + */ + "followConfirm": ParameterizedString<"name">; + /** + * プロキシアカウント + */ "proxyAccount": string; + /** + * プロキシアカウントは、特定の条件下でユーザーのリモートフォローを代行するアカウントです。例えば、ユーザーがリモートユーザーをリストに入れたとき、リストに入れられたユーザーを誰もフォローしていないとアクティビティがサーバーに配達されないため、代わりにプロキシアカウントがフォローするようにします。 + */ "proxyAccountDescription": string; + /** + * ホスト + */ "host": string; + /** + * ユーザーを選択 + */ "selectUser": string; + /** + * 宛先 + */ "recipient": string; + /** + * 注釈 + */ "annotation": string; + /** + * 連合 + */ "federation": string; + /** + * サーバー + */ "instances": string; + /** + * 初観測 + */ "registeredAt": string; + /** + * 直近のリクエスト受信 + */ "latestRequestReceivedAt": string; + /** + * 直近のステータス + */ "latestStatus": string; + /** + * ストレージ使用量 + */ "storageUsage": string; + /** + * チャート + */ "charts": string; + /** + * 1時間ごと + */ "perHour": string; + /** + * 1日ごと + */ "perDay": string; + /** + * アクティビティの配送を停止 + */ "stopActivityDelivery": string; + /** + * このインスタンスをブロック + */ "blockThisInstance": string; + /** + * インスタンスをサイレンス + */ "silenceThisInstance": string; + /** + * 操作 + */ "operations": string; + /** + * ソフトウェア + */ "software": string; + /** + * バージョン + */ "version": string; + /** + * メタデータ + */ "metadata": string; - "withNFiles": string; + /** + * {n}つのファイル + */ + "withNFiles": ParameterizedString<"n">; + /** + * モニター + */ "monitor": string; + /** + * ジョブキュー + */ "jobQueue": string; + /** + * CPUとメモリ + */ "cpuAndMemory": string; + /** + * ネットワーク + */ "network": string; + /** + * ディスク + */ "disk": string; + /** + * サーバー情報 + */ "instanceInfo": string; + /** + * 統計 + */ "statistics": string; + /** + * キューをクリア + */ "clearQueue": string; + /** + * キューをクリアしますか? + */ "clearQueueConfirmTitle": string; + /** + * 未配達の投稿は配送されなくなります。通常この操作を行う必要はありません。 + */ "clearQueueConfirmText": string; + /** + * キャッシュをクリア + */ "clearCachedFiles": string; + /** + * キャッシュされたリモートファイルをすべて削除しますか? + */ "clearCachedFilesConfirm": string; + /** + * ブロックしたサーバー + */ "blockedInstances": string; + /** + * ブロックしたいサーバーのホストを改行で区切って設定します。ブロックされたサーバーは、このインスタンスとやり取りできなくなります。 + */ "blockedInstancesDescription": string; + /** + * サイレンスしたサーバー + */ "silencedInstances": string; + /** + * サイレンスしたいインスタンスのホストを改行で区切って設定します。サイレンスされたインスタンスに所属するアカウントはすべて「サイレンス」として扱われ、フォローがすべてリクエストになり、フォロワーでないローカルアカウントにはメンションできなくなります。ブロックしたインスタンスには影響しません。 + */ "silencedInstancesDescription": string; + /** + * ミュートとブロック + */ "muteAndBlock": string; + /** + * ミュートしたユーザー + */ "mutedUsers": string; + /** + * ブロックしたユーザー + */ "blockedUsers": string; + /** + * ユーザーはいません + */ "noUsers": string; + /** + * プロフィールを編集 + */ "editProfile": string; + /** + * このノートを削除しますか? + */ "noteDeleteConfirm": string; + /** + * これ以上ピン留めできません + */ "pinLimitExceeded": string; + /** + * Sharkeyのインストールが完了しました!管理者アカウントを作成しましょう。 + */ "intro": string; + /** + * 完了 + */ "done": string; + /** + * 処理中 + */ "processing": string; + /** + * プレビュー + */ "preview": string; + /** + * デフォルト + */ "default": string; - "defaultValueIs": string; + /** + * デフォルト: {value} + */ + "defaultValueIs": ParameterizedString<"value">; + /** + * 絵文字はありません + */ "noCustomEmojis": string; + /** + * ジョブはありません + */ "noJobs": string; + /** + * 連合中 + */ "federating": string; + /** + * ブロック中 + */ "blocked": string; + /** + * 配信停止 + */ "suspended": string; + /** + * 全て + */ "all": string; + /** + * 購読中 + */ "subscribing": string; + /** + * 配信中 + */ "publishing": string; + /** + * 応答なし + */ "notResponding": string; + /** + * サーバーのフォロー + */ "instanceFollowing": string; + /** + * サーバーのフォロワー + */ "instanceFollowers": string; + /** + * サーバーのユーザー + */ "instanceUsers": string; + /** + * パスワードを変更 + */ "changePassword": string; + /** + * セキュリティ + */ "security": string; + /** + * 入力が一致しません。 + */ "retypedNotMatch": string; + /** + * 現在のパスワード + */ "currentPassword": string; + /** + * 新しいパスワード + */ "newPassword": string; + /** + * 新しいパスワード(再入力) + */ "newPasswordRetype": string; + /** + * ファイルを添付 + */ "attachFile": string; + /** + * もっと! + */ "more": string; + /** + * ハイライト + */ "featured": string; + /** + * ユーザー名かユーザーID + */ "usernameOrUserId": string; + /** + * ユーザーが見つかりません + */ "noSuchUser": string; + /** + * 照会 + */ "lookup": string; + /** + * お知らせ + */ "announcements": string; + /** + * 画像URL + */ "imageUrl": string; + /** + * 削除 + */ "remove": string; + /** + * 削除しました + */ "removed": string; - "removeAreYouSure": string; - "deleteAreYouSure": string; + /** + * 「{x}」を削除しますか? + */ + "removeAreYouSure": ParameterizedString<"x">; + /** + * 「{x}」を削除しますか? + */ + "deleteAreYouSure": ParameterizedString<"x">; + /** + * リセットしますか? + */ "resetAreYouSure": string; + /** + * よろしいですか? + */ "areYouSure": string; + /** + * 保存しました + */ "saved": string; + /** + * チャット + */ "messaging": string; + /** + * アップロード + */ "upload": string; + /** + * オリジナル画像を保持 + */ "keepOriginalUploading": string; + /** + * 画像をアップロードする時にオリジナル版を保持します。オフにするとアップロード時にブラウザでWeb公開用画像を生成します。 + */ "keepOriginalUploadingDescription": string; + /** + * ドライブから + */ "fromDrive": string; + /** + * URLから + */ "fromUrl": string; + /** + * URLアップロード + */ "uploadFromUrl": string; + /** + * アップロードしたいファイルのURL + */ "uploadFromUrlDescription": string; + /** + * アップロードをリクエストしました + */ "uploadFromUrlRequested": string; + /** + * アップロードが完了するまで時間がかかる場合があります。 + */ "uploadFromUrlMayTakeTime": string; + /** + * みつける + */ "explore": string; + /** + * 既読 + */ "messageRead": string; + /** + * これより過去の履歴はありません + */ "noMoreHistory": string; + /** + * チャットを開始 + */ "startMessaging": string; - "nUsersRead": string; - "agreeTo": string; + /** + * {n}人が読みました + */ + "nUsersRead": ParameterizedString<"n">; + /** + * {0}に同意 + */ + "agreeTo": ParameterizedString<"0">; + /** + * 同意する + */ "agree": string; + /** + * 下記に同意する + */ "agreeBelow": string; + /** + * 基本的な注意事項 + */ "basicNotesBeforeCreateAccount": string; + /** + * 利用規約 + */ "termsOfService": string; + /** + * 始める + */ "start": string; + /** + * ホーム + */ "home": string; + /** + * リモートユーザーのため、情報が不完全です。 + */ "remoteUserCaution": string; + /** + * アクティビティ + */ "activity": string; + /** + * 画像 + */ "images": string; + /** + * 画像 + */ "image": string; + /** + * 誕生日 + */ "birthday": string; - "yearsOld": string; + /** + * {age}歳 + */ + "yearsOld": ParameterizedString<"age">; + /** + * 登録日 + */ "registeredDate": string; + /** + * 場所 + */ "location": string; + /** + * テーマ + */ "theme": string; + /** + * ライトモードで使うテーマ + */ "themeForLightMode": string; + /** + * ダークモードで使うテーマ + */ "themeForDarkMode": string; + /** + * ライト + */ "light": string; + /** + * ダーク + */ "dark": string; + /** + * 明るいテーマ + */ "lightThemes": string; + /** + * 暗いテーマ + */ "darkThemes": string; + /** + * デバイスのダークモードと同期する + */ "syncDeviceDarkMode": string; + /** + * ドライブ + */ "drive": string; + /** + * ファイル名 + */ "fileName": string; + /** + * ファイルを選択 + */ "selectFile": string; + /** + * ファイルを選択 + */ "selectFiles": string; + /** + * フォルダーを選択 + */ "selectFolder": string; + /** + * フォルダーを選択 + */ "selectFolders": string; + /** + * ファイル名を変更 + */ "renameFile": string; + /** + * フォルダー名 + */ "folderName": string; + /** + * フォルダーを作成 + */ "createFolder": string; + /** + * フォルダー名を変更 + */ "renameFolder": string; + /** + * フォルダーを削除 + */ "deleteFolder": string; + /** + * フォルダー + */ "folder": string; + /** + * ファイルを追加 + */ "addFile": string; + /** + * ドライブは空です + */ "emptyDrive": string; + /** + * フォルダーは空です + */ "emptyFolder": string; + /** + * 削除できません + */ "unableToDelete": string; + /** + * 新しいファイル名を入力してください + */ "inputNewFileName": string; + /** + * 新しいキャプションを入力してください + */ "inputNewDescription": string; + /** + * 新しいフォルダ名を入力してください + */ "inputNewFolderName": string; + /** + * 移動先のフォルダーは、移動するフォルダーのサブフォルダーです。 + */ "circularReferenceFolder": string; + /** + * このフォルダは空でないため、削除できません。 + */ "hasChildFilesOrFolders": string; + /** + * URLをコピー + */ "copyUrl": string; + /** + * 名前を変更 + */ "rename": string; + /** + * アイコン + */ "avatar": string; + /** + * バナー + */ "banner": string; + /** + * 背景 + */ "background": string; + /** + * センシティブなメディアの表示 + */ "displayOfSensitiveMedia": string; + /** + * サーバーとの接続が失われたとき + */ "whenServerDisconnected": string; + /** + * サーバーから切断されました + */ "disconnectedFromServer": string; + /** + * リロード + */ "reload": string; + /** + * なにもしない + */ "doNothing": string; + /** + * リロードしますか? + */ "reloadConfirm": string; + /** + * ウォッチ + */ "watch": string; + /** + * ウォッチ解除 + */ "unwatch": string; + /** + * 許可 + */ "accept": string; + /** + * 拒否 + */ "reject": string; + /** + * 通常 + */ "normal": string; + /** + * サーバー名 + */ "instanceName": string; + /** + * サーバーの紹介 + */ "instanceDescription": string; + /** + * 管理者の名前 + */ "maintainerName": string; + /** + * 管理者のメールアドレス + */ "maintainerEmail": string; + /** + * 利用規約URL + */ "tosUrl": string; + /** + * 今年 + */ "thisYear": string; + /** + * 今月 + */ "thisMonth": string; + /** + * 今日 + */ "today": string; - "dayX": string; - "monthX": string; - "yearX": string; + /** + * {day}日 + */ + "dayX": ParameterizedString<"day">; + /** + * {month}月 + */ + "monthX": ParameterizedString<"month">; + /** + * {year}年 + */ + "yearX": ParameterizedString<"year">; + /** + * ページ + */ "pages": string; + /** + * 連携 + */ "integration": string; + /** + * 接続する + */ "connectService": string; + /** + * 切断する + */ "disconnectService": string; + /** + * ローカルタイムラインを有効にする + */ "enableLocalTimeline": string; + /** + * グローバルタイムラインを有効にする + */ "enableGlobalTimeline": string; + /** + * これらのタイムラインを無効化しても、利便性のため管理者およびモデレーターは引き続き利用することができます。 + */ "disablingTimelinesInfo": string; + /** + * 登録 + */ "registration": string; + /** + * 誰でも新規登録できるようにする + */ "enableRegistration": string; + /** + * 招待 + */ "invite": string; + /** + * ローカルユーザーひとりあたりのドライブ容量 + */ "driveCapacityPerLocalAccount": string; + /** + * リモートユーザーひとりあたりのドライブ容量 + */ "driveCapacityPerRemoteAccount": string; + /** + * メガバイト単位 + */ "inMb": string; + /** + * バナー画像のURL + */ "bannerUrl": string; + /** + * 背景画像のURL + */ "backgroundImageUrl": string; + /** + * 基本情報 + */ "basicInfo": string; + /** + * ピン留めユーザー + */ "pinnedUsers": string; + /** + * 「みつける」ページなどにピン留めしたいユーザーを改行で区切って記述します。 + */ "pinnedUsersDescription": string; + /** + * ピン留めページ + */ "pinnedPages": string; + /** + * サーバーのトップページにピン留めしたいページのパスを改行で区切って記述します。 + */ "pinnedPagesDescription": string; + /** + * ピン留めするクリップのID + */ "pinnedClipId": string; + /** + * ピン留めされたノート + */ "pinnedNotes": string; + /** + * hCaptcha + */ "hcaptcha": string; + /** + * hCaptchaを有効にする + */ "enableHcaptcha": string; + /** + * サイトキー + */ "hcaptchaSiteKey": string; + /** + * シークレットキー + */ "hcaptchaSecretKey": string; + /** + * mCaptcha + */ "mcaptcha": string; + /** + * mCaptchaを有効にする + */ "enableMcaptcha": string; + /** + * サイトキー + */ "mcaptchaSiteKey": string; + /** + * シークレットキー + */ "mcaptchaSecretKey": string; + /** + * mCaptchaのインスタンスのURL + */ "mcaptchaInstanceUrl": string; + /** + * reCAPTCHA + */ "recaptcha": string; + /** + * reCAPTCHAを有効にする + */ "enableRecaptcha": string; + /** + * サイトキー + */ "recaptchaSiteKey": string; + /** + * シークレットキー + */ "recaptchaSecretKey": string; + /** + * Turnstile + */ "turnstile": string; + /** + * Turnstileを有効にする + */ "enableTurnstile": string; + /** + * サイトキー + */ "turnstileSiteKey": string; + /** + * シークレットキー + */ "turnstileSecretKey": string; + /** + * 複数のCaptchaを使用すると干渉を起こす可能性があります。他のCaptchaを無効にしますか?キャンセルして複数のCaptchaを有効化したままにすることも可能です。 + */ "avoidMultiCaptchaConfirm": string; + /** + * アンテナ + */ "antennas": string; + /** + * アンテナの管理 + */ "manageAntennas": string; + /** + * 名前 + */ "name": string; + /** + * 受信ソース + */ "antennaSource": string; + /** + * 受信キーワード + */ "antennaKeywords": string; + /** + * 除外キーワード + */ "antennaExcludeKeywords": string; + /** + * スペースで区切るとAND指定になり、改行で区切るとOR指定になります + */ "antennaKeywordsDescription": string; + /** + * 新しいノートを通知する + */ "notifyAntenna": string; + /** + * ファイルが添付されたノートのみ + */ "withFileAntenna": string; + /** + * ブラウザへのプッシュ通知を有効にする + */ "enableServiceworker": string; + /** + * ユーザー名を改行で区切って指定します + */ "antennaUsersDescription": string; + /** + * 大文字小文字を区別する + */ "caseSensitive": string; + /** + * 返信を含む + */ "withReplies": string; + /** + * 次のアカウントに接続されています + */ "connectedTo": string; + /** + * 投稿と返信 + */ "notesAndReplies": string; + /** + * ファイル付き + */ "withFiles": string; + /** + * サイレンス + */ "silence": string; + /** + * サイレンスしますか? + */ "silenceConfirm": string; + /** + * サイレンス解除 + */ "unsilence": string; + /** + * サイレンス解除しますか? + */ "unsilenceConfirm": string; + /** + * 人気のユーザー + */ "popularUsers": string; + /** + * 最近投稿したユーザー + */ "recentlyUpdatedUsers": string; + /** + * 最近登録したユーザー + */ "recentlyRegisteredUsers": string; + /** + * 最近発見されたユーザー + */ "recentlyDiscoveredUsers": string; - "exploreUsersCount": string; + /** + * {count}のユーザーがいます + */ + "exploreUsersCount": ParameterizedString<"count">; + /** + * Fediverseを探索 + */ "exploreFediverse": string; + /** + * 人気のタグ + */ "popularTags": string; + /** + * リスト + */ "userList": string; + /** + * 情報 + */ "about": string; + /** + * Sharkeyについて + */ "aboutMisskey": string; + /** + * 管理者 + */ "administrator": string; + /** + * 確認コード + */ "token": string; + /** + * 二要素認証 + */ "2fa": string; + /** + * 二要素認証のセットアップ + */ "setupOf2fa": string; + /** + * 認証アプリ + */ "totp": string; + /** + * 認証アプリを使ってワンタイムパスワードを入力 + */ "totpDescription": string; + /** + * モデレーター + */ "moderator": string; + /** + * モデレーション + */ "moderation": string; + /** + * モデレーションノート + */ "moderationNote": string; + /** + * モデレーションノートを追加する + */ "addModerationNote": string; + /** + * モデログ + */ "moderationLogs": string; - "nUsersMentioned": string; + /** + * {n}人が投稿 + */ + "nUsersMentioned": ParameterizedString<"n">; + /** + * セキュリティキー・パスキー + */ "securityKeyAndPasskey": string; + /** + * セキュリティキー + */ "securityKey": string; + /** + * 最後の使用 + */ "lastUsed": string; - "lastUsedAt": string; + /** + * 最後の使用: {t} + */ + "lastUsedAt": ParameterizedString<"t">; + /** + * 登録を解除 + */ "unregister": string; + /** + * パスワードレスログイン + */ "passwordLessLogin": string; + /** + * パスワードを使用せず、セキュリティキーやパスキーなどのみでログインします + */ "passwordLessLoginDescription": string; + /** + * パスワードをリセット + */ "resetPassword": string; - "newPasswordIs": string; + /** + * 新しいパスワードは「{password}」です + */ + "newPasswordIs": ParameterizedString<"password">; + /** + * UIのアニメーションを減らす + */ "reduceUiAnimation": string; + /** + * 共有 + */ "share": string; + /** + * 見つかりません + */ "notFound": string; + /** + * 指定されたURLに該当するページはありませんでした。 + */ "notFoundDescription": string; + /** + * 既定アップロード先 + */ "uploadFolder": string; + /** + * すべての通知を既読にする + */ "markAsReadAllNotifications": string; + /** + * すべての投稿を既読にする + */ "markAsReadAllUnreadNotes": string; + /** + * すべてのチャットを既読にする + */ "markAsReadAllTalkMessages": string; + /** + * ヘルプ + */ "help": string; + /** + * ここにメッセージを入力 + */ "inputMessageHere": string; + /** + * 閉じる + */ "close": string; + /** + * 招待 + */ "invites": string; + /** + * メンバー + */ "members": string; + /** + * 譲渡 + */ "transfer": string; + /** + * タイトル + */ "title": string; + /** + * テキスト + */ "text": string; + /** + * 有効にする + */ "enable": string; + /** + * 次 + */ "next": string; + /** + * 再入力 + */ "retype": string; - "noteOf": string; + /** + * {user}のノート + */ + "noteOf": ParameterizedString<"user">; + /** + * すべての返信の内容を表示する + */ "expandAllCws": string; + /** + * すべての返信の内容を隠す + */ "collapseAllCws": string; + /** + * 引用付き + */ "quoteAttached": string; + /** + * 引用として添付しますか? + */ "quoteQuestion": string; + /** + * まだチャットはありません + */ "noMessagesYet": string; + /** + * 新しいメッセージがあります + */ "newMessageExists": string; + /** + * メッセージに添付できるファイルはひとつです + */ "onlyOneFileCanBeAttached": string; + /** + * 続行する前に、サインアップまたはサインインが必要です + */ "signinRequired": string; + /** + * 招待 + */ "invitations": string; + /** + * 招待コード + */ "invitationCode": string; + /** + * 確認しています + */ "checking": string; + /** + * 利用できます + */ "available": string; + /** + * 利用できません + */ "unavailable": string; + /** + * a~z、A~Z、0~9、_が使えます + */ "usernameInvalidFormat": string; + /** + * 短すぎます + */ "tooShort": string; + /** + * 長すぎます + */ "tooLong": string; + /** + * 弱いパスワード + */ "weakPassword": string; + /** + * 普通のパスワード + */ "normalPassword": string; + /** + * 強いパスワード + */ "strongPassword": string; + /** + * 一致しました + */ "passwordMatched": string; + /** + * 一致していません + */ "passwordNotMatched": string; - "signinWith": string; + /** + * {x}でログイン + */ + "signinWith": ParameterizedString<"x">; + /** + * ログインできませんでした。ユーザー名とパスワードを確認してください。 + */ "signinFailed": string; + /** + * もしくは + */ "or": string; + /** + * 言語 + */ "language": string; + /** + * UIの表示言語 + */ "uiLanguage": string; - "aboutX": string; + /** + * {x}について + */ + "aboutX": ParameterizedString<"x">; + /** + * 絵文字のスタイル + */ "emojiStyle": string; + /** + * ネイティブ + */ "native": string; + /** + * メニューをドロワーで表示しない + */ "disableDrawer": string; + /** + * ノートのアクションをホバー時のみ表示する + */ "showNoteActionsOnlyHover": string; + /** + * 履歴はありません + */ "noHistory": string; + /** + * ログイン履歴 + */ "signinHistory": string; + /** + * 高度なMFMを有効にする + */ "enableAdvancedMfm": string; + /** + * 動きのあるMFMを有効にする + */ "enableAnimatedMfm": string; + /** + * やっています + */ "doing": string; + /** + * カテゴリ + */ "category": string; + /** + * タグ + */ "tags": string; + /** + * このドキュメントのソース + */ "docSource": string; + /** + * アカウントを作成 + */ "createAccount": string; + /** + * 既存のアカウント + */ "existingAccount": string; + /** + * 再生成 + */ "regenerate": string; + /** + * フォントサイズ + */ "fontSize": string; + /** + * コーナーの丸み + */ "cornerRadius": string; + /** + * 画像が1枚のみのメディアリストの高さ + */ "mediaListWithOneImageAppearance": string; - "limitTo": string; + /** + * {x}を上限に + */ + "limitTo": ParameterizedString<"x">; + /** + * フォロー申請はありません + */ "noFollowRequests": string; + /** + * 画像を新しいタブで開く + */ "openImageInNewTab": string; + /** + * ダッシュボード + */ "dashboard": string; + /** + * ローカル + */ "local": string; + /** + * リモート + */ "remote": string; + /** + * 合計 + */ "total": string; + /** + * 前週比 + */ "weekOverWeekChanges": string; + /** + * 前日比 + */ "dayOverDayChanges": string; + /** + * アピアランス + */ "appearance": string; + /** + * クライアント設定 + */ "clientSettings": string; + /** + * アカウント設定 + */ "accountSettings": string; + /** + * プロモーション + */ "promotion": string; + /** + * プロモート + */ "promote": string; + /** + * 日数 + */ "numberOfDays": string; + /** + * このノートを非表示 + */ "hideThisNote": string; + /** + * タイムラインにおすすめのノートを表示する + */ "showFeaturedNotesInTimeline": string; + /** + * オブジェクトストレージ + */ "objectStorage": string; + /** + * オブジェクトストレージを使用 + */ "useObjectStorage": string; + /** + * Base URL + */ "objectStorageBaseUrl": string; + /** + * 参照に使用するURL。CDNやProxyを使用している場合はそのURL、S3: 'https://.s3.amazonaws.com'、GCS等: 'https://storage.googleapis.com/'。 + */ "objectStorageBaseUrlDesc": string; + /** + * Bucket + */ "objectStorageBucket": string; + /** + * 使用サービスのbucket名を指定してください。 + */ "objectStorageBucketDesc": string; + /** + * Prefix + */ "objectStoragePrefix": string; + /** + * このprefixのディレクトリ下に格納されます。 + */ "objectStoragePrefixDesc": string; + /** + * Endpoint + */ "objectStorageEndpoint": string; + /** + * S3の場合は空、それ以外の場合は各サービスのendpointを指定してください。''または':'のように指定します。 + */ "objectStorageEndpointDesc": string; + /** + * Region + */ "objectStorageRegion": string; + /** + * 'xx-east-1'のようなregionを指定してください。使用サービスにregionの概念がない場合は'us-east-1'にしてください。AWS設定ファイルまたは環境変数を参照する場合は空にしてください。 + */ "objectStorageRegionDesc": string; + /** + * SSLを使用する + */ "objectStorageUseSSL": string; + /** + * API接続にhttpsを使用しない場合はオフにしてください + */ "objectStorageUseSSLDesc": string; + /** + * Proxyを利用する + */ "objectStorageUseProxy": string; + /** + * API接続にproxyを利用しない場合はオフにしてください + */ "objectStorageUseProxyDesc": string; + /** + * アップロード時に'public-read'を設定する + */ "objectStorageSetPublicRead": string; + /** + * s3ForcePathStyleを有効にすると、バケット名をURLのホスト名ではなくパスの一部として指定することを強制します。セルフホストされたMinioなどの使用時に有効にする必要がある場合があります。 + */ "s3ForcePathStyleDesc": string; + /** + * サーバーログ + */ "serverLogs": string; + /** + * 全て削除 + */ "deleteAll": string; + /** + * タイムライン上部に投稿フォームを表示する + */ "showFixedPostForm": string; + /** + * タイムライン上部に投稿フォームを表示する(チャンネル) + */ "showFixedPostFormInChannel": string; + /** + * フォローする際、デフォルトで返信をTLに含むようにする + */ "withRepliesByDefaultForNewlyFollowed": string; + /** + * 新しいノートがあります + */ "newNoteRecived": string; + /** + * サウンド + */ "sounds": string; + /** + * サウンド + */ "sound": string; + /** + * 聴く + */ "listen": string; + /** + * なし + */ "none": string; + /** + * ページで表示 + */ "showInPage": string; + /** + * ポップアウト + */ "popout": string; + /** + * 音量 + */ "volume": string; + /** + * マスター音量 + */ "masterVolume": string; + /** + * サウンドを出力しない + */ "notUseSound": string; + /** + * Misskeyがアクティブな時のみサウンドを出力する + */ "useSoundOnlyWhenActive": string; + /** + * 詳細 + */ "details": string; + /** + * 絵文字を選択 + */ "chooseEmoji": string; + /** + * 操作を完了できません + */ "unableToProcess": string; + /** + * 最近使用 + */ "recentUsed": string; + /** + * インストール + */ "install": string; + /** + * アンインストール + */ "uninstall": string; + /** + * インストールされたアプリ + */ "installedApps": string; + /** + * ありません + */ "nothing": string; + /** + * インストール日時 + */ "installedDate": string; + /** + * 最終使用日時 + */ "lastUsedDate": string; + /** + * 状態 + */ "state": string; + /** + * ソート + */ "sort": string; + /** + * 昇順 + */ "ascendingOrder": string; + /** + * 降順 + */ "descendingOrder": string; + /** + * スクラッチパッド + */ "scratchpad": string; + /** + * スクラッチパッドは、AiScriptの実験環境を提供します。Sharkeyと対話するコードの記述、実行、結果の確認ができます。 + */ "scratchpadDescription": string; + /** + * 出力 + */ "output": string; + /** + * スクリプト + */ "script": string; + /** + * Pagesのスクリプトを無効にする + */ "disablePagesScript": string; + /** + * リモートユーザー情報の更新 + */ "updateRemoteUser": string; + /** + * アイコンを解除 + */ "unsetUserAvatar": string; + /** + * アイコンを解除しますか? + */ "unsetUserAvatarConfirm": string; + /** + * バナーを解除 + */ "unsetUserBanner": string; + /** + * バナーを解除しますか? + */ "unsetUserBannerConfirm": string; + /** + * すべてのファイルを削除 + */ "deleteAllFiles": string; + /** + * すべてのファイルを削除しますか? + */ "deleteAllFilesConfirm": string; + /** + * フォローを全解除 + */ "removeAllFollowing": string; - "removeAllFollowingDescription": string; + /** + * {host}からのフォローをすべて解除します。そのサーバーがもう存在しなくなった場合などに実行してください。 + */ + "removeAllFollowingDescription": ParameterizedString<"host">; + /** + * このユーザーは凍結されています。 + */ "userSuspended": string; + /** + * このユーザーはサイレンスされています。 + */ "userSilenced": string; + /** + * アカウントが凍結されています + */ "yourAccountSuspendedTitle": string; + /** + * このアカウントは、サーバーの利用規約に違反したなどの理由により、凍結されています。詳細については管理者までお問い合わせください。新しいアカウントを作らないでください。 + */ "yourAccountSuspendedDescription": string; + /** + * トークンが無効です + */ "tokenRevoked": string; + /** + * ログイントークンが失効しています。ログインし直してください。 + */ "tokenRevokedDescription": string; + /** + * アカウントは削除されています + */ "accountDeleted": string; + /** + * このアカウントは削除されています。 + */ "accountDeletedDescription": string; + /** + * メニュー + */ "menu": string; + /** + * 分割線 + */ "divider": string; + /** + * 項目を追加 + */ "addItem": string; + /** + * 並び替え + */ "rearrange": string; + /** + * リレー + */ "relays": string; + /** + * リレーの追加 + */ "addRelay": string; + /** + * inboxのURL + */ "inboxUrl": string; + /** + * 追加済みのリレー + */ "addedRelays": string; + /** + * プッシュ通知を行うには有効にする必要があります。 + */ "serviceworkerInfo": string; + /** + * 削除された投稿 + */ "deletedNote": string; + /** + * 非公開の投稿 + */ "invisibleNote": string; + /** + * 自動でもっと見る + */ "enableInfiniteScroll": string; + /** + * 公開範囲 + */ "visibility": string; + /** + * アンケート + */ "poll": string; + /** + * 内容を隠す + */ "useCw": string; + /** + * プレイヤーを開く + */ "enablePlayer": string; + /** + * プレイヤーを閉じる + */ "disablePlayer": string; + /** + * ポストを展開する + */ "expandTweet": string; + /** + * テーマエディター + */ "themeEditor": string; + /** + * 説明 + */ "description": string; + /** + * キャプションを付ける + */ "describeFile": string; + /** + * キャプションを入力 + */ "enterFileDescription": string; + /** + * 作者 + */ "author": string; + /** + * 未保存の変更があります。破棄しますか? + */ "leaveConfirm": string; + /** + * 管理 + */ "manage": string; + /** + * プラグイン + */ "plugins": string; + /** + * 設定のバックアップ + */ "preferencesBackups": string; + /** + * デッキ + */ "deck": string; + /** + * デッキ解除 + */ "undeck": string; + /** + * モーダルにぼかし効果を使用 + */ "useBlurEffectForModal": string; + /** + * フル機能リアクションピッカーを使用 + */ "useFullReactionPicker": string; + /** + * 幅 + */ "width": string; + /** + * 高さ + */ "height": string; + /** + * 大 + */ "large": string; + /** + * 中 + */ "medium": string; + /** + * 小 + */ "small": string; + /** + * アクセストークンの発行 + */ "generateAccessToken": string; + /** + * 権限 + */ "permission": string; + /** + * 管理者権限 + */ "adminPermission": string; + /** + * 全て有効にする + */ "enableAll": string; + /** + * 全て無効にする + */ "disableAll": string; + /** + * アカウントへのアクセス許可 + */ "tokenRequested": string; + /** + * このプラグインはここで設定した権限を行使できるようになります。 + */ "pluginTokenRequestedDescription": string; + /** + * 通知の種類 + */ "notificationType": string; + /** + * 編集 + */ "edit": string; + /** + * メールサーバー + */ "emailServer": string; + /** + * メール配信機能を有効化する + */ "enableEmail": string; + /** + * メールアドレスの確認やパスワードリセットの際に使います + */ "emailConfigInfo": string; + /** + * メール + */ "email": string; + /** + * メールアドレス + */ "emailAddress": string; + /** + * SMTP サーバーの設定 + */ "smtpConfig": string; + /** + * ホスト + */ "smtpHost": string; + /** + * ポート + */ "smtpPort": string; + /** + * ユーザー名 + */ "smtpUser": string; + /** + * パスワード + */ "smtpPass": string; + /** + * ユーザー名とパスワードを空欄にすることで、SMTP認証を無効化出来ます + */ "emptyToDisableSmtpAuth": string; + /** + * SMTP 接続に暗黙的なSSL/TLSを使用する + */ "smtpSecure": string; + /** + * STARTTLS使用時はオフにします。 + */ "smtpSecureInfo": string; + /** + * 配信テスト + */ "testEmail": string; + /** + * ワードミュート + */ "wordMute": string; + /** + * ハードワードミュート + */ "hardWordMute": string; + /** + * 正規表現エラー + */ "regexpError": string; - "regexpErrorDescription": string; + /** + * {tab}ワードミュートの{line}行目の正規表現にエラーが発生しました: + */ + "regexpErrorDescription": ParameterizedString<"tab" | "line">; + /** + * サーバーミュート + */ "instanceMute": string; - "userSaysSomething": string; + /** + * {name}が何かを言いました + */ + "userSaysSomething": ParameterizedString<"name">; + /** + * アクティブにする + */ "makeActive": string; + /** + * 表示 + */ "display": string; + /** + * コピー + */ "copy": string; + /** + * メトリクス + */ "metrics": string; + /** + * 概要 + */ "overview": string; + /** + * ログ + */ "logs": string; + /** + * 遅延 + */ "delayed": string; + /** + * データベース + */ "database": string; + /** + * チャンネル + */ "channel": string; + /** + * 作成 + */ "create": string; + /** + * 通知設定 + */ "notificationSetting": string; + /** + * 表示する通知の種別を選択してください。 + */ "notificationSettingDesc": string; + /** + * グローバル設定を使う + */ "useGlobalSetting": string; + /** + * オンにすると、アカウントの通知設定が使用されます。オフにすると、個別に設定できるようになります。 + */ "useGlobalSettingDesc": string; + /** + * その他 + */ "other": string; + /** + * ログイントークンを再生成 + */ "regenerateLoginToken": string; + /** + * ログインに使用される内部トークンを再生成します。通常この操作を行う必要はありません。再生成すると、全てのデバイスでログアウトされます。 + */ "regenerateLoginTokenDescription": string; + /** + * カスタム絵文字を検索する時のキーワードになります。 + */ "theKeywordWhenSearchingForCustomEmoji": string; + /** + * スペースで区切って複数設定できます。 + */ "setMultipleBySeparatingWithSpace": string; + /** + * ファイルIDまたはURL + */ "fileIdOrUrl": string; + /** + * 動作 + */ "behavior": string; + /** + * サンプル + */ "sample": string; + /** + * 通報 + */ "abuseReports": string; + /** + * 通報 + */ "reportAbuse": string; + /** + * ブーストを通報 + */ "reportAbuseRenote": string; - "reportAbuseOf": string; + /** + * {name}を通報する + */ + "reportAbuseOf": ParameterizedString<"name">; + /** + * 通報理由の詳細を記入してください。対象のノートがある場合はそのURLも記入してください。 + */ "fillAbuseReportDescription": string; + /** + * 内容が送信されました。ご報告ありがとうございました。 + */ "abuseReported": string; + /** + * 通報者 + */ "reporter": string; + /** + * 通報先 + */ "reporteeOrigin": string; + /** + * 通報元 + */ "reporterOrigin": string; + /** + * リモートサーバーに通報を転送する + */ "forwardReport": string; + /** + * リモートサーバーからはあなたの情報は見れず、匿名のシステムアカウントとして表示されます。 + */ "forwardReportIsAnonymous": string; + /** + * 送信 + */ "send": string; + /** + * 対応済みにする + */ "abuseMarkAsResolved": string; + /** + * 新しいタブで開く + */ "openInNewTab": string; + /** + * サイドビューで開く + */ "openInSideView": string; + /** + * デフォルトのナビゲーション + */ "defaultNavigationBehaviour": string; + /** + * これらの設定を編集するとアカウントが破損する可能性があります。 + */ "editTheseSettingsMayBreakAccount": string; + /** + * ノートのサーバー情報 + */ "instanceTicker": string; - "waitingFor": string; + /** + * {x}を待っています + */ + "waitingFor": ParameterizedString<"x">; + /** + * ランダム + */ "random": string; + /** + * システム + */ "system": string; + /** + * UI切り替え + */ "switchUi": string; + /** + * デスクトップ + */ "desktop": string; + /** + * クリップ + */ "clip": string; + /** + * 新規作成 + */ "createNew": string; + /** + * 任意 + */ "optional": string; + /** + * 新しいクリップを作成 + */ "createNewClip": string; + /** + * クリップ解除 + */ "unclip": string; - "confirmToUnclipAlreadyClippedNote": string; + /** + * このノートはすでにクリップ「{name}」に含まれています。ノートをこのクリップから除外しますか? + */ + "confirmToUnclipAlreadyClippedNote": ParameterizedString<"name">; + /** + * パブリック + */ "public": string; + /** + * 非公開 + */ "private": string; - "i18nInfo": string; + /** + * Sharkeyは有志によって様々な言語に翻訳されています。{link}で翻訳に協力できます。 + */ + "i18nInfo": ParameterizedString<"link">; + /** + * アクセストークンの管理 + */ "manageAccessTokens": string; + /** + * アカウント情報 + */ "accountInfo": string; + /** + * ノートの数 + */ "notesCount": string; + /** + * 返信した数 + */ "repliesCount": string; + /** + * ブーストした数 + */ "renotesCount": string; + /** + * 返信された数 + */ "repliedCount": string; + /** + * ブーストされた数 + */ "renotedCount": string; + /** + * フォロー数 + */ "followingCount": string; + /** + * フォロワー数 + */ "followersCount": string; + /** + * リアクションした数 + */ "sentReactionsCount": string; + /** + * リアクションされた数 + */ "receivedReactionsCount": string; + /** + * アンケートに投票した数 + */ "pollVotesCount": string; + /** + * アンケートに投票された数 + */ "pollVotedCount": string; + /** + * はい + */ "yes": string; + /** + * いいえ + */ "no": string; + /** + * ドライブのファイル数 + */ "driveFilesCount": string; + /** + * ドライブ使用量 + */ "driveUsage": string; + /** + * クローラーによるインデックスを拒否 + */ "noCrawle": string; + /** + * 外部の検索エンジンにあなたのユーザーページ、ノート、Pagesなどのコンテンツを登録(インデックス)しないよう要求します。 + */ "noCrawleDescription": string; + /** + * フォローを承認制にしても、ノートの公開範囲を「フォロワー」にしない限り、誰でもあなたのノートを見ることができます。 + */ "lockedAccountInfo": string; + /** + * デフォルトでメディアをセンシティブ設定にする + */ "alwaysMarkSensitive": string; + /** + * 添付画像のサムネイルをオリジナル画質にする + */ "loadRawImages": string; + /** + * アニメーション画像を再生しない + */ "disableShowingAnimatedImages": string; + /** + * メディアがセンシティブであることを分かりやすく表示 + */ "highlightSensitiveMedia": string; + /** + * 確認のメールを送信しました。メールに記載されたリンクにアクセスして、設定を完了してください。 + */ "verificationEmailSent": string; + /** + * 未設定 + */ "notSet": string; + /** + * メールアドレスが確認されました + */ "emailVerified": string; + /** + * お気に入りノートの数 + */ "noteFavoritesCount": string; + /** + * Pageにいいねした数 + */ "pageLikesCount": string; + /** + * Pageにいいねされた数 + */ "pageLikedCount": string; + /** + * 連絡先 + */ "contact": string; + /** + * システムのデフォルトのフォントを使う + */ "useSystemFont": string; + /** + * クリップ + */ "clips": string; + /** + * 実験的機能 + */ "experimentalFeatures": string; + /** + * 実験的 + */ "experimental": string; + /** + * これは実験的な機能です。仕様が変更されたり、正常に動作しなかったりする可能性があります。 + */ "thisIsExperimentalFeature": string; + /** + * 開発者 + */ "developer": string; + /** + * アカウントを見つけやすくする + */ "makeExplorable": string; + /** + * オフにすると、「みつける」にアカウントが載らなくなります。 + */ "makeExplorableDescription": string; + /** + * 公開ノートをインデックス不可にする + */ "makeIndexable": string; + /** + * ノート検索があなたの公開ノートをインデックス化しないようにします。 + */ "makeIndexableDescription": string; + /** + * タイムラインのノートを離して表示 + */ "showGapBetweenNotesInTimeline": string; + /** + * 複製 + */ "duplicate": string; + /** + * 左 + */ "left": string; + /** + * 中央 + */ "center": string; + /** + * 広い + */ "wide": string; + /** + * 狭い + */ "narrow": string; + /** + * 設定はページリロード後に反映されます。今すぐリロードしますか? + */ "reloadToApplySetting": string; + /** + * 反映には再起動が必要です。 + */ "needReloadToApply": string; + /** + * タイトルバーを表示する + */ "showTitlebar": string; + /** + * キャッシュをクリア + */ "clearCache": string; - "onlineUsersCount": string; - "nUsers": string; - "nNotes": string; + /** + * {n}人がオンライン + */ + "onlineUsersCount": ParameterizedString<"n">; + /** + * {n}ユーザー + */ + "nUsers": ParameterizedString<"n">; + /** + * {n}ノート + */ + "nNotes": ParameterizedString<"n">; + /** + * エラーリポートを送信 + */ "sendErrorReports": string; + /** + * オンにすると、問題が発生したときにエラーの詳細情報がSharkeyに共有され、ソフトウェアの品質向上に役立てることができます。エラー情報には、OSのバージョン、ブラウザの種類、行動履歴などが含まれます。 + */ "sendErrorReportsDescription": string; + /** + * マイテーマ + */ "myTheme": string; + /** + * 背景 + */ "backgroundColor": string; + /** + * アクセント + */ "accentColor": string; + /** + * 文字 + */ "textColor": string; + /** + * 名前を付けて保存 + */ "saveAs": string; + /** + * 高度 + */ "advanced": string; + /** + * 高度な設定 + */ "advancedSettings": string; + /** + * 値 + */ "value": string; + /** + * 作成日時 + */ "createdAt": string; + /** + * 更新日時 + */ "updatedAt": string; + /** + * 保存しますか? + */ "saveConfirm": string; + /** + * 削除しますか? + */ "deleteConfirm": string; + /** + * 有効な値ではありません。 + */ "invalidValue": string; + /** + * レジストリ + */ "registry": string; + /** + * アカウントを閉鎖する + */ "closeAccount": string; + /** + * 現在のバージョン + */ "currentVersion": string; + /** + * 最新のバージョン + */ "latestVersion": string; + /** + * お使いのクライアントは最新です。 + */ "youAreRunningUpToDateClient": string; + /** + * 新しいバージョンのクライアントが利用可能です。 + */ "newVersionOfClientAvailable": string; + /** + * 使用量 + */ "usageAmount": string; + /** + * 容量 + */ "capacity": string; + /** + * 使用中 + */ "inUse": string; + /** + * コードを編集 + */ "editCode": string; + /** + * 適用 + */ "apply": string; + /** + * サーバーからのお知らせを受け取る + */ "receiveAnnouncementFromInstance": string; + /** + * メール通知 + */ "emailNotification": string; + /** + * 公開 + */ "publish": string; + /** + * チャンネル内検索 + */ "inChannelSearch": string; + /** + * 右クリックでリアクションピッカーを開く + */ "useReactionPickerForContextMenu": string; - "typingUsers": string; + /** + * {users}が入力中 + */ + "typingUsers": ParameterizedString<"users">; + /** + * 特定の日付にジャンプ + */ "jumpToSpecifiedDate": string; + /** + * 過去のタイムラインを表示しています + */ "showingPastTimeline": string; + /** + * クリア + */ "clear": string; + /** + * 全て既読にする + */ "markAllAsRead": string; + /** + * 戻る + */ "goBack": string; + /** + * いいね解除しますか? + */ "unlikeConfirm": string; + /** + * フルビュー + */ "fullView": string; + /** + * フルビュー解除 + */ "quitFullView": string; + /** + * 説明を追加 + */ "addDescription": string; + /** + * 個々のノートのメニューから「ピン留め」を選択することで、ここにノートを表示しておくことができます。 + */ "userPagePinTip": string; + /** + * 宛先に含まれていないメンションがあります + */ "notSpecifiedMentionWarning": string; + /** + * 情報 + */ "info": string; + /** + * ユーザー情報 + */ "userInfo": string; + /** + * 不明 + */ "unknown": string; + /** + * オンライン状態 + */ "onlineStatus": string; + /** + * オンライン状態を隠す + */ "hideOnlineStatus": string; + /** + * オンライン状態を隠すと、検索などの一部機能において利便性が低下することがあります。 + */ "hideOnlineStatusDescription": string; + /** + * オンライン + */ "online": string; + /** + * アクティブ + */ "active": string; + /** + * オフライン + */ "offline": string; + /** + * 非推奨 + */ "notRecommended": string; + /** + * Botプロテクション + */ "botProtection": string; + /** + * サーバーブロック・サイレンス + */ "instanceBlocking": string; + /** + * アカウントを選択 + */ "selectAccount": string; + /** + * アカウントを切り替え + */ "switchAccount": string; + /** + * 有効 + */ "enabled": string; + /** + * 無効 + */ "disabled": string; + /** + * クイックアクション + */ "quickAction": string; + /** + * ユーザー + */ "user": string; + /** + * 管理 + */ "administration": string; + /** + * アカウント + */ "accounts": string; + /** + * 切り替え + */ "switch": string; + /** + * 管理者情報が設定されていません。 + */ "noMaintainerInformationWarning": string; + /** + * Botプロテクションが設定されていません。 + */ "noBotProtectionWarning": string; + /** + * 設定する + */ "configure": string; + /** + * ギャラリーへ投稿 + */ "postToGallery": string; + /** + * このハッシュタグで投稿 + */ "postToHashtag": string; + /** + * ギャラリー + */ "gallery": string; + /** + * 最近の投稿 + */ "recentPosts": string; + /** + * 人気の投稿 + */ "popularPosts": string; + /** + * ノートで共有 + */ "shareWithNote": string; + /** + * 広告 + */ "ads": string; + /** + * 期限 + */ "expiration": string; + /** + * 開始期間 + */ "startingperiod": string; + /** + * メモ + */ "memo": string; + /** + * 優先度 + */ "priority": string; + /** + * 高 + */ "high": string; + /** + * 中 + */ "middle": string; + /** + * 低 + */ "low": string; + /** + * メールアドレスの設定がされていません。 + */ "emailNotConfiguredWarning": string; + /** + * 比率 + */ "ratio": string; + /** + * 本文をプレビュー + */ "previewNoteText": string; + /** + * カスタムCSS + */ "customCss": string; + /** + * この設定は必ず知識のある方が行ってください。不適切な設定を行うとクライアントが正常に使用できなくなる恐れがあります。 + */ "customCssWarn": string; + /** + * グローバル + */ "global": string; + /** + * アイコンを四角形で表示 + */ "squareAvatars": string; + /** + * 送信 + */ "sent": string; + /** + * 受信 + */ "received": string; + /** + * 検索結果 + */ "searchResult": string; + /** + * ハッシュタグ + */ "hashtags": string; + /** + * トラブルシューティング + */ "troubleshooting": string; + /** + * UIにぼかし効果を使用 + */ "useBlurEffect": string; + /** + * 詳しく + */ "learnMore": string; + /** + * Sharkeyが更新されました! + */ "misskeyUpdated": string; + /** + * 更新情報を見る + */ "whatIsNew": string; + /** + * 翻訳 + */ "translate": string; - "translatedFrom": string; + /** + * {x}から翻訳 + */ + "translatedFrom": ParameterizedString<"x">; + /** + * アカウントの削除が進行中です + */ "accountDeletionInProgress": string; + /** + * サーバー上であなたのアカウントを一意に識別するための名前。アルファベット(a~z, A~Z)、数字(0~9)、およびアンダーバー(_)が使用できます。ユーザー名は後から変更することは出来ません。 + */ "usernameInfo": string; + /** + * 藍モード + */ "aiChanMode": string; + /** + * 開発者モード + */ "devMode": string; + /** + * CWを維持する + */ "keepCw": string; + /** + * Pub/Subのアカウント + */ "pubSub": string; + /** + * 直近の通信 + */ "lastCommunication": string; + /** + * 解決済み + */ "resolved": string; + /** + * 未解決 + */ "unresolved": string; + /** + * フォロワーを解除 + */ "breakFollow": string; + /** + * フォロワー解除しますか? + */ "breakFollowConfirm": string; + /** + * オンになっています + */ "itsOn": string; + /** + * オフになっています + */ "itsOff": string; + /** + * オン + */ "on": string; + /** + * オフ + */ "off": string; + /** + * アカウント登録にメールアドレスを必須にする + */ "emailRequiredForSignup": string; + /** + * 新規ユーザーの承認が必要 + */ "approvalRequiredForSignup": string; + /** + * 未読 + */ "unread": string; + /** + * フィルタ + */ "filter": string; + /** + * コントロールパネル + */ "controlPanel": string; + /** + * アカウントを管理 + */ "manageAccounts": string; + /** + * リアクション一覧を公開する + */ "makeReactionsPublic": string; + /** + * あなたがしたリアクション一覧を誰でも見れるようにします。 + */ "makeReactionsPublicDescription": string; + /** + * クラシック + */ "classic": string; + /** + * スレッドをミュート + */ "muteThread": string; + /** + * スレッドのミュートを解除 + */ "unmuteThread": string; + /** + * フォローの公開範囲 + */ "followingVisibility": string; + /** + * フォロワーの公開範囲 + */ "followersVisibility": string; + /** + * さらにスレッドを見る + */ "continueThread": string; + /** + * アカウントが削除されます。よろしいですか? + */ "deleteAccountConfirm": string; + /** + * パスワードが間違っています。 + */ "incorrectPassword": string; - "voteConfirm": string; - "voteConfirmMulti": string; + /** + * 「{choice}」に投票しますか? + */ + "voteConfirm": ParameterizedString<"choice">; + /** + * 「{choice}」に投票しますか? + *  確認後、選択肢を増やすことができます。 + */ + "voteConfirmMulti": ParameterizedString<"choice">; + /** + * 隠す + */ "hide": string; + /** + * モバイルデバイスのときドロワーで表示 + */ "useDrawerReactionPickerForMobile": string; - "welcomeBackWithName": string; - "clickToFinishEmailVerification": string; + /** + * おかえりなさい、{name}さん + */ + "welcomeBackWithName": ParameterizedString<"name">; + /** + * [{ok}]を押して、メールアドレスの確認を完了してください。 + */ + "clickToFinishEmailVerification": ParameterizedString<"ok">; + /** + * デバイスタイプ + */ "overridedDeviceKind": string; + /** + * スマートフォン + */ "smartphone": string; + /** + * タブレット + */ "tablet": string; + /** + * 自動 + */ "auto": string; + /** + * テーマカラー + */ "themeColor": string; + /** + * サイズ + */ "size": string; + /** + * 列の数 + */ "numberOfColumn": string; + /** + * 検索 + */ "searchByGoogle": string; + /** + * サーバーデフォルトのライトテーマ + */ "instanceDefaultLightTheme": string; + /** + * サーバーデフォルトのダークテーマ + */ "instanceDefaultDarkTheme": string; + /** + * オブジェクト形式のテーマコードを記入します。 + */ "instanceDefaultThemeDescription": string; + /** + * ミュートする期限 + */ "mutePeriod": string; + /** + * 期限 + */ "period": string; + /** + * 無期限 + */ "indefinitely": string; + /** + * 10分 + */ "tenMinutes": string; + /** + * 1時間 + */ "oneHour": string; + /** + * 1日 + */ "oneDay": string; + /** + * 1週間 + */ "oneWeek": string; + /** + * 1ヶ月 + */ "oneMonth": string; + /** + * 反映されるまで時間がかかる場合があります。 + */ "reflectMayTakeTime": string; + /** + * アカウント情報の取得に失敗しました + */ "failedToFetchAccountInformation": string; + /** + * レート制限を超えました + */ "rateLimitExceeded": string; + /** + * 画像のクロップ + */ "cropImage": string; + /** + * 画像をクロップしますか? + */ "cropImageAsk": string; + /** + * クロップする + */ "cropYes": string; + /** + * そのまま使う + */ "cropNo": string; + /** + * ファイル + */ "file": string; - "recentNHours": string; - "recentNDays": string; + /** + * 直近{n}時間 + */ + "recentNHours": ParameterizedString<"n">; + /** + * 直近{n}日 + */ + "recentNDays": ParameterizedString<"n">; + /** + * メールサーバーの設定がされていません。 + */ "noEmailServerWarning": string; + /** + * 未対応の通報があります。 + */ "thereIsUnresolvedAbuseReportWarning": string; + /** + * 承認待ちのユーザーがいる。 + */ "pendingUserApprovals": string; + /** + * 推奨 + */ "recommended": string; + /** + * チェック + */ "check": string; + /** + * このユーザーのドライブ容量上限を変更 + */ "driveCapOverrideLabel": string; + /** + * 0以下を指定すると解除されます。 + */ "driveCapOverrideCaption": string; + /** + * 閲覧するには管理者アカウントでログインしている必要があります。 + */ "requireAdminForView": string; + /** + * システムにより自動で作成・管理されているアカウントです。 + */ "isSystemAccount": string; - "typeToConfirm": string; + /** + * この操作を行うには {x} と入力してください + */ + "typeToConfirm": ParameterizedString<"x">; + /** + * アカウント削除 + */ "deleteAccount": string; + /** + * 承認する + */ "approveAccount": string; + /** + * 拒否と削除 + */ "denyAccount": string; + /** + * 承認済み + */ "approved": string; + /** + * 承認されていない + */ "notApproved": string; + /** + * 承認状況 + */ "approvalStatus": string; + /** + * ドキュメント + */ "document": string; + /** + * ページキャッシュ数 + */ "numberOfPageCache": string; + /** + * 多くすると利便性が向上しますが、負荷とメモリ使用量が増えます。 + */ "numberOfPageCacheDescription": string; + /** + * スレッド内の返信数 + */ "numberOfReplies": string; + /** + * この数値を大きくすると、より多くの返信が表示されます。この値を大きくしすぎると、返信が窮屈になり、読めなくなることがあります。 + */ "numberOfRepliesDescription": string; + /** + * ブースト設定 + */ "boostSettings": string; + /** + * 可視性セレクタを表示 + */ "showVisibilitySelectorOnBoost": string; + /** + * 無効の場合、以下で定義されるデフォルトの可視性が使用され、セレクタは表示されません。 + */ "showVisibilitySelectorOnBoostDescription": string; + /** + * デフォルトのブースト可視性の設定 + */ "visibilityOnBoost": string; + /** + * ログアウトしますか? + */ "logoutConfirm": string; + /** + * 最終利用日時 + */ "lastActiveDate": string; + /** + * ステータスバー + */ "statusbar": string; + /** + * 選択してください + */ "pleaseSelect": string; + /** + * 反転 + */ "reverse": string; + /** + * 色付き + */ "colored": string; + /** + * 更新間隔 + */ "refreshInterval": string; + /** + * ラベル + */ "label": string; + /** + * タイプ + */ "type": string; + /** + * 速度 + */ "speed": string; + /** + * 遅い + */ "slow": string; + /** + * 速い + */ "fast": string; + /** + * センシティブなメディアの検出 + */ "sensitiveMediaDetection": string; + /** + * ローカルのみ + */ "localOnly": string; + /** + * リモートのみ + */ "remoteOnly": string; + /** + * アップロード失敗 + */ "failedToUpload": string; + /** + * 不適切な内容を含む可能性があると判定されたためアップロードできません。 + */ "cannotUploadBecauseInappropriate": string; + /** + * ドライブの空き容量が無いためアップロードできません。 + */ "cannotUploadBecauseNoFreeSpace": string; + /** + * ファイルサイズの制限を超えているためアップロードできません。 + */ "cannotUploadBecauseExceedsFileSizeLimit": string; + /** + * ベータ + */ "beta": string; + /** + * 自動センシティブ判定 + */ "enableAutoSensitive": string; + /** + * 利用可能な場合は、機械学習を利用して自動でメディアにセンシティブフラグを設定します。この機能をオフにしても、サーバーによっては自動で設定されることがあります。 + */ "enableAutoSensitiveDescription": string; + /** + * ユーザーのメールアドレスのバリデーションを、捨てアドかどうかや実際に通信可能かどうかなどを判定しより積極的に行います。オフにすると単に文字列として正しいかどうかのみチェックされます。 + */ "activeEmailValidationDescription": string; + /** + * ナビゲーションバー + */ "navbar": string; + /** + * シャッフル + */ "shuffle": string; + /** + * アカウント + */ "account": string; + /** + * 移動 + */ "move": string; + /** + * プッシュ通知 + */ "pushNotification": string; + /** + * プッシュ通知を有効化 + */ "subscribePushNotification": string; + /** + * プッシュ通知を停止する + */ "unsubscribePushNotification": string; + /** + * プッシュ通知は有効です + */ "pushNotificationAlreadySubscribed": string; + /** + * ブラウザかサーバーがプッシュ通知に非対応 + */ "pushNotificationNotSupported": string; + /** + * 通知が既読になったらプッシュ通知を削除する + */ "sendPushNotificationReadMessage": string; + /** + * 端末の電池消費量が増加する可能性があります。 + */ "sendPushNotificationReadMessageCaption": string; + /** + * 最大化 + */ "windowMaximize": string; + /** + * 最小化 + */ "windowMinimize": string; + /** + * 元に戻す + */ "windowRestore": string; + /** + * キャプション + */ "caption": string; + /** + * Botアカウントでログイン中 + */ "loggedInAsBot": string; + /** + * ツール + */ "tools": string; + /** + * 読み込めません + */ "cannotLoad": string; + /** + * プロフィール表示回数 + */ "numberOfProfileView": string; + /** + * いいね! + */ "like": string; + /** + * いいねを解除 + */ "unlike": string; + /** + * 絵文字のようなデフォルト + */ "defaultLike": string; + /** + * いいね数 + */ "numberOfLikes": string; + /** + * 表示 + */ "show": string; + /** + * 今後表示しない + */ "neverShow": string; + /** + * また後で + */ "remindMeLater": string; + /** + * Sharkeyを気に入っていただけましたか? + */ "didYouLikeMisskey": string; - "pleaseDonate": string; - "pleaseDonateInstance": string; + /** + * Sharkeyは{host}が使用している無料のソフトウェアです。これからも開発を続けられるように、ぜひ寄付をお願いします! + */ + "pleaseDonate": ParameterizedString<"host">; + /** + * インスタンス管理者への寄付によって{host}を直接サポートすることもできます。 + */ + "pleaseDonateInstance": ParameterizedString<"host">; + /** + * ロール + */ "roles": string; + /** + * ロール + */ "role": string; + /** + * ロールはありません + */ "noRole": string; + /** + * 一般ユーザー + */ "normalUser": string; + /** + * 未定義 + */ "undefined": string; + /** + * アサイン + */ "assign": string; + /** + * アサインを解除 + */ "unassign": string; + /** + * 色 + */ "color": string; + /** + * カスタム絵文字の管理 + */ "manageCustomEmojis": string; + /** + * アバターデコレーションの管理 + */ "manageAvatarDecorations": string; + /** + * これ以上作成することはできません。 + */ "youCannotCreateAnymore": string; + /** + * 一時的に利用できません + */ "cannotPerformTemporary": string; + /** + * 操作回数が制限を超過するため一時的に利用できません。しばらく時間を置いてから再度お試しください。 + */ "cannotPerformTemporaryDescription": string; + /** + * パラメータエラー + */ "invalidParamError": string; + /** + * リクエストパラメータに問題があります。通常これはバグですが、入力した文字数が多すぎる等の可能性もあります。 + */ "invalidParamErrorDescription": string; + /** + * 操作が拒否されました + */ "permissionDeniedError": string; + /** + * このアカウントにはこの操作を行うための権限がありません。 + */ "permissionDeniedErrorDescription": string; + /** + * プリセット + */ "preset": string; + /** + * プリセットから選択 + */ "selectFromPresets": string; + /** + * 実績 + */ "achievements": string; + /** + * サーバーの応答が無効です + */ "gotInvalidResponseError": string; + /** + * サーバーがダウンまたはメンテナンスしている可能性があります。しばらくしてから再度お試しください。 + */ "gotInvalidResponseErrorDescription": string; + /** + * この投稿は迷惑になる可能性があります。 + */ "thisPostMayBeAnnoying": string; + /** + * ホームに投稿 + */ "thisPostMayBeAnnoyingHome": string; + /** + * やめる + */ "thisPostMayBeAnnoyingCancel": string; + /** + * このまま投稿 + */ "thisPostMayBeAnnoyingIgnore": string; + /** + * 見たことのあるブーストを省略して表示 + */ "collapseRenotes": string; + /** + * ファイルを折りたたむ + */ "collapseFiles": string; + /** + * 返信に会話を読み込む + */ "autoloadConversation": string; + /** + * サーバー内部エラー + */ "internalServerError": string; + /** + * サーバー内部で予期しないエラーが発生しました。 + */ "internalServerErrorDescription": string; + /** + * エラー情報をコピー + */ "copyErrorInfo": string; + /** + * このサーバーに登録する + */ "joinThisServer": string; + /** + * 他のサーバーを探す + */ "exploreOtherServers": string; + /** + * タイムラインを見てみる + */ "letsLookAtTimeline": string; + /** + * 連合なしにしますか? + */ "disableFederationConfirm": string; + /** + * 連合なしにしても投稿は非公開になりません。ほとんどの場合、連合なしにする必要はありません。 + */ "disableFederationConfirmWarn": string; + /** + * 連合なしにする + */ "disableFederationOk": string; + /** + * 現在このサーバーは招待制です。招待コードをお持ちの方のみ登録できます。 + */ "invitationRequiredToRegister": string; + /** + * このインスタンスは、登録理由を指定したユーザーのみを受け入れています。 + */ "approvalRequiredToRegister": string; + /** + * このサーバーではメール配信はサポートされていません + */ "emailNotSupported": string; + /** + * チャンネルに投稿 + */ "postToTheChannel": string; + /** + * 後から変更できません。 + */ "cannotBeChangedLater": string; + /** + * リアクションの受け入れ + */ "reactionAcceptance": string; + /** + * いいねのみ + */ "likeOnly": string; + /** + * 全て (リモートはいいねのみ) + */ "likeOnlyForRemote": string; + /** + * 非センシティブのみ + */ "nonSensitiveOnly": string; + /** + * 非センシティブのみ (リモートはいいねのみ) + */ "nonSensitiveOnlyForLocalLikeOnlyForRemote": string; + /** + * 自分に割り当てられたロール + */ "rolesAssignedToMe": string; + /** + * パスワードリセットしますか? + */ "resetPasswordConfirm": string; + /** + * センシティブワード + */ "sensitiveWords": string; + /** + * 設定したワードが含まれるノートの公開範囲をホームにします。改行で区切って複数設定できます。 + */ "sensitiveWordsDescription": string; + /** + * スペースで区切るとAND指定になり、キーワードをスラッシュで囲むと正規表現になります。 + */ "sensitiveWordsDescription2": string; + /** + * 非表示ハッシュタグ + */ "hiddenTags": string; + /** + * 設定したタグをトレンドに表示させないようにします。改行で区切って複数設定できます。 + */ "hiddenTagsDescription": string; + /** + * ノート検索は利用できません。 + */ "notesSearchNotAvailable": string; + /** + * ライセンス + */ "license": string; + /** + * お気に入り解除しますか? + */ "unfavoriteConfirm": string; + /** + * 自分のクリップ + */ "myClips": string; + /** + * ドライブクリーナー + */ "drivecleaner": string; + /** + * すべてのキューを今すぐ再試行 + */ "retryAllQueuesNow": string; + /** + * 今すぐ再試行しますか? + */ "retryAllQueuesConfirmTitle": string; + /** + * 一時的にサーバーの負荷が増大することがあります。 + */ "retryAllQueuesConfirmText": string; + /** + * リモートユーザーのチャートを生成 + */ "enableChartsForRemoteUser": string; + /** + * リモートサーバーのチャートを生成 + */ "enableChartsForFederatedInstances": string; + /** + * ノートのアクションにクリップを追加 + */ "showClipButtonInNoteFooter": string; + /** + * リアクションの表示サイズ + */ "reactionsDisplaySize": string; + /** + * リアクションの最大横幅を制限し、縮小して表示する + */ "limitWidthOfReaction": string; + /** + * ノートIDまたはURL + */ "noteIdOrUrl": string; + /** + * 動画 + */ "video": string; + /** + * 動画 + */ "videos": string; + /** + * 音声 + */ + "audio": string; + /** + * 音声 + */ + "audioFiles": string; + /** + * データセーバー + */ "dataSaver": string; + /** + * アカウントの移行 + */ "accountMigration": string; + /** + * このユーザーは新しいアカウントに移行しました: + */ "accountMoved": string; + /** + * このアカウントは移行されています + */ "accountMovedShort": string; + /** + * この操作はできません + */ "operationForbidden": string; + /** + * 常に広告を表示する + */ "forceShowAds": string; + /** + * メモを追加 + */ "addMemo": string; + /** + * メモを編集 + */ "editMemo": string; + /** + * リアクション一覧 + */ "reactionsList": string; + /** + * ブースト一覧 + */ "renotesList": string; + /** + * 通知の表示 + */ "notificationDisplay": string; + /** + * 左上 + */ "leftTop": string; + /** + * 右上 + */ "rightTop": string; + /** + * 左下 + */ "leftBottom": string; + /** + * 右下 + */ "rightBottom": string; + /** + * スタック方向 + */ "stackAxis": string; + /** + * 縦 + */ "vertical": string; + /** + * 横 + */ "horizontal": string; + /** + * 位置 + */ "position": string; + /** + * サーバールール + */ "serverRules": string; + /** + * このサーバーに登録するには、以下の内容を確認し同意する必要があります。 + */ "pleaseConfirmBelowBeforeSignup": string; + /** + * 続けるには、全ての「同意する」にチェックが入っている必要があります。 + */ "pleaseAgreeAllToContinue": string; + /** + * 続ける + */ "continue": string; + /** + * 予約ユーザー名 + */ "preservedUsernames": string; + /** + * 予約するユーザー名を改行で列挙します。ここで指定されたユーザー名はアカウント作成時に使えなくなりますが、管理者によるアカウント作成時はこの制限を受けません。また、既に存在するアカウントも影響を受けません。 + */ "preservedUsernamesDescription": string; + /** + * このファイルからノートを作成 + */ "createNoteFromTheFile": string; + /** + * アーカイブ + */ "archive": string; - "channelArchiveConfirmTitle": string; + /** + * {name}をアーカイブしますか? + */ + "channelArchiveConfirmTitle": ParameterizedString<"name">; + /** + * アーカイブすると、チャンネル一覧や検索結果に表示されなくなり、新たな書き込みもできなくなります。 + */ "channelArchiveConfirmDescription": string; + /** + * このチャンネルはアーカイブされています。 + */ "thisChannelArchived": string; + /** + * ノートの表示 + */ "displayOfNote": string; + /** + * 初期設定 + */ "initialAccountSetting": string; + /** + * フォロー中 + */ "youFollowing": string; + /** + * 生成AIによる学習を拒否 + */ "preventAiLearning": string; + /** + * 外部の文章生成AIや画像生成AIに対して、投稿したノートや画像などのコンテンツを学習の対象にしないように要求します。これはnoaiフラグをHTMLレスポンスに含めることによって実現されますが、この要求に従うかはそのAI次第であるため、学習を完全に防止するものではありません。 + */ "preventAiLearningDescription": string; + /** + * オプション + */ "options": string; + /** + * ユーザー指定 + */ "specifyUser": string; + /** + * プレビューできません + */ "failedToPreviewUrl": string; + /** + * 更新 + */ "update": string; + /** + * リアクションとして使えるロール + */ "rolesThatCanBeUsedThisEmojiAsReaction": string; + /** + * ロールの指定が一つもない場合、誰でもリアクションとして使えます。 + */ "rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription": string; + /** + * ロールは公開ロールである必要があります。 + */ "rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn": string; + /** + * リアクションを取り消しますか? + */ "cancelReactionConfirm": string; + /** + * リアクションを変更しますか? + */ "changeReactionConfirm": string; + /** + * あとで + */ "later": string; + /** + * Sharkeyへ + */ "goToMisskey": string; + /** + * 絵文字の追加辞書 + */ "additionalEmojiDictionary": string; + /** + * インストール済み + */ "installed": string; + /** + * ブランディング + */ "branding": string; + /** + * サーバーのマシン情報を公開する + */ "enableServerMachineStats": string; + /** + * 実績を有効にする + */ "enableAchievements": string; + /** + * オフにすると実績システムは無効になります。 + */ "turnOffAchievements": string; + /** + * botのハッシュタグ追加を許可する + */ "enableBotTrending": string; + /** + * オフにするとボットがハッシュタグを入力しなくなります。 + */ "turnOffBotTrending": string; + /** + * ユーザーごとのIdenticon生成を有効にする + */ "enableIdenticonGeneration": string; + /** + * オフにするとパフォーマンスが向上します。 + */ "turnOffToImprovePerformance": string; + /** + * 招待コードを作成 + */ "createInviteCode": string; + /** + * オプションを指定して作成 + */ "createWithOptions": string; + /** + * 作成数 + */ "createCount": string; + /** + * 招待コードを作成しました + */ "inviteCodeCreated": string; + /** + * 作成できる招待コードの数が上限に達しています。 + */ "inviteLimitExceeded": string; - "createLimitRemaining": string; - "inviteLimitResetCycle": string; + /** + * 作成できる招待コード: 残り {limit} 個 + */ + "createLimitRemaining": ParameterizedString<"limit">; + /** + * {time}で最大 {limit} 個の招待コードを作成できます。 + */ + "inviteLimitResetCycle": ParameterizedString<"time" | "limit">; + /** + * 有効期限 + */ "expirationDate": string; + /** + * 有効期限を設けない + */ "noExpirationDate": string; + /** + * 招待コードが使用された日時 + */ "inviteCodeUsedAt": string; + /** + * 招待コードを使用したユーザー + */ "registeredUserUsingInviteCode": string; + /** + * メール認証待ち + */ "waitingForMailAuth": string; + /** + * 招待コードを作成したユーザー + */ "inviteCodeCreator": string; + /** + * 使用日時 + */ "usedAt": string; + /** + * 未使用 + */ "unused": string; + /** + * 使用済み + */ "used": string; + /** + * 期限切れ + */ "expired": string; + /** + * 同意しますか? + */ "doYouAgree": string; + /** + * 重要ですので必ずお読みください。 + */ "beSureToReadThisAsItIsImportant": string; - "iHaveReadXCarefullyAndAgree": string; + /** + * 「{x}」の内容をよく読み、同意します。 + */ + "iHaveReadXCarefullyAndAgree": ParameterizedString<"x">; + /** + * ダイアログ + */ "dialog": string; + /** + * アイコン + */ "icon": string; + /** + * あなたへ + */ "forYou": string; + /** + * 現在のお知らせ + */ "currentAnnouncements": string; + /** + * 過去のお知らせ + */ "pastAnnouncements": string; + /** + * 未読のお知らせがあります。 + */ "youHaveUnreadAnnouncements": string; + /** + * ブラウザまたはデバイスの指示に従って、セキュリティキーまたはパスキーを使用してください。 + */ "useSecurityKey": string; + /** + * 返信 + */ "replies": string; + /** + * ブースト + */ "renotes": string; + /** + * 返信を見る + */ "loadReplies": string; + /** + * 会話を見る + */ "loadConversation": string; + /** + * ピン留めされたリスト + */ "pinnedList": string; + /** + * デバイスの画面を常にオンにする + */ "keepScreenOn": string; + /** + * クリックしてノートを開く + */ "clickToOpen": string; + /** + * ボットをタイムラインに表示 + */ "showBots": string; + /** + * このリンク先の所有者であることが確認されました + */ "verifiedLink": string; + /** + * 投稿を通知 + */ "notifyNotes": string; + /** + * 投稿の通知を解除 + */ "unnotifyNotes": string; + /** + * 認証 + */ "authentication": string; + /** + * 続けるには認証を行ってください + */ "authenticationRequiredToContinue": string; + /** + * 日時 + */ "dateAndTime": string; + /** + * ブーストを表示 + */ "showRenotes": string; + /** + * 編集済み + */ "edited": string; + /** + * 通知の受信設定 + */ "notificationRecieveConfig": string; + /** + * 相互フォロー + */ "mutualFollow": string; + /** + * ファイル付きのみ + */ "fileAttachedOnly": string; + /** + * TLに他の人への返信を含める + */ "showRepliesToOthersInTimeline": string; + /** + * TLに他の人への返信を含めない + */ "hideRepliesToOthersInTimeline": string; + /** + * TLに現在フォロー中の人全員の返信を含めるようにする + */ "showRepliesToOthersInTimelineAll": string; + /** + * TLに現在フォロー中の人全員の返信を含めないようにする + */ "hideRepliesToOthersInTimelineAll": string; + /** + * この操作は元に戻せません。本当にTLに現在フォロー中の人全員の返信を含めるようにしますか? + */ "confirmShowRepliesAll": string; + /** + * この操作は元に戻せません。本当にTLに現在フォロー中の人全員の返信を含めないようにしますか? + */ "confirmHideRepliesAll": string; + /** + * 外部サービス + */ "externalServices": string; + /** + * 運営者情報 + */ "impressum": string; + /** + * 運営者情報URL + */ "impressumUrl": string; + /** + * ドイツなどの一部の国と地域では表示が義務付けられています(Impressum)。 + */ "impressumDescription": string; + /** + * プライバシーポリシー + */ "privacyPolicy": string; + /** + * プライバシーポリシーURL + */ "privacyPolicyUrl": string; + /** + * 利用規約・プライバシーポリシー + */ "tosAndPrivacyPolicy": string; + /** + * 寄付する + */ "donation": string; + /** + * 寄付URL + */ "donationUrl": string; + /** + * アイコンデコレーション + */ "avatarDecorations": string; + /** + * 付ける + */ "attach": string; + /** + * 外す + */ "detach": string; + /** + * 全て外す + */ "detachAll": string; + /** + * 角度 + */ "angle": string; + /** + * 反転 + */ "flip": string; + /** + * アイコンのデコレーションを表示 + */ "showAvatarDecorations": string; + /** + * 離してリロード + */ "releaseToRefresh": string; + /** + * リロード中 + */ "refreshing": string; + /** + * 引っ張ってリロード + */ "pullDownToRefresh": string; + /** + * タイムラインのリアルタイム更新を無効にする + */ "disableStreamingTimeline": string; + /** + * 通知をグルーピングして表示する + */ "useGroupedNotifications": string; + /** + * メールアドレスの確認中に問題が発生しました。リンクの有効期限が切れている可能性があります。 + */ "signupPendingError": string; + /** + * 「内容を隠す」がオンの場合は注釈の記述が必要です。 + */ "cwNotationRequired": string; + /** + * リアクションする + */ "doReaction": string; + /** + * コード + */ "code": string; + /** + * 設定の反映にはリロードが必要です。 + */ "reloadRequiredToApplySettings": string; - "remainingN": string; + /** + * 残り: {n} + */ + "remainingN": ParameterizedString<"n">; + /** + * 現在の内容に上書きされますがよろしいですか? + */ "overwriteContentConfirm": string; + /** + * 季節に応じた画面の演出 + */ "seasonalScreenEffect": string; + /** + * デコる + */ "decorate": string; + /** + * 装飾を追加 + */ "addMfmFunction": string; + /** + * 高度なMFMのピッカーを表示する + */ "enableQuickAddMfmFunction": string; + /** + * バブルゲーム + */ "bubbleGame": string; + /** + * 効果音 + */ "sfx": string; + /** + * サウンドが再生されます + */ "soundWillBePlayed": string; + /** + * リプレイを見る + */ "showReplay": string; + /** + * リプレイ + */ "replay": string; + /** + * リプレイ中 + */ "replaying": string; + /** + * ランキング + */ "ranking": string; + /** + * 直近{n}日 + */ + "lastNDays": ParameterizedString<"n">; + /** + * タイトルへ + */ + "backToTitle": string; + /** + * お住まいの地域 + */ + "hemisphere": string; + /** + * センシティブなファイルを含むノートを表示 + */ + "withSensitive": string; + /** + * {name}のセンシティブなファイルを含む投稿 + */ + "userSaysSomethingSensitive": ParameterizedString<"name">; + /** + * スワイプしてタブを切り替える + */ + "enableHorizontalSwipe": string; "_bubbleGame": { + /** + * 遊び方 + */ "howToPlay": string; "_howToPlay": { + /** + * 位置を調整してハコにモノを落とします。 + */ "section1": string; + /** + * 同じ種類のモノがくっつくと別のモノに変化して、スコアが得られます。 + */ "section2": string; + /** + * モノがハコからあふれるとゲームオーバーです。ハコからあふれないようにしつつモノを融合させてハイスコアを目指そう! + */ "section3": string; }; }; "_announcement": { + /** + * 既存ユーザーのみ + */ "forExistingUsers": string; + /** + * 有効にすると、このお知らせ作成時点で存在するユーザーにのみお知らせが表示されます。無効にすると、このお知らせ作成後にアカウントを作成したユーザーにもお知らせが表示されます。 + */ "forExistingUsersDescription": string; + /** + * 既読にするのに確認が必要 + */ "needConfirmationToRead": string; + /** + * 有効にすると、このお知らせを既読にする際に確認ダイアログが表示されます。また、一括既読操作の対象になりません。 + */ "needConfirmationToReadDescription": string; + /** + * お知らせを終了 + */ "end": string; + /** + * アクティブなお知らせが多いため、UXが低下する可能性があります。終了したお知らせはアーカイブすることを検討してください。 + */ "tooManyActiveAnnouncementDescription": string; + /** + * 既読にしますか? + */ "readConfirmTitle": string; - "readConfirmText": string; + /** + * 「{title}」の内容を読み、既読にします。 + */ + "readConfirmText": ParameterizedString<"title">; + /** + * 特に新規ユーザーのUXを損ねる可能性が高いため、ストック情報ではなくフロー情報の掲示にお知らせを使用することを推奨します。 + */ "shouldNotBeUsedToPresentPermanentInfo": string; + /** + * ダイアログ形式のお知らせが同時に2つ以上ある場合、UXに悪影響を及ぼす可能性が非常に高いため、使用は慎重に行うことを推奨します。 + */ "dialogAnnouncementUxWarn": string; + /** + * 非通知 + */ "silence": string; + /** + * オンにすると、このお知らせは通知されず、既読にする必要もなくなります。 + */ "silenceDescription": string; }; "_initialAccountSetting": { + /** + * アカウントの作成が完了しました! + */ "accountCreated": string; + /** + * さっそくアカウントの初期設定を行いましょう。 + */ "letsStartAccountSetup": string; + /** + * まずはあなたのプロフィールを設定しましょう。 + */ "letsFillYourProfile": string; + /** + * プロフィール設定 + */ "profileSetting": string; + /** + * プライバシー設定 + */ "privacySetting": string; + /** + * これらの設定は後から変更できます。 + */ "theseSettingsCanEditLater": string; + /** + * この他にも様々な設定を「設定」ページから行えます。ぜひ後で確認してみてください。 + */ "youCanEditMoreSettingsInSettingsPageLater": string; + /** + * タイムラインを構築するため、気になるユーザーをフォローしてみましょう。 + */ "followUsers": string; - "pushNotificationDescription": string; + /** + * プッシュ通知を有効にすると{name}の通知をお使いのデバイスで受け取ることができます。 + */ + "pushNotificationDescription": ParameterizedString<"name">; + /** + * 初期設定が完了しました! + */ "initialAccountSettingCompleted": string; - "haveFun": string; - "youCanContinueTutorial": string; + /** + * {name}をお楽しみください! + */ + "haveFun": ParameterizedString<"name">; + /** + * このまま{name}(Sharkey)の使い方についてのチュートリアルに進むこともできますが、ここで中断してすぐに使い始めることもできます。 + */ + "youCanContinueTutorial": ParameterizedString<"name">; + /** + * チュートリアルを開始 + */ "startTutorial": string; + /** + * 初期設定をスキップしますか? + */ "skipAreYouSure": string; + /** + * 初期設定をあとでやり直しますか? + */ "laterAreYouSure": string; }; "_initialTutorial": { + /** + * チュートリアルを見る + */ "launchTutorial": string; + /** + * チュートリアル + */ "title": string; + /** + * よくできました + */ "wellDone": string; + /** + * チュートリアルを終了しますか? + */ "skipAreYouSure": string; "_landing": { + /** + * チュートリアルへようこそ + */ "title": string; + /** + * ここでは、Sharkeyの基本的な使い方や機能を確認できます。 + */ "description": string; }; "_note": { + /** + * ノートって何? + */ "title": string; + /** + * Sharkeyでの投稿は「ノート」と呼びます。ノートはタイムラインに時系列で並んでいて、リアルタイムで更新されていきます。 + */ "description": string; + /** + * 返信することができます。返信に対しての返信も可能で、スレッドのように会話を続けることもできます。 + */ "reply": string; + /** + * そのノートを自分のタイムラインに流して共有することができます。テキストを追加して引用することも可能です。 + */ "renote": string; + /** + * リアクションをつけることができます。詳しくは次のページで解説します。 + */ "reaction": string; + /** + * ノートの詳細を表示したり、リンクをコピーしたりなどの様々な操作が行えます。 + */ "menu": string; }; "_reaction": { + /** + * リアクションって何? + */ "title": string; + /** + * ノートには「リアクション」をつけることができます。「いいね」では伝わらないニュアンスも、リアクションで簡単・気軽に表現できます。 + */ "description": string; + /** + * リアクションは、ノートの「+」ボタンをクリックするとつけられます。試しにこのサンプルのノートにリアクションをつけてみてください! + */ "letsTryReacting": string; + /** + * リアクションをつけると先に進めるようになります。 + */ "reactToContinue": string; + /** + * あなたのノートが誰かにリアクションされると、リアルタイムで通知を受け取ります。 + */ "reactNotification": string; + /** + * 「ー」ボタンを押すとリアクションを取り消すことができます。 + */ "reactDone": string; }; "_timeline": { + /** + * タイムラインのしくみ + */ "title": string; + /** + * Sharkeyには、使い方に応じて複数のタイムラインが用意されています(サーバーによってはいずれかが無効になっていることがあります)。 + */ "description1": string; + /** + * あなたがフォローしているアカウントの投稿を見られます。 + */ "home": string; + /** + * このサーバーにいるユーザー全員の投稿を見られます。 + */ "local": string; + /** + * ホームタイムラインとローカルタイムラインの投稿が両方表示されます。 + */ "social": string; + /** + * 接続している他のすべてのサーバーからの投稿を見られます。 + */ "global": string; + /** + * それぞれのタイムラインは、画面上部でいつでも切り替えられます。 + */ "description2": string; - "description3": string; + /** + * その他にも、リストタイムラインやチャンネルタイムラインなどがあります。詳しくは{link}をご覧ください。 + */ + "description3": ParameterizedString<"link">; }; "_postNote": { + /** + * ノートの投稿設定 + */ "title": string; + /** + * Sharkeyにノートを投稿する際には、様々なオプションの設定が可能です。投稿フォームはこのようになっています。 + */ "description1": string; "_visibility": { + /** + * ノートを表示できる相手を制限できます。 + */ "description": string; + /** + * すべてのユーザーに公開。 + */ "public": string; + /** + * ホームタイムラインのみに公開。フォロワー・プロフィールを見に来た人・ブーストから、他のユーザーも見ることができます。 + */ "home": string; + /** + * フォロワーにのみ公開。本人以外がブーストすることはできず、またフォロワー以外は閲覧できません。 + */ "followers": string; + /** + * 指定したユーザーにのみ公開され、また相手に通知が入ります。ダイレクトメッセージのかわりにお使いいただけます。 + */ "direct": string; + /** + * 機密情報は送信する際は注意してください。 + */ "doNotSendConfidencialOnDirect1": string; + /** + * 送信先のサーバーの管理者は投稿内容を見ることが可能なので、信頼できないサーバーのユーザーにダイレクト投稿を送信する場合は、機密情報の扱いに注意が必要です。 + */ "doNotSendConfidencialOnDirect2": string; + /** + * 他のサーバーに投稿を連合しません。上記の公開範囲に関わらず、他のサーバーのユーザーは、この設定がついたノートを直接閲覧することができなくなります。 + */ "localOnly": string; }; "_cw": { + /** + * 内容を隠す(CW) + */ "title": string; + /** + * 本文のかわりに「注釈」に書いた内容が表示されます。「もっと見る」を押すと本文が表示されます。 + */ "description": string; "_exampleNote": { + /** + * 飯テロ注意 + */ "cw": string; + /** + * チョコのかかったドーナツを食べました🍩😋 + */ "note": string; }; + /** + * サーバーのガイドラインにより必要とされるノートに指定したり、ネタバレ投稿やセンシティブな文章を自主規制したりするときに使います。 + */ "useCases": string; }; }; "_howToMakeAttachmentsSensitive": { + /** + * 添付ファイルをセンシティブにするには? + */ "title": string; + /** + * サーバーのガイドラインにより必要とされる際や、そのまま見れる状態にしておくべきではない添付ファイルには、「センシティブ」設定を付けます。 + */ "description": string; + /** + * 試しに、このフォームに添付された画像をセンシティブにしてみてください! + */ "tryThisFile": string; "_exampleNote": { + /** + * 納豆のフタ開けるのミスったわね… + */ "note": string; }; + /** + * 添付ファイルをセンシティブにする際は、そのファイルをクリックしてメニューを開き、「センシティブとして設定」をクリックします。 + */ "method": string; + /** + * ファイルを添付する際は、サーバーのガイドラインに従ってセンシティブを適切に設定してください。 + */ "sensitiveSucceeded": string; + /** + * 画像をセンシティブに設定すると先に進めるようになります。 + */ "doItToContinue": string; }; "_done": { + /** + * チュートリアルは終了です🎉 + */ "title": string; - "description": string; + /** + * ここで紹介した機能はほんの一部にすぎません。Sharkeyの使い方をより詳しく知るには、{link}をご覧ください。 + */ + "description": ParameterizedString<"link">; }; }; "_timelineDescription": { + /** + * ホームタイムラインでは、あなたがフォローしているアカウントの投稿を見られます。 + */ "home": string; + /** + * ローカルタイムラインでは、このサーバーにいるユーザー全員の投稿を見られます。 + */ "local": string; + /** + * ソーシャルタイムラインには、ホームタイムラインとローカルタイムラインの投稿が両方表示されます。 + */ "social": string; + /** + * グローバルタイムラインでは、接続している他のすべてのサーバーからの投稿を見られます。 + */ "global": string; }; "_serverRules": { + /** + * 新規登録前に表示する、サーバーの簡潔なルールを設定します。内容は利用規約の要約とすることを推奨します。 + */ "description": string; }; "_serverSettings": { + /** + * アイコン画像のURL + */ "iconUrl": string; - "appIconDescription": string; + /** + * {host}がアプリとして表示される際のアイコンを指定します。 + */ + "appIconDescription": ParameterizedString<"host">; + /** + * 例: PWAや、スマートフォンのホーム画面にブックマークとして追加された時など + */ "appIconUsageExample": string; + /** + * 円形もしくは角丸にクロップされる場合があるため、塗り潰された余白のある背景を持つことが推奨されます。 + */ "appIconStyleRecommendation": string; - "appIconResolutionMustBe": string; + /** + * 解像度は必ず{resolution}である必要があります。 + */ + "appIconResolutionMustBe": ParameterizedString<"resolution">; + /** + * manifest.jsonのオーバーライド + */ "manifestJsonOverride": string; + /** + * 略称 + */ "shortName": string; + /** + * サーバーの正式名称が長い場合に、代わりに表示することのできる略称や通称。 + */ "shortNameDescription": string; + /** + * 有効にすると、各種タイムラインを取得する際のパフォーマンスが大幅に向上し、データベースへの負荷を軽減することが可能です。ただし、Redisのメモリ使用量は増加します。サーバーのメモリ容量が少ない場合、または動作が不安定な場合は無効にすることができます。 + */ "fanoutTimelineDescription": string; + /** + * データベースへのフォールバック + */ "fanoutTimelineDbFallback": string; + /** + * 有効にすると、タイムラインがキャッシュされていない場合にDBへ追加で問い合わせを行うフォールバック処理を行います。無効にすると、フォールバック処理を行わないことでさらにサーバーの負荷を軽減することができますが、タイムラインが取得できる範囲に制限が生じます。 + */ "fanoutTimelineDbFallbackDescription": string; }; "_accountMigration": { + /** + * 別のアカウントからこのアカウントに移行 + */ "moveFrom": string; + /** + * 別のアカウントへエイリアスを作成 + */ "moveFromSub": string; - "moveFromLabel": string; + /** + * 移行元のアカウント #{n} + */ + "moveFromLabel": ParameterizedString<"n">; + /** + * 別のアカウントからこのアカウントに移行したい場合、ここでエイリアスを作成しておく必要があります。 + * 移行元のアカウントをこのように入力してください: @username@server.example.com + * 削除するには、入力欄を空にして保存します(非推奨)。 + */ "moveFromDescription": string; + /** + * このアカウントを新しいアカウントへ移行 + */ "moveTo": string; + /** + * 移行先のアカウント: + */ "moveToLabel": string; + /** + * アカウントを移行すると、取り消すことはできません。 + */ "moveCannotBeUndone": string; + /** + * 新しいアカウントへ移行します。 + *  ・フォロワーが新しいアカウントを自動でフォローします + *  ・このアカウントからのフォローは全て解除されます + *  ・このアカウントではノートの作成などができなくなります + * + * フォロワーの移行は自動ですが、フォローの移行は手動で行う必要があります。移行前にこのアカウントでフォローエクスポートし、移行後すぐに移行先アカウントでインポートを行なってください。 + * リスト・ミュート・ブロックについても同様ですので、手動で移行する必要があります。 + * + * (この説明はこのサーバー(Sharkey v13.12.0以降)の仕様です。Mastodonなどの他のActivityPubソフトウェアでは挙動が異なる場合があります。) + */ "moveAccountDescription": string; + /** + * アカウントの移行には、まずは移行先のアカウントでこのアカウントに対しエイリアスを作成します。 + * エイリアス作成後、移行先のアカウントを次のように入力してください: @username@server.example.com + */ "moveAccountHowTo": string; + /** + * 移行する + */ "startMigration": string; - "migrationConfirm": string; + /** + * 本当にこのアカウントを {account} に移行しますか?一度移行すると取り消せず、二度とこのアカウントを元の状態で使用できなくなります。 + */ + "migrationConfirm": ParameterizedString<"account">; + /** + * + * アカウントは移行されています。 + * 移行を取り消すことはできません。 + */ "movedAndCannotBeUndone": string; + /** + * このアカウントからのフォロー解除は移行操作から24時間後に実行されます。 + * このアカウントのフォロー・フォロワー数は0になっています。フォロワーの解除はされないため、あなたのフォロワーはこのアカウントのフォロワー向け投稿を引き続き閲覧できます。 + */ "postMigrationNote": string; + /** + * 移行先のアカウント: + */ "movedTo": string; }; "_achievements": { + /** + * 獲得日時 + */ "earnedAt": string; "_types": { "_notes1": { + /** + * just setting up my shonk + */ "title": string; + /** + * 初めてノートを投稿した + */ "description": string; + /** + * 良いSharkeyライフを! + */ "flavor": string; }; "_notes10": { + /** + * いくつかのノート + */ "title": string; + /** + * ノートを10回投稿した + */ "description": string; }; "_notes100": { + /** + * たくさんのノート + */ "title": string; + /** + * ノートを100回投稿した + */ "description": string; }; "_notes500": { + /** + * ノートまみれ + */ "title": string; + /** + * ノートを500回投稿した + */ "description": string; }; "_notes1000": { + /** + * ノートの山 + */ "title": string; + /** + * ノートを1,000回投稿した + */ "description": string; }; "_notes5000": { + /** + * 湧き出るノート + */ "title": string; + /** + * ノートを5,000回投稿した + */ "description": string; }; "_notes10000": { + /** + * スーパーノート + */ "title": string; + /** + * ノートを10,000回投稿した + */ "description": string; }; "_notes20000": { + /** + * ニードモアノート + */ "title": string; + /** + * ノートを20,000回投稿した + */ "description": string; }; "_notes30000": { + /** + * ノートノートノート + */ "title": string; + /** + * ノートを30,000回投稿した + */ "description": string; }; "_notes40000": { + /** + * ノート工場 + */ "title": string; + /** + * ノートを40,000回投稿した + */ "description": string; }; "_notes50000": { + /** + * ノートの惑星 + */ "title": string; + /** + * ノートを50,000回投稿した + */ "description": string; }; "_notes60000": { + /** + * ノートクエーサー + */ "title": string; + /** + * ノートを60,000回投稿した + */ "description": string; }; "_notes70000": { + /** + * ブラックノートホール + */ "title": string; + /** + * ノートを70,000回投稿した + */ "description": string; }; "_notes80000": { + /** + * ノートギャラクシー + */ "title": string; + /** + * ノートを80,000回投稿した + */ "description": string; }; "_notes90000": { + /** + * ノートバース + */ "title": string; + /** + * ノートを90,000回投稿した + */ "description": string; }; "_notes100000": { + /** + * ALL YOUR NOTE ARE BELONG TO US + */ "title": string; + /** + * ノートを100,000回投稿した + */ "description": string; + /** + * そんなに書くことある? + */ "flavor": string; }; "_login3": { + /** + * ビギナーⅠ + */ "title": string; + /** + * 通算ログイン日数が3日 + */ "description": string; + /** + * 今日からね僕は ミスキストってことで + */ "flavor": string; }; "_login7": { + /** + * ビギナーⅡ + */ "title": string; + /** + * 通算ログイン日数が7日 + */ "description": string; + /** + * 慣れてきましたか? + */ "flavor": string; }; "_login15": { + /** + * ビギナーⅢ + */ "title": string; + /** + * 通算ログイン日数が15日 + */ "description": string; }; "_login30": { + /** + * ミスキストⅠ + */ "title": string; + /** + * 通算ログイン日数が30日 + */ "description": string; }; "_login60": { + /** + * ミスキストⅡ + */ "title": string; + /** + * 通算ログイン日数が60日 + */ "description": string; }; "_login100": { + /** + * ミスキストⅢ + */ "title": string; + /** + * 通算ログイン日数が100日 + */ "description": string; + /** + * そのユーザー、ミスキストにつき + */ "flavor": string; }; "_login200": { + /** + * 常連Ⅰ + */ "title": string; + /** + * 通算ログイン日数が200日 + */ "description": string; }; "_login300": { + /** + * 常連Ⅱ + */ "title": string; + /** + * 通算ログイン日数が300日 + */ "description": string; }; "_login400": { + /** + * 常連Ⅲ + */ "title": string; + /** + * 通算ログイン日数が400日 + */ "description": string; }; "_login500": { + /** + * ベテランⅠ + */ "title": string; + /** + * 通算ログイン日数が500日 + */ "description": string; + /** + * 諸君、私はノートが好きだ + */ "flavor": string; }; "_login600": { + /** + * ベテランⅡ + */ "title": string; + /** + * 通算ログイン日数が600日 + */ "description": string; }; "_login700": { + /** + * ベテランⅢ + */ "title": string; + /** + * 通算ログイン日数が700日 + */ "description": string; }; "_login800": { + /** + * ノートマスターⅠ + */ "title": string; + /** + * 通算ログイン日数が800日 + */ "description": string; }; "_login900": { + /** + * ノートマスターⅡ + */ "title": string; + /** + * 通算ログイン日数が900日 + */ "description": string; }; "_login1000": { + /** + * ノートマスターⅢ + */ "title": string; + /** + * 通算ログイン日数が1,000日 + */ "description": string; + /** + * Sharkeyを使ってくれてありがとう! + */ "flavor": string; }; "_noteClipped1": { + /** + * クリップせずにはいられないな + */ "title": string; + /** + * 初めてノートをクリップした + */ "description": string; }; "_noteFavorited1": { + /** + * 星をみるひと + */ "title": string; + /** + * 初めてノートをお気に入りに登録した + */ "description": string; }; "_myNoteFavorited1": { + /** + * 星が欲しい + */ "title": string; + /** + * 自分のノートが他の人からお気に入りに登録された + */ "description": string; }; "_profileFilled": { + /** + * 準備万端 + */ "title": string; + /** + * プロフィール設定を行った + */ "description": string; }; "_markedAsCat": { + /** + * 吾輩は猫である + */ "title": string; + /** + * アカウントをCatとして設定した + */ "description": string; + /** + * 名前はまだない。 + */ "flavor": string; }; "_following1": { + /** + * はじめてのフォロー + */ "title": string; + /** + * 初めてフォローした + */ "description": string; }; "_following10": { + /** + * ついてく、ついてく + */ "title": string; + /** + * フォローが10人を超した + */ "description": string; }; "_following50": { + /** + * 友達たくさん + */ "title": string; + /** + * フォローが50人を超した + */ "description": string; }; "_following100": { + /** + * 友達100人 + */ "title": string; + /** + * フォローが100人を超した + */ "description": string; }; "_following300": { + /** + * 友達過多 + */ "title": string; + /** + * フォローが300人を超した + */ "description": string; }; "_followers1": { + /** + * はじめてのフォロワー + */ "title": string; + /** + * 初めてフォローされた + */ "description": string; }; "_followers10": { + /** + * フォローミー! + */ "title": string; + /** + * フォロワーが10人を超した + */ "description": string; }; "_followers50": { + /** + * ぞろぞろ + */ "title": string; + /** + * フォロワーが50人を超した + */ "description": string; }; "_followers100": { + /** + * 人気者 + */ "title": string; + /** + * フォロワーが100人を超した + */ "description": string; }; "_followers300": { + /** + * 一列でお並びください + */ "title": string; + /** + * フォロワーが300人を超した + */ "description": string; }; "_followers500": { + /** + * 基地局 + */ "title": string; + /** + * フォロワーが500人を超した + */ "description": string; }; "_followers1000": { + /** + * インフルエンサー + */ "title": string; + /** + * フォロワーが1,000人を超した + */ "description": string; }; "_collectAchievements30": { + /** + * 実績コレクター + */ "title": string; + /** + * 実績を30個以上獲得した + */ "description": string; }; "_viewAchievements3min": { + /** + * 実績好き + */ "title": string; + /** + * 実績一覧を3分以上眺め続けた + */ "description": string; }; "_iLoveMisskey": { + /** + * I Love Sharkey + */ "title": string; + /** + * "I ❤ #Sharkey"を投稿した + */ "description": string; + /** + * Sharkeyを使ってくださりありがとうございます! by 開発チーム + */ "flavor": string; }; "_foundTreasure": { + /** + * 宝探し + */ "title": string; + /** + * 隠されたお宝を発見した + */ "description": string; }; "_client30min": { + /** + * ひとやすみ + */ "title": string; + /** + * クライアントを起動してから30分以上経過した + */ "description": string; }; "_client60min": { + /** + * Sharkeyの見すぎ + */ "title": string; + /** + * クライアントを起動してから60分以上経過した + */ "description": string; }; "_noteDeletedWithin1min": { + /** + * いまのなし + */ "title": string; + /** + * 投稿してから1分以内にその投稿を削除した + */ "description": string; }; "_postedAtLateNight": { + /** + * 夜行性 + */ "title": string; + /** + * 深夜にノートを投稿した + */ "description": string; + /** + * そろそろ寝よう。 + */ "flavor": string; }; "_postedAt0min0sec": { + /** + * 時報 + */ "title": string; + /** + * 0分0秒にノートを投稿した + */ "description": string; + /** + * ポッ ポッ ポッ ピーン + */ "flavor": string; }; "_selfQuote": { + /** + * 自己言及 + */ "title": string; + /** + * 自分のノートを引用した + */ "description": string; }; "_htl20npm": { + /** + * 流れるTL + */ "title": string; + /** + * ホームタイムラインの流速が20npmを越す + */ "description": string; }; "_viewInstanceChart": { + /** + * アナリスト + */ "title": string; + /** + * サーバーのチャートを表示した + */ "description": string; }; "_outputHelloWorldOnScratchpad": { + /** + * Hello, world! + */ "title": string; + /** + * スクラッチパッドで hello world を出力した + */ "description": string; }; "_open3windows": { + /** + * マルチウィンドウ + */ "title": string; + /** + * ウィンドウを3つ以上開いた状態にした + */ "description": string; }; "_driveFolderCircularReference": { + /** + * 循環参照 + */ "title": string; + /** + * ドライブのフォルダを再帰的な入れ子にしようとした + */ "description": string; }; "_reactWithoutRead": { + /** + * ちゃんと読んだ? + */ "title": string; + /** + * 100文字以上のテキストを含むノートに投稿されてから3秒以内にリアクションした + */ "description": string; }; "_clickedClickHere": { + /** + * ここをクリック + */ "title": string; + /** + * ここをクリックした + */ "description": string; }; "_justPlainLucky": { + /** + * 単なるラッキー + */ "title": string; + /** + * 10秒ごとに0.005%の確率で獲得 + */ "description": string; }; "_setNameToSyuilo": { + /** + * 神様コンプレックス + */ "title": string; + /** + * 名前を syuilo に設定した + */ "description": string; }; "_passedSinceAccountCreated1": { + /** + * 一周年 + */ "title": string; + /** + * アカウント作成から1年経過した + */ "description": string; }; "_passedSinceAccountCreated2": { + /** + * 二周年 + */ "title": string; + /** + * アカウント作成から2年経過した + */ "description": string; }; "_passedSinceAccountCreated3": { + /** + * 三周年 + */ "title": string; + /** + * アカウント作成から3年経過した + */ "description": string; }; "_loggedInOnBirthday": { + /** + * ハッピーバースデー + */ "title": string; + /** + * 誕生日にログインした + */ "description": string; }; "_loggedInOnNewYearsDay": { + /** + * あけましておめでとうございます + */ "title": string; + /** + * 元日にログインした + */ "description": string; + /** + * 今年も弊サーバーをよろしくお願いします + */ "flavor": string; }; "_cookieClicked": { + /** + * クッキーをクリックするゲーム + */ "title": string; + /** + * クッキーをクリックした + */ "description": string; + /** + * ソフト間違ってない? + */ "flavor": string; }; "_brainDiver": { + /** + * Brain Diver + */ "title": string; + /** + * Brain Diverへのリンクを投稿した + */ "description": string; + /** + * Misskey-Misskey La-Tu-Ma + */ "flavor": string; }; "_smashTestNotificationButton": { + /** + * テスト過剰 + */ "title": string; + /** + * 通知のテストをごく短時間のうちに連続して行った + */ "description": string; }; "_tutorialCompleted": { + /** + * Sharkey初心者講座 修了証 + */ "title": string; + /** + * チュートリアルを完了した + */ "description": string; }; "_bubbleGameExplodingHead": { + /** + * 🤯 + */ "title": string; + /** + * バブルゲームで最も大きいモノを出した + */ "description": string; }; "_bubbleGameDoubleExplodingHead": { + /** + * ダブル🤯 + */ "title": string; + /** + * バブルゲームで最も大きいモノを2つ同時に出した + */ "description": string; + /** + * これくらいの おべんとばこに 🤯 🤯 ちょっとつめて + */ "flavor": string; }; }; }; "_role": { + /** + * ロールの作成 + */ "new": string; + /** + * ロールの編集 + */ "edit": string; + /** + * ロール名 + */ "name": string; + /** + * ロールの説明 + */ "description": string; + /** + * ロールの権限 + */ "permission": string; + /** + * モデレーターは基本的なモデレーションに関する操作を行えます。 + * 管理者はサーバーの全ての設定を変更できます。 + */ "descriptionOfPermission": string; + /** + * アサイン + */ "assignTarget": string; + /** + * マニュアルは誰がこのロールに含まれるかを手動で管理します。 + * コンディショナルは条件を設定し、それに合致するユーザーが自動で含まれるようになります。 + */ "descriptionOfAssignTarget": string; + /** + * マニュアル + */ "manual": string; + /** + * マニュアルロール + */ "manualRoles": string; + /** + * コンディショナル + */ "conditional": string; + /** + * コンディショナルロール + */ "conditionalRoles": string; + /** + * 条件 + */ "condition": string; + /** + * これはコンディショナルロールです。 + */ "isConditionalRole": string; + /** + * 公開ロール + */ "isPublic": string; + /** + * ユーザーのプロフィールでこのロールが表示されます。 + */ "descriptionOfIsPublic": string; + /** + * オプション + */ "options": string; + /** + * ポリシー + */ "policies": string; + /** + * ベースロール + */ "baseRole": string; + /** + * ベースロールの値を使用 + */ "useBaseValue": string; + /** + * アサインするロールを選択 + */ "chooseRoleToAssign": string; + /** + * アイコン画像のURL + */ "iconUrl": string; + /** + * バッジとして表示 + */ "asBadge": string; + /** + * オンにすると、ユーザー名の横にロールのアイコンが表示されます。 + */ "descriptionOfAsBadge": string; + /** + * ユーザーを見つけやすくする + */ "isExplorable": string; + /** + * オンにすると、「みつける」でメンバー一覧が公開されるほか、ロールのタイムラインが利用可能になります。 + */ "descriptionOfIsExplorable": string; + /** + * 表示順 + */ "displayOrder": string; + /** + * 数値が大きいほどUI上で先頭に表示されます。 + */ "descriptionOfDisplayOrder": string; + /** + * モデレーターのメンバー編集を許可 + */ "canEditMembersByModerator": string; + /** + * オンにすると、管理者に加えてモデレーターもこのロールへユーザーをアサイン/アサイン解除できるようになります。オフにすると管理者のみが行えます。 + */ "descriptionOfCanEditMembersByModerator": string; + /** + * 優先度 + */ "priority": string; "_priority": { + /** + * 低 + */ "low": string; + /** + * 中 + */ "middle": string; + /** + * 高 + */ "high": string; }; "_options": { + /** + * グローバルタイムラインの閲覧 + */ "gtlAvailable": string; + /** + * バブルタイムラインの閲覧 + */ "btlAvailable": string; + /** + * ローカルタイムラインの閲覧 + */ "ltlAvailable": string; + /** + * パブリック投稿の許可 + */ "canPublicNote": string; + /** + * ノートのインポートが可能 + */ "canImportNotes": string; + /** + * サーバー招待コードの発行 + */ "canInvite": string; + /** + * 招待コードの作成可能数 + */ "inviteLimit": string; + /** + * 招待コードの発行間隔 + */ "inviteLimitCycle": string; + /** + * 招待コードの有効期限 + */ "inviteExpirationTime": string; + /** + * カスタム絵文字の管理 + */ "canManageCustomEmojis": string; + /** + * アバターデコレーションの管理 + */ "canManageAvatarDecorations": string; + /** + * ドライブ容量 + */ "driveCapacity": string; + /** + * ファイルにNSFWを常に付与 + */ "alwaysMarkNsfw": string; + /** + * ノートのピン留めの最大数 + */ "pinMax": string; + /** + * アンテナの作成可能数 + */ "antennaMax": string; + /** + * ワードミュートの最大文字数 + */ "wordMuteMax": string; + /** + * Webhookの作成可能数 + */ "webhookMax": string; + /** + * クリップの作成可能数 + */ "clipMax": string; + /** + * クリップ内のノートの最大数 + */ "noteEachClipsMax": string; + /** + * ユーザーリストの作成可能数 + */ "userListMax": string; + /** + * ユーザーリスト内のユーザーの最大数 + */ "userEachUserListsMax": string; + /** + * レートリミット + */ "rateLimitFactor": string; + /** + * 小さいほど制限が緩和され、大きいほど制限が強化されます。 + */ "descriptionOfRateLimitFactor": string; + /** + * 広告の非表示 + */ "canHideAds": string; + /** + * ノート検索の利用 + */ "canSearchNotes": string; + /** + * 翻訳機能の利用 + */ "canUseTranslator": string; + /** + * アイコンデコレーションの最大取付個数 + */ "avatarDecorationLimit": string; }; "_condition": { + /** + * ローカルユーザー + */ "isLocal": string; + /** + * リモートユーザー + */ "isRemote": string; + /** + * アカウント作成から~以内 + */ "createdLessThan": string; + /** + * アカウント作成から~経過 + */ "createdMoreThan": string; + /** + * フォロワー数が~以下 + */ "followersLessThanOrEq": string; + /** + * フォロワー数が~以上 + */ "followersMoreThanOrEq": string; + /** + * フォロー数が~以下 + */ "followingLessThanOrEq": string; + /** + * フォロー数が~以上 + */ "followingMoreThanOrEq": string; + /** + * 投稿数が~以下 + */ "notesLessThanOrEq": string; + /** + * 投稿数が~以上 + */ "notesMoreThanOrEq": string; + /** + * ~かつ~ + */ "and": string; + /** + * ~または~ + */ "or": string; + /** + * ~ではない + */ "not": string; }; }; "_sensitiveMediaDetection": { + /** + * 機械学習を使って自動でセンシティブなメディアを検出し、モデレーションに役立てることができます。サーバーの負荷が少し増えます。 + */ "description": string; + /** + * 検出感度 + */ "sensitivity": string; + /** + * 感度を低くすると、誤検知(偽陽性)が減ります。感度を高くすると、検知漏れ(偽陰性)が減ります。 + */ "sensitivityDescription": string; + /** + * センシティブフラグを設定する + */ "setSensitiveFlagAutomatically": string; + /** + * この設定をオフにしても内部的に判定結果は保持されます。 + */ "setSensitiveFlagAutomaticallyDescription": string; + /** + * 動画の解析を有効化 + */ "analyzeVideos": string; + /** + * 静止画に加えて動画も解析するようにします。サーバーの負荷が少し増えます。 + */ "analyzeVideosDescription": string; }; "_emailUnavailable": { + /** + * 既に使用されています + */ "used": string; + /** + * 形式が正しくありません + */ "format": string; + /** + * 恒久的に使用可能なアドレスではありません + */ "disposable": string; + /** + * 正しいメールサーバーではありません + */ "mx": string; + /** + * メールサーバーが応答しません + */ "smtp": string; + /** + * このメールアドレスでは登録できません + */ "banned": string; }; "_ffVisibility": { + /** + * 公開 + */ "public": string; + /** + * フォロワーだけに公開 + */ "followers": string; + /** + * 非公開 + */ "private": string; }; "_signup": { + /** + * ほとんど完了です + */ "almostThere": string; + /** + * あなたが使っているメールアドレスを入力してください。メールアドレスが公開されることはありません。 + */ "emailAddressInfo": string; - "emailSent": string; + /** + * 入力されたメールアドレス({email})宛に確認のメールが送信されました。メールに記載されたリンクにアクセスすると、アカウントの作成が完了します。メールに記載されているリンクの有効期限は30分です。 + */ + "emailSent": ParameterizedString<"email">; + /** + * アカウントが作成され、承認待ちの状態です。 + */ "approvalPending": string; + /** + * インスタンスに参加したい理由を入力してください。 + */ "reasonInfo": string; }; "_accountDelete": { + /** + * アカウントの削除 + */ "accountDelete": string; + /** + * アカウントの削除は負荷のかかる処理であるため、作成したコンテンツの数やアップロードしたファイルの数が多いと完了までに時間がかかることがあります。 + */ "mayTakeTime": string; + /** + * アカウントの削除が完了する際は、登録してあったメールアドレス宛に通知を送信します。 + */ "sendEmail": string; + /** + * アカウント削除をリクエスト + */ "requestAccountDelete": string; + /** + * 削除処理が開始されました。 + */ "started": string; + /** + * 削除が進行中 + */ "inProgress": string; }; "_ad": { + /** + * 戻る + */ "back": string; + /** + * この広告の表示頻度を下げる + */ "reduceFrequencyOfThisAd": string; + /** + * 表示しない + */ "hide": string; + /** + * 曜日はサーバーのタイムゾーンを元に指定されます。 + */ "timezoneinfo": string; + /** + * 広告配信設定 + */ "adsSettings": string; + /** + * リアルタイム更新中に広告を配信する間隔(ノートの個数) + */ "notesPerOneAd": string; + /** + * 0でリアルタイム更新時の広告配信を無効 + */ "setZeroToDisable": string; + /** + * 広告の配信間隔が極めて短いため、ユーザー体験が著しく損われる可能性があります。 + */ "adsTooClose": string; }; "_forgotPassword": { + /** + * アカウントに登録したメールアドレスを入力してください。そのアドレス宛てに、パスワードリセット用のリンクが送信されます。 + */ "enterEmail": string; + /** + * メールアドレスを登録していない場合は、管理者までお問い合わせください。 + */ "ifNoEmail": string; + /** + * このサーバーではメールがサポートされていないため、パスワードリセットを行う場合は管理者までお問い合わせください。 + */ "contactAdmin": string; }; "_gallery": { + /** + * 自分の投稿 + */ "my": string; + /** + * いいねした投稿 + */ "liked": string; + /** + * いいね! + */ "like": string; + /** + * いいね解除 + */ "unlike": string; }; "_email": { "_follow": { + /** + * フォローされました + */ "title": string; }; "_receiveFollowRequest": { + /** + * フォローリクエストを受け取りました + */ "title": string; }; }; "_plugin": { + /** + * プラグインのインストール + */ "install": string; + /** + * 信頼できないプラグインはインストールしないでください。 + */ "installWarn": string; + /** + * プラグインの管理 + */ "manage": string; + /** + * ソースを表示 + */ "viewSource": string; }; "_preferencesBackups": { + /** + * 作成したバックアップ + */ "list": string; + /** + * 新規保存 + */ "saveNew": string; + /** + * ファイルを読み込み + */ "loadFile": string; + /** + * このデバイスに適用 + */ "apply": string; + /** + * 上書き保存 + */ "save": string; + /** + * バックアップ名を入力 + */ "inputName": string; + /** + * 保存できません + */ "cannotSave": string; - "nameAlreadyExists": string; - "applyConfirm": string; - "saveConfirm": string; - "deleteConfirm": string; - "renameConfirm": string; + /** + * バックアップ名「{name}」は既に存在します。違う名前を指定してください。 + */ + "nameAlreadyExists": ParameterizedString<"name">; + /** + * バックアップ「{name}」を現在のデバイスに適用しますか?現在のデバイス設定は失われます。 + */ + "applyConfirm": ParameterizedString<"name">; + /** + * {name}に上書き保存しますか? + */ + "saveConfirm": ParameterizedString<"name">; + /** + * {name}を削除しますか? + */ + "deleteConfirm": ParameterizedString<"name">; + /** + * 「{old}」を「{new}」に変更しますか? + */ + "renameConfirm": ParameterizedString<"old" | "new">; + /** + * バックアップはありません。「新規保存」で現在のクライアント設定をサーバーに保存できます。 + */ "noBackups": string; - "createdAt": string; - "updatedAt": string; + /** + * 作成日時: {date} {time} + */ + "createdAt": ParameterizedString<"date" | "time">; + /** + * 更新日時: {date} {time} + */ + "updatedAt": ParameterizedString<"date" | "time">; + /** + * 読み込みできません + */ "cannotLoad": string; + /** + * ファイル形式が違います。 + */ "invalidFile": string; }; "_registry": { + /** + * スコープ + */ "scope": string; + /** + * キー + */ "key": string; + /** + * キー + */ "keys": string; + /** + * ドメイン + */ "domain": string; + /** + * キーを作成 + */ "createKey": string; }; "_aboutMisskey": { + /** + * Sharkeyは、Misskeyをベースにしたオープンソースのソフトウェアです。 + */ "about": string; + /** + * 主なコントリビューター + */ "contributors": string; + /** + * 全てのコントリビューター + */ "allContributors": string; + /** + * ソースコード + */ "source": string; + /** + * Sharkeyを翻訳 + */ "translation": string; + /** + * Sharkeyに寄付 + */ "donate": string; + /** + * 他にも多くの方が支援してくれています。ありがとうございます🥰 + */ "morePatrons": string; + /** + * 支援者 + */ "patrons": string; + /** + * プロジェクトメンバー + */ "projectMembers": string; }; "_displayOfSensitiveMedia": { + /** + * センシティブ設定されたメディアを隠す + */ "respect": string; + /** + * センシティブ設定されたメディアを隠さない + */ "ignore": string; + /** + * 常にメディアを隠す + */ "force": string; }; "_instanceTicker": { + /** + * 表示しない + */ "none": string; + /** + * リモートユーザーに表示 + */ "remote": string; + /** + * 常に表示 + */ "always": string; }; "_serverDisconnectedBehavior": { + /** + * 自動でリロード + */ "reload": string; + /** + * ダイアログで警告 + */ "dialog": string; + /** + * 控えめに警告 + */ "quiet": string; + /** + * 警告を無効にする + */ "disabled": string; }; "_channel": { + /** + * チャンネルを作成 + */ "create": string; + /** + * チャンネルを編集 + */ "edit": string; + /** + * バナーを設定 + */ "setBanner": string; + /** + * バナーを削除 + */ "removeBanner": string; + /** + * トレンド + */ "featured": string; + /** + * 管理中 + */ "owned": string; + /** + * フォロー中 + */ "following": string; - "usersCount": string; - "notesCount": string; + /** + * {n}人が参加中 + */ + "usersCount": ParameterizedString<"n">; + /** + * {n}投稿があります + */ + "notesCount": ParameterizedString<"n">; + /** + * 名前と説明 + */ "nameAndDescription": string; + /** + * 名前のみ + */ "nameOnly": string; + /** + * チャンネル外へのブーストと引用ブーストを許可する + */ "allowRenoteToExternal": string; }; "_menuDisplay": { + /** + * 横 + */ "sideFull": string; + /** + * 横(アイコン) + */ "sideIcon": string; + /** + * 上部 + */ "top": string; + /** + * 隠す + */ "hide": string; }; "_wordMute": { + /** + * ミュートするワード + */ "muteWords": string; + /** + * スペースで区切るとAND指定になり、改行で区切るとOR指定になります。 + */ "muteWordsDescription": string; + /** + * キーワードをスラッシュで囲むと正規表現になります。 + */ "muteWordsDescription2": string; }; "_instanceMute": { + /** + * ミュートしたサーバーのユーザーへの返信を含めて、設定したサーバーの全てのノートとブーストをミュートします。 + */ "instanceMuteDescription": string; + /** + * 改行で区切って設定します + */ "instanceMuteDescription2": string; + /** + * 設定したサーバーのノートを隠します。 + */ "title": string; + /** + * ミュートするサーバー + */ "heading": string; }; "_theme": { + /** + * テーマを探す + */ "explore": string; + /** + * テーマのインストール + */ "install": string; + /** + * テーマの管理 + */ "manage": string; + /** + * テーマコード + */ "code": string; + /** + * 説明 + */ "description": string; - "installed": string; + /** + * {name}をインストールしました + */ + "installed": ParameterizedString<"name">; + /** + * インストールされたテーマ + */ "installedThemes": string; + /** + * 標準のテーマ + */ "builtinThemes": string; + /** + * そのテーマは既にインストールされています + */ "alreadyInstalled": string; + /** + * テーマの形式が間違っています + */ "invalid": string; + /** + * テーマを作る + */ "make": string; + /** + * ベース + */ "base": string; + /** + * 定数を追加 + */ "addConstant": string; + /** + * 定数 + */ "constant": string; + /** + * デフォルト値 + */ "defaultValue": string; + /** + * 色 + */ "color": string; + /** + * プロパティを参照 + */ "refProp": string; + /** + * 定数を参照 + */ "refConst": string; + /** + * キー + */ "key": string; + /** + * 関数 + */ "func": string; + /** + * 関数の種類 + */ "funcKind": string; + /** + * 引数 + */ "argument": string; + /** + * 元にするプロパティの名前 + */ "basedProp": string; + /** + * 不透明度 + */ "alpha": string; + /** + * 暗さ + */ "darken": string; + /** + * 明るさ + */ "lighten": string; + /** + * 定数名を入力してください + */ "inputConstantName": string; + /** + * ここにテーマコードを貼り付けて、エディターにインポートできます + */ "importInfo": string; - "deleteConstantConfirm": string; + /** + * 定数 {const} を削除しても良いですか? + */ + "deleteConstantConfirm": ParameterizedString<"const">; "keys": { + /** + * アクセント + */ "accent": string; + /** + * 背景 + */ "bg": string; + /** + * 文字 + */ "fg": string; + /** + * フォーカス + */ "focus": string; + /** + * インジケーター + */ "indicator": string; + /** + * パネル + */ "panel": string; + /** + * 影 + */ "shadow": string; + /** + * ヘッダー + */ "header": string; + /** + * サイドバーの背景 + */ "navBg": string; + /** + * サイドバーの文字 + */ "navFg": string; + /** + * サイドバー文字(ホバー) + */ "navHoverFg": string; + /** + * サイドバー文字(アクティブ) + */ "navActive": string; + /** + * サイドバーのインジケーター + */ "navIndicator": string; + /** + * リンク + */ "link": string; + /** + * ハッシュタグ + */ "hashtag": string; + /** + * メンション + */ "mention": string; + /** + * あなた宛てメンション + */ "mentionMe": string; + /** + * Boost + */ "renote": string; + /** + * モーダルの背景 + */ "modalBg": string; + /** + * 分割線 + */ "divider": string; + /** + * スクロールバーの取っ手 + */ "scrollbarHandle": string; + /** + * スクロールバーの取っ手(ホバー) + */ "scrollbarHandleHover": string; + /** + * 日付ラベルの文字 + */ "dateLabelFg": string; + /** + * 情報の背景 + */ "infoBg": string; + /** + * 情報の文字 + */ "infoFg": string; + /** + * 警告の背景 + */ "infoWarnBg": string; + /** + * 警告の文字 + */ "infoWarnFg": string; + /** + * 通知トーストの背景 + */ "toastBg": string; + /** + * 通知トーストの文字 + */ "toastFg": string; + /** + * ボタンの背景 + */ "buttonBg": string; + /** + * ボタンの背景 (ホバー) + */ "buttonHoverBg": string; + /** + * 入力ボックスの縁取り + */ "inputBorder": string; + /** + * リスト項目の背景 (ホバー) + */ "listItemHoverBg": string; + /** + * ドライブフォルダーの背景 + */ "driveFolderBg": string; + /** + * 壁紙のオーバーレイ + */ "wallpaperOverlay": string; + /** + * バッジ + */ "badge": string; + /** + * チャットの背景 + */ "messageBg": string; + /** + * アクセント (暗め) + */ "accentDarken": string; + /** + * アクセント (明るめ) + */ "accentLighten": string; + /** + * 強調された文字 + */ "fgHighlighted": string; }; }; "_sfx": { + /** + * ノート + */ "note": string; + /** + * ノート(自分) + */ "noteMy": string; + /** + * 通知 + */ "notification": string; + /** + * アンテナ受信 + */ "antenna": string; + /** + * チャンネル通知 + */ "channel": string; + /** + * リアクション選択時 + */ "reaction": string; }; "_soundSettings": { + /** + * ドライブの音声を使用 + */ "driveFile": string; + /** + * ドライブのファイルを選択してください + */ "driveFileWarn": string; + /** + * このファイルは対応していません + */ "driveFileTypeWarn": string; + /** + * 音声ファイルを選択してください + */ "driveFileTypeWarnDescription": string; + /** + * 音声が長すぎます + */ "driveFileDurationWarn": string; + /** + * 長い音声を使用するとMisskeyの使用に支障をきたす可能性があります。それでも続行しますか? + */ "driveFileDurationWarnDescription": string; }; "_ago": { + /** + * 未来 + */ "future": string; + /** + * たった今 + */ "justNow": string; - "secondsAgo": string; - "minutesAgo": string; - "hoursAgo": string; - "daysAgo": string; - "weeksAgo": string; - "monthsAgo": string; - "yearsAgo": string; + /** + * {n}秒前 + */ + "secondsAgo": ParameterizedString<"n">; + /** + * {n}分前 + */ + "minutesAgo": ParameterizedString<"n">; + /** + * {n}時間前 + */ + "hoursAgo": ParameterizedString<"n">; + /** + * {n}日前 + */ + "daysAgo": ParameterizedString<"n">; + /** + * {n}週間前 + */ + "weeksAgo": ParameterizedString<"n">; + /** + * {n}ヶ月前 + */ + "monthsAgo": ParameterizedString<"n">; + /** + * {n}年前 + */ + "yearsAgo": ParameterizedString<"n">; + /** + * 日時の解析に失敗 + */ "invalid": string; }; "_timeIn": { - "seconds": string; - "minutes": string; - "hours": string; - "days": string; - "weeks": string; - "months": string; - "years": string; + /** + * {n}秒後 + */ + "seconds": ParameterizedString<"n">; + /** + * {n}分後 + */ + "minutes": ParameterizedString<"n">; + /** + * {n}時間後 + */ + "hours": ParameterizedString<"n">; + /** + * {n}日後 + */ + "days": ParameterizedString<"n">; + /** + * {n}週間後 + */ + "weeks": ParameterizedString<"n">; + /** + * {n}ヶ月後 + */ + "months": ParameterizedString<"n">; + /** + * {n}年後 + */ + "years": ParameterizedString<"n">; }; "_time": { + /** + * 秒 + */ "second": string; + /** + * 分 + */ "minute": string; + /** + * 時間 + */ "hour": string; + /** + * 日 + */ "day": string; }; "_2fa": { + /** + * 既に設定は完了しています。 + */ "alreadyRegistered": string; + /** + * 認証アプリの設定を開始 + */ "registerTOTP": string; - "step1": string; + /** + * まず、{a}や{b}などの認証アプリをお使いのデバイスにインストールします。 + */ + "step1": ParameterizedString<"a" | "b">; + /** + * 次に、表示されているQRコードをアプリでスキャンします。 + */ "step2": string; + /** + * QRコードをクリックすると、お使いの端末にインストールされている認証アプリやキーリングに登録できます。 + */ "step2Click": string; + /** + * デスクトップアプリを使用する場合は次のURIを入力します + */ "step2Uri": string; + /** + * 確認コードを入力 + */ "step3Title": string; + /** + * アプリに表示されている確認コード(トークン)を入力します。 + */ "step3": string; + /** + * 設定が完了しました + */ "setupCompleted": string; + /** + * これからログインするときも、同じようにコードを入力します。 + */ "step4": string; + /** + * お使いのブラウザはセキュリティキーに対応していません。 + */ "securityKeyNotSupported": string; + /** + * セキュリティキー・パスキーを登録するには、まず認証アプリの設定を行なってください。 + */ "registerTOTPBeforeKey": string; + /** + * FIDO2をサポートするハードウェアセキュリティキー、端末の生体認証やPINロック、パスキーといった、WebAuthn由来の鍵を登録します。 + */ "securityKeyInfo": string; + /** + * セキュリティキー・パスキーを登録する + */ "registerSecurityKey": string; + /** + * キーの名前を入力 + */ "securityKeyName": string; + /** + * ブラウザの指示に従い、セキュリティキーやパスキーを登録してください + */ "tapSecurityKey": string; + /** + * セキュリティキーを削除 + */ "removeKey": string; - "removeKeyConfirm": string; + /** + * {name}を削除しますか? + */ + "removeKeyConfirm": ParameterizedString<"name">; + /** + * セキュリティキーが登録されている場合、認証アプリの設定は解除できません。 + */ "whyTOTPOnlyRenew": string; + /** + * 認証アプリを再設定 + */ "renewTOTP": string; + /** + * 今までの認証アプリの確認コードおよびバックアップコードは使用できなくなります + */ "renewTOTPConfirm": string; + /** + * 再設定する + */ "renewTOTPOk": string; + /** + * やめておく + */ "renewTOTPCancel": string; + /** + * このウィザードを閉じる前に、以下のバックアップコードを確認してください。 + */ "checkBackupCodesBeforeCloseThisWizard": string; + /** + * バックアップコード + */ "backupCodes": string; + /** + * 認証アプリが使用できなくなった場合、以下のバックアップコードを使ってアカウントにアクセスできます。これらのコードは必ず安全な場所に保管してください。各コードは一回だけ使用できます。 + */ "backupCodesDescription": string; + /** + * バックアップコードが使用されました。認証アプリが使えなくなっている場合、なるべく早く認証アプリを再設定してください。 + */ "backupCodeUsedWarning": string; + /** + * バックアップコードが全て使用されました。認証アプリを利用できない場合、これ以上アカウントにアクセスできなくなります。認証アプリを再登録してください。 + */ "backupCodesExhaustedWarning": string; }; "_permissions": { + /** + * アカウントの情報を見る + */ "read:account": string; + /** + * アカウントの情報を変更する + */ "write:account": string; + /** + * ブロックを見る + */ "read:blocks": string; + /** + * ブロックを操作する + */ "write:blocks": string; + /** + * ドライブを見る + */ "read:drive": string; + /** + * ドライブを操作する + */ "write:drive": string; + /** + * お気に入りを見る + */ "read:favorites": string; + /** + * お気に入りを操作する + */ "write:favorites": string; + /** + * フォローの情報を見る + */ "read:following": string; + /** + * フォロー・フォロー解除する + */ "write:following": string; + /** + * チャットを見る + */ "read:messaging": string; + /** + * チャットを操作する + */ "write:messaging": string; + /** + * ミュートを見る + */ "read:mutes": string; + /** + * ミュートを操作する + */ "write:mutes": string; + /** + * ノートを作成・削除する + */ "write:notes": string; + /** + * 通知を見る + */ "read:notifications": string; + /** + * 通知を操作する + */ "write:notifications": string; + /** + * リアクションを見る + */ "read:reactions": string; + /** + * リアクションを操作する + */ "write:reactions": string; + /** + * 投票する + */ "write:votes": string; + /** + * ページを見る + */ "read:pages": string; + /** + * ページを操作する + */ "write:pages": string; + /** + * ページのいいねを見る + */ "read:page-likes": string; + /** + * ページのいいねを操作する + */ "write:page-likes": string; + /** + * ユーザーグループを見る + */ "read:user-groups": string; + /** + * ユーザーグループを操作する + */ "write:user-groups": string; + /** + * チャンネルを見る + */ "read:channels": string; + /** + * チャンネルを操作する + */ "write:channels": string; + /** + * ギャラリーを見る + */ "read:gallery": string; + /** + * ギャラリーを操作する + */ "write:gallery": string; + /** + * ギャラリーのいいねを見る + */ "read:gallery-likes": string; + /** + * ギャラリーのいいねを操作する + */ "write:gallery-likes": string; + /** + * Playを見る + */ "read:flash": string; + /** + * Playを操作する + */ "write:flash": string; + /** + * Playのいいねを見る + */ "read:flash-likes": string; + /** + * Playのいいねを操作する + */ "write:flash-likes": string; + /** + * ユーザーからの通報を見る + */ "read:admin:abuse-user-reports": string; + /** + * ユーザーアカウントを削除する + */ "write:admin:delete-account": string; + /** + * ユーザーのすべてのファイルを削除する + */ "write:admin:delete-all-files-of-a-user": string; + /** + * データベースインデックスに関する情報を見る + */ "read:admin:index-stats": string; + /** + * データベーステーブルに関する情報を見る + */ "read:admin:table-stats": string; + /** + * ユーザーのIPアドレスを見る + */ "read:admin:user-ips": string; + /** + * インスタンスのメタデータを見る + */ "read:admin:meta": string; + /** + * ユーザーのパスワードをリセットする + */ "write:admin:reset-password": string; + /** + * ユーザーからの通報を解決する + */ "write:admin:resolve-abuse-user-report": string; + /** + * メールを送る + */ "write:admin:send-email": string; + /** + * サーバーの情報を見る + */ "read:admin:server-info": string; + /** + * モデレーションログを見る + */ "read:admin:show-moderation-log": string; + /** + * ユーザーのプライベートな情報を見る + */ "read:admin:show-user": string; + /** + * ユーザーのプライベートな情報を見る + */ "read:admin:show-users": string; + /** + * ユーザーを凍結する + */ "write:admin:suspend-user": string; + /** + * ユーザーのアバターを削除する + */ "write:admin:unset-user-avatar": string; + /** + * ユーザーのバーナーを削除する + */ "write:admin:unset-user-banner": string; + /** + * ユーザーの凍結を解除する + */ "write:admin:unsuspend-user": string; + /** + * インスタンスのメタデータを操作する + */ "write:admin:meta": string; + /** + * モデレーションノートを操作する + */ "write:admin:user-note": string; + /** + * ロールを操作する + */ "write:admin:roles": string; + /** + * ロールを見る + */ "read:admin:roles": string; + /** + * リレーを操作する + */ "write:admin:relays": string; + /** + * リレーを見る + */ "read:admin:relays": string; + /** + * 招待コードを操作する + */ "write:admin:invite-codes": string; + /** + * 招待コードを見る + */ "read:admin:invite-codes": string; + /** + * お知らせを操作する + */ "write:admin:announcements": string; + /** + * お知らせを見る + */ "read:admin:announcements": string; + /** + * アバターデコレーションを操作する + */ "write:admin:avatar-decorations": string; + /** + * アバターデコレーションを見る + */ "read:admin:avatar-decorations": string; + /** + * 連合に関する情報を操作する + */ "write:admin:federation": string; + /** + * ユーザーアカウントを操作する + */ "write:admin:account": string; + /** + * ユーザーに関する情報を見る + */ "read:admin:account": string; + /** + * 絵文字を操作する + */ "write:admin:emoji": string; + /** + * 絵文字を見る + */ "read:admin:emoji": string; + /** + * ジョブキューを操作する + */ "write:admin:queue": string; + /** + * ジョブキューに関する情報を見る + */ "read:admin:queue": string; + /** + * プロモーションノートを操作する + */ "write:admin:promo": string; + /** + * ユーザーのドライブを操作する + */ "write:admin:drive": string; + /** + * ユーザーのドライブの関する情報を見る + */ "read:admin:drive": string; + /** + * 管理者用のWebsocket APIを使う + */ "read:admin:stream": string; + /** + * 広告を操作する + */ "write:admin:ad": string; + /** + * 広告を見る + */ "read:admin:ad": string; + /** + * 招待コードを作成する + */ "write:invite-codes": string; + /** + * 招待コードを取得する + */ "read:invite-codes": string; + /** + * クリップのいいねを操作する + */ "write:clip-favorite": string; + /** + * クリップのいいねを見る + */ "read:clip-favorite": string; + /** + * 連合に関する情報を取得する + */ "read:federation": string; + /** + * 違反を報告する + */ "write:report-abuse": string; }; "_auth": { + /** + * アプリへのアクセス許可 + */ "shareAccessTitle": string; - "shareAccess": string; + /** + * 「{name}」がアカウントにアクセスすることを許可しますか? + */ + "shareAccess": ParameterizedString<"name">; + /** + * アカウントへのアクセスを許可しますか? + */ "shareAccessAsk": string; - "permission": string; + /** + * {name}は次の権限を要求しています + */ + "permission": ParameterizedString<"name">; + /** + * このアプリは次の権限を要求しています + */ "permissionAsk": string; + /** + * アプリケーションに戻ってやっていってください + */ "pleaseGoBack": string; + /** + * アプリケーションに戻っています + */ "callback": string; + /** + * アクセスを拒否しました + */ "denied": string; + /** + * アプリケーションにアクセス許可を与えるには、ログインが必要です。 + */ "pleaseLogin": string; }; "_antennaSources": { + /** + * 全てのノート + */ "all": string; + /** + * フォローしているユーザーのノート + */ "homeTimeline": string; + /** + * 指定した一人または複数のユーザーのノート + */ "users": string; + /** + * 指定したリストのユーザーのノート + */ "userList": string; + /** + * 指定した一人または複数のユーザーを除いた全てのノート + */ "userBlacklist": string; }; "_weekday": { + /** + * 日曜日 + */ "sunday": string; + /** + * 月曜日 + */ "monday": string; + /** + * 火曜日 + */ "tuesday": string; + /** + * 水曜日 + */ "wednesday": string; + /** + * 木曜日 + */ "thursday": string; + /** + * 金曜日 + */ "friday": string; + /** + * 土曜日 + */ "saturday": string; }; "_widgets": { + /** + * プロフィール + */ "profile": string; + /** + * サーバー情報 + */ "instanceInfo": string; + /** + * 付箋 + */ "memo": string; + /** + * 通知 + */ "notifications": string; + /** + * タイムライン + */ "timeline": string; + /** + * カレンダー + */ "calendar": string; + /** + * トレンド + */ "trends": string; + /** + * 時計 + */ "clock": string; + /** + * RSSリーダー + */ "rss": string; + /** + * RSSティッカー + */ "rssTicker": string; + /** + * アクティビティ + */ "activity": string; + /** + * フォト + */ "photos": string; + /** + * デジタル時計 + */ "digitalClock": string; + /** + * UNIX時計 + */ "unixClock": string; + /** + * 連合 + */ "federation": string; + /** + * サーバークラウド + */ "instanceCloud": string; + /** + * 投稿フォーム + */ "postForm": string; + /** + * スライドショー + */ "slideshow": string; + /** + * ボタン + */ "button": string; + /** + * オンラインユーザー + */ "onlineUsers": string; + /** + * ジョブキュー + */ "jobQueue": string; + /** + * サーバーメトリクス + */ "serverMetric": string; + /** + * AiScriptコンソール + */ "aiscript": string; + /** + * AiScript App + */ "aiscriptApp": string; + /** + * 藍 + */ "aichan": string; + /** + * ユーザーリスト + */ "userList": string; "_userList": { + /** + * リストを選択 + */ "chooseList": string; }; + /** + * クリッカー + */ "clicker": string; + /** + * 検索 + */ "search": string; + /** + * 今日誕生日のユーザー + */ "birthdayFollowings": string; }; "_cw": { + /** + * 隠す + */ "hide": string; + /** + * もっと見る + */ "show": string; - "chars": string; - "files": string; + /** + * {count}文字 + */ + "chars": ParameterizedString<"count">; + /** + * {count}ファイル + */ + "files": ParameterizedString<"count">; }; "_poll": { + /** + * 選択肢は最低2つ必要です + */ "noOnlyOneChoice": string; - "choiceN": string; + /** + * 選択肢{n} + */ + "choiceN": ParameterizedString<"n">; + /** + * これ以上追加できません + */ "noMore": string; + /** + * 複数回答可 + */ "canMultipleVote": string; + /** + * 期限 + */ "expiration": string; + /** + * 無期限 + */ "infinite": string; + /** + * 日時指定 + */ "at": string; + /** + * 経過指定 + */ "after": string; + /** + * 期日 + */ "deadlineDate": string; + /** + * 時間 + */ "deadlineTime": string; + /** + * 期間 + */ "duration": string; - "votesCount": string; - "totalVotes": string; + /** + * {n}票 + */ + "votesCount": ParameterizedString<"n">; + /** + * 計{n}票 + */ + "totalVotes": ParameterizedString<"n">; + /** + * 投票する + */ "vote": string; + /** + * 結果を見る + */ "showResult": string; + /** + * 投票済み + */ "voted": string; + /** + * 終了済み + */ "closed": string; - "remainingDays": string; - "remainingHours": string; - "remainingMinutes": string; - "remainingSeconds": string; + /** + * 終了まであと{d}日{h}時間 + */ + "remainingDays": ParameterizedString<"d" | "h">; + /** + * 終了まであと{h}時間{m}分 + */ + "remainingHours": ParameterizedString<"h" | "m">; + /** + * 終了まであと{m}分{s}秒 + */ + "remainingMinutes": ParameterizedString<"m" | "s">; + /** + * 終了まであと{s}秒 + */ + "remainingSeconds": ParameterizedString<"s">; + /** + * 複数の選択肢 + */ "multiple": string; }; "_visibility": { + /** + * パブリック + */ "public": string; + /** + * 全てのユーザーに公開 + */ "publicDescription": string; + /** + * ホーム + */ "home": string; + /** + * ホームタイムラインのみに公開 + */ "homeDescription": string; + /** + * フォロワー + */ "followers": string; + /** + * 自分のフォロワーのみに公開 + */ "followersDescription": string; + /** + * ダイレクト + */ "specified": string; + /** + * 指定したユーザーのみに公開 + */ "specifiedDescription": string; + /** + * 連合なし + */ "disableFederation": string; + /** + * 他サーバーへの配信を行いません + */ "disableFederationDescription": string; }; "_postForm": { + /** + * このノートに返信... + */ "replyPlaceholder": string; + /** + * このノートを引用... + */ "quotePlaceholder": string; + /** + * チャンネルに投稿... + */ "channelPlaceholder": string; "_placeholders": { + /** + * いまどうしてる? + */ "a": string; + /** + * 何かありましたか? + */ "b": string; + /** + * 何をお考えですか? + */ "c": string; + /** + * 言いたいことは? + */ "d": string; + /** + * ここに書いてください + */ "e": string; + /** + * あなたが書くのを待っています... + */ "f": string; }; }; "_profile": { + /** + * 名前 + */ "name": string; + /** + * ユーザー名 + */ "username": string; + /** + * 自己紹介 + */ "description": string; + /** + * ハッシュタグを含めることができます。 + */ "youCanIncludeHashtags": string; + /** + * 追加情報 + */ "metadata": string; + /** + * 追加情報を編集 + */ "metadataEdit": string; + /** + * プロフィールに表として追加情報を表示することができます。 + */ "metadataDescription": string; + /** + * ラベル + */ "metadataLabel": string; + /** + * 内容 + */ "metadataContent": string; + /** + * アイコン画像を変更 + */ "changeAvatar": string; + /** + * バナー画像を変更 + */ "changeBanner": string; + /** + * 更新バナー + */ "updateBanner": string; + /** + * バナーを削除 + */ "removeBanner": string; + /** + * 背景を変更する + */ "changeBackground": string; + /** + * 背景を更新する + */ "updateBackground": string; + /** + * 背景を削除する + */ "removeBackground": string; + /** + * 内容にURLを設定すると、リンク先のWebサイトに自分のプロフィールへのリンクが含まれている場合に所有者確認済みアイコンを表示させることができます。 + */ "verifiedLinkDescription": string; - "avatarDecorationMax": string; + /** + * 最大{max}つまでデコレーションを付けられます。 + */ + "avatarDecorationMax": ParameterizedString<"max">; }; "_exportOrImport": { + /** + * 全てのノート + */ "allNotes": string; + /** + * お気に入りにしたノート + */ "favoritedNotes": string; + /** + * クリップ + */ "clips": string; + /** + * フォロー + */ "followingList": string; + /** + * ミュート + */ "muteList": string; + /** + * ブロック + */ "blockingList": string; + /** + * リスト + */ "userLists": string; + /** + * ミュートしているユーザーを除外 + */ "excludeMutingUsers": string; + /** + * 使われていないアカウントを除外 + */ "excludeInactiveUsers": string; + /** + * インポートした人による返信をTLに含むようにする + */ "withReplies": string; }; "_charts": { + /** + * 連合 + */ "federation": string; + /** + * リクエスト + */ "apRequest": string; + /** + * ユーザーの増減 + */ "usersIncDec": string; + /** + * ユーザーの合計 + */ "usersTotal": string; + /** + * アクティブユーザー数 + */ "activeUsers": string; + /** + * ノートの増減 + */ "notesIncDec": string; + /** + * ローカルのノートの増減 + */ "localNotesIncDec": string; + /** + * リモートのノートの増減 + */ "remoteNotesIncDec": string; + /** + * ノートの合計 + */ "notesTotal": string; + /** + * ファイルの増減 + */ "filesIncDec": string; + /** + * ファイルの合計 + */ "filesTotal": string; + /** + * ストレージ使用量の増減 + */ "storageUsageIncDec": string; + /** + * ストレージ使用量の合計 + */ "storageUsageTotal": string; }; "_instanceCharts": { + /** + * リクエスト + */ "requests": string; + /** + * ユーザーの増減 + */ "users": string; + /** + * ユーザーの累積 + */ "usersTotal": string; + /** + * ノートの増減 + */ "notes": string; + /** + * ノートの累積 + */ "notesTotal": string; + /** + * フォロー/フォロワーの増減 + */ "ff": string; + /** + * フォロー/フォロワーの累積 + */ "ffTotal": string; + /** + * キャッシュサイズの増減 + */ "cacheSize": string; + /** + * キャッシュサイズの累積 + */ "cacheSizeTotal": string; + /** + * ファイル数の増減 + */ "files": string; + /** + * ファイル数の累積 + */ "filesTotal": string; }; "_timelines": { + /** + * ホーム + */ "home": string; + /** + * ローカル + */ "local": string; + /** + * ソーシャル + */ "social": string; + /** + * グローバル + */ "global": string; }; "_play": { + /** + * Playの作成 + */ "new": string; + /** + * Playの編集 + */ "edit": string; + /** + * Playを作成しました + */ "created": string; + /** + * Playを更新しました + */ "updated": string; + /** + * Playを削除しました + */ "deleted": string; + /** + * Play設定 + */ "pageSetting": string; + /** + * このPlayを編集 + */ "editThisPage": string; + /** + * ソースを表示 + */ "viewSource": string; + /** + * 自分のPlay + */ "my": string; + /** + * いいねしたPlay + */ "liked": string; + /** + * 人気 + */ "featured": string; + /** + * タイトル + */ "title": string; + /** + * スクリプト + */ "script": string; + /** + * 説明 + */ "summary": string; }; "_pages": { + /** + * ページの作成 + */ "newPage": string; + /** + * ページの編集 + */ "editPage": string; + /** + * ソースを表示中 + */ "readPage": string; + /** + * ページを作成しました + */ "created": string; + /** + * ページを更新しました + */ "updated": string; + /** + * ページを削除しました + */ "deleted": string; + /** + * ページ設定 + */ "pageSetting": string; + /** + * 指定されたページURLは既に存在しています + */ "nameAlreadyExists": string; + /** + * 不正なページURLです + */ "invalidNameTitle": string; + /** + * 空白でないか確認してください + */ "invalidNameText": string; + /** + * このページを編集 + */ "editThisPage": string; + /** + * ソースを表示 + */ "viewSource": string; + /** + * ページを見る + */ "viewPage": string; + /** + * いいね + */ "like": string; + /** + * いいね解除 + */ "unlike": string; + /** + * 自分のページ + */ "my": string; + /** + * いいねしたページ + */ "liked": string; + /** + * 人気 + */ "featured": string; + /** + * インスペクター + */ "inspector": string; + /** + * コンテンツ + */ "contents": string; + /** + * ページブロック + */ "content": string; + /** + * 変数 + */ "variables": string; + /** + * タイトル + */ "title": string; + /** + * ページURL + */ "url": string; + /** + * ページの要約 + */ "summary": string; + /** + * 中央寄せ + */ "alignCenter": string; + /** + * ピン留めされているときにタイトルを非表示 + */ "hideTitleWhenPinned": string; + /** + * フォント + */ "font": string; + /** + * セリフ + */ "fontSerif": string; + /** + * サンセリフ + */ "fontSansSerif": string; + /** + * アイキャッチ画像を設定 + */ "eyeCatchingImageSet": string; + /** + * アイキャッチ画像を削除 + */ "eyeCatchingImageRemove": string; + /** + * ブロックを追加 + */ "chooseBlock": string; + /** + * 種類を選択 + */ "selectType": string; + /** + * コンテンツ + */ "contentBlocks": string; + /** + * 入力 + */ "inputBlocks": string; + /** + * 特殊 + */ "specialBlocks": string; "blocks": { + /** + * テキスト + */ "text": string; + /** + * テキストエリア + */ "textarea": string; + /** + * セクション + */ "section": string; + /** + * 画像 + */ "image": string; + /** + * ボタン + */ "button": string; + /** + * ノート埋め込み + */ "note": string; "_note": { + /** + * ノートID + */ "id": string; + /** + * ノートURLをペーストして設定することもできます。 + */ "idDescription": string; + /** + * 詳細な表示 + */ "detailed": string; }; }; }; "_relayStatus": { + /** + * 承認待ち + */ "requesting": string; + /** + * 承認済み + */ "accepted": string; + /** + * 拒否済み + */ "rejected": string; }; "_notification": { + /** + * ファイルがアップロードされました + */ "fileUploaded": string; - "youGotMention": string; - "youGotReply": string; - "youGotQuote": string; - "youRenoted": string; + /** + * {name}からのメンション + */ + "youGotMention": ParameterizedString<"name">; + /** + * {name}からのリプライ + */ + "youGotReply": ParameterizedString<"name">; + /** + * {name}による引用 + */ + "youGotQuote": ParameterizedString<"name">; + /** + * {name}がBoostしました + */ + "youRenoted": ParameterizedString<"name">; + /** + * フォローされました + */ "youWereFollowed": string; + /** + * フォローリクエストが来ました + */ "youReceivedFollowRequest": string; + /** + * フォローリクエストが承認されました + */ "yourFollowRequestAccepted": string; + /** + * アンケートの結果が出ました + */ "pollEnded": string; + /** + * 新しい投稿 + */ "newNote": string; - "unreadAntennaNote": string; + /** + * アンテナ {name} + */ + "unreadAntennaNote": ParameterizedString<"name">; + /** + * ロールが付与されました + */ "roleAssigned": string; + /** + * プッシュ通知の更新をしました + */ "emptyPushNotificationMessage": string; + /** + * 実績を獲得 + */ "achievementEarned": string; + /** + * 通知テスト + */ "testNotification": string; + /** + * 通知の表示を確かめる + */ "checkNotificationBehavior": string; + /** + * テスト通知を送信する + */ "sendTestNotification": string; + /** + * 通知はこのように表示されます + */ "notificationWillBeDisplayedLikeThis": string; - "reactedBySomeUsers": string; - "renotedBySomeUsers": string; - "followedBySomeUsers": string; + /** + * {n}人がリアクションしました + */ + "reactedBySomeUsers": ParameterizedString<"n">; + /** + * {n}人がブーストしました + */ + "renotedBySomeUsers": ParameterizedString<"n">; + /** + * {n}人にフォローされました + */ + "followedBySomeUsers": ParameterizedString<"n">; "_types": { + /** + * すべて + */ "all": string; + /** + * ユーザーの新規投稿 + */ "note": string; + /** + * フォロー + */ "follow": string; + /** + * メンション + */ "mention": string; + /** + * リプライ + */ "reply": string; + /** + * Boost + */ "renote": string; + /** + * 引用 + */ "quote": string; + /** + * リアクション + */ "reaction": string; + /** + * アンケートが終了 + */ "pollEnded": string; + /** + * フォロー申請を受け取った + */ "receiveFollowRequest": string; + /** + * フォローが受理された + */ "followRequestAccepted": string; + /** + * ロールが付与された + */ "roleAssigned": string; + /** + * 実績の獲得 + */ "achievementEarned": string; + /** + * 連携アプリからの通知 + */ "app": string; }; "_actions": { + /** + * フォローバック + */ "followBack": string; + /** + * 返信 + */ "reply": string; + /** + * Boost + */ "renote": string; }; }; "_deck": { + /** + * 常にメインカラムを表示 + */ "alwaysShowMainColumn": string; + /** + * カラムの寄せ + */ "columnAlign": string; + /** + * カラムを追加 + */ "addColumn": string; + /** + * カラムの設定 + */ "configureColumn": string; + /** + * 左に移動 + */ "swapLeft": string; + /** + * 右に移動 + */ "swapRight": string; + /** + * 上に移動 + */ "swapUp": string; + /** + * 下に移動 + */ "swapDown": string; + /** + * 左にスタック + */ "stackLeft": string; + /** + * 右に出す + */ "popRight": string; + /** + * プロファイル + */ "profile": string; + /** + * 新規プロファイル + */ "newProfile": string; + /** + * プロファイルを削除 + */ "deleteProfile": string; + /** + * カラムを組み合わせて自分だけのインターフェイスを作りましょう! + */ "introduction": string; + /** + * 画面の右にある + を押して、いつでもカラムを追加できます。 + */ "introduction2": string; + /** + * カラムのメニューから、「ウィジェットの編集」を選択してウィジェットを追加してください + */ "widgetsIntroduction": string; + /** + * 非ルートページは簡易UIで表示 + */ "useSimpleUiForNonRootPages": string; + /** + * 「幅を自動調整」が有効の場合、これが幅の最小値となります + */ "usedAsMinWidthWhenFlexible": string; + /** + * 幅を自動調整 + */ "flexible": string; "_columns": { + /** + * メイン + */ "main": string; + /** + * ウィジェット + */ "widgets": string; + /** + * 通知 + */ "notifications": string; + /** + * タイムライン + */ "tl": string; + /** + * アンテナ + */ "antenna": string; + /** + * リスト + */ "list": string; + /** + * チャンネル + */ "channel": string; + /** + * あなた宛て + */ "mentions": string; + /** + * ダイレクト + */ "direct": string; + /** + * ロールタイムライン + */ "roleTimeline": string; }; }; "_dialog": { - "charactersExceeded": string; - "charactersBelow": string; + /** + * 最大文字数を超えています! 現在 {current} / 制限 {max} + */ + "charactersExceeded": ParameterizedString<"current" | "max">; + /** + * 最小文字数を下回っています! 現在 {current} / 制限 {min} + */ + "charactersBelow": ParameterizedString<"current" | "min">; }; "_disabledTimeline": { + /** + * 無効化されたタイムライン + */ "title": string; + /** + * 現在のロールでは、このタイムラインを使用することはできません。 + */ "description": string; }; "_drivecleaner": { + /** + * サイズが大きい順 + */ "orderBySizeDesc": string; + /** + * 追加日が古い順 + */ "orderByCreatedAtAsc": string; }; "_webhookSettings": { + /** + * Webhookを作成 + */ "createWebhook": string; + /** + * 名前 + */ "name": string; + /** + * シークレット + */ "secret": string; + /** + * Webhookを実行するタイミング + */ "events": string; + /** + * 有効 + */ "active": string; "_events": { + /** + * フォローしたとき + */ "follow": string; + /** + * フォローされたとき + */ "followed": string; + /** + * ノートを投稿したとき + */ "note": string; + /** + * 返信されたとき + */ "reply": string; + /** + * Boostされたとき + */ "renote": string; + /** + * リアクションがあったとき + */ "reaction": string; + /** + * メンションされたとき + */ "mention": string; }; }; "_moderationLogTypes": { + /** + * ロールを作成 + */ "createRole": string; + /** + * ロールを削除 + */ "deleteRole": string; + /** + * ロールを更新 + */ "updateRole": string; + /** + * ロールへアサイン + */ "assignRole": string; + /** + * ロールのアサイン解除 + */ "unassignRole": string; + /** + * 承認済み + */ "approve": string; + /** + * 凍結 + */ "suspend": string; + /** + * 凍結解除 + */ "unsuspend": string; + /** + * カスタム絵文字追加 + */ "addCustomEmoji": string; + /** + * カスタム絵文字更新 + */ "updateCustomEmoji": string; + /** + * カスタム絵文字削除 + */ "deleteCustomEmoji": string; + /** + * サーバー設定更新 + */ "updateServerSettings": string; + /** + * モデレーションノート更新 + */ "updateUserNote": string; + /** + * ファイルを削除 + */ "deleteDriveFile": string; + /** + * ノートを削除 + */ "deleteNote": string; + /** + * 全体のお知らせを作成 + */ "createGlobalAnnouncement": string; + /** + * ユーザーへお知らせを作成 + */ "createUserAnnouncement": string; + /** + * 全体のお知らせを更新 + */ "updateGlobalAnnouncement": string; + /** + * ユーザーのお知らせを更新 + */ "updateUserAnnouncement": string; + /** + * 全体のお知らせを削除 + */ "deleteGlobalAnnouncement": string; + /** + * ユーザーのお知らせを削除 + */ "deleteUserAnnouncement": string; + /** + * パスワードをリセット + */ "resetPassword": string; + /** + * リモートサーバーを停止 + */ "suspendRemoteInstance": string; + /** + * リモートサーバーを再開 + */ "unsuspendRemoteInstance": string; + /** + * ファイルをセンシティブ付与 + */ "markSensitiveDriveFile": string; + /** + * ファイルをセンシティブ解除 + */ "unmarkSensitiveDriveFile": string; + /** + * 通報を解決 + */ "resolveAbuseReport": string; + /** + * 招待コードを作成 + */ "createInvitation": string; + /** + * 広告を作成 + */ "createAd": string; + /** + * 広告を削除 + */ "deleteAd": string; + /** + * 広告を更新 + */ "updateAd": string; + /** + * アイコンデコレーションを作成 + */ "createAvatarDecoration": string; + /** + * アイコンデコレーションを更新 + */ "updateAvatarDecoration": string; + /** + * アイコンデコレーションを削除 + */ "deleteAvatarDecoration": string; + /** + * ユーザーのアイコンを解除 + */ "unsetUserAvatar": string; + /** + * ユーザーのバナーを解除 + */ "unsetUserBanner": string; }; "_fileViewer": { + /** + * ファイルの詳細 + */ "title": string; + /** + * ファイルタイプ + */ "type": string; + /** + * ファイルサイズ + */ "size": string; + /** + * URL + */ "url": string; + /** + * 追加日 + */ "uploadedAt": string; + /** + * 添付されているノート + */ "attachedNotes": string; + /** + * このページは、このファイルをアップロードしたユーザーしか閲覧できません。 + */ "thisPageCanBeSeenFromTheAuthor": string; }; "_externalResourceInstaller": { + /** + * 外部サイトからインストール + */ "title": string; + /** + * 配布元が信頼できるかを確認した上でインストールしてください。 + */ "checkVendorBeforeInstall": string; "_plugin": { + /** + * このプラグインをインストールしますか? + */ "title": string; + /** + * プラグイン情報 + */ "metaTitle": string; }; "_theme": { + /** + * このテーマをインストールしますか? + */ "title": string; + /** + * テーマ情報 + */ "metaTitle": string; }; "_meta": { + /** + * 基本のカラースキーム + */ "base": string; }; "_vendorInfo": { + /** + * 配布元情報 + */ "title": string; + /** + * 参照したエンドポイント + */ "endpoint": string; + /** + * ファイル整合性の確認 + */ "hashVerify": string; }; "_errors": { "_invalidParams": { + /** + * パラメータが不足しています + */ "title": string; + /** + * 外部サイトからデータを取得するために必要な情報が不足しています。URLをお確かめください。 + */ "description": string; }; "_resourceTypeNotSupported": { + /** + * この外部リソースには対応していません + */ "title": string; + /** + * この外部サイトから取得したリソースの種別には対応していません。サイト管理者にお問い合わせください。 + */ "description": string; }; "_failedToFetch": { + /** + * データの取得に失敗しました + */ "title": string; + /** + * 外部サイトとの通信に失敗しました。もう一度試しても改善しない場合、サイト管理者にお問い合わせください。 + */ "fetchErrorDescription": string; + /** + * 外部サイトから取得したデータが読み取れませんでした。サイト管理者にお問い合わせください。 + */ "parseErrorDescription": string; }; "_hashUnmatched": { + /** + * 正しいデータが取得できませんでした + */ "title": string; + /** + * 提供されたデータの整合性の確認に失敗しました。セキュリティ上、インストールは続行できません。サイト管理者にお問い合わせください。 + */ "description": string; }; "_pluginParseFailed": { + /** + * AiScript エラー + */ "title": string; + /** + * データは取得できたものの、AiScriptの解析時にエラーがあったため読み込めませんでした。プラグインの作者にお問い合わせください。エラーの詳細はJavascriptコンソールをご確認ください。 + */ "description": string; }; "_pluginInstallFailed": { + /** + * プラグインのインストールに失敗しました + */ "title": string; + /** + * プラグインのインストール中に問題が発生しました。もう一度お試しください。エラーの詳細はJavascriptコンソールをご覧ください。 + */ "description": string; }; "_themeParseFailed": { + /** + * テーマ解析エラー + */ "title": string; + /** + * データは取得できたものの、テーマファイルの解析時にエラーがあったため読み込めませんでした。テーマの作者にお問い合わせください。エラーの詳細はJavascriptコンソールをご確認ください。 + */ "description": string; }; "_themeInstallFailed": { + /** + * テーマのインストールに失敗しました + */ "title": string; + /** + * テーマのインストール中に問題が発生しました。もう一度お試しください。エラーの詳細はJavascriptコンソールをご覧ください。 + */ "description": string; }; }; }; "_animatedMFM": { + /** + * MFMアニメーションを再生 + */ "play": string; + /** + * MFMアニメーション停止 + */ "stop": string; "_alert": { + /** + * MFMアニメーションには、点滅するライトや高速で動くテキスト/絵文字を含まれる場合があります。 + */ "text": string; + /** + * 再生する + */ "confirm": string; }; }; "_dataRequest": { + /** + * データリクエスト + */ "title": string; + /** + * データリクエストは3日ごとに可能です。 + */ "warn": string; + /** + * データの保存が完了すると、このアカウントに登録されているEメールアドレスにメールが送信されます。 + */ "text": string; + /** + * リクエスト + */ "button": string; }; "_dataSaver": { "_media": { + /** + * メディアの読み込み + */ "title": string; + /** + * 画像・動画が自動で読み込まれるのを防止します。隠れている画像・動画はタップすると読み込まれます。 + */ "description": string; }; "_avatar": { + /** + * アイコン画像 + */ "title": string; + /** + * アイコン画像のアニメーションが停止します。アニメーション画像は通常の画像よりファイルサイズが大きいことがあるので、データ通信量をさらに削減できます。 + */ "description": string; }; "_urlPreview": { + /** + * URLプレビューのサムネイル + */ "title": string; + /** + * URLプレビューのサムネイル画像が読み込まれなくなります。 + */ "description": string; }; "_code": { + /** + * コードハイライト + */ "title": string; + /** + * MFMなどでコードハイライト記法が使われている場合、タップするまで読み込まれなくなります。コードハイライトではハイライトする言語ごとにその定義ファイルを読み込む必要がありますが、それらが自動で読み込まれなくなるため、通信量の削減が見込めます。 + */ "description": string; }; }; + "_hemisphere": { + /** + * 北半球 + */ + "N": string; + /** + * 南半球 + */ + "S": string; + /** + * 一部のクライアント設定で、季節を判定するために使用します。 + */ + "caption": string; + }; + "_reversi": { + /** + * リバーシ + */ + "reversi": string; + /** + * 対局の設定 + */ + "gameSettings": string; + /** + * ボードを選択 + */ + "chooseBoard": string; + /** + * 先行/後攻 + */ + "blackOrWhite": string; + /** + * {name}が黒(先行) + */ + "blackIs": ParameterizedString<"name">; + /** + * ルール + */ + "rules": string; + /** + * 対局はまもなく開始されます + */ + "thisGameIsStartedSoon": string; + /** + * 相手の準備が完了するのを待っています + */ + "waitingForOther": string; + /** + * あなたの準備が完了するのを待っています + */ + "waitingForMe": string; + /** + * 準備してください + */ + "waitingBoth": string; + /** + * 準備完了 + */ + "ready": string; + /** + * 準備を再開 + */ + "cancelReady": string; + /** + * 相手のターンです + */ + "opponentTurn": string; + /** + * あなたのターンです + */ + "myTurn": string; + /** + * {name}のターンです + */ + "turnOf": ParameterizedString<"name">; + /** + * {name}のターン + */ + "pastTurnOf": ParameterizedString<"name">; + /** + * 投了 + */ + "surrender": string; + /** + * 投了により + */ + "surrendered": string; + /** + * 時間切れ + */ + "timeout": string; + /** + * 引き分け + */ + "drawn": string; + /** + * {name}の勝ち + */ + "won": ParameterizedString<"name">; + /** + * 黒 + */ + "black": string; + /** + * 白 + */ + "white": string; + /** + * 合計 + */ + "total": string; + /** + * {count}ターン目 + */ + "turnCount": ParameterizedString<"count">; + /** + * 自分の対局 + */ + "myGames": string; + /** + * みんなの対局 + */ + "allGames": string; + /** + * 終了 + */ + "ended": string; + /** + * 対局中 + */ + "playing": string; + /** + * 石の少ない方が勝ち(ロセオ) + */ + "isLlotheo": string; + /** + * ループマップ + */ + "loopedMap": string; + /** + * どこでも置けるモード + */ + "canPutEverywhere": string; + /** + * 1ターンの時間制限 + */ + "timeLimitForEachTurn": string; + /** + * フリーマッチ + */ + "freeMatch": string; + /** + * 対戦相手を探しています + */ + "lookingForPlayer": string; + /** + * 対局がキャンセルされました + */ + "gameCanceled": string; + }; + "_offlineScreen": { + /** + * オフライン - サーバーに接続できません + */ + "title": string; + /** + * サーバーに接続できません + */ + "header": string; + }; } declare const locales: { [lang: string]: Locale; diff --git a/locales/it-IT.yml b/locales/it-IT.yml index 33686ddc3..0c0333734 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -131,6 +131,7 @@ overwriteFromPinnedEmojis: "Sovrascrivi con le impostazioni globali" reactionSettingDescription2: "Trascina per riorganizzare, clicca per cancellare, usa il pulsante \"+\" per aggiungere." rememberNoteVisibility: "Ricordare le impostazioni di visibilità delle note" attachCancel: "Rimuovi allegato" +deleteFile: "File da Drive eliminato" markAsSensitive: "Segna come esplicito" unmarkAsSensitive: "Non segnare come esplicito " enterFileName: "Nome del file" @@ -380,6 +381,9 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Abilita hCaptcha" hcaptchaSiteKey: "Chiave del sito" hcaptchaSecretKey: "Chiave segreta" +enableMcaptcha: "Abilita hCaptcha" +mcaptchaSiteKey: "Chiave del sito" +mcaptchaSecretKey: "Chiave segreta" recaptcha: "reCAPTCHA" enableRecaptcha: "Abilita reCAPTCHA" recaptchaSiteKey: "Chiave del sito" @@ -1183,6 +1187,7 @@ remainingN: "Rimangono: {n}" overwriteContentConfirm: "Vuoi davvero sostituire l'attuale contenuto?" seasonalScreenEffect: "Schermate in base alla stagione" decorate: "Decora" +lastNDays: "Ultimi {n} giorni" _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." @@ -2056,6 +2061,7 @@ _profile: _exportOrImport: allNotes: "Tutte le note" favoritedNotes: "Note preferite" + clips: "Clip" followingList: "Follow" muteList: "Elenco profili silenziati" blockingList: "Elenco profili bloccati" @@ -2353,3 +2359,6 @@ _dataSaver: _code: title: "Codice evidenziato" description: "Impedire che il codice sorgente sia automaticamente evidenziato. Evidenziare il codice richiede il caricamento di un file per ogni linguaggio. Puoi evidenziare soltanto il codice che intendi leggere e ridurre il traffico inutilizzato." +_reversi: + total: "Totale" + diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml index 5609cacb2..7fe65f423 100644 --- a/locales/ja-JP.yml +++ b/locales/ja-JP.yml @@ -134,6 +134,7 @@ overwriteFromPinnedEmojis: "全般設定から上書きする" reactionSettingDescription2: "ドラッグして並び替え、クリックして削除、+を押して追加します。" rememberNoteVisibility: "公開範囲を記憶する" attachCancel: "添付取り消し" +deleteFile: "ファイルを削除" markAsSensitive: "センシティブとして設定" unmarkAsSensitive: "センシティブを解除する" enterFileName: "ファイル名を入力" @@ -1093,6 +1094,8 @@ limitWidthOfReaction: "リアクションの最大横幅を制限し、縮小し noteIdOrUrl: "ノートIDまたはURL" video: "動画" videos: "動画" +audio: "音声" +audioFiles: "音声" dataSaver: "データセーバー" accountMigration: "アカウントの移行" accountMoved: "このユーザーは新しいアカウントに移行しました:" @@ -1240,6 +1243,12 @@ showReplay: "リプレイを見る" replay: "リプレイ" replaying: "リプレイ中" ranking: "ランキング" +lastNDays: "直近{n}日" +backToTitle: "タイトルへ" +hemisphere: "お住まいの地域" +withSensitive: "センシティブなファイルを含むノートを表示" +userSaysSomethingSensitive: "{name}のセンシティブなファイルを含む投稿" +enableHorizontalSwipe: "スワイプしてタブを切り替える" _bubbleGame: howToPlay: "遊び方" @@ -2570,3 +2579,51 @@ _dataSaver: _code: title: "コードハイライト" description: "MFMなどでコードハイライト記法が使われている場合、タップするまで読み込まれなくなります。コードハイライトではハイライトする言語ごとにその定義ファイルを読み込む必要がありますが、それらが自動で読み込まれなくなるため、通信量の削減が見込めます。" + +_hemisphere: + N: "北半球" + S: "南半球" + caption: "一部のクライアント設定で、季節を判定するために使用します。" + +_reversi: + reversi: "リバーシ" + gameSettings: "対局の設定" + chooseBoard: "ボードを選択" + blackOrWhite: "先行/後攻" + blackIs: "{name}が黒(先行)" + rules: "ルール" + thisGameIsStartedSoon: "対局はまもなく開始されます" + waitingForOther: "相手の準備が完了するのを待っています" + waitingForMe: "あなたの準備が完了するのを待っています" + waitingBoth: "準備してください" + ready: "準備完了" + cancelReady: "準備を再開" + opponentTurn: "相手のターンです" + myTurn: "あなたのターンです" + turnOf: "{name}のターンです" + pastTurnOf: "{name}のターン" + surrender: "投了" + surrendered: "投了により" + timeout: "時間切れ" + drawn: "引き分け" + won: "{name}の勝ち" + black: "黒" + white: "白" + total: "合計" + turnCount: "{count}ターン目" + myGames: "自分の対局" + allGames: "みんなの対局" + ended: "終了" + playing: "対局中" + isLlotheo: "石の少ない方が勝ち(ロセオ)" + loopedMap: "ループマップ" + canPutEverywhere: "どこでも置けるモード" + timeLimitForEachTurn: "1ターンの時間制限" + freeMatch: "フリーマッチ" + lookingForPlayer: "対戦相手を探しています" + gameCanceled: "対局がキャンセルされました" + +_offlineScreen: + title: "オフライン - サーバーに接続できません" + header: "サーバーに接続できません" + diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 1f9795a5d..649d69fba 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "全般設定から上書きする" reactionSettingDescription2: "ドラッグで並び替え、クリックで削除、+を押して追加やで。" rememberNoteVisibility: "公開範囲覚えといて" attachCancel: "のっけるのやめる" +deleteFile: "ファイルをほかす" markAsSensitive: "ちょっとこれはアカン" unmarkAsSensitive: "そこまでアカンことないやろ" enterFileName: "ファイル名を入れてや" @@ -381,6 +382,8 @@ hcaptcha: "hCaptcha(キャプチャ)" enableHcaptcha: "hCaptcha(キャプチャ)をつけとく" hcaptchaSiteKey: "サイトキー" hcaptchaSecretKey: "シークレットキー" +mcaptchaSiteKey: "サイトキー" +mcaptchaSecretKey: "シークレットキー" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA(リキャプチャ)を有効にする" recaptchaSiteKey: "サイトキー" @@ -671,6 +674,7 @@ useGlobalSettingDesc: "オンにすると、アカウントの通知設定が使 other: "その他" regenerateLoginToken: "ログイントークンを再生成" regenerateLoginTokenDescription: "ログインに使われる内部トークンをもっかい作るで。いつもならこれをやる必要はないで。もっかい作ると、全部のデバイスでログアウトされるで気ぃつけてなー。" +theKeywordWhenSearchingForCustomEmoji: "カスタム絵文字を探すときのキーワードになるで。" setMultipleBySeparatingWithSpace: "スペースで区切って何個でも設定できるで。" fileIdOrUrl: "ファイルIDかURL" behavior: "動作" @@ -883,6 +887,8 @@ makeReactionsPublicDescription: "あんたがしたツッコミ一覧を誰で classic: "クラシック" muteThread: "スレッドをミュート" unmuteThread: "スレッドのミュートを解除" +followingVisibility: "フォローの公開範囲" +followersVisibility: "フォロワーの公開範囲" continueThread: "さらにスレッドを見るで" deleteAccountConfirm: "アカウントを消すで?ええんか?" incorrectPassword: "パスワードがちゃうわ。" @@ -1181,6 +1187,9 @@ remainingN: "残り:{n}" overwriteContentConfirm: "今の内容に上書きされるけどいい?" seasonalScreenEffect: "季節にあった画面の動き" decorate: "デコる" +addMfmFunction: "装飾つける" +enableQuickAddMfmFunction: "ややこしいMFMのピッカーを出す" +lastNDays: "直近{n}日" _announcement: forExistingUsers: "もうおるユーザーのみ" forExistingUsersDescription: "オンにしたらこのお知らせができた時点でおる人らにだけお知らせが行くで。切ったらこの知らせが行ったあとにアカウント作った人にもちゃんとお知らせが行くで。" @@ -1641,6 +1650,7 @@ _emailUnavailable: disposable: "ずーっと使えるアドレスじゃないみたいや" mx: "正しいメールサーバーじゃないっぽいわ" smtp: "メールサーバーがうんともすんとも言わへん" + banned: "このメールアドレスはあかん" _ffVisibility: public: "公開" followers: "フォロワーだけに公開" @@ -1932,6 +1942,55 @@ _permissions: "write:flash": "Playを操作する" "read:flash-likes": "Playのええやん!を見る" "write:flash-likes": "Playのええやん!を見る" + "read:admin:abuse-user-reports": "ユーザーからの通報を見る" + "write:admin:delete-account": "ユーザーアカウント消す" + "write:admin:delete-all-files-of-a-user": "ユーザーのファイル全部ほかす" + "read:admin:index-stats": "データベースインデックスの情報見る" + "read:admin:table-stats": "データベーステーブルの情報見る" + "read:admin:user-ips": "ユーザーのIPアドレスを見る" + "read:admin:meta": "インスタンスのメタデータ見る" + "write:admin:reset-password": "ユーザーのパスワードをリセット" + "write:admin:resolve-abuse-user-report": "ユーザーからの通報を解決する" + "write:admin:send-email": "メール送る" + "read:admin:server-info": "サーバーの情報見る" + "read:admin:show-moderation-log": "モデレーションログ見る" + "read:admin:show-user": "ユーザーのプライベートな情報見る" + "read:admin:show-users": "ユーザーのプライベートな情報見る" + "write:admin:suspend-user": "ユーザーを凍結" + "write:admin:unset-user-avatar": "ユーザーのアバターを削除" + "write:admin:unset-user-banner": "ユーザーのバナーを削除" + "write:admin:unsuspend-user": "ユーザーの凍結解除" + "write:admin:meta": "インスタンスのメタデータいじる" + "write:admin:user-note": "モデレーションノートいじる" + "write:admin:roles": "ロールをいじる" + "read:admin:roles": "ロール見る" + "write:admin:relays": "リレーいじる" + "read:admin:relays": "リレー見る" + "write:admin:invite-codes": "招待コードいじる" + "read:admin:invite-codes": "招待コード見る" + "write:admin:announcements": "お知らせいじる" + "read:admin:announcements": "お知らせ見る" + "write:admin:avatar-decorations": "アバターデコレーションをいじる" + "read:admin:avatar-decorations": "アバターデコレーション見る" + "write:admin:federation": "連合の情報いじる" + "write:admin:account": "ユーザーアカウントいじる" + "read:admin:account": "ユーザーの情報見る" + "write:admin:emoji": "絵文字いじる" + "read:admin:emoji": "絵文字見る" + "write:admin:queue": "ジョブキューいじる" + "read:admin:queue": "ジョブキューの情報見る" + "write:admin:promo": "プロモーションノートいじる" + "write:admin:drive": "ユーザーのドライブいじる" + "read:admin:drive": "ユーザーのドライブの情報見る" + "read:admin:stream": "管理者用のWebsocket API使う" + "write:admin:ad": "広告いじる" + "read:admin:ad": "広告見る" + "write:invite-codes": "招待コード作る" + "read:invite-codes": "招待コード取得" + "write:clip-favorite": "クリップのいいねいじる" + "read:clip-favorite": "クリップのいいね見る" + "read:federation": "連合の情報取得" + "write:report-abuse": "違反報告" _auth: shareAccessTitle: "アプリへのアクセス許してやったらどうや" shareAccess: "「{name}」がアカウントにアクセスすることを許可してええか?" @@ -2053,6 +2112,7 @@ _profile: _exportOrImport: allNotes: "全てのノート" favoritedNotes: "お気に入りにしたノート" + clips: "クリップ" followingList: "フォロー" muteList: "ミュート" blockingList: "ブロック" @@ -2171,6 +2231,7 @@ _notification: pollEnded: "アンケートの結果が出たみたいや" newNote: "さらの投稿" unreadAntennaNote: "アンテナ {name}" + roleAssigned: "ロールが付与されたで" emptyPushNotificationMessage: "プッシュ通知の更新をしといたで" achievementEarned: "実績を獲得しとるで" testNotification: "通知テスト" @@ -2192,6 +2253,7 @@ _notification: pollEnded: "アンケートが終了したで" receiveFollowRequest: "フォロー許可してほしいみたいやで" followRequestAccepted: "フォローが受理されたで" + roleAssigned: "ロールが付与された" achievementEarned: "実績の獲得" app: "連携アプリからの通知や" _actions: @@ -2350,3 +2412,6 @@ _dataSaver: _code: title: "コードハイライト" description: "MFMとかでコードハイライト記法が使われてるとき、タップするまで読み込まれへんくなるで。コードハイライトではハイライトする言語ごとにその決めてるファイルを読む必要はあんねんな。けどな、それは自動で読み込まれなくなるから、通信量を少なくできることができるねん。" +_reversi: + total: "合計" + diff --git a/locales/jbo-EN.yml b/locales/jbo-EN.yml index d4fea291d..297ca53dd 100644 --- a/locales/jbo-EN.yml +++ b/locales/jbo-EN.yml @@ -1,3 +1,4 @@ --- _lang_: "la .lojban." headlineMisskey: "lo se tcana noi jorne fi loi notci" + diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml index 22e24d3ba..b976f028f 100644 --- a/locales/kab-KAB.yml +++ b/locales/kab-KAB.yml @@ -104,3 +104,4 @@ _deck: _columns: notifications: "Ilɣuyen" list: "Tibdarin" + diff --git a/locales/kn-IN.yml b/locales/kn-IN.yml index b3ad46f2b..bb6d1ee24 100644 --- a/locales/kn-IN.yml +++ b/locales/kn-IN.yml @@ -84,3 +84,4 @@ _deck: notifications: "ಅಧಿಸೂಚನೆಗಳು" tl: "ಸಮಯಸಾಲು" mentions: "ಹೆಸರಿಸಿದ" + diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml index 566667ba7..1d6c6f7bb 100644 --- a/locales/ko-GS.yml +++ b/locales/ko-GS.yml @@ -373,6 +373,8 @@ hcaptcha: "에이치캡차" enableHcaptcha: "에이치캡차 키기" hcaptchaSiteKey: "사이트키" hcaptchaSecretKey: "시크릿키" +mcaptchaSiteKey: "사이트키" +mcaptchaSecretKey: "시크릿키" recaptcha: "리캡차" enableRecaptcha: "리캡차 키기" recaptchaSiteKey: "사이트키" @@ -680,6 +682,7 @@ _profile: name: "이럼" username: "사용자 이럼" _exportOrImport: + clips: "클립 맨걸기" followingList: "팔로잉" muteList: "수ᇚ후기" blockingList: "차단하기" @@ -721,3 +724,6 @@ _moderationLogTypes: deleteUserAnnouncement: "사용자 공지 걸 뭉캐기" resetPassword: "비밀번호 재설정" resolveAbuseReport: "신고 해겔하기" +_reversi: + total: "합계" + diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 4a13012ee..5ac398475 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "일반 설정을 덮어쓰기" reactionSettingDescription2: "끌어서 순서 변경, 클릭해서 삭제, +를 눌러서 추가할 수 있습니다." rememberNoteVisibility: "공개 범위를 기억하기" attachCancel: "첨부 취소" +deleteFile: "파일 삭제" markAsSensitive: "열람주의로 설정" unmarkAsSensitive: "열람주의 해제" enterFileName: "파일명을 입력" @@ -379,6 +380,9 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptcha 활성화" hcaptchaSiteKey: "사이트 키" hcaptchaSecretKey: "시크릿 키" +enableMcaptcha: "mCaptcha 활성화" +mcaptchaSiteKey: "사이트 키" +mcaptchaSecretKey: "시크릿 키" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA 활성화" recaptchaSiteKey: "사이트 키" @@ -1151,7 +1155,7 @@ showRepliesToOthersInTimeline: "타임라인에 다른 사람에게 보내는 hideRepliesToOthersInTimeline: "타임라인에 다른 사람에게 보내는 답글을 포함하지 않음" showRepliesToOthersInTimelineAll: "타임라인에 현재 팔로우 중인 사람 전원의 답글을 포함하게 하기" hideRepliesToOthersInTimelineAll: "타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오지 않게 하기" -confirmShowRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오지 않게 하시겠습니까?" +confirmShowRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오게 하시겠습니까?" confirmHideRepliesAll: "이 조작은 되돌릴 수 없습니다. 정말로 타임라인에 현재 팔로우 중인 사람 전원의 답글이 나오지 않게 하시겠습니까?" externalServices: "외부 서비스" impressum: "운영자 정보" @@ -1183,6 +1187,10 @@ seasonalScreenEffect: "계절에 따른 효과 보이기" decorate: "장식하기" addMfmFunction: "장식 추가하기" enableQuickAddMfmFunction: "상급자용 MFM 선택기 표시하기" +sfx: "효과음" +lastNDays: "최근 {n}일" +_bubbleGame: + howToPlay: "설명" _announcement: forExistingUsers: "기존 유저에게만 알림" forExistingUsersDescription: "활성화하면 이 공지사항을 게시한 시점에서 이미 가입한 유저에게만 표시합니다. 비활성화하면 게시 후에 가입한 유저에게도 표시합니다." @@ -2105,6 +2113,7 @@ _profile: _exportOrImport: allNotes: "모든 노트" favoritedNotes: "즐겨찾기한 노트" + clips: "클립" followingList: "팔로잉" muteList: "뮤트" blockingList: "차단" @@ -2404,3 +2413,6 @@ _dataSaver: _code: title: "문자열 강조" description: "MFM 등으로 문자열 강조 기법을 사용할 때 누르기 전에는 불러오지 않습니다. 문자열 강조에서는 강조할 언어마다 그 정의 파일을 불러와야 하지만 이를 자동으로 불러오지 않으므로 데이터 사용량을 줄일 수 있습니다." +_reversi: + total: "합계" + diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml index c9e5aea1e..6f03c914f 100644 --- a/locales/lo-LA.yml +++ b/locales/lo-LA.yml @@ -1,9 +1,9 @@ --- _lang_: "ພາສາລາວ" -headlineMisskey: "ເຊື່ອມຕໍ່ເຄືອຂ່າຍໂດຍຫມາຍເຫດ" -introMisskey: "ຍິນດີຕ້ອນຮັບ! Misskey ເປັນແຫຼ່ງເປີດ, ການບໍລິການ microblogging ກະຈາຍ\nສ້າງ \"ບັນທຶກ\" ເພື່ອແບ່ງປັນຄວາມຄິດຂອງທ່ານກັບທຸກໆຄົນທີ່ຢູ່ອ້ອມຮອບທ່ານ 📡\nດ້ວຍ \"ປະຕິກິລິຍາ\", ທ່ານຍັງສາມາດສະແດງຄວາມຮູ້ສຶກຂອງທ່ານຢ່າງໄວວາກ່ຽວກັບບັນທຶກຂອງທຸກໆຄົນ 👍\nມາສຳຫຼວດໂລກໃໝ່! 🚀" +headlineMisskey: "ເຊື່ອມຕໍ່ເຄືອຂ່າຍໂດຍ note" +introMisskey: "ຍິນດີຕ້ອນຮັບ! Misskey ເປັນຊອຟແວopensource, ສຳລັບບໍລິການ microblogging ແບບ decentralized\nສ້າງ “note” ເພື່ອແບ່ງປັນຄວາມຄິດຂອງທ່ານກັບທຸກໆ ຄົນທີ່ຢູ່ອ້ອມຮອບທ່ານ 📡\nຢ່າລືມ “reaction” ໂນຕຂອງລາວເພື່ອສະແດງຄວາມຮູ້ສຶກ 👍\nມາສຳຫຼວດໂລກໃໝ່ແນ! 🚀" poweredByMisskeyDescription: "{name} ແມ່ນສ່ວນໜຶ່ງຂອງການບໍລິການທີ່ຂັບເຄື່ອນໂດຍແພລດຟອມ open source. Misskey (ເອີ້ນວ່າ \"Misskey instance\")" -monthAndDay: "{ເດືອນ}/{ມື້}" +monthAndDay: "ເດືອນ{month} / ວັນ{day}" search: "ຄົ້ນຫາ" notifications: "ການແຈ້ງເຕືອນ" username: "ຊື່ຜູ້ໃຊ້" @@ -15,25 +15,25 @@ gotIt: "ເຂົ້າໃຈແລ້ວ!" cancel: "ຍົກເລີກ" noThankYou: "ບໍ່​ແມ່ນ​ຕອນ​ນີ້" enterUsername: "ປ້ອນຊື່ຜູ້ໃຊ້" -renotedBy: "Renoted ໂດຍ {ຜູ້ໃຊ້}" -noNotes: "ບໍ່ມີຫມາຍເຫດ" +renotedBy: "Renoted ໂດຍ {user}" +noNotes: "ບໍ່ມີ note" noNotifications: "ບໍ່ມີການແຈ້ງເຕືອນ" instance: "ອີນສະແຕນ" settings: "ກຳນົດຄ່າ" notificationSettings: "ຕັ້ງຄ່າການແຈ້ງເຕືອນ" basicSettings: "ການຕັ້ງຄ່າພື້ນຖານ" otherSettings: "ການຕັ້ງຄ່າອື່ນໆ" -openInWindow: "ເປີດຢູ່ໃນປ່ອງຢ້ຽມ" +openInWindow: "ເປີດໃນປ່ອງຢ້ຽມ" profile: "ໂພຼຟາຍ" -timeline: "​ເສັ້ນກຳ​ນົດ​ເວ​ລາ​" +timeline: "ໄທມ໌ໄລນ໌" noAccountDescription: "ຜູ້ໃຊ້ນີ້ຍັງບໍ່ໄດ້ຂຽນໃນຊີວະປະຫວັດຂອງເຂົາເຈົ້າເທື່ອ" login: "ເຂົ້າ​ສູ່​ລະ​ບົບ" loggingIn: "ກຳລັງເຂົ້າສູ່ລະບົບ..." logout: "ອອກ​ຈາກ​ລະ​ບົບ" signup: "ລົງ​ທະ​ບຽນ" -uploading: "ການອັບໂຫຼດ..." +uploading: "ກຳລັງອັບໂຫຼດ..." save: "ບັນທຶກ" -users: "ຜູ້ໃຊ້ຕ່າງໆ" +users: "ຜູ້ໃຊ້" addUser: "ເພີ່ມຜູ້ໃຊ້" favorite: "ເພີ່ມໃສ່ລາຍການທີ່ມັກ" favorites: "ລາຍການທີ່ມັກ" @@ -41,13 +41,14 @@ unfavorite: "ລຶບອອກຈາກລາຍການທີ່ມັກ" favorited: "ເພີ່ມໃສ່ລາຍການທີ່ມັກແລ້ວ" alreadyFavorited: "ເພີ່ມເຂົ້າໃນລາຍການທີ່ມັກແລ້ວ." cantFavorite: "ບໍ່ສາມາດເພີ່ມໃສ່ລາຍການທີ່ມັກໄດ້." -pin: "ປັກໝຸດໄປຫາໂປຣໄຟລ໌" -unpin: "ຖອດປັກໝຸດອອກຈາກໂປຣໄຟລ໌" +pin: "ປັກໝຸດ" +unpin: "ຖອດປັກໝຸດອອກ" copyContent: "ຄັດລອກເນື້ອຫາ" -copyLink: "ສຳເນົາລິ້ງ" +copyLink: "ຄັດລອກລິ້ງ" +copyLinkRenote: "ຄັດລອກລິ້ງຂອງ renote" delete: "ລຶບ" -deleteAndEdit: "ລົບ​ແລະ​ແກ້​ໄຂ​" -deleteAndEditConfirm: "ເຈົ້າ​ແນ່​ໃຈ​ບໍ່? ທີ່ທ່ານຕ້ອງການທີ່ຈະລຶບບັນທຶກນີ້ແລະແກ້ໄຂມັນ ທ່ານອາດຈະສູນເສຍການໂຕ້ຕອບ, ບັນທຶກ, ແລະການຕອບກັບທັງໝົດ" +deleteAndEdit: "ລຶບ​ແລະ​ແກ້​ໄຂ​" +deleteAndEditConfirm: "ເຈົ້າ​ແນ່​ໃຈ​ບໍ່? ທີ່ທ່ານຕ້ອງການທີ່ຈະລຶບ note ນີ້ ແລະແກ້ໄຂມັນ ທ່ານອາດຈະສູນເສຍ reaction, renote, ແລະການຕອບກັບທັງໝົດ" addToList: "ເພີ່ມໃສ່ລາຍຊື່" addToAntenna: "ເພີ່ມໃສ່ເສົາອາກາດ" sendMessage: "ສົ່ງຂໍ້ຄວາມ" @@ -66,15 +67,15 @@ showLess: "ປິດ" youGotNewFollower: "ໄດ້ຕິດຕາມທ່ານ" receiveFollowRequest: "ປະຕິບັດຕາມຄໍາຮ້ອງຂໍທີ່ໄດ້ຮັບ" followRequestAccepted: "ຜູ້ຕິດຕາມໄດ້ຍອມຮັບຄໍາຮ້ອງຂໍຂອງທ່ານ" -mention: "ໄດ້ກ່າວມາ" +mention: "ກ່າວຖືງ" mentions: "ກ່າວເຖິງ" -directNotes: "ໂດຍກົງຫມາຍເຫດ" +directNotes: "ໂພສ Direct note" importAndExport: "ນໍາເຂົ້າ / ສົ່ງອອກ" import: "ນຳເຂົ້າ" -export: "ນຳອອກ" +export: "ສົ່ງອອກ" files: "ໄຟລ໌" download: "ດາວໂຫລດ" -driveFileDeleteConfirm: "ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບໄຟລ໌ \"{name}\"? ບັນທຶກທີ່ມີໄຟລ໌ແນບນີ້ຈະຖືກລຶບຖິ້ມ" +driveFileDeleteConfirm: "ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການລຶບໄຟລ໌ \"{name}\"? note ທີ່ມີໄຟລ໌ແນບນີ້ຈະຖືກລຶບຖິ້ມ" unfollowConfirm: "ທ່ານແນ່ໃຈບໍ່ວ່າຕ້ອງການເຊົາຕິດຕາມ {name}?" exportRequested: "ໃນເວລາທີ່ທ່ານໄດ້ຮ້ອງຂໍການສົ່ງອອກ ມັນອາດຈະໃຊ້ເວລາບາງເວລາ ແລະມັນຈະຖືກເພີ່ມໃສ່ drive ຂອງທ່ານເມື່ອມັນສຳເລັດແລ້ວ" importRequested: "ໃນເວລາທີ່ທ່ານໄດ້ຮ້ອງຂໍການນໍາເຂົ້າ ມັນອາດຈະໃຊ້ເວລາບາງເວລາ" @@ -86,7 +87,7 @@ following: "ກຳລັງຕິດຕາມ" followers: "ຜູ້ຕິດຕາມ" followsYou: "ຕິດ​ຕາມ​ເຈົ້າ" createList: "ສ້າງລາຍຊື່" -manageLists: "ການບໍລິຫານບັນຊີລາຍການ" +manageLists: "ຈັດການລາຍຊື່" error: "ຂໍ້ຜິດພາດ" somethingHappened: "​ອຸຍ, ມີ​ບາງ​ຢ່າງ​ຜິ​ດ​ພາດ" retry: "ລອງໃຫມ່" @@ -96,30 +97,30 @@ serverIsDead: "ເຊີບເວີນີ້ບໍ່ຕອບສະໜອງ youShouldUpgradeClient: "ເພື່ອເບິ່ງໜ້ານີ້, ກະລຸນາໂຫຼດຂໍ້ມູນຄືນໃໝ່ເພື່ອອັບເດດລູກຄ້າຂອງທ່ານ" enterListName: "ໃສ່ຊື່ສຳລັບລາຍຊື່" privacy: "ຄວາມເປັນສ່ວນຕົວ" -makeFollowManuallyApprove: "ປະຕິບັດຕາມການຮ້ອງຂໍຮຽກຮ້ອງໃຫ້ມີການອະນຸມັດ" -defaultNoteVisibility: "ເປັນຄ່າເລີ່ມຕົ້ນ" +makeFollowManuallyApprove: "ຕິດຕາມຄຳຂໍທີ່ຕ້ອງໄດ້ຮັບການອະນຸມັດ" +defaultNoteVisibility: "ການເບິ່ງເຫັນທີ່ເປັນຄ່າເລີ່ມຕົ້ນ" follow: "ກຳລັງຕິດຕາມ" -followRequest: "ສົ່ງ​ການ​ຮ້ອງ​ຂໍ​ປະ​ຕິ​ບ​ຕາມ​" -followRequests: "ປະຕິບັດຕາມຄໍາຮ້ອງຂໍ" +followRequest: "ສົ່ງ​ຄຳຂໍ​ຕິ​ດ​ຕາມ​" +followRequests: "ສົ່ງ​ຄຳຂໍ​ຕິ​ດ​ຕາມ​" unfollow: "ເຊົາຕິດຕາມ" -followRequestPending: "ປະຕິບັດຕາມຄໍາຮ້ອງຂໍທີ່ລໍຖ້າຢູ່" -enterEmoji: "ປ້ອນອີໂມຈິ" +followRequestPending: "ລໍຖ້າການອະນຸມັດໃຫ້ຕິດຕາມ" +enterEmoji: "ປ້ອນເອໂມຈິ" renote: "Renote" unrenote: "ເລີກ Renote" -renoted: "ເກັບບັນທຶກໄວ້" -cantRenote: "ໂພສນີ້ບໍ່ສາມາດຖືກບັນທຶກໄວ້ຄືນໃໝ່ໄດ້" +renoted: "renote ແລ້ວ" +cantRenote: "ໂພສນີ້ບໍ່ສາມາດ renote ໃໝ່ໄດ້" cantReRenote: "ບໍ່ສາມາດບັນທຶກຄືນໃໝ່ໄດ້" -quote: "ລວມຂໍ້ຄວາມອ້າງອີງ" -inChannelRenote: "ຊ່ອງພຽງແຕ່ Renote" -inChannelQuote: "ຊ່ອງເທົ່ານັ້ນ Quote" -pinnedNote: "ບັນທຶກທີ່ປັກໝຸດໄວ້" -pinned: "ປັກໝຸດໄປຫາໂປຣໄຟລ໌" +quote: "ອ້າງອີງ" +inChannelRenote: "Renote ໃນ channel ເທົ່ານັ້ນ" +inChannelQuote: "ອ້າງອິງໃນ channel ເທົ່ານັ້ນ" +pinnedNote: "note ທີ່ປັກໝຸດໄວ້" +pinned: "ປັກໝຸດ" you: "ເຈົ້າ" clickToShow: "ກົດເພື່ອສະແດງໃຫ້ເຫັນ" sensitive: "NSFW" add: "ເພີ່ມ" -reaction: "ປະຕິກິລິຍາ" -reactions: "ປະຕິກິລິຍາ" +reaction: "reaction" +reactions: "reaction" attachCancel: "ເອົາໄຟລ໌ແນບ" mute: "ປີດສຽງ" unmute: "ເປີດສຽງ" @@ -306,6 +307,8 @@ basicInfo: "ຂໍ້ມຸນເບື້ອງຕົ້ນ" pinnedNotes: "ບັນທຶກທີ່ປັກໝຸດໄວ້" hcaptchaSiteKey: "ກະແຈໄຊທ໌" hcaptchaSecretKey: "ກະແຈລັບ" +mcaptchaSiteKey: "ກະແຈໄຊທ໌" +mcaptchaSecretKey: "ກະແຈລັບ" recaptcha: "reCAPTCHA" enableRecaptcha: "ເປີດໃຊ້ງານລີແຄ໋ບຈາ" recaptchaSiteKey: "ກະແຈໄຊທ໌" @@ -463,3 +466,4 @@ _webhookSettings: name: "ຊື່" _moderationLogTypes: suspend: "ລະງັບ" + diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml index 1e96a1aa9..42fbf183b 100644 --- a/locales/nl-NL.yml +++ b/locales/nl-NL.yml @@ -348,6 +348,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Inschakelen hCaptcha" hcaptchaSiteKey: "Site sleutel" hcaptchaSecretKey: "Geheime sleutel" +mcaptchaSiteKey: "Site sleutel" +mcaptchaSecretKey: "Geheime sleutel" recaptcha: "reCAPTCHA" enableRecaptcha: "Inschakelen reCAPTCHA" recaptchaSiteKey: "Site sleutel" @@ -495,3 +497,4 @@ _webhookSettings: _moderationLogTypes: suspend: "Opschorten" resetPassword: "Wachtwoord terugzetten" + diff --git a/locales/no-NO.yml b/locales/no-NO.yml index 195b1d071..85ccd6256 100644 --- a/locales/no-NO.yml +++ b/locales/no-NO.yml @@ -720,3 +720,4 @@ _webhookSettings: name: "Navn" _moderationLogTypes: suspend: "Suspender" + diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml index b0f9f4923..2ed02e3f3 100644 --- a/locales/pl-PL.yml +++ b/locales/pl-PL.yml @@ -345,6 +345,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Włącz hCaptcha" hcaptchaSiteKey: "Klucz strony" hcaptchaSecretKey: "Tajny klucz" +mcaptchaSiteKey: "Klucz strony" +mcaptchaSecretKey: "Tajny klucz" recaptcha: "reCAPTCHA" enableRecaptcha: "Włącz reCAPTCHA" recaptchaSiteKey: "Klucz strony" @@ -1232,6 +1234,7 @@ _profile: _exportOrImport: allNotes: "Wszystkie wpisy" favoritedNotes: "Ulubione wpisy" + clips: "Klip" followingList: "Obserwowani" muteList: "Wycisz" blockingList: "Zablokuj" @@ -1394,3 +1397,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Zawieś" resetPassword: "Zresetuj hasło" +_reversi: + total: "Łącznie" + diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml index 1fd2fd57e..795f947c7 100644 --- a/locales/pt-PT.yml +++ b/locales/pt-PT.yml @@ -368,6 +368,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Ativar hCaptcha" hcaptchaSiteKey: "Chave do sítio ‘web’" hcaptchaSecretKey: "Chave secreta" +mcaptchaSiteKey: "Chave do sítio ‘web’" +mcaptchaSecretKey: "Chave secreta" recaptcha: "reCAPTCHA" enableRecaptcha: "Habilitar reCAPTCHA" recaptchaSiteKey: "Chave do sítio ‘web’" @@ -1008,6 +1010,7 @@ replies: "Respostas" renotes: "Repostagens" keepScreenOn: "Manter a tela do dispositivo sempre ligada" flip: "Inversão" +lastNDays: "Últimos {n} dias" _initialAccountSetting: followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo." _serverSettings: @@ -1400,6 +1403,7 @@ _profile: username: "Nome de usuário" _exportOrImport: favoritedNotes: "Notas nos favoritos" + clips: "Clipe" followingList: "Seguindo" muteList: "Silenciar" blockingList: "Bloquear" @@ -1494,3 +1498,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Suspender" resetPassword: "Redefinir senha" +_reversi: + total: "Total" + diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml index bf8787413..e45b8b75e 100644 --- a/locales/ro-RO.yml +++ b/locales/ro-RO.yml @@ -359,6 +359,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Activează hCaptcha" hcaptchaSiteKey: "Site key" hcaptchaSecretKey: "Secret key" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret key" recaptcha: "reCAPTCHA" enableRecaptcha: "Activează reCAPTCHA" recaptchaSiteKey: "Site key" @@ -725,3 +727,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Suspendă" resetPassword: "Resetează parola" +_reversi: + total: "Total" + diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml index 25f409df9..60682fe96 100644 --- a/locales/ru-RU.yml +++ b/locales/ru-RU.yml @@ -53,8 +53,8 @@ addToAntenna: "Добавить к антенне" sendMessage: "Отправить сообщение" copyRSS: "Скопировать RSS" copyUsername: "Скопировать имя пользователя" -copyUserId: "Скопировать ID пользователя" -copyNoteId: "Скопировать ID заметки" +copyUserId: "Скопировать идентификатор пользователя" +copyNoteId: "Скопировать идентификатор заметки" copyFileId: "Скопировать ID файла" copyFolderId: "Скопировать ID папки" copyProfileUrl: "Скопировать URL профиля " @@ -134,8 +134,8 @@ unmarkAsSensitive: "Снять отметку «не для всех»" enterFileName: "Введите имя файла" mute: "Скрыть" unmute: "Отменить скрытие" -renoteMute: "Заглушить репосты" -renoteUnmute: "Включить репосты" +renoteMute: "Скрыть репосты" +renoteUnmute: "Открыть репосты" block: "Заблокировать" unblock: "Разблокировать" suspend: "Заморозить" @@ -161,8 +161,8 @@ addEmoji: "Добавить эмодзи" settingGuide: "Рекомендуемые настройки" cacheRemoteFiles: "Кешировать внешние файлы" cacheRemoteFilesDescription: "Когда эта настройка отключена, файлы с других сайтов будут загружаться прямо оттуда. Это сэкономит место на сервере, но увеличит трафик, так как не будут создаваться эскизы." -cacheRemoteSensitiveFiles: "Кешировать внешние файлы" -cacheRemoteSensitiveFilesDescription: "Описание удаленных внешних файлов в кэше" +cacheRemoteSensitiveFiles: "Кэшировать внешние файлы «не для всех»" +cacheRemoteSensitiveFilesDescription: "Если отключено, файлы «не для всех» загружаются непосредственно с удалённых серверов, не кэшируясь." flagAsBot: "Аккаунт бота" flagAsBotDescription: "Включите, если этот аккаунт управляется программой. Это позволит системе Misskey учитывать это, а также поможет разработчикам других ботов предотвратить бесконечные циклы взаимодействия." flagAsCat: "Аккаунт кота" @@ -261,6 +261,7 @@ removed: "Удалено" removeAreYouSure: "Хотите удалить «{x}»?" deleteAreYouSure: "Хотите удалить «{x}»?" resetAreYouSure: "На самом деле сбросить?" +areYouSure: "Вы уверены?" saved: "Сохранено" messaging: "Сообщения" upload: "Загрузить" @@ -278,7 +279,7 @@ noMoreHistory: "История закончилась" startMessaging: "Начать общение" nUsersRead: "Прочитали {n}" agreeTo: "Я соглашаюсь с {0}" -agree: "Согласиться" +agree: "Согласен" agreeBelow: "Согласен со следующими" basicNotesBeforeCreateAccount: "Записи, перед созданием аккаунта" termsOfService: "Условия использования" @@ -324,7 +325,7 @@ copyUrl: "Копировать ссылку" rename: "Переименовать" avatar: "Аватар" banner: "Шапка" -displayOfSensitiveMedia: "Определение деликатного контента" +displayOfSensitiveMedia: "Отображение содержимого не для всех" whenServerDisconnected: "Когда соединение с сервером потеряно" disconnectedFromServer: "Разорвано соединение с сервером" reload: "Перезагрузить" @@ -372,6 +373,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Включить hCaptcha" hcaptchaSiteKey: "Ключ сайта" hcaptchaSecretKey: "Секретный ключ" +mcaptchaSiteKey: "Ключ сайта" +mcaptchaSecretKey: "Секретный ключ" recaptcha: "reCAPTCHA" enableRecaptcha: "Включить reCAPTCHA" recaptchaSiteKey: "Ключ сайта" @@ -413,7 +416,7 @@ about: "Описание" aboutMisskey: "О Misskey" administrator: "Администратор" token: "Токен" -2fa: "2-х факторная аутентификация" +2fa: "Двухфакторная аутентификация" setupOf2fa: "Настроить двухфакторную аутентификацию" totp: "Приложение-аутентификатор" totpDescription: "Описание приложения-аутентификатора" @@ -477,7 +480,7 @@ aboutX: "Описание {x}" emojiStyle: "Стиль эмодзи" native: "Системные" disableDrawer: "Не использовать выдвижные меню" -showNoteActionsOnlyHover: "Показывать кнопки управления заметкой только при наведении" +showNoteActionsOnlyHover: "Показывать кнопки у заметок только при наведении" noHistory: "История пока пуста" signinHistory: "Журнал посещений" enableAdvancedMfm: "Включить расширенный MFM" @@ -490,8 +493,8 @@ createAccount: "Новая учётная запись" existingAccount: "Существующая учётная запись" regenerate: "Создать повторно" fontSize: "Размер шрифта" -mediaListWithOneImageAppearance: "Показывать список медиа только одним изображением" -limitTo: "Обрезать до {x}" +mediaListWithOneImageAppearance: "Вид изображения, если оно единственное в списке" +limitTo: "Ограничить до {x}" noFollowRequests: "Нерассмотренные запросы на подписку отсутствуют" openImageInNewTab: "Открыть изображение в новой вкладке" dashboard: "Панель управления" @@ -525,7 +528,7 @@ objectStorageUseSSLDesc: "Отключите, если не собираетес objectStorageUseProxy: "Использовать прокси" objectStorageUseProxyDesc: "Отключите, если не будете испоьзовать прокси для соединений по протоколу ObjectStorage." objectStorageSetPublicRead: "Устанавливать public-read при загрузке на сервер" -s3ForcePathStyleDesc: "Включение s3ForcePathStyle принудительно указывает имя корзины как часть пути в URL-адресе вместо имени хоста. Может потребоваться активация при использовании таких вещей, как локальный Minio." +s3ForcePathStyleDesc: "Включение s3ForcePathStyle приводит к тому, что имя корзины указывается как часть пути в URL, а не в имени хоста. Может потребоваться включить при использовании локального Minio или чего-то подобного." serverLogs: "Журнал сервера" deleteAll: "Удалить всё" showFixedPostForm: "Показывать поле для ввода новой заметки наверху ленты" @@ -569,7 +572,7 @@ yourAccountSuspendedTitle: "Эта учетная запись заблокир yourAccountSuspendedDescription: "Эта учетная запись была заблокирована из-за нарушения условий предоставления услуг сервера. Свяжитесь с администратором, если вы хотите узнать более подробную причину. Пожалуйста, не создавайте новую учетную запись." tokenRevoked: "Токен недействителен" tokenRevokedDescription: "Срок действия вашего токена входа истек. Пожалуйста, войдите снова." -accountDeleted: "Эта учетная запись удалена" +accountDeleted: "Учетная запись удалена" accountDeletedDescription: "Эта учетная запись удалена" menu: "Меню" divider: "Линия-разделитель" @@ -651,6 +654,7 @@ useGlobalSettingDesc: "Если включено, будут использов other: "Другие" regenerateLoginToken: "Создать новый токен для входа" regenerateLoginTokenDescription: "Создаёт новый токен, используемый внутри программы во время входа. Обычно в этом нет необходимости. При создании все устройства будут отключены." +theKeywordWhenSearchingForCustomEmoji: "Это ключевое слово будет использовано при поиске эмодзи." setMultipleBySeparatingWithSpace: "Можно написать несколько через пробел" fileIdOrUrl: "Идентификатор файла или ссылка" behavior: "Поведение" @@ -721,7 +725,7 @@ useSystemFont: "Использовать шрифт, предлагаемый с clips: "Подборки" experimentalFeatures: "Экспериментальные функции" experimental: "Экспериментальные" -thisIsExperimentalFeature: "Это экспериментальная функция. Технические характеристики могут измениться или он может работать неправильно." +thisIsExperimentalFeature: "Это экспериментальная функция. Её поведение, вероятно, поменяется в следующей версии, а ещё она может работать не так, как задумано." developer: "Разработчик" makeExplorable: "Опубликовать профиль в «Обзоре»." makeExplorableDescription: "Если выключить, ваш профиль не будет показан в разделе «Обзор»." @@ -806,7 +810,7 @@ noMaintainerInformationWarning: "Не заполнены сведения об noBotProtectionWarning: "Ботозащита не настроена" configure: "Настроить" postToGallery: "Опубликовать в галерею" -postToHashtag: "Опубликовать пост с этим хештегом" +postToHashtag: "Написать заметку с этим хэштегом" gallery: "Галерея" recentPosts: "Недавние публикации" popularPosts: "Популярные публикации" @@ -835,7 +839,7 @@ useBlurEffect: "Размытие в интерфейсе" learnMore: "Подробнее" misskeyUpdated: "Misskey обновился!" whatIsNew: "Что новенького?" -translate: "Перевод" +translate: "Перевести" translatedFrom: "Перевод. Язык оригинала — {x}" accountDeletionInProgress: "В настоящее время выполняется удаление учетной записи" usernameInfo: "Имя, которое отличает вашу учетную запись от других на этом сервере. Вы можете использовать алфавит (a~z, A~Z), цифры (0~9) или символы подчеркивания (_). Имена пользователей не могут быть изменены позже." @@ -847,11 +851,11 @@ lastCommunication: "Последнее сообщение" resolved: "Решено" unresolved: "Без решения" breakFollow: "Отписка" -breakFollowConfirm: "Удалить из подписок пользователя ?" +breakFollowConfirm: "Действительно удалить этого подписчика?" itsOn: "Включено" itsOff: "Выключено" -on: "Вкл" -off: "Выкл" +on: "Вкл." +off: "Выкл." emailRequiredForSignup: "Для регистрации учётной записи нужен адрес электронной почты" unread: "Непрочитанное" filter: "Фильтры" @@ -880,7 +884,7 @@ numberOfColumn: "Количество столбцов" searchByGoogle: "Поиск" instanceDefaultLightTheme: "Светлая тема по умолчанию" instanceDefaultDarkTheme: "Темная тема по умолчанию" -instanceDefaultThemeDescription: "Описание темы по умолчанию для инстанса" +instanceDefaultThemeDescription: "Введите код темы в формате объекта." mutePeriod: "Продолжительность скрытия" period: "Опрос длится" indefinitely: "вечно" @@ -904,7 +908,7 @@ thereIsUnresolvedAbuseReportWarning: "Остались нерешённые жа recommended: "Рекомендуем" check: "Проверить" driveCapOverrideLabel: "Изменение лимита дискового пространства для этого пользователя" -driveCapOverrideCaption: "Укажите меньше или равное нулю для отмены" +driveCapOverrideCaption: "Введите нуль или меньше, чтобы использовать значение по умолчанию." requireAdminForView: "Для просмотра необходимо иметь аккаунт администратора" isSystemAccount: "Данная учётная запись создана автоматически и управляется системой" typeToConfirm: "Введите {x} для продолжения" @@ -924,7 +928,7 @@ type: "Тип" speed: "Скорость" slow: "Медленная" fast: "Быстрая" -sensitiveMediaDetection: "Определение содержимого деликатного характера" +sensitiveMediaDetection: "Распознание содержимого не для всех" localOnly: "Локально" remoteOnly: "Только удалённо" failedToUpload: "Сбой выгрузки" @@ -1001,10 +1005,11 @@ invitationRequiredToRegister: "Этот сервер в настоящее вр emailNotSupported: "Доставка почты не поддерживается на этом сервере" postToTheChannel: "Отправить в канал" cannotBeChangedLater: "Это нельзя изменить позже" -reactionAcceptance: "Принятие реакций" -likeOnly: "Только лайки" -likeOnlyForRemote: "Только лайки с удалённых серверов" -nonSensitiveOnly: "Безопасный серфинг" +reactionAcceptance: "Допустимые реакции" +likeOnly: "Только «нравится!»" +likeOnlyForRemote: "Всё (с других серверов только «нравится!»)" +nonSensitiveOnly: "Только безопасные" +nonSensitiveOnlyForLocalLikeOnlyForRemote: "Только безопасные (с других серверов только «нравится!»)" rolesAssignedToMe: "Мои роли" resetPasswordConfirm: "Сбросить пароль?" sensitiveWords: "Чувствительные слова" @@ -1024,20 +1029,20 @@ noteIdOrUrl: "ID или ссылка на заметку" video: "Видео" videos: "Видео" dataSaver: "Экономия трафика" -accountMigration: "Перенести учётную запись" -accountMoved: "Учетная запись перенесена" +accountMigration: "Перенос учётной записи" +accountMoved: "Учётная запись перенесена" accountMovedShort: "Эта учётная запись перемещена" -operationForbidden: "Эта операция невозможна." +operationForbidden: "Это действие запрещено" forceShowAds: "Всегда отображать рекламу" -addMemo: "Добавить заметку" -editMemo: "Редактировать заметку" -reactionsList: "Реакции" +addMemo: "Добавить памятку" +editMemo: "Изменить памятку" +reactionsList: "Список реакций" renotesList: "Репосты" -notificationDisplay: "Отображение уведомления" -leftTop: "Верхний левый угол" -rightTop: "Сверху справа" -leftBottom: "Снизу слева" -rightBottom: "Снизу справа" +notificationDisplay: "Отображение уведомлений" +leftTop: "Влево вверх" +rightTop: "Вправо вверх" +leftBottom: "Влево вниз" +rightBottom: "Вправо вниз" vertical: "Вертикальная" horizontal: "Сбоку" position: "Позиция" @@ -1077,6 +1082,7 @@ replies: "Ответы" renotes: "Репост" loadReplies: "Показать ответы" flip: "Переворот" +lastNDays: "Последние {n} сут" _initialAccountSetting: accountCreated: "Аккаунт успешно создан!" letsStartAccountSetup: "Давайте настроим вашу учётную запись." @@ -1693,7 +1699,7 @@ _weekday: _widgets: profile: "Профиль" instanceInfo: "Информация об инстансе" - memo: "Напоминания" + memo: "Памятки" notifications: "Уведомления" timeline: "Лента" calendar: "Календарь" @@ -1784,6 +1790,7 @@ _profile: _exportOrImport: allNotes: "Все заметки\n" favoritedNotes: "Избранное" + clips: "Подборка" followingList: "Подписки" muteList: "Скрытые" blockingList: "Заблокированные" @@ -1959,4 +1966,10 @@ _webhookSettings: active: "Вкл." _moderationLogTypes: suspend: "Заморозить" + addCustomEmoji: "Добавлено эмодзи" + updateCustomEmoji: "Изменено эмодзи" + deleteCustomEmoji: "Удалено эмодзи" resetPassword: "Сброс пароля:" +_reversi: + total: "Всего" + diff --git a/locales/si-LK.yml b/locales/si-LK.yml index ed97d539c..cd21505a4 100644 --- a/locales/si-LK.yml +++ b/locales/si-LK.yml @@ -1 +1,2 @@ --- + diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml index ccd976769..66fafd554 100644 --- a/locales/sk-SK.yml +++ b/locales/sk-SK.yml @@ -349,6 +349,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Zapnúť hCaptchu" hcaptchaSiteKey: "Site key" hcaptchaSecretKey: "Secret key" +mcaptchaSiteKey: "Site key" +mcaptchaSecretKey: "Secret key" recaptcha: "reCAPTCHA" enableRecaptcha: "Zapnúť ReCAPTCHA" recaptchaSiteKey: "Site key" @@ -918,6 +920,7 @@ icon: "Avatar" replies: "Odpovede" renotes: "Preposlať" flip: "Preklopiť" +lastNDays: "Posledných {n} dní" _role: priority: "Priorita" _priority: @@ -1284,6 +1287,7 @@ _profile: changeBanner: "Zmeniť banner" _exportOrImport: allNotes: "Všetky poznámky" + clips: "Klip" followingList: "Sledujete" muteList: "Vypnúť zvuk" blockingList: "Zablokovať" @@ -1441,3 +1445,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Zmraziť" resetPassword: "Resetovať heslo" +_reversi: + total: "Celkom" + diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml index 4defa3b11..76b9bc90b 100644 --- a/locales/sv-SE.yml +++ b/locales/sv-SE.yml @@ -345,6 +345,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Aktivera hCaptcha" hcaptchaSiteKey: "Webbplatsnyckel" hcaptchaSecretKey: "Hemlig nyckel" +mcaptchaSiteKey: "Webbplatsnyckel" +mcaptchaSecretKey: "Hemlig nyckel" recaptcha: "reCAPTCHA" enableRecaptcha: "Aktivera reCAPTCHA" recaptchaSiteKey: "Webbplatsnyckel" @@ -574,3 +576,4 @@ _webhookSettings: _moderationLogTypes: suspend: "Suspendera" resetPassword: "Återställ Lösenord" + diff --git a/locales/th-TH.yml b/locales/th-TH.yml index d94cfbfc5..b14d85556 100644 --- a/locales/th-TH.yml +++ b/locales/th-TH.yml @@ -1,7 +1,7 @@ --- _lang_: "ภาษาไทย" -headlineMisskey: "เชื่อมต่อระบบ Network ด้วย Note" -introMisskey: "ยินดีต้อนรับทุกคนจ้า! Misskey คือ บริการไมโครบล็อกกิ้ง (MicroBlogging) แบบกระจายศูนย์อำนาจ (Decentralized) \n\nเขียน \"โน้ต (Note)\" เพื่อส่งต่อเรื่องราวของคุณให้ทั้งโลกได้รับรู้📡\nและอย่าลืมที่จะ \"React\" กับเรื่องราวของคนอื่น ๆ ด้วย! 👍\n\nมุ่งสู่โลกใบใหม่กันเถอะ🚀" +headlineMisskey: "เชื่อมต่อเครือข่ายโดยโน้ต" +introMisskey: "ยินดีต้อนรับทุกคนจ้า! Misskey คือ ซอฟต์แวร์โอเพนซอร์สสำหรับบริการไมโครบล็อกกิ้ง (MicroBlogging) แบบกระจายศูนย์อำนาจ (Decentralized) \n\nเขียน “โน้ต (Note)” เพื่อส่งต่อเรื่องราวของคุณให้ทั้งโลกได้รับรู้📡\nและอย่าลืมที่จะ “รีแอคชั่น” กับเรื่องราวของคนอื่น ๆ ด้วยนะ! 👍\n\nท่องสำรวจโลกใบใหม่กันเถอะ🚀" poweredByMisskeyDescription: "{name} เป็นส่วนหนึ่งในบริการที่ถูกขับเคลื่อนโดยแพลตฟอร์มโอเพ่นซอร์ส Misskey (เรียกว่า \"อินสแตนซ์ Misskey\")" monthAndDay: "{month}/{day}" search: "ค้นหา" @@ -9,12 +9,12 @@ notifications: "การเเจ้งเตือน" username: "ชื่อผู้ใช้" password: "รหัสผ่าน" forgotPassword: "ลืมรหัสผ่านใช่ไหม" -fetchingAsApObject: "กำลังดึงข้อมูล จาก เฟดิเวิร์ส..." -ok: "โอเค" +fetchingAsApObject: "กำลังดึงข้อมูลจากสหพันธ์..." +ok: "ตกลง" gotIt: "เข้าใจแล้ว !" cancel: "ยกเลิก" noThankYou: "ไม่เป็นไร" -enterUsername: "ใส่ชื่อผู้ใช้" +enterUsername: "กรอกชื่อผู้ใช้" renotedBy: "รีโน้ตโดย {user}" noNotes: "ไม่มีโน้ต" noNotifications: "ไม่มีการแจ้งเตือน" @@ -26,7 +26,7 @@ otherSettings: "การตั้งค่าอื่นๆ" openInWindow: "เปิดในหน้าต่าง" profile: "โปรไฟล์" timeline: "ไทม์ไลน์" -noAccountDescription: "ผู้ใช้รายนี้ยังไม่ได้เขียนลงประวัติของพวกเขา" +noAccountDescription: "ผู้ใช้รายนี้ยังไม่ได้เขียนคำแนะนำตัว" login: "เข้าสู่ระบบ" loggingIn: "กำลังเข้าสู่ระบบ" logout: "ออกจากระบบ" @@ -41,15 +41,15 @@ unfavorite: "ลบออกจากรายการโปรด" favorited: "เพิ่มแล้วในรายการโปรด" alreadyFavorited: "เพิ่มในรายการโปรดอยู่แล้ว" cantFavorite: "ไม่สามารถเพิ่มในรายการโปรดได้" -pin: "ปักหมุดไปยังโปรไฟล์" -unpin: "เลิกปักหมุดจากโปรไฟล์" +pin: "ปักหมุด" +unpin: "เลิกปักหมุด" copyContent: "คัดลอกเนื้อหา" copyLink: "คัดลอกลิงก์" copyLinkRenote: "คัดลอกลิงก์รีโน้ต" delete: "ลบ" deleteAndEdit: "ลบและแก้ไข" -deleteAndEditConfirm: "นายแน่ใจแล้วเหรอ? ว่าต้องการลบโน้ตนี้และแก้ไข คุณอาจจะสูญเสียการโต้ตอบ, โน้ต, และการตอบกลับทั้งหมดได้นะ" -addToList: "เพิ่มในลิสต์" +deleteAndEditConfirm: "คุณต้องการลบโน้ตนี้และแก้ไขใหม่ใช่ไหม? รีแอคชั่น รีโน้ต และการตอบกลับต่อโน้ตนี้ทั้งหมดจะถูกลบออกด้วย" +addToList: "เพิ่มลงรายชื่อ" addToAntenna: "เพิ่มไปยังเสาอากาศ" sendMessage: "ส่งข้อความ" copyRSS: "คัดลอก RSS" @@ -59,35 +59,35 @@ copyNoteId: "คัดลอก ID โน้ต " copyFileId: "คัดลอกไฟล์ ID" copyFolderId: "คัดลอกโฟลเดอร์ ID" copyProfileUrl: "คัดลอกโปรไฟล์ URL" -searchUser: "ค้นหาผู้ใช้งาน" +searchUser: "ค้นหาผู้ใช้" reply: "ตอบกลับ" -loadMore: "โหลดเพิ่มเติม" +loadMore: "แสดงเพิ่มเติม" showMore: "แสดงเพิ่มเติม" showLess: "ปิด" youGotNewFollower: "ได้ติดตามคุณ" receiveFollowRequest: "คำขอผู้ติดตามที่ได้รับ" -followRequestAccepted: "ผู้ติดตามได้ตอบรับคำขอร้องของคุณแล้ว" +followRequestAccepted: "อนุมัติการติดตามแล้ว" mention: "กล่าวถึง" mentions: "พูดถึง" directNotes: "ไดเร็คโน้ต" importAndExport: "นำเข้า / ส่งออก" import: "นำเข้า" -export: "นำออก" +export: "ส่งออก" files: "ไฟล์" download: "ดาวน์โหลด" -driveFileDeleteConfirm: "คุณต้องการลบไฟล์ \"{name}\" ใช่หรือไม่? โน้ตย่อที่แนบมากับไฟล์นี้ก็จะถูกลบไปด้วย" -unfollowConfirm: "นายแน่ใจแล้วหรอว่าต้องการเลิกติดตาม {name}?" -exportRequested: "เมื่อคุณได้ร้องขอการส่งออก อาจจะต้องใช้เวลาสักครู่ และจะถูกเพิ่มในไดรฟ์ของคุณเมื่อเสร็จสิ้นแล้ว" -importRequested: "เมื่อคุณได้ร้องขอการนำเข้า อาจจะต้องใช้เวลาสักครู่นะ" -lists: "รายการ" -noLists: "คุณไม่มีลิสต์ใด ๆ" +driveFileDeleteConfirm: "ต้องการลบไฟล์ “{name}” ใช่หรือไม่? โน้ตที่แนบมากับไฟล์นี้ก็จะถูกลบไปด้วย" +unfollowConfirm: "ต้องการเลิกติดตาม {name}?" +exportRequested: "คุณได้ร้องขอการส่งออก อาจใช้เวลาสักครู่ และจะถูกเพิ่มในไดรฟ์ของคุณเมื่อเสร็จสิ้นแล้ว" +importRequested: "คุณได้ร้องขอการนำเข้า การดำเนินการนี้อาจใช้เวลาสักครู่" +lists: "รายชื่อ" +noLists: "คุณไม่มีรายชื่อใดๆ" note: " โน้ต" -notes: "ตัวโน้ต" +notes: " โน้ต" following: "กำลังติดตาม" followers: "ผู้ติดตาม" followsYou: "ติดตามคุณ" -createList: "สร้างลิสต์" -manageLists: "จัดการลิสต์" +createList: "สร้างรายชื่อ" +manageLists: "จัดการรายชื่อ" error: "ผิดพลาด!" somethingHappened: "อุ๊ย ! มีอะไรบางอย่างผิดพลาด" retry: "ลองใหม่อีกครั้ง" @@ -95,37 +95,44 @@ pageLoadError: "เกิดข้อผิดพลาดในการโห pageLoadErrorDescription: "โดยปกติแล้วมักจะเกิดจากข้อผิดพลาดของเครือข่ายหรือแคชของเบราว์เซอร์ ลองล้างแคชแล้วลองใหม่อีกครั้งหลังจากรอสักครู่ " serverIsDead: "เซิร์ฟเวอร์นี้ไม่มีการตอบสนอง โปรดกรุณารอสักครู่แล้วลองใหม่อีกครั้ง" youShouldUpgradeClient: "หากต้องการดูหน้านี้ กรุณาโหลดหน้าใหม่เพื่ออัปเดตไคลเอ็นต์ของคุณ" -enterListName: "ใส่ชื่อสำหรับรายการลิสต์" +enterListName: "ป้อนนามเรียกของรายชื่อชุดนี้" privacy: "ความเป็นส่วนตัว" -makeFollowManuallyApprove: "ติดตามคำขอที่ต้องได้รับการอนุมัติ" +makeFollowManuallyApprove: "อนุมัติคำขอติดตามด้วยตนเอง" defaultNoteVisibility: "การมองเห็นที่เป็นค่าเริ่มต้น" follow: "ติดตาม" followRequest: "ส่งคำขอติดตาม" followRequests: "ส่งคำขอติดตาม" unfollow: "เลิกติดตาม" -followRequestPending: "กำลังรอดำเนินการร้องขอติดตาม" -enterEmoji: "ใส่อีโมจิ" +followRequestPending: "รออนุมัติคำขอติดตาม" +enterEmoji: "พิมพ์เอโมจิ" renote: "รีโน้ต" unrenote: "เลิกรีโน้ต" renoted: "รีโน้ตแล้ว" -cantRenote: "โพสต์นี้ไม่สามารถรีโน้ตไว้ใหม่ได้นะ" -cantReRenote: "ไม่สามารถรีโน้ตเอาไว้ใหม่ได้นะ" +cantRenote: "โพสต์นี้ไม่สามารถรีโน้ตใหม่ได้" +cantReRenote: "รีโน้ตไม่สามารถรีโน้ตซ้ำได้" quote: "อ้างอิง" -inChannelRenote: "รีโน้ตช่องแชลแนลเท่านั้น" -inChannelQuote: "อ้างช่องเท่านั้น" -pinnedNote: "โน้ตที่ปักหมุดเอาไว้" -pinned: "ปักหมุดไปยังโปรไฟล์" +inChannelRenote: "รีโน้ตในช่องเท่านั้น" +inChannelQuote: "อ้างอิงในช่องเท่านั้น" +pinnedNote: "โน้ตที่ปักหมุดไว้" +pinned: "ปักหมุด" you: "คุณ" clickToShow: "คลิกเพื่อแสดง" -sensitive: "เนื้อหาที่ละเอียดอ่อน NSFW" +sensitive: "เนื้อหาที่ละเอียดอ่อน" add: "เพิ่ม" reaction: "รีแอคชั่น" reactions: "รีแอคชั่น" -reactionSettingDescription2: "กดลากเพื่อจัดลำดับใหม่ กดคลิกเพื่อลบ กด \"+\" เพื่อเพิ่ม" +emojiPicker: "ตัวจิ้มเอโมจิ" +pinnedEmojisForReactionSettingDescription: "ตรึงเอโมจิไว้ด้านบนสำหรับรีแอคชั่นอย่างเร่งด่วน" +pinnedEmojisSettingDescription: "ตรึงเอโมจิไว้ด้านบนสำหรับพิมพ์เอโมจิอย่างเร่งด่วน" +emojiPickerDisplay: "แสดงตัวจิ้มเอโมจิ" +overwriteFromPinnedEmojisForReaction: "เขียนทับการตั้งค่ารีแอคชั่น" +overwriteFromPinnedEmojis: "เขียนทับการตั้งค่าทั่วไป" +reactionSettingDescription2: "ลากเพื่อจัดลำดับใหม่ คลิกที่เอโมจินั้นเพื่อลบ กด “+” เพื่อเพิ่ม" rememberNoteVisibility: "จดจำการตั้งค่าการมองเห็นตัวโน้ต" attachCancel: "ลบไฟล์ออกที่แนบมา" -markAsSensitive: "ทำเครื่องหมายว่าละเอียดอ่อน" -unmarkAsSensitive: "ยกเลิกทำเครื่องหมายเป็น NSFW" +deleteFile: "ลบไฟล์ออกแล้ว" +markAsSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน" +unmarkAsSensitive: "ยกเลิกทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน" enterFileName: "พิมพ์ชื่อไฟล์" mute: "ปิดเสียง" unmute: "ยกเลิกการปิดเสียง" @@ -135,34 +142,34 @@ block: "บล็อค" unblock: "เลิกปิดกั้น" suspend: "ถูกระงับ" unsuspend: "ยกเลิกระงับ" -blockConfirm: "คุณแน่ใจแล้วเหรอ? ว่าต้องการบล็อกบัญชีนี้" -unblockConfirm: "คุณแน่ใจแล้วเหรอ? ว่าต้องการปลดบล็อคบัญชีนี้" -suspendConfirm: "แน่ใจว่าคุณต้องการระงับบัญชีนี้?" -unsuspendConfirm: "นายแน่ใจแล้วหรอ? ว่าต้องการยกเลิกการระงับบัญชีนี้" -selectList: "เลือกรายการ" -editList: "แก้ไขรายการ" -selectChannel: "เลือกแชนแนล" +blockConfirm: "ต้องการบล็อกบัญชีนี้?" +unblockConfirm: "ต้องการปลดบล็อคบัญชีนี้?" +suspendConfirm: "ต้องการระงับบัญชีนี้?" +unsuspendConfirm: "ต้องการยกเลิกการระงับบัญชีนี้?" +selectList: "เลือกรายชื่อ" +editList: "แก้ไขรายชื่อ" +selectChannel: "เลือกช่อง" selectAntenna: "เลือกเสาอากาศ" editAntenna: "แก้ไขเสาอากาศ" selectWidget: "เลือกวิดเจ็ต" editWidgets: "แก้ไขวิดเจ็ต" editWidgetsExit: "เรียบร้อย" -customEmojis: "กำหนดอีโมจิเอง" -emoji: "อีโมจิ" +customEmojis: "เอโมจิที่กำหนดเอง" +emoji: "เอโมจิ" emojis: "อีโมจิ" -emojiName: "ชื่ออิโมจิ" -emojiUrl: "อิโมจิ URL" -addEmoji: "แทรกอีโมจิ" +emojiName: "ชื่อเอโมจิ" +emojiUrl: "URL ของเอโมจิ" +addEmoji: "แทรกเอโมจิ" settingGuide: "การตั้งค่าที่แนะนำ" cacheRemoteFiles: "แคชไฟล์ระยะไกล" cacheRemoteFilesDescription: "เมื่อปิดใช้งานการตั้งค่านี้ ไฟล์ระยะไกลนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกล แต่กรณีการปิดใช้งานนี้จะช่วยลดปริมาณการใช้พื้นที่จัดเก็บข้อมูล แต่เพิ่มปริมาณการใช้งาน เพราะเนื่องจากจะไม่มีการสร้างภาพขนาดย่อ" youCanCleanRemoteFilesCache: "คุณสามารถล้างแคชได้โดยคลิกที่ปุ่ม 🗑️ ในมุมมองการจัดการไฟล์" -cacheRemoteSensitiveFiles: "ไฟล์ระยะไกลที่มีความละเอียดอ่อนแคช" -cacheRemoteSensitiveFilesDescription: "เมื่อปิดการใช้งานแล้วการตั้งค่านี้ ไฟล์รีโมตที่มีความละเอียดอ่อนนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกลโดยที่ไม่มีการแคช" +cacheRemoteSensitiveFiles: "แคชไฟล์ระยะไกลที่มีเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน" +cacheRemoteSensitiveFilesDescription: "เมื่อปิดการใช้งานการตั้งค่านี้ ไฟล์ระยะไกลที่มีเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนนั้นจะถูกโหลดโดยตรงจากอินสแตนซ์ระยะไกลโดยที่ไม่มีการแคช" flagAsBot: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นบอท" flagAsBotDescription: "การเปิดใช้งานตัวเลือกนี้หากบัญชีนี้ถูกควบคุมโดยนักเขียนโปรแกรม หรือ ถ้าหากเปิดใช้งาน มันจะทำหน้าที่เป็นแฟล็กสำหรับนักพัฒนารายอื่นๆ และเพื่อป้องกันการโต้ตอบแบบไม่มีที่สิ้นสุดกับบอทตัวอื่นๆ และยังสามารถปรับเปลี่ยนระบบภายในของ Misskey เพื่อปฏิบัติต่อบัญชีนี้เป็นบอท" -flagAsCat: "ทำเครื่องหมายบอกว่าบัญชีนี้เป็นแมว" -flagAsCatDescription: "การเปิดใช้งานตัวเลือกนี้เพื่อทำเครื่องหมายบอกว่าบัญชีนี้เป็นแมว" +flagAsCat: "เมี้ยววววววว!!!!!!!!!!! (ทำเครื่องหมายว่าบัญชีนี้เป็นแมว)" +flagAsCatDescription: "เหมียวเหมียวเมี้ยว??" flagShowTimelineReplies: "แสดงตอบกลับ ในไทม์ไลน์" flagShowTimelineRepliesDescription: "แสดงการตอบกลับของผู้ใช้งานไปยังโน้ตของผู้ใช้งานรายอื่นๆในไทม์ไลน์หากได้เปิดเอาไว้" autoAcceptFollowed: "อนุมัติคำขอติดตามโดยอัตโนมัติทันที จากผู้ใช้งานที่คุณกำลังติดตาม" @@ -171,20 +178,20 @@ reloadAccountsList: "รีโหลดรายการบัญชีให loginFailed: "การเข้าสู่ระบบไม่สำเร็จ" showOnRemote: "ดูบนอินสแตนซ์ระยะไกล" general: "ทั่วไป" -wallpaper: "วอลล์เปเปอร์" -setWallpaper: "ตั้งวอลเปเปอร์" -removeWallpaper: "นำวอลเปเปอร์ออก" +wallpaper: "ภาพพื้นหลัง" +setWallpaper: "ตั้งค่าภาพพื้นหลัง" +removeWallpaper: "น้ำภาพพื้นหลังออก" searchWith: "ค้นหา: {q}" -youHaveNoLists: "คุณไม่มีลิสต์ใด ๆ " -followConfirm: "คุณแน่ใจแล้วหรอว่าต้องการที่จะติดตาม {name}?" -proxyAccount: "บัญชี พร็อกซี่" +youHaveNoLists: "คุณไม่มีรายชื่อใดๆ " +followConfirm: "ต้องการติดตาม {name} ใช่ไหม?" +proxyAccount: "บัญชีพร็อกซี่" proxyAccountDescription: "บัญชีพร็อกซี่ คือ บัญชีที่จะทำหน้าที่เป็นผู้ติดตามระยะไกลสำหรับผู้ใช้งานที่อยู่ภายใต้ด้วยเงื่อนไขบางอย่าง ยกตัวอย่าง เช่น เมื่อมีผู้ใช้งานนั้นได้เพิ่มผู้ใช้งานจากระยะไกลลงในรายการ แต่กิจกรรมของผู้ใช้ในระยะไกลนั้นจะไม่ถูกส่งไปยังอินสแตนซ์หากไม่มีผู้ใช้งานในพื้นที่ติดตามผู้ใช้รายนั้น ดังนั้นบัญชีพร็อกซีนี้จะติดตามแทน" host: "โฮสต์" selectUser: "เลือกผู้ใช้งาน" recipient: "ผู้รับ" annotation: "ความคิดเห็น" -federation: "เฟดิเวิร์ส" -instances: "Server" +federation: "สหพันธ์" +instances: "อินสแตนซ์" registeredAt: "จดทะเบียนที่" latestRequestReceivedAt: "ได้รับคำขอล่าสุดไปแล้ว" latestStatus: "สถานะล่าสุด" @@ -194,33 +201,34 @@ perHour: "ทุกชั่วโมง" perDay: "ต่อวัน" stopActivityDelivery: "หยุดส่งกิจกรรม" blockThisInstance: "บล็อกอินสแตนซ์นี้" -silenceThisInstance: "ปกปิดอินสแตนซ์นี้" +silenceThisInstance: "ปิดปากอินสแตนซ์นี้" operations: "ดำเนินการ" software: "ซอฟต์แวร์" version: "เวอร์ชั่น" metadata: "Metadata" -withNFiles: "{n} ไฟล์(s)" +withNFiles: "{n} ไฟล์" monitor: "มอนิเตอร์" jobQueue: "คิวงาน" cpuAndMemory: "ซีพียู และ หน่วยความจำ" -network: "เน็ตเวิร์ก" +network: "เครือข่าย" disk: "ดิสก์" -instanceInfo: "ข้อมูล อินสแตนซ์" +instanceInfo: "ข้อมูลอินสแตนซ์" statistics: "สถิติการใช้งาน" clearQueue: "ล้างคิว" clearQueueConfirmTitle: "คุณแน่ใจแล้วหรอว่าต้องการที่จะล้างคิว?" -clearQueueConfirmText: "บันทึกย่อที่ยังไม่ได้ส่งที่เหลืออยู่ในคิวนั้นมักจะ ไม่ถูกรวมเข้าด้วยกัน โดยปกติแล้วไม่จำเป็นต้องดำเนินการนี้" +clearQueueConfirmText: "โพสต์ที่ยังค้างในคิวจะไม่ถูกจัดส่งอีกต่อไป โดยปกติแล้วการดำเนินการนี้ไม่จำเป็น" clearCachedFiles: "ล้างแคช" -clearCachedFilesConfirm: "นายแน่ใจแล้วหรอว่าต้องการที่จะลบไฟล์ระยะไกลที่แคชไว้ทั้งหมด?" -blockedInstances: "อินสแตนซ์ที่ ถูกบล็อก" +clearCachedFilesConfirm: "ต้องการลบไฟล์ระยะไกลที่แคชไว้ทั้งหมด?" +blockedInstances: "อินสแตนซ์ที่ถูกบล็อก" blockedInstancesDescription: "ระบุชื่อโฮสต์ของอินสแตนซ์ที่คุณต้องการบล็อก อินสแตนซ์ที่อยู่ในรายการนั้นจะไม่สามารถพูดคุยกับอินสแตนซ์นี้ได้อีกต่อไป" -silencedInstances: "ปกปิดอินสแตนซ์นี้แล้ว" +silencedInstances: "ปิดปากอินสแตนซ์นี้แล้ว" +silencedInstancesDescription: "ตั้งค่ารายชื่อโฮสต์ของอินสแตนซ์ที่คุณต้องการปิดปาก บัญชีทั้งหมดของอินสแตนซ์ที่อยู่ในรายชื่อนั้นๆ จะถือว่าถูกปิดปากเช่นกัน ทำได้เฉพาะคำขอติดตามเท่านั้น และไม่สามารถกล่าวถึงบัญชีในพื้นที่ได้หากไม่ได้ติดตาม | สิ่งนี้ไม่มีผลต่ออินสแตนซ์ที่ถูกบล็อก" muteAndBlock: "ปิดเสียงและบล็อก" mutedUsers: "ผู้ใช้ที่ถูกปิดเสียง" blockedUsers: "ผู้ใช้ที่ถูกบล็อก" noUsers: "ไม่พบผู้ใช้งาน" editProfile: "แก้ไขโปรไฟล์" -noteDeleteConfirm: "นายแน่ใจแล้วหรอว่าต้องการลบโน้ตนี้นะ?" +noteDeleteConfirm: "ต้องการลบโน้ตนี้?" pinLimitExceeded: "คุณไม่สามารถปักหมุดโน้ตเพิ่มเติมใดๆได้อีก" intro: "การติดตั้ง Misskey เสร็จสิ้นแล้วนะ! โปรดสร้างผู้ใช้งานที่เป็นผู้ดูแลระบบ" done: "เสร็จสิ้น" @@ -228,7 +236,7 @@ processing: "กำลังประมวลผล..." preview: "แสดงตัวอย่าง" default: "ค่าเริ่มต้น" defaultValueIs: "ค่าเริ่มต้น: {value}" -noCustomEmojis: "ไม่มีอีโมจิ" +noCustomEmojis: "ไม่มีเอโมจิ" noJobs: "ไม่มีชิ้นงาน" federating: "สหพันธ์" blocked: "ถูกบล็อก" @@ -237,17 +245,17 @@ all: "ทั้งหมด" subscribing: "สมัครแล้ว" publishing: "กำลังเผยแพร่" notResponding: "ไม่มีการตอบสนอง" -instanceFollowing: "กำลังติดตาม บน อินสแตนซ์" +instanceFollowing: "กำลังติดตามบนอินสแตนซ์" instanceFollowers: "ผู้ติดตามของอินสแตนซ์" instanceUsers: "ผู้ใช้งานของอินสแตนซ์นี้" changePassword: "เปลี่ยนรหัสผ่าน" security: "ความปลอดภัย" -retypedNotMatch: "อินพุตไม่ตรงกันนะ" +retypedNotMatch: "ทั้งสองป้อนข้อมูลไม่สอดคล้องกัน" currentPassword: "รหัสผ่านปัจจุบัน" newPassword: "รหัสผ่านใหม่" newPasswordRetype: "ใส่รหัสผ่านใหม่อีกครั้ง" attachFile: "แนบไฟล์" -more: "เพิ่มเติม" +more: "เพิ่มเติม!" featured: "ไฮไลท์" usernameOrUserId: "ชื่อผู้ใช้หรือรหัสผู้ใช้งาน" noSuchUser: "ไม่พบผู้ใช้" @@ -256,23 +264,24 @@ announcements: "ประกาศ" imageUrl: "url รูปภาพ" remove: "ลบ" removed: "ถูกลบไปแล้ว" -removeAreYouSure: "นายแน่ใจจริงหรอว่าต้องการที่จะลบออก \"{x}\"" +removeAreYouSure: "ต้องการที่จะลบ “{x}” ออก?" deleteAreYouSure: "ต้องการลบ {x} หรือไม่คะ?" -resetAreYouSure: "รีเซ็ตเลยไหม" +resetAreYouSure: "รีเซ็ตเลยไหม?" +areYouSure: "แน่ใจแล้วใช่ไหมคะ?" saved: "บันทึกแล้ว" messaging: "แชท" -upload: "อัพโหลด" +upload: "อัปโหลด" keepOriginalUploading: "เก็บภาพต้นฉบับ" -keepOriginalUploadingDescription: "บันทึกรูปภาพที่อัพโหลดต้นฉบับตามที่เป็นอยู่ ถ้าหากปิดอยู่ ระบบจะสร้างเวอร์ชั่นที่จะแสดงบนเว็บเมื่ออัพโหลดนะ" +keepOriginalUploadingDescription: "เก็บภาพต้นฉบับไว้เมื่ออัปโหลดภาพ หากปิด รูปภาพสำหรับการเผยแพร่ทางเว็บจะถูกสร้างขึ้นในเบราว์เซอร์เมื่อทำการอัปโหลด" fromDrive: "จากไดรฟ์" fromUrl: "จาก URL" uploadFromUrl: "อัพโหลดจาก URL" uploadFromUrlDescription: "URL ของไฟล์ที่คุณต้องการอัปโหลด" -uploadFromUrlRequested: "อัพโหลดที่ร้องขอ" -uploadFromUrlMayTakeTime: "มันอาจจะต้องใช้เวลาสักครู่จนกว่าการอัพโหลดจะเสร็จสมบูรณ์นะ" +uploadFromUrlRequested: "ร้องขอการอัปโหลดแล้ว" +uploadFromUrlMayTakeTime: "การอัปโหลดอาจใช้เวลาสักครู่จึงจะเสร็จสมบูรณ์" explore: "สำรวจ" messageRead: "อ่านแล้ว" -noMoreHistory: "ในนั้นไม่มีประวัติอีกต่อไปแล้วนะ" +noMoreHistory: "ไม่มีประวัติเพิ่มเติม" startMessaging: "เริ่มการสนทนา" nUsersRead: "อ่านโดย {n}" agreeTo: "ฉันยอมรับที่จะ {0}" @@ -282,7 +291,7 @@ basicNotesBeforeCreateAccount: "หมายเหตุสำคัญ" termsOfService: "เงื่อนไขการให้บริการ" start: "เริ่มต้น​ใช้งาน​" home: "หน้าแรก" -remoteUserCaution: "เนื่องจากผู้ใช้งานรายนี้นั้น มาจากอินสแตนซ์ระยะไกล ข้อมูลที่แสดงดังกล่าวนั้นอาจจะไม่สมบูรณ์ก็ได้นะ" +remoteUserCaution: "ข้อมูลอาจไม่สมบูรณ์เนื่องจากผู้ใช้รายนี้มาจากอินสแตนซ์ระยะไกล" activity: "กิจกรรม" images: "รูปภาพ" image: "รูปภาพ" @@ -291,13 +300,13 @@ yearsOld: "{age} ปี" registeredDate: "วันที่สมัครสมาชิก" location: "ตำแหน่งที่ตั้ง" theme: "ธีม" -themeForLightMode: "ธีมที่จะใช้ในโหมดแสง" +themeForLightMode: "ธีมที่จะใช้ในโหมดสว่าง" themeForDarkMode: "ธีมที่จะใช้ในโหมดมืด" light: "สว่าง" dark: "มืด" lightThemes: "ธีมสว่าง" darkThemes: "ธีมมืด" -syncDeviceDarkMode: "ซิงค์โหมดมืดด้วยการตั้งค่ากับอุปกรณ์" +syncDeviceDarkMode: "ซิงค์โหมดมืดกับการตั้งค่าอุปกรณ์ของคุณ" drive: "ไดรฟ์" fileName: "ชื่อไฟล์" selectFile: "เลือกไฟล์" @@ -305,24 +314,25 @@ selectFiles: "เลือกไฟล์" selectFolder: "เลือกโฟลเดอร์" selectFolders: "เลือกโฟลเดอร์" renameFile: "เปลี่ยนชื่อไฟล์" -folderName: "ชื่อแฟ้ม" +folderName: "ชื่อโฟลเดอร์" createFolder: "สร้างโฟลเดอร์" renameFolder: "เปลี่ยนชื่อโฟลเดอร์" deleteFolder: "ลบโฟลเดอร์" +folder: "โฟลเดอร์" addFile: "เพิ่มไฟล์" emptyDrive: "ไดรฟ์ของคุณว่างเปล่านะ" emptyFolder: "โฟลเดอร์นี้ว่างเปล่า" unableToDelete: "ไม่สามารถลบออกได้" -inputNewFileName: "ป้อนชื่อไฟล์ใหม่นะ" +inputNewFileName: "ป้อนชื่อไฟล์ใหม่" inputNewDescription: "กรุณาใส่แคปชั่นใหม่" -inputNewFolderName: "กรุณาใส่ชื่อโฟลเดอร์ใหม่นะ\n" -circularReferenceFolder: "โฟลเดอร์ปลายทาง คือ โฟลเดอร์ย่อยของโฟลเดอร์ที่คุณต้องการที่จะย้ายล่ะนะ" -hasChildFilesOrFolders: "เนื่องจากโฟลเดอร์นี้ไม่ว่างเปล่า จึงไม่สามารถลบได้นะ" +inputNewFolderName: "กรุณาใส่ชื่อโฟลเดอร์ใหม่" +circularReferenceFolder: "โฟลเดอร์ปลายทางคือโฟลเดอร์ย่อยของโฟลเดอร์ที่คุณกำลังย้าย" +hasChildFilesOrFolders: "เนื่องจากโฟลเดอร์นี้ไม่ว่างเปล่า จึงไม่สามารถลบ" copyUrl: "คัดลอก URL" rename: "เปลี่ยนชื่อ" avatar: "ไอคอน" banner: "แบนเนอร์" -displayOfSensitiveMedia: "แสดงผลสื่อละเอียดอ่อน" +displayOfSensitiveMedia: "แสดงสื่อที่มีเนื้อหาละเอียดอ่อน" whenServerDisconnected: "สูญเสียการเชื่อมต่อกับเซิร์ฟเวอร์" disconnectedFromServer: "ถูกตัดการเชื่อมต่อออกจากเซิร์ฟเวอร์" reload: "รีโหลด" @@ -333,10 +343,10 @@ unwatch: "หยุดดู" accept: "ยอมรับ" reject: "ปฏิเสธ" normal: "ปกติ" -instanceName: "ชื่อ อินสแตนซ์" +instanceName: "ชื่ออินสแตนซ์" instanceDescription: "คำอธิบายอินสแตนซ์" maintainerName: "ผู้ดูแล" -maintainerEmail: "อีเมล์แอดมิน" +maintainerEmail: "อีเมลผู้ดูแลระบบ" tosUrl: "เงื่อนไขการให้บริการ URL" thisYear: "ปีนี้" thisMonth: "เดือนนี้" @@ -344,7 +354,7 @@ today: "วันนี้" dayX: "{day}" monthX: "เดือน {month}" yearX: "{year}" -pages: "หน้า" +pages: "หน้าเพจ" integration: "รวบรวม" connectService: "เชื่อมต่อ" disconnectService: "ตัดการเชื่อมต่อ" @@ -353,7 +363,7 @@ enableGlobalTimeline: "เปิดใช้งานไทม์ไลน์ท disablingTimelinesInfo: "ผู้ดูแลระบบและผู้ควบคุมจะสามารถเข้าถึงไทม์ไลน์ทั้งหมด ถึงแม้ว่าจะไม่ได้เปิดใช้งานก็ตาม" registration: "ลงทะเบียน" enableRegistration: "เปิดใช้งานการลงทะเบียนผู้ใช้ใหม่" -invite: "เชิญชวน" +invite: "คำเชิญ" driveCapacityPerLocalAccount: "ความจุของไดรฟ์ต่อผู้ใช้ภายในเครื่อง" driveCapacityPerRemoteAccount: "ความจุของไดรฟ์ต่อผู้ใช้ระยะไกล" inMb: "เป็นเมกะไบต์" @@ -361,15 +371,20 @@ bannerUrl: "URL รูปภาพแบนเนอร์" backgroundImageUrl: "URL ภาพพื้นหลัง" basicInfo: "ข้อมูลเบื้องต้น" pinnedUsers: "ผู้ใช้งานที่ได้รับการปักหมุด" -pinnedUsersDescription: "ลิสต์ชื่อผู้ใช้โดยคั่นด้วยการขึ้นบรรทัดใหม่เพื่อปักหมุดในแท็บ \"สำรวจ\"" -pinnedPages: "หน้าที่ปักหมุด" -pinnedPagesDescription: "ป้อนเส้นทางของหน้าที่คุณต้องการตรึงไว้ที่หน้าแรกของอินสแตนซ์นี้ โดยคั่นด้วยตัวแบ่งบรรทัด" +pinnedUsersDescription: "ป้อนชื่อผู้ใช้ที่คุณต้องการปักหมุดในหน้า “ค้นพบ” ฯลฯ คั่นด้วยการขึ้นบรรทัดใหม่" +pinnedPages: "หน้าเพจที่ปักหมุด" +pinnedPagesDescription: "ป้อนเส้นทางของหน้าเพจที่คุณต้องการปักหมุดไว้ที่หน้าแรกของอินสแตนซ์นี้ คั่นด้วยขึ้นบรรทัดใหม่" pinnedClipId: "ID ของคลิปที่จะปักหมุด" pinnedNotes: "โน้ตที่ปักหมุดไว้" hcaptcha: "hCaptcha" enableHcaptcha: "เปิดใช้ hCaptcha" hcaptchaSiteKey: "คีย์ไซต์" hcaptchaSecretKey: "คีย์ลับ" +mcaptcha: "mCaptcha" +enableMcaptcha: "เปิดใช้ mCaptcha" +mcaptchaSiteKey: "คีย์ไซต์" +mcaptchaSecretKey: "คีย์ลับ" +mcaptchaInstanceUrl: "URL ของอินสแตนซ์ของ mCaptcha" recaptcha: "reCAPTCHA" enableRecaptcha: "เปิดใช้ reCAPTCHA" recaptchaSiteKey: "คีย์ไซต์" @@ -385,26 +400,26 @@ name: "ชื่อ" antennaSource: "แหล่งเสาอากาศ" antennaKeywords: "คีย์เวิร์ดที่ควรฟัง" antennaExcludeKeywords: "คีย์เวิร์ดที่จะยกเว้น" -antennaKeywordsDescription: "คั่นด้วยช่องว่างสำหรับเงื่อนไข AND หรือด้วยการขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR นะ" +antennaKeywordsDescription: "คั่นด้วยช่องว่างสำหรับเงื่อนไข AND หรือด้วยการขึ้นบรรทัดใหม่สำหรับเงื่อนไข OR" notifyAntenna: "แจ้งเตือนเกี่ยวกับโน้ตใหม่" withFileAntenna: "เฉพาะโน้ตที่มีไฟล์" -enableServiceworker: "เปิดใช้งาน การแจ้งเตือนแบบพุชสำหรับเบราว์เซอร์ของคุณ" +enableServiceworker: "เปิดใช้งานการแจ้งเตือนแบบพุชไปยังเบราว์เซอร์ของคุณ" antennaUsersDescription: "ระบุหนึ่งชื่อผู้ใช้ต่อบรรทัด" -caseSensitive: "กรณีที่สำคัญ" +caseSensitive: "อักษรพิมพ์ใหญ่-พิมพ์เล็กความหมายต่างกัน" withReplies: "รวมตอบกลับ" connectedTo: "บัญชีดังต่อไปนี้มีการเชื่อมต่อกัน" notesAndReplies: "โพสต์และการตอบกลับ" withFiles: "รวบรวมไฟล์" silence: "ถูกปิดปาก" -silenceConfirm: "นายแน่ใจแล้วหรอว่าต้องการที่จะ ปิดปาก ผู้ใช้งานรายนี้?" +silenceConfirm: "ต้องการที่จะ ปิดปาก ผู้ใช้รายนี้?" unsilence: "ยกเลิกการปิดปาก" -unsilenceConfirm: "นายแน่ใจแล้วหรอว่าต้องการที่จะยกเลิกปิดปากผู้ใช้งานรายนี้?" +unsilenceConfirm: "ต้องการยกเลิกปิดปากผู้ใช้รายนี้?" popularUsers: "ผู้ใช้ที่เป็นที่นิยม" recentlyUpdatedUsers: "ผู้ใช้ที่เพิ่งใช้งานล่าสุด" recentlyRegisteredUsers: "ผู้ใช้ที่เข้าร่วมใหม่" recentlyDiscoveredUsers: "ผู้ใช้ที่เพิ่งค้นพบใหม่" exploreUsersCount: "มีผู้ใช้ {จำนวน} ราย" -exploreFediverse: "สำรวจเฟดดิเวิร์ส" +exploreFediverse: "สำรวจสหพันธ์" popularTags: "แท็กยอดนิยม" userList: "ลิสต์" about: "เกี่ยวกับ" @@ -419,7 +434,7 @@ moderator: "ผู้ควบคุม" moderation: "การกลั่นกรอง" moderationNote: "โน้ตการกลั่นกรอง" addModerationNote: "เพิ่มโน้ตการกลั่นกรอง" -moderationLogs: "บันทึกการกลั่นกรอง" +moderationLogs: "ปูมการแก้ไข" nUsersMentioned: "กล่าวถึงโดยผู้ใช้ {n} รายนี้" securityKeyAndPasskey: "ความปลอดภัยและรหัสผ่าน" securityKey: "กุญแจความปลอดภัย" @@ -429,11 +444,11 @@ unregister: "เลิกติดตาม" passwordLessLogin: "เข้าสู่ระบบแบบไม่ใช้รหัสผ่าน" passwordLessLoginDescription: "อนุญาตให้เข้าสู่ระบบโดยไม่ต้องใช้รหัสผ่านโดยใช้รหัสรักษาความปลอดภัยหรือรหัสผ่านเท่านั้น" resetPassword: "รีเซ็ตรหัสผ่าน" -newPasswordIs: "รหัสผ่านใหม่คือ \"{password}\"" +newPasswordIs: "รหัสผ่านใหม่คือ “{password}”" reduceUiAnimation: "ลดภาพเคลื่อนไหว UI" -share: "แชร์" +share: "แบ่งปัน" notFound: "ไม่พบหน้าที่ต้องการ" -notFoundDescription: "ไม่พบหน้าที่สอดคล้องตรงกันกับ URL นี้นะ" +notFoundDescription: "ไม่พบหน้าตาม URL ที่ระบุ" uploadFolder: "โฟลเดอร์เริ่มต้นสำหรับอัพโหลด" markAsReadAllNotifications: "ทำเครื่องหมายการแจ้งเตือนทั้งหมดว่าอ่านแล้ว" markAsReadAllUnreadNotes: "ทำเครื่องหมายโน้ตทั้งหมดว่าอ่านแล้ว" @@ -441,7 +456,7 @@ markAsReadAllTalkMessages: "ทำเครื่องหมายข้อค help: "ช่วยเหลือ" inputMessageHere: "พิมพ์ข้อความที่นี่" close: "ปิด" -invites: "เชิญชวน" +invites: "คำเชิญ" members: "สมาชิก" transfer: "ถ่ายโอน" title: "หัวข้อ" @@ -451,12 +466,12 @@ next: "ถัด​ไป" retype: "พิมพ์รหัสอีกครั้ง" noteOf: "โน้ต โดย {user}" quoteAttached: "อ้างอิง" -quoteQuestion: "นายต้องการที่จะอ้างอิงหรอ?" -noMessagesYet: "ยังไม่มีข้อความนะ" +quoteQuestion: "ต้องการที่จะแนบมันเพื่ออ้างอิงใช่ไหม?" +noMessagesYet: "ยังไม่มีข้อความ" newMessageExists: "คุณมีข้อความใหม่" -onlyOneFileCanBeAttached: "คุณสามารถแนบไฟล์กับข้อความได้เพียงไฟล์เดียวเท่านั้นนะ" -signinRequired: "กรุณาลงทะเบียนหรือลงชื่อเข้าใช้ก่อนดำเนินการต่อนะ" -invitations: "เชิญชวน" +onlyOneFileCanBeAttached: "สามารถแนบไฟล์ได้เพียงไฟล์เดียวต่อ 1 ข้อความ" +signinRequired: "กรุณาลงทะเบียนหรือลงชื่อเข้าใช้ก่อนดำเนินการต่อ" +invitations: "คำเชิญ" invitationCode: "รหัสคำเชิญ" checking: "Checking" available: "พร้อมใช้งาน" @@ -475,11 +490,11 @@ or: "หรือ" language: "ภาษา" uiLanguage: "ภาษาอินเทอร์เฟซผู้ใช้งาน" aboutX: "เกี่ยวกับ {x}" -emojiStyle: "สไตล์อิโมจิ" +emojiStyle: "สไตล์เอโมจิ" native: "ภาษาแม่" disableDrawer: "อย่าใช้ลิ้นชักสไตล์เมนู" showNoteActionsOnlyHover: "แสดงการดำเนินการเฉพาะโน้ตเมื่อโฮเวอร์" -noHistory: "ไม่มีรายการ" +noHistory: "ไม่มีประวัติ" signinHistory: "ประวัติการเข้าสู่ระบบ" enableAdvancedMfm: "เปิดใช้งาน MFM ขั้นสูง" enableAnimatedMfm: "เปิดการใช้งาน MFM ด้วยแอนิเมชั่น" @@ -491,7 +506,7 @@ createAccount: "สร้างบัญชี" existingAccount: "บัญชีที่มีอยู่" regenerate: "สร้างอีกครั้ง" fontSize: "ขนาดตัวอักษร" -mediaListWithOneImageAppearance: "ความสูงของลิสต์สื่อจะต้องมีรูปภาพเดียวเท่านั้น" +mediaListWithOneImageAppearance: "ความสูงของรายการสื่อที่มีเพียงรูปเดียว" limitTo: "จำกัดไว้ที่ {x}" noFollowRequests: "คุณไม่มีคำขอติดตามที่รอดำเนินการ" openImageInNewTab: "เปิดรูปภาพในแท็บใหม่" @@ -509,14 +524,14 @@ promote: "โปรโมท" numberOfDays: "จำนวนวัน" hideThisNote: "ซ่อนโน้ตนี้" showFeaturedNotesInTimeline: "แสดงโน้ตเด่นในไทม์ไลน์" -objectStorage: "อ็อบเจ็กต์ ที่จัดเก็บ" -useObjectStorage: "ใช้ อ็อบเจ็กต์ ที่จัดเก็บ" -objectStorageBaseUrl: "URL ฐาน" +objectStorage: "การจัดเก็บในรูปแบบอ็อบเจกต์" +useObjectStorage: "ใช้การจัดเก็บในรูปแบบอ็อบเจกต์" +objectStorageBaseUrl: "Base URL" objectStorageBaseUrlDesc: "URL ที่ใช้เป็นข้อมูลอ้างอิง ระบุ URL ของ CDN หรือ Proxy ถ้าหากคุณใช้อย่างใดอย่างหนึ่ง\n สำหรับการใช้งาน S3 'https://.s3.amazonaws.com' และสำหรับ GCS หรือบริการที่เทียบเท่าใช้ 'https://storage.googleapis.com/', เป็นต้น" objectStorageBucket: "Bucket" objectStorageBucketDesc: "โปรดระบุชื่อที่เก็บข้อมูลที่ใช้กับผู้ให้บริการของคุณ" objectStoragePrefix: "คำนำหน้า" -objectStoragePrefixDesc: "ไฟล์ทั้งหมดจะถูกเก็บไว้ภายใต้ไดเร็กทอรีที่มีคำนำหน้านี้นะ" +objectStoragePrefixDesc: "ไฟล์ทั้งหมดจะถูกเก็บไว้ภายใต้ไดเร็กทอรีที่มีคำนำหน้านี้" objectStorageEndpoint: "ปลายทาง" objectStorageEndpointDesc: "เว้นว่างไว้หากคุณใช้ AWS S3 หรือระบุปลายทางเป็น '' หรือ ':' ทั้งนี้ขึ้นอยู่กับผู้ให้บริการที่คุณใช้อยู่ด้วย" objectStorageRegion: "ภูมิภาค" @@ -525,12 +540,13 @@ objectStorageUseSSL: "ใช้ SSL" objectStorageUseSSLDesc: "ปิดการทำงานนี้ไว้ ถ้าหากคุณจะไม่ใช้ HTTPS สำหรับการเชื่อมต่อ API" objectStorageUseProxy: "เชื่อมต่อผ่านพร็อกซี" objectStorageUseProxyDesc: "ปิดสิ่งนี้ไว้ถ้าหากคุณจะไม่ใช้ Proxy สำหรับการเชื่อมต่อ API" -objectStorageSetPublicRead: "ตั้งค่า \"public-read\" ในการอัปโหลด" +objectStorageSetPublicRead: "ตั้งค่าเป็น “public-read” เมื่ออัปโหลด" s3ForcePathStyleDesc: "ถ้าหากเปิดใช้งาน s3ForcePathStyle ชื่อบัคเก็ตนั้นอาจจะต้องรวมอยู่ในเส้นทางของ URL ซึ่งตรงข้ามกับชื่อโฮสต์ของ URL คุณอาจจะต้องเปิดใช้งานการตั้งค่านี้เมื่อใช้บริการต่างๆ เช่น อินสแตนซ์ Minio ที่โฮสต์เองนะ" -serverLogs: "บันทึกของเซิร์ฟเวอร์" +serverLogs: "ปูมของเซิร์ฟเวอร์" deleteAll: "ลบทั้งหมด" showFixedPostForm: "แสดงแบบฟอร์มการโพสต์ที่ด้านบนสุดของไทม์ไลน์" -showFixedPostFormInChannel: "แสดงแบบฟอร์มกำลังโพสต์ที่ด้านบนของไทม์ไลน์ (แชนแนล)" +showFixedPostFormInChannel: "แสดงแบบฟอร์มการโพสต์ที่ด้านบนของไทม์ไลน์ (ช่อง)" +withRepliesByDefaultForNewlyFollowed: "แสดงการตอบกลับจากผู้ใช้ที่คุณเพิ่งติดตามลงไทม์ไลน์ตามค่าเริ่มต้น" newNoteRecived: "มีโน้ตใหม่" sounds: "เสียง" sound: "เสียง" @@ -538,10 +554,12 @@ listen: "ฟัง" none: "ไม่มี" showInPage: "แสดงในเพจ" popout: "ป๊อปเอาต์" -volume: "ความดัง" -masterVolume: "มาสเตอร์วอลุ่ม" +volume: "ระดับเสียง" +masterVolume: "ระดับเสียงหลัก" +notUseSound: "ไม่ใช้เสียง" +useSoundOnlyWhenActive: "มีเสียงออกเฉพาะเมื่อ Misskey ทำงานอยู่" details: "รายละเอียด" -chooseEmoji: "เลือกโมจิของเธอ" +chooseEmoji: "เลือกเอโมจิ" unableToProcess: "ไม่สามารถดำเนินการให้เสร็จสิ้นได้" recentUsed: "ใช้ล่าสุด" install: "ติดตั้ง" @@ -554,20 +572,20 @@ state: "สถานะ" sort: "เรียงลำดับ" ascendingOrder: "เรียงจากน้อยไปมาก" descendingOrder: "เรียงจากมากไปน้อย" -scratchpad: "กระดานทดลอง" +scratchpad: "Scratchpad" scratchpadDescription: "Scratchpad เป็นการจัดเตรียมสภาพแวดล้อมสำหรับการทดลอง AiScript แต่คุณสามารถเขียน ดำเนินการ และตรวจสอบผลลัพธ์ของการโต้ตอบกับ Misskey มันได้ด้วยนะ" output: "เอาท์พุต" script: "สคริปต์" disablePagesScript: "ปิดการใช้งาน AiScript บนเพจ" updateRemoteUser: "อัปเดตข้อมูลผู้ใช้งานระยะไกล" unsetUserAvatar: "เลิกตั้งอวตาร" -unsetUserAvatarConfirm: "คุณแน่ใจหรือไม่ว่าต้องการเลิกตั้งอวตาร?" +unsetUserAvatarConfirm: "ต้องการเลิกตั้งอวตาร?" unsetUserBanner: "เลิกตั้งแบนเนอร์" -unsetUserBannerConfirm: "คุณแน่ใจหรือไม่ว่าต้องการเลิกตั้งแบนเนอร์เลยมั้ย?" +unsetUserBannerConfirm: "ต้องการเลิกตั้งแบนเนอร์?" deleteAllFiles: "ลบไฟล์ทั้งหมด" -deleteAllFilesConfirm: "นายแน่ใจแล้วหรอว่าต้องการที่จะลบไฟล์ทั้งหมด?" +deleteAllFilesConfirm: "ต้องการลบไฟล์ทั้งหมดหรือไม่?" removeAllFollowing: "เลิกติดตามผู้ใช้ที่ติดตามทั้งหมด" -removeAllFollowingDescription: "การที่คุณดำเนินการนี้จะเลิกติดตามบัญชีทั้งหมดจาก {host} โปรดเรียกใช้คำสั่งสิ่งนี้หากต้องการยกเลิกอินสแตนซ์ เช่น ไม่มีอยู่แล้ว" +removeAllFollowingDescription: "เลิกติดตามทั้งหมดจาก {host} โปรดเรียกใช้สิ่งนี้เมื่ออินสแตนซ์ดังกล่าวได้สูญหายตายจากไปแล้ว" userSuspended: "ผู้ใช้รายนี้ถูกระงับการใช้งาน" userSilenced: "ผู้ใช้รายนี้กำลังถูกปิดกั้น" yourAccountSuspendedTitle: "บัญชีนี้นั้นถูกระงับ" @@ -606,7 +624,7 @@ preferencesBackups: "ตั้งค่าการสำรองข้อม deck: "เด็ค" undeck: "ออกจากเด็ค" useBlurEffectForModal: "ใช้เอฟเฟกต์เบลอสำหรับโมดอล" -useFullReactionPicker: "ใช้เครื่องมือเลือกปฏิกิริยาขนาดเต็ม" +useFullReactionPicker: "ใช้ตัวจิ้มรีแอคชั่นอย่างเต็มรูปแบบ" width: "ความกว้าง" height: "ความสูง" large: "ใหญ่" @@ -614,17 +632,18 @@ medium: "ปานกลาง" small: "เล็ก" generateAccessToken: "สร้างการเข้าถึงโทเค็น" permission: "การอนุญาต" +adminPermission: "สิทธิ์ของผู้ดูแลระบบ" enableAll: "เปิดใช้งานทั้งหมด" disableAll: "ปิดการใช้งานทั้งหมด" tokenRequested: "ให้สิทธิ์การเข้าถึงบัญชี" pluginTokenRequestedDescription: "ปลั๊กอินนี้จะสามารถใช้การอนุญาตที่ตั้งค่าไว้ที่นี่นะ" notificationType: "ประเภทการแจ้งเตือน" edit: "แก้ไข" -emailServer: "อีเมล์เซิร์ฟเวอร์" +emailServer: "อีเมลเซิร์ฟเวอร์" enableEmail: "เปิดใช้งานการกระจายอีเมล" emailConfigInfo: "ใช้เพื่อยืนยันอีเมลของคุณระหว่างการสมัครหรือถ้าหากคุณลืมรหัสผ่าน" -email: "อีเมล์" -emailAddress: "ที่อยู่อีเมล์" +email: "อีเมล" +emailAddress: "ที่อยู่อีเมล" smtpConfig: "กำหนดค่าเซิร์ฟเวอร์ SMTP" smtpHost: "โฮสต์" smtpPort: "พอร์ต" @@ -648,7 +667,7 @@ overview: "ภาพรวม" logs: "บันทึกข้อมูลระบบ" delayed: "ดีเลย์" database: "ฐานข้อมูล" -channel: "แชนแนล" +channel: "ช่อง" create: "สร้าง" notificationSetting: "ตั้งค่าการแจ้งเตือน" notificationSettingDesc: "เลือกประเภทการแจ้งเตือนที่ต้องการจะแสดง" @@ -657,6 +676,7 @@ useGlobalSettingDesc: "หากเปิดไว้ ระบบจะใช other: "อื่น ๆ" regenerateLoginToken: "สร้างโทเค็นการเข้าสู่ระบบอีกครั้ง" regenerateLoginTokenDescription: "สร้างโทเค็นใหม่ที่ใช้ภายในระหว่างการเข้าสู่ระบบ โดยตามหลักปกติแล้วการดำเนินการนี้ไม่จำเป็น หากสร้างใหม่ อุปกรณ์ทั้งหมดจะถูกออกจากระบบนะ" +theKeywordWhenSearchingForCustomEmoji: "คีย์เวิร์ดสำหรับใช้ค้นหาอีโมจิที่กำหนดเอง" setMultipleBySeparatingWithSpace: "คั่นหลายรายการด้วยช่องว่าง" fileIdOrUrl: "ไฟล์ ID หรือ URL" behavior: "พฤติกรรม" @@ -678,7 +698,7 @@ openInNewTab: "เปิดในแท็บใหม่" openInSideView: "เปิดในมุมมองด้านข้าง" defaultNavigationBehaviour: "พฤติกรรมการนำทางที่เป็นค่าเริ่มต้น" editTheseSettingsMayBreakAccount: "การแก้ไขการตั้งค่าเหล่านี้อาจทำให้บัญชีของคุณเสียหายนะ" -instanceTicker: "ข้อมูลอินสแตนซ์ของบันทึกย่อ" +instanceTicker: "ข้อมูลอินสแตนซ์ของโน้ต" waitingFor: "กำลังรอคอย {x}" random: "สุ่มค่า" system: "ระบบ" @@ -702,8 +722,8 @@ repliedCount: "จำนวนของการตอบกลับที่ renotedCount: "จำนวนรีโน้ตที่ได้รับแล้ว" followingCount: "จำนวนบัญชีที่ติดตาม" followersCount: "จำนวนผู้ติดตาม" -sentReactionsCount: "จำนวนปฏิกิริยาที่ส่ง" -receivedReactionsCount: "จำนวนปฏิกิริยาที่ได้รับ" +sentReactionsCount: "จำนวนรีแอคชั่นที่ส่ง" +receivedReactionsCount: "จำนวนรีแอคชั่นที่ได้รับ" pollVotesCount: "จำนวนโหวตที่ส่งไป" pollVotedCount: "จำนวนโหวตที่ได้รับ" yes: "ใช่" @@ -711,17 +731,17 @@ no: "ไม่" driveFilesCount: "จำนวนไฟล์ไดรฟ์" driveUsage: "การใช้พื้นที่ไดรฟ์" noCrawle: "ปฏิเสธการจัดทำดัชนีของโปรแกรมรวบรวมข้อมูล" -noCrawleDescription: "ขอให้เครื่องมือค้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ บันทึกย่อ หน้า ฯลฯ" +noCrawleDescription: "ขอให้เครื่องมือค้นหาไม่จัดทำดัชนีหน้าโปรไฟล์ โน้ต หน้าเพจ ฯลฯ" lockedAccountInfo: "เว้นแต่ว่าคุณจะต้องตั้งค่าการเปิดเผยโน้ตเป็น \"ผู้ติดตามเท่านั้น\" โน้ตย่อของคุณจะปรากฏแก่ทุกคน ถึงแม้ว่าคุณจะเป็นกำหนดให้ผู้ติดตามต้องได้รับการอนุมัติด้วยตนเองก็ตาม" -alwaysMarkSensitive: "ทำเครื่องหมายเป็น NSFW เป็นค่าเริ่มต้น" +alwaysMarkSensitive: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนเป็นค่าเริ่มต้น" loadRawImages: "โหลดภาพต้นฉบับแทนการแสดงภาพขนาดย่อ" disableShowingAnimatedImages: "ไม่ต้องเล่นภาพเคลื่อนไหว" -highlightSensitiveMedia: "ไฮไลท์สื่อที่ละเอียดอ่อน" +highlightSensitiveMedia: "ไฮไลท์สื่อที่มีเนื้อหาละเอียดอ่อน" verificationEmailSent: "ส่งอีเมลยืนยันแล้วนะ ได้โปรดกรุณาไปที่ลิงก์ที่รวมไว้เพื่อทำการตรวจสอบให้เสร็จสิ้น" notSet: "ไม่ได้ตั้งค่า" emailVerified: "อีเมลได้รับการยืนยันแล้ว" noteFavoritesCount: "จำนวนโน้ตที่ชื่นชอบ" -pageLikesCount: "จำนวนเพจที่ชอบ" +pageLikesCount: "จำนวนเพจที่ถูกใจ" pageLikedCount: "จำนวนการกดถูกใจเพจที่ได้รับแล้ว" contact: "ติดต่อ" useSystemFont: "ใช้ฟอนต์เริ่มต้นของระบบ" @@ -730,15 +750,15 @@ experimentalFeatures: "ฟังก์ชั่นทดสอบ" experimental: "ทดลอง" thisIsExperimentalFeature: "นี่คือฟีเจอร์ทดลองนะค่ะ ฟังก์ชันการทำงานบางอย่างอาจเปลี่ยนแปลงได้ และอาจไม่ทำงานหรือไม่เสถียรตามที่ตั้งใจไว้นะ" developer: "สำหรับนักพัฒนา" -makeExplorable: "ทำให้บัญชีมองเห็นใน \"สำรวจ\"" -makeExplorableDescription: "ถ้าหากคุณปิดการทำงานนี้ บัญชีของคุณนั้นจะไม่แสดงในส่วน \"สำรวจ\" นะ" +makeExplorable: "ทำให้บัญชีมองเห็นใน “สำรวจ”" +makeExplorableDescription: "ถ้าหากคุณปิดการทำงานนี้ บัญชีของคุณนั้นจะไม่แสดงในส่วน “สำรวจ”" showGapBetweenNotesInTimeline: "แสดงช่องว่างระหว่างโพสต์บนไทม์ไลน์" duplicate: "ทำซ้ำ" left: "ซ้าย" -center: "ศูนย์กลาง" +center: "กึ่งกลาง" wide: "กว้าง" narrow: "ชิด" -reloadToApplySetting: "การตั้งค่านี้จะมีผลหลังจากโหลดหน้าซ้ำเท่านั้น ต้องการที่จะโหลดใหม่เลยมั้ย" +reloadToApplySetting: "การตั้งค่านี้จะมีผลหลังจากโหลดหน้าซ้ำเท่านั้น ต้องการที่จะโหลดใหม่เลยไหม?" needReloadToApply: "จำเป็นต้องโหลดซ้ำถึงจะมีผลนะ" showTitlebar: "แสดงแถบชื่อ" clearCache: "ล้างแคช" @@ -772,21 +792,21 @@ inUse: "ใช้แล้ว" editCode: "แก้ไขโค้ด" apply: "ตกลง" receiveAnnouncementFromInstance: "รับการแจ้งเตือนจากอินสแตนซ์นี้" -emailNotification: "การแจ้งเตือนทางอีเมล์" +emailNotification: "การแจ้งเตือนทางอีเมล" publish: "เผยแพร่" inChannelSearch: "ค้นหาในช่อง" -useReactionPickerForContextMenu: "เปิดตัวเลือกปฏิกิริยาเมื่อคลิกขวา" -typingUsers: "{users} กำลัง/กำลังพิมพ์..." +useReactionPickerForContextMenu: "คลิกขวาเพื่อเปิดตัวจิ้มรีแอคชั่น" +typingUsers: "{users} กำลังพิมพ์..." jumpToSpecifiedDate: "ข้ามไปยังวันที่เฉพาะเจาะจง" showingPastTimeline: "กำลังแสดงผลไทม์ไลน์เก่า" clear: "ล้าง" markAllAsRead: "ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว" goBack: "ย้อนกลับ" -unlikeConfirm: "ลบไลค์ของคุณออกจริงๆหรอ" +unlikeConfirm: "เลิกถูกใจจริงๆ หรือ?" fullView: "มุมมองแบบเต็ม" quitFullView: "ออกจากมุมมองแบบเต็ม" addDescription: "เพิ่มคำอธิบาย" -userPagePinTip: "คุณสามารถแสดงผลโน้ตย่อได้ที่นี่โดยเลือก \"ปักหมุดที่โปรไฟล์\" จากเมนูของโน้ตย่อแต่ละรายการนะ" +userPagePinTip: "ปักหมุดโน้ตให้แสดงที่นี่ได้โดยเลือกเมนู “ปักหมุด” ของโน้ตนั้นๆ" notSpecifiedMentionWarning: "โน้ตนี้มีการกล่าวถึงผู้ใช้งานที่ไม่รวมอยู่ในผู้รับ" info: "เกี่ยวกับ" userInfo: "ข้อมูลผู้ใช้" @@ -797,7 +817,7 @@ hideOnlineStatusDescription: "การซ่อนสถานะออนไ online: "ออนไลน์" active: "ใช้งานอยู่" offline: "ออฟไลน์" -notRecommended: "ไม่ใช้งาน" +notRecommended: "ไม่แนะนำ" botProtection: "การป้องกัน Bot (or AI)" instanceBlocking: "อินสแตนซ์ที่ถูกบล็อก" selectAccount: "เลือกบัญชี" @@ -820,19 +840,19 @@ popularPosts: "โพสต์ติดอันดับ" shareWithNote: "แบ่งปันด้วยโน้ต" ads: "โฆษณา" expiration: "กำหนดเวลา" -startingperiod: "เริ่ม" -memo: "ข้อควรจำ" +startingperiod: "เริ่มเมื่อ" +memo: "เมโม" priority: "ลำดับความสำคัญ" high: "สูง" middle: "ปานกลาง" low: "ต่ำ" -emailNotConfiguredWarning: "ไม่ได้ตั้งค่าที่อยู่อีเมลนะ" +emailNotConfiguredWarning: "ยังไม่ได้ตั้งค่าที่อยู่อีเมล" ratio: "อัตราส่วน" previewNoteText: "แสดงตัวอย่าง" customCss: "CSS ที่กำหนดเอง" -customCssWarn: "ควรใช้การตั้งค่านี้เฉพาะต่อเมื่อคุณรู้ว่าการตั้งค่านี้ใช้ทำอะไร การป้อนค่าที่ไม่เหมาะสมอาจทำให้ไคลเอ็นต์หยุดทำงานตามปกติได้นะ" +customCssWarn: "ควรใช้การตั้งค่านี้เฉพาะต่อเมื่อคุณรู้มันใช้ทำอะไร การตั้งค่าที่ไม่เหมาะสมอาจทำให้ไคลเอ็นต์ไม่สามารถใช้งานได้อย่างถูกต้อง" global: "ทั่วโลก" -squareAvatars: "แสดงผลอวตารสี่เหลี่ยม" +squareAvatars: "แสดงผลอวตารเป็นสี่เหลี่ยม" sent: "ส่ง" received: "ได้รับแล้ว" searchResult: "ผลการค้นหา" @@ -849,10 +869,10 @@ usernameInfo: "ชื่อที่ระบุบัญชีของคุ aiChanMode: "โหมด Ai " devMode: "โหมดนักพัฒนา" keepCw: "เก็บคำเตือนเนื้อหา" -pubSub: "บัญชีผับ/ย่อย" +pubSub: "บัญชี Pub/Sub" lastCommunication: "การสื่อสารครั้งสุดท้ายล่าสุด" resolved: "คลี่คลายแล้ว" -unresolved: "รอการเฉลย" +unresolved: "ยังไม่ได้รับการแก้ไข" breakFollow: "ลบผู้ติดตาม" breakFollowConfirm: "ลบผู้ติดตามนี้ออกจริงหรอ?" itsOn: "เปิดใช้งาน" @@ -864,32 +884,34 @@ unread: "ไม่ได้อ่าน" filter: "กรอง" controlPanel: "แผงควบคุม" manageAccounts: "จัดการบัญชี" -makeReactionsPublic: "ตั้งค่าประวัติปฏิกิริยาต่อสาธารณะ" -makeReactionsPublicDescription: "การทำเช่นนี้จะทำให้รายการปฏิกิริยาที่ผ่านมาของคุณจะปรากฏต่อสาธารณะนะ" +makeReactionsPublic: "ตั้งค่าประวัติการรีแอคชั่นเป็นสาธารณะ" +makeReactionsPublicDescription: "การทำเช่นนี้จะทำให้รายการรีแอคชั่นของคุณที่ผ่านมาทั้งหมดปรากฏต่อสาธารณะ" classic: "คลาสสิค" muteThread: "ปิดเสียงเธรด" unmuteThread: "เปิดเสียงเธรด" +followingVisibility: "การมองเห็นที่เรากำลังติดตาม" +followersVisibility: "การมองเห็นผู้ที่กำลังติดตามเรา" continueThread: "ดูความต่อเนื่องเธรด" deleteAccountConfirm: "การดำเนินการนี้จะลบบัญชีของคุณอย่างถาวรเลยนะ แน่ใจหรอดำเนินการ?" incorrectPassword: "รหัสผ่านไม่ถูกต้อง" -voteConfirm: "ยืนยันการโหวต \"{choice}\" มั้ย?" +voteConfirm: "ยืนยันการโหวต “{choice}” ไหม?" hide: "ซ่อน" -useDrawerReactionPickerForMobile: "แสดงผล ตัวเลือกปฏิกิริยาเป็นลิ้นชักบนมือถือ" -welcomeBackWithName: "ยินดีต้อนรับการกลับมานะคะ, {name}" -clickToFinishEmailVerification: "กรุณาคลิก [{ok}] เพื่อดำเนินการยืนยันอีเมลให้เสร็จสมบูรณ์นะ" +useDrawerReactionPickerForMobile: "แสดง ตัวจิ้มรีแอคชั่น เป็นแบบลิ้นชัก เมื่อใช้บนมือถือ" +welcomeBackWithName: "ยินดีต้อนรับการกลับมานะคะ, คุณ{name}" +clickToFinishEmailVerification: "กรุณาคลิก [{ok}] เพื่อดำเนินการยืนยันอีเมลให้เสร็จสมบูรณ์" overridedDeviceKind: "ประเภทอุปกรณ์" smartphone: "สมาร์ทโฟน" tablet: "แท็บเล็ต" auto: "อัตโนมัติ" -themeColor: "อินสแตนซ์ Ticker Color" +themeColor: "สีธีม" size: "ขนาด" numberOfColumn: "จำนวนคอลัมน์" searchByGoogle: "ค้นหา" -instanceDefaultLightTheme: "ธีมสว่างค่าเริ่มต้นสำหรับอินสแตนซ์" -instanceDefaultDarkTheme: "ธีมมืดค่าเริ่มต้นอินสแตนซ์" +instanceDefaultLightTheme: "ธีมสว่างตามค่าเริ่มต้นของอินสแตนซ์" +instanceDefaultDarkTheme: "ธีมมืดตามค่าเริ่มต้นของอินสแตนซ์" instanceDefaultThemeDescription: "ป้อนรหัสธีมในรูปแบบออบเจ็กต์" mutePeriod: "ระยะเวลาปิดเสียง" -period: "สิ้นสุดการสำรวจความคิดเห็น" +period: "ระยะเวลา" indefinitely: "ตลอดไป" tenMinutes: "10 นาที" oneHour: "1 ชั่วโมง" @@ -919,9 +941,9 @@ deleteAccount: "ลบบัญชี" document: "เอกสาร" numberOfPageCache: "จำนวนหน้าเพจที่แคช" numberOfPageCacheDescription: "การเพิ่มจำนวนนี้จะช่วยเพิ่มความสะดวกให้กับผู้ใช้งาน แต่จะทำให้เซิร์ฟเวอร์โหลดมากขึ้นและต้องใช้หน่วยความจำมากขึ้นอีกด้วย" -logoutConfirm: "คุณแน่ใจว่าต้องการออกจากระบบ?" +logoutConfirm: "ต้องการออกจากระบบ?" lastActiveDate: "ใช้งานล่าสุดที่" -statusbar: "ไอคอนบนแถบสถานะ" +statusbar: "แถบสถานะ" pleaseSelect: "ตัวเลือก" reverse: "ย้อนกลับ" colored: "สี" @@ -931,16 +953,16 @@ type: "รูปแบบ" speed: "ความเร็ว" slow: "ช้า" fast: "เร็ว" -sensitiveMediaDetection: "การตรวจจับของสื่อ NSFW" +sensitiveMediaDetection: "การตรวจจับสื่อที่มีเนื้อหาละเอียดอ่อน" localOnly: "เฉพาะท้องถิ่น" -remoteOnly: "รีโมทเท่านั้น" +remoteOnly: "ระยะไกลเท่านั้น" failedToUpload: "การอัปโหลดล้มเหลว" cannotUploadBecauseInappropriate: "ไม่สามารถอัปโหลดไฟล์นี้ได้เนื่องจากระบบตรวจพบบางส่วนของไฟล์ว่านี้อาจจะเป็น NSFW" -cannotUploadBecauseNoFreeSpace: "การอัปโหลดนั้นล้มเหลวเนื่องจากไม่มีความจุของไดรฟ์" +cannotUploadBecauseNoFreeSpace: "ไม่สามารถอัปโหลดได้เนื่องจากไม่มีพื้นที่ว่างในไดรฟ์เหลือแล้ว" cannotUploadBecauseExceedsFileSizeLimit: "ไม่สามารถอัปโหลดไฟล์นี้ได้แล้วเนื่องจากเกินขีดจำกัดของขนาดไฟล์แล้ว" beta: "เบต้า" -enableAutoSensitive: "ทำเครื่องหมาย NSFW อัตโนมัติ" -enableAutoSensitiveDescription: "อนุญาตให้ตรวจหาและทำเครื่องหมายสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่องหากเป็นไปได้ แม้ว่าตัวเลือกนี้จะถูกปิดใช้งาน แต่ก็สามารถเปิดใช้งานได้ทั้งอินสแตนซ์นี้" +enableAutoSensitive: "ทำเครื่องหมายว่ามีเนื้อหาที่ละเอียดอ่อนโดยอัตโนมัติ" +enableAutoSensitiveDescription: "อนุญาตให้ตรวจหาและทำเครื่องหมายสื่อว่ามีเนื้อหาโดยละเอียดอ่อนโดยอัตโนมัติ ผ่าน Machine Learning หากเป็นไปได้ แม้ว่าคุณจะปิดคุณสมบัตินี้ ก็อาจถูกตั้งค่าโดยอัตโนมัติ ทั้งนี้ขึ้นอยู่กับเซิร์ฟเวอร์" activeEmailValidationDescription: "เปิดใช้งานการตรวจสอบที่อยู่อีเมลให้มีความเข้มงวดยิ่งขึ้น ซึ่งอาจจะรวมไปถึงการตรวจสอบที่อยู่อีเมล์ที่ใช้แล้วทิ้งและโดยให้พิจารณาว่าสามารถสื่อสารด้วยได้หรือไม่ เมื่อไม่เลือกระบบจะตรวจสอบเฉพาะรูปแบบของอีเมลเท่านั้น" navbar: "แถบนำทาง" shuffle: "สลับ" @@ -956,28 +978,28 @@ sendPushNotificationReadMessageCaption: "การแจ้งเตือนท windowMaximize: "ขยายใหญ่สุดแล้ว" windowMinimize: "ย่อเล็กที่สุด" windowRestore: "เลิกทำ" -caption: "รายละเอียด" +caption: "คำอธิบาย" loggedInAsBot: "ล็อกอินเป็นบอตอยู่ในขณะนี้" tools: "เครื่องมือ" cannotLoad: "ไม่สามารถโหลดได้" numberOfProfileView: "มุมมองโปรไฟล์" -like: "ชื่นชอบ" -unlike: "ไม่ชอบ" -numberOfLikes: "จำนวนไลค์" +like: "ถูกใจ!" +unlike: "เลิกถูกใจ" +numberOfLikes: "จำนวนยอดถูกใจ" show: "แสดงผล" neverShow: "ไม่ต้องแสดงข้อความนี้อีก" remindMeLater: "ไว้ครั้งหน้าแล้วกัน" -didYouLikeMisskey: "คุณเคยชอบ Misskey ไหม?" +didYouLikeMisskey: "คุณชอบ Misskey ไหม?" pleaseDonate: "Misskey เป็นซอฟต์แวร์ฟรีที่ใช้งานโดย {host} เราขอขอบคุณการสนับสนุนของคุณอย่างสูงเพื่อให้การพัฒนา Misskey สามารถดำเนินต่อไปได้!" roles: "บทบาท" role: "บทบาท" noRole: "ไม่พบบทบาท" normalUser: "ผู้ใช้มาตรฐาน" undefined: "ไม่ได้กำหนด" -assign: "กำหนด" -unassign: "ยังไม่มอบหมาย" +assign: "มอบหมาย" +unassign: "เลิกมอบหมาย" color: "สี" -manageCustomEmojis: "จัดการอีโมจิแบบกำหนดเอง" +manageCustomEmojis: "จัดการเอโมจิที่กำหนดเอง" manageAvatarDecorations: "จัดการตกแต่งอวตาร" youCannotCreateAnymore: "คุณถึงขีดจํากัดการสร้างแล้วนะ" cannotPerformTemporary: "ไม่สามารถใช้การได้ชั่วคราว" @@ -992,33 +1014,35 @@ achievements: "ความสำเร็จ" gotInvalidResponseError: "การตอบสนองเซิร์ฟเวอร์ไม่ถูกต้อง" gotInvalidResponseErrorDescription: "เซิร์ฟเวอร์อาจไม่สามารถเข้าถึงได้หรืออาจจะกำลังอยู่ในระหว่างปรับปรุง กรุณาลองใหม่อีกครั้งในภายหลังนะคะ" thisPostMayBeAnnoying: "โน้ตนี้อาจจะเป็นการรบกวนผู้อื่นนะคะ" -thisPostMayBeAnnoyingHome: "โพสต์ไปยังบ้านไทม์ไลน์" +thisPostMayBeAnnoyingHome: "โพสต์ไปยังไทม์ไลน์หน้าแรก" thisPostMayBeAnnoyingCancel: "เลิก" thisPostMayBeAnnoyingIgnore: "โพสต์ยังไงก็แล้วแต่" -collapseRenotes: "ยุบ renotes ที่คุณได้เห็นแล้ว" +collapseRenotes: "ยุบรีโน้ตที่คุณเคยเห็นแล้ว" internalServerError: "เซิร์ฟเวอร์ภายในเกิดข้อผิดพลาด" internalServerErrorDescription: "เซิร์ฟเวอร์รันค้นพบข้อผิดพลาดที่ไม่คาดคิด" copyErrorInfo: "คัดลอกรายละเอียดข้อผิดพลาด" joinThisServer: "ลงชื่อสมัครใช้ในอินสแตนซ์นี้" exploreOtherServers: "มองหาอินสแตนซ์อื่น" -letsLookAtTimeline: "ลองดูที่ไทม์ไลน์" +letsLookAtTimeline: "มาดูไทม์ไลน์กัน" disableFederationConfirm: "ปิดใช้งานสหพันธ์จริงๆหรอแน่ใจแล้วนะ?" disableFederationConfirmWarn: "โพสต์จะยังคงเป็นสาธารณะต่อไป เว้นแต่จะตั้งค่าเป็นอย่างอื่น" disableFederationOk: "ปิดการใช้งาน" -invitationRequiredToRegister: "อินสแตนซ์นี้เป็นแบบรับเชิญเท่านั้น คุณต้องป้อนรหัสเชิญ เพื่องลงทะเบียนเข้าใช้งาน" +invitationRequiredToRegister: "อินสแตนซ์นี้เป็นแบบรับเชิญ เฉพาะผู้ที่มีรหัสเชิญเท่านั้นที่สามารถลงทะเบียนได้" emailNotSupported: "อินสแตนซ์นี้ไม่รองรับการส่งอีเมล" postToTheChannel: "โพสต์ลงช่อง" cannotBeChangedLater: "สิ่งนี้ไม่สามารถเปลี่ยนแปลงได้ในภายหลังนะ" reactionAcceptance: "การยอมรับรีแอคชั่น" -likeOnly: "ที่ชอบเท่านั้น" -likeOnlyForRemote: "ไลค์สำหรับอินสแตนซ์ระยะไกลเท่านั้น" -nonSensitiveOnly: "ไม่มีความอ่อนไหวเท่านั้น" -nonSensitiveOnlyForLocalLikeOnlyForRemote: "ไม่มีความอ่อนไหวเท่านั้น (เฉพาะไลค์จากระยะไกลเท่านั้น)" +likeOnly: "ที่ถูกใจเท่านั้น" +likeOnlyForRemote: "ทั้งหมด (เฉพาะการถูกใจจากอินสแตนซ์ระยะไกล)" +nonSensitiveOnly: "เฉพาะไม่มีเนื้อหาละเอียดอ่อน" +nonSensitiveOnlyForLocalLikeOnlyForRemote: "เฉพาะไม่มีเนื้อหาละเอียดอ่อน (เฉพาะการถูกใจจากระยะไกลเท่านั้น)" rolesAssignedToMe: "บทบาทที่ได้รับมอบหมายให้ฉัน" resetPasswordConfirm: "รีเซ็ตรหัสผ่านของคุณจริงๆหรอ?" -sensitiveWords: "คำที่ละเอียดอ่อน" +sensitiveWords: "คำที่มีเนื้อหาละเอียดอ่อน" sensitiveWordsDescription: "การเปิดเผยโน้ตทั้งหมดที่มีคำที่กำหนดค่าไว้จะถูกตั้งค่าเป็น \"หน้าแรก\" โดยอัตโนมัติ คุณยังสามารถแสดงหลายรายการได้โดยแยกรายการโดยใช้ตัวแบ่งบรรทัดได้นะ" sensitiveWordsDescription2: "การใช้ช่องว่างนั้นอาจจะสร้างนิพจน์ AND และคำหลักที่มีเครื่องหมายทับล้อมรอบจะเปลี่ยนเป็นนิพจน์ทั่วไปนะ" +hiddenTags: "แฮชแท็กที่ซ่อนอยู่" +hiddenTagsDescription: "เลือกแท็กที่จะไม่แสดงในรายการเทรนด์ สามารถลงทะเบียนหลายแท็กได้โดยขึ้นบรรทัดใหม่" notesSearchNotAvailable: "การค้นหาโน้ตไม่พร้อมใช้งาน" license: "ใบอนุญาต" unfavoriteConfirm: "ลบออกจากรายการโปรดแน่ใจหรอ?" @@ -1029,21 +1053,24 @@ retryAllQueuesConfirmTitle: "ลองใหม่ทั้งหมดจริ retryAllQueuesConfirmText: "สิ่งนี้จะเพิ่มการโหลดเซิร์ฟเวอร์ชั่วคราวนะ" enableChartsForRemoteUser: "สร้างแผนภูมิข้อมูลผู้ใช้ระยะไกล" enableChartsForFederatedInstances: "สร้างแผนภูมิข้อมูลอินสแตนซ์ระยะไกล" -showClipButtonInNoteFooter: "เพิ่ม \"คลิป\" เพื่อบันทึกเมนูการทำงาน" -reactionsDisplaySize: "รีแอคชั่นแสดงผลขนาด" +showClipButtonInNoteFooter: "เพิ่ม “คลิป” ไปยังเมนูสั่งการของโน้ต" +reactionsDisplaySize: "ขนาดของรีแอคชั่น" +limitWidthOfReaction: "จำกัดความกว้างสูงสุดของรีแอคชั่นและแสดงให้เล็กลง" noteIdOrUrl: "โน้ต ID หรือ URL" video: "วีดีโอ" videos: "วีดีโอ" +audio: "เสียง" +audioFiles: "เสียง" dataSaver: "ประหยัดข้อมูล" -accountMigration: "การโยกย้ายบัญชี" +accountMigration: "โยกย้ายบัญชี" accountMoved: "ผู้ใช้รายนี้ได้ย้ายไปยังบัญชีใหม่แล้ว:" accountMovedShort: "บัญชีนี้ถูกโอนย้ายไปแล้วค่ะ" -operationForbidden: "ห้ามดำเนินการ" +operationForbidden: "การดำเนินการถูกห้าม" forceShowAds: "แสดงโฆษณาเสมอ" -addMemo: "เพิ่มมีโม" -editMemo: "แก้ไขมีโม" -reactionsList: "ปฏิกิริยา" -renotesList: "Renotes รีโน้ต" +addMemo: "เพิ่มเมโม" +editMemo: "แก้ไขเมโม" +reactionsList: "รายการรีแอคชั่น" +renotesList: "รายการรีโน้ต" notificationDisplay: "การแจ้งเตือน" leftTop: "บนซ้าย" rightTop: "บนขวา" @@ -1053,16 +1080,16 @@ stackAxis: "ทิศทางการซ้อน" vertical: "แนวตั้ง" horizontal: "ด้านข้าง" position: "ตำแหน่ง" -serverRules: "กฎของเซิฟเวอร์" +serverRules: "กฎของเซิร์ฟเวอร์" pleaseConfirmBelowBeforeSignup: "โปรดยืนยันที่ด้านล่างก่อนสมัครใช้งาน" pleaseAgreeAllToContinue: "คุณต้องยอมรับทุกช่องตรงด้านบนเพื่อดำเนินการต่อค่ะ" continue: "ดำเนินการต่อ" preservedUsernames: "ชื่อผู้ใช้ที่สงวนไว้" -preservedUsernamesDescription: "ลิสต์ชื่อผู้ใช้ที่จะสำรองโดยคั่นด้วยการแบ่งบรรทัดนั้น เพราะสิ่งเหล่านี้จะไม่สามารถทำได้ในระหว่างการสร้างบัญชีตามปกติ บัญชีที่มีอยู่แล้วนั้นโดยใช้ชื่อผู้ใช้เหล่านี้จะไม่ได้รับผลกระทบอะไร" +preservedUsernamesDescription: "ระบุชื่อผู้ใช้ที่จะสงวนชื่อไว้ คั่นด้วยการขึ้นบรรทัดใหม่ ชื่อผู้ใช้ที่ระบุที่นี่จะไม่สามารถใช้งานได้อีกต่อไปเมื่อสร้างบัญชีใหม่ ยกเว้นเมื่อผู้ดูแลระบบสร้างบัญชี นอกจากนี้ บัญชีที่มีอยู่แล้วจะไม่ได้รับผลกระทบ" createNoteFromTheFile: "เรียบเรียงโน้ตจากไฟล์นี้" archive: "เก็บถาวร" -channelArchiveConfirmTitle: "เก็บถาวรจริงๆ {name} มั้ย?" -channelArchiveConfirmDescription: "ช่องที่ถูกเก็บถาวรแล้วนั้นจะไม่ปรากฏในรายการช่องหรือผลการค้นหานั้นอีกต่อไปไม่สามารถเพิ่มโพสต์ใหม่ได้อีกต่อไปนะ" +channelArchiveConfirmTitle: "ต้องการเก็บถาวรเจ้า {name} ใช่ไหม?" +channelArchiveConfirmDescription: "เมื่อเก็บถาวรแล้ว จะไม่ปรากฏในรายการช่องหรือผลการค้นหาอีกต่อไป และจะไม่สามารถโพสต์ใหม่ได้อีกต่อไป" thisChannelArchived: "ช่องนี้ถูกเก็บถาวรแล้วนะ" displayOfNote: "การแสดงโน้ต" initialAccountSetting: "ตั้งค่าโปรไฟล์" @@ -1073,14 +1100,14 @@ options: "ตัวเลือกบทบาท" specifyUser: "ผู้ใช้เฉพาะ" failedToPreviewUrl: "ไม่สามารถดูตัวอย่างได้" update: "อัปเดต" -rolesThatCanBeUsedThisEmojiAsReaction: "บทบาทที่สามารถใช้อิโมจินี้เป็นรีแอคชั่นได้" -rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหากไม่ได้ระบุบทบาท ทุกคนนั้นก็สามารถใช้อิโมจินี้เป็นการแสดงความรู้สึกได้นะ" +rolesThatCanBeUsedThisEmojiAsReaction: "บทบาทที่สามารถใช้เอโมจินี้เป็นรีแอคชั่นได้" +rolesThatCanBeUsedThisEmojiAsReactionEmptyDescription: "ถ้าหากไม่ได้ระบุบทบาท ทุกคนนั้นก็สามารถใช้เอโมจินี้เพื่อรีแอคชั่นได้นะ" rolesThatCanBeUsedThisEmojiAsReactionPublicRoleWarn: "บทบาทเหล่านี้ต้องเป็นสาธารณะ" cancelReactionConfirm: "ต้องการลบรีแอคชั่นของคุณจริงๆหรอ?" changeReactionConfirm: "ต้องการเปลี่ยนรีแอคชั่นของคุณจริงๆหรอ?" later: "ไว้ทีหลัง" goToMisskey: "ถึง Misskey" -additionalEmojiDictionary: "พจนานุกรมอีโมจิเพิ่มเติม" +additionalEmojiDictionary: "พจนานุกรมเอโมจิเพิ่มเติม" installed: "ติดตั้งแล้ว" branding: "แบรนดิ้ง" enableServerMachineStats: "เผยแพร่สถานะฮาร์ดแวร์ของเซิร์ฟเวอร์" @@ -1100,45 +1127,53 @@ registeredUserUsingInviteCode: "ใช้คำเชิญแล้วโดย waitingForMailAuth: "กำลังรอการยืนยันอีเมล" inviteCodeCreator: "สร้างการเชิญแล้วโดย" usedAt: "ใช้แล้วที่" -unused: "ไม่ใช้แล้ว" -used: "ใช้แล้ว" +unused: "ยังไม่ได้ใช้" +used: "ถูกใช้แล้ว" expired: "หมดอายุแล้ว" -doYouAgree: "ยอมรับมั้ย?" +doYouAgree: "ยอมรับไหม?" beSureToReadThisAsItIsImportant: "กรุณาอ่านข้อมูลที่สำคัญอันนี้" -iHaveReadXCarefullyAndAgree: "ฉันได้อ่านข้อความ \"{x}\" และยินยอม" +iHaveReadXCarefullyAndAgree: "ฉันได้อ่านและยินยอมเนื้อหาของ “{x}”" dialog: "ไดอะล็อก" icon: "ไอคอน" forYou: "สำหรับคุณ" currentAnnouncements: "ประกาศในปัจจุบัน" pastAnnouncements: "ประกาศที่ผ่านมา" youHaveUnreadAnnouncements: "มีการประกาศที่ยังไม่ได้อ่าน" +useSecurityKey: "โปรดปฏิบัติตามคำแนะนำของเบราว์เซอร์หรืออุปกรณ์ของคุณเพื่อใช้ security key หรือ passkey" replies: "ตอบกลับ" renotes: "รีโน้ต" loadReplies: "แสดงการตอบกลับ" loadConversation: "แสดงบทสนทนา" -pinnedList: "รายการที่ปักหมุดไว้แล้ว" +pinnedList: "รายชื่อที่ปักหมุดไว้" keepScreenOn: "เปิดหน้าจอไว้" +verifiedLink: "ความเป็นเจ้าของลิงก์ได้รับการยืนยันแล้ว" notifyNotes: "แจ้งเตือนเกี่ยวกับโพสต์ใหม่" unnotifyNotes: "หยุดการแจ้งเตือนเกี่ยวกับโน้ตใหม่" authentication: "การตรวจสอบสิทธิ์" -authenticationRequiredToContinue: "กรุณาตรวจสอบการรับรองความถูกต้องเพื่อดำเนินการต่อ" +authenticationRequiredToContinue: "กรุณายืนยันตัวตนทางอิเล็กทรอนิกส์เพื่อดำเนินการต่อ" dateAndTime: "เวลาประทับ" showRenotes: "แสดงรีโน้ต" edited: "แก้ไขแล้ว" notificationRecieveConfig: "การตั้งค่าการแจ้งเตือน" mutualFollow: "ติดตามซึ่งกันและกัน" fileAttachedOnly: "เฉพาะโน้ตที่มีไฟล์เท่านั้น" -showRepliesToOthersInTimeline: "แสดงการตอบกลับไปยังอื่นๆในไทม์ไลน์" -hideRepliesToOthersInTimeline: "ซ่อนการตอบกลับไปยังอื่นๆจากไทม์ไลน์" +showRepliesToOthersInTimeline: "แสดงการตอบกลับผู้อื่นลงในไทม์ไลน์" +hideRepliesToOthersInTimeline: "ไม่แสดงการตอบกลับผู้อื่นลงในไทม์ไลน์" +showRepliesToOthersInTimelineAll: "รวมตอบกลับจากทุกคนที่คุณติดตามไว้ในไทม์ไลน์ของคุณ" +hideRepliesToOthersInTimelineAll: "ซ่อนตอบกลับจากทุกคนที่คุณติดตามไปจากไทม์ไลน์ของคุณ" +confirmShowRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการแสดงการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?" +confirmHideRepliesAll: "การดำเนินการนี้ไม่สามารถย้อนกลับได้ คุณต้องการซ่อนการตอบกลับผู้อื่นจากผู้ใช้ทุกคนที่คุณติดตามอยู่ในไทม์ไลน์ของคุณหรือไม่?" externalServices: "บริการภายนอก" impressum: "อิมเพรสชั่น" impressumUrl: "URL อิมเพรสชั่น" +impressumDescription: "การติดป้ายกำกับ (Impressum) มีผลบังคับใช้ในบางประเทศและภูมิภาค เช่น ประเทศเยอรมนี" privacyPolicy: "นโยบายความเป็นส่วนตัว" privacyPolicyUrl: "URL นโยบายความเป็นส่วนตัว" tosAndPrivacyPolicy: "เงื่อนไขในการให้บริการและนโยบายความเป็นส่วนตัว" avatarDecorations: "การตกแต่งอวตาร" attach: "แนบ" detach: "นำออก" +detachAll: "เอาออกทั้งหมด" angle: "แองเกิล" flip: "ย้อนกลับ" showAvatarDecorations: "แสดงตกแต่งอวตาร" @@ -1148,17 +1183,45 @@ pullDownToRefresh: "ดึงลงเพื่อรีเฟรช" disableStreamingTimeline: "ปิดใช้งานอัปเดตไทม์ไลน์แบบเรียลไทม์" useGroupedNotifications: "แสดงผลการแจ้งเตือนแบบกลุ่มแล้ว" signupPendingError: "มีปัญหาในการตรวจสอบที่อยู่อีเมลลิงก์อาจหมดอายุแล้ว" +cwNotationRequired: "หากเปิดใช้งาน “ซ่อนเนื้อหา” จะต้องระบุคำอธิบาย" doReaction: "เพิ่มรีแอคชั่น" +code: "โค้ด" +reloadRequiredToApplySettings: "จำเป็นต้องมีการโหลดซ้ำเพื่อให้การตั้งค่ามีผล" +remainingN: "เหลือ : {n}" +overwriteContentConfirm: "แน่ใจหรือไม่ว่าต้องการเขียนทับเนื้อหาปัจจุบัน?" +seasonalScreenEffect: "เอฟเฟกต์หน้าจอตามฤดูกาล" +decorate: "ตกแต่ง" +addMfmFunction: "เพิ่มการตกแต่ง" +enableQuickAddMfmFunction: "แสดงตัวจิ้มเลือก MFM ขั้นสูง" +bubbleGame: "เกมบับเบิ้ล" +sfx: "เสียงเอฟเฟ็กต์" +soundWillBePlayed: "จะมีการเล่นเอฟเฟกต์เสียง" +showReplay: "ดูรีเพลย์" +replay: "รีเพลย์" +replaying: "กำลังรีเพลย์" +ranking: "อันดับ" +lastNDays: "ล่าสุด {n} วันที่แล้ว" +backToTitle: "กลับไปหน้าไตเติ้ล" +enableHorizontalSwipe: "ปัดเพื่อสลับแท็บ" +_bubbleGame: + howToPlay: "วิธีเล่น" + _howToPlay: + section1: "ขยับตำแหน่งและวางวัตถุลงในกล่อง" + section2: "เมื่อวัตถุประเภทเดียวกันมารวมกัน พวกมันจะกลายเป็นวัตถุใหม่และคุณจะได้รับคะแนน" + section3: "หากวัตถุล้นออกมาจากกล่อง เกมก็จะจบลง ตั้งเป้าทำคะแนนให้สูงด้วยการหลอมวัตถุต่าง ๆ โดยไม่ทำให้ล้นกล่อง!" _announcement: forExistingUsers: "ผู้ใช้งานที่มีอยู่เท่านั้น" forExistingUsersDescription: "การประกาศนี้จะแสดงต่อผู้ใช้ที่มีอยู่ ณ จุดที่เผยแพร่นั้นๆถ้าหากเปิดใช้งาน ถ้าหากปิดใช้งานผู้ที่กำลังสมัครใหม่หลังจากโพสต์แล้วนั้นก็จะเห็นเช่นกัน" needConfirmationToRead: "จำเป็นต้องยืนยันเพื่อทำเครื่องหมายบอกว่าอ่านแล้ว" needConfirmationToReadDescription: "ข้อความแจ้งแยก ถ้าหากต้องการเพื่อยืนยันว่ากำลังทำเครื่องหมายประกาศนี้ว่าอ่านแล้วจะแสดงขึ้นถ้าหากเปิดใช้งาน การประกาศนั้นจะไม่รวมอยู่ในฟังก์ชั่นว่า \"ทำเครื่องหมายทั้งหมดว่าอ่านแล้ว\"" - end: "ประกาศเก็บถาวร" + end: "เก็บประกาศ" tooManyActiveAnnouncementDescription: "การมีประกาศที่ใช้งานมากเกินไปนั้นอาจจะทำให้ประสบการณ์ของผู้ใช้งานนั้นดูแย่ลง โปรดกรุณาพิจารณาการเก็บประกาศที่ล้าสมัยด้วยนะค่ะ" readConfirmTitle: "ทำเครื่องหมายบอกว่าอ่านแล้วเลยมั้ย?" readConfirmText: "การดำเนินการนี้จะทำเครื่องหมายเนื้อหาของ \"{title}\" บอกว่าอ่านแล้วนะ" + shouldNotBeUsedToPresentPermanentInfo: "เราขอแนะนำให้ใช้ประกาศเพื่อโพสต์ข้อมูลแบบ flow มากกว่าข้อมูลแบบ stock เนื่องจากมีแนวโน้มที่จะส่งผลเสียต่อ UX โดยเฉพาะสำหรับผู้ใช้ใหม่" + dialogAnnouncementUxWarn: "เราขอแนะนำให้ใช้ด้วยความระมัดระวัง เนื่องจากการแจ้งเตือนแบบกล่องโต้ตอบตั้งแต่ 2 รายการขึ้นไปพร้อมกันอาจส่งผลเสียต่อ UX ได้อย่างมาก" silence: "ไม่มีการแจ้งเตือน" + silenceDescription: "หากเปิดใช้งาน จะไม่ได้แจ้งเตือนประกาศนี้ และผู้ใช้จะไม่จำเป็นต้องอ่าน" _initialAccountSetting: accountCreated: "คุณได้สร้างบัญชีของคุณสำเร็จเรียบร้อยแล้ว!" letsStartAccountSetup: "สำหรับผู้เริ่มต้นมาตั้งค่าโปรไฟล์ของคุณกันเถอะ" @@ -1170,7 +1233,8 @@ _initialAccountSetting: followUsers: "ลองติดตามผู้ใช้บางคนที่คุณอาจจะสนใจเพื่อสร้างไทม์ไลน์ของคุณสิ !" pushNotificationDescription: "กำลังเปิดใช้งานการแจ้งเตือนแบบพุชจะช่วยให้คุณได้รับการแจ้งเตือนจาก {name} โดยตรงบนอุปกรณ์ของคุณนะ" initialAccountSettingCompleted: "ตั้งค่าโปรไฟล์เสร็จสมบูรณ์แล้ว!" - haveFun: "ขอให้สนุก {name}!" + haveFun: "ขอให้สนุกกับ {name}!" + youCanContinueTutorial: "คุณสามารถดำเนินการต่อด้วยบทช่วยสอนเกี่ยวกับวิธีใช้ {name} (Misskey) หรือออกจากบทช่วยสอนแล้วเริ่มใช้งานได้ทันที" startTutorial: "เริ่มการฝึกสอน" skipAreYouSure: "ต้องการข้ามการตั้งค่าโปรไฟล์จริงๆแบบนั้นหรอ?" laterAreYouSure: "ต้องการตั้งค่าโปรไฟล์ในภายหลังจริงๆอย่างงั้นหรอ?" @@ -1181,29 +1245,80 @@ _initialTutorial: skipAreYouSure: "ต้องการออกจากบทช่วยสอนใช่ไหม?" _landing: title: "ยินดีต้อนรับสู่บทช่วยสอน" + description: "คุณสามารถตรวจสอบการใช้งานและฟังก์ชั่นพื้นฐานของ Misskey ได้ที่นี่" _note: title: "โน้ตคืออะไร?" + description: "โพสต์ใน Misskey เรียกว่า “โน้ต” ซึ่งจะจัดเรียงตามลำดับเวลาบนไทม์ไลน์และอัปเดตแบบเรียลไทม์" + reply: "คุณสามารถตอบกลับได้ และคุณยังสามารถตอบกลับใส่การตอบกลับเพื่อสนทนาต่อได้เสมือนดั่งเธรด" + renote: "คุณสามารถแชร์โน้ตไปยังไทม์ไลน์ของคุณเอง คุณยังสามารถเพิ่มข้อความและเครื่องหมายคำพูดได้" + reaction: "คุณสามารถเพิ่มรีแอคชั่นได้ รายละเอียดจะอธิบายอยู่ในหน้าถัดไป" + menu: "คุณสามารถดูรายละเอียดโน้ต คัดลอกลิงก์ และดำเนินการอื่นๆ ได้" _reaction: title: "รีแอคชั่นคืออะไร?" + description: "โน้ตสามารถ“รีแอคชั่น”ด้วยเอโมจิต่างๆ ซึ่งทำให้สามารถแสดงความแตกต่างเล็กๆ น้อยๆ ที่อาจไม่สามารถสื่อออกมาได้ด้วยการแค่การกด “ถูกใจ”" + letsTryReacting: "คุณสามารถเพิ่มรีแอคชั่นได้ด้วยการคลิกปุ่ม “+” บนโน้ต ลองรีแอคชั่นโน้ตตัวอย่างนี้ดูสิ!" + reactToContinue: "เพิ่มรีแอคชั่นเพื่อดำเนินการต่อ" + reactNotification: "คุณจะได้รับการแจ้งเตือนแบบเรียลไทม์เมื่อมีคนตอบรีแอคชั่นโน้ตของคุณ" + reactDone: "คุณสามารถยกเลิกรีแอคชั่นได้โดยการกดปุ่ม “-”" _timeline: title: "แนวคิดเรื่องของไทม์ไลน์" + description1: "Misskey มีหลายไทม์ไลน์ขึ้นอยู่กับวิธีการใช้งานของคุณ (บางไทม์ไลน์อาจไม่สามารถใช้ได้ขึ้นอยู่กับนโยบายของเซิร์ฟเวอร์)" + home: "คุณสามารถดูโพสต์จากบัญชีที่คุณติดตามได้" + local: "คุณสามารถดูโพสต์จากผู้ใช้ทั้งหมดบนเซิร์ฟเวอร์นี้" + social: "โพสต์จากทั้งไทม์ไลน์หน้าแรกและไทม์ไลน์ในพื้นที่ของคุณจะปรากฏขึ้น" + global: "คุณสามารถดูโพสต์จากเซิร์ฟเวอร์ที่เชื่อมต่ออื่นๆ ทั้งหมดได้" + description2: "คุณสามารถสลับระหว่างแต่ละไทม์ไลน์ได้ตลอดเวลาได้ที่บริเวณด้านบนของหน้าจอ" + description3: "นอกจากนี้ยังมีรายการไทม์ไลน์ ไทม์ไลน์ของช่อง ฯลฯ โปรดดู {link} สำหรับรายละเอียดเพิ่มเติม" _postNote: - title: "ตั้งค่ากำลังโพสต์โน้ต" + title: "ตั้งค่าการโพสต์โน้ต" + description1: "เมื่อโพสต์โน้ตบน Misskey คุณสามารถตั้งค่าตัวเลือกต่างๆ ได้ แบบฟอร์มการส่งมีลักษณะดังนี้" _visibility: description: "คุณสามารถจำกัดผู้ที่สามารถดูโน้ตของคุณได้นะ" public: "โน้ตของคุณนั้นจะปรากฏแก่ผู้ใช้งานทุกคน" + home: "เผยแพร่บนไทม์ไลน์หน้าแรกเท่านั้น ผู้คนที่เข้าชมโปรไฟล์ของคุณ ผ่านผู้ติดตาม และผ่านการรีโน้ตสามารถเห็นได้" + followers: "มองเห็นได้เฉพาะผู้ติดตามเท่านั้น ไม่มีใครอื่นนอกจากตัวคุณเองที่สามารถรีโน้ตได้ และมีเพียงผู้ติดตามของคุณเท่านั้นที่สามารถดูได้" + direct: "เปิดให้เห็นเฉพาะผู้ใช้ที่ระบุเท่านั้น และพวกเขาจะได้รับแจ้งเตือนด้วย คุณสามารถใช้มันแทนข้อความโดยตรง (dm)" + doNotSendConfidencialOnDirect1: "โปรดใช้ความระมัดระวังในการส่งข้อมูลที่ละเอียดอ่อน" + doNotSendConfidencialOnDirect2: "ผู้ดูแลระบบเซิร์ฟเวอร์ปลายทางสามารถดูเนื้อหาที่โพสต์ได้ ดังนั้นหากคุณส่งโพสต์โดยตรงไปยังผู้ใช้บนเซิร์ฟเวอร์ที่ไม่น่าเชื่อถือ คุณจะต้องใช้ความระมัดระวังในการจัดการข้อมูลที่เป็นความลับ" + localOnly: "การโพสต์ด้วย flag นี้จะไม่รวมโน้ตไปยังเซิร์ฟเวอร์อื่น ผู้ใช้บนเซิร์ฟเวอร์อื่นจะไม่สามารถดูโน้ตเหล่านี้ได้โดยตรง โดยไม่คำนึงถึงการตั้งค่าการแสดงผลข้างต้น" _cw: title: "คำเตือนเกี่ยวกับเนื้อหา" + description: "เนื้อหาที่เขียนด้วย “คำอธิบายประกอบ” จะแสดงแทนข้อความหลัก คลิก “ดูเพิ่มเติม” เพื่อแสดงข้อความเต็ม" _exampleNote: cw: "นี่อาจจะทำให้คุณหิวอย่างแน่นอน!" + note: "เพิ่งไปกินโดนัทเคลือบช็อคโกแลตมา 🍩😋" + useCases: "ใช้สิ่งนี้เพื่อระบุโน้ตที่ต้องตามแนวทางปฏิบัติของเซิร์ฟเวอร์ หรือเพื่อควบคุมการสปอยล์และข้อความที่ละเอียดอ่อนด้วยตนเอง" + _howToMakeAttachmentsSensitive: + title: "จะทำเครื่องหมายไฟล์แนบว่ามีเนื้อหาละเอียดอ่อนได้อย่างไร?" + description: "ทำเครื่องหมายไฟล์แนบว่า “มีเนื้อหาละเอียดอ่อน” เมื่อจำเป็นตามแนวทางของเซิร์ฟเวอร์ หรือเมื่อไฟล์แนบไม่ควรปรากฏให้เห็น" + tryThisFile: "ลองทำให้รูปภาพที่แนบมากับแบบฟอร์มนี้มีเนื้อหาละเอียดอ่อน!" + _exampleNote: + note: "อุ้ย นัตโตะ ฝาเปิดเละเทะ..." + method: "หากต้องการทำให้ไฟล์แนบมีเนื้อหาละเอียดอ่อน ให้คลิกไฟล์เพื่อเปิดเมนูแล้วคลิก “ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน”" + sensitiveSucceeded: "เมื่อแนบไฟล์ โปรดตั้งค่าเครื่องหมายว่ามีเนื้อหาละเอียดอ่อนตามแนวทางของเซิร์ฟเวอร์" + doItToContinue: "ทำเครื่องหมายกับรูปภาพว่ามีเนื้อหาละเอียดอ่อน เพื่อดำเนินการต่อ" + _done: + title: "บทเรียนจบลงแล้วจ้า เย่เย่เย่ 🎉" + description: "คุณสมบัติที่แนะนำในที่นี่เป็นเพียงบางส่วนเท่านั้น หากต้องการเรียนรู้เพิ่มเติมเกี่ยวกับวิธีใช้ Misskey โปรดไปที่ {link}" +_timelineDescription: + home: "บนไทม์ไลน์หน้าแรก คุณสามารถดูโพสต์จากบัญชีที่คุณติดตามได้" + local: "ไทม์ไลน์ในพื้นที่ช่วยให้คุณเห็นโพสต์จากผู้ใช้ทั้งหมดบนเซิร์ฟเวอร์นี้" + social: "ไทม์ไลน์โซเชียลจะแสดงโพสต์จากทั้งไทม์ไลน์หน้าแรกและไทม์ไลน์ในพื้นที่" + global: "ในไทม์ไลน์ทั่วโลก คุณสามารถดูโน้ตจากเซิร์ฟเวอร์ที่เชื่อมต่อทั้งหมดได้" _serverRules: description: "ชุดของกฎที่จะแสดงก่อนการลงทะเบียนเราขอแนะนำให้ตั้งค่าสรุปข้อกำหนดในการให้บริการ" _serverSettings: iconUrl: "ไอคอน URL" + appIconDescription: "ระบุไอคอนที่จะใช้เมื่อ {host} แสดงเป็นแอป" appIconUsageExample: "E.g. เป็น PWA หรือเมื่อแสดงผลเป็นบุ๊กมาร์กหน้าจอหลักบนโทรศัพท์" + appIconStyleRecommendation: "เนื่องจากไอคอนอาจถูกครอบตัดเป็นสี่เหลี่ยมจัตุรัสหรือวงกลม จึงแนะนำให้ใช้ไอคอนที่มีขอบสีรอบๆ เนื้อหา" appIconResolutionMustBe: "ความละเอียดขั้นต่ำไว้คือ {resolution}." - manifestJsonOverride: "manifest.json โอเวอร์ลาย" + manifestJsonOverride: "เขียนทับ manifest.json" shortName: "ชื่อย่อ" + shortNameDescription: "ตัวย่อหรือชื่อทั่วไปที่สามารถแสดงแทนชื่ออย่างเป็นทางการแบบยาวของเซิร์ฟเวอร์" + fanoutTimelineDescription: "เพิ่มประสิทธิภาพการดึงข้อมูลไทม์ไลน์อย่างมาก และลดภาระในฐานข้อมูลเมื่อเปิดใช้งาน ในทางกลับกัน การใช้หน่วยความจำของ Redis จะเพิ่มขึ้น ลองปิดการใช้งานนี้ในกรณีที่หน่วยความจำเซิร์ฟเวอร์เหลือน้อยหรือเซิร์ฟเวอร์ไม่เสถียร" + fanoutTimelineDbFallback: "ฟอลแบ๊กกลับฐานข้อมูล" + fanoutTimelineDbFallbackDescription: "เมื่อเปิดใช้งาน หากไม่ได้แคชไทม์ไลน์ ไทม์ไลน์จะฟอลแบ๊กไปยังฐานข้อมูลสำหรับการ query เพิ่มเติม การปิดใช้งานจะช่วยลดภาระของเซิร์ฟเวอร์ด้วยการกำจัดกระบวนฟอลแบ๊ก แต่มันก็จะจำกัดช่วงเวลาไทม์ไลน์ที่สามารถดึงข้อมูลได้" _accountMigration: moveFrom: "ย้ายข้อมูลบัญชีอื่นไปยังอีกบัญชีนี้หนึ่ง" moveFromSub: "สร้างนามแฝงไปยังบัญชีอื่น" @@ -1212,7 +1327,7 @@ _accountMigration: moveTo: "ย้ายข้อมูลบัญชีนี้ไปยังบัญชีอีกหนึ่ง" moveToLabel: "บัญชีที่จะย้ายไปที่:" moveCannotBeUndone: "ไม่สามารถยกเลิกการโอนย้ายบัญชีได้" - moveAccountDescription: "การกระทำนี้ไม่สามารถย้อนกลับได้นะ ขั้นตอนแรก ต้องสร้างนามแฝงสำหรับบัญชีนี้ในบัญชีที่คุณต้องการย้ายไป หลังจากนั้นแล้ว ป้อนบัญชีที่จะย้ายไปในรูปแบบดังต่อไปนี้: @person@instance.com" + moveAccountDescription: "การดำเนินการนี้จะย้ายบัญชีของคุณไปยังบัญชีอื่น\n・ผู้ที่กำลังติดตามคุณจากบัญชีนี้จะถูกย้ายไปยังบัญชีใหม่โดยอัตโนมัติ\n・บัญชีนี้จะเลิกติดตามผู้ใช้ทั้งหมดที่กำลังติดตามอยู่\n・คุณจะไม่สามารถสร้างโน้ต ฯลฯ ในบัญชีนี้ได้\n\nแม้ว่าการย้ายผู้ที่ติดตามคุณจะเป็นไปโดยอัตโนมัติ แต่คุณต้องเตรียมขั้นตอนบางอย่างด้วยตนเอง เพื่อย้ายรายชื่อผู้ใช้ที่คุณกำลังติดตาม โดยดำเนินการส่งออกรายชื่อแล้วค่อยนำเข้ามาภายหลังในเมนูการตั้งค่าของบัญชีใหม่ ใช้ขั้นตอนเดียวกันนี้ใช้รายชื่อผู้ใช้ที่ถูกปิดเสียงและถูกบล็อก\n\n(คำอธิบายนี้ใช้กับ Misskey v13.12.0 ขึ้นไป, ซอฟต์แวร์ ActivityPub อื่นๆ เช่น Mastodon อาจทำงานแตกต่างออกไป)" moveAccountHowTo: "หากต้องการย้ายข้อมูลก่อนอื่นให้สร้างชื่อแทนสำหรับบัญชีนี้ ในบัญชีที่จะต้องการย้ายไป\nหลังจากที่คุณสร้างนามแฝงนั้นแล้ว ให้ป้อนบัญชีที่ต้องการจะย้ายไปในรูปแบบดังต่อไปนี้: @username@server.example.com" startMigration: "โอนย้าย" migrationConfirm: "ยืนยันการย้ายข้อมูลบัญชีนี้ไปที่ {account} เมื่อเริ่มแล้วจะไม่สามารถหยุดหรือนำกลับคืนมาได้ และคุณจะไม่สามารถใช้บัญชีนี้ในสถานะดั้งเดิมได้อีกต่อไป\n\nนอกจากนี้ คุณจำเป็นต้องสร้างบัญชีสำรองสำหรับการย้ายบัญชี" @@ -1227,31 +1342,31 @@ _achievements: description: "โพสต์โน้ตแรกของคุณ" flavor: "ขอให้มีช่วงเวลาที่ดีกับ Misskey นะคะ!" _notes10: - title: "โน้ตบางอย่าง" + title: "โน้ตไม่กี่ชิ้น" description: "โพสต์ 10 โน้ต" _notes100: - title: "โน้ตจำนวนมาก" + title: "โน้ตเยอะอยู่" description: "โพสต์ 100 โน้ต" _notes500: - title: "ครอบคลุมในโน้ต" + title: "จมคากองโน้ต" description: "โพสต์ 500 โน้ต" _notes1000: title: "ภูเขาแห่งโน้ต" description: "โพสต์ 1,000 โน้ต" _notes5000: - title: "โน้ตล้น" + title: "โน้ตล้นไปแล้ว" description: "โพสต์ 5,000 โน้ต" _notes10000: title: "ซุปเปอร์โน้ต" description: "โพสต์ 10,000 โน้ต" _notes20000: - title: "ต้องการ... เพิ่มเติม... โน้ต..." + title: "ต้ อ ง ก า ร โ น้ ต เ พิ่ ม อี ก !" description: "โพสต์ 20,000 โน้ต" _notes30000: title: "โน้ต โน้ต โน้ต!" description: "โพสต์ 30,000 โน้ต" _notes40000: - title: "โน้ตโรงงาน" + title: "โรงงานผลิตโน้ต" description: "โพสต์ 40,000 โน้ต" _notes50000: title: "ดาวเคราะห์แห่งโน้ต" @@ -1260,26 +1375,26 @@ _achievements: title: "โน้ตควอซาร์" description: "โพสต์ 60,000 โน้ต" _notes70000: - title: "โน้ตหลุมดำ" + title: "หลุม-โน้ต-ดำ" description: "โพสต์ 70,000 โน้ต" _notes80000: - title: "โน้ต กาแล็กซี่" + title: "ดาราจักรโน้ต" description: "โพสต์ 80,000 โน้ต" _notes90000: - title: "โน้ต จักรวาล" + title: "จักรวาลโน้ต" description: "โพสต์ 90,000 โน้ต" _notes100000: title: "ALL YOUR NOTE ARE BELONG TO US" description: "โพสต์ 100,000 โน้ต" - flavor: "นายแน่ใจล่ะก็ มีอะไรพูดมาได้นะ" + flavor: "มีเรื่องจะเขียนมากขนาดนั้นเลยเหรอนั่น?" _login3: title: "มือใหม่ I" description: "เข้าสู่ระบบเป็นเวลารวม 3 วัน" - flavor: "เริ่มตั้งแต่วันนี้ เรียกฉันว่ามิสคิสต์" + flavor: "ตั้งแต่วันนี้เป็นต้นไป ฉันคือมิสคิสต์" _login7: title: "มือใหม่ II" description: "เข้าสู่ระบบเป็นเวลารวม 7 วัน" - flavor: "รู้สึกเหมือนคุณได้แขวนของสิ่งต่างๆ หรือยังคะ?" + flavor: "ชินกับมันแล้วหรือยัง?" _login15: title: "มือใหม่ III" description: "เข้าสู่ระบบเป็นเวลารวม 15 วัน" @@ -1292,7 +1407,7 @@ _achievements: _login100: title: "มิสคิสท์ III" description: "เข้าสู่ระบบเป็นเวลารวม 100 วัน" - flavor: "ความรุนแรง Misskist" + flavor: "มิสคิสต์หัวรุนแรง" _login200: title: "ลูกค้าประจำ I" description: "เข้าสู่ระบบเป็นเวลารวม 200 วัน" @@ -1305,7 +1420,7 @@ _achievements: _login500: title: "ผู้เชี่ยวชาญ I" description: "เข้าสู่ระบบเป็นเวลารวม 500 วัน" - flavor: "เพื่อนของผมนะมักจะกล่าวว่าผมนะชอบจดโน้ต" + flavor: "ทุกท่าน ผมชอบโน้ต (กล่าวโดย เดอะ เ_เ_อร์)" _login600: title: "ผู้เชี่ยวชาญ II" description: "เข้าสู่ระบบเป็นเวลารวม 600 วัน" @@ -1323,7 +1438,7 @@ _achievements: description: "เข้าสู่ระบบเป็นเวลารวม 1,000 วัน" flavor: "ขอบคุณที่ใช้ Misskey นะ !" _noteClipped1: - title: "จะต้อง... คลิป..." + title: "อดไม่ได้ที่จะต้องคลิปมันเอาไว้" description: "คลิปโน้ตตัวแรกของคุณ" _noteFavorited1: title: "สตาร์เกเซอร์" @@ -1332,15 +1447,15 @@ _achievements: title: "แสวงหาดวงดาว" description: "มีคนอื่นๆที่ชื่นชอบหนึ่งในโน้ตของคุณ" _profileFilled: - title: "เตรียมไว้อย่างดี" + title: "เตรียมตัวอย่างดี" description: "ตั้งค่าโปรไฟล์ของคุณ" _markedAsCat: title: "ฉันเป็นแมว" description: "ทำเครื่องหมายบัญชีของคุณว่าเป็นแมว" - flavor: "ฉันจะให้ชื่อคุณภายหลังนะ" + flavor: "แมวน้อยไร้ชื่อ" _following1: - title: "กำลังติดตามผู้ใช้คนแรกของคุณ" - description: "ติดตามผู้ใช้" + title: "ก้าวแรกสู่...กดติดตาม" + description: "กดติดตามชาวบ้านครั้งแรก" _following10: title: "ทำต่อไป... ทำต่อไป..." description: "ติดตาม 10 บัญชีผู้ใช้" @@ -1351,7 +1466,7 @@ _achievements: title: "เพื่อน 100 คน" description: "ติดตาม 100 บัญชี" _following300: - title: "เพื่อนโอเวอร์โหลด" + title: "มีเพื่อนมากเกินไปละ" description: "ติดตาม 300 บัญชี" _followers1: title: "ผู้ติดตามคนแรก" @@ -1378,12 +1493,12 @@ _achievements: title: "นักสะสมความสำเร็จ" description: "ได้รับความสำเร็จ 30 ครั้ง" _viewAchievements3min: - title: "ชอบบรรลุผลสําเร็จ" + title: "ชอบบรรลุความสําเร็จ" description: "มองดูรายการความสำเร็จของคุณเป็นเวลาอย่างน้อย 3 นาที" _iLoveMisskey: title: "ฉันรัก Misskey" - description: "โพสต์ \"I ❤ #Misskey\"" - flavor: "ขอบคุณที่ใช้ Misskey! by ทีมผู้พัฒนา" + description: "โพสต์ “I ❤ #Misskey”" + flavor: "ขอบคุณพระคุณเป็นอย่างสูงที่ท่านใช้ Misskey นะคะ ! by ทีมผู้พัฒนา" _foundTreasure: title: "ล่าสมบัติ" description: "คุณพบสมบัติที่ซ่อนอยู่" @@ -1391,25 +1506,25 @@ _achievements: title: "พักผ่อนสักหน่อย" description: "ใช้เวลา 30 นาทีบน Misskey" _client60min: - title: "ไม่มี \"Miss\" ใน Misskey " + title: "Misskey ต้องไม่มีสิ่งใด “Miss”" description: "เปิด Misskey ค้างไว้แล้วอย่างน้อย 60 นาที" _noteDeletedWithin1min: title: "ไม่เป็นไร" description: "ลบโน้ตภายในหนึ่งนาทีหลังจากที่โพสต์" _postedAtLateNight: - title: "กลางคืน" + title: "ออกหากินยามดึกดื่น" description: "โพสต์โน้ตตอนดึกๆ" flavor: "ได้เวลาเข้านอนแล้วนะ" _postedAt0min0sec: - title: "นาฬิกาพูดได้" - description: "โพสต์บนโน้ตเมื่อเวลา 00:00 น." - flavor: "คลิก คลิก คลิก แกล๊งๆ" + title: "นาฬิกาเทียบเวลา" + description: "โพสต์โน้ตเมื่อเวลา 00:00 น." + flavor: "โป๊ะ โป๊ะ โป๊ะ ปิ้งงงงง" _selfQuote: title: "อ้างอิงตนเอง" - description: "อ้างโน้ตย่อของคุณเอง" + description: "อ้างโน้ตของคุณเอง" _htl20npm: title: "ไทม์ไลน์ไหล" - description: "มีการทำความเร็วของไทม์ไลน์ที่บ้านของคุณเกิน 20 npm (โน้ตต่อนาที)" + description: "มีการทำความเร็วของไทม์ไลน์หน้าแรกเกิน 20 npm (โน้ตต่อนาที)" _viewInstanceChart: title: "วิเคราะห์" description: "ดูแผนภูมิอินสแตนซ์ของคุณ" @@ -1426,14 +1541,14 @@ _achievements: title: "คุณอ่านมันจริงๆหรือเปล่า?" description: "มีการโต้ตอบกับโน้ตที่มีความยาวมากกว่า 100 ตัวอักษรภายใน 3 วินาทีหลังจากที่โพสต์" _clickedClickHere: - title: "คลิ๊กที่นี่" + title: "คลิกที่นี่" description: "คุณได้คลิกที่นี่" _justPlainLucky: title: "แค่ลัคกี้ธรรมดา" description: "มีโอกาสที่จะได้รับด้วยความน่าจะเป็นไปได้ 0.005% ทุก ๆ 10 วินาที" _setNameToSyuilo: - title: "พระเจ้าคอมเพล็กซ์" - description: "ตั้งชื่อของคุณเป็น \"syuilo\"" + title: "คอมเพล็กซ์ของพระเจ้า" + description: "ตั้งชื่อของคุณเป็น “syuilo”" _passedSinceAccountCreated1: title: "ครบรอบหนึ่งปี" description: "ผ่านไปหนึ่งปีแล้วนะตั้งแต่บัญชีของคุณถูกสร้างขึ้นมาน่ะ" @@ -1453,7 +1568,7 @@ _achievements: _cookieClicked: title: "เกมที่คุณคลิกที่คุกกี้" description: "คลิกคุกกี้" - flavor: "เดี๋ยวก่อนนะ คุณอยู่ในเว็บไซต์ที่ถูกต้องแน่อย่างงั้นเหรอ?" + flavor: "ใช่หรอ? แน่ใจว่าซอฟต์แวร์ทำงานถูกต้องนะ?" _brainDiver: title: "Brain Diver" description: "โพสต์ลิงก์ไปยัง Brain Diver" @@ -1461,35 +1576,47 @@ _achievements: _smashTestNotificationButton: title: "ทดสอบโอเวอร์โฟลว์" description: "ทดสอบการแจ้งเตือนทริกเกอร์ซ้ำๆ ภายในระยะเวลาอันสั้นๆ" + _tutorialCompleted: + title: "ใบรับรองการสำเร็จหลักสูตร Misskey มือใหม่" + description: "เสร็จสิ้นการสอนแล้ว" + _bubbleGameExplodingHead: + title: "🤯" + description: "สร้างวัตถุที่ใหญ่ที่สุดในเกมบับเบิ้ล" + _bubbleGameDoubleExplodingHead: + title: "ดับเบิ้ล" + description: "สร้างวัตถุที่ใหญ่ที่สุดในเกมบับเบิ้ลสองชิ้นในเวลาเดียวกัน" + flavor: "ปิ่นโตขนาดนี้ น่าจะเพิ่ม 🤯 🤯 เข้าไปนิดหน่อย" _role: new: "บทบาทใหม่" edit: "แก้ไขบทบาท" name: "ชื่อบทบาท" description: "คำอธิบายบทบาท" permission: "สิทธิ์ตามบทบาท" - descriptionOfPermission: "ผู้ดูแลกลั่นกรองเนื้อหา สามารถดำเนินการดูแลขั้นพื้นฐานได้นะ\nผู้ดูแลระบบ สามารถเปลี่ยนการตั้งค่าทั้งหมดของอินสแตนซ์ได้นะ" + descriptionOfPermission: "ผู้ควบคุม สามารถดำเนินการดูแลขั้นพื้นฐานได้\nผู้ดูแลระบบ สามารถเปลี่ยนการตั้งค่าทั้งหมดของอินสแตนซ์ได้" assignTarget: "มอบหมาย" - descriptionOfAssignTarget: "แมนนวล เพื่อเปลี่ยนผู้ที่เป็นส่วนหนึ่งของบทบาทนี้และใครที่ไม่ใช่ด้วยตนเอง\nเงื่อนไข เพื่อให้ผู้ใช้ได้รับการกำหนดและนำออกจากบทบาทนี้โดยอัตโนมัติตามเงื่อนไขชุดหนึ่ง" + descriptionOfAssignTarget: "แบบปรับเอง เพิ่มถอนบทบาทนี้แก่ผู้ใช้ด้วยตัวเอง\nแบบมีเงื่อนไข เพิ่มถอนบทบาทนี้แก่ผู้ใช้โดยอัตโนมัติหากเข้าเงื่อนไขใดต่อไปนี้" manual: "ปรับเอง" + manualRoles: "บทบาทแบบทำเอง" conditional: "มีเงื่อนไข" + conditionalRoles: "บทบาทแบบมีเงื่อนไข" condition: "เงื่อนไข" isConditionalRole: "นี่คือบทบาทที่มีเงื่อนไข" - isPublic: "บทบาทสาธารณะ" - descriptionOfIsPublic: "ทุกคนสามารถดูได้ว่าผู้ใช้งานนั้นได้รับมอบหมายบทบาทด้วยหรือไม่ \n\nบทบาทจะแสดงในโปรไฟล์ของผู้ใช้ด้วย" + isPublic: "ทำให้บทบาทเปิดเผยต่อสาธารณะ" + descriptionOfIsPublic: "บทบาทจะปรากฏบนโปรไฟล์ของผู้ใช้และเปิดเผยต่อสาธารณะ (ทุกคนสามารถเห็นได้ว่าผู้ใช้รายนี้มีบทบาทนี้)" options: "ตัวเลือกบทบาท" policies: "นโยบาย" - baseRole: "บทบาทพื้นฐาน" - useBaseValue: "ใช้บทบาทพื้นฐานเริ่มต้น" + baseRole: "เทมเพลตบทบาท" + useBaseValue: "ใช้ตามเทมเพลตบทบาท" chooseRoleToAssign: "เลือกบทบาทที่ต้องการกำหนด" iconUrl: "ไอคอน URL" asBadge: "แสดงเป็นตรา" - descriptionOfAsBadge: "ไอคอนของบทบาทนี้จะปรากฏถัดจากชื่อผู้ใช้ของผู้ใช้งานด้วยบทบาทนี้ถ้าหากเปิดใช้งาน" - isExplorable: "บทบาทไทม์ไลน์เป็นแบบสาธารณะ" - descriptionOfIsExplorable: "ไทม์ไลน์ของบทบาทนี้จะสามารถเข้าถึงได้แบบสาธารณะถ้าหากเปิดใช้งาน เส้นเวลาของบทบาทนั้นจะไม่ถูกเปิดเผยต่อสาธารณะ ถึงแม้ว่าจะไม่เปิดเผยต่อสาธารณะแม้แต่ว่า...จะตั้งค่าไว้ยังไงก็ตาม" + descriptionOfAsBadge: "เมื่อเปิดใช้งาน ไอคอนบทบาทจะปรากฏถัดจากชื่อผู้ใช้" + isExplorable: "ค้นหาผู้ใช้ได้ง่ายขึ้นโดยดูจากบทบาท" + descriptionOfIsExplorable: "เมื่อเปิดใช้งาน ไทมไลน์บทบาทนี้และสมาชิกที่มีบทบาทนี้จะเปิดเผยเป็นสาธารณะ" displayOrder: "ตำแหน่ง" descriptionOfDisplayOrder: "ยิ่งตัวเลขสูง ตำแหน่ง UI ก็ยิ่งสูงขึ้นนะ" - canEditMembersByModerator: "อนุญาตให้ผู้ดูแลแก้ไขสมาชิก" - descriptionOfCanEditMembersByModerator: "เมื่อเปิดใช้ ผู้ดูแลนอกเหนือจากผู้ดูแลระบบแล้ว จะสามารถกำหนดและยกเลิกการมอบหมายบทบาทนี้ให้กับผู้ใช้ได้ เมื่อปิด เฉพาะผู้ดูแลระบบเท่านั้นที่จะสามารถกำหนดผู้ใช้ได้นะ" + canEditMembersByModerator: "อนุญาตให้ผู้ควบคุมแก้ไขสมาชิก" + descriptionOfCanEditMembersByModerator: "เมื่อเปิดใช้ นอกเหนือจากผู้ควบคุมและผู้ดูแลระบบแล้ว จะสามารถเพิ่มถอนบทบาทนี้แก่ผู้ใช้ได้ แต่เมื่อปิดใช้ จะมีเฉพาะผู้ดูแลระบบเท่านั้นที่จะสามารถดำเนินการได้" priority: "ลำดับความสำคัญ" _priority: low: "ต่ำ" @@ -1498,12 +1625,12 @@ _role: _options: gtlAvailable: "การดูไทม์ไลน์ทั่วโลก" ltlAvailable: "การดูไทม์ไลน์ในท้องถิ่น" - canPublicNote: "สามารถส่งโน้ตสาธารณะ" + canPublicNote: "สามารถโพสต์แบบสาธารณะ" canInvite: "สร้างรหัสเชิญอินสแตนซ์" inviteLimit: "จำกัดการเชิญ" - inviteLimitCycle: "จำกัดการเชิญไว้คูลดาวน์" + inviteLimitCycle: "คูลดาวน์ในการเชิญ" inviteExpirationTime: "วันหมดอายุของรหัสการเชิญ" - canManageCustomEmojis: "จัดการอีโมจิแบบกำหนดเอง" + canManageCustomEmojis: "จัดการเอโมจิที่กำหนดเอง" canManageAvatarDecorations: "จัดการตกแต่งอวตาร" driveCapacity: "ความจุของไดรฟ์" alwaysMarkNsfw: "ทำเครื่องหมายไฟล์ว่าเป็น NSFW เสมอ" @@ -1515,13 +1642,14 @@ _role: noteEachClipsMax: "จำนวนโน้ตสูงสุดภายในคลิป" userListMax: "จำนวนรายชื่อผู้ใช้สูงสุด" userEachUserListsMax: "จำนวนผู้ใช้สูงสุดภายในรายการผู้ใช้" - rateLimitFactor: "ขีดจำกัดอัตรา" - descriptionOfRateLimitFactor: "ขีดจํากัดอัตราที่ต่ำกว่ามีข้อจํากัดน้อยกว่าข้อจํากัดที่สูงกว่า" + rateLimitFactor: "อัตราการจำกัด" + descriptionOfRateLimitFactor: "ยิ่งตัวเลขน้อยก็ยิ่งจำกัดน้อย ยิ่งมากก็ยิ่งเข้มงวดมากขึ้น" canHideAds: "ซ่อนโฆษณา" canSearchNotes: "การใช้การค้นหาโน้ต" canUseTranslator: "การใช้งานแปล" + avatarDecorationLimit: "จำนวนการตกแต่งไอคอนสูงสุดที่สามารถติดตั้งได้" _condition: - isLocal: "ผู้ใช้ภายใน" + isLocal: "ผู้ใช้ในพื้นที่" isRemote: "ผู้ใช้ระยะไกล" createdLessThan: "สร้างน้อยกว่า" createdMoreThan: "สร้างมากกว่า" @@ -1535,10 +1663,10 @@ _role: or: "หรือ" not: "ไม่" _sensitiveMediaDetection: - description: "ลดความพยายามในการดูแลเซิร์ฟเวอร์ผ่านการจดจำสื่อ NSFW โดยอัตโนมัติผ่านการเรียนรู้ของเครื่อง การทำสิ่งนี้อาจจะเพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย" - sensitivity: "การตรวจจับความไว" - sensitivityDescription: "การลดความไวนั้นจะนำไปสู่การตรวจจับที่ผิดพลาดน้อยลง (ผลบวกที่ผิดพลาด) แต่ในขณะที่การเพิ่มนั้นจะนำไปสู่การตรวจหาที่พลาดน้อยลง (ผลลบเท็จ)" - setSensitiveFlagAutomatically: "ทำเครื่องหมายว่าเป็น NSFW" + description: "ใช้ Machine Learning เพื่อตรวจจับสื่อที่มีเนื้อหาละเอียดอ่อนโดยอัตโนมัติและใช้เพื่อการกลั่นกรอง ภาระของเซิร์ฟเวอร์จะเพิ่มขึ้นเล็กน้อย" + sensitivity: "ความไวในการตรวจจับ" + sensitivityDescription: "เมื่อความไวต่ำ Misdetection (ผลบวกลวง) จะลดลง, เมื่อความไวสูง Missed detection (ผลลบลวง) จะลดลง" + setSensitiveFlagAutomatically: "ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน" setSensitiveFlagAutomaticallyDescription: "ผลลัพธ์ของการตรวจจับภายในนั้นจะยังคงอยู่ ถึงแม้ว่าจะปิดตัวเลือกนี้" analyzeVideos: "เปิดใช้งานวิเคราะห์ของวิดีโอ" analyzeVideosDescription: "การวิเคราะห์วิดีโอนอกเหนือจากรูปภาพนั้น การทำสิ่งนี้จะทำให้เพิ่มภาระบนเซิร์ฟเวอร์เล็กน้อย" @@ -1548,18 +1676,19 @@ _emailUnavailable: disposable: "ไม่สามารถใช้อีเมลชั่วคราวได้" mx: "เซิร์ฟเวอร์อีเมลนี้ไม่ถูกต้อง" smtp: "เซิร์ฟเวอร์อีเมลนี้ไม่มีการตอบสนอง" + banned: "คุณไม่สามารถลงทะเบียนด้วยที่อยู่อีเมลนี้ได้" _ffVisibility: public: "เผยแพร่" followers: "ปรากฏให้แก่ผู้ติดตามเท่านั้น" private: "ส่วนตัว" _signup: - almostThere: "เกือบจะมี" - emailAddressInfo: "โปรดกรอกอีเมลของคุณ มันจะไม่เปิดเผยต่อสาธารณะ" + almostThere: "เกือบจะเสร็จแล้ว" + emailAddressInfo: "กรุณากรอกที่อยู่อีเมลที่คุณใช้ ที่อยู่อีเมลของคุณจะไม่ถูกเผยแพร่สู่สาธารณชน" emailSent: "เราได้ส่งอีเมลยืนยันไปยังที่อยู่อีเมลของคุณแล้วนะ ({email}) โปรดคลิกลิงก์ที่รวมไว้เพื่อสร้างบัญชีให้เสร็จสิ้น" _accountDelete: accountDelete: "ลบบัญชีผู้ใช้" mayTakeTime: "เนื่องจากการลบบัญชีนี้จะเป็นกระบวนการที่ต้องใช้ทรัพยากรมาก จึงอาจจะต้องใช้เวลาสักครู่ถึงจะเสร็จสมบูรณ์ ทั้งนี้ขึ้นอยู่กับจำนวนเนื้อหาที่คุณสร้างและจำนวนไฟล์ที่คุณอัปโหลดนะ" - sendEmail: "เมื่อการลบบัญชีนี้เสร็จสิ้น เราอาจจะส่งอีเมลไปยังที่อยู่อีเมลของคุณที่เคยลงทะเบียนไว้กับบัญชีนี้นะ" + sendEmail: "เมื่อการลบบัญชีเสร็จสิ้น การแจ้งเตือนจะถูกส่งไปยังที่อยู่อีเมลที่ลงทะเบียนไว้" requestAccountDelete: "ร้องขอให้ลบบัญชี" started: "การลบได้เริ่มต้นขึ้น" inProgress: "ปัจจุบันกำลังดำเนินการลบอยู่" @@ -1569,7 +1698,9 @@ _ad: hide: "ไม่ต้องแสดง" timezoneinfo: "วันในสัปดาห์นี้จะถูกกำหนดจากโซนเวลาของเซิร์ฟเวอร์" adsSettings: "ตั้งค่าการโฆษณา" + notesPerOneAd: "อัปเดตช่วงเวลาตำแหน่งโฆษณาแบบเรียลไทม์ (จำนวนโน้ตต่อโฆษณา)" setZeroToDisable: "ตั้งค่านี้ให้เป็น 0 เพื่อปิดใช้งานโฆษณาอัปเดตแบบเรียลไทม์" + adsTooClose: "เนื่องจากช่วงเวลาการแสดงโฆษณาสั้นมาก ประสบการณ์ผู้ใช้จึงอาจลดลงอย่างมาก" _forgotPassword: enterEmail: "ป้อนที่อยู่อีเมลที่คุณเคยใช้ในการลงทะเบียนไว้ ลิงก์ที่คุณสามารถรีเซ็ตรหัสผ่านได้นั้นจะถูกส่งไปนะ" ifNoEmail: "ถ้าหากคุณไม่ได้ใช้อีเมลระหว่างการลงทะเบียน กรุณาติดต่อผู้ดูแลระบบอินสแตนซ์แทนนะ" @@ -1577,8 +1708,8 @@ _forgotPassword: _gallery: my: "แกลลอรี่ของฉัน" liked: "โพสต์ที่ถูกใจ" - like: "ชื่นชอบ" - unlike: "ลบไลค์" + like: "ถูกใจ!" + unlike: "เลิกถูกใจ" _email: _follow: title: "ได้ติดตามคุณ" @@ -1601,7 +1732,7 @@ _preferencesBackups: applyConfirm: "คุณต้องการใช้ข้อมูลสำรอง \"{name}\" กับอุปกรณ์นี้อย่างงั้นจริงหรอ การตั้งค่าที่มีอยู่ของอุปกรณ์นี้จะถูกเขียนทับนะ" saveConfirm: "บันทึกข้อมูลสำรองเป็น {name} มั้ย?" deleteConfirm: "ลบข้อมูลสำรอง {name} มั้ย?" - renameConfirm: "เปลี่ยนชื่อข้อมูลสำรองนี้จาก \"{old}\" เป็น \"{new}\" หรือป่าว" + renameConfirm: "เปลี่ยนชื่อข้อมูลสำรองนี้จาก \"{old}\" เป็น \"{new}\" หรือไม่?" noBackups: "ไม่มีข้อมูลสำรองนะ คุณสามารถสำรองข้อมูลการตั้งค่าไคลเอนต์ของคุณบนเซิร์ฟเวอร์นี้โดยใช้ \"สร้างการสำรองข้อมูลใหม่\"ได้นะ" createdAt: "สร้างเมื่อ: {date} {time}" updatedAt: "อัปเดตเมื่อ: {date} {time}" @@ -1622,9 +1753,10 @@ _aboutMisskey: donate: "บริจาคให้กับ Misskey" morePatrons: " ขอบคุณทุกท่านที่ร่วมกันช่วยเหลือตลอดมานะคะ 🥰" patrons: "สมาชิกพันธมิตร" + projectMembers: "สมาชิกในโครงการ" _displayOfSensitiveMedia: - respect: "ซ่อนสื่อทำเครื่องหมายบอกว่าละเอียดอ่อน" - ignore: "แสดงผลสื่อทำเครื่องหมายบอกว่าละเอียดอ่อน" + respect: "ซ่อนสื่อที่ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน" + ignore: "แสดงสื่อที่ทำเครื่องหมายว่ามีเนื้อหาละเอียดอ่อน" force: "ซ่อนสื่อทั้งหมด" _instanceTicker: none: "ไม่ต้องแสดง" @@ -1635,17 +1767,18 @@ _serverDisconnectedBehavior: dialog: "แสดงกล่องโต้ตอบคำเตือน" quiet: "แสดงคำเตือนที่ไม่เป็นการรบกวน" _channel: - create: "สร้างแชนแนลใหม่" - edit: "แก้ไขแชนแนล" + create: "สร้างช่องใหม่" + edit: "แก้ไขช่อง" setBanner: "เซตแบนเนอร์" removeBanner: "ลบแบนเนอร์" featured: "เทรนด์" owned: "เจ้าของ" following: "ติดตามแล้ว" usersCount: "{n} ผู้เข้าร่วม" - notesCount: "{n} โน้ต" + notesCount: "มี {n} โน้ต" nameAndDescription: "ชื่อและคำอธิบาย" nameOnly: "ชื่อเท่านั้น" + allowRenoteToExternal: "อนุญาตให้รีโน้ตและอ้างอิงนอกช่องได้" _menuDisplay: sideFull: "ด้านข้าง" sideIcon: "ด้านข้าง (ไอคอน)" @@ -1658,7 +1791,7 @@ _wordMute: _instanceMute: instanceMuteDescription: "การดำเนินการนี้จะปิดเสียง\"โน้ต/รีโน้ต\"จากอินสแตนซ์ที่อยู่ในรายการ รวมถึงบันทึกของผู้ใช้ที่ตอบกลับผู้ใช้จากอินสแตนซ์ที่ปิดเสียง" instanceMuteDescription2: "คั่นด้วยการขึ้นบรรทัดใหม่" - title: "ซ่อนโน้ตจากอินสแตนซ์ที่มีอยู่ในรายการ" + title: "ซ่อนโน้ตจากอินสแตนซ์ที่มีอยู่ในรายชื่อ" heading: "รายชื่ออินสแตนซ์ที่ถูกปิดเสียง" _theme: explore: "สำรวจธีม" @@ -1737,6 +1870,14 @@ _sfx: notification: "การเเจ้งเตือน" antenna: "เสาอากาศ" channel: "การแจ้งเตือนช่อง" + reaction: "เมื่อเลือกรีแอคชั่น" +_soundSettings: + driveFile: "ใช้เสียงจากไดรฟ์" + driveFileWarn: "เลือกไฟล์ในไดรฟ์ของคุณ" + driveFileTypeWarn: "ไม่รองรับไฟล์นี้" + driveFileTypeWarnDescription: "กรุณาเลือกไฟล์เสียง" + driveFileDurationWarn: "เสียงยาวเกินไป" + driveFileDurationWarnDescription: "การใช้เสียงที่ยาวอาจรบกวนการใช้งาน Misskey, ต้องการดำเนินการต่อหรือไม่?" _ago: future: "อนาคต" justNow: "เมื่อกี๊นี้" @@ -1748,6 +1889,14 @@ _ago: monthsAgo: "{n} เดือนที่แล้ว" yearsAgo: "{n} ปีที่ผ่านมา" invalid: "ไม่พบผลลัพธ์" +_timeIn: + seconds: "ใน {n} วินาที" + minutes: "ใน {n} นาที" + hours: "ใน {n} ชั่วโมง" + days: "ใน {n} วัน" + weeks: "ใน {n} สัปดาห์" + months: "ใน {n} เดือน" + years: "ใน {n} ปี" _time: second: "วินาที" minute: "นาที" @@ -1777,7 +1926,9 @@ _2fa: renewTOTPConfirm: "วิธีการแบบนี้จะทําให้รหัสยืนยันจากแอพก่อนหน้าของคุณหยุดทํางานเลยนะ" renewTOTPOk: "ตั้งค่าคอนฟิกใหม่" renewTOTPCancel: "ไม่เป็นไร" + checkBackupCodesBeforeCloseThisWizard: "โปรดตรวจสอบรหัสสำรองด้านล่างก่อนที่จะปิดวิซาร์ดนี้" backupCodes: "รหัสสำรองข้อมูล" + backupCodesDescription: "หากแอปยืนยันตัวตนของคุณไม่พร้อมใช้งาน คุณสามารถใช้รหัสสำรองด้านล่างเพื่อเข้าถึงบัญชีของคุณได้ อย่าลืมเก็บรหัสเหล่านี้ไว้ในที่ปลอดภัย แต่ละรหัสสามารถใช้ได้เพียงครั้งเดียวเท่านั้น" backupCodeUsedWarning: "มีการใช้รหัสสำรองแล้ว โปรดกรุณากำหนดค่าการตรวจสอบสิทธิ์แบบสองปัจจัยโดยเร็วที่สุดถ้าหากคุณยังไม่สามารถใช้งานได้อีก" backupCodesExhaustedWarning: "รหัสสำรองทั้งหมดถูกใช้แล้ว ถ้าหากคุณยังสูญเสียการเข้าถึงแอปการตรวจสอบสิทธิ์แบบสองปัจจัยคุณจะยังไม่สามารถเข้าถึงบัญชีนี้ได้ กรุณากำหนดค่าการรับรองความถูกต้องด้วยการยืนยันสองชั้น" _permissions: @@ -1798,29 +1949,78 @@ _permissions: "write:notes": "เขียนหรือลบโน้ต" "read:notifications": "ดูการแจ้งเตือนของคุณ" "write:notifications": "จัดการแจ้งเตือนของคุณ" - "read:reactions": "ดูปฏิกิริยาของคุณ" - "write:reactions": "แก้ไขปฏิกิริยาของคุณ" + "read:reactions": "ดูรีแอคชั่นของคุณ" + "write:reactions": "แก้ไขรีแอคชั่นของคุณ" "write:votes": "โหวตบนสำรวจความคิดเห็น" - "read:pages": "ดูหน้า" + "read:pages": "ดหน้าเพจ" "write:pages": "แก้ไขหรือลบเพจของคุณ" - "read:page-likes": "ดูไลค์ของคุณบนเพจ" - "write:page-likes": "แก้ไขการถูกใจของคุณบนเพจ" + "read:page-likes": "ดูรายการเพจที่ถูกใจไว้" + "write:page-likes": "แก้ไขรายการเพจที่ถูกใจ" "read:user-groups": "ดูกลุ่มผู้ใช้ของคุณ" "write:user-groups": "แก้ไขหรือลบกลุ่มผู้ใช้ของคุณ" "read:channels": "ดูแชนแนลของคุณ" "write:channels": "แก้ไขแชนแนลของคุณ" "read:gallery": "ดูแกลเลอรี่" "write:gallery": "แก้ไขแกลเลอรี่ของคุณ" - "read:gallery-likes": "ดูรายการโพสต์ในแกลเลอรีที่ชอบของคุณ" - "write:gallery-likes": "แก้ไขรายการโพสต์ในแกลเลอรีที่ชอบของคุณ" + "read:gallery-likes": "ดูรายการโพสต์แกลเลอรีที่ถูกใจไว้" + "write:gallery-likes": "แก้ไขรายการโพสต์แกลเลอรีที่ถูกใจไว้" "read:flash": "วิว เพลย์" "write:flash": "แก้ไขเพลย์" - "read:flash-likes": "ดูรายชื่อของไลค์ เพลย์" - "write:flash-likes": "แก้ไขรายชื่อของไลค์ เพลย์" + "read:flash-likes": "ดูรายการ play ที่ถูกใจไว้" + "write:flash-likes": "แก้ไขรายการ play ที่ถูกใจไว้" + "read:admin:abuse-user-reports": "ดูรายงานจากผู้ใช้" + "write:admin:delete-account": "ลบบัญชีผู้ใช้" + "write:admin:delete-all-files-of-a-user": "ลบไฟล์ทั้งหมดของผู้ใช้" + "read:admin:index-stats": "ดูข้อมูลเกี่ยวกับดัชนีฐานข้อมูล" + "read:admin:table-stats": "ดูข้อมูลเกี่ยวกับตารางฐานข้อมูล" + "read:admin:user-ips": "ดูที่อยู่ IP ของผู้ใช้" + "read:admin:meta": "ดูข้อมูลเมตาของอินสแตนซ์" + "write:admin:reset-password": "รีเซ็ตรหัสผ่านของผู้ใช้" + "write:admin:resolve-abuse-user-report": "แก้ไขรายงานจากผู้ใช้" + "write:admin:send-email": "ส่งอีเมล" + "read:admin:server-info": "ดูข้อมูลเซิร์ฟเวอร์" + "read:admin:show-moderation-log": "ดูปูมการแก้ไข" + "read:admin:show-user": "ดูข้อมูลส่วนตัวของผู้ใช้" + "read:admin:show-users": "ดูข้อมูลส่วนตัวของผู้ใช้" + "write:admin:suspend-user": "ระงับผู้ใช้" + "write:admin:unset-user-avatar": "ลบอวตารผู้ใช้" + "write:admin:unset-user-banner": "ลบแบนเนอร์ผู้ใช้" + "write:admin:unsuspend-user": "ยกเลิกการระงับผู้ใช้" + "write:admin:meta": "จัดการข้อมูลเมตาของอินสแตนซ์" + "write:admin:user-note": "จัดการโน้ตการกลั่นกรอง" + "write:admin:roles": "จัดการบทบาท" + "read:admin:roles": "ดูบทบาท" + "write:admin:relays": "จัดการรีเลย์" + "read:admin:relays": "ดูรีเลย์" + "write:admin:invite-codes": "จัดการคำเชิญ" + "read:admin:invite-codes": "ดูรหัสคำเชิญ" + "write:admin:announcements": "จัดการประกาศ" + "read:admin:announcements": "ดูประกาศ" + "write:admin:avatar-decorations": "จัดการการตกแต่งอวตาร" + "read:admin:avatar-decorations": "ดูการตกแต่งอวตาร" + "write:admin:federation": "จัดการข้อมูลเกี่ยวกับสหพันธ์" + "write:admin:account": "จัดการบัญชีผู้ใช้" + "read:admin:account": "ดูข้อมูลเกี่ยวกับผู้ใช้" + "write:admin:emoji": "จัดการเอโมจิ" + "read:admin:emoji": "ดูเอโมจิ" + "write:admin:queue": "จัดการคิวงาน" + "read:admin:queue": "ดูข้อมูลเกี่ยวกับคิวงาน" + "write:admin:promo": "จัดการโน้ตโปรโมชั่น" + "write:admin:drive": "จัดการไดรฟ์ของผู้ใช้" + "read:admin:drive": "ดูข้อมูลเกี่ยวกับไดรฟ์ของผู้ใช้" + "read:admin:stream": "ใช้ Websocket API สำหรับผู้ดูแลระบบ" + "write:admin:ad": "จัดการโฆษณา" + "read:admin:ad": "ดูโฆษณา" + "write:invite-codes": "สร้างรหัสคำเชิญ" + "read:invite-codes": "รับรหัสเชิญ" + "write:clip-favorite": "ควบคุมการถูกใจของคลิป" + "read:clip-favorite": "ดูการถูกใจของคลิป" + "read:federation": "รับข้อมูลเกี่ยวกับสหพันธ์" + "write:report-abuse": "รายงานการละเมิด" _auth: shareAccessTitle: "การให้สิทธิ์แอปพลิเคชัน" shareAccess: "คุณต้องการอนุญาตให้ \"{name}\" เข้าถึงบัญชีนี้เลยมั้ย?" - shareAccessAsk: "คุณแน่ใจแล้วจริงๆหรอว่าต้องการอนุญาตให้แอปพลิเคชันนี้เข้าถึงบัญชีของคุณแน่ใจแล้วหรอ?" + shareAccessAsk: "ต้องการอนุญาตให้แอปพลิเคชันนี้เข้าถึงบัญชีของคุณหรือไม่?" permission: "{name} ได้ขอสิทธิ์การเข้าถึงดังต่อไปนี้" permissionAsk: "แอปพลิเคชันนี้ขอสิทธิ์ดังต่อไปนี้" pleaseGoBack: "กรุณากลับไปที่แอปพลิเคชัน" @@ -1856,7 +2056,7 @@ _widgets: photos: "รูปภาพ" digitalClock: "นาฬิกาดิจิตอล" unixClock: "นาฬิกา UNIX" - federation: "Fediration" + federation: "สหพันธ์" instanceCloud: "อินสแตนซ์คลาวด์" postForm: "แบบฟอร์มการโพสต์" slideshow: "แสดงภาพนิ่ง" @@ -1869,8 +2069,9 @@ _widgets: aichan: "ไอ" userList: "รายชื่อผู้ใช้" _userList: - chooseList: "เลือกรายการ" + chooseList: "เลือกรายชื่อ" clicker: "คลิกเกอร์" + birthdayFollowings: "วันเกิดผู้ใช้ในวันนี้" _cw: hide: "ซ่อน" show: "โหลดเพิ่มเติม" @@ -1894,7 +2095,7 @@ _poll: showResult: "ดูผลลัพธ์" voted: "โหวตแล้ว" closed: "สิ้นสุดแล้ว" - remainingDays: "{d} วัน(s) {h} ชั่วโมง(s) ที่เหลืออยู่" + remainingDays: "จะเสร็จสิ้นในอีก {d} วัน {h} ชั่วโมง" remainingHours: "{h} ชั่วโมง(s) {m} นาที(s) ที่เหลืออยู่" remainingMinutes: "{m} นาที(s) {s} วินาที(s) ที่เหลืออยู่" remainingSeconds: "{s} นาที(s) ที่เหลืออยู่" @@ -1907,7 +2108,7 @@ _visibility: followersDescription: "ทำให้ผู้ติดตามนั้นมองเห็นแค่คุณเท่านั้น" specified: "ไดเร็ค" specifiedDescription: "ทำให้มองเห็นได้เฉพาะผู้ใช้ที่ระบุเท่านั้น" - disableFederation: "ไม่มีสหภาพ" + disableFederation: "ไม่มีสหพันธ์" disableFederationDescription: "อย่าส่งไปยังอินสแตนซ์อื่น" _postForm: replyPlaceholder: "ตอบกลับโน้ตนี้..." @@ -1923,8 +2124,8 @@ _postForm: _profile: name: "ชื่อ" username: "ชื่อผู้ใช้" - description: "ประวัติ" - youCanIncludeHashtags: "คุณยังสามารถใส่แฮชแท็กในประวัติของคุณได้นะ" + description: "แนะนำตัว" + youCanIncludeHashtags: "คุณสามารถใส่แฮชแท็กในส่วนแนะนำตัวของคุณได้" metadata: "ข้อมูลเพิ่มเติม" metadataEdit: "แก้ไขข้อมูลเพิ่มเติม" metadataDescription: "ใช้สิ่งเหล่านี้ คุณสามารถแสดงฟิลด์ข้อมูลเพิ่มเติมในโปรไฟล์ของคุณ" @@ -1933,13 +2134,15 @@ _profile: changeAvatar: "เปลี่ยนอวาตาร์" changeBanner: "เปลี่ยนแบนเนอร์" verifiedLinkDescription: "โดยการป้อน URL ที่มีลิงก์ไปยังโปรไฟล์ของคุณตรงนี้ ส่วนไอคอนการยืนยันความเป็นเจ้าของนั้นก็สามารถแสดงถัดจากฟิลด์ได้นะ" + avatarDecorationMax: "คุณสามารถเพิ่มการตกแต่งได้สูงสุด {max}" _exportOrImport: allNotes: "โน้ตทั้งหมด" favoritedNotes: "บันทึกที่ชื่นชอบ" + clips: "คลิป" followingList: "กำลังติดตาม" muteList: "ปิดเสียง" blockingList: "บล็อค" - userLists: "รายการ" + userLists: "รายชื่อ" excludeMutingUsers: "ยกเว้นผู้ใช้ที่ปิดเสียง" excludeInactiveUsers: "ยกเว้นผู้ใช้ที่ไม่ได้ใช้งาน" withReplies: "รวมการตอบกลับจากผู้ใช้ที่นำเข้าไว้ในไทม์ไลน์" @@ -1975,16 +2178,16 @@ _timelines: social: "โซเชี่ยล" global: "ทั่วโลก" _play: - new: "สร้างการเล่น" - edit: "แก้ไขเล่น" - created: "สร้างการเล่นแล้ว" - updated: "แก้ไขการเล่นแล้ว" - deleted: "ลบการเล่นแล้ว" - pageSetting: "ตั้งค่าการเล่น" + new: "สร้าง Play" + edit: "แก้ไข Play" + created: "สร้าง Play แล้ว" + updated: "แก้ไข Play แล้ว" + deleted: "ลบ Play แล้ว" + pageSetting: "ตั้งค่า Play" editThisPage: "แก้ไข Play นี้" viewSource: "ดูต้นฉบับ" - my: "มาย เพลย์" - liked: "ไลค์ เพลย์" + my: "Play ของฉัน" + liked: "Play ที่ถูกใจไว้" featured: "เป็นที่นิยม" title: "หัวข้อ" script: "สคริปต์" @@ -1996,15 +2199,15 @@ _pages: created: "สร้างหน้าเพจสำเร็จเรียบร้อยแล้ว" updated: "แก้ไขหน้าเพจสำเร็จเรียบร้อยแล้ว" deleted: "ลบหน้าเพจสำเร็จเรียบร้อยแล้ว" - pageSetting: "การตั้งค่าหน้า" + pageSetting: "การตั้งค่าหน้าเพจ" nameAlreadyExists: "URL ของหน้าที่ระบุนั้นมีอยู่แล้ว" invalidNameTitle: "URL ของหน้าที่ระบุนั้นไม่ถูกต้อง" invalidNameText: "ตรวจสอบให้แน่ใจนะว่าชื่อหน้าไม่ว่างเปล่า" editThisPage: "แก้ไขเพจนี้" viewSource: "ดูต้นฉบับ" - viewPage: "ดูหน้า" + viewPage: "ดูหน้าเพจ" like: "ถูกใจ" - unlike: "ลบไลค์" + unlike: "เลิกถูกใจ" my: "หน้าเพจของฉัน" liked: "หน้าเพจที่ถูกใจ" featured: "เป็นที่นิยม" @@ -2025,7 +2228,7 @@ _pages: chooseBlock: "เพิ่มบล็อค" selectType: "เลือกชนิด" contentBlocks: "เนื้อหา" - inputBlocks: "อินพุต" + inputBlocks: "ป้อนข้อมูล" specialBlocks: "พิเศษ" blocks: text: "ข้อความ" @@ -2054,12 +2257,16 @@ _notification: pollEnded: "โพลสำรวจความคิดเห็นผลลัพธ์มีพร้อมใช้งาน" newNote: "โพสต์ใหม่" unreadAntennaNote: "เสาอากาศ {name}" + roleAssigned: "ได้รับบทบาท" emptyPushNotificationMessage: "การแจ้งเตือนแบบพุชได้รับการอัพเดทแล้ว" achievementEarned: "รับความสำเร็จ" testNotification: "ทดสอบการแจ้งเตือน" - checkNotificationBehavior: "ตรวจสอบลักษณะที่ปรากฏการแจ้งเตือน" + checkNotificationBehavior: "กดเพื่อดูลักษณะการแจ้งเตือน" sendTestNotification: "ส่งทดสอบการแจ้งเตือน" notificationWillBeDisplayedLikeThis: "การแจ้งเตือนมีลักษณะแบบนี้" + reactedBySomeUsers: "ถูกรีแอคชั่นโดยผู้ใช้ {n} ราย" + renotedBySomeUsers: "Renote จากผู้ใช้จำนวน {n} ราย" + followedBySomeUsers: "มีผู้ติดตาม {n} ราย" _types: all: "ทั้งหมด" note: "โน้ตใหม่" @@ -2072,6 +2279,7 @@ _notification: pollEnded: "โพลนี้สิ้นสุดลงแล้ว" receiveFollowRequest: "ได้รับคำขอติดตาม\n" followRequestAccepted: "ยอมรับคำขอติดตาม" + roleAssigned: "ให้บทบาท" achievementEarned: "ปลดล็อกความสำเร็จแล้ว" app: "การแจ้งเตือนจากแอปที่มีลิงก์" _actions: @@ -2105,7 +2313,7 @@ _deck: tl: "ไทม์ไลน์" antenna: "เสาอากาศ" list: "รายการ" - channel: "แชนแนล" + channel: "ช่อง" mentions: "พูดถึง" direct: "ไดเร็ค" roleTimeline: "บทบาทไทม์ไลน์" @@ -2140,9 +2348,9 @@ _moderationLogTypes: unassignRole: "ถอดออกจากบทบาทแล้ว" suspend: "ถูกระงับ" unsuspend: "เลิกถูกระงับ" - addCustomEmoji: "เพิ่มอีโมจิที่กำหนดเองแล้ว" - updateCustomEmoji: "อัปเดตอีโมจิที่กำหนดเองแล้ว" - deleteCustomEmoji: "ลบอีโมจิที่กำหนดเองออกแล้ว" + addCustomEmoji: "เพิ่มเอโมจิที่กำหนดเองแล้ว" + updateCustomEmoji: "อัปเดตเอโมจิที่กำหนดเองแล้ว" + deleteCustomEmoji: "ลบเอโมจิที่กำหนดเองออกแล้ว" updateServerSettings: "อัปเดตการตั้งค่าเซิร์ฟเวอร์แล้ว" updateUserNote: "อัปเดตโน้ตการกลั่นกรองแล้ว" deleteDriveFile: "ลบไฟล์ออกแล้ว" @@ -2156,13 +2364,18 @@ _moderationLogTypes: resetPassword: "รีเซ็ตรหัสผ่าน" suspendRemoteInstance: "อินสแตนซ์ระยะไกลถูกระงับ" unsuspendRemoteInstance: "อินสแตนซ์ระยะไกลเลิกการระงับ" - markSensitiveDriveFile: "ทำเครื่องหมายไฟล์บอกว่าละเอียดอ่อน" - unmarkSensitiveDriveFile: "ยกเลิกทำเครื่องหมายไฟล์ว่าละเอียดอ่อน" + markSensitiveDriveFile: "ทำเครื่องหมายไฟล์ว่ามีเนื้อหาละเอียดอ่อน" + unmarkSensitiveDriveFile: "ยกเลิกทำเครื่องหมายไฟล์ว่ามีเนื้อหาละเอียดอ่อน" resolveAbuseReport: "รายงานได้รับการแก้ไขแล้ว" createInvitation: "สร้างคำเชิญ" createAd: "สร้างโฆษณาแล้ว" deleteAd: "ลบโฆษณาออกแล้ว" updateAd: "อัปเดตโฆษณาแล้ว" + createAvatarDecoration: "สร้างการตกแต่งไอคอนแล้ว" + updateAvatarDecoration: "อัปเดตการตกแต่งไอคอนแล้ว" + deleteAvatarDecoration: "ลบการตกแต่งไอคอนแล้ว" + unsetUserAvatar: "ลบไอคอนผู้ใช้" + unsetUserBanner: "ลบแบนเนอร์ผู้ใช้" _fileViewer: title: "รายละเอียดไฟล์" type: "ประเภทไฟล์" @@ -2172,14 +2385,59 @@ _fileViewer: attachedNotes: "โน้ตที่แนบมาด้วย" thisPageCanBeSeenFromTheAuthor: "หน้าเพจนี้จะสามารถปรากฏได้โดยผู้ใช้ที่อัปโหลดไฟล์นี้เท่านั้น" _externalResourceInstaller: + title: "ติดตั้งจากไซต์ภายนอก" + checkVendorBeforeInstall: "โปรดตรวจสอบให้แน่ใจว่าแหล่งแจกหน่ายมีความน่าเชื่อถือก่อนทำการติดตั้ง" _plugin: + title: "ต้องการติดตั้งปลั๊กอินนี้หรือไม่?" metaTitle: "ข้อมูลส่วนเสริม" _theme: + title: "ต้องการติดตั้งธีมนี้หรือไม่?" metaTitle: "ข้อมูลธีม" + _meta: + base: "โทนสีพื้นฐาน" _vendorInfo: title: "ข้อมูลผู้จัดจำหน่าย" + endpoint: "จุดอ้างอิงปลายทาง (Referenced endpoint)" + hashVerify: "การตรวจสอบแฮช (ความสมบูรณ์ของไฟล์)" _errors: + _invalidParams: + title: "พารามิเตอร์ไม่ถูกต้อง" + description: "มีสารสนเทศไม่เพียงพอที่จะโหลดข้อมูลจากไซต์ภายนอก โปรดยืนยัน URL ที่ป้อน" + _resourceTypeNotSupported: + title: "ไม่รองรับทรัพยากรภายนอกนี้" + description: "ไม่รองรับประเภทของทรัพยากรภายนอกนี้ โปรดติดต่อผู้ดูแลเว็บไซต์" + _failedToFetch: + title: "รับข้อมูลล้มเหลว" + fetchErrorDescription: "เกิดข้อผิดพลาดในการสื่อสารกับไซต์ภายนอก หากการลองอีกครั้งไม่สามารถแก้ไขปัญหานี้ได้ โปรดติดต่อผู้ดูแลไซต์" + parseErrorDescription: "เกิดข้อผิดพลาดในการประมวลผลข้อมูลที่โหลดจากไซต์ภายนอก โปรดติดต่อผู้ดูแลเว็บไซต์" + _hashUnmatched: + title: "การยืนยัน/ตรวจสอบข้อมูลล้มเหลว" + description: "เกิดข้อผิดพลาดในการตรวจสอบความสมบูรณ์ของข้อมูลที่ดึงมา เพื่อเป็นมาตรการรักษาความปลอดภัย การติดตั้งไม่สามารถดำเนินการต่อได้ โปรดติดต่อผู้ดูแลเว็บไซต์" _pluginParseFailed: title: "ข้อผิดพลาด AiScript" + description: "ดึงข้อมูลที่ร้องขอสำเร็จแล้ว แต่มีข้อผิดพลาดเกิดขึ้นระหว่างการแยกวิเคราะห์ AiScript โปรดติดต่อผู้เขียนปลั๊กอิน รายละเอียดข้อผิดพลาดสามารถดูได้ในคอนโซล Javascript" + _pluginInstallFailed: + title: "ติดตั้งปลั๊กอินล้มเหลว" + description: "เกิดปัญหาขณะติดตั้งปลั๊กอิน กรุณาลองอีกครั้ง. โปรดดูคอนโซล Javascript สำหรับรายละเอียดข้อผิดพลาด" _themeParseFailed: title: "การแยกวิเคราะห์ธีมล้มเหลว" + description: "ดึงข้อมูลที่ร้องขอสำเร็จแล้ว แต่มีข้อผิดพลาดเกิดขึ้นระหว่างการแยกวิเคราะห์ธีม โปรดติดต่อผู้เขียนธีม รายละเอียดข้อผิดพลาดสามารถดูได้ในคอนโซล Javascript" + _themeInstallFailed: + title: "ติดตั้งธีมล้มเหลว" + description: "เกิดปัญหาระหว่างการติดตั้งธีม กรุณาลองอีกครั้ง. รายละเอียดข้อผิดพลาดสามารถดูได้ในคอนโซล Javascript" +_dataSaver: + _media: + title: "โหลดมีเดีย" + description: "กันไม่ให้ภาพและวิดีโอโหลดโดยอัตโนมัติ แตะรูปภาพ/วิดีโอที่ซ่อนอยู่เพื่อโหลด" + _avatar: + title: "รูปไอคอน" + description: "ระงับการเคลื่อนไหวของภาพไอคอน ภาพเคลื่อนไหวอาจมีขนาดไฟล์ใหญ่กว่าภาพปกติ ดังนั้นจึงสามารถช่วยในการลดการใช้ข้อมูล" + _urlPreview: + title: "ธัมบ์เนลแสดงตัวอย่าง URL" + description: "ธัมบ์เนลแสดงตัวอย่าง URL จะไม่โหลดโดยอัตโนมัติ" + _code: + title: "ไฮไลต์โค้ด" + description: "หากใช้สัญลักษณ์ไฮไลต์โค้ดใน MFM ฯลฯ สัญลักษณ์เหล่านั้นจะไม่โหลดจนกว่าจะแตะ การไฮไลต์ไวยากรณ์(syntax)จำเป็นต้องดาวน์โหลดไฟล์คำจำกัดความของไฮไลต์สำหรับแต่ละภาษา ดังนั้นการปิดใช้งานการโหลดไฟล์เหล่านี้โดยอัตโนมัติจึงคาดว่าจะช่วยลดปริมาณข้อมูลการสื่อสารได้" +_reversi: + total: "รวมทั้งหมด" + diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml index 0793592d3..e93a6e43e 100644 --- a/locales/tr-TR.yml +++ b/locales/tr-TR.yml @@ -455,3 +455,4 @@ _deck: _moderationLogTypes: suspend: "askıya al" resetPassword: "Şifre sıfırlama" + diff --git a/locales/ug-CN.yml b/locales/ug-CN.yml index e48f64511..e06cee11a 100644 --- a/locales/ug-CN.yml +++ b/locales/ug-CN.yml @@ -17,3 +17,4 @@ _2fa: renewTOTPCancel: "ئۇنى توختىتىڭ" _widgets: profile: "profile" + diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml index 9b609edeb..7679ad56d 100644 --- a/locales/uk-UA.yml +++ b/locales/uk-UA.yml @@ -352,6 +352,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Увімкнути hCaptcha" hcaptchaSiteKey: "Ключ сайту" hcaptchaSecretKey: "Секретний ключ" +mcaptchaSiteKey: "Ключ сайту" +mcaptchaSecretKey: "Секретний ключ" recaptcha: "reCAPTCHA" enableRecaptcha: "Увімкнути reCAPTCHA" recaptchaSiteKey: "Ключ сайту" @@ -910,6 +912,7 @@ icon: "Аватар" replies: "Відповісти" renotes: "Поширити" flip: "Перевернути" +lastNDays: "Останні {n} днів" _achievements: earnedAt: "Відкрито" _types: @@ -1468,6 +1471,7 @@ _profile: changeBanner: "Змінити банер" _exportOrImport: allNotes: "Всі нотатки" + clips: "Добірка" followingList: "Підписки" muteList: "Ігнорувати" blockingList: "Заблокувати" @@ -1616,3 +1620,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Призупинити" resetPassword: "Скинути пароль" +_reversi: + total: "Всього" + diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml index 3d00e4739..a79a76066 100644 --- a/locales/uz-UZ.yml +++ b/locales/uz-UZ.yml @@ -366,6 +366,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "hCaptchani yoqish" hcaptchaSiteKey: "Sayt kaliti" hcaptchaSecretKey: "Mahfiy kalit" +mcaptchaSiteKey: "Sayt kaliti" +mcaptchaSecretKey: "Maxfiy kalit" recaptcha: "reCAPTCHA" enableRecaptcha: "reCAPTCHA ni yoqish" recaptchaSiteKey: "Sayt kaliti" @@ -973,6 +975,7 @@ _profile: changeBanner: "Bannerni o'zgartirish" _exportOrImport: allNotes: "Barcha qaydlar" + clips: "Klip" followingList: "Obuna bo‘lish" muteList: "Ovozni o‘chirish" blockingList: "Bloklangan foydalanuvchilar" @@ -1085,3 +1088,6 @@ _webhookSettings: _moderationLogTypes: suspend: "To'xtatish" resetPassword: "Parolni tiklash" +_reversi: + total: "Jami" + diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml index e4f7d2742..0dcf08649 100644 --- a/locales/vi-VN.yml +++ b/locales/vi-VN.yml @@ -368,6 +368,8 @@ hcaptcha: "hCaptcha" enableHcaptcha: "Bật hCaptcha" hcaptchaSiteKey: "Khóa của trang" hcaptchaSecretKey: "Khóa bí mật" +mcaptchaSiteKey: "Khóa của trang" +mcaptchaSecretKey: "Khóa bí mật" recaptcha: "reCAPTCHA" enableRecaptcha: "Bật reCAPTCHA" recaptchaSiteKey: "Khóa của trang" @@ -1044,6 +1046,7 @@ pinnedList: "Các mục đã được ghim" keepScreenOn: "Giữ màn hình luôn bật" verifiedLink: "Chúng tôi đã xác nhận bạn là chủ sở hữu của đường dẫn này" flip: "Lật" +lastNDays: "{n} ngày trước" _announcement: forExistingUsers: "Chỉ những người dùng đã tồn tại" forExistingUsersDescription: "Nếu được bật, thông báo này sẽ chỉ hiển thị với những người dùng đã tồn tại vào lúc thông báo được tạo. Nếu tắt đi, những tài khoản mới đăng ký sau khi thông báo được đăng lên cũng sẽ thấy nó." @@ -1669,6 +1672,7 @@ _profile: _exportOrImport: allNotes: "Toàn bộ tút" favoritedNotes: "Bài viết đã thích" + clips: "Lưu bài viết" followingList: "Đang theo dõi" muteList: "Ẩn" blockingList: "Chặn" @@ -1846,3 +1850,6 @@ _webhookSettings: _moderationLogTypes: suspend: "Vô hiệu hóa" resetPassword: "Đặt lại mật khẩu" +_reversi: + total: "Tổng cộng" + diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index bfacc03e0..d14a77f5a 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -121,14 +121,21 @@ sensitive: "敏感内容" add: "添加" reaction: "回应" reactions: "回应" +emojiPicker: "表情符号选择器" +pinnedEmojisForReactionSettingDescription: "可以设置发表回应时置顶显示的表情符号" +pinnedEmojisSettingDescription: "可以设置输入表情符号时置顶显示的表情符号" +emojiPickerDisplay: "选择器显示设置" +overwriteFromPinnedEmojisForReaction: "从「置顶(回应)」设置覆盖" +overwriteFromPinnedEmojis: "从全局设置覆盖" reactionSettingDescription2: "拖动重新排序,单击删除,点击 + 添加。" rememberNoteVisibility: "保存上次设置的可见性" attachCancel: "删除附件" +deleteFile: "删除文件" markAsSensitive: "标记为敏感内容" unmarkAsSensitive: "取消标记为敏感内容" enterFileName: "输入文件名" mute: "屏蔽" -unmute: "解除屏蔽" +unmute: "解除静音" renoteMute: "屏蔽转帖" renoteUnmute: "解除屏蔽转帖" block: "拉黑" @@ -209,15 +216,15 @@ instanceInfo: "服务器信息" statistics: "统计" clearQueue: "清除队列" clearQueueConfirmTitle: "确定清除队列?" -clearQueueConfirmText: "未送达的帖子将不会投递。 通常,您不需要这样做。" +clearQueueConfirmText: "未送达的帖子将不会被投递。 通常无需执行此操作。" clearCachedFiles: "清除缓存" -clearCachedFilesConfirm: "确定要清除缓存文件?" +clearCachedFilesConfirm: "确定要清除所有缓存的远程文件?" blockedInstances: "被封锁的服务器" blockedInstancesDescription: "设定要封锁的服务器,以换行来进行分割。被封锁的服务器将无法与本服务器进行交换通讯。子域名也同样会被封锁。" -silencedInstances: "沉默的服务器" -silencedInstancesDescription: "设置要静音的服务器的主机,以换行符分隔。属于静默服务器的所有帐户都将被视为“静默”,所有关注都将成为请求,并且您将无法提及非关注者的本地帐户。被阻止的实例不受影响。" -muteAndBlock: "屏蔽/拉黑" -mutedUsers: "已屏蔽用户" +silencedInstances: "被静音的服务器" +silencedInstancesDescription: "设置要静音的服务器,以换行符分隔。被静音的服务器内所有的账户将默认处于「静音」状态,仅能发送关注请求,并且在未关注状态下无法提及本地账户。被阻止的实例不受影响。" +muteAndBlock: "静音/拉黑" +mutedUsers: "已静音用户" blockedUsers: "已拉黑的用户" noUsers: "无用户" editProfile: "编辑资料" @@ -260,6 +267,7 @@ removed: "已删除" removeAreYouSure: "要删掉「{x}」吗?" deleteAreYouSure: "要删掉「{x}」吗?" resetAreYouSure: "恢复默认设置?" +areYouSure: "你确定吗?" saved: "已保存" messaging: "聊天" upload: "本地上传" @@ -352,7 +360,7 @@ connectService: "连接" disconnectService: "断开连接" enableLocalTimeline: "启用本地时间线" enableGlobalTimeline: "启用全局时间线" -disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和协作者也可以继续使用。" +disablingTimelinesInfo: "即使时间线功能被禁用,出于方便,管理员和监察员也可以继续使用。" registration: "注册" enableRegistration: "允许任何人注册" invite: "邀请" @@ -372,15 +380,20 @@ hcaptcha: "hCaptcha" enableHcaptcha: "启用 hCaptcha" hcaptchaSiteKey: "网站密钥" hcaptchaSecretKey: "hCaptcha 密钥(SecretKey)" +mcaptcha: "mCaptcha" +enableMcaptcha: "启用 mCaptcha" +mcaptchaSiteKey: "网站密钥" +mcaptchaSecretKey: "mCaptcha 密钥(SecretKey)" +mcaptchaInstanceUrl: "mCaptcha 实例地址" recaptcha: "reCAPTCHA" enableRecaptcha: "启用 reCAPTCHA\n(请注意, 此功能在中国大陆不可用. 如果启用, 可能导致无法正常使用登录或注册等功能)" recaptchaSiteKey: "网站密钥" -recaptchaSecretKey: "reCAPTCHA 密钥(SecretKey)" +recaptchaSecretKey: "mCaptcha 密钥(SecretKey)" turnstile: "Turnstile" enableTurnstile: "启用 Turnstile" turnstileSiteKey: "网站密钥" turnstileSecretKey: "Turnstile 密钥(SecretKey)" -avoidMultiCaptchaConfirm: "使用多种验证方式可能会造成干扰,您要禁用其他验证方式吗?您可以按“取消”按钮,继续保持启用多种验证方式。" +avoidMultiCaptchaConfirm: "使用多个 Captcha 可能会互相干扰,您要禁用其它 Captcha 吗?您可以按“取消”按钮,继续保持启用多种验证方式。" antennas: "天线" manageAntennas: "天线管理" name: "名称" @@ -477,7 +490,7 @@ or: "或者" language: "语言" uiLanguage: "显示语言" aboutX: "关于 {x}" -emojiStyle: "emoji 的样式" +emojiStyle: "表情符号的样式" native: "原生" disableDrawer: "不显示抽屉菜单" showNoteActionsOnlyHover: "仅在悬停时显示帖子操作" @@ -514,7 +527,7 @@ showFeaturedNotesInTimeline: "在时间线上显示热门推荐" objectStorage: "对象存储" useObjectStorage: "使用对象存储" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "这里是用于参考的 URL,如果您正在使用 CDN 或反向代理,请指定其 URL,例如 S3:“https://.s3.amazonaws.com”,GCS:“https://storage.googleapis.com/”" +objectStorageBaseUrlDesc: "用于参考的 URL,如果您正在使用 CDN 或 Proxy,请填入服务商提供的 URL;S3:“https://.s3.amazonaws.com”;GCS:“https://storage.googleapis.com/”" objectStorageBucket: "存储桶" objectStorageBucketDesc: "请指定使用的对象存储服务的存储桶名称。" objectStoragePrefix: "前缀" @@ -533,6 +546,7 @@ serverLogs: "服务器日志" deleteAll: "全部删除" showFixedPostForm: "在时间线顶部显示发帖框" showFixedPostFormInChannel: "在时间线顶部显示发帖对话框(频道)" +withRepliesByDefaultForNewlyFollowed: "在时间线中默认包含新关注用户的回复" newNoteRecived: "有新的帖子" sounds: "提示音" sound: "提示音" @@ -542,6 +556,8 @@ showInPage: "在页面中显示" popout: "弹窗" volume: "音量" masterVolume: "主音量" +notUseSound: "静音" +useSoundOnlyWhenActive: "仅在 Misskey 活跃时输出声音" details: "详情" chooseEmoji: "选择表情符号" unableToProcess: "操作无法完成" @@ -562,10 +578,14 @@ output: "输出" script: "脚本" disablePagesScript: "禁用页面脚本" updateRemoteUser: "更新远程用户信息" +unsetUserAvatar: "清除头像" +unsetUserAvatarConfirm: "要清除头像吗?" +unsetUserBanner: "清除横幅" +unsetUserBannerConfirm: "要清除横幅吗?" deleteAllFiles: "删除所有文件" deleteAllFilesConfirm: "要删除所有文件吗?" removeAllFollowing: "取消所有关注" -removeAllFollowingDescription: "取消 {host} 的所有关注者。当服务器不再存在时执行。" +removeAllFollowingDescription: "取消来自 {host} 的所有关注者。当服务器不再存在时执行。" userSuspended: "该用户已被冻结。" userSilenced: "该用户已被禁言。" yourAccountSuspendedTitle: "账户已被冻结" @@ -612,6 +632,7 @@ medium: "中" small: "小" generateAccessToken: "生成访问令牌" permission: "权限" +adminPermission: "管理员权限" enableAll: "启用全部" disableAll: "禁用全部" tokenRequested: "允许访问账户" @@ -633,6 +654,7 @@ smtpSecure: "在 SMTP 连接中使用隐式 SSL / TLS" smtpSecureInfo: "使用 STARTTLS 时关闭。" testEmail: "邮件发送测试" wordMute: "文字屏蔽" +hardWordMute: "屏蔽关键词" regexpError: "正则表达式错误" regexpErrorDescription: "{tab} 屏蔽文字的第 {line} 行的正则表达式有错误:" instanceMute: "被屏蔽的服务器" @@ -654,6 +676,7 @@ useGlobalSettingDesc: "启用时,将使用账户通知设置。关闭时,则 other: "其他" regenerateLoginToken: "重新生成登录令牌" regenerateLoginTokenDescription: "重新生成用于登录的内部令牌。通常您不需要这样做。重新生成后,您将在所有设备上登出。" +theKeywordWhenSearchingForCustomEmoji: "这将是搜素自定义表情符号时的关键词。" setMultipleBySeparatingWithSpace: "您可以使用空格分隔多个项目。" fileIdOrUrl: "文件 ID 或者 URL" behavior: "行为" @@ -866,6 +889,8 @@ makeReactionsPublicDescription: "将您发表过的回应设置成公开可见 classic: "经典" muteThread: "屏蔽帖子列表" unmuteThread: "取消屏蔽帖子列表" +followingVisibility: "关注的人的公开范围" +followersVisibility: "关注者的公开范围" continueThread: "查看更多帖子" deleteAccountConfirm: "将要删除账户。是否确认?" incorrectPassword: "密码错误" @@ -949,7 +974,7 @@ unsubscribePushNotification: "停用推送通知消息" pushNotificationAlreadySubscribed: "推送通知消息已启用" pushNotificationNotSupported: "浏览器或服务器不支持推送通知消息" sendPushNotificationReadMessage: "删除已读推送通知消息" -sendPushNotificationReadMessageCaption: "“{emptyPushNotificationMessage}”的通知消息将会显示。您终端设备的电池消耗可能会增加。" +sendPushNotificationReadMessageCaption: "您终端设备的电池消耗可能会增加。" windowMaximize: "最大化" windowMinimize: "最小化" windowRestore: "还原" @@ -975,6 +1000,7 @@ assign: "分配" unassign: "取消分配" color: "颜色" manageCustomEmojis: "管理自定义表情符号" +manageAvatarDecorations: "管理头像挂件" youCannotCreateAnymore: "抱歉,您无法再创建更多了。" cannotPerformTemporary: "暂时不可用" cannotPerformTemporaryDescription: "因操作过于频繁,暂时不可用,请稍后再试。" @@ -1015,6 +1041,8 @@ resetPasswordConfirm: "确定重置密码?" sensitiveWords: "敏感词" sensitiveWordsDescription: "将包含设置词的帖子的可见范围设置为首页。可以通过用换行符分隔来设置多个。" sensitiveWordsDescription2: "AND 条件用空格分隔,正则表达式用斜线包裹。" +hiddenTags: "隐藏标签" +hiddenTagsDescription: "设定的标签将不会在时间线上显示。可使用换行来设置多个标签。" notesSearchNotAvailable: "帖子检索不可用" license: "许可信息" unfavoriteConfirm: "确定要取消收藏吗?" @@ -1027,9 +1055,12 @@ enableChartsForRemoteUser: "生成远程用户的图表" enableChartsForFederatedInstances: "生成远程服务器的图表" showClipButtonInNoteFooter: "在贴文下方显示便签按钮" reactionsDisplaySize: "回应显示大小" +limitWidthOfReaction: "限制回应的最大宽度,并将其缩小显示" noteIdOrUrl: "帖子 ID 或 URL" video: "视频" videos: "视频" +audio: "音频" +audioFiles: "音频" dataSaver: "省流量模式" accountMigration: "账户迁移" accountMoved: "此用户已迁移账户" @@ -1126,19 +1157,67 @@ edited: "已编辑" notificationRecieveConfig: "通知接收设置" mutualFollow: "互相关注" fileAttachedOnly: "仅限媒体" -showRepliesToOthersInTimeline: "在时间线上显示给其他人的回复" -hideRepliesToOthersInTimeline: "在时间线上隐藏给其他人的回复" +showRepliesToOthersInTimeline: "在时间线中包含给别人的回复" +hideRepliesToOthersInTimeline: "在时间线中隐藏给别人的回复" +showRepliesToOthersInTimelineAll: "在时间线中包含现在关注的所有人的回复" +hideRepliesToOthersInTimelineAll: "在时间线中隐藏现在关注的所有人的回复" +confirmShowRepliesAll: "此操作不可撤销。确认要在时间线中包含现在关注的所有人的回复吗?" +confirmHideRepliesAll: "此操作不可撤销。确认要在时间线中隐藏现在关注的所有人的回复吗?" +externalServices: "外部服务" +impressum: "运营商信息" +impressumUrl: "运营商信息地址" +impressumDescription: "德国等国家和地区有义务展示此类信息(Impressum)。" +privacyPolicy: "隐私政策" +privacyPolicyUrl: "隐私政策地址" +tosAndPrivacyPolicy: "服务条款及隐私政策" avatarDecorations: "头像挂件" +attach: "佩戴" +detach: "卸下" +detachAll: "全部卸下" +angle: "角度" flip: "翻转" +showAvatarDecorations: "显示头像挂件" +releaseToRefresh: "松开以刷新" +refreshing: "刷新中" +pullDownToRefresh: "下拉以刷新" +disableStreamingTimeline: "禁止实时更新时间线" +useGroupedNotifications: "分组显示通知" +signupPendingError: "确认电子邮件时出现错误。链接可能已过期。" +cwNotationRequired: "在启用「隐藏内容」时必须输入注释" +doReaction: "回应" +reloadRequiredToApplySettings: "需要重新载入来使设置生效" +remainingN: "剩余:{n}" +overwriteContentConfirm: "将覆盖现有内容。确定吗?" +seasonalScreenEffect: "应景的画面效果" +decorate: "装饰" +addMfmFunction: "添加装饰" +enableQuickAddMfmFunction: "显示高级 MFM 选择器" +sfx: "音效" +soundWillBePlayed: "声音将会播放" +showReplay: "查看重播" +replay: "重播" +replaying: "重播中" +ranking: "排行榜" +lastNDays: "最近 {n} 天" +backToTitle: "返回标题" +hemisphere: "居住地区" +withSensitive: "显示包含敏感媒体的帖子" +enableHorizontalSwipe: "滑动切换标签页" +_bubbleGame: + howToPlay: "游戏说明" _announcement: forExistingUsers: "仅限现有用户" forExistingUsersDescription: "若启用,该公告将仅对创建此公告时存在的用户可见。 如果禁用,则在创建此公告后注册的用户也可以看到该公告。" needConfirmationToRead: "需要确认才能标记为已读" needConfirmationToReadDescription: "若启用,则会在标记已读时会显示确认对话框。此外,它也会不受批量已读操作的影响。" end: "结束公告" - tooManyActiveAnnouncementDescription: "若有大量活动公告,可能会造成用户体验可能下降。请考虑归档已完成的公告。" + tooManyActiveAnnouncementDescription: "若有大量活动公告,可能会造成用户体验下降。请考虑归档已完成的公告。" readConfirmTitle: "标记为已读?" readConfirmText: "阅读“{title}”的内容并将其标记为已读。" + shouldNotBeUsedToPresentPermanentInfo: "我们建议使用公告来发布临时性的流动信息而不是固定的常规信息,因为这可能损害用户体验,尤其是对于新用户而言。" + dialogAnnouncementUxWarn: "同时存在 2 个或以上的对话框公告极有可能对用户体验产生负面的影响,建议谨慎使用。" + silence: "不发送通知" + silenceDescription: "开启后,此条公告将不会发送通知,也不强制用户阅读。" _initialAccountSetting: accountCreated: "账户创建完成了!" letsStartAccountSetup: "来进行帐户的初始设置吧。" @@ -1151,19 +1230,91 @@ _initialAccountSetting: pushNotificationDescription: "启用推送通知的话,就可以在设备上接收来自 {name} 的通知了。" initialAccountSettingCompleted: "初始设定已经完成了!" haveFun: "希望 {name} 在这里玩得开心!" + youCanContinueTutorial: "您可以继续了解 {name}(Misskey) 的使用教程,也可以在此停止教程并立即开始使用它。\n" + startTutorial: "开始教学" skipAreYouSure: "要跳过初始设置吗?" laterAreYouSure: "要稍后再进行初始设定吗?" +_initialTutorial: + launchTutorial: "观看教学" + title: "教学" + wellDone: "做得好" + skipAreYouSure: "是否退出教学?" + _landing: + title: "欢迎来到教学" + description: "在这里,您可以查看 Misskey 的基本使用方法和功能。" + _note: + title: "什么是帖子?" + description: "在 Misskey 上发表的文章称为「帖子」。帖子在时间线上按照时间顺序排列,并实时更新。" + reply: "用来回复帖子。可以对回复进行回复,从而形成一串对话。" + renote: "用来将帖子共享到自己的时间线上。也可以加上自己的文字然后引用它。" + reaction: "用来添加回应。详细信息将在下一页进行说明。" + menu: "用来进行例如显示帖子详情、复制链接等各种各样的操作。" + _reaction: + title: "什么是回应?" + description: "您可以在帖子中添加“回应”。 您可以使用反应轻松地表达点“赞”所无法传达的细微差别。" + letsTryReacting: "回应可以通过点击帖子中的「+」按钮来添加。试着给这个示例帖子添加一个回应!" + reactToContinue: "添加一个回应来继续" + reactNotification: "当您的帖子被某人添加了回应时,将实时收到通知。" + reactDone: "通过按下「ー」按钮,可以取消已经添加的回应" + _timeline: + title: "时间线的运作方式" + description1: "Misskey 根据使用方式提供了多个时间线(根据服务器的设定,可能有一些被禁用)。" + home: "可以查看您关注的账户的帖子。" + local: "可以查看这个服务器上所有用户发表的帖子。" + social: "将同时显示首页时间线和本地时间线的内容。" + global: "可以查看所有已联合的服务器上的帖子。" + description2: "可以随时在屏幕顶部在每个时间线之间切换。" + description3: "另外,还有列表时间线和频道时间线。请参阅{link}了解更多详细信息。" + _postNote: + title: "帖子发布设置" + description1: "在 Misskey 发布帖子时,您可以设置各种选项。发帖窗口看起来是这样的。\n" + _visibility: + description: "您可以限制谁可以看到您的帖子。" + public: "向所有用户公开。\n" + home: "仅在首页时间线上发布。 关注者、从个人资料页查看过来的用户、以及通过转帖也能被别的用户看见。" + followers: "仅对关注者可见。 除了您自己之外,没有人可以转贴,并且只有您的关注者可以查看它。\n" + direct: "它将仅向指定用户公开,并且他们也会收到通知。 您可以使用它来代替私信。\n" + doNotSendConfidencialOnDirect1: "发送敏感信息时请注意。\n" + doNotSendConfidencialOnDirect2: "目标服务器的管理员可以看到发布的内容,因此如果您向不受信任的服务器上的用户发送私信,则在处理敏感信息时需要小心。" + localOnly: "不将帖子推送到其它服务器。 无论上述公开范围如何,其它服务器的用户将无法看到附加了此设定的帖子。\n" + _cw: + title: "隐藏内容 (CW)\n" + description: "显示「注解」里的内容而不是正文。点击「查看更多」将会把正文显示出来。" + _exampleNote: + cw: "深夜报复社会" + note: "茨了带巧克力的甜甜圈🍩😋" + useCases: "用于服务器条款所规定的帖子,或对剧透内容和敏感内容进行自主规制。" + _howToMakeAttachmentsSensitive: + title: "如何将附件标注为敏感内容?" + description: "对于服务器方针所要求要求的,又或者不适合直接展示的附件,请添加「敏感」标记。\n" + tryThisFile: "试试看,将附加到此窗口的图像标注为敏感!" + _exampleNote: + note: "不该打开纳豆的盖子的……" + method: "要标注附件为敏感内容,请单击该文件以打开菜单,然后单击“设置为敏感”。" + sensitiveSucceeded: "附加文件时,请遵循服务器的条款来设置正确敏感设定。\n" + doItToContinue: "将图像标记为敏感后才能够继续" + _done: + title: "恭喜您,已经完成了教程🎉\n" + description: "这里介绍的只是其中一小部分的功能。 要了解更多有关如何使用 Misskey 的更多信息,请访问 {link}。" +_timelineDescription: + home: "首页时间线可以查看您关注的账户的帖子。" + local: "本地时间线可以查看这个服务器上所有用户发表的帖子。" + social: "社交时间线将同时显示首页时间线和本地时间线的内容。" + global: "全局时间线可以查看所有已联合的服务器上的帖子。" _serverRules: description: "在新用户注册前显示服务器的简单规则。推荐显示服务条款的主要内容。" _serverSettings: iconUrl: "图标 URL" appIconDescription: "指定当 {host} 显示为 app 时的图标。" - appIconUsageExample: "例如:作为书签添加到 PWA 或手机主屏幕的时候" + appIconUsageExample: "如作为书签添加到 PWA 或手机主屏幕时" appIconStyleRecommendation: "因为有可能会被裁切为圆形或者圆角矩形,建议使用边缘带有留白背景的图标。" appIconResolutionMustBe: "分辨率必须为 {resolution}。" manifestJsonOverride: "覆盖 manifest.json" shortName: "简称" shortNameDescription: "如果服务器的正式名称很长,可以用简称或者別名来替代。" + fanoutTimelineDescription: "当启用时,可显著提高获取各种时间线时的性能,并减轻数据库的负荷。但是相对的 Redis 的内存使用量将会增加。如果服务器的内存不是很大,又或者运行不稳定的话可以把它关掉。" + fanoutTimelineDbFallback: "回退到数据库" + fanoutTimelineDbFallbackDescription: "当启用时,若时间线未被缓存,则将额外查询数据库。禁用该功能可通过不执行回退处理进一步减少服务器负载,但会限制可检索的时间线范围。" _accountMigration: moveFrom: "从别的账号迁移到此账户" moveFromSub: "为另一个账户建立别名" @@ -1390,7 +1541,7 @@ _achievements: description: "点了这里" _justPlainLucky: title: "超高校级的幸运" - description: "每 10 秒有 0.01 的概率自动获得" + description: "每 10 秒有 0.005% 的概率自动获得" _setNameToSyuilo: title: "像神一样呐" description: "将名称设定为 syuilo" @@ -1421,6 +1572,11 @@ _achievements: _smashTestNotificationButton: title: "过度测试" description: "短时间内连续测试通知" + _tutorialCompleted: + title: "Misskey 初学者课程 结业证书" + description: "完成了教学" + _bubbleGameExplodingHead: + title: "🤯" _role: new: "创建角色" edit: "编辑角色" @@ -1431,7 +1587,9 @@ _role: assignTarget: "授权对象" descriptionOfAssignTarget: "手动指手动选择谁被包括在这个角色中。\n符合条件指设置条件以自动包括符合条件的用户。" manual: "手动" + manualRoles: "手动角色" conditional: "符合条件" + conditionalRoles: "条件角色" condition: "条件" isConditionalRole: "这是一个条件控制的角色。" isPublic: "角色公开" @@ -1464,6 +1622,7 @@ _role: inviteLimitCycle: "邀请码的发行间隔" inviteExpirationTime: "邀请码的有效日期" canManageCustomEmojis: "管理自定义表情符号" + canManageAvatarDecorations: "管理头像挂件" driveCapacity: "网盘容量" alwaysMarkNsfw: "总是将文件标记为 NSFW" pinMax: "帖子置顶数量限制" @@ -1478,6 +1637,8 @@ _role: descriptionOfRateLimitFactor: "值越小限制越少,值越大限制越多。" canHideAds: "可以隐藏广告" canSearchNotes: "是否可以搜索帖子" + canUseTranslator: "使用翻译功能" + avatarDecorationLimit: "可添加头像挂件的最大个数" _condition: isLocal: "是本地用户" isRemote: "是远程用户" @@ -1506,6 +1667,7 @@ _emailUnavailable: disposable: "不是永久可用的地址" mx: "邮件服务器不正确" smtp: "邮件服务器没有响应" + banned: "无法使用此邮件地址注册" _ffVisibility: public: "公开" followers: "只有关注你的用户能看到" @@ -1526,6 +1688,10 @@ _ad: reduceFrequencyOfThisAd: "减少此广告的频率" hide: "不显示" timezoneinfo: "星期几是由服务器的时区所指定的。" + adsSettings: "广告设置" + notesPerOneAd: "在实时更新时间线中插入广告的间隔(帖子个数)" + setZeroToDisable: "设为 0 将不在实时更新时间线中投放广告" + adsTooClose: "广告投放时间间隔过短将可能显著损害用户体验。" _forgotPassword: enterEmail: "请输入您设置的电子邮箱地址,密码重置链接将发送至该邮箱上。" ifNoEmail: "如果您没有设置电子邮件地址,请联系管理员。" @@ -1565,8 +1731,8 @@ _preferencesBackups: invalidFile: "无效的的文件格式。" _registry: scope: "范围" - key: "主要" - keys: "主要" + key: "键" + keys: "键" domain: "域" createKey: "创建键" _aboutMisskey: @@ -1578,6 +1744,7 @@ _aboutMisskey: donate: "赞助 Misskey" morePatrons: "还有很多其它的人也在支持我们,非常感谢🥰" patrons: "支持者" + projectMembers: "项目成员" _displayOfSensitiveMedia: respect: "隐藏敏感媒体" ignore: "显示敏感媒体" @@ -1602,6 +1769,7 @@ _channel: notesCount: "有 {n} 个帖子" nameAndDescription: "名称与描述" nameOnly: "仅名称" + allowRenoteToExternal: "允许在频道外转帖及引用" _menuDisplay: sideFull: "横向" sideIcon: "横向(图标)" @@ -1693,6 +1861,14 @@ _sfx: notification: "通知" antenna: "天线接收" channel: "频道通知" + reaction: "选择回应时" +_soundSettings: + driveFile: "使用网盘内的音频" + driveFileWarn: "选择网盘上的文件" + driveFileTypeWarn: "不支持此文件" + driveFileTypeWarnDescription: "请选择音频文件" + driveFileDurationWarn: "音频过长" + driveFileDurationWarnDescription: "使用长音频可能会影响 Misskey 的使用。即使这样也要继续吗?" _ago: future: "未来" justNow: "最近" @@ -1706,7 +1882,12 @@ _ago: invalid: "没有" _timeIn: seconds: "{n}秒后" + minutes: "{n} 分后" + hours: "{n} 小时后" days: "{n}天后" + weeks: "{n} 周后" + months: "{n} 月后" + years: "{n} 年后" _time: second: "秒" minute: "分" @@ -1778,6 +1959,55 @@ _permissions: "write:flash": "编辑 Play" "read:flash-likes": "查看 Play 的点赞" "write:flash-likes": "编辑 Play 的点赞列表" + "read:admin:abuse-user-reports": "查看来自用户的举报" + "write:admin:delete-account": "删除用户账户" + "write:admin:delete-all-files-of-a-user": "删除用户所有的文件" + "read:admin:index-stats": "查看数据库索引相关的信息" + "read:admin:table-stats": "查看数据库表相关的信息" + "read:admin:user-ips": "查看用户 IP 地址" + "read:admin:meta": "查看实例的元数据" + "write:admin:reset-password": "重置用户密码" + "write:admin:resolve-abuse-user-report": "将来自用户的报告标记为「已解决」" + "write:admin:send-email": "发送邮件" + "read:admin:server-info": "查看服务器信息" + "read:admin:show-moderation-log": "查看管理日志" + "read:admin:show-user": "查看用户的非公开信息" + "read:admin:show-users": "查看用户的非公开信息" + "write:admin:suspend-user": "冻结用户" + "write:admin:unset-user-avatar": "删除用户头像" + "write:admin:unset-user-banner": "删除用户横幅" + "write:admin:unsuspend-user": "解除用户冻结" + "write:admin:meta": "编辑实例元数据" + "write:admin:user-note": "编辑管理笔记" + "write:admin:roles": "编辑角色" + "read:admin:roles": "查看角色" + "write:admin:relays": "编辑中继" + "read:admin:relays": "查看中继" + "write:admin:invite-codes": "编辑邀请码" + "read:admin:invite-codes": "查看邀请码" + "write:admin:announcements": "编辑公告" + "read:admin:announcements": "查看公告" + "write:admin:avatar-decorations": "编辑头像挂件" + "read:admin:avatar-decorations": "查看头像挂件" + "write:admin:federation": "编辑联合相关信息" + "write:admin:account": "编辑用户账户" + "read:admin:account": "查看用户相关情报" + "write:admin:emoji": "编辑表情文字" + "read:admin:emoji": "查看表情文字" + "write:admin:queue": "编辑作业队列" + "read:admin:queue": "查看作业队列相关情报" + "write:admin:promo": "运营推广说明" + "write:admin:drive": "编辑用户网盘" + "read:admin:drive": "查看用户网盘相关情报" + "read:admin:stream": "使用管理员用的 Websocket API" + "write:admin:ad": "编辑广告" + "read:admin:ad": "查看广告" + "write:invite-codes": "发行邀请码" + "read:invite-codes": "获取已发行的邀请码" + "write:clip-favorite": "编辑便签的点赞" + "read:clip-favorite": "查看便签的点赞" + "read:federation": "查看联合相关信息" + "write:report-abuse": "举报用户" _auth: shareAccessTitle: "应用程序授权许可" shareAccess: "您要授权允许 “{name}” 访问您的帐户吗?" @@ -1832,6 +2062,7 @@ _widgets: _userList: chooseList: "选择列表" clicker: "点击器" + birthdayFollowings: "今天是他们的生日" _cw: hide: "隐藏" show: "查看更多" @@ -1894,15 +2125,18 @@ _profile: changeAvatar: "修改头像" changeBanner: "修改横幅" verifiedLinkDescription: "如果将内容设置为 URL,当链接所指向的网页内包含自己的个人资料链接时,可以显示一个已验证图标。" + avatarDecorationMax: "最多可添加 {max} 个挂件" _exportOrImport: allNotes: "所有帖子" favoritedNotes: "收藏的帖子" + clips: "便签" followingList: "关注中" muteList: "屏蔽" blockingList: "拉黑" userLists: "列表" excludeMutingUsers: "排除屏蔽用户" excludeInactiveUsers: "排除不活跃用户" + withReplies: "在时间线中包含导入用户的回复" _charts: federation: "联合" apRequest: "请求" @@ -2014,12 +2248,16 @@ _notification: pollEnded: "问卷调查结果已生成。" newNote: "新的帖子" unreadAntennaNote: "天线 {name}" + roleAssigned: "授予的角色" emptyPushNotificationMessage: "推送通知已更新" achievementEarned: "获得成就" testNotification: "测试通知" checkNotificationBehavior: "检查通知显示" sendTestNotification: "发送测试通知" notificationWillBeDisplayedLikeThis: "通知将会这样表示" + reactedBySomeUsers: "{n} 人回应了" + renotedBySomeUsers: "{n} 人转发了" + followedBySomeUsers: "被 {n} 人关注" _types: all: "全部" note: "用户的新帖子" @@ -2032,6 +2270,7 @@ _notification: pollEnded: "问卷调查结束" receiveFollowRequest: "收到关注请求" followRequestAccepted: "关注请求已通过" + roleAssigned: "授予的角色" achievementEarned: "取得的成就" app: "关联应用的通知" _actions: @@ -2114,6 +2353,8 @@ _moderationLogTypes: deleteGlobalAnnouncement: "删除全体通知" deleteUserAnnouncement: "删除用户通知" resetPassword: "重置密码" + suspendRemoteInstance: "停止远程服务器" + unsuspendRemoteInstance: "恢复远程服务器" markSensitiveDriveFile: "标记网盘文件为敏感媒体" unmarkSensitiveDriveFile: "取消标记网盘文件为敏感媒体" resolveAbuseReport: "处理举报" @@ -2121,10 +2362,81 @@ _moderationLogTypes: createAd: "创建了广告" deleteAd: "删除了广告" updateAd: "更新了广告" + createAvatarDecoration: "新建头像挂件" + updateAvatarDecoration: "更新头像挂件" + deleteAvatarDecoration: "删除头像挂件" + unsetUserAvatar: "清除用户头像" + unsetUserBanner: "清除用户横幅" _fileViewer: + title: "文件信息" + type: "文件类型" + size: "文件大小" url: "URL" uploadedAt: "添加日期" + attachedNotes: "附加到的帖子" + thisPageCanBeSeenFromTheAuthor: "此页只能被该文件的上传者查看。" _externalResourceInstaller: + title: "从外部站点安装" + checkVendorBeforeInstall: "请在安装前确保来源可靠" + _plugin: + title: "要安装此插件吗?" + metaTitle: "插件信息" + _theme: + title: "要安装此主题吗?" + metaTitle: "主题信息" + _meta: + base: "基本配色方案" + _vendorInfo: + title: "来源信息" + endpoint: "参考端点" + hashVerify: "确认文件完整性" _errors: + _invalidParams: + title: "缺少参数" + description: "缺少从外部站点获取数据所需的信息。请检查 URL。" + _resourceTypeNotSupported: + title: "不支持此外部资源" + description: "不支持从此外部站点获取的资源类型。请联系站点管理员。" + _failedToFetch: + title: "获取数据失败" + fetchErrorDescription: "与外部站点的通信失败。 如果重试后问题仍然存在,请联系站点管理员。" + parseErrorDescription: "无法读取从外部站点取得的数据。请联系站点管理员。" + _hashUnmatched: + title: "无法获取正确数据" + description: "无法验证数据的完整性。安全起见,无法继续安装。请联系站点管理员。" _pluginParseFailed: title: "AiScript 错误" + description: "虽然取得了数据,但是由于 AiScript 解析时出现错误,无法读取数据。请联系插件的作者。可在 Javascript 控制台查看错误详情。" + _pluginInstallFailed: + title: "插件安装失败" + description: "安装插件时出现错误。请再试一次。可在 Javascript 控制台查看错误详情。" + _themeParseFailed: + title: "主题解析错误" + description: "虽然取得了主题文件,但是由于解析时出现错误,无法加载主题。请联系主题的作者。可在 Javascript 控制台查看错误详情。" + _themeInstallFailed: + title: "安装主题失败" + description: "安装主题时出错。请再试一次。可在 Javascript 控制台查看错误详情。" +_dataSaver: + _media: + title: "加载媒体" + description: "防止自动加载图像和视频。 点击隐藏的图像/视频即可加载它们。\n" + _avatar: + title: "头像" + description: "停止播放头像的动画。 由于动画图片的文件大小可能比普通图像大,这可以进一步减少数据流量。" + _urlPreview: + title: "URL预览缩略图\n" + description: "将不再加载 URL 预览缩略图。" + _code: + title: "代码高亮" + description: "如果使用了代码高亮标记,例如在 MFM 中,则在点击之前不会加载。 代码高亮要求加载每种高亮语言的定义文件,由于这些文件不再自动加载,因此有望减少数据传输量。" +_hemisphere: + N: "北半球" + S: "南半球" + caption: "在某些客户端设置中用来确定季节" +_reversi: + reversi: "黑白棋" + total: "总计" +_offlineScreen: + title: "离线——无法连接到服务器" + header: "无法连接到服务器" + diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 36b6e77e9..5e730202c 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -91,7 +91,7 @@ manageLists: "管理清單" error: "錯誤" somethingHappened: "發生錯誤" retry: "重試" -pageLoadError: "載入頁面失敗" +pageLoadError: "無法載入頁面。" pageLoadErrorDescription: "這通常是網路錯誤或瀏覽器快取殘留而引起的。請先清除瀏覽器快取,稍後再重試。" serverIsDead: "伺服器沒有回應。請稍等片刻再試。" youShouldUpgradeClient: "請重新載入以使用新版客戶端顯示此頁面。" @@ -130,6 +130,7 @@ overwriteFromPinnedEmojis: "從一般複寫設定" reactionSettingDescription2: "拖動以交換,點擊以刪除,按下「+」以新增。" rememberNoteVisibility: "記住貼文可見性" attachCancel: "移除附件" +deleteFile: "刪除檔案" markAsSensitive: "標記為敏感內容" unmarkAsSensitive: "取消標記為敏感內容" enterFileName: "請輸入檔案名稱" @@ -379,6 +380,11 @@ hcaptcha: "hCaptcha" enableHcaptcha: "啟用 hCaptcha" hcaptchaSiteKey: "網站金鑰" hcaptchaSecretKey: "金鑰" +mcaptcha: "mCaptcha" +enableMcaptcha: "啟用 mCaptcha" +mcaptchaSiteKey: "網站金鑰" +mcaptchaSecretKey: "金鑰" +mcaptchaInstanceUrl: "mCaptcha 的實例網址" recaptcha: "reCAPTCHA" enableRecaptcha: "啟用 reCAPTCHA" recaptchaSiteKey: "網站金鑰" @@ -592,10 +598,10 @@ menu: "選單" divider: "分隔線" addItem: "新增項目" rearrange: "排序方式" -relays: "中繼" -addRelay: "新增中繼" +relays: "中繼器" +addRelay: "新增中繼器" inboxUrl: "收件夾URL" -addedRelays: "已加入的中繼" +addedRelays: "已加入的中繼器" serviceworkerInfo: "如要使用推播通知,需要啟用此選項並設定金鑰。" deletedNote: "已刪除的貼文" invisibleNote: "私密的貼文" @@ -626,6 +632,7 @@ medium: "中" small: "小" generateAccessToken: "發行存取權杖" permission: "權限" +adminPermission: "管理員權限" enableAll: "啟用全部" disableAll: "停用全部" tokenRequested: "允許存取帳戶" @@ -669,6 +676,7 @@ useGlobalSettingDesc: "啟用時,將使用帳戶通知設定。停用時,則 other: "其他" regenerateLoginToken: "重新產生登入權杖" regenerateLoginTokenDescription: "重新產生用於登入的內部權杖。一般情況下是不需要這樣做的。重新產生後,所有裝置將會被登出。" +theKeywordWhenSearchingForCustomEmoji: "這是搜尋自訂表情符號時的關鍵字" setMultipleBySeparatingWithSpace: "您可以使用空格分隔多個項目。" fileIdOrUrl: "檔案 ID 或 URL" behavior: "行為" @@ -682,7 +690,7 @@ abuseReported: "檢舉完成。感謝您的報告。" reporter: "檢舉者" reporteeOrigin: "檢舉來源" reporterOrigin: "檢舉者來源" -forwardReport: "將報告轉送給遠端實例" +forwardReport: "將報告轉送給遠端伺服器" forwardReportIsAnonymous: "在遠端實例上看不到您的資訊,顯示的報告者是匿名的系统帳戶。" send: "發送" abuseMarkAsResolved: "處理完畢" @@ -690,7 +698,7 @@ openInNewTab: "在新分頁中開啟" openInSideView: "在側欄中開啟" defaultNavigationBehaviour: "預設導航" editTheseSettingsMayBreakAccount: "修改這些設定可能會毀損您的帳戶" -instanceTicker: "貼文的實例來源" +instanceTicker: "貼文的伺服器資訊" waitingFor: "等待{x}" random: "隨機" system: "系統" @@ -811,7 +819,7 @@ active: "最近活躍" offline: "離線" notRecommended: "不推薦" botProtection: "Bot 防護" -instanceBlocking: "已封鎖的實例" +instanceBlocking: "已封鎖或禁言的伺服器" selectAccount: "選擇帳戶" switchAccount: "切換帳戶" enabled: "已啟用" @@ -954,7 +962,7 @@ cannotUploadBecauseNoFreeSpace: "由於雲端硬碟沒有可用空間,因此 cannotUploadBecauseExceedsFileSizeLimit: "由於超過了檔案大小的限制,無法上傳。" beta: "測試版" enableAutoSensitive: "自動 NSFW 判定" -enableAutoSensitiveDescription: "如果可用,它將使用機器學習技術判斷檔案是否需要標記為敏感。即使關閉此功能,也可能會依實例規則而自動啟用。" +enableAutoSensitiveDescription: "如果可行,它將使用機器學習技術判斷檔案是否需要標記為敏感。即使關閉此功能,也可能會依伺服器規則而自動啟用。" activeEmailValidationDescription: "主動地驗證使用者的電子郵件地址,以確定是否是一次性地址以及是否可以真正與其進行通訊。關閉時,僅檢查格式是否正確。" navbar: "導覽列" shuffle: "隨機" @@ -964,7 +972,7 @@ pushNotification: "推播通知" subscribePushNotification: "啟用推播通知" unsubscribePushNotification: "停用推播通知" pushNotificationAlreadySubscribed: "推播通知啟用中" -pushNotificationNotSupported: "瀏覽器或實例不支援推播通知" +pushNotificationNotSupported: "瀏覽器或伺服器不支援推播通知" sendPushNotificationReadMessage: "如果已閱讀通知與訊息,就刪除推播通知" sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}」通知將立刻顯示。可能會更消耗裝置電池。" windowMaximize: "最大化" @@ -1051,6 +1059,8 @@ limitWidthOfReaction: "限制反應的最大寬度,並縮小顯示尺寸。" noteIdOrUrl: "貼文ID或URL" video: "影片" videos: "影片" +audio: "音效" +audioFiles: "音效檔案" dataSaver: "數據節省模式" accountMigration: "遷移帳戶" accountMoved: "這個使用者已遷移至新的帳戶:" @@ -1183,6 +1193,25 @@ seasonalScreenEffect: "隨季節變換畫面的呈現" decorate: "設置頭像裝飾" addMfmFunction: "插入MFM功能語法" enableQuickAddMfmFunction: "顯示高級MFM選擇器" +bubbleGame: "氣泡遊戲" +sfx: "音效" +soundWillBePlayed: "將播放音效" +showReplay: "觀看重播" +replay: "重播" +replaying: "重播中" +ranking: "排行榜" +lastNDays: "過去 {n} 天" +backToTitle: "回到遊戲標題頁" +hemisphere: "您居住的地區" +withSensitive: "顯示包含敏感檔案的貼文" +userSaysSomethingSensitive: "包含 {name} 敏感檔案的貼文" +enableHorizontalSwipe: "滑動切換時間軸" +_bubbleGame: + howToPlay: "玩法說明" + _howToPlay: + section1: "調整位置並將物體放入盒子中。" + section2: "當相同類型的物體黏在一起時,它們會變成不同的物體,您就會得到分數。" + section3: "如果物體從盒子裡溢出,遊戲就結束了。透過融合物體而不溢出盒子來獲得高分!" _announcement: forExistingUsers: "僅限既有的使用者" forExistingUsersDescription: "啟用代表僅向現存使用者顯示;停用代表張貼後註冊的新使用者也會看到。" @@ -1501,7 +1530,7 @@ _achievements: description: "首頁時間軸在一分鐘內出現超過二十篇貼文" _viewInstanceChart: title: "分析師" - description: "顯示了實例的圖表" + description: "顯示了伺服器的圖表" _outputHelloWorldOnScratchpad: title: "Hello, world!" description: "在 AiScript 控制臺輸出了「hello world」" @@ -1553,13 +1582,20 @@ _achievements: _tutorialCompleted: title: "Misskey新手講座 結業證書" description: "已完成教學課程" + _bubbleGameExplodingHead: + title: "🤯" + description: "氣泡遊戲中最大的物體出現了" + _bubbleGameDoubleExplodingHead: + title: "雙重🤯" + description: "氣泡遊戲中最大的物體同時出現了兩個" + flavor: "這樣大小的便當盒,用 🤯 🤯 稍微裝滿一些吧" _role: new: "建立角色" edit: "編輯角色" name: "角色名稱" description: "角色描述 " permission: "角色的權限" - descriptionOfPermission: "審查員執行與審查相關的基本操作。\n管理員能變更實例的全部設定" + descriptionOfPermission: "審查員執行與審查相關的基本操作。\n管理員能變更伺服器的全部設定。" assignTarget: "指派目標" descriptionOfAssignTarget: "手動是以手動管理這個角色包含的人員。\n符合條件是設定條件以自動包含符合條件的使用者。" manual: "手動" @@ -1593,7 +1629,7 @@ _role: gtlAvailable: "瀏覽全域時間軸" ltlAvailable: "瀏覽本地時間軸" canPublicNote: "允許公開貼文" - canInvite: "發行實例邀請碼" + canInvite: "發行伺服器邀請碼" inviteLimit: "可建立邀請碼的數量" inviteLimitCycle: "邀請碼的發放間隔" inviteExpirationTime: "邀請碼的有效日期" @@ -1671,7 +1707,7 @@ _ad: _forgotPassword: enterEmail: "請輸入您的帳戶註冊的電子郵件地址。 密碼重置連結將被發送到該電子郵件地址。" ifNoEmail: "如果您還沒有註冊您的電子郵件地址,請聯繫管理員。 " - contactAdmin: "此實例不支持電子郵件,請聯繫您的管理員重置您的密碼。 " + contactAdmin: "本伺服器不支援電子郵件,請聯繫您的管理員重置您的密碼。 " _gallery: my: "我的貼文" liked: "喜歡的貼文" @@ -1758,8 +1794,8 @@ _wordMute: _instanceMute: instanceMuteDescription: "包括對被靜音伺服器上的使用者的回覆,被設定的伺服器上所有貼文及轉發都會被靜音。" instanceMuteDescription2: "設定時以換行進行分隔" - title: "將隱藏被設定的實例貼文。" - heading: "將實例靜音" + title: "將隱藏被設定的伺服器貼文。" + heading: "將伺服器靜音" _theme: explore: "探索佈景主題" install: "安裝佈景主題" @@ -1927,14 +1963,63 @@ _permissions: "write:user-groups": "編輯使用者群組" "read:channels": "已查看的頻道" "write:channels": "編輯頻道" - "read:gallery": "瀏覽圖庫" - "write:gallery": "操作圖庫" - "read:gallery-likes": "讀取喜歡的圖片" - "write:gallery-likes": "操作喜歡的圖片" + "read:gallery": "瀏覽相簿" + "write:gallery": "編輯相簿" + "read:gallery-likes": "瀏覽相簿的讚" + "write:gallery-likes": "編輯相簿的讚" "read:flash": "檢視 Play" "write:flash": "編輯 Play" "read:flash-likes": "檢視 Play 的讚" "write:flash-likes": "編輯 Play 的讚" + "read:admin:abuse-user-reports": "查看來自使用者的檢舉" + "write:admin:delete-account": "刪除使用者帳戶" + "write:admin:delete-all-files-of-a-user": "刪除使用者的所有檔案" + "read:admin:index-stats": "查看資料庫索引的相關資訊" + "read:admin:table-stats": "查看資料庫表格的相關資訊" + "read:admin:user-ips": "查看使用者的 IP 位址" + "read:admin:meta": "查看實例的元資料" + "write:admin:reset-password": "重設使用者的密碼" + "write:admin:resolve-abuse-user-report": "解決來自使用者的檢舉" + "write:admin:send-email": "發送郵件" + "read:admin:server-info": "查看伺服器的資訊" + "read:admin:show-moderation-log": "查看審查紀錄" + "read:admin:show-user": "查看使用者的私密資訊" + "read:admin:show-users": "查看使用者的私密資訊" + "write:admin:suspend-user": "凍結使用者" + "write:admin:unset-user-avatar": "刪除使用者的頭像" + "write:admin:unset-user-banner": "刪除使用者的橫幅" + "write:admin:unsuspend-user": "解除凍結使用者" + "write:admin:meta": "編輯實例的元資料" + "write:admin:user-note": "編輯審查筆記" + "write:admin:roles": "編輯角色" + "read:admin:roles": "查看角色" + "write:admin:relays": "編輯中繼器" + "read:admin:relays": "查看中繼器" + "write:admin:invite-codes": "編輯邀請碼" + "read:admin:invite-codes": "查看邀請碼" + "write:admin:announcements": "編輯公告" + "read:admin:announcements": "查看公告" + "write:admin:avatar-decorations": "編輯頭像裝飾" + "read:admin:avatar-decorations": "查看頭像裝飾" + "write:admin:federation": "編輯站台聯邦的相關資訊" + "write:admin:account": "編輯使用者帳戶" + "read:admin:account": "查看使用者的相關資訊" + "write:admin:emoji": "編輯表情符號" + "read:admin:emoji": "查看表情符號" + "write:admin:queue": "編輯工作佇列" + "read:admin:queue": "查看工作佇列的相關資訊" + "write:admin:promo": "編輯推廣貼文" + "write:admin:drive": "編輯使用者的雲端硬碟" + "read:admin:drive": "查看使用者雲端硬碟的相關資訊" + "read:admin:stream": "使用管理員的 Websocket API" + "write:admin:ad": "編輯廣告" + "read:admin:ad": "查看廣告" + "write:invite-codes": "建立邀請碼" + "read:invite-codes": "取得邀請碼" + "write:clip-favorite": "編輯摘錄的讚" + "read:clip-favorite": "查看摘錄的讚" + "read:federation": "查看站台聯邦的相關資訊" + "write:report-abuse": "檢舉違規行為" _auth: shareAccessTitle: "應用程式的存取權限" shareAccess: "要授權「“{name}”」存取您的帳戶嗎?" @@ -1961,7 +2046,7 @@ _weekday: saturday: "週六" _widgets: profile: "個人檔案" - instanceInfo: "實例資訊" + instanceInfo: "伺服器資訊" memo: "備忘錄" notifications: "通知" timeline: "時間軸" @@ -1975,7 +2060,7 @@ _widgets: digitalClock: "電子時鐘" unixClock: "UNIX 時間" federation: "聯邦宇宙" - instanceCloud: "實例雲" + instanceCloud: "伺服器雲" postForm: "發文視窗" slideshow: "幻燈片" button: "按鈕" @@ -2027,7 +2112,7 @@ _visibility: specified: "指定使用者" specifiedDescription: "僅發布至指定使用者" disableFederation: "停用聯邦" - disableFederationDescription: "不要傳遞給其他實例" + disableFederationDescription: "不發送到其他伺服器" _postForm: replyPlaceholder: "回覆此貼文..." quotePlaceholder: "引用此貼文..." @@ -2056,6 +2141,7 @@ _profile: _exportOrImport: allNotes: "所有貼文" favoritedNotes: "「我的最愛」貼文" + clips: "摘錄" followingList: "追隨中" muteList: "靜音" blockingList: "封鎖" @@ -2355,3 +2441,48 @@ _dataSaver: _code: title: "程式碼突出顯示" description: "如果使用了 MFM 的程式碼突顯標記,則在點擊之前不會載入。程式碼突顯要求加載每種程式語言的突顯定義檔案,但由於這些檔案不再自動載入,因此有望減少資料流量。" +_hemisphere: + N: "北半球" + S: "南半球" + caption: "在某些客戶端的設定中,用於判斷季節。" +_reversi: + reversi: "黑白棋" + gameSettings: "對弈設定" + chooseBoard: "選擇棋盤" + blackOrWhite: "先手/後手" + blackIs: "{name} 為黑棋(先攻)" + rules: "規則" + thisGameIsStartedSoon: "對弈即將開始" + waitingForOther: "等待對手準備就緒" + waitingForMe: "等待您準備就緒" + waitingBoth: "請準備" + ready: "準備就緒" + cancelReady: "重新準備" + opponentTurn: "對手的回合" + myTurn: "您的回合" + turnOf: "{name} 的回合" + pastTurnOf: "{name} 的回合" + surrender: "認輸" + surrendered: "對手認輸" + timeout: "時間到" + drawn: "平手" + won: "{name} 獲勝" + black: "黑" + white: "白" + total: "合計" + turnCount: "{count} 回合" + myGames: "我的對弈" + allGames: "所有對弈" + ended: "" + playing: "正在對弈" + isLlotheo: "子較少的一方為勝(顛倒規則)" + loopedMap: "循環棋盤" + canPutEverywhere: "隨意置放模式" + timeLimitForEachTurn: "每回合的時間限制" + freeMatch: "自由對戰" + lookingForPlayer: "正在搜尋對手" + gameCanceled: "對弈已被取消" +_offlineScreen: + title: "離線-無法連接伺服器" + header: "無法連接伺服器" + diff --git a/package.json b/package.json index 6ed8726cc..a54aea637 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "sharkey", - "version": "2024.1.0.beta2", + "version": "2024.2.0-beta1", "codename": "shonk", "repository": { "type": "git", @@ -10,7 +10,10 @@ "workspaces": [ "packages/frontend", "packages/backend", - "packages/sw" + "packages/sw", + "packages/misskey-js", + "packages/misskey-reversi", + "packages/misskey-bubble-game" ], "private": true, "scripts": { @@ -46,17 +49,17 @@ }, "dependencies": { "execa": "8.0.1", - "cssnano": "6.0.2", + "cssnano": "6.0.3", "js-yaml": "4.1.0", - "postcss": "8.4.32", - "terser": "5.26.0", + "postcss": "8.4.33", + "terser": "5.27.0", "typescript": "5.3.3" }, "devDependencies": { - "@typescript-eslint/eslint-plugin": "6.14.0", - "@typescript-eslint/parser": "6.14.0", + "@typescript-eslint/eslint-plugin": "6.18.1", + "@typescript-eslint/parser": "6.18.1", "cross-env": "7.0.3", - "cypress": "13.6.1", + "cypress": "13.6.3", "eslint": "8.56.0", "start-server-and-test": "2.0.3", "ncp": "2.0.0" diff --git a/packages/backend/migration/1705222772858-optimize-note-index-for-array-column.js b/packages/backend/migration/1705222772858-optimize-note-index-for-array-column.js new file mode 100644 index 000000000..571bd8e8f --- /dev/null +++ b/packages/backend/migration/1705222772858-optimize-note-index-for-array-column.js @@ -0,0 +1,24 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class OptimizeNoteIndexForArrayColumns1705222772858 { + name = 'OptimizeNoteIndexForArrayColumns1705222772858' + + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_796a8c03959361f97dc2be1d5c"`); + await queryRunner.query(`DROP INDEX "public"."IDX_54ebcb6d27222913b908d56fd8"`); + await queryRunner.query(`DROP INDEX "public"."IDX_88937d94d7443d9a99a76fa5c0"`); + await queryRunner.query(`DROP INDEX "public"."IDX_51c063b6a133a9cb87145450f5"`); + await queryRunner.query(`CREATE INDEX "IDX_NOTE_FILE_IDS" ON "note" using gin ("fileIds")`) + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "IDX_NOTE_FILE_IDS"`) + await queryRunner.query(`CREATE INDEX "IDX_51c063b6a133a9cb87145450f5" ON "note" ("fileIds") `); + await queryRunner.query(`CREATE INDEX "IDX_88937d94d7443d9a99a76fa5c0" ON "note" ("tags") `); + await queryRunner.query(`CREATE INDEX "IDX_54ebcb6d27222913b908d56fd8" ON "note" ("mentions") `); + await queryRunner.query(`CREATE INDEX "IDX_796a8c03959361f97dc2be1d5c" ON "note" ("visibleUserIds") `); + } +} diff --git a/packages/backend/migration/1705475608437-reversi.js b/packages/backend/migration/1705475608437-reversi.js new file mode 100644 index 000000000..c9d69e2c7 --- /dev/null +++ b/packages/backend/migration/1705475608437-reversi.js @@ -0,0 +1,22 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi1705475608437 { + name = 'Reversi1705475608437' + + async up(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_b46ec40746efceac604142be1c"`); + await queryRunner.query(`DROP INDEX "public"."IDX_b604d92d6c7aec38627f6eaf16"`); + await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "createdAt"`); + await queryRunner.query(`ALTER TABLE "reversi_matching" DROP COLUMN "createdAt"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_matching" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`ALTER TABLE "reversi_game" ADD "createdAt" TIMESTAMP WITH TIME ZONE NOT NULL`); + await queryRunner.query(`CREATE INDEX "IDX_b604d92d6c7aec38627f6eaf16" ON "reversi_matching" ("createdAt") `); + await queryRunner.query(`CREATE INDEX "IDX_b46ec40746efceac604142be1c" ON "reversi_game" ("createdAt") `); + } +} diff --git a/packages/backend/migration/1705654039457-reversi-2.js b/packages/backend/migration/1705654039457-reversi-2.js new file mode 100644 index 000000000..33747ba9f --- /dev/null +++ b/packages/backend/migration/1705654039457-reversi-2.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi21705654039457 { + name = 'Reversi21705654039457' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "user1Accepted" TO "user1Ready"`); + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "user2Accepted" TO "user2Ready"`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "user1Ready" TO "user1Accepted"`); + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "user2Ready" TO "user2Accepted"`); + } +} diff --git a/packages/backend/migration/1705793785675-reversi-3.js b/packages/backend/migration/1705793785675-reversi-3.js new file mode 100644 index 000000000..2faf9ae6d --- /dev/null +++ b/packages/backend/migration/1705793785675-reversi-3.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi31705793785675 { + name = 'Reversi31705793785675' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "surrendered" TO "surrenderedUserId"`); + await queryRunner.query(`ALTER TABLE "reversi_game" ADD "timeoutUserId" character varying(32)`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "timeoutUserId"`); + await queryRunner.query(`ALTER TABLE "reversi_game" RENAME COLUMN "surrenderedUserId" TO "surrendered"`); + } +} diff --git a/packages/backend/migration/1705794768153-reversi-4.js b/packages/backend/migration/1705794768153-reversi-4.js new file mode 100644 index 000000000..5b7bacb21 --- /dev/null +++ b/packages/backend/migration/1705794768153-reversi-4.js @@ -0,0 +1,18 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi41705794768153 { + name = 'Reversi41705794768153' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" ADD "endedAt" TIMESTAMP WITH TIME ZONE`); + await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."endedAt" IS 'The ended date of the ReversiGame.'`); + } + + async down(queryRunner) { + await queryRunner.query(`COMMENT ON COLUMN "reversi_game"."endedAt" IS 'The ended date of the ReversiGame.'`); + await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "endedAt"`); + } +} diff --git a/packages/backend/migration/1705798904141-reversi-5.js b/packages/backend/migration/1705798904141-reversi-5.js new file mode 100644 index 000000000..7ca722160 --- /dev/null +++ b/packages/backend/migration/1705798904141-reversi-5.js @@ -0,0 +1,16 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export class Reversi51705798904141 { + name = 'Reversi51705798904141' + + async up(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" ADD "timeLimitForEachTurn" smallint NOT NULL DEFAULT '90'`); + } + + async down(queryRunner) { + await queryRunner.query(`ALTER TABLE "reversi_game" DROP COLUMN "timeLimitForEachTurn"`); + } +} diff --git a/packages/backend/package.json b/packages/backend/package.json index 719c3e98d..4ca4f85c5 100644 --- a/packages/backend/package.json +++ b/packages/backend/package.json @@ -8,7 +8,7 @@ }, "scripts": { "start": "node ./built/boot/entry.js", - "start:test": "NODE_ENV=test node ./built/boot/entry.js", + "start:test": "cross-env NODE_ENV=test node ./built/boot/entry.js", "migrate": "pnpm typeorm migration:run -d ormconfig.js", "revert": "pnpm typeorm migration:revert -d ormconfig.js", "check:connect": "node ./check_connect.js", @@ -31,7 +31,7 @@ "test:e2e": "pnpm build && pnpm build:test && pnpm jest:e2e", "test-and-coverage": "pnpm jest-and-coverage", "test-and-coverage:e2e": "pnpm build && pnpm build:test && pnpm jest-and-coverage:e2e", - "generate-api-json": "node ./generate_api_json.js" + "generate-api-json": "pnpm build && node ./generate_api_json.js" }, "optionalDependencies": { "@swc/core-android-arm64": "1.3.11", @@ -70,11 +70,11 @@ "@bull-board/ui": "5.10.2", "@discordapp/twemoji": "15.0.2", "@fastify/accepts": "4.3.0", - "@fastify/cookie": "9.2.0", + "@fastify/cookie": "9.3.1", "@fastify/cors": "8.5.0", "@fastify/express": "2.3.0", "@fastify/http-proxy": "9.3.0", - "@fastify/multipart": "8.0.0", + "@fastify/multipart": "8.1.0", "@fastify/static": "6.12.0", "@fastify/view": "8.2.0", "@misskey-dev/sharp-read-bmp": "^1.1.1", @@ -84,21 +84,21 @@ "@nestjs/testing": "10.2.10", "@peertube/http-signature": "1.7.0", "@sharkey/sfm-js": "0.24.4", - "@simplewebauthn/server": "8.3.5", + "@simplewebauthn/server": "9.0.0", "@sinonjs/fake-timers": "11.2.2", "@smithy/node-http-handler": "2.1.10", "@swc/cli": "0.1.63", - "@swc/core": "1.3.100", + "@swc/core": "1.3.105", "@twemoji/parser": "15.0.0", "accepts": "1.3.8", "ajv": "8.12.0", "archiver": "6.0.1", "argon2": "^0.31.1", - "async-mutex": "0.4.0", + "async-mutex": "0.4.1", "bcryptjs": "2.4.3", "blurhash": "2.0.5", "body-parser": "1.20.2", - "bullmq": "4.15.4", + "bullmq": "5.1.4", "cacheable-lookup": "7.0.0", "cbor": "9.0.1", "chalk": "5.3.0", @@ -107,13 +107,14 @@ "cli-highlight": "2.1.11", "color-convert": "2.0.1", "content-disposition": "0.5.4", + "crc-32": "^1.2.2", "date-fns": "2.30.0", "deep-email-validator": "0.1.21", - "fastify": "4.24.3", "fastify-multer": "^2.0.3", + "fastify": "4.25.2", "fastify-raw-body": "4.3.0", "feed": "4.2.2", - "file-type": "18.7.0", + "file-type": "19.0.0", "fluent-ffmpeg": "2.1.2", "form-data": "4.0.0", "glob": "10.3.10", @@ -126,25 +127,26 @@ "ipaddr.js": "2.1.0", "is-svg": "5.0.0", "js-yaml": "4.1.0", - "jsdom": "23.0.1", + "jsdom": "23.2.0", "json5": "2.2.3", "jsonld": "8.3.2", - "jsrsasign": "10.9.0", - "meilisearch": "0.36.0", "megalodon": "workspace:*", + "jsrsasign": "11.0.0", + "meilisearch": "0.37.0", "microformats-parser": "2.0.2", "mime-types": "2.1.35", "misskey-js": "workspace:*", + "misskey-reversi": "workspace:*", "ms": "3.0.0-canary.1", "nanoid": "5.0.4", "nested-property": "4.0.0", "node-fetch": "3.3.2", - "nodemailer": "6.9.7", + "nodemailer": "6.9.8", "oauth": "0.10.0", "oauth2orize": "1.12.0", "oauth2orize-pkce": "0.1.2", "os-utils": "0.0.14", - "otpauth": "9.2.1", + "otpauth": "9.2.2", "parse5": "7.1.2", "pg": "8.11.3", "pkce-challenge": "4.0.1", @@ -168,26 +170,26 @@ "slacc": "0.0.10", "strict-event-emitter-types": "2.0.0", "stringz": "2.1.0", - "systeminformation": "5.21.20", + "systeminformation": "5.21.23", "tinycolor2": "1.6.0", "tmp": "0.2.1", "tsc-alias": "1.8.8", "tsconfig-paths": "4.2.0", - "typeorm": "0.3.17", + "typeorm": "0.3.19", "typescript": "5.3.3", "ulid": "2.3.0", "uuid": "^9.0.1", "vary": "1.1.2", - "web-push": "3.6.6", - "ws": "8.15.1", + "web-push": "3.6.7", + "ws": "8.16.0", "xev": "3.0.2" }, "devDependencies": { "@jest/globals": "29.7.0", - "@misskey-dev/eslint-plugin": "^1.0.0", - "@nestjs/platform-express": "^10.3.0", + "@misskey-dev/eslint-plugin": "1.0.0", + "@nestjs/platform-express": "10.3.0", "@simplewebauthn/typescript-types": "8.3.4", - "@swc/jest": "0.2.29", + "@swc/jest": "0.2.31", "@types/accepts": "1.3.7", "@types/archiver": "6.0.2", "@types/bcryptjs": "2.4.6", @@ -204,7 +206,7 @@ "@types/jsrsasign": "10.5.12", "@types/mime-types": "2.1.4", "@types/ms": "0.7.34", - "@types/node": "20.10.5", + "@types/node": "20.11.5", "@types/node-fetch": "3.0.3", "@types/nodemailer": "6.4.14", "@types/oauth": "0.9.4", @@ -228,9 +230,9 @@ "@types/vary": "1.1.3", "@types/web-push": "3.6.3", "@types/ws": "8.5.10", - "@typescript-eslint/eslint-plugin": "6.14.0", - "@typescript-eslint/parser": "6.14.0", - "aws-sdk-client-mock": "3.0.0", + "@typescript-eslint/eslint-plugin": "6.18.1", + "@typescript-eslint/parser": "6.18.1", + "aws-sdk-client-mock": "3.0.1", "cross-env": "7.0.3", "eslint": "8.56.0", "eslint-plugin-import": "2.29.1", @@ -238,8 +240,8 @@ "fkill": "^9.0.0", "jest": "29.7.0", "jest-mock": "29.7.0", - "nodemon": "3.0.2", - "pid-port": "^1.0.0", + "nodemon": "3.0.3", + "pid-port": "1.0.0", "simple-oauth2": "5.0.0" } } diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts index a550fdc36..972d1be0e 100644 --- a/packages/backend/src/config.ts +++ b/packages/backend/src/config.ts @@ -149,7 +149,7 @@ export type Config = { inboxJobMaxAttempts: number | undefined; proxyRemoteFiles: boolean | undefined; customMOTD: string[] | undefined; - signToActivityPubGet: boolean | undefined; + signToActivityPubGet: boolean; checkActivityPubGetSignature: boolean | undefined; version: string; @@ -265,7 +265,7 @@ export function loadConfig(): Config { inboxJobMaxAttempts: config.inboxJobMaxAttempts, proxyRemoteFiles: config.proxyRemoteFiles, customMOTD: config.customMOTD, - signToActivityPubGet: config.signToActivityPubGet, + signToActivityPubGet: config.signToActivityPubGet ?? true, checkActivityPubGetSignature: config.checkActivityPubGetSignature, mediaProxy: externalMediaProxy ?? internalMediaProxy, externalMediaProxyEnabled: externalMediaProxy !== null && externalMediaProxy !== internalMediaProxy, diff --git a/packages/backend/src/core/AntennaService.ts b/packages/backend/src/core/AntennaService.ts index 2c27a0255..295915307 100644 --- a/packages/backend/src/core/AntennaService.ts +++ b/packages/backend/src/core/AntennaService.ts @@ -55,23 +55,29 @@ export class AntennaService implements OnApplicationShutdown { const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'antennaCreated': - this.antennas.push({ + this.antennas.push({ // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい ...body, lastUsedAt: new Date(body.lastUsedAt), + user: null, // joinなカラムは通常取ってこないので + userList: null, // joinなカラムは通常取ってこないので }); break; case 'antennaUpdated': { const idx = this.antennas.findIndex(a => a.id === body.id); if (idx >= 0) { - this.antennas[idx] = { + this.antennas[idx] = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい ...body, lastUsedAt: new Date(body.lastUsedAt), + user: null, // joinなカラムは通常取ってこないので + userList: null, // joinなカラムは通常取ってこないので }; } else { // サーバ起動時にactiveじゃなかった場合、リストに持っていないので追加する必要あり - this.antennas.push({ + this.antennas.push({ // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい ...body, lastUsedAt: new Date(body.lastUsedAt), + user: null, // joinなカラムは通常取ってこないので + userList: null, // joinなカラムは通常取ってこないので }); } } diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts index fa868ff8b..4c6713e0c 100644 --- a/packages/backend/src/core/CoreModule.ts +++ b/packages/backend/src/core/CoreModule.ts @@ -66,6 +66,8 @@ import { FeaturedService } from './FeaturedService.js'; import { FanoutTimelineService } from './FanoutTimelineService.js'; import { ChannelFollowingService } from './ChannelFollowingService.js'; import { RegistryApiService } from './RegistryApiService.js'; +import { ReversiService } from './ReversiService.js'; + import { ChartLoggerService } from './chart/ChartLoggerService.js'; import FederationChart from './chart/charts/federation.js'; import NotesChart from './chart/charts/notes.js'; @@ -80,6 +82,7 @@ import PerUserFollowingChart from './chart/charts/per-user-following.js'; import PerUserDriveChart from './chart/charts/per-user-drive.js'; import ApRequestChart from './chart/charts/ap-request.js'; import { ChartManagementService } from './chart/ChartManagementService.js'; + import { AbuseUserReportEntityService } from './entities/AbuseUserReportEntityService.js'; import { AntennaEntityService } from './entities/AntennaEntityService.js'; import { AppEntityService } from './entities/AppEntityService.js'; @@ -112,6 +115,8 @@ import { UserListEntityService } from './entities/UserListEntityService.js'; import { FlashEntityService } from './entities/FlashEntityService.js'; import { FlashLikeEntityService } from './entities/FlashLikeEntityService.js'; import { RoleEntityService } from './entities/RoleEntityService.js'; +import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js'; + import { ApAudienceService } from './activitypub/ApAudienceService.js'; import { ApDbResolverService } from './activitypub/ApDbResolverService.js'; import { ApDeliverManagerService } from './activitypub/ApDeliverManagerService.js'; @@ -199,6 +204,7 @@ const $FanoutTimelineService: Provider = { provide: 'FanoutTimelineService', use const $FanoutTimelineEndpointService: Provider = { provide: 'FanoutTimelineEndpointService', useExisting: FanoutTimelineEndpointService }; const $ChannelFollowingService: Provider = { provide: 'ChannelFollowingService', useExisting: ChannelFollowingService }; const $RegistryApiService: Provider = { provide: 'RegistryApiService', useExisting: RegistryApiService }; +const $ReversiService: Provider = { provide: 'ReversiService', useExisting: ReversiService }; const $ChartLoggerService: Provider = { provide: 'ChartLoggerService', useExisting: ChartLoggerService }; const $FederationChart: Provider = { provide: 'FederationChart', useExisting: FederationChart }; @@ -247,6 +253,7 @@ const $UserListEntityService: Provider = { provide: 'UserListEntityService', use const $FlashEntityService: Provider = { provide: 'FlashEntityService', useExisting: FlashEntityService }; const $FlashLikeEntityService: Provider = { provide: 'FlashLikeEntityService', useExisting: FlashLikeEntityService }; const $RoleEntityService: Provider = { provide: 'RoleEntityService', useExisting: RoleEntityService }; +const $ReversiGameEntityService: Provider = { provide: 'ReversiGameEntityService', useExisting: ReversiGameEntityService }; const $ApAudienceService: Provider = { provide: 'ApAudienceService', useExisting: ApAudienceService }; const $ApDbResolverService: Provider = { provide: 'ApDbResolverService', useExisting: ApDbResolverService }; @@ -336,6 +343,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FanoutTimelineEndpointService, ChannelFollowingService, RegistryApiService, + ReversiService, + ChartLoggerService, FederationChart, NotesChart, @@ -350,6 +359,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting PerUserDriveChart, ApRequestChart, ChartManagementService, + AbuseUserReportEntityService, AntennaEntityService, AppEntityService, @@ -382,6 +392,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FlashEntityService, FlashLikeEntityService, RoleEntityService, + ReversiGameEntityService, + ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -466,6 +478,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FanoutTimelineEndpointService, $ChannelFollowingService, $RegistryApiService, + $ReversiService, + $ChartLoggerService, $FederationChart, $NotesChart, @@ -480,6 +494,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $PerUserDriveChart, $ApRequestChart, $ChartManagementService, + $AbuseUserReportEntityService, $AntennaEntityService, $AppEntityService, @@ -512,6 +527,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FlashEntityService, $FlashLikeEntityService, $RoleEntityService, + $ReversiGameEntityService, + $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, @@ -597,6 +614,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FanoutTimelineEndpointService, ChannelFollowingService, RegistryApiService, + ReversiService, + FederationChart, NotesChart, UsersChart, @@ -610,6 +629,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting PerUserDriveChart, ApRequestChart, ChartManagementService, + AbuseUserReportEntityService, AntennaEntityService, AppEntityService, @@ -642,6 +662,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting FlashEntityService, FlashLikeEntityService, RoleEntityService, + ReversiGameEntityService, + ApAudienceService, ApDbResolverService, ApDeliverManagerService, @@ -726,6 +748,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FanoutTimelineEndpointService, $ChannelFollowingService, $RegistryApiService, + $ReversiService, + $FederationChart, $NotesChart, $UsersChart, @@ -739,6 +763,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $PerUserDriveChart, $ApRequestChart, $ChartManagementService, + $AbuseUserReportEntityService, $AntennaEntityService, $AppEntityService, @@ -771,6 +796,8 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting $FlashEntityService, $FlashLikeEntityService, $RoleEntityService, + $ReversiGameEntityService, + $ApAudienceService, $ApDbResolverService, $ApDeliverManagerService, diff --git a/packages/backend/src/core/DownloadService.ts b/packages/backend/src/core/DownloadService.ts index 5474272b0..5e196e83d 100644 --- a/packages/backend/src/core/DownloadService.ts +++ b/packages/backend/src/core/DownloadService.ts @@ -145,7 +145,8 @@ export class DownloadService { const parsedIp = ipaddr.parse(ip); for (const net of this.config.allowedPrivateNetworks ?? []) { - if (parsedIp.match(ipaddr.parseCIDR(net))) { + const cidr = ipaddr.parseCIDR(net); + if (cidr[0].kind() === parsedIp.kind() && parsedIp.match(ipaddr.parseCIDR(net))) { return false; } } diff --git a/packages/backend/src/core/EmailService.ts b/packages/backend/src/core/EmailService.ts index 7e812b4df..8daee148e 100644 --- a/packages/backend/src/core/EmailService.ts +++ b/packages/backend/src/core/EmailService.ts @@ -165,10 +165,17 @@ export class EmailService { email: emailAddress, }); + if (exist !== 0) { + return { + available: false, + reason: 'used', + }; + } + let validated: { valid: boolean, reason?: string | null, - }; + } = { valid: true, reason: null }; if (meta.enableActiveEmailValidation) { if (meta.enableVerifymailApi && meta.verifymailAuthKey != null) { @@ -185,27 +192,37 @@ export class EmailService { validateSMTP: false, // 日本だと25ポートが殆どのプロバイダーで塞がれていてタイムアウトになるので }); } - } else { - validated = { valid: true, reason: null }; + } + + if (!validated.valid) { + const formatReason: Record = { + regex: 'format', + disposable: 'disposable', + mx: 'mx', + smtp: 'smtp', + network: 'network', + blacklist: 'blacklist', + }; + + return { + available: false, + reason: validated.reason ? formatReason[validated.reason] ?? null : null, + }; } const emailDomain: string = emailAddress.split('@')[1]; const isBanned = this.utilityService.isBlockedHost(meta.bannedEmailDomains, emailDomain); - const available = exist === 0 && validated.valid && !isBanned; + if (isBanned) { + return { + available: false, + reason: 'banned', + }; + } return { - available, - reason: available ? null : - exist !== 0 ? 'used' : - isBanned ? 'banned' : - validated.reason === 'regex' ? 'format' : - validated.reason === 'disposable' ? 'disposable' : - validated.reason === 'mx' ? 'mx' : - validated.reason === 'smtp' ? 'smtp' : - validated.reason === 'network' ? 'network' : - validated.reason === 'blacklist' ? 'blacklist' : - null, + available: true, + reason: null, }; } @@ -222,7 +239,8 @@ export class EmailService { }, }); - const json = (await res.json()) as { + const json = (await res.json()) as Partial<{ + message: string; block: boolean; catch_all: boolean; deliverable_email: boolean; @@ -237,8 +255,15 @@ export class EmailService { mx_priority: { [key: string]: number }; privacy: boolean; related_domains: string[]; - }; + }>; + /* api error: when there is only one `message` attribute in the returned result */ + if (Object.keys(json).length === 1 && Reflect.has(json, 'message')) { + return { + valid: false, + reason: null, + }; + } if (json.email_address === undefined) { return { valid: false, @@ -281,25 +306,26 @@ export class EmailService { headers: { 'Content-Type': 'application/json', Accept: 'application/json', - Authorization: truemailAuthKey + Authorization: truemailAuthKey, }, }); - + const json = (await res.json()) as { email: string; success: boolean; - errors?: { + error?: string; + errors?: { list_match?: string; regex?: string; mx?: string; smtp?: string; } | null; }; - - if (json.email === undefined || (json.email !== undefined && json.errors?.regex)) { + + if (json.email === undefined || json.errors?.regex) { return { - valid: false, - reason: 'format', + valid: false, + reason: 'format', }; } if (json.errors?.smtp) { @@ -320,7 +346,7 @@ export class EmailService { reason: json.errors?.list_match as T || 'blacklist', }; } - + return { valid: true, reason: null, diff --git a/packages/backend/src/core/GlobalEventService.ts b/packages/backend/src/core/GlobalEventService.ts index 95a4eba74..dbbe5ecdc 100644 --- a/packages/backend/src/core/GlobalEventService.ts +++ b/packages/backend/src/core/GlobalEventService.ts @@ -5,6 +5,7 @@ import { Inject, Injectable } from '@nestjs/common'; import * as Redis from 'ioredis'; +import * as Reversi from 'misskey-reversi'; import type { MiChannel } from '@/models/Channel.js'; import type { MiUser } from '@/models/User.js'; import type { MiUserProfile } from '@/models/UserProfile.js'; @@ -18,7 +19,7 @@ import type { MiSignin } from '@/models/Signin.js'; import type { MiPage } from '@/models/Page.js'; import type { MiWebhook } from '@/models/Webhook.js'; import type { MiMeta } from '@/models/Meta.js'; -import { MiAvatarDecoration, MiRole, MiRoleAssignment } from '@/models/_.js'; +import { MiAvatarDecoration, MiReversiGame, MiRole, MiRoleAssignment } from '@/models/_.js'; import type { Packed } from '@/misc/json-schema.js'; import { DI } from '@/di-symbols.js'; import type { Config } from '@/config.js'; @@ -162,6 +163,38 @@ export interface AdminEventTypes { comment: string; }; } + +export interface ReversiEventTypes { + matched: { + game: Packed<'ReversiGameDetailed'>; + }; + invited: { + user: Packed<'User'>; + }; +} + +export interface ReversiGameEventTypes { + changeReadyStates: { + user1: boolean; + user2: boolean; + }; + updateSettings: { + userId: MiUser['id']; + key: string; + value: any; + }; + log: Reversi.Serializer.Log & { id: string | null }; + started: { + game: Packed<'ReversiGameDetailed'>; + }; + ended: { + winnerId: MiUser['id'] | null; + game: Packed<'ReversiGameDetailed'>; + }; + canceled: { + userId: MiUser['id']; + }; +} //#endregion // 辞書(interface or type)から{ type, body }ユニオンを定義 @@ -252,6 +285,14 @@ export type GlobalEvents = { name: 'notesStream'; payload: Serialized>; }; + reversi: { + name: `reversiStream:${MiUser['id']}`; + payload: EventUnionFromDictionary>; + }; + reversiGame: { + name: `reversiGameStream:${MiReversiGame['id']}`; + payload: EventUnionFromDictionary>; + }; }; // API event definitions @@ -341,4 +382,14 @@ export class GlobalEventService { public publishAdminStream(userId: MiUser['id'], type: K, value?: AdminEventTypes[K]): void { this.publish(`adminStream:${userId}`, type, typeof value === 'undefined' ? null : value); } + + @bindThis + public publishReversiStream(userId: MiUser['id'], type: K, value?: ReversiEventTypes[K]): void { + this.publish(`reversiStream:${userId}`, type, typeof value === 'undefined' ? null : value); + } + + @bindThis + public publishReversiGameStream(gameId: MiReversiGame['id'], type: K, value?: ReversiGameEventTypes[K]): void { + this.publish(`reversiGameStream:${gameId}`, type, typeof value === 'undefined' ? null : value); + } } diff --git a/packages/backend/src/core/MetaService.ts b/packages/backend/src/core/MetaService.ts index 80e802096..8e945e3d8 100644 --- a/packages/backend/src/core/MetaService.ts +++ b/packages/backend/src/core/MetaService.ts @@ -51,7 +51,10 @@ export class MetaService implements OnApplicationShutdown { const { type, body } = obj.message as GlobalEvents['internal']['payload']; switch (type) { case 'metaUpdated': { - this.cache = body; + this.cache = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい + ...body, + proxyAccount: null, // joinなカラムは通常取ってこないので + }; break; } default: diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts index 58f3d3559..345367f05 100644 --- a/packages/backend/src/core/NoteCreateService.ts +++ b/packages/backend/src/core/NoteCreateService.ts @@ -494,6 +494,9 @@ export class NoteCreateService implements OnApplicationShutdown { data.text = data.text.slice(0, DB_MAX_NOTE_TEXT_LENGTH); } data.text = data.text.trim(); + if (data.text === '') { + data.text = null; + } } else { data.text = null; } diff --git a/packages/backend/src/core/QueryService.ts b/packages/backend/src/core/QueryService.ts index f006ed494..13d8a6759 100644 --- a/packages/backend/src/core/QueryService.ts +++ b/packages/backend/src/core/QueryService.ts @@ -212,8 +212,8 @@ export class QueryService { // または 自分自身 .orWhere('note.userId = :meId') // または 自分宛て - .orWhere(':meId = ANY(note.visibleUserIds)') - .orWhere(':meId = ANY(note.mentions)') + .orWhere(':meIdAsList <@ note.visibleUserIds') + .orWhere(':meIdAsList <@ note.mentions') .orWhere(new Brackets(qb => { qb // または フォロワー宛ての投稿であり、 @@ -228,7 +228,7 @@ export class QueryService { })); })); - q.setParameters({ meId: me.id }); + q.setParameters({ meId: me.id, meIdAsList: [me.id] }); } } diff --git a/packages/backend/src/core/ReversiService.ts b/packages/backend/src/core/ReversiService.ts new file mode 100644 index 000000000..4f68fabc4 --- /dev/null +++ b/packages/backend/src/core/ReversiService.ts @@ -0,0 +1,555 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import * as Redis from 'ioredis'; +import CRC32 from 'crc-32'; +import { ModuleRef } from '@nestjs/core'; +import * as Reversi from 'misskey-reversi'; +import { IsNull } from 'typeorm'; +import type { + MiReversiGame, + ReversiGamesRepository, +} from '@/models/_.js'; +import type { MiUser } from '@/models/User.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { CacheService } from '@/core/CacheService.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { GlobalEventService } from '@/core/GlobalEventService.js'; +import { IdService } from '@/core/IdService.js'; +import { NotificationService } from '@/core/NotificationService.js'; +import { Serialized } from '@/types.js'; +import { ReversiGameEntityService } from './entities/ReversiGameEntityService.js'; +import type { OnApplicationShutdown, OnModuleInit } from '@nestjs/common'; + +const MATCHING_TIMEOUT_MS = 1000 * 15; // 15sec + +@Injectable() +export class ReversiService implements OnApplicationShutdown, OnModuleInit { + private notificationService: NotificationService; + + constructor( + private moduleRef: ModuleRef, + + @Inject(DI.redis) + private redisClient: Redis.Redis, + + @Inject(DI.reversiGamesRepository) + private reversiGamesRepository: ReversiGamesRepository, + + private cacheService: CacheService, + private userEntityService: UserEntityService, + private globalEventService: GlobalEventService, + private reversiGameEntityService: ReversiGameEntityService, + private idService: IdService, + ) { + } + + async onModuleInit() { + this.notificationService = this.moduleRef.get(NotificationService.name); + } + + @bindThis + private async cacheGame(game: MiReversiGame) { + await this.redisClient.setex(`reversi:game:cache:${game.id}`, 60 * 60, JSON.stringify(game)); + } + + @bindThis + private async deleteGameCache(gameId: MiReversiGame['id']) { + await this.redisClient.del(`reversi:game:cache:${gameId}`); + } + + @bindThis + private getBakeProps(game: MiReversiGame) { + return { + startedAt: game.startedAt, + endedAt: game.endedAt, + // ゲームの途中からユーザーが変わることは無いので + //user1Id: game.user1Id, + //user2Id: game.user2Id, + user1Ready: game.user1Ready, + user2Ready: game.user2Ready, + black: game.black, + isStarted: game.isStarted, + isEnded: game.isEnded, + winnerId: game.winnerId, + surrenderedUserId: game.surrenderedUserId, + timeoutUserId: game.timeoutUserId, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + timeLimitForEachTurn: game.timeLimitForEachTurn, + logs: game.logs, + map: game.map, + bw: game.bw, + crc32: game.crc32, + } satisfies Partial; + } + + @bindThis + public async matchSpecificUser(me: MiUser, targetUser: MiUser): Promise { + if (targetUser.id === me.id) { + throw new Error('You cannot match yourself.'); + } + + const invitations = await this.redisClient.zrange( + `reversi:matchSpecific:${me.id}`, + Date.now() - MATCHING_TIMEOUT_MS, + '+inf', + 'BYSCORE'); + + if (invitations.includes(targetUser.id)) { + await this.redisClient.zrem(`reversi:matchSpecific:${me.id}`, targetUser.id); + + const game = await this.matched(targetUser.id, me.id); + + return game; + } else { + this.redisClient.zadd(`reversi:matchSpecific:${targetUser.id}`, Date.now(), me.id); + + this.globalEventService.publishReversiStream(targetUser.id, 'invited', { + user: await this.userEntityService.pack(me, targetUser), + }); + + return null; + } + } + + @bindThis + public async matchAnyUser(me: MiUser): Promise { + //#region まず自分宛ての招待を探す + const invitations = await this.redisClient.zrange( + `reversi:matchSpecific:${me.id}`, + Date.now() - MATCHING_TIMEOUT_MS, + '+inf', + 'BYSCORE'); + + if (invitations.length > 0) { + const invitorId = invitations[Math.floor(Math.random() * invitations.length)]; + await this.redisClient.zrem(`reversi:matchSpecific:${me.id}`, invitorId); + + const game = await this.matched(invitorId, me.id); + + return game; + } + //#endregion + + const matchings = await this.redisClient.zrange( + 'reversi:matchAny', + Date.now() - MATCHING_TIMEOUT_MS, + '+inf', + 'BYSCORE'); + + const userIds = matchings.filter(id => id !== me.id); + + if (userIds.length > 0) { + // pick random + const matchedUserId = userIds[Math.floor(Math.random() * userIds.length)]; + + await this.redisClient.zrem('reversi:matchAny', me.id, matchedUserId); + + const game = await this.matched(matchedUserId, me.id); + + return game; + } else { + await this.redisClient.zadd('reversi:matchAny', Date.now(), me.id); + return null; + } + } + + @bindThis + public async matchSpecificUserCancel(user: MiUser, targetUserId: MiUser['id']) { + await this.redisClient.zrem(`reversi:matchSpecific:${targetUserId}`, user.id); + } + + @bindThis + public async matchAnyUserCancel(user: MiUser) { + await this.redisClient.zrem('reversi:matchAny', user.id); + } + + @bindThis + public async gameReady(gameId: MiReversiGame['id'], user: MiUser, ready: boolean) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isStarted) return; + + let isBothReady = false; + + if (game.user1Id === user.id) { + const updatedGame = { + ...game, + user1Ready: ready, + }; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'changeReadyStates', { + user1: ready, + user2: updatedGame.user2Ready, + }); + + if (ready && updatedGame.user2Ready) isBothReady = true; + } else if (game.user2Id === user.id) { + const updatedGame = { + ...game, + user2Ready: ready, + }; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'changeReadyStates', { + user1: updatedGame.user1Ready, + user2: ready, + }); + + if (ready && updatedGame.user1Ready) isBothReady = true; + } else { + return; + } + + if (isBothReady) { + // 3秒後、両者readyならゲーム開始 + setTimeout(async () => { + const freshGame = await this.get(game.id); + if (freshGame == null || freshGame.isStarted || freshGame.isEnded) return; + if (!freshGame.user1Ready || !freshGame.user2Ready) return; + + this.startGame(freshGame); + }, 3000); + } + } + + @bindThis + private async matched(parentId: MiUser['id'], childId: MiUser['id']): Promise { + const game = await this.reversiGamesRepository.insert({ + id: this.idService.gen(), + user1Id: parentId, + user2Id: childId, + user1Ready: false, + user2Ready: false, + isStarted: false, + isEnded: false, + logs: [], + map: Reversi.maps.eighteight.data, + bw: 'random', + isLlotheo: false, + }).then(x => this.reversiGamesRepository.findOneOrFail({ + where: { id: x.identifiers[0].id }, + relations: ['user1', 'user2'], + })); + this.cacheGame(game); + + const packed = await this.reversiGameEntityService.packDetail(game); + this.globalEventService.publishReversiStream(parentId, 'matched', { game: packed }); + + return game; + } + + @bindThis + private async startGame(game: MiReversiGame) { + let bw: number; + if (game.bw === 'random') { + bw = Math.random() > 0.5 ? 1 : 2; + } else { + bw = parseInt(game.bw, 10); + } + + const crc32 = CRC32.str(JSON.stringify(game.logs)).toString(); + + const updatedGame = await this.reversiGamesRepository.createQueryBuilder().update() + .set({ + ...this.getBakeProps(game), + startedAt: new Date(), + isStarted: true, + black: bw, + map: game.map, + crc32, + }) + .where('id = :id', { id: game.id }) + .returning('*') + .execute() + .then((response) => response.raw[0]); + // キャッシュ効率化のためにユーザー情報は再利用 + updatedGame.user1 = game.user1; + updatedGame.user2 = game.user2; + this.cacheGame(updatedGame); + + //#region 盤面に最初から石がないなどして始まった瞬間に勝敗が決定する場合があるのでその処理 + const engine = new Reversi.Game(updatedGame.map, { + isLlotheo: updatedGame.isLlotheo, + canPutEverywhere: updatedGame.canPutEverywhere, + loopedBoard: updatedGame.loopedBoard, + }); + + if (engine.isEnded) { + let winnerId; + if (engine.winner === true) { + winnerId = bw === 1 ? updatedGame.user1Id : updatedGame.user2Id; + } else if (engine.winner === false) { + winnerId = bw === 1 ? updatedGame.user2Id : updatedGame.user1Id; + } else { + winnerId = null; + } + + await this.endGame(updatedGame, winnerId, null); + + return; + } + //#endregion + + this.redisClient.setex(`reversi:game:turnTimer:${game.id}:1`, updatedGame.timeLimitForEachTurn, ''); + + this.globalEventService.publishReversiGameStream(game.id, 'started', { + game: await this.reversiGameEntityService.packDetail(updatedGame), + }); + } + + @bindThis + private async endGame(game: MiReversiGame, winnerId: MiUser['id'] | null, reason: 'surrender' | 'timeout' | null) { + const updatedGame = await this.reversiGamesRepository.createQueryBuilder().update() + .set({ + ...this.getBakeProps(game), + isEnded: true, + endedAt: new Date(), + winnerId: winnerId, + surrenderedUserId: reason === 'surrender' ? (winnerId === game.user1Id ? game.user2Id : game.user1Id) : null, + timeoutUserId: reason === 'timeout' ? (winnerId === game.user1Id ? game.user2Id : game.user1Id) : null, + }) + .where('id = :id', { id: game.id }) + .returning('*') + .execute() + .then((response) => response.raw[0]); + // キャッシュ効率化のためにユーザー情報は再利用 + updatedGame.user1 = game.user1; + updatedGame.user2 = game.user2; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'ended', { + winnerId: winnerId, + game: await this.reversiGameEntityService.packDetail(updatedGame), + }); + } + + @bindThis + public async getInvitations(user: MiUser): Promise { + const invitations = await this.redisClient.zrange( + `reversi:matchSpecific:${user.id}`, + Date.now() - MATCHING_TIMEOUT_MS, + '+inf', + 'BYSCORE'); + return invitations; + } + + @bindThis + public async updateSettings(gameId: MiReversiGame['id'], user: MiUser, key: string, value: any) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isStarted) return; + if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) return; + if ((game.user1Id === user.id) && game.user1Ready) return; + if ((game.user2Id === user.id) && game.user2Ready) return; + + if (!['map', 'bw', 'isLlotheo', 'canPutEverywhere', 'loopedBoard', 'timeLimitForEachTurn'].includes(key)) return; + + // TODO: より厳格なバリデーション + + const updatedGame = { + ...game, + [key]: value, + }; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'updateSettings', { + userId: user.id, + key: key, + value: value, + }); + } + + @bindThis + public async putStoneToGame(gameId: MiReversiGame['id'], user: MiUser, pos: number, id?: string | null) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (!game.isStarted) return; + if (game.isEnded) return; + if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) return; + + const myColor = + ((game.user1Id === user.id) && game.black === 1) || ((game.user2Id === user.id) && game.black === 2) + ? true + : false; + + const engine = Reversi.Serializer.restoreGame({ + map: game.map, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + logs: game.logs, + }); + + if (engine.turn !== myColor) return; + if (!engine.canPut(myColor, pos)) return; + + engine.putStone(pos); + + const logs = Reversi.Serializer.deserializeLogs(game.logs); + + const log = { + time: Date.now(), + player: myColor, + operation: 'put', + pos, + } as const; + + logs.push(log); + + const serializeLogs = Reversi.Serializer.serializeLogs(logs); + + const crc32 = CRC32.str(JSON.stringify(serializeLogs)).toString(); + + const updatedGame = { + ...game, + crc32, + logs: serializeLogs, + }; + this.cacheGame(updatedGame); + + this.globalEventService.publishReversiGameStream(game.id, 'log', { + ...log, + id: id ?? null, + }); + + if (engine.isEnded) { + let winnerId; + if (engine.winner === true) { + winnerId = game.black === 1 ? game.user1Id : game.user2Id; + } else if (engine.winner === false) { + winnerId = game.black === 1 ? game.user2Id : game.user1Id; + } else { + winnerId = null; + } + + await this.endGame(updatedGame, winnerId, null); + } else { + this.redisClient.setex(`reversi:game:turnTimer:${game.id}:${engine.turn ? '1' : '0'}`, updatedGame.timeLimitForEachTurn, ''); + } + } + + @bindThis + public async surrender(gameId: MiReversiGame['id'], user: MiUser) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isEnded) return; + if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) return; + + const winnerId = game.user1Id === user.id ? game.user2Id : game.user1Id; + + await this.endGame(game, winnerId, 'surrender'); + } + + @bindThis + public async checkTimeout(gameId: MiReversiGame['id']) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isEnded) return; + + const engine = Reversi.Serializer.restoreGame({ + map: game.map, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + logs: game.logs, + }); + + if (engine.turn == null) return; + + const timer = await this.redisClient.exists(`reversi:game:turnTimer:${game.id}:${engine.turn ? '1' : '0'}`); + + if (timer === 0) { + const winnerId = engine.turn ? (game.black === 1 ? game.user2Id : game.user1Id) : (game.black === 1 ? game.user1Id : game.user2Id); + + await this.endGame(game, winnerId, 'timeout'); + } + } + + @bindThis + public async cancelGame(gameId: MiReversiGame['id'], user: MiUser) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + if (game.isStarted) return; + if ((game.user1Id !== user.id) && (game.user2Id !== user.id)) return; + + await this.reversiGamesRepository.delete(game.id); + this.deleteGameCache(game.id); + + this.globalEventService.publishReversiGameStream(game.id, 'canceled', { + userId: user.id, + }); + } + + @bindThis + public async get(id: MiReversiGame['id']): Promise { + const cached = await this.redisClient.get(`reversi:game:cache:${id}`); + if (cached != null) { + // TODO: この辺りのデシリアライズ処理をどこか別のサービスに切り出したい + const parsed = JSON.parse(cached) as Serialized; + return { + ...parsed, + startedAt: parsed.startedAt != null ? new Date(parsed.startedAt) : null, + endedAt: parsed.endedAt != null ? new Date(parsed.endedAt) : null, + user1: parsed.user1 != null ? { + ...parsed.user1, + avatar: null, + banner: null, + background: null, + updatedAt: parsed.user1.updatedAt != null ? new Date(parsed.user1.updatedAt) : null, + lastActiveDate: parsed.user1.lastActiveDate != null ? new Date(parsed.user1.lastActiveDate) : null, + lastFetchedAt: parsed.user1.lastFetchedAt != null ? new Date(parsed.user1.lastFetchedAt) : null, + movedAt: parsed.user1.movedAt != null ? new Date(parsed.user1.movedAt) : null, + } : null, + user2: parsed.user2 != null ? { + ...parsed.user2, + avatar: null, + banner: null, + background: null, + updatedAt: parsed.user2.updatedAt != null ? new Date(parsed.user2.updatedAt) : null, + lastActiveDate: parsed.user2.lastActiveDate != null ? new Date(parsed.user2.lastActiveDate) : null, + lastFetchedAt: parsed.user2.lastFetchedAt != null ? new Date(parsed.user2.lastFetchedAt) : null, + movedAt: parsed.user2.movedAt != null ? new Date(parsed.user2.movedAt) : null, + } : null, + }; + } else { + const game = await this.reversiGamesRepository.findOne({ + where: { id }, + relations: ['user1', 'user2'], + }); + if (game == null) return null; + + this.cacheGame(game); + + return game; + } + } + + @bindThis + public async checkCrc(gameId: MiReversiGame['id'], crc32: string | number) { + const game = await this.get(gameId); + if (game == null) throw new Error('game not found'); + + if (crc32.toString() !== game.crc32) { + return await this.reversiGameEntityService.packDetail(game); + } else { + return null; + } + } + + @bindThis + public dispose(): void { + } + + @bindThis + public onApplicationShutdown(signal?: string | undefined): void { + this.dispose(); + } +} diff --git a/packages/backend/src/core/RoleService.ts b/packages/backend/src/core/RoleService.ts index dcd9d7399..c37b24e3f 100644 --- a/packages/backend/src/core/RoleService.ts +++ b/packages/backend/src/core/RoleService.ts @@ -181,9 +181,11 @@ export class RoleService implements OnApplicationShutdown, OnModuleInit { case 'userRoleAssigned': { const cached = this.roleAssignmentByUserIdCache.get(body.userId); if (cached) { - cached.push({ + cached.push({ // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい ...body, expiresAt: body.expiresAt ? new Date(body.expiresAt) : null, + user: null, // joinなカラムは通常取ってこないので + role: null, // joinなカラムは通常取ってこないので }); } break; diff --git a/packages/backend/src/core/WebhookService.ts b/packages/backend/src/core/WebhookService.ts index 930e6ef64..5937bf89b 100644 --- a/packages/backend/src/core/WebhookService.ts +++ b/packages/backend/src/core/WebhookService.ts @@ -49,9 +49,10 @@ export class WebhookService implements OnApplicationShutdown { switch (type) { case 'webhookCreated': if (body.active) { - this.webhooks.push({ + this.webhooks.push({ // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい ...body, latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, + user: null, // joinなカラムは通常取ってこないので }); } break; @@ -59,14 +60,16 @@ export class WebhookService implements OnApplicationShutdown { if (body.active) { const i = this.webhooks.findIndex(a => a.id === body.id); if (i > -1) { - this.webhooks[i] = { + this.webhooks[i] = { // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい ...body, latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, + user: null, // joinなカラムは通常取ってこないので }; } else { - this.webhooks.push({ + this.webhooks.push({ // TODO: このあたりのデシリアライズ処理は各modelファイル内に関数としてexportしたい ...body, latestSentAt: body.latestSentAt ? new Date(body.latestSentAt) : null, + user: null, // joinなカラムは通常取ってこないので }); } } else { diff --git a/packages/backend/src/core/activitypub/ApAudienceService.ts b/packages/backend/src/core/activitypub/ApAudienceService.ts index 440852bdf..11fc82e8b 100644 --- a/packages/backend/src/core/activitypub/ApAudienceService.ts +++ b/packages/backend/src/core/activitypub/ApAudienceService.ts @@ -58,7 +58,7 @@ export class ApAudienceService { }; } - if (toGroups.followers.length > 0) { + if (toGroups.followers.length > 0 || ccGroups.followers.length > 0) { return { visibility: 'followers', mentionedUsers, diff --git a/packages/backend/src/core/entities/ReversiGameEntityService.ts b/packages/backend/src/core/entities/ReversiGameEntityService.ts new file mode 100644 index 000000000..6c89a7059 --- /dev/null +++ b/packages/backend/src/core/entities/ReversiGameEntityService.ts @@ -0,0 +1,118 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { DI } from '@/di-symbols.js'; +import type { ReversiGamesRepository } from '@/models/_.js'; +import { awaitAll } from '@/misc/prelude/await-all.js'; +import type { Packed } from '@/misc/json-schema.js'; +import type { } from '@/models/Blocking.js'; +import type { MiReversiGame } from '@/models/ReversiGame.js'; +import { bindThis } from '@/decorators.js'; +import { IdService } from '@/core/IdService.js'; +import { UserEntityService } from './UserEntityService.js'; + +@Injectable() +export class ReversiGameEntityService { + constructor( + @Inject(DI.reversiGamesRepository) + private reversiGamesRepository: ReversiGamesRepository, + + private userEntityService: UserEntityService, + private idService: IdService, + ) { + } + + @bindThis + public async packDetail( + src: MiReversiGame['id'] | MiReversiGame, + ): Promise> { + const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src }); + + const users = await Promise.all([ + this.userEntityService.pack(game.user1 ?? game.user1Id), + this.userEntityService.pack(game.user2 ?? game.user2Id), + ]); + + return await awaitAll({ + id: game.id, + createdAt: this.idService.parse(game.id).date.toISOString(), + startedAt: game.startedAt && game.startedAt.toISOString(), + endedAt: game.endedAt && game.endedAt.toISOString(), + isStarted: game.isStarted, + isEnded: game.isEnded, + form1: game.form1, + form2: game.form2, + user1Ready: game.user1Ready, + user2Ready: game.user2Ready, + user1Id: game.user1Id, + user2Id: game.user2Id, + user1: users[0], + user2: users[1], + winnerId: game.winnerId, + winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null, + surrenderedUserId: game.surrenderedUserId, + timeoutUserId: game.timeoutUserId, + black: game.black, + bw: game.bw, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + timeLimitForEachTurn: game.timeLimitForEachTurn, + logs: game.logs, + map: game.map, + }); + } + + @bindThis + public packDetailMany( + xs: MiReversiGame[], + ) { + return Promise.all(xs.map(x => this.packDetail(x))); + } + + @bindThis + public async packLite( + src: MiReversiGame['id'] | MiReversiGame, + ): Promise> { + const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src }); + + const users = await Promise.all([ + this.userEntityService.pack(game.user1 ?? game.user1Id), + this.userEntityService.pack(game.user2 ?? game.user2Id), + ]); + + return await awaitAll({ + id: game.id, + createdAt: this.idService.parse(game.id).date.toISOString(), + startedAt: game.startedAt && game.startedAt.toISOString(), + endedAt: game.endedAt && game.endedAt.toISOString(), + isStarted: game.isStarted, + isEnded: game.isEnded, + user1Id: game.user1Id, + user2Id: game.user2Id, + user1: users[0], + user2: users[1], + winnerId: game.winnerId, + winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null, + surrenderedUserId: game.surrenderedUserId, + timeoutUserId: game.timeoutUserId, + black: game.black, + bw: game.bw, + isLlotheo: game.isLlotheo, + canPutEverywhere: game.canPutEverywhere, + loopedBoard: game.loopedBoard, + timeLimitForEachTurn: game.timeLimitForEachTurn, + }); + } + + @bindThis + public packLiteMany( + xs: MiReversiGame[], + ) { + return Promise.all(xs.map(x => this.packLite(x))); + } +} + diff --git a/packages/backend/src/di-symbols.ts b/packages/backend/src/di-symbols.ts index b6327e5db..1f64b73ce 100644 --- a/packages/backend/src/di-symbols.ts +++ b/packages/backend/src/di-symbols.ts @@ -80,5 +80,6 @@ export const DI = { userMemosRepository: Symbol('userMemosRepository'), noteEditRepository: Symbol('noteEditRepository'), bubbleGameRecordsRepository: Symbol('bubbleGameRecordsRepository'), + reversiGamesRepository: Symbol('reversiGamesRepository'), //#endregion }; diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts index 176978d35..b4f054171 100644 --- a/packages/backend/src/misc/json-schema.ts +++ b/packages/backend/src/misc/json-schema.ts @@ -39,6 +39,7 @@ import { packedAnnouncementSchema } from '@/models/json-schema/announcement.js'; import { packedSigninSchema } from '@/models/json-schema/signin.js'; import { packedRoleLiteSchema, packedRoleSchema } from '@/models/json-schema/role.js'; import { packedAdSchema } from '@/models/json-schema/ad.js'; +import { packedReversiGameLiteSchema, packedReversiGameDetailedSchema } from '@/models/json-schema/reversi-game.js'; export const refs = { UserLite: packedUserLiteSchema, @@ -78,6 +79,8 @@ export const refs = { Signin: packedSigninSchema, RoleLite: packedRoleLiteSchema, Role: packedRoleSchema, + ReversiGameLite: packedReversiGameLiteSchema, + ReversiGameDetailed: packedReversiGameDetailedSchema, }; export type Packed = SchemaType; diff --git a/packages/backend/src/models/Note.ts b/packages/backend/src/models/Note.ts index 270528288..13b6b1ce9 100644 --- a/packages/backend/src/models/Note.ts +++ b/packages/backend/src/models/Note.ts @@ -11,9 +11,6 @@ import { MiChannel } from './Channel.js'; import type { MiDriveFile } from './DriveFile.js'; @Entity('note') -@Index('IDX_NOTE_TAGS', { synchronize: false }) -@Index('IDX_NOTE_MENTIONS', { synchronize: false }) -@Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false }) export class MiNote { @PrimaryColumn(id()) public id: string; @@ -139,7 +136,7 @@ export class MiNote { }) public url: string | null; - @Index() + @Index('IDX_NOTE_FILE_IDS', { synchronize: false }) @Column({ ...id(), array: true, default: '{}', @@ -151,14 +148,14 @@ export class MiNote { }) public attachedFileTypes: string[]; - @Index() + @Index('IDX_NOTE_VISIBLE_USER_IDS', { synchronize: false }) @Column({ ...id(), array: true, default: '{}', }) public visibleUserIds: MiUser['id'][]; - @Index() + @Index('IDX_NOTE_MENTIONS', { synchronize: false }) @Column({ ...id(), array: true, default: '{}', @@ -180,7 +177,7 @@ export class MiNote { }) public emojis: string[]; - @Index() + @Index('IDX_NOTE_TAGS', { synchronize: false }) @Column('varchar', { length: 128, array: true, default: '{}', }) diff --git a/packages/backend/src/models/RepositoryModule.ts b/packages/backend/src/models/RepositoryModule.ts index 9c42b3174..19e95603b 100644 --- a/packages/backend/src/models/RepositoryModule.ts +++ b/packages/backend/src/models/RepositoryModule.ts @@ -5,7 +5,7 @@ import { Module } from '@nestjs/common'; import { DI } from '@/di-symbols.js'; -import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, NoteEdit, MiBubbleGameRecord } from './_.js'; +import { MiAbuseUserReport, MiAccessToken, MiAd, MiAnnouncement, MiAnnouncementRead, MiAntenna, MiApp, MiAuthSession, MiAvatarDecoration, MiBlocking, MiChannel, MiChannelFavorite, MiChannelFollowing, MiClip, MiClipFavorite, MiClipNote, MiDriveFile, MiDriveFolder, MiEmoji, MiFlash, MiFlashLike, MiFollowRequest, MiFollowing, MiGalleryLike, MiGalleryPost, MiHashtag, MiInstance, MiMeta, MiModerationLog, MiMuting, MiNote, MiNoteFavorite, MiNoteReaction, MiNoteThreadMuting, MiNoteUnread, MiPage, MiPageLike, MiPasswordResetRequest, MiPoll, MiPollVote, MiPromoNote, MiPromoRead, MiRegistrationTicket, MiRegistryItem, MiRelay, MiRenoteMuting, MiRetentionAggregation, MiRole, MiRoleAssignment, MiSignin, MiSwSubscription, MiUsedUsername, MiUser, MiUserIp, MiUserKeypair, MiUserList, MiUserListFavorite, MiUserListMembership, MiUserMemo, MiUserNotePining, MiUserPending, MiUserProfile, MiUserPublickey, MiUserSecurityKey, MiWebhook, NoteEdit, MiBubbleGameRecord, MiReversiGame } from './_.js'; import type { DataSource } from 'typeorm'; import type { Provider } from '@nestjs/common'; @@ -411,6 +411,12 @@ const $bubbleGameRecordsRepository: Provider = { inject: [DI.db], }; +const $reversiGamesRepository: Provider = { + provide: DI.reversiGamesRepository, + useFactory: (db: DataSource) => db.getRepository(MiReversiGame), + inject: [DI.db], +}; + @Module({ imports: [ ], @@ -482,6 +488,7 @@ const $bubbleGameRecordsRepository: Provider = { $userMemosRepository, $noteEditRepository, $bubbleGameRecordsRepository, + $reversiGamesRepository, ], exports: [ $usersRepository, @@ -551,6 +558,7 @@ const $bubbleGameRecordsRepository: Provider = { $userMemosRepository, $noteEditRepository, $bubbleGameRecordsRepository, + $reversiGamesRepository, ], }) export class RepositoryModule {} diff --git a/packages/backend/src/models/ReversiGame.ts b/packages/backend/src/models/ReversiGame.ts new file mode 100644 index 000000000..11d236e45 --- /dev/null +++ b/packages/backend/src/models/ReversiGame.ts @@ -0,0 +1,138 @@ +import { PrimaryColumn, Entity, Index, JoinColumn, Column, ManyToOne } from 'typeorm'; +import { id } from './util/id.js'; +import { MiUser } from './User.js'; + +@Entity('reversi_game') +export class MiReversiGame { + @PrimaryColumn(id()) + public id: string; + + @Column('timestamp with time zone', { + nullable: true, + comment: 'The started date of the ReversiGame.', + }) + public startedAt: Date | null; + + @Column('timestamp with time zone', { + nullable: true, + comment: 'The ended date of the ReversiGame.', + }) + public endedAt: Date | null; + + @Column(id()) + public user1Id: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user1: MiUser | null; + + @Column(id()) + public user2Id: MiUser['id']; + + @ManyToOne(type => MiUser, { + onDelete: 'CASCADE', + }) + @JoinColumn() + public user2: MiUser | null; + + @Column('boolean', { + default: false, + }) + public user1Ready: boolean; + + @Column('boolean', { + default: false, + }) + public user2Ready: boolean; + + /** + * どちらのプレイヤーが先行(黒)か + * 1 ... user1 + * 2 ... user2 + */ + @Column('integer', { + nullable: true, + }) + public black: number | null; + + @Column('boolean', { + default: false, + }) + public isStarted: boolean; + + @Column('boolean', { + default: false, + }) + public isEnded: boolean; + + @Column({ + ...id(), + nullable: true, + }) + public winnerId: MiUser['id'] | null; + + @Column({ + ...id(), + nullable: true, + }) + public surrenderedUserId: MiUser['id'] | null; + + @Column({ + ...id(), + nullable: true, + }) + public timeoutUserId: MiUser['id'] | null; + + // in sec + @Column('smallint', { + default: 90, + }) + public timeLimitForEachTurn: number; + + @Column('jsonb', { + default: [], + }) + public logs: number[][]; + + @Column('varchar', { + array: true, length: 64, + }) + public map: string[]; + + @Column('varchar', { + length: 32, + }) + public bw: string; + + @Column('boolean', { + default: false, + }) + public isLlotheo: boolean; + + @Column('boolean', { + default: false, + }) + public canPutEverywhere: boolean; + + @Column('boolean', { + default: false, + }) + public loopedBoard: boolean; + + @Column('jsonb', { + nullable: true, default: null, + }) + public form1: any | null; + + @Column('jsonb', { + nullable: true, default: null, + }) + public form2: any | null; + + @Column('varchar', { + length: 32, nullable: true, + }) + public crc32: string | null; +} diff --git a/packages/backend/src/models/_.ts b/packages/backend/src/models/_.ts index 53b9f44cc..64a917f3e 100644 --- a/packages/backend/src/models/_.ts +++ b/packages/backend/src/models/_.ts @@ -70,6 +70,8 @@ import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserListFavorite } from '@/models/UserListFavorite.js'; import { NoteEdit } from '@/models/NoteEdit.js'; import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js'; +import { MiReversiGame } from '@/models/ReversiGame.js'; + import type { Repository } from 'typeorm'; export { @@ -140,6 +142,7 @@ export { MiUserMemo, NoteEdit, MiBubbleGameRecord, + MiReversiGame, }; export type AbuseUserReportsRepository = Repository; @@ -209,3 +212,4 @@ export type FlashLikesRepository = Repository; export type UserMemoRepository = Repository; export type NoteEditRepository = Repository; export type BubbleGameRecordsRepository = Repository; +export type ReversiGamesRepository = Repository; diff --git a/packages/backend/src/models/json-schema/reversi-game.ts b/packages/backend/src/models/json-schema/reversi-game.ts new file mode 100644 index 000000000..f8a5e7451 --- /dev/null +++ b/packages/backend/src/models/json-schema/reversi-game.ts @@ -0,0 +1,232 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +export const packedReversiGameLiteSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + startedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, + endedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, + isStarted: { + type: 'boolean', + optional: false, nullable: false, + }, + isEnded: { + type: 'boolean', + optional: false, nullable: false, + }, + user1Id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user2Id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user1: { + type: 'object', + optional: false, nullable: false, + ref: 'User', + }, + user2: { + type: 'object', + optional: false, nullable: false, + ref: 'User', + }, + winnerId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + winner: { + type: 'object', + optional: false, nullable: true, + ref: 'User', + }, + surrenderedUserId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + timeoutUserId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + black: { + type: 'number', + optional: false, nullable: true, + }, + bw: { + type: 'string', + optional: false, nullable: false, + }, + isLlotheo: { + type: 'boolean', + optional: false, nullable: false, + }, + canPutEverywhere: { + type: 'boolean', + optional: false, nullable: false, + }, + loopedBoard: { + type: 'boolean', + optional: false, nullable: false, + }, + timeLimitForEachTurn: { + type: 'number', + optional: false, nullable: false, + }, + }, +} as const; + +export const packedReversiGameDetailedSchema = { + type: 'object', + properties: { + id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + createdAt: { + type: 'string', + optional: false, nullable: false, + format: 'date-time', + }, + startedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, + endedAt: { + type: 'string', + optional: false, nullable: true, + format: 'date-time', + }, + isStarted: { + type: 'boolean', + optional: false, nullable: false, + }, + isEnded: { + type: 'boolean', + optional: false, nullable: false, + }, + form1: { + type: 'object', + optional: false, nullable: true, + }, + form2: { + type: 'object', + optional: false, nullable: true, + }, + user1Ready: { + type: 'boolean', + optional: false, nullable: false, + }, + user2Ready: { + type: 'boolean', + optional: false, nullable: false, + }, + user1Id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user2Id: { + type: 'string', + optional: false, nullable: false, + format: 'id', + }, + user1: { + type: 'object', + optional: false, nullable: false, + ref: 'User', + }, + user2: { + type: 'object', + optional: false, nullable: false, + ref: 'User', + }, + winnerId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + winner: { + type: 'object', + optional: false, nullable: true, + ref: 'User', + }, + surrenderedUserId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + timeoutUserId: { + type: 'string', + optional: false, nullable: true, + format: 'id', + }, + black: { + type: 'number', + optional: false, nullable: true, + }, + bw: { + type: 'string', + optional: false, nullable: false, + }, + isLlotheo: { + type: 'boolean', + optional: false, nullable: false, + }, + canPutEverywhere: { + type: 'boolean', + optional: false, nullable: false, + }, + loopedBoard: { + type: 'boolean', + optional: false, nullable: false, + }, + timeLimitForEachTurn: { + type: 'number', + optional: false, nullable: false, + }, + logs: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'array', + optional: false, nullable: false, + }, + }, + map: { + type: 'array', + optional: false, nullable: false, + items: { + type: 'string', + optional: false, nullable: false, + }, + }, + }, +} as const; diff --git a/packages/backend/src/postgres.ts b/packages/backend/src/postgres.ts index 395c7ab44..a43b13242 100644 --- a/packages/backend/src/postgres.ts +++ b/packages/backend/src/postgres.ts @@ -78,6 +78,7 @@ import { MiFlashLike } from '@/models/FlashLike.js'; import { MiUserMemo } from '@/models/UserMemo.js'; import { NoteEdit } from '@/models/NoteEdit.js'; import { MiBubbleGameRecord } from '@/models/BubbleGameRecord.js'; +import { MiReversiGame } from '@/models/ReversiGame.js'; import { Config } from '@/config.js'; import MisskeyLogger from '@/logger.js'; @@ -194,6 +195,7 @@ export const entities = [ MiUserMemo, NoteEdit, MiBubbleGameRecord, + MiReversiGame, ...charts, ]; @@ -211,22 +213,24 @@ export function createPostgresDataSource(config: Config) { statement_timeout: 1000 * 10, ...config.db.extra, }, - replication: config.dbReplications ? { - master: { - host: config.db.host, - port: config.db.port, - username: config.db.user, - password: config.db.pass, - database: config.db.db, + ...(config.dbReplications ? { + replication: { + master: { + host: config.db.host, + port: config.db.port, + username: config.db.user, + password: config.db.pass, + database: config.db.db, + }, + slaves: config.dbSlaves!.map(rep => ({ + host: rep.host, + port: rep.port, + username: rep.user, + password: rep.pass, + database: rep.db, + })), }, - slaves: config.dbSlaves!.map(rep => ({ - host: rep.host, - port: rep.port, - username: rep.user, - password: rep.pass, - database: rep.db, - })), - } : undefined, + } : {}), synchronize: process.env.NODE_ENV === 'test', dropSchema: process.env.NODE_ENV === 'test', cache: !config.db.disableCache && process.env.NODE_ENV !== 'test' ? { // dbをcloseしても何故かredisのコネクションが内部的に残り続けるようで、テストの際に支障が出るため無効にする(キャッシュも含めてテストしたいため本当は有効にしたいが...) diff --git a/packages/backend/src/server/ActivityPubServerService.ts b/packages/backend/src/server/ActivityPubServerService.ts index 24f33fd60..b1562a95f 100644 --- a/packages/backend/src/server/ActivityPubServerService.ts +++ b/packages/backend/src/server/ActivityPubServerService.ts @@ -795,6 +795,8 @@ export class ActivityPubServerService { fastify.get<{ Params: { user: string; } }>('/users/:user', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => { if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return; + + vary(reply.raw, 'Accept'); const userId = request.params.user; @@ -809,6 +811,8 @@ export class ActivityPubServerService { fastify.get<{ Params: { user: string; } }>('/@:user', { constraints: { apOrHtml: 'ap' } }, async (request, reply) => { if (await this.shouldRefuseGetRequest(request, reply, request.params.user)) return; + + vary(reply.raw, 'Accept'); const user = await this.usersRepository.findOneBy({ usernameLower: request.params.user.toLowerCase(), diff --git a/packages/backend/src/server/FileServerService.ts b/packages/backend/src/server/FileServerService.ts index 61e8e8c84..e8de8b7a5 100644 --- a/packages/backend/src/server/FileServerService.ts +++ b/packages/backend/src/server/FileServerService.ts @@ -166,11 +166,35 @@ export class FileServerService { } if (!image) { - image = { - data: fs.createReadStream(file.path), - ext: file.ext, - type: file.mime, - }; + if (request.headers.range && file.file.size > 0) { + const range = request.headers.range as string; + const parts = range.replace(/bytes=/, '').split('-'); + const start = parseInt(parts[0], 10); + let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1; + if (end > file.file.size) { + end = file.file.size - 1; + } + const chunksize = end - start + 1; + + image = { + data: fs.createReadStream(file.path, { + start, + end, + }), + ext: file.ext, + type: file.mime, + }; + + reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`); + reply.header('Accept-Ranges', 'bytes'); + reply.header('Content-Length', chunksize); + } else { + image = { + data: fs.createReadStream(file.path), + ext: file.ext, + type: file.mime, + }; + } } if ('pipe' in image.data && typeof image.data.pipe === 'function') { @@ -201,11 +225,54 @@ export class FileServerService { reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.mime) ? file.mime : 'application/octet-stream'); reply.header('Cache-Control', 'max-age=31536000, immutable'); reply.header('Content-Disposition', contentDisposition('inline', filename)); + + if (request.headers.range && file.file.size > 0) { + const range = request.headers.range as string; + const parts = range.replace(/bytes=/, '').split('-'); + const start = parseInt(parts[0], 10); + let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1; + if (end > file.file.size) { + end = file.file.size - 1; + } + const chunksize = end - start + 1; + const fileStream = fs.createReadStream(file.path, { + start, + end, + }); + reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`); + reply.header('Accept-Ranges', 'bytes'); + reply.header('Content-Length', chunksize); + reply.code(206); + return fileStream; + } + return fs.createReadStream(file.path); } else { reply.header('Content-Type', FILE_TYPE_BROWSERSAFE.includes(file.file.type) ? file.file.type : 'application/octet-stream'); reply.header('Cache-Control', 'max-age=31536000, immutable'); reply.header('Content-Disposition', contentDisposition('inline', file.filename)); + + if (request.headers.range && file.file.size > 0) { + const range = request.headers.range as string; + const parts = range.replace(/bytes=/, '').split('-'); + const start = parseInt(parts[0], 10); + let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1; + console.log(end); + if (end > file.file.size) { + end = file.file.size - 1; + } + const chunksize = end - start + 1; + const fileStream = fs.createReadStream(file.path, { + start, + end, + }); + reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`); + reply.header('Accept-Ranges', 'bytes'); + reply.header('Content-Length', chunksize); + reply.code(206); + return fileStream; + } + return fs.createReadStream(file.path); } } catch (e) { @@ -338,11 +405,35 @@ export class FileServerService { } if (!image) { - image = { - data: fs.createReadStream(file.path), - ext: file.ext, - type: file.mime, - }; + if (request.headers.range && file.file && file.file.size > 0) { + const range = request.headers.range as string; + const parts = range.replace(/bytes=/, '').split('-'); + const start = parseInt(parts[0], 10); + let end = parts[1] ? parseInt(parts[1], 10) : file.file.size - 1; + if (end > file.file.size) { + end = file.file.size - 1; + } + const chunksize = end - start + 1; + + image = { + data: fs.createReadStream(file.path, { + start, + end, + }), + ext: file.ext, + type: file.mime, + }; + + reply.header('Content-Range', `bytes ${start}-${end}/${file.file.size}`); + reply.header('Accept-Ranges', 'bytes'); + reply.header('Content-Length', chunksize); + } else { + image = { + data: fs.createReadStream(file.path), + ext: file.ext, + type: file.mime, + }; + } } if ('cleanup' in file) { diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts index 52070b515..265aa17e0 100644 --- a/packages/backend/src/server/ServerModule.ts +++ b/packages/backend/src/server/ServerModule.ts @@ -22,10 +22,14 @@ import { SigninApiService } from './api/SigninApiService.js'; import { SigninService } from './api/SigninService.js'; import { SignupApiService } from './api/SignupApiService.js'; import { StreamingApiServerService } from './api/StreamingApiServerService.js'; +import { OpenApiServerService } from './api/openapi/OpenApiServerService.js'; import { ClientServerService } from './web/ClientServerService.js'; import { MastoConverters } from './api/mastodon/converters.js'; import { FeedService } from './web/FeedService.js'; import { UrlPreviewService } from './web/UrlPreviewService.js'; +import { ClientLoggerService } from './web/ClientLoggerService.js'; +import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; + import { MainChannelService } from './api/stream/channels/main.js'; import { AdminChannelService } from './api/stream/channels/admin.js'; import { AntennaChannelService } from './api/stream/channels/antenna.js'; @@ -40,11 +44,10 @@ import { LocalTimelineChannelService } from './api/stream/channels/local-timelin import { QueueStatsChannelService } from './api/stream/channels/queue-stats.js'; import { ServerStatsChannelService } from './api/stream/channels/server-stats.js'; import { UserListChannelService } from './api/stream/channels/user-list.js'; -import { OpenApiServerService } from './api/openapi/OpenApiServerService.js'; import { MastodonApiServerService } from './api/mastodon/MastodonApiServerService.js'; -import { ClientLoggerService } from './web/ClientLoggerService.js'; import { RoleTimelineChannelService } from './api/stream/channels/role-timeline.js'; -import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; +import { ReversiChannelService } from './api/stream/channels/reversi.js'; +import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js'; @Module({ imports: [ @@ -81,6 +84,8 @@ import { OAuth2ProviderService } from './oauth/OAuth2ProviderService.js'; BubbleTimelineChannelService, HashtagChannelService, RoleTimelineChannelService, + ReversiChannelService, + ReversiGameChannelService, HomeTimelineChannelService, HybridTimelineChannelService, LocalTimelineChannelService, diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 1f37c74c0..5b4ba9ddc 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -379,6 +379,12 @@ import * as ep___retention from './endpoints/retention.js'; import * as ep___sponsors from './endpoints/sponsors.js'; import * as ep___bubbleGame_register from './endpoints/bubble-game/register.js'; import * as ep___bubbleGame_ranking from './endpoints/bubble-game/ranking.js'; +import * as ep___reversi_cancelMatch from './endpoints/reversi/cancel-match.js'; +import * as ep___reversi_games from './endpoints/reversi/games.js'; +import * as ep___reversi_match from './endpoints/reversi/match.js'; +import * as ep___reversi_invitations from './endpoints/reversi/invitations.js'; +import * as ep___reversi_showGame from './endpoints/reversi/show-game.js'; +import * as ep___reversi_surrender from './endpoints/reversi/surrender.js'; import { GetterService } from './GetterService.js'; import { ApiLoggerService } from './ApiLoggerService.js'; import type { Provider } from '@nestjs/common'; @@ -756,6 +762,12 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention const $sponsors: Provider = { provide: 'ep:sponsors', useClass: ep___sponsors.default }; const $bubbleGame_register: Provider = { provide: 'ep:bubble-game/register', useClass: ep___bubbleGame_register.default }; const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useClass: ep___bubbleGame_ranking.default }; +const $reversi_cancelMatch: Provider = { provide: 'ep:reversi/cancel-match', useClass: ep___reversi_cancelMatch.default }; +const $reversi_games: Provider = { provide: 'ep:reversi/games', useClass: ep___reversi_games.default }; +const $reversi_match: Provider = { provide: 'ep:reversi/match', useClass: ep___reversi_match.default }; +const $reversi_invitations: Provider = { provide: 'ep:reversi/invitations', useClass: ep___reversi_invitations.default }; +const $reversi_showGame: Provider = { provide: 'ep:reversi/show-game', useClass: ep___reversi_showGame.default }; +const $reversi_surrender: Provider = { provide: 'ep:reversi/surrender', useClass: ep___reversi_surrender.default }; @Module({ imports: [ @@ -1137,6 +1149,12 @@ const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useCl $sponsors, $bubbleGame_register, $bubbleGame_ranking, + $reversi_cancelMatch, + $reversi_games, + $reversi_match, + $reversi_invitations, + $reversi_showGame, + $reversi_surrender, ], exports: [ $admin_meta, @@ -1509,6 +1527,12 @@ const $bubbleGame_ranking: Provider = { provide: 'ep:bubble-game/ranking', useCl $sponsors, $bubbleGame_register, $bubbleGame_ranking, + $reversi_cancelMatch, + $reversi_games, + $reversi_match, + $reversi_invitations, + $reversi_showGame, + $reversi_surrender, ], }) export class EndpointsModule {} diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index 7510228bb..b5d8c6d67 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -380,6 +380,12 @@ import * as ep___retention from './endpoints/retention.js'; import * as ep___sponsors from './endpoints/sponsors.js'; import * as ep___bubbleGame_register from './endpoints/bubble-game/register.js'; import * as ep___bubbleGame_ranking from './endpoints/bubble-game/ranking.js'; +import * as ep___reversi_cancelMatch from './endpoints/reversi/cancel-match.js'; +import * as ep___reversi_games from './endpoints/reversi/games.js'; +import * as ep___reversi_match from './endpoints/reversi/match.js'; +import * as ep___reversi_invitations from './endpoints/reversi/invitations.js'; +import * as ep___reversi_showGame from './endpoints/reversi/show-game.js'; +import * as ep___reversi_surrender from './endpoints/reversi/surrender.js'; const eps = [ ['admin/meta', ep___admin_meta], @@ -755,6 +761,12 @@ const eps = [ ['sponsors', ep___sponsors], ['bubble-game/register', ep___bubbleGame_register], ['bubble-game/ranking', ep___bubbleGame_ranking], + ['reversi/cancel-match', ep___reversi_cancelMatch], + ['reversi/games', ep___reversi_games], + ['reversi/match', ep___reversi_match], + ['reversi/invitations', ep___reversi_invitations], + ['reversi/show-game', ep___reversi_showGame], + ['reversi/surrender', ep___reversi_surrender], ]; interface IEndpointMetaBase { diff --git a/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts index 0cba129a0..9c057760c 100644 --- a/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts +++ b/packages/backend/src/server/api/endpoints/bubble-game/ranking.ts @@ -11,8 +11,6 @@ import { DI } from '@/di-symbols.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; export const meta = { - tags: [], - allowGet: true, cacheSec: 60, diff --git a/packages/backend/src/server/api/endpoints/bubble-game/register.ts b/packages/backend/src/server/api/endpoints/bubble-game/register.ts index af0f69e4a..8eb90fdbf 100644 --- a/packages/backend/src/server/api/endpoints/bubble-game/register.ts +++ b/packages/backend/src/server/api/endpoints/bubble-game/register.ts @@ -12,8 +12,6 @@ import { DI } from '@/di-symbols.js'; import { ApiError } from '../../error.js'; export const meta = { - tags: [], - requireCredential: true, kind: 'write:account', @@ -65,8 +63,8 @@ export default class extends Endpoint { // eslint- throw new ApiError(meta.errors.invalidSeed); } - // シードが古すぎる(1時間以上前)のも弾く - if (seedDate.getTime() < now.getTime() - 1000 * 60 * 60) { + // シードが古すぎる(5時間以上前)のも弾く + if (seedDate.getTime() < now.getTime() - 1000 * 60 * 60 * 5) { throw new ApiError(meta.errors.invalidSeed); } diff --git a/packages/backend/src/server/api/endpoints/drive/files.ts b/packages/backend/src/server/api/endpoints/drive/files.ts index b7e9d12e9..0ca31dc99 100644 --- a/packages/backend/src/server/api/endpoints/drive/files.ts +++ b/packages/backend/src/server/api/endpoints/drive/files.ts @@ -36,7 +36,7 @@ export const paramDef = { untilId: { type: 'string', format: 'misskey:id' }, folderId: { type: 'string', format: 'misskey:id', nullable: true, default: null }, type: { type: 'string', nullable: true, pattern: /^[a-zA-Z\/\-*]+$/.toString().slice(1, -1) }, - sort: { type: 'string', nullable: true, enum: ['+createdAt', '-createdAt', '+name', '-name', '+size', '-size'] }, + sort: { type: 'string', nullable: true, enum: ['+createdAt', '-createdAt', '+name', '-name', '+size', '-size', null] }, }, required: [], } as const; diff --git a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts index 14a13b09c..7a0b8b441 100644 --- a/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts +++ b/packages/backend/src/server/api/endpoints/drive/files/attached-notes.ts @@ -74,7 +74,7 @@ export default class extends Endpoint { // eslint- } const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId); - query.andWhere(':file = ANY(note.fileIds)', { file: file.id }); + query.andWhere(':file <@ note.fileIds', { file: [file.id] }); const notes = await query.limit(ps.limit).getMany(); diff --git a/packages/backend/src/server/api/endpoints/federation/instances.ts b/packages/backend/src/server/api/endpoints/federation/instances.ts index fcb4fc565..6d8ae7c02 100644 --- a/packages/backend/src/server/api/endpoints/federation/instances.ts +++ b/packages/backend/src/server/api/endpoints/federation/instances.ts @@ -62,6 +62,7 @@ export const paramDef = { '-firstRetrievedAt', '+latestRequestReceivedAt', '-latestRequestReceivedAt', + null, ], }, }, diff --git a/packages/backend/src/server/api/endpoints/hashtags/users.ts b/packages/backend/src/server/api/endpoints/hashtags/users.ts index 50aea7994..8302d2380 100644 --- a/packages/backend/src/server/api/endpoints/hashtags/users.ts +++ b/packages/backend/src/server/api/endpoints/hashtags/users.ts @@ -6,6 +6,7 @@ import { Inject, Injectable } from '@nestjs/common'; import { Endpoint } from '@/server/api/endpoint-base.js'; import type { UsersRepository } from '@/models/_.js'; +import { safeForSql } from "@/misc/safe-for-sql.js"; import { normalizeForSearch } from '@/misc/normalize-for-search.js'; import { UserEntityService } from '@/core/entities/UserEntityService.js'; import { DI } from '@/di-symbols.js'; @@ -47,8 +48,9 @@ export default class extends Endpoint { // eslint- private userEntityService: UserEntityService, ) { super(meta, paramDef, async (ps, me) => { + if (!safeForSql(normalizeForSearch(ps.tag))) throw new Error('Injection'); const query = this.usersRepository.createQueryBuilder('user') - .where(':tag = ANY(user.tags)', { tag: normalizeForSearch(ps.tag) }) + .where(':tag <@ user.tags', { tag: [normalizeForSearch(ps.tag)] }) .andWhere('user.isSuspended = FALSE'); const recent = new Date(Date.now() - (1000 * 60 * 60 * 24 * 5)); diff --git a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts index 325d54d19..7c4665857 100644 --- a/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts +++ b/packages/backend/src/server/api/endpoints/i/2fa/register-key.ts @@ -104,13 +104,13 @@ export const meta = { items: { type: 'string', enum: [ - "ble", - "cable", - "hybrid", - "internal", - "nfc", - "smart-card", - "usb", + 'ble', + 'cable', + 'hybrid', + 'internal', + 'nfc', + 'smart-card', + 'usb', ], }, }, @@ -124,8 +124,8 @@ export const meta = { authenticatorAttachment: { type: 'string', enum: [ - "cross-platform", - "platform", + 'cross-platform', + 'platform', ], }, requireResidentKey: { @@ -134,9 +134,9 @@ export const meta = { userVerification: { type: 'string', enum: [ - "discouraged", - "preferred", - "required", + 'discouraged', + 'preferred', + 'required', ], }, }, @@ -145,10 +145,11 @@ export const meta = { type: 'string', nullable: true, enum: [ - "direct", - "enterprise", - "indirect", - "none", + 'direct', + 'enterprise', + 'indirect', + 'none', + null, ], }, extensions: { diff --git a/packages/backend/src/server/api/endpoints/notes/create.test.ts b/packages/backend/src/server/api/endpoints/notes/create.test.ts index 0de5a14a9..c204f4af9 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.test.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.test.ts @@ -34,11 +34,10 @@ describe('api:notes/create', () => { .toBe(VALID); }); - // TODO - //test('null post', () => { - // expect(v({ text: null })) - // .toBe(INVALID); - //}); + test('null post', () => { + expect(v({ text: null })) + .toBe(INVALID); + }); test('0 characters post', () => { expect(v({ text: '' })) @@ -49,6 +48,11 @@ describe('api:notes/create', () => { expect(v({ text: await tooLong })) .toBe(INVALID); }); + + test('whitespace-only post', () => { + expect(v({ text: ' ' })) + .toBe(INVALID); + }); }); describe('cw', () => { diff --git a/packages/backend/src/server/api/endpoints/notes/create.ts b/packages/backend/src/server/api/endpoints/notes/create.ts index 3fef0e6aa..44ca1b9c1 100644 --- a/packages/backend/src/server/api/endpoints/notes/create.ts +++ b/packages/backend/src/server/api/endpoints/notes/create.ts @@ -177,13 +177,32 @@ export const paramDef = { }, }, // (re)note with text, files and poll are optional - anyOf: [ - { required: ['text'] }, - { required: ['renoteId'] }, - { required: ['fileIds'] }, - { required: ['mediaIds'] }, - { required: ['poll'] }, - ], + if: { + properties: { + renoteId: { + type: 'null', + }, + fileIds: { + type: 'null', + }, + mediaIds: { + type: 'null', + }, + poll: { + type: 'null', + }, + }, + }, + then: { + properties: { + text: { + type: 'string', + minLength: 1, + pattern: '[^\\s]+', + }, + }, + required: ['text'], + }, } as const; @Injectable() diff --git a/packages/backend/src/server/api/endpoints/notes/mentions.ts b/packages/backend/src/server/api/endpoints/notes/mentions.ts index 2317f8f7b..323c6c946 100644 --- a/packages/backend/src/server/api/endpoints/notes/mentions.ts +++ b/packages/backend/src/server/api/endpoints/notes/mentions.ts @@ -61,9 +61,9 @@ export default class extends Endpoint { // eslint- const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId) .andWhere(new Brackets(qb => { - qb - .where(`'{"${me.id}"}' <@ note.mentions`) - .orWhere(`'{"${me.id}"}' <@ note.visibleUserIds`); + qb // このmeIdAsListパラメータはqueryServiceのgenerateVisibilityQueryでセットされる + .where(':meIdAsList <@ note.mentions') + .orWhere(':meIdAsList <@ note.visibleUserIds'); })) // Avoid scanning primary key index .orderBy('CONCAT(note.id)', 'DESC') diff --git a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts index 89e05fd57..53544149e 100644 --- a/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts +++ b/packages/backend/src/server/api/endpoints/notes/search-by-tag.ts @@ -104,14 +104,14 @@ export default class extends Endpoint { // eslint- try { if (ps.tag) { if (!safeForSql(normalizeForSearch(ps.tag))) throw new Error('Injection'); - query.andWhere(`'{"${normalizeForSearch(ps.tag)}"}' <@ note.tags`); + query.andWhere(':tag <@ note.tags', { tag: [normalizeForSearch(ps.tag)] }); } else { query.andWhere(new Brackets(qb => { for (const tags of ps.query!) { qb.orWhere(new Brackets(qb => { for (const tag of tags) { if (!safeForSql(normalizeForSearch(tag))) throw new Error('Injection'); - qb.andWhere(`'{"${normalizeForSearch(tag)}"}' <@ note.tags`); + qb.andWhere(':tag <@ note.tags', { tag: [normalizeForSearch(tag)] }); } })); } diff --git a/packages/backend/src/server/api/endpoints/renote-mute/create.ts b/packages/backend/src/server/api/endpoints/renote-mute/create.ts index 7ff7b5de3..2d853b94f 100644 --- a/packages/backend/src/server/api/endpoints/renote-mute/create.ts +++ b/packages/backend/src/server/api/endpoints/renote-mute/create.ts @@ -73,7 +73,7 @@ export default class extends Endpoint { // eslint- } // Get mutee - const mutee = await getterService.getUser(ps.userId).catch(err => { + const mutee = await this.getterService.getUser(ps.userId).catch(err => { if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); throw err; }); diff --git a/packages/backend/src/server/api/endpoints/reversi/cancel-match.ts b/packages/backend/src/server/api/endpoints/reversi/cancel-match.ts new file mode 100644 index 000000000..8edc04950 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/cancel-match.ts @@ -0,0 +1,44 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiService } from '@/core/ReversiService.js'; + +export const meta = { + requireCredential: true, + + kind: 'write:account', + + errors: { + }, + + res: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id', nullable: true }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private reversiService: ReversiService, + ) { + super(meta, paramDef, async (ps, me) => { + if (ps.userId) { + await this.reversiService.matchSpecificUserCancel(me, ps.userId); + return; + } else { + await this.reversiService.matchAnyUserCancel(me); + } + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/games.ts b/packages/backend/src/server/api/endpoints/reversi/games.ts new file mode 100644 index 000000000..f28fe5d98 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/games.ts @@ -0,0 +1,63 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Brackets } from 'typeorm'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import { DI } from '@/di-symbols.js'; +import type { ReversiGamesRepository } from '@/models/_.js'; +import { QueryService } from '@/core/QueryService.js'; + +export const meta = { + requireCredential: false, + + res: { + type: 'array', + optional: false, nullable: false, + items: { ref: 'ReversiGameLite' }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 }, + sinceId: { type: 'string', format: 'misskey:id' }, + untilId: { type: 'string', format: 'misskey:id' }, + my: { type: 'boolean', default: false }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + @Inject(DI.reversiGamesRepository) + private reversiGamesRepository: ReversiGamesRepository, + + private reversiGameEntityService: ReversiGameEntityService, + private queryService: QueryService, + ) { + super(meta, paramDef, async (ps, me) => { + const query = this.queryService.makePaginationQuery(this.reversiGamesRepository.createQueryBuilder('game'), ps.sinceId, ps.untilId) + .andWhere('game.isStarted = TRUE') + .innerJoinAndSelect('game.user1', 'user1') + .innerJoinAndSelect('game.user2', 'user2'); + + if (ps.my && me) { + query.andWhere(new Brackets(qb => { + qb + .where('game.user1Id = :userId', { userId: me.id }) + .orWhere('game.user2Id = :userId', { userId: me.id }); + })); + } + + const games = await query.take(ps.limit).getMany(); + + return await this.reversiGameEntityService.packLiteMany(games); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/invitations.ts b/packages/backend/src/server/api/endpoints/reversi/invitations.ts new file mode 100644 index 000000000..0b7107bb0 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/invitations.ts @@ -0,0 +1,39 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { DI } from '@/di-symbols.js'; +import { UserEntityService } from '@/core/entities/UserEntityService.js'; +import { ReversiService } from '@/core/ReversiService.js'; + +export const meta = { + requireCredential: true, + + kind: 'read:account', + + res: { + type: 'array', + optional: false, nullable: false, + items: { ref: 'UserLite' }, + }, +} as const; + +export const paramDef = { +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private userEntityService: UserEntityService, + private reversiService: ReversiService, + ) { + super(meta, paramDef, async (ps, me) => { + const invitations = await this.reversiService.getInvitations(me); + + return await this.userEntityService.packMany(invitations, me); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/match.ts b/packages/backend/src/server/api/endpoints/reversi/match.ts new file mode 100644 index 000000000..1065ce5a8 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/match.ts @@ -0,0 +1,66 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiService } from '@/core/ReversiService.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import { ApiError } from '../../error.js'; +import { GetterService } from '../../GetterService.js'; + +export const meta = { + requireCredential: true, + + kind: 'write:account', + + errors: { + noSuchUser: { + message: 'No such user.', + code: 'NO_SUCH_USER', + id: '0b4f0559-b484-4e31-9581-3f73cee89b28', + }, + + isYourself: { + message: 'Target user is yourself.', + code: 'TARGET_IS_YOURSELF', + id: '96fd7bd6-d2bc-426c-a865-d055dcd2828e', + }, + }, + + res: { + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + userId: { type: 'string', format: 'misskey:id', nullable: true }, + }, + required: [], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private getterService: GetterService, + private reversiService: ReversiService, + private reversiGameEntityService: ReversiGameEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + if (ps.userId === me.id) throw new ApiError(meta.errors.isYourself); + + const target = ps.userId ? await this.getterService.getUser(ps.userId).catch(err => { + if (err.id === '15348ddd-432d-49c2-8a5a-8069753becff') throw new ApiError(meta.errors.noSuchUser); + throw err; + }) : null; + + const game = target ? await this.reversiService.matchSpecificUser(me, target) : await this.reversiService.matchAnyUser(me); + + if (game == null) return; + + return await this.reversiGameEntityService.packDetail(game); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/show-game.ts b/packages/backend/src/server/api/endpoints/reversi/show-game.ts new file mode 100644 index 000000000..86645ea4b --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/show-game.ts @@ -0,0 +1,54 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiService } from '@/core/ReversiService.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + requireCredential: false, + + errors: { + noSuchGame: { + message: 'No such game.', + code: 'NO_SUCH_GAME', + id: 'f13a03db-fae1-46c9-87f3-43c8165419e1', + }, + }, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'ReversiGameDetailed', + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + gameId: { type: 'string', format: 'misskey:id' }, + }, + required: ['gameId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private reversiService: ReversiService, + private reversiGameEntityService: ReversiGameEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const game = await this.reversiService.get(ps.gameId); + + if (game == null) { + throw new ApiError(meta.errors.noSuchGame); + } + + return await this.reversiGameEntityService.packDetail(game); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/reversi/surrender.ts b/packages/backend/src/server/api/endpoints/reversi/surrender.ts new file mode 100644 index 000000000..c809142e0 --- /dev/null +++ b/packages/backend/src/server/api/endpoints/reversi/surrender.ts @@ -0,0 +1,68 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { ReversiService } from '@/core/ReversiService.js'; +import { ApiError } from '../../error.js'; + +export const meta = { + requireCredential: true, + + kind: 'write:account', + + errors: { + noSuchGame: { + message: 'No such game.', + code: 'NO_SUCH_GAME', + id: 'ace0b11f-e0a6-4076-a30d-e8284c81b2df', + }, + + alreadyEnded: { + message: 'That game has already ended.', + code: 'ALREADY_ENDED', + id: '6c2ad4a6-cbf1-4a5b-b187-b772826cfc6d', + }, + + accessDenied: { + message: 'Access denied.', + code: 'ACCESS_DENIED', + id: '6e04164b-a992-4c93-8489-2123069973e1', + }, + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + gameId: { type: 'string', format: 'misskey:id' }, + }, + required: ['gameId'], +} as const; + +@Injectable() +export default class extends Endpoint { // eslint-disable-line import/no-default-export + constructor( + private reversiService: ReversiService, + ) { + super(meta, paramDef, async (ps, me) => { + const game = await this.reversiService.get(ps.gameId); + + if (game == null) { + throw new ApiError(meta.errors.noSuchGame); + } + + if (game.isEnded) { + throw new ApiError(meta.errors.alreadyEnded); + } + + if ((game.user1Id !== me.id) && (game.user2Id !== me.id)) { + throw new ApiError(meta.errors.accessDenied); + } + + await this.reversiService.surrender(game.id, me); + }); + } +} diff --git a/packages/backend/src/server/api/openapi/gen-spec.ts b/packages/backend/src/server/api/openapi/gen-spec.ts index 0e71510b4..971a6116b 100644 --- a/packages/backend/src/server/api/openapi/gen-spec.ts +++ b/packages/backend/src/server/api/openapi/gen-spec.ts @@ -10,7 +10,7 @@ import { schemas, convertSchemaToOpenApiSchema } from './schemas.js'; export function genOpenapiSpec(config: Config) { const spec = { - openapi: '3.0.0', + openapi: '3.1.0', info: { version: config.version, @@ -56,7 +56,7 @@ export function genOpenapiSpec(config: Config) { } } - const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res) : {}; + const resSchema = endpoint.meta.res ? convertSchemaToOpenApiSchema(endpoint.meta.res, 'res') : {}; let desc = (endpoint.meta.description ? endpoint.meta.description : 'No description provided.') + '\n\n'; @@ -71,7 +71,7 @@ export function genOpenapiSpec(config: Config) { } const requestType = endpoint.meta.requireFile ? 'multipart/form-data' : 'application/json'; - const schema = { ...endpoint.params }; + const schema = { ...convertSchemaToOpenApiSchema(endpoint.params, 'param') }; if (endpoint.meta.requireFile) { schema.properties = { @@ -210,7 +210,9 @@ export function genOpenapiSpec(config: Config) { }; spec.paths['/' + endpoint.name] = { - ...(endpoint.meta.allowGet ? { get: info } : {}), + ...(endpoint.meta.allowGet ? { + get: info, + } : {}), post: info, }; } diff --git a/packages/backend/src/server/api/openapi/schemas.ts b/packages/backend/src/server/api/openapi/schemas.ts index 2716f5f16..a862a7b74 100644 --- a/packages/backend/src/server/api/openapi/schemas.ts +++ b/packages/backend/src/server/api/openapi/schemas.ts @@ -6,32 +6,35 @@ import type { Schema } from '@/misc/json-schema.js'; import { refs } from '@/misc/json-schema.js'; -export function convertSchemaToOpenApiSchema(schema: Schema) { - // optional, refはスキーマ定義に含まれないので分離しておく +export function convertSchemaToOpenApiSchema(schema: Schema, type: 'param' | 'res') { + // optional, nullable, refはスキーマ定義に含まれないので分離しておく // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { optional, ref, ...res }: any = schema; + const { optional, nullable, ref, ...res }: any = schema; if (schema.type === 'object' && schema.properties) { - const required = Object.entries(schema.properties).filter(([k, v]) => !v.optional).map(([k]) => k); - if (required.length > 0) { + if (type === 'res') { + const required = Object.entries(schema.properties).filter(([k, v]) => !v.optional).map(([k]) => k); + if (required.length > 0) { // 空配列は許可されない - res.required = required; + res.required = required; + } } for (const k of Object.keys(schema.properties)) { - res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k]); + res.properties[k] = convertSchemaToOpenApiSchema(schema.properties[k], type); } } if (schema.type === 'array' && schema.items) { - res.items = convertSchemaToOpenApiSchema(schema.items); + res.items = convertSchemaToOpenApiSchema(schema.items, type); } - if (schema.anyOf) res.anyOf = schema.anyOf.map(convertSchemaToOpenApiSchema); - if (schema.oneOf) res.oneOf = schema.oneOf.map(convertSchemaToOpenApiSchema); - if (schema.allOf) res.allOf = schema.allOf.map(convertSchemaToOpenApiSchema); + for (const o of ['anyOf', 'oneOf', 'allOf'] as const) { + // eslint-disable-next-line @typescript-eslint/no-non-null-assertion + if (o in schema) res[o] = schema[o]!.map(schema => convertSchemaToOpenApiSchema(schema, type)); + } - if (schema.ref) { + if (type === 'res' && schema.ref) { const $ref = `#/components/schemas/${schema.ref}`; if (schema.nullable || schema.optional) { res.allOf = [{ $ref }]; @@ -40,6 +43,14 @@ export function convertSchemaToOpenApiSchema(schema: Schema) { } } + if (schema.nullable) { + if (Array.isArray(schema.type) && !schema.type.includes('null')) { + res.type.push('null'); + } else if (typeof schema.type === 'string') { + res.type = [res.type, 'null']; + } + } + return res; } @@ -72,6 +83,6 @@ export const schemas = { }, ...Object.fromEntries( - Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema)]), + Object.entries(refs).map(([key, schema]) => [key, convertSchemaToOpenApiSchema(schema, 'res')]), ), }; diff --git a/packages/backend/src/server/api/stream/ChannelsService.ts b/packages/backend/src/server/api/stream/ChannelsService.ts index 3fc3f4d31..762cd22fa 100644 --- a/packages/backend/src/server/api/stream/ChannelsService.ts +++ b/packages/backend/src/server/api/stream/ChannelsService.ts @@ -20,6 +20,8 @@ import { AntennaChannelService } from './channels/antenna.js'; import { DriveChannelService } from './channels/drive.js'; import { HashtagChannelService } from './channels/hashtag.js'; import { RoleTimelineChannelService } from './channels/role-timeline.js'; +import { ReversiChannelService } from './channels/reversi.js'; +import { ReversiGameChannelService } from './channels/reversi-game.js'; import { type MiChannelService } from './channel.js'; @Injectable() @@ -40,6 +42,8 @@ export class ChannelsService { private serverStatsChannelService: ServerStatsChannelService, private queueStatsChannelService: QueueStatsChannelService, private adminChannelService: AdminChannelService, + private reversiChannelService: ReversiChannelService, + private reversiGameChannelService: ReversiGameChannelService, ) { } @@ -61,6 +65,8 @@ export class ChannelsService { case 'serverStats': return this.serverStatsChannelService; case 'queueStats': return this.queueStatsChannelService; case 'admin': return this.adminChannelService; + case 'reversi': return this.reversiChannelService; + case 'reversiGame': return this.reversiGameChannelService; default: throw new Error(`no such channel: ${name}`); diff --git a/packages/backend/src/server/api/stream/channels/reversi-game.ts b/packages/backend/src/server/api/stream/channels/reversi-game.ts new file mode 100644 index 000000000..820c80006 --- /dev/null +++ b/packages/backend/src/server/api/stream/channels/reversi-game.ts @@ -0,0 +1,125 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Inject, Injectable } from '@nestjs/common'; +import type { MiReversiGame, ReversiGamesRepository } from '@/models/_.js'; +import { DI } from '@/di-symbols.js'; +import { bindThis } from '@/decorators.js'; +import { ReversiService } from '@/core/ReversiService.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; +import Channel, { type MiChannelService } from '../channel.js'; + +class ReversiGameChannel extends Channel { + public readonly chName = 'reversiGame'; + public static shouldShare = false; + public static requireCredential = false as const; + private gameId: MiReversiGame['id'] | null = null; + + constructor( + private reversiService: ReversiService, + private reversiGamesRepository: ReversiGamesRepository, + private reversiGameEntityService: ReversiGameEntityService, + + id: string, + connection: Channel['connection'], + ) { + super(id, connection); + } + + @bindThis + public async init(params: any) { + this.gameId = params.gameId as string; + + this.subscriber.on(`reversiGameStream:${this.gameId}`, this.send); + } + + @bindThis + public onMessage(type: string, body: any) { + switch (type) { + case 'ready': this.ready(body); break; + case 'updateSettings': this.updateSettings(body.key, body.value); break; + case 'cancel': this.cancelGame(); break; + case 'putStone': this.putStone(body.pos, body.id); break; + case 'resync': this.resync(body.crc32); break; + case 'claimTimeIsUp': this.claimTimeIsUp(); break; + } + } + + @bindThis + private async updateSettings(key: string, value: any) { + if (this.user == null) return; + + this.reversiService.updateSettings(this.gameId!, this.user, key, value); + } + + @bindThis + private async ready(ready: boolean) { + if (this.user == null) return; + + this.reversiService.gameReady(this.gameId!, this.user, ready); + } + + @bindThis + private async cancelGame() { + if (this.user == null) return; + + this.reversiService.cancelGame(this.gameId!, this.user); + } + + @bindThis + private async putStone(pos: number, id: string) { + if (this.user == null) return; + + this.reversiService.putStoneToGame(this.gameId!, this.user, pos, id); + } + + @bindThis + private async resync(crc32: string | number) { + const game = await this.reversiService.checkCrc(this.gameId!, crc32); + if (game) { + this.send('resynced', game); + } + } + + @bindThis + private async claimTimeIsUp() { + if (this.user == null) return; + + this.reversiService.checkTimeout(this.gameId!); + } + + @bindThis + public dispose() { + // Unsubscribe events + this.subscriber.off(`reversiGameStream:${this.gameId}`, this.send); + } +} + +@Injectable() +export class ReversiGameChannelService implements MiChannelService { + public readonly shouldShare = ReversiGameChannel.shouldShare; + public readonly requireCredential = ReversiGameChannel.requireCredential; + public readonly kind = ReversiGameChannel.kind; + + constructor( + @Inject(DI.reversiGamesRepository) + private reversiGamesRepository: ReversiGamesRepository, + + private reversiService: ReversiService, + private reversiGameEntityService: ReversiGameEntityService, + ) { + } + + @bindThis + public create(id: string, connection: Channel['connection']): ReversiGameChannel { + return new ReversiGameChannel( + this.reversiService, + this.reversiGamesRepository, + this.reversiGameEntityService, + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/api/stream/channels/reversi.ts b/packages/backend/src/server/api/stream/channels/reversi.ts new file mode 100644 index 000000000..cb4b1b8d5 --- /dev/null +++ b/packages/backend/src/server/api/stream/channels/reversi.ts @@ -0,0 +1,52 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +import { Injectable } from '@nestjs/common'; +import { bindThis } from '@/decorators.js'; +import Channel, { type MiChannelService } from '../channel.js'; + +class ReversiChannel extends Channel { + public readonly chName = 'reversi'; + public static shouldShare = true; + public static requireCredential = true as const; + public static kind = 'read:account'; + + constructor( + id: string, + connection: Channel['connection'], + ) { + super(id, connection); + } + + @bindThis + public async init(params: any) { + this.subscriber.on(`reversiStream:${this.user!.id}`, this.send); + } + + @bindThis + public dispose() { + // Unsubscribe events + this.subscriber.off(`reversiStream:${this.user!.id}`, this.send); + } +} + +@Injectable() +export class ReversiChannelService implements MiChannelService { + public readonly shouldShare = ReversiChannel.shouldShare; + public readonly requireCredential = ReversiChannel.requireCredential; + public readonly kind = ReversiChannel.kind; + + constructor( + ) { + } + + @bindThis + public create(id: string, connection: Channel['connection']): ReversiChannel { + return new ReversiChannel( + id, + connection, + ); + } +} diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts index 280bdb20b..9fdfafb73 100644 --- a/packages/backend/src/server/web/ClientServerService.ts +++ b/packages/backend/src/server/web/ClientServerService.ts @@ -31,12 +31,13 @@ import { PageEntityService } from '@/core/entities/PageEntityService.js'; import { GalleryPostEntityService } from '@/core/entities/GalleryPostEntityService.js'; import { ClipEntityService } from '@/core/entities/ClipEntityService.js'; import { ChannelEntityService } from '@/core/entities/ChannelEntityService.js'; -import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; +import type { ChannelsRepository, ClipsRepository, FlashsRepository, GalleryPostsRepository, MiMeta, NotesRepository, PagesRepository, ReversiGamesRepository, UserProfilesRepository, UsersRepository } from '@/models/_.js'; import type Logger from '@/logger.js'; import { deepClone } from '@/misc/clone.js'; import { bindThis } from '@/decorators.js'; import { FlashEntityService } from '@/core/entities/FlashEntityService.js'; import { RoleService } from '@/core/RoleService.js'; +import { ReversiGameEntityService } from '@/core/entities/ReversiGameEntityService.js'; import { FeedService } from './FeedService.js'; import { UrlPreviewService } from './UrlPreviewService.js'; import { ClientLoggerService } from './ClientLoggerService.js'; @@ -83,6 +84,9 @@ export class ClientServerService { @Inject(DI.flashsRepository) private flashsRepository: FlashsRepository, + @Inject(DI.reversiGamesRepository) + private reversiGamesRepository: ReversiGamesRepository, + private flashEntityService: FlashEntityService, private userEntityService: UserEntityService, private noteEntityService: NoteEntityService, @@ -90,6 +94,7 @@ export class ClientServerService { private galleryPostEntityService: GalleryPostEntityService, private clipEntityService: ClipEntityService, private channelEntityService: ChannelEntityService, + private reversiGameEntityService: ReversiGameEntityService, private metaService: MetaService, private urlPreviewService: UrlPreviewService, private feedService: FeedService, @@ -494,6 +499,8 @@ export class ClientServerService { isSuspended: false, }); + vary(reply.raw, 'Accept'); + if (user != null) { const profile = await this.userProfilesRepository.findOneByOrFail({ userId: user.id }); const meta = await this.metaService.fetch(); @@ -533,6 +540,8 @@ export class ClientServerService { return; } + vary(reply.raw, 'Accept'); + reply.redirect(`/@${user.username}${ user.host == null ? '' : '@' + user.host}`); }); @@ -700,6 +709,25 @@ export class ClientServerService { return await renderBase(reply); } }); + + // Reversi game + fastify.get<{ Params: { game: string; } }>('/reversi/g/:game', async (request, reply) => { + const game = await this.reversiGamesRepository.findOneBy({ + id: request.params.game, + }); + + if (game) { + const _game = await this.reversiGameEntityService.packDetail(game); + const meta = await this.metaService.fetch(); + reply.header('Cache-Control', 'public, max-age=3600'); + return await reply.view('reversi-game', { + game: _game, + ...this.generateCommonPugData(meta), + }); + } else { + return await renderBase(reply); + } + }); //#endregion fastify.get('/_info_card_', async (request, reply) => { diff --git a/packages/backend/src/server/web/views/reversi-game.pug b/packages/backend/src/server/web/views/reversi-game.pug new file mode 100644 index 000000000..0b5ffb2bb --- /dev/null +++ b/packages/backend/src/server/web/views/reversi-game.pug @@ -0,0 +1,20 @@ +extends ./base + +block vars + - const user1 = game.user1; + - const user2 = game.user2; + - const title = `${user1.username} vs ${user2.username}`; + - const url = `${config.url}/reversi/g/${game.id}`; + +block title + = `${title} | ${instanceName}` + +block desc + meta(name='description' content='⚫⚪Misskey Reversi⚪⚫') + +block og + meta(property='og:type' content='article') + meta(property='og:title' content= title) + meta(property='og:description' content='⚫⚪Misskey Reversi⚪⚫') + meta(property='og:url' content= url) + meta(property='twitter:card' content='summary') diff --git a/packages/backend/src/types.ts b/packages/backend/src/types.ts index e55952f29..8ff791ef9 100644 --- a/packages/backend/src/types.ts +++ b/packages/backend/src/types.ts @@ -283,7 +283,11 @@ export type Serialized = { ? (string | null) : T[K] extends Record ? Serialized - : T[K]; + : T[K] extends (Record | null) + ? (Serialized | null) + : T[K] extends (Record | undefined) + ? (Serialized | undefined) + : T[K]; }; export type FilterUnionByProperty< diff --git a/packages/backend/test/e2e/drive.ts b/packages/backend/test/e2e/drive.ts new file mode 100644 index 000000000..3a84961fc --- /dev/null +++ b/packages/backend/test/e2e/drive.ts @@ -0,0 +1,95 @@ +/* + * SPDX-FileCopyrightText: syuilo and other misskey contributors + * SPDX-License-Identifier: AGPL-3.0-only + */ + +process.env.NODE_ENV = 'test'; + +import * as assert from 'assert'; +import { MiNote } from '@/models/Note.js'; +import { api, initTestDb, makeStreamCatcher, post, signup, uploadFile } from '../utils.js'; +import type * as misskey from 'misskey-js'; +import type{ Repository } from 'typeorm' +import type { Packed } from '@/misc/json-schema.js'; + + +describe('Drive', () => { + let Notes: Repository; + + let alice: misskey.entities.SignupResponse; + let bob: misskey.entities.SignupResponse; + + beforeAll(async () => { + const connection = await initTestDb(true); + Notes = connection.getRepository(MiNote); + alice = await signup({ username: 'alice' }); + bob = await signup({ username: 'bob' }); + }, 1000 * 60 * 2); + + test('ファイルURLからアップロードできる', async () => { + // utils.js uploadUrl の処理だがAPIレスポンスも見るためここで同様の処理を書いている + + const marker = Math.random().toString(); + + const url = 'https://raw.githubusercontent.com/misskey-dev/misskey/develop/packages/backend/test/resources/Lenna.jpg' + + const catcher = makeStreamCatcher( + alice, + 'main', + (msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker, + (msg) => msg.body.file as Packed<'DriveFile'>, + 10 * 1000); + + const res = await api('drive/files/upload-from-url', { + url, + marker, + force: true, + }, alice); + + const file = await catcher; + + assert.strictEqual(res.status, 204); + assert.strictEqual(file.name, 'Lenna.jpg'); + assert.strictEqual(file.type, 'image/jpeg'); + }) + + test('ローカルからアップロードできる', async () => { + // APIレスポンスを直接使用するので utils.js uploadFile が通過することで成功とする + + const res = await uploadFile(alice, { path: 'Lenna.jpg', name: 'テスト画像' }); + + assert.strictEqual(res.body?.name, 'テスト画像.jpg'); + assert.strictEqual(res.body?.type, 'image/jpeg'); + }) + + test('添付ノート一覧を取得できる', async () => { + const ids = (await Promise.all([uploadFile(alice), uploadFile(alice), uploadFile(alice)])).map(elm => elm.body!.id) + + const note0 = await post(alice, { fileIds: [ids[0]] }); + const note1 = await post(alice, { fileIds: [ids[0], ids[1]] }); + + const attached0 = await api('drive/files/attached-notes', { fileId: ids[0] }, alice); + assert.strictEqual(attached0.body.length, 2); + assert.strictEqual(attached0.body[0].id, note1.id) + assert.strictEqual(attached0.body[1].id, note0.id) + + const attached1 = await api('drive/files/attached-notes', { fileId: ids[1] }, alice); + assert.strictEqual(attached1.body.length, 1); + assert.strictEqual(attached1.body[0].id, note1.id) + + const attached2 = await api('drive/files/attached-notes', { fileId: ids[2] }, alice); + assert.strictEqual(attached2.body.length, 0) + }) + + test('添付ノート一覧は他の人から見えない', async () => { + const file = await uploadFile(alice); + + await post(alice, { fileIds: [file.body!.id] }); + + const res = await api('drive/files/attached-notes', { fileId: file.body!.id }, bob); + assert.strictEqual(res.status, 400); + assert.strictEqual('error' in res.body, true); + + }) +}); + diff --git a/packages/backend/test/e2e/note.ts b/packages/backend/test/e2e/note.ts index 0f2e08e67..0280b051f 100644 --- a/packages/backend/test/e2e/note.ts +++ b/packages/backend/test/e2e/note.ts @@ -136,6 +136,19 @@ describe('Note', () => { assert.strictEqual(res.body.createdNote.renote.text, bobPost.text); }); + test('引用renoteで空白文字のみで構成されたtextにするとレスポンスがtext: nullになる', async () => { + const bobPost = await post(bob, { + text: 'test', + }); + const res = await api('/notes/create', { + text: ' ', + renoteId: bobPost.id, + }, alice); + + assert.strictEqual(res.status, 200); + assert.strictEqual(res.body.createdNote.text, null); + }); + test('visibility: followersでrenoteできる', async () => { const createRes = await api('/notes/create', { text: 'test', diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index 2b232a0a5..a41002cc8 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -16,6 +16,7 @@ import { DEFAULT_POLICIES } from '@/core/RoleService.js'; import { entities } from '../src/postgres.js'; import { loadConfig } from '../src/config.js'; import type * as misskey from 'misskey-js'; +import { Packed } from '@/misc/json-schema.js'; export { server as startServer, jobQueue as startJobQueue } from '@/boot/common.js'; @@ -114,6 +115,20 @@ export function randomString(chars = 'abcdefghijklmnopqrstuvwxyz0123456789', len return randomString; } +/** + * @brief プロミスにタイムアウト追加 + * @param p 待ち対象プロミス + * @param timeout 待機ミリ秒 + */ +function timeoutPromise(p: Promise, timeout: number): Promise { + return Promise.race([ + p, + new Promise((reject) =>{ + setTimeout(() => { reject(new Error('timed out')); }, timeout) + }) as never + ]); +} + export const signup = async (params?: Partial): Promise> => { const q = Object.assign({ username: randomString(), @@ -320,17 +335,16 @@ export const uploadFile = async (user?: UserToken, { path, name, blob }: UploadO }; }; -export const uploadUrl = async (user: UserToken, url: string) => { - let resolve: unknown; - const file = new Promise(ok => resolve = ok); +export const uploadUrl = async (user: UserToken, url: string): Promise> => { const marker = Math.random().toString(); - const ws = await connectStream(user, 'main', (msg) => { - if (msg.type === 'urlUploadFinished' && msg.body.marker === marker) { - ws.close(); - resolve(msg.body.file); - } - }); + const catcher = makeStreamCatcher( + user, + 'main', + (msg) => msg.type === 'urlUploadFinished' && msg.body.marker === marker, + (msg) => msg.body.file as Packed<'DriveFile'>, + 60 * 1000 + ); await api('drive/files/upload-from-url', { url, @@ -338,7 +352,7 @@ export const uploadUrl = async (user: UserToken, url: string) => { force: true, }, user); - return file; + return catcher; }; export function connectStream(user: UserToken, channel: string, listener: (message: Record) => any, params?: any): Promise { @@ -410,6 +424,35 @@ export const waitFire = async (user: UserToken, channel: string, trgr: () => any }); }; +/** + * @brief WebSocketストリームから特定条件の通知を拾うプロミスを生成 + * @param user ユーザー認証情報 + * @param channel チャンネル + * @param cond 条件 + * @param extractor 取り出し処理 + * @param timeout ミリ秒タイムアウト + * @returns 時間内に正常に処理できた場合に通知からextractorを通した値を得る + */ +export function makeStreamCatcher( + user: UserToken, + channel: string, + cond: (message: Record) => boolean, + extractor: (message: Record) => T, + timeout = 60 * 1000): Promise { + let ws: WebSocket + const p = new Promise(async (resolve) => { + ws = await connectStream(user, channel, (msg) => { + if (cond(msg)) { + resolve(extractor(msg)) + } + }); + }).finally(() => { + ws?.close(); + }); + + return timeoutPromise(p, timeout); +} + export type SimpleGetResponse = { status: number, body: any | JSDOM | null, diff --git a/packages/frontend/assets/drop-and-fusion/poi1.mp3 b/packages/frontend/assets/drop-and-fusion/collision.mp3 similarity index 100% rename from packages/frontend/assets/drop-and-fusion/poi1.mp3 rename to packages/frontend/assets/drop-and-fusion/collision.mp3 diff --git a/packages/frontend/assets/drop-and-fusion/collision_yen.mp3 b/packages/frontend/assets/drop-and-fusion/collision_yen.mp3 new file mode 100644 index 000000000..6737357f6 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/collision_yen.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/poi2.mp3 b/packages/frontend/assets/drop-and-fusion/drop.mp3 similarity index 100% rename from packages/frontend/assets/drop-and-fusion/poi2.mp3 rename to packages/frontend/assets/drop-and-fusion/drop.mp3 diff --git a/packages/frontend/assets/drop-and-fusion/drop_yen.mp3 b/packages/frontend/assets/drop-and-fusion/drop_yen.mp3 new file mode 100644 index 000000000..bbf385f15 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/drop_yen.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/bubble2.mp3 b/packages/frontend/assets/drop-and-fusion/fusion.mp3 similarity index 100% rename from packages/frontend/assets/drop-and-fusion/bubble2.mp3 rename to packages/frontend/assets/drop-and-fusion/fusion.mp3 diff --git a/packages/frontend/assets/drop-and-fusion/fusion_yen.mp3 b/packages/frontend/assets/drop-and-fusion/fusion_yen.mp3 new file mode 100644 index 000000000..e8d203fb5 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/fusion_yen.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/gameover_yen.mp3 b/packages/frontend/assets/drop-and-fusion/gameover_yen.mp3 new file mode 100644 index 000000000..c7fdcb5c8 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/gameover_yen.mp3 differ diff --git a/packages/frontend/assets/drop-and-fusion/go.png b/packages/frontend/assets/drop-and-fusion/go.png new file mode 100644 index 000000000..37468f139 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/go.png differ diff --git a/packages/frontend/assets/drop-and-fusion/cold_face.png b/packages/frontend/assets/drop-and-fusion/normal_monos/cold_face.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/cold_face.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/cold_face.png diff --git a/packages/frontend/assets/drop-and-fusion/exploding_head.png b/packages/frontend/assets/drop-and-fusion/normal_monos/exploding_head.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/exploding_head.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/exploding_head.png diff --git a/packages/frontend/assets/drop-and-fusion/face_with_open_mouth.png b/packages/frontend/assets/drop-and-fusion/normal_monos/face_with_open_mouth.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/face_with_open_mouth.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/face_with_open_mouth.png diff --git a/packages/frontend/assets/drop-and-fusion/face_with_symbols_on_mouth.png b/packages/frontend/assets/drop-and-fusion/normal_monos/face_with_symbols_on_mouth.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/face_with_symbols_on_mouth.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/face_with_symbols_on_mouth.png diff --git a/packages/frontend/assets/drop-and-fusion/grinning_squinting_face.png b/packages/frontend/assets/drop-and-fusion/normal_monos/grinning_squinting_face.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/grinning_squinting_face.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/grinning_squinting_face.png diff --git a/packages/frontend/assets/drop-and-fusion/heart_suit.png b/packages/frontend/assets/drop-and-fusion/normal_monos/heart_suit.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/heart_suit.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/heart_suit.png diff --git a/packages/frontend/assets/drop-and-fusion/pleading_face.png b/packages/frontend/assets/drop-and-fusion/normal_monos/pleading_face.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/pleading_face.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/pleading_face.png diff --git a/packages/frontend/assets/drop-and-fusion/smiling_face_with_hearts.png b/packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_hearts.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/smiling_face_with_hearts.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_hearts.png diff --git a/packages/frontend/assets/drop-and-fusion/smiling_face_with_sunglasses.png b/packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_sunglasses.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/smiling_face_with_sunglasses.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/smiling_face_with_sunglasses.png diff --git a/packages/frontend/assets/drop-and-fusion/zany_face.png b/packages/frontend/assets/drop-and-fusion/normal_monos/zany_face.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/zany_face.png rename to packages/frontend/assets/drop-and-fusion/normal_monos/zany_face.png diff --git a/packages/frontend/assets/drop-and-fusion/ready.png b/packages/frontend/assets/drop-and-fusion/ready.png new file mode 100644 index 000000000..10a87fcf5 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/ready.png differ diff --git a/packages/frontend/assets/drop-and-fusion/keycap_1.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_1.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_1.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_1.png diff --git a/packages/frontend/assets/drop-and-fusion/keycap_10.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_10.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_10.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_10.png diff --git a/packages/frontend/assets/drop-and-fusion/keycap_2.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_2.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_2.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_2.png diff --git a/packages/frontend/assets/drop-and-fusion/keycap_3.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_3.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_3.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_3.png diff --git a/packages/frontend/assets/drop-and-fusion/keycap_4.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_4.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_4.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_4.png diff --git a/packages/frontend/assets/drop-and-fusion/keycap_5.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_5.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_5.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_5.png diff --git a/packages/frontend/assets/drop-and-fusion/keycap_6.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_6.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_6.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_6.png diff --git a/packages/frontend/assets/drop-and-fusion/keycap_7.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_7.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_7.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_7.png diff --git a/packages/frontend/assets/drop-and-fusion/keycap_8.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_8.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_8.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_8.png diff --git a/packages/frontend/assets/drop-and-fusion/keycap_9.png b/packages/frontend/assets/drop-and-fusion/square_monos/keycap_9.png similarity index 100% rename from packages/frontend/assets/drop-and-fusion/keycap_9.png rename to packages/frontend/assets/drop-and-fusion/square_monos/keycap_9.png diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/candy_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/candy_color.svg new file mode 100644 index 000000000..6eab3ca49 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/candy_color.svg @@ -0,0 +1,86 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/chocolate_bar_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/chocolate_bar_color.svg new file mode 100644 index 000000000..eea5fec18 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/chocolate_bar_color.svg @@ -0,0 +1,316 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/cookie_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/cookie_color.svg new file mode 100644 index 000000000..42b628cca --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/cookie_color.svg @@ -0,0 +1,116 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/custard_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/custard_color.svg new file mode 100644 index 000000000..d967fec7f --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/custard_color.svg @@ -0,0 +1,22 @@ + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/doughnut_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/doughnut_color.svg new file mode 100644 index 000000000..e8e225bc0 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/doughnut_color.svg @@ -0,0 +1,272 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/lollipop_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/lollipop_color.svg new file mode 100644 index 000000000..ad90ac6f5 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/lollipop_color.svg @@ -0,0 +1,112 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/pancakes_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/pancakes_color.svg new file mode 100644 index 000000000..94b2ab69b --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/pancakes_color.svg @@ -0,0 +1,92 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/shaved_ice_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/shaved_ice_color.svg new file mode 100644 index 000000000..64dfef8e0 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/shaved_ice_color.svg @@ -0,0 +1,161 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/shortcake_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/shortcake_color.svg new file mode 100644 index 000000000..66e8f91f1 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/shortcake_color.svg @@ -0,0 +1,48 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/soft_ice_cream_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/soft_ice_cream_color.svg new file mode 100644 index 000000000..37be9c0cb --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/soft_ice_cream_color.svg @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/candy_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/candy_color.svg new file mode 100644 index 000000000..e673f430f --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/candy_color.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/chocolate_bar_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/chocolate_bar_color.svg new file mode 100644 index 000000000..5cd39cc9e --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/chocolate_bar_color.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/custard_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/custard_color.svg new file mode 100644 index 000000000..dd4870dd7 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/custard_color.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/doughnut_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/doughnut_color.svg new file mode 100644 index 000000000..a8d5557f5 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/doughnut_color.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/lollipop_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/lollipop_color.svg new file mode 100644 index 000000000..d3778737a --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/lollipop_color.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/pancakes_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/pancakes_color.svg new file mode 100644 index 000000000..b1a1a322e --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/pancakes_color.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shaved_ice_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shaved_ice_color.svg new file mode 100644 index 000000000..00872c7a0 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shaved_ice_color.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shortcake_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shortcake_color.svg new file mode 100644 index 000000000..e6ed1fbbf --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/shortcake_color.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/soft_ice_cream_color.svg b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/soft_ice_cream_color.svg new file mode 100644 index 000000000..b77e0c365 --- /dev/null +++ b/packages/frontend/assets/drop-and-fusion/sweets_monos/verts/soft_ice_cream_color.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/10000yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/10000yen.png new file mode 100644 index 000000000..bda777719 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/10000yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/1000yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/1000yen.png new file mode 100644 index 000000000..4c462fb1f Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/1000yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/100yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/100yen.png new file mode 100644 index 000000000..8911543af Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/100yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/10yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/10yen.png new file mode 100644 index 000000000..041f77389 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/10yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/1yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/1yen.png new file mode 100644 index 000000000..cc6dcfd74 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/1yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/2000yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/2000yen.png new file mode 100644 index 000000000..6048b7c99 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/2000yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/5000yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/5000yen.png new file mode 100644 index 000000000..b0fe26db1 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/5000yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/500yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/500yen.png new file mode 100644 index 000000000..9e3d2b766 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/500yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/50yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/50yen.png new file mode 100644 index 000000000..c8ef08997 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/50yen.png differ diff --git a/packages/frontend/assets/drop-and-fusion/yen_monos/5yen.png b/packages/frontend/assets/drop-and-fusion/yen_monos/5yen.png new file mode 100644 index 000000000..b120bdca3 Binary files /dev/null and b/packages/frontend/assets/drop-and-fusion/yen_monos/5yen.png differ diff --git a/packages/frontend/assets/reversi/logo.png b/packages/frontend/assets/reversi/logo.png new file mode 100644 index 000000000..4b0d58dec Binary files /dev/null and b/packages/frontend/assets/reversi/logo.png differ diff --git a/packages/frontend/assets/reversi/lose.mp3 b/packages/frontend/assets/reversi/lose.mp3 new file mode 100644 index 000000000..b62d50baf Binary files /dev/null and b/packages/frontend/assets/reversi/lose.mp3 differ diff --git a/packages/frontend/assets/reversi/matched.mp3 b/packages/frontend/assets/reversi/matched.mp3 new file mode 100644 index 000000000..f26d07614 Binary files /dev/null and b/packages/frontend/assets/reversi/matched.mp3 differ diff --git a/packages/frontend/assets/reversi/put.mp3 b/packages/frontend/assets/reversi/put.mp3 new file mode 100644 index 000000000..baa1b8319 Binary files /dev/null and b/packages/frontend/assets/reversi/put.mp3 differ diff --git a/packages/frontend/assets/reversi/stone_b.png b/packages/frontend/assets/reversi/stone_b.png new file mode 100644 index 000000000..9e98455a3 Binary files /dev/null and b/packages/frontend/assets/reversi/stone_b.png differ diff --git a/packages/frontend/assets/reversi/stone_w.png b/packages/frontend/assets/reversi/stone_w.png new file mode 100644 index 000000000..f2bee593d Binary files /dev/null and b/packages/frontend/assets/reversi/stone_w.png differ diff --git a/packages/frontend/assets/reversi/win.mp3 b/packages/frontend/assets/reversi/win.mp3 new file mode 100644 index 000000000..25402ce2a Binary files /dev/null and b/packages/frontend/assets/reversi/win.mp3 differ diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 5b4c2544d..1d25ff1e7 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -40,10 +40,12 @@ "chartjs-chart-matrix": "2.0.1", "chartjs-plugin-gradient": "0.6.1", "chartjs-plugin-zoom": "2.0.1", - "chromatic": "10.1.0", + "chromatic": "10.3.1", "compare-versions": "6.1.0", + "crc-32": "^1.2.2", "cropperjs": "2.0.0-beta.4", "date-fns": "2.30.0", + "defu": "^6.1.4", "escape-regexp": "0.0.1", "estree-walker": "3.0.3", "eventemitter3": "5.0.1", @@ -53,17 +55,18 @@ "json5": "2.2.3", "katex": "0.16.9", "matter-js": "0.19.0", + "misskey-bubble-game": "workspace:*", "misskey-js": "workspace:*", + "misskey-reversi": "workspace:*", "photoswipe": "5.4.3", "punycode": "2.3.1", - "rollup": "4.9.1", + "rollup": "4.9.6", "sanitize-html": "2.11.0", - "sass": "1.69.5", - "seedrandom": "^3.0.5", + "sass": "1.70.0", "shiki": "0.14.7", "strict-event-emitter-types": "2.0.0", "textarea-caret": "3.1.0", - "three": "0.159.0", + "three": "0.160.0", "throttle-debounce": "5.0.0", "tinycolor2": "1.6.0", "tsc-alias": "1.8.8", @@ -71,70 +74,70 @@ "typescript": "5.3.3", "uuid": "9.0.1", "v-code-diff": "1.7.2", - "vite": "5.0.10", - "vue": "3.4.3", + "vite": "5.0.12", + "vue": "3.4.15", "vuedraggable": "next" }, "devDependencies": { "@misskey-dev/eslint-plugin": "^1.0.0", "@misskey-dev/summaly": "^5.0.3", - "@storybook/addon-actions": "7.6.5", - "@storybook/addon-essentials": "7.6.5", - "@storybook/addon-interactions": "7.6.5", - "@storybook/addon-links": "7.6.5", - "@storybook/addon-storysource": "7.6.5", - "@storybook/addons": "7.6.5", - "@storybook/blocks": "7.6.5", - "@storybook/core-events": "7.6.5", + "@storybook/addon-actions": "7.6.10", + "@storybook/addon-essentials": "7.6.10", + "@storybook/addon-interactions": "7.6.10", + "@storybook/addon-links": "7.6.10", + "@storybook/addon-storysource": "7.6.10", + "@storybook/addons": "7.6.10", + "@storybook/blocks": "7.6.10", + "@storybook/core-events": "7.6.10", "@storybook/jest": "0.2.3", - "@storybook/manager-api": "7.6.5", - "@storybook/preview-api": "7.6.5", - "@storybook/react": "7.6.5", - "@storybook/react-vite": "7.6.5", + "@storybook/manager-api": "7.6.10", + "@storybook/preview-api": "7.6.10", + "@storybook/react": "7.6.10", + "@storybook/react-vite": "7.6.10", "@storybook/testing-library": "0.2.2", - "@storybook/theming": "7.6.5", - "@storybook/types": "7.6.5", - "@storybook/vue3": "7.6.5", - "@storybook/vue3-vite": "7.6.5", + "@storybook/theming": "7.6.10", + "@storybook/types": "7.6.10", + "@storybook/vue3": "7.6.10", + "@storybook/vue3-vite": "7.6.10", "@testing-library/vue": "8.0.1", "@types/escape-regexp": "0.0.3", "@types/estree": "1.0.5", - "@types/matter-js": "0.19.5", + "@types/matter-js": "0.19.6", "@types/micromatch": "4.0.6", - "@types/node": "20.10.5", + "@types/node": "20.11.5", "@types/punycode": "2.1.3", "@types/sanitize-html": "2.9.5", "@types/throttle-debounce": "5.0.2", "@types/tinycolor2": "1.4.6", "@types/uuid": "9.0.7", "@types/ws": "8.5.10", - "@typescript-eslint/eslint-plugin": "6.14.0", - "@typescript-eslint/parser": "6.14.0", + "@typescript-eslint/eslint-plugin": "6.18.1", + "@typescript-eslint/parser": "6.18.1", "@vitest/coverage-v8": "0.34.6", - "@vue/runtime-core": "3.4.3", - "acorn": "8.11.2", + "@vue/runtime-core": "3.4.15", + "acorn": "8.11.3", "cross-env": "7.0.3", - "cypress": "13.6.1", + "cypress": "13.6.3", "eslint": "8.56.0", "eslint-plugin-import": "2.29.1", - "eslint-plugin-vue": "9.19.2", + "eslint-plugin-vue": "9.20.1", "fast-glob": "3.3.2", "happy-dom": "10.0.3", "intersection-observer": "0.12.2", "micromatch": "4.0.5", - "msw": "1.3.2", + "msw": "2.1.2", "msw-storybook-addon": "1.10.0", - "nodemon": "3.0.2", - "prettier": "3.1.1", + "nodemon": "3.0.3", + "prettier": "3.2.4", "react": "18.2.0", "react-dom": "18.2.0", "start-server-and-test": "2.0.3", - "storybook": "7.6.5", + "storybook": "7.6.10", "storybook-addon-misskey-theme": "github:misskey-dev/storybook-addon-misskey-theme", "vite-plugin-turbosnap": "1.0.3", "vitest": "0.34.6", "vitest-fetch-mock": "0.2.2", - "vue-eslint-parser": "9.3.2", + "vue-eslint-parser": "9.4.0", "vue-tsc": "1.8.27" } } diff --git a/packages/frontend/src/boot/main-boot.ts b/packages/frontend/src/boot/main-boot.ts index f248bc10e..8f74e927a 100644 --- a/packages/frontend/src/boot/main-boot.ts +++ b/packages/frontend/src/boot/main-boot.ts @@ -75,9 +75,18 @@ export async function mainBoot() { if (defaultStore.state.enableSeasonalScreenEffect) { const month = new Date().getMonth() + 1; - if (month === 12 || month === 1) { - const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; - new SnowfallEffect().render(); + if (defaultStore.state.hemisphere === 'S') { + // ▼南半球 + if (month === 7 || month === 8) { + const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + new SnowfallEffect().render(); + } + } else { + // ▼北半球 + if (month === 12 || month === 1) { + const SnowfallEffect = (await import('@/scripts/snowfall-effect.js')).SnowfallEffect; + new SnowfallEffect().render(); + } } } @@ -203,7 +212,7 @@ export async function mainBoot() { const lastUsedDate = parseInt(lastUsed, 10); // 二時間以上前なら if (Date.now() - lastUsedDate > 1000 * 60 * 60 * 2) { - toast(i18n.t('welcomeBackWithName', { + toast(i18n.tsx.welcomeBackWithName({ name: $i.name || $i.username, })); } diff --git a/packages/frontend/src/components/MkAnnouncementDialog.vue b/packages/frontend/src/components/MkAnnouncementDialog.vue index eca5daf1c..8f550cf25 100644 --- a/packages/frontend/src/components/MkAnnouncementDialog.vue +++ b/packages/frontend/src/components/MkAnnouncementDialog.vue @@ -44,7 +44,7 @@ async function ok() { const confirm = await os.confirm({ type: 'question', title: i18n.ts._announcement.readConfirmTitle, - text: i18n.t('_announcement.readConfirmText', { title: props.announcement.title }), + text: i18n.tsx._announcement.readConfirmText({ title: props.announcement.title }), }); if (confirm.canceled) return; } diff --git a/packages/frontend/src/components/MkAutocomplete.vue b/packages/frontend/src/components/MkAutocomplete.vue index 181594c9f..976ba190f 100644 --- a/packages/frontend/src/components/MkAutocomplete.vue +++ b/packages/frontend/src/components/MkAutocomplete.vue @@ -35,6 +35,11 @@ SPDX-License-Identifier: AGPL-3.0-only {{ tag }} +
    +
  1. + {{ param }} +
  2. +
@@ -51,7 +56,7 @@ import { emojilist, getEmojiName } from '@/scripts/emojilist.js'; import { i18n } from '@/i18n.js'; import { miLocalStorage } from '@/local-storage.js'; import { customEmojis } from '@/custom-emojis.js'; -import { MFM_TAGS } from '@/const.js'; +import { MFM_TAGS, MFM_PARAMS } from '@/const.js'; type EmojiDef = { emoji: string; @@ -130,7 +135,7 @@ export default { diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue new file mode 100644 index 000000000..1952369b6 --- /dev/null +++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue @@ -0,0 +1,104 @@ + + + + + + + diff --git a/packages/frontend/src/components/MkCwButton.vue b/packages/frontend/src/components/MkCwButton.vue index 4a6d2dfba..ca19a2122 100644 --- a/packages/frontend/src/components/MkCwButton.vue +++ b/packages/frontend/src/components/MkCwButton.vue @@ -41,9 +41,9 @@ const emit = defineEmits<{ const label = computed(() => { return concat([ - props.text ? [i18n.t('_cw.chars', { count: props.text.length })] : [], + props.text ? [i18n.tsx._cw.chars({ count: props.text.length })] : [], props.renote ? [i18n.ts.quote] : [], - props.files.length !== 0 ? [i18n.t('_cw.files', { count: props.files.length })] : [], + props.files.length !== 0 ? [i18n.tsx._cw.files({ count: props.files.length })] : [], props.poll != null ? [i18n.ts.poll] : [], ] as string[][]).join(' / '); }); diff --git a/packages/frontend/src/components/MkDateSeparatedList.vue b/packages/frontend/src/components/MkDateSeparatedList.vue index b45aef45f..f6bd85bd1 100644 --- a/packages/frontend/src/components/MkDateSeparatedList.vue +++ b/packages/frontend/src/components/MkDateSeparatedList.vue @@ -46,7 +46,7 @@ export default defineComponent({ function getDateText(time: string) { const date = new Date(time).getDate(); const month = new Date(time).getMonth() + 1; - return i18n.t('monthAndDay', { + return i18n.tsx.monthAndDay({ month: month.toString(), day: date.toString(), }); diff --git a/packages/frontend/src/components/MkDialog.vue b/packages/frontend/src/components/MkDialog.vue index 2c0f6a4d7..83f5041a4 100644 --- a/packages/frontend/src/components/MkDialog.vue +++ b/packages/frontend/src/components/MkDialog.vue @@ -30,8 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only diff --git a/packages/frontend/src/components/MkDrive.vue b/packages/frontend/src/components/MkDrive.vue index 2fa5c1a0f..e68b69c14 100644 --- a/packages/frontend/src/components/MkDrive.vue +++ b/packages/frontend/src/components/MkDrive.vue @@ -82,8 +82,8 @@ SPDX-License-Identifier: AGPL-3.0-only {{ i18n.ts.loadMore }}
-
{{ i18n.t('empty-draghover') }}
-
{{ i18n.ts.emptyDrive }}
{{ i18n.t('empty-drive-description') }}
+
{{ i18n.ts['empty-draghover'] }}
+
{{ i18n.ts.emptyDrive }}
{{ i18n.ts['empty-drive-description'] }}
{{ i18n.ts.emptyFolder }}
diff --git a/packages/frontend/src/components/MkFlashPreview.vue b/packages/frontend/src/components/MkFlashPreview.vue index ab435585d..1be8a6d8f 100644 --- a/packages/frontend/src/components/MkFlashPreview.vue +++ b/packages/frontend/src/components/MkFlashPreview.vue @@ -9,7 +9,9 @@ SPDX-License-Identifier: AGPL-3.0-only

{{ flash.title }}

-

{{ flash.summary.length > 85 ? flash.summary.slice(0, 85) + '…' : flash.summary }}

+

+ +