Translations
No user-visible string is ever literal. Everything is a key, resolved at render time by BSPK_Translate.
BSPK_Translate("BSPK_labelYes") // "Yes"
BSPK_Translate("Gene_orderNumber"; 125) // "Order 125" -> [[:1]]
BSPK_Translate("Gene_animal"; $animals.length) // singular/plural
Keys live in Resources/Misc/lang/<code>/BSPK.json for the component, and in the host's own language files for the application. Files are merged in alphabetical order.
Macros inside a translated string
| Macro | Becomes |
|---|---|
[[BR]] | <br/>: this is how you get a line break on the web |
[[CR]] | a carriage return |
[[{]] [[}]] | < and > |
[[ ]] | |
[[:1]] … [[:9]] | the replacement parameters |
Trap: macros do not work in dev panel tooltips. A tooltip ends up in a
data-tipattribute rendered through CSScontent: attr(data-tip); no HTML is interpreted there, andwhite-spaceis normal. Write tooltips as single-paragraph text; around 250 characters fit without truncation.
Plurals
A target containing | is a plural form, selected by a numeric second parameter:
animal|animaux -> 2 forms: singular | plural
[[:2]] | [[:3]] propriétaire | [[:1]] propriétaires -> 3 forms: zero | one | many
Passing a number to a key that has no | is not an error: the number is simply substituted as text.
The reverse is not safe: calling a key with a plural parameter when its target has no plural form can raise an assertion rather than fall back. If a label must count, give the key its forms.
Counting a listbox selection without 4D code
To show "No item selected" / "One item selected" / "3 items selected" on a button, a field label, a translation or a text block, put a plural key in the block's normal Label field. That is the whole recipe: there is no dedicated method.
A ready-made key ships with the component: BSPK_selectedItemsCount.
The number comes from BSPK_vl_PluralCount, a component process variable reset to 0 at the top of every request and filled automatically when the POST carries vc_Pks (what sendListboxSelectRows posts for onSelectRows). BSPK_vc_SelectedPks receives the checked primary keys at the same time.
On page load the count is 0, so the "zero" form shows. Never an undefined variable, which would disable the plural and leave the | visible on screen.
A translation without | is unaffected: it is the only form available whatever the number. No existing label changes behaviour.
A missing key falls back to nothing
If the key is absent from the current language, the key itself is displayed. There is no fallback to another language: an English-only key shows as BSPK_myKey to a French user, not as its English text. This is deliberate (a missing translation must be visible), and it is why a new key has to be added to every language file.
Editing language files
Never rewrite one with a JSON serialiser. JSON Stringify / json.dump reformats the whole file and silently de-duplicates keys, producing a diff of thousands of lines in which the real change is invisible. Insert the line as text, next to its neighbours.
Component → host synchronisation adds missing keys; it never overwrites an existing value. So changing a label in the component does not change it in an application that already has that key: you must edit the host's file too, and existing installations will not pick it up at all.
Translatable block properties
A property flagged supportTranslation in the schema stores one value per language:
"vt_FieldLabel": { "fr": "Nom du client", "en": "Customer name" }
The panel edits them through a single widget that switches between an xliff key and free text per language. Two things to know when writing such a value:
- the POST carries a JSON string of all languages at once, for plain text and rich text alike: validate language by language, never the serialised blob;
- long text and rich text are base64-encoded inside each language value.
See also
- The data model: where these values sit in
properties - Translation: the block dedicated to displaying a key

