BastionGuard/sanitize_po_keep_last.py
specialworld83 f0f913a209 Release 2.0
2026-07-15 10:52:22 +02:00

222 lines
4.6 KiB
Python
Executable file

#!/usr/bin/env python3
import sys
import re
import ast
if len(sys.argv) != 3:
print("Usage: sanitize_po_keep_last.py input.po output.po", file=sys.stderr)
sys.exit(1)
input_file = sys.argv[1]
output_file = sys.argv[2]
with open(input_file, "r", encoding="utf-8") as f:
content = f.read()
def split_entries(text):
entries = []
current = []
for line in text.splitlines(keepends=True):
if line.strip() == "":
if current:
entries.append("".join(current))
current = []
else:
current.append(line)
if current:
entries.append("".join(current))
return entries
def read_po_string(lines, start_index):
value = ""
i = start_index
first = lines[i].strip()
m = re.match(
r'^(msgctxt|msgid|msgid_plural|msgstr(?:\[[0-9]+\])?)\s+(.*)$',
first
)
if not m:
return "", i
raw = m.group(2).strip()
try:
value += ast.literal_eval(raw)
except Exception:
pass
i += 1
while i < len(lines):
s = lines[i].strip()
if not s.startswith('"'):
break
try:
value += ast.literal_eval(s)
except Exception:
pass
i += 1
return value, i - 1
def entry_key(entry):
lines = entry.splitlines()
msgctxt = None
msgid = None
i = 0
while i < len(lines):
stripped = lines[i].strip()
if stripped.startswith("msgctxt "):
msgctxt, i = read_po_string(lines, i)
elif stripped.startswith("msgid "):
msgid, i = read_po_string(lines, i)
break
i += 1
if msgid is None:
return None
return (msgctxt, msgid)
def po_quote(s):
return '"' + s.replace("\\", "\\\\").replace('"', '\\"').replace("\n", "\\n") + '"'
def clean_conflict_msgstr(entry):
"""
Ripulisce i msgstr sporchi generati da merge gettext tipo:
#-#-#-#-# file1.po #-#-#-#-#
Traduzione vecchia
#-#-#-#-# file2.po #-#-#-#-#
Traduzione nuova
Tiene solo l'ultima traduzione utile.
"""
if "#-#-#-#-#" not in entry:
return entry
lines = entry.splitlines()
new_lines = []
i = 0
while i < len(lines):
stripped = lines[i].strip()
if stripped.startswith("msgstr "):
msgstr_value, end_i = read_po_string(lines, i)
if "#-#-#-#-#" in msgstr_value:
parts = re.split(
r'#-#-#-#-#.*?#-#-#-#-#\n?',
msgstr_value,
flags=re.DOTALL
)
parts = [p.strip() for p in parts if p.strip()]
chosen = parts[-1] if parts else ""
new_lines.append('msgstr ""')
if chosen:
new_lines.append(po_quote(chosen))
else:
new_lines.extend(lines[i:end_i + 1])
i = end_i + 1
continue
if re.match(r"msgstr\[[0-9]+\]\s+", stripped):
msgstr_value, end_i = read_po_string(lines, i)
if "#-#-#-#-#" in msgstr_value:
parts = re.split(
r'#-#-#-#-#.*?#-#-#-#-#\n?',
msgstr_value,
flags=re.DOTALL
)
parts = [p.strip() for p in parts if p.strip()]
chosen = parts[-1] if parts else ""
prefix = stripped.split(None, 1)[0]
new_lines.append(f'{prefix} ""')
if chosen:
new_lines.append(po_quote(chosen))
else:
new_lines.extend(lines[i:end_i + 1])
i = end_i + 1
continue
new_lines.append(lines[i])
i += 1
return "\n".join(new_lines) + "\n"
entries = split_entries(content)
header_entries = []
normal_entries = []
for entry in entries:
if not entry.strip():
continue
key = entry_key(entry)
# Header gettext: msgid ""
if key == (None, ""):
header_entries.append(clean_conflict_msgstr(entry))
continue
if key is None:
header_entries.append(entry)
continue
normal_entries.append((key, clean_conflict_msgstr(entry)))
# Tiene SOLO l'ultima occorrenza per ogni coppia msgctxt/msgid.
last_by_key = {}
order = []
for key, entry in normal_entries:
if key not in last_by_key:
order.append(key)
last_by_key[key] = entry
with open(output_file, "w", encoding="utf-8") as f:
for entry in header_entries:
f.write(entry.rstrip())
f.write("\n\n")
for key in order:
f.write(last_by_key[key].rstrip())
f.write("\n\n")