@taon-dev/i18n 21.0.27 → 21.0.30

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 (83) hide show
  1. package/VERIFIED-BUILD-DATA.jsonc +2 -2
  2. package/bin/start.js +1 -1
  3. package/browser/fesm2022/i18n-browser.mjs +220 -35
  4. package/browser/fesm2022/i18n-browser.mjs.map +1 -1
  5. package/browser/package.json +1 -1
  6. package/browser/types/i18n-browser.d.ts +73 -11
  7. package/browser-prod/fesm2022/i18n-browser-prod.mjs +220 -35
  8. package/browser-prod/fesm2022/i18n-browser-prod.mjs.map +1 -1
  9. package/browser-prod/package.json +1 -1
  10. package/browser-prod/types/i18n-browser-prod.d.ts +73 -11
  11. package/html/assets/generated/pwa/html-pwa.html +53 -53
  12. package/lib/build-info._auto-generated_.d.ts +1 -1
  13. package/lib/build-info._auto-generated_.js +1 -1
  14. package/lib/index._auto-generated_.d.ts +1 -0
  15. package/lib/index._auto-generated_.js +1 -0
  16. package/lib/index._auto-generated_.js.map +1 -1
  17. package/lib/local-translation.d.ts +67 -0
  18. package/lib/local-translation.js +172 -0
  19. package/lib/local-translation.js.map +1 -0
  20. package/lib/package.json +1 -1
  21. package/lib/start-cli.d.ts +0 -5
  22. package/lib/start-cli.js +9 -39
  23. package/lib/start-cli.js.map +1 -1
  24. package/lib/utils-po-file-extracto-to-json.test.d.ts +1 -0
  25. package/lib/utils-po-file-extracto-to-json.test.js +147 -0
  26. package/lib/utils-po-file-extracto-to-json.test.js.map +1 -0
  27. package/lib/utils-po-file.js +26 -13
  28. package/lib/utils-po-file.js.map +1 -1
  29. package/lib/utils-po-file.test.js +2 -0
  30. package/lib/utils-po-file.test.js.map +1 -1
  31. package/lib-esm/app.electron.d.ts +1 -1
  32. package/lib-esm/app.electron.js +1 -1
  33. package/lib-esm/app.hosts.d.ts +3 -3
  34. package/lib-esm/app.hosts.js +8 -8
  35. package/lib-esm/app.vscode.d.ts +0 -9
  36. package/lib-esm/app.vscode.js +54 -36
  37. package/lib-esm/app.vscode.js.map +1 -1
  38. package/lib-esm/lib/build-info._auto-generated_.d.ts +1 -1
  39. package/lib-esm/lib/build-info._auto-generated_.js +1 -1
  40. package/lib-esm/lib/index._auto-generated_.d.ts +1 -0
  41. package/lib-esm/lib/index._auto-generated_.js +1 -0
  42. package/lib-esm/lib/index._auto-generated_.js.map +1 -1
  43. package/lib-esm/lib/local-translation.d.ts +67 -0
  44. package/lib-esm/lib/local-translation.js +168 -0
  45. package/lib-esm/lib/local-translation.js.map +1 -0
  46. package/lib-esm/lib/start-cli.d.ts +0 -5
  47. package/lib-esm/lib/start-cli.js +9 -39
  48. package/lib-esm/lib/start-cli.js.map +1 -1
  49. package/lib-esm/lib/utils-po-file-extracto-to-json.test.d.ts +1 -0
  50. package/lib-esm/lib/utils-po-file-extracto-to-json.test.js +145 -0
  51. package/lib-esm/lib/utils-po-file-extracto-to-json.test.js.map +1 -0
  52. package/lib-esm/lib/utils-po-file.js +26 -13
  53. package/lib-esm/lib/utils-po-file.js.map +1 -1
  54. package/lib-esm/lib/utils-po-file.test.js +2 -0
  55. package/lib-esm/lib/utils-po-file.test.js.map +1 -1
  56. package/lib-prod/build-info._auto-generated_.d.ts +1 -1
  57. package/lib-prod/build-info._auto-generated_.js +1 -1
  58. package/lib-prod/index._auto-generated_.d.ts +1 -0
  59. package/lib-prod/index._auto-generated_.js +1 -0
  60. package/lib-prod/index._auto-generated_.js.map +1 -1
  61. package/lib-prod/local-translation.d.ts +67 -0
  62. package/lib-prod/local-translation.js +168 -0
  63. package/lib-prod/local-translation.js.map +1 -0
  64. package/lib-prod/package.json +1 -1
  65. package/lib-prod/start-cli.d.ts +0 -5
  66. package/lib-prod/start-cli.js +9 -39
  67. package/lib-prod/start-cli.js.map +1 -1
  68. package/lib-prod/utils-po-file-extracto-to-json.test.d.ts +1 -0
  69. package/lib-prod/utils-po-file-extracto-to-json.test.js +145 -0
  70. package/lib-prod/utils-po-file-extracto-to-json.test.js.map +1 -0
  71. package/lib-prod/utils-po-file.js +26 -13
  72. package/lib-prod/utils-po-file.js.map +1 -1
  73. package/lib-prod/utils-po-file.test.js +2 -0
  74. package/lib-prod/utils-po-file.test.js.map +1 -1
  75. package/package.json +1 -1
  76. package/websql/fesm2022/i18n-websql.mjs +220 -35
  77. package/websql/fesm2022/i18n-websql.mjs.map +1 -1
  78. package/websql/package.json +1 -1
  79. package/websql/types/i18n-websql.d.ts +73 -11
  80. package/websql-prod/fesm2022/i18n-websql-prod.mjs +220 -35
  81. package/websql-prod/fesm2022/i18n-websql-prod.mjs.map +1 -1
  82. package/websql-prod/package.json +1 -1
  83. package/websql-prod/types/i18n-websql-prod.d.ts +73 -11
@@ -21,4 +21,4 @@ export declare const CURRENT_PACKAGE_TAON_VERSION = "v21";
21
21
  /**
22
22
  * Autogenerated by current cli tool. Use *tnp release* to bump version.
23
23
  */
24
- export declare const CURRENT_PACKAGE_VERSION = "21.0.27";
24
+ export declare const CURRENT_PACKAGE_VERSION = "21.0.30";
@@ -22,6 +22,6 @@ export const CURRENT_PACKAGE_TAON_VERSION = 'v21';
22
22
  /**
23
23
  * Autogenerated by current cli tool. Use *tnp release* to bump version.
24
24
  */
25
- export const CURRENT_PACKAGE_VERSION = '21.0.27';
25
+ export const CURRENT_PACKAGE_VERSION = '21.0.30';
26
26
  // THIS FILE IS GENERATED - DO NOT MODIFY
27
27
  //# sourceMappingURL=build-info._auto-generated_.js.map
@@ -1,4 +1,5 @@
1
1
  export * from './build-info._auto-generated_';
2
+ export * from './local-translation';
2
3
  export * from './start-cli';
3
4
  export * from './translation-manager';
4
5
  export * from './translation.models';
@@ -4,6 +4,7 @@
4
4
  // set property "shouldGenerateAutogenIndexFile: false"
5
5
  // in taon.jsonc of your project.
6
6
  export * from './build-info._auto-generated_';
7
+ export * from './local-translation';
7
8
  export * from './start-cli';
8
9
  /* */
9
10
  /* */
@@ -1 +1 @@
1
- {"version":3,"file":"index._auto-generated_.js","sourceRoot":"","sources":[""],"names":[],"mappings":"AAAA,cAAc;AACd,kEAAkE;AAClE,wCAAwC;AACxC,wDAAwD;AACxD,kCAAkC;AAClC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,aAAa,CAAC;AAC5B,KAAK;AACL,KAAK;AACL,KAAK;AACL,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,kDAAkD,CAAC;AACjE,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC"}
1
+ {"version":3,"file":"index._auto-generated_.js","sourceRoot":"","sources":[""],"names":[],"mappings":"AAAA,cAAc;AACd,kEAAkE;AAClE,wCAAwC;AACxC,wDAAwD;AACxD,kCAAkC;AAClC,cAAc,+BAA+B,CAAC;AAC9C,cAAc,qBAAqB,CAAC;AACpC,cAAc,aAAa,CAAC;AAC5B,KAAK;AACL,KAAK;AACL,KAAK;AACL,cAAc,uBAAuB,CAAC;AACtC,cAAc,sBAAsB,CAAC;AACrC,cAAc,eAAe,CAAC;AAC9B,cAAc,kDAAkD,CAAC;AACjE,cAAc,mBAAmB,CAAC;AAClC,cAAc,iBAAiB,CAAC;AAChC,cAAc,wBAAwB,CAAC"}
@@ -0,0 +1,67 @@
1
+ import { UtilsI18n__NS__CommonLocaleCode } from 'tnp-core/lib-prod';
2
+ export interface TranslationOptions {
3
+ text: string;
4
+ from: UtilsI18n__NS__CommonLocaleCode;
5
+ to: UtilsI18n__NS__CommonLocaleCode;
6
+ /**
7
+ * Nearby source-language text.
8
+ */
9
+ context?: string;
10
+ glossary?: Record<string, string>;
11
+ }
12
+ export interface AiDownloadProgress {
13
+ /**
14
+ * General loading stage reported by Transformers.js.
15
+ */
16
+ status: 'initiate' | 'download' | 'progress' | 'progress_total' | 'done' | 'ready' | string;
17
+ /**
18
+ * Current filename, when progress concerns an individual file.
19
+ */
20
+ file?: string;
21
+ /**
22
+ * Progress from 0 to 100.
23
+ */
24
+ progress?: number;
25
+ /**
26
+ * Downloaded bytes, when provided by Transformers.js.
27
+ */
28
+ loaded?: number;
29
+ /**
30
+ * Total bytes, when provided by Transformers.js.
31
+ */
32
+ total?: number;
33
+ }
34
+ export interface MakeSureAiDownloadedOptions {
35
+ onProgress?: (progress: AiDownloadProgress) => void;
36
+ /**
37
+ * Run one tiny translation after loading.
38
+ *
39
+ * This removes most of the delay from the first real translation,
40
+ * but it performs a small amount of additional inference work.
41
+ */
42
+ warmup?: boolean;
43
+ }
44
+ export declare class LocalTranslationService {
45
+ private translatorPromise?;
46
+ private aiReady;
47
+ private warmupPromise?;
48
+ private progressListeners;
49
+ /**
50
+ * Downloads, caches and loads the translation model.
51
+ *
52
+ * Multiple simultaneous calls share the same initialization promise.
53
+ * After this resolves, translate() reuses the already loaded model.
54
+ */
55
+ makeSureAiDownloaded(options?: MakeSureAiDownloadedOptions): Promise<void>;
56
+ /**
57
+ * True only in this process after pipeline initialization succeeded.
58
+ */
59
+ isAiReady(): boolean;
60
+ private getTranslator;
61
+ private createTranslator;
62
+ private emitProgress;
63
+ private warmupTranslator;
64
+ translate(options: TranslationOptions): Promise<string>;
65
+ private protectGlossary;
66
+ private getModelsCachePath;
67
+ }
@@ -0,0 +1,168 @@
1
+ import { mkdirSync } from 'node:fs';
2
+ import { crossPlatformPath, dotTaonFolder, ___NS__first, UtilsOs__NS__getRealHomeDir } from 'tnp-core/lib-prod';
3
+ const MODEL_ID = 'Xenova/nllb-200-distilled-600M';
4
+ const nllbLanguageCodes = {
5
+ 'en-US': 'eng_Latn',
6
+ 'pl-PL': 'pol_Latn',
7
+ 'de-DE': 'deu_Latn',
8
+ };
9
+ export class LocalTranslationService {
10
+ translatorPromise;
11
+ aiReady = false;
12
+ warmupPromise;
13
+ progressListeners = new Set();
14
+ /**
15
+ * Downloads, caches and loads the translation model.
16
+ *
17
+ * Multiple simultaneous calls share the same initialization promise.
18
+ * After this resolves, translate() reuses the already loaded model.
19
+ */
20
+ async makeSureAiDownloaded(options = {}) {
21
+ //#region @backendFunc
22
+ const { onProgress, warmup = false } = options;
23
+ if (onProgress) {
24
+ this.progressListeners.add(onProgress);
25
+ }
26
+ try {
27
+ const translator = await this.getTranslator();
28
+ if (warmup) {
29
+ await this.warmupTranslator(translator);
30
+ }
31
+ }
32
+ finally {
33
+ if (onProgress) {
34
+ this.progressListeners.delete(onProgress);
35
+ }
36
+ }
37
+ //#endregion
38
+ }
39
+ /**
40
+ * True only in this process after pipeline initialization succeeded.
41
+ */
42
+ isAiReady() {
43
+ return this.aiReady;
44
+ }
45
+ getTranslator() {
46
+ this.translatorPromise ??= this.createTranslator().catch(error => {
47
+ // Allow a later call to retry after a network or disk failure.
48
+ this.translatorPromise = undefined;
49
+ this.aiReady = false;
50
+ throw error;
51
+ });
52
+ return this.translatorPromise;
53
+ }
54
+ async createTranslator() {
55
+ //#region @backendFunc
56
+ const { env, pipeline } = await import('@huggingface/transformers');
57
+ env.cacheDir = this.getModelsCachePath();
58
+ const translator = await pipeline('translation', MODEL_ID, {
59
+ dtype: 'q8',
60
+ progress_callback: (progress) => {
61
+ this.emitProgress(progress);
62
+ },
63
+ });
64
+ this.aiReady = true;
65
+ this.emitProgress({
66
+ status: 'ready',
67
+ progress: 100,
68
+ });
69
+ return translator;
70
+ //#endregion
71
+ }
72
+ emitProgress(progress) {
73
+ for (const listener of this.progressListeners) {
74
+ try {
75
+ listener(progress);
76
+ }
77
+ catch {
78
+ // A UI callback must not break model initialization.
79
+ }
80
+ }
81
+ }
82
+ warmupTranslator(translator) {
83
+ this.warmupPromise ??= translator('Hello.', {
84
+ src_lang: 'eng_Latn',
85
+ tgt_lang: 'pol_Latn',
86
+ max_new_tokens: 16,
87
+ })
88
+ .then(() => undefined)
89
+ .catch(error => {
90
+ this.warmupPromise = undefined;
91
+ throw error;
92
+ });
93
+ return this.warmupPromise;
94
+ }
95
+ async translate(options) {
96
+ //#region @backendFunc
97
+ let { text, context, from, to, glossary } = options;
98
+ const fromOrg = from;
99
+ const toOrg = to;
100
+ from = ___NS__first(from.split('-'));
101
+ to = ___NS__first(to.split('-'));
102
+ if (!text.trim() || from === to) {
103
+ return text;
104
+ }
105
+ const srcLang = nllbLanguageCodes[fromOrg];
106
+ const tgtLang = nllbLanguageCodes[toOrg];
107
+ if (!srcLang) {
108
+ throw new Error(`Unsupported source translation locale: ${fromOrg}`);
109
+ }
110
+ if (!tgtLang) {
111
+ throw new Error(`Unsupported target translation locale: ${toOrg}`);
112
+ }
113
+ const input = context?.trim()
114
+ ? `${context.trim()}\n\n${text.trim()}`
115
+ : text;
116
+ const protectedInput = this.protectGlossary(input, glossary);
117
+ const translator = await this.getTranslator();
118
+ const result = await translator(protectedInput.preparedText, {
119
+ src_lang: srcLang,
120
+ tgt_lang: tgtLang,
121
+ });
122
+ const translatedText = result[0]?.translation_text ?? '';
123
+ return protectedInput.restore(translatedText);
124
+ //#endregion
125
+ }
126
+ protectGlossary(text, glossary) {
127
+ if (!glossary || Object.keys(glossary).length === 0) {
128
+ return {
129
+ preparedText: text,
130
+ restore: translatedText => translatedText,
131
+ };
132
+ }
133
+ const replacements = [];
134
+ let preparedText = text;
135
+ Object.entries(glossary)
136
+ .sort(([left], [right]) => right.length - left.length)
137
+ .forEach(([sourceValue, translatedValue], index) => {
138
+ const placeholder = `TAONTERM${index}PLACEHOLDER`;
139
+ preparedText = preparedText.replaceAll(sourceValue, placeholder);
140
+ replacements.push({
141
+ placeholder,
142
+ translatedValue,
143
+ });
144
+ });
145
+ return {
146
+ preparedText,
147
+ restore(translatedText) {
148
+ let result = translatedText;
149
+ for (const replacement of replacements) {
150
+ result = result.replaceAll(replacement.placeholder, replacement.translatedValue);
151
+ }
152
+ return result;
153
+ },
154
+ };
155
+ }
156
+ getModelsCachePath() {
157
+ const cachePath = crossPlatformPath([
158
+ UtilsOs__NS__getRealHomeDir(),
159
+ dotTaonFolder,
160
+ 'ai-models-cache',
161
+ ]);
162
+ mkdirSync(cachePath, {
163
+ recursive: true,
164
+ });
165
+ return cachePath;
166
+ }
167
+ }
168
+ //# sourceMappingURL=local-translation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"local-translation.js","sourceRoot":"","sources":[""],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEpC,OAAO,EAAE,iBAAiB,EAAE,aAAa,EAA+lC,YAAY,EAAs8I,2BAA2B,EAAioC,MAAM,mBAAmB,CAAC;AA6DhxN,MAAM,QAAQ,GAAG,gCAAgC,CAAC;AAElD,MAAM,iBAAiB,GAEnB;IACF,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,UAAU;IACnB,OAAO,EAAE,UAAU;CACpB,CAAC;AAEF,MAAM,OAAO,uBAAuB;IAC1B,iBAAiB,CAAgB;IAEjC,OAAO,GAAG,KAAK,CAAC;IAEhB,aAAa,CAAiB;IAE9B,iBAAiB,GAAG,IAAI,GAAG,EAEhC,CAAC;IAEJ;;;;;OAKG;IACH,KAAK,CAAC,oBAAoB,CACxB,UAAuC,EAAE;QAEzC,sBAAsB;QACtB,MAAM,EAAE,UAAU,EAAE,MAAM,GAAG,KAAK,EAAE,GAAG,OAAO,CAAC;QAE/C,IAAI,UAAU,EAAE,CAAC;YACf,IAAI,CAAC,iBAAiB,CAAC,GAAG,CAAC,UAAU,CAAC,CAAC;QACzC,CAAC;QAED,IAAI,CAAC;YACH,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;YAE9C,IAAI,MAAM,EAAE,CAAC;gBACX,MAAM,IAAI,CAAC,gBAAgB,CAAC,UAAU,CAAC,CAAC;YAC1C,CAAC;QACH,CAAC;gBAAS,CAAC;YACT,IAAI,UAAU,EAAE,CAAC;gBACf,IAAI,CAAC,iBAAiB,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC;YAC5C,CAAC;QACH,CAAC;QACD,YAAY;IACd,CAAC;IAED;;OAEG;IACH,SAAS;QACP,OAAO,IAAI,CAAC,OAAO,CAAC;IACtB,CAAC;IAEO,aAAa;QACnB,IAAI,CAAC,iBAAiB,KAAK,IAAI,CAAC,gBAAgB,EAAE,CAAC,KAAK,CAAC,KAAK,CAAC,EAAE;YAC/D,+DAA+D;YAC/D,IAAI,CAAC,iBAAiB,GAAG,SAAS,CAAC;YACnC,IAAI,CAAC,OAAO,GAAG,KAAK,CAAC;YAErB,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;QAEH,OAAO,IAAI,CAAC,iBAAiB,CAAC;IAChC,CAAC;IAEO,KAAK,CAAC,gBAAgB;QAC5B,sBAAsB;QACtB,MAAM,EAAE,GAAG,EAAE,QAAQ,EAAE,GAAG,MAAM,MAAM,CACpC,2BAA2B,CAC5B,CAAC;QAEF,GAAG,CAAC,QAAQ,GAAG,IAAI,CAAC,kBAAkB,EAAE,CAAC;QAEzC,MAAM,UAAU,GAAG,MAAM,QAAQ,CAAC,aAAa,EAAE,QAAQ,EAAE;YACzD,KAAK,EAAE,IAAI;YAEX,iBAAiB,EAAE,CAAC,QAA4B,EAAE,EAAE;gBAClD,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;SACF,CAAC,CAAC;QAEH,IAAI,CAAC,OAAO,GAAG,IAAI,CAAC;QAEpB,IAAI,CAAC,YAAY,CAAC;YAChB,MAAM,EAAE,OAAO;YACf,QAAQ,EAAE,GAAG;SACd,CAAC,CAAC;QAEH,OAAO,UAAU,CAAC;QAClB,YAAY;IACd,CAAC;IAEO,YAAY,CAAC,QAA4B;QAC/C,KAAK,MAAM,QAAQ,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC;YAC9C,IAAI,CAAC;gBACH,QAAQ,CAAC,QAAQ,CAAC,CAAC;YACrB,CAAC;YAAC,MAAM,CAAC;gBACP,qDAAqD;YACvD,CAAC;QACH,CAAC;IACH,CAAC;IAEO,gBAAgB,CAAC,UAAe;QACtC,IAAI,CAAC,aAAa,KAAK,UAAU,CAAC,QAAQ,EAAE;YAC1C,QAAQ,EAAE,UAAU;YACpB,QAAQ,EAAE,UAAU;YACpB,cAAc,EAAE,EAAE;SACnB,CAAC;aACC,IAAI,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC;aACrB,KAAK,CAAC,KAAK,CAAC,EAAE;YACb,IAAI,CAAC,aAAa,GAAG,SAAS,CAAC;YAC/B,MAAM,KAAK,CAAC;QACd,CAAC,CAAC,CAAC;QAEL,OAAO,IAAI,CAAC,aAAa,CAAC;IAC5B,CAAC;IAED,KAAK,CAAC,SAAS,CAAC,OAA2B;QACzC,sBAAsB;QACtB,IAAI,EAAE,IAAI,EAAE,OAAO,EAAE,IAAI,EAAE,EAAE,EAAE,QAAQ,EAAE,GAAG,OAAO,CAAC;QAEpD,MAAM,OAAO,GAAG,IAAI,CAAC;QACrB,MAAM,KAAK,GAAG,EAAE,CAAC;QAEjB,IAAI,GAAG,YAAY,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,CAAQ,CAAC;QAC5C,EAAE,GAAG,YAAY,CAAC,EAAE,CAAC,KAAK,CAAC,GAAG,CAAC,CAAQ,CAAC;QAExC,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,IAAI,KAAK,EAAE,EAAE,CAAC;YAChC,OAAO,IAAI,CAAC;QACd,CAAC;QAED,MAAM,OAAO,GAAG,iBAAiB,CAAC,OAAO,CAAC,CAAC;QAC3C,MAAM,OAAO,GAAG,iBAAiB,CAAC,KAAK,CAAC,CAAC;QAEzC,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,0CAA0C,OAAO,EAAE,CACpD,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,OAAO,EAAE,CAAC;YACb,MAAM,IAAI,KAAK,CACb,0CAA0C,KAAK,EAAE,CAClD,CAAC;QACJ,CAAC;QAED,MAAM,KAAK,GAAG,OAAO,EAAE,IAAI,EAAE;YAC3B,CAAC,CAAC,GAAG,OAAO,CAAC,IAAI,EAAE,OAAO,IAAI,CAAC,IAAI,EAAE,EAAE;YACvC,CAAC,CAAC,IAAI,CAAC;QAET,MAAM,cAAc,GAAG,IAAI,CAAC,eAAe,CAAC,KAAK,EAAE,QAAQ,CAAC,CAAC;QAC7D,MAAM,UAAU,GAAG,MAAM,IAAI,CAAC,aAAa,EAAE,CAAC;QAE9C,MAAM,MAAM,GAAG,MAAM,UAAU,CAAC,cAAc,CAAC,YAAY,EAAE;YAC3D,QAAQ,EAAE,OAAO;YACjB,QAAQ,EAAE,OAAO;SAClB,CAAC,CAAC;QAEH,MAAM,cAAc,GAClB,MAAM,CAAC,CAAC,CAAC,EAAE,gBAAgB,IAAI,EAAE,CAAC;QAEpC,OAAO,cAAc,CAAC,OAAO,CAAC,cAAc,CAAC,CAAC;QAC9C,YAAY;IACd,CAAC;IAEO,eAAe,CACrB,IAAY,EACZ,QAAiC;QAKjC,IAAI,CAAC,QAAQ,IAAI,MAAM,CAAC,IAAI,CAAC,QAAQ,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;YACpD,OAAO;gBACL,YAAY,EAAE,IAAI;gBAClB,OAAO,EAAE,cAAc,CAAC,EAAE,CAAC,cAAc;aAC1C,CAAC;QACJ,CAAC;QAED,MAAM,YAAY,GAGb,EAAE,CAAC;QAER,IAAI,YAAY,GAAG,IAAI,CAAC;QAExB,MAAM,CAAC,OAAO,CAAC,QAAQ,CAAC;aACrB,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,EAAE,CAAC,KAAK,CAAC,EAAE,EAAE,CAAC,KAAK,CAAC,MAAM,GAAG,IAAI,CAAC,MAAM,CAAC;aACrD,OAAO,CAAC,CAAC,CAAC,WAAW,EAAE,eAAe,CAAC,EAAE,KAAK,EAAE,EAAE;YACjD,MAAM,WAAW,GAAG,WAAW,KAAK,aAAa,CAAC;YAElD,YAAY,GAAG,YAAY,CAAC,UAAU,CACpC,WAAW,EACX,WAAW,CACZ,CAAC;YAEF,YAAY,CAAC,IAAI,CAAC;gBAChB,WAAW;gBACX,eAAe;aAChB,CAAC,CAAC;QACL,CAAC,CAAC,CAAC;QAEL,OAAO;YACL,YAAY;YAEZ,OAAO,CAAC,cAAsB;gBAC5B,IAAI,MAAM,GAAG,cAAc,CAAC;gBAE5B,KAAK,MAAM,WAAW,IAAI,YAAY,EAAE,CAAC;oBACvC,MAAM,GAAG,MAAM,CAAC,UAAU,CACxB,WAAW,CAAC,WAAW,EACvB,WAAW,CAAC,eAAe,CAC5B,CAAC;gBACJ,CAAC;gBAED,OAAO,MAAM,CAAC;YAChB,CAAC;SACF,CAAC;IACJ,CAAC;IAEO,kBAAkB;QACxB,MAAM,SAAS,GAAG,iBAAiB,CAAC;YAClC,2BAA2B,EAAE;YAC7B,aAAa;YACb,iBAAiB;SAClB,CAAC,CAAC;QAEH,SAAS,CAAC,SAAS,EAAE;YACnB,SAAS,EAAE,IAAI;SAChB,CAAC,CAAC;QAEH,OAAO,SAAS,CAAC;IACnB,CAAC;CACF"}
@@ -1,4 +1,4 @@
1
1
  {
2
2
  "name": "@taon-dev/i18n/lib-prod",
3
- "version": "21.0.27"
3
+ "version": "21.0.30"
4
4
  }
@@ -1,7 +1,2 @@
1
- /**
2
- *
3
- * @param argsv process.argsv
4
- * @param filename needed if you want ipc communicaiton
5
- */
6
1
  export declare function startCli(argsv: string[], filename: string): Promise<void>;
7
2
  export default startCli;
@@ -1,44 +1,14 @@
1
- //#region tnp-helpers cli template
2
- // import { CoreModels } from 'tnp-core/src';
3
- // import { Helpers, BaseCommandLineFeature } from 'tnp-helpers/src';
4
- // import { BaseProject, BaseStartConfig } from 'tnp-helpers/src'; // @backend
5
- // class $Global extends BaseCommandLineFeature<{}> {
6
- // static [CoreModels.ClassNameStaticProperty] = '';
7
- // public _() {
8
- // console.log(`Hello world from cli`);
9
- // this._exit();
10
- // }
11
- // }
12
- //#endregion
13
- /**
14
- *
15
- * @param argsv process.argsv
16
- * @param filename needed if you want ipc communicaiton
17
- */
1
+ import { LocalTranslationService } from './local-translation';
18
2
  export async function startCli(argsv, filename) {
19
3
  //#region @backendFunc
20
- console.log('Hello from cli');
21
- // console.log({ argsv });
22
- // process.exit(0);
23
- //#region start config
24
- // new BaseStartConfig({
25
- // ProjectClass: BaseProject,
26
- // functionsOrClasses: [
27
- // {
28
- // classOrFnName: '$Version',
29
- // funcOrClass: $Global,
30
- // },
31
- // ],
32
- // argsv,
33
- // useStringArrForArgsFunctions: true,
34
- // shortArgsReplaceConfig: {
35
- // v: 'version',
36
- // },
37
- // callbackNotRecognizedCommand: async () => {
38
- // Helpers.error(`Command not recognized`, false, true);
39
- // },
40
- // });
41
- //#endregion
4
+ const s = new LocalTranslationService();
5
+ const result = await s.translate({
6
+ from: 'en-US',
7
+ to: 'de-DE',
8
+ text: argsv.slice(2).join(' '),
9
+ });
10
+ console.log(`Translated: ${result}`);
11
+ process.exit(0);
42
12
  //#endregion
43
13
  }
44
14
  export default startCli;
@@ -1 +1 @@
1
- {"version":3,"file":"start-cli.js","sourceRoot":"","sources":[""],"names":[],"mappings":"AAAA,kCAAkC;AAClC,6CAA6C;AAC7C,qEAAqE;AACrE,8EAA8E;AAE9E,qDAAqD;AACrD,sDAAsD;AACtD,iBAAiB;AACjB,2CAA2C;AAC3C,oBAAoB;AACpB,MAAM;AACN,IAAI;AACJ,YAAY;AAEZ;;;;GAIG;AACH,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,KAAe,EACf,QAAgB;IAEhB,sBAAsB;IACtB,OAAO,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC;IAC9B,0BAA0B;IAC1B,mBAAmB;IAEnB,sBAAsB;IACtB,wBAAwB;IACxB,+BAA+B;IAC/B,0BAA0B;IAC1B,QAAQ;IACR,mCAAmC;IACnC,8BAA8B;IAC9B,SAAS;IACT,OAAO;IACP,WAAW;IACX,wCAAwC;IACxC,8BAA8B;IAC9B,oBAAoB;IACpB,OAAO;IACP,gDAAgD;IAChD,4DAA4D;IAC5D,OAAO;IACP,MAAM;IACN,YAAY;IACZ,YAAY;AACd,CAAC;AAED,eAAe,QAAQ,CAAC"}
1
+ {"version":3,"file":"start-cli.js","sourceRoot":"","sources":[""],"names":[],"mappings":"AAAA,OAAO,EAAE,uBAAuB,EAAE,MAAM,qBAAqB,CAAC;AAE9D,MAAM,CAAC,KAAK,UAAU,QAAQ,CAC5B,KAAe,EACf,QAAgB;IAEhB,sBAAsB;IACtB,MAAM,CAAC,GAAG,IAAI,uBAAuB,EAAE,CAAC;IACxC,MAAM,MAAM,GAAG,MAAM,CAAC,CAAC,SAAS,CAAC;QAC/B,IAAI,EAAE,OAAO;QACb,EAAE,EAAE,OAAO;QACX,IAAI,EAAE,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC;KAC/B,CAAC,CAAC;IACH,OAAO,CAAC,GAAG,CAAC,eAAe,MAAM,EAAE,CAAC,CAAC;IACrC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;IAChB,YAAY;AACd,CAAC;AAED,eAAe,QAAQ,CAAC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,145 @@
1
+ import { UtilsPoFile__NS__extractPoToJson, UtilsPoFile__NS__generatePoFileContent } from "./utils-po-file";
2
+ describe('UtilsPoFile__NS__extractPoToJson', () => {
3
+ it('extracts Poedit PO entries and ignores empty translations', () => {
4
+ const poContent = `
5
+ msgid ""
6
+ msgstr ""
7
+ "Project-Id-Version: \\\\n"
8
+ "Language: de_DE\\\\n"
9
+ "Content-Type: text/plain; charset=UTF-8\\\\n"
10
+ "X-Generator: Poedit 3.9\\\\n"
11
+
12
+ #: lib/gm-special-draw-api/gm-special-draw.model.ts
13
+ msgid "13-er Ergebniswette"
14
+ msgstr ""
15
+
16
+ #: lib/gm-messages/gm-messages.ts
17
+ msgid "Device not found"
18
+ msgstr "Gerät nicht gefunden"
19
+
20
+ #: lib/gm-messages/gm-messages.ts
21
+ msgid "Discount amount exceeds minimum amount"
22
+ msgstr " "
23
+
24
+ #: lib/gm-messages/gm-messages.ts lib/gm-messages/gm-rpc-generated-errors.ts
25
+ msgid "Data corrupted"
26
+ msgstr "Daten beschädigt"
27
+ `;
28
+ const result = UtilsPoFile__NS__extractPoToJson(poContent);
29
+ expect(result).toEqual([
30
+ {
31
+ fileAbsPath: 'lib/gm-special-draw-api/gm-special-draw.model.ts',
32
+ fileRelativePath: 'lib/gm-special-draw-api/gm-special-draw.model.ts',
33
+ tags: [
34
+ {
35
+ lineNumber: 0,
36
+ gettextString: '13-er Ergebniswette',
37
+ context: undefined,
38
+ translation: undefined,
39
+ },
40
+ ],
41
+ },
42
+ {
43
+ fileAbsPath: 'lib/gm-messages/gm-messages.ts',
44
+ fileRelativePath: 'lib/gm-messages/gm-messages.ts',
45
+ tags: [
46
+ {
47
+ lineNumber: 0,
48
+ gettextString: 'Device not found',
49
+ context: undefined,
50
+ translation: 'Gerät nicht gefunden',
51
+ },
52
+ {
53
+ lineNumber: 0,
54
+ gettextString: 'Discount amount exceeds minimum amount',
55
+ context: undefined,
56
+ translation: undefined,
57
+ },
58
+ {
59
+ lineNumber: 0,
60
+ gettextString: 'Data corrupted',
61
+ context: undefined,
62
+ translation: 'Daten beschädigt',
63
+ },
64
+ ],
65
+ },
66
+ {
67
+ fileAbsPath: 'lib/gm-messages/gm-rpc-generated-errors.ts',
68
+ fileRelativePath: 'lib/gm-messages/gm-rpc-generated-errors.ts',
69
+ tags: [
70
+ {
71
+ lineNumber: 0,
72
+ gettextString: 'Data corrupted',
73
+ context: undefined,
74
+ translation: 'Daten beschädigt',
75
+ },
76
+ ],
77
+ },
78
+ ]);
79
+ });
80
+ it('supports references containing line numbers', () => {
81
+ const poContent = `
82
+ #: lib/example.ts:15 lib/second.ts:31
83
+ msgid "Device not found"
84
+ msgstr "Gerät nicht gefunden"
85
+ `;
86
+ expect(UtilsPoFile__NS__extractPoToJson(poContent)).toEqual([
87
+ {
88
+ fileAbsPath: 'lib/example.ts',
89
+ fileRelativePath: 'lib/example.ts',
90
+ tags: [
91
+ {
92
+ lineNumber: 15,
93
+ gettextString: 'Device not found',
94
+ context: undefined,
95
+ translation: 'Gerät nicht gefunden',
96
+ },
97
+ ],
98
+ },
99
+ {
100
+ fileAbsPath: 'lib/second.ts',
101
+ fileRelativePath: 'lib/second.ts',
102
+ tags: [
103
+ {
104
+ lineNumber: 31,
105
+ gettextString: 'Device not found',
106
+ context: undefined,
107
+ translation: 'Gerät nicht gefunden',
108
+ },
109
+ ],
110
+ },
111
+ ]);
112
+ });
113
+ it('supports PO files containing UTF-8 BOM', () => {
114
+ const poContent = '\uFEFF' +
115
+ `
116
+ msgid ""
117
+ msgstr ""
118
+ "Language: de_DE\\\\n"
119
+
120
+ #: lib/example.ts:4
121
+ msgid "Access prohibited"
122
+ msgstr "Zugriff verboten"
123
+ `;
124
+ const result = UtilsPoFile__NS__extractPoToJson(poContent);
125
+ expect(result[0]?.tags[0]?.translation).toBe('Zugriff verboten');
126
+ });
127
+ it('escapes translations when generating PO content', () => {
128
+ const result = UtilsPoFile__NS__generatePoFileContent([
129
+ {
130
+ fileAbsPath: '/project/lib/example.ts',
131
+ fileRelativePath: 'lib/example.ts',
132
+ tags: [
133
+ {
134
+ lineNumber: 10,
135
+ gettextString: 'Displayed "message"',
136
+ translation: 'Eine "Nachricht"\\nmit neuer Zeile',
137
+ },
138
+ ],
139
+ },
140
+ ], 'de-DE');
141
+ expect(result).toContain('msgid "Displayed \\"message\\""');
142
+ expect(result).toContain('msgstr "Eine \\"Nachricht\\"\\\\nmit neuer Zeile"');
143
+ });
144
+ });
145
+ //# sourceMappingURL=utils-po-file-extracto-to-json.test.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"utils-po-file-extracto-to-json.test.js","sourceRoot":"","sources":[""],"names":[],"mappings":"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"}
@@ -49,7 +49,7 @@ export function UtilsPoFile__NS__generatePoFileContent(files, locale) {
49
49
  lines.push(`msgctxt "${escapePoString(entry.context)}"`);
50
50
  }
51
51
  lines.push(`msgid "${escapePoString(entry.msgid)}"`);
52
- lines.push(`msgstr "${entry.translation || ''}"`);
52
+ lines.push(`msgstr "${escapePoString(normalizePoTranslation(entry.translation) ?? '')}"`);
53
53
  lines.push('');
54
54
  }
55
55
  return lines.join('\n');
@@ -62,16 +62,20 @@ export function UtilsPoFile__NS__extractPoToJson(poFileContent) {
62
62
  const entries = parsePoEntries(poFileContent);
63
63
  const filesMap = new Map();
64
64
  for (const entry of entries) {
65
- if (!entry.msgid)
65
+ if (!entry.msgid) {
66
66
  continue;
67
+ }
68
+ const translation = normalizePoTranslation(entry.msgstr);
67
69
  for (const ref of entry.refs) {
68
70
  const parsedRef = parsePoRef(ref);
69
- if (!parsedRef)
71
+ if (!parsedRef) {
70
72
  continue;
73
+ }
71
74
  let file = filesMap.get(parsedRef.fileAbsPath);
72
75
  if (!file) {
73
76
  file = {
74
77
  fileAbsPath: parsedRef.fileAbsPath,
78
+ fileRelativePath: parsedRef.fileAbsPath,
75
79
  tags: [],
76
80
  };
77
81
  filesMap.set(parsedRef.fileAbsPath, file);
@@ -80,7 +84,7 @@ export function UtilsPoFile__NS__extractPoToJson(poFileContent) {
80
84
  lineNumber: parsedRef.lineNumber,
81
85
  gettextString: entry.msgid,
82
86
  context: entry.msgctxt,
83
- translation: entry.msgstr || undefined,
87
+ translation,
84
88
  });
85
89
  }
86
90
  }
@@ -138,8 +142,7 @@ export function UtilsPoFile__NS__mergeGettextFile(jsonContent1FromTsHtml, json2C
138
142
  const oldTag = oldTagsByKey.get(getGettextTagKey(sourceTag));
139
143
  return {
140
144
  ...sourceTag,
141
- lineNumber: sourceTag.lineNumber,
142
- translation: oldTag?.translation || undefined,
145
+ translation: normalizePoTranslation(oldTag?.translation),
143
146
  };
144
147
  }),
145
148
  };
@@ -152,12 +155,13 @@ function getGettextTagKey(tag) {
152
155
  }
153
156
  function parsePoEntries(po) {
154
157
  //#region @backendFunc
155
- const lines = po.split(/\r?\n/);
158
+ const normalizedPo = po.replace(/^\uFEFF/, '');
159
+ const lines = normalizedPo.split(/\r?\n/);
156
160
  const entries = [];
157
161
  let current = { refs: [] };
158
162
  let activeField = null;
159
163
  const flush = () => {
160
- if (current.refs.length ||
164
+ if (current.refs.length > 0 ||
161
165
  current.msgctxt !== undefined ||
162
166
  current.msgid !== undefined ||
163
167
  current.msgstr !== undefined) {
@@ -198,18 +202,21 @@ function parsePoEntries(po) {
198
202
  }
199
203
  }
200
204
  flush();
201
- // skip header
202
- return entries.filter(entry => !(entry.msgid === '' && !entry.refs.length));
205
+ return entries.filter(entry => {
206
+ const isHeader = entry.msgid === '' && entry.refs.length === 0;
207
+ return !isHeader;
208
+ });
203
209
  //#endregion
204
210
  }
205
211
  function parsePoRef(ref) {
206
212
  //#region @backendFunc
207
- const match = ref.match(/^(.+):(\d+)$/);
208
- if (!match)
213
+ const match = ref.match(/^(.+?)(?::(\d+))?$/);
214
+ if (!match) {
209
215
  return null;
216
+ }
210
217
  return {
211
218
  fileAbsPath: match[1],
212
- lineNumber: Number(match[2]),
219
+ lineNumber: match[2] ? Number(match[2]) : 0,
213
220
  };
214
221
  //#endregion
215
222
  }
@@ -225,6 +232,12 @@ function readPoQuoted(value) {
225
232
  return '';
226
233
  return unescapePoString(match[1]);
227
234
  }
235
+ function normalizePoTranslation(translation) {
236
+ if (translation === undefined || translation === null) {
237
+ return undefined;
238
+ }
239
+ return translation.trim().length > 0 ? translation : undefined;
240
+ }
228
241
  function unescapePoString(value) {
229
242
  return value
230
243
  .replace(/\\n/g, '\n')