#!/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")