@intlayer/ai 8.1.11 → 8.2.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/index.cjs +1 -1
- package/dist/cjs/translateJSON/index.cjs +4 -4
- package/dist/cjs/translateJSON/index.cjs.map +1 -1
- package/dist/esm/index.mjs +1 -1
- package/dist/esm/translateJSON/index.mjs +4 -4
- package/dist/esm/translateJSON/index.mjs.map +1 -1
- package/dist/types/translateJSON/index.d.ts.map +1 -1
- package/package.json +29 -29
package/dist/cjs/index.cjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./aiSdk.cjs`),t=require(`./customQuery.cjs`),n=require(`./auditDictionaryMetadata/index.cjs`),r=require(`./
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`./aiSdk.cjs`),t=require(`./customQuery.cjs`),n=require(`./auditDictionaryMetadata/index.cjs`),r=require(`./utils/extractJSON.cjs`),i=require(`./translateJSON/index.cjs`);let a=require(`ai`),o=require(`@intlayer/types`);exports.AIProvider=o.AiProviders,exports.auditDictionaryMetadata=n.auditDictionaryMetadata,exports.customQuery=t.customQuery,exports.extractJson=r.extractJson,Object.defineProperty(exports,`generateText`,{enumerable:!0,get:function(){return a.generateText}}),exports.getAIConfig=e.getAIConfig,Object.defineProperty(exports,`streamText`,{enumerable:!0,get:function(){return a.streamText}}),exports.translateJSON=i.translateJSON;
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_utils_asset.cjs`);let
|
|
1
|
+
Object.defineProperty(exports,Symbol.toStringTag,{value:`Module`});const e=require(`../_virtual/_utils_asset.cjs`),t=require(`../utils/extractJSON.cjs`);let n=require(`@intlayer/types`),r=require(`ai`),i=require(`zod`),a=require(`@intlayer/core/localization`),o=require(`@toon-format/toon`);const s={provider:n.AiProviders.OPENAI,model:`gpt-5-mini`},c=e=>`${e}: ${(0,a.getLocaleName)(e,n.Locales.ENGLISH)}`,l=e=>!e||e.length===0?``:`Based on the dictionary content, identify specific tags from the list below that would be relevant:
|
|
2
2
|
|
|
3
3
|
${e.map(({key:e,description:t})=>`- ${e}: ${t}`).join(`
|
|
4
4
|
|
|
5
|
-
`)}`,
|
|
6
|
-
`)}]});return{fileContent:(0,
|
|
7
|
-
`)}]});return{fileContent:
|
|
5
|
+
`)}`,u=e=>e===`complete`?`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.`:`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.`,d=e=>{if(typeof e==`string`)return i.z.string();if(typeof e==`number`)return i.z.number();if(typeof e==`boolean`)return i.z.boolean();if(Array.isArray(e))return e.length===0?i.z.array(i.z.string()):i.z.array(d(e[0]));if(typeof e==`object`&&e){let t={};for(let n in e)t[n]=d(e[n]);return i.z.object(t)}return i.z.string()},f=e=>{if(typeof e!=`object`||!e)return 0;let t=0;for(let n in e)t+=1+f(e[n]);return t},p=async({entryFileContent:n,presetOutputContent:i,dictionaryDescription:a,aiConfig:s,entryLocale:p,outputLocale:m,tags:h,mode:g,applicationContext:_})=>{let{dataSerialization:v,...y}=s,{output:b,...x}=y,S=c(p),C=c(m),w=v===`toon`,T=e.readAsset(w?`./PROMPT_TOON.md`:`./PROMPT_JSON.md`),E=w?(0,o.encode)(n):JSON.stringify(n),D=w?(0,o.encode)(i):JSON.stringify(i),O=d(n),k=T.replace(`{{entryLocale}}`,S).replace(`{{outputLocale}}`,C).replace(`{{presetOutputContent}}`,D).replace(`{{dictionaryDescription}}`,a??``).replace(`{{applicationContext}}`,_??``).replace(`{{tagsInstructions}}`,l(h??[])).replace(`{{modeInstructions}}`,u(g));if(w){let{text:e,usage:t}=await(0,r.generateText)({...s,messages:[{role:`system`,content:k},{role:`user`,content:[`# Translation Request`,`Please translate the following TOON content.`,`- **From:** ${S}`,`- **To:** ${C}`,``,`## Entry Content:`,E].join(`
|
|
6
|
+
`)}]});return{fileContent:(0,o.decode)(e.replace(/^```(?:toon)?\n([\s\S]*?)\n```$/gm,`$1`).trim()),tokenUsed:t?.totalTokens??0}}let A=f(n)<=70,{text:j,usage:M}=await(0,r.generateText)({...x,output:A?r.Output.object({schema:O}):void 0,messages:[{role:`system`,content:k},{role:`user`,content:[`# Translation Request`,`Please translate the following JSON content.`,`- **From:** ${S}`,`- **To:** ${C}`,``,`CRITICAL INSTRUCTIONS:`,`- You MUST return ONLY raw, valid JSON.`,"- DO NOT wrap the response in Markdown code blocks (e.g., no ```json).",`- DO NOT include any conversational text before or after the JSON object.`,``,`## Entry Content:`,E].join(`
|
|
7
|
+
`)}]}),N=t.extractJson(j);return{fileContent:O.parse(N),tokenUsed:M?.totalTokens??0}};exports.aiDefaultOptions=s,exports.translateJSON=p;
|
|
8
8
|
//# sourceMappingURL=index.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","names":["AIProvider","Locales","z","readAsset","Output"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { decode, encode } from '@toon-format/toon';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\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, Locales.ENGLISH)}`;\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.any();\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 // @ts-ignore\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 // 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 messages: [\n { role: 'system', content: prompt },\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 return {\n fileContent: decode(cleanedText) as T,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n }\n\n // Use the AI SDK to generate the completion\n const { output, usage } = await generateText({\n ...validAiConfig,\n output: Output.object({\n schema: jsonToZod(entryFileContent),\n }),\n messages: [\n { role: 'system', content: prompt },\n {\n role: 'user',\n // KEY CHANGE: Explicitly repeating instructions in the user message\n content: [\n `# Translation Request`,\n `Please translate the following JSON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n return {\n fileContent: output as T,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"6PA8BA,MAAa,EAA8B,CACzC,SAAUA,EAAAA,YAAW,OACrB,MAAO,aACR,CAQK,EAAwB,GAC5B,GAAG,EAAO,KAAA,EAAA,EAAA,eAAkB,EAAQC,EAAAA,QAAQ,QAAQ,GAShD,EAAyB,GACzB,CAAC,GAAQ,EAAK,SAAW,EACpB,GAIF;;EAEP,EAAK,KAAK,CAAE,MAAK,iBAAkB,KAAK,EAAI,IAAI,IAAc,CAAC,KAAK;;EAAO,GAGvE,EAAuB,GACvB,IAAS,WACJ,gLAGF,oWAGH,EAAa,GAA+B,CAEhD,GAAI,OAAO,GAAY,SACrB,OAAOC,EAAAA,EAAE,QAAQ,CAInB,GAAI,OAAO,GAAY,SACrB,OAAOA,EAAAA,EAAE,QAAQ,CAEnB,GAAI,OAAO,GAAY,UACrB,OAAOA,EAAAA,EAAE,SAAS,CAIpB,GAAI,MAAM,QAAQ,EAAQ,CAMxB,OAJI,EAAQ,SAAW,EACdA,EAAAA,EAAE,MAAMA,EAAAA,EAAE,QAAQ,CAAC,CAGrBA,EAAAA,EAAE,MAAM,EAAU,EAAQ,GAAG,CAAC,CAIvC,GAAI,OAAO,GAAY,UAAY,EAAkB,CACnD,IAAM,EAAsC,EAAE,CAC9C,IAAK,IAAM,KAAO,EAChB,EAAM,GAAO,EAAU,EAAQ,GAAK,CAEtC,OAAOA,EAAAA,EAAE,OAAO,EAAM,CAIxB,OAAOA,EAAAA,EAAE,KAAK,EAQH,EAAgB,MAAU,CACrC,mBACA,sBACA,wBACA,WACA,cACA,eACA,OACA,OACA,wBAGG,CACH,GAAM,CAAE,oBAAmB,GAAG,GAAiB,EAEzC,CAAE,OAAQ,EAAe,GAAG,GAAkB,EAE9C,EAAuB,EAAqB,EAAY,CACxD,EAAwB,EAAqB,EAAa,CAE1D,EAAS,IAAsB,OAC/B,EAAaC,EAAAA,UACjB,EAAS,mBAAqB,mBAC/B,CACK,EAAkB,GAAA,EAAA,EAAA,QACb,EAAiB,CACxB,KAAK,UAAU,EAAiB,CAC9B,EAAmB,GAAA,EAAA,EAAA,QACd,EAAoB,CAC3B,KAAK,UAAU,EAAoB,CAGjC,EAAS,EACZ,QAAQ,kBAAmB,EAAqB,CAChD,QAAQ,mBAAoB,EAAsB,CAClD,QAAQ,0BAA2B,EAAiB,CACpD,QAAQ,4BAA6B,GAAyB,GAAG,CACjE,QAAQ,yBAA0B,GAAsB,GAAG,CAC3D,QAAQ,uBAAwB,EAAsB,GAAQ,EAAE,CAAC,CAAC,CAClE,QAAQ,uBAAwB,EAAoB,EAAK,CAAC,CAE7D,GAAI,EAAQ,CACV,GAAM,CAAE,OAAM,SAAU,MAAA,EAAA,EAAA,cAAmB,CACzC,GAAG,EACH,SAAU,CACR,CAAE,KAAM,SAAU,QAAS,EAAQ,CACnC,CACE,KAAM,OACN,QAAS,CACP,wBACA,+CACA,eAAe,IACf,aAAa,IACb,GACA,oBACA,EACD,CAAC,KAAK;EAAK,CACb,CACF,CACF,CAAC,CAOF,MAAO,CACL,aAAA,EAAA,EAAA,QALkB,EACjB,QAAQ,oCAAqC,KAAK,CAClD,MAAM,CAGyB,CAChC,UAAW,GAAO,aAAe,EAClC,CAIH,GAAM,CAAE,SAAQ,SAAU,MAAA,EAAA,EAAA,cAAmB,CAC3C,GAAG,EACH,OAAQC,EAAAA,OAAO,OAAO,CACpB,OAAQ,EAAU,EAAiB,CACpC,CAAC,CACF,SAAU,CACR,CAAE,KAAM,SAAU,QAAS,EAAQ,CACnC,CACE,KAAM,OAEN,QAAS,CACP,wBACA,+CACA,eAAe,IACf,aAAa,IACb,GACA,oBACA,EACD,CAAC,KAAK;EAAK,CACb,CACF,CACF,CAAC,CAEF,MAAO,CACL,YAAa,EACb,UAAW,GAAO,aAAe,EAClC"}
|
|
1
|
+
{"version":3,"file":"index.cjs","names":["AIProvider","Locales","z","readAsset","Output","extractJson"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport { type Locale, Locales } from '@intlayer/types';\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, Locales.ENGLISH)}`;\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 messages: [\n { role: 'system', content: prompt },\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 messages: [\n { role: 'system', content: prompt },\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/dist/esm/index.mjs
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{AIProvider as e,getAIConfig as t}from"./aiSdk.mjs";import{customQuery as n}from"./customQuery.mjs";import{auditDictionaryMetadata as r}from"./auditDictionaryMetadata/index.mjs";import{
|
|
1
|
+
import{AIProvider as e,getAIConfig as t}from"./aiSdk.mjs";import{customQuery as n}from"./customQuery.mjs";import{auditDictionaryMetadata as r}from"./auditDictionaryMetadata/index.mjs";import{extractJson as i}from"./utils/extractJSON.mjs";import{translateJSON as a}from"./translateJSON/index.mjs";import{generateText as o,streamText as s}from"ai";export{e as AIProvider,r as auditDictionaryMetadata,n as customQuery,i as extractJson,o as generateText,t as getAIConfig,s as streamText,a as translateJSON};
|
|
@@ -1,8 +1,8 @@
|
|
|
1
|
-
import{AIProvider as e}from"../aiSdk.mjs";import{readAsset as t}from"../_virtual/_utils_asset.mjs";import{
|
|
1
|
+
import{AIProvider as e}from"../aiSdk.mjs";import{readAsset as t}from"../_virtual/_utils_asset.mjs";import{extractJson as n}from"../utils/extractJSON.mjs";import{Locales as r}from"@intlayer/types";import{Output as i,generateText as a}from"ai";import{z as o}from"zod";import{getLocaleName as s}from"@intlayer/core/localization";import{decode as c,encode as l}from"@toon-format/toon";const u={provider:e.OPENAI,model:`gpt-5-mini`},d=e=>`${e}: ${s(e,r.ENGLISH)}`,f=e=>!e||e.length===0?``:`Based on the dictionary content, identify specific tags from the list below that would be relevant:
|
|
2
2
|
|
|
3
3
|
${e.map(({key:e,description:t})=>`- ${e}: ${t}`).join(`
|
|
4
4
|
|
|
5
|
-
`)}`,
|
|
6
|
-
`)}]});return{fileContent:
|
|
7
|
-
`)}]});return{fileContent:
|
|
5
|
+
`)}`,p=e=>e===`complete`?`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.`:`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.`,m=e=>{if(typeof e==`string`)return o.string();if(typeof e==`number`)return o.number();if(typeof e==`boolean`)return o.boolean();if(Array.isArray(e))return e.length===0?o.array(o.string()):o.array(m(e[0]));if(typeof e==`object`&&e){let t={};for(let n in e)t[n]=m(e[n]);return o.object(t)}return o.string()},h=e=>{if(typeof e!=`object`||!e)return 0;let t=0;for(let n in e)t+=1+h(e[n]);return t},g=async({entryFileContent:e,presetOutputContent:r,dictionaryDescription:o,aiConfig:s,entryLocale:u,outputLocale:g,tags:_,mode:v,applicationContext:y})=>{let{dataSerialization:b,...x}=s,{output:S,...C}=x,w=d(u),T=d(g),E=b===`toon`,D=t(E?`./PROMPT_TOON.md`:`./PROMPT_JSON.md`),O=E?l(e):JSON.stringify(e),k=E?l(r):JSON.stringify(r),A=m(e),j=D.replace(`{{entryLocale}}`,w).replace(`{{outputLocale}}`,T).replace(`{{presetOutputContent}}`,k).replace(`{{dictionaryDescription}}`,o??``).replace(`{{applicationContext}}`,y??``).replace(`{{tagsInstructions}}`,f(_??[])).replace(`{{modeInstructions}}`,p(v));if(E){let{text:e,usage:t}=await a({...s,messages:[{role:`system`,content:j},{role:`user`,content:[`# Translation Request`,`Please translate the following TOON content.`,`- **From:** ${w}`,`- **To:** ${T}`,``,`## Entry Content:`,O].join(`
|
|
6
|
+
`)}]});return{fileContent:c(e.replace(/^```(?:toon)?\n([\s\S]*?)\n```$/gm,`$1`).trim()),tokenUsed:t?.totalTokens??0}}let M=h(e)<=70,{text:N,usage:P}=await a({...C,output:M?i.object({schema:A}):void 0,messages:[{role:`system`,content:j},{role:`user`,content:[`# Translation Request`,`Please translate the following JSON content.`,`- **From:** ${w}`,`- **To:** ${T}`,``,`CRITICAL INSTRUCTIONS:`,`- You MUST return ONLY raw, valid JSON.`,"- DO NOT wrap the response in Markdown code blocks (e.g., no ```json).",`- DO NOT include any conversational text before or after the JSON object.`,``,`## Entry Content:`,O].join(`
|
|
7
|
+
`)}]}),F=n(N);return{fileContent:A.parse(F),tokenUsed:P?.totalTokens??0}};export{u as aiDefaultOptions,g as translateJSON};
|
|
8
8
|
//# sourceMappingURL=index.mjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","names":["AIProvider"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport { type Locale, Locales } from '@intlayer/types';\nimport { decode, encode } from '@toon-format/toon';\nimport { generateText, Output } from 'ai';\nimport { z } from 'zod';\nimport { type AIConfig, type AIOptions, AIProvider } from '../aiSdk';\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, Locales.ENGLISH)}`;\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.any();\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 // @ts-ignore\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 // 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 messages: [\n { role: 'system', content: prompt },\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 return {\n fileContent: decode(cleanedText) as T,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n }\n\n // Use the AI SDK to generate the completion\n const { output, usage } = await generateText({\n ...validAiConfig,\n output: Output.object({\n schema: jsonToZod(entryFileContent),\n }),\n messages: [\n { role: 'system', content: prompt },\n {\n role: 'user',\n // KEY CHANGE: Explicitly repeating instructions in the user message\n content: [\n `# Translation Request`,\n `Please translate the following JSON content.`,\n `- **From:** ${formattedEntryLocale}`,\n `- **To:** ${formattedOutputLocale}`,\n ``,\n `## Entry Content:`,\n entryContentStr,\n ].join('\\n'),\n },\n ],\n });\n\n return {\n fileContent: output as T,\n tokenUsed: usage?.totalTokens ?? 0,\n };\n};\n"],"mappings":"sUA8BA,MAAa,EAA8B,CACzC,SAAUA,EAAW,OACrB,MAAO,aACR,CAQK,EAAwB,GAC5B,GAAG,EAAO,IAAI,EAAc,EAAQ,EAAQ,QAAQ,GAShD,EAAyB,GACzB,CAAC,GAAQ,EAAK,SAAW,EACpB,GAIF;;EAEP,EAAK,KAAK,CAAE,MAAK,iBAAkB,KAAK,EAAI,IAAI,IAAc,CAAC,KAAK;;EAAO,GAGvE,EAAuB,GACvB,IAAS,WACJ,gLAGF,oWAGH,EAAa,GAA+B,CAEhD,GAAI,OAAO,GAAY,SACrB,OAAO,EAAE,QAAQ,CAInB,GAAI,OAAO,GAAY,SACrB,OAAO,EAAE,QAAQ,CAEnB,GAAI,OAAO,GAAY,UACrB,OAAO,EAAE,SAAS,CAIpB,GAAI,MAAM,QAAQ,EAAQ,CAMxB,OAJI,EAAQ,SAAW,EACd,EAAE,MAAM,EAAE,QAAQ,CAAC,CAGrB,EAAE,MAAM,EAAU,EAAQ,GAAG,CAAC,CAIvC,GAAI,OAAO,GAAY,UAAY,EAAkB,CACnD,IAAM,EAAsC,EAAE,CAC9C,IAAK,IAAM,KAAO,EAChB,EAAM,GAAO,EAAU,EAAQ,GAAK,CAEtC,OAAO,EAAE,OAAO,EAAM,CAIxB,OAAO,EAAE,KAAK,EAQH,EAAgB,MAAU,CACrC,mBACA,sBACA,wBACA,WACA,cACA,eACA,OACA,OACA,wBAGG,CACH,GAAM,CAAE,oBAAmB,GAAG,GAAiB,EAEzC,CAAE,OAAQ,EAAe,GAAG,GAAkB,EAE9C,EAAuB,EAAqB,EAAY,CACxD,EAAwB,EAAqB,EAAa,CAE1D,EAAS,IAAsB,OAC/B,EAAa,EACjB,EAAS,mBAAqB,mBAC/B,CACK,EAAkB,EACpB,EAAO,EAAiB,CACxB,KAAK,UAAU,EAAiB,CAC9B,EAAmB,EACrB,EAAO,EAAoB,CAC3B,KAAK,UAAU,EAAoB,CAGjC,EAAS,EACZ,QAAQ,kBAAmB,EAAqB,CAChD,QAAQ,mBAAoB,EAAsB,CAClD,QAAQ,0BAA2B,EAAiB,CACpD,QAAQ,4BAA6B,GAAyB,GAAG,CACjE,QAAQ,yBAA0B,GAAsB,GAAG,CAC3D,QAAQ,uBAAwB,EAAsB,GAAQ,EAAE,CAAC,CAAC,CAClE,QAAQ,uBAAwB,EAAoB,EAAK,CAAC,CAE7D,GAAI,EAAQ,CACV,GAAM,CAAE,OAAM,SAAU,MAAM,EAAa,CACzC,GAAG,EACH,SAAU,CACR,CAAE,KAAM,SAAU,QAAS,EAAQ,CACnC,CACE,KAAM,OACN,QAAS,CACP,wBACA,+CACA,eAAe,IACf,aAAa,IACb,GACA,oBACA,EACD,CAAC,KAAK;EAAK,CACb,CACF,CACF,CAAC,CAOF,MAAO,CACL,YAAa,EALK,EACjB,QAAQ,oCAAqC,KAAK,CAClD,MAAM,CAGyB,CAChC,UAAW,GAAO,aAAe,EAClC,CAIH,GAAM,CAAE,SAAQ,SAAU,MAAM,EAAa,CAC3C,GAAG,EACH,OAAQ,EAAO,OAAO,CACpB,OAAQ,EAAU,EAAiB,CACpC,CAAC,CACF,SAAU,CACR,CAAE,KAAM,SAAU,QAAS,EAAQ,CACnC,CACE,KAAM,OAEN,QAAS,CACP,wBACA,+CACA,eAAe,IACf,aAAa,IACb,GACA,oBACA,EACD,CAAC,KAAK;EAAK,CACb,CACF,CACF,CAAC,CAEF,MAAO,CACL,YAAa,EACb,UAAW,GAAO,aAAe,EAClC"}
|
|
1
|
+
{"version":3,"file":"index.mjs","names":["AIProvider"],"sources":["../../../src/translateJSON/index.ts"],"sourcesContent":["import { readAsset } from 'utils:asset';\nimport { getLocaleName } from '@intlayer/core/localization';\nimport { type Locale, Locales } from '@intlayer/types';\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, Locales.ENGLISH)}`;\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 messages: [\n { role: 'system', content: prompt },\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 messages: [\n { role: 'system', content: prompt },\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 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/translateJSON/index.ts"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"index.d.ts","names":[],"sources":["../../../src/translateJSON/index.ts"],"mappings":";;;;KASK,GAAA;EACH,GAAA;EACA,WAAA;AAAA;AAAA,KAGU,oBAAA,KAAyB,IAAA;EACnC,gBAAA,EAAkB,CAAA;EAClB,mBAAA,EAAqB,OAAA,CAAQ,CAAA;EAC7B,qBAAA;EACA,WAAA,EAAa,MAAA;EACb,YAAA,EAAc,MAAA;EACd,IAAA,GAAO,GAAA;EACP,QAAA,EAAU,QAAA;EACV,IAAA;EACA,kBAAA;AAAA;AAAA,KAGU,uBAAA,KAA4B,IAAA;EACtC,WAAA,EAAa,CAAA;EACb,SAAA;AAAA;AAAA,cAGW,gBAAA,EAAkB,SAAA;;;;;;cA2FlB,aAAA;EAA0B,gBAAA;EAAA,mBAAA;EAAA,qBAAA;EAAA,QAAA;EAAA,WAAA;EAAA,YAAA;EAAA,IAAA;EAAA,IAAA;EAAA;AAAA,GAUpC,oBAAA,CAAqB,CAAA,MAAK,OAAA,CAC3B,uBAAA,CAAwB,CAAA"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@intlayer/ai",
|
|
3
|
-
"version": "8.1
|
|
3
|
+
"version": "8.2.1",
|
|
4
4
|
"private": false,
|
|
5
5
|
"description": "SDK that provides AI capabilities for Intlayer applications",
|
|
6
6
|
"keywords": [
|
|
@@ -72,28 +72,28 @@
|
|
|
72
72
|
"typecheck": "tsc --noEmit --project tsconfig.types.json"
|
|
73
73
|
},
|
|
74
74
|
"dependencies": {
|
|
75
|
-
"@ai-sdk/anthropic": "3.0.
|
|
76
|
-
"@ai-sdk/google": "3.0.
|
|
77
|
-
"@ai-sdk/openai": "3.0.
|
|
78
|
-
"@intlayer/api": "8.1
|
|
79
|
-
"@intlayer/config": "8.1
|
|
80
|
-
"@intlayer/core": "8.1
|
|
81
|
-
"@intlayer/types": "8.1
|
|
75
|
+
"@ai-sdk/anthropic": "3.0.55",
|
|
76
|
+
"@ai-sdk/google": "3.0.37",
|
|
77
|
+
"@ai-sdk/openai": "3.0.39",
|
|
78
|
+
"@intlayer/api": "8.2.1",
|
|
79
|
+
"@intlayer/config": "8.2.1",
|
|
80
|
+
"@intlayer/core": "8.2.1",
|
|
81
|
+
"@intlayer/types": "8.2.1",
|
|
82
82
|
"@toon-format/toon": "2.1.0",
|
|
83
|
-
"ai": "6.0.
|
|
83
|
+
"ai": "6.0.112",
|
|
84
84
|
"zod": "4.3.6"
|
|
85
85
|
},
|
|
86
86
|
"devDependencies": {
|
|
87
|
-
"@ai-sdk/alibaba": "1.0.
|
|
88
|
-
"@ai-sdk/amazon-bedrock": "4.0.
|
|
89
|
-
"@ai-sdk/deepseek": "2.0.
|
|
90
|
-
"@ai-sdk/fireworks": "2.0.
|
|
91
|
-
"@ai-sdk/google": "3.0.
|
|
92
|
-
"@ai-sdk/google-vertex": "^4.0.
|
|
93
|
-
"@ai-sdk/groq": "3.0.
|
|
94
|
-
"@ai-sdk/huggingface": "1.0.
|
|
95
|
-
"@ai-sdk/mistral": "3.0.
|
|
96
|
-
"@ai-sdk/togetherai": "2.0.
|
|
87
|
+
"@ai-sdk/alibaba": "1.0.8",
|
|
88
|
+
"@ai-sdk/amazon-bedrock": "4.0.74",
|
|
89
|
+
"@ai-sdk/deepseek": "2.0.22",
|
|
90
|
+
"@ai-sdk/fireworks": "2.0.38",
|
|
91
|
+
"@ai-sdk/google": "3.0.37",
|
|
92
|
+
"@ai-sdk/google-vertex": "^4.0.74",
|
|
93
|
+
"@ai-sdk/groq": "3.0.27",
|
|
94
|
+
"@ai-sdk/huggingface": "1.0.35",
|
|
95
|
+
"@ai-sdk/mistral": "3.0.22",
|
|
96
|
+
"@ai-sdk/togetherai": "2.0.37",
|
|
97
97
|
"@openrouter/ai-sdk-provider": "2.2.3",
|
|
98
98
|
"@types/node": "25.3.3",
|
|
99
99
|
"@utils/ts-config": "1.0.4",
|
|
@@ -105,16 +105,16 @@
|
|
|
105
105
|
"vitest": "4.0.18"
|
|
106
106
|
},
|
|
107
107
|
"peerDependencies": {
|
|
108
|
-
"@ai-sdk/alibaba": "1.0.
|
|
109
|
-
"@ai-sdk/amazon-bedrock": "4.0.
|
|
110
|
-
"@ai-sdk/deepseek": "2.0.
|
|
111
|
-
"@ai-sdk/fireworks": "2.0.
|
|
112
|
-
"@ai-sdk/google": "3.0.
|
|
113
|
-
"@ai-sdk/google-vertex": "^4.0.
|
|
114
|
-
"@ai-sdk/groq": "3.0.
|
|
115
|
-
"@ai-sdk/huggingface": "1.0.
|
|
116
|
-
"@ai-sdk/mistral": "3.0.
|
|
117
|
-
"@ai-sdk/togetherai": "2.0.
|
|
108
|
+
"@ai-sdk/alibaba": "1.0.8",
|
|
109
|
+
"@ai-sdk/amazon-bedrock": "4.0.74",
|
|
110
|
+
"@ai-sdk/deepseek": "2.0.22",
|
|
111
|
+
"@ai-sdk/fireworks": "2.0.38",
|
|
112
|
+
"@ai-sdk/google": "3.0.37",
|
|
113
|
+
"@ai-sdk/google-vertex": "^4.0.74",
|
|
114
|
+
"@ai-sdk/groq": "3.0.27",
|
|
115
|
+
"@ai-sdk/huggingface": "1.0.35",
|
|
116
|
+
"@ai-sdk/mistral": "3.0.22",
|
|
117
|
+
"@ai-sdk/togetherai": "2.0.37",
|
|
118
118
|
"@openrouter/ai-sdk-provider": "2.2.3"
|
|
119
119
|
},
|
|
120
120
|
"peerDependenciesMeta": {
|