feat: support several language arb files
This commit is contained in:
parent
b2d96e466e
commit
0c71399867
|
@ -1,5 +1,6 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
|
||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
import 'package:pdf_signature/ui/features/pdf/widgets/pdf_screen.dart';
|
||||
import 'ui/features/preferences/providers.dart';
|
||||
|
@ -55,7 +56,10 @@ class MyApp extends StatelessWidget {
|
|||
themeMode: themeMode,
|
||||
locale: appLocale,
|
||||
supportedLocales: AppLocalizations.supportedLocales,
|
||||
localizationsDelegates: AppLocalizations.localizationsDelegates,
|
||||
localizationsDelegates: [
|
||||
...AppLocalizations.localizationsDelegates,
|
||||
LocaleNamesLocalizationsDelegate(),
|
||||
],
|
||||
home: const PdfSignatureHomePage(),
|
||||
);
|
||||
},
|
||||
|
|
|
@ -49,9 +49,7 @@ class PdfState {
|
|||
signedPage: signedPage ?? this.signedPage,
|
||||
placementsByPage: placementsByPage ?? this.placementsByPage,
|
||||
selectedPlacementIndex:
|
||||
selectedPlacementIndex == null
|
||||
? this.selectedPlacementIndex
|
||||
: selectedPlacementIndex,
|
||||
selectedPlacementIndex ?? this.selectedPlacementIndex,
|
||||
);
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"appTitle": "PDF-Signatur",
|
||||
"backgroundRemoval": "Hintergrund entfernen",
|
||||
"brightness": "Helligkeit",
|
||||
"clear": "Löschen",
|
||||
"confirm": "Bestätigen",
|
||||
"contrast": "Kontrast",
|
||||
"createNewSignature": "Neue Signatur erstellen",
|
||||
"delete": "Löschen",
|
||||
"downloadStarted": "Download gestartet",
|
||||
"dpi": "DPI:",
|
||||
"drawSignature": "Signatur zeichnen",
|
||||
"errorWithMessage": "Fehler: {message}",
|
||||
"exportingPleaseWait": "Exportiere… Bitte warten",
|
||||
"failedToGeneratePdf": "PDF konnte nicht generiert werden",
|
||||
"failedToSavePdf": "PDF konnte nicht gespeichert werden",
|
||||
"goTo": "Gehe zu:",
|
||||
"invalidOrUnsupportedFile": "Ungültige oder nicht unterstützte Datei",
|
||||
"language": "Sprache",
|
||||
"loadSignatureFromFile": "Signatur aus Datei laden",
|
||||
"lockAspectRatio": "Seitenverhältnis sperren",
|
||||
"longPressOrRightClickTheSignatureToConfirmOrDelete": "Drücken Sie lange oder klicken Sie mit der rechten Maustaste auf die Signatur, um sie zu bestätigen oder zu löschen.",
|
||||
"next": "Weiter",
|
||||
"noPdfLoaded": "Keine PDF-Datei geladen",
|
||||
"nothingToSaveYet": "Noch nichts zu speichern",
|
||||
"openPdf": "PDF öffnen...",
|
||||
"pageInfo": "Seite {current}/{total}",
|
||||
"prev": "Vorherige",
|
||||
"resetToDefaults": "Auf Standardwerte zurücksetzen",
|
||||
"savedWithPath": "Gespeichert: {path}",
|
||||
"saveSignedPdf": "Signiertes PDF speichern",
|
||||
"settings": "Einstellungen",
|
||||
"signature": "Signatur",
|
||||
"theme": "Design",
|
||||
"themeDark": "Dunkel",
|
||||
"themeLight": "Hell",
|
||||
"themeSystem": "System",
|
||||
"undo": "Rückgängig"
|
||||
}
|
|
@ -43,12 +43,6 @@
|
|||
"@invalidOrUnsupportedFile": {},
|
||||
"language": "Language",
|
||||
"@language": {},
|
||||
"languageChineseTraditional": "Traditional Chinese",
|
||||
"@languageChineseTraditional": {},
|
||||
"languageEnglish": "English",
|
||||
"@languageEnglish": {},
|
||||
"languageSpanish": "Spanish",
|
||||
"@languageSpanish": {},
|
||||
"loadSignatureFromFile": "Load Signature from file",
|
||||
"@loadSignatureFromFile": {},
|
||||
"lockAspectRatio": "Lock aspect ratio",
|
||||
|
|
|
@ -17,9 +17,6 @@
|
|||
"goTo": "Ir a:",
|
||||
"invalidOrUnsupportedFile": "Archivo inválido o no compatible",
|
||||
"language": "Idioma",
|
||||
"languageChineseTraditional": "Chino tradicional",
|
||||
"languageEnglish": "Inglés",
|
||||
"languageSpanish": "Español",
|
||||
"loadSignatureFromFile": "Cargar firma desde archivo",
|
||||
"lockAspectRatio": "Bloquear relación de aspecto",
|
||||
"longPressOrRightClickTheSignatureToConfirmOrDelete": "Pulse prolongadamente o haga clic derecho en la firma para confirmar o eliminar.",
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"appTitle": "Signature PDF",
|
||||
"backgroundRemoval": "Suppression de l'arrière-plan",
|
||||
"brightness": "Luminosité",
|
||||
"clear": "Effacer",
|
||||
"confirm": "Confirmer",
|
||||
"contrast": "Contraste",
|
||||
"createNewSignature": "Créer une nouvelle signature",
|
||||
"delete": "Supprimer",
|
||||
"downloadStarted": "Téléchargement commencé",
|
||||
"dpi": "DPI :",
|
||||
"drawSignature": "Dessiner une signature",
|
||||
"errorWithMessage": "Erreur : {message}",
|
||||
"exportingPleaseWait": "Exportation… Veuillez patienter",
|
||||
"failedToGeneratePdf": "Échec de la génération du PDF",
|
||||
"failedToSavePdf": "Échec de l'enregistrement du PDF",
|
||||
"goTo": "Aller à :",
|
||||
"invalidOrUnsupportedFile": "Fichier invalide ou non pris en charge",
|
||||
"language": "Langue",
|
||||
"loadSignatureFromFile": "Charger une signature depuis un fichier",
|
||||
"lockAspectRatio": "Verrouiller le ratio largeur/hauteur",
|
||||
"longPressOrRightClickTheSignatureToConfirmOrDelete": "Appuyez longuement ou cliquez droit sur la signature pour la confirmer ou la supprimer.",
|
||||
"next": "Suivant",
|
||||
"noPdfLoaded": "Aucun PDF chargé",
|
||||
"nothingToSaveYet": "Rien à enregistrer pour le moment",
|
||||
"openPdf": "Ouvrir un PDF...",
|
||||
"pageInfo": "Page {current}/{total}",
|
||||
"prev": "Précédent",
|
||||
"resetToDefaults": "Rétablir les valeurs par défaut",
|
||||
"savedWithPath": "Enregistré : {path}",
|
||||
"saveSignedPdf": "Enregistrer le PDF signé",
|
||||
"settings": "Paramètres",
|
||||
"signature": "Signature",
|
||||
"theme": "Thème",
|
||||
"themeDark": "Sombre",
|
||||
"themeLight": "Clair",
|
||||
"themeSystem": "Système",
|
||||
"undo": "Annuler"
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"appTitle": "PDF署名",
|
||||
"backgroundRemoval": "背景除去",
|
||||
"brightness": "明るさ",
|
||||
"clear": "クリア",
|
||||
"confirm": "確認",
|
||||
"contrast": "コントラスト",
|
||||
"createNewSignature": "新しい署名を作成",
|
||||
"delete": "削除",
|
||||
"downloadStarted": "ダウンロード開始",
|
||||
"dpi": "DPI:",
|
||||
"drawSignature": "署名をかく",
|
||||
"errorWithMessage": "エラー:{message}",
|
||||
"exportingPleaseWait": "エクスポート中…お待ちください",
|
||||
"failedToGeneratePdf": "PDFの生成に失敗しました",
|
||||
"failedToSavePdf": "PDFの保存に失敗しました",
|
||||
"goTo": "移動:",
|
||||
"invalidOrUnsupportedFile": "無効なファイルまたはサポートされていないファイル",
|
||||
"language": "言語",
|
||||
"loadSignatureFromFile": "ファイルから署名を読み込む",
|
||||
"lockAspectRatio": "アスペクト比をロック",
|
||||
"longPressOrRightClickTheSignatureToConfirmOrDelete": "署名を長押しまたは右クリックして、確認または削除します。",
|
||||
"next": "次へ",
|
||||
"noPdfLoaded": "PDFが読み込まれていません",
|
||||
"nothingToSaveYet": "まだ保存するものがありません",
|
||||
"openPdf": "PDFを開く…",
|
||||
"pageInfo": "ページ {current}/{total}",
|
||||
"prev": "前へ",
|
||||
"resetToDefaults": "デフォルトに戻す",
|
||||
"savedWithPath": "保存しました:{path}",
|
||||
"saveSignedPdf": "署名済みPDFを保存",
|
||||
"settings": "設定",
|
||||
"signature": "署名",
|
||||
"theme": "テーマ",
|
||||
"themeDark": "ダーク",
|
||||
"themeLight": "ライト",
|
||||
"themeSystem": "システム",
|
||||
"undo": "元に戻す"
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"appTitle": "PDF 서명",
|
||||
"backgroundRemoval": "배경 제거",
|
||||
"brightness": "밝기",
|
||||
"clear": "지우기",
|
||||
"confirm": "확인",
|
||||
"contrast": "대비",
|
||||
"createNewSignature": "새 서명 만들기",
|
||||
"delete": "삭제",
|
||||
"downloadStarted": "다운로드 시작됨",
|
||||
"dpi": "DPI:",
|
||||
"drawSignature": "서명 그리기",
|
||||
"errorWithMessage": "오류: {message}",
|
||||
"exportingPleaseWait": "내보내는 중... 잠시 기다려주세요",
|
||||
"failedToGeneratePdf": "PDF 생성 실패",
|
||||
"failedToSavePdf": "PDF 저장 실패",
|
||||
"goTo": "이동:",
|
||||
"invalidOrUnsupportedFile": "잘못된 파일이거나 지원되지 않는 파일입니다.",
|
||||
"language": "언어",
|
||||
"loadSignatureFromFile": "파일에서 서명 불러오기",
|
||||
"lockAspectRatio": "종횡비 고정",
|
||||
"longPressOrRightClickTheSignatureToConfirmOrDelete": "서명을 길게 누르거나 마우스 오른쪽 버튼을 클릭하여 확인하거나 삭제합니다.",
|
||||
"next": "다음",
|
||||
"noPdfLoaded": "로드된 PDF 없음",
|
||||
"nothingToSaveYet": "아직 저장할 내용이 없습니다.",
|
||||
"openPdf": "PDF 열기...",
|
||||
"pageInfo": "{current}/{total} 페이지",
|
||||
"prev": "이전",
|
||||
"resetToDefaults": "기본값으로 재설정",
|
||||
"savedWithPath": "{path}에 저장됨",
|
||||
"saveSignedPdf": "서명된 PDF 저장",
|
||||
"settings": "설정",
|
||||
"signature": "서명",
|
||||
"theme": "테마",
|
||||
"themeDark": "다크",
|
||||
"themeLight": "라이트",
|
||||
"themeSystem": "시스템",
|
||||
"undo": "실행 취소"
|
||||
}
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"appTitle": "Підпис PDF",
|
||||
"backgroundRemoval": "Видалення фону",
|
||||
"brightness": "Яскравість",
|
||||
"clear": "Очистити",
|
||||
"confirm": "Підтвердити",
|
||||
"contrast": "Контрастність",
|
||||
"createNewSignature": "Створити новий підпис",
|
||||
"delete": "Видалити",
|
||||
"downloadStarted": "Завантаження розпочато",
|
||||
"dpi": "DPI:",
|
||||
"drawSignature": "Намалювати підпис",
|
||||
"errorWithMessage": "Помилка: {message}",
|
||||
"exportingPleaseWait": "Експортування... Зачекайте",
|
||||
"failedToGeneratePdf": "Не вдалося створити PDF",
|
||||
"failedToSavePdf": "Не вдалося зберегти PDF",
|
||||
"goTo": "Перейти до:",
|
||||
"invalidOrUnsupportedFile": "Недійсний або непідтримуваний файл",
|
||||
"language": "Мова",
|
||||
"loadSignatureFromFile": "Завантажити підпис з файлу",
|
||||
"lockAspectRatio": "Зафіксувати співвідношення сторін",
|
||||
"longPressOrRightClickTheSignatureToConfirmOrDelete": "Довго натисніть або клацніть правою кнопкою миші на підпис, щоб підтвердити або видалити.",
|
||||
"next": "Далі",
|
||||
"noPdfLoaded": "PDF не завантажено",
|
||||
"nothingToSaveYet": "Ще нічого не потрібно зберігати",
|
||||
"openPdf": "Відкрити PDF...",
|
||||
"pageInfo": "Сторінка {current}/{total}",
|
||||
"prev": "Попередня",
|
||||
"resetToDefaults": "Скинути до значень за замовчуванням",
|
||||
"savedWithPath": "Збережено: {path}",
|
||||
"saveSignedPdf": "Зберегти підписаний PDF",
|
||||
"settings": "Налаштування",
|
||||
"signature": "Підпис",
|
||||
"theme": "Тема",
|
||||
"themeDark": "Темна",
|
||||
"themeLight": "Світла",
|
||||
"themeSystem": "Системна",
|
||||
"undo": "Відмінити"
|
||||
}
|
|
@ -18,9 +18,6 @@
|
|||
"goTo": "前往:",
|
||||
"invalidOrUnsupportedFile": "無效或不支援的檔案",
|
||||
"language": "語言",
|
||||
"languageChineseTraditional": "繁體中文",
|
||||
"languageEnglish": "英文",
|
||||
"languageSpanish": "西班牙文",
|
||||
"loadSignatureFromFile": "從檔案載入簽名",
|
||||
"lockAspectRatio": "鎖定長寬比",
|
||||
"longPressOrRightClickTheSignatureToConfirmOrDelete": "長按或右鍵點擊簽名以確認或刪除。",
|
||||
|
|
|
@ -0,0 +1,39 @@
|
|||
{
|
||||
"appTitle": "PDF 签名",
|
||||
"backgroundRemoval": "背景移除",
|
||||
"brightness": "亮度",
|
||||
"clear": "清除",
|
||||
"confirm": "确认",
|
||||
"contrast": "对比度",
|
||||
"createNewSignature": "创建新的签名",
|
||||
"delete": "删除",
|
||||
"downloadStarted": "下载已开始",
|
||||
"dpi": "DPI:",
|
||||
"drawSignature": "绘制签名",
|
||||
"errorWithMessage": "错误:{message}",
|
||||
"exportingPleaseWait": "正在导出... 请稍候",
|
||||
"failedToGeneratePdf": "PDF 生成失败",
|
||||
"failedToSavePdf": "PDF 保存失败",
|
||||
"goTo": "跳转到:",
|
||||
"invalidOrUnsupportedFile": "无效或不支持的文件",
|
||||
"language": "语言",
|
||||
"loadSignatureFromFile": "从文件加载签名",
|
||||
"lockAspectRatio": "锁定纵横比",
|
||||
"longPressOrRightClickTheSignatureToConfirmOrDelete": "长按或右键单击签名以确认或删除。",
|
||||
"next": "下一步",
|
||||
"noPdfLoaded": "未加载 PDF",
|
||||
"nothingToSaveYet": "尚无内容保存",
|
||||
"openPdf": "打开 PDF...",
|
||||
"pageInfo": "第 {current} 页 / 共 {total} 页",
|
||||
"prev": "上一页",
|
||||
"resetToDefaults": "恢复默认值",
|
||||
"savedWithPath": "已保存:{path}",
|
||||
"saveSignedPdf": "保存已签名的 PDF",
|
||||
"settings": "设置",
|
||||
"signature": "签名",
|
||||
"theme": "主题",
|
||||
"themeDark": "深色",
|
||||
"themeLight": "浅色",
|
||||
"themeSystem": "系统",
|
||||
"undo": "撤销"
|
||||
}
|
|
@ -18,9 +18,6 @@
|
|||
"goTo": "前往:",
|
||||
"invalidOrUnsupportedFile": "無效或不支援的檔案",
|
||||
"language": "語言",
|
||||
"languageChineseTraditional": "繁體中文",
|
||||
"languageEnglish": "英文",
|
||||
"languageSpanish": "西班牙文",
|
||||
"loadSignatureFromFile": "從檔案載入簽名",
|
||||
"lockAspectRatio": "鎖定長寬比",
|
||||
"longPressOrRightClickTheSignatureToConfirmOrDelete": "長按或右鍵點擊簽名以確認或刪除。",
|
||||
|
|
|
@ -1,32 +1,64 @@
|
|||
import 'package:flutter/material.dart';
|
||||
import 'package:flutter_riverpod/flutter_riverpod.dart';
|
||||
import 'package:shared_preferences/shared_preferences.dart';
|
||||
import 'package:pdf_signature/l10n/app_localizations.dart';
|
||||
import 'package:flutter_localized_locales/flutter_localized_locales.dart';
|
||||
|
||||
// Simple supported locales
|
||||
const supportedLocales = <Locale>[
|
||||
Locale('en'),
|
||||
Locale('zh', 'TW'),
|
||||
Locale('es'),
|
||||
];
|
||||
// Helpers to work with BCP-47 language tags
|
||||
String toLanguageTag(Locale loc) {
|
||||
final lang = loc.languageCode.toLowerCase();
|
||||
final region = loc.countryCode;
|
||||
if (region == null || region.isEmpty) return lang;
|
||||
return '$lang-${region.toUpperCase()}';
|
||||
}
|
||||
|
||||
Locale _parseLanguageTag(String tag) {
|
||||
final cleaned = tag.replaceAll('_', '-');
|
||||
final parts = cleaned.split('-');
|
||||
if (parts.length >= 2 && parts[1].isNotEmpty) {
|
||||
return Locale(parts[0].toLowerCase(), parts[1].toUpperCase());
|
||||
}
|
||||
return Locale(parts[0].toLowerCase());
|
||||
}
|
||||
|
||||
Set<String> _supportedTags() {
|
||||
return AppLocalizations.supportedLocales.map((l) => toLanguageTag(l)).toSet();
|
||||
}
|
||||
|
||||
// Keys
|
||||
const _kTheme = 'theme'; // 'light'|'dark'|'system'
|
||||
const _kLanguage = 'language'; // 'en'|'zh-TW'|'es'
|
||||
const _kLanguage = 'language'; // BCP-47 tag like 'en', 'zh-TW', 'es'
|
||||
|
||||
String _normalizeLanguageTag(String tag) {
|
||||
final parts = tag.split('-');
|
||||
if (parts.isEmpty) return 'en';
|
||||
final primary = parts[0].toLowerCase();
|
||||
if (primary == 'en') return 'en';
|
||||
if (primary == 'es') return 'es';
|
||||
if (primary == 'zh') {
|
||||
final region = parts.length > 1 ? parts[1].toUpperCase() : '';
|
||||
if (region == 'TW') return 'zh-TW';
|
||||
// other zh regions not supported; fall back to English
|
||||
return 'en';
|
||||
}
|
||||
// Fallback default
|
||||
return 'en';
|
||||
final tags = _supportedTags();
|
||||
if (tag.isEmpty) return tags.contains('en') ? 'en' : tags.first;
|
||||
// Replace underscore with hyphen and canonicalize case
|
||||
final normalized = () {
|
||||
final t = tag.replaceAll('_', '-');
|
||||
final parts = t.split('-');
|
||||
final lang = parts[0].toLowerCase();
|
||||
if (parts.length >= 2 && parts[1].isNotEmpty) {
|
||||
return '$lang-${parts[1].toUpperCase()}';
|
||||
}
|
||||
return lang;
|
||||
}();
|
||||
|
||||
// Exact match
|
||||
if (tags.contains(normalized)) return normalized;
|
||||
|
||||
// Try fallback to language-only if available
|
||||
final langOnly = normalized.split('-')[0];
|
||||
if (tags.contains(langOnly)) return langOnly;
|
||||
|
||||
// Try to pick first tag with same language
|
||||
final candidate = tags.firstWhere(
|
||||
(t) => t.split('-')[0] == langOnly,
|
||||
orElse: () => '',
|
||||
);
|
||||
if (candidate.isNotEmpty) return candidate;
|
||||
|
||||
// Final fallback to English or first supported
|
||||
return tags.contains('en') ? 'en' : tags.first;
|
||||
}
|
||||
|
||||
class PreferencesState {
|
||||
|
@ -49,7 +81,8 @@ class PreferencesNotifier extends StateNotifier<PreferencesState> {
|
|||
theme: prefs.getString(_kTheme) ?? 'system',
|
||||
language: _normalizeLanguageTag(
|
||||
prefs.getString(_kLanguage) ??
|
||||
WidgetsBinding.instance.platformDispatcher.locale.toLanguageTag(),
|
||||
WidgetsBinding.instance.platformDispatcher.locale
|
||||
.toLanguageTag(),
|
||||
),
|
||||
),
|
||||
) {
|
||||
|
@ -84,11 +117,12 @@ class PreferencesNotifier extends StateNotifier<PreferencesState> {
|
|||
}
|
||||
|
||||
Future<void> resetToDefaults() async {
|
||||
final device = WidgetsBinding.instance.platformDispatcher.locale.toLanguageTag();
|
||||
final normalized = _normalizeLanguageTag(device);
|
||||
state = PreferencesState(theme: 'system', language: normalized);
|
||||
await prefs.setString(_kTheme, 'system');
|
||||
await prefs.setString(_kLanguage, normalized);
|
||||
final device =
|
||||
WidgetsBinding.instance.platformDispatcher.locale.toLanguageTag();
|
||||
final normalized = _normalizeLanguageTag(device);
|
||||
state = PreferencesState(theme: 'system', language: normalized);
|
||||
await prefs.setString(_kTheme, 'system');
|
||||
await prefs.setString(_kLanguage, normalized);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -125,19 +159,28 @@ final themeModeProvider = Provider<ThemeMode>((ref) {
|
|||
}
|
||||
});
|
||||
|
||||
Locale _parseLanguageTag(String tag) {
|
||||
// 'zh-TW' -> ('zh','TW')
|
||||
final parts = tag.split('-');
|
||||
if (parts.length == 2) return Locale(parts[0], parts[1]);
|
||||
return Locale(parts[0]);
|
||||
}
|
||||
|
||||
final localeProvider = Provider<Locale?>((ref) {
|
||||
final prefs = ref.watch(preferencesProvider);
|
||||
final supported = _supportedTags();
|
||||
// Return explicit Locale for supported ones; if not supported, null to follow device
|
||||
final supported = {'en', 'zh-TW', 'es'};
|
||||
if (supported.contains(prefs.language)) {
|
||||
return _parseLanguageTag(prefs.language);
|
||||
}
|
||||
return null;
|
||||
});
|
||||
|
||||
/// Provides a map of BCP-47 tag -> autonym (self name), independent of UI locale.
|
||||
final languageAutonymsProvider = FutureProvider<Map<String, String>>((
|
||||
ref,
|
||||
) async {
|
||||
final tags = _supportedTags().toList()..sort();
|
||||
final delegate = LocaleNamesLocalizationsDelegate();
|
||||
final Map<String, String> result = {};
|
||||
for (final tag in tags) {
|
||||
final locale = _parseLanguageTag(tag);
|
||||
final names = await delegate.load(locale);
|
||||
final name = names.nameOf(tag) ?? tag;
|
||||
result[tag] = name;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
|
|
@ -39,25 +39,62 @@ class SettingsScreen extends ConsumerWidget {
|
|||
style: const TextStyle(fontWeight: FontWeight.bold),
|
||||
),
|
||||
const SizedBox(height: 8),
|
||||
DropdownButton<String>(
|
||||
key: const Key('ddl_language'),
|
||||
value: prefs.language,
|
||||
items: [
|
||||
DropdownMenuItem(value: 'en', child: Text(l.languageEnglish)),
|
||||
DropdownMenuItem(
|
||||
value: 'zh-TW',
|
||||
child: Text(l.languageChineseTraditional),
|
||||
ref
|
||||
.watch(languageAutonymsProvider)
|
||||
.when(
|
||||
loading:
|
||||
() => const SizedBox(
|
||||
height: 48,
|
||||
child: Center(child: CircularProgressIndicator()),
|
||||
),
|
||||
error:
|
||||
(_, __) => DropdownButton<String>(
|
||||
key: const Key('ddl_language'),
|
||||
value: prefs.language,
|
||||
items:
|
||||
AppLocalizations.supportedLocales.map((loc) {
|
||||
final tag = toLanguageTag(loc);
|
||||
return DropdownMenuItem<String>(
|
||||
value: tag,
|
||||
child: Text(tag),
|
||||
);
|
||||
}).toList(),
|
||||
onChanged:
|
||||
(v) =>
|
||||
v == null
|
||||
? null
|
||||
: ref
|
||||
.read(preferencesProvider.notifier)
|
||||
.setLanguage(v),
|
||||
),
|
||||
data: (names) {
|
||||
final items =
|
||||
AppLocalizations.supportedLocales
|
||||
.map((loc) => toLanguageTag(loc))
|
||||
.toList()
|
||||
..sort();
|
||||
return DropdownButton<String>(
|
||||
key: const Key('ddl_language'),
|
||||
value: prefs.language,
|
||||
items:
|
||||
items
|
||||
.map(
|
||||
(tag) => DropdownMenuItem<String>(
|
||||
value: tag,
|
||||
child: Text(names[tag] ?? tag),
|
||||
),
|
||||
)
|
||||
.toList(),
|
||||
onChanged:
|
||||
(v) =>
|
||||
v == null
|
||||
? null
|
||||
: ref
|
||||
.read(preferencesProvider.notifier)
|
||||
.setLanguage(v),
|
||||
);
|
||||
},
|
||||
),
|
||||
DropdownMenuItem(value: 'es', child: Text(l.languageSpanish)),
|
||||
],
|
||||
onChanged:
|
||||
(v) =>
|
||||
v == null
|
||||
? null
|
||||
: ref
|
||||
.read(preferencesProvider.notifier)
|
||||
.setLanguage(v),
|
||||
),
|
||||
const Spacer(),
|
||||
Align(
|
||||
alignment: Alignment.bottomRight,
|
||||
|
|
|
@ -49,6 +49,7 @@ dependencies:
|
|||
flutter_localizations:
|
||||
sdk: flutter
|
||||
intl: any
|
||||
flutter_localized_locales: ^2.0.5
|
||||
|
||||
dev_dependencies:
|
||||
flutter_test:
|
||||
|
|
Loading…
Reference in New Issue