@umituz/web-localization 1.1.2 → 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.
Files changed (32) hide show
  1. package/dist/domain/entities/translation.entity.d.ts +0 -1
  2. package/dist/domain/interfaces/translation-service.interface.d.ts +0 -1
  3. package/dist/index.d.ts +0 -1
  4. package/dist/infrastructure/constants/index.d.ts +0 -1
  5. package/dist/infrastructure/services/cli.service.d.ts +0 -1
  6. package/dist/infrastructure/services/google-translate.service.d.ts +0 -1
  7. package/dist/infrastructure/utils/file.util.d.ts +0 -1
  8. package/dist/infrastructure/utils/rate-limit.util.d.ts +0 -1
  9. package/dist/infrastructure/utils/text-validator.util.d.ts +0 -1
  10. package/dist/integrations/i18n.setup.d.ts +0 -1
  11. package/dist/scripts/cli.d.ts +0 -1
  12. package/package.json +1 -5
  13. package/src/domain/interfaces/translation-service.interface.ts +2 -1
  14. package/src/infrastructure/constants/index.ts +4 -0
  15. package/src/infrastructure/services/cli.service.ts +40 -33
  16. package/src/infrastructure/services/google-translate.service.ts +44 -29
  17. package/src/infrastructure/utils/file.util.ts +10 -2
  18. package/src/infrastructure/utils/rate-limit.util.ts +13 -5
  19. package/src/infrastructure/utils/text-validator.util.ts +5 -2
  20. package/src/integrations/i18n.setup.ts +14 -5
  21. package/src/scripts/cli.ts +10 -3
  22. package/dist/domain/entities/translation.entity.d.ts.map +0 -1
  23. package/dist/domain/interfaces/translation-service.interface.d.ts.map +0 -1
  24. package/dist/index.d.ts.map +0 -1
  25. package/dist/infrastructure/constants/index.d.ts.map +0 -1
  26. package/dist/infrastructure/services/cli.service.d.ts.map +0 -1
  27. package/dist/infrastructure/services/google-translate.service.d.ts.map +0 -1
  28. package/dist/infrastructure/utils/file.util.d.ts.map +0 -1
  29. package/dist/infrastructure/utils/rate-limit.util.d.ts.map +0 -1
  30. package/dist/infrastructure/utils/text-validator.util.d.ts.map +0 -1
  31. package/dist/integrations/i18n.setup.d.ts.map +0 -1
  32. package/dist/scripts/cli.d.ts.map +0 -1
@@ -27,4 +27,3 @@ export interface TranslationStats {
27
27
  skippedCount: number;
28
28
  translatedKeys: TranslationItem[];
29
29
  }
30
- //# sourceMappingURL=translation.entity.d.ts.map
@@ -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
@@ -9,4 +9,3 @@ 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";
12
- //# sourceMappingURL=index.d.ts.map
@@ -8,4 +8,3 @@ export declare const DEFAULT_TIMEOUT = 10000;
8
8
  export declare const DEFAULT_LOCALES_DIR = "src/locales";
9
9
  export declare const DEFAULT_SOURCE_DIR = "src";
10
10
  export declare const DEFAULT_BASE_LANGUAGE = "en-US";
11
- //# sourceMappingURL=index.d.ts.map
@@ -9,4 +9,3 @@ export declare class CLIService {
9
9
  translate(options?: SyncOptions): Promise<void>;
10
10
  }
11
11
  export declare const cliService: CLIService;
12
- //# sourceMappingURL=cli.service.d.ts.map
@@ -17,4 +17,3 @@ declare class GoogleTranslateService implements ITranslationService {
17
17
  }
18
18
  export declare const googleTranslateService: GoogleTranslateService;
19
19
  export { GoogleTranslateService };
20
- //# sourceMappingURL=google-translate.service.d.ts.map
@@ -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
@@ -8,4 +8,3 @@ export declare class RateLimiter {
8
8
  constructor(minDelay?: number);
9
9
  waitForSlot(): Promise<void>;
10
10
  }
11
- //# sourceMappingURL=rate-limit.util.d.ts.map
@@ -13,4 +13,3 @@ export declare function shouldSkipWord(text: string): boolean;
13
13
  * Determines if a key needs translation
14
14
  */
15
15
  export declare function needsTranslation(targetValue: unknown, sourceValue: string): boolean;
16
- //# sourceMappingURL=text-validator.util.d.ts.map
@@ -20,4 +20,3 @@ export interface SetupI18nOptions {
20
20
  */
21
21
  export declare function setupI18n(options: SetupI18nOptions): import("i18next").i18n;
22
22
  export default i18n;
23
- //# sourceMappingURL=i18n.setup.d.ts.map
@@ -3,4 +3,3 @@
3
3
  * CLI Tool for @umituz/web-localization
4
4
  */
5
5
  export {};
6
- //# sourceMappingURL=cli.d.ts.map
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@umituz/web-localization",
3
- "version": "1.1.2",
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,6 @@
11
11
  },
12
12
  "exports": {
13
13
  ".": "./src/index.ts",
14
- "./services": "./src/infrastructure/services/index.ts",
15
- "./utils": "./src/infrastructure/utils/index.ts",
16
14
  "./setup": "./src/integrations/i18n.setup.ts",
17
15
  "./package.json": "./package.json"
18
16
  },
@@ -38,8 +36,6 @@
38
36
  "dependencies": {
39
37
  "chalk": "^5.3.0",
40
38
  "commander": "^12.0.0",
41
- "dotenv": "^16.4.5",
42
- "ts-morph": "^27.0.2",
43
39
  "i18next": "^23.11.2",
44
40
  "react-i18next": "^14.1.1",
45
41
  "i18next-browser-languagedetector": "^7.2.1",
@@ -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
  }
@@ -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,11 +1,12 @@
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 {
@@ -15,6 +16,32 @@ export interface SyncOptions {
15
16
  force?: boolean;
16
17
  }
17
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
+
18
45
  export class CLIService {
19
46
  async sync(options: SyncOptions = {}): Promise<void> {
20
47
  const localesDir = path.resolve(process.cwd(), options.localesDir || DEFAULT_LOCALES_DIR);
@@ -33,7 +60,7 @@ export class CLIService {
33
60
 
34
61
  const baseData = parseTypeScriptFile(baseLangPath);
35
62
  const files = fs.readdirSync(localesDir)
36
- .filter(f => f.match(/^[a-z]{2}-[A-Z]{2}\.ts$/) && f !== `${baseLang}.ts`)
63
+ .filter(f => f.match(/^[a-z]{2}(-[A-Z]{2})?\.ts$/) && f !== `${baseLang}.ts`)
37
64
  .sort();
38
65
 
39
66
  console.log(chalk.blue(`📊 Found ${files.length} languages to sync with ${baseLang}.\n`));
@@ -41,30 +68,7 @@ export class CLIService {
41
68
  for (const file of files) {
42
69
  const targetPath = path.join(localesDir, file);
43
70
  const targetData = parseTypeScriptFile(targetPath);
44
- const langCode = file.replace(".ts", "");
45
-
46
- // Deep merge with base data structure
47
- const syncObject = (source: Record<string, unknown>, target: Record<string, unknown>): Record<string, unknown> => {
48
- const result = { ...target };
49
- for (const key in source) {
50
- if (typeof source[key] === "object" && source[key] !== null) {
51
- result[key] = syncObject(
52
- source[key] as Record<string, unknown>,
53
- (target[key] as Record<string, unknown>) || {}
54
- );
55
- } else if (target[key] === undefined) {
56
- // Let empty string indicate untranslated state
57
- result[key] = typeof source[key] === "string" ? "" : source[key];
58
- }
59
- }
60
- // Remove extra keys
61
- for (const key in target) {
62
- if (source[key] === undefined) {
63
- delete result[key];
64
- }
65
- }
66
- return result;
67
- };
71
+ const langCode = path.basename(file, ".ts");
68
72
 
69
73
  const syncedData = syncObject(baseData, targetData);
70
74
  fs.writeFileSync(targetPath, generateTypeScriptContent(syncedData, langCode));
@@ -87,7 +91,7 @@ export class CLIService {
87
91
  googleTranslateService.initialize({});
88
92
  const baseData = parseTypeScriptFile(baseLangPath);
89
93
  const files = fs.readdirSync(localesDir)
90
- .filter(f => f.match(/^[a-z]{2}-[A-Z]{2}\.ts$/) && f !== `${baseLang}.ts`)
94
+ .filter(f => f.match(/^[a-z]{2}(-[A-Z]{2})?\.ts$/) && f !== `${baseLang}.ts`)
91
95
  .sort();
92
96
 
93
97
  console.log(chalk.blue.bold(`🚀 Starting automatic translation for ${files.length} languages...\n`));
@@ -95,9 +99,9 @@ export class CLIService {
95
99
  for (const file of files) {
96
100
  const targetPath = path.join(localesDir, file);
97
101
  const targetData = parseTypeScriptFile(targetPath);
98
- const langCode = file.replace(".ts", "");
99
-
100
- const stats = {
102
+ const langCode = path.basename(file, ".ts");
103
+
104
+ const stats: TranslationStats = {
101
105
  totalCount: 0,
102
106
  successCount: 0,
103
107
  failureCount: 0,
@@ -107,10 +111,13 @@ export class CLIService {
107
111
 
108
112
  console.log(chalk.yellow(`🌍 Translating ${langCode}...`));
109
113
 
114
+ // Extract ISO 639-1 language code (e.g., "en" from "en-US")
115
+ const targetLang = langCode.includes("-") ? langCode.split("-")[0] : langCode;
116
+
110
117
  await googleTranslateService.translateObject(
111
118
  baseData,
112
119
  targetData,
113
- langCode.split("-")[0], // ISO 639-1
120
+ targetLang,
114
121
  "",
115
122
  stats,
116
123
  (key, from, to) => {
@@ -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 rateLimiter: RateLimiter | null = null;
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.rateLimiter = new RateLimiter(this.config.minDelay);
38
+ this._rateLimiter = new RateLimiter(this.config.minDelay);
38
39
  }
39
40
 
40
41
  isInitialized(): boolean {
41
- return this.config !== null && this.rateLimiter !== null;
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!.waitForSlot();
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.all(
138
+ const results = await Promise.allSettled(
131
139
  chunk.map(async (request) => {
132
- await this.rateLimiter!.waitForSlot();
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 translatedText = results[i];
144
-
145
- if (translatedText && translatedText !== request.text) {
146
- stats.successCount++;
147
- stats.translatedKeys.push({
148
- key: request.text,
149
- from: request.text,
150
- to: translatedText,
151
- });
152
- } else if (!translatedText) {
153
- stats.failureCount++;
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.skippedCount++;
166
+ stats.failureCount++;
156
167
  }
157
168
  }
158
169
  }
@@ -221,16 +232,20 @@ class GoogleTranslateService implements ITranslationService {
221
232
  }))
222
233
  );
223
234
 
224
- let resultIndex = 0;
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
+
225
241
  for (let j = 0; j < batch.length; j++) {
226
242
  const {key, enValue, currentPath} = batch[j];
227
- const translatedItem = results.translatedKeys[resultIndex];
243
+ const translatedText = translationMap.get(enValue);
228
244
 
229
- if (translatedItem && translatedItem.from === enValue && translatedItem.to !== enValue) {
230
- targetObject[key] = translatedItem.to;
245
+ if (translatedText && translatedText !== enValue) {
246
+ targetObject[key] = translatedText;
231
247
  stats.successCount++;
232
- if (onTranslate) onTranslate(currentPath, enValue, translatedItem.to);
233
- resultIndex++;
248
+ if (onTranslate) onTranslate(currentPath, enValue, translatedText);
234
249
  } else {
235
250
  stats.failureCount++;
236
251
  }
@@ -243,7 +258,7 @@ class GoogleTranslateService implements ITranslationService {
243
258
  text: string,
244
259
  targetLanguage: string,
245
260
  sourceLanguage: string,
246
- retries = 3,
261
+ retries = DEFAULT_MAX_RETRIES,
247
262
  backoffMs = 2000
248
263
  ): Promise<string> {
249
264
  // 1. Variable Protection (Extract {{variables}})
@@ -251,7 +266,7 @@ class GoogleTranslateService implements ITranslationService {
251
266
  let counter = 0;
252
267
 
253
268
  // Find all {{something}} patterns
254
- let safeText = text.replace(/\{\{([^}]+)\}\}/g, (match) => {
269
+ const safeText = text.replace(/\{\{([^}]+)\}\}/g, (match) => {
255
270
  const placeholder = `_VAR${counter}_`; // Using a simple token less likely to be split
256
271
  varMap.set(placeholder, match);
257
272
  counter++;
@@ -262,7 +277,7 @@ class GoogleTranslateService implements ITranslationService {
262
277
  const encodedText = encodeURIComponent(safeText);
263
278
  const url = `${GOOGLE_TRANSLATE_API_URL}?client=gtx&sl=${sourceLanguage}&tl=${targetLanguage}&dt=t&q=${encodedText}`;
264
279
 
265
- for (let attempt = 0; attempt <= retries; attempt++) {
280
+ for (let attempt = 0; attempt < retries; attempt++) {
266
281
  const controller = new AbortController();
267
282
  const timeoutId = setTimeout(() => controller.abort(), timeout);
268
283
 
@@ -273,7 +288,7 @@ class GoogleTranslateService implements ITranslationService {
273
288
 
274
289
  if (!response.ok) {
275
290
  if (response.status === 429 || response.status >= 500) {
276
- if (attempt < retries) {
291
+ if (attempt < retries - 1) {
277
292
  clearTimeout(timeoutId);
278
293
  // Exponential backoff
279
294
  const delay = backoffMs * Math.pow(2, attempt);
@@ -294,7 +309,7 @@ class GoogleTranslateService implements ITranslationService {
294
309
  data[0].length > 0 &&
295
310
  typeof data[0][0][0] === "string"
296
311
  ) {
297
- translatedStr = data[0].map((item: any) => item[0]).join('');
312
+ translatedStr = data[0].map((item: unknown[]) => item[0] as string).join('');
298
313
  }
299
314
 
300
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
- if (!fs.existsSync(filePath)) return {};
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(filePath, "utf-8");
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 = 100) {
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, sourceValue: string): boolean {
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
@@ -3,12 +3,19 @@ import { initReactI18next } from 'react-i18next';
3
3
  import LanguageDetector from 'i18next-browser-languagedetector';
4
4
  import { initSEO } from '@umituz/web-seo';
5
5
 
6
+ export interface DetectionOptions {
7
+ order?: string[];
8
+ caches?: string[];
9
+ lookupLocalStorage?: string;
10
+ lookupSessionstorage?: string;
11
+ }
12
+
6
13
  export interface SetupI18nOptions {
7
- resources: Record<string, { translation: any }>;
14
+ resources: Record<string, { translation: Record<string, unknown> }>;
8
15
  defaultLng?: string;
9
16
  fallbackLng?: string;
10
17
  onInit?: (instance: typeof i18n) => void;
11
- detection?: any;
18
+ detection?: DetectionOptions;
12
19
  seo?: {
13
20
  titleKey: string;
14
21
  descriptionKey: string;
@@ -21,7 +28,7 @@ export interface SetupI18nOptions {
21
28
  * Static i18n initialization to simplify main app code.
22
29
  * @description All common configuration including SEO integration is hidden inside this package.
23
30
  */
24
- export function setupI18n(options: SetupI18nOptions) {
31
+ export function setupI18n(options: SetupI18nOptions): typeof i18n {
25
32
  const {
26
33
  resources,
27
34
  defaultLng = 'en-US',
@@ -54,9 +61,11 @@ export function setupI18n(options: SetupI18nOptions) {
54
61
  });
55
62
  }
56
63
  if (onInit) onInit(i18n);
64
+ })
65
+ .catch((error) => {
66
+ console.error('Failed to initialize i18n:', error);
67
+ throw error;
57
68
  });
58
69
 
59
70
  return i18n;
60
71
  }
61
-
62
- export default i18n;
@@ -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("1.0.0");
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) {
@@ -35,7 +42,7 @@ program
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)
38
- .action(async (options) => {
45
+ .action(async (options: SyncOptions & { force?: boolean }) => {
39
46
  try {
40
47
  await cliService.translate(options);
41
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"}
@@ -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;AACvD,cAAc,2BAA2B,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;IAClB,KAAK,CAAC,EAAE,OAAO,CAAC;CACjB;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;CAuD1D;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,EAC7D,KAAK,UAAQ,GACZ,OAAO,CAAC,IAAI,CAAC;YAiEF,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":"i18n.setup.d.ts","sourceRoot":"","sources":["../../src/integrations/i18n.setup.ts"],"names":[],"mappings":"AAAA,OAAO,IAAI,MAAM,SAAS,CAAC;AAK3B,MAAM,WAAW,gBAAgB;IAC/B,SAAS,EAAE,MAAM,CAAC,MAAM,EAAE;QAAE,WAAW,EAAE,GAAG,CAAA;KAAE,CAAC,CAAC;IAChD,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,MAAM,CAAC,EAAE,CAAC,QAAQ,EAAE,OAAO,IAAI,KAAK,IAAI,CAAC;IACzC,SAAS,CAAC,EAAE,GAAG,CAAC;IAChB,GAAG,CAAC,EAAE;QACJ,QAAQ,EAAE,MAAM,CAAC;QACjB,cAAc,EAAE,MAAM,CAAC;QACvB,YAAY,CAAC,EAAE,MAAM,CAAC;QACtB,aAAa,CAAC,EAAE,MAAM,CAAC;KACxB,CAAC;CACH;AAED;;;GAGG;AACH,wBAAgB,SAAS,CAAC,OAAO,EAAE,gBAAgB,0BAoClD;AAED,eAAe,IAAI,CAAC"}
@@ -1 +0,0 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/scripts/cli.ts"],"names":[],"mappings":";AAEA;;GAEG"}