From be540697315a1692fe2a9c4fa97a2a1a729f72eb Mon Sep 17 00:00:00 2001 From: Tiny Date: Mon, 16 Feb 2026 13:58:46 +0800 Subject: [PATCH] feat: add i18n infrastructure with Simplified Chinese (zh-CN) translation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Set up react-i18next with browser language detection - Add English (en) and Simplified Chinese (zh-CN) locale files covering ~271 translation keys across 17 modules - Create LanguageSwitcher component (globe icon dropdown in titlebar) - Convert 4 key components as examples: - CustomTitlebar: tooltip labels, dropdown menu items - ProjectList: all user-facing strings - Topbar: status indicator, version display, install prompts - Settings: header, tab labels, all form labels/descriptions, toast messages, permission rules section - Remaining components can be converted incrementally using the same useTranslation() + t("key") pattern Translation conventions: - Agent → 智能体 (not 代理) - Checkpoint → 检查点 - Token → kept as-is (industry standard) - Claude Code / opcode → kept as-is (brand names) - Chinese punctuation used throughout --- package-lock.json | 718 +++++++++++++++++++++++++--- package.json | 3 + src/components/CustomTitlebar.tsx | 18 +- src/components/LanguageSwitcher.tsx | 62 +++ src/components/ProjectList.tsx | 24 +- src/components/Settings.tsx | 101 ++-- src/components/Topbar.tsx | 16 +- src/i18n/index.ts | 26 + src/i18n/locales/en.json | 136 ++++++ src/i18n/locales/zh-CN.json | 136 ++++++ src/main.tsx | 1 + 11 files changed, 1112 insertions(+), 129 deletions(-) create mode 100644 src/components/LanguageSwitcher.tsx create mode 100644 src/i18n/index.ts create mode 100644 src/i18n/locales/en.json create mode 100644 src/i18n/locales/zh-CN.json diff --git a/package-lock.json b/package-lock.json index b408ecec9..b614291cb 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "opcode", - "version": "0.1.0", + "version": "0.2.1", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "opcode", - "version": "0.1.0", + "version": "0.2.1", "license": "AGPL-3.0", "dependencies": { "@hookform/resolvers": "^3.9.1", @@ -38,10 +38,14 @@ "diff": "^8.0.2", "framer-motion": "^12.0.0-alpha.1", "html2canvas": "^1.4.1", + "i18next": "^25.8.8", + "i18next-browser-languagedetector": "^8.2.1", "lucide-react": "^0.468.0", + "posthog-js": "^1.258.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.54.2", + "react-i18next": "^16.5.4", "react-markdown": "^9.0.3", "react-syntax-highlighter": "^15.6.1", "recharts": "^2.14.1", @@ -52,7 +56,7 @@ "zustand": "^5.0.6" }, "devDependencies": { - "@tauri-apps/cli": "^2", + "@tauri-apps/cli": "^2.7.1", "@types/node": "^22.15.30", "@types/react": "^18.3.1", "@types/react-dom": "^18.3.1", @@ -111,6 +115,7 @@ "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", @@ -315,9 +320,9 @@ } }, "node_modules/@babel/runtime": { - "version": "7.27.6", - "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.27.6.tgz", - "integrity": "sha512-vbavdySgbTTrmFE+EsiqUTzlOr5bzlnJtUv9PynGCAKvfQqjIXbvFdumPM/GxMDfyuGMJaJAU6TO4zc1Jf1i8Q==", + "version": "7.28.6", + "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.6.tgz", + "integrity": "sha512-05WQkdpL9COIMz4LjTxGpPNCdlpyimKppYNoJ5Di5EUObifl8t4tuLuUBBZEpoLYOmfvIWrsp9fCl0HoPRVTdA==", "license": "MIT", "engines": { "node": ">=6.9.0" @@ -1331,6 +1336,253 @@ "@jridgewell/sourcemap-codec": "^1.4.14" } }, + "node_modules/@opentelemetry/api": { + "version": "1.9.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api/-/api-1.9.0.tgz", + "integrity": "sha512-3giAOQvZiH5F9bMlMiv8+GSPMeqg0dbaeo58/0SlA9sxSqZhnUtxzX9/2FzyhS9sWQf5S0GJE0AKBrFqjpeYcg==", + "license": "Apache-2.0", + "peer": true, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/api-logs": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/api-logs/-/api-logs-0.208.0.tgz", + "integrity": "sha512-CjruKY9V6NMssL/T1kAFgzosF1v9o6oeN+aX5JB/C/xPNtmgIJqcXHG7fA82Ou1zCpWGl4lROQUKwUNE1pMCyg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api": "^1.3.0" + }, + "engines": { + "node": ">=8.0.0" + } + }, + "node_modules/@opentelemetry/core": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.2.0.tgz", + "integrity": "sha512-FuabnnUm8LflnieVxs6eP7Z383hgQU4W1e3KJS6aOG3RxWxcHyBxH8fDMHNgu/gFx/M2jvTOW/4/PHhLz6bjWw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/exporter-logs-otlp-http": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/exporter-logs-otlp-http/-/exporter-logs-otlp-http-0.208.0.tgz", + "integrity": "sha512-jOv40Bs9jy9bZVLo/i8FwUiuCvbjWDI+ZW13wimJm4LjnlwJxGgB+N/VWOZUTpM+ah/awXeQqKdNlpLf2EjvYg==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.208.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-exporter-base": "0.208.0", + "@opentelemetry/otlp-transformer": "0.208.0", + "@opentelemetry/sdk-logs": "0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-exporter-base": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-exporter-base/-/otlp-exporter-base-0.208.0.tgz", + "integrity": "sha512-gMd39gIfVb2OgxldxUtOwGJYSH8P1kVFFlJLuut32L6KgUC4gl1dMhn+YC2mGn0bDOiQYSk/uHOdSjuKp58vvA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/otlp-transformer": "0.208.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/otlp-transformer/-/otlp-transformer-0.208.0.tgz", + "integrity": "sha512-DCFPY8C6lAQHUNkzcNT9R+qYExvsk6C5Bto2pbNxgicpcSWbe2WHShLxkOxIdNcBiYPdVHv/e7vH7K6TI+C+fQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.208.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/sdk-logs": "0.208.0", + "@opentelemetry/sdk-metrics": "2.2.0", + "@opentelemetry/sdk-trace-base": "2.2.0", + "protobufjs": "^7.3.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": "^1.3.0" + } + }, + "node_modules/@opentelemetry/otlp-transformer/node_modules/@opentelemetry/resources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", + "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.5.1.tgz", + "integrity": "sha512-BViBCdE/GuXRlp9k7nS1w6wJvY5fnFX5XvuEtWsTAOQFIO89Eru7lGW3WbfbxtCuZ/GbrJfAziXG0w0dpxL7eQ==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.5.1", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/resources/node_modules/@opentelemetry/core": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/@opentelemetry/core/-/core-2.5.1.tgz", + "integrity": "sha512-Dwlc+3HAZqpgTYq0MUyZABjFkcrKTePwuiFVLjahGD8cx3enqihmpAmdgNFO1R4m/sIe5afjJrA25Prqy4NXlA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.0.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs": { + "version": "0.208.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-logs/-/sdk-logs-0.208.0.tgz", + "integrity": "sha512-QlAyL1jRpOeaqx7/leG1vJMp84g0xKP6gJmfELBpnI4O/9xPX+Hu5m1POk9Kl+veNkyth5t19hRlN6tNY1sjbA==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/api-logs": "0.208.0", + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.4.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-logs/node_modules/@opentelemetry/resources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", + "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-metrics/-/sdk-metrics-2.2.0.tgz", + "integrity": "sha512-G5KYP6+VJMZzpGipQw7Giif48h6SGQ2PFKEYCybeXJsOCB4fp8azqMAAzE5lnnHK3ZVwYQrgmFbsUJO/zOnwGw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.9.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-metrics/node_modules/@opentelemetry/resources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", + "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/sdk-trace-base/-/sdk-trace-base-2.2.0.tgz", + "integrity": "sha512-xWQgL0Bmctsalg6PaXExmzdedSp3gyKV8mQBwK/j9VGdCDu2fmXIb2gAehBKbkXCpJ4HPkgv3QfoJWRT4dHWbw==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/resources": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/sdk-trace-base/node_modules/@opentelemetry/resources": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/resources/-/resources-2.2.0.tgz", + "integrity": "sha512-1pNQf/JazQTMA0BiO5NINUzH0cbLbbl7mntLa4aJNmCCXSj0q03T5ZXXL0zw4G55TjdL9Tz32cznGClf+8zr5A==", + "license": "Apache-2.0", + "dependencies": { + "@opentelemetry/core": "2.2.0", + "@opentelemetry/semantic-conventions": "^1.29.0" + }, + "engines": { + "node": "^18.19.0 || >=20.6.0" + }, + "peerDependencies": { + "@opentelemetry/api": ">=1.3.0 <1.10.0" + } + }, + "node_modules/@opentelemetry/semantic-conventions": { + "version": "1.39.0", + "resolved": "https://registry.npmjs.org/@opentelemetry/semantic-conventions/-/semantic-conventions-1.39.0.tgz", + "integrity": "sha512-R5R9tb2AXs2IRLNKLBJDynhkfmx7mX0vi8NkhZb3gUkPWHn6HXk5J8iQ/dql0U3ApfWym4kXXmBDRGO+oeOfjg==", + "license": "Apache-2.0", + "engines": { + "node": ">=14" + } + }, "node_modules/@parcel/watcher": { "version": "2.5.1", "resolved": "https://registry.npmjs.org/@parcel/watcher/-/watcher-2.5.1.tgz", @@ -1626,6 +1878,85 @@ "url": "https://opencollective.com/parcel" } }, + "node_modules/@posthog/core": { + "version": "1.22.0", + "resolved": "https://registry.npmjs.org/@posthog/core/-/core-1.22.0.tgz", + "integrity": "sha512-WkmOnq95aAOu6yk6r5LWr5cfXsQdpVbWDCwOxQwxSne8YV6GuZET1ziO5toSQXgrgbdcjrSz2/GopAfiL6iiAA==", + "license": "MIT", + "dependencies": { + "cross-spawn": "^7.0.6" + } + }, + "node_modules/@posthog/types": { + "version": "1.347.2", + "resolved": "https://registry.npmjs.org/@posthog/types/-/types-1.347.2.tgz", + "integrity": "sha512-aT+r/7jXOzPmUHO6sutoWzczPcYIZyhmWt1f1OvY4zKC7Pwp/ZsJWKFTxjV02p0PZz96AE83eLTe7w7b6tjhIw==", + "license": "MIT" + }, + "node_modules/@protobufjs/aspromise": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/aspromise/-/aspromise-1.1.2.tgz", + "integrity": "sha512-j+gKExEuLmKwvz3OgROXtrJ2UG2x8Ch2YZUxahh+s1F2HZ+wAceUNLkvy6zKCPVRkU++ZWQrdxsUeQXmcg4uoQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/base64": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/base64/-/base64-1.1.2.tgz", + "integrity": "sha512-AZkcAA5vnN/v4PDqKyMR5lx7hZttPDgClv83E//FMNhR2TMcLUhfRUBHCmSl0oi9zMgDDqRUJkSxO3wm85+XLg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/codegen": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/@protobufjs/codegen/-/codegen-2.0.4.tgz", + "integrity": "sha512-YyFaikqM5sH0ziFZCN3xDC7zeGaB/d0IUb9CATugHWbd1FRFwWwt4ld4OYMPWu5a3Xe01mGAULCdqhMlPl29Jg==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/eventemitter": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/eventemitter/-/eventemitter-1.1.0.tgz", + "integrity": "sha512-j9ednRT81vYJ9OfVuXG6ERSTdEL1xVsNgqpkxMsbIabzSo3goCjDIveeGv5d03om39ML71RdmrGNjG5SReBP/Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/fetch": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/fetch/-/fetch-1.1.0.tgz", + "integrity": "sha512-lljVXpqXebpsijW71PZaCYeIcE5on1w5DlQy5WH6GLbFryLUrBD4932W/E2BSpfRJWseIL4v/KPgBFxDOIdKpQ==", + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.1", + "@protobufjs/inquire": "^1.1.0" + } + }, + "node_modules/@protobufjs/float": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@protobufjs/float/-/float-1.0.2.tgz", + "integrity": "sha512-Ddb+kVXlXst9d+R9PfTIxh1EdNkgoRe5tOX6t01f1lYWOvJnSPDBlG241QLzcyPdoNTsblLUdujGSE4RzrTZGQ==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/inquire": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/inquire/-/inquire-1.1.0.tgz", + "integrity": "sha512-kdSefcPdruJiFMVSbn801t4vFK7KB/5gd2fYvrxhuJYg8ILrmn9SKSX2tZdV6V+ksulWqS7aXjBcRXl3wHoD9Q==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/path": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/@protobufjs/path/-/path-1.1.2.tgz", + "integrity": "sha512-6JOcJ5Tm08dOHAbdR3GrvP+yUUfkjG5ePsHYczMFLq3ZmMkAD98cDgcT2iA1lJ9NVwFd4tH/iSSoe44YWkltEA==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/pool": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/pool/-/pool-1.1.0.tgz", + "integrity": "sha512-0kELaGSIDBKvcgS4zkjz1PeddatrjYcmMWOlAuAPwAeccUrPHdUqo/J6LiymHHEiJT5NrF1UVwxY14f+fy4WQw==", + "license": "BSD-3-Clause" + }, + "node_modules/@protobufjs/utf8": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/@protobufjs/utf8/-/utf8-1.1.0.tgz", + "integrity": "sha512-Vvn3zZrhQZkkBE8LSuW3em98c0FwgO4nxzv6OdSxPKJIEKY2bGbHn+mhGIPerzI4twdxaP8/0+06HBpwf345Lw==", + "license": "BSD-3-Clause" + }, "node_modules/@radix-ui/number": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@radix-ui/number/-/number-1.1.1.tgz", @@ -3095,9 +3426,9 @@ } }, "node_modules/@tauri-apps/cli": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.6.2.tgz", - "integrity": "sha512-s1/eyBHxk0wG1blLeOY2IDjgZcxVrkxU5HFL8rNDwjYGr0o7yr3RAtwmuUPhz13NO+xGAL1bJZaLFBdp+5joKg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli/-/cli-2.10.0.tgz", + "integrity": "sha512-ZwT0T+7bw4+DPCSWzmviwq5XbXlM0cNoleDKOYPFYqcZqeKY31KlpoMW/MOON/tOFBPgi31a2v3w9gliqwL2+Q==", "dev": true, "license": "Apache-2.0 OR MIT", "bin": { @@ -3111,23 +3442,23 @@ "url": "https://opencollective.com/tauri" }, "optionalDependencies": { - "@tauri-apps/cli-darwin-arm64": "2.6.2", - "@tauri-apps/cli-darwin-x64": "2.6.2", - "@tauri-apps/cli-linux-arm-gnueabihf": "2.6.2", - "@tauri-apps/cli-linux-arm64-gnu": "2.6.2", - "@tauri-apps/cli-linux-arm64-musl": "2.6.2", - "@tauri-apps/cli-linux-riscv64-gnu": "2.6.2", - "@tauri-apps/cli-linux-x64-gnu": "2.6.2", - "@tauri-apps/cli-linux-x64-musl": "2.6.2", - "@tauri-apps/cli-win32-arm64-msvc": "2.6.2", - "@tauri-apps/cli-win32-ia32-msvc": "2.6.2", - "@tauri-apps/cli-win32-x64-msvc": "2.6.2" + "@tauri-apps/cli-darwin-arm64": "2.10.0", + "@tauri-apps/cli-darwin-x64": "2.10.0", + "@tauri-apps/cli-linux-arm-gnueabihf": "2.10.0", + "@tauri-apps/cli-linux-arm64-gnu": "2.10.0", + "@tauri-apps/cli-linux-arm64-musl": "2.10.0", + "@tauri-apps/cli-linux-riscv64-gnu": "2.10.0", + "@tauri-apps/cli-linux-x64-gnu": "2.10.0", + "@tauri-apps/cli-linux-x64-musl": "2.10.0", + "@tauri-apps/cli-win32-arm64-msvc": "2.10.0", + "@tauri-apps/cli-win32-ia32-msvc": "2.10.0", + "@tauri-apps/cli-win32-x64-msvc": "2.10.0" } }, "node_modules/@tauri-apps/cli-darwin-arm64": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.6.2.tgz", - "integrity": "sha512-YlvT+Yb7u2HplyN2Cf/nBplCQARC/I4uedlYHlgtxg6rV7xbo9BvG1jLOo29IFhqA2rOp5w1LtgvVGwsOf2kxw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-arm64/-/cli-darwin-arm64-2.10.0.tgz", + "integrity": "sha512-avqHD4HRjrMamE/7R/kzJPcAJnZs0IIS+1nkDP5b+TNBn3py7N2aIo9LIpy+VQq0AkN8G5dDpZtOOBkmWt/zjA==", "cpu": [ "arm64" ], @@ -3142,9 +3473,9 @@ } }, "node_modules/@tauri-apps/cli-darwin-x64": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.6.2.tgz", - "integrity": "sha512-21gdPWfv1bP8rkTdCL44in70QcYcPaDM70L+y78N8TkBuC+/+wqnHcwwjzb+mUyck6UoEw2DORagSI/oKKUGJw==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-darwin-x64/-/cli-darwin-x64-2.10.0.tgz", + "integrity": "sha512-keDmlvJRStzVFjZTd0xYkBONLtgBC9eMTpmXnBXzsHuawV2q9PvDo2x6D5mhuoMVrJ9QWjgaPKBBCFks4dK71Q==", "cpu": [ "x64" ], @@ -3159,9 +3490,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm-gnueabihf": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.6.2.tgz", - "integrity": "sha512-MW8Y6HqHS5yzQkwGoLk/ZyE1tWpnz/seDoY4INsbvUZdknuUf80yn3H+s6eGKtT/0Bfqon/W9sY7pEkgHRPQgA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm-gnueabihf/-/cli-linux-arm-gnueabihf-2.10.0.tgz", + "integrity": "sha512-e5u0VfLZsMAC9iHaOEANumgl6lfnJx0Dtjkd8IJpysZ8jp0tJ6wrIkto2OzQgzcYyRCKgX72aKE0PFgZputA8g==", "cpu": [ "arm" ], @@ -3176,9 +3507,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-gnu": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.6.2.tgz", - "integrity": "sha512-9PdINTUtnyrnQt9hvC4y1m0NoxKSw/wUB9OTBAQabPj8WLAdvySWiUpEiqJjwLhlu4T6ltXZRpNTEzous3/RXg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-gnu/-/cli-linux-arm64-gnu-2.10.0.tgz", + "integrity": "sha512-YrYYk2dfmBs5m+OIMCrb+JH/oo+4FtlpcrTCgiFYc7vcs6m3QDd1TTyWu0u01ewsCtK2kOdluhr/zKku+KP7HA==", "cpu": [ "arm64" ], @@ -3193,9 +3524,9 @@ } }, "node_modules/@tauri-apps/cli-linux-arm64-musl": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.6.2.tgz", - "integrity": "sha512-LrcJTRr7FrtQlTDkYaRXIGo/8YU/xkWmBPC646WwKNZ/S6yqCiDcOMoPe7Cx4ZvcG6sK6LUCLQMfaSNEL7PT0A==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.10.0.tgz", + "integrity": "sha512-GUoPdVJmrJRIXFfW3Rkt+eGK9ygOdyISACZfC/bCSfOnGt8kNdQIQr5WRH9QUaTVFIwxMlQyV3m+yXYP+xhSVA==", "cpu": [ "arm64" ], @@ -3210,9 +3541,9 @@ } }, "node_modules/@tauri-apps/cli-linux-riscv64-gnu": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.6.2.tgz", - "integrity": "sha512-GnTshO/BaZ9KGIazz2EiFfXGWgLur5/pjqklRA/ck42PGdUQJhV/Ao7A7TdXPjqAzpFxNo6M/Hx0GCH2iMS7IA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-riscv64-gnu/-/cli-linux-riscv64-gnu-2.10.0.tgz", + "integrity": "sha512-JO7s3TlSxshwsoKNCDkyvsx5gw2QAs/Y2GbR5UE2d5kkU138ATKoPOtxn8G1fFT1aDW4LH0rYAAfBpGkDyJJnw==", "cpu": [ "riscv64" ], @@ -3227,9 +3558,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-gnu": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.6.2.tgz", - "integrity": "sha512-QDG3WeJD6UJekmrtVPCJRzlKgn9sGzhvD58oAw5gIU+DRovgmmG2U1jH9fS361oYGjWWO7d/KM9t0kugZzi4lQ==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-gnu/-/cli-linux-x64-gnu-2.10.0.tgz", + "integrity": "sha512-Uvh4SUUp4A6DVRSMWjelww0GnZI3PlVy7VS+DRF5napKuIehVjGl9XD0uKoCoxwAQBLctvipyEK+pDXpJeoHng==", "cpu": [ "x64" ], @@ -3244,9 +3575,9 @@ } }, "node_modules/@tauri-apps/cli-linux-x64-musl": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.6.2.tgz", - "integrity": "sha512-TNVTDDtnWzuVqWBFdZ4+8ZTg17tc21v+CT5XBQ+KYCoYtCrIaHpW04fS5Tmudi+vYdBwoPDfwpKEB6LhCeFraQ==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-linux-x64-musl/-/cli-linux-x64-musl-2.10.0.tgz", + "integrity": "sha512-AP0KRK6bJuTpQ8kMNWvhIpKUkQJfcPFeba7QshOQZjJ8wOS6emwTN4K5g/d3AbCMo0RRdnZWwu67MlmtJyxC1Q==", "cpu": [ "x64" ], @@ -3261,9 +3592,9 @@ } }, "node_modules/@tauri-apps/cli-win32-arm64-msvc": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.6.2.tgz", - "integrity": "sha512-z77C1oa/hMLO/jM1JF39tK3M3v9nou7RsBnQoOY54z5WPcpVAbS0XdFhXB7sSN72BOiO3moDky9lQANQz6L3CA==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-arm64-msvc/-/cli-win32-arm64-msvc-2.10.0.tgz", + "integrity": "sha512-97DXVU3dJystrq7W41IX+82JEorLNY+3+ECYxvXWqkq7DBN6FsA08x/EFGE8N/b0LTOui9X2dvpGGoeZKKV08g==", "cpu": [ "arm64" ], @@ -3278,9 +3609,9 @@ } }, "node_modules/@tauri-apps/cli-win32-ia32-msvc": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.6.2.tgz", - "integrity": "sha512-TmD8BbzbjluBw8+QEIWUVmFa9aAluSkT1N937n1mpYLXcPbTpbunqRFiIznTwupoJNJIdtpF/t7BdZDRh5rrcg==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-ia32-msvc/-/cli-win32-ia32-msvc-2.10.0.tgz", + "integrity": "sha512-EHyQ1iwrWy1CwMalEm9z2a6L5isQ121pe7FcA2xe4VWMJp+GHSDDGvbTv/OPdkt2Lyr7DAZBpZHM6nvlHXEc4A==", "cpu": [ "ia32" ], @@ -3295,9 +3626,9 @@ } }, "node_modules/@tauri-apps/cli-win32-x64-msvc": { - "version": "2.6.2", - "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.6.2.tgz", - "integrity": "sha512-ItB8RCKk+nCmqOxOvbNtltz6x1A4QX6cSM21kj3NkpcnjT9rHSMcfyf8WVI2fkoMUJR80iqCblUX6ARxC3lj6w==", + "version": "2.10.0", + "resolved": "https://registry.npmjs.org/@tauri-apps/cli-win32-x64-msvc/-/cli-win32-x64-msvc-2.10.0.tgz", + "integrity": "sha512-NTpyQxkpzGmU6ceWBTY2xRIEaS0ZLbVx1HE1zTA3TY/pV3+cPoPPOs+7YScr4IMzXMtOw7tLw5LEXo5oIG3qaQ==", "cpu": [ "x64" ], @@ -3517,7 +3848,6 @@ "version": "22.16.4", "resolved": "https://registry.npmjs.org/@types/node/-/node-22.16.4.tgz", "integrity": "sha512-PYRhNtZdm2wH/NT2k/oAJ6/f2VD2N2Dag0lGlx2vWgMSJXGNmlce5MiTQzoWAiIJtso30mjnfQCOKVH+kAQC/g==", - "devOptional": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" @@ -3540,6 +3870,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-18.3.23.tgz", "integrity": "sha512-/LDXMQh55EzZQ0uVAZmKKhfENivEvWz6E+EYzh+/MCjMhNsotd+ZHhBGIjFDTi6+fz0OhQQQLbTgdQIxxCsC0w==", "license": "MIT", + "peer": true, "dependencies": { "@types/prop-types": "*", "csstype": "^3.0.2" @@ -3551,6 +3882,7 @@ "integrity": "sha512-MEe3UeoENYVFXzoXEWsvcpg6ZvlrFNlOQ7EOsvhI3CfAXwzPfO8Qwuxd40nepsYKqyyVQnTdEfv68q91yLcKrQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^18.0.0" } @@ -3575,6 +3907,13 @@ "sharp": "*" } }, + "node_modules/@types/trusted-types": { + "version": "2.0.7", + "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", + "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", + "license": "MIT", + "optional": true + }, "node_modules/@types/unist": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/unist/-/unist-3.0.3.tgz", @@ -3887,6 +4226,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", @@ -4063,6 +4403,31 @@ "dev": true, "license": "MIT" }, + "node_modules/core-js": { + "version": "3.48.0", + "resolved": "https://registry.npmjs.org/core-js/-/core-js-3.48.0.tgz", + "integrity": "sha512-zpEHTy1fjTMZCKLHUZoVeylt9XrzaIN2rbPXEt0k+q7JE5CkCZdo6bNq55bn24a69CH7ErAVLKijxJja4fw+UQ==", + "hasInstallScript": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/core-js" + } + }, + "node_modules/cross-spawn": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", + "license": "MIT", + "dependencies": { + "path-key": "^3.1.0", + "shebang-command": "^2.0.0", + "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/css-line-break": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/css-line-break/-/css-line-break-2.1.0.tgz", @@ -4333,6 +4698,15 @@ "csstype": "^3.0.2" } }, + "node_modules/dompurify": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/dompurify/-/dompurify-3.3.1.tgz", + "integrity": "sha512-qkdCKzLNtrgPFP1Vo+98FRzJnBRGe4ffyCea9IwHB1fyxPOeNTHpLKYGd4Uk9xvNoH0ZoOjwZxNptyMwqrId1Q==", + "license": "(MPL-2.0 OR Apache-2.0)", + "optionalDependencies": { + "@types/trusted-types": "^2.0.7" + } + }, "node_modules/electron-to-chromium": { "version": "1.5.186", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.186.tgz", @@ -4469,6 +4843,12 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/fflate": { + "version": "0.4.8", + "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.4.8.tgz", + "integrity": "sha512-FJqqoDBR00Mdj9ppamLa/Y7vxm+PRmNWA67N846RvsoYVMKB4q3y/de5PA7gUmRMYK/8CMz2GDZQmCRN1wBcWA==", + "license": "MIT" + }, "node_modules/fill-range": { "version": "7.1.1", "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", @@ -4915,6 +5295,15 @@ "integrity": "sha512-PDEfEF102G23vHmPhLyPboFCD+BkMGu+GuJe2d9/eH4FsCwvgBpnc9n0pGE+ffKdph38s6foEZiEjdgHdzp+IA==", "license": "CC0-1.0" }, + "node_modules/html-parse-stringify": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/html-parse-stringify/-/html-parse-stringify-3.0.1.tgz", + "integrity": "sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==", + "license": "MIT", + "dependencies": { + "void-elements": "3.1.0" + } + }, "node_modules/html-url-attributes": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/html-url-attributes/-/html-url-attributes-3.0.1.tgz", @@ -4948,6 +5337,47 @@ "node": ">=8.0.0" } }, + "node_modules/i18next": { + "version": "25.8.8", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.8.8.tgz", + "integrity": "sha512-gNTWXMBe9JBr6LAl2tqRfa6fn2EjrQJ3JBeH2jR+yIckwaJYdI7UfMQrnxzFjuFBb2FHy9Yn4gJB2BwLuC8/ZQ==", + "funding": [ + { + "type": "individual", + "url": "https://locize.com" + }, + { + "type": "individual", + "url": "https://locize.com/i18next.html" + }, + { + "type": "individual", + "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project" + } + ], + "license": "MIT", + "peer": true, + "dependencies": { + "@babel/runtime": "^7.28.4" + }, + "peerDependencies": { + "typescript": "^5" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/i18next-browser-languagedetector": { + "version": "8.2.1", + "resolved": "https://registry.npmjs.org/i18next-browser-languagedetector/-/i18next-browser-languagedetector-8.2.1.tgz", + "integrity": "sha512-bZg8+4bdmaOiApD7N7BPT9W8MLZG+nPTOFlLiJiT8uzKXFjhxw4v2ierCXOwB5sFDMtuA5G4kgYZ0AznZxQ/cw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.23.2" + } + }, "node_modules/inline-style-parser": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/inline-style-parser/-/inline-style-parser-0.2.4.tgz", @@ -5056,6 +5486,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/isexe": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", + "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", + "license": "ISC" + }, "node_modules/jiti": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz", @@ -5340,6 +5776,12 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "license": "MIT" }, + "node_modules/long": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/long/-/long-5.3.2.tgz", + "integrity": "sha512-mNAgZ1GmyNhD7AuqnTG3/VQ26o760+ZYBPKjPvugO8+nLbYfX6TVpJPseBvopbdY+qpZ/lKUnmEc1LeZYS3QAA==", + "license": "Apache-2.0" + }, "node_modules/longest-streak": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/longest-streak/-/longest-streak-3.1.0.tgz", @@ -6433,6 +6875,15 @@ "url": "https://github.com/fb55/entities?sponsor=1" } }, + "node_modules/path-key": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", + "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/picocolors": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", @@ -6479,6 +6930,37 @@ "node": "^10 || ^12 || >=14" } }, + "node_modules/posthog-js": { + "version": "1.347.2", + "resolved": "https://registry.npmjs.org/posthog-js/-/posthog-js-1.347.2.tgz", + "integrity": "sha512-hDbsSU30gfNhC11cBYSPpwUYB4DglbCN2G8W8NPIR/KXhT03shmuxabra/uaoI4blkr8SSSpxwvYV4gGa3hXrA==", + "license": "SEE LICENSE IN LICENSE", + "dependencies": { + "@opentelemetry/api": "^1.9.0", + "@opentelemetry/api-logs": "^0.208.0", + "@opentelemetry/exporter-logs-otlp-http": "^0.208.0", + "@opentelemetry/resources": "^2.2.0", + "@opentelemetry/sdk-logs": "^0.208.0", + "@posthog/core": "1.22.0", + "@posthog/types": "1.347.2", + "core-js": "^3.38.1", + "dompurify": "^3.3.1", + "fflate": "^0.4.8", + "preact": "^10.28.2", + "query-selector-shadow-dom": "^1.0.1", + "web-vitals": "^5.1.0" + } + }, + "node_modules/preact": { + "version": "10.28.3", + "resolved": "https://registry.npmjs.org/preact/-/preact-10.28.3.tgz", + "integrity": "sha512-tCmoRkPQLpBeWzpmbhryairGnhW9tKV6c6gr/w+RhoRoKEJwsjzipwp//1oCpGPOchvSLaAPlpcJi9MwMmoPyA==", + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/preact" + } + }, "node_modules/prismjs": { "version": "1.30.0", "resolved": "https://registry.npmjs.org/prismjs/-/prismjs-1.30.0.tgz", @@ -6515,11 +6997,42 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/protobufjs": { + "version": "7.5.4", + "resolved": "https://registry.npmjs.org/protobufjs/-/protobufjs-7.5.4.tgz", + "integrity": "sha512-CvexbZtbov6jW2eXAvLukXjXUW1TzFaivC46BpWc/3BpcCysb5Vffu+B3XHMm8lVEuy2Mm4XGex8hBSg1yapPg==", + "hasInstallScript": true, + "license": "BSD-3-Clause", + "dependencies": { + "@protobufjs/aspromise": "^1.1.2", + "@protobufjs/base64": "^1.1.2", + "@protobufjs/codegen": "^2.0.4", + "@protobufjs/eventemitter": "^1.1.0", + "@protobufjs/fetch": "^1.1.0", + "@protobufjs/float": "^1.0.2", + "@protobufjs/inquire": "^1.1.0", + "@protobufjs/path": "^1.1.2", + "@protobufjs/pool": "^1.1.0", + "@protobufjs/utf8": "^1.1.0", + "@types/node": ">=13.7.0", + "long": "^5.0.0" + }, + "engines": { + "node": ">=12.0.0" + } + }, + "node_modules/query-selector-shadow-dom": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/query-selector-shadow-dom/-/query-selector-shadow-dom-1.0.1.tgz", + "integrity": "sha512-lT5yCqEBgfoMYpf3F2xQRK7zEr1rhIIZuceDK6+xRkJQ4NMbHTwXqk4NkwDwQMNqXgG9r9fyHnzwNVs6zV5KRw==", + "license": "MIT" + }, "node_modules/react": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react/-/react-18.3.1.tgz", "integrity": "sha512-wS+hAgJShR0KhEvPJArfuPVN1+Hz1t0Y6n5jLrGQbkb4urgPE/0Rve+1kMB1v/oWgHgm4WIcV+i7F2pTVj+2iQ==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0" }, @@ -6532,6 +7045,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-18.3.1.tgz", "integrity": "sha512-5m4nQKp+rZRb09LNH59GM4BxTh9251/ylbKIbpe7TpGxfJ+9kv6BLkLBXIjjspbgbnIBNqlI23tRnTWT0snUIw==", "license": "MIT", + "peer": true, "dependencies": { "loose-envify": "^1.1.0", "scheduler": "^0.23.2" @@ -6545,6 +7059,7 @@ "resolved": "https://registry.npmjs.org/react-hook-form/-/react-hook-form-7.60.0.tgz", "integrity": "sha512-SBrYOvMbDB7cV8ZfNpaiLcgjH/a1c7aK0lK+aNigpf4xWLO8q+o4tcvVurv3c4EOyzn/3dCsYt4GKD42VvJ/+A==", "license": "MIT", + "peer": true, "engines": { "node": ">=18.0.0" }, @@ -6556,6 +7071,33 @@ "react": "^16.8.0 || ^17 || ^18 || ^19" } }, + "node_modules/react-i18next": { + "version": "16.5.4", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.4.tgz", + "integrity": "sha512-6yj+dcfMncEC21QPhOTsW8mOSO+pzFmT6uvU7XXdvM/Cp38zJkmTeMeKmTrmCMD5ToT79FmiE/mRWiYWcJYW4g==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.28.4", + "html-parse-stringify": "^3.0.1", + "use-sync-external-store": "^1.6.0" + }, + "peerDependencies": { + "i18next": ">= 25.6.2", + "react": ">= 16.8.0", + "typescript": "^5" + }, + "peerDependenciesMeta": { + "react-dom": { + "optional": true + }, + "react-native": { + "optional": true + }, + "typescript": { + "optional": true + } + } + }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", @@ -7300,6 +7842,27 @@ "node": ">=10" } }, + "node_modules/shebang-command": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", + "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", + "license": "MIT", + "dependencies": { + "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/shebang-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", + "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/simple-swizzle": { "version": "0.2.2", "resolved": "https://registry.npmjs.org/simple-swizzle/-/simple-swizzle-0.2.2.tgz", @@ -7462,6 +8025,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -7511,8 +8075,9 @@ "version": "5.6.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.3.tgz", "integrity": "sha512-hjcS1mhfuyi4WW8IWtjP7brDrG2cuDZukyrYrSauoXGNgx0S7zceP07adYkJycEr56BOUTNPzbInooiN3fn1qw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -7525,7 +8090,6 @@ "version": "6.21.0", "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", - "devOptional": true, "license": "MIT" }, "node_modules/unified": { @@ -7700,6 +8264,16 @@ } } }, + "node_modules/use-sync-external-store": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.6.0.tgz", + "integrity": "sha512-Pp6GSwGP/NrPIrxVFAIkOQeyw8lFenOHijQWkUTrDvrF4ALqylP2C/KCkeS9dpUM3KvYRQhna5vt7IL95+ZQ9w==", + "license": "MIT", + "peer": true, + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/utrie": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/utrie/-/utrie-1.0.2.tgz", @@ -7778,6 +8352,7 @@ "resolved": "https://registry.npmjs.org/vite/-/vite-6.3.5.tgz", "integrity": "sha512-cZn6NDFE7wdTpINgs++ZJ4N49W2vRp8LCKrn3Ob1kYNtOo21vfDoaV5GzBfLU4MovSAB8uNRm4jgzVQZ+mBzPQ==", "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.25.0", "fdir": "^6.4.4", @@ -7866,6 +8441,7 @@ "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-4.0.3.tgz", "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -7873,6 +8449,15 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, + "node_modules/void-elements": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/void-elements/-/void-elements-3.1.0.tgz", + "integrity": "sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==", + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/web-namespaces": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/web-namespaces/-/web-namespaces-2.0.1.tgz", @@ -7883,6 +8468,27 @@ "url": "https://github.com/sponsors/wooorm" } }, + "node_modules/web-vitals": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/web-vitals/-/web-vitals-5.1.0.tgz", + "integrity": "sha512-ArI3kx5jI0atlTtmV0fWU3fjpLmq/nD3Zr1iFFlJLaqa5wLBkUSzINwBPySCX/8jRyjlmy1Volw1kz1g9XE4Jg==", + "license": "Apache-2.0" + }, + "node_modules/which": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", + "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", + "license": "ISC", + "dependencies": { + "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" + } + }, "node_modules/xtend": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/xtend/-/xtend-4.0.2.tgz", diff --git a/package.json b/package.json index c072d265a..2edbcb202 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,14 @@ "diff": "^8.0.2", "framer-motion": "^12.0.0-alpha.1", "html2canvas": "^1.4.1", + "i18next": "^25.8.8", + "i18next-browser-languagedetector": "^8.2.1", "lucide-react": "^0.468.0", "posthog-js": "^1.258.3", "react": "^18.3.1", "react-dom": "^18.3.1", "react-hook-form": "^7.54.2", + "react-i18next": "^16.5.4", "react-markdown": "^9.0.3", "react-syntax-highlighter": "^15.6.1", "recharts": "^2.14.1", diff --git a/src/components/CustomTitlebar.tsx b/src/components/CustomTitlebar.tsx index 3342959b0..764369aa6 100644 --- a/src/components/CustomTitlebar.tsx +++ b/src/components/CustomTitlebar.tsx @@ -3,6 +3,8 @@ import { motion } from 'framer-motion'; import { Settings, Minus, Square, X, Bot, BarChart3, FileText, Network, Info, MoreVertical } from 'lucide-react'; import { getCurrentWindow } from '@tauri-apps/api/window'; import { TooltipProvider, TooltipSimple } from '@/components/ui/tooltip-modern'; +import { useTranslation } from 'react-i18next'; +import { LanguageSwitcher } from './LanguageSwitcher'; interface CustomTitlebarProps { onSettingsClick?: () => void; @@ -21,6 +23,7 @@ export const CustomTitlebar: React.FC = ({ onMCPClick, onInfoClick }) => { + const { t } = useTranslation(); const [isHovered, setIsHovered] = useState(false); const [isDropdownOpen, setIsDropdownOpen] = useState(false); const dropdownRef = useRef(null); @@ -140,7 +143,7 @@ export const CustomTitlebar: React.FC = ({ {/* Primary actions group */}
{onAgentsClick && ( - + = ({ )} {onUsageClick && ( - + = ({ {/* Secondary actions group */}
{onSettingsClick && ( - + = ({ )} + {/* Language Switcher */} + + {/* Dropdown menu for additional options */}
- + setIsDropdownOpen(!isDropdownOpen)} whileTap={{ scale: 0.97 }} @@ -222,7 +228,7 @@ export const CustomTitlebar: React.FC = ({ className="w-full px-4 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-3" > - MCP Servers + {t("titlebar.mcpServers")} )} @@ -235,7 +241,7 @@ export const CustomTitlebar: React.FC = ({ className="w-full px-4 py-2 text-left text-sm hover:bg-accent hover:text-accent-foreground transition-colors flex items-center gap-3" > - About + {t("titlebar.about")} )}
diff --git a/src/components/LanguageSwitcher.tsx b/src/components/LanguageSwitcher.tsx new file mode 100644 index 000000000..b64f7152d --- /dev/null +++ b/src/components/LanguageSwitcher.tsx @@ -0,0 +1,62 @@ +import React from "react"; +import { useTranslation } from "react-i18next"; +import { Globe } from "lucide-react"; + +const LANGUAGES = [ + { code: "en", label: "English" }, + { code: "zh-CN", label: "简体中文" }, +] as const; + +interface LanguageSwitcherProps { + className?: string; +} + +export const LanguageSwitcher: React.FC = ({ className }) => { + const { i18n, t } = useTranslation(); + const [open, setOpen] = React.useState(false); + const ref = React.useRef(null); + + React.useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (ref.current && !ref.current.contains(event.target as Node)) { + setOpen(false); + } + }; + document.addEventListener("mousedown", handleClickOutside); + return () => document.removeEventListener("mousedown", handleClickOutside); + }, []); + + const handleChange = (code: string) => { + i18n.changeLanguage(code); + setOpen(false); + }; + + return ( +
+ + {open && ( +
+
+ {LANGUAGES.map((lang) => ( + + ))} +
+
+ )} +
+ ); +}; diff --git a/src/components/ProjectList.tsx b/src/components/ProjectList.tsx index 8d8ab7995..cfb0a309d 100644 --- a/src/components/ProjectList.tsx +++ b/src/components/ProjectList.tsx @@ -1,6 +1,6 @@ import React, { useState } from "react"; import { motion } from "framer-motion"; -import { +import { FolderOpen, ChevronLeft, ChevronRight @@ -9,6 +9,7 @@ import { Button } from "@/components/ui/button"; import { Card } from "@/components/ui/card"; import type { Project } from "@/lib/api"; import { cn } from "@/lib/utils"; +import { useTranslation } from "react-i18next"; interface ProjectListProps { /** @@ -89,6 +90,7 @@ export const ProjectList: React.FC = ({ onOpenProject, className, }) => { + const { t } = useTranslation(); const [showAll, setShowAll] = useState(false); const [currentPage, setCurrentPage] = useState(1); @@ -118,9 +120,9 @@ export const ProjectList: React.FC = ({
-

Projects

+

{t("projects.title")}

- Select a project to start working with Claude Code + {t("projects.subtitle")}

= ({ className="flex items-center gap-2" > - Open Project + {t("projects.openProject")}
@@ -145,20 +147,20 @@ export const ProjectList: React.FC = ({ {displayedProjects.length > 0 ? (
-

Recent Projects

+

{t("projects.recentProjects")}

{!showAll ? ( ) : ( - )}
@@ -245,9 +247,9 @@ export const ProjectList: React.FC = ({
-

No recent projects

+

{t("projects.noProjects")}

- Open a project to get started with Claude Code + {t("projects.noProjectsDesc")}

= ({ className="flex items-center gap-2" > - Open Your First Project + {t("projects.openFirstProject")}
diff --git a/src/components/Settings.tsx b/src/components/Settings.tsx index 06d338a0c..6bf735de5 100644 --- a/src/components/Settings.tsx +++ b/src/components/Settings.tsx @@ -30,6 +30,7 @@ import { ProxySettings } from "./ProxySettings"; import { useTheme, useTrackEvent } from "@/hooks"; import { analytics } from "@/lib/analytics"; import { TabPersistenceService } from "@/services/tabPersistence"; +import { useTranslation } from "react-i18next"; interface SettingsProps { /** @@ -96,6 +97,8 @@ export const Settings: React.FC = ({ const [tabPersistenceEnabled, setTabPersistenceEnabled] = useState(true); // Startup intro preference const [startupIntroEnabled, setStartupIntroEnabled] = useState(true); + + const { t } = useTranslation(); // Load settings on mount useEffect(() => { @@ -183,7 +186,7 @@ export const Settings: React.FC = ({ } } catch (err) { console.error("Failed to load settings:", err); - setError("Failed to load settings. Please ensure ~/.claude directory exists."); + setError(t("settings.failedLoad")); setSettings({}); } finally { setLoading(false); @@ -237,11 +240,11 @@ export const Settings: React.FC = ({ setProxySettingsChanged(false); } - setToast({ message: "Settings saved successfully!", type: "success" }); + setToast({ message: t("settings.saveSuccess"), type: "success" }); } catch (err) { console.error("Failed to save settings:", err); - setError("Failed to save settings."); - setToast({ message: "Failed to save settings", type: "error" }); + setError(t("settings.failedSave")); + setToast({ message: t("settings.failedSave"), type: "error" }); } finally { setSaving(false); } @@ -339,9 +342,9 @@ export const Settings: React.FC = ({
-

Settings

+

{t("settings.title")}

- Configure Claude Code preferences + {t("settings.subtitle")}

= ({ {saving ? ( <> - Saving... + {t("common.saving")} ) : ( <> - Save Settings + {t("settings.saveSettings")} )} @@ -394,29 +397,29 @@ export const Settings: React.FC = ({
- General - Permissions - Environment - Advanced - Hooks - Commands - Storage - Proxy + {t("settings.general")} + {t("settings.permissions")} + {t("settings.environment")} + {t("settings.advanced")} + {t("settings.hooks")} + {t("settings.commands")} + {t("settings.storage")} + {t("settings.proxy")} {/* General Settings */}
-

General Settings

+

{t("settings.generalSettings")}

{/* Theme Selector */}
- +

- Choose your preferred color theme + {t("settings.themeDesc")}

@@ -430,43 +433,43 @@ export const Settings: React.FC = ({ )} > {theme === 'dark' && } - Dark + {t("settings.dark")}
@@ -601,9 +604,9 @@ export const Settings: React.FC = ({ {/* Include Co-authored By */}
- +

- Add Claude attribution to git commits and pull requests + {t("settings.coAuthoredDesc")}

= ({ {/* Verbose Output */}
- +

- Show full bash and command outputs + {t("settings.verboseOutputDesc")}

= ({
- +

- How long to retain chat transcripts locally (default: 30 days) + {t("settings.chatRetentionDesc")}

= ({ {/* Analytics Toggle */}
- +

- Help improve opcode by sharing anonymous usage data + {t("settings.enableAnalyticsDesc")}

= ({ await analytics.enable(); setAnalyticsEnabled(true); trackEvent.settingsChanged('analytics_enabled', true); - setToast({ message: "Analytics enabled", type: "success" }); + setToast({ message: t("settings.analyticsEnabled"), type: "success" }); } else { await analytics.disable(); setAnalyticsEnabled(false); trackEvent.settingsChanged('analytics_enabled', false); - setToast({ message: "Analytics disabled", type: "success" }); + setToast({ message: t("settings.analyticsDisabled"), type: "success" }); } }} /> @@ -717,9 +720,9 @@ export const Settings: React.FC = ({ {/* Tab Persistence Toggle */}
- +

- Restore your tabs when you restart the app + {t("settings.rememberTabsDesc")}

= ({ {/* Startup Intro Toggle */}
- +

- Display a brief welcome animation when the app launches + {t("settings.showWelcomeDesc")}

= ({
-

Permission Rules

+

{t("settings.permissionRules")}

- Control which tools Claude Code can use without manual approval + {t("settings.permissionDesc")}

{/* Allow Rules */}
- +
{allowRules.length === 0 ? (

- No allow rules configured. Claude will ask for approval for all tools. + {t("settings.noAllowRules")}

) : ( allowRules.map((rule) => ( @@ -834,7 +837,7 @@ export const Settings: React.FC = ({ {/* Deny Rules */}
- +
@@ -125,7 +127,7 @@ export const Topbar: React.FC = ({ trigger={statusContent} content={
-

Claude Code not found

+

{t("topbar.notFound")}

                   {versionStatus.output}
@@ -137,7 +139,7 @@ export const Topbar: React.FC = ({
                 className="w-full"
                 onClick={onSettingsClick}
               >
-                Select Claude Installation
+                {t("topbar.selectInstallation")}
               
                = ({
                 rel="noopener noreferrer"
                 className="flex items-center space-x-1 text-xs text-primary hover:underline"
               >
-                Install Claude Code
+                {t("topbar.installClaudeCode")}
                 
               
             
diff --git a/src/i18n/index.ts b/src/i18n/index.ts new file mode 100644 index 000000000..7f3218d1e --- /dev/null +++ b/src/i18n/index.ts @@ -0,0 +1,26 @@ +import i18n from "i18next"; +import { initReactI18next } from "react-i18next"; +import LanguageDetector from "i18next-browser-languagedetector"; + +import en from "./locales/en.json"; +import zhCN from "./locales/zh-CN.json"; + +i18n + .use(LanguageDetector) + .use(initReactI18next) + .init({ + resources: { + en: { translation: en }, + "zh-CN": { translation: zhCN }, + }, + fallbackLng: "en", + interpolation: { + escapeValue: false, + }, + detection: { + order: ["localStorage", "navigator"], + caches: ["localStorage"], + }, + }); + +export default i18n; diff --git a/src/i18n/locales/en.json b/src/i18n/locales/en.json new file mode 100644 index 000000000..cd144db8b --- /dev/null +++ b/src/i18n/locales/en.json @@ -0,0 +1,136 @@ +{ + "common": { + "save": "Save", + "saving": "Saving...", + "cancel": "Cancel", + "delete": "Delete", + "deleting": "Deleting...", + "edit": "Edit", + "create": "Create", + "back": "Back", + "close": "Close", + "search": "Search", + "loading": "Loading...", + "retry": "Try Again", + "confirm": "Confirm", + "import": "Import", + "export": "Export", + "add": "Add", + "remove": "Remove", + "copy": "Copy", + "preview": "Preview", + "custom": "Custom", + "examples": "Examples:", + "unsavedChanges": "You have unsaved changes. Are you sure you want to leave?", + "unsavedIndicator": "Unsaved changes" + }, + "titlebar": { + "agents": "Agents", + "usageDashboard": "Usage Dashboard", + "settings": "Settings", + "moreOptions": "More options", + "mcpServers": "MCP Servers", + "about": "About", + "language": "Language" + }, + "topbar": { + "checking": "Checking...", + "notFound": "Claude Code not found", + "selectInstallation": "Select Claude Installation", + "installClaudeCode": "Install Claude Code", + "claudeCode": "Claude Code", + "version": "Claude Code v{{version}}", + "failedCheckVersion": "Failed to check version" + }, + "projects": { + "title": "Projects", + "subtitle": "Select a project to start working with Claude Code", + "openProject": "Open Project", + "recentProjects": "Recent Projects", + "viewAll": "View all ({{count}})", + "viewLess": "View less", + "noProjects": "No recent projects", + "noProjectsDesc": "Open a project to get started with Claude Code", + "openFirstProject": "Open Your First Project" + }, + "settings": { + "title": "Settings", + "subtitle": "Configure Claude Code preferences", + "saveSettings": "Save Settings", + "generalSettings": "General Settings", + "general": "General", + "permissions": "Permissions", + "environment": "Environment", + "advanced": "Advanced", + "hooks": "Hooks", + "commands": "Commands", + "storage": "Storage", + "proxy": "Proxy", + "theme": "Theme", + "themeDesc": "Choose your preferred color theme", + "dark": "Dark", + "gray": "Gray", + "light": "Light", + "customThemeColors": "Custom Theme Colors", + "background": "Background", + "foreground": "Foreground", + "primary": "Primary", + "card": "Card", + "accent": "Accent", + "destructive": "Destructive", + "cssColorHelp": "Use CSS color values (hex, rgb, oklch, etc.). Changes apply immediately.", + "coAuthored": "Include \"Co-authored by Claude\"", + "coAuthoredDesc": "Add Claude attribution to git commits and pull requests", + "verboseOutput": "Verbose Output", + "verboseOutputDesc": "Show full bash and command outputs", + "chatRetention": "Chat Transcript Retention (days)", + "chatRetentionDesc": "How long to retain chat transcripts locally (default: 30 days)", + "changesApplyOnSave": "Changes will be applied when you save settings.", + "enableAnalytics": "Enable Analytics", + "enableAnalyticsDesc": "Help improve opcode by sharing anonymous usage data", + "analyticsEnabled": "Analytics enabled", + "analyticsDisabled": "Analytics disabled", + "privacyProtected": "Your privacy is protected", + "privacyNoPersonal": "No personal information or file contents collected", + "privacyAnonymous": "All data is anonymous with random IDs", + "privacyDisable": "You can disable analytics at any time", + "rememberTabs": "Remember Open Tabs", + "rememberTabsDesc": "Restore your tabs when you restart the app", + "tabPersistenceEnabled": "Tab persistence enabled - your tabs will be restored on restart", + "tabPersistenceDisabled": "Tab persistence disabled - tabs will not be saved", + "showWelcome": "Show Welcome Intro on Startup", + "showWelcomeDesc": "Display a brief welcome animation when the app launches", + "welcomeEnabled": "Welcome intro enabled", + "welcomeDisabled": "Welcome intro disabled", + "failedUpdatePref": "Failed to update preference", + "permissionRules": "Permission Rules", + "permissionDesc": "Control which tools Claude Code can use without manual approval", + "allowRules": "Allow Rules", + "denyRules": "Deny Rules", + "noAllowRules": "No allow rules configured. Claude will ask for approval for all tools.", + "noDenyRules": "No deny rules configured.", + "addRule": "Add Rule", + "exBashAll": "Allow all bash commands", + "exBashExact": "Allow exact command", + "exBashPrefix": "Allow commands with prefix", + "exReadFile": "Allow reading specific file", + "exEditDir": "Allow editing files in docs directory", + "envVars": "Environment Variables", + "envVarsDesc": "Environment variables applied to every Claude Code session", + "addVariable": "Add Variable", + "noEnvVars": "No environment variables configured.", + "commonVars": "Common variables:", + "advancedSettings": "Advanced Settings", + "advancedDesc": "Additional configuration options for advanced users", + "apiKeyHelper": "API Key Helper Script", + "apiKeyHelperDesc": "Custom script to generate auth values for API requests", + "rawSettings": "Raw Settings (JSON)", + "rawSettingsDesc": "This shows the raw JSON that will be saved to ~/.claude/settings.json", + "userHooks": "User Hooks", + "userHooksDesc": "Configure hooks that apply to all Claude Code sessions for your user account.", + "storedIn": "These are stored in", + "failedLoad": "Failed to load settings. Please ensure ~/.claude directory exists.", + "failedSave": "Failed to save settings.", + "saveSuccess": "Settings saved successfully!" + } +} diff --git a/src/i18n/locales/zh-CN.json b/src/i18n/locales/zh-CN.json new file mode 100644 index 000000000..1ecca52cd --- /dev/null +++ b/src/i18n/locales/zh-CN.json @@ -0,0 +1,136 @@ +{ + "common": { + "save": "保存", + "saving": "保存中...", + "cancel": "取消", + "delete": "删除", + "deleting": "删除中...", + "edit": "编辑", + "create": "创建", + "back": "返回", + "close": "关闭", + "search": "搜索", + "loading": "加载中...", + "retry": "重试", + "confirm": "确认", + "import": "导入", + "export": "导出", + "add": "添加", + "remove": "移除", + "copy": "复制", + "preview": "预览", + "custom": "自定义", + "examples": "示例:", + "unsavedChanges": "有未保存的更改,确定要离开吗?", + "unsavedIndicator": "有未保存的更改" + }, + "titlebar": { + "agents": "智能体", + "usageDashboard": "用量仪表盘", + "settings": "设置", + "moreOptions": "更多选项", + "mcpServers": "MCP 服务器", + "about": "关于", + "language": "语言" + }, + "topbar": { + "checking": "检测中...", + "notFound": "未找到 Claude Code", + "selectInstallation": "选择 Claude 安装路径", + "installClaudeCode": "安装 Claude Code", + "claudeCode": "Claude Code", + "version": "Claude Code v{{version}}", + "failedCheckVersion": "版本检测失败" + }, + "projects": { + "title": "项目", + "subtitle": "选择一个项目以开始使用 Claude Code", + "openProject": "打开项目", + "recentProjects": "最近的项目", + "viewAll": "查看全部 ({{count}})", + "viewLess": "收起", + "noProjects": "暂无最近项目", + "noProjectsDesc": "打开一个项目以开始使用 Claude Code", + "openFirstProject": "打开你的第一个项目" + }, + "settings": { + "title": "设置", + "subtitle": "配置 Claude Code 偏好设置", + "saveSettings": "保存设置", + "generalSettings": "常规设置", + "general": "常规", + "permissions": "权限", + "environment": "环境变量", + "advanced": "高级", + "hooks": "钩子", + "commands": "命令", + "storage": "存储", + "proxy": "代理", + "theme": "主题", + "themeDesc": "选择你偏好的颜色主题", + "dark": "深色", + "gray": "灰色", + "light": "浅色", + "customThemeColors": "自定义主题颜色", + "background": "背景色", + "foreground": "前景色", + "primary": "主色调", + "card": "卡片色", + "accent": "强调色", + "destructive": "危险色", + "cssColorHelp": "使用 CSS 颜色值(hex、rgb、oklch 等),更改即时生效。", + "coAuthored": "包含「Co-authored by Claude」署名", + "coAuthoredDesc": "在 Git 提交和 Pull Request 中添加 Claude 署名", + "verboseOutput": "详细输出", + "verboseOutputDesc": "显示完整的 Bash 和命令输出", + "chatRetention": "聊天记录保留天数", + "chatRetentionDesc": "本地聊天记录保留时长(默认:30 天)", + "changesApplyOnSave": "更改将在保存设置时生效。", + "enableAnalytics": "启用数据分析", + "enableAnalyticsDesc": "分享匿名使用数据以帮助改进 opcode", + "analyticsEnabled": "数据分析已启用", + "analyticsDisabled": "数据分析已禁用", + "privacyProtected": "你的隐私受到保护", + "privacyNoPersonal": "不收集任何个人信息或文件内容", + "privacyAnonymous": "所有数据均匿名,使用随机 ID", + "privacyDisable": "你可以随时禁用数据分析", + "rememberTabs": "记住打开的标签页", + "rememberTabsDesc": "重启应用时恢复标签页", + "tabPersistenceEnabled": "标签页持久化已启用 - 重启时将恢复标签页", + "tabPersistenceDisabled": "标签页持久化已禁用 - 标签页不会被保存", + "showWelcome": "启动时显示欢迎动画", + "showWelcomeDesc": "应用启动时显示简短的欢迎动画", + "welcomeEnabled": "欢迎动画已启用", + "welcomeDisabled": "欢迎动画已禁用", + "failedUpdatePref": "更新偏好设置失败", + "permissionRules": "权限规则", + "permissionDesc": "控制 Claude Code 无需手动批准即可使用的工具", + "allowRules": "允许规则", + "denyRules": "拒绝规则", + "noAllowRules": "未配置允许规则。Claude 将对所有工具请求你的批准。", + "noDenyRules": "未配置拒绝规则。", + "addRule": "添加规则", + "exBashAll": "允许所有 Bash 命令", + "exBashExact": "允许精确命令", + "exBashPrefix": "允许带前缀的命令", + "exReadFile": "允许读取特定文件", + "exEditDir": "允许编辑 docs 目录下的文件", + "envVars": "环境变量", + "envVarsDesc": "应用于每个 Claude Code 会话的环境变量", + "addVariable": "添加变量", + "noEnvVars": "未配置环境变量。", + "commonVars": "常用变量:", + "advancedSettings": "高级设置", + "advancedDesc": "面向高级用户的额外配置选项", + "apiKeyHelper": "API Key 辅助脚本", + "apiKeyHelperDesc": "自定义脚本,用于为 API 请求生成认证值", + "rawSettings": "原始设置(JSON)", + "rawSettingsDesc": "此处显示将保存到 ~/.claude/settings.json 的原始 JSON", + "userHooks": "用户钩子", + "userHooksDesc": "配置适用于所有 Claude Code 会话的用户级钩子。", + "storedIn": "存储位置:", + "failedLoad": "加载设置失败,请确保 ~/.claude 目录存在。", + "failedSave": "保存设置失败。", + "saveSuccess": "设置保存成功!" + } +} diff --git a/src/main.tsx b/src/main.tsx index 2b7b92c6c..da2d4e39d 100644 --- a/src/main.tsx +++ b/src/main.tsx @@ -1,5 +1,6 @@ import React from "react"; import ReactDOM from "react-dom/client"; +import "./i18n"; import App from "./App"; import { ErrorBoundary } from "./components/ErrorBoundary"; import { AnalyticsErrorBoundary } from "./components/AnalyticsErrorBoundary";