LogoTanStarter Docs
LogoTanStarter Docs
HomepageIntroductionCodebaseGetting StartedEnvironmentsDeployment
Configuration

Integrations

CloudflareDatabaseStorageAuthenticationEmail
Payment
AINewsletterNotificationsAnalyticsChatbox

Customization

MetadataPagesInternationalizationLanding PageBlogComponentsUser ManagementAPI Key Management

Codebase

Project StructureFormatting & LintingEditor SetupUpdating the Codebase

Projects

MkImage
X (Twitter)

Internationalization

Learn how to configure languages, add translated messages, and maintain localized content in TanStarter

TanStarter includes multilingual support powered by Paraglide JS. English is the base locale, and Chinese uses a URL prefix:

  • English page: /about
  • Chinese page: /zh/about

Related Files

Internationalization mainly touches these files:

settings.json
en.json
zh.json
middleware.ts
locale.ts
locale-switcher.tsx
FilePurpose
project.inlang/settings.jsonConfigures the base locale and supported locales
project.inlang/messages/en.jsonEnglish messages
project.inlang/messages/zh.jsonChinese messages
src/lib/locale.tsExports locale config, URL helpers, and message parsing helpers
src/locale/middleware.tsConnects Paraglide middleware so server requests know the current locale
src/locale/paraglide/Generated Paraglide runtime code; do not edit manually
src/components/layout/locale-switcher.tsxLanguage switcher used by the navbar and menus

Do not edit files under src/locale/paraglide/ manually. They are generated from project.inlang/messages/*.json.

Add UI Messages

Short UI messages such as buttons, form labels, error messages, email subjects, and component text live in project.inlang/messages/*.json.

1. Add Message Keys

Add the same keys to both English and Chinese files:

project.inlang/messages/en.json
{
  "settings_profile_title": "Profile",
  "settings_profile_description": "Update your public profile information."
}
project.inlang/messages/zh.json
{
  "settings_profile_title": "ä¸Ēäēēčĩ„æ–™",
  "settings_profile_description": "更新äŊ įš„å…Ŧåŧ€ä¸Ēäēēčĩ„æ–™äŋĄæ¯ã€‚"
}

Use module prefixes for message keys, for example:

  • auth_login_title
  • pricing_plan_pro_name
  • dashboard_empty_state_title
  • mail_verify_email_subject

This keeps sorting, searching, and maintenance predictable.

2. Use Messages in Components

Read messages from the generated Paraglide m object:

src/components/settings/profile-card.tsx
import { m } from '@/locale/paraglide/messages';

export function ProfileCard() {
  return (
    <section>
      <h2>{m.settings_profile_title()}</h2>
      <p>{m.settings_profile_description()}</p>
    </section>
  );
}

During development, pnpm dev recompiles the Paraglide runtime automatically. After the new key is generated, you can call it as m.your_key().

3. Sort and Check Keys

After adding or changing messages, run:

pnpm locale:sort
pnpm locale:check
  • pnpm locale:sort sorts keys in all locale JSON files
  • pnpm locale:check verifies key parity across locale files

If a key exists in en.json but is missing from zh.json, the check command will report it.

Add Markdown Translations

Long-form content such as blog posts, changelog entries, and legal pages is maintained as Markdown. English uses the base filename, and Chinese adds .zh before .md.

content/blog/getting-started.md
content/blog/getting-started.zh.md

content/changelog/v1.0.0.md
content/changelog/v1.0.0.zh.md

content/pages/privacy.md
content/pages/privacy.zh.md

For example, add a Chinese version of a blog post:

content/blog/my-first-post.md
---
title: My First Post
description: A short introduction to the product.
date: 2026-01-15
category: Tutorial
image: https://example.com/cover.jpg
---

English content...
content/blog/my-first-post.zh.md
---
title: æˆ‘įš„įŦŦä¸€į¯‡æ–‡įĢ 
description: ä¸€į¯‡įŽ€įŸ­įš„äē§å“äģ‹įģã€‚
date: 2026-01-15
category: æ•™į¨‹
image: https://example.com/cover.jpg
---

中文内厚...

TanStarter treats these files as different locale versions of the same slug:

  • English: /blog/my-first-post
  • Chinese: /zh/blog/my-first-post

The .zh suffix is only used to identify the locale. It does not appear in the final URL.

Add a New Language

To add another language, such as Japanese ja, update the locale config and add a message file.

1. Update Inlang Settings

Add the new locale to project.inlang/settings.json:

project.inlang/settings.json
{
  "baseLocale": "en",
  "locales": ["en", "zh", "ja"]
}

2. Add a Message File

Copy an existing locale file and translate it:

project.inlang/messages/ja.json

The new file must contain the same keys as en.json and zh.json.

3. Update Locale List

Add the language name, flag, and hreflang in src/lib/locale.ts:

src/lib/locale.ts
export const localeConfig = {
  en: {
    flag: 'đŸ‡ē🇸',
    name: 'English',
    hreflang: 'en',
  },
  zh: {
    flag: 'đŸ‡¨đŸ‡ŗ',
    name: '中文',
    hreflang: 'zh-CN',
  },
  ja: {
    flag: 'đŸ‡¯đŸ‡ĩ',
    name: 'æ—ĨæœŦčĒž',
    hreflang: 'ja',
  },
} satisfies Record<Locale, LocaleConfig>;

The locale switcher reads locales and localeConfig, so the new language appears automatically.

4. Add Localized Content Files

If blog posts, changelog entries, or legal pages should support the new language, create localized Markdown files with the same naming pattern:

content/blog/getting-started.ja.md
content/changelog/v1.0.0.ja.md
content/pages/privacy.ja.md

If the new locale is not en or zh, also update the locale suffix detection in content-collections.ts.

Commands

pnpm dev             # Start dev server and compile the locale runtime automatically
pnpm build           # Build for production and compile the locale runtime

pnpm locale:sort     # Sort message keys
pnpm locale:check    # Check message key parity across locales
pnpm locale:compile  # Compile the Paraglide runtime manually

In daily development, pnpm dev is usually enough. After adding messages, run pnpm locale:sort and pnpm locale:check to catch missing translation keys.

Next Steps

Pages

Create and maintain pages

Blog

Add localized blog content

Website Config

Configure core website settings

Metadata

Configure SEO and page metadata

Table of Contents

Related Files
Add UI Messages
1. Add Message Keys
2. Use Messages in Components
3. Sort and Check Keys
Add Markdown Translations
Add a New Language
1. Update Inlang Settings
2. Add a Message File
3. Update Locale List
4. Add Localized Content Files
Commands
Next Steps