Let’s delve into the world of localization by building a simple counter app in Flutter. This app is a handy starting point for any Flutter project, and we’ll make some modifications to showcase the power of localization.
To achieve this, we’ll leverage the Flutter localizations package, a tool that enables us to seamlessly translate our app into different languages. This process includes features like interpolation for words and phrases, ensuring accurate translations for both singular and plural scenarios.
In simpler terms, localization helps us make our app accessible to users from different linguistic backgrounds. We’ll make our counter app speak various languages and handle different language intricacies, like when we want to insert specific words or phrases and correctly translate singular and plural forms.
Related read: Creating and Uploading a Flutter Package: A Step-by-Step Guide
Let’s make your `pubspec.yaml` file changes more straightforward. Open your `pubspec.yaml` file and update it with the following content:
# Ensure compatibility with Flutter version 2.16.1 or newer but less than 3.0.0
environment:
sdk: ">=2.16.1 <3.0.0"
# Dependencies for localization
dependencies:
flutter:
sdk: flutter
flutter_localizations:
sdk: flutter
intl: ^0.17.0 # Package for internationalization and localization
cupertino_icons: ^1.0.2
dev_dependencies:
flutter_test:
sdk: flutter
flutter_lints: ^1.0.0
# Flutter configuration
flutter:
generate: true # Enables automatic code generation for localization
uses-material-design: true
These changes tell Flutter to use the necessary packages for localization, including `intl` for internationalization support. The `generate: true` line is crucial for automating the code generation process, making your life easier during localization. Now, your project is all set to handle translation, plurals, and genders.
Let’s simplify the process of creating localization configuration and translation files in plain language.
Make a file named `l10n.yaml` in your project’s root, specifically in the `lib` directory. This file guides Flutter on where to find translation files and what to name the generated Dart files.
arb-dir: lib/l10n # Location of translation files
template-arb-file: app_en.arb # Default translation file for English
output-localization-file: app_localizations.dart # Generated Dart file
We use ARB (Application Resource Bundle) files to store translations. These are simple key-value pairs like JSON. Start by making an ARB file for the default language, which is English in this case. Name it `app_en.arb` and place it in the `lib/l10n` directory.
// lib/l10n/app_en.arb
{
"appTitle": "Demo App"
}
Since we’re supporting multiple languages, create separate ARB files for each. For example, we’ll also support Hindi.
// lib/l10n/app_hi.arb
{
"appTitle": "डेमो ऐप"
}
You can add more files for additional languages based on your project’s needs.
By following these steps, you’re setting up your Flutter project for localization. The configuration file (`l10n.yaml`) helps Flutter understand where to find translations, and the ARB files hold the actual translated content for each supported language.
Next, let’s add localization to MaterialApp:
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
void main() {
runApp(const MyApp());
}
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: 'Flutter Demo'),
);
}
}
Let’s break down the role of `AppLocalizations.localizationsDelegates` and `AppLocalizations.supportedLocales` in simpler terms.
`AppLocalizations.localizationsDelegates` is like the maestro directing the localization orchestra. It’s responsible for coordinating the process of making our app multilingual. Flutter already provides built-in localizations for widgets, Material design, and Cupertino (iOS-style design). So, when you use something like `showDatePicker()`, the dialogue is magically translated to your device’s language without you needing to create a separate localization file for it.
`AppLocalizations.supportedLocales` plays the role of a guidebook for Flutter. It tells Flutter which locales (languages) our app supports. Flutter is smart enough to detect when a user switches their device language, and it only bothers to rebuild our app’s user interface when it detects a new supported locale has been added to `supportedLocales` in the `MaterialApp`.
In simpler terms, these two aspects ensure that our app can speak different languages, and Flutter knows when to update the display based on the user’s language preference. It’s like having a translator who not only knows various languages but also adapts to the user’s language choice seamlessly.
To support localization in iOS, we’ll need to make the following changes to Info.plist:
<key>CFBundleLocalizations</key>
<array>
<string>en</string>
<string>hi</string>
</array>
Let’s break down the process of using the translations from ARB files and generating Dart files in simpler terms.
In order to use the AppLocalizaions class, you’ll have to first import this class:
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
After that, you can access the locale value using;
AppLocalizations.of(context).appTitle.
Our MaterialApp now looks like this:
class MyApp extends StatelessWidget {
const MyApp({Key? key}) : super(key: key);
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
title: 'Flutter Demo',
localizationsDelegates: AppLocalizations.localizationsDelegates,
supportedLocales: AppLocalizations.supportedLocales,
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: const MyHomePage(title: AppLocalizations.of(context).appTitle),
);
}
}
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key, required this.title}) : super(key: key);
final String title;
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(widget.title),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
const Padding(
padding: EdgeInsets.only(top: 32),
child: Text(
'Flutter is Awesome',
style: TextStyle(fontSize: 24),
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text('You have pushed this button :',
style: Theme.of(context).textTheme.headline6),
Text(
'$_counter times',
style: Theme.of(context).textTheme.headline4,
)
],
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: const Icon(Icons.add),
),
);
}
}
Congratulations, our app’s title has now been successfully localized. We just need to update the other String values.
Interpolation is essentially inserting one thing into another, especially when they are different. In the context of localization, interpolation becomes valuable when you wish to include a specific word or text in the default language, such as English, across all the supported languages.
Consider our demo app, where we aim to display a message like “Flutter is awesome.” The unique aspect here is that we want to ensure the word “Flutter” remains in English, regardless of the language selected on the user’s device.
To put it simply, interpolation allows us to seamlessly blend different languages. It’s akin to having a special word or phrase that stays the same, providing a consistent and unified message for users, irrespective of the language they choose. In our scenario, interpolation ensures that certain words, like “Flutter,” maintain their original language, contributing to a cohesive user experience across various supported languages.
{
"appTitle": "Demo App",
"appDescription": "{flutter} is Awesome",
"@appDescription": {
"placeholders": {
"flutter": {
"type": "String",
"example": "Flutter"
}
}
},
}
{
"appTitle": "डेमो ऐप",
"appDescription": "{flutter} बहुत बढ़िया है",
}
Now, we need to use the interpolated text description that we included in the ARB files. Since we’ll be accessing AppLocalizations in various parts of our code, let’s incorporate it at multiple points. To do this, we’ll create an instance variable named `_locale` and initialize it within the `didChangeDependencies()` method.
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
late AppLocalizations _local;
@override
void didChangeDependencies() {
_local = AppLocalizations.of(context);
super.didChangeDependencies();
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_local.appTitle),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 32),
child: Text(
_local.appDescription('Flutter'),
style: const TextStyle(fontSize: 24),
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text("You have pushed this button :",
style: Theme.of(context).textTheme.headline6),
Text(
"$_counter",
style: Theme.of(context).textTheme.headline4,
)
],
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: "Increment",
child: const Icon(Icons.add),
),
);
}
}
Run the app and you’ll see that no matter what language your device is set to, the word “Flutter” will always be in English.
In the realm of localization, managing singular and plural forms is a common task. For instance, expressions like “Congrats, you won a coupon” versus “Congrats , you won two coupons” highlight the need to handle both singular and plural cases. It’s crucial to recognize that various languages have distinct rules for plurals. As a result, navigating plural forms requires careful consideration, and a good understanding of the target language is essential during the translation process.
{
"appTitle": "Demo App",
"appDescription": "{flutter} is Awesome",
"@appDescription": {
"placeholders": {
"flutter": {
"type": "String",
"example": "Flutter"
}
}
},
"counterText": "You have pushed this button :",
"counter": "{count,plural, =0{0} =1{1 time} other{{count} times}}",
"@counter": {
"placeholders": {
"count": {
"type": "int",
"example": "count"
}
}
},
"counterButtonText": "Increment"
}
{
"appTitle": "डेमो ऐप",
"appDescription": "{flutter} बहुत बढ़िया है",
"counterText": "आपने यह बटन दबा दिया है :",
"counter": "{count,plural, =0{0} =1{1 बार} other{{count} बार}}",
"counterButtonText": "जोड़ें"
}
Now that we’ve added plurals in the key counter for both of our ARB files and also added counterButtonText that will be used as tooltip for the increment button, we can write this:
class MyHomePage extends StatefulWidget {
const MyHomePage({Key? key}) : super(key: key);
@override
State<MyHomePage> createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
late AppLocalizations _local;
@override
void didChangeDependencies() {
_local = AppLocalizations.of(context);
super.didChangeDependencies();
}
void _incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
title: Text(_local.appTitle),
),
body: Center(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: <Widget>[
Padding(
padding: const EdgeInsets.only(top: 32),
child: Text(
_local.appDescription('Flutter'),
style: const TextStyle(fontSize: 24),
),
),
Expanded(
child: Column(
mainAxisAlignment: MainAxisAlignment.center,
children: [
Text(_local.counterText,
style: Theme.of(context).textTheme.headline6),
Text(
_local.counter(_counter),
style: Theme.of(context).textTheme.headline4,
)
],
),
),
],
),
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: _local.counterButtonText,
child: const Icon(Icons.add),
),
);
}
}
Notice how we’re passing the _counter value to the AppLocalizations.counter(), which will eventually check whether the value is singular or plural, and it will return a String value based on that.
In this Flutter MultiLanguage tutorial, we learned how to make a Flutter counter app that can speak different languages. We started by setting up our project and making it ready for translation. We also made sure our app can handle different languages by automatically generating the necessary code.
Then, we explored a cool feature called interpolation, which lets us smoothly mix different languages. For example, we can keep certain words, like “Flutter,” in English, no matter what language the user chooses. Lastly, we tackled the challenge of dealing with singular and plural forms in different languages, making our app even more adaptable.
How to Effectively Hire and Manage a Remote Team of Developers.
Download NowThe Mindbowser team's professionalism consistently impressed me. Their commitment to quality shone through in every aspect of the project. They truly went the extra mile, ensuring they understood our needs perfectly and were always willing to invest the time to...
CTO, New Day Therapeutics
I collaborated with Mindbowser for several years on a complex SaaS platform project. They took over a partially completed project and successfully transformed it into a fully functional and robust platform. Throughout the entire process, the quality of their work...
President, E.B. Carlson
Mindbowser and team are professional, talented and very responsive. They got us through a challenging situation with our IOT product successfully. They will be our go to dev team going forward.
Founder, Cascada
Amazing team to work with. Very responsive and very skilled in both front and backend engineering. Looking forward to our next project together.
Co-Founder, Emerge
The team is great to work with. Very professional, on task, and efficient.
Founder, PeriopMD
I can not express enough how pleased we are with the whole team. From the first call and meeting, they took our vision and ran with it. Communication was easy and everyone was flexible to our schedule. I’m excited to...
Founder, Seeke
Mindbowser has truly been foundational in my journey from concept to design and onto that final launch phase.
CEO, KickSnap
We had very close go live timeline and Mindbowser team got us live a month before.
CEO, BuyNow WorldWide
If you want a team of great developers, I recommend them for the next project.
Founder, Teach Reach
Mindbowser built both iOS and Android apps for Mindworks, that have stood the test of time. 5 years later they still function quite beautifully. Their team always met their objectives and I'm very happy with the end result. Thank you!
Founder, Mindworks
Mindbowser has delivered a much better quality product than our previous tech vendors. Our product is stable and passed Well Architected Framework Review from AWS.
CEO, PurpleAnt
I am happy to share that we got USD 10k in cloud credits courtesy of our friends at Mindbowser. Thank you Pravin and Ayush, this means a lot to us.
CTO, Shortlist
Mindbowser is one of the reasons that our app is successful. These guys have been a great team.
Founder & CEO, MangoMirror
Kudos for all your hard work and diligence on the Telehealth platform project. You made it possible.
CEO, ThriveHealth
Mindbowser helped us build an awesome iOS app to bring balance to people’s lives.
CEO, SMILINGMIND
They were a very responsive team! Extremely easy to communicate and work with!
Founder & CEO, TotTech
We’ve had very little-to-no hiccups at all—it’s been a really pleasurable experience.
Co-Founder, TEAM8s
Mindbowser was very helpful with explaining the development process and started quickly on the project.
Executive Director of Product Development, Innovation Lab
The greatest benefit we got from Mindbowser is the expertise. Their team has developed apps in all different industries with all types of social proofs.
Co-Founder, Vesica
Mindbowser is professional, efficient and thorough.
Consultant, XPRIZE
Very committed, they create beautiful apps and are very benevolent. They have brilliant Ideas.
Founder, S.T.A.R.S of Wellness
Mindbowser was great; they listened to us a lot and helped us hone in on the actual idea of the app. They had put together fantastic wireframes for us.
Co-Founder, Flat Earth
Ayush was responsive and paired me with the best team member possible, to complete my complex vision and project. Could not be happier.
Founder, Child Life On Call
The team from Mindbowser stayed on task, asked the right questions, and completed the required tasks in a timely fashion! Strong work team!
CEO, SDOH2Health LLC
Mindbowser was easy to work with and hit the ground running, immediately feeling like part of our team.
CEO, Stealth Startup
Mindbowser was an excellent partner in developing my fitness app. They were patient, attentive, & understood my business needs. The end product exceeded my expectations. Thrilled to share it globally.
Owner, Phalanx
Mindbowser's expertise in tech, process & mobile development made them our choice for our app. The team was dedicated to the process & delivered high-quality features on time. They also gave valuable industry advice. Highly recommend them for app development...
Co-Founder, Fox&Fork