6.8 KiB
i18n Translation Workflow Guide
This guide explains how to work with translations in the CommunityRule application. The app uses next-intl for managing UI text content, making it easy for content creators and contributors to update text without modifying component code.
Overview
All UI text is stored in JSON files under messages/en/. Components reference these translations using keys, allowing content to be edited independently of the codebase.
Directory Structure
messages/
en/
common.json # Shared UI strings (buttons, links, labels)
components/
heroBanner.json # HeroBanner component translations
numberedCards.json # NumberedCards component translations
askOrganizer.json # AskOrganizer component translations
featureGrid.json # FeatureGrid component translations
footer.json # Footer component translations
navigation.json # Navigation items
metadata.json # Page metadata (title, description)
index.ts # Exports all messages
Adding New Translation Keys
1. Identify the Component
Determine which component needs the translation. If it's a shared string (like a button label), add it to common.json. Otherwise, add it to the component-specific file.
2. Add the Key to the JSON File
Open the appropriate JSON file and add your translation key. Use descriptive, semantic keys:
Good:
{
"heroBanner": {
"title": "Collaborate",
"subtitle": "with clarity"
}
}
Bad:
{
"text1": "Collaborate",
"text2": "with clarity"
}
3. Use Nested Objects for Organization
Group related translations together:
{
"numberedCards": {
"title": "How CommunityRule works",
"buttons": {
"createCommunityRule": "Create CommunityRule",
"seeHowItWorks": "See how it works"
}
}
}
4. Update the Component
In your component, use the translation hook:
Server Components:
import { getTranslations } from "next-intl/server";
export default async function MyComponent() {
const t = await getTranslations();
return <h1>{t("heroBanner.title")}</h1>;
}
Client Components:
"use client";
import { useTranslations } from "next-intl";
export default function MyComponent() {
const t = useTranslations();
return <h1>{t("heroBanner.title")}</h1>;
}
Namespace-specific (recommended for component files):
const t = useTranslations("heroBanner");
return <h1>{t("title")}</h1>;
Translation Key Naming Conventions
- Use camelCase for keys:
buttonText,ariaLabel - Use descriptive names:
createCommunityRulenotbtn1 - Group by component: Each component has its own namespace
- Use nested objects for related strings:
buttons.createCommunityRule - Include context in comments: Use
_commentfields for clarity
Example:
{
"_comment": "HeroBanner component translations",
"title": "Collaborate",
"subtitle": "with clarity",
"description": "Help your community make important decisions..."
}
Extracting Strings from Components
When migrating a component to use translations:
- Identify hardcoded strings in the component
- Create translation keys in the appropriate JSON file
- Replace hardcoded strings with
t("key.path")calls - Test the component to ensure translations load correctly
Example Migration
Before:
export default function HeroBanner() {
return (
<div>
<h1>Collaborate</h1>
<p>with clarity</p>
</div>
);
}
After:
"use client";
import { useTranslations } from "next-intl";
export default function HeroBanner() {
const t = useTranslations("heroBanner");
return (
<div>
<h1>{t("title")}</h1>
<p>{t("subtitle")}</p>
</div>
);
}
Adding New Languages (Future)
When adding support for a new language:
- Create a new locale directory:
messages/es/(for Spanish, for example) - Copy the English files as a starting point
- Translate all strings in the JSON files
- Update
app/i18n/routing.tsto include the new locale - Test thoroughly to ensure all translations are present
Testing Translations
- Check for missing keys: Ensure all translation keys used in components exist in the JSON files
- Verify type safety: TypeScript will catch typos in translation keys at compile time
- Test in browser: Run the dev server and verify text displays correctly
- Check for fallbacks: Missing translations will show the key path (e.g.,
heroBanner.title)
Best Practices
For Content Creators
- Edit JSON files directly: No need to understand React or TypeScript
- Use descriptive comments: Add
_commentfields to explain context - Maintain consistency: Use the same terminology across components
- Test changes: Run the dev server to see your changes immediately
For Developers
- Use TypeScript: Translation keys are type-safe
- Namespace when possible: Use
useTranslations("namespace")for better organization - Server components first: Prefer server-side translations for better performance
- Extract incrementally: Migrate components one at a time
Common Patterns
Buttons and CTAs
{
"buttons": {
"createCommunityRule": "Create CommunityRule",
"seeHowItWorks": "See how it works"
}
}
Aria Labels
{
"ariaLabels": {
"followBluesky": "Follow us on Bluesky",
"followGitlab": "Follow us on GitLab"
}
}
Dynamic Content
For content that varies (like card text), use arrays or numbered keys:
{
"cards": {
"card1": { "text": "First step" },
"card2": { "text": "Second step" },
"card3": { "text": "Third step" }
}
}
Troubleshooting
Translation Key Not Found
If you see a key path like heroBanner.title instead of the text:
- Check the JSON file exists and has the key
- Verify the key path matches exactly (case-sensitive)
- Restart the dev server if you just added the key
TypeScript Errors
If TypeScript complains about translation keys:
- Ensure the key exists in the JSON file
- Check for typos in the key path
- Verify the namespace is correct if using
useTranslations("namespace")
Missing Translations
If text doesn't appear:
- Check the browser console for errors
- Verify the component is wrapped in
NextIntlClientProvider(for client components) - Ensure
getMessages()is called in server components
Resources
- next-intl Documentation
- Next.js Internationalization
- Component-specific translation files in
messages/en/components/
Last Updated: January 2025
Maintained by: CommunityRule Development Team