Files
ios/Scripts/fix-localizable-strings/main.py

200 lines
6.2 KiB
Python

#!/usr/bin/env python3
"""
fix-localizable-strings
A collection of tools for maintaining Localizable.strings files.
Usage:
python Scripts/fix-localizable-strings/main.py delete-duplicates \\
--strings <path/to/Localizable.strings> \\
[--dry-run]
python Scripts/fix-localizable-strings/main.py delete-unused \\
--strings <path/to/Localizable.strings> \\
--swift-source <dir> [--swift-source <dir> ...] \\
[--dry-run]
python Scripts/fix-localizable-strings/main.py fix-ellipsis \\
--strings <path/to/Localizable.strings> \\
[--dry-run]
"""
import argparse
import os
import sys
from delete_duplicate_strings import delete_duplicates, deduplicate
from delete_unused_strings import delete_unused, delete_unused_content, find_used_keys
from fix_ellipsis import fix_ellipsis, fix_ellipsis_file
def _pluralize(count: int, singular: str, plural: str) -> str:
return singular if count == 1 else plural
def cmd_delete_duplicates(args: argparse.Namespace) -> None:
if args.dry_run:
with open(args.strings, encoding="utf-8") as f:
content = f.read()
_, removed = deduplicate(content)
if not removed:
print(" No duplicate strings found.")
return
noun = _pluralize(len(removed), "occurrence", "occurrences")
print(f" Found {len(removed)} duplicate {noun}:")
for key in removed:
print(f" {key}")
print("\n Dry run — no changes written.")
return
removed = delete_duplicates(args.strings)
if not removed:
print(" No duplicate strings found.")
return
noun = _pluralize(len(removed), "occurrence", "occurrences")
print(f" Removed {len(removed)} duplicate {noun}:")
for key in removed:
print(f" {key}")
def cmd_delete_unused(args: argparse.Namespace) -> None:
if args.dry_run:
with open(args.strings, encoding="utf-8") as f:
content = f.read()
swift_sources = []
for swift_dir in args.swift_sources:
for dirpath, _, filenames in os.walk(swift_dir):
for filename in filenames:
if filename.endswith(".swift"):
with open(os.path.join(dirpath, filename), encoding="utf-8") as f:
swift_sources.append(f.read())
used_keys = find_used_keys(swift_sources)
_, removed = delete_unused_content(content, used_keys)
if not removed:
print(" No unused strings found.")
return
noun = _pluralize(len(removed), "key", "keys")
print(f" Found {len(removed)} unused {noun}:")
for key in removed:
print(f" {key}")
print("\n Dry run — no changes written.")
return
removed = delete_unused(args.strings, args.swift_sources)
if not removed:
print(" No unused strings found.")
return
noun = _pluralize(len(removed), "key", "keys")
print(f" Removed {len(removed)} unused {noun}:")
for key in removed:
print(f" {key}")
def cmd_fix_ellipsis(args: argparse.Namespace) -> None:
if args.dry_run:
with open(args.strings, encoding="utf-8") as f:
content = f.read()
_, changed = fix_ellipsis(content)
if not changed:
print(" No three-dot sequences found.")
return
noun = _pluralize(len(changed), "entry", "entries")
print(f" Found {len(changed)} {noun} with three-dot sequences:")
for key in changed:
print(f" {key}")
print("\n Dry run — no changes written.")
return
changed = fix_ellipsis_file(args.strings)
if not changed:
print(" No three-dot sequences found.")
return
noun = _pluralize(len(changed), "entry", "entries")
print(f" Fixed {len(changed)} {noun} with three-dot sequences:")
for key in changed:
print(f" {key}")
def build_parser():
parser = argparse.ArgumentParser(
description="Tools for maintaining Localizable.strings files."
)
subparsers = parser.add_subparsers(dest="command")
dup_parser = subparsers.add_parser(
"delete-duplicates",
help="Remove duplicate string keys, preserving the first occurrence.",
)
dup_parser.add_argument(
"--strings",
required=True,
metavar="PATH",
help="Path to the Localizable.strings file to process.",
)
dup_parser.add_argument(
"--dry-run",
action="store_true",
help="Report duplicates without modifying the strings file.",
)
unused_parser = subparsers.add_parser(
"delete-unused",
help="Remove string keys that are never referenced in Swift source code.",
)
unused_parser.add_argument(
"--strings",
required=True,
metavar="PATH",
help="Path to the Localizable.strings file to process.",
)
unused_parser.add_argument(
"--swift-source",
required=True,
action="append",
dest="swift_sources",
metavar="DIR",
help="Directory to search recursively for Swift source files. May be repeated.",
)
unused_parser.add_argument(
"--dry-run",
action="store_true",
help="Report unused keys without modifying the strings file.",
)
ellipsis_parser = subparsers.add_parser(
"fix-ellipsis",
help="Replace three-dot sequences (...) with the Unicode ellipsis character (…) in string values.",
)
ellipsis_parser.add_argument(
"--strings",
required=True,
metavar="PATH",
help="Path to the Localizable.strings file to process.",
)
ellipsis_parser.add_argument(
"--dry-run",
action="store_true",
help="Report affected entries without modifying the strings file.",
)
return parser
def main():
parser = build_parser()
args = parser.parse_args()
if args.command == "delete-duplicates":
cmd_delete_duplicates(args)
elif args.command == "delete-unused":
cmd_delete_unused(args)
elif args.command == "fix-ellipsis":
cmd_fix_ellipsis(args)
else:
parser.print_help()
sys.exit(1)
if __name__ == "__main__":
main()