Introduction
When Rails cannot find a translation for a given key, it displays the raw key path (e.g., translation missing: en.users.show.title) instead of meaningful text. This happens when locale files are out of sync, new keys are added without translations for all supported languages, or the fallback chain is not properly configured. Users see broken strings in production, especially for non-default locales.
Symptoms
- UI shows
translation missing: en.xxxtext instead of actual strings - Only affects non-default locales (English works, other languages broken)
- Translation keys appear in logs with
I18n::MissingTranslationData - New features deployed with translations only in development locale
- Email templates show raw keys instead of localized text
Check for missing translations:
``ruby
# In Rails console
I18n.t('users.show.title', locale: :ja, raise: true)
# => I18n::MissingTranslationData: translation missing: ja.users.show.title
Common Causes
- New translation keys added to one locale file but not others
- Fallback chain not configured for missing locales
- YAML syntax errors in locale files silently skipping keys
- Translation keys with wrong nesting structure
- Locale files not reloaded in production (cached from boot)
Step-by-Step Fix
- 1.Configure fallback chain for missing translations:
- 2.```ruby
- 3.# config/application.rb
- 4.config.i18n.available_locales = [:en, :ja, :zh, :es, :fr]
- 5.config.i18n.default_locale = :en
# Set up locale fallbacks config.i18n.fallbacks = { ja: [:ja, :en], "zh-TW": ["zh-TW", "zh", :en], "zh-CN": ["zh-CN", "zh", :en], es: [:es, :en], fr: [:fr, :en], } ```
- 1.Add gem for fallback support:
- 2.```ruby
- 3.# Gemfile
- 4.gem 'i18n-js'
- 5.gem 'rails-i18n'
- 6.gem 'i18n-tasks', group: :development
- 7.
` - 8.Log missing translations instead of showing raw keys:
- 9.```ruby
- 10.# config/initializers/i18n.rb
- 11.module I18n
- 12.class ExceptionHandler
- 13.def call(exception, _locale, _key, _options)
- 14.if exception.is_a?(I18n::MissingTranslation)
- 15.# Log the missing translation for later fixing
- 16.Rails.logger.warn(
- 17."Missing translation: #{exception.key.inspect} for locale: #{_locale}"
- 18.)
- 19.# Return a user-friendly fallback instead of raw key
- 20.I18n.t(exception.key, locale: :en, default: exception.key.to_s)
- 21.else
- 22.raise exception
- 23.end
- 24.end
- 25.end
- 26.end
I18n.exception_handler = I18n::ExceptionHandler.new ```
- 1.Detect missing translations with i18n-tasks:
- 2.```bash
- 3.# Install and run i18n-tasks
- 4.bundle add i18n-tasks --group development
# Check for missing translations bundle exec i18n-tasks missing
# Check for unused translations bundle exec i18n-tasks unused
# Generate missing keys from default locale bundle exec i18n-tasks add-missing
# Copy translations from en to ja bundle exec i18n-tasks copy-missing en ja ```
- 1.Fix YAML locale file structure issues:
- 2.```yaml
- 3.# config/locales/views/users/en.yml
- 4.en:
- 5.users:
- 6.show:
- 7.title: "User Profile"
- 8.edit_button: "Edit Profile"
- 9.no_posts: "This user has not created any posts yet."
# config/locales/views/users/ja.yml ja: users: show: title: "ユーザープロフィール" edit_button: "プロフィール編集" no_posts: "このユーザーはまだ投稿を作成していません。" ```
- 1.Add CI check to prevent missing translations:
- 2.```yaml
- 3.# .github/workflows/i18n-check.yml
- 4.name: i18n Check
- 5.on: [pull_request]
- 6.jobs:
- 7.i18n:
- 8.runs-on: ubuntu-latest
- 9.steps:
- 10.- uses: actions/checkout@v4
- 11.- name: Check missing translations
- 12.run: bundle exec i18n-tasks missing --no-color
- 13.
`
Prevention
- Add
i18n-tasksto CI pipeline to block PRs with missing translations - Use
defaultoption inI18n.tcalls as a safety net:I18n.t('key', default: 'Fallback text') - Keep locale files organized by feature, not by language
- Use a translation management service (Crowdin, Transifex) for team workflows
- Run
i18n-tasks normalizeto keep YAML files consistently formatted - Add pre-commit hook to check for YAML syntax errors in locale files