330 lines
15 KiB
Markdown
330 lines
15 KiB
Markdown
---
|
|
title: 'Guides pratiques'
|
|
description: 'Recettes pas à pas pour les tâches courantes de contribution : langues, thèmes, déploiement, migrations.'
|
|
slug: 'guides-pratiques'
|
|
weight: 2
|
|
date: 2026-06-16
|
|
---
|
|
|
|
## Ajouter une nouvelle langue
|
|
|
|
> TrainUs propose déjà trois langues aujourd'hui — anglais, français et danois (`da`) — pas seulement la paire EN/FR mise en avant sur le site du projet. Les étapes ci-dessous montrent comment en ajouter une de plus, avec l'espagnol (`es`) comme exemple travaillé.
|
|
|
|
Ajouter une langue demande quatre points de contact : un nouveau fichier de traduction, son enregistrement dans l'initialiseur i18n, une option dans le sélecteur des Réglages, et une entrée dans le journal de décisions.
|
|
|
|
1. `src/i18n/locales/<code>.json` — créer le fichier de traduction
|
|
2. `src/i18n/index.js` — importer et enregistrer la locale
|
|
3. `src/views/SettingsView.vue` — ajouter l'`<option>` dans le sélecteur de langue
|
|
4. `docs/decisions-log.md` — consigner la décision
|
|
|
|
### 1. Créer le fichier de traduction
|
|
|
|
Copiez `src/i18n/locales/en.json` vers `src/i18n/locales/<code>.json`, où `<code>` est le code de langue à deux lettres BCP 47 (par exemple `es`, `de`, `it`).
|
|
|
|
Traduisez chaque valeur de chaîne. Ne changez **aucun** nom de clé. Conservez tous les marqueurs d'interpolation (`{{date}}`, `{{title}}`, etc.) et les entités HTML exactement comme dans la source.
|
|
|
|
### 2. Enregistrer la locale dans i18next
|
|
|
|
Ouvrez `src/i18n/index.js`. Ajoutez un import en haut, à côté des autres :
|
|
|
|
```js
|
|
import es from './locales/es.json'
|
|
```
|
|
|
|
Puis ajoutez la locale à l'objet `resources` dans `i18next.init` :
|
|
|
|
```js
|
|
resources: {
|
|
en: { translation: en },
|
|
fr: { translation: fr },
|
|
da: { translation: da },
|
|
es: { translation: es }, // ← nouveau
|
|
},
|
|
```
|
|
|
|
### 3. Ajouter l'option dans le sélecteur des Réglages
|
|
|
|
Ouvrez `src/views/SettingsView.vue` et repérez l'élément `<select>` avec `id="settings-language"`. Ajoutez une `<option>` après les autres :
|
|
|
|
```html
|
|
<option value="es">Español</option>
|
|
```
|
|
|
|
Utilisez le nom natif de la langue comme libellé (pas le nom anglais), pour que les locuteurs puissent l'identifier sans avoir à lire l'anglais.
|
|
|
|
### 4. Mettre à jour le journal de décisions
|
|
|
|
Ajoutez une entrée dans `docs/decisions-log.md` consignant l'ajout et sa raison.
|
|
|
|
### Liste de vérification
|
|
|
|
- [ ] `src/i18n/locales/<code>.json` créé et entièrement traduit
|
|
- [ ] Import ajouté dans `src/i18n/index.js`
|
|
- [ ] `<option>` ajoutée dans `src/views/SettingsView.vue`
|
|
- [ ] La langue est sélectionnable dans les Réglages et l'interface change correctement
|
|
- [ ] `docs/decisions-log.md` mis à jour
|
|
|
|
---
|
|
|
|
## Ajouter un nouveau thème
|
|
|
|
Un thème est un ensemble nommé de surcharges de variables CSS, scopées à un attribut `[data-theme='<name>']`. L'application lit le thème actif depuis la base de données et pose cet attribut sur `<html>` avant le premier rendu.
|
|
|
|
1. `src/styles/variables.css` — définir le bloc de variables CSS
|
|
2. `src/i18n/locales/{en,fr,da}.json` — ajouter le libellé d'affichage, dans toutes les langues prises en charge
|
|
3. `src/views/SettingsView.vue` — ajouter l'`<option>` dans le sélecteur de thème
|
|
4. Test manuel dans le navigateur
|
|
5. `docs/decisions-log.md` — consigner la décision
|
|
|
|
### 1. Définir les variables CSS
|
|
|
|
Ouvrez `src/styles/variables.css` et ajoutez un nouveau bloc à la fin. Copiez le bloc `:root` comme point de départ et ne changez que les valeurs qui diffèrent du thème clair par défaut.
|
|
|
|
```css
|
|
/* Mon thème */
|
|
[data-theme='<name>'] {
|
|
--bg-color1: ...;
|
|
--bg-color2: ...;
|
|
--front-color1: ...;
|
|
--front-color2: ...;
|
|
--navbar-bg-color: ...;
|
|
--navbar-front-color: ...;
|
|
--navbar-hover-bg-color: ...;
|
|
--navbar-hover-front-color: ...;
|
|
--actionbar-bg-color: ...;
|
|
--actionbar-font-color: ...;
|
|
--border-color: ...;
|
|
--input-bg: ...;
|
|
--input-border: ...;
|
|
--btn-primary-bg: ...;
|
|
--btn-primary-color: ...;
|
|
--btn-danger-bg: ...;
|
|
--btn-danger-color: ...;
|
|
--success-color: ...;
|
|
--warning-color: ...;
|
|
}
|
|
```
|
|
|
|
La valeur du sélecteur (par exemple `trainus`) devient la clé du thème partout ailleurs. Voir la table de référence des variables en bas de cette recette pour savoir ce que chacune affecte.
|
|
|
|
### 2. Ajouter le libellé d'affichage dans toutes les langues prises en charge
|
|
|
|
TrainUs propose déjà trois langues aujourd'hui — anglais, français et danois (voir « Ajouter une nouvelle langue » ci-dessus) — donc le libellé du nouveau thème doit avoir une entrée dans **les trois** fichiers de locale, pas seulement en anglais et en français. Ajoutez `"themeMyName"` à l'objet `"settings"`, après `"themeDark"`, dans chacun :
|
|
|
|
`src/i18n/locales/en.json` :
|
|
|
|
```json
|
|
"themeMyName": "My Theme Label"
|
|
```
|
|
|
|
`src/i18n/locales/fr.json` :
|
|
|
|
```json
|
|
"themeMyName": "Mon thème"
|
|
```
|
|
|
|
`src/i18n/locales/da.json` :
|
|
|
|
```json
|
|
"themeMyName": "Mit tema"
|
|
```
|
|
|
|
Si une langue se retrouve sans la clé, i18next retombe sur `fallbackLng: 'en'` plutôt que de planter — mais ça vaut quand même le coup de le repérer pendant le test manuel ci-dessous, car un libellé resté en anglais dans une autre langue passe facilement inaperçu.
|
|
|
|
### 3. Ajouter l'option dans le sélecteur des réglages
|
|
|
|
Dans `src/views/SettingsView.vue`, repérez le bloc `<select>` du thème (recherchez `settings-theme`). Ajoutez une `<option>` après les autres :
|
|
|
|
```html
|
|
<option value="<name>">{{ $t('settings.themeMyName') }}</option>
|
|
```
|
|
|
|
La `value` doit correspondre au nom du sélecteur défini à l'étape 1.
|
|
|
|
### 4. Test manuel
|
|
|
|
1. `npm run dev`
|
|
2. Ouvrez Réglages → Thème → sélectionnez votre nouveau thème.
|
|
3. Vérifiez : la barre de navigation, le fond, les boutons et les champs s'affichent tous avec la nouvelle palette.
|
|
4. Rechargez la page pour confirmer que le thème persiste (chargé depuis la base de données au démarrage).
|
|
5. Parcourez chaque langue (anglais, français, danois) et vérifiez que le libellé du thème est bien traduit, sans retomber sur l'anglais.
|
|
6. Exportez les réglages puis réimportez-les pour confirmer que la clé du thème survit à l'aller-retour.
|
|
|
|
### 5. Mettre à jour le journal de décisions
|
|
|
|
Ajoutez une entrée dans `docs/decisions-log.md` consignant l'ajout et sa raison.
|
|
|
|
### Référence des variables
|
|
|
|
| Variable | Affecte |
|
|
| ---------------------------- | --------------------------------------- |
|
|
| `--bg-color1` | Fond principal de la page |
|
|
| `--bg-color2` | Fond des cartes / surfaces secondaires |
|
|
| `--front-color1` | Texte principal |
|
|
| `--front-color2` | Texte secondaire / discret |
|
|
| `--navbar-bg-color` | Fond du menu de navigation latéral |
|
|
| `--navbar-front-color` | Texte et icônes du menu latéral |
|
|
| `--navbar-hover-bg-color` | Fond au survol d'un élément de menu |
|
|
| `--navbar-hover-front-color` | Texte au survol d'un élément de menu |
|
|
| `--actionbar-bg-color` | Fond de la barre d'action en haut |
|
|
| `--actionbar-font-color` | Texte et icônes de la barre d'action |
|
|
| `--border-color` | Bordures des cartes et des champs |
|
|
| `--input-bg` | Fond des champs de saisie |
|
|
| `--input-border` | Bordure des champs de saisie |
|
|
| `--btn-primary-bg` | Fond du bouton principal |
|
|
| `--btn-primary-color` | Texte du bouton principal |
|
|
| `--btn-danger-bg` | Fond du bouton danger / suppression |
|
|
| `--btn-danger-color` | Texte du bouton danger |
|
|
| `--success-color` | Messages et indicateurs de succès |
|
|
| `--warning-color` | Messages et indicateurs d'avertissement |
|
|
|
|
### Liste de vérification
|
|
|
|
- [ ] Bloc de variables CSS ajouté dans `src/styles/variables.css`
|
|
- [ ] Libellé d'affichage ajouté dans toutes les langues prises en charge (anglais, français, danois)
|
|
- [ ] `<option>` ajoutée dans `src/views/SettingsView.vue`
|
|
- [ ] Thème sélectionnable, persistant au rechargement, et qui survit à l'export/import
|
|
- [ ] `docs/decisions-log.md` mis à jour
|
|
|
|
---
|
|
|
|
## Ajouter une migration de base de données
|
|
|
|
> **Demandez d'abord.** Selon les règles du projet, `SCHEMA_VERSION` n'est incrémenté qu'une fois un schéma réellement déployé auprès des utilisateurs — et seulement après confirmation avec le responsable du projet. Ajouter une colonne directement dans `SCHEMA_SQL` (dans `src/db/schema.js`) suffit pour les changements en place avant une publication ; aucune des étapes ci-dessous n'est nécessaire dans ce cas. Ne passez par une vraie migration que lorsque des utilisateurs existants ont déjà des données sous l'ancien schéma.
|
|
|
|
Une augmentation de `SCHEMA_VERSION` s'accompagne toujours d'exactement une entrée dans le registre `RELEASES` (`src/db/releases.js`), qui est la source de vérité unique lue à la fois par la migration au démarrage et par la vérification de compatibilité de Restaurer la base de données.
|
|
|
|
1. Augmentez `SCHEMA_VERSION` dans `src/db/schema.js`.
|
|
2. Écrivez le SQL qui fait passer une base de données existante de l'ancienne version à la nouvelle.
|
|
3. Ajoutez l'entrée correspondante dans `RELEASES` dans `src/db/releases.js`.
|
|
4. Testez le chemin de mise à niveau sur une base encore à l'ancienne version.
|
|
5. Mettez à jour la documentation de référence.
|
|
|
|
### 1. Augmenter la version
|
|
|
|
```js
|
|
// src/db/schema.js
|
|
export const SCHEMA_VERSION = 2 // était 1
|
|
```
|
|
|
|
### 2. Écrire le script de migration
|
|
|
|
Un script de migration est du SQL classique — `ALTER TABLE`, remplissage de données, tout ce qui est nécessaire pour amener l'ancien schéma au niveau du nouveau. Il s'exécute dans la même transaction que l'estampillage `schema_version`, donc un échec partiel se rétablit proprement.
|
|
|
|
### 3. Déclarer la release
|
|
|
|
```js
|
|
// src/db/releases.js
|
|
export const RELEASES = [
|
|
{ schemaVersion: 1, appVersion: '1.0.0', dbBreak: false, migrationScript: null },
|
|
{
|
|
schemaVersion: 2,
|
|
appVersion: '1.1.0',
|
|
dbBreak: true,
|
|
migrationScript: `ALTER TABLE exercise ADD COLUMN category TEXT;`,
|
|
},
|
|
]
|
|
```
|
|
|
|
- `dbBreak: false` avec `migrationScript: null` est une entrée « no-op » valide (seule la ligne `schema_version` est estampillée) — à utiliser pour une augmentation de version qui n'a pas besoin de toucher aux données existantes.
|
|
- `dbBreak: true` exige un `migrationScript` non nul ; `getMigrationScripts()` lève une erreur si un script manque pour une release « breaking » dans la plage demandée.
|
|
|
|
### 4. Tester le chemin de mise à niveau
|
|
|
|
Deux chemins de code lisent `RELEASES`, et les deux doivent être vérifiés :
|
|
|
|
- **Migration au démarrage** : ouvrez l'application avec une base existante estampillée à l'ancien `schema_version`. L'application doit afficher l'écran bloquant « téléchargez d'abord une sauvegarde », puis appliquer votre script et continuer avec les données intactes.
|
|
- **Restaurer la base de données** : restaurez une sauvegarde `.sqlite3` prise à l'ancienne version de schéma dans une version de l'application tournant sur la nouvelle — `checkCompatibility()` doit détecter l'écart et exécuter le même script pendant l'étape d'adaptation de la restauration.
|
|
|
|
Voir la [Référence]({{< relref "technical.fr.md" >}}) pour la machine à états complète de la migration et son diagramme de flux, et [Comprendre l'architecture]({{< relref "understanding-the-architecture.fr.md" >}}) pour le pourquoi de cette conception (sauvegarde forcée, migrations transactionnelles, échec sécurisé sur une base plus récente que l'application).
|
|
|
|
### 5. Mettre à jour la documentation
|
|
|
|
- Le tableau du schéma et le diagramme entité-relation dans la [Référence]({{< relref "technical.fr.md" >}})
|
|
- `docs/decisions-log.md`, avec la date, la décision et la justification
|
|
|
|
### Liste de vérification
|
|
|
|
- [ ] Confirmation obtenue du responsable du projet qu'une vraie augmentation de version (pas un changement en place) est justifiée
|
|
- [ ] `SCHEMA_VERSION` augmenté
|
|
- [ ] Entrée `RELEASES` ajoutée avec un `migrationScript` correct (ou `null` pour un no-op)
|
|
- [ ] Migration au démarrage testée sur une base à l'ancienne version
|
|
- [ ] Adaptation de Restaurer la base de données testée sur une sauvegarde à l'ancienne version
|
|
- [ ] Documentation de référence et `docs/decisions-log.md` mis à jour
|
|
|
|
---
|
|
|
|
## Déployer TrainUs
|
|
|
|
TrainUs est un site statique : `npm run build` génère tout ce qu'il faut dans `dist/`, prêt à être copié sur n'importe quel serveur web ou CDN. Le déploiement est unique : chaque nouvelle build remplace la précédente à la racine du site, sans dossiers par version. Le service worker gère les mises à jour de code côté client, et le système de migration au démarrage (voir [Référence]({{< relref "technical.fr.md" >}})) gère les changements de schéma de base de données, si bien que les mêmes données OPFS sont simplement conservées.
|
|
|
|
```bash
|
|
npm run build # génère les fichiers statiques dans dist/
|
|
npm run preview # prévisualiser la build de production en local
|
|
```
|
|
|
|
TrainUs utilise le VFS `opfs-sahpool` (voir la [Référence]({{< relref "technical.fr.md" >}})), qui n'a pas strictement besoin des en-têtes COOP/COEP. Les poser tout de même est recommandé par défense en profondeur, et ils pourraient devenir nécessaires si le VFS utilisé venait à changer — le serveur de développement les pose déjà par précaution :
|
|
|
|
```
|
|
Cross-Origin-Opener-Policy: same-origin
|
|
Cross-Origin-Embedder-Policy: require-corp
|
|
```
|
|
|
|
**Nginx**
|
|
|
|
```nginx
|
|
location / {
|
|
add_header Cross-Origin-Opener-Policy "same-origin";
|
|
add_header Cross-Origin-Embedder-Policy "require-corp";
|
|
}
|
|
```
|
|
|
|
**Apache**
|
|
|
|
```apache
|
|
<IfModule mod_headers.c>
|
|
Header always set Cross-Origin-Opener-Policy "same-origin"
|
|
Header always set Cross-Origin-Embedder-Policy "require-corp"
|
|
</IfModule>
|
|
```
|
|
|
|
**Caddy**
|
|
|
|
```caddy
|
|
header {
|
|
Cross-Origin-Opener-Policy "same-origin"
|
|
Cross-Origin-Embedder-Policy "require-corp"
|
|
}
|
|
```
|
|
|
|
**Netlify** (`public/_headers`)
|
|
|
|
```
|
|
/*
|
|
Cross-Origin-Opener-Policy: same-origin
|
|
Cross-Origin-Embedder-Policy: require-corp
|
|
```
|
|
|
|
**Vercel** (`vercel.json`)
|
|
|
|
```json
|
|
{
|
|
"headers": [
|
|
{
|
|
"source": "/(.*)",
|
|
"headers": [
|
|
{ "key": "Cross-Origin-Opener-Policy", "value": "same-origin" },
|
|
{ "key": "Cross-Origin-Embedder-Policy", "value": "require-corp" }
|
|
]
|
|
}
|
|
]
|
|
}
|
|
```
|
|
|
|
### Liste de vérification
|
|
|
|
- [ ] `npm run build` terminé sans erreur
|
|
- [ ] `dist/` déployé à la racine du site, en remplaçant la build précédente
|
|
- [ ] En-têtes COOP/COEP configurés sur le serveur (recommandé, pas strictement requis pour `opfs-sahpool`)
|
|
- [ ] L'application se charge et OPFS s'initialise correctement
|