Implémentation complète du multilingue FR/EN

- Installation vue-i18n v11 et création des fichiers de traduction (fr.json, en.json)
- Création store locale avec détection hiérarchique (URL > localStorage > navigator)
- Modification des routes avec préfixe /:locale? optionnel
- Toggle FR/EN dans Menu.vue avec synchronisation immédiate
- Traduction de ~200 textes dans 27 composants Vue
- Suppression des labels hardcodés en français côté backend
- Ajout route Kirby catch-all en/(:all?) pour /en/ URLs
- Helper addLocalePrefix() pour préserver locale dans liens dialogs
- Traduction pseudo-élément CSS via data attribute

Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
isUnknown 2026-02-02 18:21:11 +01:00
parent 3af95b1d20
commit 82eb8d88cc
49 changed files with 1079 additions and 295 deletions

View file

@ -1,6 +1,6 @@
<template>
<header class="flex">
<h2 id="tabslist" class="sr-only">Projets</h2>
<h2 id="tabslist" class="sr-only">{{ t('brief.projects') }}</h2>
<Tabs :tabs="tabs" @update:currentTab="changeTab" />
</header>
<section
@ -11,6 +11,7 @@
:aria-label="tabs[0].label"
class="flow"
:class="{ skeleton: isProjectsLoading }"
:data-empty-text="t('projects.none')"
>
<Project
v-for="project in currentProjects"
@ -25,6 +26,7 @@
tabindex="0"
:aria-label="tabs[1].label"
class="flow"
:data-empty-text="t('projects.none')"
>
<Project
v-for="project in archivedProjects"
@ -39,7 +41,9 @@ import Project from './project/Project.vue';
import { useProjectsStore } from '../stores/projects';
import { ref, computed } from 'vue';
import { storeToRefs } from 'pinia';
import { useI18n } from 'vue-i18n';
const { t } = useI18n();
const { projects, currentProjects, archivedProjects, isProjectsLoading } =
storeToRefs(useProjectsStore());
@ -47,13 +51,13 @@ const currentTab = ref('currentProjects');
const tabs = computed(() => {
return [
{
label: 'Projets en cours',
label: t('projects.current'),
id: 'currentProjects',
count: currentProjects.value.length,
isActive: currentTab.value === 'currentProjects',
},
{
label: 'Projets archivés',
label: t('projects.archived'),
id: 'archivedProjects',
count: archivedProjects.value.length,
isActive: currentTab.value === 'archivedProjects',
@ -72,7 +76,7 @@ section {
min-height: calc(100vh - 8.5rem);
}
section:not(.skeleton):empty::after {
content: 'Aucun projet pour le moment';
content: attr(data-empty-text);
position: absolute;
inset: 0;
display: grid;