diff --git a/packages/firebase_ui_localizations/lib/src/all_languages.dart b/packages/firebase_ui_localizations/lib/src/all_languages.dart index d3da9fd1..342dca54 100644 --- a/packages/firebase_ui_localizations/lib/src/all_languages.dart +++ b/packages/firebase_ui_localizations/lib/src/all_languages.dart @@ -1,52 +1,121 @@ // Copyright 2023, the Chromium project authors. Please see the AUTHORS file // for details. All rights reserved. Use of this source code is governed by a // BSD-style license that can be found in the LICENSE file. -import "./default_localizations.dart"; +import 'dart:ui'; +import "./default_localizations.dart"; +import 'lang/ar.dart'; +import 'lang/de.dart'; +import 'lang/en.dart'; import 'lang/es.dart'; import 'lang/es_419.dart'; -import 'lang/ko.dart'; +import 'lang/fr.dart'; +import 'lang/he.dart'; +import 'lang/hi.dart'; import 'lang/hu.dart'; import 'lang/id.dart'; -import 'lang/pt.dart'; -import 'lang/he.dart'; -import 'lang/de.dart'; import 'lang/it.dart'; -import 'lang/zh.dart'; -import 'lang/zh_tw.dart'; -import 'lang/uk.dart'; -import 'lang/th.dart'; -import 'lang/ar.dart'; -import 'lang/tr.dart'; +import 'lang/ja.dart'; +import 'lang/ko.dart'; import 'lang/nl.dart'; import 'lang/pl.dart'; -import 'lang/en.dart'; -import 'lang/ja.dart'; -import 'lang/hi.dart'; +import 'lang/pt.dart'; import 'lang/ru.dart'; -import 'lang/fr.dart'; +import 'lang/th.dart'; +import 'lang/tr.dart'; +import 'lang/uk.dart'; +import 'lang/zh.dart'; +import 'lang/zh_tw.dart'; -final localizations = { - 'es': const EsLocalizations(), - 'es_419': const Es419Localizations(), - 'ko': const KoLocalizations(), - 'hu': const HuLocalizations(), - 'id': const IdLocalizations(), - 'pt': const PtLocalizations(), - 'he': const HeLocalizations(), - 'de': const DeLocalizations(), - 'it': const ItLocalizations(), - 'zh': const ZhLocalizations(), - 'zh_tw': const ZhTWLocalizations(), - 'uk': const UkLocalizations(), - 'th': const ThLocalizations(), - 'ar': const ArLocalizations(), - 'tr': const TrLocalizations(), - 'nl': const NlLocalizations(), - 'pl': const PlLocalizations(), - 'en': const EnLocalizations(), - 'ja': const JaLocalizations(), - 'hi': const HiLocalizations(), - 'ru': const RuLocalizations(), - 'fr': const FrLocalizations(), +final Set kSupportedLanguages = { + 'ar', // Arabic + 'de', // German + 'en', // English + 'es', // Spanish Castilian + 'fr', // French + 'he', // Hebrew + 'hi', // Hindi + 'hu', // Hungarian + 'id', // Indonesian + 'it', // Italian + 'ja', // Japanese + 'ko', // Korean + 'nl', // Dutch Flemish + 'pl', // Polish + 'pt', // Portuguese + 'ru', // Russian + 'th', // Thai + 'tr', // Turkish + 'uk', // Ukrainian + 'zh', // Chinese }; + +FirebaseUILocalizationLabels getFirebaseUITranslation( + Locale useLocale, [ + Locale? defaultLocale, +]) { + final Locale locale; + if (kSupportedLanguages.contains(useLocale.languageCode)) { + locale = useLocale; + } else { + locale = defaultLocale ?? useLocale; + } + + switch (locale.languageCode) { + case 'ar': + return const ArLocalizations(); + case 'de': + return const DeLocalizations(); + case 'en': + return const EnLocalizations(); + case 'es': + switch (locale.countryCode) { + case '419': + return const Es419Localizations(); + } + return const EsLocalizations(); + case 'fr': + return const FrLocalizations(); + case 'he': + return const HeLocalizations(); + case 'hi': + return const HiLocalizations(); + case 'hu': + return const HuLocalizations(); + case 'id': + return const IdLocalizations(); + case 'it': + return const ItLocalizations(); + case 'ja': + return const JaLocalizations(); + case 'ko': + return const KoLocalizations(); + case 'nl': + return const NlLocalizations(); + case 'pl': + return const PlLocalizations(); + case 'pt': + return const PtLocalizations(); + case 'ru': + return const RuLocalizations(); + case 'th': + return const ThLocalizations(); + case 'tr': + return const TrLocalizations(); + case 'uk': + return const UkLocalizations(); + case 'zh': + switch (locale.scriptCode) { + case 'Hant': + return const ZhTWLocalizations(); + } + switch (locale.countryCode) { + case 'HK': + case 'TW': + return const ZhTWLocalizations(); + } + return const ZhLocalizations(); + } + + throw ('getTranslationLabels() called for unsupported locale "$locale"'); +} diff --git a/packages/firebase_ui_localizations/lib/src/l10n.dart b/packages/firebase_ui_localizations/lib/src/l10n.dart index 338f8e91..4ab32a04 100644 --- a/packages/firebase_ui_localizations/lib/src/l10n.dart +++ b/packages/firebase_ui_localizations/lib/src/l10n.dart @@ -36,8 +36,8 @@ class FirebaseUILocalizations { return l; } - final defaultLocalizations = localizations[kDefaultLocale.languageCode]!; - return FirebaseUILocalizations(kDefaultLocale, defaultLocalizations); + final defaultTranslation = getFirebaseUITranslation(kDefaultLocale); + return FirebaseUILocalizations(kDefaultLocale, defaultTranslation); } /// Returns localization labels. @@ -74,32 +74,20 @@ class FirebaseUILocalizationDelegate ]); @override - bool isSupported(Locale locale) { - return _forceSupportAllLocales || - localizations.keys.contains(locale.languageCode); - } + bool isSupported(Locale locale) => + _forceSupportAllLocales || + kSupportedLanguages.contains(locale.languageCode); @override Future load(Locale locale) { - late FirebaseUILocalizationLabels labels; - - final key = locale.languageCode; - final fullKey = '${key}_${locale.countryCode.toString()}'; - - if (localizations.containsKey(fullKey)) { - labels = localizations[fullKey]!; - } else if (localizations.containsKey(key)) { - labels = localizations[key]!; - } else { - labels = localizations[kDefaultLocale.languageCode]!; - } + final translation = getFirebaseUITranslation(locale, kDefaultLocale); - final l = FirebaseUILocalizations( + final localizations = FirebaseUILocalizations( locale, - overrides ?? labels, + overrides ?? translation, ); - return SynchronousFuture(l); + return SynchronousFuture(localizations); } @override diff --git a/packages/firebase_ui_localizations/test/firebase_ui_localizations_test.dart b/packages/firebase_ui_localizations/test/firebase_ui_localizations_test.dart new file mode 100644 index 00000000..b89732fa --- /dev/null +++ b/packages/firebase_ui_localizations/test/firebase_ui_localizations_test.dart @@ -0,0 +1,180 @@ +// Copyright 2022, the Chromium project authors. Please see the AUTHORS file +// for details. All rights reserved. Use of this source code is governed by a +// BSD-style license that can be found in the LICENSE file. + +import 'package:firebase_ui_localizations/firebase_ui_localizations.dart'; +import 'package:firebase_ui_localizations/src/lang/zh_tw.dart'; +import 'package:flutter/material.dart'; +import 'package:flutter_localizations/flutter_localizations.dart'; +import 'package:flutter_test/flutter_test.dart'; + +const localeZh = Locale('zh'); +const localeTW = Locale('zh', 'TW'); + +Future main() async { + late FirebaseUILocalizationDelegate delegate; + + group( + 'FirebaseUILocalization loads the appropriate Chinese translation', + () { + localizeText(BuildContext context) { + final labels = FirebaseUILocalizations.labelsOf(context); + return labels.signInWithPhoneButtonText; + } + + setUp(() async { + delegate = const FirebaseUILocalizationDelegate(); + }); + + test( + 'Loads the correct translation with the language tag "${localeZh.toLanguageTag()}"', + () async { + final localizations = await delegate.load(localeZh); + expect(localizations.labels.signInWithPhoneButtonText, '使用电话号码登录'); + }, + ); + + testWidgets( + 'UI test for the "${localeZh.toLanguageTag()}" translation', + (tester) async { + await tester.pumpWidget( + TestMaterialApp( + locale: localeZh, + localizeText: localizeText, + ), + ); + expect(find.text('使用电话号码登录'), findsOneWidget); + }, + ); + + test( + 'Loads the correct translation with the language tag "${localeTW.toLanguageTag()}"', + () async { + final localizations = await delegate.load(localeTW); + expect(localizations.labels.signInWithPhoneButtonText, '使用電話號碼登入'); + }, + ); + + testWidgets( + 'UI test for the "${localeTW.toLanguageTag()}" translation', + (tester) async { + await tester.pumpWidget( + TestMaterialApp( + locale: localeTW, + localizeText: localizeText, + ), + ); + expect(find.text('使用電話號碼登入'), findsOneWidget); + }, + ); + }, + ); + + group( + 'Localization override', + () { + localizeText(BuildContext context) { + return FirebaseUILocalizations.labelsOf(context).verifyEmailTitle; + } + + test( + 'Overrides the DefaultLocalizations', + () async { + final localizations = await const FirebaseUILocalizationDelegate( + DefaultLocalizationsOverrides(), + ).load(localeTW); + expect(localizations.labels.verifyEmailTitle, 'Overwritten'); + }, + ); + + testWidgets( + 'UI test for the default translation override', + (tester) async { + await tester.pumpWidget( + TestMaterialApp( + locale: localeZh, + localizationsOverride: const FirebaseUILocalizationDelegate( + DefaultLocalizationsOverrides(), + ), + localizeText: localizeText, + ), + ); + expect(find.text('Overwritten'), findsOneWidget); + }, + ); + + test( + 'Overrides the DefaultLocalizations', + () async { + final localizations = await const FirebaseUILocalizationDelegate( + ZhTWLocalizationsOverrides(), + ).load(localeTW); + expect(localizations.labels.verifyEmailTitle, '覆寫標題'); + }, + ); + + testWidgets( + 'UI test for the "${localeTW.toLanguageTag()}" translation override', + (tester) async { + await tester.pumpWidget( + TestMaterialApp( + locale: localeTW, + localizationsOverride: const FirebaseUILocalizationDelegate( + ZhTWLocalizationsOverrides(), + ), + localizeText: localizeText, + ), + ); + expect(find.text('覆寫標題'), findsOneWidget); + }, + ); + }, + ); +} + +class DefaultLocalizationsOverrides extends DefaultLocalizations { + const DefaultLocalizationsOverrides(); + + @override + String get verifyEmailTitle => 'Overwritten'; +} + +class ZhTWLocalizationsOverrides extends ZhTWLocalizations { + const ZhTWLocalizationsOverrides(); + + @override + String get verifyEmailTitle => '覆寫標題'; +} + +class TestMaterialApp extends StatelessWidget { + final Locale locale; + final LocalizationsDelegate? localizationsOverride; + final String Function(BuildContext context) localizeText; + + const TestMaterialApp({ + super.key, + required this.locale, + this.localizationsOverride, + required this.localizeText, + }); + + @override + Widget build(BuildContext context) { + return MaterialApp( + supportedLocales: const [localeZh, localeTW], + locale: locale, + localizationsDelegates: [ + GlobalMaterialLocalizations.delegate, + GlobalCupertinoLocalizations.delegate, + localizationsOverride == null + ? FirebaseUILocalizations.delegate + : localizationsOverride!, + ], + home: Builder( + builder: (context) => Text( + localizeText.call(context), + ), + ), + ); + } +}