Provide the localization
The localization role gets the texts of the modules and chooses the languages of the app. Its template generates the languages, the language that the user chose and what the root of the app needs to follow it. A provider keeps the texts and renders them. Texts and languages describes the role.
What a provider generates
- It generates
lib/core/l10n/l10n.dartwith the extensionAppTextsonBuildContext, whose getterl10nreturns an object with aStringgetter for each text oflocalizationRole.textsIn(input), named byAppText.getter. - Each getter returns the text in the language of the context, or in English when the text has no translation into that language.
- It renders the texts in the languages of
localizationRole.localesIn(input)only. context.l10nworks in every context below the root of the app, in the language that the root is in, also once that language changes.- A provider that loads its texts with a delegate adds the delegate to the
localizationsDelegatesof the root, and the delegate supports each language ofappLocales. One that reads the locale of the context itself needs none.
The provider adds no language to the root. supportedLocales takes the items of one contributor, and in an app with the role that is the template of the role.
With gen-l10n of Flutter
How the texts are kept is the provider's choice. gen_l10n keeps them in ARB files, one for each language, and leaves the code that reads them to gen-l10n of Flutter. The number of the files depends on the languages of the app, so its render hook generates them:
RoleOutput render(RoleHookInput<TextsData> input) {
final texts = localizationRole.textsIn(input);
return RoleOutput(
files: {
for (final language in localizationRole.localesIn(input))
'${GenL10nModule.arbDirectory}/app_$language.arb':
_arbOf(language, texts),
},
);
}
_arbOf writes each text that is in the language under the name of its getter. In an app with settings and material_theme, the file of Ukrainian has the title of the settings screen, the two texts of the fallback start screen of flutter_core, which every app has, and the texts of the entries of the theme role and of the localization role:
{
"@@locale": "uk",
"settingsTitle": "Налаштування",
"flutterCoreFallbackHint": "Стартового екрана ще немає. Додайте фічу з маршрутом або замініть цей екран.",
"flutterCoreFallbackCopied": "Скопійовано",
"themeTitle": "Тема",
"themeSystem": "Системна",
"themeLight": "Світла",
"themeDark": "Темна",
"localizationLanguage": "Мова",
"localizationSystem": "Як у системі"
}
The brick of the module has what every app gets: l10n.yaml, and lib/core/l10n/l10n.dart with the extension, whose getter returns the class that gen-l10n generates:
AppLocalizations get l10n => AppLocalizations.of(this);
The module also turns the generation on in the pubspec and gives the root of the app the delegate of that class:
const PubspecContribution.flutter(generate: true),
// The code that gen-l10n generates imports intl, in the version
// that flutter_localizations of the Flutter SDK pins.
const PubspecContribution.hosted('intl', 'any'),
const SocketContribution.arg(
AppEntryRole.appArgs,
'localizationsDelegates',
Fragment(
'AppLocalizations.delegate',
imports: [ImportRef.app('l10n/app_localizations.dart')],
),
),
A provider checks what only it knows in its validate hook. The class that gen-l10n generates has members of its own, such as supportedLocales, so gen_l10n reports a text whose getter would have the name of one of them.
A provider that keeps its texts in Dart code renders a getter for each text into its brick instead, as a fragment variable of its render hook. The fixtures of smf_pipeline have such a provider, with a delegate written by hand.
See Files of render hooks for the rules of the files that a hook generates.
What the role does itself
The role owns everything that is the same with every provider, so a provider leaves it alone:
- the list of the languages,
appLocales, and the language that the user chose,appLocale, which the app remembers in its preferences; - the
localeand thesupportedLocalesof the root; - in an app with a settings screen, the entry in which the user chooses the language;
- the section "Languages" of the README of the app, and the note of the role for coding agents, which tell where the languages are and each place that a new language goes into.
So a provider tells in a README section of its own, and in its note, only where its texts are, how to add one, and what its texts need for a new language.
The role requires the preferences role, and a provider requires what its role requires. So an app with the provider has a module that provides the preferences, and the registry of the tests of the provider has one too, as that of smf_gen_l10n has SharedPreferencesModule.
What is checked
The contract harness checks that the provider generates the extension, and that one of its files names the getter of every text of the app. So the tests of a module with texts need not look into the files of a provider.
SMF's CI checks the rest in running apps, with every provider of the role that SMF has: the app shows each of its texts in each of its languages, in English where a translation is missing, follows the language of the device, and remembers the language that the user chose. Contributing lists these tests.