frontend-i18n
SKILL.md
Frontend i18n Pattern
This skill outlines the standard pattern for implementing internationalization in Eridu frontend applications. We use Paraglide JS for type-safe, lightweight i18n.
Architecture
- Tooling:
@inlang/paraglide-js(v2+) - Configuration: Defined in
project.inlangat the app root. - Message Storage: JSON files in
src/i18n/messages/{lang}.json. - Output: Generated code in
src/paraglide(typically gitignored). - Shared Library:
@eridu/i18n(workspace package) for common translations and locale definitions.
Workflow: Adding Translations
- Locate the Message File: Open
src/i18n/messages/en.json(English is the source language). - Add Your Key: Add a new key-value pair. You can nest keys for better organization.
{ "dashboard": { "title": "My Dashboard", "welcomeUser": "Welcome back, {name}!" } } - Automatic Compilation: If the dev server (
pnpm dev) is running, Paraglide will automatically detect changes and regeneratesrc/paraglide. - Use in Component: Import the messages and use them.
Usage in Components
1. App-Specific Messages
Import from the generated local alias (usually configured as @/paraglide/messages).
import * as m from '@/paraglide/messages';
export function Dashboard({ userName }) {
// Simple message
const title = m['dashboard.title']();
// Message with parameters
const welcome = m['dashboard.welcomeUser']({ name: userName });
return (
<div>
<h1>{title}</h1>
<p>{welcome}</p>
</div>
);
}
Note: Because we use nested keys in JSON (e.g.
dashboard.title), Paraglide exports them with the exact path as the name. Since these contain dots, you must use bracket notation:m['dashboard.title']().
2. Shared Messages
For common terms (Save, Cancel, Table headers, etc.), use the shared @eridu/i18n package to ensure consistency.
import * as sharedM from '@eridu/i18n';
// Usage
<Button>{sharedM['common.save']()}</Button>
<Button>{sharedM['common.cancel']()}</Button>
3. Locale Management
Use the LocaleEnum and helper functions from @eridu/i18n when dealing with locale logic (e.g., language switchers).
import { LocaleEnum, LOCALE_LABELS } from '@eridu/i18n';
// Access available locales and their labels
{Object.entries(LOCALE_LABELS).map(([code, label]) => (
<option key={code} value={code}>{label}</option>
))}
Best Practices
- Grouping: Group messages by feature or page (e.g.,
admin,auth,settings). - Naming: Use
camelCasefor keys (e.g.,welcomeUsernotwelcome-user). - Parameters: Use named parameters
{param}instead of positional arguments. - No Hardcoding: Never hardcode user-facing strings in components. Always extract them to
en.json. - Review Shared: Before adding a generic term like "Submit" or "Error", check
packages/i18n/messages/en.jsonto see if it already exists in the shared library.
Troubleshooting
- "Property '...' does not exist on type...": Run
pnpm devorpnpm buildto force regeneration of the Paraglide files. - Missing Translations: Ensure you've added the key to
en.json. Other languages (e.g.,zh-TW) can be filled in later or via translation tools.
Weekly Installs
1
Repository
allenlin90/erid…servicesGitHub Stars
1
First Seen
Jan 21, 2026
Installed on
windsurf1
trae1
opencode1
antigravity1
gemini-cli1