Flutter学习记录——21.应用国际化处理


国际化就是让我们的应用支持多种语言,例如运行在国内的使用中文简体、在港澳台的使用繁体字、美国的使用英文、日本的用户显示的是日文等等类似场景,也可以把国际化称为本地化处理。Flutter 本身的 API 是支持国际化处理的,当然也可以用官方提供的插件库来实现。

那么这节课我们将介绍 Flutter 中应用国际化处理的基本使用详解,并配合一些案例。

1.实现应用国际化

如果我们的应用想提供多种语言模式,那么就需要进行国际化处理,Flutter 本身是支持国际化处理的。

在 Flutter 中使用国际化一般要配合 MaterialApp 或 WidgetsApp 的国际化属性localizationsDelegates 和 supportedLocales。并且在 pubspec.yaml 配置 flutter_localizations 的一个单独包。截至 2017 年 10 月,该软件包支持 15 种语言(来源于官方)。

接下来我们看下 Flutter 实现国际化的步骤。

首先需要配置 pubspec.yaml:

dependencies:
  flutter:
    sdk: flutter
  // 添加国际化包
  flutter_localizations:
    sdk: flutter

接下来在使用的页面导入包:

import 'package:flutter_localizations/flutter_localizations.dart';
使用 MaterialApp 或 WidgetsApp 的属性来配置:

class LocalizationsSamplesState extends State<LocalizationsSamples> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        // 这里要自己实现一个Localizations.delegate
      ],
      supportedLocales: [
        const Locale('en', 'US'), // English
        const Locale('zh', 'CN'), // Chinese
        // ... 其他语言支持
      ],
      home: getBody(),
    );
  }
...
}

supportedLocales 里定义的是语种,Locale 来定义语言语种,参数包括语言和国家两个标志。

接下来我们需要自己实现一个 GlobalMaterialLocalizations,本地化需要 Localizations 和Delegate 两个类才可以,我们先实现 Localizations:

import 'package:flutter/widgets.dart';

class PageLocalizations {
  final Locale locale;
  PageLocalizations(this.locale);

  static Map<String, Map<String, String>> _localizedValues = {
    'en': {
      'task title': 'Flutter Demo',
      'titlebar title': 'Flutter Demo Home Page',
      'click tip': 'You have pushed the button this many times:',
      'inc': 'Increment'
    },
    'zh': {
      'task title': 'Flutter 示例',
      'titlebar title': 'Flutter 示例主页面',
      'click tip': '你一共点击了这么多次按钮:',
      'inc': '增加'
    }
  };

  get taskTitle {
    return _localizedValues[locale.languageCode]['task title'];
  }

  get titleBarTitle {
    return _localizedValues[locale.languageCode]['titlebar title'];
  }

  get clickTop {
    return _localizedValues[locale.languageCode]['click tip'];
  }

  get inc {
    return _localizedValues[locale.languageCode]['inc'];
  }

  static PageLocalizations of(BuildContext context) {
    return Localizations.of(context, PageLocalizations);
  }
}

接下来实现 Delegate:

import 'package:flutter/foundation.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_samples/samples/pageLocalizations.dart';

class GlobalPagesLocalizations
    extends LocalizationsDelegate<PageLocalizations> {
  const GlobalPagesLocalizations();

  // 是否支持某个语言
  @override
  bool isSupported(Locale locale) {
    return ['en', 'zh'].contains(locale.languageCode);
  }

    // 加载对应的语言资源,自动调用
  @override
  Future<PageLocalizations> load(Locale locale) {
    return new SynchronousFuture<PageLocalizations>(
        new PageLocalizations(locale));
  }

  // 重新加载
  @override
  bool shouldReload(LocalizationsDelegate<PageLocalizations> old) {
    return false;
  }

  static GlobalPagesLocalizations delegate = const GlobalPagesLocalizations();
}
最后调用使用即可:

class LocalizationsSamples extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return LocalizationsSamplesState();
  }
}

class LocalizationsSamplesState extends State<LocalizationsSamples> {
  @override
  void initState() {
    super.initState();
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalPagesLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('en', 'US'), // English
        const Locale('zh', 'CN'), // Chinese
        // ... 其他语言支持
      ],
      home: WelcomePage(),
    );
  }
}

class WelcomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return WelcomeState();
  }
}

class WelcomeState extends State<WelcomePage> {

  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Localizations'),
        primary: true,
      ),
      body: Column(
        children: <Widget>[
          // 调用国际化后的属性资源
          Text(PageLocalizations.of(context).taskTitle,)
        ],
      ),
    );
  }
}

可以看到调用的方式是:PageLocalizations.of(context).taskTitle

这样,当我们的手机在切换语言环境时,应用便会自动显示当前语言环境下的字符资源,达到国际化目的。

2.使用插件库实现应用国际化

接下来我们看下通过插件库来实现的应用国际化的用法,如 intl 和 flutter_i18n 插件。这两个插件库都是 Flutter 官方的,这两个基本原理是一样的,只不过 flutter_i18n 是自动化执行了将 arb文件转为 dart 的等操作。那么这里直接以 flutter_i18n 插件为例给大家讲解通过插件库实现应用国际化。这里需要使用 Android Studio 安装一个 Flutter i18n 插件:

Flutter i18n插件

Flutter i18n 插件可以帮助我们简化手动编写 arb 和生成 dart 这个过程。其原理是通过 arb 文件来自动生成所需要的代码。

插件安装完后悔自动出现一个按钮,按这个按钮就可以自动根据项目根目录的 res 里的 arb 文件来自动在 lib/generated 生成名字叫 i18n.dart 的 dart 文件。

在这里插入图片描述

我们可以右键新建新的需要支持的语言 arb 文件。

Flutter i18n插件

我们分别看下 strings_en.arb、strings_zh_CN.arb、i18n.dart 文件内容:

// i18n.dart文件内容,这个是自动生成

import 'dart:async';

import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';

// ignore_for_file: non_constant_identifier_names
// ignore_for_file: camel_case_types
// ignore_for_file: prefer_single_quotes

//This file is automatically generated. DO NOT EDIT, all your changes would be lost.
class S implements WidgetsLocalizations {
  const S();

  static const GeneratedLocalizationsDelegate delegate =
    GeneratedLocalizationsDelegate();

  static S of(BuildContext context) => Localizations.of<S>(context, S);

  @override
  TextDirection get textDirection => TextDirection.ltr;

  String get appName => "App Name";
  String get title => "My Title";
  String hello(String name) => "Hello $name";
}

class $zh_HK extends S {
  const $zh_HK();

  @override
  TextDirection get textDirection => TextDirection.ltr;

  @override
  String get appName => "應用名";
  @override
  String get title => "我的標題";
  @override
  String hello(String name) => "妳好${name}";
}

class $en extends S {
  const $en();
}

class $zh_CN extends S {
  const $zh_CN();

  @override
  TextDirection get textDirection => TextDirection.ltr;

  @override
  String get appName => "应用名";
  @override
  String get title => "我的标题";
  @override
  String hello(String name) => "你好${name}";
}

class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
  const GeneratedLocalizationsDelegate();

  List<Locale> get supportedLocales {
    return const <Locale>[
      Locale("zh", "HK"),
      Locale("en", ""),
      Locale("zh", "CN"),
    ];
  }

  LocaleListResolutionCallback listResolution({Locale fallback}) {
    return (List<Locale> locales, Iterable<Locale> supported) {
      if (locales == null || locales.isEmpty) {
        return fallback ?? supported.first;
      } else {
        return _resolve(locales.first, fallback, supported);
      }
    };
  }

  LocaleResolutionCallback resolution({Locale fallback}) {
    return (Locale locale, Iterable<Locale> supported) {
      return _resolve(locale, fallback, supported);
    };
  }

  Locale _resolve(Locale locale, Locale fallback, Iterable<Locale> supported) {
    if (locale == null || !isSupported(locale)) {
      return fallback ?? supported.first;
    }

    final Locale languageLocale = Locale(locale.languageCode, "");
    if (supported.contains(locale)) {
      return locale;
    } else if (supported.contains(languageLocale)) {
      return languageLocale;
    } else {
      final Locale fallbackLocale = fallback ?? supported.first;
      return fallbackLocale;
    }
  }

  @override
  Future<S> load(Locale locale) {
    final String lang = getLang(locale);
    if (lang != null) {
      switch (lang) {
        case "zh_HK":
          return SynchronousFuture<S>(const $zh_HK());
        case "en":
          return SynchronousFuture<S>(const $en());
        case "zh_CN":
          return SynchronousFuture<S>(const $zh_CN());
        default:
          // NO-OP.
      }
    }
    return SynchronousFuture<S>(const S());
  }

  @override
  bool isSupported(Locale locale) =>
    locale != null && supportedLocales.contains(locale);

  @override
  bool shouldReload(GeneratedLocalizationsDelegate old) => false;
}

String getLang(Locale l) => l == null
  ? null
  : l.countryCode != null && l.countryCode.isEmpty
    ? l.languageCode
    : l.toString();

再看下 strings_en.arb、strings_zh_CN.arb 文件内容:

// strings_en.arb
{
  "appName": "App Name",
  "hello": "Hello $name",
  "title": "My Title"
}
// strings_zn_CN.arb
{
  "appName": "应用名",
  "hello": "你好${name}",
  "title": "我的标题"
}

可以看到我们也可以通过 $ 符号来进行传递动态值。

flutter_i18n 插件库地址:https://pub.dev/packages/flutter_i18n。

首先我们需要引用这个库:

dependencies:
  flutter_i18n: ^0.6.3

在使用的地方导入库:

import 'package:flutter_i18n/flutter_i18n.dart';

然后我们看下使用的方式:

import 'package:flutter/material.dart';
import 'package:flutter/widgets.dart';
import 'package:flutter_app/generated/i18n.dart';
import 'package:flutter_i18n/flutter_i18n.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

class LocalizationsSamples extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return LocalizationsSamplesState();
  }
}

class LocalizationsSamplesState extends State<LocalizationsSamples> {
      Locale _locale = const Locale('zh', 'CN');

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

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        // 配置delegate
        S.delegate,
      ],
      supportedLocales: [
        // 支持的语言
        const Locale('en', ''), // English
        const Locale('zh', 'CN'), // Chinese
        const Locale("zh", "HK"),
        // ... 其他语言支持
      ],
      // 我们也可以指定一种默认语言
      localeResolutionCallback:
          S.delegate.resolution(fallback: const Locale('en', '')),
      home: WelcomePage(),
    );
  }
}

class WelcomePage extends StatefulWidget {
  @override
  State<StatefulWidget> createState() {
    return WelcomeState();
  }
}

class WelcomeState extends State<WelcomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text('Localizations'),
        primary: true,
      ),
      body: Column(
        children: <Widget>[
          // 调用国际化后的属性资源
          Text(
            S.of(context).title,
          )
        ],
      ),
    );
  }
}

主动切换语言:

FlutterI18n.refresh(context, Locale('en', ''));

当然还有其他方法:

FlutterI18n.translate(buildContext, "your.key")

FlutterI18n.plural(buildContext, "select", 0)

通过插件库实现国际化就大概这么多,大家可以自己实践下。

发布了253 篇原创文章 · 获赞 52 · 访问量 3万+

猜你喜欢

转载自blog.csdn.net/qq_41151659/article/details/103404746