Internationalization (i18n)
UniPulse supports English (LTR) and Arabic (RTL) using react-i18next with JSON translation files and the Alexandria font family.
Supported Languages
| Language | Code | Direction | Status |
|---|---|---|---|
| English | en | LTR | Complete |
| Arabic | ar | RTL | Complete |
File Structure
apps/web/src/i18n/
├── config.ts # i18next configuration
├── en/ # English translations
│ ├── common.json # Shared strings (buttons, labels, errors)
│ ├── dashboard.json # Dashboard page
│ ├── composer.json # Composer page
│ ├── analytics.json # Analytics page
│ ├── conversations.json # Conversations page
│ ├── workflows.json # Workflows page
│ ├── ecommerce.json # E-Commerce page
│ ├── admin.json # Admin pages
│ └── ... # One file per feature domain
└── ar/ # Arabic translations (mirrors en/ structure)
├── common.json
├── dashboard.json
├── composer.json
└── ...
Configuration
// apps/web/src/i18n/config.ts
import i18n from 'i18next';
import { initReactI18next } from 'react-i18next';
import enCommon from './en/common.json';
import arCommon from './ar/common.json';
// ... other namespace imports
i18n.use(initReactI18next).init({
resources: {
en: { common: enCommon, dashboard: enDashboard, /* ... */ },
ar: { common: arCommon, dashboard: arDashboard, /* ... */ },
},
defaultNS: 'common',
fallbackLng: 'en',
interpolation: { escapeValue: false },
});
Usage in Components
Basic Translation
import { useTranslation } from 'react-i18next';
function DashboardHeader() {
const { t } = useTranslation('dashboard');
return <h1>{t('title')}</h1>;
}
With Interpolation
// en/common.json: { "welcome": "Welcome, {{name}}!" }
<p>{t('common:welcome', { name: user.name })}</p>
With Plurals
// en/common.json: { "posts_count": "{{count}} post", "posts_count_other": "{{count}} posts" }
<p>{t('common:posts_count', { count: posts.length })}</p>
Namespace-Scoped
// Access strings from a specific namespace
const { t } = useTranslation('analytics');
<h2>{t('timeSeries.title')}</h2>
// Access from another namespace with prefix
<p>{t('common:buttons.save')}</p>
RTL Support
When Arabic is selected, the dir="rtl" attribute is set on the <html> element. Tailwind CSS v4 handles RTL layout automatically using CSS logical properties.
// Language switching triggers RTL
function switchLanguage(lang: 'en' | 'ar') {
i18n.changeLanguage(lang);
document.documentElement.dir = lang === 'ar' ? 'rtl' : 'ltr';
document.documentElement.lang = lang;
}
Tailwind v4 Logical Properties
Tailwind v4 uses logical properties by default, which means:
| Physical (LTR-only) | Logical (LTR + RTL) | Behavior |
|---|---|---|
ml-4 | ms-4 (margin-inline-start) | Left in LTR, right in RTL |
mr-4 | me-4 (margin-inline-end) | Right in LTR, left in RTL |
pl-4 | ps-4 (padding-inline-start) | Left in LTR, right in RTL |
text-left | text-start | Left in LTR, right in RTL |
Always prefer ms-, me-, ps-, pe-, text-start, text-end over ml-, mr-, pl-, pr-, text-left, text-right to ensure proper RTL support.
Font: Alexandria
The Alexandria font family supports both Latin and Arabic scripts with consistent visual weight.
/* Loaded in the global stylesheet */
@import url('https://fonts.googleapis.com/css2?family=Alexandria:wght@300;400;500;600;700&display=swap');
body {
font-family: 'Alexandria', sans-serif;
}
Adding New Translations
Step-by-Step
- Add the English key to the appropriate namespace file:
// i18n/en/workflows.json
{
"builder": {
"addNode": "Add Node",
"testRun": "Test Run"
}
}
- Add the Arabic translation to the mirror file:
// i18n/ar/workflows.json
{
"builder": {
"addNode": "إضافة عقدة",
"testRun": "تشغيل تجريبي"
}
}
- Use in your component:
const { t } = useTranslation('workflows');
<Button>{t('builder.addNode')}</Button>
Always add both English and Arabic translations at the same time. Missing Arabic keys will fall back to English, which creates a mixed-language UI.
Language Persistence
The selected language is stored in the Zustand ui.store and persisted to localStorage. On page load, the stored language preference is applied before rendering.
- Frontend Local Setup -- tech stack with i18n tools
- Components -- how components use translations
- State Management -- UI store for language preference