Over-the-air localization SDK for Flutter apps. No more unnecessary app updates.
Localizely allows you to update translations for your Flutter applications over the air. There's no need to release a new version to the App Store or Google Play. With Localizely, you can instantly push updates for text translations in your mobile apps.
Most common use cases include correcting typos, optimizing texts, or updating translations on the fly.
The most commonly used approach for localizing Flutter apps involves the use of the intl package, along with tools that generate the necessary localization code. In the following sections, we will explain how to integrate Over-the-Air (OTA) translation updates into such Flutter projects.
The gen_l10n
tool represents a relatively new approach for localizing Flutter applications. This tool is part of the Flutter SDK and does not require any additional installations. We have already covered the steps for setting up and localizing apps using the gen_l10n tool. Therefore, in the following sections, we will expand on this topic and demonstrate how to enable Over-the-Air updates in such projects.
The sample Flutter app with Over-the-Air integration is available on the GitHub repository.
To begin, generate an SDK token on the Localizely platform by navigating to the Over-the-Air page for your project. This token will be required later for configuring the Localizely SDK in your Flutter project.
Create distribution on the same Over-the-Air page for your project. Although you can create multiple distributions, typically, only one distribution per project is needed. The generated Distribution ID will also be required later for configuring the Localizely SDK.
To update translations in your app, create a new release within the distribution. This will export the current state of your project and make it available to the connected Flutter apps. For each release, you can specify the following:
By default, each release will only be visible to app builds that have initialized the Localizely SDK with the prerelease flag set to true. This allows your team to test translation changes internally before you publish them to end-users.
To integrate localizely_sdk
into your Flutter project, run the following command in your terminal:
flutter pub add localizely_sdk
This command will add a line like the following to your project's pubspec.yaml
file:
dependencies:
localizely_sdk: ^2.6.4
After adding the localizely_sdk
to your project, you need to generate the necessary localization file. To do this, run the following command:
dart run localizely_sdk:generate
Note: In Flutter 3.22.0, running the command dart run localizely_sdk:generate
may produce false analyzer errors. This issue has been resolved in Flutter 3.22.1. If you need to use 3.22.0 and encounter these errors, running flutter pub get
again should fix the problem.
To automate the code generation process whenever your .arb files change, you should add the build_runner
package to your dev_dependencies
and run the watch
command:
flutter pub add build_runner -d
flutter pub run build_runner watch
Once the required localization file is generated, update the localizationsDelegates
and supportedLocales
props of the MaterialApp
widget in your Flutter application.
...
import 'package:flutter_gen/gen_l10n/localizely_localizations.dart';
class MyApp extends StatelessWidget {
...
@override
Widget build(BuildContext context) {
return MaterialApp(
...
localizationsDelegates: LocalizelyLocalizations.localizationsDelegates,
supportedLocales: LocalizelyLocalizations.supportedLocales,
...
);
}
}
For this step, you will need your SDK token (generated in Step 1) and the Distribution ID (created in Step 2). In your main.dart
file, include the following code:
import 'package:flutter/material.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:flutter_gen/gen_l10n/localizely_localizations.dart';
import 'package:localizely_sdk/localizely_sdk.dart'; // Import sdk package
void main() {
Localizely.init('<SDK_TOKEN>', '<DISTRIBUTION_ID>'); // Init sdk
Localizely.setPreRelease(true); // Add this only if you want to use prereleases
Localizely.setAppVersion('<APP_VERSION>'); // Add this only if you want to explicitly set the application version, or in cases when automatic detection is not possible (e.g. Flutter web apps)
runApp(MaterialApp(
onGenerateTitle: (context) => AppLocalizations.of(context)!.appTitle,
localizationsDelegates: LocalizelyLocalizations.localizationsDelegates,
supportedLocales: LocalizelyLocalizations.supportedLocales,
home: HomePage()));
}
class HomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _isLoading = true;
@override
void initState() {
super.initState();
// Call 'updateTranslations' after localization delegates initialization
Localizely.updateTranslations().then(
(response) => setState(() {
_isLoading = false;
}),
onError: (error) => setState(() {
_isLoading = false;
}));
}
@override,
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(AppLocalizations.of(context)!.pageHomeTitle)),
body: Center(
child: _isLoading ? CircularProgressIndicator() : Column(children: <Widget>[Text(AppLocalizations.of(context)!.welcome)])));
}
}
Please note that tracking sensitive information, such as credentials, tokens, and similar data, in Version Control Systems (VCS) like GitHub, GitLab, and others is generally considered unsafe. For real-world projects, it is recommended to extract sensitive information, such as SDK tokens and Distribution IDs, into environment variables. You can use the flutter_dotenv package or a similar one to manage this securely.
Note: Ensure you have added INTERNET
permission for Android devices.
Note: Localizely.updateTranslations()
should be called just once on app start, as shown in the given example. Please do not call it frequently after the app starts.
Note: The Over-the-Air support for Flutter apps that use gen_l10n
tool for localization is added in the localizely_sdk 2.5.0
.
There is no need to update your code, refer to the keys as usual:
AppLocalizations.of(context)!.title
Hint: In case you can't see updated translations in the app after successful OTA retrieval, please check if you have missed some of the steps.
Some tips that can help you: check if you have created a new release with the latest changes on Localizely, check if you are working with enabled or disabled prereleases, check if you are using the same languages on the Localizely and within the app, and check if you need to rebuild widgets tree after OTA update.
There is no need to update your code, change locale as usual.
The Flutter Intl is an IDE plugin that automates the generation of boilerplate code for the intl package. We have already covered the steps for setting up and localizing apps using the Flutter Intl plugin. Therefore, in the following sections, we will expand on this topic and demonstrate how to enable Over-the-Air updates in such projects.
The sample Flutter app with Over-the-Air integration is available on the GitHub repository.
To begin, generate an SDK token on the Localizely platform by navigating to the Over-the-Air page for your project. This token will be required later for configuring the Localizely SDK in your Flutter project.
Create distribution on the same Over-the-Air page for your project. Although you can create multiple distributions, typically, only one distribution per project is needed. The generated Distribution ID will also be required later for configuring the Localizely SDK.
To update translations in your app, create a new release within the distribution. This will export the current state of your project and make it available to the connected Flutter apps. For each release, you can specify the following:
By default, each release will only be visible to app builds that have initialized the Localizely SDK with the prerelease flag set to true. This allows your team to test translation changes internally before you publish them to end-users.
To integrate localizely_sdk
into your Flutter project, run the following command in your terminal:
flutter pub add localizely_sdk
This command will add a line like the following to your project's pubspec.yaml
file:
dependencies:
localizely_sdk: ^2.6.4
After adding the localizely_sdk
to your project, you need to update your flutter_intl
section within the pubspec.yaml
file and regenerate the necessary localization code.
To do this, update your pubspec.yaml
file:
...
flutter_intl:
enabled: true
localizely:
ota_enabled: true
Note: It is required to use Flutter Intl plugin for Android Studio / VS Code or intl_utils (1.5.0 or newer) in order to generate relevant localization dart code.
For this step, you will need your SDK token (generated in Step 1) and the Distribution ID (created in Step 2). In your main.dart file, include the following code:
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:localizely_sdk/localizely_sdk.dart'; // Import sdk package
import 'generated/l10n.dart';
void main() {
Localizely.init('<SDK_TOKEN>', '<DISTRIBUTION_ID>'); // Init sdk
Localizely.setPreRelease(true); // Add this only if you want to use prereleases
Localizely.setAppVersion('<APP_VERSION>'); // Add this only if you want to explicitly set the application version, or in cases when automatic detection is not possible (e.g. Flutter web apps)
runApp(MaterialApp(
onGenerateTitle: (context) => S.of(context).appTitle,
localizationsDelegates: [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
home: HomePage()));
}
class HomePage extends StatefulWidget {
@override
State<StatefulWidget> createState() => _HomePageState();
}
class _HomePageState extends State<HomePage> {
bool _isLoading = true;
@override
void initState() {
super.initState();
// Call 'updateTranslations' after localization delegates initialization
Localizely.updateTranslations().then(
(response) => setState(() {
_isLoading = false;
}),
onError: (error) => setState(() {
_isLoading = false;
}));
}
@override,
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(title: Text(S.of(context).pageHomeTitle)),
body: Center(
child: _isLoading ? CircularProgressIndicator() : Column(children: <Widget>[Text(S.of(context).welcome)])));
}
}
Please note that tracking sensitive information, such as credentials, tokens, and similar data, in Version Control Systems (VCS) like GitHub, GitLab, and others is generally considered unsafe. For real-world projects, it is recommended to extract sensitive information, such as SDK tokens and Distribution IDs, into environment variables. You can use the flutter_dotenv package or a similar one to manage this securely.
Note: Ensure you have added INTERNET
permission for Android devices.
Note: Localizely.updateTranslations()
should be called just once on app start, as shown in the given example. Please do not call it frequently after the app starts.
Note: The localizely_sdk >=2.4.0 <2.5.0
requires an update of min platform versions:
- Android: Required Android SDK 21 or newer (update minSdkVersion
to 21
in the android/app/build.gradle
file).
- iOS: Required iOS 11 or newer.
As of version 2.5.0
, these updates are no longer required due to changes in implementation.
There is no need to update your code, refer to the keys as usual:
S.of(context).title
Hint: In case you can't see updated translations in the app after successful OTA retrieval, please check if you have missed some of the steps.
Some tips that can help you: check if you have created a new release with the latest changes on Localizely, check if you are working with enabled or disabled prereleases, check if you are using the same languages on the Localizely and within the app, and check if you need to rebuild widgets tree after OTA update.
There is no need to update your code, change locale as usual:
setState(() {
S.load(Locale('en', 'US'));
});
When a user starts an application that uses Localizely SDK for the first time on a device, it generates a unique, random Device Identifier. This identifier’s sole purpose is the tracking of active users over a given period of time. It is not used for any other form or means of tracking and does not contain any user or device information.
We will not automatically lock your OTA functionality when you exceed your MAU quota. Instead, you will be informed and you will have enough time to upgrade your OTA Add-on according to your needs.
The app will fallback to previously fetched translations from Localizely, or to embedded translations during the build time, in that order. Once the app has internet access again, it can fetch the latest translations.
Previous: Translation editor
Read next: Flutter In-Context Editing
Tired of manually editing translation files?
Our platform streamlines software localization for you.