From 9471fe40fe615a19f095939981b75ebb55ef658e Mon Sep 17 00:00:00 2001
From: LittleSheep <littlesheep.code@hotmail.com>
Date: Tue, 28 Jan 2025 23:09:07 +0800
Subject: [PATCH] :sparkles: In-app language switcher

---
 assets/translations/en-US.json |   3 +
 assets/translations/zh-CN.json |   3 +
 lib/screens/settings.dart      | 107 ++++++++++++++++++++++++---------
 3 files changed, 83 insertions(+), 30 deletions(-)

diff --git a/assets/translations/en-US.json b/assets/translations/en-US.json
index 73610c6..27b3e2d 100644
--- a/assets/translations/en-US.json
+++ b/assets/translations/en-US.json
@@ -199,6 +199,9 @@
     "other": "{} comments"
   },
   "settingsAppearance": "Appearance",
+  "settingsDisplayLanguage": "Display Language",
+  "settingsDisplayLanguageDescription": "Set the application language.",
+  "settingsDisplayLanguageSystem": "Follow System",
   "settingsAppBarTransparent": "Transparent App Bar",
   "settingsAppBarTransparentDescription": "Enable transparent effect for the app bar.",
   "settingsDrawerPreferCollapse": "Prefer Drawer Collapse",
diff --git a/assets/translations/zh-CN.json b/assets/translations/zh-CN.json
index 346fb9e..14ba966 100644
--- a/assets/translations/zh-CN.json
+++ b/assets/translations/zh-CN.json
@@ -196,6 +196,9 @@
     "other": "{} 条评论"
   },
   "settingsAppearance": "外观",
+  "settingsDisplayLanguage": "显示语言",
+  "settingsDisplayLanguageDescription": "设置应用程序使用的语言",
+  "settingsDisplayLanguageSystem": "跟随系统",
   "settingsBackgroundImage": "背景图片",
   "settingsBackgroundImageDescription": "设置应用全局生效的的背景图片。",
   "settingsBackgroundImageClear": "清除现存背景图",
diff --git a/lib/screens/settings.dart b/lib/screens/settings.dart
index 360c789..33a8ea0 100644
--- a/lib/screens/settings.dart
+++ b/lib/screens/settings.dart
@@ -82,6 +82,48 @@ class _SettingsScreenState extends State<SettingsScreen> {
               crossAxisAlignment: CrossAxisAlignment.start,
               children: [
                 Text('settingsAppearance').bold().fontSize(17).tr().padding(horizontal: 20, bottom: 4),
+                ListTile(
+                  title: Text('settingsDisplayLanguage').tr(),
+                  subtitle: Text('settingsDisplayLanguageDescription').tr(),
+                  contentPadding: const EdgeInsets.only(left: 24, right: 17),
+                  leading: const Icon(Symbols.translate),
+                  trailing: DropdownButtonHideUnderline(
+                    child: DropdownButton2<Locale?>(
+                      isExpanded: true,
+                      items: [
+                        ...EasyLocalization.of(context)!.supportedLocales.mapIndexed((idx, ele) {
+                          return DropdownMenuItem<Locale?>(
+                            value: ele,
+                            child: Text('${ele.languageCode}-${ele.countryCode}').fontSize(14),
+                          );
+                        }),
+                        DropdownMenuItem<Locale?>(
+                          value: null,
+                          child: Text('settingsDisplayLanguageSystem').tr().fontSize(14),
+                        ),
+                      ],
+                      value: EasyLocalization.of(context)!.currentLocale,
+                      onChanged: (Locale? value) {
+                        if (value != null) {
+                          EasyLocalization.of(context)!.setLocale(value);
+                        } else {
+                          EasyLocalization.of(context)!.resetLocale();
+                        }
+                      },
+                      buttonStyleData: const ButtonStyleData(
+                        padding: EdgeInsets.symmetric(
+                          horizontal: 16,
+                          vertical: 5,
+                        ),
+                        height: 40,
+                        width: 160,
+                      ),
+                      menuItemStyleData: const MenuItemStyleData(
+                        height: 40,
+                      ),
+                    ),
+                  ),
+                ),
                 if (!kIsWeb)
                   ListTile(
                     title: Text('settingsBackgroundImage').tr(),
@@ -147,30 +189,31 @@ class _SettingsScreenState extends State<SettingsScreen> {
                     Color pickerColor = Color(_prefs.getInt(kAppColorSchemeStoreKey) ?? Colors.indigo.value);
                     final color = await showDialog<Color?>(
                       context: context,
-                      builder: (context) => AlertDialog(
-                        content: SingleChildScrollView(
-                          child: ColorPicker(
-                            pickerColor: pickerColor,
-                            onColorChanged: (color) => pickerColor = color,
-                            enableAlpha: false,
-                            hexInputBar: true,
+                      builder: (context) =>
+                          AlertDialog(
+                            content: SingleChildScrollView(
+                              child: ColorPicker(
+                                pickerColor: pickerColor,
+                                onColorChanged: (color) => pickerColor = color,
+                                enableAlpha: false,
+                                hexInputBar: true,
+                              ),
+                            ),
+                            actions: <Widget>[
+                              TextButton(
+                                child: const Text('dialogDismiss').tr(),
+                                onPressed: () {
+                                  Navigator.of(context).pop();
+                                },
+                              ),
+                              TextButton(
+                                child: const Text('dialogConfirm').tr(),
+                                onPressed: () {
+                                  Navigator.of(context).pop(pickerColor);
+                                },
+                              ),
+                            ],
                           ),
-                        ),
-                        actions: <Widget>[
-                          TextButton(
-                            child: const Text('dialogDismiss').tr(),
-                            onPressed: () {
-                              Navigator.of(context).pop();
-                            },
-                          ),
-                          TextButton(
-                            child: const Text('dialogConfirm').tr(),
-                            onPressed: () {
-                              Navigator.of(context).pop(pickerColor);
-                            },
-                          ),
-                        ],
-                      ),
                     );
 
                     if (color == null || !context.mounted) return;
@@ -206,11 +249,13 @@ class _SettingsScreenState extends State<SettingsScreen> {
                       value: _prefs.getInt(kAppColorSchemeStoreKey) == null
                           ? 1
                           : kColorSchemes.values
-                              .toList()
-                              .indexWhere((ele) => ele.value == _prefs.getInt(kAppColorSchemeStoreKey)),
+                          .toList()
+                          .indexWhere((ele) => ele.value == _prefs.getInt(kAppColorSchemeStoreKey)),
                       onChanged: (int? value) {
                         if (value != null && value != -1) {
-                          _prefs.setInt(kAppColorSchemeStoreKey, kColorSchemes.values.elementAt(value).value);
+                          _prefs.setInt(kAppColorSchemeStoreKey, kColorSchemes.values
+                              .elementAt(value)
+                              .value);
                           final th = context.read<ThemeProvider>();
                           th.reloadTheme(seedColorOverride: kColorSchemes.values.elementAt(value));
                           setState(() {});
@@ -342,7 +387,8 @@ class _SettingsScreenState extends State<SettingsScreen> {
                           ('Custom', _serverUrlController.text),
                       ]
                           .map(
-                            (item) => DropdownMenuItem<String>(
+                            (item) =>
+                            DropdownMenuItem<String>(
                               value: item.$2,
                               child: Column(
                                 mainAxisSize: MainAxisSize.max,
@@ -354,7 +400,7 @@ class _SettingsScreenState extends State<SettingsScreen> {
                                 ],
                               ),
                             ),
-                          )
+                      )
                           .toList(),
                       value: _serverUrlController.text,
                       onChanged: (String? value) {
@@ -409,11 +455,12 @@ class _SettingsScreenState extends State<SettingsScreen> {
                       isExpanded: true,
                       items: kImageQualityLevel.entries
                           .map(
-                            (item) => DropdownMenuItem<FilterQuality>(
+                            (item) =>
+                            DropdownMenuItem<FilterQuality>(
                               value: item.value,
                               child: Text(item.key).tr().fontSize(14),
                             ),
-                          )
+                      )
                           .toList(),
                       onChanged: (FilterQuality? value) {
                         if (value == null) return;