Update Locale Files
KIDEx automates the maintenance of the GNU gettext .po translation files that drive Kittox application localization. The Update Locale Files command scans both the YAML metadata and the Delphi source code, extracts every translatable string, and merges the result into the existing .po files of the project — preserving the translations that have already been done.
This means you can iterate freely on Models / Views / Layouts and Delphi code, and re-run this command whenever you want to refresh the .po files. Translators working in parallel never lose their work.
Why this matters
A typical Kittox application has hundreds of strings scattered across:
- YAML metadata —
DisplayLabel,DisplayLabelSingular,Hint, button captions, dialog titles, ToolView labels, validation messages - Delphi source — strings inside
_('...')calls inControllers.pas,Rules.pas,UseKitto.pasand your custom units
Manually keeping the .po files in sync with these sources is impractical. KIDEx does it in seconds with two extractors chained into a merge step:
┌──────────────────────────────────┐
│ YAML metadata (Models, Views, │
│ Layouts, Configs) │
└────────────────┬─────────────────┘
│ extract translatable nodes
▼
┌──────────────────────────────────┐
│ Delphi sources (.pas in Source/) │
└────────────────┬─────────────────┘
│ dxgettext extract
▼
┌─────────────────────┐
│ fresh .pot template │
└──────────┬──────────┘
│ merge with existing
▼
┌──────────────────────────────────┐
│ default.po + per-language .po │
│ (preserved translations + new │
│ msgids added as fuzzy/empty) │
└──────────────────────────────────┘Prerequisites
- dxgettext must be installed and reachable. KIDEx bundles a copy by default, but if your installation has been customized, Tools > Options > Localization lets you point at a different
dxgettext.exe. - The project must have a
Resources/locale/folder with at least thedefault.potemplate. The New Project Wizard creates it automatically; for projects that don't have it yet, KIDEx creates it on first run.
Launching the command
- Menu: Tools > Update Locale Files
- The command is only enabled when a project is open.
A dialog appears showing the per-language status and the Update button.
What happens, step by step
1. In-memory metadata scan
KIDEx walks the loaded Kittox metadata catalog and visits every node:
- Models —
DisplayLabel,DisplayLabelSingular,Hint,AllowedValueslabels,Rulesmessages,DefaultValuemacros containing literal strings - Views —
DisplayLabel,Title,Hint, button labels,ToolViewslabels,Layoutstitles, FieldSet titles, PageBreak labels - Layouts — fieldset titles, label overrides
- Configs — labels embedded in Config.yaml (e.g. login defaults, application title)
The set of "translatable" nodes is determined by the localizable flag on the YAML attribute annotations on the framework metadata classes — KIDEx doesn't have a hard-coded list, it discovers them via RTTI. This means custom controllers that mark new properties as localizable are picked up automatically.
The progress bar in the dialog shows the file currently being processed.
2. Delphi source extraction
Once the metadata pass is done, KIDEx runs dxgettext.exe against the project's Source/ directory. This extracts strings inside:
_('...')calls — the standard gettext conventiondgettext('domain', '...')calls — for explicit domain- Resource strings flagged with the relevant pragmas
Output: a fresh .pot template file with all the extracted msgids, regardless of whether they were translated before.
3. Merge with existing .po files
For every .po file present under Resources/locale/ (e.g. default.po, it.po, fr.po, de.po), KIDEx calls msgmerge (bundled with dxgettext) to combine:
- the new
.pottemplate (the "should be" set of strings) - the existing
.pofile (the "what's already translated" set)
The merge:
- Keeps every msgid still present in the new
.potalong with its existing translation (if any) - Adds new msgids found in the
.potbut not in the.po— empty translation, markedfuzzyif a similar string was found in the old file - Removes msgids that no longer appear in the
.pot— the translations are not lost, they're moved to the bottom of the file as commented-out entries (so they can be recovered if a string comes back later)
This is a non-destructive operation. Re-running the command twice in a row leaves the file unchanged.
4. Status summary
When the run finishes, the dialog shows a per-language summary:
| Column | Meaning |
|---|---|
| Language | The .po file processed |
| Total | Total msgids in the merged file |
| Translated | msgids with a non-empty translation |
| Untranslated | msgids with empty translation (need work) |
| Fuzzy | msgids where the merger found a similar but not identical match — review needed |
| Obsolete | msgids no longer in the metadata / source, archived at the bottom of the file |
Multiple languages
Add a new language by dropping a copy of default.po into Resources/locale/ named after the locale code (e.g. de.po for German, pt_BR.po for Brazilian Portuguese), then running Update Locale Files. KIDEx picks up the new file on its next run and includes it in the merge cycle.
The active language at runtime is selected by:
- The
LanguageIdsetting inConfig.yaml(default for all sessions) - The
kx_langcookie (per-user, set via the language switcher in the menu) - The
Accept-LanguageHTTP header (auto-detected on first visit)
See Localization for the runtime side.
Translation workflow
The recommended workflow for projects with translators:
- Developer (you): runs Update Locale Files after meaningful metadata or source changes. Commits the updated
.pofiles to source control. - Translator: opens
it.po(or whichever language) in Poedit or any other gettext editor. Translates theUntranslatedandFuzzyentries. Saves and commits. - Developer: pulls the translator's commits. Re-running Update Locale Files is idempotent — the translator's work is preserved.
Tips
- Run after every metadata change — even small ones. The longer you wait, the more
Fuzzymatches appear, which translators have to disambiguate by hand. - Keep
default.poclean — don't translate strings intodefault.po. The convention is thatdefault.pois the template with empty translations, and all real translations go into per-language files (en.po,it.po, …). - Watch the
Untranslatedcount — it's a quick health metric for the project's localization coverage. - Use
Fuzzyflags as a review queue — when the source string changes slightly, the merger marks the existing translation as fuzzy. A translator should re-validate it. Obsoleteentries are insurance — don't manually delete them. If you rename a property and rename it back next week, the obsolete entry is restored automatically.
What's NOT updated by this command
- The Delphi resource strings in compiled DCUs — those need a Delphi rebuild after the
.pofiles have been compiled into.mo(or used directly via the runtime gettext layer). - Static HTML / JS in
Resources/— if you have hand-written HTML / JS that contains user-facing strings, they need their own pipeline (typically a separateapp.po). - Database content —
AllowedValueslookup tables stored in a DB are translated at the DB level, not via.po. See Localization — Lookup translations.
See also
- Localization — runtime localization, language switching,
Accept-Languagehandling - Themes — language-aware theming (RTL languages, font preferences)
- How To Localization — patterns for custom strings in your own
Controllers.pasandRules.pas - Config Editor —
LanguageIdandCharsetsettings
