gen_date_localizations.dart 6.28 KB
Newer Older
1 2 3 4 5 6 7 8
// 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.
///
9
/// The extracted data is written into:
10
///   packages/flutter_localizations/lib/src/l10n/generated_date_localizations.dart
11
///
12 13 14
/// ## Usage
///
/// Run this program from the root of the git repository.
15 16 17 18
///
/// The following outputs the generated Dart code to the console as a dry run:
///
/// ```
19
/// dart dev/tools/localization/gen_date_localizations.dart
20 21
/// ```
///
22
/// If the data looks good, use the `--overwrite` option to overwrite the
23 24 25
/// lib/src/l10n/date_localizations.dart file:
///
/// ```
26
/// dart dev/tools/localization/gen_date_localizations.dart --overwrite
27 28 29 30 31 32 33 34
/// ```

import 'dart:async';
import 'dart:convert';
import 'dart:io';

import 'package:path/path.dart' as path;

35 36
import 'localizations_utils.dart';

37 38
const String _kCommandName = 'gen_date_localizations.dart';

39
Future<void> main(List<String> rawArgs) async {
40
  checkCwdIsRepoRoot(_kCommandName);
41

42
  final bool writeToFile = parseArgs(rawArgs).writeToFile;
43

44
  final File dotPackagesFile = File(path.join('packages', 'flutter_localizations', '.packages'));
45 46 47
  final bool dotPackagesExists = dotPackagesFile.existsSync();

  if (!dotPackagesExists) {
48
    exitWithError(
49 50 51 52 53 54 55 56 57 58 59
      '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: () {
60
        exitWithError('intl dependency not found in ${dotPackagesFile.path}');
61
        return null; // unreachable
62 63 64 65 66
      },
    )
    .split(':')
    .last;

67
  final Directory dateSymbolsDirectory = Directory(path.join(pathToIntl, 'src', 'data', 'dates', 'symbols'));
68
  final Map<String, File> symbolFiles = _listIntlData(dateSymbolsDirectory);
69
  final Directory datePatternsDirectory = Directory(path.join(pathToIntl, 'src', 'data', 'dates', 'patterns'));
70
  final Map<String, File> patternFiles = _listIntlData(datePatternsDirectory);
71
  final StringBuffer buffer = StringBuffer();
72 73 74 75 76 77 78

  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.

79
// This file has been automatically generated. Please do not edit it manually.
80
// To regenerate run (omit --overwrite to print to console instead of the file):
81
// dart --enable-asserts dev/tools/localization/gen_date_localizations.dart --overwrite
82 83 84

'''
);
85 86 87
  buffer.writeln('''
/// The subset of date symbols supported by the intl package which are also
/// supported by flutter_localizations.''');
88
  buffer.writeln('const Map<String, dynamic> dateSymbols = <String, dynamic> {');
89
  symbolFiles.forEach((String locale, File data) {
90
    if (_supportedLocales().contains(locale))
91
      buffer.writeln(_jsonToMapEntry(locale, json.decode(data.readAsStringSync())));
92 93 94
  });
  buffer.writeln('};');

95
  // Code that uses datePatterns expects it to contain values of type
96
  // Map<String, String> not Map<String, dynamic>.
97 98 99
  buffer.writeln('''
/// The subset of date patterns supported by the intl package which are also
/// supported by flutter_localizations.''');
100
  buffer.writeln('const Map<String, Map<String, String>> datePatterns = <String, Map<String, String>> {');
101
  patternFiles.forEach((String locale, File data) {
102
    if (_supportedLocales().contains(locale)) {
103
      final Map<String, dynamic> patterns = json.decode(data.readAsStringSync());
104
      buffer.writeln("'$locale': <String, String>{");
105 106 107 108 109 110
      patterns.forEach((String key, dynamic value) {
        assert(value is String);
        buffer.writeln(_jsonToMapEntry(key, value));
      });
      buffer.writeln('},');
    }
111 112 113 114
  });
  buffer.writeln('};');

  if (writeToFile) {
115
    final File dateLocalizationsFile = File(path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n', 'generated_date_localizations.dart'));
116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141
    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)
142
    return '<dynamic>[${json.map<String>(_jsonToMap).join(',')}]';
143

144
  if (json is Map<String, dynamic>) {
145
    final StringBuffer buffer = StringBuffer('<String, dynamic>{');
146 147 148 149 150 151 152 153 154 155
    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.';
}

156 157 158 159 160
Set<String> _supportedLocales() {
  final Set<String> supportedLocales = <String>{};
  final RegExp filenameRE = RegExp(r'(?:material|cupertino)_(\w+)\.arb$');
  final Directory supportedLocalesDirectory = Directory(path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n'));
  for (FileSystemEntity entity in supportedLocalesDirectory.listSync()) {
161 162
    final String filePath = entity.path;
    if (FileSystemEntity.isFileSync(filePath) && filenameRE.hasMatch(filePath))
163
      supportedLocales.add(filenameRE.firstMatch(filePath)[1]);
164
  }
165
  return supportedLocales;
166 167 168
}

Map<String, File> _listIntlData(Directory directory) {
169
  final Map<String, File> localeFiles = <String, File>{};
170 171 172 173
  for (FileSystemEntity entity in directory.listSync()) {
    final String filePath = entity.path;
    if (FileSystemEntity.isFileSync(filePath) && filePath.endsWith('.json')) {
      final String locale = path.basenameWithoutExtension(filePath);
174
      localeFiles[locale] = entity;
175 176
    }
  }
177 178 179

  final List<String> locales = localeFiles.keys.toList(growable: false);
  locales.sort();
180
  return Map<String, File>.fromIterable(locales, value: (dynamic locale) => localeFiles[locale]);
181
}