@bettercone/ui
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-intl

Create Messages

messages/en.json
{
  "billing": {
    "currentPlan": "Current Plan",
    "manageSubscription": "Manage Subscription",
    "noActiveSubscription": "No active subscription"
  },
  "usage": {
    "apiUsage": "API Usage"
  }
}
messages/pt.json
{
  "billing": {
    "currentPlan": "Plano Atual",
    "manageSubscription": "Gerenciar Assinatura",
    "noActiveSubscription": "Nenhuma assinatura ativa"
  },
  "usage": {
    "apiUsage": "Uso de API"
  }
}

Configure i18n

i18n/config.ts
export const locales = ['en', 'pt', 'es'] as const;
export type Locale = (typeof locales)[number];

export const defaultLocale: Locale = 'en';

Update Layout

app/[locale]/layout.tsx
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

emails/welcome.tsx
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.

Resources