Tutoriel frontend — « Memo »

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. 🎉