diff --git a/packages/smooth_app/lib/generic_lib/widgets/language_selector.dart b/packages/smooth_app/lib/generic_lib/widgets/language_selector.dart new file mode 100644 index 000000000000..9b55c844f9be --- /dev/null +++ b/packages/smooth_app/lib/generic_lib/widgets/language_selector.dart @@ -0,0 +1,113 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_gen/gen_l10n/app_localizations.dart' + show AppLocalizations; +import 'package:openfoodfacts/utils/LanguageHelper.dart'; +import 'package:smooth_app/data_models/user_preferences.dart'; +import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; +import 'package:smooth_app/pages/preferences/user_preferences_languages_list.dart'; + +class LanguageSelectorSettings extends StatelessWidget { + const LanguageSelectorSettings({ + required this.userPreferences, + required this.appLocalizations, + }); + + final UserPreferences userPreferences; + final AppLocalizations appLocalizations; + + static final Languages _languages = Languages(); + + @override + Widget build(BuildContext context) { + // The languages that are supported by flutter widget + final String currentLanguageCode = userPreferences.appLanguageCode ?? + Localizations.localeOf(context).toString(); + final String nameInLanguage = + _languages.getLanguageNameInEnglishFromOpenFoodFactsLanguage( + LanguageHelper.fromJson(currentLanguageCode)); + return ListTile( + leading: const Icon(Icons.language), + title: Text( + appLocalizations.choose_app_language, + ), + subtitle: Text( + '$nameInLanguage ($currentLanguageCode)', + softWrap: false, + overflow: TextOverflow.fade, + ), + onTap: () async { + final List leftovers = + _languages.getSupportedLanguagesNameInEnglish(); + leftovers.sort((OpenFoodFactsLanguage a, OpenFoodFactsLanguage b) => + _languages + .getLanguageNameInEnglishFromOpenFoodFactsLanguage(a) + .compareTo(_languages + .getLanguageNameInEnglishFromOpenFoodFactsLanguage(b))); + List filteredList = leftovers; + await showDialog( + context: context, + builder: (BuildContext context) { + return StatefulBuilder( + builder: (BuildContext context, + void Function(VoidCallback fn) setState) { + return SmoothAlertDialog( + body: Column( + children: [ + TextField( + decoration: InputDecoration( + prefixIcon: const Icon(Icons.search), + enabledBorder: const UnderlineInputBorder(), + labelText: appLocalizations.search, + ), + onChanged: (String query) { + setState( + () { + filteredList = leftovers + .where((OpenFoodFactsLanguage item) => + _languages + .getLanguageNameInEnglishFromOpenFoodFactsLanguage( + item) + .toLowerCase() + .contains(query.toLowerCase()) || + item.code.contains(query)) + .toList(); + }, + ); + }, + ), + ...List.generate( + filteredList.length, + (int index) { + final OpenFoodFactsLanguage openFoodFactsLanguage = + filteredList[index]; + final String nameInLanguage = _languages + .getLanguageNameInLanguageFromOpenFoodFactsLanguage( + openFoodFactsLanguage); + return ListTile( + title: Text( + '$nameInLanguage (${_languages.getLanguageNameInEnglishFromOpenFoodFactsLanguage(openFoodFactsLanguage)})', + softWrap: false, + overflow: TextOverflow.fade, + ), + onTap: () { + userPreferences.setAppLanguageCode( + openFoodFactsLanguage.code); + Navigator.of(context).pop(); + }, + ); + }, + ), + ], + ), + positiveAction: SmoothActionButton( + onPressed: () => Navigator.pop(context), + text: appLocalizations.cancel, + ), + ); + }, + ); + }); + }, + ); + } +} diff --git a/packages/smooth_app/lib/l10n/app_en.arb b/packages/smooth_app/lib/l10n/app_en.arb index cf0f3b394385..c1db530e84a1 100644 --- a/packages/smooth_app/lib/l10n/app_en.arb +++ b/packages/smooth_app/lib/l10n/app_en.arb @@ -1543,7 +1543,7 @@ "description": "Button label for taking a photo" }, "choose_from_gallery": "Choose from gallery", - "@choose from gallery": { + "@choose_from_gallery": { "description": "Button label for choosing a photo from gallery" }, "expand_nutrition_facts": "Expand nutrition facts table", @@ -1559,5 +1559,9 @@ "no_internet_connection": "No internet connection", "@no_internet_connection": { "description": "Message when there is no internet connection" + }, + "choose_app_language": "Choose App Language", + "@choose_app_language": { + "description": "Choose Application Language" } } \ No newline at end of file diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart index 30c05ae6105e..c1ae1950dcb1 100644 --- a/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_dev_mode.dart @@ -9,6 +9,7 @@ import 'package:smooth_app/data_models/user_preferences.dart'; import 'package:smooth_app/database/dao_product_list.dart'; import 'package:smooth_app/database/local_database.dart'; import 'package:smooth_app/generic_lib/dialogs/smooth_alert_dialog.dart'; +import 'package:smooth_app/generic_lib/widgets/language_selector.dart'; import 'package:smooth_app/helpers/data_importer/product_list_import_export.dart'; import 'package:smooth_app/helpers/data_importer/smooth_app_data_importer.dart'; import 'package:smooth_app/pages/onboarding/onboarding_flow_navigator.dart'; @@ -60,9 +61,6 @@ class UserPreferencesDevMode extends AbstractUserPreferences { static const LocalizationsDelegate delegate = GlobalMaterialLocalizations.delegate; - final List _supportedLanguageCodes = AppLocalizations.supportedLocales - .where((Locale locale) => delegate.isSupported(locale)) - .toList(); @override PreferencePageType? getPreferencePageType() => PreferencePageType.DEV_MODE; @@ -335,23 +333,9 @@ class UserPreferencesDevMode extends AbstractUserPreferences { setState(() {}); }, ), - ListTile( - leading: const Icon(Icons.language), - title: DropdownButton( - value: userPreferences.appLanguageCode ?? - Localizations.localeOf(context).toString(), - elevation: 16, - isExpanded: true, - onChanged: (String? languageCode) async => - userPreferences.setAppLanguageCode(languageCode), - items: _supportedLanguageCodes.map((Locale locale) { - final String localeString = locale.toString(); - return DropdownMenuItem( - value: localeString, - child: Text(localeString), - ); - }).toList(), - ), + LanguageSelectorSettings( + userPreferences: userPreferences, + appLocalizations: appLocalizations, ), ListTile( // Do not translate diff --git a/packages/smooth_app/lib/pages/preferences/user_preferences_languages_list.dart b/packages/smooth_app/lib/pages/preferences/user_preferences_languages_list.dart new file mode 100644 index 000000000000..0a616a14fecf --- /dev/null +++ b/packages/smooth_app/lib/pages/preferences/user_preferences_languages_list.dart @@ -0,0 +1,226 @@ +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_native_splash/cli_commands.dart'; +import 'package:openfoodfacts/utils/LanguageHelper.dart'; + +class Languages { + static const LocalizationsDelegate _delegate = + GlobalMaterialLocalizations.delegate; + + static const Map _openFoodFactsLanguagesList = + { + OpenFoodFactsLanguage.AFAR: 'Afar', + OpenFoodFactsLanguage.AFRIKAANS: 'Afrikaans', + OpenFoodFactsLanguage.AKAN: 'Akan', + OpenFoodFactsLanguage.AMHARIC: 'አማርኛ', + OpenFoodFactsLanguage.ARABIC: 'عربى', + OpenFoodFactsLanguage.ARAGONESE: 'Aragonés', + OpenFoodFactsLanguage.ASSAMESE: 'অসমীয়া', + OpenFoodFactsLanguage.AVAR: 'Авар', + OpenFoodFactsLanguage.AVESTAN: 'Avesta', + OpenFoodFactsLanguage.AYMARA: 'Aymar aru', + OpenFoodFactsLanguage.AZERBAIJANI: 'Azərbaycan', + OpenFoodFactsLanguage.BELARUSIAN: 'беларускi', + OpenFoodFactsLanguage.BULGARIAN: 'български', + OpenFoodFactsLanguage.BAMBARA: 'Bamanankan', + OpenFoodFactsLanguage.BASHKIR: 'башҡорт тілі', + OpenFoodFactsLanguage.BENGALI: 'বাংলা', + OpenFoodFactsLanguage.BIHARI_LANGUAGES: 'Bihari Languages', + OpenFoodFactsLanguage.BISLAMA: 'Bislama', + OpenFoodFactsLanguage.TIBETAN_LANGUAGE: 'Tibetan', + OpenFoodFactsLanguage.BRETON: 'Breton', + OpenFoodFactsLanguage.BOSNIAN: 'bosanski', + OpenFoodFactsLanguage.CATALAN: 'català', + OpenFoodFactsLanguage.CHECHEN: 'Chechen', + OpenFoodFactsLanguage.CHEWA: 'Chewa', + OpenFoodFactsLanguage.CHAMORRO: 'Chamoru', + OpenFoodFactsLanguage.CHURCH_SLAVONIC: 'Church Slavonic', + OpenFoodFactsLanguage.CORSICAN: 'Corsu', + OpenFoodFactsLanguage.CREE: 'ᐃᓄᒃᑎᑐᑦ', + OpenFoodFactsLanguage.CZECH: 'čeština', + OpenFoodFactsLanguage.CHUVASH: 'Chuvash', + OpenFoodFactsLanguage.WELSH: 'Cymraeg', + OpenFoodFactsLanguage.DANISH: 'dansk', + OpenFoodFactsLanguage.DZONGKHA_LANGUAGE: 'Dzongkha', + OpenFoodFactsLanguage.GERMAN: 'Deutsch', + OpenFoodFactsLanguage.MODERN_GREEK: 'Ελληνικά', + OpenFoodFactsLanguage.ENGLISH: 'English', + OpenFoodFactsLanguage.ESPERANTO: 'Esperanto', + OpenFoodFactsLanguage.SPANISH: 'Español', + OpenFoodFactsLanguage.ESTONIAN: 'eestikeel', + OpenFoodFactsLanguage.EWE: 'Eʋegbe', + OpenFoodFactsLanguage.BASQUE: 'euskara', + OpenFoodFactsLanguage.PERSIAN: 'فارسی', + OpenFoodFactsLanguage.FINNISH: 'Suomalainen', + OpenFoodFactsLanguage.FAROESE: 'Faroese', + OpenFoodFactsLanguage.FRENCH: 'Français', + OpenFoodFactsLanguage.FIJIAN_LANGUAGE: 'Fijian', + OpenFoodFactsLanguage.FULA_LANGUAGE: 'Fula', + OpenFoodFactsLanguage.IRISH: 'Gaeilge', + OpenFoodFactsLanguage.SCOTTISH_GAELIC: 'ScottishGaelic', + OpenFoodFactsLanguage.GALICIAN: 'galego', + OpenFoodFactsLanguage.GREENLANDIC: 'Greenlandic', + OpenFoodFactsLanguage.GIKUYU: 'Gikuyu', + OpenFoodFactsLanguage.GUARANI: 'Guaraní', + OpenFoodFactsLanguage.GUJARATI: 'ગુજરાતી', + OpenFoodFactsLanguage.HAUSA: 'હૌસા', + OpenFoodFactsLanguage.HEBREW: 'עִברִית', + OpenFoodFactsLanguage.HERERO: 'Herero', + OpenFoodFactsLanguage.HINDI: 'हिन्दी', + OpenFoodFactsLanguage.HIRI_MOTU: 'HiriMotu', + OpenFoodFactsLanguage.CROATIAN: 'Hrvatski', + OpenFoodFactsLanguage.HAITIAN_CREOLE: 'ayisyen', + OpenFoodFactsLanguage.HUNGARIAN: 'Magyar', + OpenFoodFactsLanguage.ARMENIAN: 'հայերեն', + OpenFoodFactsLanguage.INDONESIAN: 'bahasaIndonesia', + OpenFoodFactsLanguage.NUOSU_LANGUAGE: 'SichuanYi', + OpenFoodFactsLanguage.ICELANDIC: 'íslenskur', + OpenFoodFactsLanguage.IDO: 'Ido', + OpenFoodFactsLanguage.ITALIAN: 'Italiano', + OpenFoodFactsLanguage.INUKTITUT: 'Inuktitut', + OpenFoodFactsLanguage.INTERLINGUA: 'Interlingua', + OpenFoodFactsLanguage.INUPIAT_LANGUAGE: 'Inupiaq', + OpenFoodFactsLanguage.INTERLINGUE: 'Interlingue', + OpenFoodFactsLanguage.IGBO_LANGUAGE: 'Igbo', + OpenFoodFactsLanguage.JAPANESE: '日本語', + OpenFoodFactsLanguage.JAVANESE: 'basajawa', + OpenFoodFactsLanguage.GEORGIAN: 'ქართული', + OpenFoodFactsLanguage.KANURI: 'Kanuri', + OpenFoodFactsLanguage.KASHMIRI: 'कश्मीरी', + OpenFoodFactsLanguage.KAZAKH: 'қазақ', + OpenFoodFactsLanguage.KANNADA: 'ಕನ್ನಡ', + OpenFoodFactsLanguage.KINYARWANDA: 'Kinyarwanda', + OpenFoodFactsLanguage.KOREAN: '한국인', + OpenFoodFactsLanguage.KOMI: 'коми кыв', + OpenFoodFactsLanguage.KONGO_LANGUAGE: 'Kongo', + OpenFoodFactsLanguage.KURDISH: 'Kurdî', + OpenFoodFactsLanguage.KWANYAMA: 'Kwanyama', + OpenFoodFactsLanguage.CORNISH: 'Cornish', + OpenFoodFactsLanguage.KIRUNDI: 'Kirundi', + OpenFoodFactsLanguage.KYRGYZ: 'Кыргызча', + OpenFoodFactsLanguage.LATIN: 'latīnum', + OpenFoodFactsLanguage.LUXEMBOURGISH: 'lëtzebuergesch', + OpenFoodFactsLanguage.LAO: 'ພາສາລາວ', + OpenFoodFactsLanguage.LATVIAN: 'latviski', + OpenFoodFactsLanguage.LITHUANIAN: 'lietuvių', + OpenFoodFactsLanguage.LINGALA_LANGUAGE: 'Lingala', + OpenFoodFactsLanguage.LIMBURGISH_LANGUAGE: 'Limburgish', + OpenFoodFactsLanguage.LUBA_KATANGA_LANGUAGE: 'Luba Katanga', + OpenFoodFactsLanguage.LUGANDA: 'Luganda', + OpenFoodFactsLanguage.MALAGASY: 'Malagasy', + OpenFoodFactsLanguage.MACEDONIAN: 'македонски', + OpenFoodFactsLanguage.MAORI: 'മലയാളം', + OpenFoodFactsLanguage.MARSHALLESE: 'Ebon', + OpenFoodFactsLanguage.MONGOLIAN: 'Монгол', + OpenFoodFactsLanguage.MANX: 'Gaelg', + OpenFoodFactsLanguage.MARATHI: 'मराठी', + OpenFoodFactsLanguage.MALAY: 'Melayu', + OpenFoodFactsLanguage.MALAYALAM: 'മലയാളം', + OpenFoodFactsLanguage.MALDIVIAN_LANGUAGE: 'Maldivian', + OpenFoodFactsLanguage.MALTESE: 'Malti', + OpenFoodFactsLanguage.MOLDOVAN: 'Moldovenească', + OpenFoodFactsLanguage.BURMESE: 'မြန်မာဘာသာ', + OpenFoodFactsLanguage.BOKMAL: 'Norskbokmål', + OpenFoodFactsLanguage.NAVAJO: 'Diné bizaad', + OpenFoodFactsLanguage.NEPALI: 'नेपाली', + OpenFoodFactsLanguage.NAURUAN: 'Nauru', + OpenFoodFactsLanguage.NDONGA_DIALECT: 'Ndonga', + OpenFoodFactsLanguage.DUTCH: 'Nederlands', + OpenFoodFactsLanguage.NYNORSK: 'Norsknynorsk', + OpenFoodFactsLanguage.NORWEGIAN: 'norsk', + OpenFoodFactsLanguage.NORTHERN_NDEBELE_LANGUAGE: 'Northern Ndebele', + OpenFoodFactsLanguage.NORTHERN_SAMI: 'Sámegiella', + OpenFoodFactsLanguage.SAMOAN: 'Gagana Sāmoa', + OpenFoodFactsLanguage.SOUTHERN_NDEBELE: 'SouthNdebele', + OpenFoodFactsLanguage.OCCITAN: 'Occitan', + OpenFoodFactsLanguage.OLD_CHURCH_SLAVONIC: 'Old Church Slavonic', + OpenFoodFactsLanguage.OSSETIAN: 'Ossetian', + OpenFoodFactsLanguage.OROMO: 'Oromoo', + OpenFoodFactsLanguage.ODIA: 'ଓଡ଼ିଆ', + OpenFoodFactsLanguage.OJIBWE: 'ᐊᓂᔑᓈᐯᒧᐎᓐ', + OpenFoodFactsLanguage.PALI: 'Pali', + OpenFoodFactsLanguage.PASHTO: 'پښتو', + OpenFoodFactsLanguage.PUNJABI: 'Panjabi', + OpenFoodFactsLanguage.POLISH: 'Polski', + OpenFoodFactsLanguage.PORTUGUESE: 'Português', + OpenFoodFactsLanguage.QUECHUA_LANGUAGES: 'Runasimi', + OpenFoodFactsLanguage.ROMANSH: 'Romansh', + OpenFoodFactsLanguage.ROMANIAN: 'Română', + OpenFoodFactsLanguage.RUSSIAN: 'Русский', + OpenFoodFactsLanguage.SANSKRIT: 'संस्कृत', + OpenFoodFactsLanguage.SARDINIAN_LANGUAGE: 'Sardinian', + OpenFoodFactsLanguage.SINDHI: 'سنڌي', + OpenFoodFactsLanguage.SANGO: 'Sango', + OpenFoodFactsLanguage.SINHALA: 'සිංහල', + OpenFoodFactsLanguage.SLOVAK: 'Slovenčina', + OpenFoodFactsLanguage.SLOVENE: 'Slovenščina', + OpenFoodFactsLanguage.SHONA: 'Shona', + OpenFoodFactsLanguage.SOMALI: 'Soomaali', + OpenFoodFactsLanguage.ALBANIAN: 'shqiptare', + OpenFoodFactsLanguage.SERBIAN: 'Српски', + OpenFoodFactsLanguage.SWAZI: 'Swati', + OpenFoodFactsLanguage.SOTHO: 'SouthernSotho', + OpenFoodFactsLanguage.SUNDANESE_LANGUAGE: 'Basa Sunda', + OpenFoodFactsLanguage.SWEDISH: 'svenska', + OpenFoodFactsLanguage.SWAHILI: 'kiswahili', + OpenFoodFactsLanguage.TAMIL: 'தமிழ்', + OpenFoodFactsLanguage.TELUGU: 'తెలుగు', + OpenFoodFactsLanguage.TAJIK: 'тоҷикӣ', + OpenFoodFactsLanguage.THAI: 'ไทย', + OpenFoodFactsLanguage.TIGRINYA: 'ትግሪኛ', + OpenFoodFactsLanguage.TAGALOG: 'Tagalog', + OpenFoodFactsLanguage.TSWANA: 'Setswana', + OpenFoodFactsLanguage.TURKISH: 'Türk', + OpenFoodFactsLanguage.TURKMEN: 'Türkmen', + OpenFoodFactsLanguage.TSONGA: 'Tsonga', + OpenFoodFactsLanguage.TATAR: 'Татар', + OpenFoodFactsLanguage.TONGAN_LANGUAGE: 'Tongan', + OpenFoodFactsLanguage.TWI: 'Twi', + OpenFoodFactsLanguage.TAHITIAN: 'Tahitian', + OpenFoodFactsLanguage.UYGHUR: 'ئۇيغۇر', + OpenFoodFactsLanguage.UKRAINIAN: 'Українська', + OpenFoodFactsLanguage.URDU: 'اردو', + OpenFoodFactsLanguage.UZBEK: '"ozbek"', + OpenFoodFactsLanguage.VENDA: 'Venda', + OpenFoodFactsLanguage.VIETNAMESE: 'TiếngViệt', + OpenFoodFactsLanguage.VOLAPUK: 'Volapuk', + OpenFoodFactsLanguage.WEST_FRISIAN: 'West Frisian', + OpenFoodFactsLanguage.WOLOF: 'Wolof', + OpenFoodFactsLanguage.XHOSA: 'isiXhosa', + OpenFoodFactsLanguage.YIDDISH: 'יידיש', + OpenFoodFactsLanguage.YORUBA: 'Yoruba', + OpenFoodFactsLanguage.CHINESE: '中文', + OpenFoodFactsLanguage.ZHUANG_LANGUAGES: 'Zhuang', + OpenFoodFactsLanguage.ZULU: 'ខ្មែរ', + }; + + List getSupportedLanguagesNameInEnglish() { + final List languages = []; + + _openFoodFactsLanguagesList + .forEach((OpenFoodFactsLanguage lc, String _) => { + if (_delegate.isSupported(Locale(lc.code))) + {languages.add(lc)} + }); + + return languages; + } + + String getLanguageNameInEnglishFromOpenFoodFactsLanguage( + OpenFoodFactsLanguage openFoodFactsLanguage) { + return openFoodFactsLanguage + .toString() + .split('.') + .last + .split('_') + .join(' ') + .toLowerCase() + .capitalize(); + } + + String getLanguageNameInLanguageFromOpenFoodFactsLanguage( + OpenFoodFactsLanguage lc) { + return _openFoodFactsLanguagesList[lc] ?? + _openFoodFactsLanguagesList[OpenFoodFactsLanguage.ENGLISH]!; + } +}