Skip to content

Commit

Permalink
build(gettext): extend the "translate-desktop" command to cover the G…
Browse files Browse the repository at this point in the history
…NOME Shell extension

In the original (since lost) 1cfd35b, we tried to treat the GNOME Shell
extension as a separate "securedrop/extension" Weblate component with
its own "translate-extension" command, replicating both
"--extract-update" and "--compile" modes.

In f3d3f04, we tried to treat the GNOME Shell extension as an extension
(sorry) of the main "securedrop/securedrop" component, taking advantage
of Babel's mapping feature to parse the ".js.in" file as JavaScript.

Here we harmonize the two approaches.  "translate-desktop
--extract-update" is now a two-step process, first via Babel over the
".js.in" JavaScript file, then as usual via xgettext over the ".j2.in"
desktop templates.  (This is logically reversed, but Babel appears to
have no equivalent of xgettext's appending "--join-existing" mode.)
"translate-desktop --compile" similarly does two passes with msgfmt,
first to create the standard gettext directory layout and then to update
the desktop templates.

NB. We also add "msgmerge --no-fuzzy-matching" after #6772, which
required rebasing from "develop".
  • Loading branch information
cfm committed May 31, 2023
1 parent 821a2c7 commit 76f3ade
Show file tree
Hide file tree
Showing 2 changed files with 83 additions and 9 deletions.
4 changes: 2 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -94,8 +94,8 @@ coverage.xml
.hypothesis/
.mypy_cache/

# Translations
*.mo
# Translations compiled during packaging:
securedrop/translations/**/*.mo

# Flask stuff:
instance/
Expand Down
88 changes: 81 additions & 7 deletions securedrop/i18n_tool.py
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
LOCALE_DIR = {
"securedrop": "securedrop/translations",
"desktop": "install_files/ansible-base/roles/tails-config/templates",
"extension": "install_files/ansible-base/roles/tails-config/templates",
}


Expand Down Expand Up @@ -142,9 +141,44 @@ def translate_desktop(self, args: argparse.Namespace) -> None:

if args.extract_update:
sources = args.sources.split(",")
xgettext_flags = []

# First extract from JavaScript sources via Babel ("xgettext
# --language=JavaScript" can't parse gettext as exposed by GNOME
# JavaScript):
js_sources = [source for source in sources if ".js" in source]
if js_sources:
xgettext_flags.append("--join-existing")
subprocess.check_call(
[
"pybabel",
"extract",
"--charset=utf-8",
"--mapping",
args.mapping,
"--output",
"desktop.pot",
"--project=SecureDrop",
"--version",
args.version,
"[email protected]",
"--copyright-holder=Freedom of the Press Foundation",
"--add-comments=Translators:",
"--strip-comments",
"--add-location=never",
"--no-wrap",
*js_sources,
],
cwd=args.translations_dir,
)

# Then extract from desktop templates via xgettext in appending
# "--join-existing" mode:
desktop_sources = [source for source in sources if ".js" not in source]
subprocess.check_call(
[
"xgettext",
*xgettext_flags,
"--output=desktop.pot",
"--language=Desktop",
"--keyword",
Expand All @@ -153,7 +187,7 @@ def translate_desktop(self, args: argparse.Namespace) -> None:
args.version,
"[email protected]",
"--copyright-holder=Freedom of the Press Foundation",
*sources,
*desktop_sources,
],
cwd=args.translations_dir,
)
Expand All @@ -168,7 +202,9 @@ def translate_desktop(self, args: argparse.Namespace) -> None:
if not f.endswith(".po"):
continue
po_file = os.path.join(args.translations_dir, f)
subprocess.check_call(["msgmerge", "--update", po_file, messages_file])
subprocess.check_call(
["msgmerge", "--no-fuzzy-matching", "--update", po_file, messages_file]
)
log.warning(f"messages translations updated in {messages_file}")
else:
log.warning("desktop translations are already up to date")
Expand All @@ -181,7 +217,34 @@ def translate_desktop(self, args: argparse.Namespace) -> None:
try:
open(linguas_file, "w").write(content)

for source in args.sources.split(","):
# First, compile each message catalog for normal gettext use.
# We have to iterate over them, rather than using "pybabel
# compile --directory", in order to replicate gettext's
# standard "{locale}/LC_MESSAGES/{domain}.mo" structure (like
# "securedrop/translations").
locale_dir = os.path.join(args.translations_dir, "locale")
for po in pos:
locale = po.replace(".po", "")
output_dir = os.path.join(locale_dir, locale, "LC_MESSAGES")
subprocess.run(["mkdir", "--parents", output_dir], check=True)
subprocess.run(
[
"msgfmt",
"--output-file",
os.path.join(output_dir, "messages.mo"),
po,
],
check=True,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
cwd=args.translations_dir,
)

# Then use msgfmt to update the desktop files as usual.
desktop_sources = [
source for source in args.sources.split(",") if ".js" not in source
]
for source in desktop_sources:
target = source.rstrip(".in")
subprocess.check_call(
[
Expand Down Expand Up @@ -247,8 +310,7 @@ def set_translate_messages_parser(self, subps: _SubParsersAction) -> None:
"translate-messages", help=("Update and compile " "source and template translations")
)
translations_dir = join(dirname(realpath(__file__)), "translations")
extension_location = join(dirname(realpath(__file__)), "..", LOCALE_DIR["extension"])
sources = join(".,source_templates,journalist_templates,", extension_location)
sources = ".,source_templates,journalist_templates"
self.set_translate_parser(parser, translations_dir, sources)
mapping = "babel.cfg"
parser.add_argument(
Expand All @@ -267,7 +329,19 @@ def set_translate_desktop_parser(self, subps: _SubParsersAction) -> None:
"..",
LOCALE_DIR["desktop"],
)
sources = "desktop-journalist-icon.j2.in,desktop-source-icon.j2.in"
sources = ",".join(
[
"desktop-journalist-icon.j2.in",
"desktop-source-icon.j2.in",
"extension.js.in",
]
)
mapping = join(dirname(realpath(__file__)), "babel.cfg")
parser.add_argument(
"--mapping",
default=mapping,
help=f"Mapping of files to consider (default {mapping})",
)
self.set_translate_parser(parser, translations_dir, sources)
parser.set_defaults(func=self.translate_desktop)

Expand Down

0 comments on commit 76f3ade

Please sign in to comment.