【问题标题】:Button to change the language Flutter更改语言的按钮 Flutter
【发布时间】:2021-03-26 05:03:29
【问题描述】:

我正在 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;}

【问题讨论】:

标签: android flutter dart mobile internationalization


【解决方案1】:

看看 pub.dev 中的 language_builder 包 这是非常容易使用。通过使用 LanguageBuilder 包装根小部件,您可以配置应用程序的语言。 告诉您的应用使用手机的语言或从应用手动更改。

https://pub.dev/packages/language_builder

【讨论】:

  • 寻求资源的问题是题外话。该答案仅提供指向新资源的链接,并未显示如何实现资源以解决 OP 中的问题。虽然此链接可能会回答问题,但最好在此处包含答案的基本部分并提供链接以供参考。如果链接页面发生更改,仅链接答案可能会失效。 - From Review
【解决方案2】:

因为接受的答案有一些缺点我会设置一个新的:

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

注意 2:将 arps 替换为您的默认语言环境...

ma​​in.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;
}

现在你需要从任何其他类更改语言环境,你可以使用我的方式:

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

注意:替换值: 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'
    ),
  ],
)

【讨论】:

    【解决方案3】:

    您必须使用 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. 现在创建 Languages 语言模型和常量
    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'),
      ];
    }
    
    
    1. 现在创建应用本地化委托
    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

    【讨论】:

      【解决方案4】:

      如果您使用提供者作为状态管理,那么您可以关注这篇文章。

      https://medium.com/flutter-community/flutter-internationalization-the-easy-way-using-provider-and-json-c47caa4212b2

      【讨论】:

        【解决方案5】:

        您可以使用您喜欢的状态管理方法设置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')),
                ),
              ],
            );
          }
        }
        

        【讨论】:

        • 这对我有用!稍作修改:当我的 FlutterApp 有一个 StatfulWidget(例如 MyApp)作为主要“家”时,我无法让它工作。所以我将有状态的 MyApp 小部件包装成一个无状态的小部件(例如 MyAppWrapper)。 MyAppWrapper 唯一能做的就是它有一个构建方法返回 MyApp()。
        • 但这不会在应用重启后继续存在,对吗?
        • @Marcus 它确实无法在应用重新启动后存活下来。您必须保留数据。
        猜你喜欢
        • 1970-01-01
        • 2023-03-22
        • 2022-01-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-07-04
        • 2013-02-10
        相关资源
        最近更新 更多