@intlayer/ai 9.5.3 → 9.5.4-canary.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.
package/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  <p align="center">
2
2
  <a href="https://intlayer.org" rel="">
3
- <img src="https://raw.githubusercontent.com/aymericzip/intlayer/main/docs/assets/cover.png" width="60%" alt="Intlayer Logo" />
3
+ <img src="https://raw.githubusercontent.com/aymericzip/intlayer/main/docs/assets/cover.webp" width="60%" alt="Intlayer Logo" />
4
4
  </a>
5
5
  </p>
6
6
 
@@ -49,10 +49,10 @@ With **per-locale content files**, **TypeScript autocompletion**, **tree-shakabl
49
49
 
50
50
  | Feature | Description |
51
51
  | --------------------------------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
52
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/frameworks.png?raw=true" alt="Feature" width="700"> | **Cross-Frameworks Support**<br><br>Intlayer is compatible with all major frameworks and libraries, including Next.js, React, Vite, Vue.js, Nuxt, Preact, Express, and more. |
52
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/frameworks.webp?raw=true" alt="Feature" width="700"> | **Cross-Frameworks Support**<br><br>Intlayer is compatible with all major frameworks and libraries, including Next.js, React, Vite, Vue.js, Nuxt, Preact, Express, and more. |
53
53
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/javascript_content_management.jpg?raw=true" alt="Feature" width="700"> | **JavaScript-Powered Content Management**<br><br>Harness the flexibility of JavaScript to define and manage your content efficiently. <br><br> - [Content declaration](https://intlayer.org/doc/concept/content) |
54
54
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/per_locale_content_declaration_file.png?raw=true" alt="Feature" width="700"> | **Per-Locale Content Declaration File**<br><br>Speed up your development by declaring your content once, before auto generation.<br><br> - [Per-Locale Content Declaration File](https://intlayer.org/doc/concept/per-locale-file) |
55
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/compiler.jpg?raw=true" alt="Feature" width="700"> | **Compiler**<br><br>The Intlayer Compiler extract automatically the content from the components and generate the dictionary files.<br><br> - [Compiler](https://intlayer.org/doc/compiler) |
55
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/compiler.webp?raw=true" alt="Feature" width="700"> | **Compiler**<br><br>The Intlayer Compiler extract automatically the content from the components and generate the dictionary files.<br><br> - [Compiler](https://intlayer.org/doc/compiler) |
56
56
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/autocompletion.png?raw=true" alt="Feature" width="700"> | **Type-Safe Environment**<br><br>Leverage TypeScript to ensure your content definitions and code are error-free, while also benefiting from IDE autocompletion.<br><br> - [TypeScript configuration](https://intlayer.org/doc/environment/vite-and-react#configure-typescript) |
57
57
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/config_file.png?raw=true" alt="Feature" width="700"> | **Simplified Setup**<br><br>Get up and running quickly with minimal configuration. Adjust settings for internationalization, routing, AI, build, and content handling with ease. <br><br> - [Explore Next.js integration](https://intlayer.org/doc/environment/nextjs) |
58
58
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/content_retrieval.png?raw=true" alt="Feature" width="700"> | **Simplified Content Retrieval**<br><br>No need to call your `t` function for each piece of content. Retrieve all your content directly using a single hook.<br><br> - [React integration](https://intlayer.org/doc/environment/create-react-app) |
@@ -60,12 +60,12 @@ With **per-locale content files**, **TypeScript autocompletion**, **tree-shakabl
60
60
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/file_tree.png?raw=true" alt="Feature" width="700"> | **Organized Codebase**<br><br>Keep your codebase more organized: 1 component = 1 dictionary in the same folder. Translations close to their respective components, enhance maintainability and clarity. <br><br> - [How Intlayer works](https://intlayer.org/doc/concept/how-works-intlayer) |
61
61
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/url_routing.png?raw=true" alt="Feature" width="700"> | **Enhanced Routing**<br><br>Full support of app routing, adapting seamlessly to complex application structures, for Next.js, React, Vite, Vue.js, etc.<br><br> - [Explore Next.js integration](https://intlayer.org/doc/environment/nextjs) |
62
62
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/markdown.png?raw=true" alt="Feature" width="700"> | **Markdown Support**<br><br>Import and interpret, locale files and remote Markdown for multilingual content like privacy policies, documentation, etc. Interpret and make Markdown metadata accessible in your code.<br><br> - [Content files](https://intlayer.org/doc/concept/content/file) |
63
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/visual_editor.png?raw=true" alt="Feature" width="700"> | **Free Visual Editor & CMS**<br><br>A free visual editor and CMS are available for content writers, removing the need for a localization platform. Keep your content synchronized using Git, or externalize it totally or partially with the CMS.<br><br> - [Intlayer Editor](https://intlayer.org/doc/concept/editor) <br> - [Intlayer CMS](https://intlayer.org/doc/concept/cms) |
64
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.png?raw=true" alt="Feature" width="700"> | **Tree-shakable Content**<br><br>Tree-shakable content, reducing the size of the final bundle. Loads content per component, excluding any unused content from your bundle. Supports lazy loading to enhance app loading efficiency. <br><br> - [App build optimization](https://intlayer.org/doc/concept/how-works-intlayer#app-build-optimization) |
65
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/static_rendering.png?raw=true" alt="Feature" width="700"> | **Static Rendering**<br><br>Doesn't block Static Rendering. <br><br> - [Next.js integration](https://intlayer.org/doc/environment/nextjs) |
63
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/visual_editor.webp?raw=true" alt="Feature" width="700"> | **Free Visual Editor & CMS**<br><br>A free visual editor and CMS are available for content writers, removing the need for a localization platform. Keep your content synchronized using Git, or externalize it totally or partially with the CMS.<br><br> - [Intlayer Editor](https://intlayer.org/doc/concept/editor) <br> - [Intlayer CMS](https://intlayer.org/doc/concept/cms) |
64
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/bundle.webp?raw=true" alt="Feature" width="700"> | **Tree-shakable Content**<br><br>Tree-shakable content, reducing the size of the final bundle. Loads content per component, excluding any unused content from your bundle. Supports lazy loading to enhance app loading efficiency. <br><br> - [App build optimization](https://intlayer.org/doc/concept/how-works-intlayer#app-build-optimization) |
65
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/static_rendering.webp?raw=true" alt="Feature" width="700"> | **Static Rendering**<br><br>Doesn't block Static Rendering. <br><br> - [Next.js integration](https://intlayer.org/doc/environment/nextjs) |
66
66
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/AI_translation.png?raw=true" alt="Feature" width="700"> | **AI-Powered Translation**<br><br>Transform your website into 231 languages with just one click using Intlayer's advanced AI-powered translation tools using your own AI provider / API key. <br><br> - [CI/CD integration](https://intlayer.org/doc/concept/ci-cd) <br> - [Intlayer CLI](https://intlayer.org/doc/concept/cli) <br> - [Auto fill](https://intlayer.org/doc/concept/auto-fill) |
67
67
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/mcp.png?raw=true" alt="Feature" width="700"> | **MCP Server Integration**<br><br>Provides an MCP (Model Context Protocol) server for IDE automation, enabling seamless content management and i18n workflows directly within your development environment. <br><br> - [MCP Server](https://github.com/aymericzip/intlayer/blob/main/docs/docs/en/mcp_server.md) |
68
- | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/vscode_extension.png?raw=true" alt="Feature" width="700"> | **VSCode Extension**<br><br>Intlayer provides a VSCode extension to help you manage your content and translations, building your dictionaries, translating your content, and more. <br><br> - [VSCode Extension](https://intlayer.org/doc/vs-code-extension) |
68
+ | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/vscode_extension.webp?raw=true" alt="Feature" width="700"> | **VSCode Extension**<br><br>Intlayer provides a VSCode extension to help you manage your content and translations, building your dictionaries, translating your content, and more. <br><br> - [VSCode Extension](https://intlayer.org/doc/vs-code-extension) |
69
69
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/interoperability.png?raw=true" alt="Feature" width="700"> | **Interoperability**<br><br>Allow interoperability with react-i18next, next-i18next, next-intl, react-intl, vue-i18n. <br><br> - [Intlayer and react-intl](https://intlayer.org/blog/intlayer-with-react-intl) <br> - [Intlayer and next-intl](https://intlayer.org/blog/intlayer-with-next-intl) <br> - [Intlayer and next-i18next](https://intlayer.org/blog/intlayer-with-next-i18next) <br> - [Intlayer and vue-i18n](https://intlayer.org/blog/intlayer-with-vue-i18n) <br> - [Intlayer compat adapters](https://intlayer.org/doc/compatibility) |
70
70
  | <img src="https://github.com/aymericzip/intlayer/blob/main/docs/assets/benchmark.png?raw=true" alt="Feature" width="700"> | **Performances & Benchmark**<br><br>Uses advanced tree-shaking and dynamic loading to boost performances and keep the solution as light as possible. <br><br> - [Performances & Benchmark](https://intlayer.org/doc/benchmark) |
71
71
 
@@ -262,7 +262,7 @@ Intlayer connects **developers**, **copywriters**, and **AI agents** in one work
262
262
  - Feature flags
263
263
  - Headless CMSs (e.g. Contentful, Strapi, Sanity)
264
264
 
265
- ![CMS Preview](https://github.com/aymericzip/intlayer/blob/main/docs/assets/CMS.png?raw=true)
265
+ ![CMS Preview](https://github.com/aymericzip/intlayer/blob/main/docs/assets/CMS.webp?raw=true)
266
266
 
267
267
  ## 🌐 Readme in other languages
268
268
 
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require__utils_asset = require('../_virtual/_utils_asset.cjs');
3
3
  let ai = require("ai");
4
- let zod = require("zod");
4
+ let zod_mini = require("zod/mini");
5
5
 
6
6
  //#region src/auditDictionaryMetadata/index.ts
7
7
  const aiDefaultOptions = {};
@@ -19,10 +19,10 @@ const auditDictionaryMetadata = async ({ fileContent, tags, aiConfig, applicatio
19
19
  const { output: _unusedOutput, ...validAiConfig } = restAiConfig;
20
20
  const { output, usage } = await (0, ai.generateText)({
21
21
  ...validAiConfig,
22
- output: ai.Output.object({ schema: zod.z.object({
23
- title: zod.z.string(),
24
- description: zod.z.string(),
25
- tags: zod.z.array(zod.z.string())
22
+ output: ai.Output.object({ schema: zod_mini.z.object({
23
+ title: zod_mini.z.string(),
24
+ description: zod_mini.z.string(),
25
+ tags: zod_mini.z.array(zod_mini.z.string())
26
26
  }) }),
27
27
  system: prompt,
28
28
  messages: [
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["readAsset","generateText","Output","z"],"sources":["../../../src/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type AuditDictionaryMetadataOptions = {\n fileContent: string;\n tags?: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryMetadata = async ({\n fileContent,\n tags,\n aiConfig,\n applicationContext,\n}: AuditDictionaryMetadataOptions): Promise<\n AuditFileResultData | undefined\n> => {\n const CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n const EXAMPLE_REQUEST = readAsset('./EXAMPLE_REQUEST.md');\n const EXAMPLE_RESPONSE = readAsset('./EXAMPLE_RESPONSE.md');\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{applicationContext}}',\n applicationContext ?? ''\n ).replace(\n '{{tags}}',\n tags\n ? JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )\n : ''\n );\n\n const { dataSerialization, ...restAiConfig } = aiConfig;\n const { output: _unusedOutput, ...validAiConfig } = restAiConfig;\n\n // Use the AI SDK to generate the completion\n const { output, usage } = await generateText({\n ...validAiConfig,\n output: Output.object({\n schema: z.object({\n title: z.string(),\n description: z.string(),\n tags: z.array(z.string()),\n }),\n }),\n system: prompt,\n messages: [\n { role: 'user', content: EXAMPLE_REQUEST },\n { role: 'assistant', content: EXAMPLE_RESPONSE },\n {\n role: 'user',\n content: fileContent,\n },\n ],\n });\n\n return {\n fileContent: output,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;AA0BA,MAAa,mBAA8B,CAE3C;;;;;;AAOA,MAAa,0BAA0B,OAAO,EAC5C,aACA,MACA,UACA,yBAGG;CACH,MAAM,kBAAkBA,+BAAU,aAAa;CAC/C,MAAM,kBAAkBA,+BAAU,sBAAsB;CACxD,MAAM,mBAAmBA,+BAAU,uBAAuB;CAG1D,MAAM,SAAS,gBAAgB,QAC7B,0BACA,sBAAsB,EACxB,CAAC,CAAC,QACA,YACA,OACI,KAAK,UACH,KACG,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,aAAa,CAAC,CACzD,KAAK,MAAM,GACd,MACA,CACF,IACA,EACN;CAEA,MAAM,EAAE,mBAAmB,GAAG,iBAAiB;CAC/C,MAAM,EAAE,QAAQ,eAAe,GAAG,kBAAkB;CAGpD,MAAM,EAAE,QAAQ,UAAU,UAAMC,iBAAa;EAC3C,GAAG;EACH,QAAQC,UAAO,OAAO,EACpB,QAAQC,MAAE,OAAO;GACf,OAAOA,MAAE,OAAO;GAChB,aAAaA,MAAE,OAAO;GACtB,MAAMA,MAAE,MAAMA,MAAE,OAAO,CAAC;EAC1B,CAAC,EACH,CAAC;EACD,QAAQ;EACR,UAAU;GACR;IAAE,MAAM;IAAQ,SAAS;GAAgB;GACzC;IAAE,MAAM;IAAa,SAAS;GAAiB;GAC/C;IACE,MAAM;IACN,SAAS;GACX;EACF;CACF,CAAC;CAED,OAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;CACnC;AACF"}
1
+ {"version":3,"file":"index.cjs","names":["readAsset","generateText","Output","z"],"sources":["../../../src/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod/mini';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type AuditDictionaryMetadataOptions = {\n fileContent: string;\n tags?: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryMetadata = async ({\n fileContent,\n tags,\n aiConfig,\n applicationContext,\n}: AuditDictionaryMetadataOptions): Promise<\n AuditFileResultData | undefined\n> => {\n const CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n const EXAMPLE_REQUEST = readAsset('./EXAMPLE_REQUEST.md');\n const EXAMPLE_RESPONSE = readAsset('./EXAMPLE_RESPONSE.md');\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{applicationContext}}',\n applicationContext ?? ''\n ).replace(\n '{{tags}}',\n tags\n ? JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )\n : ''\n );\n\n const { dataSerialization, ...restAiConfig } = aiConfig;\n const { output: _unusedOutput, ...validAiConfig } = restAiConfig;\n\n // Use the AI SDK to generate the completion\n const { output, usage } = await generateText({\n ...validAiConfig,\n output: Output.object({\n schema: z.object({\n title: z.string(),\n description: z.string(),\n tags: z.array(z.string()),\n }),\n }),\n system: prompt,\n messages: [\n { role: 'user', content: EXAMPLE_REQUEST },\n { role: 'assistant', content: EXAMPLE_RESPONSE },\n {\n role: 'user',\n content: fileContent,\n },\n ],\n });\n\n return {\n fileContent: output,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;;AA0BA,MAAa,mBAA8B,CAE3C;;;;;;AAOA,MAAa,0BAA0B,OAAO,EAC5C,aACA,MACA,UACA,yBAGG;CACH,MAAM,kBAAkBA,+BAAU,aAAa;CAC/C,MAAM,kBAAkBA,+BAAU,sBAAsB;CACxD,MAAM,mBAAmBA,+BAAU,uBAAuB;CAG1D,MAAM,SAAS,gBAAgB,QAC7B,0BACA,sBAAsB,EACxB,CAAC,CAAC,QACA,YACA,OACI,KAAK,UACH,KACG,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,aAAa,CAAC,CACzD,KAAK,MAAM,GACd,MACA,CACF,IACA,EACN;CAEA,MAAM,EAAE,mBAAmB,GAAG,iBAAiB;CAC/C,MAAM,EAAE,QAAQ,eAAe,GAAG,kBAAkB;CAGpD,MAAM,EAAE,QAAQ,UAAU,UAAMC,iBAAa;EAC3C,GAAG;EACH,QAAQC,UAAO,OAAO,EACpB,QAAQC,WAAE,OAAO;GACf,OAAOA,WAAE,OAAO;GAChB,aAAaA,WAAE,OAAO;GACtB,MAAMA,WAAE,MAAMA,WAAE,OAAO,CAAC;EAC1B,CAAC,EACH,CAAC;EACD,QAAQ;EACR,UAAU;GACR;IAAE,MAAM;IAAQ,SAAS;GAAgB;GACzC;IAAE,MAAM;IAAa,SAAS;GAAiB;GAC/C;IACE,MAAM;IACN,SAAS;GACX;EACF;CACF,CAAC;CAED,OAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;CACnC;AACF"}
@@ -1,7 +1,7 @@
1
1
  Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
- const require_customQuery = require('./customQuery.cjs');
3
2
  const require_aiSdk = require('./aiSdk.cjs');
4
3
  const require_auditDictionaryMetadata_index = require('./auditDictionaryMetadata/index.cjs');
4
+ const require_customQuery = require('./customQuery.cjs');
5
5
  const require_utils_extractJSON = require('./utils/extractJSON.cjs');
6
6
  const require_translateJSON_index = require('./translateJSON/index.cjs');
7
7
  let ai = require("ai");
@@ -2,7 +2,7 @@ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
2
2
  const require__utils_asset = require('../_virtual/_utils_asset.cjs');
3
3
  const require_utils_extractJSON = require('../utils/extractJSON.cjs');
4
4
  let ai = require("ai");
5
- let zod = require("zod");
5
+ let zod_mini = require("zod/mini");
6
6
  let _intlayer_config_defaultValues = require("@intlayer/config/defaultValues");
7
7
  let _intlayer_core_localization = require("@intlayer/core/localization");
8
8
  let _toon_format_toon = require("@toon-format/toon");
@@ -38,19 +38,19 @@ const getModeInstructions = (mode) => {
38
38
  return "Mode: \"Review\" - Fill missing content and review existing keys from the preset content. If a key from the entry is missing in the output, it must be translated to the target language and added. If you detect misspelled content, or content that should be reformulated, correct it. If a translation is not coherent with the desired language, translate it.";
39
39
  };
40
40
  const jsonToZod = (content) => {
41
- if (typeof content === "string") return zod.z.string();
42
- if (typeof content === "number") return zod.z.number();
43
- if (typeof content === "boolean") return zod.z.boolean();
41
+ if (typeof content === "string") return zod_mini.z.string();
42
+ if (typeof content === "number") return zod_mini.z.number();
43
+ if (typeof content === "boolean") return zod_mini.z.boolean();
44
44
  if (Array.isArray(content)) {
45
- if (content.length === 0) return zod.z.array(zod.z.string());
46
- return zod.z.array(jsonToZod(content[0]));
45
+ if (content.length === 0) return zod_mini.z.array(zod_mini.z.string());
46
+ return zod_mini.z.array(jsonToZod(content[0]));
47
47
  }
48
48
  if (typeof content === "object" && content !== null) {
49
49
  const shape = {};
50
50
  for (const key in content) shape[key] = jsonToZod(content[key]);
51
- return zod.z.object(shape);
51
+ return zod_mini.z.object(shape);
52
52
  }
53
- return zod.z.string();
53
+ return zod_mini.z.string();
54
54
  };
55
55
  const countKeys = (obj) => {
56
56
  if (typeof obj !== "object" || obj === null) return 0;
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","names":["AIProvider","getLocaleName","DEFAULT_LOCALE","z","readAsset","encode","generateText","decode","Output","extractJson"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { DEFAULT_LOCALE } from '@intlayer/config/defaultValues';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { decode, encode } from '@toon-format/toon';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\nimport { extractJson } from '../utils/extractJSON';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type TranslateJSONOptions<T = JSON> = {\n entryFileContent: T;\n presetOutputContent: Partial<T>;\n dictionaryDescription?: string;\n entryLocale: Locale;\n outputLocale: Locale;\n tags?: Tag[];\n aiConfig: AIConfig;\n mode: 'complete' | 'review';\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData<T = JSON> = {\n fileContent: T;\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-5-mini',\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string =>\n `${locale}: ${getLocaleName(locale, DEFAULT_LOCALE)}`;\n\n/**\n * Formats tag instructions for the AI prompt.\n * Creates a string with all available tags and their descriptions.\n *\n * @param tags - The list of tags to format.\n * @returns A formatted string with tag instructions.\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n // Prepare the tag instructions.\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\nconst getModeInstructions = (mode: 'complete' | 'review'): string => {\n if (mode === 'complete') {\n return 'Mode: \"Complete\" - Enrich the preset content with the missing keys and values in the output locale. Do not update existing keys. Everything should be returned in the output.';\n }\n\n return 'Mode: \"Review\" - Fill missing content and review existing keys from the preset content. If a key from the entry is missing in the output, it must be translated to the target language and added. If you detect misspelled content, or content that should be reformulated, correct it. If a translation is not coherent with the desired language, translate it.';\n};\n\nconst jsonToZod = (content: any): z.ZodTypeAny => {\n // Base case: content is a string (the translation target)\n if (typeof content === 'string') {\n return z.string();\n }\n\n // Base cases: primitives often preserved in i18n files (e.g. strict numbers/booleans)\n if (typeof content === 'number') {\n return z.number();\n }\n if (typeof content === 'boolean') {\n return z.boolean();\n }\n\n // Recursive case: Array\n if (Array.isArray(content)) {\n // If array is empty, we assume array of strings as default for i18n\n if (content.length === 0) {\n return z.array(z.string());\n }\n // We assume all items in the array share the structure of the first item\n return z.array(jsonToZod(content[0]));\n }\n\n // Recursive case: Object\n if (typeof content === 'object' && content !== null) {\n const shape: Record<string, z.ZodTypeAny> = {};\n for (const key in content) {\n shape[key] = jsonToZod(content[key]);\n }\n return z.object(shape);\n }\n\n // Fallback\n return z.string();\n};\n\nconst countKeys = (obj: any): number => {\n if (typeof obj !== 'object' || obj === null) return 0;\n let count = 0;\n for (const key in obj) {\n count += 1 + countKeys(obj[key]);\n }\n return count;\n};\n\n/**\n * TranslateJSONs a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const translateJSON = async <T>({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions<T>): Promise<\n TranslateJSONResultData<T> | undefined\n> => {\n const { dataSerialization, ...restAiConfig } = aiConfig;\n const { output: _unusedOutput, ...validAiConfig } = restAiConfig;\n\n const formattedEntryLocale = formatLocaleWithName(entryLocale);\n const formattedOutputLocale = formatLocaleWithName(outputLocale);\n\n const isToon = dataSerialization === 'toon';\n const promptFile = readAsset(\n isToon ? './PROMPT_TOON.md' : './PROMPT_JSON.md'\n );\n const entryContentStr = isToon\n ? encode(entryFileContent)\n : JSON.stringify(entryFileContent);\n const presetContentStr = isToon\n ? encode(presetOutputContent)\n : JSON.stringify(presetOutputContent);\n\n const schema = jsonToZod(entryFileContent);\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = promptFile\n .replace('{{entryLocale}}', formattedEntryLocale)\n .replace('{{outputLocale}}', formattedOutputLocale)\n .replace('{{presetOutputContent}}', presetContentStr)\n .replace('{{dictionaryDescription}}', dictionaryDescription ?? '')\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags ?? []))\n .replace('{{modeInstructions}}', getModeInstructions(mode));\n\n if (isToon) {\n const { text, usage } = await generateText({\n ...aiConfig,\n system: prompt,\n messages: [\n {\n role: 'user',\n content: [\n `# Translation Request`,\n `Please translate the following TOON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n // Strip markdown code blocks if present\n const cleanedText = text\n .replace(/^```(?:toon)?\\n([\\s\\S]*?)\\n```$/gm, '$1')\n .trim();\n\n const decodedJson = decode(cleanedText) as T;\n\n // schema.parse(decodedJson);\n\n return {\n fileContent: decodedJson,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n }\n\n const totalKeys = countKeys(entryFileContent);\n\n const MAX_SAFE_KEYS = 70; // You will need to tune this threshold based on testing\n\n const useStrictOutput = totalKeys <= MAX_SAFE_KEYS;\n\n // Use the AI SDK to generate the completion\n const { text, usage } = await generateText({\n ...validAiConfig,\n\n // Disable schema if Schema is too complex (Block with Anthropic)\n output: useStrictOutput ? Output.object({ schema }) : undefined,\n system: prompt,\n messages: [\n {\n role: 'user',\n content: [\n `# Translation Request`,\n `Please translate the following JSON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `CRITICAL INSTRUCTIONS:`,\n `- You MUST return ONLY raw, valid JSON.`,\n `- DO NOT wrap the response in Markdown code blocks (e.g., no \\`\\`\\`json).`,\n `- DO NOT include any conversational text before or after the JSON object.`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n // Extract and re-validate deeply\n const extractedJSON = extractJson(text);\n const validatedContent = schema.parse(extractedJSON) as T;\n\n return {\n fileContent: validatedContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","names":["AIProvider","getLocaleName","DEFAULT_LOCALE","z","readAsset","encode","generateText","decode","Output","extractJson"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { DEFAULT_LOCALE } from '@intlayer/config/defaultValues';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { decode, encode } from '@toon-format/toon';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod/mini';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\nimport { extractJson } from '../utils/extractJSON';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type TranslateJSONOptions<T = JSON> = {\n entryFileContent: T;\n presetOutputContent: Partial<T>;\n dictionaryDescription?: string;\n entryLocale: Locale;\n outputLocale: Locale;\n tags?: Tag[];\n aiConfig: AIConfig;\n mode: 'complete' | 'review';\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData<T = JSON> = {\n fileContent: T;\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-5-mini',\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string =>\n `${locale}: ${getLocaleName(locale, DEFAULT_LOCALE)}`;\n\n/**\n * Formats tag instructions for the AI prompt.\n * Creates a string with all available tags and their descriptions.\n *\n * @param tags - The list of tags to format.\n * @returns A formatted string with tag instructions.\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n // Prepare the tag instructions.\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\nconst getModeInstructions = (mode: 'complete' | 'review'): string => {\n if (mode === 'complete') {\n return 'Mode: \"Complete\" - Enrich the preset content with the missing keys and values in the output locale. Do not update existing keys. Everything should be returned in the output.';\n }\n\n return 'Mode: \"Review\" - Fill missing content and review existing keys from the preset content. If a key from the entry is missing in the output, it must be translated to the target language and added. If you detect misspelled content, or content that should be reformulated, correct it. If a translation is not coherent with the desired language, translate it.';\n};\n\nconst jsonToZod = (content: any): z.ZodMiniType => {\n // Base case: content is a string (the translation target)\n if (typeof content === 'string') {\n return z.string();\n }\n\n // Base cases: primitives often preserved in i18n files (e.g. strict numbers/booleans)\n if (typeof content === 'number') {\n return z.number();\n }\n if (typeof content === 'boolean') {\n return z.boolean();\n }\n\n // Recursive case: Array\n if (Array.isArray(content)) {\n // If array is empty, we assume array of strings as default for i18n\n if (content.length === 0) {\n return z.array(z.string());\n }\n // We assume all items in the array share the structure of the first item\n return z.array(jsonToZod(content[0]));\n }\n\n // Recursive case: Object\n if (typeof content === 'object' && content !== null) {\n const shape: Record<string, z.ZodMiniType> = {};\n for (const key in content) {\n shape[key] = jsonToZod(content[key]);\n }\n return z.object(shape);\n }\n\n // Fallback\n return z.string();\n};\n\nconst countKeys = (obj: any): number => {\n if (typeof obj !== 'object' || obj === null) return 0;\n let count = 0;\n for (const key in obj) {\n count += 1 + countKeys(obj[key]);\n }\n return count;\n};\n\n/**\n * TranslateJSONs a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const translateJSON = async <T>({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions<T>): Promise<\n TranslateJSONResultData<T> | undefined\n> => {\n const { dataSerialization, ...restAiConfig } = aiConfig;\n const { output: _unusedOutput, ...validAiConfig } = restAiConfig;\n\n const formattedEntryLocale = formatLocaleWithName(entryLocale);\n const formattedOutputLocale = formatLocaleWithName(outputLocale);\n\n const isToon = dataSerialization === 'toon';\n const promptFile = readAsset(\n isToon ? './PROMPT_TOON.md' : './PROMPT_JSON.md'\n );\n const entryContentStr = isToon\n ? encode(entryFileContent)\n : JSON.stringify(entryFileContent);\n const presetContentStr = isToon\n ? encode(presetOutputContent)\n : JSON.stringify(presetOutputContent);\n\n const schema = jsonToZod(entryFileContent);\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = promptFile\n .replace('{{entryLocale}}', formattedEntryLocale)\n .replace('{{outputLocale}}', formattedOutputLocale)\n .replace('{{presetOutputContent}}', presetContentStr)\n .replace('{{dictionaryDescription}}', dictionaryDescription ?? '')\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags ?? []))\n .replace('{{modeInstructions}}', getModeInstructions(mode));\n\n if (isToon) {\n const { text, usage } = await generateText({\n ...aiConfig,\n system: prompt,\n messages: [\n {\n role: 'user',\n content: [\n `# Translation Request`,\n `Please translate the following TOON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n // Strip markdown code blocks if present\n const cleanedText = text\n .replace(/^```(?:toon)?\\n([\\s\\S]*?)\\n```$/gm, '$1')\n .trim();\n\n const decodedJson = decode(cleanedText) as T;\n\n // schema.parse(decodedJson);\n\n return {\n fileContent: decodedJson,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n }\n\n const totalKeys = countKeys(entryFileContent);\n\n const MAX_SAFE_KEYS = 70; // You will need to tune this threshold based on testing\n\n const useStrictOutput = totalKeys <= MAX_SAFE_KEYS;\n\n // Use the AI SDK to generate the completion\n const { text, usage } = await generateText({\n ...validAiConfig,\n\n // Disable schema if Schema is too complex (Block with Anthropic)\n output: useStrictOutput ? Output.object({ schema }) : undefined,\n system: prompt,\n messages: [\n {\n role: 'user',\n content: [\n `# Translation Request`,\n `Please translate the following JSON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `CRITICAL INSTRUCTIONS:`,\n `- You MUST return ONLY raw, valid JSON.`,\n `- DO NOT wrap the response in Markdown code blocks (e.g., no \\`\\`\\`json).`,\n `- DO NOT include any conversational text before or after the JSON object.`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n // Extract and re-validate deeply\n const extractedJSON = extractJson(text);\n const validatedContent = schema.parse(extractedJSON) as T;\n\n return {\n fileContent: validatedContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"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"}
@@ -1,6 +1,6 @@
1
1
  import { readAsset } from "../_virtual/_utils_asset.mjs";
2
2
  import { Output, generateText } from "ai";
3
- import { z } from "zod";
3
+ import { z } from "zod/mini";
4
4
 
5
5
  //#region src/auditDictionaryMetadata/index.ts
6
6
  const aiDefaultOptions = {};
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type AuditDictionaryMetadataOptions = {\n fileContent: string;\n tags?: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryMetadata = async ({\n fileContent,\n tags,\n aiConfig,\n applicationContext,\n}: AuditDictionaryMetadataOptions): Promise<\n AuditFileResultData | undefined\n> => {\n const CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n const EXAMPLE_REQUEST = readAsset('./EXAMPLE_REQUEST.md');\n const EXAMPLE_RESPONSE = readAsset('./EXAMPLE_RESPONSE.md');\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{applicationContext}}',\n applicationContext ?? ''\n ).replace(\n '{{tags}}',\n tags\n ? JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )\n : ''\n );\n\n const { dataSerialization, ...restAiConfig } = aiConfig;\n const { output: _unusedOutput, ...validAiConfig } = restAiConfig;\n\n // Use the AI SDK to generate the completion\n const { output, usage } = await generateText({\n ...validAiConfig,\n output: Output.object({\n schema: z.object({\n title: z.string(),\n description: z.string(),\n tags: z.array(z.string()),\n }),\n }),\n system: prompt,\n messages: [\n { role: 'user', content: EXAMPLE_REQUEST },\n { role: 'assistant', content: EXAMPLE_RESPONSE },\n {\n role: 'user',\n content: fileContent,\n },\n ],\n });\n\n return {\n fileContent: output,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;AA0BA,MAAa,mBAA8B,CAE3C;;;;;;AAOA,MAAa,0BAA0B,OAAO,EAC5C,aACA,MACA,UACA,yBAGG;CACH,MAAM,kBAAkB,UAAU,aAAa;CAC/C,MAAM,kBAAkB,UAAU,sBAAsB;CACxD,MAAM,mBAAmB,UAAU,uBAAuB;CAG1D,MAAM,SAAS,gBAAgB,QAC7B,0BACA,sBAAsB,EACxB,CAAC,CAAC,QACA,YACA,OACI,KAAK,UACH,KACG,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,aAAa,CAAC,CACzD,KAAK,MAAM,GACd,MACA,CACF,IACA,EACN;CAEA,MAAM,EAAE,mBAAmB,GAAG,iBAAiB;CAC/C,MAAM,EAAE,QAAQ,eAAe,GAAG,kBAAkB;CAGpD,MAAM,EAAE,QAAQ,UAAU,MAAM,aAAa;EAC3C,GAAG;EACH,QAAQ,OAAO,OAAO,EACpB,QAAQ,EAAE,OAAO;GACf,OAAO,EAAE,OAAO;GAChB,aAAa,EAAE,OAAO;GACtB,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;EAC1B,CAAC,EACH,CAAC;EACD,QAAQ;EACR,UAAU;GACR;IAAE,MAAM;IAAQ,SAAS;GAAgB;GACzC;IAAE,MAAM;IAAa,SAAS;GAAiB;GAC/C;IACE,MAAM;IACN,SAAS;GACX;EACF;CACF,CAAC;CAED,OAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;CACnC;AACF"}
1
+ {"version":3,"file":"index.mjs","names":[],"sources":["../../../src/auditDictionaryMetadata/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod/mini';\nimport type { AIConfig, AIOptions } from '../aiSdk';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type AuditDictionaryMetadataOptions = {\n fileContent: string;\n tags?: Tag[];\n aiConfig: AIConfig;\n applicationContext?: string;\n};\n\nexport type AuditFileResultData = {\n fileContent: {\n title: string;\n description: string;\n tags: string[];\n };\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n // Keep default options\n};\n\n/**\n * Audits a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const auditDictionaryMetadata = async ({\n fileContent,\n tags,\n aiConfig,\n applicationContext,\n}: AuditDictionaryMetadataOptions): Promise<\n AuditFileResultData | undefined\n> => {\n const CHAT_GPT_PROMPT = readAsset('./PROMPT.md');\n const EXAMPLE_REQUEST = readAsset('./EXAMPLE_REQUEST.md');\n const EXAMPLE_RESPONSE = readAsset('./EXAMPLE_RESPONSE.md');\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = CHAT_GPT_PROMPT.replace(\n '{{applicationContext}}',\n applicationContext ?? ''\n ).replace(\n '{{tags}}',\n tags\n ? JSON.stringify(\n tags\n .map(({ key, description }) => `- ${key}: ${description}`)\n .join('\\n\\n'),\n null,\n 2\n )\n : ''\n );\n\n const { dataSerialization, ...restAiConfig } = aiConfig;\n const { output: _unusedOutput, ...validAiConfig } = restAiConfig;\n\n // Use the AI SDK to generate the completion\n const { output, usage } = await generateText({\n ...validAiConfig,\n output: Output.object({\n schema: z.object({\n title: z.string(),\n description: z.string(),\n tags: z.array(z.string()),\n }),\n }),\n system: prompt,\n messages: [\n { role: 'user', content: EXAMPLE_REQUEST },\n { role: 'assistant', content: EXAMPLE_RESPONSE },\n {\n role: 'user',\n content: fileContent,\n },\n ],\n });\n\n return {\n fileContent: output,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":";;;;;AA0BA,MAAa,mBAA8B,CAE3C;;;;;;AAOA,MAAa,0BAA0B,OAAO,EAC5C,aACA,MACA,UACA,yBAGG;CACH,MAAM,kBAAkB,UAAU,aAAa;CAC/C,MAAM,kBAAkB,UAAU,sBAAsB;CACxD,MAAM,mBAAmB,UAAU,uBAAuB;CAG1D,MAAM,SAAS,gBAAgB,QAC7B,0BACA,sBAAsB,EACxB,CAAC,CAAC,QACA,YACA,OACI,KAAK,UACH,KACG,KAAK,EAAE,KAAK,kBAAkB,KAAK,IAAI,IAAI,aAAa,CAAC,CACzD,KAAK,MAAM,GACd,MACA,CACF,IACA,EACN;CAEA,MAAM,EAAE,mBAAmB,GAAG,iBAAiB;CAC/C,MAAM,EAAE,QAAQ,eAAe,GAAG,kBAAkB;CAGpD,MAAM,EAAE,QAAQ,UAAU,MAAM,aAAa;EAC3C,GAAG;EACH,QAAQ,OAAO,OAAO,EACpB,QAAQ,EAAE,OAAO;GACf,OAAO,EAAE,OAAO;GAChB,aAAa,EAAE,OAAO;GACtB,MAAM,EAAE,MAAM,EAAE,OAAO,CAAC;EAC1B,CAAC,EACH,CAAC;EACD,QAAQ;EACR,UAAU;GACR;IAAE,MAAM;IAAQ,SAAS;GAAgB;GACzC;IAAE,MAAM;IAAa,SAAS;GAAiB;GAC/C;IACE,MAAM;IACN,SAAS;GACX;EACF;CACF,CAAC;CAED,OAAO;EACL,aAAa;EACb,WAAW,OAAO,eAAe;CACnC;AACF"}
@@ -1,6 +1,6 @@
1
- import { customQuery } from "./customQuery.mjs";
2
1
  import { AIProvider as AiProviders, checkAISDKAccess, getAIConfig } from "./aiSdk.mjs";
3
2
  import { auditDictionaryMetadata } from "./auditDictionaryMetadata/index.mjs";
3
+ import { customQuery } from "./customQuery.mjs";
4
4
  import { extractJson } from "./utils/extractJSON.mjs";
5
5
  import { translateJSON } from "./translateJSON/index.mjs";
6
6
  import { generateText, jsonSchema, stepCountIs, streamText, tool } from "ai";
@@ -2,7 +2,7 @@ import { AIProvider as AiProviders } from "../aiSdk.mjs";
2
2
  import { readAsset } from "../_virtual/_utils_asset.mjs";
3
3
  import { extractJson } from "../utils/extractJSON.mjs";
4
4
  import { Output, generateText } from "ai";
5
- import { z } from "zod";
5
+ import { z } from "zod/mini";
6
6
  import { DEFAULT_LOCALE } from "@intlayer/config/defaultValues";
7
7
  import { getLocaleName } from "@intlayer/core/localization";
8
8
  import { decode, encode } from "@toon-format/toon";
@@ -1 +1 @@
1
- {"version":3,"file":"index.mjs","names":["AIProvider"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { DEFAULT_LOCALE } from '@intlayer/config/defaultValues';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { decode, encode } from '@toon-format/toon';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\nimport { extractJson } from '../utils/extractJSON';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type TranslateJSONOptions<T = JSON> = {\n entryFileContent: T;\n presetOutputContent: Partial<T>;\n dictionaryDescription?: string;\n entryLocale: Locale;\n outputLocale: Locale;\n tags?: Tag[];\n aiConfig: AIConfig;\n mode: 'complete' | 'review';\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData<T = JSON> = {\n fileContent: T;\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-5-mini',\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string =>\n `${locale}: ${getLocaleName(locale, DEFAULT_LOCALE)}`;\n\n/**\n * Formats tag instructions for the AI prompt.\n * Creates a string with all available tags and their descriptions.\n *\n * @param tags - The list of tags to format.\n * @returns A formatted string with tag instructions.\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n // Prepare the tag instructions.\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\nconst getModeInstructions = (mode: 'complete' | 'review'): string => {\n if (mode === 'complete') {\n return 'Mode: \"Complete\" - Enrich the preset content with the missing keys and values in the output locale. Do not update existing keys. Everything should be returned in the output.';\n }\n\n return 'Mode: \"Review\" - Fill missing content and review existing keys from the preset content. If a key from the entry is missing in the output, it must be translated to the target language and added. If you detect misspelled content, or content that should be reformulated, correct it. If a translation is not coherent with the desired language, translate it.';\n};\n\nconst jsonToZod = (content: any): z.ZodTypeAny => {\n // Base case: content is a string (the translation target)\n if (typeof content === 'string') {\n return z.string();\n }\n\n // Base cases: primitives often preserved in i18n files (e.g. strict numbers/booleans)\n if (typeof content === 'number') {\n return z.number();\n }\n if (typeof content === 'boolean') {\n return z.boolean();\n }\n\n // Recursive case: Array\n if (Array.isArray(content)) {\n // If array is empty, we assume array of strings as default for i18n\n if (content.length === 0) {\n return z.array(z.string());\n }\n // We assume all items in the array share the structure of the first item\n return z.array(jsonToZod(content[0]));\n }\n\n // Recursive case: Object\n if (typeof content === 'object' && content !== null) {\n const shape: Record<string, z.ZodTypeAny> = {};\n for (const key in content) {\n shape[key] = jsonToZod(content[key]);\n }\n return z.object(shape);\n }\n\n // Fallback\n return z.string();\n};\n\nconst countKeys = (obj: any): number => {\n if (typeof obj !== 'object' || obj === null) return 0;\n let count = 0;\n for (const key in obj) {\n count += 1 + countKeys(obj[key]);\n }\n return count;\n};\n\n/**\n * TranslateJSONs a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const translateJSON = async <T>({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions<T>): Promise<\n TranslateJSONResultData<T> | undefined\n> => {\n const { dataSerialization, ...restAiConfig } = aiConfig;\n const { output: _unusedOutput, ...validAiConfig } = restAiConfig;\n\n const formattedEntryLocale = formatLocaleWithName(entryLocale);\n const formattedOutputLocale = formatLocaleWithName(outputLocale);\n\n const isToon = dataSerialization === 'toon';\n const promptFile = readAsset(\n isToon ? './PROMPT_TOON.md' : './PROMPT_JSON.md'\n );\n const entryContentStr = isToon\n ? encode(entryFileContent)\n : JSON.stringify(entryFileContent);\n const presetContentStr = isToon\n ? encode(presetOutputContent)\n : JSON.stringify(presetOutputContent);\n\n const schema = jsonToZod(entryFileContent);\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = promptFile\n .replace('{{entryLocale}}', formattedEntryLocale)\n .replace('{{outputLocale}}', formattedOutputLocale)\n .replace('{{presetOutputContent}}', presetContentStr)\n .replace('{{dictionaryDescription}}', dictionaryDescription ?? '')\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags ?? []))\n .replace('{{modeInstructions}}', getModeInstructions(mode));\n\n if (isToon) {\n const { text, usage } = await generateText({\n ...aiConfig,\n system: prompt,\n messages: [\n {\n role: 'user',\n content: [\n `# Translation Request`,\n `Please translate the following TOON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n // Strip markdown code blocks if present\n const cleanedText = text\n .replace(/^```(?:toon)?\\n([\\s\\S]*?)\\n```$/gm, '$1')\n .trim();\n\n const decodedJson = decode(cleanedText) as T;\n\n // schema.parse(decodedJson);\n\n return {\n fileContent: decodedJson,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n }\n\n const totalKeys = countKeys(entryFileContent);\n\n const MAX_SAFE_KEYS = 70; // You will need to tune this threshold based on testing\n\n const useStrictOutput = totalKeys <= MAX_SAFE_KEYS;\n\n // Use the AI SDK to generate the completion\n const { text, usage } = await generateText({\n ...validAiConfig,\n\n // Disable schema if Schema is too complex (Block with Anthropic)\n output: useStrictOutput ? Output.object({ schema }) : undefined,\n system: prompt,\n messages: [\n {\n role: 'user',\n content: [\n `# Translation Request`,\n `Please translate the following JSON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `CRITICAL INSTRUCTIONS:`,\n `- You MUST return ONLY raw, valid JSON.`,\n `- DO NOT wrap the response in Markdown code blocks (e.g., no \\`\\`\\`json).`,\n `- DO NOT include any conversational text before or after the JSON object.`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n // Extract and re-validate deeply\n const extractedJSON = extractJson(text);\n const validatedContent = schema.parse(extractedJSON) as T;\n\n return {\n fileContent: validatedContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.mjs","names":["AIProvider"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { DEFAULT_LOCALE } from '@intlayer/config/defaultValues';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport type { Locale } from '@intlayer/types/allLocales';\nimport { decode, encode } from '@toon-format/toon';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod/mini';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\nimport { extractJson } from '../utils/extractJSON';\n\ntype Tag = {\n key: string;\n description?: string;\n};\n\nexport type TranslateJSONOptions<T = JSON> = {\n entryFileContent: T;\n presetOutputContent: Partial<T>;\n dictionaryDescription?: string;\n entryLocale: Locale;\n outputLocale: Locale;\n tags?: Tag[];\n aiConfig: AIConfig;\n mode: 'complete' | 'review';\n applicationContext?: string;\n};\n\nexport type TranslateJSONResultData<T = JSON> = {\n fileContent: T;\n tokenUsed: number;\n};\n\nexport const aiDefaultOptions: AIOptions = {\n provider: AIProvider.OPENAI,\n model: 'gpt-5-mini',\n};\n\n/**\n * Format a locale with its name.\n *\n * @param locale - The locale to format.\n * @returns A string in the format \"locale: name\", e.g. \"en: English\".\n */\nconst formatLocaleWithName = (locale: Locale): string =>\n `${locale}: ${getLocaleName(locale, DEFAULT_LOCALE)}`;\n\n/**\n * Formats tag instructions for the AI prompt.\n * Creates a string with all available tags and their descriptions.\n *\n * @param tags - The list of tags to format.\n * @returns A formatted string with tag instructions.\n */\nconst formatTagInstructions = (tags: Tag[]): string => {\n if (!tags || tags.length === 0) {\n return '';\n }\n\n // Prepare the tag instructions.\n return `Based on the dictionary content, identify specific tags from the list below that would be relevant:\n \n${tags.map(({ key, description }) => `- ${key}: ${description}`).join('\\n\\n')}`;\n};\n\nconst getModeInstructions = (mode: 'complete' | 'review'): string => {\n if (mode === 'complete') {\n return 'Mode: \"Complete\" - Enrich the preset content with the missing keys and values in the output locale. Do not update existing keys. Everything should be returned in the output.';\n }\n\n return 'Mode: \"Review\" - Fill missing content and review existing keys from the preset content. If a key from the entry is missing in the output, it must be translated to the target language and added. If you detect misspelled content, or content that should be reformulated, correct it. If a translation is not coherent with the desired language, translate it.';\n};\n\nconst jsonToZod = (content: any): z.ZodMiniType => {\n // Base case: content is a string (the translation target)\n if (typeof content === 'string') {\n return z.string();\n }\n\n // Base cases: primitives often preserved in i18n files (e.g. strict numbers/booleans)\n if (typeof content === 'number') {\n return z.number();\n }\n if (typeof content === 'boolean') {\n return z.boolean();\n }\n\n // Recursive case: Array\n if (Array.isArray(content)) {\n // If array is empty, we assume array of strings as default for i18n\n if (content.length === 0) {\n return z.array(z.string());\n }\n // We assume all items in the array share the structure of the first item\n return z.array(jsonToZod(content[0]));\n }\n\n // Recursive case: Object\n if (typeof content === 'object' && content !== null) {\n const shape: Record<string, z.ZodMiniType> = {};\n for (const key in content) {\n shape[key] = jsonToZod(content[key]);\n }\n return z.object(shape);\n }\n\n // Fallback\n return z.string();\n};\n\nconst countKeys = (obj: any): number => {\n if (typeof obj !== 'object' || obj === null) return 0;\n let count = 0;\n for (const key in obj) {\n count += 1 + countKeys(obj[key]);\n }\n return count;\n};\n\n/**\n * TranslateJSONs a content declaration file by constructing a prompt for AI models.\n * The prompt includes details about the project's locales, file paths of content declarations,\n * and requests for identifying issues or inconsistencies.\n */\nexport const translateJSON = async <T>({\n entryFileContent,\n presetOutputContent,\n dictionaryDescription,\n aiConfig,\n entryLocale,\n outputLocale,\n tags,\n mode,\n applicationContext,\n}: TranslateJSONOptions<T>): Promise<\n TranslateJSONResultData<T> | undefined\n> => {\n const { dataSerialization, ...restAiConfig } = aiConfig;\n const { output: _unusedOutput, ...validAiConfig } = restAiConfig;\n\n const formattedEntryLocale = formatLocaleWithName(entryLocale);\n const formattedOutputLocale = formatLocaleWithName(outputLocale);\n\n const isToon = dataSerialization === 'toon';\n const promptFile = readAsset(\n isToon ? './PROMPT_TOON.md' : './PROMPT_JSON.md'\n );\n const entryContentStr = isToon\n ? encode(entryFileContent)\n : JSON.stringify(entryFileContent);\n const presetContentStr = isToon\n ? encode(presetOutputContent)\n : JSON.stringify(presetOutputContent);\n\n const schema = jsonToZod(entryFileContent);\n\n // Prepare the prompt for AI by replacing placeholders with actual values.\n const prompt = promptFile\n .replace('{{entryLocale}}', formattedEntryLocale)\n .replace('{{outputLocale}}', formattedOutputLocale)\n .replace('{{presetOutputContent}}', presetContentStr)\n .replace('{{dictionaryDescription}}', dictionaryDescription ?? '')\n .replace('{{applicationContext}}', applicationContext ?? '')\n .replace('{{tagsInstructions}}', formatTagInstructions(tags ?? []))\n .replace('{{modeInstructions}}', getModeInstructions(mode));\n\n if (isToon) {\n const { text, usage } = await generateText({\n ...aiConfig,\n system: prompt,\n messages: [\n {\n role: 'user',\n content: [\n `# Translation Request`,\n `Please translate the following TOON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n // Strip markdown code blocks if present\n const cleanedText = text\n .replace(/^```(?:toon)?\\n([\\s\\S]*?)\\n```$/gm, '$1')\n .trim();\n\n const decodedJson = decode(cleanedText) as T;\n\n // schema.parse(decodedJson);\n\n return {\n fileContent: decodedJson,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n }\n\n const totalKeys = countKeys(entryFileContent);\n\n const MAX_SAFE_KEYS = 70; // You will need to tune this threshold based on testing\n\n const useStrictOutput = totalKeys <= MAX_SAFE_KEYS;\n\n // Use the AI SDK to generate the completion\n const { text, usage } = await generateText({\n ...validAiConfig,\n\n // Disable schema if Schema is too complex (Block with Anthropic)\n output: useStrictOutput ? Output.object({ schema }) : undefined,\n system: prompt,\n messages: [\n {\n role: 'user',\n content: [\n `# Translation Request`,\n `Please translate the following JSON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `CRITICAL INSTRUCTIONS:`,\n `- You MUST return ONLY raw, valid JSON.`,\n `- DO NOT wrap the response in Markdown code blocks (e.g., no \\`\\`\\`json).`,\n `- DO NOT include any conversational text before or after the JSON object.`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n // Extract and re-validate deeply\n const extractedJSON = extractJson(text);\n const validatedContent = schema.parse(extractedJSON) as T;\n\n return {\n fileContent: validatedContent,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@intlayer/ai",
3
- "version": "9.5.3",
3
+ "version": "9.5.4-canary.0",
4
4
  "private": false,
5
5
  "description": "SDK that provides AI capabilities for Intlayer applications",
6
6
  "keywords": [
@@ -66,20 +66,21 @@
66
66
  "prepublish": "cp -f ../../../README.md ./README.md",
67
67
  "publish": "bun publish || true",
68
68
  "publish:canary": "bun publish --access public --tag canary || true",
69
+ "publish:ci": "bun ../../../scripts/publish-package.mjs",
69
70
  "publish:latest": "bun publish --access public --tag latest || true",
70
71
  "test": "bun --bun vitest run",
71
72
  "test:watch": "bun --bun vitest",
72
73
  "typecheck": "tsc --noEmit --project tsconfig.types.json"
73
74
  },
74
75
  "dependencies": {
75
- "@ai-sdk/anthropic": "4.0.53",
76
- "@ai-sdk/openai": "4.0.66",
77
- "@intlayer/api": "9.5.3",
78
- "@intlayer/config": "9.5.3",
79
- "@intlayer/core": "9.5.3",
80
- "@intlayer/types": "9.5.3",
76
+ "@ai-sdk/anthropic": "4.0.56",
77
+ "@ai-sdk/openai": "4.0.69",
78
+ "@intlayer/api": "9.5.4-canary.0",
79
+ "@intlayer/config": "9.5.4-canary.0",
80
+ "@intlayer/core": "9.5.4-canary.0",
81
+ "@intlayer/types": "9.5.4-canary.0",
81
82
  "@toon-format/toon": "4.1.1",
82
- "ai": "7.0.99",
83
+ "ai": "7.0.105",
83
84
  "zod": "4.6.5"
84
85
  },
85
86
  "devDependencies": {
@@ -91,7 +92,7 @@
91
92
  "rimraf": "6.1.3",
92
93
  "tsdown": "0.23.0",
93
94
  "typescript": "7.0.2",
94
- "vitest": "5.0.0"
95
+ "vitest": "5.0.1"
95
96
  },
96
97
  "peerDependencies": {
97
98
  "@ai-sdk/alibaba": "<=1.0.0",