前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >【Flutter 实战】国际化及App 内切换语言功能

【Flutter 实战】国际化及App 内切换语言功能

作者头像
老孟Flutter
发布2020-11-26 16:07:48
6K0
发布2020-11-26 16:07:48
举报
文章被收录于专栏:FlutterFlutter

老孟导读:本文介绍如何实现国际化以及实现 App 内切换语言功能。

使App支持国际化

当应用程序支持不同语言的时候,就需要对应用程序进行国际化,当然国际化不仅仅指文字,也可以是布局图片等。Flutter 已经提供了组件来实现国际化,下面是实现国际化的步骤:

MaterialApp.supportedLocales 中添加支持的语言:

代码语言:javascript
复制
MaterialApp(
  title: 'Flutter IntlApp',
  supportedLocales: [
    const Locale('zh'),
    const Locale('en'),
  ],
  ...
)

比如上面的代码中,只支持英文和中文。

根据不同的语言获取不同的资源:

代码语言:javascript
复制
class AppLocalizations {
  final Locale locale;

  AppLocalizations(this.locale);

  static AppLocalizations of(BuildContext context) {
    return Localizations.of<AppLocalizations>(context, AppLocalizations);
  }

  static Map<String, Map<String, String>> _localizedValues = {
    'en': {
      'title': 'Hello World',
    },
    'zh': {
      'title': '你好',
    },
  };

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

这里只设置了一个文案,实际项目中建议不同语言存放在不同的文件中。

设置用于加载语言的 Delegate

代码语言:javascript
复制
class AppLocalizationsDelegate extends LocalizationsDelegate<AppLocalizations> {
  const AppLocalizationsDelegate();

  @override
  bool isSupported(Locale locale) => ['en', 'zh'].contains(locale.languageCode);

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

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

将此 Delegate 添加到 MaterialApp:

代码语言:javascript
复制
MaterialApp(
  title: 'Flutter IntlApp',
  localizationsDelegates: [
    AppLocalizationsDelegate(),
  ],
  supportedLocales: [
    const Locale('zh'),
    const Locale('en'),
  ],
  home: _HomePage(),
)

使用:

代码语言:javascript
复制
Scaffold(
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('国际化:${AppLocalizations.of(context).title}'),
         ],
          ),
        ],
      ),
    )

注意:Scaffold 不要添加 AppBar 数据,否则报错,具体原因下面会给出。

重点是这句:

代码语言:javascript
复制
AppLocalizations.of(context).title

此时,App会根据系统语言作为当前的语言。

系统是如何实现国际化的?

Flutter 的国际化是通过 Localizations 组件实现,上面没有用到 Localizations 组件啊,是的,App 中并没有直接使用,因为 MaterialApp 内部封装了此组件,通过 DevTools 可以查看:

Localizations 组件用于加载本地化资源、获取系统语言,Localizations 组件内部使用了 InheritedWidget 组件,当其属性即 Locale 发生变化时,其子组件将重建。

上面定义的 AppLocalizations 类内部的 of 方法:

代码语言:javascript
复制
static AppLocalizations of(BuildContext context) {
  return Localizations.of<AppLocalizations>(context, AppLocalizations);
}

Localizations.of 源代码:

这段代码是获取 Type 类型(App 传入的类型为 AppLocalizations)的资源,看一下 resourcesFor 的源代码:

关键在 _typeToResources :

_typeToResources 是一个 Map 类型, _typeToResources 初始化数据的:

widget.delegates 的类型是:

代码语言:javascript
复制
/// This list collectively defines the localized resources objects that can
/// be retrieved with [Localizations.of].
final List<LocalizationsDelegate<dynamic>> delegates;

是否还记得 MaterialApp localizationsDelegates 属性,此 delegates 就是在 MaterialApp 中设置的值,到此我们理解了

代码语言:javascript
复制
Localizations.of<AppLocalizations>(context, AppLocalizations)

这句是如何获取 AppLocalizations 实例的,当然中间还有一些其他的判断,具体可自行查看源代码。

添加系统国际化支持

前面说到 Scaffold 不要添加 AppBar 数据,否则报错,填上看其异常信息:

代码语言:javascript
复制
Scaffold(
      appBar: AppBar(),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('国际化:${AppLocalizations.of(context).title}'),
        ],
      ),
    )

上面的异常效果不明显,看控制台的异常信息:

提示 MaterialLocalizations 找不到,MaterialLocalizations 是什么呢?其实它是系统组件的国际化资源,所以修复以上异常的方法是引入 MaterialLocalizations,在pubspec.yaml文件中添加包依赖:

代码语言:javascript
复制
dependencies:
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter

MaterialApp 修改如下:

代码语言:javascript
复制
MaterialApp(
  title: 'Flutter IntlApp',
  localizationsDelegates: [
    AppLocalizationsDelegate(),
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: [
    const Locale('zh'),
    const Locale('en'),
  ],
  home: _HomePage(),
)

flutter_localizations 99%的概率会引入,但我们要知道这个并不是必须的。

添加应用程序 title 国际化

按照上面的方式国际化:

代码语言:javascript
复制
MaterialApp(
  title: '${AppLocalizations.of(context).title}',
  localizationsDelegates: [
    AppLocalizationsDelegate(),
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: [
    const Locale('zh'),
    const Locale('en'),
  ],
  home: _HomePage(),
)

直接异常了,因为此时使用的 context 是从 build 方法中传入的,而 Localizations 从 context 开始向上查找,国际化资源是在 MaterialApp 组件中的,所以无法找到 AppLocalizations。

修改方式是使用 onGenerateTitle:

代码语言:javascript
复制
MaterialApp(
  onGenerateTitle: (context) {
    return AppLocalizations.of(context).title;
  },
  localizationsDelegates: [
    AppLocalizationsDelegate(),
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: [
    const Locale('zh'),
    const Locale('en'),
  ],
  home: _HomePage(),
)

系统语言为英文:

系统语言为中文:

此方法只在 Android 上有效,iOS 上没有效果。

设置默认语言

如果 App 仅支持英文和中文,其他系统的语言也默认使用中文:

代码语言:javascript
复制
MaterialApp(
  onGenerateTitle: (context) {
    return AppLocalizations.of(context).title;
  },
  localeResolutionCallback:
      (Locale locale, Iterable<Locale> supportedLocales) {
    var result = supportedLocales
        .where((element) => element.languageCode == locale.languageCode);
    if (result.isNotEmpty) {
      return locale;
    }
    return Locale('zh');
  },
  localizationsDelegates: [
    AppLocalizationsDelegate(),
    GlobalMaterialLocalizations.delegate,
    GlobalWidgetsLocalizations.delegate,
    GlobalCupertinoLocalizations.delegate,
  ],
  supportedLocales: [
    const Locale('zh'),
    const Locale('en'),
  ],
  home: _HomePage(),
)

localeResolutionCallback 回调中 locale 参数表示当前系统语言,supportedLocales 表示支持的语言,即 MaterialApp.supportedLocales 设置的值。

通过这两个参数判断当然系统语言是否在支持的范围内,如果支持则返回系统语言,不支持则返回默认语言。

使用此方法也可以实现所有英语区域的国家使用英语,而国内、香港、澳门等使用中文。

监听系统语言切换

当更改系统语言设置时,Localizations 组件将会重新 build,而用户就看到了语言的切换,这个过程是系统完成的,代码并不需要主动去监听语言切换,但如果想监听语言切换可以通过 localeResolutionCallback 或 localeListResolutionCallback 回调来监听。通常情况下,使用localeListResolutionCallback,localeListResolutionCallback有两个参数:List<Locale> locales 和 Iterable<Locale> supportedLocales,在较新的Android系统中可以设置语言列表,List<Locale> locales就表示这个语言列表,

supportedLocales为当前应用支持的locale列表,是在MaterialApp中设置supportedLocales的值。localeListResolutionCallback返回一个Locale,此Locale表示最终使用的Locale,一般情况下在App不支持当前语言时返回一个默认值。localeListResolutionCallback的用法如下:

代码语言:javascript
复制
MaterialApp(
      supportedLocales: [
        Locale('zh'),
        Locale('en'),
      ],
      localeListResolutionCallback: (List<Locale> locales, Iterable<Locale> supportLocales){
        print('locales:$locales');
        print('supportLocales:$supportLocales');
      },     
)

输出如下:

代码语言:javascript
复制
locales:[zh_Hans_CN, ja_JP, en_GB]
supportLocales:[zh, en]

也可以通过如下代码获取当前系统语言:

代码语言:javascript
复制
Locale myLocale = Localizations.localeOf(context);

应用程序内切换语言

应用程序实现切换语言功能只需将 MaterialApp 中 locale 属性作为一个变量,切换不同的 Locale 即可达到切换语言的目的。代码如下:

代码语言:javascript
复制
class IntlApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MyApp();
  }
}



class MyApp extends StatefulWidget {

  @override
  MyAppState createState() => MyAppState();
}

class MyAppState extends State<MyApp> {

  static _AppSetting setting = _AppSetting();

  @override
  void initState() {
    super.initState();
    setting.changeLocale = (Locale locale) {
      setState(() {
        setting._locale = locale;
      });
    };
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      onGenerateTitle: (context) {
        return AppLocalizations.of(context).title;
      },
      localeResolutionCallback:
          (Locale locale, Iterable<Locale> supportedLocales) {
        var result = supportedLocales
            .where((element) => element.languageCode == locale.languageCode);
        if (result.isNotEmpty) {
          return locale;
        }
        return Locale('zh');
      },
      locale: setting._locale,
      localizationsDelegates: [
        AppLocalizationsDelegate(),
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
      ],
      supportedLocales: [
        const Locale('zh'),
        const Locale('en'),
      ],
      home: _HomePage(),
    );
  }
}

_HomePage 代码:

代码语言:javascript
复制
class _HomePage extends StatefulWidget {
  @override
  __HomePageState createState() => __HomePageState();
}

class __HomePageState extends State<_HomePage> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(),
      body: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('${AppLocalizations.of(context).title}'),
          Row(
            mainAxisAlignment: MainAxisAlignment.center,
            children: [
              RaisedButton(
                child: Text('中文'),
                onPressed: () {
                  MyAppState.setting.changeLocale(Locale('zh'));
                },
              ),
              RaisedButton(
                child: Text('英文'),
                onPressed: () {
                  MyAppState.setting.changeLocale(Locale('en'));
                },
              ),
            ],
          ),
        ],
      ),
    );
  }
}

_AppSetting 代码:

代码语言:javascript
复制
class _AppSetting {
  _AppSetting();

  Null Function(Locale locale) changeLocale;
  Locale _locale;
}
本文参与 腾讯云自媒体分享计划,分享自微信公众号。
原始发表:2020-11-17,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 老孟Flutter 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体分享计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • 使App支持国际化
  • 系统是如何实现国际化的?
  • 添加系统国际化支持
  • 添加应用程序 title 国际化
  • 设置默认语言
  • 监听系统语言切换
  • 应用程序内切换语言
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档