Skip to main content

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
}
tip

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 string
  • Map<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'

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:

ModifierEffect
@:keyNo transformation
@.upper:keyUPPERCASE
@.lower:keylowercase
@.capitalize:keyCapitalize 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:

CategoryTypical usage
ZEROLanguages with a distinct zero form (e.g. Arabic)
ONESingular (e.g. "1 item")
TWODual form languages (e.g. Arabic)
FEWSmall numbers in some languages (e.g. Russian 2–4)
MANYLarge numbers in some languages
OTHERThe 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.