Skip to content

Fix the reset form errors and translate the reset email - #1018

Merged
vjpixel merged 1 commit into
developfrom
claude/password-reset-email-i18n
Oct 19, 2026
Merged

vjpixel merged 1 commit into
developfrom
claude/password-reset-email-i18n

Conversation

@vjpixel

@vjpixel vjpixel commented Oct 18, 2026

Copy link
Copy Markdown
Member

Follow-up to #1016.

Changes

  • Reset form errors (password_reset.jinja2): the error loop used form.errors.items without calling it. Jinja2 raises TypeError: 'builtin_function_or_method' object is not iterable on that, so posting an invalid email to /users/reset-password/ returned a 500 instead of showing the error. Confirmed by rendering the snippet with Jinja2 and a dict of errors. Now calls items(), as password_reset_confirm.jinja2 already does.
  • Reset email and subject: PasswordResetView renders password_reset_email.html and password_reset_subject.txt through render_to_string. The Jinja2 backend comes first in TEMPLATES and the files live under jinja2/, so Jinja2 renders them with the i18n extension (newstyle gettext). Their text is now wrapped in _(), with the address passed as %(email)s. LocaleMiddleware has the request's language active when the email is sent, so the email goes out in the language the user browsed in.
  • etc/babel.cfg: the existing pattern only matches **.jinja2, so I added the two files. Otherwise make translate_<lang> would not extract their msgids and would mark them obsolete. Checked with pybabel extract -F ./etc/babel.cfg: it picks up the 5 new msgids.
  • pt_BR / es_ES: translations for the 5 new msgids. I edited the files by hand and kept CRLF. polib reports 0 untranslated, 0 fuzzy, no placeholder mismatches and no duplicates, and both catalogs compile. Rendering the templates with the compiled catalogs gives the translated subject and body with the address filled in.
  • Tests (src/users/tests/test_reset_password.py): posting an invalid email returns 200 and shows "Enter a valid email address." A registered email gets one message with the subject and the reset link.

The email body loses the leading indentation and the line breaks inside each paragraph, so each paragraph is one msgid.

Not changed: the email is plain text, but the address is still HTML-escaped by autoescape (e.g. o'neil@… becomes o&#39;neil@…). This was already the case before this PR.

🤖 Generated with Claude Code

- password_reset.jinja2 looped over `form.errors.items` without calling
  it, which Jinja2 rejects with "TypeError: 'builtin_function_or_method'
  object is not iterable", so posting an invalid email returned a 500
  instead of showing the error. Call `items()` as
  password_reset_confirm.jinja2 already does.
- password_reset_email.html and password_reset_subject.txt are rendered
  by the Jinja2 backend (it comes first in TEMPLATES and the files live
  under jinja2/), so wrap their text in _(), passing the address as
  %(email)s. Add both files to etc/babel.cfg so pybabel extracts them.
- pt_BR and es_ES: add translations for the new msgids.
- Add tests for the invalid-email error and for the reset email.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@vjpixel
vjpixel merged commit 921603e into develop Oct 19, 2026
8 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant