Localization (i18n)

Finch has a built-in i18n system supporting multiple languages. Language strings can be stored in JSON files or defined directly in Dart.

Configuration

Set the language options in FinchConfigs:

FinchConfigs configs = FinchConfigs(
  // Path to directory containing language JSON files
  languagePath: pathTo(env['LANGUAGE_PATH'] ?? './lib/languages'),

  // Use JSON files (LanguageSource.json) or Dart maps (LanguageSource.dart)
  languageSource: LanguageSource.json,

  // If using LanguageSource.dart, supply the map here:
  // dartLanguages: languageDart,
);

Option 1: JSON Files

Create one JSON file per language in your languagePath directory. The file name is the language code:

lib/languages/en.json

{
  "dir": "ltr",
  "logo.title": "My App",
  "greeting": "Hello, World!",
  "example.params": "My name is {name}, my age is {age}",
  "example.params.arr": "My name is {0}, my age is {1}"
}

lib/languages/fa.json

{
  "dir": "rtl",
  "logo.title": "برنامه من",
  "greeting": "سلام دنیا!",
  "example.params": "نام من {name} است، سن من {age} سال است"
}

The dir key (ltr / rtl) is used by {{ $e.dir }} in templates.

Option 2: Dart Map

Define languages as a Dart Map<String, Map<String, String>> and point FinchConfigs to it:

// lib/languages/language_dart.g.dart (or any name)
const languageDart = <String, Map<String, String>>{
  'en': {
    'dir': 'ltr',
    'logo.title': 'My App',
    'greeting': 'Hello, World!',
    'example.params': 'My name is {name}, my age is {age}',
  },
  'fa': {
    'dir': 'rtl',
    'logo.title': 'برنامه من',
    'greeting': 'سلام دنیا!',
    'example.params': 'نام من {name} است، سن من {age} سال است',
  },
};
FinchConfigs configs = FinchConfigs(
  languageSource: LanguageSource.dart,
  dartLanguages: languageDart,
);

The finch serve watcher can convert JSON files to a Dart map automatically. The generated file is written to language_dart.g.dart.

Translating in Controllers

Use the .tr extension on any key string. .write() renders the final translated string:

// Simple translation
String text = 'logo.title'.tr.write();

// With named parameters
String text = 'example.params'.tr.write({'name': 'Alice', 'age': 30});

// With positional parameters (array)
String text = 'example.params.arr'.tr.writeArr(['Alice', 30]);

Pass translated strings to templates:

rq.addParams({
  'greeting': 'greeting'.tr.write(),
  'userLine': 'example.params'.tr.write({'name': 'Alice', 'age': 30}),
});
return rq.renderView(path: 'pages/home');

Translating in Templates

Use {{ $t('key') }} for inline translation:

<h1>{{ $t('logo.title') }}</h1>
<p>{{ $t('example.params', {'name': user.name, 'age': user.age}) }}</p>
<p>{{ $t('example.params.arr', ['Alice', 30]) }}</p>

Language Switching

Language is determined in this order:

  1. First path segment if it matches a known language code (e.g., /fa/home)
  2. lang data field on API endpoints
  3. language cookie
  4. language session key
  5. Default from settings

Change language from a controller:

rq.changeLanguege('fa');

In a template, generate a URL that switches language:

<a href="{{ $e.urlToLanguage('fa') }}">فارسی</a>
<a href="{{ $e.urlToLanguage('en') }}">English</a>

Available Languages in Templates

{% for lang in $e.langs %}
  <a href="{{ $e.urlToLanguage(lang.code) }}">{{ lang.label }}</a>
{% endfor %}

Each entry: { code: 'en', label: 'English', country: 'United States' }

TString — Translation Objects

TString wraps a key and lets you defer translation:

var ts = TString('example.params');
rq.addParam('exampleTString', ts.write());

// Or using the .tr shorthand:
rq.addParam('examplePathString', 'example.path'.tr.write());

## array parameters in i18n

You can use array parameters in your i18n text. For example, if you want to translate the `example.params` key, you can do it like this:

```dart
rq.renderString(text: 'example.params'.tr.writeArr(['Alexandre', 30]));

and in your template you can use it like this:

{
    "example.params": "نام من {0} است، سن من {1} سال است",
}
<p>{{ $t('example.params', ['Alexandre', 30]) }}</p>