UNPKG

@criticalmanufacturing/dev-i18n-transform

Version:
196 lines 8.48 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); const package_1 = require("../model/package"); const file_1 = require("../model/file"); const message_1 = require("../model/message"); const index_1 = require("../logger/index"); const fs = require("fs"); const util_1 = require("../util"); const translation_1 = require("../model/translation"); /** * PO (Portable Object) parser. * Parses a set of PO files into a {@see Package} */ class PortableObjectParser { /** * PortableObject (PO) File(s) Analyser * @param packagePath Absolute package path * @param filePaths File paths to analyse */ constructor(packagePath, filePaths) { /** * Utility instance */ this._util = new util_1.Util(); this._filePaths = filePaths; this._package = new package_1.Package(packagePath); } parseFile(filePath) { // Read file into a string let data = fs.readFileSync(filePath, "utf-8"); // Split the file content by empty new lines // The first entry is the PO Header let entries = this._util.splitByEmptyLine(data); if (entries != null && entries.length > 0) { let headerInfo = this.parseHeader(entries[0]); if (headerInfo.packageName !== this._package.name) { index_1.default.warn(`The package '${this._package.name}' doesn't match the original package name saved on the header of '${filePath}'`); } // Remove the first element of the array entries.splice(0, 1); for (let entry of entries) { this.parseEntry(entry); } } else { // File contains no entries. Raise a warning index_1.default.warn(`File '${filePath}' as no entries. Continuing...`); } } parseEntry(entry) { // Entry example: // #: test\mocks\multilevelExample\mock.pt-PT.ts#objects.WIZARD // #: test\mocks\duplicatedTextExample\mock.pt-PT.ts#TEXT_1 // msgid "Wizard" // msgstr "Wizard" let filesInfo = this.extractFileInformation(entry); let translationInfo = this.extractTranslation(entry); let notes = this.extractNotesForTranslators(entry); filesInfo.forEach((fileInfo) => { let message = new message_1.Message(fileInfo.objectPath); let translation = new translation_1.Translation(fileInfo.details.language, translationInfo.value, notes.indexOf(translation_1.TranslatorNotes.AutomaticTranslation) >= 0); message.addOrUpdateTranslation(translation); let file = new file_1.File(fileInfo.filePath); if (fileInfo.references && fileInfo.references.length > 0) { for (let ref of fileInfo.references) { file.addOrUpdateReference(ref); } } file.addOrUpdateMessage(message); // Add the file to the package this._package.addOrUpdateFile(file); }); } extractFileInformation(info) { let results = []; // Extract file information from rows // #: test\mocks\multilevelExample\mock.pt-PT.ts#objects.WIZARD // #: test\mocks\multilevelExample\mock.pt-PT.ts#objects.WIZARD test\mocks\multilevelExample\mock.pt-PT.ts#objects.WIZARD2 const filePathRegex = /^#: (.+)$/gm; let filePathMatch; while ((filePathMatch = filePathRegex.exec(info)) !== null) { let pathLine = filePathMatch[1]; // Because we may have multiple files in the same line... let paths = pathLine.match(/\s?(\S*)#(\S*)/g); for (let path of paths) { let fileAndObjectMatch = /(.*)#(.*)/.exec(path.trim()); let filePath = fileAndObjectMatch[1]; let objectPath = fileAndObjectMatch[2]; if (objectPath === ".") console.log(path); // Extract file information from row // Accept single or multiple lines for references // # AddReference | import i18n from "./reference.default"; // # AddReference | import i18n from "./reference.default" | filePath; let references = []; let referencesMatch = info.match(/^# (.*?) \| (.+)$/gm); if (referencesMatch != null && referencesMatch.length > 0) { for (let i = 0; i < referencesMatch.length; i++) { // Get type of comment first and its params let ref = /^# (.*?) \| (.+)$/.exec(referencesMatch[i]); // Split the params by | and trim let params = ref[2].split("|").map(p => p.trim()); let typeOfComment = ref[1]; switch (typeOfComment) { case "AddReference": // If there is a file, ignore references that are not for this file if (params[1] && params[1] !== filePath) continue; references.push(params[0]); break; default: index_1.default.warn(`Unknown type of entry found: '${typeOfComment}'`); break; } } } results.push({ filePath: filePath, objectPath: objectPath, details: file_1.File.parseFileName(filePath), references: references }); } } return results; } /** * * @param entry Translation entry to parse */ extractTranslation(entry) { // Match full translation (may include multiple lines) let fullTranslationMatch = /msgstr (.*)(\n"?.*"?)*/.exec(entry); let fullTranslationMessage = fullTranslationMatch[0]; // Split translation by lines and parse each one let translatedMessage = ""; for (let translationLine of fullTranslationMessage.split("\n")) { let parsedLine = /(?:msgstr )?(.*)/.exec(translationLine)[1]; // Remove "" and trim the message if (parsedLine.startsWith("\"")) parsedLine = parsedLine.slice(1); if (parsedLine.endsWith("\"")) parsedLine = parsedLine.slice(0, -1); // parsedLine = parsedLine.trim(); translatedMessage += parsedLine; } return { value: translatedMessage }; } /** * Extract translators notes from the translation entry. * Translation notes currently supported: * - 001 | Automated Translation * * @param entry Translation entry to extract the notes from * @return An array of translator notes. */ extractNotesForTranslators(entry) { let notes = []; let match = /#\. (.+?)\s*\|\s*(.+)/gm.exec(entry); // Translator notes are not mandatory, so they may not exist if (match != null && match.length === 3) { let noteId = parseInt(match[1]); // Validate that this not exists if (!(noteId in translation_1.TranslatorNotes)) { index_1.default.warn(`Unknown translator note '${match[0]}'`); } else { notes.push(noteId); } } return notes; } /** * Parses the header of the PO file. * * @param header Header of the file to parse * @return Information about the package */ parseHeader(header) { // Check if we have the package name on the file header let regexMatch = /[^]\# OriginalPackageName\: (.+)/.exec(header); return { packageName: regexMatch[1] }; } run() { for (let path of this._filePaths) { this.parseFile(path); } return this._package; } } exports.PortableObjectParser = PortableObjectParser; //# sourceMappingURL=portableObject.parser.js.map