From 677e8fe323368fff89dcd5be2afe4ec35536dc37 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 2 Nov 2026 08:29:36 +0000 Subject: [PATCH 1/4] chore: set up x18n to check the DevTools client translations --- knip.jsonc | 1 + package.json | 2 + pnpm-lock.yaml | 145 ++++++++++++++++++++++++++++++++++++++++++++ pnpm-workspace.yaml | 2 + skills-lock.json | 21 +++++++ x18n.config.ts | 15 +++++ 6 files changed, 186 insertions(+) create mode 100644 x18n.config.ts diff --git a/knip.jsonc b/knip.jsonc index ced8214ddc..f1bf40dba9 100644 --- a/knip.jsonc +++ b/knip.jsonc @@ -9,6 +9,7 @@ "local.ts", "scripts/*.{js,mjs,ts}", "taze.config.ts", + "x18n.config.ts", "tests/{e2e,docs-screenshots}/**/*.ts" ], "project": [ diff --git a/package.json b/package.json index 2b7e99a0c1..02b78ee3b2 100644 --- a/package.json +++ b/package.json @@ -13,6 +13,7 @@ "watch": "turbo watch build", "dev": "pnpm run build && pnpm -C packages/devtools dev", "lint": "eslint --cache .", + "i18n:check": "x18n check", "knip": "knip", "release": "pnpm test && bumpp -r --all", "test": "pnpm lint && pnpm test:unit", @@ -43,6 +44,7 @@ "@types/node": "catalog:types", "@unocss/eslint-config": "catalog:buildtools", "@vitejs/devtools-kit": "catalog:types", + "@x18n/cli": "catalog:cli", "bumpp": "catalog:cli", "eslint": "catalog:cli", "eslint-flat-config-utils": "catalog:cli", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 7db60fc24e..4a02281fa1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -243,6 +243,9 @@ catalogs: '@playwright/test': specifier: ^1.64.1 version: 1.64.1 + '@x18n/cli': + specifier: ^0.1.6 + version: 0.1.6 bumpp: specifier: ^12.3.0 version: 12.3.0 @@ -554,6 +557,9 @@ importers: '@vitejs/devtools-kit': specifier: ^0.8.0 version: 0.8.0(@devframes/agentic@1.3.2)(cac@7.1.0)(crossws@0.4.12(srvx@0.11.22))(ocache@0.3.1)(srvx@0.11.22)(vite@8.3.5) + '@x18n/cli': + specifier: catalog:cli + version: 0.1.6(@devframes/agentic@1.3.2)(@typescript-eslint/types@8.72.0)(crossws@0.4.12(srvx@0.11.22))(ocache@0.3.1)(srvx@0.11.22)(typescript@6.0.3) bumpp: specifier: catalog:cli version: 12.3.0 @@ -3625,6 +3631,11 @@ packages: cpu: [x64] os: [win32] + '@valibot/to-json-schema@1.8.0': + resolution: {integrity: sha512-a0M+uwCuQZEPAo65NYkFSJ14O5c213KoSZmPdoCfuFvUhfULi4T2Z6Tpjv6lTVMW9RoLm74RpRAjNEa43KcN+g==} + peerDependencies: + valibot: ^1.5.0 + '@vercel/nft@1.11.0': resolution: {integrity: sha512-m1QFg+U+3yPOnP1xSYJ73UIRxLOXdts1JOhiOiyPYqEsALgrXFFINvgUaD6R6iNvaBFAjHllBCbkfx4FuOdpaA==} engines: {node: '>=20'} @@ -3867,6 +3878,25 @@ packages: '@webassemblyjs/wast-printer@1.14.1': resolution: {integrity: sha512-kPSSXE6De1XOR820C90RIo2ogvZG+c3KiHzqUoO/F34Y2shGzesfqv7o57xrxovZJH/MetF5UjroJ/R/3isoiw==} + '@x18n/builtins@0.1.6': + resolution: {integrity: sha512-Qc4XyB8vqxqEHUO1NbHiUyU83zzfp5BirQWpabvutwIcwfHn97FWgUyODR5nyDqTCHWenEQIRIBjglG+T3Yseg==} + + '@x18n/cli@0.1.6': + resolution: {integrity: sha512-w8SFEYuHq+EE/Ly+jhy1dZ9z8v7UuKzhR9KMC3Ec5w7+kTxoJdBeoxvvD+s1A9PFQJ4MtwrW5pCjZnEC6lBN1A==} + engines: {node: '>=22'} + hasBin: true + + '@x18n/core@0.1.6': + resolution: {integrity: sha512-3Y/5++ajEvdI3CXwGHd3kg7+WpAKPauY4sNJNaKROyUTT1k7klPt+nX77ffCwv2Kvs+lxPJTcYMExGIX1wGtlQ==} + + '@x18n/node@0.1.6': + resolution: {integrity: sha512-tmiJ8OGJwLq88DdxyW67a64kSQ6uPFzdMX4kegkRp5LwpUW2HuO0ZMYsSNCmTjRqnkipBRFCjyrrkPbVOf9QGw==} + engines: {node: '>=22'} + + '@x18n/ui@0.1.6': + resolution: {integrity: sha512-0rlML2yjwtcPwC5C5ncb11DO5Zsi7c91CR+jo7F3xMRC3zElLHT9id2ypzzAp61mzaGDDgGToPixWMgNBScQ/g==} + engines: {node: '>=22'} + '@xtuc/ieee754@1.2.0': resolution: {integrity: sha512-DX8nKgqcGwsc0eJSqYt5lwP4DH5FlHnmuWWBRy7X0NcaGR0ZtuyeESgMwTYVEtxmsNGY+qit4QYT/MIYTOTPeA==} @@ -5472,6 +5502,11 @@ packages: json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} + json5@2.2.3: + resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} + engines: {node: '>=6'} + hasBin: true + jsonc-eslint-parser@3.3.0: resolution: {integrity: sha512-hYTGkHGNRZnXOFZ1urhINADoqDrGfpy53cjw+dxk84QE0pUDujQzeUeamNs6Mz44/TKD49z2x6/GVSu4ZrtA+Q==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} @@ -6981,6 +7016,10 @@ packages: resolution: {integrity: sha512-Uqj49lWKB+iSSnneuwiYYJ7MZgkB+eXr0LXBhv4uDuAkXqnWmq65Sxflfvp0Lc6MdKjMUxGaeOKWJqz5SNiVIA==} engines: {node: '>=18'} + svelte@5.57.3: + resolution: {integrity: sha512-5mAftM3SoDV9X0zdF386F9p2pv8iKOwi/KfBS1XnBAkVCKFzVOLHUoa8iGWAt4buoicLmyWVRpuuD5wplXOQsg==} + engines: {node: '>=18'} + svgo@4.1.0: resolution: {integrity: sha512-bkxnTg1kSU0guhIBmibA6UUhrQmPVA1XsQLN+ylCd+UWzbnLkySOcXpyk1mrl05f+pcaCx2eHb+sp6BgMZWX+Q==} engines: {node: '>=16'} @@ -7443,6 +7482,14 @@ packages: peerDependencies: '@vue/compiler-core': ^3.5.44 + valibot@1.5.0: + resolution: {integrity: sha512-nil6AkP2TChWL43Z5uJ6GTxX01CUA+g8LWUM+N/rB9NBbkUMaUsi9PUNzlUPgoKASgmx9f7eGOYpJ04/fSa6FQ==} + peerDependencies: + typescript: ^6.0.3 + peerDependenciesMeta: + typescript: + optional: true + vanilla-jsoneditor@3.13.0: resolution: {integrity: sha512-u9C+D8Ocgr39Gnsfu7USjilfKssqbDqZehMdE0qbwIFnZFdNwkU4iKeEvJxvYbmlVi/sMLxMTrU14PhtXVyiZg==} @@ -10746,6 +10793,10 @@ snapshots: '@unrs/resolver-binding-win32-x64-msvc@1.12.2': optional: true + '@valibot/to-json-schema@1.8.0(valibot@1.5.0(typescript@6.0.3))': + dependencies: + valibot: 1.5.0(typescript@6.0.3) + '@vercel/nft@1.11.0(rollup@4.67.4)(supports-color@10.2.2)': dependencies: '@mapbox/node-pre-gyp': 2.0.3(supports-color@10.2.2) @@ -11170,6 +11221,74 @@ snapshots: '@webassemblyjs/ast': 1.14.1 '@xtuc/long': 4.2.2 + '@x18n/builtins@0.1.6(@typescript-eslint/types@8.72.0)(typescript@6.0.3)': + dependencies: + '@vue/compiler-sfc': 3.5.44 + '@x18n/core': 0.1.6(typescript@6.0.3) + json5: 2.2.3 + jsonc-parser: 3.3.1 + oxc-parser: 0.155.0 + svelte: 5.57.3(@typescript-eslint/types@8.72.0) + yaml: 2.9.1 + transitivePeerDependencies: + - '@typescript-eslint/types' + - typescript + + '@x18n/cli@0.1.6(@devframes/agentic@1.3.2)(@typescript-eslint/types@8.72.0)(crossws@0.4.12(srvx@0.11.22))(ocache@0.3.1)(srvx@0.11.22)(typescript@6.0.3)': + dependencies: + '@x18n/builtins': 0.1.6(@typescript-eslint/types@8.72.0)(typescript@6.0.3) + '@x18n/core': 0.1.6(typescript@6.0.3) + '@x18n/node': 0.1.6(@typescript-eslint/types@8.72.0)(typescript@6.0.3) + '@x18n/ui': 0.1.6(@typescript-eslint/types@8.72.0)(cac@7.1.0)(crossws@0.4.12(srvx@0.11.22))(ocache@0.3.1)(srvx@0.11.22)(typescript@6.0.3) + cac: 7.1.0 + devframe: 1.3.2(@devframes/agentic@1.3.2)(cac@7.1.0)(ocache@0.3.1)(srvx@0.11.22) + nostics: 1.3.0 + yaml: 2.9.1 + transitivePeerDependencies: + - '@devframes/agentic' + - '@typescript-eslint/types' + - crossws + - ocache + - srvx + - typescript + + '@x18n/core@0.1.6(typescript@6.0.3)': + dependencies: + nostics: 1.3.0 + valibot: 1.5.0(typescript@6.0.3) + transitivePeerDependencies: + - typescript + + '@x18n/node@0.1.6(@typescript-eslint/types@8.72.0)(typescript@6.0.3)': + dependencies: + '@x18n/builtins': 0.1.6(@typescript-eslint/types@8.72.0)(typescript@6.0.3) + '@x18n/core': 0.1.6(typescript@6.0.3) + chokidar: 6.0.0 + ignore: 7.0.14 + jiti: 2.7.0 + jsonc-parser: 3.3.1 + tinyglobby: 0.2.17 + yaml: 2.9.1 + transitivePeerDependencies: + - '@typescript-eslint/types' + - typescript + + '@x18n/ui@0.1.6(@typescript-eslint/types@8.72.0)(cac@7.1.0)(crossws@0.4.12(srvx@0.11.22))(ocache@0.3.1)(srvx@0.11.22)(typescript@6.0.3)': + dependencies: + '@devframes/agentic': 1.3.2(crossws@0.4.12(srvx@0.11.22))(devframe@1.3.2)(ocache@0.3.1) + '@valibot/to-json-schema': 1.8.0(valibot@1.5.0(typescript@6.0.3)) + '@x18n/builtins': 0.1.6(@typescript-eslint/types@8.72.0)(typescript@6.0.3) + '@x18n/core': 0.1.6(typescript@6.0.3) + devframe: 1.3.2(@devframes/agentic@1.3.2)(cac@7.1.0)(ocache@0.3.1)(srvx@0.11.22) + valibot: 1.5.0(typescript@6.0.3) + transitivePeerDependencies: + - '@typescript-eslint/types' + - cac + - crossws + - ocache + - srvx + - typescript + '@xtuc/ieee754@1.2.0': {} '@xtuc/long@4.2.2': {} @@ -12882,6 +13001,8 @@ snapshots: json-stable-stringify-without-jsonify@1.0.1: {} + json5@2.2.3: {} + jsonc-eslint-parser@3.3.0: dependencies: acorn: 8.18.0 @@ -15113,6 +15234,26 @@ snapshots: transitivePeerDependencies: - '@typescript-eslint/types' + svelte@5.57.3(@typescript-eslint/types@8.72.0): + dependencies: + '@jridgewell/remapping': 2.3.5 + '@jridgewell/sourcemap-codec': 1.6.0 + '@sveltejs/acorn-typescript': 1.0.13(acorn@8.18.0) + '@types/estree': 1.0.10 + acorn: 8.18.0 + aria-query: 5.3.1 + axobject-query: 4.1.0 + clsx: 2.1.1 + devalue: 5.9.4 + esm-env: 1.2.2 + esrap: 2.4.0(@typescript-eslint/types@8.72.0) + is-reference: 3.0.3 + locate-character: 3.0.0 + magic-string: 0.30.21 + zimmerframe: 1.1.5 + transitivePeerDependencies: + - '@typescript-eslint/types' + svgo@4.1.0: dependencies: commander: 11.1.0 @@ -15714,6 +15855,10 @@ snapshots: dependencies: '@vue/compiler-core': 3.5.44 + valibot@1.5.0(typescript@6.0.3): + optionalDependencies: + typescript: 6.0.3 + vanilla-jsoneditor@3.13.0(@typescript-eslint/types@8.72.0): dependencies: '@codemirror/autocomplete': 6.20.3 diff --git a/pnpm-workspace.yaml b/pnpm-workspace.yaml index fc31ea7e5b..7542078bd5 100644 --- a/pnpm-workspace.yaml +++ b/pnpm-workspace.yaml @@ -68,6 +68,7 @@ minimumReleaseAgeExclude: - '@unocss/vite@66.12.0' - '@unocss/webpack@66.12.0' - dompurify@3.4.17 + - '@x18n/*' - unocss@66.12.0 - chokidar@6.0.0 - skills@1.7.4 @@ -219,6 +220,7 @@ catalogs: '@nuxt/eslint': ^1.17.0 '@nuxt/test-utils': ^4.3.4 '@playwright/test': ^1.64.1 + '@x18n/cli': ^0.1.6 bumpp: ^12.3.0 eslint: ^10.14.0 eslint-flat-config-utils: ^3.2.0 diff --git a/skills-lock.json b/skills-lock.json index 7ea59af539..8cccaf9bc6 100644 --- a/skills-lock.json +++ b/skills-lock.json @@ -251,6 +251,27 @@ "skillPath": "skills/vite-devtools-kit/SKILL.md", "version": "0.8.0", "computedHash": "ecb40e4878b10688b0e29a35684d6d69261a081990230bb2e4123ebc16fb7ac8" + }, + "x18n": { + "source": "@x18n/cli", + "sourceType": "node_modules", + "skillPath": "skills/x18n/SKILL.md", + "version": "0.1.6", + "computedHash": "49b4763e9f535e516f4d85afc2eaa0be2b3cd65fc10a0b83147205b769e9d12a" + }, + "x18n-check": { + "source": "@x18n/cli", + "sourceType": "node_modules", + "skillPath": "skills/x18n-check/SKILL.md", + "version": "0.1.6", + "computedHash": "8029e8c2ef225a041c0bc19c4485a3eb921c62907bfd983313b136495adf92a3" + }, + "x18n-setup": { + "source": "@x18n/cli", + "sourceType": "node_modules", + "skillPath": "skills/x18n-setup/SKILL.md", + "version": "0.1.6", + "computedHash": "12043917e293542d639978b883fbf6fb2989a5d99f05df1454275b6e85516f2e" } } } diff --git a/x18n.config.ts b/x18n.config.ts new file mode 100644 index 0000000000..a9c21baef0 --- /dev/null +++ b/x18n.config.ts @@ -0,0 +1,15 @@ +import { defineConfig } from '@x18n/cli' + +// Translations of the Nuxt DevTools client UI. The languages mirror the ones +// the Devframe hub UI ships (`DEVTOOLS_LOCALES` in +// `packages/devtools/client/i18n/locales.ts`). Run `pnpm i18n:check`. +export default defineConfig({ + sourceLocale: 'en', + presets: ['vue-i18n'], + resources: [ + { pattern: 'packages/devtools/client/locales/{locale}.json' }, + ], + usage: { + include: ['packages/devtools/client/**/*.{ts,vue}'], + }, +}) From 22265fe9d693e9515a00dbcdd979357c4f405487 Mon Sep 17 00:00:00 2001 From: "Anthony Fu (via agent)" Date: Mon, 2 Nov 2026 08:29:58 +0000 Subject: [PATCH 2/4] feat(client): add i18n that follows the Devframe hub language --- packages/devtools/client/app.vue | 9 +- .../client/components/DisconnectIndicator.vue | 6 +- .../devtools/client/composables/frame-nav.ts | 14 ++- .../devtools/client/composables/locale.ts | 103 ++++++++++++++++++ .../client/composables/state-commands.ts | 11 +- .../devtools/client/composables/state-tabs.ts | 8 +- packages/devtools/client/composables/utils.ts | 6 - packages/devtools/client/i18n/index.ts | 65 +++++++++++ packages/devtools/client/i18n/locales.ts | 32 ++++++ packages/devtools/client/locales/de.json | 82 ++++++++++++++ packages/devtools/client/locales/en.json | 82 ++++++++++++++ packages/devtools/client/locales/es.json | 82 ++++++++++++++ packages/devtools/client/locales/fr.json | 82 ++++++++++++++ packages/devtools/client/locales/ja.json | 82 ++++++++++++++ packages/devtools/client/locales/ko.json | 82 ++++++++++++++ packages/devtools/client/locales/pt-BR.json | 82 ++++++++++++++ packages/devtools/client/locales/ru.json | 82 ++++++++++++++ packages/devtools/client/locales/zh-CN.json | 82 ++++++++++++++ packages/devtools/client/locales/zh-TW.json | 82 ++++++++++++++ packages/devtools/client/nuxt.config.ts | 8 ++ .../client/pages/modules/overview.vue | 38 +++---- packages/devtools/client/pages/settings.vue | 87 +++++++++++---- packages/devtools/client/plugins/i18n.ts | 15 +++ packages/devtools/package.json | 2 + packages/devtools/test/i18n-locales.test.ts | 36 ++++++ pnpm-lock.yaml | 62 +++++++++++ pnpm-workspace.yaml | 4 + 27 files changed, 1259 insertions(+), 67 deletions(-) create mode 100644 packages/devtools/client/composables/locale.ts create mode 100644 packages/devtools/client/i18n/index.ts create mode 100644 packages/devtools/client/i18n/locales.ts create mode 100644 packages/devtools/client/locales/de.json create mode 100644 packages/devtools/client/locales/en.json create mode 100644 packages/devtools/client/locales/es.json create mode 100644 packages/devtools/client/locales/fr.json create mode 100644 packages/devtools/client/locales/ja.json create mode 100644 packages/devtools/client/locales/ko.json create mode 100644 packages/devtools/client/locales/pt-BR.json create mode 100644 packages/devtools/client/locales/ru.json create mode 100644 packages/devtools/client/locales/zh-CN.json create mode 100644 packages/devtools/client/locales/zh-TW.json create mode 100644 packages/devtools/client/plugins/i18n.ts create mode 100644 packages/devtools/test/i18n-locales.test.ts diff --git a/packages/devtools/client/app.vue b/packages/devtools/client/app.vue index 192b1b240d..c678062348 100644 --- a/packages/devtools/client/app.vue +++ b/packages/devtools/client/app.vue @@ -7,6 +7,7 @@ import { useCopy } from '~/composables/editor' import { setupFrameNav } from '~/composables/frame-nav' import { WS_DEBOUNCE_TIME } from '~/composables/rpc' import { registerCommands } from '~/composables/state-commands' +import { i18n } from '~/i18n' import { wsConnectedOnce } from './composables/rpc' import { useSchemaInput } from './composables/state-schema' import { useDevToolsOptions } from './composables/storage-options' @@ -117,7 +118,7 @@ registerCommands(() => [ ? [ { id: 'action:eye-dropper', - title: 'Color Picker', + title: i18n.global.t('commands.colorPicker'), icon: 'i-carbon-eyedropper', action: async () => { const { sRGBHex } = await eyeDropper.open() || {} @@ -130,13 +131,13 @@ registerCommands(() => [ ...[ { id: 'action:refresh-data', - title: 'Refresh Data', + title: i18n.global.t('commands.refreshData'), icon: 'i-carbon-data-backup', action: refreshData, }, { id: 'action:reload-page', - title: 'Reload Page', + title: i18n.global.t('commands.reloadPage'), icon: 'i-carbon-reset', action: reloadPage, }, @@ -148,7 +149,7 @@ registerCommands(() => [
- Connecting.... + {{ $t('common.connecting') }}
- Disconnected from Server + {{ $t('connection.disconnected') }}
- Retrying... + {{ $t('connection.retrying') }}
- Reload DevTools Page + {{ $t('connection.reload') }}
diff --git a/packages/devtools/client/composables/frame-nav.ts b/packages/devtools/client/composables/frame-nav.ts index 78a19903a5..7ea3b10fb5 100644 --- a/packages/devtools/client/composables/frame-nav.ts +++ b/packages/devtools/client/composables/frame-nav.ts @@ -2,6 +2,7 @@ import type { ModuleBuiltinTab, ModuleCustomTab } from '~/../src/types' import { computed, watch } from 'vue' import { useRouter } from '#app/composables/router' import { useEnabledTabs } from '~/composables/state-tabs' +import { i18n } from '~/i18n' // `devframe:frame-nav` — the embedded-app half of the shared-iframe soft-nav // protocol (devframe#128 / vitejs/devtools#464). The Vite DevTools dock owns @@ -31,9 +32,8 @@ interface FrameNavTab { } /** The DevTools settings page, surfaced as its own dock member. */ -const SETTINGS_TAB: FrameNavTab = { +const SETTINGS_TAB: Omit = { id: 'settings', - title: 'Settings', icon: 'carbon:settings', category: '~builtin', // sort last within its sub-category (higher `order` renders earlier) @@ -88,7 +88,10 @@ export function setupFrameNav(): void { path: tabPath(tab), }, })), - SETTINGS_TAB, + { + ...SETTINGS_TAB, + title: i18n.global.t('common.settings'), + }, ]) function currentTabId(): string | undefined { @@ -138,8 +141,9 @@ export function setupFrameNav(): void { // Announce proactively in case the host attached before this shim loaded. announce('ready') - // Re-announce when the conditional tab set changes (show()/settings/etc). - watch(() => manifest.value.map(entry => entry.id).join(','), () => announce('manifest')) + // Re-announce when the conditional tab set changes (show()/settings/etc), + // or when the titles change (the DevTools language was switched). + watch(() => manifest.value.map(entry => `${entry.id}:${entry.title}`).join(','), () => announce('manifest')) // Report in-app navigation so the dock highlight follows. watch(() => router.currentRoute.value.path, () => { diff --git a/packages/devtools/client/composables/locale.ts b/packages/devtools/client/composables/locale.ts new file mode 100644 index 0000000000..e106eca984 --- /dev/null +++ b/packages/devtools/client/composables/locale.ts @@ -0,0 +1,103 @@ +import type { ComputedRef } from 'vue' +import type { DevToolsLocale } from '../i18n/locales' +import { matchLocale } from '@devframes/hub' +import { useLocalStorage, useNavigatorLanguage } from '@vueuse/core' +import { computed, shallowRef, watch } from 'vue' +import { DEFAULT_LOCALE, DEVFRAME_LOCALE_STORAGE_KEY, DEVTOOLS_LOCALES, LOCALE_AUTO } from '../i18n/locales' +import { rpcClient } from './rpc' + +const available = Object.keys(DEVTOOLS_LOCALES) as DevToolsLocale[] + +/** Closest supported DevTools locale for a BCP 47 tag, using Devframe's matching rules. */ +export function matchDevToolsLocale(tag: string | undefined | null): DevToolsLocale | undefined { + return matchLocale(tag ?? undefined, available) as DevToolsLocale | undefined +} + +let preference: ReturnType> | undefined + +/** + * The visitor's language pick, shared with the Devframe hub UI: `'auto'` or a + * locale tag. Backed by the hub's own `localStorage` key, so a change in the + * hub's Settings → Appearance reaches DevTools live (via the `storage` event), + * and a change made here reaches the hub. + */ +export function useLocalePreference() { + preference ??= useLocalStorage(DEVFRAME_LOCALE_STORAGE_KEY, LOCALE_AUTO, { + // Don't write the default: an absent key already means `auto` to the hub. + writeDefaults: false, + listenToStorageChanges: true, + }) + return preference +} + +/** The raw stored pick, read directly so it reflects the moment of the call. */ +function readStoredPreference(): string { + try { + return localStorage.getItem(DEVFRAME_LOCALE_STORAGE_KEY) || LOCALE_AUTO + } + catch { + return LOCALE_AUTO + } +} + +let resolved: ComputedRef | undefined + +/** + * The language DevTools renders in, resolved the same way the Devframe hub UI + * resolves its own: + * + * 1. the visitor's pick in the hub settings (unless `auto`); + * 2. the host's default, from `configs.ui.locale` in the connection meta; + * 3. the browser language; + * 4. English. + */ +export function useDevToolsLocale(): ComputedRef { + if (resolved) + return resolved + + const pick = useLocalePreference() + const { language } = useNavigatorLanguage() + + // `configs.ui.locale` is not a stable host default: the running hub UI + // overwrites it with the language it resolved, and an embedded frame shares + // the hub's connection object (devframe reads it from the parent window). + // Capture it once, on connect, and only while the pick is `auto`: then the + // value is exactly the hub's own auto result (host default, else browser), + // never a leftover explicit pick. The value is not reactive, so later + // writes by the hub are ignored on purpose. + const hostDefault = shallowRef() + watch(rpcClient, (client) => { + if (!client || hostDefault.value) + return + if (readStoredPreference() !== LOCALE_AUTO) + return + const configs = client.connectionMeta?.configs as { ui?: { locale?: string } } | undefined + hostDefault.value = matchDevToolsLocale(configs?.ui?.locale) + }, { immediate: true }) + + resolved = computed(() => { + if (pick.value && pick.value !== LOCALE_AUTO) { + const matched = matchDevToolsLocale(pick.value) + if (matched) + return matched + } + + if (hostDefault.value) + return hostDefault.value + + // Read `language` unconditionally so the `languagechange` event + // re-evaluates this, then prefer the full `navigator.languages` list. + const primary = language.value + const languages = typeof navigator !== 'undefined' && navigator.languages?.length + ? navigator.languages + : [primary] + for (const tag of languages) { + const matched = matchDevToolsLocale(tag) + if (matched) + return matched + } + + return DEFAULT_LOCALE + }) + return resolved +} diff --git a/packages/devtools/client/composables/state-commands.ts b/packages/devtools/client/composables/state-commands.ts index 2b43f81879..9a91deb7ef 100644 --- a/packages/devtools/client/composables/state-commands.ts +++ b/packages/devtools/client/composables/state-commands.ts @@ -2,6 +2,7 @@ import type { MaybeRefOrGetter } from 'vue' import { randomStr } from '@antfu/utils' import { computed, onUnmounted, reactive, toValue } from 'vue' import { useRouter } from '#app/composables/router' +import { i18n } from '../i18n' import { useEnabledTabs } from './state-tabs' export interface CommandItem { @@ -18,10 +19,10 @@ export function useCommands() { const tabs = useEnabledTabs() const router = useRouter() - const fixedCommands: CommandItem[] = [ + const fixedCommands = computed((): CommandItem[] => [ { id: 'fixed:settings', - title: 'Settings', + title: i18n.global.t('common.settings'), icon: 'carbon-settings-adjust', action: () => { router.push('/settings') @@ -29,13 +30,13 @@ export function useCommands() { }, { id: 'fixed:docs', - title: 'Nuxt Documentations', + title: i18n.global.t('commands.nuxtDocs'), icon: 'logos-nuxt-icon', action: () => { return getNuxtDocsCommands() }, }, - ] + ]) const tabCommands = computed(() => tabs.value .map((i): CommandItem => { @@ -58,7 +59,7 @@ export function useCommands() { return computed(() => { return [ - ...fixedCommands, + ...fixedCommands.value, ...tabCommands.value, ...[ ...registeredCommands.values(), diff --git a/packages/devtools/client/composables/state-tabs.ts b/packages/devtools/client/composables/state-tabs.ts index cd43322984..79c1ac42b2 100644 --- a/packages/devtools/client/composables/state-tabs.ts +++ b/packages/devtools/client/composables/state-tabs.ts @@ -2,6 +2,7 @@ import type { ModuleBuiltinTab, ModuleCustomTab, RouteInfo, TabCategory } from ' import { objectPick } from '@antfu/utils' import { computed, toValue } from 'vue' import { useRouter } from '#app/composables/router' +import { i18n } from '../i18n' import { useClientRouter } from './client' import { useCustomTabs, useServerPages } from './state' import { useDevToolsOptions } from './storage-options' @@ -17,11 +18,16 @@ export function useAllTabs() { .filter(route => !route.meta.experimental || (route.meta.experimental && settings.showExperimentalFeatures.value)) .sort((a, b) => (a.meta.order as number || 100) - (b.meta.order as number || 100)) .map((i): ModuleBuiltinTab => { + const name = i.name as string return { - name: i.name as string, + name, path: i.path, defaultOrder: i.meta.order as number | undefined, ...i.meta, + // `meta.title` is the English fallback for routes without a message. + title: i18n.global.te(`tabs.${name}`) + ? i18n.global.t(`tabs.${name}`) + : i.meta.title as string, } }), // { diff --git a/packages/devtools/client/composables/utils.ts b/packages/devtools/client/composables/utils.ts index 8268e17ade..41e2b36d6f 100644 --- a/packages/devtools/client/composables/utils.ts +++ b/packages/devtools/client/composables/utils.ts @@ -158,12 +158,6 @@ export function getComponentRelationships(component: Component, relationships?: } } -export function pluralizeByCount(count: number, singular: string, plural = `${singular}s`) { - return `${count} ${count <= 1 - ? singular - : plural}` -} - export function refreshData() { const client = useClient() const nuxt = useNuxtApp() diff --git a/packages/devtools/client/i18n/index.ts b/packages/devtools/client/i18n/index.ts new file mode 100644 index 0000000000..7843cbb3e3 --- /dev/null +++ b/packages/devtools/client/i18n/index.ts @@ -0,0 +1,65 @@ +import type { DevToolsLocale } from './locales' +import { createI18n } from 'vue-i18n' +import de from '../locales/de.json' +import en from '../locales/en.json' +import es from '../locales/es.json' +import fr from '../locales/fr.json' +import ja from '../locales/ja.json' +import ko from '../locales/ko.json' +import ptBR from '../locales/pt-BR.json' +import ru from '../locales/ru.json' +import zhCN from '../locales/zh-CN.json' +import zhTW from '../locales/zh-TW.json' +import { DEFAULT_LOCALE } from './locales' + +export type MessageSchema = typeof en + +const messages: Record = { + 'en': en, + 'zh-CN': zhCN, + 'zh-TW': zhTW, + 'ja': ja, + 'ko': ko, + 'es': es, + 'fr': fr, + 'de': de, + 'pt-BR': ptBR, + 'ru': ru, +} + +/** + * Russian has three plural forms: `one | few | many` + * (1 страница, 2 страницы, 5 страниц). + */ +function russianPlural(choice: number, choicesLength: number): number { + if (choicesLength < 3) { + return choice === 1 + ? 0 + : 1 + } + const mod10 = choice % 10 + const mod100 = choice % 100 + if (mod10 === 1 && mod100 !== 11) + return 0 + if (mod10 >= 2 && mod10 <= 4 && (mod100 < 10 || mod100 >= 20)) + return 1 + return 2 +} + +/** + * The single i18n instance of the DevTools client. Exported so composables + * running outside a component (tab lists, commands) can translate with + * `i18n.global.t`, which stays reactive to locale changes. + */ +export const i18n = createI18n<[MessageSchema], DevToolsLocale, false>({ + legacy: false, + globalInjection: true, + locale: DEFAULT_LOCALE, + fallbackLocale: DEFAULT_LOCALE, + messages, + pluralRules: { + ru: russianPlural, + }, + missingWarn: false, + fallbackWarn: false, +}) diff --git a/packages/devtools/client/i18n/locales.ts b/packages/devtools/client/i18n/locales.ts new file mode 100644 index 0000000000..66a343ca8b --- /dev/null +++ b/packages/devtools/client/i18n/locales.ts @@ -0,0 +1,32 @@ +/** + * UI languages Nuxt DevTools ships translations for. Mirrors + * `HUB_UI_LOCALES` from `@devframes/hub-ui`, so DevTools speaks every + * language the Devframe hub UI offers. Each key needs a matching + * `client/locales/.json` (checked by `x18n check` and the unit test). + */ +export const DEVTOOLS_LOCALES = { + 'en': 'English', + 'zh-CN': '简体中文', + 'zh-TW': '繁體中文', + 'ja': '日本語', + 'ko': '한국어', + 'es': 'Español', + 'fr': 'Français', + 'de': 'Deutsch', + 'pt-BR': 'Português (Brasil)', + 'ru': 'Русский', +} as const + +export type DevToolsLocale = keyof typeof DEVTOOLS_LOCALES + +export const DEFAULT_LOCALE: DevToolsLocale = 'en' + +/** + * The `localStorage` key the Devframe hub UI keeps the visitor's language + * pick under (Settings → Appearance), `'auto'` or a locale tag. The DevTools + * client is served from the same origin as the hub, so reading and writing + * this key keeps one language setting for the hub and DevTools. + */ +export const DEVFRAME_LOCALE_STORAGE_KEY = 'devframes-locale' + +export const LOCALE_AUTO = 'auto' diff --git a/packages/devtools/client/locales/de.json b/packages/devtools/client/locales/de.json new file mode 100644 index 0000000000..3068fbabeb --- /dev/null +++ b/packages/devtools/client/locales/de.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "Wird geladen...", + "connecting": "Verbindung wird hergestellt...", + "settings": "Einstellungen" + }, + "connection": { + "disconnected": "Verbindung zum Server getrennt", + "retrying": "Neuer Versuch...", + "reload": "DevTools-Seite neu laden" + }, + "tabs": { + "modules-overview": "Übersicht", + "modules-pages": "Seiten", + "modules-components": "Komponenten", + "modules-imports": "Importe", + "modules-modules": "Module", + "modules-plugins": "Plugins", + "modules-payload": "Payload", + "modules-runtime-configs": "Laufzeitkonfiguration", + "modules-hooks": "Hooks", + "modules-timeline": "Zeitleiste", + "modules-error": "Fehler", + "modules-virtual-files": "Virtuelle Dateien", + "modules-analyze-build": "Build-Analyse", + "modules-server-routes": "Server-Routen", + "modules-server-tasks": "Server-Tasks", + "modules-storage": "Speicher", + "modules-debug": "Debug" + }, + "commands": { + "nuxtDocs": "Nuxt-Dokumentation", + "colorPicker": "Farbpipette", + "refreshData": "Daten aktualisieren", + "reloadPage": "Seite neu laden" + }, + "overview": { + "pages": "{n} Seite | {n} Seiten", + "components": "{n} Komponente | {n} Komponenten", + "imports": "{n} Import | {n} Importe", + "modules": "{n} Modul | {n} Module", + "plugins": "{n} Plugin | {n} Plugins", + "ssrToFullLoad": "SSR bis vollständig geladen", + "pageLoad": "Seitenladezeit", + "navigation": "Navigation", + "hmr": "HMR", + "notConnected": "Nicht mit der Client-App verbunden, es werden nur serverseitige Daten angezeigt. Verwende den eingebetteten Modus für alle Funktionen.", + "starOnGitHub": "Auf GitHub mit Stern markieren", + "ideas": "Ideen & Vorschläge", + "roadmap": "Projekt-Roadmap", + "bugReports": "Fehler melden", + "openCommandPalette": "Befehlspalette öffnen", + "toggleDevTools": "DevTools ein-/ausblenden" + }, + "settings": { + "title": "DevTools-Einstellungen", + "appearance": "Darstellung", + "uiScale": "UI-Skalierung", + "scale": { + "tiny": "Winzig", + "small": "Klein", + "normal": "Normal", + "large": "Groß", + "huge": "Riesig" + }, + "language": "Sprache", + "languageAuto": "Automatisch (Browsersprache)", + "languageHint": "Wird mit den Einstellungen des Devframe-Hubs geteilt. Eine Änderung hier ändert auch die Sprache des Hubs.", + "features": "Funktionen", + "showHelpButtons": "Hilfe-Schaltflächen anzeigen", + "openInEditor": "Im Editor öffnen", + "editorAuto": "Automatisch", + "feedback": "Feedback", + "telemetry": "Anonyme Statistiken senden und so helfen, DevTools zu verbessern", + "learnMore": "Mehr erfahren", + "ideas": "Ideen & Vorschläge", + "bugReports": "Fehler melden", + "debug": "Debug", + "reset": "Lokale Einstellungen & Status zurücksetzen", + "resetConfirm": "Möchtest du wirklich alle lokalen Einstellungen und den Status zurücksetzen? Die App wird neu geladen." + } +} diff --git a/packages/devtools/client/locales/en.json b/packages/devtools/client/locales/en.json new file mode 100644 index 0000000000..c54ecbe535 --- /dev/null +++ b/packages/devtools/client/locales/en.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "Loading...", + "connecting": "Connecting...", + "settings": "Settings" + }, + "connection": { + "disconnected": "Disconnected from Server", + "retrying": "Retrying...", + "reload": "Reload DevTools Page" + }, + "tabs": { + "modules-overview": "Overview", + "modules-pages": "Pages", + "modules-components": "Components", + "modules-imports": "Imports", + "modules-modules": "Modules", + "modules-plugins": "Plugins", + "modules-payload": "Payload", + "modules-runtime-configs": "Runtime Configs", + "modules-hooks": "Hooks", + "modules-timeline": "Timeline", + "modules-error": "Error", + "modules-virtual-files": "Virtual Files", + "modules-analyze-build": "Build Analyze", + "modules-server-routes": "Server Routes", + "modules-server-tasks": "Server Tasks", + "modules-storage": "Storage", + "modules-debug": "Debug" + }, + "commands": { + "nuxtDocs": "Nuxt Documentations", + "colorPicker": "Color Picker", + "refreshData": "Refresh Data", + "reloadPage": "Reload Page" + }, + "overview": { + "pages": "{n} page | {n} pages", + "components": "{n} component | {n} components", + "imports": "{n} import | {n} imports", + "modules": "{n} module | {n} modules", + "plugins": "{n} plugin | {n} plugins", + "ssrToFullLoad": "SSR to full load", + "pageLoad": "Page load", + "navigation": "Navigation", + "hmr": "HMR", + "notConnected": "Not connected to the client app, showing server-side data only. Use the embedded mode for full features.", + "starOnGitHub": "Star on GitHub", + "ideas": "Ideas & Suggestions", + "roadmap": "Project Roadmap", + "bugReports": "Bug Reports", + "openCommandPalette": "Open Command Palette", + "toggleDevTools": "Toggle DevTools" + }, + "settings": { + "title": "DevTools Settings", + "appearance": "Appearance", + "uiScale": "UI Scale", + "scale": { + "tiny": "Tiny", + "small": "Small", + "normal": "Normal", + "large": "Large", + "huge": "Huge" + }, + "language": "Language", + "languageAuto": "Auto (browser language)", + "languageHint": "Shared with the Devframe hub settings. A change here also changes the hub language.", + "features": "Features", + "showHelpButtons": "Show help buttons", + "openInEditor": "Open In Editor", + "editorAuto": "Auto", + "feedback": "Feedback", + "telemetry": "Send anonymous statistics, help us improving DevTools", + "learnMore": "Learn more", + "ideas": "Ideas & Suggestions", + "bugReports": "Bug Reports", + "debug": "Debug", + "reset": "Reset Local Settings & State", + "resetConfirm": "Are you sure you to reset all local settings & state? The app will reload." + } +} diff --git a/packages/devtools/client/locales/es.json b/packages/devtools/client/locales/es.json new file mode 100644 index 0000000000..b99f2944e8 --- /dev/null +++ b/packages/devtools/client/locales/es.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "Cargando...", + "connecting": "Conectando...", + "settings": "Ajustes" + }, + "connection": { + "disconnected": "Desconectado del servidor", + "retrying": "Reintentando...", + "reload": "Recargar la página de DevTools" + }, + "tabs": { + "modules-overview": "Resumen", + "modules-pages": "Páginas", + "modules-components": "Componentes", + "modules-imports": "Importaciones", + "modules-modules": "Módulos", + "modules-plugins": "Plugins", + "modules-payload": "Payload", + "modules-runtime-configs": "Configuración de ejecución", + "modules-hooks": "Hooks", + "modules-timeline": "Línea de tiempo", + "modules-error": "Error", + "modules-virtual-files": "Archivos virtuales", + "modules-analyze-build": "Análisis de compilación", + "modules-server-routes": "Rutas del servidor", + "modules-server-tasks": "Tareas del servidor", + "modules-storage": "Almacenamiento", + "modules-debug": "Depuración" + }, + "commands": { + "nuxtDocs": "Documentación de Nuxt", + "colorPicker": "Selector de color", + "refreshData": "Actualizar datos", + "reloadPage": "Recargar página" + }, + "overview": { + "pages": "{n} página | {n} páginas", + "components": "{n} componente | {n} componentes", + "imports": "{n} importación | {n} importaciones", + "modules": "{n} módulo | {n} módulos", + "plugins": "{n} plugin | {n} plugins", + "ssrToFullLoad": "De SSR a carga completa", + "pageLoad": "Carga de página", + "navigation": "Navegación", + "hmr": "HMR", + "notConnected": "No hay conexión con la app cliente; solo se muestran datos del servidor. Usa el modo integrado para tener todas las funciones.", + "starOnGitHub": "Dar estrella en GitHub", + "ideas": "Ideas y sugerencias", + "roadmap": "Hoja de ruta del proyecto", + "bugReports": "Reportar errores", + "openCommandPalette": "Abrir la paleta de comandos", + "toggleDevTools": "Mostrar u ocultar DevTools" + }, + "settings": { + "title": "Ajustes de DevTools", + "appearance": "Apariencia", + "uiScale": "Escala de la interfaz", + "scale": { + "tiny": "Diminuta", + "small": "Pequeña", + "normal": "Normal", + "large": "Grande", + "huge": "Enorme" + }, + "language": "Idioma", + "languageAuto": "Automático (idioma del navegador)", + "languageHint": "Se comparte con los ajustes del hub de Devframe. Un cambio aquí también cambia el idioma del hub.", + "features": "Funciones", + "showHelpButtons": "Mostrar botones de ayuda", + "openInEditor": "Abrir en el editor", + "editorAuto": "Automático", + "feedback": "Comentarios", + "telemetry": "Enviar estadísticas anónimas para ayudarnos a mejorar DevTools", + "learnMore": "Más información", + "ideas": "Ideas y sugerencias", + "bugReports": "Reportar errores", + "debug": "Depuración", + "reset": "Restablecer ajustes y estado locales", + "resetConfirm": "¿Seguro que quieres restablecer todos los ajustes y el estado locales? La app se recargará." + } +} diff --git a/packages/devtools/client/locales/fr.json b/packages/devtools/client/locales/fr.json new file mode 100644 index 0000000000..99178f28a9 --- /dev/null +++ b/packages/devtools/client/locales/fr.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "Chargement...", + "connecting": "Connexion...", + "settings": "Paramètres" + }, + "connection": { + "disconnected": "Déconnecté du serveur", + "retrying": "Nouvelle tentative...", + "reload": "Recharger la page DevTools" + }, + "tabs": { + "modules-overview": "Vue d'ensemble", + "modules-pages": "Pages", + "modules-components": "Composants", + "modules-imports": "Imports", + "modules-modules": "Modules", + "modules-plugins": "Plugins", + "modules-payload": "Payload", + "modules-runtime-configs": "Configuration d'exécution", + "modules-hooks": "Hooks", + "modules-timeline": "Chronologie", + "modules-error": "Erreur", + "modules-virtual-files": "Fichiers virtuels", + "modules-analyze-build": "Analyse du build", + "modules-server-routes": "Routes serveur", + "modules-server-tasks": "Tâches serveur", + "modules-storage": "Stockage", + "modules-debug": "Débogage" + }, + "commands": { + "nuxtDocs": "Documentation Nuxt", + "colorPicker": "Pipette à couleurs", + "refreshData": "Actualiser les données", + "reloadPage": "Recharger la page" + }, + "overview": { + "pages": "{n} page | {n} pages", + "components": "{n} composant | {n} composants", + "imports": "{n} import | {n} imports", + "modules": "{n} module | {n} modules", + "plugins": "{n} plugin | {n} plugins", + "ssrToFullLoad": "Du SSR au chargement complet", + "pageLoad": "Chargement de la page", + "navigation": "Navigation", + "hmr": "HMR", + "notConnected": "Non connecté à l'application cliente : seules les données côté serveur sont affichées. Utilisez le mode intégré pour toutes les fonctionnalités.", + "starOnGitHub": "Ajouter une étoile sur GitHub", + "ideas": "Idées et suggestions", + "roadmap": "Feuille de route du projet", + "bugReports": "Signaler un bug", + "openCommandPalette": "Ouvrir la palette de commandes", + "toggleDevTools": "Afficher ou masquer DevTools" + }, + "settings": { + "title": "Paramètres de DevTools", + "appearance": "Apparence", + "uiScale": "Échelle de l'interface", + "scale": { + "tiny": "Minuscule", + "small": "Petite", + "normal": "Normale", + "large": "Grande", + "huge": "Énorme" + }, + "language": "Langue", + "languageAuto": "Automatique (langue du navigateur)", + "languageHint": "Partagé avec les paramètres du hub Devframe. Une modification ici change aussi la langue du hub.", + "features": "Fonctionnalités", + "showHelpButtons": "Afficher les boutons d'aide", + "openInEditor": "Ouvrir dans l'éditeur", + "editorAuto": "Automatique", + "feedback": "Retours", + "telemetry": "Envoyer des statistiques anonymes pour nous aider à améliorer DevTools", + "learnMore": "En savoir plus", + "ideas": "Idées et suggestions", + "bugReports": "Signaler un bug", + "debug": "Débogage", + "reset": "Réinitialiser les paramètres et l'état locaux", + "resetConfirm": "Voulez-vous vraiment réinitialiser tous les paramètres et l'état locaux ? L'application va se recharger." + } +} diff --git a/packages/devtools/client/locales/ja.json b/packages/devtools/client/locales/ja.json new file mode 100644 index 0000000000..10f938cc80 --- /dev/null +++ b/packages/devtools/client/locales/ja.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "読み込み中...", + "connecting": "接続中...", + "settings": "設定" + }, + "connection": { + "disconnected": "サーバーから切断されました", + "retrying": "再試行中...", + "reload": "DevTools ページを再読み込み" + }, + "tabs": { + "modules-overview": "概要", + "modules-pages": "ページ", + "modules-components": "コンポーネント", + "modules-imports": "インポート", + "modules-modules": "モジュール", + "modules-plugins": "プラグイン", + "modules-payload": "ペイロード", + "modules-runtime-configs": "ランタイム設定", + "modules-hooks": "フック", + "modules-timeline": "タイムライン", + "modules-error": "エラー", + "modules-virtual-files": "仮想ファイル", + "modules-analyze-build": "ビルド分析", + "modules-server-routes": "サーバールート", + "modules-server-tasks": "サーバータスク", + "modules-storage": "ストレージ", + "modules-debug": "デバッグ" + }, + "commands": { + "nuxtDocs": "Nuxt ドキュメント", + "colorPicker": "カラーピッカー", + "refreshData": "データを更新", + "reloadPage": "ページを再読み込み" + }, + "overview": { + "pages": "{n} ページ", + "components": "{n} コンポーネント", + "imports": "{n} インポート", + "modules": "{n} モジュール", + "plugins": "{n} プラグイン", + "ssrToFullLoad": "SSR から完全読み込みまで", + "pageLoad": "ページ読み込み", + "navigation": "ナビゲーション", + "hmr": "HMR", + "notConnected": "クライアントアプリに接続されていません。サーバー側のデータのみを表示しています。すべての機能を使うには埋め込みモードを使用してください。", + "starOnGitHub": "GitHub でスターする", + "ideas": "アイデアと提案", + "roadmap": "プロジェクトのロードマップ", + "bugReports": "バグ報告", + "openCommandPalette": "コマンドパレットを開く", + "toggleDevTools": "DevTools の表示切り替え" + }, + "settings": { + "title": "DevTools 設定", + "appearance": "外観", + "uiScale": "UI の拡大率", + "scale": { + "tiny": "極小", + "small": "小", + "normal": "標準", + "large": "大", + "huge": "特大" + }, + "language": "言語", + "languageAuto": "自動(ブラウザーの言語)", + "languageHint": "Devframe ハブの設定と共有されます。ここで変更するとハブの言語も変わります。", + "features": "機能", + "showHelpButtons": "ヘルプボタンを表示", + "openInEditor": "エディターで開く", + "editorAuto": "自動", + "feedback": "フィードバック", + "telemetry": "匿名の統計情報を送信して DevTools の改善に協力する", + "learnMore": "詳細", + "ideas": "アイデアと提案", + "bugReports": "バグ報告", + "debug": "デバッグ", + "reset": "ローカル設定と状態をリセット", + "resetConfirm": "すべてのローカル設定と状態をリセットしますか?アプリは再読み込みされます。" + } +} diff --git a/packages/devtools/client/locales/ko.json b/packages/devtools/client/locales/ko.json new file mode 100644 index 0000000000..eb1cddd008 --- /dev/null +++ b/packages/devtools/client/locales/ko.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "불러오는 중...", + "connecting": "연결 중...", + "settings": "설정" + }, + "connection": { + "disconnected": "서버와의 연결이 끊어졌습니다", + "retrying": "다시 시도하는 중...", + "reload": "DevTools 페이지 새로고침" + }, + "tabs": { + "modules-overview": "개요", + "modules-pages": "페이지", + "modules-components": "컴포넌트", + "modules-imports": "임포트", + "modules-modules": "모듈", + "modules-plugins": "플러그인", + "modules-payload": "페이로드", + "modules-runtime-configs": "런타임 설정", + "modules-hooks": "훅", + "modules-timeline": "타임라인", + "modules-error": "오류", + "modules-virtual-files": "가상 파일", + "modules-analyze-build": "빌드 분석", + "modules-server-routes": "서버 라우트", + "modules-server-tasks": "서버 작업", + "modules-storage": "스토리지", + "modules-debug": "디버그" + }, + "commands": { + "nuxtDocs": "Nuxt 문서", + "colorPicker": "색상 선택기", + "refreshData": "데이터 새로고침", + "reloadPage": "페이지 새로고침" + }, + "overview": { + "pages": "페이지 {n}개", + "components": "컴포넌트 {n}개", + "imports": "임포트 {n}개", + "modules": "모듈 {n}개", + "plugins": "플러그인 {n}개", + "ssrToFullLoad": "SSR부터 전체 로드까지", + "pageLoad": "페이지 로드", + "navigation": "탐색", + "hmr": "HMR", + "notConnected": "클라이언트 앱에 연결되지 않아 서버 측 데이터만 표시합니다. 모든 기능을 사용하려면 임베디드 모드를 사용하세요.", + "starOnGitHub": "GitHub에서 스타 주기", + "ideas": "아이디어 및 제안", + "roadmap": "프로젝트 로드맵", + "bugReports": "버그 신고", + "openCommandPalette": "명령 팔레트 열기", + "toggleDevTools": "DevTools 전환" + }, + "settings": { + "title": "DevTools 설정", + "appearance": "모양", + "uiScale": "UI 배율", + "scale": { + "tiny": "아주 작게", + "small": "작게", + "normal": "보통", + "large": "크게", + "huge": "아주 크게" + }, + "language": "언어", + "languageAuto": "자동 (브라우저 언어)", + "languageHint": "Devframe 허브 설정과 공유됩니다. 여기서 바꾸면 허브 언어도 바뀝니다.", + "features": "기능", + "showHelpButtons": "도움말 버튼 표시", + "openInEditor": "에디터에서 열기", + "editorAuto": "자동", + "feedback": "피드백", + "telemetry": "익명 통계를 보내 DevTools 개선에 도움 주기", + "learnMore": "자세히 알아보기", + "ideas": "아이디어 및 제안", + "bugReports": "버그 신고", + "debug": "디버그", + "reset": "로컬 설정 및 상태 초기화", + "resetConfirm": "모든 로컬 설정과 상태를 초기화할까요? 앱이 다시 로드됩니다." + } +} diff --git a/packages/devtools/client/locales/pt-BR.json b/packages/devtools/client/locales/pt-BR.json new file mode 100644 index 0000000000..ce222541b3 --- /dev/null +++ b/packages/devtools/client/locales/pt-BR.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "Carregando...", + "connecting": "Conectando...", + "settings": "Configurações" + }, + "connection": { + "disconnected": "Desconectado do servidor", + "retrying": "Tentando novamente...", + "reload": "Recarregar a página do DevTools" + }, + "tabs": { + "modules-overview": "Visão geral", + "modules-pages": "Páginas", + "modules-components": "Componentes", + "modules-imports": "Importações", + "modules-modules": "Módulos", + "modules-plugins": "Plugins", + "modules-payload": "Payload", + "modules-runtime-configs": "Configurações de runtime", + "modules-hooks": "Hooks", + "modules-timeline": "Linha do tempo", + "modules-error": "Erro", + "modules-virtual-files": "Arquivos virtuais", + "modules-analyze-build": "Análise do build", + "modules-server-routes": "Rotas do servidor", + "modules-server-tasks": "Tarefas do servidor", + "modules-storage": "Armazenamento", + "modules-debug": "Depuração" + }, + "commands": { + "nuxtDocs": "Documentação do Nuxt", + "colorPicker": "Seletor de cor", + "refreshData": "Atualizar dados", + "reloadPage": "Recarregar página" + }, + "overview": { + "pages": "{n} página | {n} páginas", + "components": "{n} componente | {n} componentes", + "imports": "{n} importação | {n} importações", + "modules": "{n} módulo | {n} módulos", + "plugins": "{n} plugin | {n} plugins", + "ssrToFullLoad": "Do SSR ao carregamento completo", + "pageLoad": "Carregamento da página", + "navigation": "Navegação", + "hmr": "HMR", + "notConnected": "Sem conexão com o app cliente; mostrando apenas dados do servidor. Use o modo incorporado para ter todos os recursos.", + "starOnGitHub": "Dar estrela no GitHub", + "ideas": "Ideias e sugestões", + "roadmap": "Roadmap do projeto", + "bugReports": "Relatar bugs", + "openCommandPalette": "Abrir a paleta de comandos", + "toggleDevTools": "Mostrar ou ocultar o DevTools" + }, + "settings": { + "title": "Configurações do DevTools", + "appearance": "Aparência", + "uiScale": "Escala da interface", + "scale": { + "tiny": "Minúscula", + "small": "Pequena", + "normal": "Normal", + "large": "Grande", + "huge": "Enorme" + }, + "language": "Idioma", + "languageAuto": "Automático (idioma do navegador)", + "languageHint": "Compartilhado com as configurações do hub Devframe. Uma mudança aqui também muda o idioma do hub.", + "features": "Recursos", + "showHelpButtons": "Mostrar botões de ajuda", + "openInEditor": "Abrir no editor", + "editorAuto": "Automático", + "feedback": "Feedback", + "telemetry": "Enviar estatísticas anônimas para nos ajudar a melhorar o DevTools", + "learnMore": "Saiba mais", + "ideas": "Ideias e sugestões", + "bugReports": "Relatar bugs", + "debug": "Depuração", + "reset": "Redefinir configurações e estado locais", + "resetConfirm": "Tem certeza de que deseja redefinir todas as configurações e o estado locais? O app será recarregado." + } +} diff --git a/packages/devtools/client/locales/ru.json b/packages/devtools/client/locales/ru.json new file mode 100644 index 0000000000..afb6789742 --- /dev/null +++ b/packages/devtools/client/locales/ru.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "Загрузка...", + "connecting": "Подключение...", + "settings": "Настройки" + }, + "connection": { + "disconnected": "Соединение с сервером потеряно", + "retrying": "Повторная попытка...", + "reload": "Перезагрузить страницу DevTools" + }, + "tabs": { + "modules-overview": "Обзор", + "modules-pages": "Страницы", + "modules-components": "Компоненты", + "modules-imports": "Импорты", + "modules-modules": "Модули", + "modules-plugins": "Плагины", + "modules-payload": "Payload", + "modules-runtime-configs": "Конфигурация среды выполнения", + "modules-hooks": "Хуки", + "modules-timeline": "Хронология", + "modules-error": "Ошибка", + "modules-virtual-files": "Виртуальные файлы", + "modules-analyze-build": "Анализ сборки", + "modules-server-routes": "Серверные маршруты", + "modules-server-tasks": "Серверные задачи", + "modules-storage": "Хранилище", + "modules-debug": "Отладка" + }, + "commands": { + "nuxtDocs": "Документация Nuxt", + "colorPicker": "Пипетка", + "refreshData": "Обновить данные", + "reloadPage": "Перезагрузить страницу" + }, + "overview": { + "pages": "{n} страница | {n} страницы | {n} страниц", + "components": "{n} компонент | {n} компонента | {n} компонентов", + "imports": "{n} импорт | {n} импорта | {n} импортов", + "modules": "{n} модуль | {n} модуля | {n} модулей", + "plugins": "{n} плагин | {n} плагина | {n} плагинов", + "ssrToFullLoad": "От SSR до полной загрузки", + "pageLoad": "Загрузка страницы", + "navigation": "Навигация", + "hmr": "HMR", + "notConnected": "Нет подключения к клиентскому приложению, показаны только серверные данные. Используйте встроенный режим для всех функций.", + "starOnGitHub": "Поставить звезду на GitHub", + "ideas": "Идеи и предложения", + "roadmap": "Дорожная карта проекта", + "bugReports": "Сообщить об ошибке", + "openCommandPalette": "Открыть палитру команд", + "toggleDevTools": "Показать или скрыть DevTools" + }, + "settings": { + "title": "Настройки DevTools", + "appearance": "Внешний вид", + "uiScale": "Масштаб интерфейса", + "scale": { + "tiny": "Крошечный", + "small": "Маленький", + "normal": "Обычный", + "large": "Большой", + "huge": "Огромный" + }, + "language": "Язык", + "languageAuto": "Авто (язык браузера)", + "languageHint": "Общая настройка с хабом Devframe. Изменение здесь меняет и язык хаба.", + "features": "Функции", + "showHelpButtons": "Показывать кнопки справки", + "openInEditor": "Открывать в редакторе", + "editorAuto": "Авто", + "feedback": "Обратная связь", + "telemetry": "Отправлять анонимную статистику, чтобы помочь улучшить DevTools", + "learnMore": "Подробнее", + "ideas": "Идеи и предложения", + "bugReports": "Сообщить об ошибке", + "debug": "Отладка", + "reset": "Сбросить локальные настройки и состояние", + "resetConfirm": "Вы уверены, что хотите сбросить все локальные настройки и состояние? Приложение будет перезагружено." + } +} diff --git a/packages/devtools/client/locales/zh-CN.json b/packages/devtools/client/locales/zh-CN.json new file mode 100644 index 0000000000..c8d3d061d7 --- /dev/null +++ b/packages/devtools/client/locales/zh-CN.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "加载中...", + "connecting": "连接中...", + "settings": "设置" + }, + "connection": { + "disconnected": "与服务器的连接已断开", + "retrying": "正在重试...", + "reload": "重新加载 DevTools 页面" + }, + "tabs": { + "modules-overview": "概览", + "modules-pages": "页面", + "modules-components": "组件", + "modules-imports": "导入", + "modules-modules": "模块", + "modules-plugins": "插件", + "modules-payload": "Payload", + "modules-runtime-configs": "运行时配置", + "modules-hooks": "钩子", + "modules-timeline": "时间线", + "modules-error": "错误", + "modules-virtual-files": "虚拟文件", + "modules-analyze-build": "构建分析", + "modules-server-routes": "服务端路由", + "modules-server-tasks": "服务端任务", + "modules-storage": "存储", + "modules-debug": "调试" + }, + "commands": { + "nuxtDocs": "Nuxt 文档", + "colorPicker": "取色器", + "refreshData": "刷新数据", + "reloadPage": "重新加载页面" + }, + "overview": { + "pages": "{n} 个页面", + "components": "{n} 个组件", + "imports": "{n} 个导入", + "modules": "{n} 个模块", + "plugins": "{n} 个插件", + "ssrToFullLoad": "SSR 到完全加载", + "pageLoad": "页面加载", + "navigation": "导航", + "hmr": "HMR", + "notConnected": "未连接到客户端应用,仅显示服务端数据。请使用嵌入模式以获得完整功能。", + "starOnGitHub": "在 GitHub 上点星", + "ideas": "想法与建议", + "roadmap": "项目路线图", + "bugReports": "问题反馈", + "openCommandPalette": "打开命令面板", + "toggleDevTools": "切换 DevTools" + }, + "settings": { + "title": "DevTools 设置", + "appearance": "外观", + "uiScale": "界面缩放", + "scale": { + "tiny": "极小", + "small": "小", + "normal": "正常", + "large": "大", + "huge": "极大" + }, + "language": "语言", + "languageAuto": "自动(浏览器语言)", + "languageHint": "与 Devframe 中心的设置共享。在此处更改也会更改中心的语言。", + "features": "功能", + "showHelpButtons": "显示帮助按钮", + "openInEditor": "在编辑器中打开", + "editorAuto": "自动", + "feedback": "反馈", + "telemetry": "发送匿名统计数据,帮助我们改进 DevTools", + "learnMore": "了解更多", + "ideas": "想法与建议", + "bugReports": "问题反馈", + "debug": "调试", + "reset": "重置本地设置与状态", + "resetConfirm": "确定要重置所有本地设置与状态吗?应用将会重新加载。" + } +} diff --git a/packages/devtools/client/locales/zh-TW.json b/packages/devtools/client/locales/zh-TW.json new file mode 100644 index 0000000000..76c3e5ca6b --- /dev/null +++ b/packages/devtools/client/locales/zh-TW.json @@ -0,0 +1,82 @@ +{ + "common": { + "loading": "載入中...", + "connecting": "連線中...", + "settings": "設定" + }, + "connection": { + "disconnected": "與伺服器的連線已中斷", + "retrying": "正在重試...", + "reload": "重新載入 DevTools 頁面" + }, + "tabs": { + "modules-overview": "概覽", + "modules-pages": "頁面", + "modules-components": "元件", + "modules-imports": "匯入", + "modules-modules": "模組", + "modules-plugins": "外掛", + "modules-payload": "Payload", + "modules-runtime-configs": "執行時設定", + "modules-hooks": "鉤子", + "modules-timeline": "時間軸", + "modules-error": "錯誤", + "modules-virtual-files": "虛擬檔案", + "modules-analyze-build": "建置分析", + "modules-server-routes": "伺服器路由", + "modules-server-tasks": "伺服器任務", + "modules-storage": "儲存", + "modules-debug": "除錯" + }, + "commands": { + "nuxtDocs": "Nuxt 文件", + "colorPicker": "取色器", + "refreshData": "重新整理資料", + "reloadPage": "重新載入頁面" + }, + "overview": { + "pages": "{n} 個頁面", + "components": "{n} 個元件", + "imports": "{n} 個匯入", + "modules": "{n} 個模組", + "plugins": "{n} 個外掛", + "ssrToFullLoad": "SSR 到完全載入", + "pageLoad": "頁面載入", + "navigation": "導覽", + "hmr": "HMR", + "notConnected": "未連線到用戶端應用程式,僅顯示伺服器端資料。請使用嵌入模式以取得完整功能。", + "starOnGitHub": "在 GitHub 上給星", + "ideas": "想法與建議", + "roadmap": "專案路線圖", + "bugReports": "問題回報", + "openCommandPalette": "開啟命令面板", + "toggleDevTools": "切換 DevTools" + }, + "settings": { + "title": "DevTools 設定", + "appearance": "外觀", + "uiScale": "介面縮放", + "scale": { + "tiny": "極小", + "small": "小", + "normal": "正常", + "large": "大", + "huge": "極大" + }, + "language": "語言", + "languageAuto": "自動(瀏覽器語言)", + "languageHint": "與 Devframe 中心的設定共用。在此處變更也會變更中心的語言。", + "features": "功能", + "showHelpButtons": "顯示說明按鈕", + "openInEditor": "在編輯器中開啟", + "editorAuto": "自動", + "feedback": "意見回饋", + "telemetry": "傳送匿名統計資料,協助我們改進 DevTools", + "learnMore": "了解更多", + "ideas": "想法與建議", + "bugReports": "問題回報", + "debug": "除錯", + "reset": "重設本機設定與狀態", + "resetConfirm": "確定要重設所有本機設定與狀態嗎?應用程式將重新載入。" + } +} diff --git a/packages/devtools/client/nuxt.config.ts b/packages/devtools/client/nuxt.config.ts index 08c3f37879..3b90781c68 100644 --- a/packages/devtools/client/nuxt.config.ts +++ b/packages/devtools/client/nuxt.config.ts @@ -113,6 +113,13 @@ export default defineNuxtConfig({ vite: { warmupEntry: false, + // vue-i18n feature flags: Composition API only, no legacy API or + // intlify devtools in the bundle. + define: { + __VUE_I18N_FULL_INSTALL__: true, + __VUE_I18N_LEGACY_API__: false, + __INTLIFY_PROD_DEVTOOLS__: false, + }, vue: { // floating-vue still implements its poppers with Options API mixins, // computed properties, and methods. Keep that runtime enabled even when @@ -156,6 +163,7 @@ export default defineNuxtConfig({ 'devframe/utils/hash', 'perfect-debounce', 'scule', + 'vue-i18n', 'vue-virtual-scroller', 'vis-data', 'vis-network', diff --git a/packages/devtools/client/pages/modules/overview.vue b/packages/devtools/client/pages/modules/overview.vue index a9430d6730..8a804c439e 100644 --- a/packages/devtools/client/pages/modules/overview.vue +++ b/packages/devtools/client/pages/modules/overview.vue @@ -9,7 +9,7 @@ import { useAllRoutes } from '~/composables/state-tabs' import { getIsMacOS } from '~/composables/utils' import { version } from '../../../package.json' import { showConnectionWarning } from '../../composables/client' -import { formatDuration, pluralizeByCount } from '../../composables/utils' +import { formatDuration } from '../../composables/utils' definePageMeta({ icon: 'carbon-information', @@ -32,7 +32,7 @@ const metricsLoading = computed(() => client.value?.metrics.loading())