• Home
  • Insight
  • Blog
  • Business
  • Entertainment
  • Health
  • Politics
  • Shop
    • Gift Shop
    • Value Shop
    • Store
    • Bargain Shop
    • Discount
  • Sports
  • Tech
  • Travel
  • USA
  • Video
  • World
    • Asia
    • Africa
    • South America
    • North America
    • Europe
    • Oceania
Thursday, July 30, 2026
No Result
View All Result
Subscribe Now
  • Home
  • Insight
  • Blog
  • Business
  • Entertainment
  • Health
  • Politics
  • Shop
    • Gift Shop
    • Value Shop
    • Store
    • Bargain Shop
    • Discount
  • Sports
  • Tech
  • Travel
  • USA
    TX Rep. Brandon Gill Mocks James Talarico Over Fauci Action Figure Post

    TX Rep. Brandon Gill Mocks James Talarico Over Fauci Action Figure Post

    NBA players are ‘being taught’ to flop

    NBA players are ‘being taught’ to flop

    Industry was warned for years about chemical ‘runaway’ dangers. Then came near-catastrophe in O.C.

    Industry was warned for years about chemical ‘runaway’ dangers. Then came near-catastrophe in O.C.

    Trump administration seeks government-wide NDAs to stop leaks : NPR

    Trump administration seeks government-wide NDAs to stop leaks : NPR

    Dog discharges shotgun inside truck, striking woman at Nebraska traffic light

    Dog discharges shotgun inside truck, striking woman at Nebraska traffic light

    Toshifumi Suzuki, Japan’s ‘God’ of Convenience Stores, Dies at 93

    Toshifumi Suzuki, Japan’s ‘God’ of Convenience Stores, Dies at 93

     Judge Sanctioned CoreCivic for Destroying Video in ICE Death Suit

     Judge Sanctioned CoreCivic for Destroying Video in ICE Death Suit

    Suspect dead after opening fire near White House security checkpoint, Secret Service says

    Suspect dead after opening fire near White House security checkpoint, Secret Service says

    Trump trashes Colbert’s high-rated finale as “no ratings”

    Trump trashes Colbert’s high-rated finale as “no ratings”

  • Video
  • World
    • Asia
    • Africa
    • South America
    • North America
    • Europe
    • Oceania
The Insight Post
  • Home
  • Insight
  • Blog
  • Business
  • Entertainment
  • Health
  • Politics
  • Shop
    • Gift Shop
    • Value Shop
    • Store
    • Bargain Shop
    • Discount
  • Sports
  • Tech
  • Travel
  • USA
    TX Rep. Brandon Gill Mocks James Talarico Over Fauci Action Figure Post

    TX Rep. Brandon Gill Mocks James Talarico Over Fauci Action Figure Post

    NBA players are ‘being taught’ to flop

    NBA players are ‘being taught’ to flop

    Industry was warned for years about chemical ‘runaway’ dangers. Then came near-catastrophe in O.C.

    Industry was warned for years about chemical ‘runaway’ dangers. Then came near-catastrophe in O.C.

    Trump administration seeks government-wide NDAs to stop leaks : NPR

    Trump administration seeks government-wide NDAs to stop leaks : NPR

    Dog discharges shotgun inside truck, striking woman at Nebraska traffic light

    Dog discharges shotgun inside truck, striking woman at Nebraska traffic light

    Toshifumi Suzuki, Japan’s ‘God’ of Convenience Stores, Dies at 93

    Toshifumi Suzuki, Japan’s ‘God’ of Convenience Stores, Dies at 93

     Judge Sanctioned CoreCivic for Destroying Video in ICE Death Suit

     Judge Sanctioned CoreCivic for Destroying Video in ICE Death Suit

    Suspect dead after opening fire near White House security checkpoint, Secret Service says

    Suspect dead after opening fire near White House security checkpoint, Secret Service says

    Trump trashes Colbert’s high-rated finale as “no ratings”

    Trump trashes Colbert’s high-rated finale as “no ratings”

  • Video
  • World
    • Asia
    • Africa
    • South America
    • North America
    • Europe
    • Oceania
No Result
View All Result
No Result
View All Result
Home Mobile

Localising Flutter applications and automating the localisation process

by Theinsightpost
August 15, 2022
in Mobile
0 0
0
Localising Flutter applications and automating the localisation process


Written by Bram De Coninck

Developing a small application or PoC in one language is acceptable, but as soon as you release an app to the public you might want to consider localising it first. I live in Belgium, a country best known for its chocolate, waffles, fries and beer. We have three official languages: Dutch, French and German. We also have English as an unofficial language. If I want to publish an app and I want as many Belgians as possible to be able to use it, it’ll definitely need to support these languages.

The more languages and locales your application supports, the bigger your target audience will be.

In this article I will guide you through localising your Flutter application using .arb (Application Resource Bundle) files and the Intl package. I will also show you how you can automatically provide your app with the latest translations using the Loco tool. I’ve written a shell script that can be run locally or used by a CI/CD tool of choice. I’ll be using Codemagic because it’s easy to set up and visualise.

Adding the dependencies to the project

Before you can localise your Flutter app, you’ll first need to add some dependencies to the pubspec.yaml file of your project.

dependencies:
  flutter:
    sdk: flutter

  flutter_localizations:
    sdk: flutter

dev_dependencies:
  flutter_test:
    sdk: flutter

  intl_translation: ^0.17.7

Localisation in the MaterialApp

Most of the setup for the localisation happens in the MaterialApp, which is the heart of your Flutter app. It doesn’t necessarily have to be a MaterialApp though, it can also be a CupertinoApp or a WidgetsApp. This is the place where the supported locales are defined and is decided which locale is going to be used in the app. It’s also where we’ll add our own localisations delegate, which is where the rest of the magic happens.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Localisation Demo';
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HelloWorld(),
      // List of all supported locales (language code followed by country code).
      // For this example, I'll be using Belgian English, Belgian Dutch and Belgian French.
      // The order of the supported locales is very important because a fallback mechanism is used.
      supportedLocales: const [
        Locale('en', 'BE'),
        Locale('nl', 'BE'),
        Locale('fr', 'BE'),
      ],
      // These delegates are responsible for loading the translations of the selected locale.
      localizationsDelegates: [
        // This is where all translations are defined, will be added later.
        const AppLocalizationsDelegate(),
        // Built-in delegate for the localisation of the Material widgets (e.g. tooltips).
        GlobalMaterialLocalizations.delegate,
        // Built-in localisation for text direction (left-to-right or right-to-left).
        GlobalWidgetsLocalizations.delegate
      ],
      // This is where it's decided which locale should be used.
      localeResolutionCallback: (Locale locale, Iterable<Locale> supportedLocales) {
        for (final supportedLocale in supportedLocales) {
          // The language of the device of the user is compared to every supported language.
          // If the language codes match, the supported locale with that language code is chosen.
          // This allows users using American English or British English as locales
          // to be able to use the Belgian English localisation.
          if (locale.languageCode == supportedLocale.languageCode) {
            return supportedLocale;
          }
        }

        // If the language of the user isn't supported, the default locale should be used.
        return supportedLocales.first;
      },
    );
  }
}

Creating the AppLocalizations class and the AppLocalizationsDelegate

The AppLocalizations class contains the logic to load the messages (= translations) of a given locale. It’s also where you’ll define all Intl messages that will be used in the app. All messages should be in the same language, the language of the ‘source locale’ of your project. In this case it’s Belgian English.

I’ve created two Intl messages: one for the app title and one for a ‘hello world’ message that we’ll use later in the UI.

class AppLocalizations {
  static Future<AppLocalizations> load(Locale locale) {
    final String name = locale.countryCode == null || locale.countryCode.isEmpty
      ? locale.languageCode
      : locale.toString();
    final String localeName = Intl.canonicalizedLocale(name);

    return initializeMessages(localeName).then((_) {
      Intl.defaultLocale = localeName;
      return AppLocalizations();
    });
  }

  // Localizations are usually accessed using the InheritedWidget "of" syntax.
  static AppLocalizations of(BuildContext context) {
    return Localizations.of<AppLocalizations>(context, AppLocalizations);
  }

  // Messages which contain a string in the default language of your app and a name as identifier.
  // CamelCasing is required for the name of the Intl message.
  String get appTitle => Intl.message('Localisation Demo', name: 'appTitle');
  String get helloWorld => Intl.message('Hello world!', name: 'helloWorld');
}

The AppLocalizationsDelegate is the glue between the AppLocalizations class and the MaterialApp. The load function returns an AppLocalizations object as it contains all localised messages. The isSupported function can be used to check if a certain locale (or in this case language code) is supported.

class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
  // As the instance of this delegate will never change, it can have a const constructor.
  const AppLocalizationsDelegate();

  // Checks whether or not a certain locale (or language code in this cast) is supported.
  // The order of the locales doesn't matter in this case.
  @override
  bool isSupported(Locale locale) => ['en', 'nl', 'fr'].contains(locale.languageCode);

  // Load the translations of a certain locale.
  @override
  Future<AppLocalizations> load(Locale locale) => AppLocalizations.load(locale);

  // Defines whether or not all the app’s widgets should be reloaded when the load method is completed.
  @override
  bool shouldReload(LocalizationsDelegate<AppLocalizations> old) => false;
}

If you copy/paste this code you’ll get an error on the initializeMessages method, as it’s not defined yet. This method will be generated later by the Intl package. Before you can start providing translations to your Flutter app, you first need to create a Loco project.

Creating the Loco project

Head over to Loco and create an account if you don’t already have one. Loco has a generous free plan which allows you to create two projects. Create a new Loco project and make sure to select the same source locale you’ve used for your Intl messages.

Next, head over to ‘Developer Tools’ and click on ‘API Keys’. This will open up a dialog in which you can create a ‘Full access key’ for your project. Make sure to save this key somewhere, as it’s impossible to retrieve it later. In case you lose this key, you’ll have to generate a new one.



How Flutter works together with Loco

Your Flutter app will interact with Loco in two ways. The Intl messages you created in the AppLocalizations class will be imported into Loco. Everytime you add or update these translations your Loco project will get an update as well. New assets will be created in your source locale or existing assets will get a new value. The second way you use Loco is by downloading all translations for all locales in .arb format and then turning these files into .dart files.

It doesn’t have to be the job of the developer to provider the translations for the app, it can also be done by a product owner or a client for example.

In your Loco project you can choose additional locales you want to support, in my case Belgian Dutch and Belgian French. Translating messages for a new locale from your source locale can be done by a very user-friendly GUI. You don’t need to be a technical person to add or edit translations.


Loco-2

Automating the localisation process

Importing messages into Loco and exporting translations from Loco can be done manually, but it’s a tedious process. These are the steps that need to be taken for the entire localisation process:

  • Generate .arb files from the Intl messages defined in the AppLocalizations class.
  • Import those .arb files into the Loco project to add/update the translations for the source locale.
  • Fetch the latest translations for all locales in .arb format from the Loco project.
  • Generate .dart files from these translations.

It’s possible to automate this process with a simple shell script I’ve provided below. The only thing you need to do is provide a value for all variables: your Loco API key, the path of the AppLocalizations class and the desired output path for the translations.

#!/usr/bin/env sh

set -e # Exit on first failed command

# Variables
LOCO_API_KEY="LOCO_API_KEY"
APP_LOCALIZATIONS_PATH="lib/app_localizations.dart"
OUTPUT_PATH="lib/l10n"

# Get packages
flutter packages get

# Generate .arb files
mkdir l10n-input
flutter pub pub run intl_translation:extract_to_arb --output-dir=l10n-input $APP_LOCALIZATIONS_PATH

# Import translations into Loco
curl -f -s --data-binary "@l10n-input/intl_messages.arb" "https://localise.biz/api/import/arb?async=true&index=id&locale=en&key=$LOCO_API_KEY"

# Export translations from Loco
curl -s -o "translated.zip" "https://localise.biz/api/export/archive/arb.zip?key=$LOCO_API_KEY"
unzip -qq "translated.zip" -d "l10n-translated"

# Create directory for output if it doesn't exist yet
if [ ! -d $OUTPUT_PATH ]
then
    mkdir $OUTPUT_PATH
fi

# Generate Dart files with translations
flutter pub pub run intl_translation:generate_from_arb --output-dir=$OUTPUT_PATH --no-use-deferred-loading $APP_LOCALIZATIONS_PATH l10n-translated/*/l10n/intl_messages_*.arb

# Cleanup
rm translated.zip
rm -rf l10n-translated
rm -rf l10n-input

It’s possible to run this script locally but you can also integrate it into your CI/CD tool of choice. I’ve chosen to use Codemagic because it’s easy to set up and use. Codemagic allows you to execute shell scripts before and after every phase of a build. It’s important to execute this script before the build phase though. I’ve decide to run the script before the test phase.


Codemagic

Using a CI/CD tool to fetch translations automatically will make sure users always have the most recent translations.

The reason it’s so useful to integrate this script into a CI/CD tool is that with every build of the application the latest translations will be fetched automatically. A developer might forget to fetch the latest translations before releasing a new version of the Flutter app to the stores and it’s possible that another person has added or updated translations on Loco.

Using localised messages in the UI

In the example below you can see how to get a localised message from the AppLocalizations class and use it in a Text widget. Try changing the language of your device without closing the app. When you open the app again, the widget will be rerendered automatically and display the message in the language you’ve selected (as long as it’s supported, obviously).

class HelloWorld extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text(AppLocalizations.of(context).helloWorld),
      ),
    );
  }
}

Using localised messages without context

Using localised messages without context is possible but it’s a bit hacky in my opinion. There’s currently no other way to do it though.

It can sometimes be useful to get a localised message without using context, in a BLoC or a ViewModel for example.

class AppLocalizations {
  static AppLocalizations current;

  AppLocalizations._(Locale locale) {
    current = this;
  }

  static Future<AppLocalizations> load(Locale locale) {
    final String name = locale.countryCode == null || locale.countryCode.isEmpty
      ? locale.languageCode
      : locale.toString();
    final String localeName = Intl.canonicalizedLocale(name);

    return initializeMessages(localeName).then((_) {
      Intl.defaultLocale = localeName;
      return AppLocalizations._(locale);
    });
  }

  // Localizations are usually accessed using the InheritedWidget "of" syntax
  static AppLocalizations of(BuildContext context) {
    return Localizations.of<AppLocalizations>(context, AppLocalizations);
  }

I’ve added a static AppLocalizations object in the AppLocalizations class. It gets its value from the private constructor, which is used in the load method. As you can see in the example below, it’s now possible to use localised messages without context.

class HelloWorld extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Text(AppLocalizations.current.helloWorld),
      ),
    );
  }
}

Localising the app title

It’s also possible to localise the name of your application by using the onGenerateTitle callback of the MaterialApp at the root of your project. Make sure you’re not using the ‘title’ property of the MaterialApp.

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      theme: ThemeData(
        primarySwatch: Colors.blue,
      ),
      home: HelloWorld(),
      // Make sure you're not using the title property!
      onGenerateTitle: (BuildContext context) => AppLocalizations.of(context).appTitle,
      ...
    );
  }
}

Conclusion

This approach makes localising your Flutter apps a lot easier. Developers create or adjust Intl messages in the source locale in the AppLocalizations class. Anyone can provide translations for those messages on Loco, it doesn’t have to be the developer. Using the provided script and a CI/CD tool of choice will make sure the Flutter app and the Loco project are always synchronised. This allows the users of the Flutter app to always have the most recent translations.

More articles by Bram De Coninck:


Bram De Coninck is a Mobile App Developer (Android, Xamarin & Flutter) born in Belgium. He recently graduated at University College Ghent in Applied Computer Science. He’s been passionate about Flutter since it was in Beta and his current mission is to start using Flutter at his workplace.



Source link

ShareTweetSend
Previous Post

Polycarp Igathe, Millicent Omanga concede defeat in Nairobi county elections

Next Post

How Putin and Erdoğan are making the West irrelevant

Related News

Announcing Codemagic Patch: an open-source CodePush rebuild for React Native
Mobile

Announcing Codemagic Patch: an open-source CodePush rebuild for React Native

July 30, 2026
React Native OTA Updates: What You Can (and Can’t) Deploy Over the Air
Mobile

React Native OTA Updates: What You Can (and Can’t) Deploy Over the Air

May 28, 2026
Your phone drawer hides an app designed to “exploit developing brains”
Mobile

Your phone drawer hides an app designed to “exploit developing brains”

May 27, 2026
iPhone 16 has a secret charging upgrade Apple didn’t even mention
Mobile

iPhone 16 has a secret charging upgrade Apple didn’t even mention

May 26, 2026
Next Post
How Putin and Erdoğan are making the West irrelevant

How Putin and Erdoğan are making the West irrelevant

Discussion about this post

Subscribe To Our Newsletters

    Customer Support


    1251 Wilcrest Drive
    Houston, Texas
    77042 USA
    Call-832.795.1420
    e-mail – news@theinsightpost.com

    Subscribe To Our Newsletters

      Categories

      • Africa
      • Africa-East
      • African Sports
      • American Sports
      • Arts
      • Asia
      • Australia
      • Business
      • Business Asia
      • Business- Africa
      • Canada
      • Defense
      • Education
      • Egypt
      • Energy
      • Entertainment
      • Europe
      • European Soccer
      • Finance
      • Germany
      • Ghana
      • Health
      • Insight
      • International
      • Investing
      • Japan
      • Latest Headlines
      • Life & Living
      • Markets
      • Mobile
      • Movies
      • New Zealand
      • Nigeria
      • Politics
      • Scholarships
      • Science
      • South Africa
      • South America
      • Sports
      • Tech
      • Travel
      • UK
      • USA
      • Weather
      • World
      No Result
      View All Result

      Recent News

      US Navy to overhaul torpedo stockpile under new two-war strategy

      US Navy to overhaul torpedo stockpile under new two-war strategy

      July 30, 2026
      Free Cloud Computing Course | AWS Re/Start Program 

      Free Cloud Computing Course | AWS Re/Start Program 

      July 30, 2026
      Thirty Years Later

      Thirty Years Later

      July 30, 2026
      Announcing Codemagic Patch: an open-source CodePush rebuild for React Native

      Announcing Codemagic Patch: an open-source CodePush rebuild for React Native

      July 30, 2026
      • Home
      • Advertise With Us
      • About Us
      • Corporate
      • Consumer Rewards
      • Forum
      • Privacy Policy
      • Social Trends

      Theinsightpost ©2026 | All Rights Reserved. Theinsightpost is an Elnegy LLC company, registered in Texas, USA

      Welcome Back!

      Login to your account below

      Forgotten Password?

      Retrieve your password

      Please enter your username or email address to reset your password.

      Log In

      Add New Playlist

      We are using cookies to give you the best experience on our website.

      You can find out more about which cookies we are using or switch them off in .

      No Result
      View All Result
      • Home
      • Insight
      • Blog
      • Business
      • Entertainment
      • Health
      • Politics
      • Shop
        • Gift Shop
        • Value Shop
        • Store
        • Bargain Shop
        • Discount
      • Sports
      • Tech
      • Travel
      • USA
      • Video
      • World
        • Asia
        • Africa
        • South America
        • North America
        • Europe
        • Oceania

      Theinsightpost ©2026 | All Rights Reserved. Theinsightpost is an Elnegy LLC company, registered in Texas, USA

      The Insight Post
      Powered by  GDPR Cookie Compliance
      Privacy Overview

      This website uses cookies so that we can provide you with the best user experience possible. Cookie information is stored in your browser and performs functions such as recognising you when you return to our website and helping our team to understand which sections of the website you find most interesting and useful.

      Strictly Necessary Cookies

      Strictly Necessary Cookie should be enabled at all times so that we can save your preferences for cookie settings.

      Cookie Policy

      More information about our Cookie Policy