fix(mobile): fallback localizations for unsupported locales

This commit is contained in:
Santo Shakil
2026-10-15 02:18:14 +06:00
parent a466c60eaf
commit 296d5a494f
5 changed files with 48 additions and 4 deletions
+2 -1
View File
@@ -40,6 +40,7 @@ import 'package:immich_mobile/theme/theme_data.dart';
import 'package:immich_mobile/utils/bootstrap.dart';
import 'package:immich_mobile/utils/cache/widgets_binding.dart';
import 'package:immich_mobile/utils/debug_print.dart';
import 'package:immich_mobile/utils/fallback_localizations.dart';
import 'package:immich_mobile/utils/licenses.dart';
import 'package:immich_mobile/utils/migration.dart';
import 'package:immich_mobile/wm_executor.dart';
@@ -283,7 +284,7 @@ class ImmichAppState extends ConsumerState<ImmichApp> with WidgetsBindingObserve
title: 'Immich',
debugShowCheckedModeBanner: true,
scaffoldMessengerKey: scaffoldMessengerKey,
localizationsDelegates: context.localizationDelegates,
localizationsDelegates: [...context.localizationDelegates, ...fallbackLocalizationDelegates],
supportedLocales: context.supportedLocales,
locale: context.locale,
themeMode: ref.watch(appConfigProvider.select((config) => config.theme.mode)),
@@ -24,6 +24,7 @@ import 'package:immich_mobile/providers/websocket.provider.dart';
import 'package:immich_mobile/routing/router.dart';
import 'package:immich_mobile/theme/color_scheme.dart';
import 'package:immich_mobile/theme/theme_data.dart';
import 'package:immich_mobile/utils/fallback_localizations.dart';
import 'package:immich_mobile/widgets/common/immich_logo.dart';
import 'package:immich_mobile/widgets/common/immich_title_text.dart';
import 'package:logging/logging.dart';
@@ -49,7 +50,7 @@ class BootstrapErrorWidget extends StatelessWidget {
builder: (lCtx) => MaterialApp(
title: 'Immich',
debugShowCheckedModeBanner: true,
localizationsDelegates: lCtx.localizationDelegates,
localizationsDelegates: [...lCtx.localizationDelegates, ...fallbackLocalizationDelegates],
supportedLocales: lCtx.supportedLocales,
locale: lCtx.locale,
themeMode: ThemeMode.system,
@@ -0,0 +1,23 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
// Flutter ships no Material or Cupertino strings for some of our languages, use English for those
const List<LocalizationsDelegate> fallbackLocalizationDelegates = [
_FallbackLocalizationsDelegate<MaterialLocalizations>(DefaultMaterialLocalizations.delegate),
_FallbackLocalizationsDelegate<CupertinoLocalizations>(DefaultCupertinoLocalizations.delegate),
];
class _FallbackLocalizationsDelegate<T> extends LocalizationsDelegate<T> {
final LocalizationsDelegate<T> delegate;
const _FallbackLocalizationsDelegate(this.delegate);
@override
bool isSupported(Locale locale) => true;
@override
Future<T> load(Locale locale) => delegate.load(locale);
@override
bool shouldReload(_FallbackLocalizationsDelegate<T> old) => false;
}
@@ -0,0 +1,17 @@
import 'package:flutter/cupertino.dart';
import 'package:flutter/material.dart';
import 'package:flutter_test/flutter_test.dart';
import '../widget_tester_extensions.dart';
void main() {
testWidgets('Material and Cupertino widgets work in languages Flutter has no translations for', (tester) async {
await tester.pumpConsumerWidget(
const Column(children: [BackButton(), CupertinoSearchTextField()]),
locale: const Locale('mi'),
);
expect(find.byTooltip('Back'), findsOneWidget);
expect(find.text('Search'), findsOneWidget);
});
}
+4 -2
View File
@@ -4,6 +4,7 @@ import 'package:flutter_test/flutter_test.dart';
import 'package:hooks_riverpod/hooks_riverpod.dart';
import 'package:immich_mobile/constants/locales.dart';
import 'package:immich_mobile/generated/codegen_loader.g.dart';
import 'package:immich_mobile/utils/fallback_localizations.dart';
extension PumpConsumerWidget on WidgetTester {
/// Wraps the provided [widget] with a localized Material app such that it
@@ -20,12 +21,13 @@ extension PumpConsumerWidget on WidgetTester {
Duration? duration,
EnginePhase phase = EnginePhase.sendSemanticsUpdate,
List<Override> overrides = const [],
Locale? locale,
}) async {
await pumpWidget(
EasyLocalization(
supportedLocales: locales.values.toList(),
path: translationsPath,
startLocale: locales.values.first,
startLocale: locale ?? locales.values.first,
fallbackLocale: locales.values.first,
saveLocale: false,
useFallbackTranslations: true,
@@ -35,7 +37,7 @@ extension PumpConsumerWidget on WidgetTester {
child: Builder(
builder: (context) => MaterialApp(
debugShowCheckedModeBanner: false,
localizationsDelegates: context.localizationDelegates,
localizationsDelegates: [...context.localizationDelegates, ...fallbackLocalizationDelegates],
supportedLocales: context.supportedLocales,
locale: context.locale,
home: Material(child: widget),