@remvst/localization 1.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.
@@ -0,0 +1,25 @@
1
+ name: Check
2
+ on:
3
+ push:
4
+ branches:
5
+ - master
6
+ workflow_dispatch:
7
+ jobs:
8
+ check:
9
+ runs-on: ubuntu-latest
10
+ concurrency:
11
+ group: check-${{ github.ref }}
12
+ cancel-in-progress: true
13
+ steps:
14
+ - uses: actions/setup-node@v3
15
+ with:
16
+ node-version: 18
17
+ - name: Check out repository code
18
+ uses: actions/checkout@v3
19
+ - name: Install dependencies
20
+ run: npm install
21
+ - name: Create testOut
22
+ run: mkdir testOut
23
+ - name: Run tests
24
+ shell: bash
25
+ run: npm test
package/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # @remvst/localization
2
+
3
+ Localization utils for TypeScript.
4
+
5
+ ## Installation
6
+
7
+ ```sh
8
+ npm install --save-dev @remvst/localization
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Create a JSON file with your localized strings (`my-localization.json`):
14
+
15
+ ```json
16
+ {
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": { "en": "Back To Main Menu", "fr": "Retour au menu principal" }
24
+ }
25
+ ```
26
+
27
+ Parse the Polyglot CSV file into a JSON file:
28
+
29
+ ```sh
30
+ npx @remvst/localize parse-csv \
31
+ --in=polyglot.csv \
32
+ --out=polyglot.json \
33
+ --languages-line-index=1
34
+ ```
35
+
36
+ Backfill missing translations using a fallback JSON:
37
+
38
+ ```sh
39
+ npx @remvst/localize combine-json \
40
+ --main=localization.json \
41
+ --fallback=polyglot.json \
42
+ --out=localization-combined.json \
43
+ --fallback-locale=en \
44
+ --locale=en \
45
+ --locale=fr \
46
+ --locale=es
47
+ ```
48
+
49
+ Backfill missing translations using Google translate:
50
+
51
+ ```sh
52
+ npx @remvst/localize google-translate \
53
+ --in=localization-combined.json \
54
+ --out=localization-full.json \
55
+ --fallback-locale=en \
56
+ --locale=en \
57
+ --locale=fr \
58
+ --locale=es
59
+ ```
60
+
61
+ Export to Typescript:
62
+
63
+ ```sh
64
+ npx @remvst/localize to-typescript \
65
+ --in=localization-full.json \
66
+ --out=localization-full.ts
67
+ ```
@@ -0,0 +1,7 @@
1
+ export declare function combineJsonCommand(options: {
2
+ main: string;
3
+ out: string;
4
+ locale: string[];
5
+ fallbackLocale: string;
6
+ fallback: string[];
7
+ }): Promise<void>;
@@ -0,0 +1,30 @@
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;
@@ -0,0 +1,6 @@
1
+ export declare function googleTranslateCommand(options: {
2
+ in: string;
3
+ out: string;
4
+ locale: string[];
5
+ fallbackLocale: string;
6
+ }): Promise<void>;
@@ -0,0 +1,20 @@
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;
@@ -0,0 +1,7 @@
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>;
@@ -0,0 +1,31 @@
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;
@@ -0,0 +1,4 @@
1
+ export declare function toTypescriptCommand(options: {
2
+ in: string;
3
+ out: string;
4
+ }): Promise<void>;
@@ -0,0 +1,11 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.toTypescriptCommand = void 0;
4
+ const translation_set_1 = require("../model/translation-set");
5
+ const fs_1 = require("fs");
6
+ async function toTypescriptCommand(options) {
7
+ const sourceJson = JSON.parse(await fs_1.promises.readFile(options.in, 'utf-8'));
8
+ const outTranslations = translation_set_1.TranslationSet.fromJSON(sourceJson);
9
+ await fs_1.promises.writeFile(options.out, outTranslations.toTypeScript());
10
+ }
11
+ exports.toTypescriptCommand = toTypescriptCommand;
package/lib/index.d.ts ADDED
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env node
2
+ export {};
package/lib/index.js ADDED
@@ -0,0 +1,123 @@
1
+ #!/usr/bin/env node
2
+ "use strict";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ const helpers_1 = require("yargs/helpers");
8
+ const yargs_1 = __importDefault(require("yargs"));
9
+ const combine_json_1 = require("./commands/combine-json");
10
+ const google_translate_1 = require("./commands/google-translate");
11
+ const parse_csv_1 = require("./commands/parse-csv");
12
+ const to_typescript_1 = require("./commands/to-typescript");
13
+ async function main() {
14
+ await (0, yargs_1.default)((0, helpers_1.hideBin)(process.argv))
15
+ .command('parse-csv', 'convert a CSV into a JSON', async (yargs) => {
16
+ const argv = await yargs
17
+ .option('in', {
18
+ type: 'string',
19
+ alias: 'i',
20
+ describe: 'Path to CSV to read from',
21
+ })
22
+ .option('out', {
23
+ type: 'string',
24
+ alias: 'o',
25
+ describe: 'Output file',
26
+ })
27
+ .option('languagesLineIndex', {
28
+ type: 'number',
29
+ alias: 'l',
30
+ describe: 'Index of the row containing the languages',
31
+ })
32
+ .demandOption('in')
33
+ .demandOption('out')
34
+ .demandOption('languagesLineIndex')
35
+ .argv;
36
+ await (0, parse_csv_1.parseCsvCommand)(argv);
37
+ })
38
+ .command('combine-json', 'combine multiple JSON files', async (yargs) => {
39
+ const argv = await yargs
40
+ .option('main', {
41
+ type: 'string',
42
+ alias: 'i',
43
+ describe: 'JSON file to read from',
44
+ })
45
+ .option('out', {
46
+ type: 'string',
47
+ alias: 'o',
48
+ describe: 'Output JSON file',
49
+ })
50
+ .option('fallbackLocale', {
51
+ type: 'string',
52
+ describe: 'Default locale to backfill translations',
53
+ default: 'en',
54
+ })
55
+ .option('fallback', {
56
+ type: 'string',
57
+ alias: 'f',
58
+ describe: 'Fallback JSON file to read from',
59
+ })
60
+ .option('locale', {
61
+ type: 'string',
62
+ alias: 'l',
63
+ describe: 'Locale to include',
64
+ })
65
+ .array('fallback')
66
+ .array('locale')
67
+ .demandOption('main')
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',
80
+ })
81
+ .options('out', {
82
+ type: 'string',
83
+ alias: 'o',
84
+ describe: 'Output Typescript file',
85
+ })
86
+ .demandOption('in')
87
+ .demandOption('out')
88
+ .argv;
89
+ await (0, to_typescript_1.toTypescriptCommand)(argv);
90
+ })
91
+ .command('google-translate', 'adds missing translations to a JSON file using Google translate', async (yargs) => {
92
+ const argv = await yargs
93
+ .option('in', {
94
+ type: 'string',
95
+ alias: 'i',
96
+ describe: 'JSON file to read from',
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
+ await (0, google_translate_1.googleTranslateCommand)(argv);
120
+ })
121
+ .argv;
122
+ }
123
+ main();
@@ -0,0 +1,21 @@
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
+ }
@@ -0,0 +1,92 @@
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 += ` ${key}: {\n`;
52
+ for (const [locale, translation] of localizedItem.entries()) {
53
+ if (!translation)
54
+ continue;
55
+ generatedFileContent += ` ${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;
package/package.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "name": "@remvst/localization",
3
+ "version": "1.0.0",
4
+ "description": "",
5
+ "main": "lib/index.js",
6
+ "bin": {
7
+ "localization": "./lib/index.js"
8
+ },
9
+ "scripts": {
10
+ "build": "rm -rf lib && tsc",
11
+ "prepublishOnly": "npm i && npm run build",
12
+ "test:polyglot": "ts-node src/index.ts index -i test/polyglot.csv -o testOut/polyglot.json -l 1",
13
+ "test:backfill-json": "ts-node src/index.ts combine-json --main=test/localization.json --out=testOut/localization-backfilled.json --fallback=testOut/polyglot.json -l en -l fr -l es",
14
+ "test:backfill-google-translate": "ts-node src/index.ts google-translate --in=testOut/localization-backfilled.json --out=testOut/localization-backfilled-google-translate.json -l en -l fr -l es",
15
+ "test:to-typescript": "ts-node src/index.ts to-typescript -i testOut/localization-backfilled.json -o testOut/localization.ts",
16
+ "test": "npm run test:polyglot && npm run test:backfill-json && npm run test:to-typescript"
17
+ },
18
+ "author": "Rémi Vansteelandt",
19
+ "license": "UNLICENSED",
20
+ "dependencies": {
21
+ "@vitalets/google-translate-api": "^9.2.0",
22
+ "yargs": "^17.7.2"
23
+ },
24
+ "devDependencies": {
25
+ "@types/node": "^18.11.5",
26
+ "@types/yargs": "^17.0.32",
27
+ "ts-node": "^10.9.1",
28
+ "typescript": "^5.2.2"
29
+ }
30
+ }
@@ -0,0 +1,39 @@
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
+ }
@@ -0,0 +1,32 @@
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
+ }
@@ -0,0 +1,41 @@
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
+ }
@@ -0,0 +1,12 @@
1
+ import { TranslationSet } from "../model/translation-set";
2
+ import { promises as fs } from 'fs';
3
+
4
+ export async function toTypescriptCommand(options: {
5
+ in: string,
6
+ out: string,
7
+ }) {
8
+ const sourceJson = JSON.parse(await fs.readFile(options.in, 'utf-8'));
9
+ const outTranslations = TranslationSet.fromJSON(sourceJson);
10
+
11
+ await fs.writeFile(options.out, outTranslations.toTypeScript());
12
+ }