234 lines
7.1 KiB
Markdown
234 lines
7.1 KiB
Markdown
# 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
|
|
```php
|
|
// 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
|
|
```php
|
|
// 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
|
|
```css
|
|
/* In app.css */
|
|
@import "tailwindcss";
|
|
|
|
@theme {
|
|
/* RTL support via logical properties */
|
|
}
|
|
```
|
|
|
|
### Font Configuration
|
|
```css
|
|
/* 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
|
|
```blade
|
|
<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
|
|
```php
|
|
// 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
|
|
```blade
|
|
<!-- 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
|