@concircle/i18n-ai-translator 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/translator.ts","../src/cache/fileCache.ts","../src/glossary/glossaryManager.ts","../src/parsers/placeholderExtractor.ts","../src/parsers/propertiesParser.ts","../src/providers/openai/client.ts","../src/providers/base.ts","../src/utils/config.ts","../src/utils/logger.ts"],"sourcesContent":["import path from 'path';\nimport { FileCache } from './cache/fileCache.js';\nimport { GlossaryManager } from './glossary/glossaryManager.js';\nimport {\n maskPlaceholders,\n restorePlaceholders,\n validatePlaceholderIntegrity,\n} from './parsers/placeholderExtractor.js';\nimport {\n cloneDocument,\n createEmptyPropertiesDocument,\n getLanguageFilePath,\n listPropertiesEntries,\n readPropertiesDocument,\n upsertPropertiesValue,\n writePropertiesDocument,\n} from './parsers/propertiesParser.js';\nimport { OpenAIProvider } from './providers/openai/client.js';\nimport {\n AIProvider,\n GlossaryConfig,\n TranslationBatchResponseItem,\n TranslationFileOptions,\n TranslationProjectOptions,\n TranslationResult,\n TranslatorConfig,\n} from './types.js';\nimport { loadTranslatorConfig, resolveTranslationMode, validateConfig } from './utils/config.js';\nimport { ConsoleLogger, SilentLogger } from './utils/logger.js';\n\nexport class Translator {\n private readonly config: TranslatorConfig;\n private readonly provider: AIProvider;\n private readonly glossaryManager: GlossaryManager;\n private readonly cache: FileCache;\n private readonly logger: ConsoleLogger | SilentLogger;\n\n constructor(config: TranslatorConfig) {\n validateConfig(config);\n this.config = config;\n this.provider = createProvider(config);\n this.glossaryManager = new GlossaryManager(config.glossary as GlossaryConfig | undefined);\n this.cache = new FileCache(config.cache);\n this.logger = config.verbose\n ? new ConsoleLogger('@concircle/i18n-ai-translator', true)\n : new SilentLogger();\n }\n\n static async fromConfig(options?: {\n configPath?: string;\n cwd?: string;\n }): Promise<Translator> {\n const config = await loadTranslatorConfig(options);\n return new Translator(config);\n }\n\n async translateProject(options?: TranslationProjectOptions): Promise<TranslationResult> {\n const config =\n options?.configPath || options?.cwd\n ? await loadTranslatorConfig({\n configPath: options.configPath,\n cwd: options.cwd,\n overrides: {\n input: options.inputPath,\n targetLanguages: options.languages,\n translationMode: options.mode,\n encodeUnicode: options.encodeUnicode,\n provider: options.provider,\n model: options.model,\n verbose: options.verbose,\n },\n })\n : this.config;\n\n const translator =\n config === this.config ? this : new Translator(config);\n\n return translator.translateFile({\n inputPath: options?.inputPath,\n languages: options?.languages,\n mode: options?.mode,\n dryRun: options?.dryRun,\n });\n }\n\n async translateFile(options?: TranslationFileOptions): Promise<TranslationResult> {\n const inputPath = path.resolve(\n process.cwd(),\n options?.inputPath ?? this.config.files?.input ?? 'i18n/i18n.properties'\n );\n const sourceDocument = readPropertiesDocument(inputPath);\n const sourceEntries = listPropertiesEntries(sourceDocument);\n const sourceKeys = Object.keys(sourceEntries);\n const languages = options?.languages ?? this.config.targetLanguages;\n const translationMode = resolveTranslationMode(\n options?.mode,\n this.config.translationMode\n );\n\n const result: TranslationResult = {\n sourceFile: inputPath,\n translationMode,\n translations: {},\n };\n\n for (const language of languages) {\n this.logger.info('Translating language', { language });\n const targetPath = getLanguageFilePath(\n inputPath,\n language,\n this.config.files?.languageFilePattern,\n this.config.files?.outputDir\n );\n const targetDocument = readPropertiesDocument(targetPath);\n const targetEntries = listPropertiesEntries(targetDocument);\n const keysToTranslate = sourceKeys.filter((key) => {\n if (translationMode === 'overwrite') {\n return true;\n }\n\n const existingValue = targetEntries[key];\n return existingValue === undefined || existingValue === '';\n });\n\n const translatedMap = await this.translateEntries(sourceEntries, keysToTranslate, language);\n const outputDocument =\n targetDocument.lines.length > 0\n ? cloneDocument(targetDocument)\n : cloneDocument(sourceDocument);\n\n for (const [key, translatedValue] of Object.entries(translatedMap)) {\n upsertPropertiesValue(outputDocument, key, translatedValue);\n }\n\n const errors: string[] = [];\n if (!options?.dryRun) {\n writePropertiesDocument(targetPath, outputDocument, {\n encodeUnicode: this.resolveEncodeUnicode(language),\n });\n }\n\n result.translations[language] = {\n outputFile: targetPath,\n success: errors.length === 0,\n translatedKeysCount: Object.keys(translatedMap).length,\n skippedKeysCount: sourceKeys.length - keysToTranslate.length,\n errors,\n dryRun: options?.dryRun ?? false,\n };\n }\n\n return result;\n }\n\n clearCache(): void {\n this.cache.clear();\n }\n\n getCacheStats() {\n return this.cache.getStats();\n }\n\n getGlossaryTerms(language: string) {\n return this.glossaryManager.getTermsForLanguage(language);\n }\n\n private async translateEntries(\n sourceEntries: Record<string, string>,\n keys: string[],\n language: string\n ): Promise<Record<string, string>> {\n const glossaryTerms = this.glossaryManager.getTermsForLanguage(language);\n const rules = this.config.rules ?? [];\n const batchSize = this.config.batchSize ?? 20;\n const translatedMap: Record<string, string> = {};\n\n for (let start = 0; start < keys.length; start += batchSize) {\n const batchKeys = keys.slice(start, start + batchSize);\n const uncachedItems: Array<{\n key: string;\n sourceValue: string;\n maskedValue: string;\n placeholders: ReturnType<typeof maskPlaceholders>['tokens'];\n }> = [];\n\n for (const key of batchKeys) {\n const sourceValue = sourceEntries[key];\n const placeholderInfo = maskPlaceholders(sourceValue);\n const cacheKey = FileCache.createKey({\n provider: this.provider.getName(),\n model: this.config.providerOptions?.model ?? 'default',\n sourceLanguage: this.config.sourceLanguage ?? 'en',\n targetLanguage: language,\n sourceValue,\n glossaryTerms,\n rules,\n });\n const cached = this.cache.get(cacheKey);\n\n if (cached) {\n translatedMap[key] = cached;\n continue;\n }\n\n uncachedItems.push({\n key,\n sourceValue,\n maskedValue: placeholderInfo.masked,\n placeholders: placeholderInfo.tokens,\n });\n }\n\n if (uncachedItems.length === 0) {\n continue;\n }\n\n const response = await this.provider.translateBatch({\n sourceLanguage: this.config.sourceLanguage ?? 'en',\n targetLanguage: language,\n items: uncachedItems,\n glossaryTerms,\n rules,\n model: this.config.providerOptions?.model,\n });\n\n for (const item of uncachedItems) {\n const translated = findResponseItem(response.items, item.key);\n const restored = restorePlaceholders(\n translated.translatedValue,\n item.placeholders\n );\n const validation = validatePlaceholderIntegrity(item.sourceValue, restored);\n\n if (!validation.valid) {\n throw new Error(\n `Placeholder validation failed for key \"${item.key}\" in ${language}: expected ${validation.expected.join(\n ', '\n )}, got ${validation.actual.join(', ')}`\n );\n }\n\n translatedMap[item.key] = restored;\n\n const cacheKey = FileCache.createKey({\n provider: this.provider.getName(),\n model: this.config.providerOptions?.model ?? 'default',\n sourceLanguage: this.config.sourceLanguage ?? 'en',\n targetLanguage: language,\n sourceValue: item.sourceValue,\n glossaryTerms,\n rules,\n });\n this.cache.set(cacheKey, restored);\n }\n }\n\n return translatedMap;\n }\n\n private resolveEncodeUnicode(language: string): boolean {\n const languageOverride = this.config.languageOptions?.[language]?.encodeUnicode;\n return languageOverride ?? this.config.encodeUnicode ?? false;\n }\n}\n\nexport async function translateProject(\n options?: TranslationProjectOptions\n): Promise<TranslationResult> {\n const config = await loadTranslatorConfig({\n configPath: options?.configPath,\n cwd: options?.cwd,\n overrides: {\n input: options?.inputPath,\n targetLanguages: options?.languages,\n translationMode: options?.mode,\n encodeUnicode: options?.encodeUnicode,\n provider: options?.provider,\n model: options?.model,\n verbose: options?.verbose,\n },\n });\n\n const translator = new Translator(config);\n return translator.translateProject(options);\n}\n\nexport async function translateFile(\n config: TranslatorConfig,\n options?: TranslationFileOptions\n): Promise<TranslationResult> {\n const translator = new Translator(config);\n return translator.translateFile(options);\n}\n\nfunction createProvider(config: TranslatorConfig): AIProvider {\n if (config.provider === 'openai') {\n return new OpenAIProvider(config.providerOptions ?? {});\n }\n\n throw new Error(`Unsupported provider: ${config.provider}`);\n}\n\nfunction findResponseItem(\n items: TranslationBatchResponseItem[],\n key: string\n): TranslationBatchResponseItem {\n const item = items.find((candidate) => candidate.key === key);\n if (!item) {\n throw new Error(`Provider response is missing translation for key \"${key}\"`);\n }\n\n return item;\n}\n\nexport function createNewTranslationDocument() {\n return createEmptyPropertiesDocument();\n}\n","import crypto from 'crypto';\nimport fs from 'fs';\nimport path from 'path';\nimport { CacheOptions, GlossaryTerm } from '../types.js';\n\ninterface CacheEntry {\n translation: string;\n createdAt: number;\n}\n\nexport class FileCache {\n private readonly enabled: boolean;\n private readonly ttlMs: number;\n private readonly cacheDir: string;\n\n constructor(options?: CacheOptions) {\n this.enabled = options?.enabled ?? true;\n this.ttlMs = options?.ttlMs ?? 7 * 24 * 60 * 60 * 1000;\n const homeDir = process.env.HOME || process.env.USERPROFILE || '/tmp';\n this.cacheDir =\n options?.dir ?? path.join(homeDir, '.i18n-ai-translator-cache');\n\n if (this.enabled && !fs.existsSync(this.cacheDir)) {\n fs.mkdirSync(this.cacheDir, { recursive: true });\n }\n }\n\n get(cacheKey: string): string | null {\n if (!this.enabled) {\n return null;\n }\n\n const filePath = path.join(this.cacheDir, `${cacheKey}.json`);\n if (!fs.existsSync(filePath)) {\n return null;\n }\n\n try {\n const entry = JSON.parse(fs.readFileSync(filePath, 'utf-8')) as CacheEntry;\n if (Date.now() - entry.createdAt > this.ttlMs) {\n fs.unlinkSync(filePath);\n return null;\n }\n\n return entry.translation;\n } catch {\n return null;\n }\n }\n\n set(cacheKey: string, translation: string): void {\n if (!this.enabled) {\n return;\n }\n\n const filePath = path.join(this.cacheDir, `${cacheKey}.json`);\n const entry: CacheEntry = {\n translation,\n createdAt: Date.now(),\n };\n\n try {\n fs.writeFileSync(filePath, JSON.stringify(entry), 'utf-8');\n } catch {\n // Best-effort cache only.\n }\n }\n\n clear(): void {\n if (!this.enabled || !fs.existsSync(this.cacheDir)) {\n return;\n }\n\n for (const fileName of fs.readdirSync(this.cacheDir)) {\n if (fileName.endsWith('.json')) {\n fs.unlinkSync(path.join(this.cacheDir, fileName));\n }\n }\n }\n\n getStats(): { enabled: boolean; ttlMs: number; cacheDir: string; entriesCount: number } {\n let entriesCount = 0;\n if (this.enabled && fs.existsSync(this.cacheDir)) {\n entriesCount = fs\n .readdirSync(this.cacheDir)\n .filter((fileName) => fileName.endsWith('.json')).length;\n }\n\n return {\n enabled: this.enabled,\n ttlMs: this.ttlMs,\n cacheDir: this.cacheDir,\n entriesCount,\n };\n }\n\n static createKey(input: {\n provider: string;\n model: string;\n sourceLanguage: string;\n targetLanguage: string;\n sourceValue: string;\n glossaryTerms: GlossaryTerm[];\n rules: string[];\n }): string {\n const json = JSON.stringify(input);\n return crypto.createHash('sha256').update(json).digest('hex');\n }\n}\n","import fs from 'fs';\nimport { GlossaryConfig, GlossaryTerm } from '../types.js';\n\nexport class GlossaryManager {\n private glossary: GlossaryConfig;\n\n constructor(glossary?: GlossaryConfig) {\n this.glossary = glossary ?? {};\n }\n\n static loadGlossary(source?: GlossaryConfig | string): GlossaryConfig {\n if (!source) {\n return {};\n }\n\n if (typeof source === 'string') {\n return GlossaryManager.loadFromFile(source);\n }\n\n GlossaryManager.validateGlossary(source);\n return source;\n }\n\n static loadFromFile(filePath: string): GlossaryConfig {\n if (!fs.existsSync(filePath)) {\n throw new Error(`Glossary file not found: ${filePath}`);\n }\n\n const content = fs.readFileSync(filePath, 'utf-8');\n const parsed = JSON.parse(content) as GlossaryConfig;\n GlossaryManager.validateGlossary(parsed);\n return parsed;\n }\n\n static validateGlossary(glossary: unknown): void {\n if (!glossary || typeof glossary !== 'object' || Array.isArray(glossary)) {\n throw new Error('Glossary must be an object');\n }\n\n const typed = glossary as GlossaryConfig;\n GlossaryManager.validateTerms(typed.shared, 'shared');\n\n if (typed.languages) {\n for (const [language, terms] of Object.entries(typed.languages)) {\n GlossaryManager.validateTerms(terms, `languages.${language}`);\n }\n }\n }\n\n private static validateTerms(terms: GlossaryTerm[] | undefined, label: string): void {\n if (!terms) {\n return;\n }\n\n if (!Array.isArray(terms)) {\n throw new Error(`${label} glossary terms must be an array`);\n }\n\n for (const term of terms) {\n if (!term || typeof term !== 'object') {\n throw new Error(`${label} glossary term must be an object`);\n }\n\n if (!term.source || typeof term.source !== 'string') {\n throw new Error(`${label} glossary term requires a string source value`);\n }\n\n if (term.target !== undefined && typeof term.target !== 'string') {\n throw new Error(`${label} glossary term target must be a string`);\n }\n\n if (term.context !== undefined && typeof term.context !== 'string') {\n throw new Error(`${label} glossary term context must be a string`);\n }\n\n if (\n term.doNotTranslate !== undefined &&\n typeof term.doNotTranslate !== 'boolean'\n ) {\n throw new Error(`${label} glossary term doNotTranslate must be a boolean`);\n }\n }\n }\n\n getGlossary(): GlossaryConfig {\n return this.glossary;\n }\n\n getTermsForLanguage(language: string): GlossaryTerm[] {\n return [\n ...(this.glossary.shared ?? []),\n ...(this.glossary.languages?.[language] ?? []),\n ];\n }\n\n hasTerms(language: string): boolean {\n return this.getTermsForLanguage(language).length > 0;\n }\n\n toJSON(): string {\n return JSON.stringify(this.glossary, null, 2);\n }\n}\n","import { PlaceholderInfo, PlaceholderToken } from '../types.js';\n\nconst PLACEHOLDER_PATTERN =\n /\\{\\d+\\}|\\{[a-zA-Z_][\\w.-]*\\}|\\$\\{[a-zA-Z_][\\w.-]*\\}|%\\d+\\$[sdif]|%[sdif]/g;\nconst PROTECTED_SEQUENCE_PATTERN = /\\\\[nrtf]/g;\nconst MASKABLE_PATTERN = new RegExp(\n `${PLACEHOLDER_PATTERN.source}|${PROTECTED_SEQUENCE_PATTERN.source}`,\n 'g'\n);\n\nexport function extractPlaceholders(text: string): string[] {\n return Array.from(text.matchAll(MASKABLE_PATTERN), (match) => match[0]);\n}\n\nexport function hasPlaceholders(text: string): boolean {\n return new RegExp(MASKABLE_PATTERN).test(text);\n}\n\nexport function maskPlaceholders(text: string): PlaceholderInfo {\n const tokens: PlaceholderToken[] = [];\n let index = 0;\n\n const masked = text.replace(MASKABLE_PATTERN, (placeholder) => {\n const token = `__I18N_PH_${index}__`;\n index += 1;\n tokens.push({ token, placeholder });\n return token;\n });\n\n return {\n original: text,\n masked,\n tokens,\n };\n}\n\nexport function restorePlaceholders(\n text: string,\n tokens: PlaceholderToken[]\n): string {\n return tokens.reduce(\n (current, token) => current.split(token.token).join(token.placeholder),\n text\n );\n}\n\nexport function validatePlaceholderIntegrity(\n sourceText: string,\n translatedText: string\n): { valid: boolean; expected: string[]; actual: string[] } {\n const expected = extractPlaceholders(sourceText).sort();\n const actual = extractPlaceholders(translatedText).sort();\n\n return {\n valid:\n expected.length === actual.length &&\n expected.every((placeholder, index) => placeholder === actual[index]),\n expected,\n actual,\n };\n}\n","import fs from 'fs';\nimport path from 'path';\nimport {\n PropertiesDocument,\n PropertiesLine,\n PropertiesSerializationOptions,\n} from '../types.js';\n\nexport function parsePropertiesDocument(content: string): PropertiesDocument {\n const eolMatch = content.match(/\\r\\n|\\n/);\n const eol = eolMatch?.[0] ?? '\\n';\n const hasTrailingNewline = content.length > 0 && content.endsWith(eol);\n const rawLines = content.length === 0 ? [] : content.split(/\\r?\\n/);\n if (hasTrailingNewline && rawLines[rawLines.length - 1] === '') {\n rawLines.pop();\n }\n const lines = rawLines.map(parseLine);\n\n return {\n lines,\n eol,\n hasTrailingNewline,\n };\n}\n\nexport function readPropertiesDocument(filePath: string): PropertiesDocument {\n if (!fs.existsSync(filePath)) {\n return createEmptyPropertiesDocument();\n }\n\n return parsePropertiesDocument(fs.readFileSync(filePath, 'utf-8'));\n}\n\nexport function createEmptyPropertiesDocument(): PropertiesDocument {\n return {\n lines: [],\n eol: '\\n',\n hasTrailingNewline: true,\n };\n}\n\nexport function listPropertiesEntries(document: PropertiesDocument): Record<string, string> {\n const entries: Record<string, string> = {};\n\n for (const line of document.lines) {\n if (line.type === 'entry') {\n entries[line.key] = line.value;\n }\n }\n\n return entries;\n}\n\nexport function cloneDocument(document: PropertiesDocument): PropertiesDocument {\n return {\n eol: document.eol,\n hasTrailingNewline: document.hasTrailingNewline,\n lines: document.lines.map((line) => ({ ...line })),\n };\n}\n\nexport function upsertPropertiesValue(\n document: PropertiesDocument,\n key: string,\n value: string\n): void {\n const existing = findEntryLine(document, key);\n\n if (existing) {\n existing.value = value;\n existing.modified = true;\n return;\n }\n\n if (document.lines.length > 0 && document.lines[document.lines.length - 1].type !== 'blank') {\n document.lines.push({ type: 'blank', raw: '' });\n }\n\n document.lines.push({\n type: 'entry',\n raw: '',\n key,\n value,\n separator: '=',\n leadingWhitespace: '',\n modified: true,\n });\n}\n\nexport function serializePropertiesDocument(\n document: PropertiesDocument,\n options: PropertiesSerializationOptions = {}\n): string {\n const serializedLines = document.lines.map((line) => serializeLine(line, options));\n const body = serializedLines.join(document.eol);\n\n if (body.length === 0) {\n return '';\n }\n\n return document.hasTrailingNewline ? `${body}${document.eol}` : body;\n}\n\nexport function writePropertiesDocument(\n filePath: string,\n document: PropertiesDocument,\n options: PropertiesSerializationOptions = {}\n): void {\n const dir = path.dirname(filePath);\n if (!fs.existsSync(dir)) {\n fs.mkdirSync(dir, { recursive: true });\n }\n\n fs.writeFileSync(filePath, serializePropertiesDocument(document, options), 'utf-8');\n}\n\nexport function createDocumentFromEntries(\n entries: Record<string, string>\n): PropertiesDocument {\n const lines: PropertiesLine[] = Object.entries(entries).map(([key, value]) => ({\n type: 'entry',\n raw: '',\n key,\n value,\n separator: '=',\n leadingWhitespace: '',\n modified: true,\n }));\n\n return {\n lines,\n eol: '\\n',\n hasTrailingNewline: true,\n };\n}\n\nexport function getLanguageFilePath(\n sourceFilePath: string,\n language: string,\n pattern?: string,\n outputDir?: string\n): string {\n const sourceDir = outputDir ?? path.dirname(sourceFilePath);\n const ext = path.extname(sourceFilePath) || '.properties';\n const baseName = path.basename(sourceFilePath, ext);\n\n if (pattern) {\n const fileName = pattern\n .replace('{baseName}', baseName)\n .replace('{language}', language)\n .replace('{ext}', ext);\n return path.join(sourceDir, fileName);\n }\n\n const fileName = baseName === 'i18n' ? `i18n_${language}${ext}` : `${baseName}_${language}${ext}`;\n return path.join(sourceDir, fileName);\n}\n\nfunction parseLine(raw: string): PropertiesLine {\n if (raw.trim() === '') {\n return { type: 'blank', raw };\n }\n\n if (/^\\s*[#!]/.test(raw)) {\n return { type: 'comment', raw };\n }\n\n const leadingWhitespace = raw.match(/^\\s*/)![0];\n const body = raw.slice(leadingWhitespace.length);\n\n let separatorIndex = -1;\n let separator = '=';\n let valueStartIndex = -1;\n\n for (let index = 0; index < body.length; index += 1) {\n const char = body[index];\n const previous = index > 0 ? body[index - 1] : '';\n if (previous === '\\\\') {\n continue;\n }\n\n if (char === '=' || char === ':') {\n separatorIndex = index;\n separator = char;\n break;\n }\n\n if (char === ' ' || char === '\\t' || char === '\\f') {\n separatorIndex = index;\n separator = char;\n\n let lookahead = index;\n while (\n lookahead < body.length &&\n (body[lookahead] === ' ' || body[lookahead] === '\\t' || body[lookahead] === '\\f')\n ) {\n lookahead += 1;\n }\n\n if (lookahead < body.length && (body[lookahead] === '=' || body[lookahead] === ':')) {\n separator = body[lookahead];\n valueStartIndex = lookahead + 1;\n }\n break;\n }\n }\n\n const rawKey = separatorIndex >= 0 ? body.slice(0, separatorIndex) : body;\n const rawValue =\n separatorIndex >= 0\n ? body\n .slice(valueStartIndex >= 0 ? valueStartIndex : separatorIndex + 1)\n .replace(/^[ \\t\\f]*/, '')\n : '';\n\n return {\n type: 'entry',\n raw,\n key: decodePropertiesToken(rawKey.trimEnd()),\n value: decodePropertiesToken(rawValue),\n separator,\n leadingWhitespace,\n modified: false,\n };\n}\n\nfunction serializeLine(\n line: PropertiesLine,\n options: PropertiesSerializationOptions\n): string {\n if (line.type !== 'entry') {\n return line.raw;\n }\n\n if (!line.modified && line.raw) {\n return line.raw;\n }\n\n return `${line.leadingWhitespace}${escapePropertiesToken(line.key)}${line.separator}${escapePropertiesValue(\n line.value,\n options\n )}`;\n}\n\nfunction findEntryLine(document: PropertiesDocument, key: string): Extract<PropertiesLine, { type: 'entry' }> | undefined {\n return document.lines.find(\n (line): line is Extract<PropertiesLine, { type: 'entry' }> =>\n line.type === 'entry' && line.key === key\n );\n}\n\nfunction decodePropertiesToken(value: string): string {\n return value\n .replace(/\\\\u([0-9a-fA-F]{4})/g, (_, hex) =>\n String.fromCharCode(parseInt(hex, 16))\n )\n .replace(/\\\\t/g, '\\t')\n .replace(/\\\\r/g, '\\r')\n .replace(/\\\\n/g, '\\n')\n .replace(/\\\\f/g, '\\f')\n .replace(/\\\\:/g, ':')\n .replace(/\\\\=/g, '=')\n .replace(/\\\\\\\\/g, '\\\\');\n}\n\nfunction escapePropertiesToken(value: string): string {\n return value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/:/g, '\\\\:')\n .replace(/=/g, '\\\\=');\n}\n\nfunction escapePropertiesValue(\n value: string,\n options: PropertiesSerializationOptions\n): string {\n const escaped = value\n .replace(/\\\\/g, '\\\\\\\\')\n .replace(/\\n/g, '\\\\n')\n .replace(/\\r/g, '\\\\r')\n .replace(/\\t/g, '\\\\t');\n\n if (!options.encodeUnicode) {\n return escaped;\n }\n\n return Array.from(escaped, (char) =>\n char.charCodeAt(0) > 0x7f\n ? `\\\\u${char.charCodeAt(0).toString(16).padStart(4, '0')}`\n : char\n ).join('');\n}\n","import OpenAI from 'openai';\nimport { BaseAIProvider } from '../base.js';\nimport {\n OpenAIProviderOptions,\n TranslationBatchRequest,\n TranslationBatchResponse,\n} from '../../types.js';\n\nconst TRANSLATION_SCHEMA = {\n type: 'object',\n additionalProperties: false,\n required: ['items'],\n properties: {\n items: {\n type: 'array',\n items: {\n type: 'object',\n additionalProperties: false,\n required: ['key', 'translatedValue'],\n properties: {\n key: { type: 'string' },\n translatedValue: { type: 'string' },\n },\n },\n },\n },\n} satisfies Record<string, unknown>;\n\nexport class OpenAIProvider extends BaseAIProvider {\n private readonly client: OpenAI;\n private readonly options: Required<\n Pick<OpenAIProviderOptions, 'model' | 'temperature' | 'maxOutputTokens'>\n > &\n Omit<OpenAIProviderOptions, 'model' | 'temperature' | 'maxOutputTokens'>;\n\n constructor(options: OpenAIProviderOptions) {\n super();\n\n if (!options.apiKey) {\n throw new Error('OpenAI API key is required');\n }\n\n this.client = new OpenAI({\n apiKey: options.apiKey,\n baseURL: options.baseURL,\n organization: options.organization,\n });\n\n this.options = {\n ...options,\n model: options.model ?? 'gpt-4.1-mini',\n temperature: options.temperature ?? 0,\n maxOutputTokens: options.maxOutputTokens ?? 4000,\n };\n }\n\n getName(): 'openai' {\n return 'openai';\n }\n\n async translateBatch(\n request: TranslationBatchRequest\n ): Promise<TranslationBatchResponse> {\n const response = await this.client.responses.create({\n model: request.model ?? this.options.model,\n temperature: this.options.temperature,\n max_output_tokens: this.options.maxOutputTokens,\n text: {\n format: {\n type: 'json_schema',\n name: 'translation_batch',\n schema: TRANSLATION_SCHEMA,\n strict: true,\n },\n },\n input: [\n {\n role: 'system',\n content: buildSystemPrompt(),\n },\n {\n role: 'user',\n content: JSON.stringify(buildPayload(request)),\n },\n ],\n });\n\n const rawResponse = response.output_text?.trim();\n if (!rawResponse) {\n throw new Error('OpenAI Responses API returned an empty response');\n }\n\n const parsed = JSON.parse(rawResponse) as {\n items?: Array<{ key?: string; translatedValue?: string }>;\n };\n\n if (!Array.isArray(parsed.items)) {\n throw new Error('OpenAI response is missing items array');\n }\n\n return {\n provider: 'openai',\n rawResponse,\n items: parsed.items.map((item) => {\n if (!item.key || typeof item.translatedValue !== 'string') {\n throw new Error('OpenAI response contains an invalid translation item');\n }\n\n return {\n key: item.key,\n translatedValue: item.translatedValue,\n };\n }),\n };\n }\n}\n\nfunction buildSystemPrompt(): string {\n return [\n 'You translate SAP UI5 i18n property values.',\n 'Return JSON only.',\n 'Translate values, never keys.',\n 'Do not add explanations.',\n 'Keep placeholder tokens like __I18N_PH_0__ unchanged.',\n 'Preserve surrounding punctuation and semantic meaning.',\n 'Apply glossary terms strictly when provided.',\n ].join(' ');\n}\n\nfunction buildPayload(request: TranslationBatchRequest): Record<string, unknown> {\n return {\n sourceLanguage: request.sourceLanguage,\n targetLanguage: request.targetLanguage,\n glossaryTerms: request.glossaryTerms,\n rules: request.rules,\n items: request.items.map((item) => ({\n key: item.key,\n value: item.maskedValue,\n placeholders: item.placeholders.map((placeholder) => placeholder.placeholder),\n })),\n };\n}\n","import { AIProvider, SupportedProvider, TranslationBatchRequest, TranslationBatchResponse } from '../types.js';\n\nexport abstract class BaseAIProvider implements AIProvider {\n abstract getName(): SupportedProvider;\n abstract translateBatch(\n request: TranslationBatchRequest\n ): Promise<TranslationBatchResponse>;\n}\n","import fs from 'fs';\nimport path from 'path';\nimport { pathToFileURL } from 'url';\nimport dotenv from 'dotenv';\nimport { GlossaryManager } from '../glossary/glossaryManager.js';\nimport {\n LanguageConfig,\n TranslationMode,\n TranslatorConfig,\n TranslatorConfigOverrides,\n} from '../types.js';\n\nconst DEFAULT_CONFIG_FILES = [\n 'i18n-ai.config.json',\n 'i18n-ai.config.mjs',\n 'i18n-ai.config.js',\n 'ui5-ai-i18n.config.json',\n 'ui5-ai-i18n.config.mjs',\n 'ui5-ai-i18n.config.js',\n];\n\nconst TOP_LEVEL_CONFIG_KEYS = [\n 'sourceLanguage',\n 'targetLanguages',\n 'translationMode',\n 'encodeUnicode',\n 'languageOptions',\n 'provider',\n 'providerOptions',\n 'files',\n 'glossary',\n 'cache',\n 'rules',\n 'batchSize',\n 'verbose',\n] as const;\n\nconst PROVIDER_OPTIONS_KEYS = [\n 'apiKey',\n 'model',\n 'baseURL',\n 'organization',\n 'temperature',\n 'maxOutputTokens',\n] as const;\n\nconst FILE_CONFIG_KEYS = ['input', 'outputDir', 'languageFilePattern'] as const;\nconst CACHE_CONFIG_KEYS = ['enabled', 'ttlMs', 'dir'] as const;\nconst LANGUAGE_CONFIG_KEYS = ['encodeUnicode'] as const;\n\nexport async function loadTranslatorConfig(options?: {\n configPath?: string;\n cwd?: string;\n overrides?: TranslatorConfigOverrides;\n}): Promise<TranslatorConfig> {\n const cwd = options?.cwd ?? process.cwd();\n loadEnvFiles(cwd);\n const configPath = options?.configPath\n ? path.resolve(cwd, options.configPath)\n : findDefaultConfigPath(cwd);\n\n const loaded = configPath ? await loadConfigFile(configPath) : {};\n if (configPath) {\n validateLoadedConfigShape(loaded, configPath);\n }\n const merged = applyOverrides(\n applyEnvDefaults({\n sourceLanguage: 'en',\n targetLanguages: [],\n translationMode: 'missing',\n encodeUnicode: false,\n provider: 'openai',\n batchSize: 20,\n verbose: false,\n files: {\n input: 'i18n/i18n.properties',\n },\n cache: {\n enabled: true,\n },\n rules: [],\n ...loaded,\n }),\n options?.overrides\n );\n\n if (merged.glossary) {\n merged.glossary = GlossaryManager.loadGlossary(merged.glossary);\n }\n\n validateConfig(merged);\n return merged;\n}\n\nexport function validateConfig(config: TranslatorConfig): void {\n if (!config.targetLanguages || config.targetLanguages.length === 0) {\n throw new Error('At least one target language is required');\n }\n\n if (config.provider !== 'openai') {\n throw new Error(`Unsupported provider: ${config.provider}`);\n }\n\n if (!config.files?.input) {\n throw new Error('files.input is required');\n }\n\n const mode = config.translationMode ?? 'missing';\n if (mode !== 'missing' && mode !== 'overwrite') {\n throw new Error(`Unsupported translationMode: ${mode}`);\n }\n\n if (config.batchSize !== undefined && config.batchSize < 1) {\n throw new Error('batchSize must be at least 1');\n }\n\n if (!config.providerOptions?.apiKey) {\n throw new Error('OpenAI API key is required via config.providerOptions.apiKey or OPENAI_API_KEY');\n }\n}\n\nexport function createDefaultConfig(\n apiKey: string,\n targetLanguages: string[]\n): TranslatorConfig {\n return {\n sourceLanguage: 'en',\n targetLanguages,\n translationMode: 'missing',\n encodeUnicode: false,\n provider: 'openai',\n providerOptions: {\n apiKey,\n model: 'gpt-4.1-mini',\n temperature: 0,\n maxOutputTokens: 4000,\n },\n files: {\n input: 'i18n/i18n.properties',\n },\n cache: {\n enabled: true,\n },\n rules: [],\n batchSize: 20,\n verbose: false,\n };\n}\n\nfunction applyEnvDefaults(config: TranslatorConfig): TranslatorConfig {\n const providerOptions = {\n ...config.providerOptions,\n };\n\n if (!providerOptions.apiKey && process.env.OPENAI_API_KEY) {\n providerOptions.apiKey = process.env.OPENAI_API_KEY;\n }\n\n if (!providerOptions.model && process.env.OPENAI_MODEL) {\n providerOptions.model = process.env.OPENAI_MODEL;\n }\n\n return {\n ...config,\n providerOptions,\n };\n}\n\nfunction applyOverrides(\n config: TranslatorConfig,\n overrides?: TranslatorConfigOverrides\n): TranslatorConfig {\n if (!overrides) {\n return config;\n }\n\n return {\n ...config,\n provider: overrides.provider ?? config.provider,\n translationMode: overrides.translationMode ?? config.translationMode,\n encodeUnicode: overrides.encodeUnicode ?? config.encodeUnicode,\n languageOptions: config.languageOptions,\n targetLanguages: overrides.targetLanguages ?? config.targetLanguages,\n verbose: overrides.verbose ?? config.verbose,\n files: {\n ...config.files,\n input: overrides.input ?? config.files?.input,\n },\n providerOptions: {\n ...config.providerOptions,\n model: overrides.model ?? config.providerOptions?.model,\n },\n };\n}\n\nfunction findDefaultConfigPath(cwd: string): string | undefined {\n for (const fileName of DEFAULT_CONFIG_FILES) {\n const candidate = path.join(cwd, fileName);\n if (fs.existsSync(candidate)) {\n return candidate;\n }\n }\n\n return undefined;\n}\n\nasync function loadConfigFile(configPath: string): Promise<Partial<TranslatorConfig>> {\n if (!fs.existsSync(configPath)) {\n throw new Error(`Config file not found: ${configPath}`);\n }\n\n if (configPath.endsWith('.json')) {\n return JSON.parse(fs.readFileSync(configPath, 'utf-8')) as Partial<TranslatorConfig>;\n }\n\n if (configPath.endsWith('.js') || configPath.endsWith('.mjs')) {\n const loaded = await import(pathToFileURL(configPath).href);\n return (loaded.default ?? loaded) as Partial<TranslatorConfig>;\n }\n\n throw new Error(`Unsupported config format: ${configPath}`);\n}\n\nfunction validateLoadedConfigShape(\n config: Partial<TranslatorConfig>,\n configPath: string\n): void {\n assertPlainObject(config, 'Config file', configPath);\n validateAllowedKeys(config, TOP_LEVEL_CONFIG_KEYS, 'Config file', configPath);\n\n if (config.providerOptions !== undefined) {\n assertPlainObject(config.providerOptions, 'config.providerOptions', configPath);\n validateAllowedKeys(\n config.providerOptions,\n PROVIDER_OPTIONS_KEYS,\n 'config.providerOptions',\n configPath\n );\n }\n\n if (config.files !== undefined) {\n assertPlainObject(config.files, 'config.files', configPath);\n validateAllowedKeys(config.files, FILE_CONFIG_KEYS, 'config.files', configPath);\n }\n\n if (config.cache !== undefined) {\n assertPlainObject(config.cache, 'config.cache', configPath);\n validateAllowedKeys(config.cache, CACHE_CONFIG_KEYS, 'config.cache', configPath);\n }\n\n if (config.languageOptions !== undefined) {\n assertPlainObject(config.languageOptions, 'config.languageOptions', configPath);\n for (const [language, value] of Object.entries(config.languageOptions)) {\n assertPlainObject(value, `config.languageOptions.${language}`, configPath);\n validateAllowedKeys(\n value as LanguageConfig,\n LANGUAGE_CONFIG_KEYS,\n `config.languageOptions.${language}`,\n configPath\n );\n }\n }\n}\n\nfunction validateAllowedKeys(\n value: object,\n allowedKeys: readonly string[],\n pathLabel: string,\n configPath: string\n): void {\n for (const key of Object.keys(value)) {\n if (!allowedKeys.includes(key)) {\n throw new Error(\n `Unknown option \"${key}\" in ${pathLabel} of ${configPath}.`\n );\n }\n }\n}\n\nfunction assertPlainObject(value: unknown, pathLabel: string, configPath: string): void {\n if (!value || typeof value !== 'object' || Array.isArray(value)) {\n throw new Error(`${pathLabel} in ${configPath} must be an object.`);\n }\n}\n\nexport function resolveTranslationMode(\n preferred?: TranslationMode,\n fallback: TranslationMode = 'missing'\n): TranslationMode {\n return preferred ?? fallback;\n}\n\nfunction loadEnvFiles(cwd: string): void {\n const envPath = path.join(cwd, '.env');\n const envLocalPath = path.join(cwd, '.env.local');\n\n if (fs.existsSync(envPath)) {\n dotenv.config({ path: envPath });\n }\n\n if (fs.existsSync(envLocalPath)) {\n dotenv.config({ path: envLocalPath, override: true });\n }\n}\n","/**\n * Logging utility for debug output\n */\n\nimport { Logger } from '../types.js';\n\n/**\n * Default logger implementation\n * Uses console for output, respects DEBUG environment variable\n */\nexport class ConsoleLogger implements Logger {\n private debug_enabled: boolean;\n private prefix: string;\n\n constructor(prefix: string = '@concircle/i18n-ai-translator', debug?: boolean) {\n this.prefix = prefix;\n this.debug_enabled =\n debug ??\n (typeof process !== 'undefined' &&\n process.env.DEBUG?.includes('i18n-ai-translator')) ??\n false;\n }\n\n debug(message: string, data?: Record<string, unknown>): void {\n if (this.debug_enabled) {\n console.debug(`[${this.prefix}:DEBUG] ${message}`, data || '');\n }\n }\n\n info(message: string, data?: Record<string, unknown>): void {\n console.info(`[${this.prefix}:INFO] ${message}`, data || '');\n }\n\n warn(message: string, data?: Record<string, unknown>): void {\n console.warn(`[${this.prefix}:WARN] ${message}`, data || '');\n }\n\n error(message: string, error?: Error): void {\n console.error(`[${this.prefix}:ERROR] ${message}`, error?.message || '');\n if (error?.stack && this.debug_enabled) {\n console.error(error.stack);\n }\n }\n\n setDebug(enabled: boolean): void {\n this.debug_enabled = enabled;\n }\n\n isDebugEnabled(): boolean {\n return this.debug_enabled;\n }\n}\n\n/**\n * No-op logger (for silent mode)\n */\nexport class SilentLogger implements Logger {\n debug(_message: string, _data?: Record<string, unknown>): void {\n // no-op\n }\n\n info(_message: string, _data?: Record<string, unknown>): void {\n // no-op\n }\n\n warn(_message: string, _data?: Record<string, unknown>): void {\n // no-op\n }\n\n error(_message: string, _error?: Error): void {\n // no-op\n }\n}\n\n/**\n * Create a logger instance\n *\n * @param debug Enable debug output\n * @param silent Enable silent mode (no output)\n * @returns Logger instance\n */\nexport function createLogger(debug: boolean = false, silent: boolean = false): Logger {\n if (silent) {\n return new SilentLogger();\n }\n\n return new ConsoleLogger('@concircle/i18n-ai-translator', debug);\n}\n"],"mappings":"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","names":["path","crypto","fs","path","FileCache","options","homeDir","cacheKey","filePath","entry","translation","fileName","entriesCount","input","json","fs","GlossaryManager","_GlossaryManager","glossary","source","filePath","content","parsed","typed","language","terms","label","term","PLACEHOLDER_PATTERN","PROTECTED_SEQUENCE_PATTERN","MASKABLE_PATTERN","extractPlaceholders","text","match","hasPlaceholders","maskPlaceholders","tokens","index","masked","placeholder","token","restorePlaceholders","current","validatePlaceholderIntegrity","sourceText","translatedText","expected","actual","fs","path","parsePropertiesDocument","content","eol","hasTrailingNewline","rawLines","parseLine","readPropertiesDocument","filePath","createEmptyPropertiesDocument","listPropertiesEntries","document","entries","line","cloneDocument","upsertPropertiesValue","key","value","existing","findEntryLine","serializePropertiesDocument","options","body","serializeLine","writePropertiesDocument","dir","createDocumentFromEntries","getLanguageFilePath","sourceFilePath","language","pattern","outputDir","sourceDir","ext","baseName","fileName","raw","leadingWhitespace","separatorIndex","separator","valueStartIndex","index","char","lookahead","rawKey","rawValue","decodePropertiesToken","escapePropertiesToken","escapePropertiesValue","_","hex","escaped","OpenAI","BaseAIProvider","TRANSLATION_SCHEMA","OpenAIProvider","BaseAIProvider","options","OpenAI","request","rawResponse","buildSystemPrompt","buildPayload","parsed","item","placeholder","fs","path","pathToFileURL","dotenv","DEFAULT_CONFIG_FILES","TOP_LEVEL_CONFIG_KEYS","PROVIDER_OPTIONS_KEYS","FILE_CONFIG_KEYS","CACHE_CONFIG_KEYS","LANGUAGE_CONFIG_KEYS","loadTranslatorConfig","options","cwd","loadEnvFiles","configPath","path","findDefaultConfigPath","loaded","loadConfigFile","validateLoadedConfigShape","merged","applyOverrides","applyEnvDefaults","GlossaryManager","validateConfig","config","mode","createDefaultConfig","apiKey","targetLanguages","providerOptions","overrides","fileName","candidate","fs","pathToFileURL","assertPlainObject","validateAllowedKeys","language","value","allowedKeys","pathLabel","key","resolveTranslationMode","preferred","fallback","envPath","envLocalPath","dotenv","ConsoleLogger","prefix","debug","message","data","error","enabled","SilentLogger","_message","_data","_error","createLogger","silent","Translator","_Translator","config","validateConfig","createProvider","GlossaryManager","FileCache","ConsoleLogger","SilentLogger","options","loadTranslatorConfig","inputPath","path","sourceDocument","readPropertiesDocument","sourceEntries","listPropertiesEntries","sourceKeys","languages","translationMode","resolveTranslationMode","result","language","targetPath","getLanguageFilePath","targetDocument","targetEntries","keysToTranslate","key","existingValue","translatedMap","outputDocument","cloneDocument","translatedValue","upsertPropertiesValue","errors","writePropertiesDocument","keys","glossaryTerms","rules","batchSize","start","batchKeys","uncachedItems","sourceValue","placeholderInfo","maskPlaceholders","cacheKey","cached","response","item","translated","findResponseItem","restored","restorePlaceholders","validation","validatePlaceholderIntegrity","translateProject","translateFile","OpenAIProvider","items","candidate"]}
@@ -0,0 +1,151 @@
1
+ type SupportedProvider = 'openai';
2
+ type TranslationMode = 'missing' | 'overwrite';
3
+ interface PlaceholderToken {
4
+ token: string;
5
+ placeholder: string;
6
+ }
7
+ interface PlaceholderInfo {
8
+ original: string;
9
+ masked: string;
10
+ tokens: PlaceholderToken[];
11
+ }
12
+ interface GlossaryTerm {
13
+ source: string;
14
+ target?: string;
15
+ context?: string;
16
+ doNotTranslate?: boolean;
17
+ }
18
+ interface GlossaryConfig {
19
+ shared?: GlossaryTerm[];
20
+ languages?: Record<string, GlossaryTerm[]>;
21
+ }
22
+ interface CacheOptions {
23
+ enabled?: boolean;
24
+ ttlMs?: number;
25
+ dir?: string;
26
+ }
27
+ interface OpenAIProviderOptions {
28
+ apiKey?: string;
29
+ model?: string;
30
+ baseURL?: string;
31
+ organization?: string;
32
+ temperature?: number;
33
+ maxOutputTokens?: number;
34
+ }
35
+ interface FileConfig {
36
+ input?: string;
37
+ outputDir?: string;
38
+ languageFilePattern?: string;
39
+ }
40
+ interface LanguageConfig {
41
+ encodeUnicode?: boolean;
42
+ }
43
+ interface TranslatorConfig {
44
+ sourceLanguage?: string;
45
+ targetLanguages: string[];
46
+ translationMode?: TranslationMode;
47
+ encodeUnicode?: boolean;
48
+ languageOptions?: Record<string, LanguageConfig>;
49
+ provider?: SupportedProvider;
50
+ providerOptions?: OpenAIProviderOptions;
51
+ files?: FileConfig;
52
+ glossary?: GlossaryConfig | string;
53
+ cache?: CacheOptions;
54
+ rules?: string[];
55
+ batchSize?: number;
56
+ verbose?: boolean;
57
+ }
58
+ interface TranslatorConfigOverrides {
59
+ targetLanguages?: string[];
60
+ translationMode?: TranslationMode;
61
+ encodeUnicode?: boolean;
62
+ provider?: SupportedProvider;
63
+ input?: string;
64
+ verbose?: boolean;
65
+ model?: string;
66
+ }
67
+ interface TranslationInputItem {
68
+ key: string;
69
+ sourceValue: string;
70
+ maskedValue: string;
71
+ placeholders: PlaceholderToken[];
72
+ }
73
+ interface TranslationBatchRequest {
74
+ sourceLanguage: string;
75
+ targetLanguage: string;
76
+ items: TranslationInputItem[];
77
+ glossaryTerms: GlossaryTerm[];
78
+ rules: string[];
79
+ model?: string;
80
+ }
81
+ interface TranslationBatchResponseItem {
82
+ key: string;
83
+ translatedValue: string;
84
+ }
85
+ interface TranslationBatchResponse {
86
+ provider: SupportedProvider;
87
+ items: TranslationBatchResponseItem[];
88
+ rawResponse?: string;
89
+ }
90
+ interface AIProvider {
91
+ getName(): SupportedProvider;
92
+ translateBatch(request: TranslationBatchRequest): Promise<TranslationBatchResponse>;
93
+ }
94
+ interface TranslationFileOptions {
95
+ inputPath?: string;
96
+ languages?: string[];
97
+ mode?: TranslationMode;
98
+ dryRun?: boolean;
99
+ }
100
+ interface TranslationProjectOptions extends TranslationFileOptions {
101
+ configPath?: string;
102
+ cwd?: string;
103
+ encodeUnicode?: boolean;
104
+ provider?: SupportedProvider;
105
+ model?: string;
106
+ verbose?: boolean;
107
+ }
108
+ interface PropertiesSerializationOptions {
109
+ encodeUnicode?: boolean;
110
+ }
111
+ interface LanguageTranslationSummary {
112
+ outputFile: string;
113
+ success: boolean;
114
+ translatedKeysCount: number;
115
+ skippedKeysCount: number;
116
+ errors: string[];
117
+ dryRun?: boolean;
118
+ }
119
+ interface TranslationResult {
120
+ sourceFile: string;
121
+ translationMode: TranslationMode;
122
+ translations: Record<string, LanguageTranslationSummary>;
123
+ }
124
+ interface Logger {
125
+ debug(message: string, data?: Record<string, unknown>): void;
126
+ info(message: string, data?: Record<string, unknown>): void;
127
+ warn(message: string, data?: Record<string, unknown>): void;
128
+ error(message: string, error?: Error): void;
129
+ }
130
+ type PropertiesLine = {
131
+ type: 'blank';
132
+ raw: string;
133
+ } | {
134
+ type: 'comment';
135
+ raw: string;
136
+ } | {
137
+ type: 'entry';
138
+ raw: string;
139
+ key: string;
140
+ value: string;
141
+ separator: string;
142
+ leadingWhitespace: string;
143
+ modified: boolean;
144
+ };
145
+ interface PropertiesDocument {
146
+ lines: PropertiesLine[];
147
+ eol: string;
148
+ hasTrailingNewline: boolean;
149
+ }
150
+
151
+ export type { AIProvider as A, CacheOptions as C, FileConfig as F, GlossaryTerm as G, Logger as L, OpenAIProviderOptions as O, PropertiesDocument as P, SupportedProvider as S, TranslationMode as T, TranslatorConfig as a, TranslationProjectOptions as b, TranslationResult as c, TranslationFileOptions as d, TranslatorConfigOverrides as e, GlossaryConfig as f, PropertiesSerializationOptions as g, PlaceholderInfo as h, PlaceholderToken as i, TranslationBatchRequest as j, TranslationBatchResponse as k, LanguageTranslationSummary as l, PropertiesLine as m, TranslationBatchResponseItem as n };
@@ -0,0 +1,151 @@
1
+ type SupportedProvider = 'openai';
2
+ type TranslationMode = 'missing' | 'overwrite';
3
+ interface PlaceholderToken {
4
+ token: string;
5
+ placeholder: string;
6
+ }
7
+ interface PlaceholderInfo {
8
+ original: string;
9
+ masked: string;
10
+ tokens: PlaceholderToken[];
11
+ }
12
+ interface GlossaryTerm {
13
+ source: string;
14
+ target?: string;
15
+ context?: string;
16
+ doNotTranslate?: boolean;
17
+ }
18
+ interface GlossaryConfig {
19
+ shared?: GlossaryTerm[];
20
+ languages?: Record<string, GlossaryTerm[]>;
21
+ }
22
+ interface CacheOptions {
23
+ enabled?: boolean;
24
+ ttlMs?: number;
25
+ dir?: string;
26
+ }
27
+ interface OpenAIProviderOptions {
28
+ apiKey?: string;
29
+ model?: string;
30
+ baseURL?: string;
31
+ organization?: string;
32
+ temperature?: number;
33
+ maxOutputTokens?: number;
34
+ }
35
+ interface FileConfig {
36
+ input?: string;
37
+ outputDir?: string;
38
+ languageFilePattern?: string;
39
+ }
40
+ interface LanguageConfig {
41
+ encodeUnicode?: boolean;
42
+ }
43
+ interface TranslatorConfig {
44
+ sourceLanguage?: string;
45
+ targetLanguages: string[];
46
+ translationMode?: TranslationMode;
47
+ encodeUnicode?: boolean;
48
+ languageOptions?: Record<string, LanguageConfig>;
49
+ provider?: SupportedProvider;
50
+ providerOptions?: OpenAIProviderOptions;
51
+ files?: FileConfig;
52
+ glossary?: GlossaryConfig | string;
53
+ cache?: CacheOptions;
54
+ rules?: string[];
55
+ batchSize?: number;
56
+ verbose?: boolean;
57
+ }
58
+ interface TranslatorConfigOverrides {
59
+ targetLanguages?: string[];
60
+ translationMode?: TranslationMode;
61
+ encodeUnicode?: boolean;
62
+ provider?: SupportedProvider;
63
+ input?: string;
64
+ verbose?: boolean;
65
+ model?: string;
66
+ }
67
+ interface TranslationInputItem {
68
+ key: string;
69
+ sourceValue: string;
70
+ maskedValue: string;
71
+ placeholders: PlaceholderToken[];
72
+ }
73
+ interface TranslationBatchRequest {
74
+ sourceLanguage: string;
75
+ targetLanguage: string;
76
+ items: TranslationInputItem[];
77
+ glossaryTerms: GlossaryTerm[];
78
+ rules: string[];
79
+ model?: string;
80
+ }
81
+ interface TranslationBatchResponseItem {
82
+ key: string;
83
+ translatedValue: string;
84
+ }
85
+ interface TranslationBatchResponse {
86
+ provider: SupportedProvider;
87
+ items: TranslationBatchResponseItem[];
88
+ rawResponse?: string;
89
+ }
90
+ interface AIProvider {
91
+ getName(): SupportedProvider;
92
+ translateBatch(request: TranslationBatchRequest): Promise<TranslationBatchResponse>;
93
+ }
94
+ interface TranslationFileOptions {
95
+ inputPath?: string;
96
+ languages?: string[];
97
+ mode?: TranslationMode;
98
+ dryRun?: boolean;
99
+ }
100
+ interface TranslationProjectOptions extends TranslationFileOptions {
101
+ configPath?: string;
102
+ cwd?: string;
103
+ encodeUnicode?: boolean;
104
+ provider?: SupportedProvider;
105
+ model?: string;
106
+ verbose?: boolean;
107
+ }
108
+ interface PropertiesSerializationOptions {
109
+ encodeUnicode?: boolean;
110
+ }
111
+ interface LanguageTranslationSummary {
112
+ outputFile: string;
113
+ success: boolean;
114
+ translatedKeysCount: number;
115
+ skippedKeysCount: number;
116
+ errors: string[];
117
+ dryRun?: boolean;
118
+ }
119
+ interface TranslationResult {
120
+ sourceFile: string;
121
+ translationMode: TranslationMode;
122
+ translations: Record<string, LanguageTranslationSummary>;
123
+ }
124
+ interface Logger {
125
+ debug(message: string, data?: Record<string, unknown>): void;
126
+ info(message: string, data?: Record<string, unknown>): void;
127
+ warn(message: string, data?: Record<string, unknown>): void;
128
+ error(message: string, error?: Error): void;
129
+ }
130
+ type PropertiesLine = {
131
+ type: 'blank';
132
+ raw: string;
133
+ } | {
134
+ type: 'comment';
135
+ raw: string;
136
+ } | {
137
+ type: 'entry';
138
+ raw: string;
139
+ key: string;
140
+ value: string;
141
+ separator: string;
142
+ leadingWhitespace: string;
143
+ modified: boolean;
144
+ };
145
+ interface PropertiesDocument {
146
+ lines: PropertiesLine[];
147
+ eol: string;
148
+ hasTrailingNewline: boolean;
149
+ }
150
+
151
+ export type { AIProvider as A, CacheOptions as C, FileConfig as F, GlossaryTerm as G, Logger as L, OpenAIProviderOptions as O, PropertiesDocument as P, SupportedProvider as S, TranslationMode as T, TranslatorConfig as a, TranslationProjectOptions as b, TranslationResult as c, TranslationFileOptions as d, TranslatorConfigOverrides as e, GlossaryConfig as f, PropertiesSerializationOptions as g, PlaceholderInfo as h, PlaceholderToken as i, TranslationBatchRequest as j, TranslationBatchResponse as k, LanguageTranslationSummary as l, PropertiesLine as m, TranslationBatchResponseItem as n };
package/package.json ADDED
@@ -0,0 +1,76 @@
1
+ {
2
+ "name": "@concircle/i18n-ai-translator",
3
+ "version": "0.1.0",
4
+ "description": "AI-powered i18n translator for UI5 apps. Translates i18n.properties files using OpenAI (extensible for other AI providers).",
5
+ "type": "module",
6
+ "bin": {
7
+ "i18n-ai-translator": "./bin/i18n-ai-translator.mjs"
8
+ },
9
+ "main": "./dist/index.cjs",
10
+ "types": "./dist/index.d.ts",
11
+ "exports": {
12
+ ".": {
13
+ "types": "./dist/index.d.ts",
14
+ "import": "./dist/index.mjs",
15
+ "require": "./dist/index.cjs",
16
+ "default": "./dist/index.mjs"
17
+ },
18
+ "./cli": {
19
+ "import": "./dist/cli.mjs",
20
+ "require": "./dist/cli.cjs",
21
+ "default": "./dist/cli.mjs"
22
+ }
23
+ },
24
+ "files": [
25
+ "dist",
26
+ "bin"
27
+ ],
28
+ "scripts": {
29
+ "build": "node ./node_modules/tsup/dist/cli-default.js",
30
+ "dev": "node ./node_modules/tsx/dist/cli.mjs src/index.ts",
31
+ "test": "node ./node_modules/vitest/vitest.mjs run",
32
+ "test:watch": "node ./node_modules/vitest/vitest.mjs",
33
+ "test:coverage": "node ./node_modules/vitest/vitest.mjs run --coverage",
34
+ "lint": "node ./node_modules/eslint/bin/eslint.js src test --ext .ts",
35
+ "format": "prettier --write 'src/**/*.ts' 'test/**/*.ts' '*.json' '*.md'",
36
+ "format:check": "prettier --check 'src/**/*.ts' 'test/**/*.ts' '*.json' '*.md'",
37
+ "prepublishOnly": "npm run build && npm run test && npm run lint"
38
+ },
39
+ "dependencies": {
40
+ "dotenv": "^16.4.5",
41
+ "openai": "^6.33.0"
42
+ },
43
+ "devDependencies": {
44
+ "@types/node": "^20.11.5",
45
+ "@typescript-eslint/eslint-plugin": "^7.1.0",
46
+ "@typescript-eslint/parser": "^7.1.0",
47
+ "eslint": "^8.56.0",
48
+ "prettier": "^3.2.5",
49
+ "tsup": "^8.5.1",
50
+ "tsx": "^4.21.0",
51
+ "typescript": "^5.4.2",
52
+ "vitest": "^1.3.1"
53
+ },
54
+ "keywords": [
55
+ "i18n",
56
+ "i18n.properties",
57
+ "translation",
58
+ "ui5",
59
+ "openai",
60
+ "ai",
61
+ "internationalization"
62
+ ],
63
+ "author": "Herbert Kaintz",
64
+ "license": "MIT",
65
+ "repository": {
66
+ "type": "git",
67
+ "url": "https://bitbucket.org/concircle/i18n-ai-translator.git"
68
+ },
69
+ "bugs": {
70
+ "url": "https://bitbucket.org/concircle/i18n-ai-translator/issues"
71
+ },
72
+ "homepage": "https://bitbucket.org/concircle/i18n-ai-translator",
73
+ "engines": {
74
+ "node": ">=20.0.0"
75
+ }
76
+ }