Unverified Commit 8be335f2 authored by Tae Hyung Kim's avatar Tae Hyung Kim Committed by GitHub

Handle dollar signs properly when generating localizations (#125514)

Currently, the code doesn't properly handle strings which contain dollar signs. The return expression for the generated localization function is computed by `generateReturnExpr` which concatenates several strings, which are either interpolated placeholders, interpolated function calls, or normal strings, but we didn't properly escape dollar signs before sending normal strings to `generateReturnExpr`.

Fixes #125461.
parent 9a28f56e
......@@ -1131,7 +1131,7 @@ class LocalizationsGenerator {
case ST.message:
final List<String> expressions = node.children.map<String>((Node node) {
if (node.type == ST.string) {
return node.value!;
return generateString(node.value!);
}
return generateVariables(node);
}).toList();
......
......@@ -291,10 +291,25 @@ String generateString(String value) {
return value;
}
/// Given a list of strings, placeholders, or helper function calls, concatenate
/// them into one expression to be returned.
/// Given a list of normal strings or interpolated variables, concatenate them
/// into a single dart string to be returned. An example of a normal string
/// would be "'Hello world!'" and an example of a interpolated variable would be
/// "'$placeholder'".
///
/// If `isSingleStringVar` is passed, then we want to convert "'$expr'" to "expr".
/// Each of the strings in [expressions] should be a raw string, which, if it
/// were to be added to a dart file, would be a properly formatted dart string
/// with escapes and/or interpolation. The purpose of this function is to
/// concatenate these dart strings into a single dart string which can be
/// returned in the generated localization files.
///
/// The following rules describe the kinds of string expressions that can be
/// handled:
/// 1. If [expressions] is empty, return the empty string "''".
/// 2. If [expressions] has only one [String] which is an interpolated variable,
/// it is converted to the variable itself e.g. ["'$expr'"] -> "expr".
/// 3. If one string in [expressions] is an interpolation and the next begins
/// with an alphanumeric character, then the former interpolation should be
/// wrapped in braces e.g. ["'$expr1'", "'another'"] -> "'${expr1}another'".
String generateReturnExpr(List<String> expressions, { bool isSingleStringVar = false }) {
if (expressions.isEmpty) {
return "''";
......@@ -304,7 +319,7 @@ String generateReturnExpr(List<String> expressions, { bool isSingleStringVar = f
} else {
final String string = expressions.reversed.fold<String>('', (String string, String expression) {
if (expression[0] != r'$') {
return generateString(expression) + string;
return expression + string;
}
final RegExp alphanumeric = RegExp(r'^([0-9a-zA-Z]|_)+$');
if (alphanumeric.hasMatch(expression.substring(1)) && !(string.isNotEmpty && alphanumeric.hasMatch(string[0]))) {
......
......@@ -3244,4 +3244,32 @@ NumberFormat.decimalPatternDigits(
);
'''));
});
// Regression test for https://github.com/flutter/flutter/issues/125461.
testWithoutContext('dollar signs are escaped properly when there is a select clause', () {
const String dollarSignWithSelect = r'''
{
"dollarSignWithSelect": "$nice_bug\nHello Bug! Manifistation #1 {selectPlaceholder, select, case{message} other{messageOther}}"
}''';
final Directory l10nDirectory = fs.currentDirectory.childDirectory('lib').childDirectory('l10n')
..createSync(recursive: true);
l10nDirectory.childFile(defaultTemplateArbFileName)
.writeAsStringSync(dollarSignWithSelect);
LocalizationsGenerator(
fileSystem: fs,
inputPathString: defaultL10nPathString,
outputPathString: defaultL10nPathString,
templateArbFileName: defaultTemplateArbFileName,
outputFileString: defaultOutputFileString,
classNameString: defaultClassNameString,
logger: logger,
suppressWarnings: true,
)
..loadResources()
..writeOutputFiles();
final String localizationsFile = fs.file(
fs.path.join(syntheticL10nPackagePath, 'output-localization-file_en.dart'),
).readAsStringSync();
expect(localizationsFile, contains(r'\$nice_bug\nHello Bug! Manifistation #1 $_temp0'));
});
}
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