Flutter Localization
Tags: flutter, dart, learning, mobile
Published: 2024-05-17 06:12:13


![Localization](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/4jwg8xucgpjolztutci1.gif)

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](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/gdsru51iwhumtl9brtt8.png)

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`:

```javascript
dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: any
  provider: ^6.1.2
```

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

**_`main.dart`_**
```javascript
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(),
    );
  }
}
```
To add localized text to the application:

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

```
- Create a new file called `l10n.yaml` in the root directory of the file and add add the below code

```javascript
arb-dir: lib/l10n
template-arb-file: app_en.arb //name can vary depending on the language used
output-localization-file: app_localizations.dart
```

- 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`_**

 ```javascript
{
  "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"
}

 ```
- **_`app_fr.arb`_**
 
**<u>Note:I have used the google translate to transalte the words.</u>**
```javascript
{
  "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"
}

```

- Now restart the app and you'll see the files generated in the **_`.dart_tool`_**

![Reference](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/skwt6r28z4mnuh3n3c8v.gif)

- Now in the **_`main.dart`_**, add a **import** statement and **AppLocalizations.delegate**

```javascript
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(),
    );
  }
}
```

- Once it is loaded, use the **AppLocalizations** in the app using:

```javascript
Text(AppLocalizations.of(context)!.<Key of the text>)
```

###local_provider

- This piece of code will update the main tree and the display the needed language when toggled.

```javascript
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();
  }
}
```

- update the `main()` function to listen to changes using **ChangeNotifierProvider**.

```javascript
void main() {
  runApp(
    ChangeNotifierProvider<LocaleProvider>(
      create: (_) => LocaleProvider(),
      child: MyApp(),
    ),
  );
}
```
###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.**

```javascript
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),
          ),
        ),
      ),
    );
  }
}
```

### Details screen

- In this screen, we will just display the title and description in the page.

```javascript
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),
            ),
          ],
        ),
      ),
    );
  }
}
```
### Full code
**_`main.dart`_**

```javascript
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();
  }
}

```

**Hope it helps!!**