feat: add i18n framework with Chinese (zh-CN) localization#3184
feat: add i18n framework with Chinese (zh-CN) localization#3184jiaminghua wants to merge 3 commits intowavetermdev:mainfrom
Conversation
WalkthroughAdds i18n support using i18next/react-i18next: new dependencies in package.json, a new initializer at frontend/app/i18n/index.ts (exports configured i18n and assigns window.__waveI18n), and two locale files (en.json, zh-CN.json). Multiple UI components, context menus, and modals were updated to replace hardcoded English strings with translation calls ( Estimated code review effort🎯 2 (Simple) | ⏱️ ~10 minutes 🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
- Add react-i18next + i18next for internationalization support - Create English and Simplified Chinese translation files (73 keys) - Localize core UI components: - Connection modal (connect, disconnect, local/remote sections) - Tab context menu (rename, close, flag, backgrounds, tab bar position) - Block frame header (split, magnify, settings, close) - AI panel header and context menu (widget context, new chat, configure modes) - Settings/config page (config files, visual/raw JSON, save, errors) - About modal (description, version, links) - Modal footer (OK/Cancel buttons) - Expose global t() function via window.__waveI18n for non-React contexts - Default language set to zh-CN with English fallback
7ad9c45 to
78dcab4
Compare
There was a problem hiding this comment.
Actionable comments posted: 3
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (2)
frontend/app/view/waveconfig/waveconfig.tsx (1)
69-90:⚠️ Potential issue | 🟡 MinorTwo visible strings still bypass i18n.
"deprecated"andSave (${model.saveShortcut})are still hardcoded English, so this view is not fully localized.🛠️ Suggested fix
- const saveTooltip = `Save (${model.saveShortcut})`; + const saveTooltip = t("app.saveWithShortcut", { shortcut: model.saveShortcut }); ... - deprecated + {t("app.deprecated")}Also add locale keys in both files:
// frontend/app/i18n/locales/en.json "app.deprecated": "Deprecated", "app.saveWithShortcut": "Save ({{shortcut}})"// frontend/app/i18n/locales/zh-CN.json "app.deprecated": "已弃用", "app.saveWithShortcut": "保存 ({{shortcut}})"Also applies to: 167-167
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/app/view/waveconfig/waveconfig.tsx` around lines 69 - 90, Replace the hardcoded strings with i18n lookups: change the literal "deprecated" label shown in the deprecatedConfigFiles.map render and the "Save (${model.saveShortcut})" text (where model.saveShortcut is used) to use the app's translation function (e.g., t('app.deprecated') and t('app.saveWithShortcut', { shortcut: model.saveShortcut })) in the WaveConfig component rendering logic (the block using deprecatedConfigFiles.map, selectedFile, handleFileSelect, and model.saveShortcut); then add the corresponding keys "app.deprecated" and "app.saveWithShortcut" with the {{shortcut}} placeholder to both locales files (en.json and zh-CN.json).frontend/app/modals/modal.tsx (1)
96-97:⚠️ Potential issue | 🟠 MajorFix label fallback logic: current implementation blocks i18n defaults and overrides intentional empty labels.
At Line 96 and Line 97, hardcoded defaults (
"Cancel","Ok") prevent translated fallbacks from ever being used for undefined props.
At Line 106 and Line 111,||also turns intentional empty strings into fallback text.Proposed fix
const ModalFooter = ({ onCancel, onOk, - cancelLabel = "Cancel", - okLabel = "Ok", + cancelLabel, + okLabel, okDisabled, cancelDisabled, }: ModalFooterProps) => { const { t } = useTranslation(); return ( <footer className="modal-footer"> {onCancel && ( <Button className="grey ghost" onClick={onCancel} disabled={cancelDisabled}> - {cancelLabel || t("app.cancel")} + {cancelLabel ?? t("app.cancel")} </Button> )} {onOk && ( <Button onClick={onOk} disabled={okDisabled}> - {okLabel || t("app.ok")} + {okLabel ?? t("app.ok")} </Button> )} </footer> ); };Also applies to: 106-111
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/app/modals/modal.tsx` around lines 96 - 97, The hardcoded default labels cancelLabel = "Cancel" and okLabel = "Ok" prevent i18n defaults and the use of || at the render sites forces empty strings to be replaced; change the prop defaults to undefined (remove the "Cancel"/"Ok" defaults) and replace any uses of logical OR (e.g., cancelLabel ||, okLabel ||) with the nullish coalescing operator (cancelLabel ?? fallback, okLabel ?? fallback) or explicit undefined checks so that undefined falls back to translations but intentional empty strings are preserved; adjust references in modal component where cancelLabel and okLabel are read (and the lines using || around 106-111) accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@frontend/app/i18n/index.ts`:
- Around line 7-13: The i18n init currently hardcodes lng: "zh-CN" which forces
Chinese for all users; update the i18n initialization (the
i18n.use(initReactI18next).init call) to compute the initial language by
checking a persisted user preference (e.g., localStorage key like "i18nLng"),
then falling back to navigator.language (or navigator.languages[0]) normalized
to one of the available resources keys ("en" or "zh-CN"), and finally to "en" if
none match; ensure you call i18n.changeLanguage with the chosen language so the
runtime language is applied and keep the resources object (en, zhCN) intact.
In `@frontend/app/i18n/locales/en.json`:
- Line 56: The translation entry "app.ok" currently uses "Ok" — update the
locale value for the "app.ok" key in frontend/app/i18n/locales/en.json to use
the standard button capitalization "OK" so UI text is consistent across the app.
In `@frontend/app/tab/tabcontextmenu.ts`:
- Around line 23-40: The tab context menu contains hardcoded English labels;
update all menu item labels to use the translation helper t(...) instead of
literal strings (e.g., replace "Rename Tab", "Copy TabId", "Flag Tab",
"Backgrounds", "Close Tab" with t("app.renameTab"), t("app.copyTabId"),
t("app.flagTab"), t("app.backgrounds"), t("app.closeTab") respectively), making
changes in buildTabBarContextMenu and the other menu-building logic in
tabcontextmenu.ts (the remaining menu items around lines 42-123) and add
corresponding keys to the localization files so the menu renders fully
translated.
---
Outside diff comments:
In `@frontend/app/modals/modal.tsx`:
- Around line 96-97: The hardcoded default labels cancelLabel = "Cancel" and
okLabel = "Ok" prevent i18n defaults and the use of || at the render sites
forces empty strings to be replaced; change the prop defaults to undefined
(remove the "Cancel"/"Ok" defaults) and replace any uses of logical OR (e.g.,
cancelLabel ||, okLabel ||) with the nullish coalescing operator (cancelLabel ??
fallback, okLabel ?? fallback) or explicit undefined checks so that undefined
falls back to translations but intentional empty strings are preserved; adjust
references in modal component where cancelLabel and okLabel are read (and the
lines using || around 106-111) accordingly.
In `@frontend/app/view/waveconfig/waveconfig.tsx`:
- Around line 69-90: Replace the hardcoded strings with i18n lookups: change the
literal "deprecated" label shown in the deprecatedConfigFiles.map render and the
"Save (${model.saveShortcut})" text (where model.saveShortcut is used) to use
the app's translation function (e.g., t('app.deprecated') and
t('app.saveWithShortcut', { shortcut: model.saveShortcut })) in the WaveConfig
component rendering logic (the block using deprecatedConfigFiles.map,
selectedFile, handleFileSelect, and model.saveShortcut); then add the
corresponding keys "app.deprecated" and "app.saveWithShortcut" with the
{{shortcut}} placeholder to both locales files (en.json and zh-CN.json).
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 69d8cf5a-fe66-4f7c-9e08-65a4e4e1b7c8
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (13)
frontend/app/aipanel/aipanel-contextmenu.tsfrontend/app/aipanel/aipanelheader.tsxfrontend/app/app.tsxfrontend/app/block/blockframe-header.tsxfrontend/app/i18n/index.tsfrontend/app/i18n/locales/en.jsonfrontend/app/i18n/locales/zh-CN.jsonfrontend/app/modals/about.tsxfrontend/app/modals/conntypeahead.tsxfrontend/app/modals/modal.tsxfrontend/app/tab/tabcontextmenu.tsfrontend/app/view/waveconfig/waveconfig.tsxpackage.json
There was a problem hiding this comment.
🧹 Nitpick comments (1)
frontend/app/modals/conntypeahead.tsx (1)
247-248: Consider using empty string or constant forvaluefor consistency.Other items with custom
onSelecthandlers (e.g.,reconnectSuggestionItem,disconnectSuggestionItem,newConnectionSuggestion) usevalue: ""since the value isn't used by their handlers. Using the translated string here is inconsistent and could cause issues if any future code matches againstvalue.♻️ Suggested fix for consistency
const connectionsEditItem: SuggestionConnectionItem = { status: "disconnected", icon: "gear", iconColor: "var(--grey-text-color)", - value: t("app.editConnections"), + value: "", label: t("app.editConnections"),🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/app/modals/conntypeahead.tsx` around lines 247 - 248, Change the suggestion item's value from the translated string to an empty string (or a shared constant) to match the other custom onSelect-handled items; leave label as t("app.editConnections") and ensure the item's onSelect handler still uses the value-agnostic logic (as in reconnectSuggestionItem, disconnectSuggestionItem, newConnectionSuggestion) so no behavior changes occur.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@frontend/app/modals/conntypeahead.tsx`:
- Around line 247-248: Change the suggestion item's value from the translated
string to an empty string (or a shared constant) to match the other custom
onSelect-handled items; leave label as t("app.editConnections") and ensure the
item's onSelect handler still uses the value-agnostic logic (as in
reconnectSuggestionItem, disconnectSuggestionItem, newConnectionSuggestion) so
no behavior changes occur.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 6f8ece89-b9a4-4a3c-a99c-b1630f8720de
⛔ Files ignored due to path filters (1)
package-lock.jsonis excluded by!**/package-lock.json
📒 Files selected for processing (13)
frontend/app/aipanel/aipanel-contextmenu.tsfrontend/app/aipanel/aipanelheader.tsxfrontend/app/app.tsxfrontend/app/block/blockframe-header.tsxfrontend/app/i18n/index.tsfrontend/app/i18n/locales/en.jsonfrontend/app/i18n/locales/zh-CN.jsonfrontend/app/modals/about.tsxfrontend/app/modals/conntypeahead.tsxfrontend/app/modals/modal.tsxfrontend/app/tab/tabcontextmenu.tsfrontend/app/view/waveconfig/waveconfig.tsxpackage.json
✅ Files skipped from review due to trivial changes (7)
- frontend/app/app.tsx
- package.json
- frontend/app/i18n/locales/en.json
- frontend/app/view/waveconfig/waveconfig.tsx
- frontend/app/i18n/locales/zh-CN.json
- frontend/app/tab/tabcontextmenu.ts
- frontend/app/aipanel/aipanel-contextmenu.ts
🚧 Files skipped from review as they are similar to previous changes (5)
- frontend/app/modals/about.tsx
- frontend/app/block/blockframe-header.tsx
- frontend/app/modals/modal.tsx
- frontend/app/aipanel/aipanelheader.tsx
- frontend/app/i18n/index.ts
- Complete tab context menu localization (rename, copy ID, flag tab, backgrounds, close tab, flag colors) - Fix 'Ok' → 'OK' capitalization in en.json - Auto-detect system language instead of hardcoding zh-CN - Persist language preference to localStorage - Support language switching via i18n.changeLanguage()
- Translate 'deprecated' label in config sidebar - Translate 'Save (shortcut)' tooltip with i18n interpolation - Fix modal footer default label logic (null check instead of ||) - Fix connectionsEditItem value to empty string for consistency - Add app.deprecated and app.saveWithShortcut translation keys
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
frontend/app/modals/conntypeahead.tsx (1)
162-169:⚠️ Potential issue | 🟡 MinorMatch Git Bash filtering against the localized label, not hardcoded English.
Line 162 filters with
"Git Bash"while Line 168 renderst("app.gitBash"). In localized UIs, users may type the translated label and get no match.Suggested fix
- if (hasGitBash && "Git Bash".toLowerCase().includes(connSelected.toLowerCase())) { + const gitBashLabel = t("app.gitBash"); + if (hasGitBash && gitBashLabel.toLowerCase().includes(connSelected.toLowerCase())) { gitBashItems.push({ @@ - label: t("app.gitBash"), + label: gitBashLabel, current: connection === "local:gitbash", }); }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@frontend/app/modals/conntypeahead.tsx` around lines 162 - 169, The filter for Git Bash currently matches the hardcoded English string "Git Bash" against connSelected; change it to match the localized label by using the translation used for rendering (t("app.gitBash")) when deciding whether to push into gitBashItems, so update the conditional in the block that references hasGitBash and connSelected to compare t("app.gitBash").toLowerCase() (or a locale-aware comparison) with connSelected.toLowerCase() while keeping the rest of the gitBashItems object (status, icon, value "local:gitbash", label t("app.gitBash"), current check against connection) unchanged.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Outside diff comments:
In `@frontend/app/modals/conntypeahead.tsx`:
- Around line 162-169: The filter for Git Bash currently matches the hardcoded
English string "Git Bash" against connSelected; change it to match the localized
label by using the translation used for rendering (t("app.gitBash")) when
deciding whether to push into gitBashItems, so update the conditional in the
block that references hasGitBash and connSelected to compare
t("app.gitBash").toLowerCase() (or a locale-aware comparison) with
connSelected.toLowerCase() while keeping the rest of the gitBashItems object
(status, icon, value "local:gitbash", label t("app.gitBash"), current check
against connection) unchanged.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Repository UI
Review profile: CHILL
Plan: Pro
Run ID: 510f1090-5938-4c87-89a7-180cf9254107
📒 Files selected for processing (5)
frontend/app/i18n/locales/en.jsonfrontend/app/i18n/locales/zh-CN.jsonfrontend/app/modals/conntypeahead.tsxfrontend/app/modals/modal.tsxfrontend/app/view/waveconfig/waveconfig.tsx
✅ Files skipped from review due to trivial changes (3)
- frontend/app/view/waveconfig/waveconfig.tsx
- frontend/app/i18n/locales/zh-CN.json
- frontend/app/i18n/locales/en.json
🚧 Files skipped from review as they are similar to previous changes (1)
- frontend/app/modals/modal.tsx
Summary
This PR adds internationalization (i18n) support to Wave Terminal with full Simplified Chinese localization, addressing long-standing community requests (#2765, #2981).
Changes
Framework
react-i18next+i18nextfor internationalizationzh-CNand English fallbackt()function viawindow.__waveI18nfor non-React contexts (event handlers, menu builders)Localized Components (7 core UI components, 73 translation keys)
Files Added
frontend/app/i18n/index.ts— i18n configurationfrontend/app/i18n/locales/en.json— English translations (73 keys)frontend/app/i18n/locales/zh-CN.json— Simplified Chinese translations (73 keys)Design Decisions
window.__waveI18n.tfor non-React contexts (menu builders) to avoid passingtthrough function parametersapp.xxxnamespace for simplicityCloses #2765
Closes #2981