diff --git a/electron/components/contextMenu.js b/electron/components/contextMenu.js
index 8dccce26311ad7b2464df2db7c4ea2adf3579c63..055612fb4086525f6c0b36bcb9554c72b983373a 100644
--- a/electron/components/contextMenu.js
+++ b/electron/components/contextMenu.js
@@ -1,4 +1,5 @@
 const { BrowserWindow } = require('electron');
+const { t } = require('./utils');
 
 const isDev = process.env.IS_DEV === 'true';
 
@@ -8,11 +9,11 @@ function getTemplateFromContext(callback, data) {
     };
     const standardActions = [
         {
-            label: 'Undo',
+            label: t('menu.edit.undo'),
             click: () => onClick('undo'),
         },
         {
-            label: 'Redo',
+            label: t('menu.edit.redo'),
             click: () => onClick('redo'),
         },
     ];
@@ -22,60 +23,60 @@ function getTemplateFromContext(callback, data) {
     if (data.context === 'flow') {
         menu.push(
             {
-                label: 'Ajouter',
+                label: t('context.add'),
                 submenu: getPagesFromContext(onClick, { position: data.position }, 'addPage', data.context),
             },
             {
-                label: 'Coller ici',
+                label: t('context.pasteHere'),
                 click: () => onClick('paste', { position: data.position }),
-            },
+            }
         );
     } else if (data.context === 'page' || data.context === 'activity' || data.context === 'pageWithQuestion') {
         if (isDev) {
             menu.push({
-                label: 'Ajouter',
+                label: t('context.add'),
                 submenu: getContentFromContext(onClick, { id: data.id }, data.context),
             });
         }
         menu.push(
             {
-                label: 'Insérer après',
+                label: t('context.insertAfter'),
                 submenu: getPagesFromContext(onClick, { id: data.id }, 'insertAfter', data.context),
             },
             {
-                label: 'Insérer avant',
+                label: t('context.insertBefore'),
                 submenu: getPagesFromContext(onClick, { id: data.id }, 'insertBefore', data.context),
             },
             {
-                label: 'Dupliquer',
+                label: t('context.duplicate'),
                 click: () => onClick('duplicatePage', { id: data.id }),
             },
             {
-                label: 'Supprimer',
+                label: t('context.delete'),
                 click: () => onClick('deleteNode', { id: data.id }),
             },
             {
                 type: 'separator',
             },
             {
-                label: 'Copier',
+                label: t('menu.edit.copy'),
                 click: () => onClick('copy', { id: data.id }),
             },
             {
-                label: 'Intervertir avec le suivant',
+                label: t('context.swapNext'),
                 click: () => onClick('swapNodeWithNext', { id: data.id }),
             },
             {
-                label: 'Intervertir avec le précédent',
+                label: t('context.swapPrevious'),
                 click: () => onClick('swapNodeWithPrevious', { id: data.id }),
-            },
+            }
         );
     } else if (data.context === 'content') {
         menu.push(
             {
-                label: 'Supprimer',
+                label: t('context.delete'),
                 click: () => onClick('deleteContent', { pageId: data.pageId, id: data.id }),
-            },
+            }
             // {
             //     label: 'Dupliquer',
             //     click: () => onClick('duplicateContent', { pageId: data.pageId, id: data.id })
@@ -84,45 +85,45 @@ function getTemplateFromContext(callback, data) {
     } else if (data.context === 'chapter') {
         menu.push(
             {
-                label: 'Insérer à la fin',
+                label: t('context.insertEnd'),
                 submenu: getPagesFromContext(onClick, { id: data.id }, 'insertAtEnd', data.context),
             },
             {
-                label: 'Insérer au début',
+                label: t('context.insertStart'),
                 submenu: getPagesFromContext(onClick, { id: data.id }, 'insertAtStart', data.context),
             },
             {
-                label: 'Intervertir avec le précédent',
+                label: t('context.swapPrevious'),
                 click: () => onClick('swapChapterWithPrevious', { id: data.id }),
             },
             {
-                label: 'Intervertir avec le suivant',
+                label: t('context.swapNext'),
                 click: () => onClick('swapChapterWithNext', { id: data.id }),
             },
             {
-                label: 'Supprimer',
+                label: t('context.delete'),
                 click: () => onClick('deleteNode', { id: data.id }),
             },
             {
-                label: 'Copier le chapitre',
+                label: t('context.copyChapter'),
                 click: () => onClick('copyChapter', { id: data.id }),
-            },
+            }
         );
     } else if (data.context === 'epoc') {
         menu.push({
-            label: 'Ajouter un nouveau chapitre',
+            label: t('context.addChapter'),
             click: () => onClick('addChapter'),
         });
     } else if (data.context === 'selection') {
         menu.push(
             {
-                label: 'Supprimer',
+                label: t('context.delete'),
                 click: () => onClick('deleteSelection', { selection: data.selection }),
             },
             {
-                label: 'Copier',
+                label: t('menu.edit.copy'),
                 click: () => onClick('copySelection', { selection: data.selection }),
-            },
+            }
         );
     }
 
@@ -134,42 +135,42 @@ function getTemplateFromContext(callback, data) {
 function getPagesFromContext(onClick, data, event, context) {
     const contents = [
         {
-            label: 'Ajouter une page Texte',
+            label: t('context.addText'),
             click: () => onClick(event, { type: 'text', ...data }),
         },
         {
-            label: 'Ajouter une page Vidéo',
+            label: t('context.addVideo'),
             click: () => onClick(event, { type: 'video', ...data }),
         },
         {
-            label: 'Ajouter une page Audio',
+            label: t('context.addAudio'),
             click: () => onClick(event, { type: 'audio', ...data }),
         },
     ];
 
     const questions = [
         {
-            label: 'Ajouter une évaluation QCM',
+            label: t('context.addChoice'),
             click: () => onClick(event, { type: 'choice', ...data }),
         },
         {
-            label: 'Ajouter une évaluation Drag & Drop',
+            label: t('context.addDragAndDrop'),
             click: () => onClick(event, { type: 'drag-and-drop', ...data }),
         },
         {
-            label: 'Ajouter une évaluation Reorder',
+            label: t('context.addReorder'),
             click: () => onClick(event, { type: 'reorder', ...data }),
         },
         {
-            label: 'Ajouter une évaluation Swipe',
+            label: t('context.addSwipe'),
             click: () => onClick(event, { type: 'swipe', ...data }),
         },
         {
-            label: 'Ajouter une évaluation Liste Déroulante',
+            label: t('context.addDropdown'),
             click: () => onClick(event, { type: 'dropdown-list', ...data }),
         },
         {
-            label: 'Ajouter une évaluation personnalisée',
+            label: t('context.addCustom'),
             click: () => onClick(event, { type: 'custom', ...data }),
         },
     ];
@@ -180,7 +181,7 @@ function getPagesFromContext(onClick, data, event, context) {
         const addChapter = [
             { type: 'separator' },
             {
-                label: 'Ajouter un chapitre',
+                label: t('context.addChapter'),
                 click: () => onClick('addChapter'),
             },
         ];
@@ -193,42 +194,42 @@ function getPagesFromContext(onClick, data, event, context) {
 function getContentFromContext(onClick, data, context) {
     const questions = [
         {
-            label: 'Ajouter une question QCM',
+            label: t('context.addQuestion'),
             click: () => onClick('addContent', { type: 'choice', ...data }),
         },
         {
-            label: 'Ajouter une question Drag & Drop',
+            label: t('context.addDragAndDrop'),
             click: () => onClick('addContent', { type: 'drag-and-drop', ...data }),
         },
         {
-            label: 'Ajouter une question Reorder',
+            label: t('context.addReorder'),
             click: () => onClick('addContent', { type: 'reorder', ...data }),
         },
         {
-            label: 'Ajouter une question Swipe',
+            label: t('context.addSwipe'),
             click: () => onClick('addContent', { type: 'swipe', ...data }),
         },
         {
-            label: 'Ajouter une question Liste Déroulante',
+            label: t('context.addDropdownList'),
             click: () => onClick('addContent', { type: 'dropdown-list', ...data }),
         },
         {
-            label: 'Ajouter une question personnalisée',
+            label: t('context.addCustom'),
             click: () => onClick('addContent', { type: 'custom', ...data }),
         },
     ];
 
     const contents = [
         {
-            label: 'Ajouter un contenu Texte',
+            label: t('context.addText'),
             click: () => onClick('addContent', { type: 'text', ...data }),
         },
         {
-            label: 'Ajouter un contenu Vidéo',
+            label: t('context.addVideo'),
             click: () => onClick('addContent', { type: 'video', ...data }),
         },
         {
-            label: 'Ajouter un contenu Audio',
+            label: t('context.addAudio'),
             click: () => onClick('addContent', { type: 'audio', ...data }),
         },
     ];
diff --git a/electron/components/ipc.js b/electron/components/ipc.js
index 3518880914d6202ae7373437dde2625c77c8dd62..f72a91fc026b83f561cc2e8a28d4e1f56c3d6485 100644
--- a/electron/components/ipc.js
+++ b/electron/components/ipc.js
@@ -32,7 +32,7 @@ const copyData = {
  * Setup ipc listeners that are received from renderer process
  * @param targetWindow
  */
-const setupIpcListener = function (targetWindow) {
+const setupIpcListener = function (targetWindow, setupMenu) {
     function ipcGuard(handler) {
         return (event, ...args) => {
             if (targetWindow.isDestroyed() || event.sender !== targetWindow.webContents) return;
@@ -251,11 +251,19 @@ const setupIpcListener = function (targetWindow) {
 
     ipcMain.on(
         'setSettings',
-        ipcGuard(async (event, data) => {
+        ipcGuard(async (_event, data) => {
+            const { spellcheck, locale } = electronStore.get('settings');
             electronStore.set('settings', data);
 
-            targetWindow.webContents.session.setSpellCheckerEnabled(electronStore.get('settings.spellcheck'));
-            targetWindow.webContents.reload();
+            if (data.spellcheck !== spellcheck) {
+                targetWindow.webContents.session.setSpellCheckerEnabled(spellcheck);
+                targetWindow.webContents.reload();
+            }
+
+            if (data.locale !== locale) {
+                setupMenu();
+                targetWindow.webContents.reload();
+            }
         }),
     );
 };
diff --git a/electron/components/utils.js b/electron/components/utils.js
index 1c8d0ab9ac5bfe95a620195353de6fdf26562aaf..bc843f7f204f5b5aa4217336c149eb4fc600efe1 100644
--- a/electron/components/utils.js
+++ b/electron/components/utils.js
@@ -1,3 +1,39 @@
+const translations = require('../../i18n/en/translation.json');
+const Store = require('electron-store');
+const electronStore = new Store();
+
+/**
+ * Get translation for a given key
+ * @param {string} key - Translation key (e.g., 'menu.app.label')
+ * @returns {string} - Translated string or key if not found
+ */
+module.exports.t = function (key) {
+    const lang = electronStore.get('settings.locale') || 'en';
+    const translationFile = require(`../../i18n/${lang}/translation.json`);
+
+    // Split key by dots and traverse the translation object
+    const keys = key.split('.');
+    let result = translationFile;
+
+    for (const k of keys) {
+        if (result && Object.prototype.hasOwnProperty.call(result, k)) {
+            result = result[k];
+        } else {
+            // If key not found, try English as fallback
+            result = translations;
+            for (const k of keys) {
+                if (result && Object.prototype.hasOwnProperty.call(result, k)) {
+                    result = result[k];
+                } else {
+                    return key; // Return the key itself if translation not found
+                }
+            }
+            return result;
+        }
+    }
+    return result;
+};
+
 /**
  * Wait for all promise to have resolve
  * @param promises
diff --git a/electron/components/window.js b/electron/components/window.js
index 48dc1d050f3e0f7b533ea094d3b7e292b6bb011d..c5557c73e7e3dfb99f7f34ff5ab8ca6484673741 100644
--- a/electron/components/window.js
+++ b/electron/components/window.js
@@ -13,6 +13,7 @@ const {
     createPreview,
     createGlobalPreview,
 } = require('./file');
+const { t } = require('./utils');
 
 const Store = require('electron-store');
 const electronStore = new Store();
@@ -111,7 +112,7 @@ const setupWindow = function (window, filepath) {
 
 const createNewWindow = () => {
     const newWindow = createMainWindow();
-    setupIpcListener(newWindow);
+    setupIpcListener(newWindow, setupMenu);
     setupWindow(newWindow);
 
     newWindow.show();
@@ -124,44 +125,45 @@ const createNewWindow = () => {
 const setupMenu = () => {
     const mainMenuTemplate = [
         {
-            label: 'App',
+            label: t('menu.app.label'),
             submenu: [
-                { label: 'À propos', role: 'about' },
+                { label: t('menu.app.about'), role: 'about' },
                 {
-                    label: 'Quitter',
+                    label: t('menu.app.quit'),
                     accelerator: 'CmdOrCtrl+Q',
                     click: function () {
                         app.quit();
                     },
                 },
+                { type: 'separator' },
             ],
         },
         {
-            label: 'Fichier',
+            label: t('menu.file.label'),
             submenu: [
                 {
-                    label: 'Nouveau',
+                    label: t('menu.file.new'),
                     accelerator: 'CmdOrCtrl+N',
                     click: function () {
                         sendToFrontend(BrowserWindow.getFocusedWindow(), 'epocProjectNew');
                     },
                 },
                 {
-                    label: 'Nouvelle fenêtre',
+                    label: t('menu.file.newWindow'),
                     click: function () {
                         ipcMain.emit('newWindow');
                     },
                 },
                 { type: 'separator' },
                 {
-                    label: 'Ouvrir',
+                    label: t('menu.file.open'),
                     accelerator: 'CmdOrCtrl+O',
                     click: function () {
                         sendToFrontend(BrowserWindow.getFocusedWindow(), 'epocProjectPicked', pickEpocProject());
                     },
                 },
                 {
-                    label: 'Ouvrir dans une nouvelle fenêtre',
+                    label: t('menu.file.openInNewWindow'),
                     click: () => {
                         const newWindow = createNewWindow();
                         const project = pickEpocProject();
@@ -171,7 +173,7 @@ const setupMenu = () => {
                     },
                 },
                 {
-                    label: 'Projet récents',
+                    label: t('menu.file.latest'),
                     submenu: [
                         ...getRecentFiles().map((project) => {
                             return {
@@ -184,7 +186,7 @@ const setupMenu = () => {
                     ],
                 },
                 {
-                    label: 'Importer un fichier .epoc',
+                    label: t('menu.file.import'),
                     click: async function () {
                         sendToFrontend(BrowserWindow.getFocusedWindow(), 'epocImportPicked');
                         const project = await pickEpocToImport();
@@ -222,7 +224,7 @@ const setupMenu = () => {
                 { type: 'separator' },
                 {
                     id: 'save',
-                    label: 'Sauvegarder',
+                    label: t('menu.file.save'),
                     accelerator: 'CmdOrCtrl+S',
                     enabled: !!(
                         store.state.projects[BrowserWindow.getFocusedWindow()?.id] &&
@@ -240,7 +242,7 @@ const setupMenu = () => {
                 },
                 {
                     id: 'saveAs',
-                    label: 'Sauvegarder sous...',
+                    label: t('menu.file.saveAs'),
                     accelerator: 'Shift+CmdOrCtrl+S',
                     enabled: !!(
                         store.state.projects[BrowserWindow.getFocusedWindow()?.id] &&
@@ -261,40 +263,40 @@ const setupMenu = () => {
             ],
         },
         {
-            label: 'Édition',
+            label: t('menu.edit.label'),
             submenu: [
                 {
-                    label: 'Annuler',
+                    label: t('menu.edit.undo'),
                     accelerator: 'CmdOrCtrl+Z',
                     click: function () {
                         sendToFrontend(BrowserWindow.getFocusedWindow(), 'undo');
                     },
                 },
                 {
-                    label: 'Rétablir',
+                    label: t('menu.edit.redo'),
                     accelerator: process.platform === 'darwin' ? 'Shift+CmdOrCtrl+Z' : 'CmdOrCtrl+Y',
                     click: function () {
                         sendToFrontend(BrowserWindow.getFocusedWindow(), 'redo');
                     },
                 },
                 { type: 'separator' },
-                { label: 'Couper', accelerator: 'CmdOrCtrl+X', role: 'cut' },
-                { label: 'Copier', accelerator: 'CmdOrCtrl+C', role: 'copy' },
-                { label: 'Coller', accelerator: 'CmdOrCtrl+V', role: 'paste' },
-                { label: 'Tout sélectionner', accelerator: 'CmdOrCtrl+A', role: 'selectAll' },
+                { label: t('menu.edit.cut'), accelerator: 'CmdOrCtrl+X', role: 'cut' },
+                { label: t('menu.edit.copy'), accelerator: 'CmdOrCtrl+C', role: 'copy' },
+                { label: t('menu.edit.paste'), accelerator: 'CmdOrCtrl+V', role: 'paste' },
+                { label: t('menu.edit.selectAll'), accelerator: 'CmdOrCtrl+A', role: 'selectAll' },
             ],
         },
         {
-            label: 'Aperçu',
+            label: t('menu.preview.label'),
             submenu: [
                 {
-                    label: "Lancer l'aperçu",
+                    label: t('menu.preview.start'),
                     click: function () {
                         createPreview(store.state.projects[BrowserWindow.getFocusedWindow().id].workdir);
                     },
                 },
                 {
-                    label: "Lancer l'aperçu global",
+                    label: t('menu.preview.global'),
                     click: function () {
                         createGlobalPreview(store.state.projects[BrowserWindow.getFocusedWindow().id].workdir);
                     },
@@ -302,31 +304,33 @@ const setupMenu = () => {
             ],
         },
         {
-            label: 'Aide',
+            label: t('menu.help.label'),
             submenu: [
                 {
-                    label: 'Documentation',
+                    label: t('menu.help.documentation'),
                     click: async function () {
                         await shell.openExternal('https://epoc.inria.fr/guide/user/getting-started/');
                     },
                 },
                 {
-                    label: 'Signaler un problème',
+                    label: t('menu.help.reportIssue'),
                     click: async function () {
                         const isDev = process.env.IS_DEV === 'true';
 
-                        const emailSubject = 'Aide éditeur';
+                        const emailSubject = t('menu.help.mailSubject');
                         const emailRecipient = 'ill-ePoc-contact@inria.fr';
                         let emailBody = '';
                         if (isDev) {
                             const appVersion = app.getVersion();
                             emailBody = encodeURIComponent(
-                                `Version: ${appVersion}\n---\n\nDécrivez votre problème ci-dessous:\n\n`,
+                                `Version: ${appVersion}\n---\n\n${t('menu.help.mailBody')}\n\n`,
                             );
                         } else {
                             const appInfo = require('../../dist/appInfo.json');
                             emailBody = encodeURIComponent(
-                                `Version: ${appInfo.version}\nBuild: ${appInfo.buildNumber}\n ---\n\nDécrivez votre problème ci-dessous:\n\n`,
+                                `Version: ${appInfo.version}\nBuild: ${appInfo.buildNumber}\n ---\n\n${t(
+                                    'menu.help.mailBody',
+                                )}\n\n`,
                             );
                         }
 
@@ -337,14 +341,14 @@ const setupMenu = () => {
                 },
                 { type: 'separator' },
                 {
-                    label: 'Outils de développement',
+                    label: t('menu.devTools'),
                     accelerator: 'CmdOrCtrl+D',
                     click: function () {
                         BrowserWindow.getFocusedWindow().webContents.toggleDevTools();
                     },
                 },
                 {
-                    label: 'Recharger',
+                    label: t('menu.reload'),
                     accelerator: 'CmdOrCtrl+R',
                     click: function () {
                         BrowserWindow.getFocusedWindow().webContents.reload();
@@ -372,4 +376,5 @@ module.exports = {
     createMainWindow,
     setupWindow,
     createNewWindow,
+    setupMenu,
 };
diff --git a/electron/electron.js b/electron/electron.js
index 3b37581e908ac15fc8599b9cd0701eaa82dc9886..1bd8b675646ebc82ad3937df1b6330e7f65ff4df 100644
--- a/electron/electron.js
+++ b/electron/electron.js
@@ -1,7 +1,7 @@
 /* eslint-disable no-undef */
 /* eslint-disable @typescript-eslint/no-var-requires */
 const { app, BrowserWindow, ipcMain } = require('electron');
-const { createMainWindow, setupWindow, createNewWindow } = require('./components/window');
+const { createMainWindow, setupWindow, createNewWindow, setupMenu } = require('./components/window');
 const { createSplashWindow } = require('./components/splash');
 const { setupIpcListener } = require('./components/ipc');
 const { waitEvent, waitAll, wait } = require('./components/utils');
@@ -40,7 +40,7 @@ app.whenReady().then(() => {
     mainWindow = createMainWindow();
     if (!headless) splashWindow = createSplashWindow();
 
-    setupIpcListener(mainWindow);
+    setupIpcListener(mainWindow, setupMenu);
 
     // Display splash screen for minimum 2s then display main window
     waitAll([waitEvent(mainWindow, 'ready-to-show'), wait(200)]).then(async () => {
diff --git a/i18n/config.ts b/i18n/config.ts
new file mode 100644
index 0000000000000000000000000000000000000000..ac4e33349e84000aae9f23a3c4ec839084de7528
--- /dev/null
+++ b/i18n/config.ts
@@ -0,0 +1,14 @@
+import { createI18n } from 'vue-i18n';
+// @ts-expect-error // json files not resolved
+import fr from './fr/translation.json';
+// @ts-expect-error // json files not resolved
+import en from './en/translation.json';
+
+export const i18n = createI18n({
+    locale: 'en',
+    fallbackLocale: 'en',
+    messages: {
+        fr,
+        en,
+    },
+});
diff --git a/i18n/en/translation.json b/i18n/en/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..bbbd0a79bd903e045e110699dde079c2b3e3f10d
--- /dev/null
+++ b/i18n/en/translation.json
@@ -0,0 +1,390 @@
+{
+    "menu": {
+        "app": {
+            "label": "App",
+            "about": "About",
+            "quit": "Quit",
+            "lang": "Language"
+        },
+        "file": {
+            "label": "File",
+            "new": "New",
+            "newWindow": "New Window",
+            "open": "Open",
+            "openInNewWindow": "Open in New Window",
+            "latest": "Recent Projects",
+            "import": "Import .epoc File",
+            "save": "Save",
+            "saveAs": "Save As..."
+        },
+        "edit": {
+            "label": "Edit",
+            "undo": "Undo",
+            "redo": "Redo",
+            "cut": "Cut",
+            "copy": "Copy",
+            "paste": "Paste",
+            "selectAll": "Select All"
+        },
+        "preview": {
+            "label": "Preview",
+            "start": "Start Preview",
+            "global": "Start Global Preview"
+        },
+        "help": {
+            "label": "Help",
+            "documentation": "Documentation",
+            "reportIssue": "Report Issue",
+            "mailSubject": "Editor Help",
+            "mailBody": "Describe your issue below:"
+        },
+        "devTools": "Developer Tools",
+        "reload": "Reload"
+    },
+    "context": {
+        "add": "Add",
+        "pasteHere": "Paste here",
+        "insertAfter": "Insert after",
+        "insertBefore": "Insert before",
+        "delete": "Delete",
+        "duplicate": "Duplicate",
+        "swapNext": "Swap with next",
+        "swapPrevious": "Swap with previous",
+        "insertEnd": "Insert at end",
+        "insertStart": "Insert at start",
+        "copyChapter": "Copy chapter",
+        "addChapter": "Add new chapter",
+        "addText": "Add text page",
+        "addVideo": "Add video page",
+        "addAudio": "Add audio page",
+        "addChoice": "Add multiple choice assessment",
+        "addDragAndDrop": "Add drag and drop assessment",
+        "addReorder": "Add reorder assessment",
+        "addSwipe": "Add swipe assessment",
+        "addDropdown": "Add dropdown assessment",
+        "addCustom": "Add custom assessment"
+    },
+
+    "global": {
+        "open": "Open",
+        "cancel": "Cancel",
+        "accept": "Accept",
+        "element": "Element",
+        "pleaseSelect": "Please select",
+        "and": "and",
+        "condition": "Condition",
+        "save": "Save",
+        "true": "True",
+        "false": "False",
+        "right": "right",
+        "left": "left",
+        "add": "Add",
+        "validate": "Confirm",
+        "delete": "Delete",
+        "conditions": "Conditions",
+        "label": "Label",
+        "chapter": "Chapter",
+        "objective": "Objective",
+        "name": "Name",
+        "file": "File"
+    },
+    "validation": {
+        "yes": "YES, DELETE",
+        "no": "NO, DO NOT DELETE",
+        "confirm": "Are you sure you want to delete this element?"
+    },
+    "landing": {
+        "published": "This ePoc is a published version, you need to import it before editing here",
+        "loadingPath": "Loading {path}",
+        "loadingEpoc": "Loading ePoc",
+        "open": "Open existing project",
+        "create": "Create new project",
+        "recents": "Recent files"
+    },
+    "editor": {
+        "select": "Click on the content element affected by the condition to select it"
+    },
+    "badge": {
+        "supported": "Supported file: SVG",
+        "select": "Select a file",
+        "conditions": "Badge earning conditions",
+        "add": "Add a condition",
+        "selectIcon": "Select an icon",
+        "defaultIcons": "Default icons",
+        "customIcons": "Custom icons",
+        "phrase": {
+            "type": {
+                "video": "the video",
+                "chapter": "the chapter",
+                "page": "the page",
+                "html": "the text",
+                "audio": "the audio",
+                "activity": "the assessment",
+                "question": "the question"
+            },
+            "verb": {
+                "started": {
+                    "true": "Have started",
+                    "false": "Have not started"
+                },
+                "completed": {
+                    "true": "Have completed",
+                    "false": "Have not completed"
+                },
+                "viewed": {
+                    "true": "Have viewed",
+                    "false": "Have not viewed"
+                },
+                "read": {
+                    "true": "Have read",
+                    "false": "Have not read"
+                },
+                "played": {
+                    "true": "Have played",
+                    "false": "Have not played"
+                },
+                "watched": {
+                    "true": "Have watched",
+                    "false": "Have not watched"
+                },
+                "listened": {
+                    "true": "Have listened to",
+                    "false": "Have not listened to"
+                },
+                "attempted": {
+                    "true": "Have attempted",
+                    "false": "Have not attempted"
+                },
+                "passed": {
+                    "true": "Have passed",
+                    "false": "Have failed"
+                },
+                "scored": "Have scored at least"
+            },
+            "scored": "{verb} {value} on"
+        }
+    },
+    "inputs": {
+        "manageConditions": "Configure conditions",
+        "updateIcon": "Update icon",
+        "leftChoice": "Left choice",
+        "rightChoice": "Right choice",
+        "accordion": "Expand/Collapse",
+        "accordionDescription": "Expand/Collapse with title and content"
+    },
+    "toast": {
+        "modelSaved": "Template saved 👌",
+        "modelExists": "Template already exists 🤔"
+    },
+    "settings": {
+        "title": "Settings",
+        "spellcheck": "Enable spell checking",
+        "lang": "Language"
+    },
+    "models": {
+        "title": "Page templates",
+        "empty": "No page template has been created",
+        "dragdrop": "Drag/drop to add a template",
+        "model": "Template"
+    },
+    "header": {
+        "undo": "Undo",
+        "redo": "Redo",
+        "preview": "Preview",
+        "publish": "Publish",
+        "adjust": "Adjust",
+        "never": "never",
+        "lastSave": "Last save:",
+        "new": "New ePoc"
+    },
+    "forms": {
+        "type": "Type here...",
+        "badge": {
+            "text": "Badge settings",
+            "updateIcon": "Update icon",
+            "obtention": "Badge earning conditions",
+            "presentation": "Badge presentation",
+            "presentationPlaceholder": "Enter badge presentation",
+            "icon": "Badge icon"
+        },
+        "content": {
+            "text": "Content",
+            "summary": "Summary",
+            "video": {
+                "label": "Video",
+                "placeholder": "Add a video",
+                "hint": "Recommended format: {format}"
+            },
+            "audio": {
+                "label": "Audio track",
+                "placeholder": "Add an audio track",
+                "hint": "Recommended format: MP3"
+            },
+            "subtitle": {
+                "label": "Language name",
+                "code": "Language code",
+                "placeholder": "Add subtitles",
+                "hint": "Accepted extensions: {extensions}"
+            },
+            "transcription": {
+                "label": "Transcription",
+                "placeholder": "Add a transcription",
+                "hint": "Accepted extensions: {extensions} <br>For users who do not wish or are unable to watch the video"
+            },
+            "thumbnail": {
+                "label": "Thumbnail",
+                "placeholder": "Add a thumbnail",
+                "hint": "Recommended format: same as video"
+            }
+        },
+        "buttons": {
+            "addBadge": "Add badge",
+            "saveModel": "Save template",
+            "duplicateEvaluation": "Duplicate assessment",
+            "duplicatePage": "Duplicate page",
+            "duplicateElement": "Duplicate element",
+            "backToPage": "Back to page",
+            "backToEpoc": "Back to ePoc"
+        },
+        "node": {
+            "conditionPlaceholder": "Enter condition {condition}...",
+            "choice": "Choice",
+            "course": "Course {course}",
+            "conditional": "Conditional content",
+            "title": "Title",
+            "subtitle": "Subtitle",
+            "duration": "Duration (in minutes)",
+            "objectives": "Learning objectives",
+            "about": "About the ePoc",
+            "cover": {
+                "title": "Cover image",
+                "placeholder": "Add a cover image",
+                "hint": "Recommended format: square (180x180)<br> Image visible in the ePoc list"
+            },
+            "teaser": {
+                "title": "Video teaser",
+                "placeholder": "Add a teaser",
+                "hint": "Recommended format: 16:9<br> Video visible on the ePoc presentation page"
+            },
+            "thumbnail": {
+                "title": "Video thumbnail",
+                "hint": "Recommended format: same as video <br> Image visible on the ePoc presentation page"
+            },
+            "presentation": "Presentation",
+            "edition": "Edition",
+            "author": {
+                "title": "Author | Authors",
+                "placeholder": "Jane Doe",
+                "image": {
+                    "title": "Author image",
+                    "placeholder": "Add an image",
+                    "hint": "Recommended format: square (100x100)<br> Image visible on the ePoc presentation page"
+                },
+                "position": {
+                    "title": "Position",
+                    "placeholder": "Researcher at Inria",
+                    "hint": "Profession, position, affiliation..."
+                },
+                "biography": "Short author biography"
+            },
+            "certificateBadge": "Number of badges required for certification",
+            "certificateScore": "Score required for certification",
+            "certificateScoreHint": "Not considered if the number of badges required for certification is greater than 0",
+            "chapterLabel": "Chapter label",
+            "chapterDuration": "Chapter duration (in minutes)",
+            "plugin": {
+                "title": "Plugin | Plugins",
+                "script": "Script file",
+                "scriptPlaceholder": "Add a script file",
+                "template": "Plugin HTML template",
+                "templatePlaceholder": "Add HTML template"
+            },
+            "licence": {
+                "title": "License",
+                "hint": "Name of your ePoc content license",
+                "url": "URL",
+                "urlPlaceholder": "https://creativecommons.org/licenses/by/4.0/deed",
+                "urlHint": "Full text of the chosen license"
+            },
+            "page": {
+                "title": "Page",
+                "hidden": "Hidden in table of contents",
+                "conditional": "Only displays under certain conditions",
+                "conditionalHint": "Option used for conditional display",
+                "components": "Components"
+            },
+            "activity": "Assessment"
+        }
+    },
+    "questions": {
+        "configuration": "Assessment configuration",
+        "question": "Question",
+        "askQuestion": "Ask the question",
+        "instruction": "Instruction",
+        "instructionPlaceholder": "Instruction to answer the question",
+        "explanation": "Explanation",
+        "typeExplanation": "Enter an explanation",
+        "addExplanation": "Add an explanation",
+        "response": "Answer",
+        "responses": "Answers",
+        "typeResponse": "Enter an answer",
+        "correctResponse": "Correct answer",
+        "categories": "Proposed answer categories",
+        "category": "Category",
+        "typeCategory": "Enter a category title",
+        "proposedChoices": "Proposed choice categories",
+        "proposedResponses": "Proposed answers",
+        "cards": "Cards",
+        "card": "Card",
+        "typeProposition": "Enter a proposition",
+        "template": {
+            "title": "Template",
+            "select": "Select a template",
+            "data": "Data",
+            "key": "Key",
+            "value": "Value"
+        },
+        "types": {
+            "qcm": "MCQ",
+            "dragDrop": "Drag & Drop",
+            "reorder": "Reorder",
+            "swipe": "Swipe",
+            "dropdownList": "Dropdown list",
+            "custom": "Custom question"
+        },
+        "score": "Score"
+    },
+    "sidebar": {
+        "content": {
+            "text": "Text",
+            "video": "Video",
+            "audio": "Audio",
+            "textTooltip": "Drag/drop to add text",
+            "videoTooltip": "Drag/drop to add video",
+            "audioTooltip": "Drag/drop to add audio"
+        },
+        "pages": {
+            "question": "Question",
+            "conditions": "Conditions",
+            "conditionsLegacy": "Conditions (legacy)",
+            "model": "Template",
+            "badge": "Badge",
+            "questionTooltip": "Click to add a question",
+            "conditionsTooltip": "Drag/drop to add a condition",
+            "modelTooltip": "Click to open template menu",
+            "badgeTooltip": "Click to open badge menu"
+        }
+    },
+    "verbs": {
+        "started": "Started",
+        "completed": "Completed",
+        "viewed": "Viewed",
+        "read": "Read",
+        "played": "Played",
+        "watched": "Watched",
+        "listened": "Listened",
+        "attempted": "Attempted",
+        "scored": "Scored",
+        "passed": "Passed"
+    }
+}
diff --git a/i18n/fr/translation.json b/i18n/fr/translation.json
new file mode 100644
index 0000000000000000000000000000000000000000..70a625ac68f7abcab5b4bec0839f14857d08139a
--- /dev/null
+++ b/i18n/fr/translation.json
@@ -0,0 +1,392 @@
+{
+    "menu": {
+        "app": {
+            "label:": "App",
+            "about": "À propos",
+            "quit": "Quitter",
+            "lang": "Langage"
+        },
+        "file": {
+            "label": "Fichier",
+            "new": "Nouveau",
+            "newWindow": "Nouvelle fenêtre",
+            "open": "Ouvrir",
+            "openInNewWindow": "Ouvrir dans une nouvelle fenêtre",
+            "latest": "Projets récents",
+            "import": "Importer un fichier .epoc",
+            "save": "Sauvegarder",
+            "saveAs": "Sauvegarder sous..."
+        },
+        "edit": {
+            "label": "Édition",
+            "undo": "Annuler",
+            "redo": "Rétablir",
+            "cut": "Couper",
+            "copy": "Copier",
+            "paste": "Coller",
+            "selectAll": "Sélectionner tout"
+        },
+        "preview": {
+            "label": "Aperçu",
+            "start": "Lancer l'aperçu",
+            "global": "Lancer l'aperçu global"
+        },
+        "help": {
+            "label": "Aide",
+            "documentation": "Documentation",
+            "reportIssue": "Signaler un problème",
+            "mailSubject": "Aide éditeur",
+            "mailBody": "Décrivez votre problème ci-dessous:"
+        },
+        "devTools": "Outils de développement",
+        "reload": "Recharger"
+    },
+    "context": {
+        "add": "Ajouter",
+        "pasteHere": "Coller ici",
+        "insertAfter": "Insérer après",
+        "insertBefore": "Insérer avant",
+        "delete": "Supprimer",
+        "duplicate": "Dupliquer",
+        "swapNext": "Échanger avec le suivant",
+        "swapPrevious": "Échanger avec le précédent",
+        "insertEnd": "Insérer à la fin",
+        "insertStart": "Insérer au début",
+        "copyChapter": "Copier le chapitre",
+        "addChapter": "Ajouter un nouveau chapitre",
+
+        "addText": "Ajouter une page texte",
+        "addVideo": "Ajouter une page vidéo",
+        "addAudio": "Ajouter une page audio",
+
+        "addChoice": "Ajouter une évaluation QCM",
+        "addDragAndDrop": "Ajouter une évaluation Drag & Drop",
+        "addReorder": "Ajouter une évaluation Reorder",
+        "addSwipe": "Ajouter une évaluation Swipe",
+        "addDropdown": "Ajouter une évaluation Liste Déroulante",
+        "addCustom": "Ajouter une évaluation personnalisée"
+    },
+
+    "global": {
+        "open": "Ouvrir",
+        "cancel": "Annuler",
+        "accept": "Accepter",
+        "element": "Élément",
+        "pleaseSelect": "Veuillez sélectionner",
+        "and": "et",
+        "condition": "Condition",
+        "save": "Sauvegarder",
+        "true": "Vrai",
+        "false": "Faux",
+        "right": "droite",
+        "left": "gauche",
+        "add": "Ajouter",
+        "validate": "Valider",
+        "delete": "Supprimer",
+        "conditions": "Conditions",
+        "label": "Label",
+        "chapter": "Chapitre",
+        "objective": "Objectif",
+        "name": "Nom",
+        "file": "Fichier"
+    },
+    "validation": {
+        "yes": "OUI, SUPPRIMER",
+        "no": "NON, NE PAS SUPPRIMER",
+        "confirm": "Êtes-vous sûr de vouloir supprimer cet élément ?"
+    },
+    "landing": {
+        "published": "Cet ePoc est une version publiée, vous devez l'importer avant de pouvoir l'éditer ici",
+        "loadingPath": "Chargement de {path}",
+        "loadingEpoc": "Chargement de l'ePoc",
+        "open": "Ouvrir un projet existant",
+        "create": "Créer un nouveau projet",
+        "recents": "Fichiers récents"
+    },
+    "editor": {
+        "select": "Cliquer sur l'élément de contenu concerné par la condition pour la sélectionner"
+    },
+    "badge": {
+        "supported": "Fichier supporté: SVG",
+        "select": "Sélectionner un fichier",
+        "conditions": "Conditions d'obtention du badge",
+        "add": "Ajouter une condition",
+        "selectIcon": "Sélectionner une icône",
+        "defaultIcons": "Icônes par défaut",
+        "customIcons": "Icônes personnalisées",
+        "phrase": {
+            "type": {
+                "video": "la vidéo",
+                "chapter": "le chapitre",
+                "page": "la page",
+                "html": "le texte",
+                "audio": "l'audio",
+                "activity": "l'évaluation",
+                "question": "la question"
+            },
+            "verb": {
+                "started": {
+                    "true": "Avoir commencé",
+                    "false": "Ne pas avoir pas commencé"
+                },
+                "completed": {
+                    "true": "Avoir terminé",
+                    "false": "Ne pas avoir terminé"
+                },
+                "viewed": {
+                    "true": "Avoir vu",
+                    "false": "Ne pas avoir vu"
+                },
+                "read": {
+                    "true": "Avoir lu",
+                    "false": "Ne pas avoir lu"
+                },
+                "played": {
+                    "true": "Avoir lancé",
+                    "false": "Ne pas avoir lancé"
+                },
+                "watched": {
+                    "true": "Avoir regardé",
+                    "false": "Ne pas avoir regardé"
+                },
+                "listened": {
+                    "true": "Avoir écouté",
+                    "false": "Ne pas avoir écouté"
+                },
+                "attempted": {
+                    "true": "Avoir tenté",
+                    "false": "Ne pas avoir tenté"
+                },
+                "passed": {
+                    "true": "Avoir réussi",
+                    "false": "Avoir échoué"
+                },
+                "scored": "Avoir obtenu un score d'au moins"
+            },
+            "scored": "{verb} {value} à"
+        }
+    },
+    "inputs": {
+        "manageConditions": "Configurer les conditions",
+        "updateIcon": "Modifier l'icône",
+        "leftChoice": "Choix gauche",
+        "rightChoice": "Choix droit",
+        "accordion": "Plier/déplier",
+        "accordionDescription": "Plier/déplier avec titre et contenu"
+    },
+    "toast": {
+        "modelSaved": "Modèle sauvegardé 👌",
+        "modelExists": "Le modèle existe déjà 🤔"
+    },
+    "settings": {
+        "title": "Paramètres",
+        "spellcheck": "Activer la vérification orthographique",
+        "lang": "Langue"
+    },
+    "models": {
+        "title": "Modèles de page",
+        "empty": "Aucun modèle de page n'as été créé",
+        "dragdrop": "Glisser/déposer pour ajouter un modèle",
+        "model": "Modèle"
+    },
+    "header": {
+        "undo": "Annuler",
+        "redo": "Rétablir",
+        "preview": "Aperçu",
+        "publish": "Publier",
+        "adjust": "Ajuster",
+        "never": "jamais",
+        "lastSave": "Dernière sauvegarde :",
+        "new": "Nouvel ePoc"
+    },
+    "forms": {
+        "type": "Saisissez...",
+        "badge": {
+            "text": "Paramètres du badge",
+            "updateIcon": "Modifier l'icône",
+            "obtention": "Conditions d'obtention du badge",
+            "presentation": "Présentation du badge",
+            "presentationPlaceholder": "Saisissez une présentation du badge",
+            "icon": "Icône du badge"
+        },
+        "content": {
+            "text": "Contenu",
+            "summary": "Résumé",
+            "video": {
+                "label": "Vidéo",
+                "placeholder": "Ajouter une vidéo",
+                "hint": "Format recommandé : {format}"
+            },
+            "audio": {
+                "label": "Piste audio",
+                "placeholder": "Ajouter une piste audio",
+                "hint": "Format recommandé : MP3"
+            },
+            "subtitle": {
+                "label": "Nom de la langue",
+                "code": "Code de la langue",
+                "placeholder": "Ajouter des sous-titre",
+                "hint": "Extensions acceptées : {extensions}"
+            },
+            "transcription": {
+                "label": "Transcription",
+                "placeholder": "Ajouter une transcription",
+                "hint": "Extensions acceptées : {extensions} <br>Pour les utilisateurs qui ne souhaitent pas ou ne sont pas en capacité d'écouter la vidéo"
+            },
+            "thumbnail": {
+                "label": "Vignette",
+                "placeholder": "Ajouter une vignette",
+                "hint": "Format recommandé : idem à la vidéo"
+            }
+        },
+        "buttons": {
+            "addBadge": "Ajouter un badge",
+            "saveModel": "Sauvegarder le modèle",
+            "duplicateEvaluation": "Dupliquer l'évaluation",
+            "duplicatePage": "Dupliquer la page",
+            "duplicateElement": "Dupliquer l'élement",
+            "backToPage": "Revenir à la page",
+            "backToEpoc": "Revenir à l'ePoc"
+        },
+        "node": {
+            "conditionPlaceholder": "Saisissez la condition {condition}...",
+            "choice": "Choix",
+            "course": "Cours {course}",
+            "conditional": "Contenus conditionnels",
+            "title": "Titre",
+            "subtitle": "Sous-titre",
+            "duration": "Durée (en minutes)",
+            "objectives": "Objectifs pédagogiques",
+            "about": "À propos de l'ePoc",
+            "cover": {
+                "title": "Image de couverture",
+                "placeholder": "Ajouter une image de couverture",
+                "hint": "Format recommandé : carré (180x180)<br> Image visible dans la liste des ePocs"
+            },
+            "teaser": {
+                "title": "Teaser vidéo",
+                "placeholder": "Ajouter un teaser",
+                "hint": "Format recommandé : 16:9<br> Vidéo visible dans la page de présentation de l'ePoc"
+            },
+            "thumbnail": {
+                "title": "Vignette de la vidéo",
+                "hint": "Format recommandé : idem que la vidéo <br> Image visible dans la page de présentation de l'ePoc"
+            },
+            "presentation": "Présentation",
+            "edition": "Édition",
+            "author": {
+                "title": "Auteur | Auteurs",
+                "placeholder": "Jeanne Dupont",
+                "image": {
+                    "title": "Image de l'auteur",
+                    "placeholder": "Ajouter une image",
+                    "hint": "Format recommandé : carré (100x100)<br> Image visible dans la page de présentation de l'ePoc"
+                },
+                "position": {
+                    "title": "Fonction",
+                    "placeholder": "Chercheur à l'Inria",
+                    "hint": "Profession, fonction, affiliation…"
+                },
+                "biography": "Courte biographie de l'auteur"
+            },
+            "certificateBadge": "Nombre de badge pour obtenir l'attestation",
+            "certificateScore": "Score pour obtenir l'attestation",
+            "certificateScoreHint": "N'est pas pris en compte si le nombre de badge pour obtenir l'attestation est supérieur à 0",
+            "chapterLabel": "Label des chapitres",
+            "chapterDuration": "Durée des chapitres (en minutes)",
+            "plugin": {
+                "title": "Plugin | Plugins",
+                "script": "Fichier de script",
+                "scriptPlaceholder": "Ajouter un fichier de script",
+                "template": "Template html du plugin",
+                "templatePlaceholder": "Ajouter un template html"
+            },
+            "licence": {
+                "title": "Licence",
+                "hint": "Nom de la licence de votre contenu ePoc",
+                "url": "URL",
+                "urlPlaceholder": "https://creativecommons.org/licenses/by/4.0/deed",
+                "urlHint": "Text complet de la licence choisie"
+            },
+            "page": {
+                "title": "Page",
+                "hidden": "Caché dans la table des matières",
+                "conditional": "Ne s'affiche qu'à certaines conditions",
+                "conditionalHint": "Option utilisée pour l'affichage conditionnel",
+                "components": "Composants"
+            },
+            "activity": "Évaluation"
+        }
+    },
+    "questions": {
+        "configuration": "Configuration de l'évaluation",
+        "question": "Question",
+        "askQuestion": "Posez la question",
+        "instruction": "Consigne",
+        "instructionPlaceholder": "Instruction pour répondre à la question",
+        "explanation": "Explication",
+        "typeExplanation": "Saisissez une explication",
+        "addExplanation": "Ajouter une explication",
+        "response": "Réponse",
+        "responses": "Réponses",
+        "typeResponse": "Saisissez une réponse",
+        "correctResponse": "Bonne réponse",
+        "categories": "Catégories de réponses proposées",
+        "category": "Catégorie",
+        "typeCategory": "Saisissez un intitulé catégorie",
+        "proposedChoices": "Catégories de choix proposées",
+        "proposedResponses": "Réponses proposées",
+        "cards": "Cartes",
+        "card": "Carte",
+        "typeProposition": "Saisissez une proposition",
+        "template": {
+            "title": "Template",
+            "select": "Selectionnez un template",
+            "data": "Données",
+            "key": "Clé",
+            "value": "Valeur"
+        },
+        "types": {
+            "qcm": "QCM",
+            "dragDrop": "Drag & Drop",
+            "reorder": "Reorder",
+            "swipe": "Swipe",
+            "dropdownList": "Liste déroulante",
+            "custom": "Question personnalisée"
+        },
+        "score": "Score"
+    },
+    "sidebar": {
+        "content": {
+            "text": "Texte",
+            "video": "Vidéo",
+            "audio": "Audio",
+            "textTooltip": "Glisser/déposer pour ajouter un texte",
+            "videoTooltip": "Glisser/déposer pour ajouter une vidéo",
+            "audioTooltip": "Glisser/déposer pour ajouter un audio"
+        },
+        "pages": {
+            "question": "Question",
+            "conditions": "Conditions",
+            "conditionsLegacy": "Conditions (legacy)",
+            "model": "Modèle",
+            "badge": "Badge",
+            "questionTooltip": "Cliquer pour ajouter une question",
+            "conditionsTooltip": "Glisser/déposer pour ajouter une condition",
+            "modelTooltip": "Cliquer pour ouvrir le menu modèle",
+            "badgeTooltip": "Cliquer pour ouvrir le menu badge"
+        }
+    },
+    "verbs": {
+        "started": "Commencé",
+        "completed": "Terminé",
+        "viewed": "Vu",
+        "read": "Lu",
+        "played": "Joué",
+        "watched": "Regardé",
+        "listened": "Écouté",
+        "attempted": "Tenté",
+        "scored": "Obtenu un score de",
+        "passed": "Réussi"
+    }
+}
diff --git a/package-lock.json b/package-lock.json
index 8d2dcd747b30d1632d3c0c3d79cf18231e44dfe5..8a8c1c5087ca8c7553ccfdb264d066298df88a4b 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,11 +22,14 @@
                 "expect": "^29.7.0",
                 "express": "^4.18.2",
                 "glob": "^8.1.0",
+                "i18next": "^24.2.3",
+                "i18next-node-fs-backend": "^2.1.3",
                 "pinia": "^2.0.28",
                 "sass": "^1.56.2",
                 "serve-static": "^1.15.0",
                 "update-electron-app": "^2.0.1",
                 "vue": "3.3",
+                "vue-i18n": "^10.0.6",
                 "vue-router": "^4.1.6",
                 "vue-tippy": "^6.0.0",
                 "vuedraggable": "^4.1.0"
@@ -51,7 +54,7 @@
                 "eslint-plugin-vue": "^9.8.0",
                 "happy-dom": "^16.7.2",
                 "ts-node": "^10.9.1",
-                "typescript": "^4.9.4",
+                "typescript": "^5.8.2",
                 "vite": "^4.0.0",
                 "vitest": "^0.25.8",
                 "vue-tsc": "^1.0.11",
@@ -178,10 +181,9 @@
             }
         },
         "node_modules/@babel/runtime": {
-            "version": "7.25.6",
-            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.25.6.tgz",
-            "integrity": "sha512-VBj9MYyDb9tuLq7yzqjgzt6Q+IBQLrGZfdjOekyEirZPHxXWoTSGUTMrpsfi58Up73d13NfYLv8HT9vmznjzhQ==",
-            "dev": true,
+            "version": "7.26.10",
+            "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.26.10.tgz",
+            "integrity": "sha512-2WJMeRQPHKSPemqk/awGrAiuFfzBmOIPXKizAsVhWH9YJqLZ0H+HS4c8loHGgW6utJ3E/ejXQUsiGaQy2NZ9Fw==",
             "license": "MIT",
             "dependencies": {
                 "regenerator-runtime": "^0.14.0"
@@ -1298,6 +1300,50 @@
             "dev": true,
             "license": "BSD-3-Clause"
         },
+        "node_modules/@intlify/core-base": {
+            "version": "10.0.6",
+            "resolved": "https://registry.npmjs.org/@intlify/core-base/-/core-base-10.0.6.tgz",
+            "integrity": "sha512-/NINGvy7t8qSCyyuqMIPmHS6CBQjqPIPVOps0Rb7xWrwwkwHJKtahiFnW1HC4iQVhzoYwEW6Js0923zTScLDiA==",
+            "license": "MIT",
+            "dependencies": {
+                "@intlify/message-compiler": "10.0.6",
+                "@intlify/shared": "10.0.6"
+            },
+            "engines": {
+                "node": ">= 16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/kazupon"
+            }
+        },
+        "node_modules/@intlify/message-compiler": {
+            "version": "10.0.6",
+            "resolved": "https://registry.npmjs.org/@intlify/message-compiler/-/message-compiler-10.0.6.tgz",
+            "integrity": "sha512-QcUYprK+e4X2lU6eJDxLuf/mUtCuVPj2RFBoFRlJJxK3wskBejzlRvh1Q0lQCi9tDOnD4iUK1ftcGylE3X3idA==",
+            "license": "MIT",
+            "dependencies": {
+                "@intlify/shared": "10.0.6",
+                "source-map-js": "^1.0.2"
+            },
+            "engines": {
+                "node": ">= 16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/kazupon"
+            }
+        },
+        "node_modules/@intlify/shared": {
+            "version": "10.0.6",
+            "resolved": "https://registry.npmjs.org/@intlify/shared/-/shared-10.0.6.tgz",
+            "integrity": "sha512-2xqwm05YPpo7TM//+v0bzS0FWiTzsjpSMnWdt7ZXs5/ZfQIedSuBXIrskd8HZ7c/cZzo1G9ALHTksnv/74vk/Q==",
+            "license": "MIT",
+            "engines": {
+                "node": ">= 16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/kazupon"
+            }
+        },
         "node_modules/@isaacs/cliui": {
             "version": "8.0.2",
             "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz",
@@ -8810,20 +8856,6 @@
                 "node": ">=16 || 14 >=14.17"
             }
         },
-        "node_modules/config-file-ts/node_modules/typescript": {
-            "version": "5.7.3",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
-            "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
-            "dev": true,
-            "license": "Apache-2.0",
-            "bin": {
-                "tsc": "bin/tsc",
-                "tsserver": "bin/tsserver"
-            },
-            "engines": {
-                "node": ">=14.17"
-            }
-        },
         "node_modules/console-control-strings": {
             "version": "1.1.0",
             "resolved": "https://registry.npmjs.org/console-control-strings/-/console-control-strings-1.1.0.tgz",
@@ -11329,7 +11361,6 @@
             "version": "4.0.1",
             "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz",
             "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==",
-            "dev": true,
             "license": "BSD-2-Clause",
             "bin": {
                 "esparse": "bin/esparse.js",
@@ -13269,6 +13300,91 @@
                 "ms": "^2.0.0"
             }
         },
+        "node_modules/i18next": {
+            "version": "24.2.3",
+            "resolved": "https://registry.npmjs.org/i18next/-/i18next-24.2.3.tgz",
+            "integrity": "sha512-lfbf80OzkocvX7nmZtu7nSTNbrTYR52sLWxPtlXX1zAhVw8WEnFk4puUkCR4B1dNQwbSpEHHHemcZu//7EcB7A==",
+            "funding": [
+                {
+                    "type": "individual",
+                    "url": "https://locize.com"
+                },
+                {
+                    "type": "individual",
+                    "url": "https://locize.com/i18next.html"
+                },
+                {
+                    "type": "individual",
+                    "url": "https://www.i18next.com/how-to/faq#i18next-is-awesome.-how-can-i-support-the-project"
+                }
+            ],
+            "license": "MIT",
+            "dependencies": {
+                "@babel/runtime": "^7.26.10"
+            },
+            "peerDependencies": {
+                "typescript": "^5"
+            },
+            "peerDependenciesMeta": {
+                "typescript": {
+                    "optional": true
+                }
+            }
+        },
+        "node_modules/i18next-node-fs-backend": {
+            "version": "2.1.3",
+            "resolved": "https://registry.npmjs.org/i18next-node-fs-backend/-/i18next-node-fs-backend-2.1.3.tgz",
+            "integrity": "sha512-CreMFiVl3ChlMc5ys/e0QfuLFOZyFcL40Jj6jaKD6DxZ/GCUMxPI9BpU43QMWUgC7r+PClpxg2cGXAl0CjG04g==",
+            "deprecated": "replaced by i18next-fs-backend",
+            "license": "MIT",
+            "dependencies": {
+                "js-yaml": "3.13.1",
+                "json5": "2.0.0"
+            }
+        },
+        "node_modules/i18next-node-fs-backend/node_modules/argparse": {
+            "version": "1.0.10",
+            "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz",
+            "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==",
+            "license": "MIT",
+            "dependencies": {
+                "sprintf-js": "~1.0.2"
+            }
+        },
+        "node_modules/i18next-node-fs-backend/node_modules/js-yaml": {
+            "version": "3.13.1",
+            "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.13.1.tgz",
+            "integrity": "sha512-YfbcO7jXDdyj0DGxYVSlSeQNHbD7XPWvrVWeVUujrQEoZzWJIRrCPoyk6kL6IAjAG2IolMK4T0hNUe0HOUs5Jw==",
+            "license": "MIT",
+            "dependencies": {
+                "argparse": "^1.0.7",
+                "esprima": "^4.0.0"
+            },
+            "bin": {
+                "js-yaml": "bin/js-yaml.js"
+            }
+        },
+        "node_modules/i18next-node-fs-backend/node_modules/json5": {
+            "version": "2.0.0",
+            "resolved": "https://registry.npmjs.org/json5/-/json5-2.0.0.tgz",
+            "integrity": "sha512-0EdQvHuLm7yJ7lyG5dp7Q3X2ku++BG5ZHaJ5FTnaXpKqDrw4pMxel5Bt3oAYMthnrthFBdnZ1FcsXTPyrQlV0w==",
+            "license": "MIT",
+            "dependencies": {
+                "minimist": "^1.2.0"
+            },
+            "bin": {
+                "json5": "lib/cli.js"
+            },
+            "engines": {
+                "node": ">=6"
+            }
+        },
+        "node_modules/i18next-node-fs-backend/node_modules/sprintf-js": {
+            "version": "1.0.3",
+            "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz",
+            "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==",
+            "license": "BSD-3-Clause"
+        },
         "node_modules/iconv-corefoundation": {
             "version": "1.1.7",
             "resolved": "https://registry.npmjs.org/iconv-corefoundation/-/iconv-corefoundation-1.1.7.tgz",
@@ -17278,7 +17394,6 @@
             "version": "0.14.1",
             "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.14.1.tgz",
             "integrity": "sha512-dYnhHh0nJoMfnkZs6GmmhFknAGRrLznOu5nc9ML+EJxGvrx6H7teuevqVqCuPcPK//3eDrrjQhehXVx9cnkGdw==",
-            "dev": true,
             "license": "MIT"
         },
         "node_modules/require-directory": {
@@ -19167,9 +19282,9 @@
             }
         },
         "node_modules/typescript": {
-            "version": "4.9.5",
-            "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz",
-            "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==",
+            "version": "5.8.2",
+            "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.8.2.tgz",
+            "integrity": "sha512-aJn6wq13/afZp/jT9QZmwEjDqqvSGp1VT5GVg+f/t6/oVyrgXM6BY1h9BRh/O5p3PlUPAe+WuiEZOmb/49RqoQ==",
             "devOptional": true,
             "license": "Apache-2.0",
             "bin": {
@@ -19177,7 +19292,7 @@
                 "tsserver": "bin/tsserver"
             },
             "engines": {
-                "node": ">=4.2.0"
+                "node": ">=14.17"
             }
         },
         "node_modules/ua-parser-js": {
@@ -19659,6 +19774,26 @@
                 "node": ">=4.0"
             }
         },
+        "node_modules/vue-i18n": {
+            "version": "10.0.6",
+            "resolved": "https://registry.npmjs.org/vue-i18n/-/vue-i18n-10.0.6.tgz",
+            "integrity": "sha512-pQPspK5H4srzlu+47+HEY2tmiY3GyYIvSPgSBdQaYVWv7t1zj1t9p1FvHlxBXyJ17t9stG/Vxj+pykrvPWBLeQ==",
+            "license": "MIT",
+            "dependencies": {
+                "@intlify/core-base": "10.0.6",
+                "@intlify/shared": "10.0.6",
+                "@vue/devtools-api": "^6.5.0"
+            },
+            "engines": {
+                "node": ">= 16"
+            },
+            "funding": {
+                "url": "https://github.com/sponsors/kazupon"
+            },
+            "peerDependencies": {
+                "vue": "^3.0.0"
+            }
+        },
         "node_modules/vue-router": {
             "version": "4.4.5",
             "resolved": "https://registry.npmjs.org/vue-router/-/vue-router-4.4.5.tgz",
diff --git a/package.json b/package.json
index 079178727de73f6c6f6bab6e5fee0acf720c43eb..5377aeb50a74e9c425ab9649430e87a9a17d303d 100644
--- a/package.json
+++ b/package.json
@@ -39,11 +39,14 @@
         "expect": "^29.7.0",
         "express": "^4.18.2",
         "glob": "^8.1.0",
+        "i18next": "^24.2.3",
+        "i18next-node-fs-backend": "^2.1.3",
         "pinia": "^2.0.28",
         "sass": "^1.56.2",
         "serve-static": "^1.15.0",
         "update-electron-app": "^2.0.1",
         "vue": "3.3",
+        "vue-i18n": "^10.0.6",
         "vue-router": "^4.1.6",
         "vue-tippy": "^6.0.0",
         "vuedraggable": "^4.1.0"
@@ -68,7 +71,7 @@
         "eslint-plugin-vue": "^9.8.0",
         "happy-dom": "^16.7.2",
         "ts-node": "^10.9.1",
-        "typescript": "^4.9.4",
+        "typescript": "^5.8.2",
         "vite": "^4.0.0",
         "vitest": "^0.25.8",
         "vue-tsc": "^1.0.11",
diff --git a/src/App.vue b/src/App.vue
index 7dcb73559e9abdefc829b58a6c17d6fae2612387..28c7eef7b80e4cc5787166be6332454a9ff5e283 100644
--- a/src/App.vue
+++ b/src/App.vue
@@ -1,7 +1,17 @@
 <script setup lang="ts">
+import { onMounted } from 'vue';
+import { useSettingsStore } from './shared/stores';
+
 // remove this when implementing dark mode
 const root = document.querySelector(':root');
 root.setAttribute('color-scheme', 'light');
+
+onMounted(() => {
+    // const settingsStore = useSettingsStore();
+    // if (!settingsStore.initialized) {
+    //     settingsStore.init();
+    // }
+});
 </script>
 
 <template>
diff --git a/src/components/ChoiceModal.vue b/src/components/ChoiceModal.vue
index 0202008aa448f6ed7d706a0579e43b05baa96d1d..b5a4dcd92c81073ac045599a5ac5b64cac3c6e12 100644
--- a/src/components/ChoiceModal.vue
+++ b/src/components/ChoiceModal.vue
@@ -1,15 +1,10 @@
 <script setup lang="ts">
 import { ref, onMounted } from 'vue';
 
-interface Props {
+defineProps<{
     acceptLabel: string;
     cancelLabel: string;
-}
-
-withDefaults(defineProps<Props>(), {
-    acceptLabel: 'Accepter',
-    cancelLabel: 'Annuler',
-});
+}>();
 
 const emits = defineEmits<{
     (e: 'accept'): void;
@@ -32,18 +27,12 @@ onMounted(() => {
 </script>
 
 <template>
-    <div
-        ref="modalScreen"
-        class="modal-backdrop"
-        tabindex="0"
-        @keyup.enter="validate"
-        @keyup.esc="cancel"
-    >
+    <div ref="modalScreen" class="modal-backdrop" tabindex="0" @keyup.enter="validate" @keyup.esc="cancel">
         <div class="modal">
             <slot />
             <button class="btn btn-close" @click="cancel"><i class="icon-x"></i></button>
-            <button class="btn-choice accept" @click="validate">{{ acceptLabel }}</button>
-            <button class="btn-choice cancel" @click="cancel">{{ cancelLabel }}</button>
+            <button class="btn-choice accept" @click="validate">{{ acceptLabel ?? $t('global.accept') }}</button>
+            <button class="btn-choice cancel" @click="cancel">{{ cancelLabel ?? $t('global.cancel') }}</button>
         </div>
     </div>
 </template>
@@ -95,4 +84,4 @@ onMounted(() => {
         color: #fff;
     }
 }
-</style>
\ No newline at end of file
+</style>
diff --git a/src/components/ValidationModal.vue b/src/components/ValidationModal.vue
index 11a0db7e67722c5181942e9601a88bad7194ac3f..7495f96072ec06b1cc7873520d8cb501d79671e7 100644
--- a/src/components/ValidationModal.vue
+++ b/src/components/ValidationModal.vue
@@ -14,12 +14,12 @@ function confirmDelete() {
 
 <template>
     <ChoiceModal
-        accept-label="OUI, SUPPRIMER"
-        cancel-label="NON, NE PAS SUPPRIMER"
+        :accept-label="$t('validation.yes')"
+        :cancel-label="$t('validation.no')"
         @cancel="editorStore.validationModal = false"
         @accept="confirmDelete"
     >
-        <h3>Souhaitez-vous vraiment supprimer cet élément ?</h3>
+        <h3>{{ $t('validation.confirm') }}</h3>
     </ChoiceModal>
 </template>
 
diff --git a/src/components/ui/UiSelect.vue b/src/components/ui/UiSelect.vue
new file mode 100644
index 0000000000000000000000000000000000000000..4d110cd2d3a439df6c20c4cdcb0050ad26ec811e
--- /dev/null
+++ b/src/components/ui/UiSelect.vue
@@ -0,0 +1,42 @@
+<script setup lang="ts">
+import { useVModel } from '@vueuse/core';
+
+const props = defineProps<{
+    id: string;
+    modelValue: string;
+    options: { value: string | number; label: string }[];
+}>();
+
+const emit = defineEmits<{
+    (e: 'update:modelValue', value: string);
+    (e: 'change');
+}>();
+
+const data = useVModel(props, 'modelValue', emit);
+</script>
+
+<template>
+    <select :id="id" v-model="data" @change="handleChange">
+        <option v-for="option in options" :key="option.value" :value="option.value">
+            {{ option.label }}
+        </option>
+    </select>
+</template>
+
+<style scoped lang="scss">
+select {
+    appearance: none;
+    padding: 0.5rem;
+    padding-right: 2rem;
+    border: 1px solid var(--border);
+    border-radius: 4px;
+    background-color: var(--item-background);
+    cursor: pointer;
+    font-size: 1rem;
+    color: var(--text);
+    background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgd2lkdGg9IjExcHgiIGhlaWdodD0iN3B4IiB2aWV3Qm94PSIwIDAgMTEuMCA3LjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxjbGlwUGF0aCBpZD0iaTAiPjxwYXRoIGQ9Ik0yNDE4LDAgTDI0MTgsMjQyNiBMMCwyNDI2IEwwLDAgTDI0MTgsMCBaIj48L3BhdGg+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImkxIj48cGF0aCBkPSJNOS4yMDE3MjIyNywwIEM5LjYxNjUwNDA2LDAgOS45OTA4OTcyMSwwLjI3MzU1MDk4NyAxMC4xNDk4ODU5LDAuNjk0MzM1OTM3IEMxMC4zMDg4NzQ2LDEuMTE1MTIwODkgMTAuMjQ5ODk0OSwxLjU5OTYwOTM3IDkuOTU0OTk2NjMsMS45MTk1MzE0NiBMNS44ODA5MDQ1NSw2LjQxOTUzMTQ2IEM1LjY1MzMxOTM0LDYuNjQxMDE1NDEgNS4zOTA0NzQ3Niw2Ljc1IDUuMTI3NjMwMTksNi43NSBDNC44NjQ3ODU2Miw2Ljc1IDQuNjAyNTgxNzgsNi42NDAxMzY3MiA0LjQwMjI0Mjg2LDYuNDIwNDEwMTYgTDAuMzI4MTUwMjkxLDEuOTIwNDEwMTYgQzAuMDA2MTQ2NTU1MTksMS41OTk2MDkzNyAtMC4wODE2OTQ5MjIzLDEuMTE0NDUzMDIgMC4wNzcxMDE1NTQ3LDAuNjk2MDkzODU3IEMwLjIzNTg5ODAzMiwwLjI3NzczNDY5NyAwLjYxMDIyNzEwNiwwIDEuMDI0Njg4NTMsMCBMOS4yMDE3MjIyNywwIFoiPjwvcGF0aD48L2NsaXBQYXRoPjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTMwNC4wIC0xMDYyLjApIj48ZyBjbGlwLXBhdGg9InVybCgjaTApIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5ODAuMCA5MC4wKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjAuMCA3MjkuMCkiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMCA1MS4wKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTUuMCAxNTAuMCkiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMCAyNC4wKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjg5LjM3MjM2OTgwNjgwMSAxOC4wKSI+PGcgY2xpcC1wYXRoPSJ1cmwoI2kxKSI+PHBvbHlnb24gcG9pbnRzPSItMS4xMTAyMjMwMmUtMTYsMCAxMC4yMzYwMTA0LDAgMTAuMjM2MDEwNCw2Ljc1IC0xLjExMDIyMzAyZS0xNiw2Ljc1IC0xLjExMDIyMzAyZS0xNiwwIiBzdHJva2U9Im5vbmUiIGZpbGw9IiMzNTQyNTgiPjwvcG9seWdvbj48L2c+PC9nPjwvZz48L2c+PC9nPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=');
+    background-repeat: no-repeat;
+    background-position: right 0.7rem top 50%;
+    background-size: 0.8rem auto;
+}
+</style>
diff --git a/src/features/badge/components/BadgePreview.vue b/src/features/badge/components/BadgePreview.vue
index 401aad1fb0fac4e082df1b542cbef0dcb0f3c40b..09352c64eb183a39ae0978184ffa3f97d6f094e7 100644
--- a/src/features/badge/components/BadgePreview.vue
+++ b/src/features/badge/components/BadgePreview.vue
@@ -36,11 +36,11 @@ const fileInput = ref(null);
     <div class="new-icon">
         <BadgeItem :icon="blob" :inactive="!url" @click="onClick" />
         <div>
-            <p class="accepted-files">Fichier supporté: SVG</p>
+            <p class="accepted-files">{{ $t('badge.supported') }}</p>
             <div v-if="!url">
                 <button id="file-selector" class="btn btn-form" @click="openFile">
                     <i class="icon-plus"></i>
-                    Sélectionner un fichier
+                    {{ $t('badge.select') }}
                 </button>
             </div>
             <div v-show="url">
diff --git a/src/features/badge/components/ConditionElementSelector.vue b/src/features/badge/components/ConditionElementSelector.vue
index 76108b235c98f3c44bbce20f17290e689bd76705..30ebfdc2915c7d388a8bf959c874e2d73b2d8bff 100644
--- a/src/features/badge/components/ConditionElementSelector.vue
+++ b/src/features/badge/components/ConditionElementSelector.vue
@@ -14,9 +14,9 @@ function onClick() {
 
 <template>
     <div class="select">
-        Elément
+        {{ $t('global.element') }}
         <div class="select-input" @click="onClick">
-            <p>{{ inputValue || 'Veuillez sélectionner' }}</p>
+            <p>{{ inputValue || $t('global.pleaseSelect') }}</p>
             <i class="icon-cible"></i>
         </div>
     </div>
diff --git a/src/features/badge/components/ConditionItem.vue b/src/features/badge/components/ConditionItem.vue
index f9eca07a965d0be93bf0420c0ad2af6284f93115..e57b6b9b53c290f9242dc265a12e24dd7cf0dd51 100644
--- a/src/features/badge/components/ConditionItem.vue
+++ b/src/features/badge/components/ConditionItem.vue
@@ -56,6 +56,12 @@ function handleVerbChange(value: string) {
     resetValue(true);
     updateCondition(value, 'verb');
 }
+
+const verbs = computed(() => {
+    if (!elementType.value) return [];
+    const res = getVerbs(elementType.value);
+    return res.value;
+});
 </script>
 
 <template>
@@ -63,7 +69,7 @@ function handleVerbChange(value: string) {
     <article>
         <i class="icon-supprimer delete" @click.stop="removeCondition"></i>
         <div class="logic-condition">
-            <button class="logic-choice active">ET</button>
+            <button class="logic-choice active">{{ $t('global.and').toUpperCase() }}</button>
         </div>
         <!-- Condition switch -->
         <!-- <div v-if="conditionIndex !== 0" class="logic-condition">
@@ -81,7 +87,7 @@ function handleVerbChange(value: string) {
                 class="grid-item"
             />
             <div class="select">
-                Condition
+                {{ $t('global.condition') }}
                 <select
                     id="condition"
                     :disabled="verbDisabled"
@@ -89,8 +95,8 @@ function handleVerbChange(value: string) {
                     :value="currentCondition.verb"
                     @change="handleVerbChange(($event.target as HTMLSelectElement).value)"
                 >
-                    <option value="">Veuillez choisir</option>
-                    <option v-for="(description, verb) in getVerbs(elementType)" :key="verb" :value="verb">
+                    <option value="">{{ $t('global.pleaseSelect') }}</option>
+                    <option v-for="(description, verb) in verbs" :key="verb" :value="verb">
                         {{ description.label }}
                     </option>
                 </select>
diff --git a/src/features/badge/components/ConditionModal.vue b/src/features/badge/components/ConditionModal.vue
index 93842c20a54fa8107deef71f7b72edb6ace53dca..2b0ddfeff8b7a088e533dd68da897d5ad61c60a5 100644
--- a/src/features/badge/components/ConditionModal.vue
+++ b/src/features/badge/components/ConditionModal.vue
@@ -53,7 +53,7 @@ onMounted(() => {
         <article class="condition-modal">
             <header>
                 <div class="content">
-                    <h2>Conditions d'obtention du badge</h2>
+                    <h2>{{ $t('badge.conditions') }}</h2>
                     <button class="btn btn-close" @click="close"><i class="icon-x"></i></button>
                 </div>
             </header>
@@ -68,14 +68,16 @@ onMounted(() => {
                 />
                 <button class="add btn btn-form" @click="addCondition">
                     <i class="icon-plus"></i>
-                    Ajouter une condition
+                    {{ $t('badge.add') }}
                 </button>
             </div>
 
             <footer>
                 <div class="content">
-                    <button class="btn-choice cancel" @click="close">ANNULER</button>
-                    <button :disabled="!allConditionsValid" class="btn-choice save" @click="save">ENREGISTRER</button>
+                    <button class="btn-choice cancel" @click="close">{{ $t('global.cancel').toUpperCase() }}</button>
+                    <button :disabled="!allConditionsValid" class="btn-choice save" @click="save">
+                        {{ $t('global.save').toUpperCase() }}
+                    </button>
                 </div>
             </footer>
         </article>
diff --git a/src/features/badge/components/ConditionValue.vue b/src/features/badge/components/ConditionValue.vue
index b9f8b79020e2af431e9d2ed1cdb7123902fea470..26bf3c7b1813f7528434bf3cb4f9504180ea7739 100644
--- a/src/features/badge/components/ConditionValue.vue
+++ b/src/features/badge/components/ConditionValue.vue
@@ -37,14 +37,14 @@ watch(
             :disabled="verb === ''"
             @change="
                 onChange(
-                    ($event.target as HTMLSelectElement).value !== ''
-                        ? ($event.target as HTMLSelectElement).value === 'true'
-                        : '',
+                    ($event.target as HTMLSelectElement).value !== '' ?
+                        ($event.target as HTMLSelectElement).value === 'true'
+                    :   '',
                 )
             "
         >
-            <option value="true">Vrai</option>
-            <option value="false">Faux</option>
+            <option value="true">{{ $t('global.true') }}</option>
+            <option value="false">{{ $t('global.false') }}</option>
         </select>
         <input
             v-else-if="valueType === 'number'"
diff --git a/src/features/badge/components/IconModal.vue b/src/features/badge/components/IconModal.vue
index adfa2fa4eb2d4ed5b67d60ec8d54612fcf5f0771..0097030955efa1141e7961afbea0b29e8e1e9eca 100644
--- a/src/features/badge/components/IconModal.vue
+++ b/src/features/badge/components/IconModal.vue
@@ -46,12 +46,12 @@ onMounted(() => {
         <article class="condition-modal">
             <header>
                 <div class="content">
-                    <h2>Sélectionner une icône</h2>
+                    <h2>{{ $t('badge.selectIcon') }}</h2>
                     <button class="btn btn-close" @click="close"><i class="icon-x"></i></button>
                 </div>
             </header>
             <div class="content">
-                <h3>Icônes par défaut</h3>
+                <h3>{{ $t('badge.defaultIcons') }}</h3>
                 <hr class="separator" />
                 <div class="badges">
                     <BadgeItem
@@ -64,7 +64,7 @@ onMounted(() => {
             </div>
 
             <div class="content">
-                <h3>Icônes personnalisées</h3>
+                <h3>{{ $t('badge.customIcons') }}</h3>
                 <hr class="separator" />
                 <div class="badges">
                     <BadgeItem
diff --git a/src/features/ePocFlow/ePocFlow.vue b/src/features/ePocFlow/ePocFlow.vue
index 81eb6a6ece7ecee61908feb3dfbc4f13e76d1c53..5d3a268cce1d96b8a8c5a87d0e36ba3c0cb85958 100644
--- a/src/features/ePocFlow/ePocFlow.vue
+++ b/src/features/ePocFlow/ePocFlow.vue
@@ -25,7 +25,8 @@ import {
     addPage,
     createPageFromContent,
     graphCopy,
-    getSelectedNodes, handleChapterDrag
+    getSelectedNodes,
+    handleChapterDrag,
 } from '@/src/shared/services/graph';
 import { saveState, saveGivenState, getCurrentState } from '@/src/shared/services/undoRedo.service';
 import { closeAllPanels, closeFormPanel, graphService, getSelectedEdges } from '@/src/shared/services';
@@ -120,7 +121,7 @@ function connect(event: Connection) {
     }
 
     const otherEdge = getConnectedEdges([targetNode], edges.value).find(
-        (edge) => edge.target === targetNode.id && edge.source !== sourceNode.id,
+        (edge) => edge.target === targetNode.id && edge.source !== sourceNode.id
     );
 
     if (otherEdge) {
@@ -188,7 +189,9 @@ function onContextMenu(event: MouseEvent) {
 function onSelectionContextMenu() {
     const selectedNodes = getSelectedNodes();
     const selectedEdges = getSelectedEdges();
-    graphService.openContextMenu('selection', { selection: JSON.stringify({ pages: selectedNodes, edges: selectedEdges }) });
+    graphService.openContextMenu('selection', {
+        selection: JSON.stringify({ pages: selectedNodes, edges: selectedEdges }),
+    });
 }
 
 function onPaneReady() {
diff --git a/src/features/ePocFlow/nodes/ActivityNode.vue b/src/features/ePocFlow/nodes/ActivityNode.vue
index 8e41271b7e382014d532ae9876f2b916cf9eeffe..eb29a66bc4854dd01385ce23b9b14998b8d9de8f 100644
--- a/src/features/ePocFlow/nodes/ActivityNode.vue
+++ b/src/features/ePocFlow/nodes/ActivityNode.vue
@@ -4,8 +4,10 @@ import { computed, ref } from 'vue';
 import { useEditorStore } from '@/src/shared/stores';
 import { getSelectedNodes } from '@/src/shared/services/graph';
 import { closeFormPanel, exitSelectNodeMode, getConnectedBadges, graphService } from '@/src/shared/services';
-
 import DraggableNode from '@/src/features/ePocFlow/nodes/content/DraggableNode.vue';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 const editorStore = useEditorStore();
 
@@ -85,7 +87,7 @@ const activityIndex = computed(() => {
                 class="node-title"
                 :class="{ active: editorStore.openedElementId ? editorStore.openedElementId === props.id : false }"
             >
-                {{ currentNode.data.formValues?.title || 'Évaluation' }}
+                {{ currentNode.data.formValues?.title || t('forms.node.activity') }}
             </p>
             <Handle
                 :data-testid="`target-activity-${activityIndex}`"
diff --git a/src/features/ePocFlow/nodes/ChapterNode.vue b/src/features/ePocFlow/nodes/ChapterNode.vue
index e3746d85e894c026e6cdf62e8838ec8e42d8caec..40db90e73904a1a0599bd5e89b235987ed4725d2 100644
--- a/src/features/ePocFlow/nodes/ChapterNode.vue
+++ b/src/features/ePocFlow/nodes/ChapterNode.vue
@@ -5,6 +5,9 @@ import { Position } from '@vue-flow/core';
 import { computed } from 'vue';
 import ContentButton from '@/src/components/ContentButton.vue';
 import { closeFormPanel, exitSelectNodeMode, getConnectedBadges, graphService } from '@/src/shared/services';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 const editorStore = useEditorStore();
 
@@ -16,15 +19,13 @@ const currentNode = findNode(props.id);
 
 const subtitle = computed(() => {
     const epocNode = findNode('1');
-    const chapterParameter = epocNode?.data?.formValues?.chapterParameter || 'Chapitre';
+    const chapterParameter = epocNode?.data?.formValues?.chapterParameter || t('global.chapter');
     const label = chapterParameter.length > 8 ? chapterParameter.substring(0, 7) + '...' : chapterParameter;
 
     return `${label} ${currentNode.data.index}`;
 });
 
-const isSource = computed(() =>
-    getConnectedEdges([currentNode], edges.value).some((edge) => edge.source === props.id),
-);
+const isSource = computed(() => getConnectedEdges([currentNode], edges.value).some((edge) => edge.source === props.id));
 
 const classList = {
     clickable: true,
diff --git a/src/features/forms/FormPanel.vue b/src/features/forms/FormPanel.vue
index 84b2a99b752df763fca662bc2b7a5b92ac1aa474..f962328fbb61d5a9e629267f393a59453fdeace4 100644
--- a/src/features/forms/FormPanel.vue
+++ b/src/features/forms/FormPanel.vue
@@ -14,6 +14,9 @@ import {
     isFormButtonDisabled,
 } from '@/src/shared/services/graph';
 import LinkedBadges from '@/src/features/badge/components/LinkedBadges.vue';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 const editorStore = useEditorStore();
 
@@ -57,8 +60,8 @@ function actionOnForm(action: string) {
 
         case 'save-model':
             if (editorStore.savePageModel(currentNode.data.elements.map((element: NodeElement) => element.action))) {
-                toaster.success('Modèle sauvegardé 👌');
-            } else toaster.error('Le modèle existe déjà 🤔');
+                toaster.success(t('toast.modelSaved'));
+            } else toaster.error(t('toast.modelExists'));
             break;
 
         case 'simple-question':
diff --git a/src/features/forms/components/inputs/HtmlInput.vue b/src/features/forms/components/inputs/HtmlInput.vue
index b999d8be701f33e78a595ace8a6f339c2d9670d0..d3491cf51fdb4896a8e039342d24bcdcc0100c7a 100644
--- a/src/features/forms/components/inputs/HtmlInput.vue
+++ b/src/features/forms/components/inputs/HtmlInput.vue
@@ -4,6 +4,9 @@ import { getTinymce } from '@tinymce/tinymce-vue/lib/cjs/main/ts/TinyMCE';
 import { Ref, ref, watch } from 'vue';
 import { graphService } from '@/src/shared/services';
 import { getCurrentState } from '@/src/shared/services/undoRedo.service';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 const props = defineProps<{
     type: 'html' | 'html-text' | 'html-inline';
@@ -66,7 +69,7 @@ const standardOptions = {
     min_height: 150,
     max_height: 800,
     height: 350,
-    templates: [{ title: 'Plier/déplier', content: template, description: 'Plier/déplier avec titre et contenu' }],
+    templates: [{ title: t('inputs.accordion'), content: template, description: t('inputs.accordionDescription') }],
     file_picker_types: 'image',
     file_picker_callback: handleFilePicker,
     link_default_target: '_blank',
diff --git a/src/features/forms/components/inputs/RepeatInput.vue b/src/features/forms/components/inputs/RepeatInput.vue
index 7d118557e65749cbe8e15b4259f6dc45293c007c..0d9b2d10ff54574b3582cade55403735bc2d64bb 100644
--- a/src/features/forms/components/inputs/RepeatInput.vue
+++ b/src/features/forms/components/inputs/RepeatInput.vue
@@ -13,6 +13,9 @@ import {
 } from '@/src/shared/interfaces';
 import { ref } from 'vue';
 import { generateContentId } from '@/src/shared/services/graph.service';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 const props = defineProps<{
     id: string;
@@ -136,7 +139,7 @@ function dragOver(event: DragEvent) {
 // Used to get "choice left/right" on swipe choice
 function getLabelIdentifier(index) {
     if (props.addButton === false) {
-        return index === 0 ? 'droite' : 'gauche';
+        return index === 0 ? t('global.right') : t('global.left');
     } else return index + 1;
 }
 </script>
@@ -199,7 +202,7 @@ function getLabelIdentifier(index) {
     <AddCard
         v-if="addButton !== false"
         :data-testid="`${id}-add`"
-        placeholder="Ajouter"
+        :placeholder="t('global.add')"
         class="add-card"
         @click="addCard"
     />
diff --git a/src/features/forms/components/inputs/badges/components/ConditionInput.vue b/src/features/forms/components/inputs/badges/components/ConditionInput.vue
index 89dca04c78fb032c72381e1344a2e2440acb926d..30962353c9707eb8825af7e2c582a64bae7a3d73 100644
--- a/src/features/forms/components/inputs/badges/components/ConditionInput.vue
+++ b/src/features/forms/components/inputs/badges/components/ConditionInput.vue
@@ -48,7 +48,7 @@ function handleMouseLeave() {
     </ul>
     <button class="btn btn-form" @click="onClick">
         <i class="icon-plus"></i>
-        Configurer les conditions
+        {{ $t('inputs.manageConditions') }}
     </button>
 </template>
 
diff --git a/src/features/forms/components/inputs/badges/components/IconPicker.vue b/src/features/forms/components/inputs/badges/components/IconPicker.vue
index 135fe49d8dc57ad9bb7dfb61f38076eadaf9a097..007fee3ba982f6ca2cdc4e466407bd6a42b3b1f1 100644
--- a/src/features/forms/components/inputs/badges/components/IconPicker.vue
+++ b/src/features/forms/components/inputs/badges/components/IconPicker.vue
@@ -19,7 +19,7 @@ function openIconModal() {
         <BadgeItem :icon="inputValue" :view-mode="true" :inactive="!inputValue" />
         <button class="btn btn-form" @click="openIconModal">
             <i class="icon-plus"></i>
-            Modifier l'icône
+            {{ $t('inputs.updateIcon') }}
         </button>
     </div>
 </template>
diff --git a/src/features/forms/components/inputs/card/components/RadioInput.vue b/src/features/forms/components/inputs/card/components/RadioInput.vue
index 626ca022c0018011dc31ad5f372a57e103c968cf..6b4ce097c3cb93739401676ed00dcb08fb9e2458 100644
--- a/src/features/forms/components/inputs/card/components/RadioInput.vue
+++ b/src/features/forms/components/inputs/card/components/RadioInput.vue
@@ -34,7 +34,7 @@ function onChange(value: string) {
                     :checked="inputValue === '1'"
                     @change="onChange('1')"
                 />
-                <label :for="'left-' + id">Choix gauche</label>
+                <label :for="'left-' + id">{{ $t('inputs.leftChoice') }}</label>
             </div>
             <div class="radio-btn">
                 <input
@@ -45,7 +45,7 @@ function onChange(value: string) {
                     type="radio"
                     @change="onChange('2')"
                 />
-                <label :for="'right-' + id">Choix droite</label>
+                <label :for="'right-' + id">{{ $t('inputs.rightChoice') }}</label>
             </div>
         </div>
     </div>
diff --git a/src/features/forms/components/inputs/card/components/SelectInput.vue b/src/features/forms/components/inputs/card/components/SelectInput.vue
index f7ae8a63e1808e7d2cbb586461d44da2dac55d65..14cef722abc91723cd41d9e673d8c90ee5f659e2 100644
--- a/src/features/forms/components/inputs/card/components/SelectInput.vue
+++ b/src/features/forms/components/inputs/card/components/SelectInput.vue
@@ -1,6 +1,8 @@
 <script setup lang="ts">
+import UiSelect from '@/src/components/ui/UiSelect.vue';
 import { getCurrentState } from '@/src/shared/services/undoRedo.service';
 import { useEditorStore } from '@/src/shared/stores';
+import { ref, watch, computed } from 'vue';
 
 const editorStore = useEditorStore();
 
@@ -22,29 +24,28 @@ const currentNode = editorStore.getCurrentGraphNode;
 const currentContent = currentNode.data.elements.find(({ id }) => id === editorStore.openedElementId);
 
 function getOptions() {
-    if(!props.linkedOptions) return props.options;
+    if (!props.linkedOptions) return props.options;
 
     // In this case we have to change the epoc formValues
     //? refactor this if another case is needed
-    if(props.id === 'template') {
+    if (props.id === 'template') {
         const epocNode = editorStore.getEpocNode;
 
         return walkObjectPath(epocNode.data.formValues, props.linkedOptions);
     } else {
         return currentContent.formValues[props.linkedOptions];
-
     }
 }
 
 function walkObjectPath(object: any, path: string) {
     const currentKey = path.split('.')[0];
 
-    if(!currentKey) {
+    if (!currentKey) {
         return object;
     }
 
-    if(currentKey === '*') {
-        if(!object) return [];
+    if (currentKey === '*') {
+        if (!object) return [];
 
         return object.map((item: any) => walkObjectPath(item, path.slice(2)));
     } else {
@@ -52,47 +53,16 @@ function walkObjectPath(object: any, path: string) {
     }
 }
 
-function onChange(event: Event) {
-    const target = event.target as HTMLInputElement;
-    const value = target.value;
+const input = ref(props.inputValue);
+watch(input, (newValue) => {
     const state = getCurrentState(true);
-
-    emit('change', value);
+    emit('change', newValue);
     emit('saveGivenState', state);
-}
+});
+
+const items = computed(() => [...getOptions().map((item: string) => ({ value: item, label: item }))]);
 </script>
 
 <template>
-    <div class="select">
-        <select :id="id" :value="inputValue" class="select-box" @change="onChange">
-            <option value="">Sélectionnez</option>
-            <option v-for="(option, index) in getOptions()" :key="index" :value="option">{{ option }}</option>
-        </select>
-    </div>
+    <UiSelect v-model="input" :options="items" />
 </template>
-
-<style scoped lang="scss">
-.select {
-    display: flex;
-    flex-direction: column;
-    margin: 1rem 0 0.5rem 0;
-    label {
-        margin-bottom: 0.5rem;
-    }
-    select {
-        appearance: none;
-        padding: 0.5rem;
-        border: 1px solid var(--border);
-        border-radius: 4px;
-        background-color: var(--item-background);
-        cursor: pointer;
-        font-size: 1rem;
-        color: var(--text);
-
-        background-image: url('data:image/svg+xml;base64,PD94bWwgdmVyc2lvbj0iMS4wIiBlbmNvZGluZz0iVVRGLTgiPz48c3ZnIHZlcnNpb249IjEuMSIgd2lkdGg9IjExcHgiIGhlaWdodD0iN3B4IiB2aWV3Qm94PSIwIDAgMTEuMCA3LjAiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgeG1sbnM6eGxpbms9Imh0dHA6Ly93d3cudzMub3JnLzE5OTkveGxpbmsiPjxkZWZzPjxjbGlwUGF0aCBpZD0iaTAiPjxwYXRoIGQ9Ik0yNDE4LDAgTDI0MTgsMjQyNiBMMCwyNDI2IEwwLDAgTDI0MTgsMCBaIj48L3BhdGg+PC9jbGlwUGF0aD48Y2xpcFBhdGggaWQ9ImkxIj48cGF0aCBkPSJNOS4yMDE3MjIyNywwIEM5LjYxNjUwNDA2LDAgOS45OTA4OTcyMSwwLjI3MzU1MDk4NyAxMC4xNDk4ODU5LDAuNjk0MzM1OTM3IEMxMC4zMDg4NzQ2LDEuMTE1MTIwODkgMTAuMjQ5ODk0OSwxLjU5OTYwOTM3IDkuOTU0OTk2NjMsMS45MTk1MzE0NiBMNS44ODA5MDQ1NSw2LjQxOTUzMTQ2IEM1LjY1MzMxOTM0LDYuNjQxMDE1NDEgNS4zOTA0NzQ3Niw2Ljc1IDUuMTI3NjMwMTksNi43NSBDNC44NjQ3ODU2Miw2Ljc1IDQuNjAyNTgxNzgsNi42NDAxMzY3MiA0LjQwMjI0Mjg2LDYuNDIwNDEwMTYgTDAuMzI4MTUwMjkxLDEuOTIwNDEwMTYgQzAuMDA2MTQ2NTU1MTksMS41OTk2MDkzNyAtMC4wODE2OTQ5MjIzLDEuMTE0NDUzMDIgMC4wNzcxMDE1NTQ3LDAuNjk2MDkzODU3IEMwLjIzNTg5ODAzMiwwLjI3NzczNDY5NyAwLjYxMDIyNzEwNiwwIDEuMDI0Njg4NTMsMCBMOS4yMDE3MjIyNywwIFoiPjwvcGF0aD48L2NsaXBQYXRoPjwvZGVmcz48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSgtMTMwNC4wIC0xMDYyLjApIj48ZyBjbGlwLXBhdGg9InVybCgjaTApIj48ZyB0cmFuc2Zvcm09InRyYW5zbGF0ZSg5ODAuMCA5MC4wKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjAuMCA3MjkuMCkiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMCA1MS4wKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMTUuMCAxNTAuMCkiPjxnIHRyYW5zZm9ybT0idHJhbnNsYXRlKDAuMCAyNC4wKSI+PGcgdHJhbnNmb3JtPSJ0cmFuc2xhdGUoMjg5LjM3MjM2OTgwNjgwMSAxOC4wKSI+PGcgY2xpcC1wYXRoPSJ1cmwoI2kxKSI+PHBvbHlnb24gcG9pbnRzPSItMS4xMTAyMjMwMmUtMTYsMCAxMC4yMzYwMTA0LDAgMTAuMjM2MDEwNCw2Ljc1IC0xLjExMDIyMzAyZS0xNiw2Ljc1IC0xLjExMDIyMzAyZS0xNiwwIiBzdHJva2U9Im5vbmUiIGZpbGw9IiMzNTQyNTgiPjwvcG9seWdvbj48L2c+PC9nPjwvZz48L2c+PC9nPjwvZz48L2c+PC9nPjwvZz48L3N2Zz4=');
-        background-repeat: no-repeat;
-        background-position: right 0.7rem top 50%;
-        background-size: 0.8rem auto;
-    }
-}
-</style>
diff --git a/src/features/settings/SettingsInput.vue b/src/features/settings/SettingsInput.vue
index d391601dd993ef9f776afbc30f4b354123a8f453..259fd89d2157d1df1a83b65aff6ab2a46374cf67 100644
--- a/src/features/settings/SettingsInput.vue
+++ b/src/features/settings/SettingsInput.vue
@@ -1,11 +1,16 @@
 <script setup lang="ts">
-import ToggleInput from "./ToggleInput.vue";
+import ToggleInput from './ToggleInput.vue';
 import { useVModel } from '@vueuse/core';
+import UiSelect from '@/src/components/ui/UiSelect.vue';
 
 const props = defineProps<{
-    type: 'toggle';
+    type: 'toggle' | 'select';
     label: string;
-    modelValue: boolean;
+    modelValue: boolean | string;
+    options?: {
+        label: string;
+        value: string;
+    }[];
 }>();
 
 const emit = defineEmits<{
@@ -14,19 +19,18 @@ const emit = defineEmits<{
 
 const data = useVModel(props, 'modelValue', emit);
 
-const id = "id" + Math.random().toString(16).slice(2)
-
+const id = 'id' + Math.random().toString(16).slice(2);
 </script>
 
 <template>
     <div class="settings-input">
         <label :for="id">{{ label }}</label>
-        <ToggleInput :id="id" v-model="data" />
+        <ToggleInput v-if="type === 'toggle'" :id="id" v-model="data" />
+        <UiSelect v-else-if="type === 'select'" :id="id" v-model="data" :options="options" />
     </div>
 </template>
 
 <style scoped lang="scss">
-
 .settings-input {
     display: flex;
     align-items: center;
diff --git a/src/features/settings/SettingsModal.vue b/src/features/settings/SettingsModal.vue
index 528a33a805847a7d3315fc782c890394359b08ef..05f29b4b4e567fa50c94dcdd84826e6049a44c6e 100644
--- a/src/features/settings/SettingsModal.vue
+++ b/src/features/settings/SettingsModal.vue
@@ -1,20 +1,19 @@
 <script setup lang="ts">
-import Modal from '@/src/components/LayoutModal.vue'
+import Modal from '@/src/components/LayoutModal.vue';
 import SettingsInput from './SettingsInput.vue';
-import ContentButton from '@/src/components/ContentButton.vue';
-import TopActionButton from '@/src/features/topBar/TopActionButton.vue';
-import { ref, onMounted, watch } from 'vue';
+import { ref, watch } from 'vue';
 import { useSettingsStore } from '@/src/shared/stores';
+import { useI18n } from 'vue-i18n';
+
+const { locale } = useI18n();
 
 const modal = ref(null);
 const settingsStore = useSettingsStore();
 const spellcheck = ref(false);
-
-onMounted(() => {
-    settingsStore.init();
-})
+const localeTmp = ref();
 
 function save() {
+    locale.value = localeTmp.value;
     settingsStore.setSettings(spellcheck.value);
     modal.value.close();
 }
@@ -23,29 +22,44 @@ function open() {
     modal.value.open();
 }
 
-watch(() => modal.value?.isOpen, (isOpen) => {
-    if (isOpen) spellcheck.value = settingsStore?.settings?.spellcheck;
-})
+watch(
+    () => modal.value?.isOpen,
+    (isOpen) => {
+        // set values here to make sure the settings were fetched from the store
+        if (isOpen) {
+            spellcheck.value = settingsStore?.settings?.spellcheck;
+            localeTmp.value = locale.value;
+        }
+    },
+);
 
 defineExpose({
-    open
-})
-
+    open,
+});
 </script>
 
 <template>
-    <Modal ref="modal" title="Paramètres">
+    <Modal ref="modal" :title="$t('settings.title')">
         <template v-if="modal" #trigger>
             <slot name="trigger" />
         </template>
 
         <div class="settings">
-            <SettingsInput v-model="spellcheck" type="toggle" label="Activer la vérification orthographique" />
+            <SettingsInput v-model="spellcheck" type="toggle" :label="$t('settings.spellcheck')" />
+            <SettingsInput
+                v-model="localeTmp"
+                type="select"
+                :label="$t('settings.lang')"
+                :options="[
+                    { label: 'English', value: 'en' },
+                    { label: 'Français', value: 'fr' },
+                ]"
+            />
         </div>
 
         <template #footer>
-            <button class="btn-choice cancel" @click="modal.close">Annuler</button>
-            <button class="btn-choice save" @click="save">Valider</button>
+            <button class="btn-choice cancel" @click="modal.close">{{ $t('global.cancel') }}</button>
+            <button class="btn-choice save" @click="save">{{ $t('global.save') }}</button>
         </template>
     </Modal>
 </template>
@@ -54,7 +68,7 @@ defineExpose({
 .settings {
     display: flex;
     flex-direction: column;
-    gap: .5rem;
+    gap: 0.5rem;
 }
 
 .btn-choice {
diff --git a/src/features/sideBar/components/ModelMenu.vue b/src/features/sideBar/components/ModelMenu.vue
index eccdcaf9a02497b97564a52e090c7bf70a4c2a02..77113d9b2adca0b30fc3c06bb0ffcaac17104820 100644
--- a/src/features/sideBar/components/ModelMenu.vue
+++ b/src/features/sideBar/components/ModelMenu.vue
@@ -7,7 +7,7 @@ const editorStore = useEditorStore();
 </script>
 
 <template>
-    <SideMenu title="Modèles de page" @close="editorStore.modelMenu = false">
+    <SideMenu :title="$t('models.title')" @close="editorStore.modelMenu = false">
         <div v-if="editorStore.pageModels.length > 0" class="models">
             <PageTemplate
                 v-for="(model, index) in editorStore.pageModels"
@@ -17,7 +17,7 @@ const editorStore = useEditorStore();
             />
         </div>
         <div v-else>
-            <h4 class="empty">Aucun modèle de page n'as été créé</h4>
+            <h4 class="empty">{{ $t('models.empty') }}</h4>
         </div>
     </SideMenu>
 </template>
diff --git a/src/features/sideBar/components/PageTemplate.vue b/src/features/sideBar/components/PageTemplate.vue
index ff7b968be486494bf4c47fc9f2187197b899f468..4216d9b99722af5ae3af2ff6e7d8d9b2b7230835 100644
--- a/src/features/sideBar/components/PageTemplate.vue
+++ b/src/features/sideBar/components/PageTemplate.vue
@@ -3,6 +3,9 @@ import { SideAction } from '@/src/shared/interfaces';
 import ContentButton from '@/src/components/ContentButton.vue';
 import { ref } from 'vue';
 import { useEditorStore } from '@/src/shared/stores';
+import { useI18n } from 'vue-i18n';
+
+const { t } = useI18n();
 
 defineProps<{
     elements: SideAction[];
@@ -11,7 +14,7 @@ defineProps<{
 
 const editorStore = useEditorStore();
 
-const templateTooltip = 'Glisser/déposer pour ajouter un modèle';
+const templateTooltip = t('models.dragdrop');
 
 const dragging = ref(false);
 
@@ -26,7 +29,7 @@ function dragStart(event: DragEvent, elements) {
 
 <template>
     <div class="container">
-        <p class="page-title">{{ name ? name : 'Modèle' }}</p>
+        <p class="page-title">{{ name ? name : t('models.model') }}</p>
         <!--suppress VueUnrecognizedDirective -->
         <div
             v-tippy="{
diff --git a/src/features/sideBar/components/SideActions.vue b/src/features/sideBar/components/SideActions.vue
index bb26f0a5e4e0a3269ced31535f92220839f5ddc5..ce3bcdb29262e7f507975a0e3454d81039f12ce9 100644
--- a/src/features/sideBar/components/SideActions.vue
+++ b/src/features/sideBar/components/SideActions.vue
@@ -1,6 +1,6 @@
 <script setup lang="ts">
 import { SideAction } from '@/src/shared/interfaces';
-import { ref } from 'vue';
+import { ref, computed } from 'vue';
 import ContentButton from '@/src/components/ContentButton.vue';
 import { useEditorStore } from '@/src/shared/stores';
 import { moveGuard } from '@/src/shared/utils/draggable';
@@ -9,15 +9,18 @@ import SettingsModal from '@/src/features/settings/SettingsModal.vue';
 
 const editorStore = useEditorStore();
 
-const standardContent = editorStore.standardPages.filter(({ type }) => {
-    const filteredPages = ['legacy-condition', 'condition', 'question', 'model', 'badge'];
-    const prodFilteredPages = env.isDev ? [] : [];
-    return ![...filteredPages, ...prodFilteredPages].includes(type);
-});
-const questionContent = editorStore.standardPages.find(({ type }) => type === 'question');
-const conditionContent = editorStore.standardPages.find(({ type }) => type === 'condition');
-const modelContent = editorStore.standardPages.find(({ type }) => type === 'model');
-const badgeContent = editorStore.standardPages.find(({ type }) => type === 'badge');
+const standardContent = computed(() =>
+    editorStore.standardPages.filter(({ type }) => {
+        const filteredPages = ['legacy-condition', 'condition', 'question', 'model', 'badge'];
+        const prodFilteredPages = env.isDev ? [] : [];
+        return ![...filteredPages, ...prodFilteredPages].includes(type);
+    }),
+);
+
+const questionContent = computed(() => editorStore.standardPages.find(({ type }) => type === 'question'));
+const conditionContent = computed(() => editorStore.standardPages.find(({ type }) => type === 'condition'));
+const modelContent = computed(() => editorStore.standardPages.find(({ type }) => type === 'model'));
+const badgeContent = computed(() => editorStore.standardPages.find(({ type }) => type === 'badge'));
 
 const dragging = ref(false);
 
@@ -56,7 +59,6 @@ function showTemplateMenu() {
 function showBadgeMenu() {
     editorStore.toggleSideMenu('badge');
 }
-
 </script>
 
 <template>
@@ -220,7 +222,9 @@ hr {
     transition: all 0.15s ease-in-out;
 }
 
-.questions-list, .actions-list, .contents-list {
+.questions-list,
+.actions-list,
+.contents-list {
     display: flex;
     flex-direction: column;
     gap: 1rem;
@@ -273,5 +277,4 @@ hr {
 .model-menu {
     flex: 1;
 }
-
 </style>
diff --git a/src/features/topBar/HamburgerMenu.vue b/src/features/topBar/HamburgerMenu.vue
index d4701bc920162bba9efda1cca535485d4fc26650..0a2f0e1aef53a07616b9836f8d00cf4c75278024 100644
--- a/src/features/topBar/HamburgerMenu.vue
+++ b/src/features/topBar/HamburgerMenu.vue
@@ -58,11 +58,11 @@ const settingsModal = ref(null);
             @click="emit('undo')"
         >
             <i class="icon-arriere"></i>
-            <span>Undo</span>
+            <span>{{ $t('header.undo') }}</span>
         </button>
         <button
             v-tippy="{
-                content: `${modifier} + ${editorStore.platform === 'darwin' ? '⇧ + z' : 'y' }`,
+                content: `${modifier} + ${editorStore.platform === 'darwin' ? '⇧ + z' : 'y'}`,
                 placement: 'left',
                 arrow: true,
                 arrowType: 'round',
@@ -73,7 +73,7 @@ const settingsModal = ref(null);
             @click="emit('redo')"
         >
             <i class="icon-avant"></i>
-            <span>Redo</span>
+            <span>{{ $t('header.redo') }}</span>
         </button>
         <button
             v-tippy="{
@@ -88,22 +88,22 @@ const settingsModal = ref(null);
             @click="emit('save')"
         >
             <i class="icon-save"></i>
-            <span>Sauvegarder</span>
+            <span>{{ $t('global.save') }}</span>
         </button>
         <button class="menu-item" :disabled="loadingPreview" @click="emit('runPreview')">
             <i class="icon-play"></i>
-            <span>Aperçu</span>
+            <span>{{ $t('header.preview') }}</span>
         </button>
         <button class="menu-item" :disabled="exporting" @click="emit('exportProject')">
             <i class="icon-export"></i>
-            <span>Publier</span>
+            <span>{{ $t('header.publish') }}</span>
         </button>
 
         <SettingsModal ref="settingsModal">
             <template #trigger>
                 <button class="menu-item" @click="settingsModal.open">
                     <i class="icon-settings"></i>
-                    <span>Paramètre</span>
+                    <span>{{ $t('settings.title') }}</span>
                 </button>
             </template>
         </SettingsModal>
diff --git a/src/features/topBar/TopActionDropdown.vue b/src/features/topBar/TopActionDropdown.vue
index 061b9e2a1027407bcc71df38f507554ba877c6df..075e6c279310cbc0e5db6b0ab2712688a46b99b5 100644
--- a/src/features/topBar/TopActionDropdown.vue
+++ b/src/features/topBar/TopActionDropdown.vue
@@ -24,7 +24,7 @@ const emit = defineEmits<{
         <i :class="icon" />
         <span v-if="text" class="text-top-bar">{{ text }}</span>
         <select id="select-box" :value="inputValue" :disabled="disabled" class="select-box" @change="onSelect">
-            <option value="0">Ajuster</option>
+            <option value="0">{{ $t('header.adjust') }}</option>
             <option value="0.5">50%</option>
             <option value="0.75">75%</option>
             <option value="1">100%</option>
diff --git a/src/features/topBar/TopActionsMenu.vue b/src/features/topBar/TopActionsMenu.vue
index 0b699578c9d121d64ab0112da3e4ffe3f5cd9344..c9a69f7956f64d0edc29239781517289d1555810 100644
--- a/src/features/topBar/TopActionsMenu.vue
+++ b/src/features/topBar/TopActionsMenu.vue
@@ -28,7 +28,7 @@ const emit = defineEmits<{
 }>();
 
 // detect the platform
-const modifier = computed(() => editorStore.platform === 'darwin' ? '⌘' : 'Ctrl');
+const modifier = computed(() => (editorStore.platform === 'darwin' ? '⌘' : 'Ctrl'));
 
 const settingsModal = ref(null);
 </script>
@@ -59,7 +59,7 @@ const settingsModal = ref(null);
             />
             <TopActionButton
                 v-tippy="{
-                    content: `${modifier} + ${editorStore.platform === 'darwin' ? '⇧ + z' : 'y' }`,
+                    content: `${modifier} + ${editorStore.platform === 'darwin' ? '⇧ + z' : 'y'}`,
                     placement: 'bottom',
                     arrow: true,
                     arrowType: 'round',
@@ -81,21 +81,21 @@ const settingsModal = ref(null);
                     animation: 'fade',
                 }"
                 icon="icon-save"
-                text="Sauvegarder"
+                :text="$t('global.save')"
                 position="right"
                 :disabled="saving"
                 @click="emit('save')"
             />
             <TopActionButton
                 icon="icon-play"
-                text="Aperçu"
+                :text="$t('header.preview')"
                 position="right"
                 :disabled="loadingPreview"
                 @click="emit('runPreview')"
             />
             <TopActionButton
                 icon="icon-export"
-                text="Publier"
+                :text="$t('header.publish')"
                 position="right"
                 :disabled="exporting"
                 @click="emit('exportProject')"
@@ -105,7 +105,7 @@ const settingsModal = ref(null);
                 <template #trigger>
                     <TopActionButton
                         icon="icon-settings"
-                        text="Paramètres"
+                        :text="$t('settings.title')"
                         position="right"
                         @click="settingsModal.open"
                     />
diff --git a/src/features/topBar/TopBar.vue b/src/features/topBar/TopBar.vue
index 0e45eb53167987e45aabd3073ab2761ae9b6a54c..a76829f41ee45c937787e06e51569a5cde09376b 100644
--- a/src/features/topBar/TopBar.vue
+++ b/src/features/topBar/TopBar.vue
@@ -4,20 +4,17 @@ import { computed, ref } from 'vue';
 import { editorService } from '@/src/shared/services';
 import { useVueFlow } from '@vue-flow/core';
 import TopActionsMenu from '@/src/features/topBar/TopActionsMenu.vue';
+import { useI18n } from 'vue-i18n';
+
+const { t, locale } = useI18n();
 
 const editorStore = useEditorStore();
 const undoRedoStore = useUndoRedoStore();
 
 const { zoomTo, fitView, onViewportChangeEnd } = useVueFlow('main');
 
-editorStore.$subscribe(() => {
-    savedSince.value = since(editorStore.currentProject.modified);
-});
-
-const savedSince = ref(since(editorStore.currentProject.modified));
-
 const zoom = ref(1);
-const zoomString = computed(() => (zoom.value === 0 ? 'Ajuster' : `${Math.round(zoom.value * 100)}%`));
+const zoomString = computed(() => (zoom.value === 0 ? t('header.adjust') : `${Math.round(zoom.value * 100)}%`));
 
 zoomTo(zoom.value);
 
@@ -34,38 +31,49 @@ function updateZoom(val: number) {
     }
 }
 
-function since(date: string) {
-    if (!date) return 'jamais';
+const savedSince = computed(() => {
+    const date = editorStore.currentProject.modified;
+    if (!date) return t('header.never');
     const milliseconds = Math.abs(Date.now() - new Date(date).getTime());
     const secs = Math.floor(Math.abs(milliseconds) / 1000);
     const mins = Math.floor(secs / 60);
     const hours = Math.floor(mins / 60);
     const days = Math.floor(hours / 24);
 
-    return (
-        'Il y a ' +
-        (days > 1 ? `${days} jours` : hours > 1 ? `${hours} heures` : mins > 1 ? `${mins} mins` : "moins d'une minute")
-    );
-}
-
-setInterval(() => {
-    savedSince.value = since(editorStore.currentProject.modified);
-}, 60000);
+    if (locale.value === 'fr') {
+        return (
+            'Il y a ' +
+            (days > 1 ? `${days} jours`
+            : hours > 1 ? `${hours} heures`
+            : mins > 1 ? `${mins} mins`
+            : "moins d'une minute")
+        );
+    } else {
+        return (
+            (days > 1 ? `${days} days`
+            : hours > 1 ? `${hours} hours`
+            : mins > 1 ? `${mins} mins`
+            : 'less than a minute') + ' ago'
+        );
+    }
+});
 
 function separateFilePath(filepath: string) {
     const file = filepath.split('/').pop();
     return [filepath.replace('/' + file, ''), '/' + file];
 }
-
 </script>
 
 <template>
     <div class="top-bar">
         <div class="top-bar-content">
             <div class="top-bar-title">
-                <h3 v-if="!editorStore.currentProject.filepath">Nouvel ePoc</h3>
-                <h3 v-else><span>{{ separateFilePath(editorStore.currentProject.filepath)[0] }}</span>{{ separateFilePath(editorStore.currentProject.filepath)[1] }}</h3>
-                <small>Dernière sauvegarde : {{ savedSince }}</small>
+                <h3 v-if="!editorStore.currentProject.filepath">{{ t('header.new') }}</h3>
+                <h3 v-else>
+                    <span>{{ separateFilePath(editorStore.currentProject.filepath)[0] }}</span>
+                    {{ separateFilePath(editorStore.currentProject.filepath)[1] }}
+                </h3>
+                <small>{{ t('header.lastSave') }} {{ savedSince }}</small>
             </div>
             <TopActionsMenu
                 :undo-disabled="undoRedoStore.undoStack.length <= 0"
diff --git a/src/main.ts b/src/main.ts
index 99d548e14f881d03cf3b7f4005daa78e238d0216..d98ecb5b22a93d32f8dc457cb255103f9e6f74c0 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -6,10 +6,13 @@ import { createPinia } from 'pinia';
 import VueTippy from 'vue-tippy';
 import 'tippy.js/dist/tippy.css';
 import draggable from 'vuedraggable';
+import { i18n } from '@/i18n/config';
 
 const app = createApp(App);
+const pinia = createPinia();
+app.use(pinia);
 app.use(router);
-app.use(createPinia());
 app.use(VueTippy);
+app.use(i18n);
 app.component('VueDraggable', draggable);
 app.mount('#app');
diff --git a/src/router.ts b/src/router.ts
index a06482a067c92d8c2df4242e95e220ab4050040a..c263ba9da2cca26f07cbfca80650ba23e2b30357 100644
--- a/src/router.ts
+++ b/src/router.ts
@@ -1,6 +1,6 @@
 import { createRouter, createWebHashHistory } from 'vue-router';
 
-export const router = createRouter({
+const router = createRouter({
     history: createWebHashHistory(),
     routes: [
         {
@@ -17,3 +17,5 @@ export const router = createRouter({
         },
     ],
 });
+
+export { router };
diff --git a/src/shared/data/badge.data.ts b/src/shared/data/badge.data.ts
index 678d8aa1e8caedffd1b1e97acf1c5228f90d2cf5..80c2640b398910579165e1d702703155b7689e48 100644
--- a/src/shared/data/badge.data.ts
+++ b/src/shared/data/badge.data.ts
@@ -1,21 +1,24 @@
 import env from '@/src/shared/utils/env';
 import { ElementType, VerbKey, Verbs } from '@/src/shared/interfaces';
+import { i18n } from '@/i18n/config';
+import { computed, ComputedRef } from 'vue';
+
 export const iconsPath = env.isDev ? '/img/badge/icon' : 'img/badge/icon';
 
 export const defaultBadgeIcons = ['audio', 'check', 'condition', 'cup', 'puzzle', 'question', 'star', 'video'];
 
-export const verbs: Verbs = {
-    started: { label: 'Commencé', valueType: 'boolean' },
-    completed: { label: 'Terminé', valueType: 'boolean' },
-    viewed: { label: 'Vu', valueType: 'boolean' },
-    read: { label: 'Lu', valueType: 'boolean' },
-    played: { label: 'Joué', valueType: 'boolean' },
-    watched: { label: 'Regardé', valueType: 'boolean' },
-    listened: { label: 'Écouté', valueType: 'boolean' },
-    attempted: { label: 'Tenté', valueType: 'boolean' },
-    scored: { label: 'Obtenu un score de', valueType: 'number' },
-    passed: { label: 'Réussi', valueType: 'boolean' },
-};
+export const verbs: ComputedRef<Verbs> = computed(() => ({
+    started: { label: i18n.global.t('verbs.started'), valueType: 'boolean' },
+    completed: { label: i18n.global.t('verbs.completed'), valueType: 'boolean' },
+    viewed: { label: i18n.global.t('verbs.viewed'), valueType: 'boolean' },
+    read: { label: i18n.global.t('verbs.read'), valueType: 'boolean' },
+    played: { label: i18n.global.t('verbs.played'), valueType: 'boolean' },
+    watched: { label: i18n.global.t('verbs.watched'), valueType: 'boolean' },
+    listened: { label: i18n.global.t('verbs.listened'), valueType: 'boolean' },
+    attempted: { label: i18n.global.t('verbs.attempted'), valueType: 'boolean' },
+    scored: { label: i18n.global.t('verbs.scored'), valueType: 'number' },
+    passed: { label: i18n.global.t('verbs.passed'), valueType: 'boolean' },
+}));
 
 export const elementVerbs: Record<ElementType, VerbKey[]> = {
     chapter: ['started'],
diff --git a/src/shared/data/form.data.ts b/src/shared/data/form.data.ts
index ddc656765f5bc30b7bcbc000e4a89fa75167ddc4..334839878a0930199d2db698159f414007eb8ab3 100644
--- a/src/shared/data/form.data.ts
+++ b/src/shared/data/form.data.ts
@@ -1,4 +1,10 @@
 import { Form } from '@/src/shared/interfaces';
 import { elementForms, questionForms, nodeForms, badgeForms } from './forms';
+import { computed, ComputedRef } from 'vue';
 
-export const formsModel: Form[] = [...elementForms, ...questionForms, ...nodeForms, ...badgeForms];
+export const formsModel: ComputedRef<Form[]> = computed(() => [
+    ...elementForms.value,
+    ...questionForms.value,
+    ...nodeForms.value,
+    ...badgeForms.value,
+]);
diff --git a/src/shared/data/forms/badgeForm.data.ts b/src/shared/data/forms/badgeForm.data.ts
index cd2ff1590b6f376897ebdcba51a125d0ef06f592..0c3e55a9e53408f8ab8aea3a36ecd43e502715af 100644
--- a/src/shared/data/forms/badgeForm.data.ts
+++ b/src/shared/data/forms/badgeForm.data.ts
@@ -1,54 +1,58 @@
 import { Form } from '@/src/shared/interfaces';
 import { badgeButtons } from './formButtons.data';
+import { computed, ComputedRef } from 'vue';
+import { i18n } from '@/i18n/config';
 
-export const customBadgeForm: Form = {
-    type: 'badge',
-    name: 'Paramètres du badge',
-    icon: 'icon-badge',
-    buttons: badgeButtons,
-    fields: [
-        {
-            inputs: [
-                {
-                    id: 'title',
-                    type: 'text',
-                    label: 'Titre',
-                    value: '',
-                    placeholder: 'Saisissez...',
-                },
-                {
-                    id: 'icon',
-                    type: 'icon-picker',
-                    label: 'Icône du badge',
-                    value: '',
-                    placeholder: "Modifier l'icône",
-                },
-            ],
-        },
-        {
-            name: "Conditions d'obtention du badge",
-            inputs: [
-                {
-                    id: 'conditions',
-                    type: 'badge-conditions',
-                    label: '',
-                    value: [],
-                },
-            ],
-        },
-        {
-            name: 'Présentation du badge',
-            inputs: [
-                {
-                    id: 'description',
-                    type: 'textarea',
-                    label: '',
-                    value: '',
-                    placeholder: 'Saisissez une présentation du badge',
-                },
-            ],
-        },
-    ],
-};
+export const customBadgeForm: ComputedRef<Form> = computed(() => {
+    return {
+        type: 'badge',
+        name: i18n.global.t('forms.badge.text'),
+        icon: 'icon-badge',
+        buttons: badgeButtons.value,
+        fields: [
+            {
+                inputs: [
+                    {
+                        id: 'title',
+                        type: 'text',
+                        label: i18n.global.t('forms.node.title'),
+                        value: '',
+                        placeholder: i18n.global.t('forms.type'),
+                    },
+                    {
+                        id: 'icon',
+                        type: 'icon-picker',
+                        label: i18n.global.t('forms.badge.icon'),
+                        value: '',
+                        placeholder: i18n.global.t('forms.badge.updateIcon'),
+                    },
+                ],
+            },
+            {
+                name: i18n.global.t('forms.badge.obtention'),
+                inputs: [
+                    {
+                        id: 'conditions',
+                        type: 'badge-conditions',
+                        label: '',
+                        value: [],
+                    },
+                ],
+            },
+            {
+                name: i18n.global.t('forms.badge.presentation'),
+                inputs: [
+                    {
+                        id: 'description',
+                        type: 'textarea',
+                        label: '',
+                        value: '',
+                        placeholder: i18n.global.t('forms.badge.presentationPlaceholder'),
+                    },
+                ],
+            },
+        ],
+    };
+});
 
-export const badgeForms: Form[] = [customBadgeForm];
+export const badgeForms: ComputedRef<Form[]> = computed(() => [customBadgeForm.value]);
diff --git a/src/shared/data/forms/contentForm.data.ts b/src/shared/data/forms/contentForm.data.ts
index 20a0f2cdb2cbdf4a1ceee9a3a1a443aecc2fe6db..0dd25100acc53e33de0e16aa389bd20ad0556e02 100644
--- a/src/shared/data/forms/contentForm.data.ts
+++ b/src/shared/data/forms/contentForm.data.ts
@@ -1,153 +1,161 @@
 import { Form } from '@/src/shared/interfaces';
 import { contentButtons } from './formButtons.data';
+import { computed, ComputedRef } from 'vue';
+import { i18n } from '@/i18n/config';
 
-export const textForm: Form = {
-    type: 'text',
-    name: 'Contenu',
-    icon: 'icon-texte',
-    buttons: contentButtons,
-    fields: [
-        {
-            inputs: [
-                {
-                    id: 'html',
-                    type: 'html',
-                    label: '',
-                    value: '',
-                    placeholder: 'Saisissez un résumé...',
-                },
-            ],
-        },
-    ],
-};
+export const textForm: ComputedRef<Form> = computed(() => {
+    return {
+        type: 'text',
+        name: i18n.global.t('forms.content.text'),
+        icon: 'icon-texte',
+        buttons: contentButtons.value,
+        fields: [
+            {
+                inputs: [
+                    {
+                        id: 'html',
+                        type: 'html',
+                        label: '',
+                        value: '',
+                        placeholder: i18n.global.t('type'),
+                    },
+                ],
+            },
+        ],
+    };
+});
 
-export const videoForm: Form = {
-    type: 'video',
-    name: 'Vidéo',
-    icon: 'icon-video',
-    buttons: contentButtons,
-    fields: [
-        {
-            inputs: [
-                {
-                    id: 'source',
-                    type: 'file',
-                    label: 'Vidéo',
-                    placeholder: 'Ajouter une vidéo',
-                    value: '',
-                    accept: '.mp4',
-                    hint: 'Format recommandé: 16:9 (720x480)'
-                },
-                {
-                    id: 'summary',
-                    type: 'html',
-                    label: 'Résumé',
-                    value: '',
-                    placeholder: 'Saisissez...',
-                },
-                {
-                    id: 'transcript',
-                    type: 'file',
-                    label: 'Transcription',
-                    value: '',
-                    placeholder: 'Ajouter une transcription',
-                    accept: '.txt,.vtt',
-                    hint: 'Extensions acceptées : .vtt, .txt <br>Pour les utilisateurs qui ne souhaitent pas ou ne sont pas en capacité d\'écouter la vidéo'
-                },
-                {
-                    id: 'poster',
-                    type: 'file',
-                    label: 'Vignette',
-                    value: '',
-                    placeholder: 'Ajouter une vignette',
-                    accept: '.png,.jpg,.jpeg,.gif,.bmp,.svg,.webp',
-                    hint: 'Format recommandé: idem à la vidéo'
-                },
-            ],
-        },
-        {
-            name: 'Sous-titres',
-            inputs: [
-                {
-                    id: 'subtitles',
-                    label: 'Sous-titres',
-                    type: 'repeat',
-                    value: [],
-                    inputs: [
-                        {
-                            id: 'label',
-                            type: 'text',
-                            label: 'Nom de la langue',
-                            value: '',
-                            placeholder: 'English',
-                        },
-                        {
-                            id: 'lang',
-                            type: 'text',
-                            label: 'Code de langue',
-                            value: '',
-                            placeholder: 'en',
-                        },
-                        {
-                            id: 'src',
-                            type: 'file',
-                            label: 'Fichier',
-                            value: '',
-                            placeholder: 'Ajouter des sous-titres',
-                            accept: '.vtt',
-                            hint: 'Extensions acceptées : .vtt'
-                        },
-                    ],
-                },
-            ],
-        },
-    ],
-};
+export const videoForm: ComputedRef<Form> = computed(() => {
+    return {
+        type: 'video',
+        name: i18n.global.t('forms.content.video.label'),
+        icon: 'icon-video',
+        buttons: contentButtons.value,
+        fields: [
+            {
+                inputs: [
+                    {
+                        id: 'source',
+                        type: 'file',
+                        label: i18n.global.t('forms.content.video.label'),
+                        placeholder: i18n.global.t('forms.content.video.placeholder'),
+                        value: '',
+                        accept: '.mp4',
+                        hint: i18n.global.t('forms.content.video.hint', { format: '16:9 (720x480)' }),
+                    },
+                    {
+                        id: 'summary',
+                        type: 'html',
+                        label: i18n.global.t('forms.content.summary'),
+                        value: '',
+                        placeholder: i18n.global.t('forms.type'),
+                    },
+                    {
+                        id: 'transcript',
+                        type: 'file',
+                        label: i18n.global.t('forms.content.transcription.label'),
+                        value: '',
+                        placeholder: i18n.global.t('forms.content.transcription.placeholder'),
+                        accept: '.txt,.vtt',
+                        hint: i18n.global.t('forms.content.transcription.hint', { extensions: '.txt,.vtt' }),
+                    },
+                    {
+                        id: 'poster',
+                        type: 'file',
+                        label: i18n.global.t('forms.content.thumbnail.label'),
+                        value: '',
+                        placeholder: i18n.global.t('forms.content.thumbnail.placeholder'),
+                        accept: '.png,.jpg,.jpeg,.gif,.bmp,.svg,.webp',
+                        hint: i18n.global.t('forms.content.thumbnail.hint'),
+                    },
+                ],
+            },
+            {
+                name: i18n.global.t('forms.node.subtitle'),
+                inputs: [
+                    {
+                        id: 'subtitles',
+                        label: i18n.global.t('forms.node.subtitle'),
+                        type: 'repeat',
+                        value: [],
+                        inputs: [
+                            {
+                                id: 'label',
+                                type: 'text',
+                                label: i18n.global.t('forms.content.subtitle.label'),
+                                value: '',
+                                placeholder: 'English',
+                            },
+                            {
+                                id: 'lang',
+                                type: 'text',
+                                label: i18n.global.t('forms.content.subtitle.code'),
+                                value: '',
+                                placeholder: 'en',
+                            },
+                            {
+                                id: 'src',
+                                type: 'file',
+                                label: i18n.global.t('global.file'),
+                                value: '',
+                                placeholder: i18n.global.t('forms.content.subtitle.placeholder'),
+                                accept: '.vtt',
+                                hint: i18n.global.t('forms.content.subtitle.hint', { extensions: '.vtt' }),
+                            },
+                        ],
+                    },
+                ],
+            },
+        ],
+    };
+});
 
-export const audioForm: Form = {
-    type: 'audio',
-    name: 'Audio',
-    icon: 'icon-audio',
-    buttons: contentButtons,
-    fields: [
-        {
-            inputs: [
-                {
-                    id: 'source',
-                    type: 'file',
-                    label: 'Piste audio',
-                    placeholder: 'Ajouter une piste audio',
-                    value: '',
-                    accept: '.mp3',
-                },
-                {
-                    id: 'summary',
-                    type: 'html',
-                    label: 'Résumé',
-                    value: '',
-                    placeholder: 'Saisissez...',
-                },
-                {
-                    id: 'transcript',
-                    type: 'file',
-                    label: 'Transcription',
-                    value: '',
-                    placeholder: 'Ajouter une transcription',
-                    accept: '.txt,.vtt',
-                    hint: 'Extensions acceptées : .vtt, .txt <br>Pour les utilisateurs qui ne souhaitent pas ou ne sont pas en capacité d\'écouter la piste audio'
-                },
-                {
-                    id: 'subtitles',
-                    type: 'file',
-                    label: 'Sous-titres',
-                    value: '',
-                    placeholder: 'Ajouter des sous-titres',
-                    accept: '.vtt',
-                    hint: 'Extensions acceptées: .vtt'
-                },
-            ],
-        },
-    ],
-};
+export const audioForm: ComputedRef<Form> = computed(() => {
+    return {
+        type: 'audio',
+        name: i18n.global.t('forms.content.audio.label'),
+        icon: 'icon-audio',
+        buttons: contentButtons.value,
+        fields: [
+            {
+                inputs: [
+                    {
+                        id: 'source',
+                        type: 'file',
+                        label: i18n.global.t('forms.content.audio.label'),
+                        placeholder: i18n.global.t('forms.content.audio.placeholder'),
+                        value: '',
+                        accept: '.mp3',
+                    },
+                    {
+                        id: 'summary',
+                        type: 'html',
+                        label: i18n.global.t('forms.content.summary'),
+                        value: '',
+                        placeholder: i18n.global.t('forms.type'),
+                    },
+                    {
+                        id: 'transcript',
+                        type: 'file',
+                        label: i18n.global.t('forms.content.transcription.label'),
+                        placeholder: i18n.global.t('forms.content.transcription.placeholder'),
+                        value: '',
+                        accept: '.txt,.vtt',
+                        hint: i18n.global.t('forms.content.transcription.hint', { extensions: '.txt,.vtt' }),
+                    },
+                    {
+                        id: 'subtitles',
+                        type: 'file',
+                        label: i18n.global.t('forms.content.subtitle.label'),
+                        value: '',
+                        placeholder: i18n.global.t('forms.content.subtitle.placeholder'),
+                        accept: '.vtt',
+                        hint: i18n.global.t('forms.content.subtitle.hint', { extensions: '.vtt' }),
+                    },
+                ],
+            },
+        ],
+    };
+});
 
-export const elementForms: Form[] = [textForm, videoForm, audioForm];
+export const elementForms: ComputedRef<Form[]> = computed(() => [textForm.value, videoForm.value, audioForm.value]);
diff --git a/src/shared/data/forms/formButtons.data.ts b/src/shared/data/forms/formButtons.data.ts
index 2787e704fd4dbf5a61c918fee4a9348dedb7bfcd..86b4819aedbeec26f1cae1b9f96b653dc0c93a51 100644
--- a/src/shared/data/forms/formButtons.data.ts
+++ b/src/shared/data/forms/formButtons.data.ts
@@ -1,39 +1,68 @@
 import { FormButton } from '@/src/shared/interfaces';
 import env from '@/src/shared/utils/env';
+import { computed, ComputedRef } from 'vue';
+import { i18n } from '@/i18n/config';
 
-export const baseButtons = [
-    { label: 'Supprimer', icon: 'icon-supprimer', action: 'delete' },
-    { label: 'Ajouter un badge', icon: 'icon-plus', action: 'add-badge' },
-];
+export const baseButtons: ComputedRef<FormButton[]> = computed(() => {
+    return [
+        { label: i18n.global.t('global.delete'), icon: 'icon-supprimer', action: 'delete' },
+        { label: i18n.global.t('forms.buttons.addBadge'), icon: 'icon-plus', action: 'add-badge' },
+    ];
+});
 
-export const pageButtons: FormButton[] =
-    env.isDev ?
-        [
-            ...baseButtons,
-            { label: 'Dupliquer la page', icon: 'icon-plus', action: 'duplicate-page' },
-            { label: 'Sauvegarder le modèle', icon: 'icon-modele', action: 'save-model' },
-        ]
-        :   [...baseButtons, { label: 'Dupliquer la page', icon: 'icon-plus', action: 'duplicate-page' }];
+export const pageButtons: ComputedRef<FormButton[]> = computed(() => {
+    if (env.isDev) {
+        return [
+            ...baseButtons.value,
+            { label: i18n.global.t('forms.buttons.duplicatePage'), icon: 'icon-plus', action: 'duplicate-page' },
+            { label: i18n.global.t('forms.buttons.saveModel'), icon: 'icon-modele', action: 'save-model' },
+        ];
+    }
 
-export const activityButtons: FormButton[] =
-    env.isDev ?
-        [
-            ...baseButtons,
-            { label: "Dupliquer l'évaluation", icon: 'icon-plus', action: 'duplicate-page' },
-            { label: 'Sauvegarder le modèle', icon: 'icon-modele', action: 'save-model' },
-        ]
-        :   [...baseButtons, { label: "Dupliquer l'évaluation", icon: 'icon-plus', action: 'duplicate-page' }];
+    return [
+        ...baseButtons.value,
+        { label: i18n.global.t('forms.buttons.duplicatePage'), icon: 'icon-plus', action: 'duplicate-page' },
+    ];
+});
 
-export const contentButtons: FormButton[] =
-    env.isDev ?
-        [
-            ...baseButtons,
-            { label: 'Revenir à la page', icon: 'icon-ecran', action: 'back-to-page' },
-            { label: "Dupliquer l'élément", icon: 'icon-plus', action: 'duplicate-element' },
-        ]
-        :   [...baseButtons];
+export const activityButtons: ComputedRef<FormButton[]> = computed(() => {
+    return env.isDev ?
+            [
+                ...baseButtons.value,
+                {
+                    label: i18n.global.t('forms.buttons.duplicateEvaluation'),
+                    icon: 'icon-plus',
+                    action: 'duplicate-page',
+                },
+                { label: i18n.global.t('forms.buttons.saveModel'), icon: 'icon-modele', action: 'save-model' },
+            ]
+        :   [
+                ...baseButtons.value,
+                {
+                    label: i18n.global.t('forms.buttons.duplicateEvaluation'),
+                    icon: 'icon-plus',
+                    action: 'duplicate-page',
+                },
+            ];
+});
 
-export const badgeButtons: FormButton[] = [
-    { label: 'Supprimer', icon: 'icon-supprimer', action: 'delete-badge' },
-    { label: "Revenir à l'ePoc", icon: 'icon-epoc', action: 'back-to-epoc' },
-];
+export const contentButtons: ComputedRef<FormButton[]> = computed(() => {
+    return env.isDev ?
+            [
+                ...baseButtons.value,
+                { label: i18n.global.t('forms.buttons.backToPage'), icon: 'icon-ecran', action: 'back-to-page' },
+                {
+                    label: i18n.global.t('forms.buttons.duplicateElement'),
+                    icon: 'icon-plus',
+                    action: 'duplicate-element',
+                },
+            ]
+        :   [...baseButtons.value];
+});
+
+export const badgeButtons: ComputedRef<FormButton[]> = computed(() => {
+    return [
+        { label: i18n.global.t('global.delete'), icon: 'icon-supprimer', action: 'delete-badge' },
+        { label: i18n.global.t('forms.buttons.backToEpoc'), icon: 'icon-epoc', action: 'back-to-epoc' },
+    ];
+});
diff --git a/src/shared/data/forms/nodeForm.data.ts b/src/shared/data/forms/nodeForm.data.ts
index b4e4d4bad8f78a9a5e1d4052ddfbc7ddcf70247b..66c8d0a9985b4c57c20b682481a50d8ce78817fa 100644
--- a/src/shared/data/forms/nodeForm.data.ts
+++ b/src/shared/data/forms/nodeForm.data.ts
@@ -1,11 +1,13 @@
 import { Form } from '@/src/shared/interfaces';
 import { activityButtons, baseButtons, pageButtons } from './formButtons.data';
+import { computed, ComputedRef } from 'vue';
+import { i18n } from '@/i18n/config';
 
-export const conditionForm: Form = {
+export const conditionForm: ComputedRef<Form> = computed(() => ({
     type: 'condition',
-    name: 'Conditions',
+    name: i18n.global.t('global.conditions'),
     icon: 'icon-condition',
-    buttons: baseButtons,
+    buttons: baseButtons.value,
     fields: [
         {
             inputs: [
@@ -14,34 +16,34 @@ export const conditionForm: Form = {
                     type: 'text',
                     label: '',
                     value: '',
-                    placeholder: 'Saisissez la condition 1...',
+                    placeholder: i18n.global.t('forms.node.conditionPlaceholder', { condition: '1' }),
                 },
                 {
                     id: 'condition2',
                     type: 'text',
                     label: '',
                     value: '',
-                    placeholder: 'Saisissez la condition 2...',
+                    placeholder: i18n.global.t('forms.node.conditionPlaceholder', { condition: '2' }),
                 },
             ],
         },
     ],
-};
+}));
 
-export const legacyConditionForm: Form = {
+export const legacyConditionForm: ComputedRef<Form> = computed(() => ({
     type: 'legacy-condition',
     name: 'Conditions (legacy)',
     icon: 'icon-condition',
-    buttons: baseButtons,
+    buttons: baseButtons.value,
     fields: [
         {
             inputs: [
                 {
                     id: 'label',
                     type: 'text',
-                    label: 'Label',
+                    label: i18n.global.t('global.label'),
                     value: '',
-                    placeholder: 'Saisissez...',
+                    placeholder: i18n.global.t('forms.type'),
                 },
             ],
         },
@@ -50,15 +52,18 @@ export const legacyConditionForm: Form = {
             inputs: [
                 {
                     id: 'choices',
-                    label: 'Choix',
+                    label: i18n.global.t('forms.node.choice'),
                     type: 'repeat',
-                    value: ['Parcours A', 'Parcours B'],
+                    value: [
+                        i18n.global.t('forms.node.course', { course: 'A' }),
+                        i18n.global.t('forms.node.course', { course: 'B' }),
+                    ],
                     inputs: [
                         {
                             id: '',
                             type: 'text',
                             label: '',
-                            placeholder: 'Parcours X',
+                            placeholder: i18n.global.t('forms.node.course', { course: 'X' }),
                             value: '',
                         },
                     ],
@@ -66,11 +71,11 @@ export const legacyConditionForm: Form = {
             ],
         },
         {
-            name: 'Contenus conditionnels',
+            name: i18n.global.t('forms.node.conditional'),
             inputs: [
                 {
                     id: 'conditionalFlag',
-                    label: 'Contenu',
+                    label: i18n.global.t('forms.content.text'),
                     type: 'repeat',
                     value: [],
                     inputs: [
@@ -78,7 +83,7 @@ export const legacyConditionForm: Form = {
                             id: 'id',
                             type: 'text',
                             label: '',
-                            placeholder: 'Contenu',
+                            placeholder: i18n.global.t('forms.type'),
                             value: '',
                         },
                         {
@@ -95,37 +100,37 @@ export const legacyConditionForm: Form = {
             ],
         },
     ],
-};
+}));
 
-export const chapterForm: Form = {
+export const chapterForm: ComputedRef<Form> = computed(() => ({
     type: 'chapter',
-    name: 'Chapitre',
+    name: i18n.global.t('global.chapter'),
     icon: 'icon-chapitre',
-    buttons: baseButtons,
+    buttons: baseButtons.value,
     fields: [
         {
             inputs: [
                 {
                     id: 'title',
                     type: 'text',
-                    label: 'Titre',
+                    label: i18n.global.t('forms.node.title'),
                     value: '',
-                    placeholder: 'Saisissez...',
+                    placeholder: i18n.global.t('forms.type'),
                 },
                 {
                     id: 'duration',
                     type: 'score',
-                    label: 'Durée (en minutes)',
+                    label: i18n.global.t('forms.node.duration'),
                     value: 0,
                 },
             ],
         },
         {
-            name: 'Objectifs pédagogiques',
+            name: i18n.global.t('forms.node.objectives'),
             inputs: [
                 {
                     id: 'objectives',
-                    label: 'Objectif',
+                    label: i18n.global.t('global.objective'),
                     type: 'repeat',
                     value: [],
                     inputs: [
@@ -133,7 +138,7 @@ export const chapterForm: Form = {
                             id: '',
                             type: 'textarea',
                             label: '',
-                            placeholder: 'Saisissez un objectif ...',
+                            placeholder: i18n.global.t('forms.type'),
                             value: '',
                         },
                     ],
@@ -141,11 +146,11 @@ export const chapterForm: Form = {
             ],
         },
     ],
-};
+}));
 
-export const epocForm: Form = {
+export const epocForm: ComputedRef<Form> = computed(() => ({
     type: 'epoc',
-    name: "A propos de l'ePoc",
+    name: i18n.global.t('forms.node.about'),
     icon: 'icon-epoc',
     buttons: [],
     fields: [
@@ -154,90 +159,90 @@ export const epocForm: Form = {
                 {
                     id: 'title',
                     type: 'text',
-                    label: 'Titre',
+                    label: i18n.global.t('forms.node.title'),
                     value: '',
-                    placeholder: 'Saisissez...',
+                    placeholder: i18n.global.t('forms.type'),
                 },
                 {
                     id: 'image',
                     type: 'file',
-                    label: 'Image de couverture',
-                    placeholder: 'Ajouter une image de couverture',
+                    label: i18n.global.t('forms.node.cover.title'),
+                    placeholder: i18n.global.t('forms.node.cover.placeholder'),
                     value: '',
                     accept: '.png,.jpg,.jpeg,.gif,.bmp,.svg,.webp',
-                    hint: 'Format recommandé : carré (180x180)<br> Image visible dans la liste des ePocs',
+                    hint: i18n.global.t('forms.node.cover.hint'),
                 },
                 {
                     id: 'teaser',
                     type: 'file',
-                    label: 'Teaser vidéo',
+                    label: i18n.global.t('forms.node.teaser.title'),
                     value: '',
-                    placeholder: 'Ajouter un teaser',
+                    placeholder: i18n.global.t('forms.node.teaser.placeholder'),
                     accept: '.mp4',
-                    hint: "Format recommandé : 16:9 (720x480) <br> Vidéo visible dans la page de présentation de l'ePoc",
+                    hint: i18n.global.t('forms.node.teaser.hint'),
                 },
                 {
                     id: 'thumbnail',
                     type: 'file',
-                    label: 'Vignette de la vidéo',
+                    label: i18n.global.t('forms.node.thumbnail.title'),
                     value: '',
-                    placeholder: 'Ajouter une vignette',
+                    placeholder: i18n.global.t('forms.content.thumbnail.placeholder'),
                     accept: '.png,.jpg,.jpeg,.gif,.bmp,.svg,.webp',
-                    hint: "Format recommandé : idem que la vidéo <br> Image visible dans la page de présentation de l'ePoc",
+                    hint: i18n.global.t('forms.node.thumbnail.hint'),
                 },
                 {
                     id: 'summary',
                     type: 'html-text',
-                    label: 'Présentation',
+                    label: i18n.global.t('forms.node.presentation'),
                     value: '',
-                    placeholder: "Saisissez une présentation de l'ePoc...",
+                    placeholder: i18n.global.t('forms.type'),
                 },
                 {
                     id: 'edition',
                     type: 'text',
-                    label: 'Edition',
+                    label: i18n.global.t('forms.node.edition'),
                     value: String(new Date().getFullYear()),
                 },
             ],
         },
         {
-            name: 'Auteurs',
+            name: i18n.global.t('forms.node.author.title', 2),
             inputs: [
                 {
                     id: 'authors',
-                    label: 'Auteur',
+                    label: i18n.global.t('forms.node.author.title', 1),
                     type: 'repeat',
                     value: [],
                     inputs: [
                         {
                             id: 'name',
                             type: 'text',
-                            label: 'Nom',
-                            placeholder: 'Jeanne Dupont',
+                            label: i18n.global.t('global.name'),
+                            placeholder: i18n.global.t('forms.node.author.placeholder'),
                             value: '',
                         },
                         {
                             id: 'image',
                             type: 'file',
-                            label: 'Image',
-                            placeholder: 'Ajouter une image',
+                            label: i18n.global.t('forms.node.author.image.title'),
+                            placeholder: i18n.global.t('forms.node.author.image.placeholder'),
                             value: '',
                             accept: '.png,.jpg,.jpeg,.gif,.bmp,.svg,.webp',
-                            hint: "Format recommandé : carré (100x100)<br> Image visible dans la page de présentation de l'ePoc",
+                            hint: i18n.global.t('forms.node.author.image.hint'),
                         },
                         {
                             id: 'title',
                             type: 'text',
-                            label: 'Fonction',
-                            placeholder: "Chercheuse à l'Inria",
+                            label: i18n.global.t('forms.node.author.position.title'),
+                            placeholder: i18n.global.t('forms.node.author.position.placeholder'),
                             value: '',
-                            hint: 'Profession, fonction, affiliation…',
+                            hint: i18n.global.t('forms.node.author.position.hint'),
                         },
                         {
                             id: 'description',
                             type: 'html-text',
-                            label: 'Courte biographie',
-                            placeholder: 'Saisissez une courte biographie...',
+                            label: i18n.global.t('forms.node.author.biography'),
+                            placeholder: i18n.global.t('forms.type'),
                             value: '',
                         },
                     ],
@@ -245,11 +250,11 @@ export const epocForm: Form = {
             ],
         },
         {
-            name: 'Objectifs pédagogiques',
+            name: i18n.global.t('forms.node.objectives'),
             inputs: [
                 {
                     id: 'objectives',
-                    label: 'Objectif',
+                    label: i18n.global.t('global.objective'),
                     type: 'repeat',
                     value: [],
                     inputs: [
@@ -257,7 +262,7 @@ export const epocForm: Form = {
                             id: '',
                             type: 'textarea',
                             label: '',
-                            placeholder: 'Saisissez un objectif ...',
+                            placeholder: i18n.global.t('forms.type'),
                             value: '',
                         },
                     ],
@@ -265,50 +270,50 @@ export const epocForm: Form = {
             ],
         },
         {
-            name: 'Paramètres :',
+            name: i18n.global.t('settings.title'),
             inputs: [
                 {
                     id: 'certificateBadgeCount',
                     type: 'score',
-                    label: "Nombre de badge pour obtenir l'attestation",
+                    label: i18n.global.t('forms.node.certificateBadge'),
                     value: 1,
                 },
                 {
                     id: 'certificateScore',
                     type: 'score',
-                    label: "Score pour obtenir l'attestation",
+                    label: i18n.global.t('forms.node.certificateScore'),
                     value: 10,
-                    hint: "N'est pas pris en compte si le nombre de badge pour obtenir l'attestation est supérieur à 0",
+                    hint: i18n.global.t('forms.node.certificateScoreHint'),
                 },
                 {
                     id: 'chapterParameter',
                     type: 'text',
-                    label: 'Label des chapitres',
+                    label: i18n.global.t('forms.node.chapterLabel'),
                     value: '',
-                    placeholder: 'Saisissez...',
+                    placeholder: i18n.global.t('forms.type'),
                 },
                 {
                     id: 'chapterDuration',
                     type: 'score',
-                    label: 'Durée des chapitres (en minutes)',
+                    label: i18n.global.t('forms.node.chapterDuration'),
                     value: 0,
                 },
             ],
         },
         {
-            name: 'Plugins',
+            name: i18n.global.t('forms.node.plugin.title', 2),
             inputs: [
                 {
                     id: 'plugins',
-                    label: 'Plugin',
+                    label: i18n.global.t('forms.node.plugin.title', 1),
                     type: 'repeat',
                     value: [],
                     inputs: [
                         {
                             id: 'script',
                             type: 'file',
-                            label: 'Fichier de script',
-                            placeholder: 'Ajouter un script',
+                            label: i18n.global.t('forms.node.plugin.script'),
+                            placeholder: i18n.global.t('forms.node.plugin.scriptPlaceholder'),
                             targetDirectory: 'plugins',
                             value: '',
                             accept: '.js',
@@ -316,8 +321,8 @@ export const epocForm: Form = {
                         {
                             id: 'template',
                             type: 'file',
-                            label: 'Template html du plugin',
-                            placeholder: 'Ajouter un template',
+                            label: i18n.global.t('forms.node.plugin.template'),
+                            placeholder: i18n.global.t('forms.node.plugin.templatePlaceholder'),
                             targetDirectory: 'plugins',
                             value: '',
                             accept: 'html',
@@ -327,72 +332,72 @@ export const epocForm: Form = {
             ],
         },
         {
-            name: 'Licence',
+            name: i18n.global.t('forms.node.licence.title'),
             inputs: [
                 {
                     id: 'licenceName',
                     type: 'text',
-                    label: 'Nom',
+                    label: i18n.global.t('global.name'),
                     placeholder: 'CC-BY 4.0',
                     value: '',
-                    hint: 'Nom de la licence de votre contenu ePoc',
+                    hint: i18n.global.t('forms.node.licence.hint'),
                 },
                 {
                     id: 'licenceUrl',
                     type: 'text',
-                    label: 'URL',
-                    placeholder: 'https://creativecommons.org/licenses/by/4.0/deed',
+                    label: i18n.global.t('forms.node.licence.url'),
+                    placeholder: i18n.global.t('forms.node.licence.urlPlaceholder'),
                     value: '',
-                    hint: 'Texte complet de la licence choisie',
+                    hint: i18n.global.t('forms.node.licence.urlHint'),
                 },
             ],
         },
     ],
-};
+}));
 
-export const pageForm: Form = {
+export const pageForm: ComputedRef<Form> = computed(() => ({
     type: 'page',
-    name: 'Page',
+    name: i18n.global.t('forms.node.page.title'),
     icon: 'icon-ecran',
-    buttons: pageButtons,
+    buttons: pageButtons.value,
     fields: [
         {
             inputs: [
                 {
                     id: 'title',
                     type: 'text',
-                    label: 'Titre',
+                    label: i18n.global.t('forms.node.title'),
                     value: '',
-                    placeholder: 'Saisissez...',
+                    placeholder: i18n.global.t('forms.type'),
                 },
                 {
                     id: 'subtitle',
                     type: 'text',
-                    label: 'Sous-titre',
+                    label: i18n.global.t('forms.node.subtitle'),
                     value: '',
-                    placeholder: 'Saisissez...',
+                    placeholder: i18n.global.t('forms.type'),
                 },
                 {
                     id: 'hidden',
                     type: 'checkbox',
-                    label: 'Caché dans la table des matières',
+                    label: i18n.global.t('forms.node.page.hidden'),
                     value: false,
                 },
                 {
                     id: 'conditional',
                     type: 'checkbox',
-                    label: "Ne s'affiche qu'a certaines conditions",
+                    label: i18n.global.t('forms.node.page.conditional'),
                     value: false,
-                    hint: "Option utilisé pour l'affichage conditionnel",
+                    hint: i18n.global.t('forms.node.page.conditionalHint'),
                 },
             ],
         },
         {
-            name: 'Composants',
+            name: i18n.global.t('forms.node.page.components'),
             inputs: [
                 {
                     id: 'components',
-                    label: 'Composants',
+                    label: i18n.global.t('forms.node.page.components'),
                     type: 'repeat',
                     value: [],
                     addButton: false,
@@ -401,58 +406,58 @@ export const pageForm: Form = {
             ],
         },
     ],
-};
+}));
 
-export const activityForm: Form = {
+export const activityForm: ComputedRef<Form> = computed(() => ({
     type: 'activity',
-    name: 'Évaluation',
+    name: i18n.global.t('forms.node.activity'),
     icon: 'icon-ecran',
-    buttons: activityButtons,
+    buttons: activityButtons.value,
     fields: [
         {
             inputs: [
                 {
                     id: 'title',
                     type: 'text',
-                    label: 'Titre',
+                    label: i18n.global.t('forms.node.title'),
                     value: '',
-                    placeholder: 'Saisissez...',
+                    placeholder: i18n.global.t('forms.type'),
                 },
                 {
                     id: 'subtitle',
                     type: 'text',
-                    label: 'Sous-titre',
+                    label: i18n.global.t('forms.node.subtitle'),
                     value: '',
-                    placeholder: 'Saisissez...',
+                    placeholder: i18n.global.t('forms.type'),
                 },
                 {
                     id: 'summary',
                     type: 'textarea',
-                    label: 'Résumé',
+                    label: i18n.global.t('forms.content.summary'),
                     value: '',
-                    placeholder: 'Saisissez...',
+                    placeholder: i18n.global.t('forms.type'),
                 },
                 {
                     id: 'hidden',
                     type: 'checkbox',
-                    label: 'Caché dans la table des matières',
+                    label: i18n.global.t('forms.node.page.hidden'),
                     value: false,
                 },
                 {
                     id: 'conditional',
                     type: 'checkbox',
-                    label: "Ne s'affiche qu'a certaines conditions",
+                    label: i18n.global.t('forms.node.page.conditional'),
                     value: false,
-                    hint: "Option utilisé pour l'affichage conditionnel",
+                    hint: i18n.global.t('forms.node.page.conditionalHint'),
                 },
             ],
         },
         {
-            name: 'Composants',
+            name: i18n.global.t('forms.node.page.components'),
             inputs: [
                 {
                     id: 'components',
-                    label: 'Composants',
+                    label: i18n.global.t('forms.node.page.components'),
                     type: 'repeat',
                     value: [],
                     addButton: false,
@@ -461,6 +466,13 @@ export const activityForm: Form = {
             ],
         },
     ],
-};
+}));
 
-export const nodeForms: Form[] = [chapterForm, pageForm, epocForm, conditionForm, legacyConditionForm, activityForm];
+export const nodeForms: ComputedRef<Form[]> = computed(() => [
+    chapterForm.value,
+    pageForm.value,
+    epocForm.value,
+    conditionForm.value,
+    legacyConditionForm.value,
+    activityForm.value,
+]);
diff --git a/src/shared/data/forms/questionsForm.data.ts b/src/shared/data/forms/questionsForm.data.ts
index dbf9d3bce036cd912fb27b55407a593b3effdb5b..88741e58aba03d6bf07cad4b9905493d3ec31dc7 100644
--- a/src/shared/data/forms/questionsForm.data.ts
+++ b/src/shared/data/forms/questionsForm.data.ts
@@ -1,79 +1,82 @@
 import { Form } from '@/src/shared/interfaces';
 import { contentButtons } from './formButtons.data';
+import { i18n } from '@/i18n/config';
+import { capitalizeFirstLetter } from '../../utils/string';
+import { ComputedRef, computed } from 'vue';
 
-export const qcmForm: Form = {
+export const qcmForm: ComputedRef<Form> = computed(() => ({
     type: 'choice',
-    name: 'QCM',
+    name: i18n.global.t('questions.types.qcm'),
     icon: 'icon-qcm',
     displayFieldIndex: true,
-    buttons: contentButtons,
+    buttons: contentButtons.value,
     fields: [
         {
-            name: "Configuration de l'évaluation",
+            name: i18n.global.t('questions.configuration'),
             inputs: [
                 {
                     id: 'score',
                     type: 'score',
-                    label: 'Score',
+                    label: i18n.global.t('questions.score'),
                     value: 0,
                 },
             ],
         },
         {
-            name: 'Question',
+            name: i18n.global.t('questions.question'),
             inputs: [
                 {
                     id: 'label',
                     type: 'textarea',
-                    label: 'Question',
+                    label: i18n.global.t('questions.question'),
                     value: '',
-                    placeholder: 'Posez la question',
+                    placeholder: i18n.global.t('questions.askQuestion'),
                 },
                 {
                     id: 'statement',
                     type: 'html-inline',
-                    label: 'Consigne',
+                    label: i18n.global.t('questions.instruction'),
                     value: '',
-                    placeholder: 'Instruction pour répondre à la question',
+                    placeholder: i18n.global.t('questions.instructionPlaceholder'),
                 },
             ],
         },
         {
-            name: 'Réponses',
+            name: i18n.global.t('questions.responses'),
             inputs: [
                 {
                     id: 'responses',
-                    label: 'Réponse',
+                    label: i18n.global.t('questions.response'),
                     type: 'repeat',
                     value: [],
                     inputs: [
                         {
                             id: 'label',
                             type: 'text',
-                            label: 'Réponse',
-                            placeholder: 'Saisissez une réponse...',
+                            label: i18n.global.t('questions.response'),
+                            placeholder: i18n.global.t('questions.typeResponse'),
                             value: '',
                         },
                         {
                             id: 'value',
                             type: 'hidden',
                             label: '',
-                            placeholder: 'Valeur cachée',
+                            placeholder: i18n.global.t('forms.type'),
                             value: '',
                         },
                         {
                             id: 'feedback',
                             type: 'textarea',
-                            label: 'Explication',
-                            placeholder: 'Saisissez une explication...',
+                            label: i18n.global.t('questions.explanation'),
+                            placeholder: i18n.global.t('questions.typeExplanation'),
                             value: '',
                             collapsible: true,
-                            collapsibleLabel: 'Ajouter une explication',
+                            collapsibleLabel: i18n.global.t('questions.addExplanation'),
                         },
                         {
                             id: 'isCorrect',
                             type: 'checkbox',
-                            label: 'Bonne réponse',
+                            label: i18n.global.t('questions.correctResponse'),
                             value: false,
                         },
                     ],
@@ -81,63 +84,63 @@ export const qcmForm: Form = {
             ],
         },
         {
-            name: 'Explication',
+            name: i18n.global.t('questions.explanation'),
             inputs: [
                 {
                     id: 'explanation',
                     type: 'html',
                     label: '',
                     value: '',
-                    placeholder: 'Saisissez une explication',
+                    placeholder: i18n.global.t('questions.typeExplanation'),
                 },
             ],
         },
     ],
-};
+}));
 
-export const dragDropForm: Form = {
+export const dragDropForm: ComputedRef<Form> = computed(() => ({
     type: 'drag-and-drop',
-    name: 'Drag & Drop',
+    name: i18n.global.t('questions.types.dragDrop'),
     icon: 'icon-dragdrop',
     displayFieldIndex: true,
-    buttons: contentButtons,
+    buttons: contentButtons.value,
     fields: [
         {
-            name: "Configuration de l'évaluation",
+            name: i18n.global.t('questions.configuration'),
             inputs: [
                 {
                     id: 'score',
                     type: 'score',
-                    label: 'Score',
+                    label: i18n.global.t('questions.score'),
                     value: 0,
                 },
             ],
         },
         {
-            name: 'Question',
+            name: i18n.global.t('questions.question'),
             inputs: [
                 {
                     id: 'label',
                     type: 'textarea',
-                    label: 'Question',
+                    label: i18n.global.t('questions.question'),
                     value: '',
-                    placeholder: 'Posez la question',
+                    placeholder: i18n.global.t('questions.askQuestion'),
                 },
                 {
                     id: 'statement',
                     type: 'html-inline',
-                    label: 'Consigne',
+                    label: i18n.global.t('questions.instruction'),
                     value: '',
-                    placeholder: 'Instruction pour répondre à la question',
+                    placeholder: i18n.global.t('questions.instructionPlaceholder'),
                 },
             ],
         },
         {
-            name: 'Catégories de réponses proposées',
+            name: i18n.global.t('questions.categories'),
             inputs: [
                 {
                     id: 'categories',
-                    label: 'Catégorie',
+                    label: i18n.global.t('questions.category'),
                     type: 'repeat',
                     value: [],
                     inputs: [
@@ -145,7 +148,7 @@ export const dragDropForm: Form = {
                             id: '',
                             type: 'textarea',
                             label: '',
-                            placeholder: 'Saisissez un intitulé catégorie..',
+                            placeholder: i18n.global.t('questions.typeCategory'),
                             value: '',
                         },
                     ],
@@ -153,36 +156,36 @@ export const dragDropForm: Form = {
             ],
         },
         {
-            name: 'Réponses proposées',
+            name: i18n.global.t('questions.proposedResponses'),
             inputs: [
                 {
                     id: 'responses',
-                    label: 'Réponse',
+                    label: i18n.global.t('questions.response'),
                     type: 'repeat',
                     value: [],
                     inputs: [
                         {
                             id: 'label',
                             type: 'text',
-                            label: 'Réponse',
-                            placeholder: 'Saisissez une réponse...',
+                            label: i18n.global.t('questions.response'),
+                            placeholder: i18n.global.t('questions.typeResponse'),
                             value: '',
                         },
                         {
                             id: 'value',
                             type: 'hidden',
                             label: '',
-                            placeholder: 'Valeur cachée',
+                            placeholder: i18n.global.t('forms.type'),
                             value: '',
                         },
                         {
                             id: 'feedback',
                             type: 'textarea',
-                            label: 'Explication',
-                            placeholder: 'Saisissez une explication...',
+                            label: i18n.global.t('questions.explanation'),
+                            placeholder: i18n.global.t('questions.typeExplanation'),
                             value: '',
                             collapsible: true,
-                            collapsibleLabel: 'Ajouter une explication',
+                            collapsibleLabel: i18n.global.t('questions.addExplanation'),
                         },
                         {
                             id: 'category',
@@ -198,87 +201,87 @@ export const dragDropForm: Form = {
             ],
         },
         {
-            name: 'Explication',
+            name: i18n.global.t('questions.explanation'),
             inputs: [
                 {
                     id: 'explanation',
                     type: 'html',
                     label: '',
                     value: '',
-                    placeholder: 'Saisissez une explication',
+                    placeholder: i18n.global.t('questions.typeExplanation'),
                 },
             ],
         },
     ],
-};
+}));
 
-export const reorderForm: Form = {
+export const reorderForm: ComputedRef<Form> = computed(() => ({
     type: 'reorder',
-    name: 'Reorder',
+    name: i18n.global.t('questions.types.reorder'),
     icon: 'icon-reorder',
     displayFieldIndex: true,
-    buttons: contentButtons,
+    buttons: contentButtons.value,
     fields: [
         {
-            name: "Configuration de l'évaluation",
+            name: i18n.global.t('questions.configuration'),
             inputs: [
                 {
                     id: 'score',
                     type: 'score',
-                    label: 'Score',
+                    label: i18n.global.t('questions.score'),
                     value: 0,
                 },
             ],
         },
         {
-            name: 'Question',
+            name: i18n.global.t('questions.question'),
             inputs: [
                 {
                     id: 'label',
                     type: 'textarea',
-                    label: 'Question',
+                    label: i18n.global.t('questions.question'),
                     value: '',
-                    placeholder: 'Posez la question',
+                    placeholder: i18n.global.t('questions.askQuestion'),
                 },
                 {
                     id: 'statement',
                     type: 'html-inline',
-                    label: 'Consigne',
+                    label: i18n.global.t('questions.instruction'),
                     value: '',
-                    placeholder: 'Instruction pour répondre à la question',
+                    placeholder: i18n.global.t('questions.instructionPlaceholder'),
                 },
             ],
         },
         {
-            name: 'Réponses',
+            name: i18n.global.t('questions.responses'),
             inputs: [
                 {
                     id: 'responses',
-                    label: 'Réponse',
+                    label: i18n.global.t('questions.response'),
                     type: 'repeat',
                     value: [],
                     inputs: [
                         {
                             id: 'label',
                             type: 'text',
-                            label: 'Réponse',
-                            placeholder: 'Saisissez une réponse...',
+                            label: i18n.global.t('questions.response'),
+                            placeholder: i18n.global.t('questions.typeResponse'),
                             value: '',
                         },
                         {
                             id: 'feedback',
                             type: 'textarea',
-                            label: 'Explication',
-                            placeholder: 'Saisissez une explication...',
+                            label: i18n.global.t('questions.explanation'),
+                            placeholder: i18n.global.t('questions.typeExplanation'),
                             value: '',
                             collapsible: true,
-                            collapsibleLabel: 'Ajouter une explication',
+                            collapsibleLabel: i18n.global.t('questions.addExplanation'),
                         },
                         {
                             id: 'value',
                             type: 'hidden',
                             label: '',
-                            placeholder: 'Valeur cachée',
+                            placeholder: i18n.global.t('forms.type'),
                             value: '',
                         },
                     ],
@@ -286,72 +289,75 @@ export const reorderForm: Form = {
             ],
         },
         {
-            name: 'Explication',
+            name: i18n.global.t('questions.explanation'),
             inputs: [
                 {
                     id: 'explanation',
                     type: 'html',
                     label: '',
                     value: '',
-                    placeholder: 'Saisissez une explication...',
+                    placeholder: i18n.global.t('questions.typeExplanation'),
                 },
             ],
         },
     ],
-};
+}));
 
-export const swipeForm: Form = {
+export const swipeForm: ComputedRef<Form> = computed(() => ({
     type: 'swipe',
-    name: 'Swipe',
+    name: i18n.global.t('questions.types.swipe'),
     icon: 'icon-swipe',
     displayFieldIndex: true,
-    buttons: contentButtons,
+    buttons: contentButtons.value,
     fields: [
         {
-            name: "Configuration de l'évaluation",
+            name: i18n.global.t('questions.configuration'),
             inputs: [
                 {
                     id: 'score',
                     type: 'score',
-                    label: 'Score',
+                    label: i18n.global.t('questions.score'),
                     value: 0,
                 },
             ],
         },
         {
-            name: 'Question',
+            name: i18n.global.t('questions.question'),
             inputs: [
                 {
                     id: 'label',
                     type: 'textarea',
-                    label: 'Question',
+                    label: i18n.global.t('questions.question'),
                     value: '',
-                    placeholder: 'Posez la question',
+                    placeholder: i18n.global.t('questions.askQuestion'),
                 },
                 {
                     id: 'statement',
                     type: 'html-inline',
-                    label: 'Consigne',
+                    label: i18n.global.t('questions.instruction'),
                     value: '',
-                    placeholder: 'Instruction pour répondre à la question',
+                    placeholder: i18n.global.t('questions.instructionPlaceholder'),
                 },
             ],
         },
         {
-            name: 'Catégories de choix proposées',
+            name: i18n.global.t('questions.proposedChoices'),
             inputs: [
                 {
                     id: 'categories',
-                    label: 'Choix',
+                    label: i18n.global.t('forms.node.choice'),
                     type: 'repeat',
-                    value: ['Droite', 'Gauche'],
+                    value: [
+                        capitalizeFirstLetter(i18n.global.t('global.right')),
+                        capitalizeFirstLetter(i18n.global.t('global.left')),
+                    ],
                     addButton: false,
                     inputs: [
                         {
                             id: '',
                             type: 'text',
                             label: '',
-                            placeholder: 'Saisissez une réponse...',
+                            placeholder: i18n.global.t('questions.typeResponse'),
                             value: '',
                         },
                     ],
@@ -359,35 +365,35 @@ export const swipeForm: Form = {
             ],
         },
         {
-            name: 'Réponse proposée',
+            name: i18n.global.t('questions.proposedResponses'),
             inputs: [
                 {
                     id: 'responses',
-                    label: 'Carte',
+                    label: i18n.global.t('questions.card'),
                     type: 'repeat',
                     value: [],
                     inputs: [
                         {
                             id: 'label',
                             type: 'text',
-                            label: 'Réponse',
-                            placeholder: 'Saisissez une proposition',
+                            label: i18n.global.t('questions.response'),
+                            placeholder: i18n.global.t('questions.typeProposition'),
                             value: '',
                         },
                         {
                             id: 'feedback',
                             type: 'textarea',
-                            label: 'Explication',
-                            placeholder: 'Saisissez une explication...',
+                            label: i18n.global.t('questions.explanation'),
+                            placeholder: i18n.global.t('questions.typeExplanation'),
                             value: '',
                             collapsible: true,
-                            collapsibleLabel: 'Ajouter une explication',
+                            collapsibleLabel: i18n.global.t('questions.addExplanation'),
                         },
                         {
                             id: 'value',
                             type: 'hidden',
                             label: '',
-                            placeholder: 'Valeur cachée',
+                            placeholder: i18n.global.t('forms.type'),
                             value: '',
                         },
                         {
@@ -404,63 +410,63 @@ export const swipeForm: Form = {
             ],
         },
         {
-            name: 'Explication',
+            name: i18n.global.t('questions.explanation'),
             inputs: [
                 {
                     id: 'explanation',
                     type: 'html',
                     label: '',
                     value: '',
-                    placeholder: 'Saisissez une explication...',
+                    placeholder: i18n.global.t('questions.typeExplanation'),
                 },
             ],
         },
     ],
-};
+}));
 
-export const listForm: Form = {
+export const listForm: ComputedRef<Form> = computed(() => ({
     type: 'dropdown-list',
-    name: 'Liste déroulante',
+    name: i18n.global.t('questions.types.dropdownList'),
     icon: 'icon-liste',
     displayFieldIndex: true,
-    buttons: contentButtons,
+    buttons: contentButtons.value,
     fields: [
         {
-            name: "Configuration de l'évaluation",
+            name: i18n.global.t('questions.configuration'),
             inputs: [
                 {
                     id: 'score',
                     type: 'score',
-                    label: 'Score',
+                    label: i18n.global.t('questions.score'),
                     value: 0,
                 },
             ],
         },
         {
-            name: 'Question',
+            name: i18n.global.t('questions.question'),
             inputs: [
                 {
                     id: 'label',
                     type: 'textarea',
-                    label: 'Question',
+                    label: i18n.global.t('questions.question'),
                     value: '',
-                    placeholder: 'Posez la question',
+                    placeholder: i18n.global.t('questions.askQuestion'),
                 },
                 {
                     id: 'statement',
                     type: 'html-inline',
-                    label: 'Consigne',
+                    label: i18n.global.t('questions.instruction'),
                     value: '',
-                    placeholder: 'Instruction pour répondre à la question',
+                    placeholder: i18n.global.t('questions.instructionPlaceholder'),
                 },
             ],
         },
         {
-            name: 'Catégories de choix proposées',
+            name: i18n.global.t('questions.proposedChoices'),
             inputs: [
                 {
                     id: 'categories',
-                    label: 'Choix',
+                    label: i18n.global.t('forms.node.choice'),
                     type: 'repeat',
                     value: [],
                     inputs: [
@@ -468,7 +474,7 @@ export const listForm: Form = {
                             id: '',
                             type: 'text',
                             label: '',
-                            placeholder: 'Saisissez une réponse...',
+                            placeholder: i18n.global.t('questions.typeResponse'),
                             value: '',
                         },
                     ],
@@ -476,35 +482,35 @@ export const listForm: Form = {
             ],
         },
         {
-            name: 'Cartes',
+            name: i18n.global.t('questions.cards'),
             inputs: [
                 {
                     id: 'responses',
-                    label: 'Carte',
+                    label: i18n.global.t('questions.card'),
                     type: 'repeat',
                     value: [],
                     inputs: [
                         {
                             id: 'label',
                             type: 'text',
-                            label: 'Réponse',
-                            placeholder: 'Saisissez une question...',
+                            label: i18n.global.t('questions.response'),
+                            placeholder: i18n.global.t('questions.typeProposition'),
                             value: '',
                         },
                         {
                             id: 'feedback',
                             type: 'textarea',
-                            label: 'Explication',
-                            placeholder: 'Saisissez une explication...',
+                            label: i18n.global.t('questions.explanation'),
+                            placeholder: i18n.global.t('questions.typeExplanation'),
                             value: '',
                             collapsible: true,
-                            collapsibleLabel: 'Ajouter une explication',
+                            collapsibleLabel: i18n.global.t('questions.addExplanation'),
                         },
                         {
                             id: 'value',
                             type: 'hidden',
                             label: '',
-                            placeholder: 'Valeur cachée',
+                            placeholder: i18n.global.t('forms.type'),
                             value: '',
                         },
                         {
@@ -521,64 +527,64 @@ export const listForm: Form = {
             ],
         },
         {
-            name: 'Explication',
+            name: i18n.global.t('questions.explanation'),
             inputs: [
                 {
                     id: 'explanation',
                     type: 'html',
                     label: '',
                     value: '',
-                    placeholder: 'Saisissez une explication...',
+                    placeholder: i18n.global.t('questions.typeExplanation'),
                 },
             ],
         },
     ],
-};
+}));
 
-export const customQuestionForm: Form = {
+export const customQuestionForm: ComputedRef<Form> = computed(() => ({
     type: 'custom',
-    name: 'Question personnalisée',
+    name: i18n.global.t('questions.types.custom'),
     icon: 'icon-terminal',
     displayFieldIndex: true,
-    buttons: contentButtons,
+    buttons: contentButtons.value,
     fields: [
         {
-            name: "Configuration de l'évaluation",
+            name: i18n.global.t('questions.configuration'),
             inputs: [
                 {
                     id: 'score',
                     type: 'score',
-                    label: 'Score',
+                    label: i18n.global.t('questions.score'),
                     value: 0,
                 },
             ],
         },
         {
-            name: 'Question',
+            name: i18n.global.t('questions.question'),
             inputs: [
                 {
                     id: 'label',
                     type: 'textarea',
-                    label: 'Question',
+                    label: i18n.global.t('questions.question'),
                     value: '',
-                    placeholder: 'Posez la question',
+                    placeholder: i18n.global.t('questions.askQuestion'),
                 },
                 {
                     id: 'statement',
                     type: 'html-inline',
-                    label: 'Consigne',
+                    label: i18n.global.t('questions.instruction'),
                     value: '',
-                    placeholder: 'Instruction pour répondre à la question',
+                    placeholder: i18n.global.t('questions.instructionPlaceholder'),
                 },
             ],
         },
         {
-            name: 'Template',
+            name: i18n.global.t('questions.template.title'),
             inputs: [
                 {
                     id: 'template',
                     type: 'select',
-                    label: 'Selectionnez un template',
+                    label: i18n.global.t('questions.template.select'),
                     value: '',
                     options: [],
                     linkedOptions: 'plugins.*.template',
@@ -586,26 +592,26 @@ export const customQuestionForm: Form = {
             ],
         },
         {
-            name: 'Données',
+            name: i18n.global.t('questions.template.data'),
             inputs: [
                 {
                     type: 'repeat',
                     id: 'data',
-                    label: 'Données',
+                    label: i18n.global.t('questions.template.data'),
                     value: [],
                     inputs: [
                         {
                             id: 'key',
                             type: 'text',
-                            label: 'Clé',
-                            placeholder: 'Clé',
+                            label: i18n.global.t('questions.template.key'),
+                            placeholder: i18n.global.t('questions.template.key'),
                             value: '',
                         },
                         {
                             id: 'value',
                             type: 'textarea',
-                            label: 'Valeur',
-                            placeholder: 'Valeur',
+                            label: i18n.global.t('questions.template.value'),
+                            placeholder: i18n.global.t('questions.template.value'),
                             value: '',
                         },
                     ],
@@ -613,29 +619,36 @@ export const customQuestionForm: Form = {
             ],
         },
         {
-            name: 'Réponse',
+            name: i18n.global.t('questions.response'),
             inputs: [
                 {
                     id: 'correctResponse',
-                    label: 'Réponse',
+                    label: i18n.global.t('questions.response'),
                     type: 'text',
                     value: '',
                 },
             ],
         },
         {
-            name: 'Explication',
+            name: i18n.global.t('questions.explanation'),
             inputs: [
                 {
                     id: 'explanation',
                     type: 'html',
                     label: '',
                     value: '',
-                    placeholder: 'Saisissez une explication',
+                    placeholder: i18n.global.t('questions.typeExplanation'),
                 },
             ],
         },
     ],
-};
+}));
 
-export const questionForms: Form[] = [qcmForm, swipeForm, reorderForm, dragDropForm, listForm, customQuestionForm];
+export const questionForms: ComputedRef<Form[]> = computed(() => [
+    qcmForm.value,
+    swipeForm.value,
+    reorderForm.value,
+    dragDropForm.value,
+    listForm.value,
+    customQuestionForm.value,
+]);
diff --git a/src/shared/data/sideBar.data.ts b/src/shared/data/sideBar.data.ts
index 8ec7d3120c3b56923c323cee6b839cba21648c8a..e55c54579e7f67b1ed4fb9dc88f86624f199cabb 100644
--- a/src/shared/data/sideBar.data.ts
+++ b/src/shared/data/sideBar.data.ts
@@ -1,108 +1,110 @@
 import { SideAction } from '@/src/shared/interfaces';
+import { i18n } from '@/i18n/config';
+import { computed, ComputedRef } from 'vue';
 
-export const questions: SideAction[] = [
+export const questions: ComputedRef<SideAction[]> = computed(() => [
     {
         icon: 'icon-qcm',
         type: 'choice',
-        label: 'QCM',
+        label: i18n.global.t('questions.types.qcm'),
     },
     {
         icon: 'icon-dragdrop',
         type: 'drag-and-drop',
-        label: 'Drag & Drop',
+        label: i18n.global.t('questions.types.dragDrop'),
     },
     {
         icon: 'icon-reorder',
         type: 'reorder',
-        label: 'Reorder',
+        label: i18n.global.t('questions.types.reorder'),
     },
     {
         icon: 'icon-swipe',
         type: 'swipe',
-        label: 'Swipe',
+        label: i18n.global.t('questions.types.swipe'),
     },
     {
         icon: 'icon-liste',
         type: 'dropdown-list',
-        label: 'Liste déroulante',
+        label: i18n.global.t('questions.types.dropdownList'),
     },
     {
         icon: 'icon-terminal',
         type: 'custom',
-        label: 'Question personnalisée',
-    }
-];
+        label: i18n.global.t('questions.types.custom'),
+    },
+]);
 
-const contents: SideAction[] = [
+const contents: ComputedRef<SideAction[]> = computed(() => [
     {
         icon: 'icon-texte',
         type: 'text',
-        label: 'Texte',
-        tooltip: 'Glisser/déposer pour ajouter un texte',
+        label: i18n.global.t('sidebar.content.text'),
+        tooltip: i18n.global.t('sidebar.content.textTooltip'),
     },
     {
         icon: 'icon-video',
         type: 'video',
-        label: 'Vidéo',
-        tooltip: 'Glisser/déposer pour ajouter une vidéo',
+        label: i18n.global.t('sidebar.content.video'),
+        tooltip: i18n.global.t('sidebar.content.videoTooltip'),
     },
     {
         icon: 'icon-audio',
         type: 'audio',
-        label: 'Audio',
-        tooltip: 'Glisser/déposer pour ajouter un audio',
+        label: i18n.global.t('sidebar.content.audio'),
+        tooltip: i18n.global.t('sidebar.content.audioTooltip'),
     },
-];
+]);
 
-export const standardActions = [...questions, ...contents];
+export const standardActions = computed(() => [...questions.value, ...contents.value]);
 
-export const standardPages: SideAction[] = [
+export const standardPages: ComputedRef<SideAction[]> = computed(() => [
     {
         icon: 'icon-texte',
         type: 'text',
-        label: 'Texte',
-        tooltip: 'Glisser/déposer pour ajouter un texte',
+        label: i18n.global.t('sidebar.content.text'),
+        tooltip: i18n.global.t('sidebar.content.textTooltip'),
     },
     {
         icon: 'icon-video',
         type: 'video',
-        label: 'Vidéo',
-        tooltip: 'Glisser/déposer pour ajouter une vidéo',
+        label: i18n.global.t('sidebar.content.video'),
+        tooltip: i18n.global.t('sidebar.content.videoTooltip'),
     },
     {
         icon: 'icon-audio',
         type: 'audio',
-        label: 'Audio',
-        tooltip: 'Glisser/déposer pour ajouter un audio',
+        label: i18n.global.t('sidebar.content.audio'),
+        tooltip: i18n.global.t('sidebar.content.audioTooltip'),
     },
     {
         icon: 'icon-question',
         type: 'question',
-        label: 'Question',
-        tooltip: 'Cliquer pour ajouter une question',
+        label: i18n.global.t('sidebar.pages.question'),
+        tooltip: i18n.global.t('sidebar.pages.questionTooltip'),
     },
     {
         icon: 'icon-condition',
         type: 'condition',
-        label: 'Conditions',
-        tooltip: 'Glisser/déposer pour ajouter une condition',
+        label: i18n.global.t('sidebar.pages.conditions'),
+        tooltip: i18n.global.t('sidebar.pages.conditionsTooltip'),
     },
     {
         icon: 'icon-condition-legacy',
         type: 'legacy-condition',
-        label: 'Conditions (legacy)',
-        tooltip: 'Glisser/déposer pour ajouter une condition',
+        label: i18n.global.t('sidebar.pages.conditionsLegacy'),
+        tooltip: i18n.global.t('sidebar.pages.conditionsTooltip'),
     },
     {
         icon: 'icon-modele',
         type: 'model',
-        label: 'Modèle',
-        tooltip: 'Cliquer pour ouvrir le menu modèle',
+        label: i18n.global.t('sidebar.pages.model'),
+        tooltip: i18n.global.t('sidebar.pages.modelTooltip'),
     },
     {
         icon: 'icon-badge',
         type: 'badge',
-        label: 'Badge',
-        tooltip: 'Cliquer pour ouvrir le menu badge',
+        label: i18n.global.t('sidebar.pages.badge'),
+        tooltip: i18n.global.t('sidebar.pages.badgeTooltip'),
     },
-];
+]);
diff --git a/src/shared/interfaces/settings.interface.ts b/src/shared/interfaces/settings.interface.ts
index c7b59d43c32c1ad339b0d9f779e43c2689ab01f2..7b4a70bfc447e7f67caf012ea62f244dcc9045b6 100644
--- a/src/shared/interfaces/settings.interface.ts
+++ b/src/shared/interfaces/settings.interface.ts
@@ -1,3 +1,4 @@
 export interface Settings {
     spellcheck: boolean;
+    locale: string;
 }
diff --git a/src/shared/services/editor.service.ts b/src/shared/services/editor.service.ts
index 7d3bf840175565ce5c0e1ee73a011346b27122c1..b551e1fc9aaaffe1bb1ebb1e01e3d867ada6ac18 100644
--- a/src/shared/services/editor.service.ts
+++ b/src/shared/services/editor.service.ts
@@ -157,9 +157,8 @@ const setup = function () {
     api.receive('settings', (data: string) => {
         const { settings } = JSON.parse(data);
         const settingsStore = useSettingsStore();
-        settingsStore.settings = {
-            spellcheck: settings?.spellcheck === undefined ? true : settings.spellcheck,
-        };
+
+        settingsStore.initSettings(settings);
     });
 
     // Adding the version to the editorStore
diff --git a/src/shared/services/graph/badge.service.ts b/src/shared/services/graph/badge.service.ts
index c9c0272ce19341c75a45a59af2d2f4fc99fe0ee7..0a37bfb0ad2e2617f18af9bdc6b26774fdc15394 100644
--- a/src/shared/services/graph/badge.service.ts
+++ b/src/shared/services/graph/badge.service.ts
@@ -6,25 +6,27 @@ import { saveState } from '@/src/shared/services/undoRedo.service';
 import { elementVerbs, verbs } from '@/src/shared/data';
 import { generateContentId, graphService } from '@/src/shared/services';
 import { Operators } from '@epoc/epoc-types/dist/v2';
+import { i18n } from '@/i18n/config';
+import { computed, ComputedRef } from 'vue';
 
 const { findNode } = useVueFlow('main');
 
-export function getVerbs(type: ElementType): Verbs {
+export function getVerbs(type: ElementType): ComputedRef<Verbs> {
     if (!type || !elementVerbs[type]) return;
 
     const verbsKeys = elementVerbs[type];
-    const res: Verbs = {};
+    const res: ComputedRef<Verbs> = computed(() => ({}));
 
     for (const key of verbsKeys) {
-        res[key] = verbs[key];
+        res.value[key] = verbs.value[key];
     }
 
     return res;
 }
 
 export function getValueType(verbKey: string): 'number' | 'boolean' {
-    if (!verbs[verbKey]) return;
-    return verbs[verbKey].valueType;
+    if (!verbs.value[verbKey]) return;
+    return verbs.value[verbKey].valueType;
 }
 
 // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -64,66 +66,66 @@ export function createRule(entry: Condition[]): Rule {
     return { and: rules };
 }
 
-const phraseType = {
-    video: 'la vidéo',
-    chapter: 'le chapitre',
-    page: 'la page',
-    html: 'le texte',
-    audio: "l'audio",
-    activity: "l'évaluation",
-    question: 'la question',
-};
+const phraseType = computed(() => ({
+    video: i18n.global.t('badge.phrase.type.video'),
+    chapter: i18n.global.t('badge.phrase.type.chapter'),
+    page: i18n.global.t('badge.phrase.type.page'),
+    html: i18n.global.t('badge.phrase.type.html'),
+    audio: i18n.global.t('badge.phrase.type.audio'),
+    activity: i18n.global.t('badge.phrase.type.activity'),
+    question: i18n.global.t('badge.phrase.type.question'),
+}));
 
-const phraseVerb = {
+const phraseVerb = computed(() => ({
     started: {
-        true: 'Avoir commencé',
-        false: 'Ne pas avoir pas commencé',
+        true: i18n.global.t('badge.phrase.verb.started.true'),
+        false: i18n.global.t('badge.phrase.verb.started.false'),
     },
     completed: {
-        true: 'Avoir terminé',
-        false: 'Ne pas avoir terminé',
+        true: i18n.global.t('badge.phrase.verb.completed.true'),
+        false: i18n.global.t('badge.phrase.verb.completed.false'),
     },
     viewed: {
-        true: 'Avoir vu',
-        false: 'Ne pas avoir vu',
+        true: i18n.global.t('badge.phrase.verb.viewed.true'),
+        false: i18n.global.t('badge.phrase.verb.viewed.false'),
     },
     read: {
-        true: 'Avoir lu',
-        false: 'Ne pas avoir lu',
+        true: i18n.global.t('badge.phrase.verb.read.true'),
+        false: i18n.global.t('badge.phrase.verb.read.false'),
     },
     played: {
-        true: 'Avoir lancé',
-        false: 'Ne pas avoir lancé',
+        true: i18n.global.t('badge.phrase.verb.played.true'),
+        false: i18n.global.t('badge.phrase.verb.played.false'),
     },
     watched: {
-        true: 'Avoir regardé',
-        false: 'Ne pas avoir regardé',
+        true: i18n.global.t('badge.phrase.verb.watched.true'),
+        false: i18n.global.t('badge.phrase.verb.watched.false'),
     },
     listened: {
-        true: 'Avoir écouté',
-        false: 'Ne pas avoir écouté',
+        true: i18n.global.t('badge.phrase.verb.listened.true'),
+        false: i18n.global.t('badge.phrase.verb.listened.false'),
     },
     attempted: {
-        true: 'Avoir tenté',
-        false: 'Ne pas avoir tenté',
+        true: i18n.global.t('badge.phrase.verb.attempted.true'),
+        false: i18n.global.t('badge.phrase.verb.attempted.false'),
     },
     passed: {
-        true: 'Avoir réussi',
-        false: 'Avoir échoué',
+        true: i18n.global.t('badge.phrase.verb.passed.true'),
+        false: i18n.global.t('badge.phrase.verb.passed.false'),
     },
-    scored: "Avoir obtenu un score d'au moins",
-};
+    scored: i18n.global.t('badge.phrase.verb.scored'),
+}));
 
 export function createPhrase(condition: Condition, elementType: ElementType) {
     const { verb, value } = condition;
     let firstPart: string;
     if (verb === 'scored') {
-        firstPart = `${phraseVerb[verb]} ${value} à`;
+        firstPart = i18n.global.t('badge.phrase.scored', { value, verb: phraseVerb[verb] });
     } else {
-        firstPart = `${phraseVerb[verb][value]}`;
+        firstPart = `${phraseVerb.value[verb][value]}`;
     }
 
-    return `${firstPart} ${phraseType[elementType]}`;
+    return `${firstPart} ${phraseType.value[elementType]}`;
 }
 
 export function getConnectedBadges(contentId: string): Badge[] {
diff --git a/src/shared/services/graph/chapter.service.ts b/src/shared/services/graph/chapter.service.ts
index ec165cde575f6ea44023c7733f9584cb2a9fd048..79bb2dc3dd4b42b6885b3b6bb91e41e8a20ffa95 100644
--- a/src/shared/services/graph/chapter.service.ts
+++ b/src/shared/services/graph/chapter.service.ts
@@ -2,6 +2,9 @@ import { Node, useVueFlow } from '@vue-flow/core';
 import { Chapter } from '@epoc/epoc-types/src/v1';
 import { generateContentId, generateId, graphService } from '@/src/shared/services';
 const { nodes, findNode, addNodes } = useVueFlow('main');
+import { i18n } from '@/i18n/config';
+
+const { t } = i18n.global;
 
 /**
  * Add a new chapter to the graph
@@ -16,7 +19,7 @@ export function addChapter(chapterId?: string, chapter?: Chapter, offsetY?: numb
         action: { icon: 'icon-chapitre', type: 'chapter' },
         formType: 'chapter',
         formValues: {},
-        title: 'Chapitre ' + (chapters.length + 1),
+        title: t('global.chapter') + (chapters.length + 1),
         contentId: generateContentId(),
         index: chapters.length + 1,
     };
@@ -51,8 +54,8 @@ export function addChapter(chapterId?: string, chapter?: Chapter, offsetY?: numb
 export function updateNextChapters(chapterId: string): void {
     const nextChapters = getNextChapters(chapterId);
 
-    for(const chapter of nextChapters) {
-        chapter.data.index --;
+    for (const chapter of nextChapters) {
+        chapter.data.index--;
     }
 }
 
@@ -64,7 +67,7 @@ export function getPreviousChapters(id: string) {
     const chapter = findNode(id);
     const chapters = nodes.value.filter((node) => node.type === 'chapter');
 
-    if(chapter.data.index === 1) return [];
+    if (chapter.data.index === 1) return [];
     else return chapters.filter((c) => c.data.index < chapter.data.index).sort((a, b) => a.data.index - b.data.index);
 }
 
@@ -76,7 +79,7 @@ export function getNextChapters(id: string) {
     const chapter = findNode(id);
     const chapters = nodes.value.filter((node) => node.type === 'chapter');
 
-    if(chapter.data.index === chapters.length) return [];
+    if (chapter.data.index === chapters.length) return [];
     else return chapters.filter((c) => c.data.index > chapter.data.index).sort((a, b) => a.data.index - b.data.index);
 }
 
@@ -89,7 +92,7 @@ export function getPreviousChapter(id: string) {
     const chapter = findNode(id);
     const chapters = nodes.value.filter((node) => node.type === 'chapter');
 
-    if(chapter.data.index === 1) return null;
+    if (chapter.data.index === 1) return null;
     else return chapters.find((c) => c.data.index === chapter.data.index - 1);
 }
 
@@ -97,11 +100,10 @@ export function getNextChapter(id: string) {
     const chapter = findNode(id);
     const chapters = nodes.value.filter((node) => node.type === 'chapter');
 
-    if(chapter.data.index === chapters.length) return null;
+    if (chapter.data.index === chapters.length) return null;
     else return chapters.find((c) => c.data.index === chapter.data.index + 1);
 }
 
-
 /**
  * Swap the chapter with the previous chapter
  * @param {string} id
@@ -111,13 +113,13 @@ export function swapChapterWithPrevious(id: string) {
     const chapter = findNode(id);
     const previousChapter = getPreviousChapter(id);
 
-    if(!previousChapter) return;
+    if (!previousChapter) return;
 
     moveChapterContents(chapter.id, previousChapter.position.y - chapter.position.y);
     moveChapterContents(previousChapter.id, chapter.position.y - previousChapter.position.y);
 
-    chapter.data.index --;
-    previousChapter.data.index ++;
+    chapter.data.index--;
+    previousChapter.data.index++;
 
     const tmpY = chapter.position.y;
     chapter.position.y = previousChapter.position.y;
@@ -132,20 +134,19 @@ export function swapChapterWithNext(id: string) {
     const chapter = findNode(id);
     const nextChapter = getNextChapter(id);
 
-    if(!nextChapter) return;
+    if (!nextChapter) return;
 
     moveChapterContents(chapter.id, nextChapter.position.y - chapter.position.y);
     moveChapterContents(nextChapter.id, chapter.position.y - nextChapter.position.y);
 
-    chapter.data.index ++;
-    nextChapter.data.index --;
+    chapter.data.index++;
+    nextChapter.data.index--;
 
     const tmpY = chapter.position.y;
     chapter.position.y = nextChapter.position.y;
     nextChapter.position.y = tmpY;
 }
 
-
 /**
  * Manage the dragging of a chapter by keeping the others at the MIN_DISTANCE between each others
  * @param {string} id
@@ -161,29 +162,29 @@ export function handleChapterDrag(id: string) {
     const nextChapters = getNextChapters(id);
 
     // Assuring the position of the chapter is valid
-    if(chapter.position.x !== 0) chapter.position.x = 0;
+    if (chapter.position.x !== 0) chapter.position.x = 0;
     if (chapter.position.y < minY) chapter.position.y = minY;
 
     // Push up all the chapters after this chapter
-    for(let i = 0; i < previousChapters.length; i++) {
+    for (let i = 0; i < previousChapters.length; i++) {
         const c = previousChapters[i];
 
-        if(c.position.x !== 0 ) c.position.x = 0;
+        if (c.position.x !== 0) c.position.x = 0;
 
         // Get the min distance for the current c
         const currentMinY = chapter.position.y - MIN_DISTANCE * (previousChapters.length - i);
-        if(c.position.y > currentMinY) c.position.y = currentMinY;
+        if (c.position.y > currentMinY) c.position.y = currentMinY;
     }
 
     // Push down all the chapters below this chapter
-    for(let i = 0; i < nextChapters.length; i++) {
+    for (let i = 0; i < nextChapters.length; i++) {
         const c = nextChapters[i];
 
-        if(c.position.x !== 0 ) c.position.x = 0;
+        if (c.position.x !== 0) c.position.x = 0;
 
         // Get the max distance for the current c
-        const maxY = chapter.position.y  + MIN_DISTANCE * (i + 1);
-        if(c.position.y < maxY) c.position.y = maxY;
+        const maxY = chapter.position.y + MIN_DISTANCE * (i + 1);
+        if (c.position.y < maxY) c.position.y = maxY;
     }
 }
 
@@ -196,7 +197,7 @@ export function moveChapterContents(chapterId: string, offsetY: number) {
     const chapter = findNode(chapterId);
 
     let nextNode = graphService.getNextNode(chapter);
-    while(nextNode) {
+    while (nextNode) {
         nextNode.position.y += offsetY;
         nextNode = graphService.getNextNode(nextNode);
     }
diff --git a/src/shared/services/graph/content.service.ts b/src/shared/services/graph/content.service.ts
index 288cb409e2a131d30873ef74a5cc4eb762712e23..ac562fd17dd747fd77f10e49256342e3dbe4df27 100644
--- a/src/shared/services/graph/content.service.ts
+++ b/src/shared/services/graph/content.service.ts
@@ -92,7 +92,9 @@ export function unselectAllContents(): void {
 }
 
 export function getContentDefaultValues(type: string) {
-    const form = [...forms.questionForms, ...forms.elementForms, ...forms.nodeForms].find((f) => f.type === type);
+    const form = [...forms.questionForms.value, ...forms.elementForms.value, ...forms.nodeForms.value].find(
+        (f) => f.type === type,
+    );
 
     return form.fields.reduce((acc, field) => {
         // eslint-disable-next-line @typescript-eslint/no-explicit-any
diff --git a/src/shared/services/graph/element.service.ts b/src/shared/services/graph/element.service.ts
index 58ef472d363d1c3dbbf4d75b113a1fa48a765705..426be115e39763b46a8652760d668476f5f29393 100644
--- a/src/shared/services/graph/element.service.ts
+++ b/src/shared/services/graph/element.service.ts
@@ -94,6 +94,8 @@ export function getElementType(contentId: string): ElementType {
         return getContentType(contentId);
     }
 
+    console.log('type', node.type);
+
     return node.type as ElementType;
 }
 
diff --git a/src/shared/services/import.service.ts b/src/shared/services/import.service.ts
index 46fe0e727ef9e472d76b8849ea72c34d498fbef2..5e8e279d30576877f4cd581491fda8e85a483de7 100644
--- a/src/shared/services/import.service.ts
+++ b/src/shared/services/import.service.ts
@@ -17,15 +17,15 @@ import { saveState } from '@/src/shared/services/undoRedo.service';
 import { useEditorStore, useGraphStore } from '@/src/shared/stores';
 
 const mapType = {
-    video: standardPages.find((s) => s.type === 'video'),
-    html: standardPages.find((s) => s.type === 'text'),
-    audio: standardPages.find((s) => s.type === 'audio'),
-    'multiple-choice': questions.find((s) => s.type === 'choice'),
-    choice: questions.find((s) => s.type === 'choice'),
-    'drag-and-drop': questions.find((s) => s.type === 'drag-and-drop'),
-    'dropdown-list': questions.find((s) => s.type === 'dropdown-list'),
-    swipe: questions.find((s) => s.type === 'swipe'),
-    reorder: questions.find((s) => s.type === 'reorder'),
+    video: standardPages.value.find((s) => s.type === 'video'),
+    html: standardPages.value.find((s) => s.type === 'text'),
+    audio: standardPages.value.find((s) => s.type === 'audio'),
+    'multiple-choice': questions.value.find((s) => s.type === 'choice'),
+    choice: questions.value.find((s) => s.type === 'choice'),
+    'drag-and-drop': questions.value.find((s) => s.type === 'drag-and-drop'),
+    'dropdown-list': questions.value.find((s) => s.type === 'dropdown-list'),
+    swipe: questions.value.find((s) => s.type === 'swipe'),
+    reorder: questions.value.find((s) => s.type === 'reorder'),
 };
 
 export function createGraphEpocFromData(epoc: EpocV1) {
@@ -70,7 +70,7 @@ export function createGraphEpocFromData(epoc: EpocV1) {
                 const contentElement = newQuestion(epoc, id, qid);
                 contentElements.push(contentElement);
             } else if (content.type === 'choice') {
-                const action = standardPages.find((s) => s.type === 'legacy-condition');
+                const action = standardPages.value.find((s) => s.type === 'legacy-condition');
                 const choiceResolver = (content as ChoiceCondition).conditionResolver;
                 const contentElement = {
                     id: generateId(),
diff --git a/src/shared/stores/editorStore.ts b/src/shared/stores/editorStore.ts
index e5f69b2db69c3dc1ead701894b8ca1e852257ca5..2d91bc251e1cb487df4f105baef3dba4e1097525 100644
--- a/src/shared/stores/editorStore.ts
+++ b/src/shared/stores/editorStore.ts
@@ -100,8 +100,8 @@ export const useEditorStore = defineStore('editor', {
 
         // Data
         pageModels: [],
-        questions: questions,
-        standardPages: standardPages,
+        questions: questions.value,
+        standardPages: standardPages.value,
 
         // Modal
         conditionModal: false,
@@ -135,7 +135,7 @@ export const useEditorStore = defineStore('editor', {
 
         sideMenuOpen(): boolean {
             return this.modelMenu || this.badgeMenu;
-        }
+        },
     },
 
     actions: {
@@ -163,7 +163,7 @@ export const useEditorStore = defineStore('editor', {
             this.openedElementId = null;
 
             setTimeout(() => {
-                this.formPanel.form = formsModel.find((form) => form.type === 'badge');
+                this.formPanel.form = formsModel.value.find((form) => form.type === 'badge');
             });
 
             if (scrollPosY) this.scrollFormPanel(scrollPosY);
@@ -183,7 +183,7 @@ export const useEditorStore = defineStore('editor', {
             //? To be sure the view is notified of closing / reopening
             this.formPanel.form = null;
             setTimeout(() => {
-                this.formPanel.form = formsModel.find((form) => form.type === formType);
+                this.formPanel.form = formsModel.value.find((form) => form.type === formType);
             });
 
             if (scrollPosY) this.scrollFormPanel(scrollPosY);
@@ -259,9 +259,9 @@ export const useEditorStore = defineStore('editor', {
         },
 
         toggleSideMenu(type: SideMenu) {
-            for(const key in sideMenus) {
-                this[sideMenus[key]] = (key === type) ? !this[sideMenus[key]] : false;
+            for (const key in sideMenus) {
+                this[sideMenus[key]] = key === type ? !this[sideMenus[key]] : false;
             }
-        }
+        },
     },
 });
diff --git a/src/shared/stores/settingsStore.ts b/src/shared/stores/settingsStore.ts
index 43a720fcfaa8b13c26be1bf2682b84d50180d2a9..ef51377e51c274a0a13c1be41a8bf4247c140cb3 100644
--- a/src/shared/stores/settingsStore.ts
+++ b/src/shared/stores/settingsStore.ts
@@ -1,19 +1,30 @@
 import { defineStore } from 'pinia';
 import { editorService } from '@/src/shared/services';
 import { Settings } from '@/src/shared/interfaces';
+import { i18n } from '@/i18n/config';
 
 interface SettingsState {
-    settings?: Settings
+    settings?: Settings;
+    initialized: boolean;
 }
 
 export const useSettingsStore = defineStore('settings', {
     state: (): SettingsState => ({
-        settings: undefined
+        settings: undefined,
+        initialized: false,
     }),
 
     actions: {
         init() {
             editorService.fetchSettings();
+            this.initialized = true;
+        },
+
+        initSettings(settings: Settings) {
+            this.settings = settings;
+            if (this.settings.locale && this.settings.locale !== i18n.global.locale) {
+                i18n.global.locale = this.settings.locale;
+            }
         },
 
         sendSettings() {
@@ -22,8 +33,9 @@ export const useSettingsStore = defineStore('settings', {
 
         setSettings(spellcheck: boolean) {
             this.settings.spellcheck = spellcheck;
+            this.settings.locale = i18n.global.locale;
 
             this.sendSettings();
-        }
-    }
-})
+        },
+    },
+});
diff --git a/src/shared/utils/string.ts b/src/shared/utils/string.ts
new file mode 100644
index 0000000000000000000000000000000000000000..4a515878b5e0760b3b5998ee9c31075bac9726e9
--- /dev/null
+++ b/src/shared/utils/string.ts
@@ -0,0 +1,3 @@
+export function capitalizeFirstLetter(val) {
+    return String(val).charAt(0).toUpperCase() + String(val).slice(1);
+}
diff --git a/src/views/EditorPage.vue b/src/views/EditorPage.vue
index 534e3cde6500ffccbdc788a78621738950b49b11..42563c8601712e88a31db2e0188f7fa8f085b84a 100644
--- a/src/views/EditorPage.vue
+++ b/src/views/EditorPage.vue
@@ -14,6 +14,8 @@ import { setupContextMenu } from '../shared/services/contextMenu.service';
 import { computed } from 'vue';
 import ModelMenu from '@/src/features/sideBar/components/ModelMenu.vue';
 import BadgeMenu from '@/src/features/sideBar/components/BadgeMenu.vue';
+import { onMounted } from 'vue';
+import { useSettingsStore } from '@/src/shared/stores';
 
 const editorStore = useEditorStore();
 
@@ -32,8 +34,11 @@ function addKeyboardEvent(event: KeyboardEvent) {
     if (metaKey || ctrlKey) {
         if (key === 'v') {
             // Permits to paste in the WYSIWYG link editor modal
-            if((event.target as HTMLElement).className.indexOf('tox-textfield') !== -1
-                || (event.target as HTMLElement).className.indexOf('tox-textarea') !== -1) return;
+            if (
+                (event.target as HTMLElement).className.indexOf('tox-textfield') !== -1 ||
+                (event.target as HTMLElement).className.indexOf('tox-textarea') !== -1
+            )
+                return;
 
             event.preventDefault();
             saveState();
@@ -82,7 +87,13 @@ function onRemoveCursor() {
 }
 
 const editorDisplay = computed(() => (editorStore.selectNodeMode ? 'editor-flex' : 'editor-grid'));
-const sideMenuOpen = computed(() => editorStore.sideMenuOpen ? 'side-menu-open' : '');
+const sideMenuOpen = computed(() => (editorStore.sideMenuOpen ? 'side-menu-open' : ''));
+
+//? For some reason, this code doesn't work in App.vue
+const settingsStore = useSettingsStore();
+if (!settingsStore.initialized) {
+    settingsStore.init();
+}
 </script>
 
 <template>
@@ -95,8 +106,8 @@ const sideMenuOpen = computed(() => editorStore.sideMenuOpen ? 'side-menu-open'
         <SideBar v-if="!editorStore.selectNodeMode" class="side-bar" @dragover="onCursorNotAllowed" />
         <TopBar v-if="!editorStore.selectNodeMode" class="top-bar" @dragover="onCursorNotAllowed" />
         <div v-if="editorStore.selectNodeMode" class="flex-information">
-            <h4>Cliquer sur l'élément de contenu concerné par la condition pour la sélectionner</h4>
-            <button class="btn btn-top-bar" @click="exitSelectNodeMode()">Annuler</button>
+            <h4>{{ $t('editor.select') }}</h4>
+            <button class="btn btn-top-bar" @click="exitSelectNodeMode()">{{ $t('global.cancel') }}</button>
         </div>
         <ePocFlow class="editor-content" @dragover="onCursorAllowed" />
         <Transition>
diff --git a/src/views/LandingPage.vue b/src/views/LandingPage.vue
index 3d9b0ef31d1743b50fec8325d5ced13b7e87f114..cd34a15f0b8f33f7453237872a5141d05ce96187 100644
--- a/src/views/LandingPage.vue
+++ b/src/views/LandingPage.vue
@@ -1,14 +1,19 @@
 <script setup lang="ts">
-import { useEditorStore } from '@/src/shared/stores';
+import { useEditorStore, useSettingsStore } from '@/src/shared/stores';
 import { editorService } from '@/src/shared/services';
 import { ePocProject } from '@/src/shared/interfaces';
 import ChoiceModal from '@/src/components/ChoiceModal.vue';
 import { createGraphFromImport } from '@/src/shared/services/import.service';
+import { onMounted } from 'vue';
 
 const editorStore = useEditorStore();
-
 editorService.setup();
 
+const settingsStore = useSettingsStore();
+if (!settingsStore.initialized) {
+    settingsStore.init();
+}
+
 function pickProject() {
     editorService.pickEpocProject();
 }
@@ -44,30 +49,30 @@ function importProject() {
             @accept="importProject"
             @cancel="cancelImport"
         >
-            <h3>Cet ePoc est une version publiée, vous devez l'importer avant de pouvoir l'éditer ici</h3>
+            <h3>{{ $t('landing.published') }}</h3>
         </ChoiceModal>
 
         <div v-if="editorStore.loading" class="loading">
             <div class="spinner"></div>
             <span v-if="editorStore.currentProject.filepath">
-                Chargement de "{{ editorStore.currentProject.filepath }}"
+                {{ $t('landing.loadingPath', { path: editorStore.currentProject.filepath }) }}
             </span>
-            <span v-else>Chargement de l'ePoc</span>
+            <span v-else>{{ $t('landing.loadingEpoc') }}</span>
         </div>
 
         <div v-else>
             <div class="buttons">
                 <button class="btn btn-outline btn-large" @click="pickProject">
                     <i class="icon-ouvrir" />
-                    Ouvrir un projet existant
+                    {{ $t('landing.open') }}
                 </button>
                 <button class="btn btn-outline btn-large" @click="createProject">
                     <i class="icon-creer" />
-                    Créer un nouveau projet
+                    {{ $t('landing.create') }}
                 </button>
             </div>
             <div>
-                <h3>Fichiers récents</h3>
+                <h3>{{ $t('landing.recents') }}</h3>
                 <hr class="separator" />
                 <div v-for="epoc of editorStore.recentProjects" :key="epoc.name" class="card-list-item">
                     <div class="card-icon">
@@ -78,7 +83,7 @@ function importProject() {
                     </p>
                     <small>{{ new Date(epoc.modified).toLocaleString() }}</small>
                     <hr class="vertical-separator" />
-                    <div class="btn-open" @click="openProject(epoc)">Ouvrir</div>
+                    <div class="btn-open" @click="openProject(epoc)">{{ $t('global.open') }}</div>
                 </div>
             </div>
         </div>