-
Notifications
You must be signed in to change notification settings - Fork 26
/
Copy pathgettext_extract.ts
93 lines (86 loc) · 3.78 KB
/
gettext_extract.ts
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
#!/usr/bin/env node
import chalk from "chalk";
import commandLineArgs, { OptionDefinition } from "command-line-args";
import fs from "node:fs";
import { glob } from "glob";
import path from "node:path";
import { loadConfig } from "./config";
import extractFromFiles from "./extract";
import { execShellCommand } from "./utils";
import { GettextConfig } from "../src/typeDefs";
const optionDefinitions: OptionDefinition[] = [{ name: "config", alias: "c", type: String }];
let options;
try {
options = commandLineArgs(optionDefinitions) as {
config?: string;
};
} catch (e) {
console.error(e);
process.exit(1);
}
var getFiles = async (config: GettextConfig) => {
const allFiles = await Promise.all(
config.input?.include.map((pattern) => {
const searchPath = path.join(config.input.path, pattern);
console.info(`Searching: ${chalk.blueBright(searchPath)}`);
return glob(searchPath);
}),
);
const excludeFiles = await Promise.all(
config.input.exclude.map((pattern) => {
const searchPath = path.join(config.input.path, pattern);
console.info(`Excluding: ${chalk.blueBright(searchPath)}`);
return glob(searchPath);
}),
);
const filesFlat = allFiles.reduce((prev, curr) => [...prev, ...curr], [] as string[]);
const excludeFlat = excludeFiles.reduce((prev, curr) => [...prev, ...curr], [] as string[]);
excludeFlat.forEach((file) => {
const index = filesFlat.indexOf(file);
if (index !== -1) {
filesFlat.splice(index, 1);
}
});
return filesFlat;
};
(async () => {
const config = await loadConfig(options);
console.info(`Input directory: ${chalk.blueBright(config.input.path)}`);
console.info(`Output directory: ${chalk.blueBright(config.output.path)}`);
console.info(`Output POT file: ${chalk.blueBright(config.output.potPath)}`);
console.info(`Locales: ${chalk.blueBright(config.output.locales)}`);
console.info();
const files = await getFiles(config);
console.info();
files.forEach((f) => console.info(chalk.grey(f)));
console.info();
await extractFromFiles(files, config.output.potPath, config);
for (const loc of config.output.locales) {
const poDir = config.output.flat ? config.output.path : path.join(config.output.path, loc);
const poFile = config.output.flat ? path.join(poDir, `${loc}.po`) : path.join(poDir, `app.po`);
fs.mkdirSync(poDir, { recursive: true });
const isFile = fs.existsSync(poFile) && fs.lstatSync(poFile).isFile();
if (isFile) {
await execShellCommand(`msgmerge --lang=${loc} --update ${poFile} ${config.output.potPath} --backup=off`);
console.info(`${chalk.green("Merged")}: ${chalk.blueBright(poFile)}`);
} else {
// https://www.gnu.org/software/gettext/manual/html_node/msginit-Invocation.html
// msginit will set Plural-Forms header if the locale is in the
// [embedded table](https://github.com/dd32/gettext/blob/master/gettext-tools/src/plural-table.c#L27)
// otherwise it will read [$GETTEXTCLDRDIR/common/supplemental/plurals.xml](https://raw.githubusercontent.com/unicode-org/cldr/main/common/supplemental/plurals.xml)
// so execShellCommand should pass the env(GETTEXTCLDRDIR) to child process
await execShellCommand(
`msginit --no-translator --locale=${loc} --input=${config.output.potPath} --output-file=${poFile}`,
);
fs.chmodSync(poFile, 0o666);
await execShellCommand(`msgattrib --no-wrap --no-obsolete -o ${poFile} ${poFile}`);
console.info(`${chalk.green("Created")}: ${chalk.blueBright(poFile)}`);
}
}
if (config.output.linguas === true) {
const linguasPath = path.join(config.output.path, "LINGUAS");
fs.writeFileSync(linguasPath, config.output.locales.join(" "));
console.info();
console.info(`${chalk.green("Created")}: ${chalk.blueBright(linguasPath)}`);
}
})();