feat(i18n): add Asturian (asturianu) as a language

Adds Asturianu, translated in full (345 strings), plus the wiring to
make a minority language work end to end — the OS language picker is an
unreliable way to reach it, so the in-app choice must be explicit and
remembered.

- Model Asturian as the real `ast` locale (honest, international-by-design),
  not a fake `es-AST` region — slang rejects a 3-letter region anyway.
- Flutter ships no Material/Cupertino/intl localizations for `ast`, so map
  the framework chrome to Spanish (`materialLocaleFor`); the app's own text
  stays Asturian via slang. Fixes an "Invalid locale ast" crash when the
  auto-backup date was formatted with intl.
- Persist the picked language in the keystore (LocaleStore) and restore it
  at startup so it survives restarts and wins over the device locale.
- Settings: add the Asturianu tile; compare the full AppLocale so `es` and
  `ast` aren't both marked selected.

Tests: LocaleStore round-trip, locale switch + Spanish-chrome fallback,
and a regression for the intl date crash under Asturian.
This commit is contained in:
vjrj 2026-07-10 15:43:33 +02:00
parent beb29915d8
commit 9bf5c5cfbc
10 changed files with 695 additions and 15 deletions

View file

@ -205,7 +205,11 @@ class TaneApp extends StatelessWidget {
// desktop (Flutter disables that by default), so the photo carousel and
// lists swipe there too.
scrollBehavior: const _DragScrollBehavior(),
locale: TranslationProvider.of(context).flutterLocale,
// Flutter ships no Material/Cupertino localizations for Asturian
// (`ast`), so render the framework chrome (date pickers, native dialog
// buttons) in Spanish the closest shipped language while the app's
// own text stays Asturian via slang. Every other locale maps 1:1.
locale: materialLocaleFor(TranslationProvider.of(context).flutterLocale),
supportedLocales: AppLocaleUtils.supportedLocales,
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
@ -219,6 +223,14 @@ class TaneApp extends StatelessWidget {
}
}
/// Maps the app's active locale to one Flutter's bundled Material/Cupertino
/// localizations can serve. Asturian (`ast`) has none, so it borrows Spanish
/// (`es`) for the framework chrome; the app's own strings stay Asturian (they
/// come from slang, not from `Localizations`). All other locales pass through.
@visibleForTesting
Locale materialLocaleFor(Locale locale) =>
locale.languageCode == 'ast' ? const Locale('es') : locale;
/// Enables mouse/trackpad drag scrolling (off by default on desktop) so the
/// photo carousel and lists can be swiped with a pointer.
class _DragScrollBehavior extends MaterialScrollBehavior {