Develop and release a simple app using Flutter that plays a sound when an icon is tapped. 2/n Internationalization
This is the second article in a series summarizing the process of developing and releasing a simple app using Flutter that plays a sound when an icon is tapped.
Summary
I am creating a simple app for my 1-year and 1-month-old son, where he can tap on animal and vehicle icons to hear sounds. My goal is to release this app on both iOS and Android platforms.
Progress so far
I have linked the previous articles to this tag:
https://zubora-code.net/ja/tags/first_flutter_tutorial
Goal for this article
In the previous step, I completed the implementation of the core functionality. In this article, I will briefly explain how to support both Japanese and English languages. The completed version can be found here.
Implementation
Library Installation
There are several libraries available for multi-language support, but I decided to go with the official one, flutter_localizations.
https://docs.flutter.dev/accessibility-and-localization/internationalization
$ flutter pub add flutter_localizations --sdk=flutter
$ flutter pub add intl:any
Setting up the MaterialApp delegate and supported locales
import 'package:flutter/material.dart';
+import 'package:flutter_localizations/flutter_localizations.dart';
import 'Sound.dart';
void main() {
@@ -12,6 +13,15 @@ class MyApp extends StatelessWidget {
Widget build(BuildContext context) {
return MaterialApp(
title: 'ぴよぴよサウンドパーク',
+ localizationsDelegates: const [
+ GlobalMaterialLocalizations.delegate,
+ GlobalWidgetsLocalizations.delegate,
+ GlobalCupertinoLocalizations.delegate,
+ ],
+ supportedLocales: const [
+ Locale('en'),
+ Locale('ja'),
+ ],
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
useMaterial3: true,
Adding generate flag: true in pubspec.yaml
We will add a setting to automatically generate language support files when installing the library.
flutter:
-
+ generate: true
Adding l10n.yaml
arb-dir: lib/l10n
template-arb-file: app_en.arb
output-localization-file: app_localizations.dart
Adding lib/l10n/app_en.arb and lib/l10n/app_ja.arb
{
"appTitle": "PiyoPiyo Sound Park",
"@appTitle": {
"description": "The app title"
},
"animal": "Animal",
"@animal": {
"description": "Animal"
},
"vehicle": "Vehicle",
"@vehicle": {
"description": "Vehicle"
}
}
{
"appTitle": "ぴよぴよサウンドパーク",
"animal": "どうぶつ",
"vehicle": "のりもの"
}
app_en.arb is specified as the template file, and app_ja.arb is the translation file.
Run flutter run command to automatically generate .dart_tool/gen_l10n
$ flutter run
Extracting values based on the context in main.dart
git diff lib/main.dart
diff --git a/lib/main.dart b/lib/main.dart
index 3b9397a..f3414a3 100644
--- a/lib/main.dart
+++ b/lib/main.dart
@@ -1,5 +1,6 @@
import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
+import 'package:flutter_gen/gen_l10n/app_localizations.dart';
import 'Sound.dart';
void main() {
@@ -12,29 +13,25 @@ class MyApp extends StatelessWidget {
@override
Widget build(BuildContext context) {
return MaterialApp(
title: "ぴよぴよサウンドパーク",
localizationsDelegates: const [
+ AppLocalizations.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
- supportedLocales: const [
- Locale('en'),
- Locale('ja'),
- ],
+ supportedLocales: AppLocalizations.supportedLocales,
theme: ThemeData(
colorScheme: ColorScheme.fromSeed(seedColor: Colors.orange),
useMaterial3: true,
),
- home: const MyHomePage(title: 'ぴよぴよサウンドパーク'),
+ home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
- const MyHomePage({super.key, required this.title});
-
- final String title;
+ const MyHomePage({super.key});
@override
State<MyHomePage> createState() => _MyHomePageState();
@@ -141,23 +138,24 @@ class _MyHomePageState extends State<MyHomePage> {
@override
Widget build(BuildContext context) {
+ final l10n = AppLocalizations.of(context)!;
return Scaffold(
appBar: AppBar(
backgroundColor: Theme.of(context).colorScheme.inversePrimary,
- title: Text(widget.title),
+ title: Text(l10n.appTitle),
),
body: Center(
child: _widgetOptions.elementAt(_selectedIndex),
),
bottomNavigationBar: BottomNavigationBar(
- items: const <BottomNavigationBarItem>[
+ items: <BottomNavigationBarItem>[
BottomNavigationBarItem(
- icon: Icon(Icons.pets),
- label: 'どうぶつ',
+ icon: const Icon(Icons.pets),
+ label: l10n.animal,
),
BottomNavigationBarItem(
- icon: Icon(Icons.directions_car),
- label: 'のりもの',
+ icon: const Icon(Icons.directions_car),
+ label: l10n.vehicle,
),
],
currentIndex: _selectedIndex,
In conclusion
Though it felt a bit cumbersome, it was easier compared to Web FrontEnd since there are no routing issues. Since it's a shame to limit the app to only Japanese when we can reach the world, I will actively pursue multi-language support in future development.
The changes made in this article can be found in the following Pull Request: https://github.com/tkugimot/touch_and_hear/pull/2?w=1
Remaining tasks include:
- Introducing Crashlytics for monitoring
- Integrating AdMob for advertising
- Creating and setting up icons/feature graphics
- Creating a support site and privacy policy site
- Adding a Drawer (for app usage, contact, privacy policy links)
- Releasing the app on Android
- Releasing the app on iOS
I will write about Crashlytics in the next article.