Foundation 6

8. Localisation

Nothing the user sees in Foundation is hard-coded. Every menu, button, label and message is looked up at run time in a pair of 4D lists, so the same component runs in English or French and can run in any language a host adds. Your own strings use the same mechanism.

How a string is found

$label_t:=Fnd_Loc_GetString("Fnd_Gen"; "Stop")
$title_t:=Fnd_Loc_GetString("Loc_General"; "DatabaseName")
$text_t:=Fnd_Loc_GetString("Loc_Invoices"; "Overdue"; String($days_i))

The first argument names a module: a 4D list of that name holds the lookup codes (Stop, Cancel, DontSave), and a list named <module>_<LANG> holds the translations at the same positions. Fnd_Loc_GetString finds the code in the key list, reads the item at the same position in the current language's list, and substitutes <<1>>, <<2>> and so on with the optional arguments.

Foundation's own lists are in the component (Fnd_Gen, Fnd_Gen_EN, Fnd_Gen_FR, eighteen such triples); a host's are in the host's lists.json and are read through Fnd_Host_ListToArray. The lookup code is what you write in code, so it never changes; the translation is what the user sees. Fnd_Gen_GetString is the same call with an English fallback for a host without the Localisation module's lists.

The language

Fnd_Loc_LanguageCode returns the two-letter code in force: EN, FR or DE, decided at startup from 4D's own localisation, and settable by the host (Fnd_Loc_LanguageCode("FR")) before the first string is read, in Fnd_Hook_Shell_Setup. German's code is DE; Foundation 5 used GR, and a host with _GR lists renames them.

Your own strings

Foundation Product Sales keeps three lists: Loc_General with the codes, Loc_General_EN and Loc_General_FR with the texts. Its Fnd_Hook_Shell_Setup reads the four database facts from them, so the About box is French when 4D is. To add a module of your own:

  1. In the 4D list editor, create Loc_Invoices with one item per lookup code, and Loc_Invoices_EN and Loc_Invoices_FR with the texts in the same order. An item beginning with a backtick is a comment and is skipped, which is how FPS groups its codes.
  2. Read with Fnd_Loc_GetString("Loc_Invoices"; "Overdue").
  3. Keep the three lists the same length. A triple whose lists differ in length is reported, not guessed at.

Fnd_Loc_Editor opens Foundation's Localisation List Editor, which shows every module, its codes and the translation per language side by side, and saves back to the lists. Fnd_Hook_List_SetEditableLists names the lists your users may edit at run time, through the List Editor; FPS chooses the French or English set by Fnd_Loc_LanguageCode.

Adding a language

Add a <module>_<LANG> list for every module you use, Foundation's included (Fnd_Gen_DE, Fnd_Shell_DE, …), with the translations in the key list's order, and set Fnd_Loc_LanguageCode("DE") at startup. A module with no list for the current language falls back to its _EN list, so a partial translation shows English where it is incomplete rather than failing. A key list that is missing, or a translation list of the wrong length, raises a bug alert naming the module; a code absent from the key list returns an empty string. Foundation 6 ships English and French complete; contributions of other languages are welcome in the repository.

Widths

Translated text is rarely the same length. Fnd_Loc_FixButtonWidths(->btn1; ->btn2; …) makes a row of buttons as wide as the widest title needs, and Fnd_Loc_FixLabelWidths(->label; ->field; …) moves each field to its label's width. Call them in On Load, after the titles are set, as FPS's Invoices form does.