i18n, thème & build
Dernière étape : la traduction, le thème et le build. Le module partage l'instance i18n du host et émet ses styles dans une couche cascade dédiée.
i18n par namespace #
// frontend/src/i18n.ts
import { registerModuleTranslations } from '@kubuno/sdk'
const fr = { create: 'Nouvelle note', search_ph: 'Rechercher…', note_shared: 'Note partagée' }
const en = { create: 'New note', search_ph: 'Search…', note_shared: 'Note shared' }
// … les 13 langues (es, pt, it, de, el, ru, ar, he, hi, zh, ja)
registerModuleTranslations('memo', { en, fr, /* … */ })import { useTranslation } from 'react-i18next'
const { t } = useTranslation('memo') // namespace
<button>{t('create')}</button> // ou t('memo:create')Thème & couche CSS #
/* frontend/src/index.css — émettre les utilitaires dans la couche `kubuno-module`
pour ne JAMAIS écraser le shell host injecté avant le module. */
@layer theme, base, components, kubuno-module;
@import "tailwindcss/theme.css" layer(theme);
@import "tailwindcss/preflight.css" layer(base);
@import "tailwindcss/utilities.css" layer(kubuno-module) source("../");
@import "./theme.css";
/* Accent propre au module (le host enrobe le contenu dans [data-module="memo"]) */
[data-module="memo"] {
--color-primary: #e8710a;
--color-primary-hover: #c75e00;
--color-primary-light: #fde7d3;
}Note
Sans la couche kubuno-module, les utilitaires Tailwind du module (réinjectés au runtime) écraseraient ceux du shell. Le tsconfig.json mappe le specifier @ui vers @kubuno/ui.
Build Vite #
// vite.config.ts — ne JAMAIS bundler les specifiers partagés
const SHARED = new Set([
'react','react-dom','react/jsx-runtime','zustand','react-i18next','i18next',
'@ui','@kubuno/sdk','@kubuno/drive','@radix-ui/react-dropdown-menu',
])
export default defineConfig({
base: './',
plugins: [react(), tailwindcss()],
build: {
cssCodeSplit: false, // un seul entry.css
rollupOptions: {
input: './src/entry.ts',
external: (s) => SHARED.has(s) || s.startsWith('@kubuno/'),
output: { format: 'es', entryFileNames: 'entry.js',
assetFileNames: (i) => i.name?.endsWith('.css') ? 'entry.css' : 'assets/[name][extname]' },
},
},
})Résultat : dist/entry.js + dist/entry.css, où React, Zustand et @kubuno/* sont external et résolus au runtime par l'import map du host. Votre module est prêt à être empaqueté en .deb !
Fin du tutoriel
Félicitations — vous avez parcouru un module complet, du schéma SQL aux interactions inter-modules. 🎉