forked from cockpit-project/cockpit-podman
-
Notifications
You must be signed in to change notification settings - Fork 0
/
esbuild-cockpit-po-plugin.js
99 lines (84 loc) · 3.51 KB
/
esbuild-cockpit-po-plugin.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
import fs from "fs";
import glob from "glob";
import path from "path";
import Jed from "jed";
import gettext_parser from "gettext-parser";
const srcdir = process.env.SRCDIR || "./";
function get_po_files() {
try {
const linguas_file = path.resolve(srcdir, "po/LINGUAS");
const linguas = fs.readFileSync(linguas_file, 'utf8').match(/\S+/g);
return linguas.map(lang => path.resolve(srcdir, 'po', lang + '.po'));
} catch (error) {
if (error.code !== 'ENOENT') {
throw error;
}
/* No LINGUAS file? Fall back to globbing.
* Note: we won't detect .po files being added in this case.
*/
return glob.sync(path.resolve(srcdir, 'po/*.po'));
}
}
function get_plural_expr(statement) {
try {
/* Check that the plural forms isn't being sneaky since we build a function here */
Jed.PF.parse(statement);
} catch (ex) {
console.error("bad plural forms: " + ex.message);
process.exit(1);
}
const expr = statement.replace(/nplurals=[1-9]; plural=([^;]*);?$/, '(n) => $1');
if (expr === statement) {
console.error("bad plural forms: " + statement);
process.exit(1);
}
return expr;
}
function buildFile(po_file, outdir, subdir='', wrapper='cockpit.locale(PO_DATA)') {
return new Promise((resolve, reject) => {
const parsed = gettext_parser.po.parse(fs.readFileSync(po_file), 'utf8');
delete parsed.translations[""][""]; // second header copy
const rtl_langs = ["ar", "fa", "he", "ur"];
const dir = rtl_langs.includes(parsed.headers.language) ? "rtl" : "ltr";
// cockpit.js only looks at "plural-forms" and "language"
const chunks = [
'{\n',
' "": {\n',
` "plural-forms": ${get_plural_expr(parsed.headers['plural-forms'])},\n`,
` "language": "${parsed.headers.language}",\n`,
` "language-direction": "${dir}"\n`,
' }'
];
for (const [msgctxt, context] of Object.entries(parsed.translations)) {
const context_prefix = msgctxt ? msgctxt + '\u0004' : ''; /* for cockpit.ngettext */
for (const [msgid, translation] of Object.entries(context)) {
/* Only include msgids which appear in this source directory */
const references = translation.comments.reference.split(/\s/);
if (!references.some(str => str.startsWith(`pkg/${subdir}`) || str.startsWith('src')))
continue;
if (translation.comments.flag && translation.comments.flag.match(/\bfuzzy\b/))
continue;
const key = JSON.stringify(context_prefix + msgid);
// cockpit.js always ignores the first item
chunks.push(`,\n ${key}: [\n null`);
for (const str of translation.msgstr) {
chunks.push(',\n ' + JSON.stringify(str));
}
chunks.push('\n ]');
}
}
chunks.push('\n}');
const output = wrapper.replace('PO_DATA', chunks.join('')) + '\n';
const lang = path.basename(po_file).slice(0, -3);
fs.writeFileSync(path.resolve(outdir, subdir + 'po.' + lang + '.js'), output);
return resolve();
});
}
export const cockpitPoPlugin = {
name: 'cockpitPoPlugin',
setup(build) {
build.onEnd(async () => {
await Promise.all(get_po_files().map(f => buildFile(f, build.initialOptions.outdir)));
});
},
}