Skip to content

Commit

Permalink
Add useFallbackTranslationsForEmptyResources parameter to use fallbac…
Browse files Browse the repository at this point in the history
…k if translation lookup returns an empty string (#663)

* Add useFallbackTranslationsForEmptyResources parameter

* Remove formatting changes in readme. Increase the version in pubspec
  • Loading branch information
VladShturma authored Apr 22, 2024
1 parent 070d895 commit 39d091e
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 11 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
# Changelog

### [3.0.6]

- add 'useFallbackTranslationsForEmptyResources' to be able to use fallback locales for empty resources.

### [3.0.5]

- add 'extraAssetLoaders' to add more assets loaders if it is needed, for example, if you want to add packages localizations to your project.
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -146,6 +146,7 @@ class MyApp extends StatelessWidget {
| startLocale | false | | Overrides device locale. |
| saveLocale | false | `true` | Save locale in device storage. |
| useFallbackTranslations | false | `false` | If a localization key is not found in the locale file, try to use the fallbackLocale file. |
| useFallbackTranslationsForEmptyResources | false | `false` | If translation is empty in the locale file, try to use the fallbackLocale file. Does not take effect if `useFallbackTranslations` is false. |
| useOnlyLangCode | false | `false` | Trigger for using only language code for reading localization files.</br></br>Example:</br>`en.json //useOnlyLangCode: true`</br>`en-US.json //useOnlyLangCode: false` |
| errorWidget | false | `FutureErrorWidget()` | Shows a custom error widget when an error occurs. |

Expand Down
30 changes: 25 additions & 5 deletions lib/src/easy_localization_app.dart
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,15 @@ class EasyLocalization extends StatefulWidget {
/// ```
final bool useFallbackTranslations;

/// If a localization key is empty in the locale file, try to use the fallbackLocale file.
/// Does not take effect if [useFallbackTranslations] is false.
/// @Default value false
/// Example:
/// ```
/// useFallbackTranslationsForEmptyResources: true
/// ```
final bool useFallbackTranslationsForEmptyResources;

/// Path to your folder with localization files.
/// Example:
/// ```dart
Expand Down Expand Up @@ -107,6 +116,7 @@ class EasyLocalization extends StatefulWidget {
this.startLocale,
this.useOnlyLangCode = false,
this.useFallbackTranslations = false,
this.useFallbackTranslationsForEmptyResources = false,
this.assetLoader = const RootBundleAssetLoader(),
this.extraAssetLoaders,
this.saveLocale = true,
Expand Down Expand Up @@ -186,6 +196,8 @@ class _EasyLocalizationState extends State<EasyLocalization> {
delegate: _EasyLocalizationDelegate(
localizationController: localizationController,
supportedLocales: widget.supportedLocales,
useFallbackTranslationsForEmptyResources:
widget.useFallbackTranslationsForEmptyResources,
),
);
}
Expand Down Expand Up @@ -265,12 +277,16 @@ class _EasyLocalizationProvider extends InheritedWidget {
class _EasyLocalizationDelegate extends LocalizationsDelegate<Localization> {
final List<Locale>? supportedLocales;
final EasyLocalizationController? localizationController;
final bool useFallbackTranslationsForEmptyResources;

/// * use only the lang code to generate i18n file path like en.json or ar.json
// final bool useOnlyLangCode;

_EasyLocalizationDelegate(
{this.localizationController, this.supportedLocales}) {
_EasyLocalizationDelegate({
required this.useFallbackTranslationsForEmptyResources,
this.localizationController,
this.supportedLocales,
}) {
EasyLocalization.logger.debug('Init Localization Delegate');
}

Expand All @@ -284,9 +300,13 @@ class _EasyLocalizationDelegate extends LocalizationsDelegate<Localization> {
await localizationController!.loadTranslations();
}

Localization.load(value,
translations: localizationController!.translations,
fallbackTranslations: localizationController!.fallbackTranslations);
Localization.load(
value,
translations: localizationController!.translations,
fallbackTranslations: localizationController!.fallbackTranslations,
useFallbackTranslationsForEmptyResources:
useFallbackTranslationsForEmptyResources,
);
return Future.value(Localization.instance);
}

Expand Down
13 changes: 10 additions & 3 deletions lib/src/localization.dart
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@ class Localization {
'capitalize': (String? val) => '${val![0].toUpperCase()}${val.substring(1)}'
};

bool _useFallbackTranslationsForEmptyResources = false;

Localization();

static Localization? _instance;
Expand All @@ -30,10 +32,13 @@ class Localization {
Locale locale, {
Translations? translations,
Translations? fallbackTranslations,
bool useFallbackTranslationsForEmptyResources = false,
}) {
instance._locale = locale;
instance._translations = translations;
instance._fallbackTranslations = fallbackTranslations;
instance._useFallbackTranslationsForEmptyResources =
useFallbackTranslationsForEmptyResources;
return translations == null ? false : true;
}

Expand Down Expand Up @@ -190,15 +195,17 @@ class Localization {

String _resolve(String key, {bool logging = true, bool fallback = true}) {
var resource = _translations?.get(key);
if (resource == null) {
if (resource == null ||
(_useFallbackTranslationsForEmptyResources && resource.isEmpty)) {
if (logging) {
EasyLocalization.logger.warning('Localization key [$key] not found');
}
if (_fallbackTranslations == null || !fallback) {
return key;
} else {
resource = _fallbackTranslations?.get(key);
if (resource == null) {
if (resource == null ||
(_useFallbackTranslationsForEmptyResources && resource.isEmpty)) {
if (logging) {
EasyLocalization.logger
.warning('Fallback localization key [$key] not found');
Expand All @@ -210,7 +217,7 @@ class Localization {
return resource;
}

bool exists(String key){
bool exists(String key) {
return _translations?.get(key) != null;
}
}
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ homepage: https://github.com/aissat/easy_localization
issue_tracker: https://github.com/aissat/easy_localization/issues
# publish_to: none

version: 3.0.5
version: 3.0.6

environment:
sdk: '>=2.12.0 <4.0.0'
Expand Down
132 changes: 130 additions & 2 deletions test/easy_localization_test.dart
Original file line number Diff line number Diff line change
Expand Up @@ -342,12 +342,18 @@ void main() {
contains('Localization key [test_missing_fallback] not found'));
}));

test('uses empty translation, not using fallback', overridePrint(() {
printLog = [];
expect(Localization.instance.tr('test_empty_fallback'), '');
}));

test('returns resource and replaces argument', () {
expect(
Localization.instance.tr('test_replace_one', args: ['one']),
'test replace one',
);
});

test('returns resource and replaces argument in any nest level', () {
expect(
Localization.instance
Expand Down Expand Up @@ -411,10 +417,59 @@ void main() {
});
});

group('tr useFallbackTranslationsForEmptyResources', () {
var r = EasyLocalizationController(
forceLocale: const Locale('en'),
supportedLocales: const [Locale('en'), Locale('fb')],
fallbackLocale: const Locale('fb'),
path: 'path',
useOnlyLangCode: true,
useFallbackTranslations: true,
onLoadError: (FlutterError e) {
log(e.toString());
},
saveLocale: false,
assetLoader: const JsonAssetLoader());

setUpAll(() async {
await r.loadTranslations();
Localization.load(
const Locale('en'),
translations: r.translations,
fallbackTranslations: r.fallbackTranslations,
useFallbackTranslationsForEmptyResources: true,
);
});

test('uses fallback translations for empty resource', overridePrint(() {
printLog = [];
expect(Localization.instance.tr('test_empty_fallback'), 'fallback!');
}));

test('reports empty resource with fallback', overridePrint(() {
printLog = [];
expect(Localization.instance.tr('test_empty_fallback'), 'fallback!');
expect(printLog.first,
contains('Localization key [test_empty_fallback] not found'));
}));

test('reports empty resource', overridePrint(() {
printLog = [];
expect(Localization.instance.tr('test_empty'), 'test_empty');
final logIterator = printLog.iterator;
logIterator.moveNext();
expect(logIterator.current,
contains('Localization key [test_empty] not found'));
logIterator.moveNext();
expect(logIterator.current,
contains('Fallback localization key [test_empty] not found'));
}));
});

group('plural', () {
var r = EasyLocalizationController(
forceLocale: const Locale('fb'),
supportedLocales: [const Locale('fb')],
forceLocale: const Locale('en'),
supportedLocales: const [Locale('en'), Locale('fb')],
fallbackLocale: const Locale('fb'),
path: 'path',
useOnlyLangCode: true,
Expand Down Expand Up @@ -469,6 +524,16 @@ void main() {
expect(printLog, isEmpty);
}));

test('two as fallback and fallback translations priority',
overridePrint(() {
printLog = [];
expect(
Localization.instance.plural('test_empty_fallback_plurals', 2),
'',
);
expect(printLog, isEmpty);
}));

test('with number format', () {
expect(
Localization.instance
Expand Down Expand Up @@ -524,6 +589,69 @@ void main() {
});
});

group('plural useFallbackTranslationsForEmptyResources', () {
var r = EasyLocalizationController(
forceLocale: const Locale('en'),
supportedLocales: const [Locale('en'), Locale('fb')],
fallbackLocale: const Locale('fb'),
path: 'path',
useOnlyLangCode: true,
useFallbackTranslations: true,
onLoadError: (FlutterError e) {
log(e.toString());
},
saveLocale: false,
assetLoader: const JsonAssetLoader());

setUpAll(() async {
await r.loadTranslations();
Localization.load(
const Locale('fb'),
translations: r.translations,
fallbackTranslations: r.fallbackTranslations,
useFallbackTranslationsForEmptyResources: true,
);
});

test('two as fallback for empty resource and fallback translations priority',
overridePrint(() {
printLog = [];
expect(
Localization.instance.plural('test_empty_fallback_plurals', 2),
'fallback two',
);
expect(printLog, isEmpty);
}));

test('reports empty plural resource with fallback',
overridePrint(() {
printLog = [];
expect(
Localization.instance.plural('test_empty_fallback_plurals', -1),
'fallback other',
);
expect(
printLog.first,
contains(
'Localization key [test_empty_fallback_plurals.other] not found'));
}));

test('reports empty plural resource', overridePrint(() {
printLog = [];
expect(
Localization.instance.plural('test_empty_plurals', -1),
'test_empty_plurals.other',
);
final logIterator = printLog.iterator;
logIterator.moveNext();
expect(logIterator.current,
contains('Localization key [test_empty_plurals.other] not found'));
logIterator.moveNext();
expect(logIterator.current,
contains('Fallback localization key [test_empty_plurals.other] not found'));
}));
});

group('extensions', () {
// setUpAll(() async {
// await Localization.load(Locale('en'),
Expand Down
36 changes: 36 additions & 0 deletions test/utils/test_asset_loaders.dart
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ class JsonAssetLoader extends AssetLoader {
Future<Map<String, dynamic>> load(String fullPath, Locale locale) {
return Future.value({
'test': 'test',
'test_empty': '',
'test_replace_one': 'test replace {}',
'test_replace_two': 'test replace {} {}',
'test_replace_named': 'test named replace {arg1} {arg2}',
Expand Down Expand Up @@ -87,6 +88,7 @@ class JsonAssetLoader extends AssetLoader {
'path': fullPath,
'test_missing_fallback':
(locale.languageCode == 'fb' ? 'fallback!' : null),
'test_empty_fallback': (locale.languageCode == 'fb' ? 'fallback!' : ''),
'test_fallback_plurals': (locale.languageCode == 'fb'
? {
'zero': 'fallback zero',
Expand All @@ -100,6 +102,40 @@ class JsonAssetLoader extends AssetLoader {
'one': '{} second',
'other': '{} seconds',
}),
'test_empty_fallback_plurals': (locale.languageCode == 'fb'
? {
'zero': 'fallback zero',
'one': 'fallback one',
'two': 'fallback two',
'few': 'fallback few',
'many': 'fallback many',
'other': 'fallback other',
}
: {
'zero': '',
'one': '',
'two': '',
'few': '',
'many': '',
'other': '',
}),
'test_empty_plurals': (locale.languageCode == 'fb'
? {
'zero': '',
'one': '',
'two': '',
'few': '',
'many': '',
'other': '',
}
: {
'zero': '',
'one': '',
'two': '',
'few': '',
'many': '',
'other': '',
})
});
}
}
Expand Down

0 comments on commit 39d091e

Please sign in to comment.