AgentSkillsCN

i18n-page

借助next-intl插件,结合服务器端组件与客户端组件模式,为Next.js页面增添国际化支持,并在arolariu.ro前端遵循RFC 1003标准,支持英语、罗马尼亚语和法语三种语言环境。

SKILL.md
--- frontmatter
name: i18n-page
description: 'Adds internationalization to Next.js pages using next-intl with server and client component patterns, supporting en/ro/fr locales following RFC 1003 for the arolariu.ro frontend.'

i18n Page Setup

Adds internationalization support to Next.js pages using next-intl.

When to Use

  • Creating a new page with user-facing text
  • Adding translations to an existing page
  • Converting hardcoded strings to i18n
  • Adding a new locale

Supported Locales

LocaleLanguageFile
enEnglishsites/arolariu.ro/messages/en.json
roRomaniansites/arolariu.ro/messages/ro.json
frFrenchsites/arolariu.ro/messages/fr.json

Message Structure

Messages are organized by namespace (usually matching the page/feature):

json
{
  "Namespace": {
    "__metadata__": {
      "title": "Page Title for SEO",
      "description": "Page description for SEO and social sharing"
    },
    "title": "Display Title",
    "subtitle": "Subtitle text",
    "actions": {
      "submit": "Submit",
      "cancel": "Cancel"
    },
    "errors": {
      "required": "This field is required",
      "invalid": "Invalid input"
    }
  }
}

Conventions:

  • __metadata__ — Reserved for SEO metadata (used by generateMetadata())
  • Nest by feature: actions.submit, errors.required
  • Use descriptive keys: invoiceCount not count
  • Keep keys in camelCase

Server Component Pattern

Use getTranslations() for Server Components:

tsx
// page.tsx (Server Component)
import {getTranslations} from "next-intl/server";
import {createMetadata} from "@/metadata";

export async function generateMetadata(): Promise<Metadata> {
  const t = await getTranslations("Namespace.__metadata__");
  return createMetadata({
    title: t("title"),
    description: t("description"),
  });
}

export default async function Page(): Promise<React.JSX.Element> {
  const t = await getTranslations("Namespace");
  return (
    <main>
      <h1>{t("title")}</h1>
      <p>{t("subtitle")}</p>
    </main>
  );
}

Client Component Pattern

Use useTranslations() hook for Client Components:

tsx
// island.tsx (Client Component)
"use client";

import {useTranslations} from "next-intl";

export default function RenderScreen(): React.JSX.Element {
  const t = useTranslations("Namespace");

  return (
    <main>
      <h1>{t("title")}</h1>
      <button>{t("actions.submit")}</button>
    </main>
  );
}

Rich Text and Interpolation

tsx
// Interpolation
t("greeting", {name: "Alex"})
// Message: "Hello, {name}!"

// Pluralization
t("itemCount", {count: items.length})
// Message: "You have {count, plural, =0 {no items} one {# item} other {# items}}"

// Rich text (HTML tags)
t.rich("terms", {
  link: (chunks) => <a href="/terms">{chunks}</a>,
})
// Message: "By continuing you agree to our <link>terms</link>"

Workflow

  1. Define namespace matching the page or feature name
  2. Add keys to messages/en.json first (primary locale)
  3. Add translations to messages/ro.json and messages/fr.json
  4. Use in Server Components with getTranslations()
  5. Use in Client Components with useTranslations()
  6. Use in metadata with generateMetadata() + createMetadata()
  7. Verify all 3 locales have matching keys

Checklist

  • Keys added to all 3 locale files (en.json, ro.json, fr.json)
  • __metadata__ namespace includes title and description
  • Server Components use getTranslations()
  • Client Components use useTranslations()
  • No hardcoded user-facing strings in JSX
  • Interpolation used for dynamic values (not string concatenation)
  • Keys follow camelCase naming
  • Nested structure matches feature organization
  • npm run generate:i18n runs without errors