Dashed locale names like `pt-BR` were misspelled as `pt-br` in several locations. This is a follow up fix to #23146.
177 lines
6.6 KiB
TypeScript
177 lines
6.6 KiB
TypeScript
import * as fs from "fs";
|
|
import * as path from "path";
|
|
import * as xml2js from "xml2js";
|
|
|
|
function main(): void {
|
|
const args = process.argv.slice(2);
|
|
if (args.length !== 3) {
|
|
console.log("Usage:");
|
|
console.log("\tnode generateLocalizedDiagnosticMessages.js <lcl source directory> <output directory> <generated diagnostics map file>");
|
|
return;
|
|
}
|
|
|
|
const inputPath = args[0];
|
|
const outputPath = args[1];
|
|
const diagnosticsMapFilePath = args[2];
|
|
|
|
// generate the lcg file for enu
|
|
generateLCGFile();
|
|
|
|
// generate other langs
|
|
fs.readdir(inputPath, (err, files) => {
|
|
handleError(err);
|
|
files.forEach(visitDirectory);
|
|
});
|
|
|
|
return;
|
|
|
|
function visitDirectory(name: string) {
|
|
const inputFilePath = path.join(inputPath, name, "diagnosticMessages", "diagnosticMessages.generated.json.lcl");
|
|
|
|
fs.readFile(inputFilePath, (err, data) => {
|
|
handleError(err);
|
|
xml2js.parseString(data.toString(), (err, result) => {
|
|
handleError(err);
|
|
if (!result || !result.LCX || !result.LCX.$ || !result.LCX.$.TgtCul) {
|
|
console.error("Unexpected XML file structure. Expected to find result.LCX.$.TgtCul.");
|
|
process.exit(1);
|
|
}
|
|
const outputDirectoryName = getPreferedLocaleName(result.LCX.$.TgtCul);
|
|
if (!outputDirectoryName) {
|
|
console.error(`Invalid output locale name for '${result.LCX.$.TgtCul}'.`);
|
|
process.exit(1);
|
|
}
|
|
writeFile(path.join(outputPath, outputDirectoryName, "diagnosticMessages.generated.json"), xmlObjectToString(result));
|
|
});
|
|
});
|
|
}
|
|
|
|
/**
|
|
* A locale name is based on the language tagging conventions of RFC 4646 (Windows Vista
|
|
* and later), and is represented by LOCALE_SNAME.
|
|
* Generally, the pattern <language>-<REGION> is used. Here, language is a lowercase ISO 639
|
|
* language code. The codes from ISO 639-1 are used when available. Otherwise, codes from
|
|
* ISO 639-2/T are used. REGION specifies an uppercase ISO 3166-1 country/region identifier.
|
|
* For example, the locale name for English (United States) is "en-US" and the locale name
|
|
* for Divehi (Maldives) is "dv-MV".
|
|
*
|
|
* If the locale is a neutral locale (no region), the LOCALE_SNAME value follows the
|
|
* pattern <language>. If it is a neutral locale for which the script is significant, the
|
|
* pattern is <language>-<Script>.
|
|
*
|
|
* More at https://msdn.microsoft.com/en-us/library/windows/desktop/dd373814(v=vs.85).aspx
|
|
*
|
|
* Most of the languages we support are neutral locales, so we want to use the language name.
|
|
* There are three exceptions, zh-CN, zh-TW and pt-BR.
|
|
*/
|
|
function getPreferedLocaleName(localeName: string) {
|
|
switch (localeName) {
|
|
case "zh-CN":
|
|
case "zh-TW":
|
|
case "pt-BR":
|
|
return localeName;
|
|
default:
|
|
return localeName.split("-")[0];
|
|
}
|
|
}
|
|
|
|
function handleError(err: null | object) {
|
|
if (err) {
|
|
console.error(err);
|
|
process.exit(1);
|
|
}
|
|
}
|
|
|
|
function xmlObjectToString(o: any) {
|
|
const out: any = {};
|
|
for (const item of o.LCX.Item[0].Item[0].Item) {
|
|
let ItemId = item.$.ItemId;
|
|
let val = item.Str[0].Tgt ? item.Str[0].Tgt[0].Val[0] : item.Str[0].Val[0];
|
|
|
|
if (typeof ItemId !== "string" || typeof val !== "string") {
|
|
console.error("Unexpected XML file structure");
|
|
process.exit(1);
|
|
}
|
|
|
|
if (ItemId.charAt(0) === ";") {
|
|
ItemId = ItemId.slice(1); // remove leading semicolon
|
|
}
|
|
|
|
val = val.replace(/]5D;/, "]"); // unescape `]`
|
|
out[ItemId] = val;
|
|
}
|
|
return JSON.stringify(out, undefined, 2);
|
|
}
|
|
|
|
|
|
function ensureDirectoryExists(directoryPath: string, action: () => void) {
|
|
fs.exists(directoryPath, exists => {
|
|
if (!exists) {
|
|
const basePath = path.dirname(directoryPath);
|
|
if (basePath !== directoryPath) {
|
|
return ensureDirectoryExists(basePath, () => fs.mkdir(directoryPath, action));
|
|
}
|
|
}
|
|
action();
|
|
});
|
|
}
|
|
|
|
function writeFile(fileName: string, contents: string) {
|
|
ensureDirectoryExists(path.dirname(fileName), () => {
|
|
fs.writeFile(fileName, contents, handleError);
|
|
});
|
|
}
|
|
|
|
function objectToList(o: Record<string, string>) {
|
|
const list: { key: string, value: string }[] = [];
|
|
for (const key in o) {
|
|
list.push({ key, value: o[key] });
|
|
}
|
|
return list;
|
|
}
|
|
|
|
function generateLCGFile() {
|
|
return fs.readFile(diagnosticsMapFilePath, (err, data) => {
|
|
handleError(err);
|
|
writeFile(
|
|
path.join(outputPath, "enu", "diagnosticMessages.generated.json.lcg"),
|
|
getLCGFileXML(
|
|
objectToList(JSON.parse(data.toString()))
|
|
.sort((a, b) => a.key > b.key ? 1 : -1) // lcg sorted by property keys
|
|
.reduce((s, { key, value }) => s + getItemXML(key, value), "")
|
|
));
|
|
});
|
|
|
|
function getItemXML(key: string, value: string) {
|
|
// escape entrt value
|
|
value = value.replace(/]/, "]5D;");
|
|
|
|
return `
|
|
<Item ItemId=";${key}" ItemType="0" PsrId="306" Leaf="true">
|
|
<Str Cat="Text">
|
|
<Val><![CDATA[${value}]]></Val>
|
|
</Str>
|
|
<Disp Icon="Str" />
|
|
</Item>`;
|
|
}
|
|
|
|
function getLCGFileXML(items: string) {
|
|
return `<?xml version="1.0" encoding="utf-8"?>
|
|
<LCX SchemaVersion="6.0" Name="diagnosticMessages.generated.json" PsrId="306" FileType="1" SrcCul="en-US" xmlns="http://schemas.microsoft.com/locstudio/2006/6/lcx">
|
|
<OwnedComments>
|
|
<Cmt Name="Dev" />
|
|
<Cmt Name="LcxAdmin" />
|
|
<Cmt Name="Rccx" />
|
|
</OwnedComments>
|
|
<Item ItemId=";String Table" ItemType="0" PsrId="306" Leaf="false">
|
|
<Disp Icon="Expand" Expand="true" Disp="true" LocTbl="false" />
|
|
<Item ItemId=";Strings" ItemType="0" PsrId="306" Leaf="false">
|
|
<Disp Icon="Str" Disp="true" LocTbl="false" />${items}
|
|
</Item>
|
|
</Item>
|
|
</LCX>`;
|
|
}
|
|
}
|
|
}
|
|
|
|
main();
|