Skip to content

Commit 5fa8db5

Browse files
authored
Fix microsoft#19270: ensure output name is a valid locale name (microsoft#19308)
* Fix microsoft#19270: ensure output name is a valid locale name * Use const instead of var * Add comment * Fix typo * Split the concat logic for generatedLCGFile
1 parent 7bfda06 commit 5fa8db5

File tree

3 files changed

+76
-8
lines changed

3 files changed

+76
-8
lines changed

Gulpfile.ts

+18-3
Original file line numberDiff line numberDiff line change
@@ -180,6 +180,23 @@ const libraryTargets = librarySourceMap.map(function(f) {
180180
return path.join(builtLocalDirectory, f.target);
181181
});
182182

183+
/**
184+
* .lcg file is what localization team uses to know what messages to localize.
185+
* The file is always generated in 'enu\diagnosticMessages.generated.json.lcg'
186+
*/
187+
const generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
188+
189+
/**
190+
* The localization target produces the two following transformations:
191+
* 1. 'src\loc\lcl\<locale>\diagnosticMessages.generated.json.lcl' => 'built\local\<locale>\diagnosticMessages.generated.json'
192+
* convert localized resources into a .json file the compiler can understand
193+
* 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
194+
* generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
195+
*/
196+
const localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"].map(function (f) {
197+
return path.join(builtLocalDirectory, f, "diagnosticMessages.generated.json");
198+
}).concat(generatedLCGFile);
199+
183200
for (const i in libraryTargets) {
184201
const entry = librarySourceMap[i];
185202
const target = libraryTargets[i];
@@ -398,7 +415,6 @@ gulp.task(generateLocalizedDiagnosticMessagesJs, /*help*/ false, [], () => {
398415
});
399416

400417
// Localize diagnostics
401-
const generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
402418
gulp.task(generatedLCGFile, [generateLocalizedDiagnosticMessagesJs, diagnosticInfoMapTs], (done) => {
403419
if (fs.existsSync(builtLocalDirectory) && needsUpdate(generatedDiagnosticMessagesJSON, generatedLCGFile)) {
404420
exec(host, [generateLocalizedDiagnosticMessagesJs, lclDirectory, builtLocalDirectory, generatedDiagnosticMessagesJSON], done, done);
@@ -576,8 +592,7 @@ gulp.task("dontUseDebugMode", /*help*/ false, [], (done) => { useDebugMode = fal
576592
gulp.task("VerifyLKG", /*help*/ false, [], () => {
577593
const expectedFiles = [builtLocalCompiler, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, typingsInstallerJs, cancellationTokenJs].concat(libraryTargets);
578594
const missingFiles = expectedFiles.
579-
concat(fs.readdirSync(lclDirectory).map(function (d) { return path.join(builtLocalDirectory, d, "diagnosticMessages.generated.json"); })).
580-
concat(generatedLCGFile).
595+
concat(localizationTargets).
581596
filter(f => !fs.existsSync(f));
582597
if (missingFiles.length > 0) {
583598
throw new Error("Cannot replace the LKG unless all built targets are present in directory " + builtLocalDirectory +

Jakefile.js

+18-3
Original file line numberDiff line numberDiff line change
@@ -239,6 +239,23 @@ var libraryTargets = librarySourceMap.map(function (f) {
239239
return path.join(builtLocalDirectory, f.target);
240240
});
241241

242+
/**
243+
* .lcg file is what localization team uses to know what messages to localize.
244+
* The file is always generated in 'enu\diagnosticMessages.generated.json.lcg'
245+
*/
246+
var generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
247+
248+
/**
249+
* The localization target produces the two following transformations:
250+
* 1. 'src\loc\lcl\<locale>\diagnosticMessages.generated.json.lcl' => 'built\local\<locale>\diagnosticMessages.generated.json'
251+
* convert localized resources into a .json file the compiler can understand
252+
* 2. 'src\compiler\diagnosticMessages.generated.json' => 'built\local\ENU\diagnosticMessages.generated.json.lcg'
253+
* generate the lcg file (source of messages to localize) from the diagnosticMessages.generated.json
254+
*/
255+
var localizationTargets = ["cs", "de", "es", "fr", "it", "ja", "ko", "pl", "pt-BR", "ru", "tr", "zh-CN", "zh-TW"].map(function (f) {
256+
return path.join(builtLocalDirectory, f);
257+
}).concat(path.dirname(generatedLCGFile));
258+
242259
// Prepends the contents of prefixFile to destinationFile
243260
function prependFile(prefixFile, destinationFile) {
244261
if (!fs.existsSync(prefixFile)) {
@@ -444,7 +461,6 @@ compileFile(generateLocalizedDiagnosticMessagesJs,
444461
/*useBuiltCompiler*/ false, { noOutFile: true, types: ["node", "xml2js"] });
445462

446463
// Localize diagnostics
447-
var generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
448464
file(generatedLCGFile, [generateLocalizedDiagnosticMessagesJs, diagnosticInfoMapTs, generatedDiagnosticMessagesJSON], function () {
449465
var cmd = host + " " + generateLocalizedDiagnosticMessagesJs + " " + lclDirectory + " " + builtLocalDirectory + " " + generatedDiagnosticMessagesJSON;
450466
console.log(cmd);
@@ -736,8 +752,7 @@ desc("Makes a new LKG out of the built js files");
736752
task("LKG", ["clean", "release", "local"].concat(libraryTargets), function () {
737753
var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, cancellationTokenFile, typingsInstallerFile, buildProtocolDts, watchGuardFile].
738754
concat(libraryTargets).
739-
concat(fs.readdirSync(lclDirectory).map(function (d) { return path.join(builtLocalDirectory, d) })).
740-
concat(path.dirname(generatedLCGFile));
755+
concat(localizationTargets);
741756
var missingFiles = expectedFiles.filter(function (f) {
742757
return !fs.existsSync(f);
743758
});

scripts/generateLocalizedDiagnosticMessages.ts

+40-2
Original file line numberDiff line numberDiff line change
@@ -27,16 +27,54 @@ function main(): void {
2727

2828
function visitDirectory(name: string) {
2929
const inputFilePath = path.join(inputPath, name, "diagnosticMessages", "diagnosticMessages.generated.json.lcl");
30-
const outputFilePath = path.join(outputPath, name, "diagnosticMessages.generated.json");
30+
3131
fs.readFile(inputFilePath, (err, data) => {
3232
handleError(err);
3333
xml2js.parseString(data.toString(), (err, result) => {
3434
handleError(err);
35-
writeFile(outputFilePath, xmlObjectToString(result));
35+
if (!result || !result.LCX || !result.LCX.$ || !result.LCX.$.TgtCul) {
36+
console.error("Unexpected XML file structure. Expected to find result.LCX.$.TgtCul.");
37+
process.exit(1);
38+
}
39+
const outputDirectoryName = getPreferedLocaleName(result.LCX.$.TgtCul);
40+
if (!outputDirectoryName) {
41+
console.error(`Invalid output locale name for '${result.LCX.$.TgtCul}'.`);
42+
process.exit(1);
43+
}
44+
writeFile(path.join(outputPath, outputDirectoryName, "diagnosticMessages.generated.json"), xmlObjectToString(result));
3645
});
3746
});
3847
}
3948

49+
/**
50+
* A locale name is based on the language tagging conventions of RFC 4646 (Windows Vista
51+
* and later), and is represented by LOCALE_SNAME.
52+
* Generally, the pattern <language>-<REGION> is used. Here, language is a lowercase ISO 639
53+
* language code. The codes from ISO 639-1 are used when available. Otherwise, codes from
54+
* ISO 639-2/T are used. REGION specifies an uppercase ISO 3166-1 country/region identifier.
55+
* For example, the locale name for English (United States) is "en-US" and the locale name
56+
* for Divehi (Maldives) is "dv-MV".
57+
*
58+
* If the locale is a neutral locale (no region), the LOCALE_SNAME value follows the
59+
* pattern <language>. If it is a neutral locale for which the script is significant, the
60+
* pattern is <language>-<Script>.
61+
*
62+
* More at https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx
63+
*
64+
* Most of the languages we support are neutral locales, so we want to use the language name.
65+
* There are three exceptions, zh-CN, zh-TW and pt-BR.
66+
*/
67+
function getPreferedLocaleName(localeName: string) {
68+
switch (localeName) {
69+
case "zh-CN":
70+
case "zh-TW":
71+
case "pt-BR":
72+
return localeName;
73+
default:
74+
return localeName.split("-")[0];
75+
}
76+
}
77+
4078
function handleError(err: null | object) {
4179
if (err) {
4280
console.error(err);

0 commit comments

Comments
 (0)