Translation management for Inertia.js applications with Rails backend
Inertia.js applications have a split architecture:
config/locales/*.yml)This creates several challenges:
Existing tools like i18n-tasks only handle Rails/backend translations.
InertiaI18n provides:
%{var} → {{var}}).svelte, .tsx, .vue filesOne source of truth: Rails YAML files, with JSON auto-generated.
Add to your Gemfile:
gem 'inertia_i18n'
Run the installer:
rails generate inertia_i18n:install
This generator will:
config/locales/frontend, config/locales/backend).config/initializers/inertia_i18n.rb).react-i18next) to your package.json.To avoid conflicts between backend and frontend translation keys, it is recommended to separate your locale files into subdirectories:
config/
└── locales/
├── backend/ # Rails-specific translations
│ ├── en.yml
│ └── ru.yml
├── frontend/ # Frontend-specific translations
│ ├── common.en.yml
│ ├── pages.en.yml
│ └── pages.ru.yml
└── en.yml # Optional: shared or legacy keys
By default, InertiaI18n will look for YAML files in config/locales/frontend. You can customize this using the source_paths configuration.
The installer creates a default configuration file. You can customize it in config/initializers/inertia_i18n.rb.
# config/initializers/inertia_i18n.rb
InertiaI18n.configure do |config|
# Recommended: point to a dedicated frontend folder
config.source_paths = [Rails.root.join('config', 'locales', 'frontend')]
config.target_path = Rails.root.join('app', 'frontend', 'locales')
config.locales = [:en, :ru]
# Scan paths are automatically set based on your detected framework
config.scan_paths = [
Rails.root.join('app', 'frontend', '**', '*.{svelte,tsx,vue}')
]
end
# One-time conversion
bundle exec rake inertia_i18n:convert
# Watch mode (auto-convert on YAML changes)
bundle exec rake inertia_i18n:watch
The recommended way to check translation health is by running the generated test as part of your test suite. See the CI Integration section for details.
You can also run a manual check from the command line:
# Find missing, unused, and out-of-sync keys
bundle exec rake inertia_i18n:health
All CLI commands load the Rails environment, so they have access to your application's configuration and behave identically to the rake tasks.
# Generate a new configuration file
inertia_i18n init
# Convert YAML to JSON
inertia_i18n convert
# Convert specific locale
inertia_i18n convert --locale=ru
# Scan frontend code for translation usage
inertia_i18n scan
# Check translation health
inertia_i18n health
# Sort and format JSON locale files
inertia_i18n normalize
# Watch for changes and auto-convert
inertia_i18n watch
Input (Rails YAML):
# config/locales/en.yml
en:
user:
greeting: "Hello, %{name}!"
items:
one: "1 item"
other: "%{count} items"
Output (i18next JSON):
{
"user": {
"greeting": "Hello, {{name}}!",
"items_one": "1 item",
"items_other": "{{count}} items"
}
}
Detects translation usage in:
{t('key')} and t('key') in <script>{t('key')} in JSX{{ t('key') }} and t('key') in scriptHandles:
t('user.greeting')t(\user.${type}.title`)` (flagged for review)t(keyVariable) (flagged for review)| Check | Description |
|---|---|
| Missing Keys | Used in code but not in JSON (breaks app) |
| Unused Keys | In JSON but never used (bloat) |
| Locale Sync | Key exists in en.json but missing in ru.json |
Auto-regenerates JSON when YAML files change:
bundle exec rake inertia_i18n:watch
# Output:
👀 Watching config/locales for YAML changes...
📝 Detected locale file changes...
Changed: config/locales/hr.en.yml
🔄 Regenerating JSON files...
✅ Done!
The best way to ensure your translations stay healthy is to check them in your Continuous Integration (CI) pipeline.
Generate a dedicated test file that runs the health check as part of your test suite:
# For RSpec
rails g inertia_i18n:test
# Creates spec/inertia_i18n_health_spec.rb
# For Minitest
# Creates test/inertia_i18n_health_test.rb
Now, your existing CI command will automatically catch translation issues:
# Run your full test suite
bundle exec rspec
# or
bundle exec rails test
When issues are found, the test will fail with a detailed report:
Failure/Error: fail message.join("\n")
RuntimeError:
Translation health check failed!
Missing Keys (1):
- home.title
Unused Keys (1):
- unused.key
Locale Synchronization Issues (2):
- unused.key (in ru)
- home.title (in ru)
# .github/workflows/ci.yml
name: CI
on: [push, pull_request]
jobs:
test:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v3
- uses: ruby/setup-ruby@v1
with:
bundler-cache: true
# Run the full test suite, which now includes the translation health check
- name: Run tests
run: bundle exec rspec
If you use i18n-tasks for your backend translations, it might flag your frontend keys as "unused" or "missing". To prevent this, configure i18n-tasks to ignore the frontend locale directory.
Add this to your config/i18n-tasks.yml:
# config/i18n-tasks.yml
data:
read:
- "config/locales/backend/**/*.yml" # Read only backend locales
- "config/locales/*.yml" # Optional: shared keys
Alternatively, you can exclude the frontend directory:
# config/i18n-tasks.yml
ignore:
- "frontend.*" # Ignore all keys starting with "frontend." (if namespaced)
InertiaI18n.configure do |config|
# Source directories for your frontend YAML files.
# Default: ['config/locales/frontend']
config.source_paths = [
'config/locales/frontend',
'config/locales/common'
]
config.source_pattern = '**/*.{yml,yaml}'
# Target: i18next JSON files
config.target_path = 'app/frontend/locales'
# Locales to process
config.locales = [:en, :ru, :de]
# Frontend paths to scan
config.scan_paths = [
'app/frontend/**/*.{js,ts,jsx,tsx,svelte,vue}'
]
# Interpolation conversion
config.interpolation = { from: '%{', to: '{{' }
# Flatten nested keys (default: false)
config.flatten_keys = false
# Ignore patterns (don't scan these files)
config.ignore_patterns = [
'**/node_modules/**',
'**/vendor/**',
'**/*.test.{js,ts}'
]
end
| Feature | InertiaI18n | i18n-tasks | i18next-parser |
|---|---|---|---|
| Rails YAML support | ✅ | ✅ | ❌ |
| i18next JSON support | ✅ | ❌ | ✅ |
| YAML → JSON conversion | ✅ | ❌ | ❌ |
| Frontend usage scanning | ✅ | ❌ | ✅ (extraction only) |
| Missing keys detection | ✅ | ✅ (backend only) | ✅ (frontend only) |
| Unused keys detection | ✅ | ✅ (backend only) | ❌ |
| Locale sync check | ✅ | ✅ | ✅ |
| Watch mode | ✅ | ❌ | ✅ |
| Rails integration | ✅ | ✅ | ❌ |
| Inertia.js specific | ✅ | ❌ | ❌ |
InertiaI18n = i18n-tasks + i18next-parser + YAML↔JSON bridge
# Clone repository
git clone https://github.com/alec-c4/inertia_i18n.git
cd inertia_i18n
# Install dependencies
bundle install
# Run tests
bundle exec rspec
# Run locally in your Rails app
# Add to Gemfile:
gem 'inertia_i18n', path: '../inertia_i18n'
git checkout -b feature/amazing-feature)git commit -m 'Add amazing feature')git push origin feature/amazing-feature)See CONTRIBUTING.md for details.
MIT License - see LICENSE.txt
Created by Alexey Poimtsev
Inspired by: