C-Menu
0.2.9
A User Interface Toolkit
Toggle main menu visibility
Loading...
Searching...
No Matches
translate_po.py
Go to the documentation of this file.
1
#!/usr/bin/env python3
2
import
sys
3
import
json
4
import
re
5
import
urllib.request
6
7
OLLAMA_API_URL =
"http://localhost:11434/api/generate"
8
MODEL_NAME =
"mistral"
# Change to your preferred local model (e.g., qwen2.5-coder)
9
10
def
ask_ollama
(text, target_lang):
11
if
not
text.strip():
12
return
""
13
14
prompt = (
15
f
"You are a professional software localization tool translating text for a C terminal utility.\n"
16
f
"Translate the following string into fluent {target_lang}.\n"
17
f
"CRITICAL RULES:\n"
18
f
"1. Preserve all printf specifiers exactly as they are (e.g., %s, %d, %x).\n"
19
f
"2. Keep layout spaces, escape codes (like \\n or \\t) identical.\n"
20
f
"3. Output ONLY the raw translated string. Do not include quotes, markdown, or explanations.\n\n"
21
f
"String to translate: \"{text}\"\n"
22
f
"Translation:"
23
)
24
25
data = {
26
"model"
: MODEL_NAME,
27
"prompt"
: prompt,
28
"stream"
:
False
,
29
"options"
: {
"temperature"
: 0.1}
30
}
31
32
try
:
33
req = urllib.request.Request(
34
OLLAMA_API_URL,
35
data=json.dumps(data).encode(
'utf-8'
),
36
headers={
'Content-Type'
:
'application/json'
}
37
)
38
with
urllib.request.urlopen(req)
as
response:
39
res_data = json.loads(response.read().decode(
'utf-8'
))
40
return
res_data[
'response'
].strip().strip(
'"'
)
41
except
Exception
as
e:
42
print(f
"Error calling Ollama: {e}"
, file=sys.stderr)
43
return
""
44
45
def
split_to_po_format
(text, indent=0):
46
"""Splits a long translated string into multi-line PO syntax if it contains real newlines."""
47
# Split by actual literal '\n' characters embedded in the translation string
48
parts = text.split(
"\\n"
)
49
if
len(parts) == 1:
50
return
f
'"{text}"\n'
51
52
out =
'""\n'
53
for
i, part
in
enumerate(parts):
54
if
i < len(parts) - 1:
55
out += f
'"{part}\\n"\n'
56
elif
part:
# Append the trailing segment if it isn't empty
57
out += f
'"{part}"\n'
58
return
out
59
60
def
parse_and_translate_po
(filepath, target_lang):
61
print(f
"Translating {filepath} into {target_lang} using Ollama..."
)
62
with
open(filepath,
'r'
, encoding=
'utf-8'
)
as
f:
63
content = f.read()
64
65
# Regex block to match entire structural PO translation blocks, capturing multi-lines cleanly
66
entry_pattern = re.compile(
67
r'(msgid\s+(?:""\s*\n)?(?:".*"\s*\n)*)\s*(msgstr\s+(?:""\s*\n)?(?:".*"\s*\n)*)'
68
)
69
70
def
replace_entry(match):
71
msgid_block = match.group(1)
72
msgstr_block = match.group(2)
73
74
# Extract content inside quotes
75
msgid_strings = re.findall(
r'"(.*)"'
, msgid_block)
76
msgstr_strings = re.findall(
r'"(.*)"'
, msgstr_block)
77
78
full_msgid =
""
.join(msgid_strings)
79
full_msgstr =
""
.join(msgstr_strings)
80
81
# Only process if the ID exists and the translation string is currently completely empty
82
if
full_msgid
and
not
full_msgstr:
83
translated =
ask_ollama
(full_msgid, target_lang)
84
print(f
" '{full_msgid}'\n -> '{translated}'"
)
85
86
# Format the output matching PO line regulations
87
formatted_msgstr =
"msgstr "
+
split_to_po_format
(translated)
88
return
f
"{msgid_block}{formatted_msgstr}"
89
90
return
match.group(0)
91
92
updated_content = entry_pattern.sub(replace_entry, content)
93
94
with
open(filepath,
'w'
, encoding=
'utf-8'
)
as
f:
95
f.write(updated_content)
96
97
if
__name__ ==
"__main__"
:
98
if
len(sys.argv) < 3:
99
print(
"Usage: ./translate_po.py <path_to_po_file> <target_language>"
)
100
sys.exit(1)
101
parse_and_translate_po
(sys.argv[1], sys.argv[2])
102
translate_po.ask_ollama
ask_ollama(text, target_lang)
Definition
translate_po.py:10
translate_po.split_to_po_format
split_to_po_format(text, indent=0)
Definition
translate_po.py:45
translate_po.parse_and_translate_po
parse_and_translate_po(filepath, target_lang)
Definition
translate_po.py:60
translate_po.py
Generated by
1.17.0