Back to Blog
Cloudrix Team||10 min read

Building a Multilingual Product: Translation API Integration Guide

Why Build Translation Into Your Product?

There are two approaches to multilingual products: translate everything manually before release, or build translation capabilities directly into your product. The first approach works for static content but fails for dynamic, user-generated, or frequently updated content. The second approach scales.

Building translation into your product means:

  • User-generated content can be translated on the fly
  • New features are automatically available in all languages
  • Content updates propagate to all languages without manual intervention
  • Users can switch languages without waiting for a manual translation cycle

Architecture Patterns

Pattern 1: Translate at Write Time

When content is created or updated, immediately translate it into all supported languages and store the translations alongside the original.

Pros: Fast read performance, no translation latency for end users, works offline Cons: Higher storage costs, translation API calls on every write, all languages must be defined upfront

Best for: Product UI strings, structured content with known language targets

``typescript // Example: Translate at write time async function createArticle(article: Article) { const translations = await Promise.all( SUPPORTED_LANGUAGES.map(async (lang) => ({ language: lang, title: await translateText(article.title, 'en', lang), body: await translateText(article.body, 'en', lang), })) );

await db.articles.create({ ...article, translations, }); } `

Pattern 2: Translate at Read Time

Translate content when a user requests it in a specific language. Cache the translation for subsequent requests.

Pros: Only translates what is actually needed, supports adding languages without re-translating everything Cons: First request in each language has translation latency, requires caching infrastructure

Best for: Large content libraries, user-generated content, applications with many supported languages

`typescript // Example: Translate at read time with caching async function getArticle(id: string, language: string) { // Check cache first const cacheKey = article:${id}:lang:${language}; const cached = await cache.get(cacheKey); if (cached) return JSON.parse(cached);

// Fetch original const article = await db.articles.findById(id);

if (language === article.originalLanguage) { return article; }

// Translate and cache const translated = { ...article, title: await translateText(article.title, article.originalLanguage, language), body: await translateText(article.body, article.originalLanguage, language), };

await cache.set(cacheKey, JSON.stringify(translated), { ttl: 86400 }); return translated; } `

Pattern 3: Hybrid Approach

Translate high-traffic content at write time and low-traffic content at read time.

Pros: Optimizes both performance and cost Cons: More complex implementation, need to classify content by traffic level

Best for: Most production applications

API Integration

Basic API Call

Most translation APIs follow a similar pattern:

`typescript interface TranslationRequest { text: string; sourceLanguage: string; targetLanguage: string; glossaryId?: string; }

interface TranslationResponse { translatedText: string; detectedLanguage?: string; confidence: number; }

async function translateText( text: string, source: string, target: string ): Promise { const response = await fetch('https://api.translateai.com/v1/translate', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': Bearer ${process.env.TRANSLATE_API_KEY}, }, body: JSON.stringify({ text, sourceLanguage: source, targetLanguage: target, glossaryId: 'your-glossary-id', }), });

const data: TranslationResponse = await response.json(); return data.translatedText; } `

Batch Translation

For efficiency, batch multiple strings into a single API call:

`typescript async function translateBatch( texts: string[], source: string, target: string ): Promise { const response = await fetch('https://api.translateai.com/v1/translate/batch', { method: 'POST', headers: { 'Content-Type': 'application/json', 'Authorization': Bearer ${process.env.TRANSLATE_API_KEY}, }, body: JSON.stringify({ texts, sourceLanguage: source, targetLanguage: target, glossaryId: 'your-glossary-id', }), });

const data = await response.json(); return data.translations.map((t: TranslationResponse) => t.translatedText); } `

Error Handling and Retries

Translation API calls can fail due to rate limits, network issues, or service outages. Implement proper error handling:

`typescript async function translateWithRetry( text: string, source: string, target: string, maxRetries: number = 3 ): Promise { for (let attempt = 1; attempt <= maxRetries; attempt++) { try { return await translateText(text, source, target); } catch (error: unknown) { const err = error as { status?: number };

if (err.status === 429) { // Rate limited - wait with exponential backoff const delay = Math.pow(2, attempt) * 1000; await new Promise(resolve => setTimeout(resolve, delay)); continue; }

if (attempt === maxRetries) { // Return original text as fallback console.error(Translation failed after ${maxRetries} attempts, error); return text; } } }

return text; } `

Caching Strategy

Translation results should be cached aggressively. The same English sentence translates to the same French sentence every time (assuming the same glossary version).

Cache Key Design

`typescript function getCacheKey(text: string, source: string, target: string, glossaryVersion: string): string { const hash = createHash('sha256').update(text).digest('hex').substring(0, 16); return trans:${source}:${target}:g${glossaryVersion}:${hash}; } `

Include the glossary version in the cache key so that updating your glossary automatically invalidates cached translations.

Cache Levels

L1: In-memory cache (Node.js Map or LRU cache): For frequently translated strings like UI labels. Sub-millisecond lookup, limited by process memory.

L2: Redis/Memcached: For all translations. Shared across application instances. Sub-millisecond to low-millisecond lookup.

L3: Database: For permanent storage of translations that should survive cache flushes.

Cache Invalidation

Invalidate cached translations when:

  • The source content changes
  • The glossary is updated
  • A human reviewer corrects a translation
  • You upgrade to a new translation model

Handling Dynamic Content

Interpolation Variables

Template strings with variables need special handling:

`typescript // English: "Hello {name}, you have {count} new messages" // The variables {name} and {count} must not be translated

function translateTemplate( template: string, source: string, target: string ): Promise { // Replace variables with placeholders const variables: string[] = []; const cleaned = template.replace(/\{(\w+)\}/g, (match) => { variables.push(match); return __VAR${variables.length - 1}__; });

// Translate the cleaned text const translated = await translateText(cleaned, source, target);

// Restore variables return translated.replace(/__VAR(\d+)__/g, (_, index) => variables[parseInt(index)]); } `

Pluralization

Different languages have different pluralization rules. English has two forms (singular, plural). Russian has three. Arabic has six. Use a library like intl-messageformat to handle pluralization correctly:

`typescript import { IntlMessageFormat } from 'intl-messageformat';

const messages = { en: '{count, plural, one {# item} other {# items}}', fr: '{count, plural, one {# element} other {# elements}}', ru: '{count, plural, one {# элемент} few {# элемента} many {# элементов} other {# элементов}}', };

function formatMessage(language: string, count: number): string { const msg = new IntlMessageFormat(messages[language], language); return msg.format({ count }) as string; } `

Performance Optimization

Pre-translate Common Content

Identify your most-viewed content and pre-translate it during off-peak hours. This ensures the most-accessed pages are always served from cache.

Lazy-Load Translations

For pages with many translatable elements, translate the visible content first and lazy-load translations for below-the-fold content:

`typescript // Translate visible content immediately const visibleTranslations = await translateBatch( visibleStrings, 'en', userLanguage );

// Translate remaining content asynchronously translateBatch(remainingStrings, 'en', userLanguage) .then(translations => updateUI(translations)); ``

Monitor API Usage

Track your translation API usage to optimize costs:

  • Cache hit rate (target above 90%)
  • Average translation latency
  • API calls per minute
  • Cost per language per month

Getting Started with TranslateAI API

TranslateAI provides a REST API with batch translation, custom glossaries, and language detection. Sign up for a free account, get your API key, and start with the code examples above.

The API supports 40+ languages, handles code blocks and formatting automatically, and includes built-in rate limiting with clear response headers. Start with a small integration, measure the impact, and expand from there.

Ready to Translate with Context-Aware AI?

Try TranslateAI free. Upload your content, configure custom glossaries, and get accurate translations in 40+ languages in seconds.

Get Started Free