xertica-ui 2.1.11 → 2.2.1
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.
- package/CHANGELOG.md +24 -3
- package/README.md +1 -1
- package/bin/cli.ts +7 -4
- package/bin/language-config.ts +79 -19
- package/components/pages/template-content/TemplateContent.tsx +204 -218
- package/dist/ThemeContext-BblcjQup.cjs +1855 -0
- package/dist/ThemeContext-BgclCB35.js +1856 -0
- package/dist/ThemeContext-Bo-W2WZH.js +1856 -0
- package/dist/ThemeContext-CP3a0jxy.cjs +1855 -0
- package/dist/ThemeContext-DQUOeziy.cjs +1855 -0
- package/dist/ThemeContext-GeEBTJ3q.cjs +1621 -0
- package/dist/ThemeContext-JyLK9B1o.js +1622 -0
- package/dist/ThemeContext-ept8jhXI.js +1856 -0
- package/dist/VerifyEmailPage-BIBOKV7Z.js +3214 -0
- package/dist/VerifyEmailPage-BiRm7Nh4.cjs +3213 -0
- package/dist/VerifyEmailPage-Bvfv8HVQ.js +3214 -0
- package/dist/VerifyEmailPage-D-FRj5TU.cjs +3213 -0
- package/dist/VerifyEmailPage-RrUApqBN.js +3214 -0
- package/dist/VerifyEmailPage-VoMI7MYH.cjs +3213 -0
- package/dist/VerifyEmailPage-hdB8JQGv.cjs +3213 -0
- package/dist/VerifyEmailPage-vYHbYK3q.js +3214 -0
- package/dist/XerticaProvider-AChwphCO.cjs +48 -0
- package/dist/XerticaProvider-AbWlr7Af.cjs +48 -0
- package/dist/XerticaProvider-BSyFrmC0.js +49 -0
- package/dist/XerticaProvider-CUYJZc32.js +49 -0
- package/dist/XerticaProvider-CWs6EwNa.js +49 -0
- package/dist/XerticaProvider-CiNKjMx1.cjs +48 -0
- package/dist/XerticaProvider-D5lLumH-.js +49 -0
- package/dist/XerticaProvider-qQUDop71.cjs +48 -0
- package/dist/XerticaXLogo-8TTzBjHw.cjs +251 -0
- package/dist/XerticaXLogo-B2svDGZh.cjs +251 -0
- package/dist/XerticaXLogo-BWaag64t.js +252 -0
- package/dist/XerticaXLogo-CFuIlYFH.js +252 -0
- package/dist/XerticaXLogo-ChryA6xj.js +252 -0
- package/dist/XerticaXLogo-CowGv7BC.js +252 -0
- package/dist/XerticaXLogo-DTee_y8X.cjs +251 -0
- package/dist/XerticaXLogo-kslQ8Tk_.cjs +251 -0
- package/dist/brand.cjs.js +2 -2
- package/dist/brand.es.js +2 -2
- package/dist/cli.js +54 -12
- package/dist/hooks.cjs.js +1 -1
- package/dist/hooks.es.js +1 -1
- package/dist/i18n.d.ts +2 -1
- package/dist/index.cjs.js +5 -5
- package/dist/index.es.js +5 -5
- package/dist/layout.cjs.js +1 -1
- package/dist/layout.es.js +1 -1
- package/dist/pages.cjs.js +1 -1
- package/dist/pages.es.js +1 -1
- package/dist/sidebar-B3EYhli0.cjs +800 -0
- package/dist/sidebar-B9NR0lCe.cjs +800 -0
- package/dist/sidebar-BvF5I2Ue.cjs +800 -0
- package/dist/sidebar-CRMiBtAi.js +801 -0
- package/dist/sidebar-CZ2mWaMM.cjs +800 -0
- package/dist/sidebar-CplprZpM.js +801 -0
- package/dist/sidebar-KIS0C2JH.js +801 -0
- package/dist/sidebar-OTO_up7Z.js +801 -0
- package/dist/xertica-ui.css +1 -1
- package/docs/architecture-improvements.md +11 -31
- package/docs/architecture.md +15 -4
- package/docs/i18n.md +112 -64
- package/docs/llms.md +1 -1
- package/guidelines/Guidelines.md +15 -8
- package/llms-compact.txt +1 -1
- package/llms-full.txt +5 -2
- package/llms.txt +1 -1
- package/package.json +1 -1
- package/templates/CLAUDE.md +165 -180
- package/templates/guidelines/Guidelines.md +17 -7
- package/templates/package.json +2 -2
- package/templates/src/app/App.tsx +1 -1
- package/templates/src/features/auth/ui/ForgotPasswordContent.tsx +9 -7
- package/templates/src/features/auth/ui/LoginContent.tsx +10 -8
- package/templates/src/features/auth/ui/ResetPasswordContent.tsx +179 -177
- package/templates/src/features/auth/ui/SocialLoginButtons.tsx +9 -4
- package/templates/src/features/auth/ui/VerifyEmailContent.tsx +84 -82
- package/templates/src/features/template/ui/CrudTemplate.tsx +115 -100
- package/templates/src/features/template/ui/DashboardTemplate.tsx +110 -94
- package/templates/src/features/template/ui/FormTemplate.tsx +117 -117
- package/templates/src/features/template/ui/LoginTemplate.tsx +59 -52
- package/templates/src/features/template/ui/TemplateContent.tsx +1314 -1193
- package/templates/src/i18n.ts +54 -7
- package/templates/src/locales/en/common.json +21 -0
- package/templates/src/locales/en/components/activityCard.json +10 -0
- package/templates/src/locales/en/components/assistant.json +119 -0
- package/templates/src/locales/en/components/media.json +29 -0
- package/templates/src/locales/en/components/notificationCard.json +5 -0
- package/templates/src/locales/en/components/profileCard.json +8 -0
- package/templates/src/locales/en/components/projectCard.json +10 -0
- package/templates/src/locales/en/components/sidebar.json +14 -0
- package/templates/src/locales/en/components/stats.json +8 -0
- package/templates/src/locales/en/components/team.json +14 -0
- package/templates/src/locales/en/errors.json +9 -0
- package/templates/src/locales/en/languageSelector.json +7 -0
- package/templates/src/locales/en/nav.json +6 -0
- package/templates/src/locales/en/pages/crudTemplate.json +25 -0
- package/templates/src/locales/en/pages/dashboardTemplate.json +20 -0
- package/templates/src/locales/en/pages/forgotPassword.json +10 -0
- package/templates/src/locales/en/pages/formTemplate.json +16 -0
- package/templates/src/locales/en/pages/home.json +7 -0
- package/templates/src/locales/en/pages/login.json +15 -0
- package/templates/src/locales/en/pages/loginTemplate.json +9 -0
- package/templates/src/locales/en/pages/resetPassword.json +18 -0
- package/templates/src/locales/en/pages/templates.json +317 -0
- package/templates/src/locales/en/pages/verifyEmail.json +12 -0
- package/templates/src/locales/en/themeToggle.json +6 -0
- package/templates/src/locales/es/common.json +21 -0
- package/templates/src/locales/es/components/activityCard.json +10 -0
- package/templates/src/locales/es/components/assistant.json +119 -0
- package/templates/src/locales/es/components/media.json +29 -0
- package/templates/src/locales/es/components/notificationCard.json +5 -0
- package/templates/src/locales/es/components/profileCard.json +8 -0
- package/templates/src/locales/es/components/projectCard.json +10 -0
- package/templates/src/locales/es/components/sidebar.json +14 -0
- package/templates/src/locales/es/components/stats.json +8 -0
- package/templates/src/locales/es/components/team.json +14 -0
- package/templates/src/locales/es/errors.json +9 -0
- package/templates/src/locales/es/languageSelector.json +7 -0
- package/templates/src/locales/es/nav.json +6 -0
- package/templates/src/locales/es/pages/crudTemplate.json +25 -0
- package/templates/src/locales/es/pages/dashboardTemplate.json +20 -0
- package/templates/src/locales/es/pages/forgotPassword.json +10 -0
- package/templates/src/locales/es/pages/formTemplate.json +16 -0
- package/templates/src/locales/es/pages/home.json +7 -0
- package/templates/src/locales/es/pages/login.json +15 -0
- package/templates/src/locales/es/pages/loginTemplate.json +9 -0
- package/templates/src/locales/es/pages/resetPassword.json +18 -0
- package/templates/src/locales/es/pages/templates.json +317 -0
- package/templates/src/locales/es/pages/verifyEmail.json +12 -0
- package/templates/src/locales/es/themeToggle.json +6 -0
- package/templates/src/locales/pt-BR/common.json +21 -0
- package/templates/src/locales/pt-BR/components/activityCard.json +10 -0
- package/templates/src/locales/pt-BR/components/assistant.json +119 -0
- package/templates/src/locales/pt-BR/components/media.json +29 -0
- package/templates/src/locales/pt-BR/components/notificationCard.json +5 -0
- package/templates/src/locales/pt-BR/components/profileCard.json +8 -0
- package/templates/src/locales/pt-BR/components/projectCard.json +10 -0
- package/templates/src/locales/pt-BR/components/sidebar.json +14 -0
- package/templates/src/locales/pt-BR/components/stats.json +8 -0
- package/templates/src/locales/pt-BR/components/team.json +14 -0
- package/templates/src/locales/pt-BR/errors.json +9 -0
- package/templates/src/locales/pt-BR/languageSelector.json +7 -0
- package/templates/src/locales/pt-BR/nav.json +6 -0
- package/templates/src/locales/pt-BR/pages/crudTemplate.json +25 -0
- package/templates/src/locales/pt-BR/pages/dashboardTemplate.json +20 -0
- package/templates/src/locales/pt-BR/pages/forgotPassword.json +10 -0
- package/templates/src/locales/pt-BR/pages/formTemplate.json +16 -0
- package/templates/src/locales/pt-BR/pages/home.json +7 -0
- package/templates/src/locales/pt-BR/pages/login.json +15 -0
- package/templates/src/locales/pt-BR/pages/loginTemplate.json +9 -0
- package/templates/src/locales/pt-BR/pages/resetPassword.json +18 -0
- package/templates/src/locales/pt-BR/pages/templates.json +317 -0
- package/templates/src/locales/pt-BR/pages/verifyEmail.json +12 -0
- package/templates/src/locales/pt-BR/themeToggle.json +6 -0
- package/templates/src/pages/AssistantPage.tsx +464 -463
- package/templates/vite.config.ts +35 -5
- package/templates/src/locales/en.json +0 -306
- package/templates/src/locales/es.json +0 -306
- package/templates/src/locales/pt-BR.json +0 -306
package/CHANGELOG.md
CHANGED
|
@@ -9,20 +9,41 @@ Versioning follows [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
|
|
9
9
|
|
|
10
10
|
## [Unreleased]
|
|
11
11
|
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## [2.2.1] — 2026-05-21
|
|
15
|
+
|
|
12
16
|
### Added
|
|
13
17
|
|
|
18
|
+
- **i18n — Cobertura completa na página de Template** — `TemplateContent.tsx` e os 4 starter templates (`LoginTemplate`, `FormTemplate`, `DashboardTemplate`, `CrudTemplate`) estão 100% traduzidos (pt-BR, en, es). Anteriormente a maioria dos textos era hard-coded em PT-BR.
|
|
19
|
+
- **i18n — Namespace `templates.*` completo** — novo namespace com 539 chaves cobrindo todos os textos visíveis: seções, alertas, abas, formulários, tabela de dados, configurações, botões (variantes/tamanhos), badges, dialogs, sidebar, componentes avançados (Pagination, Stepper, TreeView, RichTextEditor) e rodapé. Suporte completo para `<Trans>` com interpolação de `<code>` nas descrições técnicas.
|
|
20
|
+
- **i18n — Novos namespaces de starter templates** — 4 novos arquivos de locale por idioma: `loginTemplate`, `formTemplate`, `dashboardTemplate`, `crudTemplate` (total de 12 novos JSONs × 2 mirrors = 24 arquivos).
|
|
21
|
+
- **i18n — Estrutura de locales reorganizada** — os monolitos `locales/<lang>.json` foram divididos em pastas por categoria:
|
|
22
|
+
- `locales/<lang>/{common,nav,errors,languageSelector,themeToggle}.json` (transversal)
|
|
23
|
+
- `locales/<lang>/pages/{home,templates,login,resetPassword,verifyEmail,loginTemplate,formTemplate,dashboardTemplate,crudTemplate}.json`
|
|
24
|
+
- `locales/<lang>/components/{assistant,sidebar,media,projectCard,profileCard,notificationCard,activityCard,stats,team}.json`
|
|
25
|
+
- **i18n — Loader via `import.meta.glob`** — `i18n.ts` (base e templates) substituiu os 57 imports estáticos por 3 chamadas `import.meta.glob('./locales/<lang>/**/*.json', { eager: true })` + função `bundleLang()`. Adicionar um novo arquivo JSON é suficiente — sem tocar no `i18n.ts`.
|
|
26
|
+
- **CLI — Locale em pastas** — `syncLocaleFiles` e `generateI18nFile` em `bin/language-config.ts` atualizados para copiar/gerar a nova estrutura de pastas. Projetos legados com `<lang>.json` flat são migrados automaticamente no próximo `update`.
|
|
27
|
+
- **`templates/vite.config.ts` — aliases de monorepo** — aliases automáticos detectam se o `vite.config.ts` está dentro do monorepo (`../components/index.ts` existe) e apontam `xertica-ui/*` diretamente para o source TypeScript, permitindo HMR sem rebuild da lib. Em projetos gerados pelo CLI, o check retorna `false` e `xertica-ui` resolve do `node_modules` normalmente.
|
|
14
28
|
- **CLI — Language selection on `init`** — `npx xertica-ui init` now prompts for the languages the project should support (multi-select with `pt-BR`, `en`, `es`; all selected by default; minimum 1). The CLI:
|
|
15
|
-
- Copies **only** the
|
|
16
|
-
- Generates `src/i18n.ts` with
|
|
29
|
+
- Copies **only** the locale folders for the selected languages into `src/locales/` (no orphan locales)
|
|
30
|
+
- Generates `src/i18n.ts` with `import.meta.glob` calls for exactly those languages
|
|
17
31
|
- Injects the `availableLanguages` prop into the generated `src/app/App.tsx`
|
|
18
32
|
- Persists the selection in `src/locales/.languages.json` (schema v1)
|
|
19
|
-
- **CLI — `update` → Languages** — new option in the `update` command lets users add or remove languages later. Shows a diff (`+ es`, `- en`) before confirming. Regenerates `App.tsx`, `i18n.ts`, copies new locale
|
|
33
|
+
- **CLI — `update` → Languages** — new option in the `update` command lets users add or remove languages later. Shows a diff (`+ es`, `- en`) before confirming. Regenerates `App.tsx`, `i18n.ts`, copies new locale folders, and prunes removed ones.
|
|
20
34
|
- **CLI — Monolingual auto-detection** — when only one language is selected, the generated `App.tsx` includes a banner comment documenting that the `LanguageSelector` will auto-hide.
|
|
21
35
|
- **`bin/language-config.ts`** — new module encapsulating the supported-language registry (`SUPPORTED_LANGUAGES`), code generators (`generateI18nFile`, `generateAppTsx`), and persistence helpers (`readLanguagesConfig`, `writeLanguagesConfig`, `syncLocaleFiles`).
|
|
22
36
|
|
|
37
|
+
### Fixed
|
|
38
|
+
|
|
39
|
+
- **Dark mode toggle no CLI** — `XerticaProvider` no `templates/src/app/App.tsx` era gerado com `disableDarkMode` hard-coded, tornando o `ThemeToggle` um no-op. Removido de `App.tsx`, do template literal do CLI em `bin/language-config.ts` e do exemplo em `templates/CLAUDE.md`.
|
|
40
|
+
|
|
23
41
|
### Changed
|
|
24
42
|
|
|
25
43
|
- **CLI — `update` → Project files (`app` branch)** — instead of blindly overwriting `App.tsx` and `i18n.ts` with the static template (which would erase the user's language selection), the update flow now reads the persisted selection from `src/locales/.languages.json` and regenerates these files honoring it. Projects scaffolded before this feature shipped get their selection inferred from the locale files present and the inferred config is written back.
|
|
44
|
+
- **`DashboardTemplate`** — `StatsCard` deixou de chamar `useTranslation()` internamente; o sufixo de tendência (`lastMonth`) é resolvido no pai e passado como prop, reduzindo o número de subscribers i18n de 5 para 1.
|
|
45
|
+
- **`TemplateContent` — dialog inputs controlados** — os `Input` do dialog "Editar Perfil" foram convertidos de `defaultValue` (uncontrolled) para `value`/`onChange` com lazy `useState`, garantindo que o valor reflita o idioma ativo no momento da montagem sem sobrescrever edições do usuário.
|
|
46
|
+
- **`TemplateContent` — `treeData` movido para dentro do componente** — os labels dos nós do TreeView (`Components`, `UI`, `Button`…) agora reagem ao switch de idioma em tempo real, pois `treeData` é construído após `const { t } = useTranslation()`.
|
|
26
47
|
|
|
27
48
|
---
|
|
28
49
|
|
package/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
> **Enterprise-grade React design system** built on Tailwind CSS v4, Radix UI, and Lucide Icons — with a robust AI-first documentation layer for precise LLM-driven composition and autonomous agent interaction.
|
|
4
4
|
|
|
5
|
-
[](https://www.npmjs.com/package/xertica-ui)
|
|
6
6
|
[](./LICENSE)
|
|
7
7
|
|
|
8
8
|
---
|
package/bin/cli.ts
CHANGED
|
@@ -426,8 +426,10 @@ program
|
|
|
426
426
|
const localesDir = path.join(targetDir, 'src', 'locales');
|
|
427
427
|
if (await fs.pathExists(localesDir)) {
|
|
428
428
|
const entries = await fs.readdir(localesDir);
|
|
429
|
-
|
|
430
|
-
|
|
429
|
+
// Accept both the new folder layout (locales/<code>/) and the legacy
|
|
430
|
+
// flat layout (locales/<code>.json) when inferring the current set.
|
|
431
|
+
currentCodes = SUPPORTED_LANGUAGES.filter(
|
|
432
|
+
l => entries.includes(l.jsonFile) || entries.includes(`${l.jsonFile}.json`)
|
|
431
433
|
).map(l => l.code);
|
|
432
434
|
}
|
|
433
435
|
if (currentCodes.length === 0) currentCodes = DEFAULT_SELECTION;
|
|
@@ -657,8 +659,9 @@ program
|
|
|
657
659
|
const localesDir = path.join(targetDir, 'src', 'locales');
|
|
658
660
|
if (await fs.pathExists(localesDir)) {
|
|
659
661
|
const entries = await fs.readdir(localesDir);
|
|
660
|
-
|
|
661
|
-
|
|
662
|
+
// Accept both the new folder layout and the legacy flat layout
|
|
663
|
+
selectedCodes = SUPPORTED_LANGUAGES.filter(
|
|
664
|
+
l => entries.includes(l.jsonFile) || entries.includes(`${l.jsonFile}.json`)
|
|
662
665
|
).map(l => l.code);
|
|
663
666
|
}
|
|
664
667
|
if (selectedCodes.length === 0) selectedCodes = DEFAULT_SELECTION;
|
package/bin/language-config.ts
CHANGED
|
@@ -84,10 +84,19 @@ export async function writeLanguagesConfig(
|
|
|
84
84
|
// ── File-system helpers ──────────────────────────────────────────────────────
|
|
85
85
|
|
|
86
86
|
/**
|
|
87
|
-
* Copy only the locale
|
|
87
|
+
* Copy only the locale folders matching the selected codes into the project,
|
|
88
88
|
* leaving previously-copied locales for unselected languages in place ONLY if
|
|
89
89
|
* `pruneOthers` is false. When `true` (typical for re-selection on update),
|
|
90
|
-
* the unselected
|
|
90
|
+
* the unselected language folders are deleted from the project.
|
|
91
|
+
*
|
|
92
|
+
* Each language is shipped as a folder of split JSON files:
|
|
93
|
+
*
|
|
94
|
+
* templates/src/locales/<lang>/{common,nav,errors,languageSelector,themeToggle}.json
|
|
95
|
+
* templates/src/locales/<lang>/pages/<page>.json
|
|
96
|
+
* templates/src/locales/<lang>/components/<component>.json
|
|
97
|
+
*
|
|
98
|
+
* Also migrates legacy projects that still have flat `<lang>.json` files
|
|
99
|
+
* (deletes them so they don't shadow the new folder).
|
|
91
100
|
*/
|
|
92
101
|
export async function syncLocaleFiles(
|
|
93
102
|
templatesDir: string,
|
|
@@ -104,22 +113,36 @@ export async function syncLocaleFiles(
|
|
|
104
113
|
const copied: string[] = [];
|
|
105
114
|
const removed: string[] = [];
|
|
106
115
|
|
|
107
|
-
// Copy selected language
|
|
116
|
+
// Copy selected language folders from the library templates
|
|
108
117
|
for (const lang of selectedLangs) {
|
|
109
|
-
const src = path.join(templatesDir, 'src', 'locales',
|
|
110
|
-
const dest = path.join(targetLocalesDir,
|
|
118
|
+
const src = path.join(templatesDir, 'src', 'locales', lang.jsonFile);
|
|
119
|
+
const dest = path.join(targetLocalesDir, lang.jsonFile);
|
|
111
120
|
if (await fs.pathExists(src)) {
|
|
121
|
+
// Clear the target folder first so removed keys don't linger
|
|
122
|
+
await fs.remove(dest);
|
|
112
123
|
await fs.copy(src, dest, { overwrite: true });
|
|
113
|
-
copied.push(`${lang.jsonFile}
|
|
124
|
+
copied.push(`${lang.jsonFile}/`);
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Migrate legacy flat file if it exists alongside the new folder
|
|
128
|
+
const legacyFlat = path.join(targetLocalesDir, `${lang.jsonFile}.json`);
|
|
129
|
+
if (await fs.pathExists(legacyFlat)) {
|
|
130
|
+
await fs.remove(legacyFlat);
|
|
114
131
|
}
|
|
115
132
|
}
|
|
116
133
|
|
|
117
|
-
// Remove unselected language
|
|
134
|
+
// Remove unselected language folders from the project (when pruning)
|
|
118
135
|
if (options.pruneOthers) {
|
|
119
136
|
for (const lang of unselectedLangs) {
|
|
120
|
-
const dest = path.join(targetLocalesDir,
|
|
137
|
+
const dest = path.join(targetLocalesDir, lang.jsonFile);
|
|
121
138
|
if (await fs.pathExists(dest)) {
|
|
122
139
|
await fs.remove(dest);
|
|
140
|
+
removed.push(`${lang.jsonFile}/`);
|
|
141
|
+
}
|
|
142
|
+
// Also prune any legacy flat file
|
|
143
|
+
const legacyFlat = path.join(targetLocalesDir, `${lang.jsonFile}.json`);
|
|
144
|
+
if (await fs.pathExists(legacyFlat)) {
|
|
145
|
+
await fs.remove(legacyFlat);
|
|
123
146
|
removed.push(`${lang.jsonFile}.json`);
|
|
124
147
|
}
|
|
125
148
|
}
|
|
@@ -131,9 +154,13 @@ export async function syncLocaleFiles(
|
|
|
131
154
|
// ── Code generators ──────────────────────────────────────────────────────────
|
|
132
155
|
|
|
133
156
|
/**
|
|
134
|
-
* Generate the contents of `src/i18n.ts`
|
|
135
|
-
*
|
|
136
|
-
*
|
|
157
|
+
* Generate the contents of `src/i18n.ts` for a freshly-scaffolded project.
|
|
158
|
+
*
|
|
159
|
+
* Each selected language is pulled via Vite's `import.meta.glob` (one glob per
|
|
160
|
+
* language) — resolved statically at build time so JSON gets inlined and
|
|
161
|
+
* tree-shaken. No per-chunk imports to maintain; adding a new
|
|
162
|
+
* `locales/<lang>/pages/foo.json` is picked up automatically on the next
|
|
163
|
+
* Vite build.
|
|
137
164
|
*/
|
|
138
165
|
export function generateI18nFile(selectedCodes: string[]): string {
|
|
139
166
|
const selectedLangs = SUPPORTED_LANGUAGES.filter(l => selectedCodes.includes(l.code));
|
|
@@ -141,16 +168,21 @@ export function generateI18nFile(selectedCodes: string[]): string {
|
|
|
141
168
|
throw new Error('generateI18nFile: must include at least one language');
|
|
142
169
|
}
|
|
143
170
|
|
|
144
|
-
const
|
|
145
|
-
|
|
171
|
+
const fallback = selectedLangs[0].code;
|
|
172
|
+
|
|
173
|
+
const bundleLines = selectedLangs
|
|
174
|
+
.map(
|
|
175
|
+
lang =>
|
|
176
|
+
`const ${toJsIdent(lang.code)} = bundleLang(\n` +
|
|
177
|
+
` import.meta.glob('./locales/${lang.jsonFile}/**/*.json', { eager: true, import: 'default' })\n` +
|
|
178
|
+
`);`
|
|
179
|
+
)
|
|
146
180
|
.join('\n');
|
|
147
181
|
|
|
148
182
|
const resourceEntries = selectedLangs
|
|
149
183
|
.map(l => ` '${l.code}': { translation: ${toJsIdent(l.code)} },`)
|
|
150
184
|
.join('\n');
|
|
151
185
|
|
|
152
|
-
const fallback = selectedLangs[0].code;
|
|
153
|
-
|
|
154
186
|
return `// ─────────────────────────────────────────────────────────────────────────────
|
|
155
187
|
// i18n.ts — i18next configuration (generated by xertica-ui CLI)
|
|
156
188
|
//
|
|
@@ -158,14 +190,43 @@ export function generateI18nFile(selectedCodes: string[]): string {
|
|
|
158
190
|
// component is rendered. The side-effect initializes i18next synchronously so
|
|
159
191
|
// that \`useTranslation()\` is ready on first render.
|
|
160
192
|
//
|
|
193
|
+
// Locale layout (split for maintainability — merged at boot into a single
|
|
194
|
+
// \`translation\` namespace so all existing \`t('home.welcome')\`-style calls
|
|
195
|
+
// keep working without changes):
|
|
196
|
+
//
|
|
197
|
+
// locales/<lang>/{common,nav,errors,languageSelector,themeToggle}.json
|
|
198
|
+
// locales/<lang>/pages/{home,templates,login,resetPassword,verifyEmail}.json
|
|
199
|
+
// locales/<lang>/components/{assistant,sidebar,media,projectCard,
|
|
200
|
+
// profileCard,notificationCard,activityCard,
|
|
201
|
+
// stats,team}.json
|
|
202
|
+
//
|
|
161
203
|
// To add or remove a language, run: npx xertica-ui update → Languages
|
|
162
|
-
// The CLI will regenerate this file and copy/remove locale
|
|
204
|
+
// The CLI will regenerate this file and copy/remove locale folders accordingly.
|
|
163
205
|
// ─────────────────────────────────────────────────────────────────────────────
|
|
164
206
|
|
|
165
207
|
import i18n from 'i18next';
|
|
166
208
|
import { initReactI18next } from 'react-i18next';
|
|
167
209
|
|
|
168
|
-
|
|
210
|
+
/**
|
|
211
|
+
* Merge a Vite glob result into a single { key: contents } bundle. The key is
|
|
212
|
+
* derived from the file basename — folders (pages/components) are discarded
|
|
213
|
+
* because the legacy flat JSON layout used a single namespace.
|
|
214
|
+
*/
|
|
215
|
+
function bundleLang(chunks: Record<string, unknown>): Record<string, unknown> {
|
|
216
|
+
const out: Record<string, unknown> = {};
|
|
217
|
+
for (const [filePath, value] of Object.entries(chunks)) {
|
|
218
|
+
const base = filePath.split('/').pop();
|
|
219
|
+
if (!base) continue;
|
|
220
|
+
const key = base.replace(/\\.json$/, '');
|
|
221
|
+
out[key] = value;
|
|
222
|
+
}
|
|
223
|
+
return out;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
// One \`import.meta.glob\` call per language — Vite requires literal patterns,
|
|
227
|
+
// so we can't loop. \`eager: true\` inlines the JSON; \`import: 'default'\`
|
|
228
|
+
// returns the JSON value directly instead of \`{ default: ... }\`.
|
|
229
|
+
${bundleLines}
|
|
169
230
|
|
|
170
231
|
const STORAGE_KEY = 'xertica_language';
|
|
171
232
|
const DEFAULT_FALLBACK = '${fallback}';
|
|
@@ -272,8 +333,7 @@ export default function App() {
|
|
|
272
333
|
<QueryClientProvider client={queryClient}>
|
|
273
334
|
<XerticaProvider
|
|
274
335
|
apiKey={geminiApiKey}
|
|
275
|
-
googleMapsApiKey={googleMapsApiKey}
|
|
276
|
-
disableDarkMode${availableLanguagesProp}
|
|
336
|
+
googleMapsApiKey={googleMapsApiKey}${availableLanguagesProp}
|
|
277
337
|
>
|
|
278
338
|
<Router>
|
|
279
339
|
{/* AuthProvider must be inside Router (needs useNavigate) */}
|