Localization
Kittox default language is English. Applications can be localized to any language using the GNU gettext system via dxgettext for Delphi.
Localization covers two layers:
- Framework interface — buttons (Save, Cancel, Edit, Confirm, Close, etc.), error messages, dialog text
- Application metadata — field labels, allowed values, view titles, hints defined in YAML
File Structure
Home/
Locale/
it/
LC_MESSAGES/
Kitto.po # Translatable strings (source, editable)
Kitto.mo # Compiled binary (used at runtime)
Kitto.ini # dxgettext configuration
de/
LC_MESSAGES/
Kitto.po
Kitto.mo
fr/
...The framework locale folder is in KittoX/Home/Locale/. The application locale folder is in {App}/Home/Locale/. At runtime, KittoX searches the application folder first, then the framework folder (same fallback pattern as resources).
.po file (source)
The .po file contains translatable strings in standard GNU gettext format:
#: Kitto.Html.Form.pas
msgid "Save All"
msgstr "Salva tutto"
#: Kitto.Web.Application.pas
msgid "Session lost or expired, please restart!"
msgstr "Sessione persa o scaduta, riavviare!"Edit .po files with Poedit (free), which compiles the .mo automatically on save.
.mo file (compiled)
The .mo file is the binary format read by dxgettext at runtime. It is generated from the .po file by Poedit or the msgfmt command-line tool. Always regenerate the .mo after editing the .po.
Framework Strings
The framework .po file (KittoX/Home/Locale/it/LC_MESSAGES/Kitto.po) includes translations for:
| Category | Examples |
|---|---|
| Form buttons | Save, Confirm, Save All, Cancel, Close, Edit, Delete, Add, Save & Clone |
| Form titles | Add %s, Edit %s, View %s, Duplicate %s |
| List/Grid | Filters, Apply, Refresh, Search, Select, No records found, Showing %d-%d of %d |
| Confirmation dialogs | Selected %s will be deleted. Are you sure?, Yes, No |
| Login | User Name, Password, Language, Login, Logging in..., Invalid login., Logout |
| Password | Change Password, Old Password, New Password, Reset Password |
| Error handling | Error, Server is not responding, Retry, Reset, Server error, Resource not found |
| Session | Session lost or expired, please restart!, Data saved, Data deleted |
| File operations | Upload, Download, Clear, Preview |
Italian translation is included out of the box. To add a new language, copy the it/ folder, rename it (e.g. de/, fr/, es/), and translate the .po file.
Client-Side Strings
Some strings are used in JavaScript (error dialogs, toast notifications). These are injected from the server into the _Page.html template via the window.KX_STRINGS object:
window.KX_STRINGS = {
appTitle: '...',
errorTitle: '...', // _('Error')
serverNotResponding: '...', // _('Server is not responding')
retry: '...', // _('Retry')
reset: '...', // _('Reset')
dataSaved: '...', // _('Data saved')
dataDeleted: '...', // _('Data deleted')
serverError: '...', // _('Server error')
serverNotFound: '...', // _('Resource not found')
serverInternalError: '...' // _('Internal server error')
};These values come from the Delphi _() function, so they are automatically translated when a .po/.mo file is available for the user's language.
Application Metadata Localization
In YAML metadata files, wrap translatable strings with _():
Field labels and hints
Fields:
Doll_Name: String(40) not null
DisplayLabel: _(Name)
Date_Bought: Date
DisplayLabel: _(Birth Date)
Picture: Blob
DisplayLabel: _(Photo)
Hint: _(Select a picture)Allowed values
Fields:
Dress_Size: String(4)
AllowedValues:
XS: _(Extra Small)
S: _(Small)
M: _(Medium)
L: _(Large)
XL: _(Extra Large)View titles and button labels
Views:
MyView:
DisplayLabel: _(Customer List)
Controller: ListWhat must not be wrapped
_() makes a value change with the session language. That is what you want for a caption and never what you want for anything the application matches, stores or emits. In AllowedValues above, note that only the descriptions are wrapped: the keys XS, S, M… are the codes in the database column, and the same holds for the Items of a list filter.
Leave these untouched:
| Node | What it really is |
|---|---|
PluralModelName | despite the name, not a label: it is the XML element name that wraps a record set in TKRecords.GetAsXML, via TKViewTableRecords.GetXMLTagName, and is read by the XML export and FOP report tools. Localizing it would make the exported document's structure depend on the user's language. The plural caption is a different node — PluralDisplayLabel |
AllowedValues keys, filter Items keys | the values stored in the database |
comparison literals inside Expression | in CASE WHEN {Q}STATUS = 'ACT' THEN '_(Active)' … only the displayed text may be wrapped, never 'ACT' |
PhysicalName, ModelName, Model, View, Layout, Field, ImageName, ClientFileName, TemplateFileName, FileName, DefaultValue, rule parameters | table and column names, file names, object references |
A value that is not wrapped is safe by construction: TEFTree.GetString and TEFNode.GetAsString translate only when the value begins with _(, so an unmarked value is returned verbatim whatever the catalogs contain.
Where the rule lives in the source
TKModel.PluralModelName carries a /// <summary> saying what the node is and that it must not be localized, so the warning is where you meet it while reading the code. Note that DefaultPluralDisplayLabel is Pluralize(DisplayLabel) — it never reads PluralModelName, the two are unrelated.
Delphi Code
In custom controllers and rules, use _() for user-facing strings:
uses
EF.Localization;
procedure TMyRule.BeforeAdd(const ARecord: TKRecord);
begin
if SomethingWrong then
RaiseError(_('Cannot invite the same girl twice.'));
end;Generating the .po files with KIDEX
You don't write the .po entries by hand. Once the strings are marked — the standard label nodes (DisplayLabel, PluralDisplayLabel, Hint, …) and anything wrapped in _(...) in YAML, plus _('...') in Delphi code — KIDEx extracts them for you and keeps each language's default.po in sync:
- In KIDEx, expand the project's Locales node, right-click a language and choose Update….
- KIDEx scans the metadata (Models / Views / Layouts / Config) and runs dxgettext over your
Source\folder, then merges everything into that language'sdefault.po— without touching translations already done (new strings appear empty). - Translate the empty entries in Poedit and save to compile the
.mo.
See Update Locale for the full workflow, the exact set of auto-recognized YAML keys, and the prerequisites.
TIP
KIDEx maintains the application catalog (default.po). The framework catalog (Kitto.po) is shipped already translated by Ethea — you only translate it when adding a brand-new framework language.
Choosing the language
The interface language is resolved per session:
- Browser auto-detection — on the first visit KittoX reads the
Accept-Languageheader and selects the best-matching shipped language. LeaveLanguageIdempty to honor the browser. - Fixed default — set
LanguageIdto a code (e.g.en,it) to force that language regardless of the browser. - User selection — with
LanguagePerSession: Truea LanguageSwitcher (flag dropdown) lets the user change language at any time; the choice lasts for the session.
# Config.yaml
LanguageId: # empty → follow the browser (Accept-Language); set a code to force it
LanguagePerSession: TrueThe LanguageSwitcher is a controller (like the theme switcher): it lists every language for which the application ships a compiled catalog — {App}/Home/Locale/<code>/LC_MESSAGES/*.mo — plus English, each with its country flag. In the shipped examples it appears in the login form and at the bottom of the home left menu. Drop it into any layout region with Controller: LanguageSwitcher.
TIP
The framework's own KittoX/Home/Locale/ is deliberately not scanned: its catalogs translate framework strings, but a language becomes selectable only when the application has a catalog of its own. Otherwise the user could pick a language that leaves every application label untranslated.
Adding a New Language
For the application strings:
- Create
{App}/Home/Locale/<lang>/LC_MESSAGES/(e.g.de,es,pt) and copy an existingdefault.pointo it — or start from an empty file. - In KIDEx, run Update Locale on it to fill it with all current msgids.
- Translate the entries in Poedit and save to compile
default.mo.
For the framework strings, copy KittoX/Home/Locale/it/LC_MESSAGES/Kitto.po into the new <lang> folder and translate it the same way (the framework catalog is not produced by KIDEx). KittoX already ships de, es, it and pt.
The new language appears in the LanguageSwitcher as soon as the application's default.mo exists in {App}/Home/Locale/<lang>/LC_MESSAGES/. Creating only the folder, or leaving only the .po source, is not enough — and translating only the framework catalog is not enough either: without the application catalog the language stays out of the menu, on purpose.
See also
- How To: Localize an Application — step-by-step guide
- Update Locale (KIDEx) — generate/refresh the
.pofiles from YAML + Delphi sources - LanguageSwitcher — the flag drop-down controller for switching language
- Config Reference —
LanguageIdandLanguagePerSessionsettings - Login — language selector on the login page
