React Integration
Integrate LocaleLens with React using react-i18next
This repository demonstrates a minimal i18n setup for React + Vite using LocaleLens. Translations are fetched at runtime via a simple API call — no JSON files, no complex configuration.
Uses react-i18next with a simple runtime loader that fetches translations from LocaleLens.
Why this approach?
- •No static JSON files to maintain
- •Translations update without redeploying
- •Lazy loading of locales reduces initial payload size
- •LocaleLens is the source of truth
Want to customize this setup? The guide below covers a manual setup with react-i18next.
Important: The examples below are intentionally simplified for learning. For production apps, proxy LocaleLens requests through your backend to avoid exposing API keys in client-side code. See the Production Guide for secure setup patterns.
This guide uses react-i18next, the most popular i18n library for React.
npm install react-i18next i18next1. Create i18n Configuration
// i18n.ts
import i18n from 'i18next'
import { initReactI18next } from 'react-i18next'
const PROJECT_ID = import.meta.env.VITE_LOCALELENS_PROJECT_ID
const API_KEY = import.meta.env.VITE_LOCALELENS_API_KEY
const API_URL = 'https://localelens.ai/api/v1'
export async function loadTranslations(locale: string) {
const res = await fetch(
`${API_URL}/projects/${PROJECT_ID}/translations/${locale}`,
{ headers: { 'Authorization': `Bearer ${API_KEY}` } }
)
return res.json()
}
i18n
.use(initReactI18next)
.init({
lng: 'en',
fallbackLng: 'en',
interpolation: { escapeValue: false },
resources: {}
})
// Load default locale
loadTranslations('en').then((translations) => {
i18n.addResourceBundle('en', 'translation', translations)
})
export default i18n2. Initialize in App
The default locale is preloaded when i18n.ts is imported. Suspense is useful when lazy-loading additional locales later.
// App.tsx
import './i18n'
import { Suspense } from 'react'
function App() {
return (
<Suspense fallback="Loading...">
<YourApp />
</Suspense>
)
}3. Environment Variables
Add to .env (Vite example):
VITE_LOCALELENS_PROJECT_ID=your_project_id
VITE_LOCALELENS_API_KEY=ll_your_api_keyUsing Hooks
import { useTranslation } from 'react-i18next'
function MyComponent() {
const { t } = useTranslation()
return (
<div>
<h1>{t('common.welcome')}</h1>
<button>{t('common.save')}</button>
</div>
)
}Language Switching
import { useTranslation } from 'react-i18next'
// Reuse the loadTranslations function from i18n.ts
import { loadTranslations } from './i18n'
function LanguageSwitcher() {
const { i18n } = useTranslation()
async function changeLanguage(locale: string) {
// Load translations if not already loaded
if (!i18n.hasResourceBundle(locale, 'translation')) {
const translations = await loadTranslations(locale)
i18n.addResourceBundle(locale, 'translation', translations)
}
i18n.changeLanguage(locale)
}
return (
<select onChange={(e) => changeLanguage(e.target.value)}>
<option value="en">English</option>
<option value="es">Spanish</option>
<option value="fr">French</option>
</select>
)
}- •Never expose API keys in client-side code for production apps — see Production Guide
- •Use a backend proxy to keep API keys secure
- •Load translations on-demand to reduce initial payload size
- •Cache loaded translations in memory to avoid redundant API calls
- •Use flat structure for simpler key access (
common.save)