Skip to content

Commit d1d064d

Browse files
committed
Merge branch 'feat/translations'
2 parents 598daf6 + ebc755b commit d1d064d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

69 files changed

+3436
-414
lines changed

I18N_IMPLEMENTATION.md

Lines changed: 277 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,277 @@
1+
# i18n Implementation Guide
2+
3+
## ✅ Completed
4+
5+
### Infrastructure
6+
7+
- ✅ Installed `i18next` and `react-i18next`
8+
- ✅ Created i18n configuration (`src/_common/i18n/config.ts`)
9+
- ✅ Created locale store (`src/_common/store/locale.store.ts`)
10+
- ✅ Created translation files for all 9 locales:
11+
- en-US (English - United States) **FALLBACK**
12+
- en-GB (English - United Kingdom)
13+
- en-CA (English - Canada)
14+
- en-AU (English - Australia)
15+
- en-NZ (English - New Zealand)
16+
- fr-FR (French - France)
17+
- de-DE (German - Germany)
18+
- it-IT (Italian - Italy)
19+
- nl-NL (Dutch - Netherlands)
20+
21+
### Components
22+
23+
- ✅ Locale selection screen (`src/localeSelection/LocaleSelection.screen.tsx`)
24+
- ✅ Locale selector component (`src/_common/components/LocaleSelector.tsx`)
25+
- ✅ Integrated into Router (`src/Router.tsx`)
26+
- ✅ Integrated into main app (`src/main.tsx`)
27+
28+
### Translated Screens & Components
29+
30+
- ✅ Router.tsx - Error fallback
31+
- ✅ NavigationBar.tsx - Full navigation with all labels and dialogs
32+
- ✅ Boathouse.screen.tsx - Boathouse screen with boat lists
33+
- ✅ Parameters.screen.tsx - Parameter navigation tabs
34+
- ✅ Stats.screen.tsx - Statistics screen with month names and stats
35+
- ✅ Onboarding.screen.tsx - Complete onboarding flow
36+
- ✅ SessionLogs.tsx - Session history with pagination
37+
- ✅ StartSession.Dialog.tsx - Start session dialog
38+
- ✅ StopSession.Dialog.tsx - Stop session dialog
39+
40+
### Utilities
41+
42+
- ✅ Created `boat.rules.i18n.ts` for translated boat type labels
43+
44+
## 🔄 In Progress / Remaining
45+
46+
### Components Still Needing Translation (~70+ files)
47+
48+
Use this pattern for each component:
49+
50+
#### Pattern to Follow:
51+
52+
1. **Import useTranslation**:
53+
54+
```typescript
55+
import { useTranslation } from "react-i18next";
56+
```
57+
58+
2. **Add in component**:
59+
60+
```typescript
61+
const { t } = useTranslation();
62+
```
63+
64+
3. **Replace hardcoded strings**:
65+
66+
```typescript
67+
// Before:
68+
<button>Valider</button>
69+
70+
// After:
71+
<button>{t("common.confirm")}</button>
72+
```
73+
74+
#### Priority Files to Translate:
75+
76+
**Boathouse Components:**
77+
78+
- `src/boathouse/components/StartSessionDialog/StartSession.Form.tsx`
79+
- `src/boathouse/components/StartSessionDialog/BoatSection.tsx`
80+
- `src/boathouse/components/StartSessionDialog/RowersSection.tsx`
81+
- `src/boathouse/components/StartSessionDialog/RouteSection.tsx`
82+
- `src/boathouse/components/StartSessionDialog/CommentSection.tsx`
83+
- `src/boathouse/components/StartSessionDialog/StartDatetimeSection.tsx`
84+
- `src/boathouse/components/StartSessionDialog/DurationEstimationSelect.tsx`
85+
- `src/boathouse/components/StopSessionDialog/StopSession.Form.tsx`
86+
- `src/boathouse/components/BoatList/*.tsx`
87+
88+
**Logbook Components:**
89+
90+
- `src/logbook/components/IncidentLogs.tsx`
91+
- `src/logbook/components/IncidentLogsTable.tsx`
92+
- `src/logbook/components/SessionLogsTable.tsx`
93+
- `src/logbook/components/ExportSessions.tsx`
94+
95+
**Parameters Components:**
96+
97+
- `src/parameters/components/RowersCrud.tsx`
98+
- `src/parameters/components/BoatsCrud.tsx`
99+
- `src/parameters/components/MiscParams.tsx`
100+
- `src/parameters/components/UpdateRower.tsx`
101+
- `src/parameters/components/UpdateBoat.tsx`
102+
- `src/parameters/components/BulkUpdateRower.tsx`
103+
- `src/parameters/components/RowerStats.tsx`
104+
- `src/parameters/components/BoatStats.tsx`
105+
- `src/parameters/components/RowerStatsComparisons.tsx`
106+
- `src/parameters/components/BoatStatsComparisons.tsx`
107+
- `src/parameters/components/RouteConfigModal.tsx`
108+
- `src/parameters/components/BoatLevelConfigModal.tsx`
109+
- `src/parameters/components/BoatLevelSystem.tsx`
110+
- `src/parameters/components/DeleteDatas.tsx`
111+
- `src/parameters/components/dialogs/*.tsx`
112+
113+
**Stats Components:**
114+
115+
- `src/stats/components/SeasonSelector.tsx`
116+
- `src/stats/components/MonthSelector.tsx`
117+
- `src/stats/components/StackedBarChart.tsx`
118+
119+
**Common Components:**
120+
121+
- `src/_common/components/Loading.tsx`
122+
- `src/_common/components/ErrorBlock.tsx`
123+
- `src/_common/components/WindowAlert.tsx`
124+
- `src/_common/components/WindowConfirm.tsx`
125+
- `src/_common/components/WindowPrompt.tsx`
126+
127+
### Adding New Translation Keys
128+
129+
When you find a string that needs translation:
130+
131+
1. **Add to `src/_common/i18n/locales/en-US.json`** (English base):
132+
133+
```json
134+
{
135+
"category": {
136+
"newKey": "New English Text"
137+
}
138+
}
139+
```
140+
141+
2. **Add to `src/_common/i18n/locales/fr-FR.json`** (French):
142+
143+
```json
144+
{
145+
"category": {
146+
"newKey": "Nouveau texte français"
147+
}
148+
}
149+
```
150+
151+
3. **Add to all other locale files** with appropriate translations.
152+
153+
### Common Translation Categories
154+
155+
The translation files are organized into these categories:
156+
157+
- `common`: General UI elements (buttons, labels, etc.)
158+
- `navigation`: App navigation and menus
159+
- `onboarding`: Onboarding/setup flow
160+
- `boathouse`: Boathouse screen specific
161+
- `session`: Session management (start, stop, details)
162+
- `logbook`: Logbook and history
163+
- `stats`: Statistics screen
164+
- `parameters`: Settings/parameters screen
165+
- `boat`: Boat-related terms
166+
- `rower`: Rower-related terms
167+
- `error`: Error messages
168+
- `time`: Time-related terms
169+
170+
### Using Translation with Parameters
171+
172+
For dynamic values:
173+
174+
```typescript
175+
// In translation file:
176+
"pageOf": "Page {{current}} of {{total}}"
177+
178+
// In component:
179+
{t("session.pageOf", { current: 5, total: 10 })}
180+
// Output: "Page 5 of 10"
181+
```
182+
183+
### Pluralization
184+
185+
For plurals, use the i18next convention:
186+
187+
```json
188+
{
189+
"session_one": "{{count}} session",
190+
"session_other": "{{count}} sessions"
191+
}
192+
```
193+
194+
Then use:
195+
196+
```typescript
197+
t(`stats.session_${count === 1 ? "one" : "other"}`, { count });
198+
```
199+
200+
### Boat Type Labels
201+
202+
Use the utility function for boat type labels:
203+
204+
```typescript
205+
import { getTypeLabelTranslated } from "../_common/business/boat.rules.i18n";
206+
207+
// In component:
208+
const label = getTypeLabelTranslated(boatType, t);
209+
```
210+
211+
## 📋 Testing
212+
213+
1. **Test Locale Selection**: On first launch, you should see the locale selection screen
214+
2. **Test Translations**: Change locale and verify text changes
215+
3. **Test Fallback**: If a translation is missing, it should fall back to en-US
216+
217+
## 🔧 Development Commands
218+
219+
```bash
220+
# Run dev server
221+
yarn dev
222+
223+
# Build (includes TypeScript check and tests)
224+
yarn build
225+
226+
# Type check only
227+
yarn tsc
228+
229+
# Lint
230+
yarn lint
231+
```
232+
233+
## 📝 Quick Reference
234+
235+
### Import Pattern
236+
237+
```typescript
238+
import { useTranslation } from "react-i18next";
239+
240+
export const MyComponent = () => {
241+
const { t } = useTranslation();
242+
243+
return <div>{t("category.key")}</div>;
244+
};
245+
```
246+
247+
### Change Language Programmatically
248+
249+
```typescript
250+
import { changeLanguage } from "../_common/i18n/config";
251+
import { Locale } from "../_common/store/locale.store";
252+
253+
changeLanguage("fr-FR" as Locale);
254+
```
255+
256+
### Access Current Locale
257+
258+
```typescript
259+
import { useLocaleStore } from "../_common/store/locale.store";
260+
261+
const { locale } = useLocaleStore();
262+
```
263+
264+
## 🎯 Next Steps
265+
266+
1. Go through each component file in the priority list
267+
2. Add `useTranslation` hook
268+
3. Replace hardcoded strings with `t()` calls
269+
4. Add missing translation keys to all 9 locale files
270+
5. Test each screen by switching locales
271+
6. Update utility functions that return user-facing text
272+
273+
## 📚 Resources
274+
275+
- [i18next Documentation](https://www.i18next.com/)
276+
- [react-i18next Documentation](https://react.i18next.com/)
277+
- Translation files: `src/_common/i18n/locales/*.json`

package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "boathouse.rowingbeacon.com",
33
"private": true,
4-
"version": "1.19.0",
4+
"version": "1.20.0",
55
"type": "module",
66
"scripts": {
77
"dev": "vite",
@@ -38,12 +38,14 @@
3838
"clsx": "^2.0.0",
3939
"cmdk": "^1.0.0",
4040
"drizzle-orm": "^0.38.0",
41+
"i18next": "^25.6.0",
4142
"jssha": "^3.3.1",
4243
"lucide-react": "^0.412.0",
4344
"react": "^18.2.0",
4445
"react-dom": "^18.2.0",
4546
"react-error-boundary": "^5.0.0",
4647
"react-hook-form": "^7.46.2",
48+
"react-i18next": "^16.2.1",
4749
"react-select": "^5.8.0",
4850
"recharts": "^2.15.1",
4951
"sonner": "^1.6.0",

src-tauri/Cargo.lock

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src-tauri/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[package]
22
name = "rowing-beacon-tauri-app"
3-
version = "1.19.0"
3+
version = "1.20.0"
44
description = "A Tauri App"
55
authors = ["you"]
66
license = ""

src-tauri/tauri.conf.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@
1818
},
1919
"productName": "Rowing Beacon",
2020
"mainBinaryName": "Rowing Beacon",
21-
"version": "1.19.0",
21+
"version": "1.20.0",
2222
"identifier": "com.rowing-beacon-tauri-app.app",
2323
"plugins": {
2424
"sql": {

src/Router.tsx

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -10,8 +10,14 @@ import { useOnboardingStore } from "./onboarding/onboarding.store";
1010
import { ParametersScreen } from "./parameters/Parameters.screen";
1111
import { StatsScreen } from "./stats/Stats.screen";
1212
import { ErrorBoundary } from "react-error-boundary";
13+
import { useLocaleStore } from "./_common/store/locale.store";
14+
import { LocaleSelectionScreen } from "./localeSelection/LocaleSelection.screen";
15+
import { useEffect } from "react";
16+
import { changeLanguage } from "./_common/i18n/config";
17+
import { useTranslation } from "react-i18next";
1318

1419
export function Router() {
20+
const { locale } = useLocaleStore();
1521
const isOnboardingDone = useOnboardingStore((state) => state.isOnboarded);
1622
const setIsOnboardingDone = useOnboardingStore((state) => state.setOnboarded);
1723
const {
@@ -23,6 +29,18 @@ export function Router() {
2329
setPage("parameters");
2430
});
2531

32+
// Initialize language from store on mount
33+
useEffect(() => {
34+
if (locale) {
35+
changeLanguage(locale);
36+
}
37+
}, [locale]);
38+
39+
// Show locale selection first if not set
40+
if (!locale) {
41+
return <LocaleSelectionScreen onComplete={() => {}} />;
42+
}
43+
2644
if (!isOnboardingDone) {
2745
return (
2846
<OnboardingScreen
@@ -61,16 +79,17 @@ export function Router() {
6179
}
6280

6381
const ErrorFallback = () => {
82+
const { t } = useTranslation();
6483
return (
6584
<div className="flex-1 flex items-center justify-center flex-col gap-4 p-4 h-full">
6685
<AlertCircleIcon className="w-12 h-12 text-red-500" />
67-
<p className="text-lg">Une erreur est survenue</p>
86+
<p className="text-lg">{t("error.generic")}</p>
6887
<button
6988
onClick={() => window.location.reload()}
7089
className="px-4 py-2 bg-gray-100 hover:bg-gray-200 rounded-lg flex items-center gap-2"
7190
>
7291
<RefreshCwIcon className="w-4 h-4" />
73-
Recharger la page
92+
{t("error.reload")}
7493
</button>
7594
</div>
7695
);

0 commit comments

Comments
 (0)