-
-
Notifications
You must be signed in to change notification settings - Fork 48
feat(i18n): Add comprehensive multi-language support for 12 languages #394
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
733c7a6
0f2f0cf
551864f
254f5f2
21e8d7a
ed602b2
3bc19fd
1535961
472238b
b51b7db
6ad1bd2
6494535
4738daf
d226c60
f5ddd6b
f4bf8a9
b8bae47
a6f533a
e4ba731
2eaf566
5de53ff
2540bc3
a80d031
8ee14e5
b6cf7b1
af13bc3
6e91a3e
fa8f843
677cd0c
2a79f8c
a99deea
637100d
692f5ae
388db56
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,6 @@ | ||
| # CodeQL Configuration for Cortex Linux | ||
| # Uses default setup (no advanced configuration) | ||
| name: "CodeQL" | ||
| queries: | ||
| - uses: security-extended | ||
| - uses: security-and-quality |
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @RIVALHIDE Again, why this one also changed ?
Contributor
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @Anshgrover23 The workflows/codeql-analysis.yml file was inadvertently modified during AI-based testing. I apologize for the oversight; the original configuration has now been restored and pushed. Additionally, support has been added for cortex --set-language "English" alongside cortex config es, ensuring both the existing and the more human-readable syntax work as intended. |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,45 @@ | ||
| name: CodeQL Analysis | ||
|
|
||
| on: | ||
| push: | ||
| branches: [ main ] | ||
| pull_request: | ||
| branches: [ main ] | ||
| schedule: | ||
| - cron: '0 0 * * 0' | ||
|
|
||
| permissions: | ||
| security-events: write | ||
| contents: read | ||
|
|
||
| jobs: | ||
| analyze: | ||
| name: Analyze | ||
| runs-on: ubuntu-latest | ||
| strategy: | ||
| fail-fast: false | ||
| matrix: | ||
| language: [ 'python' ] | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v4 | ||
|
|
||
| - name: Set up Python | ||
| uses: actions/setup-python@v5 | ||
| with: | ||
| python-version: '3.10' | ||
|
|
||
| - name: Initialize CodeQL | ||
| uses: github/codeql-action/init@v3 | ||
| with: | ||
| languages: ${{ matrix.language }} | ||
| config-file: .github/codeql-config.yml | ||
|
|
||
| - name: Autobuild | ||
| uses: github/codeql-action/autobuild@v3 | ||
|
|
||
| - name: Perform CodeQL Analysis | ||
| uses: github/codeql-action/analyze@v3 | ||
| with: | ||
| category: "/language:${{ matrix.language }}" |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
|
@@ -10,6 +10,7 @@ | |||||||||||||||||
| from cortex.api_key_detector import auto_detect_api_key, setup_api_key | ||||||||||||||||||
| from cortex.ask import AskHandler | ||||||||||||||||||
| from cortex.branding import VERSION, console, cx_header, cx_print, show_banner | ||||||||||||||||||
| from cortex.config_manager import ConfigManager | ||||||||||||||||||
| from cortex.coordinator import InstallationCoordinator, InstallationStep, StepStatus | ||||||||||||||||||
| from cortex.demo import run_demo | ||||||||||||||||||
| from cortex.dependency_importer import ( | ||||||||||||||||||
|
|
@@ -19,6 +20,7 @@ | |||||||||||||||||
| format_package_list, | ||||||||||||||||||
| ) | ||||||||||||||||||
| from cortex.env_manager import EnvironmentManager, get_env_manager | ||||||||||||||||||
| from cortex.i18n import LanguageManager | ||||||||||||||||||
| from cortex.installation_history import InstallationHistory, InstallationStatus, InstallationType | ||||||||||||||||||
| from cortex.llm.interpreter import CommandInterpreter | ||||||||||||||||||
| from cortex.network_config import NetworkConfig | ||||||||||||||||||
|
|
@@ -37,11 +39,34 @@ | |||||||||||||||||
|
|
||||||||||||||||||
|
|
||||||||||||||||||
| class CortexCLI: | ||||||||||||||||||
| def __init__(self, verbose: bool = False): | ||||||||||||||||||
| def __init__(self, verbose: bool = False, language: str | None = None): | ||||||||||||||||||
| self.spinner_chars = ["⠋", "⠙", "⠹", "⠸", "⠼", "⠴", "⠦", "⠧", "⠇", "⠏"] | ||||||||||||||||||
| self.spinner_idx = 0 | ||||||||||||||||||
| self.verbose = verbose | ||||||||||||||||||
coderabbitai[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||||||||||||||||||
|
|
||||||||||||||||||
| # Initialize language manager with ConfigManager for saved preferences | ||||||||||||||||||
| config_mgr = ConfigManager() | ||||||||||||||||||
| self.lang_manager = LanguageManager(prefs_manager=config_mgr) | ||||||||||||||||||
| detected_language = language or self.lang_manager.detect_language() | ||||||||||||||||||
| from cortex.i18n import get_translator | ||||||||||||||||||
|
|
||||||||||||||||||
| self.translator = get_translator(detected_language) | ||||||||||||||||||
|
|
||||||||||||||||||
| def t(self, key: str, **kwargs) -> str: | ||||||||||||||||||
| """Get a translated string by key. | ||||||||||||||||||
|
|
||||||||||||||||||
| Shortcut method that delegates to self.translator.get() for retrieving | ||||||||||||||||||
| localized strings with optional variable interpolation. | ||||||||||||||||||
|
|
||||||||||||||||||
| Args: | ||||||||||||||||||
| key: Translation key in dot notation (e.g., 'install.success'). | ||||||||||||||||||
| **kwargs: Variables for string interpolation (e.g., package='nginx'). | ||||||||||||||||||
|
|
||||||||||||||||||
| Returns: | ||||||||||||||||||
| str: The translated string, or a fallback placeholder if key is missing. | ||||||||||||||||||
| """ | ||||||||||||||||||
| return self.translator.get(key, **kwargs) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Define a method to handle Docker-specific permission repairs | ||||||||||||||||||
| def docker_permissions(self, args: argparse.Namespace) -> int: | ||||||||||||||||||
| """Handle the diagnosis and repair of Docker file permissions. | ||||||||||||||||||
|
|
@@ -514,11 +539,19 @@ def _sandbox_test(self, sandbox, args: argparse.Namespace) -> int: | |||||||||||||||||
|
|
||||||||||||||||||
| def _sandbox_promote(self, sandbox, args: argparse.Namespace) -> int: | ||||||||||||||||||
| """Promote a tested package to main system.""" | ||||||||||||||||||
| from cortex.validators import validate_package_name | ||||||||||||||||||
|
|
||||||||||||||||||
| name = args.name | ||||||||||||||||||
| package = args.package | ||||||||||||||||||
| dry_run = getattr(args, "dry_run", False) | ||||||||||||||||||
| skip_confirm = getattr(args, "yes", False) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Validate package name before passing to system commands | ||||||||||||||||||
| is_valid, error = validate_package_name(package) | ||||||||||||||||||
| if not is_valid: | ||||||||||||||||||
| self._print_error(f"Invalid package name: {error}") | ||||||||||||||||||
| return 1 | ||||||||||||||||||
|
|
||||||||||||||||||
| if dry_run: | ||||||||||||||||||
| result = sandbox.promote(name, package, dry_run=True) | ||||||||||||||||||
| cx_print(f"Would run: sudo apt-get install -y {package}", "info") | ||||||||||||||||||
|
|
@@ -672,22 +705,20 @@ def install( | |||||||||||||||||
| start_time = datetime.now() | ||||||||||||||||||
|
|
||||||||||||||||||
| try: | ||||||||||||||||||
| self._print_status("🧠", "Understanding request...") | ||||||||||||||||||
| self._print_status("🧠", self.t("status.understanding")) | ||||||||||||||||||
|
|
||||||||||||||||||
| interpreter = CommandInterpreter(api_key=api_key, provider=provider) | ||||||||||||||||||
|
|
||||||||||||||||||
| self._print_status("📦", "Planning installation...") | ||||||||||||||||||
| self._print_status("📦", self.t("status.planning")) | ||||||||||||||||||
|
|
||||||||||||||||||
| for _ in range(10): | ||||||||||||||||||
| self._animate_spinner("Analyzing system requirements...") | ||||||||||||||||||
| self._animate_spinner(self.t("status.analyzing")) | ||||||||||||||||||
| self._clear_line() | ||||||||||||||||||
|
|
||||||||||||||||||
| commands = interpreter.parse(f"install {software}") | ||||||||||||||||||
|
|
||||||||||||||||||
| if not commands: | ||||||||||||||||||
| self._print_error( | ||||||||||||||||||
| "No commands generated. Please try again with a different request." | ||||||||||||||||||
| ) | ||||||||||||||||||
| self._print_error(self.t("errors.no_commands")) | ||||||||||||||||||
| return 1 | ||||||||||||||||||
|
|
||||||||||||||||||
| # Extract packages from commands for tracking | ||||||||||||||||||
|
|
@@ -699,13 +730,13 @@ def install( | |||||||||||||||||
| InstallationType.INSTALL, packages, commands, start_time | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| self._print_status("⚙️", f"Installing {software}...") | ||||||||||||||||||
| print("\nGenerated commands:") | ||||||||||||||||||
| self._print_status("⚙️", self.t("install.installing", package=software)) | ||||||||||||||||||
| print(f"\n{self.t('install.generated_commands')}:") | ||||||||||||||||||
| for i, cmd in enumerate(commands, 1): | ||||||||||||||||||
| print(f" {i}. {cmd}") | ||||||||||||||||||
|
|
||||||||||||||||||
| if dry_run: | ||||||||||||||||||
| print("\n(Dry run mode - commands not executed)") | ||||||||||||||||||
| print(f"\n({self.t('install.dry_run_note')})") | ||||||||||||||||||
| if install_id: | ||||||||||||||||||
| history.update_installation(install_id, InstallationStatus.SUCCESS) | ||||||||||||||||||
| return 0 | ||||||||||||||||||
|
|
@@ -1388,6 +1419,83 @@ def _env_load(self, env_mgr: EnvironmentManager, args: argparse.Namespace) -> in | |||||||||||||||||
|
|
||||||||||||||||||
| return 0 | ||||||||||||||||||
|
|
||||||||||||||||||
| def config(self, args: argparse.Namespace) -> int: | ||||||||||||||||||
| """Handle CLI configuration commands. | ||||||||||||||||||
|
|
||||||||||||||||||
| Routes configuration subcommands to their respective handlers. | ||||||||||||||||||
| Currently supports language configuration via the 'language' subcommand. | ||||||||||||||||||
|
|
||||||||||||||||||
| Args: | ||||||||||||||||||
| args: Parsed CLI arguments containing config_action attribute | ||||||||||||||||||
| and any subcommand-specific options. | ||||||||||||||||||
|
|
||||||||||||||||||
| Returns: | ||||||||||||||||||
| int: Exit code (0 on success, 1 on error or unknown subcommand). | ||||||||||||||||||
| """ | ||||||||||||||||||
| config_action = getattr(args, "config_action", None) | ||||||||||||||||||
|
|
||||||||||||||||||
| if not config_action: | ||||||||||||||||||
| self._print_error("Please specify a subcommand (language)") | ||||||||||||||||||
| return 1 | ||||||||||||||||||
|
|
||||||||||||||||||
| if config_action == "language": | ||||||||||||||||||
| return self._config_language(args) | ||||||||||||||||||
| else: | ||||||||||||||||||
| self._print_error(f"Unknown config subcommand: {config_action}") | ||||||||||||||||||
| return 1 | ||||||||||||||||||
|
|
||||||||||||||||||
| def _config_language(self, args: argparse.Namespace) -> int: | ||||||||||||||||||
| """Handle language configuration.""" | ||||||||||||||||||
| config_mgr = ConfigManager() | ||||||||||||||||||
| lang_mgr = LanguageManager() | ||||||||||||||||||
|
Comment on lines
+1447
to
+1450
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Inconsistent In 🔧 Suggested fix def _config_language(self, args: argparse.Namespace) -> int:
"""Handle language configuration."""
config_mgr = ConfigManager()
- lang_mgr = LanguageManager()
+ lang_mgr = LanguageManager(prefs_manager=config_mgr)
new_language = getattr(args, "language_code", None)📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||
| new_language = getattr(args, "language_code", None) | ||||||||||||||||||
|
|
||||||||||||||||||
| # Load current preferences with error handling | ||||||||||||||||||
| try: | ||||||||||||||||||
| prefs = config_mgr.load_preferences() | ||||||||||||||||||
| except (OSError, ValueError) as e: | ||||||||||||||||||
| self._print_error(f"Failed to read configuration: {e}") | ||||||||||||||||||
| return 1 | ||||||||||||||||||
|
|
||||||||||||||||||
| current_lang = prefs.get("language", "en") | ||||||||||||||||||
|
|
||||||||||||||||||
| if new_language: | ||||||||||||||||||
| # Resolve language (accepts codes like 'es' or names like 'Spanish', 'Español') | ||||||||||||||||||
| resolved = lang_mgr.resolve_language(new_language) | ||||||||||||||||||
| if not resolved: | ||||||||||||||||||
| self._print_error(f"Language '{new_language}' is not supported") | ||||||||||||||||||
| cx_print("Supported languages:", "info") | ||||||||||||||||||
| for code, name in lang_mgr.get_available_languages().items(): | ||||||||||||||||||
| console.print(f" [green]{code}[/green] - {name}") | ||||||||||||||||||
| return 1 | ||||||||||||||||||
|
|
||||||||||||||||||
| # Save preference with error handling | ||||||||||||||||||
| prefs["language"] = resolved | ||||||||||||||||||
| try: | ||||||||||||||||||
| config_mgr.save_preferences(prefs) | ||||||||||||||||||
| except (OSError, ValueError) as e: | ||||||||||||||||||
| self._print_error(f"Failed to save configuration: {e}") | ||||||||||||||||||
| return 1 | ||||||||||||||||||
|
|
||||||||||||||||||
| lang_name = lang_mgr.get_language_name(resolved) | ||||||||||||||||||
| cx_print(f"✓ Language set to {lang_name} ({resolved})", "success") | ||||||||||||||||||
| cx_print("This will be used for all future Cortex commands.", "info") | ||||||||||||||||||
| return 0 | ||||||||||||||||||
| else: | ||||||||||||||||||
| # Show current language and available options | ||||||||||||||||||
| lang_name = lang_mgr.get_language_name(current_lang) | ||||||||||||||||||
| cx_header("Language Configuration") | ||||||||||||||||||
| console.print(f" Current language: [green]{lang_name}[/green] ({current_lang})") | ||||||||||||||||||
| console.print() | ||||||||||||||||||
| console.print("[bold]Available languages:[/bold]") | ||||||||||||||||||
| for code, name in sorted(lang_mgr.get_available_languages().items()): | ||||||||||||||||||
| marker = " [cyan]◄[/cyan]" if code == current_lang else "" | ||||||||||||||||||
| console.print(f" [green]{code}[/green] - {name}{marker}") | ||||||||||||||||||
| console.print() | ||||||||||||||||||
| cx_print("Set language: cortex config language <code>", "info") | ||||||||||||||||||
| cx_print("Example: cortex config language es", "info") | ||||||||||||||||||
| return 0 | ||||||||||||||||||
|
|
||||||||||||||||||
| # --- Shell Environment Analyzer Commands --- | ||||||||||||||||||
| def _env_audit(self, args: argparse.Namespace) -> int: | ||||||||||||||||||
| """Audit shell environment variables and show their sources.""" | ||||||||||||||||||
|
|
@@ -2036,6 +2144,7 @@ def show_rich_help(): | |||||||||||||||||
| table.add_row("rollback <id>", "Undo installation") | ||||||||||||||||||
| table.add_row("notify", "Manage desktop notifications") | ||||||||||||||||||
| table.add_row("env", "Manage environment variables") | ||||||||||||||||||
| table.add_row("config", "Configure Cortex settings") | ||||||||||||||||||
| table.add_row("cache stats", "Show LLM cache statistics") | ||||||||||||||||||
| table.add_row("stack <name>", "Install the stack") | ||||||||||||||||||
| table.add_row("docker permissions", "Fix Docker bind-mount permissions") | ||||||||||||||||||
|
|
@@ -2102,6 +2211,12 @@ def main(): | |||||||||||||||||
| # Global flags | ||||||||||||||||||
| parser.add_argument("--version", "-V", action="version", version=f"cortex {VERSION}") | ||||||||||||||||||
| parser.add_argument("--verbose", "-v", action="store_true", help="Show detailed output") | ||||||||||||||||||
| parser.add_argument( | ||||||||||||||||||
| "--language", | ||||||||||||||||||
| "-L", | ||||||||||||||||||
| metavar="CODE", | ||||||||||||||||||
| help="Set display language for this command (e.g., en, es, de, ja)", | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
|
Comment on lines
+2214
to
2220
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Critical: Proposed diff- cli = CortexCLI(verbose=args.verbose)
+ cli = CortexCLI(
+ verbose=args.verbose,
+ language=getattr(args, "language", None),
+ )Also applies to: 2605-2607 🤖 Prompt for AI Agents✅ Addressed in commit 2a79f8c |
||||||||||||||||||
| subparsers = parser.add_subparsers(dest="command", help="Available commands") | ||||||||||||||||||
|
|
||||||||||||||||||
|
|
@@ -2220,6 +2335,18 @@ def main(): | |||||||||||||||||
| cache_subs = cache_parser.add_subparsers(dest="cache_action", help="Cache actions") | ||||||||||||||||||
| cache_subs.add_parser("stats", help="Show cache statistics") | ||||||||||||||||||
|
|
||||||||||||||||||
| # --- Configuration Commands --- | ||||||||||||||||||
| config_parser = subparsers.add_parser("config", help="Manage Cortex configuration") | ||||||||||||||||||
| config_subs = config_parser.add_subparsers(dest="config_action", help="Configuration actions") | ||||||||||||||||||
|
|
||||||||||||||||||
| # config language [code] | ||||||||||||||||||
| config_lang_parser = config_subs.add_parser("language", help="Get or set display language") | ||||||||||||||||||
| config_lang_parser.add_argument( | ||||||||||||||||||
| "language_code", | ||||||||||||||||||
| nargs="?", | ||||||||||||||||||
| help="Language code to set (e.g., en, es, fr, de, pt, ja, zh, ko, ar, hi, ru, it)", | ||||||||||||||||||
| ) | ||||||||||||||||||
|
|
||||||||||||||||||
| # --- Sandbox Commands (Docker-based package testing) --- | ||||||||||||||||||
| sandbox_parser = subparsers.add_parser( | ||||||||||||||||||
| "sandbox", help="Test packages in isolated Docker sandbox" | ||||||||||||||||||
|
|
@@ -2486,7 +2613,7 @@ def main(): | |||||||||||||||||
| return 0 | ||||||||||||||||||
|
|
||||||||||||||||||
| # Initialize the CLI handler | ||||||||||||||||||
| cli = CortexCLI(verbose=args.verbose) | ||||||||||||||||||
| cli = CortexCLI(verbose=args.verbose, language=args.language) | ||||||||||||||||||
|
|
||||||||||||||||||
| try: | ||||||||||||||||||
| # Route the command to the appropriate method inside the cli object | ||||||||||||||||||
|
|
@@ -2531,6 +2658,8 @@ def main(): | |||||||||||||||||
| return 1 | ||||||||||||||||||
| elif args.command == "env": | ||||||||||||||||||
| return cli.env(args) | ||||||||||||||||||
| elif args.command == "config": | ||||||||||||||||||
| return cli.config(args) | ||||||||||||||||||
| else: | ||||||||||||||||||
| parser.print_help() | ||||||||||||||||||
| return 1 | ||||||||||||||||||
|
|
||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| """ | ||
| I18N Module Initialization | ||
|
|
||
| Provides convenient access to i18n components for the rest of Cortex. | ||
|
|
||
| Author: Cortex Linux Team | ||
| License: Apache 2.0 | ||
| """ | ||
|
|
||
| from cortex.i18n.fallback_handler import FallbackHandler, get_fallback_handler | ||
| from cortex.i18n.language_manager import LanguageManager | ||
| from cortex.i18n.pluralization import PluralRules | ||
| from cortex.i18n.translator import Translator, get_translator, translate | ||
|
|
||
| __all__ = [ | ||
| "Translator", | ||
| "LanguageManager", | ||
| "PluralRules", | ||
| "FallbackHandler", | ||
| "get_translator", | ||
| "get_fallback_handler", | ||
| "translate", | ||
| ] | ||
|
|
||
| __version__ = "0.1.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@RIVALHIDE Why this file changed ?
Uh oh!
There was an error while loading. Please reload this page.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Anshgrover23 The codeql-config.yml file was inadvertently modified during AI-based testing. I apologize for the oversight; the original configuration has now been restored and pushed.