
I18n Localization
- 550 installs
- 29.9k repo stars
- Updated July 27, 2026
- davila7/claude-code-templates
i18n-localization is an agent skill with a Python checker that scans React, Vue, and Python codebases for hardcoded UI strings and missing translation keys before shipping multiple locales.
About
The i18n-localization skill packages a small Python scanner that audits your frontend and backend source for strings that should live in translation catalogs instead of hardcoded literals. developers shipping SaaS or APIs with React, Vue, or Python surfaces invoke it when adding locales or cleaning up tech debt before release. It contrasts suspicious capitalization-heavy literals against established i18n call patterns so your agent can prioritize refactors. It is a focused checker—not a full TMS integration—meant to complement react-i18next, vue-i18n, or gettext workflows. Run it after feature work or before merge when multilingual support is on the roadmap, and feed findings into your normal PR workflow.
- Python i18n checker script scans repos for hardcoded English in JSX, Vue templates, and Python literals
- Regex families flag untranslated JSX text, attribute strings (title, placeholder, label), and flash/print messages
- Recognizes proper i18n usage patterns: react-i18next t(), useTranslation, Vue $t(), Python gettext _()
- Windows console UTF-8 handling for Unicode diagnostics in CI or local runs
- Structured output path (JSON-oriented workflow) for agent-driven fix lists
I18n Localization by the numbers
- 550 all-time installs (skills.sh)
- +12 installs in the week ending Jun 22, 2026 (Skillselion tracking)
- Ranked #594 of 2,277 Frontend Development skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Jul 28, 2026 (Skillselion catalog sync)
npx skills add https://github.com/davila7/claude-code-templates --skill i18n-localizationAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 550 |
|---|---|
| repo stars | ★ 29.9k |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 27, 2026 |
| Repository | davila7/claude-code-templates ↗ |
How do you find hardcoded strings before i18n ship?
Scan React, Vue, and Python codebases for hardcoded UI copy and missing translation keys before you ship multiple locales.
Who is it for?
Frontend and full-stack developers validating react-i18next or next-intl readiness across React, Vue, and Python code before a locale launch.
Skip if: Single-language internal tools with no translation requirements or teams wanting automatic translation generation instead of audit checks.
When should I use this skill?
A developer prepares multi-locale release and needs to detect hardcoded UI copy or missing translation keys in React, Vue, or Python.
What you get
i18n audit report listing missing locale keys, hardcoded string examples, and pass/fail exit status
- i18n audit console report
- Missing-key list
- Hardcoded string examples
By the numbers
- Recognizes 8 i18n usage patterns in source code
- Uses 6 locale file glob patterns and scans up to 50 code files per audit
- Defines separate hardcoded-string regex sets for JSX, Vue, and Python
Files
i18n & Localization
Internationalization (i18n) and Localization (L10n) best practices.
---
1. Core Concepts
| Term | Meaning |
|---|---|
| i18n | Internationalization - making app translatable |
| L10n | Localization - actual translations |
| Locale | Language + Region (en-US, tr-TR) |
| RTL | Right-to-left languages (Arabic, Hebrew) |
---
2. When to Use i18n
| Project Type | i18n Needed? |
|---|---|
| Public web app | ✅ Yes |
| SaaS product | ✅ Yes |
| Internal tool | ⚠️ Maybe |
| Single-region app | ⚠️ Consider future |
| Personal project | ❌ Optional |
---
3. Implementation Patterns
React (react-i18next)
import { useTranslation } from 'react-i18next';
function Welcome() {
const { t } = useTranslation();
return <h1>{t('welcome.title')}</h1>;
}Next.js (next-intl)
import { useTranslations } from 'next-intl';
export default function Page() {
const t = useTranslations('Home');
return <h1>{t('title')}</h1>;
}Python (gettext)
from gettext import gettext as _
print(_("Welcome to our app"))---
4. File Structure
locales/
├── en/
│ ├── common.json
│ ├── auth.json
│ └── errors.json
├── tr/
│ ├── common.json
│ ├── auth.json
│ └── errors.json
└── ar/ # RTL
└── ...---
5. Best Practices
DO ✅
- Use translation keys, not raw text
- Namespace translations by feature
- Support pluralization
- Handle date/number formats per locale
- Plan for RTL from the start
- Use ICU message format for complex strings
DON'T ❌
- Hardcode strings in components
- Concatenate translated strings
- Assume text length (German is 30% longer)
- Forget about RTL layout
- Mix languages in same file
---
6. Common Issues
| Issue | Solution |
|---|---|
| Missing translation | Fallback to default language |
| Hardcoded strings | Use linter/checker script |
| Date format | Use Intl.DateTimeFormat |
| Number format | Use Intl.NumberFormat |
| Pluralization | Use ICU message format |
---
7. RTL Support
/* CSS Logical Properties */
.container {
margin-inline-start: 1rem; /* Not margin-left */
padding-inline-end: 1rem; /* Not padding-right */
}
[dir="rtl"] .icon {
transform: scaleX(-1);
}---
8. Checklist
Before shipping:
- [ ] All user-facing strings use translation keys
- [ ] Locale files exist for all supported languages
- [ ] Date/number formatting uses Intl API
- [ ] RTL layout tested (if applicable)
- [ ] Fallback language configured
- [ ] No hardcoded strings in components
---
Script
| Script | Purpose | Command |
|---|---|---|
scripts/i18n_checker.py | Detect hardcoded strings & missing translations | python scripts/i18n_checker.py <project_path> |
#!/usr/bin/env python3
"""
i18n Checker - Detects hardcoded strings and missing translations.
Scans for untranslated text in React, Vue, and Python files.
"""
import sys
import re
import json
from pathlib import Path
# Fix Windows console encoding for Unicode output
try:
sys.stdout.reconfigure(encoding='utf-8', errors='replace')
sys.stderr.reconfigure(encoding='utf-8', errors='replace')
except AttributeError:
pass # Python < 3.7
# Patterns that indicate hardcoded strings (should be translated)
HARDCODED_PATTERNS = {
'jsx': [
# Text directly in JSX: <div>Hello World</div>
r'>\s*[A-Z][a-zA-Z\s]{3,30}\s*</',
# JSX attribute strings: title="Welcome"
r'(title|placeholder|label|alt|aria-label)="[A-Z][a-zA-Z\s]{2,}"',
# Button/heading text
r'<(button|h[1-6]|p|span|label)[^>]*>\s*[A-Z][a-zA-Z\s!?.,]{3,}\s*</',
],
'vue': [
# Vue template text
r'>\s*[A-Z][a-zA-Z\s]{3,30}\s*</',
r'(placeholder|label|title)="[A-Z][a-zA-Z\s]{2,}"',
],
'python': [
# print/raise with string literals
r'(print|raise\s+\w+)\s*\(\s*["\'][A-Z][^"\']{5,}["\']',
# Flask flash messages
r'flash\s*\(\s*["\'][A-Z][^"\']{5,}["\']',
]
}
# Patterns that indicate proper i18n usage
I18N_PATTERNS = [
r't\(["\']', # t('key') - react-i18next
r'useTranslation', # React hook
r'\$t\(', # Vue i18n
r'_\(["\']', # Python gettext
r'gettext\(', # Python gettext
r'useTranslations', # next-intl
r'FormattedMessage', # react-intl
r'i18n\.', # Generic i18n
]
def find_locale_files(project_path: Path) -> list:
"""Find translation/locale files."""
patterns = [
"**/locales/**/*.json",
"**/translations/**/*.json",
"**/lang/**/*.json",
"**/i18n/**/*.json",
"**/messages/*.json",
"**/*.po", # gettext
]
files = []
for pattern in patterns:
files.extend(project_path.glob(pattern))
return [f for f in files if 'node_modules' not in str(f)]
def check_locale_completeness(locale_files: list) -> dict:
"""Check if all locales have the same keys."""
issues = []
passed = []
if not locale_files:
return {'passed': [], 'issues': ["[!] No locale files found"]}
# Group by parent folder (language)
locales = {}
for f in locale_files:
if f.suffix == '.json':
try:
lang = f.parent.name
content = json.loads(f.read_text(encoding='utf-8'))
if lang not in locales:
locales[lang] = {}
locales[lang][f.stem] = set(flatten_keys(content))
except:
continue
if len(locales) < 2:
passed.append(f"[OK] Found {len(locale_files)} locale file(s)")
return {'passed': passed, 'issues': issues}
passed.append(f"[OK] Found {len(locales)} language(s): {', '.join(locales.keys())}")
# Compare keys across locales
all_langs = list(locales.keys())
base_lang = all_langs[0]
for namespace in locales.get(base_lang, {}):
base_keys = locales[base_lang].get(namespace, set())
for lang in all_langs[1:]:
other_keys = locales.get(lang, {}).get(namespace, set())
missing = base_keys - other_keys
if missing:
issues.append(f"[X] {lang}/{namespace}: Missing {len(missing)} keys")
extra = other_keys - base_keys
if extra:
issues.append(f"[!] {lang}/{namespace}: {len(extra)} extra keys")
if not issues:
passed.append("[OK] All locales have matching keys")
return {'passed': passed, 'issues': issues}
def flatten_keys(d, prefix=''):
"""Flatten nested dict keys."""
keys = set()
for k, v in d.items():
new_key = f"{prefix}.{k}" if prefix else k
if isinstance(v, dict):
keys.update(flatten_keys(v, new_key))
else:
keys.add(new_key)
return keys
def check_hardcoded_strings(project_path: Path) -> dict:
"""Check for hardcoded strings in code files."""
issues = []
passed = []
# Find code files
extensions = {
'.tsx': 'jsx', '.jsx': 'jsx', '.ts': 'jsx', '.js': 'jsx',
'.vue': 'vue',
'.py': 'python'
}
code_files = []
for ext in extensions:
code_files.extend(project_path.rglob(f"*{ext}"))
code_files = [f for f in code_files if not any(x in str(f) for x in
['node_modules', '.git', 'dist', 'build', '__pycache__', 'venv', 'test', 'spec'])]
if not code_files:
return {'passed': ["[!] No code files found"], 'issues': []}
files_with_i18n = 0
files_with_hardcoded = 0
hardcoded_examples = []
for file_path in code_files[:50]: # Limit
try:
content = file_path.read_text(encoding='utf-8', errors='ignore')
ext = file_path.suffix
file_type = extensions.get(ext, 'jsx')
# Check for i18n usage
has_i18n = any(re.search(p, content) for p in I18N_PATTERNS)
if has_i18n:
files_with_i18n += 1
# Check for hardcoded strings
patterns = HARDCODED_PATTERNS.get(file_type, [])
hardcoded_found = False
for pattern in patterns:
matches = re.findall(pattern, content)
if matches and not has_i18n:
hardcoded_found = True
if len(hardcoded_examples) < 5:
hardcoded_examples.append(f"{file_path.name}: {str(matches[0])[:40]}...")
if hardcoded_found:
files_with_hardcoded += 1
except:
continue
passed.append(f"[OK] Analyzed {len(code_files)} code files")
if files_with_i18n > 0:
passed.append(f"[OK] {files_with_i18n} files use i18n")
if files_with_hardcoded > 0:
issues.append(f"[X] {files_with_hardcoded} files may have hardcoded strings")
for ex in hardcoded_examples:
issues.append(f" → {ex}")
else:
passed.append("[OK] No obvious hardcoded strings detected")
return {'passed': passed, 'issues': issues}
def main():
target = sys.argv[1] if len(sys.argv) > 1 else "."
project_path = Path(target)
print("\n" + "=" * 60)
print(" i18n CHECKER - Internationalization Audit")
print("=" * 60 + "\n")
# Check locale files
locale_files = find_locale_files(project_path)
locale_result = check_locale_completeness(locale_files)
# Check hardcoded strings
code_result = check_hardcoded_strings(project_path)
# Print results
print("[LOCALE FILES]")
print("-" * 40)
for item in locale_result['passed']:
print(f" {item}")
for item in locale_result['issues']:
print(f" {item}")
print("\n[CODE ANALYSIS]")
print("-" * 40)
for item in code_result['passed']:
print(f" {item}")
for item in code_result['issues']:
print(f" {item}")
# Summary
critical_issues = sum(1 for i in locale_result['issues'] + code_result['issues'] if i.startswith("[X]"))
print("\n" + "=" * 60)
if critical_issues == 0:
print("[OK] i18n CHECK: PASSED")
sys.exit(0)
else:
print(f"[X] i18n CHECK: {critical_issues} issues found")
sys.exit(1)
if __name__ == "__main__":
main()
Related skills
Forks & variants (1)
I18n Localization has 1 known copy in the catalog totaling 2 installs. They canonicalize to this original listing.
- xenitv1 - 2 installs
How it compares
Use i18n-localization to audit existing code; use translation-management platforms when writers need workflow beyond static key checks.
FAQ
What does i18n_checker.py scan?
i18n-localization's i18n_checker.py scans React TSX/JSX, Vue, and Python files for hardcoded user-facing strings while verifying locale JSON completeness across languages discovered via six glob patterns under locales/, translations/, and i18n/ paths.
Which i18n libraries does i18n-localization recognize?
i18n-localization detects proper usage patterns for react-i18next t() and useTranslation, next-intl useTranslations, Vue $t(), Python gettext() and _(), plus react-intl FormattedMessage markers.
Is I18n Localization safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.