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:
- In the 4D list editor, create
Loc_Invoiceswith one item per lookup code, andLoc_Invoices_ENandLoc_Invoices_FRwith 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. - Read with
Fnd_Loc_GetString("Loc_Invoices"; "Overdue"). - 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.