4040 "Watt" ,
4141}
4242
43+ # Domain-specific glossary for fitness/cycling terms.
44+ # Keys are English source terms (case-sensitive), values are the preferred translation.
45+ # Add entries here when the LLM picks the wrong word for a domain-specific concept.
46+ GLOSSARY : dict [str , dict [str , str ]] = {
47+ "Italian" : {
48+ "Gears" : "Marce" ,
49+ "Gear" : "Marcia" ,
50+ "Gear +" : "Marcia +" ,
51+ "Gear -" : "Marcia -" ,
52+ "Gears +" : "Marce +" ,
53+ "Gears -" : "Marce -" ,
54+ "Cadence" : "Cadenza" ,
55+ "Resistance" : "Resistenza" ,
56+ "Inclination" : "Inclinazione" ,
57+ "Incline" : "Pendenza" ,
58+ "Peloton Resistance" : "Resistenza Peloton" ,
59+ "Watts" : "Watt" ,
60+ "Lap" : "Giro" ,
61+ "Stride" : "Passo" ,
62+ "Step Count" : "Contapassi" ,
63+ "Ramp" : "Rampa" ,
64+ },
65+ "French" : {
66+ "Gears" : "Vitesses" ,
67+ "Gear" : "Vitesse" ,
68+ "Gear +" : "Vitesse +" ,
69+ "Gear -" : "Vitesse -" ,
70+ "Gears +" : "Vitesses +" ,
71+ "Gears -" : "Vitesses -" ,
72+ "Cadence" : "Cadence" ,
73+ "Resistance" : "Résistance" ,
74+ "Inclination" : "Inclinaison" ,
75+ "Incline" : "Pente" ,
76+ "Lap" : "Tour" ,
77+ },
78+ "Spanish" : {
79+ "Gears" : "Marchas" ,
80+ "Gear" : "Marcha" ,
81+ "Gear +" : "Marcha +" ,
82+ "Gear -" : "Marcha -" ,
83+ "Gears +" : "Marchas +" ,
84+ "Gears -" : "Marchas -" ,
85+ "Cadence" : "Cadencia" ,
86+ "Resistance" : "Resistencia" ,
87+ "Inclination" : "Inclinación" ,
88+ "Incline" : "Pendiente" ,
89+ "Lap" : "Vuelta" ,
90+ },
91+ "German" : {
92+ "Gears" : "Gänge" ,
93+ "Gear" : "Gang" ,
94+ "Gear +" : "Gang +" ,
95+ "Gear -" : "Gang -" ,
96+ "Gears +" : "Gänge +" ,
97+ "Gears -" : "Gänge -" ,
98+ "Cadence" : "Kadenz" ,
99+ "Resistance" : "Widerstand" ,
100+ "Inclination" : "Neigung" ,
101+ "Incline" : "Steigung" ,
102+ "Lap" : "Runde" ,
103+ },
104+ "Portuguese" : {
105+ "Gears" : "Marchas" ,
106+ "Gear" : "Marcha" ,
107+ "Gear +" : "Marcha +" ,
108+ "Gear -" : "Marcha -" ,
109+ "Gears +" : "Marchas +" ,
110+ "Gears -" : "Marchas -" ,
111+ "Cadence" : "Cadência" ,
112+ "Resistance" : "Resistência" ,
113+ "Inclination" : "Inclinação" ,
114+ "Incline" : "Inclinação" ,
115+ "Lap" : "Volta" ,
116+ },
117+ }
118+
43119
44120def should_skip_source (text : str ) -> bool :
45121 s = text .strip ()
@@ -94,12 +170,26 @@ def count_accelerators(text: str) -> int:
94170 return count
95171
96172
173+ def build_glossary_section (target_language : str ) -> str :
174+ lang_glossary = GLOSSARY .get (target_language , {})
175+ if not lang_glossary :
176+ return ""
177+ lines = [f'- "{ en } " → "{ tr } "' for en , tr in lang_glossary .items ()]
178+ return (
179+ "\n Domain glossary (fitness/cycling context — use these translations exactly):\n "
180+ + "\n " .join (lines )
181+ + "\n "
182+ )
183+
184+
97185def call_ollama (
98186 model : str ,
99187 source_text : str ,
100188 target_language : str ,
101189 timeout : int = 180 ,
102190) -> str :
191+ glossary_section = build_glossary_section (target_language )
192+
103193 prompt = f"""You are translating UI strings for QZ, a Qt/QML fitness app.
104194
105195Translate from English to { target_language } .
@@ -118,7 +208,7 @@ def call_ollama(
118208- Do not translate brand/product/protocol names: { ", " .join (BRANDS )} .
119209- Keep the translation short and suitable for a mobile app UI.
120210- If the source text is already a brand name, protocol name, number, symbol, or file pattern, return it unchanged.
121-
211+ { glossary_section }
122212Source text:
123213{ source_text }
124214"""
0 commit comments