From f843811292dc8d13da9d4c3d1cd3c8ae88dbf422 Mon Sep 17 00:00:00 2001 From: Mitsu <50143759+Mitsu13Ion@users.noreply.github.com> Date: Wed, 24 Dec 2025 17:37:31 +0100 Subject: [PATCH] feat: add i18n internationalization system (#248) * Add multilingual support and i18n integration - Implemented i18n framework using `react-i18next` for translation management. - Added support for English and French languages with translation files. - Integrated language selector into settings. - Updated all text strings in UI components to use translation keys. - Ensured smooth language switching with live updates. * Migrate remaining hard-coded strings to i18n system - TaskCard: status labels, review reasons, badges, action buttons - PhaseProgressIndicator: execution phases, progress labels - KanbanBoard: drop zone, show archived, tooltips - CustomModelModal: dialog title, description, labels - ProactiveSwapListener: account switch notifications - AgentProfileSelector: phase labels, custom configuration - GeneralSettings: agent framework option Added translation keys for en/fr locales in tasks.json, common.json, and settings.json for complete i18n coverage. * Add i18n support to dialogs and settings components - AddFeatureDialog: form labels, validation messages, buttons - AddProjectModal: dialog steps, form fields, actions - RateLimitIndicator: rate limit notifications - RateLimitModal: account switching, upgrade prompts - AdvancedSettings: updates and notifications sections - ThemeSettings: theme selection labels - Updated dialogs.json locales (en/fr) * Fix truncated 'ready' message in dialogs locales * Fix backlog terminology in i18n locales Change "Planning"/"Planification" to standard PM term "Backlog" * Migrate settings navigation and integration labels to i18n - AppSettings: nav items, section titles, buttons - IntegrationSettings: Claude accounts, auto-switch, API keys labels - Added settings nav/projectSections/integrations translation keys - Added buttons.saving to common translations * Migrate AgentProfileSettings and Sidebar init dialog to i18n - AgentProfileSettings: migrate phase config labels, section title, description, and all hardcoded strings to settings namespace - Sidebar: migrate init dialog strings to dialogs namespace with common buttons from common namespace - Add new translation keys for agent profile settings and update dialog * Migrate AppSettings navigation labels to i18n - Add useTranslation hook to AppSettings.tsx - Replace hardcoded section labels with dynamic translations - Add projectSections translations for project settings nav - Add rerunWizardDescription translation key * Add explicit typing to notificationItems array Import NotificationSettings type and use keyof to properly type the notification item keys, removing manual type assertion. --- apps/frontend/package-lock.json | 147 ++++++++--- apps/frontend/package.json | 2 + apps/frontend/src/renderer/App.tsx | 33 ++- .../renderer/components/AddFeatureDialog.tsx | 74 +++--- .../renderer/components/AddProjectModal.tsx | 46 ++-- .../components/AgentProfileSelector.tsx | 49 ++-- .../renderer/components/CustomModelModal.tsx | 14 +- .../renderer/components/GitHubSetupModal.tsx | 12 +- .../src/renderer/components/GitSetupModal.tsx | 30 ++- .../src/renderer/components/KanbanBoard.tsx | 35 +-- .../components/PhaseProgressIndicator.tsx | 59 +++-- .../components/ProactiveSwapListener.tsx | 8 +- .../components/RateLimitIndicator.tsx | 28 +- .../renderer/components/RateLimitModal.tsx | 50 ++-- .../src/renderer/components/Sidebar.tsx | 66 ++--- .../src/renderer/components/TaskCard.tsx | 42 +-- .../src/renderer/components/WelcomeScreen.tsx | 25 +- .../components/onboarding/CompletionStep.tsx | 34 +-- .../onboarding/OnboardingWizard.tsx | 20 +- .../components/onboarding/WelcomeStep.tsx | 31 +-- .../components/settings/AdvancedSettings.tsx | 80 +++--- .../settings/AgentProfileSettings.tsx | 41 ++- .../components/settings/AppSettings.tsx | 75 +++--- .../components/settings/DisplaySettings.tsx | 26 +- .../components/settings/GeneralSettings.tsx | 41 +-- .../settings/IntegrationSettings.tsx | 84 +++--- .../components/settings/LanguageSettings.tsx | 77 ++++++ .../components/settings/ThemeSettings.tsx | 7 +- apps/frontend/src/renderer/main.tsx | 3 + apps/frontend/src/shared/constants/config.ts | 4 +- apps/frontend/src/shared/constants/i18n.ts | 13 + apps/frontend/src/shared/i18n/index.ts | 61 +++++ .../src/shared/i18n/locales/en/common.json | 92 +++++++ .../src/shared/i18n/locales/en/dialogs.json | 122 +++++++++ .../shared/i18n/locales/en/navigation.json | 30 +++ .../shared/i18n/locales/en/onboarding.json | 68 +++++ .../src/shared/i18n/locales/en/settings.json | 240 ++++++++++++++++++ .../src/shared/i18n/locales/en/tasks.json | 86 +++++++ .../src/shared/i18n/locales/en/welcome.json | 16 ++ .../src/shared/i18n/locales/fr/common.json | 92 +++++++ .../src/shared/i18n/locales/fr/dialogs.json | 122 +++++++++ .../shared/i18n/locales/fr/navigation.json | 30 +++ .../shared/i18n/locales/fr/onboarding.json | 68 +++++ .../src/shared/i18n/locales/fr/settings.json | 240 ++++++++++++++++++ .../src/shared/i18n/locales/fr/tasks.json | 86 +++++++ .../src/shared/i18n/locales/fr/welcome.json | 16 ++ apps/frontend/src/shared/types/settings.ts | 3 + 47 files changed, 2132 insertions(+), 496 deletions(-) create mode 100644 apps/frontend/src/renderer/components/settings/LanguageSettings.tsx create mode 100644 apps/frontend/src/shared/constants/i18n.ts create mode 100644 apps/frontend/src/shared/i18n/index.ts create mode 100644 apps/frontend/src/shared/i18n/locales/en/common.json create mode 100644 apps/frontend/src/shared/i18n/locales/en/dialogs.json create mode 100644 apps/frontend/src/shared/i18n/locales/en/navigation.json create mode 100644 apps/frontend/src/shared/i18n/locales/en/onboarding.json create mode 100644 apps/frontend/src/shared/i18n/locales/en/settings.json create mode 100644 apps/frontend/src/shared/i18n/locales/en/tasks.json create mode 100644 apps/frontend/src/shared/i18n/locales/en/welcome.json create mode 100644 apps/frontend/src/shared/i18n/locales/fr/common.json create mode 100644 apps/frontend/src/shared/i18n/locales/fr/dialogs.json create mode 100644 apps/frontend/src/shared/i18n/locales/fr/navigation.json create mode 100644 apps/frontend/src/shared/i18n/locales/fr/onboarding.json create mode 100644 apps/frontend/src/shared/i18n/locales/fr/settings.json create mode 100644 apps/frontend/src/shared/i18n/locales/fr/tasks.json create mode 100644 apps/frontend/src/shared/i18n/locales/fr/welcome.json diff --git a/apps/frontend/package-lock.json b/apps/frontend/package-lock.json index 81c474c3..3cd2a768 100644 --- a/apps/frontend/package-lock.json +++ b/apps/frontend/package-lock.json @@ -40,10 +40,12 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "electron-updater": "^6.6.2", + "i18next": "^25.7.3", "lucide-react": "^0.560.0", "motion": "^12.23.26", "react": "^19.2.3", "react-dom": "^19.2.3", + "react-i18next": "^16.5.0", "react-markdown": "^10.1.0", "react-resizable-panels": "^3.0.6", "remark-gfm": "^4.0.1", @@ -152,6 +154,7 @@ "integrity": "sha512-e7jT4DxYvIDLk1ZHmU/m/mB19rex9sv0c2ftBtjSBv+kVM/902eh0fINUzD7UwLLNR+jU585GxUJ8/EBfAM5fw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.5", @@ -395,7 +398,6 @@ "version": "7.28.4", "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.28.4.tgz", "integrity": "sha512-Q/N6JNWvIvPnLDvjlE1OUBLPQHH6l3CltCEsHIujp45zQUSSh8K+gHnaEX45yAT1nyngnINhvWtzN+Nb9D8RAQ==", - "dev": true, "license": "MIT", "engines": { "node": ">=6.9.0" @@ -537,6 +539,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" }, @@ -560,6 +563,7 @@ } ], "license": "MIT", + "peer": true, "engines": { "node": ">=18" } @@ -599,6 +603,7 @@ "resolved": "https://registry.npmjs.org/@dnd-kit/core/-/core-6.3.1.tgz", "integrity": "sha512-xkGBRQQab4RLwgXxoqETICr6S5JlogafbhNsidmrkVv2YRs5MLwpjoF2qpiGjQt8S9AoxtIV603s0GIUpY5eYQ==", "license": "MIT", + "peer": true, "dependencies": { "@dnd-kit/accessibility": "^3.1.1", "@dnd-kit/utilities": "^3.2.2", @@ -993,7 +998,6 @@ "dev": true, "license": "BSD-2-Clause", "optional": true, - "peer": true, "dependencies": { "cross-dirname": "^0.1.0", "debug": "^4.3.4", @@ -1015,7 +1019,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "graceful-fs": "^4.2.0", "jsonfile": "^6.0.1", @@ -4016,8 +4019,7 @@ "resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz", "integrity": "sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/@types/babel__core": { "version": "7.20.5", @@ -4204,6 +4206,7 @@ "resolved": "https://registry.npmjs.org/@types/react/-/react-19.2.7.tgz", "integrity": "sha512-MWtvHrGZLFttgeEj28VXHxpmwYbor/ATPYbBfSFZEIRK0ecCFLl2Qo55z52Hss+UV9CRN7trSeq1zbgx7YDWWg==", "license": "MIT", + "peer": true, "dependencies": { "csstype": "^3.2.2" } @@ -4214,6 +4217,7 @@ "integrity": "sha512-jp2L/eY6fn+KgVVQAOqYItbF0VY/YApe5Mz2F0aykSO8gx31bYCZyvSeYxCHKvzHG5eZjc+zyaS5BrBWya2+kQ==", "devOptional": true, "license": "MIT", + "peer": true, "peerDependencies": { "@types/react": "^19.2.0" } @@ -4305,6 +4309,7 @@ "integrity": "sha512-N9lBGA9o9aqb1hVMc9hzySbhKibHmB+N3IpoShyV6HyQYRGIhlrO5rQgttypi+yEeKsKI4idxC8Jw6gXKD4THA==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@typescript-eslint/scope-manager": "8.49.0", "@typescript-eslint/types": "8.49.0", @@ -4704,7 +4709,8 @@ "version": "5.5.0", "resolved": "https://registry.npmjs.org/@xterm/xterm/-/xterm-5.5.0.tgz", "integrity": "sha512-hqJHYaQb5OptNunnyAnkHyM8aCjZ1MEIDTQu1iIbbTD/xops91NB5yq1ZK/dC2JDbVWtF23zUtl9JE2NqwT87A==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/7zip-bin": { "version": "5.2.0", @@ -4726,6 +4732,7 @@ "integrity": "sha512-NZyJarBfL7nWwIq+FDL6Zp/yHEhePMNnnJ0y3qfieCrmNvYct8uvtiV41UvlSe6apAfk0fY1FbWx+NwfmpvtTg==", "dev": true, "license": "MIT", + "peer": true, "bin": { "acorn": "bin/acorn" }, @@ -4786,6 +4793,7 @@ "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -4958,7 +4966,6 @@ "integrity": "sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==", "dev": true, "license": "Apache-2.0", - "peer": true, "dependencies": { "dequal": "^2.0.3" } @@ -5343,6 +5350,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "baseline-browser-mapping": "^2.9.0", "caniuse-lite": "^1.0.30001759", @@ -6013,8 +6021,7 @@ "integrity": "sha512-+R08/oI0nl3vfPcqftZRpytksBXDzOUveBq/NBVx0sUp1axwzPQrKinNx5yd5sxPu8j1wIy8AfnVQ+5eFdha6Q==", "dev": true, "license": "MIT", - "optional": true, - "peer": true + "optional": true }, "node_modules/cross-spawn": { "version": "7.0.6", @@ -6349,6 +6356,7 @@ "integrity": "sha512-59CAAjAhTaIMCN8y9kD573vDkxbs1uhDcrFLHSgutYdPcGOU35Rf95725snvzEOy4BFB7+eLJ8djCNPmGwG67w==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "app-builder-lib": "26.0.12", "builder-util": "26.0.11", @@ -6406,8 +6414,7 @@ "resolved": "https://registry.npmjs.org/dom-accessibility-api/-/dom-accessibility-api-0.5.16.tgz", "integrity": "sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/dotenv": { "version": "16.6.1", @@ -6483,6 +6490,7 @@ "dev": true, "hasInstallScript": true, "license": "MIT", + "peer": true, "dependencies": { "@electron/get": "^2.0.0", "@types/node": "^22.7.7", @@ -6611,7 +6619,6 @@ "dev": true, "hasInstallScript": true, "license": "MIT", - "peer": true, "dependencies": { "@electron/asar": "^3.2.1", "debug": "^4.1.1", @@ -6632,7 +6639,6 @@ "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "graceful-fs": "^4.1.2", "jsonfile": "^4.0.0", @@ -6648,7 +6654,6 @@ "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", "dev": true, "license": "MIT", - "peer": true, "optionalDependencies": { "graceful-fs": "^4.1.6" } @@ -6659,7 +6664,6 @@ "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">= 4.0.0" } @@ -7029,6 +7033,7 @@ "integrity": "sha512-LEyamqS7W5HB3ujJyvi0HQK/dtVINZvd5mAAp9eT5S/ujByGjiZLCzPcHVzuXbpJDJF/cxwHlfceVUDZ2lnSTw==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "@eslint-community/eslint-utils": "^4.8.0", "@eslint-community/regexpp": "^4.12.1", @@ -8161,6 +8166,15 @@ "node": ">=18" } }, + "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", @@ -8246,6 +8260,38 @@ "url": "https://github.com/sponsors/typicode" } }, + "node_modules/i18next": { + "version": "25.7.3", + "resolved": "https://registry.npmjs.org/i18next/-/i18next-25.7.3.tgz", + "integrity": "sha512-2XaT+HpYGuc2uTExq9TVRhLsso+Dxym6PWaKpn36wfBmTI779OQ7iP/XaZHzrnGyzU4SHpFrTYLKfVyBfAhVNA==", + "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/iconv-corefoundation": { "version": "1.1.7", "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz", @@ -9024,6 +9070,7 @@ "integrity": "sha512-Cvc9WUhxSMEo4McES3P7oK3QaXldCfNWp7pl2NNeiIFlCoLr3kfq9kb1fxftiwk1FLV7CvpvDfonxtzUDeSOPg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "cssstyle": "^4.2.1", "data-urls": "^5.0.0", @@ -9955,7 +10002,6 @@ "integrity": "sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==", "dev": true, "license": "MIT", - "peer": true, "bin": { "lz-string": "bin/bin.js" } @@ -11779,6 +11825,7 @@ "integrity": "sha512-5gTmgEY/sqK6gFXLIsQNH19lWb4ebPDLA4SdLP7dsWkIXHWlG66oPuVvXSGFPppYZz8ZDZq0dYYrbHfBCVUb1Q==", "dev": true, "license": "MIT", + "peer": true, "engines": { "node": ">=12" }, @@ -11876,6 +11923,7 @@ } ], "license": "MIT", + "peer": true, "dependencies": { "nanoid": "^3.3.11", "picocolors": "^1.1.1", @@ -11912,7 +11960,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "dependencies": { "commander": "^9.4.0" }, @@ -11930,7 +11977,6 @@ "dev": true, "license": "MIT", "optional": true, - "peer": true, "engines": { "node": "^12.20.0 || >=14" } @@ -11951,7 +11997,6 @@ "integrity": "sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "ansi-regex": "^5.0.1", "ansi-styles": "^5.0.0", @@ -11967,7 +12012,6 @@ "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "license": "MIT", - "peer": true, "engines": { "node": ">=10" }, @@ -11980,8 +12024,7 @@ "resolved": "https://registry.npmjs.org/react-is/-/react-is-17.0.2.tgz", "integrity": "sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==", "dev": true, - "license": "MIT", - "peer": true + "license": "MIT" }, "node_modules/proc-log": { "version": "2.0.1", @@ -12085,6 +12128,7 @@ "resolved": "https://registry.npmjs.org/react/-/react-19.2.3.tgz", "integrity": "sha512-Ku/hhYbVjOQnXDZFv2+RibmLFGwFdeeKHFcOTlrt7xplBnya5OGn/hIRDsqDiSUcfORsDC7MPxwork8jBwsIWA==", "license": "MIT", + "peer": true, "engines": { "node": ">=0.10.0" } @@ -12094,6 +12138,7 @@ "resolved": "https://registry.npmjs.org/react-dom/-/react-dom-19.2.3.tgz", "integrity": "sha512-yELu4WmLPw5Mr/lmeEpox5rw3RETacE++JgHqQzd2dg+YbJuat3jH4ingc+WPZhxaoFzdv9y33G+F7Nl5O0GBg==", "license": "MIT", + "peer": true, "dependencies": { "scheduler": "^0.27.0" }, @@ -12101,6 +12146,33 @@ "react": "^19.2.3" } }, + "node_modules/react-i18next": { + "version": "16.5.0", + "resolved": "https://registry.npmjs.org/react-i18next/-/react-i18next-16.5.0.tgz", + "integrity": "sha512-IMpPTyCTKxEj8klCrLKUTIUa8uYTd851+jcu2fJuUB9Agkk9Qq8asw4omyeHVnOXHrLgQJGTm5zTvn8HpaPiqw==", + "license": "MIT", + "dependencies": { + "@babel/runtime": "^7.27.6", + "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": "16.13.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.13.1.tgz", @@ -13383,7 +13455,8 @@ "version": "4.1.18", "resolved": "https://registry.npmjs.org/tailwindcss/-/tailwindcss-4.1.18.tgz", "integrity": "sha512-4+Z+0yiYyEtUVCScyfHCxOYP06L5Ne+JiHhY2IjR2KWMIWhJOYZKLSGZaP5HkZ8+bY0cxfzwDE5uOmzFXyIwxw==", - "license": "MIT" + "license": "MIT", + "peer": true }, "node_modules/tapable": { "version": "2.3.0", @@ -13440,7 +13513,6 @@ "integrity": "sha512-yYrrsWnrXMcdsnu/7YMYAofM1ktpL5By7vZhf15CrXijWWrEYZks5AXBudalfSWJLlnen/QUJUB5aoB0kqZUGA==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "mkdirp": "^0.5.1", "rimraf": "~2.6.2" @@ -13467,7 +13539,6 @@ "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", @@ -13489,7 +13560,6 @@ "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "brace-expansion": "^1.1.7" }, @@ -13503,7 +13573,6 @@ "integrity": "sha512-FP+p8RB8OWpF3YZBCrP5gtADmtXApB5AMLn+vdyA+PyxCjrCs00mjyUozssO33cwDeT3wNGdLxJ5M//YqtHAJw==", "dev": true, "license": "MIT", - "peer": true, "dependencies": { "minimist": "^1.2.6" }, @@ -13518,7 +13587,6 @@ "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", - "peer": true, "dependencies": { "glob": "^7.1.3" }, @@ -13833,8 +13901,9 @@ "version": "5.9.3", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", - "dev": true, + "devOptional": true, "license": "Apache-2.0", + "peer": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" @@ -14099,6 +14168,15 @@ } } }, + "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", + "peerDependencies": { + "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" + } + }, "node_modules/utf8-byte-length": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/utf8-byte-length/-/utf8-byte-length-1.0.5.tgz", @@ -14175,6 +14253,7 @@ "integrity": "sha512-dZwN5L1VlUBewiP6H9s2+B3e3Jg96D0vzN+Ry73sOefebhYr9f94wwkMNN/9ouoU8pV1BqA1d1zGk8928cx0rg==", "dev": true, "license": "MIT", + "peer": true, "dependencies": { "esbuild": "^0.27.0", "fdir": "^6.5.0", @@ -14821,6 +14900,15 @@ } } }, + "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/w3c-xmlserializer": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/w3c-xmlserializer/-/w3c-xmlserializer-5.0.0.tgz", @@ -15208,6 +15296,7 @@ "integrity": "sha512-Bd5fw9wlIhtqCCxotZgdTOMwGm1a0u75wARVEY9HMs1X17trvA/lMi4+MGK5EUfYkXVTbX8UDiDKW4OgzHVUZw==", "dev": true, "license": "MIT", + "peer": true, "funding": { "url": "https://github.com/sponsors/colinhacks" } diff --git a/apps/frontend/package.json b/apps/frontend/package.json index 7ab47e33..fbf35ade 100644 --- a/apps/frontend/package.json +++ b/apps/frontend/package.json @@ -73,10 +73,12 @@ "class-variance-authority": "^0.7.1", "clsx": "^2.1.1", "electron-updater": "^6.6.2", + "i18next": "^25.7.3", "lucide-react": "^0.560.0", "motion": "^12.23.26", "react": "^19.2.3", "react-dom": "^19.2.3", + "react-i18next": "^16.5.0", "react-markdown": "^10.1.0", "react-resizable-panels": "^3.0.6", "remark-gfm": "^4.0.1", diff --git a/apps/frontend/src/renderer/App.tsx b/apps/frontend/src/renderer/App.tsx index 1201ab75..26e02fb1 100644 --- a/apps/frontend/src/renderer/App.tsx +++ b/apps/frontend/src/renderer/App.tsx @@ -1,4 +1,5 @@ import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { Settings2, Download, RefreshCw, AlertCircle } from 'lucide-react'; import { DndContext, @@ -185,6 +186,14 @@ export function App() { } }, [settingsHaveLoaded, settings.onboardingCompleted]); + // Sync i18n language with settings + const { t, i18n } = useTranslation('dialogs'); + useEffect(() => { + if (settings.language && settings.language !== i18n.language) { + i18n.changeLanguage(settings.language); + } + }, [settings.language, i18n]); + // Listen for open-app-settings events (e.g., from project settings) useEffect(() => { const handleOpenAppSettings = (event: Event) => { @@ -746,19 +755,19 @@ export function App() { - Initialize Auto Claude + {t('initialize.title')} - This project doesn't have Auto Claude initialized. Would you like to set it up now? + {t('initialize.description')}
-

This will:

+

{t('initialize.willDo')}

{!settings.autoBuildPath && ( @@ -766,9 +775,9 @@ export function App() {
-

Source path not configured

+

{t('initialize.sourcePathNotConfigured')}

- Please set the Auto Claude source path in App Settings before initializing. + {t('initialize.sourcePathNotConfiguredDescription')}

@@ -779,7 +788,7 @@ export function App() {
-

Initialization Failed

+

{t('initialize.initFailed')}

{initError}

@@ -790,7 +799,7 @@ export function App() {
diff --git a/apps/frontend/src/renderer/components/AddFeatureDialog.tsx b/apps/frontend/src/renderer/components/AddFeatureDialog.tsx index a75132b1..d29e2b97 100644 --- a/apps/frontend/src/renderer/components/AddFeatureDialog.tsx +++ b/apps/frontend/src/renderer/components/AddFeatureDialog.tsx @@ -21,6 +21,7 @@ * ``` */ import { useState, useEffect } from 'react'; +import { useTranslation } from 'react-i18next'; import { Loader2, X } from 'lucide-react'; import { Dialog, @@ -68,18 +69,18 @@ interface AddFeatureDialogProps { defaultPhaseId?: string; } -// Complexity options +// Complexity options (keys for translation) const COMPLEXITY_OPTIONS = [ - { value: 'low', label: 'Low' }, - { value: 'medium', label: 'Medium' }, - { value: 'high', label: 'High' } + { value: 'low', labelKey: 'addFeature.lowComplexity' }, + { value: 'medium', labelKey: 'addFeature.mediumComplexity' }, + { value: 'high', labelKey: 'addFeature.highComplexity' } ] as const; -// Impact options +// Impact options (keys for translation) const IMPACT_OPTIONS = [ - { value: 'low', label: 'Low Impact' }, - { value: 'medium', label: 'Medium Impact' }, - { value: 'high', label: 'High Impact' } + { value: 'low', labelKey: 'addFeature.lowImpact' }, + { value: 'medium', labelKey: 'addFeature.mediumImpact' }, + { value: 'high', labelKey: 'addFeature.highImpact' } ] as const; export function AddFeatureDialog({ @@ -89,6 +90,8 @@ export function AddFeatureDialog({ onFeatureAdded, defaultPhaseId }: AddFeatureDialogProps) { + const { t } = useTranslation('dialogs'); + // Form state const [title, setTitle] = useState(''); const [description, setDescription] = useState(''); @@ -122,15 +125,15 @@ export function AddFeatureDialog({ const handleSave = async () => { // Validate required fields if (!title.trim()) { - setError('Title is required'); + setError(t('addFeature.titleRequired')); return; } if (!description.trim()) { - setError('Description is required'); + setError(t('addFeature.descriptionRequired')); return; } if (!phaseId) { - setError('Please select a phase'); + setError(t('addFeature.phaseRequired')); return; } @@ -168,7 +171,7 @@ export function AddFeatureDialog({ onOpenChange(false); onFeatureAdded?.(newFeatureId); } catch (err) { - setError(err instanceof Error ? err.message : 'Failed to add feature. Please try again.'); + setError(err instanceof Error ? err.message : t('addFeature.failedToAdd')); } finally { setIsSaving(false); } @@ -187,10 +190,9 @@ export function AddFeatureDialog({ - Add Feature + {t('addFeature.title')} - Add a new feature to your roadmap. Provide details about what you want to build - and how it fits into your product strategy. + {t('addFeature.description')} @@ -198,11 +200,11 @@ export function AddFeatureDialog({ {/* Title (Required) */}
setTitle(e.target.value)} disabled={isSaving} @@ -212,11 +214,11 @@ export function AddFeatureDialog({ {/* Description (Required) */}