Claude
Cursor
opencode
Skill
i18n-localization
Internationalization and localization patterns. Detecting hardcoded strings, managing translations, locale files, RTL support.
Virus-scanned
Reviewed automatically before listing.
Download
vodailocz-kilo-kit-mcp-skills_engineering_i18n-localization-0448e6c.zip · 4 KB
Install
skills CLI
npx skills add https://github.com/VoDaiLocz/kilo-kit-mcp/tree/main/skills/engineering/i18n-localization
Claude Code
claude plugin marketplace add https://llmmart.ai/marketplace.json && claude plugin install vodailocz-kilo-kit-mcp@llmmart
Git
git clone https://github.com/VoDaiLocz/kilo-kit-mcp.git
The skills CLI installs just this skill, for any of its supported agents. Claude Code installs the whole vodailocz/kilo-kit-mcp collection as a plugin from our marketplace. Git is the plain clone.
Skill manifest
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> |
Files (kilo-kit-mcp)
-
scripts
-
i18n_checker.py 7.5 KB
#!/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()
-
-
SKILL.md 3 KB
--- name: i18n-localization description: Internationalization and localization patterns. Detecting hardcoded strings, managing translations, locale files, RTL support. allowed-tools: Read, Glob, Grep --- # 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) ```tsx import { useTranslation } from 'react-i18next'; function Welcome() { const { t } = useTranslation(); return <h1>{t('welcome.title')}</h1>; } ``` ### Next.js (next-intl) ```tsx import { useTranslations } from 'next-intl'; export default function Page() { const t = useTranslations('Home'); return <h1>{t('title')}</h1>; } ``` ### Python (gettext) ```python 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 /* 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>` |
Comments (0)
Sign in to join the conversation.
Reviews (0)
No reviews yet.
No comments yet.