mirror of
https://github.com/flutter/flutter.git
synced 2026-02-20 02:29:02 +08:00
🐛 Normalize generated file paths for the l10n generator (#169467)
Multiple places use non-normalized file paths when generating the localization files. The PR normalizes file paths when generating the file list JSON file and the dependency file. Fixes https://github.com/flutter/flutter/issues/163591 ## Pre-launch Checklist - [x] I read the [Contributor Guide] and followed the process outlined there for submitting PRs. - [x] I read the [Tree Hygiene] wiki page, which explains my responsibilities. - [x] I read and followed the [Flutter Style Guide], including [Features we expect every widget to implement]. - [x] I signed the [CLA]. - [x] I listed at least one issue that this PR fixes in the description above. - [x] I updated/added relevant documentation (doc comments with `///`). - [x] I added new tests to check the change I am making, or this PR is [test-exempt]. - [ ] I followed the [breaking change policy] and added [Data Driven Fixes] where supported. - [x] All existing and new tests are passing. If you need help, consider asking for advice on the #hackers-new channel on [Discord]. <!-- Links --> [Contributor Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#overview [Tree Hygiene]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md [test-exempt]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#tests [Flutter Style Guide]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md [Features we expect every widget to implement]: https://github.com/flutter/flutter/blob/main/docs/contributing/Style-guide-for-Flutter-repo.md#features-we-expect-every-widget-to-implement [CLA]: https://cla.developers.google.com/ [flutter/tests]: https://github.com/flutter/tests [breaking change policy]: https://github.com/flutter/flutter/blob/main/docs/contributing/Tree-hygiene.md#handling-breaking-changes [Discord]: https://github.com/flutter/flutter/blob/main/docs/contributing/Chat.md [Data Driven Fixes]: https://github.com/flutter/flutter/blob/main/docs/contributing/Data-driven-Fixes.md
This commit is contained in:
parent
8e76fb2946
commit
4e75d56bda
@ -58,6 +58,7 @@ class GenerateLocalizationsTarget extends Target {
|
||||
final LocalizationOptions options = parseLocalizationsOptionsFromYAML(
|
||||
file: configFile,
|
||||
logger: environment.logger,
|
||||
fileSystem: environment.fileSystem,
|
||||
defaultArbDir: defaultArbDir,
|
||||
defaultSyntheticPackage: !featureFlags.isExplicitPackageDependenciesEnabled,
|
||||
);
|
||||
|
||||
@ -260,6 +260,7 @@ class GenerateLocalizationsCommand extends FlutterCommand {
|
||||
options = parseLocalizationsOptionsFromYAML(
|
||||
file: _fileSystem.file('l10n.yaml'),
|
||||
logger: _logger,
|
||||
fileSystem: _fileSystem,
|
||||
defaultArbDir: defaultArbDir,
|
||||
defaultSyntheticPackage: !featureFlags.isExplicitPackageDependenciesEnabled,
|
||||
);
|
||||
|
||||
@ -1000,6 +1000,14 @@ class LocalizationsGenerator {
|
||||
return true;
|
||||
}
|
||||
|
||||
void _addToFileList(List<String> fileList, String path) {
|
||||
fileList.add(_fs.path.normalize(path));
|
||||
}
|
||||
|
||||
void _addAllToFileList(List<String> fileList, Iterable<String> paths) {
|
||||
fileList.addAll(paths.map(_fs.path.normalize));
|
||||
}
|
||||
|
||||
// Load _allMessages from templateArbFile and _allBundles from all of the ARB
|
||||
// files in inputDirectory. Also initialized: supportedLocales.
|
||||
void loadResources() {
|
||||
@ -1030,7 +1038,8 @@ class LocalizationsGenerator {
|
||||
.toList();
|
||||
hadErrors = _allMessages.any((Message message) => message.hadErrors);
|
||||
if (inputsAndOutputsListFile != null) {
|
||||
_inputFileList.addAll(
|
||||
_addAllToFileList(
|
||||
_inputFileList,
|
||||
_allBundles.bundles.map((AppResourceBundle bundle) {
|
||||
return bundle.file.absolute.path;
|
||||
}),
|
||||
@ -1492,7 +1501,7 @@ The plural cases must be one of "=0", "=1", "=2", "zero", "one", "two", "few", "
|
||||
// Generate the required files for localizations.
|
||||
_languageFileMap.forEach((File file, String contents) {
|
||||
file.writeAsStringSync(useCRLF ? contents.replaceAll('\n', '\r\n') : contents);
|
||||
_outputFileList.add(file.absolute.path);
|
||||
_addToFileList(_outputFileList, file.absolute.path);
|
||||
});
|
||||
|
||||
baseOutputFile.writeAsStringSync(
|
||||
@ -1527,7 +1536,7 @@ The plural cases must be one of "=0", "=1", "=2", "zero", "one", "two", "few", "
|
||||
);
|
||||
}
|
||||
final File? inputsAndOutputsListFileLocal = inputsAndOutputsListFile;
|
||||
_outputFileList.add(baseOutputFile.absolute.path);
|
||||
_addToFileList(_outputFileList, baseOutputFile.absolute.path);
|
||||
if (inputsAndOutputsListFileLocal != null) {
|
||||
// Generate a JSON file containing the inputs and outputs of the gen_l10n script.
|
||||
if (!inputsAndOutputsListFileLocal.existsSync()) {
|
||||
@ -1548,7 +1557,7 @@ The plural cases must be one of "=0", "=1", "=2", "zero", "one", "two", "few", "
|
||||
void _generateUntranslatedMessagesFile(Logger logger, File untranslatedMessagesFile) {
|
||||
if (_unimplementedMessages.isEmpty) {
|
||||
untranslatedMessagesFile.writeAsStringSync('{}');
|
||||
_outputFileList.add(untranslatedMessagesFile.absolute.path);
|
||||
_addToFileList(_outputFileList, untranslatedMessagesFile.absolute.path);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -1576,6 +1585,6 @@ The plural cases must be one of "=0", "=1", "=2", "zero", "one", "two", "few", "
|
||||
|
||||
resultingFile += '}\n';
|
||||
untranslatedMessagesFile.writeAsStringSync(resultingFile);
|
||||
_outputFileList.add(untranslatedMessagesFile.absolute.path);
|
||||
_addToFileList(_outputFileList, untranslatedMessagesFile.absolute.path);
|
||||
}
|
||||
}
|
||||
|
||||
@ -479,6 +479,7 @@ class LocalizationOptions {
|
||||
LocalizationOptions parseLocalizationsOptionsFromYAML({
|
||||
required File file,
|
||||
required Logger logger,
|
||||
required FileSystem fileSystem,
|
||||
required String defaultArbDir,
|
||||
required bool defaultSyntheticPackage,
|
||||
}) {
|
||||
@ -497,14 +498,24 @@ LocalizationOptions parseLocalizationsOptionsFromYAML({
|
||||
throw Exception();
|
||||
}
|
||||
return LocalizationOptions(
|
||||
arbDir: _tryReadUri(yamlNode, 'arb-dir', logger)?.path ?? defaultArbDir,
|
||||
outputDir: _tryReadUri(yamlNode, 'output-dir', logger)?.path,
|
||||
templateArbFile: _tryReadUri(yamlNode, 'template-arb-file', logger)?.path,
|
||||
outputLocalizationFile: _tryReadUri(yamlNode, 'output-localization-file', logger)?.path,
|
||||
untranslatedMessagesFile: _tryReadUri(yamlNode, 'untranslated-messages-file', logger)?.path,
|
||||
arbDir: _tryReadFilePath(yamlNode, 'arb-dir', logger, fileSystem) ?? defaultArbDir,
|
||||
outputDir: _tryReadFilePath(yamlNode, 'output-dir', logger, fileSystem),
|
||||
templateArbFile: _tryReadFilePath(yamlNode, 'template-arb-file', logger, fileSystem),
|
||||
outputLocalizationFile: _tryReadFilePath(
|
||||
yamlNode,
|
||||
'output-localization-file',
|
||||
logger,
|
||||
fileSystem,
|
||||
),
|
||||
untranslatedMessagesFile: _tryReadFilePath(
|
||||
yamlNode,
|
||||
'untranslated-messages-file',
|
||||
logger,
|
||||
fileSystem,
|
||||
),
|
||||
outputClass: _tryReadString(yamlNode, 'output-class', logger),
|
||||
header: _tryReadString(yamlNode, 'header', logger),
|
||||
headerFile: _tryReadUri(yamlNode, 'header-file', logger)?.path,
|
||||
headerFile: _tryReadFilePath(yamlNode, 'header-file', logger, fileSystem),
|
||||
useDeferredLoading: _tryReadBool(yamlNode, 'use-deferred-loading', logger),
|
||||
preferredSupportedLocales: _tryReadStringList(yamlNode, 'preferred-supported-locales', logger),
|
||||
syntheticPackage:
|
||||
@ -596,8 +607,8 @@ List<String>? _tryReadStringList(YamlMap yamlMap, String key, Logger logger) {
|
||||
throw Exception();
|
||||
}
|
||||
|
||||
// Try to read a valid `Uri` or null from `yamlMap`, otherwise throw.
|
||||
Uri? _tryReadUri(YamlMap yamlMap, String key, Logger logger) {
|
||||
// Try to read a valid file `Uri` or null from `yamlMap` to file path, otherwise throw.
|
||||
String? _tryReadFilePath(YamlMap yamlMap, String key, Logger logger, FileSystem fileSystem) {
|
||||
final String? value = _tryReadString(yamlMap, key, logger);
|
||||
if (value == null) {
|
||||
return null;
|
||||
@ -606,5 +617,5 @@ Uri? _tryReadUri(YamlMap yamlMap, String key, Logger logger) {
|
||||
if (uri == null) {
|
||||
logger.printError('"$value" must be a relative file URI');
|
||||
}
|
||||
return uri;
|
||||
return uri != null ? fileSystem.path.normalize(uri.path) : null;
|
||||
}
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/build_system/build_system.dart';
|
||||
import 'package:flutter_tools/src/build_system/depfile.dart' show Depfile;
|
||||
import 'package:flutter_tools/src/build_system/targets/localizations.dart';
|
||||
import 'package:flutter_tools/src/cache.dart';
|
||||
import 'package:flutter_tools/src/commands/generate_localizations.dart';
|
||||
@ -516,6 +517,42 @@ format: true
|
||||
},
|
||||
);
|
||||
|
||||
testUsingContext('generates normalized input & output file paths', () async {
|
||||
final File arbFile = fileSystem.file(fileSystem.path.join('lib', 'l10n', 'app_en.arb'))
|
||||
..createSync(recursive: true);
|
||||
arbFile.writeAsStringSync('''
|
||||
{
|
||||
"helloWorld": "Hello, World!"
|
||||
}''');
|
||||
final File configFile = fileSystem.file('l10n.yaml')..createSync();
|
||||
// Writing both forward and backward slashes to test both cases.
|
||||
configFile.writeAsStringSync(r'''
|
||||
arb-dir: lib/l10n
|
||||
output-dir: lib\l10n
|
||||
format: false
|
||||
''');
|
||||
final File pubspecFile = fileSystem.file('pubspec.yaml')..createSync();
|
||||
pubspecFile.writeAsStringSync(BasicProjectWithFlutterGen().pubspec);
|
||||
|
||||
processManager.addCommand(const FakeCommand(command: <String>[]));
|
||||
final Environment environment = Environment.test(
|
||||
fileSystem.currentDirectory,
|
||||
artifacts: artifacts,
|
||||
processManager: processManager,
|
||||
fileSystem: fileSystem,
|
||||
logger: BufferLogger.test(),
|
||||
);
|
||||
const Target buildTarget = GenerateLocalizationsTarget();
|
||||
await buildTarget.build(environment);
|
||||
|
||||
final File dependencyFile = environment.buildDir.childFile(buildTarget.depfiles.single);
|
||||
final Depfile depfile = environment.depFileService.parse(dependencyFile);
|
||||
|
||||
final String oppositeSeparator = fileSystem.path.separator == '/' ? r'\' : '/';
|
||||
expect(depfile.inputs, everyElement(isNot(contains(oppositeSeparator))));
|
||||
expect(depfile.outputs, everyElement(isNot(contains(oppositeSeparator))));
|
||||
});
|
||||
|
||||
testUsingContext(
|
||||
'nullable-getter defaults to true',
|
||||
() async {
|
||||
|
||||
@ -51,6 +51,7 @@ nullable-getter: false
|
||||
final LocalizationOptions options = parseLocalizationsOptionsFromYAML(
|
||||
file: configFile,
|
||||
logger: BufferLogger.test(),
|
||||
fileSystem: fileSystem,
|
||||
defaultArbDir: fileSystem.path.join('lib', 'l10n'),
|
||||
defaultSyntheticPackage: true,
|
||||
);
|
||||
@ -90,6 +91,7 @@ nullable-getter: false
|
||||
final LocalizationOptions options = parseLocalizationsOptionsFromYAML(
|
||||
file: configFile,
|
||||
logger: BufferLogger.test(),
|
||||
fileSystem: fileSystem,
|
||||
defaultArbDir: fileSystem.path.join('lib', 'l10n'),
|
||||
defaultSyntheticPackage: true,
|
||||
);
|
||||
@ -118,6 +120,7 @@ nullable-getter: false
|
||||
final LocalizationOptions options = parseLocalizationsOptionsFromYAML(
|
||||
file: configFile,
|
||||
logger: BufferLogger.test(),
|
||||
fileSystem: fileSystem,
|
||||
defaultArbDir: fileSystem.path.join('lib', 'l10n'),
|
||||
defaultSyntheticPackage: false,
|
||||
);
|
||||
@ -136,6 +139,7 @@ preferred-supported-locales: ['en_US', 'de']
|
||||
final LocalizationOptions options = parseLocalizationsOptionsFromYAML(
|
||||
file: configFile,
|
||||
logger: BufferLogger.test(),
|
||||
fileSystem: fileSystem,
|
||||
defaultArbDir: fileSystem.path.join('lib', 'l10n'),
|
||||
defaultSyntheticPackage: true,
|
||||
);
|
||||
@ -156,6 +160,7 @@ use-deferred-loading: string
|
||||
() => parseLocalizationsOptionsFromYAML(
|
||||
file: configFile,
|
||||
logger: BufferLogger.test(),
|
||||
fileSystem: fileSystem,
|
||||
defaultArbDir: fileSystem.path.join('lib', 'l10n'),
|
||||
defaultSyntheticPackage: true,
|
||||
),
|
||||
@ -174,6 +179,7 @@ template-arb-file: {name}_en.arb
|
||||
() => parseLocalizationsOptionsFromYAML(
|
||||
file: configFile,
|
||||
logger: BufferLogger.test(),
|
||||
fileSystem: fileSystem,
|
||||
defaultArbDir: fileSystem.path.join('lib', 'l10n'),
|
||||
defaultSyntheticPackage: true,
|
||||
),
|
||||
|
||||
@ -7,6 +7,7 @@ import 'package:flutter_tools/src/artifacts.dart';
|
||||
import 'package:flutter_tools/src/base/file_system.dart';
|
||||
import 'package:flutter_tools/src/base/logger.dart';
|
||||
import 'package:flutter_tools/src/convert.dart';
|
||||
import 'package:flutter_tools/src/globals.dart' as globals show platform;
|
||||
import 'package:flutter_tools/src/localizations/gen_l10n.dart';
|
||||
import 'package:flutter_tools/src/localizations/gen_l10n_types.dart';
|
||||
import 'package:flutter_tools/src/localizations/localizations_utils.dart';
|
||||
@ -89,8 +90,10 @@ void main() {
|
||||
bool relaxSyntax = false,
|
||||
bool useNamedParameters = false,
|
||||
void Function(Directory)? setup,
|
||||
FileSystem? fileSystem,
|
||||
}) {
|
||||
final Directory l10nDirectory = fs.directory(defaultL10nPath)..createSync(recursive: true);
|
||||
final Directory l10nDirectory = (fileSystem ?? fs).directory(defaultL10nPath)
|
||||
..createSync(recursive: true);
|
||||
for (final String locale in localeToArbFile.keys) {
|
||||
l10nDirectory.childFile('app_$locale.arb').writeAsStringSync(localeToArbFile[locale]!);
|
||||
}
|
||||
@ -98,7 +101,7 @@ void main() {
|
||||
setup(l10nDirectory);
|
||||
}
|
||||
return LocalizationsGenerator(
|
||||
fileSystem: fs,
|
||||
fileSystem: fileSystem ?? fs,
|
||||
inputPathString: l10nDirectory.path,
|
||||
outputPathString: outputPathString ?? l10nDirectory.path,
|
||||
templateArbFileName: defaultTemplateArbFileName,
|
||||
@ -613,6 +616,29 @@ void main() {
|
||||
);
|
||||
});
|
||||
|
||||
testUsingContext('generates normalized input & output file paths', () {
|
||||
final FileSystem fs = MemoryFileSystem.test(
|
||||
style: globals.platform.isWindows ? FileSystemStyle.windows : FileSystemStyle.posix,
|
||||
);
|
||||
setupLocalizations(
|
||||
<String, String>{'en': singleMessageArbFileString, 'es': singleEsMessageArbFileString},
|
||||
fileSystem: fs,
|
||||
inputsAndOutputsListPath: defaultL10nPath,
|
||||
);
|
||||
final File inputsAndOutputsList = fs.file(
|
||||
fs.path.join(defaultL10nPath, 'gen_l10n_inputs_and_outputs.json'),
|
||||
);
|
||||
expect(inputsAndOutputsList.existsSync(), isTrue);
|
||||
|
||||
final Map<String, dynamic> jsonResult =
|
||||
json.decode(inputsAndOutputsList.readAsStringSync()) as Map<String, dynamic>;
|
||||
final String oppositeSeparator = globals.platform.isWindows ? '/' : r'\';
|
||||
final List<dynamic> inputList = jsonResult['inputs'] as List<dynamic>;
|
||||
expect(inputList, everyElement(isNot(contains(oppositeSeparator))));
|
||||
final List<dynamic> outputList = jsonResult['outputs'] as List<dynamic>;
|
||||
expect(outputList, everyElement(isNot(contains(oppositeSeparator))));
|
||||
});
|
||||
|
||||
testWithoutContext('setting both a headerString and a headerFile should fail', () {
|
||||
expect(
|
||||
() {
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user