diff --git a/web/app.py b/web/app.py index b25fde4..f8c2171 100644 --- a/web/app.py +++ b/web/app.py @@ -13,7 +13,7 @@ import streamlit as st from loguru import logger # Import i18n and config manager -from web.i18n import load_locales, set_language, tr, get_available_languages +from web.i18n import load_locales, set_language, tr, get_available_languages, get_language from reelforge.config import config_manager from reelforge.models.progress import ProgressEvent @@ -52,12 +52,10 @@ def safe_rerun(): def init_i18n(): """Initialize internationalization""" - # Load locales if not already loaded - load_locales() - - # Get language from session state or default to Chinese + # Locales are already loaded and system language detected on import + # Get language from session state or use auto-detected system language if "language" not in st.session_state: - st.session_state.language = "zh_CN" + st.session_state.language = get_language() # Use auto-detected language # Set current language set_language(st.session_state.language) @@ -86,7 +84,8 @@ def get_reelforge(): def init_session_state(): """Initialize session state variables""" if "language" not in st.session_state: - st.session_state.language = "zh_CN" + # Use auto-detected system language + st.session_state.language = get_language() # ============================================================================ diff --git a/web/i18n/__init__.py b/web/i18n/__init__.py index a5c7365..2229be1 100644 --- a/web/i18n/__init__.py +++ b/web/i18n/__init__.py @@ -3,13 +3,14 @@ International language support for ReelForge Web UI """ import json +import locale from pathlib import Path from typing import Dict, Optional from loguru import logger _locales: Dict[str, dict] = {} -_current_language: str = "zh_CN" +_current_language: str = "en_US" # Default fallback to English def load_locales() -> Dict[str, dict]: @@ -112,6 +113,125 @@ def get_available_languages() -> Dict[str, str]: } +def detect_system_language() -> str: + """ + Detect system/OS language and return the best matching locale code. + Falls back to English if no match found. + + This is designed for self-hosted scenarios where the server and browser + are typically on the same machine. + + Returns: + Language code (e.g., "zh_CN", "en_US") + """ + try: + import os + import platform + import subprocess + + system_locale = None + + # Method 1: macOS-specific detection (most reliable for macOS) + if platform.system() == "Darwin": # macOS + try: + # Get AppleLocale which reflects system language preference + result = subprocess.run( + ["defaults", "read", "-g", "AppleLocale"], + capture_output=True, + text=True, + timeout=2 + ) + if result.returncode == 0: + system_locale = result.stdout.strip() + logger.debug(f"System locale from macOS AppleLocale: {system_locale}") + except Exception as e: + logger.debug(f"macOS AppleLocale detection failed: {e}") + + # Fallback: try AppleLanguages + if not system_locale: + try: + result = subprocess.run( + ["defaults", "read", "-g", "AppleLanguages"], + capture_output=True, + text=True, + timeout=2 + ) + if result.returncode == 0: + # Parse array output like: ( "zh-Hans-CN", "en-CN" ) + output = result.stdout.strip() + # Extract first language + import re + match = re.search(r'"([^"]+)"', output) + if match: + lang = match.group(1) + # Convert zh-Hans-CN to zh_CN + if lang.startswith("zh-Hans"): + system_locale = "zh_CN" + elif lang.startswith("zh-Hant"): + system_locale = "zh_TW" + else: + system_locale = lang.replace("-", "_") + logger.debug(f"System locale from macOS AppleLanguages: {system_locale}") + except Exception as e: + logger.debug(f"macOS AppleLanguages detection failed: {e}") + + # Method 2: Get from environment locale (cross-platform) + if not system_locale: + try: + system_locale = locale.getdefaultlocale()[0] + logger.debug(f"System locale from getdefaultlocale(): {system_locale}") + except Exception as e: + logger.debug(f"getdefaultlocale() failed: {e}") + + # Method 3: Get from current locale + if not system_locale: + try: + system_locale = locale.getlocale()[0] + logger.debug(f"System locale from getlocale(): {system_locale}") + except Exception as e: + logger.debug(f"getlocale() failed: {e}") + + # Method 4: Try to get from environment variables + if not system_locale: + for env_var in ['LC_ALL', 'LC_MESSAGES', 'LANG', 'LANGUAGE']: + env_value = os.environ.get(env_var) + if env_value: + # Extract language code from formats like "zh_CN.UTF-8" + system_locale = env_value.split('.')[0] + logger.debug(f"System locale from {env_var}: {system_locale}") + break + + if system_locale: + # Normalize the locale string + # Handle formats: zh_CN, zh-CN, zh_CN.UTF-8, etc. + system_locale = system_locale.replace('-', '_').split('.')[0] + + # Direct match (e.g., "zh_CN") + for locale_code in _locales.keys(): + if locale_code.lower() == system_locale.lower(): + logger.info(f"System language matched: {locale_code}") + return locale_code + + # Partial match (e.g., "zh" matches "zh_CN") + lang_prefix = system_locale.split('_')[0].lower() + for locale_code in _locales.keys(): + if locale_code.lower().startswith(lang_prefix): + logger.info(f"System language partially matched: {locale_code} (from {system_locale})") + return locale_code + + logger.info("No system language detected, using fallback") + except Exception as e: + logger.warning(f"Failed to detect system language: {e}") + + # Fallback to English + return "en_US" + + # Auto-load locales on import load_locales() +# Auto-detect and set system language +_detected_language = detect_system_language() +_current_language = _detected_language +logger.info(f"Default language initialized to: {_current_language}") +