If you're a mobile developer, you might already be familiar with Flutter, Google's open-source UI toolkit for building beautiful, natively compiled applications for mobile, web, and desktop from a single codebase.
Initially known as "Sky" and exclusive to Android, Flutter now supports iOS, Linux, Mac, Windows, and Google Fuchsia. An essential aspect of using Flutter is mastering Flutter localization to make your app accessible to a global audience.
Integrating software internationalization practices can enhance your Flutter localization process, ensuring your app meets the diverse needs of users across different regions.
A well-structured localization process ensures seamless integration of translations and cultural adaptations within the app, optimizing user experience across all markets.
In this article, you'll learn how to easily translate your Flutter application into multiple languages, leveraging effective translation management techniques to streamline the process.
You might be also interested in checking out our Flutter SDK, which offers over-the-air support for your apps.
ARB files work well when you are managing one or two languages, but keeping them synchronized becomes harder as your app supports more locales and ships more frequently. A software localization platform like Lokalise connects to GitHub, GitLab, Bitbucket, or Azure Repos and can automatically sync ARB files on every push.
When new strings are added, Lokalise can translate them with AI and send only flagged content for human review. This keeps translation work connected to the development workflow instead of requiring teams to manage ARB files manually between releases.
For live updates, the Lokalise Flutter SDK is a drop-in replacement for AppLocalizations generated by flutter gen-l10n. It enables over-the-air (OTA) delivery of translation updates across iOS, Android, web, and desktop without requiring a new app store release.
Flutter localization and internationalization
Prerequisites for Flutter localization
In this article, we'll explore how to add Flutter i18n and l10n to your application. This tutorial assumes you have basic knowledge of Flutter and Dart.
If you haven't installed Flutter yet, one of the easiest ways to get started is through Visual Studio Code. Install the Flutter extension, open the Command Palette (Ctrl+Shift+P on Windows or Linux, Cmd+Shift+P on macOS), and select Flutter: New Project. Follow the prompts to install the Flutter SDK if necessary, then create an empty application named flutter_i18n_demo. You can also follow the official Flutter installation guide for detailed setup instructions.
Alternatively, you can create the same minimal project from the command line:
flutter create flutter_i18n_demo --empty
Open the project in your editor. We also need some content to demonstrate the Flutter localization process, so let's make a few more preparations.
To set up localization, add the flutter_localizations and intl packages. flutter_localizations provides Flutter's built-in localization support, while intl provides locale-aware formatting for values such as dates and numbers.
This configuration tells Flutter that our ARB files will live in lib/l10n, that app_en.arb is the template file containing the source messages, and that the generated localization class should be written to app_localizations.dart.
Flutter generates the localization code in the directory specified by arb-dir unless you configure a separate output-dir. You can find all available l10n.yaml options in the official Flutter documentation.
With the localization tooling configured, we can create our first ARB files.
Creating Flutter translation files
ARB files and Flutter localization
Flutter's localization workflow uses ARB files to store localizable strings and their metadata. ARB (Application Resource Bundle) files use a JSON-based format and can include descriptions, placeholders, and other information used during localization.
Create an l10n directory inside lib. Then create two files:
lib/l10n/app_en.arblib/l10n/app_ru.arb
We'll use app_en.arb as the template file. Add the following content:
@@locale identifies the locale represented by the file.
appTitle and welcome are message keys. Flutter uses these keys to generate Dart getters that we can access from the app.
@appTitle contains metadata for the appTitle message. Metadata isn't required for every simple message, but descriptions give translators useful context.
The translated ARB file doesn't need to repeat the metadata from the template file.
Generating Dart localization files
Flutter uses the ARB files to generate Dart classes that expose the localized messages to your application. To generate them manually, run this command from the project root:
flutter gen-l10n
With our l10n.yaml configuration, Flutter generates the localization files inside lib/l10n. You should see files including:
app_localizations.dart contains the main AppLocalizations class, information about the supported locales, and localization delegates. The locale-specific files contain the generated implementations for English and Russian.
You don't normally need to run flutter gen-l10n after every change. Flutter also runs localization code generation as part of commands such as flutter run and flutter pub get.
Don't edit the generated Dart files manually. Any changes will be overwritten the next time the localization code is generated.
Performing simple translations
Now that we have English and Russian ARB files, we can use the generated localization classes in the app.
Translating the app title
Open lib/app/my_app.dart and import the generated AppLocalizations class:
localizationsDelegates provides the localized resources used by the app, while supportedLocales tells Flutter which locales are available. Both values are generated automatically from our localization setup.
To localize the application title as well, add onGenerateTitle:
The appTitle property comes from the appTitle key in our ARB files. Flutter generates getters and methods from these keys, giving us type-safe access to localized messages in Dart.
Displaying the welcome message
Now open lib/app/pages/my_home_page.dart and import AppLocalizations:
Flutter selects a locale from AppLocalizations.supportedLocales based on the locale of the platform running the app. If the platform uses Russian, welcome resolves to Добро пожаловать!; otherwise, our English translation is used as the fallback.
The remaining hardcoded strings will be localized in the following sections as we introduce placeholders, formatting, and pluralization.
Interpolation and placeholders in Flutter translations
Inserting dynamic values
Localized messages often need to include values that are only known at runtime. Flutter handles these values with placeholders defined in ARB files.
Suppose we want to display a message containing a company name. Update lib/l10n/app_en.arb:
The {company} value is a placeholder. Its metadata tells Flutter that the generated localization method should accept a String value and provides an example that can also help translators understand the message.
Add the corresponding translation to lib/l10n/app_ru.arb:
Flutter generates createdBy as a method because the message contains a placeholder. Passing 'Lokalise' produces Tutorial by Lokalise in English and Руководство от Lokalise in Russian.
Displaying the current locale
Placeholders can also be used with values obtained from the app itself. Let's display the locale Flutter is currently using.
Localizations.localeOf(context) returns the locale currently used by the app. Calling toLanguageTag() converts it to a standard locale identifier such as en, en-US, or ru.
Finally, import the widget in lib/app/pages/my_home_page.dart:
Next, add the following message to lib/l10n/app_en.arb:
"currentDate": "Today is {date}","@currentDate": { "description": "Displays the current date", "placeholders": { "date": { "type": "DateTime", "format": "yMMMMEEEEd" } }}
The DateTime placeholder tells Flutter to format the value using the intl package. The yMMMMEEEEd format produces a locale-aware date containing the year, month, weekday, and day.
Add the corresponding translation to app_ru.arb:
"currentDate": "Сегодня {date}"
Regenerate the localization classes:
flutter gen-l10n
The same DateTime value can now be displayed differently depending on the active locale.
Localizing numbers and currencies
Numbers and currency values also have locale-specific formatting rules. Flutter's localization generator uses the NumberFormat class from the intl package to format numeric placeholders.
Add another message to the Column in my_home_page.dart:
Here, format: "currency" tells Flutter to use locale-aware currency formatting. We explicitly set the currency to USD and request two digits after the decimal separator, while the active locale determines how the resulting value is formatted.
Add the Russian translation to app_ru.arb:
"currencyDemo": "Пример цены: {value}"
Run the localization generator again:
flutter gen-l10n
The numeric value and currency remain the same, but their presentation follows the formatting conventions of the active locale.
Pluralization
Pluralization lets a localized message change according to a numeric value. Our app already has a counter, so we can use it to display different messages depending on how many times the button has been pressed.
First, replace the remaining hardcoded counter strings in lib/app/pages/my_home_page.dart:
Next, add the following messages to lib/l10n/app_en.arb:
"pressButton": "Press the button below","buttonPressed": "{count, plural, =0{Not pressed yet} one{Pressed {count} time} other{Pressed {count} times}}","@buttonPressed": { "description": "Shows how many times the button has been pressed", "placeholders": { "count": { "type": "num" } }}
The plural expression selects a message based on the value of count. Here, =0 handles zero explicitly, one handles the singular form, and other provides the remaining English plural form.
Russian uses more plural categories than English, so the same numeric value can require different word forms depending on the count. Flutter selects the appropriate plural category according to the active locale.
Regenerate the localization classes:
flutter gen-l10n
The buttonPressed message is generated as a method that accepts the count:
As the counter changes, Flutter selects the appropriate localized plural form automatically.
Switching the locale programmatically
By default, Flutter chooses the app locale based on the user's system settings and the locales listed in supportedLocales. We can also let users select a language directly inside the app.
For this simple example, we don't need an additional state management package. We'll store the selected locale in the root widget and rebuild MaterialApp when it changes.
Storing the selected locale
Open lib/app/my_app.dart and convert MyApp from a StatelessWidget to a StatefulWidget:
The _locale value starts as null, so Flutter initially uses the locale selected from the user's system settings. When _setLocale receives one of our supported locales, setState rebuilds MaterialApp with the selected locale.
Localizations.localeOf(context) gives us the locale currently used by the app. The dropdown is built from AppLocalizations.supportedLocales, so it automatically reflects the locales generated from our ARB files.
When a user selects an item, the widget passes the selected Locale back to the root widget, which updates MaterialApp.locale.
Displaying the language switcher
Next, update MyHomePage so it can receive the locale change callback.
In lib/app/pages/my_home_page.dart, update the widget class:
class MyHomePage extends StatefulWidget { const MyHomePage({ super.key, required this.onLocaleChanged, }); final ValueChanged<Locale> onLocaleChanged; @override State<MyHomePage> createState() => _MyHomePageState();}
Run the app and select en or ru from the menu. MaterialApp rebuilds with the selected locale, and the localized strings, date formatting, number formatting, and plural rules update accordingly.
Use Lokalise for Flutter localization
Managing ARB files manually is manageable for a small app, but the process becomes harder as you add languages, contributors, and more frequent releases. Lokalise is a translation management platform that supports Flutter ARB files and brings translation, review, collaboration, and file management into one place.
Lokalise can also connect to your code repository through integrations with services such as GitHub, GitLab, Bitbucket, and Azure Repos. This can reduce manual file exchange in a production workflow. For this tutorial, however, we'll upload and download the ARB files manually so you can see each step.
Uploading ARB translation files
Create a Lokalise account or sign in, then click New project from the projects dashboard.
Choose the Web and mobile project type. Give the project a name, set English as the Base language, and add the languages you want to support as Target languages.
Our Flutter app already contains English and Russian translations, and we'll add French in this example. Therefore, select Russian and French as target languages and create the project.
Open the Upload page.
Select the ARB files from lib/l10n:
app_en.arbapp_ru.arb
Upload the ARB source files only; the generated app_localizations*.dart files are not translation resources and should not be uploaded.
Because our buttonPressed message uses an ICU plural expression, enable Detect ICU plurals during the import. This tells Lokalise to recognize the message as a plural key rather than ordinary text.
Lokalise detects language codes in filenames such as app_en.arb and app_ru.arb and replaces them with the %LANG_ISO% placeholder for the project filename. Verify that the resulting filename follows a pattern such as:
app_%LANG_ISO%.arb
When translations are exported later, %LANG_ISO% is replaced with the corresponding language code.
Click Import files, then open the project editor to review the imported keys and translations.
Lokalise handles plural forms according to the rules of each language. If you need a plural form that isn't enabled for a particular language, open that language's settings and configure Custom plural forms.
Translating Flutter strings with Lokalise AI
You can translate strings manually in the editor or create an AI translation task. For this example, we'll use Lokalise AI to generate the French translations.
In the editor, select the keys you want to translate.
Choose Create task from the bulk actions menu.
Select Automatic translation as the task type, give the task a name, and optionally provide instructions describing the content and where it is used.
Next, choose English as the Source language and French as the Target language. Review the task scope and create the task.
Lokalise AI processes the selected strings and writes the generated translations back to the project. Once the task is complete, return to the editor and review the French translations before exporting them.
Lokalise AI supports plural translation keys and can generate the plural forms required by the target language.
Downloading the translated ARB files
Once the translations are ready, open Download from the project menu and choose Flutter (.arb) as the file format.
Select the languages you want to export. If you only need the newly added French translation, select French.
Make sure the exported file structure preserves the locale placeholder so that the French file is generated as:
app_fr.arb
Then click Build and download.
Extract the downloaded ARB file into your project's lib/l10n directory. The directory should now contain:
app_en.arbapp_ru.arbapp_fr.arb
Regenerate the Flutter localization classes:
flutter gen-l10n
Run the app again. Because AppLocalizations.supportedLocales is generated from the available ARB files, French is now included automatically and appears in the language switcher we created earlier.
Over-the-air (OTA) translation updates in Flutter
Downloading ARB files works well when translations should ship with a regular application release. For smaller copy changes, Lokalise also provides a Flutter SDK that can retrieve updated translations over the air (OTA).
The SDK uses its own generated localization class as a replacement for the AppLocalizations class we've used so far. Once configured, the app can download a published Lokalise OTA bundle when it starts instead of requiring every translation change to be bundled into a new application release.
Generate a Flutter SDK bundle
Before changing the Flutter code, prepare an OTA bundle in Lokalise.
First, make sure that the translation keys you want to include in the bundle are assigned to the Other platform. Flutter SDK bundles only include keys assigned to this platform.
Then open Download in your Lokalise project and select Flutter SDK as the file format. This is different from the Flutter (.arb) format we used earlier: .arb downloads are intended for files stored in the project, while the Flutter SDK format creates an OTA bundle.
Click Build only.
Lokalise creates the bundle and registers it with the OTA service. You can manage generated bundles under: Project settings → OTA Bundles → Flutter SDK.
The first generated bundle is assigned both the Production and Prerelease tags. New bundles become Prerelease by default, allowing you to test them before moving the Production tag to the new version.
When you change a bundle tag, click Save changes.
Generate an SDK token
The Flutter SDK needs an SDK token to connect your app to the Lokalise OTA service.
Open your Lokalise project settings and find the SDK token section. Click Generate new token, then copy the generated value.
The SDK token belongs to a single Lokalise project and is different from a Lokalise API token. Make sure you use the SDK token when initializing the Flutter SDK.
You'll also need your Lokalise Project ID, which you can find in the same project's settings.
We'll use placeholders for both values in the code:
YOUR_PROJECT_IDYOUR_SDK_TOKEN
Install the Lokalise Flutter SDK
From the Flutter project root, install the SDK:
flutter pub add lokalise_flutter_sdk
The SDK uses its own localization code generator. Because our template file is named app_en.arb rather than the SDK's default intl_en.arb, create a lok-l10n.yaml file in the project root:
The main generated class is Lt. It serves the same purpose as the AppLocalizations class generated earlier by flutter gen-l10n, while also integrating with Lokalise OTA.
From this point, when you change the local ARB files, regenerate these classes with:
dart run lokalise_flutter_sdk:gen-lok-l10n
Replace AppLocalizations with the Lokalise SDK class
Replace YOUR_PROJECT_ID and YOUR_SDK_TOKEN with the values from your Lokalise project.
The preRelease: true option tells the SDK to use the bundle tagged as Prerelease. This is useful while testing OTA updates. For a production release, use the production bundle instead and manage which bundle is served through the Production tag or bundle freezes.
Download updated translations
Initializing the SDK connects the app to the OTA service, but we also need to request the latest bundle.
Our MyHomePage is already a StatefulWidget, so we can request an update when the page starts.
Lokalise.instance.update() checks the OTA service for the appropriate bundle and downloads new translations when an update is available. Calling it when the app starts also provides a simple retry mechanism if a previous request failed.
For applications that can remain open for long periods, you can additionally call update() when the application resumes from the background.
Test an OTA translation update
Now we can test the complete OTA flow.
Open your Lokalise project and change an existing translation—for example, modify the welcome message.
Then:
Open Download.
Select Flutter SDK.
Click Build only.
Open Project settings → OTA Bundles → Flutter SDK.
Confirm that the new bundle has the Prerelease tag.
Because our test app uses:
preRelease: true
restart the application. When Lokalise.instance.update() runs, the SDK retrieves the latest prerelease bundle and the updated translation appears in the app without changing or downloading the local ARB files.
Once the update is ready for production, move the Production tag to the desired bundle and save the changes.
For more control, Lokalise also supports bundle freezes, which let you restrict an OTA bundle to particular application versions. The Flutter SDK reads the application version from the version field in pubspec.yaml.
Important:Lt.supportedLocales is generated from the ARB locales included in the Flutter project. OTA is therefore best suited to updating translations for locales already included in the application. Adding a completely new supported locale requires updating the local ARB files and regenerating the localization code before that locale can appear in Lt.supportedLocales.
Conclusion
In this tutorial, you learned how to localize a Flutter app with ARB files, generate localization code, handle pluralization and formatting, and switch locales programmatically. You also saw how to add another language with AI-assisted translation.
As your Flutter project grows beyond one or two languages, managing ARB files manually can become harder to maintain across releases. Lokalise can connect to your repository, automate translation with AI, and deliver translation updates to live Flutter apps via OTA across iOS, Android, web, and desktop.
Flutter localization is the process of adapting an app's UI text, date and number formats, and pluralization rules for different languages and regions. In Flutter's gen-l10n workflow, localizable strings are stored in ARB files, which Flutter uses to generate Dart localization classes.
How does Lokalise help with Flutter app localization?
Lokalise can connect to your code repository and automatically sync updated ARB files when changes are pushed. AI can translate new strings, while AI scoring and workflows can route translations that need attention to human review. The Lokalise Flutter SDK generates a localization class designed as a seamless replacement for AppLocalizations and adds support for over-the-air translation updates without requiring a new app release.
What are ARB files in Flutter?
ARB (Application Resource Bundle) files are JSON-based localization resource files used by Flutter's gen-l10n workflow. Flutter projects typically use one .arb file per locale, such as app_en.arb for English and app_de.arb for German, and flutter gen-l10n generates Dart classes that provide access to those localized strings.
Can Lokalise deliver OTA translation updates to Flutter apps?
The Lokalise Flutter SDK supports over-the-air (OTA) translation updates without requiring a new app release. It supports Flutter apps on Android, iOS, web, Windows, macOS, and Linux, allowing teams to update existing translations, fix copy, and change terminology after release. Learn more about the Lokalise Flutter SDK.
Ilya is the lead for content, documentation, and onboarding at Lokalise, where he focuses on helping engineering teams build reliable internationalization workflows. With a background at Microsoft and Cisco, he combines practical development experience with a deep understanding of global product delivery, localization systems, and developer education.
He specializes in i18n architectures across modern frameworks — including Vue, Angular, Rails, and custom localization pipelines — and has hands-on experience with Ruby, JavaScript, Python, Elixir, Go, Rust, and Solidity. His work often centers on improving translation workflows, automation, and cross-team collaboration between engineering, product, and localization teams.
Beyond his role at Lokalise, Ilya is an IT educator and author who publishes technical guides, best-practice breakdowns, and hands-on tutorials. He regularly contributes to open-source projects and maintains a long-standing passion for teaching, making complex internationalization topics accessible to developers of all backgrounds.
Outside of work, he keeps learning new technologies, writes educational content, stays active through sports, and plays music. His goal is simple: help developers ship globally-ready software without unnecessary complexity.
Ilya is the lead for content, documentation, and onboarding at Lokalise, where he focuses on helping engineering teams build reliable internationalization workflows. With a background at Microsoft and Cisco, he combines practical development experience with a deep understanding of global product delivery, localization systems, and developer education.
He specializes in i18n architectures across modern frameworks — including Vue, Angular, Rails, and custom localization pipelines — and has hands-on experience with Ruby, JavaScript, Python, Elixir, Go, Rust, and Solidity. His work often centers on improving translation workflows, automation, and cross-team collaboration between engineering, product, and localization teams.
Beyond his role at Lokalise, Ilya is an IT educator and author who publishes technical guides, best-practice breakdowns, and hands-on tutorials. He regularly contributes to open-source projects and maintains a long-standing passion for teaching, making complex internationalization topics accessible to developers of all backgrounds.
Outside of work, he keeps learning new technologies, writes educational content, stays active through sports, and plays music. His goal is simple: help developers ship globally-ready software without unnecessary complexity.
An SRT file is a plain text file used to add subtitles to videos. It’s one of the simplest and most common formats out there. If you’ve ever turned on captions on a YouTube video, there’s a good chance it was using an SRT file behind the scenes. People use SRT files for all kinds of things: social media clips, online courses, interviews, films, you name it. They’re easy to make, easy to edit, and they work pretty much everywhere without hassle. In this post, we’ll
Libraries and frameworks to translate JavaScript apps
In our previous discussions, we explored localization strategies for backend frameworks like Rails and Phoenix. Today, we shift our focus to the front-end and talk about JavaScript translation and localization. The landscape here is packed with options, which makes many developers a