1// Copyright 2019 The Chromium Authors. All rights reserved. 2// Use of this source code is governed by a BSD-style license that can be 3// found in the LICENSE file. 4 5// This program replaces the material_kn.arb and cupertino_kn.arb 6// files in flutter_localizations/packages/lib/src/l10n with versions 7// where the contents of the localized strings have been replaced by JSON 8// escapes. This is done because some of those strings contain characters 9// that can crash Emacs on Linux. There is more information 10// here: https://github.com/flutter/flutter/issues/36704 and in the README 11// in flutter_localizations/packages/lib/src/l10n. 12// 13// This app needs to be run by hand when material_kn.arb or cupertino_kn.arb 14// have been updated. 15// 16// ## Usage 17// 18// Run this program from the root of the git repository. 19// 20// ``` 21// dart dev/tools/localization/encode_kn_arb_files.dart 22// ``` 23 24import 'dart:async'; 25import 'dart:convert'; 26import 'dart:io'; 27 28import 'package:path/path.dart' as path; 29 30import 'localizations_utils.dart'; 31 32Map<String, dynamic> loadBundle(File file) { 33 if (!FileSystemEntity.isFileSync(file.path)) 34 exitWithError('Unable to find input file: ${file.path}'); 35 return json.decode(file.readAsStringSync()); 36} 37 38void encodeBundleTranslations(Map<String, dynamic> bundle) { 39 for (String key in bundle.keys) { 40 // The ARB file resource "attributes" for foo are called @foo. Don't need 41 // to encode them. 42 if (key.startsWith('@')) 43 continue; 44 final String translation = bundle[key]; 45 // Rewrite the string as a series of unicode characters in JSON format. 46 // Like "\u0012\u0123\u1234". 47 bundle[key] = translation.runes.map((int code) { 48 final String codeString = '00${code.toRadixString(16)}'; 49 return '\\u${codeString.substring(codeString.length - 4)}'; 50 }).join(); 51 } 52} 53 54void checkEncodedTranslations(Map<String, dynamic> encodedBundle, Map<String, dynamic> bundle) { 55 bool errorFound = false; 56 const JsonDecoder decoder = JsonDecoder(); 57 for (String key in bundle.keys) { 58 if (decoder.convert('"${encodedBundle[key]}"') != bundle[key]) { 59 stderr.writeln(' encodedTranslation for $key does not match original value "${bundle[key]}"'); 60 errorFound = true; 61 } 62 } 63 if (errorFound) 64 exitWithError('JSON unicode translation encoding failed'); 65} 66 67void rewriteBundle(File file, Map<String, dynamic> bundle) { 68 final StringBuffer contents = StringBuffer(); 69 contents.writeln('{'); 70 for (String key in bundle.keys) { 71 contents.writeln(' "$key": "${bundle[key]}"${key == bundle.keys.last ? '' : ','}'); 72 } 73 contents.writeln('}'); 74 file.writeAsStringSync(contents.toString()); 75} 76 77Future<void> main(List<String> rawArgs) async { 78 checkCwdIsRepoRoot('encode_kn_arb_files'); 79 80 final String l10nPath = path.join('packages', 'flutter_localizations', 'lib', 'src', 'l10n'); 81 final File materialArbFile = File(path.join(l10nPath, 'material_kn.arb')); 82 final File cupertinoArbFile = File(path.join(l10nPath, 'cupertino_kn.arb')); 83 84 final Map<String, dynamic> materialBundle = loadBundle(materialArbFile); 85 final Map<String, dynamic> cupertinoBundle = loadBundle(cupertinoArbFile); 86 87 encodeBundleTranslations(materialBundle); 88 encodeBundleTranslations(cupertinoBundle); 89 90 checkEncodedTranslations(materialBundle, loadBundle(materialArbFile)); 91 checkEncodedTranslations(cupertinoBundle, loadBundle(cupertinoArbFile)); 92 93 rewriteBundle(materialArbFile, materialBundle); 94 rewriteBundle(cupertinoArbFile, cupertinoBundle); 95} 96