From 0c713998672fe481f3c93c5a99daa214dfe4b1aa Mon Sep 17 00:00:00 2001 From: insleker Date: Sat, 30 Aug 2025 11:47:30 +0800 Subject: [PATCH] feat: support several language arb files --- lib/app.dart | 6 +- lib/data/model/model.dart | 4 +- lib/l10n/app_de.arb | 39 ++++++ lib/l10n/app_en.arb | 6 - lib/l10n/app_es.arb | 3 - lib/l10n/app_fr.arb | 39 ++++++ lib/l10n/app_ja.arb | 39 ++++++ lib/l10n/app_ko.arb | 39 ++++++ lib/l10n/app_uk.arb | 39 ++++++ lib/l10n/app_zh.arb | 3 - lib/l10n/app_zh_CN.arb | 39 ++++++ lib/l10n/app_zh_TW.arb | 3 - lib/ui/features/preferences/providers.dart | 111 ++++++++++++------ .../preferences/widgets/settings_screen.dart | 73 +++++++++--- pubspec.yaml | 1 + 15 files changed, 373 insertions(+), 71 deletions(-) create mode 100644 lib/l10n/app_de.arb create mode 100644 lib/l10n/app_fr.arb create mode 100644 lib/l10n/app_ja.arb create mode 100644 lib/l10n/app_ko.arb create mode 100644 lib/l10n/app_uk.arb create mode 100644 lib/l10n/app_zh_CN.arb diff --git a/lib/app.dart b/lib/app.dart index 5ca1ad3..cc6c1e6 100644 --- a/lib/app.dart +++ b/lib/app.dart @@ -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(), ); }, diff --git a/lib/data/model/model.dart b/lib/data/model/model.dart index 96c6760..4faeeba 100644 --- a/lib/data/model/model.dart +++ b/lib/data/model/model.dart @@ -49,9 +49,7 @@ class PdfState { signedPage: signedPage ?? this.signedPage, placementsByPage: placementsByPage ?? this.placementsByPage, selectedPlacementIndex: - selectedPlacementIndex == null - ? this.selectedPlacementIndex - : selectedPlacementIndex, + selectedPlacementIndex ?? this.selectedPlacementIndex, ); } diff --git a/lib/l10n/app_de.arb b/lib/l10n/app_de.arb new file mode 100644 index 0000000..5c0dd78 --- /dev/null +++ b/lib/l10n/app_de.arb @@ -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" +} \ No newline at end of file diff --git a/lib/l10n/app_en.arb b/lib/l10n/app_en.arb index 59c9ca2..3f90e5f 100644 --- a/lib/l10n/app_en.arb +++ b/lib/l10n/app_en.arb @@ -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", diff --git a/lib/l10n/app_es.arb b/lib/l10n/app_es.arb index d300743..95d936f 100644 --- a/lib/l10n/app_es.arb +++ b/lib/l10n/app_es.arb @@ -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.", diff --git a/lib/l10n/app_fr.arb b/lib/l10n/app_fr.arb new file mode 100644 index 0000000..c194d21 --- /dev/null +++ b/lib/l10n/app_fr.arb @@ -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" +} \ No newline at end of file diff --git a/lib/l10n/app_ja.arb b/lib/l10n/app_ja.arb new file mode 100644 index 0000000..4a7193d --- /dev/null +++ b/lib/l10n/app_ja.arb @@ -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": "元に戻す" +} \ No newline at end of file diff --git a/lib/l10n/app_ko.arb b/lib/l10n/app_ko.arb new file mode 100644 index 0000000..eb2081e --- /dev/null +++ b/lib/l10n/app_ko.arb @@ -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": "실행 취소" +} \ No newline at end of file diff --git a/lib/l10n/app_uk.arb b/lib/l10n/app_uk.arb new file mode 100644 index 0000000..81108c0 --- /dev/null +++ b/lib/l10n/app_uk.arb @@ -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": "Відмінити" +} \ No newline at end of file diff --git a/lib/l10n/app_zh.arb b/lib/l10n/app_zh.arb index 92cdeb6..634ed25 100644 --- a/lib/l10n/app_zh.arb +++ b/lib/l10n/app_zh.arb @@ -18,9 +18,6 @@ "goTo": "前往:", "invalidOrUnsupportedFile": "無效或不支援的檔案", "language": "語言", - "languageChineseTraditional": "繁體中文", - "languageEnglish": "英文", - "languageSpanish": "西班牙文", "loadSignatureFromFile": "從檔案載入簽名", "lockAspectRatio": "鎖定長寬比", "longPressOrRightClickTheSignatureToConfirmOrDelete": "長按或右鍵點擊簽名以確認或刪除。", diff --git a/lib/l10n/app_zh_CN.arb b/lib/l10n/app_zh_CN.arb new file mode 100644 index 0000000..caed805 --- /dev/null +++ b/lib/l10n/app_zh_CN.arb @@ -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": "撤销" +} \ No newline at end of file diff --git a/lib/l10n/app_zh_TW.arb b/lib/l10n/app_zh_TW.arb index 103c01f..0e4a434 100644 --- a/lib/l10n/app_zh_TW.arb +++ b/lib/l10n/app_zh_TW.arb @@ -18,9 +18,6 @@ "goTo": "前往:", "invalidOrUnsupportedFile": "無效或不支援的檔案", "language": "語言", - "languageChineseTraditional": "繁體中文", - "languageEnglish": "英文", - "languageSpanish": "西班牙文", "loadSignatureFromFile": "從檔案載入簽名", "lockAspectRatio": "鎖定長寬比", "longPressOrRightClickTheSignatureToConfirmOrDelete": "長按或右鍵點擊簽名以確認或刪除。", diff --git a/lib/ui/features/preferences/providers.dart b/lib/ui/features/preferences/providers.dart index 39faed1..7bc0be0 100644 --- a/lib/ui/features/preferences/providers.dart +++ b/lib/ui/features/preferences/providers.dart @@ -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('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 _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 { 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 { } Future 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((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((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>(( + ref, +) async { + final tags = _supportedTags().toList()..sort(); + final delegate = LocaleNamesLocalizationsDelegate(); + final Map 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; +}); diff --git a/lib/ui/features/preferences/widgets/settings_screen.dart b/lib/ui/features/preferences/widgets/settings_screen.dart index bac6f79..370ea65 100644 --- a/lib/ui/features/preferences/widgets/settings_screen.dart +++ b/lib/ui/features/preferences/widgets/settings_screen.dart @@ -39,25 +39,62 @@ class SettingsScreen extends ConsumerWidget { style: const TextStyle(fontWeight: FontWeight.bold), ), const SizedBox(height: 8), - DropdownButton( - 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( + key: const Key('ddl_language'), + value: prefs.language, + items: + AppLocalizations.supportedLocales.map((loc) { + final tag = toLanguageTag(loc); + return DropdownMenuItem( + 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( + key: const Key('ddl_language'), + value: prefs.language, + items: + items + .map( + (tag) => DropdownMenuItem( + 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, diff --git a/pubspec.yaml b/pubspec.yaml index 64c9bee..1b608ba 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -49,6 +49,7 @@ dependencies: flutter_localizations: sdk: flutter intl: any + flutter_localized_locales: ^2.0.5 dev_dependencies: flutter_test: