DEV Community

Cover image for Flutter Localization
just-vicky
just-vicky

Posted on

Flutter Localization

Localization

Each day, an increasing number of people are learning English. Still, a large number of people do not understand or speak English. Even if you have a compelling app, people may not use it. They may not be fluent in English or prefer to have it available in their native language.

You may be losing the game in non-English regions, regardless of how much the app can help them. For example, if you have an app that tells the steps to perform the specific exercise and a user is unable to understand the language, the user will simply opt for another app (which may be mediocre but available in the user's language).

What is localisation?

Adapting an application to different languages and regions. This involves translating the text, images, and other resources in the app to make it accessible and understandable to users from various country backgrounds.

It is also referred to as l10n. 10 represents the count of all letters between L and N.

Localisation abreviation

Let's setup a simple project which will have two screens and we will implement a toggle between two languages(I chose English and French)

Locale app

In this we'll also be usmain.darting Provider to update the state of the two screens:

Provider

Let me give you a taste of what provider is:

  • Provider is a state management technique in Flutter.
  • Provide data from a single source to multiple widgets in your app.
  • When the data changes, Provider automatically notifies the widgets that depend on it.
  • Creates a widget tree where widgets can access the provided data without having to pass it down manually through every widget.
  • Simplifies the process of managing and sharing data across Flutter app, making your code more organized and easier to maintain.

Now let's build the app.

Let's add the dependencies in pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: any
  provider: ^6.1.2
Enter fullscreen mode Exit fullscreen mode

Then import the flutter_localizations library and specify localizationsDelegates and supportedLocales for your *MaterialApp *

main.dart

import 'package:flutter_localizations/flutter_localizations.dart';

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en'), // English
        const Locale('fr'), // French
      ],
      locale: context.watch<LocaleProvider>().locale,
      home: const HomePage(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

To add localized text to the application:

  • In the pubspec.yaml, add a generate flag and set it true.
# The following section is specific to Flutter packages.
flutter:
  generate: true

Enter fullscreen mode Exit fullscreen mode
  • Create a new file called l10n.yaml in the root directory of the file and add add the below code
arb-dir: lib/l10n
template-arb-file: app_en.arb //name can vary depending on the language used
output-localization-file: app_localizations.dart
Enter fullscreen mode Exit fullscreen mode
  • In the lib folder, create a folder named l10n and the add the 2 files as below.

    • The data written in this file is similar to JSON. So, it will be key: value pairs
  • app_en.arb

{
  "title1": "Hello World!",
  "description1": "Hello, world! Greetings from the digital realm, where innovation meets imagination.",
  "title2": "How Are You",
  "description2": "Hey newbie coder! How's the coding journey treating you? Making any byte-sized progress?",
  "title3": "It works",
  "description3": "Fantastic to hear it works! Need further tweaks or just basking in the glory of functional code? Keep coding!",
  "details": "Details page",
  "home": "Home page"
}

Enter fullscreen mode Exit fullscreen mode
  • app_fr.arb

Note:I have used the google translate to transalte the words.

{
  "title1": "Bonjour le monde!",
  "description1": "Bonjour, monde! Salutations depuis le domaine numérique, où l'innovation rencontre l'imagination.",
  "title2": "Comment ça va",
  "description2": "Salut nouveau programmeur! Comment se passe le voyage de programmation? Faites-vous des progrès en bytes?",
  "title3": "Ça marche",
  "description3": "Fantastique d'entendre que ça marche! Besoin d'autres ajustements ou juste profiter de la gloire du code fonctionnel? Continuez à coder!",
  "details": "Page de détails",
  "home": "Page d'accueil"
}

Enter fullscreen mode Exit fullscreen mode
  • Now restart the app and you'll see the files generated in the .dart_tool

Reference

  • Now in the main.dart, add a import statement and AppLocalizations.delegate
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart'; //this line


class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      localizationsDelegates: [
        AppLocalizations.delegate, //New line
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en'), // English
        const Locale('fr'), // French
      ],
      locale: context.watch<LocaleProvider>().locale,
      home: const HomePage(),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode
  • Once it is loaded, use the AppLocalizations in the app using:
Text(AppLocalizations.of(context)!.<Key of the text>)
Enter fullscreen mode Exit fullscreen mode

local_provider

  • This piece of code will update the main tree and the display the needed language when toggled.
import 'package:provider/provider.dart';


class LocaleProvider with ChangeNotifier {
  late Locale _locale = const Locale('en');

  Locale get locale => _locale;

  void toggleLocale() {
    _locale =
        _locale.languageCode == 'en' ? const Locale('fr') : const Locale('en');
    notifyListeners();
  }
}
Enter fullscreen mode Exit fullscreen mode
  • update the main() function to listen to changes using ChangeNotifierProvider.
void main() {
  runApp(
    ChangeNotifierProvider<LocaleProvider>(
      create: (_) => LocaleProvider(),
      child: MyApp(),
    ),
  );
}
Enter fullscreen mode Exit fullscreen mode

Home screen

  • In the home screen we will display a appbar with a Icon button which will toggle between languages on pressed.

  • We will also list cards with titles on it, which when pressed will display the details screen.

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.home),
        actions: [
          IconButton(
            onPressed: () {
              context.read<LocaleProvider>().toggleLocale();
            },
            tooltip: 'Language',
            icon: const Icon(Icons.language),
          )
        ],
      ),
      body: ListView(
        children: [
          buildCard(context,
              title: AppLocalizations.of(context)!.title1,
              details: AppLocalizations.of(context)!.description1),
          buildCard(context,
              title: AppLocalizations.of(context)!.title2,
              details: AppLocalizations.of(context)!.description2),
          buildCard(context,
              title: AppLocalizations.of(context)!.title3,
              details: AppLocalizations.of(context)!.description3),
          // Add more CardItems as needed
        ],
      ),
    );
  }

  Widget buildCard(BuildContext context,
      {required String title, required String details}) {
    return GestureDetector(
      onTap: () {
        Navigator.pushNamed(context, '/details',
            arguments: {'title': title, 'details': details});
      },
      child: Card(
        margin: const EdgeInsets.all(16.0),
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Text(
            title,
            style: const TextStyle(fontSize: 20.0),
          ),
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Details screen

  • In this screen, we will just display the title and description in the page.
class DetailsPage extends StatelessWidget {
  const DetailsPage({super.key});

  @override
  Widget build(BuildContext context) {
    final Map<String, String> args = ModalRoute.of(context)!.settings.arguments as Map<String, String>;
    final String title = args['title']!;
    final String details = args['details']!;
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.details),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8.0),
            Text(
              details,
              style: const TextStyle(fontSize: 16.0),
            ),
          ],
        ),
      ),
    );
  }
}
Enter fullscreen mode Exit fullscreen mode

Full code

main.dart

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'package:provider/provider.dart';


void main() {
  runApp(
    ChangeNotifierProvider<LocaleProvider>(
      create: (_) => LocaleProvider(),
      child: MyApp(),
    ),
  );
}

class MyApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      debugShowCheckedModeBanner: false,
      title: 'Flutter Demo',
      localizationsDelegates: [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en'), // English
        const Locale('fr'), // French
      ],
      locale: context.watch<LocaleProvider>().locale,
      home: const HomePage(),
      routes: {
        '/details': (context) => const DetailsPage(),
      },
    );
  }
}

class HomePage extends StatelessWidget {
  const HomePage({Key? key}) : super(key: key);

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.home),
        actions: [
          IconButton(
            onPressed: () {
              context.read<LocaleProvider>().toggleLocale();
            },
            tooltip: 'Language',
            icon: const Icon(Icons.language),
          )
        ],
      ),
      body: ListView(
        children: [
          buildCard(context,
              title: AppLocalizations.of(context)!.title1,
              details: AppLocalizations.of(context)!.description1),
          buildCard(context,
              title: AppLocalizations.of(context)!.title2,
              details: AppLocalizations.of(context)!.description2),
          buildCard(context,
              title: AppLocalizations.of(context)!.title3,
              details: AppLocalizations.of(context)!.description3),
          // Add more CardItems as needed
        ],
      ),
    );
  }

  Widget buildCard(BuildContext context,
      {required String title, required String details}) {
    return GestureDetector(
      onTap: () {
        Navigator.pushNamed(context, '/details',
            arguments: {'title': title, 'details': details});
      },
      child: Card(
        margin: const EdgeInsets.all(16.0),
        child: Padding(
          padding: const EdgeInsets.all(16.0),
          child: Text(
            title,
            style: const TextStyle(fontSize: 20.0),
          ),
        ),
      ),
    );
  }
}

class DetailsPage extends StatelessWidget {
  const DetailsPage({super.key});

  @override
  Widget build(BuildContext context) {
    final Map<String, String> args = ModalRoute.of(context)!.settings.arguments as Map<String, String>;
    final String title = args['title']!;
    final String details = args['details']!;
    return Scaffold(
      appBar: AppBar(
        title: Text(AppLocalizations.of(context)!.details),
      ),
      body: Padding(
        padding: const EdgeInsets.all(16.0),
        child: Column(
          crossAxisAlignment: CrossAxisAlignment.start,
          children: [
            Text(
              title,
              style: const TextStyle(fontSize: 24.0, fontWeight: FontWeight.bold),
            ),
            const SizedBox(height: 8.0),
            Text(
              details,
              style: const TextStyle(fontSize: 16.0),
            ),
          ],
        ),
      ),
    );
  }
}

class LocaleProvider with ChangeNotifier {
  late Locale _locale = const Locale('en');

  Locale get locale => _locale;

  void toggleLocale() {
    _locale =
        _locale.languageCode == 'en' ? const Locale('fr') : const Locale('en');
    notifyListeners();
  }
}

Enter fullscreen mode Exit fullscreen mode

Hope it helps!!

Top comments (0)