[ Web Proxy ]
URL:
Viewing: https://raw.githubusercontent.com/python/python-docs-fa/3.15/scripts/decode_urls_to_plain_text.py [Back]  [Original]

#!/usr/bin/env python3
"""
Decode percent-encoded URLs inside .po file(s) (e.g. Persian Wikipedia
links like https://fa.wikipedia.org/wiki/%D9%85%D8%AD%D9%85%D9%88%D9%84)
into plain UTF-8 text. This avoids translation-checker tools mistaking
sequences like %D9, %A7, %E2 for printf-style placeholders (%D, %A, %E).

Usage:
    python3 decode_po_urls.py input.po [output.po]
    python3 decode_po_urls.py some_dir/ [output_dir/]
    python3 decode_po_urls.py some_dir/ --recursive

If a single input.po is given with no output.po, the file is edited in
place. If a directory is given with no output_dir, all *.po files in it
are edited in place (non-recursively by default; pass --recursive / -r
to also descend into subdirectories). If an output_dir is given, fixed
copies are written there (mirroring the relative paths found), and the
originals are left untouched.
"""

import re
import sys
from pathlib import Path
from urllib.parse import unquote

URL_RE = re.compile(r'https?://[^\s`"]+')


def decode_urls_in_url(url: str) -> str:
    if "%" not in url:
        return url
    return unquote(url)


def decode_urls(text: str) -> str:
    """Decode percent-encoded URLs, but only inside msgstr blocks.

    A .po file alternates msgid "..." / msgstr "..." blocks, each possibly
    spanning multiple quoted-string lines. We only want to touch the
    translated (msgstr) text -- the msgid must stay identical to the
    original source string.
    """
    lines = text.split("\n")
    out_lines = []
    in_msgstr = False

    for line in lines:
        stripped = line.lstrip()
        if stripped.startswith("msgid"):
            in_msgstr = False
        elif stripped.startswith("msgstr"):
            in_msgstr = True
        elif not stripped.startswith('"'):
            # Any other line (comments, blank lines, flags) ends the block.
            in_msgstr = False

        if in_msgstr:
            line = URL_RE.sub(lambda m: decode_urls_in_url(m.group(0)), line)

        out_lines.append(line)

    return "\n".join(out_lines)


def process_file(in_path: Path, out_path: Path) -> int:
    content = in_path.read_text(encoding="utf-8")
    new_content = decode_urls(content)

    n_changed = sum(
        1 for a, b in zip(content.splitlines(), new_content.splitlines()) if a != b
    )

    if n_changed:
        out_path.parent.mkdir(parents=True, exist_ok=True)
        out_path.write_text(new_content, encoding="utf-8")
        print(f"Decoded URLs on {n_changed} line(s): {in_path} -> {out_path}")
    elif out_path != in_path:
        # No changes, but writing to a different location: still copy it
        # over so the output directory mirrors the full input set.
        out_path.parent.mkdir(parents=True, exist_ok=True)
        out_path.write_text(content, encoding="utf-8")

    return n_changed


def main() -> None:
    args = [a for a in sys.argv[1:] if a not in ("--recursive", "-r")]
    recursive = any(a in ("--recursive", "-r") for a in sys.argv[1:])

    if not args:
        print(__doc__)
        sys.exit(1)

    in_path = Path(args[0])
    out_arg = Path(args[1]) if len(args) > 1 else None

    if not in_path.exists():
        print(f"No such file or directory: {in_path}")
        sys.exit(1)

    total_files = 0
    total_changed = 0

    if in_path.is_file():
        out_path = out_arg if out_arg else in_path
        total_changed += process_file(in_path, out_path)
        total_files += 1
    else:
        pattern = "**/*.po" if recursive else "*.po"
        po_files = sorted(in_path.glob(pattern))
        if not po_files:
            print(
                f"No .po files found in {in_path}"
                f"{' (recursively)' if recursive else ''}."
            )
            sys.exit(0)

        for f in po_files:
            rel = f.relative_to(in_path)
            out_path = (out_arg / rel) if out_arg else f
            total_changed += process_file(f, out_path)
            total_files += 1

    print(f"\nDone. Scanned {total_files} file(s).")


if __name__ == "__main__":
    main()

Web Proxy Viewer  |  New URL  |  Original Page