mirror of
				https://github.com/inventree/inventree-app.git
				synced 2025-10-29 20:40:35 +00:00 
			
		
		
		
	Locale switch (#200)
* Add function to set app locale * Setting for selecting app language - Adds requirement for "flutter_localized_locales" - Change main app to stateless * Reload entire app tree when language is changed * Update release notes * linting
This commit is contained in:
		| @@ -473,6 +473,16 @@ | ||||
|   "labelTemplate": "Label Template", | ||||
|   "@labelTemplate": {}, | ||||
|  | ||||
|   "language": "Language", | ||||
|   "@language": {}, | ||||
|  | ||||
|   "languageDefault": "Default system language", | ||||
|   "@languageDefault": {}, | ||||
|  | ||||
|   "languageSelect": "Select Language", | ||||
|   "@languageSelect": {}, | ||||
|  | ||||
|  | ||||
|   "lastStocktake": "Last Stocktake", | ||||
|   "@lastStocktake": {}, | ||||
|  | ||||
|   | ||||
| @@ -4,12 +4,14 @@ import "package:flutter_localizations/flutter_localizations.dart"; | ||||
| import "package:flutter_gen/gen_l10n/app_localizations.dart"; | ||||
|  | ||||
| import "package:flutter/material.dart"; | ||||
| import "package:flutter_localized_locales/flutter_localized_locales.dart"; | ||||
| import "package:one_context/one_context.dart"; | ||||
| import "package:package_info_plus/package_info_plus.dart"; | ||||
| import "package:sentry_flutter/sentry_flutter.dart"; | ||||
|  | ||||
| import "package:inventree/inventree/sentry.dart"; | ||||
| import "package:inventree/dsn.dart"; | ||||
| import "package:inventree/preferences.dart"; | ||||
| import "package:inventree/widget/home.dart"; | ||||
|  | ||||
| // Supported translations are automatically updated | ||||
| @@ -60,9 +62,43 @@ Future<void> main() async { | ||||
|  | ||||
| } | ||||
|  | ||||
| class InvenTreeApp extends StatelessWidget { | ||||
| class InvenTreeApp extends StatefulWidget { | ||||
|   // This widget is the root of your application. | ||||
|  | ||||
|   @override | ||||
|   InvenTreeAppState createState() => InvenTreeAppState(); | ||||
|  | ||||
|   static InvenTreeAppState? of(BuildContext context) => context.findAncestorStateOfType<InvenTreeAppState>(); | ||||
|  | ||||
| } | ||||
|  | ||||
|  | ||||
| class InvenTreeAppState extends State<StatefulWidget> { | ||||
|  | ||||
|   // Custom _locale (default = null; use system default) | ||||
|   Locale? _locale; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
|  | ||||
|     // Load selected locale | ||||
|     loadDefaultLocale(); | ||||
|   } | ||||
|  | ||||
|   // Load the default app locale | ||||
|   Future<void> loadDefaultLocale() async { | ||||
|     Locale? locale = await InvenTreeSettingsManager().getSelectedLocale(); | ||||
|     setLocale(locale); | ||||
|   } | ||||
|  | ||||
|   // Update the app locale | ||||
|   void setLocale(Locale? locale) { | ||||
|     setState(() { | ||||
|       _locale = locale; | ||||
|     }); | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|  | ||||
| @@ -78,11 +114,13 @@ class InvenTreeApp extends StatelessWidget { | ||||
|       home: InvenTreeHomePage(), | ||||
|       localizationsDelegates: [ | ||||
|         I18N.delegate, | ||||
|         LocaleNamesLocalizationsDelegate(), | ||||
|         GlobalMaterialLocalizations.delegate, | ||||
|         GlobalCupertinoLocalizations.delegate, | ||||
|         GlobalWidgetsLocalizations.delegate, | ||||
|       ], | ||||
|       supportedLocales: supported_locales, | ||||
|       locale: _locale, | ||||
|     ); | ||||
|   } | ||||
| } | ||||
| @@ -1,5 +1,7 @@ | ||||
| import "dart:async"; | ||||
| import "dart:ui"; | ||||
|  | ||||
| import "package:inventree/l10n/supported_locales.dart"; | ||||
| import "package:path_provider/path_provider.dart"; | ||||
| import "package:sembast/sembast.dart"; | ||||
| import "package:sembast/sembast_io.dart"; | ||||
| @@ -83,6 +85,26 @@ class InvenTreeSettingsManager { | ||||
|  | ||||
|   Future<Database> get _db async => InvenTreePreferencesDB.instance.database; | ||||
|  | ||||
|   Future<Locale?> getSelectedLocale() async { | ||||
|     final String locale_name = await getValue("customLocale", "") as String; | ||||
|  | ||||
|     if (locale_name.isEmpty) { | ||||
|       return null; | ||||
|     } | ||||
|  | ||||
|     for (var locale in supported_locales) { | ||||
|       if (locale.toString() == locale_name) { | ||||
|         return locale; | ||||
|       } | ||||
|     } | ||||
|  | ||||
|     // No matching locale found | ||||
|     return null; | ||||
|   } | ||||
|  | ||||
|   Future<void> setSelectedLocale(Locale? locale) async { | ||||
|     await setValue("customLocale", locale?.toString() ?? ""); | ||||
|   } | ||||
|  | ||||
|   Future<void> removeValue(String key) async { | ||||
|     await store.record(key).delete(await _db); | ||||
|   | ||||
| @@ -1,8 +1,12 @@ | ||||
|  | ||||
| import "package:flutter/material.dart"; | ||||
| import "package:font_awesome_flutter/font_awesome_flutter.dart"; | ||||
| import "package:flutter_localized_locales/flutter_localized_locales.dart"; | ||||
|  | ||||
| import "package:inventree/api_form.dart"; | ||||
| import "package:inventree/l10.dart"; | ||||
| import "package:inventree/l10n/supported_locales.dart"; | ||||
| import "package:inventree/main.dart"; | ||||
| import "package:inventree/preferences.dart"; | ||||
|  | ||||
|  | ||||
| @@ -27,6 +31,8 @@ class _InvenTreeAppSettingsState extends State<InvenTreeAppSettingsWidget> { | ||||
|   bool reportErrors = true; | ||||
|   bool strictHttps = false; | ||||
|  | ||||
|   Locale? locale; | ||||
|  | ||||
|   @override | ||||
|   void initState() { | ||||
|     super.initState(); | ||||
| @@ -46,14 +52,78 @@ class _InvenTreeAppSettingsState extends State<InvenTreeAppSettingsWidget> { | ||||
|     reportErrors = await InvenTreeSettingsManager().getValue(INV_REPORT_ERRORS, true) as bool; | ||||
|     strictHttps = await InvenTreeSettingsManager().getValue(INV_STRICT_HTTPS, false) as bool; | ||||
|  | ||||
|     locale = await InvenTreeSettingsManager().getSelectedLocale(); | ||||
|  | ||||
|     if (mounted) { | ||||
|       setState(() {}); | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   Future<void> _selectLocale(BuildContext context) async { | ||||
|  | ||||
|     List<Map<String, dynamic>> options = [ | ||||
|       { | ||||
|         "display_name": L10().languageDefault, | ||||
|         "value": null, | ||||
|       } | ||||
|     ]; | ||||
|  | ||||
|     // Construct a list of available locales | ||||
|     for (var locale in supported_locales) { | ||||
|       options.add({ | ||||
|         "display_name": LocaleNames.of(context)!.nameOf(locale.toString()), | ||||
|         "value": locale.toString() | ||||
|       }); | ||||
|     } | ||||
|  | ||||
|     Map<String, dynamic> fields = { | ||||
|       "locale": { | ||||
|         "label": L10().language, | ||||
|         "type": "choice", | ||||
|         "choices": options, | ||||
|         "value": locale?.toString(), | ||||
|       } | ||||
|     }; | ||||
|  | ||||
|     launchApiForm( | ||||
|       context, | ||||
|       L10().languageSelect, | ||||
|       "", | ||||
|       fields, | ||||
|       icon: FontAwesomeIcons.checkCircle, | ||||
|       onSuccess: (Map<String, dynamic> data) async { | ||||
|  | ||||
|         String locale_name = (data["locale"] ?? "") as String; | ||||
|         Locale? selected_locale; | ||||
|  | ||||
|         for (var locale in supported_locales) { | ||||
|           if (locale.toString() == locale_name) { | ||||
|             selected_locale = locale; | ||||
|           } | ||||
|         } | ||||
|  | ||||
|         await InvenTreeSettingsManager().setSelectedLocale(selected_locale); | ||||
|  | ||||
|         setState(() { | ||||
|           locale = selected_locale; | ||||
|         }); | ||||
|  | ||||
|         // Refresh the entire app locale | ||||
|         InvenTreeApp.of(context)?.setLocale(locale); | ||||
|       } | ||||
|     ); | ||||
|  | ||||
|   } | ||||
|  | ||||
|   @override | ||||
|   Widget build(BuildContext context) { | ||||
|  | ||||
|     String languageName = L10().languageDefault; | ||||
|  | ||||
|     if (locale != null) { | ||||
|       languageName = LocaleNames.of(context)!.nameOf(locale.toString()) ?? L10().languageDefault; | ||||
|     } | ||||
|  | ||||
|     return Scaffold( | ||||
|       key: _settingsKey, | ||||
|       appBar: AppBar( | ||||
| @@ -142,6 +212,14 @@ class _InvenTreeAppSettingsState extends State<InvenTreeAppSettingsWidget> { | ||||
|                 }, | ||||
|               ), | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: Text(L10().language), | ||||
|               subtitle: Text(languageName), | ||||
|               leading: FaIcon(FontAwesomeIcons.language), | ||||
|               onTap: () async { | ||||
|                 _selectLocale(context); | ||||
|               }, | ||||
|             ), | ||||
|             ListTile( | ||||
|               title: Text(L10().errorReportUpload), | ||||
|               subtitle: Text(L10().errorReportUploadDetails), | ||||
|   | ||||
		Reference in New Issue
	
	Block a user