UNPKG

@criticalmanufacturing/dev-i18n-transform

Version:
157 lines (156 loc) 15.8 kB
"use strict"; //#region Imports var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } return new (P || (P = Promise))(function (resolve, reject) { function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } step((generator = generator.apply(thisArg, _arguments || [])).next()); }); }; var __generator = (this && this.__generator) || function (thisArg, body) { var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; function verb(n) { return function (v) { return step([n, v]); }; } function step(op) { if (f) throw new TypeError("Generator is already executing."); while (_) try { if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t; if (y = 0, t) op = [op[0] & 2, t.value]; switch (op[0]) { case 0: case 1: t = op; break; case 4: _.label++; return { value: op[1], done: false }; case 5: _.label++; y = op[1]; op = [0]; continue; case 7: op = _.ops.pop(); _.trys.pop(); continue; default: if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } if (t[2]) _.ops.pop(); _.trys.pop(); continue; } op = body.call(thisArg, _); } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; } }; exports.__esModule = true; var fs = require("fs"); //#endregion var ResourcesConverter = /** @class */ (function () { function ResourcesConverter() { //#region Private Properties /** * In case of create a new file, this is the header used */ this.fileHeader = "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n <root>\n <!--\n Microsoft ResX Schema\n\n Version 2.0\n\n The primary goals of this format is to allow a simple XML format\n that is mostly human readable. The generation and parsing of the\n various data types are done through the TypeConverter classes\n associated with the data types.\n\n Example:\n\n ... ado.net/XML headers & schema ...\n <resheader name=\"resmimetype\">text/microsoft-resx</resheader>\n <resheader name=\"version\">2.0</resheader>\n <resheader name=\"reader\">System.Resources.ResXResourceReader, System.Windows.Forms, ...</resheader>\n <resheader name=\"writer\">System.Resources.ResXResourceWriter, System.Windows.Forms, ...</resheader>\n <data name=\"Name1\"><value>this is my long string</value><comment>this is a comment</comment></data>\n <data name=\"Color1\" type=\"System.Drawing.Color, System.Drawing\">Blue</data>\n <data name=\"Bitmap1\" mimetype=\"application/x-microsoft.net.object.binary.base64\">\n <value>[base64 mime encoded serialized .NET Framework object]</value>\n </data>\n <data name=\"Icon1\" type=\"System.Drawing.Icon, System.Drawing\" mimetype=\"application/x-microsoft.net.object.bytearray.base64\">\n <value>[base64 mime encoded string representing a byte array form of the .NET Framework object]</value>\n <comment>This is a comment</comment>\n </data>\n\n There are any number of \"resheader\" rows that contain simple\n name/value pairs.\n\n Each data row contains a name, and value. The row also contains a\n type or mimetype. Type corresponds to a .NET class that support\n text/value conversion through the TypeConverter architecture.\n Classes that don't support this are serialized and stored with the\n mimetype set.\n\n The mimetype is used for serialized objects, and tells the\n ResXResourceReader how to depersist the object. This is currently not\n extensible. For a given mimetype the value must be set accordingly:\n\n Note - application/x-microsoft.net.object.binary.base64 is the format\n that the ResXResourceWriter will generate, however the reader can\n read any of the formats listed below.\n\n mimetype: application/x-microsoft.net.object.binary.base64\n value : The object must be serialized with\n : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter\n : and then encoded with base64 encoding.\n\n mimetype: application/x-microsoft.net.object.soap.base64\n value : The object must be serialized with\n : System.Runtime.Serialization.Formatters.Soap.SoapFormatter\n : and then encoded with base64 encoding.\n\n mimetype: application/x-microsoft.net.object.bytearray.base64\n value : The object must be serialized into a byte array\n : using a System.ComponentModel.TypeConverter\n : and then encoded with base64 encoding.\n -->\n <xsd:schema id=\"root\" xmlns=\"\" xmlns:xsd=\"http://www.w3.org/2001/XMLSchema\" xmlns:msdata=\"urn:schemas-microsoft-com:xml-msdata\">\n <xsd:import namespace=\"http://www.w3.org/XML/1998/namespace\" />\n <xsd:element name=\"root\" msdata:IsDataSet=\"true\">\n <xsd:complexType>\n <xsd:choice maxOccurs=\"unbounded\">\n <xsd:element name=\"metadata\">\n <xsd:complexType>\n <xsd:sequence>\n <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" />\n </xsd:sequence>\n <xsd:attribute name=\"name\" use=\"required\" type=\"xsd:string\" />\n <xsd:attribute name=\"type\" type=\"xsd:string\" />\n <xsd:attribute name=\"mimetype\" type=\"xsd:string\" />\n <xsd:attribute ref=\"xml:space\" />\n </xsd:complexType>\n </xsd:element>\n <xsd:element name=\"assembly\">\n <xsd:complexType>\n <xsd:attribute name=\"alias\" type=\"xsd:string\" />\n <xsd:attribute name=\"name\" type=\"xsd:string\" />\n </xsd:complexType>\n </xsd:element>\n <xsd:element name=\"data\">\n <xsd:complexType>\n <xsd:sequence>\n <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n <xsd:element name=\"comment\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"2\" />\n </xsd:sequence>\n <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" msdata:Ordinal=\"1\" />\n <xsd:attribute name=\"type\" type=\"xsd:string\" msdata:Ordinal=\"3\" />\n <xsd:attribute name=\"mimetype\" type=\"xsd:string\" msdata:Ordinal=\"4\" />\n <xsd:attribute ref=\"xml:space\" />\n </xsd:complexType>\n </xsd:element>\n <xsd:element name=\"resheader\">\n <xsd:complexType>\n <xsd:sequence>\n <xsd:element name=\"value\" type=\"xsd:string\" minOccurs=\"0\" msdata:Ordinal=\"1\" />\n </xsd:sequence>\n <xsd:attribute name=\"name\" type=\"xsd:string\" use=\"required\" />\n </xsd:complexType>\n </xsd:element>\n </xsd:choice>\n </xsd:complexType>\n </xsd:element>\n </xsd:schema>\n <resheader name=\"resmimetype\">\n <value>text/microsoft-resx</value>\n </resheader>\n <resheader name=\"version\">\n <value>2.0</value>\n </resheader>\n <resheader name=\"reader\">\n <value>System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n </resheader>\n <resheader name=\"writer\">\n <value>System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089</value>\n </resheader>\n </root>"; //#endregion } //#endregion //#region Private Methods /** * Add new localized message to culture file * @param cultureFile (file of localized message culture) * @param localizedMessage (localized message) */ ResourcesConverter.prototype.addNewLocalizedMessage = function (cultureFile, localizedMessage) { // Read content of file var cultureFileContent = fs.readFileSync(cultureFile).toString(); // If content of file doesn't include the localized message, the localized message is added in the end of file if (cultureFileContent.match("<data name=\"" + localizedMessage.localizedMessageName + "\"") === null) { var stringToReplace = "</root>"; var newLocalizedMessage = " <data name=\"" + localizedMessage.localizedMessageName + "\" xml:space=\"preserve\">\r\n" + (" <value>" + localizedMessage.localizedMessageText + "</value>\r\n") + " </data>\r\n" + "</root>"; var textToFile = cultureFileContent.replace(stringToReplace, newLocalizedMessage); fs.writeFileSync(cultureFile, Buffer.from(textToFile), { flag: "w" }); return cultureFile; } else { return null; } }; //#endregion //#region Public Methods /** * Receiving a localized message, this method will find the file that will change and * insert the new localized message. * Return array with changed paths * @param localized (localized messages) * @param contentForEachFile (map with content for each file where keys are the paths and values the respective content) * @param filesResourcesExtension (extension of files) */ ResourcesConverter.prototype.writeToFile = function (localized, contentForEachFile, filesResourcesExtension) { return __awaiter(this, void 0, void 0, function () { var keysMapContentForEachFile, filesChanged, message, _loop_1, this_1, file; return __generator(this, function (_a) { try { keysMapContentForEachFile = Array.from(contentForEachFile.keys()); filesChanged = []; // For each localized message in localized and each key in keysMapContentForEachFile for (message in localized) { _loop_1 = function (file) { // Get content of file in position "file" var fileContent = contentForEachFile.get(keysMapContentForEachFile[file]); // Check if file already contains the localized message in "localized" for position "message" if (fileContent.match("<data name=\"" + localized[message].localizedMessageName + "\"")) { /** * Check if culture name of localized message is different of "en-US" because name of files in English * are nominated as, for example, "xpto.resx" and in other languages are "xpto.pt-PT.resx". * Additionally, if `fileContent.match(<data name="${localized[message].localizedMessageName}`, we already * know that localized message exists in needed file */ if (localized[message].cultureName !== "en-US") { // Get filename for culture name of localized message var cultureFile_1 = keysMapContentForEachFile[file].replace(filesResourcesExtension, "." + localized[message].cultureName + filesResourcesExtension); // Check if file exists if (fs.existsSync(cultureFile_1)) { // Check if the localized message doesn't exist and add new localized message to file var cultureFileName = this_1.addNewLocalizedMessage(cultureFile_1, localized[message]); // Push the name of file changed if (cultureFileName !== null && !filesChanged.includes(cultureFileName)) { filesChanged.push(cultureFileName); } } else { // Create file fs.writeFile(cultureFile_1, this_1.fileHeader, function (err) { if (err) { return console.error(err); } console.log("File " + cultureFile_1 + " created!"); }); // Check if the localized message doesn't exist and add new localized message to file var cultureFileName = this_1.addNewLocalizedMessage(cultureFile_1, localized[message]); // Push the name of file changed if (cultureFileName !== null && !filesChanged.includes(cultureFileName)) { filesChanged.push(cultureFileName); } } } } else { console.log("Localized Message: " + localized[message].localizedMessageName + " resource file was not found."); } }; this_1 = this; for (file in keysMapContentForEachFile) { _loop_1(file); } } return [2 /*return*/, filesChanged]; } catch (err) { console.log(err); return [2 /*return*/, undefined]; } return [2 /*return*/]; }); }); }; return ResourcesConverter; }()); exports.ResourcesConverter = ResourcesConverter;