v2.13.0
What's new
spec.i18n locale tags now validated at deploy time (RFC 5646 canonical casing).
The most common bug with i18n configs: you write pt-BR in your spec.i18n.locales[] but your section_translations.language column has pt-br. Negotiation runs, picks pt-BR, queries the DB, gets no rows, falls back to default. You see a half-translated page and lose an afternoon to "why is Portuguese broken in production."
The v1.55 gateway now rejects this at deploy time before the broken release flips live:
{
"code": "R402_LOCALE_NOT_CANONICAL",
"http": 400,
"phase": "validate",
"message": "Locale \"pt-br\" is not in RFC 5646 canonical casing. Did you mean \"pt-BR\"? ...",
"fix": { "input": "pt-br", "canonical": "pt-BR" }
}
The fix envelope is structured so agents can auto-correct and retry without scraping the message string.
Canonical casing rules (RFC 5646 §2.1.1)
| Subtag kind | Casing | Example |
|---|---|---|
| Primary language | lowercase | pt, zh, en |
| Script (4 alpha) | Titlecase | Hant, Latn, Cyrl |
| Region (2 alpha) | UPPERCASE | BR, US, TW |
| Region (3 digit, UN M.49) | preserved | 419 |
| Variants / extensions | lowercase | 1996, nedis |
Combined: pt-BR, zh-Hant, zh-Hant-TW, de-1996, es-419.
Why not silently canonicalize
Translations are typically keyed on the literal locale string in your DB. Auto-canonicalizing the spec would create a silent split between the spec value and the column value. Hard-fail with fix keeps the spec and the DB byte-identical by construction.
Docs
cli/llms-cli.txt— i18n section updated with the casing rules + error envelopeSKILL.md— same content in the MCP-skill body
Also in this release
chore(deps): root devDependency@run402/functionsbumped^2.6.0→^2.7.0(matches the astro workspace; type resolutions now consistent across the monorepo).docs(publish skill): internal release-skill update — mini-article social posts instead of tweet-sized blurbs.
Compatibility
- Spec writers using non-canonical casing get a clear deploy-time error with a
fixblock. No runtime behavior change. - All earlier locale specs already passing the regex
/^[A-Za-z0-9][A-Za-z0-9._-]{0,63}$/AND already in canonical casing continue to work unchanged.
Links
Co-Authored-By: Claude Opus 4.7 (1M context)