;
+export const Default: Story = {
+ args: {
+ children: 'Button',
+ },
+};
+
export const Primary: Story = {
- args: { intent: 'primary' },
+ args: {
+ intent: 'primary',
+ children: 'Primary Button',
+ },
};
export const Secondary: Story = {
- args: { intent: 'secondary' },
+ args: {
+ intent: 'secondary',
+ children: 'Secondary Button',
+ },
};
export const Danger: Story = {
- args: { intent: 'danger' },
+ args: {
+ intent: 'danger',
+ children: 'Danger Button',
+ },
+};
+
+export const Loading: Story = {
+ args: {
+ loading: true,
+ children: 'Loading Button',
+ },
+};
+
+export const Disabled: Story = {
+ args: {
+ disabled: true,
+ children: 'Disabled Button',
+ },
+};
+
+// i18n Examples
+const I18nButtonExample = ({ translationKey }: { translationKey: string }) => {
+ const { t } = useTranslation();
+ return ;
+};
+
+export const I18nSubmit: Story = {
+ render: () => ,
+ parameters: {
+ docs: {
+ description: {
+ story: 'Button with internationalized "Submit" text. Switch locale in toolbar to see German translation.',
+ },
+ },
+ },
+};
+
+export const I18nCancel: Story = {
+ render: () => ,
+ parameters: {
+ docs: {
+ description: {
+ story: 'Button with internationalized "Cancel" text. Switch locale in toolbar to see German translation.',
+ },
+ },
+ },
+};
+
+export const I18nSave: Story = {
+ render: () => ,
+ parameters: {
+ docs: {
+ description: {
+ story: 'Button with internationalized "Save" text. Switch locale in toolbar to see German translation.',
+ },
+ },
+ },
+};
+
+export const I18nLogin: Story = {
+ render: () => ,
+ parameters: {
+ docs: {
+ description: {
+ story: 'Button with internationalized "Login" text. Switch locale in toolbar to see German translation.',
+ },
+ },
+ },
};
\ No newline at end of file
diff --git a/packages/ui-kit/src/i18n/README.md b/packages/ui-kit/src/i18n/README.md
new file mode 100644
index 0000000..17e2bf6
--- /dev/null
+++ b/packages/ui-kit/src/i18n/README.md
@@ -0,0 +1,183 @@
+# Internationalization (i18n)
+
+This UI kit includes built-in internationalization support using `react-i18next`. Currently supported locales are English (`en`) and German (`de`).
+
+## Quick Start
+
+### 1. Wrap your app with I18nProvider
+
+```tsx
+import { I18nProvider } from "@org/ui-kit";
+
+function App() {
+ return {/* Your app components */};
+}
+```
+
+### 2. Use translations in components
+
+```tsx
+import { useTranslation } from "@org/ui-kit";
+
+function MyComponent() {
+ const { t } = useTranslation();
+
+ return (
+
+
{t("navigation.dashboard")}
+
+
+ );
+}
+```
+
+## Available Translation Keys
+
+### Buttons
+
+- `button.submit` - Submit / Absenden
+- `button.cancel` - Cancel / Abbrechen
+- `button.save` - Save / Speichern
+- `button.delete` - Delete / Löschen
+- `button.edit` - Edit / Bearbeiten
+- `button.close` - Close / Schließen
+- `button.back` - Back / Zurück
+- `button.next` - Next / Weiter
+- `button.previous` - Previous / Vorherige
+- `button.loading` - Loading... / Lädt...
+- `button.login` - Login / Anmelden
+- `button.logout` - Logout / Abmelden
+
+### Forms
+
+- `form.required` - This field is required / Dieses Feld ist erforderlich
+- `form.email` - Email / E-Mail
+- `form.password` - Password / Passwort
+- `form.confirmPassword` - Confirm Password / Passwort bestätigen
+- `form.firstName` - First Name / Vorname
+- `form.lastName` - Last Name / Nachname
+- `form.search` - Search / Suchen
+- `form.filter` - Filter / Filter
+- `form.clear` - Clear / Löschen
+
+### Navigation
+
+- `navigation.home` - Home / Startseite
+- `navigation.dashboard` - Dashboard / Dashboard
+- `navigation.customers` - Customers / Kunden
+- `navigation.settings` - Settings / Einstellungen
+- `navigation.profile` - Profile / Profil
+- `navigation.help` - Help / Hilfe
+
+### Tables
+
+- `table.noData` - No data available / Keine Daten verfügbar
+- `table.loading` - Loading data... / Daten werden geladen...
+- `table.rowsPerPage` - Rows per page / Zeilen pro Seite
+- `table.of` - of / von
+- `table.page` - Page / Seite
+- `table.actions` - Actions / Aktionen
+
+### Toasts
+
+- `toast.success` - Success / Erfolg
+- `toast.error` - Error / Fehler
+- `toast.warning` - Warning / Warnung
+- `toast.info` - Information / Information
+
+### Validation
+
+- `validation.required` - This field is required / Dieses Feld ist erforderlich
+- `validation.email` - Please enter a valid email address / Bitte geben Sie eine gültige E-Mail-Adresse ein
+- `validation.minLength` - Must be at least {{count}} characters / Muss mindestens {{count}} Zeichen lang sein
+- `validation.maxLength` - Must be no more than {{count}} characters / Darf höchstens {{count}} Zeichen lang sein
+- `validation.passwordMismatch` - Passwords do not match / Passwörter stimmen nicht überein
+
+## Language Switching
+
+### Programmatically
+
+```tsx
+import { useTranslation } from "@org/ui-kit";
+
+function LanguageSwitcher() {
+ const { i18n } = useTranslation();
+
+ const switchToGerman = () => {
+ i18n.changeLanguage("de");
+ };
+
+ const switchToEnglish = () => {
+ i18n.changeLanguage("en");
+ };
+
+ return (
+
+
+
+
+ );
+}
+```
+
+### In Storybook
+
+Use the locale switcher in the Storybook toolbar (globe icon) to test components in different languages.
+
+## Adding New Translations
+
+### 1. Add to English translations
+
+Edit `src/i18n/locales/en.json`:
+
+```json
+{
+ "myFeature": {
+ "title": "My Feature Title",
+ "description": "Feature description"
+ }
+}
+```
+
+### 2. Add German translations
+
+Edit `src/i18n/locales/de.json`:
+
+```json
+{
+ "myFeature": {
+ "title": "Mein Feature Titel",
+ "description": "Feature Beschreibung"
+ }
+}
+```
+
+### 3. Use in components
+
+```tsx
+const { t } = useTranslation();
+return {t("myFeature.title")}
;
+```
+
+## Interpolation
+
+For dynamic values, use interpolation:
+
+```tsx
+// Translation key: "welcome": "Welcome, {{name}}!"
+const { t } = useTranslation();
+return {t("welcome", { name: "John" })}
;
+```
+
+## Language Detection
+
+The i18n system automatically detects the user's language preference from:
+
+1. localStorage (if previously set)
+2. Browser language
+3. HTML lang attribute
+4. Falls back to English
+
+## TypeScript Support
+
+The i18n system is fully typed. Translation keys are type-checked to ensure they exist in the translation files.
diff --git a/packages/ui-kit/src/i18n/config.ts b/packages/ui-kit/src/i18n/config.ts
new file mode 100644
index 0000000..345e559
--- /dev/null
+++ b/packages/ui-kit/src/i18n/config.ts
@@ -0,0 +1,37 @@
+import i18n from 'i18next';
+import { initReactI18next } from 'react-i18next';
+import LanguageDetector from 'i18next-browser-languagedetector';
+
+// Import translation files
+import en from './locales/en.json';
+import de from './locales/de.json';
+
+export const defaultNS = 'common';
+export const resources = {
+ en: {
+ common: en,
+ },
+ de: {
+ common: de,
+ },
+} as const;
+
+i18n
+ .use(LanguageDetector)
+ .use(initReactI18next)
+ .init({
+ debug: process.env.NODE_ENV === 'development',
+ fallbackLng: 'en',
+ defaultNS,
+ ns: ['common'],
+ resources,
+ interpolation: {
+ escapeValue: false, // React already escapes values
+ },
+ detection: {
+ order: ['localStorage', 'navigator', 'htmlTag'],
+ caches: ['localStorage'],
+ },
+ });
+
+export default i18n;
\ No newline at end of file
diff --git a/packages/ui-kit/src/i18n/i18n.test.tsx b/packages/ui-kit/src/i18n/i18n.test.tsx
new file mode 100644
index 0000000..ae0d15e
--- /dev/null
+++ b/packages/ui-kit/src/i18n/i18n.test.tsx
@@ -0,0 +1,121 @@
+import React from 'react';
+import { render, screen, act } from '@testing-library/react';
+import { describe, it, expect, beforeEach } from 'vitest';
+import { I18nProvider } from '../providers/I18nProvider';
+import { useTranslation } from 'react-i18next';
+import i18n from './config';
+
+// Test component that uses translation
+const TestComponent = ({ translationKey }: { translationKey: string }) => {
+ const { t } = useTranslation();
+ return {t(translationKey)}
;
+};
+
+// Test component that shows current language
+const LanguageDisplay = () => {
+ const { i18n } = useTranslation();
+ return {i18n.language}
;
+};
+
+describe('i18n functionality', () => {
+ beforeEach(async () => {
+ // Reset to English before each test
+ await act(async () => {
+ await i18n.changeLanguage('en');
+ });
+ });
+
+ it('renders English translations by default', () => {
+ render(
+
+
+
+ );
+
+ expect(screen.getByTestId('translated-text')).toHaveTextContent('Submit');
+ });
+
+ it('switches to German translations', async () => {
+ render(
+
+
+
+ );
+
+ // Change language to German with act wrapper
+ await act(async () => {
+ await i18n.changeLanguage('de');
+ });
+
+ expect(screen.getByTestId('translated-text')).toHaveTextContent('Absenden');
+ });
+
+ it('falls back to English when German translation is missing', async () => {
+ render(
+
+
+
+ );
+
+ await act(async () => {
+ await i18n.changeLanguage('de');
+ });
+
+ // Should show the key since it doesn't exist
+ expect(screen.getByTestId('translated-text')).toHaveTextContent('nonexistent.key');
+ });
+
+ it('handles interpolation correctly', () => {
+ render(
+
+
+
+ );
+
+ // The interpolation should show the placeholder
+ expect(screen.getByTestId('translated-text')).toHaveTextContent('Must be at least {{count}} characters');
+ });
+
+ it('detects and sets language correctly', () => {
+ render(
+
+
+
+ );
+
+ expect(screen.getByTestId('current-language')).toHaveTextContent('en');
+ });
+
+ it('provides all expected translation keys for buttons', () => {
+ const buttonKeys = [
+ 'button.submit',
+ 'button.cancel',
+ 'button.save',
+ 'button.delete',
+ 'button.edit',
+ 'button.close',
+ 'button.login',
+ 'button.logout'
+ ];
+
+ render(
+
+
+ {buttonKeys.map((key) => (
+
+ ))}
+
+
+ );
+
+ // Check that all button translations are rendered by checking specific text content
+ expect(screen.getByText('Submit')).toBeInTheDocument();
+ expect(screen.getByText('Cancel')).toBeInTheDocument();
+ expect(screen.getByText('Save')).toBeInTheDocument();
+ expect(screen.getByText('Delete')).toBeInTheDocument();
+ expect(screen.getByText('Edit')).toBeInTheDocument();
+ expect(screen.getByText('Close')).toBeInTheDocument();
+ expect(screen.getByText('Login')).toBeInTheDocument();
+ expect(screen.getByText('Logout')).toBeInTheDocument();
+ });
+});
\ No newline at end of file
diff --git a/packages/ui-kit/src/i18n/index.ts b/packages/ui-kit/src/i18n/index.ts
new file mode 100644
index 0000000..8531866
--- /dev/null
+++ b/packages/ui-kit/src/i18n/index.ts
@@ -0,0 +1,2 @@
+export { default as i18n } from './config';
+export * from './types';
\ No newline at end of file
diff --git a/packages/ui-kit/src/i18n/locales/de.json b/packages/ui-kit/src/i18n/locales/de.json
new file mode 100644
index 0000000..5f9bca1
--- /dev/null
+++ b/packages/ui-kit/src/i18n/locales/de.json
@@ -0,0 +1,56 @@
+{
+ "button": {
+ "submit": "Absenden",
+ "cancel": "Abbrechen",
+ "save": "Speichern",
+ "delete": "Löschen",
+ "edit": "Bearbeiten",
+ "close": "Schließen",
+ "back": "Zurück",
+ "next": "Weiter",
+ "previous": "Vorherige",
+ "loading": "Lädt...",
+ "login": "Anmelden",
+ "logout": "Abmelden"
+ },
+ "form": {
+ "required": "Dieses Feld ist erforderlich",
+ "email": "E-Mail",
+ "password": "Passwort",
+ "confirmPassword": "Passwort bestätigen",
+ "firstName": "Vorname",
+ "lastName": "Nachname",
+ "search": "Suchen",
+ "filter": "Filter",
+ "clear": "Löschen"
+ },
+ "navigation": {
+ "home": "Startseite",
+ "dashboard": "Dashboard",
+ "customers": "Kunden",
+ "settings": "Einstellungen",
+ "profile": "Profil",
+ "help": "Hilfe"
+ },
+ "table": {
+ "noData": "Keine Daten verfügbar",
+ "loading": "Daten werden geladen...",
+ "rowsPerPage": "Zeilen pro Seite",
+ "of": "von",
+ "page": "Seite",
+ "actions": "Aktionen"
+ },
+ "toast": {
+ "success": "Erfolg",
+ "error": "Fehler",
+ "warning": "Warnung",
+ "info": "Information"
+ },
+ "validation": {
+ "required": "Dieses Feld ist erforderlich",
+ "email": "Bitte geben Sie eine gültige E-Mail-Adresse ein",
+ "minLength": "Muss mindestens {{count}} Zeichen lang sein",
+ "maxLength": "Darf höchstens {{count}} Zeichen lang sein",
+ "passwordMismatch": "Passwörter stimmen nicht überein"
+ }
+}
diff --git a/packages/ui-kit/src/i18n/locales/en.json b/packages/ui-kit/src/i18n/locales/en.json
new file mode 100644
index 0000000..b6a569c
--- /dev/null
+++ b/packages/ui-kit/src/i18n/locales/en.json
@@ -0,0 +1,56 @@
+{
+ "button": {
+ "submit": "Submit",
+ "cancel": "Cancel",
+ "save": "Save",
+ "delete": "Delete",
+ "edit": "Edit",
+ "close": "Close",
+ "back": "Back",
+ "next": "Next",
+ "previous": "Previous",
+ "loading": "Loading...",
+ "login": "Login",
+ "logout": "Logout"
+ },
+ "form": {
+ "required": "This field is required",
+ "email": "Email",
+ "password": "Password",
+ "confirmPassword": "Confirm Password",
+ "firstName": "First Name",
+ "lastName": "Last Name",
+ "search": "Search",
+ "filter": "Filter",
+ "clear": "Clear"
+ },
+ "navigation": {
+ "home": "Home",
+ "dashboard": "Dashboard",
+ "customers": "Customers",
+ "settings": "Settings",
+ "profile": "Profile",
+ "help": "Help"
+ },
+ "table": {
+ "noData": "No data available",
+ "loading": "Loading data...",
+ "rowsPerPage": "Rows per page",
+ "of": "of",
+ "page": "Page",
+ "actions": "Actions"
+ },
+ "toast": {
+ "success": "Success",
+ "error": "Error",
+ "warning": "Warning",
+ "info": "Information"
+ },
+ "validation": {
+ "required": "This field is required",
+ "email": "Please enter a valid email address",
+ "minLength": "Must be at least {{count}} characters",
+ "maxLength": "Must be no more than {{count}} characters",
+ "passwordMismatch": "Passwords do not match"
+ }
+}
diff --git a/packages/ui-kit/src/i18n/types.ts b/packages/ui-kit/src/i18n/types.ts
new file mode 100644
index 0000000..29b0396
--- /dev/null
+++ b/packages/ui-kit/src/i18n/types.ts
@@ -0,0 +1,8 @@
+import { resources, defaultNS } from './config';
+
+declare module 'i18next' {
+ interface CustomTypeOptions {
+ defaultNS: typeof defaultNS;
+ resources: typeof resources['en'];
+ }
+}
\ No newline at end of file
diff --git a/packages/ui-kit/src/index.ts b/packages/ui-kit/src/index.ts
index 2779e65..a5ebbdb 100644
--- a/packages/ui-kit/src/index.ts
+++ b/packages/ui-kit/src/index.ts
@@ -19,9 +19,14 @@ export * from './theme'
export { ThemeProvider } from './providers/ThemeProvider'
export { ToastProvider, useToastContext } from './providers/ToastProvider'
export type { ToastVariant, Toast as ToastType, ToastOptions } from './providers/ToastProvider'
+export { I18nProvider } from './providers/I18nProvider'
// Hooks
export * from './hooks'
// Utils
-export * from './utils'
\ No newline at end of file
+export * from './utils'
+
+// i18n
+export { i18n } from './i18n'
+export { useTranslation } from 'react-i18next'
\ No newline at end of file
diff --git a/packages/ui-kit/src/providers/I18nProvider.tsx b/packages/ui-kit/src/providers/I18nProvider.tsx
new file mode 100644
index 0000000..057443c
--- /dev/null
+++ b/packages/ui-kit/src/providers/I18nProvider.tsx
@@ -0,0 +1,11 @@
+import React from 'react';
+import { I18nextProvider } from 'react-i18next';
+import i18n from '../i18n/config';
+
+interface I18nProviderProps {
+ children: React.ReactNode;
+}
+
+export const I18nProvider: React.FC = ({ children }) => {
+ return {children};
+};
\ No newline at end of file
diff --git a/packages/ui-kit/src/providers/index.ts b/packages/ui-kit/src/providers/index.ts
index 0ca6e84..e378b1c 100644
--- a/packages/ui-kit/src/providers/index.ts
+++ b/packages/ui-kit/src/providers/index.ts
@@ -1,2 +1,3 @@
export * from './ThemeProvider';
-export * from './ToastProvider';
\ No newline at end of file
+export * from './ToastProvider';
+export * from './I18nProvider';
\ No newline at end of file
diff --git a/packages/ui-kit/src/vite-env.d.ts b/packages/ui-kit/src/vite-env.d.ts
new file mode 100644
index 0000000..60aca63
--- /dev/null
+++ b/packages/ui-kit/src/vite-env.d.ts
@@ -0,0 +1,7 @@
+///
+
+// Declare JSON module types for i18n
+declare module '*.json' {
+ const value: Record;
+ export default value;
+}
\ No newline at end of file
diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml
index 9b9a59b..6923656 100644
--- a/pnpm-lock.yaml
+++ b/pnpm-lock.yaml
@@ -51,6 +51,9 @@ importers:
'@storybook/addon-docs':
specifier: 8.6.14
version: 8.6.14(@types/react@19.1.4)(storybook@8.6.14(prettier@3.5.3))
+ '@storybook/addon-toolbars':
+ specifier: ^8.6.14
+ version: 8.6.14(storybook@8.6.14(prettier@3.5.3))
'@storybook/builder-vite':
specifier: 8.6.14
version: 8.6.14(storybook@8.6.14(prettier@3.5.3))(vite@5.4.19(@types/node@22.15.19))
@@ -162,6 +165,12 @@ importers:
'@playwright/test':
specifier: ^1.52.0
version: 1.52.0
+ '@testing-library/jest-dom':
+ specifier: ^6.6.3
+ version: 6.6.3
+ '@testing-library/react':
+ specifier: ^16.3.0
+ version: 16.3.0(@testing-library/dom@10.4.0)(@types/react-dom@19.1.5(@types/react@19.1.4))(@types/react@19.1.4)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)
'@types/react':
specifier: ^19.1.4
version: 19.1.4
@@ -189,6 +198,9 @@ importers:
eslint-plugin-react-refresh:
specifier: ^0.4.20
version: 0.4.20(eslint@9.27.0(jiti@2.4.2))
+ jsdom:
+ specifier: ^26.1.0
+ version: 26.1.0
postcss:
specifier: ^8.5.3
version: 8.5.3
@@ -246,6 +258,12 @@ importers:
clsx:
specifier: ^2.1.1
version: 2.1.1
+ i18next:
+ specifier: ^25.2.1
+ version: 25.2.1(typescript@5.8.3)
+ i18next-browser-languagedetector:
+ specifier: ^8.1.0
+ version: 8.1.0
jsdom:
specifier: ^26.1.0
version: 26.1.0
@@ -261,6 +279,9 @@ importers:
react-hook-form:
specifier: ^7.56.4
version: 7.56.4(react@19.1.0)
+ react-i18next:
+ specifier: ^15.5.2
+ version: 15.5.2(i18next@25.2.1(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3)
tailwind-merge:
specifier: ^2.6.0
version: 2.6.0
@@ -4207,6 +4228,9 @@ packages:
html-escaper@2.0.2:
resolution: {integrity: sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==}
+ html-parse-stringify@3.0.1:
+ resolution: {integrity: sha512-KknJ50kTInJ7qIScF3jeaFRpMpE8/lfiTdzf/twXyPBLAGrLRTmkz3AdTnKeh40X8k9L2fdYwEp/42WGXIRGcg==}
+
htmlparser2@3.10.1:
resolution: {integrity: sha512-IgieNijUMbkDovyoKObU1DUhm1iwNYE/fuifEoEHfd1oZKZDaONBSkal7Y01shxsM49R4XaMdGez3WnF9UfiCQ==}
@@ -4243,6 +4267,17 @@ packages:
engines: {node: '>=14'}
hasBin: true
+ i18next-browser-languagedetector@8.1.0:
+ resolution: {integrity: sha512-mHZxNx1Lq09xt5kCauZ/4bsXOEA2pfpwSoU11/QTJB+pD94iONFwp+ohqi///PwiFvjFOxe1akYCdHyFo1ng5Q==}
+
+ i18next@25.2.1:
+ resolution: {integrity: sha512-+UoXK5wh+VlE1Zy5p6MjcvctHXAhRwQKCxiJD8noKZzIXmnAX8gdHX5fLPA3MEVxEN4vbZkQFy8N0LyD9tUqPw==}
+ peerDependencies:
+ typescript: ^5
+ peerDependenciesMeta:
+ typescript:
+ optional: true
+
iconv-lite@0.4.24:
resolution: {integrity: sha512-v3MXnZAcvnywkTUEZomIActle7RXXeedOR31wwl7VlyoXO4Qi9arvSenNQWne1TcRwhCL1HwLI21bEqdpj8/rA==}
engines: {node: '>=0.10.0'}
@@ -5554,6 +5589,22 @@ packages:
peerDependencies:
react: ^16.8.0 || ^17 || ^18 || ^19
+ react-i18next@15.5.2:
+ resolution: {integrity: sha512-ePODyXgmZQAOYTbZXQn5rRsSBu3Gszo69jxW6aKmlSgxKAI1fOhDwSu6bT4EKHciWPKQ7v7lPrjeiadR6Gi+1A==}
+ peerDependencies:
+ i18next: '>= 23.2.3'
+ react: '>= 16.8.0'
+ react-dom: '*'
+ react-native: '*'
+ typescript: ^5
+ peerDependenciesMeta:
+ react-dom:
+ optional: true
+ react-native:
+ optional: true
+ typescript:
+ optional: true
+
react-is@16.13.1:
resolution: {integrity: sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ==}
@@ -6455,6 +6506,10 @@ packages:
jsdom:
optional: true
+ void-elements@3.1.0:
+ resolution: {integrity: sha512-Dhxzh5HZuiHQhbvTW9AMetFfBHDMYpo23Uo9btPXgdYP+3T5S+p+jgNy7spra+veYhBP2dCSgxR/i2Y02h5/6w==}
+ engines: {node: '>=0.10.0'}
+
w3c-xmlserializer@5.0.0:
resolution: {integrity: sha512-o8qghlI8NZHU1lLPrpi2+Uq7abh4GGPpYANlalzWxyWteJOCsr/P+oPBA49TOLu5FTZO4d3F9MnWJfiMo4BkmA==}
engines: {node: '>=18'}
@@ -11096,6 +11151,10 @@ snapshots:
html-escaper@2.0.2: {}
+ html-parse-stringify@3.0.1:
+ dependencies:
+ void-elements: 3.1.0
+
htmlparser2@3.10.1:
dependencies:
domelementtype: 1.3.1
@@ -11135,6 +11194,16 @@ snapshots:
husky@8.0.3: {}
+ i18next-browser-languagedetector@8.1.0:
+ dependencies:
+ '@babel/runtime': 7.27.1
+
+ i18next@25.2.1(typescript@5.8.3):
+ dependencies:
+ '@babel/runtime': 7.27.1
+ optionalDependencies:
+ typescript: 5.8.3
+
iconv-lite@0.4.24:
dependencies:
safer-buffer: 2.1.2
@@ -12645,6 +12714,16 @@ snapshots:
dependencies:
react: 19.1.0
+ react-i18next@15.5.2(i18next@25.2.1(typescript@5.8.3))(react-dom@19.1.0(react@19.1.0))(react@19.1.0)(typescript@5.8.3):
+ dependencies:
+ '@babel/runtime': 7.27.1
+ html-parse-stringify: 3.0.1
+ i18next: 25.2.1(typescript@5.8.3)
+ react: 19.1.0
+ optionalDependencies:
+ react-dom: 19.1.0(react@19.1.0)
+ typescript: 5.8.3
+
react-is@16.13.1: {}
react-is@17.0.2: {}
@@ -13637,6 +13716,8 @@ snapshots:
- supports-color
- terser
+ void-elements@3.1.0: {}
+
w3c-xmlserializer@5.0.0:
dependencies:
xml-name-validator: 5.0.0