Skip to content

feat: add multilingual localization support with in-app language switching#557

Open
eachann1024 wants to merge 4 commits intosteipete:mainfrom
eachann1024:dev/feat-app-widget-localization
Open

feat: add multilingual localization support with in-app language switching#557
eachann1024 wants to merge 4 commits intosteipete:mainfrom
eachann1024:dev/feat-app-widget-localization

Conversation

@eachann1024
Copy link
Copy Markdown

@eachann1024 eachann1024 commented Mar 17, 2026

Summary

  • add full multilingual localization support for the app and widget, with English, Simplified Chinese, and Traditional Chinese resources
  • introduce a centralized localization layer (AppStrings, AppStringResources, and AppLanguage) so menus, settings, provider details, status text, and widgets share the same string and formatting pipeline
  • add an in-app language preference that switches between System, English, Simplified Chinese, and Traditional Chinese without duplicating view logic

Technical Approach

  • move UI strings and locale-aware formatting behind shared localization helpers instead of scattering literals across views
  • package .lproj resources for both app and widget targets so localized assets ship consistently across surfaces
  • add focused localization tests for string lookup plus usage/reset wording regressions

Boundaries

  • this PR is limited to localization support and runtime language selection; it does not redesign menu visuals or change provider/business logic
  • no generated, cache, screenshot, packaging, or scratch files are included
  • .gitignore was not changed

Validation

  • pnpm check
  • swift build
  • swift test --filter AppStringsLocalizationTests
  • swift test --filter UsagePaceTextTests

@eachann1024 eachann1024 changed the title feat: add app and widget localization with language switching feat: add localization language support Mar 17, 2026
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: 93ede3af1b

ℹ️ 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 on lines +46 to +48
"Show both percentage and pace (e.g. 45%% · +5%%)" = "Show both percentage and pace (e.g. 45%% · +5%%)";
"Show pace indicator (e.g. +5%%)" = "Show pace indicator (e.g. +5%%)";
"Show remaining/used percentage (e.g. 45%%)" = "Show remaining/used percentage (e.g. 45%%)";
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 Correct percent signs in display-mode localization keys

AppStrings.menuBarDisplayModeDescription looks up keys with single % (e.g. "Show pace indicator (e.g. +5%)"), but the entries here are stored with escaped %%, so these lookups never hit and non-English users fall back to the English source text for all three descriptions in the Display settings pane. Using single % in the string keys/values fixes the localization path.

Useful? React with 👍 / 👎.

@eachann1024
Copy link
Copy Markdown
Author

Please click here to view the final video. It seems GitHub no longer allows files larger than 10 MB.
https://cleanshot.com/share/jy0S5jJ1

@eachann1024 eachann1024 changed the title feat: add localization language support feat: add multilingual localization support with in-app language switching Mar 17, 2026
@waffensam
Copy link
Copy Markdown

This PR looks strong overall. I only noticed a few Simplified Chinese terminology details that may be worth tightening before merge:

  • Keep Token untranslated instead of using 令牌 in user-facing UI. Examples: No token accounts yet., Open token file, Show all token accounts.
  • Prefer 花费 over 成本 for user-facing cost copy. This reads more naturally for a usage/credits tracking app. Examples: widget labels like Today cost, 30d cost, and copy such as Show cost summary.
  • There is still one remaining Pace in the Chinese copy (personalize Pace predictions). It would be more consistent to translate that to 节奏 as well.
  • For Widget-related copy, 小组件 is probably a better fit than the more generic 组件 in macOS Chinese UI.

Optional: Usage Dashboard may read a bit more naturally as 用量仪表盘 than 用量面板.

@eachann1024
Copy link
Copy Markdown
Author

This PR looks strong overall. I only noticed a few Simplified Chinese terminology details that may be worth tightening before merge:

  • Keep Token untranslated instead of using 令牌 in user-facing UI. Examples: No token accounts yet., Open token file, Show all token accounts.
  • Prefer 花费 over 成本 for user-facing cost copy. This reads more naturally for a usage/credits tracking app. Examples: widget labels like Today cost, 30d cost, and copy such as Show cost summary.
  • There is still one remaining Pace in the Chinese copy (personalize Pace predictions). It would be more consistent to translate that to 节奏 as well.
  • For Widget-related copy, 小组件 is probably a better fit than the more generic 组件 in macOS Chinese UI.

Optional: Usage Dashboard may read a bit more naturally as 用量仪表盘 than 用量面板.

Understood, I'm happy to improve this part of the content

@eachann1024
Copy link
Copy Markdown
Author

Addressed the terminology feedback in commit 145effa:

Simplified Chinese (zh-Hans):

  • Changed "Token" → "tokens" (lowercase, consistent with technical convention)

Traditional Chinese (zh-Hant):

  • Standardized "Cookie" → "Cookies" (plural form)
  • Unified conjunctions: "與" → "和"
  • Clarified "Token 額度" phrasing
  • Simplified "額外用量花費"

@eachann1024
Copy link
Copy Markdown
Author

Hey @steipete 👋

Just a heads-up that PR #557 is ready for review whenever you get a chance.
One thing worth highlighting: the three languages (en / zh-Hans / zh-Hant) are intentionally a starting point — the real value is the architecture. AppStrings / AppStringResources / AppLanguage gives the app and widget a shared localization pipeline, so adding any new language in the future is just a matter of dropping in a new .lproj bundle. No structural changes needed.

Validated with swift build + swift test. Happy to address any feedback before merge.

@ratulsarna
Copy link
Copy Markdown
Collaborator

@codex review

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: 145effaee2

ℹ️ About Codex in GitHub

Your team has set up Codex to 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 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

ProviderErrorView(
title: "Last \(self.store.metadata(for: self.provider).displayName) fetch failed:",
title: AppStrings.fmt(
"Last %@ fetch failed:",
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 Add missing localization entry for fetch-failure heading

This string is now routed through AppStrings.fmt, but the key "Last %@ fetch failed:" is not present in Sources/CodexBar/Resources/en.lproj/Localizable.strings or the two Chinese Localizable.strings files. Because AppStringResources falls back to the raw key when lookup fails, this heading stays English for non-English app language selections, so provider error UI is not actually localized.

Useful? React with 👍 / 👎.

formatter.maximumFractionDigits = 0
let raw = formatter.string(from: NSNumber(value: value)) ?? "\(value)"
return "\(raw) tokens"
return WidgetStrings.fmt("%@ tokens", raw)
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 Add widget localization key for token-count suffix

The widget now formats token counts via WidgetStrings.fmt("%@ tokens", raw), but "%@ tokens" is missing from all widget localization tables (Sources/CodexBarWidget/Resources/{en,zh-Hans,zh-Hant}.lproj/Localizable.strings). That makes WidgetStrings.tr fall back to the raw English template, so token-count labels in the widget remain English in Chinese locales.

Useful? React with 👍 / 👎.

eachann1024 and others added 4 commits March 30, 2026 18:16
- Simplified Chinese: Use lowercase "tokens" for technical term consistency
- Traditional Chinese: Standardize "Cookies" plural form and conjunctions, clarify "Token 额度" phrasing, simplify "额外用量花费"

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Fix display-mode localization keys using single % instead of double
  %% so lookups actually match the keys used in AppStrings
- Add missing "Last %@ fetch failed:" key to all three app locales so
  provider error headings are properly localized
- Add missing "%@ tokens" key to all three widget locales so token
  count labels in the widget are properly localized

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@eachann1024 eachann1024 force-pushed the dev/feat-app-widget-localization branch from 145effa to 4b64e4e Compare March 30, 2026 10:19
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.

3 participants