Translation and localization¶
Translation stack: service adapter, manager, auto-translation providers, and string collection.
📦 TranslationService¶
TranslationService
¶
Bases: TranslationServiceProtocol
Service wrapper around translation management.
📦 TranslationManager¶
TranslationManager
¶
Bases: QObject
Core translation engine for EzQt_App.
Handles .ts file loading, language switching, Qt translator installation and automatic widget retranslation on language change.
📡 Translation signal integration¶
TranslationManager exposes runtime signals used by the UI layer:
| Signal | Payload | Purpose |
|---|---|---|
languageChanged |
str (language code) |
Notify widgets/services that the active language changed |
translation_started |
none | Notify that async auto-translation queue became active |
translation_finished |
none | Notify that async auto-translation queue drained |
AutoTranslator also emits low-level worker signals:
| Signal | Payload | Purpose |
|---|---|---|
translation_ready |
(original, translated) |
One async translation completed successfully |
translation_error |
(original, error) |
One async translation failed |
Example wiring:
from ezqt_app.services.translation import get_translation_manager
manager = get_translation_manager()
manager.languageChanged.connect(lambda code: print(f"Language -> {code}"))
manager.translation_started.connect(lambda: print("Auto-translation started"))
manager.translation_finished.connect(lambda: print("Auto-translation finished"))
📦 EzTranslator¶
EzTranslator is a QTranslator subclass installed permanently into Qt's translator
chain. It intercepts every QCoreApplication.translate("EzQt_App", text) call made
by any widget:
- Cache hit — returns the translation immediately from the in-memory
_ts_translationsdictionary. - Identity — when the current language equals the source language (
enby default), returnssource_textas-is and persists the identity mapping to the source.tsfile. - Cache miss — dispatches an async translation request (daemon thread) via
AutoTranslatorand returnsNoneso Qt falls back to the source text for the current render cycle. When the translation arrives,LanguageChangeis re-emitted and widgets retranslate automatically.
EzTranslator is installed once at TranslationManager init and is never removed,
even during language switches (only the .qm-backed translator is swapped).
EzTranslator
¶
EzTranslator(manager: TranslationManager)
Bases: QTranslator
Qt translator that intercepts unknown strings for auto-translation.
This translator is installed alongside the compiled .qm translator.
Qt calls translate() on every installed translator in LIFO order until
one returns a non-empty string. When source_text is not yet known,
EzTranslator fires the async auto-translation pipeline and returns an
empty string so Qt falls back to the source text for this render cycle.
On the next LanguageChange (triggered once the translation arrives) the
string is found in _ts_translations and returned immediately.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
manager
|
TranslationManager
|
The owning :class: |
required |
translate
¶
translate(context: str, source_text: str, disambiguation: str | None = None, n: int = -1) -> str | None
Return the translation for source_text, or None if unknown.
Returning None signals Qt (via null QString) that this translator
did not handle the string, so Qt continues querying the next installed
translator and ultimately falls back to the source text.
Returning "" would mean "found, but translation is empty", which
would incorrectly replace every untranslated string with an empty label.
When the string is absent from the in-memory cache and auto-translation
is active, an async translation request is fired so the string will be
available on the next LanguageChange cycle.
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
context
|
str
|
Qt translation context (only |
required |
source_text
|
str
|
The English source string to translate. |
required |
disambiguation
|
str | None
|
Optional disambiguation hint (unused). |
None
|
n
|
int
|
Plural form selector (unused). |
-1
|
Returns:
| Type | Description |
|---|---|
str | None
|
The translated string if cached, |
📦 AutoTranslator¶
AutoTranslator
¶
Bases: QObject
Automatic translation manager (disabled by default).
Each call to :meth:translate spawns a lightweight daemon thread that
performs the HTTP round-trip in the background. Signals are emitted from
that thread; Qt automatically delivers them via a queued connection to
slots that live in the main thread, so the UI is never blocked.
A _pending set (guarded by _lock) deduplicates in-flight requests:
if the same source string is requested while a thread for it is still
running, no second thread is spawned.
translate
¶
Schedule an async translation and return None immediately.
If source_lang equals target_lang the method returns text
immediately (identity translation — no HTTP request is made).
If text is already cached the cached value is returned immediately.
Otherwise a daemon thread is started and None is returned; the
caller receives the result via :attr:translation_ready.
translate_sync
¶
Translate text synchronously, blocking until a result is obtained.
Intended for use in CLI scripts, test helpers, and offline batch-processing
tools that run outside the Qt event loop. Each provider call is a blocking
HTTP request; the total wait time can reach len(providers) × timeout
seconds if all providers fail.
Warning
Never call this method from the Qt main (UI) thread. Doing so
blocks the event loop for the entire duration of the HTTP round-trips,
freezing the application UI. For in-app translation use
:meth:translate instead, which runs the request in a daemon thread.
Example::
# Appropriate usage — called from a CLI script, not from a Qt slot:
translator = get_auto_translator()
translator.enabled = True
result = translator.translate_sync("Hello", "en", "fr")
print(result) # "Bonjour"
Parameters:
| Name | Type | Description | Default |
|---|---|---|---|
text
|
str
|
The source text to translate. |
required |
source_lang
|
str
|
BCP-47 language code of the source text (e.g. |
required |
target_lang
|
str
|
BCP-47 language code of the desired output (e.g. |
required |
Returns:
| Type | Description |
|---|---|
str | None
|
The translated string, or |
str | None
|
all providers fail. |
save_translation_to_ts
¶
save_translation_to_ts(original: str, translated: str, target_lang: str, ts_file_path: Path) -> None
Append a single translation entry to a Qt Linguist .ts XML file.
⚠️ Provider response validation¶
External HTTP payloads from providers are schema-validated before use.
| Provider | Validation mode |
|---|---|
GoogleTranslateProvider |
strict payload shape checks |
MyMemoryProvider |
strict object schema checks |
LibreTranslateProvider |
strict object schema checks |
Invalid payloads are rejected and logged; translation returns None.
⚠️ Cache contract¶
translations.json is validated with a strict schema on read and write.
Malformed entries are removed or rejected instead of being tolerated.
⚠️ Legacy fallback removed¶
LibreTranslateProvider no longer auto-falls back to an alternate host when
the primary host returns an HTTP error.
📦 StringCollector¶
StringCollector
¶
String collector with language detection and task generation.
🔍 Providers¶
Available provider implementations:
GoogleTranslateProviderMyMemoryProviderLibreTranslateProvider
auto_translation_enabled in translation.config.yaml controls external provider calls.
save_to_ts_files: true enables runtime persistence: every translated string is appended
to the active .ts file and the .qm is recompiled via pyside6-lrelease.
Widget registration and string collection are local workflow features and can remain
enabled independently.