diff --git a/CHANGELOG.md b/CHANGELOG.md index b33668ea972b293622bad3df98adff62ddaea448..66b2d50dc08c1fcdab6a4c3907b9cf1f1964bd3b 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,18 +1,20 @@ <!-- ## 13.x.x (unreleased) -### Improvements +### General - -### Bugfixes +### Client +- + +### Server - -You should also include the user name that made the change. --> -## 13.x.x (unreleased) +## 13.10.0 -### Improvements +### General - ユーザーã”ã¨ã«Renoteをミュートã§ãるよã†ã« - ノートã”ã¨ã«çµµæ–‡å—リアクションをå—ã‘å–ã‚‹ã‹è¨å®šã§ãるよã†ã« - クリップをãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã§ãるよã†ã« @@ -20,33 +22,43 @@ You should also include the user name that made the change. - ãƒãƒ¼ãƒ«ã®ä¸¦ã³é †ã‚’è¨å®šå¯èƒ½ã« - カスタム絵文å—ã«ãƒ©ã‚¤ã‚»ãƒ³ã‚¹æƒ…å ±ã‚’ä»˜ä¸Žã§ãるよã†ã« - 指定ã—ãŸæ–‡å—列をå«ã‚€æŠ•ç¨¿ã®å…¬é–‹ç¯„囲をホームã«ã§ãるよã†ã« -- enhance(client): è¨å®šã‹ã‚‰è‡ªåˆ†ã®ãƒãƒ¼ãƒ«ã‚’確èªã§ãるよã†ã« -- enhance(client): DM作æˆæ™‚ã«ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã‚‚å«ã‚€ã‚ˆã†ã« -- enhance(client): フォãƒãƒ¼ç”³è«‹ã®ãƒœã‚¿ãƒ³ã®ãƒ‡ã‚¶ã‚¤ãƒ³ã‚’改善 -- enhance(backend): OpenAPIエンドãƒã‚¤ãƒ³ãƒˆã‚’復旧 -- WebP/AVIF/JPEGã®web公開用画åƒã¯ã€ã‚µãƒ¼ãƒãƒ¼ã‚µã‚¤ãƒ‰ã§ã¯JPEGã§ã¯ãªãWebPã«å¤‰æ›ã™ã‚‹ã‚ˆã†ã« -- アニメーション画åƒã®ã‚µãƒ ãƒã‚¤ãƒ«ã‚’生æˆã™ã‚‹ã‚ˆã†ã« -- アクティブユーザー数ãƒãƒ£ãƒ¼ãƒˆã®è¨˜éŒ²ä¸Šé™å€¤ã‚’æ‹¡å¼µ -- Playã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ä¸Šé™æ–‡å—æ•°ã‚’2å€ã«æ‹¡å¼µ + +### Client +- è¨å®šã‹ã‚‰è‡ªåˆ†ã®ãƒãƒ¼ãƒ«ã‚’確èªã§ãるよã†ã« +- åºƒå‘Šä¸€è¦§ãƒšãƒ¼ã‚¸ã‚’è¿½åŠ +- DM作æˆæ™‚ã«ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã‚‚å«ã‚€ã‚ˆã†ã« +- フォãƒãƒ¼ç”³è«‹ã®ãƒœã‚¿ãƒ³ã®ãƒ‡ã‚¶ã‚¤ãƒ³ã‚’改善 - 付箋ウィジェットã®é«˜ã•ã‚’è¨å®šå¯èƒ½ã« -- é…é€å…ˆã‚µãƒ¼ãƒãƒ¼ãŒ410 Goneã§å¿œç”ã—ã¦ããŸå ´åˆã¯è‡ªå‹•ã§é…é€åœæ¢ã‚’ã™ã‚‹ã‚ˆã†ã« -- avatarBlurHash/bannerBlurHashã®åž‹ã‚’stringã«é™å®š - APオブジェクトを入力ã—ã¦ãƒ•ã‚§ãƒƒãƒã™ã‚‹æ©Ÿèƒ½ã¨ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚„ノートã®æ¤œç´¢æ©Ÿèƒ½ã‚’分離 - ナビゲーションãƒãƒ¼ã®é …ç›®ã«ã€Œãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ã€ã‚’è¿½åŠ ã§ãるよã†ã« -- AiScriptã‚’0.13.0ã«æ›´æ–° - -### Bugfixes +- AiScriptã‚’0.13.1ã«æ›´æ–° +- oEmbedをサãƒãƒ¼ãƒˆã—ã¦ã„るウェブサイトã®ãƒ—レビューãŒã§ãるよã†ã« + - YouTubeã‚’oEmbedã§ãƒãƒ¼ãƒ‰ã—ã€ãƒ—レビューã§å…±æœ‰ãƒœã‚¿ãƒ³ã‚’押ã™ã¨OSã®å…±æœ‰ç”»é¢ãŒã§ã‚‹ã‚ˆã†ã« + - ([Firefoxã§Spotifyã®ãƒ—レビューを開ã‘ã‚‹ã¨ãƒ•ãƒ«ã‚µã‚¤ã‚ºã˜ã‚ƒãªãプレビューサイズã ã‘å†ç”Ÿã§ãã‚‹å•é¡Œ](https://bugzilla.mozilla.org/show_bug.cgi?id=1792395)ãŒã‚ã‚Šã¾ã™) + - (ã™ã§ã«ãƒ–ラウザーã§ã‚ャッシュã•ã‚ŒãŸãƒªãƒ³ã‚¯ã«å¯¾ã—ã¦ã¯ä»¥å‰ã®ãƒ—レビュー行動ãŒè¡Œã‚ã‚Œã¦ã¾ã™ã€‚ãã®å ´åˆã€ãƒ–ラウザーã®ã‚ャッシュをクリアã—ã¦ã¾ãŸè©¦ã—ã¦ãã ã•ã„。) - プãƒãƒ•ã‚£ãƒ¼ãƒ«ã§è¨å®šã—ãŸæƒ…å ±ãŒå‰Šé™¤ã§ããªã„å•é¡Œã‚’ä¿®æ£ - ãƒãƒ¼ãƒ«ã§åºƒå‘Šã‚’無効ã«ã™ã‚‹ã¨admin/adsã§ãƒ—レビューãŒã§ã¦ã“ãªã„å•é¡Œã‚’ä¿®æ£ - /api-consoleページã«ã‚¢ã‚¯ã‚»ã‚¹ã™ã‚‹ã¨404ãŒå‡ºã‚‹å•é¡Œã‚’ä¿®æ£ +- Safariã§ãƒ—ラグインãŒè¤‡æ•°ã‚ã‚‹å ´åˆã«æ£å¸¸ã«èªã¿è¾¼ã¾ã‚Œãªã„å•é¡Œã‚’ä¿®æ£ +- Bookwyrmã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ãƒšãƒ¼ã‚¸ã§ã€Œãƒªãƒ¢ãƒ¼ãƒˆã§è¡¨ç¤ºã€ã‚’タップã—ã¦ã‚‚åå¿œãŒãªã„å•é¡Œã‚’ä¿®æ£ +- éžãƒã‚°ã‚¤ãƒ³æ™‚ã®ã€ŒMisskeyã«ã¤ã„ã¦ã€ã®è¡¨ç¤ºã‚’ä¿®æ£ + +### Server +- OpenAPIエンドãƒã‚¤ãƒ³ãƒˆã‚’復旧 +- WebP/AVIF/JPEGã®web公開用画åƒã¯ã€ã‚µãƒ¼ãƒãƒ¼ã‚µã‚¤ãƒ‰ã§ã¯JPEGã§ã¯ãªãWebPã«å¤‰æ›ã™ã‚‹ã‚ˆã†ã« +- アニメーション画åƒã®ã‚µãƒ ãƒã‚¤ãƒ«ã‚’生æˆã™ã‚‹ã‚ˆã†ã« +- アクティブユーザー数ãƒãƒ£ãƒ¼ãƒˆã®è¨˜éŒ²ä¸Šé™å€¤ã‚’æ‹¡å¼µ +- Playã®ã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ä¸Šé™æ–‡å—æ•°ã‚’2å€ã«æ‹¡å¼µ +- é…é€å…ˆã‚µãƒ¼ãƒãƒ¼ãŒ410 Goneã§å¿œç”ã—ã¦ããŸå ´åˆã¯è‡ªå‹•ã§é…é€åœæ¢ã‚’ã™ã‚‹ã‚ˆã†ã« +- avatarBlurHash/bannerBlurHashã®åž‹ã‚’stringã«é™å®š +- タイムラインå–得時ã®ãƒ‘フォーマンスを改善 - SMTP Login id length is too short - API上ã§`visibility`ã‚’`followers`ã«è¨å®šã—ã¦renoteã™ã‚‹ã¨é€£åˆã‚„削除ã§ä¸å…·åˆãŒç™ºç”Ÿã™ã‚‹å•é¡Œã‚’ä¿®æ£ - AWS S3ã‹ã‚‰ã®ãƒ•ã‚¡ã‚¤ãƒ«å‰Šé™¤ã§NoSuchKeyエラーãŒå‡ºã‚‹ã¨é€²ã‚らãªã„状態ã«ãªã‚‹å•é¡Œã‚’ä¿®æ£ -- fix(frontend): Safariã§ãƒ—ラグインãŒè¤‡æ•°ã‚ã‚‹å ´åˆã«æ£å¸¸ã«èªã¿è¾¼ã¾ã‚Œãªã„å•é¡Œã‚’ä¿®æ£ -- Bookwyrmã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒ—ãƒãƒ•ã‚£ãƒ¼ãƒ«ãƒšãƒ¼ã‚¸ã§ã€Œãƒªãƒ¢ãƒ¼ãƒˆã§è¡¨ç¤ºã€ã‚’タップã—ã¦ã‚‚åå¿œãŒãªã„å•é¡Œã‚’ä¿®æ£ - `disableCache: true`ã‚’è¨å®šã—ã¦ã„ã‚‹å ´åˆã«çµµæ–‡å—管ç†æ“作ã§ã‚¨ãƒ©ãƒ¼ãŒå‡ºã‚‹å•é¡Œã‚’ä¿®æ£ - リテンション分æžãŒä¸Šæ‰‹ã機能ã—ãªã„ã“ã¨ãŒã‚ã‚‹ã®ã‚’ä¿®æ£ - 空ã®ã‚¢ãƒ³ãƒ†ãƒŠãŒä½œæˆã§ããªã„よã†ã«ä¿®æ£ +- 特定ã®æ¡ä»¶ã§é€šå ±ãŒè¦‹ã‚Œãªã„å•é¡Œã‚’ä¿®æ£ ## 13.9.2 (2023/03/06) diff --git a/locales/de-DE.yml b/locales/de-DE.yml index eae9389acbdfadbdbfacc54eb3c3293eb7e69461..08808ea6a4fb60c4befae07df833b780e4a533f6 100644 --- a/locales/de-DE.yml +++ b/locales/de-DE.yml @@ -122,6 +122,8 @@ unmarkAsSensitive: "Als nicht NSFW markieren" enterFileName: "Dateinamen eingeben" mute: "Stummschalten" unmute: "Stummschaltung aufheben" +renoteMute: "Renotes stummschalten" +renoteUnmute: "Renote-Stummschaltung aufheben" block: "Blockieren" unblock: "Blockierung aufheben" suspend: "Sperren" @@ -153,6 +155,7 @@ flagShowTimelineReplies: "Antworten in der Chronik anzeigen" flagShowTimelineRepliesDescription: "Ist diese Option aktiviert, so werden Antworten von Benutzern auf die Notizen anderer Benutzer in der Chronik angezeigt." autoAcceptFollowed: "Follow-Anfragen von Benutzern, denen du folgst, automatisch akzeptieren" addAccount: "Benutzerkonto hinzufügen" +reloadAccountsList: "Benutzerkontoliste aktualisieren" loginFailed: "Anmeldung fehlgeschlagen" showOnRemote: "Auf Ursprungsinstanz ansehen" general: "Allgemein" @@ -544,6 +547,10 @@ userSuspended: "Dieser Benutzer wurde gesperrt." userSilenced: "Dieser Benutzer wurde instanzweit stummgeschaltet." yourAccountSuspendedTitle: "Dieses Benutzerkonto ist gesperrt" yourAccountSuspendedDescription: "Dieses Benutzerkonto wurde gesperrt, da es gegen die Nutzungsbedingungen dieses Servers verstoßen hat. Trete mit dem Betreiber in Kontakt, falls du weitere Details erfahren möchtest. Bitte erstelle kein neues Benutzerkonto." +tokenRevoked: "Ungültiger Token" +tokenRevokedDescription: "Der Token ist abgelaufen. Bitte melde dich erneut an." +accountDeleted: "Benutzerkonto wurde gelöscht" +accountDeletedDescription: "Dieses Konto wurde gelöscht." menu: "Menü" divider: "Trenner" addItem: "Element hinzufügen" @@ -959,6 +966,17 @@ invitationRequiredToRegister: "Diese Instanz ist einladungsbasiert. Du musst ein emailNotSupported: "Diese Instanz unterstützt das Versenden von Emails nicht" postToTheChannel: "In Kanal senden" cannotBeChangedLater: "Kann später nicht mehr geändert werden." +reactionAcceptance: "Reaktionsannahme" +likeOnly: "Nur \"Gefällt mir\"" +likeOnlyForRemote: "Nur \"Gefällt mir\" für fremde Instanzen" +rolesAssignedToMe: "Mir zugewiesene Rollen" +resetPasswordConfirm: "Wirklich Passwort zurücksetzen?" +sensitiveWords: "Sensible Wörter" +sensitiveWordsDescription: "Die Notizsichtbarkeit aller Notizen, die diese Wörter enthalten, wird automatisch auf \"Startseite\" gesetzt. Durch Zeilenumbrüche können mehrere konfiguriert werden." +notesSearchNotAvailable: "Die Notizsuche ist nicht verfügbar." +license: "Lizenz" +unfavoriteConfirm: "Wirklich aus Favoriten entfernen?" +myClips: "Meine Clips" _achievements: earnedAt: "Freigeschaltet am" _types: @@ -1106,7 +1124,7 @@ _achievements: title: "Beliebt" description: "Die Anzahl deiner Follower hat 100 überschritten" _followers300: - title: "Stellt euch bitte in einer Reihe auf" + title: "Eine geordnete Reihe, bitte!" description: "Die Anzahl deiner Follower hat 300 überschritten" _followers500: title: "Funkmast" @@ -1218,6 +1236,8 @@ _role: iconUrl: "Icon-URL" asBadge: "Als Abzeichen anzeigen" descriptionOfAsBadge: "Ist dies aktiviert, so wird das Icon dieser Rolle an der Seite der Namen von Benutzern mit dieser Rolle angezeigt." + displayOrder: "Position" + descriptionOfDisplayOrder: "Je höher die Nummer, desto höher die UI-Position." canEditMembersByModerator: "Moderatoren können Benutzern diese Rolle zuweisen" descriptionOfCanEditMembersByModerator: "Wenn aktiviert, so können Moderatoren und Adminstratoren anderen Benutzern diese Rolle zuweisen bzw. diese Zuweisung aufheben. Wenn deaktiviert, so ist es nur Administratoren möglich, Zuweisungen dieser Rolle zu verwalten." priority: "Priorität" @@ -1243,6 +1263,7 @@ _role: rateLimitFactor: "Versuchsanzahl" descriptionOfRateLimitFactor: "Je niedriger desto weniger restriktiv, je höher destro restriktiver." canHideAds: "Kann Werbung ausblenden" + canSearchNotes: "Nutzung der Notizsuchfunktion" _condition: isLocal: "Lokaler Benutzer" isRemote: "Benutzer fremder Instanz" @@ -1844,3 +1865,6 @@ _deck: _dialog: charactersExceeded: "Maximallänge überschritten! Momentan {current} von {max}" charactersBelow: "Minimallänge unterschritten! Momentan {current} von {min}" +_disabledTimeline: + title: "Chronik deaktiviert" + description: "Mit deinen jetzigen Rollen ist diese Chronik nicht verfügbar." diff --git a/locales/en-US.yml b/locales/en-US.yml index 53a30c741e3ff2c7d840209ba5e26ba6198d4a97..9e018ce2acdb92972b9a625db8c19a644485b7c1 100644 --- a/locales/en-US.yml +++ b/locales/en-US.yml @@ -67,7 +67,7 @@ import: "Import" export: "Export" files: "Files" download: "Download" -driveFileDeleteConfirm: "Are you sure you want to delete the file \"{name}\"? Notes with this file attached will also be deleted." +driveFileDeleteConfirm: "Are you sure you want to delete \"{name}\"? All notes with this file attached will also be deleted." unfollowConfirm: "Are you sure you want to unfollow {name}?" exportRequested: "You've requested an export. This may take a while. It will be added to your Drive once completed." importRequested: "You've requested an import. This may take a while." @@ -122,6 +122,8 @@ unmarkAsSensitive: "Unmark as NSFW" enterFileName: "Enter filename" mute: "Mute" unmute: "Unmute" +renoteMute: "Mute Renotes" +renoteUnmute: "Unmute Renotes" block: "Block" unblock: "Unblock" suspend: "Suspend" @@ -153,6 +155,7 @@ flagShowTimelineReplies: "Show replies in timeline" flagShowTimelineRepliesDescription: "Shows replies of users to notes of other users in the timeline if turned on." autoAcceptFollowed: "Automatically approve follow requests from users you're following" addAccount: "Add account" +reloadAccountsList: "Reload account list" loginFailed: "Failed to sign in" showOnRemote: "View on remote instance" general: "General" @@ -544,6 +547,10 @@ userSuspended: "This user has been suspended." userSilenced: "This user is being silenced." yourAccountSuspendedTitle: "This account is suspended" yourAccountSuspendedDescription: "This account has been suspended due to breaking the server's terms of services or similar. Contact the administrator if you would like to know a more detailed reason. Please do not create a new account." +tokenRevoked: "Invalid token" +tokenRevokedDescription: "This token has expired. Please log in again." +accountDeleted: "Account deleted" +accountDeletedDescription: "This account has been deleted." menu: "Menu" divider: "Divider" addItem: "Add Item" @@ -959,6 +966,17 @@ invitationRequiredToRegister: "This instance is invite-only. You must enter a va emailNotSupported: "This instance does not support sending emails" postToTheChannel: "Post to channel" cannotBeChangedLater: "This cannot be changed later." +reactionAcceptance: "Reaction Acceptance" +likeOnly: "Only likes" +likeOnlyForRemote: "Only likes for remote instances" +rolesAssignedToMe: "Roles assigned to me" +resetPasswordConfirm: "Really reset your password?" +sensitiveWords: "Sensitive words" +sensitiveWordsDescription: "The visibility of all notes containing any of the configured words will be set to \"Home\" automatically. You can list multiple by separating them via line breaks." +notesSearchNotAvailable: "Note search is unavailable." +license: "License" +unfavoriteConfirm: "Really remove from favorites?" +myClips: "My clips" _achievements: earnedAt: "Unlocked at" _types: @@ -1218,6 +1236,8 @@ _role: iconUrl: "Icon URL" asBadge: "Show as badge" descriptionOfAsBadge: "This role's icon will be displayed next to the username of users with this role if turned on." + displayOrder: "Position" + descriptionOfDisplayOrder: "The higher the number, the higher its UI position." canEditMembersByModerator: "Allow moderators to edit the list of members for this role" descriptionOfCanEditMembersByModerator: "When turned on, moderators as well as administrators will be able to assign and unassign users to this role. When turned off, only administrators will be able to assign users." priority: "Priority" @@ -1243,6 +1263,7 @@ _role: rateLimitFactor: "Rate limit" descriptionOfRateLimitFactor: "Lower rate limits are less restrictive, higher ones more restrictive. " canHideAds: "Can hide ads" + canSearchNotes: "Usage of note search" _condition: isLocal: "Local user" isRemote: "Remote user" @@ -1844,3 +1865,6 @@ _deck: _dialog: charactersExceeded: "You've exceeded the maximum character limit! Currently at {current} of {max}." charactersBelow: "You're below the minimum character limit! Currently at {current} of {min}." +_disabledTimeline: + title: "Timeline disabled" + description: "You cannot use this timeline under your current roles." diff --git a/locales/es-ES.yml b/locales/es-ES.yml index 3ad2c21ff7cd66e819e57a6b638c3dc07e1e566d..3e73e4c5ea9e379c2aa7e650ef92d006144616ea 100644 --- a/locales/es-ES.yml +++ b/locales/es-ES.yml @@ -122,6 +122,8 @@ unmarkAsSensitive: "Desmarcar como sensible" enterFileName: "Ingrese el nombre del archivo" mute: "Silenciar" unmute: "Dejar de silenciar" +renoteMute: "Silenciar renota" +renoteUnmute: "Desilenciar renota" block: "Bloquear" unblock: "Dejar de bloquear" suspend: "Suspender" @@ -153,6 +155,7 @@ flagShowTimelineReplies: "Mostrar respuestas a las notas en la biografÃa" flagShowTimelineRepliesDescription: "Cuando se marca, la lÃnea de tiempo muestra respuestas a otras notas además de las notas del usuario" autoAcceptFollowed: "Aceptar automáticamente las solicitudes de seguimiento de los usuarios que sigues" addAccount: "Agregar Cuenta" +reloadAccountsList: "Recargar lista de cuentas" loginFailed: "Error al iniciar sesión." showOnRemote: "Ver en una instancia remota" general: "General" @@ -506,6 +509,7 @@ objectStorageSetPublicRead: "Seleccionar \"public-read\" al subir " serverLogs: "Registros del servidor" deleteAll: "Eliminar todos" showFixedPostForm: "Mostrar el formulario de las entradas encima de la lÃnea de tiempo" +showFixedPostFormInChannel: "Mostrar el formulario de publicación por encima de la cronologÃa (Canales)" newNoteRecived: "Tienes una nota nueva" sounds: "Sonidos" sound: "Sonidos" @@ -543,6 +547,10 @@ userSuspended: "Este usuario ha sido suspendido." userSilenced: "Este usuario ha sido silenciado." yourAccountSuspendedTitle: "Esta cuenta ha sido suspendida" yourAccountSuspendedDescription: "Esta cuenta ha sido suspendida debido a violaciones de los términos de servicio del servidor y otras razones. Para más información, póngase en contacto con el administrador. Por favor, no cree una nueva cuenta." +tokenRevoked: "Token inválido" +tokenRevokedDescription: "Este token expiró, vuelve a iniciar sesión." +accountDeleted: "Cuenta borrada" +accountDeletedDescription: "Esta cuenta ha sido borrada." menu: "Menú" divider: "Divisor" addItem: "Agregar elemento" @@ -955,6 +963,16 @@ exploreOtherServers: "Buscar otra instancia" letsLookAtTimeline: "Mirar la lÃnea de tiempo local" disableFederationWarn: "Esto desactivará la federación, pero las publicaciones segurán siendo públicas al menos que se configure diferente. Usualmente no necesitas usar esta configuración." invitationRequiredToRegister: "Esta instancia está configurada sólo por invitación, tienes que ingresar un código de invitación válido." +emailNotSupported: "Esta instancia no soporta el envÃo de correo electrónico" +postToTheChannel: "Publicar en el canal" +cannotBeChangedLater: "Esto no podrá ser cambiado después." +reactionAcceptance: "Aceptación de reacciones" +likeOnly: "Sólo 'me gusta'" +likeOnlyForRemote: "Sólo reacciones de instancias remotas" +rolesAssignedToMe: "Roles asignados a mÃ" +resetPasswordConfirm: "¿Realmente quieres cambiar la contraseña?" +sensitiveWords: "Palabras sensibles" +sensitiveWordsDescription: "La visibilidad de todas las notas que contienen cualquiera de las palabras configuradas serán puestas en \"Inicio\" automáticamente. Puedes enumerás varias separándolas con saltos de lÃnea" _achievements: earnedAt: "Desbloqueado el" _types: @@ -1214,6 +1232,8 @@ _role: iconUrl: "URL del Ãcono" asBadge: "Mostrar como emblema" descriptionOfAsBadge: "Este Ãcono de rol se mostrará a lado del nombre de usuario cuando este rol se encuentre activo." + displayOrder: "Posición" + descriptionOfDisplayOrder: "Entre más alto el número, mayor es la posición en la interfaz." canEditMembersByModerator: "Permitir a los moderadores editar los miembros" descriptionOfCanEditMembersByModerator: "Si se activa, los moderadores, al igual que los administradores, serán capaces de asignar/quitar usuarios a éste rol. Si se desactiva, sólo los administradores podrán hacerlo." priority: "Prioridad" @@ -1239,6 +1259,7 @@ _role: rateLimitFactor: "Limitador" descriptionOfRateLimitFactor: "LÃmites más bajos son menos restrictivos, más altos menos restrictivos" canHideAds: "Puede ocultar anuncios" + canSearchNotes: "Uso de la búsqueda de notas" _condition: isLocal: "Usuario local" isRemote: "Usuario remoto" @@ -1840,3 +1861,6 @@ _deck: _dialog: charactersExceeded: "¡Has excedido el lÃmite de caracteres! Actualmente {current} de {max}." charactersBelow: "¡Estás por debajo del lÃmite de caracteres! Actualmente {current} de {min}." +_disabledTimeline: + title: "LÃnea de tiempo deshabilitada" + description: "No puedes usar esta lÃnea de tiempo con tus roles actuales." diff --git a/locales/it-IT.yml b/locales/it-IT.yml index d5638aeb62430171a8a0f35e40944154189751c4..44499fa3dd9cf01e286943430718ad297983169a 100644 --- a/locales/it-IT.yml +++ b/locales/it-IT.yml @@ -122,6 +122,8 @@ unmarkAsSensitive: "Segna come non sensibile" enterFileName: "Nome del file" mute: "Silenzia" unmute: "Riattiva l'audio" +renoteMute: "Silenzia i Rinota" +renoteUnmute: "Non silenziare i Rinota" block: "Blocca" unblock: "Sblocca" suspend: "Sospendi" @@ -153,6 +155,7 @@ flagShowTimelineReplies: "Mostra le risposte alle note sulla timeline." flagShowTimelineRepliesDescription: "Se è attiva, la timeline mostra le risposte alle altre note dell'utente oltre a quelle dell'utente stesso." autoAcceptFollowed: "Accetta automaticamente le richieste di follow da utenti che già segui" addAccount: "Aggiungi profilo" +reloadAccountsList: "Ricarica l'elenco dei profili" loginFailed: "Accesso non riuscito" showOnRemote: "Leggi sull'istanza remota" general: "Generali" @@ -544,6 +547,10 @@ userSuspended: "L'utente è in sospensione" userSilenced: "L'utente è silenziat@." yourAccountSuspendedTitle: "Questo profilo è sospeso" yourAccountSuspendedDescription: "Questo profilo è stato sospeso a causa di una violazione del regolamento. Per informazioni, contattare l'amministrazione. Si prega di non creare un nuovo account." +tokenRevoked: "Il token non è valido" +tokenRevokedDescription: "Il token di accesso è scaduto. Per favore, accedi nuovamente." +accountDeleted: "Profilo eliminato" +accountDeletedDescription: "Questo profilo è stato eliminato." menu: "Menù" divider: "Linea di separazione" addItem: "Aggiungi elemento" @@ -959,6 +966,17 @@ invitationRequiredToRegister: "L'accesso a questo nodo è solo ad invito. Devi i emailNotSupported: "L'istanza non supporta l'invio di email" postToTheChannel: "Pubblica sul canale" cannotBeChangedLater: "Non sarà più modificabile" +reactionAcceptance: "Accettazione reazioni" +likeOnly: "Solo i Like" +likeOnlyForRemote: "Solo Like remoti" +rolesAssignedToMe: "I miei ruoli" +resetPasswordConfirm: "Vuoi reimpostare la password?" +sensitiveWords: "Parole sensibili" +sensitiveWordsDescription: "Imposta automaticamente \"Home\" alla visibilità delle Note che contengono una qualsiasi parola tra queste configurate. Puoi separarle per riga." +notesSearchNotAvailable: "Non è possibile cercare tra le Note." +license: "Licenza" +unfavoriteConfirm: "Vuoi davvero rimuovere la preferenza?" +myClips: "Le mie Clip" _achievements: earnedAt: "Data di conseguimento" _types: @@ -1218,6 +1236,8 @@ _role: iconUrl: "URL dell'icona" asBadge: "Mostra come badge" descriptionOfAsBadge: "Se indicato, accanto al nome utente viene visualizzata l'icona del ruolo." + displayOrder: "Ordine di visualizzazione" + descriptionOfDisplayOrder: "I valori più alti vengono visualizzati per primi" canEditMembersByModerator: "Anche i Moderatori assegnano profili a questo ruolo" descriptionOfCanEditMembersByModerator: "Se disattivo, potranno farlo solamente gli Amministratori." priority: "Priorità " @@ -1243,6 +1263,7 @@ _role: rateLimitFactor: "Limite del rapporto" descriptionOfRateLimitFactor: "I rapporti più bassi sono meno restrittivi, quelli più alti lo sono di più." canHideAds: "Può nascondere i banner" + canSearchNotes: "Ricercare nelle Note" _condition: isLocal: "Profilo locale" isRemote: "Profilo remoto" @@ -1844,3 +1865,6 @@ _deck: _dialog: charactersExceeded: "Hai superato il limite di {max} caratteri! ({corrente})" charactersBelow: "Sei al di sotto del minimo di {min} caratteri! ({corrente})" +_disabledTimeline: + title: "Timeline disabilitata" + description: "Il tuo ruolo non ha i permessi per accedere a questa timeline" diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml index 081457c2ff6d9d440eb7e5ab88d72fff246213c6..bd9ae46d34e66d76b046a98490d0f40dc1280dc7 100644 --- a/locales/ja-KS.yml +++ b/locales/ja-KS.yml @@ -2,7 +2,7 @@ _lang_: "日本語 (関西å¼)" headlineMisskey: "ノートã§ã¤ãªãŒã‚‹ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯" introMisskey: "よã†ãŠè¶Šã—ï¼Misskeyã¯ã€ã‚ªãƒ¼ãƒ—ンソースã®åˆ†æ•£åž‹ãƒžã‚¤ã‚¯ãƒãƒ–ãƒã‚°ã‚µãƒ¼ãƒ“スやãん。\n「ノートã€ã‚’作ã£ã¦ã€ã„ã¾èµ·ã“ã£ã¨ã‚‹ã“ã¨ã‚’共有ã—ãŸã‚Šã€ã‚ã‚“ãŸã«ã¤ã„ã¦çš†ã«ç™ºä¿¡ã—よã†ðŸ“¡\n「リアクションã€æ©Ÿèƒ½ã§ã€çš†ã®ãƒŽãƒ¼ãƒˆã«ç´ æ—©ãåå¿œã‚’è¿½åŠ ã—ãŸã‚Šã‚‚ã§ãã‚‹ã§âœŒ\nã»ãªæ–°ã—ã„世界を探検ã—よã‹ðŸš€" -poweredByMisskeyDescription: "{name}ã¯ã€ã‚ªãƒ¼ãƒ—ンソースã®ãƒ—ラットフォーム<b>Misskey</b>を使ã£ãŸã‚µãƒ¼ãƒ“ス(Misskeyインスタンスã¨å‘¼ã°ã‚Œã‚‹ã‚„ã¤ã‚„)ã®ã²ã¨ã¤ã‚„ã§ã€‚" +poweredByMisskeyDescription: "{name}ã¯ã€ã‚ªãƒ¼ãƒ—ンソースã®ãƒ—ラットフォーム<b>Misskey</b>ã®ã‚µãƒ¼ãƒãƒ¼ã®ã²ã¨ã¤ãªã‚“ã‚„ã§ã€‚" monthAndDay: "{month}月 {day}æ—¥" search: "探ã™" notifications: "通知" @@ -15,13 +15,13 @@ gotIt: "ã»ã„" cancel: "ã‚„ã‚ã¨ã" noThankYou: "ã‚„ã‚ã¨ã" enterUsername: "ユーザーåを入れã¦ã‚„" -renotedBy: "{user}ãŒRenote" -noNotes: "ノートã¯ã‚らã¸ã‚“" -noNotifications: "通知ã¯ã‚らã¸ã‚“" -instance: "インスタンス" +renotedBy: "{user}ãŒRenoteã—ãŸã§" +noNotes: "ノートãªã‚“ã¦ã‚らã¸ã‚“ã§" +noNotifications: "通知ãªã‚“ã¦ã‚らã¸ã‚“ã§" +instance: "サーãƒãƒ¼" settings: "è¨å®š" basicSettings: "基本è¨å®š" -otherSettings: "ãã®ä»–ã®è¨å®š" +otherSettings: "ã»ã‹ã®è¨å®š" openInWindow: "ウィンドウã§é–‹ãã§" profile: "プãƒãƒ•ã‚£ãƒ¼ãƒ«" timeline: "タイムライン" @@ -55,7 +55,7 @@ searchUser: "ユーザーを検索" reply: "返事" loadMore: "ã¾ã ã¾ã ã‚ã‚‹ã§ï¼" showMore: "ã¾ã ã¾ã ã‚ã‚‹ã§ï¼" -showLess: "é–‰ã˜ã‚‹" +showLess: "ã•ã„ãªã‚‰" youGotNewFollower: "フォãƒãƒ¼ã•ã‚ŒãŸã§" receiveFollowRequest: "フォãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã•ã‚ŒãŸã§" followRequestAccepted: "フォãƒãƒ¼ãŒæ‰¿èªã•ã‚ŒãŸã§" @@ -84,12 +84,12 @@ error: "エラー" somethingHappened: "ãªã‚“ã‹ã‚¢ã‚«ãƒ³ã“ã¨ãŒèµ·ã“ã£ãŸã§" retry: "ã‚‚ã£ãºã‚“やる?" pageLoadError: "ページã®èªã¿è¾¼ã¿ã«å¤±æ•—ã—ã¦ã‚‚ã†ãŸã‚…" -pageLoadErrorDescription: "ã“ã‚Œã¯æ™®é€šã€ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‹ãƒ–ラウザã‚ャッシュãŒåŽŸå› ã‚„ã‹ã‚‰ã。ã‚ャッシュをクリアã™ã‚‹ã‹ã€ã‚‚ã†ã¡ã£ã¨ã ã‘å¾…ã£ã¦ãã‚Œã¸ã‚“ã‹ï¼Ÿ" +pageLoadErrorDescription: "ã“ã‚Œã¯æ™®é€šãªã‚‰ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯ã‹ãƒ–ラウザã‚ャッシュãŒæ‚ªã•ã—ã¦ã‚‹ã‚“よ。ã‚ャッシュをã»ã‹ã™ã‹ã€ã‚‚ã†ã¡ã‚‡ã£ã¨ã ã‘å¾…ã£ã¦ãã‚Œã¸ã‚“?" serverIsDead: "サーãƒãƒ¼ã‹ã‚‰ã®å¿œç”ãŒãªã„ã§ã€‚ã‚‚ã†ã¡ã‚‡ã„å¾…ã£ã¦ã‹ã‚‰è©¦ã—ã¦ã¿ã¦ãªã€‚" youShouldUpgradeClient: "ã“ã®ãƒšãƒ¼ã‚¸ã‚’表示ã™ã‚‹ã«ã¯ã€ãƒªãƒãƒ¼ãƒ‰ã—ã¦æ–°ã—ã„ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆã‚’使ã£ã¦ãªãƒ¼ã€‚" enterListName: "リストåを入れã¦ã‚„" privacy: "プライãƒã‚·ãƒ¼" -makeFollowManuallyApprove: "他人ã®ãƒ•ã‚©ãƒãƒ¼ã¯è¨±å¯ã—ã¦ã‹ã‚‰ã‚„ï¼" +makeFollowManuallyApprove: "ãˆãˆã£ã¦è¨€ã‚ãªãƒ•ã‚©ãƒãƒ¼ã§ãã¸ã‚“よã†ã«ã™ã‚‹" defaultNoteVisibility: "ã‚‚ã¨ã‹ã‚‰ã®å…¬é–‹ç¯„囲" follow: "フォãƒãƒ¼" followRequest: "フォãƒãƒ¼ã‚’é ¼ã‚€" @@ -113,7 +113,7 @@ sensitive: "ã¡ã‚‡ã£ã¨ã‚¢ã‚«ãƒ³ã‚„ã¤ã‚„ã§" add: "増やã™" reaction: "リアクション" reactions: "リアクション" -reactionSetting: "Reaction that will be displayed in Picker. " +reactionSetting: "ピッカーã«å‡ºã—ã¨ãリアクション" reactionSettingDescription2: "ドラッグã§ä¸¦ã³æ›¿ãˆã€ã‚¯ãƒªãƒƒã‚¯ã§å‰Šé™¤ã€ï¼‹ã‚’押ã—ã¦è¿½åŠ ã‚„ã§ã€‚" rememberNoteVisibility: "公開範囲覚ãˆã¨ã„ã¦" attachCancel: "ã®ã£ã‘ã‚‹ã®ã‚„ã‚ã‚‹" @@ -122,6 +122,8 @@ unmarkAsSensitive: "ãã“ã¾ã§ã‚¢ã‚«ãƒ³ã“ã¨ãªã„ã‚„ã‚" enterFileName: "ファイルåを入れã¦ã‚„" mute: "ミュート" unmute: "ミュートやã‚ãŸã‚‹" +renoteMute: "リノートã¯è¦‹ã„ã²ã‚“" +renoteUnmute: "リノートもやã£ã±è¦‹ã‚‹ã‚" block: "ブãƒãƒƒã‚¯" unblock: "ブãƒãƒƒã‚¯ã‚„ã‚ãŸã‚‹" suspend: "å‡çµ" @@ -145,20 +147,21 @@ addEmoji: "絵文å—ã‚’è¿½åŠ " settingGuide: "ãˆãˆæ„Ÿã˜ã®è¨å®š" cacheRemoteFiles: "リモートã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’ã‚ャッシュã™ã‚‹" cacheRemoteFilesDescription: "ã“ã®è¨å®šã‚’切ã£ã¨ãã¨ã€ãƒªãƒ¢ãƒ¼ãƒˆãƒ•ã‚¡ã‚¤ãƒ«ã‚’ã‚ャッシュã›ãšç›´ãƒªãƒ³ã‚¯ã™ã‚‹ã‚ˆã†ã«ãªã‚‹ã§ã€‚サーãƒãƒ¼ã®å®¹é‡ã¯ç¯€ç´„ã§ãã‚‹ã‘ã©ã€ã‚µãƒ ãƒã‚¤ãƒ«ãŒä½œã‚‰ã‚Œã‚“ããªã‚‹ã‹ã‚‰é€šä¿¡é‡ãŒå¢—ãˆã‚‹ã§ã€‚" -flagAsBot: "Botã‚„ã§" -flagAsBotDescription: "ã‚‚ã—ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒãƒ—ãƒã‚°ãƒ©ãƒ ã«ã‚ˆã£ã¦é‹ç”¨ã•ã‚Œã‚‹ã‚“ã‚„ã£ãŸã‚‰ã€ã“ã®ãƒ•ãƒ©ã‚°ã‚’オンã«ã—ã¦ãŸã®ã‚€ã§ã€‚オンã«ã™ã‚‹ã¨ã€åå¿œã®é€£éŽ–を防ããŸã‚ã®ãƒ•ãƒ©ã‚°ã¨ã—ã¦ä»–ã®é–‹ç™ºè€…ã«å½¹ç«‹ã£ãŸã‚Šã€Misskeyã®ã‚·ã‚¹ãƒ†ãƒ 上ã§ã®æ‰±ã„ãŒBotã«åˆã£ãŸã‚‚ã‚“ã«ãªã‚‹ã‚“ã‚„ã§ã€‚" +flagAsBot: "Botã«ã™ã‚‹ã§" +flagAsBotDescription: "ã‚‚ã—ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’プãƒã‚°ãƒ©ãƒ 使ã†ã¦é‹ç”¨ã™ã‚‹ã‚“ã‚„ã£ãŸã‚‰ã€ã“ã®ãƒ•ãƒ©ã‚°ã‚’オンã«ã—ã¦ã‚„。オンã«ã™ã‚Œã°ã€åå¿œãŒãƒãƒ¼ãƒƒã¦é€£éŽ–ã™ã‚‹ã®ã‚’é¿ã‘ã‚‹ãŸã‚ã«é–‹ç™ºè€…ãŒä½¿ã†ãŸã‚Šã€Misskeyã®ã‚·ã‚¹ãƒ†ãƒ 上ã§ã®æ‰±ã„ãŒBotã«åˆã£ãŸã‚‚ã‚“ã«ãªã‚‹ã‹ã‚‰ãªã€‚" flagAsCat: "Catã‚„ã§" flagAsCatDescription: "ワレã€çŒ«ã¡ã‚ƒã‚“ãªã‚‰ã“ã®ãƒ•ãƒ©ã‚°ã‚’ã¤ã‘ã¦ã¿ï¼Ÿ" flagShowTimelineReplies: "タイムラインã«ãƒŽãƒ¼ãƒˆã¸ã®è¿”信を表示ã™ã‚‹ã§" flagShowTimelineRepliesDescription: "オンã«ã—ãŸã‚‰ã€ã‚¿ã‚¤ãƒ ラインã«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ãƒŽãƒ¼ãƒˆã®ä»–ã«ã‚‚ãã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã®ä»–ã®ãƒŽãƒ¼ãƒˆã¸ã®è¿”信を表示ã™ã‚‹ã§ã€‚" autoAcceptFollowed: "フォãƒãƒ¼ã—ã¨ã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ãƒªã‚¯ã‚¨ã‚¹ãƒˆã‚’å‹æ‰‹ã«è¨±å¯ã—ã¨ã" addAccount: "ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‚’è¿½åŠ " +reloadAccountsList: "アカウントリストã®æƒ…å ±ã‚’æ›´æ–°" loginFailed: "ãƒã‚°ã‚¤ãƒ³ã«å¤±æ•—ã—ã¦ã‚‚ã†ãŸâ€¦" showOnRemote: "リモートã§è¦‹ã‚‹" general: "全般" wallpaper: "å£ç´™" setWallpaper: "å£ç´™ã‚’è¨å®š" -removeWallpaper: "å£ç´™ã‚’削除" +removeWallpaper: "å£ç´™ã»ã‹ã™" searchWith: "検索: {q}" youHaveNoLists: "リストãŒã‚らã¸ã‚“ã§ï¼Ÿ" followConfirm: "{name}をフォãƒãƒ¼ã—ã¦ãˆãˆã‹ï¼Ÿ" @@ -169,7 +172,7 @@ selectUser: "ユーザーをé¸ã¶" recipient: "宛先" annotation: "注釈" federation: "連åˆ" -instances: "インスタンス" +instances: "サーãƒãƒ¼" registeredAt: "åˆè¦³æ¸¬" latestRequestReceivedAt: "ã¡ã‚‡ã£ã¨å‰ã®ãƒªã‚¯ã‚¨ã‚¹ãƒˆå—ä¿¡" latestStatus: "ã¡ã‚‡ã£ã¨å‰ã®ã‚¹ãƒ†ãƒ¼ã‚¿ã‚¹" @@ -178,7 +181,7 @@ charts: "ãƒãƒ£ãƒ¼ãƒˆ" perHour: "1時間ã”ã¨" perDay: "1æ—¥ã”ã¨" stopActivityDelivery: "アクティビティã®é…é€ã‚’ã‚„ã‚ã‚‹" -blockThisInstance: "ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã‚’ブãƒãƒƒã‚¯" +blockThisInstance: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã‚’ブãƒãƒƒã‚¯ã™ã‚“ã§" operations: "æ“作" software: "ソフトウェア" version: "ãƒãƒ¼ã‚¸ãƒ§ãƒ³" @@ -189,28 +192,28 @@ jobQueue: "ジョブã‚ュー" cpuAndMemory: "CPUã¨ãƒ¡ãƒ¢ãƒª" network: "ãƒãƒƒãƒˆãƒ¯ãƒ¼ã‚¯" disk: "ディスク" -instanceInfo: "ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹æƒ…å ±" +instanceInfo: "サーãƒãƒ¼æƒ…å ±" statistics: "統計" clearQueue: "ã‚ューã«ã•ã„ãªã‚‰" clearQueueConfirmTitle: "ã‚ューをクリアã—ã¾ã£ã‹ï¼Ÿ" -clearQueueConfirmText: "未é…é”ã®æŠ•ç¨¿ã¯é…é€ã•ã‚Œãªããªã‚‹ã§ã€‚通常ã“ã®æ“作を行ã†å¿…è¦ã¯ã‚らã¸ã‚“や。" +clearQueueConfirmText: "未é…é”ã®æŠ•ç¨¿ã¯é…é€ã•ã‚Œãªããªã‚‹ã§ã€‚ãµã¤ã†ã“ã®æ“作を行ã†å¿…è¦ã¯ç„¡ã„ã‚“ã‚„ã‘ã©ãªã€‚" clearCachedFiles: "ã‚ャッシュã«ã•ã„ãªã‚‰" clearCachedFilesConfirm: "ã‚ャッシュã•ã‚Œã¨ã‚‹ãƒªãƒ¢ãƒ¼ãƒˆãƒ•ã‚¡ã‚¤ãƒ«ã‚’ã¿ã‚“ãªã»ã‹ã—ã¦ãˆãˆã‹ï¼Ÿ" -blockedInstances: "インスタンスブãƒãƒƒã‚¯" -blockedInstancesDescription: "ブãƒãƒƒã‚¯ã—ãŸã„インスタンスã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨å®šã—ã¦ãªã€‚ブãƒãƒƒã‚¯ã•ã‚Œã¦ã‚‚ã†ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã¨ã¯ã‚‚ã†é‡‘輪際やりå–ã‚Šã§ãã²ã‚“ããªã‚‹ã§ã€‚" +blockedInstances: "ブãƒãƒƒã‚¯ã—ãŸã‚µãƒ¼ãƒãƒ¼" +blockedInstancesDescription: "ブãƒãƒƒã‚¯ã—ãŸã„サーãƒãƒ¼ã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨å®šã—ã¦ãªã€‚ブãƒãƒƒã‚¯ã•ã‚Œã¦ã‚‚ã†ãŸã‚µãƒ¼ãƒãƒ¼ã¨ã¯ã‚‚ã†é‡‘輪際やりå–ã‚Šã§ãã²ã‚“ããªã‚‹ã§ã€‚ã¤ã„ã§ã«ãã®ã‚µãƒ–ドメインもブãƒãƒƒã‚¯ã™ã‚‹ã§ã€‚" muteAndBlock: "ミュートã¨ãƒ–ãƒãƒƒã‚¯" mutedUsers: "ミュートã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼" blockedUsers: "ブãƒãƒƒã‚¯ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼" -noUsers: "ユーザーã¯ãŠã‚‰ã¸ã‚“" +noUsers: "ユーザーã¯ãŠã‚‰ã‚“" editProfile: "プãƒãƒ•ã‚£ãƒ¼ãƒ«ã‚’ã„ã˜ã‚‹" noteDeleteConfirm: "ã“ã®ãƒŽãƒ¼ãƒˆã‚’削除ã—ã¾ã£ã‹ï¼Ÿ" pinLimitExceeded: "ã“れ以上ピン留ã‚ã§ãã²ã‚“" -intro: "Misskeyã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãŒå®Œäº†ã—ã¦ã‚“ï¼ç®¡ç†è€…アカウントを作ã£ã¦ã‚„。" +intro: "Misskeyã®ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ãŒå®Œäº†ã—ãŸã§ï¼ç®¡ç†è€…アカウントを作ã£ã¦ã‚„。" done: "ã§ã‘ãŸ" processing: "処ç†ã—ã¨ã‚‹" preview: "プレビュー" default: "デフォルト" -defaultValueIs: "デフォルト" +defaultValueIs: "デフォルト: {value}" noCustomEmojis: "絵文å—ã¯ã‚らã¸ã‚“" noJobs: "ジョブã¯ã‚らã¸ã‚“" federating: "連åˆã—ã¨ã‚‹" @@ -220,17 +223,17 @@ all: "ã¿ã‚“ãª" subscribing: "è³¼èªã—ã¨ã‚‹" publishing: "é…ä¿¡ã—ã¨ã‚‹" notResponding: "å¿œç”ã—ã¦ã¸ã‚“ã§" -instanceFollowing: "インスタンスã®ãƒ•ã‚©ãƒãƒ¼" -instanceFollowers: "インスタンスã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼\n" -instanceUsers: "インスタンスã®ãƒ¦ãƒ¼ã‚¶ãƒ¼" +instanceFollowing: "サーãƒãƒ¼ã®ãƒ•ã‚©ãƒãƒ¼" +instanceFollowers: "サーãƒãƒ¼ã®ãƒ•ã‚©ãƒãƒ¯ãƒ¼\n" +instanceUsers: "サーãƒãƒ¼ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼" changePassword: "パスワード変ãˆã‚‹" security: "ã‚»ã‚ュリティ" -retypedNotMatch: "ãã‚„ãªã„ãん。" +retypedNotMatch: "入れãŸã‚„ã¤åŒã˜ã«ãªã£ã¦ãªã„ã§ã€‚" currentPassword: "今ã®ãƒ‘スワード" -newPassword: "今度ã®ãƒ‘スワード" +newPassword: "次ã®ãƒ‘スワード" newPasswordRetype: "今度ã®ãƒ‘スワード(ã‚‚ã£ãºã‚“入れã¦)" attachFile: "ファイルã®ã£ã‘ã‚‹" -more: "ä»–ã®ã‚„ã¤ï¼" +more: "ä»–ã®ã‚“" featured: "ãƒã‚¤ãƒ©ã‚¤ãƒˆ" usernameOrUserId: "ユーザーåã‹ãƒ¦ãƒ¼ã‚¶ãƒ¼ID" noSuchUser: "ユーザーãŒè¦‹ã¤ã‹ã‚‰ã¸ã‚“ã§" @@ -238,15 +241,15 @@ lookup: "見ã¦ãã¦" announcements: "ãŠçŸ¥ã‚‰ã›" imageUrl: "ç”»åƒURL" remove: "ã»ã‹ã™" -removed: "削除ã—ãŸã§ï¼" +removed: "ã»ã‹ã—ãŸã§ï¼" removeAreYouSure: "「{x}ã€ã¯ã»ã‹ã—ã¦ãˆãˆã‹ï¼Ÿ" deleteAreYouSure: "「{x}ã€ã¯ã»ã‹ã—ã¦ãˆãˆã‹ï¼Ÿ" resetAreYouSure: "リセットã—ã¦ãˆãˆã‚“?" saved: "ä¿å˜ã—ãŸã§ï¼" messaging: "ãƒãƒ£ãƒƒãƒˆ" upload: "アップãƒãƒ¼ãƒ‰" -keepOriginalUploading: "オリジナル画åƒã‚’ä¿æŒã™ã‚‹ã‚" -keepOriginalUploadingDescription: "ç”»åƒã‚’上ã’ã‚‹ã¨ãã«ã‚ªãƒªã‚¸ãƒŠãƒ«ç‰ˆã‚’ä¿æŒã™ã‚‹ã§ã€‚オフã«ã—ãŸã‚‰ä¸Šã’ãŸã¨ãã«ãƒ–ラウザã§Web公開用ã®ç”»åƒã‚’生æˆã™ã‚‹ã§ã€‚ " +keepOriginalUploading: "オリジナル画åƒã®ã¾ã‚“ã¾" +keepOriginalUploadingDescription: "ç”»åƒã‚’上ã’ã‚‹ã¨ãã«ã‚ªãƒªã‚¸ãƒŠãƒ«ç‰ˆã®ã¾ã‚“ã¾ã«ã™ã‚‹ã§ã€‚オフã«ã—ãŸã‚‰ã€ä¸Šã’ãŸã¨ãã«ãƒ–ラウザã§Web公開用ã®ç”»åƒã‚’生æˆã™ã‚‹ã§ã€‚ " fromDrive: "ドライブã‹ã‚‰" fromUrl: "URLã‹ã‚‰" uploadFromUrl: "URLアップãƒãƒ¼ãƒ‰" @@ -272,8 +275,8 @@ yearsOld: "{age}æ³" registeredDate: "始ã‚ãŸæ—¥" location: "å ´æ‰€" theme: "テーマ" -themeForLightMode: "ライトモードã§ã¯ã“ã®ãƒ†ãƒ¼ãƒžã¤ã“ã¦" -themeForDarkMode: "ダークモードã§ã¯ã“ã®ãƒ†ãƒ¼ãƒžã¤ã“ã¦" +themeForLightMode: "ライトモードã§ã¯ã“ã®ãƒ†ãƒ¼ãƒžä½¿ã†ã¦" +themeForDarkMode: "ダークモードã§ã¯ã“ã®ãƒ†ãƒ¼ãƒžä½¿ã†ã¦" light: "ライト" dark: "ダーク" lightThemes: "デイゲーム" @@ -289,13 +292,13 @@ renameFile: "ファイルåã‚’ã„らã†" folderName: "フォルダーå" createFolder: "フォルダー作る" renameFolder: "フォルダーåを変ãˆã‚‹" -deleteFolder: "フォルダーを消ã—ã¦ã¾ã†" +deleteFolder: "フォルダーをã»ã‹ã™" addFile: "ãƒ•ã‚¡ã‚¤ãƒ«ã‚’è¿½åŠ " emptyDrive: "ドライブã«ã¯ãªã‚“も残ã£ã¨ã‚‰ã‚“" -emptyFolder: "ãµã‰ã‚ã ーã«ã¯ãªã‚“も残ã£ã¨ã‚‰ã‚“" +emptyFolder: "ã“ã®ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã¯ç©ºã‚„" unableToDelete: "消ãã†ãŠã‚‚ã£ã¦ã‚“ã‘ã©ãªã€ã‚ã‹ã‚“ã‹ã£ãŸã‚" inputNewFileName: "今度ã®ãƒ•ã‚¡ã‚¤ãƒ«åã¯ä½•ã«ã™ã‚‹ã‚“?" -inputNewDescription: "æ–°ã—ã„ã‚ャプションを入力ã—ã¾ã—ょ" +inputNewDescription: "æ–°ã—ã„ã‚ャプションを入れã¦ã‚„" inputNewFolderName: "今度ã®ãƒ•ã‚©ãƒ«ãƒ€åã¯ä½•ã«ã™ã‚‹ã‚“?" circularReferenceFolder: "移動先ã®ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã¯ã€ç§»å‹•ã™ã‚‹ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã®ã‚µãƒ–フォルダーや。" hasChildFilesOrFolders: "ã“ã®ãƒ•ã‚©ãƒ«ãƒ€ã€ã¾ã ãªã‚“ã‹å…¥ã£ã¨ã‚‹ã‹ã‚‰æ¶ˆã•ã‚Œã¸ã‚“" @@ -303,8 +306,8 @@ copyUrl: "URLをコピー" rename: "åå‰ã‚’変ãˆã‚‹ã§" avatar: "アイコン" banner: "ãƒãƒŠãƒ¼" -nsfw: "閲覧注æ„" -whenServerDisconnected: "サーãƒãƒ¼ã¨ã®æŽ¥ç¶šãŒåˆ‡ã‚ŒãŸã¨ã" +nsfw: "見るんã¯æ°—ã„ã¤ã‘ã¦ãª" +whenServerDisconnected: "サーãƒãƒ¼ã¨ã®æŽ¥ç¶šãŒå¤±ããªã£ã¦ã—ã‚‚ã†ãŸã¨ã" disconnectedFromServer: "サーãƒãƒ¼ãŒæ©Ÿå«Œæ‚ªã„ãã‚“" reload: "リãƒãƒ¼ãƒ‰" doNothing: "何もã›ã‚“ã¨ã" @@ -314,10 +317,10 @@ unwatch: "ウォッãƒã‚„ã‚ã‚‹" accept: "ãˆãˆã§" reject: "ã‚ã‹ã‚“" normal: "ãˆãˆæ„Ÿã˜" -instanceName: "インスタンスå" -instanceDescription: "インスタンスã®ç´¹ä»‹" -maintainerName: "管ç†è€…ã®åå‰" -maintainerEmail: "管ç†è€…ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹" +instanceName: "サーãƒãƒ¼å" +instanceDescription: "サーãƒãƒ¼ã®ç´¹ä»‹" +maintainerName: "管ç†è€…ã¯ã‚“ã®åå‰" +maintainerEmail: "管ç†è€…ã¯ã‚“ã®ãƒ¡ãƒ¼ãƒ«ã‚¢ãƒ‰ãƒ¬ã‚¹" tosUrl: "利用è¦ç´„ã®URL" thisYear: "今年" thisMonth: "今月" @@ -329,23 +332,23 @@ pages: "ページ" integration: "連æº" connectService: "ã¤ãªã’ã‚‹ã§" disconnectService: "切るã§" -enableLocalTimeline: "ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインを使ãˆã‚‹ã‚ˆã†ã«ã™ã‚‹" -enableGlobalTimeline: "ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚¤ãƒ ラインを使ãˆã‚‹ã‚ˆã†ã«ã™ã‚‹" +enableLocalTimeline: "ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインを使ãˆã‚‹ã‚ˆã†ã«ã™ã‚‹ã‚" +enableGlobalTimeline: "ã‚°ãƒãƒ¼ãƒãƒ«ã‚¿ã‚¤ãƒ ラインを使ãˆã‚‹ã‚ˆã†ã«ã™ã‚‹ã‚" disablingTimelinesInfo: "ã“ã“らã¸ã‚“ã®ã‚¿ã‚¤ãƒ ラインを使ãˆã‚“よã†ã«ã—ã¦ã—ã‚‚ã¦ã‚‚ã€ç®¡ç†è€…ã¨ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ã¯ä½¿ãˆã‚‹ã¾ã¾ã«ãªã£ã¦ã‚‹ã§ã€ãã†ã‚„ãªã‹ã£ãŸã‚‰ä¸ä¾¿ã‚„ã‹ã‚‰ãªã€‚" registration: "登録" enableRegistration: "一見ã•ã‚“ã§ã‚‚誰ã§ã‚‚ã„らã£ã—ゃ~ã„" invite: "æ¥ã¦ã‚„" -driveCapacityPerLocalAccount: "ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã²ã¨ã‚Šã‚ãŸã‚Šã®ãƒ‰ãƒ©ã‚¤ãƒ–容é‡" -driveCapacityPerRemoteAccount: "リモートユーザーã²ã¨ã‚Šã‚ãŸã‚Šã®ãƒ‰ãƒ©ã‚¤ãƒ–容é‡" +driveCapacityPerLocalAccount: "ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯ã‚“ã²ã¨ã‚Šã‚ãŸã‚Šã®ãƒ‰ãƒ©ã‚¤ãƒ–容é‡" +driveCapacityPerRemoteAccount: "リモートユーザーã¯ã‚“ã²ã¨ã‚Šã‚ãŸã‚Šã®ãƒ‰ãƒ©ã‚¤ãƒ–容é‡" inMb: "メガãƒã‚¤ãƒˆå˜ä½" iconUrl: "アイコン画åƒã®URL" bannerUrl: "ãƒãƒŠãƒ¼ç”»åƒã®URL" backgroundImageUrl: "背景画åƒã®URL" basicInfo: "åŸºæœ¬æƒ…å ±" pinnedUsers: "ピン留ã‚ã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼" -pinnedUsersDescription: "「ã¿ã¤ã‘ã‚‹ã€ãƒšãƒ¼ã‚¸ã¨ã‹ã«ãƒ”ン留ã‚ã—ãŸã„ユーザーをã“ã“ã«æ›¸ã‘ã°ãˆãˆã‚“ã‚„ã§ã€‚他ん人ã¨ã®åå‰ã¯æ”¹è¡Œã§åŒºåˆ‡ã‚Œã°ãˆãˆã‚“ã‚„ã§ã€‚" +pinnedUsersDescription: "「ã¿ã¤ã‘ã‚‹ã€ãƒšãƒ¼ã‚¸ã¨ã‹ã«ãƒ”ン留ã‚ã—ãŸã„ユーザーをã“ã“ã«æ›¸ã‘ã°ãˆãˆã‚“ã‚„ã§ã€‚ユーザー毎ã«æ”¹è¡Œã—ã¦ã‚„。" pinnedPages: "ピン留ã‚ページ" -pinnedPagesDescription: "インスタンスã®ã„ã£ã¡ã‚ƒã‚“上ã«ãƒ”ン留ã‚ã—ãŸã„ページã®ãƒ‘スを改行ã§åŒºåˆ‡ã£ã¦è¨˜è¿°ã—ã¦ãª" +pinnedPagesDescription: "サーãƒãƒ¼ã®ã„ã£ã¡ã‚ƒã‚“上ã«ãƒ”ン留ã‚ã—ãŸã„ページã®ãƒ‘スを改行ã§åŒºåˆ‡ã£ã¦è¨˜è¿°ã—ã¦ãª" pinnedClipId: "ピン留ã‚ã™ã‚‹ã‚¯ãƒªãƒƒãƒ—ã®ID" pinnedNotes: "ピン留ã‚ã•ã‚Œã¨ã‚‹ãƒŽãƒ¼ãƒˆ" hcaptcha: "hCaptcha(ã‚ャプãƒãƒ£ï¼‰" @@ -370,7 +373,7 @@ antennaExcludeKeywords: "除外ã‚ーワード" antennaKeywordsDescription: "スペースã§åŒºåˆ‡ã£ãŸã‚‹ã¨AND指定ã§ã€æ”¹è¡Œã§åŒºåˆ‡ã£ãŸã‚‹ã¨OR指定や" notifyAntenna: "æ–°ã—ã„ノートを通知ã™ã‚“ã§" withFileAntenna: "ãªã‚“ã‹æ·»ä»˜ã•ã‚ŒãŸãƒŽãƒ¼ãƒˆã ã‘" -enableServiceworker: "ServiceWorkerã‚’ã¤ã“ã¦" +enableServiceworker: "ブラウザã«ãƒ—ッシュ通知ãŒè¡Œãよã†ã«ã™ã‚‹" antennaUsersDescription: "ユーザーåを改行ã§åŒºåˆ‡ã£ãŸã£ã¦ãª" caseSensitive: "大文å—ã¨å°æ–‡å—ã¯åˆ¥ã‚‚ã‚“ã‚„" withReplies: "返信も入れãŸã£ã¦" @@ -395,23 +398,23 @@ administrator: "管ç†è€…" token: "トークン" 2fa: "二è¦ç´ èªè¨¼" totp: "èªè¨¼ã‚¢ãƒ—リ" -totpDescription: "èªè¨¼ã‚¢ãƒ—リ使ã¦ãƒ¯ãƒ³ã‚¿ã‚¤ãƒ パスワードを入れる" +totpDescription: "èªè¨¼ã‚¢ãƒ—リ使ã†ã¦ãƒ¯ãƒ³ã‚¿ã‚¤ãƒ パスワードを入れる" moderator: "モデレーター" moderation: "モデレーション" nUsersMentioned: "{n}人ãŒæŠ•ç¨¿" securityKeyAndPasskey: "ã‚»ã‚ュリティã‚ー・パスã‚ー" securityKey: "ã‚»ã‚ュリティã‚ー" lastUsed: "最後ã«ã¤ã“ã†ãŸæ—¥" -lastUsedAt: "最後ã«ä½¿ãŸã‚“: {t}" +lastUsedAt: "最後ã«ä½¿ã†ãŸã‚“ã¯: {t}" unregister: "登録やã‚ã‚‹" passwordLessLogin: "パスワード無ãã¦ã‚‚ãƒã‚°ã‚¤ãƒ³ã§ãるよã†ã«ã™ã‚‹" -passwordLessLoginDescription: "パスワードやãªãã¦ã€ã‚»ã‚ュリティã‚ーã¨ã‹ãƒ‘スã‚ーã ã‘ã§ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã‚" +passwordLessLoginDescription: "パスワードãªã‚“ã‹ã„らんã€ã‚»ã‚ュリティã‚ーã¨ã‹ãƒ‘スã‚ーã ã‘ã§ãƒã‚°ã‚¤ãƒ³ã™ã‚‹ã‚" resetPassword: "パスワードをリセット" newPasswordIs: "今度ã®ãƒ‘スワードã¯ã€Œ{password}ã€ã‚„" -reduceUiAnimation: "UIã®å‹•ãやアニメーションを減らã™" +reduceUiAnimation: "UIã®å‹•ãやアニメーションを少ãªã™ã‚‹" share: "ã‚ã‘ã‚ã‘" notFound: "見ã¤ã‹ã‚‰ã¸ã‚“ã" -notFoundDescription: "指定ã•ã‚ŒãŸURLã«è©²å½“ã™ã‚‹ãƒšãƒ¼ã‚¸ã¯ã‚らã¸ã‚“ã‚„ã£ãŸã€‚" +notFoundDescription: "言ã‚ã‚ŒãŸURLã«ã¯ã¾ã‚‹ãƒšãƒ¼ã‚¸ã¯ãªã‹ã£ãŸã§ã€‚" uploadFolder: "ã¨ã‚Šã‚ãˆãšã‚¢ãƒƒãƒ—ãƒãƒ¼ãƒ‰ã—ãŸã‚„ã¤ç½®ã„ã¨ã所" cacheClear: "ã‚ャッシュをã»ã‹ã™" markAsReadAllNotifications: "通知ã¯ã‚‚ã†å…¨ã¦èªã‚“ã ã‚ã£" @@ -419,37 +422,37 @@ markAsReadAllUnreadNotes: "投稿ã¯å…¨ã¦èªã‚“ã ã‚ã£" markAsReadAllTalkMessages: "ãƒãƒ£ãƒƒãƒˆã¯ã‚‚ã†ãœã‚“ã¶èªã‚“ã ã‚ã£" help: "ヘルプ" inputMessageHere: "ã“ã“ã«ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸æ›¸ã„ã¦ã‚„" -close: "é–‰ã˜ã‚‹" +close: "ã•ã„ãªã‚‰" invites: "æ¥ã¦ã‚„" -members: "メンãƒãƒ¼" +members: "メンãƒãƒ¼ã¯ã‚“" transfer: "è²æ¸¡" title: "タイトル" text: "テã‚スト" enable: "有効ã«ã™ã‚‹ã§" next: "次" retype: "ã‚‚ã£ã‹ã„入力" -noteOf: "{user}ã®ãƒŽãƒ¼ãƒˆ" +noteOf: "{user}ã¯ã‚“ã®ãƒŽãƒ¼ãƒˆ" quoteAttached: "引用付ã„ã¨ã‚‹ã§" quoteQuestion: "引用ã¨ã—ã¦æ·»ä»˜ã—ã¦ã‚‚ãˆãˆã‹ï¼Ÿ" noMessagesYet: "ã¾ã ãƒãƒ£ãƒƒãƒˆã¯ã‚らã¸ã‚“ã§" newMessageExists: "æ–°ã—ã„メッセージãŒããŸã§" -onlyOneFileCanBeAttached: "ã™ã¾ã‚“ã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã«æ·»ä»˜ã§ãるファイルã¯ã²ã¨ã¤ã ã‘ãªã‚“や。" +onlyOneFileCanBeAttached: "ã”ã‚ã‚“ãªã€ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã«æ·»ä»˜ã§ãるファイルã¯ã²ã¨ã¤ã ã‘ãªã‚“よ。" signinRequired: "ãƒã‚°ã‚¤ãƒ³ã—ã¦ãã‚Œã¸ã‚“?" invitations: "æ¥ã¦ã‚„" invitationCode: "招待コード" checking: "確èªã—ã¨ã‚‹ã§" -available: "利用ã§ãã‚‹\n" +available: "使ãˆã‚‹ã§" unavailable: "利用ã§ãã‚“" usernameInvalidFormat: "a~zã€A~Zã€0~9ã€_ãŒä½¿ãˆã‚‹ã§" tooShort: "çŸã™ãŽã‚„ã‚ï¼" tooLong: "é•·ã™ãŽã‚„ã‚ï¼" weakPassword: "ã¸ã¼ã„パスワード" -normalPassword: "普通ã®ãƒ‘スワード" +normalPassword: "ã¼ã¡ã¼ã¡ã®ãƒ‘スワード" strongPassword: "ãˆãˆæ„Ÿã˜ã®ãƒ‘スワード" passwordMatched: "よã—ï¼ä¸€è‡´ã‚„ï¼" passwordNotMatched: "一致ã—ã¨ã‚‰ã‚“ã§ï¼Ÿ" signinWith: "{x}ã§ãƒã‚°ã‚¤ãƒ³" -signinFailed: "ãƒã‚°ã‚¤ãƒ³ã§ãã‚“ã‹ã£ãŸã§ã€‚ã‚‚ã£ã‹ã„ユーザーåã¨ãƒ‘スワードを確èªã—ã¦ã¿ã¦ãªã€‚" +signinFailed: "ãƒã‚°ã‚¤ãƒ³ã§ãã‚“ã‹ã£ãŸã§ã€‚ã‚‚ã£ã‹ã„ユーザーåã¨ãƒ‘スワードを確èªã—ã¦ã¿ã¦ã‚„。" or: "ãã‚Œã‹" language: "言語" uiLanguage: "UIã®è¡¨ç¤ºè¨€èªž" @@ -458,7 +461,7 @@ emojiStyle: "絵文å—ã®ã‚¹ã‚¿ã‚¤ãƒ«" native: "ãƒã‚¤ãƒ†ã‚£ãƒ–" disableDrawer: "メニューをドãƒãƒ¯ãƒ¼ã§è¡¨ç¤ºã›ã‡ã¸ã‚“" showNoteActionsOnlyHover: "ノートã®æ“作部をホãƒãƒ¼æ™‚ã®ã¿è¡¨ç¤ºã™ã‚‹ã§" -noHistory: "å±¥æ´ã¯ã‚らã¸ã‚“ãã‡ã€‚" +noHistory: "å±¥æ´ã¯ãªã„ã‚。" signinHistory: "ãƒã‚°ã‚¤ãƒ³å±¥æ´" enableAdvancedMfm: "ã‚„ã‚„ã“ã—ã„MFMã‚‚ã‚ã‚Šã«ã™ã‚‹" enableAnimatedMfm: "å‹•ããŒã‚„ã‹ã¾ã—ã„MFMも許ã—ãŸã‚‹" @@ -466,12 +469,12 @@ doing: "ã‚„ã£ã¨ã‚‹ãŒãª" category: "カテゴリ" tags: "ã‚¿ã‚°" docSource: "ã“ã®ãƒ‰ã‚ュメントã®ã‚½ãƒ¼ã‚¹" -createAccount: "アカウントを作æˆ" -existingAccount: "æ—¢å˜ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆ" -regenerate: "å†ç”Ÿæˆ" -fontSize: "フォントサイズ" +createAccount: "アカウントを作るã§" +existingAccount: "å‰ã«ä½œã£ãŸã‚¢ã‚«ã‚¦ãƒ³ãƒˆ" +regenerate: "ã‚‚ã£ãºã‚“生æˆã™ã‚‹ã§" +fontSize: "å—ã®å¤§ãã•" noFollowRequests: "フォãƒãƒ¼ç”³è«‹ã¯ã‚らã¸ã‚“ã§" -openImageInNewTab: "ç”»åƒã‚’æ–°ã—ã„タブã§é–‹ã" +openImageInNewTab: "ç”»åƒã‚’æ–°ã—ã„タブã§é–‹ãã§" dashboard: "ダッシュボード" local: "ãƒãƒ¼ã‚«ãƒ«" remote: "リモート" @@ -504,7 +507,7 @@ objectStorageUseProxy: "Proxyを使ã†" objectStorageUseProxyDesc: "API接続ã«proxy使ã‚ã‚“ã®ã‚„ã£ãŸã‚‰åˆ‡ã£ã¦ãã‚Œã¸ã‚“?" objectStorageSetPublicRead: "アップãƒãƒ¼ãƒ‰ã—ãŸæ™‚ã«'public-read'ã‚’è¨å®šã—ã¦ã‚„" serverLogs: "サーãƒãƒ¼ãƒã‚°" -deleteAll: "å…¨ã¦å‰Šé™¤ã—ã¦ã‚„" +deleteAll: "全部ã»ã‹ã™" showFixedPostForm: "タイムラインã®ä¸Šã®æ–¹ã§æŠ•ç¨¿ã§ãるよã†ã«ã‚„ã£ã¦ãã‚Œã¸ã‚“?" showFixedPostFormInChannel: "タイムラインã®ä¸Šã®æ–¹ã§æŠ•ç¨¿ã§ãるよã†ã«ã™ã‚‹ã‚(ãƒãƒ£ãƒ³ãƒãƒ«)" newNoteRecived: "æ–°ã—ã„ノートãŒã‚ã‚‹ã§" @@ -514,11 +517,11 @@ listen: "è´ã" none: "ãªã—" showInPage: "ページã§è¡¨ç¤º" popout: "ãƒãƒƒãƒ—アウト" -volume: "音é‡" -masterVolume: "全体ã®éŸ³é‡" +volume: "ã‚„ã‹ã¾ã—ã•" +masterVolume: "全体ã®ã‚„ã‹ã¾ã—ã•" details: "ã‚‚ã£ã¨" chooseEmoji: "絵文å—ã‚’é¸ã¶" -unableToProcess: "ãªã‚“ã‹ä½œæ¥ãŒæ¢ã¾ã£ã¦ã—ã¾ã£ãŸã‚ˆã†ã‚„ã" +unableToProcess: "ãªã‚“ã‹å¥¥ã®æ–¹ã§è©°ã¾ã£ã¦ã‚‚ã†ãŸ" recentUsed: "最近使ã£ãŸã‚„ã¤" install: "インストール" uninstall: "アンインストール" @@ -536,14 +539,18 @@ output: "出力" script: "スクリプト" disablePagesScript: "Pagesã®ã‚¹ã‚¯ãƒªãƒ—トを無効ã«ã—ã¦ã‚„" updateRemoteUser: "ãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼æƒ…å ±ã®æ›´æ–°ã—ã¦ãれん?" -deleteAllFiles: "ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’削除" -deleteAllFilesConfirm: "ホンマã«ã™ã¹ã¦ã®ãƒ•ã‚¡ã‚¤ãƒ«ã‚’削除ã™ã‚‹ã‚“?消ã—ãŸã‚‚ã‚“ã¯ã‚‚ã†æˆ»ã£ã¦ã“ã‚“ã®ã‚„ã§ï¼Ÿ" +deleteAllFiles: "ファイルを全部ã»ã‹ã™" +deleteAllFilesConfirm: "ホンマã«ãƒ•ã‚¡ã‚¤ãƒ«å…¨éƒ¨ã»ã‹ã™ã‚“ã‹ï¼Ÿæ¶ˆã—ãŸã‚‚ã‚“ã¯ã‚‚ã†æˆ»ã£ã¦ã“ã‚“ã®ã‚„ã§ï¼Ÿ" removeAllFollowing: "フォãƒãƒ¼ã‚’全解除" removeAllFollowingDescription: "{host}ã‹ã‚‰ã®ãƒ•ã‚©ãƒãƒ¼ã‚’ã™ã¹ã¦è§£é™¤ã™ã‚‹ã§ã€‚ãã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ãŒæ¶ˆãˆã¦ç„¡ããªã£ãŸæ™‚ã¨ã‹ã«ã¯ä¾¿åˆ©ãªæ©Ÿèƒ½ã‚„ã§ã€‚" userSuspended: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯...å‡çµã•ã‚Œã¨ã‚‹ã€‚" userSilenced: "ã“ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¯...サイレンスã•ã‚Œã¨ã‚‹ã€‚" yourAccountSuspendedTitle: "ã‚ã‚“ãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆå‡çµã•ã‚Œã¨ã‚‹ã§" yourAccountSuspendedDescription: "ã‚ã‚“ãŸã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã€ã‚µãƒ¼ãƒãƒ¼ã®åˆ©ç”¨è¦ç´„ã«é•åã—ãŸã¨ã‹ã®ç†ç”±ã§ã€å‡çµã•ã‚Œã¨ã‚‹ã§ã€‚ç´°ã‹ã„ã“ã¨ã¯ç®¡ç†è€…ã¾ã§ãŠå•ã„åˆã‚ã›ãŸã£ã¦ãªãƒ¼ã€‚絶対ã«æ–°ã—ã„アカウント作ã£ãŸã‚‰ã‚ã‹ã‚“ã§ã€‚絶対やã§ã€‚" +tokenRevoked: "トークンãŒç„¡åŠ¹ã‚„ã§" +tokenRevokedDescription: "ãƒã‚°ã‚¤ãƒ³ãƒˆãƒ¼ã‚¯ãƒ³ãŒå¤±åŠ¹ã—ã¨ã‚‹ã§ã€‚ã‚‚ã£ã‹ã„ãƒã‚°ã‚¤ãƒ³ã—ã¦ã‚‚ã‚ã¦ã‚‚ãˆãˆã‹ï¼Ÿ" +accountDeleted: "アカウントã¯å‰Šé™¤ã•ã‚Œã¨ã‚‹ã§" +accountDeletedDescription: "ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯å‰Šé™¤ã•ã‚Œã¨ã‚‹ã§ã€‚" menu: "メニュー" divider: "分割線" addItem: "é …ç›®ã‚’è¿½åŠ " @@ -566,7 +573,7 @@ description: "説明" describeFile: "ã‚ャプションを付ã‘ã‚‹" enterFileDescription: "ã‚ャプションを入力" author: "作者" -leaveConfirm: "未ä¿å˜ã®å¤‰æ›´ãŒã‚ã‚‹ã§ï¼ã»ã‹ã—ã¦ãˆãˆã‹ï¼Ÿ" +leaveConfirm: "ã‚ã‚“ãŸã€ã„ã˜ã£ãŸã®ã«ã¾ã ä¿å˜ã—ã¦ãªã„ã§ï¼ã»ã‹ã—ã¦ãˆãˆã‹ï¼Ÿ" manage: "管ç†" plugins: "プラグイン" preferencesBackups: "è¨å®šã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—" @@ -600,12 +607,12 @@ smtpUser: "ユーザーå" smtpPass: "パスワード" emptyToDisableSmtpAuth: "ユーザーåã¨ãƒ‘スワードã«ãªã‚“も入れんã‹ã£ãŸã‚‰ã€SMTPèªè¨¼ã‚’無効化ã™ã‚‹ã§" smtpSecure: "SMTP 接続ã«æš—黙的ãªSSL/TLSを使用ã™ã‚‹" -smtpSecureInfo: "STARTTLS使ã£ã¨ã‚‹æ™‚ã¯ã‚ªãƒ•ã«ã™ã‚‹ã§ã€‚" +smtpSecureInfo: "STARTTLS使ã£ã¨ã‚‹æ™‚ã¯ã‚ªãƒ•ã«ã—ã¦ã‚„。" testEmail: "é…信テスト" wordMute: "ワードミュート" regexpError: "æ£è¦è¡¨ç¾ã‚¨ãƒ©ãƒ¼" regexpErrorDescription: "{tab}ワードミュートã®{line}行目ã®æ£è¦è¡¨ç¾ã«ã‚¨ãƒ©ãƒ¼ãŒå‡ºã¦ããŸã§:" -instanceMute: "インスタンスミュート" +instanceMute: "サーãƒãƒ¼ãƒŸãƒ¥ãƒ¼ãƒˆ" userSaysSomething: "{name}ãŒä½•ã‹è¨€ã†ã¨ã‚‹ã‚" makeActive: "使ã†ã§" display: "表示" @@ -624,7 +631,7 @@ useGlobalSettingDesc: "オンã«ã™ã‚‹ã¨ã€ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®é€šçŸ¥è¨å®šãŒä½¿ other: "ãã®ä»–" regenerateLoginToken: "ãƒã‚°ã‚¤ãƒ³ãƒˆãƒ¼ã‚¯ãƒ³ã‚’å†ç”Ÿæˆ" regenerateLoginTokenDescription: "ãƒã‚°ã‚¤ãƒ³ã«ä½¿ã‚れる内部トークンをもã£ã‹ã„作るã§ã€‚ã„ã¤ã‚‚ãªã‚‰ã“れをやる必è¦ã¯ãªã„ã§ã€‚ã‚‚ã£ã‹ã„作るã¨ã€å…¨éƒ¨ã®ãƒ‡ãƒã‚¤ã‚¹ã§ãƒã‚°ã‚¢ã‚¦ãƒˆã•ã‚Œã‚‹ã§æ°—ãƒã¤ã‘ã¦ãªãƒ¼ã€‚" -setMultipleBySeparatingWithSpace: "スペースã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨å®šã§ãã‚‹ã§ã€‚" +setMultipleBySeparatingWithSpace: "スペースã§åŒºåˆ‡ã£ã¦ä½•å€‹ã§ã‚‚è¨å®šã§ãã‚‹ã§ã€‚" fileIdOrUrl: "ファイルIDã‹URL" behavior: "動作" sample: "サンプル" @@ -636,7 +643,7 @@ abuseReported: "無事内容ãŒé€ä¿¡ã•ã‚ŒãŸã¿ãŸã„ã‚„ã§ã€‚ãŠãŠãã«ã€œ reporter: "é€šå ±è€…" reporteeOrigin: "é€šå ±å…ˆ" reporterOrigin: "é€šå ±å…ƒ" -forwardReport: "リモートインスタンスã«é€šå ±ã‚’転é€ã™ã‚‹ã§" +forwardReport: "リモートサーãƒãƒ¼ã«é€šå ±ã‚’転é€ã™ã‚‹ã§" forwardReportIsAnonymous: "リモートインスタンスã‹ã‚‰ã¯ã‚ã‚“ãŸã®æƒ…å ±ã¯è¦‹ã‚Œã¸ã‚“ãã£ã¦ã€åŒ¿åã®ã‚·ã‚¹ãƒ†ãƒ アカウントã¨ã—ã¦è¡¨ç¤ºã•ã‚Œã‚‹ã§ã€‚" send: "é€ä¿¡" abuseMarkAsResolved: "対応ã—ãŸã§" @@ -644,7 +651,7 @@ openInNewTab: "æ–°ã—ã„タブã§é–‹ã" openInSideView: "サイドビューã§é–‹ã" defaultNavigationBehaviour: "デフォルトã®ãƒŠãƒ“ゲーション" editTheseSettingsMayBreakAccount: "ã“ã®ã¸ã‚“ã®è¨å®šã‚’よã†ã‚ã‹ã‚‰ã‚“ã¾ã¾ã‚¤ã‚¸ã‚‹ã¨ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒå£Šã‚Œã¦ä½¿ãˆã‚“ããªã‚‹ã‹ã‚‚知れã¸ã‚“ã§ï¼Ÿ" -instanceTicker: "ノートã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹æƒ…å ±" +instanceTicker: "ノートã®ã‚µãƒ¼ãƒãƒ¼æƒ…å ±" waitingFor: "{x}ã‚’å¾…ã£ã¨ã‚‹ã§" random: "ランダム" system: "システム" @@ -655,7 +662,7 @@ createNew: "æ–°ã—ã作るã§" optional: "ä»»æ„" createNewClip: "æ–°ã—ã„クリップを作るã§" unclip: "クリップ解除ã™ã‚‹ã§" -confirmToUnclipAlreadyClippedNote: "ã“ã®ãƒŽãƒ¼ãƒˆã¯ã™ã§ã«ã‚¯ãƒªãƒƒãƒ—「{name}ã€ã«å«ã¾ã‚Œã¨ã‚‹ã§ã€‚ノートをã“ã®ã‚¯ãƒªãƒƒãƒ—ã‹ã‚‰é™¤å¤–ã—ãŸã‚‹ï¼Ÿ" +confirmToUnclipAlreadyClippedNote: "ã“ã®ãƒŽãƒ¼ãƒˆã¯ã™ã§ã«ã‚¯ãƒªãƒƒãƒ—「{name}ã€ã«å«ã¾ã‚Œã¨ã‚‹ã§ã€‚ノートをã“ã®ã‚¯ãƒªãƒƒãƒ—ã‹ã‚‰é™¤å¤–ã—よã‹ï¼Ÿ" public: "パブリック" i18nInfo: "Misskeyã¯æœ‰å¿—ã«ã‚ˆã£ã¦ã„ã‚ã‚“ãªè¨€èªžã«ç¿»è¨³ã•ã‚Œã¨ã‚‹ã§ã€‚{link}ã§ç¿»è¨³ã«å”力ã—ãŸã£ã¦ã‚„ー。" manageAccessTokens: "アクセストークンã®ç®¡ç†" @@ -672,15 +679,15 @@ receivedReactionsCount: "リアクションã•ã‚ŒãŸæ•°" pollVotesCount: "アンケートã«æŠ•ç¥¨ã—ãŸæ•°" pollVotedCount: "アンケートã«æŠ•ç¥¨ã•ã‚ŒãŸæ•°" yes: "ãˆãˆã§" -no: "ã‚ã‹ã‚“ã§" +no: "ã‚ã‹ã‚“" driveFilesCount: "ドライブã®ãƒ•ã‚¡ã‚¤ãƒ«æ•°" driveUsage: "ドライブ使用é‡ã‚„ã§" noCrawle: "クãƒãƒ¼ãƒ©ãƒ¼ã«ã‚ˆã‚‹ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã‚’æ‹’å¦ã™ã‚‹ã§" -noCrawleDescription: "検索エンジンã«ã‚ã‚“ãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒšãƒ¼ã‚¸ã€ãƒŽãƒ¼ãƒˆã€Pagesã¨ã‹ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を登録(インデックス)ã›ã‡ã¸ã‚“よã†ã«é ¼ã‚€ã§ã€‚" +noCrawleDescription: "検索エンジンã«ã‚ã‚“ãŸã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ãƒšãƒ¼ã‚¸ã€ãƒŽãƒ¼ãƒˆã€Pagesã¨ã‹ã®ã‚³ãƒ³ãƒ†ãƒ³ãƒ„を登録(インデックス)ã›ã‚“よã†ã«é ¼ã‚€ã§ã€‚邪é”ã™ã‚“ãã‚“ã‚„ã£ãŸã‚‰å¸°ã£ã¦ã€œã€‚" lockedAccountInfo: "フォãƒãƒ¼ã‚’承èªåˆ¶ã«ã—ã¨ã£ã¦ã‚‚ã€ãƒŽãƒ¼ãƒˆã®å…¬é–‹ç¯„囲を「フォãƒãƒ¯ãƒ¼ã€ã«ã›ã‡ã¸ã‚“é™ã‚Šã€èª°ã§ã‚‚ã‚ã‚“ãŸã®ãƒŽãƒ¼ãƒˆã‚’見れるã§ã€‚" alwaysMarkSensitive: "デフォルトã§ãƒ¡ãƒ‡ã‚£ã‚¢ã‚’閲覧注æ„ã«ã™ã‚‹ã§" loadRawImages: "添付画åƒã®ã‚µãƒ ãƒã‚¤ãƒ«ã‚’オリジナル画質ã«ã™ã‚‹ã§" -disableShowingAnimatedImages: "アニメーション画åƒã‚’å†ç”Ÿã—ã‚„ã¸ã‚“ã§" +disableShowingAnimatedImages: "アニメーション画åƒã‚’å†ç”Ÿã›ã‚“ã¨ãã§" verificationEmailSent: "無事確èªã®ãƒ¡ãƒ¼ãƒ«ã‚’é€ã‚ŒãŸã§ã€‚メールã«æ›¸ã„ã¦ã‚るリンクã«ã‚¢ã‚¯ã‚»ã‚¹ã—ã¦ã€è¨å®šã‚’完了ã—ã¦ãªãƒ¼ã€‚" notSet: "未è¨å®š" emailVerified: "メールアドレスã¯ç¢ºèªã•ã‚ŒãŸã§" @@ -690,14 +697,14 @@ pageLikedCount: "Pageã«ãˆãˆã‚„ã‚“ã¨æ€ã£ã¦ãã‚ŒãŸæ•°" contact: "連絡先" useSystemFont: "システムã®ãƒ‡ãƒ•ã‚©ãƒ«ãƒˆã®ãƒ•ã‚©ãƒ³ãƒˆã‚’使ã†ã§" clips: "クリップ" -experimentalFeatures: "実験的機能やã§" +experimentalFeatures: "ãŠãŸã‚ã—機能やã§" developer: "開発者やã§" makeExplorable: "アカウントを見ã¤ã‘ã‚„ã™ãã™ã‚‹ã§" makeExplorableDescription: "オフã«ã™ã‚‹ã¨ã€ã€Œã¿ã¤ã‘ã‚‹ã€ã«ã‚¢ã‚«ã‚¦ãƒ³ãƒˆãŒè¼‰ã‚‰ã‚“ããªã‚‹ã§ã€‚" showGapBetweenNotesInTimeline: "タイムラインã®ãƒŽãƒ¼ãƒˆã‚’離ã—ã¦è¡¨ç¤ºã™ã‚‹ã§" duplicate: "複製" left: "å·¦" -center: "ä¸å¤®" +center: "真んä¸" wide: "広ã„" narrow: "ç‹ã„" reloadToApplySetting: "è¨å®šã¯ãƒšãƒ¼ã‚¸ãƒªãƒãƒ¼ãƒ‰å¾Œã«åæ˜ ã•ã‚Œã‚‹ã§ã€‚今リãƒãƒ¼ãƒ‰ã—ã¨ãã‹ï¼Ÿ" @@ -708,7 +715,7 @@ onlineUsersCount: "{n}人ãŒèµ·ãã¨ã‚‹ã§" nUsers: "{n}ユーザー" nNotes: "{n}ノート" sendErrorReports: "エラーリãƒãƒ¼ãƒˆã‚’é€ã‚‹" -sendErrorReportsDescription: "オンã«ã—ãŸã‚‰ã€ãªã‚“ã‹å¤‰ãªã“ã¨ãŒèµ·ããŸã¨ãã«ã‚¨ãƒ©ãƒ¼ã®è©³ç´°ãŒMisskeyã«å…±æœ‰ã•ã‚Œã¦ã€ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®å“質å‘上ã«å½¹ç«‹ã¦ã‚‰ã‚Œã‚‹ã‚“ã‚„ã€‚ã‚¨ãƒ©ãƒ¼æƒ…å ±ã«ã¯ã€OSã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ãƒ–ラウザã®ç¨®é¡žã€è¡Œå‹•å±¥æ´ãªã©ãŒå«ã¾ã‚Œã‚‹ã§ã€‚" +sendErrorReportsDescription: "オンã«ã—ãŸã‚‰ã€å¤‰ãªã“ã¨ãŒèµ·ããŸã¨ãã«ã‚¨ãƒ©ãƒ¼ã®è©³ç´°ãŒMisskeyã«é€ã‚‰ã‚Œã¦ã€ã‚½ãƒ•ãƒˆã‚¦ã‚§ã‚¢ã®å“質å‘上ã«ä½¿ãˆã‚‹ã‚ˆã†ã«ãªã‚‹ã§ã€‚ã‚¨ãƒ©ãƒ¼æƒ…å ±ã«ã¯ã€OSã®ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã€ãƒ–ラウザã®ç¨®é¡žã€è¡Œå‹•å±¥æ´ãªã‚“ã‹ãŒå«ã¾ã‚Œã‚‹ã§ã€‚" myTheme: "マイテーマ" backgroundColor: "背景" accentColor: "アクセント" @@ -877,7 +884,7 @@ isSystemAccount: "システムãŒè‡ªå‹•ã§ä½œæˆãƒ»ç®¡ç†ã—ã¨ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ typeToConfirm: "ã“ã®æ“作をやるんãªã‚‰ {x} ã¨å…¥åŠ›ã—ã¦ãªãƒ¼" deleteAccount: "アカウント削除ã™ã‚‹ã§" document: "ドã‚ュメント" -numberOfPageCache: "ページã‚ャッシュ数やã§" +numberOfPageCache: "ページã€ã©ã‚“ã ã‘ã‚ャッシュã™ã‚“ã®ï¼Ÿ" numberOfPageCacheDescription: "増やã™ã¨ä½¿ã„ã‚„ã™ããªã‚‹ã€è² è·ã¨ãƒ¡ãƒ¢ãƒªä½¿ç”¨é‡ãŒå¢—ãˆã¦ãã§ã€‚一長一çŸã‚„ãªã€‚" logoutConfirm: "ãƒã‚°ã‚¢ã‚¦ãƒˆã—ã¾ã£ã‹ï¼Ÿ" lastActiveDate: "最後ã«ä½¿ã£ãŸæ—¥æ™‚" @@ -947,7 +954,7 @@ thisPostMayBeAnnoying: "ã“ã®æŠ•ç¨¿ã¯è¿·æƒ‘ã‹ã‚‚ã—らんã§ã€‚" thisPostMayBeAnnoyingHome: "ホームã«æŠ•ç¨¿" thisPostMayBeAnnoyingCancel: "ã‚„ã‚ã¨ã" thisPostMayBeAnnoyingIgnore: "ã“ã®ã¾ã¾æŠ•ç¨¿" -collapseRenotes: "見ãŸã“ã¨ã‚ã‚‹Renoteã¯çœç•¥ã‚„ã§" +collapseRenotes: "見ãŸã“ã¨ã‚ã‚‹Renoteã¯é£›ã°ã—ã¦è¡¨ç¤ºã™ã‚‹ã§" internalServerError: "サーãƒãƒ¼å†…部エラー" internalServerErrorDescription: "サーãƒãƒ¼å†…部ã§ã‚ˆã†åˆ†ã‹ã‚‰ã‚“エラーやã‚" copyErrorInfo: "ã‚¨ãƒ©ãƒ¼æƒ…å ±ã‚’ã‚³ãƒ”ãƒ¼" @@ -959,6 +966,17 @@ invitationRequiredToRegister: "今ã“ã®ã‚µãƒ¼ãƒãƒ¼æ‹›å¾…制ã«ãªã£ã¦ã‚‚ㆠemailNotSupported: "ã“ã®ã‚µãƒ¼ãƒãƒ¼ã¯ãƒ¡ãƒ¼ãƒ«é…ä¿¡ãŒã‚µãƒãƒ¼ãƒˆã•ã‚Œã¦ã¸ã‚“ã¿ãŸã„ã‚„ã‚" postToTheChannel: "ãƒãƒ£ãƒ³ãƒãƒ«ã«æŠ•ç¨¿" cannotBeChangedLater: "後ã‹ã‚‰ã¯å¤‰ãˆã‚‰ã‚Œã¸ã‚“ã§ã€‚" +reactionAcceptance: "リアクションã®å—ã‘入れ" +likeOnly: "ã„ã„ãã ã‘" +likeOnlyForRemote: "リモートã‹ã‚‰ã¯ã„ã„ãã ã‘ãª" +rolesAssignedToMe: "自分ã«å‰²ã‚Šå½“ã¦ã‚‰ã‚ŒãŸãƒãƒ¼ãƒ«" +resetPasswordConfirm: "パスワード作り直ã™ã‚“ã§ãˆãˆãªï¼Ÿ" +sensitiveWords: "ã‘ã£ãŸã„ãªå˜èªž" +sensitiveWordsDescription: "è¨å®šã—ãŸå˜èªžãŒå…¥ã£ã¨ã‚‹ãƒŽãƒ¼ãƒˆã®å…¬é–‹ç¯„囲をホームã«ã—ãŸã‚‹ã‚。改行ã§åŒºåˆ‡ã£ãŸã‚‰è¤‡æ•°è¨å®šã§ãã‚‹ã§ã€‚" +notesSearchNotAvailable: "ノート検索ã¯ä½¿ã‚ã‚Œã¸ã‚“ã§ã€‚" +license: "ライセンス" +unfavoriteConfirm: "ã»ã‚“ã¾ã«æ°—ã«å…¥ã‚‰ã‚“ã®ï¼Ÿ" +myClips: "自分ã®ã‚¯ãƒªãƒƒãƒ—" _achievements: earnedAt: "è²°ã£ãŸæ—¥ãƒ" _types: @@ -976,7 +994,7 @@ _achievements: title: "ノートã®ç”Ÿé§’å±±" description: "ノートを500回投稿ã—ãŸ" _notes1000: - title: "ノートã®å±±" + title: "ノートã®å…甲山" description: "ノートを1,000回投稿ã—ãŸ" _notes5000: title: "箕é¢ã®æ»ã‹ã‚‰ãƒŽãƒ¼ãƒˆ" @@ -1218,6 +1236,8 @@ _role: iconUrl: "アイコン画åƒã®URL" asBadge: "ãƒãƒƒã‚¸ã¨ã—ã¦è¦‹ã›ã‚‹" descriptionOfAsBadge: "オンã«ã™ã‚‹ã¨ã€ãƒ¦ãƒ¼ã‚¶ãƒ¼åã®æ¨ªã‚“ã¨ã“ã«ãƒãƒ¼ãƒ«ã®ã‚¢ã‚¤ã‚³ãƒ³ãŒè¡¨ç¤ºã•ã‚Œã‚‹ã§ã€‚" + displayOrder: "è¡¨ç¤ºé †" + descriptionOfDisplayOrder: "æ•°ãŒã§ã‹ã„ã»ã©ã€UI上ã§å…ˆã«è¡¨ç¤ºã•ã‚Œã‚‹ã§ã€‚" canEditMembersByModerator: "モデレーターã®ãƒ¡ãƒ³ãƒãƒ¼ç·¨é›†ã‚’許å¯" descriptionOfCanEditMembersByModerator: "オンã«ã™ã‚‹ã¨ã€ç®¡ç†è€…ã«åŠ ãˆã¦ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚¿ãƒ¼ã‚‚ã“ã®ãƒãƒ¼ãƒ«ã¸ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‚’アサイン/アサイン解除ã§ãるよã†ã«ãªã‚‹ã§ã€‚オフã«ã™ã‚‹ã¨ç®¡ç†è€…ã®ã¿ãŒè¡Œãˆã‚‹ã§ã€‚" priority: "優先度" @@ -1243,6 +1263,7 @@ _role: rateLimitFactor: "レートリミット" descriptionOfRateLimitFactor: "ã¡ã£ã¡ã‚ƒã„ã»ã©åˆ¶é™ãŒç·©ããªã£ã¦ã€å¤§ãã„ã»ã©åˆ¶é™ã•ã‚Œã‚‹ã§ã€‚" canHideAds: "広告を表示ã•ã›ã¸ã‚“" + canSearchNotes: "ノート検索を使ã‚ã™ã‹ã©ã†ã‹" _condition: isLocal: "ãƒãƒ¼ã‚«ãƒ«ãƒ¦ãƒ¼ã‚¶ãƒ¼" isRemote: "リモートユーザー" @@ -1377,7 +1398,7 @@ _wordMute: mutedNotes: "ミュートã•ã‚ŒãŸãƒŽãƒ¼ãƒˆ" _instanceMute: instanceMuteDescription: "ミュートã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®ãƒ¦ãƒ¼ã‚¶ãƒ¼ã¸ã®è¿”ä¿¡ã‚’å«ã‚ã¦ã€è¨å®šã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®å…¨ã¦ã®ãƒŽãƒ¼ãƒˆã¨Renoteをミュートã«ã™ã‚‹ã§ã€‚" - instanceMuteDescription2: "改行ã§åŒºåˆ‡ã£ã¦è¨å®šã™ã‚‹ã§" + instanceMuteDescription2: "改行ã§åŒºåˆ‡ã£ã¦è¨å®šã™ã‚‹ã‚“ã‚„ã§" title: "è¨å®šã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã®ãƒŽãƒ¼ãƒˆã‚’éš ã™ã§ã€‚" heading: "ミュートã™ã‚‹ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹" _theme: @@ -1464,7 +1485,7 @@ _sfx: channel: "ãƒãƒ£ãƒ³ãƒãƒ«é€šçŸ¥" _ago: future: "未æ¥" - justNow: "ãŸã£ãŸä»Š" + justNow: "ã¤ã„ã•ã£ã" secondsAgo: "{n}秒å‰" minutesAgo: "{n}分å‰" hoursAgo: "{n}時間å‰" @@ -1587,7 +1608,7 @@ _weekday: saturday: "土曜日" _widgets: profile: "プãƒãƒ•ã‚£ãƒ¼ãƒ«" - instanceInfo: "ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹æƒ…å ±" + instanceInfo: "サーãƒãƒ¼æƒ…å ±" memo: "付箋" notifications: "通知" timeline: "タイムライン" @@ -1690,7 +1711,7 @@ _charts: apRequest: "リクエスト" usersIncDec: "ユーザーã®å¢—減" usersTotal: "ユーザーã®åˆè¨ˆ" - activeUsers: "アクティブユーザー数" + activeUsers: "ã„ã¾ãŠã‚‹ãƒ¦ãƒ¼ã‚¶ãƒ¼æ•°" notesIncDec: "ノートã®å¢—減" localNotesIncDec: "ãƒãƒ¼ã‚«ãƒ«ã®ãƒŽãƒ¼ãƒˆã®å¢—減" remoteNotesIncDec: "リモートã®ãƒŽãƒ¼ãƒˆã®å¢—減" @@ -1844,3 +1865,6 @@ _deck: _dialog: charactersExceeded: "最大ã®æ–‡å—数を上回ã£ã¨ã‚‹ã§ï¼ä»Šã¯ {current} / 最大ã§ã‚‚ {max}" charactersBelow: "最å°ã®æ–‡å—数を下回ã£ã¨ã‚‹ã§ï¼ä»Šã¯ {current} / 最低ã§ã‚‚ {min}" +_disabledTimeline: + title: "使ã‚ã‚Œã¸ã‚“タイムライン" + description: "ã‚ã‚“ãŸã®ä»Šã®ãƒãƒ¼ãƒ«ã‚„ã£ãŸã‚‰ã€ã“ã®ã‚¿ã‚¤ãƒ ラインã¯ä½¿ã‚ã‚Œã¸ã‚“ã§ã€‚" diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml index 9115afe5a53218d857413b49cb4d21a74e13d77f..e52d619f8c65a43fff7819180d58a2b0386d5bce 100644 --- a/locales/ko-KR.yml +++ b/locales/ko-KR.yml @@ -122,6 +122,8 @@ unmarkAsSensitive: "ì—´ëžŒì£¼ì˜ í•´ì œ" enterFileName: "파ì¼ëª…ì„ ìž…ë ¥" mute: "뮤트" unmute: "뮤트 í•´ì œ" +renoteMute: "리노트를 뮤트" +renoteUnmute: "리노트 뮤트 í•´ì œ" block: "차단" unblock: "차단 í•´ì œ" suspend: "ì •ì§€" @@ -153,6 +155,7 @@ flagShowTimelineReplies: "타임ë¼ì¸ì— ë…¸íŠ¸ì˜ ë‹µê¸€ì„ í‘œì‹œí•˜ê¸°" flagShowTimelineRepliesDescription: "ì´ ì„¤ì •ì„ í™œì„±í™”í•˜ë©´ 타임ë¼ì¸ì— 다른 ìœ ì € ê°„ì˜ ë‹µê¸€ì„ í‘œì‹œí•©ë‹ˆë‹¤." autoAcceptFollowed: "팔로우 ì¤‘ì¸ ìœ ì €ë¡œë¶€í„°ì˜ íŒ”ë¡œìš° ìš”ì²ì„ ìžë™ 수ë½" addAccount: "ê³„ì • 추가" +reloadAccountsList: "ê³„ì • 리스트 ì •ë³´ ê°±ì‹ " loginFailed: "로그ì¸ì— 실패했습니다" showOnRemote: "리모트ì—ì„œ 보기" general: "ì¼ë°˜" @@ -506,6 +509,7 @@ objectStorageSetPublicRead: "ì—…ë¡œë“œí• ë•Œ 'public-read'를 ì„¤ì •í•˜ê¸°" serverLogs: "서버 로그" deleteAll: "ëª¨ë‘ ì‚ì œ" showFixedPostForm: "타임ë¼ì¸ ìƒë‹¨ì— 글 ìž‘ì„±ëž€ì„ í‘œì‹œ" +showFixedPostFormInChannel: "ì±„ë„ íƒ€ìž„ë¼ì¸ ìƒë‹¨ì— 글 ìž‘ì„±ëž€ì„ í‘œì‹œ" newNoteRecived: "새 노트가 있습니다" sounds: "소리" sound: "소리" @@ -543,6 +547,8 @@ userSuspended: "ì´ ê³„ì •ì€ ì •ì§€ëœ ìƒíƒœìž…니다." userSilenced: "ì´ ê³„ì •ì€ ì‚¬ì¼ëŸ°ìŠ¤ëœ ìƒíƒœìž…니다." yourAccountSuspendedTitle: "ê³„ì •ì´ ì •ì§€ë˜ì—ˆìŠµë‹ˆë‹¤" yourAccountSuspendedDescription: "ì´ ê³„ì •ì€ ì„œë²„ì˜ ì´ìš© ì•½ê´€ì„ ìœ„ë°˜í•˜ê±°ë‚˜, 기타 다른 ì´ìœ ë¡œ ì¸í•´ ì •ì§€ë˜ì—ˆìŠµë‹ˆë‹¤. ìžì„¸í•œ 사í•ì€ 관리ìžì—게 문ì˜í•´ 주ì‹ì‹œì˜¤. ê³„ì •ì„ ìƒˆë¡œ ìƒì„±í•˜ì§€ 마ì‹ì‹œì˜¤." +accountDeleted: "ê³„ì •ì´ ì •ì§€ë˜ì—ˆìŠµë‹ˆë‹¤" +accountDeletedDescription: "ì´ ê³„ì •ì´ ì‚ì œë˜ì—ˆìŠµë‹ˆë‹¤." menu: "메뉴" divider: "êµ¬ë¶„ì„ " addItem: "í•ëª© 추가" @@ -955,6 +961,9 @@ exploreOtherServers: "다른 서버 둘러보기" letsLookAtTimeline: "타임ë¼ì¸ 구경하기" disableFederationWarn: "ì—°í•©ì´ ë¹„í™œì„±í™”ë©ë‹ˆë‹¤. ë¹„í™œì„±í™”í•´ë„ ê²Œì‹œë¬¼ì´ ë¹„ê³µê°œê°€ ë˜ì§€ëŠ” 않습니다. ëŒ€ë¶€ë¶„ì˜ ê²½ìš° ì´ ì˜µì…˜ì„ í™œì„±í™”í• í•„ìš”ê°€ 없습니다." invitationRequiredToRegister: "현재 ì´ ì„œë²„ëŠ” 비공개입니다. 회ì›ê°€ìž…ì„ í•˜ì‹œë ¤ë©´ 초대 코드가 필요합니다." +emailNotSupported: "ì´ ì„œë²„ì—서는 ë©”ì¼ ì „ì†¡ì„ ì§€ì›í•˜ì§€ 않습니다" +postToTheChannel: "채ë„ì— ê²Œì‹œí•˜ê¸°" +cannotBeChangedLater: "ë‚˜ì¤‘ì— ë³€ê²½í• ìˆ˜ 없습니다." _achievements: earnedAt: "달성 ì¼ì‹œ" _types: diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml index 7798582db8d36c741c8a6bbc2f7e2e0c0cf26c1f..73b36f8ec2c67295388d120a940912e0525e4610 100644 --- a/locales/zh-CN.yml +++ b/locales/zh-CN.yml @@ -2,7 +2,7 @@ _lang_: "ä¸æ–‡(简体)" headlineMisskey: "通过帖å连接在一起的网络" introMisskey: "欢迎ï¼Misskey是一个开æºçš„ã€åŽ»ä¸å¿ƒåŒ–的“微åšå®¢â€æœåŠ¡ã€‚\n通过编写「帖文ã€æ¥å’Œå¤§å®¶åˆ†äº«ä½ 的以åŠä½ 周围的事情å§ï¼ðŸ“¡\n通过「回应ã€åŠŸèƒ½ï¼Œå¯ä»¥è®©ä½ 快速地对大家的帖文表达å馈ðŸ‘\næ¥æŽ¢ç´¢æ–°çš„世界å§ï¼ðŸš€" -poweredByMisskeyDescription: "{name} 由开æºå¹³å° <b>Misskey</b> 驱动(也被称为 Misskey æœåŠ¡å™¨ï¼‰" +poweredByMisskeyDescription: "{name} 是开æºå¹³å° <b>Misskey</b> çš„æœåŠ¡å™¨ä¹‹ä¸€ã€‚" monthAndDay: "{month}月 {day}æ—¥" search: "æœç´¢" notifications: "通知" @@ -122,6 +122,8 @@ unmarkAsSensitive: "å–æ¶ˆæ ‡è®°ä¸ºæ•æ„Ÿå†…容" enterFileName: "请输入文件å" mute: "å±è”½" unmute: "解除å±è”½" +renoteMute: "å±è”½è½¬å¸–" +renoteUnmute: "解除å±è”½è½¬å¸–" block: "拉黑" unblock: "å–消拉黑" suspend: "冻结" @@ -153,6 +155,7 @@ flagShowTimelineReplies: "在时间线上显示帖å的回å¤" flagShowTimelineRepliesDescription: "å¯ç”¨æ—¶ï¼Œæ—¶é—´çº¿é™¤äº†æ˜¾ç¤ºç”¨æˆ·çš„帖å外,还会显示其他用户对帖å的回å¤ã€‚" autoAcceptFollowed: "自动å…许关注者的关注" addAccount: "æ·»åŠ è´¦æˆ·" +reloadAccountsList: "更新账户列表" loginFailed: "登录失败" showOnRemote: "转到所在æœåŠ¡å™¨æ˜¾ç¤º" general: "常规设置" @@ -355,7 +358,7 @@ hcaptchaSecretKey: "hCaptcha 密钥(SecretKey)" recaptcha: "reCAPTCHA" enableRecaptcha: "å¯ç”¨ reCAPTCHA\n(请注æ„, æ¤åŠŸèƒ½åœ¨ä¸å›½å¤§é™†ä¸å¯ç”¨. 如果å¯ç”¨, å¯èƒ½å¯¼è‡´æ— 法æ£å¸¸ä½¿ç”¨ç™»å½•æˆ–注册ç‰åŠŸèƒ½)" recaptchaSiteKey: "网站密钥" -recaptchaSecretKey: "reCAPTCHA 密钥" +recaptchaSecretKey: "reCAPTCHA 密钥(SecretKey)" turnstile: "Turnstile" enableTurnstile: "å¯ç”¨Turnstile" turnstileSiteKey: "网站密钥" @@ -489,7 +492,7 @@ showFeaturedNotesInTimeline: "在时间线上显示çƒé—¨æŽ¨è" objectStorage: "对象å˜å‚¨" useObjectStorage: "使用对象å˜å‚¨" objectStorageBaseUrl: "Base URL" -objectStorageBaseUrlDesc: "用于引用的URL。如果您æ£åœ¨ä½¿ç”¨CDN或åå‘代ç†ï¼Œè¯·æŒ‡å®šå…¶URL,例如S3:“https://<bucket>.s3.amazonaws.comâ€ï¼ŒGCS:“https://storage.googleapis.com/<bucket>â€" +objectStorageBaseUrlDesc: "这里是用于å‚考的URL,如果您æ£åœ¨ä½¿ç”¨CDN或åå‘代ç†ï¼Œè¯·æŒ‡å®šå…¶URL,例如S3:“https://<bucket>.s3.amazonaws.comâ€ï¼ŒGCS:“https://storage.googleapis.com/<bucket>â€" objectStorageBucket: "å˜å‚¨æ¡¶" objectStorageBucketDesc: "请指定使用的对象å˜å‚¨æœåŠ¡çš„å˜å‚¨æ¡¶å称。" objectStoragePrefix: "å‰ç¼€" @@ -544,6 +547,10 @@ userSuspended: "该用户已被冻结。" userSilenced: "该用户已被ç¦è¨€ã€‚" yourAccountSuspendedTitle: "账户已被冻结" yourAccountSuspendedDescription: "由于è¿å了æœåŠ¡å™¨çš„æœåŠ¡æ¡æ¬¾æˆ–å…¶ä»–åŽŸå› ï¼Œè¯¥è´¦æˆ·å·²è¢«å†»ç»“ã€‚ 您å¯ä»¥ä¸Žç®¡ç†å‘˜è”系以了解更多信æ¯ã€‚ 请ä¸è¦åˆ›å»ºä¸€ä¸ªæ–°çš„账户。" +tokenRevoked: "ä»¤ç‰Œæ— æ•ˆ" +tokenRevokedDescription: "登录令牌已ç»å¤±æ•ˆã€‚请é‡æ–°ç™»å½•ã€‚" +accountDeleted: "å¸æˆ·å·²åˆ 除" +accountDeletedDescription: "æ¤å¸æˆ·å·²ç»è¢«åˆ 除。" menu: "èœå•" divider: "分割线" addItem: "æ·»åŠ é¡¹ç›®" @@ -959,6 +966,17 @@ invitationRequiredToRegister: "æ¤æœåŠ¡å™¨ç›®å‰åªå…许拥有邀请ç 的人 emailNotSupported: "æ¤æœåŠ¡å™¨ä¸æ”¯æŒå‘é€é‚®ä»¶" postToTheChannel: "å‘布到频é“" cannotBeChangedLater: "之åŽä¸èƒ½å†æ›´æ”¹ã€‚" +reactionAcceptance: "接å—表情回应" +likeOnly: "仅点赞" +likeOnlyForRemote: "远程仅点赞" +rolesAssignedToMe: "指派给自己的角色" +resetPasswordConfirm: "确定é‡ç½®å¯†ç ?" +sensitiveWords: "æ•æ„Ÿè¯" +sensitiveWordsDescription: "将包å«è®¾ç½®è¯çš„帖åçš„å¯è§èŒƒå›´è®¾ç½®ä¸ºé¦–页。å¯ä»¥é€šè¿‡ç”¨æ¢è¡Œç¬¦åˆ†éš”æ¥è®¾ç½®å¤šä¸ªã€‚" +notesSearchNotAvailable: "帖å检索ä¸å¯ç”¨" +license: "许å¯ä¿¡æ¯" +unfavoriteConfirm: "确定è¦å–消收è—å—?" +myClips: "我的便ç¾" _achievements: earnedAt: "è¾¾æˆæ—¶é—´" _types: @@ -1218,6 +1236,8 @@ _role: iconUrl: "å›¾æ ‡URL" asBadge: "ä½œä¸ºå¾½ç« æ˜¾ç¤º" descriptionOfAsBadge: "å¼€å¯åŽï¼Œç”¨æˆ·åæ—è¾¹å°†ä¼šå‡ºçŽ°è§’è‰²å›¾æ ‡ã€‚" + displayOrder: "显示顺åº" + descriptionOfDisplayOrder: "æ•°å—越大,显示ä½ç½®è¶Šé å‰ã€‚" canEditMembersByModerator: "å…许监察者编辑æˆå‘˜" descriptionOfCanEditMembersByModerator: "如果选ä¸ï¼Œç›‘察者和管ç†å‘˜éƒ½èƒ½å¤Ÿä¸ºç”¨æˆ·åˆ†é…/å–消分é…角色。如果未选ä¸ï¼Œåˆ™åªæœ‰ç®¡ç†å‘˜å¯ä»¥æ‰§è¡Œæ¤æ“作。" priority: "优先级" @@ -1243,6 +1263,7 @@ _role: rateLimitFactor: "速率é™åˆ¶" descriptionOfRateLimitFactor: "值越å°é™åˆ¶è¶Šå°‘,值越大é™åˆ¶è¶Šå¤šã€‚" canHideAds: "å¯ä»¥éšè—广告" + canSearchNotes: "是å¦å¯ä»¥æœç´¢å¸–å" _condition: isLocal: "是本地用户" isRemote: "是远程用户" @@ -1517,7 +1538,7 @@ _2fa: step4: "从现在开始,任何登录æ“作都将è¦æ±‚您æ供动æ€å£ä»¤ã€‚" securityKeyNotSupported: "您的æµè§ˆå™¨ä¸æ”¯æŒå®‰å…¨å¯†é’¥ã€‚" registerTOTPBeforeKey: "è¦æ³¨å†Œå®‰å…¨å¯†é’¥æˆ–Passkey,请先设置验è¯å™¨åº”用程åºã€‚" - securityKeyInfo: "您å¯ä»¥è®¾ç½®ä½¿ç”¨æ”¯æŒFIDO2的硬件安全密钥ã€è®¾å¤‡ä¸Šçš„指纹或PINæ¥ä¿æŠ¤æ‚¨çš„登录过程。" + securityKeyInfo: "注册兼容 WebAuthn çš„å¯†é’¥ï¼Œä¾‹å¦‚æ”¯æŒ FIDO2 的硬件安全密钥ã€è®¾å¤‡ä¸Šçš„生物识别功能ã€PIN ç ä»¥åŠ Passkey ç‰ã€‚" chromePasskeyNotSupported: "ç›®å‰ä¸æ”¯æŒ Chrome çš„Passkey。" registerSecurityKey: "注册安全密钥或Passkey" securityKeyName: "输入密钥å称" @@ -1844,3 +1865,6 @@ _deck: _dialog: charactersExceeded: "å·²ç»è¶…过了最大å—符数! 当å‰å—符数 {current} / é™åˆ¶å—符数 {max}" charactersBelow: "低于最å°å—符数ï¼å½“å‰å—符数 {current} / é™åˆ¶å—符数 {min}" +_disabledTimeline: + title: "时间线已ç¦ç”¨" + description: "您ä¸èƒ½åœ¨å½“å‰è§’色使用时间线。" diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml index 5c1512bd48ba1aafedc8a5890a386d505977568a..6109bdbeec6c418806f420c5f6b1b6a08bba84f9 100644 --- a/locales/zh-TW.yml +++ b/locales/zh-TW.yml @@ -122,14 +122,16 @@ unmarkAsSensitive: "å–消標記為æ•æ„Ÿå…§å®¹" enterFileName: "請輸入檔案å稱" mute: "éœéŸ³" unmute: "解除éœéŸ³" +renoteMute: "將轉發貼文éœéŸ³" +renoteUnmute: "解除轉發貼文的éœéŸ³" block: "å°éŽ–" unblock: "解除å°éŽ–" suspend: "å‡çµ" unsuspend: "解除å‡çµ" blockConfirm: "確定è¦å°éŽ–æ¤ç”¨æˆ¶ï¼Ÿ" unblockConfirm: "確定解除å°éŽ–æ¤ç”¨æˆ¶ï¼Ÿ" -suspendConfirm: "確定å‡çµæ¤å¸³è™Ÿï¼Ÿ" -unsuspendConfirm: "確定解å‡æ¤å¸³è™Ÿï¼Ÿ" +suspendConfirm: "確定å‡çµæ¤å¸³æˆ¶ï¼Ÿ" +unsuspendConfirm: "確定解å‡æ¤å¸³æˆ¶ï¼Ÿ" selectList: "é¸æ“‡æ¸…å–®" selectChannel: "é¸æ“‡é »é“" selectAntenna: "é¸æ“‡å¤©ç·š" @@ -153,6 +155,7 @@ flagShowTimelineReplies: "在時間軸上顯示貼文的回覆" flagShowTimelineRepliesDescription: "啟用時,時間線除了顯示用戶的貼文以外,還會顯示用戶å°å…¶ä»–貼文的回覆。" autoAcceptFollowed: "自動追隨ä¸ä½¿ç”¨è€…的追隨請求" addAccount: "æ·»åŠ å¸³æˆ¶" +reloadAccountsList: "更新帳戶清單的資訊" loginFailed: "登入失敗" showOnRemote: "轉到所在實例顯示" general: "一般" @@ -169,7 +172,7 @@ selectUser: "é¸å–使用者" recipient: "收件人" annotation: "註解" federation: "ç«™å°è¯é‚¦" -instances: "實例" +instances: "伺æœå™¨" registeredAt: "åˆæ¬¡è§€æ¸¬" latestRequestReceivedAt: "上次收到的請求" latestStatus: "最後狀態" @@ -403,7 +406,7 @@ securityKeyAndPasskey: "安全金鑰・Passkey" securityKey: "安全金鑰" lastUsed: "上次使用" lastUsedAt: "最後使用:{t}" -unregister: "註銷帳號" +unregister: "註銷帳戶" passwordLessLogin: "è¨ç½®ç„¡å¯†ç¢¼ç™»å…¥" passwordLessLoginDescription: "ä¸ä½¿ç”¨å¯†ç¢¼ï¼Œä»¥å®‰å…¨é‡‘鑰或 Passkey 登入" resetPassword: "é‡ç½®å¯†ç¢¼" @@ -544,6 +547,10 @@ userSuspended: "該使用者已被åœç”¨" userSilenced: "該用戶已被ç¦è¨€ã€‚" yourAccountSuspendedTitle: "帳戶已被å‡çµ" yourAccountSuspendedDescription: "由於é•å了伺æœå™¨çš„æœå‹™æ¢æ¬¾æˆ–å…¶ä»–åŽŸå› ï¼Œè©²å¸³æˆ¶å·²è¢«å‡çµã€‚ 您å¯ä»¥èˆ‡ç®¡ç†å“¡é€£ç¹«ä»¥äº†è§£æ›´å¤šè¨Šæ¯ã€‚ è«‹ä¸è¦å‰µå»ºä¸€å€‹æ–°çš„帳戶。" +tokenRevoked: "權æ–無效" +tokenRevokedDescription: "登入權æ–失效,請é‡æ–°ç™»å…¥ã€‚" +accountDeleted: "帳戶已被刪除" +accountDeletedDescription: "這個帳戶已被刪除。" menu: "é¸å–®" divider: "分割線" addItem: "æ–°å¢žé …ç›®" @@ -872,10 +879,10 @@ recommended: "推薦" check: "檢查" driveCapOverrideLabel: "更改這個使用者的雲端硬碟容é‡ä¸Šé™" driveCapOverrideCaption: "如果指定0以下的值,就會被å–消。" -requireAdminForView: "å¿…é ˆä»¥ç®¡ç†å“¡å¸³è™Ÿç™»å…¥æ‰å¯ä»¥æª¢è¦–。" -isSystemAccount: "由系統自動建立與管ç†çš„帳號。" +requireAdminForView: "å¿…é ˆä»¥ç®¡ç†å“¡å¸³æˆ¶ç™»å…¥æ‰å¯ä»¥æª¢è¦–。" +isSystemAccount: "由系統自動建立與管ç†çš„帳戶。" typeToConfirm: "è¦åŸ·è¡Œé€™é …æ“作,請輸入 {x} " -deleteAccount: "刪除帳號" +deleteAccount: "刪除帳戶" document: "文件" numberOfPageCache: "å¿«å–é é¢æ•¸" numberOfPageCacheDescription: "å¢žåŠ æ•¸é‡æœƒæé«˜ä¾¿åˆ©æ€§ï¼Œä½†ä¹Ÿæœƒå¢žåŠ è² è·èˆ‡è¨˜æ†¶é«”使用é‡ã€‚" @@ -915,7 +922,7 @@ sendPushNotificationReadMessageCaption: "「{emptyPushNotificationMessage}ã€é€š windowMaximize: "最大化" windowRestore: "復原" caption: "標題" -loggedInAsBot: "以機器人帳號登入ä¸" +loggedInAsBot: "以機器人帳戶登入ä¸" tools: "工具" cannotLoad: "無法載入" numberOfProfileView: "個人檔案檢視次數" @@ -958,6 +965,14 @@ disableFederationWarn: "è¯é‚¦è¢«åœç”¨äº†ã€‚å³ä½¿åœç”¨ä¹Ÿä¸æœƒè®“您的貼 invitationRequiredToRegister: "ç›®å‰é€™å€‹ä¼ºæœå™¨ç‚ºé‚€è«‹åˆ¶ï¼Œå¿…é ˆæ“有邀請碼æ‰èƒ½è¨»å†Šã€‚" emailNotSupported: "這個伺æœå™¨ä¸æ”¯æ´å¯„é€éƒµä»¶" postToTheChannel: "ç™¼å¸ƒåˆ°é »é“" +cannotBeChangedLater: "之後ä¸èƒ½è®Šæ›´ã€‚" +reactionAcceptance: "接å—表情å應" +likeOnly: "僅é™è®š" +likeOnlyForRemote: "é 端僅é™è®š" +rolesAssignedToMe: "指派給自己的角色" +resetPasswordConfirm: "é‡è¨å¯†ç¢¼ï¼Ÿ" +sensitiveWords: "æ•æ„Ÿè©ž" +sensitiveWordsDescription: "å°‡å«æœ‰è¨å®šè©žå½™çš„貼文å¯è¦‹æ€§è¨ç‚ºç™¼é€è‡³é¦–é 。å¯ä»¥ç”¨æ›è¡Œä¾†é€²è¡Œè¤‡æ•¸çš„è¨å®šã€‚" _achievements: earnedAt: "ç²å¾—日期" _types: @@ -1217,6 +1232,8 @@ _role: iconUrl: "圖示的URL" asBadge: "é¡¯ç¤ºç‚ºå¾½ç« " descriptionOfAsBadge: "開啟的話,角色圖示會顯示在用戶åæ—邊。" + displayOrder: "é¡¯ç¤ºé †åº" + descriptionOfDisplayOrder: "數å—越大,顯示在UI上的越上é¢ã€‚" canEditMembersByModerator: "å…許編輯審查員的æˆå“¡" descriptionOfCanEditMembersByModerator: "如果開啟,管ç†å“¡èˆ‡å¯©æŸ¥å“¡éƒ½å¯ä»¥ç‚ºä½¿ç”¨è€…指派/解除指派該角色。如果關閉,則åªæœ‰ç®¡ç†å“¡å¯ä»¥åŸ·è¡Œã€‚" priority: "優先級" @@ -1242,6 +1259,7 @@ _role: rateLimitFactor: "速率é™åˆ¶" descriptionOfRateLimitFactor: "值越å°é™åˆ¶è¶Šå°‘,值越大é™åˆ¶è¶Šå¤šã€‚" canHideAds: "ä¸é¡¯ç¤ºå»£å‘Š" + canSearchNotes: "å¯å¦æœå°‹è²¼æ–‡" _condition: isLocal: "本地使用者" isRemote: "é 端使用者" @@ -1713,7 +1731,7 @@ _instanceCharts: _timelines: home: "首é " local: "本地" - social: "社群" + social: "社交" global: "公開" _play: new: "新增Play" @@ -1843,3 +1861,6 @@ _deck: _dialog: charactersExceeded: "已超éŽæœ€å¤§å—數ï¼ç¾åœ¨ {current} / é™åˆ¶ {max}" charactersBelow: "低於最少å—數ï¼ç¾åœ¨ {current} / é™åˆ¶ {max}" +_disabledTimeline: + title: "åœç”¨çš„時間軸" + description: "ç›®å‰çš„角色無法使用這個時間軸。" diff --git a/package.json b/package.json index c244692b649ba2db187f8e347e45c3eec0f9986d..f68608911cde4855fce9bc964719336a1d6a214b 100644 --- a/package.json +++ b/package.json @@ -1,12 +1,12 @@ { "name": "misskey", - "version": "13.9.2", + "version": "13.10.0", "codename": "nasubi", "repository": { "type": "git", "url": "https://github.com/misskey-dev/misskey.git" }, - "packageManager": "pnpm@7.27.0", + "packageManager": "pnpm@7.29.3", "workspaces": [ "packages/frontend", "packages/backend", diff --git a/packages/backend/migration/1675053125067-fixforeignkeyreports.js b/packages/backend/migration/1675053125067-fixforeignkeyreports.js new file mode 100644 index 0000000000000000000000000000000000000000..ca5c10b11faf4e4096ca7988db4249296206d6bd --- /dev/null +++ b/packages/backend/migration/1675053125067-fixforeignkeyreports.js @@ -0,0 +1,15 @@ +export class fixforeignkeyreports1675053125067 { + name = 'fixforeignkeyreports1675053125067' + + async up(queryRunner) { + await queryRunner.query(`CREATE INDEX IF NOT EXISTS "IDX_a9021cc2e1feb5f72d3db6e9f5" ON "abuse_user_report" ("targetUserId")`); + await queryRunner.query(`DELETE FROM "abuse_user_report" WHERE "targetUserId" NOT IN (SELECT "id" FROM "user")`); + await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT IF EXISTS "FK_a9021cc2e1feb5f72d3db6e9f5f"`); + await queryRunner.query(`ALTER TABLE "abuse_user_report" ADD CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f" FOREIGN KEY ("targetUserId") REFERENCES "user"("id") ON DELETE CASCADE ON UPDATE NO ACTION`); + } + + async down(queryRunner) { + await queryRunner.query(`DROP INDEX "public"."IDX_a9021cc2e1feb5f72d3db6e9f5"`); + await queryRunner.query(`ALTER TABLE "abuse_user_report" DROP CONSTRAINT "FK_a9021cc2e1feb5f72d3db6e9f5f"`); + } +} diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts index 76fb8f636be2f632a7736406cdaec7c6ecb8f7ba..516e90dcb376464943a023ad78f4dcb18aad76cb 100644 --- a/packages/backend/src/server/api/EndpointsModule.ts +++ b/packages/backend/src/server/api/EndpointsModule.ts @@ -223,6 +223,7 @@ import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js'; import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; +import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; import * as ep___mute_delete from './endpoints/mute/delete.js'; @@ -550,6 +551,7 @@ const $i_webhooks_update: Provider = { provide: 'ep:i/webhooks/update', useClass const $i_webhooks_delete: Provider = { provide: 'ep:i/webhooks/delete', useClass: ep___i_webhooks_delete.default }; const $meta: Provider = { provide: 'ep:meta', useClass: ep___meta.default }; const $emojis: Provider = { provide: 'ep:emojis', useClass: ep___emojis.default }; +const $emoji: Provider = { provide: 'ep:emoji', useClass: ep___emoji.default }; const $miauth_genToken: Provider = { provide: 'ep:miauth/gen-token', useClass: ep___miauth_genToken.default }; const $mute_create: Provider = { provide: 'ep:mute/create', useClass: ep___mute_create.default }; const $mute_delete: Provider = { provide: 'ep:mute/delete', useClass: ep___mute_delete.default }; @@ -881,6 +883,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_webhooks_delete, $meta, $emojis, + $emoji, $miauth_genToken, $mute_create, $mute_delete, @@ -1206,6 +1209,7 @@ const $retention: Provider = { provide: 'ep:retention', useClass: ep___retention $i_webhooks_delete, $meta, $emojis, + $emoji, $miauth_genToken, $mute_create, $mute_delete, diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts index e928b0c2b1d4a5426b078886e1bf9fc777c65fe3..2930468a2215a30da4d165cc4532bcd004c7a591 100644 --- a/packages/backend/src/server/api/endpoints.ts +++ b/packages/backend/src/server/api/endpoints.ts @@ -223,6 +223,7 @@ import * as ep___i_webhooks_update from './endpoints/i/webhooks/update.js'; import * as ep___i_webhooks_delete from './endpoints/i/webhooks/delete.js'; import * as ep___meta from './endpoints/meta.js'; import * as ep___emojis from './endpoints/emojis.js'; +import * as ep___emoji from './endpoints/emoji.js'; import * as ep___miauth_genToken from './endpoints/miauth/gen-token.js'; import * as ep___mute_create from './endpoints/mute/create.js'; import * as ep___mute_delete from './endpoints/mute/delete.js'; @@ -548,6 +549,7 @@ const eps = [ ['i/webhooks/delete', ep___i_webhooks_delete], ['meta', ep___meta], ['emojis', ep___emojis], + ['emoji', ep___emoji], ['miauth/gen-token', ep___miauth_genToken], ['mute/create', ep___mute_create], ['mute/delete', ep___mute_delete], diff --git a/packages/backend/src/server/api/endpoints/emoji.ts b/packages/backend/src/server/api/endpoints/emoji.ts new file mode 100644 index 0000000000000000000000000000000000000000..681d3e649eb6054e0b19a059b01b603f0e54cc7e --- /dev/null +++ b/packages/backend/src/server/api/endpoints/emoji.ts @@ -0,0 +1,56 @@ +import { IsNull } from 'typeorm'; +import { Inject, Injectable } from '@nestjs/common'; +import type { EmojisRepository } from '@/models/index.js'; +import { Endpoint } from '@/server/api/endpoint-base.js'; +import { EmojiEntityService } from '@/core/entities/EmojiEntityService.js'; +import type { Config } from '@/config.js'; +import { DI } from '@/di-symbols.js'; + +export const meta = { + tags: ['meta'], + + requireCredential: false, + allowGet: true, + cacheSec: 3600, + + res: { + type: 'object', + optional: false, nullable: false, + ref: 'EmojiDetailed', + }, +} as const; + +export const paramDef = { + type: 'object', + properties: { + name: { + type: 'string', + }, + }, + required: ['name'], +} as const; + +// eslint-disable-next-line import/no-default-export +@Injectable() +export default class extends Endpoint<typeof meta, typeof paramDef> { + constructor( + @Inject(DI.config) + private config: Config, + + @Inject(DI.emojisRepository) + private emojisRepository: EmojisRepository, + + private emojiEntityService: EmojiEntityService, + ) { + super(meta, paramDef, async (ps, me) => { + const emoji = await this.emojisRepository.findOneOrFail({ + where: { + name: ps.name, + host: IsNull(), + }, + }); + + return this.emojiEntityService.packDetailed(emoji); + }); + } +} diff --git a/packages/backend/src/server/api/endpoints/emojis.ts b/packages/backend/src/server/api/endpoints/emojis.ts index 325b7583581baf4a4c8ba3119997cb4c5a40bca7..0711fe4a57b60f0ad0278e6c4d0fd472b14d2e52 100644 --- a/packages/backend/src/server/api/endpoints/emojis.ts +++ b/packages/backend/src/server/api/endpoints/emojis.ts @@ -23,24 +23,7 @@ export const meta = { items: { type: 'object', optional: false, nullable: false, - properties: { - name: { - type: 'string', - optional: false, nullable: false, - }, - aliases: { - type: 'array', - optional: false, nullable: false, - items: { - type: 'string', - optional: false, nullable: false, - }, - }, - category: { - type: 'string', - optional: false, nullable: true, - }, - }, + ref: 'EmojiSimple', }, }, }, diff --git a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts index 3802ae540c8250041875ed8275f4ebd32719d8b1..8a7ec65ab43b1c59147c343cc72db5252207573a 100644 --- a/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/hybrid-timeline.ts @@ -8,6 +8,7 @@ import { MetaService } from '@/core/MetaService.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; +import { IdService } from '@/core/IdService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -69,6 +70,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private metaService: MetaService, private roleService: RoleService, private activeUsersChart: ActiveUsersChart, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const policies = await this.roleService.getUserPolicies(me.id); @@ -83,7 +85,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.createdAt > :minDate', { minDate: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) // 30æ—¥å‰ã¾ã§ + .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10æ—¥å‰ã¾ã§ .andWhere(new Brackets(qb => { qb.where(`((note.userId IN (${ followingQuery.getQuery() })) OR (note.userId = :meId))`, { meId: me.id }) .orWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)'); diff --git a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts index 3810016952f0ea605d48a7939be2732d46cce139..8c1c07a9f459b449025c73d0b204dab03119c94c 100644 --- a/packages/backend/src/server/api/endpoints/notes/local-timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/local-timeline.ts @@ -8,6 +8,7 @@ import { MetaService } from '@/core/MetaService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { DI } from '@/di-symbols.js'; import { RoleService } from '@/core/RoleService.js'; +import { IdService } from '@/core/IdService.js'; import { ApiError } from '../../error.js'; export const meta = { @@ -65,6 +66,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private metaService: MetaService, private roleService: RoleService, private activeUsersChart: ActiveUsersChart, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const policies = await this.roleService.getUserPolicies(me ? me.id : null); @@ -75,7 +77,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { //#region Construct query const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.createdAt > :minDate', { minDate: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) // 30æ—¥å‰ã¾ã§ + .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10æ—¥å‰ã¾ã§ .andWhere('(note.visibility = \'public\') AND (note.userHost IS NULL)') .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') diff --git a/packages/backend/src/server/api/endpoints/notes/timeline.ts b/packages/backend/src/server/api/endpoints/notes/timeline.ts index 5ce436ee1cc549ddb1f7fd83c91afb3bf0745bd7..d9e72d2603002ed4fbfad12380fe4783e3467d7a 100644 --- a/packages/backend/src/server/api/endpoints/notes/timeline.ts +++ b/packages/backend/src/server/api/endpoints/notes/timeline.ts @@ -6,6 +6,7 @@ import { QueryService } from '@/core/QueryService.js'; import ActiveUsersChart from '@/core/chart/charts/active-users.js'; import { NoteEntityService } from '@/core/entities/NoteEntityService.js'; import { DI } from '@/di-symbols.js'; +import { IdService } from '@/core/IdService.js'; export const meta = { tags: ['notes'], @@ -56,6 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { private noteEntityService: NoteEntityService, private queryService: QueryService, private activeUsersChart: ActiveUsersChart, + private idService: IdService, ) { super(meta, paramDef, async (ps, me) => { const followees = await this.followingsRepository.createQueryBuilder('following') @@ -66,7 +68,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { //#region Construct query const query = this.queryService.makePaginationQuery(this.notesRepository.createQueryBuilder('note'), ps.sinceId, ps.untilId, ps.sinceDate, ps.untilDate) - .andWhere('note.createdAt > :minDate', { minDate: new Date(Date.now() - (1000 * 60 * 60 * 24 * 30)) }) // 30æ—¥å‰ã¾ã§ + .andWhere('note.id > :minId', { minId: this.idService.genId(new Date(Date.now() - (1000 * 60 * 60 * 24 * 10))) }) // 10æ—¥å‰ã¾ã§ .innerJoinAndSelect('note.user', 'user') .leftJoinAndSelect('user.avatar', 'avatar') .leftJoinAndSelect('user.banner', 'banner') diff --git a/packages/backend/src/server/web/UrlPreviewService.ts b/packages/backend/src/server/web/UrlPreviewService.ts index 2ce7293a529a2883decdbddcf09631d838d17b37..21cf414087c364cf846cffa08a5263929c1bfe6d 100644 --- a/packages/backend/src/server/web/UrlPreviewService.ts +++ b/packages/backend/src/server/web/UrlPreviewService.ts @@ -19,9 +19,6 @@ export class UrlPreviewService { @Inject(DI.config) private config: Config, - @Inject(DI.usersRepository) - private usersRepository: UsersRepository, - private metaService: MetaService, private httpRequestService: HttpRequestService, private loggerService: LoggerService, @@ -51,15 +48,15 @@ export class UrlPreviewService { reply.code(400); return; } - + const lang = request.query.lang; if (Array.isArray(lang)) { reply.code(400); return; } - + const meta = await this.metaService.fetch(); - + this.logger.info(meta.summalyProxy ? `(Proxy) Getting preview of ${url}@${lang} ...` : `Getting preview of ${url}@${lang} ...`); @@ -85,16 +82,16 @@ export class UrlPreviewService { throw new Error('unsupported schema included'); } - if (summary.player?.url && !(summary.player.url.startsWith('http://') || summary.player.url.startsWith('https://'))) { + if (summary.player.url && !(summary.player.url.startsWith('http://') || summary.player.url.startsWith('https://'))) { throw new Error('unsupported schema included'); } - + summary.icon = this.wrap(summary.icon); summary.thumbnail = this.wrap(summary.thumbnail); - + // Cache 7days reply.header('Cache-Control', 'max-age=604800, immutable'); - + return summary; } catch (err) { this.logger.warn(`Failed to get preview of ${url}: ${err}`); diff --git a/packages/backend/test/e2e/fetch-resource.ts b/packages/backend/test/e2e/fetch-resource.ts index 9643c45d1a0bade81c2b7584ae5851e2de4a92dd..78ca8b43ba2a4163fd9d7885a8b3e7860ae02c41 100644 --- a/packages/backend/test/e2e/fetch-resource.ts +++ b/packages/backend/test/e2e/fetch-resource.ts @@ -1,7 +1,8 @@ process.env.NODE_ENV = 'test'; import * as assert from 'assert'; -import { startServer, signup, post, api, simpleGet } from '../utils.js'; +import { startServer, channel, clip, cookie, galleryPost, signup, page, play, post, simpleGet, uploadFile } from '../utils.js'; +import type { SimpleGetResponse } from '../utils.js'; import type { INestApplicationContext } from '@nestjs/common'; // Request Accept @@ -15,189 +16,446 @@ const AP = 'application/activity+json; charset=utf-8'; const HTML = 'text/html; charset=utf-8'; const JSON_UTF8 = 'application/json; charset=utf-8'; -describe('Fetch resource', () => { +describe('Webリソース', () => { let app: INestApplicationContext; let alice: any; + let aliceUploadedFile: any; let alicesPost: any; + let alicePage: any; + let alicePlay: any; + let aliceClip: any; + let aliceGalleryPost: any; + let aliceChannel: any; + + type Request = { + path: string, + accept?: string, + cookie?: string, + }; + const ok = async (param: Request & { + type?: string, + }):Promise<SimpleGetResponse> => { + const { path, accept, cookie, type } = param; + const res = await simpleGet(path, accept, cookie); + assert.strictEqual(res.status, 200); + assert.strictEqual(res.type, type ?? HTML); + return res; + }; + + const notOk = async (param: Request & { + status?: number, + code?: string, + }): Promise<SimpleGetResponse> => { + const { path, accept, cookie, status, code } = param; + const res = await simpleGet(path, accept, cookie); + assert.notStrictEqual(res.status, 200); + if (status != null) { + assert.strictEqual(res.status, status); + } + if (code != null) { + assert.strictEqual(res.body.error.code, code); + } + return res; + }; + + const notFound = async (param: Request): Promise<SimpleGetResponse> => { + return await notOk({ + ...param, + status: 404, + }); + }; + + const metaTag = (res: SimpleGetResponse, key: string, superkey = 'name'): string => { + return res.body.window.document.querySelector('meta[' + superkey + '="' + key + '"]')?.content; + }; beforeAll(async () => { app = await startServer(); alice = await signup({ username: 'alice' }); + aliceUploadedFile = await uploadFile(alice); alicesPost = await post(alice, { text: 'test', }); + alicePage = await page(alice, {}); + alicePlay = await play(alice, {}); + aliceClip = await clip(alice, {}); + aliceGalleryPost = await galleryPost(alice, { + fileIds: [aliceUploadedFile.body.id], + }); + aliceChannel = await channel(alice, {}); }, 1000 * 60 * 2); afterAll(async () => { await app.close(); }); - describe('Common', () => { - test('meta', async () => { - const res = await api('/meta', { - }); + describe.each([ + { path: '/', type: HTML }, + { path: '/docs/ja-JP/about', type: HTML }, // "指定ã•ã‚ŒãŸURLã«è©²å½“ã™ã‚‹ãƒšãƒ¼ã‚¸ã¯ã‚ã‚Šã¾ã›ã‚“ã§ã—ãŸã€‚" + // fastify-static gives charset=UTF-8 instead of utf-8 and that's okay + { path: '/api-doc', type: 'text/html; charset=UTF-8' }, + { path: '/api.json', type: JSON_UTF8 }, + { path: '/api-console', type: HTML }, + { path: '/_info_card_', type: HTML }, + { path: '/bios', type: HTML }, + { path: '/cli', type: HTML }, + { path: '/flush', type: HTML }, + { path: '/robots.txt', type: 'text/plain; charset=UTF-8' }, + { path: '/favicon.ico', type: 'image/vnd.microsoft.icon' }, + { path: '/opensearch.xml', type: 'application/opensearchdescription+xml' }, + { path: '/apple-touch-icon.png', type: 'image/png' }, + { path: '/twemoji/2764.svg', type: 'image/svg+xml' }, + { path: '/twemoji/2764-fe0f-200d-1f525.svg', type: 'image/svg+xml' }, + { path: '/twemoji-badge/2764.png', type: 'image/png' }, + { path: '/twemoji-badge/2764-fe0f-200d-1f525.png', type: 'image/png' }, + { path: '/fluent-emoji/2764.png', type: 'image/png' }, + { path: '/fluent-emoji/2764-fe0f-200d-1f525.png', type: 'image/png' }, + ])('$path', (p) => { + test('ãŒGETã§ãる。', async () => await ok({ ...p })); + + // 注æ„: WebページãŒ200ã§å–å¾—ã§ãã¦ã‚‚ã€å®Ÿéš›ã®HTMLãŒæ£ã—ã表示ã§ãã‚‹ã¨ã¯é™ã‚‰ãªã„ + // 例ãˆã°ã€ /@xxx/pages/yyy ã«å˜åœ¨ã—ãªã„IDを渡ã—ãŸå ´åˆã€HTTPレスãƒãƒ³ã‚¹ã§ã¯ã‚¨ãƒ©ãƒ¼ã‚’区別ã§ããªã„ + // ã“ã†ã„ã£ãŸã‚¢ã‚µãƒ¼ã‚·ãƒ§ãƒ³ã¯ãƒ•ãƒãƒ³ãƒˆã‚¨ãƒ³ãƒ‰E2Eã‚„API Endpointã®ãƒ†ã‚¹ãƒˆã§æ‹…ä¿ã™ã‚‹ã€‚ + }); - assert.strictEqual(res.status, 200); - }); + describe.each([ + { path: '/twemoji/2764.png' }, + { path: '/twemoji/2764-fe0f-200d-1f525.png' }, + { path: '/twemoji-badge/2764.svg' }, + { path: '/twemoji-badge/2764-fe0f-200d-1f525.svg' }, + { path: '/fluent-emoji/2764.svg' }, + { path: '/fluent-emoji/2764-fe0f-200d-1f525.svg' }, + ])('$path', ({ path }) => { + test('ã¯GETã§ããªã„。', async () => await notFound({ path })); + }); - test('GET root', async () => { - const res = await simpleGet('/'); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, HTML); - }); + describe.each([ + { ext: 'rss', type: 'application/rss+xml; charset=utf-8' }, + { ext: 'atom', type: 'application/atom+xml; charset=utf-8' }, + { ext: 'json', type: 'application/json; charset=utf-8' }, + ])('/@:username.$ext', ({ ext, type }) => { + const path = (username: string): string => `/@${username}.${ext}`; + + test('ãŒGETã§ãる。', async () => await ok({ + path: path(alice.username), + type, + })); + + test('ã¯å˜åœ¨ã—ãªã„ユーザーã¯GETã§ããªã„。', async () => await notOk({ + path: path('nonexisting'), + status: 404, + })); + }); - test('GET docs', async () => { - const res = await simpleGet('/docs/ja-JP/about'); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, HTML); - }); + describe.each([{ path: '/api/foo' }])('$path', ({ path }) => { + test('ã¯GETã§ããªã„。', async () => await notOk({ + path, + status: 404, + code: 'UNKNOWN_API_ENDPOINT', + })); + }); - test('GET api-doc', async () => { - const res = await simpleGet('/api-doc'); - assert.strictEqual(res.status, 200); - // fastify-static gives charset=UTF-8 instead of utf-8 and that's okay - assert.strictEqual(res.type?.toLowerCase(), HTML); - }); + describe.each([{ path: '/queue' }])('$path', ({ path }) => { + test('ã¯adminã§ãªã‘ã‚Œã°GETã§ããªã„。', async () => await notOk({ + path, + status: 500, // FIXME? 403ã§ã¯ãªã„。 + })); + + test('ã¯adminãªã‚‰GETã§ãる。', async () => await ok({ + path, + cookie: cookie(alice), + })); + }); - test('GET api.json', async () => { - const res = await simpleGet('/api.json'); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, JSON_UTF8); - }); + describe.each([{ path: '/streaming' }])('$path', ({ path }) => { + test('ã¯GETã§ããªã„。', async () => await notOk({ + path, + status: 503, + })); + }); - test('GET api/foo (å˜åœ¨ã—ãªã„)', async () => { - const res = await simpleGet('/api/foo'); - assert.strictEqual(res.status, 404); - assert.strictEqual(res.body.error.code, 'UNKNOWN_API_ENDPOINT'); + describe('/@:username', () => { + const path = (username: string): string => `/@${username}`; + + describe.each([ + { accept: PREFER_HTML }, + { accept: UNSPECIFIED }, + ])('(Acceptヘッダ: $accept)', ({ accept }) => { + test('ã¯HTMLã¨ã—ã¦GETã§ãる。', async () => { + const res = await ok({ + path: path(alice.username), + accept, + type: HTML, + }); + assert.strictEqual(metaTag(res, 'misskey:user-username'), alice.username); + assert.strictEqual(metaTag(res, 'misskey:user-id'), alice.id); + + // TODO ogã‚¿ã‚°ã®æ¤œè¨¼ + // TODO profile.noCrawleã®æ¤œè¨¼ + // TODO twitter:creatorã®æ¤œè¨¼ + // TODO <link rel="me" ...>ã®æ¤œè¨¼ + }); + test('ã¯HTMLã¨ã—ã¦GETã§ãる。(å˜åœ¨ã—ãªã„IDã§ã‚‚。)', async () => await ok({ + path: path('xxxxxxxxxx'), + type: HTML, + })); }); - test('GET api-console (client page)', async () => { - const res = await simpleGet('/api-console'); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, HTML); + describe.each([ + { accept: ONLY_AP }, + { accept: PREFER_AP }, + ])('(Acceptヘッダ: $accept)', ({ accept }) => { + test('ã¯ActivityPubã¨ã—ã¦GETã§ãる。', async () => { + const res = await ok({ + path: path(alice.username), + accept, + type: AP, + }); + assert.strictEqual(res.body.type, 'Person'); + }); + + test('ã¯å˜åœ¨ã—ãªã„IDã®ã¨ãActivityPubã¨ã—ã¦GETã§ããªã„。', async () => await notFound({ + path: path('xxxxxxxxxx'), + accept, + })); }); + }); - test('GET favicon.ico', async () => { - const res = await simpleGet('/favicon.ico'); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, 'image/vnd.microsoft.icon'); + describe.each([ + // 実際ã®ãƒãƒ³ãƒ‰ãƒ«ã¯ãƒ•ãƒãƒ³ãƒˆã‚¨ãƒ³ãƒ‰(index.vue)ã§è¡Œã‚れる + { sub: 'home' }, + { sub: 'notes' }, + { sub: 'activity' }, + { sub: 'achievements' }, + { sub: 'reactions' }, + { sub: 'clips' }, + { sub: 'pages' }, + { sub: 'gallery' }, + ])('/@:username/$sub', ({ sub }) => { + const path = (username: string): string => `/@${username}/${sub}`; + + test('ã¯HTMLã¨ã—ã¦GETã§ãる。', async () => { + const res = await ok({ + path: path(alice.username), + }); + assert.strictEqual(metaTag(res, 'misskey:user-username'), alice.username); + assert.strictEqual(metaTag(res, 'misskey:user-id'), alice.id); }); + }); + + describe('/@:user/pages/:page', () => { + const path = (username: string, pagename: string): string => `/@${username}/pages/${pagename}`; - test('GET apple-touch-icon.png', async () => { - const res = await simpleGet('/apple-touch-icon.png'); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, 'image/png'); + test('ã¯HTMLã¨ã—ã¦GETã§ãる。', async () => { + const res = await ok({ + path: path(alice.username, alicePage.name), + }); + assert.strictEqual(metaTag(res, 'misskey:user-username'), alice.username); + assert.strictEqual(metaTag(res, 'misskey:user-id'), alice.id); + assert.strictEqual(metaTag(res, 'misskey:page-id'), alicePage.id); + + // TODO ogã‚¿ã‚°ã®æ¤œè¨¼ + // TODO profile.noCrawleã®æ¤œè¨¼ + // TODO twitter:creatorã®æ¤œè¨¼ }); + + test('ã¯GETã§ãる。(å˜åœ¨ã—ãªã„IDã§ã‚‚。)', async () => await ok({ + path: path(alice.username, 'xxxxxxxxxx'), + })); + }); - test('GET twemoji svg', async () => { - const res = await simpleGet('/twemoji/2764.svg'); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, 'image/svg+xml'); + describe('/users/:id', () => { + const path = (id: string): string => `/users/${id}`; + + describe.each([ + { accept: PREFER_HTML }, + { accept: UNSPECIFIED }, + ])('(Acceptヘッダ: $accept)', ({ accept }) => { + test('ã¯/@:usernameã«ãƒªãƒ€ã‚¤ãƒ¬ã‚¯ãƒˆã™ã‚‹', async () => { + const res = await simpleGet(path(alice.id), accept); + assert.strictEqual(res.status, 302); + assert.strictEqual(res.location, `/@${alice.username}`); + }); + + test('ã¯å˜åœ¨ã—ãªã„ユーザーã¯GETã§ããªã„。', async () => await notFound({ + path: path('xxxxxxxx'), + })); }); - test('GET twemoji svg with hyphen', async () => { - const res = await simpleGet('/twemoji/2764-fe0f-200d-1f525.svg'); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, 'image/svg+xml'); + describe.each([ + { accept: ONLY_AP }, + { accept: PREFER_AP }, + ])('(Acceptヘッダ: $accept)', ({ accept }) => { + test('ã¯ActivityPubã¨ã—ã¦GETã§ãる。', async () => { + const res = await ok({ + path: path(alice.id), + accept, + type: AP, + }); + assert.strictEqual(res.body.type, 'Person'); + }); + + test('ã¯å˜åœ¨ã—ãªã„IDã®ã¨ãActivityPubã¨ã—ã¦GETã§ããªã„。', async () => await notOk({ + path: path('xxxxxxxx'), + accept, + status: 404, + })); }); }); + + describe('/users/inbox', () => { + test('ãŒGETã§ãる。(POST専用ã ã‘ã©4xx/5xxã«ãªã‚‰ãšHTMLãŒè¿”ã£ã¦ãã‚‹)', async () => await ok({ + path: '/inbox', + })); - describe('/@:username', () => { - test('Only AP => AP', async () => { - const res = await simpleGet(`/@${alice.username}`, ONLY_AP); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, AP); - }); + // test.todo('POSTã§ãる?'); + }); - test('Prefer AP => AP', async () => { - const res = await simpleGet(`/@${alice.username}`, PREFER_AP); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, AP); - }); + describe('/users/:id/inbox', () => { + const path = (id: string): string => `/users/${id}/inbox`; - test('Prefer HTML => HTML', async () => { - const res = await simpleGet(`/@${alice.username}`, PREFER_HTML); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, HTML); - }); + test('ãŒGETã§ãる。(POST専用ã ã‘ã©4xx/5xxã«ãªã‚‰ãšHTMLãŒè¿”ã£ã¦ãã‚‹)', async () => await ok({ + path: path(alice.id), + })); - test('Unspecified => HTML', async () => { - const res = await simpleGet(`/@${alice.username}`, UNSPECIFIED); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, HTML); - }); + // test.todo('POSTã§ãる?'); }); - describe('/users/:id', () => { - test('Only AP => AP', async () => { - const res = await simpleGet(`/users/${alice.id}`, ONLY_AP); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, AP); - }); + describe('/users/:id/outbox', () => { + const path = (id: string): string => `/users/${id}/outbox`; - test('Prefer AP => AP', async () => { - const res = await simpleGet(`/users/${alice.id}`, PREFER_AP); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, AP); + test('ãŒGETã§ãる。', async () => { + const res = await ok({ + path: path(alice.id), + type: AP, + }); + assert.strictEqual(res.body.type, 'OrderedCollection'); }); + }); + + describe('/notes/:id', () => { + const path = (noteId: string): string => `/notes/${noteId}`; + + describe.each([ + { accept: PREFER_HTML }, + { accept: UNSPECIFIED }, + ])('(Acceptヘッダ: $accept)', ({ accept }) => { + test('ã¯HTMLã¨ã—ã¦GETã§ãる。', async () => { + const res = await ok({ + path: path(alicesPost.id), + accept, + type: HTML, + }); + assert.strictEqual(metaTag(res, 'misskey:user-username'), alice.username); + assert.strictEqual(metaTag(res, 'misskey:user-id'), alice.id); + assert.strictEqual(metaTag(res, 'misskey:note-id'), alicesPost.id); + + // TODO ogã‚¿ã‚°ã®æ¤œè¨¼ + // TODO profile.noCrawleã®æ¤œè¨¼ + // TODO twitter:creatorã®æ¤œè¨¼ + }); - test('Prefer HTML => Redirect to /@:username', async () => { - const res = await simpleGet(`/users/${alice.id}`, PREFER_HTML); - assert.strictEqual(res.status, 302); - assert.strictEqual(res.location, `/@${alice.username}`); + test('ã¯HTMLã¨ã—ã¦GETã§ãる。(å˜åœ¨ã—ãªã„IDã§ã‚‚。)', async () => await ok({ + path: path('xxxxxxxxxx'), + })); }); - test('Undecided => HTML', async () => { - const res = await simpleGet(`/users/${alice.id}`, UNSPECIFIED); - assert.strictEqual(res.status, 302); - assert.strictEqual(res.location, `/@${alice.username}`); + describe.each([ + { accept: ONLY_AP }, + { accept: PREFER_AP }, + ])('(Acceptヘッダ: $accept)', ({ accept }) => { + test('ã¯ActivityPubã¨ã—ã¦GETã§ãる。', async () => { + const res = await ok({ + path: path(alicesPost.id), + accept, + type: AP, + }); + assert.strictEqual(res.body.type, 'Note'); + }); + + test('ã¯å˜åœ¨ã—ãªã„IDã®ã¨ãActivityPubã¨ã—ã¦GETã§ããªã„。', async () => await notFound({ + path: path('xxxxxxxxxx'), + accept, + })); }); }); + + describe('/play/:id', () => { + const path = (playid: string): string => `/play/${playid}`; - describe('/notes/:id', () => { - test('Only AP => AP', async () => { - const res = await simpleGet(`/notes/${alicesPost.id}`, ONLY_AP); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, AP); + test('ãŒGETã§ãる。', async () => { + const res = await ok({ + path: path(alicePlay.id), + }); + assert.strictEqual(metaTag(res, 'misskey:user-username'), alice.username); + assert.strictEqual(metaTag(res, 'misskey:user-id'), alice.id); + assert.strictEqual(metaTag(res, 'misskey:flash-id'), alicePlay.id); + + // TODO ogã‚¿ã‚°ã®æ¤œè¨¼ + // TODO profile.noCrawleã®æ¤œè¨¼ + // TODO twitter:creatorã®æ¤œè¨¼ }); - test('Prefer AP => AP', async () => { - const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_AP); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, AP); - }); + test('ãŒGETã§ãる。(å˜åœ¨ã—ãªã„IDã§ã‚‚。)', async () => await ok({ + path: path('xxxxxxxxxx'), + })); + }); + + describe('/clips/:clip', () => { + const path = (clip: string): string => `/clips/${clip}`; - test('Prefer HTML => HTML', async () => { - const res = await simpleGet(`/notes/${alicesPost.id}`, PREFER_HTML); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, HTML); - }); + test('ãŒGETã§ãる。', async () => { + const res = await ok({ + path: path(aliceClip.id), + }); + assert.strictEqual(metaTag(res, 'misskey:user-username'), alice.username); + assert.strictEqual(metaTag(res, 'misskey:user-id'), alice.id); + assert.strictEqual(metaTag(res, 'misskey:clip-id'), aliceClip.id); - test('Unspecified => HTML', async () => { - const res = await simpleGet(`/notes/${alicesPost.id}`, UNSPECIFIED); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, HTML); + // TODO ogã‚¿ã‚°ã®æ¤œè¨¼ + // TODO profile.noCrawleã®æ¤œè¨¼ }); + + test('ãŒGETã§ãる。(å˜åœ¨ã—ãªã„IDã§ã‚‚。)', async () => await ok({ + path: path('xxxxxxxxxx'), + })); }); - describe('Feeds', () => { - test('RSS', async () => { - const res = await simpleGet(`/@${alice.username}.rss`, UNSPECIFIED); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, 'application/rss+xml; charset=utf-8'); - }); + describe('/gallery/:post', () => { + const path = (post: string): string => `/gallery/${post}`; + + test('ãŒGETã§ãる。', async () => { + const res = await ok({ + path: path(aliceGalleryPost.id), + }); + assert.strictEqual(metaTag(res, 'misskey:user-username'), alice.username); + assert.strictEqual(metaTag(res, 'misskey:user-id'), alice.id); - test('ATOM', async () => { - const res = await simpleGet(`/@${alice.username}.atom`, UNSPECIFIED); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, 'application/atom+xml; charset=utf-8'); + // FIXME: misskey:gallery-post-idã¿ãŸã„ãªmetaã‚¿ã‚°ã®è¨å®šãŒãªã„ + // TODO profile.noCrawleã®æ¤œè¨¼ + // TODO twitter:creatorã®æ¤œè¨¼ }); + + test('ãŒGETã§ãる。(å˜åœ¨ã—ãªã„IDã§ã‚‚。)', async () => await ok({ + path: path('xxxxxxxxxx'), + })); + }); + + describe('/channels/:channel', () => { + const path = (channel: string): string => `/channels/${channel}`; + + test('ã¯GETã§ãる。', async () => { + const res = await ok({ + path: path(aliceChannel.id), + }); - test('JSON', async () => { - const res = await simpleGet(`/@${alice.username}.json`, UNSPECIFIED); - assert.strictEqual(res.status, 200); - assert.strictEqual(res.type, 'application/json; charset=utf-8'); + // FIXME: misskey関連ã®metaã‚¿ã‚°ã®è¨å®šãŒãªã„ + // TODO ogã‚¿ã‚°ã®æ¤œè¨¼ }); + + test('ãŒGETã§ãる。(å˜åœ¨ã—ãªã„IDã§ã‚‚。)', async () => await ok({ + path: path('xxxxxxxxxx'), + })); }); }); diff --git a/packages/backend/test/utils.ts b/packages/backend/test/utils.ts index d1a5d6d9497e40c048a15ff9b82a970291b50c5c..4d52c2f06284e4c2031bc78c3dde20c5d96990c3 100644 --- a/packages/backend/test/utils.ts +++ b/packages/backend/test/utils.ts @@ -3,6 +3,7 @@ import { isAbsolute, basename } from 'node:path'; import WebSocket from 'ws'; import fetch, { Blob, File, RequestInit } from 'node-fetch'; import { DataSource } from 'typeorm'; +import { JSDOM } from 'jsdom'; import { entities } from '../src/postgres.js'; import { loadConfig } from '../src/config.js'; import type * as misskey from 'misskey-js'; @@ -12,6 +13,10 @@ export { server as startServer } from '@/boot/common.js'; const config = loadConfig(); export const port = config.port; +export const cookie = (me: any): string => { + return `token=${me.token};`; +}; + export const api = async (endpoint: string, params: any, me?: any) => { const normalized = endpoint.replace(/^\//, ''); return await request(`api/${normalized}`, params, me); @@ -71,6 +76,71 @@ export const react = async (user: any, note: any, reaction: string): Promise<any }, user); }; +export const page = async (user: any, page: any = {}): Promise<any> => { + const res = await api('pages/create', { + alignCenter: false, + content: [ + { + id: '2be9a64b-5ada-43a3-85f3-ec3429551ded', + text: 'Hello World!', + type: 'text', + }, + ], + eyeCatchingImageId: null, + font: 'sans-serif', + hideTitleWhenPinned: false, + name: '1678594845072', + script: '', + summary: null, + title: '', + variables: [], + ...page, + }, user); + return res.body; +}; + +export const play = async (user: any, play: any = {}): Promise<any> => { + const res = await api('flash/create', { + permissions: [], + script: 'test', + summary: '', + title: 'test', + ...play, + }, user); + return res.body; +}; + +export const clip = async (user: any, clip: any = {}): Promise<any> => { + const res = await api('clips/create', { + description: null, + isPublic: true, + name: 'test', + ...clip, + }, user); + return res.body; +}; + +export const galleryPost = async (user: any, channel: any = {}): Promise<any> => { + const res = await api('gallery/posts/create', { + description: null, + fileIds: [], + isSensitive: false, + title: 'test', + ...channel, + }, user); + return res.body; +}; + +export const channel = async (user: any, channel: any = {}): Promise<any> => { + const res = await api('channels/create', { + bannerId: null, + description: null, + name: 'test', + ...channel, + }, user); + return res.body; +}; + interface UploadOptions { /** Optional, absolute path or relative from ./resources/ */ path?: string | URL; @@ -196,10 +266,17 @@ export const waitFire = async (user: any, channel: string, trgr: () => any, cond }); }; -export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status: number, body: any, type: string | null, location: string | null }> => { +export type SimpleGetResponse = { + status: number, + body: any | JSDOM | null, + type: string | null, + location: string | null +}; +export const simpleGet = async (path: string, accept = '*/*', cookie: any = undefined): Promise<SimpleGetResponse> => { const res = await relativeFetch(path, { headers: { Accept: accept, + Cookie: cookie, }, redirect: 'manual', }); @@ -208,10 +285,14 @@ export const simpleGet = async (path: string, accept = '*/*'): Promise<{ status: 'application/json; charset=utf-8', 'application/activity+json; charset=utf-8', ]; + const htmlTypes = [ + 'text/html; charset=utf-8', + ]; - const body = jsonTypes.includes(res.headers.get('content-type') ?? '') - ? await res.json() - : null; + const body = + jsonTypes.includes(res.headers.get('content-type') ?? '') ? await res.json() : + htmlTypes.includes(res.headers.get('content-type') ?? '') ? new JSDOM(await res.text()) : + null; return { status: res.status, diff --git a/packages/frontend/package.json b/packages/frontend/package.json index 65d1a5e3666dd387aad25a3b9d0cdfd83adea19f..34024408d542497cde5d6efeef55b932bf68d13f 100644 --- a/packages/frontend/package.json +++ b/packages/frontend/package.json @@ -15,7 +15,7 @@ "@rollup/plugin-alias": "4.0.3", "@rollup/plugin-json": "6.0.0", "@rollup/pluginutils": "5.0.2", - "@syuilo/aiscript": "0.13.0", + "@syuilo/aiscript": "0.13.1", "@tabler/icons-webfont": "2.10.0", "@vitejs/plugin-vue": "4.0.0", "@vue/compiler-sfc": "3.2.47", @@ -97,7 +97,9 @@ "eslint-plugin-vue": "9.9.0", "happy-dom": "8.9.0", "start-server-and-test": "2.0.0", + "summaly": "github:misskey-dev/summaly", "vitest": "^0.29.2", + "vitest-fetch-mock": "^0.2.2", "vue-eslint-parser": "9.1.0", "vue-tsc": "1.2.0" } diff --git a/packages/frontend/src/components/MkUrlPreview.vue b/packages/frontend/src/components/MkUrlPreview.vue index 5381ecbfa54731969d91cf1c28f0f5c5fa10ade7..094709e093dd31e483b70d6eed2d534ec66e7e0b 100644 --- a/packages/frontend/src/components/MkUrlPreview.vue +++ b/packages/frontend/src/components/MkUrlPreview.vue @@ -1,7 +1,18 @@ <template> -<template v-if="playerEnabled"> - <div :class="$style.player" :style="`padding: ${(player.height || 0) / (player.width || 1) * 100}% 0 0`"> - <iframe v-if="player.url.startsWith('http://') || player.url.startsWith('https://')" :class="$style.playerIframe" :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" :width="player.width || '100%'" :heigth="player.height || 250" frameborder="0" allow="autoplay; encrypted-media" allowfullscreen/> +<template v-if="player.url && playerEnabled"> + <div + :class="$style.player" + :style="player.width ? `padding: ${(player.height || 0) / player.width * 100}% 0 0` : `padding: ${(player.height || 0)}px 0 0`" + > + <iframe + v-if="player.url.startsWith('http://') || player.url.startsWith('https://')" + sandbox="allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin" + scrolling="no" + :allow="player.allow.join(';')" + :class="$style.playerIframe" + :src="player.url + (player.url.match(/\?/) ? '&autoplay=1&auto_play=1' : '?autoplay=1&auto_play=1')" + :style="{ border: 0 }" + ></iframe> <span v-else>invalid url</span> </div> <div :class="$style.action"> @@ -28,7 +39,7 @@ <header :class="$style.header"> <h1 v-if="unknownUrl" :class="$style.title">{{ url }}</h1> <h1 v-else-if="fetching" :class="$style.title"><MkEllipsis/></h1> - <h1 v-else :class="$style.title" :title="title">{{ title }}</h1> + <h1 v-else :class="$style.title" :title="title ?? undefined">{{ title }}</h1> </header> <p v-if="unknownUrl" :class="$style.text">{{ i18n.ts.cannotLoad }}</p> <p v-else-if="fetching" :class="$style.text"><MkEllipsis/></p> @@ -37,7 +48,7 @@ <img v-if="icon" :class="$style.siteIcon" :src="icon"/> <p v-if="unknownUrl" :class="$style.siteName">?</p> <p v-else-if="fetching" :class="$style.siteName"><MkEllipsis/></p> - <p v-else :class="$style.siteName" :title="sitename">{{ sitename }}</p> + <p v-else :class="$style.siteName" :title="sitename ?? undefined">{{ sitename }}</p> </footer> </article> </component> @@ -59,6 +70,7 @@ <script lang="ts" setup> import { defineAsyncComponent, onUnmounted } from 'vue'; +import type { summaly } from 'summaly'; import { url as local } from '@/config'; import { i18n } from '@/i18n'; import * as os from '@/os'; @@ -66,6 +78,8 @@ import { deviceKind } from '@/scripts/device-kind'; import MkButton from '@/components/MkButton.vue'; import { versatileLang } from '@/scripts/intl-const'; +type SummalyResult = Awaited<ReturnType<typeof summaly>>; + const props = withDefaults(defineProps<{ url: string; detail?: boolean; @@ -91,7 +105,7 @@ let player = $ref({ url: null, width: null, height: null, -}); +} as SummalyResult['player']); let playerEnabled = $ref(false); let tweetId = $ref<string | null>(null); let tweetExpanded = $ref(props.detail); @@ -114,11 +128,7 @@ if (requestUrl.hostname === 'music.youtube.com' && requestUrl.pathname.match('^/ requestUrl.hash = ''; window.fetch(`/url?url=${encodeURIComponent(requestUrl.href)}&lang=${versatileLang}`).then(res => { - res.json().then(info => { - if (info.url == null) { - unknownUrl = true; - return; - } + res.json().then((info: SummalyResult) => { title = info.title; description = info.description; thumbnail = info.thumbnail; diff --git a/packages/frontend/src/pages/about-misskey.vue b/packages/frontend/src/pages/about-misskey.vue index e2827662a56a59c356ba591688282f6449bfcab8..60f61ed2936e5c8eb4468942cc1d2441a95c5fe1 100644 --- a/packages/frontend/src/pages/about-misskey.vue +++ b/packages/frontend/src/pages/about-misskey.vue @@ -19,7 +19,7 @@ <div style="text-align: center;"> {{ i18n.ts._aboutMisskey.about }}<br><a href="https://misskey-hub.net/docs/misskey.html" target="_blank" class="_link">{{ i18n.ts.learnMore }}</a> </div> - <div style="text-align: center;"> + <div v-if="$i != null" style="text-align: center;"> <MkButton primary rounded inline @click="iLoveMisskey">I <Mfm text="$[jelly â¤]"/> #Misskey</MkButton> </div> <FormSection> diff --git a/packages/frontend/src/pages/ads.vue b/packages/frontend/src/pages/ads.vue new file mode 100644 index 0000000000000000000000000000000000000000..728ef3c0b120a66f7ca4fd7c3550b6aacd07931d --- /dev/null +++ b/packages/frontend/src/pages/ads.vue @@ -0,0 +1,25 @@ +<template> +<MkStickyContainer> + <template #header><MkPageHeader/></template> + + <MkSpacer :content-max="500"> + <div class="_gaps"> + <MkAd v-for="ad in instance.ads" :key="ad.id" :specify="ad"/> + </div> + </MkSpacer> +</MkStickyContainer> +</template> + +<script lang="ts" setup> +import { computed, watch } from 'vue'; +import * as os from '@/os'; +import { definePageMetadata } from '@/scripts/page-metadata'; +import { i18n } from '@/i18n'; +import { instance } from '@/instance'; + +definePageMetadata({ + title: i18n.ts.ads, + icon: 'ti ti-ad', +}); +</script> + diff --git a/packages/frontend/src/pages/emojis.emoji.vue b/packages/frontend/src/pages/emojis.emoji.vue index 0edc290801af720320f169dca6d936b2d8836f0d..bdd21b29eeda3930a9c62b05ddbf074a2d576dc0 100644 --- a/packages/frontend/src/pages/emojis.emoji.vue +++ b/packages/frontend/src/pages/emojis.emoji.vue @@ -34,6 +34,17 @@ function menu(ev) { copyToClipboard(`:${props.emoji.name}:`); os.success(); }, + }, { + text: i18n.ts.info, + icon: 'ti ti-info-circle', + action: () => { + os.apiGet('emoji', { name: props.emoji.name }).then(res => { + os.alert({ + type: 'info', + text: `License: ${res.license}`, + }); + }); + }, }], ev.currentTarget ?? ev.target); } </script> diff --git a/packages/frontend/src/pages/flash/flash-edit.vue b/packages/frontend/src/pages/flash/flash-edit.vue index 242ca5e89765a2a3601d349dc7a22731ef736a92..35edcc7cdad0af2bd30ed3f9dcd9cbf2db56f4f5 100644 --- a/packages/frontend/src/pages/flash/flash-edit.vue +++ b/packages/frontend/src/pages/flash/flash-edit.vue @@ -33,7 +33,7 @@ import MkTextarea from '@/components/MkTextarea.vue'; import MkInput from '@/components/MkInput.vue'; import { useRouter } from '@/router'; -const PRESET_DEFAULT = `/// @ 0.13.0 +const PRESET_DEFAULT = `/// @ 0.13.1 var name = "" @@ -51,7 +51,7 @@ Ui:render([ ]) `; -const PRESET_OMIKUJI = `/// @ 0.13.0 +const PRESET_OMIKUJI = `/// @ 0.13.1 // ユーザーã”ã¨ã«æ—¥æ›¿ã‚ã‚Šã®ãŠã¿ãã˜ã®ãƒ—リセット // é¸æŠžè‚¢ @@ -94,7 +94,7 @@ Ui:render([ ]) `; -const PRESET_SHUFFLE = `/// @ 0.13.0 +const PRESET_SHUFFLE = `/// @ 0.13.1 // å·»ã戻ã—å¯èƒ½ãªæ–‡å—シャッフルã®ãƒ—リセット let string = "ペペãƒãƒ³ãƒãƒ¼ãƒŽ" @@ -173,7 +173,7 @@ var cursor = 0 do() `; -const PRESET_QUIZ = `/// @ 0.13.0 +const PRESET_QUIZ = `/// @ 0.13.1 let title = '地ç†ã‚¯ã‚¤ã‚º' let qas = [{ @@ -286,7 +286,7 @@ qaEls.push(Ui:C:container({ Ui:render(qaEls) `; -const PRESET_TIMELINE = `/// @ 0.13.0 +const PRESET_TIMELINE = `/// @ 0.13.1 // APIリクエストを行ã„ãƒãƒ¼ã‚«ãƒ«ã‚¿ã‚¤ãƒ ラインを表示ã™ã‚‹ãƒ—リセット @fetch() { diff --git a/packages/frontend/src/router.ts b/packages/frontend/src/router.ts index 5f184881b48c30ed338f92d1d14a2648ef029899..590c5765fd508095764b25339d9437c4fcc9b844 100644 --- a/packages/frontend/src/router.ts +++ b/packages/frontend/src/router.ts @@ -197,6 +197,9 @@ export const routes = [{ }, { path: '/about-misskey', component: page(() => import('./pages/about-misskey.vue')), +}, { + path: '/ads', + component: page(() => import('./pages/ads.vue')), }, { path: '/theme-editor', component: page(() => import('./pages/theme-editor.vue')), diff --git a/packages/frontend/src/ui/_common_/common.ts b/packages/frontend/src/ui/_common_/common.ts index a90ec6172f382af815ee3390289c8139e4a59e8d..eae4f0091c783cc5fcf574e091b26eafe1e308cd 100644 --- a/packages/frontend/src/ui/_common_/common.ts +++ b/packages/frontend/src/ui/_common_/common.ts @@ -29,6 +29,11 @@ export function openInstanceMenu(ev: MouseEvent) { icon: 'ti ti-chart-line', to: '/about#charts', }, null, { + type: 'link', + text: i18n.ts.ads, + icon: 'ti ti-ad', + to: '/ads', + }, { type: 'parent', text: i18n.ts.tools, icon: 'ti ti-tool', diff --git a/packages/frontend/test/init.ts b/packages/frontend/test/init.ts index 96730e7b568b2a16c06724fbc7bf5771961c2ab4..295107e14365ef30dd26b59c3d5bd2b53d5c5c76 100644 --- a/packages/frontend/test/init.ts +++ b/packages/frontend/test/init.ts @@ -1,4 +1,8 @@ import { vi } from 'vitest'; +import createFetchMock from 'vitest-fetch-mock'; + +const fetchMocker = createFetchMock(vi); +fetchMocker.enableMocks(); // Set i18n import locales from '../../../locales'; diff --git a/packages/frontend/test/url-preview.test.ts b/packages/frontend/test/url-preview.test.ts new file mode 100644 index 0000000000000000000000000000000000000000..205982a40a463b1257d6101051a4000da73a944f --- /dev/null +++ b/packages/frontend/test/url-preview.test.ts @@ -0,0 +1,140 @@ +import { describe, test, assert, afterEach } from 'vitest'; +import { render, cleanup, type RenderResult } from '@testing-library/vue'; +import './init'; +import type { summaly } from 'summaly'; +import { directives } from '@/directives'; +import MkUrlPreview from '@/components/MkUrlPreview.vue'; + +type SummalyResult = Awaited<ReturnType<typeof summaly>>; + +describe('MkMediaImage', () => { + const renderPreviewBy = async (summary: Partial<SummalyResult>): Promise<RenderResult> => { + if (!summary.player) { + summary.player = { + url: null, + width: null, + height: null, + allow: [], + }; + } + + fetchMock.mockOnceIf(/^\/url?/, () => { + return { + status: 200, + body: JSON.stringify(summary), + }; + }); + + const result = render(MkUrlPreview, { + props: { url: summary.url }, + global: { directives }, + }); + + await new Promise<void>(resolve => { + const observer = new MutationObserver(() => { + resolve(); + observer.disconnect(); + }); + observer.observe(result.container, { childList: true, subtree: true }); + }); + + return result; + }; + + const renderAndOpenPreview = async (summary: Partial<SummalyResult>): Promise<HTMLIFrameElement | null> => { + const mkUrlPreview = await renderPreviewBy(summary); + const buttons = mkUrlPreview.getAllByRole('button'); + buttons[0].click(); + // Wait for the click event to be fired + await Promise.resolve(); + + return mkUrlPreview.container.querySelector('iframe'); + }; + + afterEach(() => { + fetchMock.resetMocks(); + cleanup(); + }); + + test('Should render the description', async () => { + const mkUrlPreview = await renderPreviewBy({ + url: 'https://example.local', + description: 'Mocked description', + }); + mkUrlPreview.getByText('Mocked description'); + }); + + test('Having a player should render a button', async () => { + const mkUrlPreview = await renderPreviewBy({ + url: 'https://example.local', + player: { + url: 'https://example.local/player', + width: null, + height: null, + allow: [], + }, + }); + const buttons = mkUrlPreview.getAllByRole('button'); + assert.strictEqual(buttons.length, 2, 'two buttons'); + }); + + test('Having a player should setup the iframe', async () => { + const iframe = await renderAndOpenPreview({ + url: 'https://example.local', + player: { + url: 'https://example.local/player', + width: null, + height: null, + allow: [], + }, + }); + assert.exists(iframe, 'iframe should exist'); + assert.strictEqual(iframe?.src, 'https://example.local/player?autoplay=1&auto_play=1'); + assert.strictEqual( + iframe?.sandbox.toString(), + 'allow-popups allow-scripts allow-storage-access-by-user-activation allow-same-origin', + ); + }); + + test('Having a player with `allow` field should set permissions', async () => { + const iframe = await renderAndOpenPreview({ + url: 'https://example.local', + player: { + url: 'https://example.local/player', + width: null, + height: null, + allow: ['fullscreen', 'web-share'], + }, + }); + assert.exists(iframe, 'iframe should exist'); + assert.strictEqual(iframe?.allow, 'fullscreen;web-share'); + }); + + test('Having a player width should keep the fixed aspect ratio', async () => { + const iframe = await renderAndOpenPreview({ + url: 'https://example.local', + player: { + url: 'https://example.local/player', + width: 400, + height: 200, + allow: [], + }, + }); + assert.exists(iframe, 'iframe should exist'); + assert.strictEqual(iframe?.parentElement?.style.paddingTop, '50%'); + }); + + test('Having a player width should keep the fixed height', async () => { + const iframe = await renderAndOpenPreview({ + url: 'https://example.local', + player: { + url: 'https://example.local/player', + width: null, + height: 200, + allow: [], + }, + }); + assert.exists(iframe, 'iframe should exist'); + assert.strictEqual(iframe?.parentElement?.style.paddingTop, '200px'); + }); +}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 2b03ad29db0634c5adfadf0b8e88c01ae7b2fa9b..d2397b154ec671f65169c7e5038c49ee8ea0429f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -398,7 +398,7 @@ importers: '@rollup/plugin-alias': 4.0.3 '@rollup/plugin-json': 6.0.0 '@rollup/pluginutils': 5.0.2 - '@syuilo/aiscript': 0.13.0 + '@syuilo/aiscript': 0.13.1 '@tabler/icons-webfont': 2.10.0 '@testing-library/vue': ^6.6.1 '@types/escape-regexp': 0.0.1 @@ -462,6 +462,7 @@ importers: seedrandom: 3.0.5 start-server-and-test: 2.0.0 strict-event-emitter-types: 2.0.0 + summaly: github:misskey-dev/summaly syuilo-password-strength: 0.0.1 textarea-caret: 3.1.0 three: 0.150.1 @@ -475,6 +476,7 @@ importers: vanilla-tilt: 1.8.0 vite: 4.1.4 vitest: ^0.29.2 + vitest-fetch-mock: ^0.2.2 vue: 3.2.47 vue-eslint-parser: 9.1.0 vue-plyr: 7.0.0 @@ -486,7 +488,7 @@ importers: '@rollup/plugin-alias': 4.0.3_rollup@3.19.0 '@rollup/plugin-json': 6.0.0_rollup@3.19.0 '@rollup/pluginutils': 5.0.2_rollup@3.19.0 - '@syuilo/aiscript': 0.13.0 + '@syuilo/aiscript': 0.13.1 '@tabler/icons-webfont': 2.10.0 '@vitejs/plugin-vue': 4.0.0_vite@4.1.4+vue@3.2.47 '@vue/compiler-sfc': 3.2.47 @@ -567,7 +569,9 @@ importers: eslint-plugin-vue: 9.9.0_eslint@8.35.0 happy-dom: 8.9.0 start-server-and-test: 2.0.0 + summaly: github.com/misskey-dev/summaly/1bab7afee616429b8bbf7a7cbcbb8ebcef66d992 vitest: 0.29.2_zcjcryjt4bqcdu7ggonulipgea + vitest-fetch-mock: 0.2.2_vitest@0.29.2 vue-eslint-parser: 9.1.0_eslint@8.35.0 vue-tsc: 1.2.0_typescript@4.9.5 @@ -2312,7 +2316,6 @@ packages: /@sindresorhus/is/5.3.0: resolution: {integrity: sha512-CX6t4SYQ37lzxicAqsBtxA3OseeoVrh9cSJ5PFYam0GksYlupRfy1A+Q4aYD3zvcfECLc0zO2u+ZnR2UYKvCrw==} engines: {node: '>=14.16'} - dev: false /@sinonjs/commons/2.0.0: resolution: {integrity: sha512-uLa0j859mMrg2slwQYdO/AkrOfmH+X6LTVmNTS9CqexuE2IvVORIkSpJLqePAbEnKJ77aMmCwr1NUZ57120Xcg==} @@ -2473,8 +2476,8 @@ packages: dev: false optional: true - /@syuilo/aiscript/0.13.0: - resolution: {integrity: sha512-zikuEpQEp1lyNoLxsqNnR2UqyRVV1ZdTOTIyKkEU/4gX90M4t1LIvevbzfyiwoUqLkOMqhdU/poARPL3pCwygg==} + /@syuilo/aiscript/0.13.1: + resolution: {integrity: sha512-WJduqlsm7pq8r1oYbPSAf5cqR6Pve6ipGoPUUSgti51dMytMnxE2dn7d7i8759RF3yftyoclZbL/DC7IjPsTng==} dependencies: autobind-decorator: 2.4.0 seedrandom: 3.0.5 @@ -2494,7 +2497,6 @@ packages: engines: {node: '>=14.16'} dependencies: defer-to-connect: 2.0.1 - dev: false /@tabler/icons-webfont/2.10.0: resolution: {integrity: sha512-5WvGhztlM3la7NWf8Y6ktT+KD7zb/Hz/zdMeFjExXvEFupGvuANEnbGo1wXI4ADdSWUaRDtnQHcSGIjZ+gZ+OQ==} @@ -2847,7 +2849,6 @@ packages: /@types/http-cache-semantics/4.0.1: resolution: {integrity: sha512-SZs7ekbP8CN0txVG2xVRH6EgKmEm31BOxA07vkFaETzZz1xh+cbt8BcI0slpymvwhx5dlFnQG2rTlPVQn+iRPQ==} - dev: false /@types/ioredis/4.28.10: resolution: {integrity: sha512-69LyhUgrXdgcNDv7ogs1qXZomnfOEnSmrmMFqKgt1XMJxmoOSG/u3wYy13yACIfKuMJ8IhKgHafDO3sx19zVQQ==} @@ -4240,7 +4241,7 @@ packages: /axios/0.24.0: resolution: {integrity: sha512-Q6cWsys88HoPgAaFAVUb0WpPk0O8iTeisR9IMqy9G8AbO4NlpVknrnQS03zzF9PGAWgO3cgletO3VjV/P7VztA==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2_debug@4.3.4 transitivePeerDependencies: - debug dev: false @@ -4248,7 +4249,7 @@ packages: /axios/0.27.2_debug@4.3.4: resolution: {integrity: sha512-t+yRIyySRTp/wua5xEr+z1q60QmLq8ABsS5O9Me1AsE5dfKqgnCFzwiCZZ/cGNd1lq4/7akDWMxdhVlucjmnOQ==} dependencies: - follow-redirects: 1.15.2 + follow-redirects: 1.15.2_debug@4.3.4 form-data: 4.0.0 transitivePeerDependencies: - debug @@ -4694,7 +4695,6 @@ packages: /cacheable-lookup/7.0.0: resolution: {integrity: sha512-+qJyx4xiKra8mZrcwhjMRMUhD5NR1R8esPkzIYxX96JiecFoxAXFuz/GpR3+ev4PE1WamHip78wV0vcmPQtp8w==} engines: {node: '>=14.16'} - dev: false /cacheable-request/10.2.8: resolution: {integrity: sha512-IDVO5MJ4LItE6HKFQTqT2ocAQsisOoCTUDu1ddCmnhyiwFQjXNPp4081Xj23N4tO+AFEFNzGuNEf/c8Gwwt15A==} @@ -4707,7 +4707,6 @@ packages: mimic-response: 4.0.0 normalize-url: 8.0.0 responselike: 3.0.0 - dev: false /cacheable-request/7.0.2: resolution: {integrity: sha512-pouW8/FmiPQbuGpkXQ9BAPv/Mo5xDGANgSNXzTzJ8DrKGuXOssM4wIQRjfanNRh3Yu5cfYPvcorqbhg2KIJtew==} @@ -4923,7 +4922,6 @@ packages: domelementtype: 2.3.0 domhandler: 5.0.3 domutils: 3.0.1 - dev: false /cheerio/1.0.0-rc.12: resolution: {integrity: sha512-VqR8m68vM46BNnuZ5NtnGBKIE/DfN0cRIzg9n40EIq9NOv90ayxLBXA8fXC5gquFRGJSTRqBq25Jt2ECLR431Q==} @@ -4936,7 +4934,6 @@ packages: htmlparser2: 8.0.1 parse5: 7.1.2 parse5-htmlparser2-tree-adapter: 7.0.0 - dev: false /chokidar/3.5.3: resolution: {integrity: sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==} @@ -5416,12 +5413,10 @@ packages: domhandler: 5.0.3 domutils: 3.0.1 nth-check: 2.1.1 - dev: false /css-what/6.1.0: resolution: {integrity: sha512-HTUrgRJ7r4dsZKU6GjmpfRK1O76h97Z8MfS1G0FozR+oF2kG6Vfe8JE6zwrkbxigziPHinCJ+gCPjA9EaBDtRw==} engines: {node: '>= 6'} - dev: false /css.escape/1.5.1: resolution: {integrity: sha512-YUifsXXuknHlUsmlgyY0PKzgPOr7/FjCePfHNt0jxm83wHZi44VDMQ7/fGNkjY3/jV1MC+1CmZbaHzugyeRtpg==} @@ -5704,7 +5699,6 @@ packages: engines: {node: '>=10'} dependencies: mimic-response: 3.1.0 - dev: false /dedent/0.7.0: resolution: {integrity: sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==} @@ -5777,7 +5771,6 @@ packages: /defer-to-connect/2.0.1: resolution: {integrity: sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg==} engines: {node: '>=10'} - dev: false /define-properties/1.1.4: resolution: {integrity: sha512-uckOqKcfaVvtBdsVkdPv3XjveQJsNQqmhXgRi8uhvWWuPYZCNlzT8qAyblUgNoXdHdjMTzAqeGjAoli8f+bzPA==} @@ -6417,7 +6410,6 @@ packages: /escape-regexp/0.0.1: resolution: {integrity: sha512-jVgdsYRa7RKxTT6MKNC3gdT+BF0Gfhpel19+HMRZJC2L0PufB0XOBuXBoXj29NKHwuktnAXd1Z1lyiH/8vOTpw==} - dev: false /escape-string-regexp/1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} @@ -7406,7 +7398,7 @@ packages: readable-stream: 2.3.7 dev: false - /follow-redirects/1.15.2: + /follow-redirects/1.15.2_debug@4.3.4: resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==} engines: {node: '>=4.0'} peerDependencies: @@ -7414,6 +7406,8 @@ packages: peerDependenciesMeta: debug: optional: true + dependencies: + debug: 4.3.4 /for-each/0.3.3: resolution: {integrity: sha512-jqYfLp7mo9vIyQf8ykW2v7A+2N4QjeCeI5+Dz9XraiO1ign81wjiH7Fb9vSOWvQfNtmSa4H2RoQTrrXivdUZmw==} @@ -7446,7 +7440,6 @@ packages: /form-data-encoder/2.1.4: resolution: {integrity: sha512-yDYSgNMraqvnxiEXO4hi88+YZxaHC6QKzb5N84iRCTDeRO7ZALpir/lVmf/uXUhnwUr2O4HU8s/n6x+yNjQkHw==} engines: {node: '>= 14.17'} - dev: false /form-data/2.3.3: resolution: {integrity: sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==} @@ -7901,7 +7894,6 @@ packages: lowercase-keys: 3.0.0 p-cancelable: 3.0.0 responselike: 3.0.0 - dev: false /graceful-fs/4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} @@ -8164,7 +8156,6 @@ packages: /html-entities/2.3.2: resolution: {integrity: sha512-c3Ab/url5ksaT0WyleslpBEthOzWhrjQbg75y7XUsfSzi3Dgzt0l8w5e7DylRn15MTlMMD58dTfzddNS2kcAjQ==} - dev: false /html-escaper/2.0.2: resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==} @@ -8180,7 +8171,6 @@ packages: /http-cache-semantics/4.1.1: resolution: {integrity: sha512-er295DKPVsV82j5kw1Gjt+ADA/XYHsajl82cGNQG2eyoPkvgUhX+nDIyelzhIWbbsXP39EHcI6l5tYs2FYqYXQ==} - dev: false /http-errors/2.0.0: resolution: {integrity: sha512-FtwrG/euBzaEjYeRqOgly7G0qviiXoJWnvEH2Z1plBdXgbyjv34pHTSb9zoeHMyDy33+DWy5Wt9Wo+TURtOYSQ==} @@ -8236,7 +8226,6 @@ packages: dependencies: quick-lru: 5.1.1 resolve-alpn: 1.2.1 - dev: false /http_ece/1.1.0: resolution: {integrity: sha512-bptAfCDdPJxOs5zYSe7Y3lpr772s1G346R4Td5LgRUeCwIGpCGDUTJxRrhTNcAXbx37spge0kWEIH7QAYWNTlA==} @@ -8464,7 +8453,6 @@ packages: /ip-regex/4.3.0: resolution: {integrity: sha512-B9ZWJxHHOHUhUjCPrMpLD4xEq35bUTClHM1S6CBU5ixQnkZmwipwgc96vAd7AAGM9TGHvJR+Uss+/Ak6UphK+Q==} engines: {node: '>=8'} - dev: false /ip-regex/5.0.0: resolution: {integrity: sha512-fOCG6lhoKKakwv+C6KdsOnGvgXnmgfmp0myi3bcNwj3qfwPAxRKWEuFhvEFF7ceYIz6+1jRZ+yguLFAmUNPEfw==} @@ -8483,7 +8471,6 @@ packages: /ipaddr.js/2.0.1: resolution: {integrity: sha512-1qTgH9NG+IIJ4yfKs2e6Pp1bZg8wbDbKHT21HrLIeYBTRLgMYKnMTPAuI3Lcs61nfx5h1xlXnbJtH1kX5/d/ng==} engines: {node: '>= 10'} - dev: false /irregular-plurals/3.5.0: resolution: {integrity: sha512-1ANGLZ+Nkv1ptFb2pa8oG8Lem4krflKuX/gINiHJHjJUKaJHk/SXk5x6K3J+39/p0h1RQ2saROclJJ+QLvETCQ==} @@ -8699,7 +8686,6 @@ packages: engines: {node: '>=8'} dependencies: ip-regex: 4.3.0 - dev: false /is-lambda/1.0.1: resolution: {integrity: sha512-z7CMFGNrENq5iFB9Bqo64Xk6Y9sg+epq1myIcdHaGnbMTYOxvzsEtdYqQUylB7LxfkvgrrjP32T6Ywciio9UIQ==} @@ -9606,7 +9592,6 @@ packages: /jschardet/3.0.0: resolution: {integrity: sha512-lJH6tJ77V8Nzd5QWRkFYCLc13a3vADkh3r/Fi8HupZGWk2OVVDfnZP8V/VgQgZ+lzW0kG2UGb5hFgt3V3ndotQ==} engines: {node: '>=0.1.90'} - dev: false /jsdom/21.1.0: resolution: {integrity: sha512-m0lzlP7qOtthD918nenK3hdItSd2I+V3W9IrBcB36sqDwG+KnUs66IF5GY7laGWUnlM9vTsD0W1QwSEBYWWcJg==} @@ -9657,7 +9642,6 @@ packages: /json-buffer/3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} - dev: false /json-parse-even-better-errors/2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} @@ -9786,7 +9770,6 @@ packages: resolution: {integrity: sha512-5MHbFaKn8cNSmVW7BYnijeAVlE4cYA/SVkifVgrh7yotnfhKmjuXpDKjrABLnT0SfHWV21P8ow07OGfRrNDg8g==} dependencies: json-buffer: 3.0.1 - dev: false /kind-of/3.2.2: resolution: {integrity: sha512-NOW9QQXMoZGg/oqnVNoNTTIFEIid1627WCffUBJEdMxYApq7mNE7CpzucIPc+ZQg25Phej7IJSmX3hO+oblOtQ==} @@ -10059,7 +10042,6 @@ packages: /lowercase-keys/3.0.0: resolution: {integrity: sha512-ozCC6gdQ+glXOQsveKD0YsDy8DSQFjDTz4zyzEHNV5+JP5D62LmfDZ6o1cycFx9ouG940M5dE8C8CTewdj2YWQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: false /lru-cache/4.1.5: resolution: {integrity: sha512-sWZlbEP2OsHNkXrMl5GYk/jKk70MBng6UU4YI/qGDYbgf6YbP4EvmqISbXCoJiRKs+1bSpFHVgQxvJ17F2li5g==} @@ -10287,12 +10269,10 @@ packages: /mimic-response/3.1.0: resolution: {integrity: sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==} engines: {node: '>=10'} - dev: false /mimic-response/4.0.0: resolution: {integrity: sha512-e5ISH9xMYU0DzrT+jl8q2ze9D6eWBto+I8CNpe+VI+K2J/F/k3PdkdTdz4wvGVH4NTpo+NRYTVIuMQEMMcsLqg==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - dev: false /min-indent/1.0.1: resolution: {integrity: sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==} @@ -10602,7 +10582,6 @@ packages: /netmask/2.0.2: resolution: {integrity: sha512-dBpDMdxv9Irdq66304OLfEmQ9tbNRFnFTuZiLo+bD+r332bBmMJ8GBLXklIXXgxd3+v9+KUnZaUR5PJMa75Gsg==} engines: {node: '>= 0.4.0'} - dev: false /next-tick/1.1.0: resolution: {integrity: sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==} @@ -10758,7 +10737,6 @@ packages: /normalize-url/8.0.0: resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==} engines: {node: '>=14.16'} - dev: false /now-and-later/2.0.1: resolution: {integrity: sha512-KGvQ0cB70AQfg107Xvs/Fbu+dGmZoTRJp2TaPwcwQm3/7PteUyN2BCgk8KBMPGBUXZdVwyWS8fDCGFygBm19UQ==} @@ -11038,7 +11016,6 @@ packages: /p-cancelable/3.0.0: resolution: {integrity: sha512-mlVgR3PGuzlo0MmTdk4cXqXWlwQDLnONTAg6sm62XkMJEiRxN3GL3SffkYvqwonbkJBcrI7Uvv5Zh9yjvn2iUw==} engines: {node: '>=12.20'} - dev: false /p-finally/1.0.0: resolution: {integrity: sha512-LICb2p9CB7FS+0eR1oqWnHhp0FljGLZCWBE9aix0Uye9W8LTQPwMTYVGWQWIw9RdQiDg4+epXQODwIYJtSJaow==} @@ -11175,7 +11152,6 @@ packages: dependencies: domhandler: 5.0.3 parse5: 7.1.2 - dev: false /parse5/5.1.1: resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} @@ -11791,7 +11767,6 @@ packages: ipaddr.js: 2.0.1 is-ip: 3.1.0 netmask: 2.0.2 - dev: false /private-ip/3.0.0: resolution: {integrity: sha512-HkMBs4nMtrP+cvcw0bDi2BAZIGgiKI4Zq8Oc+dMqNBpHS8iGL4+WO/pRtc8Bwnv9rjnV0QwMDwEBymFtqv7Kww==} @@ -12099,7 +12074,6 @@ packages: /quick-lru/5.1.1: resolution: {integrity: sha512-WuyALRjWPDGtt/wzJiadO5AXY+8hZ80hVpe6MyivgraREW751X3SbhRvG3eLKOYN+8VEvqLcf3wdnt44Z4S4SA==} engines: {node: '>=10'} - dev: false /random-seed/0.3.0: resolution: {integrity: sha512-y13xtn3kcTlLub3HKWXxJNeC2qK4mB59evwZ5EkeRlolx+Bp2ztF7LbcZmyCnOqlHQrLnfuNbi1sVmm9lPDlDA==} @@ -12470,7 +12444,6 @@ packages: /resolve-alpn/1.2.1: resolution: {integrity: sha512-0a1F4l73/ZFZOakJnQ3FvkJ2+gSTQWz/r2KE5OdDY0TxPm5h4GkqkWWfM47T7HsbnOtcJVEF4epCVy6u7Q3K+g==} - dev: false /resolve-cwd/3.0.0: resolution: {integrity: sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==} @@ -12539,7 +12512,6 @@ packages: engines: {node: '>=14.16'} dependencies: lowercase-keys: 3.0.0 - dev: false /restore-cursor/3.1.0: resolution: {integrity: sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==} @@ -13706,7 +13678,6 @@ packages: /trace-redirect/1.0.6: resolution: {integrity: sha512-UUfa1DjjU5flcjMdaFIiIEGDTyu2y/IiMjOX4uGXa7meKBS4vD4f2Uy/tken9Qkd4Jsm4sRsfZcIIPqrRVF3Mg==} - dev: false /traverse/0.3.9: resolution: {integrity: sha512-iawgk0hLP3SxGKDfnDJf8wTz4p2qImnyihM5Hh/sGvQ3K37dPi/w8sRhdNIxYA1TwFwc5mDhIJq+O0RsvXBKdQ==} @@ -14425,6 +14396,18 @@ packages: optionalDependencies: fsevents: 2.3.2 + /vitest-fetch-mock/0.2.2_vitest@0.29.2: + resolution: {integrity: sha512-XmH6QgTSjCWrqXoPREIdbj40T7i1xnGmAsTAgfckoO75W1IEHKR8hcPCQ7SO16RsdW1t85oUm6pcQRLeBgjVYQ==} + engines: {node: '>=14.14.0'} + peerDependencies: + vitest: '>=0.16.0' + dependencies: + cross-fetch: 3.1.5 + vitest: 0.29.2_zcjcryjt4bqcdu7ggonulipgea + transitivePeerDependencies: + - encoding + dev: true + /vitest/0.29.2_zcjcryjt4bqcdu7ggonulipgea: resolution: {integrity: sha512-ydK9IGbAvoY8wkg29DQ4ivcVviCaUi3ivuPKfZEVddMTenFHUfB8EEDXQV8+RasEk1ACFLgMUqAaDuQ/Nk+mQA==} engines: {node: '>=v14.16.0'} @@ -15013,7 +14996,6 @@ packages: jschardet: 3.0.0 private-ip: 2.3.3 trace-redirect: 1.0.6 - dev: false github.com/sampotts/plyr/d434c9af16e641400aaee93188594208d88f2658: resolution: {tarball: https://codeload.github.com/sampotts/plyr/tar.gz/d434c9af16e641400aaee93188594208d88f2658}