@remvst/localization 1.0.1 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/check.yaml +1 -1
- package/.prettierignore +1 -0
- package/README.md +10 -7
- package/lib/commands/find-strings.d.ts +5 -0
- package/lib/commands/find-strings.js +26 -0
- package/lib/commands/localize.d.ts +9 -0
- package/lib/commands/localize.js +67 -0
- package/lib/commands/to-typescript.d.ts +2 -1
- package/lib/commands/to-typescript.js +14 -4
- package/lib/index.js +63 -94
- package/package.json +11 -6
- package/src/commands/find-strings.ts +38 -0
- package/src/commands/localize.ts +104 -0
- package/src/commands/to-typescript.ts +25 -7
- package/src/index.ts +89 -113
- package/test/my-script.js +3 -0
- package/tsconfig.json +12 -12
- package/lib/commands/combine-json.d.ts +0 -7
- package/lib/commands/combine-json.js +0 -30
- package/lib/commands/google-translate.d.ts +0 -6
- package/lib/commands/google-translate.js +0 -20
- package/lib/commands/parse-csv.d.ts +0 -7
- package/lib/commands/parse-csv.js +0 -31
- package/lib/model/translation-set.d.ts +0 -21
- package/lib/model/translation-set.js +0 -92
- package/src/commands/combine-json.ts +0 -39
- package/src/commands/google-translate.ts +0 -32
- package/src/commands/parse-csv.ts +0 -41
- package/src/model/translation-set.ts +0 -119
- package/test/localization.json +0 -15
- package/test/polyglot.csv +0 -645
package/src/index.ts
CHANGED
|
@@ -1,125 +1,101 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
import
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import { toTypescriptCommand } from './commands/to-typescript';
|
|
3
|
+
import yargs from "yargs";
|
|
4
|
+
import { hideBin } from "yargs/helpers";
|
|
5
|
+
import { findStringsCommand } from "./commands/find-strings";
|
|
6
|
+
import { localize } from "./commands/localize";
|
|
7
|
+
import { toTypescriptCommand } from "./commands/to-typescript";
|
|
9
8
|
|
|
10
9
|
async function main() {
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
10
|
+
await yargs(hideBin(process.argv))
|
|
11
|
+
.command(
|
|
12
|
+
"find-strings",
|
|
13
|
+
"Finds all localizable strings in a source file",
|
|
14
|
+
async (yargs) => {
|
|
15
|
+
const argv = await yargs
|
|
16
|
+
.option("in", {
|
|
17
|
+
type: "string",
|
|
18
|
+
alias: "i",
|
|
19
|
+
describe: "Folder to read from",
|
|
20
|
+
})
|
|
21
|
+
.options("out", {
|
|
22
|
+
type: "string",
|
|
23
|
+
alias: "o",
|
|
24
|
+
describe: "Output JSON file",
|
|
25
|
+
})
|
|
26
|
+
.options("localize-function-name", {
|
|
27
|
+
type: "string",
|
|
28
|
+
describe: "Name of the function used for localization",
|
|
29
|
+
})
|
|
30
|
+
.demandOption("in")
|
|
31
|
+
.demandOption("out")
|
|
32
|
+
.demandOption("localize-function-name").argv;
|
|
33
33
|
|
|
34
|
-
|
|
34
|
+
await findStringsCommand(argv);
|
|
35
|
+
},
|
|
36
|
+
)
|
|
37
|
+
.command("localize", "Creates a localization JSON file", async (yargs) => {
|
|
38
|
+
const argv = await yargs
|
|
39
|
+
.option("source-json", {
|
|
40
|
+
type: "string",
|
|
41
|
+
describe: "File that contains all strings in the original language",
|
|
35
42
|
})
|
|
36
|
-
.
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
type: 'string',
|
|
40
|
-
alias: 'i',
|
|
41
|
-
describe: 'JSON file to read from',
|
|
42
|
-
})
|
|
43
|
-
.option('out', {
|
|
44
|
-
type: 'string',
|
|
45
|
-
alias: 'o',
|
|
46
|
-
describe: 'Output JSON file',
|
|
47
|
-
})
|
|
48
|
-
.option('fallbackLocale', {
|
|
49
|
-
type: 'string',
|
|
50
|
-
describe: 'Default locale to backfill translations',
|
|
51
|
-
default: 'en',
|
|
52
|
-
})
|
|
53
|
-
.option('fallback', {
|
|
54
|
-
type: 'string',
|
|
55
|
-
alias: 'f',
|
|
56
|
-
describe: 'Fallback JSON file to read from',
|
|
57
|
-
})
|
|
58
|
-
.option('locale', {
|
|
59
|
-
type: 'string',
|
|
60
|
-
alias: 'l',
|
|
61
|
-
describe: 'Locale to include',
|
|
62
|
-
})
|
|
63
|
-
.array('fallback')
|
|
64
|
-
.array('locale')
|
|
65
|
-
.demandOption('main')
|
|
66
|
-
.demandOption('out')
|
|
67
|
-
.demandOption('locale')
|
|
68
|
-
.demandOption('fallback')
|
|
69
|
-
.argv
|
|
70
|
-
|
|
71
|
-
await combineJsonCommand(argv);
|
|
43
|
+
.option("source-locale", {
|
|
44
|
+
type: "string",
|
|
45
|
+
describe: "Locale to translate from",
|
|
72
46
|
})
|
|
73
|
-
.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
alias: 'i',
|
|
78
|
-
describe: 'JSON file to read from',
|
|
79
|
-
})
|
|
80
|
-
.options('out', {
|
|
81
|
-
type: 'string',
|
|
82
|
-
alias: 'o',
|
|
83
|
-
describe: 'Output Typescript file',
|
|
84
|
-
})
|
|
85
|
-
.demandOption('in')
|
|
86
|
-
.demandOption('out')
|
|
87
|
-
.argv
|
|
88
|
-
|
|
89
|
-
await toTypescriptCommand(argv);
|
|
47
|
+
.options("destination-json", {
|
|
48
|
+
type: "string",
|
|
49
|
+
alias: "o",
|
|
50
|
+
describe: "Output JSON file",
|
|
90
51
|
})
|
|
91
|
-
.
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
.option('out', {
|
|
99
|
-
type: 'string',
|
|
100
|
-
alias: 'o',
|
|
101
|
-
describe: 'Output JSON file',
|
|
102
|
-
})
|
|
103
|
-
.option('fallbackLocale', {
|
|
104
|
-
type: 'string',
|
|
105
|
-
describe: 'Default locale to backfill translations',
|
|
106
|
-
default: 'en',
|
|
107
|
-
})
|
|
108
|
-
.option('locale', {
|
|
109
|
-
type: 'string',
|
|
110
|
-
alias: 'l',
|
|
111
|
-
describe: 'Locale to include',
|
|
112
|
-
})
|
|
113
|
-
.array('fallback')
|
|
114
|
-
.array('locale')
|
|
115
|
-
.demandOption('in')
|
|
116
|
-
.demandOption('out')
|
|
117
|
-
.demandOption('locale')
|
|
118
|
-
.argv
|
|
119
|
-
|
|
120
|
-
await googleTranslateCommand(argv);
|
|
52
|
+
.option("destination-locale", {
|
|
53
|
+
type: "string",
|
|
54
|
+
describe: "Locale to translate to",
|
|
55
|
+
})
|
|
56
|
+
.option("context", {
|
|
57
|
+
type: "string",
|
|
58
|
+
describe: "Context to provide to the translation engine",
|
|
121
59
|
})
|
|
122
|
-
.
|
|
60
|
+
.option("engine", {
|
|
61
|
+
type: "string",
|
|
62
|
+
choices: ["gpt", "google"],
|
|
63
|
+
default: "google",
|
|
64
|
+
describe: "Translation engine to use",
|
|
65
|
+
})
|
|
66
|
+
.demandOption("source-json")
|
|
67
|
+
.demandOption("source-locale")
|
|
68
|
+
.demandOption("destination-json")
|
|
69
|
+
.demandOption("destination-locale").argv;
|
|
70
|
+
|
|
71
|
+
await localize(argv);
|
|
72
|
+
})
|
|
73
|
+
.command(
|
|
74
|
+
"to-typescript",
|
|
75
|
+
"Creates a TypeScript file that can be then imported to localize a project",
|
|
76
|
+
async (yargs) => {
|
|
77
|
+
const argv = await yargs
|
|
78
|
+
.option("default-localization", {
|
|
79
|
+
type: "string",
|
|
80
|
+
alias: "i",
|
|
81
|
+
describe: "Default localization JSON file",
|
|
82
|
+
})
|
|
83
|
+
.options("out", {
|
|
84
|
+
type: "string",
|
|
85
|
+
alias: "o",
|
|
86
|
+
describe: "Output Typescript file",
|
|
87
|
+
})
|
|
88
|
+
.options("localize-function-name", {
|
|
89
|
+
type: "string",
|
|
90
|
+
describe: "Name of the function used for localization",
|
|
91
|
+
})
|
|
92
|
+
.demandOption("default-localization")
|
|
93
|
+
.demandOption("out")
|
|
94
|
+
.demandOption("localize-function-name").argv;
|
|
95
|
+
|
|
96
|
+
await toTypescriptCommand(argv);
|
|
97
|
+
},
|
|
98
|
+
).argv;
|
|
123
99
|
}
|
|
124
100
|
|
|
125
101
|
main();
|
package/tsconfig.json
CHANGED
|
@@ -1,14 +1,14 @@
|
|
|
1
1
|
{
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
2
|
+
"compilerOptions": {
|
|
3
|
+
"target": "es2020",
|
|
4
|
+
"module": "commonjs",
|
|
5
|
+
"declaration": true,
|
|
6
|
+
"strict": true,
|
|
7
|
+
"esModuleInterop": true,
|
|
8
|
+
"allowJs": false,
|
|
9
|
+
"outDir": "./lib",
|
|
10
|
+
"skipLibCheck": true
|
|
11
|
+
},
|
|
12
|
+
"include": ["./src/**/*"],
|
|
13
|
+
"exclude": ["node_modules"]
|
|
14
14
|
}
|
|
@@ -1,30 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.combineJsonCommand = void 0;
|
|
4
|
-
const fs_1 = require("fs");
|
|
5
|
-
const translation_set_1 = require("../model/translation-set");
|
|
6
|
-
async function combineJsonCommand(options) {
|
|
7
|
-
const fallbackTranslations = [];
|
|
8
|
-
for (const fallbackPath of options.fallback || []) {
|
|
9
|
-
const sourceJson = JSON.parse(await fs_1.promises.readFile(fallbackPath, 'utf-8'));
|
|
10
|
-
const translationSet = translation_set_1.TranslationSet.fromJSON(sourceJson);
|
|
11
|
-
fallbackTranslations.push(translationSet);
|
|
12
|
-
}
|
|
13
|
-
const mainJson = JSON.parse(await fs_1.promises.readFile(options.main, 'utf-8'));
|
|
14
|
-
const outTranslations = translation_set_1.TranslationSet.fromJSON(mainJson);
|
|
15
|
-
await outTranslations.applyFallbacks(options.locale || ['en'], async (key, locale, item) => {
|
|
16
|
-
const translationInFallbackLocale = item.get(options.fallbackLocale);
|
|
17
|
-
if (!translationInFallbackLocale)
|
|
18
|
-
return null;
|
|
19
|
-
for (const fallbackSet of fallbackTranslations) {
|
|
20
|
-
if (!translationInFallbackLocale)
|
|
21
|
-
continue;
|
|
22
|
-
const fromFallbackLocale = fallbackSet.fromLocalization(options.fallbackLocale, translationInFallbackLocale)?.get(locale);
|
|
23
|
-
if (fromFallbackLocale)
|
|
24
|
-
return fromFallbackLocale;
|
|
25
|
-
}
|
|
26
|
-
return null;
|
|
27
|
-
});
|
|
28
|
-
await fs_1.promises.writeFile(options.out, JSON.stringify(outTranslations.toJSON(), null, 4));
|
|
29
|
-
}
|
|
30
|
-
exports.combineJsonCommand = combineJsonCommand;
|
|
@@ -1,20 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.googleTranslateCommand = void 0;
|
|
4
|
-
const fs_1 = require("fs");
|
|
5
|
-
const translation_set_1 = require("../model/translation-set");
|
|
6
|
-
const google_translate_api_1 = require("@vitalets/google-translate-api");
|
|
7
|
-
async function googleTranslateCommand(options) {
|
|
8
|
-
const mainJson = JSON.parse(await fs_1.promises.readFile(options.in, 'utf-8'));
|
|
9
|
-
const outTranslations = translation_set_1.TranslationSet.fromJSON(mainJson);
|
|
10
|
-
await outTranslations.applyFallbacks(options.locale || ['en'], async (_, locale, item) => {
|
|
11
|
-
const translationInFallbackLocale = item.get(options.fallbackLocale);
|
|
12
|
-
if (!translationInFallbackLocale)
|
|
13
|
-
return null;
|
|
14
|
-
console.log(`Translating ${JSON.stringify(translationInFallbackLocale)} to ${locale}`);
|
|
15
|
-
const translationResult = await (0, google_translate_api_1.translate)(translationInFallbackLocale, { from: options.fallbackLocale, to: locale });
|
|
16
|
-
return translationResult?.text;
|
|
17
|
-
});
|
|
18
|
-
await fs_1.promises.writeFile(options.out, JSON.stringify(outTranslations.toJSON(), null, 4));
|
|
19
|
-
}
|
|
20
|
-
exports.googleTranslateCommand = googleTranslateCommand;
|
|
@@ -1,7 +0,0 @@
|
|
|
1
|
-
import { TranslationSet } from "../model/translation-set";
|
|
2
|
-
export declare function parseCsv(path: string, languagesLine: number): Promise<TranslationSet>;
|
|
3
|
-
export declare function parseCsvCommand(options: {
|
|
4
|
-
in: string;
|
|
5
|
-
out: string;
|
|
6
|
-
languagesLineIndex: number;
|
|
7
|
-
}): Promise<void>;
|
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.parseCsvCommand = exports.parseCsv = void 0;
|
|
4
|
-
const fs_1 = require("fs");
|
|
5
|
-
const translation_set_1 = require("../model/translation-set");
|
|
6
|
-
async function parseCsv(path, languagesLine) {
|
|
7
|
-
const csvContent = await fs_1.promises.readFile(path, 'utf-8');
|
|
8
|
-
const csvLines = csvContent.split('\n');
|
|
9
|
-
const res = new translation_set_1.TranslationSet();
|
|
10
|
-
const ietfLine = csvLines[languagesLine];
|
|
11
|
-
const languagesIndices = new Map();
|
|
12
|
-
const ietfColumns = ietfLine.split(',');
|
|
13
|
-
for (let i = 2; i < ietfColumns.length; i++) {
|
|
14
|
-
const locale = ietfColumns[i].slice(0, 2); // only grab the first two chars to identify the language
|
|
15
|
-
languagesIndices.set(locale, i);
|
|
16
|
-
}
|
|
17
|
-
for (const line of csvLines) {
|
|
18
|
-
const columns = line.split(',');
|
|
19
|
-
const key = columns[0];
|
|
20
|
-
for (const [locale, columnIndex] of languagesIndices.entries()) {
|
|
21
|
-
res.add(key, locale, columns[columnIndex]);
|
|
22
|
-
}
|
|
23
|
-
}
|
|
24
|
-
return res;
|
|
25
|
-
}
|
|
26
|
-
exports.parseCsv = parseCsv;
|
|
27
|
-
async function parseCsvCommand(options) {
|
|
28
|
-
const polyglotTranslations = await parseCsv(options.in, options.languagesLineIndex);
|
|
29
|
-
await fs_1.promises.writeFile(options.out, JSON.stringify(polyglotTranslations, null, 4));
|
|
30
|
-
}
|
|
31
|
-
exports.parseCsvCommand = parseCsvCommand;
|
|
@@ -1,21 +0,0 @@
|
|
|
1
|
-
export type Key = string;
|
|
2
|
-
export type Locale = string;
|
|
3
|
-
export type LocalizedItem = Map<Locale, string>;
|
|
4
|
-
export declare class TranslationSet {
|
|
5
|
-
private readonly locales;
|
|
6
|
-
private readonly translations;
|
|
7
|
-
add(key: Key, locale: Locale, localization: string): void;
|
|
8
|
-
fromLocalization(language: Locale, localization: string): LocalizedItem | null;
|
|
9
|
-
toJSON(): {
|
|
10
|
-
[key: string]: {
|
|
11
|
-
[key: string]: string;
|
|
12
|
-
};
|
|
13
|
-
};
|
|
14
|
-
static fromJSON(json: {
|
|
15
|
-
[key: string]: {
|
|
16
|
-
[key: string]: string;
|
|
17
|
-
};
|
|
18
|
-
}): TranslationSet;
|
|
19
|
-
toTypeScript(): string;
|
|
20
|
-
applyFallbacks(locales: Locale[], fallback: (key: Key, locale: Locale, localizedItem: LocalizedItem) => Promise<string | null>): Promise<void>;
|
|
21
|
-
}
|
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.TranslationSet = void 0;
|
|
4
|
-
class TranslationSet {
|
|
5
|
-
constructor() {
|
|
6
|
-
this.locales = new Set();
|
|
7
|
-
this.translations = new Map();
|
|
8
|
-
}
|
|
9
|
-
add(key, locale, localization) {
|
|
10
|
-
this.locales.add(locale);
|
|
11
|
-
if (!this.translations.has(key)) {
|
|
12
|
-
this.translations.set(key, new Map());
|
|
13
|
-
}
|
|
14
|
-
this.translations.get(key).set(locale, localization);
|
|
15
|
-
}
|
|
16
|
-
fromLocalization(language, localization) {
|
|
17
|
-
const search = localization.toLowerCase();
|
|
18
|
-
for (const localizedItem of this.translations.values()) {
|
|
19
|
-
if (localizedItem.get(language)?.toLowerCase() === search) {
|
|
20
|
-
return localizedItem;
|
|
21
|
-
}
|
|
22
|
-
}
|
|
23
|
-
return null;
|
|
24
|
-
}
|
|
25
|
-
toJSON() {
|
|
26
|
-
const res = {};
|
|
27
|
-
for (const [key, localizedItem] of this.translations.entries()) {
|
|
28
|
-
res[key] = {};
|
|
29
|
-
for (const [locale, translation] of localizedItem.entries()) {
|
|
30
|
-
res[key][locale] = translation;
|
|
31
|
-
}
|
|
32
|
-
}
|
|
33
|
-
return res;
|
|
34
|
-
}
|
|
35
|
-
static fromJSON(json) {
|
|
36
|
-
const res = new TranslationSet();
|
|
37
|
-
for (const [key, localizedItem] of Object.entries(json)) {
|
|
38
|
-
for (const [locale, translation] of Object.entries(localizedItem)) {
|
|
39
|
-
res.add(key, locale, translation);
|
|
40
|
-
}
|
|
41
|
-
}
|
|
42
|
-
return res;
|
|
43
|
-
}
|
|
44
|
-
toTypeScript() {
|
|
45
|
-
let generatedFileContent = '';
|
|
46
|
-
generatedFileContent += `export type Locale = ${Array.from(this.locales).map(key => JSON.stringify(key)).join(' | ')};\n\n`;
|
|
47
|
-
generatedFileContent += `export type LocalizedKey = ${Array.from(this.translations.keys()).map(key => JSON.stringify(key)).join(' | ')};\n\n`;
|
|
48
|
-
generatedFileContent += `export type LocalizationItem = {[key in Locale]?: string};\n\n`;
|
|
49
|
-
generatedFileContent += 'export const LOCALIZATION: {[key in LocalizedKey]: LocalizationItem} = {\n';
|
|
50
|
-
for (const [key, localizedItem] of this.translations.entries()) {
|
|
51
|
-
generatedFileContent += ` ${JSON.stringify(key)}: {\n`;
|
|
52
|
-
for (const [locale, translation] of localizedItem.entries()) {
|
|
53
|
-
if (!translation)
|
|
54
|
-
continue;
|
|
55
|
-
generatedFileContent += ` ${JSON.stringify(locale)}: ${JSON.stringify(translation)},\n`;
|
|
56
|
-
}
|
|
57
|
-
generatedFileContent += ` },\n`;
|
|
58
|
-
}
|
|
59
|
-
generatedFileContent += '};\n\n';
|
|
60
|
-
generatedFileContent += `let LOCALE: Locale = 'en';\n\n`;
|
|
61
|
-
generatedFileContent += `export function setLocale(locale: string) {\n`;
|
|
62
|
-
generatedFileContent += ` LOCALE = locale.split('-')[0] as Locale\n`;
|
|
63
|
-
generatedFileContent += `}\n\n`;
|
|
64
|
-
generatedFileContent += `export function localize(key: LocalizedKey) {\n`;
|
|
65
|
-
generatedFileContent += ` const localizationItem: LocalizationItem = LOCALIZATION[key];\n`;
|
|
66
|
-
generatedFileContent += ` return localizationItem[LOCALE] || localizationItem.en;\n`;
|
|
67
|
-
generatedFileContent += `}\n`;
|
|
68
|
-
return generatedFileContent;
|
|
69
|
-
}
|
|
70
|
-
async applyFallbacks(locales, fallback) {
|
|
71
|
-
for (const [key, localizedItem] of this.translations.entries()) {
|
|
72
|
-
for (const locale of locales) {
|
|
73
|
-
let translation = localizedItem.get(locale) || null;
|
|
74
|
-
if (translation)
|
|
75
|
-
continue;
|
|
76
|
-
try {
|
|
77
|
-
translation = await fallback(key, locale, localizedItem);
|
|
78
|
-
}
|
|
79
|
-
catch (err) {
|
|
80
|
-
console.error(err);
|
|
81
|
-
continue;
|
|
82
|
-
}
|
|
83
|
-
if (!translation) {
|
|
84
|
-
console.warn(`Unable to find fallback for ${key}`);
|
|
85
|
-
continue;
|
|
86
|
-
}
|
|
87
|
-
this.add(key, locale, translation);
|
|
88
|
-
}
|
|
89
|
-
}
|
|
90
|
-
}
|
|
91
|
-
}
|
|
92
|
-
exports.TranslationSet = TranslationSet;
|
|
@@ -1,39 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import { TranslationSet, Key, Locale, LocalizedItem } from '../model/translation-set';
|
|
3
|
-
|
|
4
|
-
export async function combineJsonCommand(options: {
|
|
5
|
-
main: string,
|
|
6
|
-
out: string,
|
|
7
|
-
locale: string[],
|
|
8
|
-
fallbackLocale: string,
|
|
9
|
-
fallback: string[],
|
|
10
|
-
}) {
|
|
11
|
-
const fallbackTranslations: TranslationSet[] = [];
|
|
12
|
-
|
|
13
|
-
for (const fallbackPath of options.fallback || []) {
|
|
14
|
-
const sourceJson = JSON.parse(await fs.readFile(fallbackPath, 'utf-8'));
|
|
15
|
-
const translationSet = TranslationSet.fromJSON(sourceJson);
|
|
16
|
-
fallbackTranslations.push(translationSet);
|
|
17
|
-
}
|
|
18
|
-
|
|
19
|
-
const mainJson = JSON.parse(await fs.readFile(options.main, 'utf-8'));
|
|
20
|
-
const outTranslations = TranslationSet.fromJSON(mainJson);
|
|
21
|
-
|
|
22
|
-
await outTranslations.applyFallbacks(
|
|
23
|
-
options.locale || ['en'],
|
|
24
|
-
async (key: Key, locale: Locale, item: LocalizedItem) => {
|
|
25
|
-
const translationInFallbackLocale = item.get(options.fallbackLocale);
|
|
26
|
-
if (!translationInFallbackLocale) return null;
|
|
27
|
-
|
|
28
|
-
for (const fallbackSet of fallbackTranslations) {
|
|
29
|
-
if (!translationInFallbackLocale) continue;
|
|
30
|
-
const fromFallbackLocale = fallbackSet.fromLocalization(options.fallbackLocale, translationInFallbackLocale)?.get(locale);
|
|
31
|
-
if (fromFallbackLocale) return fromFallbackLocale;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
return null;
|
|
35
|
-
}
|
|
36
|
-
);
|
|
37
|
-
|
|
38
|
-
await fs.writeFile(options.out, JSON.stringify(outTranslations.toJSON(), null, 4));
|
|
39
|
-
}
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import { Key, Locale, LocalizedItem, TranslationSet } from "../model/translation-set";
|
|
3
|
-
import { translate } from '@vitalets/google-translate-api';
|
|
4
|
-
|
|
5
|
-
export async function googleTranslateCommand(options: {
|
|
6
|
-
in: string,
|
|
7
|
-
out: string,
|
|
8
|
-
locale: string[],
|
|
9
|
-
fallbackLocale: string,
|
|
10
|
-
}) {
|
|
11
|
-
const mainJson = JSON.parse(await fs.readFile(options.in, 'utf-8'));
|
|
12
|
-
const outTranslations = TranslationSet.fromJSON(mainJson);
|
|
13
|
-
|
|
14
|
-
await outTranslations.applyFallbacks(
|
|
15
|
-
options.locale || ['en'],
|
|
16
|
-
async (_: Key, locale: Locale, item: LocalizedItem) => {
|
|
17
|
-
const translationInFallbackLocale = item.get(options.fallbackLocale);
|
|
18
|
-
if (!translationInFallbackLocale) return null;
|
|
19
|
-
|
|
20
|
-
console.log(`Translating ${JSON.stringify(translationInFallbackLocale)} to ${locale}`);
|
|
21
|
-
|
|
22
|
-
const translationResult = await translate(
|
|
23
|
-
translationInFallbackLocale,
|
|
24
|
-
{ from: options.fallbackLocale, to: locale },
|
|
25
|
-
);
|
|
26
|
-
|
|
27
|
-
return translationResult?.text;
|
|
28
|
-
}
|
|
29
|
-
);
|
|
30
|
-
|
|
31
|
-
await fs.writeFile(options.out, JSON.stringify(outTranslations.toJSON(), null, 4));
|
|
32
|
-
}
|
|
@@ -1,41 +0,0 @@
|
|
|
1
|
-
import { promises as fs } from 'fs';
|
|
2
|
-
import { TranslationSet } from "../model/translation-set";
|
|
3
|
-
|
|
4
|
-
export async function parseCsv(
|
|
5
|
-
path: string,
|
|
6
|
-
languagesLine: number,
|
|
7
|
-
): Promise<TranslationSet> {
|
|
8
|
-
const csvContent = await fs.readFile(path, 'utf-8');
|
|
9
|
-
const csvLines = csvContent.split('\n');
|
|
10
|
-
|
|
11
|
-
const res = new TranslationSet();
|
|
12
|
-
|
|
13
|
-
const ietfLine = csvLines[languagesLine];
|
|
14
|
-
const languagesIndices = new Map<string, number>();
|
|
15
|
-
const ietfColumns = ietfLine.split(',');
|
|
16
|
-
for (let i = 2 ; i < ietfColumns.length ; i++) {
|
|
17
|
-
const locale = ietfColumns[i].slice(0, 2); // only grab the first two chars to identify the language
|
|
18
|
-
languagesIndices.set(locale, i);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
for (const line of csvLines) {
|
|
22
|
-
const columns = line.split(',');
|
|
23
|
-
|
|
24
|
-
const key = columns[0];
|
|
25
|
-
|
|
26
|
-
for (const [locale, columnIndex] of languagesIndices.entries()) {
|
|
27
|
-
res.add(key, locale, columns[columnIndex]);
|
|
28
|
-
}
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
return res;
|
|
32
|
-
}
|
|
33
|
-
|
|
34
|
-
export async function parseCsvCommand(options: {
|
|
35
|
-
in: string,
|
|
36
|
-
out: string,
|
|
37
|
-
languagesLineIndex: number,
|
|
38
|
-
}) {
|
|
39
|
-
const polyglotTranslations = await parseCsv(options.in!, options.languagesLineIndex);
|
|
40
|
-
await fs.writeFile(options.out!, JSON.stringify(polyglotTranslations, null, 4));
|
|
41
|
-
}
|