Skip to content

Commit a8b7f7d

Browse files
authoredOct 3, 2017
Include localized diagnostics (microsoft#18702)
* Add lcl files * Add loclalization script * Add localization build targets * use async exists, and add assert * Generate lcg file * Add localize task to gulpFile * Only run loclaize if the generated files neededs update. Also run localize as part of local * Fix lint errors * Linter love * Respond to code review comments
1 parent 3a2c723 commit a8b7f7d

19 files changed

+110848
-17
lines changed
 

‎.gitignore

+6-6
Original file line numberDiff line numberDiff line change
@@ -19,11 +19,6 @@ tests/baselines/local/projectOutput/*
1919
tests/baselines/reference/testresults.tap
2020
tests/services/baselines/prototyping/local/*
2121
tests/services/browser/typescriptServices.js
22-
scripts/authors.js
23-
scripts/configureNightly.js
24-
scripts/processDiagnosticMessages.d.ts
25-
scripts/processDiagnosticMessages.js
26-
scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js
2722
src/harness/*.js
2823
src/compiler/diagnosticInformationMap.generated.ts
2924
src/compiler/diagnosticMessages.generated.json
@@ -43,7 +38,12 @@ scripts/run.bat
4338
scripts/word2md.js
4439
scripts/buildProtocol.js
4540
scripts/ior.js
46-
scripts/buildProtocol.js
41+
scripts/authors.js
42+
scripts/configureNightly.js
43+
scripts/processDiagnosticMessages.d.ts
44+
scripts/processDiagnosticMessages.js
45+
scripts/importDefinitelyTypedTests/importDefinitelyTypedTests.js
46+
scripts/generateLocalizedDiagnosticMessages.js
4747
scripts/*.js.map
4848
scripts/typings/
4949
coverage/

‎.npmignore

+2-1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ internal
55
issue_template.md
66
jenkins.sh
77
lib/README.md
8+
lib/enu
89
netci.groovy
910
pull_request_template.md
1011
scripts
@@ -16,5 +17,5 @@ Jakefile.js
1617
.gitattributes
1718
.settings/
1819
.travis.yml
19-
.vscode/
20+
.vscode/
2021
test.config

‎Gulpfile.ts

+38-6
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,7 @@ const harnessDirectory = "src/harness/";
8787
const libraryDirectory = "src/lib/";
8888
const scriptsDirectory = "scripts/";
8989
const docDirectory = "doc/";
90+
const lclDirectory = "src/loc/lcl";
9091

9192
const builtDirectory = "built/";
9293
const builtLocalDirectory = "built/local/";
@@ -367,6 +368,36 @@ gulp.task(builtGeneratedDiagnosticMessagesJSON, [diagnosticInfoMapTs], (done) =>
367368

368369
gulp.task("generate-diagnostics", "Generates a diagnostic file in TypeScript based on an input JSON file", [diagnosticInfoMapTs]);
369370

371+
// Localize diagnostics script
372+
const generateLocalizedDiagnosticMessagesJs = path.join(scriptsDirectory, "generateLocalizedDiagnosticMessages.js");
373+
const generateLocalizedDiagnosticMessagesTs = path.join(scriptsDirectory, "generateLocalizedDiagnosticMessages.ts");
374+
375+
gulp.task(generateLocalizedDiagnosticMessagesJs, /*help*/ false, [], () => {
376+
const settings: tsc.Settings = getCompilerSettings({
377+
target: "es5",
378+
declaration: false,
379+
removeComments: true,
380+
noResolve: false,
381+
stripInternal: false,
382+
types: ["node", "xml2js"]
383+
}, /*useBuiltCompiler*/ false);
384+
return gulp.src(generateLocalizedDiagnosticMessagesTs)
385+
.pipe(newer(generateLocalizedDiagnosticMessagesJs))
386+
.pipe(sourcemaps.init())
387+
.pipe(tsc(settings))
388+
.pipe(sourcemaps.write("."))
389+
.pipe(gulp.dest("."));
390+
});
391+
392+
// Localize diagnostics
393+
const generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
394+
gulp.task(generatedLCGFile, [generateLocalizedDiagnosticMessagesJs, diagnosticInfoMapTs], (done) => {
395+
if (fs.existsSync(builtLocalDirectory) && needsUpdate(generatedDiagnosticMessagesJSON, generatedLCGFile)) {
396+
exec(host, [generateLocalizedDiagnosticMessagesJs, lclDirectory, builtLocalDirectory, generatedDiagnosticMessagesJSON], done, done);
397+
}
398+
});
399+
400+
gulp.task("localize", [generatedLCGFile]);
370401

371402
const servicesFile = path.join(builtLocalDirectory, "typescriptServices.js");
372403
const standaloneDefinitionsFile = path.join(builtLocalDirectory, "typescriptServices.d.ts");
@@ -494,7 +525,7 @@ gulp.task(typesMapJson, /*help*/ false, [], () => {
494525
});
495526

496527
gulp.task("lssl", "Builds language service server library", [tsserverLibraryFile]);
497-
gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON, tsserverLibraryFile]);
528+
gulp.task("local", "Builds the full compiler and services", [builtLocalCompiler, servicesFile, serverFile, builtGeneratedDiagnosticMessagesJSON, tsserverLibraryFile, "localize"]);
498529
gulp.task("tsc", "Builds only the compiler", [builtLocalCompiler]);
499530

500531
// Generate Markdown spec
@@ -536,15 +567,16 @@ gulp.task("dontUseDebugMode", /*help*/ false, [], (done) => { useDebugMode = fal
536567

537568
gulp.task("VerifyLKG", /*help*/ false, [], () => {
538569
const expectedFiles = [builtLocalCompiler, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, typingsInstallerJs, cancellationTokenJs].concat(libraryTargets);
539-
const missingFiles = expectedFiles.filter(function(f) {
540-
return !fs.existsSync(f);
541-
});
570+
const missingFiles = expectedFiles.
571+
concat(fs.readdirSync(lclDirectory).map(function (d) { return path.join(builtLocalDirectory, d, "diagnosticMessages.generated.json"); })).
572+
concat(generatedLCGFile).
573+
filter(f => !fs.existsSync(f));
542574
if (missingFiles.length > 0) {
543575
throw new Error("Cannot replace the LKG unless all built targets are present in directory " + builtLocalDirectory +
544576
". The following files are missing:\n" + missingFiles.join("\n"));
545577
}
546578
// Copy all the targets into the LKG directory
547-
return gulp.src(expectedFiles).pipe(gulp.dest(LKGDirectory));
579+
return gulp.src([...expectedFiles, path.join(builtLocalDirectory, "**"), `!${path.join(builtLocalDirectory, "tslint")}`, `!${path.join(builtLocalDirectory, "*.*")}`]).pipe(gulp.dest(LKGDirectory));
548580
});
549581

550582
gulp.task("LKGInternal", /*help*/ false, ["lib", "local"]);
@@ -1051,7 +1083,7 @@ gulp.task("lint", "Runs tslint on the compiler sources. Optional arguments are:
10511083
const fileMatcher = cmdLineOptions["files"];
10521084
const files = fileMatcher
10531085
? `src/**/${fileMatcher}`
1054-
: "Gulpfile.ts 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
1086+
: "Gulpfile.ts 'scripts/generateLocalizedDiagnosticMessages.ts' scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
10551087
const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
10561088
console.log("Linting: " + cmd);
10571089
child_process.execSync(cmd, { stdio: [0, 1, 2] });

‎Jakefile.js

+41-4
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ var libraryDirectory = "src/lib/";
1717
var scriptsDirectory = "scripts/";
1818
var unittestsDirectory = "src/harness/unittests/";
1919
var docDirectory = "doc/";
20+
var lclDirectory = "src/loc/lcl";
2021

2122
var builtDirectory = "built/";
2223
var builtLocalDirectory = "built/local/";
@@ -427,7 +428,40 @@ compileFile(processDiagnosticMessagesJs,
427428
[processDiagnosticMessagesTs],
428429
[processDiagnosticMessagesTs],
429430
[],
430-
/*useBuiltCompiler*/ false);
431+
/*useBuiltCompiler*/ false);
432+
433+
// Localize diagnostics script
434+
var generateLocalizedDiagnosticMessagesJs = path.join(scriptsDirectory, "generateLocalizedDiagnosticMessages.js");
435+
var generateLocalizedDiagnosticMessagesTs = path.join(scriptsDirectory, "generateLocalizedDiagnosticMessages.ts");
436+
437+
file(generateLocalizedDiagnosticMessagesTs);
438+
439+
compileFile(generateLocalizedDiagnosticMessagesJs,
440+
[generateLocalizedDiagnosticMessagesTs],
441+
[generateLocalizedDiagnosticMessagesTs],
442+
[],
443+
/*useBuiltCompiler*/ false, { noOutFile: true, types: ["node", "xml2js"] });
444+
445+
// Localize diagnostics
446+
var generatedLCGFile = path.join(builtLocalDirectory, "enu", "diagnosticMessages.generated.json.lcg");
447+
file(generatedLCGFile, [generateLocalizedDiagnosticMessagesJs, diagnosticInfoMapTs, generatedDiagnosticMessagesJSON], function () {
448+
var cmd = host + " " + generateLocalizedDiagnosticMessagesJs + " " + lclDirectory + " " + builtLocalDirectory + " " + generatedDiagnosticMessagesJSON;
449+
console.log(cmd);
450+
var ex = jake.createExec([cmd]);
451+
// Add listeners for output and error
452+
ex.addListener("stdout", function (output) {
453+
process.stdout.write(output);
454+
});
455+
ex.addListener("stderr", function (error) {
456+
process.stderr.write(error);
457+
});
458+
ex.addListener("cmdEnd", function () {
459+
complete();
460+
});
461+
ex.run();
462+
}, { async: true });
463+
464+
task("localize", [generatedLCGFile]);
431465

432466
var buildProtocolTs = path.join(scriptsDirectory, "buildProtocol.ts");
433467
var buildProtocolJs = path.join(scriptsDirectory, "buildProtocol.js");
@@ -644,7 +678,7 @@ task("build-fold-end", [], function () {
644678

645679
// Local target to build the compiler and services
646680
desc("Builds the full compiler and services");
647-
task("local", ["build-fold-start", "generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, buildProtocolDts, builtGeneratedDiagnosticMessagesJSON, "lssl", "build-fold-end"]);
681+
task("local", ["build-fold-start", "generate-diagnostics", "lib", tscFile, servicesFile, nodeDefinitionsFile, serverFile, buildProtocolDts, builtGeneratedDiagnosticMessagesJSON, "lssl", "localize", "build-fold-end"]);
648682

649683
// Local target to build only tsc.js
650684
desc("Builds only the compiler");
@@ -699,7 +733,10 @@ task("generate-spec", [specMd]);
699733
// Makes a new LKG. This target does not build anything, but errors if not all the outputs are present in the built/local directory
700734
desc("Makes a new LKG out of the built js files");
701735
task("LKG", ["clean", "release", "local"].concat(libraryTargets), function () {
702-
var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, cancellationTokenFile, typingsInstallerFile, buildProtocolDts, watchGuardFile].concat(libraryTargets);
736+
var expectedFiles = [tscFile, servicesFile, serverFile, nodePackageFile, nodeDefinitionsFile, standaloneDefinitionsFile, tsserverLibraryFile, tsserverLibraryDefinitionFile, cancellationTokenFile, typingsInstallerFile, buildProtocolDts, watchGuardFile].
737+
concat(libraryTargets).
738+
concat(fs.readdirSync(lclDirectory).map(function (d) { return path.join(builtLocalDirectory, d) })).
739+
concat(path.dirname(generatedLCGFile));
703740
var missingFiles = expectedFiles.filter(function (f) {
704741
return !fs.existsSync(f);
705742
});
@@ -1229,7 +1266,7 @@ task("lint", ["build-rules"], () => {
12291266
const fileMatcher = process.env.f || process.env.file || process.env.files;
12301267
const files = fileMatcher
12311268
? `src/**/${fileMatcher}`
1232-
: "Gulpfile.ts 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
1269+
: "Gulpfile.ts 'scripts/generateLocalizedDiagnosticMessages.ts' 'scripts/tslint/**/*.ts' 'src/**/*.ts' --exclude src/lib/es5.d.ts --exclude 'src/lib/*.generated.d.ts'";
12331270
const cmd = `node node_modules/tslint/bin/tslint ${files} --formatters-dir ./built/local/tslint/formatters --format autolinkableStylish`;
12341271
console.log("Linting: " + cmd);
12351272
jake.exec([cmd], { interactive: true }, () => {

‎package.json

+2
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,8 @@
4949
"@types/q": "latest",
5050
"@types/run-sequence": "latest",
5151
"@types/through2": "latest",
52+
"@types/xml2js": "^0.4.0",
53+
"xml2js": "^0.4.19",
5254
"browser-resolve": "^1.11.2",
5355
"browserify": "latest",
5456
"chai": "latest",
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
import * as fs from "fs";
2+
import * as path from "path";
3+
import * as xml2js from "xml2js";
4+
5+
function main(): void {
6+
const args = process.argv.slice(2);
7+
if (args.length !== 3) {
8+
console.log("Usage:");
9+
console.log("\tnode generateLocalizedDiagnosticMessages.js <lcl source directory> <output directory> <generated diagnostics map file>");
10+
return;
11+
}
12+
13+
const inputPath = args[0];
14+
const outputPath = args[1];
15+
const diagnosticsMapFilePath = args[2];
16+
17+
// generate the lcg file for enu
18+
generateLCGFile();
19+
20+
// generate other langs
21+
fs.readdir(inputPath, (err, files) => {
22+
handleError(err);
23+
files.forEach(visitDirectory);
24+
});
25+
26+
return;
27+
28+
function visitDirectory(name: string) {
29+
const inputFilePath = path.join(inputPath, name, "diagnosticMessages", "diagnosticMessages.generated.json.lcl");
30+
const outputFilePath = path.join(outputPath, name, "diagnosticMessages.generated.json");
31+
fs.readFile(inputFilePath, (err, data) => {
32+
handleError(err);
33+
xml2js.parseString(data.toString(), (err, result) => {
34+
handleError(err);
35+
writeFile(outputFilePath, xmlObjectToString(result));
36+
});
37+
});
38+
}
39+
40+
function handleError(err: null | object) {
41+
if (err) {
42+
console.error(err);
43+
process.exit(1);
44+
}
45+
}
46+
47+
function xmlObjectToString(o: any) {
48+
const out: any = {};
49+
for (const item of o["LCX"]["Item"][0]["Item"][0]["Item"]) {
50+
let ItemId = item["$"]["ItemId"];
51+
let Val = item["Str"][0]["Tgt"] ? item["Str"][0]["Tgt"][0]["Val"][0] : item["Str"][0]["Val"][0];
52+
53+
if (typeof ItemId !== "string" || typeof Val !== "string") {
54+
console.error("Unexpected XML file structure");
55+
process.exit(1);
56+
}
57+
58+
if (ItemId.charAt(0) === ";") {
59+
ItemId = ItemId.slice(1); // remove leading semicolon
60+
}
61+
62+
Val = Val.replace(/]5D;/, "]"); // unescape `]`
63+
out[ItemId] = Val;
64+
}
65+
return JSON.stringify(out, undefined, 2);
66+
}
67+
68+
69+
function ensureDirectoryExists(directoryPath: string, action: () => void) {
70+
fs.exists(directoryPath, exists => {
71+
if (!exists) {
72+
const basePath = path.dirname(directoryPath);
73+
if (basePath !== directoryPath) {
74+
return ensureDirectoryExists(basePath, () => fs.mkdir(directoryPath, action));
75+
}
76+
}
77+
action();
78+
});
79+
}
80+
81+
function writeFile(fileName: string, contents: string) {
82+
ensureDirectoryExists(path.dirname(fileName), () => {
83+
fs.writeFile(fileName, contents, handleError);
84+
});
85+
}
86+
87+
function objectToList(o: Record<string, string>) {
88+
const list: { key: string, value: string }[] = [];
89+
for (const key in o) {
90+
list.push({ key, value: o[key] });
91+
}
92+
return list;
93+
}
94+
95+
function generateLCGFile() {
96+
return fs.readFile(diagnosticsMapFilePath, (err, data) => {
97+
handleError(err);
98+
writeFile(
99+
path.join(outputPath, "enu", "diagnosticMessages.generated.json.lcg"),
100+
getLCGFileXML(
101+
objectToList(JSON.parse(data.toString()))
102+
.sort((a, b) => a.key > b.key ? 1 : -1) // lcg sorted by property keys
103+
.reduce((s, { key, value }) => s + getItemXML(key, value), "")
104+
));
105+
});
106+
107+
function getItemXML(key: string, value: string) {
108+
// escape entrt value
109+
value = value.replace(/]/, "]5D;");
110+
111+
return `
112+
<Item ItemId=";${key}" ItemType="0" PsrId="306" Leaf="true">
113+
<Str Cat="Text">
114+
<Val><![CDATA[${value}]]></Val>
115+
</Str>
116+
<Disp Icon="Str" />
117+
</Item>`;
118+
}
119+
120+
function getLCGFileXML(items: string) {
121+
return `<?xml version="1.0" encoding="utf-8"?>
122+
<LCX SchemaVersion="6.0" Name="diagnosticMessages.generated.json" PsrId="306" FileType="1" SrcCul="en-US" xmlns="http://schemas.microsoft.com/locstudio/2006/6/lcx">
123+
<OwnedComments>
124+
<Cmt Name="Dev" />
125+
<Cmt Name="LcxAdmin" />
126+
<Cmt Name="Rccx" />
127+
</OwnedComments>
128+
<Item ItemId=";String Table" ItemType="0" PsrId="306" Leaf="false">
129+
<Disp Icon="Expand" Expand="true" Disp="true" LocTbl="false" />
130+
<Item ItemId=";Strings" ItemType="0" PsrId="306" Leaf="false">
131+
<Disp Icon="Str" Disp="true" LocTbl="false" />${items}
132+
</Item>
133+
</Item>
134+
</LCX>`;
135+
}
136+
}
137+
}
138+
139+
main();

0 commit comments

Comments
 (0)
Please sign in to comment.