I2P+ uses gettext PO files for all user-facing strings. Each module has a
bundle-messages.cfg describing its source paths and a locale/ directory
holding one messages_<lang>.po file per language.
apps/<module>/locale/messages_<lang>.po - Application bundles
(routerconsole, i2ptunnel, susimail, susidns, i2psnark, addressbook, SAM,
desktopgui, ministreaming)core/locale/, router/locale/ - Core and router bundlesinstaller/resources/locale/po/ - Installer bundlebundle-messages.sh --cfg <name>ant poupdate-source. Review the
changed messages_en.po files for tagging errors in the Java source and
revert files whose only changes are line-number shifts.msgstr in the non-English PO files. The English
messages_en.po is the template - its msgstr entries must stay empty and
the system falls back to msgid.Generate the bundles by running the build, or directly:
tools/scripts/bundle-messages.sh --dir <module-dir>
The script prints a translation coverage summary per language; pass
--no-coverage to suppress it. Requires xgettext, msgfmt, and
msgmerge on your PATH.
For large runs, read the untranslated entries with polib and apply them in a
batch:
import polib
po = polib.Pofile("apps/routerconsole/locale/messages_de.po")
untranslated = po.untranslated_entries() # msgid list, in file order
Write the translations to a small trans_de.py file holding a t list, then
loop entry.msgstr = t[i] and po.save(). Bash heredocs truncate silently on
large datasets - write the data as a file instead. Helper scripts live in
tools/scripts/: extract_untranslated.py, batch_translate.py.
tools/scripts/translation_status.sh <locale_folder> - per-language status
for a bundletools/scripts/cleanup_po.sh - tidy PO filesbundle-messages.sh
runmessages_en.po - the template’s msgstr must remain emptylocale/ directory