@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":"translation-service.mjs","names":["translator: Translator<any>","cache: TranslationCache","config: TranslationServiceConfig","logger: Logger","endTime","filteredMetadata: MetadataSchema","overriddenTranslations: Record<string, string>","hashesNeedingTranslation: string[]","newTranslations: Record<string, string>","errors: TranslationError[]","entries: Record<string, TranslatableEntry>","result: Record<string, string>"],"sources":["../../src/translators/translation-service.ts"],"sourcesContent":["/**\n * TranslationService - Main orchestrator for translation workflow\n *\n * Responsibilities:\n * - Coordinates between metadata, cache, and translator\n * - Determines what needs translation\n * - Handles caching strategy\n * - Manages partial failures\n */\n\nimport type { TranslationCache } from \"./cache\";\nimport type { TranslatableEntry, Translator } from \"./api\";\nimport type { MetadataSchema } from \"../types\";\nimport {\n type PluralizationConfig,\n PluralizationService,\n} from \"./pluralization\";\nimport type { Logger } from \"../utils/logger\";\nimport type { LocaleCode } from \"lingo.dev/spec\";\nimport { PseudoTranslator } from \"./pseudotranslator\";\n\nexport interface TranslationServiceConfig {\n /**\n * Source locale (e.g., \"en\")\n */\n sourceLocale: LocaleCode;\n\n /**\n * Pluralization configuration\n * If provided, enables automatic pluralization of source messages\n */\n pluralization: Omit<PluralizationConfig, \"sourceLocale\">;\n}\n\nexport interface TranslationResult {\n /**\n * Successfully translated entries (hash -> translated text)\n */\n translations: Record<string, string>;\n\n errors: TranslationError[];\n\n stats: {\n total: number;\n cached: number;\n translated: number;\n failed: number;\n };\n}\n\nexport interface TranslationError {\n hash: string;\n sourceText: string;\n error: string;\n}\n\nexport class TranslationService {\n private useCache = true;\n private pluralizationService?: PluralizationService;\n\n constructor(\n private translator: Translator<any>,\n private cache: TranslationCache,\n private config: TranslationServiceConfig,\n private logger: Logger,\n ) {\n const isPseudo = this.translator instanceof PseudoTranslator;\n this.useCache = !isPseudo;\n\n // Initialize pluralization service if enabled\n // Do this once at construction to avoid repeated API key validation and model creation\n if (this.config.pluralization?.enabled !== false && !isPseudo) {\n this.logger.info(\"Initializing pluralization service...\");\n this.pluralizationService = new PluralizationService(\n {\n ...this.config.pluralization,\n sourceLocale: this.config.sourceLocale,\n },\n this.logger,\n );\n }\n }\n\n /**\n * Translate entries to target locale\n *\n * @param locale Target locale (including source locale)\n * @param metadata Metadata schema with all entries\n * @param requestedHashes Optional: only translate specific hashes\n * @returns Translation result with translations and errors\n */\n async translate(\n locale: LocaleCode,\n metadata: MetadataSchema,\n requestedHashes?: string[],\n ): Promise<TranslationResult> {\n const startTime = performance.now();\n\n // Step 1: Determine which hashes we need to work with\n const workingHashes = requestedHashes || Object.keys(metadata.entries);\n\n this.logger.info(\n `Translation requested for ${workingHashes.length} hashes in locale: ${locale}`,\n );\n\n // Step 2: Check cache first (same for all locales, including source)\n this.logger.debug(`[TRACE] Checking cache for locale: ${locale}`);\n const cacheStartTime = performance.now();\n const cachedTranslations = this.useCache\n ? await this.cache.get(locale)\n : {};\n const cacheEndTime = performance.now();\n this.logger.debug(\n `[TRACE] Cache check completed in ${(cacheEndTime - cacheStartTime).toFixed(2)}ms, found ${Object.keys(cachedTranslations).length} entries`,\n );\n\n // Step 3: Determine what needs translation/pluralization\n const uncachedHashes = workingHashes.filter(\n (hash) => !cachedTranslations[hash],\n );\n this.logger.debug(\n `[TRACE] ${uncachedHashes.length} hashes need processing, ${workingHashes.length - uncachedHashes.length} are cached`,\n );\n\n const cachedCount = workingHashes.length - uncachedHashes.length;\n\n if (uncachedHashes.length === 0) {\n // All cached!\n const endTime = performance.now();\n this.logger.info(\n `Cache hit for all ${workingHashes.length} hashes in ${locale} in ${(endTime - startTime).toFixed(2)}ms`,\n );\n\n return {\n translations: this.pickTranslations(cachedTranslations, workingHashes),\n errors: [],\n stats: {\n total: workingHashes.length,\n cached: cachedCount,\n translated: 0,\n failed: 0,\n },\n };\n }\n\n this.logger.info(\n `Generating translations for ${uncachedHashes.length} uncached hashes in ${locale}...`,\n );\n\n // Step 4: Filter metadata to only uncached entries\n const filteredMetadata: MetadataSchema = {\n ...metadata,\n entries: Object.fromEntries(\n uncachedHashes\n .map((hash) => [hash, metadata.entries[hash]])\n .filter(([_, entry]) => entry !== undefined),\n ),\n };\n\n // Step 5: Process pluralization for filtered entries\n if (this.pluralizationService) {\n this.logger.info(\n `Processing pluralization for ${Object.keys(filteredMetadata.entries).length} entries...`,\n );\n const pluralStats =\n await this.pluralizationService.process(filteredMetadata);\n this.logger.info(\n `Pluralization stats: ${pluralStats.pluralized} pluralized, ${pluralStats.rejected} rejected, ${pluralStats.failed} failed`,\n );\n }\n\n // Step 6: Separate overridden entries from entries that need translation\n const overriddenTranslations: Record<string, string> = {};\n const hashesNeedingTranslation: string[] = [];\n\n this.logger.debug(\n `[TRACE] Checking for overrides in ${uncachedHashes.length} entries`,\n );\n\n for (const hash of uncachedHashes) {\n const entry = filteredMetadata.entries[hash];\n if (!entry) continue;\n\n // Check if this entry has an override for the current locale\n if (entry.overrides && entry.overrides[locale]) {\n overriddenTranslations[hash] = entry.overrides[locale];\n this.logger.debug(\n `[TRACE] Using override for ${hash} in locale ${locale}: \"${entry.overrides[locale]}\"`,\n );\n } else {\n hashesNeedingTranslation.push(hash);\n }\n }\n\n const overrideCount = Object.keys(overriddenTranslations).length;\n if (overrideCount > 0) {\n this.logger.info(\n `Found ${overrideCount} override(s) for locale ${locale}, skipping AI translation for these entries`,\n );\n }\n\n // Step 7: Prepare entries for translation (excluding overridden ones)\n this.logger.debug(\n `[TRACE] Preparing ${hashesNeedingTranslation.length} entries for translation (after overrides)`,\n );\n const entriesToTranslate = this.prepareEntries(\n filteredMetadata,\n hashesNeedingTranslation,\n );\n this.logger.debug(\n `[TRACE] Prepared ${Object.keys(entriesToTranslate).length} entries`,\n );\n\n // Step 8: Translate or return source text\n let newTranslations: Record<string, string> = { ...overriddenTranslations };\n const errors: TranslationError[] = [];\n\n if (locale === this.config.sourceLocale) {\n // For source locale, just return the (possibly pluralized) sourceText\n this.logger.debug(\n `[TRACE] Source locale detected, returning sourceText for ${hashesNeedingTranslation.length} entries`,\n );\n for (const [hash, entry] of Object.entries(entriesToTranslate)) {\n newTranslations[hash] = entry.text;\n }\n } else if (Object.keys(entriesToTranslate).length > 0) {\n // For other locales, translate only entries without overrides\n try {\n this.logger.debug(\n `[TRACE] Calling translator.translate() for ${locale} with ${Object.keys(entriesToTranslate).length} entries`,\n );\n this.logger.debug(`[TRACE] About to await translator.translate()...`);\n const translateStartTime = performance.now();\n this.logger.debug(`[TRACE] Executing translator.translate() NOW`);\n const translatedTexts = await this.translator.translate(\n locale,\n entriesToTranslate,\n );\n this.logger.debug(`[TRACE] translator.translate() returned`);\n\n // Merge translated texts with overridden translations\n newTranslations = { ...overriddenTranslations, ...translatedTexts };\n\n const translateEndTime = performance.now();\n this.logger.debug(\n `[TRACE] translator.translate() completed in ${(translateEndTime - translateStartTime).toFixed(2)}ms`,\n );\n this.logger.debug(\n `[TRACE] Received ${Object.keys(translatedTexts).length} translations (+ ${overrideCount} overrides)`,\n );\n } catch (error) {\n // Complete failure - log and return what we have from cache\n this.logger.error(`Translation failed completely:`, error);\n\n return {\n translations: this.pickTranslations(\n cachedTranslations,\n workingHashes,\n ),\n errors: [\n {\n hash: \"all\",\n sourceText: \"all\",\n error:\n error instanceof Error\n ? error.message\n : \"Unknown translation error\",\n },\n ],\n stats: {\n total: workingHashes.length,\n cached: cachedCount,\n translated: 0,\n failed: uncachedHashes.length,\n },\n };\n }\n\n // Check for partial failures (some hashes didn't get translated)\n for (const hash of uncachedHashes) {\n if (!newTranslations[hash]) {\n const entry = filteredMetadata.entries[hash];\n errors.push({\n hash,\n sourceText: entry?.sourceText || \"\",\n error: \"Translation not returned by translator\",\n });\n }\n }\n }\n\n // Step 5: Update cache with successful translations (skip for pseudo)\n if (this.useCache && Object.keys(newTranslations).length > 0) {\n try {\n this.logger.debug(\n `[TRACE] Updating cache with ${Object.keys(newTranslations).length} translations for ${locale}`,\n );\n const updateStartTime = performance.now();\n await this.cache.update(locale, newTranslations);\n const updateEndTime = performance.now();\n this.logger.debug(\n `[TRACE] Cache update completed in ${(updateEndTime - updateStartTime).toFixed(2)}ms`,\n );\n this.logger.info(\n `Updated cache with ${Object.keys(newTranslations).length} translations for ${locale}`,\n );\n } catch (error) {\n this.logger.error(`Failed to update cache:`, error);\n // Don't fail the request if cache update fails\n }\n }\n\n // Step 6: Merge and return\n const allTranslations = { ...cachedTranslations, ...newTranslations };\n const result = this.pickTranslations(allTranslations, workingHashes);\n\n const endTime = performance.now();\n this.logger.info(\n `Translation completed for ${locale}: ${Object.keys(newTranslations).length} new, ${cachedCount} cached, ${errors.length} errors in ${(endTime - startTime).toFixed(2)}ms`,\n );\n\n return {\n translations: result,\n errors,\n stats: {\n total: workingHashes.length,\n cached: cachedCount,\n translated: Object.keys(newTranslations).length,\n failed: errors.length,\n },\n };\n }\n\n /**\n * Prepare metadata entries for translation\n */\n private prepareEntries(\n metadata: MetadataSchema,\n hashes: string[],\n ): Record<string, TranslatableEntry> {\n const entries: Record<string, TranslatableEntry> = {};\n\n for (const hash of hashes) {\n const entry = metadata.entries[hash];\n if (entry) {\n entries[hash] = {\n text: entry.sourceText,\n context: entry.context || {},\n };\n }\n }\n\n return entries;\n }\n\n /**\n * Pick only requested translations from the full set\n */\n // TODO (AleksandrSl 14/12/2025): SHould I use this in the build somehow?\n private pickTranslations(\n allTranslations: Record<string, string>,\n requestedHashes: string[],\n ): Record<string, string> {\n const result: Record<string, string> = {};\n\n for (const hash of requestedHashes) {\n if (allTranslations[hash]) {\n result[hash] = allTranslations[hash];\n }\n }\n\n return result;\n }\n}\n"],"mappings":"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"}
@@ -0,0 +1,49 @@
1
+ const require_index = require('./pseudotranslator/index.cjs');
2
+ const require_service = require('./lingo/service.cjs');
3
+
4
+ //#region src/translators/translator-factory.ts
5
+ /**
6
+ * Create a translator instance based on configuration
7
+ *
8
+ * Development mode behavior:
9
+ * - If translator is "pseudo" or dev.usePseudotranslator is true, use pseudotranslator
10
+ * - If API keys are missing, auto-fallback to pseudotranslator (with warning)
11
+ * - Otherwise, create real translator
12
+ *
13
+ * Production mode behavior:
14
+ * - Always require real translator with valid API keys
15
+ * - Throw error if API keys are missing
16
+ *
17
+ * Note: Translators are stateless and don't handle caching.
18
+ * Caching is handled by TranslationService layer.
19
+ *
20
+ * API key validation is now done in the LingoTranslator constructor
21
+ * which validates and fetches all keys once at initialization.
22
+ */
23
+ function createTranslator(config, logger) {
24
+ const isDev = config.environment === "development";
25
+ if (isDev && config.dev?.usePseudotranslator) {
26
+ logger.info("📝 Using pseudotranslator (dev.usePseudotranslator enabled)");
27
+ return new require_index.PseudoTranslator({ delayMedian: 100 }, logger);
28
+ }
29
+ try {
30
+ const models = config.models;
31
+ logger.info(`Creating Lingo translator with models: ${JSON.stringify(models)}`);
32
+ return new require_service.Service({
33
+ models,
34
+ sourceLocale: config.sourceLocale,
35
+ prompt: config.prompt
36
+ }, logger);
37
+ } catch (error) {
38
+ if (isDev) {
39
+ const errorMsg = error instanceof Error ? error.message : String(error);
40
+ logger.error(`\n❌ [Lingo] Translation setup error: ${errorMsg}\n`);
41
+ logger.warn("⚠️ [Lingo] Auto-fallback to pseudotranslator in development mode.\n Set the required API keys for real translations.\n");
42
+ return new require_index.PseudoTranslator({ delayMedian: 100 }, logger);
43
+ }
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ //#endregion
49
+ exports.createTranslator = createTranslator;
@@ -0,0 +1,50 @@
1
+ import { PseudoTranslator } from "./pseudotranslator/index.mjs";
2
+ import { Service } from "./lingo/service.mjs";
3
+
4
+ //#region src/translators/translator-factory.ts
5
+ /**
6
+ * Create a translator instance based on configuration
7
+ *
8
+ * Development mode behavior:
9
+ * - If translator is "pseudo" or dev.usePseudotranslator is true, use pseudotranslator
10
+ * - If API keys are missing, auto-fallback to pseudotranslator (with warning)
11
+ * - Otherwise, create real translator
12
+ *
13
+ * Production mode behavior:
14
+ * - Always require real translator with valid API keys
15
+ * - Throw error if API keys are missing
16
+ *
17
+ * Note: Translators are stateless and don't handle caching.
18
+ * Caching is handled by TranslationService layer.
19
+ *
20
+ * API key validation is now done in the LingoTranslator constructor
21
+ * which validates and fetches all keys once at initialization.
22
+ */
23
+ function createTranslator(config, logger) {
24
+ const isDev = config.environment === "development";
25
+ if (isDev && config.dev?.usePseudotranslator) {
26
+ logger.info("📝 Using pseudotranslator (dev.usePseudotranslator enabled)");
27
+ return new PseudoTranslator({ delayMedian: 100 }, logger);
28
+ }
29
+ try {
30
+ const models = config.models;
31
+ logger.info(`Creating Lingo translator with models: ${JSON.stringify(models)}`);
32
+ return new Service({
33
+ models,
34
+ sourceLocale: config.sourceLocale,
35
+ prompt: config.prompt
36
+ }, logger);
37
+ } catch (error) {
38
+ if (isDev) {
39
+ const errorMsg = error instanceof Error ? error.message : String(error);
40
+ logger.error(`\n❌ [Lingo] Translation setup error: ${errorMsg}\n`);
41
+ logger.warn("⚠️ [Lingo] Auto-fallback to pseudotranslator in development mode.\n Set the required API keys for real translations.\n");
42
+ return new PseudoTranslator({ delayMedian: 100 }, logger);
43
+ }
44
+ throw error;
45
+ }
46
+ }
47
+
48
+ //#endregion
49
+ export { createTranslator };
50
+ //# sourceMappingURL=translator-factory.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"translator-factory.mjs","names":[],"sources":["../../src/translators/translator-factory.ts"],"sourcesContent":["/**\n * Factory for creating translators based on configuration\n */\n\nimport type { Translator } from \"./api\";\nimport { PseudoTranslator } from \"./pseudotranslator\";\nimport { Service } from \"./lingo\";\nimport { Logger } from \"../utils/logger\";\nimport type { LocaleCode } from \"lingo.dev/spec\";\nimport type { LingoEnvironment } from \"../types\";\n\ninterface TranslatorFactoryConfig {\n sourceLocale: LocaleCode;\n models: \"lingo.dev\" | Record<string, string>;\n prompt?: string;\n environment: LingoEnvironment;\n dev?: {\n usePseudotranslator?: boolean;\n };\n}\n\n/**\n * Create a translator instance based on configuration\n *\n * Development mode behavior:\n * - If translator is \"pseudo\" or dev.usePseudotranslator is true, use pseudotranslator\n * - If API keys are missing, auto-fallback to pseudotranslator (with warning)\n * - Otherwise, create real translator\n *\n * Production mode behavior:\n * - Always require real translator with valid API keys\n * - Throw error if API keys are missing\n *\n * Note: Translators are stateless and don't handle caching.\n * Caching is handled by TranslationService layer.\n *\n * API key validation is now done in the LingoTranslator constructor\n * which validates and fetches all keys once at initialization.\n */\nexport function createTranslator(\n config: TranslatorFactoryConfig,\n logger: Logger,\n): Translator<unknown> {\n const isDev = config.environment === \"development\";\n\n // 1. Explicit dev override takes precedence\n if (isDev && config.dev?.usePseudotranslator) {\n logger.info(\"📝 Using pseudotranslator (dev.usePseudotranslator enabled)\");\n return new PseudoTranslator({ delayMedian: 100 }, logger);\n }\n\n // 2. Try to create real translator\n // LingoTranslator constructor will validate and fetch API keys\n // If validation fails, it will throw an error with helpful message\n try {\n const models = config.models;\n\n logger.info(\n `Creating Lingo translator with models: ${JSON.stringify(models)}`,\n );\n\n return new Service(\n {\n models,\n sourceLocale: config.sourceLocale,\n prompt: config.prompt,\n },\n logger,\n );\n } catch (error) {\n // 3. Auto-fallback in dev mode if creation fails\n if (isDev) {\n // Use console.error to ensure visibility in all contexts (loader, server, etc.)\n const errorMsg = error instanceof Error ? error.message : String(error);\n logger.error(`\\n❌ [Lingo] Translation setup error: ${errorMsg}\\n`);\n logger.warn(\n `⚠️ [Lingo] Auto-fallback to pseudotranslator in development mode.\\n` +\n ` Set the required API keys for real translations.\\n`,\n );\n\n return new PseudoTranslator({ delayMedian: 100 }, logger);\n }\n\n // 4. Fail in production\n throw error;\n }\n}\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AAuCA,SAAgB,iBACd,QACA,QACqB;CACrB,MAAM,QAAQ,OAAO,gBAAgB;AAGrC,KAAI,SAAS,OAAO,KAAK,qBAAqB;AAC5C,SAAO,KAAK,8DAA8D;AAC1E,SAAO,IAAI,iBAAiB,EAAE,aAAa,KAAK,EAAE,OAAO;;AAM3D,KAAI;EACF,MAAM,SAAS,OAAO;AAEtB,SAAO,KACL,0CAA0C,KAAK,UAAU,OAAO,GACjE;AAED,SAAO,IAAI,QACT;GACE;GACA,cAAc,OAAO;GACrB,QAAQ,OAAO;GAChB,EACD,OACD;UACM,OAAO;AAEd,MAAI,OAAO;GAET,MAAM,WAAW,iBAAiB,QAAQ,MAAM,UAAU,OAAO,MAAM;AACvE,UAAO,MAAM,wCAAwC,SAAS,IAAI;AAClE,UAAO,KACL,4HAED;AAED,UAAO,IAAI,iBAAiB,EAAE,aAAa,KAAK,EAAE,OAAO;;AAI3D,QAAM"}
@@ -0,0 +1,161 @@
1
+ import { PluralizationConfig } from "./translators/pluralization/types.cjs";
2
+ import { LocaleCode } from "lingo.dev/spec";
3
+
4
+ //#region src/types.d.ts
5
+
6
+ /**
7
+ * Cookie configuration for locale persistence
8
+ */
9
+ interface CookieConfig {
10
+ /**
11
+ * Name of the cookie to store the locale
12
+ * @default 'locale'
13
+ */
14
+ name: string;
15
+ /**
16
+ * Maximum age of the cookie in seconds
17
+ * @default 31536000 (1 year)
18
+ */
19
+ maxAge: number;
20
+ }
21
+ /**
22
+ * Locale persistence configuration
23
+ * Currently only supports cookie-based persistence
24
+ */
25
+ type LocalePersistenceConfig = {
26
+ type: "cookie";
27
+ config: CookieConfig;
28
+ };
29
+ /**
30
+ * Field that we require users to fill in the config. The rest could be taken from defaults.
31
+ */
32
+ type LingoConfigRequiredFields = "sourceLocale" | "targetLocales";
33
+ type LingoInternalFields = "environment" | "cacheType";
34
+ /**
35
+ * Configuration for the Lingo compiler
36
+ */
37
+ type PartialLingoConfig = Pick<LingoConfig, LingoConfigRequiredFields> & Partial<Omit<LingoConfig, LingoConfigRequiredFields | "dev" | LingoInternalFields> & {
38
+ dev: Partial<LingoConfig["dev"]>;
39
+ }>;
40
+ type LingoEnvironment = "development" | "production";
41
+ /**
42
+ * Lingo config with all the defaults applied
43
+ */
44
+ type LingoConfig = {
45
+ /**
46
+ * Root directory of the source code
47
+ */
48
+ sourceRoot: string;
49
+ /**
50
+ * Directory for lingo files (.lingo/)
51
+ */
52
+ lingoDir: string;
53
+ /**
54
+ * Environment mode
55
+ * Determines metadata file naming and translator behavior
56
+ *
57
+ * @default "production"
58
+ * @internal
59
+ */
60
+ environment: LingoEnvironment;
61
+ /**
62
+ * Cache implementation type
63
+ * - "local": Local file system cache (default)
64
+ * - "remote": Remote cache (future)
65
+ *
66
+ * @default "local"
67
+ * @internal Since we do not support more types, there is no need to make it public, but it allows keeping the config in the single place
68
+ */
69
+ cacheType: "local";
70
+ /**
71
+ * The locale to translate from.
72
+ *
73
+ * This must match one of the following formats:
74
+ *
75
+ * - [ISO 639-1 language code](https://en.wikipedia.org/wiki/ISO_639-1) (e.g., `"en"`)
76
+ * - [IETF BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) (e.g., `"en-US"`)
77
+ *
78
+ * @default "en"
79
+ */
80
+ sourceLocale: LocaleCode;
81
+ /**
82
+ * The locale(s) to translate to.
83
+ *
84
+ * Each locale must match one of the following formats:
85
+ *
86
+ * - [ISO 639-1 language code](https://en.wikipedia.org/wiki/ISO_639-1) (e.g., `"en"`)
87
+ * - [IETF BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) (e.g., `"en-US"`)
88
+ *
89
+ * @default ["es"]
90
+ */
91
+ targetLocales: LocaleCode[];
92
+ /**
93
+ * Whether to require 'use i18n' directive
94
+ */
95
+ useDirective: boolean;
96
+ /**
97
+ * Model configuration for lingo translator
98
+ * - Use "lingo.dev" for Lingo.dev Engine (recommended)
99
+ * - Use locale-pair mapping for direct LLM providers
100
+ *
101
+ * Examples:
102
+ * - "lingo.dev"
103
+ * - { "en:es": "google:gemini-2.0-flash", "*:*": "groq:llama3-8b-8192" }
104
+ *
105
+ * @default "lingo.dev"
106
+ */
107
+ models: "lingo.dev" | Record<string, string>;
108
+ /**
109
+ * Custom translation prompt for lingo translator
110
+ * Use {SOURCE_LOCALE} and {TARGET_LOCALE} placeholders
111
+ */
112
+ prompt?: string;
113
+ /**
114
+ * Pluralization configuration
115
+ * Automatically detects and converts messages to ICU MessageFormat
116
+ */
117
+ pluralization: Omit<PluralizationConfig, "sourceLocale">;
118
+ /**
119
+ * Development-specific settings
120
+ */
121
+ dev: {
122
+ /**
123
+ * Use pseudotranslator in development instead of real translator
124
+ * Useful for:
125
+ * - Testing i18n without API calls
126
+ * - Verifying correct elements are translated
127
+ * - Saving AI tokens during development
128
+ *
129
+ * @default false
130
+ */
131
+ usePseudotranslator?: boolean;
132
+ /**
133
+ * Starting port for the translation server in development mode
134
+ * Server will try this port first, then increment if unavailable
135
+ *
136
+ * @default 60000
137
+ */
138
+ translationServerStartPort: number;
139
+ translationServerUrl?: string;
140
+ };
141
+ /**
142
+ * Locale persistence configuration
143
+ * Defines how locale changes should be persisted
144
+ *
145
+ * @default { type: 'cookie', config: { name: 'locale' } }
146
+ */
147
+ localePersistence: LocalePersistenceConfig;
148
+ /**
149
+ * Build mode for CI/production
150
+ * - "translate": Generate translations at build time, fail if translation fails (default)
151
+ * - "cache-only": Only use cached translations, fail if cache is incomplete
152
+ *
153
+ * Can be overridden by LINGO_BUILD_MODE environment variable
154
+ *
155
+ * @default "translate"
156
+ */
157
+ buildMode: "translate" | "cache-only";
158
+ };
159
+ //#endregion
160
+ export { CookieConfig, PartialLingoConfig };
161
+ //# sourceMappingURL=types.d.cts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.cts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;AA4BA;AAKA;AAEA;AAKY,UA9BK,YAAA,CA8Ba;EAAQ;;;;EAIhC,IAAA,EAAA,MAAA;EAAoC;;;;EAHxC,MAAA,EAAA,MAAA;;AASF;AAKA;;;AAoDiB,KA/EL,uBAAA,GA+EK;EAkBO,IAAA,EAAA,QAAA;EAYF,MAAA,EA7G0C,YA6G1C;CAAL;;;;KAxGL,yBAAA;KAEA,mBAAA;;;;KAKA,kBAAA,GAAqB,KAAK,aAAa,6BACjD,QACE,KACE,aACA,oCAAoC;OAE/B,QAAQ;;KAIP,gBAAA;;;;KAKA,WAAA;;;;;;;;;;;;;;;;eAkBG;;;;;;;;;;;;;;;;;;;;gBAsBC;;;;;;;;;;;iBAYC;;;;;;;;;;;;;;;;wBAkBO;;;;;;;;;;iBAYP,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAkCD"}
@@ -0,0 +1,161 @@
1
+ import { PluralizationConfig } from "./translators/pluralization/types.mjs";
2
+ import { LocaleCode } from "lingo.dev/spec";
3
+
4
+ //#region src/types.d.ts
5
+
6
+ /**
7
+ * Cookie configuration for locale persistence
8
+ */
9
+ interface CookieConfig {
10
+ /**
11
+ * Name of the cookie to store the locale
12
+ * @default 'locale'
13
+ */
14
+ name: string;
15
+ /**
16
+ * Maximum age of the cookie in seconds
17
+ * @default 31536000 (1 year)
18
+ */
19
+ maxAge: number;
20
+ }
21
+ /**
22
+ * Locale persistence configuration
23
+ * Currently only supports cookie-based persistence
24
+ */
25
+ type LocalePersistenceConfig = {
26
+ type: "cookie";
27
+ config: CookieConfig;
28
+ };
29
+ /**
30
+ * Field that we require users to fill in the config. The rest could be taken from defaults.
31
+ */
32
+ type LingoConfigRequiredFields = "sourceLocale" | "targetLocales";
33
+ type LingoInternalFields = "environment" | "cacheType";
34
+ /**
35
+ * Configuration for the Lingo compiler
36
+ */
37
+ type PartialLingoConfig = Pick<LingoConfig, LingoConfigRequiredFields> & Partial<Omit<LingoConfig, LingoConfigRequiredFields | "dev" | LingoInternalFields> & {
38
+ dev: Partial<LingoConfig["dev"]>;
39
+ }>;
40
+ type LingoEnvironment = "development" | "production";
41
+ /**
42
+ * Lingo config with all the defaults applied
43
+ */
44
+ type LingoConfig = {
45
+ /**
46
+ * Root directory of the source code
47
+ */
48
+ sourceRoot: string;
49
+ /**
50
+ * Directory for lingo files (.lingo/)
51
+ */
52
+ lingoDir: string;
53
+ /**
54
+ * Environment mode
55
+ * Determines metadata file naming and translator behavior
56
+ *
57
+ * @default "production"
58
+ * @internal
59
+ */
60
+ environment: LingoEnvironment;
61
+ /**
62
+ * Cache implementation type
63
+ * - "local": Local file system cache (default)
64
+ * - "remote": Remote cache (future)
65
+ *
66
+ * @default "local"
67
+ * @internal Since we do not support more types, there is no need to make it public, but it allows keeping the config in the single place
68
+ */
69
+ cacheType: "local";
70
+ /**
71
+ * The locale to translate from.
72
+ *
73
+ * This must match one of the following formats:
74
+ *
75
+ * - [ISO 639-1 language code](https://en.wikipedia.org/wiki/ISO_639-1) (e.g., `"en"`)
76
+ * - [IETF BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) (e.g., `"en-US"`)
77
+ *
78
+ * @default "en"
79
+ */
80
+ sourceLocale: LocaleCode;
81
+ /**
82
+ * The locale(s) to translate to.
83
+ *
84
+ * Each locale must match one of the following formats:
85
+ *
86
+ * - [ISO 639-1 language code](https://en.wikipedia.org/wiki/ISO_639-1) (e.g., `"en"`)
87
+ * - [IETF BCP 47 language tag](https://en.wikipedia.org/wiki/IETF_language_tag) (e.g., `"en-US"`)
88
+ *
89
+ * @default ["es"]
90
+ */
91
+ targetLocales: LocaleCode[];
92
+ /**
93
+ * Whether to require 'use i18n' directive
94
+ */
95
+ useDirective: boolean;
96
+ /**
97
+ * Model configuration for lingo translator
98
+ * - Use "lingo.dev" for Lingo.dev Engine (recommended)
99
+ * - Use locale-pair mapping for direct LLM providers
100
+ *
101
+ * Examples:
102
+ * - "lingo.dev"
103
+ * - { "en:es": "google:gemini-2.0-flash", "*:*": "groq:llama3-8b-8192" }
104
+ *
105
+ * @default "lingo.dev"
106
+ */
107
+ models: "lingo.dev" | Record<string, string>;
108
+ /**
109
+ * Custom translation prompt for lingo translator
110
+ * Use {SOURCE_LOCALE} and {TARGET_LOCALE} placeholders
111
+ */
112
+ prompt?: string;
113
+ /**
114
+ * Pluralization configuration
115
+ * Automatically detects and converts messages to ICU MessageFormat
116
+ */
117
+ pluralization: Omit<PluralizationConfig, "sourceLocale">;
118
+ /**
119
+ * Development-specific settings
120
+ */
121
+ dev: {
122
+ /**
123
+ * Use pseudotranslator in development instead of real translator
124
+ * Useful for:
125
+ * - Testing i18n without API calls
126
+ * - Verifying correct elements are translated
127
+ * - Saving AI tokens during development
128
+ *
129
+ * @default false
130
+ */
131
+ usePseudotranslator?: boolean;
132
+ /**
133
+ * Starting port for the translation server in development mode
134
+ * Server will try this port first, then increment if unavailable
135
+ *
136
+ * @default 60000
137
+ */
138
+ translationServerStartPort: number;
139
+ translationServerUrl?: string;
140
+ };
141
+ /**
142
+ * Locale persistence configuration
143
+ * Defines how locale changes should be persisted
144
+ *
145
+ * @default { type: 'cookie', config: { name: 'locale' } }
146
+ */
147
+ localePersistence: LocalePersistenceConfig;
148
+ /**
149
+ * Build mode for CI/production
150
+ * - "translate": Generate translations at build time, fail if translation fails (default)
151
+ * - "cache-only": Only use cached translations, fail if cache is incomplete
152
+ *
153
+ * Can be overridden by LINGO_BUILD_MODE environment variable
154
+ *
155
+ * @default "translate"
156
+ */
157
+ buildMode: "translate" | "cache-only";
158
+ };
159
+ //#endregion
160
+ export { CookieConfig, PartialLingoConfig };
161
+ //# sourceMappingURL=types.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.mts","names":[],"sources":["../src/types.ts"],"sourcesContent":[],"mappings":";;;;;AA4BA;AAKA;AAEA;AAKY,UA9BK,YAAA,CA8Ba;EAAQ;;;;EAIhC,IAAA,EAAA,MAAA;EAAoC;;;;EAHxC,MAAA,EAAA,MAAA;;AASF;AAKA;;;AAoDiB,KA/EL,uBAAA,GA+EK;EAkBO,IAAA,EAAA,QAAA;EAYF,MAAA,EA7G0C,YA6G1C;CAAL;;;;KAxGL,yBAAA;KAEA,mBAAA;;;;KAKA,kBAAA,GAAqB,KAAK,aAAa,6BACjD,QACE,KACE,aACA,oCAAoC;OAE/B,QAAQ;;KAIP,gBAAA;;;;KAKA,WAAA;;;;;;;;;;;;;;;;eAkBG;;;;;;;;;;;;;;;;;;;;gBAsBC;;;;;;;;;;;iBAYC;;;;;;;;;;;;;;;;wBAkBO;;;;;;;;;;iBAYP,KAAK;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;qBAkCD"}
@@ -0,0 +1,58 @@
1
+
2
+ //#region src/utils/config-factory.ts
3
+ /**
4
+ * Default configuration values
5
+ */
6
+ const DEFAULT_CONFIG = {
7
+ sourceRoot: "src",
8
+ lingoDir: "lingo",
9
+ useDirective: false,
10
+ dev: { translationServerStartPort: 6e4 },
11
+ localePersistence: {
12
+ type: "cookie",
13
+ config: {
14
+ name: "locale",
15
+ maxAge: 31536e3
16
+ }
17
+ },
18
+ models: "lingo.dev",
19
+ pluralization: {
20
+ enabled: true,
21
+ model: "groq:llama-3.1-8b-instant"
22
+ },
23
+ buildMode: "translate"
24
+ };
25
+ /**
26
+ * Create a LoaderConfig with defaults applied
27
+ *
28
+ * @param options - Partial config to override defaults
29
+ * @returns Complete LoaderConfig with all defaults applied
30
+ *
31
+ */
32
+ function createLingoConfig(options) {
33
+ return {
34
+ ...DEFAULT_CONFIG,
35
+ ...options,
36
+ environment: options.environment ?? (process.env.NODE_ENV === "development" ? "development" : "production"),
37
+ cacheType: options.cacheType ?? "local",
38
+ dev: {
39
+ ...DEFAULT_CONFIG.dev,
40
+ ...options.dev
41
+ },
42
+ pluralization: {
43
+ ...DEFAULT_CONFIG.pluralization,
44
+ ...options.pluralization
45
+ },
46
+ localePersistence: {
47
+ ...DEFAULT_CONFIG.localePersistence,
48
+ ...options.localePersistence,
49
+ config: {
50
+ ...DEFAULT_CONFIG.localePersistence.config,
51
+ ...options.localePersistence?.config
52
+ }
53
+ }
54
+ };
55
+ }
56
+
57
+ //#endregion
58
+ exports.createLingoConfig = createLingoConfig;
@@ -0,0 +1,58 @@
1
+ //#region src/utils/config-factory.ts
2
+ /**
3
+ * Default configuration values
4
+ */
5
+ const DEFAULT_CONFIG = {
6
+ sourceRoot: "src",
7
+ lingoDir: "lingo",
8
+ useDirective: false,
9
+ dev: { translationServerStartPort: 6e4 },
10
+ localePersistence: {
11
+ type: "cookie",
12
+ config: {
13
+ name: "locale",
14
+ maxAge: 31536e3
15
+ }
16
+ },
17
+ models: "lingo.dev",
18
+ pluralization: {
19
+ enabled: true,
20
+ model: "groq:llama-3.1-8b-instant"
21
+ },
22
+ buildMode: "translate"
23
+ };
24
+ /**
25
+ * Create a LoaderConfig with defaults applied
26
+ *
27
+ * @param options - Partial config to override defaults
28
+ * @returns Complete LoaderConfig with all defaults applied
29
+ *
30
+ */
31
+ function createLingoConfig(options) {
32
+ return {
33
+ ...DEFAULT_CONFIG,
34
+ ...options,
35
+ environment: options.environment ?? (process.env.NODE_ENV === "development" ? "development" : "production"),
36
+ cacheType: options.cacheType ?? "local",
37
+ dev: {
38
+ ...DEFAULT_CONFIG.dev,
39
+ ...options.dev
40
+ },
41
+ pluralization: {
42
+ ...DEFAULT_CONFIG.pluralization,
43
+ ...options.pluralization
44
+ },
45
+ localePersistence: {
46
+ ...DEFAULT_CONFIG.localePersistence,
47
+ ...options.localePersistence,
48
+ config: {
49
+ ...DEFAULT_CONFIG.localePersistence.config,
50
+ ...options.localePersistence?.config
51
+ }
52
+ }
53
+ };
54
+ }
55
+
56
+ //#endregion
57
+ export { createLingoConfig };
58
+ //# sourceMappingURL=config-factory.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"config-factory.mjs","names":[],"sources":["../../src/utils/config-factory.ts"],"sourcesContent":["/**\n * Config factory for creating LoaderConfig instances\n */\nimport type {\n LingoConfig,\n LingoConfigRequiredFields,\n LingoInternalFields,\n PartialLingoConfig,\n} from \"../types\";\n\n/**\n * Default configuration values\n */\nexport const DEFAULT_CONFIG = {\n sourceRoot: \"src\",\n lingoDir: \"lingo\",\n useDirective: false,\n dev: {\n translationServerStartPort: 60000,\n },\n localePersistence: {\n type: \"cookie\" as const,\n config: {\n name: \"locale\",\n maxAge: 31536000,\n },\n },\n models: \"lingo.dev\",\n pluralization: {\n enabled: true,\n model: \"groq:llama-3.1-8b-instant\",\n },\n buildMode: \"translate\",\n} satisfies Omit<\n LingoConfig,\n // Looks like we can use LingoInternalFields, but it's only a coincidence that the types match, we may want to provide default for internal fields\n LingoConfigRequiredFields | \"environment\" | \"cacheType\"\n>;\n\n/**\n * Create a LoaderConfig with defaults applied\n *\n * @param options - Partial config to override defaults\n * @returns Complete LoaderConfig with all defaults applied\n *\n */\nexport function createLingoConfig(\n options: PartialLingoConfig & Partial<Pick<LingoConfig, LingoInternalFields>>,\n): LingoConfig {\n return {\n ...DEFAULT_CONFIG,\n ...options,\n environment:\n options.environment ??\n (process.env.NODE_ENV === \"development\" ? \"development\" : \"production\"),\n cacheType: options.cacheType ?? \"local\",\n dev: {\n ...DEFAULT_CONFIG.dev,\n ...options.dev,\n },\n pluralization: {\n ...DEFAULT_CONFIG.pluralization,\n ...options.pluralization,\n },\n localePersistence: {\n ...DEFAULT_CONFIG.localePersistence,\n ...options.localePersistence,\n config: {\n ...DEFAULT_CONFIG.localePersistence.config,\n ...options.localePersistence?.config,\n },\n },\n };\n}\n"],"mappings":";;;;AAaA,MAAa,iBAAiB;CAC5B,YAAY;CACZ,UAAU;CACV,cAAc;CACd,KAAK,EACH,4BAA4B,KAC7B;CACD,mBAAmB;EACjB,MAAM;EACN,QAAQ;GACN,MAAM;GACN,QAAQ;GACT;EACF;CACD,QAAQ;CACR,eAAe;EACb,SAAS;EACT,OAAO;EACR;CACD,WAAW;CACZ;;;;;;;;AAaD,SAAgB,kBACd,SACa;AACb,QAAO;EACL,GAAG;EACH,GAAG;EACH,aACE,QAAQ,gBACP,QAAQ,IAAI,aAAa,gBAAgB,gBAAgB;EAC5D,WAAW,QAAQ,aAAa;EAChC,KAAK;GACH,GAAG,eAAe;GAClB,GAAG,QAAQ;GACZ;EACD,eAAe;GACb,GAAG,eAAe;GAClB,GAAG,QAAQ;GACZ;EACD,mBAAmB;GACjB,GAAG,eAAe;GAClB,GAAG,QAAQ;GACX,QAAQ;IACN,GAAG,eAAe,kBAAkB;IACpC,GAAG,QAAQ,mBAAmB;IAC/B;GACF;EACF"}
@@ -0,0 +1,17 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ let crypto = require("crypto");
3
+ crypto = require_rolldown_runtime.__toESM(crypto);
4
+
5
+ //#region src/utils/hash.ts
6
+ /**
7
+ * Generate a hash for a translation entry
8
+ * Hash is based on: sourceText + context
9
+ * This ensures that the same text in different components or files gets different hashes
10
+ */
11
+ function generateTranslationHash(sourceText, context) {
12
+ const input = `${sourceText}::${Object.entries(context).sort((a, b) => a[0].localeCompare(b[0])).map(([key, value]) => `${key}:${value}`).join("::")}`;
13
+ return crypto.default.createHash("md5").update(input).digest("hex").substring(0, 12);
14
+ }
15
+
16
+ //#endregion
17
+ exports.generateTranslationHash = generateTranslationHash;
@@ -0,0 +1,16 @@
1
+ import crypto from "crypto";
2
+
3
+ //#region src/utils/hash.ts
4
+ /**
5
+ * Generate a hash for a translation entry
6
+ * Hash is based on: sourceText + context
7
+ * This ensures that the same text in different components or files gets different hashes
8
+ */
9
+ function generateTranslationHash(sourceText, context) {
10
+ const input = `${sourceText}::${Object.entries(context).sort((a, b) => a[0].localeCompare(b[0])).map(([key, value]) => `${key}:${value}`).join("::")}`;
11
+ return crypto.createHash("md5").update(input).digest("hex").substring(0, 12);
12
+ }
13
+
14
+ //#endregion
15
+ export { generateTranslationHash };
16
+ //# sourceMappingURL=hash.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"hash.mjs","names":[],"sources":["../../src/utils/hash.ts"],"sourcesContent":["import crypto from \"crypto\";\n\n/*\n * If we find md5 too slow, we can use FNV-1a\n *\n * function generateHash(text: string): string {\n * let hash = 2166136261;\n * for (let i = 0; i < text.length; i++) {\n * hash ^= text.charCodeAt(i);\n * hash += (hash << 1) + (hash << 4) + (hash << 7) + (hash << 8) + (hash << 12);\n * }\n * return (hash >>> 0).toString(36).padStart(7, '0').slice(0, 12);\n * }\n *\n */\n\n/**\n * Generate a hash for a translation entry\n * Hash is based on: sourceText + context\n * This ensures that the same text in different components or files gets different hashes\n */\nexport function generateTranslationHash(\n sourceText: string,\n context: Record<string, any>,\n): string {\n const input = `${sourceText}::${Object.entries(context)\n .sort((a, b) => a[0].localeCompare(b[0]))\n .map(([key, value]) => `${key}:${value}`)\n .join(\"::\")}`;\n return crypto.createHash(\"md5\").update(input).digest(\"hex\").substring(0, 12); // Use first 12 chars for brevity\n}\n\n/**\n * Validate that a hash matches the expected format\n */\nexport function isValidHash(hash: string): boolean {\n return /^[a-f0-9]{12}$/.test(hash);\n}\n"],"mappings":";;;;;;;;AAqBA,SAAgB,wBACd,YACA,SACQ;CACR,MAAM,QAAQ,GAAG,WAAW,IAAI,OAAO,QAAQ,QAAQ,CACpD,MAAM,GAAG,MAAM,EAAE,GAAG,cAAc,EAAE,GAAG,CAAC,CACxC,KAAK,CAAC,KAAK,WAAW,GAAG,IAAI,GAAG,QAAQ,CACxC,KAAK,KAAK;AACb,QAAO,OAAO,WAAW,MAAM,CAAC,OAAO,MAAM,CAAC,OAAO,MAAM,CAAC,UAAU,GAAG,GAAG"}
@@ -0,0 +1,14 @@
1
+ const require_rolldown_runtime = require('../_virtual/rolldown_runtime.cjs');
2
+ let lingo_dev_locale_codes = require("lingo.dev/locale-codes");
3
+
4
+ //#region src/utils/is-valid-locale.ts
5
+ function isValidLocale(locale) {
6
+ return (0, lingo_dev_locale_codes.isValidLocale)(locale);
7
+ }
8
+ function parseLocaleOrThrow(locale) {
9
+ if (locale && isValidLocale(locale)) return locale;
10
+ else throw new Error(`Invalid locale: ${locale}`);
11
+ }
12
+
13
+ //#endregion
14
+ exports.parseLocaleOrThrow = parseLocaleOrThrow;