Signup email validation message ignores the app language
- Dominant language
- TypeScript
- Stars
- 156k
- Forks
- 24.6k
- Avg merge
- 22h 9m
- Merged PRs (30d)
- 610
Description
### Self Checks
- [X] I have read the [Contributing Guide](https://github.com/langgenius/dify/blob/main/CONTRIBUTING.md) and [Language Policy](https://github.com/langgenius/dify/issues/1542).
- [X] This is only for bug report, if you would like to ask a question, please head to [Discussions](https://github.com/langgenius/dify/discussions/categories/general).
- [X] I have searched for existing issues [search for existing issues](https://github.com/langgenius/dify/issues), including closed ones.
- [X] I confirm that I am using English to submit this report, otherwise it will be closed.
- [X] 【中文用户 & Non English User】请使用英语提交,否则会被关闭 :)
- [X] Please do not modify this template :) and fill in all the required fields.
### Dify version
1.16.0-rc1 (read from `main` @ `120c38ba`)
### Cloud or Self Hosted
Self Hosted (Source)
### Steps to reproduce
Client-side only, so it applies to Cloud too.
1. Set the browser's language to something other than the app's — e.g. app 简体中文, browser German.
2. Go to `/signup`.
3. Type `abc` into the email field and press "Verify".
### ✔️ Expected Behavior
The invalid-email message in the app's language: 请输入有效的邮箱地址.
That string already exists — `error.emailInValid` in `web/i18n//login.json`, translated in all 23 locales.
### ❌ Actual Behavior
The browser's own message, in the **browser's** language, ignoring the app's:
| browser | shown today |
| --- | --- |
| de | Die E-Mail-Adresse muss ein @-Zeichen enthalten. In der Angabe "abc" fehlt ein @-Zeichen. |
| ja | メール アドレスに「@」を挿入してください。「abc」内に「@」がありません。 |
| fr | Veuillez inclure "@" dans l'adresse e-mail. Il manque un symbole "@" dans "abc". |
`web/app/signup/components/input-mail.tsx` has `` inside a `` with no `noValidate`, so the browser runs constraint validation before `submit` fires. For a value the browser rejects, `handleSubmit` never runs — and `handleSubmit` is where the translated messages live:
```ts
if (!email) {
toast.error(t(($) => $['error.emailEmpty'], { ns: 'login' }))
return
}
if (!emailRegex.test(email)) {
toast.error(t(($) => $['error.emailInValid'], { ns: 'login' }))
return
}
```
Which message you get depends on *how* the email is wrong, because `emailRegex` and the browser disagree:
- `abc` — browser rejects → browser's language, `handleSubmit` never runs
- `a@b` — browser accepts, `emailRegex` rejects → 请输入有效的邮箱地址
So `error.emailInValid` is unreachable for the most common typo there is, and reachable only for a rarer one. The submit button is `disabled={isPending || !email}`, so the empty case can't be reached from the UI and `error.emailEmpty` never shows either.
#### Where it came from
This looks like a side effect of #30455 / #30456 (`refactor(web): align signup mail submit and tests`, merged 2026-01-04), which moved this form from `onSubmit={noop}` + button `onClick` to a real `` with `type="submit"`. That's the right call for form semantics — but submitting a form for real also runs the browser's constraint validation first, and `type="email"` now intercepts submit before `handleSubmit` can report the translated message.
The code matches: the forms that were not refactored — `(shareLayout)/webapp-signin/*` and `webapp-reset-password`, still `onSubmit={noop}` with no submit button — never trigger native validation and are unaffected. Only the refactored signup form is.
`input-mail.spec.tsx` (added by that same PR) can't catch it either: it drives the form with `fireEvent.submit(form)`, which dispatches the event directly and skips constraint validation entirely. The tests pass precisely because they bypass the path that breaks in a browser.
#### Suggested fix
Add `noValidate` to the form. The app already validates both cases and already has both messages translated, so this just lets them fire:
```diff
{
e.preventDefault()
handleSubmit()
}}
>
```
`type="email"` stays, so the mobile email keyboard is unaffected — `noValidate` only turns off constraint validation. No new strings and nothing for translators.
A regression test belongs in the existing `input-mail.spec.tsx`, but it has to drive the form the way a browser does — `fireEvent.submit` won't reproduce this. Asserting on `input.checkValidity()` / `validity.valid` covers it without needing a real browser.
Happy to open the PR if you'd like to assign this to me.
Contributor guide
Research direction
Start with web/app/signup/components/input-mail.tsx and the existing input-mail.spec.tsx test. Check how native form validation affects submission for an invalid email; the issue notes that fireEvent.submit bypasses this behavior and suggests checking input.checkValidity() or validity.valid. Done when an invalid email produces the app-language message and the regression test covers the browser validation path.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- frontend, internationalization
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Quiet
- Clarity
- Clearly specified
- Newbie friendliness
- 74/100