@@ -273,6 +273,11 @@ def _install_qt_translator(self) -> None:
273273 self ._qt_translator ._current_locale = self .get_locale ()
274274 self ._tagger ._qt_translators .add_translator (self ._qt_translator )
275275
276+ # Only emit signal if application is already running (not during startup)
277+ # This ensures UI retranslation when plugins are installed dynamically
278+ if hasattr (self ._tagger , 'window' ) and self ._tagger .window :
279+ self ._tagger ._qt_translators_updated .emit ()
280+
276281 def _remove_qt_translator (self ) -> None :
277282 """Remove Qt translator for .ui file translations."""
278283 if not self ._qt_translator :
@@ -281,6 +286,27 @@ def _remove_qt_translator(self) -> None:
281286 self ._tagger ._qt_translators .remove_translator (self ._qt_translator )
282287 self ._qt_translator = None
283288
289+ def reload_translations (self ) -> None :
290+ """Reload translations and reinstall Qt translator.
291+
292+ Used when plugin is updated to refresh translations without recreating API instance.
293+ """
294+ # Clear existing translations
295+ self ._translations .clear ()
296+
297+ # Remove old Qt translator
298+ self ._remove_qt_translator ()
299+
300+ # Reload translations from disk
301+ self ._load_translations ()
302+
303+ # Reinstall Qt translator with new translations
304+ self ._install_qt_translator ()
305+
306+ # Emit signal to trigger Qt translator reinstall and UI retranslation
307+ # This is only called during plugin updates, not normal installation
308+ self ._tagger ._qt_translators_updated .emit ()
309+
284310 def _is_valid_locale (self , locale : str ) -> bool :
285311 """Check if locale string is valid (basic sanity check).
286312
@@ -402,6 +428,9 @@ def _load_translation_file_for_locale(self, locale: str) -> bool:
402428 data = self ._load_translation_file (file_path , format , locale )
403429 if data :
404430 self ._translations [locale ] = data
431+ self ._logger .debug (
432+ "Loaded %d translations for locale '%s': %s" , len (data ), locale , list (data .keys ())[:10 ]
433+ )
405434 return True
406435 return False
407436
@@ -531,20 +560,31 @@ def tr(self, key: str, text: str | None = None, **kwargs) -> str:
531560 # Try exact locale match (e.g., de_DE)
532561 if locale in self ._translations and key in self ._translations [locale ]:
533562 result = self ._translations [locale ][key ]
563+ self ._logger .debug ("tr() found exact locale match: '%s' -> '%s'" , key , result )
534564 else :
535565 # Try language without region (e.g., de from de_DE)
536566 lang = locale .split ('_' )[0 ]
537567 if lang in self ._translations and key in self ._translations [lang ]:
538568 result = self ._translations [lang ][key ]
569+ self ._logger .debug ("tr() found language match: '%s' -> '%s'" , key , result )
570+ else :
571+ # Try source locale as fallback
572+ if self ._source_locale in self ._translations and key in self ._translations [self ._source_locale ]:
573+ result = self ._translations [self ._source_locale ][key ]
574+ self ._logger .debug ("tr() found source locale match: '%s' -> '%s'" , key , result )
575+ else :
576+ self ._logger .debug ("tr() no translation found for key '%s' in any locale" , key )
539577
540578 # Fall back to text parameter or key
541579 if result is None :
542580 result = text if text is not None else key
581+ self ._logger .debug ("tr() using fallback: '%s' -> '%s'" , key , result )
543582
544583 # Apply placeholder substitution
545584 if kwargs :
546585 result = result .format (** kwargs )
547586
587+ self ._logger .debug ("tr() final result: '%s' -> '%s'" , key , result )
548588 return result
549589
550590 def trn (self , key : str , singular : str | None = None , plural : str | None = None , n : int = 0 , ** kwargs ) -> str :
@@ -587,6 +627,15 @@ def trn(self, key: str, singular: str | None = None, plural: str | None = None,
587627 result = trans [plural_form ]
588628 elif isinstance (trans , dict ) and 'other' in trans :
589629 result = trans ['other' ]
630+ else :
631+ # Try source locale as fallback
632+ if self ._source_locale in self ._translations and key in self ._translations [self ._source_locale ]:
633+ trans = self ._translations [self ._source_locale ][key ]
634+ source_plural_form = get_plural_form (self ._source_locale , n )
635+ if isinstance (trans , dict ) and source_plural_form in trans :
636+ result = trans [source_plural_form ]
637+ elif isinstance (trans , dict ) and 'other' in trans :
638+ result = trans ['other' ]
590639
591640 # Fall back to singular/plural parameters
592641 if result is None :
0 commit comments