Internationalization

The site supports three locales with full content and UI translations.

Supported Locales

Code

Label

Role

en

English

Default locale

zh-CN

简体中文

Simplified Chinese

ja

日本語

Japanese

Configuration lives in src/lib/i18n.ts:

export const locales = ["en", "zh-CN", "ja"] as const;
export type Locale = (typeof locales)[number];
export const defaultLocale: Locale = "en";

Locale Routing

All routes are prefixed with a locale segment (e.g., /en/blog, /zh-CN/blog). The middleware in src/middleware.ts enforces this:

  1. Pass-through — requests to /_next, /api, /fonts, and static files (paths containing .) are not processed

  2. Valid locale — if the first path segment is en, zh-CN, or ja, the request passes through unchanged

  3. Detection — the Accept-Language header is parsed; the first matching locale is selected, falling back to en

  4. Redirect — the user is redirected to /{detectedLocale}{path}

The middleware matcher excludes _next, api, fonts, favicon.ico, robots.txt, and sitemap.xml.

Translation System

The t() function provides UI translations with placeholder support:

t(key: string, locale: Locale, params?: Record<string, string | number>): string

Behaviour:

  • Returns the translated string for the given locale

  • Falls back to en if the key is missing in the requested locale

  • Returns the raw key if no translation exists at all

  • Supports {placeholder} interpolation: t("pageOf", "zh-CN", { page: 1, total: 5 })"第 1 页,共 5 页"

Translation Keys

Approximately 80 keys are defined, organized by feature area:

Navbar: brand, blog, gallery, solutions, pricing, developer, login, adminLogin

Blog: readMore, featuredArticles, allArticles, noArticles, noArticlesTitle, noArticlesDesc, publishedOn, by, filterByCategory, filterByTag (with {tag}), allCategories, searchPlaceholder, categories, tags, share, copyLink, linkCopied, seeMore, home, lastUpdated (with {date})

Gallery: mediaGallery, noGalleries, viewGallery, photos

Pagination: previous, next, pageOf (with {page} and {total})

Footer: Section headings (products, platform, services, company), link labels, legal links (termsOfService, disclaimer, privacyPolicy, manageCookies), copyright (with {year}), icp

Cookie consent: Bar text and buttons, settings dialog headings and toggle descriptions (Essential, Performance, Advertising)

Theme: lightMode, darkMode

Errors: backToHome, httpErrorCode, errorSuggest, tryAgain, plus title and description pairs for HTTP status codes 400, 401, 403, 404, 405, 408, 429, 500, 502, 503, 504, and a default

SEO: siteTitle, siteDescription

CMS Content Localization

Strapi articles and categories are localized server-side. The seed scripts create English content first, then add zh-CN and ja localizations via Strapi’s /actions/localize endpoint.

The fetchPublicArticles and fetchCategories functions pass the locale query parameter to Strapi, which returns the appropriate translation.

Adding a New Language

  1. Add the locale code to the locales array in src/lib/i18n.ts

  2. Add a label to localeLabels (e.g., "ko": "한국어")

  3. Add translations for all keys in the translations object

  4. Add CMS localizations in Strapi for existing articles and categories

  5. Test the middleware detects the new locale from Accept-Language

The LocaleSwitcher component automatically renders all entries from localeLabels, so no component changes are needed.