@globalart/ownlate-nestjs-translator 1.0.1 → 1.0.2
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/index.cjs +14 -4
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +3 -2
- package/dist/index.d.mts +3 -2
- package/dist/index.mjs +14 -4
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/index.cjs
CHANGED
|
@@ -52,8 +52,10 @@ let TranslatorService = _TranslatorService = class TranslatorService {
|
|
|
52
52
|
onModuleDestroy() {
|
|
53
53
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
54
54
|
}
|
|
55
|
-
|
|
56
|
-
const
|
|
55
|
+
translate(namespace, key, placeholders, locale) {
|
|
56
|
+
const resolvedLocale = locale ?? this.options.locale;
|
|
57
|
+
const namespaceTranslations = this.translations[namespace];
|
|
58
|
+
const text = (namespaceTranslations ? this.resolveLocaleTranslations(namespaceTranslations, resolvedLocale) : void 0)?.[key] ?? key;
|
|
57
59
|
if (!placeholders) return text;
|
|
58
60
|
return Object.entries(placeholders).reduce((result, [placeholderKey, value]) => result.replace(new RegExp(`\\{\\{${placeholderKey}\\}\\}`, "g"), String(value)), text);
|
|
59
61
|
}
|
|
@@ -88,15 +90,23 @@ let TranslatorService = _TranslatorService = class TranslatorService {
|
|
|
88
90
|
return typeof data === "object" && data !== null;
|
|
89
91
|
}
|
|
90
92
|
buildTranslations(data) {
|
|
91
|
-
const locale = this.options.locale ?? "en";
|
|
92
93
|
const filesMap = this.options.filesMap ?? {};
|
|
93
94
|
const result = {};
|
|
94
95
|
for (const [fileName, languages] of Object.entries(data)) {
|
|
95
96
|
const namespace = filesMap[fileName] ?? fileName.replace(/\.json$/, "");
|
|
96
|
-
result[namespace] = languages
|
|
97
|
+
result[namespace] = languages;
|
|
97
98
|
}
|
|
98
99
|
return result;
|
|
99
100
|
}
|
|
101
|
+
resolveLocaleTranslations(languages, locale) {
|
|
102
|
+
if (locale) {
|
|
103
|
+
const localeTranslations = languages[locale];
|
|
104
|
+
if (localeTranslations) return localeTranslations;
|
|
105
|
+
}
|
|
106
|
+
const [firstLocale] = Object.keys(languages);
|
|
107
|
+
if (!firstLocale) return {};
|
|
108
|
+
return languages[firstLocale];
|
|
109
|
+
}
|
|
100
110
|
};
|
|
101
111
|
TranslatorService = _TranslatorService = __decorate([
|
|
102
112
|
(0, _nestjs_common.Injectable)(),
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["ConfigurableModuleBuilder","Logger","HttpModule"],"sources":["../src/translator.module-definition.ts","../src/translator.service.ts","../src/translator.module.ts"],"sourcesContent":["import { ConfigurableModuleBuilder } from \"@nestjs/common\";\nimport type { TranslatorModuleOptions } from \"./translator-module.options\";\n\nexport const {\n ConfigurableModuleClass,\n MODULE_OPTIONS_TOKEN,\n OPTIONS_TYPE,\n} = new ConfigurableModuleBuilder<TranslatorModuleOptions>()\n .setClassMethodName(\"forRoot\")\n .setExtras({ isGlobal: true }, (definition, extras) => ({\n ...definition,\n global: extras.isGlobal,\n }))\n .build();\n","import { HttpService } from \"@nestjs/axios\";\nimport {\n Inject,\n Injectable,\n Logger,\n OnModuleDestroy,\n OnModuleInit,\n} from \"@nestjs/common\";\nimport { firstValueFrom } from \"rxjs\";\nimport type { TranslatorModuleOptions } from \"./translator-module.options\";\nimport { MODULE_OPTIONS_TOKEN } from \"./translator.module-definition\";\nimport type {\n OwnlateTranslationsResponse,\n TranslationPlaceholders,\n} from \"./translator.types\";\n\nconst TRANSLATIONS_API_URL =\n \"https://api.ownlate.com/public/v1/segments/translations-map\";\nconst POLL_INTERVAL_MS = 5 * 60 * 1000;\n\n@Injectable()\nexport class TranslatorService implements OnModuleInit, OnModuleDestroy {\n private translations: Record<string, Record<string, string
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["ConfigurableModuleBuilder","Logger","HttpModule"],"sources":["../src/translator.module-definition.ts","../src/translator.service.ts","../src/translator.module.ts"],"sourcesContent":["import { ConfigurableModuleBuilder } from \"@nestjs/common\";\nimport type { TranslatorModuleOptions } from \"./translator-module.options\";\n\nexport const {\n ConfigurableModuleClass,\n MODULE_OPTIONS_TOKEN,\n OPTIONS_TYPE,\n} = new ConfigurableModuleBuilder<TranslatorModuleOptions>()\n .setClassMethodName(\"forRoot\")\n .setExtras({ isGlobal: true }, (definition, extras) => ({\n ...definition,\n global: extras.isGlobal,\n }))\n .build();\n","import { HttpService } from \"@nestjs/axios\";\nimport {\n Inject,\n Injectable,\n Logger,\n OnModuleDestroy,\n OnModuleInit,\n} from \"@nestjs/common\";\nimport { firstValueFrom } from \"rxjs\";\nimport type { TranslatorModuleOptions } from \"./translator-module.options\";\nimport { MODULE_OPTIONS_TOKEN } from \"./translator.module-definition\";\nimport type {\n OwnlateTranslationsResponse,\n TranslationPlaceholders,\n} from \"./translator.types\";\n\nconst TRANSLATIONS_API_URL =\n \"https://api.ownlate.com/public/v1/segments/translations-map\";\nconst POLL_INTERVAL_MS = 5 * 60 * 1000;\n\n@Injectable()\nexport class TranslatorService implements OnModuleInit, OnModuleDestroy {\n private translations: Record<\n string,\n Record<string, Record<string, string>>\n > = {};\n private pollTimer?: NodeJS.Timeout;\n private isLoading = false;\n private readonly logger = new Logger(TranslatorService.name);\n\n constructor(\n @Inject(MODULE_OPTIONS_TOKEN)\n private readonly options: TranslatorModuleOptions,\n private readonly httpService: HttpService,\n ) {}\n\n async onModuleInit(): Promise<void> {\n await this.loadTranslations();\n this.pollTimer = setInterval(() => {\n void this.refreshTranslations();\n }, POLL_INTERVAL_MS);\n }\n\n onModuleDestroy(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n }\n }\n\n translate(\n namespace: string,\n key: string,\n placeholders?: TranslationPlaceholders,\n locale?: string,\n ): string {\n const resolvedLocale = locale ?? this.options.locale;\n const namespaceTranslations = this.translations[namespace];\n const localeTranslations = namespaceTranslations\n ? this.resolveLocaleTranslations(namespaceTranslations, resolvedLocale)\n : undefined;\n const text = localeTranslations?.[key] ?? key;\n\n if (!placeholders) {\n return text;\n }\n\n return Object.entries(placeholders).reduce(\n (result, [placeholderKey, value]) =>\n result.replace(\n new RegExp(`\\\\{\\\\{${placeholderKey}\\\\}\\\\}`, \"g\"),\n String(value),\n ),\n text,\n );\n }\n\n private async refreshTranslations(): Promise<void> {\n if (this.isLoading) {\n return;\n }\n\n try {\n await this.loadTranslations();\n } catch (error) {\n this.logger.error(\n `Failed to refresh translations: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private async loadTranslations(): Promise<void> {\n this.isLoading = true;\n\n try {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n };\n\n if (this.options.apiKey) {\n headers.Authorization = `Bearer ${this.options.apiKey}`;\n }\n\n const { data } = await firstValueFrom(\n this.httpService.get<unknown>(TRANSLATIONS_API_URL, {\n headers,\n params: {\n projectId: this.options.projectId,\n },\n }),\n );\n\n if (this.isErrorResponse(data)) {\n throw new Error(`Failed to load translations: ${data.message}`);\n }\n\n if (!this.isTranslationsResponse(data)) {\n throw new Error(\"Failed to load translations: invalid response format\");\n }\n\n this.translations = this.buildTranslations(data);\n } finally {\n this.isLoading = false;\n }\n }\n\n private isErrorResponse(\n data: unknown,\n ): data is { statusCode: number; message: string } {\n return (\n typeof data === \"object\" &&\n data !== null &&\n \"statusCode\" in data &&\n \"message\" in data\n );\n }\n\n private isTranslationsResponse(\n data: unknown,\n ): data is OwnlateTranslationsResponse {\n return typeof data === \"object\" && data !== null;\n }\n\n private buildTranslations(\n data: OwnlateTranslationsResponse,\n ): Record<string, Record<string, Record<string, string>>> {\n const filesMap = this.options.filesMap ?? {};\n const result: Record<string, Record<string, Record<string, string>>> = {};\n\n for (const [fileName, languages] of Object.entries(data)) {\n const namespace = filesMap[fileName] ?? fileName.replace(/\\.json$/, \"\");\n result[namespace] = languages;\n }\n\n return result;\n }\n\n private resolveLocaleTranslations(\n languages: Record<string, Record<string, string>>,\n locale?: string,\n ): Record<string, string> {\n if (locale) {\n const localeTranslations = languages[locale];\n if (localeTranslations) {\n return localeTranslations;\n }\n }\n\n const [firstLocale] = Object.keys(languages);\n if (!firstLocale) {\n return {};\n }\n\n return languages[firstLocale];\n }\n}\n","import { HttpModule } from \"@nestjs/axios\";\nimport { Module } from \"@nestjs/common\";\nimport { ConfigurableModuleClass } from \"./translator.module-definition\";\nimport { TranslatorService } from \"./translator.service\";\n\n@Module({\n imports: [HttpModule],\n providers: [TranslatorService],\n exports: [TranslatorService],\n})\nexport class TranslatorModule extends ConfigurableModuleClass {}\n"],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -3,8 +3,8 @@ import { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
|
|
3
3
|
|
|
4
4
|
//#region src/translator-module.options.d.ts
|
|
5
5
|
interface TranslatorModuleOptions {
|
|
6
|
-
apiKey?: string;
|
|
7
6
|
projectId: string;
|
|
7
|
+
apiKey?: string;
|
|
8
8
|
locale?: string;
|
|
9
9
|
filesMap?: Record<string, string>;
|
|
10
10
|
}
|
|
@@ -34,12 +34,13 @@ declare class TranslatorService implements OnModuleInit, OnModuleDestroy {
|
|
|
34
34
|
constructor(options: TranslatorModuleOptions, httpService: HttpService);
|
|
35
35
|
onModuleInit(): Promise<void>;
|
|
36
36
|
onModuleDestroy(): void;
|
|
37
|
-
translate(namespace: string, key: string, placeholders?: TranslationPlaceholders):
|
|
37
|
+
translate(namespace: string, key: string, placeholders?: TranslationPlaceholders, locale?: string): string;
|
|
38
38
|
private refreshTranslations;
|
|
39
39
|
private loadTranslations;
|
|
40
40
|
private isErrorResponse;
|
|
41
41
|
private isTranslationsResponse;
|
|
42
42
|
private buildTranslations;
|
|
43
|
+
private resolveLocaleTranslations;
|
|
43
44
|
}
|
|
44
45
|
//#endregion
|
|
45
46
|
export { type OwnlateTranslationsResponse, type TranslationPlaceholders, TranslatorModule, type TranslatorModuleOptions, TranslatorService };
|
package/dist/index.d.mts
CHANGED
|
@@ -3,8 +3,8 @@ import { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
|
|
3
3
|
|
|
4
4
|
//#region src/translator-module.options.d.ts
|
|
5
5
|
interface TranslatorModuleOptions {
|
|
6
|
-
apiKey?: string;
|
|
7
6
|
projectId: string;
|
|
7
|
+
apiKey?: string;
|
|
8
8
|
locale?: string;
|
|
9
9
|
filesMap?: Record<string, string>;
|
|
10
10
|
}
|
|
@@ -34,12 +34,13 @@ declare class TranslatorService implements OnModuleInit, OnModuleDestroy {
|
|
|
34
34
|
constructor(options: TranslatorModuleOptions, httpService: HttpService);
|
|
35
35
|
onModuleInit(): Promise<void>;
|
|
36
36
|
onModuleDestroy(): void;
|
|
37
|
-
translate(namespace: string, key: string, placeholders?: TranslationPlaceholders):
|
|
37
|
+
translate(namespace: string, key: string, placeholders?: TranslationPlaceholders, locale?: string): string;
|
|
38
38
|
private refreshTranslations;
|
|
39
39
|
private loadTranslations;
|
|
40
40
|
private isErrorResponse;
|
|
41
41
|
private isTranslationsResponse;
|
|
42
42
|
private buildTranslations;
|
|
43
|
+
private resolveLocaleTranslations;
|
|
43
44
|
}
|
|
44
45
|
//#endregion
|
|
45
46
|
export { type OwnlateTranslationsResponse, type TranslationPlaceholders, TranslatorModule, type TranslatorModuleOptions, TranslatorService };
|
package/dist/index.mjs
CHANGED
|
@@ -51,8 +51,10 @@ let TranslatorService = _TranslatorService = class TranslatorService {
|
|
|
51
51
|
onModuleDestroy() {
|
|
52
52
|
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
53
53
|
}
|
|
54
|
-
|
|
55
|
-
const
|
|
54
|
+
translate(namespace, key, placeholders, locale) {
|
|
55
|
+
const resolvedLocale = locale ?? this.options.locale;
|
|
56
|
+
const namespaceTranslations = this.translations[namespace];
|
|
57
|
+
const text = (namespaceTranslations ? this.resolveLocaleTranslations(namespaceTranslations, resolvedLocale) : void 0)?.[key] ?? key;
|
|
56
58
|
if (!placeholders) return text;
|
|
57
59
|
return Object.entries(placeholders).reduce((result, [placeholderKey, value]) => result.replace(new RegExp(`\\{\\{${placeholderKey}\\}\\}`, "g"), String(value)), text);
|
|
58
60
|
}
|
|
@@ -87,15 +89,23 @@ let TranslatorService = _TranslatorService = class TranslatorService {
|
|
|
87
89
|
return typeof data === "object" && data !== null;
|
|
88
90
|
}
|
|
89
91
|
buildTranslations(data) {
|
|
90
|
-
const locale = this.options.locale ?? "en";
|
|
91
92
|
const filesMap = this.options.filesMap ?? {};
|
|
92
93
|
const result = {};
|
|
93
94
|
for (const [fileName, languages] of Object.entries(data)) {
|
|
94
95
|
const namespace = filesMap[fileName] ?? fileName.replace(/\.json$/, "");
|
|
95
|
-
result[namespace] = languages
|
|
96
|
+
result[namespace] = languages;
|
|
96
97
|
}
|
|
97
98
|
return result;
|
|
98
99
|
}
|
|
100
|
+
resolveLocaleTranslations(languages, locale) {
|
|
101
|
+
if (locale) {
|
|
102
|
+
const localeTranslations = languages[locale];
|
|
103
|
+
if (localeTranslations) return localeTranslations;
|
|
104
|
+
}
|
|
105
|
+
const [firstLocale] = Object.keys(languages);
|
|
106
|
+
if (!firstLocale) return {};
|
|
107
|
+
return languages[firstLocale];
|
|
108
|
+
}
|
|
99
109
|
};
|
|
100
110
|
TranslatorService = _TranslatorService = __decorate([
|
|
101
111
|
Injectable(),
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":[],"sources":["../src/translator.module-definition.ts","../src/translator.service.ts","../src/translator.module.ts"],"sourcesContent":["import { ConfigurableModuleBuilder } from \"@nestjs/common\";\nimport type { TranslatorModuleOptions } from \"./translator-module.options\";\n\nexport const {\n ConfigurableModuleClass,\n MODULE_OPTIONS_TOKEN,\n OPTIONS_TYPE,\n} = new ConfigurableModuleBuilder<TranslatorModuleOptions>()\n .setClassMethodName(\"forRoot\")\n .setExtras({ isGlobal: true }, (definition, extras) => ({\n ...definition,\n global: extras.isGlobal,\n }))\n .build();\n","import { HttpService } from \"@nestjs/axios\";\nimport {\n Inject,\n Injectable,\n Logger,\n OnModuleDestroy,\n OnModuleInit,\n} from \"@nestjs/common\";\nimport { firstValueFrom } from \"rxjs\";\nimport type { TranslatorModuleOptions } from \"./translator-module.options\";\nimport { MODULE_OPTIONS_TOKEN } from \"./translator.module-definition\";\nimport type {\n OwnlateTranslationsResponse,\n TranslationPlaceholders,\n} from \"./translator.types\";\n\nconst TRANSLATIONS_API_URL =\n \"https://api.ownlate.com/public/v1/segments/translations-map\";\nconst POLL_INTERVAL_MS = 5 * 60 * 1000;\n\n@Injectable()\nexport class TranslatorService implements OnModuleInit, OnModuleDestroy {\n private translations: Record<string, Record<string, string
|
|
1
|
+
{"version":3,"file":"index.mjs","names":[],"sources":["../src/translator.module-definition.ts","../src/translator.service.ts","../src/translator.module.ts"],"sourcesContent":["import { ConfigurableModuleBuilder } from \"@nestjs/common\";\nimport type { TranslatorModuleOptions } from \"./translator-module.options\";\n\nexport const {\n ConfigurableModuleClass,\n MODULE_OPTIONS_TOKEN,\n OPTIONS_TYPE,\n} = new ConfigurableModuleBuilder<TranslatorModuleOptions>()\n .setClassMethodName(\"forRoot\")\n .setExtras({ isGlobal: true }, (definition, extras) => ({\n ...definition,\n global: extras.isGlobal,\n }))\n .build();\n","import { HttpService } from \"@nestjs/axios\";\nimport {\n Inject,\n Injectable,\n Logger,\n OnModuleDestroy,\n OnModuleInit,\n} from \"@nestjs/common\";\nimport { firstValueFrom } from \"rxjs\";\nimport type { TranslatorModuleOptions } from \"./translator-module.options\";\nimport { MODULE_OPTIONS_TOKEN } from \"./translator.module-definition\";\nimport type {\n OwnlateTranslationsResponse,\n TranslationPlaceholders,\n} from \"./translator.types\";\n\nconst TRANSLATIONS_API_URL =\n \"https://api.ownlate.com/public/v1/segments/translations-map\";\nconst POLL_INTERVAL_MS = 5 * 60 * 1000;\n\n@Injectable()\nexport class TranslatorService implements OnModuleInit, OnModuleDestroy {\n private translations: Record<\n string,\n Record<string, Record<string, string>>\n > = {};\n private pollTimer?: NodeJS.Timeout;\n private isLoading = false;\n private readonly logger = new Logger(TranslatorService.name);\n\n constructor(\n @Inject(MODULE_OPTIONS_TOKEN)\n private readonly options: TranslatorModuleOptions,\n private readonly httpService: HttpService,\n ) {}\n\n async onModuleInit(): Promise<void> {\n await this.loadTranslations();\n this.pollTimer = setInterval(() => {\n void this.refreshTranslations();\n }, POLL_INTERVAL_MS);\n }\n\n onModuleDestroy(): void {\n if (this.pollTimer) {\n clearInterval(this.pollTimer);\n }\n }\n\n translate(\n namespace: string,\n key: string,\n placeholders?: TranslationPlaceholders,\n locale?: string,\n ): string {\n const resolvedLocale = locale ?? this.options.locale;\n const namespaceTranslations = this.translations[namespace];\n const localeTranslations = namespaceTranslations\n ? this.resolveLocaleTranslations(namespaceTranslations, resolvedLocale)\n : undefined;\n const text = localeTranslations?.[key] ?? key;\n\n if (!placeholders) {\n return text;\n }\n\n return Object.entries(placeholders).reduce(\n (result, [placeholderKey, value]) =>\n result.replace(\n new RegExp(`\\\\{\\\\{${placeholderKey}\\\\}\\\\}`, \"g\"),\n String(value),\n ),\n text,\n );\n }\n\n private async refreshTranslations(): Promise<void> {\n if (this.isLoading) {\n return;\n }\n\n try {\n await this.loadTranslations();\n } catch (error) {\n this.logger.error(\n `Failed to refresh translations: ${error instanceof Error ? error.message : String(error)}`,\n );\n }\n }\n\n private async loadTranslations(): Promise<void> {\n this.isLoading = true;\n\n try {\n const headers: Record<string, string> = {\n Accept: \"application/json\",\n };\n\n if (this.options.apiKey) {\n headers.Authorization = `Bearer ${this.options.apiKey}`;\n }\n\n const { data } = await firstValueFrom(\n this.httpService.get<unknown>(TRANSLATIONS_API_URL, {\n headers,\n params: {\n projectId: this.options.projectId,\n },\n }),\n );\n\n if (this.isErrorResponse(data)) {\n throw new Error(`Failed to load translations: ${data.message}`);\n }\n\n if (!this.isTranslationsResponse(data)) {\n throw new Error(\"Failed to load translations: invalid response format\");\n }\n\n this.translations = this.buildTranslations(data);\n } finally {\n this.isLoading = false;\n }\n }\n\n private isErrorResponse(\n data: unknown,\n ): data is { statusCode: number; message: string } {\n return (\n typeof data === \"object\" &&\n data !== null &&\n \"statusCode\" in data &&\n \"message\" in data\n );\n }\n\n private isTranslationsResponse(\n data: unknown,\n ): data is OwnlateTranslationsResponse {\n return typeof data === \"object\" && data !== null;\n }\n\n private buildTranslations(\n data: OwnlateTranslationsResponse,\n ): Record<string, Record<string, Record<string, string>>> {\n const filesMap = this.options.filesMap ?? {};\n const result: Record<string, Record<string, Record<string, string>>> = {};\n\n for (const [fileName, languages] of Object.entries(data)) {\n const namespace = filesMap[fileName] ?? fileName.replace(/\\.json$/, \"\");\n result[namespace] = languages;\n }\n\n return result;\n }\n\n private resolveLocaleTranslations(\n languages: Record<string, Record<string, string>>,\n locale?: string,\n ): Record<string, string> {\n if (locale) {\n const localeTranslations = languages[locale];\n if (localeTranslations) {\n return localeTranslations;\n }\n }\n\n const [firstLocale] = Object.keys(languages);\n if (!firstLocale) {\n return {};\n }\n\n return languages[firstLocale];\n }\n}\n","import { HttpModule } from \"@nestjs/axios\";\nimport { Module } from \"@nestjs/common\";\nimport { ConfigurableModuleClass } from \"./translator.module-definition\";\nimport { TranslatorService } from \"./translator.service\";\n\n@Module({\n imports: [HttpModule],\n providers: [TranslatorService],\n exports: [TranslatorService],\n})\nexport class TranslatorModule extends ConfigurableModuleClass {}\n"],"mappings":"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"}
|