Skip to content

feat(desktop): add zh locale support and localize navigation/menu labels#8639

Open
lingrany wants to merge 7 commits intoaaif-goose:mainfrom
lingrany:feat/zh-cn-localization-recovery
Open

feat(desktop): add zh locale support and localize navigation/menu labels#8639
lingrany wants to merge 7 commits intoaaif-goose:mainfrom
lingrany:feat/zh-cn-localization-recovery

Conversation

@lingrany
Copy link
Copy Markdown

Summary

This PR improves Chinese localization support in Goose Desktop by addressing two major gaps:

  1. Enables Chinese locales in desktop i18n locale resolution
  2. Fixes UI areas that were still showing hardcoded English labels:
    • Sidebar navigation labels
    • Native Electron top application menu labels (File/Edit/View/Window/Help and related items)

Changes

1) Enable Chinese locale support

  • File: ui/desktop/src/i18n/index.ts
  • Added zh and zh-CN to SUPPORTED_LOCALES

2) Localize sidebar navigation labels at runtime

  • File: ui/desktop/src/components/Layout/NavigationPanel.tsx
  • Added runtime i18n mapping via useIntl / formatMessage
  • Replaced static sidebar labels with translated labels based on nav item IDs
  • This fixes previously hardcoded English labels like:
    • Home / Chat / Recipes / Skills / Apps / Scheduler / Extensions / Settings

3) Localize native Electron app menu in zh locale

  • File: ui/desktop/src/main.ts
  • Added zh locale detection for desktop menu localization
  • Added top-level menu lookup compatible with both English and Chinese labels
  • Added recursive menu label localization before Menu.setApplicationMenu(menu)
  • Localized common menu entries (File/Edit/View/Window/Help and relevant submenu items)

Why

Before this change, Chinese message files existed/loaded for many UI areas, but:

  • Sidebar navigation still used hardcoded English labels
  • Native Electron menu labels were not localized via frontend i18n

This caused visible mixed-language UI for Chinese users.

Testing

  • Ran desktop i18n compile flow
  • Verified sidebar labels render in Chinese under zh locale
  • Verified top app menu labels and common submenu entries are localized in Chinese
  • Verified menu extension logic still works after label lookup changes

Scope

This PR intentionally focuses on locale wiring + hardcoded label fixes for high-visibility UI areas.
It does not attempt to fully translate every desktop message key.

Notes

  • Changes are desktop-only (ui/desktop)
  • No backend/server API behavior changes

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: aecd7db973

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread ui/desktop/src/main.ts Outdated
};

function isZhLocale(): boolean {
const locale = app.getLocale()?.toLowerCase() ?? '';
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Honor GOOSE_LOCALE when deciding menu language

isZhLocale() only consults app.getLocale(), but desktop i18n gives GOOSE_LOCALE highest priority for UI language selection. In the common override case (GOOSE_LOCALE=zh on a non-Chinese OS), the React UI will switch to Chinese while the native app menu remains English because this check returns false, producing a mixed-language experience and bypassing the explicit locale override. Use the same configured locale source as the renderer before falling back to OS locale.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Addressed in dfb964f.

I updated the native menu locale check to honor GOOSE_LOCALE first, then fall back to app.getLocale(), so menu localization matches desktop i18n locale precedence.

Signed-off-by: lingrany <250338893@qq.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: dfb964fede

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


/** The set of locales that have translation catalogs. */
const SUPPORTED_LOCALES = new Set(['en']);
const SUPPORTED_LOCALES = new Set(['en', 'zh', 'zh-CN']);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Normalize underscore locale tags before matching locales

With zh/zh-CN now listed as supported, GOOSE_LOCALE=zh_CN becomes a practical input, but getLocale() still compares the raw tag and only derives a base via split('-'), so zh_CN misses both checks and falls back to English. Meanwhile isZhLocale() in main.ts already normalizes _ to -, so this produces a mixed-language app (Chinese native menu, English renderer). Normalize candidate tags in getLocale() before matching/canonicalizing to keep renderer and menu localization consistent.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Addressed in 1acf963.

I updated i18n locale resolution to normalize underscore tags (e.g. zh_CN -> zh-CN) before canonicalization/matching, so renderer locale selection stays consistent with native menu locale handling.

Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 1acf963035

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

Comment thread ui/desktop/src/i18n/index.ts Outdated
Comment on lines +57 to +58
if (SUPPORTED_LOCALES.has(base)) {
// Validate the full tag is a well-formed BCP 47 locale before using it
// for formatting. Invalid tags (e.g. "en-") would cause RangeError in
// Intl APIs, so fall back to the base language in that case.
let locale = base;
try {
[locale] = Intl.getCanonicalLocales(tag);
} catch {
// tag is not valid BCP 47 — use the base language instead
}
return { locale, messageLocale: base };
return { locale: canonical, messageLocale: base };
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Fall back to base locale when canonicalization fails

If Intl.getCanonicalLocales(normalized) throws (for example GOOSE_LOCALE=en- or zh_), this branch still returns the invalid canonical tag as locale. That value is exported as currentLocale and later used by IntlProvider and date/time formatters (e.g. in renderer.tsx and timeUtils.ts), where invalid locale tags can raise RangeError: Incorrect locale information provided at runtime. Preserve the previous behavior by returning base when canonicalization fails so malformed but base-supported locale inputs degrade safely.

Useful? React with 👍 / 👎.

Signed-off-by: lingrany <250338893@qq.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 739b333891

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".


/** The set of locales that have translation catalogs. */
const SUPPORTED_LOCALES = new Set(['en']);
const SUPPORTED_LOCALES = new Set(['en', 'zh', 'zh-CN']);
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Keep zh locales unsupported until catalogs exist

Adding 'zh' and 'zh-CN' to SUPPORTED_LOCALES makes getLocale() return those values, so the renderer now calls loadMessages('zh' | 'zh-CN'); however the repo only ships src/i18n/messages/en.json (and i18n-compile only compiles files in that folder), so those imports fall back to {} and the app runs with Chinese locale formatting but English UI strings plus a warning on startup. This is a user-visible regression for anyone setting GOOSE_LOCALE=zh*; either commit the corresponding zh catalog(s) or defer advertising these locales as supported.

Useful? React with 👍 / 👎.

Signed-off-by: lingrany <250338893@qq.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 26b8f2c057

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

@@ -0,0 +1,4706 @@
{
"alertBox.autoCompactAt": {
"defaultMessage": "Auto compact at"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Populate zh catalogs with actual Chinese strings

Adding zh/zh-CN as supported locales now routes renderer message loading to these new catalogs, but the catalog content is still English. Fresh evidence: src/i18n/messages/zh.json and src/i18n/messages/zh-CN.json are byte-identical to en.json (for example, alertBox.autoCompactAt is still "Auto compact at"), so users with GOOSE_LOCALE=zh* get English UI strings while other surfaces (like menu/date formatting) switch locale, resulting in a mixed-language experience. This should either ship with real Chinese translations or keep zh* unsupported until translations exist.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks! Addressed in 1ffccb1.

I populated zh/zh-CN catalogs with actual Chinese strings for core, user-visible desktop areas (navigation, settings tabs, queue and key list views), so these locales are no longer byte-identical copies of en.json.

lingrany added 2 commits April 18, 2026 18:57
Signed-off-by: lingrany <250338893@qq.com>
… flows

Signed-off-by: lingrany <250338893@qq.com>
Copy link
Copy Markdown

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 612b5b4356

ℹ️ About Codex in GitHub

Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".

"defaultMessage": "选择语音转文本方式"
},
"dictationSettings.configureApiKey": {
"defaultMessage": "配置 the API key in <b>{设置Path}</b>"
Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Keep ICU variable keys untranslated in zh catalogs

The new Chinese catalogs changed ICU placeholder names (for example {settingsPath} became {设置Path} and {providerName} became {提供方Name}), but call sites still provide settingsPath/providerName values (see dictation/provider settings components). In zh locales, formatMessage cannot resolve these renamed variables, which triggers formatting errors and forces fallback/default text instead of the intended localized string; placeholder identifiers must remain exactly the same as the source message keys in both zh.json and zh-CN.json.

Useful? React with 👍 / 👎.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant