Lilong Zhang / 【Flutter】国际化代码实现

Created Mon, 27 Feb 2023 22:21:33 +0800
1093 Words

Flutter里的国际化,简言之,分为两大步:

  1. 创建本地化资源。
  2. 创建本地化资源代理,并配置到APP入口函数。

应用内切换语言,关键点有两个:

  1. 指定应用的语言:设置 MaterialApp 的 locate 参数。
  2. 取得当前语言对应的本地化资源文件:Localizations.of<S>(context, S)

Localizations 继承自 StatefulWidget ,内部属性 locale 有变化,child 就会重建。

以下,为国际化代码的具体实现:

前提

pubspec.yaml 文件中添加它和 intl作为依赖

flutter pub add flutter_localizations --sdk=flutter
flutter pub add intl:any

最终的 pubspec.yaml 文件中形如:

dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: any

1. 创建本地化资源

1.1 创建本地化资源接口,声明需要翻译的字段。

使用接口定义待翻译字段的好处是:实现类/子类 必须对抽象字段进行赋值,否则编译器会报错。 相比之下,字典的方式,某个 key 没有赋值,编译器检查不出来,而且,哪些 key 是有用的,哪些 key 没用了,也不好确定。

/// 本地化资源 基类
abstract class S {
  /// 本地化资源代理对象
  static const LocalizationsDelegate delegate = ProjectLocalizationsDelegate();

  /// 根据上下文中的 [Locale] 取得对应的本地化资源。
  static S of(BuildContext context) {
    return Localizations.of<S>(context, S)!;
  }

  /// 支持的语言。
  /// 如果本地没有保存的语言配置参数,APP会默认使用第一个作为默认语言。
  static List<Locale> supportedLocales = [
    const Locale('en'),
    const Locale('ja'),
    const Locale('zh')
  ];

  // 不需要翻译的字段,直接赋值。
  static String appName = 'AppName';
  static String english = 'English';
  static String japanese = '日本語';
  static String simpleChinese = '简体中文';
  static Map<String, String> localeSets = {
    'en': english,
    'ja': japanese,
    'zh': simpleChinese
  };

  // 需要翻译的字段追加到下面,在子类中进行赋值。
  String get cancel;
  String get ok;
  String get readAndAgree;
  String get privacyPolicy;
  String get termsOfService;

  String get me;
  String get settingsLanguage;
}

1.2 创建指定语言的资源类(实现上一步中的接口),对接口中的成员变量进行赋值。

/// 英文
class ProjectLocalizationsEN implements S {
  @override
  String get cancel => "Cancel";

  @override
  String get ok => "OK";
}

/// 中文
class ProjectLocalizationsZH implements S {
  @override
  String get cancel => "取消";

  @override
  String get ok => "确定";
}

/// 日文
class ProjectLocalizationsJA implements S {
  @override
  String get cancel => "キャンセル";

  @override
  String get ok => "確定";
}

2. 创建本地化资源代理

2.1 创建本地化资源代理类(继承 LocalizationsDelegate),重写 load 方法,根据 locale 返回对应语言的资源。

/// 项目本地化资源代理
class ProjectLocalizationsDelegate extends LocalizationsDelegate<S> {
  const ProjectLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) => true;

  @override
  Future<S> load(Locale locale) {
    return SynchronousFuture<S>(getMaterialTranslation(locale));
  }

  @override
  bool shouldReload(ProjectLocalizationsDelegate old) => false;

  /// 根据 locale 得到对应的本地化资源
  S getMaterialTranslation(Locale locale) {
    switch (locale.languageCode) {
      case 'en':
        return ProjectLocalizationsEN();
      case 'zh':
        return ProjectLocalizationsZH();
      case 'ja':
        return ProjectLocalizationsJA();
      default:
        return ProjectLocalizationsEN();
    }
  }
}

2.2 在程序入口 MaterialApp 中的 localizationsDelegates 参数中,加入自己创建的本地化资源代理类。

import 'package:flutter_localizations/flutter_localizations.dart';

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

  @override
  Widget build(BuildContext context) {
    return Consumer<LocaleStore>(builder: (context, localeStore, _) {
      return OKToast(
        movingOnWindowChange: false,
        child: MaterialApp(
          onGenerateTitle: (context) => S.appName,  // 不需要翻译的字段,可以直接类名调用。
          theme: AppTheme.lightTheme(context),
          localizationsDelegates: const [
            GlobalMaterialLocalizations.delegate,
            GlobalWidgetsLocalizations.delegate,
            // 项目本地化资源代理
            S.delegate,
          ],
          // 支持的语言
          supportedLocales: S.supportedLocales,
          // 指定语言,如果 localStore 里没有保存的语言参数,则直接使用 S 文件中配置的第一个语言。
          locale: localeStore.languageCode == null
              ? S.supportedLocales.first
              : Locale(localeStore.languageCode!),
          routes: RouteMap.routes,
          home: const HomePage(),
          builder: (context, child) => GestureDetector(
            onTap: () => CommonUtils.hideKeyboard(context),
            child: child,
          ),
        ),
      );
    });
  }
}

配置完成,开始使用:

  • 不需要翻译的字段,使用方式: S.appName
  • 支持多语言的字段,使用方式: S.of(context).ok S.of(context). cancel