mirror of
https://github.com/flutter/flutter.git
synced 2025-06-03 00:51:18 +00:00
[gen_l10n] Update the arb filename parsing logic (#60185)
* Update the arb filename parsing to account for underscores * Generalize the locale searching algorithm * Update filename and @@locale behavior to require them to match
This commit is contained in:
parent
98d66de888
commit
1e6e7150f3
@ -5,6 +5,9 @@
|
|||||||
import 'dart:convert';
|
import 'dart:convert';
|
||||||
import 'dart:io';
|
import 'dart:io';
|
||||||
|
|
||||||
|
import 'package:intl/locale.dart';
|
||||||
|
import 'package:path/path.dart' as path;
|
||||||
|
|
||||||
import 'localizations_utils.dart';
|
import 'localizations_utils.dart';
|
||||||
|
|
||||||
// The set of date formats that can be automatically localized.
|
// The set of date formats that can be automatically localized.
|
||||||
@ -374,19 +377,53 @@ class AppResourceBundle {
|
|||||||
}
|
}
|
||||||
|
|
||||||
String localeString = resources['@@locale'] as String;
|
String localeString = resources['@@locale'] as String;
|
||||||
if (localeString == null) {
|
|
||||||
final RegExp filenameRE = RegExp(r'^[^_]*_(\w+)\.arb$');
|
// Look for the first instance of an ISO 639-1 language code, matching exactly.
|
||||||
final RegExpMatch match = filenameRE.firstMatch(file.path);
|
final String fileName = path.basenameWithoutExtension(file.path);
|
||||||
localeString = match == null ? null : match[1];
|
|
||||||
|
for (int index = 0; index < fileName.length; index += 1) {
|
||||||
|
// If an underscore was found, check if locale string follows.
|
||||||
|
if (fileName[index] == '_' && fileName[index + 1] != null) {
|
||||||
|
// If Locale.tryParse fails, it returns null.
|
||||||
|
final Locale parserResult = Locale.tryParse(fileName.substring(index + 1));
|
||||||
|
// If the parserResult is not an actual locale identifier, end the loop.
|
||||||
|
if (parserResult != null && _iso639Languages.contains(parserResult.languageCode)) {
|
||||||
|
// The parsed result uses dashes ('-'), but we want underscores ('_').
|
||||||
|
final String parserLocaleString = parserResult.toString().replaceAll('-', '_');
|
||||||
|
|
||||||
|
|
||||||
|
if (localeString == null) {
|
||||||
|
// If @@locale was not defined, use the filename locale suffix.
|
||||||
|
localeString = parserLocaleString;
|
||||||
|
} else {
|
||||||
|
// If the localeString was defined in @@locale and in the filename, verify to
|
||||||
|
// see if the parsed locale matches, throw an error if it does not. This
|
||||||
|
// prevents developers from confusing issues when both @@locale and
|
||||||
|
// "_{locale}" is specified in the filename.
|
||||||
|
if (localeString != parserLocaleString) {
|
||||||
|
throw L10nException(
|
||||||
|
'The locale specified in @@locale and the arb filename do not match. \n'
|
||||||
|
'Please make sure that they match, since this prevents any confusion \n'
|
||||||
|
'with which locale to use. Otherwise, specify the locale in either the \n'
|
||||||
|
'filename of the @@locale key only.\n'
|
||||||
|
'Current @@locale value: $localeString\n'
|
||||||
|
'Current filename extension: $parserLocaleString'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if (localeString == null) {
|
if (localeString == null) {
|
||||||
throw L10nException(
|
throw L10nException(
|
||||||
"The following .arb file's locale could not be determined: \n"
|
"The following .arb file's locale could not be determined: \n"
|
||||||
'${file.path} \n'
|
'${file.path} \n'
|
||||||
"Make sure that the locale is specified in the file's '@@locale' "
|
"Make sure that the locale is specified in the file's '@@locale' "
|
||||||
'property or as part of the filename (e.g. file_en.arb)'
|
'property or as part of the filename (e.g. file_en.arb)'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
final Iterable<String> ids = resources.keys.where((String key) => !key.startsWith('@'));
|
final Iterable<String> ids = resources.keys.where((String key) => !key.startsWith('@'));
|
||||||
return AppResourceBundle._(file, LocaleInfo.fromString(localeString), resources, ids);
|
return AppResourceBundle._(file, LocaleInfo.fromString(localeString), resources, ids);
|
||||||
@ -469,3 +506,191 @@ class AppResourceBundleCollection {
|
|||||||
return 'AppResourceBundleCollection(${_directory.path}, ${locales.length} locales)';
|
return 'AppResourceBundleCollection(${_directory.path}, ${locales.length} locales)';
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// A set containing all the ISO630-1 languages. This list was pulled from https://datahub.io/core/language-codes.
|
||||||
|
final Set<String> _iso639Languages = <String>{
|
||||||
|
'aa',
|
||||||
|
'ab',
|
||||||
|
'ae',
|
||||||
|
'af',
|
||||||
|
'ak',
|
||||||
|
'am',
|
||||||
|
'an',
|
||||||
|
'ar',
|
||||||
|
'as',
|
||||||
|
'av',
|
||||||
|
'ay',
|
||||||
|
'az',
|
||||||
|
'ba',
|
||||||
|
'be',
|
||||||
|
'bg',
|
||||||
|
'bh',
|
||||||
|
'bi',
|
||||||
|
'bm',
|
||||||
|
'bn',
|
||||||
|
'bo',
|
||||||
|
'br',
|
||||||
|
'bs',
|
||||||
|
'ca',
|
||||||
|
'ce',
|
||||||
|
'ch',
|
||||||
|
'co',
|
||||||
|
'cr',
|
||||||
|
'cs',
|
||||||
|
'cu',
|
||||||
|
'cv',
|
||||||
|
'cy',
|
||||||
|
'da',
|
||||||
|
'de',
|
||||||
|
'dv',
|
||||||
|
'dz',
|
||||||
|
'ee',
|
||||||
|
'el',
|
||||||
|
'en',
|
||||||
|
'eo',
|
||||||
|
'es',
|
||||||
|
'et',
|
||||||
|
'eu',
|
||||||
|
'fa',
|
||||||
|
'ff',
|
||||||
|
'fi',
|
||||||
|
'fj',
|
||||||
|
'fo',
|
||||||
|
'fr',
|
||||||
|
'fy',
|
||||||
|
'ga',
|
||||||
|
'gd',
|
||||||
|
'gl',
|
||||||
|
'gn',
|
||||||
|
'gu',
|
||||||
|
'gv',
|
||||||
|
'ha',
|
||||||
|
'he',
|
||||||
|
'hi',
|
||||||
|
'ho',
|
||||||
|
'hr',
|
||||||
|
'ht',
|
||||||
|
'hu',
|
||||||
|
'hy',
|
||||||
|
'hz',
|
||||||
|
'ia',
|
||||||
|
'id',
|
||||||
|
'ie',
|
||||||
|
'ig',
|
||||||
|
'ii',
|
||||||
|
'ik',
|
||||||
|
'io',
|
||||||
|
'is',
|
||||||
|
'it',
|
||||||
|
'iu',
|
||||||
|
'ja',
|
||||||
|
'jv',
|
||||||
|
'ka',
|
||||||
|
'kg',
|
||||||
|
'ki',
|
||||||
|
'kj',
|
||||||
|
'kk',
|
||||||
|
'kl',
|
||||||
|
'km',
|
||||||
|
'kn',
|
||||||
|
'ko',
|
||||||
|
'kr',
|
||||||
|
'ks',
|
||||||
|
'ku',
|
||||||
|
'kv',
|
||||||
|
'kw',
|
||||||
|
'ky',
|
||||||
|
'la',
|
||||||
|
'lb',
|
||||||
|
'lg',
|
||||||
|
'li',
|
||||||
|
'ln',
|
||||||
|
'lo',
|
||||||
|
'lt',
|
||||||
|
'lu',
|
||||||
|
'lv',
|
||||||
|
'mg',
|
||||||
|
'mh',
|
||||||
|
'mi',
|
||||||
|
'mk',
|
||||||
|
'ml',
|
||||||
|
'mn',
|
||||||
|
'mr',
|
||||||
|
'ms',
|
||||||
|
'mt',
|
||||||
|
'my',
|
||||||
|
'na',
|
||||||
|
'nb',
|
||||||
|
'nd',
|
||||||
|
'ne',
|
||||||
|
'ng',
|
||||||
|
'nl',
|
||||||
|
'nn',
|
||||||
|
'no',
|
||||||
|
'nr',
|
||||||
|
'nv',
|
||||||
|
'ny',
|
||||||
|
'oc',
|
||||||
|
'oj',
|
||||||
|
'om',
|
||||||
|
'or',
|
||||||
|
'os',
|
||||||
|
'pa',
|
||||||
|
'pi',
|
||||||
|
'pl',
|
||||||
|
'ps',
|
||||||
|
'pt',
|
||||||
|
'qu',
|
||||||
|
'rm',
|
||||||
|
'rn',
|
||||||
|
'ro',
|
||||||
|
'ru',
|
||||||
|
'rw',
|
||||||
|
'sa',
|
||||||
|
'sc',
|
||||||
|
'sd',
|
||||||
|
'se',
|
||||||
|
'sg',
|
||||||
|
'si',
|
||||||
|
'sk',
|
||||||
|
'sl',
|
||||||
|
'sm',
|
||||||
|
'sn',
|
||||||
|
'so',
|
||||||
|
'sq',
|
||||||
|
'sr',
|
||||||
|
'ss',
|
||||||
|
'st',
|
||||||
|
'su',
|
||||||
|
'sv',
|
||||||
|
'sw',
|
||||||
|
'ta',
|
||||||
|
'te',
|
||||||
|
'tg',
|
||||||
|
'th',
|
||||||
|
'ti',
|
||||||
|
'tk',
|
||||||
|
'tl',
|
||||||
|
'tn',
|
||||||
|
'to',
|
||||||
|
'tr',
|
||||||
|
'ts',
|
||||||
|
'tt',
|
||||||
|
'tw',
|
||||||
|
'ty',
|
||||||
|
'ug',
|
||||||
|
'uk',
|
||||||
|
'ur',
|
||||||
|
'uz',
|
||||||
|
've',
|
||||||
|
'vi',
|
||||||
|
'vo',
|
||||||
|
'wa',
|
||||||
|
'wo',
|
||||||
|
'xh',
|
||||||
|
'yi',
|
||||||
|
'yo',
|
||||||
|
'za',
|
||||||
|
'zh',
|
||||||
|
'zu',
|
||||||
|
};
|
||||||
|
@ -308,6 +308,61 @@ void main() {
|
|||||||
expect(generator.header, '/// Sample header in a text file');
|
expect(generator.header, '/// Sample header in a text file');
|
||||||
});
|
});
|
||||||
|
|
||||||
|
test('sets templateArbFileName with more than one underscore correctly', () {
|
||||||
|
final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
|
||||||
|
..createSync(recursive: true);
|
||||||
|
l10nDirectory.childFile('app_localizations_en.arb')
|
||||||
|
.writeAsStringSync(singleMessageArbFileString);
|
||||||
|
l10nDirectory.childFile('app_localizations_es.arb')
|
||||||
|
.writeAsStringSync(singleEsMessageArbFileString);
|
||||||
|
LocalizationsGenerator generator;
|
||||||
|
try {
|
||||||
|
generator = LocalizationsGenerator(fs);
|
||||||
|
generator
|
||||||
|
..initialize(
|
||||||
|
inputPathString: defaultL10nPathString,
|
||||||
|
templateArbFileName: 'app_localizations_en.arb',
|
||||||
|
outputFileString: defaultOutputFileString,
|
||||||
|
classNameString: defaultClassNameString,
|
||||||
|
)
|
||||||
|
..loadResources()
|
||||||
|
..writeOutputFiles();
|
||||||
|
} on L10nException catch (e) {
|
||||||
|
fail('Generating output should not fail: \n${e.message}');
|
||||||
|
}
|
||||||
|
|
||||||
|
final Directory outputDirectory = fs.directory('lib').childDirectory('l10n');
|
||||||
|
expect(outputDirectory.childFile('output-localization-file.dart').existsSync(), isTrue);
|
||||||
|
expect(outputDirectory.childFile('output-localization-file_en.dart').existsSync(), isTrue);
|
||||||
|
expect(outputDirectory.childFile('output-localization-file_es.dart').existsSync(), isTrue);
|
||||||
|
});
|
||||||
|
|
||||||
|
test('filenames with invalid locales should not be recognized', () {
|
||||||
|
final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
|
||||||
|
..createSync(recursive: true);
|
||||||
|
l10nDirectory.childFile('app_localizations_en.arb')
|
||||||
|
.writeAsStringSync(singleMessageArbFileString);
|
||||||
|
l10nDirectory.childFile('app_localizations_en_CA_foo.arb')
|
||||||
|
.writeAsStringSync(singleMessageArbFileString);
|
||||||
|
LocalizationsGenerator generator;
|
||||||
|
try {
|
||||||
|
generator = LocalizationsGenerator(fs);
|
||||||
|
generator
|
||||||
|
..initialize(
|
||||||
|
inputPathString: defaultL10nPathString,
|
||||||
|
templateArbFileName: 'app_localizations_en.arb',
|
||||||
|
outputFileString: defaultOutputFileString,
|
||||||
|
classNameString: defaultClassNameString,
|
||||||
|
)
|
||||||
|
..loadResources();
|
||||||
|
} on L10nException catch (e) {
|
||||||
|
expect(e.message, contains('The following .arb file\'s locale could not be determined'));
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
fail('Using app_en_CA_foo.arb should fail as it is not a valid locale.');
|
||||||
|
});
|
||||||
|
|
||||||
test('correctly creates an unimplemented messages file', () {
|
test('correctly creates an unimplemented messages file', () {
|
||||||
fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
|
fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
|
||||||
..createSync(recursive: true)
|
..createSync(recursive: true)
|
||||||
@ -799,7 +854,7 @@ void main() {
|
|||||||
expect(generator.supportedLocales.contains(LocaleInfo.fromString('zh')), true);
|
expect(generator.supportedLocales.contains(LocaleInfo.fromString('zh')), true);
|
||||||
});
|
});
|
||||||
|
|
||||||
test('correctly prioritizes @@locale property in arb file over filename', () {
|
test('correctly requires @@locale property in arb file to match the filename locale suffix', () {
|
||||||
const String arbFileWithEnLocale = '''
|
const String arbFileWithEnLocale = '''
|
||||||
{
|
{
|
||||||
"@@locale": "en",
|
"@@locale": "en",
|
||||||
@ -837,15 +892,14 @@ void main() {
|
|||||||
);
|
);
|
||||||
generator.loadResources();
|
generator.loadResources();
|
||||||
} on L10nException catch (e) {
|
} on L10nException catch (e) {
|
||||||
fail('Setting language and locales should not fail: \n${e.message}');
|
expect(e.message, contains('The locale specified in @@locale and the arb filename do not match.'));
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// @@locale property should hold higher priority
|
fail(
|
||||||
expect(generator.supportedLocales.contains(LocaleInfo.fromString('en')), true);
|
'An exception should occur if the @@locale and the filename extensions are '
|
||||||
expect(generator.supportedLocales.contains(LocaleInfo.fromString('zh')), true);
|
'defined but not matching.'
|
||||||
// filename should not be used since @@locale is specified
|
);
|
||||||
expect(generator.supportedLocales.contains(LocaleInfo.fromString('es')), false);
|
|
||||||
expect(generator.supportedLocales.contains(LocaleInfo.fromString('am')), false);
|
|
||||||
});
|
});
|
||||||
|
|
||||||
test("throws when arb file's locale could not be determined", () {
|
test("throws when arb file's locale could not be determined", () {
|
||||||
|
Loading…
Reference in New Issue
Block a user