# Using translations in your Tanstack Start Supabase project

> Learn how to use translations in Server Components, Client Components, and Server Actions with Makerkit's next-intl-based translation system.

*Canonical: https://makerkit.dev/docs/tanstack-supabase/translations/using-translations*

---

Makerkit uses `use-intl` for internationalization, abstracted behind the `@kit/i18n` package. This abstraction ensures future changes to the translation library won't break your code.

{% sequence title="Steps to use translations" description="Learn how to use translations in your Tanstack Start Supabase project." %}

[Understand the translation architecture](#translation-architecture)

[Use translations in components](#using-translations-in-components)

[Use translations in client components](#using-translations-in-client-components)

[Work with translation keys and namespaces](#working-with-translation-keys)

{% /sequence %}

## Translation Architecture

The translation system supports:

1. **Components** - Access translations via the `Trans` component from `@kit/ui/trans` or the `useTranslations` hook from `use-intl`
2. **Server functions & route `head()`** - Build a translator with `getTranslator` from `~/lib/i18n/translator`
3. **Cookie-based locale** - The active locale is stored in a `locale` cookie and resolved server-side on each request

Translation files are stored in `packages/i18n/src/messages/{locale}/`. The default structure includes:

```
packages/i18n/src/messages/
└── en/
    ├── common.json      # Shared UI strings
    ├── auth.json        # Authentication flows
    ├── account.json     # Account settings
    ├── teams.json       # Team management
    ├── billing.json     # Billing and subscriptions
    └── marketing.json   # Marketing pages
```

## Using Translations in Components

Route components render translated strings with the `Trans` component from `@kit/ui/trans`.

### Using the Trans Component

The `Trans` component renders translated strings directly in JSX:

```tsx title="apps/web/src/routes/_authenticated/dashboard/index.tsx"
import { Trans } from '@kit/ui/trans';

function HomePage() {
  return (
    <div>
      <h1>
        <Trans i18nKey="common.homeTabLabel" />
      </h1>

      <p>
        <Trans i18nKey="common.homeTabDescription" />
      </p>
    </div>
  );
}
```

**Import the `Trans` component from `@kit/ui/trans`** - the Makerkit wrapper reads the `use-intl` context under the hood.

### Using Translations in a Route `head()`

To set the document `<title>` from the translation catalog, build a translator with `getTranslator` (it runs outside React render, so it is synchronous):

```tsx title="apps/web/src/routes/_authenticated/dashboard/index.tsx"
import { createFileRoute } from '@tanstack/react-router';

import { getTranslator } from '#/lib/i18n/translator';

export const Route = createFileRoute('/dashboard/')({
  head: () => ({
    meta: [{ title: getTranslator()('common.homeTabLabel') }],
  }),
});
```

## Using Translations in Client Components

Components receive translations through the `I18nProvider` (a wrapper around `use-intl`'s `IntlProvider`) mounted in the root providers.

### Using the useTranslations Hook

The `useTranslations` hook provides access to the translation function:

```tsx title="components/my-component.tsx"
import { useTranslations } from 'use-intl';

export function MyComponent() {
  const t = useTranslations();

  return (
    <button onClick={() => alert(t('common.cancel'))}>
      {t('common.cancel')}
    </button>
  );
}
```

### Specifying Namespaces

Load specific namespaces for scoped access:

```tsx title="components/billing-component.tsx"
import { useTranslations } from 'use-intl';

export function BillingComponent() {
  const t = useTranslations('billing');

  // Keys without namespace prefix
  return <span>{t('subscriptionSettingsTabLabel')}</span>;
}
```

### Using Trans in Client Components

The `Trans` component also works in Client Components:

```tsx title="components/welcome-message.tsx"
import { Trans } from '@kit/ui/trans';

export function WelcomeMessage() {
  return (
    <p>
      <Trans i18nKey="common.signedInAs" />
    </p>
  );
}
```

## Working with Translation Keys

### Key Format

Translation keys use dot notation `namespace.keyPath`:

```tsx
// Simple key
<Trans i18nKey="common.cancel" />

// Nested key
<Trans i18nKey="common.routes.home" />

// With namespace in useTranslations
const t = useTranslations('auth');
t('signIn'); // Equivalent to 'auth.signIn'
```

### Interpolation

Pass dynamic values to translations using single braces:

```json title="packages/i18n/src/messages/en/common.json"
{
  "pageOfPages": "Page {page} of {total}",
  "showingRecordCount": "Showing {pageSize} of {totalCount} rows"
}
```

```tsx
import { Trans } from '@kit/ui/trans';

// Using Trans component
<Trans
  i18nKey="common.pageOfPages"
  values={{ page: 1, total: 10 }}
/>

// Using t function
const t = useTranslations();
t('common.showingRecordCount', { pageSize: 25, totalCount: 100 });
```

### Nested Translations

Access nested objects with dot notation:

```json title="packages/i18n/src/messages/en/common.json"
{
  "routes": {
    "home": "Home",
    "account": "Account",
    "billing": "Billing"
  },
  "roles": {
    "owner": {
      "label": "Owner"
    },
    "member": {
      "label": "Member"
    }
  }
}
```

```tsx
<Trans i18nKey="common.routes.home" />
<Trans i18nKey="common.roles.owner.label" />
```

### HTML in Translations

For translations containing HTML, use the `Trans` component with components prop:

```json title="packages/i18n/src/messages/en/auth.json"
{
  "clickToAcceptAs": "Click the button below to accept the invite as <b>{email}</b>"
}
```

```tsx
<Trans
  i18nKey="auth.clickToAcceptAs"
  values={{ email: user.email }}
  components={{ b: <strong /> }}
/>
```

## Common Patterns

### Conditional Translations

```tsx
import { useTranslations, useLocale } from 'use-intl';

const t = useTranslations();
const locale = useLocale();

// Check current language
if (locale === 'en') {
  // English-specific logic
}

// Translate with values
const label = t('optional.key', { name: 'World' });
```

### Pluralization

use-intl uses ICU message format for pluralization:

```json title="packages/i18n/src/messages/en/common.json"
{
  "itemCount": "{count, plural, one {# item} other {# items}}"
}
```

```tsx
t('common.itemCount', { count: 1 }); // "1 item"
t('common.itemCount', { count: 5 }); // "5 items"
```

### Date and Number Formatting

Use the standard `Intl` APIs alongside translations:

```tsx
const locale = useLocale();

const formattedDate = new Intl.DateTimeFormat(locale).format(date);
const formattedNumber = new Intl.NumberFormat(locale).format(1234.56);
```

## Server Functions

Prefer returning translation **keys** from server functions and rendering them on the client with `Trans` — this keeps translation concerns on the client. When you do need to resolve a string server-side, build a translator with `getTranslator`:

```tsx title="apps/web/src/lib/server/example.functions.ts"
import { createServerFn } from '@tanstack/react-start';

import { authActionMiddleware } from '@kit/action-middleware';

import { getTranslator } from '#/lib/i18n/translator';

export const myFunction = createServerFn({ method: 'POST' })
  .middleware(authActionMiddleware)
  .handler(async () => {
    const t = getTranslator();

    // Use translations
    const message = t('common.genericServerError');

    return { error: message };
  });
```

## Environment Variables

Configure language behavior with these environment variables:

```bash title=".env"
# Default language (fallback when user preference unavailable)
VITE_DEFAULT_LOCALE=en
```

The active locale is resolved server-side on each request (via the `detectLocale` server function in `apps/web/src/lib/i18n/i18n.functions.ts`) using the following precedence:

1. The `locale` cookie (set when the user picks a language)
2. The browser's `Accept-Language` header
3. Falls back to `VITE_DEFAULT_LOCALE`

## Troubleshooting

### Missing Translation Warning

If you see a missing translation warning, check:

1. The key exists in your translation file
2. All interpolation values are provided
3. The namespace is registered in the message registry at `packages/i18n/src/messages/index.ts`

### Translations Not Updating

If translations don't update after editing JSON files:

1. Restart the development server
2. Clear browser cache
3. Check for JSON syntax errors in translation files

{% faq
   title="Frequently Asked Questions"
   items=[
     {"question": "How do I switch languages programmatically?", "answer": "Call useChangeLocale() from @kit/i18n/navigation with the new locale. It writes the locale cookie and reloads so the server re-resolves the locale on the next render."},
     {"question": "Why are my translations not showing?", "answer": "Check that the namespace is registered in the message registry in packages/i18n/src/messages/index.ts, the JSON file exists in packages/i18n/src/messages/{locale}/, and verify the key uses dot notation (namespace.key not namespace:key)."},
     {"question": "Can I use translations in a server function?", "answer": "Yes, import getTranslator from ~/lib/i18n/translator and call it to build a synchronous t() function. Prefer returning translation keys from server functions and rendering them on the client with Trans where possible."},
     {"question": "What's the difference between Trans component and useTranslations hook?", "answer": "Trans is a React component that renders translated strings directly in JSX, supporting interpolation and HTML. useTranslations is a hook that returns a t() function for programmatic access to translations, useful for attributes, conditionals, or non-JSX contexts."},
     {"question": "How do I handle missing translations during development?", "answer": "Missing translations log warnings to the console. Use [TODO] prefixes in your JSON values to make untranslated strings searchable. The system falls back to the key name if no translation is found."}
   ]
/%}

## Related Documentation

- [Adding Translations](/docs/tanstack-supabase/translations/adding-translations) - Add new languages and namespaces
- [Language Selector](/docs/tanstack-supabase/translations/language-selector) - Let users change their language
- [Email Translations](/docs/tanstack-supabase/translations/email-translations) - Translate email templates
