更改Flutter语言的按钮。

55

我正在使用Flutter构建一个应用程序,目前我正在使用带有JSON的国际化,其中应用程序的语言基于用户在手机上的默认语言,这很有效,但我希望为用户提供一种更改语言的机会,而无需更改手机系统语言设置,只需单击一个按钮,然后应用程序就可以更改语言而不必经过设置。

这是代码:

主要部分:

import 'package:flutter/material.dart';
import 'package:flutter_app_darkmode/app_localizations.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

  void main() => runApp(MyApp());

  class MyApp extends StatelessWidget {
      @override
      Widget build(BuildContext context) => MaterialApp(
      supportedLocales: [
      Locale('en', "ZA"),
      Locale('pt', "MZ"),
      ],
      localizationsDelegates: [
      AppLocalizations.delegate,
      GlobalMaterialLocalizations.delegate,
      GlobalWidgetsLocalizations.delegate
      ],
      localeResolutionCallback: (locale, supportedLocales) {
       for (var supportedLocale in supportedLocales) {
        if (supportedLocale.languageCode == locale.languageCode &&
            supportedLocale.countryCode == locale.countryCode) {
          return supportedLocale;
          } else {
          if (MyHomePage.local != null) {
            for (int i = 0; i < supportedLocales.length; i++) {
              if (MyHomePage.local == supportedLocales.elementAt(i)) {
                return supportedLocales.elementAt(i);
              }}}}}
            return supportedLocales.first;
            },
           home: MyHomePage(),
          );}

         class MyHomePage extends StatefulWidget {
         @override
         _MyHomePageState createState() => _MyHomePageState();

           class _MyHomePageState extends State<MyHomePage> {
           getLocale() {
           Locale myLocale = Localizations.localeOf(context);
            print(myLocale);}

            @override
             Widget build(BuildContext context) {
              getLocale();
               return Scaffold(
                body: Center(
                child: Padding(
                 padding: const EdgeInsets.all(8.0),
                  child: Column(
                  mainAxisSize: MainAxisSize.min,
                  children: <Widget>[
                   Text(
                   AppLocalizations.of(context).translate('first_string'),
                   style: TextStyle(fontSize: 25),
                   textAlign: TextAlign.center,),
                  Text(
                  AppLocalizations.of(context).translate('second_string'),
                    style: TextStyle(fontSize: 25),
                    textAlign: TextAlign.center,),
                    RaisedButton(
                      child: Text('PT'),
                    onPressed: () {},
                     ),],),),),);}}

app_locations类:

import 'dart:async';
import 'dart:convert';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';

class AppLocalizations {
final Locale locale;

  AppLocalizations(this.locale);

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

    static const LocalizationsDelegate<AppLocalizations> delegate =
    _AppLocalizationsDelegate();

    Map<String, String> _localizedStrings;

       Future<bool> load() async {
       String jsonString =
        await rootBundle.loadString('lang/${locale.languageCode}.json');

        Map<String, dynamic> jsonMap = json.decode(jsonString);

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

        return true;}

       String translate(String key) {
      return _localizedStrings[key];}}

     class _AppLocalizationsDelegate
     extends LocalizationsDelegate<AppLocalizations> {
     const _AppLocalizationsDelegate();

       @override
       bool isSupported(Locale locale) {
       return ['en', 'pt'].contains(locale.languageCode);}

       @override
       Future<AppLocalizations> load(Locale locale) async {
       AppLocalizations localizations = new AppLocalizations(locale);
       await localizations.load();
       return localizations;}

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

3
这可能对您有所帮助,https://www.developerlibs.com/2019/03/flutter-localization-or-multi-language-example.html - Shirsh Shukla
1
我建议您研究一下Get包:https://pub.dev/packages/get。它的国际化支持非常强大且易于使用,您可以在此处阅读更多信息:https://pub.dev/packages/get#internationalization。 一旦按下按钮,您可以使用“更改语言环境”下描述的方法来更改翻译。 - Tanay Neotia
1
我建议这个链接:https://medium.com/@puneetsethi25/flutter-internationalization-switching-locales-manually-f182ec9b8ff0 - puspak saha
6个回答

102
您可以使用您喜欢的状态管理方法设置MaterialApplocale属性。例如:
class MyApp extends StatefulWidget {
  @override
  _MyAppState createState() => _MyAppState();

  static _MyAppState of(BuildContext context) => context.findAncestorStateOfType<_MyAppState>();
}

class _MyAppState extends State<MyApp> {
  Locale _locale;

  void setLocale(Locale value) {
    setState(() {
      _locale = value;
    });
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: _locale,
      home: Dashboard(),
    );
  }
}

class Dashboard extends StatelessWidget {
  @override
  Widget build(BuildContext context) {
    return Column(
      children: [
        TextButton(
          child: Text("Set locale to German"),
          onPressed: () => MyApp.of(context).setLocale(Locale.fromSubtags(languageCode: 'de')),
        ),
        TextButton(
          child: Text("Set locale to English"),
          onPressed: () => MyApp.of(context).setLocale(Locale.fromSubtags(languageCode: 'en')),
        ),
      ],
    );
  }
}

2
这对我很有用!经过一点修改:当我的FlutterApp将StatefulWidget(例如MyApp)作为主要“home”时,我无法使其正常工作。因此,我将有状态的MyApp小部件包装到一个无状态小部件中(例如MyAppWrapper)。 MyAppWrapper唯一要做的就是具有返回MyApp()的构建方法。 - drpawelo
但这样做在应用程序重新启动后不会生效,对吧? - Marcus
1
@Marcus确实无法在应用程序重新启动后保留数据。您需要将数据持久化。 - spkersten
任何保留更改的示例 - LearnFlutter
3
你可以使用Shared Preferences包来持久保存选择的数据。当选择改变时,将数据保存在其中。 然后,要读取数据,你可以这样做:`@override void didChangeDependencies() { SharedPreferencesHelper.getLanguageCode().then((languageCode) { setState(() { _locale = Locale(languageCode); }); });super.didChangeDependencies(); }` - Sam Bains
由于某些原因,我在不同的页面上更改了语言,那个页面可以正常工作,但当我返回时却不行。我需要刷新应用程序才能看到完全的更改。 - undefined

13

你必须使用 Flutter 提供的本地化功能。需要使用自定义委托和 JSON 文件来支持不同语言。

我使用了 bloc 实现此功能。

按照以下步骤进行操作:

  1. 在根目录下创建一个名为 assets/languages/ 的文件夹。
  2. 为你支持的语言创建对应的 JSON 文件,例如:en.json, es.json。
  3. 针对每个语言字符串,在每个文件中创建相应的键值对。
  4. main.dart 中创建默认的 locale, supportedLocales and localizationsDelegates
import 'package:flutter/material.dart';
import 'package:flutter_bloc/flutter_bloc.dart';
import 'package:flutter_localizations/flutter_localizations.dart';

import 'package:movie_app/common/constants/languages.dart';
import 'package:movie_app/presentation/app_localizations.dart';
import 'package:movie_app/presentation/blocs/language/language_bloc.dart';
import 'package:movie_app/presentation/journeys/home/home_screen.dart';

class MovieApp extends StatefulWidget {
  @override
  _MovieAppState createState() => _MovieAppState();
}

class _MovieAppState extends State<MovieApp> {
  LanguageBloc _languageBloc;

  @override
  void initState() {
    _languageBloc = LanguageBloc();
    super.initState();
  }

  @override
  void dispose() {
    _languageBloc.close();
    super.dispose();
  }

  @override
  Widget build(BuildContext context) {
    return BlocProvider<LanguageBloc>.value(
      value: _languageBloc,
      child: BlocBuilder<LanguageBloc, LanguageState>(
        builder: (context, state) {
          if (state is LanguageLoaded) {
            return MaterialApp(
                debugShowCheckedModeBanner: false,
                title: 'Movie App',
                home: HomeScreen(),
                supportedLocales:
                    Languages.languages.map((e) => Locale(e.code)).toList(),
                locale: state.locale,
                localizationsDelegates: [
                  AppLocalizations.delegate,
                  GlobalMaterialLocalizations.delegate,
                  GlobalWidgetsLocalizations.delegate,
                ],
              );
          }
          return SizedBox.shrink();
        },
      ),
    );
  }
}
  1. 现在创建语言语言模型和常量
class LanguageEntity {
  final String code;
  final String value;

  const LanguageEntity({
     this.code,
     this.value,
  });
}

class Languages {
  const Languages._();

  static const languages = [
    LanguageEntity(code: 'en', value: 'English'),
    LanguageEntity(code: 'es', value: 'Spanish'),
  ];
}

现在创建应用程序本地化委托。
import 'dart:convert';

import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:movie_app/common/constants/languages.dart';

class AppLocalizations {
  final Locale locale;

  AppLocalizations(this.locale);

  static const LocalizationsDelegate<AppLocalizations> delegate =
      _AppLocalizationDelagate();

  static AppLocalizations of(context) =>
      Localizations.of<AppLocalizations>(context, AppLocalizations);

  Map<String, String> _localisedString;

  Future<bool> load() async {
    final jsonString = await rootBundle
        .loadString('assets/languages/${locale.languageCode}.json');
    final Map<String, dynamic> jsonMap = json.decode(jsonString);
    _localisedString =
        jsonMap.map((key, value) => MapEntry(key, value.toString()));
    return true;
  }

  String translate(String key) {
    return _localisedString[key];
  }
}

class _AppLocalizationDelagate extends LocalizationsDelegate<AppLocalizations> {
  const _AppLocalizationDelagate();

  @override
  bool isSupported(Locale locale) {
    return Languages.languages
        .map((e) => e.code)
        .toList()
        .contains(locale.languageCode);
  }

  @override
  bool shouldReload(covariant LocalizationsDelegate old) {
    return false;
  }

  @override
  Future<AppLocalizations> load(Locale locale) async {
    AppLocalizations appLocalizations = AppLocalizations(locale);
    await appLocalizations.load();
    return appLocalizations;
  }
}
  1. 现在创建区块
import 'dart:async';

import 'package:bloc/bloc.dart';
// import 'package:equatable/equatable.dart';
import 'package:flutter/material.dart';
import 'package:movie_app/common/constants/languages.dart';
import 'package:movie_app/domain/entities/language_entity.dart';

part 'language_event.dart';
part 'language_state.dart';

class LanguageBloc extends Bloc<LanguageEvent, LanguageState> {
  LanguageBloc() : super(LanguageLoaded(Locale(Languages.languages[0].code)));

  @override
  Stream<LanguageState> mapEventToState(
    LanguageEvent event,
  ) async* {
    if (event is ToggleLanguageEvent) {
      yield LanguageLoaded(Locale(event.language.code));
    }
  }
}

8.现在创建事件

part of 'language_bloc.dart';

abstract class LanguageEvent {
  const LanguageEvent();
}

class ToggleLanguageEvent extends LanguageEvent {
  final LanguageEntity language;

  ToggleLanguageEvent(this.language);
}
  1. 现在创建状态
part of 'language_bloc.dart';

abstract class LanguageState {
  const LanguageState();

}

class LanguageLoaded extends LanguageState {
  final Locale locale;

  LanguageLoaded(this.locale);
}

10.现在创建按钮以更改语言。

RaisedButton(child: ,RaisedButton(child: Text('Switch', 
     onPressed: (int index) {
        BlocProvider.of<LanguageBloc>(context).add(
          ToggleLanguageEvent(
            Languages.languages[index], // index value can be 0 or 1 in our case
          ),                            // 0 - en, 1 - es
        );
        Navigator.of(context).pop();
      },
);

另外,请参考此链接以获得清晰的实现方式: https://www.youtube.com/watch?v=W-2p3zB1z8k


8

由于被接受的答案存在一些缺点,我将提出新的方案:

更改应用程序的区域设置可以通过多种方式进行,这里我将展示两种方式:

方法1(使用 rxdartSharedPreferences 插件):

为了更好地理解,我创建了一个项目来解释如何操作,您可以在https://github.com/AnasSafi/flutter_localization_example上找到它。

该项目的输出:

enter image description here


方法2:

注意1:我使用了SharedPreferences插件来保存客户端选择的语言环境。

注意2:请用您的默认区域设置替换arps...

main.dart文件的全部代码:

import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter/services.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:flutter_easyloading/flutter_easyloading.dart';
import 'package:shared_preferences/shared_preferences.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized(); //To solve problem (ServicesBinding.defaultBinaryMessenger was accessed before the binding was initialized)
  runApp(MainApp());
}

class MainApp extends StatefulWidget {
  const MainApp({Key? key}) : super(key: key);
  
  /*
  To Change Locale of App
   */
  static void setLocale(BuildContext context, Locale newLocale) async {
    _MainAppState? state = context.findAncestorStateOfType<_MainAppState>();

    var prefs = await SharedPreferences.getInstance();
    prefs.setString('languageCode', newLocale.languageCode);
    prefs.setString('countryCode', "");

    state?.setState(() {
      state._locale = newLocale;
    });
    
  }

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

class _MainAppState extends State<MainApp> {
  Locale _locale = Locale('ar', 'ps');

  @override
  void initState() {
    super.initState();
    this._fetchLocale().then((locale) {
      setState(() {
        this._locale = locale;
      });
    });
  }


  /*
  To get local from SharedPreferences if exists
   */
  Future<Locale> _fetchLocale() async {
    var prefs = await SharedPreferences.getInstance();

    String languageCode = prefs.getString('languageCode') ?? 'ar';
    String countryCode = prefs.getString('countryCode') ?? 'ps';

    return Locale(languageCode, countryCode);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: _locale,
      localizationsDelegates: [
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
        const FallbackCupertinoLocalisationsDelegate(),
      ],
      supportedLocales: [
        const Locale('en', ''), // English, no country code
        const Locale('ar', ''), // Arabic, no country code
      ],
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
      ),
      home: InitializeApp(), // here use your own home name...
    );
  }
}


/*
  To solve problem of hold press on inputs
 */
class FallbackCupertinoLocalisationsDelegate extends LocalizationsDelegate<CupertinoLocalizations> {
  const FallbackCupertinoLocalisationsDelegate();

  @override
  bool isSupported(Locale locale) => true;

  @override
  Future<CupertinoLocalizations> load(Locale locale) =>
      DefaultCupertinoLocalizations.load(locale);

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

现在,如果你需要从任何其他类更改本地化选项,可以使用我的方法:

导入 main.dart 文件,然后使用我的简单 DropdownButton:

注意:将此行value: AppLocalizations.of(context)!.locale.toString(),替换为获取应用当前本地化选项的方式...

import 'package:yout_project_name/main.dart';

DropdownButton(
  onChanged: (v) => setState(() {
    MainApp.setLocale(context, Locale(v.toString(), ""));
  }),
  value: AppLocalizations.of(context)!.locale.toString(), // change this line with your way to get current locale to select it as default in dropdown
  items: [
    DropdownMenuItem(
      child: Text( 'English'), value: 'en'
    ),
    DropdownMenuItem(
      child: Text( 'العربية'), value: 'ar'
    ),
  ],
)

5

2
请看一下 pub.dev 上的 language_builder 软件包。它非常易于使用。通过将根小部件包装在 LanguageBuilder 中,您可以配置应用程序的语言。告诉您的应用程序使用手机的语言或在应用程序中手动更改语言。

https://pub.dev/packages/language_builder


1
询问资源的问题是不符合规范的。这个答案只提供了一个新资源的链接,没有显示如何实现该资源以解决 OP 中的问题。虽然这个链接可能会回答这个问题,但最好在此处包含答案的主要部分,并提供参考链接。 如果链接的页面发生变化,仅包含链接的答案可能会变得无效。- 从评论中审查 - Trenton McKinney

1
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 'package:flutter_gen/gen_l10n/app_localizations.dart';

void main() {
  WidgetsFlutterBinding.ensureInitialized();
  runApp(const MainApp());
}

class MainApp extends StatefulWidget {
  const MainApp({Key? key}) : super(key: key);

  /*
  To Change Locale of App
   */
  static void setLocale(BuildContext context, Locale newLocale) async {
    _MainAppState? state = context.findAncestorStateOfType<_MainAppState>();

    var prefs = await SharedPreferences.getInstance();
    prefs.setString('languageCode', newLocale.languageCode);
    prefs.setString('countryCode', "");

    state?.setState(() {
      state._locale = newLocale;
    });
  }

  @override
  // ignore: library_private_types_in_public_api
  _MainAppState createState() => _MainAppState();
}

class _MainAppState extends State<MainApp> {
  Locale _locale = const Locale('ar', 'tn');

  @override
  void initState() {
    super.initState();
    _fetchLocale().then((locale) {
      setState(() {
        _locale = locale;
      });
    });
  }

  /*
  To get local from SharedPreferences if exists
   */
  Future<Locale> _fetchLocale() async {
    var prefs = await SharedPreferences.getInstance();

    String languageCode = prefs.getString('languageCode') ?? 'ar';
    String countryCode = prefs.getString('countryCode') ?? 'tn';

    return Locale(languageCode, countryCode);
  }

  @override
  Widget build(BuildContext context) {
    return MaterialApp(
      locale: _locale,
      // ignore: prefer_const_literals_to_create_immutables
      localizationsDelegates: [
        AppLocalizations.delegate,
        GlobalMaterialLocalizations.delegate,
        GlobalWidgetsLocalizations.delegate,
        GlobalCupertinoLocalizations.delegate,
        const FallbackCupertinoLocalisationsDelegate(),
      ],
      // ignore: prefer_const_literals_to_create_immutables
      supportedLocales: [
        const Locale('en', ''), // English, no country code
        const Locale('fr', ''),
        const Locale('ar', ''),
      ],
      theme: ThemeData(
        primarySwatch: Colors.deepPurple,
      ),
      home: const AppContent(), // here use your own home name...
    );
  }
}

/*
  To solve problem of hold press on inputs
 */
class FallbackCupertinoLocalisationsDelegate
    extends LocalizationsDelegate<CupertinoLocalizations> {
  const FallbackCupertinoLocalisationsDelegate();

  @override
  bool isSupported(Locale locale) => true;

  @override
  Future<CupertinoLocalizations> load(Locale locale) =>
      DefaultCupertinoLocalizations.load(locale);

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

//___________________________________
class AppContent extends StatefulWidget {
  const AppContent({super.key});

  @override
  State<AppContent> createState() => _AppContentState();
}

class _AppContentState extends State<AppContent> {
  @override
  Widget build(BuildContext context) {
    return Scaffold(
      body: Center(
        child: Column(
          children: [
            Text(AppLocalizations.of(context)!.hello),
            const SizedBox(height: 10),
            TextButton(
              onPressed: () {
                setState(() {
                  MainApp.setLocale(context, const Locale("fr", ""));
                });
              },
              child: const Text("Fr"),
            ),
            const SizedBox(height: 10),
            TextButton(
              onPressed: () {
                setState(() {
                  MainApp.setLocale(context, const Locale("en", ""));
                });
              },
              child: const Text("En"),
            ),
            const SizedBox(height: 10),
            TextButton(
              onPressed: () {
                setState(() {
                  MainApp.setLocale(context, const Locale("ar", ""));
                });
              },
              child: const Text("Ar"),
            ),
            const SizedBox(height: 10)
          ],
        ),
      ),
    );
  }
}


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接