Implementiert den kompletten Kern-Flow der Emergency App gemäß docs/plan.md: - Notrufnummern-Datenbasis für 199 Länder + Modelle/Repository (AP1) - DialerService für tel:-Notrufe ohne Auto-Dial (AP2) - Statisches UI mit Panik-Button-Fallback (AP3) - Priorisierte Ländererkennung: SIM/Netz -> GPS/Geocoding -> Cache (AP4) - EmergencyProvider verdrahtet Repository/LocationService mit der UI (AP5) - Manuelle Länderauswahl als Fallback samt eigener Priorität (AP6) - UI-Lokalisierung (de/en/fr/es), an erkanntes Land gekoppelt (AP7) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
244 lines
4.9 KiB
Dart
244 lines
4.9 KiB
Dart
import 'package:flutter/widgets.dart';
|
||
|
||
/// Ordnet ISO 3166-1 alpha-2 Ländercodes eine primäre Amts-/Mehrheitssprache
|
||
/// (ISO 639-1) zu (`docs/plan.md`, Abschnitt 2 + 8, AP7: "Lokalisierung:
|
||
/// Sprache an erkanntes Land gekoppelt").
|
||
///
|
||
/// **Bewusst pragmatisch, nicht für jedes Land 100% exakt:** Viele Länder
|
||
/// sind mehrsprachig (z. B. Belgien, Kamerun); hier wird jeweils die
|
||
/// praktisch sinnvollste/meistgesprochene Amtssprache gewählt. Deckt alle
|
||
/// Länder aus `assets/data/emergency_numbers.json` ab, ohne Anspruch auf
|
||
/// linguistische Vollständigkeit – die App braucht daraus nur eine
|
||
/// plausible UI-Sprache, nicht eine sprachwissenschaftlich exakte Aussage.
|
||
///
|
||
/// Die meisten hier gelisteten Sprachen haben (Stand AP7) **keine** eigene
|
||
/// ARB-Übersetzung (nur `de`/`en`/`fr`/`es` existieren) – das ist gewollt,
|
||
/// siehe [resolveAppLocale]: Nicht unterstützte Sprachen fallen automatisch
|
||
/// auf Englisch zurück.
|
||
const Map<String, String> countryLocaleMap = {
|
||
'AD': 'ca',
|
||
'AE': 'ar',
|
||
'AF': 'fa',
|
||
'AG': 'en',
|
||
'AL': 'sq',
|
||
'AM': 'hy',
|
||
'AO': 'pt',
|
||
'AR': 'es',
|
||
'AT': 'de',
|
||
'AU': 'en',
|
||
'AZ': 'az',
|
||
'BA': 'bs',
|
||
'BB': 'en',
|
||
'BD': 'bn',
|
||
'BE': 'nl',
|
||
'BF': 'fr',
|
||
'BG': 'bg',
|
||
'BH': 'ar',
|
||
'BI': 'rn',
|
||
'BJ': 'fr',
|
||
'BN': 'ms',
|
||
'BO': 'es',
|
||
'BR': 'pt',
|
||
'BS': 'en',
|
||
'BT': 'dz',
|
||
'BW': 'en',
|
||
'BY': 'be',
|
||
'BZ': 'en',
|
||
'CA': 'en',
|
||
'CD': 'fr',
|
||
'CF': 'fr',
|
||
'CG': 'fr',
|
||
'CH': 'de',
|
||
'CI': 'fr',
|
||
'CL': 'es',
|
||
'CM': 'fr',
|
||
'CN': 'zh',
|
||
'CO': 'es',
|
||
'CR': 'es',
|
||
'CU': 'es',
|
||
'CV': 'pt',
|
||
'CY': 'el',
|
||
'CZ': 'cs',
|
||
'DE': 'de',
|
||
'DJ': 'fr',
|
||
'DK': 'da',
|
||
'DM': 'en',
|
||
'DO': 'es',
|
||
'DZ': 'ar',
|
||
'EC': 'es',
|
||
'EE': 'et',
|
||
'EG': 'ar',
|
||
'ER': 'ti',
|
||
'ES': 'es',
|
||
'ET': 'am',
|
||
'FI': 'fi',
|
||
'FJ': 'en',
|
||
'FM': 'en',
|
||
'FR': 'fr',
|
||
'GA': 'fr',
|
||
'GB': 'en',
|
||
'GD': 'en',
|
||
'GE': 'ka',
|
||
'GH': 'en',
|
||
'GM': 'en',
|
||
'GN': 'fr',
|
||
'GQ': 'es',
|
||
'GR': 'el',
|
||
'GT': 'es',
|
||
'GW': 'pt',
|
||
'GY': 'en',
|
||
'HK': 'zh',
|
||
'HN': 'es',
|
||
'HR': 'hr',
|
||
'HT': 'fr',
|
||
'HU': 'hu',
|
||
'ID': 'id',
|
||
'IE': 'en',
|
||
'IL': 'he',
|
||
'IN': 'hi',
|
||
'IQ': 'ar',
|
||
'IR': 'fa',
|
||
'IS': 'is',
|
||
'IT': 'it',
|
||
'JM': 'en',
|
||
'JO': 'ar',
|
||
'JP': 'ja',
|
||
'KE': 'sw',
|
||
'KG': 'ky',
|
||
'KH': 'km',
|
||
'KI': 'en',
|
||
'KM': 'ar',
|
||
'KN': 'en',
|
||
'KP': 'ko',
|
||
'KR': 'ko',
|
||
'KW': 'ar',
|
||
'KZ': 'kk',
|
||
'LA': 'lo',
|
||
'LB': 'ar',
|
||
'LC': 'en',
|
||
'LI': 'de',
|
||
'LK': 'si',
|
||
'LR': 'en',
|
||
'LS': 'en',
|
||
'LT': 'lt',
|
||
'LU': 'fr',
|
||
'LV': 'lv',
|
||
'LY': 'ar',
|
||
'MA': 'ar',
|
||
'MC': 'fr',
|
||
'MD': 'ro',
|
||
'ME': 'sr',
|
||
'MG': 'fr',
|
||
'MH': 'en',
|
||
'MK': 'mk',
|
||
'ML': 'fr',
|
||
'MM': 'my',
|
||
'MN': 'mn',
|
||
'MO': 'zh',
|
||
'MR': 'ar',
|
||
'MT': 'mt',
|
||
'MU': 'en',
|
||
'MV': 'dv',
|
||
'MW': 'en',
|
||
'MX': 'es',
|
||
'MY': 'ms',
|
||
'MZ': 'pt',
|
||
'NA': 'en',
|
||
'NE': 'fr',
|
||
'NG': 'en',
|
||
'NI': 'es',
|
||
'NL': 'nl',
|
||
'NO': 'nb',
|
||
'NP': 'ne',
|
||
'NR': 'en',
|
||
'NZ': 'en',
|
||
'OM': 'ar',
|
||
'PA': 'es',
|
||
'PE': 'es',
|
||
'PG': 'en',
|
||
'PH': 'en',
|
||
'PK': 'ur',
|
||
'PL': 'pl',
|
||
'PS': 'ar',
|
||
'PT': 'pt',
|
||
'PW': 'en',
|
||
'PY': 'es',
|
||
'QA': 'ar',
|
||
'RO': 'ro',
|
||
'RS': 'sr',
|
||
'RU': 'ru',
|
||
'RW': 'rw',
|
||
'SA': 'ar',
|
||
'SB': 'en',
|
||
'SC': 'en',
|
||
'SD': 'ar',
|
||
'SE': 'sv',
|
||
'SG': 'en',
|
||
'SI': 'sl',
|
||
'SK': 'sk',
|
||
'SL': 'en',
|
||
'SM': 'it',
|
||
'SN': 'fr',
|
||
'SO': 'so',
|
||
'SR': 'nl',
|
||
'SS': 'en',
|
||
'ST': 'pt',
|
||
'SV': 'es',
|
||
'SY': 'ar',
|
||
'SZ': 'en',
|
||
'TD': 'fr',
|
||
'TG': 'fr',
|
||
'TH': 'th',
|
||
'TJ': 'tg',
|
||
'TL': 'pt',
|
||
'TM': 'tk',
|
||
'TN': 'ar',
|
||
'TO': 'en',
|
||
'TR': 'tr',
|
||
'TT': 'en',
|
||
'TV': 'en',
|
||
'TW': 'zh',
|
||
'TZ': 'sw',
|
||
'UA': 'uk',
|
||
'UG': 'en',
|
||
'US': 'en',
|
||
'UY': 'es',
|
||
'UZ': 'uz',
|
||
'VA': 'it',
|
||
'VC': 'en',
|
||
'VE': 'es',
|
||
'VN': 'vi',
|
||
'VU': 'en',
|
||
'WS': 'en',
|
||
'XK': 'sq',
|
||
'YE': 'ar',
|
||
'ZA': 'en',
|
||
'ZM': 'en',
|
||
'ZW': 'en',
|
||
};
|
||
|
||
/// Sprache, auf die zurückgefallen wird, wenn kein gemapptes/unterstütztes
|
||
/// Ergebnis ermittelbar ist. Muss immer als `AppLocalizations.supportedLocales`
|
||
/// vorhanden sein.
|
||
const _fallbackLanguageCode = 'en';
|
||
|
||
/// Berechnet die effektiv zu verwendende App-[Locale] aus einem (optionalen)
|
||
/// ISO 3166-1 alpha-2 Ländercode und der Liste tatsächlich unterstützter
|
||
/// Locales (`AppLocalizations.supportedLocales`).
|
||
///
|
||
/// Regeln (`docs/plan.md`, Abschnitt 2 + 8, AP7):
|
||
/// - [isoCode] ist `null` (Land noch nicht ermittelt/unbekannt) → Fallback
|
||
/// Englisch, kein Rätselraten über die Gerätesystemsprache.
|
||
/// - [isoCode] ist nicht in [countryLocaleMap] gelistet → Fallback Englisch.
|
||
/// - Die gemappte Sprache ist nicht in [supportedLocales] enthalten (also
|
||
/// keine ARB-Übersetzung vorhanden) → Fallback Englisch.
|
||
/// - Sonst: die gemappte Sprache.
|
||
Locale resolveAppLocale(String? isoCode, List<Locale> supportedLocales) {
|
||
final languageCode = isoCode == null ? null : countryLocaleMap[isoCode.toUpperCase()];
|
||
if (languageCode != null &&
|
||
supportedLocales.any((locale) => locale.languageCode == languageCode)) {
|
||
return Locale(languageCode);
|
||
}
|
||
return const Locale(_fallbackLanguageCode);
|
||
}
|