I18N-005 recommended Best Practices

No hardcoded strings in code

Hardcoded strings bypass the translation system entirely. Lint rules should enforce using i18n functions for user-facing strings.

Question to ask

"How many English strings are hiding in your components?"

Verification guide

Severity: Recommended

Hardcoded strings bypass the translation system entirely. They're invisible to translators and create inconsistent UX across locales.

Check automatically:

# Check for i18n lint rules
grep -riE "i18next/no-literal-string|no-literal-string|@calm/react-intl|eslint-plugin-i18n" package.json .eslintrc* eslint.config* 2>/dev/null

# Sample JSX for hardcoded user-facing strings (red flags)
grep -riE ">[A-Z][a-z]+.*</" src/ --include="*.tsx" --include="*.jsx" 2>/dev/null | grep -v node_modules | head -15

# Look for common hardcoded patterns in buttons, labels
grep -riE "<button>[^{<]+</button>|<label>[^{<]+</label>|placeholder=\"[A-Za-z]+" src/ --include="*.tsx" --include="*.jsx" 2>/dev/null | grep -v node_modules | head -10

Pass criteria:

  • Lint rule enforces no literal strings in JSX (or equivalent)
  • User-facing strings go through i18n functions (t(), <FormattedMessage>, etc.)
  • Exceptions documented (e.g., brand names, technical terms)

Fail criteria:

  • No linting enforcement
  • Obvious user-facing strings hardcoded
  • "We'll extract them later" (they won't)

Note: Some strings legitimately don't need translation (brand names, code identifiers). The lint rule should have an escape hatch for these.

Evidence to capture:

  • Lint rule configured
  • Sample of hardcoded strings found (if any)
  • Exception handling approach

Section

42. Internationalization (i18n)

Team & Development