内容简介:In this tutorial, you’ll learn how to use the Flutter Intl plugin to internationalize and localize your app. Why should anyone care about internationalization and localization, though? Well, let the numbers do the talking:But more people learn English ever
In this tutorial, you’ll learn how to use the Flutter Intl plugin to internationalize and localize your app. Why should anyone care about internationalization and localization, though? Well, let the numbers do the talking:
- Eight of the top ten countries for app downloads are not English-speaking, according to Sensor Tower .
- Localized apps are downloaded 128% as often and earn 26% more revenue per supported country, according to Distimo .
But more people learn English every day, so maybe these numbers will lose relevance over time, right? Nope. The next billion internet users will come from emerging economies. Here’s what Google already knows about them:
- They have a mobile-only mindset. Most have never used a PC and never will — which is lucky for mobile developers.
- They need localized content . Even the small percentage that knows English prefers content in their language.
Nelson Mandela captured that sentiment when he said: “If you talk to a man in a language he understands, that goes to his head. If you talk to him in his language, that goes to his heart.” You want users to have that kind of passion for your app.
Given all this, can you still think of a single reason not to localize your app? If your answer is that you don’t know how to do it, this tutorial will help.
By the end of this tutorial, you’ll know how to:
- Tell the difference between internationalization and localization.
- Internationalize a Flutter app.
- Use the Intl package.
- Use the Flutter Intl IDE plugin .
- Localize materials and Cupertino widgets.
- Make your app feel like you developed it in the user’s native language.
Note : This article assumes you’re comfortable with Flutter basics. If that’s not the case, check out Getting Started with Flutter .
Getting Started
Download the starter project by clicking the Download Materials button at the top or bottom of the tutorial.
Unzip the file and open Android Studio 4.0 or later. You can use Visual Studio Code instead, but you’ll need to adapt some instructions.
Click Open an existing Android Studio project and choose the starter folder from your unzipped download.
The IDE will prompt you with the message: ‘Pub get’ has not been run at the top of your screen. Click Get dependencies and wait for the download to finish.
Today, you’ll work on a very important app indeed — a death by caffeine calculator, the Buzz Kill .
Start off by taking a brief look at the code. Open the pages/form_page.dart file and take a look around. There’s a lot of code, but don’t be intimidated! Most of it is simply setting up a list of coffee drinks for a user to select.
Next up open the pages/results_page.dart
file. results_page
is a simple widget that displays some safe coffee consumption information to the user.
Take a brief look at the code, focusing on pages/form_page.dart and pages/results_page.dart . Then build and run the project to get familiar with it.
For now, Buzz Kill only works in English. Your job is to make it welcoming for Brazilians.
Why Brazilian Portuguese (pt-BR)? Because Brazil has the third-most app downloads, but only 5% of the population speaks English. That makes it an excellent target for localization.
Talking about languages, do you know how to say internationalization in dt-FL (Flutterian Dart)? It’s i18n. Why? It encompasses the first and last letters of “internationalization” and then shortens the 18 letters in between.
Before you start improving Buzz Kill, there’s one important thing to clear up: what internationalization and localization actually mean.
Differentiating Between Internationalization and Localization
It’s not uncommon to see the words internationalization ( i18n ) and localization ( l10n ) used — mistakenly — interchangeably. Where do you draw the line?
Localization is the process of making your app available to other locales. It includes the parameters defining the user’s language, region and any particular variant — for example, en-US (American English).
That sounds easy, but how do you do it? Do you create a copy of your code for each language? Or add if
statements all over the place choosing which text to display? No, that’s where internationalization comes to the rescue.
Internationalization is how you architect and engineer your code to make it easy to localize. So, yes, you can have an internationalized app that supports just one locale, because it has the capacity to ultimately support more than one locale without dramatic engineering efforts. In fact, that’s encouraged. Internationalize even if becoming a polyglot isn’t in your app’s plans. It’ll make your code cleaner and more future-proof.
Next, you’ll see how you use Flutter to internationalize your apps.
Internationalizing in Flutter
These are the stepping stones to i18n in Flutter:
-
Create a class containing dynamic
String
properties or functions for every translatable text of your app. For example, aformPageAppBarTitle
property may return'Death by Caffeine Calculator'
or'Calculadora de Morte por Cafeína'
, depending on the locale. - Create an intermediary class in charge of setting up instances of the class created in the previous step when the app initializes or the locale changes.
-
Provide an instance of this intermediary class to a
Localizations
widget living insideMaterialApp
. -
Replace hard-coded texts with their new, dynamic versions. For example,
Text('Death by Caffeine Calculator')
becomesText(Localizations.of<ClassFromTheFirstStepContainingValuesThatVaryPerLocale>(context, ClassFromTheFirstStepContainingValuesThatVaryPerLocale).formPageAppBarTitle)
.
Although ClassFromTheFirstStepContainingValuesThatVaryPerLocale
is a meaningful name, for sure, it’s quite long, and you’ll be typing it a lot. From now on, you’ll call it S
.
Maintaining Translations
The question that remains is: How should S
store the String
s for all the supported languages?
That’s what you call an implementation detail, and as such, it’s up to you. But here are some ideas:
-
Use a
Map<String, Map<String, String>>
, where the firstMap
‘s key is the language, and the secondMap
‘s key is the ID of the text, e.g.,formPageAppBarTitle
. - Maintain — and read from — JSON-like files, one per locale.
- Use your creativity.
The second approach looks like an excellent choice, and it’s what you’ll use in this tutorial. You’ll count on the Intl package to help you read the values from the files.
Keeping content separate from logic keeps your code neat and makes it feasible even for non-coders to change it.
Even with Intl’s help, there’s plenty of work left for you to do. The process of changing the delegate every time a new language comes in and changing S
whenever you need to add a new String
— which happens often — is cumbersome and robotic.
As you know, wiping out cumbersome and robotic tasks is a developer’s superpower. That’s why the people at Localizely created the mind-blowing Flutter Intl plugin for Android Studio and Visual Studio Code that handles those tasks for you.
Whenever you add a new language or String
, the plugin generates a new LocalizationsDelegate
and S
for you.
Note : Don’t confuse the Intl package with the Flutter Intl IDE plugin . The latter handles generating the classes that use the former. You’ll use both in this tutorial.
Setting up Flutter Intl
Open Android Studio’s preferences by pressing Command-, (comma) on macOS or Control-Alt-S on Linux or Windows.
Select Plugins on the left-side panel (1) and Marketplace in the upper tab bar (2). Type intl in the search bar (3), then click Install for Localizely ‘s Flutter Intl result (4).
With the plugin installed, restart your IDE.
Generating Classes
Open pubspec.yaml
and replace # TODO: Add intl and flutter_localizations here.
with:
intl: 0.16.1 flutter_localizations: sdk: flutter
You’ve just added two packages to your project:
- intl : Facilities for i18n and l10n such as message translation and date/number formatting and parsing. You’ll mostly deal with this package through the generated classes.
- flutter_localizations : Hang tight! You’ll learn more about this in just a second.
Select the starter folder in the project directory then, on the menu bar, select Tools ▸ Flutter Intl ▸ Initialize for the Project .
The command above added a flutter_intl
section to your pubspec.yaml
. Add main_locale: en_US
to it, below and in the same indentation level of enabled: true
.
flutter_intl: main_locale: en_US enabled: true
Click Pub get in the Flutter commands bar at the top of your screen.
Return to Tools ▸ Flutter Intl on the IDE’s menu bar, but this time, select Remove Locale . Choose en and click OK .
Here, you changed your default locale from en to en_US , properly identifying the nuance. That’ll allow you to execute custom logic based on the country code later.
The plugin created two folders, including some files, inside lib :
- generated : Holds the generated classes. You won’t need to touch them.
- l10n : Home of your .arb s, the JSON syntax files that’ll hold your translations. The one for American English is already there for you.
Now that you’ve set up Flutter Intl, it’s time to configure your app to use it.
Configuring Your App
Go to lib/main.dart , and add these two imports below the others:
import 'package:buzzkill/generated/l10n.dart'; import 'package:flutter_localizations/flutter_localizations.dart';
Now, inside MaterialApp
, remove // TODO: Specify localizationsDelegates and supportedLocales.
and add this instead:
localizationsDelegates: [ // 1 S.delegate, // 2 GlobalMaterialLocalizations.delegate, GlobalWidgetsLocalizations.delegate, GlobalCupertinoLocalizations.delegate, ], supportedLocales: S.delegate.supportedLocales,
Here’s what you did:
-
Remember the intermediate class? The delegate responsible for setting up instances of
S
? The plugin generated it for you, and you use it here. - Not only does your app’s text need translations, but some Flutter widgets do as well. The delegates taking care of them came from the flutter_localizations package you added above.
Back in the menu bar, select Tools ▸ Flutter Intl ▸ Add Locale . Type in pt_BR and click OK .
The lib/l10n folder now contains a new file — intl_pt_BR.arb — and the Android version of your app is ready to support en-US and pt-BR.
Only the Android version? Not the iOS? Yep! That’s because one specific iOS configuration file needs your special care. The good news is that you don’t need Xcode to do it.
In your project structure, open ios/Runner/Info.plist
and substitute <!-- TODO: Specify supported locales. -->
with:
<key>CFBundleLocalizations</key> <array> <string>en_US</string> <string>pt_BR</string> </array>
Stop the previous execution of the app, then build and run it again to make sure you haven’t introduced any errors. Don’t expect any visual changes.
Extracting the Strings
Replace what’s in lib/l10n/intl_en_US.arb with:
{ "@@locale": "en_US", "formPageAppBarTitle": "Death by Caffeine Calculator", "firstSuggestedDrinkName": "Drip Coffee (Cup)", "secondSuggestedDrinkName": "Espresso (Shot)", "thirdSuggestedDrinkName": "Latte (Mug)", "formPageWeightInputLabel": "Body Weight", "formPageWeightInputSuffix": "pounds", "formPageRadioListLabel": "Choose a drink", "formPageActionButtonTitle": "CALCULATE", "formPageCustomDrinkRadioTitle": "Other", "formPageCustomDrinkServingSizeInputLabel": "Serving Size", "formPageCustomDrinkServingSizeInputSuffix": "fl. oz", "formPageCustomDrinkCaffeineAmountInputLabel": "Caffeine", "formPageCustomDrinkCaffeineAmountInputSuffix": "mg", "resultsPageAppBarTitle": "Dosages", "resultsPageLethalDosageTitle": "Lethal Dosage", "resultsPageFirstDisclaimer": "*Based on {servingSize} fl. oz serving.", "resultsPageLethalDosageMessage": "{quantity, plural, one{One serving.} other{{formattedNumber} servings in your system at one time.}}", "resultsPageSafeDosageTitle": "Daily Safe Maximum", "resultsPageSafeDosageMessage": "{quantity, plural, one{One serving per day.} other{{formattedNumber} servings per day.}}", "resultsPageSecondDisclaimer": "*Applies to age 18 and over. This calculator does not replace professional medical advice." }
These are the en-US entries for every visible line of text within Buzz Kill. Pay special attention to:
- @@locale : Identifies the locale of the file. If you don’t add it, Intl can infer it from the file name, but it’ll give you a warning.
-
resultsPageFirstDisclaimer
:
{servingSize}
is a placeholder.S
dynamically replaces it with the value you specify when using the function it generates. -
resultsPageLethalDosageMessage
and resultsPageSafeDosageMessage
: These are plurals
. Their values depend on a number specified when calling the function. Besides that, they also use a
{formattedNumber}
placeholder, like resultsPageFirstDisclaimer .
Don’t worry! These concepts are easier to grasp when you see them reflected on the Dart side.
Note : You shouldn’t reuse entries, which is why you named them after the place they appear in your app. The same string can have different translations depending on its context.
Now that you have your .arb file set up for American English, it’s time to add the Brazilian Portuguese version to Buzz Kill.
Adding Brazilian Portuguese Translations
This time, inside lib/l10n/intl_pt_BR.arb , replace everything with:
{ "@@locale": "pt_BR", "formPageAppBarTitle": "Calculadora de Morte por Cafeína", "firstSuggestedDrinkName": "Café Coado (Xícara)", "secondSuggestedDrinkName": "Espresso (Shot)", "thirdSuggestedDrinkName": "Latte (Caneca)", "formPageWeightInputLabel": "Peso Corporal", "formPageWeightInputSuffix": "libras", "formPageRadioListLabel": "Escolha uma bebida", "formPageActionButtonTitle": "CALCULAR", "formPageCustomDrinkRadioTitle": "Outra", "formPageCustomDrinkServingSizeInputLabel": "Tamanho", "formPageCustomDrinkServingSizeInputSuffix": "fl. oz", "formPageCustomDrinkCaffeineAmountInputLabel": "Cafeína", "formPageCustomDrinkCaffeineAmountInputSuffix": "mg", "resultsPageAppBarTitle": "Dosagens", "resultsPageLethalDosageTitle": "Dose Letal", "resultsPageLethalDosageMessage": "{quantity, plural, one{Uma porção.} other{{formattedNumber} porções no seu sistema de uma vez.}}", "resultsPageSafeDosageTitle": "Limite Seguro Diário", "resultsPageSafeDosageMessage": "{quantity, plural, one{Uma porção por dia.} other{{formattedNumber} porções por dia.}}", "resultsPageFirstDisclaimer": "*Baseado em uma porção de {servingSize} fl. oz.", "resultsPageSecondDisclaimer": "*Se aplica a pessoas com 18 anos ou mais. Essa calculadora não substitui conselhos médicos profissionais." }
There’s nothing new here. These are the Portuguese translations for the same lib/l10n/intl_en_US.arb entries.
Trigger the classes to re-generate by saving the file with Command-S on macOS or Control-S on Linux or Windows.
Build and run again. Everything should look the same as before, and now you’re ready to code for real.
You’ve accomplished a lot, but the app won’t reflect your changes until you remove the hard-coded values from your widgets.
Removing Hard-Coded Values
Your journey begins on lib/pages/form_page.dart , Buzz Kill’s home. Open the file and start by adding this line at the top of the imports block:
import 'package:buzzkill/generated/l10n.dart';
The UI presents the user with three suggested caffeinated drinks. In code, you control them with _drinkSuggestions
. Remove the current _drinkSuggestions
definition and add this instead:
// 1 List<Drink> _drinkSuggestions; // 2 Locale _userLocale; @override void didChangeDependencies() { // 3 final newLocale = Localizations.localeOf(context); if (newLocale != _userLocale) { _userLocale = newLocale; // 4 _weightTextController.clear(); _servingSizeTextController.clear(); _caffeineTextController.clear(); _selectedDrinkSuggestion = null; _drinkSuggestions = [ Drink( // 5 name: S.of(context).firstSuggestedDrinkName, caffeineAmount: 145, servingSize: 8, ), Drink( name: S.of(context).secondSuggestedDrinkName, caffeineAmount: 77, servingSize: 1.5, ), Drink( name: S.of(context).thirdSuggestedDrinkName, caffeineAmount: 154, servingSize: 16, ), ]; } super.didChangeDependencies(); }
There’s a lot going on in there:
-
The property can’t be final anymore. Since you have strings that need to be localized in your list of
Drink
s, you now need to initialize it insidedidChangeDependencies()
before thecontext
will be available. -
Locale
contains information about (drum roll, please), the current locale! You use this property to track locale changes while the app is open. That way, you can reset your form fields and suggestions if a change occurs. -
You’re getting the current
Locale
. - Here, you reset the input fields.
-
You defined
firstSuggestedDrinkName
inside the .arb files and, like magic, it became a property ofS
.S.of(context)
is short forLocalizations.of<S>(context, S)
;
Now that you’ve translated the suggestions, it’s time to move on to build()
. From top to bottom, here’s a sequence of substitutions for you to make:
-
'Death by Caffeine Calculator'
becomesS.of(context).formPageAppBarTitle
-
'Body Weight'
becomesS.of(context).formPageWeightInputLabel
-
'pounds'
becomesS.of(context).formPageWeightInputSuffix
-
'Choose a drink'
becomesS.of(context).formPageRadioListLabel
-
'Other'
becomesS.of(context).formPageCustomDrinkRadioTitle
-
'Serving Size'
becomesS.of(context).formPageCustomDrinkServingSizeInputLabel
-
'fl. oz'
becomesS.of(context).formPageCustomDrinkServingSizeInputSuffix
-
'Caffeine'
becomesS.of(context).formPageCustomDrinkCaffeineAmountInputLabel
-
'mg'
becomesS.of(context).formPageCustomDrinkCaffeineAmountInputSuffix
-
'CALCULATE'
becomesS.of(context).formPageActionButtonTitle
Go through the build()
method and replace each instance of the above strings with their corresponding localized variants.
Wow, that was heavy-ish! If only the Buzz Kill’s developer had been smart enough to internationalize it from the start. :]
Adjusting the Results Page
You’re halfway there. Now, it’s time to give lib/pages/results_page.dart the same treatment. Open the file and add this import at the top of the imports block:
import 'package:buzzkill/generated/l10n.dart';
Now, move on to build()
and make these replacements:
-
'Dosages'
becomesS.of(context).resultsPageAppBarTitle
; -
'Lethal Dosage'
becomesS.of(context).resultsPageLethalDosageTitle
; -
'Daily Safe Maximum'
becomesS.of(context).resultsPageSafeDosageTitle
; -
'*Based on ${drink.servingSize} fl. oz serving.'
becomesS.of(context).resultsPageFirstDisclaimer(drink.servingSize)
;
You skipped a few strings in the build()
method, so double back and make sure to update them:
-
-
Remove:
lethalDosage == 1 ? 'One serving.' : '${lethalDosage.toStringAsFixed(1)} ' 'servings in your system at one time.'
-
Add:
S.of(context).resultsPageLethalDosageMessage( lethalDosage, lethalDosage.toStringAsFixed(1), )
-
Remove:
-
-
Remove:
safeDosage == 1 ? 'One serving per day.' : '${safeDosage.toStringAsFixed(1)} ' 'servings per day.'
-
Add:
S.of(context).resultsPageSafeDosageMessage( safeDosage, safeDosage.toStringAsFixed(1), )
-
Remove:
-
-
Remove:
'*Applies to age 18 and over. This calculator ' 'does not replace professional medical advice.'
-
Add:
S.of(context).resultsPageSecondDisclaimer
-
Remove:
Notice that some of these aren’t properties of S
, but functions, because they need arguments like a plurals value or filler text for a placeholder.
Finally, build and run the app. Mess with the device’s language configuration and watch the app respond accordingly. The .gif below demonstrates how to do that on Android (on the left) and iOS (on the right), but the steps may vary on your OS version.
Congratulations, you’ve translated your first app!
Going Beyond Translation
Not to be a total buzzkill (ha!), but you’re just getting started with localization. Simply put: The product must feel local, and that goes beyond just the language. Different regions use different formats for everything from the time of day to how they write out phone numbers.
They say a picture is worth a thousand words. This one shows some things that localization covers:
Can you guess which aspects of localization apply to Buzz Kill?
- Measurement Units : Brazilians — and most of the rest of the world — don’t use the imperial system. Translating “pounds” and “fl. oz” into Portuguese doesn’t mean that people will understand them.
- Number Formatting : Unlike the United States, Brazil uses a comma as the decimal separator and a dot as the thousand separator.
- Culture : Drip coffees and espressos are pretty common in Brazil as well as in the United States, but lattes aren’t!
- Text Direction : In some languages, writing goes from the right to the left ( RTL ). Localization for these languages goes beyond just text direction. For example, if your left and right paddings have different values, you want to switch them for an RTL locale. Buzz Kill doesn’t support any RTL languages at the moment, but you’ll see how easy it is to be ready to support them right out of the box.
Next, you’ll put these localization features in place.
Making the App Feel Local
You’ll start by changing the text. Roll up your sleeves, open lib/l10n/intl_pt_BR.arb and replace:
-
"thirdSuggestedDrinkName": "Latte (Caneca)"
with"thirdSuggestedDrinkName": "Pingado (Copo Americano)"
; -
"formPageWeightInputSuffix": "libras"
with"formPageWeightInputSuffix": "quilos"
; -
"formPageCustomDrinkServingSizeInputSuffix": "fl. oz"
with"formPageCustomDrinkServingSizeInputSuffix": "ml"
; -
"resultsPageFirstDisclaimer": "*Baseado em uma porção de {servingSize} fl. oz."
with"resultsPageFirstDisclaimer": "*Baseado em uma porção de {servingSize} ml."
;
Save the file with Command-S on macOS or Control-S on Linux or Windows.
First, you’ve changed the third suggestion name from Latte to a well-known caffeinated friend of Brazilians, the pingado. Then you changed the weight measure name from pounds to kilograms, and the liquid volume measure name from fluid ounces to milliliters.
As you might have guessed, changing measure unit names isn’t enough. You need some math to convert them.
Converting Measures
Create a new file by right-clicking the lib folder and choosing New ▸ Dart File . Name it measurement_conversion and enter the following code:
import 'package:flutter/widgets.dart'; bool _shouldUseImperialSystem(Locale locale) { final countryCode = locale.countryCode; return countryCode == 'US'; } // 1 extension IntMeasurementConversion on int { int get _roundedPoundFromKg => (this * 2.20462).round(); double get _flOzFromMl => this * 0.033814; // 2 int toPoundsIfNotAlready(Locale locale) { if (_shouldUseImperialSystem(locale)) { return this; } return _roundedPoundFromKg; } double toFlOzIfNotAlready(Locale locale) { if (_shouldUseImperialSystem(locale)) { return toDouble(); } return _flOzFromMl; } } extension DoubleMeasurementConversion on double { int get _roundedMlFromFlOz => (this * 29.5735).round(); // 3 double toMillilitersIfShouldUseMetricSystem(Locale locale) { if (_shouldUseImperialSystem(locale)) { return this; } return _roundedMlFromFlOz.toDouble(); } }
Going over it step-by-step:
-
You use Dart extension methods
to add utilities to
int
anddouble
. - The functions in this extension convert a number to its imperial system counterpart if the user entered it using the metric system.
- Finally, you’re converting the number to the metric system if the user isn’t using the imperial system.
Now, you need to use the functionalities you’ve just added.
Implementing the Measurement Conversion
Go to lib/pages/form_page.dart and add an import to the previously created file at the top:
import 'package:buzzkill/measurement_conversion.dart';
Inside _pushResultsPage()
, replace the weight
and drink
variable declarations with:
final weight = _weightTextController.intValue .toPoundsIfNotAlready( _userLocale, ); final drink = _selectedDrinkSuggestion ?? Drink( caffeineAmount: _caffeineTextController.intValue, servingSize: _servingSizeTextController.intValue .toFlOzIfNotAlready( _userLocale, ), );
If the user entered the weight in kilograms and the serving size in milliliters, this code converts them to their imperial system alternatives because that’s how ResultsPage
and Drink
expect them.
Now, it’s time to make the numbers look like your Brazilian users expect them to.
Formatting Numbers
Go to lib/pages/results_page.dart and, at the top of the import block, add these two imports:
import 'package:buzzkill/measurement_conversion.dart'; import 'package:intl/intl.dart';
At the beginning of the build()
method, add this line below the declaration of lethalDosage
:
final numberFormat = NumberFormat('#.#');
NumberFormat
comes from Intl. It handles the decimal/thousand separators localization.
Still in build()
, make these substitutions:
-
lethalDosage.toStringAsFixed(1)
becomesnumberFormat.format(lethalDosage)
; -
safeDosage.toStringAsFixed(1)
becomesnumberFormat.format(safeDosage)
; -
S.of(context).resultsPageFirstDisclaimer(drink.servingSize)
becomes:S.of(context).resultsPageFirstDisclaimer( numberFormat.format( drink.servingSize.toMillilitersIfShouldUseMetricSystem( Localizations.localeOf(context), ), ), )
In the first two, you started using the numberFormat
to format your numbers. The last substitution also uses one of the lib/measurement_conversion.dart
functions to display the serving size converted back to milliliters, in case the user is not in the U.S.
Customizing Drink Suggestions
Much like the metric units, you can’t change a suggested caffeinated drink just by replacing its name.
Lattes and pingados have different amounts of caffeine and serving sizes, so you need to make a few additional changes. Open lib/drink.dart , and add the following import:
import 'package:buzzkill/generated/l10n.dart';
Add this function to Drink
:
static List<Drink> suggestionListOf(BuildContext context) { final suggestionList = [ Drink( name: S.of(context).firstSuggestedDrinkName, caffeineAmount: 145, servingSize: 8, ), Drink( name: S.of(context).secondSuggestedDrinkName, caffeineAmount: 77, servingSize: 1.5, ), ]; final countryCode = Localizations.localeOf(context).countryCode; if (countryCode == 'BR') { suggestionList.add( Drink( name: S.of(context).thirdSuggestedDrinkName, servingSize: 6.42, caffeineAmount: 23, ), ); } else { suggestionList.add( Drink( name: S.of(context).thirdSuggestedDrinkName, caffeineAmount: 154, servingSize: 16, ), ); } return suggestionList; }
That makes the last item of the list adaptable to the device’s country.
Open lib/pages/form_page.dart
, and in didChangeDependencies()
, change the _drinkSuggestions
initialization to:
_drinkSuggestions = Drink.suggestionListOf(context);
Here, you delegate the creation of the suggested drinks to Drink
. You can now add a drink specific to the culture of the country you’re targeting. Now, you’ll add support for RTL languages.
Preparing for RTL Languages
For the final touch, open lib/ui_components/informative_card.dart
and change the padding
of the second Padding
to:
const EdgeInsetsDirectional.only( top: 16, end: 16, bottom: 16, )
Can you spot the difference?
When using EdgeInsetsDirectional
, you don’t specify left
and right
, but start
and end
. This is RTL-friendly. Make a habit of using it whenever you need different values for left
and right
.
Finally, build and run and delight yourself with a fully-localized app. Again, play with the device’s language settings to ensure everything works as intended.
Where to Go From Here?
You can download the completed project files by clicking on the Download Materials button at the top or bottom of the tutorial.
Take a look at an updated version of thel10n illustration:
You’ve now colored some of the circles and tried out some of the available localization options. This is the furthest you can go with Buzz Kill.
By now, you’re hopefully seen there’s no better return on investment for your apps than localization.
In less than an hour, you opened Buzz Kill up to a multitude of new potential users without adding a single feature. And it only gets better: You don’t have to go through the internationalization process again when you add support for new locales. Just add a new .arb file and you’re good to go — assuming there aren’t any other localization requirements for you to meet.
For future projects, you can also look to the Intl package to help you with:
- Date Parsing/Formatting : Not every country uses the U.S.’s month/day/year format for dates. Intl can handle that for you.
- Genders : Intl lets you display text depending on a given subject gender, just as you can display singular or plural language based on a given number.
- Bidirectional Text : When right-to-left meets left-to-right language, as when a Hebrew page embeds an English word, Intl can help.
Wanna know what happens if the user selects a language other than American English or Brazilian Portuguese? You can learn more about it in the official Flutter docs .
If you enjoyed this article or have any questions, let us know by using the comments section below.
以上就是本文的全部内容,希望本文的内容对大家的学习或者工作能带来一定的帮助,也希望大家多多支持 码农网
猜你喜欢:本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
编译原理
Alfred V.Aho、Jeffrey D.Ullman、Ravi Sethi / 李建中 / 机械工业出版社 / 2003-8 / 55.00元
《编译原理》作者Alfred V.Aho、Ravi Sethi和Jeffrey D.Ullman是世界著名的计算机 科学家,他们在计算机科学理论、数据库等很多领域都做出了杰出贡献。《编译原理》 是编译领域无可替代的经典著作,被广大计算机专业人士誉为“龙书”。《编译原理》一 直被世界各地的著名高等院校和科研机构(如贝尔实验室、哥伦比亚大学、普 林斯顿大学和斯坦福大学等)广泛用作本科生和研究生编译原理......一起来看看 《编译原理》 这本书的介绍吧!