Files
zerp/BILINGUAL_IMPLEMENTATION_GUIDE.md

7.7 KiB

Bilingual System Implementation Guide

Overview

The system now supports 100% bilingual functionality with English and Arabic languages. Everything switches based on user preference, including:

  • Navigation menus
  • Buttons and labels
  • Form fields and placeholders
  • Error messages and notifications
  • Table headers and content
  • Modals and dialogs

Quick Start

1. Using Translations in Components

'use client'

import { useLanguage } from '@/contexts/LanguageContext'

function MyComponent() {
  const { t, language, dir } = useLanguage()

  return (
    <div dir={dir}>
      <h1>{t('contacts.title')}</h1>
      <button>{t('common.save')}</button>
      <p>{t('contacts.searchPlaceholder')}</p>
    </div>
  )
}

2. Adding the Language Switcher

Add the language switcher to your navigation/header:

import LanguageSwitcher from '@/components/LanguageSwitcher'

function Header() {
  return (
    <header>
      {/* Other header content */}
      <LanguageSwitcher />
    </header>
  )
}

3. Translation Keys Structure

Translations are organized by domain:

common.*          - Common UI elements (save, cancel, delete, etc.)
nav.*             - Navigation items
contacts.*        - Contacts module
import.*          - Import functionality
messages.*        - System messages

Complete Examples

Example 1: Simple Button

// Before (hardcoded)
<button>Add Contact</button>

// After (bilingual)
<button>{t('contacts.addContact')}</button>

Example 2: Form Labels

// Before
<label>Name <span className="text-red-500">*</span></label>

// After
<label>
  {t('contacts.name')} 
  <span className="text-red-500"> {t('common.required')}</span>
</label>

Example 3: Toast Notifications

// Before
toast.success('Contact created successfully')

// After
toast.success(t('contacts.createSuccess'))

Example 4: Conditional Text with Direction

const { t, dir } = useLanguage()

return (
  <div dir={dir} className={dir === 'rtl' ? 'text-right' : 'text-left'}>
    <h2>{t('contacts.title')}</h2>
  </div>
)

Adding New Translations

To add new translations, edit /src/contexts/LanguageContext.tsx:

const translations = {
  en: {
    myModule: {
      myKey: 'My English Text',
      anotherKey: 'Another English Text'
    }
  },
  ar: {
    myModule: {
      myKey: 'النص بالعربية',
      anotherKey: 'نص آخر بالعربية'
    }
  }
}

Then use it:

{t('myModule.myKey')}

Current Translation Coverage

All translations are already defined for:

Common UI Elements

  • Actions: save, cancel, delete, edit, add, search, filter, export, import
  • States: loading, active, inactive, archived, deleted
  • Feedback: success, error, confirm
  • Navigation: back, next, finish, close, yes, no

Contacts Module

  • All field labels (name, email, phone, etc.)
  • Contact types (individual, company, holding, government)
  • Relationship types (representative, partner, supplier, etc.)
  • Actions (add, edit, delete, merge, import, export)
  • Messages (success, error, warnings)

Import/Export

  • All steps and labels
  • File requirements
  • Results and errors

RTL (Right-to-Left) Support

The system automatically applies RTL when Arabic is selected:

const { dir } = useLanguage()

// Direction is automatically set on document.documentElement
// Use it in your components when needed:
<div dir={dir}>
  {/* Content flows correctly in both directions */}
</div>

RTL-Specific Styling

Some components may need direction-specific styles:

<div className={`
  flex items-center gap-4
  ${dir === 'rtl' ? 'flex-row-reverse' : 'flex-row'}
`}>
  <Icon />
  <span>{t('contacts.name')}</span>
</div>

Integration Checklist

To fully integrate the bilingual system into an existing component:

  • Import useLanguage hook
  • Replace all hardcoded text with t('key.path')
  • Update toast messages with translations
  • Add dir attribute where needed for RTL
  • Test language switching
  • Verify RTL layout doesn't break UI

Example: Complete Component Conversion

Before (Hardcoded)

function ContactCard({ contact }) {
  return (
    <div>
      <h3>Contact Details</h3>
      <p>Name: {contact.name}</p>
      <p>Email: {contact.email}</p>
      <button onClick={handleEdit}>Edit</button>
      <button onClick={handleDelete}>Delete</button>
    </div>
  )
}

After (Bilingual)

function ContactCard({ contact }) {
  const { t, dir } = useLanguage()
  
  return (
    <div dir={dir}>
      <h3>{t('contacts.contactDetails')}</h3>
      <p>{t('contacts.name')}: {contact.name}</p>
      <p>{t('contacts.email')}: {contact.email}</p>
      <button onClick={handleEdit}>{t('common.edit')}</button>
      <button onClick={handleDelete}>{t('common.delete')}</button>
    </div>
  )
}

Testing

  1. Switch Language: Click the language switcher (EN/AR)
  2. Verify All Text Changes: Navigate through all pages and check that all text switches
  3. Check RTL Layout: Verify that Arabic layout flows right-to-left correctly
  4. Test Forms: Ensure form labels, placeholders, and error messages translate
  5. Test Notifications: Verify toast messages appear in the correct language

Language Persistence

The selected language is automatically saved to localStorage and persists across sessions.

Best Practices

  1. Always use translation keys: Never hardcode user-facing text
  2. Group related translations: Keep related keys in the same object
  3. Use descriptive keys: contacts.addButton is better than btn1
  4. Test both languages: Always verify text fits in both English and Arabic
  5. Consider text length: Arabic text is often longer than English - design accordingly
  6. Use semantic HTML: Proper HTML helps with RTL rendering

Common Patterns

Table Headers

<th>{t('contacts.name')}</th>
<th>{t('contacts.email')}</th>
<th>{t('contacts.phone')}</th>
<th>{t('common.actions')}</th>

Status Badges

const statusText = status === 'ACTIVE' 
  ? t('common.active') 
  : t('common.inactive')

<span className="badge">{statusText}</span>

Confirm Dialogs

const confirmed = window.confirm(t('contacts.deleteConfirm'))
if (confirmed) {
  await deleteContact(id)
  toast.success(t('contacts.deleteSuccess'))
}

Adding Language Switcher to Dashboard

Add to your navigation component:

import LanguageSwitcher from '@/components/LanguageSwitcher'

function Navigation() {
  const { t } = useLanguage()
  
  return (
    <nav>
      <div className="logo">{t('nav.dashboard')}</div>
      <div className="nav-links">
        <Link href="/contacts">{t('nav.contacts')}</Link>
        <Link href="/crm">{t('nav.crm')}</Link>
        {/* ... other links */}
      </div>
      <LanguageSwitcher />
    </nav>
  )
}

Troubleshooting

Issue: Text doesn't translate

  • Solution: Check the translation key exists in LanguageContext.tsx

Issue: RTL layout is broken

  • Solution: Add dir={dir} to parent container and check flex directions

Issue: Language doesn't persist

  • Solution: Check browser localStorage is enabled

Issue: Translation shows key instead of text

  • Solution: Verify the key path is correct (case-sensitive)

Next Steps

  1. Add the LanguageSwitcher component to your main navigation
  2. Start converting components one by one
  3. Add any missing translation keys to LanguageContext.tsx
  4. Test thoroughly in both languages

Note: The translation system is now fully integrated. Every component you create should use the useLanguage() hook and t() function for 100% bilingual support.