Skip to content

#8060: complete i18n foundation with Japanese locale support#17738

Closed
jethac wants to merge 136 commits intogoogle-gemini:mainfrom
jethac:i18n-rebased-2026
Closed

#8060: complete i18n foundation with Japanese locale support#17738
jethac wants to merge 136 commits intogoogle-gemini:mainfrom
jethac:i18n-rebased-2026

Conversation

@jethac
Copy link
Copy Markdown

@jethac jethac commented Jan 28, 2026

Summary

Rebases and extends the approved-but-unmerged i18n work from #8060 by @hoteye,
resolving 1,700+ commits of drift since the original PR, and implements Phase 2
with full Japanese locale support.

Implements Phases 1 & 2 of #6525 (i18n infrastructure + string extraction +
locale switching)
Supersedes #8060 (hoteye's approved PR, blocked by unrelated CI flakiness)

What's Included

Phase 1: From @hoteye's original work (preserved with their authorship):

  • i18next + react-i18next infrastructure
  • useTranslation hook and translation loading system
  • renderStyledText utility for styled interpolation in Ink
  • English locale files structure (common.json, help.json, dialogs.json)
  • i18n test suite

Phase 1: New in this PR:

  • Test environment fix: Initialize i18n in test-setup.ts so tests see
    translated strings
  • Tips localization: 152 informative tips (settings, shortcuts, commands)
    now in loading.json
  • Helper functions: getInformativeTips(),
    getInteractiveShellWaitingPhrase(), getWaitingForConfirmationPhrase()
  • Lint fixes: Resolve ESLint errors from updated rules since original PR

Phase 2: Language selection & Japanese locale:

  • Language setting: experimental.language enum in Settings dialog (Auto /
    English / 日本語)
  • Experimental category: Placed the language setting under the
    "Experimental" section to reflect its WIP status.
  • WIP Translation support: Load and merge translations from
    ~/.gemini/locales/ at runtime—enables translators to test work without
    rebuilding.
  • Dynamic language detection: Options derived from manifest.json in both
    built-in and user-provided locale folders.
  • System language auto-detection: GEMINI_LANGLANG
    Intl.DateTimeFormat'en'
  • Japanese translations: Complete ja/ locale with all 5 namespace files:
    • common.json - UI buttons, status messages
    • dialogs.json - Auth and settings dialog strings
    • help.json - Help section and keyboard shortcuts
    • loading.json - 152 tips translated to natural Japanese
    • commands.json - 76 slash command descriptions
  • Exported utilities: getAvailableLanguages(), isLanguageAvailable(),
    getLanguageOptions()
  • Command description translations: getCommandDescription() helper, Help
    component and slash completion suggestions show translated descriptions
  • Locale manifest system: Each locale folder contains a manifest.json with
    displayName, eliminating hardcoded language labels
  • Documentation: docs/cli/internationalization.md contributor guide +
    GEMINI.md i18n coding conventions (requested by @cornmander in feat(i18n): Phase 1 - Extract strings + i18next foundation #8060)
  • Bundle locale support: Copy i18n locale files to bundle/locales/ so
    they're available when running via npx (esbuild bundle path)
  • Saved language preference: Read ~/.gemini/settings.json directly from
    the experimental section to honor the user's saved language on startup.
  • Update/installation strings: Extracted 15 update notification and package
    manager messages to common:update.*
  • Session stats strings: Extracted 22 session statistics, model usage table,
    and goodbye message strings to dialogs:stats.*

Intentionally deferred to Phase 3:

  • /language command for runtime locale switching without restart
  • Additional locale translations (zh, es, fr, ko, de)
  • Witty loading phrases (culture-specific humor doesn't translate well)

Phased Rollout (per #6525)

Phase Scope Status
Phase 1 i18n infrastructure + string extraction (English only) ✅ This PR
Phase 2 Language option in Settings + locale switching + Japanese ✅ This PR
Phase 3 Additional locales + runtime switching 📋 Future PR

Architecture Highlights

Self-Describing Locale Packs

Each locale folder contains a manifest.json that declares its own display
name. The i18n module scans locales/ at startup, reads each manifest, and
populates the Settings dialog—no hardcoded language labels anywhere:

locales/en/manifest.json  →  { "displayName": "English" }
locales/ja/manifest.json  →  { "displayName": "日本語" }
locales/tlh/manifest.json →  { "displayName": "tlhIngan Hol" }  // works!

System Language Auto-Detection

GEMINI_LANG=ja  →  Japanese (explicit override)
settings.json general.language  →  Saved preference
LANG=ja_JP.UTF-8  →  Japanese (Unix locale)
Intl.DateTimeFormat().resolvedOptions().locale  →  System locale
Fallback  →  English

Commit History

Commit Description
de2f834 @hoteye's foundation (cherry-picked)
a978b22 Fix test environment i18n initialization
b8eaebc Add i18n initialization to CLI entrypoint
65cfa49 Integrate i18n into auth and settings dialogs
b077ee4 Integrate i18n into Help component
a3d5183 Add localization support for informative tips
cc97f71 Update package-lock.json
46b9f26 Add dynamic language detection and utility exports
968d76c Add language setting to settings schema
bc3b6c2 Add Japanese locale translations
23f98fe Cache language options to prevent Settings dialog flickering
ee5503f Add command description translations (Help + slash completion)
3f06fd8 Revert enum useEffect fix (belongs in separate upstream PR)
97510b6 Use manifest.json for locale display names
3a6911a Add internationalization.md guide and GEMINI.md i18n section
b0c48ff Store language as string-with-options to fix Settings flickering
6ddb272 Copy i18n locale files to bundle for npx support
4101c52 Read saved language preference from settings.json
92be116 Extract update and installation strings
e4c647b Extract session stats and summary strings
3a6e6e8 Add format string tip to translation guidelines
9bc0e8a Remove unused cache efficiency imports from StatsDisplay

How to Add a New Locale

  1. Create packages/cli/src/i18n/locales/{lang}/ (e.g., zh/, es/, tlh/)
    OR ~/.gemini/locales/{lang}/ for WIP testing.
  2. Add a manifest.json with { "displayName": "Your Language Name" }
  3. Copy and translate the 5 JSON namespace files from en/
  4. Done! The language will automatically appear in Settings — no code changes
    needed. External locales in ~/.gemini/locales/ are merged with built-in
    ones at runtime.

Testing

cd packages/cli
npm run build  # Passes
npm test       # All tests pass (snapshots updated for new setting)

# Test Japanese locale
GEMINI_LANG=ja npm start

Attribution

The i18next infrastructure and initial component integration is entirely
@hoteye's work from #8060. That PR was approved by @jacob314 and @cornmander but
got stuck on unrelated flaky CI tests and was eventually closed by the stale
bot. This PR preserves hoteye's commit with their authorship intact.


cc: @hoteye @jacob314 @cornmander

@jethac jethac requested review from a team as code owners January 28, 2026 04:03
@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello @jethac, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the CLI by integrating a robust internationalization framework, enabling the application to support multiple languages. It establishes the core infrastructure for translations and introduces comprehensive Japanese language support, making the CLI more accessible to a global user base. The changes also include a flexible system for language detection and management, along with detailed documentation for future localization efforts.

Highlights

  • Internationalization Foundation: Rebases and extends existing i18n work, incorporating i18next and react-i18next infrastructure, useTranslation hook, translation loading system, and renderStyledText utility.
  • Japanese Locale Support: Implements full Japanese locale support, including complete ja/ locale files for common UI strings, dialogs, help, loading tips, and commands.
  • Dynamic Language Selection: Introduces an experimental.language setting in the Settings dialog, allowing users to switch between 'Auto', 'English', and '日本語'. It also supports dynamic language detection via environment variables (GEMINI_LANG, LANG) and Intl.DateTimeFormat.
  • Locale Manifest System: Establishes a self-describing locale pack system where each language folder contains a manifest.json with its displayName, enabling easy addition of new languages without code changes.
  • Comprehensive String Localization: All user-facing strings across various components (auth, settings, tips, command descriptions, session stats, update notifications) have been extracted and localized, ensuring a fully translatable UI.
  • Developer Documentation: Adds a new docs/cli/internationalization.md contributor guide and an i18n section to GEMINI.md, detailing best practices for adding new languages and using translation patterns.
  • Build and Test Integration: Includes a fix for i18n initialization in the test environment, ensures locale files are copied to the bundle/locales/ directory for npx support, and updates package-lock.json with new dependencies.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist Bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request introduces a comprehensive and well-architected internationalization (i18n) foundation to the CLI, including full Japanese locale support. The changes are extensive, touching nearly all UI components, commands, and utility files to extract hardcoded strings and replace them with calls to the new i18n system powered by i18next. Key highlights include a dynamic locale loading system that supports user-provided translations, a robust language detection strategy, and a clever renderStyledText utility for handling styled text within translated strings. The implementation is thorough, including updated documentation, build scripts, and test configurations. The code quality is excellent, and I found no issues that meet the high or critical severity threshold.

@gemini-cli gemini-cli Bot added the priority/p2 Important but can be addressed in a future release. label Jan 28, 2026
@hoteye
Copy link
Copy Markdown
Contributor

hoteye commented Jan 28, 2026

Potential i18n miss: commands.json uses dotted flat keys, but i18next default keySeparator='.'; t('commands:bug.responses.noSandbox') won’t resolve unless you disable the separator or nest the JSON. Thanks for carrying this forward — excited to see it land!

@jethac
Copy link
Copy Markdown
Author

jethac commented Jan 28, 2026

Thanks for the comment @hoteye! Dotted flat key separation seems to not be an issue as of i18next v25 (take a look at this video I recorded here), so I think we're good here.

Also, thanks so much for your initial work! I was talking with @LyalinDotCom the other day about how we lack this feature, and it was a really pleasant surprise to see that you had already done so much to loosen the jar. Likewise, excited to do my part in getting your work into the product!

Now, I'm going to see about getting these checks to pass so it can hopefully be merged before I have to do too much more rebasing... 😅

@jethac jethac changed the title feat(i18n): Phase 1+2 - Complete i18n foundation with Japanese locale support #8060: complete i18n foundation with Japanese locale support Jan 28, 2026
@jethac jethac force-pushed the i18n-rebased-2026 branch 3 times, most recently from c813cb4 to c91eda4 Compare January 28, 2026 19:48
hoteye and others added 18 commits January 28, 2026 11:55
- Set up i18next and react-i18next infrastructure
- Extract UI strings from Help, Auth, and Settings components
- Add English locale files with proper structure
- Implement styledText utility for interpolation
- Convert Help tests to use snapshots per Jacob's feedback
- Add direct component styling verification

This is a pure refactoring with zero behavioral changes.
All tests pass and code functionality remains identical.
Add i18n module import to test-setup.ts to ensure translations are loaded before tests run. This fixes test failures where i18n strings showed raw keys instead of translated text.
Import i18n module in gemini.tsx to enable internationalization across the CLI application.
Replace hardcoded strings with i18n translations in AuthDialog and SettingsDialog components.
Replace hardcoded strings in Help component with i18n translations. Use single-brace placeholders for renderStyledText compatibility.
Add loading.json namespace with 152 informative tips. Export helper functions from i18n module. Keep witty phrases in English (culture-specific).
- Add synchronous detectAvailableLanguagesSync() to scan locales/ at module load
- Add getAvailableLanguages() to get list of available locale packs
- Add isLanguageAvailable() to check if a language has translations
- Add getLanguageOptions() returning options formatted for settings schema
- Add getSystemLanguage() for auto-detection (GEMINI_LANG > LANG > Intl > en)
- Language options are now derived from filesystem, not hardcoded
- Add general.language enum setting with options from getLanguageOptions()
- Options dynamically populated from available locale packs
- Setting requires restart, shows in settings dialog
- Update SettingsDialog snapshots for new language setting
- Add ja/common.json: UI buttons, status messages
- Add ja/dialogs.json: auth and settings dialog strings
- Add ja/help.json: help section, keyboard shortcuts
- Add ja/loading.json: 152 informative tips in natural Japanese

Japanese locale is now fully functional - set GEMINI_LANG=ja or
select '日本語' in Settings to use.
- Compute and freeze language options at module load time
- Return same cached reference from getLanguageOptions()
- Prevents React re-renders from new object references
- Add commands.json locale files (en + ja) with 76 command descriptions
- Add 'commands' namespace to i18n resource loader
- Add getCommandDescription() helper for runtime translation lookup
- Translate command descriptions in Help.tsx and suggestion completions
- Mock i18n in useSlashCompletion tests to isolate completion logic
The useEffect that rebuilds pendingSettings from globalPendingChanges only
handled boolean, number, and string types. Enum values (stored as strings)
were silently dropped, causing the Settings dialog to revert enum changes
(like Language) on re-render.
Each locale folder now contains a manifest.json with a displayName field,
allowing language packs to self-report their name. This eliminates the
hardcoded LANGUAGE_LABELS map and makes the system fully extensible—any
locale (including non-standard codes like 'tlh') just needs a folder
with a manifest.json to appear in the Settings dialog.
Add docs/cli/internationalization.md covering locale structure, manifest
system, translation patterns, and how to add new languages. Update GEMINI.md
with i18n coding conventions so Gemini CLI follows them when generating code.
Both requested by cornmander during google-gemini#8060 review.
…kering

The language setting was type 'enum', but SettingsDialog's useEffect for
globalPendingChanges doesn't handle enum values—a pre-existing upstream bug.
Instead of fixing that generic bug in our i18n PR, change the language
setting to type 'string' (since the value IS a directory name). The
useEffect already handles strings. For presentation, string definitions
with options are shown as cyclable toggles with labels from manifest.json.
The esbuild bundle (bundle/gemini.js) is the entry point when running
via npx, but copy_bundle_assets.js did not copy the i18n locale JSON
files into the bundle directory. This caused detectAvailableLanguagesSync()
to find no locale folders and fall back to English-only.
@jethac jethac force-pushed the i18n-rebased-2026 branch from c91eda4 to 427b84b Compare January 28, 2026 19:57
@jethac
Copy link
Copy Markdown
Author

jethac commented Jan 28, 2026

I think it looks ready for review now - I guess it would make sense to squash all of the non-component-specific changes though.

@jacob314
Copy link
Copy Markdown
Contributor

Great to see you helping to move this forward! However this PR is far too large for us to safely land. Would suggest breaking it down by areas of the project and landing in chunks that are each under 1000 lines.

@gemini-cli
Copy link
Copy Markdown
Contributor

gemini-cli Bot commented Jan 29, 2026

Hi there! Thank you for your contribution to Gemini CLI. We really appreciate the time and effort you've put into this pull request.

To keep our backlog manageable and ensure we're focusing on current priorities, we are closing pull requests that haven't seen maintainer activity for 30 days. Currently, the team is prioritizing work associated with 🔒 maintainer only or help wanted issues.

If you believe this change is still critical, please feel free to comment with updated details. Otherwise, we encourage contributors to focus on open issues labeled as help wanted. Thank you for your understanding!

@gemini-cli gemini-cli Bot closed this Jan 29, 2026
@jethac
Copy link
Copy Markdown
Author

jethac commented Jan 29, 2026

Thanks Jacob! I'll get on that right now as discussed...!

@jethac
Copy link
Copy Markdown
Author

jethac commented Jan 29, 2026

This is a massive and excellent PR that lays a solid foundation for i18n! 弄 The architecture with self-describing locale packs and the extensive migration of existing components is very impressive.

I have a few findings, primarily around testing conventions and one potential architectural consideration.

Testing Conventions

  1. Use Project Test Wrappers:
    In \packages/cli/src/ui/utils/styledText.test.tsx, you are importing
    ender\ directly from \ink-testing-library.
    \\ ypescript
    import { render } from 'ink-testing-library';
    \
    Please change this to use the project's wrapper from ../../test-utils/renderer.js\ (or wherever the test helpers are exported in the actual file structure, likely ../test-utils/renderer.js\ or similar). The project mandates using the custom
    ender\ or
    enderWithProviders\ to ensure \�ct\ is handled correctly and to avoid flaky tests.

  2. Wait For:
    Ensure that any async waiting in new tests uses the custom \waitFor\ from the project's test utils, not \�i.waitFor. While I didn't see explicit \waitFor\ calls in the snippets, it's a general reminder for the new test files if you add async assertions later.

Architecture & Design

  1. Top-Level Await in \i18n/index.ts:
    \\ ypescript
    // Initialize i18n immediately
    await initializeI18n();
    \
    You are using top-level await to initialize i18n. Since this project requires Node.js >= 20.0.0, this is technically supported. However, please ensure this doesn't negatively impact the startup time for commands that might not need the full UI (though almost all do). It forces the module system to wait, which is likely the intent, but just double-checking that this was intentional to block app start until translations are ready.

  2. Synchronous File I/O:
    In \packages/cli/src/i18n/index.ts:
    \\ ypescript
    function scanLocalesDir(...) { ... fsSync.readdirSync ... }
    \
    You are using synchronous file I/O (\ sSync) at module level. Generally, we avoid sync I/O to prevent blocking the event loop. Since this runs once at startup, it might be acceptable, but if the \locales\ directory grows or if this runs in a context where latency matters critically (like tab completion), it could be a bottleneck. Consider if this can be made async or if the impact is negligible.

React Best Practices

  1. useTranslation Dependencies:
    Great job adding \ \ to the dependency arrays of your \useMemo\ and \useCallback\ hooks (e.g., in \SettingsDialog.tsx). This is often missed!

  2. styledText Utility:
    The
    enderStyledText\ utility is a clever solution for the Ink environment.
    \\ ypescript
    export function renderStyledText(...)
    \
    The implementation looks safe with the regex splitting. Good job adding the validation to throw errors on mismatching placeholders 窶 this will save a lot of debugging time for translators!

Documentation

  1. GEMINI.md:
    Thank you for adding the Internationalization (i18n) section to \GEMINI.md. This is crucial for maintaining the pattern in future PRs.

Overall, this is a fantastic contribution. Just fixing the test renderer import is the main blocker.

@jethac
Copy link
Copy Markdown
Author

jethac commented Jan 29, 2026

I have a few additional findings to add to my previous review:

PR Standards

  1. PR Title:
    The PR title feat(i18n): Phase 1 - Extract strings + i18next foundation #8060: complete i18n foundation with Japanese locale support\ does not follow the Conventional Commits standard. Please update it to something like \ eat(i18n): complete foundation and add Japanese support.

Quality & Risks

  1. Model Context Quality:
    This PR translates slash command descriptions and system responses (like bug reports, directory status, etc.). Please ensure @anj-s (or the appropriate quality owner) is tagged to review these changes. We need to be certain that translating these system-descriptive strings doesn't degrade Gemini's ability to understand the CLI's state when users share output with the model.

Validations

  1. Test Wrappers (Confirmed):
    I verified that the existing test file \packages/cli/src/ui/components/SettingsDialog.test.tsx\ correctly uses the project's
    ender\ wrapper. The issue I flagged in point docs: Add setup instructions for API key to README #1 is specific to the new test file \packages/cli/src/ui/utils/styledText.test.tsx.
    I also verified that \packages/cli/src/ui/hooks/useSlashCompletion.test.ts\ correctly uses the project's test wrappers. 弄

  2. State Management (Confirmed):
    I reviewed \SettingsDialog.tsx\ and confirmed that the side effects (like \saveModifiedSettings\ and \ oggleVimEnabled) are correctly placed within the event handler body and not inside \setState\ callbacks. This looks correct.

Thanks again for the great work!

@jethac
Copy link
Copy Markdown
Author

jethac commented Jan 29, 2026

(sorry for the noise, friends - it was suggested that i run /review-frontend 17738 on this, and TIL this command existed - what a great tool!)

@jethac
Copy link
Copy Markdown
Author

jethac commented Jan 29, 2026

Huh, interesting - I am on Windows and I have my Language for non-Unicode programs setting set to Japanese (Japan), and I think, but cannot yet confirm, that that's causing garbled characters in the output of /review-frontend - will try and repro and fix after I land this i18n issue.

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

Labels

priority/p2 Important but can be addressed in a future release.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants