@globalart/ownlate-nestjs-translator 1.0.1
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 +128 -0
- package/dist/index.cjs.map +1 -0
- package/dist/index.d.cts +46 -0
- package/dist/index.d.mts +46 -0
- package/dist/index.mjs +116 -0
- package/dist/index.mjs.map +1 -0
- package/package.json +67 -0
package/dist/index.cjs
ADDED
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let _nestjs_axios = require("@nestjs/axios");
|
|
3
|
+
let _nestjs_common = require("@nestjs/common");
|
|
4
|
+
let rxjs = require("rxjs");
|
|
5
|
+
//#region src/translator.module-definition.ts
|
|
6
|
+
const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE } = new _nestjs_common.ConfigurableModuleBuilder().setClassMethodName("forRoot").setExtras({ isGlobal: true }, (definition, extras) => ({
|
|
7
|
+
...definition,
|
|
8
|
+
global: extras.isGlobal
|
|
9
|
+
})).build();
|
|
10
|
+
//#endregion
|
|
11
|
+
//#region \0@oxc-project+runtime@0.132.0/helpers/decorateMetadata.js
|
|
12
|
+
function __decorateMetadata(k, v) {
|
|
13
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
14
|
+
}
|
|
15
|
+
//#endregion
|
|
16
|
+
//#region \0@oxc-project+runtime@0.132.0/helpers/decorateParam.js
|
|
17
|
+
function __decorateParam(paramIndex, decorator) {
|
|
18
|
+
return function(target, key) {
|
|
19
|
+
decorator(target, key, paramIndex);
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
//#endregion
|
|
23
|
+
//#region \0@oxc-project+runtime@0.132.0/helpers/decorate.js
|
|
24
|
+
function __decorate(decorators, target, key, desc) {
|
|
25
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
26
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
27
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
28
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
29
|
+
}
|
|
30
|
+
//#endregion
|
|
31
|
+
//#region src/translator.service.ts
|
|
32
|
+
var _ref, _TranslatorService;
|
|
33
|
+
const TRANSLATIONS_API_URL = "https://api.ownlate.com/public/v1/segments/translations-map";
|
|
34
|
+
const POLL_INTERVAL_MS = 300 * 1e3;
|
|
35
|
+
let TranslatorService = _TranslatorService = class TranslatorService {
|
|
36
|
+
options;
|
|
37
|
+
httpService;
|
|
38
|
+
translations = {};
|
|
39
|
+
pollTimer;
|
|
40
|
+
isLoading = false;
|
|
41
|
+
logger = new _nestjs_common.Logger(_TranslatorService.name);
|
|
42
|
+
constructor(options, httpService) {
|
|
43
|
+
this.options = options;
|
|
44
|
+
this.httpService = httpService;
|
|
45
|
+
}
|
|
46
|
+
async onModuleInit() {
|
|
47
|
+
await this.loadTranslations();
|
|
48
|
+
this.pollTimer = setInterval(() => {
|
|
49
|
+
this.refreshTranslations();
|
|
50
|
+
}, POLL_INTERVAL_MS);
|
|
51
|
+
}
|
|
52
|
+
onModuleDestroy() {
|
|
53
|
+
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
54
|
+
}
|
|
55
|
+
async translate(namespace, key, placeholders) {
|
|
56
|
+
const text = this.translations[namespace]?.[key] ?? key;
|
|
57
|
+
if (!placeholders) return text;
|
|
58
|
+
return Object.entries(placeholders).reduce((result, [placeholderKey, value]) => result.replace(new RegExp(`\\{\\{${placeholderKey}\\}\\}`, "g"), String(value)), text);
|
|
59
|
+
}
|
|
60
|
+
async refreshTranslations() {
|
|
61
|
+
if (this.isLoading) return;
|
|
62
|
+
try {
|
|
63
|
+
await this.loadTranslations();
|
|
64
|
+
} catch (error) {
|
|
65
|
+
this.logger.error(`Failed to refresh translations: ${error instanceof Error ? error.message : String(error)}`);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
async loadTranslations() {
|
|
69
|
+
this.isLoading = true;
|
|
70
|
+
try {
|
|
71
|
+
const headers = { Accept: "application/json" };
|
|
72
|
+
if (this.options.apiKey) headers.Authorization = `Bearer ${this.options.apiKey}`;
|
|
73
|
+
const { data } = await (0, rxjs.firstValueFrom)(this.httpService.get(TRANSLATIONS_API_URL, {
|
|
74
|
+
headers,
|
|
75
|
+
params: { projectId: this.options.projectId }
|
|
76
|
+
}));
|
|
77
|
+
if (this.isErrorResponse(data)) throw new Error(`Failed to load translations: ${data.message}`);
|
|
78
|
+
if (!this.isTranslationsResponse(data)) throw new Error("Failed to load translations: invalid response format");
|
|
79
|
+
this.translations = this.buildTranslations(data);
|
|
80
|
+
} finally {
|
|
81
|
+
this.isLoading = false;
|
|
82
|
+
}
|
|
83
|
+
}
|
|
84
|
+
isErrorResponse(data) {
|
|
85
|
+
return typeof data === "object" && data !== null && "statusCode" in data && "message" in data;
|
|
86
|
+
}
|
|
87
|
+
isTranslationsResponse(data) {
|
|
88
|
+
return typeof data === "object" && data !== null;
|
|
89
|
+
}
|
|
90
|
+
buildTranslations(data) {
|
|
91
|
+
const locale = this.options.locale ?? "en";
|
|
92
|
+
const filesMap = this.options.filesMap ?? {};
|
|
93
|
+
const result = {};
|
|
94
|
+
for (const [fileName, languages] of Object.entries(data)) {
|
|
95
|
+
const namespace = filesMap[fileName] ?? fileName.replace(/\.json$/, "");
|
|
96
|
+
result[namespace] = languages[locale] ?? {};
|
|
97
|
+
}
|
|
98
|
+
return result;
|
|
99
|
+
}
|
|
100
|
+
};
|
|
101
|
+
TranslatorService = _TranslatorService = __decorate([
|
|
102
|
+
(0, _nestjs_common.Injectable)(),
|
|
103
|
+
__decorateParam(0, (0, _nestjs_common.Inject)(MODULE_OPTIONS_TOKEN)),
|
|
104
|
+
__decorateMetadata("design:paramtypes", [Object, typeof (_ref = typeof _nestjs_axios.HttpService !== "undefined" && _nestjs_axios.HttpService) === "function" ? _ref : Object])
|
|
105
|
+
], TranslatorService);
|
|
106
|
+
//#endregion
|
|
107
|
+
//#region src/translator.module.ts
|
|
108
|
+
let TranslatorModule = class TranslatorModule extends ConfigurableModuleClass {};
|
|
109
|
+
TranslatorModule = __decorate([(0, _nestjs_common.Module)({
|
|
110
|
+
imports: [_nestjs_axios.HttpModule],
|
|
111
|
+
providers: [TranslatorService],
|
|
112
|
+
exports: [TranslatorService]
|
|
113
|
+
})], TranslatorModule);
|
|
114
|
+
//#endregion
|
|
115
|
+
Object.defineProperty(exports, "TranslatorModule", {
|
|
116
|
+
enumerable: true,
|
|
117
|
+
get: function() {
|
|
118
|
+
return TranslatorModule;
|
|
119
|
+
}
|
|
120
|
+
});
|
|
121
|
+
Object.defineProperty(exports, "TranslatorService", {
|
|
122
|
+
enumerable: true,
|
|
123
|
+
get: function() {
|
|
124
|
+
return TranslatorService;
|
|
125
|
+
}
|
|
126
|
+
});
|
|
127
|
+
|
|
128
|
+
//# sourceMappingURL=index.cjs.map
|
|
@@ -0,0 +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>> = {};\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 async translate(\n namespace: string,\n key: string,\n placeholders?: TranslationPlaceholders,\n ): Promise<string> {\n const namespaceTranslations = this.translations[namespace];\n const text = namespaceTranslations?.[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, string>> {\n const locale = this.options.locale ?? \"en\";\n const filesMap = this.options.filesMap ?? {};\n const result: Record<string, Record<string, string>> = {};\n\n for (const [fileName, languages] of Object.entries(data)) {\n const namespace =\n filesMap[fileName] ?? fileName.replace(/\\.json$/, \"\");\n result[namespace] = languages[locale] ?? {};\n }\n\n return result;\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":";;;;;AAGA,MAAa,EACX,yBACA,sBACA,iBACE,IAAIA,eAAAA,0BAAmD,EACxD,mBAAmB,SAAS,EAC5B,UAAU,EAAE,UAAU,KAAK,IAAI,YAAY,YAAY;CACtD,GAAG;CACH,QAAQ,OAAO;AACjB,EAAE,EACD,MAAM;;;;;;;;;;;;;;;;;;;;;;;;ACGT,MAAM,uBACJ;AACF,MAAM,mBAAmB,MAAS;AAG3B,IAAA,oBAAA,qBAAA,MAAM,kBAA2D;CAQnD;CACA;CARnB,eAA+D,CAAC;CAChE;CACA,YAAoB;CACpB,SAA0B,IAAIC,eAAAA,OAAAA,mBAAyB,IAAI;CAE3D,YACE,SAEA,aACA;EAFiB,KAAA,UAAA;EACA,KAAA,cAAA;CAChB;CAEH,MAAM,eAA8B;EAClC,MAAM,KAAK,iBAAiB;EAC5B,KAAK,YAAY,kBAAkB;GACjC,KAAU,oBAAoB;EAChC,GAAG,gBAAgB;CACrB;CAEA,kBAAwB;EACtB,IAAI,KAAK,WACP,cAAc,KAAK,SAAS;CAEhC;CAEA,MAAM,UACJ,WACA,KACA,cACiB;EAEjB,MAAM,OADwB,KAAK,aAAa,aACX,QAAQ;EAE7C,IAAI,CAAC,cACH,OAAO;EAGT,OAAO,OAAO,QAAQ,YAAY,EAAE,QACjC,QAAQ,CAAC,gBAAgB,WACxB,OAAO,QACL,IAAI,OAAO,SAAS,eAAe,SAAS,GAAG,GAC/C,OAAO,KAAK,CACd,GACF,IACF;CACF;CAEA,MAAc,sBAAqC;EACjD,IAAI,KAAK,WACP;EAGF,IAAI;GACF,MAAM,KAAK,iBAAiB;EAC9B,SAAS,OAAO;GACd,KAAK,OAAO,MACV,mCAAmC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC1F;EACF;CACF;CAEA,MAAc,mBAAkC;EAC9C,KAAK,YAAY;EAEjB,IAAI;GACF,MAAM,UAAkC,EACtC,QAAQ,mBACV;GAEA,IAAI,KAAK,QAAQ,QACf,QAAQ,gBAAgB,UAAU,KAAK,QAAQ;GAGjD,MAAM,EAAE,SAAS,OAAA,GAAA,KAAA,gBACf,KAAK,YAAY,IAAa,sBAAsB;IAClD;IACA,QAAQ,EACN,WAAW,KAAK,QAAQ,UAC1B;GACF,CAAC,CACH;GAEA,IAAI,KAAK,gBAAgB,IAAI,GAC3B,MAAM,IAAI,MAAM,gCAAgC,KAAK,SAAS;GAGhE,IAAI,CAAC,KAAK,uBAAuB,IAAI,GACnC,MAAM,IAAI,MAAM,sDAAsD;GAGxE,KAAK,eAAe,KAAK,kBAAkB,IAAI;EACjD,UAAU;GACR,KAAK,YAAY;EACnB;CACF;CAEA,gBACE,MACiD;EACjD,OACE,OAAO,SAAS,YAChB,SAAS,QACT,gBAAgB,QAChB,aAAa;CAEjB;CAEA,uBACE,MACqC;EACrC,OAAO,OAAO,SAAS,YAAY,SAAS;CAC9C;CAEA,kBACE,MACwC;EACxC,MAAM,SAAS,KAAK,QAAQ,UAAU;EACtC,MAAM,WAAW,KAAK,QAAQ,YAAY,CAAC;EAC3C,MAAM,SAAiD,CAAC;EAExD,KAAK,MAAM,CAAC,UAAU,cAAc,OAAO,QAAQ,IAAI,GAAG;GACxD,MAAM,YACJ,SAAS,aAAa,SAAS,QAAQ,WAAW,EAAE;GACtD,OAAO,aAAa,UAAU,WAAW,CAAC;EAC5C;EAEA,OAAO;CACT;AACF;;gCAjIY;+CAQA,oBAAoB,CAAA;;;;;AClBzB,IAAA,mBAAA,MAAM,yBAAyB,wBAAwB,CAAC;0DALvD;CACN,SAAS,CAACC,cAAAA,UAAU;CACpB,WAAW,CAAC,iBAAiB;CAC7B,SAAS,CAAC,iBAAiB;AAC7B,CAAC,CAAA,GAAA,gBAAA"}
|
package/dist/index.d.cts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { HttpService } from "@nestjs/axios";
|
|
2
|
+
import { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
|
3
|
+
|
|
4
|
+
//#region src/translator-module.options.d.ts
|
|
5
|
+
interface TranslatorModuleOptions {
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
locale?: string;
|
|
9
|
+
filesMap?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/translator.module-definition.d.ts
|
|
13
|
+
declare const ConfigurableModuleClass: import("@nestjs/common").ConfigurableModuleCls<TranslatorModuleOptions, "forRoot", "create", {
|
|
14
|
+
isGlobal: boolean;
|
|
15
|
+
}>, MODULE_OPTIONS_TOKEN: string | symbol, OPTIONS_TYPE: TranslatorModuleOptions & Partial<{
|
|
16
|
+
isGlobal: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/translator.module.d.ts
|
|
20
|
+
declare class TranslatorModule extends ConfigurableModuleClass {}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/translator.types.d.ts
|
|
23
|
+
type OwnlateTranslationsResponse = Record<string, Record<string, Record<string, string>>>;
|
|
24
|
+
type TranslationPlaceholders = Record<string, string | number>;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/translator.service.d.ts
|
|
27
|
+
declare class TranslatorService implements OnModuleInit, OnModuleDestroy {
|
|
28
|
+
private readonly options;
|
|
29
|
+
private readonly httpService;
|
|
30
|
+
private translations;
|
|
31
|
+
private pollTimer?;
|
|
32
|
+
private isLoading;
|
|
33
|
+
private readonly logger;
|
|
34
|
+
constructor(options: TranslatorModuleOptions, httpService: HttpService);
|
|
35
|
+
onModuleInit(): Promise<void>;
|
|
36
|
+
onModuleDestroy(): void;
|
|
37
|
+
translate(namespace: string, key: string, placeholders?: TranslationPlaceholders): Promise<string>;
|
|
38
|
+
private refreshTranslations;
|
|
39
|
+
private loadTranslations;
|
|
40
|
+
private isErrorResponse;
|
|
41
|
+
private isTranslationsResponse;
|
|
42
|
+
private buildTranslations;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { type OwnlateTranslationsResponse, type TranslationPlaceholders, TranslatorModule, type TranslatorModuleOptions, TranslatorService };
|
|
46
|
+
//# sourceMappingURL=index.d.cts.map
|
package/dist/index.d.mts
ADDED
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { HttpService } from "@nestjs/axios";
|
|
2
|
+
import { OnModuleDestroy, OnModuleInit } from "@nestjs/common";
|
|
3
|
+
|
|
4
|
+
//#region src/translator-module.options.d.ts
|
|
5
|
+
interface TranslatorModuleOptions {
|
|
6
|
+
apiKey?: string;
|
|
7
|
+
projectId: string;
|
|
8
|
+
locale?: string;
|
|
9
|
+
filesMap?: Record<string, string>;
|
|
10
|
+
}
|
|
11
|
+
//#endregion
|
|
12
|
+
//#region src/translator.module-definition.d.ts
|
|
13
|
+
declare const ConfigurableModuleClass: import("@nestjs/common").ConfigurableModuleCls<TranslatorModuleOptions, "forRoot", "create", {
|
|
14
|
+
isGlobal: boolean;
|
|
15
|
+
}>, MODULE_OPTIONS_TOKEN: string | symbol, OPTIONS_TYPE: TranslatorModuleOptions & Partial<{
|
|
16
|
+
isGlobal: boolean;
|
|
17
|
+
}>;
|
|
18
|
+
//#endregion
|
|
19
|
+
//#region src/translator.module.d.ts
|
|
20
|
+
declare class TranslatorModule extends ConfigurableModuleClass {}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region src/translator.types.d.ts
|
|
23
|
+
type OwnlateTranslationsResponse = Record<string, Record<string, Record<string, string>>>;
|
|
24
|
+
type TranslationPlaceholders = Record<string, string | number>;
|
|
25
|
+
//#endregion
|
|
26
|
+
//#region src/translator.service.d.ts
|
|
27
|
+
declare class TranslatorService implements OnModuleInit, OnModuleDestroy {
|
|
28
|
+
private readonly options;
|
|
29
|
+
private readonly httpService;
|
|
30
|
+
private translations;
|
|
31
|
+
private pollTimer?;
|
|
32
|
+
private isLoading;
|
|
33
|
+
private readonly logger;
|
|
34
|
+
constructor(options: TranslatorModuleOptions, httpService: HttpService);
|
|
35
|
+
onModuleInit(): Promise<void>;
|
|
36
|
+
onModuleDestroy(): void;
|
|
37
|
+
translate(namespace: string, key: string, placeholders?: TranslationPlaceholders): Promise<string>;
|
|
38
|
+
private refreshTranslations;
|
|
39
|
+
private loadTranslations;
|
|
40
|
+
private isErrorResponse;
|
|
41
|
+
private isTranslationsResponse;
|
|
42
|
+
private buildTranslations;
|
|
43
|
+
}
|
|
44
|
+
//#endregion
|
|
45
|
+
export { type OwnlateTranslationsResponse, type TranslationPlaceholders, TranslatorModule, type TranslatorModuleOptions, TranslatorService };
|
|
46
|
+
//# sourceMappingURL=index.d.mts.map
|
package/dist/index.mjs
ADDED
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
import { HttpModule, HttpService } from "@nestjs/axios";
|
|
2
|
+
import { ConfigurableModuleBuilder, Inject, Injectable, Logger, Module } from "@nestjs/common";
|
|
3
|
+
import { firstValueFrom } from "rxjs";
|
|
4
|
+
//#region src/translator.module-definition.ts
|
|
5
|
+
const { ConfigurableModuleClass, MODULE_OPTIONS_TOKEN, OPTIONS_TYPE } = new ConfigurableModuleBuilder().setClassMethodName("forRoot").setExtras({ isGlobal: true }, (definition, extras) => ({
|
|
6
|
+
...definition,
|
|
7
|
+
global: extras.isGlobal
|
|
8
|
+
})).build();
|
|
9
|
+
//#endregion
|
|
10
|
+
//#region \0@oxc-project+runtime@0.132.0/helpers/decorateMetadata.js
|
|
11
|
+
function __decorateMetadata(k, v) {
|
|
12
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
13
|
+
}
|
|
14
|
+
//#endregion
|
|
15
|
+
//#region \0@oxc-project+runtime@0.132.0/helpers/decorateParam.js
|
|
16
|
+
function __decorateParam(paramIndex, decorator) {
|
|
17
|
+
return function(target, key) {
|
|
18
|
+
decorator(target, key, paramIndex);
|
|
19
|
+
};
|
|
20
|
+
}
|
|
21
|
+
//#endregion
|
|
22
|
+
//#region \0@oxc-project+runtime@0.132.0/helpers/decorate.js
|
|
23
|
+
function __decorate(decorators, target, key, desc) {
|
|
24
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
25
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
26
|
+
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
27
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
28
|
+
}
|
|
29
|
+
//#endregion
|
|
30
|
+
//#region src/translator.service.ts
|
|
31
|
+
var _ref, _TranslatorService;
|
|
32
|
+
const TRANSLATIONS_API_URL = "https://api.ownlate.com/public/v1/segments/translations-map";
|
|
33
|
+
const POLL_INTERVAL_MS = 300 * 1e3;
|
|
34
|
+
let TranslatorService = _TranslatorService = class TranslatorService {
|
|
35
|
+
options;
|
|
36
|
+
httpService;
|
|
37
|
+
translations = {};
|
|
38
|
+
pollTimer;
|
|
39
|
+
isLoading = false;
|
|
40
|
+
logger = new Logger(_TranslatorService.name);
|
|
41
|
+
constructor(options, httpService) {
|
|
42
|
+
this.options = options;
|
|
43
|
+
this.httpService = httpService;
|
|
44
|
+
}
|
|
45
|
+
async onModuleInit() {
|
|
46
|
+
await this.loadTranslations();
|
|
47
|
+
this.pollTimer = setInterval(() => {
|
|
48
|
+
this.refreshTranslations();
|
|
49
|
+
}, POLL_INTERVAL_MS);
|
|
50
|
+
}
|
|
51
|
+
onModuleDestroy() {
|
|
52
|
+
if (this.pollTimer) clearInterval(this.pollTimer);
|
|
53
|
+
}
|
|
54
|
+
async translate(namespace, key, placeholders) {
|
|
55
|
+
const text = this.translations[namespace]?.[key] ?? key;
|
|
56
|
+
if (!placeholders) return text;
|
|
57
|
+
return Object.entries(placeholders).reduce((result, [placeholderKey, value]) => result.replace(new RegExp(`\\{\\{${placeholderKey}\\}\\}`, "g"), String(value)), text);
|
|
58
|
+
}
|
|
59
|
+
async refreshTranslations() {
|
|
60
|
+
if (this.isLoading) return;
|
|
61
|
+
try {
|
|
62
|
+
await this.loadTranslations();
|
|
63
|
+
} catch (error) {
|
|
64
|
+
this.logger.error(`Failed to refresh translations: ${error instanceof Error ? error.message : String(error)}`);
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
async loadTranslations() {
|
|
68
|
+
this.isLoading = true;
|
|
69
|
+
try {
|
|
70
|
+
const headers = { Accept: "application/json" };
|
|
71
|
+
if (this.options.apiKey) headers.Authorization = `Bearer ${this.options.apiKey}`;
|
|
72
|
+
const { data } = await firstValueFrom(this.httpService.get(TRANSLATIONS_API_URL, {
|
|
73
|
+
headers,
|
|
74
|
+
params: { projectId: this.options.projectId }
|
|
75
|
+
}));
|
|
76
|
+
if (this.isErrorResponse(data)) throw new Error(`Failed to load translations: ${data.message}`);
|
|
77
|
+
if (!this.isTranslationsResponse(data)) throw new Error("Failed to load translations: invalid response format");
|
|
78
|
+
this.translations = this.buildTranslations(data);
|
|
79
|
+
} finally {
|
|
80
|
+
this.isLoading = false;
|
|
81
|
+
}
|
|
82
|
+
}
|
|
83
|
+
isErrorResponse(data) {
|
|
84
|
+
return typeof data === "object" && data !== null && "statusCode" in data && "message" in data;
|
|
85
|
+
}
|
|
86
|
+
isTranslationsResponse(data) {
|
|
87
|
+
return typeof data === "object" && data !== null;
|
|
88
|
+
}
|
|
89
|
+
buildTranslations(data) {
|
|
90
|
+
const locale = this.options.locale ?? "en";
|
|
91
|
+
const filesMap = this.options.filesMap ?? {};
|
|
92
|
+
const result = {};
|
|
93
|
+
for (const [fileName, languages] of Object.entries(data)) {
|
|
94
|
+
const namespace = filesMap[fileName] ?? fileName.replace(/\.json$/, "");
|
|
95
|
+
result[namespace] = languages[locale] ?? {};
|
|
96
|
+
}
|
|
97
|
+
return result;
|
|
98
|
+
}
|
|
99
|
+
};
|
|
100
|
+
TranslatorService = _TranslatorService = __decorate([
|
|
101
|
+
Injectable(),
|
|
102
|
+
__decorateParam(0, Inject(MODULE_OPTIONS_TOKEN)),
|
|
103
|
+
__decorateMetadata("design:paramtypes", [Object, typeof (_ref = typeof HttpService !== "undefined" && HttpService) === "function" ? _ref : Object])
|
|
104
|
+
], TranslatorService);
|
|
105
|
+
//#endregion
|
|
106
|
+
//#region src/translator.module.ts
|
|
107
|
+
let TranslatorModule = class TranslatorModule extends ConfigurableModuleClass {};
|
|
108
|
+
TranslatorModule = __decorate([Module({
|
|
109
|
+
imports: [HttpModule],
|
|
110
|
+
providers: [TranslatorService],
|
|
111
|
+
exports: [TranslatorService]
|
|
112
|
+
})], TranslatorModule);
|
|
113
|
+
//#endregion
|
|
114
|
+
export { TranslatorModule, TranslatorService };
|
|
115
|
+
|
|
116
|
+
//# sourceMappingURL=index.mjs.map
|
|
@@ -0,0 +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>> = {};\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 async translate(\n namespace: string,\n key: string,\n placeholders?: TranslationPlaceholders,\n ): Promise<string> {\n const namespaceTranslations = this.translations[namespace];\n const text = namespaceTranslations?.[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, string>> {\n const locale = this.options.locale ?? \"en\";\n const filesMap = this.options.filesMap ?? {};\n const result: Record<string, Record<string, string>> = {};\n\n for (const [fileName, languages] of Object.entries(data)) {\n const namespace =\n filesMap[fileName] ?? fileName.replace(/\\.json$/, \"\");\n result[namespace] = languages[locale] ?? {};\n }\n\n return result;\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":";;;;AAGA,MAAa,EACX,yBACA,sBACA,iBACE,IAAI,0BAAmD,EACxD,mBAAmB,SAAS,EAC5B,UAAU,EAAE,UAAU,KAAK,IAAI,YAAY,YAAY;CACtD,GAAG;CACH,QAAQ,OAAO;AACjB,EAAE,EACD,MAAM;;;;;;;;;;;;;;;;;;;;;;;;ACGT,MAAM,uBACJ;AACF,MAAM,mBAAmB,MAAS;AAG3B,IAAA,oBAAA,qBAAA,MAAM,kBAA2D;CAQnD;CACA;CARnB,eAA+D,CAAC;CAChE;CACA,YAAoB;CACpB,SAA0B,IAAI,OAAA,mBAAyB,IAAI;CAE3D,YACE,SAEA,aACA;EAFiB,KAAA,UAAA;EACA,KAAA,cAAA;CAChB;CAEH,MAAM,eAA8B;EAClC,MAAM,KAAK,iBAAiB;EAC5B,KAAK,YAAY,kBAAkB;GACjC,KAAU,oBAAoB;EAChC,GAAG,gBAAgB;CACrB;CAEA,kBAAwB;EACtB,IAAI,KAAK,WACP,cAAc,KAAK,SAAS;CAEhC;CAEA,MAAM,UACJ,WACA,KACA,cACiB;EAEjB,MAAM,OADwB,KAAK,aAAa,aACX,QAAQ;EAE7C,IAAI,CAAC,cACH,OAAO;EAGT,OAAO,OAAO,QAAQ,YAAY,EAAE,QACjC,QAAQ,CAAC,gBAAgB,WACxB,OAAO,QACL,IAAI,OAAO,SAAS,eAAe,SAAS,GAAG,GAC/C,OAAO,KAAK,CACd,GACF,IACF;CACF;CAEA,MAAc,sBAAqC;EACjD,IAAI,KAAK,WACP;EAGF,IAAI;GACF,MAAM,KAAK,iBAAiB;EAC9B,SAAS,OAAO;GACd,KAAK,OAAO,MACV,mCAAmC,iBAAiB,QAAQ,MAAM,UAAU,OAAO,KAAK,GAC1F;EACF;CACF;CAEA,MAAc,mBAAkC;EAC9C,KAAK,YAAY;EAEjB,IAAI;GACF,MAAM,UAAkC,EACtC,QAAQ,mBACV;GAEA,IAAI,KAAK,QAAQ,QACf,QAAQ,gBAAgB,UAAU,KAAK,QAAQ;GAGjD,MAAM,EAAE,SAAS,MAAM,eACrB,KAAK,YAAY,IAAa,sBAAsB;IAClD;IACA,QAAQ,EACN,WAAW,KAAK,QAAQ,UAC1B;GACF,CAAC,CACH;GAEA,IAAI,KAAK,gBAAgB,IAAI,GAC3B,MAAM,IAAI,MAAM,gCAAgC,KAAK,SAAS;GAGhE,IAAI,CAAC,KAAK,uBAAuB,IAAI,GACnC,MAAM,IAAI,MAAM,sDAAsD;GAGxE,KAAK,eAAe,KAAK,kBAAkB,IAAI;EACjD,UAAU;GACR,KAAK,YAAY;EACnB;CACF;CAEA,gBACE,MACiD;EACjD,OACE,OAAO,SAAS,YAChB,SAAS,QACT,gBAAgB,QAChB,aAAa;CAEjB;CAEA,uBACE,MACqC;EACrC,OAAO,OAAO,SAAS,YAAY,SAAS;CAC9C;CAEA,kBACE,MACwC;EACxC,MAAM,SAAS,KAAK,QAAQ,UAAU;EACtC,MAAM,WAAW,KAAK,QAAQ,YAAY,CAAC;EAC3C,MAAM,SAAiD,CAAC;EAExD,KAAK,MAAM,CAAC,UAAU,cAAc,OAAO,QAAQ,IAAI,GAAG;GACxD,MAAM,YACJ,SAAS,aAAa,SAAS,QAAQ,WAAW,EAAE;GACtD,OAAO,aAAa,UAAU,WAAW,CAAC;EAC5C;EAEA,OAAO;CACT;AACF;;CAjIC,WAAW;oBAQP,OAAO,oBAAoB,CAAA;;;;;AClBzB,IAAA,mBAAA,MAAM,yBAAyB,wBAAwB,CAAC;+BAL9D,OAAO;CACN,SAAS,CAAC,UAAU;CACpB,WAAW,CAAC,iBAAiB;CAC7B,SAAS,CAAC,iBAAiB;AAC7B,CAAC,CAAA,GAAA,gBAAA"}
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@globalart/ownlate-nestjs-translator",
|
|
3
|
+
"version": "1.0.1",
|
|
4
|
+
"description": "",
|
|
5
|
+
"license": "ISC",
|
|
6
|
+
"type": "module",
|
|
7
|
+
"files": [
|
|
8
|
+
"dist"
|
|
9
|
+
],
|
|
10
|
+
"main": "./dist/index.cjs",
|
|
11
|
+
"module": "./dist/index.mjs",
|
|
12
|
+
"types": "./dist/index.d.cts",
|
|
13
|
+
"exports": {
|
|
14
|
+
".": {
|
|
15
|
+
"import": {
|
|
16
|
+
"types": "./dist/index.d.mts",
|
|
17
|
+
"default": "./dist/index.mjs"
|
|
18
|
+
},
|
|
19
|
+
"require": {
|
|
20
|
+
"types": "./dist/index.d.cts",
|
|
21
|
+
"default": "./dist/index.cjs"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
},
|
|
25
|
+
"scripts": {
|
|
26
|
+
"build": "tsdown",
|
|
27
|
+
"build:watch": "tsdown --watch",
|
|
28
|
+
"test": "echo \"Error: no test specified\" && exit 1",
|
|
29
|
+
"prepublishOnly": "npm run build",
|
|
30
|
+
"publish:dev": "npm publish --access public --tag dev",
|
|
31
|
+
"publish:npm": "release-it --config .release-it.json"
|
|
32
|
+
},
|
|
33
|
+
"keywords": [],
|
|
34
|
+
"author": "",
|
|
35
|
+
"repository": {
|
|
36
|
+
"type": "git",
|
|
37
|
+
"url": "git+https://github.com/GlobalArtInc/ownlate-nestjs-translator.git"
|
|
38
|
+
},
|
|
39
|
+
"packageManager": "pnpm@10.21.0",
|
|
40
|
+
"dependencies": {
|
|
41
|
+
"@nestjs/axios": "^4.0.1",
|
|
42
|
+
"@nestjs/common": "^11.0.1",
|
|
43
|
+
"@nestjs/core": "^11.0.1",
|
|
44
|
+
"@nestjs/platform-express": "^11.0.1",
|
|
45
|
+
"axios": "^1.16.1",
|
|
46
|
+
"reflect-metadata": "^0.2.2",
|
|
47
|
+
"rxjs": "^7.8.1"
|
|
48
|
+
},
|
|
49
|
+
"peerDependencies": {
|
|
50
|
+
"@nestjs/axios": "^4.0.0",
|
|
51
|
+
"@nestjs/common": "^10.0.0 || ^11.0.0",
|
|
52
|
+
"@nestjs/core": "^10.0.0 || ^11.0.0",
|
|
53
|
+
"@nestjs/platform-express": "^10.0.0 || ^11.0.0",
|
|
54
|
+
"axios": "^1.0.0",
|
|
55
|
+
"reflect-metadata": "^0.2.2",
|
|
56
|
+
"rxjs": "^7.8.0"
|
|
57
|
+
},
|
|
58
|
+
"devDependencies": {
|
|
59
|
+
"@types/node": "^25.9.1",
|
|
60
|
+
"release-it": "20.0.1",
|
|
61
|
+
"tsdown": "0.22.0",
|
|
62
|
+
"typescript": "^6.0.2"
|
|
63
|
+
},
|
|
64
|
+
"publishConfig": {
|
|
65
|
+
"access": "public"
|
|
66
|
+
}
|
|
67
|
+
}
|