
Security News
Axios Maintainer Confirms Social Engineering Attack Behind npm Compromise
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.
@shopkit/i18n
Advanced tools
Internationalization utilities for Next.js storefronts with next-intl integration.
npm install @shopkit/i18n
# or
bun add @shopkit/i18n
npm install next next-intl react
// 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;
// middleware.ts
import { createLocaleMiddleware } from '@shopkit/i18n/middleware';
import { i18nConfig } from './src/i18n/config';
export default createLocaleMiddleware(i18nConfig);
export const config = {
matcher: ['/((?!api|_next|.*\\..*).*)'],
};
// 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',
});
// 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>;
}
// 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';
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'); // false
import { createGetLocale } from '@shopkit/i18n/server';
const getLocale = createGetLocale(i18nConfig);
// In a server component or API route
const locale = await getLocale(); // Returns detected locale
import { createSetLocaleCookie } from '@shopkit/i18n/server';
const setLocaleCookie = createSetLocaleCookie(i18nConfig);
// Set locale preference
await setLocaleCookie('es');
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;
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'
For page builder integration:
import { TranslationProvider } from '@shopkit/i18n/client';
function PageBuilderWrapper({ children, translations }) {
return (
<TranslationProvider translations={translations} locale="en">
{children}
</TranslationProvider>
);
}
'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.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).*)',
],
};
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}"
}
}
interface I18nConfig {
locales: readonly string[];
defaultLocale: string;
isValidLocale: (locale: string) => boolean;
}
interface I18nConfigOptions {
locales: string[];
defaultLocale: string;
localeDetection?: LocaleDetectionMethod[];
cookieName?: string;
}
type LocaleDetectionMethod = 'cookie' | 'header' | 'default';
interface TranslationParams {
[key: string]: string | number | boolean;
}
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>
);
}
MIT
FAQs
Internationalization utilities for Next.js storefronts
We found that @shopkit/i18n demonstrated a healthy version release cadence and project activity because the last version was released less than a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?

Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.

Security News
Axios compromise traced to social engineering, showing how attacks on maintainers can bypass controls and expose the broader software supply chain.

Security News
Node.js has paused its bug bounty program after funding ended, removing payouts for vulnerability reports but keeping its security process unchanged.

Security News
The Axios compromise shows how time-dependent dependency resolution makes exposure harder to detect and contain.