Localization
Reference for the mylekha_template_engine localization system — setup, translation keys, plural forms, link keys, and using the tr filter in templates.
Setup
Call LocalizationService.ensureInitialize() once at app startup, before rendering any templates.
import 'package:mylekha_html_renderer/mylekha_html_renderer.dart';
void main() async {
LocalizationService.ensureInitialize(
const Locale('en'),
translations: Translations({
'welcome': 'Welcome to Mylekha',
'greeting': 'Hello, {}!',
'item_count': {
'ONE': '{} item',
'OTHER': '{} items',
},
}),
fallbackTranslations: Translations({
'welcome': 'Welcome',
}),
);
// Now render templates that use | tr
}
fallbackTranslations is returned when a key is not found in the primary translations.
Translations Object
Translations(Map<String, dynamic> map)
The map values can be:
String— a simple translated stringMap<String, dynamic>— plural forms or nested keys
Flat Keys
The simplest case: a string key maps to a string value.
Translations({
'app_name': 'Mylekha POS',
'save_button': 'Save',
'cancel_button': 'Cancel',
})
In templates:
<title>{{ "app_name" | tr }}</title>
<button>{{ "save_button" | tr }}</button>
Nested Keys
Use dot-notation to access keys inside nested maps:
Translations({
'errors': {
'required': 'This field is required.',
'invalid_email': 'Please enter a valid email.',
},
'nav': {
'home': 'Home',
'orders': 'Orders',
},
})
In templates:
<span class="error">{{ "errors.required" | tr }}</span>
<a href="/">{{ "nav.home" | tr }}</a>
Positional Arguments
Pass arguments to fill {} placeholders:
Translations({
'greeting': 'Hello, {}!',
'item_info': '{} — {} in stock',
})
In templates:
{{ "greeting" | tr: user.name }}
{{ "item_info" | tr: item.name, item.stock }}
From Dart:
LocalizationService.tr('greeting', ['Alice']) // → 'Hello, Alice!'
Plural Forms
Map a key to a Map with CLDR plural category keys. The correct form is selected based on the count argument and the current locale's plural rules.
Translations({
'item_count': {
'ZERO': 'No items',
'ONE': '{} item',
'OTHER': '{} items',
},
'day_count': {
'ONE': 'Today',
'TWO': 'Tomorrow',
'OTHER': 'In {} days',
},
})
In templates (pass the count as the first tr argument):
{{ "item_count" | tr: item_count }}
From Dart:
Localization.instance.plural('item_count', 3, args: ['3'])
// → '3 items'
Link Keys
Reference another translation key inside a value using @:key. The referred key is substituted inline.
Translations({
'first_name': 'John',
'last_name': 'Doe',
'full_name': '@:first_name @:last_name',
})
Modifiers transform the linked value at insertion time:
| Modifier | Effect |
|---|---|
@:key | No transformation |
@.upper:key | UPPERCASE |
@.lower:key | lowercase |
@.capitalize:key | Capitalize first letter |
Translations({
'company': 'mylekha',
'header': '@.upper:company POS', // → 'MYLEKHA POS'
})
Using tr in Templates
Basic translation
{{ "welcome" | tr }}
With positional arguments
{{ "greeting" | tr: user.name }}
{{ "item_info" | tr: item.name, item.stock }}
Chained with other filters
{{ "app_name" | tr | upper }}
Inside a conditional
{% if order.paid %}
{{ "payment.confirmed" | tr }}
{% else %}
{{ "payment.pending" | tr }}
{% endif %}
Multiple Locales
LocalizationService is a singleton — call ensureInitialize again with a new locale to switch:
// Switch to Khmer
LocalizationService.ensureInitialize(
const Locale('km'),
translations: Translations(khmerTranslations),
fallbackTranslations: Translations(englishTranslations),
);
CLDR Plural Categories
The engine uses CLDR plural rules to select the correct plural form:
| Category | Typical usage |
|---|---|
ZERO | Languages with a distinct zero form (e.g. Arabic) |
ONE | Singular (e.g. "1 item") |
TWO | Dual form languages (e.g. Arabic) |
FEW | Small numbers in some languages (e.g. Russian 2–4) |
MANY | Large numbers in some languages |
OTHER | The general/default plural form |
For most English usage, only ONE and OTHER are needed. The plural rule for each locale is resolved automatically from the built-in CLDR rules — no manual configuration required.