Locales and fallback
Translate messages per locale, and know which text a user gets when a translation is missing.
The messages catalog has one map of templates per locale. A request names the user's locale.
The engine looks for the template from the most to the least specific tag: fr-CA, then fr, then
the ruleset's defaultLocale (en unless you set it), then the key itself.
Tags are compared exactly, including case. Only the default locale must have every key.
Syntax
defaultLocale: en
messages:
en: { order.tooMany: "You can order at most {max} items." }
fr: { order.tooMany: "Vous pouvez commander au plus {max} articles." }
fr-CA: { order.tooMany: "Vous pouvez commander au maximum {max} articles." }{ "entity": "Order", "operation": "create", "locale": "fr-CH", "data": { "quantity": 11 } }Example
There is no fr-CH catalog, so the engine drops the last subtag and uses fr.
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.locales, version: 1.0.0, title: Locales }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
defaultLocale: en
rules:
- id: order.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, 10] }
severity: error
finding: { code: LRN-LOC-001, message: order.tooMany, args: { max: 10 } }
messages:
en:
order.tooMany: "You can order at most {max} items."
fr:
order.tooMany: "Vous pouvez commander au plus {max} articles."
fr-CA:
order.tooMany: "Vous pouvez commander au maximum {max} articles."
tests:
- name: fr-CH has no catalog, so fr is used
entity: Order
operation: create
given:
data: { quantity: 11 }
locale: fr-CH
expect:
decision: deny
findings:
- { rule: order.quantity.max, message: Vous pouvez commander au plus 10 articles. }
- name: fr-CA has its own catalog
entity: Order
operation: create
given:
data: { quantity: 11 }
locale: fr-CA
expect:
decision: deny
findings:
- { rule: order.quantity.max, message: Vous pouvez commander au maximum 10 articles. }
- name: de falls back to the default locale
entity: Order
operation: create
given:
data: { quantity: 11 }
locale: de
expect:
decision: deny
findings:
- { rule: order.quantity.max, message: You can order at most 10 items. }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 11
},
"locale": "fr-CH"
}Result, from the engine
Decisiondeny1 finding, server channel
LRN-LOC-001errorblockingVous pouvez commander au plus 10 articles./quantity
The other golden tests show fr-CA, which has its own catalog, and de, which falls back to the
default locale.
Common mistakes
- Writing
fr_CA. Locale tags use a hyphen, likefr-CA. An underscore is not a tag. - Different case.
fr-cadoes not find thefr-CAcatalog. It falls back tofr. - A key only in a translation. The default locale must have every key, or the ruleset fails to load.
Exercise
Make French the default language. Remove the fr-CA catalog. A request in Spanish (es) must then
show the French text.
Hint
Set defaultLocale: fr. Keep the en catalog for English users.
Show answer
ruleCascade: 1.0.0
kind: RuleSet
metadata: { id: learn.locales, version: 1.0.0, title: Locales }
scope:
- { level: organization, id: learn }
entities:
Order:
schema: { $ref: "./learn.openapi.yaml#/components/schemas/Order" }
defaultLocale: fr
rules:
- id: order.quantity.max
kind: validation
target: { entity: Order, field: /quantity }
operations: [create]
assert: { op: lte, args: [{ var: data.quantity }, 10] }
severity: error
finding: { code: LRN-LOC-001, message: order.tooMany, args: { max: 10 } }
messages:
en:
order.tooMany: "You can order at most {max} items."
fr:
order.tooMany: "Vous pouvez commander au plus {max} articles."
tests:
- name: es falls back to the new default locale, fr
entity: Order
operation: create
given:
data: { quantity: 11 }
locale: es
expect:
decision: deny
findings:
- { rule: order.quantity.max, message: Vous pouvez commander au plus 10 articles. }
- name: en still has its own catalog
entity: Order
operation: create
given:
data: { quantity: 11 }
locale: en
expect:
decision: deny
findings:
- { rule: order.quantity.max, message: You can order at most 10 items. }{
"entity": "Order",
"operation": "create",
"data": {
"quantity": 11
},
"locale": "es"
}Result, from the engine
Decisiondeny1 finding, server channel
LRN-LOC-001errorblockingVous pouvez commander au plus 10 articles./quantity