6666logger = logging .getLogger (__name__ )
6767
6868
69+ class LanguageManager :
70+ """Manages application languages loaded from JSON files."""
71+
72+ def __init__ (self ):
73+ self .languages = {}
74+ self .current_language = "en"
75+ self .strings = {}
76+ self ._load_languages ()
77+
78+ def _load_languages (self ):
79+ """Load languages from JSON files in languages directory."""
80+ languages_dir = Path (__file__ ).parent / "languages"
81+
82+ if not languages_dir .exists ():
83+ logger .warning (f"Languages directory not found: { languages_dir } " )
84+ self ._load_default_language ()
85+ return
86+
87+ try :
88+ for lang_file in sorted (languages_dir .glob ("*.json" )):
89+ try :
90+ with open (lang_file , 'r' , encoding = 'utf-8' ) as f :
91+ lang_data = json .load (f )
92+ lang_code = lang_data .get ('code' , lang_file .stem )
93+ self .languages [lang_code ] = {
94+ 'name' : lang_data .get ('name' , lang_code ),
95+ 'native_name' : lang_data .get ('native_name' , lang_code ),
96+ 'strings' : lang_data .get ('strings' , {})
97+ }
98+ except Exception as e :
99+ logger .error (f"Failed to load language from { lang_file } : { e } " )
100+ except Exception as e :
101+ logger .error (f"Error loading languages: { e } " )
102+ self ._load_default_language ()
103+
104+ if self .languages :
105+ self .current_language = list (self .languages .keys ())[0 ]
106+ self .strings = self .languages [self .current_language ]['strings' ].copy ()
107+ else :
108+ self ._load_default_language ()
109+
110+ def _load_default_language (self ):
111+ """Load default English language as fallback."""
112+ self .languages = {
113+ "en" : {
114+ "name" : "English" ,
115+ "native_name" : "English" ,
116+ "strings" : {
117+ "app_title" : "BCASL Standalone - Before Compilation Actions System Loader" ,
118+ "workspace_config" : "Workspace Configuration" ,
119+ "workspace_label" : "Workspace:" ,
120+ "no_workspace" : "No workspace selected" ,
121+ "browse_button" : "Browse..." ,
122+ "config_summary" : "Configuration summary" ,
123+ "execution_log" : "Execution Log:" ,
124+ "run_async" : "Run asynchronously" ,
125+ "theme_label" : "Theme:" ,
126+ "configure_plugins" : "⚙️ Configure Plugins" ,
127+ "run_bcasl" : "▶️ Run BCASL" ,
128+ "clear_log" : "🗑️ Clear Log" ,
129+ "exit_button" : "Exit" ,
130+ "ready" : "Ready" ,
131+ "running" : "Running BCASL..." ,
132+ "completed" : "Completed" ,
133+ "failed" : "Failed" ,
134+ }
135+ }
136+ }
137+ self .current_language = "en"
138+ self .strings = self .languages ["en" ]["strings" ].copy ()
139+
140+ def set_language (self , lang_code : str ) -> bool :
141+ """Set the current language."""
142+ if lang_code not in self .languages :
143+ return False
144+ self .current_language = lang_code
145+ self .strings = self .languages [lang_code ]['strings' ].copy ()
146+ return True
147+
148+ def get_language_names (self ) -> list :
149+ """Get list of available language codes."""
150+ return list (self .languages .keys ())
151+
152+ def get_language_display_names (self ) -> list :
153+ """Get list of available language display names."""
154+ return [self .languages [code ]['name' ] for code in self .get_language_names ()]
155+
156+ def get (self , key : str , default : str = "" ) -> str :
157+ """Get translated string."""
158+ return self .strings .get (key , default )
159+
160+ def format (self , key : str , ** kwargs ) -> str :
161+ """Get translated string with formatting."""
162+ template = self .strings .get (key , "" )
163+ try :
164+ return template .format (** kwargs )
165+ except KeyError :
166+ return template
167+
168+
69169class ThemeManager :
70170 """Manages application themes loaded from JSON files."""
71171
@@ -305,8 +405,10 @@ def __init__(self, workspace_dir: Optional[str] = None):
305405 self ._config_cache = None
306406 self ._last_config_load_time = 0
307407
308- # Initialize theme manager
408+ # Initialize language and theme managers
409+ self .language_manager = LanguageManager ()
309410 self .theme_manager = ThemeManager ()
411+ self ._load_language_preference ()
310412 self ._load_theme_preference ()
311413
312414 self .setWindowTitle ("BCASL Standalone - Before Compilation Actions System Loader" )
@@ -365,14 +467,25 @@ def __init__(self, workspace_dir: Optional[str] = None):
365467 self .chk_async .setToolTip ("Execute BCASL in background thread for better responsiveness" )
366468 options_layout .addWidget (self .chk_async )
367469
470+ # Language selector
471+ lang_label = QLabel ("Language:" )
472+ self .lang_combo = QComboBox ()
473+ self .lang_combo .addItems (list (self .language_manager .get_language_names ()))
474+ self .lang_combo .setCurrentText (self .language_manager .current_language )
475+ self .lang_combo .currentTextChanged .connect (self ._on_language_changed )
476+ self .lang_combo .setMaximumWidth (100 )
477+
368478 # Theme selector
369479 theme_label = QLabel ("Theme:" )
370480 self .theme_combo = QComboBox ()
371481 self .theme_combo .addItems (list (self .theme_manager .THEMES .keys ()))
372482 self .theme_combo .setCurrentText (self .theme_manager .current_theme )
373483 self .theme_combo .currentTextChanged .connect (self ._on_theme_changed )
374484 self .theme_combo .setMaximumWidth (120 )
485+
375486 options_layout .addStretch ()
487+ options_layout .addWidget (lang_label )
488+ options_layout .addWidget (self .lang_combo )
376489 options_layout .addWidget (theme_label )
377490 options_layout .addWidget (self .theme_combo )
378491 layout .addLayout (options_layout )
@@ -409,6 +522,28 @@ def __init__(self, workspace_dir: Optional[str] = None):
409522 if workspace_dir :
410523 self ._load_config_info ()
411524
525+ def _load_language_preference (self ):
526+ """Load language preference from config file."""
527+ try :
528+ config_file = Path .home () / ".bcasl_language"
529+ if config_file .exists ():
530+ with open (config_file , 'r' ) as f :
531+ data = json .load (f )
532+ lang = data .get ('language' , 'en' )
533+ if lang in self .language_manager .get_language_names ():
534+ self .language_manager .set_language (lang )
535+ except Exception :
536+ pass
537+
538+ def _save_language_preference (self ):
539+ """Save language preference to config file."""
540+ try :
541+ config_file = Path .home () / ".bcasl_language"
542+ with open (config_file , 'w' ) as f :
543+ json .dump ({'language' : self .language_manager .current_language }, f )
544+ except Exception :
545+ pass
546+
412547 def _load_theme_preference (self ):
413548 """Load theme preference from config file."""
414549 try :
@@ -431,6 +566,13 @@ def _save_theme_preference(self):
431566 except Exception :
432567 pass
433568
569+ def _on_language_changed (self , lang_code : str ):
570+ """Handle language change."""
571+ if self .language_manager .set_language (lang_code ):
572+ self ._save_language_preference ()
573+ # Note: Full UI translation would require updating all text elements
574+ # For now, language preference is saved for next session
575+
434576 def _on_theme_changed (self , theme_name : str ):
435577 """Handle theme change."""
436578 if self .theme_manager .set_theme (theme_name ):
0 commit comments