@umituz/web-localization 1.1.0 → 1.1.4
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/dist/domain/entities/translation.entity.d.ts +0 -1
- package/dist/domain/interfaces/translation-service.interface.d.ts +0 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -0
- package/dist/infrastructure/constants/index.d.ts +0 -1
- package/dist/infrastructure/services/cli.service.d.ts +1 -1
- package/dist/infrastructure/services/cli.service.js +1 -1
- package/dist/infrastructure/services/google-translate.service.d.ts +1 -2
- package/dist/infrastructure/services/google-translate.service.js +7 -3
- package/dist/infrastructure/utils/file.util.d.ts +0 -1
- package/dist/infrastructure/utils/rate-limit.util.d.ts +0 -1
- package/dist/infrastructure/utils/text-validator.util.d.ts +0 -1
- package/dist/integrations/i18n.setup.d.ts +22 -0
- package/dist/integrations/i18n.setup.js +38 -0
- package/dist/scripts/cli.d.ts +0 -1
- package/dist/scripts/cli.js +1 -0
- package/package.json +6 -5
- package/src/domain/interfaces/translation-service.interface.ts +2 -1
- package/src/index.ts +1 -0
- package/src/infrastructure/constants/index.ts +4 -0
- package/src/infrastructure/services/cli.service.ts +43 -34
- package/src/infrastructure/services/google-translate.service.ts +53 -33
- package/src/infrastructure/utils/file.util.ts +10 -2
- package/src/infrastructure/utils/rate-limit.util.ts +13 -5
- package/src/infrastructure/utils/text-validator.util.ts +5 -2
- package/src/integrations/i18n.setup.ts +71 -0
- package/src/scripts/cli.ts +11 -3
- package/dist/domain/entities/translation.entity.d.ts.map +0 -1
- package/dist/domain/interfaces/translation-service.interface.d.ts.map +0 -1
- package/dist/index.d.ts.map +0 -1
- package/dist/infrastructure/constants/index.d.ts.map +0 -1
- package/dist/infrastructure/services/cli.service.d.ts.map +0 -1
- package/dist/infrastructure/services/google-translate.service.d.ts.map +0 -1
- package/dist/infrastructure/utils/file.util.d.ts.map +0 -1
- package/dist/infrastructure/utils/rate-limit.util.d.ts.map +0 -1
- package/dist/infrastructure/utils/text-validator.util.d.ts.map +0 -1
- package/dist/scripts/cli.d.ts.map +0 -1
|
@@ -18,4 +18,3 @@ export interface ITranslationService {
|
|
|
18
18
|
translateBatch(requests: TranslationRequest[]): Promise<TranslationStats>;
|
|
19
19
|
translateObject(sourceObject: Record<string, unknown>, targetObject: Record<string, unknown>, targetLanguage: string, path?: string, stats?: TranslationStats, onTranslate?: (key: string, from: string, to: string) => void): Promise<void>;
|
|
20
20
|
}
|
|
21
|
-
//# sourceMappingURL=translation-service.interface.d.ts.map
|
package/dist/index.d.ts
CHANGED
|
@@ -8,4 +8,4 @@ export * from "./infrastructure/services/google-translate.service";
|
|
|
8
8
|
export * from "./infrastructure/constants";
|
|
9
9
|
export * from "./infrastructure/utils/text-validator.util";
|
|
10
10
|
export * from "./infrastructure/utils/rate-limit.util";
|
|
11
|
-
|
|
11
|
+
export * from "./integrations/i18n.setup";
|
package/dist/index.js
CHANGED
|
@@ -8,3 +8,4 @@ export * from "./infrastructure/services/google-translate.service";
|
|
|
8
8
|
export * from "./infrastructure/constants";
|
|
9
9
|
export * from "./infrastructure/utils/text-validator.util";
|
|
10
10
|
export * from "./infrastructure/utils/rate-limit.util";
|
|
11
|
+
export * from "./integrations/i18n.setup";
|
|
@@ -2,10 +2,10 @@ export interface SyncOptions {
|
|
|
2
2
|
localesDir?: string;
|
|
3
3
|
sourceDir?: string;
|
|
4
4
|
baseLang?: string;
|
|
5
|
+
force?: boolean;
|
|
5
6
|
}
|
|
6
7
|
export declare class CLIService {
|
|
7
8
|
sync(options?: SyncOptions): Promise<void>;
|
|
8
9
|
translate(options?: SyncOptions): Promise<void>;
|
|
9
10
|
}
|
|
10
11
|
export declare const cliService: CLIService;
|
|
11
|
-
//# sourceMappingURL=cli.service.d.ts.map
|
|
@@ -81,7 +81,7 @@ export class CLIService {
|
|
|
81
81
|
await googleTranslateService.translateObject(baseData, targetData, langCode.split("-")[0], // ISO 639-1
|
|
82
82
|
"", stats, (key, from, to) => {
|
|
83
83
|
process.stdout.write(chalk.gray(` • ${key}: ${from.substring(0, 15)}... → ${to.substring(0, 15)}...\r`));
|
|
84
|
-
});
|
|
84
|
+
}, options.force);
|
|
85
85
|
if (stats.successCount > 0) {
|
|
86
86
|
fs.writeFileSync(targetPath, generateTypeScriptContent(targetData, langCode));
|
|
87
87
|
console.log(chalk.green(` ✅ Successfully translated ${stats.successCount} keys.`));
|
|
@@ -12,9 +12,8 @@ declare class GoogleTranslateService implements ITranslationService {
|
|
|
12
12
|
private ensureInitialized;
|
|
13
13
|
translate(request: TranslationRequest): Promise<TranslationResponse>;
|
|
14
14
|
translateBatch(requests: TranslationRequest[]): Promise<TranslationStats>;
|
|
15
|
-
translateObject(sourceObject: Record<string, unknown>, targetObject: Record<string, unknown>, targetLanguage: string, path?: string, stats?: TranslationStats, onTranslate?: (key: string, from: string, to: string) => void): Promise<void>;
|
|
15
|
+
translateObject(sourceObject: Record<string, unknown>, targetObject: Record<string, unknown>, targetLanguage: string, path?: string, stats?: TranslationStats, onTranslate?: (key: string, from: string, to: string) => void, force?: boolean): Promise<void>;
|
|
16
16
|
private callTranslateAPI;
|
|
17
17
|
}
|
|
18
18
|
export declare const googleTranslateService: GoogleTranslateService;
|
|
19
19
|
export { GoogleTranslateService };
|
|
20
|
-
//# sourceMappingURL=google-translate.service.d.ts.map
|
|
@@ -118,7 +118,7 @@ class GoogleTranslateService {
|
|
|
118
118
|
failureCount: 0,
|
|
119
119
|
skippedCount: 0,
|
|
120
120
|
translatedKeys: [],
|
|
121
|
-
}, onTranslate) {
|
|
121
|
+
}, onTranslate, force = false) {
|
|
122
122
|
if (!sourceObject || typeof sourceObject !== "object")
|
|
123
123
|
return;
|
|
124
124
|
if (!targetObject || typeof targetObject !== "object")
|
|
@@ -135,11 +135,11 @@ class GoogleTranslateService {
|
|
|
135
135
|
if (!targetObject[key] || typeof targetObject[key] !== "object") {
|
|
136
136
|
targetObject[key] = {};
|
|
137
137
|
}
|
|
138
|
-
await this.translateObject(enValue, targetObject[key], targetLanguage, currentPath, stats, onTranslate);
|
|
138
|
+
await this.translateObject(enValue, targetObject[key], targetLanguage, currentPath, stats, onTranslate, force);
|
|
139
139
|
}
|
|
140
140
|
else if (typeof enValue === "string") {
|
|
141
141
|
stats.totalCount++;
|
|
142
|
-
if (needsTranslation(targetValue, enValue)) {
|
|
142
|
+
if (force || needsTranslation(targetValue, enValue)) {
|
|
143
143
|
textsToTranslate.push({ key, enValue, currentPath });
|
|
144
144
|
}
|
|
145
145
|
else {
|
|
@@ -161,10 +161,14 @@ class GoogleTranslateService {
|
|
|
161
161
|
const translatedItem = results.translatedKeys[resultIndex];
|
|
162
162
|
if (translatedItem && translatedItem.from === enValue && translatedItem.to !== enValue) {
|
|
163
163
|
targetObject[key] = translatedItem.to;
|
|
164
|
+
stats.successCount++;
|
|
164
165
|
if (onTranslate)
|
|
165
166
|
onTranslate(currentPath, enValue, translatedItem.to);
|
|
166
167
|
resultIndex++;
|
|
167
168
|
}
|
|
169
|
+
else {
|
|
170
|
+
stats.failureCount++;
|
|
171
|
+
}
|
|
168
172
|
}
|
|
169
173
|
}
|
|
170
174
|
}
|
|
@@ -7,4 +7,3 @@ export declare function parseTypeScriptFile(filePath: string): Record<string, un
|
|
|
7
7
|
* Generates a TypeScript file content from an object
|
|
8
8
|
*/
|
|
9
9
|
export declare function generateTypeScriptContent(obj: Record<string, unknown>, langCode?: string): string;
|
|
10
|
-
//# sourceMappingURL=file.util.d.ts.map
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import i18n from 'i18next';
|
|
2
|
+
export interface SetupI18nOptions {
|
|
3
|
+
resources: Record<string, {
|
|
4
|
+
translation: any;
|
|
5
|
+
}>;
|
|
6
|
+
defaultLng?: string;
|
|
7
|
+
fallbackLng?: string;
|
|
8
|
+
onInit?: (instance: typeof i18n) => void;
|
|
9
|
+
detection?: any;
|
|
10
|
+
seo?: {
|
|
11
|
+
titleKey: string;
|
|
12
|
+
descriptionKey: string;
|
|
13
|
+
defaultImage?: string;
|
|
14
|
+
twitterHandle?: string;
|
|
15
|
+
};
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Static i18n initialization to simplify main app code.
|
|
19
|
+
* @description All common configuration including SEO integration is hidden inside this package.
|
|
20
|
+
*/
|
|
21
|
+
export declare function setupI18n(options: SetupI18nOptions): import("i18next").i18n;
|
|
22
|
+
export default i18n;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
import i18n from 'i18next';
|
|
2
|
+
import { initReactI18next } from 'react-i18next';
|
|
3
|
+
import LanguageDetector from 'i18next-browser-languagedetector';
|
|
4
|
+
import { initSEO } from '@umituz/web-seo';
|
|
5
|
+
/**
|
|
6
|
+
* Static i18n initialization to simplify main app code.
|
|
7
|
+
* @description All common configuration including SEO integration is hidden inside this package.
|
|
8
|
+
*/
|
|
9
|
+
export function setupI18n(options) {
|
|
10
|
+
const { resources, defaultLng = 'en-US', fallbackLng = 'en-US', onInit, detection = {
|
|
11
|
+
order: ['localStorage', 'navigator'],
|
|
12
|
+
caches: ['localStorage'],
|
|
13
|
+
}, seo, } = options;
|
|
14
|
+
i18n
|
|
15
|
+
.use(LanguageDetector)
|
|
16
|
+
.use(initReactI18next)
|
|
17
|
+
.init({
|
|
18
|
+
resources,
|
|
19
|
+
lng: defaultLng,
|
|
20
|
+
fallbackLng,
|
|
21
|
+
interpolation: {
|
|
22
|
+
escapeValue: false,
|
|
23
|
+
},
|
|
24
|
+
detection,
|
|
25
|
+
})
|
|
26
|
+
.then(() => {
|
|
27
|
+
if (seo) {
|
|
28
|
+
initSEO({
|
|
29
|
+
i18n,
|
|
30
|
+
...seo,
|
|
31
|
+
});
|
|
32
|
+
}
|
|
33
|
+
if (onInit)
|
|
34
|
+
onInit(i18n);
|
|
35
|
+
});
|
|
36
|
+
return i18n;
|
|
37
|
+
}
|
|
38
|
+
export default i18n;
|
package/dist/scripts/cli.d.ts
CHANGED
package/dist/scripts/cli.js
CHANGED
|
@@ -29,6 +29,7 @@ program
|
|
|
29
29
|
.description("Automatically translate missing strings using Google Translate")
|
|
30
30
|
.option("-d, --locales-dir <dir>", "Directory containing locale files", "src/locales")
|
|
31
31
|
.option("-b, --base-lang <lang>", "Base language code", "en-US")
|
|
32
|
+
.option("-f, --force", "Force re-translation of all strings", false)
|
|
32
33
|
.action(async (options) => {
|
|
33
34
|
try {
|
|
34
35
|
await cliService.translate(options);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@umituz/web-localization",
|
|
3
|
-
"version": "1.1.
|
|
3
|
+
"version": "1.1.4",
|
|
4
4
|
"description": "Google Translate integrated localization package for web applications",
|
|
5
5
|
"main": "./src/index.ts",
|
|
6
6
|
"types": "./src/index.ts",
|
|
@@ -11,8 +11,7 @@
|
|
|
11
11
|
},
|
|
12
12
|
"exports": {
|
|
13
13
|
".": "./src/index.ts",
|
|
14
|
-
"./
|
|
15
|
-
"./utils": "./src/infrastructure/utils/index.ts",
|
|
14
|
+
"./setup": "./src/integrations/i18n.setup.ts",
|
|
16
15
|
"./package.json": "./package.json"
|
|
17
16
|
},
|
|
18
17
|
"scripts": {
|
|
@@ -37,8 +36,10 @@
|
|
|
37
36
|
"dependencies": {
|
|
38
37
|
"chalk": "^5.3.0",
|
|
39
38
|
"commander": "^12.0.0",
|
|
40
|
-
"
|
|
41
|
-
"
|
|
39
|
+
"i18next": "^23.11.2",
|
|
40
|
+
"react-i18next": "^14.1.1",
|
|
41
|
+
"i18next-browser-languagedetector": "^7.2.1",
|
|
42
|
+
"@umituz/web-seo": "file:../web-seo"
|
|
42
43
|
},
|
|
43
44
|
"devDependencies": {
|
|
44
45
|
"@types/node": "^20.12.7",
|
|
@@ -28,6 +28,7 @@ export interface ITranslationService {
|
|
|
28
28
|
targetLanguage: string,
|
|
29
29
|
path?: string,
|
|
30
30
|
stats?: TranslationStats,
|
|
31
|
-
onTranslate?: (key: string, from: string, to: string) => void
|
|
31
|
+
onTranslate?: (key: string, from: string, to: string) => void,
|
|
32
|
+
force?: boolean
|
|
32
33
|
): Promise<void>;
|
|
33
34
|
}
|
package/src/index.ts
CHANGED
|
@@ -9,3 +9,4 @@ export * from "./infrastructure/services/google-translate.service";
|
|
|
9
9
|
export * from "./infrastructure/constants";
|
|
10
10
|
export * from "./infrastructure/utils/text-validator.util";
|
|
11
11
|
export * from "./infrastructure/utils/rate-limit.util";
|
|
12
|
+
export * from "./integrations/i18n.setup";
|
|
@@ -11,3 +11,7 @@ export const DEFAULT_TIMEOUT = 10000;
|
|
|
11
11
|
export const DEFAULT_LOCALES_DIR = "src/locales";
|
|
12
12
|
export const DEFAULT_SOURCE_DIR = "src";
|
|
13
13
|
export const DEFAULT_BASE_LANGUAGE = "en-US";
|
|
14
|
+
|
|
15
|
+
// Rate limiter defaults
|
|
16
|
+
export const RATE_LIMIT_DEFAULT_DELAY = 100;
|
|
17
|
+
export const RATE_LIMIT_MIN_DELAY = 0;
|
|
@@ -1,19 +1,47 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
2
|
import path from "path";
|
|
3
3
|
import chalk from "chalk";
|
|
4
|
+
import type { TranslationStats } from "../../domain/entities/translation.entity.js";
|
|
4
5
|
import { googleTranslateService } from "./google-translate.service.js";
|
|
5
6
|
import { parseTypeScriptFile, generateTypeScriptContent } from "../utils/file.util.js";
|
|
6
|
-
import {
|
|
7
|
-
DEFAULT_LOCALES_DIR,
|
|
8
|
-
DEFAULT_BASE_LANGUAGE
|
|
7
|
+
import {
|
|
8
|
+
DEFAULT_LOCALES_DIR,
|
|
9
|
+
DEFAULT_BASE_LANGUAGE
|
|
9
10
|
} from "../constants/index.js";
|
|
10
11
|
|
|
11
12
|
export interface SyncOptions {
|
|
12
13
|
localesDir?: string;
|
|
13
14
|
sourceDir?: string;
|
|
14
15
|
baseLang?: string;
|
|
16
|
+
force?: boolean;
|
|
15
17
|
}
|
|
16
18
|
|
|
19
|
+
// Extracted outside loop for better performance
|
|
20
|
+
const syncObject = (
|
|
21
|
+
source: Record<string, unknown>,
|
|
22
|
+
target: Record<string, unknown>
|
|
23
|
+
): Record<string, unknown> => {
|
|
24
|
+
const result = { ...target };
|
|
25
|
+
for (const key in source) {
|
|
26
|
+
if (typeof source[key] === "object" && source[key] !== null) {
|
|
27
|
+
result[key] = syncObject(
|
|
28
|
+
source[key] as Record<string, unknown>,
|
|
29
|
+
(target[key] as Record<string, unknown>) || {}
|
|
30
|
+
);
|
|
31
|
+
} else if (target[key] === undefined) {
|
|
32
|
+
// Let empty string indicate untranslated state
|
|
33
|
+
result[key] = typeof source[key] === "string" ? "" : source[key];
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
// Remove extra keys
|
|
37
|
+
for (const key in target) {
|
|
38
|
+
if (source[key] === undefined) {
|
|
39
|
+
delete result[key];
|
|
40
|
+
}
|
|
41
|
+
}
|
|
42
|
+
return result;
|
|
43
|
+
};
|
|
44
|
+
|
|
17
45
|
export class CLIService {
|
|
18
46
|
async sync(options: SyncOptions = {}): Promise<void> {
|
|
19
47
|
const localesDir = path.resolve(process.cwd(), options.localesDir || DEFAULT_LOCALES_DIR);
|
|
@@ -32,7 +60,7 @@ export class CLIService {
|
|
|
32
60
|
|
|
33
61
|
const baseData = parseTypeScriptFile(baseLangPath);
|
|
34
62
|
const files = fs.readdirSync(localesDir)
|
|
35
|
-
.filter(f => f.match(/^[a-z]{2}-[A-Z]{2}
|
|
63
|
+
.filter(f => f.match(/^[a-z]{2}(-[A-Z]{2})?\.ts$/) && f !== `${baseLang}.ts`)
|
|
36
64
|
.sort();
|
|
37
65
|
|
|
38
66
|
console.log(chalk.blue(`📊 Found ${files.length} languages to sync with ${baseLang}.\n`));
|
|
@@ -40,30 +68,7 @@ export class CLIService {
|
|
|
40
68
|
for (const file of files) {
|
|
41
69
|
const targetPath = path.join(localesDir, file);
|
|
42
70
|
const targetData = parseTypeScriptFile(targetPath);
|
|
43
|
-
const langCode =
|
|
44
|
-
|
|
45
|
-
// Deep merge with base data structure
|
|
46
|
-
const syncObject = (source: Record<string, unknown>, target: Record<string, unknown>): Record<string, unknown> => {
|
|
47
|
-
const result = { ...target };
|
|
48
|
-
for (const key in source) {
|
|
49
|
-
if (typeof source[key] === "object" && source[key] !== null) {
|
|
50
|
-
result[key] = syncObject(
|
|
51
|
-
source[key] as Record<string, unknown>,
|
|
52
|
-
(target[key] as Record<string, unknown>) || {}
|
|
53
|
-
);
|
|
54
|
-
} else if (target[key] === undefined) {
|
|
55
|
-
// Let empty string indicate untranslated state
|
|
56
|
-
result[key] = typeof source[key] === "string" ? "" : source[key];
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
// Remove extra keys
|
|
60
|
-
for (const key in target) {
|
|
61
|
-
if (source[key] === undefined) {
|
|
62
|
-
delete result[key];
|
|
63
|
-
}
|
|
64
|
-
}
|
|
65
|
-
return result;
|
|
66
|
-
};
|
|
71
|
+
const langCode = path.basename(file, ".ts");
|
|
67
72
|
|
|
68
73
|
const syncedData = syncObject(baseData, targetData);
|
|
69
74
|
fs.writeFileSync(targetPath, generateTypeScriptContent(syncedData, langCode));
|
|
@@ -86,7 +91,7 @@ export class CLIService {
|
|
|
86
91
|
googleTranslateService.initialize({});
|
|
87
92
|
const baseData = parseTypeScriptFile(baseLangPath);
|
|
88
93
|
const files = fs.readdirSync(localesDir)
|
|
89
|
-
.filter(f => f.match(/^[a-z]{2}-[A-Z]{2}
|
|
94
|
+
.filter(f => f.match(/^[a-z]{2}(-[A-Z]{2})?\.ts$/) && f !== `${baseLang}.ts`)
|
|
90
95
|
.sort();
|
|
91
96
|
|
|
92
97
|
console.log(chalk.blue.bold(`🚀 Starting automatic translation for ${files.length} languages...\n`));
|
|
@@ -94,9 +99,9 @@ export class CLIService {
|
|
|
94
99
|
for (const file of files) {
|
|
95
100
|
const targetPath = path.join(localesDir, file);
|
|
96
101
|
const targetData = parseTypeScriptFile(targetPath);
|
|
97
|
-
const langCode =
|
|
98
|
-
|
|
99
|
-
const stats = {
|
|
102
|
+
const langCode = path.basename(file, ".ts");
|
|
103
|
+
|
|
104
|
+
const stats: TranslationStats = {
|
|
100
105
|
totalCount: 0,
|
|
101
106
|
successCount: 0,
|
|
102
107
|
failureCount: 0,
|
|
@@ -106,15 +111,19 @@ export class CLIService {
|
|
|
106
111
|
|
|
107
112
|
console.log(chalk.yellow(`🌍 Translating ${langCode}...`));
|
|
108
113
|
|
|
114
|
+
// Extract ISO 639-1 language code (e.g., "en" from "en-US")
|
|
115
|
+
const targetLang = langCode.includes("-") ? langCode.split("-")[0] : langCode;
|
|
116
|
+
|
|
109
117
|
await googleTranslateService.translateObject(
|
|
110
118
|
baseData,
|
|
111
119
|
targetData,
|
|
112
|
-
|
|
120
|
+
targetLang,
|
|
113
121
|
"",
|
|
114
122
|
stats,
|
|
115
123
|
(key, from, to) => {
|
|
116
124
|
process.stdout.write(chalk.gray(` • ${key}: ${from.substring(0, 15)}... → ${to.substring(0, 15)}...\r`));
|
|
117
|
-
}
|
|
125
|
+
},
|
|
126
|
+
options.force
|
|
118
127
|
);
|
|
119
128
|
|
|
120
129
|
if (stats.successCount > 0) {
|
|
@@ -22,11 +22,12 @@ import {
|
|
|
22
22
|
GOOGLE_TRANSLATE_API_URL,
|
|
23
23
|
DEFAULT_MIN_DELAY,
|
|
24
24
|
DEFAULT_TIMEOUT,
|
|
25
|
+
DEFAULT_MAX_RETRIES,
|
|
25
26
|
} from "../constants/index.js";
|
|
26
27
|
|
|
27
28
|
class GoogleTranslateService implements ITranslationService {
|
|
28
29
|
private config: TranslationServiceConfig | null = null;
|
|
29
|
-
private
|
|
30
|
+
private _rateLimiter: RateLimiter | null = null;
|
|
30
31
|
|
|
31
32
|
initialize(config: TranslationServiceConfig): void {
|
|
32
33
|
this.config = {
|
|
@@ -34,11 +35,18 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
34
35
|
timeout: DEFAULT_TIMEOUT,
|
|
35
36
|
...config,
|
|
36
37
|
};
|
|
37
|
-
this.
|
|
38
|
+
this._rateLimiter = new RateLimiter(this.config.minDelay);
|
|
38
39
|
}
|
|
39
40
|
|
|
40
41
|
isInitialized(): boolean {
|
|
41
|
-
return this.config !== null && this.
|
|
42
|
+
return this.config !== null && this._rateLimiter !== null;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
private get rateLimiter(): RateLimiter {
|
|
46
|
+
if (!this._rateLimiter) {
|
|
47
|
+
throw new Error("RateLimiter not initialized");
|
|
48
|
+
}
|
|
49
|
+
return this._rateLimiter;
|
|
42
50
|
}
|
|
43
51
|
|
|
44
52
|
private ensureInitialized(): void {
|
|
@@ -75,7 +83,7 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
75
83
|
};
|
|
76
84
|
}
|
|
77
85
|
|
|
78
|
-
await this.rateLimiter
|
|
86
|
+
await this.rateLimiter.waitForSlot();
|
|
79
87
|
|
|
80
88
|
try {
|
|
81
89
|
const translatedText = await this.callTranslateAPI(
|
|
@@ -127,9 +135,9 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
127
135
|
}
|
|
128
136
|
|
|
129
137
|
for (const chunk of chunks) {
|
|
130
|
-
const results = await Promise.
|
|
138
|
+
const results = await Promise.allSettled(
|
|
131
139
|
chunk.map(async (request) => {
|
|
132
|
-
await this.rateLimiter
|
|
140
|
+
await this.rateLimiter.waitForSlot();
|
|
133
141
|
return this.callTranslateAPI(
|
|
134
142
|
request.text,
|
|
135
143
|
request.targetLanguage,
|
|
@@ -140,19 +148,22 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
140
148
|
|
|
141
149
|
for (let i = 0; i < chunk.length; i++) {
|
|
142
150
|
const request = chunk[i];
|
|
143
|
-
const
|
|
144
|
-
|
|
145
|
-
if (
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
151
|
+
const result = results[i];
|
|
152
|
+
|
|
153
|
+
if (result.status === "fulfilled") {
|
|
154
|
+
const translatedText = result.value;
|
|
155
|
+
if (translatedText && translatedText !== request.text) {
|
|
156
|
+
stats.successCount++;
|
|
157
|
+
stats.translatedKeys.push({
|
|
158
|
+
key: request.text,
|
|
159
|
+
from: request.text,
|
|
160
|
+
to: translatedText,
|
|
161
|
+
});
|
|
162
|
+
} else {
|
|
163
|
+
stats.skippedCount++;
|
|
164
|
+
}
|
|
154
165
|
} else {
|
|
155
|
-
stats.
|
|
166
|
+
stats.failureCount++;
|
|
156
167
|
}
|
|
157
168
|
}
|
|
158
169
|
}
|
|
@@ -172,7 +183,8 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
172
183
|
skippedCount: 0,
|
|
173
184
|
translatedKeys: [],
|
|
174
185
|
},
|
|
175
|
-
onTranslate?: (key: string, from: string, to: string) => void
|
|
186
|
+
onTranslate?: (key: string, from: string, to: string) => void,
|
|
187
|
+
force = false
|
|
176
188
|
): Promise<void> {
|
|
177
189
|
if (!sourceObject || typeof sourceObject !== "object") return;
|
|
178
190
|
if (!targetObject || typeof targetObject !== "object") return;
|
|
@@ -196,11 +208,12 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
196
208
|
targetLanguage,
|
|
197
209
|
currentPath,
|
|
198
210
|
stats,
|
|
199
|
-
onTranslate
|
|
211
|
+
onTranslate,
|
|
212
|
+
force
|
|
200
213
|
);
|
|
201
214
|
} else if (typeof enValue === "string") {
|
|
202
215
|
stats.totalCount++;
|
|
203
|
-
if (needsTranslation(targetValue, enValue)) {
|
|
216
|
+
if (force || needsTranslation(targetValue, enValue)) {
|
|
204
217
|
textsToTranslate.push({key, enValue, currentPath});
|
|
205
218
|
} else {
|
|
206
219
|
stats.skippedCount++;
|
|
@@ -219,15 +232,22 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
219
232
|
}))
|
|
220
233
|
);
|
|
221
234
|
|
|
222
|
-
|
|
235
|
+
// Create a map for quick lookup of translations
|
|
236
|
+
const translationMap = new Map<string, string>();
|
|
237
|
+
for (const item of results.translatedKeys) {
|
|
238
|
+
translationMap.set(item.from, item.to);
|
|
239
|
+
}
|
|
240
|
+
|
|
223
241
|
for (let j = 0; j < batch.length; j++) {
|
|
224
242
|
const {key, enValue, currentPath} = batch[j];
|
|
225
|
-
const
|
|
226
|
-
|
|
227
|
-
if (
|
|
228
|
-
targetObject[key] =
|
|
229
|
-
|
|
230
|
-
|
|
243
|
+
const translatedText = translationMap.get(enValue);
|
|
244
|
+
|
|
245
|
+
if (translatedText && translatedText !== enValue) {
|
|
246
|
+
targetObject[key] = translatedText;
|
|
247
|
+
stats.successCount++;
|
|
248
|
+
if (onTranslate) onTranslate(currentPath, enValue, translatedText);
|
|
249
|
+
} else {
|
|
250
|
+
stats.failureCount++;
|
|
231
251
|
}
|
|
232
252
|
}
|
|
233
253
|
}
|
|
@@ -238,7 +258,7 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
238
258
|
text: string,
|
|
239
259
|
targetLanguage: string,
|
|
240
260
|
sourceLanguage: string,
|
|
241
|
-
retries =
|
|
261
|
+
retries = DEFAULT_MAX_RETRIES,
|
|
242
262
|
backoffMs = 2000
|
|
243
263
|
): Promise<string> {
|
|
244
264
|
// 1. Variable Protection (Extract {{variables}})
|
|
@@ -246,7 +266,7 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
246
266
|
let counter = 0;
|
|
247
267
|
|
|
248
268
|
// Find all {{something}} patterns
|
|
249
|
-
|
|
269
|
+
const safeText = text.replace(/\{\{([^}]+)\}\}/g, (match) => {
|
|
250
270
|
const placeholder = `_VAR${counter}_`; // Using a simple token less likely to be split
|
|
251
271
|
varMap.set(placeholder, match);
|
|
252
272
|
counter++;
|
|
@@ -257,7 +277,7 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
257
277
|
const encodedText = encodeURIComponent(safeText);
|
|
258
278
|
const url = `${GOOGLE_TRANSLATE_API_URL}?client=gtx&sl=${sourceLanguage}&tl=${targetLanguage}&dt=t&q=${encodedText}`;
|
|
259
279
|
|
|
260
|
-
for (let attempt = 0; attempt
|
|
280
|
+
for (let attempt = 0; attempt < retries; attempt++) {
|
|
261
281
|
const controller = new AbortController();
|
|
262
282
|
const timeoutId = setTimeout(() => controller.abort(), timeout);
|
|
263
283
|
|
|
@@ -268,7 +288,7 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
268
288
|
|
|
269
289
|
if (!response.ok) {
|
|
270
290
|
if (response.status === 429 || response.status >= 500) {
|
|
271
|
-
if (attempt < retries) {
|
|
291
|
+
if (attempt < retries - 1) {
|
|
272
292
|
clearTimeout(timeoutId);
|
|
273
293
|
// Exponential backoff
|
|
274
294
|
const delay = backoffMs * Math.pow(2, attempt);
|
|
@@ -289,7 +309,7 @@ class GoogleTranslateService implements ITranslationService {
|
|
|
289
309
|
data[0].length > 0 &&
|
|
290
310
|
typeof data[0][0][0] === "string"
|
|
291
311
|
) {
|
|
292
|
-
translatedStr = data[0].map((item:
|
|
312
|
+
translatedStr = data[0].map((item: unknown[]) => item[0] as string).join('');
|
|
293
313
|
}
|
|
294
314
|
|
|
295
315
|
// 2. Re-inject Variables
|
|
@@ -1,13 +1,21 @@
|
|
|
1
1
|
import fs from "fs";
|
|
2
|
+
import path from "path";
|
|
2
3
|
|
|
3
4
|
/**
|
|
4
5
|
* Parses a TypeScript file containing an object export
|
|
5
6
|
* @description Simplistic parser for 'export default { ... }' or 'export const data = { ... }'
|
|
7
|
+
* @security Note: Uses Function constructor - only use with trusted local files
|
|
6
8
|
*/
|
|
7
9
|
export function parseTypeScriptFile(filePath: string): Record<string, unknown> {
|
|
8
|
-
|
|
10
|
+
// Validate file path is within project directory
|
|
11
|
+
const resolvedPath = path.resolve(filePath);
|
|
12
|
+
if (!resolvedPath.startsWith(process.cwd())) {
|
|
13
|
+
throw new Error(`Security: File path outside project directory: ${filePath}`);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
if (!fs.existsSync(resolvedPath)) return {};
|
|
9
17
|
|
|
10
|
-
const content = fs.readFileSync(
|
|
18
|
+
const content = fs.readFileSync(resolvedPath, "utf-8");
|
|
11
19
|
|
|
12
20
|
// Extract the object part
|
|
13
21
|
// This is a naive implementation, but matches the pattern used in the project
|
|
@@ -3,23 +3,31 @@
|
|
|
3
3
|
* @description Controls the frequency of API requests
|
|
4
4
|
*/
|
|
5
5
|
|
|
6
|
+
import { RATE_LIMIT_DEFAULT_DELAY } from "../constants/index.js";
|
|
7
|
+
|
|
6
8
|
export class RateLimiter {
|
|
7
9
|
private lastRequestTime = 0;
|
|
8
|
-
private minDelay: number;
|
|
10
|
+
private readonly minDelay: number;
|
|
9
11
|
|
|
10
|
-
constructor(minDelay =
|
|
12
|
+
constructor(minDelay = RATE_LIMIT_DEFAULT_DELAY) {
|
|
13
|
+
if (minDelay < 0) {
|
|
14
|
+
throw new Error("minDelay must be non-negative");
|
|
15
|
+
}
|
|
11
16
|
this.minDelay = minDelay;
|
|
12
17
|
}
|
|
13
18
|
|
|
14
19
|
async waitForSlot(): Promise<void> {
|
|
15
20
|
const now = Date.now();
|
|
16
21
|
const elapsedTime = now - this.lastRequestTime;
|
|
17
|
-
|
|
22
|
+
|
|
18
23
|
if (elapsedTime < this.minDelay) {
|
|
19
24
|
const waitTime = this.minDelay - elapsedTime;
|
|
20
25
|
await new Promise(resolve => setTimeout(resolve, waitTime));
|
|
26
|
+
// Set to the time when we can make the next request
|
|
27
|
+
this.lastRequestTime = Date.now();
|
|
28
|
+
} else {
|
|
29
|
+
// No wait needed, update to current time
|
|
30
|
+
this.lastRequestTime = now;
|
|
21
31
|
}
|
|
22
|
-
|
|
23
|
-
this.lastRequestTime = Date.now();
|
|
24
32
|
}
|
|
25
33
|
}
|
|
@@ -2,6 +2,10 @@
|
|
|
2
2
|
* Text Validation Utilities
|
|
3
3
|
*/
|
|
4
4
|
|
|
5
|
+
// Default words/patterns to skip during translation
|
|
6
|
+
const DEFAULT_SKIPLIST = ["@umituz"] as const;
|
|
7
|
+
const skiplist = [...DEFAULT_SKIPLIST];
|
|
8
|
+
|
|
5
9
|
/**
|
|
6
10
|
* Validates if the text is suitable for translation
|
|
7
11
|
*/
|
|
@@ -16,14 +20,13 @@ export function isValidText(text: unknown): text is string {
|
|
|
16
20
|
* Checks if a word should be skipped (e.g., proper nouns, symbols)
|
|
17
21
|
*/
|
|
18
22
|
export function shouldSkipWord(text: string): boolean {
|
|
19
|
-
const skiplist = ["@umituz"];
|
|
20
23
|
return skiplist.some(word => text.includes(word));
|
|
21
24
|
}
|
|
22
25
|
|
|
23
26
|
/**
|
|
24
27
|
* Determines if a key needs translation
|
|
25
28
|
*/
|
|
26
|
-
export function needsTranslation(targetValue: unknown,
|
|
29
|
+
export function needsTranslation(targetValue: unknown, _sourceValue: string): boolean {
|
|
27
30
|
if (typeof targetValue !== "string") return true;
|
|
28
31
|
if (targetValue.length === 0) return true; // Empty string means untranslated
|
|
29
32
|
// Do NOT return true if target === source anymore, to avoid infinite translations for words that are identical in both languages
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import i18n from 'i18next';
|
|
2
|
+
import { initReactI18next } from 'react-i18next';
|
|
3
|
+
import LanguageDetector from 'i18next-browser-languagedetector';
|
|
4
|
+
import { initSEO } from '@umituz/web-seo';
|
|
5
|
+
|
|
6
|
+
export interface DetectionOptions {
|
|
7
|
+
order?: string[];
|
|
8
|
+
caches?: string[];
|
|
9
|
+
lookupLocalStorage?: string;
|
|
10
|
+
lookupSessionstorage?: string;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export interface SetupI18nOptions {
|
|
14
|
+
resources: Record<string, { translation: Record<string, unknown> }>;
|
|
15
|
+
defaultLng?: string;
|
|
16
|
+
fallbackLng?: string;
|
|
17
|
+
onInit?: (instance: typeof i18n) => void;
|
|
18
|
+
detection?: DetectionOptions;
|
|
19
|
+
seo?: {
|
|
20
|
+
titleKey: string;
|
|
21
|
+
descriptionKey: string;
|
|
22
|
+
defaultImage?: string;
|
|
23
|
+
twitterHandle?: string;
|
|
24
|
+
};
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* Static i18n initialization to simplify main app code.
|
|
29
|
+
* @description All common configuration including SEO integration is hidden inside this package.
|
|
30
|
+
*/
|
|
31
|
+
export function setupI18n(options: SetupI18nOptions): typeof i18n {
|
|
32
|
+
const {
|
|
33
|
+
resources,
|
|
34
|
+
defaultLng = 'en-US',
|
|
35
|
+
fallbackLng = 'en-US',
|
|
36
|
+
onInit,
|
|
37
|
+
detection = {
|
|
38
|
+
order: ['localStorage', 'navigator'],
|
|
39
|
+
caches: ['localStorage'],
|
|
40
|
+
},
|
|
41
|
+
seo,
|
|
42
|
+
} = options;
|
|
43
|
+
|
|
44
|
+
i18n
|
|
45
|
+
.use(LanguageDetector)
|
|
46
|
+
.use(initReactI18next)
|
|
47
|
+
.init({
|
|
48
|
+
resources,
|
|
49
|
+
lng: defaultLng,
|
|
50
|
+
fallbackLng,
|
|
51
|
+
interpolation: {
|
|
52
|
+
escapeValue: false,
|
|
53
|
+
},
|
|
54
|
+
detection,
|
|
55
|
+
})
|
|
56
|
+
.then(() => {
|
|
57
|
+
if (seo) {
|
|
58
|
+
initSEO({
|
|
59
|
+
i18n,
|
|
60
|
+
...seo,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
if (onInit) onInit(i18n);
|
|
64
|
+
})
|
|
65
|
+
.catch((error) => {
|
|
66
|
+
console.error('Failed to initialize i18n:', error);
|
|
67
|
+
throw error;
|
|
68
|
+
});
|
|
69
|
+
|
|
70
|
+
return i18n;
|
|
71
|
+
}
|
package/src/scripts/cli.ts
CHANGED
|
@@ -6,21 +6,28 @@
|
|
|
6
6
|
|
|
7
7
|
import { Command } from "commander";
|
|
8
8
|
import { cliService } from "../infrastructure/services/cli.service.js";
|
|
9
|
+
import type { SyncOptions } from "../infrastructure/services/cli.service.js";
|
|
9
10
|
import chalk from "chalk";
|
|
11
|
+
import { readFileSync } from "fs";
|
|
12
|
+
import { dirname, join } from "path";
|
|
13
|
+
import { fileURLToPath } from "url";
|
|
14
|
+
|
|
15
|
+
const __dirname = dirname(fileURLToPath(import.meta.url));
|
|
16
|
+
const packageJson = JSON.parse(readFileSync(join(__dirname, "../../package.json"), "utf-8"));
|
|
10
17
|
|
|
11
18
|
const program = new Command();
|
|
12
19
|
|
|
13
20
|
program
|
|
14
21
|
.name("web-loc")
|
|
15
22
|
.description("Localization CLI tool for web applications")
|
|
16
|
-
.version(
|
|
23
|
+
.version(packageJson.version);
|
|
17
24
|
|
|
18
25
|
program
|
|
19
26
|
.command("sync")
|
|
20
27
|
.description("Synchronize missing keys from base language to other languages")
|
|
21
28
|
.option("-d, --locales-dir <dir>", "Directory containing locale files", "src/locales")
|
|
22
29
|
.option("-b, --base-lang <lang>", "Base language code", "en-US")
|
|
23
|
-
.action(async (options) => {
|
|
30
|
+
.action(async (options: SyncOptions) => {
|
|
24
31
|
try {
|
|
25
32
|
await cliService.sync(options);
|
|
26
33
|
} catch (error) {
|
|
@@ -34,7 +41,8 @@ program
|
|
|
34
41
|
.description("Automatically translate missing strings using Google Translate")
|
|
35
42
|
.option("-d, --locales-dir <dir>", "Directory containing locale files", "src/locales")
|
|
36
43
|
.option("-b, --base-lang <lang>", "Base language code", "en-US")
|
|
37
|
-
.
|
|
44
|
+
.option("-f, --force", "Force re-translation of all strings", false)
|
|
45
|
+
.action(async (options: SyncOptions & { force?: boolean }) => {
|
|
38
46
|
try {
|
|
39
47
|
await cliService.translate(options);
|
|
40
48
|
} catch (error) {
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"translation.entity.d.ts","sourceRoot":"","sources":["../../../src/domain/entities/translation.entity.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,MAAM,WAAW,kBAAkB;IACjC,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,CAAC,EAAE,MAAM,CAAC;CAClC;AAED,MAAM,WAAW,mBAAmB;IAClC,QAAQ,CAAC,YAAY,EAAE,MAAM,CAAC;IAC9B,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,cAAc,EAAE,MAAM,CAAC;IAChC,QAAQ,CAAC,OAAO,EAAE,OAAO,CAAC;IAC1B,QAAQ,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,eAAe;IAC9B,QAAQ,CAAC,GAAG,EAAE,MAAM,CAAC;IACrB,QAAQ,CAAC,IAAI,EAAE,MAAM,CAAC;IACtB,QAAQ,CAAC,EAAE,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,gBAAgB;IAC/B,UAAU,EAAE,MAAM,CAAC;IACnB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,YAAY,EAAE,MAAM,CAAC;IACrB,cAAc,EAAE,eAAe,EAAE,CAAC;CACnC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"translation-service.interface.d.ts","sourceRoot":"","sources":["../../../src/domain/interfaces/translation-service.interface.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,gCAAgC,CAAC;AAExC;;GAEG;AACH,MAAM,WAAW,wBAAwB;IACvC,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AAED;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAClC,UAAU,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI,CAAC;IACnD,aAAa,IAAI,OAAO,CAAC;IACzB,SAAS,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC,CAAC;IACrE,cAAc,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC,CAAC;IAC1E,eAAe,CACb,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,cAAc,EAAE,MAAM,EACtB,IAAI,CAAC,EAAE,MAAM,EACb,KAAK,CAAC,EAAE,gBAAgB,EACxB,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,GAC5D,OAAO,CAAC,IAAI,CAAC,CAAC;CAClB"}
|
package/dist/index.d.ts.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,cAAc,sCAAsC,CAAC;AACrD,cAAc,mDAAmD,CAAC;AAClE,cAAc,oDAAoD,CAAC;AACnE,cAAc,4BAA4B,CAAC;AAC3C,cAAc,4CAA4C,CAAC;AAC3D,cAAc,wCAAwC,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/constants/index.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,eAAO,MAAM,wBAAwB,wDAAwD,CAAC;AAE9F,eAAO,MAAM,iBAAiB,MAAM,CAAC;AACrC,eAAO,MAAM,mBAAmB,IAAI,CAAC;AACrC,eAAO,MAAM,eAAe,QAAQ,CAAC;AAErC,eAAO,MAAM,mBAAmB,gBAAgB,CAAC;AACjD,eAAO,MAAM,kBAAkB,QAAQ,CAAC;AACxC,eAAO,MAAM,qBAAqB,UAAU,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.service.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/services/cli.service.ts"],"names":[],"mappings":"AAUA,MAAM,WAAW,WAAW;IAC1B,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,qBAAa,UAAU;IACf,IAAI,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;IA0D9C,SAAS,CAAC,OAAO,GAAE,WAAgB,GAAG,OAAO,CAAC,IAAI,CAAC;CAsD1D;AAED,eAAO,MAAM,UAAU,YAAmB,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"google-translate.service.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/services/google-translate.service.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,EACV,kBAAkB,EAClB,mBAAmB,EACnB,gBAAgB,EACjB,MAAM,6CAA6C,CAAC;AACrD,OAAO,KAAK,EACV,mBAAmB,EACnB,wBAAwB,EACzB,MAAM,0DAA0D,CAAC;AAalE,cAAM,sBAAuB,YAAW,mBAAmB;IACzD,OAAO,CAAC,MAAM,CAAyC;IACvD,OAAO,CAAC,WAAW,CAA4B;IAE/C,UAAU,CAAC,MAAM,EAAE,wBAAwB,GAAG,IAAI;IASlD,aAAa,IAAI,OAAO;IAIxB,OAAO,CAAC,iBAAiB;IAQnB,SAAS,CAAC,OAAO,EAAE,kBAAkB,GAAG,OAAO,CAAC,mBAAmB,CAAC;IAsDpE,cAAc,CAAC,QAAQ,EAAE,kBAAkB,EAAE,GAAG,OAAO,CAAC,gBAAgB,CAAC;IAyDzE,eAAe,CACnB,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACrC,cAAc,EAAE,MAAM,EACtB,IAAI,SAAK,EACT,KAAK,GAAE,gBAMN,EACD,WAAW,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,MAAM,KAAK,IAAI,GAC5D,OAAO,CAAC,IAAI,CAAC;YA6DF,gBAAgB;CAkF/B;AAED,eAAO,MAAM,sBAAsB,wBAA+B,CAAC;AACnE,OAAO,EAAE,sBAAsB,EAAE,CAAC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"file.util.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/utils/file.util.ts"],"names":[],"mappings":"AAEA;;;GAGG;AACH,wBAAgB,mBAAmB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAoB7E;AAED;;GAEG;AACH,wBAAgB,yBAAyB,CAAC,GAAG,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,QAAQ,CAAC,EAAE,MAAM,GAAG,MAAM,CAajG"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"rate-limit.util.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/utils/rate-limit.util.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,qBAAa,WAAW;IACtB,OAAO,CAAC,eAAe,CAAK;IAC5B,OAAO,CAAC,QAAQ,CAAS;gBAEb,QAAQ,SAAM;IAIpB,WAAW,IAAI,OAAO,CAAC,IAAI,CAAC;CAWnC"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"text-validator.util.d.ts","sourceRoot":"","sources":["../../../src/infrastructure/utils/text-validator.util.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;GAEG;AACH,wBAAgB,WAAW,CAAC,IAAI,EAAE,OAAO,GAAG,IAAI,IAAI,MAAM,CAKzD;AAED;;GAEG;AACH,wBAAgB,cAAc,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAGpD;AAED;;GAEG;AACH,wBAAgB,gBAAgB,CAAC,WAAW,EAAE,OAAO,EAAE,WAAW,EAAE,MAAM,GAAG,OAAO,CAKnF"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/scripts/cli.ts"],"names":[],"mappings":";AAEA;;GAEG"}
|