Я создаю приложение во 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;}
Я бы порекомендовал заглянуть в пакет Get: pub.dev/packages/get . Его поддержка интернационализации довольно надежна и проста в использовании, вы можете прочитать больше здесь: pub.dev/packages/get#internationalization. Вы можете использовать метод, описанный в разделе «изменить языковой стандарт», как только ваша кнопка будет нажата, чтобы изменить переводы.
Я бы предложил это: medium.com/@puneetsethi25/…
Вы можете установить свойство locale
для MaterialApp
с помощью вашего любимого метода управления состоянием. Например:
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 Маркус действительно не выдерживает перезапуска приложения. Вам придется сохранять данные.
Любой пример, который сохраняет изменения
@LearnFlutter, вы можете использовать пакет Shared Preferences и сохранять данные там при изменении выбора. Затем, чтобы прочитать данные, вы можете сделать это: @override void didChangeDependencies() { SharedPreferencesHelper.getLanguageCode().then((languageCode) { setState(() { _locale = Locale(languageCode); }); }); super.didChangeDependencies(); }
Вы должны использовать локализации, предоставленные Flutter. Вы должны использовать настраиваемый делегат и файлы JSON для поддерживаемых языков.
Я реализовал с помощью bloc
Шаги, чтобы следовать,
assets/languages/
в корневой папкеJSON
для поддерживаемых языков.
Например: en.json, es.jsonmain.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();
},
),
);
}
}
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;
}
}
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);
}
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://thewikihow.com/video_W-2p3zB1z8k
Если вы используете провайдер в качестве управления состоянием, вы можете следовать этой статье.
Поскольку принятый ответ имеет некоторые недостатки, я установлю новый:
Изменить локаль приложения можно несколькими способами, здесь я покажу два способа:
Способ 1 (используя плагины rxdart
и SharedPreferences
):
Для большего понимания я был создан проект, чтобы объяснить, как это сделать, вы можете найти его в https://github.com/AnasSafi/flutter_localization_example
Выход проекта:
Способ 2:
Примечание 1: я использовал плагин SharedPreferences для сохранения локали, которая выбор клиента.
Примечание 2. Замените ar и ps на локаль по умолчанию...
Полный код файла 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:
Примечание: заменить значение: 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'
),
],
)
Взгляните на пакет language_builder в pub.dev. Он очень прост в использовании. Обернув корневой виджет с помощью LanguageBuilder, вы можете настроить язык своего приложения. Скажите своему приложению использовать язык телефона или изменить его вручную из приложения.
https://pub.dev/packages/language_builder
Вопросы с просьбой о ресурсах не по теме. Этот ответ предлагает только ссылку на новый ресурс и не показывает, как реализовать ресурс для решения вопроса в ОП. Хотя эта ссылка может ответить на вопрос, лучше включить сюда основные части ответа и предоставить ссылку для справки. Ответы, содержащие только ссылки, могут стать недействительными, если связанная страница изменится. - Из отзыва
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)
],
),
),
);
}
}
это может быть полезно для вас,developerlibs.com/2019/03/…