Skip to content
fluenti

Quick Start (React Router)

  1. Install packages

    Terminal window
    pnpm add @fluenti/core @fluenti/react react-router-dom
    pnpm add -D @fluenti/cli @fluenti/vite-plugin @vitejs/plugin-react
  2. Configure Vite

    vite.config.ts
    import { defineConfig } from 'vite'
    import react from '@vitejs/plugin-react'
    import fluenti from '@fluenti/vite-plugin'
    export default defineConfig({
    plugins: [
    fluenti({ framework: 'react' }),
    react(),
    ],
    })
  3. Create fluenti.config.ts

    fluenti.config.ts
    import { defineConfig } from '@fluenti/cli'
    export default defineConfig({
    sourceLocale: 'en',
    locales: ['en', 'zh-CN', 'ja'],
    catalogDir: './locales',
    format: 'po',
    include: ['./src/**/*.{tsx,ts}'],
    compileOutDir: './src/locales/compiled',
    })
  4. Set up the root with I18nProvider and routing

    src/main.tsx
    import { createRoot } from 'react-dom/client'
    import { BrowserRouter, Routes, Route } from 'react-router-dom'
    import { I18nProvider } from '@fluenti/react'
    import { getDirection } from '@fluenti/core'
    import { useState, useEffect } from 'react'
    import en from './locales/compiled/en'
    import zhCN from './locales/compiled/zh-CN'
    import ja from './locales/compiled/ja'
    import { Home } from './pages/Home'
    const messages = { en, 'zh-CN': zhCN, ja }
    function getInitialLocale(): string {
    const match = document.cookie.match(/(?:^|;\s*)locale=([^;]*)/)
    if (match) return decodeURIComponent(match[1])
    return 'en'
    }
    function Root() {
    const [locale, setLocale] = useState(getInitialLocale)
    useEffect(() => {
    document.documentElement.lang = locale
    document.documentElement.dir = getDirection(locale)
    }, [locale])
    const handleLocaleChange = (loc: string) => {
    document.cookie = `locale=${loc};path=/;max-age=31536000`
    setLocale(loc)
    }
    return (
    <I18nProvider locale={locale} fallbackLocale="en" messages={messages}>
    <BrowserRouter>
    <Routes>
    <Route path="/" element={<Home onLocaleChange={handleLocaleChange} />} />
    </Routes>
    </BrowserRouter>
    </I18nProvider>
    )
    }
    createRoot(document.getElementById('root')!).render(<Root />)
  5. Write a page component

    src/pages/Home.tsx
    export function Home({ onLocaleChange }: { onLocaleChange: (loc: string) => void }) {
    const name = 'World'
    return (
    <div>
    <h1>{t`Welcome to my app`}</h1>
    <p>{t`Hello, ${name}!`}</p>
    <button onClick={() => onLocaleChange('en')}>English</button>
    <button onClick={() => onLocaleChange('zh-CN')}>中文</button>
    <button onClick={() => onLocaleChange('ja')}>日本語</button>
    </div>
    )
    }

    The t`...` tagged template is a compiler macro injected by the Vite plugin — no import needed.

  6. Extract, translate, and compile

    Terminal window
    npx fluenti extract
    # ... translate the .po files ...
    npx fluenti compile

React Router works well with lazy-loaded routes. The Vite plugin can tree-shake messages per chunk:

const About = lazy(() => import('./pages/About'))
<Route path="/about" element={
<Suspense fallback={<p>Loading...</p>}>
<About />
</Suspense>
} />

For more on code splitting, see Code Splitting.

  • Cookie-based locale persistence (server-readable for future SSR migration)
  • Lazy-loaded route components with Suspense for code splitting
  • preloadLocale() on hover for instant locale switching