Check Rails i18n YAML in CI
Rails raises I18n::MissingInterpolationArgument when a translation asks for a %{variable} the caller does not pass, and picks plural keys by the language's rules. The gate parses the YAML with line numbers, checks %{...} arguments, plural keys per CLDR and tags in _html strings.
What the gate reads
| Format id | Name | Extensions | Placeholders and plurals | Layout |
|---|---|---|---|---|
rails-yaml | Rails i18n YAML | .yml, .yaml | %{name}, %<name>s, one/few/many/other plural keys | One file per locale |
Detected layout
For a Ruby on Rails project laid out like this:
Gemfile
config/locales/en.yml
config/locales/fr.yml
config/locales/ru.ymlnpx shipglot check finds the files without a config. npx shipglot init writes what it detected to shipglot.config.json:
{
"sourceLocale": "en",
"files": [
{
"format": "rails-yaml",
"path": "config/locales/**/{locale}.yml"
}
]
}Add "locales" to restrict target languages, "glossary" for approved terms, and "checks" to change a check's severity. The gate's README lists every option.
GitHub Actions workflow
Save as .github/workflows/translations.yml. The step fails on errors and annotates the pull request; warnings show in the job summary.
name: Translations
on:
pull_request:
paths:
- "config/locales/**"
- "shipglot.config.json"
jobs:
shipglot:
runs-on: ubuntu-latest
permissions:
contents: read
steps:
- uses: actions/checkout@v5
- uses: aurelien64/shipglot@v0
with:
fail-on: errorTo see findings in GitHub code scanning as well, add sarif-file: shipglot.sarif under with: and upload it with github/codeql-action/upload-sarif@v3 (the job then needs security-events: write).
Defects the gate catches
Each example below was run through the gate when this page was built, with the config above. The findings under it are the gate's output.
1. A %{variable} dropped
The count disappears from the French notice, and a renamed variable would raise MissingInterpolationArgument.
config/locales/en.yml
en:
inbox:
unread: "You have %{count} unread messages from %{sender}"config/locales/fr.yml
fr:
inbox:
unread: "Vous avez des messages non lus de %{sender}"Gate output
inbox.unreadplaceholder mismatch: missing%{count}config/locales/fr.yml:3
2. Plural keys Russian needs
Rails looks up :few for 3 and :many for 5. With only :one and :other, those counts raise or fall back.
config/locales/en.yml
en:
messages:
one: "%{count} message"
other: "%{count} messages"config/locales/ru.yml
ru:
messages:
one: "%{count} сообщение"
other: "%{count} сообщений"Gate output
messagesplural is missing categories few, many required by ruconfig/locales/ru.yml:4
3. YAML that does not load
A bad indent makes the whole locale file fail to load when the app boots.
config/locales/en.yml
en:
inbox:
title: "Inbox"config/locales/fr.yml
fr:
inbox:
title: "Boîte de réception"
empty: "Aucun message"Gate output
- All mapping items must start at the same columnconfig/locales/fr.yml:4
emptynot in source locale enconfig/locales/fr.yml:4
4. A tag dropped from an _html string
Keys ending in _html are rendered as HTML. The French text lost the <strong> emphasis.
config/locales/en.yml
en:
billing:
notice_html: "Your trial ends in <strong>%{days} days</strong>."config/locales/fr.yml
fr:
billing:
notice_html: "Votre essai se termine dans %{days} jours."Gate output
billing.notice_htmlmarkup mismatch: missing<strong>,</strong>config/locales/fr.yml:3
Run it on your machine
npx shipglot check
npx shipglot check --reporter json=shipglot.jsonExit code 0 means clean, 1 means findings at or above --fail-on, 2 means a usage or config error. Checks used on this page: placeholder-mismatch, plural-forms, parse-error, markup-mismatch.
Translation in the same workflow
With a Shipglot account, npx shipglot translate translates new and changed keys and writes them into these files before the check runs. Hosted translation is not open yet; see the signup page.