mirror of
https://github.com/immich-app/immich.git
synced 2024-12-29 15:11:58 +00:00
feat(mobile): in app language selector (#8574)
* feat(mobile): select locale in the mobile app * add additional locale * use the same locale variable across the app * using different data structure * drop down with button * update pull locales * open app ios * remove dependency * format fix
This commit is contained in:
parent
335c03d0b8
commit
82aeb3292a
7 changed files with 584 additions and 489 deletions
172
localizely.yml
172
localizely.yml
|
@ -1,78 +1,94 @@
|
||||||
config_version: 1.0
|
config_version: 1.0
|
||||||
project_id: ead34689-ec52-41d9-b675-09bc85a6cbd7
|
project_id: ead34689-ec52-41d9-b675-09bc85a6cbd7
|
||||||
file_type: json
|
file_type: json
|
||||||
branch: main
|
branch: main
|
||||||
upload:
|
upload:
|
||||||
files:
|
files:
|
||||||
- file: mobile/assets/i18n/en-US.json
|
- file: mobile/assets/i18n/en-US.json
|
||||||
locale_code: en-US
|
locale_code: en-US
|
||||||
download:
|
download:
|
||||||
params:
|
params:
|
||||||
export_empty_as: main
|
export_empty_as: main
|
||||||
files:
|
files:
|
||||||
- file: mobile/assets/i18n/en-US.json
|
- file: mobile/assets/i18n/en-US.json
|
||||||
locale_code: en-US
|
locale_code: en-US
|
||||||
- file: mobile/assets/i18n/de-DE.json
|
- file: mobile/assets/i18n/de-DE.json
|
||||||
locale_code: de-DE
|
locale_code: de-DE
|
||||||
- file: mobile/assets/i18n/da-DK.json
|
- file: mobile/assets/i18n/da-DK.json
|
||||||
locale_code: da-DK
|
locale_code: da-DK
|
||||||
- file: mobile/assets/i18n/it-IT.json
|
- file: mobile/assets/i18n/it-IT.json
|
||||||
locale_code: it-IT
|
locale_code: it-IT
|
||||||
- file: mobile/assets/i18n/es-ES.json
|
- file: mobile/assets/i18n/es-ES.json
|
||||||
locale_code: es-ES
|
locale_code: es-ES
|
||||||
- file: mobile/assets/i18n/vi-VN.json
|
- file: mobile/assets/i18n/vi-VN.json
|
||||||
locale_code: vi-VN
|
locale_code: vi-VN
|
||||||
- file: mobile/assets/i18n/fr-FR.json
|
- file: mobile/assets/i18n/fr-FR.json
|
||||||
locale_code: fr-FR
|
locale_code: fr-FR
|
||||||
- file: mobile/assets/i18n/ja-JP.json
|
- file: mobile/assets/i18n/ja-JP.json
|
||||||
locale_code: ja-JP
|
locale_code: ja-JP
|
||||||
- file: mobile/assets/i18n/pl-PL.json
|
- file: mobile/assets/i18n/pl-PL.json
|
||||||
locale_code: pl-PL
|
locale_code: pl-PL
|
||||||
- file: mobile/assets/i18n/fi-FI.json
|
- file: mobile/assets/i18n/fi-FI.json
|
||||||
locale_code: fi-FI
|
locale_code: fi-FI
|
||||||
- file: mobile/assets/i18n/pt-PT.json
|
- file: mobile/assets/i18n/pt-PT.json
|
||||||
locale_code: pt-PT
|
locale_code: pt-PT
|
||||||
- file: mobile/assets/i18n/pt-BR.json
|
- file: mobile/assets/i18n/pt-BR.json
|
||||||
locale_code: pt-BR
|
locale_code: pt-BR
|
||||||
- file: mobile/assets/i18n/cs-CZ.json
|
- file: mobile/assets/i18n/cs-CZ.json
|
||||||
locale_code: cs-CZ
|
locale_code: cs-CZ
|
||||||
- file: mobile/assets/i18n/uk-UA.json
|
- file: mobile/assets/i18n/uk-UA.json
|
||||||
locale_code: uk-UA
|
locale_code: uk-UA
|
||||||
- file: mobile/assets/i18n/ru-RU.json
|
- file: mobile/assets/i18n/ru-RU.json
|
||||||
locale_code: ru-RU
|
locale_code: ru-RU
|
||||||
- file: mobile/assets/i18n/zh-CN.json
|
- file: mobile/assets/i18n/zh-CN.json
|
||||||
locale_code: zh-CN
|
locale_code: zh-CN
|
||||||
- file: mobile/assets/i18n/sk-SK.json
|
- file: mobile/assets/i18n/sk-SK.json
|
||||||
locale_code: sk-SK
|
locale_code: sk-SK
|
||||||
- file: mobile/assets/i18n/nl-NL.json
|
- file: mobile/assets/i18n/nl-NL.json
|
||||||
locale_code: nl-NL
|
locale_code: nl-NL
|
||||||
- file: mobile/assets/i18n/nb-NO.json
|
- file: mobile/assets/i18n/nb-NO.json
|
||||||
locale_code: nb-NO
|
locale_code: nb-NO
|
||||||
- file: mobile/assets/i18n/sv-SE.json
|
- file: mobile/assets/i18n/sv-SE.json
|
||||||
locale_code: sv-SE
|
locale_code: sv-SE
|
||||||
- file: mobile/assets/i18n/mn.json
|
- file: mobile/assets/i18n/mn.json
|
||||||
locale_code: mn
|
locale_code: mn
|
||||||
- file: mobile/assets/i18n/ko-KR.json
|
- file: mobile/assets/i18n/ko-KR.json
|
||||||
locale_code: ko-KR
|
locale_code: ko-KR
|
||||||
- file: mobile/assets/i18n/sr-Latn.json
|
- file: mobile/assets/i18n/sr-Latn.json
|
||||||
locale_code: sr-Latn
|
locale_code: sr-Latn
|
||||||
- file: mobile/assets/i18n/sr-Cyrl.json
|
- file: mobile/assets/i18n/sr-Cyrl.json
|
||||||
locale_code: sr-Cyrl
|
locale_code: sr-Cyrl
|
||||||
- file: mobile/assets/i18n/hi-IN.json
|
- file: mobile/assets/i18n/hi-IN.json
|
||||||
locale_code: hi-IN
|
locale_code: hi-IN
|
||||||
- file: mobile/assets/i18n/es-PE.json
|
- file: mobile/assets/i18n/es-PE.json
|
||||||
locale_code: es-PE
|
locale_code: es-PE
|
||||||
- file: mobile/assets/i18n/es-MX.json
|
- file: mobile/assets/i18n/es-MX.json
|
||||||
locale_code: es-MX
|
locale_code: es-MX
|
||||||
- file: mobile/assets/i18n/sv-FI.json
|
- file: mobile/assets/i18n/sv-FI.json
|
||||||
locale_code: sv-FI
|
locale_code: sv-FI
|
||||||
- file: mobile/assets/i18n/ca.json
|
- file: mobile/assets/i18n/ca-CA.json
|
||||||
locale_code: ca
|
locale_code: ca-CA
|
||||||
- file: mobile/assets/i18n/hu-HU.json
|
- file: mobile/assets/i18n/hu-HU.json
|
||||||
locale_code: hu-HU
|
locale_code: hu-HU
|
||||||
- file: mobile/assets/i18n/lv-LV.json
|
- file: mobile/assets/i18n/lv-LV.json
|
||||||
locale_code: lv-LV
|
locale_code: lv-LV
|
||||||
- file: mobile/assets/i18n/zh-Hans.json
|
- file: mobile/assets/i18n/zh-Hans.json
|
||||||
locale_code: zh-Hans
|
locale_code: zh-Hans
|
||||||
- file: mobile/assets/i18n/th-TH.json
|
- file: mobile/assets/i18n/th-TH.json
|
||||||
locale_code: th-TH
|
locale_code: th-TH
|
||||||
|
- file: mobile/assets/i18n/lt-LT.json
|
||||||
|
locale_code: lt-LT
|
||||||
|
- file: mobile/assets/i18n/el-GR.json
|
||||||
|
locale_code: el-GR
|
||||||
|
- file: mobile/assets/i18n/fr-CA.json
|
||||||
|
locale_code: fr-CA
|
||||||
|
- file: mobile/assets/i18n/es-US.json
|
||||||
|
locale_code: es-US
|
||||||
|
- file: mobile/assets/i18n/sl-SI.json
|
||||||
|
locale_code: sl-SI
|
||||||
|
- file: mobile/assets/i18n/ar-JO.json
|
||||||
|
locale_code: ar-JO
|
||||||
|
- file: mobile/assets/i18n/he-IL.json
|
||||||
|
locale_code: he-IL
|
||||||
|
- file: mobile/assets/i18n/ro-RO.json
|
||||||
|
locale_code: ro-RO
|
||||||
|
|
|
@ -400,7 +400,9 @@
|
||||||
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
"setting_notifications_total_progress_subtitle": "Overall upload progress (done/total assets)",
|
||||||
"setting_notifications_total_progress_title": "Show background backup total progress",
|
"setting_notifications_total_progress_title": "Show background backup total progress",
|
||||||
"setting_pages_app_bar_settings": "Settings",
|
"setting_pages_app_bar_settings": "Settings",
|
||||||
|
"setting_languages_title": "Languages",
|
||||||
"settings_require_restart": "Please restart Immich to apply this setting",
|
"settings_require_restart": "Please restart Immich to apply this setting",
|
||||||
|
"setting_languages_apply": "Apply",
|
||||||
"share_add": "Add",
|
"share_add": "Add",
|
||||||
"share_add_photos": "Add photos",
|
"share_add_photos": "Add photos",
|
||||||
"share_add_title": "Add a title",
|
"share_add_title": "Add a title",
|
||||||
|
|
|
@ -1,121 +1,124 @@
|
||||||
<?xml version="1.0" encoding="UTF-8"?>
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||||
<plist version="1.0">
|
<plist version="1.0">
|
||||||
<dict>
|
<dict>
|
||||||
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
<key>BGTaskSchedulerPermittedIdentifiers</key>
|
||||||
<array>
|
<array>
|
||||||
<string>app.alextran.immich.backgroundFetch</string>
|
<string>app.alextran.immich.backgroundFetch</string>
|
||||||
<string>app.alextran.immich.backgroundProcessing</string>
|
<string>app.alextran.immich.backgroundProcessing</string>
|
||||||
</array>
|
</array>
|
||||||
<key>CADisableMinimumFrameDurationOnPhone</key>
|
<key>CADisableMinimumFrameDurationOnPhone</key>
|
||||||
<true/>
|
<true />
|
||||||
<key>CFBundleDevelopmentRegion</key>
|
<key>CFBundleDevelopmentRegion</key>
|
||||||
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
<string>$(DEVELOPMENT_LANGUAGE)</string>
|
||||||
<key>CFBundleDisplayName</key>
|
<key>CFBundleDisplayName</key>
|
||||||
<string>Immich</string>
|
<string>Immich</string>
|
||||||
<key>CFBundleExecutable</key>
|
<key>CFBundleExecutable</key>
|
||||||
<string>$(EXECUTABLE_NAME)</string>
|
<string>$(EXECUTABLE_NAME)</string>
|
||||||
<key>CFBundleIdentifier</key>
|
<key>CFBundleIdentifier</key>
|
||||||
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
<string>$(PRODUCT_BUNDLE_IDENTIFIER)</string>
|
||||||
<key>CFBundleInfoDictionaryVersion</key>
|
<key>CFBundleInfoDictionaryVersion</key>
|
||||||
<string>6.0</string>
|
<string>6.0</string>
|
||||||
<key>CFBundleLocalizations</key>
|
<key>CFBundleLocalizations</key>
|
||||||
<array>
|
<array>
|
||||||
<string>en</string>
|
<string>en</string>
|
||||||
<string>de</string>
|
<string>ar</string>
|
||||||
<string>da</string>
|
<string>ca</string>
|
||||||
<string>it</string>
|
<string>cs</string>
|
||||||
<string>es</string>
|
<string>da</string>
|
||||||
<string>vi</string>
|
<string>de</string>
|
||||||
<string>fr</string>
|
<string>es</string>
|
||||||
<string>ja</string>
|
<string>fi</string>
|
||||||
<string>pl</string>
|
<string>fr</string>
|
||||||
<string>fi</string>
|
<string>he</string>
|
||||||
<string>pt</string>
|
<string>hi</string>
|
||||||
<string>cs</string>
|
<string>hu</string>
|
||||||
<string>uk</string>
|
<string>it</string>
|
||||||
<string>ru</string>
|
<string>ja</string>
|
||||||
<string>zh</string>
|
<string>ko</string>
|
||||||
<string>sk</string>
|
<string>lv</string>
|
||||||
<string>nl</string>
|
<string>mn</string>
|
||||||
<string>nb</string>
|
<string>nb</string>
|
||||||
<string>sv</string>
|
<string>nl</string>
|
||||||
<string>mn</string>
|
<string>pl</string>
|
||||||
<string>ko</string>
|
<string>pt</string>
|
||||||
<string>sr</string>
|
<string>ro</string>
|
||||||
<string>hi</string>
|
<string>ru</string>
|
||||||
<string>ca</string>
|
<string>sk</string>
|
||||||
<string>hu</string>
|
<string>sl</string>
|
||||||
<string>lv</string>
|
<string>sr</string>
|
||||||
<string>th</string>
|
<string>sv</string>
|
||||||
<string>sl</string>
|
<string>th</string>
|
||||||
</array>
|
<string>uk</string>
|
||||||
<key>CFBundleName</key>
|
<string>vi</string>
|
||||||
<string>immich_mobile</string>
|
<string>zh</string>
|
||||||
<key>CFBundlePackageType</key>
|
</array>
|
||||||
<string>APPL</string>
|
<key>CFBundleName</key>
|
||||||
<key>CFBundleShortVersionString</key>
|
<string>immich_mobile</string>
|
||||||
<string>1.101.0</string>
|
<key>CFBundlePackageType</key>
|
||||||
<key>CFBundleSignature</key>
|
<string>APPL</string>
|
||||||
<string>????</string>
|
<key>CFBundleShortVersionString</key>
|
||||||
<key>CFBundleVersion</key>
|
<string>1.101.0</string>
|
||||||
<string>147</string>
|
<key>CFBundleSignature</key>
|
||||||
<key>FLTEnableImpeller</key>
|
<string>????</string>
|
||||||
<true/>
|
<key>CFBundleVersion</key>
|
||||||
<key>ITSAppUsesNonExemptEncryption</key>
|
<string>147</string>
|
||||||
<false/>
|
<key>FLTEnableImpeller</key>
|
||||||
<key>LSApplicationQueriesSchemes</key>
|
<true />
|
||||||
<array>
|
<key>ITSAppUsesNonExemptEncryption</key>
|
||||||
<string>https</string>
|
<false />
|
||||||
</array>
|
<key>LSApplicationQueriesSchemes</key>
|
||||||
<key>LSRequiresIPhoneOS</key>
|
<array>
|
||||||
<true/>
|
<string>https</string>
|
||||||
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
|
</array>
|
||||||
<true/>
|
<key>LSRequiresIPhoneOS</key>
|
||||||
<key>NSAppTransportSecurity</key>
|
<true />
|
||||||
<dict>
|
<key>MGLMapboxMetricsEnabledSettingShownInApp</key>
|
||||||
<key>NSAllowsArbitraryLoads</key>
|
<true />
|
||||||
<true/>
|
<key>NSAppTransportSecurity</key>
|
||||||
</dict>
|
<dict>
|
||||||
<key>NSCameraUsageDescription</key>
|
<key>NSAllowsArbitraryLoads</key>
|
||||||
<string>We need to access the camera to let you take beautiful video using this app</string>
|
<true />
|
||||||
<key>NSLocationWhenInUseUsageDescription</key>
|
</dict>
|
||||||
<string>Enable location setting to show position of assets on map</string>
|
<key>NSCameraUsageDescription</key>
|
||||||
<key>NSMicrophoneUsageDescription</key>
|
<string>We need to access the camera to let you take beautiful video using this app</string>
|
||||||
<string>We need to access the microphone to let you take beautiful video using this app</string>
|
<key>NSLocationWhenInUseUsageDescription</key>
|
||||||
<key>NSPhotoLibraryAddUsageDescription</key>
|
<string>Enable location setting to show position of assets on map</string>
|
||||||
<string>We need to manage backup your photos album</string>
|
<key>NSMicrophoneUsageDescription</key>
|
||||||
<key>NSPhotoLibraryUsageDescription</key>
|
<string>We need to access the microphone to let you take beautiful video using this app</string>
|
||||||
<string>We need to manage backup your photos album</string>
|
<key>NSPhotoLibraryAddUsageDescription</key>
|
||||||
<key>UIApplicationSupportsIndirectInputEvents</key>
|
<string>We need to manage backup your photos album</string>
|
||||||
<true/>
|
<key>NSPhotoLibraryUsageDescription</key>
|
||||||
<key>UIBackgroundModes</key>
|
<string>We need to manage backup your photos album</string>
|
||||||
<array>
|
<key>UIApplicationSupportsIndirectInputEvents</key>
|
||||||
<string>fetch</string>
|
<true />
|
||||||
<string>processing</string>
|
<key>UIBackgroundModes</key>
|
||||||
</array>
|
<array>
|
||||||
<key>UILaunchStoryboardName</key>
|
<string>fetch</string>
|
||||||
<string>LaunchScreen</string>
|
<string>processing</string>
|
||||||
<key>UIMainStoryboardFile</key>
|
</array>
|
||||||
<string>Main</string>
|
<key>UILaunchStoryboardName</key>
|
||||||
<key>UIStatusBarHidden</key>
|
<string>LaunchScreen</string>
|
||||||
<false/>
|
<key>UIMainStoryboardFile</key>
|
||||||
<key>UISupportedInterfaceOrientations</key>
|
<string>Main</string>
|
||||||
<array>
|
<key>UIStatusBarHidden</key>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
<false />
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<key>UISupportedInterfaceOrientations</key>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<array>
|
||||||
</array>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
<key>UISupportedInterfaceOrientations~ipad</key>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<array>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
<string>UIInterfaceOrientationPortrait</string>
|
</array>
|
||||||
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
<key>UISupportedInterfaceOrientations~ipad</key>
|
||||||
<string>UIInterfaceOrientationLandscapeLeft</string>
|
<array>
|
||||||
<string>UIInterfaceOrientationLandscapeRight</string>
|
<string>UIInterfaceOrientationPortrait</string>
|
||||||
</array>
|
<string>UIInterfaceOrientationPortraitUpsideDown</string>
|
||||||
<key>UIViewControllerBasedStatusBarAppearance</key>
|
<string>UIInterfaceOrientationLandscapeLeft</string>
|
||||||
<true/>
|
<string>UIInterfaceOrientationLandscapeRight</string>
|
||||||
<key>io.flutter.embedded_views_preview</key>
|
</array>
|
||||||
<true/>
|
<key>UIViewControllerBasedStatusBarAppearance</key>
|
||||||
</dict>
|
<true />
|
||||||
</plist>
|
<key>io.flutter.embedded_views_preview</key>
|
||||||
|
<true />
|
||||||
|
</dict>
|
||||||
|
</plist>
|
|
@ -1,43 +1,48 @@
|
||||||
import 'dart:ui';
|
import 'dart:ui';
|
||||||
|
|
||||||
const List<Locale> locales = [
|
const Map<String, Locale> locales = {
|
||||||
// Default locale
|
// Default locale
|
||||||
Locale('en', 'US'),
|
'English (en_US)': Locale('en', 'US'),
|
||||||
// Additional locales
|
// Additional locales
|
||||||
Locale('de', 'DE'),
|
'Arabic (ar_JO)': Locale('ar', 'JO'),
|
||||||
Locale('da', 'DK'),
|
'Catalan (ca_CA)': Locale('ca', 'CA'),
|
||||||
Locale('it', 'IT'),
|
'Chinese (zh_CN)': Locale('zh', 'CN'),
|
||||||
Locale('es', 'ES'),
|
'Chinese Simplified (zh_Hans)': Locale('zh', 'Hans'),
|
||||||
Locale('vi', 'VN'),
|
'Czech (cs_CZ)': Locale('cs', 'CZ'),
|
||||||
Locale('fr', 'CA'),
|
'Danish (da_DK)': Locale('da', 'DK'),
|
||||||
Locale('fr', 'FR'),
|
'Dutch (nl_NL)': Locale('nl', 'NL'),
|
||||||
Locale('ja', 'JP'),
|
'Finnish (fi_FI)': Locale('fi', 'FI'),
|
||||||
Locale('pl', 'PL'),
|
'French (fr_CA)': Locale('fr', 'CA'),
|
||||||
Locale('fi', 'FI'),
|
'French (fr_FR)': Locale('fr', 'FR'),
|
||||||
Locale('pt', 'PT'),
|
'German (de_DE)': Locale('de', 'DE'),
|
||||||
Locale('cs', 'CZ'),
|
'Greek (el_GR)': Locale('el', 'GR'),
|
||||||
Locale('uk', 'UA'),
|
'Hebrew (he_IL)': Locale('he', 'IL'),
|
||||||
Locale('ru', 'RU'),
|
'Hindi (hi_IN)': Locale('hi', 'IN'),
|
||||||
Locale('zh', 'CN'),
|
'Hungarian (hu_HU)': Locale('hu', 'HU'),
|
||||||
Locale('sk', 'SK'),
|
'Italian (it_IT)': Locale('it', 'IT'),
|
||||||
Locale('nl', 'NL'),
|
'Japanese (ja_JP)': Locale('ja', 'JP'),
|
||||||
Locale('nb', 'NO'),
|
'Korean (ko_KR)': Locale('ko', 'KR'),
|
||||||
Locale('sv', 'SE'),
|
'Latvian (lv_LV)': Locale('lv', 'LV'),
|
||||||
Locale('mn', 'MN'),
|
'Lithuanian (lt_LT)': Locale('lt', 'LT'),
|
||||||
Locale('ko', 'KR'),
|
'Mongolian (mn_MN)': Locale('mn', 'MN'),
|
||||||
Locale('sr', 'Latn'),
|
'Norwegian Bokmål (nb_NO)': Locale('nb', 'NO'),
|
||||||
Locale('sr', 'Cyrl'),
|
'Polish (pl_PL)': Locale('pl', 'PL'),
|
||||||
Locale('hi', 'IN'),
|
'Portuguese (pt_PT)': Locale('pt', 'PT'),
|
||||||
Locale('es', 'PE'),
|
'Romanian (ro_RO)': Locale('ro', 'RO'),
|
||||||
Locale('es', 'MX'),
|
'Russian (ru_RU)': Locale('ru', 'RU'),
|
||||||
Locale('es', 'US'),
|
'Serbian Cyrillic (sr_Cyrl)': Locale('sr', 'Cyrl'),
|
||||||
Locale('sv', 'FI'),
|
'Serbian Latin (sr_Latn)': Locale('sr', 'Latn'),
|
||||||
Locale('ca', 'CA'),
|
'Slovak (sk_SK)': Locale('sk', 'SK'),
|
||||||
Locale('hu', 'HU'),
|
'Slovenian (sl_SI)': Locale('sl', 'SI'),
|
||||||
Locale('lv', 'LV'),
|
'Spanish (es_ES)': Locale('es', 'ES'),
|
||||||
Locale('zh', 'Hans'),
|
'Spanish (es_MX)': Locale('es', 'MX'),
|
||||||
Locale('th', 'TH'),
|
'Spanish (es_PE)': Locale('es', 'PE'),
|
||||||
Locale('sl', 'SI'),
|
'Spanish (es_US)': Locale('es', 'US'),
|
||||||
];
|
'Swedish (sv_FI)': Locale('sv', 'FI'),
|
||||||
|
'Swedish (sv_SE)': Locale('sv', 'SE'),
|
||||||
|
'Thai (th_TH)': Locale('th', 'TH'),
|
||||||
|
'Ukrainian (uk_UA)': Locale('uk', 'UA'),
|
||||||
|
'Vietnamese (vi_VN)': Locale('vi', 'VN'),
|
||||||
|
};
|
||||||
|
|
||||||
const String translationsPath = 'assets/i18n';
|
const String translationsPath = 'assets/i18n';
|
||||||
|
|
|
@ -1,225 +1,225 @@
|
||||||
import 'dart:async';
|
import 'dart:async';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
import 'package:device_info_plus/device_info_plus.dart';
|
import 'package:device_info_plus/device_info_plus.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter/services.dart';
|
import 'package:flutter/services.dart';
|
||||||
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
import 'package:flutter_displaymode/flutter_displaymode.dart';
|
||||||
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
import 'package:timezone/data/latest.dart';
|
import 'package:timezone/data/latest.dart';
|
||||||
import 'package:immich_mobile/constants/locales.dart';
|
import 'package:immich_mobile/constants/locales.dart';
|
||||||
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
import 'package:immich_mobile/modules/backup/background_service/background.service.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/backup_album.model.dart';
|
import 'package:immich_mobile/modules/backup/models/backup_album.model.dart';
|
||||||
import 'package:immich_mobile/modules/backup/models/duplicated_asset.model.dart';
|
import 'package:immich_mobile/modules/backup/models/duplicated_asset.model.dart';
|
||||||
import 'package:immich_mobile/routing/router.dart';
|
import 'package:immich_mobile/routing/router.dart';
|
||||||
import 'package:immich_mobile/routing/tab_navigation_observer.dart';
|
import 'package:immich_mobile/routing/tab_navigation_observer.dart';
|
||||||
import 'package:immich_mobile/shared/cache/widgets_binding.dart';
|
import 'package:immich_mobile/shared/cache/widgets_binding.dart';
|
||||||
import 'package:immich_mobile/shared/models/album.dart';
|
import 'package:immich_mobile/shared/models/album.dart';
|
||||||
import 'package:immich_mobile/shared/models/android_device_asset.dart';
|
import 'package:immich_mobile/shared/models/android_device_asset.dart';
|
||||||
import 'package:immich_mobile/shared/models/asset.dart';
|
import 'package:immich_mobile/shared/models/asset.dart';
|
||||||
import 'package:immich_mobile/shared/models/etag.dart';
|
import 'package:immich_mobile/shared/models/etag.dart';
|
||||||
import 'package:immich_mobile/shared/models/exif_info.dart';
|
import 'package:immich_mobile/shared/models/exif_info.dart';
|
||||||
import 'package:immich_mobile/shared/models/ios_device_asset.dart';
|
import 'package:immich_mobile/shared/models/ios_device_asset.dart';
|
||||||
import 'package:immich_mobile/shared/models/logger_message.model.dart';
|
import 'package:immich_mobile/shared/models/logger_message.model.dart';
|
||||||
import 'package:immich_mobile/shared/models/store.dart';
|
import 'package:immich_mobile/shared/models/store.dart';
|
||||||
import 'package:immich_mobile/shared/models/user.dart';
|
import 'package:immich_mobile/shared/models/user.dart';
|
||||||
import 'package:immich_mobile/shared/providers/app_state.provider.dart';
|
import 'package:immich_mobile/shared/providers/app_state.provider.dart';
|
||||||
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
import 'package:immich_mobile/shared/providers/db.provider.dart';
|
||||||
import 'package:immich_mobile/shared/services/immich_logger.service.dart';
|
import 'package:immich_mobile/shared/services/immich_logger.service.dart';
|
||||||
import 'package:immich_mobile/shared/services/local_notification.service.dart';
|
import 'package:immich_mobile/shared/services/local_notification.service.dart';
|
||||||
import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
|
import 'package:immich_mobile/utils/http_ssl_cert_override.dart';
|
||||||
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
import 'package:immich_mobile/utils/immich_app_theme.dart';
|
||||||
import 'package:immich_mobile/utils/migration.dart';
|
import 'package:immich_mobile/utils/migration.dart';
|
||||||
import 'package:isar/isar.dart';
|
import 'package:isar/isar.dart';
|
||||||
import 'package:logging/logging.dart';
|
import 'package:logging/logging.dart';
|
||||||
import 'package:path_provider/path_provider.dart';
|
import 'package:path_provider/path_provider.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
ImmichWidgetsBinding();
|
ImmichWidgetsBinding();
|
||||||
|
|
||||||
final db = await loadDb();
|
final db = await loadDb();
|
||||||
await initApp();
|
await initApp();
|
||||||
await migrateDatabaseIfNeeded(db);
|
await migrateDatabaseIfNeeded(db);
|
||||||
HttpOverrides.global = HttpSSLCertOverride();
|
HttpOverrides.global = HttpSSLCertOverride();
|
||||||
|
|
||||||
runApp(
|
runApp(
|
||||||
ProviderScope(
|
ProviderScope(
|
||||||
overrides: [dbProvider.overrideWithValue(db)],
|
overrides: [dbProvider.overrideWithValue(db)],
|
||||||
child: const MainWidget(),
|
child: const MainWidget(),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initApp() async {
|
Future<void> initApp() async {
|
||||||
await EasyLocalization.ensureInitialized();
|
await EasyLocalization.ensureInitialized();
|
||||||
|
|
||||||
if (kReleaseMode && Platform.isAndroid) {
|
if (kReleaseMode && Platform.isAndroid) {
|
||||||
try {
|
try {
|
||||||
await FlutterDisplayMode.setHighRefreshRate();
|
await FlutterDisplayMode.setHighRefreshRate();
|
||||||
debugPrint("Enabled high refresh mode");
|
debugPrint("Enabled high refresh mode");
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
debugPrint("Error setting high refresh rate: $e");
|
debugPrint("Error setting high refresh rate: $e");
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Initialize Immich Logger Service
|
// Initialize Immich Logger Service
|
||||||
ImmichLogger();
|
ImmichLogger();
|
||||||
|
|
||||||
var log = Logger("ImmichErrorLogger");
|
var log = Logger("ImmichErrorLogger");
|
||||||
|
|
||||||
FlutterError.onError = (details) {
|
FlutterError.onError = (details) {
|
||||||
FlutterError.presentError(details);
|
FlutterError.presentError(details);
|
||||||
log.severe(
|
log.severe(
|
||||||
'FlutterError - Catch all',
|
'FlutterError - Catch all',
|
||||||
"${details.toString()}\nException: ${details.exception}\nLibrary: ${details.library}\nContext: ${details.context}",
|
"${details.toString()}\nException: ${details.exception}\nLibrary: ${details.library}\nContext: ${details.context}",
|
||||||
details.stack,
|
details.stack,
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
PlatformDispatcher.instance.onError = (error, stack) {
|
PlatformDispatcher.instance.onError = (error, stack) {
|
||||||
log.severe('PlatformDispatcher - Catch all', error, stack);
|
log.severe('PlatformDispatcher - Catch all', error, stack);
|
||||||
return true;
|
return true;
|
||||||
};
|
};
|
||||||
|
|
||||||
initializeTimeZones();
|
initializeTimeZones();
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<Isar> loadDb() async {
|
Future<Isar> loadDb() async {
|
||||||
final dir = await getApplicationDocumentsDirectory();
|
final dir = await getApplicationDocumentsDirectory();
|
||||||
Isar db = await Isar.open(
|
Isar db = await Isar.open(
|
||||||
[
|
[
|
||||||
StoreValueSchema,
|
StoreValueSchema,
|
||||||
ExifInfoSchema,
|
ExifInfoSchema,
|
||||||
AssetSchema,
|
AssetSchema,
|
||||||
AlbumSchema,
|
AlbumSchema,
|
||||||
UserSchema,
|
UserSchema,
|
||||||
BackupAlbumSchema,
|
BackupAlbumSchema,
|
||||||
DuplicatedAssetSchema,
|
DuplicatedAssetSchema,
|
||||||
LoggerMessageSchema,
|
LoggerMessageSchema,
|
||||||
ETagSchema,
|
ETagSchema,
|
||||||
if (Platform.isAndroid) AndroidDeviceAssetSchema,
|
if (Platform.isAndroid) AndroidDeviceAssetSchema,
|
||||||
if (Platform.isIOS) IOSDeviceAssetSchema,
|
if (Platform.isIOS) IOSDeviceAssetSchema,
|
||||||
],
|
],
|
||||||
directory: dir.path,
|
directory: dir.path,
|
||||||
maxSizeMiB: 256,
|
maxSizeMiB: 256,
|
||||||
);
|
);
|
||||||
Store.init(db);
|
Store.init(db);
|
||||||
return db;
|
return db;
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImmichApp extends ConsumerStatefulWidget {
|
class ImmichApp extends ConsumerStatefulWidget {
|
||||||
const ImmichApp({super.key});
|
const ImmichApp({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
ImmichAppState createState() => ImmichAppState();
|
ImmichAppState createState() => ImmichAppState();
|
||||||
}
|
}
|
||||||
|
|
||||||
class ImmichAppState extends ConsumerState<ImmichApp>
|
class ImmichAppState extends ConsumerState<ImmichApp>
|
||||||
with WidgetsBindingObserver {
|
with WidgetsBindingObserver {
|
||||||
@override
|
@override
|
||||||
void didChangeAppLifecycleState(AppLifecycleState state) {
|
void didChangeAppLifecycleState(AppLifecycleState state) {
|
||||||
switch (state) {
|
switch (state) {
|
||||||
case AppLifecycleState.resumed:
|
case AppLifecycleState.resumed:
|
||||||
debugPrint("[APP STATE] resumed");
|
debugPrint("[APP STATE] resumed");
|
||||||
ref.read(appStateProvider.notifier).handleAppResume();
|
ref.read(appStateProvider.notifier).handleAppResume();
|
||||||
break;
|
break;
|
||||||
case AppLifecycleState.inactive:
|
case AppLifecycleState.inactive:
|
||||||
debugPrint("[APP STATE] inactive");
|
debugPrint("[APP STATE] inactive");
|
||||||
ref.read(appStateProvider.notifier).handleAppInactivity();
|
ref.read(appStateProvider.notifier).handleAppInactivity();
|
||||||
break;
|
break;
|
||||||
case AppLifecycleState.paused:
|
case AppLifecycleState.paused:
|
||||||
debugPrint("[APP STATE] paused");
|
debugPrint("[APP STATE] paused");
|
||||||
ref.read(appStateProvider.notifier).handleAppPause();
|
ref.read(appStateProvider.notifier).handleAppPause();
|
||||||
break;
|
break;
|
||||||
case AppLifecycleState.detached:
|
case AppLifecycleState.detached:
|
||||||
debugPrint("[APP STATE] detached");
|
debugPrint("[APP STATE] detached");
|
||||||
ref.read(appStateProvider.notifier).handleAppDetached();
|
ref.read(appStateProvider.notifier).handleAppDetached();
|
||||||
break;
|
break;
|
||||||
case AppLifecycleState.hidden:
|
case AppLifecycleState.hidden:
|
||||||
debugPrint("[APP STATE] hidden");
|
debugPrint("[APP STATE] hidden");
|
||||||
ref.read(appStateProvider.notifier).handleAppHidden();
|
ref.read(appStateProvider.notifier).handleAppHidden();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> initApp() async {
|
Future<void> initApp() async {
|
||||||
WidgetsBinding.instance.addObserver(this);
|
WidgetsBinding.instance.addObserver(this);
|
||||||
|
|
||||||
// Draw the app from edge to edge
|
// Draw the app from edge to edge
|
||||||
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
SystemChrome.setEnabledSystemUIMode(SystemUiMode.edgeToEdge);
|
||||||
|
|
||||||
// Sets the navigation bar color
|
// Sets the navigation bar color
|
||||||
SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(
|
SystemUiOverlayStyle overlayStyle = const SystemUiOverlayStyle(
|
||||||
systemNavigationBarColor: Colors.transparent,
|
systemNavigationBarColor: Colors.transparent,
|
||||||
);
|
);
|
||||||
if (Platform.isAndroid) {
|
if (Platform.isAndroid) {
|
||||||
// Android 8 does not support transparent app bars
|
// Android 8 does not support transparent app bars
|
||||||
final info = await DeviceInfoPlugin().androidInfo;
|
final info = await DeviceInfoPlugin().androidInfo;
|
||||||
if (info.version.sdkInt <= 26) {
|
if (info.version.sdkInt <= 26) {
|
||||||
overlayStyle = context.isDarkTheme
|
overlayStyle = context.isDarkTheme
|
||||||
? SystemUiOverlayStyle.dark
|
? SystemUiOverlayStyle.dark
|
||||||
: SystemUiOverlayStyle.light;
|
: SystemUiOverlayStyle.light;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SystemChrome.setSystemUIOverlayStyle(overlayStyle);
|
SystemChrome.setSystemUIOverlayStyle(overlayStyle);
|
||||||
await ref.read(localNotificationService).setup();
|
await ref.read(localNotificationService).setup();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
initState() {
|
initState() {
|
||||||
super.initState();
|
super.initState();
|
||||||
initApp().then((_) => debugPrint("App Init Completed"));
|
initApp().then((_) => debugPrint("App Init Completed"));
|
||||||
WidgetsBinding.instance.addPostFrameCallback((_) {
|
WidgetsBinding.instance.addPostFrameCallback((_) {
|
||||||
// needs to be delayed so that EasyLocalization is working
|
// needs to be delayed so that EasyLocalization is working
|
||||||
ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
|
ref.read(backgroundServiceProvider).resumeServiceIfEnabled();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
void dispose() {
|
void dispose() {
|
||||||
WidgetsBinding.instance.removeObserver(this);
|
WidgetsBinding.instance.removeObserver(this);
|
||||||
super.dispose();
|
super.dispose();
|
||||||
}
|
}
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
var router = ref.watch(appRouterProvider);
|
var router = ref.watch(appRouterProvider);
|
||||||
|
|
||||||
return MaterialApp(
|
return MaterialApp(
|
||||||
localizationsDelegates: context.localizationDelegates,
|
localizationsDelegates: context.localizationDelegates,
|
||||||
supportedLocales: context.supportedLocales,
|
supportedLocales: context.supportedLocales,
|
||||||
locale: context.locale,
|
locale: context.locale,
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
home: MaterialApp.router(
|
home: MaterialApp.router(
|
||||||
title: 'Immich',
|
title: 'Immich',
|
||||||
debugShowCheckedModeBanner: false,
|
debugShowCheckedModeBanner: false,
|
||||||
themeMode: ref.watch(immichThemeProvider),
|
themeMode: ref.watch(immichThemeProvider),
|
||||||
darkTheme: immichDarkTheme,
|
darkTheme: immichDarkTheme,
|
||||||
theme: immichLightTheme,
|
theme: immichLightTheme,
|
||||||
routeInformationParser: router.defaultRouteParser(),
|
routeInformationParser: router.defaultRouteParser(),
|
||||||
routerDelegate: router.delegate(
|
routerDelegate: router.delegate(
|
||||||
navigatorObservers: () => [TabNavigationObserver(ref: ref)],
|
navigatorObservers: () => [TabNavigationObserver(ref: ref)],
|
||||||
),
|
),
|
||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// ignore: prefer-single-widget-per-file
|
// ignore: prefer-single-widget-per-file
|
||||||
class MainWidget extends StatelessWidget {
|
class MainWidget extends StatelessWidget {
|
||||||
const MainWidget({super.key});
|
const MainWidget({super.key});
|
||||||
|
|
||||||
@override
|
@override
|
||||||
Widget build(BuildContext context) {
|
Widget build(BuildContext context) {
|
||||||
return EasyLocalization(
|
return EasyLocalization(
|
||||||
supportedLocales: locales,
|
supportedLocales: locales.values.toList(),
|
||||||
path: translationsPath,
|
path: translationsPath,
|
||||||
useFallbackTranslations: true,
|
useFallbackTranslations: true,
|
||||||
fallbackLocale: locales.first,
|
fallbackLocale: locales.values.first,
|
||||||
child: const ImmichApp(),
|
child: const ImmichApp(),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,31 +1,31 @@
|
||||||
// ignore_for_file: implementation_imports
|
// ignore_for_file: implementation_imports
|
||||||
|
|
||||||
import 'package:flutter/foundation.dart';
|
import 'package:flutter/foundation.dart';
|
||||||
import 'package:easy_localization/src/asset_loader.dart';
|
import 'package:easy_localization/src/asset_loader.dart';
|
||||||
import 'package:easy_localization/src/easy_localization_controller.dart';
|
import 'package:easy_localization/src/easy_localization_controller.dart';
|
||||||
import 'package:easy_localization/src/localization.dart';
|
import 'package:easy_localization/src/localization.dart';
|
||||||
import 'package:immich_mobile/constants/locales.dart';
|
import 'package:immich_mobile/constants/locales.dart';
|
||||||
|
|
||||||
/// Workaround to manually load translations in another Isolate
|
/// Workaround to manually load translations in another Isolate
|
||||||
Future<bool> loadTranslations() async {
|
Future<bool> loadTranslations() async {
|
||||||
await EasyLocalizationController.initEasyLocation();
|
await EasyLocalizationController.initEasyLocation();
|
||||||
|
|
||||||
final controller = EasyLocalizationController(
|
final controller = EasyLocalizationController(
|
||||||
supportedLocales: locales,
|
supportedLocales: locales.values.toList(),
|
||||||
useFallbackTranslations: true,
|
useFallbackTranslations: true,
|
||||||
saveLocale: true,
|
saveLocale: true,
|
||||||
assetLoader: const RootBundleAssetLoader(),
|
assetLoader: const RootBundleAssetLoader(),
|
||||||
path: translationsPath,
|
path: translationsPath,
|
||||||
useOnlyLangCode: false,
|
useOnlyLangCode: false,
|
||||||
onLoadError: (e) => debugPrint(e.toString()),
|
onLoadError: (e) => debugPrint(e.toString()),
|
||||||
fallbackLocale: locales.first,
|
fallbackLocale: locales.values.first,
|
||||||
);
|
);
|
||||||
|
|
||||||
await controller.loadTranslations();
|
await controller.loadTranslations();
|
||||||
|
|
||||||
return Localization.load(
|
return Localization.load(
|
||||||
controller.locale,
|
controller.locale,
|
||||||
translations: controller.translations,
|
translations: controller.translations,
|
||||||
fallbackTranslations: controller.fallbackTranslations,
|
fallbackTranslations: controller.fallbackTranslations,
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -2,7 +2,10 @@ import 'package:auto_route/auto_route.dart';
|
||||||
import 'package:easy_localization/easy_localization.dart';
|
import 'package:easy_localization/easy_localization.dart';
|
||||||
import 'package:flutter/material.dart';
|
import 'package:flutter/material.dart';
|
||||||
import 'package:flutter_hooks/flutter_hooks.dart';
|
import 'package:flutter_hooks/flutter_hooks.dart';
|
||||||
|
import 'package:hooks_riverpod/hooks_riverpod.dart';
|
||||||
|
import 'package:immich_mobile/constants/locales.dart';
|
||||||
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
import 'package:immich_mobile/extensions/build_context_extensions.dart';
|
||||||
|
import 'package:immich_mobile/modules/backup/background_service/localization.dart';
|
||||||
import 'package:immich_mobile/modules/settings/ui/advanced_settings.dart';
|
import 'package:immich_mobile/modules/settings/ui/advanced_settings.dart';
|
||||||
import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_settings.dart';
|
import 'package:immich_mobile/modules/settings/ui/asset_list_settings/asset_list_settings.dart';
|
||||||
import 'package:immich_mobile/modules/settings/ui/backup_settings/backup_settings.dart';
|
import 'package:immich_mobile/modules/settings/ui/backup_settings/backup_settings.dart';
|
||||||
|
@ -16,6 +19,7 @@ enum SettingSection {
|
||||||
'setting_notifications_title',
|
'setting_notifications_title',
|
||||||
Icons.notifications_none_rounded,
|
Icons.notifications_none_rounded,
|
||||||
),
|
),
|
||||||
|
languages('setting_languages_title', Icons.language),
|
||||||
preferences('preferences_settings_title', Icons.interests_outlined),
|
preferences('preferences_settings_title', Icons.interests_outlined),
|
||||||
backup('backup_controller_page_backup', Icons.cloud_upload_outlined),
|
backup('backup_controller_page_backup', Icons.cloud_upload_outlined),
|
||||||
timeline('asset_list_settings_title', Icons.auto_awesome_mosaic_outlined),
|
timeline('asset_list_settings_title', Icons.auto_awesome_mosaic_outlined),
|
||||||
|
@ -27,6 +31,7 @@ enum SettingSection {
|
||||||
|
|
||||||
Widget get widget => switch (this) {
|
Widget get widget => switch (this) {
|
||||||
SettingSection.notifications => const NotificationSetting(),
|
SettingSection.notifications => const NotificationSetting(),
|
||||||
|
SettingSection.languages => const LanguageSettings(),
|
||||||
SettingSection.preferences => const PreferenceSetting(),
|
SettingSection.preferences => const PreferenceSetting(),
|
||||||
SettingSection.backup => const BackupSettings(),
|
SettingSection.backup => const BackupSettings(),
|
||||||
SettingSection.timeline => const AssetListSettings(),
|
SettingSection.timeline => const AssetListSettings(),
|
||||||
|
@ -37,6 +42,70 @@ enum SettingSection {
|
||||||
const SettingSection(this.title, this.icon);
|
const SettingSection(this.title, this.icon);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
class LanguageSettings extends HookConsumerWidget {
|
||||||
|
const LanguageSettings({super.key});
|
||||||
|
|
||||||
|
@override
|
||||||
|
Widget build(BuildContext context, WidgetRef ref) {
|
||||||
|
final currentLocale = context.locale;
|
||||||
|
final textController = useTextEditingController(
|
||||||
|
text: locales.keys.firstWhere(
|
||||||
|
(countryName) => locales[countryName] == currentLocale,
|
||||||
|
),
|
||||||
|
);
|
||||||
|
|
||||||
|
final selectedLocale = useState<Locale>(currentLocale);
|
||||||
|
|
||||||
|
return ListView(
|
||||||
|
padding: const EdgeInsets.all(16),
|
||||||
|
children: [
|
||||||
|
DropdownMenu(
|
||||||
|
inputDecorationTheme: InputDecorationTheme(
|
||||||
|
border: OutlineInputBorder(
|
||||||
|
borderRadius: BorderRadius.circular(20),
|
||||||
|
),
|
||||||
|
contentPadding: const EdgeInsets.only(left: 16),
|
||||||
|
),
|
||||||
|
menuStyle: MenuStyle(
|
||||||
|
shape: MaterialStatePropertyAll<OutlinedBorder>(
|
||||||
|
RoundedRectangleBorder(
|
||||||
|
borderRadius: BorderRadius.circular(15),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
),
|
||||||
|
menuHeight: context.height * 0.5,
|
||||||
|
hintText: "Languages",
|
||||||
|
label: const Text('Languages'),
|
||||||
|
dropdownMenuEntries: locales.keys
|
||||||
|
.map(
|
||||||
|
(countryName) => DropdownMenuEntry(
|
||||||
|
value: locales[countryName],
|
||||||
|
label: countryName,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.toList(),
|
||||||
|
controller: textController,
|
||||||
|
onSelected: (value) {
|
||||||
|
if (value != null) {
|
||||||
|
selectedLocale.value = value;
|
||||||
|
}
|
||||||
|
},
|
||||||
|
),
|
||||||
|
const SizedBox(height: 16),
|
||||||
|
ElevatedButton(
|
||||||
|
onPressed: selectedLocale.value == currentLocale
|
||||||
|
? null
|
||||||
|
: () {
|
||||||
|
context.setLocale(selectedLocale.value);
|
||||||
|
loadTranslations();
|
||||||
|
},
|
||||||
|
child: const Text('setting_languages_apply').tr(),
|
||||||
|
),
|
||||||
|
],
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@RoutePage()
|
@RoutePage()
|
||||||
class SettingsPage extends StatelessWidget {
|
class SettingsPage extends StatelessWidget {
|
||||||
const SettingsPage({super.key});
|
const SettingsPage({super.key});
|
||||||
|
|
Loading…
Reference in a new issue