From b110dd9c807632091633f5dd095245590ad22809 Mon Sep 17 00:00:00 2001 From: srfwb <264158739+srfwb@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:33:34 +0200 Subject: [PATCH 01/12] docs(spec): translate WeCode to English design spec --- .../2026-04-27-translate-to-english-design.md | 114 ++++++++++++++++++ 1 file changed, 114 insertions(+) create mode 100644 docs/superpowers/specs/2026-04-27-translate-to-english-design.md diff --git a/docs/superpowers/specs/2026-04-27-translate-to-english-design.md b/docs/superpowers/specs/2026-04-27-translate-to-english-design.md new file mode 100644 index 0000000..ee05fec --- /dev/null +++ b/docs/superpowers/specs/2026-04-27-translate-to-english-design.md @@ -0,0 +1,114 @@ +# Translate WeCode to English — Design Spec + +## Context + +WeCode currently has all UI strings in French. The decision is to switch the entire app to English — UI labels, error messages, editor hints, lesson content, changelog, and internal type values. No i18n library; strings stay hardcoded, just in English instead of French. + +## Scope + +~150 French strings across ~30 files. Grouped by domain: + +### 1. Types (internal values) + +- `LessonData.difficulty`: `"débutant" | "intermédiaire" | "avancé"` → `"beginner" | "intermediate" | "advanced"` (in `src/lessons/types.ts` + both JSON files) +- `HomeTab`: `"accueil"` → `"home"` (in `src/home/homeStore.ts` + all consumers: `HomeNav.tsx`, `HomeShell.tsx`, `CommandPalette.tsx`, `useCommandPaletteShortcut.ts`) +- `PALETTE_GROUP_LABELS`: French labels → English (in `src/palette/types.ts`) + +### 2. Editor hints (`src/ide/editor/hints.ts`) + +~50 entries with `kindLabel`, `title`, `body`, and `example` fields. All French bodies/titles to English. `kindLabel` values like `"Élément HTML"` → `"HTML Element"`, `"Propriété CSS"` → `"CSS Property"`. + +### 3. Lesson JSON files (`src/lessons/data/`) + +- `html-01-structure.json`: title, description, heading, paragraphs, checkpoint labels +- `challenge-01-simple-page.json`: same fields + +### 4. Changelog (`src/changelog.md`) + +Entire file rewritten in English. Section headings `Ajouté / Amélioré / Corrigé` → `Added / Changed / Fixed`. All bullet points translated. + +### 5. Home components (`src/home/`) + +| File | French strings | +|------|----------------| +| `HomeNav.tsx` | "Accueil", "Leçons" labels | +| `HomeRailFoot.tsx` | "Nouveautés de la", "Docs et aide" | +| `HomeSearch.tsx` | placeholder, aria-label | +| `ContinueCard.tsx` | eyebrow, meta text, buttons | +| `ContinueLessonCard.tsx` | eyebrow, subtitle, button | +| `ContinueSection.tsx` | heading, empty state | +| `RecentProjectsSection.tsx` | heading, empty state, context menu | +| `ChangelogModal.tsx` | title, close button | +| `LessonCard.tsx` | status badges, time label | +| `LessonsListView.tsx` | heading | +| `ChallengesListView.tsx` | heading | + +### 6. IDE shell components (`src/ide/`) + +| File | French strings | +|------|----------------| +| `Toolbar.tsx` | aria-labels, placeholder, tooltips | +| `StatusBar.tsx` | (reads from statusBarFormat) | +| `statusBarFormat.ts` | "Texte", "Sauvegarde auto", "Sauvegardé il y a..." | +| `ConfirmDialog.tsx` | default confirm/cancel labels | +| `Toasts.tsx` | aria-label | +| `FileTree.tsx` | labels, aria-labels, modal titles, context menu items | +| `LessonDock.tsx` | "Leçon"/"Challenge", "points", hint footer | +| `OpenFilesTabs.tsx` | empty state, aria-label | +| `fileErrorMessages.ts` | all 8 error messages | + +### 7. Project modals (`src/projects/ui/`) + +| File | French strings | +|------|----------------| +| `CreateProjectModal.tsx` | title, field labels, placeholder, toast, buttons | +| `DeleteProjectDialog.tsx` | title, body, checkbox, warning, toast, buttons | +| `RenameProjectModal.tsx` | title, body, label, toast, buttons | + +### 8. Palette (`src/palette/`) + +| File | French strings | +|------|----------------| +| `CommandPalette.tsx` | aria-label, placeholder, empty state, footer hints | +| `commands.ts` | command title/subtitle | +| `sources.ts` | pill labels, lesson placeholder toast | + +### 9. Other + +- `src/projects/actions.ts` — error message "Un projet avec ce nom existe déjà" +- `src/projects/paths.ts` — validation messages +- `src/projects/diskAutoSave.ts` — toast message +- `src/lessons/lessonStore.ts` — (no direct French, reads from JSON) + +### 10. Tests to update + +- `src/ide/shell/fileErrorMessages.test.ts` — French assertions → English +- `src/ide/shell/statusBarFormat.test.ts` — French format strings → English +- `src/home/HomeShell.test.tsx` — French text matchers → English +- `src/palette/CommandPalette.test.tsx` — French text matchers → English + +### 11. CLAUDE.md update + +- `"UI strings are in French"` → `"UI strings are in English"` +- Changelog convention: mention both files are now in English + +## What does NOT change + +- `CHANGELOG.md` (root) — already in English +- `README.md` — already in English +- Git conventions (commits, PR bodies, branches) — already in English +- Code identifiers — already in English +- CSS class names — already in English +- Rust backend error messages — already in English + +## Verification + +```bash +npm run typecheck # type changes (difficulty, HomeTab) compile +npm run lint +npm run test # updated test assertions pass +npm run format:check +npm run build +grep -rn "Sauvegard\|Rechercher\|Nouveau\|Supprimer\|Renommer\|Annuler\|Confirmer\|Leçon\|débutant\|Reprends\|Fichiers\|fichier\|dossier\|Accueil" src/ --include="*.ts" --include="*.tsx" | grep -v node_modules | grep -v ".test." +# Should return 0 hits (no French left in source, excluding test context) +``` From 9c7865ac89a0f21c653d74e34934bf678b6e5c92 Mon Sep 17 00:00:00 2001 From: srfwb <264158739+srfwb@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:44:19 +0200 Subject: [PATCH 02/12] docs(plan): translate WeCode to English implementation plan --- .../plans/2026-04-27-translate-to-english.md | 479 ++++++++++++++++++ 1 file changed, 479 insertions(+) create mode 100644 docs/superpowers/plans/2026-04-27-translate-to-english.md diff --git a/docs/superpowers/plans/2026-04-27-translate-to-english.md b/docs/superpowers/plans/2026-04-27-translate-to-english.md new file mode 100644 index 0000000..5dc8cd0 --- /dev/null +++ b/docs/superpowers/plans/2026-04-27-translate-to-english.md @@ -0,0 +1,479 @@ +# Translate WeCode to English — Implementation Plan + +> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. + +**Goal:** Replace every French UI string in WeCode with its English equivalent, including internal type values, lesson content, editor hints, and the in-app changelog. + +**Architecture:** No i18n library. Strings stay hardcoded. File-by-file replacement grouped by domain. Type unions that use French values (`"débutant"`, `"accueil"`) are renamed to English equivalents. Tests updated to match new strings. + +**Tech Stack:** TypeScript, React, Vitest, JSON. + +**Spec:** `docs/superpowers/specs/2026-04-27-translate-to-english-design.md` + +--- + +### Task 1: Internal type values (difficulty + HomeTab) + +**Files:** +- Modify: `src/lessons/types.ts` +- Modify: `src/lessons/data/html-01-structure.json` +- Modify: `src/lessons/data/challenge-01-simple-page.json` +- Modify: `src/home/homeStore.ts` +- Modify: `src/home/HomeShell.tsx` +- Modify: `src/home/rail/HomeNav.tsx` +- Modify: `src/palette/useCommandPaletteShortcut.ts` +- Modify: `src/palette/types.ts` + +- [ ] **Step 1: Change `difficulty` type and JSON values** + +In `src/lessons/types.ts`, change: +```ts +difficulty: "débutant" | "intermédiaire" | "avancé"; +``` +to: +```ts +difficulty: "beginner" | "intermediate" | "advanced"; +``` + +In both JSON files, change `"difficulty": "débutant"` to `"difficulty": "beginner"`. + +- [ ] **Step 2: Change `HomeTab` type and consumers** + +In `src/home/homeStore.ts`, change: +```ts +export type HomeTab = "accueil" | "lessons" | "challenges"; +// and default: +tab: "accueil", +``` +to: +```ts +export type HomeTab = "home" | "lessons" | "challenges"; +// and default: +tab: "home", +``` + +In `src/home/HomeShell.tsx`, replace all `"accueil"` with `"home"`. +In `src/home/rail/HomeNav.tsx`, replace `tab === "accueil"` and `setTab("accueil")` with `"home"`. +In `src/palette/useCommandPaletteShortcut.ts`, if it references `"accueil"`, replace with `"home"`. + +- [ ] **Step 3: Change PALETTE_GROUP_LABELS** + +In `src/palette/types.ts`, change: +```ts +export const PALETTE_GROUP_LABELS: Record = { + jump: "Reprendre", + files: "Fichiers", + commands: "Commandes", + lessons: "Leçons", +}; +``` +to: +```ts +export const PALETTE_GROUP_LABELS: Record = { + jump: "Jump back in", + files: "Files", + commands: "Commands", + lessons: "Lessons", +}; +``` + +- [ ] **Step 4: Run typecheck** + +Run: `npm run typecheck` +Expected: 0 errors (the type changes propagate correctly). + +- [ ] **Step 5: Commit** + +```bash +git add -A +git commit -m "refactor(i18n): switch internal type values from French to English" +``` + +--- + +### Task 2: Home components + +**Files:** +- Modify: `src/home/rail/HomeNav.tsx` +- Modify: `src/home/rail/HomeRailFoot.tsx` +- Modify: `src/home/sections/HomeSearch.tsx` +- Modify: `src/home/sections/ContinueCard.tsx` +- Modify: `src/home/sections/ContinueLessonCard.tsx` +- Modify: `src/home/sections/ContinueSection.tsx` +- Modify: `src/home/sections/RecentProjectsSection.tsx` +- Modify: `src/home/sections/ChangelogModal.tsx` +- Modify: `src/home/sections/LessonCard.tsx` +- Modify: `src/home/sections/LessonsListView.tsx` +- Modify: `src/home/sections/ChallengesListView.tsx` + +Translation table for this task: + +| French | English | +|--------|---------| +| Accueil | Home | +| Leçons | Lessons | +| Nouveautés de la | What's new in | +| Docs et aide | Docs & help | +| Rechercher un projet, une leçon ou ouvrir un fichier… | Search projects, lessons, or open a file… | +| Rechercher | Search | +| Reprends là où tu t'étais arrêté | Pick up where you left off | +| fichier(s) | file(s) | +| ligne(s) | line(s) | +| modifié | edited | +| Continuer | Continue | +| Ouvrir le dossier | Open folder | +| Aucun projet en cours | No active project | +| Ouvre ou crée un projet pour le voir apparaître ici. | Open or create a project to see it here. | +| Projets récents | Recent projects | +| Pas encore de projets | No projects yet | +| La liste de tes projets récents arrivera avec le système de projets. | Your recent projects will appear here. | +| Nouveau projet | New project | +| Rechercher… | Search… | +| Nouveautés | What's new | +| Fermer | Close | +| Toutes les leçons | All lessons | +| Tous les challenges | All challenges | +| ✓ terminé | ✓ completed | +| ● en cours | ● in progress | +| pas commencé | not started | +| ✓ réussi | ✓ passed | +| pas tenté | not attempted | +| Leçon · checkpoint | Lesson · checkpoint | +| Challenge · checkpoint | Challenge · checkpoint | +| Ouvrir {name} | Open {name} | +| Ouvrir | Open | +| Ouvrir le dossier | Open folder | +| Renommer | Rename | +| Supprimer | Delete | + +- [ ] **Step 1: Translate all Home component strings** + +Go through each file and replace every French string with its English equivalent using the table above. Preserve the same structure — just change the text. + +- [ ] **Step 2: Run typecheck + lint** + +Run: `npm run typecheck && npm run lint` + +- [ ] **Step 3: Commit** + +```bash +git add src/home/ +git commit -m "refactor(i18n): translate Home components to English" +``` + +--- + +### Task 3: IDE shell components + +**Files:** +- Modify: `src/ide/shell/Toolbar.tsx` +- Modify: `src/ide/shell/statusBarFormat.ts` +- Modify: `src/ide/shell/ConfirmDialog.tsx` +- Modify: `src/ide/shell/Toasts.tsx` +- Modify: `src/ide/shell/fileErrorMessages.ts` +- Modify: `src/ide/tree/FileTree.tsx` +- Modify: `src/ide/dock/LessonDock.tsx` +- Modify: `src/ide/preview/PreviewBar.tsx` (if French strings exist) + +Translation table: + +| French | English | +|--------|---------| +| Retour à l'accueil | Back to home | +| Rechercher fichiers, commandes… | Search files, commands… | +| Annuler — bientôt disponible | Undo — coming soon | +| Formater — bientôt disponible | Format — coming soon | +| Texte | Text | +| Sauvegarde auto | Auto-save | +| Sauvegardé à l'instant | Saved just now | +| Sauvegardé il y a {n} s | Saved {n}s ago | +| Sauvegardé il y a {n} min | Saved {n}m ago | +| Sauvegardé il y a {n} h | Saved {n}h ago | +| Confirmer | Confirm | +| Annuler | Cancel | +| Fermer la notification | Close notification | +| Fichiers | Files | +| Nouveau fichier | New file | +| Aucun fichier | No files | +| Sauvegarde auto active | Auto-save active | +| Renommer | Rename | +| Supprimer le fichier | Delete file | +| Supprimer {path} ? Cette action est irréversible. | Delete {path}? This action cannot be undone. | +| Supprimer | Delete | +| Valider | Submit | +| nom du fichier | filename | +| Le nom ne peut pas contenir « / ». | Name cannot contain "/". | +| Caractère interdit dans le nom. | Forbidden character in name. | +| Le nom ne peut pas contenir « . » ou « .. ». | Name cannot contain "." or "..". | +| Le nom contient un caractère interdit... | Name contains a forbidden character... | +| Nom de fichier invalide. | Invalid filename. | +| Utilise « / » pour séparer les dossiers... | Use "/" to separate folders... | +| Un fichier du même nom existe déjà. | A file with this name already exists. | +| Ce fichier n'existe plus. | This file no longer exists. | +| Impossible de renommer un dossier. | Cannot rename a directory. | +| Impossible de supprimer un dossier directement. | Cannot delete a directory directly. | +| Leçon / Challenge | Lesson / Challenge | +| points | points | +| Survole un mot-clé dans l'éditeur pour une explication rapide. | Hover a keyword in the editor for a quick explanation. | +| Aucun fichier ouvert | No file open | +| Fermer {filename} | Close {filename} | + +- [ ] **Step 1: Translate all IDE shell strings** + +- [ ] **Step 2: Run typecheck + lint** + +- [ ] **Step 3: Commit** + +```bash +git add src/ide/ +git commit -m "refactor(i18n): translate IDE shell components to English" +``` + +--- + +### Task 4: Project modals + actions + +**Files:** +- Modify: `src/projects/ui/CreateProjectModal.tsx` +- Modify: `src/projects/ui/DeleteProjectDialog.tsx` +- Modify: `src/projects/ui/RenameProjectModal.tsx` +- Modify: `src/projects/actions.ts` +- Modify: `src/projects/paths.ts` +- Modify: `src/projects/diskAutoSave.ts` + +Translation table: + +| French | English | +|--------|---------| +| Choisis un emplacement | Choose a location | +| Nouveau projet | New project | +| Nom du projet | Project name | +| Mon super projet | My awesome project | +| Modèle | Template | +| Emplacement | Location | +| Changer… | Change… | +| Annuler | Cancel | +| Création… / Créer | Creating… / Create | +| Projet « {name} » créé. | Project "{name}" created. | +| Supprimer le projet | Delete project | +| Le projet {name} sera retiré de ta liste. | Project {name} will be removed from your list. | +| Aussi supprimer le dossier et tous ses fichiers sur le disque | Also delete the folder and all files from disk | +| Cette action est irréversible. Les fichiers seront définitivement supprimés. | This action cannot be undone. Files will be permanently deleted. | +| Suppression… | Deleting… | +| Supprimer le dossier | Delete folder | +| Retirer | Remove | +| Projet « {name} » et son dossier supprimés. | Project "{name}" and its folder deleted. | +| Projet « {name} » retiré de WeCode. | Project "{name}" removed from WeCode. | +| Renommer le projet | Rename project | +| Le dossier sur disque garde son nom d'origine — seule l'étiquette dans WeCode change. | The folder on disk keeps its original name — only the label in WeCode changes. | +| Nouveau nom | New name | +| Renommage… / Renommer | Renaming… / Rename | +| Projet renommé en « {name} ». | Project renamed to "{name}". | +| Un projet avec ce nom existe déjà dans ce dossier. | A project with this name already exists in this folder. | +| Un projet avec le nom « {name} » existe déjà. | A project named "{name}" already exists. | +| Le nom ne peut pas être vide. | Name cannot be empty. | +| Le nom est trop long (max 80 caractères). | Name is too long (max 80 characters). | +| Le nom ne peut pas contenir : \\ / : * ? " < > \| | Name cannot contain: \\ / : * ? " < > \| | +| Nom invalide. | Invalid name. | +| « {name} » est un nom réservé par Windows. | "{name}" is a Windows reserved name. | +| Impossible de sauvegarder {path} sur le disque. | Failed to save {path} to disk. | + +- [ ] **Step 1: Translate all project modal and action strings** + +- [ ] **Step 2: Run typecheck + lint** + +- [ ] **Step 3: Commit** + +```bash +git add src/projects/ +git commit -m "refactor(i18n): translate project modals and actions to English" +``` + +--- + +### Task 5: Palette + +**Files:** +- Modify: `src/palette/CommandPalette.tsx` +- Modify: `src/palette/commands.ts` +- Modify: `src/palette/sources.ts` + +| French | English | +|--------|---------| +| Palette de commandes | Command palette | +| Rechercher un projet, une leçon ou ouvrir un fichier… | Search projects, lessons, or open a file… | +| Rechercher | Search | +| Effacer la recherche | Clear search | +| Aucun résultat pour « {query} ». | No results for "{query}". | +| naviguer | navigate | +| ouvrir | open | +| fermer | close | +| ↵ ouvrir | ↵ open | +| Nouveau projet | New project | +| Partir d'un modèle | From a template | +| commande | command | +| projet | project | +| fichier | file | +| leçon | lesson | +| Parcours de leçons | Lesson path | +| Bientôt disponible | Coming soon | + +- [ ] **Step 1: Translate all palette strings** + +- [ ] **Step 2: Commit** + +```bash +git add src/palette/ +git commit -m "refactor(i18n): translate command palette to English" +``` + +--- + +### Task 6: Editor hints + +**Files:** +- Modify: `src/ide/editor/hints.ts` + +This is the largest single file (~400 lines). Every `HTML_HINTS` and `CSS_HINTS` entry has French `kindLabel`, `title`, and `body` fields. + +- [ ] **Step 1: Translate all hint entries** + +Change `kindLabel`: +- `"Élément HTML"` → `"HTML Element"` +- `"Attribut HTML"` → `"HTML Attribute"` +- `"Propriété CSS"` → `"CSS Property"` + +For each entry, translate the `body` field from French to English. Keep `title` as the technical term (already in English: `html`, `head`, `body`, `class`, etc.). Translate the French body text to concise English equivalents. + +Example: +```ts +// Before: +html: { kind: "element", kindLabel: "Élément HTML", title: "", body: "La racine de tout document HTML.", example: "..." }, +// After: +html: { kind: "element", kindLabel: "HTML Element", title: "", body: "The root of every HTML document.", example: "..." }, +``` + +- [ ] **Step 2: Commit** + +```bash +git add src/ide/editor/hints.ts +git commit -m "refactor(i18n): translate editor hover hints to English" +``` + +--- + +### Task 7: Lesson JSON content + +**Files:** +- Modify: `src/lessons/data/html-01-structure.json` +- Modify: `src/lessons/data/challenge-01-simple-page.json` + +- [ ] **Step 1: Translate lesson content** + +`html-01-structure.json`: +- title: "HTML Page Structure" +- description: "Learn the basic tags: html, head, body, title, and paragraph." +- heading: "Build the skeleton" +- paragraphs: translate the instructional text +- checkpoint labels: "Add a `` tag", "Add a `` tag", etc. + +`challenge-01-simple-page.json`: +- title: "Build a simple page" +- description: "A heading, a paragraph, and a background color — your turn." +- heading: "Objective" +- paragraph: translate +- checkpoint labels: "Add an `

` heading", "Add a `

` paragraph", "Set a background color" + +- [ ] **Step 2: Commit** + +```bash +git add src/lessons/data/ +git commit -m "refactor(i18n): translate lesson and challenge content to English" +``` + +--- + +### Task 8: In-app changelog + +**Files:** +- Modify: `src/changelog.md` + +- [ ] **Step 1: Rewrite changelog in English** + +Replace the entire content with English equivalents. Section headings: `Added` / `Changed` / `Fixed` (matching the root `CHANGELOG.md` style). Translate every bullet point. + +- [ ] **Step 2: Commit** + +```bash +git add src/changelog.md +git commit -m "refactor(i18n): translate in-app changelog to English" +``` + +--- + +### Task 9: Update tests + +**Files:** +- Modify: `src/ide/shell/fileErrorMessages.test.ts` +- Modify: `src/ide/shell/statusBarFormat.test.ts` +- Modify: `src/home/HomeShell.test.tsx` +- Modify: `src/palette/CommandPalette.test.tsx` + +- [ ] **Step 1: Update test assertions to match English strings** + +In `fileErrorMessages.test.ts`: French assertion strings → English (e.g., `"caractère interdit"` → `"forbidden character"` or whatever the new English message contains). + +In `statusBarFormat.test.ts`: `"Sauvegarde auto"` → `"Auto-save"`, `"Sauvegardé à l'instant"` → `"Saved just now"`, etc. + +In `HomeShell.test.tsx`: `screen.getByText(/Bienvenue dans/i)` → `screen.getByText(/Pick up where/i)` or whatever the new heading is. `screen.getByRole("heading", { name: /Projets récents/i })` → `"Recent projects"`. + +In `CommandPalette.test.tsx`: French matchers → English. + +- [ ] **Step 2: Run all tests** + +Run: `npm test` +Expected: all 154+ tests pass. + +- [ ] **Step 3: Commit** + +```bash +git add -A +git commit -m "test(i18n): update test assertions for English strings" +``` + +--- + +### Task 10: CLAUDE.md update + final verification + +**Files:** +- Modify: `CLAUDE.md` (project-level, local) + +- [ ] **Step 1: Update CLAUDE.md** + +Change `UI strings are in **French**.` to `UI strings are in **English**.` + +Update the changelog convention to note both `CHANGELOG.md` and `src/changelog.md` are in English. + +- [ ] **Step 2: Run full CI** + +```bash +npm run typecheck +npm run lint +npm run format:check +npm test +npm run build +``` + +- [ ] **Step 3: Grep for remaining French** + +```bash +grep -rn "Sauvegard\|Rechercher\|Nouveau\|Supprimer\|Renommer\|Annuler\|Confirmer\|Leçon\|débutant\|Reprends\|Fichiers\|dossier\|Accueil\|Parcours\|Bientôt\|Fermer\|Ouvrir le" src/ --include="*.ts" --include="*.tsx" --include="*.json" --include="*.md" +``` + +Expected: 0 hits. If any remain, fix them. + +- [ ] **Step 4: Commit** + +```bash +git commit -m "docs(i18n): update CLAUDE.md for English-only UI" +``` From dca04d0659cf215748f0ccb78be3fffee81314b8 Mon Sep 17 00:00:00 2001 From: srfwb <264158739+srfwb@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:54:36 +0200 Subject: [PATCH 03/12] refactor(i18n): switch internal type values from French to English --- src/home/HomeShell.tsx | 2 +- src/home/homeStore.ts | 4 ++-- src/home/rail/HomeNav.tsx | 4 ++-- src/lessons/data/challenge-01-simple-page.json | 2 +- src/lessons/data/html-01-structure.json | 2 +- src/lessons/types.ts | 2 +- src/palette/types.ts | 10 +++++----- 7 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/home/HomeShell.tsx b/src/home/HomeShell.tsx index b549b85..ea6fd9d 100644 --- a/src/home/HomeShell.tsx +++ b/src/home/HomeShell.tsx @@ -17,7 +17,7 @@ export function HomeShell() {

- {tab === "accueil" && ( + {tab === "home" && ( <> diff --git a/src/home/homeStore.ts b/src/home/homeStore.ts index 1cfe640..c674665 100644 --- a/src/home/homeStore.ts +++ b/src/home/homeStore.ts @@ -1,6 +1,6 @@ import { create } from "zustand"; -export type HomeTab = "accueil" | "lessons" | "challenges"; +export type HomeTab = "home" | "lessons" | "challenges"; interface HomeState { tab: HomeTab; @@ -8,6 +8,6 @@ interface HomeState { } export const useHomeStore = create((set) => ({ - tab: "accueil", + tab: "home", setTab: (tab) => set({ tab }), })); diff --git a/src/home/rail/HomeNav.tsx b/src/home/rail/HomeNav.tsx index e844d2f..6ecb1f6 100644 --- a/src/home/rail/HomeNav.tsx +++ b/src/home/rail/HomeNav.tsx @@ -21,8 +21,8 @@ export function HomeNav() { } label="Accueil" - active={tab === "accueil"} - onClick={() => setTab("accueil")} + active={tab === "home"} + onClick={() => setTab("home")} /> } diff --git a/src/lessons/data/challenge-01-simple-page.json b/src/lessons/data/challenge-01-simple-page.json index 2920e24..33075ce 100644 --- a/src/lessons/data/challenge-01-simple-page.json +++ b/src/lessons/data/challenge-01-simple-page.json @@ -3,7 +3,7 @@ "type": "challenge", "title": "Crée une page simple", "description": "Un titre, un paragraphe et une couleur de fond — à toi de jouer.", - "difficulty": "débutant", + "difficulty": "beginner", "estimatedMinutes": 10, "tags": ["html", "css"], "allowFileOps": true, diff --git a/src/lessons/data/html-01-structure.json b/src/lessons/data/html-01-structure.json index 5e49bfa..96f0e6c 100644 --- a/src/lessons/data/html-01-structure.json +++ b/src/lessons/data/html-01-structure.json @@ -3,7 +3,7 @@ "type": "lesson", "title": "La structure d'une page HTML", "description": "Découvre les balises de base : html, head, body, titre et paragraphe.", - "difficulty": "débutant", + "difficulty": "beginner", "estimatedMinutes": 10, "tags": ["html"], "allowFileOps": false, diff --git a/src/lessons/types.ts b/src/lessons/types.ts index 4ef2c9c..c692ea8 100644 --- a/src/lessons/types.ts +++ b/src/lessons/types.ts @@ -22,7 +22,7 @@ export interface LessonData { type: "lesson" | "challenge"; title: string; description: string; - difficulty: "débutant" | "intermédiaire" | "avancé"; + difficulty: "beginner" | "intermediate" | "advanced"; estimatedMinutes: number; tags: string[]; allowFileOps: boolean; diff --git a/src/palette/types.ts b/src/palette/types.ts index 7fd6b79..42e8b29 100644 --- a/src/palette/types.ts +++ b/src/palette/types.ts @@ -35,13 +35,13 @@ export const PALETTE_GROUP_ORDER: readonly PaletteGroupKey[] = [ "lessons", ]; -// Centralised user-facing labels (French). Keep in one place so the overlay +// Centralised user-facing labels. Keep in one place so the overlay // and the tests can agree. export const PALETTE_GROUP_LABELS: Record = { - jump: "Reprendre", - files: "Fichiers", - commands: "Commandes", - lessons: "Leçons", + jump: "Jump back in", + files: "Files", + commands: "Commands", + lessons: "Lessons", }; // Soft palette shared with project cards / template cards so glyphs read at a From e46777c9e5401c8c29d0ecb7f57af2b66431515f Mon Sep 17 00:00:00 2001 From: srfwb <264158739+srfwb@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:56:28 +0200 Subject: [PATCH 04/12] refactor(i18n): translate Home components to English --- src/home/rail/HomeNav.tsx | 4 ++-- src/home/rail/HomeRailFoot.tsx | 4 ++-- src/home/sections/ChallengesListView.tsx | 2 +- src/home/sections/ChangelogModal.tsx | 4 ++-- src/home/sections/ContinueCard.tsx | 12 ++++++------ src/home/sections/ContinueLessonCard.tsx | 6 +++--- src/home/sections/ContinueSection.tsx | 6 +++--- src/home/sections/HomeSearch.tsx | 4 ++-- src/home/sections/LessonCard.tsx | 8 ++++---- src/home/sections/LessonsListView.tsx | 2 +- src/home/sections/RecentProjectsSection.tsx | 10 +++++----- 11 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/home/rail/HomeNav.tsx b/src/home/rail/HomeNav.tsx index 6ecb1f6..e8c4d18 100644 --- a/src/home/rail/HomeNav.tsx +++ b/src/home/rail/HomeNav.tsx @@ -20,13 +20,13 @@ export function HomeNav() {
diff --git a/src/home/sections/ContinueLessonCard.tsx b/src/home/sections/ContinueLessonCard.tsx index 22b4371..5db545e 100644 --- a/src/home/sections/ContinueLessonCard.tsx +++ b/src/home/sections/ContinueLessonCard.tsx @@ -14,12 +14,12 @@ export function ContinueLessonCard({ lesson, doneCount, totalCount }: Props) {

{lesson.title}

- {lesson.type === "lesson" ? "Leçon" : "Challenge"} · checkpoint {doneCount} /{" "} + {lesson.type === "lesson" ? "Lesson" : "Challenge"} · checkpoint {doneCount} /{" "} {totalCount}

@@ -30,7 +30,7 @@ export function ContinueLessonCard({ lesson, doneCount, totalCount }: Props) { className="home-btn home-btn--primary" onClick={() => useLessonStore.getState().startLesson(lesson.id)} > - Continuer + Continue
diff --git a/src/home/sections/ContinueSection.tsx b/src/home/sections/ContinueSection.tsx index 0028c60..88c93cf 100644 --- a/src/home/sections/ContinueSection.tsx +++ b/src/home/sections/ContinueSection.tsx @@ -32,7 +32,7 @@ export function ContinueSection() { return (
-

Reprends là où tu t'étais arrêté

+

Pick up where you left off

{lessonInProgress ? ( } - title="Aucun projet en cours" - subtitle="Ouvre ou crée un projet pour le voir apparaître ici." + title="No project in progress" + subtitle="Open or create a project to see it appear here." /> )}
diff --git a/src/home/sections/HomeSearch.tsx b/src/home/sections/HomeSearch.tsx index ea43d16..9c2370c 100644 --- a/src/home/sections/HomeSearch.tsx +++ b/src/home/sections/HomeSearch.tsx @@ -16,8 +16,8 @@ export function HomeSearch() { diff --git a/src/home/sections/LessonCard.tsx b/src/home/sections/LessonCard.tsx index ae1d46c..2e3fc4f 100644 --- a/src/home/sections/LessonCard.tsx +++ b/src/home/sections/LessonCard.tsx @@ -23,17 +23,17 @@ export function LessonCard({ type="button" className={`lesson-card${status === "in-progress" ? " lesson-card--in-progress" : ""}`} onClick={onStart} - aria-label={`Commencer ${lesson.title}`} + aria-label={`Start ${lesson.title}`} >
{status === "completed" && ( - ✓ terminé + ✓ completed )} {status === "in-progress" && ( - ● en cours + ● in progress )} {status === "not-started" && ( - pas commencé + not started )} ~{lesson.estimatedMinutes} min
diff --git a/src/home/sections/LessonsListView.tsx b/src/home/sections/LessonsListView.tsx index 1d944ae..89a5048 100644 --- a/src/home/sections/LessonsListView.tsx +++ b/src/home/sections/LessonsListView.tsx @@ -15,7 +15,7 @@ export function LessonsListView() { return (
-

Toutes les leçons

+

All lessons

{completed} / {LESSONS.length} diff --git a/src/home/sections/RecentProjectsSection.tsx b/src/home/sections/RecentProjectsSection.tsx index c77697b..18481dc 100644 --- a/src/home/sections/RecentProjectsSection.tsx +++ b/src/home/sections/RecentProjectsSection.tsx @@ -23,7 +23,7 @@ export function RecentProjectsSection() { return (
-

Projets récents

+

Recent projects

{projects.length > 0 && {projects.length}}
{projects.length > 0 ? ( @@ -35,8 +35,8 @@ export function RecentProjectsSection() { ) : ( } - title="Pas encore de projets" - subtitle="La liste de tes projets récents arrivera avec le système de projets." + title="No projects yet" + subtitle="Your recent projects will appear here once you create or open one." /> )} {menu && ( @@ -46,11 +46,11 @@ export function RecentProjectsSection() { onClose={() => setMenu(null)} items={[ { - label: "Nouveau projet", + label: "New project", onSelect: () => openCreate({ templateId: "html-css" }), }, { - label: "Rechercher…", + label: "Search…", onSelect: () => openPalette(), }, ]} From 76ff33c8abecc367b8308e6b7fac10acf6364a7d Mon Sep 17 00:00:00 2001 From: srfwb <264158739+srfwb@users.noreply.github.com> Date: Mon, 27 Apr 2026 08:59:10 +0200 Subject: [PATCH 05/12] refactor(i18n): translate IDE shell components to English --- src/ide/dock/LessonDock.tsx | 8 +++--- src/ide/preview/PreviewBar.tsx | 8 +++--- src/ide/shell/ConfirmDialog.tsx | 4 +-- src/ide/shell/Toasts.tsx | 2 +- src/ide/shell/Toolbar.tsx | 14 +++++------ src/ide/shell/fileErrorMessages.ts | 18 +++++++------- src/ide/shell/statusBarFormat.ts | 16 ++++++------ src/ide/tree/FileTree.tsx | 40 +++++++++++++++--------------- 8 files changed, 55 insertions(+), 55 deletions(-) diff --git a/src/ide/dock/LessonDock.tsx b/src/ide/dock/LessonDock.tsx index 6307113..2a2e7a1 100644 --- a/src/ide/dock/LessonDock.tsx +++ b/src/ide/dock/LessonDock.tsx @@ -33,11 +33,11 @@ export function LessonDock() { {currentStepIndex + 1} - {lesson.type === "lesson" ? "Leçon" : "Challenge"} · {step.heading} + {lesson.type === "lesson" ? "Lesson" : "Challenge"} · {step.heading}
- {doneCount} / {totalCount} points + {doneCount} / {totalCount} pts
@@ -53,7 +53,7 @@ export function LessonDock() { - Survole un mot-clé dans l'éditeur pour une explication rapide. + Hover a keyword in the editor for a quick explanation.

@@ -101,7 +101,7 @@ function CheckpointRow({ }) { const cls = `cp${status === "done" ? " done" : status === "active" ? " active" : ""}`; const metaCls = `meta${status === "active" ? " live" : ""}`; - const metaText = status === "done" ? "fait" : status === "active" ? "vérification…" : "—"; + const metaText = status === "done" ? "done" : status === "active" ? "checking…" : "—"; return (
diff --git a/src/ide/preview/PreviewBar.tsx b/src/ide/preview/PreviewBar.tsx index 34c3142..36b5298 100644 --- a/src/ide/preview/PreviewBar.tsx +++ b/src/ide/preview/PreviewBar.tsx @@ -16,8 +16,8 @@ export function PreviewBar({ onRefresh, path }: PreviewBarProps) { type="button" className="t-btn" onClick={onRefresh} - title="Recharger l'aperçu" - aria-label="Recharger l'aperçu" + title="Reload preview" + aria-label="Reload preview" > -
+
-
+
{DEVICE_ORDER.map((d) => (
-
+
- Rechercher fichiers, commandes… + Search files, commands… Ctrl K
@@ -33,8 +33,8 @@ export function Toolbar() {
From 7ea09ea5459e4a4a916fbea51912850a09a0d4d7 Mon Sep 17 00:00:00 2001 From: srfwb <264158739+srfwb@users.noreply.github.com> Date: Mon, 27 Apr 2026 09:01:11 +0200 Subject: [PATCH 06/12] refactor(i18n): translate project modals and actions to English --- src/projects/actions.ts | 4 ++-- src/projects/diskAutoSave.ts | 2 +- src/projects/paths.ts | 10 +++++----- src/projects/ui/CreateProjectModal.tsx | 20 ++++++++++---------- src/projects/ui/DeleteProjectDialog.tsx | 16 ++++++++-------- src/projects/ui/RenameProjectModal.tsx | 12 ++++++------ 6 files changed, 32 insertions(+), 32 deletions(-) diff --git a/src/projects/actions.ts b/src/projects/actions.ts index 22865ed..a3f8cae 100644 --- a/src/projects/actions.ts +++ b/src/projects/actions.ts @@ -41,7 +41,7 @@ export function renameProject(id: string, newName: string): void { const duplicate = useProjectStore .getState() .projects.some((p) => p.id !== id && p.name.toLowerCase() === name.toLowerCase()); - if (duplicate) throw new Error(`Un projet avec le nom « ${name} » existe déjà.`); + if (duplicate) throw new Error(`A project named "${name}" already exists.`); useProjectStore.getState().upsert({ ...project, name }); } @@ -68,7 +68,7 @@ async function createProjectImpl(input: { const projectPath = joinChild(parentDir, name); const exists = await invoke("fs_path_exists", { path: projectPath }); if (exists) { - throw new Error(`Un projet avec ce nom existe déjà dans ce dossier.`); + throw new Error(`A project with this name already exists in this folder.`); } await invoke("fs_ensure_dir", { dirPath: projectPath }); diff --git a/src/projects/diskAutoSave.ts b/src/projects/diskAutoSave.ts index 075cc1a..0e91fba 100644 --- a/src/projects/diskAutoSave.ts +++ b/src/projects/diskAutoSave.ts @@ -67,7 +67,7 @@ export function attachDiskAutoSave(vfs: VirtualFS): AutoSaveHandle { } } catch (err) { console.error("disk autosave job failed", relPath, err); - toast.error(`Impossible de sauvegarder ${relPath} sur le disque.`); + toast.error(`Failed to save ${relPath} to disk.`); } } const paths = vfs.listFiles(); diff --git a/src/projects/paths.ts b/src/projects/paths.ts index 603c1b1..a68eeaa 100644 --- a/src/projects/paths.ts +++ b/src/projects/paths.ts @@ -37,17 +37,17 @@ const WINDOWS_RESERVED_NAMES = new Set([ export function validateProjectName(raw: string): string { const name = raw.trim(); - if (!name) return "Le nom ne peut pas être vide."; - if (name.length > 80) return "Le nom est trop long (max 80 caractères)."; + if (!name) return "Name cannot be empty."; + if (name.length > 80) return "Name is too long (max 80 characters)."; if (FORBIDDEN_CHARS.test(name)) { - return 'Le nom ne peut pas contenir : \\ / : * ? " < > |'; + return 'Name cannot contain: \\ / : * ? " < > |'; } - if (name === "." || name === "..") return "Nom invalide."; + if (name === "." || name === "..") return "Invalid name."; // Windows reserved names are case-insensitive and refuse extensions too // (CON.txt is as broken as CON). Strip the extension for the comparison. const base = name.split(".")[0] ?? name; if (WINDOWS_RESERVED_NAMES.has(base.toUpperCase())) { - return `« ${name} » est un nom réservé par Windows.`; + return `"${name}" is a name reserved by Windows.`; } return ""; } diff --git a/src/projects/ui/CreateProjectModal.tsx b/src/projects/ui/CreateProjectModal.tsx index c8d3ef1..7db7116 100644 --- a/src/projects/ui/CreateProjectModal.tsx +++ b/src/projects/ui/CreateProjectModal.tsx @@ -42,7 +42,7 @@ export function CreateProjectModal({ initialTemplateId }: Props) { const picked = await openDialog({ directory: true, multiple: false, - title: "Choisis un emplacement", + title: "Choose a location", ...(parentDir ? { defaultPath: parentDir } : {}), }); if (typeof picked === "string") setParentDir(picked); @@ -57,7 +57,7 @@ export function CreateProjectModal({ initialTemplateId }: Props) { setSubmitting(true); try { await createProject({ name: name.trim(), templateId, parentDir }); - toast.success(`Projet « ${name.trim()} » créé.`); + toast.success(`Project "${name.trim()}" created.`); closeAll(); } catch (err) { const message = err instanceof Error ? err.message : String(err); @@ -79,22 +79,22 @@ export function CreateProjectModal({ initialTemplateId }: Props) { onSubmit={submit} >
- Nouveau projet + New project
- Modèle + Template
{TEMPLATES.map((t) => (
diff --git a/src/projects/ui/DeleteProjectDialog.tsx b/src/projects/ui/DeleteProjectDialog.tsx index f390c81..77389a4 100644 --- a/src/projects/ui/DeleteProjectDialog.tsx +++ b/src/projects/ui/DeleteProjectDialog.tsx @@ -26,8 +26,8 @@ export function DeleteProjectDialog({ projectId }: Props) { await deleteProject(projectId, { removeFromDisk }); toast.success( removeFromDisk - ? `Projet « ${project.name} » et son dossier supprimés.` - : `Projet « ${project.name} » retiré de WeCode.`, + ? `Project "${project.name}" and its folder deleted.` + : `Project "${project.name}" removed from WeCode.`, ); closeAll(); } catch (err) { @@ -52,10 +52,10 @@ export function DeleteProjectDialog({ projectId }: Props) { aria-labelledby="delete-project-title" >
- Supprimer le projet + Delete project
- Le projet {project.name} sera retiré de ta liste. + Project {project.name} will be removed from your list.
{project.path}
@@ -65,17 +65,17 @@ export function DeleteProjectDialog({ projectId }: Props) { checked={removeFromDisk} onChange={(e) => setRemoveFromDisk(e.target.checked)} /> - Aussi supprimer le dossier et tous ses fichiers sur le disque + Also delete the folder and all its files from disk {removeFromDisk && (
- Cette action est irréversible. Les fichiers seront définitivement supprimés. + This action cannot be undone. The files will be permanently deleted.
)}
diff --git a/src/projects/ui/RenameProjectModal.tsx b/src/projects/ui/RenameProjectModal.tsx index b543e2d..3abb468 100644 --- a/src/projects/ui/RenameProjectModal.tsx +++ b/src/projects/ui/RenameProjectModal.tsx @@ -31,7 +31,7 @@ export function RenameProjectModal({ projectId }: Props) { setSubmitting(true); try { renameProject(projectId, name.trim()); - toast.success(`Projet renommé en « ${name.trim()} ».`); + toast.success(`Project renamed to "${name.trim()}".`); closeAll(); } catch (err) { toast.error(err instanceof Error ? err.message : String(err)); @@ -51,13 +51,13 @@ export function RenameProjectModal({ projectId }: Props) { onSubmit={submit} >
- Renommer le projet + Rename project
- Le dossier sur disque garde son nom d'origine — seule l'étiquette dans WeCode change. + The folder on disk keeps its original name — only the label in WeCode changes.