Documentation

Adding Translations

Step-by-step guide for translating UI copy in Motoko Base features using next-intl message catalogs.

Open inChatGPT (opens in a new tab)Claude (opens in a new tab)Cursor (opens in a new tab)Step-by-step guide for translating UI copy in Motoko Base features using next-intl message catalogs.

This guide walks through adding or updating translated strings in Motoko Base. Read Internationalization first if you need the routing and locale-resolution overview.

Quick checklist

When you add UI copy to a feature:

  1. Create or extend a JSON namespace under messages/en/
  2. Export it from messages/en/index.ts
  3. Mirror the same keys in messages/es/, messages/de/, messages/fr/, and messages/pt-BR/
  4. Replace hardcoded strings in components with useTranslations() or getTranslations()
  5. Use formatDate() / formatNumber() from @/lib/i18n/format for locale-aware formatting

1. Define English messages

Add a namespace file for your feature. Follow the existing pattern — flat or nested keys, consistent naming:

// messages/en/my-feature.json
{
  "page": {
    "title": "My Feature",
    "description": "Manage your items."
  },
  "actions": {
    "create": "Create item",
    "delete": "Delete"
  },
  "empty": "No items yet."
}

Register the namespace in the locale index:

// messages/en/index.ts
import myFeature from "./my-feature.json";

const messages = {
  // …existing namespaces
  myFeature,
};

export default messages;

Use camelCase for the export key (myFeature) — that becomes the namespace passed to useTranslations("myFeature").

2. Mirror keys in every locale

Each locale folder must expose the same key structure. Untranslated locales can temporarily copy English values, but every key must exist to avoid runtime missing-message errors.

Repeat for messages/es/index.ts, messages/de/index.ts, messages/fr/index.ts, and messages/pt-BR/index.ts.

Tip: when iterating quickly, copy the English JSON to other locales first, then translate in a second pass.

3. Use translations in a client component

Most feature pages are client components. Import useTranslations from next-intl:

"use client";

import { useTranslations } from "next-intl";
import { PageHeader } from "@/components/layout/page-header";

export function MyFeaturePage() {
  const t = useTranslations("myFeature");

  return (
    <div className="p-6 md:p-8">
      <PageHeader
        title={t("page.title")}
        description={t("page.description")}
      />
      <p>{t("empty")}</p>
    </div>
  );
}

Interpolation

ICU-style placeholders work out of the box:

{
  "welcome": {
    "title": "Welcome back, {name}"
  }
}
t("welcome.title", { name: firstName })

Dynamic keys

When keys come from data (e.g. stat IDs), cast carefully or use a typed map — see src/features/dashboard/components/chat/chat-page.tsx for a reference pattern.

4. Use translations on the server

For Server Components, layouts, and generateMetadata:

import { getTranslations } from "next-intl/server";

export async function generateMetadata() {
  const t = await getTranslations("myFeature");
  return {
    title: t("page.title"),
    description: t("page.description"),
  };
}

export default async function Page() {
  const t = await getTranslations("myFeature");
  return <h1>{t("page.title")}</h1>;
}

Dashboard routes resolve locale via AppIntlProvider in src/app/dashboard/layout.tsx — you do not need a [locale] segment in the URL.

5. Format dates and numbers

Pass the active locale to shared helpers:

import { useLocale } from "next-intl";
import { formatDate } from "@/lib/i18n/format";

const locale = useLocale();
formatDate(new Date(), locale, { dateStyle: "medium" });

On the server, use getLocale() from next-intl/server.

6. Translate dashboard navigation

Dashboard nav labels come from messages/{locale}/dashboard.json, merged in src/features/dashboard/components/sidebar/sidebar.tsx. When you add a new dashboard section:

  1. Add the nav label key to dashboard.json in every locale
  2. Add the route in src/features/dashboard/config/nav.ts (structure only — icon and href)
  3. Wire the label in the sidebar via useTranslations("dashboard")

7. Translate marketing content

Do not put translatable marketing copy in src/features/marketing/config.ts (deprecated for copy). Instead:

  1. Add structural data in src/features/marketing/marketing-structure.ts (id, hrefs, tech refs, ordering)
  2. Add display strings in messages/{locale}/marketing.json under the matching id

useMarketingContent() merges structure + copy automatically.

8. Translate documentation pages

Docs are MDX files, not JSON namespaces. For each new page:

content/docs/my-section/my-page.mdx       # English
content/docs/my-section/my-page.de.mdx    # German
content/docs/my-section/my-page.es.mdx    # Spanish
content/docs/my-section/my-page.fr.mdx    # French
content/docs/my-section/my-page.pt-BR.mdx # Brazilian Portuguese

List the slug once in content/docs/my-section/meta.json. Localized section titles go in meta.de.json, etc.

Docs chrome strings (Previous, Next, Copy MD) live in messages/{locale}/docs.json.

9. Translate emails (optional)

Email templates can load locale-specific copy via src/lib/i18n/email-messages.ts. Transactional emails currently default to English unless you extend the resolver — see src/lib/i18n/resolve-email-locale.ts when you need localized verification or password-reset emails.

Common mistakes

MistakeFix
Hardcoded English in JSXMove string to JSON namespace
Missing key in one localeAdd the key to all five locale files
Using next/link on marketing pagesUse Link from @/i18n/navigation
Hardcoded "en-US" in IntlUse @/lib/i18n/format helpers
Putting marketing copy in config.tsUse marketing-structure.ts + marketing.json

Reference implementations

Copy patterns from these translated areas:

FeatureNamespaceComponent
Chat (dashboard)dashboardsrc/features/dashboard/components/chat/chat-page.tsx
Appearance settingssettingssrc/features/dashboard/components/settings/appearance-settings-page.tsx
Authauthsrc/components/ui/auth-section-3.tsx
Marketingmarketingsrc/features/marketing/lib/use-marketing-content.ts
Dashboard navdashboardsrc/features/dashboard/components/sidebar/sidebar.tsx

Next steps

Internationalization — Routing, locale resolution, and SEO.

Project structure — Feature folder layout.

Configuration — Nav, billing, and product config files.