feat(localization): Improve locale resolution and add Chinese script variants
This commit is contained in:
@@ -105,14 +105,40 @@ class AppLocale extends _$AppLocale {
|
|||||||
_storage = ref.watch(optimizedStorageServiceProvider);
|
_storage = ref.watch(optimizedStorageServiceProvider);
|
||||||
final code = _storage.getLocaleCode();
|
final code = _storage.getLocaleCode();
|
||||||
if (code != null && code.isNotEmpty) {
|
if (code != null && code.isNotEmpty) {
|
||||||
return Locale(code);
|
final parsed = _parseLocaleCode(code);
|
||||||
|
if (parsed != null) return parsed;
|
||||||
}
|
}
|
||||||
return null; // system default
|
return null; // system default
|
||||||
}
|
}
|
||||||
|
|
||||||
Future<void> setLocale(Locale? locale) async {
|
Future<void> setLocale(Locale? locale) async {
|
||||||
state = locale;
|
state = locale;
|
||||||
await _storage.setLocaleCode(locale?.languageCode);
|
await _storage.setLocaleCode(locale?.toLanguageTag());
|
||||||
|
}
|
||||||
|
|
||||||
|
Locale? _parseLocaleCode(String code) {
|
||||||
|
final normalized = code.replaceAll('_', '-');
|
||||||
|
final parts = normalized.split('-');
|
||||||
|
if (parts.isEmpty || parts.first.isEmpty) return null;
|
||||||
|
|
||||||
|
final language = parts.first;
|
||||||
|
String? script;
|
||||||
|
String? country;
|
||||||
|
|
||||||
|
for (var i = 1; i < parts.length; i++) {
|
||||||
|
final part = parts[i];
|
||||||
|
if (part.length == 4) {
|
||||||
|
script = '${part[0].toUpperCase()}${part.substring(1).toLowerCase()}';
|
||||||
|
} else if (part.length == 2 || part.length == 3) {
|
||||||
|
country = part.toUpperCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Locale.fromSubtags(
|
||||||
|
languageCode: language,
|
||||||
|
scriptCode: script,
|
||||||
|
countryCode: country,
|
||||||
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -38,7 +38,7 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
return l10n.currentlyUsingLightTheme;
|
return l10n.currentlyUsingLightTheme;
|
||||||
}();
|
}();
|
||||||
final locale = ref.watch(appLocaleProvider);
|
final locale = ref.watch(appLocaleProvider);
|
||||||
final currentLanguageCode = locale?.languageCode ?? 'system';
|
final currentLanguageCode = locale?.toLanguageTag() ?? 'system';
|
||||||
final languageLabel = _resolveLanguageLabel(context, currentLanguageCode);
|
final languageLabel = _resolveLanguageLabel(context, currentLanguageCode);
|
||||||
final activeTheme = ref.watch(appThemePaletteProvider);
|
final activeTheme = ref.watch(appThemePaletteProvider);
|
||||||
|
|
||||||
@@ -198,7 +198,7 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
Widget _buildLanguageSection(
|
Widget _buildLanguageSection(
|
||||||
BuildContext context,
|
BuildContext context,
|
||||||
WidgetRef ref,
|
WidgetRef ref,
|
||||||
String currentLanguageCode,
|
String currentLanguageTag,
|
||||||
String languageLabel,
|
String languageLabel,
|
||||||
) {
|
) {
|
||||||
final theme = context.conduitTheme;
|
final theme = context.conduitTheme;
|
||||||
@@ -220,15 +220,16 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
onTap: () async {
|
onTap: () async {
|
||||||
final selected = await _showLanguageSelector(
|
final selected = await _showLanguageSelector(
|
||||||
context,
|
context,
|
||||||
currentLanguageCode,
|
currentLanguageTag,
|
||||||
);
|
);
|
||||||
if (selected == null) return;
|
if (selected == null) return;
|
||||||
if (selected == 'system') {
|
if (selected == 'system') {
|
||||||
await ref.read(appLocaleProvider.notifier).setLocale(null);
|
await ref.read(appLocaleProvider.notifier).setLocale(null);
|
||||||
} else {
|
} else {
|
||||||
|
final parsed = _parseLocaleTag(selected);
|
||||||
await ref
|
await ref
|
||||||
.read(appLocaleProvider.notifier)
|
.read(appLocaleProvider.notifier)
|
||||||
.setLocale(Locale(selected));
|
.setLocale(parsed ?? Locale(selected));
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
),
|
),
|
||||||
@@ -1741,6 +1742,8 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String _resolveLanguageLabel(BuildContext context, String code) {
|
String _resolveLanguageLabel(BuildContext context, String code) {
|
||||||
|
final normalizedCode = code.replaceAll('_', '-');
|
||||||
|
|
||||||
switch (code) {
|
switch (code) {
|
||||||
case 'en':
|
case 'en':
|
||||||
return AppLocalizations.of(context)!.english;
|
return AppLocalizations.of(context)!.english;
|
||||||
@@ -1757,10 +1760,21 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
case 'ru':
|
case 'ru':
|
||||||
return AppLocalizations.of(context)!.russian;
|
return AppLocalizations.of(context)!.russian;
|
||||||
case 'zh':
|
case 'zh':
|
||||||
return AppLocalizations.of(context)!.chinese;
|
return AppLocalizations.of(context)!.chineseSimplified;
|
||||||
case 'ko':
|
case 'ko':
|
||||||
return AppLocalizations.of(context)!.korean;
|
return AppLocalizations.of(context)!.korean;
|
||||||
|
case 'zh-Hant':
|
||||||
|
return AppLocalizations.of(context)!.chineseTraditional;
|
||||||
default:
|
default:
|
||||||
|
if (normalizedCode == 'zh-hant') {
|
||||||
|
return AppLocalizations.of(context)!.chineseTraditional;
|
||||||
|
}
|
||||||
|
if (normalizedCode == 'zh') {
|
||||||
|
return AppLocalizations.of(context)!.chineseSimplified;
|
||||||
|
}
|
||||||
|
if (normalizedCode == 'ko') {
|
||||||
|
return AppLocalizations.of(context)!.korean;
|
||||||
|
}
|
||||||
return AppLocalizations.of(context)!.system;
|
return AppLocalizations.of(context)!.system;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1929,6 +1943,8 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
|
|
||||||
Future<String?> _showLanguageSelector(BuildContext context, String current) {
|
Future<String?> _showLanguageSelector(BuildContext context, String current) {
|
||||||
|
final normalizedCurrent = current.replaceAll('_', '-');
|
||||||
|
|
||||||
return showModalBottomSheet<String>(
|
return showModalBottomSheet<String>(
|
||||||
context: context,
|
context: context,
|
||||||
backgroundColor: Colors.transparent,
|
backgroundColor: Colors.transparent,
|
||||||
@@ -1949,52 +1965,79 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
const SizedBox(height: Spacing.sm),
|
const SizedBox(height: Spacing.sm),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.system),
|
title: Text(AppLocalizations.of(context)!.system),
|
||||||
trailing: current == 'system' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'system'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'system'),
|
onTap: () => Navigator.pop(context, 'system'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.english),
|
title: Text(AppLocalizations.of(context)!.english),
|
||||||
trailing: current == 'en' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'en'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'en'),
|
onTap: () => Navigator.pop(context, 'en'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.deutsch),
|
title: Text(AppLocalizations.of(context)!.deutsch),
|
||||||
trailing: current == 'de' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'de'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'de'),
|
onTap: () => Navigator.pop(context, 'de'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.espanol),
|
title: Text(AppLocalizations.of(context)!.espanol),
|
||||||
trailing: current == 'es' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'es'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'es'),
|
onTap: () => Navigator.pop(context, 'es'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.francais),
|
title: Text(AppLocalizations.of(context)!.francais),
|
||||||
trailing: current == 'fr' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'fr'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'fr'),
|
onTap: () => Navigator.pop(context, 'fr'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.italiano),
|
title: Text(AppLocalizations.of(context)!.italiano),
|
||||||
trailing: current == 'it' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'it'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'it'),
|
onTap: () => Navigator.pop(context, 'it'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.nederlands),
|
title: Text(AppLocalizations.of(context)!.nederlands),
|
||||||
trailing: current == 'nl' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'nl'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'nl'),
|
onTap: () => Navigator.pop(context, 'nl'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.russian),
|
title: Text(AppLocalizations.of(context)!.russian),
|
||||||
trailing: current == 'ru' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'ru'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'ru'),
|
onTap: () => Navigator.pop(context, 'ru'),
|
||||||
),
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.chinese),
|
title: Text(AppLocalizations.of(context)!.chineseSimplified),
|
||||||
trailing: current == 'zh' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'zh'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'zh'),
|
onTap: () => Navigator.pop(context, 'zh'),
|
||||||
),
|
),
|
||||||
|
ListTile(
|
||||||
|
title: Text(AppLocalizations.of(context)!.chineseTraditional),
|
||||||
|
trailing: normalizedCurrent == 'zh-Hant'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
|
onTap: () => Navigator.pop(context, 'zh-Hant'),
|
||||||
|
),
|
||||||
ListTile(
|
ListTile(
|
||||||
title: Text(AppLocalizations.of(context)!.korean),
|
title: Text(AppLocalizations.of(context)!.korean),
|
||||||
trailing: current == 'ko' ? const Icon(Icons.check) : null,
|
trailing: normalizedCurrent == 'ko'
|
||||||
|
? const Icon(Icons.check)
|
||||||
|
: null,
|
||||||
onTap: () => Navigator.pop(context, 'ko'),
|
onTap: () => Navigator.pop(context, 'ko'),
|
||||||
),
|
),
|
||||||
const SizedBox(height: Spacing.sm),
|
const SizedBox(height: Spacing.sm),
|
||||||
@@ -2006,6 +2049,31 @@ class AppCustomizationPage extends ConsumerWidget {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Locale? _parseLocaleTag(String code) {
|
||||||
|
final normalized = code.replaceAll('_', '-');
|
||||||
|
final parts = normalized.split('-');
|
||||||
|
if (parts.isEmpty || parts.first.isEmpty) return null;
|
||||||
|
|
||||||
|
final language = parts.first;
|
||||||
|
String? script;
|
||||||
|
String? country;
|
||||||
|
|
||||||
|
for (var i = 1; i < parts.length; i++) {
|
||||||
|
final part = parts[i];
|
||||||
|
if (part.length == 4) {
|
||||||
|
script = '${part[0].toUpperCase()}${part.substring(1).toLowerCase()}';
|
||||||
|
} else if (part.length == 2 || part.length == 3) {
|
||||||
|
country = part.toUpperCase();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return Locale.fromSubtags(
|
||||||
|
languageCode: language,
|
||||||
|
scriptCode: script,
|
||||||
|
countryCode: country,
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
class _PaletteOption extends StatelessWidget {
|
class _PaletteOption extends StatelessWidget {
|
||||||
const _PaletteOption({
|
const _PaletteOption({
|
||||||
required this.themeDefinition,
|
required this.themeDefinition,
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"nederlands": "Niederländisch",
|
"nederlands": "Niederländisch",
|
||||||
"russian": "Russisch",
|
"russian": "Russisch",
|
||||||
"chinese": "Chinesisch",
|
"chinese": "Chinesisch",
|
||||||
|
"chineseSimplified": "Chinesisch (Vereinfacht)",
|
||||||
|
"chineseTraditional": "Chinesisch (Traditionell)",
|
||||||
"korean": "Koreanisch",
|
"korean": "Koreanisch",
|
||||||
"deleteMessagesTitle": "Nachrichten löschen",
|
"deleteMessagesTitle": "Nachrichten löschen",
|
||||||
"deleteMessagesMessage": "{count} Nachrichten löschen?",
|
"deleteMessagesMessage": "{count} Nachrichten löschen?",
|
||||||
|
|||||||
@@ -862,6 +862,14 @@
|
|||||||
"@chinese": {
|
"@chinese": {
|
||||||
"description": "Language name: Chinese."
|
"description": "Language name: Chinese."
|
||||||
},
|
},
|
||||||
|
"chineseSimplified": "Chinese (Simplified)",
|
||||||
|
"@chineseSimplified": {
|
||||||
|
"description": "Language name: Chinese (Simplified)."
|
||||||
|
},
|
||||||
|
"chineseTraditional": "Chinese (Traditional)",
|
||||||
|
"@chineseTraditional": {
|
||||||
|
"description": "Language name: Chinese (Traditional)."
|
||||||
|
},
|
||||||
"korean": "한국어",
|
"korean": "한국어",
|
||||||
"@korean": {
|
"@korean": {
|
||||||
"description": "Language name: Korean."
|
"description": "Language name: Korean."
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"nederlands": "Nederlands",
|
"nederlands": "Nederlands",
|
||||||
"russian": "Русский",
|
"russian": "Русский",
|
||||||
"chinese": "中文",
|
"chinese": "中文",
|
||||||
|
"chineseSimplified": "Chino (simplificado)",
|
||||||
|
"chineseTraditional": "Chino (tradicional)",
|
||||||
"korean": "한국어",
|
"korean": "한국어",
|
||||||
"deleteMessagesTitle": "Eliminar mensajes",
|
"deleteMessagesTitle": "Eliminar mensajes",
|
||||||
"deleteMessagesMessage": "¿Eliminar {count} mensajes?",
|
"deleteMessagesMessage": "¿Eliminar {count} mensajes?",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"nederlands": "Néerlandais",
|
"nederlands": "Néerlandais",
|
||||||
"russian": "Russe",
|
"russian": "Russe",
|
||||||
"chinese": "Chinois",
|
"chinese": "Chinois",
|
||||||
|
"chineseSimplified": "Chinois (simplifié)",
|
||||||
|
"chineseTraditional": "Chinois (traditionnel)",
|
||||||
"korean": "Coréen",
|
"korean": "Coréen",
|
||||||
"deleteMessagesTitle": "Supprimer les messages",
|
"deleteMessagesTitle": "Supprimer les messages",
|
||||||
"deleteMessagesMessage": "Supprimer {count} messages ?",
|
"deleteMessagesMessage": "Supprimer {count} messages ?",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"nederlands": "Olandese",
|
"nederlands": "Olandese",
|
||||||
"russian": "Russo",
|
"russian": "Russo",
|
||||||
"chinese": "Cinese",
|
"chinese": "Cinese",
|
||||||
|
"chineseSimplified": "Cinese (semplificato)",
|
||||||
|
"chineseTraditional": "Cinese (tradizionale)",
|
||||||
"korean": "Coreano",
|
"korean": "Coreano",
|
||||||
"deleteMessagesTitle": "Elimina messaggi",
|
"deleteMessagesTitle": "Elimina messaggi",
|
||||||
"deleteMessagesMessage": "Eliminare {count} messaggi?",
|
"deleteMessagesMessage": "Eliminare {count} messaggi?",
|
||||||
|
|||||||
@@ -286,6 +286,8 @@
|
|||||||
"nederlands": "Nederlands",
|
"nederlands": "Nederlands",
|
||||||
"russian": "Русский",
|
"russian": "Русский",
|
||||||
"chinese": "中文",
|
"chinese": "中文",
|
||||||
|
"chineseSimplified": "중국어(간체)",
|
||||||
|
"chineseTraditional": "중국어(번체)",
|
||||||
"korean": "한국어",
|
"korean": "한국어",
|
||||||
"deleteMessagesTitle": "메시지 삭제",
|
"deleteMessagesTitle": "메시지 삭제",
|
||||||
"deleteMessagesMessage": "{count}개의 메시지를 삭제하시겠습니까?",
|
"deleteMessagesMessage": "{count}개의 메시지를 삭제하시겠습니까?",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"nederlands": "Nederlands",
|
"nederlands": "Nederlands",
|
||||||
"russian": "Русский",
|
"russian": "Русский",
|
||||||
"chinese": "中文",
|
"chinese": "中文",
|
||||||
|
"chineseSimplified": "Chinees (vereenvoudigd)",
|
||||||
|
"chineseTraditional": "Chinees (traditioneel)",
|
||||||
"korean": "한국어",
|
"korean": "한국어",
|
||||||
"deleteMessagesTitle": "Berichten verwijderen",
|
"deleteMessagesTitle": "Berichten verwijderen",
|
||||||
"deleteMessagesMessage": "{count} berichten verwijderen?",
|
"deleteMessagesMessage": "{count} berichten verwijderen?",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"nederlands": "Nederlands",
|
"nederlands": "Nederlands",
|
||||||
"russian": "Русский",
|
"russian": "Русский",
|
||||||
"chinese": "中文",
|
"chinese": "中文",
|
||||||
|
"chineseSimplified": "Китайский (упрощённый)",
|
||||||
|
"chineseTraditional": "Китайский (традиционный)",
|
||||||
"korean": "한국어",
|
"korean": "한국어",
|
||||||
"deleteMessagesTitle": "Удалить сообщения",
|
"deleteMessagesTitle": "Удалить сообщения",
|
||||||
"deleteMessagesMessage": "Удалить {count, plural, one{{count} сообщение} few{{count} сообщения} other{{count} сообщений}}?",
|
"deleteMessagesMessage": "Удалить {count, plural, one{{count} сообщение} few{{count} сообщения} other{{count} сообщений}}?",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"nederlands": "Nederlands",
|
"nederlands": "Nederlands",
|
||||||
"russian": "Русский",
|
"russian": "Русский",
|
||||||
"chinese": "中文",
|
"chinese": "中文",
|
||||||
|
"chineseSimplified": "简体中文",
|
||||||
|
"chineseTraditional": "繁體中文",
|
||||||
"korean": "한국어",
|
"korean": "한국어",
|
||||||
"deleteMessagesTitle": "删除消息",
|
"deleteMessagesTitle": "删除消息",
|
||||||
"deleteMessagesMessage": "删除 {count} 条消息?",
|
"deleteMessagesMessage": "删除 {count} 条消息?",
|
||||||
|
|||||||
@@ -178,6 +178,8 @@
|
|||||||
"nederlands": "Nederlands",
|
"nederlands": "Nederlands",
|
||||||
"russian": "Русский",
|
"russian": "Русский",
|
||||||
"chinese": "中文",
|
"chinese": "中文",
|
||||||
|
"chineseSimplified": "簡體中文",
|
||||||
|
"chineseTraditional": "繁體中文",
|
||||||
"korean": "한국어",
|
"korean": "한국어",
|
||||||
"deleteMessagesTitle": "刪除消息",
|
"deleteMessagesTitle": "刪除消息",
|
||||||
"deleteMessagesMessage": "刪除 {count} 條消息?",
|
"deleteMessagesMessage": "刪除 {count} 條消息?",
|
||||||
|
|||||||
@@ -189,12 +189,8 @@ class _ConduitAppState extends ConsumerState<ConduitApp> {
|
|||||||
if (deviceLocales == null || deviceLocales.isEmpty) {
|
if (deviceLocales == null || deviceLocales.isEmpty) {
|
||||||
return supported.first;
|
return supported.first;
|
||||||
}
|
}
|
||||||
for (final device in deviceLocales) {
|
final resolved = _resolveSupportedLocale(deviceLocales, supported);
|
||||||
for (final loc in supported) {
|
return resolved ?? supported.first;
|
||||||
if (loc.languageCode == device.languageCode) return loc;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return supported.first;
|
|
||||||
},
|
},
|
||||||
builder: (context, child) {
|
builder: (context, child) {
|
||||||
final brightness = Theme.of(context).brightness;
|
final brightness = Theme.of(context).brightness;
|
||||||
@@ -221,6 +217,73 @@ class _ConduitAppState extends ConsumerState<ConduitApp> {
|
|||||||
),
|
),
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bool _prefersTraditionalChinese(Locale deviceLocale) {
|
||||||
|
final script = deviceLocale.scriptCode?.toLowerCase();
|
||||||
|
if (script == 'hant') return true;
|
||||||
|
|
||||||
|
final country = deviceLocale.countryCode?.toUpperCase();
|
||||||
|
return country == 'TW' || country == 'HK' || country == 'MO';
|
||||||
|
}
|
||||||
|
|
||||||
|
Locale? _resolveSupportedLocale(
|
||||||
|
List<Locale>? deviceLocales,
|
||||||
|
Iterable<Locale> supported,
|
||||||
|
) {
|
||||||
|
if (deviceLocales == null || deviceLocales.isEmpty) return null;
|
||||||
|
|
||||||
|
for (final device in deviceLocales) {
|
||||||
|
final prefersTraditional = _prefersTraditionalChinese(device);
|
||||||
|
final deviceLanguage = device.languageCode.toLowerCase();
|
||||||
|
final deviceScript = device.scriptCode?.toLowerCase();
|
||||||
|
final deviceCountry = device.countryCode?.toUpperCase();
|
||||||
|
|
||||||
|
// Pass 1: match language with script (or preferred Traditional)
|
||||||
|
for (final loc in supported) {
|
||||||
|
final languageMatches =
|
||||||
|
loc.languageCode.toLowerCase() == deviceLanguage;
|
||||||
|
if (!languageMatches) continue;
|
||||||
|
|
||||||
|
final locScript = loc.scriptCode?.toLowerCase();
|
||||||
|
final scriptMatches =
|
||||||
|
locScript != null &&
|
||||||
|
locScript.isNotEmpty &&
|
||||||
|
(locScript == deviceScript ||
|
||||||
|
(loc.languageCode == 'zh' &&
|
||||||
|
locScript == 'hant' &&
|
||||||
|
prefersTraditional));
|
||||||
|
if (!scriptMatches) continue;
|
||||||
|
|
||||||
|
final locCountry = loc.countryCode?.toUpperCase();
|
||||||
|
final countryMatches =
|
||||||
|
locCountry == null ||
|
||||||
|
locCountry.isEmpty ||
|
||||||
|
locCountry == deviceCountry;
|
||||||
|
|
||||||
|
if (countryMatches) {
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 2: prefer Traditional Chinese when applicable
|
||||||
|
if (prefersTraditional) {
|
||||||
|
for (final loc in supported) {
|
||||||
|
if (loc.languageCode == 'zh' && loc.scriptCode == 'Hant') {
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pass 3: language-only match
|
||||||
|
for (final loc in supported) {
|
||||||
|
if (loc.languageCode.toLowerCase() == deviceLanguage) {
|
||||||
|
return loc;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Dismisses the soft keyboard whenever the user scrolls.
|
/// Dismisses the soft keyboard whenever the user scrolls.
|
||||||
|
|||||||
Reference in New Issue
Block a user