mirror of
https://github.com/MacRimi/ProxMenux.git
synced 2026-09-29 10:06:41 +00:00
fix(i18n): keep {placeholders} out of the translator in the TUI cache
The Monitor's generator protects them; this one did not, so a provider
read the word inside the braces and translated it. "{count}" came back
as "{conta}" in Portuguese and "{počet}" in Slovak, and the consumer —
finding no placeholder to substitute — fell back to English, which is
every message the recent batches introduced.
The token carries no underscores: argos splits on them and hands back
"PMX PH 0".
Two further fixes this depends on:
- protect_catalog_titles rebuilt the glossary pattern without the word
boundaries the module-level one has, so a short term matched inside
longer words once the catalogue was loaded.
- CT and VM join the glossary. Read as initials they get reordered —
"CT" comes back as "TC" in Spanish — naming something Proxmox does not.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -94,6 +94,11 @@ PROTECTED_TECHNICAL_TERMS = (
|
||||
"GPU",
|
||||
"CPU",
|
||||
"AMD",
|
||||
# Proxmox's own words for a container and a virtual machine. Read as
|
||||
# initials they get reordered — "CT" comes back as "TC" in Spanish —
|
||||
# and the reader is told about something Proxmox does not call that.
|
||||
"CT",
|
||||
"VM",
|
||||
)
|
||||
TECHNICAL_TERM_RE = re.compile(
|
||||
"|".join(
|
||||
@@ -106,6 +111,9 @@ TRANSLATE_CALL_RE = re.compile(
|
||||
r"""translate\s+(?P<quote>["'])(?P<text>(?:\\.|(?! (?P=quote) ).)*?)(?P=quote)""",
|
||||
re.VERBOSE | re.DOTALL,
|
||||
)
|
||||
# Same shape the Monitor's generator uses, so a message reads identically
|
||||
# whichever of the two catalogues it comes from.
|
||||
PLACEHOLDER_RE = re.compile(r"\{[A-Za-z_][A-Za-z0-9_]*\}")
|
||||
# Providers that answer the same thing every time for the same input, so a
|
||||
# second attempt cannot produce a different result. `appimage` shells out to a
|
||||
# binary that may reach a network service, so it is not on the list.
|
||||
@@ -133,11 +141,44 @@ def protect_catalog_titles(directories) -> None:
|
||||
if not titles:
|
||||
return
|
||||
terms = tuple(sorted(set(PROTECTED_TECHNICAL_TERMS) | titles, key=len, reverse=True))
|
||||
# Same word boundaries as the module-level pattern. Without them a short
|
||||
# term matches inside longer words, and every application title in the
|
||||
# catalogue joins the glossary here.
|
||||
TECHNICAL_TERM_RE = re.compile(
|
||||
"|".join(re.escape(term) for term in terms), re.IGNORECASE)
|
||||
"|".join(
|
||||
rf"(?<![A-Za-z0-9_]){re.escape(term)}(?![A-Za-z0-9_])"
|
||||
for term in terms
|
||||
),
|
||||
re.IGNORECASE,
|
||||
)
|
||||
print(f"Protected application names: {len(titles)}", flush=True)
|
||||
|
||||
|
||||
def protect_placeholders(text: str) -> tuple[str, list[str]]:
|
||||
"""Replace each ``{name}`` with a token before translation.
|
||||
|
||||
A provider reads the name inside the braces as a word and translates
|
||||
it: ``{count}`` comes back as ``{conta}`` in Portuguese and ``{počet}``
|
||||
in Slovak. The consumer then finds no placeholder to substitute and
|
||||
falls back to English, so the translation was paid for and never used.
|
||||
The token carries no underscores because argos splits on them.
|
||||
"""
|
||||
found: list[str] = []
|
||||
|
||||
def _swap(match: re.Match[str]) -> str:
|
||||
found.append(match.group(0))
|
||||
return f"PMXPH{len(found) - 1:03d}"
|
||||
|
||||
return PLACEHOLDER_RE.sub(_swap, text), found
|
||||
|
||||
|
||||
def restore_placeholders(text: str, found: list[str]) -> str:
|
||||
"""Put the original ``{name}`` back where each token landed."""
|
||||
for index, original in enumerate(found):
|
||||
text = text.replace(f"PMXPH{index:03d}", original)
|
||||
return text
|
||||
|
||||
|
||||
def protect_technical_terms(text: str) -> tuple[str, list[str]]:
|
||||
"""Replace glossary terms with stable tokens before translation."""
|
||||
protected: list[str] = []
|
||||
@@ -433,7 +474,8 @@ def translate_text(
|
||||
timeout: int,
|
||||
appimage_path: Path,
|
||||
) -> str:
|
||||
protected_text, protected_terms = protect_technical_terms(text)
|
||||
protected_text, placeholders = protect_placeholders(text)
|
||||
protected_text, protected_terms = protect_technical_terms(protected_text)
|
||||
if provider == "argos":
|
||||
translated = translate_argos(protected_text, dest_lang)
|
||||
elif provider == "googletrans":
|
||||
@@ -447,6 +489,7 @@ def translate_text(
|
||||
else:
|
||||
raise ValueError(f"Unknown provider: {provider}")
|
||||
translated = restore_technical_terms(clean_translation(translated), protected_terms)
|
||||
translated = restore_placeholders(translated, placeholders)
|
||||
return restore_sentence_spacing(translated) if translated else text
|
||||
|
||||
|
||||
|
||||
@@ -892,6 +892,8 @@
|
||||
"Completed with errors —": "Mit Fehlern abgeschlossen –",
|
||||
"Completed.": "Vollendet.",
|
||||
"Completed. Devices added to VM {vmid}: {count}.": "Abgeschlossen. Zur VM {vmid} hinzugefügte Geräte: {count}.",
|
||||
"Completed. Disks added to CT {ctid}: {count}.": "Abgeschlossen. Zu CT {ctid} hinzugefügte Festplatten: {count}.",
|
||||
"Completed. Disks added to VM {vmid}: {count}.": "Abgeschlossen. Zu VM {vmid} hinzugefügte Festplatten: {count}.",
|
||||
"Completed. Press Enter to return to menu...": "Vollendet. Drücken Sie die Eingabetaste, um zum Menü zurückzukehren...",
|
||||
"Completing its final cleanup...": "Abschluss der endgültigen Bereinigung...",
|
||||
"Completing pending package configurations...": "Ausstehende Paketkonfigurationen werden abgeschlossen...",
|
||||
|
||||
@@ -893,6 +893,8 @@
|
||||
"Completed with errors —": "Completado con errores.",
|
||||
"Completed.": "Terminado.",
|
||||
"Completed. Devices added to VM {vmid}: {count}.": "Completado. Dispositivos agregados a la VM {vmid}: {count}.",
|
||||
"Completed. Disks added to CT {ctid}: {count}.": "Completado. Discos añadidos a CT {ctid}: {count}.",
|
||||
"Completed. Disks added to VM {vmid}: {count}.": "Completado. Discos añadidos a VM {vmid}: {count}.",
|
||||
"Completed. Press Enter to return to menu...": "Terminado. Presione Enter para regresar al menú...",
|
||||
"Completing its final cleanup...": "Completando su limpieza final...",
|
||||
"Completing pending package configurations...": "Completando las configuraciones de paquetes pendientes...",
|
||||
|
||||
@@ -893,6 +893,8 @@
|
||||
"Completed with errors —": "Complété avec des erreurs —",
|
||||
"Completed.": "Complété.",
|
||||
"Completed. Devices added to VM {vmid}: {count}.": "Terminé. Appareils ajoutés à la VM {vmid} : {count}.",
|
||||
"Completed. Disks added to CT {ctid}: {count}.": "Terminé. Disques ajoutés à CT {ctid}: {count}.",
|
||||
"Completed. Disks added to VM {vmid}: {count}.": "Terminé. Disques ajoutés à VM {vmid}: {count}.",
|
||||
"Completed. Press Enter to return to menu...": "Complété. Appuyez sur Entrée pour revenir au menu...",
|
||||
"Completing its final cleanup...": "Compléter son nettoyage final...",
|
||||
"Completing pending package configurations...": "Finalisation des configurations de packages en attente...",
|
||||
|
||||
@@ -893,6 +893,8 @@
|
||||
"Completed with errors —": "Completato con errori —",
|
||||
"Completed.": "Completato.",
|
||||
"Completed. Devices added to VM {vmid}: {count}.": "completato. Dispositivi aggiunti alla VM {vmid}: {count}.",
|
||||
"Completed. Disks added to CT {ctid}: {count}.": "Completato. I dischi aggiunti a CT {ctid}: {count}.",
|
||||
"Completed. Disks added to VM {vmid}: {count}.": "Completato. I dischi aggiunti a VM {vmid}: {count}.",
|
||||
"Completed. Press Enter to return to menu...": "Completato. Premere Invio per tornare al menu...",
|
||||
"Completing its final cleanup...": "Completa la sua pulizia finale...",
|
||||
"Completing pending package configurations...": "completamento delle configurazioni dei pacchetti in sospeso...",
|
||||
|
||||
@@ -893,6 +893,8 @@
|
||||
"Completed with errors —": "Concluído com erros -",
|
||||
"Completed.": "Concluído.",
|
||||
"Completed. Devices added to VM {vmid}: {count}.": "Concluído. Dispositivos adicionados à VM {vmid}: {count}.",
|
||||
"Completed. Disks added to CT {ctid}: {count}.": "Concluído. Discos adicionados ao CT {ctid}: {count}.",
|
||||
"Completed. Disks added to VM {vmid}: {count}.": "Concluído. Discos adicionados ao VM {vmid}: {count}.",
|
||||
"Completed. Press Enter to return to menu...": "Concluído. Pressione Enter para retornar ao menu...",
|
||||
"Completing its final cleanup...": "Completando sua limpeza final...",
|
||||
"Completing pending package configurations...": "Concluindo configurações de pacotes pendentes...",
|
||||
|
||||
@@ -892,6 +892,8 @@
|
||||
"Completed with errors —": "Dokončené s chybami -",
|
||||
"Completed.": "Dokončené.",
|
||||
"Completed. Devices added to VM {vmid}: {count}.": "Dokončené. Zariadenia pridané do VM {vmid}: {count}.",
|
||||
"Completed. Disks added to CT {ctid}: {count}.": "Hotovo. Disky pridané k CT {ctid}: {count}.",
|
||||
"Completed. Disks added to VM {vmid}: {count}.": "Hotovo. Disky pridané k VM {vmid}: {count}.",
|
||||
"Completed. Press Enter to return to menu...": "Dokončené. Stlačte Enter pre návrat do menu...",
|
||||
"Completing its final cleanup...": "Dokončiť záverečné čistenie...",
|
||||
"Completing pending package configurations...": "Dokončenie čakajúcich konfigurácií balíka...",
|
||||
|
||||
@@ -891,6 +891,8 @@
|
||||
"Completed with errors —": "Kompletterad med fel —",
|
||||
"Completed.": "Avslutad.",
|
||||
"Completed. Devices added to VM {vmid}: {count}.": "Klar. Enheter som har lagts till VM {vmid}: {count}.",
|
||||
"Completed. Disks added to CT {ctid}: {count}.": "Slutfört. Disker läggs till CT {ctid}: {count}.",
|
||||
"Completed. Disks added to VM {vmid}: {count}.": "Slutfört. Disker läggs till VM {vmid}: {count}.",
|
||||
"Completed. Press Enter to return to menu...": "Avslutad. Tryck på Enter för att återgå till menyn...",
|
||||
"Completing its final cleanup...": "Slutför sin sista rengöring...",
|
||||
"Completing pending package configurations...": "Slutför väntande paketkonfigurationer...",
|
||||
|
||||
Reference in New Issue
Block a user