aboutsummaryrefslogtreecommitdiff
path: root/packages/pogen/src/potextract.ts
diff options
context:
space:
mode:
Diffstat (limited to 'packages/pogen/src/potextract.ts')
-rw-r--r--packages/pogen/src/potextract.ts743
1 files changed, 438 insertions, 305 deletions
diff --git a/packages/pogen/src/potextract.ts b/packages/pogen/src/potextract.ts
index 243d44c6f..3ebcb3700 100644
--- a/packages/pogen/src/potextract.ts
+++ b/packages/pogen/src/potextract.ts
@@ -19,7 +19,7 @@
*/
import * as ts from "typescript";
import * as fs from "fs";
-import * as path from "path"
+import * as path from "path";
const DEFAULT_PO_HEADER = `# SOME DESCRIPTIVE TITLE.
# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
@@ -38,368 +38,501 @@ msgstr ""
"Language: \\n"
"MIME-Version: 1.0\\n"
"Content-Type: text/plain; charset=UTF-8\\n"
-"Content-Transfer-Encoding: 8bit\\n"\n\n`
-
+"Content-Transfer-Encoding: 8bit\\n"\n\n`;
function wordwrap(str: string, width: number = 80): string[] {
var regex = ".{1," + width + "}(\\s|$)|\\S+(\\s|$)";
return str.match(RegExp(regex, "g"));
}
-function processFile(
+function getTemplate(node: ts.Node): string {
+ switch (node.kind) {
+ case ts.SyntaxKind.FirstTemplateToken:
+ return (<any>node).text;
+ case ts.SyntaxKind.TemplateExpression:
+ let te = <ts.TemplateExpression>node;
+ let textFragments = [te.head.text];
+ for (let tsp of te.templateSpans) {
+ textFragments.push(`%${(textFragments.length - 1) / 2 + 1}$s`);
+ textFragments.push(tsp.literal.text.replace(/%/g, "%%"));
+ }
+ return textFragments.join("");
+ default:
+ return "(pogen.ts: unable to parse)";
+ }
+}
+
+function getComment(
sourceFile: ts.SourceFile,
- outChunks: string[],
- knownMessageIds: Set<string>,
-) {
- let lastTokLine = 0;
- let preLastTokLine = 0;
- processNode(sourceFile);
-
- function getTemplate(node: ts.Node): string {
- switch (node.kind) {
- case ts.SyntaxKind.FirstTemplateToken:
- return (<any>node).text;
- case ts.SyntaxKind.TemplateExpression:
- let te = <ts.TemplateExpression>node;
- let textFragments = [te.head.text];
- for (let tsp of te.templateSpans) {
- textFragments.push(`%${(textFragments.length - 1) / 2 + 1}$s`);
- textFragments.push(tsp.literal.text.replace(/%/g, "%%"));
- }
- return textFragments.join("");
- default:
- return "(pogen.ts: unable to parse)";
+ preLastTokLine: number,
+ lastTokLine: number,
+ node: ts.Node,
+): string {
+ let lc = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
+ 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);
+ if (comments) {
+ lastComments = comments;
}
}
+ if (!lastComments) {
+ return;
+ }
+ let candidate = lastComments[lastComments.length - 1];
+ let candidateEndLine = ts.getLineAndCharacterOfPosition(
+ sourceFile,
+ candidate.end,
+ ).line;
+ if (candidateEndLine != lc.line - 1) {
+ return;
+ }
+ let text = sourceFile.text.slice(candidate.pos, candidate.end);
+ switch (candidate.kind) {
+ case ts.SyntaxKind.SingleLineCommentTrivia:
+ // Remove comment leader
+ text = text.replace(/^[/][/]\s*/, "");
+ break;
+ case ts.SyntaxKind.MultiLineCommentTrivia:
+ // Remove comment leader and trailer,
+ // handling white space just like xgettext.
+ text = text
+ .replace(/^[/][*](\s*?\n|\s*)?/, "")
+ .replace(/(\n[ \t]*?)?[*][/]$/, "");
+ break;
+ }
+ return text;
+}
- function getComment(node: ts.Node): string {
- let lc = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
- let lastComments;
- for (let l = preLastTokLine; l < lastTokLine; l++) {
- let pos = ts.getPositionOfLineAndCharacter(sourceFile, l, 0);
- let comments = ts.getTrailingCommentRanges(sourceFile.text, pos);
- if (comments) {
- lastComments = comments;
- }
+function getPath(node: ts.Node): { path: string[]; ctx: string } {
+ switch (node.kind) {
+ case ts.SyntaxKind.PropertyAccessExpression: {
+ let pae = <ts.PropertyAccessExpression>node;
+ return {
+ path: Array.prototype.concat(getPath(pae.expression).path, [
+ pae.name.text,
+ ]),
+ ctx: "",
+ };
}
- if (!lastComments) {
- return;
+ case ts.SyntaxKind.Identifier: {
+ let id = <ts.Identifier>node;
+ return {
+ path: [id.text],
+ ctx: "",
+ };
}
- let candidate = lastComments[lastComments.length - 1];
- let candidateEndLine = ts.getLineAndCharacterOfPosition(
- sourceFile,
- candidate.end,
- ).line;
- if (candidateEndLine != lc.line - 1) {
- return;
+ 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,
+ };
+ }
}
- let text = sourceFile.text.slice(candidate.pos, candidate.end);
- switch (candidate.kind) {
- case ts.SyntaxKind.SingleLineCommentTrivia:
- // Remove comment leader
- text = text.replace(/^[/][/]\s*/, "");
- break;
- case ts.SyntaxKind.MultiLineCommentTrivia:
- // Remove comment leader and trailer,
- // handling white space just like xgettext.
- text = text
- .replace(/^[/][*](\s*?\n|\s*)?/, "")
- .replace(/(\n[ \t]*?)?[*][/]$/, "");
- break;
+ default: {
+ // console.log("ASDASD", ts.SyntaxKind[node.kind], node);
}
- return text;
}
+ return {
+ path: ["(other)"],
+ ctx: "",
+ };
+}
- function getPath(node: ts.Node): string[] {
- switch (node.kind) {
- case ts.SyntaxKind.PropertyAccessExpression:
- let pae = <ts.PropertyAccessExpression>node;
- return Array.prototype.concat(getPath(pae.expression), [pae.name.text]);
- case ts.SyntaxKind.Identifier:
- let id = <ts.Identifier>node;
- return [id.text];
- }
- return ["(other)"];
+function arrayEq<T>(a1: T[], a2: T[]) {
+ if (a1.length != a2.length) {
+ return false;
}
-
- function arrayEq<T>(a1: T[], a2: T[]) {
- if (a1.length != a2.length) {
+ for (let i = 0; i < a1.length; i++) {
+ if (a1[i] != a2[i]) {
return false;
}
- for (let i = 0; i < a1.length; i++) {
- if (a1[i] != a2[i]) {
- return false;
- }
- }
- return true;
}
+ return true;
+}
- interface TemplateResult {
- comment: string;
- path: string[];
- template: string;
- line: number;
- }
+interface TemplateResult {
+ comment: string;
+ path: string[];
+ template: string;
+ line: number;
+ context: string;
+}
- function processTaggedTemplateExpression(
- tte: ts.TaggedTemplateExpression,
- ): TemplateResult {
- let lc = ts.getLineAndCharacterOfPosition(sourceFile, tte.pos);
- if (lc.line != lastTokLine) {
- preLastTokLine = lastTokLine;
- lastTokLine = lc.line;
- }
- let path = getPath(tte.tag);
- let res: TemplateResult = {
- path,
- line: lc.line,
- comment: getComment(tte),
- template: getTemplate(tte.template),
- };
- return res;
- }
+function processTaggedTemplateExpression(
+ sourceFile: ts.SourceFile,
+ preLastTokLine: number,
+ lastTokLine: number,
+ tte: ts.TaggedTemplateExpression,
+): TemplateResult {
+ let path = getPath(tte.tag);
+ let res: TemplateResult = {
+ path: path.path,
+ line: lastTokLine,
+ comment: getComment(sourceFile, preLastTokLine, lastTokLine, tte),
+ template: getTemplate(tte.template),
+ context: path.ctx,
+ };
+ return res;
+}
- function formatMsgComment(line: number, comment?: string) {
- if (comment) {
- for (let cl of comment.split("\n")) {
- outChunks.push(`#. ${cl}\n`);
- }
+function formatMsgComment(
+ sourceFile: ts.SourceFile,
+ outChunks: string[],
+ line: number,
+ comment?: string,
+) {
+ if (comment) {
+ for (let cl of comment.split("\n")) {
+ outChunks.push(`#. ${cl}\n`);
}
- const fn = path.posix.relative(process.cwd(), sourceFile.fileName);
- outChunks.push(`#: ${fn}:${line + 1}\n`);
- outChunks.push(`#, c-format\n`);
}
+ const fn = path.posix.relative(process.cwd(), sourceFile.fileName);
+ outChunks.push(`#: ${fn}:${line + 1}\n`);
+ outChunks.push(`#, c-format\n`);
+}
- function formatMsgLine(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));
- let parts = m.map((x) => x.replace(/\n/g, "\\n").replace(/"/g, '\\"'))
- .map((p) => wordwrap(p))
- .reduce((a, b) => a.concat(b));
- if (parts.length == 1) {
- outChunks.push(`${head} "${parts[0]}"\n`);
- } else {
- outChunks.push(`${head} ""\n`);
- for (let p of parts) {
- outChunks.push(`"${p}"\n`);
- }
+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));
+ let parts = m
+ .map((x) => x.replace(/\n/g, "\\n").replace(/"/g, '\\"'))
+ .map((p) => wordwrap(p))
+ .reduce((a, b) => a.concat(b));
+ if (parts.length == 1) {
+ outChunks.push(`${head} "${parts[0]}"\n`);
+ } else {
+ outChunks.push(`${head} ""\n`);
+ for (let p of parts) {
+ outChunks.push(`"${p}"\n`);
}
}
+}
- function getJsxElementPath(node: ts.Node) {
- let path;
- let process = (childNode) => {
- switch (childNode.kind) {
- case ts.SyntaxKind.JsxOpeningElement: {
- let e = childNode as ts.JsxOpeningElement;
- return (path = getPath(e.tagName));
- }
- default:
- break;
- }
- };
- ts.forEachChild(node, process);
- return path;
- }
-
- function translateJsxExpression(node: ts.Node, h) {
- switch (node.kind) {
- case ts.SyntaxKind.StringLiteral: {
- let e = node as ts.StringLiteral;
- return e.text;
+function getJsxElementPath(node: ts.Node) {
+ let path;
+ let process = (childNode) => {
+ switch (childNode.kind) {
+ case ts.SyntaxKind.JsxOpeningElement: {
+ let e = childNode as ts.JsxOpeningElement;
+ return (path = getPath(e.tagName).path);
}
default:
- return `%${h[0]++}$s`;
+ break;
}
- }
+ };
+ ts.forEachChild(node, process);
+ return path;
+}
- function trim(s: string) {
- return s.replace(/^[ \n\t]*/, "").replace(/[ \n\t]*$/, "");
+function translateJsxExpression(node: ts.Node, h) {
+ switch (node.kind) {
+ case ts.SyntaxKind.StringLiteral: {
+ let e = node as ts.StringLiteral;
+ return e.text;
+ }
+ default:
+ return `%${h[0]++}$s`;
}
+}
- function getJsxContent(node: ts.Node) {
- let fragments = [];
- let holeNum = [1];
- let process = (childNode) => {
- switch (childNode.kind) {
- case ts.SyntaxKind.JsxText: {
- let e = childNode as ts.JsxText;
- let s = e.text;
- let t = s.split("\n").map(trim).join(" ");
- if (s[0] === " ") {
- t = " " + t;
- }
- if (s[s.length - 1] === " ") {
- t = t + " ";
+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;
}
- fragments.push(t);
- }
- case ts.SyntaxKind.JsxOpeningElement:
- break;
- case ts.SyntaxKind.JsxSelfClosingElement:
- case ts.SyntaxKind.JsxElement:
- fragments.push(`%${holeNum[0]++}$s`);
- break;
- case ts.SyntaxKind.JsxExpression: {
- let e = childNode as ts.JsxExpression;
- fragments.push(translateJsxExpression(e.expression, holeNum));
- break;
- }
- case ts.SyntaxKind.JsxClosingElement:
- break;
- default:
- console.log("unhandled node type: ", childNode.kind)
- let lc = ts.getLineAndCharacterOfPosition(
- childNode.getSourceFile(),
- childNode.getStart(),
- );
- console.error(
- `unrecognized syntax in JSX Element ${ts.SyntaxKind[childNode.kind]} (${childNode.getSourceFile().fileName}:${lc.line + 1}:${lc.character + 1}`,
- );
- break;
+ result[id] = JSON.parse(v.getText(sour));
+ });
+ return;
}
- };
- ts.forEachChild(node, process);
- return fragments.join("").trim().replace(/ +/g, " ");
- }
+ }
+ });
+ return result;
+}
- function getJsxSingular(node: ts.Node) {
- let res;
- let process = (childNode) => {
- switch (childNode.kind) {
- case ts.SyntaxKind.JsxElement: {
- let path = getJsxElementPath(childNode);
- if (arrayEq(path, ["i18n", "TranslateSingular"])) {
- res = getJsxContent(childNode);
- }
+function getJsxContent(node: ts.Node) {
+ let fragments = [];
+ let holeNum = [1];
+ let process = (childNode) => {
+ switch (childNode.kind) {
+ case ts.SyntaxKind.JsxText: {
+ let e = childNode as ts.JsxText;
+ let s = e.text;
+ let t = s.split("\n").map(trim).join(" ");
+ if (s[0] === " ") {
+ t = " " + t;
+ }
+ if (s[s.length - 1] === " ") {
+ t = t + " ";
}
- default:
- break;
+ fragments.push(t);
}
- };
- ts.forEachChild(node, process);
- return res;
- }
+ case ts.SyntaxKind.JsxOpeningElement:
+ break;
+ case ts.SyntaxKind.JsxSelfClosingElement:
+ case ts.SyntaxKind.JsxElement:
+ fragments.push(`%${holeNum[0]++}$s`);
+ break;
+ case ts.SyntaxKind.JsxExpression: {
+ let e = childNode as ts.JsxExpression;
+ fragments.push(translateJsxExpression(e.expression, holeNum));
+ break;
+ }
+ case ts.SyntaxKind.JsxClosingElement:
+ break;
+ default:
+ console.log("unhandled node type: ", childNode.kind);
+ let lc = ts.getLineAndCharacterOfPosition(
+ childNode.getSourceFile(),
+ childNode.getStart(),
+ );
+ console.error(
+ `unrecognized syntax in JSX Element ${
+ ts.SyntaxKind[childNode.kind]
+ } (${childNode.getSourceFile().fileName}:${lc.line + 1}:${
+ lc.character + 1
+ }`,
+ );
+ break;
+ }
+ };
+ ts.forEachChild(node, process);
+ return fragments.join("").trim().replace(/ +/g, " ");
+}
- function getJsxPlural(node: ts.Node) {
- let res;
- let process = (childNode) => {
- switch (childNode.kind) {
- case ts.SyntaxKind.JsxElement: {
- let path = getJsxElementPath(childNode);
- if (arrayEq(path, ["i18n", "TranslatePlural"])) {
- res = getJsxContent(childNode);
- }
+function getJsxSingular(node: ts.Node) {
+ let res;
+ let process = (childNode) => {
+ switch (childNode.kind) {
+ case ts.SyntaxKind.JsxElement: {
+ let path = getJsxElementPath(childNode);
+ if (arrayEq(path, ["i18n", "TranslateSingular"])) {
+ res = getJsxContent(childNode);
}
- default:
- break;
}
- };
- ts.forEachChild(node, process);
- return res;
- }
+ default:
+ break;
+ }
+ };
+ ts.forEachChild(node, process);
+ return res;
+}
- function processNode(node: ts.Node) {
- switch (node.kind) {
- 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(node);
- if (!knownMessageIds.has(content)) {
- knownMessageIds.add(content);
- formatMsgComment(line, comment);
- formatMsgLine("msgid", content);
- outChunks.push(`msgstr ""\n`);
- outChunks.push("\n");
- }
- return;
- }
- if (arrayEq(path, ["i18n", "TranslateSwitch"])) {
- let { line } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
- let comment = getComment(node);
- formatMsgComment(line, comment);
- let singularForm = getJsxSingular(node);
- if (!singularForm) {
- console.error("singular form missing");
- process.exit(1);
- }
- let pluralForm = getJsxPlural(node);
- if (!pluralForm) {
- console.error("plural form missing");
- process.exit(1);
- }
- if (!knownMessageIds.has(singularForm)) {
- knownMessageIds.add(singularForm);
- formatMsgLine("msgid", singularForm);
- formatMsgLine("msgid_plural", pluralForm);
- outChunks.push(`msgstr[0] ""\n`);
- outChunks.push(`msgstr[1] ""\n`);
- outChunks.push(`\n`);
- }
- return;
+function getJsxPlural(node: ts.Node) {
+ let res;
+ let process = (childNode) => {
+ switch (childNode.kind) {
+ case ts.SyntaxKind.JsxElement: {
+ let path = getJsxElementPath(childNode);
+ if (arrayEq(path, ["i18n", "TranslatePlural"])) {
+ res = getJsxContent(childNode);
}
+ }
+ default:
break;
- case ts.SyntaxKind.CallExpression: {
- // might be i18n.plural(i18n[.X]`...`, i18n[.X]`...`)
- let ce = <ts.CallExpression>node;
- let path = getPath(ce.expression);
- if (!arrayEq(path, ["i18n", "plural"])) {
- break;
- }
- if (ce.arguments[0].kind != ts.SyntaxKind.TaggedTemplateExpression) {
- break;
- }
- if (ce.arguments[1].kind != ts.SyntaxKind.TaggedTemplateExpression) {
- break;
- }
- let { line } = ts.getLineAndCharacterOfPosition(sourceFile, ce.pos);
- let t1 = processTaggedTemplateExpression(
- <ts.TaggedTemplateExpression>ce.arguments[0],
- );
- let t2 = processTaggedTemplateExpression(
- <ts.TaggedTemplateExpression>ce.arguments[1],
+ }
+ };
+ ts.forEachChild(node, process);
+ return res;
+}
+
+function processNode(
+ node: ts.Node,
+ preLastTokLine: number,
+ lastTokLine: number,
+ sourceFile: ts.SourceFile,
+ outChunks: string[],
+ knownMessageIds: Set<string>,
+) {
+ switch (node.kind) {
+ case ts.SyntaxKind.JsxElement:
+ let path = getJsxElementPath(node);
+ if (arrayEq(path, ["i18n", "Translate"])) {
+ const content = getJsxContent(node);
+ const { line } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
+ const comment = getComment(
+ sourceFile,
+ preLastTokLine,
+ lastTokLine,
+ node,
);
- let comment = getComment(ce);
- const msgid = t1.template;
+ const context = getJsxAttribute(sourceFile, node)["context"] ?? "";
+ const msgid = context + content;
if (!knownMessageIds.has(msgid)) {
knownMessageIds.add(msgid);
- formatMsgComment(line, comment);
- formatMsgLine("msgid", t1.template);
- formatMsgLine("msgid_plural", t2.template);
- outChunks.push(`msgstr[0] ""\n`);
- outChunks.push(`msgstr[1] ""\n`);
+ formatMsgComment(sourceFile, outChunks, line, comment);
+ formatMsgLine(outChunks, "msgctxt", context);
+ formatMsgLine(outChunks, "msgid", content);
+ outChunks.push(`msgstr ""\n`);
outChunks.push("\n");
}
-
- // Important: no processing for child i18n expressions here
return;
}
- case ts.SyntaxKind.TaggedTemplateExpression: {
- let tte = <ts.TaggedTemplateExpression>node;
- let { comment, template, line, path } =
- processTaggedTemplateExpression(tte);
- if (path[0] != "i18n") {
- break;
+ if (arrayEq(path, ["i18n", "TranslateSwitch"])) {
+ let { line } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
+ let comment = getComment(sourceFile, preLastTokLine, lastTokLine, node);
+ formatMsgComment(sourceFile, outChunks, line, comment);
+ let singularForm = getJsxSingular(node);
+ if (!singularForm) {
+ console.error("singular form missing");
+ process.exit(1);
}
- const msgid = template;
+ let pluralForm = getJsxPlural(node);
+ if (!pluralForm) {
+ console.error("plural form missing");
+ process.exit(1);
+ }
+ const context = getJsxAttribute(sourceFile, node)["context"] ?? "";
+ const msgid = context + singularForm;
if (!knownMessageIds.has(msgid)) {
knownMessageIds.add(msgid);
- formatMsgComment(line, comment);
- formatMsgLine("msgid", template);
- outChunks.push(`msgstr ""\n`);
- outChunks.push("\n");
+ formatMsgLine(outChunks, "msgctxt", context);
+ formatMsgLine(outChunks, "msgid", singularForm);
+ formatMsgLine(outChunks, "msgid_plural", pluralForm);
+ outChunks.push(`msgstr[0] ""\n`);
+ outChunks.push(`msgstr[1] ""\n`);
+ outChunks.push(`\n`);
}
+ return;
+ }
+ break;
+ case ts.SyntaxKind.CallExpression: {
+ // might be i18n.plural(i18n[.X]`...`, i18n[.X]`...`)
+ let ce = <ts.CallExpression>node;
+ let path = getPath(ce.expression);
+ if (!arrayEq(path.path, ["i18n", "plural"])) {
break;
}
- }
+ if (ce.arguments[0].kind != ts.SyntaxKind.TaggedTemplateExpression) {
+ break;
+ }
+ if (ce.arguments[1].kind != ts.SyntaxKind.TaggedTemplateExpression) {
+ 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,
+ 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,
+ tte2,
+ );
+ let comment = getComment(sourceFile, preLastTokLine, lastTokLine, ce);
+ 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`);
+ outChunks.push(`msgstr[1] ""\n`);
+ outChunks.push("\n");
+ }
- ts.forEachChild(node, processNode);
+ // Important: no processing for child i18n expressions here
+ return;
+ }
+ case ts.SyntaxKind.TaggedTemplateExpression: {
+ let tte = <ts.TaggedTemplateExpression>node;
+ 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 = 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");
+ }
+ break;
+ }
}
+
+ ts.forEachChild(node, (n) => {
+ processNode(
+ n,
+ lastTokLine,
+ preLastTokLine,
+ sourceFile,
+ outChunks,
+ knownMessageIds,
+ );
+ });
+}
+
+export function processFile2(sourceFile: ts.SourceFile): string {
+ // let lastTokLine = 0;
+ // let preLastTokLine = 0;
+ const result: string[] = new Array<string>();
+ processNode(sourceFile, 0, 0, sourceFile, result, new Set<string>());
+ return result.join("");
+}
+
+export function processFile(
+ sourceFile: ts.SourceFile,
+ outChunks: string[],
+ knownMessageIds: Set<string>,
+) {
+ // let lastTokLine = 0;
+ // let preLastTokLine = 0;
+ processNode(sourceFile, 0, 0, sourceFile, outChunks, knownMessageIds);
}
export function potextract() {
@@ -439,11 +572,11 @@ export function potextract() {
!prog.isSourceFileDefaultLibrary(x),
);
- let header: string
+ let header: string;
try {
- header = fs.readFileSync("src/i18n/poheader", "utf-8")
+ header = fs.readFileSync("src/i18n/poheader", "utf-8");
} catch (e) {
- header = DEFAULT_PO_HEADER
+ header = DEFAULT_PO_HEADER;
}
const chunks = [header];