Skip to content

packaging/windows: i18n the installer (auto-driven from po/<lang>.po) - #899

Merged
mrjimenez merged 8 commits into
amule-project:masterfrom
got3nks:feat/installer-i18n
Jun 7, 2026
Merged

packaging/windows: i18n the installer (auto-driven from po/<lang>.po)#899
mrjimenez merged 8 commits into
amule-project:masterfrom
got3nks:feat/installer-i18n

Conversation

@got3nks

@got3nks got3nks commented Jun 7, 2026

Copy link
Copy Markdown
Contributor

Refs #897.

Wires the Windows installer for full i18n in three commits:

1. installer.nsi — register all NSIS-bundled languages + extract custom strings

  • !insertmacro MUI_LANGUAGE "..." for every language NSIS 3.x's Contrib/Language files/ ships (67 entries). The Welcome / License / Components / Directory / Install / Finish pages + standard buttons + error dialogs come translated for free.
  • Custom strings (section names, descriptions, MessageBox / DetailPrint) extracted into 16 LangString MYSTR_* blocks. English bodies inlined as the baseline.
  • Section names use SectionSetText in .onInit / un.onInit (Section first-arg is compile-time; the runtime locale is applied via this NSIS API; preserves the magic "Uninstall" entry-point and un.* dispatch rules).
  • English first → NSIS auto-fallback for any LangString a non-English language is missing.

2. installer_strings.c + POTFILES.in — make strings visible to xgettext

  • New stub file packaging/windows/installer_strings.c lists each translatable string inside _("..."). Uses a local #define _(s) (s) so editors parse it cleanly; the file is never compiled.
  • Added to po/POTFILES.in so the existing xgettext sweep picks them up.
  • po/amule.pot regenerated — installer strings now appear next to the app strings in every po/<lang>.po after the next msgmerge pass.

3. po-to-nsh.py — generate per-language LangString blocks from .po

  • Bridge script reads each po/<locale>.po, matches msgstr entries against the English msgids of the installer string keys, translates standard C escapes (\r, \n, ", \) into NSIS string-literal escapes ($\r, $\n, $\", $\\), passes NSIS runtime variables ($INSTDIR, $0, $APPDATA) through verbatim.
  • Output: packaging/windows/installer_strings_generated.nsh — gitignored, regenerated on every installer build.
  • installer.nsi pulls it in via !include /NONFATAL after the hand-written English block. /NONFATAL so developers can still invoke makensis directly without going through build.sh.
  • build.sh installer runs python3 po-to-nsh.py po/ installer_strings_generated.nsh before makensis.
  • packaging/windows/README.md grows an Installer translations subsection covering the auto-detect / fallback behaviour, the translator workflow, and the three files to update in lockstep when adding a new string.

Translator workflow

Same as for app strings: edit po/<lang>.po. The installer strings appear in the .po marked #: packaging/windows/installer_strings.c:NN. Translate them, commit. The next installer build picks them up automatically.

Test plan

  • packaging green — CI's windows-installer matrix (x64 + arm64) compiles installer.nsi against the runner's NSIS distribution and validates that every MUI_LANGUAGE name maps to a real .nlf file. (Initial run flagged Malagasy.nlf missing from the Chocolatey-installed NSIS; dropped from the list; the remaining 67 names resolved. A re-run on the head of this branch is what gates this checkbox.)
  • manual verification — Download the installer artifacts from the green packaging run, install on a Windows host whose system locale is something other than English (e.g. French or German). Confirm the MUI pages (Welcome / License / Components / Directory / Install / Finish) render in that language, section names + descriptions on the Components page render in English (the LangString tables won't have non-English bodies until translators contribute via po/.po), and uninstall flows end-to-end without errors. Also smoke-test once with a system locale not in NSIS's bundle (e.g. Esperanto) to confirm graceful fallback to English.

got3nks added 4 commits June 7, 2026 13:31
The Windows installer was English-only by default -- the original
afd508c commit only registered `!insertmacro MUI_LANGUAGE "English"`
and inlined every section name / description / MessageBox / DetailPrint
as a hardcoded English literal. Translating it required two changes:
register the additional MUI_LANGUAGE entries (which pulls in NSIS's
bundled translations for the Welcome / License / Components / Directory
/ Install / Finish pages plus all standard buttons and error dialogs
"for free"), and extract the aMule-specific strings to LangString
tables so they can be localized.

Register MUI_LANGUAGE for all 37 catalogs in po/ that map to a NSIS
bundled language file. English is registered first so it acts as the
runtime fallback for any LangString entry a given language is missing.
NSIS auto-detects the user's system locale at install / uninstall time
and routes to the matching registered language; no picker is shown.

Extract the aMule-specific user-facing strings into 16 LangString
declarations (5 section names, 5 component descriptions, 6 runtime
messages). Section first-args remain English literals because NSIS
evaluates them at compile time -- the runtime locale is applied via
SectionSetText in .onInit / un.onInit, which also keeps the magic
"Uninstall" entry-point name and the `un.*` dispatch-prefix rules
intact for the uninstaller block. MessageBox / DetailPrint / MUI_*_
DESCRIPTION_TEXT call sites use the LangString refs directly.

Initial commit ships only the ${LANG_ENGLISH} bodies. Translators
contribute additional per-${LANG_*} blocks via follow-up PRs (one
block per language). A future change can autogenerate the per-language
blocks from the aMule po/ catalogs so translators stay on the existing
gettext workflow; that bridging script is deferred to a follow-up
since it requires a small extraction stub in the source tree to make
the installer strings visible to xgettext and a po-to-nsh script in
packaging/windows/.

Refs amule-project#897.
Follow-up to the previous commit, which registered only the 37
languages aMule has a po/ catalog for. The MUI built-in pages come
from NSIS's bundled .nlf files for free for every registered
language -- so capping at the po/ subset under-served users whose
Windows is in a language NSIS supports but aMule's runtime app
catalog doesn't.

Register every language NSIS 3.x ships a Contrib/Language file for
(67 entries). Users whose system language is in this broader set
now get the MUI Welcome / License / Components / Directory / Install
/ Finish pages plus standard buttons and error dialogs in their
language even if aMule has no app-level translation for it.

The aMule-specific strings (the LangString block below the language
registrations) continue to fall back to English for any language
not represented in po/ -- NSIS's "first registered language wins
for missing LangString entries" rule applies, and English remains
first in the list.
Missed in the prior commit's pass over NSIS 3.x's Contrib/Language
files set. The bundle ships Malagasy.nsh; the rest of the list
(Armenian through Welsh) is already covered.
Adds packaging/windows/installer_strings.c -- a stub file that lists
each translatable string from installer.nsi inside a _() invocation
so the existing xgettext sweep over POTFILES.in picks them up into
po/amule.pot. The file uses a local `#define _(s) (s)` so it parses
as valid C in editors with C language servers; it is not built into
any artifact.

This gives translators a single workflow: they keep editing
po/<lang>.po the way they do today, and the installer-specific
strings now appear in their .po next to the app strings. The
per-language LangString blocks for the installer are generated from
the resulting msgstr entries by the bridge script added in the next
commit.

Add packaging/windows/installer_strings.c to po/POTFILES.in and
regenerate po/amule.pot from the existing Makevars (--keyword=_
--keyword=wxTRANSLATE --keyword=wxPLURAL:1,2 --from-code=UTF-8) so
the new strings are visible immediately. The bulk of the .pot diff
is line-ref shifts in unchanged entries (source files moved since
the last regeneration in f936bc5); the new content sits at the
end and is straightforward to spot.

Refs amule-project#897.
@got3nks
got3nks force-pushed the feat/installer-i18n branch from 214f7cb to 60fb06e Compare June 7, 2026 11:33
got3nks added 4 commits June 7, 2026 14:04
Closes the loop from amule-project#897: translators contribute Windows installer
strings the same way they contribute app strings -- by editing
po/<lang>.po -- and the per-language LangString blocks for the
installer are generated from those .po files at installer build time
by packaging/windows/po-to-nsh.py.

The bridge script:
- maps each gettext locale aMule carries a po/ catalog for to the
  matching NSIS Contrib/Language file (Italian, French, German, ...
  -- table at the top of po-to-nsh.py);
- parses each po/<locale>.po and matches msgstr entries against the
  English msgids declared as the installer string keys (the KEYS
  list mirrors installer_strings.c and the hand-written
  ${LANG_ENGLISH} block in installer.nsi);
- translates standard C escapes in the msgstr (\r, \n, ", \\) into
  their NSIS string-literal equivalents ($\r, $\n, $\", $\\) and
  passes NSIS runtime variables ($INSTDIR, $0, $APPDATA) through
  verbatim;
- emits a `LangString MYSTR_KEY ${LANG_NSISNAME} "..."` line per
  (key, language) pair into installer_strings_generated.nsh.

Wiring:
- installer.nsi `!include /NONFATAL "installer_strings_generated.nsh"`
  after the hand-written English LangString block. /NONFATAL means
  developers invoking makensis directly without going through
  build.sh still get a working installer (custom strings stay English
  for non-English languages via NSIS's first-registered-fallback).
- packaging/windows/build.sh `installer` subcommand runs
  python3 po-to-nsh.py po/ installer_strings_generated.nsh
  before makensis. python3 is available in every shipping build
  environment (MSYS2 includes it; Linux + macOS runners ship it).
- .gitignore excludes installer_strings_generated.nsh: it's a build
  artifact, regenerated on every installer build from the .po files.
- packaging/windows/README.md grows an "Installer translations"
  subsection covering the auto-detect / fallback behaviour, the
  translator workflow (edit po/<lang>.po as normal), and the three
  files to update in lockstep when adding or renaming a string.

Refs amule-project#897.
NSIS 3.x (as installed via Chocolatey on the CI windows-installer
runner) ships 67 .nlf files under Contrib/Language files/. The
initial registration list included three names that aren't in that
set, so makensis aborts on the MUI_LANGUAGEEX include:

  Malagasy   (Malagasy.nlf not in NSIS 3.x)
  Uyghur     (Uyghur.nlf not in NSIS 3.x)
  Yiddish    (Yiddish.nlf not in NSIS 3.x)

Drop all three. Confirmed by installing NSIS via choco on a local
Windows host and diffing the bundled language set against the
installer's registration list. The remaining 67 entries match the
NSIS bundle exactly.
xgettext defaults the comment-line copyright to the literal 'YEAR'
placeholder when --package-version doesn't drive it; the prior
amule.pot had it substituted to 2026. Restore that.
The windows-installer matrix on the GH Actions runner failed at
`packaging/windows/build.sh installer` with
'python3: command not found'. MSYS2's `python` package ships the
interpreter as `python` -- there's no `python3` symlink in the base
install -- and the workflow's msys2/setup-msys2 step wasn't
installing the package at all.

Fix both ends:

- packaging/windows/build.sh: try `python3` then `python`, pick the
  first that resolves; fail with a helpful pacman/apt hint if
  neither is available.
- .github/workflows/packaging.yml: add `python` to the MSYS2 install
  list for the windows-installer job so the runner has it.

The defensive build.sh change also covers anyone invoking the
installer build outside CI (e.g. a developer on a dev Linux/macOS
box where the interpreter may only be called `python`).
@got3nks
got3nks force-pushed the feat/installer-i18n branch from 60fb06e to b48d6e7 Compare June 7, 2026 12:04
@got3nks
got3nks marked this pull request as ready for review June 7, 2026 12:31
@mrjimenez
mrjimenez merged commit d739bc4 into amule-project:master Jun 7, 2026
7 checks passed
got3nks added a commit to got3nks/amule that referenced this pull request Jun 7, 2026
Picks up the entries left untranslated or marked fuzzy after the
preceding pot/po regen pass:

* po/it.po -- 14 untranslated installer-string entries (extracted
  from packaging/windows/installer_strings.c via amule-project#899) and 4 fuzzy
  entries:
  - 'Uninstall' had been msgmerge-mapped to the existing 'Installa'
    translation (wrong direction); fixed to 'Disinstalla'.
  - 'aMule application files (required).' had been msgmerge-mapped to
    the existing 'Integrazione di aMule non riuscita' translation
    (unrelated); fixed to the literal 'File dell'applicazione aMule
    (obbligatori).'.
  - 'Start aMule when I log in' kept the substantively correct
    existing translation, just unfuzzied.
  - The web-server-startup message updated to reference the
    -DBUILD_WEBSERVER=YES CMake flag we use today.

* docs/man/po/manpages-it.po -- 9 untranslated entries (the new
  --configure-autostart / --force-zlib / -t/--category / -o-and-p
  optional-arg / GD-support / aMule-online-sign FILES entries from
  the amule-project#900 audit fixes) and 5 fuzzy entries on synopsis lines and
  the 'all link found' grammar fix.

Refs amule-org/amule-org.github.io#4.
mrjimenez pushed a commit that referenced this pull request Jun 7, 2026
Pre-release pot/po refresh ahead of 3.0.0. Driven by
scripts/update-po.sh which runs xgettext over POTFILES.in to
regenerate po/amule.pot and then msgmerge into every po/*.po.

The substantive change picked up by this sweep is the 16 Windows-
installer translatable strings extracted into amule.pot via the
packaging/windows/installer_strings.c stub added in #899 (the
LangString MYSTR_* set used by packaging/windows/installer.nsi via
the po-to-nsh.py bridge). Each catalog gains those 16 entries with
empty msgstr; translators see them in the next .po edit session and
the installer build's bridge script flips them from English
fallback to the translated text as soon as a non-empty msgstr is
present.

The bulk of the diff outside that is cosmetic: gettext-tools 0.27
rewraps some long strings differently than whatever version last
ran an update on the committed catalogs, and the #: source-location
refs shift by a few lines as source files have moved on since the
last sweep. No msgid content was lost; no existing translation was
discarded -- fuzzy markers grow only on entries whose source string
genuinely changed (e.g. man-page strings touched by the audit
fixes in #900).

Refs amule-org/amule-org.github.io#4 (pre-3.0.0 housekeeping).
mrjimenez pushed a commit that referenced this pull request Jun 7, 2026
Picks up the entries left untranslated or marked fuzzy after the
preceding pot/po regen pass:

* po/it.po -- 14 untranslated installer-string entries (extracted
  from packaging/windows/installer_strings.c via #899) and 4 fuzzy
  entries:
  - 'Uninstall' had been msgmerge-mapped to the existing 'Installa'
    translation (wrong direction); fixed to 'Disinstalla'.
  - 'aMule application files (required).' had been msgmerge-mapped to
    the existing 'Integrazione di aMule non riuscita' translation
    (unrelated); fixed to the literal 'File dell'applicazione aMule
    (obbligatori).'.
  - 'Start aMule when I log in' kept the substantively correct
    existing translation, just unfuzzied.
  - The web-server-startup message updated to reference the
    -DBUILD_WEBSERVER=YES CMake flag we use today.

* docs/man/po/manpages-it.po -- 9 untranslated entries (the new
  --configure-autostart / --force-zlib / -t/--category / -o-and-p
  optional-arg / GD-support / aMule-online-sign FILES entries from
  the #900 audit fixes) and 5 fuzzy entries on synopsis lines and
  the 'all link found' grammar fix.

Refs amule-org/amule-org.github.io#4.
got3nks added a commit to got3nks/amule that referenced this pull request Jun 7, 2026
…ule-project#912)

Extends existing categories (preferring extensions over new lines):
- Performance/Upload: amule-project#898 SlotAllocation default raised.
- Networking & Discovery: wire-parser hardening list extended with
  amule-project#879/amule-project#882/amule-project#890/amule-project#886; new amuleweb security hardening bullet
  consolidating ngosang's amule-project#869-amule-project#874 triage (all landed in amule-project#875);
  amulegui list extended with amule-project#857; shared-folder watcher extended
  with amule-project#858.
- Packaging: Windows installer i18n line extended with amule-project#899.
- Internals & Refactoring: new docs-polish + code-quality bullets
  covering amule-project#851/amule-project#855/amule-project#862/amule-project#888/amule-project#900/amule-project#866/amule-project#867/amule-project#895 and amule-project#909/amule-project#910/amule-project#912.
- Translations: new pre-release final-wave bullet covering amule-project#847/amule-project#856/
  amule-project#891/amule-project#908/amule-project#860/amule-project#904/amule-project#859/amule-project#863/amule-project#861/amule-project#880/amule-project#911/amule-project#901/amule-project#902/amule-project#889/amule-project#868/amule-project#853.
- Bug Fixes & Stability: amule-project#850/amule-project#854/amule-project#878/amule-project#906.
- CI: ccache wiring (amule-project#892, amule-project#903) + CodeQL binutils-dev (amule-project#907).

Contributors footer gains mifritscher and nguyenhoangminhhieu2004-gif
(both first-time contributors).

PR index extended through amule-project#912.
mrjimenez pushed a commit that referenced this pull request Jun 8, 2026
Extends existing categories (preferring extensions over new lines):
- Performance/Upload: #898 SlotAllocation default raised.
- Networking & Discovery: wire-parser hardening list extended with
  #879/#882/#890/#886; new amuleweb security hardening bullet
  consolidating ngosang's #869-#874 triage (all landed in #875);
  amulegui list extended with #857; shared-folder watcher extended
  with #858.
- Packaging: Windows installer i18n line extended with #899.
- Internals & Refactoring: new docs-polish + code-quality bullets
  covering #851/#855/#862/#888/#900/#866/#867/#895 and #909/#910/#912.
- Translations: new pre-release final-wave bullet covering #847/#856/
  #891/#908/#860/#904/#859/#863/#861/#880/#911/#901/#902/#889/#868/#853.
- Bug Fixes & Stability: #850/#854/#878/#906.
- CI: ccache wiring (#892, #903) + CodeQL binutils-dev (#907).

Contributors footer gains mifritscher and nguyenhoangminhhieu2004-gif
(both first-time contributors).

PR index extended through #912.
@got3nks
got3nks deleted the feat/installer-i18n branch June 8, 2026 10:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants