Skip to content

Commit 62cb038

Browse files
ericyangpanclaude
andcommitted
refactor(skill/i18n): improve workflow documentation and scripts
Enhanced documentation and refactored sync/translate scripts for better clarity and maintainability. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent f96a87a commit 62cb038

File tree

3 files changed

+425
-171
lines changed

3 files changed

+425
-171
lines changed

.claude/skills/i18n/SKILL.md

Lines changed: 131 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -5,31 +5,57 @@ description: Internationalization management tool for syncing and translating la
55

66
# I18n Management Skill
77

8-
Manage multilingual content in the `locales/` directory. This skill provides tools to synchronize language files with the English reference and assist with translations.
8+
Manage multilingual content in the `translations/` directory. This skill provides tools to synchronize language files with the English reference and assist with translations.
99

1010
## Overview
1111

12-
This project uses `next-intl` for internationalization with JSON message files stored in `locales/`:
12+
This project uses `next-intl` for internationalization with JSON message files organized in `translations/`:
1313

14-
- `locales/en.json` - English (source of truth)
15-
- `locales/zh-Hans.json` - Simplified Chinese
16-
- Additional language files can be added
14+
```
15+
translations/
16+
├── en/ # English (source of truth)
17+
│ ├── index.ts # Main export file
18+
│ ├── shared.json
19+
│ ├── components.json
20+
│ └── pages/
21+
│ ├── home.json
22+
│ ├── manifesto.json
23+
│ ├── docs.json
24+
│ ├── articles.json
25+
│ ├── curated-collections.json
26+
│ ├── stacks.json
27+
│ └── comparison.json
28+
├── de/ # German
29+
├── zh-Hans/ # Simplified Chinese
30+
└── ko/ # Korean
31+
```
32+
33+
**Important:** Each locale must maintain the exact same file structure and JSON key order as `en/`.
34+
35+
## Enabled Locales
36+
37+
Currently enabled locales in `src/i18n/config.ts`:
38+
- `en` - English (source of truth)
39+
- `de` - Deutsch (German)
40+
- `zh-Hans` - 简体中文 (Simplified Chinese)
41+
- `ko` - 한국어 (Korean)
1742

18-
All language files must maintain the same nested key structure as `en.json`.
43+
Additional locale directories may exist but are not enabled in the config.
1944

2045
## Subcommands
2146

2247
### `sync`
2348

24-
Synchronize all language files with `en.json` as the source of truth.
49+
Synchronize all enabled locale directories with `en/` as the source of truth.
2550

2651
**What it does:**
2752

28-
- Scans all `.json` files in `locales/` directory
29-
- Compares each file's keys with `en.json`
53+
- Scans all locale directories in `translations/` that are enabled in config
54+
- Compares each JSON file's keys with the corresponding English file
3055
- **Adds missing keys** with English text as placeholder (needs translation)
31-
- **Removes extra keys** not present in `en.json`
32-
- Preserves JSON structure and formatting (2-space indentation)
56+
- **Removes extra keys** not present in English files
57+
- **Preserves JSON structure, key order, and formatting** (2-space indentation)
58+
- **Keeps the `index.ts` file structure** for each locale
3359

3460
**Usage:**
3561

@@ -48,25 +74,30 @@ node .claude/skills/i18n/scripts/sync.mjs
4874
**Output Example:**
4975

5076
```
51-
🔄 Syncing language files with en.json...
77+
🔄 Syncing translation files with en/...
5278
53-
✓ Synced zh-Hans.json
54-
+ Added 3 keys
55-
- Removed 1 key
79+
✓ Synced de/
80+
+ Added 3 keys in components.json
81+
+ Added 5 keys in pages/home.json
82+
- Removed 1 key in shared.json
83+
84+
✓ Synced zh-Hans/
85+
+ Added 8 keys in pages/stacks.json
5686
5787
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
5888
✓ Sync complete!
59-
Modified: 1 file
60-
Added: 3 keys
89+
Modified: 2 locales
90+
Added: 16 keys
6191
Removed: 1 key
6292
```
6393

6494
**When to use:**
6595

66-
- After adding new keys to `en.json`
67-
- After removing obsolete keys from `en.json`
96+
- After adding new keys to any English JSON file
97+
- After removing obsolete keys from any English JSON file
98+
- After adding new JSON files to the English structure
6899
- Before starting translation work (ensures all keys exist)
69-
- When adding a new language file
100+
- When adding a new locale
70101

71102
---
72103

@@ -76,7 +107,7 @@ Generate translation tasks for Claude Code to translate missing content.
76107

77108
**What it does:**
78109

79-
- Reads `en.json` and `<locale>.json`
110+
- Reads all JSON files from `en/` and `<locale>/`
80111
- Identifies keys that need translation (currently in English or missing)
81112
- Outputs a structured translation task with guidelines
82113
- Provides context and instructions for accurate translation
@@ -100,7 +131,7 @@ node .claude/skills/i18n/scripts/translate.mjs zh-Hans
100131
1. The script outputs content that needs translation in JSON format
101132
2. Claude Code reads the guidelines and translates the content
102133
3. You provide the translated JSON back
103-
4. Claude Code updates the language file
134+
4. Claude Code updates the locale JSON files
104135

105136
**Translation Guidelines (automatically enforced):**
106137

@@ -109,18 +140,7 @@ node .claude/skills/i18n/scripts/translate.mjs zh-Hans
109140
**Don't translate URLs:** `https://example.com`
110141
**Don't translate file paths:** `/path/to/file`
111142
**Maintain terminology consistency** throughout the translation
112-
113-
**Supported Locales:**
114-
115-
- `zh-Hans` - 简体中文 (Simplified Chinese)
116-
- `zh-Hant` - 繁體中文 (Traditional Chinese)
117-
- `ja` - 日本語 (Japanese)
118-
- `ko` - 한국어 (Korean)
119-
- `fr` - Français (French)
120-
- `de` - Deutsch (German)
121-
- `es` - Español (Spanish)
122-
- `pt` - Português (Portuguese)
123-
- `ru` - Русский (Russian)
143+
**Preserve reference syntax:** `@:path.to.key` for internal references
124144

125145
**Output Example:**
126146

@@ -134,17 +154,24 @@ node .claude/skills/i18n/scripts/translate.mjs zh-Hans
134154
Target Language: 简体中文 (Simplified Chinese)
135155
Entries to translate: 15
136156
157+
Files affected:
158+
- components.json: 5 entries
159+
- pages/home.json: 8 entries
160+
- shared.json: 2 entries
161+
137162
⚠ Translation Guidelines:
138163
1. Preserve brand names: "AI Coding Stack", "Claude Code"
139164
2. Keep placeholders intact: {count}, {name}, ${variable}
140165
3. Don't translate URLs and file paths
141166
4. Maintain consistent terminology
167+
5. Preserve reference syntax: @:path.to.key
142168
143169
Content to translate:
144170
145171
{
146-
"pages.home.title": "Welcome to AI Coding Stack",
147-
"pages.home.description": "Discover the best AI coding tools",
172+
"components.languageSwitcher.english": "English",
173+
"pages.home.hero.title": "Welcome to AI Coding Stack",
174+
"shared.navigation.docs": "Documentation",
148175
...
149176
}
150177
```
@@ -168,6 +195,35 @@ Content to translate:
168195
**JSON Format:** 2-space indentation, trailing newline
169196
**Encoding:** UTF-8
170197

198+
### Translation File Structure
199+
200+
Each locale has:
201+
1. **JSON files**: Contain the actual message data
202+
2. **index.ts**: Imports and assembles messages
203+
204+
```typescript
205+
// translations/en/index.ts
206+
import components from './components.json'
207+
import articles from './pages/articles.json'
208+
// ... other imports
209+
210+
const messages = {
211+
shared,
212+
components,
213+
pages: {
214+
home,
215+
manifesto,
216+
docs,
217+
articles,
218+
curatedCollections,
219+
...stacks,
220+
comparison,
221+
},
222+
}
223+
224+
export default messages
225+
```
226+
171227
### How Keys Are Compared
172228

173229
The scripts use recursive traversal to handle nested JSON structures. Keys are compared using dot notation:
@@ -176,35 +232,54 @@ The scripts use recursive traversal to handle nested JSON structures. Keys are c
176232
{
177233
"pages": {
178234
"home": {
179-
"title": "Welcome"
235+
"hero": {
236+
"title": "Welcome"
237+
}
180238
}
181239
}
182240
}
183241
```
184242

185-
Becomes: `pages.home.title = "Welcome"`
243+
Becomes: `pages.home.hero.title = "Welcome"`
186244

187245
### Adding a New Language
188246

189247
1. Add the locale to `src/i18n/config.ts`:
190248

191249
```typescript
192-
export const locales = ['en', 'zh-Hans', 'ja'] as const; // Add 'ja'
250+
export const locales = ['en', 'de', 'zh-Hans', 'ko', 'ja'] as const; // Add 'ja'
193251
```
194252

195-
2. Create an empty JSON file or copy `en.json`:
253+
2. Update locale names:
254+
255+
```typescript
256+
export const localeNames: Record<Locale, string> = {
257+
// ...
258+
ja: '日本語',
259+
}
260+
261+
export const localeToOgLocale: Record<Locale, string> = {
262+
// ...
263+
ja: 'ja_JP',
264+
}
265+
```
266+
267+
3. Create the locale directory structure:
196268

197269
```bash
198-
cp locales/en.json locales/ja.json
270+
mkdir -p translations/ja/pages
271+
cp translations/en/index.ts translations/ja/index.ts
272+
cp translations/en/*.json translations/ja/
273+
cp translations/en/pages/*.json translations/ja/pages/
199274
```
200275

201-
3. Run sync to ensure structure matches:
276+
4. Run sync to ensure structure matches:
202277

203278
```
204279
Please run the i18n sync command
205280
```
206281

207-
4. Run translate to generate translation tasks:
282+
5. Run translate to generate translation tasks:
208283

209284
```
210285
Please run the i18n translate command for ja
@@ -213,22 +288,24 @@ Please run the i18n translate command for ja
213288
## Best Practices
214289

215290
1. **Always run `sync` before `translate`** to ensure all keys exist
216-
2. **Make changes to `en.json` first**, then sync other languages
291+
2. **Make changes to English files first**, then sync other locales
217292
3. **Review translations** for context accuracy, especially technical terms
218293
4. **Use Git** to track changes and review diffs before committing
219294
5. **Keep brand names consistent** across all languages
220295
6. **Test translations** in the actual UI to verify formatting and length
296+
7. **Preserve key order** to make diffs easier to review
297+
8. **Use reference syntax** (`@:path.to.key`) for reused content
221298

222299
## Troubleshooting
223300

224-
**Problem:** Script says "file not found"
301+
**Problem:** Script says "directory not found"
225302

226303
- **Solution:** Ensure you're running from project root
227-
- Check that `locales/` directory exists
304+
- Check that `translations/` directory exists
228305

229306
**Problem:** Keys are out of sync after adding new content
230307

231-
- **Solution:** Run `sync` command to update all language files
308+
- **Solution:** Run `sync` command to update all locale files
232309

233310
**Problem:** Translation contains placeholders like `{0}` instead of `{count}`
234311

@@ -238,18 +315,21 @@ Please run the i18n translate command for ja
238315

239316
- **Solution:** Run `translate` to find missing translations, check for English fallbacks
240317

318+
**Problem:** index.ts imports are wrong after sync
319+
320+
- **Solution:** The sync script preserves index.ts structure; manually check imports match actual JSON files
321+
241322
## Integration with next-intl
242323

243324
This skill is designed to work with the project's `next-intl` setup:
244325

245326
```typescript
246327
// src/i18n/request.ts
247-
export default getRequestConfig(async ({ locale }) => ({
248-
messages: (await import(`../../locales/${locale}.json`)).default,
249-
}));
328+
const rawMessages = (await import(`../../translations/${locale}/index.ts`)).default
329+
const messages = resolveReferences(rawMessages)
250330
```
251331

252-
The JSON files are loaded dynamically based on the current locale.
332+
The JSON files are loaded through the index.ts for each locale, and the `resolveReferences` function handles `@:path` reference syntax.
253333

254334
## License
255335

0 commit comments

Comments
 (0)