Skip to content

Commit

Permalink
Add 'supportedLocales' property
Browse files Browse the repository at this point in the history
  • Loading branch information
Pavel-Sulimau committed Mar 2, 2021
1 parent 51e2bfc commit 1c38185
Show file tree
Hide file tree
Showing 7 changed files with 178 additions and 54 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 2.3.0

- Add `supportedLocales` property that can be used to fill `MaterialApp`'s `supportedLocales` argument.

## 2.2.1

- Fix compilation error occurring when non-standard name (not 'strings.i18n.json') is used for json files.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ Lightweight i18n solution. Use JSON files to create typesafe translations.

```yaml
dependencies:
fast_i18n: ^2.2.1
fast_i18n: ^2.3.0

dev_dependencies:
build_runner: any
Expand Down
15 changes: 14 additions & 1 deletion example/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

```yaml
dependencies:
fast_i18n: ^2.2.1
fast_i18n: ^2.3.0

dev_dependencies:
build_runner: any
Expand Down Expand Up @@ -67,6 +67,19 @@ void initState() {
}
```

## Step 4a: Override 'supportedLocales'

```dart
MaterialApp(
localizationsDelegates: const [
GlobalMaterialLocalizations.delegate,
GlobalWidgetsLocalizations.delegate,
GlobalCupertinoLocalizations.delegate,
],
supportedLocales: LocaleSettings.supportedLocales, // <---
)
```

## Step 4b: iOS-only

```
Expand Down
59 changes: 44 additions & 15 deletions lib/fast_i18n.dart
Original file line number Diff line number Diff line change
@@ -1,41 +1,70 @@
library fast_i18n;

import 'dart:io';
import 'dart:ui';

import 'package:fast_i18n/utils.dart';

class FastI18n {
static const _localePartsDelimiter = '-';

/// returns the locale string used by the device
static String getDeviceLocale() {
return Platform.localeName;
}
/// Returns the locale string used by the device.
static String getDeviceLocale() => Platform.localeName;

/// returns the candidate (or part of it) if it is supported
/// fallback to base locale
/// Returns the candidate (or part of it) if it is supported.
/// Fallbacks to base locale.
static String selectLocale(String candidate, List<String> supported, String baseLocale) {
// normalize
candidate = Utils.normalize(candidate);

// 1st try: match exactly
String selected = supported.firstWhere((element) => element == candidate,
orElse: () => null);
String selected = supported.firstWhere((element) => element == candidate, orElse: () => null);
if (selected != null) return selected;

// 2nd try: match the first part (language)
List<String> deviceLocaleParts = candidate.split('-');
selected = supported.firstWhere(
(element) => element == deviceLocaleParts.first,
orElse: () => null);
List<String> deviceLocaleParts = candidate.split(_localePartsDelimiter);
selected =
supported.firstWhere((element) => element == deviceLocaleParts.first, orElse: () => null);
if (selected != null) return selected;

// 3rd try: match the second part (region)
selected = supported.firstWhere(
(element) => element == deviceLocaleParts.last,
orElse: () => null);
selected =
supported.firstWhere((element) => element == deviceLocaleParts.last, orElse: () => null);
if (selected != null) return selected;

// fallback: default locale
return baseLocale;
}

/// Converts the passed locales from [String] to [Locale].
/// Puts the [baseLocale] into the the beginning of the list.
static List<Locale> convertToLocales(List<String> locales, String baseLocale) {
final rawSupportedLocales = [
baseLocale,
...locales.where((locale) => locale != baseLocale),
];

final supportedLocales = rawSupportedLocales.map((rawLocale) {
if (rawLocale.contains(_localePartsDelimiter)) {
final localeParts =
rawLocale.split(_localePartsDelimiter).where((part) => part.isNotEmpty).toList();
if (localeParts.length == 2) {
return Locale.fromSubtags(languageCode: localeParts[0], countryCode: localeParts[1]);
} else if (localeParts.length == 3) {
return Locale.fromSubtags(
languageCode: localeParts[0],
scriptCode: localeParts[1],
countryCode: localeParts[2],
);
} else {
throw Exception(
"The locale '$rawLocale' is not in a supported format. Examples of the supported formats: 'en', 'en-US', 'zh-Hans-CN'.");
}
} else {
return Locale.fromSubtags(languageCode: rawLocale);
}
}).toList();

return supportedLocales;
}
}
90 changes: 54 additions & 36 deletions lib/src/generator.dart
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List<I18nData> allL
buffer.writeln();
buffer.writeln('/// Method A: Simple');
buffer.writeln('///');
buffer.writeln('/// Widgets using this method will not be updated when locale changes during runtime.');
buffer.writeln(
'/// Widgets using this method will not be updated when locale changes during runtime.');
buffer.writeln('/// Translation happens during initialization of the widget (call of t).');
buffer.writeln('///');
buffer.writeln('/// Usage:');
Expand All @@ -89,7 +90,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List<I18nData> allL
buffer.writeln('/// Method B: Advanced');
buffer.writeln('///');
buffer.writeln('/// All widgets using this method will trigger a rebuild when locale changes.');
buffer.writeln('/// Use this if you have e.g. a settings page where the user can select the locale during runtime.');
buffer.writeln(
'/// Use this if you have e.g. a settings page where the user can select the locale during runtime.');
buffer.writeln('///');
buffer.writeln('/// Step 1:');
buffer.writeln('/// wrap your App with');
Expand All @@ -104,7 +106,8 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List<I18nData> allL
buffer.writeln('\t$translationsClass._(); // no constructor');
buffer.writeln();
buffer.writeln('\tstatic $baseClassName of(BuildContext context) {');
buffer.writeln('\t\treturn context.dependOnInheritedWidgetOfExactType<$inheritedClass>().translations;');
buffer.writeln(
'\t\treturn context.dependOnInheritedWidgetOfExactType<$inheritedClass>().translations;');
buffer.writeln('\t}');
buffer.writeln('}');

Expand All @@ -114,18 +117,19 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List<I18nData> allL
buffer.writeln('\t$settingsClass._(); // no constructor');

buffer.writeln();
buffer.writeln('\t/// Use locale of the device, fallbacks to base locale.');
buffer.writeln('\t/// Uses locale of the device, fallbacks to base locale.');
buffer.writeln('\t/// Returns the locale which has been set.');
buffer.writeln('\tstatic String useDeviceLocale() {');
buffer.writeln('\t\tString deviceLocale = FastI18n.getDeviceLocale();');
buffer.writeln('\t\treturn setLocale(deviceLocale);');
buffer.writeln('\t}');

buffer.writeln();
buffer.writeln('\t/// Set locale, fallbacks to base locale.');
buffer.writeln('\t/// Sets locale, fallbacks to base locale.');
buffer.writeln('\t/// Returns the locale which has been set.');
buffer.writeln('\tstatic String setLocale(String locale) {');
buffer.writeln('\t\t$localeVar = FastI18n.selectLocale(locale, $mapVar.keys.toList(), $baseLocaleVar);');
buffer.writeln(
'\t\t$localeVar = FastI18n.selectLocale(locale, $mapVar.keys.toList(), $baseLocaleVar);');
buffer.writeln('\t\t$translateVar = $mapVar[$localeVar];');
buffer.writeln();
buffer.writeln('\t\tif ($translationProviderKey.currentState != null) {');
Expand All @@ -136,36 +140,45 @@ void _generateHeader(StringBuffer buffer, I18nConfig config, List<I18nData> allL
buffer.writeln('\t}');

buffer.writeln();
buffer.writeln('\t/// Get current locale.');
buffer.writeln('\t/// Gets current locale.');
buffer.writeln('\tstatic String get currentLocale {');
buffer.writeln('\t\treturn $localeVar;');
buffer.writeln('\t}');

buffer.writeln();
buffer.writeln('\t/// Get base locale.');
buffer.writeln('\t/// Gets base locale.');
buffer.writeln('\tstatic String get baseLocale {');
buffer.writeln('\t\treturn $baseLocaleVar;');
buffer.writeln('\t}');

buffer.writeln();
buffer.writeln('\t/// Get supported locales.');
buffer.writeln('\t/// Gets supported locales.');
buffer.writeln('\tstatic List<String> get locales {');
buffer.writeln('\t\treturn $mapVar.keys.toList();');
buffer.writeln('\t}');

buffer.writeln();
buffer.writeln('\t/// Get supported locales with base locale sorted first.');
buffer.writeln('\tstatic List<Locale> get supportedLocales {');
buffer.writeln('\t\treturn FastI18n.convertToLocales($mapVar.keys.toList(), $baseLocaleVar);');
buffer.writeln('\t}');

buffer.writeln('}');

// TranslationProvider
buffer.writeln();
buffer.writeln('GlobalKey<$translationProviderStateClass> $translationProviderKey = new GlobalKey<$translationProviderStateClass>();');
buffer.writeln(
'GlobalKey<$translationProviderStateClass> $translationProviderKey = new GlobalKey<$translationProviderStateClass>();');
buffer.writeln();
buffer.writeln('class $translationProviderClass extends StatefulWidget {');
buffer.writeln('\t$translationProviderClass({@required this.child}) : super(key: $translationProviderKey);');
buffer.writeln(
'\t$translationProviderClass({@required this.child}) : super(key: $translationProviderKey);');
buffer.writeln();
buffer.writeln('\tfinal Widget child;');
buffer.writeln();
buffer.writeln('\t@override');
buffer.writeln('\t$translationProviderStateClass createState() => $translationProviderStateClass();');
buffer.writeln(
'\t$translationProviderStateClass createState() => $translationProviderStateClass();');
buffer.writeln('}');

buffer.writeln();
Expand Down Expand Up @@ -221,7 +234,7 @@ void _generateLocale(StringBuffer buffer, I18nConfig config, I18nData localeData
buffer,
queue,
task.className,
task.members
task.members,
);
} while (queue.isNotEmpty);
}
Expand All @@ -235,11 +248,9 @@ void _generateClass(
StringBuffer buffer,
Queue<ClassTask> queue,
String className,
Map<String, Value> currMembers
Map<String, Value> currMembers,
) {
String finalClassName = base
? className
: className + locale.capitalize().replaceAll('-', '');
String finalClassName = base ? className : className + locale.capitalize().replaceAll('-', '');

buffer.writeln();

Expand Down Expand Up @@ -277,15 +288,13 @@ void _generateClass(
// inline map
String type = value.plainStrings ? 'String' : 'dynamic';
buffer.write('Map<String, $type> get $key => ');
_generateMap(
base, locale, buffer, queue, childClassName, value.entries, 0);
_generateMap(base, locale, buffer, queue, childClassName, value.entries, 0);
} else {
// generate a class later on
queue.add(ClassTask(childClassName, value.entries));

String finalChildClassName = base
? childClassName
: childClassName + locale.capitalize().replaceAll('-', '');
String finalChildClassName =
base ? childClassName : childClassName + locale.capitalize().replaceAll('-', '');

buffer.writeln('$finalChildClassName get $key => $finalChildClassName._instance;');
}
Expand All @@ -298,13 +307,14 @@ void _generateClass(
/// generates a map of ONE locale
/// similar to _generateClass but anonymous and accessible via key
void _generateMap(
bool base,
String locale,
StringBuffer buffer,
Queue<ClassTask> queue,
String className,
Map<String, Value> currMembers,
int depth) {
bool base,
String locale,
StringBuffer buffer,
Queue<ClassTask> queue,
String className,
Map<String, Value> currMembers,
int depth,
) {
buffer.writeln('{');

currMembers.forEach((key, value) {
Expand All @@ -328,9 +338,8 @@ void _generateMap(
// generate a class later on
queue.add(ClassTask(childClassName, value.entries));

String finalChildClassName = base
? childClassName
: childClassName + locale.capitalize().replaceAll('-', '');
String finalChildClassName =
base ? childClassName : childClassName + locale.capitalize().replaceAll('-', '');

buffer.writeln('\'$key\': $finalChildClassName._instance,');
}
Expand All @@ -349,8 +358,15 @@ void _generateMap(
}

/// generates a list
void _generateList(bool base, String locale, StringBuffer buffer,
Queue<ClassTask> queue, String className, List<Value> currList, int depth) {
void _generateList(
bool base,
String locale,
StringBuffer buffer,
Queue<ClassTask> queue,
String className,
List<Value> currList,
int depth,
) {
buffer.writeln('[');

for (int i = 0; i < currList.length; i++) {
Expand Down Expand Up @@ -421,8 +437,10 @@ extension on String {

String toCase(String caseName) {
switch (caseName) {
case 'snake': return snakeCase;
case 'camel': return camelCase;
case 'snake':
return snakeCase;
case 'camel':
return camelCase;
default:
return this;
}
Expand Down
2 changes: 1 addition & 1 deletion pubspec.yaml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
name: fast_i18n
description: Lightweight i18n solution. Use JSON files to create typesafe translations.
version: 2.2.1
version: 2.3.0
homepage: https://github.com/Tienisto/flutter-fast-i18n

environment:
Expand Down
Loading

0 comments on commit 1c38185

Please sign in to comment.