001 国际化定义
涉及语言及地区差异的适配改造过程。除了翻译文案之外,还需要将货币单位和背景
图等资源也设计成可根据不同地区自适应的变量。即语言差异配置抽取和国际化代码生成。
002 如何实现
国际化插件flutter_localizations,其内部提供了不同语言地区的配置封装,能够帮助我们
自动地从翻译稿生成Dart代码。
使用多个arb文件存储文案在不同语言地区的映射关系,并使用Flutter i18n插件来实现代码自动转换。
关键点:在程序入口为其设置了支持国际化两个重要参数即 localizationsDelegates 与 supportedLocales。前者为应用的翻译回调,而后者则为应用所支持的语言地区属性。
程序中通过 S.of(context),直接获取 arb 文件中翻译的文案。
003 代码
flutter_localizations:
sdk: flutter
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 S current;
static const GeneratedLocalizationsDelegate delegate =
GeneratedLocalizationsDelegate();
static S of(BuildContext context) => Localizations.of<S>(context, S);
@override
TextDirection get textDirection => TextDirection.ltr;
String get app_title => "app_title";
String get main_title => "My Main Title";
String message_tip(String count) => "You have pushed the button many times: $count";
}
class $en extends S {
const $en();
}
class $zh extends S {
const $zh();
@override
TextDirection get textDirection => TextDirection.ltr;
@override
String get app_title => "App标题";
@override
String get main_title => "主标题";
@override
String message_tip(String count) => "点击按钮次数: $count";
}
class GeneratedLocalizationsDelegate extends LocalizationsDelegate<S> {
const GeneratedLocalizationsDelegate();
List<Locale> get supportedLocales {
return const <Locale>[
Locale("en", ""),
Locale("zh", ""),
];
}
LocaleListResolutionCallback listResolution({Locale fallback, bool withCountry = true}) {
return (List<Locale> locales, Iterable<Locale> supported) {
if (locales == null || locales.isEmpty) {
return fallback ?? supported.first;
} else {
return _resolve(locales.first, fallback, supported, withCountry);
}
};
}
LocaleResolutionCallback resolution({Locale fallback, bool withCountry = true}) {
return (Locale locale, Iterable<Locale> supported) {
return _resolve(locale, fallback, supported, withCountry);
};
}
@override
Future<S> load(Locale locale) {
final String lang = getLang(locale);
if (lang != null) {
switch (lang) {
case "en":
S.current = const $en();
return SynchronousFuture<S>(S.current);
case "zh":
S.current = const $zh();
return SynchronousFuture<S>(S.current);
default:
// NO-OP.
}
}
S.current = const S();
return SynchronousFuture<S>(S.current);
}
@override
bool isSupported(Locale locale) => _isSupported(locale, true);
@override
bool shouldReload(GeneratedLocalizationsDelegate old) => false;
///
/// Internal method to resolve a locale from a list of locales.
///
Locale _resolve(Locale locale, Locale fallback, Iterable<Locale> supported, bool withCountry) {
if (locale == null || !_isSupported(locale, withCountry)) {
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;
}
}
///
/// Returns true if the specified locale is supported, false otherwise.
///
bool _isSupported(Locale locale, bool withCountry) {
if (locale != null) {
for (Locale supportedLocale in supportedLocales) {
// Language must always match both locales.
if (supportedLocale.languageCode != locale.languageCode) {
continue;
}
// If country code matches, return this locale.
if (supportedLocale.countryCode == locale.countryCode) {
return true;
}
// If no country requirement is requested, check if this locale has no country.
if (true != withCountry && (supportedLocale.countryCode == null || supportedLocale.countryCode.isEmpty)) {
return true;
}
}
}
return false;
}
}
String getLang(Locale l) => l == null
? null
: l.countryCode != null && l.countryCode.isEmpty
? l.languageCode
: l.toString();
import 'package:flutter/material.dart';
import 'package:i18n_demo/generated/i18n.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
void main() => runApp(MyApp());
class MyApp extends StatelessWidget {
// This widget is the root of your application.
@override
Widget build(BuildContext context) {
return MaterialApp(
localizationsDelegates: const [
S.delegate,
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
],
supportedLocales: S.delegate.supportedLocales,
onGenerateTitle: (context){
return S.of(context).app_title;
},
theme: ThemeData(
primarySwatch: Colors.blue,
),
home: MyHomePage(),
);
}
}
class MyHomePage extends StatefulWidget {
MyHomePage({Key key}) : super(key: key);
@override
_MyHomePageState createState() => _MyHomePageState();
}
class _MyHomePageState extends State<MyHomePage> {
int _counter = 0;
_incrementCounter() {
setState(() {
_counter++;
});
}
@override
Widget build(BuildContext context) {
return Scaffold(
appBar: AppBar(
// Here we take the value from the MyHomePage object that was created by
// the App.build method, and use it to set our appbar title.
title: Text(S.of(context).main_title),
),
body: Center(
child: Column(
children: <Widget>[
Text(
S.of(context).message_tip(_counter.toString())
)
],
)
),
floatingActionButton: FloatingActionButton(
onPressed: _incrementCounter,
tooltip: 'Increment',
child: Icon(Icons.add),
), // This trailing comma makes auto-formatting nicer for build methods.
);
}
}
strings_en.arb
{
"app_title": "app_title",
"main_title": "My Main Title",
"message_tip" : "You have pushed the button many times: $count"
}
strings_zh.arb
{
"app_title": "App标题",
"main_title": "主标题",
"message_tip" : "点击按钮次数: $count"
}