From 9b529764a7055f6d13b1b01790eba7cd25216583 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 18 Dec 2022 03:40:21 +0000 Subject: [PATCH 1/3] docs: update README.md [skip ci] --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 0ff3d86..3e47c04 100644 --- a/README.md +++ b/README.md @@ -60,7 +60,7 @@ This project is licensed under the MIT License - see the [LICENSE](https://githu ## Contributors ✨ -[![All Contributors](https://img.shields.io/badge/all_contributors-6-orange.svg?style=flat-square)](#contributors-) +[![All Contributors](https://img.shields.io/badge/all_contributors-7-orange.svg?style=flat-square)](#contributors-) Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/docs/en/emoji-key)): @@ -77,6 +77,7 @@ Thanks goes to these wonderful people ([emoji key](https://allcontributors.org/d Luiz Oliveira Montedonio
Luiz Oliveira Montedonio

🌍 ⚠️ Izayoi Hibiki
Izayoi Hibiki

🌍 ⚠️ Hrafnkell Baldursson
Hrafnkell Baldursson

🌍 ⚠️ + Arturo
Arturo

🌍 ⚠️ From 60c74b589505b6c9666eca9d9d295533484a9787 Mon Sep 17 00:00:00 2001 From: "allcontributors[bot]" <46447321+allcontributors[bot]@users.noreply.github.com> Date: Sun, 18 Dec 2022 03:40:22 +0000 Subject: [PATCH 2/3] docs: update .all-contributorsrc [skip ci] --- .all-contributorsrc | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/.all-contributorsrc b/.all-contributorsrc index ca3d78a..63916f4 100644 --- a/.all-contributorsrc +++ b/.all-contributorsrc @@ -67,6 +67,16 @@ "translation", "test" ] + }, + { + "login": "Arturo-Lopez", + "name": "Arturo", + "avatar_url": "https://avatars.githubusercontent.com/u/35613775?v=4", + "profile": "http://arturolopez.co", + "contributions": [ + "translation", + "test" + ] } ], "contributorsPerLine": 7, From 83afe58df7c49f482d4dcfd084cf1a2ddc2f618c Mon Sep 17 00:00:00 2001 From: Arturo <35613775+Arturo-Lopez@users.noreply.github.com> Date: Sun, 18 Dec 2022 00:40:40 -0300 Subject: [PATCH 3/3] feat: add es (spanish) locale * feat: add es (spanish) locale * feat: updated es locale and added tests * feat: updated es locale, es test and added es locale to with-next-i18next --- .../with-next-i18next/next-i18next.config.js | 2 +- examples/with-next-i18next/pages/index.tsx | 1 + .../public/locales/es/common.json | 7 + .../public/locales/es/zod.json | 112 ++++++++++ packages/core/locales/es/zod.json | 112 ++++++++++ packages/core/tests/integrations/es.test.ts | 199 ++++++++++++++++++ 6 files changed, 432 insertions(+), 1 deletion(-) create mode 100644 examples/with-next-i18next/public/locales/es/common.json create mode 100644 examples/with-next-i18next/public/locales/es/zod.json create mode 100644 packages/core/locales/es/zod.json create mode 100644 packages/core/tests/integrations/es.test.ts diff --git a/examples/with-next-i18next/next-i18next.config.js b/examples/with-next-i18next/next-i18next.config.js index 9fbe9de..524069d 100644 --- a/examples/with-next-i18next/next-i18next.config.js +++ b/examples/with-next-i18next/next-i18next.config.js @@ -5,7 +5,7 @@ const path = require("path"); module.exports = { i18n: { defaultLocale: "en", - locales: ["en", "ja", "fr", "ar", "pt", "zh-CN", "is"], + locales: ["en", "ja", "fr", "ar", "pt", "zh-CN", "is", "es"], }, localePath: path.resolve("./public/locales"), interpolation: { diff --git a/examples/with-next-i18next/pages/index.tsx b/examples/with-next-i18next/pages/index.tsx index a325ead..e95bb90 100644 --- a/examples/with-next-i18next/pages/index.tsx +++ b/examples/with-next-i18next/pages/index.tsx @@ -77,6 +77,7 @@ export default function HookForm() { borderLeftRadius={0} > + diff --git a/examples/with-next-i18next/public/locales/es/common.json b/examples/with-next-i18next/public/locales/es/common.json new file mode 100644 index 0000000..ee1b30a --- /dev/null +++ b/examples/with-next-i18next/public/locales/es/common.json @@ -0,0 +1,7 @@ +{ + "User name": "Nombre de usuario", + "John Doe": "John Doe", + "Email": "Correo", + "Favorite number": "Número favorito", + "Submit": "Enviar" +} diff --git a/examples/with-next-i18next/public/locales/es/zod.json b/examples/with-next-i18next/public/locales/es/zod.json new file mode 100644 index 0000000..7299947 --- /dev/null +++ b/examples/with-next-i18next/public/locales/es/zod.json @@ -0,0 +1,112 @@ +{ + "errors": { + "invalid_type": "Esperado {{expected}}, recibido {{received}}", + "invalid_type_received_undefined": "Requerido", + "invalid_literal": "Valor literal inválido, era esperado {{expected}}", + "unrecognized_keys": "Llave(s) no reconocida(s) en el objeto: {{- keys}}", + "invalid_union": "Entrada inválida", + "invalid_union_discriminator": "Valor discriminador inválido. Era esperado {{- options}}", + "invalid_enum_value": "Valor de enum inválido. Era esperado {{- options}}, fue recibido '{{received}}'", + "invalid_arguments": "Argumentos de función inválido", + "invalid_return_type": "Tipo de retorno de función inválido", + "invalid_date": "Fecha inválida", + "custom": "Entrada inválida", + "invalid_intersection_types": "Valores de intersección no pudieron ser mezclados", + "not_multiple_of": "Número debe ser multiplo de {{multipleOf}}", + "not_finite": "Número no puede ser infinito", + "invalid_string": { + "email": "{{validation}} inválido", + "url": "{{validation}} inválida", + "uuid": "{{validation}} inválido", + "cuid": "{{validation}} inválido", + "regex": "Inválido", + "datetime": "{{validation}} inválido", + "startsWith": "Entrada inválida: debe comenzar con \"{{startsWith}}\"", + "endsWith": "Entrada inválida: debe finalizar con \"{{endsWith}}\"" + }, + "too_small": { + "array": { + "exact": "El array debe contener exactamente {{minimum}} elemento(s)", + "inclusive": "El array debe contener al menos {{minimum}} elemento(s)", + "not_inclusive": "El array debe contener más de {{minimum}} elemento(s)" + }, + "string": { + "exact": "El texto debe contener exactamente {{minimum}} carácter(es)", + "inclusive": "El texto debe contener al menos {{minimum}} carácter(es)", + "not_inclusive": "El texto debe contener más de {{minimum}} carácter(es)" + }, + "number": { + "exact": "El número debe ser exactamente {{minimum}}", + "inclusive": "El número debe ser mayor o igual a {{minimum}}", + "not_inclusive": "El número debe ser mayor a {{minimum}}" + }, + "set": { + "exact": "Entrada inválida", + "inclusive": "Entrada inválida", + "not_inclusive": "Entrada inválida" + }, + "date": { + "exact": "La fecha debe ser exactamente {{- minimum, datetime}}", + "inclusive": "La fecha debe ser mayor o igual a {{- minimum, datetime}}", + "not_inclusive": "La fecha debe ser mayor a {{- minimum, datetime}}" + } + }, + "too_big": { + "array": { + "exact": "El array debe contener exactamente {{maximum}} elemento(s)", + "inclusive": "El array debe contener como máximo {{maximum}} elemento(s)", + "not_inclusive": "El array debe contener menos que {{maximum}} elemento(s)" + }, + "string": { + "exact": "El texto debe contener exactamente {{maximum}} carácter(es)", + "inclusive": "El texto debe contener como máximo {{maximum}} carácter(es)", + "not_inclusive": "El texto debe contener menos que {{maximum}} carácter(es)" + }, + "number": { + "exact": "El número debe ser exactamente {{maximum}}", + "inclusive": "El número debe ser menor o igual a {{maximum}}", + "not_inclusive": "El número debe ser menor a {{maximum}}" + }, + "set": { + "exact": "Entrada inválida", + "inclusive": "Entrada inválida", + "not_inclusive": "Entrada inválida" + }, + "date": { + "exact": "La fecha debe ser exactamente {{- maximum, datetime}}", + "inclusive": "La fecha debe ser menor o igual a {{- maximum, datetime}}", + "not_inclusive": "La fecha debe ser menor a {{- maximum, datetime}}" + } + } + }, + "validations": { + "email": "correo", + "url": "url", + "uuid": "uuid", + "cuid": "cuid", + "regex": "expresión regular", + "datetime": "datetime" + }, + "types": { + "function": "function", + "number": "number", + "string": "string", + "nan": "nan", + "integer": "integer", + "float": "float", + "boolean": "boolean", + "date": "date", + "bigint": "bigint", + "undefined": "undefined", + "symbol": "symbol", + "null": "null", + "array": "array", + "object": "object", + "unknown": "unknown", + "promise": "promise", + "void": "void", + "never": "never", + "map": "map", + "set": "set" + } +} diff --git a/packages/core/locales/es/zod.json b/packages/core/locales/es/zod.json new file mode 100644 index 0000000..7299947 --- /dev/null +++ b/packages/core/locales/es/zod.json @@ -0,0 +1,112 @@ +{ + "errors": { + "invalid_type": "Esperado {{expected}}, recibido {{received}}", + "invalid_type_received_undefined": "Requerido", + "invalid_literal": "Valor literal inválido, era esperado {{expected}}", + "unrecognized_keys": "Llave(s) no reconocida(s) en el objeto: {{- keys}}", + "invalid_union": "Entrada inválida", + "invalid_union_discriminator": "Valor discriminador inválido. Era esperado {{- options}}", + "invalid_enum_value": "Valor de enum inválido. Era esperado {{- options}}, fue recibido '{{received}}'", + "invalid_arguments": "Argumentos de función inválido", + "invalid_return_type": "Tipo de retorno de función inválido", + "invalid_date": "Fecha inválida", + "custom": "Entrada inválida", + "invalid_intersection_types": "Valores de intersección no pudieron ser mezclados", + "not_multiple_of": "Número debe ser multiplo de {{multipleOf}}", + "not_finite": "Número no puede ser infinito", + "invalid_string": { + "email": "{{validation}} inválido", + "url": "{{validation}} inválida", + "uuid": "{{validation}} inválido", + "cuid": "{{validation}} inválido", + "regex": "Inválido", + "datetime": "{{validation}} inválido", + "startsWith": "Entrada inválida: debe comenzar con \"{{startsWith}}\"", + "endsWith": "Entrada inválida: debe finalizar con \"{{endsWith}}\"" + }, + "too_small": { + "array": { + "exact": "El array debe contener exactamente {{minimum}} elemento(s)", + "inclusive": "El array debe contener al menos {{minimum}} elemento(s)", + "not_inclusive": "El array debe contener más de {{minimum}} elemento(s)" + }, + "string": { + "exact": "El texto debe contener exactamente {{minimum}} carácter(es)", + "inclusive": "El texto debe contener al menos {{minimum}} carácter(es)", + "not_inclusive": "El texto debe contener más de {{minimum}} carácter(es)" + }, + "number": { + "exact": "El número debe ser exactamente {{minimum}}", + "inclusive": "El número debe ser mayor o igual a {{minimum}}", + "not_inclusive": "El número debe ser mayor a {{minimum}}" + }, + "set": { + "exact": "Entrada inválida", + "inclusive": "Entrada inválida", + "not_inclusive": "Entrada inválida" + }, + "date": { + "exact": "La fecha debe ser exactamente {{- minimum, datetime}}", + "inclusive": "La fecha debe ser mayor o igual a {{- minimum, datetime}}", + "not_inclusive": "La fecha debe ser mayor a {{- minimum, datetime}}" + } + }, + "too_big": { + "array": { + "exact": "El array debe contener exactamente {{maximum}} elemento(s)", + "inclusive": "El array debe contener como máximo {{maximum}} elemento(s)", + "not_inclusive": "El array debe contener menos que {{maximum}} elemento(s)" + }, + "string": { + "exact": "El texto debe contener exactamente {{maximum}} carácter(es)", + "inclusive": "El texto debe contener como máximo {{maximum}} carácter(es)", + "not_inclusive": "El texto debe contener menos que {{maximum}} carácter(es)" + }, + "number": { + "exact": "El número debe ser exactamente {{maximum}}", + "inclusive": "El número debe ser menor o igual a {{maximum}}", + "not_inclusive": "El número debe ser menor a {{maximum}}" + }, + "set": { + "exact": "Entrada inválida", + "inclusive": "Entrada inválida", + "not_inclusive": "Entrada inválida" + }, + "date": { + "exact": "La fecha debe ser exactamente {{- maximum, datetime}}", + "inclusive": "La fecha debe ser menor o igual a {{- maximum, datetime}}", + "not_inclusive": "La fecha debe ser menor a {{- maximum, datetime}}" + } + } + }, + "validations": { + "email": "correo", + "url": "url", + "uuid": "uuid", + "cuid": "cuid", + "regex": "expresión regular", + "datetime": "datetime" + }, + "types": { + "function": "function", + "number": "number", + "string": "string", + "nan": "nan", + "integer": "integer", + "float": "float", + "boolean": "boolean", + "date": "date", + "bigint": "bigint", + "undefined": "undefined", + "symbol": "symbol", + "null": "null", + "array": "array", + "object": "object", + "unknown": "unknown", + "promise": "promise", + "void": "void", + "never": "never", + "map": "map", + "set": "set" + } +} diff --git a/packages/core/tests/integrations/es.test.ts b/packages/core/tests/integrations/es.test.ts new file mode 100644 index 0000000..50d9c86 --- /dev/null +++ b/packages/core/tests/integrations/es.test.ts @@ -0,0 +1,199 @@ +import { test, expect, beforeAll } from "vitest"; +import { z } from "zod"; +import { init, getErrorMessage, getErrorMessageFromZodError } from "./helpers"; + +const LOCALE = "es"; + +beforeAll(async () => { + await init(LOCALE); +}); + +test("string parser error messages", () => { + const schema = z.string(); + + expect(getErrorMessage(schema.safeParse(undefined))).toEqual("Requerido"); + expect(getErrorMessage(schema.safeParse(1))).toEqual( + "Esperado string, recibido number" + ); + expect(getErrorMessage(schema.safeParse(true))).toEqual( + "Esperado string, recibido boolean" + ); + expect(getErrorMessage(schema.safeParse(Date))).toEqual( + "Esperado string, recibido function" + ); + expect(getErrorMessage(schema.safeParse(new Date()))).toEqual( + "Esperado string, recibido date" + ); + expect(getErrorMessage(schema.email().safeParse(""))).toEqual( + "correo inválido" + ); + expect(getErrorMessage(schema.url().safeParse(""))).toEqual("url inválida"); + expect(getErrorMessage(schema.regex(/aaa/).safeParse(""))).toEqual( + "Inválido" + ); + expect(getErrorMessage(schema.startsWith("foo").safeParse(""))).toEqual( + 'Entrada inválida: debe comenzar con "foo"' + ); + expect(getErrorMessage(schema.endsWith("bar").safeParse(""))).toEqual( + 'Entrada inválida: debe finalizar con "bar"' + ); + expect(getErrorMessage(schema.min(5).safeParse("a"))).toEqual( + "El texto debe contener al menos 5 carácter(es)" + ); + expect(getErrorMessage(schema.max(5).safeParse("abcdef"))).toEqual( + "El texto debe contener como máximo 5 carácter(es)" + ); + expect(getErrorMessage(schema.length(5).safeParse("abcdef"))).toEqual( + "El texto debe contener exactamente 5 carácter(es)" + ); + expect( + getErrorMessage(schema.datetime().safeParse("2020-01-01T00:00:00+02:00")) + ).toEqual("datetime inválido"); +}); + +test("number parser error messages", () => { + const schema = z.number(); + + expect(getErrorMessage(schema.safeParse(undefined))).toEqual("Requerido"); + expect(getErrorMessage(schema.safeParse(""))).toEqual( + "Esperado number, recibido string" + ); + expect(getErrorMessage(schema.safeParse(null))).toEqual( + "Esperado number, recibido null" + ); + expect(getErrorMessage(schema.safeParse(NaN))).toEqual( + "Esperado number, recibido nan" + ); + expect(getErrorMessage(schema.int().safeParse(0.1))).toEqual( + "Esperado integer, recibido float" + ); + expect(getErrorMessage(schema.multipleOf(5).safeParse(2))).toEqual( + "Número debe ser multiplo de 5" + ); + expect(getErrorMessage(schema.step(0.1).safeParse(0.0001))).toEqual( + "Número debe ser multiplo de 0.1" + ); + expect(getErrorMessage(schema.lt(5).safeParse(10))).toEqual( + "El número debe ser menor a 5" + ); + expect(getErrorMessage(schema.lte(5).safeParse(10))).toEqual( + "El número debe ser menor o igual a 5" + ); + expect(getErrorMessage(schema.gt(5).safeParse(1))).toEqual( + "El número debe ser mayor a 5" + ); + expect(getErrorMessage(schema.gte(5).safeParse(1))).toEqual( + "El número debe ser mayor o igual a 5" + ); + expect(getErrorMessage(schema.nonnegative().safeParse(-1))).toEqual( + "El número debe ser mayor o igual a 0" + ); + expect(getErrorMessage(schema.nonpositive().safeParse(1))).toEqual( + "El número debe ser menor o igual a 0" + ); + expect(getErrorMessage(schema.negative().safeParse(1))).toEqual( + "El número debe ser menor a 0" + ); + expect(getErrorMessage(schema.positive().safeParse(0))).toEqual( + "El número debe ser mayor a 0" + ); + expect(getErrorMessage(schema.finite().safeParse(Infinity))).toEqual( + "Número no puede ser infinito" + ); +}); + +test("date parser error messages", async () => { + const testDate = new Date("2022-08-01"); + const schema = z.date(); + + expect(getErrorMessage(schema.safeParse("2022-12-01"))).toEqual( + "Esperado date, recibido string" + ); + expect( + getErrorMessage(schema.min(testDate).safeParse(new Date("2022-07-29"))) + ).toEqual( + `La fecha debe ser mayor o igual a ${testDate.toLocaleDateString(LOCALE)}` + ); + expect( + getErrorMessage(schema.max(testDate).safeParse(new Date("2022-08-02"))) + ).toEqual( + `La fecha debe ser menor o igual a ${testDate.toLocaleDateString(LOCALE)}` + ); + try { + await schema.parseAsync(new Date("invalid")); + } catch (err) { + expect((err as z.ZodError).issues[0].message).toEqual("Fecha inválida"); + } +}); + +test("array parser error messages", () => { + const schema = z.string().array(); + + expect(getErrorMessage(schema.safeParse(""))).toEqual( + "Esperado array, recibido string" + ); + expect(getErrorMessage(schema.min(5).safeParse([""]))).toEqual( + "El array debe contener al menos 5 elemento(s)" + ); + expect(getErrorMessage(schema.max(2).safeParse(["", "", ""]))).toEqual( + "El array debe contener como máximo 2 elemento(s)" + ); + expect(getErrorMessage(schema.nonempty().safeParse([]))).toEqual( + "El array debe contener al menos 1 elemento(s)" + ); + expect(getErrorMessage(schema.length(2).safeParse([]))).toEqual( + "El array debe contener exactamente 2 elemento(s)" + ); +}); + +test("function parser error messages", () => { + const functionParse = z + .function(z.tuple([z.string()]), z.number()) + .parse((a: any) => a); + expect(getErrorMessageFromZodError(() => functionParse(""))).toEqual( + "Tipo de retorno de función inválido" + ); + expect(getErrorMessageFromZodError(() => functionParse(1 as any))).toEqual( + "Argumentos de función inválido" + ); +}); + +test("other parser error messages", () => { + expect( + getErrorMessage( + z + .intersection( + z.number(), + z.number().transform((x) => x + 1) + ) + .safeParse(1234) + ) + ).toEqual("Valores de intersección no pudieron ser mezclados"); + expect(getErrorMessage(z.literal(12).safeParse(""))).toEqual( + "Valor literal inválido, era esperado 12" + ); + expect(getErrorMessage(z.enum(["A", "B", "C"]).safeParse("D"))).toEqual( + "Valor de enum inválido. Era esperado 'A' | 'B' | 'C', fue recibido 'D'" + ); + expect( + getErrorMessage( + z + .object({ dog: z.string() }) + .strict() + .safeParse({ dog: "", cat: "", rat: "" }) + ) + ).toEqual("Llave(s) no reconocida(s) en el objeto: 'cat', 'rat'"); + expect( + getErrorMessage( + z + .discriminatedUnion("type", [ + z.object({ type: z.literal("a"), a: z.string() }), + z.object({ type: z.literal("b"), b: z.string() }), + ]) + .safeParse({ type: "c", c: "abc" }) + ) + ).toEqual("Valor discriminador inválido. Era esperado 'a' | 'b'"); + expect( + getErrorMessage(z.union([z.string(), z.number()]).safeParse([true])) + ).toEqual("Entrada inválida"); +});