@theia/localization-manager 1.48.1 → 1.48.3
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/README.md +67 -67
- package/lib/common.d.ts +4 -4
- package/lib/common.js +26 -26
- package/lib/deepl-api.d.ts +26 -26
- package/lib/deepl-api.js +93 -93
- package/lib/index.d.ts +3 -3
- package/lib/index.js +21 -21
- package/lib/localization-extractor.d.ts +13 -13
- package/lib/localization-extractor.js +368 -368
- package/lib/localization-extractor.spec.d.ts +1 -1
- package/lib/localization-extractor.spec.js +138 -138
- package/lib/localization-manager.d.ts +24 -24
- package/lib/localization-manager.js +148 -148
- package/lib/localization-manager.spec.d.ts +1 -1
- package/lib/localization-manager.spec.js +84 -84
- package/package.json +3 -3
- package/src/common.ts +27 -27
- package/src/deepl-api.ts +153 -153
- package/src/index.ts +19 -19
- package/src/localization-extractor.spec.ts +151 -151
- package/src/localization-extractor.ts +422 -422
- package/src/localization-manager.spec.ts +91 -91
- package/src/localization-manager.ts +160 -160
|
@@ -1,91 +1,91 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2021 TypeFox and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
import * as assert from 'assert';
|
|
18
|
-
import { DeeplParameters, DeeplResponse } from './deepl-api';
|
|
19
|
-
import { LocalizationManager, LocalizationOptions } from './localization-manager';
|
|
20
|
-
|
|
21
|
-
describe('localization-manager#translateLanguage', () => {
|
|
22
|
-
|
|
23
|
-
async function mockLocalization(parameters: DeeplParameters): Promise<DeeplResponse> {
|
|
24
|
-
return {
|
|
25
|
-
translations: parameters.text.map(value => ({
|
|
26
|
-
detected_source_language: '',
|
|
27
|
-
text: `[${value}]`
|
|
28
|
-
}))
|
|
29
|
-
};
|
|
30
|
-
}
|
|
31
|
-
|
|
32
|
-
const manager = new LocalizationManager(mockLocalization);
|
|
33
|
-
const defaultOptions: LocalizationOptions = {
|
|
34
|
-
authKey: '',
|
|
35
|
-
freeApi: false,
|
|
36
|
-
sourceFile: '',
|
|
37
|
-
targetLanguages: ['EN']
|
|
38
|
-
};
|
|
39
|
-
|
|
40
|
-
it('should translate a single value', async () => {
|
|
41
|
-
const input = {
|
|
42
|
-
key: 'value'
|
|
43
|
-
};
|
|
44
|
-
const target = {};
|
|
45
|
-
await manager.translateLanguage(input, target, 'EN', defaultOptions);
|
|
46
|
-
assert.deepStrictEqual(target, {
|
|
47
|
-
key: '[value]'
|
|
48
|
-
});
|
|
49
|
-
});
|
|
50
|
-
|
|
51
|
-
it('should translate nested values', async () => {
|
|
52
|
-
const input = {
|
|
53
|
-
a: {
|
|
54
|
-
b: 'b'
|
|
55
|
-
},
|
|
56
|
-
c: 'c'
|
|
57
|
-
};
|
|
58
|
-
const target = {};
|
|
59
|
-
await manager.translateLanguage(input, target, 'EN', defaultOptions);
|
|
60
|
-
assert.deepStrictEqual(target, {
|
|
61
|
-
a: {
|
|
62
|
-
b: '[b]'
|
|
63
|
-
},
|
|
64
|
-
c: '[c]'
|
|
65
|
-
});
|
|
66
|
-
});
|
|
67
|
-
|
|
68
|
-
it('should not override existing targets', async () => {
|
|
69
|
-
const input = {
|
|
70
|
-
a: 'a'
|
|
71
|
-
};
|
|
72
|
-
const target = {
|
|
73
|
-
a: 'b'
|
|
74
|
-
};
|
|
75
|
-
await manager.translateLanguage(input, target, 'EN', defaultOptions);
|
|
76
|
-
assert.deepStrictEqual(target, {
|
|
77
|
-
a: 'b'
|
|
78
|
-
});
|
|
79
|
-
});
|
|
80
|
-
|
|
81
|
-
it('should keep placeholders intact', async () => {
|
|
82
|
-
const input = {
|
|
83
|
-
key: '{1} {0}'
|
|
84
|
-
};
|
|
85
|
-
const target = {};
|
|
86
|
-
await manager.translateLanguage(input, target, 'EN', defaultOptions);
|
|
87
|
-
assert.deepStrictEqual(target, {
|
|
88
|
-
key: '[{1} {0}]'
|
|
89
|
-
});
|
|
90
|
-
});
|
|
91
|
-
});
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2021 TypeFox and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import * as assert from 'assert';
|
|
18
|
+
import { DeeplParameters, DeeplResponse } from './deepl-api';
|
|
19
|
+
import { LocalizationManager, LocalizationOptions } from './localization-manager';
|
|
20
|
+
|
|
21
|
+
describe('localization-manager#translateLanguage', () => {
|
|
22
|
+
|
|
23
|
+
async function mockLocalization(parameters: DeeplParameters): Promise<DeeplResponse> {
|
|
24
|
+
return {
|
|
25
|
+
translations: parameters.text.map(value => ({
|
|
26
|
+
detected_source_language: '',
|
|
27
|
+
text: `[${value}]`
|
|
28
|
+
}))
|
|
29
|
+
};
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const manager = new LocalizationManager(mockLocalization);
|
|
33
|
+
const defaultOptions: LocalizationOptions = {
|
|
34
|
+
authKey: '',
|
|
35
|
+
freeApi: false,
|
|
36
|
+
sourceFile: '',
|
|
37
|
+
targetLanguages: ['EN']
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
it('should translate a single value', async () => {
|
|
41
|
+
const input = {
|
|
42
|
+
key: 'value'
|
|
43
|
+
};
|
|
44
|
+
const target = {};
|
|
45
|
+
await manager.translateLanguage(input, target, 'EN', defaultOptions);
|
|
46
|
+
assert.deepStrictEqual(target, {
|
|
47
|
+
key: '[value]'
|
|
48
|
+
});
|
|
49
|
+
});
|
|
50
|
+
|
|
51
|
+
it('should translate nested values', async () => {
|
|
52
|
+
const input = {
|
|
53
|
+
a: {
|
|
54
|
+
b: 'b'
|
|
55
|
+
},
|
|
56
|
+
c: 'c'
|
|
57
|
+
};
|
|
58
|
+
const target = {};
|
|
59
|
+
await manager.translateLanguage(input, target, 'EN', defaultOptions);
|
|
60
|
+
assert.deepStrictEqual(target, {
|
|
61
|
+
a: {
|
|
62
|
+
b: '[b]'
|
|
63
|
+
},
|
|
64
|
+
c: '[c]'
|
|
65
|
+
});
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
it('should not override existing targets', async () => {
|
|
69
|
+
const input = {
|
|
70
|
+
a: 'a'
|
|
71
|
+
};
|
|
72
|
+
const target = {
|
|
73
|
+
a: 'b'
|
|
74
|
+
};
|
|
75
|
+
await manager.translateLanguage(input, target, 'EN', defaultOptions);
|
|
76
|
+
assert.deepStrictEqual(target, {
|
|
77
|
+
a: 'b'
|
|
78
|
+
});
|
|
79
|
+
});
|
|
80
|
+
|
|
81
|
+
it('should keep placeholders intact', async () => {
|
|
82
|
+
const input = {
|
|
83
|
+
key: '{1} {0}'
|
|
84
|
+
};
|
|
85
|
+
const target = {};
|
|
86
|
+
await manager.translateLanguage(input, target, 'EN', defaultOptions);
|
|
87
|
+
assert.deepStrictEqual(target, {
|
|
88
|
+
key: '[{1} {0}]'
|
|
89
|
+
});
|
|
90
|
+
});
|
|
91
|
+
});
|
|
@@ -1,160 +1,160 @@
|
|
|
1
|
-
// *****************************************************************************
|
|
2
|
-
// Copyright (C) 2021 TypeFox and others.
|
|
3
|
-
//
|
|
4
|
-
// This program and the accompanying materials are made available under the
|
|
5
|
-
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
-
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
-
//
|
|
8
|
-
// This Source Code may also be made available under the following Secondary
|
|
9
|
-
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
-
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
-
// with the GNU Classpath Exception which is available at
|
|
12
|
-
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
-
//
|
|
14
|
-
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
-
// *****************************************************************************
|
|
16
|
-
|
|
17
|
-
import * as chalk from 'chalk';
|
|
18
|
-
import * as fs from 'fs-extra';
|
|
19
|
-
import * as path from 'path';
|
|
20
|
-
import { Localization, sortLocalization } from './common';
|
|
21
|
-
import { deepl, DeeplLanguage, DeeplParameters, isSupportedLanguage, supportedLanguages } from './deepl-api';
|
|
22
|
-
|
|
23
|
-
export interface LocalizationOptions {
|
|
24
|
-
freeApi: Boolean
|
|
25
|
-
authKey: string
|
|
26
|
-
sourceFile: string
|
|
27
|
-
sourceLanguage?: string
|
|
28
|
-
targetLanguages: string[]
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export type LocalizationFunction = (parameters: DeeplParameters) => Promise<string[]>;
|
|
32
|
-
|
|
33
|
-
export class LocalizationManager {
|
|
34
|
-
|
|
35
|
-
constructor(private localizationFn = deepl) { }
|
|
36
|
-
|
|
37
|
-
async localize(options: LocalizationOptions): Promise<void> {
|
|
38
|
-
let source: Localization = {};
|
|
39
|
-
const cwd = process.env.INIT_CWD || process.cwd();
|
|
40
|
-
const sourceFile = path.resolve(cwd, options.sourceFile);
|
|
41
|
-
try {
|
|
42
|
-
source = await fs.readJson(sourceFile);
|
|
43
|
-
} catch {
|
|
44
|
-
console.log(chalk.red(`Could not read file "${options.sourceFile}"`));
|
|
45
|
-
process.exit(1);
|
|
46
|
-
}
|
|
47
|
-
const languages: string[] = [];
|
|
48
|
-
for (const targetLanguage of options.targetLanguages) {
|
|
49
|
-
if (!isSupportedLanguage(targetLanguage)) {
|
|
50
|
-
console.log(chalk.yellow(`Language "${targetLanguage}" is not supported for automatic localization`));
|
|
51
|
-
} else {
|
|
52
|
-
languages.push(targetLanguage);
|
|
53
|
-
}
|
|
54
|
-
}
|
|
55
|
-
if (languages.length !== options.targetLanguages.length) {
|
|
56
|
-
console.log('Supported languages: ' + supportedLanguages.join(', '));
|
|
57
|
-
}
|
|
58
|
-
const existingTranslations: Map<string, Localization> = new Map();
|
|
59
|
-
for (const targetLanguage of languages) {
|
|
60
|
-
try {
|
|
61
|
-
const targetPath = this.translationFileName(sourceFile, targetLanguage);
|
|
62
|
-
existingTranslations.set(targetLanguage, await fs.readJson(targetPath));
|
|
63
|
-
} catch {
|
|
64
|
-
existingTranslations.set(targetLanguage, {});
|
|
65
|
-
}
|
|
66
|
-
}
|
|
67
|
-
await Promise.all(languages.map(language => this.translateLanguage(source, existingTranslations.get(language)!, language, options)));
|
|
68
|
-
|
|
69
|
-
for (const targetLanguage of languages) {
|
|
70
|
-
const targetPath = this.translationFileName(sourceFile, targetLanguage);
|
|
71
|
-
try {
|
|
72
|
-
const translation = existingTranslations.get(targetLanguage)!;
|
|
73
|
-
await fs.writeJson(targetPath, sortLocalization(translation), { spaces: 2 });
|
|
74
|
-
} catch {
|
|
75
|
-
console.error(chalk.red(`Error writing translated file to '${targetPath}'`));
|
|
76
|
-
}
|
|
77
|
-
}
|
|
78
|
-
}
|
|
79
|
-
|
|
80
|
-
protected translationFileName(original: string, language: string): string {
|
|
81
|
-
const directory = path.dirname(original);
|
|
82
|
-
const fileName = path.basename(original, '.json');
|
|
83
|
-
return path.join(directory, `${fileName}.${language.toLowerCase()}.json`);
|
|
84
|
-
}
|
|
85
|
-
|
|
86
|
-
async translateLanguage(source: Localization, target: Localization, targetLanguage: string, options: LocalizationOptions): Promise<void> {
|
|
87
|
-
const map = this.buildLocalizationMap(source, target);
|
|
88
|
-
if (map.text.length > 0) {
|
|
89
|
-
try {
|
|
90
|
-
const translationResponse = await this.localizationFn({
|
|
91
|
-
auth_key: options.authKey,
|
|
92
|
-
free_api: options.freeApi,
|
|
93
|
-
target_lang: targetLanguage.toUpperCase() as DeeplLanguage,
|
|
94
|
-
source_lang: options.sourceLanguage?.toUpperCase() as DeeplLanguage,
|
|
95
|
-
text: map.text.map(e => this.addIgnoreTags(e)),
|
|
96
|
-
tag_handling: ['xml'],
|
|
97
|
-
ignore_tags: ['x']
|
|
98
|
-
});
|
|
99
|
-
translationResponse.translations.forEach(({ text }, i) => {
|
|
100
|
-
map.localize(i, this.removeIgnoreTags(text));
|
|
101
|
-
});
|
|
102
|
-
console.log(chalk.green(`Successfully translated ${map.text.length} value${map.text.length > 1 ? 's' : ''} for language "${targetLanguage}"`));
|
|
103
|
-
} catch (e) {
|
|
104
|
-
console.log(chalk.red(`Could not translate into language "${targetLanguage}"`), e);
|
|
105
|
-
}
|
|
106
|
-
} else {
|
|
107
|
-
console.log(`No translation necessary for language "${targetLanguage}"`);
|
|
108
|
-
}
|
|
109
|
-
}
|
|
110
|
-
|
|
111
|
-
protected addIgnoreTags(text: string): string {
|
|
112
|
-
return text.replace(/(\{\d*\})/g, '<x>$1</x>');
|
|
113
|
-
}
|
|
114
|
-
|
|
115
|
-
protected removeIgnoreTags(text: string): string {
|
|
116
|
-
return text.replace(/<x>(\{\d+\})<\/x>/g, '$1');
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
protected buildLocalizationMap(source: Localization, target: Localization): LocalizationMap {
|
|
120
|
-
const functionMap = new Map<number, (value: string) => void>();
|
|
121
|
-
const text: string[] = [];
|
|
122
|
-
const process = (s: Localization, t: Localization) => {
|
|
123
|
-
// Delete all extra keys in the target translation first
|
|
124
|
-
for (const key of Object.keys(t)) {
|
|
125
|
-
if (!(key in s)) {
|
|
126
|
-
delete t[key];
|
|
127
|
-
}
|
|
128
|
-
}
|
|
129
|
-
for (const [key, value] of Object.entries(s)) {
|
|
130
|
-
if (!(key in t)) {
|
|
131
|
-
if (typeof value === 'string') {
|
|
132
|
-
functionMap.set(text.length, translation => t[key] = translation);
|
|
133
|
-
text.push(value);
|
|
134
|
-
} else {
|
|
135
|
-
const newLocalization: Localization = {};
|
|
136
|
-
t[key] = newLocalization;
|
|
137
|
-
process(value, newLocalization);
|
|
138
|
-
}
|
|
139
|
-
} else if (typeof value === 'object') {
|
|
140
|
-
if (typeof t[key] === 'string') {
|
|
141
|
-
t[key] = {};
|
|
142
|
-
}
|
|
143
|
-
process(value, t[key] as Localization);
|
|
144
|
-
}
|
|
145
|
-
}
|
|
146
|
-
};
|
|
147
|
-
|
|
148
|
-
process(source, target);
|
|
149
|
-
|
|
150
|
-
return {
|
|
151
|
-
text,
|
|
152
|
-
localize: (index, value) => functionMap.get(index)!(value)
|
|
153
|
-
};
|
|
154
|
-
}
|
|
155
|
-
}
|
|
156
|
-
|
|
157
|
-
export interface LocalizationMap {
|
|
158
|
-
text: string[]
|
|
159
|
-
localize: (index: number, value: string) => void
|
|
160
|
-
}
|
|
1
|
+
// *****************************************************************************
|
|
2
|
+
// Copyright (C) 2021 TypeFox and others.
|
|
3
|
+
//
|
|
4
|
+
// This program and the accompanying materials are made available under the
|
|
5
|
+
// terms of the Eclipse Public License v. 2.0 which is available at
|
|
6
|
+
// http://www.eclipse.org/legal/epl-2.0.
|
|
7
|
+
//
|
|
8
|
+
// This Source Code may also be made available under the following Secondary
|
|
9
|
+
// Licenses when the conditions for such availability set forth in the Eclipse
|
|
10
|
+
// Public License v. 2.0 are satisfied: GNU General Public License, version 2
|
|
11
|
+
// with the GNU Classpath Exception which is available at
|
|
12
|
+
// https://www.gnu.org/software/classpath/license.html.
|
|
13
|
+
//
|
|
14
|
+
// SPDX-License-Identifier: EPL-2.0 OR GPL-2.0-only WITH Classpath-exception-2.0
|
|
15
|
+
// *****************************************************************************
|
|
16
|
+
|
|
17
|
+
import * as chalk from 'chalk';
|
|
18
|
+
import * as fs from 'fs-extra';
|
|
19
|
+
import * as path from 'path';
|
|
20
|
+
import { Localization, sortLocalization } from './common';
|
|
21
|
+
import { deepl, DeeplLanguage, DeeplParameters, isSupportedLanguage, supportedLanguages } from './deepl-api';
|
|
22
|
+
|
|
23
|
+
export interface LocalizationOptions {
|
|
24
|
+
freeApi: Boolean
|
|
25
|
+
authKey: string
|
|
26
|
+
sourceFile: string
|
|
27
|
+
sourceLanguage?: string
|
|
28
|
+
targetLanguages: string[]
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export type LocalizationFunction = (parameters: DeeplParameters) => Promise<string[]>;
|
|
32
|
+
|
|
33
|
+
export class LocalizationManager {
|
|
34
|
+
|
|
35
|
+
constructor(private localizationFn = deepl) { }
|
|
36
|
+
|
|
37
|
+
async localize(options: LocalizationOptions): Promise<void> {
|
|
38
|
+
let source: Localization = {};
|
|
39
|
+
const cwd = process.env.INIT_CWD || process.cwd();
|
|
40
|
+
const sourceFile = path.resolve(cwd, options.sourceFile);
|
|
41
|
+
try {
|
|
42
|
+
source = await fs.readJson(sourceFile);
|
|
43
|
+
} catch {
|
|
44
|
+
console.log(chalk.red(`Could not read file "${options.sourceFile}"`));
|
|
45
|
+
process.exit(1);
|
|
46
|
+
}
|
|
47
|
+
const languages: string[] = [];
|
|
48
|
+
for (const targetLanguage of options.targetLanguages) {
|
|
49
|
+
if (!isSupportedLanguage(targetLanguage)) {
|
|
50
|
+
console.log(chalk.yellow(`Language "${targetLanguage}" is not supported for automatic localization`));
|
|
51
|
+
} else {
|
|
52
|
+
languages.push(targetLanguage);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
if (languages.length !== options.targetLanguages.length) {
|
|
56
|
+
console.log('Supported languages: ' + supportedLanguages.join(', '));
|
|
57
|
+
}
|
|
58
|
+
const existingTranslations: Map<string, Localization> = new Map();
|
|
59
|
+
for (const targetLanguage of languages) {
|
|
60
|
+
try {
|
|
61
|
+
const targetPath = this.translationFileName(sourceFile, targetLanguage);
|
|
62
|
+
existingTranslations.set(targetLanguage, await fs.readJson(targetPath));
|
|
63
|
+
} catch {
|
|
64
|
+
existingTranslations.set(targetLanguage, {});
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
await Promise.all(languages.map(language => this.translateLanguage(source, existingTranslations.get(language)!, language, options)));
|
|
68
|
+
|
|
69
|
+
for (const targetLanguage of languages) {
|
|
70
|
+
const targetPath = this.translationFileName(sourceFile, targetLanguage);
|
|
71
|
+
try {
|
|
72
|
+
const translation = existingTranslations.get(targetLanguage)!;
|
|
73
|
+
await fs.writeJson(targetPath, sortLocalization(translation), { spaces: 2 });
|
|
74
|
+
} catch {
|
|
75
|
+
console.error(chalk.red(`Error writing translated file to '${targetPath}'`));
|
|
76
|
+
}
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
protected translationFileName(original: string, language: string): string {
|
|
81
|
+
const directory = path.dirname(original);
|
|
82
|
+
const fileName = path.basename(original, '.json');
|
|
83
|
+
return path.join(directory, `${fileName}.${language.toLowerCase()}.json`);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
async translateLanguage(source: Localization, target: Localization, targetLanguage: string, options: LocalizationOptions): Promise<void> {
|
|
87
|
+
const map = this.buildLocalizationMap(source, target);
|
|
88
|
+
if (map.text.length > 0) {
|
|
89
|
+
try {
|
|
90
|
+
const translationResponse = await this.localizationFn({
|
|
91
|
+
auth_key: options.authKey,
|
|
92
|
+
free_api: options.freeApi,
|
|
93
|
+
target_lang: targetLanguage.toUpperCase() as DeeplLanguage,
|
|
94
|
+
source_lang: options.sourceLanguage?.toUpperCase() as DeeplLanguage,
|
|
95
|
+
text: map.text.map(e => this.addIgnoreTags(e)),
|
|
96
|
+
tag_handling: ['xml'],
|
|
97
|
+
ignore_tags: ['x']
|
|
98
|
+
});
|
|
99
|
+
translationResponse.translations.forEach(({ text }, i) => {
|
|
100
|
+
map.localize(i, this.removeIgnoreTags(text));
|
|
101
|
+
});
|
|
102
|
+
console.log(chalk.green(`Successfully translated ${map.text.length} value${map.text.length > 1 ? 's' : ''} for language "${targetLanguage}"`));
|
|
103
|
+
} catch (e) {
|
|
104
|
+
console.log(chalk.red(`Could not translate into language "${targetLanguage}"`), e);
|
|
105
|
+
}
|
|
106
|
+
} else {
|
|
107
|
+
console.log(`No translation necessary for language "${targetLanguage}"`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
protected addIgnoreTags(text: string): string {
|
|
112
|
+
return text.replace(/(\{\d*\})/g, '<x>$1</x>');
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
protected removeIgnoreTags(text: string): string {
|
|
116
|
+
return text.replace(/<x>(\{\d+\})<\/x>/g, '$1');
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
protected buildLocalizationMap(source: Localization, target: Localization): LocalizationMap {
|
|
120
|
+
const functionMap = new Map<number, (value: string) => void>();
|
|
121
|
+
const text: string[] = [];
|
|
122
|
+
const process = (s: Localization, t: Localization) => {
|
|
123
|
+
// Delete all extra keys in the target translation first
|
|
124
|
+
for (const key of Object.keys(t)) {
|
|
125
|
+
if (!(key in s)) {
|
|
126
|
+
delete t[key];
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
for (const [key, value] of Object.entries(s)) {
|
|
130
|
+
if (!(key in t)) {
|
|
131
|
+
if (typeof value === 'string') {
|
|
132
|
+
functionMap.set(text.length, translation => t[key] = translation);
|
|
133
|
+
text.push(value);
|
|
134
|
+
} else {
|
|
135
|
+
const newLocalization: Localization = {};
|
|
136
|
+
t[key] = newLocalization;
|
|
137
|
+
process(value, newLocalization);
|
|
138
|
+
}
|
|
139
|
+
} else if (typeof value === 'object') {
|
|
140
|
+
if (typeof t[key] === 'string') {
|
|
141
|
+
t[key] = {};
|
|
142
|
+
}
|
|
143
|
+
process(value, t[key] as Localization);
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
};
|
|
147
|
+
|
|
148
|
+
process(source, target);
|
|
149
|
+
|
|
150
|
+
return {
|
|
151
|
+
text,
|
|
152
|
+
localize: (index, value) => functionMap.get(index)!(value)
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export interface LocalizationMap {
|
|
158
|
+
text: string[]
|
|
159
|
+
localize: (index: number, value: string) => void
|
|
160
|
+
}
|