feat(app): the app speaks British and American English and German

Settings offers English (UK), English (US) and Deutsch after the
device's language, each named in its own language. app_en.arb, the
template, is now British English (colour, centre) and the fallback for
any language the app doesn't speak; app_en_US.arb overrides the five
messages American English spells differently, and app_en_GB.arb is
empty, there so that Material's texts and dates are British too. The
German translation says "du" and keeps one set of terms throughout
(Lernset, Karteikarte, Lernpfad; Anki's own Stapel and Unterstapel in
the Anki import). arb_files_test checks that German has every message
of the template and that every translation keeps its placeholders.

resolveAppLocale picks the first language the device prefers that the
app speaks, in its country's variant where there is one; other English
is British, and a country alone never picks a language (Spanish in the
United States isn't American English), which Flutter's own resolution
would do.

Both MaterialApps take material_ui's GlobalMaterialLocalizations
delegates: gen-l10n's list brings flutter_localizations', which serve
only package:flutter/material.dart, so material_ui's widgets found no
Material texts in German and threw.

The editor's own text blocks show our placeholders while empty (list
item, to-do, quote, heading, and the desktop's / hint): the editor's
own follow a locale of their own, process-wide, in few languages. The
last texts written into widgets (the person's data failing to open,
the cards failing to read, the About dialog) moved into the arb files.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Signed-off-by: Tizian 「ティツィアーン」 <raisondetredev@pm.me>
This commit is contained in:
Tizian 「ティツィアーン」 2026-10-07 02:01:01 +02:00
commit 3ee9bce0c5
Signed by: raisondetredev
GPG key ID: 07F6FCF405488B64

View file

@ -5,6 +5,7 @@ import 'package:flutter/widgets.dart';
import 'package:flutter_localizations/flutter_localizations.dart';
import 'package:intl/intl.dart' as intl;
import 'app_localizations_de.dart';
import 'app_localizations_en.dart';
// ignore_for_file: type=lint
@ -92,7 +93,12 @@ abstract class AppLocalizations {
];
/// A list of this localizations delegate's supported locales.
static const List<Locale> supportedLocales = <Locale>[Locale('en')];
static const List<Locale> supportedLocales = <Locale>[
Locale('de'),
Locale('en'),
Locale('en', 'GB'),
Locale('en', 'US'),
];
/// The app's name, e.g. in the window title and the rail's logo.
///
@ -271,7 +277,7 @@ abstract class AppLocalizations {
/// No description provided for @studysetSheetColor.
///
/// In en, this message translates to:
/// **'Color'**
/// **'Colour'**
String get studysetSheetColor;
/// No description provided for @studysetSheetDiscardTitle.
@ -301,7 +307,7 @@ abstract class AppLocalizations {
/// No description provided for @colorPickerLabel.
///
/// In en, this message translates to:
/// **'Color: {color}'**
/// **'Colour: {color}'**
String colorPickerLabel(String color);
/// No description provided for @studysetCountFlashcards.
@ -1363,7 +1369,7 @@ abstract class AppLocalizations {
/// No description provided for @studysetSheetInheritColor.
///
/// In en, this message translates to:
/// **'Same color as the studyset it is in'**
/// **'Same colour as the studyset it is in'**
String get studysetSheetInheritColor;
/// No description provided for @exploreTitle.
@ -1498,6 +1504,24 @@ abstract class AppLocalizations {
/// **'About Halkyon Learn'**
String get settingsAbout;
/// No description provided for @aboutLegalese.
///
/// In en, this message translates to:
/// **'Free software: the app is under the GNU Affero General Public License, version 3 or any later version, the server under the Server Side Public License, version 1.'**
String get aboutLegalese;
/// No description provided for @aboutSourceCode.
///
/// In en, this message translates to:
/// **'The source of what this instance runs:'**
String get aboutSourceCode;
/// No description provided for @aboutSourceUnreachable.
///
/// In en, this message translates to:
/// **'Couldn\'t reach this instance to ask where its source is.'**
String get aboutSourceUnreachable;
/// No description provided for @settingsAppearance.
///
/// In en, this message translates to:
@ -1693,7 +1717,7 @@ abstract class AppLocalizations {
/// No description provided for @tagSheetColor.
///
/// In en, this message translates to:
/// **'Color'**
/// **'Colour'**
String get tagSheetColor;
/// No description provided for @tagSheetCreate.
@ -1870,6 +1894,12 @@ abstract class AppLocalizations {
/// **'No flashcards have this tag.'**
String get cardFilterNothing;
/// No description provided for @cardListFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t read the cards.'**
String get cardListFailed;
/// No description provided for @cardListMore.
///
/// In en, this message translates to:
@ -2191,7 +2221,7 @@ abstract class AppLocalizations {
/// No description provided for @formatAlignCenter.
///
/// In en, this message translates to:
/// **'Center'**
/// **'Centre'**
String get formatAlignCenter;
/// No description provided for @formatAlignRight.
@ -2992,6 +3022,12 @@ abstract class AppLocalizations {
/// **'Try again'**
String get commonTryAgain;
/// No description provided for @personDataFailed.
///
/// In en, this message translates to:
/// **'Couldn\'t open this device\'s data.'**
String get personDataFailed;
/// No description provided for @sessionUnreachable.
///
/// In en, this message translates to:
@ -3160,6 +3196,24 @@ abstract class AppLocalizations {
/// **'Quote'**
String get blockQuote;
/// No description provided for @blockListItem.
///
/// In en, this message translates to:
/// **'List item'**
String get blockListItem;
/// No description provided for @blockChecklistItem.
///
/// In en, this message translates to:
/// **'To-do'**
String get blockChecklistItem;
/// No description provided for @editorSlashHint.
///
/// In en, this message translates to:
/// **'Type / to insert a block, or just start typing'**
String get editorSlashHint;
/// No description provided for @blockDivider.
///
/// In en, this message translates to:
@ -4642,15 +4696,31 @@ class _AppLocalizationsDelegate
@override
bool isSupported(Locale locale) =>
<String>['en'].contains(locale.languageCode);
<String>['de', 'en'].contains(locale.languageCode);
@override
bool shouldReload(_AppLocalizationsDelegate old) => false;
}
AppLocalizations lookupAppLocalizations(Locale locale) {
// Lookup logic when language+country codes are specified.
switch (locale.languageCode) {
case 'en':
{
switch (locale.countryCode) {
case 'GB':
return AppLocalizationsEnGb();
case 'US':
return AppLocalizationsEnUs();
}
break;
}
}
// Lookup logic when only language code is specified.
switch (locale.languageCode) {
case 'de':
return AppLocalizationsDe();
case 'en':
return AppLocalizationsEn();
}