Skip to content

Commit fc3b4d8

Browse files
authored
Merge pull request #142 from wtfzdotnet/copilot/fix-98
Implement Core Internationalization (i18n) Infrastructure with React i18next
2 parents 24503fc + bf9b57c commit fc3b4d8

34 files changed

Lines changed: 2407 additions & 89 deletions

.github/copilot-instructions.md

Lines changed: 134 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,140 @@ This is a modern React application built with:
88
- **Testing**: Vitest 3.2.3 + Playwright 1.53.0
99
- **Documentation**: Storybook 9.0.8
1010
- **Linting**: ESLint 9.25.0
11+
- **Internationalization**: React i18next with locale-based measurement units and currency
12+
13+
## Internationalization (i18n) & Localization (l10n) - MANDATORY
14+
15+
This project implements comprehensive internationalization. **ALL component suggestions must include i18n support.**
16+
17+
### Core i18n Rules for Copilot
18+
19+
#### 1. NEVER Suggest Hardcoded Strings
20+
```typescript
21+
// ❌ NEVER suggest this
22+
<button>Save Recipe</button>
23+
<p>Cooking time: 30 minutes</p>
24+
25+
// ✅ ALWAYS suggest this
26+
const { t } = useTranslation();
27+
<button>{t('buttons.save')}</button>
28+
<p>{t('recipe.cookingTime', { minutes: 30 })}</p>
29+
```
30+
31+
#### 2. ALWAYS Include Translation Hooks
32+
```typescript
33+
// ✅ Required pattern for ALL components
34+
import { useTranslation } from 'react-i18next';
35+
import { useLocale } from '@/providers/LocaleProvider';
36+
37+
export const MyComponent = () => {
38+
const { t } = useTranslation('namespace');
39+
const { locale, formatCurrency } = useLocale();
40+
41+
return (
42+
<div>
43+
<h1>{t('title')}</h1>
44+
<span>{formatCurrency(price)}</span>
45+
</div>
46+
);
47+
};
48+
```
49+
50+
#### 3. Locale-Aware Measurement Units
51+
```typescript
52+
// ✅ Auto-detect and convert units based on locale
53+
const { locale, convert } = useLocale();
54+
55+
const displayTemp = locale.measurementSystem === 'metric'
56+
? `${temp}°C`
57+
: `${convert.temperature.celsiusToFahrenheit(temp)}°F`;
58+
59+
const displayWeight = locale.measurementSystem === 'metric'
60+
? `${weight}g`
61+
: `${convert.weight.gramsToOunces(weight)}oz`;
62+
```
63+
64+
#### 4. RTL (Right-to-Left) Compatibility - MANDATORY
65+
66+
**ALL components must support RTL languages (Arabic, Hebrew):**
67+
68+
```typescript
69+
// ✅ ALWAYS use logical properties
70+
className="ms-4 me-2 text-start" // NOT ml-4 mr-2 text-left
71+
72+
// ✅ ALWAYS include RTL-aware icons
73+
<ChevronRight className="rtl:rotate-180" />
74+
75+
// ✅ ALWAYS use RTL-safe layouts
76+
className="flex justify-start rtl:flex-row-reverse"
77+
```
78+
79+
### Copilot Auto-Generation Rules
80+
81+
When suggesting components, GitHub Copilot MUST:
82+
83+
1. **Include useTranslation hook** in every component
84+
2. **Replace ALL strings** with `t()` function calls
85+
3. **Add locale measurement conversion** for relevant data
86+
4. **Include RTL layout classes** in Tailwind CSS
87+
5. **Wrap Storybook stories** with LocaleProvider
88+
6. **Generate translation keys** in proper namespace structure
89+
90+
### Required Storybook Pattern
91+
```typescript
92+
// ✅ ALWAYS wrap stories with LocaleProvider
93+
export default {
94+
decorators: [
95+
(Story) => (
96+
<LocaleProvider defaultLocale="en-US">
97+
<Story />
98+
</LocaleProvider>
99+
),
100+
],
101+
};
102+
103+
// ✅ ALWAYS include RTL testing story
104+
export const RTLTest: Story = {
105+
decorators: [
106+
(Story) => (
107+
<div dir="rtl" className="rtl">
108+
<LocaleProvider defaultLocale="en-US">
109+
<Story />
110+
</LocaleProvider>
111+
</div>
112+
),
113+
],
114+
};
115+
```
116+
117+
### Translation Key Structure
118+
```typescript
119+
// ✅ Suggested translation structure
120+
{
121+
"componentName": {
122+
"title": "Component Title",
123+
"description": "Component description",
124+
"actions": {
125+
"save": "Save",
126+
"cancel": "Cancel"
127+
},
128+
"validation": {
129+
"required": "This field is required",
130+
"invalid": "Invalid {{fieldType}}"
131+
}
132+
}
133+
}
134+
```
135+
136+
### Locale Support Matrix
137+
138+
| Locale | Language | Region | Measurement | Currency | Direction |
139+
|--------|----------|--------|-------------|----------|-----------|
140+
| en-US | English | United States | Imperial | USD | LTR |
141+
| nl-NL | Dutch | Netherlands | Metric | EUR | LTR |
142+
| ar-SA | Arabic | Saudi Arabia | Metric | SAR | RTL |
143+
144+
*Arabic (ar-SA) demonstrates complete RTL layout support with right-to-left text direction*
11145

12146
## Atomic Design + Component-Driven Development
13147

.storybook/preview.tsx

Lines changed: 37 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
import React from 'react';
22
import '../src/index.css'; // Add this line to import Tailwind CSS
33
import { ThemeProvider } from '../src/providers/ThemeProvider';
4+
import { LocaleProvider } from '../src/providers/LocaleProvider';
5+
import '../src/i18n'; // Initialize i18n
46

57
// Optimized font loading for all environments
68
// Load fonts efficiently with display=swap for better performance
@@ -93,15 +95,43 @@ const preview = {
9395
},
9496
},
9597

98+
// Add global toolbar for locale selection
99+
globalTypes: {
100+
locale: {
101+
description: 'Internationalization locale',
102+
defaultValue: 'en-US',
103+
toolbar: {
104+
title: 'Locale',
105+
icon: 'globe',
106+
items: [
107+
{ value: 'en-US', title: '🇺🇸 English (US)', right: 'Imperial, USD' },
108+
{ value: 'nl-NL', title: '🇳🇱 Nederlands (NL)', right: 'Metric, EUR' },
109+
{ value: 'ar-SA', title: '🇸🇦 العربية (SA)', right: 'RTL, Metric, SAR' },
110+
],
111+
dynamicTitle: true,
112+
},
113+
},
114+
},
115+
96116
// Global decorators
97117
decorators: [
98-
(Story) => (
99-
<ThemeProvider defaultTheme="light" storageKey="storybook-ui-theme">
100-
<div style={{ padding: '1rem' }} className="bg-background text-foreground">
101-
<Story />
102-
</div>
103-
</ThemeProvider>
104-
),
118+
(Story, context) => {
119+
const { locale } = context.globals;
120+
121+
return (
122+
<ThemeProvider defaultTheme="light" storageKey="storybook-ui-theme">
123+
<LocaleProvider defaultLocale={locale}>
124+
<div
125+
style={{ padding: '1rem' }}
126+
className="bg-background text-foreground"
127+
dir={locale === 'ar-SA' ? 'rtl' : 'ltr'}
128+
>
129+
<Story />
130+
</div>
131+
</LocaleProvider>
132+
</ThemeProvider>
133+
);
134+
},
105135
],
106136
};
107137

0 commit comments

Comments
 (0)