Flutter进阶(2):国际化开发

fengMisaka / 2024-10-23 / 原文

在全球化趋势下,应用支持多语言是非常重要的。Flutter 提供了强大的国际化(i18n)支持,可以通过 flutter_localizations 与 gen-l10n 工具生成的本地化类来实现不同区域和语言的适配。本篇博客将介绍如何在 Flutter 项目中使用 flutter_localizations,并实现多语言支持与动态切换。

先看下效果图:

Flutter_multiLanguage.gif


一. 什么是国际化(i18n)和本地化(l10n)?

国际化(i18n) 是指编写软件,使其能够在不同的语言和文化环境中运行。它包含了将软件中的文本、日期、时间等元素抽象出来,以便根据用户的区域设置进行翻译和显示。

本地化(l10n) 是国际化的具体应用步骤,它包括将应用程序的语言、日期格式、货币符号等内容适配到特定地区。

二、部署环境&初始化

(1)这边用到vscode进行开发,需要下载 Flutter Intl 插件。

Flutter_mulitLanguage_A.png


(2)下载完成后,在项目中的pubspec.yaml添加依赖库:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter

(3)在vscode中按command\ctrl+shift+p,然后会弹出一个输入vscode内置命令的输入框和下拉结果。在 flutter 项目中,首次需要搜索Flutter Intl: Initialize命令,回车即可初始化和生成国际化相关文件代码。

Flutter_mulitLanguage_C.png


Flutter_mulitLanguage_D.png


就在项目中lib中默认生成了以下文件:

|-- generated 自动生成的文件,不需要手动编辑
    |-- intl
    |   |-- messages_all.dart 桥接多个语言文件 下面en zh等文件。
    |   |-- messages_en.dart 转译文件 自动从l10n/intl_en.arb 提取key/value 自动生成代码
    |-- l10n.dart 入口文件代码,不需要手动编辑
|-- l10n  存放国际化json文件位置 arb=json
    |-- intl_en.arb

三、修改 MaterialApp 以支持国际化

在 MaterialApp 中配置 localizationsDelegates 和 supportedLocales 以支持本地化。具体代码如下:

class MyApp extends StatefulWidget {
  const MyApp({super.key});  
  @override
  State<MyApp> createState() => _MyAppState();
}
 
class _MyAppState extends State<MyApp> {
  Locale _locale = Locale('en'); // 默认语言
 
  void setLocale(Locale locale) {
    setState(() {
      _locale = locale;
    });
  }
 
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: _locale,// 设置当前的 Locale
      localizationsDelegates: const [
        S.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('en', 'US'), // 英文
        Locale('zh', 'CN'), // 简体中文
        Locale('zh', 'TW') // 繁体中文
      ],
      home: MyHomePage(
        onLocaleChange: (Locale locale) {
          setLocale(locale);
        },
      ),
    );
  }
}

四、添加和生成语言文件(新增国际化)

intl使用.arb文件来存储不同语言的翻译资源。

(1)添加新的国际化文件可以搜索Flutter Intl: Add Locale命令,回车会切换到输入国际化编码。

Flutter_mulitLanguage_E.png


(2)然后在编辑框会提示输入国际化编码,例如要添加简体中文就输入:zh,添加繁体中文就添加:zh_TW。

(3)然后就会在l10n文件夹,自动添加 intl_zh.arbintl_zh_TW.arb文件,以及在generated/l10n文件夹中生成转译文件。

Flutter_mulitLanguage_F.png


语言文件示例

lib/l10n/intl_en.arb:

{
  "content": "china"
}

lib/l10n/intl_zh.arb:

{
  "content": "中国"
}

lib/l10n/intl_zh_TW.arb:

{
  "content": "中國"
}

五、动态切换语言

代码如下:

class MyHomePage extends StatelessWidget {
  final Function(Locale) onLocaleChange;

  const MyHomePage({super.key, required this.onLocaleChange});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter 国际化 Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            //此处国际化文字
            Text(
              S.current.content,
              style: const TextStyle(
                  color: Colors.red, fontSize: 50, fontWeight: FontWeight.w700),
            ),
            changeButton(const Locale('zh', 'CN'), '简体'),
            changeButton(const Locale('zh', 'TW'), '繁体'),
            changeButton(const Locale('en', 'US'), '英文'),
          ],
        ),
      ),
    );
  }

  // 动态切换语言
  Widget changeButton(Locale newLocale, String text) {
    return ElevatedButton(
      onPressed: () {
        onLocaleChange(newLocale); // 调用 MyAppState 的 setLocale 方法
      },
      child: Text(text),
    );
  }
}

六、通过手机系统语言来显示对应语言的话

代码如下:

class _MyAppState extends State<MyApp> {
  Locale _locale = const Locale('en', 'US'); // 默认语言

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      //locale: _locale, // 【(1)localec参数不用设置】
      // 设置当前的 Locale
      localizationsDelegates: const [
        S.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('en', 'US'), //英文
        Locale('zh', 'CN'), //简体中文
        Locale('zh', 'TW') //繁体中文
      ],
      //如果使用系统语言,使用下面的:【(2)判断是什么系统语言,就返回下面的语言值】
      localeResolutionCallback: (locale, supportedLocales) {
        if (locale?.countryCode == 'CN') {// 如果语言是简体
          return const Locale('zh', 'CN');
        } else if (locale?.countryCode == 'TW') {// 如果语言繁体
          return const Locale('zh', 'TW');
        } else {
          return const Locale('en', 'US');
        }
      },
      home: MyHomePage(
        onLocaleChange: (Locale locale) {
          setLocale(locale);
        },
      ),
    );
  }
}

七、完整示例代码

import 'package:flutter/material.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'generated/l10n.dart';

void main() {
  runApp(const MyApp());
}

class MyApp extends StatefulWidget {
  const MyApp({super.key});
  @override
  State<MyApp> createState() => _MyAppState();
}

class _MyAppState extends State<MyApp> {
  Locale _locale = const Locale('en', 'US'); // 默认语言

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: _locale,
      // 设置当前的 Locale
      localizationsDelegates: const [
        S.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: const [
        Locale('en', 'US'), //英文
        Locale('zh', 'CN'), //简体中文
        Locale('zh', 'TW') //繁体中文
      ],
      //如果使用系统语言,使用下面的:
      /*localeResolutionCallback: (locale, supportedLocales) {
        if (locale?.countryCode == 'CN') {// 如果语言是简体
          return const Locale('zh', 'CN');
        } else if (locale?.countryCode == 'TW') {// 如果语言繁体
          return const Locale('zh', 'TW');
        } else {
          return const Locale('en', 'US');
        }
      },*/
      home: MyHomePage(
        onLocaleChange: (Locale locale) {
          setLocale(locale);
        },
      ),
    );
  }
}

class MyHomePage extends StatelessWidget {
  final Function(Locale) onLocaleChange;

  const MyHomePage({super.key, required this.onLocaleChange});

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: const Text('Flutter 国际化 Demo'),
      ),
      body: Center(
        child: Column(
          mainAxisAlignment: MainAxisAlignment.center,
          children: <Widget>[
            //此处国际化文字
            Text(
              S.current.content,
              style: const TextStyle(
                  color: Colors.red, fontSize: 50, fontWeight: FontWeight.w700),
            ),
            changeButton(const Locale('zh', 'CN'), '简体'),
            changeButton(const Locale('zh', 'TW'), '繁体'),
            changeButton(const Locale('en', 'US'), '英文'),
          ],
        ),
      ),
    );
  }

  Widget changeButton(Locale newLocale, String text) {
    return ElevatedButton(
      onPressed: () {
        onLocaleChange(newLocale); // 调用 MyAppState 的 setLocale 方法
      },
      child: Text(text),
    );
  }
}