velix_i18n 1.2.0
velix_i18n: ^1.2.0 copied to clipboard
a flutter i18n framework offering translation and interpolation capabilities.
Introduction #
Velix is Dart/Flutter library implementing some of the core parts required in every Flutter application:
- type meta data
- specification and validation of type constraints
- general purpose mapping framework
- json mapper
- model-based two-way form data-binding
- i18n
- dependency injection container
- command pattern for ui actions
Check out some articles on Medium:
Detailed information can be found in the corresponding Wiki.
I18N #
An i18n is implemented with more or less the same scope and api as popular libraries like i18next. but some additional features i haven't found anywhere else.
The solution is made up of two distinct elements.
A LocaleManager is responsible to keep track of the current locale.
It is constructed via
LocaleManager(this._currentLocale, {List<Locale>? supportedLocales })
and can return the current locale via the locale property.
Being a ChangeNotifier - it can inform listeners about any changes of this property,
I18N is a class that is used to configure the setup. The constructor accepts the parameters:
LocaleManager localeManagerthe locale managerTranslationLoader loadera loader that is used to load localizationsLocale? fallbackLocalea fallback locale that will be used for missing keys in the main locale.List<String>? preloadNamespaceslist of namespaces to preloadList<Formatter>? formatterslist of additional fromatter that can be used in the interpolatin process.int cacheSize = 50cache size fro prcomputed interpolation fucntions in a LRU cache
After initialization a string extension
tr([Map<String,dynamic>? args])
can be used to translate keys that consist of a - ":" separated .- namespace and path and optional args.
Example
"app:main.title".tr()
"app:main.greeting".tr({"user": "Andi"})
Let's look at the different concepts.
TranslationLoader #
The class TranslationLoader is used to load translations.
abstract class TranslationLoader {
/// load translations given a namespace and a list of locales
/// [locales] list of [Locale]s that determine the overall result. Starting with the first locale, teh resulting map is computed, the following locales will act as fallbacks, in case of missing keys.
Future<Map<String, dynamic>> load(List<Locale> locales, String namespace);
}
The result is a - possibly recursive - map contaning the localizations.
The argument is a list of locales, since we wan't to have a fallback mechanism in case of missing localization values in the main locale. This list is computed with the following logic:
- start with the main locale
- if the locale has a country code, continue wioth the language code
- continue with a possible fallback locale
- if the fallback locale has a country code, continue with it's language code
Example: Locale is "de_DE", fallback "en_US"
will result in: ["de_DE", "de", "en_US", "en"]
Loaders should merge an overall result, starting in the order of the list by only adding values in case of missing entries.
AssetTranslationOrder
The class AssetTranslationOrder is used to load localizations from assets.
Arguments are:
this.basePath = 'assets/locales'base path for assetsMap<String, String>? namespacePackageMapoptional map that assigns package names to namespaces
Example
{
"validation": "velix"
}
tells the loader, that the namespace "validation" is stored in the assets of teh package "velix".
Translations are stored in json files under the locale dir.
Example
assets/
de/
validation.json
Interpolation #
Translations can contain placeholders that will be replaced by supplied values. Different possibilities exist:
variable
"hello {world}!" with arguments {"world": "world"} will result in "hello world!".
variable with format
"{value:number}"
will format numbers given the current locale ( influencing "," or "." in floating point numbers )
variable with format and arguments
"{price:currency(name: 'EUR')}"
Different formatters allow different parameters, with all formatters typically supporting String laccle as a locale code.
Implemented formatters are:
number
String localeint minimumFractionDigitsminimum number of digitsint maximumFractionDigitsmaximum number of digits
currency
String localeString namename of the currency
date
String localeString patternpattern accoring to the used formatting classDateFormat, e.g.yMMMd
Unil now, the parameters where part of the template itself. In order to be more flexible, they can also relate to dynamic parameters by prefixing the with "$".
Example:
Template is: "{price:currency(name: $currencyName)"
and is called with the args {"price": 100, "currencyName": "EUR"}
Setup #
A typical setup requires to initialize the main elements:
var localeManager = LocaleManager(Locale('en', "EN"), supportedLocales: [Locale('en', "EN"), Locale('de', "DE")]);
var i18n = I18N(
fallbackLocale: Locale("en", "EN"),
localeManager: localeManager,
loader: AssetTranslationLoader(
namespacePackageMap: {
"validation": "velix"
}
),
missingKeyHandler: (key) => '##$key##',
preloadNamespaces: ["validation", "example"]
);
// load namespaces
runApp(
ChangeNotifierProvider.value(
value: localeManager,
child: TODOApp(i18n: i18n),
),
);
and an application running as a locale consumer including some localizationDelegates:
...
child: Consumer<LocaleManager>(
builder: (BuildContext context, LocaleManager localeManager, Widget? child) {
return CupertinoApp(
...
// localization
localizationsDelegates: [I18nDelegate(i18n: i18n), GlobalCupertinoLocalizations.delegate,],
supportedLocales: localeManager.supportedLocales,
locale: localeManager.locale
);
}
)
Installation #
The library is published on pub.flutter-io.cn