Skip to content

Conversation

@RIVALHIDE
Copy link
Contributor

@RIVALHIDE RIVALHIDE commented Jan 14, 2026

Related Issue

Closes #93

Summary

Adds comprehensive multi-language support (i18n) to the Cortex CLI with proper localization, formatting, tests, and documentation.

Supported Languages

Code Language Native Name
en English English
es Spanish Español
fr French Français
de German Deutsch
zh Chinese 中文

Key Features

  • ✅ Scalable i18n framework with externalized messages in YAML catalogs
  • ✅ Language selection via cortex config language <code> or cortex --set-language "English"
  • ✅ OS language auto-detection from LANG, LC_ALL, LANGUAGE environment variables
  • ✅ Persistent language preferences stored in ~/.cortex/preferences.yaml
  • ✅ Locale-aware formatting for dates, times, and numbers using Babel
  • ✅ Graceful fallback to English for missing translations
  • ✅ Debug mode (CORTEX_I18N_DEBUG=1) to show translation keys
  • ✅ 100% CLI messages translated (no English leaks)
  • ✅ Unit tests with >80% coverage

Usage Examples

Set language by code

cortex config language es

Set language by name (human-readable)

cortex --set-language "Spanish"

View current language info

cortex config language --info

List available languages

cortex config language --list

Use auto-detection

cortex config language --auto

Clear preference (use OS default)

cortex config language --clear

Files Changed

File Change Reason
pyproject.toml Modified Added cortex.i18n package, locale YAML files as package data, upgraded setuptools to >=64.0 for editable install support
MANIFEST.in Modified Added recursive-include cortex/i18n/locales *.yaml to include translations in PyPI distribution
cortex/cli.py Modified Integrated i18n throughout CLI - replaced hardcoded strings with t() calls, added config language subcommand, added --set-language global flag
cortex/i18n/__init__.py Added Module initialization, exports core i18n API (t, set_language, get_language, etc.)
cortex/i18n/translator.py Added Core translation engine - loads YAML catalogs, handles interpolation, manages fallback to English
cortex/i18n/config.py Added Language preference persistence to ~/.cortex/preferences.yaml, preference resolution order
cortex/i18n/detector.py Added OS language auto-detection from environment variables (LANG, LC_ALL, LANGUAGE, LC_MESSAGES)
cortex/i18n/formatter.py Added Locale-aware formatting for numbers, dates, time-ago, durations, and file sizes using Babel
cortex/i18n/locales/en.yaml Added English message catalog (source language) - 376 lines
cortex/i18n/locales/es.yaml Added Spanish translations - 100% coverage
cortex/i18n/locales/fr.yaml Added French translations - 100% coverage
cortex/i18n/locales/de.yaml Added German translations - 100% coverage
cortex/i18n/locales/zh.yaml Added Chinese (Simplified) translations - 100% coverage
tests/test_i18n.py Added Comprehensive test suite - translator, config, detector, formatter, CLI integration (>80% coverage)
docs/i18n-guide.md Added Documentation for translators and developers on using/extending the i18n system

AI Disclosure

  • AI/IDE/Agents used (please describe below)

Used Cursor AI (Claude opus 4.5) to assist with:

  • Writing comprehensive unit tests
  • Integrating i18n into existing CLI commands
  • Debugging and fixing linting/type-checking issues
  • Creating translator documentation

Checklist

  • PR title follows format: type(scope): description or [scope] description
  • Tests pass (pytest tests/)
  • MVP label added if closing MVP issue
  • Update "Cortex -h" (if needed)

Architecture

Language Resolution Order

  1. CORTEX_LANGUAGE environment variable (highest priority)
  2. User preference in ~/.cortex/preferences.yaml
  3. OS-detected language from LANG/LC_ALL
  4. Default: English

Demo video

Screencast.from.14-01-26.07.13.17.PM.IST.webm

Summary by CodeRabbit

  • New Features

    • Full i18n support (en, es, fr, de, zh): localized CLI messages, translation catalogs, translator API, and locale-aware formatting/parsing.
    • CLI language management: list/info/set, --language/--set-language early switch, auto-detect and persist preference, and reset.
  • Documentation

    • Added comprehensive i18n guide covering usage, adding languages, CLI commands, and troubleshooting.
  • Tests

    • Extensive unit and integration tests for translations, detection, persistence, and formatting; e2e assertions relaxed for localized outputs.
  • Chores

    • Packaging updated to include locale files.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

📝 Walkthrough

Walkthrough

Adds a complete i18n subsystem (translator, catalogs, detector, formatter, persisted LanguageConfig), integrates language selection and auto-detection into the CLI (global flag and config subcommands), ships five locale catalogs, updates packaging and docs, expands tests, and relaxes some integration assertions for localized outputs.

Changes

Cohort / File(s) Summary of changes
i18n core
cortex/i18n/translator.py, cortex/i18n/__init__.py, cortex/i18n/config.py, cortex/i18n/detector.py, cortex/i18n/formatter.py
New i18n subsystem: Translator (catalog loading, t()), global translator management, LanguageConfig (persistence, env override, atomic write + locking), OS locale detection, and LocaleFormatter (date/number/time/file-size formatting). Exposes public helpers/constants (SUPPORTED_LANGUAGES, get/set language, etc.).
Locale catalogs
cortex/i18n/locales/en.yaml, cortex/i18n/locales/de.yaml, cortex/i18n/locales/es.yaml, cortex/i18n/locales/fr.yaml, cortex/i18n/locales/zh.yaml
Added English source catalog and full translations for German, Spanish, French, and Chinese (data-only YAMLs with placeholders aligned to CLI messages).
CLI integration
cortex/cli.py
Wired i18n into CLI: replaced many user-facing strings with translation lookups, added config subcommand (language/show), global --set-language/--language handling before dispatch, language resolution/normalization helpers, and translator reset behavior on language changes.
Packaging & docs
pyproject.toml, docs/i18n-guide.md
Include cortex.i18n package and locale YAMLs in package data; add an i18n guide covering developer and translator workflows, catalog structure, and CLI usage.
Tests & integration
tests/test_i18n.py, tests/integration/test_end_to_end.py
New comprehensive unit tests for translator, detector, formatter, and LanguageConfig; CLI language handling tests; relaxed end-to-end assertions to accept localized output variants.

Sequence Diagram(s)

sequenceDiagram
  participant CLI as "CLI (user)"
  participant Config as "LanguageConfig\n(~/.cortex/preferences.yaml)"
  participant Translator as "Translator\n(catalogs)"
  participant OS as "OS env\n(LANG/LC_*/LANGUAGE)"
  participant FS as "Filesystem"

  rect rgba(200,220,255,0.5)
  CLI->>Config: request current language (startup / config show)
  Config-->>CLI: return language (env override / saved / detected)
  end

  rect rgba(200,255,200,0.5)
  CLI->>Translator: set language (flag / config set)
  Translator->>Translator: load/reload catalogs (en + target)
  Translator-->>CLI: translations available
  end

  rect rgba(255,230,200,0.5)
  Translator->>FS: read locales/{lang}.yaml
  Config->>FS: read/write preferences.yaml (atomic + lock)
  end

  rect rgba(240,200,255,0.5)
  OS-->>Config: provide detected locale (detect_os_language)
  Config-->>Translator: fallback language if no saved/env
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

enhancement

Suggested reviewers

  • Suyashd999
  • Anshgrover23

Poem

"I nibbled YAML, hopped through keys with delight,
Catalogs in five tongues now sparkle at night.
I set the language, watched messages sing,
The CLI learned new words — the rabbit did spring! 🐇✨"

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The PR title 'feat: add i18n support for 5 languages (en, es, fr, de, zh)' clearly and concisely describes the primary change—adding internationalization support with specific language codes.
Description check ✅ Passed The PR description is comprehensive and complete. It includes all required template sections: Related Issue (#93), detailed Summary with supported languages, AI Disclosure (Cursor AI), Checklist with items marked, Usage Examples, Files Changed table, and Architecture details.
Linked Issues check ✅ Passed All coding requirements from issue #93 are met: i18n framework implemented, 5 languages supported (exceeding 3 minimum), language auto-detection from env vars, user preference persistence in ~/.cortex/preferences.yaml, complete message translation with fallback, locale-aware formatting, and comprehensive unit tests (>80% coverage).
Out of Scope Changes check ✅ Passed All changes are scoped to i18n implementation and CLI integration. The PR adds the cortex.i18n package, updates pyproject.toml and MANIFEST.in for packaging, integrates translations in cortex/cli.py, adds tests and documentation. Integration test relaxation (header and success message assertions) is a necessary adaptation for i18n output variations.
Docstring Coverage ✅ Passed Docstring coverage is 95.56% which is sufficient. The required threshold is 80.00%.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @RIVALHIDE, 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 Cortex CLI by integrating full internationalization capabilities. It allows the CLI to adapt to the user's language preference, providing a localized experience across all messages and data formatting. This change broadens the accessibility and usability of Cortex for a global user base, making it more intuitive and user-friendly regardless of their native language.

Highlights

  • Multi-language Support: Introduces comprehensive internationalization (i18n) support for the Cortex CLI, enabling it to display messages in 5 languages: English, Spanish, French, German, and Chinese (Simplified).
  • Flexible Language Selection: Users can select their preferred language via a new cortex config language subcommand, a global --set-language flag, or through automatic detection from OS environment variables. Preferences are persistently stored.
  • Locale-Aware Formatting: Implements locale-specific formatting for dates, times, numbers, and file sizes, ensuring a natural and culturally appropriate display for users in different regions.
  • Robust Translation Framework: A scalable i18n framework is established using externalized messages in YAML catalogs, featuring graceful fallback to English for any missing translations and a debug mode to aid development.
  • Comprehensive Coverage: All existing CLI messages have been translated (100% coverage), and the new i18n system is backed by extensive unit tests with over 80% coverage.
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
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-structured internationalization (i18n) framework, which is a fantastic addition to the CLI. The implementation is thorough, covering translation, configuration, auto-detection, formatting, and testing. The new documentation and test suite are particularly impressive. My review focuses on a few areas for improvement to enhance maintainability and performance:

  • Resolving a circular dependency within the i18n module to improve the overall design and remove code duplication.
  • Optimizing a language name resolution function for better performance.
  • Suggesting a more direct use of the babel library for formatting to simplify the code and improve scalability.

Overall, this is a very strong contribution that significantly enhances the user experience.

I am having trouble creating individual review comments. Click here to see my feedback.

cortex/i18n/config.py (20-21)

high

This SUPPORTED_LANGUAGES constant is a duplicate of the one defined in cortex/i18n/translator.py. This duplication can lead to maintenance issues if the two get out of sync. It appears to be a workaround for a circular dependency between config.py and translator.py.

To resolve this, I recommend creating a single source of truth in a new file, for example cortex/i18n/constants.py. Both modules could then import from this new file, which would break the dependency cycle and eliminate the duplicated constant.

Example for cortex/i18n/constants.py:

SUPPORTED_LANGUAGES: dict[str, dict[str, str]] = {
    "en": {"name": "English", "native": "English"},
    # ... other languages
}
DEFAULT_LANGUAGE = "en"

This change would significantly improve the design and robustness of the i18n module.

cortex/cli.py (978-981)

medium

This local import (from cortex.i18n.translator import reset_translator) is used here and in other places to work around a circular dependency. While this pattern works, it's often a sign of a design issue that could be improved.

Resolving the underlying circular dependency (as suggested in another comment regarding cortex/i18n/config.py) would allow this import to be moved to the top of the file, improving code readability and structure.

            lang_config.clear_language()
            reset_translator()

cortex/cli.py (2140-2178)

medium

The _resolve_language_name function rebuilds the name_to_code lookup dictionary on every call. Since the list of supported languages is static, this dictionary can be created once and cached for better performance.

You can achieve this by attaching the lookup table to the function object itself, so it's only computed on the first call. This avoids polluting the module's global namespace while still being efficient.

def _resolve_language_name(name: str) -> str | None:
    """
    Resolve a language name or code to a supported language code.

    Accepts:
    - Language codes: en, es, fr, de, zh
    - English names: English, Spanish, French, German, Chinese
    - Native names: Español, Français, Deutsch, 中文

    Args:
        name: Language name or code (case-insensitive for Latin scripts)

    Returns:
        Language code if found, None otherwise
    """
    if not hasattr(_resolve_language_name, "_lookup"):
        lookup = {}
        for code, info in SUPPORTED_LANGUAGES.items():
            lookup[code] = code
            lookup[info["name"].lower()] = code
            lookup[info["native"].lower()] = code
            lookup[info["native"]] = code  # For case-sensitive names like 中文
        _resolve_language_name._lookup = lookup

    stripped_name = name.strip()
    # Check lowercased version first, then original for case-sensitive names
    return _resolve_language_name._lookup.get(stripped_name.lower()) or _resolve_language_name._lookup.get(stripped_name)

cortex/i18n/formatter.py (148-153)

medium

The LocaleFormatter class provides a custom implementation for formatting dates, numbers, and other data types. While functional, this approach is difficult to maintain and scale, as it requires adding large configuration blocks to LOCALE_CONFIGS for each new language.

The pull request description mentions using Babel. I recommend refactoring this class to use the babel library's formatting functions directly. This would leverage its extensive locale data, simplify the code, and make it more robust.

For example, format_date could be simplified to:

from babel.dates import format_date as babel_format_date

def format_date(self, dt: datetime) -> str:
    """Formats a date using babel."""
    return babel_format_date(dt, locale=self.language)

Similar refactoring can be applied to format_number, format_datetime, and format_time_ago, which would remove the need for the large LOCALE_CONFIGS dictionary.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@cortex/i18n/config.py`:
- Around line 54-60: The TOCTOU arises because the existence check for
self.preferences_file happens before acquiring self._file_lock; move the
exists() check inside the critical section so the lock covers both the check and
the file read in _load_preferences (acquire self._file_lock, then if
self.preferences_file.exists(): open/read with yaml.safe_load and return or {}),
and keep the existing exception handling (yaml.YAMLError, OSError) to handle
race-related failures.

In `@cortex/i18n/formatter.py`:
- Around line 358-370: get_formatter currently caches a LocaleFormatter once and
never updates it when the app language changes; modify get_formatter so that
after importing and calling get_language() it compares that language to the
existing _formatter's language and, if different, either set _formatter.language
to the new value (if LocaleFormatter exposes a setter) or re-create _formatter =
LocaleFormatter(language=new_lang); ensure this logic lives inside get_formatter
so every access stays in sync and also handle the case where _formatter is None
by creating it as before.
🧹 Nitpick comments (8)
docs/i18n-guide.md (1)

188-195: Add language specifier to fenced code block.

The code block showing the directory structure is missing a language specifier, which triggers a markdown lint warning (MD040). Use text or plaintext for non-code content.

🛠️ Suggested fix
-```
+```text
 cortex/i18n/locales/
 ├── en.yaml    # English (source language)
 ├── es.yaml    # Spanish
 ├── fr.yaml    # French
 ├── de.yaml    # German
 └── zh.yaml    # Chinese
</details>

</blockquote></details>
<details>
<summary>tests/test_i18n.py (3)</summary><blockquote>

`649-659`: **Path construction may be fragile across different test execution contexts.**

The path construction at line 655 assumes a specific relative directory structure from the test file location. This could break if tests are run from different working directories or if the project structure changes.



<details>
<summary>♻️ Suggested improvement using package resource discovery</summary>

```diff
     def test_all_supported_languages_have_catalogs(self):
         """Test that all supported languages have message catalogs."""
-        from pathlib import Path
-
         from cortex.i18n.translator import SUPPORTED_LANGUAGES
+        import importlib.resources
 
-        locales_dir = Path(__file__).parent.parent / "cortex" / "i18n" / "locales"
+        # Use importlib.resources for reliable package resource discovery
+        try:
+            # Python 3.9+
+            locales_ref = importlib.resources.files("cortex.i18n") / "locales"
+        except AttributeError:
+            # Fallback for older Python
+            from pathlib import Path
+            locales_dir = Path(__file__).parent.parent / "cortex" / "i18n" / "locales"
+        else:
+            locales_dir = locales_ref
 
         for lang_code in SUPPORTED_LANGUAGES:
-            catalog_path = locales_dir / f"{lang_code}.yaml"
-            self.assertTrue(catalog_path.exists(), f"Missing catalog for language: {lang_code}")
+            # Check if catalog exists
+            catalog_name = f"{lang_code}.yaml"
+            if hasattr(locales_dir, 'joinpath'):
+                catalog_path = locales_dir.joinpath(catalog_name)
+                exists = catalog_path.is_file() if hasattr(catalog_path, 'is_file') else True
+            else:
+                catalog_path = locales_dir / catalog_name
+                exists = catalog_path.exists()
+            self.assertTrue(exists, f"Missing catalog for language: {lang_code}")

Alternatively, you could verify catalog existence by attempting to load them through the Translator class, which already handles resource discovery internally.


20-21: Unused import: yaml.

The yaml module is imported but never used in the test file. This was likely intended for direct YAML parsing but the tests use the Translator class instead.

🧹 Remove unused import
 from unittest.mock import MagicMock, patch
 
-import yaml
-
 
 class TestTranslator(unittest.TestCase):

18-18: Unused import: MagicMock.

MagicMock is imported but not used in this test file. Only patch is used from unittest.mock.

🧹 Remove unused import
-from unittest.mock import MagicMock, patch
+from unittest.mock import patch
cortex/i18n/config.py (1)

19-21: Consider renaming to avoid confusion with translator.SUPPORTED_LANGUAGES.

The comment acknowledges the circular import concern, but using the same name for different types (set here vs dict in translator.py) can confuse maintainers. Consider renaming to SUPPORTED_LANGUAGE_CODES to clarify it's just for membership checking.

cortex/cli.py (1)

1164-1177: Consider narrowing the exception type.

The catch-all Exception on line 1175 masks the specific error. Since set_language raises ValueError and _save_preferences raises RuntimeError, catching these specifically would provide better error handling.

♻️ Proposed refinement
         try:
             lang_config.set_language(code)
             # Reset the global translator to pick up the new language
             from cortex.i18n.translator import reset_translator

             reset_translator()
             set_language(code)

             lang_info = SUPPORTED_LANGUAGES[code]
             cx_print(t("language.changed", language=lang_info["native"]), "success")
             return 0
-        except Exception as e:
+        except (ValueError, RuntimeError) as e:
             self._print_error(f"Failed to set language: {e}")
             return 1
cortex/i18n/formatter.py (1)

330-351: Duration format strings are not localized.

The method uses hardcoded English abbreviations ("ms", "s", "m", "h"). While this is acceptable for a CLI tool (these abbreviations are widely understood), consider adding localized duration strings to LOCALE_CONFIGS for full i18n consistency if needed in the future.

cortex/i18n/translator.py (1)

173-180: Consider catching additional interpolation errors.

The str.format() method can raise IndexError for positional arguments and ValueError for malformed format strings. While KeyError handles named arguments, broader exception handling would be safer.

♻️ Proposed refinement
         # Interpolate variables
         if kwargs:
             try:
                 message = message.format(**kwargs)
-            except KeyError:
+            except (KeyError, IndexError, ValueError):
                 # If interpolation fails, return message without interpolation
                 pass
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 232c056 and fe4f64a.

📒 Files selected for processing (14)
  • cortex/cli.py
  • cortex/i18n/__init__.py
  • cortex/i18n/config.py
  • cortex/i18n/detector.py
  • cortex/i18n/formatter.py
  • cortex/i18n/locales/de.yaml
  • cortex/i18n/locales/en.yaml
  • cortex/i18n/locales/es.yaml
  • cortex/i18n/locales/fr.yaml
  • cortex/i18n/locales/zh.yaml
  • cortex/i18n/translator.py
  • docs/i18n-guide.md
  • pyproject.toml
  • tests/test_i18n.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide for Python code
Include type hints in Python code
Add docstrings for all public APIs in Python code
Use dry-run mode by default for all installation operations
Do not use silent sudo - require explicit user confirmation for privilege escalation
Implement Firejail sandboxing for execution of untrusted code
Log all operations to ~/.cortex/history.db for audit purposes

Files:

  • cortex/i18n/detector.py
  • cortex/i18n/config.py
  • cortex/i18n/__init__.py
  • tests/test_i18n.py
  • cortex/cli.py
  • cortex/i18n/formatter.py
  • cortex/i18n/translator.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain test coverage above 80% for pull requests

Files:

  • tests/test_i18n.py
🧬 Code graph analysis (3)
cortex/i18n/config.py (2)
cortex/i18n/detector.py (1)
  • detect_os_language (104-144)
cortex/i18n/translator.py (5)
  • get_language (279-286)
  • set_language (265-276)
  • language (61-63)
  • language (66-83)
  • get_language_info (318-334)
cortex/i18n/formatter.py (3)
cortex/i18n/translator.py (3)
  • language (61-63)
  • language (66-83)
  • get_language (279-286)
cortex/cli.py (1)
  • config (1080-1094)
cortex/i18n/config.py (1)
  • get_language (77-107)
cortex/i18n/translator.py (2)
cortex/i18n/formatter.py (2)
  • language (165-167)
  • language (170-175)
cortex/i18n/config.py (3)
  • get_language (77-107)
  • set_language (109-128)
  • get_language_info (139-176)
🪛 markdownlint-cli2 (0.18.1)
docs/i18n-guide.md

188-188: Fenced code blocks should have a language specified

(MD040, fenced-code-language)

🔇 Additional comments (29)
cortex/i18n/locales/zh.yaml (1)

1-307: LGTM! Comprehensive Chinese translations.

The Chinese (Simplified) locale file is well-structured, mirrors the English source catalog, and preserves all placeholders for dynamic value interpolation. The translations cover all required sections for CLI i18n support.

pyproject.toml (2)

2-2: Setuptools version bump looks appropriate.

The bump to setuptools>=64.0 provides better support for package-data configuration. This version has been stable since 2022.


101-106: Package configuration correctly includes i18n module and locale data.

The cortex.i18n package is properly added to the packages list, and the package-data entry ensures YAML locale files are included in distributions. This enables runtime loading of translation catalogs.

cortex/i18n/detector.py (4)

14-52: Well-structured language mappings with comprehensive variant coverage.

The SUPPORTED_LANGUAGES set and LANGUAGE_MAPPINGS dictionary properly cover regional variants for all five supported languages. The C/POSIX locale defaults to English, which follows standard POSIX behavior.


55-101: Robust locale parsing with comprehensive fallback strategies.

The _parse_locale function handles multiple locale string formats well (e.g., en_US.UTF-8, es_ES, zh-CN). The progressive fallback through direct mapping, language prefix, and full locale lookup ensures maximum compatibility.

Type hints and docstring are present per coding guidelines.


104-144: Correct implementation of OS language detection.

The function follows the standard GNU gettext priority order (LANGUAGELC_ALLLC_MESSAGESLANG) and correctly handles the colon-separated LANGUAGE variable. The fallback to English ensures graceful degradation.

Docstring with examples and type hints are present per coding guidelines.


147-162: Useful debugging utility for locale information.

The get_os_locale_info function provides comprehensive locale environment variable exposure plus the computed detected_language, which is valuable for troubleshooting i18n issues.

cortex/i18n/locales/de.yaml (1)

1-307: LGTM! Comprehensive German translations.

The German locale file is well-structured, mirrors the English source catalog, and preserves all placeholders for dynamic value interpolation. The translations appear natural and cover all required CLI sections.

cortex/i18n/locales/es.yaml (1)

1-307: LGTM! Comprehensive Spanish translations.

The Spanish locale file is well-structured, mirrors the English source catalog, and preserves all placeholders for dynamic value interpolation. The translations cover all required CLI sections for complete i18n support.

cortex/i18n/locales/en.yaml (1)

1-375: Well-documented source catalog with excellent translator guidance.

The English source file is well-structured with:

  • Clear variable documentation comments above interpolated messages (e.g., lines 35-38, 44-45)
  • Consistent snake_case key naming
  • Logical grouping by feature/module
  • Comprehensive coverage of all CLI messages

This serves as an excellent reference for translators.

docs/i18n-guide.md (1)

1-480: Comprehensive and well-organized i18n documentation.

The guide thoroughly covers all aspects of the i18n system including usage examples, adding new languages, translation guidelines, and troubleshooting. The language resolution order documentation (lines 422-429) is particularly valuable for understanding priority.

cortex/i18n/__init__.py (1)

1-58: Clean public API surface with comprehensive documentation.

The module initialization properly:

  • Provides a centralized entry point for all i18n functionality
  • Documents usage examples in the module docstring (lines 10-20)
  • Organizes __all__ exports by category for clarity
  • Lists all supported languages in the docstring
tests/test_i18n.py (1)

1-977: Comprehensive test suite with excellent coverage of i18n components.

The test suite covers:

  • Core translator functionality including language switching, interpolation, and fallback
  • Configuration persistence with proper temp directory isolation
  • OS language detection with various environment variable combinations
  • Locale-aware formatting across all supported languages
  • CLI integration for language commands
  • Language name resolution for both codes and human-readable names

The use of setUp/tearDown with reset_translator() ensures proper test isolation. As per coding guidelines, this appears to meet the >80% coverage target for the i18n module.

cortex/i18n/locales/fr.yaml (1)

1-307: French translations are complete and properly localized.

The translation catalog maintains consistent structure with the English source and correctly preserves all functional {variable} placeholders for interpolation. Verification confirms all 22 placeholders used in translations ({action}, {code}, {count}, {current}, {description}, {end}, {error}, {id}, {language}, {name}, {original}, {package}, {passed}, {path}, {provider}, {rate}, {seconds}, {start}, {step}, {suggested}, {total}, {version}) match exactly between en.yaml and fr.yaml. Cultural adaptations like "NPD" for "DND" are appropriate.

cortex/i18n/config.py (2)

109-128: LGTM!

The set_language method properly validates input, normalizes to lowercase, and provides a clear error message with supported options.


139-176: LGTM!

The get_language_info method provides excellent diagnostic information with clear source attribution (environment/config/auto-detected/default), which is valuable for debugging language resolution issues.

cortex/cli.py (6)

25-31: LGTM!

The i18n imports are well-organized and include all necessary components for CLI internationalization.


158-161: LGTM!

Good use of translation keys for user-facing API key messages.


2307-2345: LGTM!

Excellent helper function that provides a user-friendly way to specify languages by code, English name, or native name. The case-insensitive matching for Latin scripts while preserving exact matching for non-Latin scripts (like 中文) is thoughtfully implemented.


2348-2386: LGTM!

The _handle_set_language function provides excellent UX by listing all supported languages when an invalid input is provided, helping users discover available options.


2917-2927: LGTM!

Smart handling that allows combining language setting with command execution (e.g., cortex --set-language es install nginx), providing a convenient one-liner workflow.


2636-2655: LGTM!

The config parser setup properly mirrors the implementation with appropriate argument definitions for the language subcommand.

cortex/i18n/formatter.py (3)

14-145: LGTM!

Well-researched locale configurations. The French file size units ("o", "Ko", "Mo", "Go", "To") correctly follow the French convention of using "octet" instead of "byte".


284-328: LGTM!

The format_time_ago method correctly handles singular/plural forms and covers all common time intervals from seconds to years. The early return for negative differences (future dates) gracefully handles edge cases.


169-175: Bug: Language setter checks wrong variable.

Line 172 checks self._language (the current language) instead of value (the new language being set). This means the setter always succeeds if the current language is valid, regardless of whether the new value is valid.

🐛 Proposed fix
     `@language.setter`
     def language(self, value: str) -> None:
         """Set the language."""
-        if self._language in LOCALE_CONFIGS:
+        if value in LOCALE_CONFIGS:
             self._language = value
         else:
             self._language = "en"

Likely an incorrect or invalid review comment.

cortex/i18n/translator.py (4)

95-116: LGTM!

Good defensive implementation. Using yaml.safe_load protects against arbitrary code execution, and the silent fallback for missing catalogs ensures graceful degradation.


247-262: LGTM!

Clean lazy initialization with appropriate use of LanguageConfig for language resolution. The debug mode via CORTEX_I18N_DEBUG environment variable is valuable for translation development and testing.


289-309: LGTM!

The t() function provides a clean, concise API for translations throughout the codebase. The docstring examples clearly demonstrate usage patterns.


337-344: LGTM!

Returning a copy of SUPPORTED_LANGUAGES is good defensive programming, preventing accidental mutation of the module-level constant.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@Anshgrover23
Copy link
Collaborator

@coderabbitai Review.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 14, 2026

@Anshgrover23: I'll review the changes in this PR now.

✅ Actions performed

Review triggered.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

Copy link
Collaborator

@Anshgrover23 Anshgrover23 left a comment

Choose a reason for hiding this comment

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

@RIVALHIDE Added some comments.
Also, No test for malformed YAML handling. There are still some coderabbit comments left to address.

@Anshgrover23 Anshgrover23 marked this pull request as draft January 15, 2026 07:50
@github-actions
Copy link

github-actions bot commented Jan 16, 2026

CLA Verification Passed

All contributors have signed the CLA.

Contributor Signed As
@RIVALHIDE @RIVALHIDE
@Anshgrover23 @Anshgrover23
@Suyashd999 @Suyashd999

@Suyashd999
Copy link
Collaborator

@RIVALHIDE will it be able to handle translation for any new command that is added to the Cortex in the future ?

@Anshgrover23 Anshgrover23 marked this pull request as ready for review January 16, 2026 12:30
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 6

🤖 Fix all issues with AI agents
In `@cortex/cli.py`:
- Around line 1503-1530: The output for `config language` leaks untranslated
English: replace raw source keys and hardcoded hint strings in the `--info`
printing block and the "no code" branch with the existing translation calls used
by `_config_show` and other `config.*` keys; specifically, use the `t(...)`
function for labels like environment/source/detected_language (instead of raw
"environment") and swap the hardcoded hints shown in the no-code branch (the two
console.print lines that start with "[dim]Use: cortex config language...") to
call the appropriate `t('config.*')` keys (or reuse the same keys `_config_show`
uses) and output via `cx_print`/`console.print` so all user-facing text in
`get_language`, `SUPPORTED_LANGUAGES` display, and the `--info` branch is
localized.
- Around line 2972-3043: The lookup fails for accented Latin names because
_normalize_for_lookup and _resolve_language_name use .lower(); change
normalization to use .casefold() for ASCII/Latin handling so accented inputs
like "español" or "français" match; update _normalize_for_lookup to return
s.casefold() when _is_ascii(s) (or otherwise when ASCII/Latin detection applies)
and then in _resolve_language_name build keys with english_name.casefold(), use
native_normalized = _normalize_for_lookup(native_name) and still preserve the
original native_name in name_to_code for exact matches; ensure
SUPPORTED_LANGUAGES checks use the same normalization logic.

In `@cortex/i18n/detector.py`:
- Around line 95-121: The locale parsing in the detector (working on
locale_string and LANGUAGE_MAPPINGS) only handles underscores and ignores
hyphenated locales like "en-US"; update the normalization so that hyphens are
converted to underscores (e.g., replace "-" with "_") immediately after
lowercasing/stripping and before removing encoding suffix and performing
lookups, then use the normalized full_locale and language part (from split("_"))
when checking LANGUAGE_MAPPINGS in the existing branches (direct mapping,
language part, regional variant).

In `@cortex/i18n/locales/en.yaml`:
- Around line 120-128: The file contains duplicate top-level keys "cache" and
"env" so later blocks overwrite earlier translations and drop keys like
cache.saved_calls and cache.read_error and many env.* labels; locate all "cache"
and "env" blocks (e.g., the cache block with keys stats_header, hits, misses,
hit_rate, saved_calls, read_error, unexpected_error and the various env.*
labels) and merge their entries into a single "cache" map and a single "env" map
respectively, removing the duplicate blocks and ensuring no keys are lost
(combine values, preferring the most complete/accurate strings for any
conflicting keys). Ensure the merged sections include all previously defined
keys such as cache.saved_calls, cache.read_error, unexpected_error, and all
env.* labels so YAML retains every translation.

In `@cortex/i18n/locales/zh.yaml`:
- Around line 32-44: The YAML defines the top-level mapping key "language" twice
which causes the first block (keys like changed, current, auto_detected,
set_from_env, set_from_config, default, available, invalid_code,
supported_codes, select_prompt, switched_to) to be silently overwritten; merge
the missing key(s) such as set_failed into the first "language" mapping and
remove the duplicate second "language" block so all translations live under a
single "language" mapping (ensure keys like changed, switched_to and set_failed
are present and deduplicated).

In `@tests/test_i18n.py`:
- Around line 854-868: The test method test_cli_language_list contains an unused
import "sys"; remove that import to clean up the test. Edit the
test_cli_language_list function and delete the line importing sys so only the
required imports (patch, argparse, CortexCLI) remain, leaving the rest of the
test (creating argparse.Namespace and calling CortexCLI().config) unchanged.
🧹 Nitpick comments (2)
cortex/i18n/config.py (1)

247-283: Add audit logging for language preference changes.

Language updates are persisted but not recorded in the history DB. If audit logging is required for all operations, please record set/clear events (e.g., via InstallationHistory with a CONFIG-like type). As per coding guidelines, please log operations to ~/.cortex/history.db.

tests/test_i18n.py (1)

759-769: Consider using importlib.resources for more robust path resolution.

The current path construction assumes a specific directory structure relative to the test file. While this works for standard test execution, it could break if the project structure changes.

♻️ Optional: Use importlib.resources for path resolution
     def test_all_supported_languages_have_catalogs(self):
         """Test that all supported languages have message catalogs."""
-        from pathlib import Path
-
+        import importlib.resources
         from cortex.i18n.translator import SUPPORTED_LANGUAGES
 
-        locales_dir = Path(__file__).parent.parent / "cortex" / "i18n" / "locales"
-
         for lang_code in SUPPORTED_LANGUAGES:
-            catalog_path = locales_dir / f"{lang_code}.yaml"
-            self.assertTrue(catalog_path.exists(), f"Missing catalog for language: {lang_code}")
+            # Check if resource is accessible within the package
+            try:
+                with importlib.resources.files("cortex.i18n.locales").joinpath(f"{lang_code}.yaml").open():
+                    pass
+            except FileNotFoundError:
+                self.fail(f"Missing catalog for language: {lang_code}")
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between f880f50 and 0c8bbb2.

📒 Files selected for processing (7)
  • cortex/cli.py
  • cortex/i18n/config.py
  • cortex/i18n/detector.py
  • cortex/i18n/formatter.py
  • cortex/i18n/locales/en.yaml
  • cortex/i18n/locales/zh.yaml
  • tests/test_i18n.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • cortex/i18n/formatter.py
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide for Python code
Include type hints in Python code
Add docstrings for all public APIs in Python code
Use dry-run mode by default for all installation operations
Do not use silent sudo - require explicit user confirmation for privilege escalation
Implement Firejail sandboxing for execution of untrusted code
Log all operations to ~/.cortex/history.db for audit purposes

Files:

  • cortex/cli.py
  • tests/test_i18n.py
  • cortex/i18n/config.py
  • cortex/i18n/detector.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain test coverage above 80% for pull requests

Files:

  • tests/test_i18n.py
🧬 Code graph analysis (2)
tests/test_i18n.py (4)
cortex/i18n/formatter.py (13)
  • language (181-183)
  • language (186-191)
  • LocaleFormatter (164-367)
  • format_date (197-208)
  • format_date (400-402)
  • format_number (238-265)
  • format_number (415-417)
  • format_file_size (267-298)
  • format_file_size (420-422)
  • format_datetime (223-236)
  • format_datetime (410-412)
  • format_time (210-221)
  • format_time (405-407)
cortex/i18n/config.py (3)
  • set_language (247-273)
  • get_language (211-245)
  • LanguageConfig (50-328)
cortex/cli.py (5)
  • config (1469-1483)
  • CortexCLI (62-2969)
  • _resolve_language_name (2997-3053)
  • _handle_set_language (3056-3094)
  • main (3158-3997)
cortex/i18n/detector.py (2)
  • detect_os_language (124-164)
  • get_os_locale_info (167-182)
cortex/i18n/config.py (2)
cortex/i18n/detector.py (1)
  • detect_os_language (124-164)
cortex/i18n/translator.py (5)
  • t (291-311)
  • get_language (281-288)
  • set_language (267-278)
  • language (61-63)
  • language (66-83)
🪛 YAMLlint (1.37.1)
cortex/i18n/locales/en.yaml

[error] 431-431: duplication of key "env" in mapping

(key-duplicates)


[error] 459-459: duplication of key "cache" in mapping

(key-duplicates)

cortex/i18n/locales/zh.yaml

[error] 131-131: duplication of key "language" in mapping

(key-duplicates)

🔇 Additional comments (9)
tests/test_i18n.py (9)

1-19: LGTM!

The module docstring clearly documents the test coverage areas, and the imports are appropriate for the test suite.


21-212: LGTM!

The TestTranslator class provides comprehensive coverage of the translator functionality including language switching, variable interpolation, debug mode, singleton behavior, and translations across all 5 supported languages. The setUp/tearDown methods properly reset the global translator state for test isolation.


235-346: LGTM!

Excellent coverage of malformed YAML edge cases. These tests validate the production hardening mentioned in the commit messages, ensuring the config loader is resilient to:

  • Malformed YAML syntax
  • Empty files
  • Whitespace-only files
  • Invalid root types (list, string instead of dict)
  • Non-string language values
  • Null language values

This defensive testing prevents crashes in production when users have corrupted config files.


423-528: LGTM!

The environment variable precedence tests correctly verify the documented priority order: LANGUAGELC_ALLLC_MESSAGESLANG. The fallback scenarios (unsupported languages, C/POSIX locales, empty environment) are well covered.


530-754: LGTM!

The TestLocaleFormatter class provides solid coverage of locale-aware formatting including dates, numbers, file sizes, relative times, and durations across multiple locales. The invalid language fallback test (lines 748-753) ensures robustness.


788-832: LGTM!

The TestTranslationCompleteness class directly addresses the PR objective of ensuring "complete message translation with graceful fallback to English (avoid English leaks)". The tests verify that:

  • Common keys are translated in all languages
  • Spanish translations aren't just English placeholders
  • Variable interpolation works correctly across all languages

964-1037: LGTM!

The TestResolveLanguageName class provides excellent coverage of the language resolution logic, including:

  • Direct code matches
  • English name resolution (case-insensitive)
  • Native name resolution (including non-Latin scripts like 中文)
  • Whitespace handling
  • Invalid/unsupported language rejection

The tests for non-Latin script handling (lines 1013-1029) directly validate the PR's handling of scripts where .lower() is meaningless.


1039-1102: LGTM!

The TestSetLanguageFlag class validates the --set-language global CLI flag, ensuring it correctly handles language codes, English names, native names, and invalid input with appropriate exit codes.


1-1105: Multi-process file locking is already properly implemented in cortex/i18n/config.py.

The LanguageConfig class uses both threading.Lock for intra-process synchronization (line 73) and fcntl.flock for inter-process synchronization (lines 96-118), with platform-specific handling for Windows. The implementation includes exclusive locks for writes and shared locks for reads, with comprehensive error handling and documentation. No additional concurrency tests are needed in test_i18n.py as the codebase has a dedicated test_thread_safety.py for concurrent access scenarios.

Likely an incorrect or invalid review comment.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

@RIVALHIDE
Copy link
Contributor Author

@Suyashd999 Yes, the system supports new commands, but developers must manually add translation keys to en.yaml and other locale files — there's no auto-translation.

Copy link
Collaborator

@Anshgrover23 Anshgrover23 left a comment

Choose a reason for hiding this comment

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

@RIVALHIDE Kindly address bot comments and then create a new Issue regarding auto-translation thing.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🤖 Fix all issues with AI agents
In `@cortex/cli.py`:
- Around line 3085-3123: The hardcoded header "[bold]Supported
languages:[/bold]" in _handle_set_language causes an English string leak;
replace it with a call to the i18n translator (use the existing t() helper) e.g.
console.print(t("language.supported_languages_header")) preserving formatting
(bold) as needed, and add the corresponding translation key/value in the locale
files so all supported locales show a localized header; update any tests or docs
that expect the old literal if present.

In `@cortex/i18n/detector.py`:
- Around line 75-123: _parse_locale currently strips encoding suffixes but
leaves locale modifiers like "@latin", causing wrong fallbacks; modify the
normalization to also remove any "@modifier" suffix (and ensure this occurs
after hyphen->underscore normalization and encoding removal) so entries like
"en_US.UTF-8@latin" or "sr_RS@latin" reduce to the canonical "en_us" / "sr_rs"
before checking LANGUAGE_MAPPINGS; update the regex used to strip suffixes in
_parse_locale (or add a second strip step) to remove both the ".encoding" and
any "@modifier" portions prior to the LANGUAGE_MAPPINGS lookups.
📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0c8bbb2 and 9df25a7.

📒 Files selected for processing (6)
  • cortex/cli.py
  • cortex/i18n/config.py
  • cortex/i18n/detector.py
  • cortex/i18n/locales/en.yaml
  • cortex/i18n/locales/zh.yaml
  • tests/test_i18n.py
🚧 Files skipped from review as they are similar to previous changes (2)
  • cortex/i18n/locales/en.yaml
  • cortex/i18n/locales/zh.yaml
🧰 Additional context used
📓 Path-based instructions (2)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide for Python code
Include type hints in Python code
Add docstrings for all public APIs in Python code
Use dry-run mode by default for all installation operations
Do not use silent sudo - require explicit user confirmation for privilege escalation
Implement Firejail sandboxing for execution of untrusted code
Log all operations to ~/.cortex/history.db for audit purposes

Files:

  • cortex/i18n/detector.py
  • cortex/i18n/config.py
  • cortex/cli.py
  • tests/test_i18n.py
tests/**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

Maintain test coverage above 80% for pull requests

Files:

  • tests/test_i18n.py
🧬 Code graph analysis (1)
cortex/cli.py (3)
cortex/i18n/config.py (5)
  • LanguageConfig (50-374)
  • get_language (211-245)
  • set_language (247-277)
  • get_language_info (291-335)
  • clear_language (279-289)
cortex/i18n/translator.py (6)
  • get_language (281-288)
  • set_language (267-278)
  • t (291-311)
  • get_language_info (320-336)
  • language (61-63)
  • language (66-83)
cortex/branding.py (2)
  • cx_print (62-82)
  • cx_header (95-101)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test (Python 3.11)
  • GitHub Check: Test (Python 3.12)
  • GitHub Check: Test (Python 3.10)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.11)
  • GitHub Check: test (3.10)
🔇 Additional comments (18)
cortex/i18n/detector.py (3)

21-34: Single-source supported languages is clean.
Nice to derive codes directly from translator.SUPPORTED_LANGUAGES to avoid drift.


126-166: Detection order and supported-code filtering look solid.
Priority and validation are clear and consistent.


169-184: Debug locale info helper is clear and useful.

cortex/i18n/config.py (4)

35-47: Supported-language derivation is consistent.


69-210: Concurrency + atomic write handling is robust.
Thread locks, file locks, and temp-file rename make persistence resilient.


211-335: Resolution order and info payload are clear.
Good separation of env/config/auto/default sources.


337-374: Audit logging of language changes is a good addition.

tests/test_i18n.py (3)

21-848: Broad i18n coverage looks solid.
Great breadth across translator, config, detector, formatter, and catalog integrity.


849-1117: CLI integration and name-resolution tests are solid.


582-589: This review comment is incorrect: the code does not use Babel for number formatting.

The LocaleFormatter.format_number() method uses hardcoded locale separators from the LOCALE_CONFIGS dictionary, where French is configured with a plain space (U+0020). The test assertion is correct and not brittle—the formatter will consistently return "1 234 567" with regular spaces because the separator is fixed in the configuration, not determined by an external library like Babel.

The proposed regex fix is unnecessary and misleading, as it would incorrectly suggest the function can return multiple separator types when it cannot.

Likely an incorrect or invalid review comment.

cortex/cli.py (8)

170-172: Localized API-key missing prompts look good.


493-566: Stack list/install flows are properly localized.


581-598: Sandbox usage header/intro now localized.


858-973: Install progress/status messaging is now i18n-friendly.

Also applies to: 1008-1010


1452-1463: Cache stats output is localized consistently.


1469-1616: Config language output is well localized and consistent.


2984-3083: Language-name normalization/resolution logic looks robust.


3241-3247: Parser wiring for --set-language and config language looks solid.

Also applies to: 3471-3491, 3874-3885, 3961-3963

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@cortex/i18n/locales/de.yaml`:
- Around line 1-309: The de.yaml locale is missing 79 keys causing mixed
English/German UI; add the entirely missing sections (ui, docker, role) and add
the missing keys in existing sections (env, config, sandbox, cache, errors,
wizard) so their keys and placeholder tokens exactly match en.yaml; for each
added key provide a German translation preserving placeholders like
{count}/{name}/{error}, then run the existing locale validation (the same
schema/validation used to compare en.yaml) to ensure no extra/mismatched keys
remain and confirm the i18n loader uses the updated de locale for German users.

In `@cortex/i18n/locales/es.yaml`:
- Around line 1-309: The Spanish locale is missing entire sections and keys (add
the missing top-level sections "ui", "docker", and "role" plus any extended keys
present in en.yaml that are absent here) so update the es.yaml to mirror
en.yaml's structure: copy the missing "ui", "docker", and "role" sections and
any additional keys that de.yaml lacked, provide Spanish translations ensuring
placeholders (e.g., {name}, {id}, {count}, {error}, {package}, {language},
{code}) exactly match the English keys, and keep existing keys' placeholder
formats unchanged so fallback behavior is not relied upon.

In `@cortex/i18n/locales/fr.yaml`:
- Around line 1-309: The French locale is missing the same keys present in
de.yaml and es.yaml; compare fr.yaml's top-level sections (common, language,
cli, config, install, stack, sandbox, history, rollback, doctor, wizard, notify,
env, api_key, cache, errors, progress) against the other locales and add any
missing entries (including pluralization variants, placeholdered strings like
{count},{id},{error},{package},{version},{step},{total}, and keys under
cli.commands and cli.args) so fr.yaml structurally matches de.yaml/es.yaml;
preserve existing French wording (including "NPD" in notify.dnd_updated) and
ensure all placeholder names and key names match exactly.
🧹 Nitpick comments (6)
cortex/i18n/detector.py (1)

75-130: Remove redundant lookup at lines 126-128.

The check at lines 126-128 is unreachable dead code. If locale_lower matched LANGUAGE_MAPPINGS, it would have already returned at line 117-118. This branch can never execute since locale_lower hasn't changed between line 117 and line 127.

♻️ Proposed fix
     # Try just the language part (before underscore)
     if "_" in locale_lower:
         lang_part = locale_lower.split("_")[0]
         if lang_part in LANGUAGE_MAPPINGS:
             return LANGUAGE_MAPPINGS[lang_part]
-
-        # Try full locale for regional variants (already normalized)
-        if locale_lower in LANGUAGE_MAPPINGS:
-            return LANGUAGE_MAPPINGS[locale_lower]
 
     return None
cortex/cli.py (5)

1547-1548: Duplicated import of reset_translator within same method.

The reset_translator function is imported twice within _config_language() at lines 1547-1548 and 1568-1570. Move the import to the top of the method or to the module level to avoid redundancy.

♻️ Proposed fix
     def _config_language(self, args: argparse.Namespace) -> int:
         """Handle language configuration."""
+        from cortex.i18n.translator import reset_translator
+
         lang_config = LanguageConfig()
 
         # ... (list and info handling) ...
 
         # Handle 'auto' to clear saved preference
         if code.lower() == "auto":
             lang_config.clear_language()
-            from cortex.i18n.translator import reset_translator
-
             reset_translator()
             # ...
 
         # Validate and set language
         # ...
         try:
             lang_config.set_language(code)
             # Reset the global translator to pick up the new language
-            from cortex.i18n.translator import reset_translator
-
             reset_translator()
             set_language(code)

Also applies to: 1568-1570


1506-1515: Duplicated source translation mapping.

The source_translation_keys / source_translations dictionary is defined identically in both _config_language() (lines 1506-1515) and _config_show() (lines 1593-1599). Extract this to a module-level constant or helper function to follow DRY principles.

♻️ Proposed fix
# At module level or within the class
_SOURCE_TRANSLATION_KEYS = {
    "environment": "language.set_from_env",
    "config": "language.set_from_config",
    "auto-detected": "language.auto_detected",
    "default": "language.default",
}

def _translate_source(source: str) -> str:
    """Translate a language source identifier to localized text."""
    key = _SOURCE_TRANSLATION_KEYS.get(source)
    return t(key) if key else source

Also applies to: 1593-1599


1611-1612: Hardcoded configuration paths in display.

The paths ~/.cortex/preferences.yaml and ~/.cortex/history.db are hardcoded. Consider using the same path resolution logic used elsewhere (e.g., from LanguageConfig or InstallationHistory) to ensure consistency if these paths ever change.


3012-3022: Consider moving unicodedata import to module level.

The import unicodedata statement inside _normalize_for_lookup() is executed on every call. While Python caches imports, moving it to the module level is cleaner and follows the project's import style.

♻️ Proposed fix
+import unicodedata
 import argparse
 import logging
 # ... other imports ...

 def _normalize_for_lookup(s: str) -> str:
     # ...
     try:
-        import unicodedata
-
         has_latin = any(unicodedata.category(c).startswith("L") and ord(c) < 0x3000 for c in s)

1565-1575: Consider audit logging for language preference changes.

Per coding guidelines, operations should be logged to ~/.cortex/history.db for audit purposes. Language configuration changes are not currently logged. While this is a minor configuration change rather than an installation, consider adding a log entry for traceability if strict audit compliance is required.

Based on coding guidelines: "Log all operations to ~/.cortex/history.db for audit purposes"

📜 Review details

Configuration used: defaults

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 9df25a7 and e758a88.

📒 Files selected for processing (7)
  • cortex/cli.py
  • cortex/i18n/detector.py
  • cortex/i18n/locales/de.yaml
  • cortex/i18n/locales/en.yaml
  • cortex/i18n/locales/es.yaml
  • cortex/i18n/locales/fr.yaml
  • cortex/i18n/locales/zh.yaml
🚧 Files skipped from review as they are similar to previous changes (1)
  • cortex/i18n/locales/zh.yaml
🧰 Additional context used
📓 Path-based instructions (1)
**/*.py

📄 CodeRabbit inference engine (AGENTS.md)

**/*.py: Follow PEP 8 style guide for Python code
Include type hints in Python code
Add docstrings for all public APIs in Python code
Use dry-run mode by default for all installation operations
Do not use silent sudo - require explicit user confirmation for privilege escalation
Implement Firejail sandboxing for execution of untrusted code
Log all operations to ~/.cortex/history.db for audit purposes

Files:

  • cortex/cli.py
  • cortex/i18n/detector.py
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (6)
  • GitHub Check: Test (Python 3.11)
  • GitHub Check: Test (Python 3.12)
  • GitHub Check: Test (Python 3.10)
  • GitHub Check: test (3.12)
  • GitHub Check: test (3.10)
  • GitHub Check: test (3.11)
🔇 Additional comments (18)
cortex/i18n/detector.py (5)

1-18: LGTM! Module docstring and imports are appropriate.

The module docstring clearly documents the purpose and the environment variables checked. Type hints are properly set up with TYPE_CHECKING import pattern.


21-34: LGTM! Clean implementation with proper circular import handling.

The late import pattern and docstring are well done. The function correctly derives supported codes from the single source of truth.


37-72: LGTM! Comprehensive locale variant mappings.

The mapping covers common regional variants for all supported languages and correctly handles C/POSIX locales.


133-173: LGTM! Correct environment variable priority and handling.

The function properly implements GNU gettext-style language detection with correct priority ordering and handles the colon-separated LANGUAGE variable appropriately.


176-191: LGTM! Useful diagnostic function with proper typing.

The function provides comprehensive locale information for debugging purposes. The type hint correctly reflects that environment variable values may be None.

cortex/i18n/locales/en.yaml (2)

1-471: LGTM! English source catalog is well-structured.

The file is properly organized with clear section headers, appropriate placeholder documentation via comments, and no duplicate top-level keys. The previous issue with duplicate env and cache sections appears to have been resolved.

The comprehensive key coverage in en.yaml serves as the authoritative source for fallback when translations are missing in other locales.


35-38: Good practice: Placeholder documentation for translators.

The inline comments documenting placeholder variables (e.g., {action}, {error}) provide clear guidance for translators. This pattern is consistently applied throughout the file.

cortex/cli.py (11)

25-31: LGTM - i18n imports are well-structured.

The imports from cortex.i18n provide all necessary utilities for internationalization support throughout the CLI.


170-172: LGTM - API key messages properly internationalized.

User-facing error messages and hints are correctly routed through the translation function.


496-566: LGTM - Stack command messages properly internationalized.

The stack-related user messages use translation keys consistently with proper parameter interpolation for dynamic values like stack names and package counts.


582-598: Sandbox command descriptions remain hardcoded.

The sandbox usage section translates headers but keeps command syntax descriptions in English (lines 585-591). This is likely intentional for CLI command consistency, but verify if these should be translated for full i18n coverage.


859-1009: LGTM - Installation flow comprehensively internationalized.

The install command's user-facing messages (analysis, planning, execution, success/failure) are properly routed through translation with appropriate parameter interpolation.


1452-1462: LGTM - Cache statistics display internationalized.

Cache stats messages and error handling use translation keys with proper error parameter interpolation.


3085-3124: LGTM - _handle_set_language implementation is correct.

The function properly resolves language names, validates against SUPPORTED_LANGUAGES, persists the preference, resets the translator, and provides user feedback with appropriate error handling.


3241-3247: LGTM - Global language flag well-defined.

The --set-language flag accepts both language codes and human-readable names, with clear help text showing examples.


3471-3490: LGTM - Config command parser structure is correct.

The config language subcommand properly supports setting by code, listing available languages, and showing current configuration info.


3874-3884: LGTM - Global language flag handling is well-implemented.

The logic correctly handles three scenarios:

  1. Language-only command (cortex --set-language es) - sets language and exits
  2. Language setting fails - returns error immediately
  3. Language + command (cortex --set-language es install nginx) - sets language then continues

The inline comment clearly documents this behavior.


3961-3962: LGTM - Config command routing correctly added.

The config command is properly routed to the cli.config(args) method.

✏️ Tip: You can disable this entire section by setting review_details to false in your review settings.

mikejmorgan-ai
mikejmorgan-ai previously approved these changes Jan 17, 2026
Copy link
Member

@mikejmorgan-ai mikejmorgan-ai left a comment

Choose a reason for hiding this comment

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

Review: APPROVED ✅

i18n Support - 5 Languages (PR #593)

Code Quality

  • ✅ Thread-safe file operations with proper locking (threading.Lock + fcntl.flock)
  • ✅ TOCTOU race condition properly addressed - exists() check and file read within critical section
  • ✅ Formatter singleton correctly syncs language on each access
  • ✅ Comprehensive error handling for malformed YAML, empty files, invalid types
  • ✅ Well-documented code with clear docstrings

Test Coverage

  • ✅ 89 tests all passing
  • ✅ Tests cover: translator, config, detector, formatter, CLI integration
  • ✅ Edge cases tested: malformed YAML, null values, invalid types, whitespace files
  • ✅ All 5 languages verified: English, Spanish, French, German, Chinese

Architecture

  • ✅ Clean separation: translator.py, formatter.py, config.py, detector.py
  • ✅ Single source of truth for supported languages (SUPPORTED_LANGUAGES dict)
  • ✅ Proper fallback chain: env var → config file → OS detection → default

Notes for CI

  • Requires Python 3.10+ due to union type syntax (type | None)
  • Ensure CI runs with compatible Python version

Ready for merge once Ansh approves.

Reviewed-by: Claude Code

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cortex/cli.py (1)

516-540: Require explicit --execute for stack installs (dry‑run by default).
cortex stack <name> currently performs a real install unless --dry-run is passed. This conflicts with the dry‑run‑by‑default safety requirement for installation operations in this CLI.

🔧 Suggested fix
@@
-        if args.dry_run:
-            return self._handle_stack_dry_run(stack, packages)
-
-        return self._handle_stack_real_install(stack, packages)
+        execute = getattr(args, "execute", False)
+        if args.dry_run or not execute:
+            return self._handle_stack_dry_run(stack, packages)
+
+        return self._handle_stack_real_install(stack, packages)
@@
 stack_parser.add_argument(
     "--dry-run", action="store_true", help="Show what would be installed (requires stack name)"
 )
+stack_parser.add_argument(
+    "--execute",
+    action="store_true",
+    help="Execute stack installation (default: dry-run)",
+)

As per coding guidelines, please make dry‑run the default for stack installs.

🤖 Fix all issues with AI agents
In `@cortex/cli.py`:
- Around line 581-592: The sandbox command help prints per-command descriptions
in English; replace each hardcoded description in the no-action help block (the
strings currently printed in the if not action branch of cortex/cli.py) with
translated keys via the t(...) helper (e.g. t("sandbox.cmd_create"),
t("sandbox.cmd_install"), t("sandbox.cmd_test"), t("sandbox.cmd_promote"),
t("sandbox.cmd_cleanup"), t("sandbox.cmd_list"), t("sandbox.cmd_exec")) while
preserving the command syntax and spacing, and add corresponding sandbox.cmd_*
entries to the locale files so translations are available.
🧹 Nitpick comments (1)
cortex/cli.py (1)

1525-1563: Allow config language to accept human‑readable names.
--set-language already resolves names via _resolve_language_name, but config language only accepts codes. Reusing the resolver keeps UX consistent.

♻️ Recommended refactor
@@
-        # Validate and set language
-        code = code.lower()
-        if code not in SUPPORTED_LANGUAGES:
-            self._print_error(t("language.invalid_code", code=code))
+        # Validate and set language (allow names as well as codes)
+        code_input = code
+        resolved = _resolve_language_name(code_input)
+        if not resolved:
+            self._print_error(t("language.invalid_code", code=code_input))
             console.print(
                 f"[dim]{t('language.supported_codes')}: {', '.join(SUPPORTED_LANGUAGES.keys())}[/dim]"
             )
             return 1
+        code = resolved

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
cortex/cli.py (1)

699-709: Update sandbox_promote to default to dry-run mode.

The --dry-run flag uses action="store_true" (line 3522) without a default value, requiring users to explicitly opt-in. Per coding guidelines, all installation operations must default to dry-run. The current implementation allows dry_run to default to False (line 703), which contradicts the requirement to "implement dry-run by default for all installation operations." This should align with other protective operations like remove.

🤖 Fix all issues with AI agents
In `@cortex/cli.py`:
- Around line 500-501: Replace the hardcoded English fallback strings in the
stack printing calls so they use the app’s localization keys instead of literal
text: change stack.get('name', 'Unnamed Stack') and stack.get('description', 'No
description') to use the translator lookup (e.g., t("stack.unnamed") and
t("stack.no_description") or the project's gettext wrapper) as the default
values; also add the corresponding keys ("stack.unnamed" and
"stack.no_description") to the locale files so the defaults are localized.
🧹 Nitpick comments (2)
cortex/cli.py (2)

1593-1599: Duplicate source translation mapping - consider extracting.

The source_translations dictionary is duplicated here and in _config_language (lines 1506-1511). This violates DRY and could lead to inconsistencies if keys change.

♻️ Proposed refactor

Extract to a module-level constant or helper function:

# Near the top of the file or in a dedicated section
def _get_source_display(source: str) -> str:
    """Translate language source identifier to localized display string."""
    source_keys = {
        "environment": "language.set_from_env",
        "config": "language.set_from_config",
        "auto-detected": "language.auto_detected",
        "default": "language.default",
    }
    key = source_keys.get(source)
    return t(key) if key else source

Then use in both methods:

-            source_translations = {
-                "environment": t("language.set_from_env"),
-                ...
-            }
-            source_display = source_translations.get(lang_info["source"], lang_info["source"])
+            source_display = _get_source_display(lang_info["source"])

3012-3021: Consider moving unicodedata import to module level.

The unicodedata import inside _normalize_for_lookup adds overhead on each call. While negligible for the current 5-language set, this could be moved to the module level for cleaner code since unicodedata is a standard library module with no startup cost concerns.

♻️ Proposed refactor
 import argparse
 import logging
 import os
 import sys
 import time
+import unicodedata
 import uuid

Then remove the inline import:

     try:
-        import unicodedata
-
         has_latin = any(unicodedata.category(c).startswith("L") and ord(c) < 0x3000 for c in s)

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 3

🤖 Fix all issues with AI agents
In `@cortex/cli.py`:
- Around line 906-907: The printed executing message appends an extra "..."
while the translation key t('install.executing') already contains ellipses;
update the print call in cortex/cli.py that currently reads
print(f"\n{t('install.executing')}...") to remove the added ellipses so it only
prints print(f"\n{t('install.executing')}"), preserving the translation string
as the sole source of punctuation.
- Around line 1452-1457: The printed cache hit-rate is using the translation
string t('cache.hit_rate') without substituting its {rate} placeholder and
saved_calls is incorrectly using stats.hits; update the three cx_print calls in
this block: format t('cache.hit_rate') with .format(rate=hit_rate) (or
equivalent interpolation) when calling cx_print for the hit rate, and use
stats.saved_calls (not stats.hits) for the saved calls line so the labels and
values are correct; keep using cx_print and t(...) as the surrounding helpers.
- Around line 170-172: The error output is using a hardcoded "Error:" prefix so
localized messages still get an English prefix; update the printing to use the
i18n label instead of the hardcoded string by changing the call path that prints
errors (the _print_error helper or its callers) to prepend t("error.prefix") (or
the appropriate i18n key used elsewhere) rather than the literal "Error:";
locate and modify the _print_error implementation or replace its usage around
the lines where _print_error(t("api_key.not_found")) is called so the prefix
comes from t(...) and ensure cx_print/t usage remains consistent for info hints.

@sonarqubecloud
Copy link

Copy link
Collaborator

@Anshgrover23 Anshgrover23 left a comment

Choose a reason for hiding this comment

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

LGTM!

@Anshgrover23 Anshgrover23 merged commit b614ec6 into cortexlinux:main Jan 17, 2026
18 checks passed
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.

Multi-Language CLI Support

4 participants