From 3b3cb083e7ec0c909494dd38c1630f92b855fded Mon Sep 17 00:00:00 2001 From: Ilham Date: Sun, 10 Dec 2023 18:30:42 +0800 Subject: [PATCH] feat: add locale id --- .../with-next-i18next/next-i18next.config.js | 1 + examples/with-next-i18next/pages/index.tsx | 1 + .../public/locales/id/common.json | 7 + .../public/locales/id/zod.json | 112 +++++++++ packages/core/locales/id/zod.json | 112 +++++++++ packages/core/tests/integrations/id.test.ts | 217 ++++++++++++++++++ 6 files changed, 450 insertions(+) create mode 100644 examples/with-next-i18next/public/locales/id/common.json create mode 100644 examples/with-next-i18next/public/locales/id/zod.json create mode 100644 packages/core/locales/id/zod.json create mode 100644 packages/core/tests/integrations/id.test.ts diff --git a/examples/with-next-i18next/next-i18next.config.js b/examples/with-next-i18next/next-i18next.config.js index 5864b8d..b6d5fda 100644 --- a/examples/with-next-i18next/next-i18next.config.js +++ b/examples/with-next-i18next/next-i18next.config.js @@ -32,6 +32,7 @@ module.exports = { "hr-HR", "fi", "cs", + "id", ], }, localePath: path.resolve("./public/locales"), diff --git a/examples/with-next-i18next/pages/index.tsx b/examples/with-next-i18next/pages/index.tsx index 68c07e9..28e5ba0 100644 --- a/examples/with-next-i18next/pages/index.tsx +++ b/examples/with-next-i18next/pages/index.tsx @@ -76,6 +76,7 @@ export default function HookForm() { borderLeftRadius={0} > + diff --git a/examples/with-next-i18next/public/locales/id/common.json b/examples/with-next-i18next/public/locales/id/common.json new file mode 100644 index 0000000..3c27779 --- /dev/null +++ b/examples/with-next-i18next/public/locales/id/common.json @@ -0,0 +1,7 @@ +{ + "username": "Nama user", + "username_placeholder": "John Doe", + "email": "Email", + "favoriteNumber": "Angka favorit", + "submit": "Kirim" +} diff --git a/examples/with-next-i18next/public/locales/id/zod.json b/examples/with-next-i18next/public/locales/id/zod.json new file mode 100644 index 0000000..1a5f5b6 --- /dev/null +++ b/examples/with-next-i18next/public/locales/id/zod.json @@ -0,0 +1,112 @@ +{ + "errors": { + "invalid_type": "Nilai seharusnya memiliki tipe data {{expected}}, diterima {{received}}", + "invalid_type_received_undefined": "Wajib diisi", + "invalid_literal": "Nilai literal tidak valid, seharusnya {{expected}}", + "unrecognized_keys": "Key {{- keys}} tidak valid pada object", + "invalid_union": "Input tidak valid", + "invalid_union_discriminator": "Nilai discriminator tidak ditemukan dalam {{- options}}", + "invalid_enum_value": "Nilai enum '{{received}}' tidak ditemukan dalam {{- options}}", + "invalid_arguments": "Argumen function tidak valid", + "invalid_return_type": "Tipe data return function tidak valid", + "invalid_date": "Tanggal tidak valid", + "custom": "Input tidak valid", + "invalid_intersection_types": "Hasil intersection tidak dapat disatukan", + "not_multiple_of": "Angka harus merupakan kelipatan dari {{multipleOf}}", + "not_finite": "Angka harus berhingga", + "invalid_string": { + "email": "Format {{validation}} tidak valid", + "url": "Format {{validation}} tidak valid", + "uuid": "Format {{validation}} tidak valid", + "cuid": "Format {{validation}} tidak valid", + "regex": "Tidak valid", + "datetime": "Format {{validation}} tidak valid", + "startsWith": "String harus dimulai dengan \"{{startsWith}}\"", + "endsWith": "String harus diakhiri dengan \"{{endsWith}}\"" + }, + "too_small": { + "array": { + "exact": "Array harus berisi tepat {{minimum}} elemen", + "inclusive": "Array harus berisi minimal {{minimum}} elemen", + "not_inclusive": "Array harus berisi lebih dari {{minimum}} elemen" + }, + "string": { + "exact": "String harus {{minimum}} karakter", + "inclusive": "String harus minimal {{minimum}} karakter", + "not_inclusive": "String harus lebih dari {{minimum}} karakter" + }, + "number": { + "exact": "Angka harus bernilai {{minimum}}", + "inclusive": "Angka harus lebih atau sama dengan dari {{minimum}}", + "not_inclusive": "Angka harus lebih dari {{minimum}}" + }, + "set": { + "exact": "Input tidak valid", + "inclusive": "Input tidak valid", + "not_inclusive": "Input tidak valid" + }, + "date": { + "exact": "Tanggal harus bernilai {{- minimum, datetime}}", + "inclusive": "Tanggal harus lebih atau sama dengan dari {{- minimum, datetime}}", + "not_inclusive": "Tanggal harus lebih dari {{- minimum, datetime}}" + } + }, + "too_big": { + "array": { + "exact": "Array harus berisi tepat {{maximum}} elemen", + "inclusive": "Array harus berisi maksimal {{maximum}} elemen", + "not_inclusive": "Array harus berisi kurang dari {{maximum}} elemen" + }, + "string": { + "exact": "String harus {{maximum}} karakter", + "inclusive": "String harus maksimal {{maximum}} karakter", + "not_inclusive": "String harus kurang dari {{maximum}} karakter" + }, + "number": { + "exact": "Angka harus bernilai {{maximum}}", + "inclusive": "Angka harus kurang atau sama dengan dari {{maximum}}", + "not_inclusive": "Angka harus kurang dari {{maximum}}" + }, + "set": { + "exact": "Input tidak valid", + "inclusive": "Input tidak valid", + "not_inclusive": "Input tidak valid" + }, + "date": { + "exact": "Tanggal harus bernilai {{- maximum, datetime}}", + "inclusive": "Tanggal harus kurang atau sama dengan dari {{- maximum, datetime}}", + "not_inclusive": "Tanggal harus kurang dari {{- maximum, datetime}}" + } + } + }, + "validations": { + "email": "email", + "url": "url", + "uuid": "uuid", + "cuid": "cuid", + "regex": "regex", + "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/id/zod.json b/packages/core/locales/id/zod.json new file mode 100644 index 0000000..1a5f5b6 --- /dev/null +++ b/packages/core/locales/id/zod.json @@ -0,0 +1,112 @@ +{ + "errors": { + "invalid_type": "Nilai seharusnya memiliki tipe data {{expected}}, diterima {{received}}", + "invalid_type_received_undefined": "Wajib diisi", + "invalid_literal": "Nilai literal tidak valid, seharusnya {{expected}}", + "unrecognized_keys": "Key {{- keys}} tidak valid pada object", + "invalid_union": "Input tidak valid", + "invalid_union_discriminator": "Nilai discriminator tidak ditemukan dalam {{- options}}", + "invalid_enum_value": "Nilai enum '{{received}}' tidak ditemukan dalam {{- options}}", + "invalid_arguments": "Argumen function tidak valid", + "invalid_return_type": "Tipe data return function tidak valid", + "invalid_date": "Tanggal tidak valid", + "custom": "Input tidak valid", + "invalid_intersection_types": "Hasil intersection tidak dapat disatukan", + "not_multiple_of": "Angka harus merupakan kelipatan dari {{multipleOf}}", + "not_finite": "Angka harus berhingga", + "invalid_string": { + "email": "Format {{validation}} tidak valid", + "url": "Format {{validation}} tidak valid", + "uuid": "Format {{validation}} tidak valid", + "cuid": "Format {{validation}} tidak valid", + "regex": "Tidak valid", + "datetime": "Format {{validation}} tidak valid", + "startsWith": "String harus dimulai dengan \"{{startsWith}}\"", + "endsWith": "String harus diakhiri dengan \"{{endsWith}}\"" + }, + "too_small": { + "array": { + "exact": "Array harus berisi tepat {{minimum}} elemen", + "inclusive": "Array harus berisi minimal {{minimum}} elemen", + "not_inclusive": "Array harus berisi lebih dari {{minimum}} elemen" + }, + "string": { + "exact": "String harus {{minimum}} karakter", + "inclusive": "String harus minimal {{minimum}} karakter", + "not_inclusive": "String harus lebih dari {{minimum}} karakter" + }, + "number": { + "exact": "Angka harus bernilai {{minimum}}", + "inclusive": "Angka harus lebih atau sama dengan dari {{minimum}}", + "not_inclusive": "Angka harus lebih dari {{minimum}}" + }, + "set": { + "exact": "Input tidak valid", + "inclusive": "Input tidak valid", + "not_inclusive": "Input tidak valid" + }, + "date": { + "exact": "Tanggal harus bernilai {{- minimum, datetime}}", + "inclusive": "Tanggal harus lebih atau sama dengan dari {{- minimum, datetime}}", + "not_inclusive": "Tanggal harus lebih dari {{- minimum, datetime}}" + } + }, + "too_big": { + "array": { + "exact": "Array harus berisi tepat {{maximum}} elemen", + "inclusive": "Array harus berisi maksimal {{maximum}} elemen", + "not_inclusive": "Array harus berisi kurang dari {{maximum}} elemen" + }, + "string": { + "exact": "String harus {{maximum}} karakter", + "inclusive": "String harus maksimal {{maximum}} karakter", + "not_inclusive": "String harus kurang dari {{maximum}} karakter" + }, + "number": { + "exact": "Angka harus bernilai {{maximum}}", + "inclusive": "Angka harus kurang atau sama dengan dari {{maximum}}", + "not_inclusive": "Angka harus kurang dari {{maximum}}" + }, + "set": { + "exact": "Input tidak valid", + "inclusive": "Input tidak valid", + "not_inclusive": "Input tidak valid" + }, + "date": { + "exact": "Tanggal harus bernilai {{- maximum, datetime}}", + "inclusive": "Tanggal harus kurang atau sama dengan dari {{- maximum, datetime}}", + "not_inclusive": "Tanggal harus kurang dari {{- maximum, datetime}}" + } + } + }, + "validations": { + "email": "email", + "url": "url", + "uuid": "uuid", + "cuid": "cuid", + "regex": "regex", + "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/id.test.ts b/packages/core/tests/integrations/id.test.ts new file mode 100644 index 0000000..ef607a7 --- /dev/null +++ b/packages/core/tests/integrations/id.test.ts @@ -0,0 +1,217 @@ +import { test, expect, beforeAll } from "vitest"; +import { z } from "zod"; +import { init, getErrorMessage, getErrorMessageFromZodError } from "./helpers"; + +const LOCALE = "id"; + +beforeAll(async () => { + await init(LOCALE); +}); + +test("string parser error messages", () => { + const schema = z.string(); + + expect(getErrorMessage(schema.safeParse(undefined))).toEqual("Wajib diisi"); + expect(getErrorMessage(schema.safeParse(1))).toEqual( + "Nilai seharusnya memiliki tipe data string, diterima number" + ); + expect(getErrorMessage(schema.safeParse(true))).toEqual( + "Nilai seharusnya memiliki tipe data string, diterima boolean" + ); + expect(getErrorMessage(schema.safeParse(Date))).toEqual( + "Nilai seharusnya memiliki tipe data string, diterima function" + ); + expect(getErrorMessage(schema.safeParse(new Date()))).toEqual( + "Nilai seharusnya memiliki tipe data string, diterima date" + ); + expect(getErrorMessage(schema.email().safeParse(""))).toEqual( + "Format email tidak valid" + ); + expect(getErrorMessage(schema.url().safeParse(""))).toEqual( + "Format url tidak valid" + ); + expect(getErrorMessage(schema.regex(/aaa/).safeParse(""))).toEqual( + "Tidak valid" + ); + expect(getErrorMessage(schema.startsWith("foo").safeParse(""))).toEqual( + 'String harus dimulai dengan "foo"' + ); + expect(getErrorMessage(schema.endsWith("bar").safeParse(""))).toEqual( + 'String harus diakhiri dengan "bar"' + ); + expect(getErrorMessage(schema.min(5).safeParse("a"))).toEqual( + "String harus minimal 5 karakter" + ); + expect(getErrorMessage(schema.max(5).safeParse("abcdef"))).toEqual( + "String harus maksimal 5 karakter" + ); + expect(getErrorMessage(schema.length(5).safeParse("abcdef"))).toEqual( + "String harus 5 karakter" + ); + expect( + getErrorMessage(schema.datetime().safeParse("2020-01-01T00:00:00+02:00")) + ).toEqual("Format datetime tidak valid"); +}); + +test("number parser error messages", () => { + const schema = z.number(); + + expect(getErrorMessage(schema.safeParse(undefined))).toEqual("Wajib diisi"); + expect(getErrorMessage(schema.safeParse(""))).toEqual( + "Nilai seharusnya memiliki tipe data number, diterima string" + ); + expect(getErrorMessage(schema.safeParse(null))).toEqual( + "Nilai seharusnya memiliki tipe data number, diterima null" + ); + expect(getErrorMessage(schema.safeParse(NaN))).toEqual( + "Nilai seharusnya memiliki tipe data number, diterima nan" + ); + expect(getErrorMessage(schema.int().safeParse(0.1))).toEqual( + "Nilai seharusnya memiliki tipe data integer, diterima float" + ); + expect(getErrorMessage(schema.multipleOf(5).safeParse(2))).toEqual( + "Angka harus merupakan kelipatan dari 5" + ); + expect(getErrorMessage(schema.step(0.1).safeParse(0.0001))).toEqual( + "Angka harus merupakan kelipatan dari 0.1" + ); + expect(getErrorMessage(schema.lt(5).safeParse(10))).toEqual( + "Angka harus kurang dari 5" + ); + expect(getErrorMessage(schema.lte(5).safeParse(10))).toEqual( + "Angka harus kurang atau sama dengan dari 5" + ); + expect(getErrorMessage(schema.gt(5).safeParse(1))).toEqual( + "Angka harus lebih dari 5" + ); + expect(getErrorMessage(schema.gte(5).safeParse(1))).toEqual( + "Angka harus lebih atau sama dengan dari 5" + ); + expect(getErrorMessage(schema.nonnegative().safeParse(-1))).toEqual( + "Angka harus lebih atau sama dengan dari 0" + ); + expect(getErrorMessage(schema.nonpositive().safeParse(1))).toEqual( + "Angka harus kurang atau sama dengan dari 0" + ); + expect(getErrorMessage(schema.negative().safeParse(1))).toEqual( + "Angka harus kurang dari 0" + ); + expect(getErrorMessage(schema.positive().safeParse(0))).toEqual( + "Angka harus lebih dari 0" + ); + expect(getErrorMessage(schema.finite().safeParse(Infinity))).toEqual( + "Angka harus berhingga" + ); +}); + +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( + "Nilai seharusnya memiliki tipe data date, diterima string" + ); + expect( + getErrorMessage(schema.min(testDate).safeParse(new Date("2022-07-29"))) + ).toEqual( + `Tanggal harus lebih atau sama dengan dari ${testDate.toLocaleDateString( + LOCALE + )}` + ); + expect( + getErrorMessage(schema.max(testDate).safeParse(new Date("2022-08-02"))) + ).toEqual( + `Tanggal harus kurang atau sama dengan dari ${testDate.toLocaleDateString( + LOCALE + )}` + ); + try { + await schema.parseAsync(new Date("invalid")); + } catch (err) { + expect((err as z.ZodError).issues[0].message).toEqual( + "Tanggal tidak valid" + ); + } +}); + +test("array parser error messages", () => { + const schema = z.string().array(); + + expect(getErrorMessage(schema.safeParse(""))).toEqual( + "Nilai seharusnya memiliki tipe data array, diterima string" + ); + expect(getErrorMessage(schema.min(5).safeParse([""]))).toEqual( + "Array harus berisi minimal 5 elemen" + ); + expect(getErrorMessage(schema.max(2).safeParse(["", "", ""]))).toEqual( + "Array harus berisi maksimal 2 elemen" + ); + expect(getErrorMessage(schema.nonempty().safeParse([]))).toEqual( + "Array harus berisi minimal 1 elemen" + ); + expect(getErrorMessage(schema.length(2).safeParse([]))).toEqual( + "Array harus berisi tepat 2 elemen" + ); +}); + +test("function parser error messages", () => { + const functionParse = z + .function(z.tuple([z.string()]), z.number()) + .parse((a: any) => a); + expect(getErrorMessageFromZodError(() => functionParse(""))).toEqual( + "Tipe data return function tidak valid" + ); + expect(getErrorMessageFromZodError(() => functionParse(1 as any))).toEqual( + "Argumen function tidak valid" + ); +}); + +test("other parser error messages", () => { + expect( + getErrorMessage( + z + .intersection( + z.number(), + z.number().transform((x) => x + 1) + ) + .safeParse(1234) + ) + ).toEqual("Hasil intersection tidak dapat disatukan"); + expect(getErrorMessage(z.literal(12).safeParse(""))).toEqual( + "Nilai literal tidak valid, seharusnya 12" + ); + expect(getErrorMessage(z.enum(["A", "B", "C"]).safeParse("D"))).toEqual( + "Nilai enum 'D' tidak ditemukan dalam 'A' | 'B' | 'C'" + ); + expect( + getErrorMessage( + z + .object({ dog: z.string() }) + .strict() + .safeParse({ dog: "", cat: "", rat: "" }) + ) + ).toEqual("Key 'cat', 'rat' tidak valid pada object"); + 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("Nilai discriminator tidak ditemukan dalam 'a' | 'b'"); + expect( + getErrorMessage(z.union([z.string(), z.number()]).safeParse([true])) + ).toEqual("Input tidak valid"); + expect( + getErrorMessage( + z + .string() + .refine(() => { + return false; + }) + .safeParse("") + ) + ).toEqual("Input tidak valid"); +});