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