Internationalisation (gettext)¶
XCSoar uses GNU gettext for
UI strings. Catalogues live in po/ (33 languages plus the
xcsoar.pot template). Macros are defined in
src/Language/Language.hpp.
Marking strings in source¶
_("Text")— translate immediately at the call site.N_("Text")— mark for extraction only (static arrays / initialisers); translate later withgettext().AddChoices()does this forStaticEnumChoicelabels.C_("Context", "Text")/NC_("Context", "Text")— same as_()/N_(), with a gettextmsgctxtso identical English strings can be disambiguated by role.
Do not mark log messages for translation
(LogFormat(), LogDebug(), LogInfo(), LogError(),
and similar). Keep logs in English for consistency and debugging.
Do not mark ICAO abbreviations and codes for translation. They stay
as published in Doc 8400 (IAS, TAS, CAS, EAS, QNH,
QFE, QNE, IFR, VFR, VMC, IMC, and the rest of
that list). Write the abbreviation as a string literal. A sentence
around it is ordinary language and still uses _() / N_().
Inside that sentence, leave the abbreviation unchanged. Do not add a
msgid that is only the abbreviation. Leave abbreviations that are
already in the catalogues alone.
data.FmtComment("{} {}", speed, "TAS");
AddBoolean("IAS",
_("Show the speed-to-fly as indicated airspeed."),
on, listener);
Do not translate brand names. Write them as published: WeGlide,
SoaringScoring, SkySight, XC Therm, NetCDF. A label that is only the
name is a string literal. A sentence around it still uses _() /
N_(), and the name stays unchanged inside that sentence.
Reuse existing English strings whenever the same wording fits.
Search po/xcsoar.pot and existing _() / N_() call sites
before adding a new string. Slightly different spellings or punctuation
create extra catalogue entries and more work for every language. Prefer
matching an existing msgid (or an existing msgctxt + msgid
pair) over inventing a near-duplicate. Use msgctxt only when the
same English text genuinely needs different translations by role.
When to use msgctxt¶
Use context for short, ambiguous UI labels that share English wording but mean different things (button vs setting vs status). Prefer existing context names:
Button,Menu,Setting,StatusWeather control,Weather layer,Abbreviation,InfoBox
Examples:
C_("Button", "Setup")
NC_("Setting", "Auto")
C_("Status", "Live")
C_("Weather control", "Time")
Avoid inventing new context names when an existing one fits.
Updating catalogues¶
After adding or changing marked strings:
make update-po TARGET=UNIX
This regenerates po/xcsoar.pot and merges into every
po/*.po.
What make update-po does¶
Implemented in build/gettext.mk:
xgettext/msgcatwith--no-wrapand--no-location(keywords_,N_,C_,NC_) →po/xcsoar.potFor each language file:
msgmerge --no-wrap --no-location --previousmsgattrib --clear-fuzzy --empty --no-obsolete --clear-previoustools/update_po_low_churn.pypatches only changed entries
#: file:line locations are omitted on purpose. They churn on every
source edit and are not required at runtime.
Low-churn policy¶
Routine catalogue updates must not rewrap or rewrite untouched entries.
tools/update_po_low_churn.py:
Rewrites only entries whose translation or key changed
Keeps original
msgid/msgstrquote wrapping where possibleStrips leftover
#:linesUpgrades 1:1 bare →
msgctxtadditions in place when possibleFalls back safely if entry block spans overlap
Never bulk-save a whole .po with polib (or any tool that
rewraps the file). That destroys low-churn history and produces huge
diffs. Edit msgstr values with targeted replacements, or go through
make update-po / update_po_low_churn.py.
Filling translations¶
Run
make update-po.Fill only the new empty
msgstrentries (all 33 languages when possible). Do not rewrite, rewrap, or reorder existing translations.Validate:
msgfmt --check -o /dev/null po/<lang>.po
Spot-check placeholders (
%s,%u, …) match themsgid.Avoid
msgstrtext that looks like printf tokens unless themsgidhas the same placeholders (e.g. Hungarian50%-nálcan be misread as%-n; prefer a spelled-out “percent” form).
Keep brand names (WeGlide, SoaringScoring, SkySight,
XC Therm, NetCDF, …) and ICAO abbreviations (IAS, TAS,
QNH, …) unchanged in every language.
Commit organisation¶
Small fixes: one
po:commit (orpo/<lang>:for a single file), usually at the end of a feature branch.Large refreshes:
po: Regenerate translation catalog(xcsoar.pot, and any source fix required for extraction)One
po/<lang>: Refresh translationscommit per language
Each language file should be touched once on a refresh branch when practical. Fold follow-up msgstr fixes into that language’s commit.
.po syntax notes¶
Never put custom comments between
#, fuzzyandmsgid, or betweenmsgidandmsgstr.Place comments before
#, fuzzyor at the start of an entry block.Invalid syntax breaks
msgmergeduringmake update-po.