Internationalisation · Python · Linux
jalang — a bilingual desktop, built from gettext
An answer to "what if my whole operating system spoke two languages at once?" jalang merges Linux's translation catalogs into brand-new system locales so every menu, dialog and tooltip in GNOME reads as Primary (Bracket) — e.g. Lêer (File). Pure Python over the standard gettext toolchain, and it uninstalls as cleanly as it installs.
Lêer (File) · Wysig (Edit) · Instellings (Settings)
The bilingual desktop
Pick the generated locale in Settings → Region & Language, log back in, and the entire GNOME shell renders every string as Primary (Bracket) at once — read your interface in one tongue while absorbing another by osmosis.
Japanese (Afrikaans), French (English), Greek (Afrikaans)
Read the build scripts ↗
Overview
Most software speaks one language at a time. jalang asks a stranger question: can the desktop speak two — at once, in every string — so you read your interface in one tongue while learning another by osmosis? The answer is a build tool, build_langpack.py PRIMARY BRACKET OUTCODE, that fuses two of the system's existing gettext catalogs into one and registers it as a fresh, selectable system locale. Pick it in Settings, log back in, and the entire GNOME shell is suddenly bilingual.
What's in it
🧩 A catalog merger
Each compiled .mo is unpacked, parsed, and recombined string-by-string into primary (bracket) — preserving the leading/trailing newlines msgfmt's strict check insists on.
🗂 Two trees, unioned
Ubuntu hides translations in two places — /usr/share/locale and the langpack overlay. jalang unions both, or the whole shell (GTK, nautilus, gnome-shell) is left untranslated.
🏷 Brand-new locales
Each pack ships as its own locale code chosen for a tidy display name — "Japanese (South Africa)", "French (United Kingdom)" — generated with localedef and registered in SUPPORTED.
↩ Clean reversal
An uninstall.sh tears out the merged trees, generated locales and registry lines and resets the display language — the experiment leaves no residue.
How it works
- The merge:
msgunfmteach.mo→ a small PO parser →combine()joins the strings asprimary (bracket), thenmsgfmtcompiles the result back. When primary and bracket are identical (untranslated upstream) the bracket is dropped, so you never seeFile (File). - The bracket source: the bracket can be any installed language or the literal
SOURCE— meaning "use the original English msgid" — so Afrikaans-primary packs still bracket nearly everything by falling back to English where no Afrikaans string exists. - Two catalog trees: the critical gotcha — Ubuntu splits translations across
/usr/share/locale(upstream) and/usr/share/locale-langpack(the Ubuntu langpacks that actually hold GTK, GNOME Shell and nautilus). jalang overlays both. - Making it selectable: to surface in Settings → Region & Language a locale must be generated with
localedef -i <src> -f UTF-8 <code>.UTF-8and listed in/usr/share/i18n/SUPPORTEDand/etc/locale.gen— verified against GNOME's own locale enumeration. - Additive & safe: merged catalogs live under their new locale code, leaving the original ja/fr/el/af catalogs untouched, and the whole thing reverses with one script.
primary × bracket combination can be regenerated on demand.