Since 2.10.0 the whole app sits behind one all-or-nothing initialization gate, and that gate discarded the only evidence of its own failure. It caught the error, logged nothing but `error.runtimeType`, rendered an icon plus the word "Error" plus Retry, and never reported the error because catching it kept the crash reporter from ever seeing it. There is no log file on any platform, the buffer is in memory only, a double-clicked Windows release build has no console, and the log viewer lives in Settings, behind the gate that just failed. #1732 is the result: a Windows 11 user whose app will not boot and who cannot produce a single byte of diagnostic detail. The gate now names its phases. Each step is wrapped so a throw carries the phase it came from, replacing a `Future.wait` that discarded every error but the first and could not attribute it to any of four concurrent steps. The failure screen renders the phase, the exception type, the message and an expandable stack, plus copy and upload actions that reuse the existing log-relay flow. The record is persisted next to the database so the next successful launch can surface it in Settings > Logs, and it is reported to the crash reporter explicitly. Only preferences and the database still gate the launch. Window chrome, locale, crash-reporting init, TV/performance detection, the image-cache budget and download storage are best-effort and time-bounded, so a stalled platform thread degrades instead of holding the splash forever. Sentry no longer receives the startup work as its `appRunner`: that made a startup failure indistinguishable from a Sentry failure, and the guard would then have re-run migrations and the database open a second time. The two remaining fatal steps become recoverable. Preference reads tolerate a value whose stored type no longer matches, dropping the key and defaulting instead of failing the boot. A store that cannot be parsed is detected before either desktop plugin backend can memoise it, which is what makes an in-process repair possible at all. Repair is never automatic: it states what it will cost, salvages the credential-vault key and every tracker and Seerr session it can validate out of the damaged bytes, reseeds them, and moves the original aside rather than deleting it. Servers and profiles survive a salvaged key because their tokens are ciphertext in the database; tracker and Seerr sessions are plaintext preference entries, so the copy says they may still need reconnecting. Nothing derived from the store reaches a diagnostic. `FormatException` prints an excerpt of whatever it failed to parse, and during startup that document holds the vault key, refresh tokens and session cookies while the redaction manager still has nothing registered, so the wrapper keeps only the cause's type and offset and the record is an allowlist of already-redacted fields. The quarantined copy is labelled as containing credentials, is never offered for upload, and can be deleted from the dialog. Also self-heals orphaned WAL/SHM sidecars on desktop rather than only tvOS, makes every `createTable` migration step idempotent, keeps MSVC link by-products out of the Windows bundle, and asserts bundle contents in CI. Refs #1732
482 lines
15 KiB
Dart
482 lines
15 KiB
Dart
/// Generated file. Do not edit.
|
|
///
|
|
/// Source: lib/i18n
|
|
/// To regenerate, run: `dart run slang`
|
|
///
|
|
/// Locales: 22
|
|
/// Strings: 32938 (1497 per locale)
|
|
|
|
// coverage:ignore-file
|
|
// ignore_for_file: type=lint, unused_import
|
|
// dart format off
|
|
|
|
import 'package:flutter/widgets.dart';
|
|
import 'package:intl/intl.dart';
|
|
import 'package:slang/generated.dart';
|
|
import 'package:slang_flutter/slang_flutter.dart';
|
|
export 'package:slang_flutter/slang_flutter.dart';
|
|
|
|
import 'strings_az.g.dart' deferred as l_az;
|
|
import 'strings_bg.g.dart' deferred as l_bg;
|
|
import 'strings_da.g.dart' deferred as l_da;
|
|
import 'strings_de.g.dart' deferred as l_de;
|
|
import 'strings_es.g.dart' deferred as l_es;
|
|
import 'strings_fr.g.dart' deferred as l_fr;
|
|
import 'strings_hu.g.dart' deferred as l_hu;
|
|
import 'strings_it.g.dart' deferred as l_it;
|
|
import 'strings_ja.g.dart' deferred as l_ja;
|
|
import 'strings_kk.g.dart' deferred as l_kk;
|
|
import 'strings_ko.g.dart' deferred as l_ko;
|
|
import 'strings_nb.g.dart' deferred as l_nb;
|
|
import 'strings_nl.g.dart' deferred as l_nl;
|
|
import 'strings_pl.g.dart' deferred as l_pl;
|
|
import 'strings_pt.g.dart' deferred as l_pt;
|
|
import 'strings_ru.g.dart' deferred as l_ru;
|
|
import 'strings_sv.g.dart' deferred as l_sv;
|
|
import 'strings_tr.g.dart' deferred as l_tr;
|
|
import 'strings_uz.g.dart' deferred as l_uz;
|
|
import 'strings_zh.g.dart' deferred as l_zh;
|
|
import 'strings_zh_Hant.g.dart' deferred as l_zh_Hant;
|
|
part 'strings_en.g.dart';
|
|
|
|
/// Supported locales.
|
|
///
|
|
/// Usage:
|
|
/// - LocaleSettings.setLocale(AppLocale.en) // set locale
|
|
/// - Locale locale = AppLocale.en.flutterLocale // get flutter locale from enum
|
|
/// - if (LocaleSettings.currentLocale == AppLocale.en) // locale check
|
|
enum AppLocale with BaseAppLocale<AppLocale, Translations> {
|
|
en(languageCode: 'en'),
|
|
az(languageCode: 'az'),
|
|
bg(languageCode: 'bg'),
|
|
da(languageCode: 'da'),
|
|
de(languageCode: 'de'),
|
|
es(languageCode: 'es'),
|
|
fr(languageCode: 'fr'),
|
|
hu(languageCode: 'hu'),
|
|
it(languageCode: 'it'),
|
|
ja(languageCode: 'ja'),
|
|
kk(languageCode: 'kk'),
|
|
ko(languageCode: 'ko'),
|
|
nb(languageCode: 'nb'),
|
|
nl(languageCode: 'nl'),
|
|
pl(languageCode: 'pl'),
|
|
pt(languageCode: 'pt'),
|
|
ru(languageCode: 'ru'),
|
|
sv(languageCode: 'sv'),
|
|
tr(languageCode: 'tr'),
|
|
uz(languageCode: 'uz'),
|
|
zh(languageCode: 'zh'),
|
|
zhHant(languageCode: 'zh', scriptCode: 'Hant');
|
|
|
|
const AppLocale({
|
|
required this.languageCode,
|
|
this.scriptCode, // ignore: unused_element, unused_element_parameter
|
|
this.countryCode, // ignore: unused_element, unused_element_parameter
|
|
});
|
|
|
|
@override final String languageCode;
|
|
@override final String? scriptCode;
|
|
@override final String? countryCode;
|
|
|
|
@override
|
|
Future<Translations> build({
|
|
Map<String, Node>? overrides,
|
|
PluralResolver? cardinalResolver,
|
|
PluralResolver? ordinalResolver,
|
|
}) async {
|
|
switch (this) {
|
|
case AppLocale.en:
|
|
return TranslationsEn(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.az:
|
|
await l_az.loadLibrary();
|
|
return l_az.TranslationsAz(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.bg:
|
|
await l_bg.loadLibrary();
|
|
return l_bg.TranslationsBg(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.da:
|
|
await l_da.loadLibrary();
|
|
return l_da.TranslationsDa(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.de:
|
|
await l_de.loadLibrary();
|
|
return l_de.TranslationsDe(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.es:
|
|
await l_es.loadLibrary();
|
|
return l_es.TranslationsEs(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.fr:
|
|
await l_fr.loadLibrary();
|
|
return l_fr.TranslationsFr(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.hu:
|
|
await l_hu.loadLibrary();
|
|
return l_hu.TranslationsHu(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.it:
|
|
await l_it.loadLibrary();
|
|
return l_it.TranslationsIt(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.ja:
|
|
await l_ja.loadLibrary();
|
|
return l_ja.TranslationsJa(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.kk:
|
|
await l_kk.loadLibrary();
|
|
return l_kk.TranslationsKk(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.ko:
|
|
await l_ko.loadLibrary();
|
|
return l_ko.TranslationsKo(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.nb:
|
|
await l_nb.loadLibrary();
|
|
return l_nb.TranslationsNb(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.nl:
|
|
await l_nl.loadLibrary();
|
|
return l_nl.TranslationsNl(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.pl:
|
|
await l_pl.loadLibrary();
|
|
return l_pl.TranslationsPl(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.pt:
|
|
await l_pt.loadLibrary();
|
|
return l_pt.TranslationsPt(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.ru:
|
|
await l_ru.loadLibrary();
|
|
return l_ru.TranslationsRu(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.sv:
|
|
await l_sv.loadLibrary();
|
|
return l_sv.TranslationsSv(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.tr:
|
|
await l_tr.loadLibrary();
|
|
return l_tr.TranslationsTr(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.uz:
|
|
await l_uz.loadLibrary();
|
|
return l_uz.TranslationsUz(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.zh:
|
|
await l_zh.loadLibrary();
|
|
return l_zh.TranslationsZh(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.zhHant:
|
|
await l_zh_Hant.loadLibrary();
|
|
return l_zh_Hant.TranslationsZhHant(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
}
|
|
}
|
|
|
|
@override
|
|
Translations buildSync({
|
|
Map<String, Node>? overrides,
|
|
PluralResolver? cardinalResolver,
|
|
PluralResolver? ordinalResolver,
|
|
}) {
|
|
switch (this) {
|
|
case AppLocale.en:
|
|
return TranslationsEn(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.az:
|
|
return l_az.TranslationsAz(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.bg:
|
|
return l_bg.TranslationsBg(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.da:
|
|
return l_da.TranslationsDa(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.de:
|
|
return l_de.TranslationsDe(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.es:
|
|
return l_es.TranslationsEs(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.fr:
|
|
return l_fr.TranslationsFr(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.hu:
|
|
return l_hu.TranslationsHu(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.it:
|
|
return l_it.TranslationsIt(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.ja:
|
|
return l_ja.TranslationsJa(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.kk:
|
|
return l_kk.TranslationsKk(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.ko:
|
|
return l_ko.TranslationsKo(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.nb:
|
|
return l_nb.TranslationsNb(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.nl:
|
|
return l_nl.TranslationsNl(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.pl:
|
|
return l_pl.TranslationsPl(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.pt:
|
|
return l_pt.TranslationsPt(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.ru:
|
|
return l_ru.TranslationsRu(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.sv:
|
|
return l_sv.TranslationsSv(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.tr:
|
|
return l_tr.TranslationsTr(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.uz:
|
|
return l_uz.TranslationsUz(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.zh:
|
|
return l_zh.TranslationsZh(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
case AppLocale.zhHant:
|
|
return l_zh_Hant.TranslationsZhHant(
|
|
overrides: overrides,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
}
|
|
}
|
|
|
|
/// Gets current instance managed by [LocaleSettings].
|
|
Translations get translations => LocaleSettings.instance.getTranslations(this);
|
|
}
|
|
|
|
/// Method A: Simple
|
|
///
|
|
/// No rebuild after locale change.
|
|
/// Translation happens during initialization of the widget (call of t).
|
|
/// Configurable via 'translate_var'.
|
|
///
|
|
/// Usage:
|
|
/// String a = t.someKey.anotherKey;
|
|
/// String b = t['someKey.anotherKey']; // Only for edge cases!
|
|
Translations get t => LocaleSettings.instance.currentTranslations;
|
|
|
|
/// Method B: Advanced
|
|
///
|
|
/// All widgets using this method will trigger a rebuild when locale changes.
|
|
/// Use this if you have e.g. a settings page where the user can select the locale during runtime.
|
|
///
|
|
/// Step 1:
|
|
/// wrap your App with
|
|
/// TranslationProvider(
|
|
/// child: MyApp()
|
|
/// );
|
|
///
|
|
/// Step 2:
|
|
/// final t = Translations.of(context); // Get t variable.
|
|
/// String a = t.someKey.anotherKey; // Use t variable.
|
|
/// String b = t['someKey.anotherKey']; // Only for edge cases!
|
|
class TranslationProvider extends BaseTranslationProvider<AppLocale, Translations> {
|
|
TranslationProvider({required super.child}) : super(settings: LocaleSettings.instance);
|
|
|
|
static InheritedLocaleData<AppLocale, Translations> of(BuildContext context) => InheritedLocaleData.of<AppLocale, Translations>(context);
|
|
}
|
|
|
|
/// Method B shorthand via [BuildContext] extension method.
|
|
/// Configurable via 'translate_var'.
|
|
///
|
|
/// Usage (e.g. in a widget's build method):
|
|
/// context.t.someKey.anotherKey
|
|
extension BuildContextTranslationsExtension on BuildContext {
|
|
Translations get t => TranslationProvider.of(this).translations;
|
|
}
|
|
|
|
/// Manages all translation instances and the current locale
|
|
class LocaleSettings extends BaseFlutterLocaleSettings<AppLocale, Translations> {
|
|
LocaleSettings._() : super(
|
|
utils: AppLocaleUtils.instance,
|
|
lazy: true,
|
|
);
|
|
|
|
static final instance = LocaleSettings._();
|
|
|
|
// static aliases (checkout base methods for documentation)
|
|
static AppLocale get currentLocale => instance.currentLocale;
|
|
static Stream<AppLocale> getLocaleStream() => instance.getLocaleStream();
|
|
static Future<AppLocale> setLocale(AppLocale locale, {bool? listenToDeviceLocale = false}) => instance.setLocale(locale, listenToDeviceLocale: listenToDeviceLocale);
|
|
static Future<AppLocale> setLocaleRaw(String rawLocale, {bool? listenToDeviceLocale = false}) => instance.setLocaleRaw(rawLocale, listenToDeviceLocale: listenToDeviceLocale);
|
|
static Future<AppLocale> useDeviceLocale() => instance.useDeviceLocale();
|
|
static Future<void> setPluralResolver({String? language, AppLocale? locale, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver}) => instance.setPluralResolver(
|
|
language: language,
|
|
locale: locale,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
|
|
// synchronous versions
|
|
static AppLocale setLocaleSync(AppLocale locale, {bool? listenToDeviceLocale = false}) => instance.setLocaleSync(locale, listenToDeviceLocale: listenToDeviceLocale);
|
|
static AppLocale setLocaleRawSync(String rawLocale, {bool? listenToDeviceLocale = false}) => instance.setLocaleRawSync(rawLocale, listenToDeviceLocale: listenToDeviceLocale);
|
|
static AppLocale useDeviceLocaleSync() => instance.useDeviceLocaleSync();
|
|
static void setPluralResolverSync({String? language, AppLocale? locale, PluralResolver? cardinalResolver, PluralResolver? ordinalResolver}) => instance.setPluralResolverSync(
|
|
language: language,
|
|
locale: locale,
|
|
cardinalResolver: cardinalResolver,
|
|
ordinalResolver: ordinalResolver,
|
|
);
|
|
}
|
|
|
|
/// Provides utility functions without any side effects.
|
|
class AppLocaleUtils extends BaseAppLocaleUtils<AppLocale, Translations> {
|
|
AppLocaleUtils._() : super(
|
|
baseLocale: AppLocale.en,
|
|
locales: AppLocale.values,
|
|
);
|
|
|
|
static final instance = AppLocaleUtils._();
|
|
|
|
// static aliases (checkout base methods for documentation)
|
|
static AppLocale parse(String rawLocale) => instance.parse(rawLocale);
|
|
static AppLocale parseLocaleParts({required String languageCode, String? scriptCode, String? countryCode}) => instance.parseLocaleParts(languageCode: languageCode, scriptCode: scriptCode, countryCode: countryCode);
|
|
static AppLocale findDeviceLocale() => instance.findDeviceLocale();
|
|
static List<Locale> get supportedLocales => instance.supportedLocales;
|
|
static List<String> get supportedLocalesRaw => instance.supportedLocalesRaw;
|
|
}
|