Just-In-Time Web Localization Engine. tstlai (TypeScript Translation AI) is a middleware-ready library that automatically translates your web application's HTML content on the fly using AI. Unlike traditional i18n libraries that require maintaining massive JSON files, tstlai intercepts your HTML, extracts the text, and uses AI to provide context-aware translations.
Live Demo: This very site uses tstlai! Change your browser language or visit /es, /fr, /de, or any locale to see it in action. Read how we built it →
No manual translation files. Content is translated as it is rendered.
Intelligent parsing ensures your HTML structure, classes, and attributes remain untouched.
SHA-256 content hashing with memory or Redis caching for production-grade performance.
Pluggable AI providers (OpenAI) for high-quality, context-aware translations.
Automatically detects Right-to-Left languages and sets the dir="rtl" attribute.
Respects data-no-translate attributes and ignores script, style, and code tags.
Watch translations appear one-by-one as they complete from the LLM with streaming support.
Tiered language support with quality indicators. Use isLanguageSupported() to validate locales.
Generate static translation files at build time. Translate once, deploy everywhere.
Disambiguate words like "Save", "Post", "Match" with context hints for accurate translations.
Dynamic endpoints with cryptographic tokens prevent API abuse. Each session gets a unique, unguessable path.
tstlai uses a multi-stage workflow to translate your HTML content:
The engine parses incoming HTML and extracts text nodes. It automatically ignores non-content tags like script, style, and code.
Each text segment is hashed using SHA-256. The system checks the cache for existing translations to save costs and reduce latency.
Cache misses are batched and sent to the AI provider in a single request for efficient translation.
The translated text is injected back into the DOM, preserving the original layout perfectly.
npm install tstlaiimport { Tstlai } from 'tstlai';
async function main() {
// 1. Initialize the Engine
const translator = new Tstlai({
targetLang: 'es', // Target language code
provider: {
type: 'openai',
// apiKey, model, and baseUrl can be omitted if set via env vars:
// OPENAI_API_KEY, OPENAI_MODEL, OPENAI_BASE_URL
},
cache: {
type: 'memory',
ttl: 3600 // Cache duration in seconds
}
});
// 2. Your raw HTML content
const rawHtml = `
<article>
<h1>Welcome to the Future</h1>
<p>Translate your website instantly.</p>
<button data-no-translate>tstlai v1.0</button>
</article>
`;
// 3. Process the HTML
const result = await translator.process(rawHtml);
console.log(result.html);
}
main();<article>
<h1>Bienvenido al Futuro</h1>
<p>Traduce tu sitio web al instante.</p>
<button data-no-translate>tstlai v1.0</button>
</article>tstlai provides first-class integrations for popular frameworks:
import express from 'express';
import { Tstlai, integrations } from 'tstlai';
const app = express();
const translator = new Tstlai({
targetLang: 'es',
provider: { type: 'openai' }
});
// Apply middleware
app.use(integrations.createExpressMiddleware(translator));
app.get('/', (req, res) => {
res.send('<h1>Hello World</h1>');
// Automatically translated to <h1>Hola Mundo</h1>
});Drop AutoTranslate into your layout and your entire app translates automatically. No changes to page components needed.
// src/lib/translator.ts
import { Tstlai } from 'tstlai';
const translators = new Map<string, Tstlai>();
export function getTranslator(locale: string): Tstlai {
if (!translators.has(locale)) {
translators.set(locale, new Tstlai({
targetLang: locale,
sourceLang: 'en',
provider: { type: 'openai' },
cache: { type: 'redis' } // or 'memory'
}));
}
return translators.get(locale)!;
}
// src/app/api/tstlai/translate/route.ts
import { createNextRouteHandler } from 'tstlai/next';
import { getTranslator } from '@/lib/translator';
export const POST = async (req: Request) => {
const { targetLang } = await req.json();
const handler = createNextRouteHandler(getTranslator(targetLang));
return handler(new Request(req.url, {
method: 'POST',
headers: req.headers,
body: JSON.stringify({ targetLang }),
}));
};
// src/app/[locale]/layout.tsx
import { AutoTranslate } from 'tstlai/next';
export default async function LocaleLayout({ children, params }) {
const { locale } = await params;
return (
<>
{children}
{locale !== 'en' && <AutoTranslate targetLang={locale} />}
</>
);
}
// That's it! Your pages stay exactly as they are.
// English renders normally, other locales auto-translate.Zero-effort migration from next-intl. Use your existing en.json as the source of truth.
// src/i18n.ts
import { Tstlai, integrations } from 'tstlai';
import enMessages from '../messages/en.json';
const translator = new Tstlai({
targetLang: 'en',
provider: { type: 'openai' }
});
export const { getTranslations } = integrations.createNextIntlAdapter(
translator,
enMessages
);
// In Server Components (just like next-intl):
import { getTranslations } from '@/i18n';
export default async function Page({ params: { locale } }) {
const t = await getTranslations(locale);
return <h1>{t('landing.hero.title')}</h1>;
}Plugin-based integration with createFastifyPlugin()
Middleware support with createAstroMiddleware()
Handler wrapper with createRemixHandler()
Zero-config with AutoTranslate - scans DOM and translates automatically
const translator = new Tstlai({
// ...
cache: {
type: 'redis',
connectionString: 'redis://localhost:6379',
ttl: 86400, // 24 hours
keyPrefix: 'tstlai:' // Default namespace
}
});
// Multi-language cache keys: tstlai:<hash>:<lang>
// Translations for different languages are stored separatelyOPENAI_API_KEYYour OpenAI API keyOPENAI_MODELDefault: gpt-3.5-turboOPENAI_BASE_URLDefault: https://api.openai.com/v1REDIS_URLDefault: redis://localhost:6379Add the data-no-translate attribute to prevent specific elements from being translated:
<span data-no-translate>BrandName™</span>Generate static translation files at build time and disambiguate context-sensitive words.
Create translation files from your source JSON at build time. Translate once, deploy everywhere.
# Generate Spanish translations from English source
npx tstlai generate --source messages/en.json --target es --output messages/es.json
# Generate multiple locales
npx tstlai generate --source messages/en.json --target es,fr,de --output messages/Disambiguate words like "Save", "Post", "Match" that have different meanings in different contexts.
// In your messages/en.json
{
"actions": {
"save": "$t/Save/$ctx/button to store data",
"post": "$t/Post/$ctx/publish content to feed"
},
"sports": {
"match": "$t/Match/$ctx/sports competition game"
}
}
// Or use the translateText() API with context
await translator.translateText("Save", { context: "button to store data" });
await translator.translateText("Match", { context: "sports competition" });tstlai exports its language support data for use in middleware and routing.
SUPPORTED_LANGUAGESAll 46 supported languagesTIER_1_LANGUAGES12 high-quality languagesTIER_2_LANGUAGES21 good-quality languagesTIER_3_LANGUAGES13 functional languagesSHORT_CODE_DEFAULTSMap of short codes (en → en_US)isLanguageSupported(code)Check if locale is supportednormalizeLocaleCode(code)Resolve short codes to full localeimport { isLanguageSupported, SHORT_CODE_DEFAULTS } from 'tstlai';
// Use short codes for browser negotiation
const NEGOTIATION_LOCALES = Object.keys(SHORT_CODE_DEFAULTS);
// In your middleware
if (!isLanguageSupported(locale)) {
return NextResponse.redirect(new URL('/en' + pathname, request.url));
}Watch translations appear progressively as each one completes from the LLM.
<AutoTranslate
targetLang={locale}
stream={true}
streamEndpoint="/api/tstlai/stream"
streamBuffer={500} // Buffer first 500ms for smoother initial render
/>// src/app/api/tstlai/stream/route.ts
import { createNextStreamingRouteHandler } from 'tstlai/next';
import { getTranslator } from '@/lib/translator';
export const POST = async (req: Request) => {
const { targetLang } = await req.json();
const handler = createNextStreamingRouteHandler(getTranslator(targetLang));
return handler(req);
};