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..399caf2 --- /dev/null +++ b/docs/superpowers/plans/2026-04-27-translate-to-english.md @@ -0,0 +1,502 @@ +# 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" +``` 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..211f9e2 --- /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) +``` diff --git a/src/changelog.md b/src/changelog.md index a86eed8..5900fa9 100644 --- a/src/changelog.md +++ b/src/changelog.md @@ -1,90 +1,90 @@ -# Nouveautés +# What's new -## v0.3.0 — 27 avril 2026 +## v0.3.0 — April 27, 2026 -### Ajouté +### Added -- **Système de leçons** — des leçons guidées pas à pas avec validation en temps réel. L'app vérifie ton code à chaque modification et coche les objectifs au fur et à mesure. -- **Système de challenges** — des défis libres où tu dois atteindre un objectif sans instructions détaillées. Tu peux créer et supprimer des fichiers. -- **Navigation par onglets dans l'Accueil** — le rail gauche est maintenant un routeur : clique sur Leçons ou Challenges pour voir la liste complète avec ton avancement. -- **Compteur de progression** — le rail affiche combien de leçons tu as terminées (ex : `1 / 2`). -- **Carte « Reprends »** — si tu as une leçon en cours, la carte de reprise la montre en priorité sur les projets. -- **Première leçon** : « La structure d'une page HTML » — apprends ``, ``, ``, `<h1>`, `<p>` avec 5 checkpoints. -- **Premier challenge** : « Crée une page simple » — un titre, un paragraphe et une couleur de fond. -- **Modale changelog** — clique sur « Nouveautés de la vX.Y.Z » dans le rail pour voir l'historique des mises à jour. +- **Lesson system** — step-by-step guided lessons with real-time validation. The app checks your code on every edit and ticks off objectives as you go. +- **Challenge system** — open-ended challenges where you reach a goal without detailed instructions. You can create and delete files. +- **Tab navigation in Home** — the left rail is now a router: click Lessons or Challenges to see the full list with your progress. +- **Progress counter** — the rail shows how many lessons you have completed (e.g. `1 / 2`). +- **"Continue" card** — if you have a lesson in progress, the resume card shows it above your projects. +- **First lesson**: "HTML Page Structure" — learn `<head>`, `<body>`, `<title>`, `<h1>`, `<p>` with 5 checkpoints. +- **First challenge**: "Build a simple page" — a heading, a paragraph, and a background color. +- **Changelog modal** — click "What's new in vX.Y.Z" in the rail to view the update history. -### Amélioré +### Changed -- Les raccourcis clavier (Ctrl+Tab, Ctrl+W) fonctionnent en mode leçon. Ctrl+N est bloqué (pas de création de fichier en leçon). -- Le dock leçon est un vrai `<button>` accessible au clavier (avec `aria-expanded`). -- Le menu « Ouvrir » en clic droit sur un fichier fonctionne en mode leçon. -- La barre de statut affiche la progression des checkpoints au lieu de la version en mode leçon. +- Keyboard shortcuts (Ctrl+Tab, Ctrl+W) work in lesson mode. Ctrl+N is blocked (no file creation during a lesson). +- The lesson dock is a proper accessible `<button>` (with `aria-expanded`). +- The "Open" right-click context menu on a file works in lesson mode. +- The status bar shows checkpoint progress instead of the version in lesson mode. -### Corrigé +### Fixed -- Le dock se replie correctement (le contenu ne déborde plus sous le header). -- Le contour ambre ne s'affiche plus sur les boutons de sélection de template. -- La propriété CSS dans les règles de validation est correctement échappée (pas de faux match avec des caractères spéciaux). +- The dock collapses correctly (content no longer overflows below the header). +- The amber outline no longer appears on template selection buttons. +- The CSS property in validation rules is properly escaped (no false match with special characters). --- -## v0.2.0-2 — 25 avril 2026 +## v0.2.0-2 — April 25, 2026 -### Ajouté +### Added -- **Palette de commandes** (Ctrl+K / ⌘K) — rechercher un projet, un fichier ou une commande depuis l'Accueil. -- **Menu contextuel** sur la section Projets récents — clic droit pour créer un nouveau projet ou lancer une recherche. -- **Logo pixel-perfect** — le logo WeCode correspond maintenant exactement au design original dans toutes ses variantes. -- Bannière et carte Open Graph pour le repo GitHub. +- **Command palette** (Ctrl+K / ⌘K) — search for a project, a file, or a command from Home. +- **Context menu** on the Recent Projects section — right-click to create a new project or start a search. +- **Pixel-perfect logo** — the WeCode logo now exactly matches the original design in all its variants. +- Open Graph banner and card for the GitHub repo. -### Amélioré +### Changed -- L'app ne plante plus si le dossier du dernier projet a été supprimé pendant qu'elle était fermée. -- Un toast d'erreur apparaît si la sauvegarde automatique échoue (disque plein, permissions, etc.). -- Renommer un projet avec un nom déjà pris par un autre est maintenant interdit. -- Les onglets de l'ancien projet sont vidés quand tu passes à un autre. -- Le bouton « Ouvrir le dossier » fonctionne sur Windows (utilise `revealItemInDir` au lieu de `openPath`). -- L'animation d'apparition des cartes projets est plus rapide. -- Les items de navigation du rail sont maintenant accessibles au clavier. -- Le focus clavier est visible sur les onglets de l'éditeur. +- The app no longer crashes if the last project's folder was deleted while it was closed. +- An error toast appears if autosave fails (disk full, permissions, etc.). +- Renaming a project to a name already taken by another is now blocked. +- The previous project's tabs are cleared when switching to another project. +- The "Open folder" button works on Windows (uses `revealItemInDir` instead of `openPath`). +- The project card appear animation is faster. +- Rail navigation items are now keyboard-accessible. +- Keyboard focus is visible on editor tabs. -### Corrigé +### Fixed -- Le contour orange parasite ne s'affiche plus sur les boutons de template ni sur le menu contextuel. -- Le menu natif du navigateur (Retour, Actualiser, Inspecter) est bloqué partout dans l'app. +- The stray orange outline no longer appears on template buttons or the context menu. +- The browser's native menu (Back, Refresh, Inspect) is blocked everywhere in the app. --- -## v0.2.0-1 — 23 avril 2026 +## v0.2.0-1 — April 23, 2026 -### Ajouté +### Added -- **Page d'accueil** — vue par défaut avec la liste des projets récents, la section « Continue le parcours » et les modèles de projet. -- **Projets sur disque** — chaque projet est un vrai dossier sous `~/Documents/WeCode/`. -- Créer un projet depuis un modèle (Dossier vierge ou HTML + CSS) avec un sélecteur de dossier natif. -- Renommer et supprimer un projet (avec option de suppression du dossier sur le disque). -- Menu contextuel sur les cartes projets : Ouvrir, Ouvrir le dossier, Renommer, Supprimer. -- Rechargement automatique quand un éditeur externe modifie les fichiers du projet actif. -- Aperçu mobile / desktop dans le preview. -- Infobulles sur les mots-clés HTML et CSS dans l'éditeur. -- Notifications toast et boîtes de confirmation. -- Raccourcis clavier : `Ctrl+N` (nouveau fichier), `Ctrl+W` (fermer l'onglet), `Ctrl+Tab` / `Ctrl+Shift+Tab` (naviguer entre les onglets). -- Barre de statut avec sauvegarde auto, latence preview, langue, encodage, ligne:colonne. -- Refonte visuelle basée sur un design system oklch (Geist + JetBrains Mono). +- **Home page** — default view with the recent projects list, the "Continue your path" section, and project templates. +- **Disk-backed projects** — each project is a real folder under `~/Documents/WeCode/`. +- Create a project from a template (Blank folder or HTML + CSS) with a native folder picker. +- Rename and delete a project (with an option to remove the folder from disk). +- Context menu on project cards: Open, Open folder, Rename, Delete. +- Automatic reload when an external editor modifies files in the active project. +- Mobile / desktop toggle in the preview panel. +- Hover tooltips on HTML and CSS keywords in the editor. +- Toast notifications and confirmation dialogs. +- Keyboard shortcuts: `Ctrl+N` (new file), `Ctrl+W` (close tab), `Ctrl+Tab` / `Ctrl+Shift+Tab` (navigate between tabs). +- Status bar with autosave, preview latency, language, encoding, line:column. +- Visual redesign based on an oklch design system (Geist + JetBrains Mono). -### Amélioré +### Changed -- Latence preview réduite de ~1 s à ~100 ms. +- Preview latency reduced from ~1 s to ~100 ms. -### Corrigé +### Fixed -- Fuites de listeners lors des recharges HMR. -- Le panneau d'aperçu ne reste plus blanc sans mises à jour. +- Listener leaks on HMR reloads. +- The preview panel no longer stays blank without updates. --- -## v0.1.0 — 20 avril 2026 +## v0.1.0 — April 20, 2026 -### Ajouté +### Added -- Première version : shell Tauri v2, éditeur trois panneaux (arborescence, éditeur CodeMirror 6, aperçu live) avec un système de fichiers virtuel en mémoire. +- First release: Tauri v2 shell, three-panel editor (file tree, CodeMirror 6 editor, live preview) with an in-memory virtual file system. diff --git a/src/home/HomeShell.test.tsx b/src/home/HomeShell.test.tsx index 6802e74..0dc1679 100644 --- a/src/home/HomeShell.test.tsx +++ b/src/home/HomeShell.test.tsx @@ -19,10 +19,10 @@ describe("HomeShell", () => { }); }); - it("renders the welcome headline and the Projets récents section header", () => { + it("renders the welcome headline and the Recent projects section header", () => { render(<HomeShell />); - expect(screen.getByText(/Bienvenue dans/i)).toBeTruthy(); - expect(screen.getByRole("heading", { name: /Projets récents/i })).toBeTruthy(); + expect(screen.getByText(/Welcome to/i)).toBeTruthy(); + expect(screen.getByRole("heading", { name: /Recent projects/i })).toBeTruthy(); }); }); 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() { <div className="home-shell"> <HomeRail /> <main className="home-main"> - {tab === "accueil" && ( + {tab === "home" && ( <> <HomeSearch /> <ContinueSection /> 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<HomeState>((set) => ({ - tab: "accueil", + tab: "home", setTab: (tab) => set({ tab }), })); diff --git a/src/home/rail/HomeGreeting.tsx b/src/home/rail/HomeGreeting.tsx index 8e9f84d..73ac144 100644 --- a/src/home/rail/HomeGreeting.tsx +++ b/src/home/rail/HomeGreeting.tsx @@ -1,5 +1,5 @@ function formatDate(now: Date): string { - return new Intl.DateTimeFormat("fr-FR", { + return new Intl.DateTimeFormat("en-US", { weekday: "long", day: "numeric", month: "long", @@ -12,7 +12,7 @@ export function HomeGreeting() { <div className="home-greet"> <div className="home-greet-date">{dateLabel}</div> <h1 className="home-greet-title"> - Bienvenue dans <span className="home-greet-accent">WeCode</span>. + Welcome to <span className="home-greet-accent">WeCode</span>. </h1> </div> ); diff --git a/src/home/rail/HomeNav.tsx b/src/home/rail/HomeNav.tsx index e844d2f..e8c4d18 100644 --- a/src/home/rail/HomeNav.tsx +++ b/src/home/rail/HomeNav.tsx @@ -20,13 +20,13 @@ export function HomeNav() { <nav className="home-nav" aria-label="Navigation principale"> <HomeNavItem icon={<IconHome />} - label="Accueil" - active={tab === "accueil"} - onClick={() => setTab("accueil")} + label="Home" + active={tab === "home"} + onClick={() => setTab("home")} /> <HomeNavItem icon={<IconBook />} - label="Leçons" + label="Lessons" active={tab === "lessons"} onClick={() => setTab("lessons")} counter={`${completed} / ${LESSONS.length}`} diff --git a/src/home/rail/HomeRailFoot.tsx b/src/home/rail/HomeRailFoot.tsx index 76c6cbf..5c8e034 100644 --- a/src/home/rail/HomeRailFoot.tsx +++ b/src/home/rail/HomeRailFoot.tsx @@ -12,11 +12,11 @@ export function HomeRailFoot() { <div className="home-rail-sep" aria-hidden="true" /> <button type="button" className="home-rail-link" onClick={() => setShowChangelog(true)}> <IconShield /> - Nouveautés de la {APP_VERSION} + What's new in {APP_VERSION} </button> <a className="home-rail-link"> <IconHelp /> - Docs et aide + Docs & help </a> {showChangelog && <ChangelogModal onClose={() => setShowChangelog(false)} />} </div> diff --git a/src/home/sections/ChallengesListView.tsx b/src/home/sections/ChallengesListView.tsx index 5c5f6f5..b8119e4 100644 --- a/src/home/sections/ChallengesListView.tsx +++ b/src/home/sections/ChallengesListView.tsx @@ -15,7 +15,7 @@ export function ChallengesListView() { return ( <section className="home-section"> <div className="home-sec-head"> - <h3>Tous les challenges</h3> + <h3>All challenges</h3> <span className="home-sec-count"> {completed} / {CHALLENGES.length} </span> diff --git a/src/home/sections/ChangelogModal.tsx b/src/home/sections/ChangelogModal.tsx index b84ea90..d7d4cdf 100644 --- a/src/home/sections/ChangelogModal.tsx +++ b/src/home/sections/ChangelogModal.tsx @@ -47,7 +47,7 @@ export function ChangelogModal({ onClose }: Props) { aria-labelledby="changelog-title" > <div id="changelog-title" className="modal__title"> - Nouveautés + What's new </div> <div className="cl-body" @@ -55,7 +55,7 @@ export function ChangelogModal({ onClose }: Props) { /> <div className="modal__actions"> <button type="button" onClick={onClose}> - Fermer + Close </button> </div> </div> diff --git a/src/home/sections/ContinueCard.tsx b/src/home/sections/ContinueCard.tsx index 5834568..584520d 100644 --- a/src/home/sections/ContinueCard.tsx +++ b/src/home/sections/ContinueCard.tsx @@ -15,16 +15,16 @@ export function ContinueCard({ project }: Props) { <div className="home-continue-main"> <div className="home-continue-eyebrow"> <span className="home-continue-pulse" aria-hidden="true" /> - Reprends là où tu t'étais arrêté + Pick up where you left off </div> <h2 className="home-continue-title">{project.name}</h2> <p className="home-continue-sub"> <span className="home-continue-meta"> - {project.fileCount} fichier{project.fileCount > 1 ? "s" : ""} · {project.lineCount}{" "} - ligne{project.lineCount > 1 ? "s" : ""} + {project.fileCount} file{project.fileCount > 1 ? "s" : ""} · {project.lineCount} line + {project.lineCount > 1 ? "s" : ""} </span> <span className="home-continue-meta"> - modifié {formatRelativeTime(project.lastOpenedAt)} + edited {formatRelativeTime(project.lastOpenedAt)} </span> </p> </div> @@ -34,7 +34,7 @@ export function ContinueCard({ project }: Props) { className="home-btn home-btn--primary" onClick={() => void openProject(project.id)} > - Continuer + Continue <IconChevronRight /> </button> <button @@ -42,7 +42,7 @@ export function ContinueCard({ project }: Props) { className="home-btn home-btn--ghost" onClick={() => void revealItemInDir(project.path)} > - Ouvrir le dossier + Open folder </button> </div> </div> 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) { <div className="home-continue-main"> <div className="home-continue-eyebrow"> <span className="home-continue-pulse" aria-hidden="true" /> - Reprends là où tu t'étais arrêté + Pick up where you left off </div> <h2 className="home-continue-title">{lesson.title}</h2> <p className="home-continue-sub"> <span className="home-continue-meta"> - {lesson.type === "lesson" ? "Leçon" : "Challenge"} · checkpoint {doneCount} /{" "} + {lesson.type === "lesson" ? "Lesson" : "Challenge"} · checkpoint {doneCount} /{" "} {totalCount} </span> </p> @@ -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 <IconChevronRight /> </button> </div> 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 ( <section className="home-section"> <div className="home-sec-head"> - <h3>Reprends là où tu t'étais arrêté</h3> + <h3>Pick up where you left off</h3> </div> {lessonInProgress ? ( <ContinueLessonCard @@ -45,8 +45,8 @@ export function ContinueSection() { ) : ( <EmptyState icon={<IconClock />} - 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." /> )} </section> 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() { <IconSearch /> <input type="text" - placeholder="Rechercher un projet, une leçon ou ouvrir un fichier…" - aria-label="Rechercher" + placeholder="Search projects, lessons, or open a file…" + aria-label="Search" readOnly tabIndex={-1} /> 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}`} > <div className="lesson-card__header"> {status === "completed" && ( - <span className="lesson-card__badge lesson-card__badge--done">✓ terminé</span> + <span className="lesson-card__badge lesson-card__badge--done">✓ completed</span> )} {status === "in-progress" && ( - <span className="lesson-card__badge lesson-card__badge--active">● en cours</span> + <span className="lesson-card__badge lesson-card__badge--active">● in progress</span> )} {status === "not-started" && ( - <span className="lesson-card__badge lesson-card__badge--todo">pas commencé</span> + <span className="lesson-card__badge lesson-card__badge--todo">not started</span> )} <span className="lesson-card__time">~{lesson.estimatedMinutes} min</span> </div> diff --git a/src/home/sections/LessonPathSection.tsx b/src/home/sections/LessonPathSection.tsx index e016f4f..7eb7fad 100644 --- a/src/home/sections/LessonPathSection.tsx +++ b/src/home/sections/LessonPathSection.tsx @@ -5,12 +5,12 @@ export function LessonPathSection() { return ( <section className="home-section"> <div className="home-sec-head"> - <h3>Continue le parcours</h3> + <h3>Continue your path</h3> </div> <EmptyState icon={<IconSparkle />} - title="Parcours à venir" - subtitle="Les leçons HTML, CSS et JS seront publiées dans les prochaines versions." + title="Coming soon" + subtitle="HTML, CSS and JS lessons will be published in upcoming versions." /> </section> ); 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 ( <section className="home-section"> <div className="home-sec-head"> - <h3>Toutes les leçons</h3> + <h3>All lessons</h3> <span className="home-sec-count"> {completed} / {LESSONS.length} </span> diff --git a/src/home/sections/ProjectCard.tsx b/src/home/sections/ProjectCard.tsx index 34bf1c7..870bf29 100644 --- a/src/home/sections/ProjectCard.tsx +++ b/src/home/sections/ProjectCard.tsx @@ -31,7 +31,7 @@ export function ProjectCard({ project, index = 0 }: Props) { style={{ "--proj-idx": index } as React.CSSProperties} onClick={() => void openProject(project.id)} onContextMenu={onContext} - aria-label={`Ouvrir ${project.name}`} + aria-label={`Open ${project.name}`} > <div className={`home-proj-thumb home-proj-thumb--${project.kind}`}> <span>{project.kind.toUpperCase()}</span> @@ -50,7 +50,7 @@ export function ProjectCard({ project, index = 0 }: Props) { )} </div> <div className="home-proj-when">{formatRelativeTime(project.lastOpenedAt)}</div> - <span className="home-proj-open">ouvrir ↵</span> + <span className="home-proj-open">open ↵</span> </button> {menu && ( <ContextMenu @@ -58,14 +58,14 @@ export function ProjectCard({ project, index = 0 }: Props) { y={menu.y} onClose={() => setMenu(null)} items={[ - { label: "Ouvrir", onSelect: () => void openProject(project.id) }, + { label: "Open", onSelect: () => void openProject(project.id) }, { - label: "Ouvrir le dossier", + label: "Open folder", onSelect: () => void revealItemInDir(project.path), }, - { label: "Renommer", onSelect: () => openRename({ id: project.id }) }, + { label: "Rename", onSelect: () => openRename({ id: project.id }) }, { - label: "Supprimer", + label: "Delete", destructive: true, onSelect: () => openDelete({ id: project.id }), }, 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 ( <section className="home-section" onContextMenu={onSectionContext}> <div className="home-sec-head"> - <h3>Projets récents</h3> + <h3>Recent projects</h3> {projects.length > 0 && <span className="home-sec-count">{projects.length}</span>} </div> {projects.length > 0 ? ( @@ -35,8 +35,8 @@ export function RecentProjectsSection() { ) : ( <EmptyState icon={<IconSparkle />} - 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(), }, ]} 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() { </svg> <span className="lesson-chip"> <span className="num">{currentStepIndex + 1}</span> - {lesson.type === "lesson" ? "Leçon" : "Challenge"} · {step.heading} + {lesson.type === "lesson" ? "Lesson" : "Challenge"} · {step.heading} </span> <div className="progress"> <span> - {doneCount} / {totalCount} points + {doneCount} / {totalCount} pts </span> <ProgressRing value={progress} /> </div> @@ -53,7 +53,7 @@ export function LessonDock() { <circle cx="12" cy="12" r="9" /> <path d="M12 8v4M12 16h.01" /> </svg> - Survole un mot-clé dans l'éditeur pour une explication rapide. + Hover a keyword in the editor for a quick explanation. </p> </div> <div className="checkpoints"> @@ -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 ( <div className={cls}> diff --git a/src/ide/editor/hints.ts b/src/ide/editor/hints.ts index c2bb34e..66486cd 100644 --- a/src/ide/editor/hints.ts +++ b/src/ide/editor/hints.ts @@ -11,289 +11,289 @@ export interface Hint { /** * Seed dictionary. Intentionally partial — new entries are welcome. * Keys are lowercased tag / attribute / property names exactly as they appear - * in the source. Content is in French and pedagogical. + * in the source. Content is in English and pedagogical. */ export const HTML_HINTS: Record<string, Hint> = { html: { kind: "element", - kindLabel: "balise", + kindLabel: "HTML Element", title: "<html>", - body: "La racine de tout document HTML. Tout le reste vit à l'intérieur.", - example: `<html lang="fr">\n …\n</html>`, + body: "The root of every HTML document. Everything else lives inside it.", + example: `<html lang="en">\n …\n</html>`, }, head: { kind: "element", - kindLabel: "balise", + kindLabel: "HTML Element", title: "<head>", - body: "Contient les métadonnées de la page : titre, feuilles de style, polices. Rien n'y est visible à l'écran.", - example: `<head>\n <title>Ma page\n`, + body: "Contains the page's metadata: title, stylesheets, fonts. Nothing here is visible on screen.", + example: `\n My page\n`, }, title: { kind: "element", - kindLabel: "balise", + kindLabel: "HTML Element", title: "", - body: "Le texte qui apparaît dans l'onglet du navigateur et les résultats de recherche.", - example: `<title>Mon premier site`, + body: "The text that appears in the browser tab and search results.", + example: `My first site`, }, body: { kind: "element", - kindLabel: "balise", + kindLabel: "HTML Element", title: "", - body: "Le contenu visible de la page — tout ce qu'un visiteur peut voir.", - example: `\n

Bonjour !

\n`, + body: "The visible content of the page — everything a visitor can see.", + example: `\n

Hello!

\n`, }, meta: { kind: "element", - kindLabel: "balise", + kindLabel: "HTML Element", title: "", - body: "Donne au navigateur des infos sur la page. charset='utf-8' autorise tous les caractères Unicode.", + body: "Gives the browser information about the page. charset='utf-8' enables all Unicode characters.", example: ``, }, link: { kind: "element", - kindLabel: "balise", + kindLabel: "HTML Element", title: "", - body: "Relie la page à une ressource externe — le plus souvent une feuille de style CSS. Auto-fermante.", + body: "Connects the page to an external resource — most often a CSS stylesheet. Self-closing.", example: ``, }, header: { kind: "element", - kindLabel: "balise", + kindLabel: "HTML Element", title: "
", - body: "Conteneur sémantique pour le haut d'une page ou d'une section — souvent un logo et un titre.", - example: `
\n

Mon site

\n
`, + body: "Semantic container for the top of a page or section — often a logo and a title.", + example: `
\n

My site

\n
`, }, nav: { kind: "element", - kindLabel: "balise", + kindLabel: "HTML Element", title: "