Skip to content

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:

  1. Framework interface — buttons (Save, Cancel, Edit, Confirm, Close, etc.), error messages, dialog text
  2. 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:

po
#: 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:

CategoryExamples
Form buttonsSave, Confirm, Save All, Cancel, Close, Edit, Delete, Add, Save & Clone
Form titlesAdd %s, Edit %s, View %s, Duplicate %s
List/GridFilters, Apply, Refresh, Search, Select, No records found, Showing %d-%d of %d
Confirmation dialogsSelected %s will be deleted. Are you sure?, Yes, No
LoginUser Name, Password, Language, Login, Logging in..., Invalid login., Logout
PasswordChange Password, Old Password, New Password, Reset Password
Error handlingError, Server is not responding, Retry, Reset, Server error, Resource not found
SessionSession lost or expired, please restart!, Data saved, Data deleted
File operationsUpload, 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:

javascript
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

yaml
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

yaml
Fields:
  Dress_Size: String(4)
    AllowedValues:
      XS: _(Extra Small)
      S: _(Small)
      M: _(Medium)
      L: _(Large)
      XL: _(Extra Large)

View titles and button labels

yaml
Views:
  MyView:
    DisplayLabel: _(Customer List)
    Controller: List

What 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:

NodeWhat it really is
PluralModelNamedespite 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 keysthe values stored in the database
comparison literals inside Expressionin 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 parameterstable 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:

pascal
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:

  1. In KIDEx, expand the project's Locales node, right-click a language and choose Update….
  2. KIDEx scans the metadata (Models / Views / Layouts / Config) and runs dxgettext over your Source\ folder, then merges everything into that language's default.powithout touching translations already done (new strings appear empty).
  3. 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-Language header and selects the best-matching shipped language. Leave LanguageId empty to honor the browser.
  • Fixed default — set LanguageId to a code (e.g. en, it) to force that language regardless of the browser.
  • User selection — with LanguagePerSession: True a LanguageSwitcher (flag dropdown) lets the user change language at any time; the choice lasts for the session.
yaml
# Config.yaml
LanguageId:            # empty → follow the browser (Accept-Language); set a code to force it
LanguagePerSession: True

The 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:

  1. Create {App}/Home/Locale/<lang>/LC_MESSAGES/ (e.g. de, es, pt) and copy an existing default.po into it — or start from an empty file.
  2. In KIDEx, run Update Locale on it to fill it with all current msgids.
  3. 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

Released under Apache License, Version 2.0.