libra/docs/stories/story-1.3-bilingual-infrast...

7.1 KiB

Story 1.3: Bilingual Infrastructure (Arabic/English)

Epic Reference

Epic 1: Core Foundation & Infrastructure

User Story

As a user (admin or client), I want full bilingual support with Arabic as primary and English as secondary language, So that I can use the platform in my preferred language with proper RTL/LTR layout.

Story Context

Existing System Integration

  • Integrates with: Laravel localization, Tailwind CSS, user preferences
  • Technology: Laravel lang files, Tailwind RTL, Google Fonts
  • Follows pattern: Laravel localization best practices
  • Touch points: All views, navigation, date/time formatting

Reference Documents

  • PRD Section 4.2: Language Support requirements (RTL/LTR, numerals, date/time formats)
  • PRD Section 7.1.C: Typography specifications (Arabic: Cairo/Tajawal, English: Montserrat/Lato)

Acceptance Criteria

Functional Requirements

  • Language files for Arabic (ar) and English (en)
  • Language toggle in navigation (visible on all pages)
  • User language preference stored in users.preferred_language
  • Guest language stored in session (persists across page loads)
  • RTL layout for Arabic, LTR for English
  • All UI elements translatable via __() helper
  • Language switch preserves current page (redirect back to same URL)
  • Form validation messages display in current locale
  • Missing translations fall back to key name (not break page)

Date/Time Formatting

  • Arabic: DD/MM/YYYY format
  • English: MM/DD/YYYY format
  • Both: 12-hour time format (AM/PM)
  • Both: Western numerals (123) - no Arabic numerals

Typography

  • Arabic fonts: Cairo or Tajawal (Google Fonts)
  • English fonts: Montserrat or Lato (Google Fonts)
  • Font weights: 300, 400, 600, 700
  • font-display: swap for performance

Integration Requirements

  • Language middleware sets locale from user preference or session
  • Direction attribute (dir="rtl" or dir="ltr") on HTML element
  • Tailwind RTL utilities working
  • Forms align correctly in both directions

Quality Requirements

  • No hardcoded strings in views
  • All translation keys organized by feature
  • Tests verify language switching
  • No layout breaks when switching languages

Technical Notes

Language Toggle Route & Controller

// routes/web.php
Route::get('/language/{locale}', function (string $locale) {
    if (!in_array($locale, ['ar', 'en'])) {
        abort(400);
    }

    session(['locale' => $locale]);

    if (auth()->check()) {
        auth()->user()->update(['preferred_language' => $locale]);
    }

    return redirect()->back();
})->name('language.switch');

Language Middleware

// app/Http/Middleware/SetLocale.php
// Register in bootstrap/app.php: ->withMiddleware(fn($m) => $m->web(append: SetLocale::class))
public function handle($request, Closure $next)
{
    $locale = session('locale',
        auth()->user()?->preferred_language ?? 'ar'
    );
    app()->setLocale($locale);
    return $next($request);
}

Translation File Structure

resources/lang/
  ar/
    auth.php
    pagination.php
    validation.php
    messages.php
    navigation.php
  en/
    auth.php
    pagination.php
    validation.php
    messages.php
    navigation.php

RTL Support with Tailwind 4

/* In app.css */
@import "tailwindcss";

@theme {
  /* RTL support via logical properties */
}

Font Configuration

/* Google Fonts import */
@import url('https://fonts.googleapis.com/css2?family=Cairo:wght@300;400;600;700&family=Montserrat:wght@300;400;600;700&display=swap');

@theme {
  --font-arabic: 'Cairo', 'Tajawal', sans-serif;
  --font-english: 'Montserrat', 'Lato', sans-serif;
}

Layout Template

<html lang="{{ app()->getLocale() }}" dir="{{ app()->getLocale() === 'ar' ? 'rtl' : 'ltr' }}">
<head>
    <style>
        body { font-family: var(--font-{{ app()->getLocale() === 'ar' ? 'arabic' : 'english' }}); }
    </style>
</head>

Date Formatting Helper

// In a helper or service (app/Helpers/DateHelper.php or as a service)
public function formatDate($date, $locale = null): string
{
    $locale = $locale ?? app()->getLocale();
    $format = $locale === 'ar' ? 'd/m/Y' : 'm/d/Y';
    return Carbon::parse($date)->format($format);
}

Language Toggle Component

<!-- resources/views/components/language-toggle.blade.php -->
<div class="flex items-center gap-2">
    <a
        href="{{ route('language.switch', 'ar') }}"
        @class([
            'text-sm px-2 py-1 rounded',
            'bg-gold text-navy font-bold' => app()->getLocale() === 'ar',
            'text-gold hover:text-gold-light' => app()->getLocale() !== 'ar',
        ])
    >
        العربية
    </a>
    <span class="text-gold/50">|</span>
    <a
        href="{{ route('language.switch', 'en') }}"
        @class([
            'text-sm px-2 py-1 rounded',
            'bg-gold text-navy font-bold' => app()->getLocale() === 'en',
            'text-gold hover:text-gold-light' => app()->getLocale() !== 'en',
        ])
    >
        English
    </a>
</div>

Testing Requirements

Feature Tests

  • Test language toggle stores preference in session for guests
  • Test language toggle stores preference in database for authenticated users
  • Test locale middleware sets correct locale from user preference
  • Test locale middleware falls back to session for guests
  • Test locale middleware defaults to 'ar' when no preference set
  • Test date formatting helper returns DD/MM/YYYY for Arabic locale
  • Test date formatting helper returns MM/DD/YYYY for English locale
  • Test language switch redirects back to same page

Browser Tests (Pest v4)

  • Test RTL layout renders correctly for Arabic (dir="rtl" on html)
  • Test LTR layout renders correctly for English (dir="ltr" on html)
  • Test no layout breaks when toggling languages
  • Test Arabic font (Cairo) loads for Arabic locale
  • Test English font (Montserrat) loads for English locale

Manual Testing Checklist

  • Verify RTL alignment in navigation, forms, and content
  • Verify LTR alignment in navigation, forms, and content
  • Test on Chrome, Firefox, Safari for RTL rendering
  • Verify no horizontal scroll appears in either direction

Definition of Done

  • Language toggle works in navigation
  • Arabic and English translations complete for core UI
  • RTL layout renders correctly for Arabic
  • LTR layout renders correctly for English
  • User preference persists in database
  • Guest preference persists in session
  • Dates format correctly per language
  • Fonts load properly for both languages
  • Tests pass for language switching
  • Code formatted with Pint

Dependencies

  • Story 1.1: Database schema (users.preferred_language column)
  • Story 1.4: Base UI (navigation for language toggle)

Risk Assessment

  • Primary Risk: RTL edge cases in complex layouts
  • Mitigation: Use Tailwind logical properties (start/end vs left/right), test early
  • Rollback: Fallback to LTR-only temporarily

Estimation

Complexity: Medium-High Estimated Effort: 4-5 hours