Skip to main content

Texts and languages

The texts that a user sees belong to the modules that show them. A module does not know which other modules the app has, which languages they speak or how the app keeps its translations. So it gives its texts to the localization role, and reads them back through the role:

A text and its owner​

A text is a LocalizedText: a name, the text in English, and its translations by the code of their language, such as uk. The owner of a text is the module, or the template of a role, that gives it to the role as TextsData.

The role names every text apart from those of other owners. The getter of a text starts with the id of its owner in lowerCamelCase, followed by the name of the text: the text title of the module haptics is read as context.l10n.hapticsTitle. When the texts of two owners would still need one getter, the role reports them.

A text has no parameters and no plural forms, so neither its English text nor a translation has a brace.

With the role and without it​

A module that only uses the localization role works in an app without it too. Its templates read a text through a variable that the role makes for it, and the variable has one value for each kind of app:

AppThe variable text_title renders as
with the localization rolecontext.l10n.hapticsTitle, with the import of the file of the texts
without it'Haptic feedback', the English text as a literal

The template of a role reads its texts the same way, through its render hook. Texts shows the code.

The languages of the app​

The template of the role chooses the languages of the app before the app is rendered:

  • By default, every language that a text of the app is in, English first and the others in the order the texts name them.
  • With the option --locales, the languages that it names, in its order. English is always among them, since a text reads in English wherever it has no translation.

An app can be only in a language in which Flutter has the texts of its own widgets. The role knows those languages as LocalizationRole.supportedLanguages, the set of the oldest Flutter that SMF generates apps for. It leaves any other language of a text out of the app, and --locales refuses such a language. Without the option, the role warns of each language that it leaves out, with the owner and the texts. It always warns about each text that has no translation into a language of the app.

The role asks no question: the languages follow from the texts of the modules and from the option.

Every app has texts. flutter_core, the module that every app has, gives the role the two texts of its fallback start screen, in English and in Ukrainian. So by default an app with the role is in both languages, also an app without a feature.

What the role generates​

Whichever module provides the role, its template generates lib/core/l10n/app_locale.dart with:

  • appLocales, the languages of the app as Locales, the first of which the app uses when the device asks for none of them;
  • appLocale, which keeps the language that the user chose: its value is one of appLocales, or null while the app follows the device, and choose() changes it;
  • AppLocaleScope, the widget around the root of the app through which the root reads appLocale, so that it rebuilds in the new language when the choice changes;
  • restoreAppLocale(), the restorer of the choice.

The template also gives the root MaterialApp its locale, its supportedLocales and the delegates of Flutter's own localizations, adds flutter_localizations to the app, and names the languages in the Info.plist of the iOS app. In the README of the app, it writes the section "Languages", which tells each place that a new language goes into.

The language that the user chose​

The language is a setting of the role, as the theme mode is a setting of the theme role. So the role works with two more roles:

RoleThe localization roleWhat its template gives it
Preferencesrequires itrestoreAppLocale, a restorer. A choice is saved under the key localization.locale, as the code of the language, and removed when the app follows the device again.
Settings screenuses itIn an app with a settings screen, LanguageSetting, the entry in which the user chooses a language of the app or the languages of the device, in lib/core/l10n/language_setting.dart.

The texts of the entry are texts of the template itself, in English and in Ukrainian, which it gives the role only in an app with a settings screen. The entry shows a language by its name in that language. The role has the names of English and Ukrainian, and smf create warns of any other language of such an app, whose name the developer adds to the file of the entry.

Because the role requires the preferences, an app with a module that provides the localization always has a module that provides the preferences, and remembers the language. Languages shows appLocale from the side of the app.

What the provider generates​

The provider keeps the texts. It generates lib/core/l10n/l10n.dart with the extension AppTexts on BuildContext, whose getter l10n returns an object with a String getter for each text of the app:

  • Each getter returns the text in the language of the context, or in English when the text has no translation into that language.
  • The provider renders the texts in the languages of the app only.
  • context.l10n works in every context below the root of the app, in the language that the root is in, also once that language changes.

How the texts get there is the provider's choice: in files of translations that a tool turns into code, or in Dart code. gen_l10n keeps them in ARB files, one for each language, from which gen-l10n of Flutter generates the code. A provider that loads its texts with a delegate adds the delegate to the localizationsDelegates of the root.

Rules​

In an app with the role, the role checks the modules before the app is generated, and the contract harness checks the generated code:

RuleWhat it requires
localization.textsThe texts of a module have lowerCamelCase names that differ, an English text, translations by the code of their language, and no braces. A variable of a brick that reads a text reads one that the app has, and is its English text in an app without the role. A text in the data of another role, such as the label of a destination, is among the texts that its module gave the role, and two such texts of one name do not differ.
localization.text_accessCode of a module reads only its own texts and those of the modules it depends on. The template of a role reads its own, and the texts in the data that the modules give its role or a role that it requires or uses, such as the labels of the destinations.
localization.texts_renderedA file of the provider names the getter of every text of the app.

The role also says what holds at the root of the app. Every delegate among the localizationsDelegates of the root supports each language of the app, and the supportedLocales of the root are those of the role alone: the argument takes the items of one contributor, so smf create reports a module that gives the root a locale of its own as a conflict with the role. So a module brings no delegate for texts of its own, which would have to know the languages of the app, and adds no language to the root. It gives its texts to the role.

Next​

  • Languages shows what the app has and how its code reads a text.
  • Texts shows how a module gives its texts and reads them in its templates.
  • Provide the localization covers a provider of the role.