taler-typescript-core

Wallet core logic and WebUIs for various components
Log | Files | Refs | Submodules | README | LICENSE

commit 590073c50f2561ee10538fe0cfda5378a501bbd7
parent f6ca179d6da99b61988529cbd91a6177d6f3e692
Author: Sebastian <sebasjm@gmail.com>
Date:   Tue, 29 Oct 2024 12:52:28 -0300

add support for context

Diffstat:
Mpackages/pogen/src/potextract.test.ts | 60+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Mpackages/pogen/src/potextract.ts | 154++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------------
2 files changed, 177 insertions(+), 37 deletions(-)

diff --git a/packages/pogen/src/potextract.test.ts b/packages/pogen/src/potextract.test.ts @@ -35,7 +35,7 @@ function process(src: string): string { return processFile2(source).trim(); } -test("p2p: should select the coin", (t) => { +test("should extract the key from inner body", (t) => { t.deepEqual( process(`<i18n.Translate>something</i18n.Translate>`), `#: test.tsx:3 @@ -44,3 +44,61 @@ msgid "something" msgstr ""`, ); }); + +test("should support context on tags", (t) => { + t.deepEqual( + process( + `return <div> + <i18n.Translate context="some_context" anotherkey="not the context" asd={"asd"} zxc={asd()}>something</i18n.Translate> + </div>`, + ), + `#: test.tsx:4 +#, c-format +msgctxt "some_context" +msgid "something" +msgstr ""`, + ); +}); + +test("should support context on string template", (t) => { + t.deepEqual( + process(`return i18n.context("wire transfer")\`send\`;`), + `#: test.tsx:3 +#, c-format +msgctxt "wire transfer" +msgid "send" +msgstr ""`, + ); +}); + +test("should support same message id with different context", (t) => { + t.deepEqual( + process( + `return i18n.context("wire transfer")\`send\` + i18n.context("gift")\`send\`;`, + ), + `#: test.tsx:3 +#, c-format +msgctxt "wire transfer" +msgid "send" +msgstr "" + +#: test.tsx:3 +#, c-format +msgctxt "gift" +msgid "send" +msgstr ""`, + ); +}); + +test("should support on string template", (t) => { + t.deepEqual( + process(` + // comment of the translation + return i18n.str\`another key\`;`), + `#. comment of the translation +#: test.tsx:5 +#, c-format +msgid "another key" +msgstr ""`, + ); +}); diff --git a/packages/pogen/src/potextract.ts b/packages/pogen/src/potextract.ts @@ -64,12 +64,12 @@ function getTemplate(node: ts.Node): string { function getComment( sourceFile: ts.SourceFile, - lastTokLine: number, preLastTokLine: number, + lastTokLine: number, node: ts.Node, ): string { let lc = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); - let lastComments; + let lastComments: ts.CommentRange[]; for (let l = preLastTokLine; l < lastTokLine; l++) { let pos = ts.getPositionOfLineAndCharacter(sourceFile, l, 0); let comments = ts.getTrailingCommentRanges(sourceFile.text, pos); @@ -105,16 +105,47 @@ function getComment( return text; } -function getPath(node: ts.Node): string[] { +function getPath(node: ts.Node): { path: string[]; ctx: string } { switch (node.kind) { - case ts.SyntaxKind.PropertyAccessExpression: + case ts.SyntaxKind.PropertyAccessExpression: { let pae = <ts.PropertyAccessExpression>node; - return Array.prototype.concat(getPath(pae.expression), [pae.name.text]); - case ts.SyntaxKind.Identifier: + return { + path: Array.prototype.concat(getPath(pae.expression).path, [ + pae.name.text, + ]), + ctx: "", + }; + } + case ts.SyntaxKind.Identifier: { let id = <ts.Identifier>node; - return [id.text]; + return { + path: [id.text], + ctx: "", + }; + } + case ts.SyntaxKind.CallExpression: { + const call = <ts.CallExpression>node; + + const firstArg = call.arguments[0]; + if ( + call.arguments.length === 1 && + firstArg.kind === ts.SyntaxKind.StringLiteral + ) { + const str = <ts.StringLiteral>firstArg; + return { + path: getPath(call.expression).path, + ctx: str.text, + }; + } + } + default: { + // console.log("ASDASD", ts.SyntaxKind[node.kind], node); + } } - return ["(other)"]; + return { + path: ["(other)"], + ctx: "", + }; } function arrayEq<T>(a1: T[], a2: T[]) { @@ -134,6 +165,7 @@ interface TemplateResult { path: string[]; template: string; line: number; + context: string; } function processTaggedTemplateExpression( @@ -142,17 +174,13 @@ function processTaggedTemplateExpression( lastTokLine: number, tte: ts.TaggedTemplateExpression, ): TemplateResult { - let lc = ts.getLineAndCharacterOfPosition(sourceFile, tte.pos); - if (lc.line != lastTokLine) { - preLastTokLine = lastTokLine; // HERE - lastTokLine = lc.line; - } let path = getPath(tte.tag); let res: TemplateResult = { - path, - line: lc.line, + path: path.path, + line: lastTokLine, comment: getComment(sourceFile, preLastTokLine, lastTokLine, tte), template: getTemplate(tte.template), + context: path.ctx, }; return res; } @@ -177,8 +205,8 @@ function formatMsgLine(outChunks: string[], head: string, msg: string) { const m = msg.match(/(.*\n|.+$)/g); if (!m) return; // Do escaping, wrap break at newlines - console.log("head", JSON.stringify(head)); - console.log("msg", JSON.stringify(msg)); + // console.log("head", JSON.stringify(head)); + // console.log("msg", JSON.stringify(msg)); let parts = m .map((x) => x.replace(/\n/g, "\\n").replace(/"/g, '\\"')) .map((p) => wordwrap(p)) @@ -199,7 +227,7 @@ function getJsxElementPath(node: ts.Node) { switch (childNode.kind) { case ts.SyntaxKind.JsxOpeningElement: { let e = childNode as ts.JsxOpeningElement; - return (path = getPath(e.tagName)); + return (path = getPath(e.tagName).path); } default: break; @@ -224,6 +252,28 @@ function trim(s: string) { return s.replace(/^[ \n\t]*/, "").replace(/[ \n\t]*$/, ""); } +function getJsxAttribute(sour: ts.SourceFile, node: ts.Node) { + const result = {}; + ts.forEachChild(node, (childNode: ts.Node) => { + switch (childNode.kind) { + case ts.SyntaxKind.JsxOpeningElement: { + let e = childNode as ts.JsxOpeningElement; + + e.attributes.properties.map((p) => { + const id = p.getChildAt(0, sour).getText(sour); + const v = p.getChildAt(2, sour); + if (v.kind !== ts.SyntaxKind.StringLiteral) { + return undefined; + } + result[id] = JSON.parse(v.getText(sour)); + }); + return; + } + } + }); + return result; +} + function getJsxContent(node: ts.Node) { let fragments = []; let holeNum = [1]; @@ -322,12 +372,20 @@ function processNode( case ts.SyntaxKind.JsxElement: let path = getJsxElementPath(node); if (arrayEq(path, ["i18n", "Translate"])) { - let content = getJsxContent(node); - let { line } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); - let comment = getComment(sourceFile, preLastTokLine, lastTokLine, node); - if (!knownMessageIds.has(content)) { - knownMessageIds.add(content); + const content = getJsxContent(node); + const { line } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos); + const comment = getComment( + sourceFile, + preLastTokLine, + lastTokLine, + node, + ); + const context = getJsxAttribute(sourceFile, node)["context"] ?? ""; + const msgid = context + content; + if (!knownMessageIds.has(msgid)) { + knownMessageIds.add(msgid); formatMsgComment(sourceFile, outChunks, line, comment); + formatMsgLine(outChunks, "msgctxt", context); formatMsgLine(outChunks, "msgid", content); outChunks.push(`msgstr ""\n`); outChunks.push("\n"); @@ -348,8 +406,11 @@ function processNode( console.error("plural form missing"); process.exit(1); } - if (!knownMessageIds.has(singularForm)) { - knownMessageIds.add(singularForm); + const context = getJsxAttribute(sourceFile, node)["context"] ?? ""; + const msgid = context + singularForm; + if (!knownMessageIds.has(msgid)) { + knownMessageIds.add(msgid); + formatMsgLine(outChunks, "msgctxt", context); formatMsgLine(outChunks, "msgid", singularForm); formatMsgLine(outChunks, "msgid_plural", pluralForm); outChunks.push(`msgstr[0] ""\n`); @@ -363,7 +424,7 @@ function processNode( // might be i18n.plural(i18n[.X]`...`, i18n[.X]`...`) let ce = <ts.CallExpression>node; let path = getPath(ce.expression); - if (!arrayEq(path, ["i18n", "plural"])) { + if (!arrayEq(path.path, ["i18n", "plural"])) { break; } if (ce.arguments[0].kind != ts.SyntaxKind.TaggedTemplateExpression) { @@ -373,23 +434,37 @@ function processNode( break; } let { line } = ts.getLineAndCharacterOfPosition(sourceFile, ce.pos); + const tte1 = <ts.TaggedTemplateExpression>ce.arguments[0]; + let lc1 = ts.getLineAndCharacterOfPosition(sourceFile, tte1.pos); + if (lc1.line != lastTokLine) { + preLastTokLine = lastTokLine; // HERE + lastTokLine = lc1.line; + } let t1 = processTaggedTemplateExpression( sourceFile, preLastTokLine, lastTokLine, - <ts.TaggedTemplateExpression>ce.arguments[0], + tte1, ); + + const tte2 = <ts.TaggedTemplateExpression>ce.arguments[1]; + let lc2 = ts.getLineAndCharacterOfPosition(sourceFile, tte2.pos); + if (lc2.line != lastTokLine) { + preLastTokLine = lastTokLine; // HERE + lastTokLine = lc2.line; + } let t2 = processTaggedTemplateExpression( sourceFile, preLastTokLine, lastTokLine, - <ts.TaggedTemplateExpression>ce.arguments[1], + tte2, ); let comment = getComment(sourceFile, preLastTokLine, lastTokLine, ce); - const msgid = t1.template; + const msgid = path.ctx + t1.template; if (!knownMessageIds.has(msgid)) { knownMessageIds.add(msgid); formatMsgComment(sourceFile, outChunks, line, comment); + formatMsgLine(outChunks, "msgctxt", path.ctx); formatMsgLine(outChunks, "msgid", t1.template); formatMsgLine(outChunks, "msgid_plural", t2.template); outChunks.push(`msgstr[0] ""\n`); @@ -402,19 +477,26 @@ function processNode( } case ts.SyntaxKind.TaggedTemplateExpression: { let tte = <ts.TaggedTemplateExpression>node; - let { comment, template, line, path } = processTaggedTemplateExpression( - sourceFile, - preLastTokLine, - lastTokLine, - tte, - ); + let lc2 = ts.getLineAndCharacterOfPosition(sourceFile, tte.pos); + if (lc2.line != lastTokLine) { + preLastTokLine = lastTokLine; + lastTokLine = lc2.line; + } + let { comment, template, line, path, context } = + processTaggedTemplateExpression( + sourceFile, + preLastTokLine, + lastTokLine, + tte, + ); if (path[0] != "i18n") { break; } - const msgid = template; + const msgid = context + template; if (!knownMessageIds.has(msgid)) { knownMessageIds.add(msgid); formatMsgComment(sourceFile, outChunks, line, comment); + formatMsgLine(outChunks, "msgctxt", context); formatMsgLine(outChunks, "msgid", template); outChunks.push(`msgstr ""\n`); outChunks.push("\n");