1616from __future__ import annotations
1717
1818from typing import Any , Optional , Callable
19+ import os
20+ import json
1921
2022_GLOBAL_TR : dict [str , Any ] = {}
2123_GLOBAL_LANG : str = "en"
@@ -96,10 +98,16 @@ def register_plugin_translations(plugin_id: str, tr: dict) -> None:
9698
9799def translate (plugin_id : str , key : str , default : Optional [str ] = None ) -> str :
98100 try :
99- if plugin_id and plugin_id in _PLUGIN_TR :
100- val = _PLUGIN_TR [plugin_id ].get (key )
101- if isinstance (val , str ):
102- return val
101+ if plugin_id :
102+ if plugin_id in _PLUGIN_TR :
103+ val = _PLUGIN_TR [plugin_id ].get (key )
104+ if isinstance (val , str ):
105+ return val
106+ low = str (plugin_id ).lower ()
107+ if low in _PLUGIN_TR :
108+ val = _PLUGIN_TR [low ].get (key )
109+ if isinstance (val , str ):
110+ return val
103111 except Exception :
104112 pass
105113 try :
@@ -125,6 +133,10 @@ def unregister_i18n_handler(fn: Callable[[Any, dict], None]) -> None:
125133
126134def apply_translations (gui , tr : dict ) -> None :
127135 set_translations (gui , tr )
136+ try :
137+ _load_all_plugin_languages (get_language_code ())
138+ except Exception :
139+ pass
128140 for fn in list (_HANDLERS ):
129141 try :
130142 fn (gui , tr )
@@ -136,7 +148,6 @@ def load_plugin_language_file(plugin_package: str, code: str) -> dict:
136148 """Load language file for a plugin from its package's languages folder."""
137149 try :
138150 import importlib .resources as ilr
139- import json
140151
141152 lang_data = {}
142153 if not plugin_package :
@@ -160,3 +171,72 @@ def load_plugin_language_file(plugin_package: str, code: str) -> dict:
160171 return lang_data
161172 except Exception :
162173 return {}
174+
175+
176+ def _discover_plugins_dir () -> str | None :
177+ try :
178+ base = os .path .abspath (
179+ os .path .join (os .path .dirname (__file__ ), os .pardir , os .pardir )
180+ )
181+ cand = os .path .join (base , "Plugins" )
182+ if os .path .isdir (cand ):
183+ return cand
184+ except Exception :
185+ pass
186+ return None
187+
188+
189+ def _load_plugin_languages_from_fs (plugins_dir : str , code : str ) -> dict [str , dict ]:
190+ data : dict [str , dict ] = {}
191+ normalized = normalize_language_code (code )
192+ candidates = [
193+ f"{ normalized } .json" ,
194+ f"{ normalized .lower ()} .json" ,
195+ "en.json" ,
196+ ]
197+ try :
198+ for entry in os .listdir (plugins_dir ):
199+ plugin_path = os .path .join (plugins_dir , entry )
200+ if not os .path .isdir (plugin_path ):
201+ continue
202+ if not os .path .isfile (os .path .join (plugin_path , "__init__.py" )):
203+ continue
204+ lang_dir = os .path .join (plugin_path , "languages" )
205+ if not os .path .isdir (lang_dir ):
206+ continue
207+ payload : dict = {}
208+ for name in candidates :
209+ p = os .path .join (lang_dir , name )
210+ if not os .path .isfile (p ):
211+ continue
212+ try :
213+ with open (p , encoding = "utf-8" ) as f :
214+ content = json .load (f )
215+ if isinstance (content , dict ):
216+ payload .update (content )
217+ break
218+ except Exception :
219+ continue
220+ if payload :
221+ data [entry ] = payload
222+ except Exception :
223+ pass
224+ return data
225+
226+
227+ def _load_all_plugin_languages (code : str ) -> None :
228+ """Load translations for all plugins in Plugins/ folder."""
229+ try :
230+ _PLUGIN_TR .clear ()
231+ except Exception :
232+ pass
233+ plugins_dir = _discover_plugins_dir ()
234+ if not plugins_dir :
235+ return
236+ data = _load_plugin_languages_from_fs (plugins_dir , code )
237+ for plugin_id , tr in data .items ():
238+ register_plugin_translations (plugin_id , tr )
239+ try :
240+ register_plugin_translations (str (plugin_id ).lower (), tr )
241+ except Exception :
242+ pass
0 commit comments