summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--.config/docker_example.yml19
-rw-r--r--.config/example.yml15
-rw-r--r--.devcontainer/devcontainer.json6
-rw-r--r--.devcontainer/devcontainer.yml15
-rwxr-xr-x.devcontainer/init.sh2
-rw-r--r--CHANGELOG.md22
-rw-r--r--chart/files/default.yml16
-rw-r--r--healthcheck.sh2
-rw-r--r--locales/ar-SA.yml18
-rw-r--r--locales/bn-BD.yml5
-rw-r--r--locales/ca-ES.yml29
-rw-r--r--locales/cs-CZ.yml10
-rw-r--r--locales/da-DK.yml3
-rw-r--r--locales/de-DE.yml8
-rw-r--r--locales/el-GR.yml1
-rw-r--r--locales/en-US.yml56
-rw-r--r--locales/es-ES.yml67
-rw-r--r--locales/fr-FR.yml46
-rw-r--r--locales/hr-HR.yml1
-rw-r--r--locales/ht-HT.yml1
-rw-r--r--locales/hu-HU.yml1
-rw-r--r--locales/id-ID.yml153
-rw-r--r--locales/index.d.ts76
-rw-r--r--locales/it-IT.yml144
-rw-r--r--locales/ja-JP.yml24
-rw-r--r--locales/ja-KS.yml81
-rw-r--r--locales/jbo-EN.yml1
-rw-r--r--locales/kab-KAB.yml1
-rw-r--r--locales/kn-IN.yml1
-rw-r--r--locales/ko-GS.yml91
-rw-r--r--locales/ko-KR.yml231
-rw-r--r--locales/lo-LA.yml5
-rw-r--r--locales/nl-NL.yml5
-rw-r--r--locales/no-NO.yml3
-rw-r--r--locales/pl-PL.yml163
-rw-r--r--locales/pt-PT.yml19
-rw-r--r--locales/ro-RO.yml5
-rw-r--r--locales/ru-RU.yml21
-rw-r--r--locales/si-LK.yml19
-rw-r--r--locales/sk-SK.yml5
-rw-r--r--locales/sv-SE.yml5
-rw-r--r--locales/th-TH.yml53
-rw-r--r--locales/tr-TR.yml5
-rw-r--r--locales/ug-CN.yml1
-rw-r--r--locales/uk-UA.yml5
-rw-r--r--locales/uz-UZ.yml5
-rw-r--r--locales/verify.js53
-rw-r--r--locales/vi-VN.yml81
-rw-r--r--locales/zh-CN.yml71
-rw-r--r--locales/zh-TW.yml175
-rw-r--r--package.json2
-rw-r--r--packages/backend/migration/1716129964060-ChannelIdDenormalizedForMiPoll.js21
-rw-r--r--packages/backend/migration/1716345015347-NotRespondingSince.js16
-rw-r--r--packages/backend/migration/1716447138870-SuspensionStateInsteadOfIsSspended.js50
-rw-r--r--packages/backend/migration/1716450883149-RemoveAntennaNotify.js16
-rw-r--r--packages/backend/migration/1717117195275-inquiryUrl.js16
-rw-r--r--packages/backend/package.json6
-rw-r--r--packages/backend/src/boot/entry.ts3
-rw-r--r--packages/backend/src/boot/master.ts20
-rw-r--r--packages/backend/src/boot/ready.ts6
-rw-r--r--packages/backend/src/config.ts7
-rw-r--r--packages/backend/src/core/AnnouncementService.ts49
-rw-r--r--packages/backend/src/core/CoreModule.ts6
-rw-r--r--packages/backend/src/core/CustomEmojiService.ts7
-rw-r--r--packages/backend/src/core/DriveService.ts14
-rw-r--r--packages/backend/src/core/FanoutTimelineEndpointService.ts13
-rw-r--r--packages/backend/src/core/FetchInstanceMetadataService.ts2
-rw-r--r--packages/backend/src/core/NoteCreateService.ts1
-rw-r--r--packages/backend/src/core/activitypub/ApInboxService.ts117
-rw-r--r--packages/backend/src/core/activitypub/models/ApNoteService.ts8
-rw-r--r--packages/backend/src/core/activitypub/type.ts1
-rw-r--r--packages/backend/src/core/entities/AbuseUserReportEntityService.ts34
-rw-r--r--packages/backend/src/core/entities/AnnouncementEntityService.ts71
-rw-r--r--packages/backend/src/core/entities/AntennaEntityService.ts2
-rw-r--r--packages/backend/src/core/entities/BlockingEntityService.ts14
-rw-r--r--packages/backend/src/core/entities/ClipEntityService.ts12
-rw-r--r--packages/backend/src/core/entities/DriveFileEntityService.ts10
-rw-r--r--packages/backend/src/core/entities/FlashEntityService.ts14
-rw-r--r--packages/backend/src/core/entities/FollowRequestEntityService.ts27
-rw-r--r--packages/backend/src/core/entities/FollowingEntityService.ts24
-rw-r--r--packages/backend/src/core/entities/GalleryPostEntityService.ts12
-rw-r--r--packages/backend/src/core/entities/InstanceEntityService.ts3
-rw-r--r--packages/backend/src/core/entities/InviteCodeEntityService.ts25
-rw-r--r--packages/backend/src/core/entities/MetaEntityService.ts1
-rw-r--r--packages/backend/src/core/entities/ModerationLogEntityService.ts17
-rw-r--r--packages/backend/src/core/entities/MutingEntityService.ts14
-rw-r--r--packages/backend/src/core/entities/NoteEntityService.ts12
-rw-r--r--packages/backend/src/core/entities/NoteReactionEntityService.ts11
-rw-r--r--packages/backend/src/core/entities/PageEntityService.ts12
-rw-r--r--packages/backend/src/core/entities/RenoteMutingEntityService.ts14
-rw-r--r--packages/backend/src/core/entities/ReversiGameEntityService.ts66
-rw-r--r--packages/backend/src/core/entities/UserListEntityService.ts5
-rw-r--r--packages/backend/src/misc/json-schema.ts2
-rw-r--r--packages/backend/src/models/Antenna.ts3
-rw-r--r--packages/backend/src/models/Instance.ts17
-rw-r--r--packages/backend/src/models/Meta.ts6
-rw-r--r--packages/backend/src/models/Poll.ts9
-rw-r--r--packages/backend/src/models/json-schema/antenna.ts9
-rw-r--r--packages/backend/src/models/json-schema/federation-instance.ts5
-rw-r--r--packages/backend/src/models/json-schema/meta.ts4
-rw-r--r--packages/backend/src/queue/processors/DeliverProcessorService.ts14
-rw-r--r--packages/backend/src/queue/processors/ExportAntennasProcessorService.ts1
-rw-r--r--packages/backend/src/queue/processors/ImportAntennasProcessorService.ts4
-rw-r--r--packages/backend/src/queue/processors/InboxProcessorService.ts15
-rw-r--r--packages/backend/src/server/HealthServerService.ts54
-rw-r--r--packages/backend/src/server/NodeinfoServerService.ts13
-rw-r--r--packages/backend/src/server/ServerModule.ts2
-rw-r--r--packages/backend/src/server/ServerService.ts3
-rw-r--r--packages/backend/src/server/api/ApiCallService.ts77
-rw-r--r--packages/backend/src/server/api/ApiServerService.ts2
-rw-r--r--packages/backend/src/server/api/EndpointsModule.ts4
-rw-r--r--packages/backend/src/server/api/endpoints.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/admin/meta.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/admin/roles/users.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/admin/show-users.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/admin/update-meta.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/announcements.ts11
-rw-r--r--packages/backend/src/server/api/endpoints/announcements/show.ts54
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/create.ts4
-rw-r--r--packages/backend/src/server/api/endpoints/antennas/update.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/drive/files/find.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/following/requests/list.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/i/notifications.ts60
-rw-r--r--packages/backend/src/server/api/endpoints/i/update.ts36
-rw-r--r--packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts7
-rw-r--r--packages/backend/src/server/api/endpoints/notes/reactions.ts2
-rw-r--r--packages/backend/src/server/api/endpoints/roles/users.ts5
-rw-r--r--packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts10
-rw-r--r--packages/backend/src/server/api/endpoints/users/show.ts10
-rw-r--r--packages/backend/src/server/web/ClientServerService.ts27
-rw-r--r--packages/backend/src/server/web/views/base.pug3
-rw-r--r--packages/backend/test/e2e/antennas.ts5
-rw-r--r--packages/backend/test/e2e/move.ts2
-rw-r--r--packages/backend/test/unit/AnnouncementService.ts2
-rw-r--r--packages/frontend/.storybook/preview-head.html2
-rw-r--r--packages/frontend/package.json4
-rw-r--r--packages/frontend/src/cache.ts1
-rw-r--r--packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue108
-rw-r--r--packages/frontend/src/components/MkFormDialog.file.vue71
-rw-r--r--packages/frontend/src/components/MkFormDialog.vue12
-rw-r--r--packages/frontend/src/components/MkModal.vue5
-rw-r--r--packages/frontend/src/components/MkPostForm.vue27
-rw-r--r--packages/frontend/src/components/global/MkAd.stories.impl.ts8
-rw-r--r--packages/frontend/src/os.ts2
-rw-r--r--packages/frontend/src/pages/admin/federation.vue14
-rw-r--r--packages/frontend/src/pages/admin/files.vue27
-rw-r--r--packages/frontend/src/pages/admin/index.vue19
-rw-r--r--packages/frontend/src/pages/admin/moderation.vue9
-rw-r--r--packages/frontend/src/pages/admin/users.vue2
-rw-r--r--packages/frontend/src/pages/announcement.vue142
-rw-r--r--packages/frontend/src/pages/announcements.vue25
-rw-r--r--packages/frontend/src/pages/channel.vue3
-rw-r--r--packages/frontend/src/pages/contact.vue18
-rw-r--r--packages/frontend/src/pages/explore.featured.vue3
-rw-r--r--packages/frontend/src/pages/instance-info.vue29
-rw-r--r--packages/frontend/src/pages/my-antennas/editor.vue3
-rw-r--r--packages/frontend/src/pages/settings/general.vue10
-rw-r--r--packages/frontend/src/pages/share.vue29
-rw-r--r--packages/frontend/src/pages/timeline.vue6
-rw-r--r--packages/frontend/src/router/definition.ts3
-rw-r--r--packages/frontend/src/scripts/admin-lookup.ts (renamed from packages/frontend/src/scripts/lookup-user.ts)23
-rw-r--r--packages/frontend/src/scripts/collapsed.ts19
-rw-r--r--packages/frontend/src/scripts/form.ts30
-rw-r--r--packages/frontend/src/scripts/get-note-menu.ts38
-rw-r--r--packages/frontend/src/ui/_common_/announcements.vue2
-rw-r--r--packages/frontend/src/ui/deck/antenna-column.vue24
-rw-r--r--packages/frontend/src/ui/deck/channel-column.vue37
-rw-r--r--packages/frontend/src/ui/deck/deck-store.ts2
-rw-r--r--packages/frontend/src/ui/deck/list-column.vue22
-rw-r--r--packages/frontend/src/ui/deck/role-timeline-column.vue23
-rw-r--r--packages/frontend/src/ui/deck/tl-column.vue20
-rw-r--r--packages/frontend/src/ui/deck/tl-note-notification.ts107
-rw-r--r--packages/misskey-js/etc/misskey-js.api.md8
-rw-r--r--packages/misskey-js/package.json6
-rw-r--r--packages/misskey-js/src/autogen/apiClientJSDoc.ts13
-rw-r--r--packages/misskey-js/src/autogen/endpoint.ts3
-rw-r--r--packages/misskey-js/src/autogen/entities.ts2
-rw-r--r--packages/misskey-js/src/autogen/types.ts79
-rw-r--r--packages/misskey-js/src/consts.ts1
-rw-r--r--pnpm-lock.yaml762
181 files changed, 4056 insertions, 890 deletions
diff --git a/.config/docker_example.yml b/.config/docker_example.yml
index 296237c95d..c22bd83c2e 100644
--- a/.config/docker_example.yml
+++ b/.config/docker_example.yml
@@ -163,7 +163,7 @@ redis:
# ┌───────────────────────────â”
#───┘ MeiliSearch configuration └─────────────────────────────
-# You can set scope to local (default value) or global
+# You can set scope to local (default value) or global
# (include notes from remote).
#meilisearch:
@@ -193,6 +193,21 @@ redis:
id: 'aidx'
+# ┌────────────────â”
+#───┘ Error tracking └──────────────────────────────────────────
+
+# Sentry is available for error tracking.
+# See the Sentry documentation for more details on options.
+
+#sentryForBackend:
+# enableNodeProfiling: true
+# options:
+# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
+#sentryForFrontend:
+# options:
+# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
# ┌─────────────────────â”
#───┘ Other configuration └─────────────────────────────────────
@@ -261,7 +276,7 @@ signToActivityPubGet: true
checkActivityPubGetSignature: false
# For security reasons, uploading attachments from the intranet is prohibited,
-# but exceptions can be made from the following settings. Default value is "undefined".
+# but exceptions can be made from the following settings. Default value is "undefined".
# Read changelog to learn more (Improvements of 12.90.0 (2021/09/04)).
#allowedPrivateNetworks: [
# '127.0.0.1/32'
diff --git a/.config/example.yml b/.config/example.yml
index 73ee1c5346..9126bdfd91 100644
--- a/.config/example.yml
+++ b/.config/example.yml
@@ -205,6 +205,21 @@ redis:
id: 'aidx'
+# ┌────────────────â”
+#───┘ Error tracking └──────────────────────────────────────────
+
+# Sentry is available for error tracking.
+# See the Sentry documentation for more details on options.
+
+#sentryForBackend:
+# enableNodeProfiling: true
+# options:
+# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
+#sentryForFrontend:
+# options:
+# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
# ┌─────────────────────â”
#───┘ Other configuration └─────────────────────────────────────
diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json
index 182ee2fbb2..31b6212cb5 100644
--- a/.devcontainer/devcontainer.json
+++ b/.devcontainer/devcontainer.json
@@ -4,12 +4,10 @@
"service": "app",
"workspaceFolder": "/workspace",
"features": {
- "ghcr.io/devcontainers-contrib/features/pnpm:2": {
- "version": "8.9.2"
- },
"ghcr.io/devcontainers/features/node:1": {
"version": "20.12.2"
- }
+ },
+ "ghcr.io/devcontainers-contrib/features/corepack:1": {}
},
"forwardPorts": [3000],
"postCreateCommand": "sudo chmod 755 .devcontainer/init.sh && .devcontainer/init.sh",
diff --git a/.devcontainer/devcontainer.yml b/.devcontainer/devcontainer.yml
index 7ea0929469..beefcfd0a2 100644
--- a/.devcontainer/devcontainer.yml
+++ b/.devcontainer/devcontainer.yml
@@ -132,6 +132,21 @@ redis:
id: 'aidx'
+# ┌────────────────â”
+#───┘ Error tracking └──────────────────────────────────────────
+
+# Sentry is available for error tracking.
+# See the Sentry documentation for more details on options.
+
+#sentryForBackend:
+# enableNodeProfiling: true
+# options:
+# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
+#sentryForFrontend:
+# options:
+# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
# ┌─────────────────────â”
#───┘ Other configuration └─────────────────────────────────────
diff --git a/.devcontainer/init.sh b/.devcontainer/init.sh
index bcad3e6d85..729e1a9d2d 100755
--- a/.devcontainer/init.sh
+++ b/.devcontainer/init.sh
@@ -4,6 +4,8 @@ set -xe
sudo chown -R node /workspace
git submodule update --init
+corepack install
+corepack enable
pnpm config set store-dir /home/node/.local/share/pnpm/store
pnpm install --frozen-lockfile
cp .devcontainer/devcontainer.yml .config/default.yml
diff --git a/CHANGELOG.md b/CHANGELOG.md
index bc00d5975c..9f78ba677d 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -3,8 +3,10 @@
### Note
- コントロールパãƒãƒ«å†…ã«ã‚るサマリープロキシã®è¨­å®šå€‹æ‰€ãŒã‚»ã‚­ãƒ¥ãƒªãƒ†ã‚£ã‹ã‚‰å…¨èˆ¬ã¸å¤‰æ›´ã¨ãªã‚Šã¾ã™ã€‚
- 悪æ„ã®ã‚る第三者ãŒãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã«ãªã‚Šã™ã¾ã—ãŸã‚¢ã‚¯ãƒ†ã‚£ãƒ“ティをå—ã‘å–れã¦ã—ã¾ã†å•題を修正ã—ã¾ã—ãŸã€‚詳ã—ãã¯[GitHub security advisory](https://github.com/misskey-dev/misskey/security/advisories/GHSA-2vxv-pv3m-3wvj)ã‚’ã”覧ãã ã•ã„。
+- 管ç†è€…å‘ã‘æ¨©é™ `read:admin:show-users` 㯠`read:admin:show-user` ã«çµ±åˆã•れã¾ã—ãŸã€‚å¿…è¦ã«å¿œã˜ã¦APIトークンをå†ç™ºè¡Œã—ã¦ãã ã•ã„。
### General
+- Feat: エラートラッキングã«Sentryを使用ã§ãるよã†ã«ãªã‚Šã¾ã—ãŸ
- Enhance: URLãƒ—ãƒ¬ãƒ“ãƒ¥ãƒ¼ã®æœ‰åŠ¹åŒ–ãƒ»ç„¡åŠ¹åŒ–ã‚’è¨­å®šã§ãるよã†ã« #13569
- Enhance: アンテナã§Botã«ã‚ˆã‚‹ãƒŽãƒ¼ãƒˆã‚’除外ã§ãるよã†ã«
(Cherry-picked from https://github.com/MisskeyIO/misskey/pull/545)
@@ -15,11 +17,18 @@
- サスペンド済ã¿ãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹
- éµã‚¢ã‚«ã‚¦ãƒ³ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹
- 「アカウントを見ã¤ã‘ã‚„ã™ãã™ã‚‹ã€ãŒæœ‰åйãªãƒ¦ãƒ¼ã‚¶ãƒ¼ã‹
+- Enhance: Goneを出ã•ãšã«çµ‚了ã—ãŸã‚µãƒ¼ãƒãƒ¼ã¸ã®é…ä¿¡åœæ­¢ã‚’自動的ã«è¡Œã†ã‚ˆã†ã«
+ - ã‚‚ã—ãã®ã‚ˆã†ãªã‚µãƒ¼ãƒãƒ¼ã‹ã‚‰ã‹ã‚‰é…ä¿¡ãŒå±Šã„ãŸå ´åˆã«ã¯è‡ªå‹•çš„ã«é…ä¿¡ã‚’å†é–‹ã—ã¾ã™
+- Enhance: é…ä¿¡åœæ­¢ã®ç†ç”±ã‚’表示ã™ã‚‹ã‚ˆã†ã«
+- Enhance: サーãƒãƒ¼ã®ãŠå•ã„åˆã‚ã›å…ˆURLを設定ã§ãるよã†ã«ãªã‚Šã¾ã—ãŸ
- Fix: Playä½œæˆæ™‚ã«è¨­å®šã—ãŸå…¬é–‹ç¯„å›²ãŒæ©Ÿèƒ½ã—ã¦ã„ãªã„å•題を修正
- Fix: æ­£è¦åŒ–ã•れã¦ã„ãªã„状態ã®hashtagãŒé€£åˆã•れã¦ããŸhtmlã«å«ã¾ã‚Œã¦ã„ã‚‹ã¨hashtagãŒæ­£ã—ãhashtagã«å¾©å…ƒã•れãªã„å•題を修正
+- Fix: ã¿ã¤ã‘ã‚‹ã®ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆæ¬„ã«ã¦ãƒãƒ£ãƒ³ãƒãƒ«ã®ã‚¢ãƒ³ã‚±ãƒ¼ãƒˆãŒå«ã¾ã‚Œã¦ã—ã¾ã†å•題を修正
### Client
- Feat: アップロードã™ã‚‹ãƒ•ァイルã®åå‰ã‚’ランダム文字列ã«ã§ãるよã†ã«
+- Feat: 個別ã®ãŠçŸ¥ã‚‰ã›ã«ãƒªãƒ³ã‚¯ã§é£›ã¹ã‚‹ã‚ˆã†ã«
+ (Based on https://github.com/MisskeyIO/misskey/pull/639)
- Enhance: 自分ã®ãƒŽãƒ¼ãƒˆã®æ·»ä»˜ãƒ•ァイルã‹ã‚‰ç›´æŽ¥ãƒ•ァイルã®è©³ç´°ãƒšãƒ¼ã‚¸ã«é£›ã¹ã‚‹ã‚ˆã†ã«
- Enhance: 広告ãŒMisskeyã¨åŒä¸€ãƒ‰ãƒ¡ã‚¤ãƒ³ã®å ´åˆã¯Routerã§é·ç§»ã™ã‚‹ã‚ˆã†ã«
- Enhance: リアクション・ã„ã„ã­ã®ç·æ•°ã‚’表示ã™ã‚‹ã‚ˆã†ã«
@@ -40,6 +49,11 @@
- Enhance: 通報ã®ã‚³ãƒ¡ãƒ³ãƒˆå†…ã®ãƒªãƒ³ã‚¯ã‚’クリックã—ãŸéš›ã€ã‚¦ã‚£ãƒ³ãƒ‰ã‚¦ã§é–‹ãよã†ã«
- Enhance: `Ui:C:postForm` ãŠã‚ˆã³ `Ui:C:postFormButton` ã« `localOnly` 㨠`visibility` を設定ã§ãるよã†ã«
- Enhance: AiScriptã‚’0.18.0ã«ãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚¢ãƒƒãƒ—
+- Enhance: 通常ã®ãƒŽãƒ¼ãƒˆã§ã‚‚ã€ãŠæ°—ã«å…¥ã‚Šã«ç™»éŒ²ã—ãŸãƒãƒ£ãƒ³ãƒãƒ«ã«ãƒªãƒŽãƒ¼ãƒˆã§ãるよã†ã«
+- Enhance: é•·ã„テキストをペーストã—ãŸéš›ã«ãƒ†ã‚­ã‚¹ãƒˆãƒ•ァイルã¨ã—ã¦æ·»ä»˜ã™ã‚‹ã‹ã©ã†ã‹ã‚’é¸æŠžã§ãるよã†ã«
+- Enhance: æ–°ç€ãƒŽãƒ¼ãƒˆã‚’サウンドã§é€šçŸ¥ã™ã‚‹æ©Ÿèƒ½ã‚’deck UIã«è¿½åŠ ã—ã¾ã—ãŸ
+- Enhance: コントロールパãƒãƒ«ã®ã‚¯ã‚¤ãƒƒã‚¯ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‹ã‚‰ãƒ•ァイルを照会ã§ãるよã†ã«
+- Enhance: コントロールパãƒãƒ«ã®ã‚¯ã‚¤ãƒƒã‚¯ã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã‹ã‚‰é€šå¸¸ã®ç…§ä¼šã‚’行ãˆã‚‹ã‚ˆã†ã«
- Fix: 一部ã®ãƒšãƒ¼ã‚¸å†…ãƒªãƒ³ã‚¯ãŒæ­£ã—ã動作ã—ãªã„å•題を修正
- Fix: 周年ã®å®Ÿç¸¾ãŒé–年を考慮ã—ãªã„å•題を修正
- Fix: ローカルURLã®ãƒ—レビューãƒãƒƒãƒ—アップãŒå·¦ä¸Šã«è¡¨ç¤ºã•れる
@@ -59,6 +73,8 @@
- Fix: リãƒãƒ¼ã‚·ã®å¯¾å±€ã‚’æ­£ã—ã共有ã§ããªã„ã“ã¨ãŒã‚ã‚‹å•題を修正
- Fix: 通知をグループ化ã—ã¦ã„ã‚‹éš›ã«ã€äººæ•°ãŒæ­£å¸¸ã«è¡¨ç¤ºã•れãªã„ã“ã¨ãŒã‚ã‚‹å•題を修正
- Fix: 連åˆãªã—ã®çŠ¶æ…‹ã®èª­ã¿æ›¸ããŒã§ããªã„å•題を修正
+- Fix: `/share` ã§æ—¥æœ¬èªžç­‰ã‚’å«ã‚€urlãŒurlエンコードã•れãªã„å•題を修正
+- Fix: ファイルを5ã¤ä»¥ä¸Šæ·»ä»˜ã—ã¦ã‚‚テキストãŒãªã„ã¨ãƒŽãƒ¼ãƒˆãŒæŠ˜ã‚ŠãŸãŸã¾ã‚Œãªã„å•題を修正
### Server
- Enhance: エンドãƒã‚¤ãƒ³ãƒˆ`antennas/update`ã®å¿…須項目を`antennaId`ã®ã¿ã«
@@ -80,6 +96,12 @@
- Fix: グローãƒãƒ«ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã§è¿”ä¿¡ãŒè¡¨ç¤ºã•れãªã„ã“ã¨ãŒã‚ã‚‹å•題を修正
- Fix: リノートをミュートã—ãŸãƒ¦ãƒ¼ã‚¶ã®æŠ•稿ã®ãƒªãƒŽãƒ¼ãƒˆãŒãƒŸãƒ¥ãƒ¼ãƒˆã•れるå•題を修正
- Fix: AP Linkç­‰ã¯æ·»ä»˜ãƒ•ァイル扱ã„ã—ãªã„よã†ã«ãªã© (#13754)
+- Fix: FTTãŒæœ‰åйã‹ã¤sinceIdã®ã¿ã‚’指定ã—ãŸå ´åˆã«å¸°ã£ã¦æ¥ã‚‹ãƒ¬ã‚¹ãƒãƒ³ã‚¹ãŒé€†é †ã§ã‚ã‚‹å•題を修正
+- Fix: `/i/notifications`ã« `includeTypes`ã‹`excludeTypes`を指定ã—ã¦ã„ã‚‹ã¨ãã€é€šçŸ¥ãŒå­˜åœ¨ã™ã‚‹ã®ã«ç©ºé…列を返ã™ã“ã¨ãŒã‚ã‚‹å•題を修正
+- Fix: 複数idを指定ã™ã‚‹`users/show`ãŒé–¢ä¿‚ãªã„ユーザを返ã™ã“ã¨ãŒã‚ã‚‹å•題を修正
+- Fix: `/tags` 㨠`/user-tags` ãŒæ¤œç´¢ã‚¨ãƒ³ã‚¸ãƒ³ã«ã‚¤ãƒ³ãƒ‡ãƒƒã‚¯ã‚¹ã•れãªã„よã†ã«
+- Fix: ã‚‚ã¨ã‚‚ã¨ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã§ã¯ãªã„ã¨é€£åˆã•れã¦ã„ãŸãƒ•ァイルãŒã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã¨ã—ã¦é€£åˆã•れãŸå ´åˆã«ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã¨ã—ã¦ãã®ãƒ•ァイルを扱ã†ã‚ˆã†ã«
+ - センシティブã¨ã—ã¦é€£åˆã—ãŸãƒ•ァイルã¯éžã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ã¨ã—ã¦é€£åˆã•れã¦ã‚‚センシティブã¨ã—ã¦æ‰±ã‚れã¾ã™
## 2024.3.1
diff --git a/chart/files/default.yml b/chart/files/default.yml
index 9c81964736..2e1381ec57 100644
--- a/chart/files/default.yml
+++ b/chart/files/default.yml
@@ -152,6 +152,22 @@ redis:
# ID SETTINGS AFTER THAT!
id: "aidx"
+
+# ┌────────────────â”
+#───┘ Error tracking └──────────────────────────────────────────
+
+# Sentry is available for error tracking.
+# See the Sentry documentation for more details on options.
+
+#sentryForBackend:
+# enableNodeProfiling: true
+# options:
+# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
+#sentryForFrontend:
+# options:
+# dsn: 'https://examplePublicKey@o0.ingest.sentry.io/0'
+
# ┌─────────────────────â”
#───┘ Other configuration └─────────────────────────────────────
diff --git a/healthcheck.sh b/healthcheck.sh
index 02f13576e9..216776b28f 100644
--- a/healthcheck.sh
+++ b/healthcheck.sh
@@ -4,4 +4,4 @@
# SPDX-License-Identifier: AGPL-3.0-only
PORT=$(grep '^port:' /sharkey/.config/default.yml | awk 'NR==1{print $2; exit}')
-curl -s -S -o /dev/null "http://localhost:${PORT}"
+curl -Sfso/dev/null "http://localhost:${PORT}/healthz"
diff --git a/locales/ar-SA.yml b/locales/ar-SA.yml
index 17c8f24fa5..955d672c1d 100644
--- a/locales/ar-SA.yml
+++ b/locales/ar-SA.yml
@@ -123,6 +123,7 @@ reactions: "Ø§Ù„ØªÙØ§Ø¹Ù„ات"
reactionSettingDescription2: "اسحب لترتيب ØŒ انقر للحذ٠، استخدم \"+\" Ù„Ù„Ø¥Ø¶Ø§ÙØ©."
rememberNoteVisibility: "تذكر إعدادت مدى رؤية الملاحظات"
attachCancel: "أزل المرÙÙ‚"
+deleteFile: "Ø­ÙØ°Ù الملÙ"
markAsSensitive: "علّمه كمحتوى حساس"
unmarkAsSensitive: "ألغ تعيينه كمحتوى حساس"
enterFileName: "ادخل اسم الملÙ"
@@ -1015,6 +1016,8 @@ sourceCode: "Ø§Ù„Ø´ÙØ±Ø© المصدرية"
flip: "اقلب"
lastNDays: "آخر {n} أيام"
surrender: "ألغÙ"
+_delivery:
+ stop: "Ù…ÙØ¹Ù„ّق"
_initialAccountSetting:
accountCreated: "نجح إنشاء حسابك!"
letsStartAccountSetup: "إذا كنت جديدًا لنعدّ حسابك الشخصي."
@@ -1565,8 +1568,21 @@ _webhookSettings:
reaction: "عند Ø§Ù„ØªÙØ§Ø¹Ù„"
_moderationLogTypes:
suspend: "علÙÙ‚"
+ deleteDriveFile: "Ø­ÙØ°Ù الملÙ"
+ deleteNote: "Ø­ÙØ°Ùت الملاحظة"
+ createGlobalAnnouncement: "Ø£Ùنشئ إعلان عام"
+ createUserAnnouncement: "Ø£Ùنشئ إعلان مستخدم"
+ updateGlobalAnnouncement: "Ø­ÙØ¯Ø« إعلان عام"
+ updateUserAnnouncement: "Ø­ÙØ¯Ø« إعلان مستخدم"
resetPassword: "أعد تعيين كلمتك السرية"
createInvitation: "ولÙّد دعوة"
_reversi:
total: "المجموع"
-
+ lookingForPlayer: "يبحث عن خصم..."
+ gameCanceled: "Ø£Ùلغيت اللعبة."
+ opponentHasSettingsChanged: "غيَر الخصم إعدادته."
+ showBoardLabels: "اعرض ترقيم الصÙو٠والأعمدة على اللوح"
+ useAvatarAsStone: "حوَل الحجارة إلى صور مستخدمين"
+_offlineScreen:
+ title: "غير متصل - يتعذر الاتصال بالخادم"
+ header: "يتعذر الاتصال بالخادم"
diff --git a/locales/bn-BD.yml b/locales/bn-BD.yml
index 2a23cda06b..abcf07da83 100644
--- a/locales/bn-BD.yml
+++ b/locales/bn-BD.yml
@@ -857,6 +857,10 @@ replies: "জবাব"
renotes: "রিনোট"
sourceCode: "সোরà§à¦¸ কোড"
flip: "উলà§à¦Ÿà¦¾à¦¨"
+_delivery:
+ stop: "সà§à¦¥à¦—িত করা হয়েছে"
+ _type:
+ none: "পà§à¦°à¦•াশ করা হচà§à¦›à§‡"
_role:
priority: "অগà§à¦°à¦¾à¦§à¦¿à¦•ার"
_priority:
@@ -1347,4 +1351,3 @@ _moderationLogTypes:
resetPassword: "পাসওয়ারà§à¦¡ রিসেট করà§à¦¨"
_reversi:
total: "মোট"
-
diff --git a/locales/ca-ES.yml b/locales/ca-ES.yml
index 985f658999..bda8579e27 100644
--- a/locales/ca-ES.yml
+++ b/locales/ca-ES.yml
@@ -400,6 +400,7 @@ name: "Nom"
antennaSource: "Font de l'antena"
antennaKeywords: "Paraules clau a seguir"
antennaExcludeKeywords: "Paraules clau a excloure"
+antennaExcludeBots: "Exclou els bots"
antennaKeywordsDescription: "Separar amb espais per la condició AND o amb salts de línia per la condició OR."
notifyAntenna: "Notifica'm les publicacions noves"
withFileAntenna: "Només les publicacions amb fitxers"
@@ -494,6 +495,7 @@ emojiStyle: "Estil d'emoji"
native: "Nadiu"
disableDrawer: "No mostrar els menús en calaixos"
showNoteActionsOnlyHover: "Només mostra accions de la nota en passar amb el cursor"
+showReactionsCount: "Mostra el nombre de reaccions a les publicacions"
noHistory: "No hi ha un registre previ"
signinHistory: "Historial d'autenticacions"
enableAdvancedMfm: "Habilitar l'MFM avançat"
@@ -543,7 +545,7 @@ objectStorageUseProxyDesc: "Desactiva'l si no faràs servir un Proxy per les con
objectStorageSetPublicRead: "Configurar les pujades com públiques "
s3ForcePathStyleDesc: "Si s3ForcePathStyle es troba activat el nom del dipòsit s'ha d'incloure a l'adreça URL en comtes del nom del host. Potser que necessitis activar-ho quan facis servir, per exemple, Minio a un servidor propi."
serverLogs: "Registres del servidor"
-deleteAll: "Esborrar tot"
+deleteAll: "Elimina-ho tot"
showFixedPostForm: "Mostrar el formulari per escriure a l'inici de la línia de temps"
showFixedPostFormInChannel: "Mostrar el formulari d'escriptura al principi de la línia de temps (Canals)"
withRepliesByDefaultForNewlyFollowed: "Inclou les respostes d'usuaris nous seguits a la línia de temps per defecte."
@@ -691,9 +693,9 @@ reporter: "Denunciant "
reporteeOrigin: "Origen de la denúncia "
reporterOrigin: "Origen del denunciant"
forwardReport: "Transferir la denúncia a una instància remota"
-forwardReportIsAnonymous: "En comptes del teu compte, es farà servir un compte anònim com a denunciat a la instància remota."
-send: "Enviar"
-abuseMarkAsResolved: "Marcar la denúncia com a resolta"
+forwardReportIsAnonymous: "En lloc del teu compte, es farà servir un compte anònim com a denunciant al servidor remot."
+send: "Envia"
+abuseMarkAsResolved: "Marca la denúncia com a resolta"
openInNewTab: "Obre a una pestanya nova"
openInSideView: "Obre a una vista lateral"
defaultNavigationBehaviour: "Navegació per defecte"
@@ -853,7 +855,7 @@ customCss: "CSS personalitzat"
customCssWarn: "Aquesta configuració només hauries de configurar-la si saps que fas. Si poses valors inadequats pots fer que el client deixi de funcionar correctament."
global: "Global"
squareAvatars: "Mostrar avatars quadrats"
-sent: "Enviar"
+sent: "Envia"
received: "Rebut"
searchResult: "Resultats de la cerca"
hashtags: "Etiquetes"
@@ -991,6 +993,7 @@ neverShow: "No mostrar més "
remindMeLater: "Recorda-m'ho més tard"
didYouLikeMisskey: "T'està agradant Misskey?"
pleaseDonate: "A {host} fem servir el software lliure Misskey. Considera fer un donatiu a Misskey perquè pugui continuar el seu desenvolupament!"
+correspondingSourceIsAvailable: "El codi font corresponent està disponible a {anchor}."
roles: "Rols"
role: "Rols"
noRole: "No s'han trobat rols"
@@ -1159,6 +1162,7 @@ showRenotes: "Mostrar impulsos"
edited: "Editat"
notificationRecieveConfig: "Paràmetres de notificacions"
mutualFollow: "Seguidor mutu"
+followingOrFollower: "Seguit o seguidor"
fileAttachedOnly: "Només notes amb adjunts"
showRepliesToOthersInTimeline: "Mostrar les respostes a altres a la línia de temps"
hideRepliesToOthersInTimeline: "Amagar les respostes a altres a la línia de temps"
@@ -1168,6 +1172,9 @@ confirmShowRepliesAll: "Aquesta opció no té marxa enrere. Vols mostrar les tev
confirmHideRepliesAll: "Aquesta opció no té marxa enrere. Vols ocultar les teves respostes a tots els usuaris que segueixes a la línia de temps?"
externalServices: "Serveis externs"
sourceCode: "Codi font"
+repositoryUrl: "URL del repositori"
+feedback: "Opinió"
+feedbackUrl: "URL per a opinar"
impressum: "Impressum"
impressumUrl: "Adreça URL impressum"
impressumDescription: "A països, com Alemanya, la inclusió de la informació de contacte de l'operador (un Impressum) és requereix de manera legal per llocs comercials."
@@ -1203,6 +1210,7 @@ soundWillBePlayed: "Es reproduiran efectes de so"
showReplay: "Veure reproducció"
replay: "Reproduir"
replaying: "Reproduint"
+endReplay: "Tanca la redifusió"
ranking: "Classificació"
lastNDays: "Últims {n} dies"
backToTitle: "Torna al títol"
@@ -1210,7 +1218,16 @@ hemisphere: "Geolocalització"
withSensitive: "Incloure notes amb fitxers sensibles"
userSaysSomethingSensitive: "La publicació de {name} conte material sensible"
enableHorizontalSwipe: "Lliscar per canviar de pestanya"
+loading: "S’està carregant"
surrender: "Cancel·lar "
+gameRetry: "Torna a provar"
+notUsePleaseLeaveBlank: "Si no voleu usar-ho, deixeu-ho en blanc"
+useTotp: "Usa una contrasenya d'un sol ús"
+useBackupCode: "Usa un codi de recuperació"
+_delivery:
+ stop: "Suspés"
+ _type:
+ none: "S'està publicant"
_bubbleGame:
howToPlay: "Com es juga"
_howToPlay:
@@ -1915,7 +1932,6 @@ _2fa:
registerTOTP: "Registrar una aplicació autenticadora"
step1: "Primer instal·la una aplicació autenticadora (com {a} o {b}) al teu dispositiu."
step2: "Després escaneja el codi QR que es mostra en aquesta pantalla."
- step2Click: "Fent clic en aquest codi QR et permetrà registrar l'autenticació de doble factor a la teva clau de seguretat o en l'aplicació d'autenticació del teu dispositiu."
step2Uri: "Escriu la següent URI si estàs fent servir una aplicació d'escriptori "
step3Title: "Escriu un codi d'autenticació"
step3: "Escriu el codi d'autenticació (token) que es mostra a la teva aplicació per finalitzar la configuració."
@@ -1989,7 +2005,6 @@ _permissions:
"read:admin:server-info": "Veure informació del servidor"
"read:admin:show-moderation-log": "Veure registre de moderació "
"read:admin:show-user": "Veure informació privada de l'usuari "
- "read:admin:show-users": "Veure informació privada de l'usuari "
"write:admin:suspend-user": "Suspendre usuari"
"write:admin:unset-user-avatar": "Esborrar avatar d'usuari "
"write:admin:unset-user-banner": "Esborrar bàner de l'usuari "
diff --git a/locales/cs-CZ.yml b/locales/cs-CZ.yml
index 6dad336b7f..bcab28db2c 100644
--- a/locales/cs-CZ.yml
+++ b/locales/cs-CZ.yml
@@ -1099,6 +1099,10 @@ sourceCode: "Zdrojový kód"
flip: "OtoÄit"
lastNDays: "Posledních {n} dnů"
surrender: "Zrušit"
+_delivery:
+ stop: "Suspendováno"
+ _type:
+ none: "Publikuji"
_initialAccountSetting:
accountCreated: "Váš úÄet byl úspěšnÄ› vytvoÅ™en!"
letsStartAccountSetup: "Pro zaÄátek si nastavte svůj profil."
@@ -1664,7 +1668,6 @@ _2fa:
registerTOTP: "Registrovat aplikaci autentizátoru"
step1: "Nejprve si do zařízení nainstalujte aplikaci pro ověřování (například {a} nebo {b})."
step2: "Poté naskenujte QR kód zobrazený na této obrazovce."
- step2Click: "Kliknutím na tento QR kód můžete zaregistrovat 2FA do bezpeÄnostního klíÄe nebo aplikace autentizace telefonu."
step3Title: "Zadejte ověřovací kód"
step3: "Pro dokonÄení nastavení zadejte token poskytnutý vaší aplikací."
step4: "Od této chvíle budou všechny budoucí pokusy o přihlášení vyžadovat tento přihlašovací token."
@@ -1718,7 +1721,7 @@ _auth:
shareAccessTitle: "Udělovat oprávnění k aplikacím"
shareAccess: "Chcete autorizovat \"{name}\" pro přístup k tomuto úÄtu?"
shareAccessAsk: "Opravdu chcete této aplikaci povolit přístup k vaÅ¡emu úÄtu?"
- permission: "{jméno} požaduje tato oprávnění"
+ permission: "{name} požaduje tato oprávnění"
permissionAsk: "Tato aplikace požaduje následující oprávnění"
pleaseGoBack: "Vraťte se prosím zpět do aplikace"
callback: "Návrat k aplikaci"
@@ -1942,7 +1945,7 @@ _notification:
youGotMention: "{name} vás zmínil"
youGotReply: "{name} vám odpověděl"
youGotQuote: "{name} vás citoval"
- youRenoted: "Poznámka od {jméno}"
+ youRenoted: "Poznámka od {name}"
youWereFollowed: "Máte nového následovníka"
youReceivedFollowRequest: "Obdrželi jste žádost o sledování"
yourFollowRequestAccepted: "Vaše žádost o sledování byla přijata"
@@ -2025,4 +2028,3 @@ _moderationLogTypes:
createInvitation: "Vygenerovat pozvánku"
_reversi:
total: "Celkem"
-
diff --git a/locales/da-DK.yml b/locales/da-DK.yml
index d1fbec9f67..5eb7a5a5f4 100644
--- a/locales/da-DK.yml
+++ b/locales/da-DK.yml
@@ -1,3 +1,4 @@
---
_lang_: "Dansk"
-
+headlineMisskey: ""
+introMisskey: "よã†ã“ãï¼Misskeyã¯ã€ã‚ªãƒ¼ãƒ—ンソースã®åˆ†æ•£åž‹ãƒžã‚¤ã‚¯ãƒ­ãƒ–ログサービスã§ã™ã€‚\n「ノートã€ã‚’作æˆã—ã¦ã€ã„ã¾èµ·ã“ã£ã¦ã„ã‚‹ã“ã¨ã‚’共有ã—ãŸã‚Šã€ã‚ãªãŸã«ã¤ã„ã¦çš†ã«ç™ºä¿¡ã—よã†ðŸ“¡\nã€Œãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³ã€æ©Ÿèƒ½ã§ã€çš†ã®ãƒŽãƒ¼ãƒˆã«ç´ æ—©ãå応を追加ã™ã‚‹ã“ã¨ã‚‚ã§ãã¾ã™ðŸ‘\næ–°ã—ã„世界を探検ã—よã†ðŸš€"
diff --git a/locales/de-DE.yml b/locales/de-DE.yml
index 7cb451e233..47b97a8e82 100644
--- a/locales/de-DE.yml
+++ b/locales/de-DE.yml
@@ -654,7 +654,7 @@ smtpSecureInfo: "Schalte dies aus, falls du STARTTLS verwendest."
testEmail: "Emailversand testen"
wordMute: "Wortstummschaltung"
regexpError: "Fehler in einem regulären Ausdruck"
-regexpErrorDescription: "Im regulären Ausdruck deiner {tab}en Wortstummschaltungen ist ein Fehler aufgetreten:"
+regexpErrorDescription: "Im regulären Ausdruck deiner in Zeile {line} von {tab}en Wortstummschaltungen ist ein Fehler aufgetreten:"
instanceMute: "Instanzstummschaltungen"
userSaysSomething: "{name} hat etwas gesagt"
makeActive: "Aktivieren"
@@ -1188,6 +1188,10 @@ addMfmFunction: "MFM hinzufügen"
sfx: "Soundeffekte"
lastNDays: "Letzten {n} Tage"
surrender: "Abbrechen"
+_delivery:
+ stop: "Gesperrt"
+ _type:
+ none: "Wird veröffentlicht"
_announcement:
forExistingUsers: "Nur für existierende Nutzer"
forExistingUsersDescription: "Ist diese Option aktiviert, wird diese Ankündigung nur Nutzern angezeigt, die zum Zeitpunkt der Ankündigung bereits registriert sind. Ist sie deaktiviert, wird sie auch Nutzern, die sich nach dessen Veröffentlichung registrieren, angezeigt."
@@ -1822,7 +1826,6 @@ _2fa:
registerTOTP: "Authentifizierungs-App registrieren"
step1: "Installiere zuerst eine Authentifizierungsapp (z.B. {a} oder {b}) auf deinem Gerät."
step2: "Dann, scanne den angezeigten QR-Code mit deinem Gerät."
- step2Click: "Durch Klicken dieses QR-Codes kannst du Verifikation mit deinem Security-Token oder einer App registrieren."
step2Uri: "Nutzt du ein Desktopprogramm, gib folgende URI eingeben"
step3Title: "Authentifizierungsscode eingeben"
step3: "Gib zum Abschluss den Code (Token) ein, der von deiner App angezeigt wird."
@@ -2292,4 +2295,3 @@ _reversi:
black: "Schwarz"
white: "Weiß"
total: "Gesamt"
-
diff --git a/locales/el-GR.yml b/locales/el-GR.yml
index bb5639a741..2098c7ef50 100644
--- a/locales/el-GR.yml
+++ b/locales/el-GR.yml
@@ -398,4 +398,3 @@ _moderationLogTypes:
suspend: "Αποβολή"
_reversi:
total: "ΣÏνολο"
-
diff --git a/locales/en-US.yml b/locales/en-US.yml
index edf4abfac1..ffebe5fbec 100644
--- a/locales/en-US.yml
+++ b/locales/en-US.yml
@@ -112,11 +112,14 @@ unrenote: "Remove boost"
renoted: "Boosted."
quoted: "Quoted."
rmboost: "Unboosted."
+renotedToX: "Boosted from {name} users"
cantRenote: "This post can't be boosted."
cantReRenote: "A boost can't be boosted."
quote: "Quote"
inChannelRenote: "Channel-only Boost"
inChannelQuote: "Channel-only Quote"
+renoteToChannel: "Renote to channel"
+renoteToOtherChannel: "Renote to other channel"
pinnedNote: "Pinned note"
pinned: "Pin to profile"
you: "You"
@@ -411,6 +414,7 @@ name: "Name"
antennaSource: "Antenna source"
antennaKeywords: "Keywords to listen to"
antennaExcludeKeywords: "Keywords to exclude"
+antennaExcludeBots: "Exclude bot accounts"
antennaKeywordsDescription: "Separate with spaces for an AND condition or with line breaks for an OR condition."
notifyAntenna: "Notify about new notes"
withFileAntenna: "Only notes with files"
@@ -480,6 +484,7 @@ expandAllCws: "Show content for all replies"
collapseAllCws: "Hide content for all replies"
quoteAttached: "Quote"
quoteQuestion: "Append as quote?"
+attachAsFileQuestion: "The text in clipboard is long. Would you want to attach it as text file?"
noMessagesYet: "No messages yet"
newMessageExists: "There are new messages"
onlyOneFileCanBeAttached: "You can only attach one file to a message"
@@ -507,6 +512,7 @@ emojiStyle: "Emoji style"
native: "Native"
disableDrawer: "Don't use drawer-style menus"
showNoteActionsOnlyHover: "Only show note actions on hover"
+showReactionsCount: "See the number of reactions in notes"
noHistory: "No history available"
signinHistory: "Login history"
enableAdvancedMfm: "Enable advanced MFM"
@@ -1273,6 +1279,25 @@ enableHorizontalSwipe: "Swipe to switch tabs"
loading: "Loading"
surrender: "Cancel"
gameRetry: "Retry"
+notUsePleaseLeaveBlank: "Leave blank if not used"
+useTotp: "Enter the One-Time Password"
+useBackupCode: "Use the backup codes"
+launchApp: "Launch the app"
+useNativeUIForVideoAudioPlayer: "Use UI of browser when play video and audio"
+keepOriginalFilename: "Keep original file name"
+keepOriginalFilenameDescription: "If you turn off this setting, files names will be replaced with random string automatically when you upload files."
+noDescription: "There is not the explanation"
+alwaysConfirmFollow: "Always confirm when following"
+inquiry: "Contact"
+_delivery:
+ status: "Delivery status"
+ stop: "Suspended"
+ resume: "Delivery resume"
+ _type:
+ none: "Publishing"
+ manuallySuspended: "Manually suspended"
+ goneSuspended: "Server is suspended due to server deletion"
+ autoSuspendedForNotResponding: "Server is suspended due to no responding"
_bubbleGame:
howToPlay: "How to play"
hold: "Hold"
@@ -1734,6 +1759,11 @@ _role:
roleAssignedTo: "Assigned to manual roles"
isLocal: "Local user"
isRemote: "Remote user"
+ isCat: "Cat Users"
+ isBot: "Bot Users"
+ isSuspended: "Suspended user"
+ isLocked: "Private accounts"
+ isExplorable: "Effective user of \"make an account discoverable\""
createdLessThan: "Less than X has passed since account creation"
createdMoreThan: "More than X has passed since account creation"
followersLessThanOrEq: "Has X or fewer followers"
@@ -1805,6 +1835,7 @@ _plugin:
installWarn: "Please do not install untrustworthy plugins."
manage: "Manage plugins"
viewSource: "View source"
+ viewLog: "Show log"
_preferencesBackups:
list: "Created backups"
saveNew: "Save new backup"
@@ -1997,7 +2028,6 @@ _2fa:
registerTOTP: "Register authenticator app"
step1: "First, install an authentication app (such as {a} or {b}) on your device."
step2: "Then, scan the QR code displayed on this screen."
- step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app."
step2Uri: "Enter the following URI if you are using a desktop program"
step3Title: "Enter an authentication code"
step3: "Enter the authentication code (token) provided by your app to finish setup."
@@ -2021,6 +2051,7 @@ _2fa:
backupCodesDescription: "You can use these codes to gain access to your account in case of becoming unable to use your two-factor authentificator app. Each can only be used once. Please keep them in a safe place."
backupCodeUsedWarning: "A backup code has been used. Please reconfigure two-factor authentification as soon as possible if you are no longer able to use it."
backupCodesExhaustedWarning: "All backup codes have been used. Should you lose access to your two-factor authentification app, you will be unable to access this account. Please reconfigure two-factor authentification."
+ moreDetailedGuideHere: "Here is detailed guide"
_permissions:
"read:account": "View your account information"
"write:account": "Edit your account information"
@@ -2071,7 +2102,6 @@ _permissions:
"read:admin:server-info": "View server info"
"read:admin:show-moderation-log": "View moderation log"
"read:admin:show-user": "View private user info"
- "read:admin:show-users": "View private user info"
"write:admin:suspend-user": "Suspend user"
"write:admin:unset-user-avatar": "Remove user avatar"
"write:admin:unset-user-banner": "Remove user banner"
@@ -2289,6 +2319,7 @@ _play:
title: "Title"
script: "Script"
summary: "Description"
+ visibilityDescription: "Putting it private means it won't be visible on your profile, but anyone that has the URL can still access it."
_pages:
newPage: "Create a new Page"
editPage: "Edit this Page"
@@ -2333,6 +2364,8 @@ _pages:
section: "Section"
image: "Images"
button: "Button"
+ dynamic: "Dynamic Blocks"
+ dynamicDescription: "This block has been abolished. Please use {play} from now on."
note: "Embedded note"
_note:
id: "Note ID"
@@ -2362,6 +2395,7 @@ _notification:
sendTestNotification: "Send test notification"
notificationWillBeDisplayedLikeThis: "Notifications look like this"
reactedBySomeUsers: "{n} users reacted"
+ likedBySomeUsers: "{n} users liked your note"
renotedBySomeUsers: "Boosted by {n} users"
followedBySomeUsers: "Followed by {n} users"
flushNotification: "Clear notifications"
@@ -2684,3 +2718,21 @@ _reversi:
_offlineScreen:
title: "Offline - cannot connect to the server"
header: "Unable to connect to the server"
+_urlPreviewSetting:
+ title: "URL preview settings"
+ enable: "Enable URL preview"
+ timeout: "Time out when getting preview (ms)"
+ timeoutDescription: "If it takes longer than this value to get the preview, the preview won’t be generated."
+ maximumContentLength: "Maximum Content-Length (bytes)"
+ maximumContentLengthDescription: "If Content-Length is higher than this value, the preview won't be generated."
+ requireContentLength: "Generate the preview only if you could get Content-Length"
+ requireContentLengthDescription: "If other server doesn't return Content-Length, the preview won't be generated."
+ userAgent: "User-Agent"
+ userAgentDescription: "Sets the User-Agent to be used when retrieving previews. If left blank, the default User-Agent will be used."
+ summaryProxy: "Proxy endpoints that generate previews"
+ summaryProxyDescription: "Not Misskey itself, but generate previews using Summaly Proxy."
+ summaryProxyDescription2: "The following parameters are linked to the proxy as a query string. If the proxy does not support them, the values are ignored."
+_mediaControls:
+ pip: "Picture in Picture"
+ playbackRate: "Playback Speed"
+ loop: "Loop playback"
diff --git a/locales/es-ES.yml b/locales/es-ES.yml
index c77773949f..209c2dec2d 100644
--- a/locales/es-ES.yml
+++ b/locales/es-ES.yml
@@ -235,7 +235,7 @@ done: "Terminado"
processing: "Procesando"
preview: "Vista previa"
default: "Predeterminado"
-defaultValueIs: "Predeterminado"
+defaultValueIs: "Por defecto: {value}"
noCustomEmojis: "No hay emojis personalizados"
noJobs: "No hay trabajos"
federating: "Federando"
@@ -400,6 +400,7 @@ name: "Nombre"
antennaSource: "Origen de la antena"
antennaKeywords: "Palabras clave para recibir"
antennaExcludeKeywords: "Palabras clave para excluir"
+antennaExcludeBots: "Excluir bots"
antennaKeywordsDescription: "Separar con espacios es una declaración AND, separar con una linea nueva es una declaración OR"
notifyAntenna: "Notificar nueva nota"
withFileAntenna: "Sólo notas con archivos adjuntados"
@@ -494,6 +495,7 @@ emojiStyle: "Estilo de emoji"
native: "Nativo"
disableDrawer: "No mostrar los menús en cajones"
showNoteActionsOnlyHover: "Mostrar acciones de la nota sólo al pasar el cursor"
+showReactionsCount: "Mostrar el número de reacciones en las notas"
noHistory: "No hay datos en el historial"
signinHistory: "Historial de ingresos"
enableAdvancedMfm: "Habilitar MFM avanzado"
@@ -991,6 +993,7 @@ neverShow: "No mostrar de nuevo"
remindMeLater: "Recordar después"
didYouLikeMisskey: "¿Te gusta Misskey?"
pleaseDonate: "{host} usa el software gratuito Misskey. Por favor ¡Considera donar al proyecto principal para que podamos continuar!"
+correspondingSourceIsAvailable: "El código fuente correspondiente se encuentra disponible en {anchor}"
roles: "Roles"
role: "Rol"
noRole: "Rol no encontrado"
@@ -1042,6 +1045,7 @@ 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"
sensitiveWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
prohibitedWords: "Palabras explícitas"
+prohibitedWordsDescription: "Activa un error cuando se intenta publicar una nota que contiene una o varias palabras prohibidas. Se pueden establecer varias palabras, una por línea."
prohibitedWordsDescription2: "Si se usan espacios se crearán expresiones AND y las palabras subsecuentes con barras inclinadas se convertirán en expresiones regulares."
hiddenTags: "Hashtags ocultos"
hiddenTagsDescription: "Selecciona las etiquetas que no se mostrarán en tendencias. Una etiqueta por línea."
@@ -1158,6 +1162,7 @@ showRenotes: "Mostrar renotas"
edited: "Editado"
notificationRecieveConfig: "Ajustes de Notificaciones"
mutualFollow: "Os seguís mutuamente"
+followingOrFollower: "Siguiendo o seguidor"
fileAttachedOnly: "Solo notas con archivos"
showRepliesToOthersInTimeline: "Mostrar respuestas a otros en la línea de tiempo"
hideRepliesToOthersInTimeline: "Ocultar respuestas a otros en la línea de tiempo"
@@ -1167,6 +1172,12 @@ confirmShowRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres
confirmHideRepliesAll: "Esta operación es irreversible. ¿Confirmas que quieres ocultar tus respuestas a otros usuarios que sigues en tu línea de tiempo?"
externalServices: "Servicios Externos"
sourceCode: "Código fuente"
+sourceCodeIsNotYetProvided: "El código fuente aún no está disponible. Contacta con el administrador para solucionarlo."
+repositoryUrl: "URL del repositorio"
+repositoryUrlDescription: "Si estás usando Misskey tal cual (sin cambios en el código fuente), entra en https://github.com/misskey-dev/misskey"
+repositoryUrlOrTarballRequired: "Si no has publicado un repositorio aún, deberás publicar un tarball en su lugar. Mira el archivo .config/example.yml para más información."
+feedback: "Comentarios"
+feedbackUrl: "URL de comentarios"
impressum: "Impressum"
impressumUrl: "Impressum URL"
impressumDescription: "En algunos países, como Alemania, la inclusión del operador de datos (el Impressum) es requerido legalmente para sitios web comerciales."
@@ -1202,6 +1213,8 @@ soundWillBePlayed: "Se reproducirán efectos sonoros"
showReplay: "Ver reproducción"
replay: "Reproducir"
replaying: "Reproduciendo"
+endReplay: "Terminar reproducción"
+copyReplayData: "Copiar datos de reproducción"
ranking: "Clasificación"
lastNDays: "Últimos {n} días"
backToTitle: "Regresar al inicio"
@@ -1209,9 +1222,32 @@ hemisphere: "Región"
withSensitive: "Mostrar notas que contengan material sensible"
userSaysSomethingSensitive: "La publicación de {name} contiene material sensible"
enableHorizontalSwipe: "Deslice para cambiar de pestaña"
+loading: "Cargando"
surrender: "detener"
+gameRetry: "Reintentar"
+notUsePleaseLeaveBlank: "Dejar en blanco si no se usa"
+useTotp: "Introduce la contraseña de un solo uso"
+useBackupCode: "Usar códigos de respaldo"
+launchApp: "Ejecutar la app"
+useNativeUIForVideoAudioPlayer: "Usar la interfaz del navegador cuando se reproduce audio y vídeo"
+keepOriginalFilename: "Mantener el nombre original del archivo"
+noDescription: "No hay descripción"
+alwaysConfirmFollow: "Confirmar siempre cuando se sigue a alguien"
+_delivery:
+ stop: "Suspendido"
+ _type:
+ none: "Publicando"
_bubbleGame:
howToPlay: "Cómo jugar"
+ hold: "Mantener"
+ _score:
+ score: "Puntos"
+ scoreYen: "Cantidad de dinero ganada"
+ highScore: "Puntuación más alta"
+ maxChain: "Número máximo de cadenas"
+ yen: "{yen} Yenes"
+ estimatedQty: "{qty} Piezas"
+ scoreSweets: "{onigiriQtyWithUnit} Onigiris"
_howToPlay:
section1: "Ajuste la posición y deje caer el objeto en la caja"
section2: "Cuando dos objetos del mismo tipo se tocan, cambian a otro tipo y consigues puntos"
@@ -1329,7 +1365,7 @@ _serverSettings:
_accountMigration:
moveFrom: "Trasladar de otra cuenta a ésta"
moveFromSub: "Crear un alias para otra cuenta."
- moveFromLabel: "Cuenta desde la que se realiza el traslado:"
+ moveFromLabel: "Cuenta desde la que se realiza el traslado #{n}"
moveFromDescription: "Si quieres transferir seguidores de otra cuenta a esta cuenta y trasladarlos, tendrás que crear un alias aquí. Asegúrate de crearlo antes de realizar el traslado. Introduce la cuenta desde la que estás moviendo los seguidores así: @person@instance.com"
moveTo: "Mover esta cuenta a una nueva"
moveToLabel: "Cuenta destino:"
@@ -1588,8 +1624,11 @@ _achievements:
description: "Tutorial completado"
_bubbleGameExplodingHead:
title: "🤯"
+ description: "El objeto más grande en el juego de burbujas"
_bubbleGameDoubleExplodingHead:
title: "Doble 🤯"
+ description: "Dos de los objetos más grandes en el juego de burbujas al mismo tiempo"
+ flavor: "Puedes llenar el bento un poco de esta forma 🤯 🤯."
_role:
new: "Crear rol"
edit: "Editar rol"
@@ -1630,6 +1669,7 @@ _role:
gtlAvailable: "Explorar la línea de tiempo global"
ltlAvailable: "Explorar la línea de tiempo local"
canPublicNote: "Permitir la publicación"
+ mentionMax: "Número máximo de menciones en una nota"
canInvite: "Puede crear códigos de invitación"
inviteLimit: "Límite de invitaciones"
inviteLimitCycle: "Enfriamiento del límite de invitaciones"
@@ -1653,8 +1693,13 @@ _role:
canUseTranslator: "Uso de traductor"
avatarDecorationLimit: "Número máximo de decoraciones de avatar"
_condition:
+ roleAssignedTo: "Asignado a roles manuales"
isLocal: "Usuario local"
isRemote: "Usuario remoto"
+ isCat: "Usuarios Gato"
+ isBot: "Usuarios Bot"
+ isSuspended: "Usuario suspendido"
+ isLocked: "Cuentas privadas"
createdLessThan: "Menos de X han pasado desde la creación de la cuenta"
createdMoreThan: "Más de X han pasado desde la creación de la cuenta"
followersLessThanOrEq: "Tiene X o menos seguidores"
@@ -1724,6 +1769,7 @@ _plugin:
installWarn: "Por favor no instale plugins que no son de confianza"
manage: "Gestionar plugins"
viewSource: "Ver la fuente"
+ viewLog: "Ver log"
_preferencesBackups:
list: "Respaldos creados"
saveNew: "Guardar nuevo respaldo"
@@ -1753,6 +1799,8 @@ _aboutMisskey:
contributors: "Principales colaboradores"
allContributors: "Todos los colaboradores"
source: "Código fuente"
+ original: "Original"
+ thisIsModifiedVersion: "{name} usa una versión modificada de Misskey."
translation: "Traducir Misskey"
donate: "Donar a Misskey"
morePatrons: "Muchas más personas nos apoyan. Muchas gracias🥰"
@@ -1911,7 +1959,6 @@ _2fa:
registerTOTP: "Registrar aplicación autenticadora"
step1: "Primero, instale en su dispositivo la aplicación de autenticación {a} o {b} u otra."
step2: "Luego, escanee con la aplicación el código QR mostrado en pantalla."
- step2Click: "Clicking on this QR code will allow you to register 2FA to your security key or phone authenticator app.\nTocar este código QR te permitirá registrar la autenticación 2FA a tu llave de seguridad o aplicación autenticadora."
step2Uri: "Si usas una aplicación de escritorio, introduce en ella la siguiente URL."
step3Title: "Ingresa un código de autenticación"
step3: "Para terminar, ingrese el token mostrado en la aplicación."
@@ -1935,6 +1982,7 @@ _2fa:
backupCodesDescription: "En caso de que no puedas usar tu aplicación de autenticación, podrás usar los códigos de respaldo que figuran abajo para acceder a tu cuenta. Asegúrate de guardar en lugar seguro los códigos de respaldo. Cada uno de los códigos de respaldo es de un solo uso."
backupCodeUsedWarning: "Has usado todos los códigos de respaldo. Si dejas de tener acceso a tu aplicación de autenticación, no podrás volver a iniciar sesión en tu cuenta. Por favor, reconfigura tu aplicación de autenticación lo antes posible."
backupCodesExhaustedWarning: "Has usado todos los códigos de respaldo. Si dejas de tener acceso a tu aplicación de autenticación, no podrás volver a iniciar sesión en la cuenta que figura arriba. Por favor, reconfigura tu aplicación de autenticación lo antes posible."
+ moreDetailedGuideHere: "Guía detallada"
_permissions:
"read:account": "Ver información de la cuenta"
"write:account": "Editar información de la cuenta"
@@ -1976,6 +2024,7 @@ _permissions:
"write:admin:delete-account": "Eliminar cuentas de usuario"
"write:admin:delete-all-files-of-a-user": "Eliminar todos los archivos de un usuario"
"read:admin:index-stats": "Ver datos indexados"
+ "read:admin:table-stats": "Ver estadísticas de las tablas de la base de datos"
"read:admin:user-ips": "Ver dirección IP de usuario"
"read:admin:meta": "Ver metadatos de la instancia"
"write:admin:reset-password": "Restablecer contraseñas de usuario"
@@ -1984,7 +2033,6 @@ _permissions:
"read:admin:server-info": "Ver información del servidor"
"read:admin:show-moderation-log": "Ver log de moderación"
"read:admin:show-user": "Ver información privada de usuario"
- "read:admin:show-users": "Ver información privada de usuario"
"write:admin:suspend-user": "Suspender cuentas de usuario"
"write:admin:unset-user-avatar": "Quitar avatares de usuario"
"write:admin:unset-user-banner": "Quitar banner de usuarios"
@@ -2196,6 +2244,7 @@ _play:
title: "Título"
script: "Script"
summary: "Descripción"
+ visibilityDescription: "Poniéndola como privada significa que no será visible en tu perfil, pero cualquiera que tenga la URL aún podrá acceder a ella."
_pages:
newPage: "Crear página"
editPage: "Editar página"
@@ -2240,6 +2289,8 @@ _pages:
section: "Sección"
image: "Imagen"
button: "Botón"
+ dynamic: "Bloques Dinámicos"
+ dynamicDescription: "Los bloques dinámicos están obsoletos. A partir de ahora, utiliza {play} por favor."
note: "Nota embebida"
_note:
id: "Id de la nota"
@@ -2449,3 +2500,11 @@ _reversi:
reversi: "Reversi"
won: "{name} ha ganado"
total: "Total"
+_urlPreviewSetting:
+ timeout: "Timeout de la carga de vista previa de las URLs (ms)"
+ maximumContentLength: "Content-Length Máximo (bytes)"
+ userAgent: "User-Agent"
+_mediaControls:
+ pip: "Picture in Picture"
+ playbackRate: "Velocidad de reproducción"
+ loop: "Reproducción en bucle"
diff --git a/locales/fr-FR.yml b/locales/fr-FR.yml
index 6a32f75231..22a9e2449f 100644
--- a/locales/fr-FR.yml
+++ b/locales/fr-FR.yml
@@ -129,7 +129,7 @@ overwriteFromPinnedEmojisForReaction: "Remplacer par les émojis épinglés pour
overwriteFromPinnedEmojis: "Remplacer par les émojis épinglés globalement"
reactionSettingDescription2: "Déplacer pour réorganiser, cliquer pour effacer, utiliser « + » pour ajouter."
rememberNoteVisibility: "Se souvenir de la visibilité des notes"
-attachCancel: "Supprimer le fichier attaché"
+attachCancel: "Supprimer le fichier joint"
deleteFile: "Fichier supprimé"
markAsSensitive: "Marquer comme sensible"
unmarkAsSensitive: "Supprimer le marquage comme sensible"
@@ -400,6 +400,7 @@ name: "Nom"
antennaSource: "Source de l’antenne"
antennaKeywords: "Mots clés à recevoir"
antennaExcludeKeywords: "Mots clés à exclure"
+antennaExcludeBots: "Exclure les comptes robot"
antennaKeywordsDescription: "Séparer avec des espaces pour la condition AND. Séparer avec un saut de ligne pour une condition OR."
notifyAntenna: "Me notifier pour les nouvelles notes"
withFileAntenna: "Notes ayant des fichiers joints uniquement"
@@ -494,6 +495,7 @@ emojiStyle: "Style des émojis"
native: "Natif"
disableDrawer: "Les menus ne s'affichent pas dans le tiroir"
showNoteActionsOnlyHover: "Afficher les actions de note uniquement au survol"
+showReactionsCount: "Afficher le nombre de réactions des notes"
noHistory: "Pas d'historique"
signinHistory: "Historique de connexion"
enableAdvancedMfm: "Activer la MFM avancée"
@@ -541,6 +543,7 @@ objectStorageUseSSLDesc: "Désactivez cette option si vous n'utilisez pas HTTPS
objectStorageUseProxy: "Se connecter via proxy"
objectStorageUseProxyDesc: "Désactivez cette option si vous n'utilisez pas de proxy pour la connexion API"
objectStorageSetPublicRead: "Régler sur « public » lors de l'envoi"
+s3ForcePathStyleDesc: "Si s3ForcePathStyle est activé, le nom du compartiment doit être spécifié comme une partie du chemin de l'URL plutôt que le nom d'hôte. Il faudra peut-être l'activer lors de l'utilisation d'une instance de Minio autohébergée, etc."
serverLogs: "Journal du serveur"
deleteAll: "Supprimer tout"
showFixedPostForm: "Afficher le formulaire de publication en haut du fil d'actualité"
@@ -655,7 +658,7 @@ testEmail: "Tester la distribution de courriel"
wordMute: "Filtre de mots"
hardWordMute: "Filtre de mots dur"
regexpError: "Erreur d’expression régulière"
-regexpErrorDescription: "Une erreur s'est produite dans l'expression régulière sur la ligne {ligne} de votre mot muet {tab} :"
+regexpErrorDescription: "Une erreur s'est produite dans l'expression régulière sur la ligne {line} de votre mot muet {tab} :"
instanceMute: "Instance en sourdine"
userSaysSomething: "{name} a dit quelque chose"
makeActive: "Activer"
@@ -675,6 +678,7 @@ useGlobalSettingDesc: "S'il est activé, les paramètres de notification de votr
other: "Autre"
regenerateLoginToken: "Régénérer le jeton de connexion"
regenerateLoginTokenDescription: "Générer un nouveau jeton d'authentification. Cette opération ne devrait pas être nécessaire ; lors de la génération d'un nouveau jeton, tous les appareils seront déconnectés. "
+theKeywordWhenSearchingForCustomEmoji: "Ce mot-clé est utilisé lors de la recherche des émojis personnalisés."
setMultipleBySeparatingWithSpace: "Vous pouvez en définir plusieurs, en les séparant par des espaces."
fileIdOrUrl: "ID du fichier ou URL"
behavior: "Comportement"
@@ -989,6 +993,7 @@ neverShow: "Ne plus afficher"
remindMeLater: "Peut-être plus tard"
didYouLikeMisskey: "Avez-vous aimé Misskey ?"
pleaseDonate: "Misskey est le logiciel libre utilisé par {host}. Merci de faire un don pour que nous puissions continuer à le développer !"
+correspondingSourceIsAvailable: "Le code source correspondant est disponible à {anchor}"
roles: "Rôles"
role: "Rôles"
noRole: "Aucun rôle"
@@ -1003,6 +1008,7 @@ youCannotCreateAnymore: "Vous avez atteint la limite de création."
cannotPerformTemporary: "Temporairement indisponible"
cannotPerformTemporaryDescription: "Temporairement indisponible puisque le nombre d'opérations dépasse la limite. Veuillez patienter un peu, puis réessayer."
invalidParamError: "Paramètres invalides"
+invalidParamErrorDescription: "Les paramètres de la requête sont invalides. Il s'agit généralement d'un bogue, mais cela peut aussi être causé par un excès de caractères ou quelque chose de similaire."
permissionDeniedError: "Opération refusée"
permissionDeniedErrorDescription: "Ce compte n'a pas la permission d'effectuer cette opération."
preset: "Préréglage"
@@ -1016,6 +1022,7 @@ thisPostMayBeAnnoyingCancel: "Annuler"
thisPostMayBeAnnoyingIgnore: "Publier quand-même"
collapseRenotes: "Réduire les renotes déjà vues"
internalServerError: "Erreur interne du serveur"
+internalServerErrorDescription: "Une erreur inattendue s'est produite sur le serveur."
copyErrorInfo: "Copier les détails de l’erreur"
joinThisServer: "S'inscrire à cette instance"
exploreOtherServers: "Trouver une autre instance"
@@ -1035,8 +1042,10 @@ nonSensitiveOnlyForLocalLikeOnlyForRemote: "Non sensibles seulement (mentions j'
rolesAssignedToMe: "Rôles attribués à moi"
resetPasswordConfirm: "Souhaitez-vous réinitialiser votre mot de passe ?"
sensitiveWords: "Mots sensibles"
+sensitiveWordsDescription: "Définir la visibilité des notes contenant un mot défini ici au fil principal automatiquement. Vous pouvez définir plusieurs valeurs en les séparant par des sauts de ligne."
sensitiveWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
prohibitedWords: "Mots interdits"
+prohibitedWordsDescription: "Publier une note contenant un mot défini ici produira une erreur. Vous pouvez définir plusieurs valeurs en les séparant par des sauts de ligne."
prohibitedWordsDescription2: "Séparer par une espace pour créer une expression AND ; entourer de barres obliques pour créer une expression régulière."
hiddenTags: "Hashtags cachés"
hiddenTagsDescription: "Les hashtags définis ne s'afficheront pas dans les tendances. Vous pouvez définir plusieurs hashtags en faisant un saut de ligne."
@@ -1082,9 +1091,11 @@ pleaseConfirmBelowBeforeSignup: "Pour vous inscrire sur cette instance, vous dev
pleaseAgreeAllToContinue: "Pour continuer, veuillez accepter tous les champs ci-dessus."
continue: "Continuer"
preservedUsernames: "Noms d'utilisateur·rice réservés"
+preservedUsernamesDescription: "Énumérez les noms d'utilisateur à réserver, séparés par des nouvelles lignes. Les noms d'utilisateur spécifiés ici ne seront plus utilisables lors de la création d'un compte, sauf la création manuelle par un administrateur. De plus, les comptes existants ne seront pas affectés."
createNoteFromTheFile: "Rédiger une note de ce fichier"
archive: "Archive"
channelArchiveConfirmTitle: "Voulez-vous vraiment archiver {name} ?"
+channelArchiveConfirmDescription: "Une fois archivé, le canal n'apparaîtra plus dans la liste des canaux ni dans les résultats de recherche, et la publication des nouvelles notes sera impossible."
thisChannelArchived: "Ce canal a été archivé."
displayOfNote: "Affichage de la note"
initialAccountSetting: "Configuration initiale du profil"
@@ -1113,6 +1124,8 @@ createWithOptions: "Options"
createCount: "Quantité à créer"
inviteCodeCreated: "Code d'invitation créé"
inviteLimitExceeded: "Vous avez atteint la limite de codes d'invitation que vous pouvez générer."
+createLimitRemaining: "Codes d'invitation pouvant être créés : {limit} restants"
+inviteLimitResetCycle: "Vous pouvez créer jusqu'à {limit} codes d'invitation en {time}."
expirationDate: "Date d’expiration"
noExpirationDate: "Ne pas expirer"
inviteCodeUsedAt: "Code d'invitation utilisé à"
@@ -1132,11 +1145,14 @@ forYou: "Pour vous"
currentAnnouncements: "Annonces actuelles"
pastAnnouncements: "Annonces passées"
youHaveUnreadAnnouncements: "Il y a des annonces non lues."
+useSecurityKey: "Suivez les instructions de votre navigateur ou de votre appareil pour utiliser une clé de sécurité ou une clé d'accès."
replies: "Réponses"
renotes: "Renotes"
loadReplies: "Inclure les réponses"
loadConversation: "Afficher la conversation"
pinnedList: "Liste épinglée"
+keepScreenOn: "Garder l'écran toujours allumé"
+verifiedLink: "Votre propriété de ce lien a été vérifiée"
notifyNotes: "Notifier à propos des nouvelles notes"
unnotifyNotes: "Ne pas notifier pour la publication des notes"
authentication: "Authentification"
@@ -1146,6 +1162,7 @@ showRenotes: "Afficher les renotes"
edited: "Modifié"
notificationRecieveConfig: "Paramètres des notifications"
mutualFollow: "Abonnement mutuel"
+followingOrFollower: "Abonnement ou abonné"
fileAttachedOnly: "Avec fichiers joints seulement"
showRepliesToOthersInTimeline: "Afficher les réponses aux autres dans le fil"
hideRepliesToOthersInTimeline: "Masquer les réponses aux autres dans le fil"
@@ -1201,10 +1218,16 @@ ranking: "Classement"
lastNDays: "Derniers {n} jours"
backToTitle: "Retourner au titre"
hemisphere: "Votre région"
+withSensitive: "Afficher les notes contenant des fichiers joints sensibles"
+userSaysSomethingSensitive: "Note de {name} contenant des fichiers joints sensibles"
enableHorizontalSwipe: "Glisser pour changer d'onglet"
loading: "Chargement en cours"
surrender: "Annuler"
gameRetry: "Réessayer"
+_delivery:
+ stop: "Suspendu·e"
+ _type:
+ none: "Publié"
_bubbleGame:
howToPlay: "Comment jouer"
hold: "Réserver"
@@ -1212,15 +1235,25 @@ _bubbleGame:
score: "Score"
scoreYen: "Montant gagné"
highScore: "Meilleur score"
+ maxChain: "Nombre maximum de chaînes"
yen: "{yen} yens"
+ estimatedQty: "{qty} pièces"
_announcement:
forExistingUsers: "Pour les utilisateurs existants seulement"
+ needConfirmationToRead: "Exiger la confirmation de la lecture"
+ needConfirmationToReadDescription: "Si activé, afficher un dialogue de confirmation quand l'annonce est marquée comme lue. Aussi, elle sera exclue de « marquer tout comme lu » ."
+ end: "Archiver l'annonce"
+ tooManyActiveAnnouncementDescription: "Un grand nombre d'annonces actives peut baisser l'expérience utilisateur. Considérez d'archiver les annonces obsolètes."
readConfirmTitle: "Marquer comme lu ?"
+ readConfirmText: "Cela marquera le contenu de « {title} » comme lu."
shouldNotBeUsedToPresentPermanentInfo: "Puisque cela pourrait nuire considérablement à l'expérience utilisateur pour les nouveaux utilisateurs, il est recommandé d'utiliser les annonces pour afficher des informations temporaires plutôt que des informations persistantes."
dialogAnnouncementUxWarn: "Avoir deux ou plus annonces de style dialogue en même temps pourrait nuire considérablement à l'expérience utilisateur. Veuillez les utiliser avec caution."
silence: "Ne pas me notifier"
silenceDescription: "Si activée, vous ne recevrez pas de notifications sur les annonces et n'aurez pas besoin de les marquer comme lues."
_initialAccountSetting:
+ accountCreated: "Votre compte a été créé avec succès !"
+ letsStartAccountSetup: "Procédons au réglage initial du compte."
+ letsFillYourProfile: "Commençons par configurer votre profil !"
profileSetting: "Paramètres du profil"
privacySetting: "Paramètres de confidentialité"
initialAccountSettingCompleted: "Configuration du profil terminée avec succès !"
@@ -1288,7 +1321,7 @@ _initialTutorial:
doItToContinue: "Marquez le fichier joint comme sensible pour procéder."
_done:
title: "Le tutoriel est terminé ! 🎉"
- description: "Les fonctionnalités introduites ici ne sont que quelques-unes. Pour savoir plus sur l'utilisation de Misskey, veuillez consulter {lien}."
+ description: "Les fonctionnalités introduites ici ne sont que quelques-unes. Pour savoir plus sur l'utilisation de Misskey, veuillez consulter {link}."
_timelineDescription:
home: "Sur le fil principal, vous pouvez voir les notes des utilisateurs auxquels vous êtes abonné·e."
local: "Sur le fil local, vous pouvez voir les notes de tous les utilisateurs sur cette instance."
@@ -1449,7 +1482,7 @@ _role:
edit: "Modifier le rôle"
name: "Nom du rôle"
description: "Description du rôle"
- permission: "Rôle et autorisations"
+ permission: "Autorisations du rôle"
assignTarget: "Attribuer"
manual: "Manuel"
manualRoles: "Rôles manuels"
@@ -1984,7 +2017,7 @@ _notification:
unreadAntennaNote: "Antenne {name}"
roleAssigned: "Rôle attribué"
emptyPushNotificationMessage: "Les notifications push ont été mises à jour"
- achievementEarned: "Accomplissement"
+ achievementEarned: "Accomplissement déverrouillé"
testNotification: "Tester la notification"
reactedBySomeUsers: "{n} utilisateur·rice·s ont réagi"
renotedBySomeUsers: "{n} utilisateur·rice·s ont renoté"
@@ -2001,7 +2034,7 @@ _notification:
receiveFollowRequest: "Demande d'abonnement reçue"
followRequestAccepted: "Demande d'abonnement acceptée"
roleAssigned: "Rôle reçu"
- achievementEarned: "Accomplissement"
+ achievementEarned: "Déverrouillage d'accomplissement"
app: "Notifications provenant des apps"
_actions:
followBack: "Suivre"
@@ -2139,4 +2172,5 @@ _dataSaver:
title: "Mise en évidence du code"
description: "Si la notation de mise en évidence du code est utilisée, par exemple dans la MFM, elle ne sera pas chargée tant qu'elle n'aura pas été tapée. La mise en évidence du code nécessite le chargement du fichier de définition de chaque langue à mettre en évidence, mais comme ces fichiers ne sont plus chargés automatiquement, on peut s'attendre à une réduction du trafic de données."
_reversi:
+ waitingBoth: "Préparez-vous"
total: "Total"
diff --git a/locales/hr-HR.yml b/locales/hr-HR.yml
index 881aa8464e..9cfebdd01a 100644
--- a/locales/hr-HR.yml
+++ b/locales/hr-HR.yml
@@ -3,4 +3,3 @@ _lang_: "japanski"
ok: "OK"
gotIt: "Razumijem"
cancel: "otkazati"
-
diff --git a/locales/ht-HT.yml b/locales/ht-HT.yml
index 1698c9f280..e3595c79b6 100644
--- a/locales/ht-HT.yml
+++ b/locales/ht-HT.yml
@@ -16,4 +16,3 @@ _2fa:
renewTOTPCancel: "Sispann"
_widgets:
profile: "pwofil"
-
diff --git a/locales/hu-HU.yml b/locales/hu-HU.yml
index 2f7006484a..023a91494d 100644
--- a/locales/hu-HU.yml
+++ b/locales/hu-HU.yml
@@ -102,4 +102,3 @@ _deck:
_columns:
notifications: "Értesítések"
tl: "Idővonal"
-
diff --git a/locales/id-ID.yml b/locales/id-ID.yml
index b638d7991f..ac87d92f82 100644
--- a/locales/id-ID.yml
+++ b/locales/id-ID.yml
@@ -108,11 +108,14 @@ enterEmoji: "Masukkan emoji"
renote: "Renote"
unrenote: "Hapus renote"
renoted: "Telah direnote"
+renotedToX: "{name} telah merenote"
cantRenote: "Postingan ini tidak dapat direnote"
cantReRenote: "Renote tidak dapat direnote"
quote: "Kutip"
inChannelRenote: "Hanya renote dalam kanal"
inChannelQuote: "Hanya kutip dalam kanal"
+renoteToChannel: "Renote ke kanal"
+renoteToOtherChannel: "Renote ke kanal lainnya"
pinnedNote: "Catatan yang disematkan"
pinned: "Sematkan ke profil"
you: "Kamu"
@@ -400,6 +403,7 @@ name: "Nama"
antennaSource: "Sumber Antenna"
antennaKeywords: "Kata kunci yang diterima"
antennaExcludeKeywords: "Kata kunci yang dikecualikan"
+antennaExcludeBots: "Kecualikan akun bot"
antennaKeywordsDescription: "Pisahkan dengan spasi untuk kondisi AND. Pisahkan dengan baris baru untuk kondisi OR."
notifyAntenna: "Beritahu untuk catatan baru"
withFileAntenna: "Hanya tampilkan catatan dengan berkas yang dilampirkan"
@@ -467,6 +471,7 @@ retype: "Masukkan ulang"
noteOf: "Catatan milik {user}"
quoteAttached: "Dikutip"
quoteQuestion: "Apakah kamu ingin menambahkan kutipan?"
+attachAsFileQuestion: "Teks dalam papan klip terlalu panjang. Apakah kamu ingin melampirkannya sebagai berkas teks?"
noMessagesYet: "Tidak ada pesan"
newMessageExists: "Kamu mendapatkan pesan baru"
onlyOneFileCanBeAttached: "Kamu hanya dapat melampirkan satu berkas ke dalam pesan"
@@ -494,6 +499,7 @@ emojiStyle: "Gaya emoji"
native: "Native"
disableDrawer: "Jangan gunakan menu bergaya laci"
showNoteActionsOnlyHover: "Hanya tampilkan aksi catatan saat ditunjuk"
+showReactionsCount: "Lihat jumlah reaksi dalam catatan"
noHistory: "Tidak ada riwayat"
signinHistory: "Riwayat masuk"
enableAdvancedMfm: "Nyalakan MFM tingkat lanjut"
@@ -991,6 +997,7 @@ neverShow: "Jangan tampilkan lagi"
remindMeLater: "Mungkin nanti"
didYouLikeMisskey: "Apakah kamu mulai menyukai Misskey?"
pleaseDonate: "{host} menggunakan perangkat lunak bebas yaitu Misskey. Kami sangat mengapresiasi sekali donasi dari kamu agar pengembangan Misskey tetap dapat berlanjut!"
+correspondingSourceIsAvailable: "Sumber kode terkait tersedia di {anchor}"
roles: "Peran"
role: "Peran"
noRole: "Peran tidak temukan"
@@ -1042,6 +1049,7 @@ sensitiveWords: "Kata sensitif"
sensitiveWordsDescription: "Visibilitas dari semua catatan mengandung kata yang telah diatur akan dijadikan \"Beranda\" secara otomatis. Kamu dapat mendaftarkan kata tersebut lebih dari satu dengan menuliskannya di baris baru."
sensitiveWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
prohibitedWords: "Kata yang dilarang"
+prohibitedWordsDescription: "Menyalakan kesalahan ketika mencoba untuk memposting catatan dengan set kata-kata yang termasuk. Beberapa kata dapat diatur dan dipisahkan dengan baris baru."
prohibitedWordsDescription2: "Menggunakan spasi akan membuat ekspresi AND dan kata kunci disekitarnya dengan garis miring akan mengubahnya menjadi ekspresi reguler."
hiddenTags: "Tagar tersembunyi"
hiddenTagsDescription: "Pilih tanda yang mana akan tidak diperlihatkan dalam daftar tren.\nTanda lebih dari satu dapat didaftarkan dengan tiap baris."
@@ -1158,6 +1166,7 @@ showRenotes: "Tampilkan renote"
edited: "Telah disunting"
notificationRecieveConfig: "Pengaturan notifikasi"
mutualFollow: "Saling mengikuti"
+followingOrFollower: "Mengikuti atau pengikut"
fileAttachedOnly: "Hanya catatan dengan berkas"
showRepliesToOthersInTimeline: "Tampilkan balasan ke pengguna lain dalam lini masa"
hideRepliesToOthersInTimeline: "Sembunyikan balasan ke orang lain dari lini masa"
@@ -1167,6 +1176,12 @@ confirmShowRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk
confirmHideRepliesAll: "Operasi ini tidak dapat diubah. Apakah kamu yakin untuk menyembunyikan balasan ke lainnya dari semua orang yang kamu ikuti di lini masa?"
externalServices: "Layanan eksternal"
sourceCode: "Sumber kode"
+sourceCodeIsNotYetProvided: "Sumber kode belum tersedia. Hubungi admin untuk memperbaiki masalah ini."
+repositoryUrl: "URL Repositori"
+repositoryUrlDescription: "Jika kamu menggunakan Misskey begitu saja (tanpa ada perubahan dalam kode sumber), masukkan https://github.com/misskey-dev/misskey"
+repositoryUrlOrTarballRequired: "Apabila kamu masih mempublikasikan repositori, kamu setidaknya harus menyediakan berkas tarball. Lihat .config/example.yml untuk informasi lebih lanjut."
+feedback: "Umpan balik"
+feedbackUrl: "URL Umpan balik"
impressum: "Impressum"
impressumUrl: "Tautan Impressum"
impressumDescription: "Pada beberapa negara seperti Jerman, inklusi dari informasi kontak operator (sebuah Impressum) diperlukan secara legal untuk situs web komersil."
@@ -1202,6 +1217,8 @@ soundWillBePlayed: "Suara yang akan dimainkan"
showReplay: "Lihat tayangan ulang"
replay: "Tayangan ulang"
replaying: "Menayangkan Ulang"
+endReplay: "Keluat dari tayangan ulang"
+copyReplayData: "Salin data tayangan ulang"
ranking: "Peringkat"
lastNDays: "{n} hari terakhir"
backToTitle: "Ke Judul"
@@ -1209,11 +1226,43 @@ hemisphere: "Letak kamu tinggal"
withSensitive: "Lampirkan catatan dengan berkas sensitif"
userSaysSomethingSensitive: "Postingan oleh {name} mengandung konten sensitif"
enableHorizontalSwipe: "Geser untuk mengganti tab"
+loading: "Memuat..."
surrender: "Batalkan"
+gameRetry: "Coba lagi"
+notUsePleaseLeaveBlank: "Kosongi bila tidak digunakan"
+useTotp: "Gunakan TOTP"
+useBackupCode: "Gunakan kode cadangan"
+launchApp: "Luncurkan Aplikasi"
+useNativeUIForVideoAudioPlayer: "Gunakan antarmuka peramban ketika memainkan video dan audio"
+keepOriginalFilename: "Simpan nama berkas asli"
+keepOriginalFilenameDescription: "Apabila pengaturan ini dimatikan, nama berkas akan diganti dengan string acak secara otomatis ketika kamu mengunggah berkas."
+noDescription: "Tidak ada deskripsi"
+alwaysConfirmFollow: "Selalu konfirmasi ketika mengikuti"
+inquiry: "Hubungi kami"
+_delivery:
+ status: "Status pengiriman"
+ stop: "Ditangguhkan"
+ resume: "Lanjutkan pengiriman"
+ _type:
+ none: "Sedang menyiarkan langsung"
+ manuallySuspended: "Ditangguhkan manual"
+ goneSuspended: "Sedang ditangguhkan untuk penghapusan peladen"
+ autoSuspendedForNotResponding: "Sedang ditangguhkan karena peladen tidak menjawab"
_bubbleGame:
howToPlay: "Cara bermain"
+ hold: "Tahan"
+ _score:
+ score: "Skor"
+ scoreYen: "Jumlah uang didapat"
+ highScore: "Skor tertinggi"
+ maxChain: "Jumlah skor berantai"
+ yen: "{yen} Yen"
+ estimatedQty: "{qty} buah"
+ scoreSweets: "{onigiriQtyWithUnit} onigiri"
_howToPlay:
section1: "Atur posisi dan jatuhkan obyek ke dalam kotak."
+ section2: "Ketika dua obyek menyentuh tipe yang sama satu sama lain, obyek tersebut akan berganti dan kamu mendapatkan poin skor."
+ section3: "Permainan berakhir jika obyek memenuhi kotak. Capai skor tertinggi dengan menggabungkan obyek bersama sambil menghindari obyek tersebut memenuhi kotak permainan!"
_announcement:
forExistingUsers: "Hanya pengguna yang telah ada"
forExistingUsersDescription: "Pengumuman ini akan dimunculkan ke pengguna yang sudah ada dari titik waktu publikasi jika dinyalakan. Apabila dimatikan, mereka yang baru mendaftar setelah publikasi ini akan juga melihatnya."
@@ -1257,26 +1306,59 @@ _initialTutorial:
reply: "Klik pada tombol ini untuk membalas ke sebuah pesan. Bisa juga untuk membalas ke sebuah balasan dan melanjutkannya seperti percakapan selayaknya utas."
renote: "Kamu dapat membagikan catatan ke lini masa milikmu. Kamu juga dapat mengutipnya dengan komentarmu."
reaction: "Kamu dapat menambahkan reaksi ke Catatan. Detil lebih lanjut akan dijelaskan di halaman berikutnya."
+ menu: "Kamu dapat melihat detil catatan, menyalin tautan, dan melakukan aksi lainnya."
_reaction:
title: "Apa itu Reaksi?"
+ description: "Catatan dapat direaksi dengan berbagai emoji. Reaksi memperbolehkan kamu untuk mengekspresikan nuansa yang tidak dapat disampaikan hanya dengan sebuah \"suka\"."
+ letsTryReacting: "Reaksi dapat ditambahkan dengan mengklik tombol '+' pada catatan. Coba lakukan mereaksi contoh catatan ini!"
+ reactToContinue: "Tambahkan reaksi untuk melanjutkan."
+ reactNotification: "Kamu akan menerima notifikasi real0time ketika seseorang mereaksi catatan kamu."
+ reactDone: "Kamu dapat mengurungkan reaksi dengan menekan tombol '-'."
_timeline:
title: "Konsep Lini Masa"
+ description1: "Misskey menyediakan berbagai lini masa sesuai dengan penggunaan (beberapa mungkin tidak tersedia karena bergantung dengan kebijakan peladen)."
+ home: "Kamu dapat melihat catatan dari akun yang kamu ikuti."
+ local: "Kamu dapat melihat catatan dari semua pengguna yang ada pada peladen ini."
+ social: "Catatan dari linimasa Beranda dan Lokal akan ditampilkan."
+ global: "Kamu dapat melihat catatan dari semua peladen yang terhubung."
+ description2: "Kamu dapat mengganti linimasa di bagian atas layar kamu kapan saja."
+ description3: "Sebagai tambahan, terdapat juga linimasa daftar dan linimasa kanal. Untuk detil lebih lanjut, silahkan melihat ke tautan berikut: {link}."
_postNote:
title: "Pengaturan posting Catatan"
+ description1: "Ketika memposting catatan ke Misskey, terdapat beberapa opsi yang tersedia. Form posting terlihat seperti ini."
_visibility:
+ description: "Kamu dapat membatasi siapa yang dapat melihat catatan kamu."
public: "Perlihatkan catatan ke semua pengguna."
home: "Hanya publik ke lini masa Beranda. Pengguna yang mengunjungi profilmu melalui pengikut dan renote dapat melihatnya."
followers: "Perlihatkan ke pengikut saja. Hanya pengikut yang dapat melihat postinganmu dan tidak dapat direnote oleh siapapun."
direct: "Hanya perlihatkan ke pengguna spesifik dan penerima akan diberi tahu. Dapat juga digunakan sebagai alternatif dari pesan langsung."
+ doNotSendConfidencialOnDirect1: "Hati-hati ketika mengirim informasi yang sensitif!"
+ doNotSendConfidencialOnDirect2: "Admin dari peladen dapat melihat apa yang kamu tulis. Hati-hati dengan informasi sensitif ketika mengirimkan catatan langsung kepada pengguna pada peladen yang tidak dipercaya."
+ localOnly: "Memposting dengan opsi ini tidak akan memfederasi catatan ke peladen lain. Pengguna pada peladen lain tidak akan dapat melihat catatan ini secara langsung, meskipun dengan pengaturan visibilitas yang sudah diatur di atas."
_cw:
title: "Peringatan Konten (CW)"
+ description: "Alih-alih isinya, konten yang ditulis dalam kolom 'komentar' akan ditampilkan. Menekan 'Selebihnya' akan menampilkan isi konten."
_exampleNote:
cw: "Peringatan: Bikin Lapar!"
note: "Baru aja makan donat berlapis coklat ðŸ©ðŸ˜‹"
+ useCases: "Fungsi ini digunakan ketika mengikutik panduan peladen untuk catatan yang dibutuhkan atau untuk membatasi diri dari teks sensitif atau spoiler."
_howToMakeAttachmentsSensitive:
title: "Bagaimana menandai lampiran sebagai sensitif?"
+ description: "Fungsi ini digunakan untuk lampiran yang dibutuhkan oleh panduan peladen atau sesuatu yang seharusnya tidak boleh dibiarkan begitu saja dengan cara menambahkan penanda \"sensitif\"."
+ tryThisFile: "Coba tandai gambar yang dilampirkan pada form ini sebagai sensitif!"
+ _exampleNote:
+ note: "Ups, kesalahan banget buka penutup wadah natto..."
+ method: "Untuk menandai lampiran sebagai sensitif, klik gambar pada berkas, buka menu, lalu klik \"Tandai sebagai sensitif\"."
+ sensitiveSucceeded: "Ketika melampirkan berkas, mohon atur sensitifitas sesuai dengan panduan peladen."
+ doItToContinue: "Tandai berkas terlampir sebagai sensitif untuk melanjutkan."
_done:
title: "Kamu telah menyelesaikan tutorial! 🎉"
+ description: "Fungsi yang diperkenalkan di sini merupakan sebagian kecil dari fitur yang ada. Untuk pemahaman lebih detil dalam menggunakan Misskey, kamu dapat merujuk ke {link}."
+_timelineDescription:
+ home: "Pada linimasa Beranda, kamu dapat melihat catatan dari akun yang kamu ikuti."
+ local: "Pada linimasa Lokal, kamu dapat melihat catatan dari semua pengguna yang ada pada peladen ini."
+ social: "Linimasa sosial menampilkan catatan dari kedua linimasa Beranda dan Lokal."
+ global: "Pada linimasa Global, kamu dapat melihat catatan dari semua peladen yang terhubung."
_serverRules:
description: "Daftar peraturan akan ditampilkan sebelum pendaftaran. Mengatur ringkasan dari Syarat dan Ketentuan sangat direkomendasikan."
_serverSettings:
@@ -1288,6 +1370,9 @@ _serverSettings:
manifestJsonOverride: "Ambil alih manifest.json"
shortName: "Nama pendek"
shortNameDescription: "Inisial untuk nama instansi yang dapat ditampilkan apabila nama lengkap resmi terlalu panjang."
+ fanoutTimelineDescription: "Dapat meningkatkan performa dalam pengambilan data linimasa dan mengurangi beban pada database ketika dinyalakan. Sebagai gantinya, penggunaan memory pada Redis akan meningkan. Pertimbangkan untuk menonaktifkan fitur ini jika mengalami kekurangan memori pada server atau menyebabkan server tidak stabil."
+ fanoutTimelineDbFallback: "Fallback ke database"
+ fanoutTimelineDbFallbackDescription: "Ketika diaktifkan, lini masa akan fallback ke database untuk melakukan kueri tambahan apabila linimasa tidak disimpan dalam cache. Menonaktifkan ini dapat mengurangi beban server dengan mengeliminasi proses fallback, namun dapat berakibat membatasi jarak data dari lini masa yang dapat diambil."
_accountMigration:
moveFrom: "Pindahkan akun lain ke akun ini"
moveFromSub: "Buat alias ke akun lain"
@@ -1545,6 +1630,16 @@ _achievements:
_smashTestNotificationButton:
title: "Tes overflow"
description: "Picu tes notifikasi secara berulang dalam waktu yang sangat pendek"
+ _tutorialCompleted:
+ title: "Ijazah Sekolah Dasar Misskey"
+ description: "Tutorial selesai"
+ _bubbleGameExplodingHead:
+ title: "🤯"
+ description: "Obyek paling terbesar di permainan gelembung"
+ _bubbleGameDoubleExplodingHead:
+ title: "Ganda 🤯"
+ description: "Dua dari obyek paling terbesar pada permainan gelembung di waktu yang sama"
+ flavor: "Kamu dapat mengisi kotak makan siang seperti ini 🤯 🤯."
_role:
new: "Buat peran"
edit: "Sunting peran"
@@ -1555,7 +1650,9 @@ _role:
assignTarget: "Tipe tugas"
descriptionOfAssignTarget: "<b>Manual</b> untuk mengganti secara manual siapa yang mendapatkan peran ini dan siapa yang tidak.\n<b>Kondisional</b> untuk pengguna secara otomatis dimasukkan atau dihapus dari peran berdasarkan kondisi yang ditentukan."
manual: "Manual"
+ manualRoles: "Peran manual"
conditional: "Kondisional"
+ conditionalRoles: "Peran kondisional"
condition: "Kondisi"
isConditionalRole: "Ini adalah peran kondisional"
isPublic: "Publikkan Peran"
@@ -1583,6 +1680,7 @@ _role:
gtlAvailable: "Dapat melihat lini masa global"
ltlAvailable: "Dapat melihat lini masa lokal"
canPublicNote: "Dapat mengirim catatan publik"
+ mentionMax: "Jumlah maksimum sebutan dalam sebuah catatan"
canInvite: "Dapat membuat kode undangan instansi"
inviteLimit: "Batas jumlah undangan"
inviteLimitCycle: "Interval Penerbitan Kode Undangan"
@@ -1604,9 +1702,16 @@ _role:
canHideAds: "Dapat menyembunyikan iklan"
canSearchNotes: "Penggunaan pencarian catatan"
canUseTranslator: "Penggunaan penerjemah"
+ avatarDecorationLimit: "Jumlah maksimum dekorasi avatar yang dapat diterapkan"
_condition:
+ roleAssignedTo: "Ditugaskan ke peran manual"
isLocal: "Pengguna lokal"
isRemote: "Pengguna remote"
+ isCat: "Pengguna Kucing"
+ isBot: "Pengguna Bot"
+ isSuspended: "Pengguna yang ditangguhkan"
+ isLocked: "Akun privat"
+ isExplorable: "Pengguna efektif yang akunnya dapat dicari"
createdLessThan: "Telah berlalu kurang dari X sejak pembuatan akun"
createdMoreThan: "Telah berlalu lebih dari X sejak pembuatan akun"
followersLessThanOrEq: "Memiliki pengikut X atau kurang dari tersebut"
@@ -1632,6 +1737,7 @@ _emailUnavailable:
disposable: "Alamat surel temporer tidak dapat digunakan"
mx: "Peladen alamat surel ini tidak valid"
smtp: "Peladen alamat surel ini tidak merespon"
+ banned: "Kamu tidak dapat mendaftar dengan alamat surel ini"
_ffVisibility:
public: "Terbitkan"
followers: "Tampil untuk pengikut saja"
@@ -1675,6 +1781,7 @@ _plugin:
installWarn: "Mohon jangan memasang plugin yang tidak dapat dipercayai."
manage: "Manajemen plugin"
viewSource: "Lihat sumber"
+ viewLog: "Tampilkan log"
_preferencesBackups:
list: "Cadangan yang dibuat"
saveNew: "Simpan cadangan baru"
@@ -1704,10 +1811,13 @@ _aboutMisskey:
contributors: "Kontributor utama"
allContributors: "Seluruh kontributor"
source: "Sumber kode"
+ original: "Asli"
+ thisIsModifiedVersion: "{name} menggunakan versi modifikasi dari Misskey yang asli."
translation: "Terjemahkan Misskey"
donate: "Donasi ke Misskey"
morePatrons: "Kami sangat mengapresiasi dukungan dari banyak penolong lain yang tidak tercantum disini. Terima kasih! 🥰"
patrons: "Pendukung"
+ projectMembers: "Anggota proyek"
_displayOfSensitiveMedia:
respect: "Sembunyikan media yang ditandai sensitif"
ignore: "Tampilkan media yang ditandai sensitif"
@@ -1732,6 +1842,7 @@ _channel:
notesCount: "terdapat {n} catatan"
nameAndDescription: "Nama dan deskripsi"
nameOnly: "Hanya nama"
+ allowRenoteToExternal: "Perbolehkan catat ulang dan kutipan di luar dari kanal"
_menuDisplay:
sideFull: "Horisontal"
sideIcon: "Horisontal (Ikon)"
@@ -1860,7 +1971,6 @@ _2fa:
registerTOTP: "Daftarkan aplikasi autentikator"
step1: "Pertama, pasang aplikasi autentikasi (seperti {a} atau {b}) di perangkat kamu."
step2: "Lalu, pindai kode QR yang ada di layar."
- step2Click: "Mengeklik kode QR ini akan membolehkanmu untuk mendaftarkan 2FA ke security-key atau aplikasi autentikator ponsel."
step2Uri: "Masukkan URI berikut jika kamu menggunakan program desktop"
step3Title: "Masukkan kode autentikasi"
step3: "Masukkan token yang telah disediakan oleh aplikasimu untuk menyelesaikan pemasangan."
@@ -1884,6 +1994,7 @@ _2fa:
backupCodesDescription: "Kamu dapat menggunakan kode ini untuk mendapatkan akses ke akun kamu apabila berada dalam situasi tidak dapat menggunakan aplikasi autentikasi 2-faktor yang kamu miliki. Setiap kode hanya dapat digunakan satu kali. Mohon simpan kode ini di tempat yang aman."
backupCodeUsedWarning: "Kode cadangan telah digunakan. Mohon mengatur ulang autentikasi 2-faktor secepatnya apabila kamu sudah tidak dapat menggunakannya lagi."
backupCodesExhaustedWarning: "Semua kode cadangan telah digunakan. Apabila kamu kehilangan akses pada aplikasi autentikasi 2-faktor milikmu, kamu tidak dapat mengakses akun ini lagi. Mohon atur ulang autentikasi 2-faktor kamu."
+ moreDetailedGuideHere: "Berikut panduan detilnya"
_permissions:
"read:account": "Lihat informasi akun"
"write:account": "Sunting informasi akun"
@@ -1934,7 +2045,6 @@ _permissions:
"read:admin:server-info": "Lihat informasi peladen"
"read:admin:show-moderation-log": "Lihat log moderasi"
"read:admin:show-user": "Lihat informasi pengguna privat"
- "read:admin:show-users": "Lihat informasi pengguna privat"
"write:admin:suspend-user": "Tangguhkan pengguna"
"write:admin:unset-user-avatar": "Hapus avatar pengguna"
"write:admin:unset-user-banner": "Hapus banner pengguna"
@@ -2145,6 +2255,7 @@ _play:
title: "Judul"
script: "Script"
summary: "Deskripsi"
+ visibilityDescription: "Membuat catatan ini privat berarti tidak akan terlihat pada profil kamu, namun siapapun yang memiliki URL dari catatan ini akan dapat mengaksesnya."
_pages:
newPage: "Buat halaman baru"
editPage: "Sunting halaman"
@@ -2189,6 +2300,8 @@ _pages:
section: "Bagian"
image: "Gambar"
button: "Tombol"
+ dynamic: "Blok Dinamis"
+ dynamicDescription: "Blok ini telah dihapus. Mohon gunakan {play} dari sekarang."
note: "Catatan yang ditanam"
_note:
id: "ID Catatan"
@@ -2218,8 +2331,10 @@ _notification:
sendTestNotification: "Kirim tes notifikasi"
notificationWillBeDisplayedLikeThis: "Notifikasi akan terlihat seperti ini"
reactedBySomeUsers: "{n} orang memberikan reaksi"
+ likedBySomeUsers: "{n} pengguna menyukai catatan kamu"
renotedBySomeUsers: "{n} orang telah merenote"
followedBySomeUsers: "{n} orang telah mengikuti"
+ flushNotification: "Bersihkan notifikasi"
_types:
all: "Semua"
note: "Catatan baru"
@@ -2317,6 +2432,7 @@ _moderationLogTypes:
resetPassword: "Atur ulang kata sandi"
suspendRemoteInstance: "Instansi luar telah ditangguhkan"
unsuspendRemoteInstance: "Instansi luar batal ditangguhkan"
+ updateRemoteInstanceNote: "Catatan moderasi telah diperbaharui untuk peladen luar."
markSensitiveDriveFile: "Berkas ditandai sensitif"
unmarkSensitiveDriveFile: "Berkas batal ditandai sensitif"
resolveAbuseReport: "Laporan terselesaikan"
@@ -2428,4 +2544,35 @@ _reversi:
isLlotheo: "Pemain dengan batu yang sedikit menang (Llotheo)"
loopedMap: "Peta melingkar"
canPutEverywhere: "Keping dapat ditaruh dimana saja"
-
+ timeLimitForEachTurn: "Batas waktu untuk gantian"
+ freeMatch: "Pertandingan bebas"
+ lookingForPlayer: "Mencari lawan..."
+ gameCanceled: "Permainan ini telah dibatalkan."
+ shareToTlTheGameWhenStart: "Bagikan permainan ke lini masa ketika dimulai"
+ iStartedAGame: "Permainan telah dimulai! #MisskeyReversi"
+ opponentHasSettingsChanged: "Lawan telah mengganti pengaturan mereka."
+ allowIrregularRules: "Aturan non-reguler (bebas sepenuhnya)"
+ disallowIrregularRules: "Tanpa aturan non-reguler"
+ showBoardLabels: "Tampilkan penomoran baris dan kolom pada papan"
+ useAvatarAsStone: "Ubah batu menjadi avatar pengguna"
+_offlineScreen:
+ title: "Luring - tidak dapat terhubung ke peladen"
+ header: "Tidak dapat tersambung ke server"
+_urlPreviewSetting:
+ title: "Pengaturan pratinjau URL"
+ enable: "Aktifkan pratinjau URL"
+ timeout: "Waktu timeout pratinjau URL (ms)"
+ timeoutDescription: "Apabila ini memakan waktu lama dari nilai yang ditentukan untuk mendapatkan pratinjau, pratinjau tidak akan dibuat."
+ maximumContentLength: "Content-Length Maksimum (bytes)"
+ maximumContentLengthDescription: "Apabila Content-Length lebih besar dari nilai ini, pratinjau tidak akan dibuat."
+ requireContentLength: "Buat pratinjau hanya ketika Content-Length dapat didapatkan"
+ requireContentLengthDescription: "Apabila peladen lain tidak memberika Content-Length, pratinjau tidak akan dibuat."
+ userAgent: "User-Agent"
+ userAgentDescription: "Atur User-Agent yang digunakan untuk mengambil pratinjau. Apabila dibiarkan kosong, User-Agent bawaan akan digunakan."
+ summaryProxy: "Titik akhir proksi yang membuat pratinjau"
+ summaryProxyDescription: "Bukan untuk Misskey, namun untuk menghasilkan pratinjau menggunakan Summaly Proxy."
+ summaryProxyDescription2: "Parameter berikut tertautkan dengan proksi sebagai string kueri. Apabila proksi tidak mendukung tersebut, nilai di dalamnya diabaikan."
+_mediaControls:
+ pip: "Gambar dalam Gambar"
+ playbackRate: "Kecepatan Pemutaran"
+ loop: "Ulangi Pemutaran"
diff --git a/locales/index.d.ts b/locales/index.d.ts
index 17ddbb3f63..e491bfa27f 100644
--- a/locales/index.d.ts
+++ b/locales/index.d.ts
@@ -465,6 +465,10 @@ export interface Locale extends ILocale {
*/
"rmboost": string;
/**
+ * {name} ã«ãƒªãƒŽãƒ¼ãƒˆã—ã¾ã—ãŸã€‚
+ */
+ "renotedToX": ParameterizedString<"name">;
+ /**
* ã“ã®æŠ•ç¨¿ã¯ãƒ–ーストã§ãã¾ã›ã‚“。
*/
"cantRenote": string;
@@ -485,6 +489,14 @@ export interface Locale extends ILocale {
*/
"inChannelQuote": string;
/**
+ * ãƒãƒ£ãƒ³ãƒãƒ«ã«ãƒªãƒŽãƒ¼ãƒˆ
+ */
+ "renoteToChannel": string;
+ /**
+ * ä»–ã®ãƒãƒ£ãƒ³ãƒãƒ«ã«ãƒªãƒŽãƒ¼ãƒˆ
+ */
+ "renoteToOtherChannel": string;
+ /**
* ピン留ã‚ã•れãŸãƒŽãƒ¼ãƒˆ
*/
"pinnedNote": string;
@@ -945,7 +957,7 @@ export interface Locale extends ILocale {
*/
"silencedInstances": string;
/**
- * サイレンスã—ãŸã„インスタンスã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨­å®šã—ã¾ã™ã€‚サイレンスã•れãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«æ‰€å±žã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã™ã¹ã¦ã€Œã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã€ã¨ã—ã¦æ‰±ã‚れã€ãƒ•ォローãŒã™ã¹ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ãªã‚Šã€ãƒ•ォロワーã§ãªã„ローカルアカウントã«ã¯ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã§ããªããªã‚Šã¾ã™ã€‚ブロックã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«ã¯å½±éŸ¿ã—ã¾ã›ã‚“。
+ * サイレンスã—ãŸã„サーãƒãƒ¼ã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨­å®šã—ã¾ã™ã€‚サイレンスã•れãŸã‚µãƒ¼ãƒãƒ¼ã«æ‰€å±žã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã™ã¹ã¦ã€Œã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã€ã¨ã—ã¦æ‰±ã‚れã€ãƒ•ォローãŒã™ã¹ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ãªã‚Šã¾ã™ã€‚ブロックã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«ã¯å½±éŸ¿ã—ã¾ã›ã‚“。
*/
"silencedInstancesDescription": string;
/**
@@ -1309,6 +1321,10 @@ export interface Locale extends ILocale {
*/
"selectFolders": string;
/**
+ * ファイルãŒé¸æŠžã•れã¦ã„ã¾ã›ã‚“
+ */
+ "fileNotSelected": string;
+ /**
* ファイルåを変更
*/
"renameFile": string;
@@ -1941,6 +1957,10 @@ export interface Locale extends ILocale {
*/
"quoteQuestion": string;
/**
+ * クリップボードã®ãƒ†ã‚­ã‚¹ãƒˆãŒé•·ã„ã§ã™ã€‚テキストファイルã¨ã—ã¦æ·»ä»˜ã—ã¾ã™ã‹ï¼Ÿ
+ */
+ "attachAsFileQuestion": string;
+ /**
* ã¾ã ãƒãƒ£ãƒƒãƒˆã¯ã‚りã¾ã›ã‚“
*/
"noMessagesYet": string;
@@ -4246,7 +4266,7 @@ export interface Locale extends ILocale {
*/
"thisPostIsMissingAltText": string;
/**
- * 見ãŸã“ã¨ã®ã‚るブーストをçœç•¥ã—ã¦è¡¨ç¤º
+ * ブーストã®ã‚¹ãƒžãƒ¼ãƒˆçœç•¥
*/
"collapseRenotes": string;
/**
@@ -4258,6 +4278,10 @@ export interface Locale extends ILocale {
*/
"autoloadConversation": string;
/**
+ * リアクションやリノートをã—ãŸã“ã¨ãŒã‚るノートをãŸãŸã‚“ã§è¡¨ç¤ºã—ã¾ã™ã€‚
+ */
+ "collapseRenotesDescription": string;
+ /**
* サーãƒãƒ¼å†…部エラー
*/
"internalServerError": string;
@@ -5153,6 +5177,38 @@ export interface Locale extends ILocale {
* ãŠå•ã„åˆã‚ã›
*/
"inquiry": string;
+ "_delivery": {
+ /**
+ * é…信状態
+ */
+ "status": string;
+ /**
+ * é…ä¿¡åœæ­¢
+ */
+ "stop": string;
+ /**
+ * é…ä¿¡å†é–‹
+ */
+ "resume": string;
+ "_type": {
+ /**
+ * é…信中
+ */
+ "none": string;
+ /**
+ * æ‰‹å‹•åœæ­¢ä¸­
+ */
+ "manuallySuspended": string;
+ /**
+ * サーãƒãƒ¼å‰Šé™¤ã®ãŸã‚åœæ­¢ä¸­
+ */
+ "goneSuspended": string;
+ /**
+ * サーãƒãƒ¼å¿œç­”ãªã—ã®ãŸã‚åœæ­¢ä¸­
+ */
+ "autoSuspendedForNotResponding": string;
+ };
+ };
"_bubbleGame": {
/**
* éŠã³æ–¹
@@ -5612,6 +5668,14 @@ export interface Locale extends ILocale {
* 有効ã«ã™ã‚‹ã¨ã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ãŒã‚­ãƒ£ãƒƒã‚·ãƒ¥ã•れã¦ã„ãªã„å ´åˆã«DBã¸è¿½åŠ ã§å•ã„åˆã‚ã›ã‚’行ã†ãƒ•ォールãƒãƒƒã‚¯å‡¦ç†ã‚’行ã„ã¾ã™ã€‚無効ã«ã™ã‚‹ã¨ã€ãƒ•ォールãƒãƒƒã‚¯å‡¦ç†ã‚’行ã‚ãªã„ã“ã¨ã§ã•らã«ã‚µãƒ¼ãƒãƒ¼ã®è² è·ã‚’軽減ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ãŒã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ãŒå–å¾—ã§ãる範囲ã«åˆ¶é™ãŒç”Ÿã˜ã¾ã™ã€‚
*/
"fanoutTimelineDbFallbackDescription": string;
+ /**
+ * å•ã„åˆã‚ã›å…ˆURL
+ */
+ "inquiryUrl": string;
+ /**
+ * サーãƒãƒ¼é‹å–¶è€…ã¸ã®ãŠå•ã„åˆã‚ã›ãƒ•ォームã®URLã‚„ã€é‹å–¶è€…ã®é€£çµ¡å…ˆç­‰ãŒè¨˜è¼‰ã•れãŸWebページã®URLを指定ã—ã¾ã™ã€‚
+ */
+ "inquiryUrlDescription": string;
};
"_accountMigration": {
/**
@@ -8113,10 +8177,6 @@ export interface Locale extends ILocale {
*/
"read:admin:show-user": string;
/**
- * ユーザーã®ãƒ—ãƒ©ã‚¤ãƒ™ãƒ¼ãƒˆãªæƒ…報を見る
- */
- "read:admin:show-users": string;
- /**
* ユーザーをå‡çµã™ã‚‹
*/
"write:admin:suspend-user": string;
@@ -9353,6 +9413,10 @@ export interface Locale extends ILocale {
*/
"addColumn": string;
/**
+ * æ–°ç€ãƒŽãƒ¼ãƒˆé€šçŸ¥ã®è¨­å®š
+ */
+ "newNoteNotificationSettings": string;
+ /**
* カラムã®è¨­å®š
*/
"configureColumn": string;
diff --git a/locales/it-IT.yml b/locales/it-IT.yml
index 3868eea9e3..2b04c5abfb 100644
--- a/locales/it-IT.yml
+++ b/locales/it-IT.yml
@@ -86,7 +86,7 @@ note: "Nota"
notes: "Note"
following: "Follow"
followers: "Follower"
-followsYou: "Segue"
+followsYou: "Follower"
createList: "Aggiungi una nuova lista"
manageLists: "Gestisci liste"
error: "Errore"
@@ -135,12 +135,12 @@ deleteFile: "File da Drive eliminato"
markAsSensitive: "Segna come esplicito"
unmarkAsSensitive: "Non segnare come esplicito "
enterFileName: "Nome del file"
-mute: "Silenzia"
+mute: "Silenziare"
unmute: "Riattiva l'audio"
-renoteMute: "Silenzia le Rinota"
+renoteMute: "Silenziare le Rinota"
renoteUnmute: "Non silenziare le Rinota"
-block: "Blocca"
-unblock: "Sblocca"
+block: "Bloccare"
+unblock: "Sbloccare"
suspend: "Sospensione"
unsuspend: "Revoca la sospensione"
blockConfirm: "Vuoi davvero bloccare il profilo?"
@@ -201,8 +201,8 @@ charts: "Grafici"
perHour: "orario"
perDay: "giornaliero"
stopActivityDelivery: "Interrompi la distribuzione di attività"
-blockThisInstance: "Blocca questa istanza"
-silenceThisInstance: "Silenzia l'istanza"
+blockThisInstance: "Bloccare l'istanza"
+silenceThisInstance: "Silenziare l'istanza"
operations: "Operazioni"
software: "Software"
version: "Versione"
@@ -224,7 +224,7 @@ blockedInstances: "Istanze bloccate"
blockedInstancesDescription: "Elenca le istanze che vuoi bloccare, una per riga. Esse non potranno più interagire con la tua istanza."
silencedInstances: "Istanze silenziate"
silencedInstancesDescription: "Elenca i nomi host delle istanze che vuoi silenziare. Tutti i profili nelle istanze silenziate vengono trattati come tali. Possono solo inviare richieste di follow e menzionare soltanto i profili locali che seguono. Le istanze bloccate non sono interessate."
-muteAndBlock: "Silenziati / Bloccati"
+muteAndBlock: "Silenziare e bloccare"
mutedUsers: "Profili silenziati"
blockedUsers: "Profili bloccati"
noUsers: "Non ci sono profili"
@@ -401,6 +401,7 @@ name: "Nome"
antennaSource: "Fonte dell'antenna"
antennaKeywords: "Parole chiavi da ricevere"
antennaExcludeKeywords: "Parole chiavi da escludere"
+antennaExcludeBots: "Escludere i Bot"
antennaKeywordsDescription: "Sparando con uno spazio indichi la condizione E (and). Separando con un a capo, indichi la condizione O (or)."
notifyAntenna: "Invia notifiche delle nuove note"
withFileAntenna: "Solo note con file in allegato"
@@ -411,7 +412,7 @@ withReplies: "Includere le risposte"
connectedTo: "Connessione ai seguenti profili:"
notesAndReplies: "Note e risposte"
withFiles: "Con allegati"
-silence: "Silenzia"
+silence: "Silenziare"
silenceConfirm: "Vuoi davvero silenziare questo profilo?"
unsilence: "Riattiva"
unsilenceConfirm: "Vuoi davvero riattivare questo profilo?"
@@ -451,7 +452,7 @@ share: "Condividi"
notFound: "Non trovato"
notFoundDescription: "Nessuna pagina corrisponde all'URL indicata."
uploadFolder: "Destinazione caricamento predefinita"
-markAsReadAllNotifications: "Segna tutte le notifiche come lette"
+markAsReadAllNotifications: "Segnare tutte le notifiche come lette"
markAsReadAllUnreadNotes: "Segna tutte le note come lette"
markAsReadAllTalkMessages: "Segna tutte le chat come lette"
help: "Guida"
@@ -495,6 +496,7 @@ emojiStyle: "Stile emoji"
native: "Nativo"
disableDrawer: "Non mostrare il menù sul drawer"
showNoteActionsOnlyHover: "Mostra le azioni delle Note solo al passaggio del mouse"
+showReactionsCount: "Visualizza il numero di reazioni su una nota"
noHistory: "Nessuna cronologia"
signinHistory: "Storico degli accessi al profilo"
enableAdvancedMfm: "Attiva MFM avanzati"
@@ -578,7 +580,7 @@ scratchpadDescription: "Lo Scratchpad offre un ambiente per esperimenti di AiScr
output: "Uscita"
script: "Script"
disablePagesScript: "Disabilita AiScript nelle pagine"
-updateRemoteUser: "Aggiorna le informazioni dal profilo remoto"
+updateRemoteUser: "Aggiorna dati dal profilo remoto"
unsetUserAvatar: "Rimozione foto profilo"
unsetUserAvatarConfirm: "Vuoi davvero rimuovere la foto profilo?"
unsetUserBanner: "Rimuovi intestazione profilo"
@@ -588,7 +590,7 @@ deleteAllFilesConfirm: "Vuoi davvero eliminare tutti i file?"
removeAllFollowing: "Annulla tutti i follow"
removeAllFollowingDescription: "Cancella tutti i follows del server {host}. Per favore, esegui se, ad esempio, l'istanza non esiste più."
userSuspended: "L'utente è in sospensione"
-userSilenced: "Profilo silente."
+userSilenced: "Profilo silenziato"
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"
@@ -658,7 +660,7 @@ wordMute: "Filtri parole"
hardWordMute: "Filtro parole forte"
regexpError: "errore regex"
regexpErrorDescription: "Si è verificato un errore nell'espressione regolare alla riga {line} della parola muta {tab}:"
-instanceMute: "Silenzia l'istanza"
+instanceMute: "Silenziare l'istanza"
userSaysSomething: "{name} ha parlato"
makeActive: "Attiva"
display: "Visualizza"
@@ -683,14 +685,14 @@ fileIdOrUrl: "ID o URL del file"
behavior: "Comportamento"
sample: "Esempio"
abuseReports: "Segnalazioni"
-reportAbuse: "Segnala"
-reportAbuseRenote: "Segnala la Rinota"
-reportAbuseOf: "Segnala {name}"
+reportAbuse: "Segnalare"
+reportAbuseRenote: "Segnalare la Rinota"
+reportAbuseOf: "Segnalare {name}"
fillAbuseReportDescription: "Per favore, spiegaci il motivo della segnalazione. Se riguarda una Nota precisa, indica anche l'indirizzo URL."
abuseReported: "La segnalazione è stata inviata. Grazie."
reporter: "il corrispondente"
-reporteeOrigin: "Origine del segnalato"
-reporterOrigin: "Origine del segnalatore"
+reporteeOrigin: "Segnalazione a"
+reporterOrigin: "Segnalazione da"
forwardReport: "Inoltro di un report a un'istanza remota."
forwardReportIsAnonymous: "L'istanza remota non vedrà le tue informazioni, apparirai come profilo di sistema, anonimo."
send: "Inviare"
@@ -864,7 +866,7 @@ troubleshooting: "Risoluzione problemi"
useBlurEffect: "Utilizza effetto sfocatura"
learnMore: "Più dettagli"
misskeyUpdated: "Misskey è stato aggiornato!"
-whatIsNew: "Visualizza le informazioni sull'aggiornamento"
+whatIsNew: "Informazioni sull'aggiornamento"
translate: "Traduci"
translatedFrom: "Traduzione da {x}"
accountDeletionInProgress: "È in corso l'eliminazione del profilo"
@@ -890,7 +892,7 @@ manageAccounts: "Gestisci i profili"
makeReactionsPublic: "Pubblicare la lista delle reazioni."
makeReactionsPublicDescription: "La lista delle reazioni che avete fatto è a disposizione di tutti."
classic: "Classico"
-muteThread: "Silenzia conversazione"
+muteThread: "Silenziare conversazione"
unmuteThread: "Riattiva la conversazione"
followingVisibility: "Visibilità dei profili seguiti"
followersVisibility: "Visibilità dei profili che ti seguono"
@@ -972,11 +974,11 @@ shuffle: "Casuale"
account: "Account"
move: "Sposta"
pushNotification: "Notifiche Push"
-subscribePushNotification: "Attiva le notifiche push"
-unsubscribePushNotification: "Disattiva le notifiche push"
+subscribePushNotification: "Attivare le notifiche push"
+unsubscribePushNotification: "Disattivare le notifiche push"
pushNotificationAlreadySubscribed: "Le notifiche push sono già attivate"
pushNotificationNotSupported: "Il client o il server non supporta le notifiche push"
-sendPushNotificationReadMessage: "Elimina le notifiche push dopo la relativa lettura"
+sendPushNotificationReadMessage: "Eliminare le notifiche push dopo la relativa lettura"
sendPushNotificationReadMessageCaption: "Se possibile, verrà mostrata brevemente una notifica con il testo \"{emptyPushNotificationMessage}\". Potrebbe influire negativamente sulla durata della batteria."
windowMaximize: "Ingrandisci"
windowMinimize: "Contrai finestra"
@@ -1164,6 +1166,7 @@ showRenotes: "Includi le Rinota"
edited: "Modificato"
notificationRecieveConfig: "Preferenze di notifica"
mutualFollow: "Follow reciproco"
+followingOrFollower: "Following o Follower"
fileAttachedOnly: "Solo con allegati"
showRepliesToOthersInTimeline: "Risposte altrui nella TL"
hideRepliesToOthersInTimeline: "Nascondi Riposte altrui nella TL"
@@ -1214,6 +1217,8 @@ soundWillBePlayed: "Con musica ed effetti sonori"
showReplay: "Vedi i replay"
replay: "Replay"
replaying: "Replay in corso"
+endReplay: "Termina replay"
+copyReplayData: "Copia replay"
ranking: "Classifica"
lastNDays: "Ultimi {n} giorni"
backToTitle: "Torna al titolo"
@@ -1221,9 +1226,32 @@ hemisphere: "Geolocalizzazione"
withSensitive: "Mostra le Note con allegati espliciti"
userSaysSomethingSensitive: "Note da {name} con allegati espliciti"
enableHorizontalSwipe: "Trascina per invertire i tab"
+loading: "Caricamento"
surrender: "Annulla"
+gameRetry: "Riprova"
+notUsePleaseLeaveBlank: "Lasciare vuoto, se non in uso"
+useTotp: "Usare il codice OTP"
+useBackupCode: "Usare il codice usa-e-getta"
+launchApp: "Esegui l'App"
+useNativeUIForVideoAudioPlayer: "Riprodurre audio/video usando le funzionalità del browser"
+keepOriginalFilename: "Mantieni il nome file originale"
+keepOriginalFilenameDescription: "Disattivandola, i file verranno caricati usando nomi casuali."
+noDescription: "Manca la descrizione"
+_delivery:
+ stop: "Sospensione"
+ _type:
+ none: "Pubblicazione"
_bubbleGame:
howToPlay: "Come giocare"
+ hold: "Tieni"
+ _score:
+ score: "Punteggio"
+ scoreYen: "Capitale"
+ highScore: "Punteggio migliore"
+ maxChain: "Miglior combo"
+ yen: "{yen}ï¿¥"
+ estimatedQty: "{qty} punti"
+ scoreSweets: "Onigiri {onigiriQtyWithUnit}"
_howToPlay:
section1: "Scegli la posizione e rilascia l'oggetto nel contenitore."
section2: "Se due oggetti dello stesso tipo si toccano, si trasformano in un oggetto diverso, aumentando il punteggio."
@@ -1239,7 +1267,7 @@ _announcement:
readConfirmText: "Hai già letto \"{title}Ë?"
shouldNotBeUsedToPresentPermanentInfo: "Ti consigliamo di utilizzare gli annunci per pubblicare informazioni tempestive e limitate nel tempo, anziché informazioni importanti a lungo andare nel tempo, poiché potrebbero risultare difficili da ritrovare e peggiorare la fruibilità del servizio, specialmente alle nuove persone iscritte."
dialogAnnouncementUxWarn: "Ti consigliamo di usarli con cautela, poiché è molto probabile che avere più di un annuncio in stile \"finestra di dialogo\" peggiori sensibilmente la fruibilità del servizio, specialmente alle nuove persone iscritte."
- silence: "Silenzia gli annunci"
+ silence: "Silenziare gli annunci"
silenceDescription: "Se attivi questa opzione, non riceverai notifiche sugli annunci, evitando di contrassegnarle come già lette."
_initialAccountSetting:
accountCreated: "Il tuo profilo è stato creato!"
@@ -1278,14 +1306,14 @@ _initialTutorial:
letsTryReacting: "Puoi aggiungere una Reazione cliccando il bottone \"{reaction}\" della relativa Nota. Prova ad aggiungerne una a questa Nota di esempio!"
reactToContinue: "Aggiungere la Reazione ti consentirà di procedere col tutorial."
reactNotification: "Quando qualcuno reagisce alle tue Note, ricevi una notifica in tempo reale."
- reactDone: "Puoi annullare la tua Reazione premendo il bottone \"{undo}\""
+ reactDone: "Annulla la tua Reazione premendo il bottone \"{undo}\""
_timeline:
title: "Come funziona la Timeline"
description1: "Misskey fornisce alcune Timeline (sequenze cronologiche di Note). Una di queste potrebbe essere stata disattivata dagli amministratori."
- home: "Puoi vedere le Note provenienti dai profili che segui (follow)."
- local: "Puoi vedere tutte le Note pubblicate dai profili di questa istanza."
- social: "Puoi vedere sia le Note della Timeline Home che quelle della Timeline Locale, insieme!"
- global: "Puoi vedere le Note da pubblicate da tutte le altre istanze federate con la nostra."
+ home: "le Note provenienti dai profili che segui (follow)."
+ local: "tutte le Note pubblicate dai profili di questa istanza."
+ social: "sia le Note della Timeline Home che quelle della Timeline Locale, insieme!"
+ global: "le Note da pubblicate da tutte le altre istanze federate con la nostra."
description2: "Nella parte superiore dello schermo, puoi scegliere una Timeline o l'altra in qualsiasi momento."
description3: "Ci sono anche sequenze temporali di elenchi, sequenze temporali di canali, ecc. Per ulteriori dettagli, consultare il {link}.\nPuoi vedere anche Timeline delle liste di profili (se ne hai create), canali, ecc... Per i dettagli, visita {link}."
_postNote:
@@ -1309,13 +1337,13 @@ _initialTutorial:
useCases: "Utilizzalo per chiarire il contenuto della Nota, prima che sia letta. Come richiesto dal regolamento del server o per autoregolamentare spoiler e testi troppo espliciti."
_howToMakeAttachmentsSensitive:
title: "Come indicare che gli allegati sono espliciti?"
- description: "Contrassegnare gli allegati come espliciti, va fatto quando è richiesto dal regolamento del server o quando gli allegati non devono essere immediatamente visibili."
+ description: "Si fa quando è richiesto dal regolamento del server o quando non devono essere visibili immediatamente."
tryThisFile: "Prova a rendere esplicite le immagini allegate a questo modulo!"
_exampleNote:
- note: "Ho fatto un errore aprendo il coperchio del natto... (fagioli di soia fermentati, particolarmente appiccicosi)"
- method: "Per indicare che un allegato è esplicito, tocca il file per aprirne il menu e scegliere la voce \"Segna come esplicito\"."
- sensitiveSucceeded: "Quando alleghi file, assicurati di indicare se è materiale esplicito, in modo appropriato, in base al regolamento del tuo server."
- doItToContinue: "Impostando l'immagine come esplicita, potrai procedere col tutorial."
+ note: "AAA! Ho rotto il coperchio del natto... (fagioli di soia fermentati)"
+ method: "Tocca il file, si aprirà il menu, scegli la voce \"Segna come esplicito\""
+ sensitiveSucceeded: "Quando alleghi file, assicurati di indicare se è materiale esplicito in modo appropriato, decidi in base al regolamento dell'istanza."
+ doItToContinue: "Imposta l'immagine come esplicita per procedere col tutorial."
_done:
title: "Il tutorial è finito! 🎉"
description: "Queste sono solamente alcune delle funzionalità principali di Misskey. Per ulteriori informazioni, {link}."
@@ -1341,7 +1369,7 @@ _serverSettings:
_accountMigration:
moveFrom: "Migra un altro profilo dentro a questo"
moveFromSub: "Crea un alias verso un altro profilo remoto"
- moveFromLabel: "Profilo da cui migrare:"
+ moveFromLabel: "Profilo da cui migrare #{n}"
moveFromDescription: "Se desideri spostare i profili follower da un altro profilo a questo, devi prima creare un alias qui. Assicurati averlo creato PRIMA di eseguire l'attività! Inserisci l'indirizzo del profilo mittente in questo modo: @persona@istanza.it"
moveTo: "Migrare questo profilo verso un un altro"
moveToLabel: "Profilo verso cui migrare"
@@ -1645,6 +1673,7 @@ _role:
gtlAvailable: "Disponibilità della Timeline Federata"
ltlAvailable: "Disponibilità della Timeline Locale"
canPublicNote: "Scrivere Note con Visibilità Pubblica"
+ mentionMax: "Numero massimo di menzioni in una nota"
canInvite: "Generare codici di invito all'istanza"
inviteLimit: "Limite di codici invito"
inviteLimitCycle: "Intervallo di emissione del codice di invito"
@@ -1668,6 +1697,7 @@ _role:
canUseTranslator: "Tradurre le Note"
avatarDecorationLimit: "Numero massimo di decorazioni foto profilo installabili"
_condition:
+ roleAssignedTo: "Assegnato a ruoli manualmente"
isLocal: "Profilo locale"
isRemote: "Profilo remoto"
createdLessThan: "Profilo creato da meno di N"
@@ -1739,6 +1769,7 @@ _plugin:
installWarn: "Si prega di installare soltanto estensioni che provengono da fonti affidabili."
manage: "Gestisci estensioni"
viewSource: "Visualizza sorgente"
+ viewLog: "Mostra log"
_preferencesBackups:
list: "Elenco di impostazioni salvate in precedenza"
saveNew: "Nuovo salvataggio"
@@ -1814,7 +1845,7 @@ _instanceMute:
instanceMuteDescription: "Disattiva tutte le note, le note di rinvio (condivisione) dell'istanza configurata, comprese le risposte agli utenti dell'istanza."
instanceMuteDescription2: "Impostazione separata da una nuova riga"
title: "Nasconde le note dell'istanza configurata."
- heading: "Istanze da silenziare."
+ heading: "Istanze da silenziare"
_theme:
explore: "Esplora temi"
install: "Installa un tema"
@@ -1929,7 +1960,6 @@ _2fa:
registerTOTP: "Registra una App di autenticazione a due fattori (2FA/MFA)"
step1: "Innanzitutto, installa sul dispositivo un'App di autenticazione come {a} o {b}."
step2: "Quindi, tramite la App installata, scansiona questo codice QR."
- step2Click: "Cliccando sul codice QR, puoi registrarlo con l'app di autenticazione o il portachiavi installato sul tuo dispositivo."
step2Uri: "Inserisci il seguente URL se desideri utilizzare una App per PC"
step3Title: "Inserisci il codice di verifica"
step3: "Inserite il token visualizzato nell'app e il gioco è fatto."
@@ -1953,6 +1983,7 @@ _2fa:
backupCodesDescription: "Puoi usare questi codici usa-e-getta per ottenere l'accesso al tuo profilo in caso sia impossibile usare l'App col codice OTP. Salvali in un posto sicuro."
backupCodeUsedWarning: "È stato usato un codice usa-e-getta. Per favore, riconfigura l'autenticazione a due fattori il prima possibile, nel caso la configurazione precedente abbia smesso di funzionare."
backupCodesExhaustedWarning: "Hai esaurito i codici usa-e-getta. Se l'App che genera il codice OTP non è più disponibile, non potrai più accedere al tuo profilo. Ripeti la configurazione per l'autenticazione a due fattori."
+ moreDetailedGuideHere: "Informazioni dettagliate sull'autenticazione multi fattore (2FA/MFA)"
_permissions:
"read:account": "Visualizza le informazioni sul profilo"
"write:account": "Modifica le informazioni sul profilo"
@@ -1969,8 +2000,8 @@ _permissions:
"read:mutes": "Vedi i profili silenziati"
"write:mutes": "Gestisci i profili silenziati"
"write:notes": "Creare / Eliminare note"
- "read:notifications": "Visualizza notifiche"
- "write:notifications": "Gerisci notifiche"
+ "read:notifications": "Visualizzare notifiche"
+ "write:notifications": "Gestire notifiche"
"read:reactions": "Vedi reazioni"
"write:reactions": "Gerisci reazioni"
"write:votes": "Votare"
@@ -2003,7 +2034,6 @@ _permissions:
"read:admin:server-info": "Vedere le informazioni sul server"
"read:admin:show-moderation-log": "Vedere lo storico di moderazione"
"read:admin:show-user": "Vedere le informazioni private degli account utente"
- "read:admin:show-users": "Vedere le informazioni private degli account utente"
"write:admin:suspend-user": "Sospendere i profili"
"write:admin:unset-user-avatar": "Rimuovere la foto profilo dai profili"
"write:admin:unset-user-banner": "Rimuovere l'immagine testata dai profili"
@@ -2214,6 +2244,7 @@ _play:
title: "Titolo"
script: "Script"
summary: "Descrizione"
+ visibilityDescription: "Impostarlo su privato significa che non verrà visualizzato sul tuo profilo, ma chiunque ha l'URL potrà comunque accedervi."
_pages:
newPage: "Crea pagina"
editPage: "Modifica pagina"
@@ -2258,6 +2289,8 @@ _pages:
section: "Sezione"
image: "Immagini"
button: "Pulsante"
+ dynamic: "Riquadri dinamici"
+ dynamicDescription: "Questo riquadro è obsoleto. Utilizza {play} da ora in poi."
note: "Nota integrata"
_note:
id: "ID nota"
@@ -2282,13 +2315,15 @@ _notification:
roleAssigned: "Ruolo assegnato"
emptyPushNotificationMessage: "Le notifiche push sono state aggiornate."
achievementEarned: "Obiettivo raggiunto"
- testNotification: "Prova la notifica"
- checkNotificationBehavior: "Prova il comportamento della notifica"
+ testNotification: "Provare la notifica"
+ checkNotificationBehavior: "Provare il comportamento della notifica"
sendTestNotification: "Spedisci una notifica di prova"
notificationWillBeDisplayedLikeThis: "La notifica apparirà così"
reactedBySomeUsers: "{n} reazioni"
+ likedBySomeUsers: "{n} apprezzamenti"
renotedBySomeUsers: "{n} Rinota"
- followedBySomeUsers: "{n} nuovi follower"
+ followedBySomeUsers: "{n} follower"
+ flushNotification: "Azzera le notifiche"
_types:
all: "Tutto"
note: "Nuove Note"
@@ -2340,8 +2375,8 @@ _deck:
direct: "Note Dirette"
roleTimeline: "Timeline Ruolo"
_dialog:
- charactersExceeded: "Hai superato il limite di {max} caratteri! ({corrente})"
- charactersBelow: "Sei al di sotto del minimo di {min} caratteri! ({corrente})"
+ charactersExceeded: "Hai superato il limite di {max} caratteri! ({current})"
+ charactersBelow: "Sei al di sotto del minimo di {min} caratteri! ({current})"
_disabledTimeline:
title: "Timeline disabilitata"
description: "Il ruolo in cui sei non ti permette di leggere questa timeline"
@@ -2386,6 +2421,7 @@ _moderationLogTypes:
resetPassword: "Password azzerata"
suspendRemoteInstance: "Istanza remota sospesa"
unsuspendRemoteInstance: "Istanza remota riattivata"
+ updateRemoteInstanceNote: "Aggiornamento del promemoria di moderazione per il server remoto"
markSensitiveDriveFile: "File nel Drive segnato come esplicito"
unmarkSensitiveDriveFile: "File nel Drive segnato come non esplicito"
resolveAbuseReport: "Segnalazione risolta"
@@ -2506,6 +2542,24 @@ _reversi:
opponentHasSettingsChanged: "L'avversario ha cambiato configurazione"
allowIrregularRules: "Regole inconsuete (completamente libere)"
disallowIrregularRules: "Impedire le regole inconsuete"
+ showBoardLabels: "Mostra le coordinate del gioco"
+ useAvatarAsStone: "Immagini profilo come pedine"
_offlineScreen:
title: "Scollegato. Impossibile connettersi al server"
header: "Impossibile connettersi al server"
+_urlPreviewSetting:
+ title: "Impostazioni per l'anteprima delle URL"
+ enable: "Attiva l'anteprima delle URL"
+ timeout: "Timeout dell'anteprima in millisecondi"
+ timeoutDescription: "Impegna al massimo il tempo indicato, altrimenti ignora l'anteprima"
+ maximumContentLength: "Grandezza del contenuto (Content-Length in byte)"
+ maximumContentLengthDescription: "Se la grandezza supera il valore, l'anteprima verrà ignorata."
+ requireContentLength: "Genenerare l'anteprima solo quando è definito Content-Length"
+ requireContentLengthDescription: "In assenza di questo parametro dal server remoto, l'anteprima verrà ignorata."
+ userAgent: "User-Agent"
+ userAgentDescription: "Definire con quale User-Agent si intende identificarsi durante l'acquisizione di un'anteprima. Se è vuoto, useremo il valore predefinito."
+ summaryProxy: "Endpoint proxy che genera l'anteprima"
+_mediaControls:
+ pip: "Sovraimpressione"
+ playbackRate: "Velocità di riproduzione"
+ loop: "Ripetizione infinita"
diff --git a/locales/ja-JP.yml b/locales/ja-JP.yml
index b37ba2ec51..daf50b8853 100644
--- a/locales/ja-JP.yml
+++ b/locales/ja-JP.yml
@@ -110,6 +110,7 @@ enterEmoji: "絵文字を入力"
renote: "ブースト"
unrenote: "ブースト解除"
renoted: "ブーストã—ã¾ã—ãŸã€‚"
+renotedToX: "{name} ã«ãƒ–ーストã—ã¾ã—ãŸã€‚"
quoted: "引用。"
rmboost: "ブースト解除ã—ã¾ã—ãŸã€‚"
cantRenote: "ã“ã®æŠ•ç¨¿ã¯ãƒ–ーストã§ãã¾ã›ã‚“。"
@@ -117,6 +118,8 @@ cantReRenote: "ブーストをブーストã™ã‚‹ã“ã¨ã¯ã§ãã¾ã›ã‚“。"
quote: "引用"
inChannelRenote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…ブースト"
inChannelQuote: "ãƒãƒ£ãƒ³ãƒãƒ«å†…引用"
+renoteToChannel: "ãƒãƒ£ãƒ³ãƒãƒ«ã«ãƒªãƒŽãƒ¼ãƒˆ"
+renoteToOtherChannel: "ä»–ã®ãƒãƒ£ãƒ³ãƒãƒ«ã«ãƒªãƒŽãƒ¼ãƒˆ"
pinnedNote: "ピン留ã‚ã•れãŸãƒŽãƒ¼ãƒˆ"
pinned: "ピン留ã‚"
you: "ã‚ãªãŸ"
@@ -232,7 +235,7 @@ clearCachedFilesConfirm: "キャッシュã•れãŸãƒªãƒ¢ãƒ¼ãƒˆãƒ•ァイルをã™
blockedInstances: "ブロックã—ãŸã‚µãƒ¼ãƒãƒ¼"
blockedInstancesDescription: "ブロックã—ãŸã„サーãƒãƒ¼ã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨­å®šã—ã¾ã™ã€‚ブロックã•れãŸã‚µãƒ¼ãƒãƒ¼ã¯ã€ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã¨ã‚„りå–りã§ããªããªã‚Šã¾ã™ã€‚"
silencedInstances: "サイレンスã—ãŸã‚µãƒ¼ãƒãƒ¼"
-silencedInstancesDescription: "サイレンスã—ãŸã„インスタンスã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨­å®šã—ã¾ã™ã€‚サイレンスã•れãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«æ‰€å±žã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã™ã¹ã¦ã€Œã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã€ã¨ã—ã¦æ‰±ã‚れã€ãƒ•ォローãŒã™ã¹ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ãªã‚Šã€ãƒ•ォロワーã§ãªã„ローカルアカウントã«ã¯ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³ã§ããªããªã‚Šã¾ã™ã€‚ブロックã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«ã¯å½±éŸ¿ã—ã¾ã›ã‚“。"
+silencedInstancesDescription: "サイレンスã—ãŸã„サーãƒãƒ¼ã®ãƒ›ã‚¹ãƒˆã‚’改行ã§åŒºåˆ‡ã£ã¦è¨­å®šã—ã¾ã™ã€‚サイレンスã•れãŸã‚µãƒ¼ãƒãƒ¼ã«æ‰€å±žã™ã‚‹ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã¯ã™ã¹ã¦ã€Œã‚µã‚¤ãƒ¬ãƒ³ã‚¹ã€ã¨ã—ã¦æ‰±ã‚れã€ãƒ•ォローãŒã™ã¹ã¦ãƒªã‚¯ã‚¨ã‚¹ãƒˆã«ãªã‚Šã¾ã™ã€‚ブロックã—ãŸã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã«ã¯å½±éŸ¿ã—ã¾ã›ã‚“。"
muteAndBlock: "ミュートã¨ãƒ–ロック"
mutedUsers: "ミュートã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼"
blockedUsers: "ブロックã—ãŸãƒ¦ãƒ¼ã‚¶ãƒ¼"
@@ -323,6 +326,7 @@ selectFile: "ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠž"
selectFiles: "ãƒ•ã‚¡ã‚¤ãƒ«ã‚’é¸æŠž"
selectFolder: "ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã‚’é¸æŠž"
selectFolders: "ãƒ•ã‚©ãƒ«ãƒ€ãƒ¼ã‚’é¸æŠž"
+fileNotSelected: "ファイルãŒé¸æŠžã•れã¦ã„ã¾ã›ã‚“"
renameFile: "ファイルåを変更"
folderName: "フォルダーå"
createFolder: "フォルダーを作æˆ"
@@ -481,6 +485,7 @@ expandAllCws: "ã™ã¹ã¦ã®è¿”ä¿¡ã®å†…容を表示ã™ã‚‹"
collapseAllCws: "ã™ã¹ã¦ã®è¿”ä¿¡ã®å†…容を隠ã™"
quoteAttached: "引用付ã"
quoteQuestion: "引用ã¨ã—ã¦æ·»ä»˜ã—ã¾ã™ã‹ï¼Ÿ"
+attachAsFileQuestion: "クリップボードã®ãƒ†ã‚­ã‚¹ãƒˆãŒé•·ã„ã§ã™ã€‚テキストファイルã¨ã—ã¦æ·»ä»˜ã—ã¾ã™ã‹ï¼Ÿ"
noMessagesYet: "ã¾ã ãƒãƒ£ãƒƒãƒˆã¯ã‚りã¾ã›ã‚“"
newMessageExists: "æ–°ã—ã„メッセージãŒã‚りã¾ã™"
onlyOneFileCanBeAttached: "ãƒ¡ãƒƒã‚»ãƒ¼ã‚¸ã«æ·»ä»˜ã§ãるファイルã¯ã²ã¨ã¤ã§ã™"
@@ -1057,7 +1062,8 @@ thisPostMayBeAnnoyingIgnore: "ã“ã®ã¾ã¾æŠ•稿"
thisPostIsMissingAltTextCancel: "ã‚„ã‚ã‚‹"
thisPostIsMissingAltTextIgnore: "ã“ã®ã¾ã¾æŠ•稿"
thisPostIsMissingAltText: "ã“ã®æŠ•ç¨¿ã«æ·»ä»˜ã•れãŸãƒ•ァイル㮠1 ã¤ã«ä»£æ›¿ãƒ†ã‚­ã‚¹ãƒˆãŒã‚りã¾ã›ã‚“。ã™ã¹ã¦ã®æ·»ä»˜ãƒ•ァイルã«ä»£æ›¿ãƒ†ã‚­ã‚¹ãƒˆãŒå«ã¾ã‚Œã¦ã„ã‚‹ã“ã¨ã‚’確èªã—ã¦ãã ã•ã„。"
-collapseRenotes: "見ãŸã“ã¨ã®ã‚るブーストをçœç•¥ã—ã¦è¡¨ç¤º"
+collapseRenotes: "ブーストã®ã‚¹ãƒžãƒ¼ãƒˆçœç•¥"
+collapseRenotesDescription: "リアクションやブーストをã—ãŸã“ã¨ãŒã‚るノートをãŸãŸã‚“ã§è¡¨ç¤ºã—ã¾ã™ã€‚"
collapseFiles: "ファイルを折りãŸãŸã‚€"
autoloadConversation: "返信ã«ä¼šè©±ã‚’読ã¿è¾¼ã‚€"
internalServerError: "サーãƒãƒ¼å†…部エラー"
@@ -1285,6 +1291,16 @@ noDescription: "説明文ã¯ã‚りã¾ã›ã‚“"
alwaysConfirmFollow: "フォローã®éš›å¸¸ã«ç¢ºèªã™ã‚‹"
inquiry: "ãŠå•ã„åˆã‚ã›"
+_delivery:
+ status: "é…信状態"
+ stop: "é…ä¿¡åœæ­¢"
+ resume: "é…ä¿¡å†é–‹"
+ _type:
+ none: "é…信中"
+ manuallySuspended: "æ‰‹å‹•åœæ­¢ä¸­"
+ goneSuspended: "サーãƒãƒ¼å‰Šé™¤ã®ãŸã‚åœæ­¢ä¸­"
+ autoSuspendedForNotResponding: "サーãƒãƒ¼å¿œç­”ãªã—ã®ãŸã‚åœæ­¢ä¸­"
+
_bubbleGame:
howToPlay: "éŠã³æ–¹"
hold: "ホールド"
@@ -1416,6 +1432,8 @@ _serverSettings:
fanoutTimelineDescription: "有効ã«ã™ã‚‹ã¨ã€å„種タイムラインをå–å¾—ã™ã‚‹éš›ã®ãƒ‘フォーマンスãŒå¤§å¹…ã«å‘上ã—ã€ãƒ‡ãƒ¼ã‚¿ãƒ™ãƒ¼ã‚¹ã¸ã®è² è·ã‚’軽減ã™ã‚‹ã“ã¨ãŒå¯èƒ½ã§ã™ã€‚ãŸã ã—ã€Redisã®ãƒ¡ãƒ¢ãƒªä½¿ç”¨é‡ã¯å¢—加ã—ã¾ã™ã€‚サーãƒãƒ¼ã®ãƒ¡ãƒ¢ãƒªå®¹é‡ãŒå°‘ãªã„å ´åˆã€ã¾ãŸã¯å‹•作ãŒä¸å®‰å®šãªå ´åˆã¯ç„¡åйã«ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ã€‚"
fanoutTimelineDbFallback: "データベースã¸ã®ãƒ•ォールãƒãƒƒã‚¯"
fanoutTimelineDbFallbackDescription: "有効ã«ã™ã‚‹ã¨ã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ãŒã‚­ãƒ£ãƒƒã‚·ãƒ¥ã•れã¦ã„ãªã„å ´åˆã«DBã¸è¿½åŠ ã§å•ã„åˆã‚ã›ã‚’行ã†ãƒ•ォールãƒãƒƒã‚¯å‡¦ç†ã‚’行ã„ã¾ã™ã€‚無効ã«ã™ã‚‹ã¨ã€ãƒ•ォールãƒãƒƒã‚¯å‡¦ç†ã‚’行ã‚ãªã„ã“ã¨ã§ã•らã«ã‚µãƒ¼ãƒãƒ¼ã®è² è·ã‚’軽減ã™ã‚‹ã“ã¨ãŒã§ãã¾ã™ãŒã€ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ãŒå–å¾—ã§ãる範囲ã«åˆ¶é™ãŒç”Ÿã˜ã¾ã™ã€‚"
+ inquiryUrl: "å•ã„åˆã‚ã›å…ˆURL"
+ inquiryUrlDescription: "サーãƒãƒ¼é‹å–¶è€…ã¸ã®ãŠå•ã„åˆã‚ã›ãƒ•ォームã®URLã‚„ã€é‹å–¶è€…ã®é€£çµ¡å…ˆç­‰ãŒè¨˜è¼‰ã•れãŸWebページã®URLを指定ã—ã¾ã™ã€‚"
_accountMigration:
moveFrom: "別ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã‹ã‚‰ã“ã®ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ç§»è¡Œ"
@@ -2127,7 +2145,6 @@ _permissions:
"read:admin:server-info": "サーãƒãƒ¼ã®æƒ…報を見る"
"read:admin:show-moderation-log": "モデレーションログを見る"
"read:admin:show-user": "ユーザーã®ãƒ—ãƒ©ã‚¤ãƒ™ãƒ¼ãƒˆãªæƒ…報を見る"
- "read:admin:show-users": "ユーザーã®ãƒ—ãƒ©ã‚¤ãƒ™ãƒ¼ãƒˆãªæƒ…報を見る"
"write:admin:suspend-user": "ユーザーをå‡çµã™ã‚‹"
"write:admin:unset-user-avatar": "ユーザーã®ã‚¢ãƒã‚¿ãƒ¼ã‚’削除ã™ã‚‹"
"write:admin:unset-user-banner": "ユーザーã®ãƒãƒ¼ãƒŠãƒ¼ã‚’削除ã™ã‚‹"
@@ -2470,6 +2487,7 @@ _deck:
alwaysShowMainColumn: "常ã«ãƒ¡ã‚¤ãƒ³ã‚«ãƒ©ãƒ ã‚’表示"
columnAlign: "カラムã®å¯„ã›"
addColumn: "カラムを追加"
+ newNoteNotificationSettings: "æ–°ç€ãƒŽãƒ¼ãƒˆé€šçŸ¥ã®è¨­å®š"
configureColumn: "カラムã®è¨­å®š"
swapLeft: "å·¦ã«ç§»å‹•"
swapRight: "å³ã«ç§»å‹•"
diff --git a/locales/ja-KS.yml b/locales/ja-KS.yml
index edae188bb6..641425aa17 100644
--- a/locales/ja-KS.yml
+++ b/locales/ja-KS.yml
@@ -402,6 +402,7 @@ name: "åå‰"
antennaSource: "å—信ソース(ã“ã®ã‚½ãƒ¼ã‚¹ã¯é£Ÿã‚れã¸ã‚“)"
antennaKeywords: "å—信キーワード"
antennaExcludeKeywords: "除外キーワード"
+antennaExcludeBots: "Botアカウントを除外"
antennaKeywordsDescription: "スペースã§åŒºåˆ‡ã£ãŸã‚‹ã¨AND指定ã§ã€æ”¹è¡Œã§åŒºåˆ‡ã£ãŸã‚‹ã¨OR指定や"
notifyAntenna: "æ–°ã—ã„ノートを通知ã™ã‚“ã§"
withFileAntenna: "ãªã‚“ã‹æ·»ä»˜ã•れãŸãƒŽãƒ¼ãƒˆã ã‘"
@@ -496,6 +497,7 @@ emojiStyle: "絵文字ã®ã‚¹ã‚¿ã‚¤ãƒ«"
native: "ãƒã‚¤ãƒ†ã‚£ãƒ–"
disableDrawer: "メニューをドロワーã§è¡¨ç¤ºã›ãˆã¸ã‚“"
showNoteActionsOnlyHover: "ãƒŽãƒ¼ãƒˆã®æ“作部をホãƒãƒ¼æ™‚ã®ã¿è¡¨ç¤ºã™ã‚‹ã§"
+showReactionsCount: "ノートã®ãƒªã‚¢ã‚¯ã‚·ãƒ§ãƒ³æ•°ã‚’表示ã™ã‚‹"
noHistory: "履歴ã¯ãªã„ã‚。"
signinHistory: "ログイン履歴"
enableAdvancedMfm: "ã‚„ã‚„ã“ã—ã„MFMã‚‚ã‚りã«ã™ã‚‹"
@@ -1044,6 +1046,8 @@ resetPasswordConfirm: "パスワード作り直ã™ã‚“ã§ãˆãˆãªï¼Ÿ"
sensitiveWords: "ã‘ã£ãŸã„ãªå˜èªž"
sensitiveWordsDescription: "設定ã—ãŸå˜èªžãŒå…¥ã£ã¨ã‚‹ãƒŽãƒ¼ãƒˆã®å…¬é–‹ç¯„囲をホームã«ã—ãŸã‚‹ã‚。改行ã§åŒºåˆ‡ã£ãŸã‚‰è¤‡æ•°è¨­å®šã§ãã‚‹ã§ã€‚"
sensitiveWordsDescription2: "スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã€ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’スラッシュã§å›²ã‚“ã ã‚‰æ­£è¦è¡¨ç¾ã‚„。"
+prohibitedWords: "ç¦æ­¢ãƒ¯ãƒ¼ãƒ‰"
+prohibitedWordsDescription: "設定ã—ãŸè¨€è‘‰ãŒå«ã¾ã‚Œã‚‹ãƒŽãƒ¼ãƒˆã‚’投稿ã—よã†ã¨ã—ãŸã‚‰ã€ã‚¨ãƒ©ãƒ¼ãŒå‡ºã‚‹ã‚ˆã†ã«ã™ã‚‹ã§ã€‚改行ã§åŒºåˆ‡ã£ã¦è¤‡æ•°è¨­å®šã§ãã‚‹ã§ã€‚"
prohibitedWordsDescription2: "スペースã§åŒºåˆ‡ã‚‹ã¨AND指定ã€ã‚­ãƒ¼ãƒ¯ãƒ¼ãƒ‰ã‚’スラッシュã§å›²ã‚“ã ã‚‰æ­£è¦è¡¨ç¾ã‚„。"
hiddenTags: "見ãˆã¦ã¸ã‚“ãƒãƒƒã‚·ãƒ¥ã‚¿ã‚°"
hiddenTagsDescription: "設定ã—ãŸã‚¿ã‚°ã‚’最近æµè¡Œã‚Šã®ã¨ã“ã«è¦‹ãˆã‚“よã†ã«ã™ã‚“ã§ã€‚複数設定ã™ã‚‹ã¨ãã¯æ”¹è¡Œã§åŒºåˆ‡ã£ã¦ãªã€‚"
@@ -1160,6 +1164,7 @@ showRenotes: "ブースト出ã™"
edited: "ã„ã˜ã£ãŸã‚„ã¤"
notificationRecieveConfig: "通知もらã†ã‹ã®è¨­å®š"
mutualFollow: "ãŠäº’ã„フォローã—ã¦ã‚“ã§"
+followingOrFollower: "フォロー中ã¾ãŸã¯ãƒ•ォロワー"
fileAttachedOnly: "ファイルã®ã£ã‘ã¦ã‚ã‚‹ã‚„ã¤ã ã‘"
showRepliesToOthersInTimeline: "タイムラインã«ä»–ã®äººã¸ã®è¿”ä¿¡ã¨ã‹ã‚‚入れるã§"
hideRepliesToOthersInTimeline: "タイムラインã«ä»–ã®äººã¸ã®è¿”ä¿¡ã¨ã‹ã¯å…¥ã‚Œã¸ã‚“"
@@ -1169,6 +1174,12 @@ confirmShowRepliesAll: "ã“れã¯å…ƒã«æˆ»ã›ã¸ã‚“ã‹ã‚‰æ…Žé‡ã«æ±ºã‚ã¦ã‚„ã€
confirmHideRepliesAll: "ã“れã¯å…ƒã«æˆ»ã›ã¸ã‚“ã‹ã‚‰æ…Žé‡ã«æ±ºã‚ã¦ã‚„。本当ã«ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³ã«ä»Šãƒ•ォローã—ã¨ã‚‹å…¨å“¡ã®è¿”信を入れã¸ã‚“ã®ã‹ï¼Ÿ"
externalServices: "ä»–ã®ã‚µã‚¤ãƒˆã®ã‚µãƒ¼ãƒ“ス"
sourceCode: "ソースコード"
+sourceCodeIsNotYetProvided: "ソースコードã¯ã¾ã æä¾›ã•れã¦ã¸ã‚“ã§ã€‚å•題ã®ä¿®æ­£ã«ã¤ã„ã¦ç®¡ç†è€…ã«å•ã„åˆã‚ã›ã¦ã¿ã€‚"
+repositoryUrl: "リãƒã‚¸ãƒˆãƒªURL"
+repositoryUrlDescription: "ソースコードãŒå…¬é–‹ã•れã¦ã„るリãƒã‚¸ãƒˆãƒªãŒã‚ã‚‹å ´åˆã€ãã®URLを記入ã™ã‚‹ã§ã€‚Misskeyã‚’ãã®ã¾ã‚“ã¾ï¼ˆã‚½ãƒ¼ã‚¹ã‚³ãƒ¼ãƒ‰ã«ã„ã‹ãªã‚‹å¤‰æ›´ã‚‚加ãˆãšã«ï¼‰ä½¿ã£ã¨ã‚‹å ´åˆã¯ https://github.com/misskey-dev/misskey ã¨è¨˜å…¥ã™ã‚‹ã§ã€‚"
+repositoryUrlOrTarballRequired: "リãƒã‚¸ãƒˆãƒªã‚’公開ã—ã¦ã¸ã‚“ãªã‚‰ã€ä»£ã‚りã«tarballã‚’æä¾›ã™ã‚‹å¿…è¦ãŒã‚ã‚‹ã§ã€‚詳細ã¯.config/example.ymlã‚’å‚ç…§ã—ã¦ãªã€‚"
+feedback: "フィードãƒãƒƒã‚¯"
+feedbackUrl: "フィードãƒãƒƒã‚¯URL"
impressum: "é‹å–¶è€…ã®æƒ…å ±"
impressumUrl: "é‹å–¶è€…ã®æƒ…å ±URL"
impressumDescription: "ドイツã¨ã‹ã®ä¸€éƒ¨ã‚“ã¨ã“ã‚ã§ã¯ãªã€è¡¨ç¤ºãŒç¾©å‹™ä»˜ã‘られã¦ã‚“ã­ã‚“(Impressum)。"
@@ -1204,6 +1215,8 @@ soundWillBePlayed: "サウンドãŒå†ç”Ÿã•れるã§"
showReplay: "リプレイ見る"
replay: "リプレイ"
replaying: "リプレイ中"
+endReplay: "リプレイを終了"
+copyReplayData: "リプレイデータをコピー"
ranking: "ランキング"
lastNDays: "ç›´è¿‘{n}æ—¥"
backToTitle: "タイトルã¸"
@@ -1211,9 +1224,34 @@ hemisphere: "ä½ã‚“ã§ã‚‹åœ°åŸŸ"
withSensitive: "センシティブãªãƒ•ァイルをå«ã‚€ãƒŽãƒ¼ãƒˆã‚’表示"
userSaysSomethingSensitive: "{name}ã®ã‚»ãƒ³ã‚·ãƒ†ã‚£ãƒ–ãªãƒ•ァイルをå«ã‚€æŠ•稿"
enableHorizontalSwipe: "スワイプã—ã¦ã‚¿ãƒ–を切り替ãˆã‚‹"
+loading: "読ã¿è¾¼ã¿ä¸­"
surrender: "ã‚„ã‚ã¨ã"
+gameRetry: "ã‚‚ã†ã„ã£ã¡ã‚‡"
+notUsePleaseLeaveBlank: "使用ã›ãˆã¸ã‚“å ´åˆã¯ç©ºæ¬„ã«ã—ã¦ã‚„"
+useTotp: "ワンタイムパスワードを使ã†"
+useBackupCode: "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コードを使ã†"
+launchApp: "アプリを起動"
+useNativeUIForVideoAudioPlayer: "動画・音声ã®å†ç”Ÿã«ãƒ–ラウザã®UIを使用ã™ã‚‹"
+keepOriginalFilename: "オリジナルã®ãƒ•ァイルåã‚’ä¿æŒ"
+keepOriginalFilenameDescription: "ã“ã®è¨­å®šã‚’オフã«ã™ã‚‹ã¨ã€ã‚¢ãƒƒãƒ—ロード時ã«ãƒ•ァイルåãŒè‡ªå‹•ã§ãƒ©ãƒ³ãƒ€ãƒ æ–‡å­—列ã«ç½®ãæ›ãˆã‚‰ã‚Œã‚‹ã§ã€‚"
+noDescription: "説明文ã¯ã‚らã¸ã‚“ã§"
+alwaysConfirmFollow: "フォローã®éš›å¸¸ã«ç¢ºèªã™ã‚‹"
+inquiry: "å•ã„åˆã‚ã›"
+_delivery:
+ stop: "é…ä¿¡ã›ã‡ã¸ã‚“"
+ _type:
+ none: "é…ä¿¡ã—ã¨ã‚‹"
_bubbleGame:
howToPlay: "éŠã³æ–¹"
+ hold: "ホールド"
+ _score:
+ score: "スコア"
+ scoreYen: "稼ã„ã é‡‘é¡"
+ highScore: "ãƒã‚¤ã‚¹ã‚³ã‚¢"
+ maxChain: "最大ãƒã‚§ãƒ¼ãƒ³æ•°"
+ yen: "{yen}円"
+ estimatedQty: "{qty}個分"
+ scoreSweets: "ãŠã«ãŽã‚Š {onigiriQtyWithUnit}"
_howToPlay:
section1: "ä½ç½®ã‚’調整ã—ã¦ãƒã‚³ã«ãƒ¢ãƒŽã‚’è½ã¨ã™ã§ã€‚"
section2: "åŒã˜ã‚‚ã‚“ãŒãã£ã¤ã„ãŸã‚‰åˆ¥ã®ã‚„ã¤ã«ãªã£ã¦ã€ã‚¹ã‚³ã‚¢ãŒã‚‚らãˆã‚‹ã§ã€‚"
@@ -1635,6 +1673,7 @@ _role:
gtlAvailable: "グローãƒãƒ«ã‚¿ã‚¤ãƒ ãƒ©ã‚¤ãƒ³è¦‹ã‚‹"
ltlAvailable: "ローカルタイムライン見る"
canPublicNote: "パブリック投稿ã§ãã‚‹ã‹"
+ mentionMax: "ãƒŽãƒ¼ãƒˆå†…ã®æœ€å¤§ãƒ¡ãƒ³ã‚·ãƒ§ãƒ³æ•°"
canInvite: "サーãƒãƒ¼æ‹›å¾…コード作る"
inviteLimit: "招待コード作れる数"
inviteLimitCycle: "招待コードã®ä½œã‚Œã‚‹é–“éš”"
@@ -1658,8 +1697,14 @@ _role:
canUseTranslator: "翻訳使ãˆã‚‹ã‹ã©ã†ã‹"
avatarDecorationLimit: "アイコンデコã®ã„ã£ã¡ã°ã‚“ã¤ã‘れる数"
_condition:
+ roleAssignedTo: "マニュアルロールã«ã‚¢ã‚µã‚¤ãƒ³æ¸ˆã¿"
isLocal: "ローカルユーザー"
isRemote: "リモートユーザー"
+ isCat: "猫ユーザー"
+ isBot: "botユーザー"
+ isSuspended: "サスペンド済ã¿ãƒ¦ãƒ¼ã‚¶ãƒ¼"
+ isLocked: "éµã‚¢ã‚«ã‚¦ãƒ³ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼"
+ isExplorable: "「アカウントを見ã¤ã‘ã‚„ã™ãã™ã‚‹ã€ãŒæœ‰åйãªãƒ¦ãƒ¼ã‚¶ãƒ¼"
createdLessThan: "アカウント作ã£ã¦ã‹ã‚‰ï½žä»¥å†…"
createdMoreThan: "アカウント作ã£ã¦ã‹ã‚‰ï½žçµŒéŽ"
followersLessThanOrEq: "フォロワー数ãŒï½žä»¥ä¸‹"
@@ -1729,6 +1774,7 @@ _plugin:
installWarn: "ä¿¡é ¼ã§ãã¸ã‚“プラグインã¯ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã›ã‚“ã¨ã£ã¦ãª"
manage: "プラグインã®ç®¡ç†"
viewSource: "ソース見る"
+ viewLog: "ログを表示"
_preferencesBackups:
list: "作ã£ãŸãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—"
saveNew: "æ–°ã—ãä¿å­˜"
@@ -1743,8 +1789,8 @@ _preferencesBackups:
deleteConfirm: "{name}を消ã™ã‚“?"
renameConfirm: "「{old}ã€ã‚’「{new}ã€ã«å¤‰ãˆã‚‹ã‚“?"
noBackups: "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—ã¯ãªã„ã§ã€‚「新ã—ãä¿å­˜ã€ã£ã¦ã¨ã“ã§ã“ã®ã‚¯ãƒ©ã‚¤ã‚¢ãƒ³ãƒˆè¨­å®šã‚’鯖ã«ä¿å­˜ã§ãã‚‹ã§ã€‚"
- createdAt: "作ã£ãŸæ—¥æ™‚:{date}{time}"
- updatedAt: "更新日時:{date}{time}"
+ createdAt: "作ã£ãŸæ—¥æ™‚: {date} {time}"
+ updatedAt: "更新日時: {date} {time}"
cannotLoad: "読ã¿è¾¼ã¿ã§ãã¸ã‚“..."
invalidFile: "ファイル形å¼ãŒé•ã†ã§ï¼Ÿ"
_registry:
@@ -1758,6 +1804,8 @@ _aboutMisskey:
contributors: "主ãªè²¢çŒ®è€…"
allContributors: "å…¨ã¦ã®è²¢çŒ®è€…"
source: "ソースコード"
+ original: "オリジナル"
+ thisIsModifiedVersion: "{name}ã¯ã‚ªãƒªã‚¸ãƒŠãƒ«ã®Sharkeyã‚’ã„ã˜ã£ãŸãƒãƒ¼ã‚¸ãƒ§ãƒ³ã‚’ã¤ã“ã†ã¦ã‚‹ã§ã€‚"
translation: "Sharkeyを翻訳"
donate: "Sharkeyã«å¯„付"
morePatrons: "ä»–ã«ã‚‚ãŽã‚‡ã†ã•ã‚“ã®äººã‹ã‚‰ã‚µãƒãƒ¼ãƒˆã—ã¦ã‚‚ã‚ã¦ã‚“ã­ã‚“。ã»ã‚“ã¾ãŠãŠãã«ðŸ¥°"
@@ -1916,7 +1964,6 @@ _2fa:
registerTOTP: "èªè¨¼ã‚¢ãƒ—リã®è¨­å®šã¯ã˜ã‚ã‚‹"
step1: "ã»ã‚“ãªã‚‰ã€{a}ã‚„{b}ã¨ã‹ã®èªè¨¼ã‚¢ãƒ—リを使ã£ã¨ã‚‹ãƒ‡ãƒã‚¤ã‚¹ã«ã‚¤ãƒ³ã‚¹ãƒˆãƒ¼ãƒ«ã—ã¦ãªã€‚"
step2: "次ã«ã€ã“ã“ã«ã‚ã‚‹QRコードをアプリã§ã‚¹ã‚­ãƒ£ãƒ³ã—ã¦ãªï½žã€‚"
- step2Click: "QRコード押ã—ãŸã‚‰ã€ä»Šä½¿ã¨ã‚‹ç«¯æœ«ã«å…¥ã£ã¨ã‚‹èªè¨¼ã‚¢ãƒ—リã¨ã‹ã‚­ãƒ¼ãƒªãƒ³ã‚°ã«ç™»éŒ²ã§ãã‚‹ã§ã€‚"
step2Uri: "ãƒ‡ã‚¹ã‚¯ãƒˆãƒƒãƒ—ã‚¢ãƒ—ãƒªã‚’ä½¿ã†æ™‚ã¯æ¬¡ã®URIを入れるã§"
step3Title: "確èªã‚³ãƒ¼ãƒ‰ã‚’入れã¦ãƒ¼ã‚„"
step3: "ã‚¢ãƒ—ãƒªã«æ˜ ã£ã¨ã‚‹ç¢ºèªã‚³ãƒ¼ãƒ‰ï¼ˆãƒˆãƒ¼ã‚¯ãƒ³ï¼‰ã‚’入れã¦çµ‚ã‚りや。"
@@ -1940,6 +1987,7 @@ _2fa:
backupCodesDescription: "èªè¨¼ã‚¢ãƒ—リãŒä½¿ç”¨ã§ãã‚“ãªã£ãŸå ´åˆã€ä»¥ä¸‹ã®ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コードを使ã£ã¦ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã«ã‚¢ã‚¯ã‚»ã‚¹ã§ãã‚‹ã§ã€‚ã“れらã®ã‚³ãƒ¼ãƒ‰ã¯å¿…ãšå®‰å…¨ãªå ´æ‰€ã«ç½®ã„ã¨ãや。å„コードã¯ä¸€å›žã ã‘使用ã§ãã‚‹ã§ã€‚"
backupCodeUsedWarning: "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コードãŒä½¿ç”¨ã•れãŸã§ã€‚èªè¨¼ã‚¢ãƒ—リãŒä½¿ãˆãªããªã£ã¦ã‚‹ã‚“å ´åˆã€ãªã‚‹ã¹ãæ—©ãèªè¨¼ã‚¢ãƒ—リをå†è¨­å®šã—や。"
backupCodesExhaustedWarning: "ãƒãƒƒã‚¯ã‚¢ãƒƒãƒ—コードãŒå…¨ã¦ä½¿ç”¨ã•れãŸã§ã€‚èªè¨¼ã‚¢ãƒ—リを利用ã§ãã‚“å ´åˆã€ã“れ以上アカウントã«ã‚¢ã‚¯ã‚»ã‚¹ã§ããªããªã‚‹ã§ã€‚èªè¨¼ã‚¢ãƒ—リをå†ç™»éŒ²ã—や。"
+ moreDetailedGuideHere: "詳細ãªã‚¬ã‚¤ãƒ‰ã¯ã“ã¡ã‚‰"
_permissions:
"read:account": "ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®æƒ…報を見るã§"
"write:account": "ã‚¢ã‚«ã‚¦ãƒ³ãƒˆã®æƒ…報を変更ã™ã‚‹ã§"
@@ -1990,7 +2038,6 @@ _permissions:
"read:admin:server-info": "サーãƒãƒ¼ã®æƒ…報見る"
"read:admin:show-moderation-log": "モデレーションログ見る"
"read:admin:show-user": "ユーザーã®ãƒ—ãƒ©ã‚¤ãƒ™ãƒ¼ãƒˆãªæƒ…報見る"
- "read:admin:show-users": "ユーザーã®ãƒ—ãƒ©ã‚¤ãƒ™ãƒ¼ãƒˆãªæƒ…報見る"
"write:admin:suspend-user": "ユーザーをå‡çµ"
"write:admin:unset-user-avatar": "ユーザーã®ã‚¢ãƒã‚¿ãƒ¼ã‚’削除"
"write:admin:unset-user-banner": "ユーザーã®ãƒãƒŠãƒ¼ã‚’削除"
@@ -2201,6 +2248,7 @@ _play:
title: "タイトル"
script: "スクリプト"
summary: "説明"
+ visibilityDescription: "éžå…¬é–‹ã«è¨­å®šã™ã‚‹ã¨ãƒ—ロフィールã«è¡¨ç¤ºã•れã¸ã‚“ããªã‚‹ã‘ã©ã€URLを知ã£ã¨ã‚‹äººã¯å¼•ãç¶šãアクセスã§ãã‚‹ã§ã€‚"
_pages:
newPage: "ページを作る"
editPage: "ページã®ç·¨é›†"
@@ -2245,6 +2293,8 @@ _pages:
section: "セクション"
image: "ç”»åƒ"
button: "ボタン"
+ dynamic: "動的ブロック"
+ dynamicDescription: "ã“ã®ãƒ–ロックã¯å»ƒæ­¢ã•れã¨ã‚‹ã§ã€‚今後ã¯{play}を利用ã—ã¦ã‚„。"
note: "ノート埋ã‚è¾¼ã¿"
_note:
id: "ノートID"
@@ -2274,8 +2324,10 @@ _notification:
sendTestNotification: "テスト通知をé€ä¿¡ã™ã‚‹ã§"
notificationWillBeDisplayedLikeThis: "通知ã¯ã“ã®ã‚ˆã†ã«è¡¨ç¤ºã•れるã§"
reactedBySomeUsers: "{n}人ãŒãƒ„ッコんã ã§"
+ likedBySomeUsers: "{n}人ãŒã„ã„ã­ã—ãŸã§"
renotedBySomeUsers: "{n}人ãŒãƒ–ーストã—ãŸã§"
followedBySomeUsers: "{n}人ã«ãƒ•ォローã•れãŸã§"
+ flushNotification: "通知ã®å±¥æ­´ã‚’リセットã™ã‚‹"
_types:
all: "ã™ã¹ã¦"
note: "ã‚ã‚“ãŸã‚‰ã®æ–°è¦æŠ•稿"
@@ -2373,6 +2425,7 @@ _moderationLogTypes:
resetPassword: "パスワードをリセット"
suspendRemoteInstance: "リモートサーãƒãƒ¼ã‚’æ­¢ã‚ã‚“ã§"
unsuspendRemoteInstance: "リモートサーãƒãƒ¼ã‚’å†é–‹ã™ã‚“ã§"
+ updateRemoteInstanceNote: "リモートサーãƒãƒ¼ã®ãƒ¢ãƒ‡ãƒ¬ãƒ¼ã‚·ãƒ§ãƒ³ãƒŽãƒ¼ãƒˆæ›´æ–°"
markSensitiveDriveFile: "ファイルをセンシティブ付与"
unmarkSensitiveDriveFile: "ファイルをセンシティブ解除"
resolveAbuseReport: "苦情を解決"
@@ -2493,6 +2546,26 @@ _reversi:
opponentHasSettingsChanged: "相手ãŒè¨­å®šå¤‰ãˆãŸã§"
allowIrregularRules: "å¤‰å‰‡è¨±å¯ (完全フリー)"
disallowIrregularRules: "変則ãªã—"
+ showBoardLabels: "盤é¢ã«è¡Œãƒ»åˆ—番å·ã‚’表示"
+ useAvatarAsStone: "石をアイコンã«ã™ã‚‹"
_offlineScreen:
title: "オフライン - サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã²ã‚“ã§"
header: "サーãƒãƒ¼ã«æŽ¥ç¶šã§ãã¸ã‚“ã‚"
+_urlPreviewSetting:
+ title: "URLプレビューã®è¨­å®š"
+ enable: "URLプレビューを有効ã«ã™ã‚‹"
+ timeout: "プレビューå–得時ã®ã‚¿ã‚¤ãƒ ã‚¢ã‚¦ãƒˆ(ms)"
+ timeoutDescription: "プレビューå–å¾—ã®æ‰€è¦æ™‚é–“ãŒã“ã®å€¤ã‚’è¶…ãˆãŸå ´åˆã€ãƒ—レビューã¯ç”Ÿæˆã•れã¸ã‚“ã§ã€‚"
+ maximumContentLength: "Content-Lengthã®æœ€å¤§å€¤(byte)"
+ maximumContentLengthDescription: "Content-LengthãŒã“ã®å€¤ã‚’è¶…ãˆãŸå ´åˆã€ãƒ—レビューã¯ç”Ÿæˆã•れã¸ã‚“ã§ã€‚"
+ requireContentLength: "Content-LengthãŒå–å¾—ã§ããŸå ´åˆã®ã¿ãƒ—レビューを生æˆ"
+ requireContentLengthDescription: "相手サーãƒãŒContent-Lengthã‚’è¿”ã•ãªã„å ´åˆã€ãƒ—レビューã¯ç”Ÿæˆã•れã¸ã‚“ã§ã€‚"
+ userAgent: "User-Agent"
+ userAgentDescription: "プレビューå–得時ã«ä½¿ç”¨ã•れるUser-Agentを設定ã™ã‚‹ã§ã€‚空欄ã®å ´åˆã€ãƒ‡ãƒ•ォルトã®User-AgentãŒä½¿ç”¨ã•れるã§ã€‚"
+ summaryProxy: "プレビューを生æˆã™ã‚‹ãƒ—ロキシã®ã‚¨ãƒ³ãƒ‰ãƒã‚¤ãƒ³ãƒˆ"
+ summaryProxyDescription: "Misskey本体やãªãã€ã‚µãƒžãƒªãƒ¼ãƒ—ロキシを使用ã—ã¦ãƒ—レビューを生æˆã™ã‚‹ã§ã€‚"
+ summaryProxyDescription2: "プロキシã«ã¯ä¸‹è¨˜ãƒ‘ラメータãŒã‚¯ã‚¨ãƒªæ–‡å­—列ã¨ã—ã¦é€£æºã•れるã§ã€‚プロキシå´ãŒã“れらをサãƒãƒ¼ãƒˆã›ãˆã¸ã‚“ã¨ãã¯ã€è¨­å®šå€¤ã¯ç„¡è¦–ã•れるã§ã€‚"
+_mediaControls:
+ pip: "ピクãƒãƒ£ã‚¤ãƒ³ãƒ”クãƒãƒ£"
+ playbackRate: "å†ç”Ÿé€Ÿåº¦"
+ loop: "ループå†ç”Ÿ"
diff --git a/locales/jbo-EN.yml b/locales/jbo-EN.yml
index 297ca53dd7..d4fea291d7 100644
--- a/locales/jbo-EN.yml
+++ b/locales/jbo-EN.yml
@@ -1,4 +1,3 @@
---
_lang_: "la .lojban."
headlineMisskey: "lo se tcana noi jorne fi loi notci"
-
diff --git a/locales/kab-KAB.yml b/locales/kab-KAB.yml
index b976f028f0..22e24d3baa 100644
--- a/locales/kab-KAB.yml
+++ b/locales/kab-KAB.yml
@@ -104,4 +104,3 @@ _deck:
_columns:
notifications: "Ilɣuyen"
list: "Tibdarin"
-
diff --git a/locales/kn-IN.yml b/locales/kn-IN.yml
index bb6d1ee242..b3ad46f2b1 100644
--- a/locales/kn-IN.yml
+++ b/locales/kn-IN.yml
@@ -84,4 +84,3 @@ _deck:
notifications: "ಅಧಿಸೂಚನೆಗಳà³"
tl: "ಸಮಯಸಾಲà³"
mentions: "ಹೆಸರಿಸಿದ"
-
diff --git a/locales/ko-GS.yml b/locales/ko-GS.yml
index 39492d902f..9466aff01f 100644
--- a/locales/ko-GS.yml
+++ b/locales/ko-GS.yml
@@ -16,8 +16,8 @@ cancel: "ì•„ì´ì˜ˆ"
noThankYou: "뎃어예"
enterUsername: "ì‚¬ìš©ìž ì´ëŸ¼ 서기"
renotedBy: "{user}ë‹˜ì´ ë¦¬ë…¸íŠ¸í–‡ì–´ì˜ˆ"
-noNotes: "노트가 없십니다"
-noNotifications: "ì•Œë¦¼ì´ ì—†ì‹­ë‹ˆë‹¤"
+noNotes: "노트가 á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
+noNotifications: "ì•Œë¦¼ì´ á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
instance: "서버"
settings: "설정"
notificationSettings: "알림 설정"
@@ -26,7 +26,7 @@ otherSettings: "다린 설정"
openInWindow: "창서 ì˜ê¸°"
profile: "프로필"
timeline: "타임ë¼ì¸"
-noAccountDescription: "ìžê¸°ì†Œê°œê°€ 없십니다"
+noAccountDescription: "ìžê¸°ì†Œê°œê°€ á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
login: "로그ì¸"
loggingIn: "로그ì¸í•˜ê³  잇어예"
logout: "로그아웃"
@@ -80,7 +80,7 @@ unfollowConfirm: "{name}님얼 고마 팔로잉합니꺼?"
exportRequested: "내가기 요청얼 햇십니다. ì‹œê°„ì´ ìª¼ë§¤ 걸릴 ê¹ë‹ˆë‹¤. ìš”ì²­ì´ ê»•ë‚˜ëª¨ ‘드ë¼ì´ë¸Œâ€™ì— 옇십니다."
importRequested: "가오기 요청얼 햇십니다. ì‹œê°„ì´ ìª¼ë§¤ 걸릴 ê¹ë‹ˆë‹¤."
lists: "리스트"
-noLists: "리스트가 없십니다"
+noLists: "리스트가 á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
note: "노트"
notes: "노트"
following: "팔로잉"
@@ -161,7 +161,7 @@ youCanCleanRemoteFilesCache: "íŒŒì¼ ê°„ë¦¬ìœ¼ ðŸ—‘ï¸ ëª¨ëƒ¥ì–¼ 누질리모 ìº
cacheRemoteSensitiveFiles: "웬ê²ìœ¼ 수ᇚ힌 파ì¼ì–¼ ìºì‹œí•˜ê¸°"
cacheRemoteSensitiveFilesDescription: "ìš” 설정얼 꺼모 ì›¬ê² á„‰á…®á‡šížŒ 파ì¼ì´ ìºì‹œí•˜ì§€ ì•„ì´í•˜ê³  바리 ë§í¬í•©ë‹ˆë‹¤."
flagAsBot: "ìžë™ 게정입니다"
-flagAsBotDescription: "ìš” 게정얼 프로그램서 설ë¼ë¨¼ 키야 합니다. 키모 다런 개발ìžê°€ 반엉얼 ë‹ì—†ì´ ë°í’€ì´í•˜ì§€ 몬 하게 ë„ì•„ 줄 수 잇고 Misskey으 시스템서 ìžë™ ê²Œì •ì´ ëŽë‹ˆë‹¤."
+flagAsBotDescription: "ìš” 게정얼 프로그램서 설ë¼ë¨¼ 키야 합니다. 키모 다런 개발ìžê°€ 반엉얼 ë‹á„‹á…¥á‡ì´ ë°í’€ì´í•˜ì§€ 몬 하게 ë„ì•„ 줄 수 잇고 Misskey으 시스템서 ìžë™ ê²Œì •ì´ ëŽë‹ˆë‹¤."
flagAsCat: "ì• ì›…ì• ì›…ì• ì›…ì• ì›…!"
flagAsCatDescription: "애옹?"
flagShowTimelineReplies: "타임ë¼ì¸ì„œ 노트으 답하기 보기"
@@ -176,7 +176,7 @@ wallpaper: "벡지"
setWallpaper: "벡지 설정"
removeWallpaper: "벡지 ë­‰ìºê¸°"
searchWith: "찾기: {q}"
-youHaveNoLists: "리스트가 없십니다"
+youHaveNoLists: "리스트가 á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
followConfirm: "{name}님얼 팔로잉합니꺼?"
proxyAccount: "프ë½ì‹œ 게정"
proxyAccountDescription: "프ë½ì‹œ 게정언 턱벨한 ì¡°ê²ì„œ ì›¬ê² íŒ”ë¡œìž‰ì–¼ 하넌 게정입니다. 사용ìžê°€ ì›¬ê² ì‚¬ìš©ìžëŸ´ ë¦¬ìŠ¤íŠ¸ì— ì˜‡ì–¼ 때 ë¦¬ìŠ¤íŠ¸ì— ì˜‡ì–¸ 사용ìžëŸ´ ëˆ„ë„ íŒ”ë¡œìž‰ ì•„ì´í•˜ëª¨ í• ë™ì´ 서버로 ì•„ì´ ì˜¤ë‹ˆê»˜ ìš” ê²Œì •ì´ ì•„ì¸ í”„ë½ì‹œ 게정얼 팔로잉하게 합니다."
@@ -210,17 +210,17 @@ instanceInfo: "서버 정보"
statistics: "통게"
clearQueue: "ëŒ€ê¸°ì˜ ë¹„ìš°ê¸°"
clearQueueConfirmTitle: "대기ì˜ì–¼ 비ì›ë‹ˆêº¼?"
-clearQueueConfirmText: "대기ì˜ì— 잇넌 걸얼 ì•„ì´ ë³´ëƒ…ë‹ˆë‹¤. íì´ ìš” ë™ìž‘ì–¸ í•  필요가 없십니다."
+clearQueueConfirmText: "대기ì˜ì— 잇넌 걸얼 ì•„ì´ ë³´ëƒ…ë‹ˆë‹¤. íì´ ìš” ë™ìž‘ì–¸ í•  필요가 á„‹á…¥á‡ì‹­ë‹ˆë‹¤."
clearCachedFiles: "ìºì‹œ 비우기"
clearCachedFilesConfirm: "ìºì‹œí•œ ì›¬ê² íŒŒì¼ì–¼ ë§ìº‰ 뭉캡니꺼?"
blockedInstances: "차단한 서버"
blockedInstancesDescription: "차단할ë¼ë„Œ 서버으 호스트럴 줄 바꿈해서로 ë¹„ì´ ì¤ë‹ˆë‹¤. 차단한 서버넌 ìš” 서버하고 êµë¥˜ 몬 합니다."
silencedInstances: "수ᇚ훈 서버"
-silencedInstancesDescription: "수ᇚ훌ë¼ë„Œ 서버으 호스트럴 줄 바꿈해서로 ë¹„ì´ ì¤ë‹ˆë‹¤. 수ᇚ훈 서버으 게정언 ë§ìº‰ ‘수ᇚ후기’가 ë°ì„œ 팔로잉 요청만 ë°ê³  팔로워가 ì•„ì¸ ë¡œì»¬ 게정서 멘션얼 몬 합니다. 차단한 서버넌 ìƒê°„ 없십니다."
+silencedInstancesDescription: "수ᇚ훌ë¼ë„Œ 서버으 호스트럴 줄 바꿈해서로 ë¹„ì´ ì¤ë‹ˆë‹¤. 수ᇚ훈 서버으 게정언 ë§ìº‰ ‘수ᇚ후기’가 ë°ì„œ 팔로잉 요청만 ë°ê³  팔로워가 ì•„ì¸ ë¡œì»¬ 게정서 멘션얼 몬 합니다. 차단한 서버넌 ìƒê°„ á„‹á…¥á‡ì‹­ë‹ˆë‹¤."
muteAndBlock: "수ᇚ훔하고 차단"
mutedUsers: "수ᇚ훈 사용ìž"
blockedUsers: "차단한 사용ìž"
-noUsers: "사용ìžê°€ 없십니다"
+noUsers: "사용ìžê°€ á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
editProfile: "프로필 ì ê¸°"
noteDeleteConfirm: "요 노트럴 뭉캡니꺼?"
pinLimitExceeded: "ë” ëª¬ 붙입니다"
@@ -230,15 +230,15 @@ processing: "처리하고 잇어예"
preview: "미리보기"
default: "기본값"
defaultValueIs: "기본값: {value}"
-noCustomEmojis: "ì´ëª¨ì§€ê°€ 없십니다"
-noJobs: "ìž‘ì—…ì´ ì—†ì‹­ë‹ˆë‹¤"
+noCustomEmojis: "ì´ëª¨ì§€ê°€ á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
+noJobs: "ìž‘ì—…ì´ á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
federating: "옌합하고 잇어예"
blocked: "차단햇어예"
suspended: "고만 보내예"
all: "ë§ìº‰"
subscribing: "구ë…하고 잇어예"
publishing: "보내고 잇어예"
-notResponding: "ë‹µì´ ì—†ì–´ì˜ˆ"
+notResponding: "ë‹µì´ á„‹á…¥á‡ì–´ì˜ˆ"
instanceFollowing: "서버으 팔로잉"
instanceFollowers: "서버으 팔로워"
instanceUsers: "서버으 사용ìž"
@@ -275,7 +275,7 @@ uploadFromUrlRequested: "올리기럴 요청햇십니다"
uploadFromUrlMayTakeTime: "올리기가 껕날ë¼ë¨¼ ì‹œê°„ì´ ìª¼ë§¤ 걸릴 ê¹ë‹ˆë‹¤."
explore: "살펴보기"
messageRead: "ì´ëŸ¿ì–´ì˜ˆ"
-noMoreHistory: "요카마 엣날 기ë¡ì´ 없십니다"
+noMoreHistory: "요카마 옛날 기ë¡ì´ á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
startMessaging: "대화하기"
nUsersRead: "{n}ë©©ì´ ì´ëŸ¿ì‹­ë‹ˆë‹¤"
agreeTo: "{0}ì— ë™ì´í•˜ê¸°"
@@ -432,28 +432,28 @@ securityKey: "보안키"
lastUsed: "마지막 쓰임"
lastUsedAt: "마지막 쓰임: {t}"
unregister: "맨걸기 무루기"
-passwordLessLogin: "비밀번호 없시 로그ì¸"
-passwordLessLoginDescription: "비밀번호 ë§ê³  보안키나 패스키 ê°™ì€ ê²ƒë§Œ ì¨ ê°€ 로그ì¸í•©ë‹ˆë‹¤."
+passwordLessLogin: "비밀번호 á„‹á…¥á‡ì´ 로그ì¸"
+passwordLessLoginDescription: "비밀번호 á„‹á…¥á‡ì´ 보안 키나 패스 키만 서서 로그ì¸í•©ë‹ˆë‹¤."
resetPassword: "비밀번호 재설정"
-newPasswordIs: "새 비밀번호는 \"{password}\" 입니다"
+newPasswordIs: "새 비밀번호넌 ‘{password}’입니다"
reduceUiAnimation: "화면 움ì§ìž„ íš¨ê³¼ë“¤ì„ á„‰á…®á‡ší›„ê¸°"
share: "노누기"
notFound: "몬 찾앗십니다"
-notFoundDescription: "고런 주소로 들어가는 í•˜ë©˜ì€ ì—†ì‹­ë‹ˆë‹¤."
-uploadFolder: "기본 업로드 위치"
-markAsReadAllNotifications: "모든 알림 ì´ëŸ¿ë‹¤ê³  표시"
-markAsReadAllUnreadNotes: "모든 글 ì´ëŸ¿ë‹¤ê³  표시"
-markAsReadAllTalkMessages: "모든 대화 ì´ëŸ¿ë‹¤ê³  표시"
+notFoundDescription: "ì„  ì£¼ì†Œì— ë§žë„Œ 페ì´ì§€ê°€ á„‹á…¥á‡ì‹­ë‹ˆë‹¤."
+uploadFolder: "기본 올리기 위치"
+markAsReadAllNotifications: "ëª¨ë˜ ì•Œë¦¼ì–¼ ì½ì—„ í¬ì‹œ"
+markAsReadAllUnreadNotes: "ëª¨ë˜ ê±¸ì–¼ ì½ì—„ í¬ì‹œ"
+markAsReadAllTalkMessages: "ëª¨ë˜ ëŒ€í™” ì½ì—„ í¬ì‹œ"
help: "ë„움ë§"
-inputMessageHere: "여따가 메시지를 입력해주ì´ì†Œ"
-close: "닫기"
+inputMessageHere: "옇다 메시지럴 서ì´ì†Œ"
+close: "꺼기"
invites: "초대하기"
-members: "멤버"
-transfer: "ì–‘ë„"
+members: "구성ì›"
+transfer: "넘구기"
title: "제목"
-text: "글"
+text: "걸"
enable: "키기"
-next: "다ìŒ"
+next: "다엄"
retype: "다시 서기"
noteOf: "{user}님으 노트"
quoteAttached: "따옴"
@@ -468,6 +468,7 @@ tooShort: "억수로 짜립니다"
tooLong: "억수로 집니다"
passwordMatched: "맞십니다"
passwordNotMatched: "안 맞십니다"
+signinWith: "{n}서 로그ì¸"
signinFailed: "ë¡œê·¸ì¸ ëª¬ 했십니다. ê³  ì´ë¦„ì´ëž‘ 비밀번호 제대로 ì¼ëŠ”ê°€ 확ì¸í•´ 주ì´ì†Œ."
or: "아니면"
language: "언어"
@@ -512,13 +513,13 @@ useObjectStorage: "오브ì íЏ 스토리지 키기"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "오브ì íЏ (미디어) 참조 ë§í¬ 만들 때 쓰는 URL임다. CDN ë‚´ì§€ 프ë½ì‹œë¥¼ 쓴다 ì¹´ë©˜ì€ ê·¸ URLì„ ê°–ë‹¤ 늫고, ì•„ì´ë©´ ì¨ë¨¹ì„ 서비스네 ê°€ì´ë“œë¥¼ ë´ë´ê°€ 공개ì ìœ¼ë¡œ 접근할 수 있는 주소를 ì—¬ 넣어 주ì´ì†Œ. 그니께, ë‚´ê°€ AWS S3ì„ ì“´ë‹¤ ì¹´ë©´ì€ 'https://<bucket>.s3.amazonaws.com', GCS를 쓴다 ì¹´ë©´ 'https://storage.googleapis.com/<bucket>' 처럼 쓰믄 ë˜ìž…니ë”."
objectStorageBucket: "Bucket"
-objectStorageBucketDesc: "ì¨ë¨¹ì„ ì„œë¹„ìŠ¤ì˜ ë°”ê»˜ì“° ì´ë¦„ì„ ì—¬ ì¨ ì£¼ì´ì†Œ."
+objectStorageBucketDesc: "설 서비스으 버킷 ì´ëŸ¼ì–¼ 서 주ì´ì†Œ."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "ìš” Prefix 디렉토리 안ì—다가 파ì¼ì´ 들어ê°ë‹¤."
objectStorageEndpoint: "Endpoint"
-objectStorageEndpointDesc: "AWS S3ì„ ì“¸ë¼ë©˜ 요는 비워ë‘ê³ , ì•„ì´ë©˜ì€ ê·¸ 서비스 ê°€ì´ë“œì— 맞게 endpoint를 넣어 주ì´ì†Œ. '<host>' ë‚´ì§€ '<host>:<port>'처럼 넣십니다."
+objectStorageEndpointDesc: "AWS S3넌 비아 ë‘ê³  다런 것언 ê±° 서비스으 엔드í¬ì¸íŠ¸ëŸ´ 서 주ì´ì†Œ. ‘<host>’나 ‘<host>:<port>’맨치로 섭니다."
objectStorageRegion: "Region"
-objectStorageRegionDesc: "'xx-east-1' ê°™ì€ region ì´ë¦„ì„ ì˜‡ì–´ 주ì´ì†Œ. ë§Œì•½ì— ë‚´ 서비스엔 region ê°™ì€ ê°œë…ì´ ìŽë‹¤, ì¹´ë©´ì€ ëŒ€ì‹ ì— 'us-east-1'ë¼ê³  í•´ ë‘ì´ì†Œ. AWS 설정 파ì¼ì´ë‚˜ 환경 변수를 ëŒì–´ë‹¤ 쓰겠다믄 요는 비워 ë‘ì´ì†Œ."
+objectStorageRegionDesc: "‘xx-east-1’맨치로 리전 ì´ëŸ¼ì–¼ 서 주ì´ì†Œ. 설 ì„œë¹„ìŠ¤ì— ë¦¬ì „ ê°œë„´ì´ á„‹á…¥á‡ì–´ë¨¼ ‘us-east-1’ë¼ê³  í•´ ë‘ì´ì†Œ. ì—ì´ë”블유ì—스 설정 파ì¼ì´ë‚˜ 환겡 벤수가 이ᇇ어면 비아 ë‘ì´ì†Œ."
objectStorageUseSSL: "SSL 쓰기"
objectStorageUseSSLDesc: "API 호출할 때 HTTPS 안 ì“¸ê±°ë©´ì€ êº¼ ë‘ì´ì†Œ"
objectStorageUseProxy: "ì—°ê²°ì— í”„ë½ì‹œ 사용"
@@ -534,7 +535,7 @@ newNoteRecived: "새 노트 있어예"
sounds: "소리"
sound: "소리"
listen: "듣기"
-none: "ì—†ìŒ"
+none: "á„‹á…¥á‡ì—„"
showInPage: "바닥서 보기"
popout: "새 창 열기"
volume: "ìŒëŸ‰"
@@ -542,13 +543,13 @@ masterVolume: "대빵 ìŒëŸ‰"
notUseSound: "ìŒì†Œê±°í•˜ê¸°"
useSoundOnlyWhenActive: "Misskeyê°€ 활성화ë˜ì–´ ìžˆì„ ë•Œë§Œ 소리 내기"
details: "ìžì„¸ížˆ"
-chooseEmoji: "ì´ëª¨ì§€ ì„ íƒ"
+chooseEmoji: "ì´ëª¨ì§€ 개리기"
unableToProcess: "작업 다 몬 했십니다"
recentUsed: "최근 쓴 놈"
install: "설치"
uninstall: "삭제"
installedApps: "ì„¤ì¹˜ëœ ì• í”Œë¦¬ì¼€ì´ì…˜"
-nothing: "ë­£ë„ ì—†ì–´ì˜ˆ"
+nothing: "á„‹á…¥á‡ì–´ì˜ˆ"
installedDate: "설치한 날"
lastUsedDate: "마지막 사용"
state: "ìƒíƒœ"
@@ -579,6 +580,7 @@ enableInfiniteScroll: "알아서 ë” ë³´ê¸°"
useCw: "내용 수ᇚ후기"
description: "설멩"
describeFile: "캡션 옇기"
+enterFileDescription: "캡션 서기"
author: "ë§¨ë˜ ì‚¬ëžŒ"
manage: "간리"
emailServer: "ì „ìžìš°íŽœ 서버"
@@ -598,6 +600,7 @@ reporter: "신고한 사람"
reporteeOrigin: "ì‹ ê³ ë´ ì‚¬ëžŒ"
reporterOrigin: "신고한 곳"
forwardReport: "ì›¬ê² ì„œë²„ì— ì‹ ê³  보내기"
+waitingFor: "{x}(얼)럴 지달리고 잇십니다"
random: "무작ì´"
system: "시스템"
clip: "í´ë¦½ 맨걸기"
@@ -610,10 +613,13 @@ followersCount: "팔로워 수"
noteFavoritesCount: "질겨찾기한 노트 수"
clips: "í´ë¦½ 맨걸기"
clearCache: "ìºì‹œ 비우기"
+typingUsers: "{users} ë‹˜ì´ ì„œê³  잇어예"
unlikeConfirm: "좋네예럴 무룹니꺼?"
info: "ì •ë³´"
+selectAccount: "계정 개리기"
user: "사용ìž"
administration: "간리"
+translatedFrom: "{x}서 번옉"
on: "í‚´"
off: "껌"
hide: "수ᇚ후기"
@@ -625,6 +631,8 @@ oneDay: "하리"
oneWeek: "한 주"
oneMonth: "한 달"
file: "파ì¼"
+typeToConfirm: "게ì†í• ë¼ë¨¼ {x}럴 ëˆ„ì§ˆë¼ ì£¼ì´ì†Œ"
+pleaseSelect: "개리 주ì´ì†Œ"
tools: "ë„구"
like: "좋네예!"
unlike: "좋네예 무루기"
@@ -632,7 +640,7 @@ numberOfLikes: "좋네예 수"
show: "보기"
roles: "옉할"
role: "옉할"
-noRole: "ì˜‰í• ì´ ì—†ì‹­ë‹ˆë‹¤"
+noRole: "ì˜‰í• ì´ á„‹á…¥á‡ì‹­ë‹ˆë‹¤"
thisPostMayBeAnnoyingCancel: "ì•„ì´ì˜ˆ"
likeOnly: "좋네예마"
myClips: "ë‚´ í´ë¦½"
@@ -641,6 +649,10 @@ replies: "답하기"
renotes: "리노트"
attach: "옇기"
surrender: "ì•„ì´ì˜ˆ"
+_delivery:
+ stop: "고만 보내예"
+ _type:
+ none: "보내고 잇어예"
_initialAccountSetting:
startTutorial: "길ë¼ìž¡ì´ 하기"
_initialTutorial:
@@ -720,6 +732,7 @@ _theme:
_sfx:
note: "새 노트"
notification: "알림"
+ reaction: "리액션 개리기"
_2fa:
step3Title: "í•™ì¸ ê¸°í˜¸ëŸ´ 서기"
renewTOTPCancel: "뎃어예"
@@ -744,6 +757,9 @@ _cw:
_visibility:
home: "ëœë¨¸ë¦¬"
followers: "팔로워"
+_postForm:
+ _placeholders:
+ e: "옇다 서 주ì´ì†Œ"
_profile:
name: "ì´ëŸ¼"
username: "ì‚¬ìš©ìž ì´ëŸ¼"
@@ -796,5 +812,8 @@ _moderationLogTypes:
resetPassword: "비밀번호 재설정"
resolveAbuseReport: "신고 해겔하기"
_reversi:
- total: "합계"
-
+ reversi: "리버시"
+ chooseBoard: "보드 개리기"
+ black: "꺼ë©"
+ white: "í—ˆì˜"
+ total: "합게"
diff --git a/locales/ko-KR.yml b/locales/ko-KR.yml
index 39a186a7f8..69e1216ce4 100644
--- a/locales/ko-KR.yml
+++ b/locales/ko-KR.yml
@@ -38,9 +38,9 @@ addUser: "유저 추가"
favorite: "ì¦ê²¨ì°¾ê¸°"
favorites: "ì¦ê²¨ì°¾ê¸°"
unfavorite: "ì¦ê²¨ì°¾ê¸°ì—서 제거"
-favorited: "ì¦ê²¨ì°¾ê¸°ì— 등ë¡í–ˆìŠµë‹ˆë‹¤"
-alreadyFavorited: "ì´ë¯¸ ì¦ê²¨ì°¾ê¸°ì— 등ë¡ë˜ì–´ 있습니다"
-cantFavorite: "ì¦ê²¨ì°¾ê¸°ì— 등ë¡í•˜ì§€ 못했습니다"
+favorited: "ì¦ê²¨ì°¾ê¸°ì— 등ë¡í–ˆìŠµë‹ˆë‹¤."
+alreadyFavorited: "ì´ë¯¸ ì¦ê²¨ì°¾ê¸°ì— 등ë¡í–ˆìŠµë‹ˆë‹¤."
+cantFavorite: "ì¦ê²¨ì°¾ê¸°ì— 등ë¡í•˜ì§€ 못했습니다."
pin: "í”„ë¡œí•„ì— ê³ ì •"
unpin: "프로필ì—서 ê³ ì • í•´ì œ"
copyContent: "내용 복사"
@@ -93,7 +93,7 @@ somethingHappened: "오류가 ë°œìƒí–ˆìŠµë‹ˆë‹¤"
retry: "다시 시ë„"
pageLoadError: "페ì´ì§€ë¥¼ 불러오지 못했습니다."
pageLoadErrorDescription: "ë„¤íŠ¸ì›Œí¬ ì—°ê²° ë˜ëŠ” 브ë¼ìš°ì € ìºì‹œë¡œ ì¸í•´ ë°œìƒí–ˆì„ ê°€ëŠ¥ì„±ì´ ë†’ìŠµë‹ˆë‹¤. ìºì‹œë¥¼ 삭제하거나, 잠시 후 다시 시ë„í•´ 주세요."
-serverIsDead: "서버로부터 ì‘ë‹µì´ ì—†ìŠµë‹ˆë‹¤. 잠시 후 다시 시ë„해주세요."
+serverIsDead: "서버가 ì‘답하지 않습니다. 잠시 후 다시 시ë„í•´ 주세요."
youShouldUpgradeClient: "ì´ íŽ˜ì´ì§€ë¥¼ 표시하려면 새로고침하여 새로운 ë²„ì „ì˜ í´ë¼ì´ì–¸íŠ¸ë¥¼ ì´ìš©í•´ 주십시오."
enterListName: "리스트 ì´ë¦„ì„ ìž…ë ¥"
privacy: "프ë¼ì´ë²„시"
@@ -119,8 +119,8 @@ you: "나"
clickToShow: "í´ë¦­í•˜ì—¬ 보기"
sensitive: "열람 주ì˜"
add: "추가"
-reaction: "리액션"
-reactions: "리액션"
+reaction: "ë°˜ì‘"
+reactions: "ë°˜ì‘"
emojiPicker: "ì´ëª¨ì§€ ì„ íƒê¸°"
pinnedEmojisForReactionSettingDescription: "ë¦¬ì•¡ì…˜ì„ í•  때 í”„ë¡œí•„ì— ê³ ì •í•˜ì—¬ 표시할 ì´ëª¨ì§€ë¥¼ 설정할 수 있습니다"
pinnedEmojisSettingDescription: "ì´ëª¨ì§€ë¥¼ 입력할 때 í”„ë¡œí•„ì— ê³ ì •í•˜ì—¬ 표시할 ì´ëª¨ì§€ë¥¼ 설정할 수 있습니다"
@@ -169,7 +169,7 @@ cacheRemoteSensitiveFilesDescription: "ì´ ì„¤ì •ì„ ë¹„í™œì„±í™”í•˜ë©´ 리모íŠ
flagAsBot: "나는 봇입니다"
flagAsBotDescription: "ì´ ê³„ì •ì„ ìžë™í™”ëœ ìˆ˜ë‹¨ìœ¼ë¡œ 운용할 ê²½ìš°ì— í™œì„±í™”í•´ 주세요. ì´ í”Œëž˜ê·¸ë¥¼ 활성화하면, 다른 ë´‡ì´ ì´ë¥¼ 참고하여 ë´‡ ë¼ë¦¬ì˜ 무한 연쇄 ë°˜ì‘ì„ íšŒí”¼í•˜ê±°ë‚˜, ì´ ê³„ì •ì˜ ì‹œìŠ¤í…œ ìƒì—ì„œì˜ ì·¨ê¸‰ì´ Bot ìš´ì˜ì— 최ì í™”ë˜ëŠ” ë“±ì˜ ë³€í™”ê°€ ìƒê¹ë‹ˆë‹¤."
flagAsCat: "미야아아아오오오오오오오오오옹!!!!!!!"
-flagAsCatDescription: "야옹?"
+flagAsCatDescription: "야옹?(ì´ ê³„ì •ì´ ê³ ì–‘ì´ë¼ë©´ 눌러 주세요.)"
flagShowTimelineReplies: "타임ë¼ì¸ì— ë…¸íŠ¸ì˜ ë‹µê¸€ì„ í‘œì‹œí•˜ê¸°"
flagShowTimelineRepliesDescription: "ì´ ì„¤ì •ì„ í™œì„±í™”í•˜ë©´ 타임ë¼ì¸ì— 다른 유저 ê°„ì˜ ë‹µê¸€ì„ í‘œì‹œí•©ë‹ˆë‹¤."
autoAcceptFollowed: "팔로우 ì¤‘ì¸ ìœ ì €ë¡œë¶€í„°ì˜ íŒ”ë¡œìš° ìš”ì²­ì„ ìžë™ 수ë½"
@@ -187,7 +187,7 @@ followConfirm: "{name}ë‹˜ì„ íŒ”ë¡œìš° 하시겠습니까?"
proxyAccount: "프ë¡ì‹œ 계정"
proxyAccountDescription: "프ë¡ì‹œ ê³„ì •ì€ íŠ¹ì • ì¡°ê±´ 하ì—서 ìœ ì €ì˜ ë¦¬ëª¨íŠ¸ 팔로우를 대행하는 계정입니다. 예를 들면, 유저가 리모트 유저를 ë¦¬ìŠ¤íŠ¸ì— ë„£ì—ˆì„ ë•Œ, ë¦¬ìŠ¤íŠ¸ì— ë“¤ì–´ê°„ 유저를 ì•„ë¬´ë„ íŒ”ë¡œìš°í•œ ì ì´ 없다면 액티비티가 서버로 배달ë˜ì§€ 않기 때문ì—, 대신 프ë¡ì‹œ ê³„ì •ì´ í•´ë‹¹ 유저를 팔로우하ë„ë¡ í•©ë‹ˆë‹¤."
host: "호스트"
-selectUser: "유저 ì„ íƒ"
+selectUser: "ì‚¬ìš©ìž ì„ íƒ"
recipient: "수신ì¸"
annotation: "ë‚´ìš©ì— ëŒ€í•œ 주ì„"
federation: "ì—°í•©"
@@ -230,7 +230,7 @@ noUsers: "ì•„ë¬´ë„ ì—†ìŠµë‹ˆë‹¤"
editProfile: "프로필 수정"
noteDeleteConfirm: "ì´ ë…¸íŠ¸ë¥¼ 삭제하시겠습니까?"
pinLimitExceeded: "ë” ì´ìƒ ê³ ì •í•  수 없습니다."
-intro: "Misskeyì˜ ì„¤ì¹˜ê°€ 완료ë˜ì—ˆìŠµë‹ˆë‹¤! ê´€ë¦¬ìž ê³„ì •ì„ ìƒì„±í•´ì£¼ì„¸ìš”."
+intro: "Misskeyì˜ ì„¤ì¹˜ë¥¼ 완료했습니다! ê´€ë¦¬ìž ê³„ì •ì„ ë§Œë“¤ì–´ 주세요."
done: "완료"
processing: "처리중"
preview: "미리보기"
@@ -296,7 +296,7 @@ activity: "활ë™"
images: "ì´ë¯¸ì§€"
image: "ì´ë¯¸ì§€"
birthday: "ìƒì¼"
-yearsOld: "{age}세"
+yearsOld: "만 {age} 세"
registeredDate: "등ë¡ì¼"
location: "장소"
theme: "테마"
@@ -400,6 +400,7 @@ name: "ì´ë¦„"
antennaSource: "ë°›ì„ ì†ŒìŠ¤"
antennaKeywords: "ë°›ì„ ê²€ìƒ‰ì–´"
antennaExcludeKeywords: "제외할 검색어"
+antennaExcludeBots: "봇 계정 제외"
antennaKeywordsDescription: "공백으로 구분하는 경우 AND, 줄바꿈으로 구분하는 경우 OR로 지정ë©ë‹ˆë‹¤"
notifyAntenna: "새로운 노트를 알림"
withFileAntenna: "파ì¼ì´ ì²¨ë¶€ëœ ë…¸íŠ¸ë§Œ"
@@ -414,11 +415,11 @@ silence: "사ì¼ëŸ°ìФ"
silenceConfirm: "ì´ ê³„ì •ì„ ì‚¬ì¼ëŸ°ìŠ¤ë¡œ 설정하시겠습니까?"
unsilence: "사ì¼ëŸ°ìФ í•´ì œ"
unsilenceConfirm: "ì´ ê³„ì •ì˜ ì‚¬ì¼ëŸ°ìŠ¤ë¥¼ 해제하시겠습니까?"
-popularUsers: "ì¸ê¸° 유저"
-recentlyUpdatedUsers: "최근 활ë™í•œ 유저"
-recentlyRegisteredUsers: "최근 가입한 유저"
-recentlyDiscoveredUsers: "최근 발견한 유저"
-exploreUsersCount: "{count}ëª…ì˜ ìœ ì €ê°€ 있습니다"
+popularUsers: "ì¸ê¸° 사용ìž"
+recentlyUpdatedUsers: "ìµœê·¼ì— í™œë™í•œ 사용ìž"
+recentlyRegisteredUsers: "ìµœê·¼ì— ê°€ìž…í•œ 사용ìž"
+recentlyDiscoveredUsers: "ìµœê·¼ì— ë°œê²¬í•œ 사용ìž"
+exploreUsersCount: "{count}ëª…ì˜ ì‚¬ìš©ìžê°€ 있습니다"
exploreFediverse: "연합우주를 íƒìƒ‰"
popularTags: "ì¸ê¸° 태그"
userList: "리스트"
@@ -470,7 +471,7 @@ quoteQuestion: "ì¸ìš©í•´ì„œ 작성하시겠습니까?"
noMessagesYet: "ì•„ì§ ëŒ€í™”ê°€ 없습니다"
newMessageExists: "새 메시지가 있습니다"
onlyOneFileCanBeAttached: "ë©”ì‹œì§€ì— ì²¨ë¶€í•  수 있는 파ì¼ì€ 하나까지입니다"
-signinRequired: "ë¡œê·¸ì¸ í•´ì£¼ì„¸ìš”"
+signinRequired: "진행하기 ì „ì— ë¡œê·¸ì¸ì„ í•´ 주세요"
invitations: "초대"
invitationCode: "초대 코드"
checking: "확ì¸í•˜ëŠ” 중입니다"
@@ -494,6 +495,7 @@ emojiStyle: "ì´ëª¨ì§€ 스타ì¼"
native: "기본"
disableDrawer: "드로어 메뉴를 사용하지 않기"
showNoteActionsOnlyHover: "노트 ì•¡ì…˜ ë²„íŠ¼ì„ ë§ˆìš°ìŠ¤ë¥¼ ì˜¬ë ¸ì„ ë•Œì—ë§Œ 표시"
+showReactionsCount: "ë…¸íŠ¸ì˜ ë°˜ì‘ ìˆ˜ë¥¼ 표시하기"
noHistory: "기ë¡ì´ 없습니다"
signinHistory: "ë¡œê·¸ì¸ ê¸°ë¡"
enableAdvancedMfm: "고급 MFMì„ í™œì„±í™”"
@@ -529,13 +531,13 @@ useObjectStorage: "오브ì íЏ 스토리지를 사용"
objectStorageBaseUrl: "Base URL"
objectStorageBaseUrlDesc: "오브ì íЏ (미디어) 참조 URL ì„ ë§Œë“¤ 때 사용ë˜ëŠ” URL입니다. CDN ë˜ëŠ” 프ë¡ì‹œë¥¼ 사용하는 경우 ê·¸ URLì„ ì§€ì •í•˜ê³ , ê·¸ ì™¸ì˜ ê²½ìš° 사용할 ì„œë¹„ìŠ¤ì˜ ê°€ì´ë“œì— ë”°ë¼ ê³µê°œì ìœ¼ë¡œ 액세스 í•  수 있는 주소를 지정해 주세요. 예를 들어, AWS S3ì˜ ê²½ìš° 'https://<bucket>.s3.amazonaws.com', GCSë“±ì˜ ê²½ìš° 'https://storage.googleapis.com/<bucket>' 와 ê°™ì´ ì§€ì •í•©ë‹ˆë‹¤."
objectStorageBucket: "Bucket"
-objectStorageBucketDesc: "사용 ì„œë¹„ìŠ¤ì˜ bucketëª…ì„ ì§€ì •í•´ì£¼ì„¸ìš”."
+objectStorageBucketDesc: "사용하는 ì„œë¹„ìŠ¤ì˜ bucket ì´ë¦„ì„ ì§€ì •í•´ 주세요."
objectStoragePrefix: "Prefix"
objectStoragePrefixDesc: "ì´ Prefix ì˜ ë””ë ‰í† ë¦¬ ì•„ëž˜ì— íŒŒì¼ì´ 저장ë©ë‹ˆë‹¤."
objectStorageEndpoint: "Endpoint"
-objectStorageEndpointDesc: "AWS S3ì˜ ê²½ìš° 공란, 다른 ì„œë¹„ìŠ¤ì˜ ê²½ìš° ê° ì„œë¹„ìŠ¤ì˜ ê°€ì´ë“œì— 맞게 endpoint를 설정해주세요. '<host>' í˜¹ì€ '<host>:<port>' 와 ê°™ì´ ì§€ì •í•©ë‹ˆë‹¤."
+objectStorageEndpointDesc: "AWS S3는 비워 ë‘ê³  다른 서비스는 ê° ì„œë¹„ìŠ¤ì˜ endpoint를 설정해 주세요. ‘<host>’ í˜¹ì€ â€˜<host>:<port>’처럼 지정합니다."
objectStorageRegion: "Region"
-objectStorageRegionDesc: "'xx-east-1'와 ê°™ì´ regionì„ ì§€ì •í•´ 주세요. 사용하는 ì„œë¹„ìŠ¤ì— region ê°œë…ì´ ì—†ëŠ” 경우 'us-east-1'으로 설정해 주세요. AWS 설정 íŒŒì¼ ë˜ëŠ” 환경 변수를 참조할 경우ì—는 비워주세요."
+objectStorageRegionDesc: "‘xx-east-1’처럼 regionì„ ì§€ì •í•´ 주세요. 사용하는 ì„œë¹„ìŠ¤ì— region ê°œë…ì´ ì—†ìœ¼ë©´ ‘us-east-1’처럼 설정해 주세요. AWS 설정 파ì¼ì´ë‚˜ 환경 변수가 있으면 비워 주세요."
objectStorageUseSSL: "SSL 사용"
objectStorageUseSSLDesc: "API 호출시 HTTPS 를 사용하지 않는 경우 OFF 로 설정해 주세요"
objectStorageUseProxy: "ì—°ê²°ì— í”„ë¡ì‹œë¥¼ 사용"
@@ -577,7 +579,7 @@ scratchpadDescription: "스í¬ëž˜ì¹˜ 패드는 AiScript ì˜ í…ŒìŠ¤íŠ¸ 환경ì„
output: "출력"
script: "스í¬ë¦½íЏ"
disablePagesScript: "Pages ì—서 AiScript 를 사용하지 않ìŒ"
-updateRemoteUser: "리모트 유저 정보 갱신"
+updateRemoteUser: "ì›ê²© ì‚¬ìš©ìž ì •ë³´ 갱신"
unsetUserAvatar: "아바타 제거"
unsetUserAvatarConfirm: "아바타를 제거할까요?"
unsetUserBanner: "배너 제거"
@@ -660,7 +662,7 @@ regexpErrorDescription: "{tab}단어 뮤트 {line}í–‰ì˜ ì •ê·œ 표현ì‹ì— 오
instanceMute: "서버 뮤트"
userSaysSomething: "{name}ë‹˜ì´ ë¬´ì–¸ê°€ë¥¼ ë§í–ˆìŠµë‹ˆë‹¤"
makeActive: "활성화"
-display: "표시"
+display: "보기"
copy: "복사"
metrics: "통계"
overview: "요약"
@@ -684,7 +686,7 @@ sample: "예시"
abuseReports: "ì‹ ê³ "
reportAbuse: "ì‹ ê³ "
reportAbuseRenote: "리노트 신고하기"
-reportAbuseOf: "{name}ì„ ì‹ ê³ í•˜ê¸°"
+reportAbuseOf: "{name} 신고하기"
fillAbuseReportDescription: "신고하려는 ì´ìœ ë¥¼ ìžì„¸ížˆ 알려주세요. 특정 ê²Œì‹œë¬¼ì„ ì‹ ê³ í•  때ì—는 ê²Œì‹œë¬¼ì˜ URLë„ í¬í•¨í•´ 주세요."
abuseReported: "신고를 보냈습니다. ì‹ ê³ í•´ 주셔서 ê°ì‚¬í•©ë‹ˆë‹¤."
reporter: "ì‹ ê³ ìž"
@@ -709,7 +711,7 @@ createNew: "새로 만들기"
optional: "옵션"
createNewClip: "새 í´ë¦½ 만들기"
unclip: "í´ë¦½ í•´ì œ"
-confirmToUnclipAlreadyClippedNote: "ì´ ë…¸íŠ¸ëŠ” ì´ë¯¸ \"{name}\" í´ë¦½ì— í¬í•¨ë˜ì–´ 있습니다. í´ë¦½ì„ 해제하시겠습니까?"
+confirmToUnclipAlreadyClippedNote: "ì´ ë…¸íŠ¸ëŠ” ‘{name}’ í´ë¦½ì„ ì´ë¯¸ í¬í•¨í•©ë‹ˆë‹¤. í´ë¦½ì—서 제외하시겠습니까?"
public: "공개"
private: "비공개"
i18nInfo: "Misskey는 ìžì›ë´‰ì‚¬ìžë“¤ì— ì˜í•´ 다양한 언어로 번역ë˜ê³  있습니다. {link}ì—서 ë²ˆì—­ì— ì°¸ê°€í•  수 있습니다."
@@ -722,13 +724,13 @@ repliedCount: "ë°›ì€ ë‹µê¸€ 수"
renotedCount: "ë°›ì€ ë¦¬ë…¸íŠ¸ 수"
followingCount: "팔로우 수"
followersCount: "팔로워 수"
-sentReactionsCount: "보낸 리액션 수"
-receivedReactionsCount: "ë°›ì€ ë¦¬ì•¡ì…˜ 수"
-pollVotesCount: "투표한 횟수"
-pollVotedCount: "íˆ¬í‘œë°›ì€ íšŸìˆ˜"
+sentReactionsCount: "ë°˜ì‘ ìˆ˜"
+receivedReactionsCount: "ë°›ì€ ë°˜ì‘ ìˆ˜"
+pollVotesCount: "투표 수"
+pollVotedCount: "ë°›ì€ íˆ¬í‘œ 수"
yes: "예"
no: "아니오"
-driveFilesCount: "드ë¼ì´ë¸Œ íŒŒì¼ ê°œìˆ˜"
+driveFilesCount: "드ë¼ì´ë¸Œì— 있는 íŒŒì¼ ìˆ˜"
driveUsage: "드ë¼ì´ë¸Œ 사용량"
noCrawle: "ê²€ìƒ‰ì—”ì§„ì˜ ì¸ë±ì‹± ê±°ë¶€"
noCrawleDescription: "ê²€ìƒ‰ì—”ì§„ì— ì‚¬ìš©ìž íŽ˜ì´ì§€, 노트, 페ì´ì§€ ë“±ì˜ ì½˜í…츠를 ì¸ë±ì‹±ë˜ì§€ 않게 합니다."
@@ -763,7 +765,7 @@ needReloadToApply: "변경 ì‚¬í•­ì€ ìƒˆë¡œê³ ì¹¨í•˜ë©´ ì ìš©ë©ë‹ˆë‹¤."
showTitlebar: "타ì´í‹€ 바를 표시하기"
clearCache: "ìºì‹œ 비우기"
onlineUsersCount: "{n}ëª…ì´ ì ‘ì† ì¤‘"
-nUsers: "{n} 유저"
+nUsers: "{n} 사용ìž"
nNotes: "{n} 노트"
sendErrorReports: "오류 보고서 보내기"
sendErrorReportsDescription: "ì´ ì„¤ì •ì„ í™œì„±í™”í•˜ë©´, 문제가 ë°œìƒí–ˆì„ 때 ì˜¤ë¥˜ì— ëŒ€í•œ ìƒì„¸ 정보를 Misskeyì— ë³´ë‚´ì–´ ë” ë‚˜ì€ ì†Œí”„íŠ¸ì›¨ì–´ë¥¼ 만드는 ë°ì— ë„ì›€ì„ ì¤„ 수 있습니다."
@@ -809,7 +811,7 @@ addDescription: "설명 추가"
userPagePinTip: "ê° ë…¸íŠ¸ì˜ ë©”ë‰´ì—서 ã€Œí”„ë¡œí•„ì— ê³ ì •ã€ì„ ì„ íƒí•˜ëŠ” 것으로, ì—¬ê¸°ì— ë…¸íŠ¸ë¥¼ 표시해 둘 수 있어요."
notSpecifiedMentionWarning: "수신ìžê°€ ì„ íƒë˜ì§€ ì•Šì€ ë©˜ì…˜ì´ ìžˆì–´ìš”"
info: "ì •ë³´"
-userInfo: "유저 정보"
+userInfo: "ì‚¬ìš©ìž ì •ë³´"
unknown: "알 수 ì—†ìŒ"
onlineStatus: "온ë¼ì¸ ìƒíƒœ"
hideOnlineStatus: "온ë¼ì¸ ìƒíƒœ 숨기기"
@@ -897,7 +899,7 @@ incorrectPassword: "비밀번호가 올바르지 않습니다."
voteConfirm: "\"{choice}\"ì— íˆ¬í‘œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
hide: "숨기기"
useDrawerReactionPickerForMobile: "모바ì¼ì—서 드로어 메뉴로 표시"
-welcomeBackWithName: "환ì˜í•©ë‹ˆë‹¤, {name}님"
+welcomeBackWithName: "{name}님, 환ì˜í•©ë‹ˆë‹¤."
clickToFinishEmailVerification: "[{ok}]를 눌러 ì´ë©”ì¼ ì¸ì¦ì„ 완료하세요."
overridedDeviceKind: "장치 유형"
smartphone: "스마트í°"
@@ -1092,9 +1094,9 @@ preservedUsernames: "ì˜ˆì•½ëœ ì‚¬ìš©ìžëª…"
preservedUsernamesDescription: "예약할 사용ìžëª…ì„ í•œ ì¤„ì— í•˜ë‚˜ì”© 입력합니다. 여기ì—서 지정한 사용ìžëª…으로는 ê³„ì •ì„ ìƒì„±í•  수 없게 ë©ë‹ˆë‹¤. 단, ê´€ë¦¬ìž ê¶Œí•œìœ¼ë¡œ ê³„ì •ì„ ìƒì„±í•  때ì—는 해당ë˜ì§€ 않으며, ì´ë¯¸ 존재하는 ê³„ì •ë„ ì˜í–¥ì„ 받지 않습니다."
createNoteFromTheFile: "ì´ íŒŒì¼ë¡œ 노트를 작성"
archive: "ì•„ì¹´ì´ë¸Œ"
-channelArchiveConfirmTitle: "{name} ì„(를) ì•„ì¹´ì´ë¸Œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
-channelArchiveConfirmDescription: "ì•„ì¹´ì´ë¸Œí•œ 채ë„ì€ ì±„ë„ ëª©ë¡ê³¼ 검색 ê²°ê³¼ì— í‘œì‹œë˜ì§€ 않으며, 채ë„ì— ìƒˆë¡œìš´ 노트를 작성할 수 없게 ë©ë‹ˆë‹¤."
-thisChannelArchived: "ì´ ì±„ë„ì€ ì•„ì¹´ì´ë¸Œë˜ì—ˆìŠµë‹ˆë‹¤."
+channelArchiveConfirmTitle: "{name} 채ë„ì„ ë³´ì¡´í•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
+channelArchiveConfirmDescription: "보존한 채ë„ì€ ì±„ë„ ëª©ë¡ê³¼ 검색 ê²°ê³¼ì— í‘œì‹œë˜ì§€ 않으며 새로운 ë…¸íŠ¸ë„ ìž‘ì„±í•  수 없습니다."
+thisChannelArchived: "ì´ ì±„ë„ì€ ë³´ì¡´ë˜ì—ˆìŠµë‹ˆë‹¤."
displayOfNote: "노트 표시"
initialAccountSetting: "초기 설정"
youFollowing: "팔로잉"
@@ -1156,10 +1158,11 @@ unnotifyNotes: "새 노트 알림 ë„기"
authentication: "ì¸ì¦"
authenticationRequiredToContinue: "계ì†í•˜ë ¤ë©´ ì¸ì¦í•˜ì‹­ì‹œì˜¤"
dateAndTime: "ì¼ì‹œ"
-showRenotes: "리노트 표시"
+showRenotes: "리노트 보기"
edited: "수정ë¨"
notificationRecieveConfig: "알림 설정"
mutualFollow: "맞팔로우"
+followingOrFollower: "팔로 중ì´ê±°ë‚˜ 팔로워"
fileAttachedOnly: "미디어를 í¬í•¨í•œ 노트만"
showRepliesToOthersInTimeline: "타임ë¼ì¸ì— 다른 사람ì—게 보내는 ë‹µê¸€ì„ í¬í•¨"
hideRepliesToOthersInTimeline: "타임ë¼ì¸ì— 다른 사람ì—게 보내는 ë‹µê¸€ì„ í¬í•¨í•˜ì§€ 않ìŒ"
@@ -1210,16 +1213,38 @@ soundWillBePlayed: "소리가 재ìƒë©ë‹ˆë‹¤"
showReplay: "ë¦¬í”Œë ˆì´ ë³´ê¸°"
replay: "리플레ì´"
replaying: "ë¦¬í”Œë ˆì´ ì¤‘"
+endReplay: "ë¦¬í”Œë ˆì´ ì¢…ë£Œ"
+copyReplayData: "ë¦¬í”Œë ˆì´ ë°ì´í„°ë¥¼ 복사"
ranking: "랭킹"
lastNDays: "최근 {n}ì¼"
backToTitle: "타ì´í‹€ë¡œ 가기"
hemisphere: "거주 지역"
withSensitive: "민ê°í•œ 파ì¼ì´ í¬í•¨ëœ 노트 보기"
-userSaysSomethingSensitive: "{name}ì˜ ë¯¼ê°í•œ 파ì¼ì´ í¬í•¨ëœ 게시물"
+userSaysSomethingSensitive: "{name} ê°™ì€ ë¯¼ê°í•œ 파ì¼ì´ í¬í•¨ëœ 글"
enableHorizontalSwipe: "스와ì´í”„하여 탭 전환"
+loading: "불러오는 중"
surrender: "그만ë‘기"
+gameRetry: "다시 시ë„"
+notUsePleaseLeaveBlank: "사용하지 않는 경우 비워ë‘세요."
+useTotp: "ì¼íšŒìš© 비밀번호 사용"
+useBackupCode: "백업 코드 사용"
+launchApp: "앱 실행"
+useNativeUIForVideoAudioPlayer: "브ë¼ìš°ì € UIì—서 미디어 재ìƒ"
+_delivery:
+ stop: "ì •ì§€ë¨"
+ _type:
+ none: "ë°°í¬ ì¤‘"
_bubbleGame:
howToPlay: "설명"
+ hold: "홀드"
+ _score:
+ score: "ì ìˆ˜"
+ scoreYen: "번 ëˆ"
+ highScore: "최고 ì ìˆ˜"
+ maxChain: "최대 콤보 수"
+ yen: "{yen}ì—”"
+ estimatedQty: "{qty}개"
+ scoreSweets: "오니기리 {onigiriQtyWithUnit}"
_howToPlay:
section1: "위치를 조정하여 ìƒìžì— ë¬¼ê±´ì„ ë–¨ì–´ëœ¨ë¦½ë‹ˆë‹¤."
section2: "ê°™ì€ ì¢…ë¥˜ì˜ ë¬¼ê±´ì´ ë¶™ìœ¼ë©´ 다른 물건으로 바뀌면서 ì ìˆ˜ë¥¼ 얻게 ë©ë‹ˆë‹¤."
@@ -1232,7 +1257,7 @@ _announcement:
end: "공지ì—서 내리기"
tooManyActiveAnnouncementDescription: "ê³µì§€ì‚¬í•­ì´ ë„ˆë¬´ ë§Žì„ ê²½ìš°, ì‚¬ìš©ìž ê²½í—˜ì— ì˜í–¥ì„ ë¼ì¹  ê°€ëŠ¥ì„±ì´ ìžˆìŠµë‹ˆë‹¤. ì˜¤ëž˜ëœ ê³µì§€ì‚¬í•­ì€ ì•„ì¹´ì´ë¸Œí•˜ì‹œëŠ” ê²ƒì„ ê¶Œìž¥ë“œë¦½ë‹ˆë‹¤."
readConfirmTitle: "ì½ìŒìœ¼ë¡œ 표시합니까?"
- readConfirmText: "\"{title}\"ì„(를) ì½ìŒìœ¼ë¡œ 표시합니다."
+ readConfirmText: "〈{title}ã€‰ì˜ ë‚´ìš©ì„ ì½ìŒìœ¼ë¡œ 표시합니다."
shouldNotBeUsedToPresentPermanentInfo: "ì‹ ê·œ ìœ ì €ì˜ ì´ìš© ê²½í—˜ì— ì•…ì˜í–¥ì„ ë¼ì¹  수 있으므로, ì¼ì‹œì ì¸ 알림 수단으로만 사용하고 ê³ ì •ëœ ì •ë³´ì—는 ì‚¬ìš©ì„ ì§€ì–‘í•˜ëŠ” ê²ƒì„ ì¶”ì²œí•©ë‹ˆë‹¤."
dialogAnnouncementUxWarn: "다ì´ì–¼ë¡œê·¸ í˜•íƒœì˜ ì•Œë¦¼ì´ ë™ì‹œì— 2ê°œ ì´ìƒ 존재하는 경우, ì‚¬ìš©ìž ê²½í—˜ì— ì•…ì˜í–¥ì„ ë¼ì¹  수 있으므로 신중히 결정하십시오."
silence: "조용히 알림"
@@ -1513,7 +1538,7 @@ _achievements:
_iLoveMisskey:
title: "I Love Misskey"
description: "\"I ⤠#Misskey\"를 í¬ìŠ¤íŠ¸í–ˆìŠµë‹ˆë‹¤"
- flavor: "Misskey를 ì´ìš©í•´ì£¼ì…”서 ê°ì‚¬í•©ë‹ˆë‹¤! - 개발팀 ì¼ë™"
+ flavor: "Misskey를 ì´ìš©í•´ 주셔서 ê°ì‚¬í•©ë‹ˆë‹¤! ― 개발 팀"
_foundTreasure:
title: "보물찾기"
description: "숨겨진 ë³´ë¬¼ì„ ë°œê²¬í–ˆìŠµë‹ˆë‹¤"
@@ -1551,10 +1576,10 @@ _achievements:
description: "3ê°œ ì´ìƒì˜ ì°½ì„ ì—´ì—ˆìŠµë‹ˆë‹¤"
_driveFolderCircularReference:
title: "순환 참조"
- description: "드ë¼ì´ë¸Œ í´ë”를 ìžì‹ ì„ 가리키ë„ë¡ ë§Œë“œë ¤ 시ë„했습니다"
+ description: "드ë¼ì´ë¸Œ í´ë”ì— ìŠ¤ìŠ¤ë¡œë¥¼ 넣게 했습니다"
_reactWithoutRead:
title: "ì½ê³  답하긴 하시는 건가요?"
- description: "100ìžê°€ 넘는 노트가 작성ë˜ê³  3ì´ˆ ì•ˆì— ë°˜ì‘했습니다"
+ description: "100ìžê°€ 넘는 노트를 작성한 ì§€ 3ì´ˆ ì•ˆì— ë°˜ì‘했어요"
_clickedClickHere:
title: "여기를 누르세요"
description: "여기를 눌렀습니다"
@@ -1641,6 +1666,7 @@ _role:
gtlAvailable: "글로벌 타임ë¼ì¸ ë³´ì´ê¸°"
ltlAvailable: "로컬 타임ë¼ì¸ ë³´ì´ê¸°"
canPublicNote: "공개 노트 허용"
+ mentionMax: "ë…¸íŠ¸ì— ë„£ì„ ìˆ˜ 있는 멘션 수"
canInvite: "서버 초대 코드 발행"
inviteLimit: "초대 한ë„"
inviteLimitCycle: "초대 발급 간격"
@@ -1650,13 +1676,13 @@ _role:
driveCapacity: "드ë¼ì´ë¸Œ 용량"
alwaysMarkNsfw: "파ì¼ì„ í•­ìƒ NSFW로 지정"
pinMax: "고정할 수 있는 노트 수"
- antennaMax: "최대 안테나 ìƒì„± 허용 수"
+ antennaMax: "만들 수 있는 안테나 수"
wordMuteMax: "단어 뮤트할 수 있는 ë¬¸ìž ìˆ˜"
- webhookMax: "ìƒì„±í•  수 있는 웹훅 수"
- clipMax: "ìƒì„±í•  수 있는 í´ë¦½ 수"
- noteEachClipsMax: "ê° í´ë¦½ì— 추가할 수 있는 노트 수"
- userListMax: "ìƒì„±í•  수 있는 유저 리스트 수"
- userEachUserListsMax: "유저 리스트당 최대 ì‚¬ìš©ìž ìˆ˜"
+ webhookMax: "만들 수 있는 ì›¹í›„í¬ ìˆ˜"
+ clipMax: "만들 수 있는 í´ë¦½ 수"
+ noteEachClipsMax: "í´ë¦½ì— ë„£ì„ ìˆ˜ 있는 노트 수"
+ userListMax: "만들 수 있는 ì‚¬ìš©ìž ë¦¬ìŠ¤íŠ¸ 수"
+ userEachUserListsMax: "ì‚¬ìš©ìž ë¦¬ìŠ¤íŠ¸ì— ë„£ì„ ìˆ˜ 있는 ì‚¬ìš©ìž ìˆ˜"
rateLimitFactor: "요청 ë¹ˆë„ ì œí•œ"
descriptionOfRateLimitFactor: "ìž‘ì„ìˆ˜ë¡ ì œí•œì´ ì™„í™”ë˜ê³ , í´ìˆ˜ë¡ ì œí•œì´ ê°•í™”ë©ë‹ˆë‹¤."
canHideAds: "광고 숨기기"
@@ -1664,16 +1690,17 @@ _role:
canUseTranslator: "번역 ê¸°ëŠ¥ì˜ ì‚¬ìš©"
avatarDecorationLimit: "아바타 장ì‹ì˜ 최대 붙임 개수"
_condition:
+ roleAssignedTo: "ìˆ˜ë™ ì—­í• ì— ì´ë¯¸ 할당ë¨"
isLocal: "로컬 사용ìž"
isRemote: "리모트 사용ìž"
createdLessThan: "가입한 ì§€ ë‹¤ìŒ ì¼ìˆ˜ ì´ë‚´ì¸ 유저"
createdMoreThan: "가입한 ì§€ ë‹¤ìŒ ì¼ìˆ˜ ì´ìƒì¸ 유저"
followersLessThanOrEq: "팔로워 수가 ë‹¤ìŒ ì´í•˜ì¸ 유저"
- followersMoreThanOrEq: "팔로워 수가 ë‹¤ìŒ ì´ìƒì¸ 유저"
+ followersMoreThanOrEq: "팔로워 수가 다ìŒë³´ë‹¤ ë§Žì€ ì‚¬ìš©ìž"
followingLessThanOrEq: "팔로잉 수가 ë‹¤ìŒ ì´í•˜ì¸ 유저"
- followingMoreThanOrEq: "팔로잉 수가 ë‹¤ìŒ ì´ìƒì¸ 유저"
+ followingMoreThanOrEq: "팔로잉 수가 다ìŒë³´ë‹¤ ë§Žì€ ì‚¬ìš©ìž"
notesLessThanOrEq: "노트 수가 ë‹¤ìŒ ì´í•˜ì¸ 유저"
- notesMoreThanOrEq: "노트 수가 ë‹¤ìŒ ì´ìƒì¸ 유저"
+ notesMoreThanOrEq: "노트 수가 다ìŒë³´ë‹¤ ë§Žì€ ì‚¬ìš©ìž"
and: "다ìŒì„ ëª¨ë‘ ë§Œì¡±"
or: "다ìŒì„ 하나ë¼ë„ 만족"
not: "다ìŒì„ 만족하지 않ìŒ"
@@ -1735,6 +1762,7 @@ _plugin:
installWarn: "신뢰할 수 없는 플러그ì¸ì€ 설치하지 않는 ê²ƒì´ ì¢‹ìŠµë‹ˆë‹¤."
manage: "í”ŒëŸ¬ê·¸ì¸ ê´€ë¦¬"
viewSource: "소스 보기"
+ viewLog: "로그 보기"
_preferencesBackups:
list: "ìƒì„±í•œ 백업"
saveNew: "새 백업 만들기"
@@ -1745,12 +1773,12 @@ _preferencesBackups:
cannotSave: "저장하지 못했습니다"
nameAlreadyExists: "\"{name}\" ë°±ì—…ì´ ì´ë¯¸ 존재합니다. 다른 ì´ë¦„ì„ ì„¤ì •í•˜ì—¬ 주십시오."
applyConfirm: "\"{name}\" ë°±ì—…ì„ í˜„ìž¬ ê¸°ê¸°ì— ì ìš©í•˜ì‹œê² ìŠµë‹ˆê¹Œ? 현재 ì„¤ì •ì€ ë®ì–´ 씌워집니다."
- saveConfirm: "{name} ì„ ë®ì–´ì“°ì‹œê² ìŠµë‹ˆê¹Œ?"
- deleteConfirm: "{name} ì„(를) 삭제하시겠습니까?"
- renameConfirm: "\"{old}\" ë°±ì—…ì„ \"{new}\"(으)로 바꾸시겠습니까?"
+ saveConfirm: "{name} ë°±ì—…ì„ ë®ì–´ì“°ì‹œê² ìŠµë‹ˆê¹Œ?"
+ deleteConfirm: "{name} ë°±ì—…ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
+ renameConfirm: "‘{old}’ ë°±ì—…ì„ â€˜{new}’ 백업으로 바꾸시겠습니까?"
noBackups: "ì €ìž¥ëœ ë°±ì—…ì´ ì—†ìŠµë‹ˆë‹¤. \"새 백업 만들기\"를 눌러 현재 í´ë¼ì´ì–¸íЏ ì„¤ì •ì„ ì„œë²„ì— ë°±ì—…í•  수 있습니다."
- createdAt: "ìƒì„± ë‚ ì§œ: {date} {time}"
- updatedAt: "갱신 날짜: {date} {time}"
+ createdAt: "만든 날짜: {date} {time}"
+ updatedAt: "고친 날짜: {date} {time}"
cannotLoad: "ê°€ì ¸ì˜¤ê¸°ì— ì‹¤íŒ¨í–ˆìŠµë‹ˆë‹¤"
invalidFile: "íŒŒì¼ í˜•ì‹ì´ 올바르지 않습니다."
_registry:
@@ -1924,7 +1952,6 @@ _2fa:
registerTOTP: "ì¸ì¦ 앱 설정 시작"
step1: "먼저, {a}나 {b}ë“±ì˜ ì¸ì¦ ì•±ì„ ì‚¬ìš© ì¤‘ì¸ ë””ë°”ì´ìŠ¤ì— ì„¤ì¹˜í•©ë‹ˆë‹¤."
step2: "ê·¸ 후, 표시ë˜ì–´ 있는 QR코드를 앱으로 스캔합니다."
- step2Click: "QR 코드를 í´ë¦­í•˜ë©´ ê¸°ê¸°ì— ì„¤ì¹˜ëœ ì¸ì¦ ì•±ì— ë“±ë¡í•  수 있습니다."
step2Uri: "ë°ìФí¬í†± ì•±ì„ ì‚¬ìš©í•˜ë ¤ë©´ ë‹¤ìŒ URI를 입력하십시오"
step3Title: "ì¸ì¦ 코드 ìž…ë ¥"
step3: "ì•±ì— í‘œì‹œëœ í† í°ì„ 입력하시면 완료ë©ë‹ˆë‹¤."
@@ -1937,7 +1964,7 @@ _2fa:
securityKeyName: "키 ì´ë¦„ ìž…ë ¥"
tapSecurityKey: "브ë¼ìš°ì €ì˜ ì§€ì‹œì— ë”°ë¼ ë³´ì•ˆ 키 ë˜ëŠ” 패스키를 등ë¡í•˜ì—¬ 주십시오"
removeKey: "보안 키를 삭제"
- removeKeyConfirm: "{name} ì„(를) 삭제하시겠습니까?"
+ removeKeyConfirm: "{name} ì•±ì„ ì‚­ì œí•˜ì‹œê² ìŠµë‹ˆê¹Œ?"
whyTOTPOnlyRenew: "보안 키가 등ë¡ë˜ì–´ 있는 경우 ì¸ì¦ ì•±ì„ í•´ì œí•  수 없습니다."
renewTOTP: "ì¸ì¦ 앱 재설정"
renewTOTPConfirm: "ê¸°ì¡´ì— ë“±ë¡ë˜ì–´ ìžˆë˜ ì¸ì¦ 키는 사용하지 못하게 ë©ë‹ˆë‹¤."
@@ -1998,7 +2025,6 @@ _permissions:
"read:admin:server-info": "서버 정보 보기"
"read:admin:show-moderation-log": "ì¡°ì • ê¸°ë¡ ë³´ê¸°"
"read:admin:show-user": "ì‚¬ìš©ìž ê°œì¸ì •ë³´ 보기"
- "read:admin:show-users": "ì‚¬ìš©ìž ê°œì¸ì •ë³´ 보기"
"write:admin:suspend-user": "ì‚¬ìš©ìž ì •ì§€í•˜ê¸°"
"write:admin:unset-user-avatar": "ì‚¬ìš©ìž ì•„ë°”íƒ€ 삭제하기"
"write:admin:unset-user-banner": "ì‚¬ìš©ìž ë°°ë„ˆ 삭제하기"
@@ -2078,7 +2104,7 @@ _widgets:
postForm: "글 입력란"
slideshow: "슬ë¼ì´ë“œ 쇼"
button: "버튼"
- onlineUsers: "온ë¼ì¸ 유저"
+ onlineUsers: "온ë¼ì¸ 사용ìž"
jobQueue: "작업 대기열"
serverMetric: "서버 통계"
aiscript: "AiScript 콘솔"
@@ -2137,10 +2163,10 @@ _postForm:
c: "ë¬´ì—‡ì„ ìƒê°í•˜ê³  있나요?"
d: "ë§í•˜ê³  ì‹¶ì€ ê²Œ 있나요?"
e: "ì—¬ê¸°ì— ì ì–´ 주세요"
- f: "작성해주시길 기다리고 있어요..."
+ f: "글 쓰기를 기다려요…"
_profile:
name: "ì´ë¦„"
- username: "유저명"
+ username: "ì‚¬ìš©ìž ì´ë¦„"
description: "ìžê¸°ì†Œê°œ"
youCanIncludeHashtags: "해시 태그를 í¬í•¨í•  수 있습니다."
metadata: "추가 정보"
@@ -2168,7 +2194,7 @@ _charts:
apRequest: "요청"
usersIncDec: "유저 수 ì¦ê°"
usersTotal: "유저 수 합계"
- activeUsers: "활성 유저 수"
+ activeUsers: "í™œë™ ì‚¬ìš©ìž ìˆ˜"
notesIncDec: "노트 수 ì¦ê°"
localNotesIncDec: "로컬 노트 수 ì¦ê°"
remoteNotesIncDec: "리모트 노트 수 ì¦ê°"
@@ -2179,8 +2205,8 @@ _charts:
storageUsageTotal: "스토리지 사용량 합계"
_instanceCharts:
requests: "요청"
- users: "유저 수 ì¦ê°"
- usersTotal: "ëˆ„ì  ìœ ì € 수"
+ users: "ì‚¬ìš©ìž ìˆ˜ ì°¨ì´"
+ usersTotal: "ëˆ„ì  ì‚¬ìš©ìž ìˆ˜"
notes: "노트 수 ì¦ê°"
notesTotal: "ëˆ„ì  ë…¸íŠ¸ 수"
ff: "팔로잉/팔로워 ì¦ê°"
@@ -2209,6 +2235,7 @@ _play:
title: "제목"
script: "스í¬ë¦½íЏ"
summary: "설명"
+ visibilityDescription: "비공개로 설정하면 í”„ë¡œí•„ì— í‘œì‹œí•˜ì§€ 않지만 URLì„ ì•„ëŠ” ì‚¬ëžŒì€ ê³„ì†í•´ì„œ ì ‘ì†í•  수 있습니다."
_pages:
newPage: "페ì´ì§€ 만들기"
editPage: "페ì´ì§€ 수정"
@@ -2219,7 +2246,7 @@ _pages:
pageSetting: "페ì´ì§€ 설정"
nameAlreadyExists: "지정한 페ì´ì§€ URLì´ ì´ë¯¸ 존재합니다"
invalidNameTitle: "유효하지 ì•Šì€ íŽ˜ì´ì§€ URL입니다"
- invalidNameText: "비어있지 않ì€ì§€ 확ì¸í•´ì£¼ì„¸ìš”"
+ invalidNameText: "비어있는지 확ì¸í•´ 주세요"
editThisPage: "ì´ íŽ˜ì´ì§€ë¥¼ 편집"
viewSource: "소스 보기"
viewPage: "페ì´ì§€ 보기"
@@ -2253,6 +2280,8 @@ _pages:
section: "섹션"
image: "ì´ë¯¸ì§€"
button: "버튼"
+ dynamic: "ë™ì  블ë¡"
+ dynamicDescription: "ì´ ë¸”ë¡ì€ íì§€ë˜ì—ˆìŠµë‹ˆë‹¤. ì´ì œë¶€í„° {play}ì—서 ì´ìš©í•´ 주세요."
note: "노트필기"
_note:
id: "노트 ID"
@@ -2282,17 +2311,19 @@ _notification:
sendTestNotification: "테스트 알림 보내기"
notificationWillBeDisplayedLikeThis: "ì•Œë¦¼ì´ ì´ë ‡ê²Œ 표시ë©ë‹ˆë‹¤"
reactedBySomeUsers: "{n}ëª…ì´ ë°˜ì‘했습니다"
+ likedBySomeUsers: "{n}ëª…ì´ ì¢‹ì•„ìš”ë¥¼ 했습니다"
renotedBySomeUsers: "{n}ëª…ì´ ë¦¬ë…¸íŠ¸í–ˆìŠµë‹ˆë‹¤"
followedBySomeUsers: "{n}명ì—게 팔로우ë¨"
+ flushNotification: "알림 ì´ë ¥ì„ 초기화"
_types:
all: "ì „ë¶€"
- note: "ìœ ì €ì˜ ìƒˆ 게시물"
+ note: "사용ìžì˜ 새 글"
follow: "팔로잉"
mention: "멘션"
reply: "답글"
renote: "리노트"
quote: "ì¸ìš©"
- reaction: "리액션"
+ reaction: "ë°˜ì‘"
pollEnded: "투표가 종료ë¨"
receiveFollowRequest: "팔로우 ìš”ì²­ì„ ë°›ì•˜ì„ ë•Œ"
followRequestAccepted: "팔로우 ìš”ì²­ì´ ìŠ¹ì¸ë˜ì—ˆì„ 때"
@@ -2335,7 +2366,7 @@ _deck:
direct: "다ì´ë ‰íЏ"
roleTimeline: "ì—­í•  타임ë¼ì¸"
_dialog:
- charactersExceeded: "최대 글ìžìˆ˜ë¥¼ 초과하였습니다! 현재 {current} / 최대 {min}"
+ charactersExceeded: "최대 글ìžìˆ˜ë¥¼ 초과하였습니다! 현재 {current} / 최대 {max}"
charactersBelow: "최소 글ìžìˆ˜ 미만입니다! 현재 {current} / 최소 {min}"
_disabledTimeline:
title: "ë¹„í™œì„±í™”ëœ íƒ€ìž„ë¼ì¸"
@@ -2459,7 +2490,7 @@ _dataSaver:
_hemisphere:
N: "ë¶ë°˜êµ¬"
S: "남반구"
- caption: "ì¼ë¶€ í´ë¼ì´ì–¸íЏ 설정ì—서 ê³„ì ˆì„ íŒë‹¨í•˜ê¸° 위해 사용합니다."
+ caption: "ì¼ë¶€ í´ë¼ì´ì–¸íЏ 설정ì—서 ê³„ì ˆì„ íŒë‹¨í•˜ë ¤ê³  사용합니다."
_reversi:
reversi: "리버시"
gameSettings: "대국 설정"
@@ -2467,41 +2498,61 @@ _reversi:
blackOrWhite: "선공/후공"
blackIs: "{name}ë‹˜ì´ í‘(ì„ ê³µ)"
rules: "규칙"
- thisGameIsStartedSoon: "ëŒ€êµ­ì´ ê³§ 시작ë©ë‹ˆë‹¤"
- waitingForOther: "ìƒëŒ€ë°©ì˜ 준비가 완료ë˜ê¸°ë¥¼ 기다리고 있습니다."
- waitingForMe: "ë‹¹ì‹ ì˜ ì¤€ë¹„ê°€ 완료ë˜ê¸°ë¥¼ 기다리고 있습니다."
+ thisGameIsStartedSoon: "ëŒ€êµ­ì„ ê³§ 시작합니다"
+ waitingForOther: "ìƒëŒ€ì˜ 준비가 ë나기를 기다리고 있습니다."
+ waitingForMe: "ë‚˜ì˜ ì¤€ë¹„ê°€ ë나기를 기다리고 있습니다."
waitingBoth: "준비하세요"
ready: "준비 완료"
- cancelReady: "준비 다시 시작"
+ cancelReady: "준비ë˜ì§€ 않ìŒ"
opponentTurn: "ìƒëŒ€ì˜ 차례입니다"
- myTurn: "ë‹¹ì‹ ì˜ ì°¨ë¡€ìž…ë‹ˆë‹¤"
- turnOf: "{name}ì˜ ì°¨ë¡€ìž…ë‹ˆë‹¤"
- pastTurnOf: "{name}ì˜ ì°¨ë¡€"
+ myTurn: "ë‚˜ì˜ ì°¨ë¡€ìž…ë‹ˆë‹¤"
+ turnOf: "{name}ë‹˜ì˜ ì°¨ë¡€ìž…ë‹ˆë‹¤"
+ pastTurnOf: "{name}ë‹˜ì˜ ì°¨ë¡€"
surrender: "기권"
- surrendered: "ê¸°ê¶Œì— ì˜í•´"
+ surrendered: "ìƒëŒ€ì˜ 기권"
timeout: "시간 초과"
drawn: "무승부"
- won: "{name}ì˜ ìŠ¹ë¦¬"
+ won: "{name}ë‹˜ì˜ ìŠ¹ë¦¬"
black: "í‘"
white: "ë°±"
total: "합계"
- turnCount: "{count}턴 째"
+ turnCount: "{count}번째 수"
myGames: "내 대국"
- allGames: "모ë‘ì˜ ëŒ€êµ­"
+ allGames: "모든 대국"
ended: "종료"
playing: "대국 중"
- isLlotheo: "ëŒì´ ì ì€ ì‚¬ëžŒì´ ìŠ¹ë¦¬ (로세오)"
- loopedMap: "루프 ì§€ë„"
- canPutEverywhere: "ì–´ë””ì—ë„ ë‘˜ 수 있는 모드"
- timeLimitForEachTurn: "1í„´ì˜ ì‹œê°„ 제한"
- freeMatch: "프리매치"
- lookingForPlayer: "ìƒëŒ€ë¥¼ 찾고 있습니다"
+ isLlotheo: "ëŒì´ ì ì€ ìª½ì´ ìŠ¹ë¦¬(로세오)"
+ loopedMap: "순환 ì§€ë„"
+ canPutEverywhere: "어디든 둘 수 있는 모드"
+ timeLimitForEachTurn: "ê° ìˆ˜ì˜ ì‹œê°„ 제한"
+ freeMatch: "ìžìœ  대국"
+ lookingForPlayer: "대국 ìƒëŒ€ë¥¼ 찾고 있습니다"
gameCanceled: "ëŒ€êµ­ì´ ì·¨ì†Œë˜ì—ˆìŠµë‹ˆë‹¤"
- shareToTlTheGameWhenStart: "대국 시작 시 타임ë¼ì¸ì— ëŒ€êµ­ì„ ê²Œì‹œ"
- iStartedAGame: "ëŒ€êµ­ì´ ì‹œìž‘ë˜ì—ˆìŠµë‹ˆë‹¤! #MisskeyReversi"
- opponentHasSettingsChanged: "ìƒëŒ€ë°©ì´ ì„¤ì •ì„ ë³€ê²½í–ˆìŠµë‹ˆë‹¤"
- allowIrregularRules: "규칙변경 허가 (완전 ìžìœ )"
- disallowIrregularRules: "규칙변경 ì—†ìŒ"
+ shareToTlTheGameWhenStart: "ëŒ€êµ­ì´ ì‹œìž‘í•  때 타임ë¼ì¸ì— 공유"
+ iStartedAGame: "ëŒ€êµ­ì„ ì‹œìž‘í•˜ì˜€ìŠµë‹ˆë‹¤! #MisskeyReversi"
+ opponentHasSettingsChanged: "ìƒëŒ€ê°€ ì„¤ì •ì„ ë³€ê²½í–ˆìŠµë‹ˆë‹¤"
+ allowIrregularRules: "규칙 변경 허용(완전 ìžìœ )"
+ disallowIrregularRules: "규칙 변경 ì—†ìŒ"
+ showBoardLabels: "íŒì— 행·열 번호 표시"
+ useAvatarAsStone: "ëŒì„ ì•„ì´ì½˜ìœ¼ë¡œ 표시"
_offlineScreen:
title: "오프ë¼ì¸ - ì„œë²„ì— ì ‘ì†í•  수 없습니다"
header: "ì„œë²„ì— ì ‘ì†í•  수 없습니다"
+_urlPreviewSetting:
+ title: "URL 미리보기 설정"
+ enable: "URL 미리보기 활성화"
+ timeout: "미리보기를 불러올 ë•Œì˜ íƒ€ìž„ì•„ì›ƒ (ms)"
+ timeoutDescription: "미리보기를 ë¡œë”©í•˜ëŠ”ë° ê±¸ë¦¬ëŠ” ì‹œê°„ì´ ì •í•œ 시간보다 오래 걸리는 경우, 미리보기를 ìƒì„±í•˜ì§€ 않습니다."
+ maximumContentLength: "Content-Lengthì˜ ìµœëŒ€ì¹˜ (byte)"
+ maximumContentLengthDescription: "Content-Lengthê°€ ì´ ê°’ì„ ë„˜ì–´ì„œë©´ 미리보기를 ìƒì„±í•˜ì§€ 않습니다."
+ requireContentLength: "Content-Length를 ì–»ì—ˆì„ ë•Œë§Œ 미리보기 만들기"
+ requireContentLengthDescription: "ìƒëŒ€ 서버가 Content-Length를 ë˜ëŒë ¤ì£¼ì§€ 않는다면 미리보기를 만들지 않습니다."
+ userAgent: "User-Agent"
+ userAgentDescription: "미리보기를 ì–»ì„ ë•Œ 사용한 User-Agent를 설정합니다. 비어 있다면 ê¸°ë³¸ê°’ì˜ User-Agent를 사용합니다."
+ summaryProxy: "미리보기를 만든 프ë¡ì‹œì˜ 엔드í¬ì¸íЏ"
+ summaryProxyDescription: "Misskey 본체를 사용하지 않고 서머리 프ë¡ì‹œë¡œ 미리보기를 만듭니다."
+ summaryProxyDescription2: "프ë¡ì‹œëŠ” ì•„ëž˜ì˜ íŒŒë¼ë¯¸í„°ë¥¼ 쿼리 문ìžì—´ë¡œ ì—°ë™í•©ë‹ˆë‹¤. 프ë¡ì‹œ ì¸¡ì´ ì´ë¥¼ ì§€ì›í•˜ì§€ 않으면 ì„¤ì •ê°’ì„ ë¬´ì‹œí•©ë‹ˆë‹¤."
+_mediaControls:
+ pip: "화면 ì† í™”ë©´"
+ playbackRate: "ìž¬ìƒ ì†ë„"
+ loop: "반복 재ìƒ"
diff --git a/locales/lo-LA.yml b/locales/lo-LA.yml
index 6f03c914fd..087bac3745 100644
--- a/locales/lo-LA.yml
+++ b/locales/lo-LA.yml
@@ -395,6 +395,10 @@ searchByGoogle: "ຄົ້ນຫາ"
file: "ໄຟລ໌"
replies: "ຕອບ​ໄປ​ທີ"
renotes: "Renote"
+_delivery:
+ stop: "ໂຈະ"
+ _type:
+ none: "àºàº²àº™â€‹àºžàº´àº¡â€‹à»€àºœàºµàºâ€‹à»àºœà»ˆ"
_role:
_priority:
middle: "ປານàºàº²àº‡"
@@ -466,4 +470,3 @@ _webhookSettings:
name: "ຊື່"
_moderationLogTypes:
suspend: "ລະງັບ"
-
diff --git a/locales/nl-NL.yml b/locales/nl-NL.yml
index 42fbf183be..2154e248af 100644
--- a/locales/nl-NL.yml
+++ b/locales/nl-NL.yml
@@ -429,6 +429,10 @@ loggedInAsBot: "Momenteel als bot ingelogd"
icon: "Avatar"
replies: "Antwoorden"
renotes: "Herdelen"
+_delivery:
+ stop: "Opgeschort"
+ _type:
+ none: "Publiceren"
_email:
_follow:
title: "volgde jou"
@@ -497,4 +501,3 @@ _webhookSettings:
_moderationLogTypes:
suspend: "Opschorten"
resetPassword: "Wachtwoord terugzetten"
-
diff --git a/locales/no-NO.yml b/locales/no-NO.yml
index 098faa8add..2b4c9b7776 100644
--- a/locales/no-NO.yml
+++ b/locales/no-NO.yml
@@ -464,6 +464,8 @@ icon: "Avatar"
replies: "Svar"
renotes: "Renote"
surrender: "Avbryt"
+_delivery:
+ stop: "Suspendert"
_initialAccountSetting:
theseSettingsCanEditLater: "Du kan endre disse innstillingene senere."
_achievements:
@@ -721,4 +723,3 @@ _webhookSettings:
name: "Navn"
_moderationLogTypes:
suspend: "Suspender"
-
diff --git a/locales/pl-PL.yml b/locales/pl-PL.yml
index b7566aaa46..4acd6af991 100644
--- a/locales/pl-PL.yml
+++ b/locales/pl-PL.yml
@@ -20,6 +20,7 @@ noNotes: "Brak wpisów"
noNotifications: "Brak powiadomień"
instance: "Instancja"
settings: "Ustawienia"
+notificationSettings: "Powiadomienia"
basicSettings: "Podstawowe ustawienia"
otherSettings: "Pozostałe ustawienia"
openInWindow: "Otwórz w oknie"
@@ -44,13 +45,20 @@ pin: "Przypnij do profilu"
unpin: "Odepnij z profilu"
copyContent: "Skopiuj zawartość"
copyLink: "Skopiuj odnośnik"
+copyLinkRenote: "Skopiuj link renote'a"
delete: "Usuń"
deleteAndEdit: "Usuń i edytuj"
deleteAndEditConfirm: "Czy na pewno chcesz usunąć ten wpis i zedytować go? Utracisz wszystkie reakcje, udostępnienia i odpowiedzi do tego wpisu."
addToList: "Dodaj do listy"
+addToAntenna: "Dodaj do anteny"
sendMessage: "Wyślij wiadomość"
copyRSS: "Kopiuj RSS"
copyUsername: "Kopiuj nazwę użytkownika"
+copyUserId: "Kopiuj ID użytkownika"
+copyNoteId: "Kopiuj ID notatki"
+copyFileId: "Kopiuj ID pliku"
+copyFolderId: "Kopiuj ID folderu"
+copyProfileUrl: "Kopiuj URL profilu"
searchUser: "Wyszukiwanie użytkowników"
reply: "Odpowiedz"
loadMore: "Załaduj więcej"
@@ -103,6 +111,8 @@ renoted: "Udostępniono."
cantRenote: "Ten wpis nie może zostać udostępniony."
cantReRenote: "Udostępnienie nie może zostać udostępnione."
quote: "Cytuj"
+inChannelRenote: "Renote tylko na kanale"
+inChannelQuote: "Cytat tylko na kanale"
pinnedNote: "Przypięty wpis"
pinned: "Przypnij do profilu"
you: "Ty"
@@ -111,14 +121,23 @@ sensitive: "NSFW"
add: "Dodaj"
reaction: "Reakcja"
reactions: "Reakcja"
+emojiPicker: "Selektor Emoji"
+pinnedEmojisForReactionSettingDescription: "Ustaw emotikony które powinny być przypięte i od razu wyświetlone podczas reagowania."
+pinnedEmojisSettingDescription: "Ustaw emotikony które powinny być przypięte i wyświetlone podczas przeglądania selektora Emoji"
+emojiPickerDisplay: "Wyświetlanie selektora Emoji"
+overwriteFromPinnedEmojisForReaction: "Zastąp z ustawień reakcji"
+overwriteFromPinnedEmojis: "Zastąp z ogólnych ustawień"
reactionSettingDescription2: "Przeciągnij aby zmienić kolejność, naciśnij aby usunąć, naciśnij „+†aby dodać"
rememberNoteVisibility: "Zapamiętuj ustawienia widoczności wpisu"
attachCancel: "Usuń załącznik"
+deleteFile: "Usuń plik"
markAsSensitive: "Oznacz jako NSFW"
unmarkAsSensitive: "Cofnij NSFW"
enterFileName: "Wprowadź nazwę pliku"
mute: "Wycisz"
unmute: "Cofnij wyciszenie"
+renoteMute: "Wycisz renote'y"
+renoteUnmute: "Wyłącz wyciszenie renote'ów"
block: "Zablokuj"
unblock: "Odblokuj"
suspend: "ZawieÅ›"
@@ -128,8 +147,10 @@ unblockConfirm: "Czy na pewno chcesz odblokować to konto?"
suspendConfirm: "Czy na pewno chcesz zawiesić to konto?"
unsuspendConfirm: "Czy na pewno chcesz cofnąć zawieszenie tego konta?"
selectList: "Wybierz listÄ™"
+editList: "Edytuj listÄ™"
selectChannel: "Wybierz kanał"
selectAntenna: "Wybierz AntennÄ™"
+editAntenna: "Edytuj antenÄ™"
selectWidget: "Wybierz widżet"
editWidgets: "Edytuj widżety"
editWidgetsExit: "Gotowe"
@@ -142,11 +163,15 @@ addEmoji: "Dodaj emoji"
settingGuide: "Proponowana konfiguracja"
cacheRemoteFiles: "Przechowuj zdalne pliki w pamięci podręcznej"
cacheRemoteFilesDescription: "Gdy ta opcja jest wyłączona, zdalne pliki są ładowane bezpośrednio ze zdalnych instancji. Wyłączenie the opcji zmniejszy użycie powierzchni dyskowej, ale zwiększy transfer, ponieważ miniaturki nie będą generowane."
+youCanCleanRemoteFilesCache: "Możesz wyczyÅ›cić cache poprzez klikniÄ™cie przycisku ðŸ—‘ï¸ w widoku menedżera plików."
+cacheRemoteSensitiveFiles: "Przechowuj wrażliwe zdalne pliki w pamięci podręcznej"
+cacheRemoteSensitiveFilesDescription: "Gdy ta opcja jest wyłączona, wrażliwe pliki zdalne są wczytywane bezpośrednio ze zdalnej instancji bez cacheowania."
flagAsBot: "To konto jest botem"
flagAsBotDescription: "Jeżeli ten kanał jest kontrolowany przez jakiś program, ustaw tę opcję. Jeżeli włączona, będzie działać jako flaga informująca innych programistów, aby zapobiegać nieskończonej interakcji z różnymi botami i dostosowywać wewnętrzne systemy Misskey, traktując konto jako bota."
flagAsCat: "To konto jest kotem"
flagAsCatDescription: "Przełącz tę opcję, aby konto było oznaczone jako kot."
flagShowTimelineReplies: "Pokazuj odpowiedzi na osi czasu"
+flagShowTimelineRepliesDescription: "Gdy włączone, pokazuje odpowiedzi użytkowników na notatki innych użytkowników w osi czasu."
autoAcceptFollowed: "Automatycznie przyjmuj prośby o możliwość obserwacji od użytkowników, których obserwujesz"
addAccount: "Dodaj konto"
reloadAccountsList: "Odśwież listę kont"
@@ -176,6 +201,7 @@ perHour: "co godzinÄ™"
perDay: "co dzień"
stopActivityDelivery: "Przestań przesyłać aktywności"
blockThisInstance: "Zablokuj tÄ™ instancjÄ™"
+silenceThisInstance: "Wycisz tÄ™ instancjÄ™"
operations: "Działania"
software: "Oprogramowanie"
version: "Wersja"
@@ -195,6 +221,8 @@ clearCachedFiles: "Wyczyść pamięć podręczną"
clearCachedFilesConfirm: "Czy na pewno chcesz usunąć wszystkie zdalne pliki z pamięci podręcznej?"
blockedInstances: "Zablokowane instancje"
blockedInstancesDescription: "Wypisz nazwy hostów instancji, które powinny zostać zablokowane. Wypisane instancje nie będą mogły dłużej komunikować się z tą instancją."
+silencedInstances: "Wyciszone instancje"
+silencedInstancesDescription: "Wypisz nazwy hostów instancji, które chcesz wyciszyć. Wszystkie konta wymienionych instancji będą traktowane jako wyciszone, będą mogły jedynie wysyłać prośby o obserwację i nie będą mogły wspominać kont lokalnych, jeśli nie będą obserwowane. Nie będzie to miało wpływu na zablokowane instancje."
muteAndBlock: "Wycisz / Zablokuj"
mutedUsers: "Wyciszeni użytkownicy"
blockedUsers: "Zablokowani użytkownicy"
@@ -239,10 +267,12 @@ removed: "Pomyślnie usunięto"
removeAreYouSure: "Czy na pewno chcesz usunąć „{x}�"
deleteAreYouSure: "Czy na pewno chcesz usunąć „{x}�"
resetAreYouSure: "Czy na pewno chcesz zresetować?"
+areYouSure: "Na pewno?"
saved: "Zapisano"
messaging: "Wiadomości"
upload: "Wyślij"
keepOriginalUploading: "Zachowaj oryginalny obraz"
+keepOriginalUploadingDescription: "Zapisuje oryginalnie przesłany obraz w niezmienionej postaci. Jeśli ta opcja jest wyłączona, po przesłaniu zostanie wygenerowana wersja do wyświetlenia w Internecie."
fromDrive: "Z dysku"
fromUrl: "Z adresu URL"
uploadFromUrl: "Wyślij z adresu URL"
@@ -255,7 +285,10 @@ noMoreHistory: "Nie ma dalszej historii"
startMessaging: "Rozpocznij czat"
nUsersRead: "przeczytano przez {n}"
agreeTo: "Wyrażam zgodę na {0}"
+agree: "Zatwierdź"
agreeBelow: "Zaakceptuj poniżej"
+basicNotesBeforeCreateAccount: "Ważne notatki"
+termsOfService: "Warunki usługi"
start: "Rozpocznij"
home: "Strona główna"
remoteUserCaution: "Te informacje mogą nie być aktualne, ponieważ użytkownik pochodzi ze zdalnej instancji."
@@ -285,6 +318,7 @@ folderName: "Nazwa katalogu"
createFolder: "Utwórz katalog"
renameFolder: "Zmień nazwę katalogu"
deleteFolder: "Usuń ten katalog"
+folder: "Folder"
addFile: "Dodaj plik"
emptyDrive: "Dysk jest pusty"
emptyFolder: "Ten katalog jest pusty"
@@ -298,6 +332,7 @@ copyUrl: "Skopiuj adres URL"
rename: "Zmień nazwę"
avatar: "Awatar"
banner: "Baner"
+displayOfSensitiveMedia: "Wyświetlanie wrażliwej zawartości"
whenServerDisconnected: "Po utracie połączenia z serwerem"
disconnectedFromServer: "Utracono połączenie z serwerem."
reload: "Odśwież"
@@ -345,8 +380,11 @@ hcaptcha: "hCaptcha"
enableHcaptcha: "Włącz hCaptcha"
hcaptchaSiteKey: "Klucz strony"
hcaptchaSecretKey: "Tajny klucz"
+mcaptcha: "mCaptcha"
+enableMcaptcha: "Włącz mCaptcha"
mcaptchaSiteKey: "Klucz strony"
mcaptchaSecretKey: "Tajny klucz"
+mcaptchaInstanceUrl: "URL instancji mCaptcha"
recaptcha: "reCAPTCHA"
enableRecaptcha: "Włącz reCAPTCHA"
recaptchaSiteKey: "Klucz strony"
@@ -389,15 +427,19 @@ aboutMisskey: "O Misskey"
administrator: "Admin"
token: "Token"
2fa: "Klucz 2FA "
+setupOf2fa: "Skonfiguruj dwuetapowÄ… autentykacjÄ™"
totp: "Klucz aplikacji uwierzytelniajÄ…cej (totp)"
totpDescription: "Opis klucza czasowego"
moderator: "Moderator"
moderation: "Moderacja"
+moderationNote: "Notka moderacyjna"
+addModerationNote: "Dodaj notkÄ™ moderacyjnÄ…"
+moderationLogs: "Logi moderacyjne"
nUsersMentioned: "{n} wspomnianych użytkowników"
securityKeyAndPasskey: "Klucz bezpieczeństwa i klucze Passkey"
securityKey: "Klucz bezpieczeństwa"
lastUsed: "Ostatnio używane"
-lastUsedAt: "Ostatnio używane w"
+lastUsedAt: "Ostatnio używane: {t}"
unregister: "Cofnij rejestracjÄ™"
passwordLessLogin: "Skonfiguruj logowanie bez użycia hasła"
passwordLessLoginDescription: "Opis logowania bez użycia hasła"
@@ -451,8 +493,12 @@ aboutX: "O {x}"
emojiStyle: "Styl emoji"
native: "Natywny"
disableDrawer: "Nie używaj menu w stylu szuflady"
+showNoteActionsOnlyHover: "Pokazuj akcje notatek tylko po najechaniu myszkÄ…"
+showReactionsCount: "Wyświetl liczbę reakcji na notatkę"
noHistory: "Brak historii"
signinHistory: "Historia logowania"
+enableAdvancedMfm: "Włącz zaawansowane MFM"
+enableAnimatedMfm: "Włącz animowane MFM"
doing: "Przetwarzanie..."
category: "Kategoria"
tags: "Tagi"
@@ -461,6 +507,8 @@ createAccount: "Utwórz konto"
existingAccount: "IstniejÄ…ce konto"
regenerate: "Wygeneruj ponownie"
fontSize: "Rozmiar czcionki"
+mediaListWithOneImageAppearance: "Wysokość list multimediów z tylko jednym obrazem"
+limitTo: "Limituj do {x}"
noFollowRequests: "Nie masz żadnych oczekujących próśb o możliwość obserwacji"
openImageInNewTab: "Otwórz obraz w nowej karcie"
dashboard: "Kokpit"
@@ -480,6 +528,7 @@ showFeaturedNotesInTimeline: "Pokazuj wyróżnione wpisy w osi czasu"
objectStorage: "Pamięć obiektowa"
useObjectStorage: "Używaj pamięci obiektowej"
objectStorageBaseUrl: "Podstawowy URL"
+objectStorageBaseUrlDesc: "Adres URL używany jako odniesienie. Podaj adres URL swojego CDN lub Proxy, gdy używasz któregokolwiek z nich.\nDla S3 użyj 'https://<bucket>.s3.amazonaws.com' a dla GCS lub równej usługi użyj 'https://storage.googleapis.com/<bucket>', itd."
objectStorageBucket: "Bucket"
objectStorageBucketDesc: "Podaj nazwę „wiadra†używaną przez konfigurowaną usługę."
objectStoragePrefix: "Prefiks"
@@ -492,9 +541,13 @@ objectStorageUseSSL: "Użyj SSL"
objectStorageUseSSLDesc: "Wyłącz, jeżeli nie zamierzasz używać HTTPS dla połączenia z API"
objectStorageUseProxy: "Połącz przez proxy"
objectStorageUseProxyDesc: "Wyłącz, jeżeli nie zamierzasz używać proxy dla połączenia z pamięcią blokową"
+objectStorageSetPublicRead: "Ustaw opcję \"public-read\" przy przesyłaniu"
+s3ForcePathStyleDesc: "Jeśli opcja s3ForcePathStyle jest włączona, nazwa Bucket'u musi być zawarta w ścieżce adresu URL, a nie w nazwie hosta adresu URL. Włączenie tego ustawienia może być konieczne w przypadku użycia usług takich jak self-hosted instancja Minio."
serverLogs: "Dziennik zdarzeń"
deleteAll: "Usuń wszystkie"
showFixedPostForm: "Wyświetlaj formularz tworzenia wpisu w górnej części osi czasu"
+showFixedPostFormInChannel: "Wyświetl formularz postowania w górnej części osi czasu (Kanały)"
+withRepliesByDefaultForNewlyFollowed: "Domyślnie uwzględnij odpowiedzi nowo obserwowanych użytkowników w osi czasu"
newNoteRecived: "Masz nowy wpis"
sounds: "Dźwięk"
sound: "Dźwięki"
@@ -504,6 +557,8 @@ showInPage: "Pokaż na stronie"
popout: "Popout"
volume: "Głośność"
masterVolume: "Głośność główna"
+notUseSound: "Wyłącz dźwięk"
+useSoundOnlyWhenActive: "Puszczaj dźwięki tylko, gdy Misskey jest aktywne."
details: "Szczegóły"
chooseEmoji: "Wybierz emoji"
unableToProcess: "Nie udało się dokończyć działania."
@@ -524,6 +579,10 @@ output: "Wyjście"
script: "Skrypt"
disablePagesScript: "Wyłącz AiScript na Stronach"
updateRemoteUser: "Aktualizuj zdalne dane o użytkowniku"
+unsetUserAvatar: "Usuń awatar"
+unsetUserAvatarConfirm: "Czy na pewno chcesz usunąć awatar tego użytkownika?"
+unsetUserBanner: "Usuń baner"
+unsetUserBannerConfirm: "Czy na pewno chcesz usunąć baner?"
deleteAllFiles: "Usuń wszystkie pliki"
deleteAllFilesConfirm: "Czy na pewno chcesz usunąć wszystkie pliki?"
removeAllFollowing: "Przestań obserwować"
@@ -539,6 +598,7 @@ accountDeletedDescription: "Opis konta usuniętego"
menu: "Menu"
divider: "Rozdzielacz"
addItem: "Dodaj element"
+rearrange: "Posortuj"
relays: "Przekaźniki"
addRelay: "Dodaj przekaźnik"
inboxUrl: "Adres URL skrzynki nadawczej"
@@ -573,6 +633,7 @@ medium: "Åšrednie"
small: "Małe"
generateAccessToken: "Generuj token dostępu"
permission: "Uprawnienia"
+adminPermission: "Uprawnienia administracyjne"
enableAll: "Włącz wszystko"
disableAll: "Wyłącz wszystko"
tokenRequested: "Przydziel dostęp do konta"
@@ -590,9 +651,12 @@ smtpPort: "Port"
smtpUser: "Nazwa użytkownika"
smtpPass: "Hasło"
emptyToDisableSmtpAuth: "Pozostaw adres e-mail i hasło puste, aby wyłączyć weryfikację SMTP"
+smtpSecure: "Użyj niejawnego SSL/TLS dla połączeń SMTP"
smtpSecureInfo: "Wyłącz, jeżeli używasz STARTTLS"
testEmail: "Przetestuj dostarczanie wiadomości e-mail"
wordMute: "Wyciszenie słowa"
+regexpError: "Błąd wyrażenia regularnego"
+regexpErrorDescription: "Wystąpił błąd w wyrażeniu regularnym w linii {line} twoich {tab} wyciszeń:"
instanceMute: "Wyciszone instancje"
userSaysSomething: "{name} powiedział(-a) coś"
makeActive: "Aktywuj"
@@ -612,18 +676,22 @@ useGlobalSettingDesc: "Jeżeli włączone, zostaną wykorzystane ustawienia powi
other: "Inne"
regenerateLoginToken: "Generuj token logowania ponownie"
regenerateLoginTokenDescription: "Regeneruje token używany wewnętrznie podczas logowania. Zazwyczaj nie jest to konieczne. Po regeneracji wszystkie urządzenia zostaną wylogowane."
+theKeywordWhenSearchingForCustomEmoji: "To jest słowo kluczowe używane podczas wyszukiwania customowych Emoji."
setMultipleBySeparatingWithSpace: "Możesz ustawić wiele, oddzielając je spacjami."
fileIdOrUrl: "ID pliku albo URL"
behavior: "Zachowanie"
sample: "Przykład"
abuseReports: "Zgłoszenia"
reportAbuse: "Zgłoś"
+reportAbuseRenote: "Zgłoś renote"
reportAbuseOf: "Zgłoś {name}"
fillAbuseReportDescription: "Wypełnij szczegóły zgłoszenia. Jeżeli dotyczy ono określonego wpisu, uwzględnij jego adres URL."
abuseReported: "Twoje zgłoszenie zostało wysłane. Dziękujemy."
+reporter: "Zgłaszający"
reporteeOrigin: "Pochodzenie zgłoszonego"
reporterOrigin: "Pochodzenie zgłaszającego"
forwardReport: "Przekaż zgłoszenie do innej instancji"
+forwardReportIsAnonymous: "Zamiast twojego konta, anonimowe konto systemowe będzie wyświetlone jako zgłaszający na instancji zdalnej."
send: "Wyślij"
abuseMarkAsResolved: "Oznacz zgłoszenie jako rozwiązane"
openInNewTab: "Otwórz w nowej karcie"
@@ -668,6 +736,7 @@ lockedAccountInfo: "Dopóki nie ustawisz widoczności wpisu na \"Obserwujący\",
alwaysMarkSensitive: "Oznacz domyślnie jako NSFW"
loadRawImages: "Wyświetlaj zdjęcia w załącznikach w całości zamiast miniatur"
disableShowingAnimatedImages: "Nie odtwarzaj animowanych obrazów"
+highlightSensitiveMedia: "Podkreśl wrażliwą zawartość"
verificationEmailSent: "Wiadomość weryfikacyjna została wysłana. Odwiedź uwzględniony odnośnik, aby ukończyć weryfikację."
notSet: "Nie ustawiono"
emailVerified: "Adres e-mail został potwierdzony"
@@ -678,6 +747,8 @@ contact: "Kontakt"
useSystemFont: "Używaj domyślnej czcionki systemu"
clips: "Klipy"
experimentalFeatures: "Eksperymentalne funkcje"
+experimental: "Eksperymentalne"
+thisIsExperimentalFeature: "Ta funkcja jest eksperymentalna. Jej funkcjonalność może ulec zmianie, i może ona nie funkcjonować tak, jak zamierzono."
developer: "Programista"
makeExplorable: "Pokazuj konto na stronie „Eksplorujâ€"
makeExplorableDescription: "Jeżeli wyłączysz tÄ™ opcjÄ™, Twoje konto nie bÄ™dzie wyÅ›wietlać siÄ™ w sekcji „Eksplorujâ€."
@@ -695,12 +766,14 @@ onlineUsersCount: "{n} osób jest online"
nUsers: "{n} użytkowników"
nNotes: "{n} wpisów"
sendErrorReports: "Wyślij raporty o błędach"
+sendErrorReportsDescription: "Gdy włączone, jeśli wystąpi problem, szczegółowe informacje o błędach będą udostępniane Misskey, pomagając ulepszyć jakość Misskey.\nBędzie to zawierało informacje takie jak wersja twojego systemu operacyjnego, jakiej przeglądarki używasz, twoja aktywność w Misskey, itd."
myTheme: "Mój motyw"
backgroundColor: "Tło"
accentColor: "Akcent"
textColor: "Tekst"
saveAs: "Zapisz jako…"
advanced: "Zaawansowane"
+advancedSettings: "Zaawansowane ustawienia"
value: "Wartość"
createdAt: "Utworzono"
updatedAt: "Zaktualizowano"
@@ -760,12 +833,14 @@ noMaintainerInformationWarning: "Informacje o administratorze nie sÄ… skonfiguro
noBotProtectionWarning: "Zabezpieczenie przed botami nie jest skonfigurowane."
configure: "Skonfiguruj"
postToGallery: "Opublikuj w galerii"
+postToHashtag: "Postuj do tego hashtagu"
gallery: "Galeria"
recentPosts: "Ostatnie wpisy"
popularPosts: "Popularne wpisy"
shareWithNote: "Udostępnij z wpisem"
ads: "Reklamy"
expiration: "Ankieta kończy się"
+startingperiod: "PoczÄ…tek"
memo: "Notatki"
priority: "Priorytet"
high: "Wysoki"
@@ -792,13 +867,19 @@ translatedFrom: "Przetłumaczone z {x}"
accountDeletionInProgress: "Trwa usuwanie konta"
usernameInfo: "Nazwa, która identyfikuje Twoje konto spośród innych na tym serwerze. Możesz użyć alfabetu (a~z, A~Z), cyfr (0~9) lub podkreślników (_). Nazwy użytkownika nie mogą być później zmieniane."
aiChanMode: "Tryb Ai"
+devMode: "Tryb programisty"
keepCw: "Zostaw ostrzeżenia o zawartości"
pubSub: "Konta Pub/Sub"
+lastCommunication: "Ostatnia komunikacja"
resolved: "RozwiÄ…zane"
unresolved: "NierozwiÄ…zane"
breakFollow: "Usuń obserwującego"
+breakFollowConfirm: "Czy na pewno usunąć tego obserwującego?"
itsOn: "Włączone"
itsOff: "Wyłączone"
+on: "Włączone"
+off: "Wyłączone"
+emailRequiredForSignup: "Wymagaj adresu e-mail do rejestracji"
unread: "Nieodczytane"
filter: "Filtr"
controlPanel: "Panel sterowania"
@@ -808,6 +889,8 @@ makeReactionsPublicDescription: "To spowoduje, że lista wszystkich Twoich dotyc
classic: "Klasyczny"
muteThread: "Wycisz wÄ…tek"
unmuteThread: "Wyłącz wyciszenie wątku"
+followingVisibility: "Widoczność obserwacji"
+followersVisibility: "Widoczność obserwujących"
continueThread: "Pokaż kontynuację wątku"
deleteAccountConfirm: "Spowoduje to nieodwracalne usunięcie Twojego konta. Kontynuować?"
incorrectPassword: "Nieprawidłowe hasło."
@@ -820,9 +903,14 @@ overridedDeviceKind: "Typ urzÄ…dzenia"
smartphone: "Smartfon"
tablet: "Tablet"
auto: "Automatycznie"
+themeColor: "Motyw kolorystyczny"
size: "Rozmiar"
numberOfColumn: "Liczba kolumn"
searchByGoogle: "Szukaj"
+instanceDefaultLightTheme: "Domyślny motyw dla trybu jasnego"
+instanceDefaultDarkTheme: "Domyślny motyw dla trybu ciemnego"
+instanceDefaultThemeDescription: "Opis domyślnego motywu instancji"
+mutePeriod: "Okres wyciszenia"
period: "Ankieta kończy się"
indefinitely: "Nigdy"
tenMinutes: "10 minut"
@@ -831,29 +919,50 @@ oneDay: "1 dzień"
oneWeek: "1 tydzień"
oneMonth: "jeden miesiÄ…c"
failedToFetchAccountInformation: "Nie udało się uzyskać informacji o koncie"
+rateLimitExceeded: "Limit szybkości przekroczony"
+cropImage: "Przytnij obraz"
+cropImageAsk: "Czy chcesz przyciąć obrazek?"
+cropYes: "Tak, przytnij"
+cropNo: "Nie chce przycinać"
file: "Pliki"
+recentNHours: "W ciÄ…gu ostatnich {n} godzin"
+recentNDays: "W ciÄ…gu ostatnich {n} dni"
+noEmailServerWarning: "Serwer Email nie jest skonfigurowany"
recommended: "Zalecane"
check: "Zweryfikuj"
+driveCapOverrideLabel: "Zmień limit pojemności dysku użytkownika"
+requireAdminForView: "Aby to zobaczyć, musisz być administratorem"
+isSystemAccount: "To jest konto stworzone i zarzÄ…dzane przez system"
+typeToConfirm: "Wprowadź {x}, aby potwierdzić"
deleteAccount: "Usuń konto"
document: "Dokumentacja"
numberOfPageCache: "Ilość stron w cache"
+numberOfPageCacheDescription: "Zwiększenie tej liczby polepszy wygodę, ale spowoduje większe obciążenie jako użycie pamięci na urządzeniu użytkownika."
logoutConfirm: "Czy na pewno chcesz się wylogować?"
lastActiveDate: "Ostatnio użyte w"
statusbar: "Pasek stanu"
pleaseSelect: "Wybierz opcjÄ™"
reverse: "Odwróć"
colored: "Kolorowe"
+refreshInterval: "Okres aktualizacji"
label: "Etykieta"
type: "Typ"
speed: "Prędkość"
+slow: "Wolny"
+fast: "Szybki"
+sensitiveMediaDetection: "Detekcja wrażliwej zawartości"
localOnly: "Lokalne tylko"
+remoteOnly: "Tylko zdalne instancje"
failedToUpload: "Przesyłanie nie powiodło się"
cannotUploadBecauseInappropriate: "Nie można przesłać tego pliku, ponieważ jego części zostały wykryte jako potencjalnie nieodpowiednie."
cannotUploadBecauseNoFreeSpace: "Przesyłanie nie powiodło się z powodu braku miejsca na dysku."
+cannotUploadBecauseExceedsFileSizeLimit: "Nie można przesłać pliku, ponieważ wykracza on poza limit wielkości pliku."
beta: "Beta"
enableAutoSensitive: "Automatyczne oznaczanie NSFW"
enableAutoSensitiveDescription: "Umożliwia automatyczne wykrywanie i oznaczanie zawartości NSFW za pomocą uczenia maszynowego. Nawet jeśli ta opcja jest wyłączona, może być włączona w całej instancji."
+activeEmailValidationDescription: "Włącza bardziej restrykcyjną walidację adresów e-mail, co obejmuje sprawdzanie adresów jednorazowych i czy komunikacja z tym adresem jest możliwa. Gdy wyłączone, tylko format adresu e-mail jest sprawdzany."
navbar: "Pasek nawigacyjny"
+shuffle: "Mieszaj"
account: "Konta"
move: "PrzenieÅ›"
pushNotification: "Powiadomienia"
@@ -863,22 +972,74 @@ pushNotificationAlreadySubscribed: "Powiadomienia push są włączone"
pushNotificationNotSupported: "Przeglądarka lub instancja nie obsługuje powiadomień push"
sendPushNotificationReadMessage: "Usuń powiadomienia push po przeczytaniu powiadomień i wiadomości."
sendPushNotificationReadMessageCaption: "Chwilowo pojawi się powiadomienie \"{emptyPushNotificationMessage}\". Może wzrosnąć zużycie baterii urządzenia."
+windowMaximize: "Maksymalizuj"
+windowMinimize: "Minimalizuj"
+windowRestore: "Przywróć"
+caption: "Legenda"
loggedInAsBot: "JesteÅ› obecnie zalogowany/a jako bot"
+tools: "Narzędzia"
+cannotLoad: "Nie można wczytać"
+numberOfProfileView: "Wyświetlenia profilu"
like: "Polub"
+unlike: "Usuń polubienie"
+numberOfLikes: "Liczba polubień"
show: "Wyświetlanie"
+neverShow: "Nie pokazuj ponownie"
+remindMeLater: "Przypomnij później"
+didYouLikeMisskey: "Czy Misskey się tobie spodobało?"
+pleaseDonate: "{host} używa darmowego oprogramowania — Misskey. Bylibyśmy bardzo wdzięczni za datki, które pozwolą na kontynuację rozwoju Misskey!"
+correspondingSourceIsAvailable: "Odpowiedni kod źródłowy jest dostępny pod {anchor}."
+roles: "Role"
+role: "Rola"
+noRole: "Rola nie znaleziona"
+normalUser: "Normalny użytkownik"
+undefined: "Niezdefiniowane"
+assign: "Przydziel"
+unassign: "Cofnij przydzielenie"
color: "Kolor"
+manageCustomEmojis: "ZarzÄ…dzaj niestandardowymi Emoji"
+manageAvatarDecorations: "ZarzÄ…dzaj dekoracjami awatara"
+invalidParamError: "Błąd parametrów"
+permissionDeniedError: "Odrzucono operacje"
+permissionDeniedErrorDescription: "Konto nie posiada uprawnień"
+preset: "Konfiguracja"
+selectFromPresets: "Wybierz konfiguracje"
+achievements: "Osiągnięcia"
+thisPostMayBeAnnoyingCancel: "Odrzuć"
+internalServerError: "Wewnętrzny błąd serwera"
+internalServerErrorDescription: "Niespodziewany błąd po stronie serwera"
+copyErrorInfo: "Kopiuj informacje o błędzie"
+joinThisServer: "Dołącz do chaty"
+disableFederationOk: "Wyłącz federacje"
+invitationRequiredToRegister: "Ten serwer wymaga zaproszenia. Tylko osoby z zaproszeniem mogą się zarejestrować"
+emailNotSupported: "Wysyłanie wiadomości E-mail nie jest obsługiwane na tym serwerze"
+postToTheChannel: "Publikuj na kanale"
youFollowing: "Åšledzeni"
icon: "Awatar"
replies: "Odpowiedzi"
renotes: "Udostępnień"
sourceCode: "Kod źródłowy"
flip: "Odwróć"
+lastNDays: "W ciÄ…gu ostatnich {n} dni"
+surrender: "Odrzuć"
+gameRetry: "Spróbuj ponownie"
+_delivery:
+ stop: "Zawieszono"
+ _type:
+ none: "Publikowanie"
+_bubbleGame:
+ _score:
+ score: "Wynik"
_role:
+ assignTarget: "Przydziel"
priority: "Priorytet"
_priority:
low: "Niski"
middle: "Åšrednie"
high: "Wysoki"
+ _options:
+ canManageCustomEmojis: "ZarzÄ…dzaj niestandardowymi Emoji"
+ canManageAvatarDecorations: "ZarzÄ…dzaj dekoracjami awatara"
_sensitiveMediaDetection:
description: "Zmniejsza wysiłek związany z moderacją serwera dzięki automatycznemu rozpoznawaniu zawartości NSFW za pomocą uczenia maszynowego. To nieznacznie zwiększy obciążenie serwera."
setSensitiveFlagAutomatically: "Oznacz jako NSFW"
diff --git a/locales/pt-PT.yml b/locales/pt-PT.yml
index 26657e7b52..0bfd1f778b 100644
--- a/locales/pt-PT.yml
+++ b/locales/pt-PT.yml
@@ -733,9 +733,9 @@ reloadToApplySetting: "As configurações serão refletidas após recarregar a p
needReloadToApply: "É necessário recarregar a página para refletir as alterações."
showTitlebar: "Exibir barra de título"
clearCache: "Limpar o cache"
-onlineUsersCount: "Pessoas Online"
-nUsers: "Usuários"
-nNotes: "Notas"
+onlineUsersCount: "{n} Pessoas Online"
+nUsers: "{n} Usuários"
+nNotes: "{n} Notas"
sendErrorReports: "Enviar relatórios de erro"
sendErrorReportsDescription: "Ao ativar essa opção, informações detalhadas de erro serão compartilhadas com o Misskey em caso de problemas, o que pode ajudar a melhorar a qualidade do software. As informações de erro podem incluir a versão do sistema operacional, o tipo de navegador e o sua atividade no Misskey."
myTheme: "Meu tema"
@@ -767,7 +767,7 @@ emailNotification: "Notificações por e-mail"
publish: "Publicar"
inChannelSearch: "Pesquisar no canal"
useReactionPickerForContextMenu: "Clique com o botão direito do mouse para abrir o seletor de reações."
-typingUsers: "digitando"
+typingUsers: "{users} pessoas digitando"
jumpToSpecifiedDate: "Pular para uma data específica"
showingPastTimeline: "Visualizar linha de tempo anterior"
clear: "Limpar"
@@ -834,7 +834,7 @@ learnMore: "Saiba mais"
misskeyUpdated: "Misskey foi atualizado!"
whatIsNew: "Ver atualizações"
translate: "Traduzir"
-translatedFrom: "Traduzido de"
+translatedFrom: "Traduzido de {x}"
accountDeletionInProgress: "Encerramento de conta em andamento"
usernameInfo: "O nome para identificar exclusivamente a sua conta no servidor. Pode conter letras (az, AZ), números (0~9) e sublinhados (_). O nome de usuário não pode ser alterado posteriormente."
aiChanMode: "Modo AI-chan"
@@ -1012,6 +1012,10 @@ keepScreenOn: "Manter a tela do dispositivo sempre ligada"
flip: "Inversão"
lastNDays: "Últimos {n} dias"
surrender: "Cancelar"
+_delivery:
+ stop: "Suspenso"
+ _type:
+ none: "Publicando"
_initialAccountSetting:
followUsers: "Siga usuários que lhe interessam para criar a sua linha do tempo."
_serverSettings:
@@ -1301,8 +1305,8 @@ _preferencesBackups:
_channel:
featured: "Destaques"
following: "Seguindo"
- usersCount: "usuários ativos"
- notesCount: "notas"
+ usersCount: "{n} usuários ativos"
+ notesCount: "{n} notas"
nameAndDescription: "Nome e descrição"
_menuDisplay:
sideFull: "Exibir painel lateral inteiro"
@@ -1501,4 +1505,3 @@ _moderationLogTypes:
resetPassword: "Redefinir senha"
_reversi:
total: "Total"
-
diff --git a/locales/ro-RO.yml b/locales/ro-RO.yml
index e45b8b75ec..88424cbbfb 100644
--- a/locales/ro-RO.yml
+++ b/locales/ro-RO.yml
@@ -651,6 +651,10 @@ show: "Arată"
icon: "Avatar"
replies: "Răspunsuri"
renotes: "Re-notează"
+_delivery:
+ stop: "Suspendat"
+ _type:
+ none: "Publicare"
_role:
_priority:
middle: "Mediu"
@@ -729,4 +733,3 @@ _moderationLogTypes:
resetPassword: "Resetează parola"
_reversi:
total: "Total"
-
diff --git a/locales/ru-RU.yml b/locales/ru-RU.yml
index d666b69490..71f5cad601 100644
--- a/locales/ru-RU.yml
+++ b/locales/ru-RU.yml
@@ -17,7 +17,7 @@ noThankYou: "Ðет, ÑпаÑибо"
enterUsername: "Введите Ð¸Ð¼Ñ Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ"
renotedBy: "{user} делитÑÑ"
noNotes: "Ðет ни одной заметки"
-noNotifications: "Ðет ни одного уведомлениÑ"
+noNotifications: "Ðет уведомлений"
instance: "ИнÑтанÑ"
settings: "ÐаÑтройки"
notificationSettings: "ÐаÑтройки уведомлений"
@@ -129,6 +129,7 @@ overwriteFromPinnedEmojis: "Заменить на Ñмодзи из общего
reactionSettingDescription2: "РаÑÑтавлÑйте перетаÑкиванием, удалÑйте нажатием, добавлÑйте кнопкой «+»."
rememberNoteVisibility: "Запоминать видимоÑть заметок"
attachCancel: "Удалить вложение"
+deleteFile: "Удалить файл"
markAsSensitive: "Отметить как «не Ð´Ð»Ñ Ð²Ñех»"
unmarkAsSensitive: "СнÑть отметку «не Ð´Ð»Ñ Ð²Ñех»"
enterFileName: "Введите Ð¸Ð¼Ñ Ñ„Ð°Ð¹Ð»Ð°"
@@ -312,6 +313,7 @@ folderName: "Ð˜Ð¼Ñ Ð¿Ð°Ð¿ÐºÐ¸"
createFolder: "Создать папку"
renameFolder: "Переименовать папку"
deleteFolder: "Удалить папку"
+folder: "Папка"
addFile: "Добавить файл"
emptyDrive: "ДиÑк пуÑÑ‚"
emptyFolder: "Папка пуÑта"
@@ -373,6 +375,8 @@ hcaptcha: "hCaptcha"
enableHcaptcha: "Включить hCaptcha"
hcaptchaSiteKey: "Ключ Ñайта"
hcaptchaSecretKey: "Секретный ключ"
+mcaptcha: "mCaptcha"
+enableMcaptcha: "Включить mCaptcha"
mcaptchaSiteKey: "Ключ Ñайта"
mcaptchaSecretKey: "Секретный ключ"
recaptcha: "reCAPTCHA"
@@ -542,6 +546,8 @@ showInPage: "Показать Ñтраницу"
popout: "Развернуть"
volume: "ГромкоÑть"
masterVolume: "ОÑÐ½Ð¾Ð²Ð½Ð°Ñ Ñ€ÐµÐ³ÑƒÐ»Ð¸Ñ€Ð¾Ð²ÐºÐ° громкоÑти"
+notUseSound: "Выключить звук"
+useSoundOnlyWhenActive: "ИÑпользовать звук, когда Misskey активен."
details: "Подробнее"
chooseEmoji: "Выберите Ñмодзи"
unableToProcess: "Ðе удаётÑÑ Ð·Ð°Ð²ÐµÑ€ÑˆÐ¸Ñ‚ÑŒ операцию"
@@ -562,6 +568,10 @@ output: "Выходы"
script: "Скрипт"
disablePagesScript: "Отключить Ñкрипты на «Страницах»"
updateRemoteUser: "Обновить данные Ð¿Ð¾Ð»ÑŒÐ·Ð¾Ð²Ð°Ñ‚ÐµÐ»Ñ Ñ ÐµÐ³Ð¾ Ñервера"
+unsetUserAvatar: "Убрать аватар"
+unsetUserAvatarConfirm: "Вы точно хотите убрать аватар?"
+unsetUserBanner: "Убрать баннер"
+unsetUserBannerConfirm: "Вы точно хотите убрать баннер?"
deleteAllFiles: "Удалить вÑе файлы"
deleteAllFilesConfirm: "Ð’Ñ‹ хотите удалить вÑе файлы?"
removeAllFollowing: "Удалить вÑех подпиÑчиков"
@@ -612,6 +622,7 @@ medium: "Средне"
small: "Мелко"
generateAccessToken: "Создать токен доÑтупа"
permission: "РазрешениÑ"
+adminPermission: "ДоÑтуп админиÑтратора"
enableAll: "Включить вÑе"
disableAll: "Выключить вÑÑ‘"
tokenRequested: "Открыть доÑтуп к учётной запиÑи"
@@ -633,6 +644,7 @@ smtpSecure: "ИÑпользовать SSL/TLS Ð´Ð»Ñ SMTP-Ñоединений"
smtpSecureInfo: "Выключите при иÑпользовании STARTTLS."
testEmail: "Проверка доÑтавки Ñлектронной почты"
wordMute: "Скрытие Ñлов"
+hardWordMute: ""
regexpError: "Ошибка в регулÑрном выражении"
regexpErrorDescription: "Ð’ ÑпиÑке {tab} Ñкрытых Ñлов, в Ñтроке {line} обнаружена ÑинтакÑичеÑÐºÐ°Ñ Ð¾ÑˆÐ¸Ð±ÐºÐ°:"
instanceMute: "Глушение инÑтанÑов"
@@ -1084,8 +1096,13 @@ renotes: "РепоÑÑ‚"
loadReplies: "Показать ответы"
sourceCode: "ИÑходный код"
flip: "Переворот"
+code: "Код"
lastNDays: "ПоÑледние {n} Ñут"
surrender: "Этот поÑÑ‚ не может быть отменен."
+_delivery:
+ stop: "Заморожено"
+ _type:
+ none: "ПубликациÑ"
_initialAccountSetting:
accountCreated: "Ðккаунт уÑпешно Ñоздан!"
letsStartAccountSetup: "Давайте наÑтроим вашу учётную запиÑÑŒ."
@@ -1626,7 +1643,6 @@ _2fa:
registerTOTP: "Ðачните наÑтраивать приложение-аутентификатор"
step1: "Прежде вÑего, уÑтановите на уÑтройÑтво приложение Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸, например, {a} или {b}."
step2: "Далее отÑканируйте отображаемый QR-код при помощи приложениÑ."
- step2Click: "Ðажав на QR-код, вы можете зарегиÑтрироватьÑÑ Ñ Ð¿Ð¾Ð¼Ð¾Ñ‰ÑŒÑŽ Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð´Ð»Ñ Ð°ÑƒÑ‚ÐµÐ½Ñ‚Ð¸Ñ„Ð¸ÐºÐ°Ñ†Ð¸Ð¸ или брелка Ð´Ð»Ñ ÐºÐ»ÑŽÑ‡ÐµÐ¹, уÑтановленного на вашем уÑтройÑтве."
step3Title: "Введите проверочный код"
step3: "И наконец, введите код, который покажет приложение."
step4: "Теперь при каждом входе на Ñайт вам нужно будет вводить код из Ð¿Ñ€Ð¸Ð»Ð¾Ð¶ÐµÐ½Ð¸Ñ Ð°Ð½Ð°Ð»Ð¾Ð³Ð¸Ñ‡Ð½Ñ‹Ð¼ образом."
@@ -1975,4 +1991,3 @@ _moderationLogTypes:
resetPassword: "Ð¡Ð±Ñ€Ð¾Ñ Ð¿Ð°Ñ€Ð¾Ð»Ñ:"
_reversi:
total: "Ð’Ñего"
-
diff --git a/locales/si-LK.yml b/locales/si-LK.yml
index cd21505a47..e130d68ed8 100644
--- a/locales/si-LK.yml
+++ b/locales/si-LK.yml
@@ -1,2 +1,19 @@
---
-
+_lang_: "සිංහල"
+monthAndDay: "{month}-{day}"
+username: "පරිà·à·“ලක à¶±à·à¶¸à¶º"
+password: "මුරපදය"
+cancel: "අවලංගු කරන්න"
+instance: "සර්වර්"
+login: "පිවිසෙන්න"
+users: "පරිà·à·“ලක"
+note: "à¶±à·à¶§à·Š"
+notes: "à¶±à·à¶§à·Š"
+instances: "සර්වර්"
+smtpUser: "පරිà·à·“ලක à¶±à·à¶¸à¶º"
+smtpPass: "මුරපදය"
+user: "පරිà·à·“ලක"
+_sfx:
+ note: "à¶±à·à¶§à·Š"
+_profile:
+ username: "පරිà·à·“ලක à¶±à·à¶¸à¶º"
diff --git a/locales/sk-SK.yml b/locales/sk-SK.yml
index f280f91270..6ae90395ab 100644
--- a/locales/sk-SK.yml
+++ b/locales/sk-SK.yml
@@ -922,6 +922,10 @@ renotes: "Preposlať"
sourceCode: "Zdrojový kód"
flip: "Preklopiť"
lastNDays: "Posledných {n} dní"
+_delivery:
+ stop: "Zmrazené"
+ _type:
+ none: "Zverejňovanie"
_role:
priority: "Priorita"
_priority:
@@ -1448,4 +1452,3 @@ _moderationLogTypes:
resetPassword: "Resetovať heslo"
_reversi:
total: "Celkom"
-
diff --git a/locales/sv-SE.yml b/locales/sv-SE.yml
index 76b9bc90b7..11b2b36499 100644
--- a/locales/sv-SE.yml
+++ b/locales/sv-SE.yml
@@ -488,6 +488,10 @@ dataSaver: "Databesparing"
icon: "Profilbild"
replies: "Svar"
renotes: "Omnotera"
+_delivery:
+ stop: "Suspenderad"
+ _type:
+ none: "Publiceras"
_achievements:
_types:
_open3windows:
@@ -576,4 +580,3 @@ _webhookSettings:
_moderationLogTypes:
suspend: "Suspendera"
resetPassword: "Återställ Lösenord"
-
diff --git a/locales/th-TH.yml b/locales/th-TH.yml
index f0ddeab822..01510cc03c 100644
--- a/locales/th-TH.yml
+++ b/locales/th-TH.yml
@@ -33,7 +33,7 @@ logout: "ออà¸à¸ˆà¸²à¸à¸£à¸°à¸šà¸š"
signup: "สร้างบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰"
uploading: "à¸à¸³à¸¥à¸±à¸‡à¸­à¸±à¸›à¹‚หลด"
save: "บันทึà¸"
-users: "ผู้ใช้งาน"
+users: "ผู้ใช้"
addUser: "เพิ่มผู้ใช้"
favorite: "รายà¸à¸²à¸£à¹‚ปรด"
favorites: "รายà¸à¸²à¸£à¹‚ปรด"
@@ -400,6 +400,7 @@ name: "ชื่อ"
antennaSource: "à¹à¸«à¸¥à¹ˆà¸‡à¹€à¸ªà¸²à¸­à¸²à¸à¸²à¸¨"
antennaKeywords: "คีย์เวิร์ดที่ควรฟัง"
antennaExcludeKeywords: "คีย์เวิร์ดที่จะยà¸à¹€à¸§à¹‰à¸™"
+antennaExcludeBots: "ยà¸à¹€à¸§à¹‰à¸™à¸šà¸±à¸à¸Šà¸µà¸šà¸­à¸•"
antennaKeywordsDescription: "คั่นด้วยช่องว่างสำหรับเงื่อนไข AND หรือด้วยà¸à¸²à¸£à¸‚ึ้นบรรทัดใหม่สำหรับเงื่อนไข OR"
notifyAntenna: "à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนเà¸à¸µà¹ˆà¸¢à¸§à¸à¸±à¸šà¹‚น้ตใหม่"
withFileAntenna: "เฉพาะโน้ตที่มีไฟล์"
@@ -494,6 +495,7 @@ emojiStyle: "สไตล์เอโมจิ"
native: "ภาษาà¹à¸¡à¹ˆ"
disableDrawer: "อย่าใช้ลิ้นชัà¸à¸ªà¹„ตล์เมนู"
showNoteActionsOnlyHover: "à¹à¸ªà¸”งà¸à¸²à¸£à¸”ำเนินà¸à¸²à¸£à¹€à¸‰à¸žà¸²à¸°à¹‚น้ตเมื่อโฮเวอร์"
+showReactionsCount: "à¹à¸ªà¸”งจำนวนรีà¹à¸­à¸à¸Šà¸±à¹ˆà¸™à¹ƒà¸™à¹‚น้ต"
noHistory: "ไม่มีประวัติ"
signinHistory: "ประวัติà¸à¸²à¸£à¹€à¸‚้าสู่ระบบ"
enableAdvancedMfm: "เปิดใช้งาน MFM ขั้นสูง"
@@ -825,7 +827,7 @@ switchAccount: "สลับบัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰"
enabled: "เปิดใช้งาน"
disabled: "ปิดà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸‡à¸²à¸™"
quickAction: "ปุ่มลัด"
-user: "ผู้ใช้งาน"
+user: "ผู้ใช้"
administration: "à¸à¸²à¸£à¸ˆà¸±à¸”à¸à¸²à¸£"
accounts: "บัà¸à¸Šà¸µà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰"
switch: "สลับ"
@@ -1223,6 +1225,20 @@ enableHorizontalSwipe: "ปัดเพื่อสลับà¹à¸—็บ"
loading: "à¸à¸³à¸¥à¸±à¸‡à¹‚หลด"
surrender: "ยอมà¹à¸žà¹‰"
gameRetry: "เริ่มเà¸à¸¡à¹ƒà¸«à¸¡à¹ˆ"
+notUsePleaseLeaveBlank: "หาà¸à¹„ม่ได้ใช้à¸à¸£à¸¸à¸“าเว้นว่างไว้"
+useTotp: "ใช้รหัสผ่านà¹à¸šà¸šà¹ƒà¸Šà¹‰à¸„รั้งเดียว (TOTP)"
+useBackupCode: "ใช้รหัสสำรอง"
+launchApp: "เริ่มà¹à¸­à¸›"
+useNativeUIForVideoAudioPlayer: "ใช้ UI ของเบราว์เซอร์เพื่อเล่นวิดีโอ/เสียง"
+keepOriginalFilename: "คงชื่อไฟล์เดิมไว้"
+keepOriginalFilenameDescription: "หาà¸à¸›à¸´à¸”à¸à¸²à¸£à¸•ั้งค่านี้ ในระหว่างà¸à¸²à¸£à¸­à¸±à¸›à¹‚หลดชื่อไฟล์จะถูà¸à¹à¸—นที่ด้วยสตริงà¹à¸šà¸šà¸ªà¸¸à¹ˆà¸¡à¹‚ดยอัตโนมัติ"
+noDescription: "ไม่มีข้อความอธิบาย"
+alwaysConfirmFollow: "à¹à¸ªà¸”งข้อความยืนยันเมื่อà¸à¸”ติดตาม"
+inquiry: "ติดต่อเรา"
+_delivery:
+ stop: "ถูà¸à¸£à¸°à¸‡à¸±à¸š"
+ _type:
+ none: "à¸à¸³à¸¥à¸±à¸‡à¹€à¸œà¸¢à¹à¸žà¸£à¹ˆ"
_bubbleGame:
howToPlay: "วิธีเล่น"
hold: "หยุดชั่วคราว"
@@ -1351,7 +1367,7 @@ _serverSettings:
_accountMigration:
moveFrom: "ย้ายข้อมูลบัà¸à¸Šà¸µà¸­à¸·à¹ˆà¸™à¹„ปยังอีà¸à¸šà¸±à¸à¸Šà¸µà¸™à¸µà¹‰à¸«à¸™à¸¶à¹ˆà¸‡"
moveFromSub: "สร้างนามà¹à¸à¸‡à¹„ปยังบัà¸à¸Šà¸µà¸­à¸·à¹ˆà¸™"
- moveFromLabel: "บัà¸à¸Šà¸µà¸—ี่จะย้ายจาà¸:"
+ moveFromLabel: "บัà¸à¸Šà¸µà¸—ี่จะย้ายจาภ#{n}"
moveFromDescription: "ถ้าหาà¸à¸„ุณต้องà¸à¸²à¸£à¹‚อนข้อมูล คุณจำเป็นต้องสร้างบัà¸à¸Šà¸µà¸ªà¸³à¸£à¸­à¸‡à¸ªà¸³à¸«à¸£à¸±à¸šà¸à¸²à¸£à¸¢à¹‰à¸²à¸¢à¸šà¸±à¸à¸Šà¸µ หลังจาà¸à¸™à¸±à¹‰à¸™à¸›à¹‰à¸­à¸™à¸šà¸±à¸à¸Šà¸µà¸—ี่จะย้ายไปในรูปà¹à¸šà¸šà¸•่อไปนี้: @person@instance.com"
moveTo: "ย้ายข้อมูลบัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ปยังบัà¸à¸Šà¸µà¸­à¸µà¸à¸«à¸™à¸¶à¹ˆà¸‡"
moveToLabel: "บัà¸à¸Šà¸µà¸—ี่จะย้ายไปที่:"
@@ -1682,6 +1698,11 @@ _role:
roleAssignedTo: "มอบหมายให้มีบทบาทà¹à¸šà¸šà¸—ำมือ"
isLocal: "ผู้ใช้ในพื้นที่"
isRemote: "ผู้ใช้ระยะไà¸à¸¥"
+ isCat: "ผู้ใช้ที่เป็นà¹à¸¡à¸§"
+ isBot: "ผู้ใช้ที่เป็นบอต"
+ isSuspended: "ผู้ใช้ที่ถูà¸à¸£à¸°à¸‡à¸±à¸š"
+ isLocked: "ผู้ใช้บัà¸à¸Šà¸µà¹„ม่เปิดเผยสาธารณะ"
+ isExplorable: "ผู้ใช้ที่เปิดใช้งาน “ทำให้บัà¸à¸Šà¸µà¸‚องฉันค้นหาได้ง่ายขึ้นâ€"
createdLessThan: "สร้างน้อยà¸à¸§à¹ˆà¸²"
createdMoreThan: "สร้างมาà¸à¸à¸§à¹ˆà¸²"
followersLessThanOrEq: "จำนวนผู้ติดตามน้อยà¸à¸§à¹ˆà¸²à¸«à¸£à¸·à¸­à¹€à¸—่าà¸à¸±à¸š\n"
@@ -1751,6 +1772,7 @@ _plugin:
installWarn: "à¸à¸£à¸¸à¸“าอย่าติดตั้งปลั๊à¸à¸­à¸´à¸™à¸—ี่ไม่น่าเชื่อถือนะคะ"
manage: "จัดà¸à¸²à¸£à¸›à¸¥à¸±à¹Šà¸à¸­à¸´à¸™"
viewSource: "ดูต้นฉบับ"
+ viewLog: "à¹à¸ªà¸”งปูม"
_preferencesBackups:
list: "สร้างà¸à¸²à¸£à¸ªà¸³à¸£à¸­à¸‡à¸‚้อมูล"
saveNew: "บันทึà¸à¸‚้อมูลสำรองใหม่"
@@ -1940,7 +1962,6 @@ _2fa:
registerTOTP: "ลงทะเบียนà¹à¸­à¸žà¸•ัวตรวจสอบสิทธิ์"
step1: "ขั้นตอนà¹à¸£à¸ ติดตั้งà¹à¸­à¸›à¸¢à¸·à¸™à¸¢à¸±à¸™à¸•ัวตน (เช่น {a} หรือ {b}) บนอุปà¸à¸£à¸“์ของคุณ"
step2: "จาà¸à¸™à¸±à¹‰à¸™à¸ªà¹à¸à¸™à¸£à¸«à¸±à¸ª QR ที่à¹à¸ªà¸”งบนหน้าจอนี้"
- step2Click: "à¸à¸²à¸£à¸„ลิà¸à¸—ี่รหัส QR นี้จะช่วยให้คุณนั้นสามารถลงทะเบียน 2FA à¸à¸±à¸šà¸„ีย์ความปลอดภัยหรือà¹à¸­à¸›à¸•รวจสอบความถูà¸à¸•้องของโทรศัพท์ได้"
step2Uri: "ป้อนใส่ URL ดังต่อไปนี้ถ้าหาà¸à¸„ุณใช้โปรà¹à¸à¸£à¸¡à¹€à¸”สà¸à¹Œà¸—็อป"
step3Title: "ป้อนรหัสยืนยัน"
step3: "ป้อนโทเค็นที่à¹à¸­à¸›à¸‚องคุณให้มาเพื่อเสร็จสิ้นà¸à¸²à¸£à¸•ั้งค่า"
@@ -1964,6 +1985,7 @@ _2fa:
backupCodesDescription: "หาà¸à¹à¸­à¸›à¸¢à¸·à¸™à¸¢à¸±à¸™à¸•ัวตนของคุณไม่พร้อมใช้งาน คุณสามารถใช้รหัสสำรองด้านล่างเพื่อเข้าถึงบัà¸à¸Šà¸µà¸‚องคุณได้ อย่าลืมเà¸à¹‡à¸šà¸£à¸«à¸±à¸ªà¹€à¸«à¸¥à¹ˆà¸²à¸™à¸µà¹‰à¹„ว้ในที่ปลอดภัย à¹à¸•่ละรหัสสามารถใช้ได้เพียงครั้งเดียวเท่านั้น"
backupCodeUsedWarning: "มีà¸à¸²à¸£à¹ƒà¸Šà¹‰à¸£à¸«à¸±à¸ªà¸ªà¸³à¸£à¸­à¸‡à¹à¸¥à¹‰à¸§ โปรดà¸à¸£à¸¸à¸“าà¸à¸³à¸«à¸™à¸”ค่าà¸à¸²à¸£à¸•รวจสอบสิทธิ์à¹à¸šà¸šà¸ªà¸­à¸‡à¸›à¸±à¸ˆà¸ˆà¸±à¸¢à¹‚ดยเร็วที่สุดถ้าหาà¸à¸„ุณยังไม่สามารถใช้งานได้อีà¸"
backupCodesExhaustedWarning: "รหัสสำรองทั้งหมดถูà¸à¹ƒà¸Šà¹‰à¹à¸¥à¹‰à¸§ ถ้าหาà¸à¸„ุณยังสูà¸à¹€à¸ªà¸µà¸¢à¸à¸²à¸£à¹€à¸‚้าถึงà¹à¸­à¸›à¸à¸²à¸£à¸•รวจสอบสิทธิ์à¹à¸šà¸šà¸ªà¸­à¸‡à¸›à¸±à¸ˆà¸ˆà¸±à¸¢à¸„ุณจะยังไม่สามารถเข้าถึงบัà¸à¸Šà¸µà¸™à¸µà¹‰à¹„ด้ à¸à¸£à¸¸à¸“าà¸à¸³à¸«à¸™à¸”ค่าà¸à¸²à¸£à¸£à¸±à¸šà¸£à¸­à¸‡à¸„วามถูà¸à¸•้องด้วยà¸à¸²à¸£à¸¢à¸·à¸™à¸¢à¸±à¸™à¸ªà¸­à¸‡à¸Šà¸±à¹‰à¸™"
+ moreDetailedGuideHere: "คลิà¸à¸—ี่นี่เพื่อดูคำà¹à¸™à¸°à¸™à¸³à¹‚ดยละเอียด"
_permissions:
"read:account": "ดูข้อมูลบัà¸à¸Šà¸µà¸‚องคุณ"
"write:account": "à¹à¸à¹‰à¹„ขข้อมูลบัà¸à¸Šà¸µà¸‚องคุณ"
@@ -2014,7 +2036,6 @@ _permissions:
"read:admin:server-info": "ดูข้อมูลเซิร์ฟเวอร์"
"read:admin:show-moderation-log": "ดูปูมà¸à¸²à¸£à¹à¸à¹‰à¹„ข"
"read:admin:show-user": "ดูข้อมูลส่วนตัวของผู้ใช้"
- "read:admin:show-users": "ดูข้อมูลส่วนตัวของผู้ใช้"
"write:admin:suspend-user": "ระงับผู้ใช้"
"write:admin:unset-user-avatar": "ลบอวตารผู้ใช้"
"write:admin:unset-user-banner": "ลบà¹à¸šà¸™à¹€à¸™à¸­à¸£à¹Œà¸œà¸¹à¹‰à¹ƒà¸Šà¹‰"
@@ -2225,6 +2246,7 @@ _play:
title: "หัวข้อ"
script: "สคริปต์"
summary: "รายละเอียด"
+ visibilityDescription: "หาà¸à¸•ั้งค่าเป็นส่วนตัว มันจะไม่ปราà¸à¸à¹ƒà¸™à¹‚ปรไฟล์อีà¸à¸•่อไป à¹à¸•่ผู้ที่ทราบ URL ของมันจะยังสามารถเข้าถึงได้"
_pages:
newPage: "สร้างหน้าเพจใหม่"
editPage: "à¹à¸à¹‰à¹„ขหน้าเพจ"
@@ -2269,6 +2291,8 @@ _pages:
section: "ประเภท"
image: "รูปภาพ"
button: "ปุ่ม"
+ dynamic: "บล็อà¸à¹à¸šà¸šà¹„ดนามิà¸"
+ dynamicDescription: "บล็อà¸à¸™à¸µà¹‰à¸¥à¹‰à¸²à¸ªà¸¡à¸±à¸¢à¹à¸¥à¹‰à¸§ โปรดใช้ {play} à¹à¸—น นับจาà¸à¸™à¸µà¹‰à¹€à¸›à¹‡à¸™à¸•้นไป"
note: "โน้ตที่à¸à¸±à¸‡à¸•ัว"
_note:
id: "โน้ต ID"
@@ -2298,6 +2322,7 @@ _notification:
sendTestNotification: "ส่งทดสอบà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน"
notificationWillBeDisplayedLikeThis: "à¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือนมีลัà¸à¸©à¸“ะà¹à¸šà¸šà¸™à¸µà¹‰"
reactedBySomeUsers: "ถูà¸à¸£à¸µà¹à¸­à¸„ชั่นโดยผู้ใช้ {n} ราย"
+ likedBySomeUsers: "{n} คนถูà¸à¹ƒà¸ˆ"
renotedBySomeUsers: "รีโน้ตจาà¸à¸œà¸¹à¹‰à¹ƒà¸Šà¹‰ {n} ราย"
followedBySomeUsers: "มีผู้ติดตาม {n} ราย"
flushNotification: "ล้างประวัติà¸à¸²à¸£à¹à¸ˆà¹‰à¸‡à¹€à¸•ือน"
@@ -2524,3 +2549,21 @@ _reversi:
_offlineScreen:
title: "ออฟไลน์ - ไม่สามารถเชื่อมต่อà¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹„ด้"
header: "ไม่สามารถเชื่อมต่อà¸à¸±à¸šà¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¹„ด้"
+_urlPreviewSetting:
+ title: "à¸à¸²à¸£à¸•ั้งค่าà¸à¸²à¸£à¹à¸ªà¸”งตัวอย่าง URL"
+ enable: "เปิดใช้งานà¸à¸²à¸£à¹à¸ªà¸”งตัวอย่าง URL"
+ timeout: "เวลาจำà¸à¸±à¸”ในà¸à¸²à¸£à¹‚หลดตัวอย่าง URL (ms)"
+ timeoutDescription: "หาà¸à¹€à¸§à¸¥à¸²à¸—ี่ใช้ในà¸à¸²à¸£à¹‚หลดเà¸à¸´à¸™à¸„่านี้ จะไม่มีà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸à¸²à¸£à¹à¸ªà¸”งตัวอย่าง"
+ maximumContentLength: "ค่าสูงสุดของ Content-Length (byte)"
+ maximumContentLengthDescription: "หาภContent-Length เà¸à¸´à¸™à¸„่านี้ จะไม่มีà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸à¸²à¸£à¹à¸ªà¸”งตัวอย่าง"
+ requireContentLength: "สร้างà¸à¸²à¸£à¹à¸ªà¸”งตัวอย่างเฉพาะในà¸à¸£à¸“ีที่รับ Content-Length ไหว"
+ requireContentLengthDescription: "หาà¸à¹€à¸‹à¸´à¸£à¹Œà¸Ÿà¹€à¸§à¸­à¸£à¹Œà¸­à¸·à¹ˆà¸™à¹„ม่ส่งคืน Content-Length จะไม่มีà¸à¸²à¸£à¸ªà¸£à¹‰à¸²à¸‡à¸à¸²à¸£à¹à¸ªà¸”งตัวอย่าง"
+ userAgent: "User-Agent"
+ userAgentDescription: "ตั้งค่า User-Agent ที่ใช้ในà¸à¸²à¸£à¸£à¸±à¸šà¸à¸²à¸£à¹à¸ªà¸”งตัวอย่าง หาà¸à¹€à¸§à¹‰à¸™à¸§à¹ˆà¸²à¸‡à¹„ว้ ระบบจะใช้ User-Agent เริ่มต้น"
+ summaryProxy: "endpoint ของพร็อà¸à¸‹à¸µà¸—ี่สร้างà¸à¸²à¸£à¹à¸ªà¸”งตัวอย่าง"
+ summaryProxyDescription: "สร้างà¸à¸²à¸£à¹à¸ªà¸”งตัวอย่างด้วย summary Proxy à¹à¸—นที่จะใช้เนื้อหา Misskey"
+ summaryProxyDescription2: "พารามิเตอร์ต่อไปนี้จะถูà¸à¹ƒà¸Šà¹‰à¹€à¸›à¹‡à¸™à¸ªà¸•ริงà¸à¸²à¸£à¸ªà¸·à¸šà¸„้นเพื่อเชื่อมต่อà¸à¸±à¸šà¸žà¸£à¹‡à¸­à¸à¸‹à¸µ หาà¸à¸à¸±à¹ˆà¸‡à¸žà¸£à¹‡à¸­à¸à¸‹à¸µà¹„ม่รองรับà¸à¸²à¸£à¸•ั้งค่าเหล่านี้จะถูà¸à¸¥à¸°à¹€à¸§à¹‰à¸™"
+_mediaControls:
+ pip: "รูปภาพในรูปภาม"
+ playbackRate: "ความเร็วในà¸à¸²à¸£à¹€à¸¥à¹ˆà¸™"
+ loop: "เล่นวนซ้ำ"
diff --git a/locales/tr-TR.yml b/locales/tr-TR.yml
index e93a6e43e1..cf6729a81d 100644
--- a/locales/tr-TR.yml
+++ b/locales/tr-TR.yml
@@ -378,6 +378,10 @@ addMemo: "Kısa not ekle"
icon: "Avatar"
replies: "yanıt"
renotes: "vazgeçme"
+_delivery:
+ stop: "Askıya alınmış"
+ _type:
+ none: "Paylaşım"
_accountDelete:
started: "Silme işlemi başlatıldı"
_email:
@@ -455,4 +459,3 @@ _deck:
_moderationLogTypes:
suspend: "askıya al"
resetPassword: "Şifre sıfırlama"
-
diff --git a/locales/ug-CN.yml b/locales/ug-CN.yml
index e06cee11a2..e48f64511c 100644
--- a/locales/ug-CN.yml
+++ b/locales/ug-CN.yml
@@ -17,4 +17,3 @@ _2fa:
renewTOTPCancel: "ئۇنى توختىتىڭ"
_widgets:
profile: "profile"
-
diff --git a/locales/uk-UA.yml b/locales/uk-UA.yml
index df36f43c06..661ecf19d7 100644
--- a/locales/uk-UA.yml
+++ b/locales/uk-UA.yml
@@ -914,6 +914,10 @@ renotes: "Поширити"
sourceCode: "Вихідний код"
flip: "Перевернути"
lastNDays: "ОÑтанні {n} днів"
+_delivery:
+ stop: "Призупинено"
+ _type:
+ none: "ПублікаціÑ"
_achievements:
earnedAt: "Відкрито"
_types:
@@ -1623,4 +1627,3 @@ _moderationLogTypes:
resetPassword: "Скинути пароль"
_reversi:
total: "Ð’Ñього"
-
diff --git a/locales/uz-UZ.yml b/locales/uz-UZ.yml
index a79a76066a..306705e42e 100644
--- a/locales/uz-UZ.yml
+++ b/locales/uz-UZ.yml
@@ -846,6 +846,10 @@ icon: "Avatar"
replies: "Javoblar"
renotes: "Qayta qayd etish"
flip: "Teskari"
+_delivery:
+ stop: "To'xtatilgan"
+ _type:
+ none: "Yuborilmoqda"
_achievements:
_types:
_viewInstanceChart:
@@ -1090,4 +1094,3 @@ _moderationLogTypes:
resetPassword: "Parolni tiklash"
_reversi:
total: "Jami"
-
diff --git a/locales/verify.js b/locales/verify.js
new file mode 100644
index 0000000000..a8e9875d6e
--- /dev/null
+++ b/locales/verify.js
@@ -0,0 +1,53 @@
+import locales from './index.js';
+
+let valid = true;
+
+function writeError(type, lang, tree, data) {
+ process.stderr.write(JSON.stringify({ type, lang, tree, data }));
+ process.stderr.write('\n');
+ valid = false;
+}
+
+function verify(expected, actual, lang, trace) {
+ for (let key in expected) {
+ if (!Object.prototype.hasOwnProperty.call(actual, key)) {
+ continue;
+ }
+ if (typeof expected[key] === 'object') {
+ if (typeof actual[key] !== 'object') {
+ writeError('mismatched_type', lang, trace ? `${trace}.${key}` : key, { expected: 'object', actual: typeof actual[key] });
+ continue;
+ }
+ verify(expected[key], actual[key], lang, trace ? `${trace}.${key}` : key);
+ } else if (typeof expected[key] === 'string') {
+ switch (typeof actual[key]) {
+ case 'object':
+ writeError('mismatched_type', lang, trace ? `${trace}.${key}` : key, { expected: 'string', actual: 'object' });
+ break;
+ case 'undefined':
+ continue;
+ case 'string':
+ const expectedParameters = new Set(expected[key].match(/\{[^}]+\}/g)?.map((s) => s.slice(1, -1)));
+ const actualParameters = new Set(actual[key].match(/\{[^}]+\}/g)?.map((s) => s.slice(1, -1)));
+ for (let parameter of expectedParameters) {
+ if (!actualParameters.has(parameter)) {
+ writeError('missing_parameter', lang, trace ? `${trace}.${key}` : key, { parameter });
+ }
+ }
+ }
+ }
+ }
+}
+
+const { ['ja-JP']: original, ...verifiees } = locales;
+
+for (let lang in verifiees) {
+ if (!Object.prototype.hasOwnProperty.call(locales, lang)) {
+ continue;
+ }
+ verify(original, verifiees[lang], lang);
+}
+
+if (!valid) {
+ process.exit(1);
+}
diff --git a/locales/vi-VN.yml b/locales/vi-VN.yml
index 15530a5cd3..cf6eafd69f 100644
--- a/locales/vi-VN.yml
+++ b/locales/vi-VN.yml
@@ -121,9 +121,11 @@ sensitive: "Nhạy cảm"
add: "Thêm"
reaction: "Biểu cảm"
reactions: "Biểu cảm"
+emojiPicker: "Bá»™ chá»n biểu tượng cảm xúc"
reactionSettingDescription2: "Kéo để sắp xếp, nhấn để xóa, nhấn \"+\" để thêm."
rememberNoteVisibility: "Lưu kiểu tút mặc định"
attachCancel: "Gỡ tập tin đính kèm"
+deleteFile: "Xoá tệp tin"
markAsSensitive: "Äánh dấu là nhạy cảm"
unmarkAsSensitive: "BỠđánh dấu nhạy cảm"
enterFileName: "Nhập tên tập tin"
@@ -257,6 +259,7 @@ removed: "Äã xóa"
removeAreYouSure: "Bạn có chắc muốn gỡ \"{x}\"?"
deleteAreYouSure: "Bạn có chắc muốn xóa \"{x}\"?"
resetAreYouSure: "Bạn có chắc muốn đặt lại?"
+areYouSure: "Bạn chắc chứ?"
saved: "Äã lưu"
messaging: "Trò chuyện"
upload: "Tải lên"
@@ -307,6 +310,7 @@ folderName: "Tên thư mục"
createFolder: "Tạo thư mục"
renameFolder: "Äổi tên thư mục"
deleteFolder: "Xóa thư mục"
+folder: "Thư mục"
addFile: "Thêm tập tin"
emptyDrive: "Ổ đĩa của bạn trống trơn"
emptyFolder: "Thư mục trống"
@@ -368,6 +372,8 @@ hcaptcha: "hCaptcha"
enableHcaptcha: "Bật hCaptcha"
hcaptchaSiteKey: "Khóa của trang"
hcaptchaSecretKey: "Khóa bí mật"
+mcaptcha: "mCaptcha"
+enableMcaptcha: "Bật mCaptcha"
mcaptchaSiteKey: "Khóa của trang"
mcaptchaSecretKey: "Khóa bí mật"
recaptcha: "reCAPTCHA"
@@ -385,6 +391,7 @@ name: "Tên"
antennaSource: "Nguồn trạm phát sóng"
antennaKeywords: "Từ khóa để nghe"
antennaExcludeKeywords: "Từ khóa để lá»c ra"
+antennaExcludeBots: "Loại trừ các tài khoản bot"
antennaKeywordsDescription: "Phân cách bằng dấu cách cho Ä‘iá»u kiện AND hoặc bằng xuống dòng cho Ä‘iá»u kiện OR."
notifyAntenna: "Thông báo có tút mới"
withFileAntenna: "Chỉ những tút có media"
@@ -537,6 +544,7 @@ showInPage: "Hiện trong trang"
popout: "Pop-out"
volume: "Âm lượng"
masterVolume: "Âm thanh chung"
+notUseSound: "Tắt tiếng"
details: "Chi tiết"
chooseEmoji: "Chá»n emoji"
unableToProcess: "Không thể hoàn tất hành động"
@@ -557,6 +565,10 @@ output: "Nguồn ra"
script: "Kịch bản"
disablePagesScript: "Tắt AiScript trên Trang"
updateRemoteUser: "Cập nhật thông tin ngưá»i dùng ở máy chá»§ khác"
+unsetUserAvatar: "Gỡ ảnh đại diện"
+unsetUserAvatarConfirm: "Bạn có chắc muốn gỡ ảnh đại diện?"
+unsetUserBanner: "Gỡ ảnh bìa"
+unsetUserBannerConfirm: "Bạn có chắc muốn gỡ ảnh bìa?"
deleteAllFiles: "Xóa toàn bộ tập tin"
deleteAllFilesConfirm: "Bạn có chắc xóa toàn bộ tập tin?"
removeAllFollowing: "Ngưng theo dõi tất cả má»i ngưá»i"
@@ -859,6 +871,8 @@ makeReactionsPublicDescription: "Äiá»u này sẽ hiển thị công khai danh
classic: "Cổ điển"
muteThread: "Không quan tâm nữa"
unmuteThread: "Quan tâm tút này"
+followingVisibility: "Hiển thị lượt theo dõi"
+followersVisibility: "Hiển thị ngưá»i theo dõi"
continueThread: "Tiếp tục xem chuỗi tút"
deleteAccountConfirm: "Äiá»u này sẽ khiến tài khoản bị xóa vÄ©nh viá»…n. Vẫn tiếp tục?"
incorrectPassword: "Sai mật khẩu."
@@ -968,6 +982,7 @@ assign: "Phân công"
unassign: "Hủy phân công"
color: "Màu sắc"
manageCustomEmojis: "Quản lý CustomEmoji"
+manageAvatarDecorations: "Quản lý trang trí ảnh đại diện"
youCannotCreateAnymore: "Bạn đã tới giới hạn tạo."
cannotPerformTemporary: "Tạm thá»i không sá»­ dụng được"
cannotPerformTemporaryDescription: "Tạm thá»i không sá»­ dụng được vì lần số Ä‘iá»u kiện quá giá»›i hạn. Thá»­ lại sau má»t lát nữa."
@@ -991,18 +1006,24 @@ copyErrorInfo: "Sao chép thông tin lỗi"
joinThisServer: "Äăng ký trên chá»§ máy này"
exploreOtherServers: "Tìm chủ máy khác"
letsLookAtTimeline: "Thá»­ xem Timeline"
+disableFederationOk: "Vô hiệu hoá"
emailNotSupported: "Máy chủ này không hỗ trợ gửi email"
postToTheChannel: "Äăng lên kênh"
cannotBeChangedLater: "Không thể thay đổi sau này."
+likeOnly: "Chỉ lượt thích"
rolesAssignedToMe: "Vai trò được giao cho tôi"
resetPasswordConfirm: "Bạn thực sự muốn đặt lại mật khẩu?"
sensitiveWords: "Các từ nhạy cảm"
+prohibitedWords: "Các từ bị cấm"
license: "Giấy phép"
unfavoriteConfirm: "Bạn thá»±c sá»± muốn xoá khá»i mục yêu thích?"
+retryAllQueuesConfirmTitle: "Bạn có muốn thử lại?"
retryAllQueuesConfirmText: "Äiá»u này sẽ tạm thá»i làm tăng mức độ tải cá»§a máy chá»§."
enableChartsForRemoteUser: "Tạo biểu đồ ngưá»i dùng từ xa"
video: "Video"
videos: "Các video"
+audio: "Âm thanh"
+audioFiles: "Âm thanh"
dataSaver: "Tiết kiệm dung lượng"
accountMigration: "Chuyển tài khoản"
accountMoved: "Ngưá»i dùng này đã chuyển sang má»™t tài khoản má»›i:"
@@ -1019,36 +1040,88 @@ vertical: "Dá»c"
horizontal: "Thanh bên"
position: "Vị trí"
serverRules: "Luật của máy chủ"
+pleaseConfirmBelowBeforeSignup: "Äể đăng ký trên máy chá»§ này, bạn phải xem xét và đồng ý vá»›i những Ä‘iá»u sau."
+pleaseAgreeAllToContinue: "Bạn phải đồng ý tất cả Ä‘iá»u trên để tiếp tục."
+continue: "Tiếp tục"
+archive: "Lưu trữ"
+thisChannelArchived: "Kênh này đã được lưu trữ."
+initialAccountSetting: "Thiết lập hồ sơ"
youFollowing: "Äang theo dõi"
+preventAiLearning: "Từ chối sá»­ dụng công nghệ Máy Há»c (AI Sáng Tạo)"
+options: "Tùy chá»n"
+specifyUser: "Ngưá»i dùng chỉ định"
+failedToPreviewUrl: "Không thể xem trước"
+update: "Cập nhật"
later: "Äể sau"
goToMisskey: "Tá»›i Misskey"
installed: "Äã tải xuống"
branding: "Thương hiệu"
turnOffToImprovePerformance: "Tắt mục này có thể cải thiện hiệu năng."
+createInviteCode: "Tạo lá»i má»i"
+createWithOptions: "Tạo cùng tùy chá»n"
+createCount: "Số lượng má»i"
+inviteCodeCreated: "Lá»i má»i đã được tạo"
+inviteLimitExceeded: "Bạn đã vượt quá số lượng má»i mà bạn có thể tạo."
+createLimitRemaining: "Giá»›i hạn lượt má»i: Còn lại {limit}"
+inviteLimitResetCycle: "Giới hạn này sẽ được đặt lại vỠ{limit} lúc {time}."
expirationDate: "Ngày hết hạn"
noExpirationDate: "Vô thá»i hạn"
+inviteCodeUsedAt: "Mã má»i đã được sá»­ dụng lúc"
+registeredUserUsingInviteCode: "Lá»i má»i đã được sá»­ dụng bởi"
waitingForMailAuth: "Äang chá» xác nhận email"
+inviteCodeCreator: "Lá»i má»i đã được tạo bởi"
+usedAt: "Sử dụng vào lúc"
unused: "Chưa được sử dụng"
used: "Äã được sá»­ dụng"
expired: "Äã hết hạn"
doYouAgree: "Äồng ý?"
-iHaveReadXCarefullyAndAgree: "Tôi đã Ä‘á»c và đồng ý vá»›i \"x\"."
+beSureToReadThisAsItIsImportant: "Hãy Ä‘á»c kỹ vì nó rất quan trá»ng."
+iHaveReadXCarefullyAndAgree: "Tôi đã Ä‘á»c và đồng ý vá»›i \"{x}\"."
dialog: "Hộp thoại"
icon: "Ảnh đại diện"
forYou: "Dành cho bạn"
currentAnnouncements: "Thông báo hiện tại"
pastAnnouncements: "Thông báo trước đó"
youHaveUnreadAnnouncements: "Có thông báo chưa Ä‘á»c."
+useSecurityKey: "Làm theo hướng dẫn trên trình duyệt hoặc thiết bị của bạn để sử dụng khóa bảo mật hoặc mật mã."
replies: "Trả lá»i"
renotes: "Äăng lại"
loadReplies: "Hiển thị các trả lá»i"
+loadConversation: "Xem cuộc trò chuyện"
pinnedList: "Các mục đã được ghim"
keepScreenOn: "Giữ màn hình luôn bật"
verifiedLink: "Chúng tôi đã xác nhận bạn là chá»§ sở hữu cá»§a đưá»ng dẫn này"
+authentication: "Xác thực"
+authenticationRequiredToContinue: "Vui lòng xác thực để tiếp tục"
+dateAndTime: "Ngày và giá»"
+edited: "Äã chỉnh sá»­a"
+notificationRecieveConfig: "Cài đặt thông báo"
+mutualFollow: "Theo dõi lẫn nhau"
+followingOrFollower: "Äang theo dõi hoặc ngưá»i theo dõi"
+externalServices: "Các dịch vụ bên ngoài"
sourceCode: "Mã nguồn"
+feedback: "Phản hồi"
+feedbackUrl: "URL phản hồi"
+privacyPolicy: "Chính sách bảo mật"
+privacyPolicyUrl: "URL Chính sách bảo mật"
+tosAndPrivacyPolicy: "Äiá»u khoản sá»­ dụng và Chính sách bảo mật"
+avatarDecorations: "Trang trí ảnh đại diện"
+attach: "Mặc"
+detach: "Bá»"
+detachAll: "BỠtất cả"
+angle: "Góc"
flip: "Lật"
+showAvatarDecorations: "Hiển thị trang trí ảnh đại diện"
+releaseToRefresh: "Thả để làm mới"
+refreshing: "Äang làm má»›i"
+pullDownToRefresh: "Kéo xuống để làm mới"
+cwNotationRequired: "Nếu \"Ẩn nội dung\" được bật thì cần phải có chú thích."
lastNDays: "{n} ngày trước"
surrender: "Từ chối"
+_delivery:
+ stop: "Äã vô hiệu hóa"
+ _type:
+ none: "Äang đăng"
_announcement:
forExistingUsers: "Chỉ những ngưá»i dùng đã tồn tại"
forExistingUsersDescription: "Nếu được bật, thông báo này sẽ chỉ hiển thị vá»›i những ngưá»i dùng đã tồn tại vào lúc thông báo được tạo. Nếu tắt Ä‘i, những tài khoản má»›i đăng ký sau khi thông báo được đăng lên cÅ©ng sẽ thấy nó."
@@ -1280,6 +1353,7 @@ _role:
ltlAvailable: "Xem Timeline trong máy chủ này"
canPublicNote: "Cho phép đăng bài công khai"
canManageCustomEmojis: "Quản lý CustomEmoji"
+ canManageAvatarDecorations: "Quản lý trang trí ảnh đại diện"
driveCapacity: "Dữ liệu Drive"
pinMax: "Giới hạn ghim bài viết"
antennaMax: "Giới hạn tạo ăng ten"
@@ -1508,7 +1582,6 @@ _2fa:
registerTOTP: "Äăng ký ứng dụng xác thá»±c"
step1: "Trước tiên, hãy cài đặt một ứng dụng xác minh (chẳng hạn như {a} hoặc {b}) trên thiết bị của bạn."
step2: "Sau đó, quét mã QR hiển thị trên màn hình này."
- step2Click: "Quét mã QR trên ứng dụng xác thực (Authy, Google authenticator, v.v.)"
step3Title: "Nhập mã xác thực"
step3: "Nhập mã token do ứng dụng của bạn cung cấp để hoàn tất thiết lập."
step4: "Kể từ bây giá», những lần đăng nhập trong tương lai sẽ yêu cầu mã token đăng nhập đó."
@@ -1790,7 +1863,7 @@ _notification:
youReceivedFollowRequest: "Bạn vừa có một yêu cầu theo dõi"
yourFollowRequestAccepted: "Yêu cầu theo dõi của bạn đã được chấp nhận"
pollEnded: "Cuá»™c bình chá»n đã kết thúc"
- unreadAntennaNote: "Ăng ten"
+ unreadAntennaNote: "Ăng ten {name}"
emptyPushNotificationMessage: "Äã cập nhật thông báo đẩy"
achievementEarned: "Hoàn thành Achievement"
_types:
@@ -1852,6 +1925,6 @@ _webhookSettings:
_moderationLogTypes:
suspend: "Vô hiệu hóa"
resetPassword: "Äặt lại mật khẩu"
+ createInvitation: "Tạo lá»i má»i"
_reversi:
total: "Tổng cộng"
-
diff --git a/locales/zh-CN.yml b/locales/zh-CN.yml
index ddd8914146..7d1148909f 100644
--- a/locales/zh-CN.yml
+++ b/locales/zh-CN.yml
@@ -58,7 +58,7 @@ copyUserId: "å¤åˆ¶ç”¨æˆ· ID"
copyNoteId: "å¤åˆ¶å¸–å­ ID"
copyFileId: "å¤åˆ¶æ–‡ä»¶ID"
copyFolderId: "å¤åˆ¶æ–‡ä»¶å¤¹ID"
-copyProfileUrl: "å¤åˆ¶é…置文件URL"
+copyProfileUrl: "å¤åˆ¶ä¸ªäººèµ„æ–™URL"
searchUser: "æœç´¢ç”¨æˆ·"
reply: "回å¤"
loadMore: "查看更多"
@@ -108,11 +108,14 @@ enterEmoji: "输入表情符å·"
renote: "转å‘"
unrenote: "å–æ¶ˆè½¬å‘"
renoted: "已转å‘。"
+renotedToX: "转帖给 {name}"
cantRenote: "该帖无法转å‘。"
cantReRenote: "è½¬å‘æ— æ³•è¢«å†æ¬¡è½¬å‘。"
quote: "引用"
inChannelRenote: "在频é“内转å‘"
inChannelQuote: "在频é“内引用"
+renoteToChannel: "转帖至频é“"
+renoteToOtherChannel: "转帖至其它频é“"
pinnedNote: "已置顶的帖å­"
pinned: "置顶"
you: "您"
@@ -313,6 +316,7 @@ selectFile: "选择文件"
selectFiles: "选择文件"
selectFolder: "选择文件夹"
selectFolders: "选择多个文件夹"
+fileNotSelected: "未选择文件"
renameFile: "é‡å‘½å文件"
folderName: "文件夹åç§°"
createFolder: "创建文件夹"
@@ -400,6 +404,7 @@ name: "åç§°"
antennaSource: "æŽ¥æ”¶æ¥æº"
antennaKeywords: "包å«å…³é”®å­—"
antennaExcludeKeywords: "排除关键字"
+antennaExcludeBots: "排除机器人账户"
antennaKeywordsDescription: "AND æ¡ä»¶ç”¨ç©ºæ ¼åˆ†éš”,OR æ¡ä»¶ç”¨æ¢è¡Œç¬¦åˆ†éš”。"
notifyAntenna: "å¼€å¯é€šçŸ¥"
withFileAntenna: "仅带有附件的帖å­"
@@ -467,6 +472,7 @@ retype: "釿–°è¾“å…¥"
noteOf: "{user} 的帖å­"
quoteAttached: "已引用"
quoteQuestion: "是å¦å¼•用此链接内容?"
+attachAsFileQuestion: "剪贴æ¿å†…的文字过长。è¦è½¬æ¢ä¸ºæ–‡æœ¬æ–‡ä»¶å¹¶æ·»åŠ å—?"
noMessagesYet: "现在没有新的èŠå¤©"
newMessageExists: "æ–°ä¿¡æ¯"
onlyOneFileCanBeAttached: "åªèƒ½æ·»åŠ ä¸€ä¸ªé™„ä»¶"
@@ -494,6 +500,7 @@ emojiStyle: "表情符å·çš„æ ·å¼"
native: "原生"
disableDrawer: "䏿˜¾ç¤ºæŠ½å±‰èœå•"
showNoteActionsOnlyHover: "ä»…åœ¨æ‚¬åœæ—¶æ˜¾ç¤ºå¸–å­æ“作"
+showReactionsCount: "显示帖å­çš„回应数"
noHistory: "没有历å²è®°å½•"
signinHistory: "登录历å²"
enableAdvancedMfm: "å¯ç”¨æ‰©å±• MFM"
@@ -614,7 +621,7 @@ disablePlayer: "关闭播放器"
expandTweet: "展开帖å­"
themeEditor: "主题编辑器"
description: "æè¿°"
-describeFile: "添加标题"
+describeFile: "添加æè¿°"
enterFileDescription: "输入标题"
author: "作者"
leaveConfirm: "存在未ä¿å­˜çš„æ›´æ”¹ã€‚è¦æ”¾å¼ƒæ›´æ”¹å—?"
@@ -1019,6 +1026,7 @@ thisPostMayBeAnnoyingHome: "å‘到首页"
thisPostMayBeAnnoyingCancel: "å–æ¶ˆ"
thisPostMayBeAnnoyingIgnore: "就这样å‘布"
collapseRenotes: "çœç•¥æ˜¾ç¤ºå·²ç»çœ‹è¿‡çš„转å‘内容"
+collapseRenotesDescription: "å°†å›žåº”è¿‡æˆ–è½¬è´´è¿‡çš„è´´å­æŠ˜å è¡¨ç¤ºã€‚"
internalServerError: "内部æœåŠ¡å™¨é”™è¯¯"
internalServerErrorDescription: "内部æœåС噍å‘生了预期外的错误"
copyErrorInfo: "å¤åˆ¶é”™è¯¯ä¿¡æ¯"
@@ -1201,7 +1209,7 @@ code: "代ç "
reloadRequiredToApplySettings: "需è¦é‡æ–°è½½å…¥æ¥ä½¿è®¾ç½®ç”Ÿæ•ˆ"
remainingN: "剩余:{n}"
overwriteContentConfirm: "将覆盖现有内容。确定å—?"
-seasonalScreenEffect: "åº”æ™¯çš„ç”»é¢æ•ˆæžœ"
+seasonalScreenEffect: "符åˆå½“å‰å­£èŠ‚çš„ç”»é¢æ•ˆæžœ"
decorate: "装饰"
addMfmFunction: "添加装饰"
enableQuickAddMfmFunction: "显示高级 MFM 选择器"
@@ -1223,6 +1231,25 @@ enableHorizontalSwipe: "æ»‘åŠ¨åˆ‡æ¢æ ‡ç­¾é¡µ"
loading: "读å–中"
surrender: "å–æ¶ˆ"
gameRetry: "é‡è¯•"
+notUsePleaseLeaveBlank: "如ä¸ä½¿ç”¨è¯·ç•™ç©º"
+useTotp: "使用一次性代ç "
+useBackupCode: "使用备用代ç "
+launchApp: "å¯åŠ¨åº”ç”¨"
+useNativeUIForVideoAudioPlayer: "使用æµè§ˆå™¨çš„ UI 播放动画åŠéŸ³é¢‘"
+keepOriginalFilename: "ä¿æŒåŽŸæ–‡ä»¶å"
+keepOriginalFilenameDescription: "若关闭此设置,上传文件时文件å将被替æ¢ä¸ºéšæœºå­—ç¬¦ã€‚"
+noDescription: "没有æè¿°"
+alwaysConfirmFollow: "总是确认关注"
+inquiry: "è”系我们"
+_delivery:
+ status: "投递状æ€"
+ stop: "åœæ­¢æŠ•递"
+ resume: "继续投递"
+ _type:
+ none: "投递中"
+ manuallySuspended: "æ‰‹åŠ¨åœæ­¢ä¸­"
+ goneSuspended: "å› æœåŠ¡å™¨è¢«åˆ é™¤è€Œåœæ­¢"
+ autoSuspendedForNotResponding: "å› æœåŠ¡å™¨æ— åº”ç­”è€Œåœæ­¢"
_bubbleGame:
howToPlay: "游æˆè¯´æ˜Ž"
hold: "抓ä½"
@@ -1233,6 +1260,7 @@ _bubbleGame:
maxChain: "最高连击数"
yen: "{yen} 日元"
estimatedQty: "约 {qty} 个"
+ scoreSweets: "相当于 {onigiriQtyWithUnit} 饭团"
_howToPlay:
section1: "对准ä½ç½®å°†Emoji投入盒å­ã€‚"
section2: "相åŒçš„Emojiç›¸äº’æŽ¥è§¦åˆæˆåŽä¼šå¾—到新的Emoji,以此获得分数。"
@@ -1350,7 +1378,7 @@ _serverSettings:
_accountMigration:
moveFrom: "从别的账å·è¿ç§»åˆ°æ­¤è´¦æˆ·"
moveFromSub: "为å¦ä¸€ä¸ªè´¦æˆ·å»ºç«‹åˆ«å"
- moveFromLabel: "è¿ç§»å‰çš„账户"
+ moveFromLabel: "è¿ç§»å‰çš„账户 #{n}"
moveFromDescription: "如果è¿ç§»æ—¶éœ€è¦ç»§æ‰¿å…¶ä»–账户的关注者,你需è¦åˆ›å»ºä¸€ä¸ªåˆ«å。此æ“作需è¦åœ¨è¿ç§»å‰å®Œæˆï¼\n请åƒè¿™æ ·è¾“å…¥è¦è¿ç§»çš„账户:@username@server.example.com\n如果è¦åˆ é™¤ï¼Œè¯·å°†è¾“入字段留空,并ä¿å­˜ï¼ˆä¸æŽ¨è)。"
moveTo: "把这个账户è¿ç§»åˆ°æ–°çš„账户"
moveToLabel: "è¿ç§»åŽçš„账户"
@@ -1680,6 +1708,11 @@ _role:
roleAssignedTo: "已分é…给手动角色"
isLocal: "是本地用户"
isRemote: "是远程用户"
+ isCat: "猫猫用户"
+ isBot: "机器人用户"
+ isSuspended: "åœç”¨çš„用户"
+ isLocked: "é”æŽ¨ç”¨æˆ·"
+ isExplorable: "å¯ç”¨â€œä½¿è´¦å·å¯è§â€çš„用户"
createdLessThan: "账户创建时间少于"
createdMoreThan: "账户创建时间超过"
followersLessThanOrEq: "关注者ä¸å¤šäºŽ"
@@ -1749,6 +1782,7 @@ _plugin:
installWarn: "请ä¸è¦å®‰è£…ä¸å¯ä¿¡çš„æ’ä»¶ã€‚"
manage: "ç®¡ç†æ’ä»¶..."
viewSource: "查看æºä»£ç "
+ viewLog: "显示日志"
_preferencesBackups:
list: "已创建的备份"
saveNew: "å¦å­˜ä¸º"
@@ -1938,7 +1972,6 @@ _2fa:
registerTOTP: "开始设置认è¯åº”用"
step1: "首先,在您的设备上安装验è¯åº”用,例如 {a} 或 {b}。"
step2: "ç„¶åŽï¼Œæ‰«æå±å¹•上显示的二维ç ã€‚"
- step2Click: "通过点击二维ç ï¼Œæ‚¨å¯ä»¥ä½¿ç”¨è®¾å¤‡ä¸Šå®‰è£…的身份验è¯å™¨åº”ç”¨ç¨‹åºæˆ–密钥环进行注册"
step2Uri: "如果使用桌é¢åº”用程åºçš„è¯ï¼Œè¯·è¾“入下é¢çš„ URI"
step3Title: "输入验è¯ç "
step3: "输入您的应用æä¾›çš„动æ€å£ä»¤ä»¥å®Œæˆè®¾ç½®ã€‚"
@@ -1962,6 +1995,7 @@ _2fa:
backupCodesDescription: "如果无法使用认è¯åº”用,å¯ä»¥ä½¿ç”¨ä»¥ä¸‹çš„å¤‡ç”¨ä»£ç æ¥è®¿é—®è´¦æˆ·ã€‚请务必将这些代ç ä¿å­˜åœ¨å®‰å…¨çš„地方。æ¯ä¸ªä»£ç ä»…å¯ä½¿ç”¨ä¸€æ¬¡ã€‚"
backupCodeUsedWarning: "已使用备用代ç ã€‚如果无法使用认è¯åº”ç”¨ï¼Œè¯·å°½å¿«é‡æ–°è®¾å®šã€‚"
backupCodesExhaustedWarning: "已使用完所有的备用代ç ã€‚如果无法使用认è¯åº”用,将无法å†è®¿é—®æ‚¨çš„è´¦æˆ·ã€‚è¯·å†æ¬¡è®¾å®šè®¤è¯åº”用。"
+ moreDetailedGuideHere: "此处为详细指å—"
_permissions:
"read:account": "查看账户信æ¯"
"write:account": "æ›´æ”¹å¸æˆ·ä¿¡æ¯"
@@ -2012,7 +2046,6 @@ _permissions:
"read:admin:server-info": "查看æœåŠ¡å™¨ä¿¡æ¯"
"read:admin:show-moderation-log": "æŸ¥çœ‹ç®¡ç†æ—¥å¿—"
"read:admin:show-user": "查看用户的éžå…¬å¼€ä¿¡æ¯"
- "read:admin:show-users": "查看用户的éžå…¬å¼€ä¿¡æ¯"
"write:admin:suspend-user": "冻结用户"
"write:admin:unset-user-avatar": "删除用户头åƒ"
"write:admin:unset-user-banner": "删除用户横幅"
@@ -2223,6 +2256,7 @@ _play:
title: "标题"
script: "脚本"
summary: "æè¿°"
+ visibilityDescription: "设置为ä¸å…¬å¼€åŽèµ„料将ä¸å†æ˜¾ç¤ºï¼Œä½†çŸ¥é“ URL 的人ä»å¯ç»§ç»­è®¿é—®ã€‚"
_pages:
newPage: "创建页é¢"
editPage: "编辑页é¢"
@@ -2267,6 +2301,8 @@ _pages:
section: "章节"
image: "图片"
button: "按钮"
+ dynamic: "动æ€åŒºå—"
+ dynamicDescription: "这个区å—å·²ç»åºŸå¼ƒã€‚以åŽè¯·ä½¿ç”¨{play}。"
note: "嵌入的帖å­"
_note:
id: "å¸–å­ ID"
@@ -2296,6 +2332,7 @@ _notification:
sendTestNotification: "å‘逿µ‹è¯•通知"
notificationWillBeDisplayedLikeThis: "通知将会这样表示"
reactedBySomeUsers: "{n} 人回应了"
+ likedBySomeUsers: "{n}人赞了你的帖å­"
renotedBySomeUsers: "{n} 人转å‘了"
followedBySomeUsers: "被 {n} 人关注"
flushNotification: "é‡ç½®é€šçŸ¥åކå²"
@@ -2322,6 +2359,7 @@ _deck:
alwaysShowMainColumn: "总是显示主列"
columnAlign: "列对é½"
addColumn: "添加列"
+ newNoteNotificationSettings: "新帖å­é€šçŸ¥è®¾å®š"
configureColumn: "列设置"
swapLeft: "å‘左移动"
swapRight: "å‘å³ç§»åЍ"
@@ -2478,6 +2516,7 @@ _hemisphere:
_reversi:
reversi: "黑白棋"
gameSettings: "对局设置"
+ chooseBoard: "选择棋盘"
blackOrWhite: "先手/åŽæ‰‹"
blackIs: "{name}执黑(先手)"
rules: "规则"
@@ -2504,6 +2543,8 @@ _reversi:
allGames: "所有对局"
ended: "结æŸ"
playing: "对局中"
+ isLlotheo: "è½å­å°‘的一方获胜(åˆå奥赛罗)"
+ loopedMap: "循环棋盘"
canPutEverywhere: "æ— é™åˆ¶æ”¾ç½®æ¨¡å¼"
timeLimitForEachTurn: "1回åˆçš„æ—¶é—´é™åˆ¶"
freeMatch: "自由匹é…"
@@ -2519,3 +2560,21 @@ _reversi:
_offlineScreen:
title: "离线——无法连接到æœåС噍"
header: "无法连接到æœåС噍"
+_urlPreviewSetting:
+ title: "设置 URL 预览"
+ enable: "å¯ç”¨ URL 预览"
+ timeout: "超时阈值(ms)"
+ timeoutDescription: "如果获å–预览所用时间超过这个值,则ä¸ç”Ÿæˆé¢„览。"
+ maximumContentLength: "Content-Length 的最大值(byte)"
+ maximumContentLengthDescription: "如果 Content-Length 超过这个值,则ä¸ç”Ÿæˆé¢„览。"
+ requireContentLength: "仅在能å–å¾— Content-Length 时生æˆé¢„览"
+ requireContentLengthDescription: "如果目标æœåС噍ä¸è¿”回 Content-Length,则ä¸ç”Ÿæˆé¢„览。"
+ userAgent: "User-Agent"
+ userAgentDescription: "设定获å–预览时使用的 User-Agent。留空时将使用默认的 User-Agent。"
+ summaryProxy: "用æ¥ç”Ÿæˆé¢„览的代ç†çš„ endpoint。"
+ summaryProxyDescription: "ä¸ä½¿ç”¨ Misskey 本体,而是通过 Summaly Proxy 生æˆé¢„览。"
+ summaryProxyDescription2: "下é¢çš„傿•°å°†ä½œä¸ºæŸ¥è¯¢å­—符串å‘é€è‡³ä»£ç†ã€‚代ç†ä¾§å¦‚æžœä¸æ”¯æŒæ­¤è®¾ç½®ï¼Œåˆ™å¿½ç•¥è®¾å®šå€¼ã€‚"
+_mediaControls:
+ pip: "画中画"
+ playbackRate: "播放速度"
+ loop: "循环播放"
diff --git a/locales/zh-TW.yml b/locales/zh-TW.yml
index f8bdd002b4..78116254ba 100644
--- a/locales/zh-TW.yml
+++ b/locales/zh-TW.yml
@@ -108,11 +108,14 @@ enterEmoji: "輸入表情符號"
renote: "轉發"
unrenote: "å–æ¶ˆè½‰ç™¼"
renoted: "轉發æˆåŠŸã€‚"
+renotedToX: "轉發給 {name} 了。"
cantRenote: "無法轉發此貼文。"
cantReRenote: "無法轉發之å‰å·²ç¶“轉發éŽçš„內容。"
quote: "引用"
inChannelRenote: "在頻é“內轉發"
inChannelQuote: "在頻é“內引用"
+renoteToChannel: "轉發至頻é“"
+renoteToOtherChannel: "轉發至其他頻é“"
pinnedNote: "已置頂的貼文"
pinned: "置頂"
you: "您"
@@ -169,7 +172,7 @@ cacheRemoteSensitiveFilesDescription: "è‹¥åœç”¨é€™å€‹è¨­å®šï¼Œå‰‡ä¸æœƒå¿«å–é
flagAsBot: "此使用者是機器人"
flagAsBotDescription: "å¦‚æžœæœ¬å¸³æˆ¶æ˜¯ç”±ç¨‹å¼æŽ§åˆ¶ï¼Œè«‹å•Ÿç”¨æ­¤é¸é …。啟用後,會作為標示幫助其他開發者防止機器人之間產生無é™äº’動的行為,並會調整 Misskey 內部系統將本帳戶識別為機器人。"
flagAsCat: "此帳戶是一隻貓,喵~~~ï¼ï¼ï¼"
-flagAsCatDescription: "如果想將本帳戶標示為一隻貓,請開啟此標示"
+flagAsCatDescription: "喵喵喵??"
flagShowTimelineReplies: "在時間軸上顯示貼文的回覆"
flagShowTimelineRepliesDescription: "啟用後,時間軸除了顯示使用者的貼文以外,還會顯示使用者å°å…¶ä»–貼文的回覆。"
autoAcceptFollowed: "自動å…許來自追隨中使用者的追隨請求"
@@ -205,7 +208,7 @@ silenceThisInstance: "ç¦è¨€æ­¤ä¼ºæœå™¨"
operations: "æ“作"
software: "軟體"
version: "版本"
-metadata: "元資料"
+metadata: "詮釋資料"
withNFiles: "{n} 個檔案"
monitor: "監視器"
jobQueue: "佇列"
@@ -313,6 +316,7 @@ selectFile: "鏿“‡æª”案"
selectFiles: "鏿“‡æª”案"
selectFolder: "鏿“‡è³‡æ–™å¤¾"
selectFolders: "鏿“‡è³‡æ–™å¤¾"
+fileNotSelected: "å°šæœªé¸æ“‡æª”案"
renameFile: "釿–°å‘½å檔案"
folderName: "資料夾å稱"
createFolder: "新增資料夾"
@@ -366,7 +370,7 @@ enableRegistration: "開放新使用者註冊"
invite: "邀請"
driveCapacityPerLocalAccount: "æ¯å€‹æœ¬åœ°ä½¿ç”¨è€…的雲端硬碟容é‡"
driveCapacityPerRemoteAccount: "æ¯å€‹éžæœ¬åœ°ç”¨æˆ¶çš„雲端空間大å°"
-inMb: "以Mbps為單ä½"
+inMb: "以 MB 為單ä½"
bannerUrl: "橫幅圖片URL"
backgroundImageUrl: "背景圖片的來æºç¶²å€ "
basicInfo: "基本資訊"
@@ -378,12 +382,12 @@ pinnedClipId: "置頂的摘錄ID"
pinnedNotes: "已置頂的貼文"
hcaptcha: "hCaptcha"
enableHcaptcha: "啟用 hCaptcha"
-hcaptchaSiteKey: "網站金鑰"
-hcaptchaSecretKey: "金鑰"
+hcaptchaSiteKey: "hcaptchaSiteKey"
+hcaptchaSecretKey: "hcaptchaSecretKey"
mcaptcha: "mCaptcha"
enableMcaptcha: "啟用 mCaptcha"
mcaptchaSiteKey: "網站金鑰"
-mcaptchaSecretKey: "金鑰"
+mcaptchaSecretKey: "ç§å¯†é‡‘é‘°"
mcaptchaInstanceUrl: "mCaptcha 的實例網å€"
recaptcha: "reCAPTCHA"
enableRecaptcha: "啟用 reCAPTCHA"
@@ -391,8 +395,8 @@ recaptchaSiteKey: "網站金鑰"
recaptchaSecretKey: "金鑰"
turnstile: "Turnstile"
enableTurnstile: "啟用 Turnstile"
-turnstileSiteKey: "網站金鑰"
-turnstileSecretKey: "金鑰"
+turnstileSiteKey: "turnstileSiteKey"
+turnstileSecretKey: "turnstileSecretKey"
avoidMultiCaptchaConfirm: "使用多種驗證方å¼å¯èƒ½æœƒé€ æˆå¹²æ“¾ï¼Œæ‚¨è¦é—œé–‰å…¶ä»–驗證方å¼å—Žï¼Ÿæ‚¨å¯ä»¥æŒ‰ã€Œå–消ã€ä¿ç•™å¤šç¨®é©—證方å¼ã€‚"
antennas: "天線"
manageAntennas: "管ç†å¤©ç·š"
@@ -400,6 +404,7 @@ name: "å稱"
antennaSource: "接收來æº"
antennaKeywords: "包å«é—œéµå­—"
antennaExcludeKeywords: "排除關éµå­—"
+antennaExcludeBots: "排除機器人帳戶"
antennaKeywordsDescription: "空格代表「以åŠã€ï¼ˆAND),æ›è¡Œä»£è¡¨ã€Œæˆ–者ã€ï¼ˆOR)"
notifyAntenna: "通知有新貼文"
withFileAntenna: "僅帶有附件的貼文"
@@ -463,10 +468,11 @@ title: "標題"
text: "文字"
enable: "啟用"
next: "下一步"
-retype: "冿¬¡è¼¸å…¥"
+retype: "釿–°è¼¸å…¥"
noteOf: "{user}的貼文"
quoteAttached: "引用"
quoteQuestion: "是å¦è¦å¼•用?"
+attachAsFileQuestion: "å‰ªè²¼ç°¿çš„æ–‡å­—è¼ƒé•·ã€‚è«‹å•æ˜¯å¦è¦å°‡å…¶ä»¥æ–‡å­—檔的方å¼é™„加呢?"
noMessagesYet: "沒有訊æ¯"
newMessageExists: "有新的訊æ¯"
onlyOneFileCanBeAttached: "åªèƒ½åР入䏀個附件"
@@ -494,6 +500,7 @@ emojiStyle: "表情符號的風格"
native: "原生"
disableDrawer: "ä¸é¡¯ç¤ºä¸‹æ‹‰å¼é¸å–®"
showNoteActionsOnlyHover: "僅在游標åœç•™æ™‚顯示貼文的æ“作é¸é …"
+showReactionsCount: "é¡¯ç¤ºè²¼æ–‡çš„åæ‡‰æ•¸ç›®"
noHistory: "沒有歷å²ç´€éŒ„"
signinHistory: "登入歷å²"
enableAdvancedMfm: "啟用進階 MFM"
@@ -600,7 +607,7 @@ addItem: "新增項目"
rearrange: "æŽ’åºæ–¹å¼"
relays: "中繼器"
addRelay: "新增中繼器"
-inboxUrl: "收件夾URL"
+inboxUrl: "收件夾 URL"
addedRelays: "已加入的中繼器"
serviceworkerInfo: "如è¦ä½¿ç”¨æŽ¨æ’­é€šçŸ¥ï¼Œéœ€è¦å•Ÿç”¨æ­¤é¸é …並設定金鑰。"
deletedNote: "已刪除的貼文"
@@ -750,7 +757,7 @@ experimentalFeatures: "實驗中的功能"
experimental: "實驗性"
thisIsExperimentalFeature: "這是實驗性的功能。å¯èƒ½æœƒæœ‰è®Šæ›´è¦æ ¼å’Œä¸èƒ½æ­£å¸¸å‹•作的å¯èƒ½æ€§ã€‚"
developer: "開發者"
-makeExplorable: "使自己的帳戶能夠在「探索ã€é é¢ä¸­é¡¯ç¤º"
+makeExplorable: "使自己的帳戶更容易被找到"
makeExplorableDescription: "å¦‚æžœé—œé–‰ï¼Œå¸³æˆ¶å°‡ä¸æœƒè¢«é¡¯ç¤ºåœ¨ã€ŒæŽ¢ç´¢ã€é é¢ä¸­ã€‚"
showGapBetweenNotesInTimeline: "分開顯示時間軸上的貼文"
duplicate: "複製"
@@ -789,7 +796,7 @@ newVersionOfClientAvailable: "新版本的客戶端å¯ç”¨ã€‚"
usageAmount: "使用é‡"
capacity: "容é‡"
inUse: "已使用"
-editCode: "編輯代碼"
+editCode: "編輯程å¼ç¢¼"
apply: "套用"
receiveAnnouncementFromInstance: "接收來自伺æœå™¨çš„通知"
emailNotification: "郵件通知"
@@ -1019,6 +1026,7 @@ thisPostMayBeAnnoyingHome: "發佈到首é "
thisPostMayBeAnnoyingCancel: "退出"
thisPostMayBeAnnoyingIgnore: "直接發佈貼文"
collapseRenotes: "çœç•¥é¡¯ç¤ºå·²çœ‹éŽçš„轉發貼文"
+collapseRenotesDescription: "將已åšéŽå應和轉發的貼文折疊顯示。"
internalServerError: "內部伺æœå™¨éŒ¯èª¤"
internalServerErrorDescription: "內部伺æœå™¨å‡ºç¾æ„外錯誤。"
copyErrorInfo: "複製錯誤資訊"
@@ -1060,7 +1068,7 @@ enableChartsForFederatedInstances: "生æˆé ç«¯ä¼ºæœå™¨çš„圖表"
showClipButtonInNoteFooter: "新增摘錄按鈕至貼文"
reactionsDisplaySize: "忇‰çš„顯示尺寸"
limitWidthOfReaction: "é™åˆ¶å應的最大寬度,並縮å°é¡¯ç¤ºå°ºå¯¸ã€‚"
-noteIdOrUrl: "貼文ID或URL"
+noteIdOrUrl: "貼文 ID 或 URL"
video: "影片"
videos: "影片"
audio: "音效"
@@ -1075,7 +1083,7 @@ addMemo: "新增備註"
editMemo: "編輯備註"
reactionsList: "忇‰åˆ—表"
renotesList: "轉發貼文列表"
-notificationDisplay: "通知的顯示"
+notificationDisplay: "通知"
leftTop: "左上"
rightTop: "å³ä¸Š"
leftBottom: "左下"
@@ -1177,15 +1185,15 @@ repositoryUrlOrTarballRequired: "å¦‚æžœå„²å­˜åº«ä¸æ˜¯å…¬é–‹çš„,則必須æä¾
feedback: "æ„見回饋"
feedbackUrl: "æ„見回饋 URL"
impressum: "營é‹è€…資訊"
-impressumUrl: "營é‹è€…資訊網å€"
+impressumUrl: "營é‹è€…資訊 URL"
impressumDescription: "在德國與部份地å€å¿…é ˆè¦æ˜Žç¢ºé¡¯ç¤ºç‡Ÿé‹è€…資訊。"
privacyPolicy: "éš±ç§æ”¿ç­–"
-privacyPolicyUrl: "éš±ç§æ”¿ç­–ç¶²å€"
+privacyPolicyUrl: "éš±ç§æ”¿ç­– URL"
tosAndPrivacyPolicy: "æœå‹™æ¢æ¬¾å’Œéš±ç§æ”¿ç­–"
avatarDecorations: "é ­åƒè£é£¾"
attach: "è£ä¸Š"
detach: "å–下"
-detachAll: "移除所有è£é£¾"
+detachAll: "全部移除"
angle: "角度"
flip: "翻轉"
showAvatarDecorations: "顯示頭åƒè£é£¾"
@@ -1203,7 +1211,7 @@ remainingN: "剩餘:{n}"
overwriteContentConfirm: "確定è¦è¦†è“‹ç›®å‰çš„內容嗎?"
seasonalScreenEffect: "隨季節變æ›ç•«é¢çš„呈ç¾"
decorate: "設置頭åƒè£é£¾"
-addMfmFunction: "æ’å…¥MFM功能語法"
+addMfmFunction: "æ’å…¥ MFM 功能語法"
enableQuickAddMfmFunction: "顯示高級 MFM 鏿“‡å™¨"
bubbleGame: "æ°£æ³¡éŠæˆ²"
sfx: "音效"
@@ -1223,6 +1231,25 @@ enableHorizontalSwipe: "æ»‘å‹•åˆ‡æ›æ™‚間軸"
loading: "載入中"
surrender: "退出"
gameRetry: "å†è©¦ä¸€æ¬¡"
+notUsePleaseLeaveBlank: "如果ä¸ä½¿ç”¨çš„話請留白"
+useTotp: "使用一次性密碼"
+useBackupCode: "使用備用驗證碼"
+launchApp: "啟動 APP"
+useNativeUIForVideoAudioPlayer: "使用ç€è¦½å™¨çš„ UI 播放影片與音訊"
+keepOriginalFilename: "ä¿ç•™åŽŸå§‹æª”å"
+keepOriginalFilenameDescription: "如果關閉此設置,上傳時檔案å稱會自動替æ›ç‚ºéš¨æ©Ÿå­—串。"
+noDescription: "沒有說明文字"
+alwaysConfirmFollow: "點擊追隨時總是顯示確èªè¨Šæ¯"
+inquiry: "è¯çµ¡æˆ‘們"
+_delivery:
+ status: "傳é€ç‹€æ…‹"
+ stop: "åœæ­¢å‚³é€"
+ resume: "æ¢å¾©å‚³é€"
+ _type:
+ none: "直播中"
+ manuallySuspended: "手動暫åœä¸­"
+ goneSuspended: "因為伺æœå™¨åˆªé™¤æ‰€ä»¥æš«åœä¸­"
+ autoSuspendedForNotResponding: "因為伺æœå™¨æ²’有回應所以暫åœä¸­"
_bubbleGame:
howToPlay: "玩法說明"
hold: "ä¿ç•™"
@@ -1231,7 +1258,7 @@ _bubbleGame:
scoreYen: "賺å–的金é¡"
highScore: "最高分"
maxChain: "最大çµåˆæ•¸"
- yen: "{yen} 日圓"
+ yen: "{yen}円"
estimatedQty: "{qty}個"
scoreSweets: "飯糰 {onigiriQtyWithUnit}"
_howToPlay:
@@ -1259,7 +1286,7 @@ _initialAccountSetting:
privacySetting: "éš±ç§è¨­å®š"
theseSettingsCanEditLater: "這裡的設定å¯ä»¥åœ¨ä¹‹å¾Œè®Šæ›´ã€‚"
youCanEditMoreSettingsInSettingsPageLater: "除此之外,還å¯ä»¥åœ¨ã€Œè¨­å®šã€é é¢é€²è¡Œå„種設定。之後請確èªçœ‹çœ‹ã€‚"
- followUsers: "為了構築時間軸,試著追蹤您感興趣的使用者å§ã€‚"
+ followUsers: "為了構築時間軸,試著追隨您感興趣的使用者å§ã€‚"
pushNotificationDescription: "啟用推é€é€šçŸ¥ï¼Œå°±å¯ä»¥åœ¨è¨­å‚™ä¸ŠæŽ¥æ”¶{name}的通知。"
initialAccountSettingCompleted: "åˆå§‹è¨­å®šå®Œæˆäº†ï¼"
haveFun: "盡情享å—{name}å§ï¼"
@@ -1314,7 +1341,7 @@ _initialTutorial:
title: "éš±è—內容(CW)"
description: "將顯示「註釋ã€ä¸­å¯«å…¥çš„å…§å®¹è€Œä¸æ˜¯æœ¬æ–‡ã€‚按一下「顯示內容ã€ä»¥é¡¯ç¤ºæœ¬æ–‡ã€‚"
_exampleNote:
- cw: "ç¾Žé£Ÿææ€–主義注æ„"
+ cw: "æ³¨æ„æ¶ˆå¤œæ–‡"
note: "我åƒäº†ä¸€å€‹å·§å…‹åŠ›ç”œç”œåœˆðŸ©ðŸ˜‹"
useCases: "伺æœå™¨çš„æœå‹™æ¢æ¬¾å¯èƒ½æœƒè¦ç¯„特定的貼文需è¦ä½¿ç”¨éš±è—內容,除此之外也會用在隱è—劇情洩æ¼èˆ‡æ•感內容的貼文。"
_howToMakeAttachmentsSensitive:
@@ -1339,7 +1366,7 @@ _serverRules:
_serverSettings:
iconUrl: "圖示的 URL"
appIconDescription: "指定顯示 {host} ç‚ºæ‡‰ç”¨ç¨‹å¼æ™‚的圖示。"
- appIconUsageExample: "例如:漸進å¼ç¶²è·¯æ‡‰ç”¨ç¨‹å¼ï¼ˆPWAï¼‰ã€æ–¼æ‰‹æ©Ÿæ¡Œé¢æ–°å¢žæ›¸ç±¤"
+ appIconUsageExample: "例如:PWA 或是在手機桌é¢ä½œç‚ºæ›¸ç±¤ç­‰"
appIconStyleRecommendation: "因為å¯èƒ½æœƒè£å‰ªæˆåœ“形或圓角,所以建議用單色填滿邊框åŠèƒŒæ™¯ã€‚"
appIconResolutionMustBe: "è§£æžåº¦å¿…須為 {resolution}。"
manifestJsonOverride: "覆寫 manifest.json"
@@ -1348,10 +1375,12 @@ _serverSettings:
fanoutTimelineDescription: "如果啟用的話,檢索å„å€‹æ™‚é–“è»¸çš„æ€§èƒ½æœƒé¡¯è‘—ææ˜‡ï¼Œè³‡æ–™åº«çš„è² è·ä¹Ÿæœƒæ¸›å°‘。ä¸éŽï¼ŒRedis çš„è¨˜æ†¶é«”ä½¿ç”¨é‡æœƒå¢žåŠ ã€‚å¦‚æžœä¼ºæœå™¨çš„è¨˜æ†¶é«”å®¹é‡æ¯”較少或者é‹è¡Œä¸ç©©å®šï¼Œå¯ä»¥åœç”¨ã€‚"
fanoutTimelineDbFallback: "資料庫的回退"
fanoutTimelineDbFallbackDescription: "若啟用,在時間軸沒有快å–的情æ³ä¸‹å°‡åŸ·è¡Œå›žé€€è™•ç†ä»¥é¡å¤–查詢資料庫。若åœç”¨ï¼Œå¯ä»¥é€éŽä¸åŸ·è¡Œå›žé€€è™•ç†ä¾†é€²ä¸€æ­¥æ¸›å°‘伺æœå™¨çš„è² è·ï¼Œä½†æœƒé™åˆ¶å¯å–得的時間軸範åœã€‚"
+ inquiryUrl: "è¯çµ¡è¡¨å–®ç¶²å€"
+ inquiryUrlDescription: "指定伺æœå™¨é‹ç‡Ÿè€…çš„è¯çµ¡è¡¨å–®ç¶²å€æˆ–包å«é‹ç‡Ÿè€…è¯çµ¡è³‡è¨Šç¶²é çš„ç¶²å€ã€‚"
_accountMigration:
moveFrom: "從其他帳戶é·ç§»åˆ°é€™å€‹å¸³æˆ¶"
moveFromSub: "為å¦ä¸€å€‹å¸³æˆ¶å»ºç«‹åˆ¥å"
- moveFromLabel: "è¦é·ç§»éŽä¾†çš„帳戶:"
+ moveFromLabel: "è¦é·ç§»éŽä¾†çš„帳戶 #{n}"
moveFromDescription: "如果你想把追隨者從別的帳戶é·ç§»éŽä¾†ï¼Œå¿…須先在這裡建立別å。請務必在執行é·ç§»ä¹‹å‰å»ºç«‹åˆ¥åï¼è«‹åƒé€™æ¨£è¼¸å…¥è¦é·ç§»çš„帳戶:@person@instance.com"
moveTo: "將這個帳戶é·ç§»è‡³æ–°çš„帳戶"
moveToLabel: "è¦é·ç§»åˆ°çš„帳戶:"
@@ -1547,7 +1576,7 @@ _achievements:
_postedAt0min0sec:
title: "報時"
description: "在零分零秒發佈貼文"
- flavor: "啵ã€å•µã€å•µã€å—¶ãƒ¼ãƒ¼"
+ flavor: "啵.啵.啵.嗶ー"
_selfQuote:
title: "自我引用"
description: "引用了自己的貼文"
@@ -1682,6 +1711,11 @@ _role:
roleAssignedTo: "手動指派角色完æˆ"
isLocal: "本地使用者"
isRemote: "é ç«¯ä½¿ç”¨è€…"
+ isCat: "貓使用者"
+ isBot: "機器人使用者"
+ isSuspended: "è¢«åœæ¬Šçš„使用者"
+ isLocked: "上鎖的使用者"
+ isExplorable: "開啟了「使您的帳戶更容易被找到ã€åŠŸèƒ½çš„ä½¿ç”¨è€…"
createdLessThan: "帳戶加入時間ä¸è¶…éŽ"
createdMoreThan: "帳戶加入時間已超éŽ"
followersLessThanOrEq: "追隨者人數在~以下"
@@ -1751,6 +1785,7 @@ _plugin:
installWarn: "è«‹ä¸è¦å®‰è£ä¾†æºä¸æ˜Žçš„外掛。"
manage: "管ç†å¤–掛"
viewSource: "檢視原始碼"
+ viewLog: "顯示記錄 "
_preferencesBackups:
list: "已備份的設定檔"
saveNew: "å¦å­˜æ–°æª”"
@@ -1839,7 +1874,7 @@ _theme:
invalid: "佈景主題格å¼éŒ¯èª¤"
make: "製作佈景主題"
base: "基於"
- addConstant: "添加常數"
+ addConstant: "新增常數"
constant: "常數"
defaultValue: "é è¨­å€¼"
color: "é¡è‰²"
@@ -1914,22 +1949,22 @@ _soundSettings:
_ago:
future: "未來"
justNow: "剛剛"
- secondsAgo: "{n} ç§’å‰"
- minutesAgo: "{n} 分é˜å‰ "
- hoursAgo: "{n} å°æ™‚å‰"
- daysAgo: "{n} 天å‰"
- weeksAgo: "{n} 週å‰"
- monthsAgo: "{n} 個月å‰"
- yearsAgo: "{n} å¹´å‰"
+ secondsAgo: "{n}ç§’å‰"
+ minutesAgo: "{n}分é˜å‰"
+ hoursAgo: "{n}å°æ™‚å‰"
+ daysAgo: "{n}天å‰"
+ weeksAgo: "{n}周å‰"
+ monthsAgo: "{n}個月å‰"
+ yearsAgo: "{n}å¹´å‰"
invalid: "ç„¡"
_timeIn:
- seconds: "{n} 秒後"
- minutes: "{n} 分後"
- hours: "{n} å°æ™‚後"
- days: "{n} 日後"
- weeks: "{n} 週後"
- months: "{n} 個月後"
- years: "{n} 年後"
+ seconds: "{n}秒後"
+ minutes: "{n}分é˜å¾Œ"
+ hours: "{n}å°æ™‚後"
+ days: "{n}天後"
+ weeks: "{n}周後"
+ months: "{n}個月後"
+ years: "{n}年後"
_time:
second: "ç§’"
minute: "分é˜"
@@ -1940,7 +1975,6 @@ _2fa:
registerTOTP: "開始設定驗證應用程å¼"
step1: "首先,在您的è£ç½®ä¸Šå®‰è£é©—證程å¼ï¼Œä¾‹å¦‚ {a} 或 {b}。"
step2: "然後,掃æèž¢å¹•上的 QR 碼。"
- step2Click: "您å¯ä»¥é»žæ“Š QR 碼,以使用è£ç½®ä¸Šçš„é©—è­‰æ‡‰ç”¨ç¨‹å¼æˆ–金鑰環註冊。"
step2Uri: "使用桌é¢ç‰ˆæ‡‰ç”¨ç¨‹å¼æ™‚,請輸入以下的 URI"
step3Title: "輸入驗證碼"
step3: "è¼¸å…¥æ‡‰ç”¨ç¨‹å¼æ‰€æä¾›çš„æ¬Šæ–以完æˆè¨­å®šã€‚"
@@ -1964,6 +1998,7 @@ _2fa:
backupCodesDescription: "如果驗證應用程å¼ä¸èƒ½ç”¨äº†ï¼Œå¯ä»¥ä½¿ç”¨ä»¥ä¸‹çš„å‚™ç”¨é©—è­‰ç¢¼å­˜å–æ‚¨çš„帳戶。請務必妥善ä¿ç®¡é€™å€‹é©—證碼。æ¯å€‹é©—證碼åªèƒ½ä½¿ç”¨ä¸€æ¬¡ã€‚"
backupCodeUsedWarning: "已使用備用驗證碼。如果無法使用驗證應用程å¼ï¼Œè«‹ç›¡å¿«é‡æ–°è¨­å®šã€‚"
backupCodesExhaustedWarning: "已使用所有備用驗證碼。如果無法使用驗證應用程å¼ï¼Œå‰‡å°‡ç„¡æ³•å†å­˜å–æ‚¨çš„å¸³æˆ¶ã€‚è«‹é‡æ–°è¨­å®šæ‚¨çš„驗證應用程å¼ã€‚"
+ moreDetailedGuideHere: "請點擊此處查看詳細說明。"
_permissions:
"read:account": "查看我的帳戶資訊"
"write:account": "更改我的帳戶資訊"
@@ -2007,19 +2042,18 @@ _permissions:
"read:admin:index-stats": "查看資料庫索引的相關資訊"
"read:admin:table-stats": "查看資料庫表格的相關資訊"
"read:admin:user-ips": "查看使用者的 IP ä½å€"
- "read:admin:meta": "查看實例的元資料"
+ "read:admin:meta": "查看實例的詮釋資料"
"write:admin:reset-password": "é‡è¨­ä½¿ç”¨è€…的密碼"
"write:admin:resolve-abuse-user-report": "解決來自使用者的檢舉"
"write:admin:send-email": "發é€éƒµä»¶"
"read:admin:server-info": "查看伺æœå™¨çš„資訊"
"read:admin:show-moderation-log": "查看審查紀錄"
"read:admin:show-user": "查看使用者的ç§å¯†è³‡è¨Š"
- "read:admin:show-users": "查看使用者的ç§å¯†è³‡è¨Š"
"write:admin:suspend-user": "å‡çµä½¿ç”¨è€…"
"write:admin:unset-user-avatar": "刪除使用者的頭åƒ"
"write:admin:unset-user-banner": "刪除使用者的橫幅"
"write:admin:unsuspend-user": "解除å‡çµä½¿ç”¨è€…"
- "write:admin:meta": "編輯實例的元資料"
+ "write:admin:meta": "編輯實例的詮釋資料"
"write:admin:user-note": "編輯審查筆記"
"write:admin:roles": "編輯角色"
"read:admin:roles": "查看角色"
@@ -2067,13 +2101,13 @@ _antennaSources:
userList: "來自特定清單中的貼文"
userBlacklist: "除指定使用者外的所有貼文"
_weekday:
- sunday: "週日"
- monday: "週一"
- tuesday: "週二"
- wednesday: "週三"
- thursday: "週四"
- friday: "週五"
- saturday: "週六"
+ sunday: "星期天"
+ monday: "星期一"
+ tuesday: "星期二"
+ wednesday: "星期三"
+ thursday: "星期四"
+ friday: "星期五"
+ saturday: "星期六"
_widgets:
profile: "個人檔案"
instanceInfo: "伺æœå™¨è³‡è¨Š"
@@ -2122,7 +2156,7 @@ _poll:
deadlineDate: "截止日期"
deadlineTime: "å°æ™‚"
duration: "時長"
- votesCount: "{n} 票"
+ votesCount: "{n}票"
totalVotes: "åˆè¨ˆ {n} 票"
vote: "投票"
showResult: "é¡¯ç¤ºçµæžœ"
@@ -2155,7 +2189,7 @@ _postForm:
e: "寫些什麼å§â€¦â€¦"
f: "éœå¾…發文……"
_profile:
- name: "å稱"
+ name: "åå­—"
username: "使用者å稱"
description: "關於我"
youCanIncludeHashtags: "你也å¯ä»¥åœ¨ã€Œé—œæ–¼æˆ‘ã€ä¸­åŠ ä¸Š #tag"
@@ -2188,7 +2222,7 @@ _charts:
notesIncDec: "貼文増減"
localNotesIncDec: "本地貼文増減"
remoteNotesIncDec: "é ç«¯è²¼æ–‡æ•¸ç›®å¢žå‡"
- notesTotal: "貼文åˆå…±"
+ notesTotal: "貼文總數"
filesIncDec: "檔案增減"
filesTotal: "檔案總數"
storageUsageIncDec: "儲存空間增減"
@@ -2213,10 +2247,10 @@ _timelines:
_play:
new: "新增 Play"
edit: "編輯 Play"
- created: "已新增Play "
- updated: "已更新Play "
+ created: "已新增 Play "
+ updated: "已更新 Play "
deleted: "已刪除 Play"
- pageSetting: "Play設定"
+ pageSetting: "Play 設定"
editThisPage: "編輯此 Play"
viewSource: "檢視原始碼"
my: "自己的 Play"
@@ -2225,10 +2259,11 @@ _play:
title: "標題"
script: "腳本"
summary: "æè¿°"
+ visibilityDescription: "如果您將其設為ç§å¯†ï¼Œå®ƒå°‡ä¸å†é¡¯ç¤ºåœ¨æ‚¨çš„個人資料中,但知é“該 URL 的人ä»ç„¶å¯ä»¥å­˜å–它。"
_pages:
newPage: "建立é é¢"
editPage: "編輯é é¢"
- readPage: "正檢視原始碼"
+ readPage: "正在檢視原始碼"
created: "é é¢å·²å»ºç«‹"
updated: "é é¢å·²æ›´æ–°"
deleted: "é é¢å·²è¢«åˆªé™¤"
@@ -2255,7 +2290,7 @@ _pages:
hideTitleWhenPinned: "被置頂於個人資料時隱è—é é¢æ¨™é¡Œ"
font: "å­—åž‹"
fontSerif: "襯線體"
- fontSansSerif: "無襯線體"
+ fontSansSerif: "黑體"
eyeCatchingImageSet: "設定å°é¢å½±åƒ"
eyeCatchingImageRemove: "刪除å°é¢å½±åƒ"
chooseBlock: "新增方塊"
@@ -2269,6 +2304,8 @@ _pages:
section: "倿®µ"
image: "圖片"
button: "按鈕"
+ dynamic: "動態方塊"
+ dynamicDescription: "這個方塊已經廢止,ç¾åœ¨é–‹å§‹è«‹ä½¿ç”¨ {play}。"
note: "嵌å¼è²¼æ–‡"
_note:
id: "貼文ID"
@@ -2298,6 +2335,7 @@ _notification:
sendTestNotification: "ç™¼é€æ¸¬è©¦é€šçŸ¥"
notificationWillBeDisplayedLikeThis: "通知會以這樣的方å¼é¡¯ç¤º"
reactedBySomeUsers: "{n}人åšå‡ºäº†å應"
+ likedBySomeUsers: "{n} 人按了讚"
renotedBySomeUsers: "{n}人åšäº†è½‰ç™¼"
followedBySomeUsers: "被{n}人追隨了"
flushNotification: "é‡ç½®é€šçŸ¥æ­·å²ç´€éŒ„"
@@ -2324,6 +2362,7 @@ _deck:
alwaysShowMainColumn: "總是顯示主欄"
columnAlign: "å°é½Šæ¬„ä½"
addColumn: "新增欄ä½"
+ newNoteNotificationSettings: "新貼文通知的設定"
configureColumn: "欄ä½çš„設定"
swapLeft: "å‘左移動"
swapRight: "å‘å³ç§»å‹•"
@@ -2362,7 +2401,7 @@ _drivecleaner:
orderByCreatedAtAsc: "按新增日期é™åºæŽ’列"
_webhookSettings:
createWebhook: "建立 Webhook"
- name: "å稱"
+ name: "åå­—"
secret: "密鑰"
events: "何時é‹è¡Œ Webhook"
active: "已啟用"
@@ -2524,3 +2563,21 @@ _reversi:
_offlineScreen:
title: "離線ï¼ç„¡æ³•連接伺æœå™¨"
header: "無法連接伺æœå™¨"
+_urlPreviewSetting:
+ title: "URL é è¦½è¨­å®š"
+ enable: "啟用 URL é è¦½"
+ timeout: "å–å¾—é è¦½çš„逾時時間 (ms)"
+ timeoutDescription: "è‹¥å–å¾—é è¦½æ‰€éœ€çš„æ™‚é–“è¶…éŽé€™å€‹å€¼ï¼Œå‰‡ä¸æœƒç”¢ç”Ÿé è¦½ã€‚"
+ maximumContentLength: "Content-Length 的最大値 (byte)"
+ maximumContentLengthDescription: "è‹¥ Content-Length è¶…éŽé€™å€‹å€¼ï¼Œå‰‡ä¸æœƒç”¢ç”Ÿé è¦½ã€‚"
+ requireContentLength: "僅在能夠å–å¾— Content-Length 時,æ‰ç”¢ç”Ÿé è¦½ã€‚"
+ requireContentLengthDescription: "è‹¥å°æ–¹çš„伺æœå™¨æœªå›žå‚³ Content -Lengthï¼Œå‰‡ä¸æœƒç”¢ç”Ÿé è¦½ã€‚"
+ userAgent: "User-Agent"
+ userAgentDescription: "設定ç²å–é è¦½æ™‚使用的 User-Agent 。如果留空,將使用é è¨­çš„ User-Agent 。"
+ summaryProxy: "產生é è¦½çš„代ç†ç«¯é»ž"
+ summaryProxyDescription: "使用摘è¦ä»£ç†ç¨‹å¼è€Œä¸æ˜¯ Misskey 本身產生é è¦½ã€‚"
+ summaryProxyDescription2: "ä»¥ä¸‹åƒæ•¸æœƒä½œç‚ºæŸ¥è©¢å­—串連çµåˆ°ä»£ç†ã€‚如果代ç†ç«¯ä¸æ”¯æ´ï¼Œé€™äº›è¨­å®šå°‡è¢«å¿½ç•¥ã€‚"
+_mediaControls:
+ pip: "畫中畫"
+ playbackRate: "播放速度"
+ loop: "循環播放"
diff --git a/package.json b/package.json
index f0a0ef50fe..ec7134f36e 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "sharkey",
- "version": "2024.5.0-beta.1",
+ "version": "2024.5.0-rc.9",
"codename": "shonk",
"repository": {
"type": "git",
diff --git a/packages/backend/migration/1716129964060-ChannelIdDenormalizedForMiPoll.js b/packages/backend/migration/1716129964060-ChannelIdDenormalizedForMiPoll.js
new file mode 100644
index 0000000000..f736378c04
--- /dev/null
+++ b/packages/backend/migration/1716129964060-ChannelIdDenormalizedForMiPoll.js
@@ -0,0 +1,21 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class ChannelIdDenormalizedForMiPoll1716129964060 {
+ name = 'ChannelIdDenormalizedForMiPoll1716129964060'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "poll" ADD "channelId" character varying(32)`);
+ await queryRunner.query(`COMMENT ON COLUMN "poll"."channelId" IS '[Denormalized]'`);
+ await queryRunner.query(`CREATE INDEX "IDX_c1240fcc9675946ea5d6c2860e" ON "poll" ("channelId") `);
+ await queryRunner.query(`UPDATE "poll" SET "channelId" = "note"."channelId" FROM "note" WHERE "poll"."noteId" = "note"."id"`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`DROP INDEX "public"."IDX_c1240fcc9675946ea5d6c2860e"`);
+ await queryRunner.query(`COMMENT ON COLUMN "poll"."channelId" IS '[Denormalized]'`);
+ await queryRunner.query(`ALTER TABLE "poll" DROP COLUMN "channelId"`);
+ }
+}
diff --git a/packages/backend/migration/1716345015347-NotRespondingSince.js b/packages/backend/migration/1716345015347-NotRespondingSince.js
new file mode 100644
index 0000000000..fc4ee6639a
--- /dev/null
+++ b/packages/backend/migration/1716345015347-NotRespondingSince.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class NotRespondingSince1716345015347 {
+ name = 'NotRespondingSince1716345015347'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "instance" ADD "notRespondingSince" TIMESTAMP WITH TIME ZONE`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "instance" DROP COLUMN "notRespondingSince"`);
+ }
+}
diff --git a/packages/backend/migration/1716447138870-SuspensionStateInsteadOfIsSspended.js b/packages/backend/migration/1716447138870-SuspensionStateInsteadOfIsSspended.js
new file mode 100644
index 0000000000..4808a9a3db
--- /dev/null
+++ b/packages/backend/migration/1716447138870-SuspensionStateInsteadOfIsSspended.js
@@ -0,0 +1,50 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class SuspensionStateInsteadOfIsSspended1716345771510 {
+ name = 'SuspensionStateInsteadOfIsSspended1716345771510'
+
+ async up(queryRunner) {
+ await queryRunner.query(`CREATE TYPE "public"."instance_suspensionstate_enum" AS ENUM('none', 'manuallySuspended', 'goneSuspended', 'autoSuspendedForNotResponding')`);
+
+ await queryRunner.query(`DROP INDEX "public"."IDX_34500da2e38ac393f7bb6b299c"`);
+
+ await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "isSuspended" TO "suspensionState"`);
+
+ await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" DROP DEFAULT`);
+
+ await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" TYPE "public"."instance_suspensionstate_enum" USING (
+ CASE "suspensionState"
+ WHEN TRUE THEN 'manuallySuspended'::instance_suspensionstate_enum
+ ELSE 'none'::instance_suspensionstate_enum
+ END
+ )`);
+
+ await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" SET DEFAULT 'none'`);
+
+ await queryRunner.query(`CREATE INDEX "IDX_3ede46f507c87ad698051d56a8" ON "instance" ("suspensionState") `);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`DROP INDEX "public"."IDX_3ede46f507c87ad698051d56a8"`);
+
+ await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" DROP DEFAULT`);
+
+ await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" TYPE boolean USING (
+ CASE "suspensionState"
+ WHEN 'none'::instance_suspensionstate_enum THEN FALSE
+ ELSE TRUE
+ END
+ )`);
+
+ await queryRunner.query(`ALTER TABLE "instance" ALTER COLUMN "suspensionState" SET DEFAULT false`);
+
+ await queryRunner.query(`ALTER TABLE "instance" RENAME COLUMN "suspensionState" TO "isSuspended"`);
+
+ await queryRunner.query(`CREATE INDEX "IDX_34500da2e38ac393f7bb6b299c" ON "instance" ("isSuspended") `);
+
+ await queryRunner.query(`DROP TYPE "public"."instance_suspensionstate_enum"`);
+ }
+}
diff --git a/packages/backend/migration/1716450883149-RemoveAntennaNotify.js b/packages/backend/migration/1716450883149-RemoveAntennaNotify.js
new file mode 100644
index 0000000000..b5a2441855
--- /dev/null
+++ b/packages/backend/migration/1716450883149-RemoveAntennaNotify.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class RemoveAntennaNotify1716450883149 {
+ name = 'RemoveAntennaNotify1716450883149'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "antenna" DROP COLUMN "notify"`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "antenna" ADD "notify" boolean NOT NULL`);
+ }
+}
diff --git a/packages/backend/migration/1717117195275-inquiryUrl.js b/packages/backend/migration/1717117195275-inquiryUrl.js
new file mode 100644
index 0000000000..29ca31af14
--- /dev/null
+++ b/packages/backend/migration/1717117195275-inquiryUrl.js
@@ -0,0 +1,16 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export class InquiryUrl1717117195275 {
+ name = 'InquiryUrl1717117195275'
+
+ async up(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" ADD "inquiryUrl" character varying(1024)`);
+ }
+
+ async down(queryRunner) {
+ await queryRunner.query(`ALTER TABLE "meta" DROP COLUMN "inquiryUrl"`);
+ }
+}
diff --git a/packages/backend/package.json b/packages/backend/package.json
index 5d4e9b93a3..b05dc10c11 100644
--- a/packages/backend/package.json
+++ b/packages/backend/package.json
@@ -4,7 +4,7 @@
"private": true,
"type": "module",
"engines": {
- "node": ">=20.10.0"
+ "node": "^20.10.0"
},
"scripts": {
"start": "node ./built/boot/entry.js",
@@ -84,6 +84,8 @@
"@nestjs/core": "10.3.8",
"@nestjs/testing": "10.3.8",
"@peertube/http-signature": "1.7.0",
+ "@sentry/node": "^8.5.0",
+ "@sentry/profiling-node": "^8.5.0",
"@simplewebauthn/server": "10.0.0",
"@sinonjs/fake-timers": "11.2.2",
"@smithy/node-http-handler": "2.5.0",
@@ -119,7 +121,7 @@
"form-data": "4.0.0",
"glob": "10.3.10",
"got": "14.2.1",
- "happy-dom": "14.7.1",
+ "happy-dom": "10.0.3",
"hpagent": "1.2.0",
"htmlescape": "1.1.1",
"http-link-header": "1.1.3",
diff --git a/packages/backend/src/boot/entry.ts b/packages/backend/src/boot/entry.ts
index 3882686fdc..d1744b4b4b 100644
--- a/packages/backend/src/boot/entry.ts
+++ b/packages/backend/src/boot/entry.ts
@@ -15,6 +15,7 @@ import Logger from '@/logger.js';
import { envOption } from '../env.js';
import { masterMain } from './master.js';
import { workerMain } from './worker.js';
+import { readyRef } from './ready.js';
import 'reflect-metadata';
@@ -79,6 +80,8 @@ async function main() {
await workerMain();
}
+ readyRef.value = true;
+
// ユニットテスト時ã«MisskeyãŒå­ãƒ—ロセスã§èµ·å‹•ã•ã‚ŒãŸæ™‚ã®ãŸã‚
// ãれ以外ã®ã¨ã㯠process.send ã¯ä½¿ãˆãªã„ã®ã§å¼¾ã
if (process.send) {
diff --git a/packages/backend/src/boot/master.ts b/packages/backend/src/boot/master.ts
index aafd43beea..303ba94207 100644
--- a/packages/backend/src/boot/master.ts
+++ b/packages/backend/src/boot/master.ts
@@ -10,6 +10,8 @@ import * as os from 'node:os';
import cluster from 'node:cluster';
import chalk from 'chalk';
import chalkTemplate from 'chalk-template';
+import * as Sentry from '@sentry/node';
+import { nodeProfilingIntegration } from '@sentry/profiling-node';
import Logger from '@/logger.js';
import { loadConfig } from '@/config.js';
import type { Config } from '@/config.js';
@@ -74,6 +76,24 @@ export async function masterMain() {
bootLogger.succ('Sharkey initialized');
+ if (config.sentryForBackend) {
+ Sentry.init({
+ integrations: [
+ ...(config.sentryForBackend.enableNodeProfiling ? [nodeProfilingIntegration()] : []),
+ ],
+
+ // Performance Monitoring
+ tracesSampleRate: 1.0, // Capture 100% of the transactions
+
+ // Set sampling rate for profiling - this is relative to tracesSampleRate
+ profilesSampleRate: 1.0,
+
+ maxBreadcrumbs: 0,
+
+ ...config.sentryForBackend.options,
+ });
+ }
+
if (envOption.disableClustering) {
if (envOption.onlyServer) {
await server();
diff --git a/packages/backend/src/boot/ready.ts b/packages/backend/src/boot/ready.ts
new file mode 100644
index 0000000000..591ae5cb58
--- /dev/null
+++ b/packages/backend/src/boot/ready.ts
@@ -0,0 +1,6 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+export const readyRef = { value: false };
diff --git a/packages/backend/src/config.ts b/packages/backend/src/config.ts
index 7eb5b86b18..92c3e34889 100644
--- a/packages/backend/src/config.ts
+++ b/packages/backend/src/config.ts
@@ -8,6 +8,7 @@ import { fileURLToPath } from 'node:url';
import { dirname, resolve } from 'node:path';
import * as yaml from 'js-yaml';
import { globSync } from 'glob';
+import * as Sentry from '@sentry/node';
import type { RedisOptions } from 'ioredis';
type RedisOptionsSource = Partial<RedisOptions> & {
@@ -57,6 +58,8 @@ type Source = {
index: string;
scope?: 'local' | 'global' | string[];
};
+ sentryForBackend?: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; };
+ sentryForFrontend?: { options: Partial<Sentry.NodeOptions> };
publishTarballInsteadOfProvideRepositoryUrl?: boolean;
@@ -174,6 +177,8 @@ export type Config = {
redisForPubsub: RedisOptions & RedisOptionsSource;
redisForJobQueue: RedisOptions & RedisOptionsSource;
redisForTimelines: RedisOptions & RedisOptionsSource;
+ sentryForBackend: { options: Partial<Sentry.NodeOptions>; enableNodeProfiling: boolean; } | undefined;
+ sentryForFrontend: { options: Partial<Sentry.NodeOptions> } | undefined;
perChannelMaxNoteCacheCount: number;
perUserNotificationsMaxCount: number;
deactivateAntennaThreshold: number;
@@ -251,6 +256,8 @@ export function loadConfig(): Config {
redisForPubsub: config.redisForPubsub ? convertRedisOptions(config.redisForPubsub, host) : redis,
redisForJobQueue: config.redisForJobQueue ? convertRedisOptions(config.redisForJobQueue, host) : redis,
redisForTimelines: config.redisForTimelines ? convertRedisOptions(config.redisForTimelines, host) : redis,
+ sentryForBackend: config.sentryForBackend,
+ sentryForFrontend: config.sentryForFrontend,
id: config.id,
proxy: config.proxy,
proxySmtp: config.proxySmtp,
diff --git a/packages/backend/src/core/AnnouncementService.ts b/packages/backend/src/core/AnnouncementService.ts
index b298a70929..9b60df2cae 100644
--- a/packages/backend/src/core/AnnouncementService.ts
+++ b/packages/backend/src/core/AnnouncementService.ts
@@ -4,13 +4,14 @@
*/
import { Inject, Injectable } from '@nestjs/common';
-import { Brackets } from 'typeorm';
+import { Brackets, EntityNotFoundError } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { MiUser } from '@/models/User.js';
import type { AnnouncementReadsRepository, AnnouncementsRepository, MiAnnouncement, MiAnnouncementRead, UsersRepository } from '@/models/_.js';
import { bindThis } from '@/decorators.js';
import { Packed } from '@/misc/json-schema.js';
import { IdService } from '@/core/IdService.js';
+import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
import { GlobalEventService } from '@/core/GlobalEventService.js';
import { ModerationLogService } from '@/core/ModerationLogService.js';
@@ -29,6 +30,7 @@ export class AnnouncementService {
private idService: IdService,
private globalEventService: GlobalEventService,
private moderationLogService: ModerationLogService,
+ private announcementEntityService: AnnouncementEntityService,
) {
}
@@ -79,7 +81,7 @@ export class AnnouncementService {
userId: values.userId,
}).then(x => this.announcementsRepository.findOneByOrFail(x.identifiers[0]));
- const packed = (await this.packMany([announcement]))[0];
+ const packed = await this.announcementEntityService.pack(announcement);
if (values.userId) {
this.globalEventService.publishMainStream(values.userId, 'announcementCreated', {
@@ -178,6 +180,24 @@ export class AnnouncementService {
}
@bindThis
+ public async getAnnouncement(announcementId: MiAnnouncement['id'], me: MiUser | null): Promise<Packed<'Announcement'>> {
+ const announcement = await this.announcementsRepository.findOneByOrFail({ id: announcementId });
+ if (me) {
+ if (announcement.userId && announcement.userId !== me.id) {
+ throw new EntityNotFoundError(this.announcementsRepository.metadata.target, { id: announcementId });
+ }
+
+ const read = await this.announcementReadsRepository.findOneBy({
+ announcementId: announcement.id,
+ userId: me.id,
+ });
+ return this.announcementEntityService.pack({ ...announcement, isRead: read !== null }, me);
+ } else {
+ return this.announcementEntityService.pack(announcement, null);
+ }
+ }
+
+ @bindThis
public async read(user: MiUser, announcementId: MiAnnouncement['id']): Promise<void> {
try {
await this.announcementReadsRepository.insert({
@@ -193,29 +213,4 @@ export class AnnouncementService {
this.globalEventService.publishMainStream(user.id, 'readAllAnnouncements');
}
}
-
- @bindThis
- public async packMany(
- announcements: MiAnnouncement[],
- me?: { id: MiUser['id'] } | null | undefined,
- options?: {
- reads?: MiAnnouncementRead[];
- },
- ): Promise<Packed<'Announcement'>[]> {
- const reads = me ? (options?.reads ?? await this.getReads(me.id)) : [];
- return announcements.map(announcement => ({
- id: announcement.id,
- createdAt: this.idService.parse(announcement.id).date.toISOString(),
- updatedAt: announcement.updatedAt?.toISOString() ?? null,
- text: announcement.text,
- title: announcement.title,
- imageUrl: announcement.imageUrl,
- icon: announcement.icon,
- display: announcement.display,
- needConfirmationToRead: announcement.needConfirmationToRead,
- silence: announcement.silence,
- forYou: announcement.userId === me?.id,
- isRead: reads.some(read => read.announcementId === announcement.id),
- }));
- }
}
diff --git a/packages/backend/src/core/CoreModule.ts b/packages/backend/src/core/CoreModule.ts
index e68cb86af0..9baec9a59f 100644
--- a/packages/backend/src/core/CoreModule.ts
+++ b/packages/backend/src/core/CoreModule.ts
@@ -84,6 +84,7 @@ import ApRequestChart from './chart/charts/ap-request.js';
import { ChartManagementService } from './chart/ChartManagementService.js';
import { AbuseUserReportEntityService } from './entities/AbuseUserReportEntityService.js';
+import { AnnouncementEntityService } from './entities/AnnouncementEntityService.js';
import { AntennaEntityService } from './entities/AntennaEntityService.js';
import { AppEntityService } from './entities/AppEntityService.js';
import { AuthSessionEntityService } from './entities/AuthSessionEntityService.js';
@@ -223,6 +224,7 @@ const $ApRequestChart: Provider = { provide: 'ApRequestChart', useExisting: ApRe
const $ChartManagementService: Provider = { provide: 'ChartManagementService', useExisting: ChartManagementService };
const $AbuseUserReportEntityService: Provider = { provide: 'AbuseUserReportEntityService', useExisting: AbuseUserReportEntityService };
+const $AnnouncementEntityService: Provider = { provide: 'AnnouncementEntityService', useExisting: AnnouncementEntityService };
const $AntennaEntityService: Provider = { provide: 'AntennaEntityService', useExisting: AntennaEntityService };
const $AppEntityService: Provider = { provide: 'AppEntityService', useExisting: AppEntityService };
const $AuthSessionEntityService: Provider = { provide: 'AuthSessionEntityService', useExisting: AuthSessionEntityService };
@@ -363,6 +365,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
ChartManagementService,
AbuseUserReportEntityService,
+ AnnouncementEntityService,
AntennaEntityService,
AppEntityService,
AuthSessionEntityService,
@@ -499,6 +502,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$ChartManagementService,
$AbuseUserReportEntityService,
+ $AnnouncementEntityService,
$AntennaEntityService,
$AppEntityService,
$AuthSessionEntityService,
@@ -635,6 +639,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
ChartManagementService,
AbuseUserReportEntityService,
+ AnnouncementEntityService,
AntennaEntityService,
AppEntityService,
AuthSessionEntityService,
@@ -770,6 +775,7 @@ const $ApQuestionService: Provider = { provide: 'ApQuestionService', useExisting
$ChartManagementService,
$AbuseUserReportEntityService,
+ $AnnouncementEntityService,
$AntennaEntityService,
$AppEntityService,
$AuthSessionEntityService,
diff --git a/packages/backend/src/core/CustomEmojiService.ts b/packages/backend/src/core/CustomEmojiService.ts
index 2fbba4d6c5..bfbc2b172d 100644
--- a/packages/backend/src/core/CustomEmojiService.ts
+++ b/packages/backend/src/core/CustomEmojiService.ts
@@ -369,10 +369,11 @@ export class CustomEmojiService implements OnApplicationShutdown {
@bindThis
public async populateEmojis(emojiNames: string[], noteUserHost: string | null): Promise<Record<string, string>> {
const emojis = await Promise.all(emojiNames.map(x => this.populateEmoji(x, noteUserHost)));
- const res = {} as any;
+ const res = {} as Record<string, string>;
for (let i = 0; i < emojiNames.length; i++) {
- if (emojis[i] != null) {
- res[emojiNames[i]] = emojis[i];
+ const resolvedEmoji = emojis[i];
+ if (resolvedEmoji != null) {
+ res[emojiNames[i]] = resolvedEmoji;
}
}
return res;
diff --git a/packages/backend/src/core/DriveService.ts b/packages/backend/src/core/DriveService.ts
index dc84ea1999..af5451bfc8 100644
--- a/packages/backend/src/core/DriveService.ts
+++ b/packages/backend/src/core/DriveService.ts
@@ -477,14 +477,20 @@ export class DriveService {
if (user && !force) {
// Check if there is a file with the same hash
- const much = await this.driveFilesRepository.findOneBy({
+ const matched = await this.driveFilesRepository.findOneBy({
md5: info.md5,
userId: user.id,
});
- if (much) {
- this.registerLogger.info(`file with same hash is found: ${much.id}`);
- return much;
+ if (matched) {
+ this.registerLogger.info(`file with same hash is found: ${matched.id}`);
+ if (sensitive && !matched.isSensitive) {
+ // The file is federated as sensitive for this time, but was federated as non-sensitive before.
+ // Therefore, update the file to sensitive.
+ await this.driveFilesRepository.update({ id: matched.id }, { isSensitive: true });
+ matched.isSensitive = true;
+ }
+ return matched;
}
}
diff --git a/packages/backend/src/core/FanoutTimelineEndpointService.ts b/packages/backend/src/core/FanoutTimelineEndpointService.ts
index 2f4d98fab4..5725c795ed 100644
--- a/packages/backend/src/core/FanoutTimelineEndpointService.ts
+++ b/packages/backend/src/core/FanoutTimelineEndpointService.ts
@@ -62,8 +62,8 @@ export class FanoutTimelineEndpointService {
// 呼ã³å‡ºã—å…ƒã¨ä»¥ä¸‹ã®å‡¦ç†ã‚’シンプルã«ã™ã‚‹ãŸã‚ã«dbFallbackã‚’ç½®ãæ›ãˆã‚‹
if (!ps.useDbFallback) ps.dbFallback = () => Promise.resolve([]);
- const shouldPrepend = ps.sinceId && !ps.untilId;
- const idCompare: (a: string, b: string) => number = shouldPrepend ? (a, b) => a < b ? -1 : 1 : (a, b) => a > b ? -1 : 1;
+ const ascending = ps.sinceId && !ps.untilId;
+ const idCompare: (a: string, b: string) => number = ascending ? (a, b) => a < b ? -1 : 1 : (a, b) => a > b ? -1 : 1;
const redisResult = await this.fanoutTimelineService.getMulti(ps.redisTimelines, ps.untilId, ps.sinceId);
@@ -148,9 +148,7 @@ export class FanoutTimelineEndpointService {
if (ps.allowPartial ? redisTimeline.length !== 0 : redisTimeline.length >= ps.limit) {
// å分Redisã‹ã‚‰ã¨ã‚ŒãŸ
- const result = redisTimeline.slice(0, ps.limit);
- if (shouldPrepend) result.reverse();
- return result;
+ return redisTimeline.slice(0, ps.limit);
}
}
@@ -158,8 +156,7 @@ export class FanoutTimelineEndpointService {
const remainingToRead = ps.limit - redisTimeline.length;
let dbUntil: string | null;
let dbSince: string | null;
- if (shouldPrepend) {
- redisTimeline.reverse();
+ if (ascending) {
dbUntil = ps.untilId;
dbSince = noteIds[noteIds.length - 1];
} else {
@@ -167,7 +164,7 @@ export class FanoutTimelineEndpointService {
dbSince = ps.sinceId;
}
const gotFromDb = await ps.dbFallback(dbUntil, dbSince, remainingToRead);
- return shouldPrepend ? [...gotFromDb, ...redisTimeline] : [...redisTimeline, ...gotFromDb];
+ return [...redisTimeline, ...gotFromDb];
}
return await ps.dbFallback(ps.untilId, ps.sinceId, ps.limit);
diff --git a/packages/backend/src/core/FetchInstanceMetadataService.ts b/packages/backend/src/core/FetchInstanceMetadataService.ts
index 8d173855f3..aa16468ecb 100644
--- a/packages/backend/src/core/FetchInstanceMetadataService.ts
+++ b/packages/backend/src/core/FetchInstanceMetadataService.ts
@@ -154,7 +154,7 @@ export class FetchInstanceMetadataService {
throw new Error('No wellknown links');
}
- const links = wellknown.links as any[];
+ const links = wellknown.links as ({ rel: string, href: string; })[];
const link1_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/1.0');
const link2_0 = links.find(link => link.rel === 'http://nodeinfo.diaspora.software/ns/schema/2.0');
diff --git a/packages/backend/src/core/NoteCreateService.ts b/packages/backend/src/core/NoteCreateService.ts
index 1f575c083a..4ff0d0fbef 100644
--- a/packages/backend/src/core/NoteCreateService.ts
+++ b/packages/backend/src/core/NoteCreateService.ts
@@ -662,6 +662,7 @@ export class NoteCreateService implements OnApplicationShutdown {
noteVisibility: insert.visibility,
userId: user.id,
userHost: user.host,
+ channelId: insert.channelId,
});
await transactionalEntityManager.insert(MiPoll, poll);
diff --git a/packages/backend/src/core/activitypub/ApInboxService.ts b/packages/backend/src/core/activitypub/ApInboxService.ts
index 6ff03b22e1..cf66816566 100644
--- a/packages/backend/src/core/activitypub/ApInboxService.ts
+++ b/packages/backend/src/core/activitypub/ApInboxService.ts
@@ -28,6 +28,7 @@ import type { UsersRepository, NotesRepository, FollowingsRepository, AbuseUserR
import { bindThis } from '@/decorators.js';
import type { MiRemoteUser } from '@/models/User.js';
import { isNotNull } from '@/misc/is-not-null.js';
+import { GlobalEventService } from '@/core/GlobalEventService.js';
import { getApHrefNullable, getApId, getApIds, getApType, isAccept, isActor, isAdd, isAnnounce, isBlock, isCollection, isCollectionOrOrderedCollection, isCreate, isDelete, isFlag, isFollow, isLike, isMove, isPost, isReject, isRemove, isTombstone, isUndo, isUpdate, validActor, validPost } from './type.js';
import { ApNoteService } from './models/ApNoteService.js';
import { ApLoggerService } from './ApLoggerService.js';
@@ -36,9 +37,8 @@ import { ApResolverService } from './ApResolverService.js';
import { ApAudienceService } from './ApAudienceService.js';
import { ApPersonService } from './models/ApPersonService.js';
import { ApQuestionService } from './models/ApQuestionService.js';
-import { GlobalEventService } from '@/core/GlobalEventService.js';
import type { Resolver } from './ApResolverService.js';
-import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove } from './type.js';
+import type { IAccept, IAdd, IAnnounce, IBlock, ICreate, IDelete, IFlag, IFollow, ILike, IObject, IReject, IRemove, IUndo, IUpdate, IMove, IPost } from './type.js';
@Injectable()
export class ApInboxService {
@@ -90,13 +90,15 @@ export class ApInboxService {
}
@bindThis
- public async performActivity(actor: MiRemoteUser, activity: IObject): Promise<void> {
+ public async performActivity(actor: MiRemoteUser, activity: IObject): Promise<string | void> {
+ let result = undefined as string | void;
if (isCollectionOrOrderedCollection(activity)) {
+ const results = [] as [string, string | void][];
const resolver = this.apResolverService.createResolver();
for (const item of toArray(isCollection(activity) ? activity.items : activity.orderedItems)) {
const act = await resolver.resolve(item);
try {
- await this.performOneActivity(actor, act);
+ results.push([getApId(item), await this.performOneActivity(actor, act)]);
} catch (err) {
if (err instanceof Error || typeof err === 'string') {
this.logger.error(err);
@@ -105,8 +107,13 @@ export class ApInboxService {
}
}
}
+
+ const hasReason = results.some(([, reason]) => (reason != null && !reason.startsWith('ok')));
+ if (hasReason) {
+ result = results.map(([id, reason]) => `${id}: ${reason}`).join('\n');
+ }
} else {
- await this.performOneActivity(actor, activity);
+ result = await this.performOneActivity(actor, activity);
}
// ã¤ã„ã§ã«ãƒªãƒ¢ãƒ¼ãƒˆãƒ¦ãƒ¼ã‚¶ãƒ¼ã®æƒ…å ±ãŒå¤ã‹ã£ãŸã‚‰æ›´æ–°ã—ã¦ãŠã
@@ -117,42 +124,43 @@ export class ApInboxService {
});
}
}
+ return result;
}
@bindThis
- public async performOneActivity(actor: MiRemoteUser, activity: IObject): Promise<void> {
+ public async performOneActivity(actor: MiRemoteUser, activity: IObject): Promise<string | void> {
if (actor.isSuspended) return;
if (isCreate(activity)) {
- await this.create(actor, activity);
+ return await this.create(actor, activity);
} else if (isDelete(activity)) {
- await this.delete(actor, activity);
+ return await this.delete(actor, activity);
} else if (isUpdate(activity)) {
- await this.update(actor, activity);
+ return await this.update(actor, activity);
} else if (isFollow(activity)) {
- await this.follow(actor, activity);
+ return await this.follow(actor, activity);
} else if (isAccept(activity)) {
- await this.accept(actor, activity);
+ return await this.accept(actor, activity);
} else if (isReject(activity)) {
- await this.reject(actor, activity);
+ return await this.reject(actor, activity);
} else if (isAdd(activity)) {
- await this.add(actor, activity).catch(err => this.logger.error(err));
+ return await this.add(actor, activity);
} else if (isRemove(activity)) {
- await this.remove(actor, activity).catch(err => this.logger.error(err));
+ return await this.remove(actor, activity);
} else if (isAnnounce(activity)) {
- await this.announce(actor, activity);
+ return await this.announce(actor, activity);
} else if (isLike(activity)) {
- await this.like(actor, activity);
+ return await this.like(actor, activity);
} else if (isUndo(activity)) {
- await this.undo(actor, activity);
+ return await this.undo(actor, activity);
} else if (isBlock(activity)) {
- await this.block(actor, activity);
+ return await this.block(actor, activity);
} else if (isFlag(activity)) {
- await this.flag(actor, activity);
+ return await this.flag(actor, activity);
} else if (isMove(activity)) {
- await this.move(actor, activity);
+ return await this.move(actor, activity);
} else {
- this.logger.warn(`unrecognized activity type: ${activity.type}`);
+ return `unrecognized activity type: ${activity.type}`;
}
}
@@ -234,38 +242,49 @@ export class ApInboxService {
}
@bindThis
- private async add(actor: MiRemoteUser, activity: IAdd): Promise<void> {
+ private async add(actor: MiRemoteUser, activity: IAdd): Promise<string | void> {
if (actor.uri !== activity.actor) {
- throw new Error('invalid actor');
+ return 'invalid actor';
}
if (activity.target == null) {
- throw new Error('target is null');
+ return 'target is null';
}
if (activity.target === actor.featured) {
const note = await this.apNoteService.resolveNote(activity.object);
- if (note == null) throw new Error('note not found');
+ if (note == null) return 'note not found';
await this.notePiningService.addPinned(actor, note.id);
return;
}
- throw new Error(`unknown target: ${activity.target}`);
+ return `unknown target: ${activity.target}`;
}
@bindThis
- private async announce(actor: MiRemoteUser, activity: IAnnounce): Promise<void> {
+ private async announce(actor: MiRemoteUser, activity: IAnnounce): Promise<string | void> {
const uri = getApId(activity);
this.logger.info(`Announce: ${uri}`);
+ const resolver = this.apResolverService.createResolver();
+
+ if (!activity.object) return 'skip: activity has no object property';
const targetUri = getApId(activity.object);
+ if (targetUri.startsWith('bear:')) return 'skip: bearcaps url not supported.';
+
+ const target = await resolver.resolve(activity.object).catch(e => {
+ this.logger.error(`Resolution failed: ${e}`);
+ return e;
+ });
+
+ if (isPost(target)) return await this.announceNote(actor, activity, target);
- await this.announceNote(actor, activity, targetUri);
+ return `skip: unknown object type ${getApType(target)}`;
}
@bindThis
- private async announceNote(actor: MiRemoteUser, activity: IAnnounce, targetUri: string): Promise<void> {
+ private async announceNote(actor: MiRemoteUser, activity: IAnnounce, target: IPost): Promise<string | void> {
const uri = getApId(activity);
if (actor.isSuspended) {
@@ -288,24 +307,21 @@ export class ApInboxService {
// Announce対象をresolve
let renote;
try {
- renote = await this.apNoteService.resolveNote(targetUri);
- if (renote == null) throw new Error('announce target is null');
+ renote = await this.apNoteService.resolveNote(target);
+ if (renote == null) return 'announce target is null';
} catch (err) {
// 対象ãŒ4xxãªã‚‰ã‚¹ã‚­ãƒƒãƒ—
if (err instanceof StatusError) {
if (!err.isRetryable) {
- this.logger.warn(`Ignored announce target ${targetUri} - ${err.statusCode}`);
- return;
+ return `Ignored announce target ${target.id} - ${err.statusCode}`;
}
-
- this.logger.warn(`Error in announce target ${targetUri} - ${err.statusCode}`);
+ return `Error in announce target ${target.id} - ${err.statusCode}`;
}
throw err;
}
if (!await this.noteEntityService.isVisibleForMe(renote, actor.id)) {
- this.logger.warn('skip: invalid actor for this activity');
- return;
+ return 'skip: invalid actor for this activity';
}
this.logger.info(`Creating the (Re)Note: ${uri}`);
@@ -314,8 +330,7 @@ export class ApInboxService {
const createdAt = activity.published ? new Date(activity.published) : null;
if (createdAt && createdAt < this.idService.parse(renote.id).date) {
- this.logger.warn('skip: malformed createdAt');
- return;
+ return 'skip: malformed createdAt';
}
await this.noteCreateService.create(actor, {
@@ -349,11 +364,15 @@ export class ApInboxService {
}
@bindThis
- private async create(actor: MiRemoteUser, activity: ICreate): Promise<void> {
+ private async create(actor: MiRemoteUser, activity: ICreate): Promise<string | void> {
const uri = getApId(activity);
this.logger.info(`Create: ${uri}`);
+ if (!activity.object) return 'skip: activity has no object property';
+ const targetUri = getApId(activity.object);
+ if (targetUri.startsWith('bear:')) return 'skip: bearcaps url not supported.';
+
// copy audiences between activity <=> object.
if (typeof activity.object === 'object') {
const to = unique(concat([toArray(activity.to), toArray(activity.object.to)]));
@@ -380,7 +399,7 @@ export class ApInboxService {
if (isPost(object)) {
await this.createNote(resolver, actor, object, false, activity);
} else {
- this.logger.warn(`Unknown type: ${getApType(object)}`);
+ return `Unknown type: ${getApType(object)}`;
}
}
@@ -422,7 +441,7 @@ export class ApInboxService {
@bindThis
private async delete(actor: MiRemoteUser, activity: IDelete): Promise<string> {
if (actor.uri !== activity.actor) {
- throw new Error('invalid actor');
+ return 'invalid actor';
}
// 削除対象objectã®type
@@ -581,29 +600,29 @@ export class ApInboxService {
}
@bindThis
- private async remove(actor: MiRemoteUser, activity: IRemove): Promise<void> {
+ private async remove(actor: MiRemoteUser, activity: IRemove): Promise<string | void> {
if (actor.uri !== activity.actor) {
- throw new Error('invalid actor');
+ return 'invalid actor';
}
if (activity.target == null) {
- throw new Error('target is null');
+ return 'target is null';
}
if (activity.target === actor.featured) {
const note = await this.apNoteService.resolveNote(activity.object);
- if (note == null) throw new Error('note not found');
+ if (note == null) return 'note not found';
await this.notePiningService.removePinned(actor, note.id);
return;
}
- throw new Error(`unknown target: ${activity.target}`);
+ return `unknown target: ${activity.target}`;
}
@bindThis
private async undo(actor: MiRemoteUser, activity: IUndo): Promise<string> {
if (actor.uri !== activity.actor) {
- throw new Error('invalid actor');
+ return 'invalid actor';
}
const uri = activity.id ?? activity;
@@ -614,7 +633,7 @@ export class ApInboxService {
const object = await resolver.resolve(activity.object).catch(e => {
this.logger.error(`Resolution failed: ${e}`);
- throw e;
+ return e;
});
// don't queue because the sender may attempt again when timeout
diff --git a/packages/backend/src/core/activitypub/models/ApNoteService.ts b/packages/backend/src/core/activitypub/models/ApNoteService.ts
index 542bb9e2e5..cad1af02e5 100644
--- a/packages/backend/src/core/activitypub/models/ApNoteService.ts
+++ b/packages/backend/src/core/activitypub/models/ApNoteService.ts
@@ -86,20 +86,20 @@ export class ApNoteService {
const expectHost = this.utilityService.extractDbHost(uri);
if (!validPost.includes(getApType(object))) {
- return new Error(`invalid Note: invalid object type ${getApType(object)}`);
+ return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: invalid object type ${getApType(object)}`);
}
if (object.id && this.utilityService.extractDbHost(object.id) !== expectHost) {
- return new Error(`invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`);
+ return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: id has different host. expected: ${expectHost}, actual: ${this.utilityService.extractDbHost(object.id)}`);
}
const actualHost = object.attributedTo && this.utilityService.extractDbHost(getOneApId(object.attributedTo));
if (object.attributedTo && actualHost !== expectHost) {
- return new Error(`invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`);
+ return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', `invalid Note: attributedTo has different host. expected: ${expectHost}, actual: ${actualHost}`);
}
if (object.published && !this.idService.isSafeT(new Date(object.published).valueOf())) {
- return new Error('invalid Note: published timestamp is malformed');
+ return new IdentifiableError('d450b8a9-48e4-4dab-ae36-f4db763fda7c', 'invalid Note: published timestamp is malformed');
}
return null;
diff --git a/packages/backend/src/core/activitypub/type.ts b/packages/backend/src/core/activitypub/type.ts
index cf2c3f185e..8edd8a1aba 100644
--- a/packages/backend/src/core/activitypub/type.ts
+++ b/packages/backend/src/core/activitypub/type.ts
@@ -334,3 +334,4 @@ export const isAnnounce = (object: IObject): object is IAnnounce => getApType(ob
export const isBlock = (object: IObject): object is IBlock => getApType(object) === 'Block';
export const isFlag = (object: IObject): object is IFlag => getApType(object) === 'Flag';
export const isMove = (object: IObject): object is IMove => getApType(object) === 'Move';
+export const isNote = (object: IObject): object is IPost => getApType(object) === 'Note';
diff --git a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts
index 49f256d870..b0e1d1ab36 100644
--- a/packages/backend/src/core/entities/AbuseUserReportEntityService.ts
+++ b/packages/backend/src/core/entities/AbuseUserReportEntityService.ts
@@ -10,6 +10,8 @@ import { awaitAll } from '@/misc/prelude/await-all.js';
import type { MiAbuseUserReport } from '@/models/AbuseUserReport.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
+import { isNotNull } from '@/misc/is-not-null.js';
+import type { Packed } from '@/misc/json-schema.js';
import { UserEntityService } from './UserEntityService.js';
@Injectable()
@@ -26,6 +28,11 @@ export class AbuseUserReportEntityService {
@bindThis
public async pack(
src: MiAbuseUserReport['id'] | MiAbuseUserReport,
+ hint?: {
+ packedReporter?: Packed<'UserDetailedNotMe'>,
+ packedTargetUser?: Packed<'UserDetailedNotMe'>,
+ packedAssignee?: Packed<'UserDetailedNotMe'>,
+ },
) {
const report = typeof src === 'object' ? src : await this.abuseUserReportsRepository.findOneByOrFail({ id: src });
@@ -37,13 +44,13 @@ export class AbuseUserReportEntityService {
reporterId: report.reporterId,
targetUserId: report.targetUserId,
assigneeId: report.assigneeId,
- reporter: this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
+ reporter: hint?.packedReporter ?? this.userEntityService.pack(report.reporter ?? report.reporterId, null, {
schema: 'UserDetailedNotMe',
}),
- targetUser: this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
+ targetUser: hint?.packedTargetUser ?? this.userEntityService.pack(report.targetUser ?? report.targetUserId, null, {
schema: 'UserDetailedNotMe',
}),
- assignee: report.assigneeId ? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
+ assignee: report.assigneeId ? hint?.packedAssignee ?? this.userEntityService.pack(report.assignee ?? report.assigneeId, null, {
schema: 'UserDetailedNotMe',
}) : null,
forwarded: report.forwarded,
@@ -51,9 +58,24 @@ export class AbuseUserReportEntityService {
}
@bindThis
- public packMany(
- reports: any[],
+ public async packMany(
+ reports: MiAbuseUserReport[],
) {
- return Promise.all(reports.map(x => this.pack(x)));
+ const _reporters = reports.map(({ reporter, reporterId }) => reporter ?? reporterId);
+ const _targetUsers = reports.map(({ targetUser, targetUserId }) => targetUser ?? targetUserId);
+ const _assignees = reports.map(({ assignee, assigneeId }) => assignee ?? assigneeId).filter(isNotNull);
+ const _userMap = await this.userEntityService.packMany(
+ [..._reporters, ..._targetUsers, ..._assignees],
+ null,
+ { schema: 'UserDetailedNotMe' },
+ ).then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(
+ reports.map(report => {
+ const packedReporter = _userMap.get(report.reporterId);
+ const packedTargetUser = _userMap.get(report.targetUserId);
+ const packedAssignee = report.assigneeId != null ? _userMap.get(report.assigneeId) : undefined;
+ return this.pack(report, { packedReporter, packedTargetUser, packedAssignee });
+ }),
+ );
}
}
diff --git a/packages/backend/src/core/entities/AnnouncementEntityService.ts b/packages/backend/src/core/entities/AnnouncementEntityService.ts
new file mode 100644
index 0000000000..90b04d0229
--- /dev/null
+++ b/packages/backend/src/core/entities/AnnouncementEntityService.ts
@@ -0,0 +1,71 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import { DI } from '@/di-symbols.js';
+import type { AnnouncementsRepository, AnnouncementReadsRepository, MiAnnouncement, MiUser } from '@/models/_.js';
+import type { Packed } from '@/misc/json-schema.js';
+import { bindThis } from '@/decorators.js';
+import { IdService } from '@/core/IdService.js';
+
+@Injectable()
+export class AnnouncementEntityService {
+ constructor(
+ @Inject(DI.announcementsRepository)
+ private announcementsRepository: AnnouncementsRepository,
+
+ @Inject(DI.announcementReadsRepository)
+ private announcementReadsRepository: AnnouncementReadsRepository,
+
+ private idService: IdService,
+ ) {
+ }
+
+ @bindThis
+ public async pack(
+ src: MiAnnouncement['id'] | MiAnnouncement & { isRead?: boolean | null },
+ me?: { id: MiUser['id'] } | null | undefined,
+ ): Promise<Packed<'Announcement'>> {
+ const announcement = typeof src === 'object'
+ ? src
+ : await this.announcementsRepository.findOneByOrFail({
+ id: src,
+ }) as MiAnnouncement & { isRead?: boolean | null };
+
+ if (me && announcement.isRead === undefined) {
+ announcement.isRead = await this.announcementReadsRepository
+ .countBy({
+ announcementId: announcement.id,
+ userId: me.id,
+ })
+ .then((count: number) => count > 0);
+ }
+
+ return {
+ id: announcement.id,
+ createdAt: this.idService.parse(announcement.id).date.toISOString(),
+ updatedAt: announcement.updatedAt?.toISOString() ?? null,
+ title: announcement.title,
+ text: announcement.text,
+ imageUrl: announcement.imageUrl,
+ icon: announcement.icon,
+ display: announcement.display,
+ forYou: announcement.userId === me?.id,
+ needConfirmationToRead: announcement.needConfirmationToRead,
+ silence: announcement.silence,
+ isRead: announcement.isRead !== null ? announcement.isRead : undefined,
+ };
+ }
+
+ @bindThis
+ public async packMany(
+ announcements: (MiAnnouncement['id'] | MiAnnouncement & { isRead?: boolean | null } | MiAnnouncement)[],
+ me?: { id: MiUser['id'] } | null | undefined,
+ ) : Promise<Packed<'Announcement'>[]> {
+ return (await Promise.allSettled(announcements.map(x => this.pack(x, me))))
+ .filter(result => result.status === 'fulfilled')
+ .map(result => (result as PromiseFulfilledResult<Packed<'Announcement'>>).value);
+ }
+}
diff --git a/packages/backend/src/core/entities/AntennaEntityService.ts b/packages/backend/src/core/entities/AntennaEntityService.ts
index 3ec8efa6bf..e770028af3 100644
--- a/packages/backend/src/core/entities/AntennaEntityService.ts
+++ b/packages/backend/src/core/entities/AntennaEntityService.ts
@@ -38,12 +38,12 @@ export class AntennaEntityService {
users: antenna.users,
caseSensitive: antenna.caseSensitive,
localOnly: antenna.localOnly,
- notify: antenna.notify,
excludeBots: antenna.excludeBots,
withReplies: antenna.withReplies,
withFile: antenna.withFile,
isActive: antenna.isActive,
hasUnreadNote: false, // TODO
+ notify: false, // å¾Œæ–¹äº’æ›æ€§ã®ãŸã‚
};
}
}
diff --git a/packages/backend/src/core/entities/BlockingEntityService.ts b/packages/backend/src/core/entities/BlockingEntityService.ts
index c8c1520ceb..1e699032e2 100644
--- a/packages/backend/src/core/entities/BlockingEntityService.ts
+++ b/packages/backend/src/core/entities/BlockingEntityService.ts
@@ -29,6 +29,9 @@ export class BlockingEntityService {
public async pack(
src: MiBlocking['id'] | MiBlocking,
me?: { id: MiUser['id'] } | null | undefined,
+ hint?: {
+ blockee?: Packed<'UserDetailedNotMe'>,
+ },
): Promise<Packed<'Blocking'>> {
const blocking = typeof src === 'object' ? src : await this.blockingsRepository.findOneByOrFail({ id: src });
@@ -36,17 +39,20 @@ export class BlockingEntityService {
id: blocking.id,
createdAt: this.idService.parse(blocking.id).date.toISOString(),
blockeeId: blocking.blockeeId,
- blockee: this.userEntityService.pack(blocking.blockeeId, me, {
+ blockee: hint?.blockee ?? this.userEntityService.pack(blocking.blockeeId, me, {
schema: 'UserDetailedNotMe',
}),
});
}
@bindThis
- public packMany(
- blockings: any[],
+ public async packMany(
+ blockings: MiBlocking[],
me: { id: MiUser['id'] },
) {
- return Promise.all(blockings.map(x => this.pack(x, me)));
+ const _blockees = blockings.map(({ blockee, blockeeId }) => blockee ?? blockeeId);
+ const _userMap = await this.userEntityService.packMany(_blockees, me, { schema: 'UserDetailedNotMe' })
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(blockings.map(blocking => this.pack(blocking, me, { blockee: _userMap.get(blocking.blockeeId) })));
}
}
diff --git a/packages/backend/src/core/entities/ClipEntityService.ts b/packages/backend/src/core/entities/ClipEntityService.ts
index ce49c3458c..3855a28436 100644
--- a/packages/backend/src/core/entities/ClipEntityService.ts
+++ b/packages/backend/src/core/entities/ClipEntityService.ts
@@ -35,6 +35,9 @@ export class ClipEntityService {
public async pack(
src: MiClip['id'] | MiClip,
me?: { id: MiUser['id'] } | null | undefined,
+ hint?: {
+ packedUser?: Packed<'UserLite'>
+ },
): Promise<Packed<'Clip'>> {
const meId = me ? me.id : null;
const clip = typeof src === 'object' ? src : await this.clipsRepository.findOneByOrFail({ id: src });
@@ -44,7 +47,7 @@ export class ClipEntityService {
createdAt: this.idService.parse(clip.id).date.toISOString(),
lastClippedAt: clip.lastClippedAt ? clip.lastClippedAt.toISOString() : null,
userId: clip.userId,
- user: this.userEntityService.pack(clip.user ?? clip.userId),
+ user: hint?.packedUser ?? this.userEntityService.pack(clip.user ?? clip.userId),
name: clip.name,
description: clip.description,
isPublic: clip.isPublic,
@@ -55,11 +58,14 @@ export class ClipEntityService {
}
@bindThis
- public packMany(
+ public async packMany(
clips: MiClip[],
me?: { id: MiUser['id'] } | null | undefined,
) {
- return Promise.all(clips.map(x => this.pack(x, me)));
+ const _users = clips.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users, me)
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(clips.map(clip => this.pack(clip, me, { packedUser: _userMap.get(clip.userId) })));
}
}
diff --git a/packages/backend/src/core/entities/DriveFileEntityService.ts b/packages/backend/src/core/entities/DriveFileEntityService.ts
index 26bf386cbc..02ff2e7754 100644
--- a/packages/backend/src/core/entities/DriveFileEntityService.ts
+++ b/packages/backend/src/core/entities/DriveFileEntityService.ts
@@ -222,6 +222,9 @@ export class DriveFileEntityService {
public async packNullable(
src: MiDriveFile['id'] | MiDriveFile,
options?: PackOptions,
+ hint?: {
+ packedUser?: Packed<'UserLite'>
+ },
): Promise<Packed<'DriveFile'> | null> {
const opts = Object.assign({
detail: false,
@@ -249,7 +252,7 @@ export class DriveFileEntityService {
detail: true,
}) : null,
userId: file.userId,
- user: (opts.withUser && file.userId) ? this.userEntityService.pack(file.userId) : null,
+ user: (opts.withUser && file.userId) ? hint?.packedUser ?? this.userEntityService.pack(file.userId) : null,
});
}
@@ -258,7 +261,10 @@ export class DriveFileEntityService {
files: MiDriveFile[],
options?: PackOptions,
): Promise<Packed<'DriveFile'>[]> {
- const items = await Promise.all(files.map(f => this.packNullable(f, options)));
+ const _user = files.map(({ user, userId }) => user ?? userId).filter(isNotNull);
+ const _userMap = await this.userEntityService.packMany(_user)
+ .then(users => new Map(users.map(user => [user.id, user])));
+ const items = await Promise.all(files.map(f => this.packNullable(f, options, f.userId ? { packedUser: _userMap.get(f.userId) } : {})));
return items.filter(isNotNull);
}
diff --git a/packages/backend/src/core/entities/FlashEntityService.ts b/packages/backend/src/core/entities/FlashEntityService.ts
index db4cf6d360..d110f7afc6 100644
--- a/packages/backend/src/core/entities/FlashEntityService.ts
+++ b/packages/backend/src/core/entities/FlashEntityService.ts
@@ -33,6 +33,9 @@ export class FlashEntityService {
public async pack(
src: MiFlash['id'] | MiFlash,
me?: { id: MiUser['id'] } | null | undefined,
+ hint?: {
+ packedUser?: Packed<'UserLite'>
+ },
): Promise<Packed<'Flash'>> {
const meId = me ? me.id : null;
const flash = typeof src === 'object' ? src : await this.flashsRepository.findOneByOrFail({ id: src });
@@ -42,7 +45,7 @@ export class FlashEntityService {
createdAt: this.idService.parse(flash.id).date.toISOString(),
updatedAt: flash.updatedAt.toISOString(),
userId: flash.userId,
- user: this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } ã™ã‚‹ã¨ç„¡é™ãƒ«ãƒ¼ãƒ—ã™ã‚‹ã®ã§æ³¨æ„
+ user: hint?.packedUser ?? this.userEntityService.pack(flash.user ?? flash.userId, me), // { schema: 'UserDetailed' } ã™ã‚‹ã¨ç„¡é™ãƒ«ãƒ¼ãƒ—ã™ã‚‹ã®ã§æ³¨æ„
title: flash.title,
summary: flash.summary,
script: flash.script,
@@ -52,11 +55,14 @@ export class FlashEntityService {
}
@bindThis
- public packMany(
- flashs: MiFlash[],
+ public async packMany(
+ flashes: MiFlash[],
me?: { id: MiUser['id'] } | null | undefined,
) {
- return Promise.all(flashs.map(x => this.pack(x, me)));
+ const _users = flashes.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users, me)
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(flashes.map(flash => this.pack(flash, me, { packedUser: _userMap.get(flash.userId) })));
}
}
diff --git a/packages/backend/src/core/entities/FollowRequestEntityService.ts b/packages/backend/src/core/entities/FollowRequestEntityService.ts
index 763b75101f..0101ec8aa7 100644
--- a/packages/backend/src/core/entities/FollowRequestEntityService.ts
+++ b/packages/backend/src/core/entities/FollowRequestEntityService.ts
@@ -10,6 +10,7 @@ import type { } from '@/models/Blocking.js';
import type { MiUser } from '@/models/User.js';
import type { MiFollowRequest } from '@/models/FollowRequest.js';
import { bindThis } from '@/decorators.js';
+import type { Packed } from '@/misc/json-schema.js';
import { UserEntityService } from './UserEntityService.js';
@Injectable()
@@ -26,14 +27,36 @@ export class FollowRequestEntityService {
public async pack(
src: MiFollowRequest['id'] | MiFollowRequest,
me?: { id: MiUser['id'] } | null | undefined,
+ hint?: {
+ packedFollower?: Packed<'UserLite'>,
+ packedFollowee?: Packed<'UserLite'>,
+ },
) {
const request = typeof src === 'object' ? src : await this.followRequestsRepository.findOneByOrFail({ id: src });
return {
id: request.id,
- follower: await this.userEntityService.pack(request.followerId, me),
- followee: await this.userEntityService.pack(request.followeeId, me),
+ follower: hint?.packedFollower ?? await this.userEntityService.pack(request.followerId, me),
+ followee: hint?.packedFollowee ?? await this.userEntityService.pack(request.followeeId, me),
};
}
+
+ @bindThis
+ public async packMany(
+ requests: MiFollowRequest[],
+ me?: { id: MiUser['id'] } | null | undefined,
+ ) {
+ const _followers = requests.map(({ follower, followerId }) => follower ?? followerId);
+ const _followees = requests.map(({ followee, followeeId }) => followee ?? followeeId);
+ const _userMap = await this.userEntityService.packMany([..._followers, ..._followees], me)
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(
+ requests.map(req => {
+ const packedFollower = _userMap.get(req.followerId);
+ const packedFollowee = _userMap.get(req.followeeId);
+ return this.pack(req, me, { packedFollower, packedFollowee });
+ }),
+ );
+ }
}
diff --git a/packages/backend/src/core/entities/FollowingEntityService.ts b/packages/backend/src/core/entities/FollowingEntityService.ts
index 24cd33e3f7..d2dbaf2270 100644
--- a/packages/backend/src/core/entities/FollowingEntityService.ts
+++ b/packages/backend/src/core/entities/FollowingEntityService.ts
@@ -78,6 +78,10 @@ export class FollowingEntityService {
populateFollowee?: boolean;
populateFollower?: boolean;
},
+ hint?: {
+ packedFollowee?: Packed<'UserDetailedNotMe'>,
+ packedFollower?: Packed<'UserDetailedNotMe'>,
+ },
): Promise<Packed<'Following'>> {
const following = typeof src === 'object' ? src : await this.followingsRepository.findOneByOrFail({ id: src });
@@ -88,25 +92,35 @@ export class FollowingEntityService {
createdAt: this.idService.parse(following.id).date.toISOString(),
followeeId: following.followeeId,
followerId: following.followerId,
- followee: opts.populateFollowee ? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
+ followee: opts.populateFollowee ? hint?.packedFollowee ?? this.userEntityService.pack(following.followee ?? following.followeeId, me, {
schema: 'UserDetailedNotMe',
}) : undefined,
- follower: opts.populateFollower ? this.userEntityService.pack(following.follower ?? following.followerId, me, {
+ follower: opts.populateFollower ? hint?.packedFollower ?? this.userEntityService.pack(following.follower ?? following.followerId, me, {
schema: 'UserDetailedNotMe',
}) : undefined,
});
}
@bindThis
- public packMany(
- followings: any[],
+ public async packMany(
+ followings: MiFollowing[],
me?: { id: MiUser['id'] } | null | undefined,
opts?: {
populateFollowee?: boolean;
populateFollower?: boolean;
},
) {
- return Promise.all(followings.map(x => this.pack(x, me, opts)));
+ const _followees = opts?.populateFollowee ? followings.map(({ followee, followeeId }) => followee ?? followeeId) : [];
+ const _followers = opts?.populateFollower ? followings.map(({ follower, followerId }) => follower ?? followerId) : [];
+ const _userMap = await this.userEntityService.packMany([..._followees, ..._followers], me, { schema: 'UserDetailedNotMe' })
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(
+ followings.map(following => {
+ const packedFollowee = opts?.populateFollowee ? _userMap.get(following.followeeId) : undefined;
+ const packedFollower = opts?.populateFollower ? _userMap.get(following.followerId) : undefined;
+ return this.pack(following, me, opts, { packedFollowee, packedFollower });
+ }),
+ );
}
}
diff --git a/packages/backend/src/core/entities/GalleryPostEntityService.ts b/packages/backend/src/core/entities/GalleryPostEntityService.ts
index 101182a9e5..9746a4c1af 100644
--- a/packages/backend/src/core/entities/GalleryPostEntityService.ts
+++ b/packages/backend/src/core/entities/GalleryPostEntityService.ts
@@ -35,6 +35,9 @@ export class GalleryPostEntityService {
public async pack(
src: MiGalleryPost['id'] | MiGalleryPost,
me?: { id: MiUser['id'] } | null | undefined,
+ hint?: {
+ packedUser?: Packed<'UserLite'>
+ },
): Promise<Packed<'GalleryPost'>> {
const meId = me ? me.id : null;
const post = typeof src === 'object' ? src : await this.galleryPostsRepository.findOneByOrFail({ id: src });
@@ -44,7 +47,7 @@ export class GalleryPostEntityService {
createdAt: this.idService.parse(post.id).date.toISOString(),
updatedAt: post.updatedAt.toISOString(),
userId: post.userId,
- user: this.userEntityService.pack(post.user ?? post.userId, me),
+ user: hint?.packedUser ?? this.userEntityService.pack(post.user ?? post.userId, me),
title: post.title,
description: post.description,
fileIds: post.fileIds,
@@ -58,11 +61,14 @@ export class GalleryPostEntityService {
}
@bindThis
- public packMany(
+ public async packMany(
posts: MiGalleryPost[],
me?: { id: MiUser['id'] } | null | undefined,
) {
- return Promise.all(posts.map(x => this.pack(x, me)));
+ const _users = posts.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users, me)
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(posts.map(post => this.pack(post, me, { packedUser: _userMap.get(post.userId) })));
}
}
diff --git a/packages/backend/src/core/entities/InstanceEntityService.ts b/packages/backend/src/core/entities/InstanceEntityService.ts
index b4a518a1c6..002a93397d 100644
--- a/packages/backend/src/core/entities/InstanceEntityService.ts
+++ b/packages/backend/src/core/entities/InstanceEntityService.ts
@@ -39,7 +39,8 @@ export class InstanceEntityService {
followingCount: instance.followingCount,
followersCount: instance.followersCount,
isNotResponding: instance.isNotResponding,
- isSuspended: instance.isSuspended,
+ isSuspended: instance.suspensionState !== 'none',
+ suspensionState: instance.suspensionState,
isBlocked: this.utilityService.isBlockedHost(meta.blockedHosts, instance.host),
softwareName: instance.softwareName,
softwareVersion: instance.softwareVersion,
diff --git a/packages/backend/src/core/entities/InviteCodeEntityService.ts b/packages/backend/src/core/entities/InviteCodeEntityService.ts
index 891543bc0f..26f57e1299 100644
--- a/packages/backend/src/core/entities/InviteCodeEntityService.ts
+++ b/packages/backend/src/core/entities/InviteCodeEntityService.ts
@@ -12,6 +12,7 @@ import type { MiUser } from '@/models/User.js';
import type { MiRegistrationTicket } from '@/models/RegistrationTicket.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
+import { isNotNull } from '@/misc/is-not-null.js';
import { UserEntityService } from './UserEntityService.js';
@Injectable()
@@ -29,6 +30,10 @@ export class InviteCodeEntityService {
public async pack(
src: MiRegistrationTicket['id'] | MiRegistrationTicket,
me?: { id: MiUser['id'] } | null | undefined,
+ hints?: {
+ packedCreatedBy?: Packed<'UserLite'>,
+ packedUsedBy?: Packed<'UserLite'>,
+ },
): Promise<Packed<'InviteCode'>> {
const target = typeof src === 'object' ? src : await this.registrationTicketsRepository.findOneOrFail({
where: {
@@ -42,18 +47,28 @@ export class InviteCodeEntityService {
code: target.code,
expiresAt: target.expiresAt ? target.expiresAt.toISOString() : null,
createdAt: this.idService.parse(target.id).date.toISOString(),
- createdBy: target.createdBy ? await this.userEntityService.pack(target.createdBy, me) : null,
- usedBy: target.usedBy ? await this.userEntityService.pack(target.usedBy, me) : null,
+ createdBy: target.createdBy ? hints?.packedCreatedBy ?? await this.userEntityService.pack(target.createdBy, me) : null,
+ usedBy: target.usedBy ? hints?.packedUsedBy ?? await this.userEntityService.pack(target.usedBy, me) : null,
usedAt: target.usedAt ? target.usedAt.toISOString() : null,
used: !!target.usedAt,
});
}
@bindThis
- public packMany(
- targets: any[],
+ public async packMany(
+ tickets: MiRegistrationTicket[],
me: { id: MiUser['id'] },
) {
- return Promise.all(targets.map(x => this.pack(x, me)));
+ const _createdBys = tickets.map(({ createdBy, createdById }) => createdBy ?? createdById).filter(isNotNull);
+ const _usedBys = tickets.map(({ usedBy, usedById }) => usedBy ?? usedById).filter(isNotNull);
+ const _userMap = await this.userEntityService.packMany([..._createdBys, ..._usedBys], me)
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(
+ tickets.map(ticket => {
+ const packedCreatedBy = ticket.createdById != null ? _userMap.get(ticket.createdById) : undefined;
+ const packedUsedBy = ticket.usedById != null ? _userMap.get(ticket.usedById) : undefined;
+ return this.pack(ticket, me, { packedCreatedBy, packedUsedBy });
+ }),
+ );
}
}
diff --git a/packages/backend/src/core/entities/MetaEntityService.ts b/packages/backend/src/core/entities/MetaEntityService.ts
index fa643e45a7..34d46e50e5 100644
--- a/packages/backend/src/core/entities/MetaEntityService.ts
+++ b/packages/backend/src/core/entities/MetaEntityService.ts
@@ -67,6 +67,7 @@ export class MetaEntityService {
impressumUrl: instance.impressumUrl,
donationUrl: instance.donationUrl,
privacyPolicyUrl: instance.privacyPolicyUrl,
+ inquiryUrl: instance.inquiryUrl,
disableRegistration: instance.disableRegistration,
emailRequiredForSignup: instance.emailRequiredForSignup,
approvalRequiredForSignup: instance.approvalRequiredForSignup,
diff --git a/packages/backend/src/core/entities/ModerationLogEntityService.ts b/packages/backend/src/core/entities/ModerationLogEntityService.ts
index 205e147bd1..bf1b2a002c 100644
--- a/packages/backend/src/core/entities/ModerationLogEntityService.ts
+++ b/packages/backend/src/core/entities/ModerationLogEntityService.ts
@@ -8,9 +8,10 @@ import { DI } from '@/di-symbols.js';
import type { ModerationLogsRepository } from '@/models/_.js';
import { awaitAll } from '@/misc/prelude/await-all.js';
import type { } from '@/models/Blocking.js';
-import type { MiModerationLog } from '@/models/ModerationLog.js';
+import { MiModerationLog } from '@/models/ModerationLog.js';
import { bindThis } from '@/decorators.js';
import { IdService } from '@/core/IdService.js';
+import type { Packed } from '@/misc/json-schema.js';
import { UserEntityService } from './UserEntityService.js';
@Injectable()
@@ -27,6 +28,9 @@ export class ModerationLogEntityService {
@bindThis
public async pack(
src: MiModerationLog['id'] | MiModerationLog,
+ hint?: {
+ packedUser?: Packed<'UserDetailedNotMe'>,
+ },
) {
const log = typeof src === 'object' ? src : await this.moderationLogsRepository.findOneByOrFail({ id: src });
@@ -36,17 +40,20 @@ export class ModerationLogEntityService {
type: log.type,
info: log.info,
userId: log.userId,
- user: this.userEntityService.pack(log.user ?? log.userId, null, {
+ user: hint?.packedUser ?? this.userEntityService.pack(log.user ?? log.userId, null, {
schema: 'UserDetailedNotMe',
}),
});
}
@bindThis
- public packMany(
- reports: any[],
+ public async packMany(
+ reports: MiModerationLog[],
) {
- return Promise.all(reports.map(x => this.pack(x)));
+ const _users = reports.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users, null, { schema: 'UserDetailedNotMe' })
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(reports.map(report => this.pack(report, { packedUser: _userMap.get(report.userId) })));
}
}
diff --git a/packages/backend/src/core/entities/MutingEntityService.ts b/packages/backend/src/core/entities/MutingEntityService.ts
index 0a52f429a2..d361a20271 100644
--- a/packages/backend/src/core/entities/MutingEntityService.ts
+++ b/packages/backend/src/core/entities/MutingEntityService.ts
@@ -30,6 +30,9 @@ export class MutingEntityService {
public async pack(
src: MiMuting['id'] | MiMuting,
me?: { id: MiUser['id'] } | null | undefined,
+ hints?: {
+ packedMutee?: Packed<'UserDetailedNotMe'>,
+ },
): Promise<Packed<'Muting'>> {
const muting = typeof src === 'object' ? src : await this.mutingsRepository.findOneByOrFail({ id: src });
@@ -38,18 +41,21 @@ export class MutingEntityService {
createdAt: this.idService.parse(muting.id).date.toISOString(),
expiresAt: muting.expiresAt ? muting.expiresAt.toISOString() : null,
muteeId: muting.muteeId,
- mutee: this.userEntityService.pack(muting.muteeId, me, {
+ mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
schema: 'UserDetailedNotMe',
}),
});
}
@bindThis
- public packMany(
- mutings: any[],
+ public async packMany(
+ mutings: MiMuting[],
me: { id: MiUser['id'] },
) {
- return Promise.all(mutings.map(x => this.pack(x, me)));
+ const _mutees = mutings.map(({ mutee, muteeId }) => mutee ?? muteeId);
+ const _userMap = await this.userEntityService.packMany(_mutees, me, { schema: 'UserDetailedNotMe' })
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(mutings.map(muting => this.pack(muting, me, { packedMutee: _userMap.get(muting.muteeId) })));
}
}
diff --git a/packages/backend/src/core/entities/NoteEntityService.ts b/packages/backend/src/core/entities/NoteEntityService.ts
index 9f59f89d17..ca755ea286 100644
--- a/packages/backend/src/core/entities/NoteEntityService.ts
+++ b/packages/backend/src/core/entities/NoteEntityService.ts
@@ -307,6 +307,7 @@ export class NoteEntityService implements OnModuleInit {
_hint_?: {
myReactions: Map<MiNote['id'], string | null>;
packedFiles: Map<MiNote['fileIds'][number], Packed<'DriveFile'> | null>;
+ packedUsers: Map<MiUser['id'], Packed<'UserLite'>>
};
},
): Promise<Packed<'Note'>> {
@@ -336,13 +337,14 @@ export class NoteEntityService implements OnModuleInit {
.filter(x => x.startsWith(':') && x.includes('@') && !x.includes('@.')) // リモートカスタム絵文字ã®ã¿
.map(x => this.reactionService.decodeReaction(x).reaction.replaceAll(':', ''));
const packedFiles = options?._hint_?.packedFiles;
+ const packedUsers = options?._hint_?.packedUsers;
const packed: Packed<'Note'> = await awaitAll({
id: note.id,
createdAt: this.idService.parse(note.id).date.toISOString(),
updatedAt: note.updatedAt ? note.updatedAt.toISOString() : undefined,
userId: note.userId,
- user: this.userEntityService.pack(note.user ?? note.userId, me),
+ user: packedUsers?.get(note.userId) ?? this.userEntityService.pack(note.user ?? note.userId, me),
text: text,
cw: note.cw,
visibility: note.visibility,
@@ -465,12 +467,20 @@ export class NoteEntityService implements OnModuleInit {
// TODO: 本当㯠renote ã¨ã‹ reply ãŒãªã„ã®ã« renoteId ã¨ã‹ replyId ãŒã‚ã£ãŸã‚‰ã“ã“ã§è§£æ±ºã—ã¦ãŠã
const fileIds = notes.map(n => [n.fileIds, n.renote?.fileIds, n.reply?.fileIds]).flat(2).filter(isNotNull);
const packedFiles = fileIds.length > 0 ? await this.driveFileEntityService.packManyByIdsMap(fileIds) : new Map();
+ const users = [
+ ...notes.map(({ user, userId }) => user ?? userId),
+ ...notes.map(({ replyUserId }) => replyUserId).filter(isNotNull),
+ ...notes.map(({ renoteUserId }) => renoteUserId).filter(isNotNull),
+ ];
+ const packedUsers = await this.userEntityService.packMany(users, me)
+ .then(users => new Map(users.map(u => [u.id, u])));
return await Promise.all(notes.map(n => this.pack(n, me, {
...options,
_hint_: {
myReactions: myReactionsMap,
packedFiles,
+ packedUsers,
},
})));
}
diff --git a/packages/backend/src/core/entities/NoteReactionEntityService.ts b/packages/backend/src/core/entities/NoteReactionEntityService.ts
index 3f4fa3cf96..46ec13704c 100644
--- a/packages/backend/src/core/entities/NoteReactionEntityService.ts
+++ b/packages/backend/src/core/entities/NoteReactionEntityService.ts
@@ -52,6 +52,9 @@ export class NoteReactionEntityService implements OnModuleInit {
options?: {
withNote: boolean;
},
+ hints?: {
+ packedUser?: Packed<'UserLite'>
+ },
): Promise<Packed<'NoteReaction'>> {
const opts = Object.assign({
withNote: false,
@@ -62,7 +65,7 @@ export class NoteReactionEntityService implements OnModuleInit {
return {
id: reaction.id,
createdAt: this.idService.parse(reaction.id).date.toISOString(),
- user: await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
+ user: hints?.packedUser ?? await this.userEntityService.pack(reaction.user ?? reaction.userId, me),
type: this.reactionService.convertLegacyReaction(reaction.reaction),
...(opts.withNote ? {
note: await this.noteEntityService.pack(reaction.note ?? reaction.noteId, me),
@@ -81,7 +84,9 @@ export class NoteReactionEntityService implements OnModuleInit {
const opts = Object.assign({
withNote: false,
}, options);
-
- return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts)));
+ const _users = reactions.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users, me)
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(reactions.map(reaction => this.pack(reaction, me, opts, { packedUser: _userMap.get(reaction.userId) })));
}
}
diff --git a/packages/backend/src/core/entities/PageEntityService.ts b/packages/backend/src/core/entities/PageEntityService.ts
index 65c69a49a7..142d9e81db 100644
--- a/packages/backend/src/core/entities/PageEntityService.ts
+++ b/packages/backend/src/core/entities/PageEntityService.ts
@@ -40,6 +40,9 @@ export class PageEntityService {
public async pack(
src: MiPage['id'] | MiPage,
me?: { id: MiUser['id'] } | null | undefined,
+ hint?: {
+ packedUser?: Packed<'UserLite'>
+ },
): Promise<Packed<'Page'>> {
const meId = me ? me.id : null;
const page = typeof src === 'object' ? src : await this.pagesRepository.findOneByOrFail({ id: src });
@@ -91,7 +94,7 @@ export class PageEntityService {
createdAt: this.idService.parse(page.id).date.toISOString(),
updatedAt: page.updatedAt.toISOString(),
userId: page.userId,
- user: this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } ã™ã‚‹ã¨ç„¡é™ãƒ«ãƒ¼ãƒ—ã™ã‚‹ã®ã§æ³¨æ„
+ user: hint?.packedUser ?? this.userEntityService.pack(page.user ?? page.userId, me), // { schema: 'UserDetailed' } ã™ã‚‹ã¨ç„¡é™ãƒ«ãƒ¼ãƒ—ã™ã‚‹ã®ã§æ³¨æ„
content: page.content,
variables: page.variables,
title: page.title,
@@ -110,11 +113,14 @@ export class PageEntityService {
}
@bindThis
- public packMany(
+ public async packMany(
pages: MiPage[],
me?: { id: MiUser['id'] } | null | undefined,
) {
- return Promise.all(pages.map(x => this.pack(x, me)));
+ const _users = pages.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users, me)
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(pages.map(page => this.pack(page, me, { packedUser: _userMap.get(page.userId) })));
}
}
diff --git a/packages/backend/src/core/entities/RenoteMutingEntityService.ts b/packages/backend/src/core/entities/RenoteMutingEntityService.ts
index 0b05a5db80..e4e154109a 100644
--- a/packages/backend/src/core/entities/RenoteMutingEntityService.ts
+++ b/packages/backend/src/core/entities/RenoteMutingEntityService.ts
@@ -30,6 +30,9 @@ export class RenoteMutingEntityService {
public async pack(
src: MiRenoteMuting['id'] | MiRenoteMuting,
me?: { id: MiUser['id'] } | null | undefined,
+ hints?: {
+ packedMutee?: Packed<'UserDetailedNotMe'>
+ },
): Promise<Packed<'RenoteMuting'>> {
const muting = typeof src === 'object' ? src : await this.renoteMutingsRepository.findOneByOrFail({ id: src });
@@ -37,18 +40,21 @@ export class RenoteMutingEntityService {
id: muting.id,
createdAt: this.idService.parse(muting.id).date.toISOString(),
muteeId: muting.muteeId,
- mutee: this.userEntityService.pack(muting.muteeId, me, {
+ mutee: hints?.packedMutee ?? this.userEntityService.pack(muting.muteeId, me, {
schema: 'UserDetailedNotMe',
}),
});
}
@bindThis
- public packMany(
- mutings: any[],
+ public async packMany(
+ mutings: MiRenoteMuting[],
me: { id: MiUser['id'] },
) {
- return Promise.all(mutings.map(x => this.pack(x, me)));
+ const _users = mutings.map(({ mutee, muteeId }) => mutee ?? muteeId);
+ const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailedNotMe' })
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(mutings.map(muting => this.pack(muting, me, { packedMutee: _userMap.get(muting.muteeId) })));
}
}
diff --git a/packages/backend/src/core/entities/ReversiGameEntityService.ts b/packages/backend/src/core/entities/ReversiGameEntityService.ts
index 32cbe631e4..df042e75c1 100644
--- a/packages/backend/src/core/entities/ReversiGameEntityService.ts
+++ b/packages/backend/src/core/entities/ReversiGameEntityService.ts
@@ -28,13 +28,15 @@ export class ReversiGameEntityService {
@bindThis
public async packDetail(
src: MiReversiGame['id'] | MiReversiGame,
+ hint?: {
+ packedUser1?: Packed<'UserLite'>,
+ packedUser2?: Packed<'UserLite'>,
+ },
): Promise<Packed<'ReversiGameDetailed'>> {
const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
- const users = await Promise.all([
- this.userEntityService.pack(game.user1 ?? game.user1Id),
- this.userEntityService.pack(game.user2 ?? game.user2Id),
- ]);
+ const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
+ const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
return await awaitAll({
id: game.id,
@@ -49,10 +51,10 @@ export class ReversiGameEntityService {
user2Ready: game.user2Ready,
user1Id: game.user1Id,
user2Id: game.user2Id,
- user1: users[0],
- user2: users[1],
+ user1,
+ user2,
winnerId: game.winnerId,
- winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
+ winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
surrenderedUserId: game.surrenderedUserId,
timeoutUserId: game.timeoutUserId,
black: game.black,
@@ -68,22 +70,35 @@ export class ReversiGameEntityService {
}
@bindThis
- public packDetailMany(
- xs: MiReversiGame[],
+ public async packDetailMany(
+ games: MiReversiGame[],
) {
- return Promise.all(xs.map(x => this.packDetail(x)));
+ const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
+ const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
+ const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(
+ games.map(game => {
+ return this.packDetail(game, {
+ packedUser1: _userMap.get(game.user1Id),
+ packedUser2: _userMap.get(game.user2Id),
+ });
+ }),
+ );
}
@bindThis
public async packLite(
src: MiReversiGame['id'] | MiReversiGame,
+ hint?: {
+ packedUser1?: Packed<'UserLite'>,
+ packedUser2?: Packed<'UserLite'>,
+ },
): Promise<Packed<'ReversiGameLite'>> {
const game = typeof src === 'object' ? src : await this.reversiGamesRepository.findOneByOrFail({ id: src });
- const users = await Promise.all([
- this.userEntityService.pack(game.user1 ?? game.user1Id),
- this.userEntityService.pack(game.user2 ?? game.user2Id),
- ]);
+ const user1 = hint?.packedUser1 ?? await this.userEntityService.pack(game.user1 ?? game.user1Id);
+ const user2 = hint?.packedUser2 ?? await this.userEntityService.pack(game.user2 ?? game.user2Id);
return await awaitAll({
id: game.id,
@@ -94,10 +109,10 @@ export class ReversiGameEntityService {
isEnded: game.isEnded,
user1Id: game.user1Id,
user2Id: game.user2Id,
- user1: users[0],
- user2: users[1],
+ user1,
+ user2,
winnerId: game.winnerId,
- winner: game.winnerId ? users.find(u => u.id === game.winnerId)! : null,
+ winner: game.winnerId ? [user1, user2].find(u => u.id === game.winnerId)! : null,
surrenderedUserId: game.surrenderedUserId,
timeoutUserId: game.timeoutUserId,
black: game.black,
@@ -111,10 +126,21 @@ export class ReversiGameEntityService {
}
@bindThis
- public packLiteMany(
- xs: MiReversiGame[],
+ public async packLiteMany(
+ games: MiReversiGame[],
) {
- return Promise.all(xs.map(x => this.packLite(x)));
+ const _user1s = games.map(({ user1, user1Id }) => user1 ?? user1Id);
+ const _user2s = games.map(({ user2, user2Id }) => user2 ?? user2Id);
+ const _userMap = await this.userEntityService.packMany([..._user1s, ..._user2s])
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return Promise.all(
+ games.map(game => {
+ return this.packLite(game, {
+ packedUser1: _userMap.get(game.user1Id),
+ packedUser2: _userMap.get(game.user2Id),
+ });
+ }),
+ );
}
}
diff --git a/packages/backend/src/core/entities/UserListEntityService.ts b/packages/backend/src/core/entities/UserListEntityService.ts
index 09cab24521..b77249c5cb 100644
--- a/packages/backend/src/core/entities/UserListEntityService.ts
+++ b/packages/backend/src/core/entities/UserListEntityService.ts
@@ -50,11 +50,14 @@ export class UserListEntityService {
public async packMembershipsMany(
memberships: MiUserListMembership[],
) {
+ const _users = memberships.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users)
+ .then(users => new Map(users.map(u => [u.id, u])));
return Promise.all(memberships.map(async x => ({
id: x.id,
createdAt: this.idService.parse(x.id).date.toISOString(),
userId: x.userId,
- user: await this.userEntityService.pack(x.userId),
+ user: _userMap.get(x.userId) ?? await this.userEntityService.pack(x.userId),
withReplies: x.withReplies,
})));
}
diff --git a/packages/backend/src/misc/json-schema.ts b/packages/backend/src/misc/json-schema.ts
index a620d7c94b..41e5bfe9e4 100644
--- a/packages/backend/src/misc/json-schema.ts
+++ b/packages/backend/src/misc/json-schema.ts
@@ -228,7 +228,7 @@ export type SchemaTypeDef<p extends Schema> =
p['items']['allOf'] extends ReadonlyArray<Schema> ? UnionToIntersection<UnionSchemaType<NonNullable<p['items']['allOf']>>>[] :
never
) :
- p['items'] extends NonNullable<Schema> ? SchemaTypeDef<p['items']>[] :
+ p['items'] extends NonNullable<Schema> ? SchemaType<p['items']>[] :
any[]
) :
p['anyOf'] extends ReadonlyArray<Schema> ? UnionSchemaType<p['anyOf']> & PartialIntersection<UnionSchemaType<p['anyOf']>> :
diff --git a/packages/backend/src/models/Antenna.ts b/packages/backend/src/models/Antenna.ts
index f5e819059e..33e6f48189 100644
--- a/packages/backend/src/models/Antenna.ts
+++ b/packages/backend/src/models/Antenna.ts
@@ -90,9 +90,6 @@ export class MiAntenna {
})
public expression: string | null;
- @Column('boolean')
- public notify: boolean;
-
@Index()
@Column('boolean', {
default: true,
diff --git a/packages/backend/src/models/Instance.ts b/packages/backend/src/models/Instance.ts
index 7dd4e5b10c..dd625f95d3 100644
--- a/packages/backend/src/models/Instance.ts
+++ b/packages/backend/src/models/Instance.ts
@@ -81,13 +81,22 @@ export class MiInstance {
public isNotResponding: boolean;
/**
- * ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã¸ã®é…ä¿¡ã‚’åœæ­¢ã™ã‚‹ã‹
+ * ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã¨ä¸é€šã«ãªã£ãŸæ—¥æ™‚
+ */
+ @Column('timestamp with time zone', {
+ nullable: true,
+ })
+ public notRespondingSince: Date | null;
+
+ /**
+ * ã“ã®ã‚¤ãƒ³ã‚¹ã‚¿ãƒ³ã‚¹ã¸ã®é…信状態
*/
@Index()
- @Column('boolean', {
- default: false,
+ @Column('enum', {
+ default: 'none',
+ enum: ['none', 'manuallySuspended', 'goneSuspended', 'autoSuspendedForNotResponding'],
})
- public isSuspended: boolean;
+ public suspensionState: 'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding';
@Column('varchar', {
length: 64, nullable: true,
diff --git a/packages/backend/src/models/Meta.ts b/packages/backend/src/models/Meta.ts
index 9b71cf6d57..fb021f0f6b 100644
--- a/packages/backend/src/models/Meta.ts
+++ b/packages/backend/src/models/Meta.ts
@@ -404,6 +404,12 @@ export class MiMeta {
public donationUrl: string | null;
@Column('varchar', {
+ length: 1024,
+ nullable: true,
+ })
+ public inquiryUrl: string | null;
+
+ @Column('varchar', {
length: 8192,
nullable: true,
})
diff --git a/packages/backend/src/models/Poll.ts b/packages/backend/src/models/Poll.ts
index c2693dbb19..ca985c8b24 100644
--- a/packages/backend/src/models/Poll.ts
+++ b/packages/backend/src/models/Poll.ts
@@ -8,6 +8,7 @@ import { noteVisibilities } from '@/types.js';
import { id } from './util/id.js';
import { MiNote } from './Note.js';
import type { MiUser } from './User.js';
+import type { MiChannel } from "@/models/Channel.js";
@Entity('poll')
export class MiPoll {
@@ -58,6 +59,14 @@ export class MiPoll {
comment: '[Denormalized]',
})
public userHost: string | null;
+
+ @Index()
+ @Column({
+ ...id(),
+ nullable: true,
+ comment: '[Denormalized]',
+ })
+ public channelId: MiChannel['id'] | null;
//#endregion
constructor(data: Partial<MiPoll>) {
diff --git a/packages/backend/src/models/json-schema/antenna.ts b/packages/backend/src/models/json-schema/antenna.ts
index 78cf6d3ba2..b5b9a5b42c 100644
--- a/packages/backend/src/models/json-schema/antenna.ts
+++ b/packages/backend/src/models/json-schema/antenna.ts
@@ -72,10 +72,6 @@ export const packedAntennaSchema = {
optional: false, nullable: false,
default: false,
},
- notify: {
- type: 'boolean',
- optional: false, nullable: false,
- },
excludeBots: {
type: 'boolean',
optional: false, nullable: false,
@@ -99,5 +95,10 @@ export const packedAntennaSchema = {
optional: false, nullable: false,
default: false,
},
+ notify: {
+ type: 'boolean',
+ optional: false, nullable: false,
+ default: false,
+ },
},
} as const;
diff --git a/packages/backend/src/models/json-schema/federation-instance.ts b/packages/backend/src/models/json-schema/federation-instance.ts
index 7b8ab22831..a602126dc8 100644
--- a/packages/backend/src/models/json-schema/federation-instance.ts
+++ b/packages/backend/src/models/json-schema/federation-instance.ts
@@ -45,6 +45,11 @@ export const packedFederationInstanceSchema = {
type: 'boolean',
optional: false, nullable: false,
},
+ suspensionState: {
+ type: 'string',
+ nullable: false, optional: false,
+ enum: ['none', 'manuallySuspended', 'goneSuspended', 'autoSuspendedForNotResponding'],
+ },
isBlocked: {
type: 'boolean',
optional: false, nullable: false,
diff --git a/packages/backend/src/models/json-schema/meta.ts b/packages/backend/src/models/json-schema/meta.ts
index 47a9c48c1c..7edd877f80 100644
--- a/packages/backend/src/models/json-schema/meta.ts
+++ b/packages/backend/src/models/json-schema/meta.ts
@@ -243,6 +243,10 @@ export const packedMetaLiteSchema = {
type: 'string',
optional: false, nullable: true,
},
+ inquiryUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
serverRules: {
type: 'array',
optional: false, nullable: false,
diff --git a/packages/backend/src/queue/processors/DeliverProcessorService.ts b/packages/backend/src/queue/processors/DeliverProcessorService.ts
index 5fed070929..b73195afc3 100644
--- a/packages/backend/src/queue/processors/DeliverProcessorService.ts
+++ b/packages/backend/src/queue/processors/DeliverProcessorService.ts
@@ -5,6 +5,7 @@
import { Inject, Injectable } from '@nestjs/common';
import * as Bull from 'bullmq';
+import { Not } from 'typeorm';
import { DI } from '@/di-symbols.js';
import type { InstancesRepository } from '@/models/_.js';
import type Logger from '@/logger.js';
@@ -62,7 +63,7 @@ export class DeliverProcessorService {
if (suspendedHosts == null) {
suspendedHosts = await this.instancesRepository.find({
where: {
- isSuspended: true,
+ suspensionState: Not('none'),
},
});
this.suspendedHostsCache.set(suspendedHosts);
@@ -79,6 +80,7 @@ export class DeliverProcessorService {
if (i.isNotResponding) {
this.federatedInstanceService.update(i.id, {
isNotResponding: false,
+ notRespondingSince: null,
});
}
@@ -98,7 +100,15 @@ export class DeliverProcessorService {
if (!i.isNotResponding) {
this.federatedInstanceService.update(i.id, {
isNotResponding: true,
+ notRespondingSince: new Date(),
});
+ } else if (i.notRespondingSince) {
+ // 1週間以上ä¸é€šãªã‚‰ã‚µã‚¹ãƒšãƒ³ãƒ‰
+ if (i.suspensionState === 'none' && i.notRespondingSince.getTime() <= Date.now() - 1000 * 60 * 60 * 24 * 7) {
+ this.federatedInstanceService.update(i.id, {
+ suspensionState: 'autoSuspendedForNotResponding',
+ });
+ }
}
this.apRequestChart.deliverFail();
@@ -116,7 +126,7 @@ export class DeliverProcessorService {
if (job.data.isSharedInbox && res.statusCode === 410) {
this.federatedInstanceService.fetch(host).then(i => {
this.federatedInstanceService.update(i.id, {
- isSuspended: true,
+ suspensionState: 'goneSuspended',
});
});
throw new Bull.UnrecoverableError(`${host} is gone`);
diff --git a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
index 1d8e90f367..88c4ea29c0 100644
--- a/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
+++ b/packages/backend/src/queue/processors/ExportAntennasProcessorService.ts
@@ -84,7 +84,6 @@ export class ExportAntennasProcessorService {
excludeBots: antenna.excludeBots,
withReplies: antenna.withReplies,
withFile: antenna.withFile,
- notify: antenna.notify,
}));
if (antennas.length - 1 !== index) {
write(', ');
diff --git a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
index ff1c04de06..e5b7c5ac52 100644
--- a/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
+++ b/packages/backend/src/queue/processors/ImportAntennasProcessorService.ts
@@ -47,9 +47,8 @@ const validate = new Ajv().compile({
excludeBots: { type: 'boolean' },
withReplies: { type: 'boolean' },
withFile: { type: 'boolean' },
- notify: { type: 'boolean' },
},
- required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'],
+ required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile'],
});
@Injectable()
@@ -92,7 +91,6 @@ export class ImportAntennasProcessorService {
excludeBots: antenna.excludeBots,
withReplies: antenna.withReplies,
withFile: antenna.withFile,
- notify: antenna.notify,
}).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0]));
this.logger.succ('Antenna created: ' + result.id);
this.globalEventService.publishInternalEvent('antennaCreated', result);
diff --git a/packages/backend/src/queue/processors/InboxProcessorService.ts b/packages/backend/src/queue/processors/InboxProcessorService.ts
index ce32a482fd..641b8b8607 100644
--- a/packages/backend/src/queue/processors/InboxProcessorService.ts
+++ b/packages/backend/src/queue/processors/InboxProcessorService.ts
@@ -193,6 +193,8 @@ export class InboxProcessorService {
this.federatedInstanceService.update(i.id, {
latestRequestReceivedAt: new Date(),
isNotResponding: false,
+ // ã‚‚ã—サーãƒãƒ¼ãŒæ­»ã‚“ã§ã‚‹ãŸã‚ã«é…ä¿¡ãŒæ­¢ã¾ã£ã¦ã„ãŸå ´åˆã«ã¯è‡ªå‹•çš„ã«å¾©æ´»ã•ã›ã¦ã‚ã’ã‚‹
+ suspensionState: i.suspensionState === 'autoSuspendedForNotResponding' ? 'none' : undefined,
});
this.fetchInstanceMetadataService.fetchInstanceMetadata(i);
@@ -207,13 +209,22 @@ export class InboxProcessorService {
// アクティビティを処ç†
try {
- await this.apInboxService.performActivity(authUser.user, activity);
+ const result = await this.apInboxService.performActivity(authUser.user, activity);
+ if (result && !result.startsWith('ok')) {
+ this.logger.warn(`inbox activity ignored (maybe): id=${activity.id} reason=${result}`);
+ return result;
+ }
} catch (e) {
if (e instanceof IdentifiableError) {
if (e.id === '689ee33f-f97c-479a-ac49-1b9f8140af99') {
return 'blocked notes with prohibited words';
}
- if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') return 'actor has been suspended';
+ if (e.id === '85ab9bd7-3a41-4530-959d-f07073900109') {
+ return 'actor has been suspended';
+ }
+ if (e.id === 'd450b8a9-48e4-4dab-ae36-f4db763fda7c') { // invalid Note
+ return e.message;
+ }
}
throw e;
}
diff --git a/packages/backend/src/server/HealthServerService.ts b/packages/backend/src/server/HealthServerService.ts
new file mode 100644
index 0000000000..2c3ed85925
--- /dev/null
+++ b/packages/backend/src/server/HealthServerService.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Inject, Injectable } from '@nestjs/common';
+import * as Redis from 'ioredis';
+import { DataSource } from 'typeorm';
+import { bindThis } from '@/decorators.js';
+import { DI } from '@/di-symbols.js';
+import { readyRef } from '@/boot/ready.js';
+import type { FastifyInstance, FastifyPluginOptions } from 'fastify';
+import type { MeiliSearch } from 'meilisearch';
+
+@Injectable()
+export class HealthServerService {
+ constructor(
+ @Inject(DI.redis)
+ private redis: Redis.Redis,
+
+ @Inject(DI.redisForPub)
+ private redisForPub: Redis.Redis,
+
+ @Inject(DI.redisForSub)
+ private redisForSub: Redis.Redis,
+
+ @Inject(DI.redisForTimelines)
+ private redisForTimelines: Redis.Redis,
+
+ @Inject(DI.db)
+ private db: DataSource,
+
+ @Inject(DI.meilisearch)
+ private meilisearch: MeiliSearch | null,
+ ) {}
+
+ @bindThis
+ public createServer(fastify: FastifyInstance, options: FastifyPluginOptions, done: (err?: Error) => void) {
+ fastify.get('/', async (request, reply) => {
+ reply.code(await Promise.all([
+ new Promise<void>((resolve, reject) => readyRef.value ? resolve() : reject()),
+ this.redis.ping(),
+ this.redisForPub.ping(),
+ this.redisForSub.ping(),
+ this.redisForTimelines.ping(),
+ this.db.query('SELECT 1'),
+ ...(this.meilisearch ? [this.meilisearch.health()] : []),
+ ]).then(() => 200, () => 503));
+ reply.header('Cache-Control', 'no-store');
+ });
+
+ done();
+ }
+}
diff --git a/packages/backend/src/server/NodeinfoServerService.ts b/packages/backend/src/server/NodeinfoServerService.ts
index d4f4c8b752..716bb0944b 100644
--- a/packages/backend/src/server/NodeinfoServerService.ts
+++ b/packages/backend/src/server/NodeinfoServerService.ts
@@ -36,12 +36,12 @@ export class NodeinfoServerService {
@bindThis
public getLinks() {
return [{
- rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
- href: this.config.url + nodeinfo2_1path
- }, {
- rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
- href: this.config.url + nodeinfo2_0path,
- }];
+ rel: 'http://nodeinfo.diaspora.software/ns/schema/2.1',
+ href: this.config.url + nodeinfo2_1path,
+ }, {
+ rel: 'http://nodeinfo.diaspora.software/ns/schema/2.0',
+ href: this.config.url + nodeinfo2_0path,
+ }];
}
@bindThis
@@ -107,6 +107,7 @@ export class NodeinfoServerService {
langs: meta.langs,
tosUrl: meta.termsOfServiceUrl,
privacyPolicyUrl: meta.privacyPolicyUrl,
+ inquiryUrl: meta.inquiryUrl,
impressumUrl: meta.impressumUrl,
donationUrl: meta.donationUrl,
repositoryUrl: meta.repositoryUrl,
diff --git a/packages/backend/src/server/ServerModule.ts b/packages/backend/src/server/ServerModule.ts
index e0c4768ffc..39c8f67b8e 100644
--- a/packages/backend/src/server/ServerModule.ts
+++ b/packages/backend/src/server/ServerModule.ts
@@ -8,6 +8,7 @@ import { EndpointsModule } from '@/server/api/EndpointsModule.js';
import { CoreModule } from '@/core/CoreModule.js';
import { ApiCallService } from './api/ApiCallService.js';
import { FileServerService } from './FileServerService.js';
+import { HealthServerService } from './HealthServerService.js';
import { NodeinfoServerService } from './NodeinfoServerService.js';
import { ServerService } from './ServerService.js';
import { WellKnownServerService } from './WellKnownServerService.js';
@@ -58,6 +59,7 @@ import { ReversiGameChannelService } from './api/stream/channels/reversi-game.js
ClientServerService,
ClientLoggerService,
FeedService,
+ HealthServerService,
UrlPreviewService,
ActivityPubServerService,
FileServerService,
diff --git a/packages/backend/src/server/ServerService.ts b/packages/backend/src/server/ServerService.ts
index 2cf826deac..9eddf434f7 100644
--- a/packages/backend/src/server/ServerService.ts
+++ b/packages/backend/src/server/ServerService.ts
@@ -28,6 +28,7 @@ import { ApiServerService } from './api/ApiServerService.js';
import { StreamingApiServerService } from './api/StreamingApiServerService.js';
import { WellKnownServerService } from './WellKnownServerService.js';
import { FileServerService } from './FileServerService.js';
+import { HealthServerService } from './HealthServerService.js';
import { ClientServerService } from './web/ClientServerService.js';
import { OpenApiServerService } from './api/openapi/OpenApiServerService.js';
import { MastodonApiServerService } from './api/mastodon/MastodonApiServerService.js';
@@ -63,6 +64,7 @@ export class ServerService implements OnApplicationShutdown {
private wellKnownServerService: WellKnownServerService,
private nodeinfoServerService: NodeinfoServerService,
private fileServerService: FileServerService,
+ private healthServerService: HealthServerService,
private clientServerService: ClientServerService,
private globalEventService: GlobalEventService,
private loggerService: LoggerService,
@@ -110,6 +112,7 @@ export class ServerService implements OnApplicationShutdown {
fastify.register(this.nodeinfoServerService.createServer);
fastify.register(this.wellKnownServerService.createServer);
fastify.register(this.oauth2ProviderService.createServer, { prefix: '/oauth' });
+ fastify.register(this.healthServerService.createServer, { prefix: '/healthz' });
fastify.get<{ Params: { path: string }; Querystring: { static?: any; badge?: any; }; }>('/emoji/:path(.*)', async (request, reply) => {
const path = request.params.path;
diff --git a/packages/backend/src/server/api/ApiCallService.ts b/packages/backend/src/server/api/ApiCallService.ts
index 9836689872..271ef80554 100644
--- a/packages/backend/src/server/api/ApiCallService.ts
+++ b/packages/backend/src/server/api/ApiCallService.ts
@@ -7,6 +7,7 @@ import { randomUUID } from 'node:crypto';
import * as fs from 'node:fs';
import * as stream from 'node:stream/promises';
import { Inject, Injectable } from '@nestjs/common';
+import * as Sentry from '@sentry/node';
import { DI } from '@/di-symbols.js';
import { getIpHash } from '@/misc/get-ip-hash.js';
import type { MiLocalUser, MiUser } from '@/models/User.js';
@@ -17,6 +18,7 @@ import { MetaService } from '@/core/MetaService.js';
import { createTemp } from '@/misc/create-temp.js';
import { bindThis } from '@/decorators.js';
import { RoleService } from '@/core/RoleService.js';
+import type { Config } from '@/config.js';
import { ApiError } from './error.js';
import { RateLimiterService } from './RateLimiterService.js';
import { ApiLoggerService } from './ApiLoggerService.js';
@@ -38,6 +40,9 @@ export class ApiCallService implements OnApplicationShutdown {
private userIpHistoriesClearIntervalId: NodeJS.Timeout;
constructor(
+ @Inject(DI.config)
+ private config: Config,
+
@Inject(DI.userIpsRepository)
private userIpsRepository: UserIpsRepository,
@@ -88,6 +93,48 @@ export class ApiCallService implements OnApplicationShutdown {
}
}
+ #onExecError(ep: IEndpoint, data: any, err: Error): void {
+ if (err instanceof ApiError || err instanceof AuthenticationError) {
+ throw err;
+ } else {
+ const errId = randomUUID();
+ this.logger.error(`Internal error occurred in ${ep.name}: ${err.message}`, {
+ ep: ep.name,
+ ps: data,
+ e: {
+ message: err.message,
+ code: err.name,
+ stack: err.stack,
+ id: errId,
+ },
+ });
+ console.error(err, errId);
+
+ if (this.config.sentryForBackend) {
+ Sentry.captureMessage(`Internal error occurred in ${ep.name}: ${err.message}`, {
+ extra: {
+ ep: ep.name,
+ ps: data,
+ e: {
+ message: err.message,
+ code: err.name,
+ stack: err.stack,
+ id: errId,
+ },
+ },
+ });
+ }
+
+ throw new ApiError(null, {
+ e: {
+ message: err.message,
+ code: err.name,
+ id: errId,
+ },
+ });
+ }
+ }
+
@bindThis
public handleRequest(
endpoint: IEndpoint & { exec: any },
@@ -362,31 +409,11 @@ export class ApiCallService implements OnApplicationShutdown {
}
// API invoking
- return await ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => {
- if (err instanceof ApiError || err instanceof AuthenticationError) {
- throw err;
- } else {
- const errId = randomUUID();
- this.logger.error(`Internal error occurred in ${ep.name}: ${err.message}`, {
- ep: ep.name,
- ps: data,
- e: {
- message: err.message,
- code: err.name,
- stack: err.stack,
- id: errId,
- },
- });
- console.error(err, errId);
- throw new ApiError(null, {
- e: {
- message: err.message,
- code: err.name,
- id: errId,
- },
- });
- }
- });
+ if (this.config.sentryForBackend) {
+ return await Sentry.startSpan({ name: 'API: ' + ep.name }, () => ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => this.#onExecError(ep, data, err)));
+ } else {
+ return await ep.exec(data, user, token, file, request.ip, request.headers).catch((err: Error) => this.#onExecError(ep, data, err));
+ }
}
@bindThis
diff --git a/packages/backend/src/server/api/ApiServerService.ts b/packages/backend/src/server/api/ApiServerService.ts
index e99244cdd0..4a5935f930 100644
--- a/packages/backend/src/server/api/ApiServerService.ts
+++ b/packages/backend/src/server/api/ApiServerService.ts
@@ -137,7 +137,7 @@ export class ApiServerService {
const instances = await this.instancesRepository.find({
select: ['host'],
where: {
- isSuspended: false,
+ suspensionState: 'none',
},
});
diff --git a/packages/backend/src/server/api/EndpointsModule.ts b/packages/backend/src/server/api/EndpointsModule.ts
index f2945f477c..f44635fba0 100644
--- a/packages/backend/src/server/api/EndpointsModule.ts
+++ b/packages/backend/src/server/api/EndpointsModule.ts
@@ -88,6 +88,7 @@ import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'
import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
import * as ep___admin_roles_users from './endpoints/admin/roles/users.js';
import * as ep___announcements from './endpoints/announcements.js';
+import * as ep___announcements_show from './endpoints/announcements/show.js';
import * as ep___antennas_create from './endpoints/antennas/create.js';
import * as ep___antennas_delete from './endpoints/antennas/delete.js';
import * as ep___antennas_list from './endpoints/antennas/list.js';
@@ -473,6 +474,7 @@ const $admin_roles_unassign: Provider = { provide: 'ep:admin/roles/unassign', us
const $admin_roles_updateDefaultPolicies: Provider = { provide: 'ep:admin/roles/update-default-policies', useClass: ep___admin_roles_updateDefaultPolicies.default };
const $admin_roles_users: Provider = { provide: 'ep:admin/roles/users', useClass: ep___admin_roles_users.default };
const $announcements: Provider = { provide: 'ep:announcements', useClass: ep___announcements.default };
+const $announcements_show: Provider = { provide: 'ep:announcements/show', useClass: ep___announcements_show.default };
const $antennas_create: Provider = { provide: 'ep:antennas/create', useClass: ep___antennas_create.default };
const $antennas_delete: Provider = { provide: 'ep:antennas/delete', useClass: ep___antennas_delete.default };
const $antennas_list: Provider = { provide: 'ep:antennas/list', useClass: ep___antennas_list.default };
@@ -862,6 +864,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
$admin_roles_updateDefaultPolicies,
$admin_roles_users,
$announcements,
+ $announcements_show,
$antennas_create,
$antennas_delete,
$antennas_list,
@@ -1245,6 +1248,7 @@ const $reversi_verify: Provider = { provide: 'ep:reversi/verify', useClass: ep__
$admin_roles_updateDefaultPolicies,
$admin_roles_users,
$announcements,
+ $announcements_show,
$antennas_create,
$antennas_delete,
$antennas_list,
diff --git a/packages/backend/src/server/api/endpoints.ts b/packages/backend/src/server/api/endpoints.ts
index f83d2cacff..89f60933ef 100644
--- a/packages/backend/src/server/api/endpoints.ts
+++ b/packages/backend/src/server/api/endpoints.ts
@@ -88,6 +88,7 @@ import * as ep___admin_roles_unassign from './endpoints/admin/roles/unassign.js'
import * as ep___admin_roles_updateDefaultPolicies from './endpoints/admin/roles/update-default-policies.js';
import * as ep___admin_roles_users from './endpoints/admin/roles/users.js';
import * as ep___announcements from './endpoints/announcements.js';
+import * as ep___announcements_show from './endpoints/announcements/show.js';
import * as ep___antennas_create from './endpoints/antennas/create.js';
import * as ep___antennas_delete from './endpoints/antennas/delete.js';
import * as ep___antennas_list from './endpoints/antennas/list.js';
@@ -471,6 +472,7 @@ const eps = [
['admin/roles/update-default-policies', ep___admin_roles_updateDefaultPolicies],
['admin/roles/users', ep___admin_roles_users],
['announcements', ep___announcements],
+ ['announcements/show', ep___announcements_show],
['antennas/create', ep___antennas_create],
['antennas/delete', ep___antennas_delete],
['antennas/list', ep___antennas_list],
diff --git a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
index 4ababae9f2..9984441de7 100644
--- a/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
+++ b/packages/backend/src/server/api/endpoints/admin/federation/update-instance.ts
@@ -47,13 +47,21 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
throw new Error('instance not found');
}
+ const isSuspendedBefore = instance.suspensionState !== 'none';
+ let suspensionState: undefined | 'manuallySuspended' | 'none';
+
+ if (ps.isSuspended != null && isSuspendedBefore !== ps.isSuspended) {
+ suspensionState = ps.isSuspended ? 'manuallySuspended' : 'none';
+ }
+
await this.federatedInstanceService.update(instance.id, {
isSuspended: ps.isSuspended,
+ suspensionState,
isNSFW: ps.isNSFW,
moderationNote: ps.moderationNote,
});
- if (ps.isSuspended != null && instance.isSuspended !== ps.isSuspended) {
+ if (ps.isSuspended != null && isSuspendedBefore !== ps.isSuspended) {
if (ps.isSuspended) {
this.moderationLogService.log(me, 'suspendRemoteInstance', {
id: instance.id,
diff --git a/packages/backend/src/server/api/endpoints/admin/meta.ts b/packages/backend/src/server/api/endpoints/admin/meta.ts
index 1a4ae844b5..ca4d63b834 100644
--- a/packages/backend/src/server/api/endpoints/admin/meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/meta.ts
@@ -458,6 +458,10 @@ export const meta = {
type: 'string',
optional: false, nullable: true,
},
+ inquiryUrl: {
+ type: 'string',
+ optional: false, nullable: true,
+ },
repositoryUrl: {
type: 'string',
optional: false, nullable: true,
@@ -545,6 +549,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
impressumUrl: instance.impressumUrl,
donationUrl: instance.donationUrl,
privacyPolicyUrl: instance.privacyPolicyUrl,
+ inquiryUrl: instance.inquiryUrl,
disableRegistration: instance.disableRegistration,
emailRequiredForSignup: instance.emailRequiredForSignup,
approvalRequiredForSignup: instance.approvalRequiredForSignup,
diff --git a/packages/backend/src/server/api/endpoints/admin/roles/users.ts b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
index 45758d4f50..198166bec2 100644
--- a/packages/backend/src/server/api/endpoints/admin/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/roles/users.ts
@@ -89,10 +89,13 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.limit(ps.limit)
.getMany();
+ const _users = assigns.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
+ .then(users => new Map(users.map(u => [u.id, u])));
return await Promise.all(assigns.map(async assign => ({
id: assign.id,
createdAt: this.idService.parse(assign.id).date.toISOString(),
- user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
+ user: _userMap.get(assign.userId) ?? await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
expiresAt: assign.expiresAt?.toISOString() ?? null,
})));
});
diff --git a/packages/backend/src/server/api/endpoints/admin/show-users.ts b/packages/backend/src/server/api/endpoints/admin/show-users.ts
index 685da928e3..5f16519403 100644
--- a/packages/backend/src/server/api/endpoints/admin/show-users.ts
+++ b/packages/backend/src/server/api/endpoints/admin/show-users.ts
@@ -16,7 +16,7 @@ export const meta = {
requireCredential: true,
requireModerator: true,
- kind: 'read:admin:show-users',
+ kind: 'read:admin:show-user',
res: {
type: 'array',
diff --git a/packages/backend/src/server/api/endpoints/admin/update-meta.ts b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
index 95dd7346e7..015a1e1f7c 100644
--- a/packages/backend/src/server/api/endpoints/admin/update-meta.ts
+++ b/packages/backend/src/server/api/endpoints/admin/update-meta.ts
@@ -113,6 +113,7 @@ export const paramDef = {
impressumUrl: { type: 'string', nullable: true },
donationUrl: { type: 'string', nullable: true },
privacyPolicyUrl: { type: 'string', nullable: true },
+ inquiryUrl: { type: 'string', nullable: true },
useObjectStorage: { type: 'boolean' },
objectStorageBaseUrl: { type: 'string', nullable: true },
objectStorageBucket: { type: 'string', nullable: true },
@@ -430,6 +431,10 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
set.privacyPolicyUrl = ps.privacyPolicyUrl;
}
+ if (ps.inquiryUrl !== undefined) {
+ set.inquiryUrl = ps.inquiryUrl;
+ }
+
if (ps.useObjectStorage !== undefined) {
set.useObjectStorage = ps.useObjectStorage;
}
diff --git a/packages/backend/src/server/api/endpoints/announcements.ts b/packages/backend/src/server/api/endpoints/announcements.ts
index 3b12f5b62c..ff8dd73605 100644
--- a/packages/backend/src/server/api/endpoints/announcements.ts
+++ b/packages/backend/src/server/api/endpoints/announcements.ts
@@ -7,9 +7,9 @@ import { Inject, Injectable } from '@nestjs/common';
import { Brackets } from 'typeorm';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { QueryService } from '@/core/QueryService.js';
-import { AnnouncementService } from '@/core/AnnouncementService.js';
+import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
import { DI } from '@/di-symbols.js';
-import type { AnnouncementReadsRepository, AnnouncementsRepository } from '@/models/_.js';
+import type { AnnouncementsRepository } from '@/models/_.js';
export const meta = {
tags: ['meta'],
@@ -44,11 +44,8 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
@Inject(DI.announcementsRepository)
private announcementsRepository: AnnouncementsRepository,
- @Inject(DI.announcementReadsRepository)
- private announcementReadsRepository: AnnouncementReadsRepository,
-
private queryService: QueryService,
- private announcementService: AnnouncementService,
+ private announcementEntityService: AnnouncementEntityService,
) {
super(meta, paramDef, async (ps, me) => {
const query = this.queryService.makePaginationQuery(this.announcementsRepository.createQueryBuilder('announcement'), ps.sinceId, ps.untilId)
@@ -60,7 +57,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const announcements = await query.limit(ps.limit).getMany();
- return this.announcementService.packMany(announcements, me);
+ return this.announcementEntityService.packMany(announcements, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/announcements/show.ts b/packages/backend/src/server/api/endpoints/announcements/show.ts
new file mode 100644
index 0000000000..6312a0a54c
--- /dev/null
+++ b/packages/backend/src/server/api/endpoints/announcements/show.ts
@@ -0,0 +1,54 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import { Injectable } from '@nestjs/common';
+import { EntityNotFoundError } from 'typeorm';
+import { Endpoint } from '@/server/api/endpoint-base.js';
+import { AnnouncementService } from '@/core/AnnouncementService.js';
+import { ApiError } from '../../error.js';
+
+export const meta = {
+ tags: ['meta'],
+
+ requireCredential: false,
+
+ res: {
+ type: 'object',
+ optional: false, nullable: false,
+ ref: 'Announcement',
+ },
+
+ errors: {
+ noSuchAnnouncement: {
+ message: 'No such announcement.',
+ code: 'NO_SUCH_ANNOUNCEMENT',
+ id: 'b57b5e1d-4f49-404a-9edb-46b00268f121',
+ },
+ },
+} as const;
+
+export const paramDef = {
+ type: 'object',
+ properties: {
+ announcementId: { type: 'string', format: 'misskey:id' },
+ },
+ required: ['announcementId'],
+} as const;
+
+@Injectable()
+export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-disable-line import/no-default-export
+ constructor(
+ private announcementService: AnnouncementService,
+ ) {
+ super(meta, paramDef, async (ps, me) => {
+ try {
+ return await this.announcementService.getAnnouncement(ps.announcementId, me);
+ } catch (err) {
+ if (err instanceof EntityNotFoundError) throw new ApiError(meta.errors.noSuchAnnouncement);
+ throw err;
+ }
+ });
+ }
+}
diff --git a/packages/backend/src/server/api/endpoints/antennas/create.ts b/packages/backend/src/server/api/endpoints/antennas/create.ts
index 57c8eb4958..6b7bacb054 100644
--- a/packages/backend/src/server/api/endpoints/antennas/create.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/create.ts
@@ -67,9 +67,8 @@ export const paramDef = {
excludeBots: { type: 'boolean' },
withReplies: { type: 'boolean' },
withFile: { type: 'boolean' },
- notify: { type: 'boolean' },
},
- required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile', 'notify'],
+ required: ['name', 'src', 'keywords', 'excludeKeywords', 'users', 'caseSensitive', 'withReplies', 'withFile'],
} as const;
@Injectable()
@@ -128,7 +127,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
excludeBots: ps.excludeBots,
withReplies: ps.withReplies,
withFile: ps.withFile,
- notify: ps.notify,
}).then(x => this.antennasRepository.findOneByOrFail(x.identifiers[0]));
this.globalEventService.publishInternalEvent('antennaCreated', antenna);
diff --git a/packages/backend/src/server/api/endpoints/antennas/update.ts b/packages/backend/src/server/api/endpoints/antennas/update.ts
index e6720aacf8..0c30bca9e0 100644
--- a/packages/backend/src/server/api/endpoints/antennas/update.ts
+++ b/packages/backend/src/server/api/endpoints/antennas/update.ts
@@ -66,7 +66,6 @@ export const paramDef = {
excludeBots: { type: 'boolean' },
withReplies: { type: 'boolean' },
withFile: { type: 'boolean' },
- notify: { type: 'boolean' },
},
required: ['antennaId'],
} as const;
@@ -124,7 +123,6 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
excludeBots: ps.excludeBots,
withReplies: ps.withReplies,
withFile: ps.withFile,
- notify: ps.notify,
isActive: true,
lastUsedAt: new Date(),
});
diff --git a/packages/backend/src/server/api/endpoints/drive/files/find.ts b/packages/backend/src/server/api/endpoints/drive/files/find.ts
index 595a6957b2..502d42f9e0 100644
--- a/packages/backend/src/server/api/endpoints/drive/files/find.ts
+++ b/packages/backend/src/server/api/endpoints/drive/files/find.ts
@@ -54,7 +54,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
folderId: ps.folderId ?? IsNull(),
});
- return await Promise.all(files.map(file => this.driveFileEntityService.pack(file, { self: true })));
+ return await this.driveFileEntityService.packMany(files, { self: true });
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/following/requests/list.ts b/packages/backend/src/server/api/endpoints/following/requests/list.ts
index 88f559138b..fa59e38976 100644
--- a/packages/backend/src/server/api/endpoints/following/requests/list.ts
+++ b/packages/backend/src/server/api/endpoints/following/requests/list.ts
@@ -71,7 +71,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.limit(ps.limit)
.getMany();
- return await Promise.all(requests.map(req => this.followRequestEntityService.pack(req)));
+ return await this.followRequestEntityService.packMany(requests, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/i/notifications.ts b/packages/backend/src/server/api/endpoints/i/notifications.ts
index 594e8b95c8..5e97b90f99 100644
--- a/packages/backend/src/server/api/endpoints/i/notifications.ts
+++ b/packages/backend/src/server/api/endpoints/i/notifications.ts
@@ -7,7 +7,7 @@ import { In } from 'typeorm';
import * as Redis from 'ioredis';
import { Inject, Injectable } from '@nestjs/common';
import type { NotesRepository } from '@/models/_.js';
-import { obsoleteNotificationTypes, notificationTypes, FilterUnionByProperty } from '@/types.js';
+import { FilterUnionByProperty, notificationTypes, obsoleteNotificationTypes } from '@/types.js';
import { Endpoint } from '@/server/api/endpoint-base.js';
import { NoteReadService } from '@/core/NoteReadService.js';
import { NotificationEntityService } from '@/core/entities/NotificationEntityService.js';
@@ -84,27 +84,51 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const includeTypes = ps.includeTypes && ps.includeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][];
const excludeTypes = ps.excludeTypes && ps.excludeTypes.filter(type => !(obsoleteNotificationTypes).includes(type as any)) as typeof notificationTypes[number][];
- const limit = ps.limit + (ps.untilId ? 1 : 0) + (ps.sinceId ? 1 : 0); // untilIdã«æŒ‡å®šã—ãŸã‚‚ã®ã‚‚å«ã¾ã‚Œã‚‹ãŸã‚+1
- const notificationsRes = await this.redisClient.xrevrange(
- `notificationTimeline:${me.id}`,
- ps.untilId ? this.idService.parse(ps.untilId).date.getTime() : '+',
- ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime() : '-',
- 'COUNT', limit);
+ let sinceTime = ps.sinceId ? this.idService.parse(ps.sinceId).date.getTime().toString() : null;
+ let untilTime = ps.untilId ? this.idService.parse(ps.untilId).date.getTime().toString() : null;
- if (notificationsRes.length === 0) {
- return [];
- }
+ let notifications: MiNotification[];
+ for (;;) {
+ let notificationsRes: [id: string, fields: string[]][];
- let notifications = notificationsRes.map(x => JSON.parse(x[1][1])).filter(x => x.id !== ps.untilId && x !== ps.sinceId) as MiNotification[];
+ // sinceidã®ã¿ã®å ´åˆã¯å¤ã„é †ã€ãã†ã§ãªã„å ´åˆã¯æ–°ã—ã„順。 QueryService.makePaginationQueryã‚‚å‚ç…§
+ if (sinceTime && !untilTime) {
+ notificationsRes = await this.redisClient.xrange(
+ `notificationTimeline:${me.id}`,
+ '(' + sinceTime,
+ '+',
+ 'COUNT', ps.limit);
+ } else {
+ notificationsRes = await this.redisClient.xrevrange(
+ `notificationTimeline:${me.id}`,
+ untilTime ? '(' + untilTime : '+',
+ sinceTime ? '(' + sinceTime : '-',
+ 'COUNT', ps.limit);
+ }
- if (includeTypes && includeTypes.length > 0) {
- notifications = notifications.filter(notification => includeTypes.includes(notification.type));
- } else if (excludeTypes && excludeTypes.length > 0) {
- notifications = notifications.filter(notification => !excludeTypes.includes(notification.type));
- }
+ if (notificationsRes.length === 0) {
+ return [];
+ }
- if (notifications.length === 0) {
- return [];
+ notifications = notificationsRes.map(x => JSON.parse(x[1][1])) as MiNotification[];
+
+ if (includeTypes && includeTypes.length > 0) {
+ notifications = notifications.filter(notification => includeTypes.includes(notification.type));
+ } else if (excludeTypes && excludeTypes.length > 0) {
+ notifications = notifications.filter(notification => !excludeTypes.includes(notification.type));
+ }
+
+ if (notifications.length !== 0) {
+ // 通知ãŒï¼‘件以上ã‚ã‚‹å ´åˆã¯è¿”ã™
+ break;
+ }
+
+ // フィルタã—ãŸã“ã¨ã§é€šçŸ¥ãŒ0ä»¶ã«ãªã£ãŸå ´åˆã€æ¬¡ã®ãƒšãƒ¼ã‚¸ã‚’å–å¾—ã™ã‚‹
+ if (ps.sinceId && !ps.untilId) {
+ sinceTime = notificationsRes[notificationsRes.length - 1][0];
+ } else {
+ untilTime = notificationsRes[notificationsRes.length - 1][0];
+ }
}
// Mark all as read
diff --git a/packages/backend/src/server/api/endpoints/i/update.ts b/packages/backend/src/server/api/endpoints/i/update.ts
index 06edb28578..aa2f85845f 100644
--- a/packages/backend/src/server/api/endpoints/i/update.ts
+++ b/packages/backend/src/server/api/endpoints/i/update.ts
@@ -530,26 +530,32 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
private async verifyLink(url: string, user: MiLocalUser) {
if (!safeForSql(url)) return;
- const html = await this.httpRequestService.getHtml(url);
+ try {
+ const html = await this.httpRequestService.getHtml(url);
- const { window } = new JSDOM(html);
- const doc = window.document;
+ const { window } = new JSDOM(html);
+ const doc = window.document;
- const myLink = `${this.config.url}/@${user.username}`;
+ const myLink = `${this.config.url}/@${user.username}`;
- const aEls = Array.from(doc.getElementsByTagName('a'));
- const linkEls = Array.from(doc.getElementsByTagName('link'));
+ const aEls = Array.from(doc.getElementsByTagName('a'));
+ const linkEls = Array.from(doc.getElementsByTagName('link'));
- const includesMyLink = aEls.some(a => a.href === myLink);
- const includesRelMeLinks = [...aEls, ...linkEls].some(link => link.rel === 'me' && link.href === myLink);
+ const includesMyLink = aEls.some(a => a.href === myLink);
+ const includesRelMeLinks = [...aEls, ...linkEls].some(link => link.rel === 'me' && link.href === myLink);
- if (includesMyLink || includesRelMeLinks) {
- await this.userProfilesRepository.createQueryBuilder('profile').update()
- .where('userId = :userId', { userId: user.id })
- .set({
- verifiedLinks: () => `array_append("verifiedLinks", '${url}')`, // ã“ã“ã§SQLインジェクションã•れãã†ãªã®ã§ã¨ã‚Šã‚ãˆãš safeForSql ã§å¼¾ã„ã¦ã„ã‚‹
- })
- .execute();
+ if (includesMyLink || includesRelMeLinks) {
+ await this.userProfilesRepository.createQueryBuilder('profile').update()
+ .where('userId = :userId', { userId: user.id })
+ .set({
+ verifiedLinks: () => `array_append("verifiedLinks", '${url}')`, // ã“ã“ã§SQLインジェクションã•れãã†ãªã®ã§ã¨ã‚Šã‚ãˆãš safeForSql ã§å¼¾ã„ã¦ã„ã‚‹
+ })
+ .execute();
+ }
+
+ window.close();
+ } catch (err) {
+ // ãªã«ã‚‚ã—ãªã„
}
}
}
diff --git a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
index ba38573065..4fd6f8682d 100644
--- a/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
+++ b/packages/backend/src/server/api/endpoints/notes/polls/recommendation.ts
@@ -32,6 +32,7 @@ export const paramDef = {
properties: {
limit: { type: 'integer', minimum: 1, maximum: 100, default: 10 },
offset: { type: 'integer', default: 0 },
+ excludeChannels: { type: 'boolean', default: false },
},
required: [],
} as const;
@@ -86,6 +87,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
query.setParameters(mutingQuery.getParameters());
//#endregion
+ //#region exclude channels
+ if (ps.excludeChannels) {
+ query.andWhere('poll.channelId IS NULL');
+ }
+ //#endregion
+
const polls = await query
.orderBy('poll.noteId', 'DESC')
.limit(ps.limit)
diff --git a/packages/backend/src/server/api/endpoints/notes/reactions.ts b/packages/backend/src/server/api/endpoints/notes/reactions.ts
index 3beb5064ae..7e334df93e 100644
--- a/packages/backend/src/server/api/endpoints/notes/reactions.ts
+++ b/packages/backend/src/server/api/endpoints/notes/reactions.ts
@@ -79,7 +79,7 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const reactions = await query.limit(ps.limit).getMany();
- return await Promise.all(reactions.map(reaction => this.noteReactionEntityService.pack(reaction, me)));
+ return await this.noteReactionEntityService.packMany(reactions, me);
});
}
}
diff --git a/packages/backend/src/server/api/endpoints/roles/users.ts b/packages/backend/src/server/api/endpoints/roles/users.ts
index 85d100ce1c..48d350af59 100644
--- a/packages/backend/src/server/api/endpoints/roles/users.ts
+++ b/packages/backend/src/server/api/endpoints/roles/users.ts
@@ -92,9 +92,12 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
.limit(ps.limit)
.getMany();
+ const _users = assigns.map(({ user, userId }) => user ?? userId);
+ const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
+ .then(users => new Map(users.map(u => [u.id, u])));
return await Promise.all(assigns.map(async assign => ({
id: assign.id,
- user: await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
+ user: _userMap.get(assign.userId) ?? await this.userEntityService.pack(assign.user!, me, { schema: 'UserDetailed' }),
})));
});
}
diff --git a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
index 02aa037466..9248a2fa68 100644
--- a/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
+++ b/packages/backend/src/server/api/endpoints/users/get-frequently-replied-users.ts
@@ -118,12 +118,14 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
const repliedUsersSorted = Object.keys(repliedUsers).sort((a, b) => repliedUsers[b] - repliedUsers[a]);
// Extract top replied users
- const topRepliedUsers = repliedUsersSorted.slice(0, ps.limit);
+ const topRepliedUserIds = repliedUsersSorted.slice(0, ps.limit);
// Make replies object (includes weights)
- const repliesObj = await Promise.all(topRepliedUsers.map(async (user) => ({
- user: await this.userEntityService.pack(user, me, { schema: 'UserDetailed' }),
- weight: repliedUsers[user] / peak,
+ const _userMap = await this.userEntityService.packMany(topRepliedUserIds, me, { schema: 'UserDetailed' })
+ .then(users => new Map(users.map(u => [u.id, u])));
+ const repliesObj = await Promise.all(topRepliedUserIds.map(async (userId) => ({
+ user: _userMap.get(userId) ?? await this.userEntityService.pack(userId, me, { schema: 'UserDetailed' }),
+ weight: repliedUsers[userId] / peak,
})));
return repliesObj;
diff --git a/packages/backend/src/server/api/endpoints/users/show.ts b/packages/backend/src/server/api/endpoints/users/show.ts
index bd81989cb9..062326e28d 100644
--- a/packages/backend/src/server/api/endpoints/users/show.ts
+++ b/packages/backend/src/server/api/endpoints/users/show.ts
@@ -110,14 +110,16 @@ export default class extends Endpoint<typeof meta, typeof paramDef> { // eslint-
});
// リクエストã•れãŸé€šã‚Šã«ä¸¦ã¹æ›¿ãˆ
+ // 順番ã¯ä¿æŒã•れるã‘ã©æ•°ã¯æ¸›ã£ã¦ã‚‹å¯èƒ½æ€§ãŒã‚ã‚‹
const _users: MiUser[] = [];
for (const id of ps.userIds) {
- _users.push(users.find(x => x.id === id)!);
+ const user = users.find(x => x.id === id);
+ if (user != null) _users.push(user);
}
- return await Promise.all(_users.map(u => this.userEntityService.pack(u, me, {
- schema: 'UserDetailed',
- })));
+ const _userMap = await this.userEntityService.packMany(_users, me, { schema: 'UserDetailed' })
+ .then(users => new Map(users.map(u => [u.id, u])));
+ return _users.map(u => _userMap.get(u.id)!);
} else {
// Lookup user
if (typeof ps.host === 'string' && typeof ps.username === 'string') {
diff --git a/packages/backend/src/server/web/ClientServerService.ts b/packages/backend/src/server/web/ClientServerService.ts
index 84c10370f6..702e306feb 100644
--- a/packages/backend/src/server/web/ClientServerService.ts
+++ b/packages/backend/src/server/web/ClientServerService.ts
@@ -456,7 +456,7 @@ export class ClientServerService {
//#endregion
- const renderBase = async (reply: FastifyReply) => {
+ const renderBase = async (reply: FastifyReply, data: { [key: string]: any } = {}) => {
const meta = await this.metaService.fetch();
reply.header('Cache-Control', 'public, max-age=30');
return await reply.view('base', {
@@ -465,6 +465,7 @@ export class ClientServerService {
title: meta.name ?? 'Misskey',
desc: meta.description,
...await this.generateCommonPugData(meta),
+ ...data,
});
};
@@ -483,7 +484,9 @@ export class ClientServerService {
};
// Atom
- fastify.get<{ Params: { user: string; } }>('/@:user.atom', async (request, reply) => {
+ fastify.get<{ Params: { user?: string; } }>('/@:user.atom', async (request, reply) => {
+ if (request.params.user == null) return await renderBase(reply);
+
const feed = await getFeed(request.params.user);
if (feed) {
@@ -496,7 +499,9 @@ export class ClientServerService {
});
// RSS
- fastify.get<{ Params: { user: string; } }>('/@:user.rss', async (request, reply) => {
+ fastify.get<{ Params: { user?: string; } }>('/@:user.rss', async (request, reply) => {
+ if (request.params.user == null) return await renderBase(reply);
+
const feed = await getFeed(request.params.user);
if (feed) {
@@ -509,7 +514,9 @@ export class ClientServerService {
});
// JSON
- fastify.get<{ Params: { user: string; } }>('/@:user.json', async (request, reply) => {
+ fastify.get<{ Params: { user?: string; } }>('/@:user.json', async (request, reply) => {
+ if (request.params.user == null) return await renderBase(reply);
+
const feed = await getFeed(request.params.user);
if (feed) {
@@ -762,6 +769,18 @@ export class ClientServerService {
});
//#endregion
+ //region noindex pages
+ // Tags
+ fastify.get<{ Params: { clip: string; } }>('/tags/:tag', async (request, reply) => {
+ return await renderBase(reply, { noindex: true });
+ });
+
+ // User with Tags
+ fastify.get<{ Params: { clip: string; } }>('/user-tags/:tag', async (request, reply) => {
+ return await renderBase(reply, { noindex: true });
+ });
+ //endregion
+
fastify.get('/_info_card_', async (request, reply) => {
const meta = await this.metaService.fetch(true);
diff --git a/packages/backend/src/server/web/views/base.pug b/packages/backend/src/server/web/views/base.pug
index 0f555e0637..c1e38717c8 100644
--- a/packages/backend/src/server/web/views/base.pug
+++ b/packages/backend/src/server/web/views/base.pug
@@ -55,6 +55,9 @@ html
block title
= title || 'Sharkey'
+ if noindex
+ meta(name='robots' content='noindex')
+
block desc
meta(name='description' content= desc || '✨🌎✨ A interplanetary communication platform ✨🚀✨')
diff --git a/packages/backend/test/e2e/antennas.ts b/packages/backend/test/e2e/antennas.ts
index cf5c7dd130..101238b601 100644
--- a/packages/backend/test/e2e/antennas.ts
+++ b/packages/backend/test/e2e/antennas.ts
@@ -38,7 +38,6 @@ describe('アンテナ', () => {
excludeKeywords: [['']],
keywords: [['keyword']],
name: 'test',
- notify: false,
src: 'all' as const,
userListId: null,
users: [''],
@@ -151,7 +150,6 @@ describe('アンテナ', () => {
isActive: true,
keywords: [['keyword']],
name: 'test',
- notify: false,
src: 'all',
userListId: null,
users: [''],
@@ -159,6 +157,7 @@ describe('アンテナ', () => {
withReplies: false,
excludeBots: false,
localOnly: false,
+ notify: false,
};
assert.deepStrictEqual(response, expected);
});
@@ -219,8 +218,6 @@ describe('アンテナ', () => {
{ parameters: () => ({ withReplies: true }) },
{ parameters: () => ({ withFile: false }) },
{ parameters: () => ({ withFile: true }) },
- { parameters: () => ({ notify: false }) },
- { parameters: () => ({ notify: true }) },
];
test.each(antennaParamPattern)('を作æˆã§ãã‚‹ã“ã¨($#)', async ({ parameters }) => {
const response = await successfulApiCall({
diff --git a/packages/backend/test/e2e/move.ts b/packages/backend/test/e2e/move.ts
index 4e5306da97..35050130dc 100644
--- a/packages/backend/test/e2e/move.ts
+++ b/packages/backend/test/e2e/move.ts
@@ -191,7 +191,6 @@ describe('Account Move', () => {
localOnly: false,
withReplies: false,
withFile: false,
- notify: false,
}, alice);
antennaId = antenna.body.id;
@@ -435,7 +434,6 @@ describe('Account Move', () => {
localOnly: false,
withReplies: false,
withFile: false,
- notify: false,
}, alice);
assert.strictEqual(res.status, 403);
diff --git a/packages/backend/test/unit/AnnouncementService.ts b/packages/backend/test/unit/AnnouncementService.ts
index aa082ff2f2..81da0fac31 100644
--- a/packages/backend/test/unit/AnnouncementService.ts
+++ b/packages/backend/test/unit/AnnouncementService.ts
@@ -10,6 +10,7 @@ import { ModuleMocker } from 'jest-mock';
import { Test } from '@nestjs/testing';
import { GlobalModule } from '@/GlobalModule.js';
import { AnnouncementService } from '@/core/AnnouncementService.js';
+import { AnnouncementEntityService } from '@/core/entities/AnnouncementEntityService.js';
import type {
AnnouncementReadsRepository,
AnnouncementsRepository,
@@ -67,6 +68,7 @@ describe('AnnouncementService', () => {
],
providers: [
AnnouncementService,
+ AnnouncementEntityService,
CacheService,
IdService,
],
diff --git a/packages/frontend/.storybook/preview-head.html b/packages/frontend/.storybook/preview-head.html
index 4722fe7f5f..ae42fd49bc 100644
--- a/packages/frontend/.storybook/preview-head.html
+++ b/packages/frontend/.storybook/preview-head.html
@@ -5,7 +5,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/about-icon.png?raw=true" as="image" type="image/png" crossorigin="anonymous">
<link rel="preload" href="https://github.com/misskey-dev/misskey/blob/master/packages/frontend/assets/fedi.jpg?raw=true" as="image" type="image/jpeg" crossorigin="anonymous">
-<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@3.3.0/tabler-icons.min.css">
+<link rel="stylesheet" href="https://unpkg.com/@tabler/icons-webfont@3.3.0/dist/tabler-icons.min.css">
<link rel="stylesheet" href="https://unpkg.com/@fontsource/m-plus-rounded-1c/index.css">
<style>
html {
diff --git a/packages/frontend/package.json b/packages/frontend/package.json
index 9ea67b594c..648134b491 100644
--- a/packages/frontend/package.json
+++ b/packages/frontend/package.json
@@ -30,7 +30,7 @@
"@twemoji/parser": "15.1.1",
"@vitejs/plugin-vue": "5.0.4",
"@vue/compiler-sfc": "3.4.26",
- "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.4",
+ "aiscript-vscode": "github:aiscript-dev/aiscript-vscode#v0.1.9",
"astring": "1.8.6",
"broadcast-channel": "7.0.0",
"buraha": "0.0.1",
@@ -120,7 +120,7 @@
"eslint-plugin-import": "2.29.1",
"eslint-plugin-vue": "9.25.0",
"fast-glob": "3.3.2",
- "happy-dom": "14.7.1",
+ "happy-dom": "10.0.3",
"intersection-observer": "0.12.2",
"micromatch": "4.0.5",
"msw": "2.2.14",
diff --git a/packages/frontend/src/cache.ts b/packages/frontend/src/cache.ts
index b286528de6..bfe8fbe0e4 100644
--- a/packages/frontend/src/cache.ts
+++ b/packages/frontend/src/cache.ts
@@ -11,3 +11,4 @@ export const clipsCache = new Cache<Misskey.entities.Clip[]>(1000 * 60 * 30, ()
export const rolesCache = new Cache(1000 * 60 * 30, () => misskeyApi('admin/roles/list'));
export const userListsCache = new Cache<Misskey.entities.UserList[]>(1000 * 60 * 30, () => misskeyApi('users/lists/list'));
export const antennasCache = new Cache<Misskey.entities.Antenna[]>(1000 * 60 * 30, () => misskeyApi('antennas/list'));
+export const favoritedChannelsCache = new Cache<Misskey.entities.Channel[]>(1000 * 60 * 30, () => misskeyApi('channels/my-favorites', { limit: 100 }));
diff --git a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
index 84b5375a41..c7f1288729 100644
--- a/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
+++ b/packages/frontend/src/components/MkCustomEmojiDetailedDialog.vue
@@ -4,77 +4,81 @@ SPDX-License-Identifier: AGPL-3.0-only
-->
<template>
- <MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')">
- <template #header>:{{ emoji.name }}:</template>
- <template #default>
- <MkSpacer>
- <div style="display: flex; flex-direction: column; gap: 1em;">
- <div :class="$style.emojiImgWrapper">
- <MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji>
- </div>
- <MkKeyValue :copy="`:${emoji.name}:`">
- <template #key>{{ i18n.ts.name }}</template>
- <template #value>{{ emoji.name }}</template>
- </MkKeyValue>
- <MkKeyValue>
- <template #key>{{ i18n.ts.tags }}</template>
- <template #value>
- <div v-if="emoji.aliases.length === 0">{{ i18n.ts.none }}</div>
- <div v-else :class="$style.aliases">
- <span v-for="alias in emoji.aliases" :key="alias" :class="$style.alias">
- {{ alias }}
- </span>
- </div>
- </template>
- </MkKeyValue>
- <MkKeyValue>
- <template #key>{{ i18n.ts.category }}</template>
- <template #value>{{ emoji.category ?? i18n.ts.none }}</template>
- </MkKeyValue>
- <MkKeyValue>
- <template #key>{{ i18n.ts.sensitive }}</template>
- <template #value>{{ emoji.isSensitive ? i18n.ts.yes : i18n.ts.no }}</template>
- </MkKeyValue>
- <MkKeyValue>
- <template #key>{{ i18n.ts.localOnly }}</template>
- <template #value>{{ emoji.localOnly ? i18n.ts.yes : i18n.ts.no }}</template>
- </MkKeyValue>
- <MkKeyValue>
- <template #key>{{ i18n.ts.license }}</template>
- <template #value><Mfm :text="emoji.license ?? i18n.ts.none" /></template>
- </MkKeyValue>
- <MkKeyValue :copy="emoji.url">
- <template #key>{{ i18n.ts.emojiUrl }}</template>
- <template #value>
- <MkLink :url="emoji.url" target="_blank">{{ emoji.url }}</MkLink>
- </template>
- </MkKeyValue>
- </div>
- </MkSpacer>
- </template>
- </MkModalWindow>
+<MkModalWindow ref="dialogEl" @close="cancel()" @closed="$emit('closed')">
+ <template #header>:{{ emoji.name }}:</template>
+ <template #default>
+ <MkSpacer>
+ <div style="display: flex; flex-direction: column; gap: 1em;">
+ <div :class="$style.emojiImgWrapper">
+ <MkCustomEmoji :name="emoji.name" :normal="true" :useOriginalSize="true" style="height: 100%;"></MkCustomEmoji>
+ </div>
+ <MkKeyValue :copy="`:${emoji.name}:`">
+ <template #key>{{ i18n.ts.name }}</template>
+ <template #value>{{ emoji.name }}</template>
+ </MkKeyValue>
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.tags }}</template>
+ <template #value>
+ <div v-if="emoji.aliases.length === 0">{{ i18n.ts.none }}</div>
+ <div v-else :class="$style.aliases">
+ <span v-for="alias in emoji.aliases" :key="alias" :class="$style.alias">
+ {{ alias }}
+ </span>
+ </div>
+ </template>
+ </MkKeyValue>
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.category }}</template>
+ <template #value>{{ emoji.category ?? i18n.ts.none }}</template>
+ </MkKeyValue>
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.sensitive }}</template>
+ <template #value>{{ emoji.isSensitive ? i18n.ts.yes : i18n.ts.no }}</template>
+ </MkKeyValue>
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.localOnly }}</template>
+ <template #value>{{ emoji.localOnly ? i18n.ts.yes : i18n.ts.no }}</template>
+ </MkKeyValue>
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.license }}</template>
+ <template #value><Mfm :text="emoji.license ?? i18n.ts.none"/></template>
+ </MkKeyValue>
+ <MkKeyValue :copy="emoji.url">
+ <template #key>{{ i18n.ts.emojiUrl }}</template>
+ <template #value>
+ <MkLink :url="emoji.url" target="_blank">{{ emoji.url }}</MkLink>
+ </template>
+ </MkKeyValue>
+ </div>
+ </MkSpacer>
+ </template>
+</MkModalWindow>
</template>
<script lang="ts" setup>
import * as Misskey from 'misskey-js';
import { defineProps, shallowRef } from 'vue';
+import MkLink from '@/components/MkLink.vue';
import { i18n } from '@/i18n.js';
import MkModalWindow from '@/components/MkModalWindow.vue';
import MkKeyValue from '@/components/MkKeyValue.vue';
-import MkLink from './MkLink.vue';
+
const props = defineProps<{
emoji: Misskey.entities.EmojiDetailed,
}>();
+
const emit = defineEmits<{
(ev: 'ok', cropped: Misskey.entities.DriveFile): void;
(ev: 'cancel'): void;
(ev: 'closed'): void;
}>();
+
const dialogEl = shallowRef<InstanceType<typeof MkModalWindow>>();
-const cancel = () => {
+
+function cancel() {
emit('cancel');
dialogEl.value!.close();
-};
+}
</script>
<style lang="scss" module>
diff --git a/packages/frontend/src/components/MkFormDialog.file.vue b/packages/frontend/src/components/MkFormDialog.file.vue
new file mode 100644
index 0000000000..9360594236
--- /dev/null
+++ b/packages/frontend/src/components/MkFormDialog.file.vue
@@ -0,0 +1,71 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<div>
+ <MkButton inline rounded primary @click="selectButton($event)">{{ i18n.ts.selectFile }}</MkButton>
+ <div :class="['_nowrap', !fileName && $style.fileNotSelected]">{{ friendlyFileName }}</div>
+</div>
+</template>
+
+<script setup lang="ts">
+import * as Misskey from 'misskey-js';
+import { computed, ref } from 'vue';
+import { i18n } from '@/i18n.js';
+import MkButton from '@/components/MkButton.vue';
+import { selectFile } from '@/scripts/select-file.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+
+const props = defineProps<{
+ fileId?: string | null;
+ validate?: (file: Misskey.entities.DriveFile) => Promise<boolean>;
+}>();
+
+const emit = defineEmits<{
+ (ev: 'update', result: Misskey.entities.DriveFile): void;
+}>();
+
+const fileUrl = ref('');
+const fileName = ref<string>('');
+
+const friendlyFileName = computed<string>(() => {
+ if (fileName.value) {
+ return fileName.value;
+ }
+ if (fileUrl.value) {
+ return fileUrl.value;
+ }
+
+ return i18n.ts.fileNotSelected;
+});
+
+if (props.fileId) {
+ misskeyApi('drive/files/show', {
+ fileId: props.fileId,
+ }).then((apiRes) => {
+ fileName.value = apiRes.name;
+ fileUrl.value = apiRes.url;
+ });
+}
+
+function selectButton(ev: MouseEvent) {
+ selectFile(ev.currentTarget ?? ev.target).then(async (file) => {
+ if (!file) return;
+ if (props.validate && !await props.validate(file)) return;
+
+ emit('update', file);
+ fileName.value = file.name;
+ fileUrl.value = file.url;
+ });
+}
+
+</script>
+
+<style module>
+.fileNotSelected {
+ font-weight: 700;
+ color: var(--infoWarnFg);
+}
+</style>
diff --git a/packages/frontend/src/components/MkFormDialog.vue b/packages/frontend/src/components/MkFormDialog.vue
index deedc5badb..124f114111 100644
--- a/packages/frontend/src/components/MkFormDialog.vue
+++ b/packages/frontend/src/components/MkFormDialog.vue
@@ -21,8 +21,9 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSpacer :marginMin="20" :marginMax="32">
<div v-if="Object.keys(form).filter(item => !form[item].hidden).length > 0" class="_gaps_m">
- <template v-for="(v, k) in Object.fromEntries(Object.entries(form).filter(([_, v]) => !('hidden' in v) || 'hidden' in v && !v.hidden))">
- <MkInput v-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1">
+ <template v-for="(v, k) in Object.fromEntries(Object.entries(form))">
+ <template v-if="typeof v.hidden == 'function' ? v.hidden(values) : v.hidden"></template>
+ <MkInput v-else-if="v.type === 'number'" v-model="values[k]" type="number" :step="v.step || 1">
<template #label><span v-text="v.label || k"></span><span v-if="v.required === false"> ({{ i18n.ts.optional }})</span></template>
<template v-if="v.description" #caption>{{ v.description }}</template>
</MkInput>
@@ -53,6 +54,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkButton v-else-if="v.type === 'button'" @click="v.action($event, values)">
<span v-text="v.content || k"></span>
</MkButton>
+ <XFile
+ v-else-if="v.type === 'drive-file'"
+ :fileId="v.defaultFileId"
+ :validate="async f => !v.validate || await v.validate(f)"
+ @update="f => values[k] = f"
+ />
</template>
</div>
<div v-else class="_fullinfo">
@@ -72,6 +79,7 @@ import MkSelect from './MkSelect.vue';
import MkRange from './MkRange.vue';
import MkButton from './MkButton.vue';
import MkRadios from './MkRadios.vue';
+import XFile from './MkFormDialog.file.vue';
import type { Form } from '@/scripts/form.js';
import MkModalWindow from '@/components/MkModalWindow.vue';
import { i18n } from '@/i18n.js';
diff --git a/packages/frontend/src/components/MkModal.vue b/packages/frontend/src/components/MkModal.vue
index eb240da759..9e69ab2207 100644
--- a/packages/frontend/src/components/MkModal.vue
+++ b/packages/frontend/src/components/MkModal.vue
@@ -276,8 +276,11 @@ const align = () => {
const onOpened = () => {
emit('opened');
+ // NOTE: Chromatic テストã®éš›ã« undefined ã«ãªã‚‹å ´åˆãŒã‚ã‚‹
+ if (content.value == null) return;
+
// モーダルコンテンツã«ãƒžã‚¦ã‚¹ãƒœã‚¿ãƒ³ãŒæŠ¼ã•れã€ã‚³ãƒ³ãƒ†ãƒ³ãƒ„外ã§ãƒžã‚¦ã‚¹ãƒœã‚¿ãƒ³ãŒé›¢ã•れãŸã¨ãã«ãƒ¢ãƒ¼ãƒ€ãƒ«ãƒãƒƒã‚¯ã‚°ãƒ©ã‚¦ãƒ³ãƒ‰ã‚¯ãƒªãƒƒã‚¯ã¨åˆ¤å®šã•ã›ãªã„ãŸã‚ã«ãƒžã‚¦ã‚¹ã‚¤ãƒ™ãƒ³ãƒˆã‚’監視ã—フラグ管ç†ã™ã‚‹
- const el = content.value!.children[0];
+ const el = content.value.children[0];
el.addEventListener('mousedown', ev => {
contentClicking = true;
window.addEventListener('mouseup', ev => {
diff --git a/packages/frontend/src/components/MkPostForm.vue b/packages/frontend/src/components/MkPostForm.vue
index caadfa7ca7..c6631fe1f4 100644
--- a/packages/frontend/src/components/MkPostForm.vue
+++ b/packages/frontend/src/components/MkPostForm.vue
@@ -192,7 +192,7 @@ const localOnly = ref(props.initialLocalOnly ?? (defaultStore.state.rememberNote
const visibility = ref(props.initialVisibility ?? (defaultStore.state.rememberNoteVisibility ? defaultStore.state.visibility : defaultStore.state.defaultNoteVisibility));
const visibleUsers = ref<Misskey.entities.UserDetailed[]>([]);
if (props.initialVisibleUsers) {
- props.initialVisibleUsers.forEach(pushVisibleUser);
+ props.initialVisibleUsers.forEach(u => pushVisibleUser(u));
}
const reactionAcceptance = ref(defaultStore.state.reactionAcceptance);
const autocomplete = ref(null);
@@ -338,7 +338,7 @@ if (props.reply && ['home', 'followers', 'specified'].includes(props.reply.visib
misskeyApi('users/show', {
userIds: props.reply.visibleUserIds.filter(uid => uid !== $i.id && uid !== props.reply?.userId),
}).then(users => {
- users.forEach(pushVisibleUser);
+ users.forEach(u => pushVisibleUser(u));
});
}
@@ -618,6 +618,23 @@ async function onPaste(ev: ClipboardEvent) {
quoteId.value = paste.substring(url.length).match(/^\/notes\/(.+?)\/?$/)?.[1] ?? null;
});
}
+
+ if (paste.length > 1000) {
+ ev.preventDefault();
+ os.confirm({
+ type: 'info',
+ text: i18n.ts.attachAsFileQuestion,
+ }).then(({ canceled }) => {
+ if (canceled) {
+ insertTextAtCursor(textareaEl.value, paste);
+ return;
+ }
+
+ const fileName = formatTimeString(new Date(), defaultStore.state.pastedFileName).replace(/{{number}}/g, "0");
+ const file = new File([paste], `${fileName}.txt`, { type: "text/plain" });
+ upload(file, `${fileName}.txt`);
+ });
+ }
}
function onDragover(ev) {
@@ -1004,11 +1021,7 @@ onMounted(() => {
}
if (draft.data.visibleUserIds) {
misskeyApi('users/show', { userIds: draft.data.visibleUserIds }).then(users => {
- for (let i = 0; i < users.length; i++) {
- if (users[i].id === draft.data.visibleUserIds[i]) {
- pushVisibleUser(users[i]);
- }
- }
+ users.forEach(u => pushVisibleUser(u));
});
}
}
diff --git a/packages/frontend/src/components/global/MkAd.stories.impl.ts b/packages/frontend/src/components/global/MkAd.stories.impl.ts
index a1d274382f..aef26ab92d 100644
--- a/packages/frontend/src/components/global/MkAd.stories.impl.ts
+++ b/packages/frontend/src/components/global/MkAd.stories.impl.ts
@@ -11,6 +11,10 @@ import { i18n } from '@/i18n.js';
let lock: Promise<undefined> | undefined;
+function sleep(ms: number) {
+ return new Promise(resolve => setTimeout(resolve, ms));
+}
+
const common = {
render(args) {
return {
@@ -43,6 +47,8 @@ const common = {
lock = new Promise(r => resolve = r);
try {
+ // NOTE: sleep ã—ãªã„ã¨ä½•æ•…ã‹è½ã¡ã‚‹
+ await sleep(100);
const canvas = within(canvasElement);
const a = canvas.getByRole<HTMLAnchorElement>('link');
// await expect(a.href).toMatch(/^https?:\/\/.*#test$/);
@@ -53,7 +59,7 @@ const common = {
const i = buttons[0];
await expect(i).toBeInTheDocument();
await userEvent.click(i);
- // await expect(canvasElement).toHaveTextContent(i18n.ts._ad.back);
+ await expect(canvasElement).toHaveTextContent(i18n.ts._ad.back);
await expect(a).not.toBeInTheDocument();
await expect(i).not.toBeInTheDocument();
buttons = canvas.getAllByRole<HTMLButtonElement>('button');
diff --git a/packages/frontend/src/os.ts b/packages/frontend/src/os.ts
index fc73622d6b..c728b014a2 100644
--- a/packages/frontend/src/os.ts
+++ b/packages/frontend/src/os.ts
@@ -525,7 +525,7 @@ export function waiting(): Promise<void> {
});
}
-export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true } | { result: GetFormResultType<F> }> {
+export function form<F extends Form>(title: string, f: F): Promise<{ canceled: true, result?: undefined } | { canceled?: false, result: GetFormResultType<F> }> {
return new Promise(resolve => {
popup(defineAsyncComponent(() => import('@/components/MkFormDialog.vue')), { title, form: f }, {
done: result => {
diff --git a/packages/frontend/src/pages/admin/federation.vue b/packages/frontend/src/pages/admin/federation.vue
index db9211929f..c1f958cece 100644
--- a/packages/frontend/src/pages/admin/federation.vue
+++ b/packages/frontend/src/pages/admin/federation.vue
@@ -59,6 +59,7 @@ SPDX-License-Identifier: AGPL-3.0-only
</template>
<script lang="ts" setup>
+import * as Misskey from 'misskey-js';
import { computed, ref } from 'vue';
import XHeader from './_header_.vue';
import MkInput from '@/components/MkInput.vue';
@@ -92,8 +93,17 @@ const pagination = {
})),
};
-function getStatus(instance) {
- if (instance.isSuspended) return 'Suspended';
+function getStatus(instance: Misskey.entities.FederationInstance) {
+ switch (instance.suspensionState) {
+ case 'manuallySuspended':
+ return 'Manually Suspended';
+ case 'goneSuspended':
+ return 'Automatically Suspended (Gone)';
+ case 'autoSuspendedForNotResponding':
+ return 'Automatically Suspended (Not Responding)';
+ case 'none':
+ break;
+ }
if (instance.isBlocked) return 'Blocked';
if (instance.isSilenced) return 'Silenced';
if (instance.isNotResponding) return 'Error';
diff --git a/packages/frontend/src/pages/admin/files.vue b/packages/frontend/src/pages/admin/files.vue
index 2a70f1c4ec..29b5bc5f88 100644
--- a/packages/frontend/src/pages/admin/files.vue
+++ b/packages/frontend/src/pages/admin/files.vue
@@ -42,7 +42,7 @@ import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkFileListForAdmin from '@/components/MkFileListForAdmin.vue';
import * as os from '@/os.js';
-import { misskeyApi } from '@/scripts/misskey-api.js';
+import { lookupFile } from '@/scripts/admin-lookup.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
@@ -73,33 +73,10 @@ function clear() {
});
}
-function show(file) {
- os.pageWindow(`/admin/file/${file.id}`);
-}
-
-async function find() {
- const { canceled, result: q } = await os.inputText({
- title: i18n.ts.fileIdOrUrl,
- minLength: 1,
- });
- if (canceled) return;
-
- misskeyApi('admin/drive/show-file', q.startsWith('http://') || q.startsWith('https://') ? { url: q.trim() } : { fileId: q.trim() }).then(file => {
- show(file);
- }).catch(err => {
- if (err.code === 'NO_SUCH_FILE') {
- os.alert({
- type: 'error',
- text: i18n.ts.notFound,
- });
- }
- });
-}
-
const headerActions = computed(() => [{
text: i18n.ts.lookup,
icon: 'ph-magnifying-glass ph-bold ph-lg',
- handler: find,
+ handler: lookupFile,
}, {
text: i18n.ts.clearCachedFiles,
icon: 'ph-trash ph-bold ph-lg',
diff --git a/packages/frontend/src/pages/admin/index.vue b/packages/frontend/src/pages/admin/index.vue
index 0fd073dd0d..f0d56f1a6e 100644
--- a/packages/frontend/src/pages/admin/index.vue
+++ b/packages/frontend/src/pages/admin/index.vue
@@ -34,9 +34,10 @@ import { i18n } from '@/i18n.js';
import MkSuperMenu from '@/components/MkSuperMenu.vue';
import MkInfo from '@/components/MkInfo.vue';
import { instance } from '@/instance.js';
+import { lookup } from '@/scripts/lookup.js';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
-import { lookupUser, lookupUserByEmail } from '@/scripts/lookup-user.js';
+import { lookupUser, lookupUserByEmail, lookupFile } from '@/scripts/admin-lookup.js';
import { PageMetadata, definePageMetadata, provideMetadataReceiver, provideReactiveMetadata } from '@/scripts/page-metadata.js';
import { useRouter } from '@/router/supplier.js';
@@ -92,7 +93,7 @@ const menuDef = computed(() => [{
type: 'button',
icon: 'ph-magnifying-glass ph-bold ph-lg',
text: i18n.ts.lookup,
- action: lookup,
+ action: adminLookup,
}, ...(instance.disableRegistration ? [{
type: 'button',
icon: 'ph-user-plus ph-bold ph-lg',
@@ -297,7 +298,7 @@ function invite() {
});
}
-function lookup(ev: MouseEvent) {
+function adminLookup(ev: MouseEvent) {
os.popupMenu([{
text: i18n.ts.user,
icon: 'ph-user ph-bold ph-lg',
@@ -311,22 +312,16 @@ function lookup(ev: MouseEvent) {
lookupUserByEmail();
},
}, {
- text: i18n.ts.note,
- icon: 'ph-pencil-simple ph-bold ph-lg',
- action: () => {
- alert('TODO');
- },
- }, {
text: i18n.ts.file,
icon: 'ph-cloud ph-bold ph-lg',
action: () => {
- alert('TODO');
+ lookupFile();
},
}, {
- text: i18n.ts.instance,
+ text: i18n.ts.lookup,
icon: 'ph-planet ph-bold ph-lg',
action: () => {
- alert('TODO');
+ lookup();
},
}], ev.currentTarget ?? ev.target);
}
diff --git a/packages/frontend/src/pages/admin/moderation.vue b/packages/frontend/src/pages/admin/moderation.vue
index 13af28b659..27057838a1 100644
--- a/packages/frontend/src/pages/admin/moderation.vue
+++ b/packages/frontend/src/pages/admin/moderation.vue
@@ -39,6 +39,12 @@ SPDX-License-Identifier: AGPL-3.0-only
<template #caption>Choose which instances should be displayed in the bubble.</template>
</MkTextarea>
+ <MkInput v-model="inquiryUrl" type="url">
+ <template #prefix><i class="ti ti-link"></i></template>
+ <template #label>{{ i18n.ts._serverSettings.inquiryUrl }}</template>
+ <template #caption>{{ i18n.ts._serverSettings.inquiryUrlDescription }}</template>
+ </MkInput>
+
<MkTextarea v-model="preservedUsernames">
<template #label>{{ i18n.ts.preservedUsernames }}</template>
<template #caption>{{ i18n.ts.preservedUsernamesDescription }}</template>
@@ -98,6 +104,7 @@ const preservedUsernames = ref<string>('');
const bubbleTimeline = ref<string>('');
const tosUrl = ref<string | null>(null);
const privacyPolicyUrl = ref<string | null>(null);
+const inquiryUrl = ref<string | null>(null);
async function init() {
const meta = await misskeyApi('admin/meta');
@@ -112,6 +119,7 @@ async function init() {
privacyPolicyUrl.value = meta.privacyPolicyUrl;
bubbleTimeline.value = meta.bubbleInstances.join('\n');
bubbleTimelineEnabled.value = meta.policies.btlAvailable;
+ inquiryUrl.value = meta.inquiryUrl;
}
function save() {
@@ -121,6 +129,7 @@ function save() {
approvalRequiredForSignup: approvalRequiredForSignup.value,
tosUrl: tosUrl.value,
privacyPolicyUrl: privacyPolicyUrl.value,
+ inquiryUrl: inquiryUrl.value,
sensitiveWords: sensitiveWords.value.split('\n'),
prohibitedWords: prohibitedWords.value.split('\n'),
hiddenTags: hiddenTags.value.split('\n'),
diff --git a/packages/frontend/src/pages/admin/users.vue b/packages/frontend/src/pages/admin/users.vue
index 001b7dc82d..80696c8cea 100644
--- a/packages/frontend/src/pages/admin/users.vue
+++ b/packages/frontend/src/pages/admin/users.vue
@@ -64,7 +64,7 @@ import MkInput from '@/components/MkInput.vue';
import MkSelect from '@/components/MkSelect.vue';
import MkPagination from '@/components/MkPagination.vue';
import * as os from '@/os.js';
-import { lookupUser } from '@/scripts/lookup-user.js';
+import { lookupUser } from '@/scripts/admin-lookup.js';
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import MkUserCardMini from '@/components/MkUserCardMini.vue';
diff --git a/packages/frontend/src/pages/announcement.vue b/packages/frontend/src/pages/announcement.vue
new file mode 100644
index 0000000000..85ae9062d4
--- /dev/null
+++ b/packages/frontend/src/pages/announcement.vue
@@ -0,0 +1,142 @@
+<!--
+SPDX-FileCopyrightText: syuilo and misskey-project
+SPDX-License-Identifier: AGPL-3.0-only
+-->
+
+<template>
+<MkStickyContainer>
+ <template #header><MkPageHeader :actions="headerActions" :tabs="headerTabs"/></template>
+ <MkSpacer :contentMax="800">
+ <Transition
+ :enterActiveClass="defaultStore.state.animation ? $style.fadeEnterActive : ''"
+ :leaveActiveClass="defaultStore.state.animation ? $style.fadeLeaveActive : ''"
+ :enterFromClass="defaultStore.state.animation ? $style.fadeEnterFrom : ''"
+ :leaveToClass="defaultStore.state.animation ? $style.fadeLeaveTo : ''"
+ mode="out-in"
+ >
+ <div v-if="announcement" :key="announcement.id" class="_panel" :class="$style.announcement">
+ <div v-if="announcement.forYou" :class="$style.forYou"><i class="ti ti-pin"></i> {{ i18n.ts.forYou }}</div>
+ <div :class="$style.header">
+ <span v-if="$i && !announcement.silence && !announcement.isRead" style="margin-right: 0.5em;">🆕</span>
+ <span style="margin-right: 0.5em;">
+ <i v-if="announcement.icon === 'info'" class="ti ti-info-circle"></i>
+ <i v-else-if="announcement.icon === 'warning'" class="ti ti-alert-triangle" style="color: var(--warn);"></i>
+ <i v-else-if="announcement.icon === 'error'" class="ti ti-circle-x" style="color: var(--error);"></i>
+ <i v-else-if="announcement.icon === 'success'" class="ti ti-check" style="color: var(--success);"></i>
+ </span>
+ <Mfm :text="announcement.title"/>
+ </div>
+ <div :class="$style.content">
+ <Mfm :text="announcement.text"/>
+ <img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
+ <div style="margin-top: 8px; opacity: 0.7; font-size: 85%;">
+ {{ i18n.ts.createdAt }}: <MkTime :time="announcement.createdAt" mode="detail"/>
+ </div>
+ <div v-if="announcement.updatedAt" style="opacity: 0.7; font-size: 85%;">
+ {{ i18n.ts.updatedAt }}: <MkTime :time="announcement.updatedAt" mode="detail"/>
+ </div>
+ </div>
+ <div v-if="$i && !announcement.silence && !announcement.isRead" :class="$style.footer">
+ <MkButton primary @click="read(announcement)"><i class="ti ti-check"></i> {{ i18n.ts.gotIt }}</MkButton>
+ </div>
+ </div>
+ <MkError v-else-if="error" @retry="fetch()"/>
+ <MkLoading v-else/>
+ </Transition>
+ </MkSpacer>
+</MkStickyContainer>
+</template>
+
+<script lang="ts" setup>
+import { ref, computed, watch } from 'vue';
+import * as Misskey from 'misskey-js';
+import MkButton from '@/components/MkButton.vue';
+import * as os from '@/os.js';
+import { misskeyApi } from '@/scripts/misskey-api.js';
+import { i18n } from '@/i18n.js';
+import { definePageMetadata } from '@/scripts/page-metadata.js';
+import { $i, updateAccount } from '@/account.js';
+import { defaultStore } from '@/store.js';
+
+const props = defineProps<{
+ announcementId: string;
+}>();
+
+const announcement = ref<Misskey.entities.Announcement | null>(null);
+const error = ref<any>(null);
+const path = computed(() => props.announcementId);
+
+function fetch() {
+ announcement.value = null;
+ misskeyApi('announcements/show', {
+ announcementId: props.announcementId,
+ }).then(async _announcement => {
+ announcement.value = _announcement;
+ }).catch(err => {
+ error.value = err;
+ });
+}
+
+async function read(target: Misskey.entities.Announcement): Promise<void> {
+ if (target.needConfirmationToRead) {
+ const confirm = await os.confirm({
+ type: 'question',
+ title: i18n.ts._announcement.readConfirmTitle,
+ text: i18n.tsx._announcement.readConfirmText({ title: target.title }),
+ });
+ if (confirm.canceled) return;
+ }
+
+ target.isRead = true;
+ await misskeyApi('i/read-announcement', { announcementId: target.id });
+ if ($i) {
+ updateAccount({
+ unreadAnnouncements: $i.unreadAnnouncements.filter((a: { id: string; }) => a.id !== target.id),
+ });
+ }
+}
+
+watch(() => path.value, fetch, { immediate: true });
+
+const headerActions = computed(() => []);
+
+const headerTabs = computed(() => []);
+
+definePageMetadata(() => ({
+ title: announcement.value ? `${i18n.ts.announcements}: ${announcement.value.title}` : i18n.ts.announcements,
+ icon: 'ti ti-speakerphone',
+}));
+</script>
+
+<style lang="scss" module>
+.announcement {
+ padding: 16px;
+}
+
+.forYou {
+ display: flex;
+ align-items: center;
+ line-height: 24px;
+ font-size: 90%;
+ white-space: pre;
+ color: #d28a3f;
+}
+
+.header {
+ margin-bottom: 16px;
+ font-weight: bold;
+ font-size: 120%;
+}
+
+.content {
+ > img {
+ display: block;
+ max-height: 300px;
+ max-width: 100%;
+ }
+}
+
+.footer {
+ margin-top: 16px;
+}
+</style>
diff --git a/packages/frontend/src/pages/announcements.vue b/packages/frontend/src/pages/announcements.vue
index 4f5abdb385..07bbc46ffc 100644
--- a/packages/frontend/src/pages/announcements.vue
+++ b/packages/frontend/src/pages/announcements.vue
@@ -21,14 +21,19 @@ SPDX-License-Identifier: AGPL-3.0-only
<i v-else-if="announcement.icon === 'error'" class="ph-x-circle ph-bold ph-lg" style="color: var(--error);"></i>
<i v-else-if="announcement.icon === 'success'" class="ph-check ph-bold ph-lg" style="color: var(--success);"></i>
</span>
- <span>{{ announcement.title }}</span>
+ <MkA :to="`/announcements/${announcement.id}`"><span>{{ announcement.title }}</span></MkA>
</div>
<div :class="$style.content">
<Mfm :text="announcement.text"/>
<img v-if="announcement.imageUrl" :src="announcement.imageUrl"/>
- <div style="opacity: 0.7; font-size: 85%;">
- <MkTime :time="announcement.updatedAt ?? announcement.createdAt" mode="detail"/>
- </div>
+ <MkA :to="`/announcements/${announcement.id}`">
+ <div style="margin-top: 8px; opacity: 0.7; font-size: 85%;">
+ {{ i18n.ts.createdAt }}: <MkTime :time="announcement.createdAt" mode="detail"/>
+ </div>
+ <div v-if="announcement.updatedAt" style="opacity: 0.7; font-size: 85%;">
+ {{ i18n.ts.updatedAt }}: <MkTime :time="announcement.updatedAt" mode="detail"/>
+ </div>
+ </MkA>
</div>
<div v-if="tab !== 'past' && $i && !announcement.silence && !announcement.isRead" :class="$style.footer">
<MkButton primary @click="read(announcement)"><i class="ph-check ph-bold ph-lg"></i> {{ i18n.ts.gotIt }}</MkButton>
@@ -73,24 +78,24 @@ const paginationEl = ref<InstanceType<typeof MkPagination>>();
const tab = ref('current');
-async function read(announcement) {
- if (announcement.needConfirmationToRead) {
+async function read(target) {
+ if (target.needConfirmationToRead) {
const confirm = await os.confirm({
type: 'question',
title: i18n.ts._announcement.readConfirmTitle,
- text: i18n.tsx._announcement.readConfirmText({ title: announcement.title }),
+ text: i18n.tsx._announcement.readConfirmText({ title: target.title }),
});
if (confirm.canceled) return;
}
if (!paginationEl.value) return;
- paginationEl.value.updateItem(announcement.id, a => {
+ paginationEl.value.updateItem(target.id, a => {
a.isRead = true;
return a;
});
- misskeyApi('i/read-announcement', { announcementId: announcement.id });
+ misskeyApi('i/read-announcement', { announcementId: target.id });
updateAccount({
- unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== announcement.id),
+ unreadAnnouncements: $i!.unreadAnnouncements.filter(a => a.id !== target.id),
});
}
diff --git a/packages/frontend/src/pages/channel.vue b/packages/frontend/src/pages/channel.vue
index ee081d07ee..e49a16fecd 100644
--- a/packages/frontend/src/pages/channel.vue
+++ b/packages/frontend/src/pages/channel.vue
@@ -83,6 +83,7 @@ import { definePageMetadata } from '@/scripts/page-metadata.js';
import { deviceKind } from '@/scripts/device-kind.js';
import MkNotes from '@/components/MkNotes.vue';
import { url } from '@/config.js';
+import { favoritedChannelsCache } from '@/cache.js';
import MkButton from '@/components/MkButton.vue';
import MkInput from '@/components/MkInput.vue';
import { defaultStore } from '@/store.js';
@@ -170,6 +171,7 @@ function favorite() {
channelId: channel.value.id,
}).then(() => {
favorited.value = true;
+ favoritedChannelsCache.delete();
});
}
@@ -185,6 +187,7 @@ async function unfavorite() {
channelId: channel.value.id,
}).then(() => {
favorited.value = false;
+ favoritedChannelsCache.delete();
});
}
diff --git a/packages/frontend/src/pages/contact.vue b/packages/frontend/src/pages/contact.vue
index 33083c0f40..b6cfebf229 100644
--- a/packages/frontend/src/pages/contact.vue
+++ b/packages/frontend/src/pages/contact.vue
@@ -7,7 +7,21 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkStickyContainer>
<template #header><MkPageHeader/></template>
<MkSpacer :contentMax="600" :marginMin="20">
- <div>{{ instance.maintainerEmail }}</div>
+ <div class="_gaps">
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.inquiry }}</template>
+ <template #value>
+ <MkLink :url="instance.inquiryUrl" target="_blank">{{ instance.inquiryUrl }}</MkLink>
+ </template>
+ </MkKeyValue>
+
+ <MkKeyValue>
+ <template #key>{{ i18n.ts.email }}</template>
+ <template #value>
+ <div>{{ instance.maintainerEmail }}</div>
+ </template>
+ </MkKeyValue>
+ </div>
</MkSpacer>
</MkStickyContainer>
</template>
@@ -16,6 +30,8 @@ SPDX-License-Identifier: AGPL-3.0-only
import { i18n } from '@/i18n.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
import { instance } from '@/instance.js';
+import MkKeyValue from '@/components/MkKeyValue.vue';
+import MkLink from '@/components/MkLink.vue';
definePageMetadata(() => ({
title: i18n.ts.inquiry,
diff --git a/packages/frontend/src/pages/explore.featured.vue b/packages/frontend/src/pages/explore.featured.vue
index b5c8e70166..cfdb235d3a 100644
--- a/packages/frontend/src/pages/explore.featured.vue
+++ b/packages/frontend/src/pages/explore.featured.vue
@@ -29,6 +29,9 @@ const paginationForPolls = {
endpoint: 'notes/polls/recommendation' as const,
limit: 10,
offsetMode: true,
+ params: {
+ excludeChannels: true,
+ },
};
const tab = ref('notes');
diff --git a/packages/frontend/src/pages/instance-info.vue b/packages/frontend/src/pages/instance-info.vue
index 4099e2bac9..0cc4c1a48a 100644
--- a/packages/frontend/src/pages/instance-info.vue
+++ b/packages/frontend/src/pages/instance-info.vue
@@ -35,7 +35,16 @@ SPDX-License-Identifier: AGPL-3.0-only
<FormSection v-if="iAmModerator">
<template #label>Moderation</template>
<div class="_gaps_s">
- <MkSwitch v-model="suspended" :disabled="!instance" @update:modelValue="toggleSuspend">{{ i18n.ts.stopActivityDelivery }}</MkSwitch>
+ <MkKeyValue>
+ <template #key>
+ {{ i18n.ts._delivery.status }}
+ </template>
+ <template #value>
+ {{ i18n.ts._delivery._type[suspensionState] }}
+ </template>
+ </MkKeyValue>
+ <MkButton v-if="suspensionState === 'none'" :disabled="!instance" danger @click="stopDelivery">{{ i18n.ts._delivery.stop }}</MkButton>
+ <MkButton v-if="suspensionState !== 'none'" :disabled="!instance" @click="resumeDelivery">{{ i18n.ts._delivery.resume }}</MkButton>
<MkSwitch v-model="isBlocked" :disabled="!meta || !instance" @update:modelValue="toggleBlock">{{ i18n.ts.blockThisInstance }}</MkSwitch>
<MkSwitch v-model="isSilenced" :disabled="!meta || !instance" @update:modelValue="toggleSilenced">{{ i18n.ts.silenceThisInstance }}</MkSwitch>
<MkSwitch v-model="isNSFW" :disabled="!instance" @update:modelValue="toggleNSFW">Mark as NSFW</MkSwitch>
@@ -156,7 +165,7 @@ const tab = ref('overview');
const chartSrc = ref('instance-requests');
const meta = ref<Misskey.entities.AdminMetaResponse | null>(null);
const instance = ref<Misskey.entities.FederationInstance | null>(null);
-const suspended = ref(false);
+const suspensionState = ref<'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding'>('none');
const isBlocked = ref(false);
const isSilenced = ref(false);
const isNSFW = ref(false);
@@ -185,7 +194,7 @@ async function fetch(): Promise<void> {
instance.value = await misskeyApi('federation/show-instance', {
host: props.host,
});
- suspended.value = instance.value?.isSuspended ?? false;
+ suspensionState.value = instance.value?.suspensionState ?? 'none';
isBlocked.value = instance.value?.isBlocked ?? false;
isSilenced.value = instance.value?.isSilenced ?? false;
isNSFW.value = instance.value?.isNSFW ?? false;
@@ -212,11 +221,21 @@ async function toggleSilenced(): Promise<void> {
});
}
-async function toggleSuspend(): Promise<void> {
+async function stopDelivery(): Promise<void> {
if (!instance.value) throw new Error('No instance?');
+ suspensionState.value = 'manuallySuspended';
await misskeyApi('admin/federation/update-instance', {
host: instance.value.host,
- isSuspended: suspended.value,
+ isSuspended: true,
+ });
+}
+
+async function resumeDelivery(): Promise<void> {
+ if (!instance.value) throw new Error('No instance?');
+ suspensionState.value = 'none';
+ await misskeyApi('admin/federation/update-instance', {
+ host: instance.value.host,
+ isSuspended: false,
});
}
diff --git a/packages/frontend/src/pages/my-antennas/editor.vue b/packages/frontend/src/pages/my-antennas/editor.vue
index 07fc459688..51dbb3b08f 100644
--- a/packages/frontend/src/pages/my-antennas/editor.vue
+++ b/packages/frontend/src/pages/my-antennas/editor.vue
@@ -39,7 +39,6 @@ SPDX-License-Identifier: AGPL-3.0-only
<MkSwitch v-model="localOnly">{{ i18n.ts.localOnly }}</MkSwitch>
<MkSwitch v-model="caseSensitive">{{ i18n.ts.caseSensitive }}</MkSwitch>
<MkSwitch v-model="withFile">{{ i18n.ts.withFileAntenna }}</MkSwitch>
- <MkSwitch v-model="notify">{{ i18n.ts.notifyAntenna }}</MkSwitch>
</div>
<div :class="$style.actions">
<MkButton inline primary @click="saveAntenna()"><i class="ph-floppy-disk ph-bold ph-lg"></i> {{ i18n.ts.save }}</MkButton>
@@ -82,7 +81,6 @@ const localOnly = ref<boolean>(props.antenna.localOnly);
const excludeBots = ref<boolean>(props.antenna.excludeBots);
const withReplies = ref<boolean>(props.antenna.withReplies);
const withFile = ref<boolean>(props.antenna.withFile);
-const notify = ref<boolean>(props.antenna.notify);
const userLists = ref<Misskey.entities.UserList[] | null>(null);
watch(() => src.value, async () => {
@@ -99,7 +97,6 @@ async function saveAntenna() {
excludeBots: excludeBots.value,
withReplies: withReplies.value,
withFile: withFile.value,
- notify: notify.value,
caseSensitive: caseSensitive.value,
localOnly: localOnly.value,
users: users.value.trim().split('\n').map(x => x.trim()),
diff --git a/packages/frontend/src/pages/settings/general.vue b/packages/frontend/src/pages/settings/general.vue
index e772b8c167..3b4f000e61 100644
--- a/packages/frontend/src/pages/settings/general.vue
+++ b/packages/frontend/src/pages/settings/general.vue
@@ -50,13 +50,17 @@ SPDX-License-Identifier: AGPL-3.0-only
<div class="_gaps_m">
<div class="_gaps_s">
- <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch>
- <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch>
+ <MkSwitch v-model="collapseRenotes">
+ <template #label>{{ i18n.ts.collapseRenotes }}</template>
+ <template #caption>{{ i18n.ts.collapseRenotesDescription }}</template>
+ </MkSwitch>
<MkSwitch v-model="collapseRenotes">{{ i18n.ts.collapseRenotes }}</MkSwitch>
<MkSwitch v-model="collapseFiles">{{ i18n.ts.collapseFiles }}</MkSwitch>
<MkSwitch v-model="uncollapseCW">Uncollapse CWs on notes</MkSwitch>
- <MkSwitch v-model="autoloadConversation">{{ i18n.ts.autoloadConversation }}</MkSwitch>
<MkSwitch v-model="expandLongNote">Always expand long notes</MkSwitch>
+ <MkSwitch v-model="showNoteActionsOnlyHover">{{ i18n.ts.showNoteActionsOnlyHover }}</MkSwitch>
+ <MkSwitch v-model="showClipButtonInNoteFooter">{{ i18n.ts.showClipButtonInNoteFooter }}</MkSwitch>
+ <MkSwitch v-model="autoloadConversation">{{ i18n.ts.autoloadConversation }}</MkSwitch>
<MkSwitch v-model="advancedMfm">{{ i18n.ts.enableAdvancedMfm }}</MkSwitch>
<MkSwitch v-if="advancedMfm" v-model="animatedMfm">{{ i18n.ts.enableAnimatedMfm }}</MkSwitch>
<MkSwitch v-if="advancedMfm" v-model="enableQuickAddMfmFunction">{{ i18n.ts.enableQuickAddMfmFunction }}</MkSwitch>
diff --git a/packages/frontend/src/pages/share.vue b/packages/frontend/src/pages/share.vue
index 1eeeb587eb..8d974b17fb 100644
--- a/packages/frontend/src/pages/share.vue
+++ b/packages/frontend/src/pages/share.vue
@@ -64,7 +64,34 @@ async function init() {
// Googleニュース対策
if (text?.startsWith(`${title.value}.\n`)) noteText += text.replace(`${title.value}.\n`, '');
else if (text && title.value !== text) noteText += `${text}\n`;
- if (url) noteText += `${url}`;
+ if (url) {
+ try {
+ // Normalize the URL to URL-encoded and puny-coded from with the URL constructor.
+ //
+ // It's common to use unicode characters in the URL for better visibility of URL
+ // like: https://ja.wikipedia.org/wiki/ミスキー
+ // or like: https://è—.moe/
+ // However, in the MFM, the unicode characters must be URL-encoded to be parsed as `url` node
+ // like: https://ja.wikipedia.org/wiki/%E3%83%9F%E3%82%B9%E3%82%AD%E3%83%BC
+ // or like: https://xn--931a.moe/
+ // Therefore, we need to normalize the URL to URL-encoded form.
+ //
+ // The URL constructor will parse the URL and normalize unicode characters
+ // in the host to punycode and in the path component to URL-encoded form.
+ // (see url.spec.whatwg.org)
+ //
+ // In addition, the current MFM renderer decodes the URL-encoded path and / punycode encoded host name so
+ // this normalization doesn't make the visible URL ugly.
+ // (see MkUrl.vue)
+
+ noteText += new URL(url).href;
+ } catch {
+ // fallback to original URL if the URL is invalid.
+ // note that this is extremely rare since the `url` parameter is designed to share a URL and
+ // the URL constructor will throw TypeError only if failure, which means the URL is not valid.
+ noteText += url;
+ }
+ }
initialText.value = noteText.trim();
if (visibility.value === 'specified') {
diff --git a/packages/frontend/src/pages/timeline.vue b/packages/frontend/src/pages/timeline.vue
index a9f7a163f6..1e5a244dd4 100644
--- a/packages/frontend/src/pages/timeline.vue
+++ b/packages/frontend/src/pages/timeline.vue
@@ -49,7 +49,7 @@ import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
import { $i } from '@/account.js';
import { definePageMetadata } from '@/scripts/page-metadata.js';
-import { antennasCache, userListsCache } from '@/cache.js';
+import { antennasCache, userListsCache, favoritedChannelsCache } from '@/cache.js';
import { deviceKind } from '@/scripts/device-kind.js';
import { deepMerge } from '@/scripts/merge.js';
import { MenuItem } from '@/types/menu.js';
@@ -180,9 +180,7 @@ async function chooseAntenna(ev: MouseEvent): Promise<void> {
}
async function chooseChannel(ev: MouseEvent): Promise<void> {
- const channels = await misskeyApi('channels/my-favorites', {
- limit: 100,
- });
+ const channels = await favoritedChannelsCache.fetch();
const items: MenuItem[] = [
...channels.map(channel => {
const lastReadedAt = miLocalStorage.getItemAsJson(`channelLastReadedAt:${channel.id}`) ?? null;
diff --git a/packages/frontend/src/router/definition.ts b/packages/frontend/src/router/definition.ts
index e411a145c1..f18dac2a44 100644
--- a/packages/frontend/src/router/definition.ts
+++ b/packages/frontend/src/router/definition.ts
@@ -194,6 +194,9 @@ const routes: RouteDef[] = [{
path: '/announcements',
component: page(() => import('@/pages/announcements.vue')),
}, {
+ path: '/announcements/:announcementId',
+ component: page(() => import('@/pages/announcement.vue')),
+}, {
path: '/about',
component: page(() => import('@/pages/about.vue')),
hash: 'initialTab',
diff --git a/packages/frontend/src/scripts/lookup-user.ts b/packages/frontend/src/scripts/admin-lookup.ts
index efc9132e75..1b57b853c9 100644
--- a/packages/frontend/src/scripts/lookup-user.ts
+++ b/packages/frontend/src/scripts/admin-lookup.ts
@@ -63,3 +63,26 @@ export async function lookupUserByEmail() {
}
}
}
+
+export async function lookupFile() {
+ const { canceled, result: q } = await os.inputText({
+ title: i18n.ts.fileIdOrUrl,
+ minLength: 1,
+ });
+ if (canceled) return;
+
+ const show = (file) => {
+ os.pageWindow(`/admin/file/${file.id}`);
+ };
+
+ misskeyApi('admin/drive/show-file', q.startsWith('http://') || q.startsWith('https://') ? { url: q.trim() } : { fileId: q.trim() }).then(file => {
+ show(file);
+ }).catch(err => {
+ if (err.code === 'NO_SUCH_FILE') {
+ os.alert({
+ type: 'error',
+ text: i18n.ts.notFound,
+ });
+ }
+ });
+}
diff --git a/packages/frontend/src/scripts/collapsed.ts b/packages/frontend/src/scripts/collapsed.ts
index 237bd37c7a..4ec88a3c65 100644
--- a/packages/frontend/src/scripts/collapsed.ts
+++ b/packages/frontend/src/scripts/collapsed.ts
@@ -6,15 +6,16 @@
import * as Misskey from 'misskey-js';
export function shouldCollapsed(note: Misskey.entities.Note, urls: string[]): boolean {
- const collapsed = note.cw == null && note.text != null && (
- (note.text.includes('$[x2')) ||
- (note.text.includes('$[x3')) ||
- (note.text.includes('$[x4')) ||
- (note.text.includes('$[scale')) ||
- (note.text.split('\n').length > 9) ||
- (note.text.length > 500) ||
- (note.files.length >= 5) ||
- (urls.length >= 4)
+ const collapsed = note.cw == null && (
+ note.text != null && (
+ (note.text.includes('$[x2')) ||
+ (note.text.includes('$[x3')) ||
+ (note.text.includes('$[x4')) ||
+ (note.text.includes('$[scale')) ||
+ (note.text.split('\n').length > 9) ||
+ (note.text.length > 500) ||
+ (urls.length >= 4)
+ ) || note.files.length >= 5
);
return collapsed;
diff --git a/packages/frontend/src/scripts/form.ts b/packages/frontend/src/scripts/form.ts
index b0db404f28..242a504c3b 100644
--- a/packages/frontend/src/scripts/form.ts
+++ b/packages/frontend/src/scripts/form.ts
@@ -3,18 +3,22 @@
* SPDX-License-Identifier: AGPL-3.0-only
*/
+import * as Misskey from 'misskey-js';
+
type EnumItem = string | {
label: string;
value: string;
};
+type Hidden = boolean | ((v: any) => boolean);
+
export type FormItem = {
label?: string;
type: 'string';
default: string | null;
description?: string;
required?: boolean;
- hidden?: boolean;
+ hidden?: Hidden;
multiline?: boolean;
treatAsMfm?: boolean;
} | {
@@ -23,27 +27,27 @@ export type FormItem = {
default: number | null;
description?: string;
required?: boolean;
- hidden?: boolean;
+ hidden?: Hidden;
step?: number;
} | {
label?: string;
type: 'boolean';
default: boolean | null;
description?: string;
- hidden?: boolean;
+ hidden?: Hidden;
} | {
label?: string;
type: 'enum';
default: string | null;
required?: boolean;
- hidden?: boolean;
+ hidden?: Hidden;
enum: EnumItem[];
} | {
label?: string;
type: 'radio';
default: unknown | null;
required?: boolean;
- hidden?: boolean;
+ hidden?: Hidden;
options: {
label: string;
value: unknown;
@@ -58,20 +62,27 @@ export type FormItem = {
min: number;
max: number;
textConverter?: (value: number) => string;
+ hidden?: Hidden;
} | {
label?: string;
type: 'object';
default: Record<string, unknown> | null;
- hidden: boolean;
+ hidden: Hidden;
} | {
label?: string;
type: 'array';
default: unknown[] | null;
- hidden: boolean;
+ hidden: Hidden;
} | {
type: 'button';
content?: string;
+ hidden?: Hidden;
action: (ev: MouseEvent, v: any) => void;
+} | {
+ type: 'drive-file';
+ defaultFileId?: string | null;
+ hidden?: Hidden;
+ validate?: (v: Misskey.entities.DriveFile) => Promise<boolean>;
};
export type Form = Record<string, FormItem>;
@@ -84,8 +95,9 @@ type GetItemType<Item extends FormItem> =
Item['type'] extends 'range' ? number :
Item['type'] extends 'enum' ? string :
Item['type'] extends 'array' ? unknown[] :
- Item['type'] extends 'object' ? Record<string, unknown>
- : never;
+ Item['type'] extends 'object' ? Record<string, unknown> :
+ Item['type'] extends 'drive-file' ? Misskey.entities.DriveFile | undefined :
+ never;
export type GetFormResultType<F extends Form> = {
[P in keyof F]: GetItemType<F[P]>;
diff --git a/packages/frontend/src/scripts/get-note-menu.ts b/packages/frontend/src/scripts/get-note-menu.ts
index c0db701114..3d3653b84f 100644
--- a/packages/frontend/src/scripts/get-note-menu.ts
+++ b/packages/frontend/src/scripts/get-note-menu.ts
@@ -16,7 +16,7 @@ import { url } from '@/config.js';
import { defaultStore, noteActions } from '@/store.js';
import { miLocalStorage } from '@/local-storage.js';
import { getUserMenu } from '@/scripts/get-user-menu.js';
-import { clipsCache } from '@/cache.js';
+import { clipsCache, favoritedChannelsCache } from '@/cache.js';
import { MenuItem } from '@/types/menu.js';
import MkRippleEffect from '@/components/MkRippleEffect.vue';
import { isSupportShare } from '@/scripts/navigator.js';
@@ -552,6 +552,7 @@ export function getRenoteMenu(props: {
const channelRenoteItems: MenuItem[] = [];
const normalRenoteItems: MenuItem[] = [];
+ const normalExternalChannelRenoteItems: MenuItem[] = [];
if (appearNote.channel) {
channelRenoteItems.push(...[{
@@ -630,12 +631,47 @@ export function getRenoteMenu(props: {
});
},
}]);
+
+ normalExternalChannelRenoteItems.push({
+ type: 'parent',
+ icon: 'ti ti-repeat',
+ text: appearNote.channel ? i18n.ts.renoteToOtherChannel : i18n.ts.renoteToChannel,
+ children: async () => {
+ const channels = await favoritedChannelsCache.fetch();
+ return channels.filter((channel) => {
+ if (!appearNote.channelId) return true;
+ return channel.id !== appearNote.channelId;
+ }).map((channel) => ({
+ text: channel.name,
+ action: () => {
+ const el = props.renoteButton.value;
+ if (el) {
+ const rect = el.getBoundingClientRect();
+ const x = rect.left + (el.offsetWidth / 2);
+ const y = rect.top + (el.offsetHeight / 2);
+ os.popup(MkRippleEffect, { x, y }, {}, 'end');
+ }
+
+ if (!props.mock) {
+ misskeyApi('notes/create', {
+ renoteId: appearNote.id,
+ channelId: channel.id,
+ }).then(() => {
+ os.toast(i18n.tsx.renotedToX({ name: channel.name }));
+ });
+ }
+ },
+ }));
+ },
+ });
}
const renoteItems = [
...normalRenoteItems,
...(channelRenoteItems.length > 0 && normalRenoteItems.length > 0) ? [{ type: 'divider' }] as MenuItem[] : [],
...channelRenoteItems,
+ ...(normalExternalChannelRenoteItems.length > 0 && (normalRenoteItems.length > 0 || channelRenoteItems.length > 0)) ? [{ type: 'divider' }] as MenuItem[] : [],
+ ...normalExternalChannelRenoteItems,
];
return {
diff --git a/packages/frontend/src/ui/_common_/announcements.vue b/packages/frontend/src/ui/_common_/announcements.vue
index b49eff9148..37d89f682f 100644
--- a/packages/frontend/src/ui/_common_/announcements.vue
+++ b/packages/frontend/src/ui/_common_/announcements.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
v-for="announcement in $i.unreadAnnouncements.filter(x => x.display === 'banner')"
:key="announcement.id"
:class="$style.item"
- to="/announcements"
+ :to="`/announcements/${announcement.id}`"
>
<span :class="$style.icon">
<i v-if="announcement.icon === 'info'" class="ph-info ph-bold ph-lg"></i>
diff --git a/packages/frontend/src/ui/deck/antenna-column.vue b/packages/frontend/src/ui/deck/antenna-column.vue
index 79c7c48073..2df8b9ead7 100644
--- a/packages/frontend/src/ui/deck/antenna-column.vue
+++ b/packages/frontend/src/ui/deck/antenna-column.vue
@@ -9,18 +9,22 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ph-flying-saucer ph-bold ph-lg"></i><span style="margin-left: 8px;">{{ column.name }}</span>
</template>
- <MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId"/>
+ <MkTimeline v-if="column.antennaId" ref="timeline" src="antenna" :antenna="column.antennaId" @note="onNote"/>
</XColumn>
</template>
<script lang="ts" setup>
-import { onMounted, shallowRef } from 'vue';
+import { onMounted, ref, shallowRef, watch } from 'vue';
import XColumn from './column.vue';
import { updateColumn, Column } from './deck-store.js';
import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
const props = defineProps<{
column: Column;
@@ -28,6 +32,7 @@ const props = defineProps<{
}>();
const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
onMounted(() => {
if (props.column.antennaId == null) {
@@ -35,6 +40,10 @@ onMounted(() => {
}
});
+watch(soundSetting, v => {
+ updateColumn(props.column.id, { soundSetting: v });
+});
+
async function setAntenna() {
const antennas = await misskeyApi('antennas/list');
const { canceled, result: antenna } = await os.select({
@@ -54,7 +63,11 @@ function editAntenna() {
os.pageWindow('my/antennas/' + props.column.antennaId);
}
-const menu = [
+function onNote() {
+ sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [
{
icon: 'ph-pencil-simple ph-bold ph-lg',
text: i18n.ts.selectAntenna,
@@ -65,6 +78,11 @@ const menu = [
text: i18n.ts.editAntenna,
action: editAntenna,
},
+ {
+ icon: 'ti ti-bell',
+ text: i18n.ts._deck.newNoteNotificationSettings,
+ action: () => soundSettingsButton(soundSetting),
+ },
];
/*
diff --git a/packages/frontend/src/ui/deck/channel-column.vue b/packages/frontend/src/ui/deck/channel-column.vue
index 993be46910..8f326c6689 100644
--- a/packages/frontend/src/ui/deck/channel-column.vue
+++ b/packages/frontend/src/ui/deck/channel-column.vue
@@ -13,21 +13,26 @@ SPDX-License-Identifier: AGPL-3.0-only
<div style="padding: 8px; text-align: center;">
<MkButton primary gradate rounded inline small @click="post"><i class="ph-pencil-simple ph-bold ph-lg"></i></MkButton>
</div>
- <MkTimeline ref="timeline" src="channel" :channel="column.channelId" :key="column.channelId + column.withRenotes + column.onlyFiles" :withRenotes="withRenotes" :onlyFiles="onlyFiles"/>
+ <MkTimeline ref="timeline" src="channel" :channel="column.channelId" :key="column.channelId + column.withRenotes + column.onlyFiles" :withRenotes="withRenotes" :onlyFiles="onlyFiles" @note="onNote"/>
</template>
</XColumn>
</template>
<script lang="ts" setup>
-import { watch, ref, shallowRef } from 'vue';
+import { ref, shallowRef, watch } from 'vue';
import * as Misskey from 'misskey-js';
import XColumn from './column.vue';
import { updateColumn, Column } from './deck-store.js';
import MkTimeline from '@/components/MkTimeline.vue';
import MkButton from '@/components/MkButton.vue';
import * as os from '@/os.js';
+import { favoritedChannelsCache } from '@/cache.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
const props = defineProps<{
column: Column;
@@ -51,25 +56,29 @@ watch(onlyFiles, v => {
});
});
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
+
if (props.column.channelId == null) {
setChannel();
}
+watch(soundSetting, v => {
+ updateColumn(props.column.id, { soundSetting: v });
+});
+
async function setChannel() {
- const channels = await misskeyApi('channels/my-favorites', {
- limit: 100,
- });
- const { canceled, result: channel } = await os.select({
+ const channels = await favoritedChannelsCache.fetch();
+ const { canceled, result: chosenChannel } = await os.select({
title: i18n.ts.selectChannel,
items: channels.map(x => ({
value: x, text: x.name,
})),
default: props.column.channelId,
});
- if (canceled) return;
+ if (canceled || chosenChannel == null) return;
updateColumn(props.column.id, {
- channelId: channel.id,
- name: channel.name,
+ channelId: chosenChannel.id,
+ name: chosenChannel.name,
});
}
@@ -85,7 +94,11 @@ async function post() {
});
}
-const menu = [{
+function onNote() {
+ sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [{
icon: 'ph-pencil-simple ph-bold ph-lg',
text: i18n.ts.selectChannel,
action: setChannel,
@@ -97,5 +110,9 @@ const menu = [{
type: 'switch',
text: i18n.ts.fileAttachedOnly,
ref: onlyFiles,
+}, {
+ icon: 'ti ti-bell',
+ text: i18n.ts._deck.newNoteNotificationSettings,
+ action: () => soundSettingsButton(soundSetting),
}];
</script>
diff --git a/packages/frontend/src/ui/deck/deck-store.ts b/packages/frontend/src/ui/deck/deck-store.ts
index 6c4e2fd52b..1a4f7c5e17 100644
--- a/packages/frontend/src/ui/deck/deck-store.ts
+++ b/packages/frontend/src/ui/deck/deck-store.ts
@@ -9,6 +9,7 @@ import { notificationTypes } from 'misskey-js';
import { Storage } from '@/pizzax.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { deepClone } from '@/scripts/clone.js';
+import { SoundStore } from '@/store.js';
type ColumnWidget = {
name: string;
@@ -33,6 +34,7 @@ export type Column = {
withRenotes?: boolean;
withReplies?: boolean;
onlyFiles?: boolean;
+ soundSetting: SoundStore;
};
export const deckStore = markRaw(new Storage('deck', {
diff --git a/packages/frontend/src/ui/deck/list-column.vue b/packages/frontend/src/ui/deck/list-column.vue
index f7988ed1b7..14f3e5fcd9 100644
--- a/packages/frontend/src/ui/deck/list-column.vue
+++ b/packages/frontend/src/ui/deck/list-column.vue
@@ -9,7 +9,7 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ph-list ph-bold ph-lg"></i><span style="margin-left: 8px;">{{ column.name }}</span>
</template>
- <MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" :key="column.listId + column.withRenotes + column.onlyFiles" :withRenotes="withRenotes" :onlyFiles="onlyFiles"/>
+ <MkTimeline v-if="column.listId" ref="timeline" src="list" :list="column.listId" :key="column.listId + column.withRenotes + column.onlyFiles" :withRenotes="withRenotes" :onlyFiles="onlyFiles" @note="onNote"/>
</XColumn>
</template>
@@ -21,6 +21,10 @@ import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
const props = defineProps<{
column: Column;
@@ -30,6 +34,7 @@ const props = defineProps<{
const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
const withRenotes = ref(props.column.withRenotes ?? true);
const onlyFiles = ref(props.column.onlyFiles ?? false);
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
if (props.column.listId == null) {
setList();
@@ -47,6 +52,10 @@ watch(onlyFiles, v => {
});
});
+watch(soundSetting, v => {
+ updateColumn(props.column.id, { soundSetting: v });
+});
+
async function setList() {
const lists = await misskeyApi('users/lists/list');
const { canceled, result: list } = await os.select({
@@ -66,7 +75,11 @@ function editList() {
os.pageWindow('my/lists/' + props.column.listId);
}
-const menu = [
+function onNote() {
+ sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [
{
icon: 'ph-pencil-simple ph-bold ph-lg',
text: i18n.ts.selectList,
@@ -87,5 +100,10 @@ const menu = [
text: i18n.ts.fileAttachedOnly,
ref: onlyFiles,
},
+ {
+ icon: 'ti ti-bell',
+ text: i18n.ts._deck.newNoteNotificationSettings,
+ action: () => soundSettingsButton(soundSetting),
+ },
];
</script>
diff --git a/packages/frontend/src/ui/deck/role-timeline-column.vue b/packages/frontend/src/ui/deck/role-timeline-column.vue
index 1a673a1753..2fe53918ff 100644
--- a/packages/frontend/src/ui/deck/role-timeline-column.vue
+++ b/packages/frontend/src/ui/deck/role-timeline-column.vue
@@ -9,18 +9,22 @@ SPDX-License-Identifier: AGPL-3.0-only
<i class="ph-seal-check ph-bold ph-lg"></i><span style="margin-left: 8px;">{{ column.name }}</span>
</template>
- <MkTimeline v-if="column.roleId" ref="timeline" src="role" :role="column.roleId"/>
+ <MkTimeline v-if="column.roleId" ref="timeline" src="role" :role="column.roleId" @note="onNote"/>
</XColumn>
</template>
<script lang="ts" setup>
-import { onMounted, shallowRef } from 'vue';
+import { onMounted, ref, shallowRef, watch } from 'vue';
import XColumn from './column.vue';
import { updateColumn, Column } from './deck-store.js';
import MkTimeline from '@/components/MkTimeline.vue';
import * as os from '@/os.js';
import { misskeyApi } from '@/scripts/misskey-api.js';
import { i18n } from '@/i18n.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
const props = defineProps<{
column: Column;
@@ -28,6 +32,7 @@ const props = defineProps<{
}>();
const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
onMounted(() => {
if (props.column.roleId == null) {
@@ -35,6 +40,10 @@ onMounted(() => {
}
});
+watch(soundSetting, v => {
+ updateColumn(props.column.id, { soundSetting: v });
+});
+
async function setRole() {
const roles = (await misskeyApi('roles/list')).filter(x => x.isExplorable);
const { canceled, result: role } = await os.select({
@@ -50,10 +59,18 @@ async function setRole() {
});
}
-const menu = [{
+function onNote() {
+ sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [{
icon: 'ph-pencil-simple ph-bold ph-lg',
text: i18n.ts.role,
action: setRole,
+}, {
+ icon: 'ti ti-bell',
+ text: i18n.ts._deck.newNoteNotificationSettings,
+ action: () => soundSettingsButton(soundSetting),
}];
/*
diff --git a/packages/frontend/src/ui/deck/tl-column.vue b/packages/frontend/src/ui/deck/tl-column.vue
index 3745d026e8..d9474bad8c 100644
--- a/packages/frontend/src/ui/deck/tl-column.vue
+++ b/packages/frontend/src/ui/deck/tl-column.vue
@@ -29,6 +29,7 @@ SPDX-License-Identifier: AGPL-3.0-only
:withRenotes="withRenotes"
:withReplies="withReplies"
:onlyFiles="onlyFiles"
+ @note="onNote"
/>
</XColumn>
</template>
@@ -42,6 +43,10 @@ import * as os from '@/os.js';
import { $i } from '@/account.js';
import { i18n } from '@/i18n.js';
import { instance } from '@/instance.js';
+import { MenuItem } from '@/types/menu.js';
+import { SoundStore } from '@/store.js';
+import { soundSettingsButton } from '@/ui/deck/tl-note-notification.js';
+import * as sound from '@/scripts/sound.js';
const props = defineProps<{
column: Column;
@@ -54,6 +59,7 @@ const timeline = shallowRef<InstanceType<typeof MkTimeline>>();
const isLocalTimelineAvailable = (($i == null && instance.policies.ltlAvailable) || ($i != null && $i.policies.ltlAvailable));
const isGlobalTimelineAvailable = (($i == null && instance.policies.gtlAvailable) || ($i != null && $i.policies.gtlAvailable));
const isBubbleTimelineAvailable = ($i == null && instance.policies.btlAvailable) || ($i != null && $i.policies.btlAvailable);
+const soundSetting = ref<SoundStore>(props.column.soundSetting ?? { type: null, volume: 1 });
const withRenotes = ref(props.column.withRenotes ?? true);
const withReplies = ref(props.column.withReplies ?? false);
const onlyFiles = ref(props.column.onlyFiles ?? false);
@@ -76,6 +82,10 @@ watch(onlyFiles, v => {
});
});
+watch(soundSetting, v => {
+ updateColumn(props.column.id, { soundSetting: v });
+});
+
onMounted(() => {
if (props.column.tl == null) {
setType();
@@ -113,11 +123,19 @@ async function setType() {
});
}
-const menu = [{
+function onNote() {
+ sound.playMisskeySfxFile(soundSetting.value);
+}
+
+const menu: MenuItem[] = [{
icon: 'ph-pencil-simple ph-bold ph-lg',
text: i18n.ts.timeline,
action: setType,
}, {
+ icon: 'ti ti-bell',
+ text: i18n.ts._deck.newNoteNotificationSettings,
+ action: () => soundSettingsButton(soundSetting),
+}, {
type: 'switch',
text: i18n.ts.showRenotes,
ref: withRenotes,
diff --git a/packages/frontend/src/ui/deck/tl-note-notification.ts b/packages/frontend/src/ui/deck/tl-note-notification.ts
new file mode 100644
index 0000000000..275ea56ba0
--- /dev/null
+++ b/packages/frontend/src/ui/deck/tl-note-notification.ts
@@ -0,0 +1,107 @@
+/*
+ * SPDX-FileCopyrightText: syuilo and misskey-project
+ * SPDX-License-Identifier: AGPL-3.0-only
+ */
+
+import * as Misskey from 'misskey-js';
+import { Ref } from 'vue';
+import { SoundStore } from '@/store.js';
+import { getSoundDuration, playMisskeySfxFile, soundsTypes, SoundType } from '@/scripts/sound.js';
+import { i18n } from '@/i18n.js';
+import * as os from '@/os.js';
+
+export async function soundSettingsButton(soundSetting: Ref<SoundStore>): Promise<void> {
+ function getSoundTypeName(f: SoundType): string {
+ switch (f) {
+ case null:
+ return i18n.ts.none;
+ case '_driveFile_':
+ return i18n.ts._soundSettings.driveFile;
+ default:
+ return f;
+ }
+ }
+
+ const { canceled, result } = await os.form(i18n.ts.sound, {
+ type: {
+ type: 'enum',
+ label: i18n.ts.sound,
+ default: soundSetting.value.type ?? 'none',
+ enum: soundsTypes.map(f => ({
+ value: f ?? 'none', label: getSoundTypeName(f),
+ })),
+ },
+ soundFile: {
+ type: 'drive-file',
+ label: i18n.ts.file,
+ defaultFileId: soundSetting.value.type === '_driveFile_' ? soundSetting.value.fileId : null,
+ hidden: v => v.type !== '_driveFile_',
+ validate: async (file: Misskey.entities.DriveFile) => {
+ if (!file.type.startsWith('audio')) {
+ os.alert({
+ type: 'warning',
+ title: i18n.ts._soundSettings.driveFileTypeWarn,
+ text: i18n.ts._soundSettings.driveFileTypeWarnDescription,
+ });
+ return false;
+ }
+
+ const duration = await getSoundDuration(file.url);
+ if (duration >= 2000) {
+ const { canceled } = await os.confirm({
+ type: 'warning',
+ title: i18n.ts._soundSettings.driveFileDurationWarn,
+ text: i18n.ts._soundSettings.driveFileDurationWarnDescription,
+ okText: i18n.ts.continue,
+ cancelText: i18n.ts.cancel,
+ });
+ if (canceled) return false;
+ }
+
+ return true;
+ },
+ },
+ volume: {
+ type: 'range',
+ label: i18n.ts.volume,
+ default: soundSetting.value.volume ?? 1,
+ textConverter: (v) => `${Math.floor(v * 100)}%`,
+ min: 0,
+ max: 1,
+ step: 0.05,
+ },
+ listen: {
+ type: 'button',
+ content: i18n.ts.listen,
+ action: (_, v) => {
+ const sound = buildSoundStore(v);
+ if (!sound) return;
+ playMisskeySfxFile(sound);
+ },
+ },
+ });
+ if (canceled) return;
+
+ const res = buildSoundStore(result);
+ if (res) soundSetting.value = res;
+
+ function buildSoundStore(result: any): SoundStore | null {
+ const type = (result.type === 'none' ? null : result.type) as SoundType;
+ const volume = result.volume as number;
+ const fileId = result.soundFile?.id ?? (soundSetting.value.type === '_driveFile_' ? soundSetting.value.fileId : undefined);
+ const fileUrl = result.soundFile?.url ?? (soundSetting.value.type === '_driveFile_' ? soundSetting.value.fileUrl : undefined);
+
+ if (type === '_driveFile_') {
+ if (!fileUrl || !fileId) {
+ os.alert({
+ type: 'warning',
+ text: i18n.ts._soundSettings.driveFileWarn,
+ });
+ return null;
+ }
+ return { type, volume, fileId, fileUrl };
+ } else {
+ return { type, volume };
+ }
+ }
+}
diff --git a/packages/misskey-js/etc/misskey-js.api.md b/packages/misskey-js/etc/misskey-js.api.md
index 84d7f57dfe..022549af71 100644
--- a/packages/misskey-js/etc/misskey-js.api.md
+++ b/packages/misskey-js/etc/misskey-js.api.md
@@ -352,6 +352,12 @@ type AnnouncementsRequest = operations['announcements']['requestBody']['content'
type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
// @public (undocumented)
+type AnnouncementsShowRequest = operations['announcements___show']['requestBody']['content']['application/json'];
+
+// @public (undocumented)
+type AnnouncementsShowResponse = operations['announcements___show']['responses']['200']['content']['application/json'];
+
+// @public (undocumented)
type Antenna = components['schemas']['Antenna'];
// @public (undocumented)
@@ -1260,6 +1266,8 @@ declare namespace entities {
AdminRolesUsersResponse,
AnnouncementsRequest,
AnnouncementsResponse,
+ AnnouncementsShowRequest,
+ AnnouncementsShowResponse,
AntennasCreateRequest,
AntennasCreateResponse,
AntennasDeleteRequest,
diff --git a/packages/misskey-js/package.json b/packages/misskey-js/package.json
index cc5d5bdbf4..6ec4674c08 100644
--- a/packages/misskey-js/package.json
+++ b/packages/misskey-js/package.json
@@ -1,8 +1,9 @@
{
"type": "module",
"name": "misskey-js",
- "version": "2024.5.0-beta.1",
+ "version": "2024.5.0-rc.9",
"description": "Misskey SDK for JavaScript",
+ "license": "MIT",
"main": "./built/index.js",
"types": "./built/index.d.ts",
"exports": {
@@ -30,7 +31,8 @@
},
"repository": {
"type": "git",
- "url": "git+https://github.com/misskey-dev/misskey.js.git"
+ "url": "https://github.com/misskey-dev/misskey.git",
+ "directory": "packages/misskey-js"
},
"devDependencies": {
"@microsoft/api-extractor": "7.43.1",
diff --git a/packages/misskey-js/src/autogen/apiClientJSDoc.ts b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
index 1796148530..68137b103e 100644
--- a/packages/misskey-js/src/autogen/apiClientJSDoc.ts
+++ b/packages/misskey-js/src/autogen/apiClientJSDoc.ts
@@ -678,7 +678,7 @@ declare module '../api.js' {
/**
* No description provided.
*
- * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
+ * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
*/
request<E extends 'admin/show-users', P extends Endpoints[E]['req']>(
endpoint: E,
@@ -909,6 +909,17 @@ declare module '../api.js' {
/**
* No description provided.
*
+ * **Credential required**: *No*
+ */
+ request<E extends 'announcements/show', P extends Endpoints[E]['req']>(
+ endpoint: E,
+ params: P,
+ credential?: string | null,
+ ): Promise<SwitchCaseResponseType<E, P>>;
+
+ /**
+ * No description provided.
+ *
* **Credential required**: *Yes* / **Permission**: *write:account*
*/
request<E extends 'antennas/create', P extends Endpoints[E]['req']>(
diff --git a/packages/misskey-js/src/autogen/endpoint.ts b/packages/misskey-js/src/autogen/endpoint.ts
index e223f5faf7..9f0ff8364c 100644
--- a/packages/misskey-js/src/autogen/endpoint.ts
+++ b/packages/misskey-js/src/autogen/endpoint.ts
@@ -106,6 +106,8 @@ import type {
AdminRolesUsersResponse,
AnnouncementsRequest,
AnnouncementsResponse,
+ AnnouncementsShowRequest,
+ AnnouncementsShowResponse,
AntennasCreateRequest,
AntennasCreateResponse,
AntennasDeleteRequest,
@@ -651,6 +653,7 @@ export type Endpoints = {
'admin/roles/update-default-policies': { req: AdminRolesUpdateDefaultPoliciesRequest; res: EmptyResponse };
'admin/roles/users': { req: AdminRolesUsersRequest; res: AdminRolesUsersResponse };
'announcements': { req: AnnouncementsRequest; res: AnnouncementsResponse };
+ 'announcements/show': { req: AnnouncementsShowRequest; res: AnnouncementsShowResponse };
'antennas/create': { req: AntennasCreateRequest; res: AntennasCreateResponse };
'antennas/delete': { req: AntennasDeleteRequest; res: EmptyResponse };
'antennas/list': { req: EmptyRequest; res: AntennasListResponse };
diff --git a/packages/misskey-js/src/autogen/entities.ts b/packages/misskey-js/src/autogen/entities.ts
index ab78dd1666..356cafae34 100644
--- a/packages/misskey-js/src/autogen/entities.ts
+++ b/packages/misskey-js/src/autogen/entities.ts
@@ -109,6 +109,8 @@ export type AdminRolesUsersRequest = operations['admin___roles___users']['reques
export type AdminRolesUsersResponse = operations['admin___roles___users']['responses']['200']['content']['application/json'];
export type AnnouncementsRequest = operations['announcements']['requestBody']['content']['application/json'];
export type AnnouncementsResponse = operations['announcements']['responses']['200']['content']['application/json'];
+export type AnnouncementsShowRequest = operations['announcements___show']['requestBody']['content']['application/json'];
+export type AnnouncementsShowResponse = operations['announcements___show']['responses']['200']['content']['application/json'];
export type AntennasCreateRequest = operations['antennas___create']['requestBody']['content']['application/json'];
export type AntennasCreateResponse = operations['antennas___create']['responses']['200']['content']['application/json'];
export type AntennasDeleteRequest = operations['antennas___delete']['requestBody']['content']['application/json'];
diff --git a/packages/misskey-js/src/autogen/types.ts b/packages/misskey-js/src/autogen/types.ts
index 32a66f4399..715278cebd 100644
--- a/packages/misskey-js/src/autogen/types.ts
+++ b/packages/misskey-js/src/autogen/types.ts
@@ -567,7 +567,7 @@ export type paths = {
* admin/show-users
* @description No description provided.
*
- * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
+ * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
*/
post: operations['admin___show-users'];
};
@@ -751,6 +751,15 @@ export type paths = {
*/
post: operations['announcements'];
};
+ '/announcements/show': {
+ /**
+ * announcements/show
+ * @description No description provided.
+ *
+ * **Credential required**: *No*
+ */
+ post: operations['announcements___show'];
+ };
'/antennas/create': {
/**
* antennas/create
@@ -4575,7 +4584,6 @@ export type components = {
caseSensitive: boolean;
/** @default false */
localOnly: boolean;
- notify: boolean;
/** @default false */
excludeBots: boolean;
/** @default false */
@@ -4584,6 +4592,8 @@ export type components = {
isActive: boolean;
/** @default false */
hasUnreadNote: boolean;
+ /** @default false */
+ notify: boolean;
};
Clip: {
/**
@@ -4618,6 +4628,8 @@ export type components = {
followersCount: number;
isNotResponding: boolean;
isSuspended: boolean;
+ /** @enum {string} */
+ suspensionState: 'none' | 'manuallySuspended' | 'goneSuspended' | 'autoSuspendedForNotResponding';
isBlocked: boolean;
/** @example misskey */
softwareName: string | null;
@@ -4970,6 +4982,7 @@ export type components = {
impressumUrl: string | null;
logoImageUrl: string | null;
privacyPolicyUrl: string | null;
+ inquiryUrl: string | null;
serverRules: string[];
themeColor: string | null;
policies: components['schemas']['RolePolicies'];
@@ -5124,6 +5137,7 @@ export type operations = {
shortName: string | null;
objectStorageS3ForcePathStyle: boolean;
privacyPolicyUrl: string | null;
+ inquiryUrl: string | null;
repositoryUrl: string | null;
/**
* @deprecated
@@ -8802,7 +8816,7 @@ export type operations = {
* admin/show-users
* @description No description provided.
*
- * **Credential required**: *Yes* / **Permission**: *read:admin:show-users*
+ * **Credential required**: *Yes* / **Permission**: *read:admin:show-user*
*/
'admin___show-users': {
requestBody: {
@@ -9319,6 +9333,7 @@ export type operations = {
impressumUrl?: string | null;
donationUrl?: string | null;
privacyPolicyUrl?: string | null;
+ inquiryUrl?: string | null;
useObjectStorage?: boolean;
objectStorageBaseUrl?: string | null;
objectStorageBucket?: string | null;
@@ -10086,6 +10101,60 @@ export type operations = {
};
};
/**
+ * announcements/show
+ * @description No description provided.
+ *
+ * **Credential required**: *No*
+ */
+ announcements___show: {
+ requestBody: {
+ content: {
+ 'application/json': {
+ /** Format: misskey:id */
+ announcementId: string;
+ };
+ };
+ };
+ responses: {
+ /** @description OK (with results) */
+ 200: {
+ content: {
+ 'application/json': components['schemas']['Announcement'];
+ };
+ };
+ /** @description Client error */
+ 400: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Authentication error */
+ 401: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Forbidden error */
+ 403: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description I'm Ai */
+ 418: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ /** @description Internal server error */
+ 500: {
+ content: {
+ 'application/json': components['schemas']['Error'];
+ };
+ };
+ };
+ };
+ /**
* antennas/create
* @description No description provided.
*
@@ -10108,7 +10177,6 @@ export type operations = {
excludeBots?: boolean;
withReplies: boolean;
withFile: boolean;
- notify: boolean;
};
};
};
@@ -10390,7 +10458,6 @@ export type operations = {
excludeBots?: boolean;
withReplies?: boolean;
withFile?: boolean;
- notify?: boolean;
};
};
};
@@ -21696,6 +21763,8 @@ export type operations = {
limit?: number;
/** @default 0 */
offset?: number;
+ /** @default false */
+ excludeChannels?: boolean;
};
};
};
diff --git a/packages/misskey-js/src/consts.ts b/packages/misskey-js/src/consts.ts
index 4de567e6d4..518fc75ec6 100644
--- a/packages/misskey-js/src/consts.ts
+++ b/packages/misskey-js/src/consts.ts
@@ -58,7 +58,6 @@ export const permissions = [
'read:admin:server-info',
'read:admin:show-moderation-log',
'read:admin:show-user',
- 'read:admin:show-users',
'write:admin:suspend-user',
'write:admin:approve-user',
'write:admin:nsfw-user',
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index d33ecf8bf8..a5ef08947c 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -136,6 +136,12 @@ importers:
'@peertube/http-signature':
specifier: 1.7.0
version: 1.7.0
+ '@sentry/node':
+ specifier: ^8.5.0
+ version: 8.5.0
+ '@sentry/profiling-node':
+ specifier: ^8.5.0
+ version: 8.5.0
'@simplewebauthn/server':
specifier: 10.0.0
version: 10.0.0(encoding@0.1.13)
@@ -242,8 +248,8 @@ importers:
specifier: 14.2.1
version: 14.2.1
happy-dom:
- specifier: 14.7.1
- version: 14.7.1
+ specifier: 10.0.3
+ version: 10.0.3
hpagent:
specifier: 1.2.0
version: 1.2.0
@@ -730,8 +736,8 @@ importers:
specifier: 3.4.26
version: 3.4.26
aiscript-vscode:
- specifier: github:aiscript-dev/aiscript-vscode#v0.1.4
- version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424
+ specifier: github:aiscript-dev/aiscript-vscode#v0.1.9
+ version: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02
astring:
specifier: 1.8.6
version: 1.8.6
@@ -879,7 +885,7 @@ importers:
version: 8.0.9(@types/react@18.0.28)(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
'@storybook/addon-interactions':
specifier: 8.0.9
- version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+ version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
'@storybook/addon-links':
specifier: 8.0.9
version: 8.0.9(react@18.3.1)
@@ -912,7 +918,7 @@ importers:
version: 8.0.9(encoding@0.1.13)(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(rollup@4.17.2)(typescript@5.4.5)(vite@5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3))
'@storybook/test':
specifier: 8.0.9
- version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+ version: 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
'@storybook/theming':
specifier: 8.0.9
version: 8.0.9(react-dom@18.3.1(react@18.3.1))(react@18.3.1)
@@ -969,7 +975,7 @@ importers:
version: 7.7.1(eslint@8.57.0)(typescript@5.4.5)
'@vitest/coverage-v8':
specifier: 0.34.6
- version: 0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+ version: 0.34.6(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
'@vue/runtime-core':
specifier: 3.4.26
version: 3.4.26
@@ -995,8 +1001,8 @@ importers:
specifier: 3.3.2
version: 3.3.2
happy-dom:
- specifier: 14.7.1
- version: 14.7.1
+ specifier: 10.0.3
+ version: 10.0.3
intersection-observer:
specifier: 0.12.2
version: 0.12.2
@@ -1035,10 +1041,10 @@ importers:
version: 1.0.3
vitest:
specifier: 0.34.6
- version: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+ version: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
vitest-fetch-mock:
specifier: 0.2.2
- version: 0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+ version: 0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
vue-component-type-helpers:
specifier: 2.0.16
version: 2.0.16
@@ -1135,7 +1141,7 @@ importers:
version: 3.2.5
ts-jest:
specifier: ^29.1.1
- version: 29.1.2(@babel/core@7.24.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.0))(esbuild@0.20.2)(jest@29.7.0(@types/node@20.12.7))(typescript@5.1.6)
+ version: 29.1.2(@babel/core@7.23.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.5))(esbuild@0.20.2)(jest@29.7.0(@types/node@20.12.7))(typescript@5.1.6)
typedoc:
specifier: ^0.25.3
version: 0.25.13(typescript@5.1.6)
@@ -3353,6 +3359,154 @@ packages:
'@open-draft/until@2.1.0':
resolution: {integrity: sha512-U69T3ItWHvLwGg5eJ0n3I62nWuE6ilHlmz7zM0npLBRvPRd7e6NYmg54vvRtP5mZG7kZqZCFVdsTWo7BPtBujg==}
+ '@opentelemetry/api-logs@0.51.1':
+ resolution: {integrity: sha512-E3skn949Pk1z2XtXu/lxf6QAZpawuTM/IUEXcAzpiUkTd73Hmvw26FiN3cJuTmkpM5hZzHwkomVdtrh/n/zzwA==}
+ engines: {node: '>=14'}
+
+ '@opentelemetry/api@1.8.0':
+ resolution: {integrity: sha512-I/s6F7yKUDdtMsoBWXJe8Qz40Tui5vsuKCWJEWVL+5q9sSWRzzx6v2KeNsOBEwd94j0eWkpWCH4yB6rZg9Mf0w==}
+ engines: {node: '>=8.0.0'}
+
+ '@opentelemetry/context-async-hooks@1.24.1':
+ resolution: {integrity: sha512-R5r6DO4kgEOVBxFXhXjwospLQkv+sYxwCfjvoZBe7Zm6KKXAV9kDSJhi/D1BweowdZmO+sdbENLs374gER8hpQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.9.0'
+
+ '@opentelemetry/core@1.24.1':
+ resolution: {integrity: sha512-wMSGfsdmibI88K9wB498zXY04yThPexo8jvwNNlm542HZB7XrrMRBbAyKJqG8qDRJwIBdBrPMi4V9ZPW/sqrcg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.9.0'
+
+ '@opentelemetry/instrumentation-connect@0.36.0':
+ resolution: {integrity: sha512-k9++bmJZ9zDEs3u3DnKTn2l7QTiNFg3gPx7G9rW0TPnP+xZoBSBTrEcGYBaqflQlrFG23Q58+X1sM2ayWPv5Fg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-express@0.39.0':
+ resolution: {integrity: sha512-AG8U7z7D0JcBu/7dDcwb47UMEzj9/FMiJV2iQZqrsZnxR3FjB9J9oIH2iszJYci2eUdp2WbdvtpD9RV/zmME5A==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-fastify@0.36.1':
+ resolution: {integrity: sha512-3Nfm43PI0I+3EX+1YbSy6xbDu276R1Dh1tqAk68yd4yirnIh52Kd5B+nJ8CgHA7o3UKakpBjj6vSzi5vNCzJIA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-graphql@0.40.0':
+ resolution: {integrity: sha512-LVRdEHWACWOczv2imD+mhUrLMxsEjPPi32vIZJT57zygR5aUiA4em8X3aiGOCycgbMWkIu8xOSGSxdx3JmzN+w==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-hapi@0.38.0':
+ resolution: {integrity: sha512-ZcOqEuwuutTDYIjhDIStix22ECblG/i9pHje23QGs4Q4YS4RMaZ5hKCoQJxW88Z4K7T53rQkdISmoXFKDV8xMg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-http@0.51.1':
+ resolution: {integrity: sha512-6b3nZnFFEz/3xZ6w8bVxctPUWIPWiXuPQ725530JgxnN1cvYFd8CJ75PrHZNjynmzSSnqBkN3ef4R9N+RpMh8Q==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-ioredis@0.40.0':
+ resolution: {integrity: sha512-Jv/fH7KhpWe4KBirsiqeUJIYrsdR2iu2l4nWhfOlRvaZ+zYIiLEzTQR6QhBbyRoAbU4OuYJzjWusOmmpGBnwng==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-koa@0.40.0':
+ resolution: {integrity: sha512-dJc3H/bKMcgUYcQpLF+1IbmUKus0e5Fnn/+ru/3voIRHwMADT3rFSUcGLWSczkg68BCgz0vFWGDTvPtcWIFr7A==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mongodb@0.43.0':
+ resolution: {integrity: sha512-bMKej7Y76QVUD3l55Q9YqizXybHUzF3pujsBFjqbZrRn2WYqtsDtTUlbCK7fvXNPwFInqZ2KhnTqd0gwo8MzaQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mongoose@0.38.1':
+ resolution: {integrity: sha512-zaeiasdnRjXe6VhYCBMdkmAVh1S5MmXC/0spet+yqoaViGnYst/DOxPvhwg3yT4Yag5crZNWsVXnA538UjP6Ow==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mysql2@0.38.1':
+ resolution: {integrity: sha512-qkpHMgWSDTYVB1vlZ9sspf7l2wdS5DDq/rbIepDwX5BA0N0068JTQqh0CgAh34tdFqSCnWXIhcyOXC2TtRb0sg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-mysql@0.38.1':
+ resolution: {integrity: sha512-+iBAawUaTfX/HAlvySwozx0C2B6LBfNPXX1W8Z2On1Uva33AGkw2UjL9XgIg1Pj4eLZ9R4EoJ/aFz+Xj4E/7Fw==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-nestjs-core@0.37.1':
+ resolution: {integrity: sha512-ebYQjHZEmGHWEALwwDGhSQVLBaurFnuLIkZD5igPXrt7ohfF4lc5/4al1LO+vKc0NHk8SJWStuRueT86ISA8Vg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation-pg@0.41.0':
+ resolution: {integrity: sha512-BSlhpivzBD77meQNZY9fS4aKgydA8AJBzv2dqvxXFy/Hq64b7HURgw/ztbmwFeYwdF5raZZUifiiNSMLpOJoSA==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation@0.43.0':
+ resolution: {integrity: sha512-S1uHE+sxaepgp+t8lvIDuRgyjJWisAb733198kwQTUc9ZtYQ2V2gmyCtR1x21ePGVLoMiX/NWY7WA290hwkjJQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/instrumentation@0.51.1':
+ resolution: {integrity: sha512-JIrvhpgqY6437QIqToyozrUG1h5UhwHkaGK/WAX+fkrpyPtc+RO5FkRtUd9BH0MibabHHvqsnBGKfKVijbmp8w==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.3.0
+
+ '@opentelemetry/redis-common@0.36.2':
+ resolution: {integrity: sha512-faYX1N0gpLhej/6nyp6bgRjzAKXn5GOEMYY7YhciSfCoITAktLUtQ36d24QEWNA1/WA1y6qQunCe0OhHRkVl9g==}
+ engines: {node: '>=14'}
+
+ '@opentelemetry/resources@1.24.1':
+ resolution: {integrity: sha512-cyv0MwAaPF7O86x5hk3NNgenMObeejZFLJJDVuSeSMIsknlsj3oOZzRv3qSzlwYomXsICfBeFFlxwHQte5mGXQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.9.0'
+
+ '@opentelemetry/sdk-metrics@1.24.1':
+ resolution: {integrity: sha512-FrAqCbbGao9iKI+Mgh+OsC9+U2YMoXnlDHe06yH7dvavCKzE3S892dGtX54+WhSFVxHR/TMRVJiK/CV93GR0TQ==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.3.0 <1.9.0'
+
+ '@opentelemetry/sdk-trace-base@1.24.1':
+ resolution: {integrity: sha512-zz+N423IcySgjihl2NfjBf0qw1RWe11XIAWVrTNOSSI6dtSPJiVom2zipFB2AEEtJWpv0Iz6DY6+TjnyTV5pWg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': '>=1.0.0 <1.9.0'
+
+ '@opentelemetry/semantic-conventions@1.24.1':
+ resolution: {integrity: sha512-VkliWlS4/+GHLLW7J/rVBA00uXus1SWvwFvcUDxDwmFxYfg/2VI6ekwdXS28cjI8Qz2ky2BzG8OUHo+WeYIWqw==}
+ engines: {node: '>=14'}
+
+ '@opentelemetry/sql-common@0.40.1':
+ resolution: {integrity: sha512-nSDlnHSqzC3pXn/wZEZVLuAuJ1MYMXPBwtv2qAbCa3847SaHItdE7SzUq/Jtb0KZmh1zfAbNi3AAMjztTT4Ugg==}
+ engines: {node: '>=14'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.1.0
+
'@peculiar/asn1-android@2.3.10':
resolution: {integrity: sha512-z9Rx9cFJv7UUablZISe7uksNbFJCq13hO0yEAOoIpAymALTLlvUOSLnGiQS7okPaM5dP42oTLhezH6XDXRXjGw==}
@@ -3383,6 +3537,9 @@ packages:
resolution: {integrity: sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==}
engines: {node: '>=14'}
+ '@prisma/instrumentation@5.14.0':
+ resolution: {integrity: sha512-DeybWvIZzu/mUsOYP9MVd6AyBj+MP7xIMrcuIn25MX8FiQX39QBnET5KhszTAip/ToctUuDwSJ46QkIoyo3RFA==}
+
'@radix-ui/react-compose-refs@1.0.1':
resolution: {integrity: sha512-fDSBgd44FKHa1FRMU59qBMPFcl2PZE+2nmqunj+BWFyYYjnhIDWL2ItDs3rrbJDQOtzt5nIebLCQc4QRfz6LJw==}
peerDependencies:
@@ -3545,6 +3702,37 @@ packages:
'@rushstack/ts-command-line@4.19.2':
resolution: {integrity: sha512-cqmXXmBEBlzo9WtyUrHtF9e6kl0LvBY7aTSVX4jfnBfXWZQWnPq9JTFPlQZ+L/ZwjZ4HrNwQsOVvhe9oOucZkw==}
+ '@sentry/core@8.5.0':
+ resolution: {integrity: sha512-SO3ddBzGdha+Oflp+IKwBxj+7ds1q69OAT3VsypTd+WUFQdI9DIhR92Bjf+QQZCIzUNOi79VWOh3aOi3f6hMnw==}
+ engines: {node: '>=14.18'}
+
+ '@sentry/node@8.5.0':
+ resolution: {integrity: sha512-t9cHAx/wLJYtdVf2XlzKlRJGvwdAp1wjzG0tC4E1Znx74OuUS1cFNo5WrGuOi0/YcWSxiJaxBvtUcsWK86fIgw==}
+ engines: {node: '>=14.18'}
+
+ '@sentry/opentelemetry@8.5.0':
+ resolution: {integrity: sha512-AbxFUNjuTKQ9ugZrssmGtPxWkBr4USNoP7GjaaGCNwNzvIVYCa+i8dv7BROJiW2lsxNAremULEbh+nbVmhGxDA==}
+ engines: {node: '>=14.18'}
+ peerDependencies:
+ '@opentelemetry/api': ^1.8.0
+ '@opentelemetry/core': ^1.24.1
+ '@opentelemetry/instrumentation': ^0.51.1
+ '@opentelemetry/sdk-trace-base': ^1.23.0
+ '@opentelemetry/semantic-conventions': ^1.23.0
+
+ '@sentry/profiling-node@8.5.0':
+ resolution: {integrity: sha512-nEXJqVNfZWYi4PakQXBZCJeH59UlnBv+zaYftDNUUXttCmzRXpL1ujNm5mJrJHlWjV7tgIFw02HW3nh2yyKOkw==}
+ engines: {node: '>=14.18'}
+ hasBin: true
+
+ '@sentry/types@8.5.0':
+ resolution: {integrity: sha512-eDgkSmKI4+XL0QZm4H3j/n1RgnrbnjXZmjj+LsfccRZQwbPu9bWlc8q7Y7Ty1gOsoUpX+TecNLp2a8CRID4KHA==}
+ engines: {node: '>=14.18'}
+
+ '@sentry/utils@8.5.0':
+ resolution: {integrity: sha512-fdrCzo8SAYiw9JBhkJPqYqJkDXZ/wICzN7+zcXIuzKNhE1hdoFjeKcPnpUI3bKZCG6e3hT1PTYQXhVw7GIZV9w==}
+ engines: {node: '>=14.18'}
+
'@shikijs/core@1.4.0':
resolution: {integrity: sha512-CxpKLntAi64h3j+TwWqVIQObPTED0FyXLHTTh3MKXtqiQNn2JGcMQQ362LftDbc9kYbDtrksNMNoVmVXzKFYUQ==}
@@ -4307,12 +4495,18 @@ packages:
'@types/connect@3.4.35':
resolution: {integrity: sha512-cdeYyv4KWoEgpBISTxWvqYsVy444DOqehiF3fM3ne10AmJ62RSyNkUnxMJXHQWRQQX2eR94m5y1IZyDwBjV9FQ==}
+ '@types/connect@3.4.36':
+ resolution: {integrity: sha512-P63Zd/JUGq+PdrM1lv0Wv5SBYeA2+CORvbrXbngriYY0jzLUWfQMQQxOhjONEz/wlHOAxOdY7CY65rgQdTjq2w==}
+
'@types/content-disposition@0.5.8':
resolution: {integrity: sha512-QVSSvno3dE0MgO76pJhmv4Qyi/j0Yk9pBp0Y7TJ2Tlj+KCgJWY6qX7nnxCOLkZ3VYRSIk1WTxCvwUSdx6CCLdg==}
'@types/cookie@0.6.0':
resolution: {integrity: sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA==}
+ '@types/cookies@0.9.0':
+ resolution: {integrity: sha512-40Zk8qR147RABiQ7NQnBzWzDcjKzNrntB5BAmeGCb2p/MIyOE+4BVvc17wumsUqUw00bJYqoXFHYygQnEFh4/Q==}
+
'@types/core-js@2.5.8':
resolution: {integrity: sha512-VgnAj6tIAhJhZdJ8/IpxdatM8G4OD3VWGlp6xIxUGENZlpbob9Ty4VVdC1FIEp0aK6DBscDDjyzy5FB60TuNqg==}
@@ -4383,9 +4577,15 @@ packages:
'@types/htmlescape@1.1.3':
resolution: {integrity: sha512-tuC81YJXGUe0q8WRtBNW+uyx79rkkzWK651ALIXXYq5/u/IxjX4iHneGF2uUqzsNp+F+9J2mFZOv9jiLTtIq0w==}
+ '@types/http-assert@1.5.5':
+ resolution: {integrity: sha512-4+tE/lwdAahgZT1g30Jkdm9PzFRde0xwxBNUyRsCitRvCQB90iuA2uJYdUnhnANRcqGXaWOGY4FEoxeElNAK2g==}
+
'@types/http-cache-semantics@4.0.4':
resolution: {integrity: sha512-1m0bIFVc7eJWyve9S0RnuRgcQqF/Xd5QsUZAZeQFr1Q3/p9JWoQQEqmVy+DPTNpGXwhgIetAoYF8JSc33q29QA==}
+ '@types/http-errors@2.0.4':
+ resolution: {integrity: sha512-D0CFMMtydbJAegzOyHjtiKPLlvnm3iTZyZRSZoLq2mRhDdmLfIWOCYPfQJ4cu2erKghU++QvjcUjp/5h7hESpA==}
+
'@types/http-link-header@1.0.5':
resolution: {integrity: sha512-AxhIKR8UbyoqCTNp9rRepkktHuUOw3DjfOfDCaO9kwI8AYzjhxyrvZq4+mRw/2daD3hYDknrtSeV6SsPwmc71w==}
@@ -4422,9 +4622,21 @@ packages:
'@types/jsrsasign@10.5.14':
resolution: {integrity: sha512-lppSlfK6etu+cuKs40K4rg8As79PH6hzIB+v55zSqImbSH3SE6Fm8MBHCiI91cWlAP3Z4igtJK1VL3fSN09blQ==}
+ '@types/keygrip@1.0.6':
+ resolution: {integrity: sha512-lZuNAY9xeJt7Bx4t4dx0rYCDqGPW8RXhQZK1td7d4H6E9zYbLoOtjBvfwdTKpsyxQI/2jv+armjX/RW+ZNpXOQ==}
+
'@types/keyv@3.1.4':
resolution: {integrity: sha512-BQ5aZNSCpj7D6K2ksrRCTmKRLEpnPvWDiLPfoGyhZ++8YtiK9d/3DBKPJgry359X/P1PfruyYwvnvwFjuEiEIg==}
+ '@types/koa-compose@3.2.8':
+ resolution: {integrity: sha512-4Olc63RY+MKvxMwVknCUDhRQX1pFQoBZ/lXcRLP69PQkEpze/0cr8LNqJQe5NFb/b19DWi2a5bTi2VAlQzhJuA==}
+
+ '@types/koa@2.14.0':
+ resolution: {integrity: sha512-DTDUyznHGNHAl+wd1n0z1jxNajduyTh8R53xoewuerdBzGo6Ogj6F2299BFtrexJw4NtgjsI5SMPCmV9gZwGXA==}
+
+ '@types/koa__router@12.0.3':
+ resolution: {integrity: sha512-5YUJVv6NwM1z7m6FuYpKfNLTZ932Z6EF6xy2BbtpJSyn13DKNQEkXVffFVSnJHxvwwWh2SAeumpjAYUELqgjyw==}
+
'@types/lodash@4.14.191':
resolution: {integrity: sha512-BdZ5BCCvho3EIXw6wUCXHe7rS53AIDPLE+JzwgT+OsJk53oBfbSmZZ7CX4VaRoN78N+TJpFi9QPlfIVNmJYWxQ==}
@@ -4458,6 +4670,9 @@ packages:
'@types/mute-stream@0.0.4':
resolution: {integrity: sha512-CPM9nzrCPPJHQNA9keH9CVkVI+WR5kMa+7XEs5jcGQ0VoAGnLv242w8lIVgwAEfmE4oufJRaTc9PNLQl0ioAow==}
+ '@types/mysql@2.15.22':
+ resolution: {integrity: sha512-wK1pzsJVVAjYCSZWQoWHziQZbNggXFDUEIGf54g4ZM/ERuP86uGdWeKZWMYlqTPMZfHJJvLPyogXGvCOg87yLQ==}
+
'@types/node-fetch@3.0.3':
resolution: {integrity: sha512-HhggYPH5N+AQe/OmN6fmhKmRRt2XuNJow+R3pQwJxOOF9GuwM7O2mheyGeIrs5MOIeNjDEdgdoyHBOrFeJBR3g==}
deprecated: This is a stub types definition. node-fetch provides its own type definitions, so you do not need this installed.
@@ -4495,9 +4710,15 @@ packages:
'@types/parse-link-header@2.0.3':
resolution: {integrity: sha512-ffLAxD6Xqcf2gSbtEJehj8yJ5R/2OZqD4liodQvQQ+hhO4kg1mk9ToEZQPMtNTm/zIQj2GNleQbsjPp9+UQm4Q==}
+ '@types/pg-pool@2.0.4':
+ resolution: {integrity: sha512-qZAvkv1K3QbmHHFYSNRYPkRjOWRLBYrL4B9c+wG0GSVGBw0NtJwPcgx/DSddeDJvRGMHCEQ4VMEVfuJ/0gZ3XQ==}
+
'@types/pg@8.11.5':
resolution: {integrity: sha512-2xMjVviMxneZHDHX5p5S6tsRRs7TpDHeeK7kTTMe/kAC/mRRNjWHjZg0rkiY+e17jXSZV3zJYDxXV8Cy72/Vuw==}
+ '@types/pg@8.6.1':
+ resolution: {integrity: sha512-1Kc4oAGzAl7uqUStZCDvaLFqZrW9qWSjXOmBfdgyBP5La7Us6Mg4GBvRlSoaZMhQF/zSj1C8CtKMBkoiT8eL8w==}
+
'@types/pretty-hrtime@1.0.1':
resolution: {integrity: sha512-VjID5MJb1eGKthz2qUerWT8+R4b9N+CHvGCzg9fn4kWZgaF9AhdYikQio3R7wV8YY1NsQKPaCwKz1Yff+aHNUQ==}
@@ -4558,6 +4779,9 @@ packages:
'@types/serviceworker@0.0.67':
resolution: {integrity: sha512-7TCH7iNsCSNb+aUD9M/36TekrWFSLCjNK8zw/3n5kOtRjbLtDfGYMXTrDnGhSfqXNwpqmt9Vd90w5C/ad1tX6Q==}
+ '@types/shimmer@1.0.5':
+ resolution: {integrity: sha512-9Hp0ObzwwO57DpLFF0InUjUm/II8GmKAvzbefxQTihCb7KI6yc9yzf0nLc4mVdby5N4DRCgQM2wCup9KTieeww==}
+
'@types/simple-oauth2@5.0.7':
resolution: {integrity: sha512-8JbWVJbiTSBQP/7eiyGKyXWAqp3dKQZpaA+pdW16FCi32ujkzRMG8JfjoAzdWt6W8U591ZNdHcPtP2D7ILTKuA==}
@@ -5003,6 +5227,16 @@ packages:
resolution: {integrity: sha512-PYAthTa2m2VKxuvSD3DPC/Gy+U+sOA1LAuT8mkmRuvw+NACSaeXEQ+NHcVF7rONl6qcaxV3Uuemwawk+7+SJLw==}
engines: {node: '>= 0.6'}
+ acorn-import-assertions@1.9.0:
+ resolution: {integrity: sha512-cmMwop9x+8KFhxvKrKfPYmN6/pKTYYHBqLa0DfvVZcKMJWNyWLnaqND7dx/qn66R7ewM1UX5XMaDVP5wlVTaVA==}
+ peerDependencies:
+ acorn: ^8
+
+ acorn-import-attributes@1.9.5:
+ resolution: {integrity: sha512-n02Vykv5uA3eHGM/Z2dQrcD56kL8TyDb2p1+0P83PClMnC/nc+anbQRhIOWnSq4Ke/KvDPrY3C9hDtC/A3eHnQ==}
+ peerDependencies:
+ acorn: ^8
+
acorn-jsx@5.3.2:
resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==}
peerDependencies:
@@ -5046,9 +5280,9 @@ packages:
resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==}
engines: {node: '>=18'}
- aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424:
- resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424}
- version: 0.1.4
+ aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02:
+ resolution: {tarball: https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02}
+ version: 0.1.9
engines: {vscode: ^1.83.0}
ajv-draft-04@1.0.0:
@@ -6963,9 +7197,8 @@ packages:
engines: {node: '>=0.4.7'}
hasBin: true
- happy-dom@14.7.1:
- resolution: {integrity: sha512-v60Q0evZ4clvMcrAh5/F8EdxDdfHdFrtffz/CNe10jKD+nFweZVxM91tW+UyY2L4AtpgIaXdZ7TQmiO1pfcwbg==}
- engines: {node: '>=16.0.0'}
+ happy-dom@10.0.3:
+ resolution: {integrity: sha512-WkCP+Z5fX6U5PY+yHP3ElV5D9PoxRAHRWPFq3pG9rg/6Hjf5ak7dozAgSCywsTRUq2qfa8vV8OQvUy5pRXy8EQ==}
hard-rejection@2.1.0:
resolution: {integrity: sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==}
@@ -7153,6 +7386,12 @@ packages:
resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
engines: {node: '>=6'}
+ import-in-the-middle@1.4.2:
+ resolution: {integrity: sha512-9WOz1Yh/cvO/p69sxRmhyQwrIGGSp7EIdcb+fFNVi7CzQGQB8U1/1XrKVSbEd/GNOAeM0peJtmi7+qphe7NvAw==}
+
+ import-in-the-middle@1.7.4:
+ resolution: {integrity: sha512-Lk+qzWmiQuRPPulGQeK5qq0v32k2bHnWrRPFgqyvhw7Kkov5L6MOLOIU3pcWeujc9W4q54Cp3Q2WV16eQkc7Bg==}
+
import-lazy@4.0.0:
resolution: {integrity: sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==}
engines: {node: '>=8'}
@@ -8306,6 +8545,9 @@ packages:
resolution: {integrity: sha512-qxBgB7Qa2sEQgHFjj0dSigq7fX4k6Saisd5Nelwp2q8mlbAFh5dHV9JTTlF8viYJLSSWgMCZFUom8PJcMNBoJw==}
engines: {node: '>= 8'}
+ module-details-from-path@1.0.3:
+ resolution: {integrity: sha512-ySViT69/76t8VhE1xXHK6Ch4NcDd26gx0MzKXLO+F7NOtnqH68d9zF94nT8ZWSxXh8ELOERsnJO/sWt1xZYw5A==}
+
mri@1.2.0:
resolution: {integrity: sha512-tzzskb3bG8LvYGFF/mDTpq3jpI6Q9wc3LEmBaghu+DdCssd1FakN7Bc0hVNmEyGq1bq3RgfkCb3cmQLpNPOroA==}
engines: {node: '>=4'}
@@ -8409,6 +8651,10 @@ packages:
nise@5.1.4:
resolution: {integrity: sha512-8+Ib8rRJ4L0o3kfmyVCL7gzrohyDe0cMFTBa2d364yIrEGMEoetznKJx899YxjybU6bL9SQkYPSBBs1gyYs8Xg==}
+ node-abi@3.62.0:
+ resolution: {integrity: sha512-CPMcGa+y33xuL1E0TcNIu4YyaZCxnnvkVaEXrsosR3FxN+fV8xvb7Mzpb7IgKler10qeMkE6+Dp8qJhpzdq35g==}
+ engines: {node: '>=10'}
+
node-abort-controller@3.1.1:
resolution: {integrity: sha512-AGK2yQKIjRuqnc6VkX2Xj5d+QW8xZ87pa1UK6yA6ouUyuxfHuMP6umE5QK7UmTeOAymo+Zx1Fxiuw9rVx8taHQ==}
@@ -8622,6 +8868,10 @@ packages:
resolution: {integrity: sha512-es3mGcDXV6TKPo6n3aohzHm0qxhLyR39MhF6mkD1FwFGjhxnqMqfSIgM0eCpInZvqatve4CxmXcMZw3jnnsaXw==}
hasBin: true
+ opentelemetry-instrumentation-fetch-node@1.2.0:
+ resolution: {integrity: sha512-aiSt/4ubOTyb1N5C2ZbGrBvaJOXIZhZvpRPYuUVxQJe27wJZqf/o65iPrqgLcgfeOLaQ8cS2Q+762jrYvniTrA==}
+ engines: {node: '>18.0.0'}
+
optionator@0.9.3:
resolution: {integrity: sha512-JjCoypp+jKn1ttEFExxhetCKeJt9zhAgAve5FXHixTvFDW/5aEktX9bufBKLRRMdU7bNtpLfcGu94B3cdEJgjg==}
engines: {node: '>= 0.8.0'}
@@ -9539,6 +9789,10 @@ packages:
resolution: {integrity: sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==}
engines: {node: '>=0.10.0'}
+ require-in-the-middle@7.3.0:
+ resolution: {integrity: sha512-nQFEv9gRw6SJAwWD2LrL0NmQvAcO7FBwJbwmr2ttPAacfy0xuiOjE5zt+zM4xDyuyvUaxBi/9gb2SoCyNEVJcw==}
+ engines: {node: '>=8.6.0'}
+
require-main-filename@2.0.0:
resolution: {integrity: sha512-NKN5kMDylKuldxYLSUfrbo5Tuzh4hd+2E8NPPX02mZtn1VuREQToYe/ZdlJy+J3uCpfaiGF05e7B8W0iXbQHmg==}
@@ -9752,6 +10006,9 @@ packages:
shiki@1.4.0:
resolution: {integrity: sha512-5WIn0OL8PWm7JhnTwRWXniy6eEDY234mRrERVlFa646V2ErQqwIFd2UML7e0Pq9eqSKLoMa3Ke+xbsF+DAuy+Q==}
+ shimmer@1.2.1:
+ resolution: {integrity: sha512-sQTKC1Re/rM6XyFM6fIAGHRPVGvyXfgzIDvzoq608vM+jeyVD0Tu1E6Np0Kc2zAIFWIj963V2800iF/9LPieQw==}
+
side-channel@1.0.4:
resolution: {integrity: sha512-q5XPytqFEIKHkGdiMIrY10mvLRvnQh42/+GoBlFW3b2LXLE2xxJpZFdm94we0BaoV3RwJyGqg5wS7epxTv0Zvw==}
@@ -10848,8 +11105,8 @@ packages:
vue-component-type-helpers@2.0.16:
resolution: {integrity: sha512-qisL/iAfdO++7w+SsfYQJVPj6QKvxp4i1MMxvsNO41z/8zu3KuAw9LkhKUfP/kcOWGDxESp+pQObWppXusejCA==}
- vue-component-type-helpers@2.0.17:
- resolution: {integrity: sha512-2car49m8ciqg/JjgMBkx7o/Fd2A7fHESxNqL/2vJYFLXm4VwYO4yH0rexOi4a35vwNgDyvt17B07Vj126l9rAQ==}
+ vue-component-type-helpers@2.0.19:
+ resolution: {integrity: sha512-cN3f1aTxxKo4lzNeQAkVopswuImUrb5Iurll9Gaw5cqpnbTAxtEMM1mgi6ou4X79OCyqYv1U1mzBHJkzmiK82w==}
vue-demi@0.14.7:
resolution: {integrity: sha512-EOG8KXDQNwkJILkx/gPcoL/7vH+hORoBaKgGe+6W7VFMvCYJfmF2dGbvgDroVnI8LU7/kTu8mbjRZGBU1z9NTA==}
@@ -10948,6 +11205,10 @@ packages:
webpack-virtual-modules@0.5.0:
resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
+ whatwg-encoding@2.0.0:
+ resolution: {integrity: sha512-p41ogyeMUrw3jWclHWTQg1k05DSVXPLcVxRTYsXUk+ZooOCZLcoYgPZ/HL/D/N+uQPOtcp1me1WhBEaX02mhWg==}
+ engines: {node: '>=12'}
+
whatwg-encoding@3.1.1:
resolution: {integrity: sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==}
engines: {node: '>=18'}
@@ -11894,12 +12155,6 @@ snapshots:
'@babel/core': 7.23.5
'@babel/helper-plugin-utils': 7.22.5
- '@babel/plugin-syntax-bigint@7.8.3(@babel/core@7.24.0)':
- dependencies:
- '@babel/core': 7.24.0
- '@babel/helper-plugin-utils': 7.22.5
- optional: true
-
'@babel/plugin-syntax-class-properties@7.12.13(@babel/core@7.23.5)':
dependencies:
'@babel/core': 7.23.5
@@ -13698,6 +13953,203 @@ snapshots:
'@open-draft/until@2.1.0': {}
+ '@opentelemetry/api-logs@0.51.1':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+
+ '@opentelemetry/api@1.8.0': {}
+
+ '@opentelemetry/context-async-hooks@1.24.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+
+ '@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/semantic-conventions': 1.24.1
+
+ '@opentelemetry/instrumentation-connect@0.36.0(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ '@types/connect': 3.4.36
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-express@0.39.0(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-fastify@0.36.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-graphql@0.40.0(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-hapi@0.38.0(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-http@0.51.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ semver: 7.6.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-ioredis@0.40.0(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/redis-common': 0.36.2
+ '@opentelemetry/semantic-conventions': 1.24.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-koa@0.40.0(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ '@types/koa': 2.14.0
+ '@types/koa__router': 12.0.3
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mongodb@0.43.0(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/sdk-metrics': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mongoose@0.38.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mysql2@0.38.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.8.0)
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-mysql@0.38.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ '@types/mysql': 2.15.22
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-nestjs-core@0.37.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation-pg@0.41.0(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ '@opentelemetry/sql-common': 0.40.1(@opentelemetry/api@1.8.0)
+ '@types/pg': 8.6.1
+ '@types/pg-pool': 2.0.4
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/instrumentation@0.43.0(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@types/shimmer': 1.0.5
+ import-in-the-middle: 1.4.2
+ require-in-the-middle: 7.3.0
+ semver: 7.6.0
+ shimmer: 1.2.1
+ transitivePeerDependencies:
+ - supports-color
+ optional: true
+
+ '@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/api-logs': 0.51.1
+ '@types/shimmer': 1.0.5
+ import-in-the-middle: 1.7.4
+ require-in-the-middle: 7.3.0
+ semver: 7.6.0
+ shimmer: 1.2.1
+ transitivePeerDependencies:
+ - supports-color
+
+ '@opentelemetry/redis-common@0.36.2': {}
+
+ '@opentelemetry/resources@1.24.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+
+ '@opentelemetry/sdk-metrics@1.24.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0)
+ lodash.merge: 4.6.2
+
+ '@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+
+ '@opentelemetry/semantic-conventions@1.24.1': {}
+
+ '@opentelemetry/sql-common@0.40.1(@opentelemetry/api@1.8.0)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+
'@peculiar/asn1-android@2.3.10':
dependencies:
'@peculiar/asn1-schema': 2.3.8
@@ -13745,6 +14197,14 @@ snapshots:
'@pkgjs/parseargs@0.11.0':
optional: true
+ '@prisma/instrumentation@5.14.0':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
+ transitivePeerDependencies:
+ - supports-color
+
'@radix-ui/react-compose-refs@1.0.1(@types/react@18.0.28)(react@18.3.1)':
dependencies:
'@babel/runtime': 7.23.4
@@ -13891,6 +14351,72 @@ snapshots:
transitivePeerDependencies:
- '@types/node'
+ '@sentry/core@8.5.0':
+ dependencies:
+ '@sentry/types': 8.5.0
+ '@sentry/utils': 8.5.0
+
+ '@sentry/node@8.5.0':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/context-async-hooks': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-connect': 0.36.0(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-express': 0.39.0(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-fastify': 0.36.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-graphql': 0.40.0(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-hapi': 0.38.0(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-http': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-ioredis': 0.40.0(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-koa': 0.40.0(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-mongodb': 0.43.0(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-mongoose': 0.38.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-mysql': 0.38.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-mysql2': 0.38.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-nestjs-core': 0.37.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation-pg': 0.41.0(@opentelemetry/api@1.8.0)
+ '@opentelemetry/resources': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ '@prisma/instrumentation': 5.14.0
+ '@sentry/core': 8.5.0
+ '@sentry/opentelemetry': 8.5.0(@opentelemetry/api@1.8.0)(@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0))(@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/semantic-conventions@1.24.1)
+ '@sentry/types': 8.5.0
+ '@sentry/utils': 8.5.0
+ optionalDependencies:
+ opentelemetry-instrumentation-fetch-node: 1.2.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@sentry/opentelemetry@8.5.0(@opentelemetry/api@1.8.0)(@opentelemetry/core@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/instrumentation@0.51.1(@opentelemetry/api@1.8.0))(@opentelemetry/sdk-trace-base@1.24.1(@opentelemetry/api@1.8.0))(@opentelemetry/semantic-conventions@1.24.1)':
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/core': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/instrumentation': 0.51.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/sdk-trace-base': 1.24.1(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ '@sentry/core': 8.5.0
+ '@sentry/types': 8.5.0
+ '@sentry/utils': 8.5.0
+
+ '@sentry/profiling-node@8.5.0':
+ dependencies:
+ '@sentry/core': 8.5.0
+ '@sentry/node': 8.5.0
+ '@sentry/types': 8.5.0
+ '@sentry/utils': 8.5.0
+ detect-libc: 2.0.3
+ node-abi: 3.62.0
+ transitivePeerDependencies:
+ - supports-color
+
+ '@sentry/types@8.5.0': {}
+
+ '@sentry/utils@8.5.0':
+ dependencies:
+ '@sentry/types': 8.5.0
+
'@shikijs/core@1.4.0': {}
'@sideway/address@4.1.4':
@@ -14347,11 +14873,11 @@ snapshots:
dependencies:
'@storybook/global': 5.0.0
- '@storybook/addon-interactions@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+ '@storybook/addon-interactions@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
dependencies:
'@storybook/global': 5.0.0
'@storybook/instrumenter': 8.0.9
- '@storybook/test': 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+ '@storybook/test': 8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
'@storybook/types': 8.0.9
polished: 4.2.2
ts-dedent: 2.2.0
@@ -14855,14 +15381,14 @@ snapshots:
- encoding
- supports-color
- '@storybook/test@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+ '@storybook/test@8.0.9(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
dependencies:
'@storybook/client-logger': 8.0.9
'@storybook/core-events': 8.0.9
'@storybook/instrumenter': 8.0.9
'@storybook/preview-api': 8.0.9
'@testing-library/dom': 9.3.4
- '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
+ '@testing-library/jest-dom': 6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))
'@testing-library/user-event': 14.5.2(@testing-library/dom@9.3.4)
'@vitest/expect': 1.3.1
'@vitest/spy': 1.6.0
@@ -14923,7 +15449,7 @@ snapshots:
ts-dedent: 2.2.0
type-fest: 2.19.0
vue: 3.4.26(typescript@5.4.5)
- vue-component-type-helpers: 2.0.17
+ vue-component-type-helpers: 2.0.19
transitivePeerDependencies:
- encoding
- supports-color
@@ -15079,7 +15605,7 @@ snapshots:
lz-string: 1.5.0
pretty-format: 27.5.1
- '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+ '@testing-library/jest-dom@6.4.2(@jest/globals@29.7.0)(@types/jest@29.5.12)(jest@29.7.0(@types/node@20.12.7))(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
dependencies:
'@adobe/css-tools': 4.3.3
'@babel/runtime': 7.23.4
@@ -15093,7 +15619,7 @@ snapshots:
'@jest/globals': 29.7.0
'@types/jest': 29.5.12
jest: 29.7.0(@types/node@20.12.7)
- vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+ vitest: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
'@testing-library/user-event@14.5.2(@testing-library/dom@9.3.4)':
dependencies:
@@ -15189,10 +15715,21 @@ snapshots:
dependencies:
'@types/node': 20.12.7
+ '@types/connect@3.4.36':
+ dependencies:
+ '@types/node': 20.12.7
+
'@types/content-disposition@0.5.8': {}
'@types/cookie@0.6.0': {}
+ '@types/cookies@0.9.0':
+ dependencies:
+ '@types/connect': 3.4.35
+ '@types/express': 4.17.17
+ '@types/keygrip': 1.0.6
+ '@types/node': 20.12.7
+
'@types/core-js@2.5.8': {}
'@types/cross-spawn@6.0.2':
@@ -15266,8 +15803,12 @@ snapshots:
'@types/htmlescape@1.1.3': {}
+ '@types/http-assert@1.5.5': {}
+
'@types/http-cache-semantics@4.0.4': {}
+ '@types/http-errors@2.0.4': {}
+
'@types/http-link-header@1.0.5':
dependencies:
'@types/node': 20.12.7
@@ -15305,10 +15846,31 @@ snapshots:
'@types/jsrsasign@10.5.14': {}
+ '@types/keygrip@1.0.6': {}
+
'@types/keyv@3.1.4':
dependencies:
'@types/node': 20.12.7
+ '@types/koa-compose@3.2.8':
+ dependencies:
+ '@types/koa': 2.14.0
+
+ '@types/koa@2.14.0':
+ dependencies:
+ '@types/accepts': 1.3.7
+ '@types/content-disposition': 0.5.8
+ '@types/cookies': 0.9.0
+ '@types/http-assert': 1.5.5
+ '@types/http-errors': 2.0.4
+ '@types/keygrip': 1.0.6
+ '@types/koa-compose': 3.2.8
+ '@types/node': 20.12.7
+
+ '@types/koa__router@12.0.3':
+ dependencies:
+ '@types/koa': 2.14.0
+
'@types/lodash@4.14.191': {}
'@types/matter-js@0.19.6': {}
@@ -15337,6 +15899,10 @@ snapshots:
dependencies:
'@types/node': 20.12.7
+ '@types/mysql@2.15.22':
+ dependencies:
+ '@types/node': 20.12.7
+
'@types/node-fetch@3.0.3':
dependencies:
node-fetch: 3.3.2
@@ -15378,12 +15944,22 @@ snapshots:
'@types/parse-link-header@2.0.3': {}
+ '@types/pg-pool@2.0.4':
+ dependencies:
+ '@types/pg': 8.11.5
+
'@types/pg@8.11.5':
dependencies:
'@types/node': 20.12.7
pg-protocol: 1.6.0
pg-types: 4.0.1
+ '@types/pg@8.6.1':
+ dependencies:
+ '@types/node': 20.12.7
+ pg-protocol: 1.6.1
+ pg-types: 2.2.0
+
'@types/pretty-hrtime@1.0.1': {}
'@types/prop-types@15.7.5': {}
@@ -15439,6 +16015,8 @@ snapshots:
'@types/serviceworker@0.0.67': {}
+ '@types/shimmer@1.0.5': {}
+
'@types/simple-oauth2@5.0.7': {}
'@types/sinon@10.0.13':
@@ -15842,7 +16420,7 @@ snapshots:
vite: 5.2.11(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
vue: 3.4.26(typescript@5.4.5)
- '@vitest/coverage-v8@0.34.6(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
+ '@vitest/coverage-v8@0.34.6(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3))':
dependencies:
'@ampproject/remapping': 2.2.1
'@bcoe/v8-coverage': 0.2.3
@@ -15855,7 +16433,7 @@ snapshots:
std-env: 3.7.0
test-exclude: 6.0.0
v8-to-istanbul: 9.2.0
- vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+ vitest: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
transitivePeerDependencies:
- supports-color
@@ -16063,6 +16641,15 @@ snapshots:
mime-types: 2.1.35
negotiator: 0.6.3
+ acorn-import-assertions@1.9.0(acorn@8.11.3):
+ dependencies:
+ acorn: 8.11.3
+ optional: true
+
+ acorn-import-attributes@1.9.5(acorn@8.11.3):
+ dependencies:
+ acorn: 8.11.3
+
acorn-jsx@5.3.2(acorn@7.4.1):
dependencies:
acorn: 7.4.1
@@ -16103,7 +16690,7 @@ snapshots:
clean-stack: 5.2.0
indent-string: 5.0.0
- aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/3f79d6f0550369267220aa67702287948d885424:
+ aiscript-vscode@https://codeload.github.com/aiscript-dev/aiscript-vscode/tar.gz/34bf4e1530efcf1efa855bd04e2dab39735e1b02:
dependencies:
'@aiscript-dev/aiscript-languageserver': https://github.com/aiscript-dev/aiscript-languageserver/releases/download/0.1.6/aiscript-dev-aiscript-languageserver-0.1.6.tgz
vscode-languageclient: 9.0.1
@@ -16374,20 +16961,6 @@ snapshots:
transitivePeerDependencies:
- supports-color
- babel-jest@29.7.0(@babel/core@7.24.0):
- dependencies:
- '@babel/core': 7.24.0
- '@jest/transform': 29.7.0
- '@types/babel__core': 7.20.0
- babel-plugin-istanbul: 6.1.1
- babel-preset-jest: 29.6.3(@babel/core@7.24.0)
- chalk: 4.1.2
- graceful-fs: 4.2.11
- slash: 3.0.0
- transitivePeerDependencies:
- - supports-color
- optional: true
-
babel-plugin-istanbul@6.1.1:
dependencies:
'@babel/helper-plugin-utils': 7.22.5
@@ -16445,36 +17018,12 @@ snapshots:
'@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.23.5)
'@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.23.5)
- babel-preset-current-node-syntax@1.0.1(@babel/core@7.24.0):
- dependencies:
- '@babel/core': 7.24.0
- '@babel/plugin-syntax-async-generators': 7.8.4(@babel/core@7.24.0)
- '@babel/plugin-syntax-bigint': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-class-properties': 7.12.13(@babel/core@7.24.0)
- '@babel/plugin-syntax-import-meta': 7.10.4(@babel/core@7.24.0)
- '@babel/plugin-syntax-json-strings': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-logical-assignment-operators': 7.10.4(@babel/core@7.24.0)
- '@babel/plugin-syntax-nullish-coalescing-operator': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-numeric-separator': 7.10.4(@babel/core@7.24.0)
- '@babel/plugin-syntax-object-rest-spread': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-optional-catch-binding': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-optional-chaining': 7.8.3(@babel/core@7.24.0)
- '@babel/plugin-syntax-top-level-await': 7.14.5(@babel/core@7.24.0)
- optional: true
-
babel-preset-jest@29.6.3(@babel/core@7.23.5):
dependencies:
'@babel/core': 7.23.5
babel-plugin-jest-hoist: 29.6.3
babel-preset-current-node-syntax: 1.0.1(@babel/core@7.23.5)
- babel-preset-jest@29.6.3(@babel/core@7.24.0):
- dependencies:
- '@babel/core': 7.24.0
- babel-plugin-jest-hoist: 29.6.3
- babel-preset-current-node-syntax: 1.0.1(@babel/core@7.24.0)
- optional: true
-
babel-walk@3.0.0-canary-5:
dependencies:
'@babel/types': 7.24.0
@@ -18758,10 +19307,13 @@ snapshots:
optionalDependencies:
uglify-js: 3.17.4
- happy-dom@14.7.1:
+ happy-dom@10.0.3:
dependencies:
+ css.escape: 1.5.1
entities: 4.5.0
+ iconv-lite: 0.6.3
webidl-conversions: 7.0.0
+ whatwg-encoding: 2.0.0
whatwg-mimetype: 3.0.0
hard-rejection@2.1.0: {}
@@ -18929,6 +19481,21 @@ snapshots:
parent-module: 1.0.1
resolve-from: 4.0.0
+ import-in-the-middle@1.4.2:
+ dependencies:
+ acorn: 8.11.3
+ acorn-import-assertions: 1.9.0(acorn@8.11.3)
+ cjs-module-lexer: 1.2.2
+ module-details-from-path: 1.0.3
+ optional: true
+
+ import-in-the-middle@1.7.4:
+ dependencies:
+ acorn: 8.11.3
+ acorn-import-attributes: 1.9.5(acorn@8.11.3)
+ cjs-module-lexer: 1.2.2
+ module-details-from-path: 1.0.3
+
import-lazy@4.0.0: {}
import-local@3.1.0:
@@ -20420,6 +20987,8 @@ snapshots:
mock-socket@9.3.1: {}
+ module-details-from-path@1.0.3: {}
+
mri@1.2.0: {}
ms@2.0.0: {}
@@ -20535,6 +21104,10 @@ snapshots:
just-extend: 4.2.1
path-to-regexp: 1.8.0
+ node-abi@3.62.0:
+ dependencies:
+ semver: 7.6.0
+
node-abort-controller@3.1.1: {}
node-addon-api@3.2.1:
@@ -20763,6 +21336,15 @@ snapshots:
undici: 5.28.2
yargs-parser: 21.1.1
+ opentelemetry-instrumentation-fetch-node@1.2.0:
+ dependencies:
+ '@opentelemetry/api': 1.8.0
+ '@opentelemetry/instrumentation': 0.43.0(@opentelemetry/api@1.8.0)
+ '@opentelemetry/semantic-conventions': 1.24.1
+ transitivePeerDependencies:
+ - supports-color
+ optional: true
+
optionator@0.9.3:
dependencies:
'@aashutoshrathi/word-wrap': 1.2.6
@@ -21735,6 +22317,14 @@ snapshots:
require-from-string@2.0.2: {}
+ require-in-the-middle@7.3.0:
+ dependencies:
+ debug: 4.3.4(supports-color@8.1.1)
+ module-details-from-path: 1.0.3
+ resolve: 1.22.8
+ transitivePeerDependencies:
+ - supports-color
+
require-main-filename@2.0.0: {}
requires-port@1.0.0: {}
@@ -21995,6 +22585,8 @@ snapshots:
dependencies:
'@shikijs/core': 1.4.0
+ shimmer@1.2.1: {}
+
side-channel@1.0.4:
dependencies:
call-bind: 1.0.2
@@ -22590,7 +23182,7 @@ snapshots:
ts-dedent@2.2.0: {}
- ts-jest@29.1.2(@babel/core@7.24.0)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.24.0))(esbuild@0.20.2)(jest@29.7.0(@types/node@20.12.7))(typescript@5.1.6):
+ ts-jest@29.1.2(@babel/core@7.23.5)(@jest/types@29.6.3)(babel-jest@29.7.0(@babel/core@7.23.5))(esbuild@0.20.2)(jest@29.7.0(@types/node@20.12.7))(typescript@5.1.6):
dependencies:
bs-logger: 0.2.6
fast-json-stable-stringify: 2.1.0
@@ -22603,9 +23195,9 @@ snapshots:
typescript: 5.1.6
yargs-parser: 21.1.1
optionalDependencies:
- '@babel/core': 7.24.0
+ '@babel/core': 7.23.5
'@jest/types': 29.6.3
- babel-jest: 29.7.0(@babel/core@7.24.0)
+ babel-jest: 29.7.0(@babel/core@7.23.5)
esbuild: 0.20.2
ts-map@1.0.3: {}
@@ -22967,14 +23559,14 @@ snapshots:
sass: 1.76.0
terser: 5.30.3
- vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)):
+ vitest-fetch-mock@0.2.2(encoding@0.1.13)(vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)):
dependencies:
cross-fetch: 3.1.6(encoding@0.1.13)
- vitest: 0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
+ vitest: 0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3)
transitivePeerDependencies:
- encoding
- vitest@0.34.6(happy-dom@14.7.1)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3):
+ vitest@0.34.6(happy-dom@10.0.3)(jsdom@24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3))(sass@1.76.0)(terser@5.30.3):
dependencies:
'@types/chai': 4.3.11
'@types/chai-subset': 1.3.5
@@ -23001,7 +23593,7 @@ snapshots:
vite-node: 0.34.6(@types/node@20.12.7)(sass@1.76.0)(terser@5.30.3)
why-is-node-running: 2.2.2
optionalDependencies:
- happy-dom: 14.7.1
+ happy-dom: 10.0.3
jsdom: 24.0.0(bufferutil@4.0.7)(utf-8-validate@6.0.3)
transitivePeerDependencies:
- less
@@ -23052,7 +23644,7 @@ snapshots:
vue-component-type-helpers@2.0.16: {}
- vue-component-type-helpers@2.0.17: {}
+ vue-component-type-helpers@2.0.19: {}
vue-demi@0.14.7(vue@3.4.26(typescript@5.4.5)):
dependencies:
@@ -23171,6 +23763,10 @@ snapshots:
webpack-virtual-modules@0.5.0: {}
+ whatwg-encoding@2.0.0:
+ dependencies:
+ iconv-lite: 0.6.3
+
whatwg-encoding@3.1.1:
dependencies:
iconv-lite: 0.6.3