localization-i18n-vite-kit
v0.2.2
Published
Vite i18n kit: component translations.json, prod locales folder, strip imports
Readme
localization-i18n-vite-kit
Детерминированный i18n для Vite + React SPA: рядом с компонентом лежит translations.json. В development строки попадают в бандл и регистрируются через i18next.addResourceBundle. В production Vite-плагины вырезают эти импорты и собирают артефакты в dist/locales/<lng>/<namespace>.json; загрузка идёт через i18next-http-backend.
Публичный пакет на npm: localization-i18n-vite-kit. API: createI18n и плагины stripProdComponentTranslationsPlugin, i18nLocalesPlugin. Хелперов вроде defineI18nProjectConfig / createI18nRuntime в пакете нет.
npm install localization-i18n-vite-kitЭкспорты
| Путь | Назначение |
|------|------------|
| localization-i18n-vite-kit | createI18n, типы, реэкспорт i18n |
| localization-i18n-vite-kit/vite | i18nLocalesPlugin, stripProdComponentTranslationsPlugin |
Порядок внедрения
Под каждым шагом — пример кода и пояснения.
1. Установка
В package.json добавить пакет (версия — актуальная с npm), пиры в dependencies, vite обычно как devDependency для сборки приложения. Затем npm install.
{
"dependencies": {
"localization-i18n-vite-kit": "^0.2.2",
"i18next": "^25.0.0",
"i18next-http-backend": "^3.0.0",
"react-i18next": "^16.0.0",
"zustand": "^5.0.0"
}
}Нижние границы версий пиров — в peerDependencies этого пакета. Локально: "localization-i18n-vite-kit": "file:./path/to/localization-i18n-vite-kit".
2. Конфиг: фасад (src/config/i18n-localization.ts)
- Импорт только
createI18nизlocalization-i18n-vite-kit. - Один модуль на всё приложение:
registerComponentTranslationsимпортируют только отсюда — путь должен совпасть сregisterModuleSpecifiersв Vite (шаг 4).
Поля createI18n({ … }):
| Поле | Зачем |
|------|--------|
| supportedLocales | Коды языков (as const), как ключи в translations.json. |
| defaultLocale | Запасной язык; сброс стора после ошибки загрузки словаря в prod. |
| namespaces | Все namespace из проектных translations.json; в dev все попадают в i18n.init сразу. |
| initialNamespacesOnLoad | Подмножество: что грузить по HTTP при старте только в production; остальные ns — при первом useTranslation(ns). |
| defaultNamespace | defaultNS / fallbackNS в i18next. |
import { createI18n } from 'localization-i18n-vite-kit'
export const SUPPORTED_LOCALES = ['ru', 'en', 'de'] as const
export const DEFAULT_LOCALE = 'ru' as const
export type LanguageLocale = (typeof SUPPORTED_LOCALES)[number]
export const {
i18n,
initI18nApp,
registerComponentTranslations,
isLocale,
getResolvedLocale,
useLocaleStore,
} = createI18n({
supportedLocales: SUPPORTED_LOCALES,
defaultLocale: DEFAULT_LOCALE,
namespaces: ['common', 'demo'] as const,
initialNamespacesOnLoad: ['common'] as const,
defaultNamespace: 'common',
})3. Точка входа (src/main.tsx или main.ts)
Сначала await initI18nApp(), затем рендер App. Дерево с useTranslation обернуть в Suspense.
import { StrictMode, Suspense } from 'react'
import { createRoot } from 'react-dom/client'
import './app/global.scss'
async function bootstrap() {
const rootEl = document.getElementById('root')
if (!rootEl) throw new Error('#root element not found')
const { initI18nApp } = await import('./config/i18n-localization')
await initI18nApp()
const App = (await import('./app/App')).default
createRoot(rootEl).render(
<StrictMode>
<Suspense fallback={<div>Загрузка…</div>}>
<App />
</Suspense>
</StrictMode>,
)
}
void bootstrap()BrowserRouter и др.: не монтировать App до await initI18nApp(); Suspense — родитель участка с переводами (роутер снаружи или внутри — по проекту).
4. Vite (vite.config.ts)
Плагины из localization-i18n-vite-kit/vite. Порядок для переводов: stripProdComponentTranslationsPlugin → react() → i18nLocalesPlugin. registerModuleSpecifiers — та же строка, что в import { registerComponentTranslations } from '…' в компонентах.
Между этими плагинами могут быть другие плагины проекта (например обработка изображений), если порядок осознан.
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'
import { resolve } from 'path'
import {
i18nLocalesPlugin,
stripProdComponentTranslationsPlugin,
} from 'localization-i18n-vite-kit/vite'
export default defineConfig({
plugins: [
stripProdComponentTranslationsPlugin({
registerModuleSpecifiers: ['@/config/i18n-localization'],
}),
react(),
i18nLocalesPlugin({ sourceDir: 'src' }),
],
resolve: {
dedupe: ['react', 'react-dom', 'react/jsx-runtime', 'i18next', 'react-i18next', 'i18next-http-backend'],
alias: { '@': resolve(__dirname, 'src') },
},
})Другой путь к фасаду — синхронно править registerModuleSpecifiers и импорты в компонентах. Корень исходников не src — sourceDir / alias. Опция i18nLocalesPlugin: translationsFileName (по умолчанию translations.json).
5. Компонент + translations.json
На верхнем уровне модуля: registerComponentTranslations('ИмяПапки', raw) — имя папки = префикс в t(\${P}.ключ`)**. **useTranslation(raw.namespace)** совпадает с полем **namespace** в JSON и с **namespaces** в **createI18n`.
MyComponent/MyComponent.tsx:
import { useTranslation } from 'react-i18next'
import { registerComponentTranslations } from '@/config/i18n-localization'
import raw from './translations.json'
registerComponentTranslations('MyComponent', raw)
const P = 'MyComponent' as const
export function MyComponent() {
const { t } = useTranslation(raw.namespace)
return (
<section>
<h2>{t(`${P}.title`)}</h2>
<p>{t(`${P}.body`)}</p>
</section>
)
}MyComponent/translations.json (ключи языков = supportedLocales):
{
"namespace": "demo",
"ru": { "title": "Заголовок", "body": "Текст" },
"en": { "title": "Title", "body": "Body" },
"de": { "title": "Titel", "body": "Text" }
}Поведение development и production
| Режим | Как живут строки |
|-------|------------------|
| Dev | Импорт translations.json + registerComponentTranslations, строки в бандле, без HTTP к locales/. |
| Prod | Плагин strip убирает импорты/вызовы; i18nLocalesPlugin пишет dist/locales/<lng>/<ns>.json; initI18nApp подключает HttpBackend, loadPath: {BASE_URL}locales/{{lng}}/{{ns}}.json. |
Детали: fallbackLng: false, load: 'languageOnly', react: { useSuspense: true }. При failedLoading для языка ≠ defaultLocale встроенный zustand-стор сбрасывает локаль на defaultLocale. Persist в localStorage, ключ i18n-localization.
Типичные ошибки
- Имя пакета:
localization-i18n-vite-kit, неi18n-vite-kit. - Плагины только из
localization-i18n-vite-kit/vite. registerModuleSpecifiers= буквальное совпадение с импортомregisterComponentTranslations(алиасы учитывать).- Тип
LocaleStoreбрать изlocalization-i18n-vite-kit.
Типы (экспорт)
ComponentTranslationsJson, I18nConfig, LocaleStore, I18nRuntime, I18nLocalesPluginOptions, StripProdTranslationsPluginOptions.
Разработка репозитория пакета
npm install
npm run buildprepublishOnly запускает сборку перед публикацией на npm.
