Flutter: App Localization (Quick Guide)

Flutter: App Localization (Quick Guide)

Almost all the countries have their own language and most people understand only one or two languages. The application experience can be painful if the application is not available in its own language. So localization is needed to ensure people can use the application easily.

The localization implementation becomes painful if it is not integrated in a proper way. Here I am adding quick steps to integrate the localization feature.

Step 1:

Enable localization from the pubspec.yaml. Take a look at four lines with the comment # Add this line

### pubspec.yaml

name: language_switch
description: A new Flutter project.

# Prevent accidental publishing to pub.dev.
publish_to: 'none'

version: 1.0.0+1

environment:
  sdk: ">=2.16.2 <3.0.0"

dependencies:
  cupertino_icons: ^1.0.2
  flutter:
    sdk: flutter
  flutter_localizations:  # Add this line
    sdk: flutter          # Add this line
  intl: ^0.17.0           # Add this line

dev_dependencies:
  flutter_lints: ^1.0.0
  flutter_test:
    sdk: flutter

# For information on the generic Dart part of this file, see the
# following page: https://dart.dev/tools/pub/pubspec

# The following section is specific to Flutter.
flutter:
  generate: true          # Add this line
  uses-material-design: true

Step 2:

Add file l10n.yaml to the root of the flutter project stating the localized JSON file location and generated localization file location.

### l10n.yaml

arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart

Step 4:

Add respective localized strings inside the folder lib/l10n.

image.png

// app_en.arb

{
  "helloWorld": "Hello World!",
  "@helloWorld": {
    "description": "The conventional newborn programmer greeting"
  }
}
// app_ja.arb

{
  "helloWorld": "「こんにちは世界」"
}
// app_ne.arb

{
  "helloWorld": "नमस्कार संसार!"
}

Step 5:

Configure the application to support the localized language values, restoring the previously used locale.

// main.dart

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

import 'src/home.dart';

void main() {
  // For simplicity default language is set to English
  // You should persist language to local storage and retrieve from there
  Locale locale = const Locale.fromSubtags(languageCode: 'en');
  runApp(MyApp(locale));
}

class MyApp extends StatefulWidget {
  const MyApp(this.locale, {Key? key}) : super(key: key);
  final Locale locale;

  @override
  State<MyApp> createState() => _MyAppState();

  // This function is used to switch the language in runtime
  static _MyAppState? of(BuildContext context) =>
      context.findAncestorStateOfType<_MyAppState>();
}

class _MyAppState extends State<MyApp> {
  late Locale _locale;

  void setLocale(Locale value) {
    setState(() {
      _locale = value;
    });
  }

  @override
  void initState() {
    super.initState();
    _locale = widget.locale;
  }

  // This widget is the root of your application.
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      title: 'Language Demo',
      theme: ThemeData(primarySwatch: Colors.blue),
      // if locale value is not set, default system provided locale will be used
      locale: _locale,
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
        AppLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('en', 'US'),
        Locale('ne', 'NP'),
        Locale('ja', 'JP'),
      ],
      home: const MyHomePage(),
    );
  }
}

Step 6:

Use the localized string and update the locale setting while using the application.

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

import '../main.dart';

class MyHomePage extends StatefulWidget {
  const MyHomePage({Key? key}) : super(key: key);

  @override
  State<MyHomePage> createState() => _MyHomePageState();
}

class _MyHomePageState extends State<MyHomePage> {
  void _switchLanguage(BuildContext context, String languageCode) {
    // switch language; also persist the value to local storage to retrieve later 
    // when application is restarted; retrieved and used inside main.dart
    MyApp.of(context)
        ?.setLocale(Locale.fromSubtags(languageCode: languageCode));
  }

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter Language Switch'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.spaceEvenly,
          crossAxisAlignment: CrossAxisAlignment.center,
          children: <Widget>[
            Text(AppLocalizations.of(context)?.helloWorld ?? 'n/a'),
            Row(
              mainAxisAlignment: MainAxisAlignment.spaceEvenly,
              children: [
                ElevatedButton(
                  onPressed: () => _switchLanguage(context, 'en'),
                  child: const Text('English'),
                ),
                ElevatedButton(
                    onPressed: () => _switchLanguage(context, 'ne'),
                    child: const Text('Nepali')),
                ElevatedButton(
                    onPressed: () => _switchLanguage(context, 'ja'),
                    child: const Text('Japanese')),
              ],
            ),
          ],
        ),
      ), // This trailing comma makes auto-formatting nicer for build methods.
    );
  }
}

image.png

Bonus :

The access to the localized string needs to import the AppLocalizations file generated which might be shown as an error by the IDE. Even its showing red, the application will run without any errors if the localization key is correct. An extension of context can also be used to fetch the localization values like below.

// localization_ext.dart

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

extension ContextExt on BuildContext {
  AppLocalizations? get localization => AppLocalizations.of(this);
}

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

  @override
  Widget build(BuildContext context) {
    //return Text(AppLocalizations.of(context)?.helloWorld ?? 'n/a');
    // using extension
    return Text(context.localization?.helloWorld ?? 'n/a');
  }
}

References:

  1. https://docs.flutter.dev/development/accessibility-and-localization/internationalization
  2. https://stackoverflow.com/a/65380235/3710341