资料
宁皓网 20 Flutter移动应用:国际化
Flutter实战*第二版 第十三章 国际化我的GIT仓库 / 分支: learn-localizations-ninghao
环境
pubspec.yaml
name: flutter_project
description: A new Flutter project.
publish_to: 'none' # Remove this line if you wish to publish to pub.dev
version: 1.0.0+1
environment:
  sdk: '>=2.12.0 <3.0.0'
dependencies:
  dio: ^4.0.4
  flutter:
    sdk: flutter
  flutter_localizations:
    sdk: flutter
  intl: ^0.17.0
dev_dependencies:
  flutter_test:
    sdk: flutter
  intl_generator: ^0.1.0+0
#  intl_translation: ^0.17.10
flutter:
  uses-material-design: trueFlutter环境
Flutter 2.6.0-6.0.pre.6 • channel master • https://github.com/flutter/flutter.git
Framework • revision 0c5431d99c (5 months ago) • 2021-09-05 22:31:02 -0400
Engine • revision b9c633900e
Tools • Dart 2.15.0 (build 2.15.0-82.0.dev)android studio

项目结构

设置国际化参数
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_project/localizations_const.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'i18n/i18n_demo.dart';
// 自动生成的
import 'i18n/intl/ninghao_demo_localizations.dart';
// 手动生成的
// import 'i18n/ninghao_demo_localizations.dart';
void main() => runApp(Home());
class Home extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      //locale: locale_enUs,
      // 系统回调回来的语言设置
      localeResolutionCallback: (
        Locale? locale,
        Iterable<Locale> supportedLocales,
      ) {
        print('localeResolutionCallback locale = $locale');
        print('localeResolutionCallback supportedLocales = $supportedLocales');
        return locale_enUs;
      },
      localizationsDelegates: [
        // 本地化的代理类
        // 为Material组件库提供的本地化的字符串和其他值,它可以使Material组建支持多语言
        GlobalMaterialLocalizations.delegate,
        // 定义组件默认的文本方向,从左到右或从右到左,这是因为有些语言的阅读习惯不是从左到右
        GlobalWidgetsLocalizations.delegate,
        // 注册我们自己的Delegate
        DemoLocalizationsDelegate(),
        NinghaoDemoLocalizationDelegate(),
      ],
      supportedLocales: mapLocals,
      title: 'Woolha.com Flutter Tutorial',
      theme: ThemeData(primarySwatch: Colors.blue),
      home: MyApp(),
      debugShowCheckedModeBanner: false,
    );
  }
}
class MyApp extends StatefulWidget {
  @override
  State<MyApp> createState() => _MyAppState();
}
class _MyAppState extends State<MyApp> {
  LOCAL_TYPE local_type = LOCAL_TYPE.CH;
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      appBar: AppBar(
        title: Text(DemoLocalizations.of(context).title),
        centerTitle: true,
        backgroundColor: Colors.green,
        brightness: Brightness.dark,
        //automaticallyImplyLeading: false,
        leading: Icon(Icons.add),
        actions: [
          IconButton(
            onPressed: () {
              setState(() {
                //local_type = toggleLocal(local_type);
                _showDatePicker1(context);
              });
            },
            icon: Icon(Icons.toggle_on),
          ),
        ],
      ),
      body: I18nDemo(),
      // body: Center(
      //   child: Text(getLocalName(local_type)),
      // ),
    );
  }
  // 日历选择
  Future<DateTime?> _showDatePicker1(BuildContext context) {
    bool useIOS = true;
    var date = DateTime.now();
    return useIOS
        ? showDatePicker(
            context: context,
            initialDate: date,
            firstDate: date,
            lastDate: date.add(Duration(days: 30)),
          )
        : showCupertinoModalPopup(
            context: context,
            builder: (ctx) {
              return Container(
                height: 200,
                color: Colors.white,
                child: CupertinoDatePicker(
                  mode: CupertinoDatePickerMode.dateAndTime,
                  minimumDate: date,
                  maximumDate: date.add(Duration(days: 30)),
                  onDateTimeChanged: (DateTime value) {
                    print(value);
                  },
                  maximumYear: date.year + 1,
                ),
              );
            },
          );
  }
}设置固定的Locale
- 设置locale参数
 
MaterialApp(
      locale: locale_enUs,
      ....- 通过获取系统语言的回调监听设置
 
MaterialApp(
      // 系统回调回来的语言设置
      localeResolutionCallback: (
        Locale? locale,
        Iterable<Locale> supportedLocales,
      ) {
        print('localeResolutionCallback locale = $locale');
        print('localeResolutionCallback supportedLocales = $supportedLocales');
        return locale_enUs; // 此时默认的是英文, 可以根据具体要求修改
      },
      ...
    );其中:Locale? locale 返回系统修改的语言
 Iterable supportedLocales 为APP中所支持的语言
- 设置国际化的代理lo
 
MaterialApp(
	...
      localizationsDelegates: [
        // 本地化的代理类
        // 为Material组件库提供的本地化的字符串和其他值,它可以使Material组建支持多语言
        GlobalMaterialLocalizations.delegate,
        // 定义组件默认的文本方向,从左到右或从右到左,这是因为有些语言的阅读习惯不是从左到右
        GlobalWidgetsLocalizations.delegate,
        // 注册我们自己的Delegate
        DemoLocalizationsDelegate(),
        NinghaoDemoLocalizationDelegate(),
      ],
	...
    );设置代理, 所有的代理都继承LocalizationsDelegate, 可以使用系统的+自定义的
- 所有支持的语言
 
const locale_enUs = const Locale('en', 'US');
const locale_zhCN = const Locale('zh', 'CN');
const locale_zhHK = const Locale('zh', 'HK');
// 这里放所有能支持的语言
const List<Locale> mapLocals = [
  locale_enUs, // 美国英语
  locale_zhCN, // 中文简体
  locale_zhHK,
];
MaterialApp(
	...
      supportedLocales: mapLocals,
	...
    )手写国际化
import 'dart:ui';
import 'package:flutter/cupertino.dart';
import 'package:flutter/foundation.dart';
import '../localizations_const.dart';
// 手动版本
class NinghaoDemoLocalizations {
  //传入要使用的Local 
  final Locale locale;
  NinghaoDemoLocalizations(this.locale);
  // 工具方法, 通过Localizations.of<T>(context, T) 来获取相对应的语言
  static NinghaoDemoLocalizations? of(BuildContext context) {
    return Localizations.of<NinghaoDemoLocalizations>(
        context, NinghaoDemoLocalizations);
  }
  // 'en'就是语言的名字
  // 每个map中, 都有具体要使用的字段
  static Map<String, Map<String, String>> _localized = {
    'en': {
      'title': 'hello',
    },
    'zh': {
      'title': '您好',
    },
  };
  // 通过传入的locale.languageCode取出对应的字段
  String? get title {
    Map<String, String>? map = _localized[locale.languageCode];
    return map == null ? null : map['title'];
  }
}
class NinghaoDemoLocalizationDelegate
    extends LocalizationsDelegate<NinghaoDemoLocalizations> {
  NinghaoDemoLocalizationDelegate();
  // 判断是否支持这个语言
  @override
  bool isSupported(Locale locale) {
    return isContainsThisLanguage(locale);
  }
  // 载入对应的语言资源包
  @override
  Future<NinghaoDemoLocalizations> load(Locale locale) {
    return SynchronousFuture<NinghaoDemoLocalizations>(
        NinghaoDemoLocalizations(locale));
  }
  // 是否每次重新加载资源
  @override
  bool shouldReload(
      covariant LocalizationsDelegate<NinghaoDemoLocalizations> old) {
    return false; // 是否加载本地资源
  }
}使用
class I18nDemo extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    Locale locale = Localizations.localeOf(context);
    return Center(
      child: Column(
        mainAxisAlignment: MainAxisAlignment.center,
        children: [
          Text('${locale.toString()}'),
          Text(
            '${Localizations.of(context, NinghaoDemoLocalizations).title}', // 使用手动创建title字段
            style: Theme.of(context).textTheme.headline4,
          ),
        ],
      ),
    );
  }
}
}自动生成国际化
在我本地的环境使用
dev_dependencies:
  intl_generator: ^0.1.0+0
  ...先创建一个类,作为要生成arb的模版
class NinghaoDemoLocalizations {
  String get title =>
      Intl.message(
        'hello',
        name: 'title',
        desc: 'demo localizations.',
      );
  String greet(String name) =>
      Intl.message(
        'hello $name',
        name: 'greet',
        desc: 'greet someone.',
        args: [name],
      );
}通过命令行生成arb文件
flutter pub run intl_generator:extract_to_arb --output-dir=lib/i18n/intl/ lib/i18n/intl/ninghao_demo_localizations.dart分解:
flutter pub run intl_generator:extract_to_arb 
	--output-dir=lib/i18n/intl/    // 要生成arb文件的目录
	lib/i18n/intl/ninghao_demo_localizations.dart // 生成arb文件使用的模板会生成intl_messages.arb文件
{
  "@@last_modified": "2022-02-09T15:56:37.677571",
  "title": "hello",
  "@title": {
    "description": "demo localizations.",
    "type": "text",
    "placeholders": {}
  },
  "greet": "hello {name}",
  "@greet": {
    "description": "greet someone.",
    "type": "text",
    "placeholders": {
      "name": {}
    }
  }
}通过粘贴复制改名,创建多个语言的文件intl_en.arb和intl_zh.arb
intl_en.arb
{
  "@@last_modified": "2022-02-09T15:56:37.677571",
  "title": "hello",
  "@title": {
    "description": "demo localizations.",
    "type": "text",
    "placeholders": {}
  },
  "greet": "hello {name}",
  "@greet": {
    "description": "greet someone.",
    "type": "text",
    "placeholders": {
      "name": {}
    }
  }
}intl_zh.arb
{
  "@@last_modified": "2022-02-09T15:56:37.677571",
  "title": "您好",
  "@title": {
    "description": "演示本地化.",
    "type": "text",
    "placeholders": {}
  },
  "greet": "您好 {name}",
  "@greet": {
    "description": "问候某人.",
    "type": "text",
    "placeholders": {
      "name": {}
    }
  }
}通过命令,将所有的arb文件生成对应的dart文件,及dart管理文件
以下都是自动生成的
 ninghao_demo_messages_all.dart
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that looks up messages for specific locales by
// delegating to the appropriate library.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:implementation_imports, file_names
// ignore_for_file:unnecessary_brace_in_string_interps, directives_ordering
// ignore_for_file:argument_type_not_assignable, invalid_assignment
// ignore_for_file:prefer_single_quotes, prefer_generic_function_type_aliases
// ignore_for_file:comment_references
// ignore_for_file:avoid_catches_without_on_clauses
import 'dart:async';
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
import 'package:intl/src/intl_helpers.dart';
import 'ninghao_demo_messages_en.dart' as messages_en;
import 'ninghao_demo_messages_messages.dart' as messages_messages;
import 'ninghao_demo_messages_zh.dart' as messages_zh;
typedef Future<dynamic> LibraryLoader();
Map<String, LibraryLoader> _deferredLibraries = {
  'en': () => Future.value(null),
  'messages': () => Future.value(null),
  'zh': () => Future.value(null),
};
MessageLookupByLibrary? _findExact(String localeName) {
  switch (localeName) {
    case 'en':
      return messages_en.messages;
    case 'messages':
      return messages_messages.messages;
    case 'zh':
      return messages_zh.messages;
    default:
      return null;
  }
}
/// User programs should call this before using [localeName] for messages.
Future<bool> initializeMessages(String localeName) async {
  // 验证是否是有效的Local
  final availableLocale = Intl.verifiedLocale(
    localeName,
    (locale) => _deferredLibraries[locale] != null,
    onFailure: (_) => null);
  if (availableLocale == null) {
    return Future.value(false);
  }
  final lib = _deferredLibraries[availableLocale];
  await (lib == null ? Future.value(false) : lib());
  initializeInternalMessageLookup(() => CompositeMessageLookup());
  messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
  return Future.value(true);
}
bool _messagesExistFor(String locale) {
  try {
    return _findExact(locale) != null;
  } catch (e) {
    return false;
  }
}
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
  final actualLocale = Intl.verifiedLocale(locale, _messagesExistFor,
      onFailure: (_) => null);
  if (actualLocale == null) return null;
  return _findExact(actualLocale);
}ninghao_demo_messages_en.dart
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a en locale. All the
// messages from the main program should be duplicated here with the same
// function name.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:unnecessary_brace_in_string_interps
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, always_declare_return_types
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
final messages = MessageLookup();
typedef String MessageIfAbsent(String? messageStr, List<Object>? args);
class MessageLookup extends MessageLookupByLibrary {
  String get localeName => 'en';
  static m0(name) => "hello ${name}";
  final messages = _notInlinedMessages(_notInlinedMessages);
  static Map<String, Function> _notInlinedMessages(_) => <String, Function> {
    "greet" : m0,
    "title" : MessageLookupByLibrary.simpleMessage("hello")
  };
}ninghao_demo_messages_messages.dart
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a messages locale. All the
// messages from the main program should be duplicated here with the same
// function name.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:unnecessary_brace_in_string_interps
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, always_declare_return_types
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
final messages = MessageLookup();
typedef String MessageIfAbsent(String? messageStr, List<Object>? args);
class MessageLookup extends MessageLookupByLibrary {
  String get localeName => 'messages';
  static m0(name) => "hello ${name}";
  final messages = _notInlinedMessages(_notInlinedMessages);
  static Map<String, Function> _notInlinedMessages(_) => <String, Function> {
    "greet" : m0,
    "title" : MessageLookupByLibrary.simpleMessage("hello")
  };
}ninghao_demo_messages_zh.dart
// DO NOT EDIT. This is code generated via package:intl/generate_localized.dart
// This is a library that provides messages for a zh locale. All the
// messages from the main program should be duplicated here with the same
// function name.
// Ignore issues from commonly used lints in this file.
// ignore_for_file:unnecessary_brace_in_string_interps
// ignore_for_file:prefer_single_quotes,comment_references, directives_ordering
// ignore_for_file:annotate_overrides,prefer_generic_function_type_aliases
// ignore_for_file:unused_import, file_names, always_declare_return_types
import 'package:intl/intl.dart';
import 'package:intl/message_lookup_by_library.dart';
final messages = MessageLookup();
typedef String MessageIfAbsent(String? messageStr, List<Object>? args);
class MessageLookup extends MessageLookupByLibrary {
  String get localeName => 'zh';
  static m0(name) => "您好 ${name}";
  final messages = _notInlinedMessages(_notInlinedMessages);
  static Map<String, Function> _notInlinedMessages(_) => <String, Function> {
    "greet" : m0,
    "title" : MessageLookupByLibrary.simpleMessage("您好")
  };
}在ninghao_demo_localization.dart中使用自动生成的文件
import 'package:flutter/foundation.dart';
import 'package:flutter/material.dart';
import 'package:intl/intl.dart';
import '../../localizations_const.dart';
import 'ninghao_demo_messages_all.dart';
// 自动版本
// 宁皓网教程
// flutter pub pub run intl_translation:extract_to_arb
// --output-dir=lib/demo/i18n/intl/
// lib/demo/i18n/intl/ninghao_demo_localizations.dart
// 自己摸索
// flutter pub run intl_generator:extract_to_arb --output-dir=lib/i18n/intl/ lib/i18n/intl/ninghao_demo_localizations.dart
class NinghaoDemoLocalizations {
	// 通过Localizations.of<T>(context, T)使用国际化
  static NinghaoDemoLocalizations? of(BuildContext context) {
    return Localizations.of<NinghaoDemoLocalizations>(
        context,
        NinghaoDemoLocalizations
    );
  }
  // 加载要使用的资源包
  static Future<NinghaoDemoLocalizations> load(Locale locale) {
    // 当前要使用的Local
    final String name = locale.countryCode?.isEmpty == true
        ? locale.languageCode
        : locale.toString();
	// 带有格式化的local string   localName = "en_US"
    final String localeName = Intl.canonicalizedLocale(name);
    return initializeMessages(localeName).then((bool _) {
      Intl.defaultLocale = localeName;
      return NinghaoDemoLocalizations();
    });
  }
  String get title =>
      Intl.message(
        'hello',
        name: 'title',
        desc: 'demo localizations.',
      );
  String greet(String name) =>
      Intl.message(
        'hello $name',
        name: 'greet',
        desc: 'greet someone.',
        args: [name],
      );
}
// 通过arb反过来生成dart文件
// flutter pub pub run intl_translation:generate_from_arb
// --generated-file-prefix=ninghao_demo_ --output-dir=lib/demo/i18n/intl
// --no-use-deferred-loading lib/demo/i18n/intl/ninghao_demo_localizations.dart
// lib/demo/i18n/int/intl_*.arb
// 自己摸索
//flutter pub run intl_generator:generate_from_arb --generated-file-prefix=ninghao_demo_ --output-dir=lib/i18n/intl/ --no-use-deferred-loading lib/i18n/intl/ninghao_demo_localizations.dart lib/i18n/intl/intl_*.arb
class NinghaoDemoLocalizationDelegate extends LocalizationsDelegate<NinghaoDemoLocalizations> {
  NinghaoDemoLocalizationDelegate();
  @override
  bool isSupported(Locale locale) {
    return isContainsThisLanguage(locale);
  }
  @override
  Future<NinghaoDemoLocalizations> load(Locale locale) {
    return NinghaoDemoLocalizations.load(locale); // 使用自动生成的国际化语言包
  }
  @override
  bool shouldReload(
      covariant LocalizationsDelegate<NinghaoDemoLocalizations> old) {
    return false; // 是否每次重新加载本地资源
  }
}自动国际化如何实现的
- 通过load进行加载
 
Future<bool> initializeMessages(String localeName) async {
  final availableLocale = Intl.verifiedLocale(
    localeName,
    (locale) => _deferredLibraries[locale] != null,
    onFailure: (_) => null);
  if (availableLocale == null) {
    return Future.value(false);
  }
  final lib = _deferredLibraries[availableLocale];
  await (lib == null ? Future.value(false) : lib());
  initializeInternalMessageLookup(() => CompositeMessageLookup());
  messageLookup.addLocale(availableLocale, _findGeneratedMessagesFor);
  return Future.value(true);
}
MessageLookupByLibrary? _findGeneratedMessagesFor(String locale) {
  final actualLocale = Intl.verifiedLocale(locale, _messagesExistFor,
      onFailure: (_) => null);
  if (actualLocale == null) return null;
  return _findExact(actualLocale);
}
MessageLookupByLibrary? _findExact(String localeName) {
  switch (localeName) {
    case 'en':
      return messages_en.messages;
    case 'messages':
      return messages_messages.messages;
    case 'zh':
      return messages_zh.messages;
    default:
      return null;
  }
}
// 是这么引用的
import 'ninghao_demo_messages_en.dart' as messages_en;
import 'ninghao_demo_messages_messages.dart' as messages_messages;
import 'ninghao_demo_messages_zh.dart' as messages_zh;调用了就是自动生成的文件

-------一下是Old版本-------------------------------------------------------------
 -------一下是Old版本-------------------------------------------------------------
 -------一下是Old版本-------------------------------------------------------------
 -------一下是Old版本-------------------------------------------------------------
 -------一下是Old版本-------------------------------------------------------------
安装步骤
添加依赖
将intl package 添加到pubspec.yaml文件中
dependencies:
		  flutter:
		    sdk: flutter
		  flutter_localizations:
		    sdk: flutter
		  intl: ^0.17.0 # Add this line在pubspec.yaml中,启用generate标志
# The following section is specific to Flutter.
		flutter:
		  generate: true # Add this line在Flutter项目中的根目录中添加一个新的yaml文件,命名为l10n.yaml文件
arb-dir: lib/l10n                         # 文件存放的目录
		template-arb-file: intl_en.arb   # 依赖的模版文件 
		output-localization-file: app_localizations.dart   # 要生成的文件(自动生成,不清楚)
在${FLUTTER_PROJECT}/lib/l10n中,添加intl_en.arb模版文件。如下:
{
    "helloWorld": "Hello World!",
    "@helloWorld": {
      "description": "The conventional newborn programmer greeting"
    }
}在同一目录中,添加一个intl_es.arb文件,对同一条信息做西班牙语的翻译
{
    "helloWorld": "Hola Mundo!"
}自动生成generate目录


正常使用
引入依赖
import 'package:flutter_localizations/flutter_localizations.dart'; // 国际化
 import 'package:flutter_helper/generated/l10n.dart';添加代理
localizationsDelegates: [
        S.delegate, // 这个是自动生成的
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate
      ],添加多语言支持
supportedLocales: [
        Locale('en', ''), //English, no country code
        Locale('ko', ''), //Spanish, no country code
        Locale('zh', ''), //Spanish, no country code
      ],添加默认语言
locale: Locale('ko', ''),总的添加Local
class LanguageApp extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    // var localization = AppLocalizationDelegate();
    // var support = localization.supportedLocales;
    return const MaterialApp(
      title: 'Localizations Sample App',
      locale: Locale('ko', ''),
      localizationsDelegates: [
        S.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate
      ],
      supportedLocales: [
        Locale('en', ''), //English, no country code
        Locale('ko', ''), //Spanish, no country code
        Locale('zh', ''), //Spanish, no country code
      ],
      home:  Scaffold(
        body: _MyHome(),
      ),
    );
  }
}获取字符串资源
Text(S.of(context).helloWorld) // helloWorld是自动生成的方法
                










