@remvst/localization 1.0.2 → 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/.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 -9
- 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 -9
- 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 -35
- package/lib/commands/google-translate.d.ts +0 -6
- package/lib/commands/google-translate.js +0 -25
- package/lib/commands/parse-csv.d.ts +0 -7
- package/lib/commands/parse-csv.js +0 -36
- package/lib/model/translation-set.d.ts +0 -21
- package/lib/model/translation-set.js +0 -93
- package/src/commands/combine-json.ts +0 -41
- package/src/commands/google-translate.ts +0 -34
- package/src/commands/parse-csv.ts +0 -43
- package/src/model/translation-set.ts +0 -120
- package/test/localization.json +0 -15
- package/test/polyglot.csv +0 -645
package/.prettierignore
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
testOut/
|
package/README.md
CHANGED
|
@@ -14,13 +14,16 @@ Create a JSON file with your localized strings (`my-localization.json`):
|
|
|
14
14
|
|
|
15
15
|
```json
|
|
16
16
|
{
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
17
|
+
"ok": { "en": "Okay" },
|
|
18
|
+
"confirm": { "en": "Confirm" },
|
|
19
|
+
"cancel": { "en": "Cancel" },
|
|
20
|
+
"back": { "en": "Back" },
|
|
21
|
+
"play": { "en": "Play" },
|
|
22
|
+
"time": { "en": "Time" },
|
|
23
|
+
"backToMainMenu": {
|
|
24
|
+
"en": "Back To Main Menu",
|
|
25
|
+
"fr": "Retour au menu principal"
|
|
26
|
+
}
|
|
24
27
|
}
|
|
25
28
|
```
|
|
26
29
|
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.findStringsCommand = void 0;
|
|
4
|
+
const fs_1 = require("fs");
|
|
5
|
+
const glob_1 = require("glob");
|
|
6
|
+
async function findStringsCommand(options) {
|
|
7
|
+
const jsfiles = await (0, glob_1.glob)(options.in);
|
|
8
|
+
const strings = new Set();
|
|
9
|
+
for (const file of jsfiles) {
|
|
10
|
+
const content = await fs_1.promises.readFile(file, "utf-8");
|
|
11
|
+
const matches = [
|
|
12
|
+
...content.matchAll(new RegExp(`${options.localizeFunctionName}\\('([^']+)'\\)`, "g")),
|
|
13
|
+
...content.matchAll(new RegExp(`${options.localizeFunctionName}\\("([^"]+)"\\)`, "g")),
|
|
14
|
+
...content.matchAll(new RegExp(`${options.localizeFunctionName}\\(\`([^"]+)\`\\)`, "g")),
|
|
15
|
+
];
|
|
16
|
+
for (const [_, string] of matches) {
|
|
17
|
+
strings.add(string);
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
const outJson = {};
|
|
21
|
+
for (const string of Array.from(strings).sort()) {
|
|
22
|
+
outJson[string] = string;
|
|
23
|
+
}
|
|
24
|
+
await fs_1.promises.writeFile(options.out, JSON.stringify(outJson, null, 4));
|
|
25
|
+
}
|
|
26
|
+
exports.findStringsCommand = findStringsCommand;
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
export declare function googleTranslate(text: string, from: string, to: string): Promise<string>;
|
|
2
|
+
export declare function localize(options: {
|
|
3
|
+
sourceJson: string;
|
|
4
|
+
sourceLocale: string;
|
|
5
|
+
destinationJson: string;
|
|
6
|
+
destinationLocale: string;
|
|
7
|
+
context?: string;
|
|
8
|
+
engine?: string;
|
|
9
|
+
}): Promise<void>;
|
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
5
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.localize = exports.googleTranslate = void 0;
|
|
7
|
+
const dotenv_1 = require("dotenv");
|
|
8
|
+
const openai_1 = __importDefault(require("openai"));
|
|
9
|
+
const fs_1 = require("fs");
|
|
10
|
+
const google_translate_api_1 = require("@vitalets/google-translate-api");
|
|
11
|
+
(0, dotenv_1.config)();
|
|
12
|
+
async function googleTranslate(text, from, to) {
|
|
13
|
+
const result = await (0, google_translate_api_1.translate)(text, { from, to });
|
|
14
|
+
return result.text;
|
|
15
|
+
}
|
|
16
|
+
exports.googleTranslate = googleTranslate;
|
|
17
|
+
async function gptTranslate(text, from, to, context) {
|
|
18
|
+
const openai = new openai_1.default({ apiKey: process.env.OPENAI_API_KEY });
|
|
19
|
+
const prompt = `Translate the following text from ${from} to ${to}.` +
|
|
20
|
+
(context ? ` Context: ${context}` : "") +
|
|
21
|
+
`\nText: ${text}`;
|
|
22
|
+
const completion = await openai.chat.completions.create({
|
|
23
|
+
model: "gpt-3.5-turbo",
|
|
24
|
+
messages: [
|
|
25
|
+
{
|
|
26
|
+
role: "system",
|
|
27
|
+
content: `
|
|
28
|
+
You are a helpful translation assistant.
|
|
29
|
+
Only return the string that would be used for translation, so it can be put in the game as is (verbatim).
|
|
30
|
+
`.trim(),
|
|
31
|
+
},
|
|
32
|
+
{ role: "user", content: prompt },
|
|
33
|
+
],
|
|
34
|
+
max_tokens: 1000,
|
|
35
|
+
temperature: 0.3,
|
|
36
|
+
});
|
|
37
|
+
return completion.choices?.[0]?.message?.content?.trim() || "";
|
|
38
|
+
}
|
|
39
|
+
async function localize(options) {
|
|
40
|
+
let existingJson = {};
|
|
41
|
+
try {
|
|
42
|
+
existingJson = JSON.parse(await fs_1.promises.readFile(options.destinationJson, "utf-8"));
|
|
43
|
+
}
|
|
44
|
+
catch (error) { }
|
|
45
|
+
const allStrings = JSON.parse(await fs_1.promises.readFile(options.sourceJson, "utf-8"));
|
|
46
|
+
const outJson = {};
|
|
47
|
+
console.log("Translating strings from", options.sourceLocale, "to", options.destinationLocale);
|
|
48
|
+
try {
|
|
49
|
+
for (const key of Object.keys(allStrings).sort()) {
|
|
50
|
+
if (existingJson[key]) {
|
|
51
|
+
outJson[key] = existingJson[key];
|
|
52
|
+
continue;
|
|
53
|
+
}
|
|
54
|
+
if (options.engine === "gpt") {
|
|
55
|
+
outJson[key] = await gptTranslate(key, options.sourceLocale, options.destinationLocale, options.context);
|
|
56
|
+
}
|
|
57
|
+
else {
|
|
58
|
+
outJson[key] = await googleTranslate(key, options.sourceLocale, options.destinationLocale);
|
|
59
|
+
}
|
|
60
|
+
console.log(`${key} => ${outJson[key]}`);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
finally {
|
|
64
|
+
await fs_1.promises.writeFile(options.destinationJson, JSON.stringify(outJson, null, 4));
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
exports.localize = localize;
|
|
@@ -1,16 +1,21 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
-
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
-
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
-
};
|
|
5
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
3
|
exports.toTypescriptCommand = void 0;
|
|
7
|
-
const path_1 = __importDefault(require("path"));
|
|
8
|
-
const translation_set_1 = require("../model/translation-set");
|
|
9
4
|
const fs_1 = require("fs");
|
|
10
5
|
async function toTypescriptCommand(options) {
|
|
11
|
-
const
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
6
|
+
const allStringsJson = JSON.parse(await fs_1.promises.readFile(options.defaultLocalization, "utf-8"));
|
|
7
|
+
let ts = "";
|
|
8
|
+
ts += `export type LocalizedKey = ${Object.keys(allStringsJson)
|
|
9
|
+
.map((key) => JSON.stringify(key))
|
|
10
|
+
.join(" | ")};\n\n`;
|
|
11
|
+
ts += `export type Localization = {[key in LocalizedKey]: string};\n\n`;
|
|
12
|
+
ts += `let LOCALIZATION: Localization = ${JSON.stringify(allStringsJson)};\n\n`;
|
|
13
|
+
ts += `export function setLocalization(localization: Localization) {\n`;
|
|
14
|
+
ts += ` LOCALIZATION = localization;\n`;
|
|
15
|
+
ts += `}\n\n`;
|
|
16
|
+
ts += `export function ${options.localizeFunctionName}(key: string) {\n`;
|
|
17
|
+
ts += ` return LOCALIZATION[key] || key;\n`;
|
|
18
|
+
ts += `}\n`;
|
|
19
|
+
await fs_1.promises.writeFile(options.out, ts);
|
|
15
20
|
}
|
|
16
21
|
exports.toTypescriptCommand = toTypescriptCommand;
|
package/lib/index.js
CHANGED
|
@@ -4,120 +4,89 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
4
4
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
5
5
|
};
|
|
6
6
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
7
|
-
const helpers_1 = require("yargs/helpers");
|
|
8
7
|
const yargs_1 = __importDefault(require("yargs"));
|
|
9
|
-
const
|
|
10
|
-
const
|
|
11
|
-
const
|
|
8
|
+
const helpers_1 = require("yargs/helpers");
|
|
9
|
+
const find_strings_1 = require("./commands/find-strings");
|
|
10
|
+
const localize_1 = require("./commands/localize");
|
|
12
11
|
const to_typescript_1 = require("./commands/to-typescript");
|
|
13
12
|
async function main() {
|
|
14
13
|
await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
|
|
15
|
-
.command(
|
|
14
|
+
.command("find-strings", "Finds all localizable strings in a source file", async (yargs) => {
|
|
16
15
|
const argv = await yargs
|
|
17
|
-
.option(
|
|
18
|
-
type:
|
|
19
|
-
alias:
|
|
20
|
-
describe:
|
|
16
|
+
.option("in", {
|
|
17
|
+
type: "string",
|
|
18
|
+
alias: "i",
|
|
19
|
+
describe: "Folder to read from",
|
|
21
20
|
})
|
|
22
|
-
.
|
|
23
|
-
type:
|
|
24
|
-
alias:
|
|
25
|
-
describe:
|
|
21
|
+
.options("out", {
|
|
22
|
+
type: "string",
|
|
23
|
+
alias: "o",
|
|
24
|
+
describe: "Output JSON file",
|
|
26
25
|
})
|
|
27
|
-
.
|
|
28
|
-
type:
|
|
29
|
-
|
|
30
|
-
describe: 'Index of the row containing the languages',
|
|
26
|
+
.options("localize-function-name", {
|
|
27
|
+
type: "string",
|
|
28
|
+
describe: "Name of the function used for localization",
|
|
31
29
|
})
|
|
32
|
-
.demandOption(
|
|
33
|
-
.demandOption(
|
|
34
|
-
.demandOption(
|
|
35
|
-
|
|
36
|
-
await (0, parse_csv_1.parseCsvCommand)(argv);
|
|
30
|
+
.demandOption("in")
|
|
31
|
+
.demandOption("out")
|
|
32
|
+
.demandOption("localize-function-name").argv;
|
|
33
|
+
await (0, find_strings_1.findStringsCommand)(argv);
|
|
37
34
|
})
|
|
38
|
-
.command(
|
|
35
|
+
.command("localize", "Creates a localization JSON file", async (yargs) => {
|
|
39
36
|
const argv = await yargs
|
|
40
|
-
.option(
|
|
41
|
-
type:
|
|
42
|
-
|
|
43
|
-
describe: 'JSON file to read from',
|
|
44
|
-
})
|
|
45
|
-
.option('out', {
|
|
46
|
-
type: 'string',
|
|
47
|
-
alias: 'o',
|
|
48
|
-
describe: 'Output JSON file',
|
|
37
|
+
.option("source-json", {
|
|
38
|
+
type: "string",
|
|
39
|
+
describe: "File that contains all strings in the original language",
|
|
49
40
|
})
|
|
50
|
-
.option(
|
|
51
|
-
type:
|
|
52
|
-
describe:
|
|
53
|
-
default: 'en',
|
|
41
|
+
.option("source-locale", {
|
|
42
|
+
type: "string",
|
|
43
|
+
describe: "Locale to translate from",
|
|
54
44
|
})
|
|
55
|
-
.
|
|
56
|
-
type:
|
|
57
|
-
alias:
|
|
58
|
-
describe:
|
|
45
|
+
.options("destination-json", {
|
|
46
|
+
type: "string",
|
|
47
|
+
alias: "o",
|
|
48
|
+
describe: "Output JSON file",
|
|
59
49
|
})
|
|
60
|
-
.option(
|
|
61
|
-
type:
|
|
62
|
-
|
|
63
|
-
describe: 'Locale to include',
|
|
50
|
+
.option("destination-locale", {
|
|
51
|
+
type: "string",
|
|
52
|
+
describe: "Locale to translate to",
|
|
64
53
|
})
|
|
65
|
-
.
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
.demandOption('out')
|
|
69
|
-
.demandOption('locale')
|
|
70
|
-
.demandOption('fallback')
|
|
71
|
-
.argv;
|
|
72
|
-
await (0, combine_json_1.combineJsonCommand)(argv);
|
|
73
|
-
})
|
|
74
|
-
.command('to-typescript', 'converts a JSON file into a TypeScript file', async (yargs) => {
|
|
75
|
-
const argv = await yargs
|
|
76
|
-
.option('in', {
|
|
77
|
-
type: 'string',
|
|
78
|
-
alias: 'i',
|
|
79
|
-
describe: 'JSON file to read from',
|
|
54
|
+
.option("context", {
|
|
55
|
+
type: "string",
|
|
56
|
+
describe: "Context to provide to the translation engine",
|
|
80
57
|
})
|
|
81
|
-
.
|
|
82
|
-
type:
|
|
83
|
-
|
|
84
|
-
|
|
58
|
+
.option("engine", {
|
|
59
|
+
type: "string",
|
|
60
|
+
choices: ["gpt", "google"],
|
|
61
|
+
default: "google",
|
|
62
|
+
describe: "Translation engine to use",
|
|
85
63
|
})
|
|
86
|
-
.demandOption(
|
|
87
|
-
.demandOption(
|
|
88
|
-
.
|
|
89
|
-
|
|
64
|
+
.demandOption("source-json")
|
|
65
|
+
.demandOption("source-locale")
|
|
66
|
+
.demandOption("destination-json")
|
|
67
|
+
.demandOption("destination-locale").argv;
|
|
68
|
+
await (0, localize_1.localize)(argv);
|
|
90
69
|
})
|
|
91
|
-
.command(
|
|
70
|
+
.command("to-typescript", "Creates a TypeScript file that can be then imported to localize a project", async (yargs) => {
|
|
92
71
|
const argv = await yargs
|
|
93
|
-
.option(
|
|
94
|
-
type:
|
|
95
|
-
alias:
|
|
96
|
-
describe:
|
|
97
|
-
})
|
|
98
|
-
.option('out', {
|
|
99
|
-
type: 'string',
|
|
100
|
-
alias: 'o',
|
|
101
|
-
describe: 'Output JSON file',
|
|
72
|
+
.option("default-localization", {
|
|
73
|
+
type: "string",
|
|
74
|
+
alias: "i",
|
|
75
|
+
describe: "Default localization JSON file",
|
|
102
76
|
})
|
|
103
|
-
.
|
|
104
|
-
type:
|
|
105
|
-
|
|
106
|
-
|
|
77
|
+
.options("out", {
|
|
78
|
+
type: "string",
|
|
79
|
+
alias: "o",
|
|
80
|
+
describe: "Output Typescript file",
|
|
107
81
|
})
|
|
108
|
-
.
|
|
109
|
-
type:
|
|
110
|
-
|
|
111
|
-
describe: 'Locale to include',
|
|
82
|
+
.options("localize-function-name", {
|
|
83
|
+
type: "string",
|
|
84
|
+
describe: "Name of the function used for localization",
|
|
112
85
|
})
|
|
113
|
-
.
|
|
114
|
-
.
|
|
115
|
-
.demandOption(
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
.argv;
|
|
119
|
-
await (0, google_translate_1.googleTranslateCommand)(argv);
|
|
120
|
-
})
|
|
121
|
-
.argv;
|
|
86
|
+
.demandOption("default-localization")
|
|
87
|
+
.demandOption("out")
|
|
88
|
+
.demandOption("localize-function-name").argv;
|
|
89
|
+
await (0, to_typescript_1.toTypescriptCommand)(argv);
|
|
90
|
+
}).argv;
|
|
122
91
|
}
|
|
123
92
|
main();
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@remvst/localization",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.0",
|
|
4
4
|
"description": "",
|
|
5
5
|
"main": "lib/index.js",
|
|
6
6
|
"bin": {
|
|
@@ -9,21 +9,26 @@
|
|
|
9
9
|
"scripts": {
|
|
10
10
|
"build": "rm -rf lib && tsc",
|
|
11
11
|
"prepublishOnly": "npm i && npm run build",
|
|
12
|
-
"test:
|
|
13
|
-
"test:
|
|
14
|
-
"test:
|
|
15
|
-
"test
|
|
16
|
-
"
|
|
12
|
+
"test:find-strings": "ts-node src/index.ts find-strings -i test/**/*.js -o testOut/localization-en.json --localize-function-name=i18n",
|
|
13
|
+
"test:localize": "ts-node src/index.ts localize --source-json=testOut/localization-en.json --source-locale=en --destination-locale=fr --destination-json=testOut/localization-fr.json",
|
|
14
|
+
"test:to-typescript": "ts-node src/index.ts to-typescript --default-localization=testOut/localization-en.json --out=testOut/localization.ts --localize-function-name=i18n && tsc testOut/localization.ts",
|
|
15
|
+
"test": "npm run test:find-strings && npm run test:localize && npm run test:to-typescript && npm run prettier:check",
|
|
16
|
+
"prettier:check": "prettier --check .",
|
|
17
|
+
"prettier:fix": "prettier --write ."
|
|
17
18
|
},
|
|
18
19
|
"author": "Rémi Vansteelandt",
|
|
19
20
|
"license": "UNLICENSED",
|
|
20
21
|
"dependencies": {
|
|
22
|
+
"openai": "^4.30.0",
|
|
21
23
|
"@vitalets/google-translate-api": "^9.2.0",
|
|
24
|
+
"dotenv": "^16.4.5",
|
|
25
|
+
"glob": "^13.0.6",
|
|
22
26
|
"yargs": "^17.7.2"
|
|
23
27
|
},
|
|
24
28
|
"devDependencies": {
|
|
25
29
|
"@types/node": "^18.11.5",
|
|
26
30
|
"@types/yargs": "^17.0.32",
|
|
31
|
+
"prettier": "3.8.1",
|
|
27
32
|
"ts-node": "^10.9.1",
|
|
28
33
|
"typescript": "^5.2.2"
|
|
29
34
|
}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import { promises as fs } from "fs";
|
|
2
|
+
import { glob } from "glob";
|
|
3
|
+
|
|
4
|
+
export async function findStringsCommand(options: {
|
|
5
|
+
in: string;
|
|
6
|
+
out: string;
|
|
7
|
+
localizeFunctionName: string;
|
|
8
|
+
}) {
|
|
9
|
+
const jsfiles = await glob(options.in);
|
|
10
|
+
|
|
11
|
+
const strings = new Set<string>();
|
|
12
|
+
for (const file of jsfiles) {
|
|
13
|
+
const content = await fs.readFile(file, "utf-8");
|
|
14
|
+
|
|
15
|
+
const matches = [
|
|
16
|
+
...content.matchAll(
|
|
17
|
+
new RegExp(`${options.localizeFunctionName}\\('([^']+)'\\)`, "g"),
|
|
18
|
+
),
|
|
19
|
+
...content.matchAll(
|
|
20
|
+
new RegExp(`${options.localizeFunctionName}\\("([^"]+)"\\)`, "g"),
|
|
21
|
+
),
|
|
22
|
+
...content.matchAll(
|
|
23
|
+
new RegExp(`${options.localizeFunctionName}\\(\`([^"]+)\`\\)`, "g"),
|
|
24
|
+
),
|
|
25
|
+
];
|
|
26
|
+
|
|
27
|
+
for (const [_, string] of matches) {
|
|
28
|
+
strings.add(string);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const outJson: Record<string, string> = {};
|
|
33
|
+
for (const string of Array.from(strings).sort()) {
|
|
34
|
+
outJson[string] = string;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
await fs.writeFile(options.out, JSON.stringify(outJson, null, 4));
|
|
38
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { config } from "dotenv";
|
|
2
|
+
import OpenAI from "openai";
|
|
3
|
+
import { promises as fs } from "fs";
|
|
4
|
+
import { translate } from "@vitalets/google-translate-api";
|
|
5
|
+
|
|
6
|
+
config();
|
|
7
|
+
|
|
8
|
+
export async function googleTranslate(
|
|
9
|
+
text: string,
|
|
10
|
+
from: string,
|
|
11
|
+
to: string,
|
|
12
|
+
): Promise<string> {
|
|
13
|
+
const result = await translate(text, { from, to });
|
|
14
|
+
return result.text;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
async function gptTranslate(
|
|
18
|
+
text: string,
|
|
19
|
+
from: string,
|
|
20
|
+
to: string,
|
|
21
|
+
context?: string,
|
|
22
|
+
): Promise<string> {
|
|
23
|
+
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
|
|
24
|
+
|
|
25
|
+
const prompt =
|
|
26
|
+
`Translate the following text from ${from} to ${to}.` +
|
|
27
|
+
(context ? ` Context: ${context}` : "") +
|
|
28
|
+
`\nText: ${text}`;
|
|
29
|
+
const completion = await openai.chat.completions.create({
|
|
30
|
+
model: "gpt-3.5-turbo",
|
|
31
|
+
messages: [
|
|
32
|
+
{
|
|
33
|
+
role: "system",
|
|
34
|
+
content: `
|
|
35
|
+
You are a helpful translation assistant.
|
|
36
|
+
Only return the string that would be used for translation, so it can be put in the game as is (verbatim).
|
|
37
|
+
`.trim(),
|
|
38
|
+
},
|
|
39
|
+
{ role: "user", content: prompt },
|
|
40
|
+
],
|
|
41
|
+
max_tokens: 1000,
|
|
42
|
+
temperature: 0.3,
|
|
43
|
+
});
|
|
44
|
+
return completion.choices?.[0]?.message?.content?.trim() || "";
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export async function localize(options: {
|
|
48
|
+
sourceJson: string;
|
|
49
|
+
sourceLocale: string;
|
|
50
|
+
destinationJson: string;
|
|
51
|
+
destinationLocale: string;
|
|
52
|
+
context?: string;
|
|
53
|
+
engine?: string;
|
|
54
|
+
}) {
|
|
55
|
+
let existingJson: Record<string, string> = {};
|
|
56
|
+
try {
|
|
57
|
+
existingJson = JSON.parse(
|
|
58
|
+
await fs.readFile(options.destinationJson, "utf-8"),
|
|
59
|
+
);
|
|
60
|
+
} catch (error) {}
|
|
61
|
+
|
|
62
|
+
const allStrings: Record<string, string> = JSON.parse(
|
|
63
|
+
await fs.readFile(options.sourceJson, "utf-8"),
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
const outJson: Record<string, string> = {};
|
|
67
|
+
|
|
68
|
+
console.log(
|
|
69
|
+
"Translating strings from",
|
|
70
|
+
options.sourceLocale,
|
|
71
|
+
"to",
|
|
72
|
+
options.destinationLocale,
|
|
73
|
+
);
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
for (const key of Object.keys(allStrings).sort()) {
|
|
77
|
+
if (existingJson[key]) {
|
|
78
|
+
outJson[key] = existingJson[key];
|
|
79
|
+
continue;
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
if (options.engine === "gpt") {
|
|
83
|
+
outJson[key] = await gptTranslate(
|
|
84
|
+
key,
|
|
85
|
+
options.sourceLocale,
|
|
86
|
+
options.destinationLocale,
|
|
87
|
+
options.context,
|
|
88
|
+
);
|
|
89
|
+
} else {
|
|
90
|
+
outJson[key] = await googleTranslate(
|
|
91
|
+
key,
|
|
92
|
+
options.sourceLocale,
|
|
93
|
+
options.destinationLocale,
|
|
94
|
+
);
|
|
95
|
+
}
|
|
96
|
+
console.log(`${key} => ${outJson[key]}`);
|
|
97
|
+
}
|
|
98
|
+
} finally {
|
|
99
|
+
await fs.writeFile(
|
|
100
|
+
options.destinationJson,
|
|
101
|
+
JSON.stringify(outJson, null, 4),
|
|
102
|
+
);
|
|
103
|
+
}
|
|
104
|
+
}
|
|
@@ -1,14 +1,30 @@
|
|
|
1
|
-
import
|
|
2
|
-
import { TranslationSet } from "../model/translation-set";
|
|
3
|
-
import { promises as fs } from 'fs';
|
|
1
|
+
import { promises as fs } from "fs";
|
|
4
2
|
|
|
5
3
|
export async function toTypescriptCommand(options: {
|
|
6
|
-
|
|
7
|
-
|
|
4
|
+
defaultLocalization: string;
|
|
5
|
+
out: string;
|
|
6
|
+
localizeFunctionName: string;
|
|
8
7
|
}) {
|
|
9
|
-
|
|
10
|
-
|
|
8
|
+
const allStringsJson = JSON.parse(
|
|
9
|
+
await fs.readFile(options.defaultLocalization, "utf-8"),
|
|
10
|
+
);
|
|
11
11
|
|
|
12
|
-
|
|
13
|
-
|
|
12
|
+
let ts = "";
|
|
13
|
+
|
|
14
|
+
ts += `export type LocalizedKey = ${Object.keys(allStringsJson)
|
|
15
|
+
.map((key) => JSON.stringify(key))
|
|
16
|
+
.join(" | ")};\n\n`;
|
|
17
|
+
ts += `export type Localization = {[key in LocalizedKey]: string};\n\n`;
|
|
18
|
+
|
|
19
|
+
ts += `let LOCALIZATION: Localization = ${JSON.stringify(allStringsJson)};\n\n`;
|
|
20
|
+
|
|
21
|
+
ts += `export function setLocalization(localization: Localization) {\n`;
|
|
22
|
+
ts += ` LOCALIZATION = localization;\n`;
|
|
23
|
+
ts += `}\n\n`;
|
|
24
|
+
|
|
25
|
+
ts += `export function ${options.localizeFunctionName}(key: string) {\n`;
|
|
26
|
+
ts += ` return LOCALIZATION[key] || key;\n`;
|
|
27
|
+
ts += `}\n`;
|
|
28
|
+
|
|
29
|
+
await fs.writeFile(options.out, ts);
|
|
14
30
|
}
|