diff --git a/packages/core/src/locales.ts b/packages/core/src/locales.ts index 5e0ed62a75..b7180c3400 100644 --- a/packages/core/src/locales.ts +++ b/packages/core/src/locales.ts @@ -1,5 +1,6 @@ import az from "./locales/az.js"; import en from "./locales/en.js"; import es from "./locales/es.js"; +import ja from "./locales/ja.js"; -export { az, es, en }; +export { az, es, en, ja }; diff --git a/packages/core/src/locales/ja.ts b/packages/core/src/locales/ja.ts new file mode 100644 index 0000000000..ff1ad5f8ef --- /dev/null +++ b/packages/core/src/locales/ja.ts @@ -0,0 +1,122 @@ +import type { $ZodStringFormats } from "../checks.js"; +import type * as errors from "../errors.js"; +import * as util from "../util.js"; + +const Sizable: Record = { + string: { unit: "文字", verb: "である" }, + file: { unit: "バイト", verb: "である" }, + array: { unit: "要素", verb: "である" }, + set: { unit: "要素", verb: "である" }, +}; + +function getSizing(origin: string): { unit: string; verb: string } | null { + return Sizable[origin] ?? null; +} + +export const parsedType = (data: any): string => { + const t = typeof data; + + switch (t) { + case "number": { + return Number.isNaN(data) ? "NaN" : "数値"; + } + case "object": { + if (Array.isArray(data)) { + return "配列"; + } + if (data === null) { + return "null"; + } + + if (Object.getPrototypeOf(data) !== Object.prototype && data.constructor) { + return data.constructor.name; + } + } + } + return t; +}; + +const Nouns: { + [k in $ZodStringFormats | (string & {})]?: string; +} = { + regex: "入力値", + email: "メールアドレス", + url: "URL", + emoji: "絵文字", + uuid: "UUID", + uuidv4: "UUIDv4", + uuidv6: "UUIDv6", + nanoid: "nanoid", + guid: "GUID", + cuid: "cuid", + cuid2: "cuid2", + ulid: "ULID", + xid: "XID", + ksuid: "KSUID", + datetime: "ISO日時", + date: "ISO日付", + time: "ISO時刻", + duration: "ISO期間", + ipv4: "IPv4アドレス", + ipv6: "IPv6アドレス", + cidrv4: "IPv4範囲", + cidrv6: "IPv6範囲", + base64: "base64エンコード文字列", + base64url: "base64urlエンコード文字列", + json_string: "JSON文字列", + e164: "E.164番号", + jwt: "JWT", + template_literal: "入力値", +}; + +const error: errors.$ZodErrorMap = (issue) => { + switch (issue.code) { + case "invalid_type": + return `無効な入力: ${issue.expected}が期待されましたが、${parsedType(issue.input)}が入力されました`; + case "invalid_value": + if (issue.values.length === 1) return `無効な入力: ${util.stringifyPrimitive(issue.values[0])}が期待されました`; + return `無効な選択: ${util.joinValues(issue.values, "、")}のいずれかである必要があります`; + case "too_big": { + const adj = issue.inclusive ? "<=" : "<"; + const sizing = getSizing(issue.origin); + if (sizing) + return `大きすぎる値: ${issue.origin ?? "値"}は${issue.maximum.toString()}${sizing.unit ?? "要素"}${adj}である必要があります`; + return `大きすぎる値: ${issue.origin ?? "値"}は${issue.maximum.toString()}${adj}である必要があります`; + } + case "too_small": { + const adj = issue.inclusive ? ">=" : ">"; + const sizing = getSizing(issue.origin); + if (sizing) + return `小さすぎる値: ${issue.origin}は${issue.minimum.toString()}${sizing.unit}${adj}である必要があります`; + return `小さすぎる値: ${issue.origin}は${issue.minimum.toString()}${adj}である必要があります`; + } + case "invalid_format": { + const _issue = issue as errors.$ZodStringFormatIssues; + if (_issue.format === "starts_with") return `無効な文字列: "${issue}"で始まる必要があります`; + if (_issue.format === "ends_with") return `無効な文字列: "${_issue.suffix}"で終わる必要があります`; + if (_issue.format === "includes") return `無効な文字列: "${_issue.includes}"を含む必要があります`; + if (_issue.format === "regex") return `無効な文字列: パターン${_issue.pattern}に一致する必要があります`; + return `無効な${Nouns[_issue.format] ?? issue.format}`; + } + case "not_multiple_of": + return `無効な数値: ${issue.divisor}の倍数である必要があります`; + case "unrecognized_keys": + return `認識されていないキー${issue.keys.length > 1 ? "群" : ""}: ${util.joinValues(issue.keys, "、")}`; + case "invalid_key": + return `${issue.origin}内の無効なキー`; + case "invalid_union": + return "無効な入力"; + case "invalid_element": + return `${issue.origin}内の無効な値`; + default: + return `無効な入力`; + } +}; + +export { error }; + +export default function (): { localeError: errors.$ZodErrorMap } { + return { + localeError: error, + }; +} diff --git a/packages/docs/content/error-customization.mdx b/packages/docs/content/error-customization.mdx index 5795950067..8125c1218e 100644 --- a/packages/docs/content/error-customization.mdx +++ b/packages/docs/content/error-customization.mdx @@ -357,6 +357,7 @@ The following locales are available: - `az` — Azerbaijani - `en` — English +- `ja` — Japanese ## Error precedence @@ -389,4 +390,3 @@ Below is a quick reference for determining error precedence: if multiple error c ```ts z.config(z.locales.en()); ``` -