summaryrefslogtreecommitdiff
path: root/packages/pogen
diff options
context:
space:
mode:
Diffstat (limited to 'packages/pogen')
-rwxr-xr-xpackages/pogen/bin/pogen2
-rw-r--r--packages/pogen/example/proj1/package.json5
-rw-r--r--packages/pogen/example/proj1/src/i18n/test.pot (renamed from packages/pogen/example/messages.po)105
-rw-r--r--packages/pogen/example/proj1/src/test.ts (renamed from packages/pogen/example/test.ts)3
-rw-r--r--packages/pogen/example/proj1/src/test2.tsx (renamed from packages/pogen/example/test2.tsx)2
-rw-r--r--packages/pogen/example/proj1/tsconfig.json2
-rw-r--r--packages/pogen/package.json10
-rw-r--r--packages/pogen/po2.js32
-rw-r--r--packages/pogen/src/dumpTree.ts (renamed from packages/pogen/dumpTree.ts)0
-rw-r--r--packages/pogen/src/po2ts.ts146
-rw-r--r--packages/pogen/src/pogen.ts48
-rw-r--r--packages/pogen/src/potextract.ts (renamed from packages/pogen/pogen.ts)217
-rw-r--r--packages/pogen/tsconfig.json23
13 files changed, 413 insertions, 182 deletions
diff --git a/packages/pogen/bin/pogen b/packages/pogen/bin/pogen
index a7ef879d8..3b17d1df7 100755
--- a/packages/pogen/bin/pogen
+++ b/packages/pogen/bin/pogen
@@ -1,2 +1,2 @@
-#!/usr/bin/env node
+#!/usr/bin/env -S node --trace-deprecation
require('../lib/pogen.js').main();
diff --git a/packages/pogen/example/proj1/package.json b/packages/pogen/example/proj1/package.json
index 954139ecf..97adf0f3a 100644
--- a/packages/pogen/example/proj1/package.json
+++ b/packages/pogen/example/proj1/package.json
@@ -7,5 +7,8 @@
"test": "echo \"Error: no test specified\" && exit 1"
},
"author": "",
- "license": "ISC"
+ "license": "ISC",
+ "pogen": {
+ "domain": "test"
+ }
}
diff --git a/packages/pogen/example/messages.po b/packages/pogen/example/proj1/src/i18n/test.pot
index 1addae3f2..8bc5ef236 100644
--- a/packages/pogen/example/messages.po
+++ b/packages/pogen/example/proj1/src/i18n/test.pot
@@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: PACKAGE VERSION\n"
"Report-Msgid-Bugs-To: \n"
-"POT-Creation-Date: 2016-01-27 01:51+0100\n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <LL@li.org>\n"
@@ -17,68 +17,78 @@ msgstr ""
"Content-Type: text/plain; charset=UTF-8\n"
"Content-Transfer-Encoding: 8bit\n"
-#: example/test.ts:3
-#, csharp-format
+#: src/test.ts:4
+#, c-format
msgid "Hello1, World"
msgstr ""
-#: example/test.ts:4
-#, csharp-format
-msgid "Hello2, World"
+#: src/test.ts:8
+#, c-format
+msgid "Hello, quoted \"world\""
msgstr ""
-#: example/test.ts:5
-#, csharp-format
-msgid "Hello3, World"
-msgstr ""
-
-#. This is a comment and should be included
-#: example/test.ts:9
-#, csharp-format
-msgid "Hello4, World"
-msgstr ""
-
-#: example/test.ts:12
-#, csharp-format
+#: src/test.ts:15
+#, c-format
msgid "Hello5, World"
msgstr ""
-#: example/test.ts:13
-#, csharp-format
-msgid "Hello6,{0} World"
+#: src/test.ts:16
+#, c-format
+msgid "Hello6,%1$s World"
msgstr ""
#. This one has a multi line comment.
#. It has multiple lines, and a trailing empty line.
#.
-#: example/test.ts:20
-#, csharp-format
-msgid "Hello7,{0} World{1}"
+#: src/test.ts:23
+#, c-format
+msgid "Hello7,%1$s World%2$s"
msgstr ""
-#: example/test.ts:21
-#, csharp-format
-msgid "{0}Hello8,{1} World{2}"
+#: src/test.ts:23
+#, c-format
+msgid "one %1$s"
+msgid_plural "many %1$s"
+msgstr[0] ""
+msgstr[1] ""
+
+#: src/test.ts:26
+#, c-format
+msgid "one bla %1$s"
+msgid_plural "many bla %1$s"
+msgstr[0] ""
+msgstr[1] ""
+
+#: src/test.ts:31
+#, c-format
+msgid "I have %1$s apple"
+msgid_plural "I have %1$s apples"
+msgstr[0] ""
+msgstr[1] ""
+
+#: src/test.ts:35
+#, c-format
+msgid "%1$sHello8,%2$s World%3$s"
msgstr ""
#.
#. This one has a multi line comment.
#. It has multiple lines, and a leading empty line.
-#: example/test.ts:28
-#, csharp-format
+#: src/test.ts:42
+#, c-format
msgid "Hello9,\" '\" World"
msgstr ""
-#: example/test.ts:32
-#, csharp-format
+#: src/test.ts:46
+#, c-format
msgid ""
"Hello10\n"
" ,\" '\" Wo\n"
" rld"
msgstr ""
-#: example/test.ts:37
-#, csharp-format
+#: src/test.ts:51
+#, c-format
msgid ""
"Hello11 this is a long long string\n"
"it will go over multiple lines and in the pofile\n"
@@ -86,8 +96,8 @@ msgid ""
msgstr ""
#. This is a single line comment
-#: example/test.ts:42
-#, csharp-format
+#: src/test.ts:56
+#, c-format
msgid ""
"Hello12 this is a long long string it will go over multiple lines and in the "
"pofile it should be wrapped and stuff. asdf asdf asdf asdf asdf asdf asdf asdf "
@@ -95,13 +105,26 @@ msgid ""
"asdf"
msgstr ""
-#: example/test.ts:42
-#, csharp-format
+#. First occurrence
+#: src/test.ts:65
+#, c-format
msgid "This message appears twice"
msgstr ""
-#: example/test.ts:45
-#, csharp-format
-msgid "This message appears twice"
+#: src/test2.tsx:1
+#, c-format
+msgid "foo %1$s foo baz"
+msgstr ""
+
+#: src/test2.tsx:5
+#, c-format
+msgid "singular form second line"
+msgid_plural "plural form"
+msgstr[0] ""
+msgstr[1] ""
+
+#: src/test2.tsx:17
+#, c-format
+msgid "\"foo\""
msgstr ""
diff --git a/packages/pogen/example/test.ts b/packages/pogen/example/proj1/src/test.ts
index 6577fa314..2e9b4cbdd 100644
--- a/packages/pogen/example/test.ts
+++ b/packages/pogen/example/proj1/src/test.ts
@@ -1,9 +1,12 @@
declare var i18n: any;
+
console.log(i18n`Hello1, World`);
console.log(i18n.foo()`Hello2, World`);
console.log(i18n.foo()`Hello3, World`);
+console.log(i18n`Hello, quoted "world"`);
+
/* This is a comment and should be included */
console.log(i18n().foo()`Hello4, World`);
diff --git a/packages/pogen/example/test2.tsx b/packages/pogen/example/proj1/src/test2.tsx
index 4133f86fb..1c1ab49f2 100644
--- a/packages/pogen/example/test2.tsx
+++ b/packages/pogen/example/proj1/src/test2.tsx
@@ -13,3 +13,5 @@ let y = (
</i18n.TranslatePlural>
</i18n.TranslateSwitch>
);
+
+let z = <i18n.Translate>"foo"</i18n.Translate>;
diff --git a/packages/pogen/example/proj1/tsconfig.json b/packages/pogen/example/proj1/tsconfig.json
index 30cb65e1d..36ef053db 100644
--- a/packages/pogen/example/proj1/tsconfig.json
+++ b/packages/pogen/example/proj1/tsconfig.json
@@ -4,7 +4,7 @@
"composite": true,
"declaration": true,
"declarationMap": false,
- "target": "ES6",
+ "target": "ES2020",
"module": "ESNext",
"moduleResolution": "node",
"sourceMap": true,
diff --git a/packages/pogen/package.json b/packages/pogen/package.json
index 3d744f736..24edc348b 100644
--- a/packages/pogen/package.json
+++ b/packages/pogen/package.json
@@ -1,19 +1,21 @@
{
"name": "@gnu-taler/pogen",
- "version": "0.0.5",
+ "version": "0.10.7",
"bin": {
"pogen": "bin/pogen"
},
"author": "Florian Dold",
"license": "GPL-2.0+",
"scripts": {
- "prepare": "tsc",
+ "clean": "rm -rf lib",
"compile": "tsc"
},
"devDependencies": {
- "typescript": "^4.1.3"
+ "po2json": "^0.4.5",
+ "typescript": "^5.3.3"
},
"dependencies": {
- "@types/node": "^14.14.22"
+ "@types/node": "^18.11.17",
+ "glob": "^10.3.10"
}
}
diff --git a/packages/pogen/po2.js b/packages/pogen/po2.js
deleted file mode 100644
index 532a1522f..000000000
--- a/packages/pogen/po2.js
+++ /dev/null
@@ -1,32 +0,0 @@
-const ts = require("typescript");
-
-const configPath = ts.findConfigFile(
- /*searchPath*/ "./",
- ts.sys.fileExists,
- "tsconfig.json"
- );
-if (!configPath) {
- throw new Error("Could not find a valid 'tsconfig.json'.");
-}
-
-console.log(configPath);
-
-const cmdline = ts.getParsedCommandLineOfConfigFile(configPath, {}, {
- fileExists: ts.sys.fileExists,
- getCurrentDirectory: ts.sys.getCurrentDirectory,
- onUnRecoverableConfigFileDiagnostic: (e) => console.log(e),
- readDirectory: ts.sys.readDirectory,
- readFile: ts.sys.readFile,
- useCaseSensitiveFileNames: true,
-})
-
-console.log(cmdline);
-
-const prog = ts.createProgram({
- options: cmdline.options,
- rootNames: cmdline.fileNames,
-});
-
-const allFiles = prog.getSourceFiles();
-
-console.log(allFiles.map(x => x.path)); \ No newline at end of file
diff --git a/packages/pogen/dumpTree.ts b/packages/pogen/src/dumpTree.ts
index af25caf32..af25caf32 100644
--- a/packages/pogen/dumpTree.ts
+++ b/packages/pogen/src/dumpTree.ts
diff --git a/packages/pogen/src/po2ts.ts b/packages/pogen/src/po2ts.ts
new file mode 100644
index 000000000..0b5b1384d
--- /dev/null
+++ b/packages/pogen/src/po2ts.ts
@@ -0,0 +1,146 @@
+/*
+ This file is part of GNU Taler
+ (C) 2020 Taler Systems S.A.
+
+ GNU Taler is free software; you can redistribute it and/or modify it under the
+ terms of the GNU General Public License as published by the Free Software
+ Foundation; either version 3, or (at your option) any later version.
+
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
+ WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
+ A PARTICULAR PURPOSE. See the GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License along with
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
+ */
+
+/**
+ * Convert a <lang>.po file into a JavaScript / TypeScript expression.
+ */
+
+// @ts-ignore
+import * as po2jsonLib from "po2json";
+import * as fs from "fs";
+import glob = require("glob");
+
+//types defined by the po2json library
+type Header = {
+ domain: string;
+ lang: string;
+ 'plural_forms': string;
+};
+
+type MessagesType = Record<string, undefined | Array<string>> & { "": Header }
+interface pojsonType {
+ // X-Domain or 'messages'
+ domain: string;
+ locale_data: {
+ messages: MessagesType
+ }
+}
+// ----------- end pf po2json
+
+interface StringsType {
+ // X-Domain or 'messages'
+ domain: string;
+ lang: string;
+ completeness: number,
+ 'plural_forms': string;
+ locale_data: {
+ messages: Record<string, undefined | Array<string>>
+ }
+}
+
+// This prelude match the types above
+const TYPES_FOR_STRING_PRELUDE = `
+export interface StringsType {
+ domain: string;
+ lang: string;
+ completeness: number;
+ 'plural_forms': string;
+ locale_data: {
+ messages: Record<string, unknown>;
+ };
+};
+`;
+
+const DEFAULT_STRING_PRELUDE = `${TYPES_FOR_STRING_PRELUDE}export const strings: Record<string,StringsType> = {};\n\n`
+
+
+export function po2ts(): void {
+ const files = glob.sync("src/i18n/*.po");
+
+ if (files.length === 0) {
+ console.error("no .po files found in src/i18n/");
+ process.exit(1);
+ }
+
+ console.log(files);
+
+ let prelude: string;
+ try {
+ prelude = fs.readFileSync("src/i18n/strings-prelude", "utf-8")
+ } catch (e) {
+ prelude = DEFAULT_STRING_PRELUDE
+ }
+
+ const chunks = [prelude];
+
+ for (const filename of files) {
+ const m = filename.match(/([a-zA-Z0-9-_]+).po/);
+
+ if (!m) {
+ console.error("error: unexpected filename (expected <lang>.po)");
+ process.exit(1);
+ }
+
+ const lang = m[1];
+ const poAsJson: pojsonType = po2jsonLib.parseFileSync(filename, {
+ format: "jed1.x",
+ fuzzy: true,
+ });
+ const header = poAsJson.locale_data.messages[""]
+ const total = calculateTotalTranslations(poAsJson.locale_data.messages)
+ const completeness =
+ header.lang === "en"
+ ? 100 // 'en' is always complete
+ : Math.floor(total.translations * 100 / total.keys);
+
+ const strings: StringsType = {
+ locale_data: poAsJson.locale_data,
+ domain: poAsJson.domain,
+ plural_forms: header.plural_forms,
+ lang: header.lang,
+ completeness,
+ }
+ const value = JSON.stringify(strings, undefined, 2)
+ const s = `strings['${lang}'] = ${value};\n\n`
+ chunks.push(s);
+ }
+
+ const tsContents = chunks.join("");
+
+ fs.writeFileSync("src/i18n/strings.ts", tsContents);
+}
+
+function calculateTotalTranslations(msgs: MessagesType): { keys: number, translations: number } {
+ const kv = Object.entries(msgs)
+ const [keys, translations] = kv.reduce(([total, withTranslation], translation) => {
+ if (!translation || translation.length !== 2 || !translation[1]) {
+ //current key is empty
+ return [total, withTranslation]
+ }
+ const v = translation[1]
+ if (!Array.isArray(v)) {
+ // this is not a translation
+ return [total, withTranslation]
+ }
+ if (!v.length || !v[0].length) {
+ //translation is missing
+ return [total + 1, withTranslation]
+ }
+ //current key has a translation
+ return [total + 1, withTranslation + 1]
+ }, [0, 0])
+ return { keys, translations }
+} \ No newline at end of file
diff --git a/packages/pogen/src/pogen.ts b/packages/pogen/src/pogen.ts
new file mode 100644
index 000000000..72b7c81d7
--- /dev/null
+++ b/packages/pogen/src/pogen.ts
@@ -0,0 +1,48 @@
+import { potextract } from "./potextract.js";
+import * as child_process from "child_process";
+import * as fs from "fs";
+import glob = require("glob");
+import { po2ts } from "./po2ts.js";
+
+function usage(): never {
+ console.log("usage: pogen <extract|merge|emit>");
+ process.exit(1);
+}
+
+export function main() {
+ const subcommand = process.argv[2];
+ if (process.argv.includes("--help") || !subcommand) {
+ usage();
+ }
+ switch (subcommand) {
+ case "extract":
+ potextract();
+ break;
+ case "merge": {
+ const packageJson = JSON.parse(
+ fs.readFileSync("./package.json", { encoding: "utf-8" }),
+ );
+
+ const poDomain = packageJson.pogen?.domain;
+ if (!poDomain) {
+ console.error("missing 'pogen.domain' field in package.json");
+ process.exit(1);
+ }
+ const files = glob.sync("src/i18n/*.po");
+ console.log(files);
+ for (const f of files) {
+ console.log(`merging ${f}`);
+ child_process.execSync(
+ `msgmerge -o '${f}' '${f}' 'src/i18n/${poDomain}.pot'`,
+ );
+ }
+ break;
+ }
+ case "emit":
+ po2ts();
+ break;
+ default:
+ console.error(`unknown subcommand '${subcommand}'`);
+ usage();
+ }
+}
diff --git a/packages/pogen/pogen.ts b/packages/pogen/src/potextract.ts
index 23ac389f4..243d44c6f 100644
--- a/packages/pogen/pogen.ts
+++ b/packages/pogen/src/potextract.ts
@@ -1,43 +1,59 @@
/*
- This file is part of TALER
- (C) 2016 GNUnet e.V.
+ This file is part of GNU Taler
+ (C) 2019-2022 Taler Systems S.A.
- TALER is free software; you can redistribute it and/or modify it under the
+ GNU Taler is free software; you can redistribute it and/or modify it under the
terms of the GNU General Public License as published by the Free Software
Foundation; either version 3, or (at your option) any later version.
- TALER is distributed in the hope that it will be useful, but WITHOUT ANY
+ GNU Taler is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
A PARTICULAR PURPOSE. See the GNU General Public License for more details.
You should have received a copy of the GNU General Public License along with
- TALER; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
- */
-
-/**
- * Generate .po file from list of source files.
- *
- * Note that duplicate message IDs are NOT merged, to get the same output as
- * you would from xgettext, just run msguniq.
- *
- * @author Florian Dold
+ GNU Taler; see the file COPYING. If not, see <http://www.gnu.org/licenses/>
*/
/**
* Imports.
*/
-import { readFileSync } from "fs";
import * as ts from "typescript";
+import * as fs from "fs";
+import * as path from "path"
+
+const DEFAULT_PO_HEADER = `# SOME DESCRIPTIVE TITLE.
+# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
+# This file is distributed under the same license as the PACKAGE package.
+# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
+#
+#, fuzzy
+msgid ""
+msgstr ""
+"Project-Id-Version: PACKAGE VERSION\\n"
+"Report-Msgid-Bugs-To: \\n"
+"POT-Creation-Date: 2016-11-23 00:00+0100\\n"
+"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
+"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
+"Language-Team: LANGUAGE <LL@li.org>\\n"
+"Language: \\n"
+"MIME-Version: 1.0\\n"
+"Content-Type: text/plain; charset=UTF-8\\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"));
}
-export function processFile(sourceFile: ts.SourceFile) {
- processNode(sourceFile);
+function processFile(
+ 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) {
@@ -138,7 +154,7 @@ export function processFile(sourceFile: ts.SourceFile) {
path,
line: lc.line,
comment: getComment(tte),
- template: getTemplate(tte.template).replace(/"/g, '\\"'),
+ template: getTemplate(tte.template),
};
return res;
}
@@ -146,26 +162,29 @@ export function processFile(sourceFile: ts.SourceFile) {
function formatMsgComment(line: number, comment?: string) {
if (comment) {
for (let cl of comment.split("\n")) {
- console.log(`#. ${cl}`);
+ outChunks.push(`#. ${cl}\n`);
}
}
- console.log(`#: ${sourceFile.fileName}:${line + 1}`);
- console.log(`#, c-format`);
+ 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
- let parts = msg
- .match(/(.*\n|.+$)/g)
- .map((x) => x.replace(/\n/g, "\\n"))
+ 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) {
- console.log(`${head} "${parts[0]}"`);
+ outChunks.push(`${head} "${parts[0]}"\n`);
} else {
- console.log(`${head} ""`);
+ outChunks.push(`${head} ""\n`);
for (let p of parts) {
- console.log(`"${p}"`);
+ outChunks.push(`"${p}"\n`);
}
}
}
@@ -197,7 +216,7 @@ export function processFile(sourceFile: ts.SourceFile) {
}
}
- function trim(s) {
+ function trim(s: string) {
return s.replace(/^[ \n\t]*/, "").replace(/[ \n\t]*$/, "");
}
@@ -208,7 +227,7 @@ export function processFile(sourceFile: ts.SourceFile) {
switch (childNode.kind) {
case ts.SyntaxKind.JsxText: {
let e = childNode as ts.JsxText;
- let s = e.getFullText();
+ let s = e.text;
let t = s.split("\n").map(trim).join(" ");
if (s[0] === " ") {
t = " " + t;
@@ -220,6 +239,7 @@ export function processFile(sourceFile: ts.SourceFile) {
}
case ts.SyntaxKind.JsxOpeningElement:
break;
+ case ts.SyntaxKind.JsxSelfClosingElement:
case ts.SyntaxKind.JsxElement:
fragments.push(`%${holeNum[0]++}$s`);
break;
@@ -231,16 +251,13 @@ export function processFile(sourceFile: ts.SourceFile) {
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
- }`,
+ `unrecognized syntax in JSX Element ${ts.SyntaxKind[childNode.kind]} (${childNode.getSourceFile().fileName}:${lc.line + 1}:${lc.character + 1}`,
);
break;
}
@@ -293,10 +310,13 @@ export function processFile(sourceFile: ts.SourceFile) {
let content = getJsxContent(node);
let { line } = ts.getLineAndCharacterOfPosition(sourceFile, node.pos);
let comment = getComment(node);
- formatMsgComment(line, comment);
- formatMsgLine("msgid", content);
- console.log(`msgstr ""`);
- console.log();
+ 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"])) {
@@ -313,11 +333,14 @@ export function processFile(sourceFile: ts.SourceFile) {
console.error("plural form missing");
process.exit(1);
}
- formatMsgLine("msgid", singularForm);
- formatMsgLine("msgid_plural", pluralForm);
- console.log(`msgstr[0] ""`);
- console.log(`msgstr[1] ""`);
- console.log();
+ 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;
}
break;
@@ -342,29 +365,35 @@ export function processFile(sourceFile: ts.SourceFile) {
<ts.TaggedTemplateExpression>ce.arguments[1],
);
let comment = getComment(ce);
-
- formatMsgComment(line, comment);
- formatMsgLine("msgid", t1.template);
- formatMsgLine("msgid_plural", t2.template);
- console.log(`msgstr[0] ""`);
- console.log(`msgstr[1] ""`);
- console.log();
+ const msgid = t1.template;
+ 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`);
+ 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,
- );
+ let { comment, template, line, path } =
+ processTaggedTemplateExpression(tte);
if (path[0] != "i18n") {
break;
}
- formatMsgComment(line, comment);
- formatMsgLine("msgid", template);
- console.log(`msgstr ""`);
- console.log();
+ const msgid = template;
+ if (!knownMessageIds.has(msgid)) {
+ knownMessageIds.add(msgid);
+ formatMsgComment(line, comment);
+ formatMsgLine("msgid", template);
+ outChunks.push(`msgstr ""\n`);
+ outChunks.push("\n");
+ }
break;
}
}
@@ -373,7 +402,7 @@ export function processFile(sourceFile: ts.SourceFile) {
}
}
-export function main() {
+export function potextract() {
const configPath = ts.findConfigFile(
/*searchPath*/ "./",
ts.sys.fileExists,
@@ -396,41 +425,47 @@ export function main() {
},
);
- const fileNames = cmdline.fileNames;
+ const prog = ts.createProgram({
+ options: cmdline.options,
+ rootNames: cmdline.fileNames,
+ });
- fileNames.sort();
+ const allFiles = prog.getSourceFiles();
- const outChunks: string[] = [];
+ const ownFiles = allFiles.filter(
+ (x) =>
+ !x.isDeclarationFile &&
+ !prog.isSourceFileFromExternalLibrary(x) &&
+ !prog.isSourceFileDefaultLibrary(x),
+ );
- outChunks.push(`# SOME DESCRIPTIVE TITLE.
-# Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
-# This file is distributed under the same license as the PACKAGE package.
-# FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
-#
-#, fuzzy
-msgid ""
-msgstr ""
-"Project-Id-Version: PACKAGE VERSION\\n"
-"Report-Msgid-Bugs-To: \\n"
-"POT-Creation-Date: 2016-11-23 00:00+0100\\n"
-"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\\n"
-"Last-Translator: FULL NAME <EMAIL@ADDRESS>\\n"
-"Language-Team: LANGUAGE <LL@li.org>\\n"
-"Language: \\n"
-"MIME-Version: 1.0\\n"
-"Content-Type: text/plain; charset=UTF-8\\n"
-"Content-Transfer-Encoding: 8bit\\n"`);
-
- fileNames.forEach((fileName) => {
- let sourceFile = ts.createSourceFile(
- fileName,
- readFileSync(fileName).toString(),
- ts.ScriptTarget.ES2016,
- /*setParentNodes */ true,
- );
- processFile(sourceFile);
- });
+ let header: string
+ try {
+ header = fs.readFileSync("src/i18n/poheader", "utf-8")
+ } catch (e) {
+ header = DEFAULT_PO_HEADER
+ }
+
+ const chunks = [header];
+
+ const knownMessageIds = new Set<string>();
- const out = outChunks.join("");
- console.log(out);
+ for (const f of ownFiles) {
+ processFile(f, chunks, knownMessageIds);
+ }
+
+ const pot = chunks.join("");
+
+ //console.log(pot);
+
+ const packageJson = JSON.parse(
+ fs.readFileSync("./package.json", { encoding: "utf-8" }),
+ );
+
+ const poDomain = packageJson.pogen?.domain;
+ if (!poDomain) {
+ console.error("missing 'pogen.domain' field in package.json");
+ process.exit(1);
+ }
+ fs.writeFileSync(`./src/i18n/${poDomain}.pot`, pot);
}
diff --git a/packages/pogen/tsconfig.json b/packages/pogen/tsconfig.json
index d61e5595a..482ce6fe8 100644
--- a/packages/pogen/tsconfig.json
+++ b/packages/pogen/tsconfig.json
@@ -1,13 +1,14 @@
{
- "compilerOptions": {
- "module": "commonjs",
- "target": "es5",
- "noImplicitAny": false,
- "sourceMap": false,
- "outDir": "lib",
- "incremental": true
- },
- "files": [
- "pogen.ts"
- ]
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "ES2020",
+ "noImplicitAny": false,
+ "outDir": "lib",
+ "incremental": true,
+ "moduleResolution": "node",
+ "sourceMap": true,
+ "lib": ["ES2020"],
+ "types": ["node"]
+ },
+ "include": ["src/**/*.ts"]
}