brainstorming
brainstorming
You MUST use this before any creative work - creating features, building components, adding functionality, modifying behavior, or planning anything new, in software or out of it (a talk, a business, a renovation).
audit-i18nAudit and fix internationalisation in web or mobile apps. Use when "audit i18n", "fix translations", "add locale", "hardcoded strings", "localisation", "translation quality", or "the Japanese feels like Google Translate".
Independent GitHub snapshot · Automated classification · Original documentation governs
Degree of freedom: MIXED — Quality judgment and rewrite [HIGH freedom];
Phase 2 key-diff and Phase 4 locale walk [LOW freedom — run exactly].
Audit-and-fix exception. Find bad copy, then fix it. Not present-then-stop.
Fix the strings the audit named — extract, rewrite, add the missing key — with surgical edits to the source line and the locale entry; do not reformat or re-key whole translation files, and copy you did not audit stays as it is. Unrelated bugs met on the way go in the report as follow-ups. Add tests only where the repo already tests i18n; the Phase 2 key-diff scripts stay out of the commit.
Translations that read like machine output erode user trust — especially in the user's own language. Find every unnatural, jargon-heavy, or technically worded string and rewrite it to sound like a real person in that locale.
The goal: every string should sound like it was written by a native speaker who understands the user, not translated from English by a developer.
These apply to every string in every locale:
| Bad | Better | Why |
|---|---|---|
| "Invalid email format" | "That email doesn't look right — check for typos" | Guides, doesn't blame |
| "An error occurred" | "Something went wrong. Try again, or contact us if it keeps happening" | Actionable |
| "Please enter a valid value" | "This field is required" or "Enter a number between 1 and 100" | Specific |
| "Authentication failed" | "Wrong email or password" | Plain language |
| "Submit" | "Save", "Send message", "Create account" | Says what actually happens |
| "OK" | "Got it", "Done", or the specific action | Less dismissive |
| "User" | "you", "your account" | Conversational |
| "The operation was successful" | "Done!" or "Saved" | Natural |
| "Do you want to proceed?" | "Are you sure? This can't be undone." | Honest and specific |
file:line) and the base-locale counterpart[TODO] if you are not a native speakerObserve:
CheckoutButton.tsx:42renders<button>Submit</button>(no i18n key).jaerrors.email_invalidis "無効なメールアドレス";enkey exists. Interpret: the CTA does not name the outcome; JA is a literal of "Invalid email address" — it blames and gives no next step. Classify: hardcoded + too-literal error. Severity: High — primary checkout CTA and a blocking validation error. Finding: extractbuttons.place_order; rewrite JA to guide a typo check; then apply (audit-and-fix).
package.json → i18next, react-i18next, next-intl, vue-i18n, @angular/localize,
i18n-js, rosetta, lingui, typesafe-i18n
src/i18n/ → translation files
locales/ → JSON/YAML translation files
messages/ → next-intl message files
public/locales → common path for react-i18next
Identify:
en, ja, fr, de, zh, etc.)Fetch library docs:
context7:resolve-library-id
{
"libraryName": "react-i18next"
}
Look for user-facing text that bypasses the i18n system entirely.
Scan for:
<p>Welcome back</p>, <button>Save</button>`Hello ${name}`toast.error("Something failed")placeholder="Enter your email"aria-label="Close dialog"alt text on meaningful imagesUse shell to scan (the codebase search tools may be slow on large repos):
grep -rn 'placeholder="' src/ --include="*.tsx" --include="*.jsx" | head -30
grep -rn '"en":' src/ --include="*.ts" --include="*.tsx" | head -20
Build a list: file path, line number, string content, whether it has an i18n key already.
For each locale, check that all keys from the base locale (usually en) exist:
# Example: compare en.json vs ja.json key counts
node -e "
const en = require('./public/locales/en/common.json');
const ja = require('./public/locales/ja/common.json');
const missing = Object.keys(en).filter(k => !ja[k]);
console.log('Missing in ja:', missing);
"
Or for next-intl format:
node -e "
const en = require('./messages/en.json');
const ja = require('./messages/ja.json');
function findMissing(base, target, path='') {
for (const k of Object.keys(base)) {
const kp = path ? path+'.'+k : k;
if (!(k in target)) console.log('MISSING:', kp);
else if (typeof base[k] === 'object') findMissing(base[k], target[k], kp);
}
}
findMissing(en, ja);
"
This is the most important phase. For each locale, review every string against the natural-language principles at the top of this skill.
Error messages — must guide, not blame, and tell the user what to do:
// Before
"errors.email_invalid": "Invalid email address"
// After
"errors.email_invalid": "That doesn't look like a valid email — check for typos and try again"
Action buttons — must describe the outcome, not just label the input:
// Before
"buttons.submit": "Submit"
"buttons.confirm": "OK"
// After
"buttons.submit": "Save changes" // or whatever the action actually does
"buttons.confirm": "Got it" // or the specific action: "Delete account", "Send message"
Empty states — should explain and invite, not just state the absence:
// Before
"empty.projects": "No projects"
// After
"empty.projects": "You haven't created any projects yet. Start one to get going."
Success messages — should feel warm and confirm the action clearly:
// Before
"success.saved": "Data saved successfully"
// After
"success.saved": "Saved!" // or "Your changes are saved"
Loading states — should be human:
// Before
"loading.data": "Loading data..."
// After
"loading.data": "Getting things ready…" // or just omit "Loading" entirely
| Locale | Tone notes |
|---|---|
en | Friendly, direct, conversational. Use contractions ("you've", "can't"). No formal passive voice. |
ja | Use polite but natural keigo (〜ます/〜です form). Avoid literal translations of English idioms. Use short, clear sentences. Don't overuse katakana loanwords when Japanese alternatives exist. |
ko | Polite 합쇼체 or 해요체 depending on the app's audience. Avoid Konglish where natural Korean exists. |
zh-TW / zh-CN | Traditional vs simplified — do not mix. Natural, not literal. Check that date/number formats are locale-correct. |
fr | Formal vous for most apps; tu only for clearly youth/casual products. Gender agreement must be correct — check nouns. |
de | German compounds can get long — check for truncation. Formal Sie for business apps. |
es | Check for tú/usted/vosotros consistency. Latin American vs Spain variants differ. |
RTL (ar, he) | Mirror layout. Check that numbers are LTR within RTL text. Verify UI wraps correctly. |
For each active locale:
PW="npx --yes @playwright/cli@latest"
$PW -s=i18n open --headed "http://localhost:3000/ja" # or set the locale cookie, then `goto`
$PW -s=i18n snapshot # capture the state
$PW -s=i18n screenshot --filename ".playwright-mcp/i18n-ja-<route>.png"
Look for:
common.save)$10.00 vs 10,00 € vs ¥1,000)Save screenshots under .playwright-mcp/.
file:line + key, not "Japanese feels off"[TODO]; do not ship confident wrong idiomaudit-responsive; WCAG → audit-accessibilityFor every hardcoded string found in Phase 1, create an i18n key and replace it:
// Before (react-i18next)
<p>Welcome back, {name}!</p>
// After
const { t } = useTranslation('common');
<p>{t('welcome_back', { name })}</p>
// en/common.json
"welcome_back": "Welcome back, {{name}}!"
// ja/common.json
"welcome_back": "おかえりなさい、{{name}}さん!"
Edit the translation files directly. Prioritise:
For any key missing in a non-English locale, add a natural translation. If you don't have a native speaker for a locale, use a clearly-marked placeholder:
"some_key": "[TODO: needs native Japanese review] {{count}} items selected"
Use the i18n library's format functions rather than manual formatting:
// react-i18next with i18next-icu or i18next-intervalPlural
t('items_count', { count: 5 }) // handles plural forms automatically
// Intl API for dates and numbers (framework-agnostic)
new Intl.DateTimeFormat(locale, { dateStyle: 'long' }).format(date);
new Intl.NumberFormat(locale, { style: 'currency', currency: 'JPY' }).format(1234);
## i18n Audit Report — [App] — [Date]
### Active locales
[list with coverage % — keys present / total keys]
### Hardcoded strings found
| File | Line | String | Action |
|------|------|--------|--------|
| src/components/Foo.tsx | 42 | "Save" | Extracted to key `buttons.save` |
### Translation quality issues fixed
| Locale | Key | Before | After | Issue type |
|--------|-----|--------|-------|-----------|
| ja | errors.email_invalid | "無効なメールアドレス" | "メールアドレスが正しくないようです。入力内容をご確認ください。" | Too literal, not helpful |
### Missing keys added
[count per locale]
### Formatting fixes
[date/number/currency issues found and fixed]
### Still needs native review
[keys marked [TODO] that need a human native speaker]