// Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. /// This program extracts localized date symbols and patterns from the intl /// package for the subset of locales supported by the flutter_localizations /// package. /// /// The extracted data is written into: /// packages/flutter_localizations/lib/src/l10n/date_localizations.dart /// /// ## Usage /// /// Run this program from the root of the git repository. /// /// The following outputs the generated Dart code to the console as a dry run: /// /// ``` /// dart dev/tools/gen_date_localizations.dart /// ``` /// /// If the data looks good, use the `--overwrite` option to overwrite the /// lib/src/l10n/date_localizations.dart file: /// /// ``` /// dart dev/tools/gen_date_localizations.dart --overwrite /// ``` import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'package:path/path.dart' as path; import 'localizations_utils.dart'; const String _kCommandName = 'gen_date_localizations.dart'; Future<void> main(List<String> rawArgs) async { checkCwdIsRepoRoot(_kCommandName); final bool writeToFile = parseArgs(rawArgs).writeToFile; final File dotPackagesFile = File(path.join('packages', 'flutter_localizations', '.packages')); final bool dotPackagesExists = dotPackagesFile.existsSync(); if (!dotPackagesExists) { exitWithError( 'File not found: ${dotPackagesFile.path}. $_kCommandName must be run ' 'after a successful "flutter update-packages".' ); } final String pathToIntl = dotPackagesFile .readAsStringSync() .split('\n') .firstWhere( (String line) => line.startsWith('intl:'), orElse: () { exitWithError('intl dependency not found in ${dotPackagesFile.path}'); return null; // unreachable }, ) .split(':') .last; final Directory dateSymbolsDirectory = Directory(path.join(pathToIntl, 'src', 'data', 'dates', 'symbols')); final Map<String, File> symbolFiles = _listIntlData(dateSymbolsDirectory); final Directory datePatternsDirectory = Directory(path.join(pathToIntl, 'src', 'data', 'dates', 'patterns')); final Map<String, File> patternFiles = _listIntlData(datePatternsDirectory); final List<String> materialLocales = _materialLocales().toList(); final StringBuffer buffer = StringBuffer(); buffer.writeln( ''' // Copyright 2017 The Chromium Authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be // found in the LICENSE file. // This file has been automatically generated. Please do not edit it manually. // To regenerate run (omit --overwrite to print to console instead of the file): // dart --enable-asserts dev/tools/gen_date_localizations.dart --overwrite // ignore_for_file: public_member_api_docs ''' ); buffer.writeln('const Map<String, dynamic> dateSymbols = <String, dynamic> {'); symbolFiles.forEach((String locale, File data) { if (materialLocales.contains(locale)) buffer.writeln(_jsonToMapEntry(locale, json.decode(data.readAsStringSync()))); }); buffer.writeln('};'); // Code that uses datePatterns expects it to contain values of type // Map<String, String> not Map<String, dynamic>. buffer.writeln('const Map<String, Map<String, String>> datePatterns = <String, Map<String, String>> {'); patternFiles.forEach((String locale, File data) { if (materialLocales.contains(locale)) { final Map<String, dynamic> patterns = json.decode(data.readAsStringSync()); buffer.writeln("'$locale': <String, String>{"); patterns.forEach((String key, dynamic value) { assert(value is String); buffer.writeln(_jsonToMapEntry(key, value)); }); buffer.writeln('},'); } }); buffer.writeln('};'); if (writeToFile) { final File dateLocalizationsFile = File(path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'date_localizations.dart')); dateLocalizationsFile.writeAsStringSync(buffer.toString()); Process.runSync(path.join('bin', 'cache', 'dart-sdk', 'bin', 'dartfmt'), <String>[ '-w', dateLocalizationsFile.path, ]); } else { print(buffer); } } String _jsonToMapEntry(String key, dynamic value) { return "'$key': ${_jsonToMap(value)},"; } String _jsonToMap(dynamic json) { if (json == null || json is num || json is bool) return '$json'; if (json is String) { if (json.contains("'")) return 'r"""$json"""'; else return "r'''$json'''"; } if (json is Iterable) return '<dynamic>[${json.map<String>(_jsonToMap).join(',')}]'; if (json is Map<String, dynamic>) { final StringBuffer buffer = StringBuffer('<String, dynamic>{'); json.forEach((String key, dynamic value) { buffer.writeln(_jsonToMapEntry(key, value)); }); buffer.write('}'); return buffer.toString(); } throw 'Unsupported JSON type ${json.runtimeType} of value $json.'; } Iterable<String> _materialLocales() sync* { final RegExp filenameRE = RegExp(r'material_(\w+)\.arb$'); final Directory materialLocalizationsDirectory = Directory(path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n')); for (FileSystemEntity entity in materialLocalizationsDirectory.listSync()) { final String filePath = entity.path; if (FileSystemEntity.isFileSync(filePath) && filenameRE.hasMatch(filePath)) yield filenameRE.firstMatch(filePath)[1]; } } Map<String, File> _listIntlData(Directory directory) { final Map<String, File> localeFiles = <String, File>{}; for (FileSystemEntity entity in directory.listSync()) { final String filePath = entity.path; if (FileSystemEntity.isFileSync(filePath) && filePath.endsWith('.json')) { final String locale = path.basenameWithoutExtension(filePath); localeFiles[locale] = entity; } } final List<String> locales = localeFiles.keys.toList(growable: false); locales.sort(); return Map<String, File>.fromIterable(locales, value: (dynamic locale) => localeFiles[locale]); }