mirror of
https://github.com/pocket-id/pocket-id.git
synced 2026-10-29 12:09:05 +01:00
feat(frontend): use plain-language validation messages
This commit is contained in:
@@ -1101,5 +1101,15 @@
|
||||
"register_your_first_api": "Register your first API",
|
||||
"create_your_first_api_key": "Create your first API key",
|
||||
"add_your_first_group": "Add your first group",
|
||||
"add_your_first_oidc_client": "Add your first OIDC client"
|
||||
"add_your_first_oidc_client": "Add your first OIDC client",
|
||||
"validation_required": "Required",
|
||||
"validation_min_length": "Enter at least {count} characters",
|
||||
"validation_max_length": "Enter at most {count} characters",
|
||||
"validation_min_number": "Enter {minimum} or more",
|
||||
"validation_max_number": "Enter {maximum} or less",
|
||||
"validation_min_items": "Choose at least {count}",
|
||||
"validation_max_items": "Choose at most {count}",
|
||||
"validation_email": "Enter a valid email address",
|
||||
"validation_url": "Enter a valid URL",
|
||||
"validation_number": "Enter a number"
|
||||
}
|
||||
|
||||
@@ -1,25 +1,23 @@
|
||||
import {
|
||||
extractLocaleFromCookie,
|
||||
setLocale as setParaglideLocale,
|
||||
type Locale
|
||||
} from '#lib/paraglide/runtime.js';
|
||||
import { getLocale, setLocale as setParaglideLocale, type Locale } from '#lib/paraglide/runtime.js';
|
||||
import { setDefaultOptions } from 'date-fns';
|
||||
import { z } from 'zod/v4';
|
||||
import { validationErrorMap } from './validation-error-util';
|
||||
|
||||
export async function setLocale(locale: Locale, reload = true) {
|
||||
await setLocaleForLibraries(locale);
|
||||
setParaglideLocale(locale, { reload });
|
||||
document.documentElement.lang = locale;
|
||||
}
|
||||
|
||||
export async function setLocaleForLibraries(
|
||||
locale: Locale = (extractLocaleFromCookie() as Locale) || 'en'
|
||||
) {
|
||||
export async function setLocaleForLibraries(locale: Locale = getLocale()) {
|
||||
document.documentElement.lang = locale;
|
||||
|
||||
let dateFnsLocale: string = locale;
|
||||
if (dateFnsLocale === 'en') {
|
||||
dateFnsLocale = 'en-US'; // datefns doesn't have 'en'
|
||||
}
|
||||
|
||||
z.config({ customError: validationErrorMap });
|
||||
|
||||
const [zodResult, dateFnsResult] = await Promise.allSettled([
|
||||
import(`../../../node_modules/zod/v4/locales/${locale}.js`),
|
||||
import(`../../../node_modules/date-fns/locale/${dateFnsLocale}.js`)
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
import { m } from '#lib/paraglide/messages.js';
|
||||
import type { z } from 'zod/v4';
|
||||
|
||||
const isEmpty = (input: unknown) => input === undefined || input === null || input === '';
|
||||
|
||||
// Plain-language messages for the checks forms use; anything not covered falls back to Zod's own locale
|
||||
export const validationErrorMap: z.core.$ZodErrorMap = (issue) => {
|
||||
switch (issue.code) {
|
||||
case 'invalid_type':
|
||||
if (isEmpty(issue.input)) return m.validation_required();
|
||||
if (issue.expected === 'number' || issue.expected === 'int') return m.validation_number();
|
||||
return undefined;
|
||||
case 'too_small': {
|
||||
const count = Number(issue.minimum);
|
||||
if (issue.origin === 'string') {
|
||||
return count <= 1 ? m.validation_required() : m.validation_min_length({ count });
|
||||
}
|
||||
if (issue.origin === 'number' || issue.origin === 'int') {
|
||||
return m.validation_min_number({ minimum: count });
|
||||
}
|
||||
if (issue.origin === 'array' || issue.origin === 'set')
|
||||
return m.validation_min_items({ count });
|
||||
return undefined;
|
||||
}
|
||||
case 'too_big': {
|
||||
const count = Number(issue.maximum);
|
||||
if (issue.origin === 'string') return m.validation_max_length({ count });
|
||||
if (issue.origin === 'number' || issue.origin === 'int') {
|
||||
return m.validation_max_number({ maximum: count });
|
||||
}
|
||||
if (issue.origin === 'array' || issue.origin === 'set')
|
||||
return m.validation_max_items({ count });
|
||||
return undefined;
|
||||
}
|
||||
case 'invalid_format':
|
||||
if (isEmpty(issue.input)) return m.validation_required();
|
||||
if (issue.format === 'email') return m.validation_email();
|
||||
if (issue.format === 'url') return m.validation_url();
|
||||
return undefined;
|
||||
default:
|
||||
return undefined;
|
||||
}
|
||||
};
|
||||
@@ -208,9 +208,10 @@ test.describe('User Signup', () => {
|
||||
|
||||
await page.goto('/signup');
|
||||
|
||||
await page.getByLabel('Email').fill('not-an-email');
|
||||
await page.getByRole('button', { name: 'Create account' }).click();
|
||||
|
||||
await expect(page.getByText('Invalid email address').first()).toBeVisible();
|
||||
await expect(page.getByText('Enter a valid email address')).toBeVisible();
|
||||
});
|
||||
|
||||
test('Open signup - duplicate email shows error', async ({ page }) => {
|
||||
|
||||
Reference in New Issue
Block a user