在app文字语种 上,公司App主要针对国外用户,但同时还得兼顾国内的用户。
对于flutter来说,主要有两种实现方式,但是基本思想是一样的:都是根据App运行环境的语种,加载(我使用的本地加载)不同的静态资源,再进行渲染。
第一种方式利用 shared_preferences
主要记录第一种持久化存储的方案,因为后期的开发中还会使用到 sqlite
配置:
flutter已经提供国际化多语言,我们只需要配置好,然后使用就可以了。
1,准备两个语言版本的 json 文件:
// xlfd_zh.json
{
"title": "我是小李飞刀"
}
// xlfd_en.json
{
"title": "i am xlfd"
}
在pubspec.yaml配置assets:
flutter:
uses-material-design: true
assets:
- lang/xlfd_en.json
- lang/xlfd_zh.json
2,引入依赖 --- pubspec.yaml
flutter_localizations:flutter内置实现本地化的库;
shared_preferences:flutter插件,对android(SharedPreferences)和ios(NSUserDefaults)的存储进行了封装,可实现键值对存取;
flutter_localizations:
sdk: flutter
shared_preferences: ^0.5.3+4
ps:记得运行 flutter pub get
关键的两点是:如何拿到json文件的内容,并且根据系统语言加载对应的jason内容;如何监测系统语言的变化(用户在设置里切换语言),并且知道当前系统是何种语言;
3,监测系统语言的变化;获取当前系统是何种语言
根级 Widget MaterialApp 中有一个配置项 localeResolutionCallback,值是一个回调,在App打开或者语言配置发生变化(在手机的设置里更改语言,app代码更改等)的时候会触发这个回调,入参两个参数 deviceLocale supportedLocales;前者会入参一个携带语言信息的Locale值,后者包含我们后面即将设置的语言支持种类
然后新建一个trahslations.dart文件:
import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart' show rootBundle;
import 'package:shared_preferences/shared_preferences.dart';
/// 自定义的Translations类
class Translations {
Translations(Locale locale) {
this.locale = locale;
_localizedValues = null;
}
Locale locale;
static Map<dynamic, dynamic> _localizedValues;
static Translations of(BuildContext context){
return Localizations.of<Translations>(context, Translations);
}
String text(String key) {
if(_localizedValues==null) {
return "json文件无内容或获取内容失败";
}
return _localizedValues[key] ?? '**找不到 $key 对应的值**';
}
static Future<Translations> load(Locale locale) async {
SharedPreferences sp = await SharedPreferences.getInstance();
// 取出存储中的语种"lang"
String lang = sp.get("lang") == null ? "zh" : sp.get("lang");
print('配置中存储的语言:$lang');
Translations translations = new Translations(locale);
// 读取json,格式化---此处注意层级关系
String jsonContent = await rootBundle.loadString("lang/xlfd_$lang.json");
_localizedValues = json.decode(jsonContent);
applic.shouldReload = false;
return translations;
}
get currentLanguage => locale.languageCode;
}
/// 自定义的localization代表,它的作用是在验证支持的语言前,初始化我们的自定义Translations类
class TranslationsDelegate extends LocalizationsDelegate<Translations> {
const TranslationsDelegate();
/// 改这里是为了不硬编码支持的语言
@override
bool isSupported(Locale locale) => applic.supportedLanguages.contains(locale.languageCode);
@override
Future<Translations> load(Locale locale)=> Translations.load(locale);
@override
bool shouldReload(TranslationsDelegate old) => false;
}
/// Delegate类的实现,每次选择一种新的语言时,强制初始化一个新的Translations类
class SpecificLocalizationDelegate extends LocalizationsDelegate<Translations> {
final Locale overriddenLocale;
const SpecificLocalizationDelegate(this.overriddenLocale);
@override
bool isSupported(Locale locale) => overriddenLocale != null;
@override
Future<Translations> load(Locale locale) => Translations.load(overriddenLocale);
@override
bool shouldReload(LocalizationsDelegate<Translations> old) {
return applic.shouldReload??false;
}
}
typedef void LocaleChangeCallback(Locale locale);
class APPLIC {
// 支持的语言列表
final List<String> supportedLanguages = ['en','zh'];
// 支持的Locales列表
Iterable<Locale> supportedLocales() => supportedLanguages.map<Locale>((lang) => new Locale(lang, ''));
// 当语言改变时调用的方法
LocaleChangeCallback onLocaleChanged;
bool shouldReload;
static final APPLIC _applic = new APPLIC._internal();
factory APPLIC(){
return _applic;
}
APPLIC._internal();
}
APPLIC applic = new APPLIC();
准备工作基本完毕,现在进行配置:
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
/// 国际化依赖
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:shared_preferences/shared_preferences.dart';
import 'lang/trahslations.dart';
/// 国际化依赖
import 'pages/Routes.dart';
import 'pages/quotePages.dart';
void main() => runApp(MyApp());
class MyApp extends StatefulWidget {
MyApp({Key key}) : super(key: key);
_MyAppState createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
SpecificLocalizationDelegate _localeOverrideDelegate;
onLocaleChange(Locale locale) async {
print('onLocaleChange--触发');
// 实例化SharedPreferences以供存储
SharedPreferences sp = await SharedPreferences.getInstance();
// 更新语言配置 --- key为"lang",value为locale.languageCode
await sp.setString("lang", locale.languageCode);
// 根据语言更新_localeOverrideDelegate,以供给localizationsDelegates
setState(() {
_localeOverrideDelegate = new SpecificLocalizationDelegate(locale);
});
}
@override
void initState() {
super.initState();
_localeOverrideDelegate = new SpecificLocalizationDelegate(null);
applic.onLocaleChanged = onLocaleChange;
}
// App 根级 widget
@override
Widget build(BuildContext context) {
return MaterialApp(
home: Sign_inPage(), // App 首页
routes: routesMap(), // routes注册
initialRoute: 'sign_in', // 在注册了routes的前提下,指定一个route作为首页 --- 级别高于 home
// 国际化配置
localizationsDelegates: [
_localeOverrideDelegate,
const TranslationsDelegate(),
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: applic.supportedLocales(), // 支持的语种
localeResolutionCallback: (deviceLocale, supportedLocales) {
String langStr = deviceLocale.toString();
bool isCN = false;
if (langStr.isNotEmpty) {isCN = deviceLocale.toString().toLowerCase().indexOf('zh', 0) > -1;}
String langCode = 'en';
if (isCN) {langCode = 'zh';}
applic.shouldReload = true;
applic.onLocaleChanged(new Locale(langCode,''));
return;
}
);
}
}
4,读取 json 文件,并且根据系统语言(第3步提供)加载对应的 json 文件
在第三步中的trahslations.dart文件内:
根据lang动态加载不同的json文件;
使用
String _lang(String key) {
return Translations.of(context).text(key);
}
_lang('title');
主动变更语言:
添加新的语言
首先在 trahslations.dart 文件中的 APPLIC 类里的 supportedLanguages 添加上要增加的语言code:
然后添加json文件:
// xlfd_xlfd.json
{
"title": "俺是小李飞刀"
}
flutter:
uses-material-design: true
assets:
- lang/xlfd_en.json
- lang/xlfd_zh.json
- lang/xlfd_xlfd.json
测试:
效果: