From 55df9b83515c061c7323a2ab66b88029a9ae97cc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natalia=20K=C4=99dziora?= Date: Wed, 5 Mar 2025 13:36:02 +0100 Subject: [PATCH 1/7] ilib-loctool-json: Add support for "localizable: source" keyword --- packages/ilib-loctool-json/JsonFile.js | 198 +- packages/ilib-loctool-json/README.md | 85 + .../ilib-loctool-json/test/JsonFile.test.js | 1614 +++++++++-------- 3 files changed, 1009 insertions(+), 888 deletions(-) diff --git a/packages/ilib-loctool-json/JsonFile.js b/packages/ilib-loctool-json/JsonFile.js index ffa60660d..5b286b89f 100644 --- a/packages/ilib-loctool-json/JsonFile.js +++ b/packages/ilib-loctool-json/JsonFile.js @@ -329,6 +329,108 @@ function getSchemaType(schema, type, root) { return undefined; } +JsonFile.prototype.extracted = function(localizable, json, ref, translations, locale, returnValue, type) { + 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 { + 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); + } + } 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); + } + } 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); + } + return {text, returnValue}; +} + +JsonFile.prototype.isLocalizable = function (localizable, schema) { + const keywords = ["source"]; + + localizable = localizable || schema.localizable === true || keywords.includes(schema.localizable); + return localizable; +} + JsonFile.prototype.parseObj = function(json, root, schema, ref, name, localizable, translations, locale) { if (!json || !schema) return; @@ -342,104 +444,16 @@ JsonFile.prototype.parseObj = function(json, root, schema, ref, name, localizabl // in the tree if (!schema) return this.sparseValue(json); - localizable |= schema.localizable; + localizable = this.isLocalizable(localizable, schema); 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; - } else { - source = text; - } - translatedText = this.type.pseudos[locale].getString(source); - } else { - 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); - } - } 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); - } - } 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); - } + const __ret = this.extracted(localizable, json, ref, translations, locale, returnValue, type); + var text = __ret.text; + returnValue = __ret.returnValue; break; case "array": diff --git a/packages/ilib-loctool-json/README.md b/packages/ilib-loctool-json/README.md index 10e6c8083..1f7bfbe86 100644 --- a/packages/ilib-loctool-json/README.md +++ b/packages/ilib-loctool-json/README.md @@ -299,6 +299,91 @@ For strings that have an `enum` keyword, each of the values in the `enum` will not be translated as well, as the code that reads this json file is explicitly expecting one of the given fixed values. +### The `isComment` Keyword +The `isComment` keyword specifies that the property value should be treated as a comment for the translator. + +#### Example + +Assume we have the following `schema.json`: +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "sample-schema", + "title": "Sample schema with isComment", + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + "defaultMessage": { + "type": "string", + "localizable": true + }, + "description": { + "type": "string", + "isComment": true + } + } + } +} +``` +And a JSON file with the following content: +```json +{ + "project.whatever.text": { + "defaultMessage": "Text to translate", + "description": "A comment for the translator" + } +} +``` +The corresponding `` in XLIFF file will look as follows: +```xml + + Text to translate + A comment for the translator + +``` +Note that the `` tag content is set to the `description` property value from the JSON file (`"A comment for the translator"`), as the `isComment` keyword for the property `description` in the associated JSON schema is set to `true`. + + +### The `usePropertyKeyAsResname` Keyword +The `usePropertyKeyAsResname` keyword specifies that the property key from the JSON file should be used as the resource name for localization. + +#### Example + +Assume we have the following `schema.json`: +```json +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "sample-schema", + "title": "Sample schema with usePropertyKeyAsResname", + "type": "object", + "additionalProperties": { + "type": "object", + "properties": { + ... + }, + "usePropertyKeyAsResname": true, + } +} +``` +And a JSON file with the following content: +```json +{ + "project.whatever.text": { + "defaultMessage": "Text to translate", + "description": "A comment for the translator" + } +} +``` +The corresponding `` in XLIFF file will look as follows: +```xml + + ... + +``` +Mind, that the `resname` attribute is set to the property key from the JSON file (`"project.whatever.text"`). + + ## JSON File Generation When you create a new, empty JsonFile instance that is not backed diff --git a/packages/ilib-loctool-json/test/JsonFile.test.js b/packages/ilib-loctool-json/test/JsonFile.test.js index 064f94e01..2d603dd18 100644 --- a/packages/ilib-loctool-json/test/JsonFile.test.js +++ b/packages/ilib-loctool-json/test/JsonFile.test.js @@ -23,11 +23,11 @@ var fs = require("fs"); var JsonFile = require("../JsonFile.js"); var JsonFileType = require("../JsonFileType.js"); -var CustomProject = require("loctool/lib/CustomProject.js"); -var TranslationSet = require("loctool/lib/TranslationSet.js"); -var ResourceString = require("loctool/lib/ResourceString.js"); -var ResourcePlural = require("loctool/lib/ResourcePlural.js"); -var ResourceArray = require("loctool/lib/ResourceArray.js"); +var CustomProject = require("loctool/lib/CustomProject.js"); +var TranslationSet = require("loctool/lib/TranslationSet.js"); +var ResourceString = require("loctool/lib/ResourceString.js"); +var ResourcePlural = require("loctool/lib/ResourcePlural.js"); +var ResourceArray = require("loctool/lib/ResourceArray.js"); function diff(a, b) { var min = Math.min(a.length, b.length); @@ -47,7 +47,7 @@ var p = new CustomProject({ id: "foo", sourceLocale: "en-US" }, "./test/testfiles", { - locales:["en-GB"], + locales: ["en-GB"], targetDir: ".", nopseudo: true, json: { @@ -116,7 +116,7 @@ var p2 = new CustomProject({ id: "foo", sourceLocale: "en-US" }, "./test/testfiles", { - locales:["en-GB"], + locales: ["en-GB"], identify: true, targetDir: "testfiles", nopseudo: false, @@ -142,7 +142,7 @@ function rmrf(path) { } } -afterEach(function() { +afterEach(function () { [ "test/testfiles/resources/de/DE/messages.json", "test/testfiles/resources/de/DE/sparse2.json", @@ -154,15 +154,15 @@ afterEach(function() { ].forEach(rmrf); }); -describe("jsonfile", function() { - test("JsonFileConstructor", function() { +describe("jsonfile", function () { + test("JsonFileConstructor", function () { expect.assertions(1); var jf = new JsonFile({project: p, type: t}); expect(jf).toBeTruthy(); }); - test("JsonFileConstructorParams", function() { + test("JsonFileConstructorParams", function () { expect.assertions(1); var jf = new JsonFile({ @@ -174,7 +174,7 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); }); - test("JsonFileConstructorNoFile", function() { + test("JsonFileConstructorNoFile", function () { expect.assertions(1); var jf = new JsonFile({ @@ -184,91 +184,91 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); }); - test("JsonFileEscapeProp", function() { + test("JsonFileEscapeProp", function () { expect.assertions(1); expect(JsonFile.escapeProp("escape/tilde~tilde")).toBe("escape~1tilde~0tilde"); }); - test("JsonFileEscapePropNoChange", function() { + test("JsonFileEscapePropNoChange", function () { expect.assertions(1); expect(JsonFile.escapeProp("permissions")).toBe("permissions"); }); - test("JsonFileEscapePropDontEscapeOthers", function() { + test("JsonFileEscapePropDontEscapeOthers", function () { expect.assertions(1); expect(JsonFile.escapeProp("permissions% \" ^ | \\")).toBe("permissions% \" ^ | \\"); }); - test("JsonFileUnescapeProp", function() { + test("JsonFileUnescapeProp", function () { expect.assertions(1); expect(JsonFile.unescapeProp("escape~1tilde~0tilde")).toBe("escape/tilde~tilde"); }); - test("JsonFileUnescapePropTricky", function() { + test("JsonFileUnescapePropTricky", function () { expect.assertions(1); expect(JsonFile.unescapeProp("escape~3tilde~4tilde")).toBe("escape~3tilde~4tilde"); }); - test("JsonFileUnescapePropNoChange", function() { + test("JsonFileUnescapePropNoChange", function () { expect.assertions(1); expect(JsonFile.unescapeProp("permissions")).toBe("permissions"); }); - test("JsonFileUnescapePropDontEscapeOthers", function() { + test("JsonFileUnescapePropDontEscapeOthers", function () { expect.assertions(1); expect(JsonFile.unescapeProp("permissions% \" ^ | \\")).toBe("permissions% \" ^ | \\"); }); - test("JsonFileEscapeRef", function() { + test("JsonFileEscapeRef", function () { expect.assertions(1); expect(JsonFile.escapeRef("escape/tilde~tilde")).toBe("escape~1tilde~0tilde"); }); - test("JsonFileEscapeRefNoChange", function() { + test("JsonFileEscapeRefNoChange", function () { expect.assertions(1); expect(JsonFile.escapeRef("permissions")).toBe("permissions"); }); - test("JsonFileEscapeRefDontEscapeOthers", function() { + test("JsonFileEscapeRefDontEscapeOthers", function () { expect.assertions(1); expect(JsonFile.escapeRef("permissions% \" ^ | \\")).toBe("permissions%25%20%22%20%5E%20%7C%20%5C"); }); - test("JsonFileUnescapeRef", function() { + test("JsonFileUnescapeRef", function () { expect.assertions(1); expect(JsonFile.unescapeRef("escape~1tilde~0tilde")).toBe("escape/tilde~tilde"); }); - test("JsonFileUnescapeRefTricky", function() { + test("JsonFileUnescapeRefTricky", function () { expect.assertions(1); expect(JsonFile.unescapeRef("escape~3tilde~4tilde")).toBe("escape~3tilde~4tilde"); }); - test("JsonFileUnescapeRefNoChange", function() { + test("JsonFileUnescapeRefNoChange", function () { expect.assertions(1); expect(JsonFile.unescapeRef("permissions")).toBe("permissions"); }); - test("JsonFileUnescapeRefDontEscapeOthers", function() { + test("JsonFileUnescapeRefDontEscapeOthers", function () { expect.assertions(1); expect(JsonFile.unescapeRef("permissions%25%20%22%20%5E%20%7C%20%5C")).toBe("permissions% \" ^ | \\"); }); - test("JsonFileParseSimpleGetByKey", function() { + test("JsonFileParseSimpleGetByKey", function () { expect.assertions(5); var jf = new JsonFile({ @@ -278,10 +278,10 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": "this is string two"\n' + - '}\n'); + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": "this is string two"\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -293,7 +293,7 @@ describe("jsonfile", function() { expect(r.getKey()).toBe("string 1"); }); - test("JsonFileParseSimpleRightStrings", function() { + test("JsonFileParseSimpleRightStrings", function () { expect.assertions(8); var jf = new JsonFile({ @@ -303,10 +303,10 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": "this is string two"\n' + - '}\n'); + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": "this is string two"\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -322,7 +322,7 @@ describe("jsonfile", function() { expect(resources[1].getKey()).toBe("string 2"); }); - test("JsonFileParseSimpleDontExtractEmpty", function() { + test("JsonFileParseSimpleDontExtractEmpty", function () { expect.assertions(6); var jf = new JsonFile({ @@ -332,10 +332,10 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": ""\n' + - '}\n'); + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": ""\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -348,7 +348,7 @@ describe("jsonfile", function() { expect(resources[0].getKey()).toBe("string 1"); }); - test("JsonFileParseEscapeStringKeys", function() { + test("JsonFileParseEscapeStringKeys", function () { expect.assertions(8); var jf = new JsonFile({ @@ -358,10 +358,10 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "/user": "this is string one",\n' + - ' "~tilde": "this is string two"\n' + - '}\n'); + '{\n' + + ' "/user": "this is string one",\n' + + ' "~tilde": "this is string two"\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -377,7 +377,7 @@ describe("jsonfile", function() { expect(resources[1].getKey()).toBe("~tilde"); }); - test("JsonFileParseSimpleRejectThingsThatAreNotInTheSchema", function() { + test("JsonFileParseSimpleRejectThingsThatAreNotInTheSchema", function () { expect.assertions(6); var jf = new JsonFile({ @@ -387,12 +387,12 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": {\n' + - ' "asdf": "asdf"\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": {\n' + + ' "asdf": "asdf"\n' + + ' }\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -405,7 +405,7 @@ describe("jsonfile", function() { expect(resources[0].getKey()).toBe("string 1"); }); - test("JsonFileParseComplexRightSize", function() { + test("JsonFileParseComplexRightSize", function () { expect.assertions(3); // when it's named messages.json, it should apply the messages-schema schema @@ -417,26 +417,26 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singular",\n' + - ' "many": "many",\n' + - ' "other": "plural"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "b",\n' + - ' "c": "d"\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "string 1",\n' + - ' "string 2",\n' + - ' "string 3"\n' + - ' ]\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singular",\n' + + ' "many": "many",\n' + + ' "other": "plural"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "b",\n' + + ' "c": "d"\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "string 1",\n' + + ' "string 2",\n' + + ' "string 3"\n' + + ' ]\n' + + ' }\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -444,7 +444,7 @@ describe("jsonfile", function() { expect(set.size()).toBe(4); }); - test("JsonFileParseComplexRightStrings", function() { + test("JsonFileParseComplexRightStrings", function () { expect.assertions(26); // when it's named messages.json, it should apply the messages-schema schema @@ -456,26 +456,26 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singular",\n' + - ' "many": "many",\n' + - ' "other": "plural"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "b",\n' + - ' "c": "d"\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "string 1",\n' + - ' "string 2",\n' + - ' "string 3"\n' + - ' ]\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singular",\n' + + ' "many": "many",\n' + + ' "other": "plural"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "b",\n' + + ' "c": "d"\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "string 1",\n' + + ' "string 2",\n' + + ' "string 3"\n' + + ' ]\n' + + ' }\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -513,7 +513,7 @@ describe("jsonfile", function() { expect(arrayStrings[2]).toBe("string 3"); }); - test("JsonFileParseComplexRightStringsTranslated", function() { + test("JsonFileParseComplexRightStringsTranslated", function () { expect.assertions(38); // when it's named messages.json, it should apply the messages-schema schema @@ -525,26 +525,26 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "eins",\n' + - ' "many": "vielen",\n' + - ' "other": "mehrere"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "b",\n' + - ' "c": "d"\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "Zeichenfolge 1",\n' + - ' "Zeichenfolge 2",\n' + - ' "Zeichenfolge 3"\n' + - ' ]\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "eins",\n' + + ' "many": "vielen",\n' + + ' "other": "mehrere"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "b",\n' + + ' "c": "d"\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "Zeichenfolge 1",\n' + + ' "Zeichenfolge 2",\n' + + ' "Zeichenfolge 3"\n' + + ' ]\n' + + ' }\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -594,7 +594,7 @@ describe("jsonfile", function() { expect(arrayStrings[2]).toBe("Zeichenfolge 3"); }); - test("JsonFileParseArrayOfStrings", function() { + test("JsonFileParseArrayOfStrings", function () { expect.assertions(11); // when it's named arrays.json, it should apply the arrays schema @@ -606,12 +606,12 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "strings": [\n' + - ' "string 1",\n' + - ' "string 2",\n' + - ' "string 3"\n' + - ' ]\n' + - '}\n'); + ' "strings": [\n' + + ' "string 1",\n' + + ' "string 2",\n' + + ' "string 3"\n' + + ' ]\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -630,7 +630,7 @@ describe("jsonfile", function() { expect(arrayStrings[2]).toBe("string 3"); }); - test("JsonFileParseArrayOfNumbers", function() { + test("JsonFileParseArrayOfNumbers", function () { expect.assertions(12); var jf = new JsonFile({ @@ -641,13 +641,13 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "numbers": [\n' + - ' 15,\n' + - ' -3,\n' + - ' 1.18,\n' + - ' 0\n' + - ' ]\n' + - '}\n'); + ' "numbers": [\n' + + ' 15,\n' + + ' -3,\n' + + ' 1.18,\n' + + ' 0\n' + + ' ]\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -667,7 +667,7 @@ describe("jsonfile", function() { expect(arrayNumbers[3]).toBe("0"); }); - test("JsonFileParseArrayOfBooleans", function() { + test("JsonFileParseArrayOfBooleans", function () { expect.assertions(10); var jf = new JsonFile({ @@ -678,11 +678,11 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "booleans": [\n' + - ' true,\n' + - ' false\n' + - ' ]\n' + - '}\n'); + ' "booleans": [\n' + + ' true,\n' + + ' false\n' + + ' ]\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -700,7 +700,7 @@ describe("jsonfile", function() { expect(arrayBooleans[1]).toBe("false"); }); - test("JsonFileParseArrayOfObjects", function() { + test("JsonFileParseArrayOfObjects", function () { expect.assertions(13); var jf = new JsonFile({ @@ -711,17 +711,17 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "objects": [\n' + - ' {\n' + - ' "name": "First Object",\n' + - ' "randomProp": "Non-translatable"\n' + - ' },\n' + - ' {\n' + - ' "name": "Second Object",\n' + - ' "description": "String property"\n' + - ' }\n' + - ' ]\n' + - '}\n'); + ' "objects": [\n' + + ' {\n' + + ' "name": "First Object",\n' + + ' "randomProp": "Non-translatable"\n' + + ' },\n' + + ' {\n' + + ' "name": "Second Object",\n' + + ' "description": "String property"\n' + + ' }\n' + + ' ]\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -742,7 +742,7 @@ describe("jsonfile", function() { expect(resources[2].getSource()).toBe('String property'); }); - test("JsonFileParseArrayOfStringsInsideOfAnyOf", function() { + test("JsonFileParseArrayOfStringsInsideOfAnyOf", function () { expect.assertions(14); // when it's named arrays.json, it should apply the arrays schema @@ -766,7 +766,7 @@ describe("jsonfile", function() { ' ]\n' + ' }\n' + ']\n' - ); + ); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -790,7 +790,7 @@ describe("jsonfile", function() { expect(arrayStrings[2]).toBe("string 6"); }); - test("JsonFileParseArrayWithRef", function() { + test("JsonFileParseArrayWithRef", function () { expect.assertions(10); var jf = new JsonFile({ @@ -801,17 +801,17 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "itemsArray": [\n' + - ' {\n' + - ' "itemField": "First item",\n' + - ' "itemFieldIgnore": "Non-translatable"\n' + - ' },\n' + - ' {\n' + - ' "itemField": "Second item",\n' + - ' "itemFieldIgnore": "Non-translatable"\n' + - ' }\n' + - ' ]\n' + - '}\n'); + ' "itemsArray": [\n' + + ' {\n' + + ' "itemField": "First item",\n' + + ' "itemFieldIgnore": "Non-translatable"\n' + + ' },\n' + + ' {\n' + + ' "itemField": "Second item",\n' + + ' "itemFieldIgnore": "Non-translatable"\n' + + ' }\n' + + ' ]\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -828,7 +828,7 @@ describe("jsonfile", function() { expect(resources[1].getSource()).toBe('Second item'); }); - test("JsonFileParseDeepRightSize", function() { + test("JsonFileParseDeepRightSize", function () { expect.assertions(3); // when it's named messages.json, it should apply the messages-schema schema @@ -840,27 +840,27 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "x": {\n' + - ' "y": {\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singular",\n' + - ' "many": "many",\n' + - ' "other": "plural"\n' + - ' }\n' + - ' }\n' + - ' }\n' + - ' },\n' + - ' "a": {\n' + - ' "b": {\n' + - ' "strings": {\n' + - ' "a": "b",\n' + - ' "c": "d"\n' + - ' }\n' + - ' }\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "x": {\n' + + ' "y": {\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singular",\n' + + ' "many": "many",\n' + + ' "other": "plural"\n' + + ' }\n' + + ' }\n' + + ' }\n' + + ' },\n' + + ' "a": {\n' + + ' "b": {\n' + + ' "strings": {\n' + + ' "a": "b",\n' + + ' "c": "d"\n' + + ' }\n' + + ' }\n' + + ' }\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -868,7 +868,7 @@ describe("jsonfile", function() { expect(set.size()).toBe(3); }); - test("JsonFileParseDeepRightStrings", function() { + test("JsonFileParseDeepRightStrings", function () { expect.assertions(19); // when it's named messages.json, it should apply the messages-schema schema @@ -880,27 +880,27 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "x": {\n' + - ' "y": {\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singular",\n' + - ' "many": "many",\n' + - ' "other": "plural"\n' + - ' }\n' + - ' }\n' + - ' }\n' + - ' },\n' + - ' "a": {\n' + - ' "b": {\n' + - ' "strings": {\n' + - ' "a": "b",\n' + - ' "c": "d"\n' + - ' }\n' + - ' }\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "x": {\n' + + ' "y": {\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singular",\n' + + ' "many": "many",\n' + + ' "other": "plural"\n' + + ' }\n' + + ' }\n' + + ' }\n' + + ' },\n' + + ' "a": {\n' + + ' "b": {\n' + + ' "strings": {\n' + + ' "a": "b",\n' + + ' "c": "d"\n' + + ' }\n' + + ' }\n' + + ' }\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -929,7 +929,7 @@ describe("jsonfile", function() { expect(resources[2].getKey()).toBe("a/b/strings/c"); }); - test("JsonFileParseTestInvalidJson", function() { + test("JsonFileParseTestInvalidJson", function () { expect.assertions(2); // when it's named messages.json, it should apply the messages-schema schema @@ -940,33 +940,33 @@ describe("jsonfile", function() { }); expect(jf).toBeTruthy(); - expect(function(test) { + expect(function (test) { jf.parse( - '{\n' + - ' "x": {\n' + - ' "y": {,@#\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singular",\n' + - ' "many": "many",\n' + - ' "other": "plural"\n' + - ' }\n' + - ' }\n' + - ' }\n' + - ' },\n' + - ' "a": {\n' + - ' "b": {\n' + - ' "strings": {\n' + - ' "a": "b",\n' + - ' "c": "d"\n' + - ' }\n' + - ' }\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "x": {\n' + + ' "y": {,@#\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singular",\n' + + ' "many": "many",\n' + + ' "other": "plural"\n' + + ' }\n' + + ' }\n' + + ' }\n' + + ' },\n' + + ' "a": {\n' + + ' "b": {\n' + + ' "strings": {\n' + + ' "a": "b",\n' + + ' "c": "d"\n' + + ' }\n' + + ' }\n' + + ' }\n' + + '}\n'); }).toThrow(); }); - test("JsonFileParseRefsRightSize", function() { + test("JsonFileParseRefsRightSize", function () { expect.assertions(3); // when it's named messages.json, it should apply the messages-schema schema @@ -978,29 +978,29 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "owner": {\n' + - ' "name": "Foo Bar",\n' + - ' "phone": {\n' + - ' "number": "1-555-555-1212",\n' + - ' "type": "Mobile"\n' + - ' }\n' + - ' },\n' + - ' "customer1": {\n' + - ' "name": "Customer One",\n' + - ' "phone": {\n' + - ' "number": "1-555-555-1212",\n' + - ' "type": "Home"\n' + - ' }\n' + - ' },\n' + - ' "customer2": {\n' + - ' "name": "Customer Two",\n' + - ' "phone": {\n' + - ' "number": "1-555-555-1212",\n' + - ' "type": "Work"\n' + - ' }\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "owner": {\n' + + ' "name": "Foo Bar",\n' + + ' "phone": {\n' + + ' "number": "1-555-555-1212",\n' + + ' "type": "Mobile"\n' + + ' }\n' + + ' },\n' + + ' "customer1": {\n' + + ' "name": "Customer One",\n' + + ' "phone": {\n' + + ' "number": "1-555-555-1212",\n' + + ' "type": "Home"\n' + + ' }\n' + + ' },\n' + + ' "customer2": {\n' + + ' "name": "Customer Two",\n' + + ' "phone": {\n' + + ' "number": "1-555-555-1212",\n' + + ' "type": "Work"\n' + + ' }\n' + + ' }\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -1008,7 +1008,7 @@ describe("jsonfile", function() { expect(set.size()).toBe(3); }); - test("JsonFileParseRefsRightStrings", function() { + test("JsonFileParseRefsRightStrings", function () { expect.assertions(13); // when it's named messages.json, it should apply the messages-schema schema @@ -1020,29 +1020,29 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "owner": {\n' + - ' "name": "Foo Bar",\n' + - ' "phone": {\n' + - ' "number": "1-555-555-1212",\n' + - ' "type": "Mobile"\n' + - ' }\n' + - ' },\n' + - ' "customer1": {\n' + - ' "name": "Customer One",\n' + - ' "phone": {\n' + - ' "number": "1-555-555-1212",\n' + - ' "type": "Home"\n' + - ' }\n' + - ' },\n' + - ' "customer2": {\n' + - ' "name": "Customer Two",\n' + - ' "phone": {\n' + - ' "number": "1-555-555-1212",\n' + - ' "type": "Work"\n' + - ' }\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "owner": {\n' + + ' "name": "Foo Bar",\n' + + ' "phone": {\n' + + ' "number": "1-555-555-1212",\n' + + ' "type": "Mobile"\n' + + ' }\n' + + ' },\n' + + ' "customer1": {\n' + + ' "name": "Customer One",\n' + + ' "phone": {\n' + + ' "number": "1-555-555-1212",\n' + + ' "type": "Home"\n' + + ' }\n' + + ' },\n' + + ' "customer2": {\n' + + ' "name": "Customer Two",\n' + + ' "phone": {\n' + + ' "number": "1-555-555-1212",\n' + + ' "type": "Work"\n' + + ' }\n' + + ' }\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -1064,7 +1064,7 @@ describe("jsonfile", function() { expect(resources[2].getKey()).toBe("customer2/phone/type"); }); - test("JsonFileParseDefaultSchema", function() { + test("JsonFileParseDefaultSchema", function () { expect.assertions(5); var jf = new JsonFile({ @@ -1075,10 +1075,10 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": "this is string two"\n' + - '}\n'); + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": "this is string two"\n' + + '}\n'); var set = jf.getTranslationSet(); expect(set).toBeTruthy(); @@ -1090,44 +1090,44 @@ describe("jsonfile", function() { expect(r.getKey()).toBe("string 1"); }); -/* - can't do comments yet + /* + can't do comments yet - test("JsonFileParseExtractComments", function() { - expect.assertions(8); + test("JsonFileParseExtractComments", function() { + expect.assertions(8); - var jf = new JsonFile({ - project: p, - type: t - }); - expect(jf).toBeTruthy(); + var jf = new JsonFile({ + project: p, + type: t + }); + expect(jf).toBeTruthy(); - jf.parse( - '{\n' + - ' // comment for string 1\,' + - ' "string 1": "this is string one",\n' + - ' // comment for string 2\,' + - ' "string 2": "this is string two"\n' + - '}\n'); + jf.parse( + '{\n' + + ' // comment for string 1\,' + + ' "string 1": "this is string one",\n' + + ' // comment for string 2\,' + + ' "string 2": "this is string two"\n' + + '}\n'); - var set = jf.getTranslationSet(); - expect(set).toBeTruthy(); + var set = jf.getTranslationSet(); + expect(set).toBeTruthy(); - expect(set.size()).toBe(2); - var resources = set.getAll(); - expect(resources.length).toBe(2); + expect(set.size()).toBe(2); + var resources = set.getAll(); + expect(resources.length).toBe(2); - expect(resources[0].getSource()).toBe("this is string one"); - expect(resources[0].getKey()).toBe("string 1"); - expect(resources[0].getNote()).toBe("comment for string 1"); + expect(resources[0].getSource()).toBe("this is string one"); + expect(resources[0].getKey()).toBe("string 1"); + expect(resources[0].getNote()).toBe("comment for string 1"); - expect(resources[1].getSource()).toBe("this is string two"); - expect(resources[1].getKey()).toBe("string 2"); - }); + expect(resources[1].getSource()).toBe("this is string two"); + expect(resources[1].getKey()).toBe("string 2"); + }); -*/ + */ - test("JsonFileExtractFile", function() { + test("JsonFileExtractFile", function () { expect.assertions(28); var base = path.dirname(module.id); @@ -1183,7 +1183,7 @@ describe("jsonfile", function() { expect(resources[4].getKey()).toBe("strings/c"); }); - test("JsonFileExtractUndefinedFile", function() { + test("JsonFileExtractUndefinedFile", function () { expect.assertions(2); var base = path.dirname(module.id); @@ -1202,7 +1202,7 @@ describe("jsonfile", function() { expect(set.size()).toBe(0); }); - test("JsonFileExtractBogusFile", function() { + test("JsonFileExtractBogusFile", function () { expect.assertions(2); var base = path.dirname(module.id); @@ -1222,7 +1222,7 @@ describe("jsonfile", function() { expect(set.size()).toBe(0); }); - test("JsonFileLocalizeTextSimple", function() { + test("JsonFileLocalizeTextSimple", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1232,10 +1232,10 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": "this is string two"\n' + - '}\n'); + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": "this is string two"\n' + + '}\n'); var translations = new TranslationSet(); translations.add(new ResourceString({ @@ -1250,16 +1250,16 @@ describe("jsonfile", function() { var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "string 1": "C\'est la chaîne numéro 1",\n' + - ' "string 2": "this is string two"\n' + - '}\n'; + '{\n' + + ' "string 1": "C\'est la chaîne numéro 1",\n' + + ' "string 2": "this is string two"\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileLocalizeTextWithSchema", function() { + test("JsonFileLocalizeTextWithSchema", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1270,30 +1270,30 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singular",\n' + - ' "many": "many",\n' + - ' "other": "plural"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "b",\n' + - ' "c": "d"\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "string 1",\n' + - ' "string 2",\n' + - ' "string 3"\n' + - ' ]\n' + - ' },\n' + - ' "others": {\n' + - ' "first": "abc",\n' + - ' "second": "bcd"\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singular",\n' + + ' "many": "many",\n' + + ' "other": "plural"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "b",\n' + + ' "c": "d"\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "string 1",\n' + + ' "string 2",\n' + + ' "string 3"\n' + + ' ]\n' + + ' },\n' + + ' "others": {\n' + + ' "first": "abc",\n' + + ' "second": "bcd"\n' + + ' }\n' + + '}\n'); var translations = new TranslationSet(); translations.add(new ResourcePlural({ @@ -1351,36 +1351,36 @@ describe("jsonfile", function() { var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singulaire",\n' + - ' "many": "plupart",\n' + - ' "other": "autres"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "la b",\n' + - ' "c": "la d"\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "chaîne 1",\n' + - ' "chaîne 2",\n' + - ' "chaîne 3"\n' + - ' ]\n' + - ' },\n' + - ' "others": {\n' + - ' "first": "abc",\n' + - ' "second": "bcd"\n' + - ' }\n' + - '}\n'; + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singulaire",\n' + + ' "many": "plupart",\n' + + ' "other": "autres"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "la b",\n' + + ' "c": "la d"\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "chaîne 1",\n' + + ' "chaîne 2",\n' + + ' "chaîne 3"\n' + + ' ]\n' + + ' },\n' + + ' "others": {\n' + + ' "first": "abc",\n' + + ' "second": "bcd"\n' + + ' }\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileLocalizeTextMethodSparse", function() { + test("JsonFileLocalizeTextMethodSparse", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1391,10 +1391,10 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": "this is string two"\n' + - '}\n'); + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": "this is string two"\n' + + '}\n'); var translations = new TranslationSet(); translations.add(new ResourceString({ @@ -1409,15 +1409,15 @@ describe("jsonfile", function() { var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "string 1": "C\'est la chaîne numéro 1"\n' + - '}\n'; + '{\n' + + ' "string 1": "C\'est la chaîne numéro 1"\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileLocalizeTextWithSchemaSparseComplex", function() { + test("JsonFileLocalizeTextWithSchemaSparseComplex", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1428,26 +1428,26 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singular",\n' + - ' "many": "many",\n' + - ' "other": "plural"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "b",\n' + - ' "c": "d"\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "string 1",\n' + - ' "string 2",\n' + - ' "string 3"\n' + - ' ]\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singular",\n' + + ' "many": "many",\n' + + ' "other": "plural"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "b",\n' + + ' "c": "d"\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "string 1",\n' + + ' "string 2",\n' + + ' "string 3"\n' + + ' ]\n' + + ' }\n' + + '}\n'); var translations = new TranslationSet(); translations.add(new ResourcePlural({ @@ -1479,24 +1479,24 @@ describe("jsonfile", function() { var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singulaire",\n' + - ' "many": "plupart",\n' + - ' "other": "autres"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "la b"\n' + - ' }\n' + - '}\n'; + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singulaire",\n' + + ' "many": "plupart",\n' + + ' "other": "autres"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "la b"\n' + + ' }\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileLocalizeArrayOfStrings", function() { + test("JsonFileLocalizeArrayOfStrings", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1507,12 +1507,12 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "strings": [\n' + - ' "string 1",\n' + - ' "string 2",\n' + - ' "string 3"\n' + - ' ]\n' + - '}\n'); + ' "strings": [\n' + + ' "string 1",\n' + + ' "string 2",\n' + + ' "string 3"\n' + + ' ]\n' + + '}\n'); var translations = new TranslationSet('en-US'); translations.add(new ResourceArray({ @@ -1535,19 +1535,19 @@ describe("jsonfile", function() { var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "strings": [\n' + - ' "chaîne 1",\n' + - ' "chaîne 2",\n' + - ' "chaîne 3"\n' + - ' ]\n' + - '}\n'; + '{\n' + + ' "strings": [\n' + + ' "chaîne 1",\n' + + ' "chaîne 2",\n' + + ' "chaîne 3"\n' + + ' ]\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileLocalizeArrayOfNumbers", function() { + test("JsonFileLocalizeArrayOfNumbers", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1558,13 +1558,13 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "numbers": [\n' + - ' 15,\n' + - ' -3,\n' + - ' 1.18,\n' + - ' 0\n' + - ' ]\n' + - '}\n'); + ' "numbers": [\n' + + ' 15,\n' + + ' -3,\n' + + ' 1.18,\n' + + ' 0\n' + + ' ]\n' + + '}\n'); var translations = new TranslationSet('en-US'); translations.add(new ResourceArray({ @@ -1589,20 +1589,20 @@ describe("jsonfile", function() { var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "numbers": [\n' + - ' 29,\n' + - ' 12,\n' + - ' -17.3,\n' + - ' 0\n' + - ' ]\n' + - '}\n'; + '{\n' + + ' "numbers": [\n' + + ' 29,\n' + + ' 12,\n' + + ' -17.3,\n' + + ' 0\n' + + ' ]\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileLocalizeArrayOfBooleans", function() { + test("JsonFileLocalizeArrayOfBooleans", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1613,11 +1613,11 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "booleans": [\n' + - ' true,\n' + - ' false\n' + - ' ]\n' + - '}\n'); + ' "booleans": [\n' + + ' true,\n' + + ' false\n' + + ' ]\n' + + '}\n'); var translations = new TranslationSet('en-US'); translations.add(new ResourceArray({ @@ -1638,18 +1638,18 @@ describe("jsonfile", function() { var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "booleans": [\n' + - ' false,\n' + - ' true\n' + - ' ]\n' + - '}\n'; + '{\n' + + ' "booleans": [\n' + + ' false,\n' + + ' true\n' + + ' ]\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileLocalizeArrayOfObjects", function() { + test("JsonFileLocalizeArrayOfObjects", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1660,17 +1660,17 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "objects": [\n' + - ' {\n' + - ' "name": "First Object",\n' + - ' "randomProp": "Non-translatable"\n' + - ' },\n' + - ' {\n' + - ' "name": "Second Object",\n' + - ' "description": "String property"\n' + - ' }\n' + - ' ]\n' + - '}\n'); + ' "objects": [\n' + + ' {\n' + + ' "name": "First Object",\n' + + ' "randomProp": "Non-translatable"\n' + + ' },\n' + + ' {\n' + + ' "name": "Second Object",\n' + + ' "description": "String property"\n' + + ' }\n' + + ' ]\n' + + '}\n'); var translations = new TranslationSet('en-US'); translations.add(new ResourceString({ @@ -1704,24 +1704,24 @@ describe("jsonfile", function() { var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "objects": [\n' + - ' {\n' + - ' "name": "Premier objet",\n' + - ' "randomProp": "Non-translatable"\n' + - ' },\n' + - ' {\n' + - ' "name": "Deuxième objet",\n' + - ' "description": "Propriété String"\n' + - ' }\n' + - ' ]\n' + - '}\n'; + '{\n' + + ' "objects": [\n' + + ' {\n' + + ' "name": "Premier objet",\n' + + ' "randomProp": "Non-translatable"\n' + + ' },\n' + + ' {\n' + + ' "name": "Deuxième objet",\n' + + ' "description": "Propriété String"\n' + + ' }\n' + + ' ]\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileLocalizeArrayOfObjectsWithBooleansOnly", function() { + test("JsonFileLocalizeArrayOfObjectsWithBooleansOnly", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1761,7 +1761,7 @@ describe("jsonfile", function() { expect(actual).toBe(expected); }); - test("Localize an array of objects that includes an empty object", function() { + test("Localize an array of objects that includes an empty object", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1772,18 +1772,18 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse('{\n' + - ' "objects": [\n' + - ' {},\n' + - ' {\n' + - ' "name": "First Object",\n' + - ' "randomProp": "Non-translatable"\n' + - ' },\n' + - ' {\n' + - ' "name": "Second Object",\n' + - ' "description": "String property"\n' + - ' }\n' + - ' ]\n' + - '}\n'); + ' "objects": [\n' + + ' {},\n' + + ' {\n' + + ' "name": "First Object",\n' + + ' "randomProp": "Non-translatable"\n' + + ' },\n' + + ' {\n' + + ' "name": "Second Object",\n' + + ' "description": "String property"\n' + + ' }\n' + + ' ]\n' + + '}\n'); var translations = new TranslationSet('en-US'); translations.add(new ResourceString({ @@ -1816,25 +1816,25 @@ describe("jsonfile", function() { var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "objects": [\n' + - ' {},\n' + - ' {\n' + - ' "name": "Premier objet",\n' + - ' "randomProp": "Non-translatable"\n' + - ' },\n' + - ' {\n' + - ' "name": "Deuxième objet",\n' + - ' "description": "Propriété String"\n' + - ' }\n' + - ' ]\n' + - '}\n'; + '{\n' + + ' "objects": [\n' + + ' {},\n' + + ' {\n' + + ' "name": "Premier objet",\n' + + ' "randomProp": "Non-translatable"\n' + + ' },\n' + + ' {\n' + + ' "name": "Deuxième objet",\n' + + ' "description": "Propriété String"\n' + + ' }\n' + + ' ]\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileLocalizeTextUsePseudoForMissing", function() { + test("JsonFileLocalizeTextUsePseudoForMissing", function () { expect.assertions(2); var jf = new JsonFile({ @@ -1845,191 +1845,191 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singular",\n' + - ' "many": "many",\n' + - ' "other": "plural"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "b",\n' + - ' "c": "d"\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "string 1",\n' + - ' "string 2",\n' + - ' "string 3"\n' + - ' ]\n' + - ' },\n' + - ' "others": {\n' + - ' "first": "abc",\n' + - ' "second": "bcd"\n' + - ' }\n' + - '}\n'); + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singular",\n' + + ' "many": "many",\n' + + ' "other": "plural"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "b",\n' + + ' "c": "d"\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "string 1",\n' + + ' "string 2",\n' + + ' "string 3"\n' + + ' ]\n' + + ' },\n' + + ' "others": {\n' + + ' "first": "abc",\n' + + ' "second": "bcd"\n' + + ' }\n' + + '}\n'); var translations = new TranslationSet(); var actual = jf.localizeText(translations, "fr-FR"); var expected = - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "[šíñğüľàŕ3210]",\n' + - ' "many": "[màñÿ10]",\n' + - ' "other": "[þľüŕàľ210]"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "[b0]",\n' + - ' "c": "[ð0]"\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "[šţŕíñğ 13210]",\n' + - ' "[šţŕíñğ 23210]",\n' + - ' "[šţŕíñğ 33210]"\n' + - ' ]\n' + - ' },\n' + - ' "others": {\n' + - ' "first": "abc",\n' + - ' "second": "bcd"\n' + - ' }\n' + - '}\n'; + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "[šíñğüľàŕ3210]",\n' + + ' "many": "[màñÿ10]",\n' + + ' "other": "[þľüŕàľ210]"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "[b0]",\n' + + ' "c": "[ð0]"\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "[šţŕíñğ 13210]",\n' + + ' "[šţŕíñğ 23210]",\n' + + ' "[šţŕíñğ 33210]"\n' + + ' ]\n' + + ' },\n' + + ' "others": {\n' + + ' "first": "abc",\n' + + ' "second": "bcd"\n' + + ' }\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); -/* - not implemented yet - - test("JsonFileLocalizeTextMethodSpread", function() { - expect.assertions(2); - - var jf = new JsonFile({ - project: p, - pathName: "./json/spread.json", - type: t - }); - expect(jf).toBeTruthy(); - - jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": "this is string two"\n' + - '}\n'); + /* + not implemented yet - var translations = new TranslationSet(); - translations.add(new ResourceString({ - project: "foo", - key: "string 1", - source: "this is string one", - sourceLocale: "en-US", - target: "C'est la chaîne numéro 1", - targetLocale: "fr-FR", - datatype: "json" - })); - translations.add(new ResourceString({ - project: "foo", - key: "string 2", - source: "this is string two", - sourceLocale: "en-US", - target: "C'est la chaîne numéro 2", - targetLocale: "fr-FR", - datatype: "json" - })); + test("JsonFileLocalizeTextMethodSpread", function() { + expect.assertions(2); - var actual = jf.localizeText(translations, "fr-FR"); - var expected = - '{\n' + - ' "string 1": {\n' + - ' "fr-FR": "C\'est la chaîne numéro 1",\n' + - ' },\n' + - ' "string 2": {\n' + - ' "fr-FR": "C\'est la chaîne numéro 2"\n' + - ' },\n' + - '}\n'; + var jf = new JsonFile({ + project: p, + pathName: "./json/spread.json", + type: t + }); + expect(jf).toBeTruthy(); - diff(actual, expected); - expect(actual).toBe(expected); - }); + jf.parse( + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": "this is string two"\n' + + '}\n'); - test("JsonFileLocalizeTextMethodSpreadMultilingual", function() { - expect.assertions(2); + var translations = new TranslationSet(); + translations.add(new ResourceString({ + project: "foo", + key: "string 1", + source: "this is string one", + sourceLocale: "en-US", + target: "C'est la chaîne numéro 1", + targetLocale: "fr-FR", + datatype: "json" + })); + translations.add(new ResourceString({ + project: "foo", + key: "string 2", + source: "this is string two", + sourceLocale: "en-US", + target: "C'est la chaîne numéro 2", + targetLocale: "fr-FR", + datatype: "json" + })); + + var actual = jf.localizeText(translations, "fr-FR"); + var expected = + '{\n' + + ' "string 1": {\n' + + ' "fr-FR": "C\'est la chaîne numéro 1",\n' + + ' },\n' + + ' "string 2": {\n' + + ' "fr-FR": "C\'est la chaîne numéro 2"\n' + + ' },\n' + + '}\n'; - var jf = new JsonFile({ - project: p, - pathName: "./json/spread.json", - type: t + diff(actual, expected); + expect(actual).toBe(expected); }); - expect(jf).toBeTruthy(); - jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": "this is string two"\n' + - '}\n'); + test("JsonFileLocalizeTextMethodSpreadMultilingual", function() { + expect.assertions(2); - var translations = new TranslationSet(); - translations.add(new ResourceString({ - project: "foo", - key: "string 1", - source: "this is string one", - sourceLocale: "en-US", - target: "C'est la chaîne numéro 1", - targetLocale: "fr-FR", - datatype: "json" - })); - translations.add(new ResourceString({ - project: "foo", - key: "string 2", - source: "this is string two", - sourceLocale: "en-US", - target: "C'est la chaîne numéro 2", - targetLocale: "fr-FR", - datatype: "json" - })); - translations.add(new ResourceString({ - project: "foo", - key: "string 1", - source: "this is string one", - sourceLocale: "en-US", - target: "Dies ist die Zeichenfolge 1", - targetLocale: "de", - datatype: "json" - })); - translations.add(new ResourceString({ - project: "foo", - key: "string 2", - source: "this is string two", - sourceLocale: "en-US", - target: "Dies ist die Zeichenfolge 2", - targetLocale: "de", - datatype: "json" - })); + var jf = new JsonFile({ + project: p, + pathName: "./json/spread.json", + type: t + }); + expect(jf).toBeTruthy(); - var actual = jf.localizeText(translations, ["fr-FR", "de"]); - var expected = - '{\n' + - ' "string 1": {\n' + - ' "fr-FR": "C\'est la chaîne numéro 1",\n' + - ' "de": "Dies ist die Zeichenfolge 1",\n' + - ' },\n' + - ' "string 2": {\n' + - ' "fr-FR": "C\'est la chaîne numéro 2"\n' + - ' "de": "Dies ist die Zeichenfolge 2"\n' + - ' },\n' + - '}\n'; + jf.parse( + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": "this is string two"\n' + + '}\n'); - diff(actual, expected); - expect(actual).toBe(expected); - }); -*/ + var translations = new TranslationSet(); + translations.add(new ResourceString({ + project: "foo", + key: "string 1", + source: "this is string one", + sourceLocale: "en-US", + target: "C'est la chaîne numéro 1", + targetLocale: "fr-FR", + datatype: "json" + })); + translations.add(new ResourceString({ + project: "foo", + key: "string 2", + source: "this is string two", + sourceLocale: "en-US", + target: "C'est la chaîne numéro 2", + targetLocale: "fr-FR", + datatype: "json" + })); + translations.add(new ResourceString({ + project: "foo", + key: "string 1", + source: "this is string one", + sourceLocale: "en-US", + target: "Dies ist die Zeichenfolge 1", + targetLocale: "de", + datatype: "json" + })); + translations.add(new ResourceString({ + project: "foo", + key: "string 2", + source: "this is string two", + sourceLocale: "en-US", + target: "Dies ist die Zeichenfolge 2", + targetLocale: "de", + datatype: "json" + })); + + var actual = jf.localizeText(translations, ["fr-FR", "de"]); + var expected = + '{\n' + + ' "string 1": {\n' + + ' "fr-FR": "C\'est la chaîne numéro 1",\n' + + ' "de": "Dies ist die Zeichenfolge 1",\n' + + ' },\n' + + ' "string 2": {\n' + + ' "fr-FR": "C\'est la chaîne numéro 2"\n' + + ' "de": "Dies ist die Zeichenfolge 2"\n' + + ' },\n' + + '}\n'; - test("JsonFileLocalize", function() { + diff(actual, expected); + expect(actual).toBe(expected); + }); + */ + + test("JsonFileLocalize", function () { expect.assertions(7); var base = path.dirname(module.id); @@ -2169,32 +2169,32 @@ describe("jsonfile", function() { var content = fs.readFileSync(path.join(base, "testfiles/resources/fr/FR/messages.json"), "utf-8"); var expected = - '{\n' + - ' "plurals": {\n' + - ' "foo": "asdf",\n' + // this line is not localized - ' "bar": {\n' + - ' "one": "singulaire",\n' + - ' "many": "plupart",\n' + - ' "other": "autres"\n' + - ' }\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "chaîne 1",\n' + - ' "chaîne 2",\n' + - ' "chaîne 3"\n' + - ' ],\n' + - ' "asdfasdf": [\n' + - ' "1",\n' + - ' "2",\n' + - ' "3"\n' + - ' ]\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "la b",\n' + - ' "c": "la d"\n' + - ' }\n' + - '}\n'; + '{\n' + + ' "plurals": {\n' + + ' "foo": "asdf",\n' + // this line is not localized + ' "bar": {\n' + + ' "one": "singulaire",\n' + + ' "many": "plupart",\n' + + ' "other": "autres"\n' + + ' }\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "chaîne 1",\n' + + ' "chaîne 2",\n' + + ' "chaîne 3"\n' + + ' ],\n' + + ' "asdfasdf": [\n' + + ' "1",\n' + + ' "2",\n' + + ' "3"\n' + + ' ]\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "la b",\n' + + ' "c": "la d"\n' + + ' }\n' + + '}\n'; diff(content, expected); expect(content).toBe(expected); @@ -2202,37 +2202,37 @@ describe("jsonfile", function() { content = fs.readFileSync(path.join(base, "testfiles/resources/de/DE/messages.json"), "utf-8"); var expected = - '{\n' + - ' "plurals": {\n' + - ' "foo": "asdf",\n' + // this line is not localized - ' "bar": {\n' + - ' "one": "einslige",\n' + - ' "many": "mehrere",\n' + - ' "other": "andere"\n' + - ' }\n' + - ' },\n' + - ' "arrays": {\n' + - ' "asdf": [\n' + - ' "Zeichenfolge 1",\n' + - ' "Zeichenfolge 2",\n' + - ' "Zeichenfolge 3"\n' + - ' ],\n' + - ' "asdfasdf": [\n' + - ' "1",\n' + - ' "2",\n' + - ' "3"\n' + - ' ]\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "Die b",\n' + - ' "c": "Der d"\n' + - ' }\n' + - '}\n'; + '{\n' + + ' "plurals": {\n' + + ' "foo": "asdf",\n' + // this line is not localized + ' "bar": {\n' + + ' "one": "einslige",\n' + + ' "many": "mehrere",\n' + + ' "other": "andere"\n' + + ' }\n' + + ' },\n' + + ' "arrays": {\n' + + ' "asdf": [\n' + + ' "Zeichenfolge 1",\n' + + ' "Zeichenfolge 2",\n' + + ' "Zeichenfolge 3"\n' + + ' ],\n' + + ' "asdfasdf": [\n' + + ' "1",\n' + + ' "2",\n' + + ' "3"\n' + + ' ]\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "Die b",\n' + + ' "c": "Der d"\n' + + ' }\n' + + '}\n'; diff(content, expected); expect(content).toBe(expected); }); - test("JsonFileLocalizeNoTranslations", function() { + test("JsonFileLocalizeNoTranslations", function () { expect.assertions(5); var base = path.dirname(module.id); @@ -2266,7 +2266,7 @@ describe("jsonfile", function() { expect(fs.existsSync(path.join(base, "testfiles/resources/de/DE/messages.json"))).toBeTruthy(); }); - test("JsonFileLocalizeMethodSparse", function() { + test("JsonFileLocalizeMethodSparse", function () { expect.assertions(7); var base = path.dirname(module.id); @@ -2356,18 +2356,18 @@ describe("jsonfile", function() { var content = fs.readFileSync(path.join(base, "testfiles/resources/fr/FR/sparse2.json"), "utf-8"); var expected = - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "singulaire",\n' + - ' "many": "plupart",\n' + - ' "other": "autres"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "la b"\n' + - ' }\n' + - '}\n'; + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "singulaire",\n' + + ' "many": "plupart",\n' + + ' "other": "autres"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "la b"\n' + + ' }\n' + + '}\n'; diff(content, expected); expect(content).toBe(expected); @@ -2375,23 +2375,23 @@ describe("jsonfile", function() { content = fs.readFileSync(path.join(base, "testfiles/resources/de/DE/sparse2.json"), "utf-8"); var expected = - '{\n' + - ' "plurals": {\n' + - ' "bar": {\n' + - ' "one": "einslige",\n' + - ' "many": "mehrere",\n' + - ' "other": "andere"\n' + - ' }\n' + - ' },\n' + - ' "strings": {\n' + - ' "a": "Die b"\n' + - ' }\n' + - '}\n'; + '{\n' + + ' "plurals": {\n' + + ' "bar": {\n' + + ' "one": "einslige",\n' + + ' "many": "mehrere",\n' + + ' "other": "andere"\n' + + ' }\n' + + ' },\n' + + ' "strings": {\n' + + ' "a": "Die b"\n' + + ' }\n' + + '}\n'; diff(content, expected); expect(content).toBe(expected); }); - test("JsonFileLocalizeExtractNewStrings", function() { + test("JsonFileLocalizeExtractNewStrings", function () { expect.assertions(43); var base = path.dirname(module.id); @@ -2516,7 +2516,7 @@ describe("jsonfile", function() { expect(resources[3].getTargetLocale()).toBe("fr-FR"); }); - test("JsonFileLocalizeWithAlternateFileNameTemplate", function() { + test("JsonFileLocalizeWithAlternateFileNameTemplate", function () { expect.assertions(5); var base = path.dirname(module.id); @@ -2550,7 +2550,7 @@ describe("jsonfile", function() { expect(fs.existsSync(path.join(base, "testfiles/resources/deep_de-DE.json"))).toBeTruthy(); }); - test("JsonFileLocalizeDefaultMethodAndTemplate", function() { + test("JsonFileLocalizeDefaultMethodAndTemplate", function () { expect.assertions(4); var base = path.dirname(module.id); @@ -2563,10 +2563,10 @@ describe("jsonfile", function() { expect(jf).toBeTruthy(); jf.parse( - '{\n' + - ' "string 1": "this is string one",\n' + - ' "string 2": "this is string two"\n' + - '}\n'); + '{\n' + + ' "string 1": "this is string one",\n' + + ' "string 2": "this is string two"\n' + + '}\n'); var translations = new TranslationSet(); translations.add(new ResourceString({ @@ -2594,16 +2594,16 @@ describe("jsonfile", function() { // default method is copy so this should be the whole file var expected = - '{\n' + - ' "string 1": "C\'est la chaîne numéro 1",\n' + - ' "string 2": "this is string two"\n' + - '}\n'; + '{\n' + + ' "string 1": "C\'est la chaîne numéro 1",\n' + + ' "string 2": "this is string two"\n' + + '}\n'; diff(content, expected); expect(content).toBe(expected); }); - test("JsonFileGetLocalizedTextGeneratedString", function() { + test("JsonFileGetLocalizedTextGeneratedString", function () { expect.assertions(2); var base = path.dirname(module.id); @@ -2629,15 +2629,15 @@ describe("jsonfile", function() { var actual = jf.localizeText(undefined, "fr-FR"); var expected = - '{\n' + - ' "string 1": "C\'est la chaîne numéro 1"\n' + - '}\n'; + '{\n' + + ' "string 1": "C\'est la chaîne numéro 1"\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileGetLocalizedTextGeneratedPlural", function() { + test("JsonFileGetLocalizedTextGeneratedPlural", function () { expect.assertions(2); var base = path.dirname(module.id); @@ -2671,15 +2671,15 @@ describe("jsonfile", function() { var actual = jf.localizeText(undefined, "fr-FR"); var expected = - '{\n' + - ' "string 1": "one#Ceci est la chaîne \'one\'|few#Ceci est la chaîne \'few\'|#Ceci est la chaîne \'other\'"\n' + - '}\n'; + '{\n' + + ' "string 1": "one#Ceci est la chaîne \'one\'|few#Ceci est la chaîne \'few\'|#Ceci est la chaîne \'other\'"\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileGetLocalizedTextGeneratedArray", function() { + test("JsonFileGetLocalizedTextGeneratedArray", function () { expect.assertions(2); var base = path.dirname(module.id); @@ -2713,19 +2713,19 @@ describe("jsonfile", function() { var actual = jf.localizeText(undefined, "fr-FR"); var expected = - '{\n' + - ' "string 1": [\n' + - ' "C\'est la chaîne numéro 1",\n' + - ' "C\'est la chaîne numéro 2",\n' + - ' "C\'est la chaîne numéro 3"\n' + - ' ]\n' + - '}\n'; + '{\n' + + ' "string 1": [\n' + + ' "C\'est la chaîne numéro 1",\n' + + ' "C\'est la chaîne numéro 2",\n' + + ' "C\'est la chaîne numéro 3"\n' + + ' ]\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileGetLocalizedTextGeneratedAll", function() { + test("JsonFileGetLocalizedTextGeneratedAll", function () { expect.assertions(2); var base = path.dirname(module.id); @@ -2785,21 +2785,21 @@ describe("jsonfile", function() { var actual = jf.localizeText(undefined, "fr-FR"); var expected = - '{\n' + - ' "string 1": "C\'est la chaîne numéro 1",\n' + - ' "string 2": "one#Ceci est la chaîne \'one\'|few#Ceci est la chaîne \'few\'|#Ceci est la chaîne \'other\'",\n' + - ' "string 3": [\n' + - ' "C\'est la chaîne numéro 1",\n' + - ' "C\'est la chaîne numéro 2",\n' + - ' "C\'est la chaîne numéro 3"\n' + - ' ]\n' + - '}\n'; + '{\n' + + ' "string 1": "C\'est la chaîne numéro 1",\n' + + ' "string 2": "one#Ceci est la chaîne \'one\'|few#Ceci est la chaîne \'few\'|#Ceci est la chaîne \'other\'",\n' + + ' "string 3": [\n' + + ' "C\'est la chaîne numéro 1",\n' + + ' "C\'est la chaîne numéro 2",\n' + + ' "C\'est la chaîne numéro 3"\n' + + ' ]\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); - test("JsonFileGetLocalizedTextGeneratedEscapeDoubleQuotes", function() { + test("JsonFileGetLocalizedTextGeneratedEscapeDoubleQuotes", function () { expect.assertions(2); var base = path.dirname(module.id); @@ -2860,18 +2860,40 @@ describe("jsonfile", function() { // need to escape the double quotes for json syntax var expected = - '{\n' + - ' "string 1": "C\'est la \\\"chaîne\\\" numéro 1",\n' + - ' "string 2": "one#Ceci est la chaîne \\\"one\\\"|few#Ceci est la chaîne \\\"few\\\"|#Ceci est la chaîne \\\"other\\\"",\n' + - ' "string 3": [\n' + - ' "C\'est la chaîne numéro \\\"1\\\"",\n' + - ' "C\'est la chaîne numéro \\\"2\\\"",\n' + - ' "C\'est la chaîne numéro \\\"3\\\""\n' + - ' ]\n' + - '}\n'; + '{\n' + + ' "string 1": "C\'est la \\\"chaîne\\\" numéro 1",\n' + + ' "string 2": "one#Ceci est la chaîne \\\"one\\\"|few#Ceci est la chaîne \\\"few\\\"|#Ceci est la chaîne \\\"other\\\"",\n' + + ' "string 3": [\n' + + ' "C\'est la chaîne numéro \\\"1\\\"",\n' + + ' "C\'est la chaîne numéro \\\"2\\\"",\n' + + ' "C\'est la chaîne numéro \\\"3\\\""\n' + + ' ]\n' + + '}\n'; diff(actual, expected); expect(actual).toBe(expected); }); }); + +it.each([ + { localizable: false, schema: { localizable: "source" } }, + { localizable: false, schema: { localizable: "source" } }, + + { localizable: false, schema: { localizable: true } }, + { localizable: false, schema: { localizable: true } }, +])("sets localizable to true, for supported schema.localizable values", ({localizable, schema}) => { + + const result = JsonFile.prototype.isLocalizable(localizable, schema); + + expect(result).toBe(true); +}); + +it.each([ + { localizable: false, schema: { localizable: "invalid keyword" } }, +])("sets localizable to false, for unsupported schema.localizable values", ({localizable, schema}) => { + + const result = JsonFile.prototype.isLocalizable(localizable, schema); + + expect(result).toBe(false); +}); From 304c1e213871a0e05cfbc254954a2a6667dcbf58 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natalia=20K=C4=99dziora?= Date: Thu, 6 Mar 2025 12:58:35 +0100 Subject: [PATCH 2/7] [ilib-loctool-json] Replace bitwise OR with logical OR --- packages/ilib-loctool-json/JsonFile.js | 9 ++-- .../ilib-loctool-json/test/JsonFile.test.js | 44 +++++++++---------- 2 files changed, 27 insertions(+), 26 deletions(-) diff --git a/packages/ilib-loctool-json/JsonFile.js b/packages/ilib-loctool-json/JsonFile.js index 5b286b89f..2bbbcf54a 100644 --- a/packages/ilib-loctool-json/JsonFile.js +++ b/packages/ilib-loctool-json/JsonFile.js @@ -288,7 +288,7 @@ function getSchemaType(schema, type, root) { return getSchemaType(subschema, type, root); }); if (subschema) { - subschema.localizable |= localizable; + subschema.localizable = subschema.localizable || localizable; } return subschema; } @@ -297,7 +297,8 @@ function getSchemaType(schema, type, root) { subschema = getSchemaType(schema.not, type, root); if (subschema) { if (type === subschema.type) return undefined; // matches, so "not" fails - subschema.localizable |= localizable; + subschema.localizable = subschema.localizable || localizable; + } return subschema; } @@ -308,7 +309,7 @@ function getSchemaType(schema, type, root) { }); if (matches.length !== 1) return undefined; if (matches[0]) { - matches[0].localizable |= localizable; + matches[0].localizable = matches[0].localizable || localizable; } return matches[0]; } @@ -320,7 +321,7 @@ function getSchemaType(schema, type, root) { if (!matches) return undefined; // it matches all of them, so just choose the first one if (matches[0]) { - matches[0].localizable |= localizable; + matches[0].localizable = matches[0].localizable || localizable; } return matches[0]; } diff --git a/packages/ilib-loctool-json/test/JsonFile.test.js b/packages/ilib-loctool-json/test/JsonFile.test.js index 2d603dd18..f251354b6 100644 --- a/packages/ilib-loctool-json/test/JsonFile.test.js +++ b/packages/ilib-loctool-json/test/JsonFile.test.js @@ -2875,25 +2875,25 @@ describe("jsonfile", function () { }); }); - -it.each([ - { localizable: false, schema: { localizable: "source" } }, - { localizable: false, schema: { localizable: "source" } }, - - { localizable: false, schema: { localizable: true } }, - { localizable: false, schema: { localizable: true } }, -])("sets localizable to true, for supported schema.localizable values", ({localizable, schema}) => { - - const result = JsonFile.prototype.isLocalizable(localizable, schema); - - expect(result).toBe(true); -}); - -it.each([ - { localizable: false, schema: { localizable: "invalid keyword" } }, -])("sets localizable to false, for unsupported schema.localizable values", ({localizable, schema}) => { - - const result = JsonFile.prototype.isLocalizable(localizable, schema); - - expect(result).toBe(false); -}); +// +// it.each([ +// { localizable: false, schema: { localizable: "source" } }, +// { localizable: false, schema: { localizable: "source" } }, +// +// { localizable: false, schema: { localizable: true } }, +// { localizable: false, schema: { localizable: true } }, +// ])("sets localizable to true, for supported schema.localizable values", ({localizable, schema}) => { +// +// const result = JsonFile.prototype.isLocalizable(localizable, schema); +// +// expect(result).toBe(true); +// }); +// +// it.each([ +// { localizable: false, schema: { localizable: "invalid keyword" } }, +// ])("sets localizable to false, for unsupported schema.localizable values", ({localizable, schema}) => { +// +// const result = JsonFile.prototype.isLocalizable(localizable, schema); +// +// expect(result).toBe(false); +// }); From 52b61cc584bd9abd8f0fc1aa94012445ad4b8f3b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natalia=20K=C4=99dziora?= Date: Thu, 6 Mar 2025 13:34:55 +0100 Subject: [PATCH 3/7] [ilib-loctool-json] Refactor support for "localizable: source" keyword --- packages/ilib-loctool-json/JsonFile.js | 557 ++++++++++++------------- 1 file changed, 272 insertions(+), 285 deletions(-) diff --git a/packages/ilib-loctool-json/JsonFile.js b/packages/ilib-loctool-json/JsonFile.js index 2bbbcf54a..d5629ed34 100644 --- a/packages/ilib-loctool-json/JsonFile.js +++ b/packages/ilib-loctool-json/JsonFile.js @@ -32,7 +32,7 @@ var Locale = require("ilib/lib/Locale.js"); * of the project file * @param {FileType} type the file type instance of this file */ -var JsonFile = function(options) { +var JsonFile = function (options) { this.project = options.project; this.pathName = path.normalize(options.pathName || ""); this.type = options.type; @@ -60,15 +60,12 @@ var JsonFile = function(options) { * @param {String} string the string to unescape * @returns {String} the unescaped string */ -JsonFile.unescapeString = function(string) { +JsonFile.unescapeString = function (string) { var unescaped = string; unescaped = he.decode(unescaped); - unescaped = unescaped. - replace(/^\\\\/g, "\\"). - replace(/([^\\])\\\\/g, "$1\\"). - replace(/\\(.)/g, "$1"); + unescaped = unescaped.replace(/^\\\\/g, "\\").replace(/([^\\])\\\\/g, "$1\\").replace(/\\(.)/g, "$1"); return unescaped; }; @@ -85,47 +82,29 @@ JsonFile.unescapeString = function(string) { * @param {String} string the string to clean * @returns {String} the cleaned string */ -JsonFile.cleanString = function(string) { +JsonFile.cleanString = function (string) { var unescaped = JsonFile.unescapeString(string); - unescaped = unescaped. - replace(/[ \n\t\r\f]+/g, " "). - trim(); + unescaped = unescaped.replace(/[ \n\t\r\f]+/g, " ").trim(); return unescaped; }; -JsonFile.escapeProp = function(prop) { - return prop. - replace(/~/g, "~0"). - replace(/\//g, "~1"); +JsonFile.escapeProp = function (prop) { + return prop.replace(/~/g, "~0").replace(/\//g, "~1"); }; -JsonFile.unescapeProp = function(prop) { - return prop. - replace(/~1/g, "/"). - replace(/~0/g, "~"); +JsonFile.unescapeProp = function (prop) { + return prop.replace(/~1/g, "/").replace(/~0/g, "~"); }; -JsonFile.escapeRef = function(prop) { - return JsonFile.escapeProp(prop). - replace(/%/g, "%25"). - replace(/\^/g, "%5E"). - replace(/\|/g, "%7C"). - replace(/\\/g, "%5C"). - replace(/"/g, "%22"). - replace(/ /g, "%20"); +JsonFile.escapeRef = function (prop) { + return JsonFile.escapeProp(prop).replace(/%/g, "%25").replace(/\^/g, "%5E").replace(/\|/g, "%7C").replace(/\\/g, "%5C").replace(/"/g, "%22").replace(/ /g, "%20"); }; -JsonFile.unescapeRef = function(prop) { - return JsonFile.unescapeProp(prop. - replace(/%5E/g, "^"). - replace(/%7C/g, "|"). - replace(/%5C/g, "\\"). - replace(/%22/g, "\""). - replace(/%20/g, " "). - replace(/%25/g, "%")); +JsonFile.unescapeRef = function (prop) { + return JsonFile.unescapeProp(prop.replace(/%5E/g, "^").replace(/%7C/g, "|").replace(/%5C/g, "\\").replace(/%22/g, "\"").replace(/%20/g, " ").replace(/%25/g, "%")); }; function isPrimitive(type) { @@ -142,13 +121,13 @@ function getArrayTypeFromSchema(schema, root) { return null; } - if (typeof(schema.items["$ref"]) !== 'undefined') { + if (typeof (schema.items["$ref"]) !== 'undefined') { // substitute the referenced schema for this one var refname = schema.items["$ref"]; var otherschema = root["$$refs"][refname]; if (!otherschema) { console.log("Unknown reference " + refname + " while parsing " + - this.pathName + " with schema " + root["$id"]); + this.pathName + " with schema " + root["$id"]); return; } schema = otherschema; @@ -170,7 +149,7 @@ function getArrayTypeFromSchema(schema, root) { * Converts each element of one-dimensional array to a primitive type. */ function convertArrayElementsToType(array, type) { - if (!ilib.isArray(array)){ + if (!ilib.isArray(array)) { return array; } @@ -212,14 +191,14 @@ var pluralCategories = { function isPlural(node) { if (!node) return false; var props = Object.keys(node); - return Object.keys(node).length && props.every(function(prop) { - return pluralCategories[prop] && typeof(node[prop]) === "string"; + return Object.keys(node).length && props.every(function (prop) { + return pluralCategories[prop] && typeof (node[prop]) === "string"; }); } function isNotEmpty(obj) { - if (isPrimitive(typeof(obj))) { - return typeof(obj) !== 'undefined'; + if (isPrimitive(typeof (obj))) { + return typeof (obj) !== 'undefined'; } else if (ilib.isArray(obj)) { return obj.length > 0; } else { @@ -241,10 +220,10 @@ function isNotEmpty(obj) { * primitive processed by the visitor function */ function objectMap(object, visitor) { - if (isPrimitive(typeof(object))) { + if (isPrimitive(typeof (object))) { return visitor(object); } else if (ilib.isArray(object)) { - return object.map(function(item) { + return object.map(function (item) { return objectMap(item, visitor); }); } else { @@ -258,7 +237,7 @@ function objectMap(object, visitor) { } } -JsonFile.prototype.sparseValue = function(value) { +JsonFile.prototype.sparseValue = function (value) { return (!this.mapping || !this.mapping.method || this.mapping.method !== "sparse") ? value : undefined; }; @@ -267,7 +246,7 @@ function getSchemaType(schema, type, root) { var subschema; // dereference first so we can figure out if the type matches below - if (typeof(schema["$ref"]) !== 'undefined') { + if (typeof (schema["$ref"]) !== 'undefined') { // substitute the referenced schema for this one var refname = schema["$ref"]; var otherschema = root["$$refs"][refname]; @@ -284,7 +263,7 @@ function getSchemaType(schema, type, root) { } if (schema.anyOf) { - subschema = schema.anyOf.find(function(subschema) { + subschema = schema.anyOf.find(function (subschema) { return getSchemaType(subschema, type, root); }); if (subschema) { @@ -304,7 +283,7 @@ function getSchemaType(schema, type, root) { } if (schema.oneOf) { - var matches = schema.oneOf.map(function(subschema) { + var matches = schema.oneOf.map(function (subschema) { return getSchemaType(subschema, type, root); }); if (matches.length !== 1) return undefined; @@ -315,7 +294,7 @@ function getSchemaType(schema, type, root) { } if (schema.allOf) { - var matches = schema.allOf.every(function(subschema) { + var matches = schema.allOf.every(function (subschema) { return getSchemaType(subschema, type, root); }); if (!matches) return undefined; @@ -330,275 +309,283 @@ function getSchemaType(schema, type, root) { return undefined; } -JsonFile.prototype.extracted = function(localizable, json, ref, translations, locale, returnValue, type) { - 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); +JsonFile.prototype.handleSource = function (json, ref, translations, locale, returnValue, type) { + 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 { - 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); + source = text; } + translatedText = this.type.pseudos[locale].getString(source); } 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; + if (translated) { + translatedText = translated.getTarget(); } else { - opts.source = text; + 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); + } } - this.set.add(this.API.newResource(opts)); - returnValue = this.sparseValue(text); + } + if (translatedText) { + returnValue = convertValueToType(translatedText, type); } } 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); + // 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); } } 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); } return {text, returnValue}; } -JsonFile.prototype.isLocalizable = function (localizable, schema) { - const keywords = ["source"]; +JsonFile.prototype.extractFromPrimitive = function (localizable, json, ref, translations, locale, returnValue, type) { + console.log({localizable}) + + switch (localizable) { + case true: + case "source": + const __ret = this.handleSource(json, ref, translations, locale, returnValue, type); + var text = __ret.text; + returnValue = __ret.returnValue; + break + default: + returnValue = this.sparseValue(json); + break; + } + - localizable = localizable || schema.localizable === true || keywords.includes(schema.localizable); - return localizable; + return {text, returnValue}; } -JsonFile.prototype.parseObj = function(json, root, schema, ref, name, localizable, translations, locale) { +JsonFile.prototype.parseObj = function (json, root, schema, ref, name, localizable, translations, locale) { if (!json || !schema) return; if (this.type.hasType(schema)) { var returnValue; - var thisType = Array.isArray(json) ? "array" : typeof(json); + var thisType = Array.isArray(json) ? "array" : typeof (json); schema = getSchemaType(schema, thisType, root); // if the type of this value does not match the schema type, just skip this node // in the tree if (!schema) return this.sparseValue(json); - localizable = this.isLocalizable(localizable, schema); - var type = schema.type || typeof(json); + localizable = localizable || schema.localizable + var type = schema.type || typeof (json); switch (type) { - case "boolean": - case "number": - case "integer": - case "string": - const __ret = this.extracted(localizable, json, ref, translations, locale, returnValue, type); - var text = __ret.text; - returnValue = __ret.returnValue; - break; + case "boolean": + case "number": + case "integer": + case "string": + const __ret = this.extractFromPrimitive(localizable, json, ref, translations, locale, returnValue, type); + var text = __ret.text; + returnValue = __ret.returnValue; + 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 (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(); + 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 (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], + 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; } } return isNotEmpty(returnValue) ? returnValue : undefined; }; -JsonFile.prototype.parseObjArray = function(json, root, schema, ref, name, localizable, translations, locale) { +JsonFile.prototype.parseObjArray = function (json, root, schema, ref, name, localizable, translations, locale) { if (!ilib.isArray(json)) { this.logger.warn(this.pathName + '/' + ref + " is a " + - typeof(json) + " but should be an array according to the schema... skipping."); + typeof (json) + " but should be an array according to the schema... skipping."); return null; } @@ -634,7 +621,7 @@ JsonFile.prototype.parseObjArray = function(json, root, schema, ref, name, local } // Convert all items to Strings so we can process them properly - var sourceArray = json.map(function(item) { + var sourceArray = json.map(function (item) { return String(item); }); @@ -686,13 +673,13 @@ JsonFile.prototype.parseObjArray = function(json, root, schema, ref, name, local 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); + tester.cleanHashKey(), + this.type.datatype); source = sourceRes ? sourceRes.getTargetArray() : sourceArray; } else { source = sourceArray; } - translatedArray = source.map(function(item) { + translatedArray = source.map(function (item) { return this.type.pseudos[locale].getString(item); }.bind(this)); @@ -724,7 +711,7 @@ JsonFile.prototype.parseObjArray = function(json, root, schema, ref, name, local })); if (this.type.missingPseudo && !this.project.settings.nopseudo) { - translatedArray = sourceArray.map(function(item) { + translatedArray = sourceArray.map(function (item) { return this.type.missingPseudo.getString(item); }.bind(this)); translatedArray = this.sparseValue(translatedArray); @@ -740,7 +727,7 @@ JsonFile.prototype.parseObjArray = function(json, root, schema, ref, name, local * project's translation set. * @param {String} data the string to parse */ -JsonFile.prototype.parse = function(data) { +JsonFile.prototype.parse = function (data) { this.logger.debug("Extracting strings from " + this.pathName); this.json = JSON5.parse(data); @@ -753,7 +740,7 @@ JsonFile.prototype.parse = function(data) { * Extract all the localizable strings from the Json file and add them to the * project's translation set. */ -JsonFile.prototype.extract = function() { +JsonFile.prototype.extract = function () { this.logger.debug("Extracting strings from " + this.pathName); if (this.pathName) { var p = path.join(this.project.root, this.pathName); @@ -775,11 +762,11 @@ JsonFile.prototype.extract = function() { * @returns {TranslationSet} The set of resources found in the * current Json file. */ -JsonFile.prototype.getTranslationSet = function() { +JsonFile.prototype.getTranslationSet = function () { return this.set; } -JsonFile.prototype.write = function() { +JsonFile.prototype.write = function () { if (!this.json && this.isDirty()) { var locale = this.locale.getSpec(); var pathName = this.pathName || this.getLocalizedPath(locale); @@ -799,7 +786,7 @@ JsonFile.prototype.write = function() { * * @returns {Resource} all of the resources available in this resource file. */ -JsonFile.prototype.getAll = function() { +JsonFile.prototype.getAll = function () { return this.set.getAll(); }; @@ -811,7 +798,7 @@ JsonFile.prototype.getAll = function() { * * @param {Resource} res a resource to add to this file */ -JsonFile.prototype.addResource = function(res) { +JsonFile.prototype.addResource = function (res) { this.logger.trace("JsonFile.addResource: " + JSON.stringify(res) + " to " + this.project.getProjectId() + ", " + this.locale + ", " + JSON.stringify(this.context)); var resLocale = res.getTargetLocale() || res.getSourceLocale(); if (res && res.getProject() === this.project.getProjectId() && resLocale === this.locale.getSpec()) { @@ -837,7 +824,7 @@ JsonFile.prototype.addResource = function(res) { * @returns {boolean} true if this resource file has been * modified since it was loaded */ -JsonFile.prototype.isDirty = function() { +JsonFile.prototype.isDirty = function () { return this.set.isDirty(); }; @@ -847,22 +834,22 @@ JsonFile.prototype.isDirty = function() { * @param {String] locale the locale spec for the target locale * @returns {String} the localized path name */ -JsonFile.prototype.getLocalizedPath = function(locale) { +JsonFile.prototype.getLocalizedPath = function (locale) { return this.type.getLocalizedPath(this.mapping, this.pathName, locale); }; -JsonFile.prototype.generateText = function(locale) { +JsonFile.prototype.generateText = function (locale) { var returnValue = {}; var resources = this.set.getAll(); - resources.forEach(function(res) { + resources.forEach(function (res) { switch (res.getType()) { case 'plural': var strings = res.getTargetPlurals() || res.getSourcePlurals(); var categories = Object.keys(pluralCategories); - returnValue[res.getKey()] = categories.filter(function(cat) { + returnValue[res.getKey()] = categories.filter(function (cat) { return strings[cat]; - }).map(function(cat) { + }).map(function (cat) { return (cat !== "other" ? cat : "") + "#" + strings[cat]; }).join("|"); break; @@ -887,7 +874,7 @@ JsonFile.prototype.generateText = function(locale) { * @param {String} locale the locale to translate to * @returns {String} the localized text of this file */ -JsonFile.prototype.localizeText = function(translations, locale) { +JsonFile.prototype.localizeText = function (translations, locale) { // "#" is the root reference var returnValue = (!this.json && this.set.size() > 0) ? this.generateText(locale) : @@ -903,7 +890,7 @@ JsonFile.prototype.localizeText = function(translations, locale) { * translations * @param {Array.} locales array of locales to translate to */ -JsonFile.prototype.localize = function(translations, locales) { +JsonFile.prototype.localize = function (translations, locales) { // don't localize if there is no text for (var i = 0; i < locales.length; i++) { if (!this.project.isSourceLocale(locales[i])) { From 148d019e99d35f711ffa88609e64683f8c40836d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natalia=20K=C4=99dziora?= Date: Thu, 6 Mar 2025 13:47:49 +0100 Subject: [PATCH 4/7] [ilib-loctool-json] Add support for "localizable: comment" keyword --- packages/ilib-loctool-json/JsonFile.js | 24 ++++++++++++++++++++++++ 1 file changed, 24 insertions(+) diff --git a/packages/ilib-loctool-json/JsonFile.js b/packages/ilib-loctool-json/JsonFile.js index d5629ed34..fd52a8be5 100644 --- a/packages/ilib-loctool-json/JsonFile.js +++ b/packages/ilib-loctool-json/JsonFile.js @@ -309,6 +309,26 @@ function getSchemaType(schema, type, root) { return undefined; } +JsonFile.prototype.handleComment = function (json, ref) { + 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; + } +} + JsonFile.prototype.handleSource = function (json, ref, translations, locale, returnValue, type) { if (isPrimitive(typeof (json))) { var text = String(json); @@ -410,6 +430,10 @@ JsonFile.prototype.extractFromPrimitive = function (localizable, json, ref, tran var text = __ret.text; returnValue = __ret.returnValue; break + case "comment": + this.handleComment(json, ref); + returnValue = this.sparseValue(json); + break; default: returnValue = this.sparseValue(json); break; From 62b42790f42f11b9a2c4e36c40620acd011a4553 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natalia=20K=C4=99dziora?= Date: Thu, 6 Mar 2025 15:46:10 +0100 Subject: [PATCH 5/7] [ilib-loctool-json] Add support for "localizable: key" keyword --- packages/ilib-loctool-json/JsonFile.js | 15 ++++++++++++--- 1 file changed, 12 insertions(+), 3 deletions(-) diff --git a/packages/ilib-loctool-json/JsonFile.js b/packages/ilib-loctool-json/JsonFile.js index fd52a8be5..d4d0819ed 100644 --- a/packages/ilib-loctool-json/JsonFile.js +++ b/packages/ilib-loctool-json/JsonFile.js @@ -394,7 +394,7 @@ JsonFile.prototype.handleSource = function (json, ref, translations, locale, ret var opts = { resType: "string", project: this.project.getProjectId(), - key: key, + key: this.key ?? key, sourceLocale: this.project.sourceLocale, pathName: this.pathName, state: "new", @@ -421,8 +421,6 @@ JsonFile.prototype.handleSource = function (json, ref, translations, locale, ret } JsonFile.prototype.extractFromPrimitive = function (localizable, json, ref, translations, locale, returnValue, type) { - console.log({localizable}) - switch (localizable) { case true: case "source": @@ -444,6 +442,7 @@ JsonFile.prototype.extractFromPrimitive = function (localizable, json, ref, tran } JsonFile.prototype.parseObj = function (json, root, schema, ref, name, localizable, translations, locale) { + console.log(11111, localizable) if (!json || !schema) return; if (this.type.hasType(schema)) { @@ -456,7 +455,11 @@ JsonFile.prototype.parseObj = function (json, root, schema, ref, name, localizab // in the tree if (!schema) return this.sparseValue(json); + const prevLoc = localizable; localizable = localizable || schema.localizable + console.log(22222, localizable) + console.log(33333, prevLoc) + var type = schema.type || typeof (json); switch (type) { case "boolean": @@ -473,6 +476,12 @@ JsonFile.prototype.parseObj = function (json, root, schema, ref, name, localizab break; case "object": + if(localizable === "key") { + this.key = ref.startsWith("#/") ? JsonFile.unescapeRef(ref).substring(2) : ref; + console.log(11111) + localizable = prevLoc; + } + if (typeof (json) !== "object") { this.logger.warn(this.pathName + '/' + ref + " is a " + typeof (json) + " but should be an object according to the schema... skipping."); From 57fca729093c8c82d3b6e06b5764b3e26abde151 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natalia=20K=C4=99dziora?= Date: Thu, 6 Mar 2025 15:47:01 +0100 Subject: [PATCH 6/7] TODO: Add tests From debe8108506cc8bb3744a91d551a9faf3a50045d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Natalia=20K=C4=99dziora?= Date: Fri, 7 Mar 2025 08:11:24 +0100 Subject: [PATCH 7/7] [ilib-loctool-json] Add test for localizable keywords --- packages/ilib-loctool-json/JsonFile.js | 2 +- .../ilib-loctool-json/test/JsonFile.test.js | 101 ++++++++++++++---- .../test/testfiles/json/localizable.json | 18 ++++ .../testfiles/schemas/localizable-schema.json | 25 +++++ 4 files changed, 123 insertions(+), 23 deletions(-) create mode 100644 packages/ilib-loctool-json/test/testfiles/json/localizable.json create mode 100644 packages/ilib-loctool-json/test/testfiles/schemas/localizable-schema.json diff --git a/packages/ilib-loctool-json/JsonFile.js b/packages/ilib-loctool-json/JsonFile.js index d4d0819ed..e8d205530 100644 --- a/packages/ilib-loctool-json/JsonFile.js +++ b/packages/ilib-loctool-json/JsonFile.js @@ -429,7 +429,7 @@ JsonFile.prototype.extractFromPrimitive = function (localizable, json, ref, tran returnValue = __ret.returnValue; break case "comment": - this.handleComment(json, ref); + this.handleComment(json, this.key ?? ref); returnValue = this.sparseValue(json); break; default: diff --git a/packages/ilib-loctool-json/test/JsonFile.test.js b/packages/ilib-loctool-json/test/JsonFile.test.js index f251354b6..cb1fafc18 100644 --- a/packages/ilib-loctool-json/test/JsonFile.test.js +++ b/packages/ilib-loctool-json/test/JsonFile.test.js @@ -2875,25 +2875,82 @@ describe("jsonfile", function () { }); }); -// -// it.each([ -// { localizable: false, schema: { localizable: "source" } }, -// { localizable: false, schema: { localizable: "source" } }, -// -// { localizable: false, schema: { localizable: true } }, -// { localizable: false, schema: { localizable: true } }, -// ])("sets localizable to true, for supported schema.localizable values", ({localizable, schema}) => { -// -// const result = JsonFile.prototype.isLocalizable(localizable, schema); -// -// expect(result).toBe(true); -// }); -// -// it.each([ -// { localizable: false, schema: { localizable: "invalid keyword" } }, -// ])("sets localizable to false, for unsupported schema.localizable values", ({localizable, schema}) => { -// -// const result = JsonFile.prototype.isLocalizable(localizable, schema); -// -// expect(result).toBe(false); -// }); + +describe("JsonFile localizable schema keyword", () => { + test('parses localizable properties correctly', () => { + const project = createTestProject(); + const jsonFileType = new JsonFileType(project); + const jsonFile = new JsonFile({ + project: project, + type: jsonFileType, + pathName: './testfiles/schemas/localizable.json' + }); + + jsonFile.parse(`{ + "project.whateverModal.saveButton": { + "defaultMessage": "Save", + "description": "Button text for save" + } + }`); + + const set = jsonFile.getTranslationSet(); + const resource = set.get(ResourceString.hashKey("foo", "en-US", "project.whateverModal.saveButton", "json")); + + expect(resource).toBeTruthy(); + expect(resource.getSource()).toBe("Save"); + expect(resource.getKey()).toBe("project.whateverModal.saveButton"); + expect(resource.getComment()).toBe("Button text for save"); + }); + + test('extracts localizable properties correctly', () => { + const project = createTestProject(); + const jsonFileType = new JsonFileType(project); + const jsonFile = new JsonFile({ + project: project, + type: jsonFileType, + pathName: './testfiles/schemas/localizable.json' + }); + + jsonFile.parse(`{ + "project.whateverModal.saveButton": { + "defaultMessage": "Save", + "description": "Button text for save" + } + }`); + + const set = jsonFile.getTranslationSet(); + const resource = set.get(ResourceString.hashKey("foo", "en-US", "project.whateverModal.saveButton", "json")); + + expect(resource).toBeTruthy(); + + jsonFile.extract(); + const translationSet = jsonFile.getTranslationSet(); + const resources = translationSet.getAll(); + + expect(resources).toHaveLength(1); + }); +}); + +function createTestProject() { + return new CustomProject({ + name: 'foo', + id: 'foo', + sourceLocale: 'en-US' + }, './test/testfiles', { + locales: ['en-GB'], + targetDir: '.', + nopseudo: true, + json: { + schemas: [ + "./test/testfiles/schemas" + ], + mappings: { + "**/localizable.json": { + "schema": "localizable-schema", + "method": "copy", + "template": "resources/localizable_[locale].json" + } + } + } + }) +} diff --git a/packages/ilib-loctool-json/test/testfiles/json/localizable.json b/packages/ilib-loctool-json/test/testfiles/json/localizable.json new file mode 100644 index 000000000..0d0d88bcf --- /dev/null +++ b/packages/ilib-loctool-json/test/testfiles/json/localizable.json @@ -0,0 +1,18 @@ +{ + "project.whateverModal.saveButton": { + "defaultMessage": "Save", + "description": "Button text for save" + }, + "project.whateverModal.createButton": { + "defaultMessage": "Create", + "description": "Button text for create" + }, + "project.whateverModal.invalid": { + "defaultMessage": "Invalid", + "description": "Title for invalid" + }, + "project.whateverModal.nameLabel": { + "description": "Input label", + "defaultMessage": "Name" + } +} diff --git a/packages/ilib-loctool-json/test/testfiles/schemas/localizable-schema.json b/packages/ilib-loctool-json/test/testfiles/schemas/localizable-schema.json new file mode 100644 index 000000000..e5e0793da --- /dev/null +++ b/packages/ilib-loctool-json/test/testfiles/schemas/localizable-schema.json @@ -0,0 +1,25 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "$id": "localizable-schema", + "title": "Sample schema for testing localizable supported keywords (comment, key, source)", + "type": "object", + "additionalProperties": { + "type": "object", + "localizable": "key", + "properties": { + "defaultMessage": { + "type": "string", + "localizable": "source" + }, + "description": { + "type": "string", + "localizable": "comment" + } + }, + "required": [ + "defaultMessage", + "description" + ], + "additionalProperties": false + } +}