@lingo.dev/compiler 0.1.0

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 (300) hide show
  1. package/LICENSE.md +201 -0
  2. package/README.md +192 -0
  3. package/build/_virtual/rolldown_runtime.cjs +29 -0
  4. package/build/_virtual/rolldown_runtime.mjs +7 -0
  5. package/build/index.cjs +0 -0
  6. package/build/index.d.cts +2 -0
  7. package/build/index.d.mts +2 -0
  8. package/build/index.mjs +1 -0
  9. package/build/metadata/manager.cjs +131 -0
  10. package/build/metadata/manager.mjs +123 -0
  11. package/build/metadata/manager.mjs.map +1 -0
  12. package/build/plugin/build-translator.cjs +198 -0
  13. package/build/plugin/build-translator.mjs +196 -0
  14. package/build/plugin/build-translator.mjs.map +1 -0
  15. package/build/plugin/cleanup.cjs +20 -0
  16. package/build/plugin/cleanup.mjs +20 -0
  17. package/build/plugin/cleanup.mjs.map +1 -0
  18. package/build/plugin/next-compiler-loader.cjs +41 -0
  19. package/build/plugin/next-compiler-loader.d.cts +12 -0
  20. package/build/plugin/next-compiler-loader.d.cts.map +1 -0
  21. package/build/plugin/next-compiler-loader.d.mts +13 -0
  22. package/build/plugin/next-compiler-loader.d.mts.map +1 -0
  23. package/build/plugin/next-compiler-loader.mjs +42 -0
  24. package/build/plugin/next-compiler-loader.mjs.map +1 -0
  25. package/build/plugin/next-config-loader.cjs +13 -0
  26. package/build/plugin/next-config-loader.d.cts +8 -0
  27. package/build/plugin/next-config-loader.d.cts.map +1 -0
  28. package/build/plugin/next-config-loader.d.mts +9 -0
  29. package/build/plugin/next-config-loader.d.mts.map +1 -0
  30. package/build/plugin/next-config-loader.mjs +14 -0
  31. package/build/plugin/next-config-loader.mjs.map +1 -0
  32. package/build/plugin/next-locale-client-loader.cjs +9 -0
  33. package/build/plugin/next-locale-client-loader.d.cts +8 -0
  34. package/build/plugin/next-locale-client-loader.d.cts.map +1 -0
  35. package/build/plugin/next-locale-client-loader.d.mts +9 -0
  36. package/build/plugin/next-locale-client-loader.d.mts.map +1 -0
  37. package/build/plugin/next-locale-client-loader.mjs +10 -0
  38. package/build/plugin/next-locale-client-loader.mjs.map +1 -0
  39. package/build/plugin/next-locale-server-loader.cjs +9 -0
  40. package/build/plugin/next-locale-server-loader.d.cts +8 -0
  41. package/build/plugin/next-locale-server-loader.d.cts.map +1 -0
  42. package/build/plugin/next-locale-server-loader.d.mts +9 -0
  43. package/build/plugin/next-locale-server-loader.d.mts.map +1 -0
  44. package/build/plugin/next-locale-server-loader.mjs +10 -0
  45. package/build/plugin/next-locale-server-loader.mjs.map +1 -0
  46. package/build/plugin/next.cjs +220 -0
  47. package/build/plugin/next.d.cts +9 -0
  48. package/build/plugin/next.d.cts.map +1 -0
  49. package/build/plugin/next.d.mts +9 -0
  50. package/build/plugin/next.d.mts.map +1 -0
  51. package/build/plugin/next.mjs +222 -0
  52. package/build/plugin/next.mjs.map +1 -0
  53. package/build/plugin/transform/babel-compat.cjs +13 -0
  54. package/build/plugin/transform/babel-compat.mjs +10 -0
  55. package/build/plugin/transform/babel-compat.mjs.map +1 -0
  56. package/build/plugin/transform/index.cjs +44 -0
  57. package/build/plugin/transform/index.mjs +42 -0
  58. package/build/plugin/transform/index.mjs.map +1 -0
  59. package/build/plugin/transform/metadata.cjs +142 -0
  60. package/build/plugin/transform/metadata.mjs +141 -0
  61. package/build/plugin/transform/metadata.mjs.map +1 -0
  62. package/build/plugin/transform/parse-override.cjs +145 -0
  63. package/build/plugin/transform/parse-override.mjs +144 -0
  64. package/build/plugin/transform/parse-override.mjs.map +1 -0
  65. package/build/plugin/transform/process-file.cjs +391 -0
  66. package/build/plugin/transform/process-file.mjs +390 -0
  67. package/build/plugin/transform/process-file.mjs.map +1 -0
  68. package/build/plugin/transform/use-i18n.cjs +8 -0
  69. package/build/plugin/transform/use-i18n.mjs +7 -0
  70. package/build/plugin/transform/use-i18n.mjs.map +1 -0
  71. package/build/plugin/transform/utils.cjs +205 -0
  72. package/build/plugin/transform/utils.mjs +192 -0
  73. package/build/plugin/transform/utils.mjs.map +1 -0
  74. package/build/plugin/unplugin.cjs +188 -0
  75. package/build/plugin/unplugin.d.cts +8 -0
  76. package/build/plugin/unplugin.d.cts.map +1 -0
  77. package/build/plugin/unplugin.d.mts +8 -0
  78. package/build/plugin/unplugin.d.mts.map +1 -0
  79. package/build/plugin/unplugin.mjs +186 -0
  80. package/build/plugin/unplugin.mjs.map +1 -0
  81. package/build/plugin/vite.cjs +28 -0
  82. package/build/plugin/vite.d.cts +9 -0
  83. package/build/plugin/vite.d.cts.map +1 -0
  84. package/build/plugin/vite.d.mts +9 -0
  85. package/build/plugin/vite.d.mts.map +1 -0
  86. package/build/plugin/vite.mjs +29 -0
  87. package/build/plugin/vite.mjs.map +1 -0
  88. package/build/plugin/webpack.cjs +27 -0
  89. package/build/plugin/webpack.d.cts +8 -0
  90. package/build/plugin/webpack.d.cts.map +1 -0
  91. package/build/plugin/webpack.d.mts +8 -0
  92. package/build/plugin/webpack.d.mts.map +1 -0
  93. package/build/plugin/webpack.mjs +28 -0
  94. package/build/plugin/webpack.mjs.map +1 -0
  95. package/build/react/client/index.cjs +9 -0
  96. package/build/react/client/index.d.cts +5 -0
  97. package/build/react/client/index.d.mts +5 -0
  98. package/build/react/client/index.mjs +6 -0
  99. package/build/react/client/useTranslation.cjs +71 -0
  100. package/build/react/client/useTranslation.d.cts +42 -0
  101. package/build/react/client/useTranslation.d.cts.map +1 -0
  102. package/build/react/client/useTranslation.d.mts +42 -0
  103. package/build/react/client/useTranslation.d.mts.map +1 -0
  104. package/build/react/client/useTranslation.mjs +71 -0
  105. package/build/react/client/useTranslation.mjs.map +1 -0
  106. package/build/react/next/client.cjs +25 -0
  107. package/build/react/next/client.d.cts +9 -0
  108. package/build/react/next/client.d.cts.map +1 -0
  109. package/build/react/next/client.d.mts +9 -0
  110. package/build/react/next/client.d.mts.map +1 -0
  111. package/build/react/next/client.mjs +24 -0
  112. package/build/react/next/client.mjs.map +1 -0
  113. package/build/react/next/cookie-locale-resolver.cjs +29 -0
  114. package/build/react/next/cookie-locale-resolver.d.cts +33 -0
  115. package/build/react/next/cookie-locale-resolver.d.cts.map +1 -0
  116. package/build/react/next/cookie-locale-resolver.d.mts +33 -0
  117. package/build/react/next/cookie-locale-resolver.d.mts.map +1 -0
  118. package/build/react/next/cookie-locale-resolver.mjs +29 -0
  119. package/build/react/next/cookie-locale-resolver.mjs.map +1 -0
  120. package/build/react/next/server.cjs +21 -0
  121. package/build/react/next/server.d.cts +13 -0
  122. package/build/react/next/server.d.cts.map +1 -0
  123. package/build/react/next/server.d.mts +14 -0
  124. package/build/react/next/server.d.mts.map +1 -0
  125. package/build/react/next/server.mjs +20 -0
  126. package/build/react/next/server.mjs.map +1 -0
  127. package/build/react/server/ServerLingoProvider.cjs +19 -0
  128. package/build/react/server/ServerLingoProvider.d.cts +12 -0
  129. package/build/react/server/ServerLingoProvider.d.cts.map +1 -0
  130. package/build/react/server/ServerLingoProvider.d.mts +12 -0
  131. package/build/react/server/ServerLingoProvider.d.mts.map +1 -0
  132. package/build/react/server/ServerLingoProvider.mjs +19 -0
  133. package/build/react/server/ServerLingoProvider.mjs.map +1 -0
  134. package/build/react/server/index.cjs +7 -0
  135. package/build/react/server/index.d.cts +4 -0
  136. package/build/react/server/index.d.mts +4 -0
  137. package/build/react/server/index.mjs +5 -0
  138. package/build/react/server/useTranslation.cjs +60 -0
  139. package/build/react/server/useTranslation.d.cts +36 -0
  140. package/build/react/server/useTranslation.d.cts.map +1 -0
  141. package/build/react/server/useTranslation.d.mts +36 -0
  142. package/build/react/server/useTranslation.d.mts.map +1 -0
  143. package/build/react/server/useTranslation.mjs +60 -0
  144. package/build/react/server/useTranslation.mjs.map +1 -0
  145. package/build/react/server-only/index.cjs +42 -0
  146. package/build/react/server-only/index.d.cts +38 -0
  147. package/build/react/server-only/index.d.cts.map +1 -0
  148. package/build/react/server-only/index.d.mts +38 -0
  149. package/build/react/server-only/index.d.mts.map +1 -0
  150. package/build/react/server-only/index.mjs +42 -0
  151. package/build/react/server-only/index.mjs.map +1 -0
  152. package/build/react/server-only/translations.cjs +85 -0
  153. package/build/react/server-only/translations.mjs +85 -0
  154. package/build/react/server-only/translations.mjs.map +1 -0
  155. package/build/react/shared/LingoContext.cjs +14 -0
  156. package/build/react/shared/LingoContext.d.cts +41 -0
  157. package/build/react/shared/LingoContext.d.cts.map +1 -0
  158. package/build/react/shared/LingoContext.d.mts +41 -0
  159. package/build/react/shared/LingoContext.d.mts.map +1 -0
  160. package/build/react/shared/LingoContext.mjs +13 -0
  161. package/build/react/shared/LingoContext.mjs.map +1 -0
  162. package/build/react/shared/LingoProvider.cjs +274 -0
  163. package/build/react/shared/LingoProvider.d.cts +76 -0
  164. package/build/react/shared/LingoProvider.d.cts.map +1 -0
  165. package/build/react/shared/LingoProvider.d.mts +76 -0
  166. package/build/react/shared/LingoProvider.d.mts.map +1 -0
  167. package/build/react/shared/LingoProvider.mjs +274 -0
  168. package/build/react/shared/LingoProvider.mjs.map +1 -0
  169. package/build/react/shared/LocaleSwitcher.cjs +61 -0
  170. package/build/react/shared/LocaleSwitcher.d.cts +71 -0
  171. package/build/react/shared/LocaleSwitcher.d.cts.map +1 -0
  172. package/build/react/shared/LocaleSwitcher.d.mts +71 -0
  173. package/build/react/shared/LocaleSwitcher.d.mts.map +1 -0
  174. package/build/react/shared/LocaleSwitcher.mjs +61 -0
  175. package/build/react/shared/LocaleSwitcher.mjs.map +1 -0
  176. package/build/react/shared/render-rich-text.cjs +55 -0
  177. package/build/react/shared/render-rich-text.d.cts +17 -0
  178. package/build/react/shared/render-rich-text.d.cts.map +1 -0
  179. package/build/react/shared/render-rich-text.d.mts +17 -0
  180. package/build/react/shared/render-rich-text.d.mts.map +1 -0
  181. package/build/react/shared/render-rich-text.mjs +54 -0
  182. package/build/react/shared/render-rich-text.mjs.map +1 -0
  183. package/build/react/shared/utils.cjs +34 -0
  184. package/build/react/shared/utils.mjs +35 -0
  185. package/build/react/shared/utils.mjs.map +1 -0
  186. package/build/react/types.d.cts +16 -0
  187. package/build/react/types.d.cts.map +1 -0
  188. package/build/react/types.d.mts +16 -0
  189. package/build/react/types.d.mts.map +1 -0
  190. package/build/translation-server/logger.cjs +37 -0
  191. package/build/translation-server/logger.mjs +37 -0
  192. package/build/translation-server/logger.mjs.map +1 -0
  193. package/build/translation-server/translation-server.cjs +547 -0
  194. package/build/translation-server/translation-server.mjs +544 -0
  195. package/build/translation-server/translation-server.mjs.map +1 -0
  196. package/build/translation-server/ws-events.cjs +15 -0
  197. package/build/translation-server/ws-events.mjs +15 -0
  198. package/build/translation-server/ws-events.mjs.map +1 -0
  199. package/build/translators/api.cjs +12 -0
  200. package/build/translators/api.mjs +12 -0
  201. package/build/translators/api.mjs.map +1 -0
  202. package/build/translators/cache-factory.cjs +26 -0
  203. package/build/translators/cache-factory.mjs +27 -0
  204. package/build/translators/cache-factory.mjs.map +1 -0
  205. package/build/translators/lingo/model-factory.cjs +179 -0
  206. package/build/translators/lingo/model-factory.mjs +174 -0
  207. package/build/translators/lingo/model-factory.mjs.map +1 -0
  208. package/build/translators/lingo/prompt.cjs +43 -0
  209. package/build/translators/lingo/prompt.mjs +43 -0
  210. package/build/translators/lingo/prompt.mjs.map +1 -0
  211. package/build/translators/lingo/service.cjs +152 -0
  212. package/build/translators/lingo/service.mjs +152 -0
  213. package/build/translators/lingo/service.mjs.map +1 -0
  214. package/build/translators/lingo/shots.cjs +28 -0
  215. package/build/translators/lingo/shots.mjs +28 -0
  216. package/build/translators/lingo/shots.mjs.map +1 -0
  217. package/build/translators/local-cache.cjs +115 -0
  218. package/build/translators/local-cache.mjs +113 -0
  219. package/build/translators/local-cache.mjs.map +1 -0
  220. package/build/translators/parse-xml.cjs +109 -0
  221. package/build/translators/parse-xml.mjs +108 -0
  222. package/build/translators/parse-xml.mjs.map +1 -0
  223. package/build/translators/pluralization/icu-validator.cjs +36 -0
  224. package/build/translators/pluralization/icu-validator.mjs +36 -0
  225. package/build/translators/pluralization/icu-validator.mjs.map +1 -0
  226. package/build/translators/pluralization/pattern-detector.cjs +25 -0
  227. package/build/translators/pluralization/pattern-detector.mjs +25 -0
  228. package/build/translators/pluralization/pattern-detector.mjs.map +1 -0
  229. package/build/translators/pluralization/prompt.cjs +98 -0
  230. package/build/translators/pluralization/prompt.mjs +98 -0
  231. package/build/translators/pluralization/prompt.mjs.map +1 -0
  232. package/build/translators/pluralization/service.cjs +247 -0
  233. package/build/translators/pluralization/service.mjs +247 -0
  234. package/build/translators/pluralization/service.mjs.map +1 -0
  235. package/build/translators/pluralization/shots.cjs +53 -0
  236. package/build/translators/pluralization/shots.mjs +53 -0
  237. package/build/translators/pluralization/shots.mjs.map +1 -0
  238. package/build/translators/pluralization/types.d.cts +17 -0
  239. package/build/translators/pluralization/types.d.cts.map +1 -0
  240. package/build/translators/pluralization/types.d.mts +17 -0
  241. package/build/translators/pluralization/types.d.mts.map +1 -0
  242. package/build/translators/pseudotranslator/index.cjs +129 -0
  243. package/build/translators/pseudotranslator/index.mjs +129 -0
  244. package/build/translators/pseudotranslator/index.mjs.map +1 -0
  245. package/build/translators/translation-service.cjs +182 -0
  246. package/build/translators/translation-service.mjs +183 -0
  247. package/build/translators/translation-service.mjs.map +1 -0
  248. package/build/translators/translator-factory.cjs +49 -0
  249. package/build/translators/translator-factory.mjs +50 -0
  250. package/build/translators/translator-factory.mjs.map +1 -0
  251. package/build/types.d.cts +161 -0
  252. package/build/types.d.cts.map +1 -0
  253. package/build/types.d.mts +161 -0
  254. package/build/types.d.mts.map +1 -0
  255. package/build/utils/config-factory.cjs +58 -0
  256. package/build/utils/config-factory.mjs +58 -0
  257. package/build/utils/config-factory.mjs.map +1 -0
  258. package/build/utils/hash.cjs +17 -0
  259. package/build/utils/hash.mjs +16 -0
  260. package/build/utils/hash.mjs.map +1 -0
  261. package/build/utils/is-valid-locale.cjs +14 -0
  262. package/build/utils/is-valid-locale.mjs +14 -0
  263. package/build/utils/is-valid-locale.mjs.map +1 -0
  264. package/build/utils/logger.cjs +51 -0
  265. package/build/utils/logger.mjs +50 -0
  266. package/build/utils/logger.mjs.map +1 -0
  267. package/build/utils/path-helpers.cjs +49 -0
  268. package/build/utils/path-helpers.mjs +47 -0
  269. package/build/utils/path-helpers.mjs.map +1 -0
  270. package/build/utils/timeout.cjs +42 -0
  271. package/build/utils/timeout.mjs +41 -0
  272. package/build/utils/timeout.mjs.map +1 -0
  273. package/build/virtual/code-generator.cjs +54 -0
  274. package/build/virtual/code-generator.mjs +53 -0
  275. package/build/virtual/code-generator.mjs.map +1 -0
  276. package/build/virtual/config.cjs +10 -0
  277. package/build/virtual/config.d.cts +9 -0
  278. package/build/virtual/config.d.cts.map +1 -0
  279. package/build/virtual/config.d.mts +9 -0
  280. package/build/virtual/config.d.mts.map +1 -0
  281. package/build/virtual/config.mjs +8 -0
  282. package/build/virtual/config.mjs.map +1 -0
  283. package/build/virtual/locale/client.cjs +23 -0
  284. package/build/virtual/locale/client.d.cts +19 -0
  285. package/build/virtual/locale/client.d.cts.map +1 -0
  286. package/build/virtual/locale/client.d.mts +19 -0
  287. package/build/virtual/locale/client.d.mts.map +1 -0
  288. package/build/virtual/locale/client.mjs +22 -0
  289. package/build/virtual/locale/client.mjs.map +1 -0
  290. package/build/virtual/locale/server.cjs +13 -0
  291. package/build/virtual/locale/server.d.cts +13 -0
  292. package/build/virtual/locale/server.d.cts.map +1 -0
  293. package/build/virtual/locale/server.d.mts +13 -0
  294. package/build/virtual/locale/server.d.mts.map +1 -0
  295. package/build/virtual/locale/server.mjs +13 -0
  296. package/build/virtual/locale/server.mjs.map +1 -0
  297. package/build/widget/lingo-dev-widget.cjs +228 -0
  298. package/build/widget/lingo-dev-widget.mjs +229 -0
  299. package/build/widget/lingo-dev-widget.mjs.map +1 -0
  300. package/package.json +189 -0
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../../../src/translators/pluralization/types.ts"],"sourcesContent":[],"mappings":";;;;KAkBY,mBAAA;gBACI"}
@@ -0,0 +1,129 @@
1
+
2
+ //#region src/translators/pseudotranslator/index.ts
3
+ /**
4
+ * Pseudo-translator that uses pseudolocalization
5
+ * Useful for testing i18n without actual translation APIs
6
+ */
7
+ var PseudoTranslator = class {
8
+ constructor(config, logger) {
9
+ this.config = config;
10
+ this.logger = logger;
11
+ }
12
+ translate(locale, entries) {
13
+ this.logger.debug(`[TRACE-PSEUDO] translate() ENTERED for ${locale} with ${Object.keys(entries).length} entries`);
14
+ const delay = this.config?.delayMedian ?? 0;
15
+ const actualDelay = this.getRandomDelay(delay);
16
+ this.logger.debug(`[TRACE-PSEUDO] Config delay: ${delay}ms, actual delay: ${actualDelay}ms`);
17
+ return new Promise((resolve) => {
18
+ this.logger.debug(`[TRACE-PSEUDO] Promise created, scheduling setTimeout for ${actualDelay}ms`);
19
+ setTimeout(() => {
20
+ this.logger.debug(`[TRACE-PSEUDO] setTimeout callback fired for ${locale}, processing entries`);
21
+ const result = Object.fromEntries(Object.entries(entries).map(([hash, entry]) => {
22
+ return [hash, `${locale}/${pseudolocalize(entry.text)}`];
23
+ }));
24
+ this.logger.debug(`[TRACE-PSEUDO] Pseudolocalization complete, resolving with ${Object.keys(result).length} translations`);
25
+ resolve(result);
26
+ this.logger.debug(`[TRACE-PSEUDO] Promise resolved for ${locale}`);
27
+ }, actualDelay);
28
+ this.logger.debug(`[TRACE-PSEUDO] setTimeout scheduled, returning promise`);
29
+ });
30
+ }
31
+ getRandomDelay(median) {
32
+ if (median === 0) return 0;
33
+ const min = median * .5;
34
+ const max = median * 1.5;
35
+ return Math.floor(Math.random() * (max - min + 1)) + min;
36
+ }
37
+ };
38
+ /**
39
+ * Character map for pseudolocalization
40
+ */
41
+ const PSEUDO_MAP = {
42
+ a: "á",
43
+ b: "ḅ",
44
+ c: "ç",
45
+ d: "ḍ",
46
+ e: "é",
47
+ f: "ƒ",
48
+ g: "ĝ",
49
+ h: "ĥ",
50
+ i: "í",
51
+ j: "ĵ",
52
+ k: "ḳ",
53
+ l: "ĺ",
54
+ m: "ṁ",
55
+ n: "ñ",
56
+ o: "ó",
57
+ p: "ṗ",
58
+ q: "ɋ",
59
+ r: "ŕ",
60
+ s: "ś",
61
+ t: "ţ",
62
+ u: "ú",
63
+ v: "ṿ",
64
+ w: "ŵ",
65
+ x: "ẋ",
66
+ y: "ý",
67
+ z: "ẑ",
68
+ A: "Á",
69
+ B: "Ḅ",
70
+ C: "Ç",
71
+ D: "Ḍ",
72
+ E: "É",
73
+ F: "Ƒ",
74
+ G: "Ĝ",
75
+ H: "Ĥ",
76
+ I: "Í",
77
+ J: "Ĵ",
78
+ K: "Ḳ",
79
+ L: "Ĺ",
80
+ M: "Ṁ",
81
+ N: "Ñ",
82
+ O: "Ó",
83
+ P: "Ṗ",
84
+ Q: "Ɋ",
85
+ R: "Ŕ",
86
+ S: "Ś",
87
+ T: "Ţ",
88
+ U: "Ú",
89
+ V: "Ṿ",
90
+ W: "Ŵ",
91
+ X: "Ẋ",
92
+ Y: "Ý",
93
+ Z: "Ẑ"
94
+ };
95
+ /**
96
+ * Pseudolocalize a string
97
+ * Adds brackets, expands length by ~30%, and uses accented characters
98
+ * Preserves variable placeholders {name} and component tags <a0>, </a0>
99
+ */
100
+ function pseudolocalize(text) {
101
+ if (!text.trim() || text.match(/^{.*}$/)) return text;
102
+ const preserveRegex = /(\{\w+}|<\/?\w+\/?>)/g;
103
+ const parts = [];
104
+ let lastIndex = 0;
105
+ let match;
106
+ while ((match = preserveRegex.exec(text)) !== null) {
107
+ if (match.index > lastIndex) parts.push({
108
+ text: text.substring(lastIndex, match.index),
109
+ preserve: false
110
+ });
111
+ parts.push({
112
+ text: match[0],
113
+ preserve: true
114
+ });
115
+ lastIndex = preserveRegex.lastIndex;
116
+ }
117
+ if (lastIndex < text.length) parts.push({
118
+ text: text.substring(lastIndex),
119
+ preserve: false
120
+ });
121
+ let result = "";
122
+ for (const part of parts) if (part.preserve) result += part.text;
123
+ else for (const char of part.text) result += PSEUDO_MAP[char] || char;
124
+ const padding = " ".repeat(Math.ceil(text.length * .3));
125
+ return `${result}${padding}`;
126
+ }
127
+
128
+ //#endregion
129
+ exports.PseudoTranslator = PseudoTranslator;
@@ -0,0 +1,129 @@
1
+ //#region src/translators/pseudotranslator/index.ts
2
+ /**
3
+ * Pseudo-translator that uses pseudolocalization
4
+ * Useful for testing i18n without actual translation APIs
5
+ */
6
+ var PseudoTranslator = class {
7
+ constructor(config, logger) {
8
+ this.config = config;
9
+ this.logger = logger;
10
+ }
11
+ translate(locale, entries) {
12
+ this.logger.debug(`[TRACE-PSEUDO] translate() ENTERED for ${locale} with ${Object.keys(entries).length} entries`);
13
+ const delay = this.config?.delayMedian ?? 0;
14
+ const actualDelay = this.getRandomDelay(delay);
15
+ this.logger.debug(`[TRACE-PSEUDO] Config delay: ${delay}ms, actual delay: ${actualDelay}ms`);
16
+ return new Promise((resolve) => {
17
+ this.logger.debug(`[TRACE-PSEUDO] Promise created, scheduling setTimeout for ${actualDelay}ms`);
18
+ setTimeout(() => {
19
+ this.logger.debug(`[TRACE-PSEUDO] setTimeout callback fired for ${locale}, processing entries`);
20
+ const result = Object.fromEntries(Object.entries(entries).map(([hash, entry]) => {
21
+ return [hash, `${locale}/${pseudolocalize(entry.text)}`];
22
+ }));
23
+ this.logger.debug(`[TRACE-PSEUDO] Pseudolocalization complete, resolving with ${Object.keys(result).length} translations`);
24
+ resolve(result);
25
+ this.logger.debug(`[TRACE-PSEUDO] Promise resolved for ${locale}`);
26
+ }, actualDelay);
27
+ this.logger.debug(`[TRACE-PSEUDO] setTimeout scheduled, returning promise`);
28
+ });
29
+ }
30
+ getRandomDelay(median) {
31
+ if (median === 0) return 0;
32
+ const min = median * .5;
33
+ const max = median * 1.5;
34
+ return Math.floor(Math.random() * (max - min + 1)) + min;
35
+ }
36
+ };
37
+ /**
38
+ * Character map for pseudolocalization
39
+ */
40
+ const PSEUDO_MAP = {
41
+ a: "á",
42
+ b: "ḅ",
43
+ c: "ç",
44
+ d: "ḍ",
45
+ e: "é",
46
+ f: "ƒ",
47
+ g: "ĝ",
48
+ h: "ĥ",
49
+ i: "í",
50
+ j: "ĵ",
51
+ k: "ḳ",
52
+ l: "ĺ",
53
+ m: "ṁ",
54
+ n: "ñ",
55
+ o: "ó",
56
+ p: "ṗ",
57
+ q: "ɋ",
58
+ r: "ŕ",
59
+ s: "ś",
60
+ t: "ţ",
61
+ u: "ú",
62
+ v: "ṿ",
63
+ w: "ŵ",
64
+ x: "ẋ",
65
+ y: "ý",
66
+ z: "ẑ",
67
+ A: "Á",
68
+ B: "Ḅ",
69
+ C: "Ç",
70
+ D: "Ḍ",
71
+ E: "É",
72
+ F: "Ƒ",
73
+ G: "Ĝ",
74
+ H: "Ĥ",
75
+ I: "Í",
76
+ J: "Ĵ",
77
+ K: "Ḳ",
78
+ L: "Ĺ",
79
+ M: "Ṁ",
80
+ N: "Ñ",
81
+ O: "Ó",
82
+ P: "Ṗ",
83
+ Q: "Ɋ",
84
+ R: "Ŕ",
85
+ S: "Ś",
86
+ T: "Ţ",
87
+ U: "Ú",
88
+ V: "Ṿ",
89
+ W: "Ŵ",
90
+ X: "Ẋ",
91
+ Y: "Ý",
92
+ Z: "Ẑ"
93
+ };
94
+ /**
95
+ * Pseudolocalize a string
96
+ * Adds brackets, expands length by ~30%, and uses accented characters
97
+ * Preserves variable placeholders {name} and component tags <a0>, </a0>
98
+ */
99
+ function pseudolocalize(text) {
100
+ if (!text.trim() || text.match(/^{.*}$/)) return text;
101
+ const preserveRegex = /(\{\w+}|<\/?\w+\/?>)/g;
102
+ const parts = [];
103
+ let lastIndex = 0;
104
+ let match;
105
+ while ((match = preserveRegex.exec(text)) !== null) {
106
+ if (match.index > lastIndex) parts.push({
107
+ text: text.substring(lastIndex, match.index),
108
+ preserve: false
109
+ });
110
+ parts.push({
111
+ text: match[0],
112
+ preserve: true
113
+ });
114
+ lastIndex = preserveRegex.lastIndex;
115
+ }
116
+ if (lastIndex < text.length) parts.push({
117
+ text: text.substring(lastIndex),
118
+ preserve: false
119
+ });
120
+ let result = "";
121
+ for (const part of parts) if (part.preserve) result += part.text;
122
+ else for (const char of part.text) result += PSEUDO_MAP[char] || char;
123
+ const padding = " ".repeat(Math.ceil(text.length * .3));
124
+ return `${result}${padding}`;
125
+ }
126
+
127
+ //#endregion
128
+ export { PseudoTranslator };
129
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["config: PseudoTranslatorConfig","logger: Logger","PSEUDO_MAP: Record<string, string>","parts: Array<{ text: string; preserve: boolean }>","match: RegExpExecArray | null"],"sources":["../../../src/translators/pseudotranslator/index.ts"],"sourcesContent":["/**\n * Pseudotranslator for testing without actual translation APIs\n */\n\nimport type { TranslatableEntry, Translator } from \"../api\";\nimport { Logger } from \"../../utils/logger\";\nimport type { LocaleCode } from \"lingo.dev/spec\";\n\nexport interface PseudoTranslatorConfig {\n delayMedian?: number;\n}\n\n/**\n * Pseudo-translator that uses pseudolocalization\n * Useful for testing i18n without actual translation APIs\n */\nexport class PseudoTranslator implements Translator<PseudoTranslatorConfig> {\n constructor(\n readonly config: PseudoTranslatorConfig,\n private readonly logger: Logger,\n ) {}\n\n translate(locale: LocaleCode, entries: Record<string, TranslatableEntry>) {\n this.logger.debug(\n `[TRACE-PSEUDO] translate() ENTERED for ${locale} with ${Object.keys(entries).length} entries`,\n );\n const delay = this.config?.delayMedian ?? 0;\n const actualDelay = this.getRandomDelay(delay);\n\n this.logger.debug(\n `[TRACE-PSEUDO] Config delay: ${delay}ms, actual delay: ${actualDelay}ms`,\n );\n\n return new Promise<Record<string, string>>((resolve) => {\n this.logger.debug(\n `[TRACE-PSEUDO] Promise created, scheduling setTimeout for ${actualDelay}ms`,\n );\n\n setTimeout(() => {\n this.logger.debug(\n `[TRACE-PSEUDO] setTimeout callback fired for ${locale}, processing entries`,\n );\n\n const result = Object.fromEntries(\n Object.entries(entries).map(([hash, entry]) => {\n return [hash, `${locale}/${pseudolocalize(entry.text)}`];\n }),\n );\n\n this.logger.debug(\n `[TRACE-PSEUDO] Pseudolocalization complete, resolving with ${Object.keys(result).length} translations`,\n );\n resolve(result);\n this.logger.debug(`[TRACE-PSEUDO] Promise resolved for ${locale}`);\n }, actualDelay);\n\n this.logger.debug(\n `[TRACE-PSEUDO] setTimeout scheduled, returning promise`,\n );\n });\n }\n\n private getRandomDelay(median: number): number {\n if (median === 0) return 0;\n // Generate random delay with distribution around median\n // Use a simple approach: median ± 50%\n const min = median * 0.5;\n const max = median * 1.5;\n return Math.floor(Math.random() * (max - min + 1)) + min;\n }\n}\n\n/**\n * Character map for pseudolocalization\n */\nconst PSEUDO_MAP: Record<string, string> = {\n a: \"á\",\n b: \"ḅ\",\n c: \"ç\",\n d: \"ḍ\",\n e: \"é\",\n f: \"ƒ\",\n g: \"ĝ\",\n h: \"ĥ\",\n i: \"í\",\n j: \"ĵ\",\n k: \"ḳ\",\n l: \"ĺ\",\n m: \"ṁ\",\n n: \"ñ\",\n o: \"ó\",\n p: \"ṗ\",\n q: \"ɋ\",\n r: \"ŕ\",\n s: \"ś\",\n t: \"ţ\",\n u: \"ú\",\n v: \"ṿ\",\n w: \"ŵ\",\n x: \"ẋ\",\n y: \"ý\",\n z: \"ẑ\",\n A: \"Á\",\n B: \"Ḅ\",\n C: \"Ç\",\n D: \"Ḍ\",\n E: \"É\",\n F: \"Ƒ\",\n G: \"Ĝ\",\n H: \"Ĥ\",\n I: \"Í\",\n J: \"Ĵ\",\n K: \"Ḳ\",\n L: \"Ĺ\",\n M: \"Ṁ\",\n N: \"Ñ\",\n O: \"Ó\",\n P: \"Ṗ\",\n Q: \"Ɋ\",\n R: \"Ŕ\",\n S: \"Ś\",\n T: \"Ţ\",\n U: \"Ú\",\n V: \"Ṿ\",\n W: \"Ŵ\",\n X: \"Ẋ\",\n Y: \"Ý\",\n Z: \"Ẑ\",\n};\n\n/**\n * Pseudolocalize a string\n * Adds brackets, expands length by ~30%, and uses accented characters\n * Preserves variable placeholders {name} and component tags <a0>, </a0>\n */\nexport function pseudolocalize(text: string): string {\n // Don't pseudolocalize if it's just whitespace or a variable placeholder\n if (!text.trim() || text.match(/^{.*}$/)) {\n return text;\n }\n\n // Regular expression to match patterns we should NOT translate:\n // - Variable placeholders: {varName}\n // - Component tags: <tagName> or </tagName>\n const preserveRegex = /(\\{\\w+}|<\\/?\\w+\\/?>)/g;\n\n // Split text into parts that should be preserved and parts that should be translated\n const parts: Array<{ text: string; preserve: boolean }> = [];\n let lastIndex = 0;\n let match: RegExpExecArray | null;\n\n while ((match = preserveRegex.exec(text)) !== null) {\n // Add text before the match (to be translated)\n if (match.index > lastIndex) {\n parts.push({\n text: text.substring(lastIndex, match.index),\n preserve: false,\n });\n }\n\n // Add the matched pattern (to be preserved)\n parts.push({\n text: match[0],\n preserve: true,\n });\n\n lastIndex = preserveRegex.lastIndex;\n }\n\n // Add remaining text (to be translated)\n if (lastIndex < text.length) {\n parts.push({\n text: text.substring(lastIndex),\n preserve: false,\n });\n }\n\n // Convert characters in translatable parts only\n let result = \"\";\n for (const part of parts) {\n if (part.preserve) {\n // Keep placeholders and tags as-is\n result += part.text;\n } else {\n // Pseudolocalize the text\n for (const char of part.text) {\n result += PSEUDO_MAP[char] || char;\n }\n }\n }\n\n // Add padding to simulate longer translations (~30% longer)\n const padding = \" \".repeat(Math.ceil(text.length * 0.3));\n\n // Wrap in brackets to identify translated strings\n return `${result}${padding}`;\n}\n"],"mappings":";;;;;AAgBA,IAAa,mBAAb,MAA4E;CAC1E,YACE,AAASA,QACT,AAAiBC,QACjB;EAFS;EACQ;;CAGnB,UAAU,QAAoB,SAA4C;AACxE,OAAK,OAAO,MACV,0CAA0C,OAAO,QAAQ,OAAO,KAAK,QAAQ,CAAC,OAAO,UACtF;EACD,MAAM,QAAQ,KAAK,QAAQ,eAAe;EAC1C,MAAM,cAAc,KAAK,eAAe,MAAM;AAE9C,OAAK,OAAO,MACV,gCAAgC,MAAM,oBAAoB,YAAY,IACvE;AAED,SAAO,IAAI,SAAiC,YAAY;AACtD,QAAK,OAAO,MACV,6DAA6D,YAAY,IAC1E;AAED,oBAAiB;AACf,SAAK,OAAO,MACV,gDAAgD,OAAO,sBACxD;IAED,MAAM,SAAS,OAAO,YACpB,OAAO,QAAQ,QAAQ,CAAC,KAAK,CAAC,MAAM,WAAW;AAC7C,YAAO,CAAC,MAAM,GAAG,OAAO,GAAG,eAAe,MAAM,KAAK,GAAG;MACxD,CACH;AAED,SAAK,OAAO,MACV,8DAA8D,OAAO,KAAK,OAAO,CAAC,OAAO,eAC1F;AACD,YAAQ,OAAO;AACf,SAAK,OAAO,MAAM,uCAAuC,SAAS;MACjE,YAAY;AAEf,QAAK,OAAO,MACV,yDACD;IACD;;CAGJ,AAAQ,eAAe,QAAwB;AAC7C,MAAI,WAAW,EAAG,QAAO;EAGzB,MAAM,MAAM,SAAS;EACrB,MAAM,MAAM,SAAS;AACrB,SAAO,KAAK,MAAM,KAAK,QAAQ,IAAI,MAAM,MAAM,GAAG,GAAG;;;;;;AAOzD,MAAMC,aAAqC;CACzC,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACH,GAAG;CACJ;;;;;;AAOD,SAAgB,eAAe,MAAsB;AAEnD,KAAI,CAAC,KAAK,MAAM,IAAI,KAAK,MAAM,SAAS,CACtC,QAAO;CAMT,MAAM,gBAAgB;CAGtB,MAAMC,QAAoD,EAAE;CAC5D,IAAI,YAAY;CAChB,IAAIC;AAEJ,SAAQ,QAAQ,cAAc,KAAK,KAAK,MAAM,MAAM;AAElD,MAAI,MAAM,QAAQ,UAChB,OAAM,KAAK;GACT,MAAM,KAAK,UAAU,WAAW,MAAM,MAAM;GAC5C,UAAU;GACX,CAAC;AAIJ,QAAM,KAAK;GACT,MAAM,MAAM;GACZ,UAAU;GACX,CAAC;AAEF,cAAY,cAAc;;AAI5B,KAAI,YAAY,KAAK,OACnB,OAAM,KAAK;EACT,MAAM,KAAK,UAAU,UAAU;EAC/B,UAAU;EACX,CAAC;CAIJ,IAAI,SAAS;AACb,MAAK,MAAM,QAAQ,MACjB,KAAI,KAAK,SAEP,WAAU,KAAK;KAGf,MAAK,MAAM,QAAQ,KAAK,KACtB,WAAU,WAAW,SAAS;CAMpC,MAAM,UAAU,IAAI,OAAO,KAAK,KAAK,KAAK,SAAS,GAAI,CAAC;AAGxD,QAAO,GAAG,SAAS"}
@@ -0,0 +1,182 @@
1
+ const require_index = require('./pseudotranslator/index.cjs');
2
+ const require_service = require('./pluralization/service.cjs');
3
+
4
+ //#region src/translators/translation-service.ts
5
+ var TranslationService = class {
6
+ useCache = true;
7
+ pluralizationService;
8
+ constructor(translator, cache, config, logger) {
9
+ this.translator = translator;
10
+ this.cache = cache;
11
+ this.config = config;
12
+ this.logger = logger;
13
+ const isPseudo = this.translator instanceof require_index.PseudoTranslator;
14
+ this.useCache = !isPseudo;
15
+ if (this.config.pluralization?.enabled !== false && !isPseudo) {
16
+ this.logger.info("Initializing pluralization service...");
17
+ this.pluralizationService = new require_service.PluralizationService({
18
+ ...this.config.pluralization,
19
+ sourceLocale: this.config.sourceLocale
20
+ }, this.logger);
21
+ }
22
+ }
23
+ /**
24
+ * Translate entries to target locale
25
+ *
26
+ * @param locale Target locale (including source locale)
27
+ * @param metadata Metadata schema with all entries
28
+ * @param requestedHashes Optional: only translate specific hashes
29
+ * @returns Translation result with translations and errors
30
+ */
31
+ async translate(locale, metadata, requestedHashes) {
32
+ const startTime = performance.now();
33
+ const workingHashes = requestedHashes || Object.keys(metadata.entries);
34
+ this.logger.info(`Translation requested for ${workingHashes.length} hashes in locale: ${locale}`);
35
+ this.logger.debug(`[TRACE] Checking cache for locale: ${locale}`);
36
+ const cacheStartTime = performance.now();
37
+ const cachedTranslations = this.useCache ? await this.cache.get(locale) : {};
38
+ const cacheEndTime = performance.now();
39
+ this.logger.debug(`[TRACE] Cache check completed in ${(cacheEndTime - cacheStartTime).toFixed(2)}ms, found ${Object.keys(cachedTranslations).length} entries`);
40
+ const uncachedHashes = workingHashes.filter((hash) => !cachedTranslations[hash]);
41
+ this.logger.debug(`[TRACE] ${uncachedHashes.length} hashes need processing, ${workingHashes.length - uncachedHashes.length} are cached`);
42
+ const cachedCount = workingHashes.length - uncachedHashes.length;
43
+ if (uncachedHashes.length === 0) {
44
+ const endTime$1 = performance.now();
45
+ this.logger.info(`Cache hit for all ${workingHashes.length} hashes in ${locale} in ${(endTime$1 - startTime).toFixed(2)}ms`);
46
+ return {
47
+ translations: this.pickTranslations(cachedTranslations, workingHashes),
48
+ errors: [],
49
+ stats: {
50
+ total: workingHashes.length,
51
+ cached: cachedCount,
52
+ translated: 0,
53
+ failed: 0
54
+ }
55
+ };
56
+ }
57
+ this.logger.info(`Generating translations for ${uncachedHashes.length} uncached hashes in ${locale}...`);
58
+ const filteredMetadata = {
59
+ ...metadata,
60
+ entries: Object.fromEntries(uncachedHashes.map((hash) => [hash, metadata.entries[hash]]).filter(([_, entry]) => entry !== void 0))
61
+ };
62
+ if (this.pluralizationService) {
63
+ this.logger.info(`Processing pluralization for ${Object.keys(filteredMetadata.entries).length} entries...`);
64
+ const pluralStats = await this.pluralizationService.process(filteredMetadata);
65
+ this.logger.info(`Pluralization stats: ${pluralStats.pluralized} pluralized, ${pluralStats.rejected} rejected, ${pluralStats.failed} failed`);
66
+ }
67
+ const overriddenTranslations = {};
68
+ const hashesNeedingTranslation = [];
69
+ this.logger.debug(`[TRACE] Checking for overrides in ${uncachedHashes.length} entries`);
70
+ for (const hash of uncachedHashes) {
71
+ const entry = filteredMetadata.entries[hash];
72
+ if (!entry) continue;
73
+ if (entry.overrides && entry.overrides[locale]) {
74
+ overriddenTranslations[hash] = entry.overrides[locale];
75
+ this.logger.debug(`[TRACE] Using override for ${hash} in locale ${locale}: "${entry.overrides[locale]}"`);
76
+ } else hashesNeedingTranslation.push(hash);
77
+ }
78
+ const overrideCount = Object.keys(overriddenTranslations).length;
79
+ if (overrideCount > 0) this.logger.info(`Found ${overrideCount} override(s) for locale ${locale}, skipping AI translation for these entries`);
80
+ this.logger.debug(`[TRACE] Preparing ${hashesNeedingTranslation.length} entries for translation (after overrides)`);
81
+ const entriesToTranslate = this.prepareEntries(filteredMetadata, hashesNeedingTranslation);
82
+ this.logger.debug(`[TRACE] Prepared ${Object.keys(entriesToTranslate).length} entries`);
83
+ let newTranslations = { ...overriddenTranslations };
84
+ const errors = [];
85
+ if (locale === this.config.sourceLocale) {
86
+ this.logger.debug(`[TRACE] Source locale detected, returning sourceText for ${hashesNeedingTranslation.length} entries`);
87
+ for (const [hash, entry] of Object.entries(entriesToTranslate)) newTranslations[hash] = entry.text;
88
+ } else if (Object.keys(entriesToTranslate).length > 0) {
89
+ try {
90
+ this.logger.debug(`[TRACE] Calling translator.translate() for ${locale} with ${Object.keys(entriesToTranslate).length} entries`);
91
+ this.logger.debug(`[TRACE] About to await translator.translate()...`);
92
+ const translateStartTime = performance.now();
93
+ this.logger.debug(`[TRACE] Executing translator.translate() NOW`);
94
+ const translatedTexts = await this.translator.translate(locale, entriesToTranslate);
95
+ this.logger.debug(`[TRACE] translator.translate() returned`);
96
+ newTranslations = {
97
+ ...overriddenTranslations,
98
+ ...translatedTexts
99
+ };
100
+ const translateEndTime = performance.now();
101
+ this.logger.debug(`[TRACE] translator.translate() completed in ${(translateEndTime - translateStartTime).toFixed(2)}ms`);
102
+ this.logger.debug(`[TRACE] Received ${Object.keys(translatedTexts).length} translations (+ ${overrideCount} overrides)`);
103
+ } catch (error) {
104
+ this.logger.error(`Translation failed completely:`, error);
105
+ return {
106
+ translations: this.pickTranslations(cachedTranslations, workingHashes),
107
+ errors: [{
108
+ hash: "all",
109
+ sourceText: "all",
110
+ error: error instanceof Error ? error.message : "Unknown translation error"
111
+ }],
112
+ stats: {
113
+ total: workingHashes.length,
114
+ cached: cachedCount,
115
+ translated: 0,
116
+ failed: uncachedHashes.length
117
+ }
118
+ };
119
+ }
120
+ for (const hash of uncachedHashes) if (!newTranslations[hash]) {
121
+ const entry = filteredMetadata.entries[hash];
122
+ errors.push({
123
+ hash,
124
+ sourceText: entry?.sourceText || "",
125
+ error: "Translation not returned by translator"
126
+ });
127
+ }
128
+ }
129
+ if (this.useCache && Object.keys(newTranslations).length > 0) try {
130
+ this.logger.debug(`[TRACE] Updating cache with ${Object.keys(newTranslations).length} translations for ${locale}`);
131
+ const updateStartTime = performance.now();
132
+ await this.cache.update(locale, newTranslations);
133
+ const updateEndTime = performance.now();
134
+ this.logger.debug(`[TRACE] Cache update completed in ${(updateEndTime - updateStartTime).toFixed(2)}ms`);
135
+ this.logger.info(`Updated cache with ${Object.keys(newTranslations).length} translations for ${locale}`);
136
+ } catch (error) {
137
+ this.logger.error(`Failed to update cache:`, error);
138
+ }
139
+ const allTranslations = {
140
+ ...cachedTranslations,
141
+ ...newTranslations
142
+ };
143
+ const result = this.pickTranslations(allTranslations, workingHashes);
144
+ const endTime = performance.now();
145
+ this.logger.info(`Translation completed for ${locale}: ${Object.keys(newTranslations).length} new, ${cachedCount} cached, ${errors.length} errors in ${(endTime - startTime).toFixed(2)}ms`);
146
+ return {
147
+ translations: result,
148
+ errors,
149
+ stats: {
150
+ total: workingHashes.length,
151
+ cached: cachedCount,
152
+ translated: Object.keys(newTranslations).length,
153
+ failed: errors.length
154
+ }
155
+ };
156
+ }
157
+ /**
158
+ * Prepare metadata entries for translation
159
+ */
160
+ prepareEntries(metadata, hashes) {
161
+ const entries = {};
162
+ for (const hash of hashes) {
163
+ const entry = metadata.entries[hash];
164
+ if (entry) entries[hash] = {
165
+ text: entry.sourceText,
166
+ context: entry.context || {}
167
+ };
168
+ }
169
+ return entries;
170
+ }
171
+ /**
172
+ * Pick only requested translations from the full set
173
+ */
174
+ pickTranslations(allTranslations, requestedHashes) {
175
+ const result = {};
176
+ for (const hash of requestedHashes) if (allTranslations[hash]) result[hash] = allTranslations[hash];
177
+ return result;
178
+ }
179
+ };
180
+
181
+ //#endregion
182
+ exports.TranslationService = TranslationService;
@@ -0,0 +1,183 @@
1
+ import { PseudoTranslator } from "./pseudotranslator/index.mjs";
2
+ import { PluralizationService } from "./pluralization/service.mjs";
3
+
4
+ //#region src/translators/translation-service.ts
5
+ var TranslationService = class {
6
+ useCache = true;
7
+ pluralizationService;
8
+ constructor(translator, cache, config, logger) {
9
+ this.translator = translator;
10
+ this.cache = cache;
11
+ this.config = config;
12
+ this.logger = logger;
13
+ const isPseudo = this.translator instanceof PseudoTranslator;
14
+ this.useCache = !isPseudo;
15
+ if (this.config.pluralization?.enabled !== false && !isPseudo) {
16
+ this.logger.info("Initializing pluralization service...");
17
+ this.pluralizationService = new PluralizationService({
18
+ ...this.config.pluralization,
19
+ sourceLocale: this.config.sourceLocale
20
+ }, this.logger);
21
+ }
22
+ }
23
+ /**
24
+ * Translate entries to target locale
25
+ *
26
+ * @param locale Target locale (including source locale)
27
+ * @param metadata Metadata schema with all entries
28
+ * @param requestedHashes Optional: only translate specific hashes
29
+ * @returns Translation result with translations and errors
30
+ */
31
+ async translate(locale, metadata, requestedHashes) {
32
+ const startTime = performance.now();
33
+ const workingHashes = requestedHashes || Object.keys(metadata.entries);
34
+ this.logger.info(`Translation requested for ${workingHashes.length} hashes in locale: ${locale}`);
35
+ this.logger.debug(`[TRACE] Checking cache for locale: ${locale}`);
36
+ const cacheStartTime = performance.now();
37
+ const cachedTranslations = this.useCache ? await this.cache.get(locale) : {};
38
+ const cacheEndTime = performance.now();
39
+ this.logger.debug(`[TRACE] Cache check completed in ${(cacheEndTime - cacheStartTime).toFixed(2)}ms, found ${Object.keys(cachedTranslations).length} entries`);
40
+ const uncachedHashes = workingHashes.filter((hash) => !cachedTranslations[hash]);
41
+ this.logger.debug(`[TRACE] ${uncachedHashes.length} hashes need processing, ${workingHashes.length - uncachedHashes.length} are cached`);
42
+ const cachedCount = workingHashes.length - uncachedHashes.length;
43
+ if (uncachedHashes.length === 0) {
44
+ const endTime$1 = performance.now();
45
+ this.logger.info(`Cache hit for all ${workingHashes.length} hashes in ${locale} in ${(endTime$1 - startTime).toFixed(2)}ms`);
46
+ return {
47
+ translations: this.pickTranslations(cachedTranslations, workingHashes),
48
+ errors: [],
49
+ stats: {
50
+ total: workingHashes.length,
51
+ cached: cachedCount,
52
+ translated: 0,
53
+ failed: 0
54
+ }
55
+ };
56
+ }
57
+ this.logger.info(`Generating translations for ${uncachedHashes.length} uncached hashes in ${locale}...`);
58
+ const filteredMetadata = {
59
+ ...metadata,
60
+ entries: Object.fromEntries(uncachedHashes.map((hash) => [hash, metadata.entries[hash]]).filter(([_, entry]) => entry !== void 0))
61
+ };
62
+ if (this.pluralizationService) {
63
+ this.logger.info(`Processing pluralization for ${Object.keys(filteredMetadata.entries).length} entries...`);
64
+ const pluralStats = await this.pluralizationService.process(filteredMetadata);
65
+ this.logger.info(`Pluralization stats: ${pluralStats.pluralized} pluralized, ${pluralStats.rejected} rejected, ${pluralStats.failed} failed`);
66
+ }
67
+ const overriddenTranslations = {};
68
+ const hashesNeedingTranslation = [];
69
+ this.logger.debug(`[TRACE] Checking for overrides in ${uncachedHashes.length} entries`);
70
+ for (const hash of uncachedHashes) {
71
+ const entry = filteredMetadata.entries[hash];
72
+ if (!entry) continue;
73
+ if (entry.overrides && entry.overrides[locale]) {
74
+ overriddenTranslations[hash] = entry.overrides[locale];
75
+ this.logger.debug(`[TRACE] Using override for ${hash} in locale ${locale}: "${entry.overrides[locale]}"`);
76
+ } else hashesNeedingTranslation.push(hash);
77
+ }
78
+ const overrideCount = Object.keys(overriddenTranslations).length;
79
+ if (overrideCount > 0) this.logger.info(`Found ${overrideCount} override(s) for locale ${locale}, skipping AI translation for these entries`);
80
+ this.logger.debug(`[TRACE] Preparing ${hashesNeedingTranslation.length} entries for translation (after overrides)`);
81
+ const entriesToTranslate = this.prepareEntries(filteredMetadata, hashesNeedingTranslation);
82
+ this.logger.debug(`[TRACE] Prepared ${Object.keys(entriesToTranslate).length} entries`);
83
+ let newTranslations = { ...overriddenTranslations };
84
+ const errors = [];
85
+ if (locale === this.config.sourceLocale) {
86
+ this.logger.debug(`[TRACE] Source locale detected, returning sourceText for ${hashesNeedingTranslation.length} entries`);
87
+ for (const [hash, entry] of Object.entries(entriesToTranslate)) newTranslations[hash] = entry.text;
88
+ } else if (Object.keys(entriesToTranslate).length > 0) {
89
+ try {
90
+ this.logger.debug(`[TRACE] Calling translator.translate() for ${locale} with ${Object.keys(entriesToTranslate).length} entries`);
91
+ this.logger.debug(`[TRACE] About to await translator.translate()...`);
92
+ const translateStartTime = performance.now();
93
+ this.logger.debug(`[TRACE] Executing translator.translate() NOW`);
94
+ const translatedTexts = await this.translator.translate(locale, entriesToTranslate);
95
+ this.logger.debug(`[TRACE] translator.translate() returned`);
96
+ newTranslations = {
97
+ ...overriddenTranslations,
98
+ ...translatedTexts
99
+ };
100
+ const translateEndTime = performance.now();
101
+ this.logger.debug(`[TRACE] translator.translate() completed in ${(translateEndTime - translateStartTime).toFixed(2)}ms`);
102
+ this.logger.debug(`[TRACE] Received ${Object.keys(translatedTexts).length} translations (+ ${overrideCount} overrides)`);
103
+ } catch (error) {
104
+ this.logger.error(`Translation failed completely:`, error);
105
+ return {
106
+ translations: this.pickTranslations(cachedTranslations, workingHashes),
107
+ errors: [{
108
+ hash: "all",
109
+ sourceText: "all",
110
+ error: error instanceof Error ? error.message : "Unknown translation error"
111
+ }],
112
+ stats: {
113
+ total: workingHashes.length,
114
+ cached: cachedCount,
115
+ translated: 0,
116
+ failed: uncachedHashes.length
117
+ }
118
+ };
119
+ }
120
+ for (const hash of uncachedHashes) if (!newTranslations[hash]) {
121
+ const entry = filteredMetadata.entries[hash];
122
+ errors.push({
123
+ hash,
124
+ sourceText: entry?.sourceText || "",
125
+ error: "Translation not returned by translator"
126
+ });
127
+ }
128
+ }
129
+ if (this.useCache && Object.keys(newTranslations).length > 0) try {
130
+ this.logger.debug(`[TRACE] Updating cache with ${Object.keys(newTranslations).length} translations for ${locale}`);
131
+ const updateStartTime = performance.now();
132
+ await this.cache.update(locale, newTranslations);
133
+ const updateEndTime = performance.now();
134
+ this.logger.debug(`[TRACE] Cache update completed in ${(updateEndTime - updateStartTime).toFixed(2)}ms`);
135
+ this.logger.info(`Updated cache with ${Object.keys(newTranslations).length} translations for ${locale}`);
136
+ } catch (error) {
137
+ this.logger.error(`Failed to update cache:`, error);
138
+ }
139
+ const allTranslations = {
140
+ ...cachedTranslations,
141
+ ...newTranslations
142
+ };
143
+ const result = this.pickTranslations(allTranslations, workingHashes);
144
+ const endTime = performance.now();
145
+ this.logger.info(`Translation completed for ${locale}: ${Object.keys(newTranslations).length} new, ${cachedCount} cached, ${errors.length} errors in ${(endTime - startTime).toFixed(2)}ms`);
146
+ return {
147
+ translations: result,
148
+ errors,
149
+ stats: {
150
+ total: workingHashes.length,
151
+ cached: cachedCount,
152
+ translated: Object.keys(newTranslations).length,
153
+ failed: errors.length
154
+ }
155
+ };
156
+ }
157
+ /**
158
+ * Prepare metadata entries for translation
159
+ */
160
+ prepareEntries(metadata, hashes) {
161
+ const entries = {};
162
+ for (const hash of hashes) {
163
+ const entry = metadata.entries[hash];
164
+ if (entry) entries[hash] = {
165
+ text: entry.sourceText,
166
+ context: entry.context || {}
167
+ };
168
+ }
169
+ return entries;
170
+ }
171
+ /**
172
+ * Pick only requested translations from the full set
173
+ */
174
+ pickTranslations(allTranslations, requestedHashes) {
175
+ const result = {};
176
+ for (const hash of requestedHashes) if (allTranslations[hash]) result[hash] = allTranslations[hash];
177
+ return result;
178
+ }
179
+ };
180
+
181
+ //#endregion
182
+ export { TranslationService };
183
+ //# sourceMappingURL=translation-service.mjs.map