What this is about
A document has a language and a time zone. The language decides two things: which language variant of the template CRMS takes, and how numbers and dates look. The time zone decides which clock time a point in time shows in the document.
You may name both in the request. If you leave them out, CRMS uses fixed fallbacks.
Where the language comes from
-
1Client→CRMSnames
localein the request, for examplede-CHIf it is set, it applies. -
2CIAShas chosen a language for the current requestIn this order: the claim
localein the token, then the headerAccept-Language, thencodamai.i18n.default-locale. CRMS reads no token and no header itself. -
3CRMSoutside a request:
codamai.i18n.default-localeResult: the document's language
With this language, CRMS looks for the matching language variant of the template: first exactly de-CH, then de, then the template’s default variant. How this works in detail is described in How CRMS finds the template. Which variant was used is in the response header Crms-Template-Variant.
CRMS always formats numbers and dates in the requested language, even if a variant of another language was used. In the PDF, CRMS sets the language as lang on the <html> element if the template sets none. Accessible PDFs need that.
Where the time zone comes from
-
1Client→CRMSnames
timeZonein the request, for exampleEurope/BerlinIf it is set, it applies. -
2CRMSreads the container's environment variable
TZThe same variable applies to all CodamAI modules. The POSIX form:Europe/Berlinis allowed too. IfTZis not a known time zone, the application does not start. -
3CRMSwithout
TZ: UTCResult: the document's time zone
There is no time zone setting of CRMS’s own. For FINAL, CRMS stores language and time zone with the document.
How values look in the document
The template formats with FreeMarker. Language and time zone are already set. With the payload {"amount": 1234.5, "n": 1234567, "flag": true, "due": "2026-10-17", "at": "2026-10-17T08:30:00Z"}, due declared as DATE and at as DATETIME, and the time zone Europe/Berlin, you get:
| In the template | locale: de | locale: en |
|---|---|---|
${amount} | 1.234,5 | 1,234.5 |
${amount?string('0.00')} | 1234,50 | 1234.50 |
${amount?c} | 1234.5 | 1234.5 |
${n} | 1.234.567 | 1,234,567 |
${flag} | true | true |
${due} | 17.10.2026 | Oct 17, 2026 |
${due?string.long} | 17. Oktober 2026 | October 17, 2026 |
${due?string('dd.MM.yyyy')} | 17.10.2026 | 17.10.2026 |
${at} | 17.10.2026, 10:30:00 | Oct 17, 2026, 10:30:00 AM |
${at?string('dd.MM.yyyy HH:mm z')} | 17.10.2026 10:30 MESZ | 17.10.2026 10:30 CEST |
?cprints a number without formatting, for machines rather than people. You need it in table cells, for example, see Output types and formats.- Booleans always come as
trueorfalse. If you want “yes” and “no”, write${flag?string('yes', 'no')}. - The point in time
08:30Zappears as10:30, because the document is rendered inEurope/Berlin. - A calendar date (
DATE) counts as midnight in the document’s time zone. That is why it does not shift to the day before.