@scopeact/autoi18n 1.2.1 → 1.3.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,4 +1,3 @@
1
-
2
1
  # @scopeact/autoi18n
3
2
 
4
3
  [![npm version](https://img.shields.io/npm/v/@scopeact/autoi18n.svg?style=flat-square)](https://www.npmjs.com/package/@scopeact/autoi18n)
@@ -88,12 +87,15 @@ Created via `init`, the `auto-i18n.config.json` controls the magic:
88
87
 
89
88
  ```json
90
89
  {
90
+ "$schema": "https://unpkg.com/@scopeact/autoi18n@latest/schema.json",
91
91
  "sourceLang": "pt",
92
- "targetLangs": ["en", "es"],
92
+ "targetLangs": [ "en", "es" ],
93
+ "autoInject": false,
93
94
  "i18nLibrary": "@scopeact/autoi18n",
94
95
  "provider": "openai",
95
96
  "localesDir": "./locales",
96
- "files": ["src/**/*.tsx"]
97
+ "model": "gpt-3.5-turbo",
98
+ "files": [ "app/**/*.tsx", "components/**/*.tsx" ]
97
99
  }
98
100
  ```
99
101
 
@@ -1,6 +1,6 @@
1
1
  // src/core/scanner.ts
2
2
  import ora2 from "ora";
3
- import { Project, SyntaxKind } from "ts-morph";
3
+ import { Project, SyntaxKind as SyntaxKind2 } from "ts-morph";
4
4
 
5
5
  // src/core/config.ts
6
6
  import fs from "fs";
@@ -21,11 +21,11 @@ function createConfig() {
21
21
  sourceLang: "pt",
22
22
  targetLangs: ["en", "es"],
23
23
  autoInject: false,
24
- i18nLibrary: "react-i18next",
24
+ i18nLibrary: "@scopeact/autoi18n",
25
25
  provider: "openai",
26
26
  localesDir: "./locales",
27
27
  model: "gpt-3.5-turbo",
28
- files: ["src/**/*.tsx", "app/**/*.tsx"]
28
+ files: ["app/**/*.tsx", "components/**/*.tsx"]
29
29
  };
30
30
  fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
31
31
  spinner.succeed();
@@ -184,20 +184,44 @@ async function askOllama(prompt, model, mode) {
184
184
  import chalk from "chalk";
185
185
 
186
186
  // src/core/dependecies.ts
187
- import "ts-morph";
187
+ import { SyntaxKind } from "ts-morph";
188
188
  function ensureI18nImport(sourceFile) {
189
- const importDeclaration = sourceFile.getImportDeclaration("react-i18next");
189
+ const isClientFile = sourceFile.getStatementsWithComments().some((s) => s.getText().includes("'use client'") || s.getText().includes('"use client"'));
190
+ const i18nLibBase = readConfig("i18nLibrary") || "@scopeact/autoi18n";
191
+ let targetModule = i18nLibBase;
192
+ let functionToInject = "";
193
+ if (i18nLibBase === "@scopeact/autoi18n") {
194
+ targetModule = isClientFile ? "@scopeact/autoi18n/client" : "@scopeact/autoi18n/server";
195
+ functionToInject = isClientFile ? "useI18n" : "getI18n";
196
+ } else {
197
+ const legacyMap = { "react-i18next": "useTranslation", "next-intl": "useTranslations" };
198
+ functionToInject = legacyMap[i18nLibBase];
199
+ }
200
+ let importDeclaration = sourceFile.getImportDeclaration(targetModule);
190
201
  if (!importDeclaration) {
191
202
  sourceFile.addImportDeclaration({
192
- moduleSpecifier: "react-i18next",
193
- namedImports: ["useTranslation"]
203
+ moduleSpecifier: targetModule,
204
+ namedImports: [functionToInject]
194
205
  });
195
206
  } else {
196
- const namedImports = importDeclaration.getNamedImports().map((i) => i.getName());
197
- if (!namedImports.includes("useTranslation")) {
198
- importDeclaration.addNamedImport("useTranslation");
207
+ if (!importDeclaration.getNamedImports().some((i) => i.getName() === functionToInject)) {
208
+ importDeclaration.addNamedImport(functionToInject);
199
209
  }
200
210
  }
211
+ sourceFile.getFunctions().forEach((fn) => {
212
+ const isComponent = fn.getDescendantsOfKind(SyntaxKind.JsxElement).length > 0;
213
+ if (!isComponent) return;
214
+ if (isClientFile) {
215
+ if (!fn.getText().includes(functionToInject)) {
216
+ fn.insertStatements(0, `const { t } = ${functionToInject}();`);
217
+ }
218
+ } else {
219
+ if (!fn.getText().includes(functionToInject)) {
220
+ fn.setIsAsync(true);
221
+ fn.insertStatements(0, `const { t } = await ${functionToInject}();`);
222
+ }
223
+ }
224
+ });
201
225
  }
202
226
 
203
227
  // src/core/scanner.ts
@@ -221,7 +245,7 @@ async function scanFilesAndRun() {
221
245
  ensureI18nImport(arquivo);
222
246
  }
223
247
  arquivo.forEachDescendant((node) => {
224
- if (node.getKind() === SyntaxKind.JsxText) {
248
+ if (node.getKind() === SyntaxKind2.JsxText) {
225
249
  const textoOriginal = node.getText();
226
250
  if (textoOriginal.trim().length > 0) {
227
251
  pendentes.push({ node, text: textoOriginal.trim() });
@@ -293,4 +317,4 @@ export {
293
317
  run,
294
318
  init
295
319
  };
296
- //# sourceMappingURL=chunk-RLNUAAKC.js.map
320
+ //# sourceMappingURL=chunk-BITUBE6M.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/core/scanner.ts","../src/core/config.ts","../src/core/utils.ts","../src/core/ai.ts","../src/core/dependecies.ts","../src/core/index.ts"],"sourcesContent":["import ora from \"ora\";\nimport { Node, Project, SyntaxKind } from \"ts-morph\";\nimport { readConfig } from \"./config.js\";\nimport { cleanJsonResponse, createExtractionPrompt, createTranslationPrompt, lerJson, salvarJson, type ExtractionItem } from \"./utils.js\";\nimport { askLLM } from \"./ai.js\";\nimport chalk from \"chalk\";\nimport { ensureI18nImport } from \"./dependecies.js\";\n\ninterface JsonTranslation {\n [key: string]: string;\n}\n\nexport async function scanFilesAndRun() {\n const sourceLang = readConfig('sourceLang') || 'pt';\n const targetLangs = readConfig('targetLangs') || ['en'];\n const localesDir = readConfig('localesDir') || './locales';\n\n const pendentes: { node: Node, text: string }[] = [];\n const stringsUnicas = new Set<string>();\n const novasChavesParaSalvar: Record<string, string> = {};\n const mapaTextoParaChave: Record<string, string> = {};\n\n const project = new Project({\n skipAddingFilesFromTsConfig: true,\n });\n\n const spinner = ora(\"📂 Carregando e analisando arquivos...\").start();\n project.addSourceFilesAtPaths(readConfig(\"files\"));\n\n const arquivos = project.getSourceFiles();\n\n for (const arquivo of arquivos) {\n const shouldInject = readConfig(\"autoInject\") || false;\n if(shouldInject) {\n ensureI18nImport(arquivo);\n }\n arquivo.forEachDescendant((node) => {\n if (node.getKind() === SyntaxKind.JsxText) {\n const textoOriginal = node.getText();\n if (textoOriginal.trim().length > 0) {\n pendentes.push({ node, text: textoOriginal.trim() });\n stringsUnicas.add(textoOriginal.trim());\n }\n }\n });\n }\n\n //pluralização elegante\n const fileCount = arquivos.length;\n const strCount = stringsUnicas.size;\n const sFiles = fileCount === 1 ? '' : 's';\n const sStr = strCount === 1 ? '' : 's';\n spinner.succeed(`Encontrado(s) ${chalk.cyan(strCount)} texto${sStr} único${sStr} em ${chalk.cyan(fileCount)} arquivo${sFiles}.`);\n const spinnerLLM = ora(`🧠 Gerando chaves inteligentes para ${strCount} textos...`).start();\n const listaParaIA = Array.from(stringsUnicas).map((text, index) => ({\n id: index,\n text\n })) as ExtractionItem[];\n\n const respostaIA = JSON.parse(cleanJsonResponse(await askLLM(createExtractionPrompt(listaParaIA), 'json')));\n\n respostaIA.forEach((item: any) => {\n const originalText = listaParaIA.find(l => l.id === item.id)?.text;\n if (originalText) mapaTextoParaChave[originalText] = item.key;\n });\n\n pendentes.forEach(({ node, text }) => {\n const key = mapaTextoParaChave[text];\n if (key) {\n node.replaceWithText(`{t('${key}')}`);\n }\n });\n project.saveSync();\n spinnerLLM.succeed(chalk.green(\"Código atualizado com as novas chaves i18n!\"));\n\n const spinnerSave = ora(\"💾 Gerando arquivos de tradução...\").start();\n\n for (const [textoOriginal, chaveGerada] of Object.entries(mapaTextoParaChave)) {\n novasChavesParaSalvar[chaveGerada] = textoOriginal;\n }\n\n const pathPt = `${localesDir}/${sourceLang}.json`;\n const jsonPtAtual = lerJson(pathPt);\n const jsonPtFinal = { ...jsonPtAtual, ...novasChavesParaSalvar };\n\n salvarJson(pathPt, jsonPtFinal);\n\n // Salvar em outros idiomas\n for (const idioma of targetLangs) {\n const pathIdioma = `${localesDir}/${idioma}.json`;\n const jsonIdiomaAtual = lerJson(pathIdioma) as JsonTranslation;\n\n const deltaParaTraduzir: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(novasChavesParaSalvar)) {\n if (!jsonIdiomaAtual[key]) {\n deltaParaTraduzir[key] = value;\n }\n }\n\n if (Object.keys(deltaParaTraduzir).length > 0) {\n const prompt = createTranslationPrompt(deltaParaTraduzir, sourceLang, idioma);\n\n const respostaIA = cleanJsonResponse(await askLLM(prompt, 'json'));\n const traducoesEn = JSON.parse(respostaIA);\n\n const jsonIdiomaFinal = { ...jsonIdiomaAtual, ...traducoesEn };\n salvarJson(pathIdioma, jsonIdiomaFinal);\n }\n }\n\n spinnerSave.succeed('Tradução concluída e chaves salvas com sucesso!');\n}","import fs from \"fs\";\nimport path from \"path\";\nimport ora from \"ora\";\n\nconst configPath = path.join(process.cwd(), \"auto-i18n.config.json\");\n\nexport function readConfig(key: string) {\n if (!fs.existsSync(configPath)) {\n createConfig();\n }\n const config = JSON.parse(fs.readFileSync(configPath, \"utf-8\"));\n return config[key];\n}\n\nexport function createConfig() {\n const spinner = ora('Criando arquivos de configuração...').start();\n const defaultConfig = {\n $schema: `https://unpkg.com/@scopeact/autoi18n@latest/schema.json`,\n sourceLang: \"pt\",\n targetLangs: [\"en\", \"es\"],\n autoInject: false,\n i18nLibrary: \"@scopeact/autoi18n\",\n provider: \"openai\",\n localesDir: \"./locales\",\n model: \"gpt-3.5-turbo\",\n files: [\"app/**/*.tsx\", \"components/**/*.tsx\"]\n };\n\n fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));\n spinner.succeed();\n}","import fs from 'fs';\nimport path from 'path';\n\nexport interface ExtractionItem {\n id: number;\n text: string;\n}\n\nexport function createExtractionPrompt(itens: ExtractionItem[], contexto?: string): string {\n return `\nVocê é um especialista em i18n. Recebi uma lista de textos de uma interface React.\nSua tarefa é gerar chaves (keys) semânticas e curtas em inglês (snake_case) para cada item.\n\nREGRAS:\n1. Retorne APENAS um array de objetos JSON.\n2. Cada objeto deve ter: \"id\" (o mesmo enviado) e \"key\" (a chave sugerida).\n3. As chaves devem ser curtas (max 4 palavras). \n4. Textos longos devem ter chaves que resumam o sentido.\n\nEXEMPLO DE ENTRADA:\n[ { \"id\": 1, \"text\": \"Salvar alterações\" } ]\n\nEXEMPLO DE SAÍDA:\n[ { \"id\": 1, \"key\": \"button_save_changes\" } ]\n\nITENS PARA PROCESSAR:\n${JSON.stringify(itens, null, 2)}\n\n${contexto ? `Contexto do projeto: ${contexto}` : ''}\n`.trim();\n}\n\nexport function createTranslationPrompt(\n delta: object, \n from: string, \n to: string, \n contexto?: string\n): string {\n return `\nVocê é um tradutor sênior de software especializado em localização (i18n).\nSua tarefa é traduzir os VALORES das novas chaves de interface que foram adicionadas ao projeto.\n\nIDIOMAS: De \"${from}\" para \"${to}\".\n\nREGRAS CRÍTICAS:\n1. MANUTENÇÃO DE CHAVES: Mantenha as chaves (keys) EXATAMENTE como estão. Não as traduza nem as altere.\n2. TRADUÇÃO DE VALORES: Traduza apenas os valores para o idioma de destino.\n3. VARIÁVEIS E PLACEHOLDERS: Preserve placeholders como {{name}}, {0}, %s ou qualquer texto entre chaves. Eles NÃO devem ser traduzidos.\n4. TOM DE VOZ: Use um tom profissional e conciso, adequado para interfaces de usuário (botões, labels, mensagens de erro).\n5. FORMATO: Retorne APENAS o objeto JSON puro, sem explicações ou blocos de código markdown.\n\n${contexto ? `CONTEXTO DO PROJETO: ${contexto}` : ''}\n\nDADOS PARA TRADUZIR:\n${JSON.stringify(delta, null, 2)}\n`.trim();\n}\n\nexport function cleanJsonResponse(text: string): string {\n return text.replace(/```json|```/g, '').trim();\n}\n\nexport function lerJson(caminho: string): object {\n if (!fs.existsSync(caminho)) {\n fs.mkdirSync(path.dirname(caminho), { recursive: true });\n fs.writeFileSync(caminho, '{}');\n return {};\n }\n \n return JSON.parse(fs.readFileSync(caminho, 'utf-8'));\n}\n\nexport function salvarJson(caminho: string, json: object): void {\n if(!fs.existsSync(caminho)) {\n fs.mkdirSync(path.dirname(caminho), { recursive: true });\n }\n fs.writeFileSync(caminho, JSON.stringify(json, null, 2));\n}","import dotenv from 'dotenv';\ndotenv.config();\nimport { OpenAI } from 'openai';\nimport { GoogleGenerativeAI } from '@google/generative-ai';\nimport { readConfig } from \"./config.js\";\n\nexport type AIProvider = 'openai' | 'google' | 'deepseek' | 'openrouter' | 'ollama';\n\nexport async function askLLM(prompt: string, mode = 'text'): Promise<string> {\n const provider = readConfig('provider') || 'openai' as AIProvider;\n const model = readConfig('model');\n checkEnv(provider);\n switch (provider) {\n case 'openai':\n return askOpenAI(prompt, model || 'gpt-4o', mode);\n case 'google':\n return askGemini(prompt, model || 'gemini-2.5-flash', mode);\n case 'deepseek':\n return askDeepSeek(prompt, model || 'deepseek-chat', mode);\n case 'openrouter':\n return askOpenRouter(prompt, model || 'google/gemini-2.0-flash-001', mode);\n case 'ollama':\n return askOllama(prompt, model || 'llama3', mode);\n default:\n throw new Error(`Provedor de IA desconhecido: ${provider}`);\n }\n}\n\nexport function checkEnv(provider: string) {\n if (!process.env[`${provider.toUpperCase()}_API_KEY`]) {\n throw new Error(`${provider.toUpperCase()}_API_KEY não foi definido`);\n }\n}\n\nasync function askOpenAI(prompt: string, model: string, mode: string) {\n const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n const response = await openai.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askGemini(prompt: string, model: string, mode: string) {\n const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || '');\n const gemini = genAI.getGenerativeModel({ model, generationConfig: { responseMimeType: mode === 'json' ? 'application/json' : 'text/plain' } });\n const result = await gemini.generateContent(prompt);\n return result.response.text();\n}\n\nasync function askDeepSeek(prompt: string, model: string, mode: string) {\n const client = new OpenAI({\n apiKey: process.env.DEEPSEEK_API_KEY,\n baseURL: 'https://api.deepseek.com'\n });\n const response = await client.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askOpenRouter(prompt: string, model: string, mode: string) {\n const client = new OpenAI({\n apiKey: process.env.OPENROUTER_API_KEY,\n baseURL: 'https://openrouter.ai/api/v1',\n defaultHeaders: {\n \"HTTP-Referer\": \"https://github.com/felipevetter/auto-i18n\",\n }\n });\n const response = await client.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askOllama(prompt: string, model: string, mode: string) {\n const response = await fetch('http://localhost:11434/api/generate', {\n method: 'POST',\n body: JSON.stringify({\n model,\n prompt,\n format: mode === 'json' ? 'json' : 'text',\n stream: false,\n }),\n });\n const data = await response.json() as { response: string };\n return data.response;\n}","import { SourceFile, SyntaxKind } from \"ts-morph\";\nimport { readConfig } from \"./config.js\";\n\nexport function ensureI18nImport(sourceFile: SourceFile) {\n const isClientFile = sourceFile.getStatementsWithComments()\n .some(s => s.getText().includes(\"'use client'\") || s.getText().includes('\"use client\"'));\n\n const i18nLibBase = readConfig(\"i18nLibrary\") || \"@scopeact/autoi18n\";\n let targetModule = i18nLibBase;\n let functionToInject = \"\";\n\n if (i18nLibBase === \"@scopeact/autoi18n\") {\n targetModule = isClientFile ? \"@scopeact/autoi18n/client\" : \"@scopeact/autoi18n/server\";\n functionToInject = isClientFile ? \"useI18n\" : \"getI18n\";\n } else {\n const legacyMap: any = { 'react-i18next': 'useTranslation', 'next-intl': 'useTranslations' };\n functionToInject = legacyMap[i18nLibBase];\n }\n\n let importDeclaration = sourceFile.getImportDeclaration(targetModule);\n\n if (!importDeclaration) {\n sourceFile.addImportDeclaration({\n moduleSpecifier: targetModule,\n namedImports: [functionToInject]\n });\n } else {\n if (!importDeclaration.getNamedImports().some(i => i.getName() === functionToInject)) {\n importDeclaration.addNamedImport(functionToInject);\n }\n }\n\n sourceFile.getFunctions().forEach(fn => {\n const isComponent = fn.getDescendantsOfKind(SyntaxKind.JsxElement).length > 0;\n if (!isComponent) return;\n\n if (isClientFile) {\n if (!fn.getText().includes(functionToInject)) {\n fn.insertStatements(0, `const { t } = ${functionToInject}();`);\n }\n } else {\n if (!fn.getText().includes(functionToInject)) {\n fn.setIsAsync(true);\n fn.insertStatements(0, `const { t } = await ${functionToInject}();`);\n }\n }\n })\n}","import { scanFilesAndRun } from \"./scanner.js\";\nimport { createConfig } from \"./config.js\";\n\nexport const run = () => {\n scanFilesAndRun();\n};\n\nexport const init = () => {\n createConfig();\n}"],"mappings":";AAAA,OAAOA,UAAS;AAChB,SAAe,SAAS,cAAAC,mBAAkB;;;ACD1C,OAAO,QAAQ;AACf,OAAO,UAAU;AACjB,OAAO,SAAS;AAEhB,IAAM,aAAa,KAAK,KAAK,QAAQ,IAAI,GAAG,uBAAuB;AAE5D,SAAS,WAAW,KAAa;AACpC,MAAI,CAAC,GAAG,WAAW,UAAU,GAAG;AAC5B,iBAAa;AAAA,EACjB;AACA,QAAM,SAAS,KAAK,MAAM,GAAG,aAAa,YAAY,OAAO,CAAC;AAC9D,SAAO,OAAO,GAAG;AACrB;AAEO,SAAS,eAAe;AAC3B,QAAM,UAAU,IAAI,2CAAqC,EAAE,MAAM;AACjE,QAAM,gBAAgB;AAAA,IAClB,SAAS;AAAA,IACT,YAAY;AAAA,IACZ,aAAa,CAAC,MAAM,IAAI;AAAA,IACxB,YAAY;AAAA,IACZ,aAAa;AAAA,IACb,UAAU;AAAA,IACV,YAAY;AAAA,IACZ,OAAO;AAAA,IACP,OAAO,CAAC,gBAAgB,qBAAqB;AAAA,EACjD;AAEA,KAAG,cAAc,YAAY,KAAK,UAAU,eAAe,MAAM,CAAC,CAAC;AACnE,UAAQ,QAAQ;AACpB;;;AC9BA,OAAOC,SAAQ;AACf,OAAOC,WAAU;AAOV,SAAS,uBAAuB,OAAyB,UAA2B;AACzF,SAAO;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBP,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA;AAAA,EAE9B,WAAW,wBAAwB,QAAQ,KAAK,EAAE;AAAA,EAClD,KAAK;AACP;AAEO,SAAS,wBACd,OACA,MACA,IACA,UACQ;AACR,SAAO;AAAA;AAAA;AAAA;AAAA,eAIM,IAAI,WAAW,EAAE;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAS9B,WAAW,wBAAwB,QAAQ,KAAK,EAAE;AAAA;AAAA;AAAA,EAGlD,KAAK,UAAU,OAAO,MAAM,CAAC,CAAC;AAAA,EAC9B,KAAK;AACP;AAEO,SAAS,kBAAkB,MAAsB;AACtD,SAAO,KAAK,QAAQ,gBAAgB,EAAE,EAAE,KAAK;AAC/C;AAEO,SAAS,QAAQ,SAAyB;AAC/C,MAAI,CAACD,IAAG,WAAW,OAAO,GAAG;AAC3B,IAAAA,IAAG,UAAUC,MAAK,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AACvD,IAAAD,IAAG,cAAc,SAAS,IAAI;AAC9B,WAAO,CAAC;AAAA,EACV;AAEA,SAAO,KAAK,MAAMA,IAAG,aAAa,SAAS,OAAO,CAAC;AACrD;AAEO,SAAS,WAAW,SAAiB,MAAoB;AAC9D,MAAG,CAACA,IAAG,WAAW,OAAO,GAAG;AAC1B,IAAAA,IAAG,UAAUC,MAAK,QAAQ,OAAO,GAAG,EAAE,WAAW,KAAK,CAAC;AAAA,EACzD;AACA,EAAAD,IAAG,cAAc,SAAS,KAAK,UAAU,MAAM,MAAM,CAAC,CAAC;AACzD;;;AC7EA,OAAO,YAAY;AAEnB,SAAS,cAAc;AACvB,SAAS,0BAA0B;AAFnC,OAAO,OAAO;AAOd,eAAsB,OAAO,QAAgB,OAAO,QAAyB;AACzE,QAAM,WAAW,WAAW,UAAU,KAAK;AAC3C,QAAM,QAAQ,WAAW,OAAO;AAChC,WAAS,QAAQ;AACnB,UAAQ,UAAU;AAAA,IAChB,KAAK;AACH,aAAO,UAAU,QAAQ,SAAS,UAAU,IAAI;AAAA,IAClD,KAAK;AACH,aAAO,UAAU,QAAQ,SAAS,oBAAoB,IAAI;AAAA,IAC5D,KAAK;AACH,aAAO,YAAY,QAAQ,SAAS,iBAAiB,IAAI;AAAA,IAC3D,KAAK;AACH,aAAO,cAAc,QAAQ,SAAS,+BAA+B,IAAI;AAAA,IAC3E,KAAK;AACH,aAAO,UAAU,QAAQ,SAAS,UAAU,IAAI;AAAA,IAClD;AACE,YAAM,IAAI,MAAM,gCAAgC,QAAQ,EAAE;AAAA,EAC9D;AACF;AAEO,SAAS,SAAS,UAAkB;AACvC,MAAI,CAAC,QAAQ,IAAI,GAAG,SAAS,YAAY,CAAC,UAAU,GAAG;AACnD,UAAM,IAAI,MAAM,GAAG,SAAS,YAAY,CAAC,8BAA2B;AAAA,EACxE;AACJ;AAEA,eAAe,UAAU,QAAgB,OAAe,MAAc;AACpE,QAAM,SAAS,IAAI,OAAO,EAAE,QAAQ,QAAQ,IAAI,eAAe,CAAC;AAChE,QAAM,WAAW,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,IACpD,iBAAiB,SAAS,SAAS,EAAE,MAAM,cAAc,IAAI,EAAE,MAAM,OAAO;AAAA,IAC5E,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,IAC5C;AAAA,EACF,CAAC;AACD,SAAO,UAAU,UAAU,CAAC,GAAG,SAAS,WAAW;AACrD;AAEA,eAAe,UAAU,QAAgB,OAAe,MAAc;AACpE,QAAM,QAAQ,IAAI,mBAAmB,QAAQ,IAAI,kBAAkB,EAAE;AACrE,QAAM,SAAS,MAAM,mBAAmB,EAAE,OAAO,kBAAkB,EAAE,kBAAkB,SAAS,SAAS,qBAAqB,aAAa,EAAE,CAAC;AAC9I,QAAM,SAAS,MAAM,OAAO,gBAAgB,MAAM;AAClD,SAAO,OAAO,SAAS,KAAK;AAC9B;AAEA,eAAe,YAAY,QAAgB,OAAe,MAAc;AACtE,QAAM,SAAS,IAAI,OAAO;AAAA,IACxB,QAAQ,QAAQ,IAAI;AAAA,IACpB,SAAS;AAAA,EACX,CAAC;AACD,QAAM,WAAW,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,IACpD,iBAAiB,SAAS,SAAS,EAAE,MAAM,cAAc,IAAI,EAAE,MAAM,OAAO;AAAA,IAC5E,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,IAC5C;AAAA,EACF,CAAC;AACD,SAAO,UAAU,UAAU,CAAC,GAAG,SAAS,WAAW;AACrD;AAEA,eAAe,cAAc,QAAgB,OAAe,MAAc;AACxE,QAAM,SAAS,IAAI,OAAO;AAAA,IACxB,QAAQ,QAAQ,IAAI;AAAA,IACpB,SAAS;AAAA,IACT,gBAAgB;AAAA,MACd,gBAAgB;AAAA,IAClB;AAAA,EACF,CAAC;AACD,QAAM,WAAW,MAAM,OAAO,KAAK,YAAY,OAAO;AAAA,IACpD,iBAAiB,SAAS,SAAS,EAAE,MAAM,cAAc,IAAI,EAAE,MAAM,OAAO;AAAA,IAC5E,UAAU,CAAC,EAAE,MAAM,QAAQ,SAAS,OAAO,CAAC;AAAA,IAC5C;AAAA,EACF,CAAC;AACD,SAAO,UAAU,UAAU,CAAC,GAAG,SAAS,WAAW;AACrD;AAEA,eAAe,UAAU,QAAgB,OAAe,MAAc;AACpE,QAAM,WAAW,MAAM,MAAM,uCAAuC;AAAA,IAClE,QAAQ;AAAA,IACR,MAAM,KAAK,UAAU;AAAA,MACnB;AAAA,MACA;AAAA,MACA,QAAQ,SAAS,SAAS,SAAS;AAAA,MACnC,QAAQ;AAAA,IACV,CAAC;AAAA,EACH,CAAC;AACD,QAAM,OAAO,MAAM,SAAS,KAAK;AACjC,SAAO,KAAK;AACd;;;AHvFA,OAAO,WAAW;;;AILlB,SAAqB,kBAAkB;AAGhC,SAAS,iBAAiB,YAAwB;AACrD,QAAM,eAAe,WAAW,0BAA0B,EACrD,KAAK,OAAK,EAAE,QAAQ,EAAE,SAAS,cAAc,KAAK,EAAE,QAAQ,EAAE,SAAS,cAAc,CAAC;AAE3F,QAAM,cAAc,WAAW,aAAa,KAAK;AACjD,MAAI,eAAe;AACnB,MAAI,mBAAmB;AAEvB,MAAI,gBAAgB,sBAAsB;AACtC,mBAAe,eAAe,8BAA8B;AAC5D,uBAAmB,eAAe,YAAY;AAAA,EAClD,OAAO;AACH,UAAM,YAAiB,EAAE,iBAAiB,kBAAkB,aAAa,kBAAkB;AAC3F,uBAAmB,UAAU,WAAW;AAAA,EAC5C;AAEA,MAAI,oBAAoB,WAAW,qBAAqB,YAAY;AAEpE,MAAI,CAAC,mBAAmB;AACpB,eAAW,qBAAqB;AAAA,MAC5B,iBAAiB;AAAA,MACjB,cAAc,CAAC,gBAAgB;AAAA,IACnC,CAAC;AAAA,EACL,OAAO;AACH,QAAI,CAAC,kBAAkB,gBAAgB,EAAE,KAAK,OAAK,EAAE,QAAQ,MAAM,gBAAgB,GAAG;AAClF,wBAAkB,eAAe,gBAAgB;AAAA,IACrD;AAAA,EACJ;AAEA,aAAW,aAAa,EAAE,QAAQ,QAAM;AACpC,UAAM,cAAc,GAAG,qBAAqB,WAAW,UAAU,EAAE,SAAS;AAC5E,QAAI,CAAC,YAAa;AAElB,QAAI,cAAc;AACd,UAAI,CAAC,GAAG,QAAQ,EAAE,SAAS,gBAAgB,GAAG;AAC1C,WAAG,iBAAiB,GAAG,iBAAiB,gBAAgB,KAAK;AAAA,MACjE;AAAA,IACJ,OAAO;AACH,UAAI,CAAC,GAAG,QAAQ,EAAE,SAAS,gBAAgB,GAAG;AAC1C,WAAG,WAAW,IAAI;AAClB,WAAG,iBAAiB,GAAG,uBAAuB,gBAAgB,KAAK;AAAA,MACvE;AAAA,IACJ;AAAA,EACJ,CAAC;AACL;;;AJnCA,eAAsB,kBAAkB;AACpC,QAAM,aAAa,WAAW,YAAY,KAAK;AAC/C,QAAM,cAAc,WAAW,aAAa,KAAK,CAAC,IAAI;AACtD,QAAM,aAAa,WAAW,YAAY,KAAK;AAE/C,QAAM,YAA4C,CAAC;AACnD,QAAM,gBAAgB,oBAAI,IAAY;AACtC,QAAM,wBAAgD,CAAC;AACvD,QAAM,qBAA6C,CAAC;AAEpD,QAAM,UAAU,IAAI,QAAQ;AAAA,IACxB,6BAA6B;AAAA,EACjC,CAAC;AAED,QAAM,UAAUE,KAAI,+CAAwC,EAAE,MAAM;AACpE,UAAQ,sBAAsB,WAAW,OAAO,CAAC;AAEjD,QAAM,WAAW,QAAQ,eAAe;AAExC,aAAW,WAAW,UAAU;AAC5B,UAAM,eAAe,WAAW,YAAY,KAAK;AACjD,QAAG,cAAc;AACb,uBAAiB,OAAO;AAAA,IAC5B;AACA,YAAQ,kBAAkB,CAAC,SAAS;AAChC,UAAI,KAAK,QAAQ,MAAMC,YAAW,SAAS;AACvC,cAAM,gBAAgB,KAAK,QAAQ;AACnC,YAAI,cAAc,KAAK,EAAE,SAAS,GAAG;AACjC,oBAAU,KAAK,EAAE,MAAM,MAAM,cAAc,KAAK,EAAE,CAAC;AACnD,wBAAc,IAAI,cAAc,KAAK,CAAC;AAAA,QAC1C;AAAA,MACJ;AAAA,IACJ,CAAC;AAAA,EACL;AAGA,QAAM,YAAY,SAAS;AAC3B,QAAM,WAAW,cAAc;AAC/B,QAAM,SAAS,cAAc,IAAI,KAAK;AACtC,QAAM,OAAO,aAAa,IAAI,KAAK;AACnC,UAAQ,QAAQ,iBAAiB,MAAM,KAAK,QAAQ,CAAC,SAAS,IAAI,YAAS,IAAI,OAAO,MAAM,KAAK,SAAS,CAAC,WAAW,MAAM,GAAG;AAC/H,QAAM,aAAaD,KAAI,8CAAuC,QAAQ,YAAY,EAAE,MAAM;AAC1F,QAAM,cAAc,MAAM,KAAK,aAAa,EAAE,IAAI,CAAC,MAAM,WAAW;AAAA,IAChE,IAAI;AAAA,IACJ;AAAA,EACJ,EAAE;AAEF,QAAM,aAAa,KAAK,MAAM,kBAAkB,MAAM,OAAO,uBAAuB,WAAW,GAAG,MAAM,CAAC,CAAC;AAE1G,aAAW,QAAQ,CAAC,SAAc;AAC9B,UAAM,eAAe,YAAY,KAAK,OAAK,EAAE,OAAO,KAAK,EAAE,GAAG;AAC9D,QAAI,aAAc,oBAAmB,YAAY,IAAI,KAAK;AAAA,EAC9D,CAAC;AAED,YAAU,QAAQ,CAAC,EAAE,MAAM,KAAK,MAAM;AAClC,UAAM,MAAM,mBAAmB,IAAI;AACnC,QAAI,KAAK;AACL,WAAK,gBAAgB,OAAO,GAAG,KAAK;AAAA,IACxC;AAAA,EACJ,CAAC;AACD,UAAQ,SAAS;AACjB,aAAW,QAAQ,MAAM,MAAM,gDAA6C,CAAC;AAE7E,QAAM,cAAcA,KAAI,iDAAoC,EAAE,MAAM;AAEpE,aAAW,CAAC,eAAe,WAAW,KAAK,OAAO,QAAQ,kBAAkB,GAAG;AAC3E,0BAAsB,WAAW,IAAI;AAAA,EACzC;AAEA,QAAM,SAAS,GAAG,UAAU,IAAI,UAAU;AAC1C,QAAM,cAAc,QAAQ,MAAM;AAClC,QAAM,cAAc,EAAE,GAAG,aAAa,GAAG,sBAAsB;AAE/D,aAAW,QAAQ,WAAW;AAG9B,aAAW,UAAU,aAAa;AAC9B,UAAM,aAAa,GAAG,UAAU,IAAI,MAAM;AAC1C,UAAM,kBAAkB,QAAQ,UAAU;AAE1C,UAAM,oBAA4C,CAAC;AAEnD,eAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,qBAAqB,GAAG;AAC9D,UAAI,CAAC,gBAAgB,GAAG,GAAG;AACvB,0BAAkB,GAAG,IAAI;AAAA,MAC7B;AAAA,IACJ;AAEA,QAAI,OAAO,KAAK,iBAAiB,EAAE,SAAS,GAAG;AAC3C,YAAM,SAAS,wBAAwB,mBAAmB,YAAY,MAAM;AAE5E,YAAME,cAAa,kBAAkB,MAAM,OAAO,QAAQ,MAAM,CAAC;AACjE,YAAM,cAAc,KAAK,MAAMA,WAAU;AAEzC,YAAM,kBAAkB,EAAE,GAAG,iBAAiB,GAAG,YAAY;AAC7D,iBAAW,YAAY,eAAe;AAAA,IAC1C;AAAA,EACJ;AAEA,cAAY,QAAQ,0DAAiD;AACzE;;;AK7GO,IAAM,MAAM,MAAM;AACrB,kBAAgB;AACpB;AAEO,IAAM,OAAO,MAAM;AACtB,eAAa;AACjB;","names":["ora","SyntaxKind","fs","path","ora","SyntaxKind","respostaIA"]}
@@ -21,11 +21,11 @@ function createConfig() {
21
21
  sourceLang: "pt",
22
22
  targetLangs: ["en", "es"],
23
23
  autoInject: false,
24
- i18nLibrary: "react-i18next",
24
+ i18nLibrary: "@scopeact/autoi18n",
25
25
  provider: "openai",
26
26
  localesDir: "./locales",
27
27
  model: "gpt-3.5-turbo",
28
- files: ["src/**/*.tsx", "app/**/*.tsx"]
28
+ files: ["app/**/*.tsx", "components/**/*.tsx"]
29
29
  };
30
30
  _fs2.default.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));
31
31
  spinner.succeed();
@@ -186,18 +186,42 @@ var _chalk = require('chalk'); var _chalk2 = _interopRequireDefault(_chalk);
186
186
  // src/core/dependecies.ts
187
187
 
188
188
  function ensureI18nImport(sourceFile) {
189
- const importDeclaration = sourceFile.getImportDeclaration("react-i18next");
189
+ const isClientFile = sourceFile.getStatementsWithComments().some((s) => s.getText().includes("'use client'") || s.getText().includes('"use client"'));
190
+ const i18nLibBase = readConfig("i18nLibrary") || "@scopeact/autoi18n";
191
+ let targetModule = i18nLibBase;
192
+ let functionToInject = "";
193
+ if (i18nLibBase === "@scopeact/autoi18n") {
194
+ targetModule = isClientFile ? "@scopeact/autoi18n/client" : "@scopeact/autoi18n/server";
195
+ functionToInject = isClientFile ? "useI18n" : "getI18n";
196
+ } else {
197
+ const legacyMap = { "react-i18next": "useTranslation", "next-intl": "useTranslations" };
198
+ functionToInject = legacyMap[i18nLibBase];
199
+ }
200
+ let importDeclaration = sourceFile.getImportDeclaration(targetModule);
190
201
  if (!importDeclaration) {
191
202
  sourceFile.addImportDeclaration({
192
- moduleSpecifier: "react-i18next",
193
- namedImports: ["useTranslation"]
203
+ moduleSpecifier: targetModule,
204
+ namedImports: [functionToInject]
194
205
  });
195
206
  } else {
196
- const namedImports = importDeclaration.getNamedImports().map((i) => i.getName());
197
- if (!namedImports.includes("useTranslation")) {
198
- importDeclaration.addNamedImport("useTranslation");
207
+ if (!importDeclaration.getNamedImports().some((i) => i.getName() === functionToInject)) {
208
+ importDeclaration.addNamedImport(functionToInject);
199
209
  }
200
210
  }
211
+ sourceFile.getFunctions().forEach((fn) => {
212
+ const isComponent = fn.getDescendantsOfKind(_tsmorph.SyntaxKind.JsxElement).length > 0;
213
+ if (!isComponent) return;
214
+ if (isClientFile) {
215
+ if (!fn.getText().includes(functionToInject)) {
216
+ fn.insertStatements(0, `const { t } = ${functionToInject}();`);
217
+ }
218
+ } else {
219
+ if (!fn.getText().includes(functionToInject)) {
220
+ fn.setIsAsync(true);
221
+ fn.insertStatements(0, `const { t } = await ${functionToInject}();`);
222
+ }
223
+ }
224
+ });
201
225
  }
202
226
 
203
227
  // src/core/scanner.ts
@@ -293,4 +317,4 @@ var init = () => {
293
317
 
294
318
 
295
319
  exports.run = run; exports.init = init;
296
- //# sourceMappingURL=chunk-TWTG4RTI.cjs.map
320
+ //# sourceMappingURL=chunk-VIPNHGIN.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/chunk-VIPNHGIN.cjs","../src/core/scanner.ts","../src/core/config.ts","../src/core/utils.ts","../src/core/ai.ts","../src/core/dependecies.ts","../src/core/index.ts"],"names":["respostaIA"],"mappings":"AAAA;ACAA,oEAAgB;AAChB,mCAA0C;ADE1C;AACA;AEJA,gEAAe;AACf,wEAAiB;AACjB;AAEA,IAAM,WAAA,EAAa,cAAA,CAAK,IAAA,CAAK,OAAA,CAAQ,GAAA,CAAI,CAAA,EAAG,uBAAuB,CAAA;AAE5D,SAAS,UAAA,CAAW,GAAA,EAAa;AACpC,EAAA,GAAA,CAAI,CAAC,YAAA,CAAG,UAAA,CAAW,UAAU,CAAA,EAAG;AAC5B,IAAA,YAAA,CAAa,CAAA;AAAA,EACjB;AACA,EAAA,MAAM,OAAA,EAAS,IAAA,CAAK,KAAA,CAAM,YAAA,CAAG,YAAA,CAAa,UAAA,EAAY,OAAO,CAAC,CAAA;AAC9D,EAAA,OAAO,MAAA,CAAO,GAAG,CAAA;AACrB;AAEO,SAAS,YAAA,CAAA,EAAe;AAC3B,EAAA,MAAM,QAAA,EAAU,2BAAA,2CAAyC,CAAA,CAAE,KAAA,CAAM,CAAA;AACjE,EAAA,MAAM,cAAA,EAAgB;AAAA,IAClB,OAAA,EAAS,CAAA,uDAAA,CAAA;AAAA,IACT,UAAA,EAAY,IAAA;AAAA,IACZ,WAAA,EAAa,CAAC,IAAA,EAAM,IAAI,CAAA;AAAA,IACxB,UAAA,EAAY,KAAA;AAAA,IACZ,WAAA,EAAa,oBAAA;AAAA,IACb,QAAA,EAAU,QAAA;AAAA,IACV,UAAA,EAAY,WAAA;AAAA,IACZ,KAAA,EAAO,eAAA;AAAA,IACP,KAAA,EAAO,CAAC,cAAA,EAAgB,qBAAqB;AAAA,EACjD,CAAA;AAEA,EAAA,YAAA,CAAG,aAAA,CAAc,UAAA,EAAY,IAAA,CAAK,SAAA,CAAU,aAAA,EAAe,IAAA,EAAM,CAAC,CAAC,CAAA;AACnE,EAAA,OAAA,CAAQ,OAAA,CAAQ,CAAA;AACpB;AFEA;AACA;AGjCA;AACA;AAOO,SAAS,sBAAA,CAAuB,KAAA,EAAyB,QAAA,EAA2B;AACzF,EAAA,OAAO,CAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAiBP,IAAA,CAAK,SAAA,CAAU,KAAA,EAAO,IAAA,EAAM,CAAC,CAAC,CAAA;AAAA;AAAA,EAE9B,SAAA,EAAW,CAAA,qBAAA,EAAwB,QAAQ,CAAA,EAAA;AACtC;AACP;AAKE;AAGO,EAAA;AAAA;AAAA;AAAA;AAIuB,aAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AASa;AAAO;AAAA;AAGpB;AACzB;AACP;AAEwD;AACd,EAAA;AAC1C;AAEiD;AAClB,EAAA;AACW,IAAA;AACR,IAAA;AACtB,IAAA;AACV,EAAA;AAE2C,EAAA;AAC7C;AAEgE;AAClC,EAAA;AACY,IAAA;AACxC,EAAA;AACyC,EAAA;AAC3C;AHmB8C;AACA;AIjG3B;AAEI;AACY;AAFrB;AAO+D;AAC9B,EAAA;AACX,EAAA;AACf,EAAA;AACD,EAAA;AACX,IAAA;AAC+B,MAAA;AAC/B,IAAA;AAC+B,MAAA;AAC/B,IAAA;AACiC,MAAA;AACjC,IAAA;AACmC,MAAA;AACnC,IAAA;AAC+B,MAAA;AACpC,IAAA;AACkB,MAAA;AACpB,EAAA;AACF;AAE2C;AACG,EAAA;AACG,IAAA;AAC7C,EAAA;AACJ;AAEsE;AACxB,EAAA;AACT,EAAA;AACI,IAAA;AACD,IAAA;AACpC,IAAA;AACD,EAAA;AACuC,EAAA;AAC1C;AAEsE;AAC/B,EAAA;AACK,EAAA;AACE,EAAA;AAChB,EAAA;AAC9B;AAE0D;AAC9B,EAAA;AACJ,IAAA;AACX,IAAA;AACV,EAAA;AACkC,EAAA;AACI,IAAA;AACD,IAAA;AACpC,IAAA;AACD,EAAA;AACuC,EAAA;AAC1C;AAE4D;AAChC,EAAA;AACJ,IAAA;AACX,IAAA;AACO,IAAA;AACE,MAAA;AAClB,IAAA;AACD,EAAA;AACkC,EAAA;AACI,IAAA;AACD,IAAA;AACpC,IAAA;AACD,EAAA;AACuC,EAAA;AAC1C;AAEsE;AACvC,EAAA;AACnB,IAAA;AACa,IAAA;AACnB,MAAA;AACA,MAAA;AACmC,MAAA;AAC3B,MAAA;AACT,IAAA;AACF,EAAA;AACgC,EAAA;AACrB,EAAA;AACd;AJyF8C;AACA;ACjL5B;ADmL4B;AACA;AKzLP;AAGkB;AACrB,EAAA;AAGY,EAAA;AACzB,EAAA;AACI,EAAA;AAEmB,EAAA;AACR,IAAA;AACI,IAAA;AAC/B,EAAA;AACuC,IAAA;AACF,IAAA;AAC5C,EAAA;AAEmC,EAAA;AAEX,EAAA;AACY,IAAA;AACX,MAAA;AACc,MAAA;AAClC,IAAA;AACE,EAAA;AACsC,IAAA;AACJ,MAAA;AACrC,IAAA;AACJ,EAAA;AAEwC,EAAA;AACb,IAAA;AACL,IAAA;AAEA,IAAA;AACa,MAAA;AACA,QAAA;AAC3B,MAAA;AACG,IAAA;AACwB,MAAA;AACL,QAAA;AACK,QAAA;AAC3B,MAAA;AACJ,IAAA;AACH,EAAA;AACL;ALkL8C;AACA;ACtNN;AACM,EAAA;AACE,EAAA;AACF,EAAA;AAES,EAAA;AACzB,EAAA;AAC6B,EAAA;AACH,EAAA;AAExB,EAAA;AACK,IAAA;AAChC,EAAA;AAEmB,EAAA;AACqB,EAAA;AAED,EAAA;AAER,EAAA;AACI,IAAA;AACf,IAAA;AACW,MAAA;AAC5B,IAAA;AACoC,IAAA;AACE,MAAA;AACK,QAAA;AACE,QAAA;AACJ,UAAA;AACG,UAAA;AACpC,QAAA;AACJ,MAAA;AACH,IAAA;AACL,EAAA;AAG2B,EAAA;AACI,EAAA;AACO,EAAA;AACH,EAAA;AACS,EAAA;AACrB,EAAA;AACqB,EAAA;AACpC,IAAA;AACJ,IAAA;AACF,EAAA;AAE4B,EAAA;AAEI,EAAA;AACQ,IAAA;AACD,IAAA;AACxC,EAAA;AAEqC,EAAA;AACC,IAAA;AAC1B,IAAA;AAC+B,MAAA;AACxC,IAAA;AACH,EAAA;AACgB,EAAA;AACc,EAAA;AAEP,EAAA;AAEmB,EAAA;AACF,IAAA;AACzC,EAAA;AAE0C,EAAA;AACR,EAAA;AACO,EAAA;AAEX,EAAA;AAGI,EAAA;AACY,IAAA;AACA,IAAA;AAES,IAAA;AAET,IAAA;AACX,MAAA;AACE,QAAA;AAC7B,MAAA;AACJ,IAAA;AAEmC,IAAA;AACQ,MAAA;AAEF,MAAA;AACNA,MAAAA;AAEF,MAAA;AACS,MAAA;AAC1C,IAAA;AACJ,EAAA;AAEoB,EAAA;AACxB;ADkM8C;AACA;AMhTrB;AACL,EAAA;AACpB;AAE0B;AACT,EAAA;AACjB;ANiT8C;AACA;AACA;AACA;AACA","file":"/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/chunk-VIPNHGIN.cjs","sourcesContent":[null,"import ora from \"ora\";\nimport { Node, Project, SyntaxKind } from \"ts-morph\";\nimport { readConfig } from \"./config.js\";\nimport { cleanJsonResponse, createExtractionPrompt, createTranslationPrompt, lerJson, salvarJson, type ExtractionItem } from \"./utils.js\";\nimport { askLLM } from \"./ai.js\";\nimport chalk from \"chalk\";\nimport { ensureI18nImport } from \"./dependecies.js\";\n\ninterface JsonTranslation {\n [key: string]: string;\n}\n\nexport async function scanFilesAndRun() {\n const sourceLang = readConfig('sourceLang') || 'pt';\n const targetLangs = readConfig('targetLangs') || ['en'];\n const localesDir = readConfig('localesDir') || './locales';\n\n const pendentes: { node: Node, text: string }[] = [];\n const stringsUnicas = new Set<string>();\n const novasChavesParaSalvar: Record<string, string> = {};\n const mapaTextoParaChave: Record<string, string> = {};\n\n const project = new Project({\n skipAddingFilesFromTsConfig: true,\n });\n\n const spinner = ora(\"📂 Carregando e analisando arquivos...\").start();\n project.addSourceFilesAtPaths(readConfig(\"files\"));\n\n const arquivos = project.getSourceFiles();\n\n for (const arquivo of arquivos) {\n const shouldInject = readConfig(\"autoInject\") || false;\n if(shouldInject) {\n ensureI18nImport(arquivo);\n }\n arquivo.forEachDescendant((node) => {\n if (node.getKind() === SyntaxKind.JsxText) {\n const textoOriginal = node.getText();\n if (textoOriginal.trim().length > 0) {\n pendentes.push({ node, text: textoOriginal.trim() });\n stringsUnicas.add(textoOriginal.trim());\n }\n }\n });\n }\n\n //pluralização elegante\n const fileCount = arquivos.length;\n const strCount = stringsUnicas.size;\n const sFiles = fileCount === 1 ? '' : 's';\n const sStr = strCount === 1 ? '' : 's';\n spinner.succeed(`Encontrado(s) ${chalk.cyan(strCount)} texto${sStr} único${sStr} em ${chalk.cyan(fileCount)} arquivo${sFiles}.`);\n const spinnerLLM = ora(`🧠 Gerando chaves inteligentes para ${strCount} textos...`).start();\n const listaParaIA = Array.from(stringsUnicas).map((text, index) => ({\n id: index,\n text\n })) as ExtractionItem[];\n\n const respostaIA = JSON.parse(cleanJsonResponse(await askLLM(createExtractionPrompt(listaParaIA), 'json')));\n\n respostaIA.forEach((item: any) => {\n const originalText = listaParaIA.find(l => l.id === item.id)?.text;\n if (originalText) mapaTextoParaChave[originalText] = item.key;\n });\n\n pendentes.forEach(({ node, text }) => {\n const key = mapaTextoParaChave[text];\n if (key) {\n node.replaceWithText(`{t('${key}')}`);\n }\n });\n project.saveSync();\n spinnerLLM.succeed(chalk.green(\"Código atualizado com as novas chaves i18n!\"));\n\n const spinnerSave = ora(\"💾 Gerando arquivos de tradução...\").start();\n\n for (const [textoOriginal, chaveGerada] of Object.entries(mapaTextoParaChave)) {\n novasChavesParaSalvar[chaveGerada] = textoOriginal;\n }\n\n const pathPt = `${localesDir}/${sourceLang}.json`;\n const jsonPtAtual = lerJson(pathPt);\n const jsonPtFinal = { ...jsonPtAtual, ...novasChavesParaSalvar };\n\n salvarJson(pathPt, jsonPtFinal);\n\n // Salvar em outros idiomas\n for (const idioma of targetLangs) {\n const pathIdioma = `${localesDir}/${idioma}.json`;\n const jsonIdiomaAtual = lerJson(pathIdioma) as JsonTranslation;\n\n const deltaParaTraduzir: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(novasChavesParaSalvar)) {\n if (!jsonIdiomaAtual[key]) {\n deltaParaTraduzir[key] = value;\n }\n }\n\n if (Object.keys(deltaParaTraduzir).length > 0) {\n const prompt = createTranslationPrompt(deltaParaTraduzir, sourceLang, idioma);\n\n const respostaIA = cleanJsonResponse(await askLLM(prompt, 'json'));\n const traducoesEn = JSON.parse(respostaIA);\n\n const jsonIdiomaFinal = { ...jsonIdiomaAtual, ...traducoesEn };\n salvarJson(pathIdioma, jsonIdiomaFinal);\n }\n }\n\n spinnerSave.succeed('Tradução concluída e chaves salvas com sucesso!');\n}","import fs from \"fs\";\nimport path from \"path\";\nimport ora from \"ora\";\n\nconst configPath = path.join(process.cwd(), \"auto-i18n.config.json\");\n\nexport function readConfig(key: string) {\n if (!fs.existsSync(configPath)) {\n createConfig();\n }\n const config = JSON.parse(fs.readFileSync(configPath, \"utf-8\"));\n return config[key];\n}\n\nexport function createConfig() {\n const spinner = ora('Criando arquivos de configuração...').start();\n const defaultConfig = {\n $schema: `https://unpkg.com/@scopeact/autoi18n@latest/schema.json`,\n sourceLang: \"pt\",\n targetLangs: [\"en\", \"es\"],\n autoInject: false,\n i18nLibrary: \"@scopeact/autoi18n\",\n provider: \"openai\",\n localesDir: \"./locales\",\n model: \"gpt-3.5-turbo\",\n files: [\"app/**/*.tsx\", \"components/**/*.tsx\"]\n };\n\n fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));\n spinner.succeed();\n}","import fs from 'fs';\nimport path from 'path';\n\nexport interface ExtractionItem {\n id: number;\n text: string;\n}\n\nexport function createExtractionPrompt(itens: ExtractionItem[], contexto?: string): string {\n return `\nVocê é um especialista em i18n. Recebi uma lista de textos de uma interface React.\nSua tarefa é gerar chaves (keys) semânticas e curtas em inglês (snake_case) para cada item.\n\nREGRAS:\n1. Retorne APENAS um array de objetos JSON.\n2. Cada objeto deve ter: \"id\" (o mesmo enviado) e \"key\" (a chave sugerida).\n3. As chaves devem ser curtas (max 4 palavras). \n4. Textos longos devem ter chaves que resumam o sentido.\n\nEXEMPLO DE ENTRADA:\n[ { \"id\": 1, \"text\": \"Salvar alterações\" } ]\n\nEXEMPLO DE SAÍDA:\n[ { \"id\": 1, \"key\": \"button_save_changes\" } ]\n\nITENS PARA PROCESSAR:\n${JSON.stringify(itens, null, 2)}\n\n${contexto ? `Contexto do projeto: ${contexto}` : ''}\n`.trim();\n}\n\nexport function createTranslationPrompt(\n delta: object, \n from: string, \n to: string, \n contexto?: string\n): string {\n return `\nVocê é um tradutor sênior de software especializado em localização (i18n).\nSua tarefa é traduzir os VALORES das novas chaves de interface que foram adicionadas ao projeto.\n\nIDIOMAS: De \"${from}\" para \"${to}\".\n\nREGRAS CRÍTICAS:\n1. MANUTENÇÃO DE CHAVES: Mantenha as chaves (keys) EXATAMENTE como estão. Não as traduza nem as altere.\n2. TRADUÇÃO DE VALORES: Traduza apenas os valores para o idioma de destino.\n3. VARIÁVEIS E PLACEHOLDERS: Preserve placeholders como {{name}}, {0}, %s ou qualquer texto entre chaves. Eles NÃO devem ser traduzidos.\n4. TOM DE VOZ: Use um tom profissional e conciso, adequado para interfaces de usuário (botões, labels, mensagens de erro).\n5. FORMATO: Retorne APENAS o objeto JSON puro, sem explicações ou blocos de código markdown.\n\n${contexto ? `CONTEXTO DO PROJETO: ${contexto}` : ''}\n\nDADOS PARA TRADUZIR:\n${JSON.stringify(delta, null, 2)}\n`.trim();\n}\n\nexport function cleanJsonResponse(text: string): string {\n return text.replace(/```json|```/g, '').trim();\n}\n\nexport function lerJson(caminho: string): object {\n if (!fs.existsSync(caminho)) {\n fs.mkdirSync(path.dirname(caminho), { recursive: true });\n fs.writeFileSync(caminho, '{}');\n return {};\n }\n \n return JSON.parse(fs.readFileSync(caminho, 'utf-8'));\n}\n\nexport function salvarJson(caminho: string, json: object): void {\n if(!fs.existsSync(caminho)) {\n fs.mkdirSync(path.dirname(caminho), { recursive: true });\n }\n fs.writeFileSync(caminho, JSON.stringify(json, null, 2));\n}","import dotenv from 'dotenv';\ndotenv.config();\nimport { OpenAI } from 'openai';\nimport { GoogleGenerativeAI } from '@google/generative-ai';\nimport { readConfig } from \"./config.js\";\n\nexport type AIProvider = 'openai' | 'google' | 'deepseek' | 'openrouter' | 'ollama';\n\nexport async function askLLM(prompt: string, mode = 'text'): Promise<string> {\n const provider = readConfig('provider') || 'openai' as AIProvider;\n const model = readConfig('model');\n checkEnv(provider);\n switch (provider) {\n case 'openai':\n return askOpenAI(prompt, model || 'gpt-4o', mode);\n case 'google':\n return askGemini(prompt, model || 'gemini-2.5-flash', mode);\n case 'deepseek':\n return askDeepSeek(prompt, model || 'deepseek-chat', mode);\n case 'openrouter':\n return askOpenRouter(prompt, model || 'google/gemini-2.0-flash-001', mode);\n case 'ollama':\n return askOllama(prompt, model || 'llama3', mode);\n default:\n throw new Error(`Provedor de IA desconhecido: ${provider}`);\n }\n}\n\nexport function checkEnv(provider: string) {\n if (!process.env[`${provider.toUpperCase()}_API_KEY`]) {\n throw new Error(`${provider.toUpperCase()}_API_KEY não foi definido`);\n }\n}\n\nasync function askOpenAI(prompt: string, model: string, mode: string) {\n const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n const response = await openai.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askGemini(prompt: string, model: string, mode: string) {\n const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || '');\n const gemini = genAI.getGenerativeModel({ model, generationConfig: { responseMimeType: mode === 'json' ? 'application/json' : 'text/plain' } });\n const result = await gemini.generateContent(prompt);\n return result.response.text();\n}\n\nasync function askDeepSeek(prompt: string, model: string, mode: string) {\n const client = new OpenAI({\n apiKey: process.env.DEEPSEEK_API_KEY,\n baseURL: 'https://api.deepseek.com'\n });\n const response = await client.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askOpenRouter(prompt: string, model: string, mode: string) {\n const client = new OpenAI({\n apiKey: process.env.OPENROUTER_API_KEY,\n baseURL: 'https://openrouter.ai/api/v1',\n defaultHeaders: {\n \"HTTP-Referer\": \"https://github.com/felipevetter/auto-i18n\",\n }\n });\n const response = await client.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askOllama(prompt: string, model: string, mode: string) {\n const response = await fetch('http://localhost:11434/api/generate', {\n method: 'POST',\n body: JSON.stringify({\n model,\n prompt,\n format: mode === 'json' ? 'json' : 'text',\n stream: false,\n }),\n });\n const data = await response.json() as { response: string };\n return data.response;\n}","import { SourceFile, SyntaxKind } from \"ts-morph\";\nimport { readConfig } from \"./config.js\";\n\nexport function ensureI18nImport(sourceFile: SourceFile) {\n const isClientFile = sourceFile.getStatementsWithComments()\n .some(s => s.getText().includes(\"'use client'\") || s.getText().includes('\"use client\"'));\n\n const i18nLibBase = readConfig(\"i18nLibrary\") || \"@scopeact/autoi18n\";\n let targetModule = i18nLibBase;\n let functionToInject = \"\";\n\n if (i18nLibBase === \"@scopeact/autoi18n\") {\n targetModule = isClientFile ? \"@scopeact/autoi18n/client\" : \"@scopeact/autoi18n/server\";\n functionToInject = isClientFile ? \"useI18n\" : \"getI18n\";\n } else {\n const legacyMap: any = { 'react-i18next': 'useTranslation', 'next-intl': 'useTranslations' };\n functionToInject = legacyMap[i18nLibBase];\n }\n\n let importDeclaration = sourceFile.getImportDeclaration(targetModule);\n\n if (!importDeclaration) {\n sourceFile.addImportDeclaration({\n moduleSpecifier: targetModule,\n namedImports: [functionToInject]\n });\n } else {\n if (!importDeclaration.getNamedImports().some(i => i.getName() === functionToInject)) {\n importDeclaration.addNamedImport(functionToInject);\n }\n }\n\n sourceFile.getFunctions().forEach(fn => {\n const isComponent = fn.getDescendantsOfKind(SyntaxKind.JsxElement).length > 0;\n if (!isComponent) return;\n\n if (isClientFile) {\n if (!fn.getText().includes(functionToInject)) {\n fn.insertStatements(0, `const { t } = ${functionToInject}();`);\n }\n } else {\n if (!fn.getText().includes(functionToInject)) {\n fn.setIsAsync(true);\n fn.insertStatements(0, `const { t } = await ${functionToInject}();`);\n }\n }\n })\n}","import { scanFilesAndRun } from \"./scanner.js\";\nimport { createConfig } from \"./config.js\";\n\nexport const run = () => {\n scanFilesAndRun();\n};\n\nexport const init = () => {\n createConfig();\n}"]}
package/dist/cli.cjs CHANGED
@@ -2,17 +2,17 @@
2
2
  "use strict";
3
3
 
4
4
 
5
- var _chunkTWTG4RTIcjs = require('./chunk-TWTG4RTI.cjs');
5
+ var _chunkVIPNHGINcjs = require('./chunk-VIPNHGIN.cjs');
6
6
 
7
7
  // src/cli/index.ts
8
8
  var _commander = require('commander');
9
9
  var program = new (0, _commander.Command)();
10
10
  program.name("@scopeact/autoi18n").description("CLI para gera\xE7\xE3o de arquivos e tradu\xE7\xE3o autom\xE1tica de arquivos tsx");
11
11
  program.command("run").action(() => {
12
- _chunkTWTG4RTIcjs.run.call(void 0, );
12
+ _chunkVIPNHGINcjs.run.call(void 0, );
13
13
  });
14
14
  program.command("init").action(() => {
15
- _chunkTWTG4RTIcjs.init.call(void 0, );
15
+ _chunkVIPNHGINcjs.init.call(void 0, );
16
16
  });
17
- program.parse();
17
+ program.parse(process.argv);
18
18
  //# sourceMappingURL=cli.cjs.map
package/dist/cli.cjs.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/cli.cjs","../src/cli/index.ts"],"names":[],"mappings":"AAAA;AACA;AACE;AACA;AACF,wDAA6B;AAC7B;AACA;ACHA,sCAAwB;AACxB,IAAM,QAAA,EAAU,IAAI,uBAAA,CAAQ,CAAA;AAE5B,OAAA,CACG,IAAA,CAAK,oBAAoB,CAAA,CACzB,WAAA,CAAY,mFAAoE,CAAA;AAEnF,OAAA,CACG,OAAA,CAAQ,KAAK,CAAA,CACb,MAAA,CAAO,CAAA,EAAA,GAAM;AACZ,EAAA,mCAAA,CAAI;AACN,CAAC,CAAA;AAEH,OAAA,CACG,OAAA,CAAQ,MAAM,CAAA,CACd,MAAA,CAAO,CAAA,EAAA,GAAM;AACZ,EAAA,oCAAA,CAAK;AACP,CAAC,CAAA;AAEH,OAAA,CAAQ,KAAA,CAAM,CAAA","file":"/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/cli.cjs","sourcesContent":[null,"#!/usr/bin/env node\n\nimport { run, init } from '../core/index.js';\nimport { Command } from 'commander';\nconst program = new Command();\n\nprogram\n .name('@scopeact/autoi18n')\n .description('CLI para geração de arquivos e tradução automática de arquivos tsx');\n\nprogram\n .command('run')\n .action(() => {\n run();\n });\n\nprogram\n .command('init')\n .action(() => {\n init();\n });\n\nprogram.parse();"]}
1
+ {"version":3,"sources":["/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/cli.cjs","../src/cli/index.ts"],"names":[],"mappings":"AAAA;AACA;AACE;AACA;AACF,wDAA6B;AAC7B;AACA;ACHA,sCAAwB;AACxB,IAAM,QAAA,EAAU,IAAI,uBAAA,CAAQ,CAAA;AAE5B,OAAA,CACG,IAAA,CAAK,oBAAoB,CAAA,CACzB,WAAA,CAAY,mFAAoE,CAAA;AAEnF,OAAA,CACG,OAAA,CAAQ,KAAK,CAAA,CACb,MAAA,CAAO,CAAA,EAAA,GAAM;AACZ,EAAA,mCAAA,CAAI;AACN,CAAC,CAAA;AAEH,OAAA,CACG,OAAA,CAAQ,MAAM,CAAA,CACd,MAAA,CAAO,CAAA,EAAA,GAAM;AACZ,EAAA,oCAAA,CAAK;AACP,CAAC,CAAA;AAEH,OAAA,CAAQ,KAAA,CAAM,OAAA,CAAQ,IAAI,CAAA","file":"/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/cli.cjs","sourcesContent":[null,"#!/usr/bin/env node\n\nimport { run, init } from '../core/index.js';\nimport { Command } from 'commander';\nconst program = new Command();\n\nprogram\n .name('@scopeact/autoi18n')\n .description('CLI para geração de arquivos e tradução automática de arquivos tsx');\n\nprogram\n .command('run')\n .action(() => {\n run();\n });\n\nprogram\n .command('init')\n .action(() => {\n init();\n });\n\nprogram.parse(process.argv);"]}
package/dist/cli.js CHANGED
@@ -2,7 +2,7 @@
2
2
  import {
3
3
  init,
4
4
  run
5
- } from "./chunk-RLNUAAKC.js";
5
+ } from "./chunk-BITUBE6M.js";
6
6
 
7
7
  // src/cli/index.ts
8
8
  import { Command } from "commander";
@@ -14,5 +14,5 @@ program.command("run").action(() => {
14
14
  program.command("init").action(() => {
15
15
  init();
16
16
  });
17
- program.parse();
17
+ program.parse(process.argv);
18
18
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { run, init } from '../core/index.js';\nimport { Command } from 'commander';\nconst program = new Command();\n\nprogram\n .name('@scopeact/autoi18n')\n .description('CLI para geração de arquivos e tradução automática de arquivos tsx');\n\nprogram\n .command('run')\n .action(() => {\n run();\n });\n\nprogram\n .command('init')\n .action(() => {\n init();\n });\n\nprogram.parse();"],"mappings":";;;;;;;AAGA,SAAS,eAAe;AACxB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,oBAAoB,EACzB,YAAY,mFAAoE;AAEnF,QACG,QAAQ,KAAK,EACb,OAAO,MAAM;AACZ,MAAI;AACN,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,OAAO,MAAM;AACZ,OAAK;AACP,CAAC;AAEH,QAAQ,MAAM;","names":[]}
1
+ {"version":3,"sources":["../src/cli/index.ts"],"sourcesContent":["#!/usr/bin/env node\n\nimport { run, init } from '../core/index.js';\nimport { Command } from 'commander';\nconst program = new Command();\n\nprogram\n .name('@scopeact/autoi18n')\n .description('CLI para geração de arquivos e tradução automática de arquivos tsx');\n\nprogram\n .command('run')\n .action(() => {\n run();\n });\n\nprogram\n .command('init')\n .action(() => {\n init();\n });\n\nprogram.parse(process.argv);"],"mappings":";;;;;;;AAGA,SAAS,eAAe;AACxB,IAAM,UAAU,IAAI,QAAQ;AAE5B,QACG,KAAK,oBAAoB,EACzB,YAAY,mFAAoE;AAEnF,QACG,QAAQ,KAAK,EACb,OAAO,MAAM;AACZ,MAAI;AACN,CAAC;AAEH,QACG,QAAQ,MAAM,EACd,OAAO,MAAM;AACZ,OAAK;AACP,CAAC;AAEH,QAAQ,MAAM,QAAQ,IAAI;","names":[]}
@@ -1,9 +1,9 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
3
 
4
- var _chunkTWTG4RTIcjs = require('../chunk-TWTG4RTI.cjs');
4
+ var _chunkVIPNHGINcjs = require('../chunk-VIPNHGIN.cjs');
5
5
 
6
6
 
7
7
 
8
- exports.init = _chunkTWTG4RTIcjs.init; exports.run = _chunkTWTG4RTIcjs.run;
8
+ exports.init = _chunkVIPNHGINcjs.init; exports.run = _chunkVIPNHGINcjs.run;
9
9
  //# sourceMappingURL=index.cjs.map
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  init,
3
3
  run
4
- } from "../chunk-RLNUAAKC.js";
4
+ } from "../chunk-BITUBE6M.js";
5
5
  export {
6
6
  init,
7
7
  run
package/dist/index.cjs CHANGED
@@ -1,7 +1,7 @@
1
1
  "use strict";Object.defineProperty(exports, "__esModule", {value: true});
2
2
 
3
3
 
4
- var _chunkTWTG4RTIcjs = require('./chunk-TWTG4RTI.cjs');
4
+ var _chunkVIPNHGINcjs = require('./chunk-VIPNHGIN.cjs');
5
5
 
6
6
 
7
7
 
@@ -17,5 +17,5 @@ var _chunk36HQJSGIcjs = require('./chunk-36HQJSGI.cjs');
17
17
 
18
18
 
19
19
 
20
- exports.I18nProvider = _chunk36HQJSGIcjs.I18nProvider; exports.getI18n = _chunkTJT6ONONcjs.getI18n; exports.getI18nConfig = _chunkTJT6ONONcjs.getI18nConfig; exports.init = _chunkTWTG4RTIcjs.init; exports.run = _chunkTWTG4RTIcjs.run; exports.useI18n = _chunk36HQJSGIcjs.useI18n;
20
+ exports.I18nProvider = _chunk36HQJSGIcjs.I18nProvider; exports.getI18n = _chunkTJT6ONONcjs.getI18n; exports.getI18nConfig = _chunkTJT6ONONcjs.getI18nConfig; exports.init = _chunkVIPNHGINcjs.init; exports.run = _chunkVIPNHGINcjs.run; exports.useI18n = _chunk36HQJSGIcjs.useI18n;
21
21
  //# sourceMappingURL=index.cjs.map
package/dist/index.js CHANGED
@@ -1,7 +1,7 @@
1
1
  import {
2
2
  init,
3
3
  run
4
- } from "./chunk-RLNUAAKC.js";
4
+ } from "./chunk-BITUBE6M.js";
5
5
  import {
6
6
  getI18n,
7
7
  getI18nConfig
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@scopeact/autoi18n",
3
- "version": "1.2.1",
3
+ "version": "1.3.0",
4
4
  "description": "Auto i18n tool",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
package/schema.json CHANGED
@@ -10,14 +10,20 @@
10
10
  },
11
11
  "targetLangs": {
12
12
  "type": "array",
13
- "items": { "type": "string" },
13
+ "items": {
14
+ "type": "string"
15
+ },
14
16
  "description": "Lista de idiomas para os quais você deseja traduzir."
15
17
  },
16
18
  "files": {
17
19
  "type": "array",
18
- "items": { "type": "string" },
20
+ "items": {
21
+ "type": "string"
22
+ },
19
23
  "description": "Glob patterns dos arquivos que o CLI deve escanear.",
20
- "default": ["src/**/*.tsx"]
24
+ "default": [
25
+ "app/**/*.tsx"
26
+ ]
21
27
  },
22
28
  "autoInject": {
23
29
  "type": "boolean",
@@ -27,19 +33,43 @@
27
33
  "i18nLibrary": {
28
34
  "type": "string",
29
35
  "description": "A biblioteca de i18n que será utilizada.",
30
- "default": "react-i18next",
31
- "enum": ["react-i18next", "next-i18n"]
36
+ "default": "@scopeact/autoi18n",
37
+ "enum": [
38
+ "react-i18next",
39
+ "next-intl",
40
+ "@scopeact/autoi18n"
41
+ ]
32
42
  },
33
43
  "provider": {
34
44
  "type": "string",
35
45
  "description": "O Provider de IA que será utilizado para tradução.",
36
46
  "default": "openai",
37
- "enum": ["openai", "google", "deepseek", "openrouter", "ollama"]
47
+ "enum": [
48
+ "openai",
49
+ "google",
50
+ "deepseek",
51
+ "openrouter",
52
+ "ollama"
53
+ ]
38
54
  },
39
55
  "model": {
40
56
  "type": "string",
41
57
  "description": "O modelo de IA que será utilizado para tradução.",
42
- "default": "gpt-3.5-turbo"
58
+ "default": "gpt-4o-mini",
59
+ "anyOf": [
60
+ {
61
+ "enum": [
62
+ "gpt-3.5-turbo",
63
+ "gpt-4",
64
+ "gpt-4-turbo",
65
+ "gpt-4o-mini",
66
+ "gpt-4o",
67
+ "gemini-2.5-flash",
68
+ "gemini-2.5-pro",
69
+ "gemini-2.5-flash-lite"
70
+ ]
71
+ }
72
+ ]
43
73
  },
44
74
  "localesDir": {
45
75
  "type": "string",
@@ -47,5 +77,9 @@
47
77
  "default": "./locales"
48
78
  }
49
79
  },
50
- "required": ["sourceLang", "targetLangs", "localesDir"]
80
+ "required": [
81
+ "sourceLang",
82
+ "targetLangs",
83
+ "localesDir"
84
+ ]
51
85
  }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/core/scanner.ts","../src/core/config.ts","../src/core/utils.ts","../src/core/ai.ts","../src/core/dependecies.ts","../src/core/index.ts"],"sourcesContent":["import ora from \"ora\";\nimport { Node, Project, SyntaxKind } from \"ts-morph\";\nimport { readConfig } from \"./config.js\";\nimport { cleanJsonResponse, createExtractionPrompt, createTranslationPrompt, lerJson, salvarJson, type ExtractionItem } from \"./utils.js\";\nimport { askLLM } from \"./ai.js\";\nimport chalk from \"chalk\";\nimport { ensureI18nImport } from \"./dependecies.js\";\n\ninterface JsonTranslation {\n [key: string]: string;\n}\n\nexport async function scanFilesAndRun() {\n const sourceLang = readConfig('sourceLang') || 'pt';\n const targetLangs = readConfig('targetLangs') || ['en'];\n const localesDir = readConfig('localesDir') || './locales';\n\n const pendentes: { node: Node, text: string }[] = [];\n const stringsUnicas = new Set<string>();\n const novasChavesParaSalvar: Record<string, string> = {};\n const mapaTextoParaChave: Record<string, string> = {};\n\n const project = new Project({\n skipAddingFilesFromTsConfig: true,\n });\n\n const spinner = ora(\"📂 Carregando e analisando arquivos...\").start();\n project.addSourceFilesAtPaths(readConfig(\"files\"));\n\n const arquivos = project.getSourceFiles();\n\n for (const arquivo of arquivos) {\n const shouldInject = readConfig(\"autoInject\") || false;\n if(shouldInject) {\n ensureI18nImport(arquivo);\n }\n arquivo.forEachDescendant((node) => {\n if (node.getKind() === SyntaxKind.JsxText) {\n const textoOriginal = node.getText();\n if (textoOriginal.trim().length > 0) {\n pendentes.push({ node, text: textoOriginal.trim() });\n stringsUnicas.add(textoOriginal.trim());\n }\n }\n });\n }\n\n //pluralização elegante\n const fileCount = arquivos.length;\n const strCount = stringsUnicas.size;\n const sFiles = fileCount === 1 ? '' : 's';\n const sStr = strCount === 1 ? '' : 's';\n spinner.succeed(`Encontrado(s) ${chalk.cyan(strCount)} texto${sStr} único${sStr} em ${chalk.cyan(fileCount)} arquivo${sFiles}.`);\n const spinnerLLM = ora(`🧠 Gerando chaves inteligentes para ${strCount} textos...`).start();\n const listaParaIA = Array.from(stringsUnicas).map((text, index) => ({\n id: index,\n text\n })) as ExtractionItem[];\n\n const respostaIA = JSON.parse(cleanJsonResponse(await askLLM(createExtractionPrompt(listaParaIA), 'json')));\n\n respostaIA.forEach((item: any) => {\n const originalText = listaParaIA.find(l => l.id === item.id)?.text;\n if (originalText) mapaTextoParaChave[originalText] = item.key;\n });\n\n pendentes.forEach(({ node, text }) => {\n const key = mapaTextoParaChave[text];\n if (key) {\n node.replaceWithText(`{t('${key}')}`);\n }\n });\n project.saveSync();\n spinnerLLM.succeed(chalk.green(\"Código atualizado com as novas chaves i18n!\"));\n\n const spinnerSave = ora(\"💾 Gerando arquivos de tradução...\").start();\n\n for (const [textoOriginal, chaveGerada] of Object.entries(mapaTextoParaChave)) {\n novasChavesParaSalvar[chaveGerada] = textoOriginal;\n }\n\n const pathPt = `${localesDir}/${sourceLang}.json`;\n const jsonPtAtual = lerJson(pathPt);\n const jsonPtFinal = { ...jsonPtAtual, ...novasChavesParaSalvar };\n\n salvarJson(pathPt, jsonPtFinal);\n\n // Salvar em outros idiomas\n for (const idioma of targetLangs) {\n const pathIdioma = `${localesDir}/${idioma}.json`;\n const jsonIdiomaAtual = lerJson(pathIdioma) as JsonTranslation;\n\n const deltaParaTraduzir: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(novasChavesParaSalvar)) {\n if (!jsonIdiomaAtual[key]) {\n deltaParaTraduzir[key] = value;\n }\n }\n\n if (Object.keys(deltaParaTraduzir).length > 0) {\n const prompt = createTranslationPrompt(deltaParaTraduzir, sourceLang, idioma);\n\n const respostaIA = cleanJsonResponse(await askLLM(prompt, 'json'));\n const traducoesEn = JSON.parse(respostaIA);\n\n const jsonIdiomaFinal = { ...jsonIdiomaAtual, ...traducoesEn };\n salvarJson(pathIdioma, jsonIdiomaFinal);\n }\n }\n\n spinnerSave.succeed('Tradução concluída e chaves salvas com sucesso!');\n}","import fs from \"fs\";\nimport path from \"path\";\nimport ora from \"ora\";\n\nconst configPath = path.join(process.cwd(), \"auto-i18n.config.json\");\n\nexport function readConfig(key: string) {\n if (!fs.existsSync(configPath)) {\n createConfig();\n }\n const config = JSON.parse(fs.readFileSync(configPath, \"utf-8\"));\n return config[key];\n}\n\nexport function createConfig() {\n const spinner = ora('Criando arquivos de configuração...').start();\n const defaultConfig = {\n $schema: `https://unpkg.com/@scopeact/autoi18n@latest/schema.json`,\n sourceLang: \"pt\",\n targetLangs: [\"en\", \"es\"],\n autoInject: false,\n i18nLibrary: \"react-i18next\",\n provider: \"openai\",\n localesDir: \"./locales\",\n model: \"gpt-3.5-turbo\",\n files: [\"src/**/*.tsx\", \"app/**/*.tsx\"]\n };\n\n fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));\n spinner.succeed();\n}","import fs from 'fs';\nimport path from 'path';\n\nexport interface ExtractionItem {\n id: number;\n text: string;\n}\n\nexport function createExtractionPrompt(itens: ExtractionItem[], contexto?: string): string {\n return `\nVocê é um especialista em i18n. Recebi uma lista de textos de uma interface React.\nSua tarefa é gerar chaves (keys) semânticas e curtas em inglês (snake_case) para cada item.\n\nREGRAS:\n1. Retorne APENAS um array de objetos JSON.\n2. Cada objeto deve ter: \"id\" (o mesmo enviado) e \"key\" (a chave sugerida).\n3. As chaves devem ser curtas (max 4 palavras). \n4. Textos longos devem ter chaves que resumam o sentido.\n\nEXEMPLO DE ENTRADA:\n[ { \"id\": 1, \"text\": \"Salvar alterações\" } ]\n\nEXEMPLO DE SAÍDA:\n[ { \"id\": 1, \"key\": \"button_save_changes\" } ]\n\nITENS PARA PROCESSAR:\n${JSON.stringify(itens, null, 2)}\n\n${contexto ? `Contexto do projeto: ${contexto}` : ''}\n`.trim();\n}\n\nexport function createTranslationPrompt(\n delta: object, \n from: string, \n to: string, \n contexto?: string\n): string {\n return `\nVocê é um tradutor sênior de software especializado em localização (i18n).\nSua tarefa é traduzir os VALORES das novas chaves de interface que foram adicionadas ao projeto.\n\nIDIOMAS: De \"${from}\" para \"${to}\".\n\nREGRAS CRÍTICAS:\n1. MANUTENÇÃO DE CHAVES: Mantenha as chaves (keys) EXATAMENTE como estão. Não as traduza nem as altere.\n2. TRADUÇÃO DE VALORES: Traduza apenas os valores para o idioma de destino.\n3. VARIÁVEIS E PLACEHOLDERS: Preserve placeholders como {{name}}, {0}, %s ou qualquer texto entre chaves. Eles NÃO devem ser traduzidos.\n4. TOM DE VOZ: Use um tom profissional e conciso, adequado para interfaces de usuário (botões, labels, mensagens de erro).\n5. FORMATO: Retorne APENAS o objeto JSON puro, sem explicações ou blocos de código markdown.\n\n${contexto ? `CONTEXTO DO PROJETO: ${contexto}` : ''}\n\nDADOS PARA TRADUZIR:\n${JSON.stringify(delta, null, 2)}\n`.trim();\n}\n\nexport function cleanJsonResponse(text: string): string {\n return text.replace(/```json|```/g, '').trim();\n}\n\nexport function lerJson(caminho: string): object {\n if (!fs.existsSync(caminho)) {\n fs.mkdirSync(path.dirname(caminho), { recursive: true });\n fs.writeFileSync(caminho, '{}');\n return {};\n }\n \n return JSON.parse(fs.readFileSync(caminho, 'utf-8'));\n}\n\nexport function salvarJson(caminho: string, json: object): void {\n if(!fs.existsSync(caminho)) {\n fs.mkdirSync(path.dirname(caminho), { recursive: true });\n }\n fs.writeFileSync(caminho, JSON.stringify(json, null, 2));\n}","import dotenv from 'dotenv';\ndotenv.config();\nimport { OpenAI } from 'openai';\nimport { GoogleGenerativeAI } from '@google/generative-ai';\nimport { readConfig } from \"./config.js\";\n\nexport type AIProvider = 'openai' | 'google' | 'deepseek' | 'openrouter' | 'ollama';\n\nexport async function askLLM(prompt: string, mode = 'text'): Promise<string> {\n const provider = readConfig('provider') || 'openai' as AIProvider;\n const model = readConfig('model');\n checkEnv(provider);\n switch (provider) {\n case 'openai':\n return askOpenAI(prompt, model || 'gpt-4o', mode);\n case 'google':\n return askGemini(prompt, model || 'gemini-2.5-flash', mode);\n case 'deepseek':\n return askDeepSeek(prompt, model || 'deepseek-chat', mode);\n case 'openrouter':\n return askOpenRouter(prompt, model || 'google/gemini-2.0-flash-001', mode);\n case 'ollama':\n return askOllama(prompt, model || 'llama3', mode);\n default:\n throw new Error(`Provedor de IA desconhecido: ${provider}`);\n }\n}\n\nexport function checkEnv(provider: string) {\n if (!process.env[`${provider.toUpperCase()}_API_KEY`]) {\n throw new Error(`${provider.toUpperCase()}_API_KEY não foi definido`);\n }\n}\n\nasync function askOpenAI(prompt: string, model: string, mode: string) {\n const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n const response = await openai.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askGemini(prompt: string, model: string, mode: string) {\n const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || '');\n const gemini = genAI.getGenerativeModel({ model, generationConfig: { responseMimeType: mode === 'json' ? 'application/json' : 'text/plain' } });\n const result = await gemini.generateContent(prompt);\n return result.response.text();\n}\n\nasync function askDeepSeek(prompt: string, model: string, mode: string) {\n const client = new OpenAI({\n apiKey: process.env.DEEPSEEK_API_KEY,\n baseURL: 'https://api.deepseek.com'\n });\n const response = await client.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askOpenRouter(prompt: string, model: string, mode: string) {\n const client = new OpenAI({\n apiKey: process.env.OPENROUTER_API_KEY,\n baseURL: 'https://openrouter.ai/api/v1',\n defaultHeaders: {\n \"HTTP-Referer\": \"https://github.com/felipevetter/auto-i18n\",\n }\n });\n const response = await client.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askOllama(prompt: string, model: string, mode: string) {\n const response = await fetch('http://localhost:11434/api/generate', {\n method: 'POST',\n body: JSON.stringify({\n model,\n prompt,\n format: mode === 'json' ? 'json' : 'text',\n stream: false,\n }),\n });\n const data = await response.json() as { response: string };\n return data.response;\n}","import { SourceFile } from \"ts-morph\";\n\nexport function ensureI18nImport(sourceFile: SourceFile) {\n const importDeclaration = sourceFile.getImportDeclaration(\"react-i18next\");\n \n if (!importDeclaration) {\n sourceFile.addImportDeclaration({\n moduleSpecifier: \"react-i18next\",\n namedImports: [\"useTranslation\"]\n });\n } else {\n const namedImports = importDeclaration.getNamedImports().map(i => i.getName());\n if (!namedImports.includes(\"useTranslation\")) {\n importDeclaration.addNamedImport(\"useTranslation\");\n }\n }\n}","import { scanFilesAndRun } from \"./scanner.js\";\nimport { createConfig } from \"./config.js\";\n\nexport const run = () => {\n scanFilesAndRun();\n};\n\nexport const init = () => {\n createConfig();\n}"],"mappings":"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","names":["ora","fs","path","ora","respostaIA"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/chunk-TWTG4RTI.cjs","../src/core/scanner.ts","../src/core/config.ts","../src/core/utils.ts","../src/core/ai.ts","../src/core/dependecies.ts","../src/core/index.ts"],"names":["respostaIA"],"mappings":"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","file":"/home/runner/work/auto-i18n/auto-i18n/packages/autoi18n/dist/chunk-TWTG4RTI.cjs","sourcesContent":[null,"import ora from \"ora\";\nimport { Node, Project, SyntaxKind } from \"ts-morph\";\nimport { readConfig } from \"./config.js\";\nimport { cleanJsonResponse, createExtractionPrompt, createTranslationPrompt, lerJson, salvarJson, type ExtractionItem } from \"./utils.js\";\nimport { askLLM } from \"./ai.js\";\nimport chalk from \"chalk\";\nimport { ensureI18nImport } from \"./dependecies.js\";\n\ninterface JsonTranslation {\n [key: string]: string;\n}\n\nexport async function scanFilesAndRun() {\n const sourceLang = readConfig('sourceLang') || 'pt';\n const targetLangs = readConfig('targetLangs') || ['en'];\n const localesDir = readConfig('localesDir') || './locales';\n\n const pendentes: { node: Node, text: string }[] = [];\n const stringsUnicas = new Set<string>();\n const novasChavesParaSalvar: Record<string, string> = {};\n const mapaTextoParaChave: Record<string, string> = {};\n\n const project = new Project({\n skipAddingFilesFromTsConfig: true,\n });\n\n const spinner = ora(\"📂 Carregando e analisando arquivos...\").start();\n project.addSourceFilesAtPaths(readConfig(\"files\"));\n\n const arquivos = project.getSourceFiles();\n\n for (const arquivo of arquivos) {\n const shouldInject = readConfig(\"autoInject\") || false;\n if(shouldInject) {\n ensureI18nImport(arquivo);\n }\n arquivo.forEachDescendant((node) => {\n if (node.getKind() === SyntaxKind.JsxText) {\n const textoOriginal = node.getText();\n if (textoOriginal.trim().length > 0) {\n pendentes.push({ node, text: textoOriginal.trim() });\n stringsUnicas.add(textoOriginal.trim());\n }\n }\n });\n }\n\n //pluralização elegante\n const fileCount = arquivos.length;\n const strCount = stringsUnicas.size;\n const sFiles = fileCount === 1 ? '' : 's';\n const sStr = strCount === 1 ? '' : 's';\n spinner.succeed(`Encontrado(s) ${chalk.cyan(strCount)} texto${sStr} único${sStr} em ${chalk.cyan(fileCount)} arquivo${sFiles}.`);\n const spinnerLLM = ora(`🧠 Gerando chaves inteligentes para ${strCount} textos...`).start();\n const listaParaIA = Array.from(stringsUnicas).map((text, index) => ({\n id: index,\n text\n })) as ExtractionItem[];\n\n const respostaIA = JSON.parse(cleanJsonResponse(await askLLM(createExtractionPrompt(listaParaIA), 'json')));\n\n respostaIA.forEach((item: any) => {\n const originalText = listaParaIA.find(l => l.id === item.id)?.text;\n if (originalText) mapaTextoParaChave[originalText] = item.key;\n });\n\n pendentes.forEach(({ node, text }) => {\n const key = mapaTextoParaChave[text];\n if (key) {\n node.replaceWithText(`{t('${key}')}`);\n }\n });\n project.saveSync();\n spinnerLLM.succeed(chalk.green(\"Código atualizado com as novas chaves i18n!\"));\n\n const spinnerSave = ora(\"💾 Gerando arquivos de tradução...\").start();\n\n for (const [textoOriginal, chaveGerada] of Object.entries(mapaTextoParaChave)) {\n novasChavesParaSalvar[chaveGerada] = textoOriginal;\n }\n\n const pathPt = `${localesDir}/${sourceLang}.json`;\n const jsonPtAtual = lerJson(pathPt);\n const jsonPtFinal = { ...jsonPtAtual, ...novasChavesParaSalvar };\n\n salvarJson(pathPt, jsonPtFinal);\n\n // Salvar em outros idiomas\n for (const idioma of targetLangs) {\n const pathIdioma = `${localesDir}/${idioma}.json`;\n const jsonIdiomaAtual = lerJson(pathIdioma) as JsonTranslation;\n\n const deltaParaTraduzir: Record<string, string> = {};\n\n for (const [key, value] of Object.entries(novasChavesParaSalvar)) {\n if (!jsonIdiomaAtual[key]) {\n deltaParaTraduzir[key] = value;\n }\n }\n\n if (Object.keys(deltaParaTraduzir).length > 0) {\n const prompt = createTranslationPrompt(deltaParaTraduzir, sourceLang, idioma);\n\n const respostaIA = cleanJsonResponse(await askLLM(prompt, 'json'));\n const traducoesEn = JSON.parse(respostaIA);\n\n const jsonIdiomaFinal = { ...jsonIdiomaAtual, ...traducoesEn };\n salvarJson(pathIdioma, jsonIdiomaFinal);\n }\n }\n\n spinnerSave.succeed('Tradução concluída e chaves salvas com sucesso!');\n}","import fs from \"fs\";\nimport path from \"path\";\nimport ora from \"ora\";\n\nconst configPath = path.join(process.cwd(), \"auto-i18n.config.json\");\n\nexport function readConfig(key: string) {\n if (!fs.existsSync(configPath)) {\n createConfig();\n }\n const config = JSON.parse(fs.readFileSync(configPath, \"utf-8\"));\n return config[key];\n}\n\nexport function createConfig() {\n const spinner = ora('Criando arquivos de configuração...').start();\n const defaultConfig = {\n $schema: `https://unpkg.com/@scopeact/autoi18n@latest/schema.json`,\n sourceLang: \"pt\",\n targetLangs: [\"en\", \"es\"],\n autoInject: false,\n i18nLibrary: \"react-i18next\",\n provider: \"openai\",\n localesDir: \"./locales\",\n model: \"gpt-3.5-turbo\",\n files: [\"src/**/*.tsx\", \"app/**/*.tsx\"]\n };\n\n fs.writeFileSync(configPath, JSON.stringify(defaultConfig, null, 2));\n spinner.succeed();\n}","import fs from 'fs';\nimport path from 'path';\n\nexport interface ExtractionItem {\n id: number;\n text: string;\n}\n\nexport function createExtractionPrompt(itens: ExtractionItem[], contexto?: string): string {\n return `\nVocê é um especialista em i18n. Recebi uma lista de textos de uma interface React.\nSua tarefa é gerar chaves (keys) semânticas e curtas em inglês (snake_case) para cada item.\n\nREGRAS:\n1. Retorne APENAS um array de objetos JSON.\n2. Cada objeto deve ter: \"id\" (o mesmo enviado) e \"key\" (a chave sugerida).\n3. As chaves devem ser curtas (max 4 palavras). \n4. Textos longos devem ter chaves que resumam o sentido.\n\nEXEMPLO DE ENTRADA:\n[ { \"id\": 1, \"text\": \"Salvar alterações\" } ]\n\nEXEMPLO DE SAÍDA:\n[ { \"id\": 1, \"key\": \"button_save_changes\" } ]\n\nITENS PARA PROCESSAR:\n${JSON.stringify(itens, null, 2)}\n\n${contexto ? `Contexto do projeto: ${contexto}` : ''}\n`.trim();\n}\n\nexport function createTranslationPrompt(\n delta: object, \n from: string, \n to: string, \n contexto?: string\n): string {\n return `\nVocê é um tradutor sênior de software especializado em localização (i18n).\nSua tarefa é traduzir os VALORES das novas chaves de interface que foram adicionadas ao projeto.\n\nIDIOMAS: De \"${from}\" para \"${to}\".\n\nREGRAS CRÍTICAS:\n1. MANUTENÇÃO DE CHAVES: Mantenha as chaves (keys) EXATAMENTE como estão. Não as traduza nem as altere.\n2. TRADUÇÃO DE VALORES: Traduza apenas os valores para o idioma de destino.\n3. VARIÁVEIS E PLACEHOLDERS: Preserve placeholders como {{name}}, {0}, %s ou qualquer texto entre chaves. Eles NÃO devem ser traduzidos.\n4. TOM DE VOZ: Use um tom profissional e conciso, adequado para interfaces de usuário (botões, labels, mensagens de erro).\n5. FORMATO: Retorne APENAS o objeto JSON puro, sem explicações ou blocos de código markdown.\n\n${contexto ? `CONTEXTO DO PROJETO: ${contexto}` : ''}\n\nDADOS PARA TRADUZIR:\n${JSON.stringify(delta, null, 2)}\n`.trim();\n}\n\nexport function cleanJsonResponse(text: string): string {\n return text.replace(/```json|```/g, '').trim();\n}\n\nexport function lerJson(caminho: string): object {\n if (!fs.existsSync(caminho)) {\n fs.mkdirSync(path.dirname(caminho), { recursive: true });\n fs.writeFileSync(caminho, '{}');\n return {};\n }\n \n return JSON.parse(fs.readFileSync(caminho, 'utf-8'));\n}\n\nexport function salvarJson(caminho: string, json: object): void {\n if(!fs.existsSync(caminho)) {\n fs.mkdirSync(path.dirname(caminho), { recursive: true });\n }\n fs.writeFileSync(caminho, JSON.stringify(json, null, 2));\n}","import dotenv from 'dotenv';\ndotenv.config();\nimport { OpenAI } from 'openai';\nimport { GoogleGenerativeAI } from '@google/generative-ai';\nimport { readConfig } from \"./config.js\";\n\nexport type AIProvider = 'openai' | 'google' | 'deepseek' | 'openrouter' | 'ollama';\n\nexport async function askLLM(prompt: string, mode = 'text'): Promise<string> {\n const provider = readConfig('provider') || 'openai' as AIProvider;\n const model = readConfig('model');\n checkEnv(provider);\n switch (provider) {\n case 'openai':\n return askOpenAI(prompt, model || 'gpt-4o', mode);\n case 'google':\n return askGemini(prompt, model || 'gemini-2.5-flash', mode);\n case 'deepseek':\n return askDeepSeek(prompt, model || 'deepseek-chat', mode);\n case 'openrouter':\n return askOpenRouter(prompt, model || 'google/gemini-2.0-flash-001', mode);\n case 'ollama':\n return askOllama(prompt, model || 'llama3', mode);\n default:\n throw new Error(`Provedor de IA desconhecido: ${provider}`);\n }\n}\n\nexport function checkEnv(provider: string) {\n if (!process.env[`${provider.toUpperCase()}_API_KEY`]) {\n throw new Error(`${provider.toUpperCase()}_API_KEY não foi definido`);\n }\n}\n\nasync function askOpenAI(prompt: string, model: string, mode: string) {\n const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });\n const response = await openai.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askGemini(prompt: string, model: string, mode: string) {\n const genAI = new GoogleGenerativeAI(process.env.GOOGLE_API_KEY || '');\n const gemini = genAI.getGenerativeModel({ model, generationConfig: { responseMimeType: mode === 'json' ? 'application/json' : 'text/plain' } });\n const result = await gemini.generateContent(prompt);\n return result.response.text();\n}\n\nasync function askDeepSeek(prompt: string, model: string, mode: string) {\n const client = new OpenAI({\n apiKey: process.env.DEEPSEEK_API_KEY,\n baseURL: 'https://api.deepseek.com'\n });\n const response = await client.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askOpenRouter(prompt: string, model: string, mode: string) {\n const client = new OpenAI({\n apiKey: process.env.OPENROUTER_API_KEY,\n baseURL: 'https://openrouter.ai/api/v1',\n defaultHeaders: {\n \"HTTP-Referer\": \"https://github.com/felipevetter/auto-i18n\",\n }\n });\n const response = await client.chat.completions.create({\n response_format: mode === 'json' ? { type: 'json_object' } : { type: 'text' },\n messages: [{ role: 'user', content: prompt }],\n model,\n });\n return response?.choices?.[0]?.message?.content || '';\n}\n\nasync function askOllama(prompt: string, model: string, mode: string) {\n const response = await fetch('http://localhost:11434/api/generate', {\n method: 'POST',\n body: JSON.stringify({\n model,\n prompt,\n format: mode === 'json' ? 'json' : 'text',\n stream: false,\n }),\n });\n const data = await response.json() as { response: string };\n return data.response;\n}","import { SourceFile } from \"ts-morph\";\n\nexport function ensureI18nImport(sourceFile: SourceFile) {\n const importDeclaration = sourceFile.getImportDeclaration(\"react-i18next\");\n \n if (!importDeclaration) {\n sourceFile.addImportDeclaration({\n moduleSpecifier: \"react-i18next\",\n namedImports: [\"useTranslation\"]\n });\n } else {\n const namedImports = importDeclaration.getNamedImports().map(i => i.getName());\n if (!namedImports.includes(\"useTranslation\")) {\n importDeclaration.addNamedImport(\"useTranslation\");\n }\n }\n}","import { scanFilesAndRun } from \"./scanner.js\";\nimport { createConfig } from \"./config.js\";\n\nexport const run = () => {\n scanFilesAndRun();\n};\n\nexport const init = () => {\n createConfig();\n}"]}