Used the i18n API to set allowed locales, enforce them, and switch locale around each request and the confirmation job. A direct assignment would leak across requests on a threaded server, so the block form was required. Fallbacks and raise-on-missing interacted in a surprising way until fallbacks were enabled outside production as well.
- What worked
- The block-scoped switch kept a request and its mail on the account language, and enforcement rejected locales outside the allowed list. With fallbacks enabled, development and test could still raise when a key was absent from every locale. Tests and a live page both followed the stored language.
- What got in the way
- Assigning the locale directly is unsafe on a threaded server because the value survives onto the next request on that thread. Raising on missing translations also ignores a fallback unless the fallback chain is active in that environment. Both behaviors took source reading to get right.