Unverified Commit 19e7db58 authored by Shi-Hao Hong's avatar Shi-Hao Hong Committed by GitHub

[gen_l10n] Escape special JSON characters in generateString utility function (#53605)

parent a94e647d
...@@ -373,36 +373,36 @@ class $classNamePrefix$camelCaseName extends $superClass {'''; ...@@ -373,36 +373,36 @@ class $classNamePrefix$camelCaseName extends $superClass {''';
/// foo "bar" => 'foo "bar"' /// foo "bar" => 'foo "bar"'
/// foo 'bar' => "foo 'bar'" /// foo 'bar' => "foo 'bar'"
/// foo 'bar' "baz" => '''foo 'bar' "baz"''' /// foo 'bar' "baz" => '''foo 'bar' "baz"'''
/// foo\bar => 'foo\\bar'
/// foo\nbar => 'foo\\\\nbar'
/// ``` /// ```
/// ///
/// When [shouldEscapeDollar] is set to true, the /// This function is used by tools that take in a JSON-formatted file to
/// result avoids character escaping, with the /// generate Dart code. For this reason, characters with special meaning
/// exception of the dollar sign: /// in JSON files. For example, the backspace character (\b) have to be
/// properly escaped by this function so that the generated Dart code
/// correctly represents this character:
/// ```
/// foo\bar => 'foo\\bar'
/// foo\nbar => 'foo\\nbar'
/// foo\\nbar => 'foo\\\\nbar'
/// foo\\bar => 'foo\\\\bar'
/// foo\ bar => 'foo\\ bar'
/// ```
/// ///
/// When [shouldEscapeDollar] is set to true, the '$' characters in the
/// input will be replaced by '$' in the returned string:
/// ``` /// ```
/// foo$bar = 'foo\$bar' /// foo$bar = 'foo\$bar'
/// ``` /// ```
/// ///
/// When [shouldEscapeDollar] is set to false, the /// When [shouldEscapeDollar] is set to false, '$' will be replaced
/// result tries to avoid character escaping: /// by '\$' in the returned string:
///
/// ``` /// ```
/// foo$bar => 'foo\\\$bar' /// foo$bar => 'foo\\\$bar'
/// ``` /// ```
/// ///
/// [shouldEscapeDollar] is true by default. /// [shouldEscapeDollar] is true by default.
///
/// Strings with newlines are not supported.
String generateString(String value, { bool escapeDollar = true }) { String generateString(String value, { bool escapeDollar = true }) {
assert(escapeDollar != null); assert(escapeDollar != null);
assert(
!value.contains('\n'),
'Since it is assumed that the input string comes '
'from a json/arb file source, messages cannot '
'contain newlines.'
);
const String backslash = '__BACKSLASH__'; const String backslash = '__BACKSLASH__';
assert( assert(
...@@ -419,6 +419,11 @@ String generateString(String value, { bool escapeDollar = true }) { ...@@ -419,6 +419,11 @@ String generateString(String value, { bool escapeDollar = true }) {
value = value value = value
.replaceAll("'", "\\'") .replaceAll("'", "\\'")
.replaceAll('"', '\\"') .replaceAll('"', '\\"')
.replaceAll('\n', '\\n')
.replaceAll('\f', '\\f')
.replaceAll('\t', '\\t')
.replaceAll('\r', '\\r')
.replaceAll('\b', '\\b')
.replaceAll(backslash, '\\\\'); .replaceAll(backslash, '\\\\');
return "'$value'"; return "'$value'";
......
...@@ -45,15 +45,31 @@ void main() { ...@@ -45,15 +45,31 @@ void main() {
}); });
test('handles backslash', () { test('handles backslash', () {
expect(generateString(r'ab\c'), "'ab\\\\c'"); expect(generateString(r'ab\c'), r"'ab\\c'");
}); });
test('handles backslash followed by "n" character', () { test('handles backslash followed by "n" character', () {
expect(generateString(r'ab\nc'), "'ab\\\\nc'"); expect(generateString(r'ab\nc'), r"'ab\\nc'");
}); });
test('does not support multiline strings', () { test('supports newline escaping', () {
expect(() => generateString('ab\nc'), throwsA(isA<AssertionError>())); expect(generateString('ab\nc'), "'ab\\nc'");
});
test('supports form feed escaping', () {
expect(generateString('ab\fc'), "'ab\\fc'");
});
test('supports tab escaping', () {
expect(generateString('ab\tc'), "'ab\\tc'");
});
test('supports carriage return escaping', () {
expect(generateString('ab\rc'), "'ab\\rc'");
});
test('supports backspace escaping', () {
expect(generateString('ab\bc'), "'ab\\bc'");
}); });
}); });
} }
...@@ -88,28 +88,29 @@ void main() { ...@@ -88,28 +88,29 @@ void main() {
'#l10n 9 (Hello GB fallback World)\n' '#l10n 9 (Hello GB fallback World)\n'
'#l10n 10 (--- General formatting tests ---)\n' '#l10n 10 (--- General formatting tests ---)\n'
'#l10n 11 (Hello World)\n' '#l10n 11 (Hello World)\n'
'#l10n 12 (Hello World)\n' '#l10n 12 (Hello _NEWLINE_ World)\n'
'#l10n 13 (Hello World)\n' '#l10n 13 (Hello World)\n'
'#l10n 14 (Hello World on Friday, January 1, 1960)\n' '#l10n 14 (Hello World)\n'
'#l10n 15 (Hello world argument on 1/1/1960 at 00:00)\n' '#l10n 15 (Hello World on Friday, January 1, 1960)\n'
'#l10n 16 (Hello World from 1960 to 2020)\n' '#l10n 16 (Hello world argument on 1/1/1960 at 00:00)\n'
'#l10n 17 (Hello for 123)\n' '#l10n 17 (Hello World from 1960 to 2020)\n'
'#l10n 18 (Hello for price USD123.00)\n' '#l10n 18 (Hello for 123)\n'
'#l10n 19 (Hello)\n' '#l10n 19 (Hello for price USD123.00)\n'
'#l10n 20 (Hello World)\n' '#l10n 20 (Hello)\n'
'#l10n 21 (Hello two worlds)\n' '#l10n 21 (Hello World)\n'
'#l10n 22 (Hello)\n' '#l10n 22 (Hello two worlds)\n'
'#l10n 23 (Hello new World)\n' '#l10n 23 (Hello)\n'
'#l10n 24 (Hello two new worlds)\n' '#l10n 24 (Hello new World)\n'
'#l10n 25 (Hello on Friday, January 1, 1960)\n' '#l10n 25 (Hello two new worlds)\n'
'#l10n 26 (Hello World, on Friday, January 1, 1960)\n' '#l10n 26 (Hello on Friday, January 1, 1960)\n'
'#l10n 27 (Hello two worlds, on Friday, January 1, 1960)\n' '#l10n 27 (Hello World, on Friday, January 1, 1960)\n'
'#l10n 28 (Hello other 0 worlds, with a total of 100 citizens)\n' '#l10n 28 (Hello two worlds, on Friday, January 1, 1960)\n'
'#l10n 29 (Hello World of 101 citizens)\n' '#l10n 29 (Hello other 0 worlds, with a total of 100 citizens)\n'
'#l10n 30 (Hello two worlds with 102 total citizens)\n' '#l10n 30 (Hello World of 101 citizens)\n'
'#l10n 31 ([Hello] -World- #123#)\n' '#l10n 31 (Hello two worlds with 102 total citizens)\n'
'#l10n 32 (Flutter\'s amazing!)\n' '#l10n 32 ([Hello] -World- #123#)\n'
'#l10n 33 (Flutter is "amazing"!)\n' '#l10n 33 (Flutter\'s amazing!)\n'
'#l10n 34 (Flutter is "amazing"!)\n'
'#l10n END\n' '#l10n END\n'
); );
}); });
......
...@@ -124,6 +124,7 @@ class Home extends StatelessWidget { ...@@ -124,6 +124,7 @@ class Home extends StatelessWidget {
final AppLocalizations localizations = AppLocalizations.of(context); final AppLocalizations localizations = AppLocalizations.of(context);
results.addAll(<String>[ results.addAll(<String>[
'${localizations.helloWorld}', '${localizations.helloWorld}',
'${localizations.helloNewlineWorld}',
'${localizations.hello("World")}', '${localizations.hello("World")}',
'${localizations.greeting("Hello", "World")}', '${localizations.greeting("Hello", "World")}',
'${localizations.helloWorldOn(DateTime(1960))}', '${localizations.helloWorldOn(DateTime(1960))}',
...@@ -154,7 +155,9 @@ class Home extends StatelessWidget { ...@@ -154,7 +155,9 @@ class Home extends StatelessWidget {
try { try {
int n = 0; int n = 0;
for (final String result in results) { for (final String result in results) {
print('#l10n $n ($result)'); // Newline character replacement is necessary because
// the stream breaks up stdout by new lines.
print('#l10n $n (${result.replaceAll('\n', '_NEWLINE_')})');
n += 1; n += 1;
} }
} }
...@@ -188,6 +191,11 @@ void main() { ...@@ -188,6 +191,11 @@ void main() {
"description": "The conventional newborn programmer greeting" "description": "The conventional newborn programmer greeting"
}, },
"helloNewlineWorld": "Hello \n World",
"@helloNewlineWorld": {
"description": "The JSON decoder should convert backslash-n to a newline character in the generated Dart string."
},
"hello": "Hello {world}", "hello": "Hello {world}",
"@hello": { "@hello": {
"description": "A message with a single parameter", "description": "A message with a single parameter",
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment