Skip to main content

React Integration

Integrate LocaleLens with React using react-i18next

React + Vite — Minimal i18n Example
Recommended for new projects

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.

→ View the example on GitHub

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.

Installation

This guide uses react-i18next, the most popular i18n library for React.

npm install react-i18next i18next
Setup

1. 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 i18n

2. 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_key
Usage

Using 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>
  )
}
Best Practices
  • 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)