@sntlr/registry-shell 1.0.0 → 1.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (36) hide show
  1. package/README.md +2 -5
  2. package/dist/adapter/custom.d.ts +1 -1
  3. package/dist/adapter/default.d.ts +3 -3
  4. package/dist/adapter/default.js +3 -3
  5. package/dist/cli/init.js +0 -3
  6. package/dist/cli/init.js.map +1 -1
  7. package/dist/cli/shared.js +0 -5
  8. package/dist/cli/shared.js.map +1 -1
  9. package/dist/config-loader.d.ts +0 -2
  10. package/dist/config-loader.js +0 -1
  11. package/dist/define-config.d.ts +0 -6
  12. package/package.json +1 -1
  13. package/src/adapter/custom.ts +1 -1
  14. package/src/adapter/default.ts +3 -3
  15. package/src/cli/init.ts +0 -3
  16. package/src/cli/shared.ts +0 -4
  17. package/src/config-loader.ts +0 -3
  18. package/src/define-config.ts +0 -7
  19. package/src/next-app/app/globals.css +329 -329
  20. package/src/next-app/app/page.tsx +1 -1
  21. package/src/next-app/components/component-icon.tsx +140 -140
  22. package/src/next-app/components/heading-anchor.tsx +52 -52
  23. package/src/next-app/{fallback → components}/homepage.tsx +2 -3
  24. package/src/next-app/components/navigation-progress.tsx +62 -62
  25. package/src/next-app/components/resizable-preview.tsx +101 -101
  26. package/src/next-app/components/sidebar-provider.tsx +75 -75
  27. package/src/next-app/hooks/use-controls.ts +72 -72
  28. package/src/next-app/lib/i18n.tsx +630 -630
  29. package/src/next-app/lib/registry-adapter.ts +6 -32
  30. package/src/next-app/lib/utils.ts +6 -6
  31. package/src/next-app/next-env.d.ts +6 -6
  32. package/src/next-app/next.config.ts +0 -5
  33. package/src/next-app/postcss.config.mjs +7 -7
  34. package/src/next-app/user-aliases.d.ts +0 -7
  35. package/src/next-app/app/_user-global.css +0 -6
  36. package/src/next-app/app/_user-sources.css +0 -9
@@ -1,630 +1,630 @@
1
- "use client"
2
-
3
- import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useState, type ReactNode } from "react"
4
-
5
- /**
6
- * `useLayoutEffect` runs synchronously after DOM commit and *before* paint, so
7
- * it lets us swap the locale post-hydration without ever flashing the default
8
- * content. On the server it warns, so we fall back to `useEffect` there — the
9
- * fallback never actually runs because Next.js doesn't execute effects during
10
- * SSR, but using it satisfies React's "isomorphic" contract.
11
- */
12
- const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect
13
-
14
- /**
15
- * Locale code. The shell ships translations for `"en"` and `"fr"` in its
16
- * base table; registries can add more via `extraTranslations` on their
17
- * shell config. The type is widened to `string` so arbitrary registry-
18
- * supplied locales type-check.
19
- */
20
- export type Locale = string
21
-
22
- const translations = {
23
- en: {
24
- // Header
25
- "header.search": "Search...",
26
-
27
- // Sidebar
28
- "sidebar.documentation": "Documentation",
29
- "sidebar.components": "Components",
30
- "sidebar.blocks": "Blocks",
31
-
32
- // Search
33
- "search.placeholder": "Search documentation and components...",
34
- "search.noResults": "No results found.",
35
- "search.groupDocs": "Documentation",
36
- "search.groupComponents": "Components",
37
-
38
- // Home
39
- "home.title": "UI Registry",
40
- "home.description": "A custom component registry built with shadcn. Browse documentation and components to get started.",
41
- "home.getStarted": "Get Started",
42
- "home.browseComponents": "Browse Components",
43
- "home.openSource": "Open Source",
44
- "home.builtOnShadcn": "Built on shadcn",
45
- "home.heroTitle": "Scintillar UI",
46
- "home.heroDescription": "Web components built for live collaboration with React & Next.",
47
- "home.heroFeature.collaborative": "Collaborative",
48
- "home.heroFeature.tested": "Tested",
49
- "home.heroFeature.accessible": "Accessible",
50
- "home.prototypingTitle": "Your code. Your repo. Our standards.",
51
- "home.prototypingDescription": "Stop maintaining the technical scaffolding. Get an audited component registry that drops natively into your workflow and your tests.",
52
- "home.proto.openSource.title": "You own the code",
53
- "home.proto.openSource.desc": "Components ship straight into your codebase. Pull from the registry, edit them as you like — or let us handle the heavy lifting.",
54
- "home.proto.openSource.label.registry": "Registry",
55
- "home.proto.openSource.label.yours": "your codebase",
56
- "home.proto.tested.title": "Heavily tested",
57
- "home.proto.tested.desc": "Every component is audited for accessibility, interactivity and stability. Real test suites, not promises.",
58
- "home.proto.tested.suite": "Test suite",
59
- "home.proto.tested.passed": "passed",
60
- "home.proto.tested.case.renders": "renders the trigger",
61
- "home.proto.tested.case.opens": "opens on click",
62
- "home.proto.tested.case.closes": "closes on Escape",
63
- "home.proto.tested.case.focus": "traps focus when open",
64
- "home.proto.tested.case.keyboard": "supports keyboard nav",
65
- "home.proto.tested.case.motion": "respects reduced motion",
66
- "home.proto.tested.case.axe": "passes axe scan",
67
- "home.proto.ai.title": "Built for AI agents",
68
- "home.proto.ai.desc": "We obsess over AIX (AI Experience) the same way the rest of the world obsesses over DevEX. Predictable APIs, machine-readable docs, structured props, and registries that LLMs can navigate.",
69
- "home.proto.ds.title": "Scintillar Design System",
70
- "home.proto.ds.desc": "Documents complex realtime workflows, recommends a database & infra stack, and ships a one-line installer for a working collaborative starter — most of the hard problems already solved.",
71
- "home.proto.ds.cta": "Learn more",
72
- "home.proto.ds.glow": "Premium",
73
- "home.dashboardTitle": "Real apps, not toys",
74
- "home.dashboardDescription": "A complete analytics dashboard — stat cards, sortable tables, charts, activity feed — built entirely with Scintillar components. Drop it into your codebase and own every line.",
75
- "home.dash.cardTitle": "Analytics Overview",
76
- "home.dash.range": "Last 7 days",
77
- "home.dash.tab.overview": "Overview",
78
- "home.dash.tab.revenue": "Revenue",
79
- "home.dash.tab.users": "Users",
80
- "home.dash.chartTitle": "Revenue Over Time",
81
- "home.dash.activityTitle": "Recent Activity",
82
- "home.dash.tableTitle": "Recent Transactions",
83
- "home.dash.col.name": "Name",
84
- "home.dash.col.email": "Email",
85
- "home.dash.col.status": "Status",
86
- "home.dash.col.amount": "Amount",
87
- "home.socialProofTitle": "What developers are saying",
88
- "home.installHint": "npx shadcn@latest add",
89
- "home.showcaseTitle": "Built for real apps",
90
- "home.showcaseDescription": "Real-world patterns from authentication to collaboration, ready to drop into your project.",
91
- "home.featuresTitle": "Why Scintillar?",
92
- "home.featureA11yTitle": "Accessible by Default",
93
- "home.featureA11yDesc": "WCAG 2.1 AA audited. Keyboard navigation, ARIA attributes, and screen reader support out of the box.",
94
- "home.featurePreviewTitle": "Interactive Previews",
95
- "home.featurePreviewDesc": "Zoom, pan, and drag on a live canvas. Tweak props in real time and see exactly how components behave.",
96
- "home.featureCopyTitle": "Copy & Paste",
97
- "home.featureCopyDesc": "No black-box dependencies. Copy the source into your project and own it completely.",
98
- "home.featureTypedTitle": "Fully Typed & Tested",
99
- "home.featureTypedDesc": "Strict TypeScript, 145+ unit tests, E2E coverage, visual regression, and automated accessibility audits.",
100
- "home.statsComponents": "Components & Blocks",
101
- "home.statsOpenSource": "Open-Source",
102
- "home.statsCollaborative": "Collaborative",
103
- "home.statsAccessible": "Accessible",
104
- "home.statsLocalized": "Localized",
105
- "home.statsCoverage": "Test Coverage",
106
- "home.statsYoursToKeep": "Yours to Keep",
107
- "home.codeTitle": "Get started in seconds",
108
- "home.codeDescription": "Add any component from the registry to your project. You own the code.",
109
- "home.codeCopied": "Copied!",
110
- "home.ctaTitle": "Ready to build?",
111
- "home.ctaDescription": "Start adding components to your project today.",
112
- "home.viewOnGithub": "View on GitHub",
113
- "footer.components": "Components",
114
- "footer.docs": "Documentation",
115
- "footer.gettingStarted": "Getting Started",
116
- "footer.accessibility": "Accessibility",
117
- "footer.community": "Community",
118
- "footer.legal": "Legal",
119
- "footer.privacy": "Privacy Policy",
120
- "footer.terms": "Terms of Service",
121
- "footer.faq": "FAQ",
122
- "footer.contact": "Contact",
123
- "footer.copyright": "Scintillar. All rights reserved.",
124
- "home.uiClientTitle": "Interested by the shell, not the components?",
125
- "home.uiClientDescription": "Use @sntlr/ui-client to scaffold a complete component documentation site with interactive previews, accessibility audits, and more.",
126
- "home.uiClientFeature1Title": "Interactive Preview",
127
- "home.uiClientFeature1Desc": "Zoom, pan, and drag on a live component canvas.",
128
- "home.uiClientFeature2Title": "Auto-generated Docs",
129
- "home.uiClientFeature2Desc": "Props, accessibility, and test documentation out of the box.",
130
- "home.uiClientFeature3Title": "Accessible & i18n",
131
- "home.uiClientFeature3Desc": "WCAG 2.1 AA compliant with built-in language support.",
132
- "home.uiClientLearnMore": "Learn more",
133
-
134
- // Hero — Chart
135
- "hero.chart.title": "Weekly Revenue",
136
- "hero.chart.range": "7d",
137
- "hero.chart.day.mon": "Mon",
138
- "hero.chart.day.tue": "Tue",
139
- "hero.chart.day.wed": "Wed",
140
- "hero.chart.day.thu": "Thu",
141
- "hero.chart.day.fri": "Fri",
142
- "hero.chart.day.sat": "Sat",
143
- "hero.chart.day.sun": "Sun",
144
-
145
- // Hero — Notifications
146
- "hero.notifications.title": "Notifications",
147
- "hero.notifications.markAllRead": "Mark all as read",
148
- "hero.notifications.settings": "Notification settings",
149
- "hero.notifications.settingsAria": "Settings",
150
- "hero.notifications.searchPlaceholder": "Search...",
151
- "hero.notifications.dismiss": "Dismiss notification",
152
- "hero.notifications.markRead": "Mark as read",
153
- "hero.notifications.markUnread": "Mark as unread",
154
- "hero.notifications.delete": "Delete",
155
- "hero.notifications.empty": "No notifications",
156
-
157
- // Hero — Cursors
158
- "hero.cursors.title": "Live Cursors",
159
- "hero.cursors.badge": "collaborative",
160
- "hero.cursors.you": "You",
161
- "hero.display.group": "Label style",
162
- "hero.display.name": "Show names",
163
- "hero.display.avatar": "Show avatars",
164
-
165
- // Hero — Data Table
166
- "hero.dataTable.searchPlaceholder": "Search invoices...",
167
- "hero.dataTable.col.invoice": "Invoice",
168
- "hero.dataTable.col.status": "Status",
169
- "hero.dataTable.col.method": "Method",
170
- "hero.dataTable.col.amount": "Amount",
171
- "hero.dataTable.status.paid": "Paid",
172
- "hero.dataTable.status.pending": "Pending",
173
- "hero.dataTable.status.overdue": "Overdue",
174
- "hero.dataTable.method.card": "Card",
175
- "hero.dataTable.method.paypal": "PayPal",
176
- "hero.dataTable.method.bank": "Bank",
177
- "hero.dataTable.filter.status": "Status",
178
- "hero.dataTable.filter.method": "Method",
179
- "hero.dataTable.agg.sum": "Sum",
180
- "hero.dataTable.agg.avg": "Avg",
181
- "hero.dataTable.agg.max": "Max",
182
- "hero.dataTable.columns": "Columns",
183
- "hero.dataTable.rowsPerPage": "Rows per page",
184
-
185
- // Hero — Profile
186
- "hero.profile.defaultName": "Jane Doe",
187
- "hero.profile.defaultBio": "Product designer at Scintillar. Building beautiful and accessible UIs.",
188
- "hero.profile.title": "Profile",
189
- "hero.profile.description": "Manage your public profile information.",
190
- "hero.profile.cancel": "Cancel",
191
- "hero.profile.save": "Save",
192
- "hero.profile.uploadHint": "Click to upload",
193
- "hero.profile.avatarAlt": "Avatar",
194
- "hero.profile.nameLabel": "Name",
195
- "hero.profile.namePlaceholder": "Your name",
196
- "hero.profile.emailLabel": "Email",
197
- "hero.profile.bioLabel": "Bio",
198
- "hero.profile.bioPlaceholder": "Tell us about yourself",
199
-
200
- // Hero — WYSIWYG (toolbar aria + author name)
201
- "hero.wysiwyg.aria.bold": "Bold",
202
- "hero.wysiwyg.aria.italic": "Italic",
203
- "hero.wysiwyg.aria.underline": "Underline",
204
- "hero.wysiwyg.aria.heading": "Heading",
205
- "hero.wysiwyg.aria.list": "List",
206
- "hero.wysiwyg.aria.quote": "Quote",
207
- "hero.wysiwyg.aria.alignLeft": "Align left",
208
- "hero.wysiwyg.aria.alignCenter": "Align center",
209
- "hero.wysiwyg.aria.alignRight": "Align right",
210
- "hero.wysiwyg.author": "Edith",
211
-
212
- // Component page
213
- "component.subtitle": "Install this component from the Scintillar registry.",
214
- "component.installation": "Installation",
215
- "component.source": "Source",
216
- "component.propsBehavior": "Props & Behavior",
217
- "component.docsPlaceholder": "Documentation for this component has not been written yet.",
218
- "component.guidelinesPlaceholder": "Usage guidelines for this component have not been written yet.",
219
- "component.a11yPlaceholder": "Accessibility documentation for this component has not been written yet.",
220
-
221
- // Tabs
222
- "tabs.install": "Install",
223
- "tabs.documentation": "Documentation",
224
- "tabs.guidelines": "Guidelines",
225
- "tabs.accessibility": "Accessibility",
226
- "tabs.tests": "Tests",
227
- "component.testsPlaceholder": "No functional tests have been written for this component yet.",
228
- "tests.health": "Health",
229
- "tests.propsDocs": "Props documented",
230
- "tests.a11yDocs": "Accessibility documented",
231
- "tests.preview": "Preview available",
232
- "tests.unit": "Unit Tests",
233
- "tests.interaction": "Interaction Tests",
234
- "tests.visual": "Visual Regression",
235
- "tests.accessibility": "Accessibility Audit",
236
- "tests.performance": "Performance",
237
- "tests.tests": "Tests",
238
-
239
- // TOC
240
- "toc.title": "On this page",
241
-
242
- // Controls
243
- "controls.title": "Controls",
244
-
245
- // Preview
246
- "preview.noPreview": "No preview available",
247
-
248
- // Accessibility
249
- "a11y.skipToContent": "Skip to content",
250
- "a11y.loading": "Loading...",
251
- "a11y.keyboard": "Keyboard Interactions",
252
- "a11y.focus": "Focus Management",
253
- "a11y.focusVisible": "Focus visible",
254
- "a11y.noFocusVisible": "No focus visible",
255
- "a11y.focusTrapped": "Focus trapped",
256
- "a11y.noFocusTrap": "No focus trap",
257
- "a11y.screenReader": "Screen Reader",
258
- "a11y.colorMotion": "Color & Motion",
259
- "a11y.text": "Text",
260
- "a11y.ui": "UI",
261
- "a11y.motionLabel": "Motion",
262
- "a11y.noKeyboard": "This component has no keyboard interactions.",
263
- "a11y.devChecklist": "Developer Checklist",
264
- "a11y.checklistComplete": "All checks passed!",
265
- "a11y.checklistCompleteDesc": "You've verified all accessibility requirements for this component.",
266
- "a11y.checklistDismiss": "Show checklist",
267
- "a11y.element": "Element",
268
- "a11y.role": "Role",
269
- "a11y.key": "Key",
270
- "a11y.action": "Action",
271
-
272
- // Settings
273
- "settings.title": "Settings",
274
- "settings.theme": "Theme",
275
- "settings.light": "Light",
276
- "settings.dark": "Dark",
277
- "settings.system": "System",
278
- "settings.language": "Language",
279
- },
280
- fr: {
281
- // Header
282
- "header.search": "Rechercher...",
283
-
284
- // Sidebar
285
- "sidebar.documentation": "Documentation",
286
- "sidebar.components": "Composants",
287
- "sidebar.blocks": "Blocs",
288
-
289
- // Search
290
- "search.placeholder": "Rechercher dans la documentation et les composants...",
291
- "search.noResults": "Aucun résultat trouvé.",
292
- "search.groupDocs": "Documentation",
293
- "search.groupComponents": "Composants",
294
-
295
- // Home
296
- "home.title": "UI Registry",
297
- "home.description": "Un registre de composants personnalisé construit avec shadcn. Parcourez la documentation et les composants pour commencer.",
298
- "home.getStarted": "Commencer",
299
- "home.browseComponents": "Parcourir les composants",
300
- "home.openSource": "Open Source",
301
- "home.builtOnShadcn": "Basé sur shadcn",
302
- "home.heroTitle": "Scintillar UI",
303
- "home.heroDescription": "Composants web pensés pour la collaboration en direct pour React & Next.",
304
- "home.heroFeature.collaborative": "Collaboratifs",
305
- "home.heroFeature.tested": "Testés",
306
- "home.heroFeature.accessible": "Accessibles",
307
- "home.prototypingTitle": "Votre code. Votre dépôt. Nos standards.",
308
- "home.prototypingDescription": "Libérez-vous de la maintenance du cadre technique. Profitez d'un registre de composants audités qui s'intègrent nativement dans votre workflow et vos tests.",
309
- "home.proto.openSource.title": "Le code vous appartient",
310
- "home.proto.openSource.desc": "Les composants atterrissent directement dans votre dépôt. Récupérez-les depuis le registre, modifiez-les comme vous voulez — ou laissez-nous faire le gros du travail.",
311
- "home.proto.openSource.label.registry": "Registre",
312
- "home.proto.openSource.label.yours": "votre dépôt",
313
- "home.proto.tested.title": "Hautement testés",
314
- "home.proto.tested.desc": "Chaque composant est audité pour l'accessibilité, l'interactivité et la stabilité. De vrais tests, pas des promesses.",
315
- "home.proto.tested.suite": "Suite de tests",
316
- "home.proto.tested.passed": "réussis",
317
- "home.proto.tested.case.renders": "rendu du trigger",
318
- "home.proto.tested.case.opens": "ouverture au clic",
319
- "home.proto.tested.case.closes": "fermeture avec Échap",
320
- "home.proto.tested.case.focus": "piège le focus à l'ouverture",
321
- "home.proto.tested.case.keyboard": "navigation au clavier",
322
- "home.proto.tested.case.motion": "respect du prefers-reduced-motion",
323
- "home.proto.tested.case.axe": "audit axe réussi",
324
- "home.proto.ai.title": "Conçus pour les agents IA",
325
- "home.proto.ai.desc": "Nous nous obsédons pour l'AIX (AI Experience) autant que le reste du monde s'obsède pour la DevEX. APIs prévisibles, docs lisibles par machine, props structurées, et un registre que les LLM peuvent naviguer.",
326
- "home.proto.ds.title": "Scintillar Design System",
327
- "home.proto.ds.desc": "Documente les workflows temps-réel complexes, recommande une stack base de données & infra, et installe en une ligne un starter collaboratif fonctionnel — la plupart des problèmes durs déjà résolus.",
328
- "home.proto.ds.cta": "En savoir plus",
329
- "home.proto.ds.glow": "Premium",
330
- "home.dashboardTitle": "De vraies applis, pas des jouets",
331
- "home.dashboardDescription": "Un tableau de bord analytique complet — cartes de stats, tableaux triables, graphiques, fil d'activité — construit entièrement avec les composants Scintillar. Intégrez-le à votre dépôt et appropriez-vous chaque ligne.",
332
- "home.dash.cardTitle": "Vue d'ensemble analytique",
333
- "home.dash.range": "7 derniers jours",
334
- "home.dash.tab.overview": "Vue d'ensemble",
335
- "home.dash.tab.revenue": "Revenus",
336
- "home.dash.tab.users": "Utilisateurs",
337
- "home.dash.chartTitle": "Évolution des revenus",
338
- "home.dash.activityTitle": "Activité récente",
339
- "home.dash.tableTitle": "Transactions récentes",
340
- "home.dash.col.name": "Nom",
341
- "home.dash.col.email": "Courriel",
342
- "home.dash.col.status": "Statut",
343
- "home.dash.col.amount": "Montant",
344
- "home.socialProofTitle": "Ce que les développeurs en disent",
345
- "home.installHint": "npx shadcn@latest add",
346
- "home.showcaseTitle": "Conçu pour de vrais projets",
347
- "home.showcaseDescription": "Des scénarios concrets, de l'authentification à la collaboration, prêts à intégrer dans votre projet.",
348
- "home.featuresTitle": "Pourquoi Scintillar ?",
349
- "home.featureA11yTitle": "Accessible par défaut",
350
- "home.featureA11yDesc": "Audité WCAG 2.1 AA. Navigation clavier, attributs ARIA et support lecteur d'écran inclus.",
351
- "home.featurePreviewTitle": "Aperçus interactifs",
352
- "home.featurePreviewDesc": "Zoomez, glissez et manipulez sur un canevas interactif. Modifiez les props en temps réel.",
353
- "home.featureCopyTitle": "Copier-coller",
354
- "home.featureCopyDesc": "Pas de dépendance opaque. Copiez le code dans votre projet et appropriez-vous-le.",
355
- "home.featureTypedTitle": "Typé et testé",
356
- "home.featureTypedDesc": "TypeScript strict, plus de 145 tests unitaires, couverture E2E, régression visuelle et audits d'accessibilité automatisés.",
357
- "home.statsComponents": "Composants et blocs",
358
- "home.statsOpenSource": "Open-source",
359
- "home.statsCollaborative": "Collaboratif",
360
- "home.statsAccessible": "Accessible",
361
- "home.statsLocalized": "Localisé",
362
- "home.statsCoverage": "Couverture de tests",
363
- "home.statsYoursToKeep": "Le code vous appartient",
364
- "home.codeTitle": "Démarrez en quelques secondes",
365
- "home.codeDescription": "Ajoutez n'importe quel composant du registre à votre projet. Le code vous appartient.",
366
- "home.codeCopied": "Copié !",
367
- "home.ctaTitle": "Prêt à construire ?",
368
- "home.ctaDescription": "Commencez à ajouter des composants à votre projet dès aujourd'hui.",
369
- "home.viewOnGithub": "Voir sur GitHub",
370
- "footer.components": "Composants",
371
- "footer.docs": "Documentation",
372
- "footer.gettingStarted": "Commencer",
373
- "footer.accessibility": "Accessibilité",
374
- "footer.community": "Communauté",
375
- "footer.legal": "Légal",
376
- "footer.privacy": "Politique de confidentialité",
377
- "footer.terms": "Conditions d'utilisation",
378
- "footer.faq": "FAQ",
379
- "footer.contact": "Contact",
380
- "footer.copyright": "Scintillar. Tous droits réservés.",
381
- "home.uiClientTitle": "Intéressé par le shell, pas les composants ?",
382
- "home.uiClientDescription": "Utilisez @sntlr/ui-client pour générer un site de documentation de composants complet avec aperçus interactifs, audits d'accessibilité et plus encore.",
383
- "home.uiClientFeature1Title": "Aperçu interactif",
384
- "home.uiClientFeature1Desc": "Zoomez, glissez et manipulez sur un canevas de composants.",
385
- "home.uiClientFeature2Title": "Docs auto-générées",
386
- "home.uiClientFeature2Desc": "Documentation des props, de l'accessibilité et des tests incluse.",
387
- "home.uiClientFeature3Title": "Accessible et i18n",
388
- "home.uiClientFeature3Desc": "Conforme WCAG 2.1 AA avec support multilingue intégré.",
389
- "home.uiClientLearnMore": "En savoir plus",
390
-
391
- // Hero — Chart
392
- "hero.chart.title": "Revenus hebdomadaires",
393
- "hero.chart.range": "7 j",
394
- "hero.chart.day.mon": "Lun",
395
- "hero.chart.day.tue": "Mar",
396
- "hero.chart.day.wed": "Mer",
397
- "hero.chart.day.thu": "Jeu",
398
- "hero.chart.day.fri": "Ven",
399
- "hero.chart.day.sat": "Sam",
400
- "hero.chart.day.sun": "Dim",
401
-
402
- // Hero — Notifications
403
- "hero.notifications.title": "Notifications",
404
- "hero.notifications.markAllRead": "Tout marquer comme lu",
405
- "hero.notifications.settings": "Paramètres des notifications",
406
- "hero.notifications.settingsAria": "Paramètres",
407
- "hero.notifications.searchPlaceholder": "Rechercher...",
408
- "hero.notifications.dismiss": "Ignorer la notification",
409
- "hero.notifications.markRead": "Marquer comme lu",
410
- "hero.notifications.markUnread": "Marquer comme non lu",
411
- "hero.notifications.delete": "Supprimer",
412
- "hero.notifications.empty": "Aucune notification",
413
-
414
- // Hero — Cursors
415
- "hero.cursors.title": "Curseurs en direct",
416
- "hero.cursors.badge": "collaboratif",
417
- "hero.cursors.you": "Vous",
418
- "hero.display.group": "Style d'étiquette",
419
- "hero.display.name": "Afficher les noms",
420
- "hero.display.avatar": "Afficher les avatars",
421
-
422
- // Hero — Data Table
423
- "hero.dataTable.searchPlaceholder": "Rechercher des factures...",
424
- "hero.dataTable.col.invoice": "Facture",
425
- "hero.dataTable.col.status": "Statut",
426
- "hero.dataTable.col.method": "Méthode",
427
- "hero.dataTable.col.amount": "Montant",
428
- "hero.dataTable.status.paid": "Payée",
429
- "hero.dataTable.status.pending": "En attente",
430
- "hero.dataTable.status.overdue": "En retard",
431
- "hero.dataTable.method.card": "Carte",
432
- "hero.dataTable.method.paypal": "PayPal",
433
- "hero.dataTable.method.bank": "Virement",
434
- "hero.dataTable.filter.status": "Statut",
435
- "hero.dataTable.filter.method": "Méthode",
436
- "hero.dataTable.agg.sum": "Somme",
437
- "hero.dataTable.agg.avg": "Moy.",
438
- "hero.dataTable.agg.max": "Max",
439
- "hero.dataTable.columns": "Colonnes",
440
- "hero.dataTable.rowsPerPage": "Lignes par page",
441
-
442
- // Hero — Profile
443
- "hero.profile.defaultName": "Jeanne Dupont",
444
- "hero.profile.defaultBio": "Designer produit chez Scintillar. Conçoit des interfaces belles et accessibles.",
445
- "hero.profile.title": "Profil",
446
- "hero.profile.description": "Gérez vos informations de profil public.",
447
- "hero.profile.cancel": "Annuler",
448
- "hero.profile.save": "Enregistrer",
449
- "hero.profile.uploadHint": "Cliquez pour téléverser",
450
- "hero.profile.avatarAlt": "Avatar",
451
- "hero.profile.nameLabel": "Nom",
452
- "hero.profile.namePlaceholder": "Votre nom",
453
- "hero.profile.emailLabel": "Courriel",
454
- "hero.profile.bioLabel": "Bio",
455
- "hero.profile.bioPlaceholder": "Parlez-nous de vous",
456
-
457
- // Hero — WYSIWYG (toolbar aria + author name)
458
- "hero.wysiwyg.aria.bold": "Gras",
459
- "hero.wysiwyg.aria.italic": "Italique",
460
- "hero.wysiwyg.aria.underline": "Souligné",
461
- "hero.wysiwyg.aria.heading": "Titre",
462
- "hero.wysiwyg.aria.list": "Liste",
463
- "hero.wysiwyg.aria.quote": "Citation",
464
- "hero.wysiwyg.aria.alignLeft": "Aligner à gauche",
465
- "hero.wysiwyg.aria.alignCenter": "Centrer",
466
- "hero.wysiwyg.aria.alignRight": "Aligner à droite",
467
- "hero.wysiwyg.author": "Édith",
468
-
469
- // Component page
470
- "component.subtitle": "Installez ce composant depuis le registre Scintillar.",
471
- "component.installation": "Installation",
472
- "component.source": "Source",
473
- "component.propsBehavior": "Props et comportement",
474
- "component.docsPlaceholder": "La documentation de ce composant n'a pas encore été rédigée.",
475
- "component.guidelinesPlaceholder": "Les directives d'utilisation de ce composant n'ont pas encore été rédigées.",
476
- "component.a11yPlaceholder": "La documentation d'accessibilité de ce composant n'a pas encore été rédigée.",
477
-
478
- // Tabs
479
- "tabs.install": "Installer",
480
- "tabs.documentation": "Documentation",
481
- "tabs.guidelines": "Directives",
482
- "tabs.accessibility": "Accessibilité",
483
- "tabs.tests": "Tests",
484
- "component.testsPlaceholder": "Aucun test fonctionnel n'a été écrit pour ce composant.",
485
- "tests.health": "Santé",
486
- "tests.propsDocs": "Props documentés",
487
- "tests.a11yDocs": "Accessibilité documentée",
488
- "tests.preview": "Aperçu disponible",
489
- "tests.unit": "Tests unitaires",
490
- "tests.interaction": "Tests d'interaction",
491
- "tests.visual": "Régression visuelle",
492
- "tests.accessibility": "Audit d'accessibilité",
493
- "tests.performance": "Performance",
494
- "tests.tests": "Tests",
495
-
496
- // TOC
497
- "toc.title": "Sur cette page",
498
-
499
- // Controls
500
- "controls.title": "Contrôles",
501
-
502
- // Preview
503
- "preview.noPreview": "Aucun aperçu disponible",
504
-
505
- // Accessibility
506
- "a11y.skipToContent": "Aller au contenu",
507
- "a11y.loading": "Chargement...",
508
- "a11y.keyboard": "Interactions clavier",
509
- "a11y.focus": "Gestion du focus",
510
- "a11y.focusVisible": "Focus visible",
511
- "a11y.noFocusVisible": "Pas de focus visible",
512
- "a11y.focusTrapped": "Focus piégé",
513
- "a11y.noFocusTrap": "Pas de piège à focus",
514
- "a11y.screenReader": "Lecteur d'écran",
515
- "a11y.colorMotion": "Couleur et mouvement",
516
- "a11y.text": "Texte",
517
- "a11y.ui": "UI",
518
- "a11y.motionLabel": "Mouvement",
519
- "a11y.noKeyboard": "Ce composant n'a pas d'interactions clavier.",
520
- "a11y.devChecklist": "Liste de vérification développeur",
521
- "a11y.checklistComplete": "Toutes les vérifications sont passées !",
522
- "a11y.checklistCompleteDesc": "Vous avez vérifié toutes les exigences d'accessibilité pour ce composant.",
523
- "a11y.checklistDismiss": "Afficher la liste",
524
- "a11y.element": "Élément",
525
- "a11y.role": "Rôle",
526
- "a11y.key": "Touche",
527
- "a11y.action": "Action",
528
-
529
- // Settings
530
- "settings.title": "Paramètres",
531
- "settings.theme": "Thème",
532
- "settings.light": "Clair",
533
- "settings.dark": "Sombre",
534
- "settings.system": "Système",
535
- "settings.language": "Langue",
536
- },
537
- } as const
538
-
539
- type TranslationKey = keyof typeof translations.en
540
-
541
- type Dictionary = Record<string, string>
542
-
543
- interface I18nContextValue {
544
- locale: Locale
545
- setLocale: (locale: Locale) => void
546
- t: (key: TranslationKey | (string & {})) => string
547
- }
548
-
549
- const I18nContext = createContext<I18nContextValue | null>(null)
550
-
551
- /** Merge the shell's base translations with any extras a registry provides. */
552
- function mergeDicts(
553
- extra?: Record<string, Dictionary>,
554
- ): { en: Dictionary; fr: Dictionary } {
555
- return {
556
- en: { ...translations.en, ...(extra?.en ?? {}) },
557
- fr: { ...translations.fr, ...(extra?.fr ?? {}) },
558
- }
559
- }
560
-
561
- export function I18nProvider({
562
- children,
563
- extraTranslations,
564
- defaultLocale = "en",
565
- availableLocales,
566
- }: {
567
- children: ReactNode
568
- extraTranslations?: Record<string, Dictionary>
569
- /** Initial locale (SSR-safe). Comes from registry config. */
570
- defaultLocale?: Locale
571
- /** Whitelist of valid locales for persistence restore. */
572
- availableLocales?: Locale[]
573
- }) {
574
- // Always start with the default locale on both server and first client render
575
- // so the SSR HTML and the first hydration commit are identical. Reading from
576
- // localStorage in the initializer would cause a hydration mismatch when the
577
- // user has a non-default locale, and React would discard the mismatched
578
- // subtree — which restarts every CSS animation in the page (including the
579
- // bento-enter cards). We pull the persisted locale in a layout effect so the
580
- // locale swap happens before the first paint, with no visible flash.
581
- const [locale, setLocale] = useState<Locale>(defaultLocale)
582
-
583
- useIsomorphicLayoutEffect(() => {
584
- const stored = (typeof window !== "undefined" ? localStorage.getItem("locale") : null)
585
- if (stored && stored !== locale) {
586
- // Accept any persisted locale that's in the registry's whitelist, or
587
- // (backwards-compat) the legacy hard-coded en/fr when no whitelist.
588
- const allow = availableLocales && availableLocales.length > 0
589
- ? availableLocales
590
- : ["en", "fr"]
591
- if (allow.includes(stored)) setLocale(stored)
592
- }
593
- // Run once on mount; `locale` is the value at first commit and never
594
- // changes between renders here, so omitting it is safe.
595
- }, [locale, availableLocales])
596
-
597
- const changeLocale = useCallback((l: Locale) => {
598
- setLocale(l)
599
- localStorage.setItem("locale", l)
600
- }, [])
601
-
602
- const merged = mergeDicts(extraTranslations)
603
-
604
- const t = useCallback(
605
- (key: TranslationKey | (string & {})) => {
606
- const k = key as string
607
- const dict = merged[locale as keyof typeof merged]
608
- return dict?.[k] ?? merged.en[k] ?? k
609
- },
610
- [locale, merged]
611
- )
612
-
613
- return (
614
- <I18nContext.Provider value={{ locale, setLocale: changeLocale, t }}>
615
- {children}
616
- </I18nContext.Provider>
617
- )
618
- }
619
-
620
- export function useLocale() {
621
- const ctx = useContext(I18nContext)
622
- if (!ctx) throw new Error("useLocale must be used within I18nProvider")
623
- return { locale: ctx.locale, setLocale: ctx.setLocale }
624
- }
625
-
626
- export function useTranslations() {
627
- const ctx = useContext(I18nContext)
628
- if (!ctx) throw new Error("useTranslations must be used within I18nProvider")
629
- return ctx.t
630
- }
1
+ "use client"
2
+
3
+ import { createContext, useCallback, useContext, useEffect, useLayoutEffect, useState, type ReactNode } from "react"
4
+
5
+ /**
6
+ * `useLayoutEffect` runs synchronously after DOM commit and *before* paint, so
7
+ * it lets us swap the locale post-hydration without ever flashing the default
8
+ * content. On the server it warns, so we fall back to `useEffect` there — the
9
+ * fallback never actually runs because Next.js doesn't execute effects during
10
+ * SSR, but using it satisfies React's "isomorphic" contract.
11
+ */
12
+ const useIsomorphicLayoutEffect = typeof window !== "undefined" ? useLayoutEffect : useEffect
13
+
14
+ /**
15
+ * Locale code. The shell ships translations for `"en"` and `"fr"` in its
16
+ * base table; registries can add more via `extraTranslations` on their
17
+ * shell config. The type is widened to `string` so arbitrary registry-
18
+ * supplied locales type-check.
19
+ */
20
+ export type Locale = string
21
+
22
+ const translations = {
23
+ en: {
24
+ // Header
25
+ "header.search": "Search...",
26
+
27
+ // Sidebar
28
+ "sidebar.documentation": "Documentation",
29
+ "sidebar.components": "Components",
30
+ "sidebar.blocks": "Blocks",
31
+
32
+ // Search
33
+ "search.placeholder": "Search documentation and components...",
34
+ "search.noResults": "No results found.",
35
+ "search.groupDocs": "Documentation",
36
+ "search.groupComponents": "Components",
37
+
38
+ // Home
39
+ "home.title": "UI Registry",
40
+ "home.description": "A custom component registry built with shadcn. Browse documentation and components to get started.",
41
+ "home.getStarted": "Get Started",
42
+ "home.browseComponents": "Browse Components",
43
+ "home.openSource": "Open Source",
44
+ "home.builtOnShadcn": "Built on shadcn",
45
+ "home.heroTitle": "Scintillar UI",
46
+ "home.heroDescription": "Web components built for live collaboration with React & Next.",
47
+ "home.heroFeature.collaborative": "Collaborative",
48
+ "home.heroFeature.tested": "Tested",
49
+ "home.heroFeature.accessible": "Accessible",
50
+ "home.prototypingTitle": "Your code. Your repo. Our standards.",
51
+ "home.prototypingDescription": "Stop maintaining the technical scaffolding. Get an audited component registry that drops natively into your workflow and your tests.",
52
+ "home.proto.openSource.title": "You own the code",
53
+ "home.proto.openSource.desc": "Components ship straight into your codebase. Pull from the registry, edit them as you like — or let us handle the heavy lifting.",
54
+ "home.proto.openSource.label.registry": "Registry",
55
+ "home.proto.openSource.label.yours": "your codebase",
56
+ "home.proto.tested.title": "Heavily tested",
57
+ "home.proto.tested.desc": "Every component is audited for accessibility, interactivity and stability. Real test suites, not promises.",
58
+ "home.proto.tested.suite": "Test suite",
59
+ "home.proto.tested.passed": "passed",
60
+ "home.proto.tested.case.renders": "renders the trigger",
61
+ "home.proto.tested.case.opens": "opens on click",
62
+ "home.proto.tested.case.closes": "closes on Escape",
63
+ "home.proto.tested.case.focus": "traps focus when open",
64
+ "home.proto.tested.case.keyboard": "supports keyboard nav",
65
+ "home.proto.tested.case.motion": "respects reduced motion",
66
+ "home.proto.tested.case.axe": "passes axe scan",
67
+ "home.proto.ai.title": "Built for AI agents",
68
+ "home.proto.ai.desc": "We obsess over AIX (AI Experience) the same way the rest of the world obsesses over DevEX. Predictable APIs, machine-readable docs, structured props, and registries that LLMs can navigate.",
69
+ "home.proto.ds.title": "Scintillar Design System",
70
+ "home.proto.ds.desc": "Documents complex realtime workflows, recommends a database & infra stack, and ships a one-line installer for a working collaborative starter — most of the hard problems already solved.",
71
+ "home.proto.ds.cta": "Learn more",
72
+ "home.proto.ds.glow": "Premium",
73
+ "home.dashboardTitle": "Real apps, not toys",
74
+ "home.dashboardDescription": "A complete analytics dashboard — stat cards, sortable tables, charts, activity feed — built entirely with Scintillar components. Drop it into your codebase and own every line.",
75
+ "home.dash.cardTitle": "Analytics Overview",
76
+ "home.dash.range": "Last 7 days",
77
+ "home.dash.tab.overview": "Overview",
78
+ "home.dash.tab.revenue": "Revenue",
79
+ "home.dash.tab.users": "Users",
80
+ "home.dash.chartTitle": "Revenue Over Time",
81
+ "home.dash.activityTitle": "Recent Activity",
82
+ "home.dash.tableTitle": "Recent Transactions",
83
+ "home.dash.col.name": "Name",
84
+ "home.dash.col.email": "Email",
85
+ "home.dash.col.status": "Status",
86
+ "home.dash.col.amount": "Amount",
87
+ "home.socialProofTitle": "What developers are saying",
88
+ "home.installHint": "npx shadcn@latest add",
89
+ "home.showcaseTitle": "Built for real apps",
90
+ "home.showcaseDescription": "Real-world patterns from authentication to collaboration, ready to drop into your project.",
91
+ "home.featuresTitle": "Why Scintillar?",
92
+ "home.featureA11yTitle": "Accessible by Default",
93
+ "home.featureA11yDesc": "WCAG 2.1 AA audited. Keyboard navigation, ARIA attributes, and screen reader support out of the box.",
94
+ "home.featurePreviewTitle": "Interactive Previews",
95
+ "home.featurePreviewDesc": "Zoom, pan, and drag on a live canvas. Tweak props in real time and see exactly how components behave.",
96
+ "home.featureCopyTitle": "Copy & Paste",
97
+ "home.featureCopyDesc": "No black-box dependencies. Copy the source into your project and own it completely.",
98
+ "home.featureTypedTitle": "Fully Typed & Tested",
99
+ "home.featureTypedDesc": "Strict TypeScript, 145+ unit tests, E2E coverage, visual regression, and automated accessibility audits.",
100
+ "home.statsComponents": "Components & Blocks",
101
+ "home.statsOpenSource": "Open-Source",
102
+ "home.statsCollaborative": "Collaborative",
103
+ "home.statsAccessible": "Accessible",
104
+ "home.statsLocalized": "Localized",
105
+ "home.statsCoverage": "Test Coverage",
106
+ "home.statsYoursToKeep": "Yours to Keep",
107
+ "home.codeTitle": "Get started in seconds",
108
+ "home.codeDescription": "Add any component from the registry to your project. You own the code.",
109
+ "home.codeCopied": "Copied!",
110
+ "home.ctaTitle": "Ready to build?",
111
+ "home.ctaDescription": "Start adding components to your project today.",
112
+ "home.viewOnGithub": "View on GitHub",
113
+ "footer.components": "Components",
114
+ "footer.docs": "Documentation",
115
+ "footer.gettingStarted": "Getting Started",
116
+ "footer.accessibility": "Accessibility",
117
+ "footer.community": "Community",
118
+ "footer.legal": "Legal",
119
+ "footer.privacy": "Privacy Policy",
120
+ "footer.terms": "Terms of Service",
121
+ "footer.faq": "FAQ",
122
+ "footer.contact": "Contact",
123
+ "footer.copyright": "Scintillar. All rights reserved.",
124
+ "home.uiClientTitle": "Interested by the shell, not the components?",
125
+ "home.uiClientDescription": "Use @sntlr/ui-client to scaffold a complete component documentation site with interactive previews, accessibility audits, and more.",
126
+ "home.uiClientFeature1Title": "Interactive Preview",
127
+ "home.uiClientFeature1Desc": "Zoom, pan, and drag on a live component canvas.",
128
+ "home.uiClientFeature2Title": "Auto-generated Docs",
129
+ "home.uiClientFeature2Desc": "Props, accessibility, and test documentation out of the box.",
130
+ "home.uiClientFeature3Title": "Accessible & i18n",
131
+ "home.uiClientFeature3Desc": "WCAG 2.1 AA compliant with built-in language support.",
132
+ "home.uiClientLearnMore": "Learn more",
133
+
134
+ // Hero — Chart
135
+ "hero.chart.title": "Weekly Revenue",
136
+ "hero.chart.range": "7d",
137
+ "hero.chart.day.mon": "Mon",
138
+ "hero.chart.day.tue": "Tue",
139
+ "hero.chart.day.wed": "Wed",
140
+ "hero.chart.day.thu": "Thu",
141
+ "hero.chart.day.fri": "Fri",
142
+ "hero.chart.day.sat": "Sat",
143
+ "hero.chart.day.sun": "Sun",
144
+
145
+ // Hero — Notifications
146
+ "hero.notifications.title": "Notifications",
147
+ "hero.notifications.markAllRead": "Mark all as read",
148
+ "hero.notifications.settings": "Notification settings",
149
+ "hero.notifications.settingsAria": "Settings",
150
+ "hero.notifications.searchPlaceholder": "Search...",
151
+ "hero.notifications.dismiss": "Dismiss notification",
152
+ "hero.notifications.markRead": "Mark as read",
153
+ "hero.notifications.markUnread": "Mark as unread",
154
+ "hero.notifications.delete": "Delete",
155
+ "hero.notifications.empty": "No notifications",
156
+
157
+ // Hero — Cursors
158
+ "hero.cursors.title": "Live Cursors",
159
+ "hero.cursors.badge": "collaborative",
160
+ "hero.cursors.you": "You",
161
+ "hero.display.group": "Label style",
162
+ "hero.display.name": "Show names",
163
+ "hero.display.avatar": "Show avatars",
164
+
165
+ // Hero — Data Table
166
+ "hero.dataTable.searchPlaceholder": "Search invoices...",
167
+ "hero.dataTable.col.invoice": "Invoice",
168
+ "hero.dataTable.col.status": "Status",
169
+ "hero.dataTable.col.method": "Method",
170
+ "hero.dataTable.col.amount": "Amount",
171
+ "hero.dataTable.status.paid": "Paid",
172
+ "hero.dataTable.status.pending": "Pending",
173
+ "hero.dataTable.status.overdue": "Overdue",
174
+ "hero.dataTable.method.card": "Card",
175
+ "hero.dataTable.method.paypal": "PayPal",
176
+ "hero.dataTable.method.bank": "Bank",
177
+ "hero.dataTable.filter.status": "Status",
178
+ "hero.dataTable.filter.method": "Method",
179
+ "hero.dataTable.agg.sum": "Sum",
180
+ "hero.dataTable.agg.avg": "Avg",
181
+ "hero.dataTable.agg.max": "Max",
182
+ "hero.dataTable.columns": "Columns",
183
+ "hero.dataTable.rowsPerPage": "Rows per page",
184
+
185
+ // Hero — Profile
186
+ "hero.profile.defaultName": "Jane Doe",
187
+ "hero.profile.defaultBio": "Product designer at Scintillar. Building beautiful and accessible UIs.",
188
+ "hero.profile.title": "Profile",
189
+ "hero.profile.description": "Manage your public profile information.",
190
+ "hero.profile.cancel": "Cancel",
191
+ "hero.profile.save": "Save",
192
+ "hero.profile.uploadHint": "Click to upload",
193
+ "hero.profile.avatarAlt": "Avatar",
194
+ "hero.profile.nameLabel": "Name",
195
+ "hero.profile.namePlaceholder": "Your name",
196
+ "hero.profile.emailLabel": "Email",
197
+ "hero.profile.bioLabel": "Bio",
198
+ "hero.profile.bioPlaceholder": "Tell us about yourself",
199
+
200
+ // Hero — WYSIWYG (toolbar aria + author name)
201
+ "hero.wysiwyg.aria.bold": "Bold",
202
+ "hero.wysiwyg.aria.italic": "Italic",
203
+ "hero.wysiwyg.aria.underline": "Underline",
204
+ "hero.wysiwyg.aria.heading": "Heading",
205
+ "hero.wysiwyg.aria.list": "List",
206
+ "hero.wysiwyg.aria.quote": "Quote",
207
+ "hero.wysiwyg.aria.alignLeft": "Align left",
208
+ "hero.wysiwyg.aria.alignCenter": "Align center",
209
+ "hero.wysiwyg.aria.alignRight": "Align right",
210
+ "hero.wysiwyg.author": "Edith",
211
+
212
+ // Component page
213
+ "component.subtitle": "Install this component from the Scintillar registry.",
214
+ "component.installation": "Installation",
215
+ "component.source": "Source",
216
+ "component.propsBehavior": "Props & Behavior",
217
+ "component.docsPlaceholder": "Documentation for this component has not been written yet.",
218
+ "component.guidelinesPlaceholder": "Usage guidelines for this component have not been written yet.",
219
+ "component.a11yPlaceholder": "Accessibility documentation for this component has not been written yet.",
220
+
221
+ // Tabs
222
+ "tabs.install": "Install",
223
+ "tabs.documentation": "Documentation",
224
+ "tabs.guidelines": "Guidelines",
225
+ "tabs.accessibility": "Accessibility",
226
+ "tabs.tests": "Tests",
227
+ "component.testsPlaceholder": "No functional tests have been written for this component yet.",
228
+ "tests.health": "Health",
229
+ "tests.propsDocs": "Props documented",
230
+ "tests.a11yDocs": "Accessibility documented",
231
+ "tests.preview": "Preview available",
232
+ "tests.unit": "Unit Tests",
233
+ "tests.interaction": "Interaction Tests",
234
+ "tests.visual": "Visual Regression",
235
+ "tests.accessibility": "Accessibility Audit",
236
+ "tests.performance": "Performance",
237
+ "tests.tests": "Tests",
238
+
239
+ // TOC
240
+ "toc.title": "On this page",
241
+
242
+ // Controls
243
+ "controls.title": "Controls",
244
+
245
+ // Preview
246
+ "preview.noPreview": "No preview available",
247
+
248
+ // Accessibility
249
+ "a11y.skipToContent": "Skip to content",
250
+ "a11y.loading": "Loading...",
251
+ "a11y.keyboard": "Keyboard Interactions",
252
+ "a11y.focus": "Focus Management",
253
+ "a11y.focusVisible": "Focus visible",
254
+ "a11y.noFocusVisible": "No focus visible",
255
+ "a11y.focusTrapped": "Focus trapped",
256
+ "a11y.noFocusTrap": "No focus trap",
257
+ "a11y.screenReader": "Screen Reader",
258
+ "a11y.colorMotion": "Color & Motion",
259
+ "a11y.text": "Text",
260
+ "a11y.ui": "UI",
261
+ "a11y.motionLabel": "Motion",
262
+ "a11y.noKeyboard": "This component has no keyboard interactions.",
263
+ "a11y.devChecklist": "Developer Checklist",
264
+ "a11y.checklistComplete": "All checks passed!",
265
+ "a11y.checklistCompleteDesc": "You've verified all accessibility requirements for this component.",
266
+ "a11y.checklistDismiss": "Show checklist",
267
+ "a11y.element": "Element",
268
+ "a11y.role": "Role",
269
+ "a11y.key": "Key",
270
+ "a11y.action": "Action",
271
+
272
+ // Settings
273
+ "settings.title": "Settings",
274
+ "settings.theme": "Theme",
275
+ "settings.light": "Light",
276
+ "settings.dark": "Dark",
277
+ "settings.system": "System",
278
+ "settings.language": "Language",
279
+ },
280
+ fr: {
281
+ // Header
282
+ "header.search": "Rechercher...",
283
+
284
+ // Sidebar
285
+ "sidebar.documentation": "Documentation",
286
+ "sidebar.components": "Composants",
287
+ "sidebar.blocks": "Blocs",
288
+
289
+ // Search
290
+ "search.placeholder": "Rechercher dans la documentation et les composants...",
291
+ "search.noResults": "Aucun résultat trouvé.",
292
+ "search.groupDocs": "Documentation",
293
+ "search.groupComponents": "Composants",
294
+
295
+ // Home
296
+ "home.title": "UI Registry",
297
+ "home.description": "Un registre de composants personnalisé construit avec shadcn. Parcourez la documentation et les composants pour commencer.",
298
+ "home.getStarted": "Commencer",
299
+ "home.browseComponents": "Parcourir les composants",
300
+ "home.openSource": "Open Source",
301
+ "home.builtOnShadcn": "Basé sur shadcn",
302
+ "home.heroTitle": "Scintillar UI",
303
+ "home.heroDescription": "Composants web pensés pour la collaboration en direct pour React & Next.",
304
+ "home.heroFeature.collaborative": "Collaboratifs",
305
+ "home.heroFeature.tested": "Testés",
306
+ "home.heroFeature.accessible": "Accessibles",
307
+ "home.prototypingTitle": "Votre code. Votre dépôt. Nos standards.",
308
+ "home.prototypingDescription": "Libérez-vous de la maintenance du cadre technique. Profitez d'un registre de composants audités qui s'intègrent nativement dans votre workflow et vos tests.",
309
+ "home.proto.openSource.title": "Le code vous appartient",
310
+ "home.proto.openSource.desc": "Les composants atterrissent directement dans votre dépôt. Récupérez-les depuis le registre, modifiez-les comme vous voulez — ou laissez-nous faire le gros du travail.",
311
+ "home.proto.openSource.label.registry": "Registre",
312
+ "home.proto.openSource.label.yours": "votre dépôt",
313
+ "home.proto.tested.title": "Hautement testés",
314
+ "home.proto.tested.desc": "Chaque composant est audité pour l'accessibilité, l'interactivité et la stabilité. De vrais tests, pas des promesses.",
315
+ "home.proto.tested.suite": "Suite de tests",
316
+ "home.proto.tested.passed": "réussis",
317
+ "home.proto.tested.case.renders": "rendu du trigger",
318
+ "home.proto.tested.case.opens": "ouverture au clic",
319
+ "home.proto.tested.case.closes": "fermeture avec Échap",
320
+ "home.proto.tested.case.focus": "piège le focus à l'ouverture",
321
+ "home.proto.tested.case.keyboard": "navigation au clavier",
322
+ "home.proto.tested.case.motion": "respect du prefers-reduced-motion",
323
+ "home.proto.tested.case.axe": "audit axe réussi",
324
+ "home.proto.ai.title": "Conçus pour les agents IA",
325
+ "home.proto.ai.desc": "Nous nous obsédons pour l'AIX (AI Experience) autant que le reste du monde s'obsède pour la DevEX. APIs prévisibles, docs lisibles par machine, props structurées, et un registre que les LLM peuvent naviguer.",
326
+ "home.proto.ds.title": "Scintillar Design System",
327
+ "home.proto.ds.desc": "Documente les workflows temps-réel complexes, recommande une stack base de données & infra, et installe en une ligne un starter collaboratif fonctionnel — la plupart des problèmes durs déjà résolus.",
328
+ "home.proto.ds.cta": "En savoir plus",
329
+ "home.proto.ds.glow": "Premium",
330
+ "home.dashboardTitle": "De vraies applis, pas des jouets",
331
+ "home.dashboardDescription": "Un tableau de bord analytique complet — cartes de stats, tableaux triables, graphiques, fil d'activité — construit entièrement avec les composants Scintillar. Intégrez-le à votre dépôt et appropriez-vous chaque ligne.",
332
+ "home.dash.cardTitle": "Vue d'ensemble analytique",
333
+ "home.dash.range": "7 derniers jours",
334
+ "home.dash.tab.overview": "Vue d'ensemble",
335
+ "home.dash.tab.revenue": "Revenus",
336
+ "home.dash.tab.users": "Utilisateurs",
337
+ "home.dash.chartTitle": "Évolution des revenus",
338
+ "home.dash.activityTitle": "Activité récente",
339
+ "home.dash.tableTitle": "Transactions récentes",
340
+ "home.dash.col.name": "Nom",
341
+ "home.dash.col.email": "Courriel",
342
+ "home.dash.col.status": "Statut",
343
+ "home.dash.col.amount": "Montant",
344
+ "home.socialProofTitle": "Ce que les développeurs en disent",
345
+ "home.installHint": "npx shadcn@latest add",
346
+ "home.showcaseTitle": "Conçu pour de vrais projets",
347
+ "home.showcaseDescription": "Des scénarios concrets, de l'authentification à la collaboration, prêts à intégrer dans votre projet.",
348
+ "home.featuresTitle": "Pourquoi Scintillar ?",
349
+ "home.featureA11yTitle": "Accessible par défaut",
350
+ "home.featureA11yDesc": "Audité WCAG 2.1 AA. Navigation clavier, attributs ARIA et support lecteur d'écran inclus.",
351
+ "home.featurePreviewTitle": "Aperçus interactifs",
352
+ "home.featurePreviewDesc": "Zoomez, glissez et manipulez sur un canevas interactif. Modifiez les props en temps réel.",
353
+ "home.featureCopyTitle": "Copier-coller",
354
+ "home.featureCopyDesc": "Pas de dépendance opaque. Copiez le code dans votre projet et appropriez-vous-le.",
355
+ "home.featureTypedTitle": "Typé et testé",
356
+ "home.featureTypedDesc": "TypeScript strict, plus de 145 tests unitaires, couverture E2E, régression visuelle et audits d'accessibilité automatisés.",
357
+ "home.statsComponents": "Composants et blocs",
358
+ "home.statsOpenSource": "Open-source",
359
+ "home.statsCollaborative": "Collaboratif",
360
+ "home.statsAccessible": "Accessible",
361
+ "home.statsLocalized": "Localisé",
362
+ "home.statsCoverage": "Couverture de tests",
363
+ "home.statsYoursToKeep": "Le code vous appartient",
364
+ "home.codeTitle": "Démarrez en quelques secondes",
365
+ "home.codeDescription": "Ajoutez n'importe quel composant du registre à votre projet. Le code vous appartient.",
366
+ "home.codeCopied": "Copié !",
367
+ "home.ctaTitle": "Prêt à construire ?",
368
+ "home.ctaDescription": "Commencez à ajouter des composants à votre projet dès aujourd'hui.",
369
+ "home.viewOnGithub": "Voir sur GitHub",
370
+ "footer.components": "Composants",
371
+ "footer.docs": "Documentation",
372
+ "footer.gettingStarted": "Commencer",
373
+ "footer.accessibility": "Accessibilité",
374
+ "footer.community": "Communauté",
375
+ "footer.legal": "Légal",
376
+ "footer.privacy": "Politique de confidentialité",
377
+ "footer.terms": "Conditions d'utilisation",
378
+ "footer.faq": "FAQ",
379
+ "footer.contact": "Contact",
380
+ "footer.copyright": "Scintillar. Tous droits réservés.",
381
+ "home.uiClientTitle": "Intéressé par le shell, pas les composants ?",
382
+ "home.uiClientDescription": "Utilisez @sntlr/ui-client pour générer un site de documentation de composants complet avec aperçus interactifs, audits d'accessibilité et plus encore.",
383
+ "home.uiClientFeature1Title": "Aperçu interactif",
384
+ "home.uiClientFeature1Desc": "Zoomez, glissez et manipulez sur un canevas de composants.",
385
+ "home.uiClientFeature2Title": "Docs auto-générées",
386
+ "home.uiClientFeature2Desc": "Documentation des props, de l'accessibilité et des tests incluse.",
387
+ "home.uiClientFeature3Title": "Accessible et i18n",
388
+ "home.uiClientFeature3Desc": "Conforme WCAG 2.1 AA avec support multilingue intégré.",
389
+ "home.uiClientLearnMore": "En savoir plus",
390
+
391
+ // Hero — Chart
392
+ "hero.chart.title": "Revenus hebdomadaires",
393
+ "hero.chart.range": "7 j",
394
+ "hero.chart.day.mon": "Lun",
395
+ "hero.chart.day.tue": "Mar",
396
+ "hero.chart.day.wed": "Mer",
397
+ "hero.chart.day.thu": "Jeu",
398
+ "hero.chart.day.fri": "Ven",
399
+ "hero.chart.day.sat": "Sam",
400
+ "hero.chart.day.sun": "Dim",
401
+
402
+ // Hero — Notifications
403
+ "hero.notifications.title": "Notifications",
404
+ "hero.notifications.markAllRead": "Tout marquer comme lu",
405
+ "hero.notifications.settings": "Paramètres des notifications",
406
+ "hero.notifications.settingsAria": "Paramètres",
407
+ "hero.notifications.searchPlaceholder": "Rechercher...",
408
+ "hero.notifications.dismiss": "Ignorer la notification",
409
+ "hero.notifications.markRead": "Marquer comme lu",
410
+ "hero.notifications.markUnread": "Marquer comme non lu",
411
+ "hero.notifications.delete": "Supprimer",
412
+ "hero.notifications.empty": "Aucune notification",
413
+
414
+ // Hero — Cursors
415
+ "hero.cursors.title": "Curseurs en direct",
416
+ "hero.cursors.badge": "collaboratif",
417
+ "hero.cursors.you": "Vous",
418
+ "hero.display.group": "Style d'étiquette",
419
+ "hero.display.name": "Afficher les noms",
420
+ "hero.display.avatar": "Afficher les avatars",
421
+
422
+ // Hero — Data Table
423
+ "hero.dataTable.searchPlaceholder": "Rechercher des factures...",
424
+ "hero.dataTable.col.invoice": "Facture",
425
+ "hero.dataTable.col.status": "Statut",
426
+ "hero.dataTable.col.method": "Méthode",
427
+ "hero.dataTable.col.amount": "Montant",
428
+ "hero.dataTable.status.paid": "Payée",
429
+ "hero.dataTable.status.pending": "En attente",
430
+ "hero.dataTable.status.overdue": "En retard",
431
+ "hero.dataTable.method.card": "Carte",
432
+ "hero.dataTable.method.paypal": "PayPal",
433
+ "hero.dataTable.method.bank": "Virement",
434
+ "hero.dataTable.filter.status": "Statut",
435
+ "hero.dataTable.filter.method": "Méthode",
436
+ "hero.dataTable.agg.sum": "Somme",
437
+ "hero.dataTable.agg.avg": "Moy.",
438
+ "hero.dataTable.agg.max": "Max",
439
+ "hero.dataTable.columns": "Colonnes",
440
+ "hero.dataTable.rowsPerPage": "Lignes par page",
441
+
442
+ // Hero — Profile
443
+ "hero.profile.defaultName": "Jeanne Dupont",
444
+ "hero.profile.defaultBio": "Designer produit chez Scintillar. Conçoit des interfaces belles et accessibles.",
445
+ "hero.profile.title": "Profil",
446
+ "hero.profile.description": "Gérez vos informations de profil public.",
447
+ "hero.profile.cancel": "Annuler",
448
+ "hero.profile.save": "Enregistrer",
449
+ "hero.profile.uploadHint": "Cliquez pour téléverser",
450
+ "hero.profile.avatarAlt": "Avatar",
451
+ "hero.profile.nameLabel": "Nom",
452
+ "hero.profile.namePlaceholder": "Votre nom",
453
+ "hero.profile.emailLabel": "Courriel",
454
+ "hero.profile.bioLabel": "Bio",
455
+ "hero.profile.bioPlaceholder": "Parlez-nous de vous",
456
+
457
+ // Hero — WYSIWYG (toolbar aria + author name)
458
+ "hero.wysiwyg.aria.bold": "Gras",
459
+ "hero.wysiwyg.aria.italic": "Italique",
460
+ "hero.wysiwyg.aria.underline": "Souligné",
461
+ "hero.wysiwyg.aria.heading": "Titre",
462
+ "hero.wysiwyg.aria.list": "Liste",
463
+ "hero.wysiwyg.aria.quote": "Citation",
464
+ "hero.wysiwyg.aria.alignLeft": "Aligner à gauche",
465
+ "hero.wysiwyg.aria.alignCenter": "Centrer",
466
+ "hero.wysiwyg.aria.alignRight": "Aligner à droite",
467
+ "hero.wysiwyg.author": "Édith",
468
+
469
+ // Component page
470
+ "component.subtitle": "Installez ce composant depuis le registre Scintillar.",
471
+ "component.installation": "Installation",
472
+ "component.source": "Source",
473
+ "component.propsBehavior": "Props et comportement",
474
+ "component.docsPlaceholder": "La documentation de ce composant n'a pas encore été rédigée.",
475
+ "component.guidelinesPlaceholder": "Les directives d'utilisation de ce composant n'ont pas encore été rédigées.",
476
+ "component.a11yPlaceholder": "La documentation d'accessibilité de ce composant n'a pas encore été rédigée.",
477
+
478
+ // Tabs
479
+ "tabs.install": "Installer",
480
+ "tabs.documentation": "Documentation",
481
+ "tabs.guidelines": "Directives",
482
+ "tabs.accessibility": "Accessibilité",
483
+ "tabs.tests": "Tests",
484
+ "component.testsPlaceholder": "Aucun test fonctionnel n'a été écrit pour ce composant.",
485
+ "tests.health": "Santé",
486
+ "tests.propsDocs": "Props documentés",
487
+ "tests.a11yDocs": "Accessibilité documentée",
488
+ "tests.preview": "Aperçu disponible",
489
+ "tests.unit": "Tests unitaires",
490
+ "tests.interaction": "Tests d'interaction",
491
+ "tests.visual": "Régression visuelle",
492
+ "tests.accessibility": "Audit d'accessibilité",
493
+ "tests.performance": "Performance",
494
+ "tests.tests": "Tests",
495
+
496
+ // TOC
497
+ "toc.title": "Sur cette page",
498
+
499
+ // Controls
500
+ "controls.title": "Contrôles",
501
+
502
+ // Preview
503
+ "preview.noPreview": "Aucun aperçu disponible",
504
+
505
+ // Accessibility
506
+ "a11y.skipToContent": "Aller au contenu",
507
+ "a11y.loading": "Chargement...",
508
+ "a11y.keyboard": "Interactions clavier",
509
+ "a11y.focus": "Gestion du focus",
510
+ "a11y.focusVisible": "Focus visible",
511
+ "a11y.noFocusVisible": "Pas de focus visible",
512
+ "a11y.focusTrapped": "Focus piégé",
513
+ "a11y.noFocusTrap": "Pas de piège à focus",
514
+ "a11y.screenReader": "Lecteur d'écran",
515
+ "a11y.colorMotion": "Couleur et mouvement",
516
+ "a11y.text": "Texte",
517
+ "a11y.ui": "UI",
518
+ "a11y.motionLabel": "Mouvement",
519
+ "a11y.noKeyboard": "Ce composant n'a pas d'interactions clavier.",
520
+ "a11y.devChecklist": "Liste de vérification développeur",
521
+ "a11y.checklistComplete": "Toutes les vérifications sont passées !",
522
+ "a11y.checklistCompleteDesc": "Vous avez vérifié toutes les exigences d'accessibilité pour ce composant.",
523
+ "a11y.checklistDismiss": "Afficher la liste",
524
+ "a11y.element": "Élément",
525
+ "a11y.role": "Rôle",
526
+ "a11y.key": "Touche",
527
+ "a11y.action": "Action",
528
+
529
+ // Settings
530
+ "settings.title": "Paramètres",
531
+ "settings.theme": "Thème",
532
+ "settings.light": "Clair",
533
+ "settings.dark": "Sombre",
534
+ "settings.system": "Système",
535
+ "settings.language": "Langue",
536
+ },
537
+ } as const
538
+
539
+ type TranslationKey = keyof typeof translations.en
540
+
541
+ type Dictionary = Record<string, string>
542
+
543
+ interface I18nContextValue {
544
+ locale: Locale
545
+ setLocale: (locale: Locale) => void
546
+ t: (key: TranslationKey | (string & {})) => string
547
+ }
548
+
549
+ const I18nContext = createContext<I18nContextValue | null>(null)
550
+
551
+ /** Merge the shell's base translations with any extras a registry provides. */
552
+ function mergeDicts(
553
+ extra?: Record<string, Dictionary>,
554
+ ): { en: Dictionary; fr: Dictionary } {
555
+ return {
556
+ en: { ...translations.en, ...(extra?.en ?? {}) },
557
+ fr: { ...translations.fr, ...(extra?.fr ?? {}) },
558
+ }
559
+ }
560
+
561
+ export function I18nProvider({
562
+ children,
563
+ extraTranslations,
564
+ defaultLocale = "en",
565
+ availableLocales,
566
+ }: {
567
+ children: ReactNode
568
+ extraTranslations?: Record<string, Dictionary>
569
+ /** Initial locale (SSR-safe). Comes from registry config. */
570
+ defaultLocale?: Locale
571
+ /** Whitelist of valid locales for persistence restore. */
572
+ availableLocales?: Locale[]
573
+ }) {
574
+ // Always start with the default locale on both server and first client render
575
+ // so the SSR HTML and the first hydration commit are identical. Reading from
576
+ // localStorage in the initializer would cause a hydration mismatch when the
577
+ // user has a non-default locale, and React would discard the mismatched
578
+ // subtree — which restarts every CSS animation in the page (including the
579
+ // bento-enter cards). We pull the persisted locale in a layout effect so the
580
+ // locale swap happens before the first paint, with no visible flash.
581
+ const [locale, setLocale] = useState<Locale>(defaultLocale)
582
+
583
+ useIsomorphicLayoutEffect(() => {
584
+ const stored = (typeof window !== "undefined" ? localStorage.getItem("locale") : null)
585
+ if (stored && stored !== locale) {
586
+ // Accept any persisted locale that's in the registry's whitelist, or
587
+ // (backwards-compat) the legacy hard-coded en/fr when no whitelist.
588
+ const allow = availableLocales && availableLocales.length > 0
589
+ ? availableLocales
590
+ : ["en", "fr"]
591
+ if (allow.includes(stored)) setLocale(stored)
592
+ }
593
+ // Run once on mount; `locale` is the value at first commit and never
594
+ // changes between renders here, so omitting it is safe.
595
+ }, [locale, availableLocales])
596
+
597
+ const changeLocale = useCallback((l: Locale) => {
598
+ setLocale(l)
599
+ localStorage.setItem("locale", l)
600
+ }, [])
601
+
602
+ const merged = mergeDicts(extraTranslations)
603
+
604
+ const t = useCallback(
605
+ (key: TranslationKey | (string & {})) => {
606
+ const k = key as string
607
+ const dict = merged[locale as keyof typeof merged]
608
+ return dict?.[k] ?? merged.en[k] ?? k
609
+ },
610
+ [locale, merged]
611
+ )
612
+
613
+ return (
614
+ <I18nContext.Provider value={{ locale, setLocale: changeLocale, t }}>
615
+ {children}
616
+ </I18nContext.Provider>
617
+ )
618
+ }
619
+
620
+ export function useLocale() {
621
+ const ctx = useContext(I18nContext)
622
+ if (!ctx) throw new Error("useLocale must be used within I18nProvider")
623
+ return { locale: ctx.locale, setLocale: ctx.setLocale }
624
+ }
625
+
626
+ export function useTranslations() {
627
+ const ctx = useContext(I18nContext)
628
+ if (!ctx) throw new Error("useTranslations must be used within I18nProvider")
629
+ return ctx.t
630
+ }