7. Internationalization
7.1. Example 21: A Bilingual Application
A bilingual application has three types of text to translate:
- application text: labels and messages. These are stored in dictionaries, one per language;
- dates and amounts: the [intl] package (see Example 09), in the current language;
- text from the [Flutter] package itself: the buttons on its windows, the days and months in its calendar. The [flutter_localizations] package provides these.
7.1.1. The dictionaries
Each text has a key ("HOME.HELLO"). The parameters are written as [{{name}}]; a plural text has multiple keys, such as [_zero], [_one], and [_other]. These are the conventions of the [JavaScript] [i18next] library, used by the [React] and [Vue.js] clients of [RdvMedecins]: The case study will use their dictionaries as-is. The English dictionary has the same keys:
7.1.2. The translations
- lines 30–34: the translations: a [ChangeNotifier] (the screens will be rebuilt when the language changes), which retains the current language and its dictionary;
- lines 42–45: the source language: the one stored on the device, otherwise the phone’s language (if known), otherwise English;
- lines 48–54: change language: load the dictionary, a JSON file included with the application ([rootBundle.loadString] reads a file from the [assets] section of [pubspec.yaml]), decode it ([jsonDecode]), save the selection, and update the screens;
- lines 58–63: search for a key “HOME.HELLO” in the dictionary: it traverses, section by section, the nested JSON objects. [node is Map<String, Object?> ? ... : null]: a type check, which “promotes” the variable in the branch where it is true;
- Lines 66–83: the translation. For a plural (an integer parameter [count]), [Intl.pluralLogic] chooses the form according to the language rules: in French, 0 and 1 are singular; in English, only 1. An unknown key is displayed as-is: you can immediately see what’s missing. Finally, each [{{name}}] is replaced by the parameter’s value.
Why not use the official [Flutter] tool? [Flutter] offers a generator ([flutter gen-l10n]) that creates, from [.arb] files, a class with one method per text ([AppLocalizations.of(context).hello('Marie')]): The keys are validated at compile time. This is the best solution for an application where all texts are known in advance. But our server returns keys (“ERRORS.LOGIN_TAKEN”) that need to be translated at runtime: a dictionary, which is queried by the key, is much better suited for this purpose and allows us to share dictionaries with other clients.
7.1.3. The translated page
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 | |
- lines 30–31: an extension of [BuildContext] (see Example 09): [context.t('KEY')] reads the translations provided above, subscribes to updates, and performs the translation;
- lines 36–38: initialization: the names of the days and months in all languages, followed by the translations;
- line 39: [ChangeNotifierProvider.value] provides an object ALREADY created (by [main]);
- line 50: the application title, translated;
- lines 52–54: the language of [Flutter] itself follows that of the application;
- lines 76–77: the date and price, formatted in the current language, then passed as parameters to the text strings;
- lines 86–90: the language selection in the top bar;
- line 110: [count] selects the plural form and replaces [{{count}}];
- lines 124–128: the calendar for [Flutter]: its texts follow the language of [MaterialApp];
- lines 144–146: what the server [RdvMedecins] would return: a key and its parameters. The key from the server IS a key from the dictionary.
In English (left); in French, after an addition (middle); the calendar, in French (right):
![]() | ![]() | ![]() |
Changing the language re-renders the entire screen without restarting or making any requests to the server. The selected language is stored on the device.


