iT邦幫忙

2021 iThome 鐵人賽

DAY 6
0
Mobile Development

Flutter - 複製貼上到開發套件之旅系列 第 6

【第六天 - Flutter 多國語系】

前言

今日的程式碼 => GITHUB
這邊我想要介紹如何切換語言、設定 App 初始的語言,且下此開啟 App 時也會記住已經被切換過的語言。

備註:

這邊的教學,功能正常,但寫法不一定是最好的。這邊推薦另外的套件

設定 YAML 檔案

  flutter_localizations:
    sdk: flutter
  shared_preferences: ^2.0.7
- - - - - - - - - - - - - - - - - - - - -
  assets:
    - language/

建立 Json 檔案

建立一個 language 在專案目錄底下建立兩個 Json

  • en.json
{
 "name": "flutter_localizations example",
 "introduce": "Hello"
}
  • zh.json
{
 "name": "語言切換範例",
 "introduce" : "你好呀"
}

大致上的邏輯

會給予 sharedpreferences 一個預設的語言 data(zh),手機一打開時,會在 didChangeDependencies 裡面去讀取 sharedpreferences,進而去設定 local 的地區是屬於什麼。

MyApp

  • supportedLocales = App 要支援的語言
  • localizationsDelegates = 委託確保加載正確語言的本地化數據
  • localeResolutionCallback = 檢查手機是否支援這個語言
  • locale = 現在手機的語言是什麼
  • AppLocalizations = 是我們自己建立管理語言的一個 class
class MyApp extends StatefulWidget {
  MyApp({Key? key}) : super(key: key);

  @override
  _MyAppState createState() => _MyAppState();

  /// 當地區更改時,重新設定地區,當使用者按下變換語言時,會觸發。
  static void setLocale(BuildContext context, Locale newLocale) {
    _MyAppState? state = context.findAncestorStateOfType<_MyAppState>();
    state!.changeLocale(newLocale);
  }
}

class _MyAppState extends State<MyApp> {
  Locale _locale = new Locale.fromSubtags(languageCode: 'zh');
  /// 更改地區
  void changeLocale(Locale locale) {
    setState(() {
      this._locale = locale;
    });
  }

  @override
  void didChangeDependencies() async {
    super.didChangeDependencies();
    final languageApp = AppLocalizations();
    final localeKey = await languageApp.readLocaleKey();
    if (localeKey == "en") {
      this._locale = new Locale("en", "EN");
    } else {
      this._locale = new Locale.fromSubtags(languageCode: 'zh');
    }
    setState(() {});
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
        supportedLocales: [
          Locale('en', 'US'),
          const Locale.fromSubtags(languageCode: 'zh'),
        ],
        // These delegates make sure that the localization data for the proper language is loaded
        // 委託確保加載正確語言的本地化數據
        localizationsDelegates: [
          // This class will be added later
          // A class which loads the translations from JSON files
          AppLocalizations.delegate,
          // A class which loads the translations from JSON files
          GlobalMaterialLocalizations.delegate,
          // Built-in localization of basic text for Material widgets
          GlobalWidgetsLocalizations.delegate,
        ],
        locale: _locale,
        // Returns a locale which will be used by the app
        localeResolutionCallback: (locale, supportedLocales) {
          // Check if the current device locale is supported
          // 檢查手機是否支援這個語言
          for (var supportedLocaleLanguage in supportedLocales) {
            if (supportedLocaleLanguage.languageCode == locale?.languageCode &&
                supportedLocaleLanguage.countryCode == locale?.countryCode) {
              return supportedLocaleLanguage;
            }
          }
          // If device not support with locale to get language code then default get first on from the list
          return supportedLocales.first;
        },
        home: HomePage());
  }
}

AppLocalizations 管理語言的 class

class AppLocalizations {
  final Locale locale;
  AppLocalizations({this.locale = const Locale.fromSubtags(languageCode: 'zh')});
  /// Helper method to keep the code in the widgets concise
  /// Localizations are accessed using an InheritedWidget "of" syntax
  /// 訪問本地化
  static AppLocalizations? of(BuildContext context) {
    return Localizations.of<AppLocalizations>(context, AppLocalizations);
  }
  /// 儲存 SharedPreferences
  void keepLocaleKey(String localeKey) async {
    final _prefs = await SharedPreferences.getInstance();
    await _prefs.remove("localeKey");
    await _prefs.setString("localeKey", localeKey);
  }
  /// 讀取 SharedPreferences
  Future<String> readLocaleKey() async {
    final _prefs = await SharedPreferences.getInstance();
    // 初始化最一剛開始的語言
    return _prefs.getString("localeKey")??'zh';
  }
  
  /// 更改語言,重新設定語言
  void setLocale(BuildContext context, Locale locale) async {
    // keep value in shared pref
    keepLocaleKey(locale.languageCode);
    print("key language :${locale.languageCode}");
    MyApp.setLocale(context, locale);
  }
  /// Static member to have a simple access to the delegate from the MaterialApp
  /// 提供 Main Page 可以直接訪問。
  static const LocalizationsDelegate<AppLocalizations> delegate =
  _AppLocalizationsDelegate();

  late Map<String, String> _localizedStrings;
  /// Load the language JSON file from the "lang" folder
  /// 讀取 Json 格式
  Future<bool> load() async {
    String jsonString =
    await rootBundle.loadString('language/${locale.languageCode}.json');
    Map<String, dynamic> jsonMap = json.decode(jsonString);

    _localizedStrings = jsonMap.map((key, value) {
      return MapEntry(key, value.toString());
    });

    return true;
  }

  /// This method will be called from every widget which needs a localized text
  /// 提供每一個需要轉換語言的文字
  String translate(String key) {
    return _localizedStrings[key]!;
  }
}

/// LocalizationsDelegate is a factory for a set of localized resources
/// In this case, the localized strings will be gotten in an AppLocalizations object
/// 本地化的字符串將在 AppLocalizations 對像中獲取
class _AppLocalizationsDelegate
    extends LocalizationsDelegate<AppLocalizations> {
  // This delegate instance will never change (it doesn't even have fields!)
  // It can provide a constant constructor.
  const _AppLocalizationsDelegate();
  /// 之泉的語言代碼
  @override
  bool isSupported(Locale locale) {
    // Include all of your supported language codes here
    return ['en', 'zh'].contains(locale.languageCode);
  }
  /// 讀取 Json
  @override
  Future<AppLocalizations> load(Locale locale) async {
    // AppLocalizations class is where the JSON loading actually runs
    AppLocalizations localizations = new AppLocalizations(locale: locale);
    await localizations.load();
    return localizations;
  }
  /// 使否重新 reload
  @override
  bool shouldReload(_AppLocalizationsDelegate old) => false;
}

設定需要改的文字

Text(AppLocalizations.of(context)!.translate('introduce')),

更改語言按鈕時

AppLocalizations localeApp = AppLocalizations();

// 之後再按鈕的 onPressed 裡面 
onPressed: () async {
  if (await localeApp.readLocaleKey() == "zh") {
    localeApp.setLocale(context, Locale("en", "EN"));
  } else {
    localeApp.setLocale(
    context, Locale.fromSubtags(languageCode: 'zh'));
  }
}

上一篇
【第五天 - Fluter BottomNavigationBar(下)行為分析】
下一篇
【第七天 - Flutter Api、Json 物件教學】
系列文
Flutter - 複製貼上到開發套件之旅30

尚未有邦友留言

立即登入留言