Skip to content

Commit

Permalink
Merge pull request #24 from tverra/translated-name
Browse files Browse the repository at this point in the history
Parse and translate country
  • Loading branch information
Daniel-Ioannou authored Apr 4, 2021
2 parents 174b685 + 247fa18 commit 9b66b04
Show file tree
Hide file tree
Showing 3 changed files with 227 additions and 0 deletions.
1 change: 1 addition & 0 deletions lib/country_picker.dart
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import 'src/country_list_theme_data.dart';
export 'src/country.dart';
export 'src/country_list_theme_data.dart';
export 'src/country_localizations.dart';
export 'src/country_parser.dart';

/// Shows a bottom sheet containing a list of countries to select one.
///
Expand Down
16 changes: 16 additions & 0 deletions lib/src/country.dart
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
import 'package:country_picker/src/country_parser.dart';
import 'package:flutter/material.dart';

import 'country_localizations.dart';

///The country Model that has all the country
Expand Down Expand Up @@ -33,6 +36,11 @@ class Country {
'This feature was deprecated after v1.0.6.')
String get displayNameNoE164Cc => displayNameNoCountryCode;

String? getTranslatedName(BuildContext context) {
return CountryLocalizations.of(context)
?.countryName(countryCode: countryCode);
}

Country({
required this.phoneCode,
required this.countryCode,
Expand Down Expand Up @@ -60,6 +68,14 @@ class Country {
displayNameNoCountryCode = json['display_name_no_e164_cc'],
e164Key = json['e164_key'];

static Country parse(String country) {
return CountryParser.parse(country);
}

static Country? tryParse(String country) {
return CountryParser.tryParse(country);
}

Map<String, dynamic> toJson() {
final Map<String, dynamic> data = <String, dynamic>{};
data['e164_cc'] = phoneCode;
Expand Down
210 changes: 210 additions & 0 deletions lib/src/country_parser.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,210 @@
import 'package:country_picker/country_picker.dart';
import 'package:flutter/material.dart';

import 'country.dart';
import 'res/country_codes.dart';
import 'res/strings/cn.dart';
import 'res/strings/en.dart';
import 'res/strings/es.dart';
import 'res/strings/gr.dart';
import 'res/strings/nb.dart';
import 'res/strings/nn.dart';
import 'res/strings/pt.dart';
import 'res/strings/tw.dart';

/// Used to parse simple string representations of countries, commonly used in
/// databases and other forms of storage, to a Country object.
class CountryParser {
/// Returns a single country if [country] matches a country code or name.
///
/// Throws an [ArgumentError] if no matching element is found.
static Country parse(String country) {
return tryParseCountryCode(country) ?? parseCountryName(country);
}

/// Returns a single country if [country] matches a country code or name.
///
/// returns null if no matching element is found.
static Country? tryParse(String country) {
return tryParseCountryCode(country) ?? tryParseCountryName(country);
}

/// Returns a single country if it matches the given [countryCode] (iso2_cc).
///
/// Throws a [StateError] if no matching element is found.
static Country parseCountryCode(String countryCode) {
return _getFromCode(countryCode.toUpperCase());
}

/// Returns a single country that matches the given [countryCode] (iso2_cc).
///
/// Returns null if no matching element is found.
static Country? tryParseCountryCode(String countryCode) {
try {
return parseCountryCode(countryCode);
} catch (_) {
return null;
}
}

/// Returns a single country if it matches the given [countryName].
///
/// Uses the application [context] to determine what language is used in the
/// app, if provided, and checks the country names for this locale if any.
/// If no match is found and no [locales] are given, the default language is
/// checked (english), followed by the rest of the available translations. If
/// any [locales] are given, only those supported languages are used, in
/// addition to the [context] language.
///
/// Throws an [ArgumentError] if no matching element is found.
static Country parseCountryName(
String countryName, {
BuildContext? context,
List<Locale>? locales,
}) {
final String countryNameLower = countryName.toLowerCase();

final CountryLocalizations? localizations =
context != null ? CountryLocalizations.of(context) : null;

final String languageCode = _anyLocalizedNameToCode(
countryNameLower,
localizations?.locale,
locales,
);

return _getFromCode(languageCode);
}

/// Returns a single country if it matches the given [countryName].
///
/// Returns null if no matching element is found.
static Country? tryParseCountryName(
String countryName, {
BuildContext? context,
List<Locale>? locales,
}) {
try {
return parseCountryName(countryName, context: context, locales: locales);
} catch (_) {
return null;
}
}

/// Returns a country that matches the [countryCode] (iso2_cc).
static Country _getFromCode(String countryCode) {
return Country.from(
json: countryCodes.singleWhere(
(Map<String, dynamic> c) => c['iso2_cc'] == countryCode,
),
);
}

/// Returns a country code that matches a country with the given [name] for
/// any language, or the ones given by [locales]. If no locale list is given,
/// the language for the [locale] is prioritized, followed by the default
/// language, english.
static String _anyLocalizedNameToCode(
String name,
Locale? locale,
List<Locale>? locales,
) {
String? code;

if (locale != null) code = _localizedNameToCode(name, locale);
if (code == null && locales == null) {
code = _localizedNameToCode(name, const Locale('en'));
}
if (code != null) return code;

final List<Locale> localeList = locales ?? <Locale>[];

if (locales == null) {
final List<Locale> exclude = <Locale>[const Locale('en')];
if (locale != null) exclude.add(locale);
localeList.addAll(_supportedLanguages(exclude: exclude));
}

return _nameToCodeFromGivenLocales(name, localeList);
}

/// Returns the country code that matches the given [name] for any of the
/// [locales].
///
/// Throws an [ArgumentError] if no matching element is found.
static String _nameToCodeFromGivenLocales(String name, List<Locale> locales) {
String? code;

for (int i = 0; i < locales.length && code == null; i++) {
code = _localizedNameToCode(name, locales[i]);
}

if (code == null) {
throw ArgumentError.value('No country found');
}

return code;
}

/// Returns the code for the country that matches the given [name] in the
/// language given by the [locale]. Defaults to english.
///
/// Returns null if no match is found.
static String? _localizedNameToCode(String name, Locale locale) {
final Map<String, String> translation = _getTranslation(locale);

String? code;

translation.forEach((key, value) {
if (value.toLowerCase() == name) code = key;
});

return code;
}

// ToDo: solution to prevent manual update on adding new localizations?
/// Returns a translation for the given [locale]. Defaults to english.
static Map<String, String> _getTranslation(Locale locale) {
switch (locale.languageCode) {
case 'zh':
switch (locale.scriptCode) {
case 'Hant':
return tw;
case 'Hans':
default:
return cn;
}
case 'el':
return gr;
case 'es':
return es;
case 'pt':
return pt;
case 'nb':
return nb;
case 'nn':
return nn;
case 'en':
default:
return en;
}
}

// ToDo: solution to prevent manual update on adding new localizations?
/// A list of the supported locales, except those included in the [exclude]
/// list.
static List<Locale> _supportedLanguages({
List<Locale> exclude = const <Locale>[],
}) {
return <Locale>[
const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hant'),
const Locale.fromSubtags(languageCode: 'zh', scriptCode: 'Hans'),
const Locale('el'),
const Locale('es'),
const Locale('pt'),
const Locale('nb'),
const Locale('nn'),
const Locale('en'),
]..removeWhere((Locale l) => exclude.contains(l));
}
}

0 comments on commit 9b66b04

Please sign in to comment.