GuidesCustomization
Internationalization
Add multi-language support to @bettercone/ui components
Internationalization (i18n)
All @bettercone/ui components support internationalization through the localization prop.
Component Localization
Billing Components
import { SubscriptionCard } from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
<SubscriptionCard
authClient={authClient}
localization={{
currentPlan: "Plano Atual",
manageSubscription: "Gerenciar Assinatura",
noActiveSubscription: "Nenhuma assinatura ativa",
noActiveSubscriptionDescription: "Escolha um plano para começar",
billingCycle: "Ciclo de Cobrança",
nextBillingDate: "Próxima Data de Cobrança"
}}
onManageSubscription={handleManageSubscription}
/>Usage Components
import { ApiUsageCard } from '@bettercone/ui';
<ApiUsageCard
current={8500}
limit={10000}
localization={{
apiUsage: "Uso de API",
used: "Usado",
of: "de",
upgradePrompt: "Atualize seu plano para mais chamadas"
}}
onUpgrade={() => router.push('/pricing')}
/>Team Components
import { TeamDashboard } from '@bettercone/ui';
<TeamDashboard
organization={activeOrg}
subscription={subscription}
seatAllocation={seatData}
seatAllocationCardProps={{
localization: {
seats: "Assentos",
used: "Usados",
available: "Disponíveis",
inviteMember: "Convidar Membro"
}
}}
/>Setup next-intl (Optional)
For full app internationalization:
Install Package
pnpm add next-intlCreate Messages
{
"billing": {
"currentPlan": "Current Plan",
"manageSubscription": "Manage Subscription",
"noActiveSubscription": "No active subscription"
},
"usage": {
"apiUsage": "API Usage"
}
}{
"billing": {
"currentPlan": "Plano Atual",
"manageSubscription": "Gerenciar Assinatura",
"noActiveSubscription": "Nenhuma assinatura ativa"
},
"usage": {
"apiUsage": "Uso de API"
}
}Configure i18n
export const locales = ['en', 'pt', 'es'] as const;
export type Locale = (typeof locales)[number];
export const defaultLocale: Locale = 'en';Update Layout
import { NextIntlClientProvider } from 'next-intl';
import { getMessages } from 'next-intl/server';
export default async function LocaleLayout({
children,
params: { locale },
}) {
const messages = await getMessages();
return (
<NextIntlClientProvider messages={messages}>
{children}
</NextIntlClientProvider>
);
}Usage with next-intl
'use client';
import { useTranslations } from 'next-intl';
import { SubscriptionCard } from '@bettercone/ui';
import { authClient } from '@/lib/auth-client';
export function BillingPage() {
const t = useTranslations('billing');
return (
<SubscriptionCard
authClient={authClient}
localization={{
currentPlan: t('currentPlan'),
manageSubscription: t('manageSubscription'),
noActiveSubscription: t('noActiveSubscription')
}}
onManageSubscription={handleManageSubscription}
/>
);
}Language Switcher
'use client';
import { usePathname, useRouter } from 'next/navigation';
import { useLocale } from 'next-intl';
export function LanguageSwitcher() {
const locale = useLocale();
const router = useRouter();
const pathname = usePathname();
const switchLocale = (newLocale: string) => {
const path = pathname.replace(`/${locale}`, `/${newLocale}`);
router.push(path);
};
return (
<select value={locale} onChange={(e) => switchLocale(e.target.value)}>
<option value="en">English</option>
<option value="pt">Português</option>
<option value="es">Español</option>
</select>
);
}Date & Number Formatting
import { useFormatter } from 'next-intl';
export function FormattedData() {
const format = useFormatter();
return (
<div>
<p>{format.dateTime(new Date(), { dateStyle: 'long' })}</p>
<p>{format.number(1234.56, { style: 'currency', currency: 'USD' })}</p>
</div>
);
}Email Templates
import { useTranslations } from 'next-intl';
export function WelcomeEmail({ locale }: { locale: string }) {
const t = useTranslations('emails.welcome');
return (
<html lang={locale}>
<body>
<h1>{t('title')}</h1>
<p>{t('message')}</p>
</body>
</html>
);
}Use namespaces to organize translations by feature or page.