Internationalization is one of those features that's easy to add to a new project and nightmarish to retrofit onto an existing one. When applications start, nobody thinks about German users. By the time German users become important, there are thousands of hardcoded strings scattered across the codebase.
The manual work of i18n is staggering: find every hardcoded string, extract it to a translation file, replace it with a translation key, repeat thousands of times. Then manage those translation files forever, keeping them synchronized across languages as features change.
AI agents can automate the entire i18n lifecycle: extracting strings, generating keys, managing translation files, and keeping everything synchronized as your application evolves.
The i18n Challenge
Several factors make internationalization difficult.
String Discovery
Hardcoded strings hide everywhere: UI components, error messages, validation rules, email templates, PDF generators. Finding them all requires checking every file.
Key Generation
Each string needs a meaningful key. button.submit is better than string_1234. Creating good keys for thousands of strings takes judgment and time.
Context Preservation
Translators need context. "Run" could be a verb (execute) or a noun (a jog). Without context, translations are guesses.
Pluralization Complexity
Different languages have different pluralization rules. English has 2 forms (1 item, N items). Polish has 4. Arabic has 6. Your i18n solution must handle them all.
Ongoing Maintenance
As features change, translations must follow. New strings need translation. Changed strings need re-translation. Deleted strings need cleanup. This never ends.
Synchronization
Translation files across languages must stay synchronized. Missing keys break the application for some users. Orphan keys waste translator time.
Extracting Hardcoded Strings
The first step is finding what needs translation.
Full Codebase Extraction
@devonair identify all hardcoded user-facing strings in /src
@devonair extract strings from UI components and replace with i18n calls
The agent scans code, identifies strings, and generates extraction candidates.
Selective Extraction
@devonair extract strings from /src/components/checkout for translation
Focus on specific areas for incremental rollout.
Template Extraction
@devonair extract strings from email templates in /templates
Don't forget non-UI text.
Error Message Extraction
@devonair extract and internationalize error messages across the application
Users see errors in their language too.
Key Generation Strategies
Semantic Keys
@devonair generate semantic i18n keys like checkout.button.submit
Keys describe what the string is, not what it says.
Hierarchical Keys
@devonair organize keys by feature: user.profile.name, user.settings.language
Hierarchical organization scales better.
Automatic Key Generation
@devonair generate unique keys based on file path and string position
When quantity matters more than beauty.
Key Migration
@devonair migrate from numeric keys to semantic keys
Improve key quality over time.
Translation File Management
Format Support
@devonair manage translations in JSON format with nested keys
@devonair convert translation files from YAML to JSON
Different projects need different formats.
File Organization
@devonair organize translations by language in /locales/{lang}/
@devonair split large translation files by feature area
Synchronization
@devonair synchronize translation files across all languages
@devonair add missing keys from en.json to all other language files
Keep language files aligned.
Cleanup
@devonair remove orphan keys not referenced in code
@devonair identify and merge duplicate translations
Extracting to Frameworks
Different frameworks have different i18n patterns.
React (react-i18next)
@devonair convert hardcoded strings to t() calls with react-i18next
@devonair use Trans component for strings with embedded JSX
Vue (vue-i18n)
@devonair convert strings to $t() calls for Vue i18n
@devonair add i18n blocks to single-file components
Angular (ngx-translate)
@devonair convert strings to translate pipe and service calls
Next.js (next-i18next)
@devonair set up next-i18next and extract page-level translations
Node.js Backend
@devonair internationalize API error messages and validation
Handling Pluralization
@devonair add pluralization support for strings with counts
@devonair convert "{count} items" to proper ICU pluralization format
Before:
`${count} item${count !== 1 ? 's' : ''}`
After (ICU format):
{count, plural, one {# item} other {# items}}
Context for Translators
Good translations need context.
Comments
@devonair add translator comments explaining context for ambiguous strings
Screenshots
@devonair generate screenshots showing strings in context
Visual context clarifies usage.
Usage Examples
@devonair add example usage to translation file comments
Show translators how strings are used.
Translation Integration
Translation Service Integration
@devonair sync translation files with Lokalise
@devonair push new strings to Phrase for translation
@devonair pull completed translations from Crowdin
Machine Translation
@devonair generate initial translations using machine translation for review
Start with machine translation, refine with humans.
Translation Memory
@devonair apply translation memory to reuse existing translations
Consistent terminology across the application.
Validation and Quality
Key Validation
@devonair verify all i18n keys have translations in all supported languages
@devonair identify keys with missing translations
Format Validation
@devonair validate ICU message format syntax in all translations
Catch syntax errors before runtime.
Variable Consistency
@devonair verify translated strings have same variables as source
Ensure {name} appears in all translations that need it.
Length Validation
@devonair identify translations that are unusually longer than source
German translations of English are often 30% longer - does the UI handle it?
Workflow Integration
PR-Level i18n
@devonair on PR: extract new strings and add to translation files
@devonair on PR: warn if adding hardcoded user-facing strings
Catch i18n issues during development.
Translation Requests
@devonair generate translation request for new strings in this release
Package new strings for translators.
Release Validation
@devonair before release: verify all languages have complete translations
Don't ship with missing translations.
RTL Support
Right-to-left languages need special handling.
RTL Auditing
@devonair audit components for RTL compatibility
@devonair identify hardcoded directional styles
RTL Fixes
@devonair convert left/right styles to logical properties for RTL support
Use start/end instead of left/right.
Date, Number, and Currency
Localization extends beyond strings.
Date Formatting
@devonair ensure all dates use locale-aware formatting
@devonair convert hardcoded date formats to Intl.DateTimeFormat
Number Formatting
@devonair ensure numbers use locale-appropriate separators
1,000 vs 1.000 depending on locale.
Currency
@devonair format currencies correctly for each locale
Position, symbol, and formatting vary.
Testing i18n
Missing Translation Detection
@devonair test for missing translation warnings in all routes
Pseudo-Localization
@devonair generate pseudo-locale to test i18n completeness
Pseudo-locales make untranslated strings obvious.
Visual Regression
@devonair test UI with longest translations to check for overflow
Ensure layouts handle translation length.
RTL Testing
@devonair run visual tests in RTL mode
Verify RTL layout correctness.
Metrics and Reporting
Translation Coverage
@devonair report on translation coverage by language
Know how complete each language is.
Untranslated Strings
@devonair report on untranslated strings added this month
Track i18n debt accumulation.
Translation Age
@devonair identify translations that may be stale after source changes
Changed source strings need translation review.
Getting Started
Start with extraction:
@devonair analyze codebase and report on hardcoded strings requiring i18n
Know the scope.
Extract progressively:
@devonair extract strings from /src/components/common to i18n
@devonair extract strings from next highest-priority area
Set up workflow:
@devonair on PR: flag new hardcoded strings and offer extraction
Prevent new i18n debt while addressing existing debt.
Internationalization that happens automatically is internationalization that actually gets done. When strings extract themselves and translations stay synchronized, reaching global users becomes achievable.
FAQ
Should I use keys or source strings as identifiers?
Both approaches work. Keys (button.submit) are more stable but require lookup. Source strings ("Submit") are self-documenting but break when English changes. Most teams prefer keys for maintainability.
How do I handle strings with variables?
Use your framework's interpolation: t('greeting', { name }) for "Hello, {name}!". Ensure variables are positionally independent since languages order words differently.
What about strings that change based on gender?
ICU MessageFormat supports gender selection. For "He/She submitted", use {gender, select, male {He} female {She} other {They}} submitted. This is a complex area - involve linguists for quality.
How do I prioritize which languages to support?
Start with your highest-traffic locales. Use analytics to identify where users are. Consider regulatory requirements (EU often requires local language support). Expand based on business priorities.