From 2f566e90137714578cb8882a794238a4bca6cb77 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natalia=20K=C4=99dziora?= Date: Mon, 17 Feb 2025 16:34:57 +0100 Subject: [PATCH] ilib-loctool-json: Fix missing comment in XLIFF translation unit --- packages/ilib-loctool-json/JsonFile.js | 435 +++++++++++++------------ 1 file changed, 231 insertions(+), 204 deletions(-) diff --git a/packages/ilib-loctool-json/JsonFile.js b/packages/ilib-loctool-json/JsonFile.js index ffa60660d..9338d2ddf 100644 --- a/packages/ilib-loctool-json/JsonFile.js +++ b/packages/ilib-loctool-json/JsonFile.js @@ -343,237 +343,264 @@ JsonFile.prototype.parseObj = function(json, root, schema, ref, name, localizabl if (!schema) return this.sparseValue(json); localizable |= schema.localizable; + const isComment = schema.isComment ?? false; + var type = schema.type || typeof(json); switch (type) { - case "boolean": - case "number": - case "integer": - case "string": - if (localizable) { - if (isPrimitive(typeof(json))) { - var text = String(json); - var key = JsonFile.unescapeRef(ref).substring(2); // strip off the #/ part - if (translations) { - // localize it - var tester = this.API.newResource({ - resType: "string", - project: this.project.getProjectId(), - sourceLocale: this.project.getSourceLocale(), - reskey: key, - datatype: this.type.datatype - }); - var hashkey = tester.hashKeyForTranslation(locale); - var translated = translations.getClean(hashkey); - var translatedText; - if (locale === this.project.pseudoLocale && this.project.settings.nopseudo) { - translatedText = this.sparseValue(text); - } else if (!translated && this.type && this.type.pseudos[locale]) { - var source, sourceLocale = this.type.pseudos[locale].getPseudoSourceLocale(); - if (sourceLocale !== this.project.sourceLocale) { - // translation is derived from a different locale's translation instead of from the source string - var sourceRes = translations.getClean( - tester.cleanHashKey(), - this.type.datatype); - source = sourceRes ? sourceRes.getTarget() : text; + case "boolean": + case "number": + case "integer": + case "string": + if(isComment) { + this.comment = json; + + /* + Find the resource related to the currently processed JSON value and + update its comment property with the associated comment. + + This is necessary because JSON values are processed in the order + they are defined in the JSON file, meaning we need to account for + different value orderings. For example, in one case, we may get + "description" first, which has "isComment" set to true, and therefore, it is a comment source. + In another case, we may get "defaultMessage" first, which is instead used as a + source string for translation. + */ + const resources = this.set.getBy({ reskey: ref }) + if(resources && resources.length) { + const resource = resources[0]; + resource.comment = this.comment; + } + } + + if (localizable) { + if (isPrimitive(typeof(json))) { + var text = String(json); + var key = JsonFile.unescapeRef(ref).substring(2); // strip off the #/ part + if (translations) { + // localize it + var tester = this.API.newResource({ + resType: "string", + project: this.project.getProjectId(), + sourceLocale: this.project.getSourceLocale(), + reskey: key, + datatype: this.type.datatype + }); + var hashkey = tester.hashKeyForTranslation(locale); + var translated = translations.getClean(hashkey); + var translatedText; + if (locale === this.project.pseudoLocale && this.project.settings.nopseudo) { + translatedText = this.sparseValue(text); + } else if (!translated && this.type && this.type.pseudos[locale]) { + var source, sourceLocale = this.type.pseudos[locale].getPseudoSourceLocale(); + if (sourceLocale !== this.project.sourceLocale) { + // translation is derived from a different locale's translation instead of from the source string + var sourceRes = translations.getClean( + tester.cleanHashKey(), + this.type.datatype); + source = sourceRes ? sourceRes.getTarget() : text; + } else { + source = text; + } + translatedText = this.type.pseudos[locale].getString(source); } else { - source = text; + if (translated) { + translatedText = translated.getTarget(); + } else { + if (this.type && this.API.utils.containsActualText(text)) { + this.logger.trace("New string found: " + text); + this.type.newres.add(this.API.newResource({ + resType: "string", + project: this.project.getProjectId(), + key: key, + sourceLocale: this.project.sourceLocale, + source: text, + targetLocale: locale, + target: text, + pathName: this.pathName, + state: "new", + datatype: this.type.datatype, + index: this.resourceIndex++ + })); + translatedText = this.type && this.type.missingPseudo && !this.project.settings.nopseudo ? + this.type.missingPseudo.getString(text) : text; + translatedText = this.sparseValue(translatedText); + } else { + translatedText = this.sparseValue(text); + } + } + } + if (translatedText) { + returnValue = convertValueToType(translatedText, type); } - translatedText = this.type.pseudos[locale].getString(source); } else { - if (translated) { - translatedText = translated.getTarget(); + // extract this new string + var opts = { + resType: "string", + project: this.project.getProjectId(), + key: this.key ?? key, + sourceLocale: this.project.sourceLocale, + pathName: this.pathName, + state: "new", + comment: this.comment, + datatype: this.type.datatype, + index: this.resourceIndex++ + }; + if (locale !== this.project.sourceLocale) { + opts.target = text; + opts.targetLocale = locale; } else { - if (this.type && this.API.utils.containsActualText(text)) { - this.logger.trace("New string found: " + text); - this.type.newres.add(this.API.newResource({ - resType: "string", - project: this.project.getProjectId(), - key: key, - sourceLocale: this.project.sourceLocale, - source: text, - targetLocale: locale, - target: text, - pathName: this.pathName, - state: "new", - datatype: this.type.datatype, - index: this.resourceIndex++ - })); - translatedText = this.type && this.type.missingPseudo && !this.project.settings.nopseudo ? - this.type.missingPseudo.getString(text) : text; - translatedText = this.sparseValue(translatedText); - } else { - translatedText = this.sparseValue(text); - } + opts.source = text; } - } - if (translatedText) { - returnValue = convertValueToType(translatedText, type); + this.set.add(this.API.newResource(opts)); + returnValue = this.sparseValue(text); } } else { - // extract this new string - var opts = { - resType: "string", - project: this.project.getProjectId(), - key: key, - sourceLocale: this.project.sourceLocale, - pathName: this.pathName, - state: "new", - comment: this.comment, - datatype: this.type.datatype, - index: this.resourceIndex++ - }; - if (locale !== this.project.sourceLocale) { - opts.target = text; - opts.targetLocale = locale; - } else { - opts.source = text; - } - this.set.add(this.API.newResource(opts)); - returnValue = this.sparseValue(text); + // no way to parse the additional items beyond the end of the array, + // so just ignore them + this.logger.warn(this.pathName + '/' + ref + ": value should be type " + type + " but found " + typeof(json)); + returnValue = this.sparseValue(json); } } else { - // no way to parse the additional items beyond the end of the array, - // so just ignore them - this.logger.warn(this.pathName + '/' + ref + ": value should be type " + type + " but found " + typeof(json)); returnValue = this.sparseValue(json); } - } else { - returnValue = this.sparseValue(json); - } - break; + break; + + case "array": + returnValue = this.parseObjArray(json, root, schema, ref, name, localizable, translations, locale); + break; - case "array": - returnValue = this.parseObjArray(json, root, schema, ref, name, localizable, translations, locale); - break; + case "object": + if(schema.usePropertyKeyAsResname) { + this.key = JsonFile.unescapeRef(ref).substring(2); + } - case "object": - if (typeof(json) !== "object") { - this.logger.warn(this.pathName + '/' + ref + " is a " + - typeof(json) + " but should be an object according to the schema... skipping."); - return; - } - if (isPlural(json)) { - // handle this as a single plural resource instance instead - // of an object that has resources inside of it - var sourcePlurals = json; - if (localizable) { - var key = JsonFile.unescapeRef(ref).substring(2); // strip off the #/ part - if (translations) { - // localize it - var tester = this.API.newResource({ - resType: "plural", - project: this.project.getProjectId(), - sourceLocale: this.project.getSourceLocale(), - reskey: key, - datatype: this.type.datatype - }); - var hashkey = tester.hashKeyForTranslation(locale); - var translated = translations.getClean(hashkey); - var translatedPlurals; - if (locale === this.project.pseudoLocale && this.project.settings.nopseudo) { - translatedPlurals = this.sparseValue(sourcePlurals); - } else if (!translated && this.type && this.type.pseudos[locale]) { - var source, sourceLocale = this.type.pseudos[locale].getPseudoSourceLocale(); - if (sourceLocale !== this.project.sourceLocale) { - // translation is derived from a different locale's translation instead of from the source string - var sourceRes = translations.getClean( - tester.cleanHashKey(), - this.type.datatype); - source = sourceRes ? sourceRes.getTargetPlurals() : sourcePlurals; - } else { - source = sourcePlurals; - } - translatedPlurals = objectMap(source, function(item) { - return this.type.pseudos[locale].getString(item); - }.bind(this)); - } else { - if (translated) { - translatedPlurals = translated.getTargetPlurals(); + if (typeof(json) !== "object") { + this.logger.warn(this.pathName + '/' + ref + " is a " + + typeof(json) + " but should be an object according to the schema... skipping."); + return; + } + if (isPlural(json)) { + // handle this as a single plural resource instance instead + // of an object that has resources inside of it + var sourcePlurals = json; + if (localizable) { + var key = JsonFile.unescapeRef(ref).substring(2); // strip off the #/ part + if (translations) { + // localize it + var tester = this.API.newResource({ + resType: "plural", + project: this.project.getProjectId(), + sourceLocale: this.project.getSourceLocale(), + reskey: key, + datatype: this.type.datatype + }); + var hashkey = tester.hashKeyForTranslation(locale); + var translated = translations.getClean(hashkey); + var translatedPlurals; + if (locale === this.project.pseudoLocale && this.project.settings.nopseudo) { + translatedPlurals = this.sparseValue(sourcePlurals); + } else if (!translated && this.type && this.type.pseudos[locale]) { + var source, sourceLocale = this.type.pseudos[locale].getPseudoSourceLocale(); + if (sourceLocale !== this.project.sourceLocale) { + // translation is derived from a different locale's translation instead of from the source string + var sourceRes = translations.getClean( + tester.cleanHashKey(), + this.type.datatype); + source = sourceRes ? sourceRes.getTargetPlurals() : sourcePlurals; + } else { + source = sourcePlurals; + } + translatedPlurals = objectMap(source, function(item) { + return this.type.pseudos[locale].getString(item); + }.bind(this)); } else { - if (this.type) { - this.logger.trace("New string found: " + text); - this.type.newres.add(this.API.newResource({ - resType: "plural", - project: this.project.getProjectId(), - key: key, - sourceLocale: this.project.sourceLocale, - sourceStrings: sourcePlurals, - targetLocale: locale, - targetStrings: sourcePlurals, - pathName: this.pathName, - state: "new", - datatype: this.type.datatype, - index: this.resourceIndex++ - })); - if (this.type && this.type.missingPseudo && !this.project.settings.nopseudo) { - translatedPlurals = objectMap(sourcePlurals, function(item) { - return this.type.missingPseudo.getString(item); - }.bind(this)); - translatedPlurals = this.sparseValue(translatedPlurals); + if (translated) { + translatedPlurals = translated.getTargetPlurals(); + } else { + if (this.type) { + this.logger.trace("New string found: " + text); + this.type.newres.add(this.API.newResource({ + resType: "plural", + project: this.project.getProjectId(), + key: key, + sourceLocale: this.project.sourceLocale, + sourceStrings: sourcePlurals, + targetLocale: locale, + targetStrings: sourcePlurals, + pathName: this.pathName, + state: "new", + datatype: this.type.datatype, + index: this.resourceIndex++ + })); + if (this.type && this.type.missingPseudo && !this.project.settings.nopseudo) { + translatedPlurals = objectMap(sourcePlurals, function(item) { + return this.type.missingPseudo.getString(item); + }.bind(this)); + translatedPlurals = this.sparseValue(translatedPlurals); + } else { + translatedPlurals = this.sparseValue(sourcePlurals); + } } else { translatedPlurals = this.sparseValue(sourcePlurals); } - } else { - translatedPlurals = this.sparseValue(sourcePlurals); } } - } - returnValue = translatedPlurals; - } else { - // extract this new resource - var opts = { - resType: "plural", - project: this.project.getProjectId(), - key: JsonFile.unescapeRef(ref).substring(2), - sourceLocale: this.project.sourceLocale, - pathName: this.pathName, - state: "new", - comment: this.comment, - datatype: this.type.datatype, - index: this.resourceIndex++ - } - if (locale !== this.project.sourceLocale) { - opts.targetStrings = sourcePlurals; - opts.targetLocale = locale; + returnValue = translatedPlurals; } else { - opts.sourceStrings = sourcePlurals; + // extract this new resource + var opts = { + resType: "plural", + project: this.project.getProjectId(), + key: JsonFile.unescapeRef(ref).substring(2), + sourceLocale: this.project.sourceLocale, + pathName: this.pathName, + state: "new", + comment: this.comment, + datatype: this.type.datatype, + index: this.resourceIndex++ + } + if (locale !== this.project.sourceLocale) { + opts.targetStrings = sourcePlurals; + opts.targetLocale = locale; + } else { + opts.sourceStrings = sourcePlurals; + } + this.set.add(this.API.newResource(opts)); + returnValue = this.sparseValue(sourcePlurals); } - this.set.add(this.API.newResource(opts)); + } else { returnValue = this.sparseValue(sourcePlurals); } } else { - returnValue = this.sparseValue(sourcePlurals); + returnValue = {}; + var props = Object.keys(json); + props.forEach(function(prop) { + if (schema.properties && schema.properties[prop]) { + returnValue[prop] = this.parseObj( + json[prop], + root, + schema.properties[prop], + this.key ?? ref + '/' + JsonFile.escapeRef(prop), + prop, + localizable, + translations, + locale); + } else if (schema.additionalProperties) { + returnValue[prop] = this.parseObj( + json[prop], + root, + schema.additionalProperties, + ref + '/' + JsonFile.escapeRef(prop), + prop, + localizable, + translations, + locale); + } else { + returnValue[prop] = this.sparseValue(json[prop]); + } + }.bind(this)); } - } else { - returnValue = {}; - var props = Object.keys(json); - props.forEach(function(prop) { - if (schema.properties && schema.properties[prop]) { - returnValue[prop] = this.parseObj( - json[prop], - root, - schema.properties[prop], - ref + '/' + JsonFile.escapeRef(prop), - prop, - localizable, - translations, - locale); - } else if (schema.additionalProperties) { - returnValue[prop] = this.parseObj( - json[prop], - root, - schema.additionalProperties, - ref + '/' + JsonFile.escapeRef(prop), - prop, - localizable, - translations, - locale); - } else { - returnValue[prop] = this.sparseValue(json[prop]); - } - }.bind(this)); - } - break; + break; } }