{"id":13859,"date":"2021-04-06T09:38:00","date_gmt":"2021-04-06T07:38:00","guid":{"rendered":"https:\/\/phrase.com\/blog\/?p=13859"},"modified":"2023-11-08T11:37:15","modified_gmt":"2023-11-08T10:37:15","slug":"flutter-localization","status":"publish","type":"post","link":"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/","title":{"rendered":"The Ultimate Guide to Flutter Localization"},"content":{"rendered":"\n<div id=\"acf\/text-block_60c9b1f7091c838ea2928585cad75e74\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><a href=\"https:\/\/flutter.dev\/\">Flutter<\/a>, Google\u2019s multi-platform app framework, has not only garnered popularity in the mobile app development arena but has seamlessly branched out to web, Linux, macOS, and Windows. Beyond that, Flutter is blazing fast and a joy to work with.<\/p>\n<p>When it comes to Flutter app internationalization (i18n), the Flutter team has crafted a solid built-in solution. In this tutorial, we\u2019ll set up and configure Flutter\u2019s i18n libraries, use them to load and display translations and work through date\/time formatting, among other localization goodies.<\/p>\n<p>\ud83e\udd3f\u00a0<em>Go deeper \u00bb<\/em> Flutter\u2019s native localization package is built on the first-party\u00a0<a href=\"https:\/\/pub.dev\/packages\/intl\">Dart intl package<\/a>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n\n<div id=\"acf\/blog-cta-block_3a4591612f09be0f625716c82f527482\" class=\"pxblock pxblock--blog-cta bg--green image--orientation-landscape\">\n\t<div class=\"block-container\">\n\t\t\t\t\t<div class=\"image image--align-middle\">\n\t\t\t\t<img loading=\"lazy\" decoding=\"async\" width=\"1260\" height=\"992\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero.png\" class=\"attachment-original size-original\" alt=\"String Management UI visual | Phrase\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero.png 1260w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero-300x236.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero-1024x806.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2022\/08\/String_Hero-768x605.png 768w\" sizes=\"(max-width: 1260px) 100vw, 1260px\" \/>\t\t\t<\/div>\n\t\t\t\t<div class=\"content\">\n\t\t\t<p class=\"subhead\">Phrase Strings<\/p>\n<p class=\"secondary h6\">Take your web or mobile app global without any hassle<\/p>\n<div class=\"text--copy\">\n<p class=\"small\">Adapt your software, website, or video game for global audiences with the leanest and most realiable software localization platform.<\/p>\n<p><a class=\"btn btn--outline\" href=\"https:\/\/phrase.com\/platform\/strings\/\">Explore Phrase Strings<\/a><\/p>\n<\/div>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n\n<div id=\"acf\/text-block_f489cf287ae2dbc0a29ed9c2baebf5ca\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_69_1 counter-hierarchy ez-toc-counter ez-toc-grey ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Overview<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#our-demo-app\" title=\"Our demo app\">Our demo app<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#versions-used\" title=\"Versions used\">Versions used<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#installation-and-setup\" title=\"Installation and setup\">Installation and setup<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#localization-configuration\" title=\"Localization configuration\">Localization configuration<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#adding-translation-files\" title=\"Adding translation files\">Adding translation files<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#configuring-our-app\" title=\"Configuring our app\">Configuring our app<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#automatic-code-generation\" title=\"Automatic code generation\">Automatic code generation<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#using-our-applocalizations\" title=\"Using our AppLocalizations\">Using our AppLocalizations<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#locale-resolution\" title=\"Locale resolution\">Locale resolution<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#updating-the-ios-project\" title=\"Updating the iOS project\">Updating the iOS project<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#getting-the-active-locale\" title=\"Getting the active locale\">Getting the active locale<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#basic-translation-messages\" title=\"Basic translation messages\">Basic translation messages<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#interpolation-in-messages\" title=\"Interpolation in messages\">Interpolation in messages<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#plurals\" title=\"Plurals\">Plurals<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#number-formatting\" title=\"Number formatting\">Number formatting<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#date-formatting\" title=\"Date Formatting\">Date Formatting<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#directionality-left-to-right-and-right-to-left\" title=\"Directionality: left-to-right and right-to-left\">Directionality: left-to-right and right-to-left<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#adding-localized-assets\" title=\"Adding localized assets\">Adding localized assets<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#changing-language-in-app\" title=\"Changing language in app\">Changing language in app<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/phrase.com\/blog\/posts\/flutter-localization\/#flutter-localization-made-simpler\" title=\"Flutter localization made simpler\">Flutter localization made simpler<\/a><\/li><\/ul><\/nav><\/div>\n<h2><span class=\"ez-toc-section\" id=\"our-demo-app\"><\/span>Our demo app<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>To keep things grounded and fun, we\u2019ll build a small demo app and localize it:\u00a0<em>Heroes of Computer Science<\/em>\u00a0presents a selection of notable figures in the relatively short history of computing.<\/p>\n<figure id=\"attachment_13861\" aria-describedby=\"caption-attachment-13861\" style=\"width: 350px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-13861\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-app-en-544x1024.png\" alt=\"Heroes of Computer Science home screen | Phrase\" width=\"350\" height=\"658\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-app-en-544x1024.png 544w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-app-en-159x300.png 159w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-app-en-768x1445.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-app-en-816x1536.png 816w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-app-en.png 1044w\" sizes=\"(max-width: 350px) 100vw, 350px\" \/><figcaption id=\"caption-attachment-13861\" class=\"wp-caption-text\">Our app in English\u2014and soon in other languages<\/figcaption><\/figure>\n<h3><span class=\"ez-toc-section\" id=\"versions-used\"><\/span>Versions used<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>We\u2019re using the following language, framework, and package versions in this article:<\/p>\n<ul>\n<li>Dart 3.1.1<\/li>\n<li>Flutter 3.13.3<\/li>\n<li>DevTools 2.25.0<\/li>\n<li>flutter_localizations (version seems tied to Flutter) \u2014 provides localizations to common widgets, like Material or Cupertino widgets.<\/li>\n<li>intl 0.18.0 \u2014 the backbone of the localization system; allows us to create and use our own localizations; used for formatting dates and numbers.<\/li>\n<\/ul>\n<p>Now let\u2019s look at the code for our starter app, which is quite straightforward.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-1\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\">\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter\/material.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'screens\/hero_list.dart'<\/span>;\n\n<span class=\"hljs-keyword\">void<\/span> main() =&gt; runApp(MyApp());\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyApp<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">return<\/span> MaterialApp(\n      title: <span class=\"hljs-string\">'Heroes of Computer Science'<\/span>,\n      theme: ThemeData(\n        primarySwatch: Colors.blue,\n      ),\n      home: HeroList(title: <span class=\"hljs-string\">'Heroes of Computer Science'<\/span>),\n    );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-1\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_62ee6839c53a2cbfd032a4919a081ac8\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Our root widget is a bread-and-butter <code>MaterialApp<\/code>, with a <code>HeroList<\/code> at its <code>home<\/code> route.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-2\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter\/material.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_i18n_2021\/screens\/settings.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_i18n_2021\/widgets\/hero_card.dart'<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroList<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> title;\n\n  HeroList({<span class=\"hljs-keyword\">this<\/span>.title = <span class=\"hljs-string\">''<\/span>});\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">return<\/span> Scaffold(\n      appBar: AppBar(\n        title: Text(title),\n        actions: &lt;Widget&gt;&#91;\n          IconButton(\n            icon: Icon(Icons.settings),\n            tooltip: <span class=\"hljs-string\">'Open settings'<\/span>,\n            onPressed: () {\n              Navigator.push(\n                context,\n                MaterialPageRoute(builder: (context) =&gt; Settings()),\n              );\n            },\n          )\n        ],\n      ),\n      body: Padding(\n        padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.all(<span class=\"hljs-number\">16<\/span>),\n        child: Column(\n          children: &#91;\n            Padding(\n              padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.only(bottom: <span class=\"hljs-number\">8.0<\/span>),\n              child: Text(<span class=\"hljs-string\">'6 Heroes'<\/span>),\n            ),\n            Expanded(\n              child: ListView(\n                children: &lt;Widget&gt;&#91;\n                  HeroCard(\n                    name: <span class=\"hljs-string\">'Grace Hopper'<\/span>,\n                    born: <span class=\"hljs-string\">'9 December 1906'<\/span>,\n                    bio: <span class=\"hljs-string\">'Devised theory of machine...'<\/span>,\n                  ),\n                  HeroCard(\n                    name: <span class=\"hljs-string\">'Alan Turing'<\/span>,\n                    born: <span class=\"hljs-string\">'23 June 1912'<\/span>,\n                    bio: <span class=\"hljs-string\">'Father of theoretical computer...'<\/span>,\n                  ),\n                  <span class=\"hljs-comment\">\/\/ ...<\/span>\n                ],\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-2\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_3d87ce0ba4fc687403b237e56a0d94ce\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><code>HeroList<\/code> mainly houses a <code>ListView<\/code> of parameterized <code>HeroCard<\/code>s.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-3\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter\/material.dart'<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroCard<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  final <span class=\"hljs-built_in\">String<\/span> name;\n  final <span class=\"hljs-built_in\">String<\/span> born;\n  final <span class=\"hljs-built_in\">String<\/span> bio;\n  final <span class=\"hljs-built_in\">String<\/span> imagePath;\n  final <span class=\"hljs-built_in\">String<\/span> placeholderImagePath = <span class=\"hljs-string\">'assets\/images\/placeholder.jpg'<\/span>;\n\n  <span class=\"hljs-keyword\">const<\/span> HeroCard({\n    Key key,\n    <span class=\"hljs-keyword\">this<\/span>.name = <span class=\"hljs-string\">''<\/span>,\n    <span class=\"hljs-keyword\">this<\/span>.born = <span class=\"hljs-string\">''<\/span>,\n    <span class=\"hljs-keyword\">this<\/span>.bio = <span class=\"hljs-string\">''<\/span>,\n    <span class=\"hljs-keyword\">this<\/span>.imagePath,\n  }) : <span class=\"hljs-keyword\">super<\/span>(key: key);\n\n  @override\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">var<\/span> theme = Theme.of(context);\n\n    <span class=\"hljs-keyword\">return<\/span> Card(\n      child: Padding(\n        padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.all(<span class=\"hljs-number\">4.0<\/span>),\n        <span class=\"hljs-attr\">child<\/span>: Row(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          <span class=\"hljs-attr\">children<\/span>: &lt;Widget&gt;&#91;\n            Padding(\n              padding: const EdgeInsets.only(right: 8.0),\n              child: ClipRRect(\n                borderRadius: BorderRadius.circular(2),\n                child: Image.asset(\n                  imagePath ?? placeholderImagePath,\n                  width: 100,\n                  height: 100,\n                ),\n              ),\n            ),\n            Expanded(\n              child: Column(\n                crossAxisAlignment: CrossAxisAlignment.start,\n                children: &lt;Widget&gt;&#91;\n                  Padding(\n                    padding: const EdgeInsets.only(top: 4),\n                    child: Text(\n                      name,\n                      style: theme.textTheme.headline6,\n                    ),\n                  ),\n                  Padding(\n                    padding: const EdgeInsets.only(top: 2, bottom: 4),\n                    child: Text(\n                      born.isEmpty ? '' : 'Born $born',\n                      style: TextStyle(\n                        fontSize: 12,\n                        fontWeight: FontWeight.w300,\n                      ),\n                    ),\n                  ),\n                  Text(\n                    bio,\n                    style: TextStyle(fontSize: 14),\n                  ),\n                ],\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-3\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_a627c8c711a344210a80d42ef41c09c1\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p><code>HeroCard<\/code> displays the given image and string params in a nice Material <code>Card<\/code> widget and pretties everything up. Alright, let\u2019s get to localizing this puppy!<\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> You can get the code of the app up to this point from the <a href=\"https:\/\/github.com\/PhraseApp-Blog\/flutter-2-i18n-2021\/tree\/start\">start branch of our GitHub repo<\/a>. The <a href=\"https:\/\/github.com\/PhraseApp-Blog\/flutter-2-i18n-2021\/tree\/main\">main branch<\/a> also contains the fully localized app.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"installation-and-setup\"><\/span>Installation and setup<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We can install our packages by adding a few lines to <code>pubspec.yaml<\/code>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-4\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-attr\">version:<\/span> <span class=\"hljs-number\">1.0<\/span><span class=\"hljs-number\">.0<\/span><span class=\"hljs-string\">+1<\/span>\n\n<span class=\"hljs-attr\">environment:<\/span>\n\u00a0\u00a0<span class=\"hljs-string\">sdk:<\/span>\u00a0<span class=\"hljs-string\">'&gt;=3.1.1\u00a0&lt;4.0.0'<\/span>\n\n<span class=\"hljs-attr\">dependencies:<\/span>\n\u00a0\u00a0<span class=\"hljs-attr\">flutter:<\/span>\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-string\">sdk:<\/span>\u00a0<span class=\"hljs-string\">flutter<\/span>\n\u00a0\u00a0\u00a0\u00a0\n\u00a0\u00a0<span class=\"hljs-string\">cupertino_icons:<\/span>\u00a0<span class=\"hljs-string\">^1.0.2<\/span>\n\n  <span class=\"hljs-comment\"># Add the flutter_localizations package<\/span>\n\u00a0\u00a0<span class=\"hljs-attr\">flutter_localizations:<\/span>\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-string\">sdk:<\/span>\u00a0<span class=\"hljs-string\">flutter<\/span>\n  <span class=\"hljs-comment\"># Add the intl package<\/span>\n\u00a0\u00a0<span class=\"hljs-string\">intl:<\/span>\u00a0<span class=\"hljs-string\">^0.18.0<\/span>\n\n<span class=\"hljs-attr\">dev_dependencies:<\/span>\n\u00a0\u00a0<span class=\"hljs-attr\">flutter_test:<\/span>\n\u00a0\u00a0\u00a0\u00a0<span class=\"hljs-string\">sdk:<\/span>\u00a0<span class=\"hljs-string\">flutter<\/span>\n\n<span class=\"hljs-attr\">flutter:<\/span>\n\u00a0\u00a0<span class=\"hljs-string\">generate:<\/span>\u00a0<span class=\"hljs-literal\">true<\/span> <span class=\"hljs-comment\"># Add this line<\/span>\n \u00a0<span class=\"hljs-string\">uses-material-design:<\/span>\u00a0<span class=\"hljs-literal\">true<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-4\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b684022ea53834a977ca49eab40cf6e1\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>After adding the highlighted lines above we can run\u00a0<code>flutter pub get<\/code>\u00a0from the command line to pull in our packages. The\u00a0<code>generate: true<\/code> line is necessary for the automatic code generation the localization packages provide for us. We\u2019ll go more into code generation business shortly. For now, do include the line. It can save you quite some time.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"localization-configuration\"><\/span>Localization configuration<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>With our packages installed, let\u2019s add a <code>l10n.yaml<\/code> file to the root of our project. This file configures where our translation files will sit and the names of auto-generated dart files.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-5\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-attr\">arb-dir:<\/span> <span class=\"hljs-string\">lib\/l10n<\/span>\n<span class=\"hljs-attr\">template-arb-file:<\/span> <span class=\"hljs-string\">app_en.arb<\/span>\n<span class=\"hljs-attr\">output-localization-file:<\/span> <span class=\"hljs-string\">app_localizations.dart<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-5\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_570813ec7f1033553f0ec096a354119e\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\udd17 <em>Resource \u00bb<\/em> The <a href=\"https:\/\/docs.google.com\/document\/d\/10e0saTfAv32OZLRmONy866vnaw0I2jwL8zukykpgWBc\/edit#heading=h.upij01jgi58m\">official Internationalization User Guide<\/a> covers many more options that can go in <code>l10n.yaml<\/code> to control the Flutter i18n code generator.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"adding-translation-files\"><\/span>Adding translation files<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Flutter localization uses ARB (Application Resource Bundle) files to house its translations by default. These are simple files written in JSON syntax. At the very least, we need a template file that corresponds to our default locale (English in our case). We specified that our template file will be <code>lib\/l10n\/app_en.arb<\/code> in our above configuration. So let\u2019s create this housing directory and add our template translations file to it.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-6\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">{\n  <span class=\"hljs-attr\">\"appTitle\"<\/span>: <span class=\"hljs-string\">\"Heroes of Computer Science\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-6\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_58fba1700446575c967db29b43ee413f\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Of course, all these shenanigans wouldn\u2019t make much sense if we couldn\u2019t provide translations for other locales. We\u2019ll add an Arabic translations file here. Feel free to add any language you like. We&#8217;ll touch on right-to-left (RTL) layouts a bit later, so if you\u2019re interested in that you might want to stick to Arabic or another RTL language.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-7\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">{\n  <span class=\"hljs-attr\">\"appTitle\"<\/span>: <span class=\"hljs-string\">\"\u0623\u0628\u0637\u0627\u0644 \u0639\u0644\u0648\u0645 \u0627\u0644\u0643\u0645\u0628\u064a\u0648\u062a\u0631\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-7\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_ee5514d715e7e4c471dc1d82e739f8cb\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We can add as many locale translations as we want. We just need to make sure that our files conform to our configured naming convention: <code>lib\/l10n\/app_&lt;locale&gt;.arb<\/code><\/p>\n<h3><span class=\"ez-toc-section\" id=\"configuring-our-app\"><\/span>Configuring our app<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Let\u2019s start telling our app about our anxious interest in i18n. We need to configure our <code>main.dart<\/code> file to use the Flutter localization packages.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-8\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter\/material.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_localizations\/flutter_localizations.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'screens\/hero_list.dart'<\/span>;\n\n<span class=\"hljs-keyword\">void<\/span> main() =&gt; runApp(MyApp());\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyApp<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  @override\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">return<\/span> MaterialApp(\n      title: <span class=\"hljs-string\">'Heroes of Computer Science'<\/span>,\n      <span class=\"hljs-attr\">localizationsDelegates<\/span>: &#91;\n        GlobalMaterialLocalizations.delegate,\n        GlobalWidgetsLocalizations.delegate,\n        GlobalCupertinoLocalizations.delegate,\n      ],\n      <span class=\"hljs-attr\">supportedLocales<\/span>: &#91;\n        <span class=\"hljs-comment\">\/\/ 'en' is the language code. We could optionally provide a<\/span>\n        <span class=\"hljs-comment\">\/\/ country code as the second param, e.g.<\/span>\n        <span class=\"hljs-comment\">\/\/ Locale('en', 'US'). If we do that, we may want to<\/span>\n        <span class=\"hljs-comment\">\/\/ provide an additional app_en_US.arb file for<\/span>\n        <span class=\"hljs-comment\">\/\/ region-specific translations.<\/span>\n        <span class=\"hljs-keyword\">const<\/span> Locale(<span class=\"hljs-string\">'en'<\/span>, <span class=\"hljs-string\">''<\/span>),\n        <span class=\"hljs-keyword\">const<\/span> Locale(<span class=\"hljs-string\">'ar'<\/span>, <span class=\"hljs-string\">''<\/span>),\n      ],\n      <span class=\"hljs-attr\">theme<\/span>: ThemeData(\n        primarySwatch: Colors.blue,\n      ),\n      <span class=\"hljs-attr\">home<\/span>: HeroList(title: <span class=\"hljs-string\">'Heroes of Computer Science'<\/span>),\n    );\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-8\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_27c5ca8b810795341a35df66e1b6437a\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>After importing <code>flutter_localizations.dart<\/code>, we add the <code>localizationsDelegates<\/code> and <code>supportedLocales<\/code> props to the <code>MaterialApp<\/code> constructor. <code>localizationsDelegates<\/code> provide localizations to our app. The ones included above provide localizations for Flutter widgets, Material, and Cupertino, which have already been localized by the Flutter team.<\/p>\n<p>For example, suppose we had a <code>MaterialApp<\/code> and called <a href=\"https:\/\/api.flutter.dev\/flutter\/material\/showDatePicker.html\">the showDatePicker() function<\/a> somewhere within it. Supposing also that our operating system language is set to Arabic, we would see something like the following.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13862\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-date-picker-ar.png\" alt=\"Flutter showDatePicker() calendar | Phrase\" width=\"350\" height=\"549\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-date-picker-ar.png 598w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-date-picker-ar-191x300.png 191w\" sizes=\"(max-width: 350px) 100vw, 350px\" \/><\/p>\n<p>Note that we didn\u2019t have to translate anything ourselves to get this. The date picker widget has already been localized by the Flutter team. We just need to wire up the correct delegates in our app constructor, as we did above. Big hats off to the Flutter team for this: what a time-saver!<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> At the time of writing, <code>flutter_localizations<\/code> <a href=\"https:\/\/flutter.dev\/docs\/development\/accessibility-and-localization\/internationalization#setting-up\">supports 78 languages<\/a>.<\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> The <a href=\"https:\/\/flutter.dev\/docs\/development\/accessibility-and-localization\/internationalization#how-internationalization-in-flutter-works\">official Flutter documentation<\/a> does a good job of explaining how the different parts, like delegates and the <code>Localizations<\/code> class, work together for their i18n\/l10n.<\/p>\n<p>The <code>supportedLocales<\/code> prop we provided to the <code>MaterialApp<\/code> constructor lists the languages our app supports. Flutter will <em>only<\/em> rebuild widgets in response to a locale change if the locale is in the <code>supportedLocales<\/code> list. We\u2019ll get back to <code>supportedLocales<\/code> in a moment when we discuss locale resolution. Right now, let\u2019s generate some code!<\/p>\n<h3><span class=\"ez-toc-section\" id=\"automatic-code-generation\"><\/span>Automatic code generation<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>In order to use the translations in the ARB files in our Flutter app, we need to generate some Dart files that we import whenever we need the translations. To generate these files, just make sure you\u2019ve followed the installation and setup steps up to this point and run the app. That&#8217;s right, just run the app. The code will automatically get generated, and if all went well, you should see the following files in your project directory:<\/p>\n<ul>\n<li><code>.dart_tool\/flutter_gen\/gen_l10n\/app_localizations.dart<\/code><\/li>\n<li><code>.dart_tool\/flutter_gen\/gen_l10n\/app_localizations_en.dart<\/code><\/li>\n<li><code>.dart_tool\/flutter_gen\/gen_l10n\/app_localizations_ar.dart<\/code><\/li>\n<\/ul>\n<p>\ud83d\uddd2\ufe0f<em> Note \u00bb<\/em> If these files weren\u2019t generated, make sure your Flutter app has no compilation errors and check your debug console when you run the app.<\/p>\n<h3><span class=\"ez-toc-section\" id=\"using-our-applocalizations\"><\/span>Using our AppLocalizations<span class=\"ez-toc-section-end\"><\/span><\/h3>\n<p>Let\u2019s make use of the newly generated code files to localize our app title.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-9\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter\/material.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_i18n_2021\/screens\/settings.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_localizations\/flutter_localizations.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_gen\/gen_l10n\/app_localizations.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'screens\/hero_list.dart'<\/span>;\n\n<span class=\"hljs-keyword\">void<\/span> main() =&gt; runApp(MyApp());\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyApp<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">return<\/span> MaterialApp(\n      onGenerateTitle: (context) {\n        <span class=\"hljs-keyword\">return<\/span> AppLocalizations.of(context).appTitle;\n      },\n      localizationsDelegates: &#91;\n        AppLocalizations.delegate,\n        GlobalMaterialLocalizations.delegate,\n        GlobalWidgetsLocalizations.delegate,\n        GlobalCupertinoLocalizations.delegate,\n      ],\n      supportedLocales: &#91;\n        <span class=\"hljs-keyword\">const<\/span> Locale(<span class=\"hljs-string\">'en'<\/span>, <span class=\"hljs-string\">''<\/span>),\n        <span class=\"hljs-keyword\">const<\/span> Locale(<span class=\"hljs-string\">'ar'<\/span>, <span class=\"hljs-string\">''<\/span>),\n      ],\n      theme: ThemeData(\n        primarySwatch: Colors.blue,\n      ),\n      <span class=\"hljs-comment\">\/\/ remove home: HeroList(...)<\/span>\n      initialRoute: <span class=\"hljs-string\">'\/'<\/span>,\n      routes: {\n        <span class=\"hljs-string\">'\/'<\/span>: (context) =&gt; HeroList(title: AppLocalizations.of(context).appTitle),\n        <span class=\"hljs-string\">'\/settings'<\/span>: (context) =&gt; Settings(),\n      },\n    );\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-9\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_d1881ded3e9d194204cf013c1eb4b447\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We import <code>app_localizations.dart<\/code> and add the auto-generated <code>AppLocalizations.delegate<\/code> to our delegate list. This provides us with the <code>AppLocalizations<\/code> widget, which we use to translate the app title and the <code>HeroList<\/code> title. The auto-generated <code>appTitle<\/code> property will contain the translation that matches the active locale, pulled from our <code>app_&lt;locale&gt;.arb<\/code> file.<\/p>\n<p>\u270b <em>Heads up \u00bb<\/em> Due to loading order, our translations won\u2019t be ready when we\u2019re constructing our <code>MaterialApp<\/code>. So we use the <code>onGenerateTitle<\/code> and <code>routes<\/code> props, and their builder <code>(context) {}<\/code> functions to make sure that our translations are ready when we set our title strings.<\/p>\n<p>Now, if we set our operating system language to Arabic and run our app, lo and behold!<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13863\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-ar-title-544x1024.png\" alt=\"Heroes of Computer Science with Arabic title | Phrase\" width=\"350\" height=\"658\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-ar-title-544x1024.png 544w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-ar-title-159x300.png 159w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-ar-title-768x1445.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-ar-title-816x1536.png 816w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-ar-title.png 1044w\" sizes=\"(max-width: 350px) 100vw, 350px\" \/><\/p>\n<p>Our title is now in Arabic. Moreover, notice how Flutter has laid out many of its widgets in a right-to-left direction automatically for us. Since Arabic is a right-to-left language, this saves us a ton of time! We\u2019ll have to fix that padding to the left of the image in the <code>HeroCard<\/code>s, and we\u2019ll do that when we tackle directionality a bit later.<\/p>\n<p>\ud83e\udd3f <em>Go deeper \u00bb<\/em> The eagle-eyed reader may have noticed that <code>AppLocalizations.of(context)<\/code> looks a lot like calling an <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/InheritedWidget-class.html\">InheritedWidget<\/a>. That\u2019s because localization objects work a lot like <code>InheritedWidget<\/code>s.<\/p>\n<p>That\u2019s it for setup. We have the foundation for localizing our app now. One question that you might have at this point is, \u201chow does Flutter decide what locale to use?\u201d Let\u2019s talk about that.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"locale-resolution\"><\/span>Locale resolution<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The locales we provided to <code>MaterialApp(supportedLocales: [...])<\/code> are the only ones Flutter will use to determine the active locale when the app runs. To do this, Flutter uses three properties of a locale:<\/p>\n<ul>\n<li>The language code, e.g. <code>'en'<\/code>for English<\/li>\n<li>The country code (optional), e.g. the <code>US<\/code> part in <code>en_US<\/code><\/li>\n<li><a href=\"https:\/\/flutter.dev\/docs\/development\/accessibility-and-localization\/internationalization#advanced-locale-definition\">The script code<\/a> (optional) \u2014the letter set used, e.g. traditional (<code>Hant<\/code>) or simplified Chinese (<code>Hans<\/code>)<\/li>\n<\/ul>\n<p>By default, Flutter will read the user\u2019s preferred system locales and:<\/p>\n<ol>\n<li>Try to match the <code>languageCode<\/code>, <code>scriptCode<\/code>, and <code>countryCode<\/code> with one in <code>supportedLocales<\/code>. If that fails,<\/li>\n<li>Try to match the <code>languageCode<\/code> and <code>scriptCode<\/code> with one in <code>supportedLocales<\/code>. If that fails,<\/li>\n<li>Try to match the <code>languageCode<\/code> and <code>countryCode<\/code> with one in <code>supportedLocales<\/code>. If that fails,<\/li>\n<li>Try to match the <code>languageCode<\/code> with one in <code>supportedLocales<\/code>. If that fails,<\/li>\n<li>Try to match the <code>countryCode<\/code> with one in <code>supportedLocales<\/code> only when all preferred locales fail to match. If that fails,<\/li>\n<li>Return the first element of <code>supportedLocales<\/code> as a fallback.<\/li>\n<\/ol>\n<p>So in our app, if the user\u2019s iOS language is set to <code>ar_SA<\/code>, they would see our <code>ar<\/code> localizations (4. above) . If the user\u2019s iOS language is set to <code>fr<\/code> (French), they would see our <code>en<\/code> localizations (6. above). On Android, a user can have a list of preferred locales, not just one. This is covered by Flutter in the above resolution algorithm.<\/p>\n<p>\ud83d\udd17 Resource \u00bb The above algorithm is a paraphrasing of the <a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/WidgetsApp\/supportedLocales.html\">official documentation of the supportedLocales property<\/a>.<\/p>\n<p>\u270b <em>Heads up \u00bb<\/em> If your app supports a locale with a country code, like <code>fr_CA<\/code> (Canadian French), you should provide a fallback without the country code, like <code>fr<\/code>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"updating-the-ios-project\"><\/span>Updating the iOS project<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>The official Flutter documentation mentions the need to update the <code>Info.plist<\/code> directly in the iOS app bundle, adding our supported locales to it. If <code>Info.plist<\/code> isn\u2019t updated our iOS app might not work as expected. To make the update we just need to open <code>ios\/Runner\/Info.plist<\/code> in any text editor and make sure the following entries are in there.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-10\" data-shcb-language-name=\"plaintext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">&lt;key&gt;CFBundleLocalizations&lt;\/key&gt;\n&lt;array&gt;\n  &lt;string&gt;en&lt;\/string&gt;\n  &lt;string&gt;ar&lt;\/string&gt;\n&lt;\/array&gt;<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-10\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">plaintext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_36cf7fdf661634f1c443b881f4255d35\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<h2><span class=\"ez-toc-section\" id=\"getting-the-active-locale\"><\/span>Getting the active locale<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We sometimes need to know what the runtime locale is in our code. We can do this with the following snippet.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-11\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\">Locale activeLocale = Localizations.localeOf(context);\n\n<span class=\"hljs-comment\">\/\/ Assuming our active locale is fr_CA...<\/span>\ndebugPrint(activeLocale.languageCode);\n<span class=\"hljs-comment\">\/\/ =&gt; fr<\/span>\n\ndebugPrint(activeLocale.countryCode);\n<span class=\"hljs-comment\">\/\/ =&gt; CA<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-11\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_5f4dab88b25a6cd407a0dcc5f6e123be\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\u270b <em>Heads up \u00bb<\/em> Notice that we\u2019re using <code>Localizations<\/code> , a widget built into Flutter, and not the auto-generated <code>AppLocalizations<\/code>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"basic-translation-messages\"><\/span>Basic translation messages<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We already covered basic translation messages when we added our <code>appTitle<\/code> message. However, let\u2019s quickly go over the workflow for adding messages. We\u2019ll translate the tooltip of our <code>HeroList<\/code>&#8216;s app bar icon button next. Might as well make <em>some<\/em> use of it since we&#8217;re not really building a settings screen \ud83d\ude05.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-12\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroList<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> title;\n\n  HeroList({<span class=\"hljs-keyword\">this<\/span>.title = <span class=\"hljs-string\">''<\/span>});\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">return<\/span> Scaffold(\n      appBar: AppBar(\n        title: Text(title),\n        actions: &lt;Widget&gt;&#91;\n          IconButton(\n            icon: Icon(Icons.settings),\n            tooltip: <span class=\"hljs-string\">'Open settings'<\/span>,\n            onPressed: () {\n              Navigator.push(\n                context,\n                MaterialPageRoute(builder: (context) =&gt; Settings()),\n              );\n            },\n          )\n        ],\n      ),\n      body: ...\n  );\n}\n\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-12\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_650b0a37f0f951c2c67a0ac30e0c937b\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Let\u2019s get that tooltip localized, shall we? First, we\u2019ll add the relevant entries to our ARB files.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-13\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ English<\/span>\n{\n  <span class=\"hljs-attr\">\"appTitle\"<\/span>: <span class=\"hljs-string\">\"Heroes of Computer Science\"<\/span>,\n  <span class=\"hljs-attr\">\"openSettings\"<\/span>: <span class=\"hljs-string\">\"Open Settings\"<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ Arabic<\/span>\n{\n  <span class=\"hljs-attr\">\"appTitle\"<\/span>: <span class=\"hljs-string\">\"\u0623\u0628\u0637\u0627\u0644 \u0639\u0644\u0648\u0645 \u0627\u0644\u0643\u0645\u0628\u064a\u0648\u062a\u0631\"<\/span>,\n  <span class=\"hljs-attr\">\"openSettings\"<\/span>: <span class=\"hljs-string\">\"\u0625\u0641\u062a\u062d \u0627\u0644\u0625\u0639\u062f\u0627\u062f\u0627\u062a\"<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-13\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b96e6821584d1a316c3a45a85ab91498\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Next, let\u2019s <em>reload our app to regenerate our code files<\/em>. This step is really important and forgetting it can lead to undue frustration. Note that this is a full app restart (<img loading=\"lazy\" decoding=\"async\" class=\"alignnone wp-image-13872\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-app-reload-1.png\" alt=\"Refresh button | Phrase\" width=\"24\" height=\"24\" \/>), not a hot reload.<\/p>\n<p>Now we can update our code to use our new localized message.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-14\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-comment\">\/\/ ... <\/span>\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_gen\/gen_l10n\/app_localizations.dart'<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroList<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> title;\n\n  HeroList({<span class=\"hljs-keyword\">this<\/span>.title = <span class=\"hljs-string\">''<\/span>});\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">var<\/span> t = AppLocalizations.of(context);\n\n    <span class=\"hljs-keyword\">return<\/span> Scaffold(\n      appBar: AppBar(\n        title: Text(title),\n        actions: &lt;Widget&gt;&#91;\n          IconButton(\n            icon: Icon(Icons.settings),\n            tooltip: t.openSettings,\n            onPressed: () {\n              Navigator.push(\n                context,\n                MaterialPageRoute(builder: (context) =&gt; Settings()),\n              );\n            },\n          )\n        ],\n      ),\n      body: ... \n    );\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-14\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_79313ce33b5c83b9c1bc6912c73e83ea\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>With this code in place, when we reload our app we should see the localized value of our tooltip in the <em>Widget Inspector<\/em>.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-13864 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-ar-tooltip.png\" alt=\"Widget Inspector tooltip | Phrase\" width=\"400\" height=\"152\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-ar-tooltip.png 400w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/04\/flutteri18n2021p1-ar-tooltip-300x114.png 300w\" sizes=\"(max-width: 400px) 100vw, 400px\" \/><\/p>\n<p>\u270b <em>Heads up \u00bb<\/em> You may often get <a href=\"https:\/\/docs.google.com\/document\/d\/10e0saTfAv32OZLRmONy866vnaw0I2jwL8zukykpgWBc\/edit#heading=h.xru0hle4tb2x\">error highlighting<\/a> in your IDE after you add new translation messages. If you\u2019ve reloaded your app, the error may be incorrect (you might be just fine). If you get a message saying that there are build errors, you can try to run the app anyway. As long as the app builds and runs, and you see your new translations, then all is probably well. To make the error go away in the IDE, trying shutting your app down entirely and starting it up again.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"interpolation-in-messages\"><\/span>Interpolation in messages<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We\u2019re on our way to translating our app. But what about interpolating dynamic runtime values in our translation messages? For example, Steve Wozniak\u2019s bio contains the product names <em>Apple I<\/em> and <em>Apple II<\/em>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-15\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-comment\">\/\/ ... <\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroList<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ ... <\/span>\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">return<\/span> Scaffold(\n      <span class=\"hljs-comment\">\/\/ ... <\/span>\n      body: HeroCard(\n        name: <span class=\"hljs-string\">'Steve Wozniak'<\/span>,\n        born: <span class=\"hljs-string\">'11 August 1950'<\/span>,\n        bio: <span class=\"hljs-string\">'Designed &amp; developed the Apple I &amp; '<\/span>\n             <span class=\"hljs-string\">'Apple II microcomputers.'<\/span>,\n        imagePath: <span class=\"hljs-string\">'assets\/images\/steve_wozniak.jpg'<\/span>,\n      ),\n      <span class=\"hljs-comment\">\/\/ ... <\/span>\n    );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-15\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_779a12e9d76017bbdefe2a9f13ee9f38\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>When we localize this message, we might want to keep <em>Apple I<\/em> and <em>Apple II<\/em> in their original English regardless of the active locale. We can use placeholders in our translation files to accomplish this.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-16\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ English<\/span>\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"wozniakBio\"<\/span>: <span class=\"hljs-string\">\"Developed the {appleOne} &amp; {appleTwo} microcomputers.\"<\/span>,\n  <span class=\"hljs-attr\">\"@wozniakBio\"<\/span>: {\n    <span class=\"hljs-attr\">\"placeholders\"<\/span>: {\n      <span class=\"hljs-attr\">\"appleOne\"<\/span>: {},\n      <span class=\"hljs-attr\">\"appleTwo\"<\/span>: {}\n    }\n  },\n  \n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ Arabic<\/span>\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"wozniakBio\"<\/span>: <span class=\"hljs-string\">\"\u0637\u0648\u0631 \u062c\u0647\u0627\u0632\u064a \u0643\u0645\u0628\u064a\u0648\u062a\u0631 {appleOne} \u0648 {appleTwo}\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-16\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_37095d089959153a6cb634dfdc96a8d8\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We use the <code>{placeholderName}<\/code> syntax to set the placeholders for our dynamic values, and we can have as many placeholders as we want in a message.<\/p>\n<p>You\u2019ve probably also noticed the <code>@wozniakBio<\/code> key in our English translations above. This entry is a companion to the <code>wozniakBio<\/code> message in the same file. Companion entries are optional for basic messages but required for messages with placeholders. In fact, we use companion entries to define message placeholders (<a href=\"https:\/\/docs.google.com\/document\/d\/10e0saTfAv32OZLRmONy866vnaw0I2jwL8zukykpgWBc\/edit#heading=h.emv7twpb8iyo\">among other things<\/a>).<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> The companion entry for a message with key <code>foo<\/code> must have a key of <code>@foo<\/code>. We only need companion entries in our default\/template translation file (English in our case).<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-17\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-string\">\"@wozniakBio\"<\/span>: { <span class=\"hljs-string\">\"placeholders\"<\/span>: { <span class=\"hljs-string\">\"appleOne\"<\/span>: {}, <span class=\"hljs-string\">\"appleTwo\"<\/span>: {} } }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-17\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_5e9c64308930d7fe4d3b7b119a499aba\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\u270b <em>Heads up \u00bb<\/em> Placeholder names must be valid Dart method parameter names.<\/p>\n<p>We could use the <code>placeholders<\/code> object to specify the type of each value, and even provide examples as documentation if we want. We can also leave the definition as an empty <code>{}<\/code>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-18\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-string\">\"@wozniakBio\"<\/span>: {\n    <span class=\"hljs-string\">\"placeholders\"<\/span>: {\n      <span class=\"hljs-string\">\"appleOne\"<\/span>: {\n        <span class=\"hljs-comment\">\/\/ Explicit type<\/span>\n        <span class=\"hljs-string\">\"type\"<\/span>: <span class=\"hljs-string\">\"String\"<\/span>,\n        <span class=\"hljs-comment\">\/\/ A little doc<\/span>\n        <span class=\"hljs-string\">\"example\"<\/span>: <span class=\"hljs-string\">\"Apple I\"<\/span>\n      },\n      <span class=\"hljs-comment\">\/\/ It's perfectly ok to just specifiy the name<\/span>\n      <span class=\"hljs-string\">\"appleTwo\"<\/span>: {}\n    }\n  }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-18\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b75905469d543b0ab944f34e048a1500\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>The <code>type<\/code> is used in the method Flutter will generate on <code>AppLocalizations<\/code> for our <code>wozniakBio<\/code> message.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-19\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-comment\">\/\/ ... <\/span>\n\nabstract <span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">AppLocalizations<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ ... <\/span>\n\n  <span class=\"hljs-comment\">\/\/ This method is implemented in app_localizations_en.dart<\/span>\n  <span class=\"hljs-comment\">\/\/ and app_localizations_ar.dart.<\/span>\n  <span class=\"hljs-comment\">\/\/ Explicit type for appleOne parameter. Implicit appleTwo<\/span>\n  <span class=\"hljs-comment\">\/\/ parameter.<\/span>\n  <span class=\"hljs-built_in\">String<\/span> wozniakBio(<span class=\"hljs-built_in\">String<\/span> appleOne, <span class=\"hljs-built_in\">Object<\/span> appleTwo);\n\n  <span class=\"hljs-comment\">\/\/ ... <\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-19\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_6edd419b99ddb1be5eca2385bbad3811\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\uddd2\ufe0f<em> Note \u00bb<\/em> It\u2019s perfectly OK for most cases to use empty <code>{}<\/code> for placeholder definitions. An empty definition will cause the parameter to be of type <code>Object<\/code>. Under the hood Flutter will just use the <code>theParameter.toString()<\/code> value of the given parameter, so an <code>Object<\/code> param will work just fine. An implicit <code>Object<\/code> placeholder also keeps our messages flexible to take any type, since all Dart types derive from <a href=\"https:\/\/api.dart.dev\/stable\/2.12.4\/dart-core\/Object-class.html\">Object<\/a> and have a <code>toString()<\/code>.<\/p>\n<p>OK, after rerunning the app to update <code>AppLocalizations<\/code>, we can localize the Woz\u2019s bio message with the new method.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-20\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-comment\">\/\/ ... <\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroList<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ ... <\/span>\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">var<\/span> t = AppLocalizations.of(context);\n\n    <span class=\"hljs-keyword\">return<\/span> Scaffold(\n      <span class=\"hljs-comment\">\/\/ ... <\/span>\n      body: HeroCard(\n        name: <span class=\"hljs-string\">'Steve Wozniak'<\/span>,\n        born: <span class=\"hljs-string\">'11 August 1950'<\/span>,\n        bio: t.wozniakBio(<span class=\"hljs-string\">'Apple I'<\/span>, <span class=\"hljs-string\">'Apple II'<\/span>),\n        imagePath: <span class=\"hljs-string\">'assets\/images\/steve_wozniak.jpg'<\/span>,\n      ),\n      <span class=\"hljs-comment\">\/\/ ... <\/span>\n    );\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-20\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_2a62868300147f1b746695a4d8229a4b\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>With that in place, we know we can never be sued by any fruit-flavored companies for misrepresenting their products in <em>any<\/em> language.<\/p>\n<figure id=\"attachment_14105\" aria-describedby=\"caption-attachment-14105\" style=\"width: 550px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-14105\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-woz-bio-ar-en-1024x702.png\" alt=\"Interpolated values in Arabic and English | Phrase\" width=\"550\" height=\"377\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-woz-bio-ar-en-1024x702.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-woz-bio-ar-en-300x206.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-woz-bio-ar-en-768x527.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-woz-bio-ar-en.png 1070w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><figcaption id=\"caption-attachment-14105\" class=\"wp-caption-text\">Interpolated values in Arabic and English<\/figcaption><\/figure>\n<h2><span class=\"ez-toc-section\" id=\"plurals\"><\/span>Plurals<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We often need to handle dynamic plurals in our localization. \u201cYou have received <em>one message<\/em>\u201d or \u201cYou have received <em>3 messages<\/em>\u201d, for example.<\/p>\n<p>It\u2019s important to note that <a href=\"https:\/\/unicode-org.github.io\/cldr-staging\/charts\/latest\/supplemental\/language_plural_rules.html\">different languages handle plurals differently<\/a>. For example, English has two plural forms: one and other (other == zero and &gt;1). Arabic has six plural forms. This can be a bit of a headache when localizing using libraries that don\u2019t support complex plural rules. Luckily, Flutter\u2019s first-party i18n solution handles complex plurals out of the box, so it has us covered. Let\u2019s use it to localize the hero counter in our app.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-14106\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-hero-count-before-l10n-1024x616.png\" alt=\"Heroes of Computer Science home screen English pluralization | Phrase\" width=\"550\" height=\"331\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-hero-count-before-l10n-1024x616.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-hero-count-before-l10n-300x180.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-hero-count-before-l10n-768x462.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-hero-count-before-l10n.png 1169w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-21\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroList<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">return<\/span> Scaffold(\n      <span class=\"hljs-comment\">\/\/ ...<\/span>\n      body: Padding(\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n        child: Column(\n          children: &#91;\n            Padding(\n              padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.only(bottom: <span class=\"hljs-number\">8.0<\/span>),\n              <span class=\"hljs-comment\">\/\/ Localized string should replace '6 Heroes'<\/span>\n              child: Text(<span class=\"hljs-string\">'6 Heroes'<\/span>), \n            ),\n            <span class=\"hljs-comment\">\/\/ ...<\/span>\n          ],\n        ),\n      ),\n    );\n  }\n}\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-21\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_d56dca454317452d4e8822efd7e7fbca\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>First, let\u2019s add the message to our template English ARB file.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-22\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"heroCount\"<\/span>: <span class=\"hljs-string\">\"{count,plural, =0{No heroes yet} =1{1 hero} other{{count} heroes}}\"<\/span>,\n  <span class=\"hljs-attr\">\"@heroCount\"<\/span>: {\n    <span class=\"hljs-attr\">\"placeholders\"<\/span>: {\n      <span class=\"hljs-attr\">\"count\"<\/span>: {}\n    }\n  },\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-22\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_96368f886556b5b79388a7a73f8c0313\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We specify a <code>count<\/code> placeholder in our message, and use it with the special <code>{count,plural,...}<\/code> syntax to define the different plural forms.<\/p>\n<p>\u270b <em>Heads up \u00bb<\/em> The <code>count<\/code> param will always be an <code>int<\/code> type. If you specify another type for <code>count<\/code>, Flutter will ignore it and use <code>int<\/code> anyway.<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> You can add placeholders other than <code>count<\/code> to a plural message; they\u2019re specified as usual (see <em>Interpolation<\/em> above).<\/p>\n<p>Flutter supports the following plural forms.<\/p>\n<ul>\n<li>zero \u279e <code>=0{No heroes}<\/code><\/li>\n<li>one \u279e <code>=1{One hero}<\/code><\/li>\n<li>two \u279e <code>=2(Two heroes}<\/code><\/li>\n<li>few \u279e <code>few{The {count} heroes}<\/code><\/li>\n<li>many \u279e <code>many{{count} heroes}<\/code><\/li>\n<li>other \u279e <code>other{{count} heroes}<\/code><\/li>\n<\/ul>\n<p><code>few<\/code>, <code>many<\/code>, and <code>other<\/code> <a href=\"https:\/\/unicode-org.github.io\/cldr-staging\/charts\/latest\/supplemental\/language_plural_rules.html\">have different meanings depending on the active language<\/a>. The only required form in <em>any<\/em> language is the <code>other<\/code> form.<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> We didn\u2019t need to use the zero <code>=0<\/code> form in our English message above. If we had omitted it, Flutter would have used our <code>other<\/code> form instead.<\/p>\n<p>Alright, let\u2019s add our Arabic message. As we mentioned earlier, Arabic has six plural forms.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-23\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\">{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"heroCount\"<\/span>: <span class=\"hljs-string\">\"{count,plural, =0{\u0644\u0627 \u062a\u0648\u062c\u062f \u0623\u0628\u0637\u0627\u0644 \u0628\u0639\u062f} =1{\u0628\u0637\u0644 \u0648\u0627\u062d\u062f} =2{\u0628\u0637\u0644\u0627\u0646} few{{count} \u0623\u0628\u0637\u0627\u0644} many{{count} \u0628\u0637\u0644} other{{count} \u0628\u0637\u0644}}\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-23\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_61f4c23cd1f0df4ed39e9c0c4b2bdd25\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Now let\u2019s wire it all up and use our new message in our <code>HeroList<\/code> widget.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-24\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-comment\">\/\/ ...<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroList<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">var<\/span> t = AppLocalizations.of(context);\n    <span class=\"hljs-keyword\">return<\/span> Scaffold(\n      <span class=\"hljs-comment\">\/\/ ...<\/span>\n      body: Padding(\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n        child: Column(\n          children: &#91;\n            Padding(\n              padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.only(bottom: <span class=\"hljs-number\">8.0<\/span>),\n              child: Text(t.heroCount(<span class=\"hljs-number\">6<\/span>)),\n            ),\n            <span class=\"hljs-comment\">\/\/ ...<\/span>\n          ],\n        ),\n      ),\n    );\n  }\n}\n\n<span class=\"hljs-comment\">\/\/ ...<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-24\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_1246c17d9c80e9901f400127c6fed485\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Of course, in a production app the count parameter passed to <code>t.heroCount()<\/code> would be dynamic. Flutter chooses the correct plural form from our message depending on the active locale.<\/p>\n<figure id=\"attachment_14107\" aria-describedby=\"caption-attachment-14107\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-14107\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-en-plural-forms-1024x379.png\" alt=\"English plural forms in our app | Phrase\" width=\"500\" height=\"185\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-en-plural-forms-1024x379.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-en-plural-forms-300x111.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-en-plural-forms-768x284.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-en-plural-forms.png 1485w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/><figcaption id=\"caption-attachment-14107\" class=\"wp-caption-text\">English plural forms in our app<\/figcaption><\/figure>\n<figure id=\"attachment_14108\" aria-describedby=\"caption-attachment-14108\" style=\"width: 500px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-14108\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-ar-plural-forms-1024x716.png\" alt=\"Arabic plural forms in our app | Phrase\" width=\"500\" height=\"350\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-ar-plural-forms-1024x716.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-ar-plural-forms-300x210.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-ar-plural-forms-768x537.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-ar-plural-forms.png 1485w\" sizes=\"(max-width: 500px) 100vw, 500px\" \/><figcaption id=\"caption-attachment-14108\" class=\"wp-caption-text\">Arabic plural forms in our app<\/figcaption><\/figure>\n<h2><span class=\"ez-toc-section\" id=\"number-formatting\"><\/span>Number formatting<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We can format numbers in our localized messages using our friend the <code>placeholders<\/code> object in the companion entries of our template ARB file (English in our case). There&#8217;s no great place to put number formatting in our little demo app, so we&#8217;ll just pretend we have an e-commerce app to demonstrate.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-25\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ app_en.arb in example app that has a shopping cart <\/span>\n{\n  <span class=\"hljs-attr\">\"itemTotal\"<\/span>: <span class=\"hljs-string\">\"Your total is: {value}\"<\/span>,\n  <span class=\"hljs-attr\">\"@itemTotal\"<\/span>: {\n    <span class=\"hljs-attr\">\"placeholders\"<\/span>: {\n      <span class=\"hljs-attr\">\"value\"<\/span>: {\n        <span class=\"hljs-attr\">\"type\"<\/span>: <span class=\"hljs-string\">\"double\"<\/span>,\n        <span class=\"hljs-attr\">\"format\"<\/span>: <span class=\"hljs-string\">\"currency\"<\/span>\n      }\n    }\n  }\n}\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-25\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_64daa18ab8b05413e8b5082c78b7970f\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Notice that we specified an explicit <code>type<\/code> and <code>format<\/code> to control how the number will be displayed. As per usual, we can translate our message in our other locale files.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-26\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ app_ar.arb in example app that has a shopping cart<\/span>\n{ <span class=\"hljs-attr\">\"itemTotal\"<\/span>: <span class=\"hljs-string\">\"\u0625\u062c\u0645\u0627\u0644\u064a: {value}\"<\/span> }<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-26\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_d601e74c637947ef0502ab4cd5bb691a\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>After we reload our app, we can use our message as usual.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-27\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-comment\">\/\/ In a Widget<\/span>\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_gen\/gen_l10n\/app_localizations.dart'<\/span>;\n\n<span class=\"hljs-comment\">\/\/ In some widget builder with a context<\/span>\n<span class=\"hljs-keyword\">var<\/span> t = AppLocalizations.of(context);\n<span class=\"hljs-keyword\">var<\/span> message = t.itemTotal(<span class=\"hljs-number\">56.12<\/span>);\n<span class=\"hljs-comment\">\/\/ =&gt; \"Your total is USD56.12\" when current locale is English<\/span>\n<span class=\"hljs-comment\">\/\/ =&gt; \"\u0625\u062c\u0645\u0627\u0644\u064a: EGP56.12\" when current locale is Arabic<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-27\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_b5edca951b1c6a93afcc5a4b7e2cdd6d\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\u270b <em>Heads up \u00bb<\/em> You can\u2019t override number <code>format<\/code>s per locale. The format you specify in your template locale (English in our case) will be used across locales regardless of any <code>format<\/code> override you specify in your other locale files.<\/p>\n<p>Remember that underneath the hood Flutter is using the <a href=\"https:\/\/pub.dev\/packages\/intl\">Dart intl<\/a> library for most of its i18n work. The currency format we used above is one of several formats built into the intl number formatter. Other formats include decimals, percentages, and more.<\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> Check out the <a href=\"https:\/\/docs.google.com\/document\/d\/10e0saTfAv32OZLRmONy866vnaw0I2jwL8zukykpgWBc\/edit#heading=h.4jrxhcva98zs\">official user guide<\/a> for all the available formats.`<\/p>\n<p>However, we don\u2019t have to rely on Flutter to pass our numbers to intl. We can use intl directly to gain more control over our number formatting.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-28\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_gen\/gen_l10n\/app_localizations.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:intl\/intl.dart'<\/span>;\n\n<span class=\"hljs-comment\">\/\/ In some Widget builder with a context<\/span>\n<span class=\"hljs-keyword\">var<\/span> currentLocale = AppLocalizations.of(context).localeName;\n<span class=\"hljs-keyword\">var<\/span> compact = NumberFormat.compact(locale: currentLocale).format(<span class=\"hljs-number\">6000000<\/span>);\n<span class=\"hljs-comment\">\/\/ =&gt; \"6M\" when current locale is US English<\/span>\n<span class=\"hljs-comment\">\/\/ =&gt; \"\u0666 \u0645\u0644\u064a\u0648\u0646\" when current locale is Egyptian Arabic<\/span>\n\n<span class=\"hljs-keyword\">var<\/span> simpleCurrency = NumberFormat.simpleCurrency(locale: currentLocale).format(<span class=\"hljs-number\">14.24<\/span>);\n<span class=\"hljs-comment\">\/\/ =&gt; \"$14.24\" when current locale is US English<\/span>\n<span class=\"hljs-comment\">\/\/ =&gt; \"\u062c.\u0645.\u200f \u0661\u0664\u066b\u0662\u0664\" when current locale is Egyptian Arabic<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-28\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_ac7df51dde24f46a4d2cd826a6e573c8\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>\ud83d\udd17 <em>Resource \u00bb<\/em> You don\u2019t need to use the predefined formats like <code>compact<\/code> and <code>simpleCurrency<\/code>. The intl <code>NumberFormat<\/code> constructor gives you granular control over your number formats. Read all about it on <a href=\"https:\/\/pub.dev\/documentation\/intl\/latest\/intl\/NumberFormat-class.html\">the official documentation<\/a>.<\/p>\n<p>\u270b <em>Heads up \u00bb<\/em> The only way I could get Eastern Arabic numerals (\u0661\u060c\u0662\u060c\u0663\u2026) rendering for Arabic is by setting the <code>locale<\/code> param to <code>\"ar_EG\"<\/code> (Egyptian Arabic). Neither <code>\"ar\"<\/code> or any <code>\"ar_XX\"<\/code> variant other than Egyptian worked for me.<\/p>\n<p>\u270b <em>Heads up \u00bb<\/em> Formats didn\u2019t work with the plural <code>count<\/code> variable for me. It seems that Flutter is overriding the format when it processes plurals. If you are able get formats in your plurals, please let us know how you did it in the comments below.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"date-formatting\"><\/span>Date Formatting<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Our heroes currently have hard-coded birth dates that aren\u2019t localized, which isn&#8217;t too cool.<\/p>\n<figure id=\"attachment_14109\" aria-describedby=\"caption-attachment-14109\" style=\"width: 550px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-14109\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-unlocalized-date-1024x332.png\" alt=\"English date format in Arabic localization | Phrase\" width=\"550\" height=\"178\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-unlocalized-date-1024x332.png 1024w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-unlocalized-date-300x97.png 300w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-unlocalized-date-768x249.png 768w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-unlocalized-date.png 1115w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><figcaption id=\"caption-attachment-14109\" class=\"wp-caption-text\">We want this date localized in Arabic<\/figcaption><\/figure>\n<p>Recall that we\u2019re rendering each of our heroes with a <code>HeroCard<\/code> widget.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-29\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter\/material.dart'<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroCard<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> name;\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> born;\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> bio;\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> imagePath;\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n  <span class=\"hljs-keyword\">const<\/span> HeroCard({\n    Key key,\n    <span class=\"hljs-keyword\">this<\/span>.name = <span class=\"hljs-string\">''<\/span>,\n    <span class=\"hljs-keyword\">this<\/span>.born = <span class=\"hljs-string\">''<\/span>,\n    <span class=\"hljs-keyword\">this<\/span>.bio = <span class=\"hljs-string\">''<\/span>,\n    <span class=\"hljs-keyword\">this<\/span>.imagePath,\n  }) : <span class=\"hljs-keyword\">super<\/span>(key: key);\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    <span class=\"hljs-keyword\">return<\/span> Card(\n      child: Padding(\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n        Padding(\n          padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.only(top: <span class=\"hljs-number\">2<\/span>, bottom: <span class=\"hljs-number\">4<\/span>),\n          child: Text(\n            born.isEmpty ? <span class=\"hljs-string\">''<\/span> : <span class=\"hljs-string\">'Born <span class=\"hljs-subst\">$born<\/span>'<\/span>,\n            <span class=\"hljs-comment\">\/\/ ...<\/span>\n          ),\n        ),\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n      ),\n    );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-29\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_6348dea686caab570fabd34bb946dfc3\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>To format the <code>born<\/code> date for each locale our app supports, we first add some new localized messages with interpolated date values.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-30\" data-shcb-language-name=\"JSON \/ JSON with Comments\" data-shcb-language-slug=\"json\"><span><code class=\"hljs language-json\"><span class=\"hljs-comment\">\/\/ English<\/span>\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"heroBorn\"<\/span>: <span class=\"hljs-string\">\"Born {date}\"<\/span>,\n  <span class=\"hljs-attr\">\"@heroBorn\"<\/span>: {\n    <span class=\"hljs-attr\">\"placeholders\"<\/span>: {\n      <span class=\"hljs-attr\">\"date\"<\/span>: {\n        <span class=\"hljs-attr\">\"type\"<\/span>: <span class=\"hljs-string\">\"DateTime\"<\/span>,\n        <span class=\"hljs-attr\">\"format\"<\/span>: <span class=\"hljs-string\">\"yMMMd\"<\/span>\n      }\n    }\n  },\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}\n\n<span class=\"hljs-comment\">\/\/ Arabic<\/span>\n{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-attr\">\"heroBorn\"<\/span>: <span class=\"hljs-string\">\"\u062a\u0627\u0631\u064a\u062e \u0627\u0644\u0645\u064a\u0644\u0627\u062f {date}\"<\/span>,\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-30\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JSON \/ JSON with Comments<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">json<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_7914c46e3886e12a28e57d7b7ee1fcc5\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>When we define our <code>date<\/code> placeholder in our template localization file, we need to give it the <code>DateTime<\/code> type. We can then use a <code>format<\/code> to specify how we want to display the date. The format <code>yMMMd<\/code> we defined above stands for \u201cyear, abbreviated month, day\u201d, which in US English would render to something like \u201cDec 9, 1906\u201d.<\/p>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> In fact, underneath the hood Flutter is just using intl\u2019s <code>DateFormat<\/code> class, generating code like <code>DateFormat.yMMMd(localeName).format(date)<\/code>. The <code>yMMMd<\/code> named constructor is a handy shortcut called a \u201cskeleton\u201d, and there are quite a few we can use. Check them out in <a href=\"https:\/\/pub.dev\/documentation\/intl\/latest\/intl\/DateFormat-class.html\">the official DateFormat documentation<\/a>.<\/p>\n<p>\ud83d\uddd2\ufe0f\u00a0<em>Note \u00bb<\/em> We didn\u2019t have to call our placeholder variable <code>date<\/code>. We could have given it any name, as long as it was a valid Dart function parameter name.<\/p>\n<p>Alright, let\u2019s wire this up in our widget to get our new messages displayed.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-31\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter\/material.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:intl\/intl.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_gen\/gen_l10n\/app_localizations.dart'<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroCard<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> name;\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> born;\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> bio;\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">String<\/span> imagePath;\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-keyword\">final<\/span> <span class=\"hljs-built_in\">DateTime<\/span> bornDateTime;\n\n  HeroCard({\n    Key key,\n    <span class=\"hljs-keyword\">this<\/span>.name = <span class=\"hljs-string\">''<\/span>,\n    <span class=\"hljs-keyword\">this<\/span>.born = <span class=\"hljs-string\">''<\/span>,\n    <span class=\"hljs-keyword\">this<\/span>.bio = <span class=\"hljs-string\">''<\/span>,\n    <span class=\"hljs-keyword\">this<\/span>.imagePath,\n  }) : bornDateTime = DateFormat(<span class=\"hljs-string\">'d MMMM yyyy'<\/span>).parse(born),\n       <span class=\"hljs-keyword\">super<\/span>(key: key);\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    <span class=\"hljs-keyword\">var<\/span> t = AppLocalizations.of(context);\n    <span class=\"hljs-keyword\">return<\/span> Card(\n      child: Padding(\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n        Padding(\n          padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.only(top: <span class=\"hljs-number\">2<\/span>, bottom: <span class=\"hljs-number\">4<\/span>),\n          child: Text(\n            born.isEmpty ? <span class=\"hljs-string\">''<\/span> : t.heroBorn(bornDateTime),\n            <span class=\"hljs-comment\">\/\/ ...<\/span>\n          ),\n        ),\n        <span class=\"hljs-comment\">\/\/ ...<\/span>\n      ),\n    );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-31\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_f1b6f274f669e0a2455f2cff0458143c\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>We\u2019re handed a hero&#8217;s birthdate as a <code>String<\/code>, so we need to parse it to a <code>DateTime<\/code> first. We use intl\u2019s <code>DateFormat<\/code> class to do this in our widget constructor.<\/p>\n<p>In the <code>build<\/code> method, we simply pass the parsed <code>DateTime<\/code> to our <code>t.heroBorn()<\/code> localized message. This gives us nicely localized dates.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-14121\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-en-date-1.png\" alt=\"English version of Alan Turning card | Phrase\" width=\"550\" height=\"178\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-en-date-1.png 583w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-en-date-1-300x97.png 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-14111\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-ar-date.png\" alt=\"Arabic version of Alan Turing card | Phrase\" width=\"550\" height=\"201\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-ar-date.png 583w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-ar-date-300x110.png 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><\/p>\n<p>What if we don\u2019t want to use any of the predefined skeletons, and to fully customize our date formats? Well, much like number formatting (see above), we would need to use the <code>intl.DateFormat<\/code> class directly. Suppose we wanted to display our hero birthdates in a format like \u201c1912-06-23\u201d. We would do something like the following.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-32\" data-shcb-language-name=\"JavaScript\" data-shcb-language-slug=\"javascript\"><span><code class=\"hljs language-javascript\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:intl\/intl.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_gen\/gen_l10n\/app_localizations.dart'<\/span>;\n\n<span class=\"hljs-comment\">\/\/ In widget builder with context<\/span>\n<span class=\"hljs-keyword\">var<\/span> t = AppLocalizations.of(context);\n<span class=\"hljs-keyword\">var<\/span> bornDateTime = DateTime(<span class=\"hljs-number\">1912<\/span>, <span class=\"hljs-number\">6<\/span>, <span class=\"hljs-number\">23<\/span>);\n<span class=\"hljs-keyword\">var<\/span> formattedBorn = DateFormat(<span class=\"hljs-string\">'yyyy-MM-dd'<\/span>, t.localeName).format(bornDateTime);\n<span class=\"hljs-keyword\">var<\/span> message = t.heroBorn(formattedBorn);\n<span class=\"hljs-comment\">\/\/ =&gt; \"1912-06-23\" in US English<\/span>\n<span class=\"hljs-comment\">\/\/ =&gt; \"\u0661\u0669\u0661\u0662-\u0660\u0666-\u0662\u0663\" in Egyptian Arabic<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-32\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">JavaScript<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">javascript<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_4b9ae62cd4c4772bfa48056dd89fa015\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Our localized <code>heroBorn<\/code> messages in our ARB files would then just take regular <code>Object<\/code> or <code>String<\/code> params, since we\u2019ve already done the formatting for them.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"directionality-left-to-right-and-right-to-left\"><\/span>Directionality: left-to-right and right-to-left<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>While English is a left-to-right (LTR) language, Arabic goes the other way and is laid out right-to-left (RTL). This is currently causing a problem for us when our app is used on a device with Arabic as the system language.<\/p>\n<figure id=\"attachment_14114\" aria-describedby=\"caption-attachment-14114\" style=\"width: 550px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-14114\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-broken-ar.png\" alt=\"Arabic version of Heroes of Computer Science with lacking padding | Phrase\" width=\"550\" height=\"520\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-broken-ar.png 662w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-broken-ar-300x284.png 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><figcaption id=\"caption-attachment-14114\" class=\"wp-caption-text\">Note the lack of padding between images and text<\/figcaption><\/figure>\n<p>The image and the text in each card are flush because we\u2019re using <code>EdgeInsets.only(right)<\/code> to define the padding around our image.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-33\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:intl\/intl.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_gen\/gen_l10n\/app_localizations.dart'<\/span>;\n\n<span class=\"hljs-comment\">\/\/ In widget builder with context<\/span>\n<span class=\"hljs-keyword\">var<\/span> t = AppLocalizations.of(context);\n<span class=\"hljs-keyword\">var<\/span> bornDateTime = <span class=\"hljs-built_in\">DateTime<\/span>(<span class=\"hljs-number\">1912<\/span>, <span class=\"hljs-number\">6<\/span>, <span class=\"hljs-number\">23<\/span>);\n<span class=\"hljs-keyword\">var<\/span> formattedBorn = DateFormat(<span class=\"hljs-string\">'yyyy-MM-dd'<\/span>, t.localeName).format(bornDateTime);\n<span class=\"hljs-keyword\">var<\/span> message = t.heroBorn(formattedBorn);\n<span class=\"hljs-comment\">\/\/ =&gt; \"1912-06-23\" in US English<\/span>\n<span class=\"hljs-comment\">\/\/ =&gt; \"\u0661\u0669\u0661\u0662-\u0660\u0666-\u0662\u0663\" in Egyptian Arabic<\/span>\n<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-33\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_2200e3101de33781799d7c17c3a8e956\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>This works in LTR languages, where we want a right margin between the image and the text. In RTL languages, however, we want the margin on the <em>left<\/em>.<\/p>\n<p>An easy remedy here is to use <code>EdgeInsetsDirectional<\/code> instead of <code>EdgeInsets<\/code>.<\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-34\" data-shcb-language-name=\"PHP\" data-shcb-language-slug=\"php\"><span><code class=\"hljs language-php\"><span class=\"hljs-comment\">\/\/...<\/span>\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroCard<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n\n  @override\n  Widget build(BuildContext context) {\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    <span class=\"hljs-keyword\">return<\/span> Card(\n      child: Padding(\n        padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.all(<span class=\"hljs-number\">4.0<\/span>),\n        child: Row(\n          crossAxisAlignment: CrossAxisAlignment.start,\n          children: &lt;Widget&gt;&#91;\n            Padding(\n              padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsetsDirectional.only(end: <span class=\"hljs-number\">8.0<\/span>),\n              child: ClipRRect(\n                <span class=\"hljs-comment\">\/\/ Portrait image...<\/span>\n              ),\n            ),\n            Expanded(\n              <span class=\"hljs-comment\">\/\/ Text widgets...<\/span>\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-34\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">PHP<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">php<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_fb82a3ce2c11e669dcb8f28aa7990e74\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Notice that we use <code>end<\/code> instead of <code>right<\/code> to set the padding between the image and the text. <code>EdgeInsetsDirectional<\/code> is one of a few layout Flutter widgets that are locale-direction-aware. These widgets take <code>start<\/code> and <code>end<\/code> parameters instead of <code>left<\/code> and <code>right<\/code>. And the cool thing is that these directional widgets will do the correct thing automatically for the active locale:<\/p>\n<ul>\n<li><code>start<\/code> == <code>left<\/code> for LTR languages<\/li>\n<li><code>start<\/code> == <code>right<\/code> for RTL languages<\/li>\n<li><code>end<\/code> == <code>right<\/code> for LTR languages<\/li>\n<li><code>end<\/code> == <code>left<\/code> for RTL languages<\/li>\n<\/ul>\n<p>With this small tweak to the code, our layout issue is resolved.<\/p>\n<figure id=\"attachment_14115\" aria-describedby=\"caption-attachment-14115\" style=\"width: 550px\" class=\"wp-caption aligncenter\"><img loading=\"lazy\" decoding=\"async\" class=\"wp-image-14115\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-fixed-ar.png\" alt=\"Arabic version of Heroes of Computer Science with padding | Phrase\" width=\"550\" height=\"517\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-fixed-ar.png 664w, https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-fixed-ar-300x282.png 300w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><figcaption id=\"caption-attachment-14115\" class=\"wp-caption-text\">The layout now adapts to the active locale\u2019s direction<\/figcaption><\/figure>\n<p>\ud83d\udd17 <em>Resource \u00bb<\/em> At the time of writing, the official Flutter documentation lists the following directional widgets:<\/p>\n<ul>\n<li><a href=\"https:\/\/api.flutter.dev\/flutter\/painting\/EdgeInsetsDirectional-class.html\">EdgeInsetsDirectional<\/a><\/li>\n<li><a href=\"https:\/\/api.flutter.dev\/flutter\/painting\/AlignmentDirectional-class.html\">AlignmentDirectional<\/a><\/li>\n<li><a href=\"https:\/\/api.flutter.dev\/flutter\/painting\/BorderDirectional-class.html\">BorderDirectional<\/a><\/li>\n<li><a href=\"https:\/\/api.flutter.dev\/flutter\/painting\/BorderRadiusDirectional-class.html\">BorderRadiusDirectional<\/a><\/li>\n<li><a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/PositionedDirectional-class.html\">PositionedDirectional<\/a><\/li>\n<li><a href=\"https:\/\/api.flutter.dev\/flutter\/widgets\/AnimatedPositionedDirectional-class.html\">AnimatedPositionedDirectional<\/a><\/li>\n<\/ul>\n<p>With all that in place, our final app looks all globalized-like.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-14142\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2021\/05\/flutteri18n2021p2-final-en-ar-1-1024x887.png\" alt=\"Arabic and English version of Heroes of Computer Science side-by-side | Phrase\" width=\"800\" height=\"693\" \/><\/p>\n<p>\ud83d\udd17 Resource \u00bb Get the complete code for our demo app from <a href=\"https:\/\/github.com\/PhraseApp-Blog\/flutter-2-i18n-2021\">our GitHub repo<\/a>.<\/p>\n<h2><span class=\"ez-toc-section\" id=\"adding-localized-assets\"><\/span>Adding localized assets<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Localizing images in Flutter involves using different sets of images for different locales or languages in your app. This is commonly done to display images with text or content that matches the user&#8217;s preferred language.<\/p>\n<p>Let\u2019s add a flag, which displays differently depending on the user\u2019s region.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-68808\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/flutter-l10n-flag-language.png\" alt=\"Country flag added to a localized Flutter app | Phrase\" width=\"550\" height=\"764\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/flutter-l10n-flag-language.png 618w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/flutter-l10n-flag-language-216x300.png 216w\" sizes=\"(max-width: 550px) 100vw, 550px\" \/><\/p>\n<p>We\u2019ll start by adding the images, one for each locale we plan to support. We should organize these images in a folder structure based on locales, for example:<!-- notionvc: fc5508b4-43d5-43ba-a914-18ef9baa152d --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-35\" data-shcb-language-name=\"plaintext\" data-shcb-language-slug=\"plaintext\"><span><code class=\"hljs language-plaintext\">\u2514\u2500\u2500 assets\/\n    \u2514\u2500\u2500 images\/\n        \u251c\u2500\u2500 eg\/\n        \u2502   \u2514\u2500\u2500 flag.jpg\n        \u251c\u2500\u2500 us\/\n        \u2502   \u2514\u2500\u2500 flag.jpg\n        \u2514\u2500\u2500 flag.jpg<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-35\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">plaintext<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">plaintext<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_8f58988268d0099619747deab44cdc39\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>For this tutorial we will add flag images for Egypt and the US. The file directly in the images folder (with no locale) is a fallback image for other regions.<\/p>\n<p>Next, let\u2019s register the files in <code>pubspec.yml<\/code> file.<\/p>\n<p><!-- notionvc: 8d2588ec-6a65-4ecd-a48b-892e51f0c4be --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-36\" data-shcb-language-name=\"YAML\" data-shcb-language-slug=\"yaml\"><span><code class=\"hljs language-yaml\"><span class=\"hljs-comment\"># pubspec.yml<\/span>\n\n<span class=\"hljs-comment\"># ...<\/span>\n\n<span class=\"hljs-attr\">flutter:<\/span>\n  <span class=\"hljs-comment\"># ...<\/span>\n  <span class=\"hljs-attr\">assets:<\/span>\n  <span class=\"hljs-comment\"># ...<\/span>\n    <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-string\">assets\/images\/in\/flag.jpg<\/span>\n    <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-string\">assets\/images\/us\/flag.jpg<\/span>\n    <span class=\"hljs-bullet\">-<\/span> <span class=\"hljs-string\">assets\/images\/flag.jpg<\/span>\n\n<span class=\"hljs-comment\"># ...<\/span><\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-36\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">YAML<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">yaml<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_c5d62d30c41aaffc2b39a48344b15b9c\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Now we can load the images in our <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">hero_list.dart<\/span><\/code> file:<!-- notionvc: 8a12d860-6906-4664-bced-66bf768073ee --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-37\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\">\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter\/material.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_gen\/gen_l10n\/app_localizations.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'..\/widgets\/hero_card.dart'<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroList<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-comment\">\/\/ ...<\/span>\n    <span class=\"hljs-built_in\">String<\/span> getImagePath(<span class=\"hljs-built_in\">String<\/span> imageName) {\n      <span class=\"hljs-built_in\">String<\/span> basePath = <span class=\"hljs-string\">'assets\/images\/'<\/span>;\n\n      <span class=\"hljs-comment\">\/\/ When country code isn't supported, we<\/span>\n      <span class=\"hljs-comment\">\/\/ display fallback image.<\/span>\n      <span class=\"hljs-keyword\">if<\/span> (locale.countryCode?.isEmpty == <span class=\"hljs-keyword\">true<\/span>) {\n        <span class=\"hljs-keyword\">return<\/span>  basePath + <span class=\"hljs-string\">'flag.jpg'<\/span>;\n      }\n\n      <span class=\"hljs-built_in\">String<\/span> localePath = <span class=\"hljs-string\">'<span class=\"hljs-subst\">${locale.countryCode!.toLowerCase()}<\/span>\/'<\/span>;\n      <span class=\"hljs-keyword\">return<\/span> basePath + localePath + imageName;\n    }\n\n    <span class=\"hljs-keyword\">return<\/span> Scaffold(\n      <span class=\"hljs-comment\">\/\/ ...<\/span>\n      body: Padding(\n        padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.all(<span class=\"hljs-number\">16<\/span>),\n        child: Column(\n          children: &#91;\n\t    <span class=\"hljs-comment\">\/\/ ...<\/span>\n            Padding(\n              padding: <span class=\"hljs-keyword\">const<\/span> EdgeInsets.only(bottom: <span class=\"hljs-number\">2.0<\/span>),\n              child: Image.asset(\n                getImagePath(<span class=\"hljs-string\">'flag.jpg'<\/span>),\n                width: <span class=\"hljs-number\">40<\/span>,\n                height: <span class=\"hljs-number\">40<\/span>,\n              ),\n            ),\n          ],\n        ),\n      ),\n    );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-37\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_3bc0686236dc975cc5c7f29124235df6\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Now in the <code><span class=\"notion-enable-hover\" spellcheck=\"false\" data-token-index=\"1\">main.dart<\/span><\/code> file make the following changes<!-- notionvc: bb9617c5-f2af-4173-becf-6ef08ae5d950 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-38\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\">...\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyApp<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> MyApp({<span class=\"hljs-keyword\">super<\/span>.key});\n\n  <span class=\"hljs-comment\">\/\/ This widget is the root of your application.<\/span>\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">return<\/span> MaterialApp(\n    ...    \n    supportedLocales: &#91;\n        <span class=\"hljs-keyword\">const<\/span> Locale(<span class=\"hljs-string\">'ar'<\/span>, <span class=\"hljs-string\">''<\/span>),\n        <span class=\"hljs-keyword\">const<\/span> Locale(<span class=\"hljs-string\">'en'<\/span>, <span class=\"hljs-string\">''<\/span>),\n\t\t\t\t<span class=\"hljs-comment\">\/\/ Add the supported region codes <\/span>\n        <span class=\"hljs-keyword\">const<\/span> Locale(<span class=\"hljs-string\">'ar'<\/span>, <span class=\"hljs-string\">'EG'<\/span>),\n        <span class=\"hljs-keyword\">const<\/span> Locale(<span class=\"hljs-string\">'en'<\/span>, <span class=\"hljs-string\">'US'<\/span>),\n      ],\n   ...\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-38\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_2620edbdd15f4b76871204488c48a8b2\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Now the app shows the appropriate asset for the user\u2019s locale and shows a fallback if their locale is not supported. <!-- notionvc: 11d89077-09d6-4de7-9bed-5fc1c4306970 --><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"aligncenter wp-image-69078 size-full\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/computer-science-hero-demo-app-screen.png\" alt=\"Demo app screen showing the appropriate asset for the user\u2019s locale and a fallback for not supported locales | Phrase\" width=\"600\" height=\"301\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/computer-science-hero-demo-app-screen.png 600w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/computer-science-hero-demo-app-screen-300x151.png 300w\" sizes=\"(max-width: 600px) 100vw, 600px\" \/><\/p>\n<h2><span class=\"ez-toc-section\" id=\"changing-language-in-app\"><\/span>Changing language in app<!-- notionvc: 9d86816e-04be-46f7-a903-fcb6f0d3bae1 --><span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>Sometimes a user will want to have their OS in one language, and a specific app in another. To accommodate that, let\u2019s add a language selector in our app. It should work for all platforms supported by Flutter. <!-- notionvc: 9444f110-143a-471a-955f-a1a9bba64d3b --><\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-69084 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/in-app-language.png\" alt=\"Demo app screen showing in-app languages | Phrase\" width=\"341\" height=\"480\" srcset=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/in-app-language.png 341w, https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/in-app-language-213x300.png 213w\" sizes=\"(max-width: 341px) 100vw, 341px\" \/><\/p>\n<p>In the <code>main.dart<\/code> file, we\u2019ll make the following changes:<\/p>\n<p><!-- notionvc: 0530a815-e9ef-468c-8f66-36c065263ac7 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-39\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-keyword\">void<\/span> main() {\n  runApp(<span class=\"hljs-keyword\">const<\/span> MyApp());\n}\n\n<span class=\"hljs-comment\">\/\/ We need to make MyApp Stateful because<\/span>\n<span class=\"hljs-comment\">\/\/ it needs to react when the Locale changes.<\/span>\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">MyApp<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatefulWidget<\/span> <\/span>{\n  <span class=\"hljs-keyword\">const<\/span> MyApp({Key? key}) : <span class=\"hljs-keyword\">super<\/span>(key: key);\n\n  <span class=\"hljs-meta\">@override<\/span>\n  State&lt;MyApp&gt; createState() =&gt; _MyAppState();\n\n  <span class=\"hljs-keyword\">static<\/span> <span class=\"hljs-keyword\">void<\/span> setLocale(BuildContext context, Locale newLocale) {\n    _MyAppState? state = context.findAncestorStateOfType&lt;_MyAppState&gt;();\n    state?.setLocale(newLocale);\n  }\n}\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">_MyAppState<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">State<\/span>&lt;<span class=\"hljs-title\">MyApp<\/span>&gt; <\/span>{\n  Locale? _locale;\n\n  setLocale(Locale locale) {\n    setState(() {\n      _locale = locale;\n    });\n  }\n\n  <span class=\"hljs-comment\">\/\/ This widget is the root of your application.<\/span>\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n\n    <span class=\"hljs-keyword\">return<\/span> MaterialApp(\n      <span class=\"hljs-comment\">\/\/ ...<\/span>\n      locale: _locale,\n      initialRoute: <span class=\"hljs-string\">'\/'<\/span>,\n      routes: {\n        <span class=\"hljs-string\">'\/'<\/span>: (context) {\n          <span class=\"hljs-keyword\">return<\/span> HeroList(title: AppLocalizations.of(context)!.appTitle);\n        },\n      },\n    );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-39\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_2219a78ff24d1367d25588d3f1757fd8\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>Now, in the <code>hero_list.dart<\/code> file, let\u2019s make the dropdown menu and call the <code>setLocale()<\/code> method we defined in the above.<\/p>\n<p><!-- notionvc: a23338ef-f12b-4f8c-a1ac-fc207a9d47b9 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n\n\n<pre class=\"wp-block-code\" aria-describedby=\"shcb-language-40\" data-shcb-language-name=\"Dart\" data-shcb-language-slug=\"dart\"><span><code class=\"hljs language-dart\"><span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter\/material.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'package:flutter_gen\/gen_l10n\/app_localizations.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'..\/main.dart'<\/span>;\n<span class=\"hljs-keyword\">import<\/span> <span class=\"hljs-string\">'..\/widgets\/hero_card.dart'<\/span>;\n\n<span class=\"hljs-class\"><span class=\"hljs-keyword\">class<\/span> <span class=\"hljs-title\">HeroList<\/span> <span class=\"hljs-keyword\">extends<\/span> <span class=\"hljs-title\">StatelessWidget<\/span> <\/span>{\n  <span class=\"hljs-comment\">\/\/ ...<\/span>\n  <span class=\"hljs-meta\">@override<\/span>\n  Widget build(BuildContext context) {\n    <span class=\"hljs-keyword\">var<\/span> t = AppLocalizations.of(context)!;\n    <span class=\"hljs-keyword\">final<\/span> Locale locale = Localizations.localeOf(context);\n    <span class=\"hljs-comment\">\/\/ Dropdown options<\/span>\n    <span class=\"hljs-keyword\">var<\/span> items = &#91;\n      <span class=\"hljs-string\">'en'<\/span>,\n      <span class=\"hljs-string\">'ar'<\/span>,\n    ];\n\n    <span class=\"hljs-keyword\">return<\/span> Scaffold(\n      appBar: AppBar(\n        title: Text(title),\n        actions: &lt;Widget&gt;&#91;\n          DropdownButton(\n            <span class=\"hljs-comment\">\/\/ Down Arrow Icon<\/span>\n            icon: <span class=\"hljs-keyword\">const<\/span> Icon(Icons.settings, color: Colors.white,),\n            items: items.map((<span class=\"hljs-built_in\">String<\/span> items) {\n              <span class=\"hljs-keyword\">return<\/span> DropdownMenuItem(\n                value: items,\n                child: Text(items),\n              );\n            }).toList(),\n            onChanged: (<span class=\"hljs-built_in\">String<\/span>? newValue) {\n              MyApp.setLocale(context, Locale(newValue));\n            },\n          ),\n        ],\n      ),\n      <span class=\"hljs-comment\">\/\/ ...<\/span>\n    );\n  }\n}<\/code><\/span><small class=\"shcb-language\" id=\"shcb-language-40\"><span class=\"shcb-language__label\">Code language:<\/span> <span class=\"shcb-language__name\">Dart<\/span> <span class=\"shcb-language__paren\">(<\/span><span class=\"shcb-language__slug\">dart<\/span><span class=\"shcb-language__paren\">)<\/span><\/small><\/pre>\n\n\n<div id=\"acf\/text-block_d667fe6121565c14765cffe135bce711\" class=\"pxblock pxblock--text spacing--default bg--white\">\n\n\t\n\t<div class=\"container\">\n\t\t<div class=\"wysiwyg animate-in\">\n\t\t\t<p>With that, the user can select the app language independently from the system language.<\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> Before selecting a locale, it will be set to the user\u2019s system default locale.<\/p>\n<p><img loading=\"lazy\" decoding=\"async\" class=\"size-full wp-image-69090 aligncenter\" src=\"https:\/\/phrase.com\/wp-content\/uploads\/2023\/10\/simulator-screen-recording-iphone-14-plus.gif\" alt=\"Simulator app screen recording on iPhone 14 Plus | Phrase\" width=\"296\" height=\"640\" \/><\/p>\n<p>\ud83d\uddd2\ufe0f <em>Note \u00bb<\/em> Before the user manually selects a locale in-app, the system locale is used as a default. However, the user\u2019s manually selected in-app locale <strong>won&#8217;t persist<\/strong>\u00a0when the app is restarted. To persist the user\u2019s in-app locale preference, you need to save and retrieve the locale using something like the <a href=\"https:\/\/pub.dev\/packages\/shared_preferences\">Shared Preferences plugin<\/a>.<\/p>\n<p>The final project is available on <a href=\"https:\/\/github.com\/PhraseApp-Blog\/flutter-i18n-v2\">GitHub<\/a>.<\/p>\n<p><!-- notionvc: e6fc0d1e-c22a-45b2-8816-ef3f1c993cdc --><\/p>\n<h2><span class=\"ez-toc-section\" id=\"flutter-localization-made-simpler\"><\/span>Flutter localization made simpler<span class=\"ez-toc-section-end\"><\/span><\/h2>\n<p>We hope you enjoyed our Flutter localization tutorial and picked up some handy tricks. Now, if you&#8217;re ready to take your localization game up a notch, <a href=\"https:\/\/phrase.com\/platform\/strings\/\">Phrase Strings<\/a> is your go-to solution. A localization wizard for your Flutter apps, Phrase Strings comes with ARB file support, and it&#8217;s super dev-friendly with its easy-to-use API and CLI. It also features a slick strings editor that makes life a breeze for translations.<\/p>\n<p>What&#8217;s more, it syncs seamlessly with GitHub, GitLab, and Bitbucket, and even offers over-the-air translations for mobile apps to handle the localization heavy lifting, allowing you to stay focused on the code you love so much. Check out all <a href=\"https:\/\/phrase.com\/roles\/developers\/\">Phrase features for developers <\/a>and see for yourself how they can help you take your apps global more quickly.<\/p>\n<p><!-- notionvc: 23cc7d5c-db75-40d3-a597-92c2e44cb6b5 --><\/p>\n<p><!-- notionvc: 91de31c5-0f6d-40c2-9d94-f70620d96540 --><\/p>\n\t\t<\/div>\n\t<\/div>\n<\/div>\n","protected":false},"excerpt":{"rendered":"<p>Let&#8217;s decode the secrets of Flutter localization so you can speak your users&#8217; language and keep coding your way to global domination.<\/p>\n","protected":false},"author":41,"featured_media":2612,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_acf_changed":false,"_stopmodifiedupdate":true,"_modified_date":"","_searchwp_excluded":"","footnotes":""},"categories":[40],"class_list":["post-13859","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-software-localization"],"acf":[],"_links":{"self":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/13859"}],"collection":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/users\/41"}],"replies":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/comments?post=13859"}],"version-history":[{"count":57,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/13859\/revisions"}],"predecessor-version":[{"id":69685,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/posts\/13859\/revisions\/69685"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media\/2612"}],"wp:attachment":[{"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/media?parent=13859"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/phrase.com\/wp-json\/wp\/v2\/categories?post=13859"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}