91 lines
2.6 KiB
Python
91 lines
2.6 KiB
Python
#!/usr/bin/env python
|
|
"""Filter .po files to keep only entries for a specific module."""
|
|
|
|
from __future__ import annotations
|
|
|
|
import argparse
|
|
from pathlib import Path
|
|
from typing import Iterable
|
|
|
|
import polib
|
|
|
|
|
|
def _extract_modules_from_comment(entry: polib.POEntry) -> set[str]:
|
|
"""Return module names declared in translator comments (#.)."""
|
|
|
|
tcomment = entry.tcomment or ""
|
|
modules: set[str] = set()
|
|
for line in tcomment.splitlines():
|
|
stripped = line.strip()
|
|
lowered = stripped.lower()
|
|
if lowered.startswith("module:") or lowered.startswith("modules:"):
|
|
_, _, tail = stripped.partition(":")
|
|
for module in tail.split(","):
|
|
module_name = module.strip()
|
|
if module_name:
|
|
modules.add(module_name)
|
|
return modules
|
|
|
|
|
|
def belongs_to_module(entry: polib.POEntry, module_name: str) -> bool:
|
|
"""Return True if the entry references ONLY the target module."""
|
|
|
|
declared_modules = _extract_modules_from_comment(entry)
|
|
if declared_modules:
|
|
return declared_modules == {module_name}
|
|
|
|
locations = [occ[0] for occ in entry.occurrences if occ and occ[0]]
|
|
if locations:
|
|
return all(module_name in location for location in locations)
|
|
return False
|
|
|
|
|
|
def filter_po_file(path: Path, module_name: str, dry_run: bool = False) -> None:
|
|
po = polib.pofile(str(path))
|
|
kept_entries: list[polib.POEntry] = []
|
|
removed = 0
|
|
|
|
for entry in po:
|
|
if entry.msgid == "":
|
|
kept_entries.append(entry)
|
|
continue
|
|
|
|
if belongs_to_module(entry, module_name):
|
|
kept_entries.append(entry)
|
|
else:
|
|
removed += 1
|
|
|
|
if dry_run:
|
|
print(
|
|
f"[DRY-RUN] {path}: would keep {len(kept_entries)} entries, remove {removed} entries"
|
|
)
|
|
return
|
|
|
|
new_po = polib.POFile()
|
|
new_po.metadata = po.metadata
|
|
|
|
for entry in kept_entries:
|
|
new_po.append(entry)
|
|
|
|
new_po.save(str(path))
|
|
print(
|
|
f"Filtered {path}: kept {len(kept_entries)} entries, removed {removed} entries"
|
|
)
|
|
|
|
|
|
def parse_args(args: Iterable[str] | None = None) -> argparse.Namespace:
|
|
parser = argparse.ArgumentParser(description=__doc__)
|
|
parser.add_argument("module", help="Module technical name to keep")
|
|
parser.add_argument("files", nargs="+", type=Path, help=".po files to filter")
|
|
parser.add_argument("--dry-run", action="store_true", help="Only report counts")
|
|
return parser.parse_args(args)
|
|
|
|
|
|
def main() -> None:
|
|
args = parse_args()
|
|
for file_path in args.files:
|
|
filter_po_file(file_path, args.module, dry_run=args.dry_run)
|
|
|
|
|
|
if __name__ == "__main__":
|
|
main()
|