@molecule/api-ai-translation-llm
Provider bond · ai-translation · API (Node) · v1.1.1 · Apache-2.0
Language-model translation provider for molecule.dev — translates through any @molecule/api-ai model, hosted or self-hosted
npm install @molecule/api-ai-translation-llmnpm · Source on GitHub · Implements @molecule/api-ai-translation
How it works
@molecule/api-ai-translation-llm is a provider bond on the API (Node) side: it implements the ai-translation core interface (@molecule/api-ai-translation) with a concrete vendor or library behind it.
Your code calls the core; you wire this provider once at startup. Swapping vendors later is one line in that wiring, not a rewrite.
import { setProvider as setAi } from '@molecule/api-ai'
import { provider as deepseek } from '@molecule/api-ai-deepseek'
import { setProvider, requireProvider } from '@molecule/api-ai-translation'
import { createProvider } from '@molecule/api-ai-translation-llm'
setAi(deepseek) // the app's AI provider
setProvider(createProvider()) // translate with it — or createProvider({ ai, model }) for another
const { translations } = await requireProvider().translate({
text: ['{{count}} items in your cart', 'Checkout'],
targetLang: 'de',
protect: ['{{count}}'],
context: 'Labels in an online store',
})Works with: @molecule/api-ai, @molecule/api-ai-translation
Reference
Language-model translation provider for molecule.dev.
Implements the @molecule/api-ai-translation contract with any
@molecule/api-ai provider, so translation can run on whichever model is
cheapest or best for the job — a hosted model (DeepSeek, Qwen, GPT, Claude, …)
or a self-hosted one through @molecule/api-ai-local (vLLM, Ollama,
llama.cpp), with no translation-vendor account at all.
Auto-generated, AI-first package reference for the molecule.dev ecosystem. It is written to be read by coding agents as much as by people, and is generated from this package's source — edit
src/index.tsJSDoc, not this file.
Quick Start
import { setProvider as setAi } from '@molecule/api-ai'
import { provider as deepseek } from '@molecule/api-ai-deepseek'
import { setProvider, requireProvider } from '@molecule/api-ai-translation'
import { createProvider } from '@molecule/api-ai-translation-llm'
setAi(deepseek) // the app's AI provider
setProvider(createProvider()) // translate with it — or createProvider({ ai, model }) for another
const { translations } = await requireProvider().translate({
text: ['{{count}} items in your cart', 'Checkout'],
targetLang: 'de',
protect: ['{{count}}'],
context: 'Labels in an online store',
})
Type
provider
Installation
npm install @molecule/api-ai-translation-llm @molecule/api-ai @molecule/api-ai-translation
API
Interfaces
MaskedText
A text prepared for the model, plus what is needed to restore it.
interface MaskedText {
/** The text with each protected substring replaced by `⟦N⟧`. */
masked: string
/** The protected substrings, in order of occurrence. */
originals: string[]
}
LlmTranslationConfig
Configuration for the language-model translation provider.
interface LlmTranslationConfig {
/**
* The `@molecule/api-ai` provider to translate with. Defaults to the provider
* bonded as `ai` (`requireProvider()` from `@molecule/api-ai`) at first use —
* pass one explicitly to translate with a different model than the app chats
* with (e.g. a cheap model, or a self-hosted one via `@molecule/api-ai-local`).
*/
ai?: AIProvider
/** Model id passed to the AI provider. Defaults to the provider's own default model. */
model?: string
/** Texts per model request. Defaults to 40 — larger batches are cheaper but fail more often. */
batchSize?: number
/** Sampling temperature. Defaults to 0 (translation should be deterministic). */
temperature?: number
/**
* Extra instructions appended to the system prompt — a style guide, a glossary
* ("translate 'workspace' as 'Arbeitsbereich'"), or product context.
*/
instructions?: string
}
Functions
languageName(code)
English name of a language code, falling back to the code itself.
Accepts provider-flavored codes in any case (de, DE, PT-BR, zh-TW, ZH-HANT).
function languageName(code: string): string
code— A BCP-47-style language code.
Returns: The language's English name, e.g. 'German' or 'Chinese (Taiwan)'.
systemPrompt(targetLang, sourceLang, context, instructions)
Builds the system prompt for one target language.
function systemPrompt(
targetLang: string,
sourceLang: string | undefined,
context: string | undefined,
instructions: string | undefined,
): string
targetLang— Target language code.sourceLang— Source language code, if known.context— Caller-supplied context about the texts (not translated).instructions— Extra standing instructions from the provider config.
Returns: The system prompt.
parseTranslations(answer, expected)
Parses the model's answer into an array of strings.
function parseTranslations(answer: string, expected: number): string[] | null
answer— The model's full text output.expected— How many translations the request carried.
Returns: The translations, or null when the answer is not a same-length string array.
maskText(text, protect)
Replaces each protected substring (longest first) with a numbered token.
function maskText(text: string, protect: string[]): MaskedText
text— The caller's text.protect— Literal substrings to protect.
Returns: The masked text and its protected substrings.
tokensIntact(translated, originals)
Whether a translation carries every token of its source exactly once and no others.
function tokensIntact(translated: string, originals: string[]): boolean
translated— The model's output for one text.originals— The substringsmaskTextprotected for that text.
Returns: True when every ⟦N⟧ is present once and none is extra.
unmaskText(translated, originals)
Restores protected substrings by index.
function unmaskText(translated: string, originals: string[]): string
translated— The model's output for one text.originals— The substringsmaskTextprotected for that text.
Returns: The restored text.
createProvider(config)
Creates a language-model translation provider.
function createProvider(config?: LlmTranslationConfig): AITranslationProvider
config— Which AI provider/model to use and how to batch.
Returns: An AITranslationProvider backed by a language model.
Constants
provider
The provider implementation.
const provider: AITranslationProvider
Core Interface
Implements @molecule/api-ai-translation interface.
Bond Wiring
Setup function to register this provider with the core interface:
import { setProvider } from '@molecule/api-ai-translation'
import { provider } from '@molecule/api-ai-translation-llm'
export function setupAiTranslationLlm(): void {
setProvider(provider)
}
Injection Notes
Requirements
Peer dependencies:
@molecule/api-ai>=1.0.0@molecule/api-ai-translation>=1.1.0
Runtime Dependencies
-
@molecule/api-ai -
@molecule/api-ai-translation -
Uses the AI provider bonded as
aiunless you pass one. To translate with a cheaper model than the app chats with, passcreateProvider({ ai, model }). Spend is the AI provider's token spend —getUsage()only counts characters in this process (characterLimitisInfinity). -
protectplaceholders travel as numbered tokens (⟦0⟧) and are restored by index; the model may move them for word order. A batch whose answer is not a same-length JSON array, or that loses a token, is retried in halves down to one text. A text that still fails comes back WITHOUT its placeholders intact — so check every result holds its placeholders before storing it; never store the English source as a "translation" instead. -
Language codes are free-form (
de,DE,PT-BR,zh-TW): they are turned into English language names for the model.getSupportedLanguages()lists ~80 common languages for pickers; the model can do more. -
context(e.g. "button labels in a banking app") and the config'sinstructions(a glossary or style guide) go into the prompt — use them: one-word UI strings are ambiguous ("Close" as a verb or an adjective), and context is the only fix. -
formality,glossaryId,modelType,preserveFormattingandtagHandlinghave no model equivalent and are ignored. -
Output quality depends on the model. Small models are weaker on low-resource languages (Yoruba, Lao, Zulu…); spot-check those.
E2E Tests
Integration checklist — drive the real UI (live preview, no mocks), adapt each item to this app's actual screens/flows, and check every box off one by one. A box you can't check is an integration bug to fix — not a skip. The sandbox has a live AI provider, so translations run for real; output is non-deterministic, so assert on the resulting LANGUAGE/meaning, never an exact string:
- Translating real text to a target language through the UI returns text ACTUALLY in that language — English→Spanish produces recognizably Spanish, not the original echoed back or left in English.
- Switching the target language (from the picker populated by getSupportedLanguages('target')) changes the output language for the same input — the same source re-translates into the newly chosen language.
- With sourceLang omitted the provider auto-detects: a known-language input comes back with the correct detectedSourceLang, and if the UI shows a detected-language label it names the right one.
- Text already in the target language is left sensible — unchanged or a valid paraphrase, never mangled, doubled, or emptied.
- Empty or untranslatable input (whitespace, emoji, a bare code snippet) is handled gracefully — a clear UI state, nothing crashes.
- A provider failure (bad key, quota exhausted, network drop) surfaces a visible error in the UI, not an unhandled 500 or a silently blank result.
- The translate call runs server-side only — the provider key never reaches the browser (check the network panel: no key in any request).