---
name: i18n-management
description: Manage Arabic/English translations, RTL/LTR layouts, and bilingual content for Mawidi. Use when adding new UI text, updating translations, fixing RTL issues, or creating bilingual features. Ensures consistent language handling across 70+ pages.
---

# i18n Management Skill

## Overview

Manage Mawidi's bilingual Arabic/English internationalization system. This skill covers translation management, RTL/LTR layout handling, and bilingual content patterns used across the 70+ page application.

**Languages:** Arabic (ar) - RTL, English (en) - LTR
**Market:** GCC (Gulf Cooperation Council)
**Translation Files:** `lib/config/translations/`

## When to Use

Use this skill when:

- Adding new UI text/labels
- Updating existing translations
- Fixing RTL layout issues
- Creating bilingual email templates
- Building language-aware components
- Debugging translation key issues

## Translation File Structure

```
lib/config/translations/
├── ar.ts                 # Main Arabic translations
├── en.ts                 # Main English translations
└── modules/
    ├── common/
    │   ├── ar.ts         # Shared components
    │   └── en.ts
    ├── dashboard.ar.ts   # Dashboard Arabic
    ├── dashboard.en.ts   # Dashboard English
    ├── industries/       # Industry-specific (22 files)
    │   ├── healthcare.ar.ts
    │   ├── healthcare.en.ts
    │   └── ...
    └── pages/            # Page-specific (34 files)
        ├── pricing.ar.ts
        ├── pricing.en.ts
        └── ...
```

## Adding Translations

### Step 1: Add to Both Language Files

**English (en.ts):**

```typescript
// lib/config/translations/en.ts
export const en = {
  // ... existing translations

  booking: {
    // ... existing
    cancelSuccess: "Booking cancelled successfully",
    cancelConfirm: "Are you sure you want to cancel?",
  },
};
```

**Arabic (ar.ts):**

```typescript
// lib/config/translations/ar.ts
export const ar = {
  // ... existing translations

  booking: {
    // ... existing
    cancelSuccess: "تم إلغاء الحجز بنجاح",
    cancelConfirm: "هل أنت متأكد من إلغاء الحجز؟",
  },
};
```

### Step 2: Use in Components

```tsx
// Access translations via params.lang
export default function Page({ params }: { params: { lang: "ar" | "en" } }) {
  const t = translations[params.lang];

  return (
    <div dir={params.lang === "ar" ? "rtl" : "ltr"}>
      <h1>{t.booking.cancelSuccess}</h1>
    </div>
  );
}
```

## Translation Patterns

### Using UI Helper

```tsx
import { UI } from "@/lib/config";

// In server component
export default function Page({ params }: { params: { lang: "ar" | "en" } }) {
  const { t, dir } = UI[params.lang];

  return (
    <main dir={dir}>
      <h1>{t.common.welcome}</h1>
      <p>{t.pages.pricing.description}</p>
    </main>
  );
}
```

### Client Components

```tsx
"use client";

import { useParams } from "next/navigation";
import { UI } from "@/lib/config";

export function ClientComponent() {
  const params = useParams();
  const lang = params.lang as "ar" | "en";
  const { t, dir } = UI[lang];

  return (
    <div className={dir === "rtl" ? "text-right" : "text-left"}>
      {t.common.submit}
    </div>
  );
}
```

### Dynamic Translations with Variables

```typescript
// Translation with placeholder
const en = {
  greeting: "Welcome, {{name}}!",
  itemCount: "You have {{count}} appointments",
};

const ar = {
  greeting: "مرحباً، {{name}}!",
  itemCount: "لديك {{count}} مواعيد",
};

// Usage with template replacement
function translate(text: string, vars: Record<string, string | number>) {
  return text.replace(/\{\{(\w+)\}\}/g, (_, key) => String(vars[key] || ""));
}

// Example
translate(t.greeting, { name: "أحمد" }); // "مرحباً، أحمد!"
```

## RTL/LTR Layout Patterns

### Direction-Aware CSS

```css
/* Use logical properties */
.container {
  /* Instead of: margin-left: 1rem; */
  margin-inline-start: 1rem;

  /* Instead of: padding-right: 2rem; */
  padding-inline-end: 2rem;

  /* Instead of: text-align: left; */
  text-align: start;
}
```

### Tailwind RTL Utilities

```tsx
// Automatic RTL handling
<div className="rtl:space-x-reverse flex space-x-4">
  <span className="rtl:text-right ltr:text-left">{t.label}</span>
  <span className="rtl:ml-4 ltr:mr-4">{t.value}</span>
</div>

// Icon flipping
<ChevronLeft className="rtl:rotate-180" />

// Border direction
<div className="border-l-4 rtl:border-l-0 rtl:border-r-4 border-green-500" />
```

### Layout Components

```tsx
// Direction-aware flex
export function DirectionalFlex({
  children,
  lang,
}: {
  children: React.ReactNode;
  lang: "ar" | "en";
}) {
  return (
    <div
      className="flex items-center gap-4"
      style={{ direction: lang === "ar" ? "rtl" : "ltr" }}
    >
      {children}
    </div>
  );
}
```

## Font Management

### Arabic Font Loading

```tsx
// app/[lang]/layout.tsx
import { IBM_Plex_Sans_Arabic } from "next/font/google";
import { Plus_Jakarta_Sans } from "next/font/google";

const arabicFont = IBM_Plex_Sans_Arabic({
  subsets: ["arabic"],
  weight: ["400", "500", "600", "700"],
  variable: "--font-arabic",
});

const englishFont = Plus_Jakarta_Sans({
  subsets: ["latin"],
  weight: ["400", "500", "600", "700", "800"],
  variable: "--font-display",
});

export default function Layout({
  children,
  params,
}: {
  children: React.ReactNode;
  params: { lang: "ar" | "en" };
}) {
  const fontClass =
    params.lang === "ar" ? arabicFont.variable : englishFont.variable;

  return (
    <html lang={params.lang} dir={params.lang === "ar" ? "rtl" : "ltr"}>
      <body className={fontClass}>{children}</body>
    </html>
  );
}
```

### Font Class Usage

```tsx
// Apply correct font based on language
<h1
  className={`text-4xl font-bold ${lang === "ar" ? "font-arabic" : "font-display"}`}
>
  {t.title}
</h1>
```

## Number and Date Formatting

### Number Formatting

```typescript
// Format numbers based on locale
export function formatNumber(num: number, lang: "ar" | "en"): string {
  return new Intl.NumberFormat(lang === "ar" ? "ar-SA" : "en-US").format(num);
}

// Currency formatting
export function formatCurrency(
  amount: number,
  currency: string,
  lang: "ar" | "en",
): string {
  return new Intl.NumberFormat(lang === "ar" ? "ar-SA" : "en-US", {
    style: "currency",
    currency,
  }).format(amount);
}

// Examples
formatNumber(1234567, "ar"); // "١٬٢٣٤٬٥٦٧"
formatNumber(1234567, "en"); // "1,234,567"
formatCurrency(99.99, "QAR", "ar"); // "٩٩٫٩٩ ر.ق."
```

### Date Formatting

```typescript
// Format dates based on locale
export function formatDate(
  date: Date,
  lang: "ar" | "en",
  options: Intl.DateTimeFormatOptions = { dateStyle: "long" },
): string {
  return new Intl.DateTimeFormat(
    lang === "ar" ? "ar-SA" : "en-US",
    options,
  ).format(date);
}

// Examples
formatDate(new Date(), "ar"); // "١٥ يناير ٢٠٢٥"
formatDate(new Date(), "en"); // "January 15, 2025"

// Relative time
export function formatRelativeTime(date: Date, lang: "ar" | "en"): string {
  const rtf = new Intl.RelativeTimeFormat(lang === "ar" ? "ar-SA" : "en-US", {
    numeric: "auto",
  });

  const diff = Math.floor(
    (date.getTime() - Date.now()) / (1000 * 60 * 60 * 24),
  );
  return rtf.format(diff, "day");
}
```

## Bilingual Email Templates

### Email Template Pattern

```typescript
// lib/email-templates/booking-confirmation.ts

interface BookingEmailData {
  customerName: string;
  serviceName: string;
  date: string;
  time: string;
}

export function getBookingConfirmationEmail(
  data: BookingEmailData,
  lang: "ar" | "en",
) {
  const templates = {
    en: {
      subject: `Booking Confirmed - ${data.serviceName}`,
      body: `
        <h1>Booking Confirmed!</h1>
        <p>Dear ${data.customerName},</p>
        <p>Your appointment for ${data.serviceName} has been confirmed.</p>
        <p><strong>Date:</strong> ${data.date}</p>
        <p><strong>Time:</strong> ${data.time}</p>
      `,
    },
    ar: {
      subject: `تأكيد الحجز - ${data.serviceName}`,
      body: `
        <h1 dir="rtl">تم تأكيد الحجز!</h1>
        <p dir="rtl">عزيزي ${data.customerName}،</p>
        <p dir="rtl">تم تأكيد موعدك لـ ${data.serviceName}.</p>
        <p dir="rtl"><strong>التاريخ:</strong> ${data.date}</p>
        <p dir="rtl"><strong>الوقت:</strong> ${data.time}</p>
      `,
    },
  };

  return templates[lang];
}
```

## Translation Keys Checklist

When adding new features, ensure translations for:

- [ ] Page titles and meta descriptions
- [ ] Navigation labels
- [ ] Form labels and placeholders
- [ ] Button text
- [ ] Success/error messages
- [ ] Empty state text
- [ ] Loading states
- [ ] Email templates
- [ ] Validation error messages
- [ ] Tooltips and help text

## Common Mistakes to Avoid

### ❌ Hardcoded Text

```tsx
// BAD
<button>Submit</button>

// GOOD
<button>{t.common.submit}</button>
```

### ❌ Directional CSS

```css
/* BAD */
margin-left: 1rem;
padding-right: 2rem;

/* GOOD */
margin-inline-start: 1rem;
padding-inline-end: 2rem;
```

### ❌ Missing Direction Attribute

```tsx
// BAD
<div>{arabicContent}</div>

// GOOD
<div dir="rtl">{arabicContent}</div>
```

### ❌ Concatenating Translations

```tsx
// BAD - Word order differs between languages
<p>{t.you} {t.have} {count} {t.items}</p>

// GOOD - Use template with placeholder
<p>{translate(t.itemCount, { count })}</p>
```

## Testing Translations

### Manual Testing Checklist

1. Switch between `/ar/` and `/en/` URLs
2. Verify all text is translated (no English in Arabic pages)
3. Check RTL layout doesn't break design
4. Verify numbers/dates use correct locale
5. Test form validation messages in both languages
6. Check email templates in both languages

### Automated Testing

```typescript
// __tests__/translations.test.ts

import { ar } from "@/lib/config/translations/ar";
import { en } from "@/lib/config/translations/en";

describe("Translation completeness", () => {
  it("should have matching keys in both languages", () => {
    const arKeys = getAllKeys(ar);
    const enKeys = getAllKeys(en);

    expect(arKeys.sort()).toEqual(enKeys.sort());
  });

  it("should not have empty translations", () => {
    const arValues = getAllValues(ar);
    const enValues = getAllValues(en);

    arValues.forEach((v) => expect(v).not.toBe(""));
    enValues.forEach((v) => expect(v).not.toBe(""));
  });
});
```

## Quick Reference

### Adding a New Translation

1. Add key to `lib/config/translations/en.ts`
2. Add key to `lib/config/translations/ar.ts`
3. Use in component: `{t.your.new.key}`
4. Test in both `/ar/` and `/en/` routes

### RTL Quick Fixes

| Issue                 | Solution                                  |
| --------------------- | ----------------------------------------- |
| Text alignment wrong  | Add `dir={lang === 'ar' ? 'rtl' : 'ltr'}` |
| Spacing reversed      | Use `rtl:` Tailwind prefix                |
| Icons pointing wrong  | Add `rtl:rotate-180` class                |
| Borders on wrong side | Use `rtl:border-l-0 rtl:border-r-4`       |

## See Also

- [add-translation.ts](../add-translation.ts) - Programmatic translation addition
- [send-bilingual-email.ts](../send-bilingual-email.ts) - Bilingual email sending
- [brand-guidelines](../brand-guidelines/SKILL.md) - Typography for Arabic/English
