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