@shopkit/i18n
v0.1.0
Published
Internationalization utilities for Next.js storefronts
Downloads
118
Maintainers
Readme
@shopkit/i18n
Internationalization utilities for Next.js storefronts with next-intl integration.
Features
- next-intl Integration: Built on top of next-intl for robust i18n
- Server/Client Separation: Next.js App Router compatible architecture
- Locale Detection: Cookie, header, and default-based locale detection
- Formatting Utilities: Currency, date, number, and relative time formatting
- Translation Provider: React context for page builder translation access
- Middleware Support: Easy locale routing middleware setup
Installation
npm install @shopkit/i18n
# or
bun add @shopkit/i18nPeer Dependencies
npm install next next-intl reactQuick Start
1. Configure i18n
// src/i18n/config.ts
import { createI18nConfig } from '@shopkit/i18n';
export const i18nConfig = createI18nConfig({
locales: ['en', 'es', 'fr', 'de'],
defaultLocale: 'en',
localeDetection: ['cookie', 'header', 'default'],
});
export const { locales, defaultLocale, isValidLocale } = i18nConfig;2. Setup Middleware
// middleware.ts
import { createLocaleMiddleware } from '@shopkit/i18n/middleware';
import { i18nConfig } from './src/i18n/config';
export default createLocaleMiddleware(i18nConfig);
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)'],
};3. Server-Side Locale Detection
// src/i18n/server.ts
import { createGetLocale, createI18nRequestConfig } from '@shopkit/i18n/server';
import { i18nConfig } from './config';
export const getLocale = createGetLocale(i18nConfig);
export const i18nRequestConfig = createI18nRequestConfig({
locales: i18nConfig.locales,
defaultLocale: i18nConfig.defaultLocale,
messagesPath: './messages',
});4. Client-Side Formatting
// components/ProductPrice.tsx
'use client';
import { formatCurrency } from '@shopkit/i18n/client';
import { useLocale } from 'next-intl';
export function ProductPrice({ price }: { price: number }) {
const locale = useLocale();
return <span>{formatCurrency(price, locale, 'USD')}</span>;
}Subpath Exports
// Main entry (types and config factories only)
import { createI18nConfig, createLocaleValidator } from '@shopkit/i18n';
import type { I18nConfig, Locale } from '@shopkit/i18n';
// Server utilities
import { createGetLocale, createI18nRequestConfig, createSetLocaleCookie } from '@shopkit/i18n/server';
// Client utilities (formatters, hooks)
import { formatCurrency, formatDate, formatNumber, formatPercent, formatRelativeTime } from '@shopkit/i18n/client';
import { TranslationProvider, useTranslation } from '@shopkit/i18n/client';
// Middleware
import { createLocaleMiddleware } from '@shopkit/i18n/middleware';Configuration
createI18nConfig
import { createI18nConfig } from '@shopkit/i18n';
const config = createI18nConfig({
// Required
locales: ['en', 'es', 'fr'], // Supported locales
defaultLocale: 'en', // Fallback locale
// Optional
localeDetection: ['cookie', 'header', 'default'], // Detection order
cookieName: 'NEXT_LOCALE', // Cookie name for locale storage
});
// Returns:
config.locales; // ['en', 'es', 'fr']
config.defaultLocale; // 'en'
config.isValidLocale('es'); // true
config.isValidLocale('xx'); // falseServer Utilities
createGetLocale
import { createGetLocale } from '@shopkit/i18n/server';
const getLocale = createGetLocale(i18nConfig);
// In a server component or API route
const locale = await getLocale(); // Returns detected localecreateSetLocaleCookie
import { createSetLocaleCookie } from '@shopkit/i18n/server';
const setLocaleCookie = createSetLocaleCookie(i18nConfig);
// Set locale preference
await setLocaleCookie('es');createI18nRequestConfig
import { createI18nRequestConfig } from '@shopkit/i18n/server';
const i18nRequestConfig = createI18nRequestConfig({
locales: ['en', 'es'],
defaultLocale: 'en',
messagesPath: './messages', // Path to translation files
});
// Use with next-intl
export default i18nRequestConfig;Client Utilities
Formatting Functions
import {
formatCurrency,
formatDate,
formatNumber,
formatPercent,
formatRelativeTime,
} from '@shopkit/i18n/client';
// Currency formatting
formatCurrency(99.99, 'en-US', 'USD'); // '$99.99'
formatCurrency(99.99, 'de-DE', 'EUR'); // '99,99 €'
// Date formatting
formatDate(new Date(), 'en-US'); // 'Feb 10, 2026'
formatDate(new Date(), 'en-US', { dateStyle: 'full' }); // 'Tuesday, February 10, 2026'
// Number formatting
formatNumber(1234567.89, 'en-US'); // '1,234,567.89'
formatNumber(1234567.89, 'de-DE'); // '1.234.567,89'
// Percentage formatting
formatPercent(0.1234, 'en-US'); // '12%'
formatPercent(0.1234, 'en-US', { maximumFractionDigits: 2 }); // '12.34%'
// Relative time
formatRelativeTime(-1, 'day', 'en-US'); // 'yesterday'
formatRelativeTime(2, 'hour', 'en-US'); // 'in 2 hours'TranslationProvider
For page builder integration:
import { TranslationProvider } from '@shopkit/i18n/client';
function PageBuilderWrapper({ children, translations }) {
return (
<TranslationProvider translations={translations} locale="en">
{children}
</TranslationProvider>
);
}useTranslation Hook
'use client';
import { useTranslation } from '@shopkit/i18n/client';
function MyComponent() {
const { t, locale } = useTranslation();
return (
<div>
<h1>{t('welcome.title')}</h1>
<p>{t('welcome.description', { name: 'John' })}</p>
<span>Current locale: {locale}</span>
</div>
);
}Middleware
createLocaleMiddleware
// middleware.ts
import { createLocaleMiddleware } from '@shopkit/i18n/middleware';
import { i18nConfig } from './src/i18n/config';
export default createLocaleMiddleware(i18nConfig);
// Optionally configure matcher
export const config = {
matcher: [
// Match all paths except static files and API routes
'/((?!api|_next/static|_next/image|favicon.ico).*)',
],
};Translation Files
Create JSON translation files:
messages/
en.json
es.json
fr.json// messages/en.json
{
"common": {
"addToCart": "Add to Cart",
"checkout": "Checkout",
"continueShopping": "Continue Shopping"
},
"product": {
"outOfStock": "Out of Stock",
"inStock": "In Stock",
"price": "Price: {price}"
}
}Type Reference
I18nConfig
interface I18nConfig {
locales: readonly string[];
defaultLocale: string;
isValidLocale: (locale: string) => boolean;
}I18nConfigOptions
interface I18nConfigOptions {
locales: string[];
defaultLocale: string;
localeDetection?: LocaleDetectionMethod[];
cookieName?: string;
}
type LocaleDetectionMethod = 'cookie' | 'header' | 'default';TranslationParams
interface TranslationParams {
[key: string]: string | number | boolean;
}Integration with @shopkit/builder
import { createPageBuilder } from '@shopkit/builder';
import { TranslationProvider } from '@shopkit/i18n/client';
// Wrap page builder output with translations
function LocalizedPage({ pageContent, translations, locale }) {
return (
<TranslationProvider translations={translations} locale={locale}>
{pageContent}
</TranslationProvider>
);
}License
MIT
