diff --git a/docs/ja/README.md b/docs/ja/README.md
new file mode 100644
index 00000000..a4c043e0
--- /dev/null
+++ b/docs/ja/README.md
@@ -0,0 +1,38 @@
+# Documentation
+
+- はじめに
+ - [概要](./overview.md)
+ - [チュートリアル](./tutorial.md)
+ - [v1 へのアップグレード](./upgrading-v1.md)
+- ガイド
+ - [バリデーション](./validation.md)
+ - [ネストされたオブジェクトと配列](./complex-structures.md)
+ - [インテントボタン](./intent-button.md)
+ - [チェックボックスとラジオグループ](./checkbox-and-radio-group.md)
+ - [ファイルのアップロード](./file-upload.md)
+ - [アクセシビリティ](./accessibility.md)
+- インテグレーション
+ - [UI ライブラリ](./integration/ui-libraries.md)
+ - [Remix](./integration/remix.md)
+ - [Next.js](./integration/nextjs.md)
+- API リファレンス
+ - @conform-to/react
+ - [useForm](./api/react/useForm.md)
+ - [useField](./api/react/useField.md)
+ - [useFormMetadata](./api/react/useFormMetadata.md)
+ - [useInputControl](./api/react/useInputControl.md)
+ - [FormProvider](./api/react/FormProvider.md)
+ - [FormStateInput](./api/react/FormStateInput.md)
+ - [getFormProps](./api/react/getFormProps.md)
+ - [getFieldsetProps](./api/react/getFieldsetProps.md)
+ - [getInputProps](./api/react/getInputProps.md)
+ - [getSelectProps](./api/react/getSelectProps.md)
+ - [getTextareaProps](./api/react/getTextareaProps.md)
+ - [getCollectionProps](./api/react/getCollectionProps.md)
+ - @conform-to/yup
+ - [parseWithYup](./api/yup/parseWithYup.md)
+ - [getYupConstraint](./api/yup/getYupConstraint.md)
+ - @conform-to/zod
+ - [parseWithZod](./api/zod/parseWithZod.md)
+ - [getZodConstraint](./api/zod/getZodConstraint.md)
+ - [conformZodMessage](./api/zod/conformZodMessage.md)
diff --git a/docs/ja/accessibility.md b/docs/ja/accessibility.md
new file mode 100644
index 00000000..ebc0dbb7
--- /dev/null
+++ b/docs/ja/accessibility.md
@@ -0,0 +1,183 @@
+# アクセシビリティ
+
+フォームをアクセシブルにするには、各フォーム要素を適切な属性で設定する必要がありますが、 Conform がその手助けをします。
+
+## Aria 属性
+
+アクセシビリティに関しては、通常、異なる要素を関連付けるために一意の ID が必要になる Aria 属性が最初に思い浮かびます。Conform は、必要なすべての ID を生成してくれることで、この点でのサポートを提供します。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return (
+
+ );
+}
+```
+
+## バリデーション属性
+
+バリデーション属性も、スクリーンリーダーのヒントを改善するなど、アクセシビリティにおいて重要な役割を果たします。 Conform を使用すると、 zod や yup スキーマからバリデーション属性を導出し、各フィールドのメタデータにそれらを反映させることができます。
+
+```tsx
+import { parseWithZod, getZodConstraint } from '@conform-to/zod';
+import { useForm } from '@conform-to/react';
+import { z } from 'zod';
+
+const schema = z.object({
+ message: z
+ .string()
+ .min(10)
+ .max(100)
+ .regex(/^[A-Za-z0-9 ]{10-100}$/),
+});
+
+function Example() {
+ const [form, fields] = useForm({
+ constraint: getZodConstraint(schema),
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+ });
+
+ return (
+
+ );
+}
+```
+
+## プログレッシブエンハンスメント
+
+プログレッシブエンハンスメントも、一時的なネットワークの問題の影響を最小限に抑えるなど、アクセシビリティを支援します。たとえば、 Conform を使用すると、ページのリフレッシュをまたいでもフォームデータと状態が保持されるように、フィールドリストを操作できます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+export default function Example() {
+ const [form, fields] = useForm();
+
+ return (
+
+ );
+}
+```
+
+## ボイラープレートの削減
+
+上記で述べたすべての属性を設定することは、面倒でエラーが発生しやすい作業です。 Conform は 、関連するすべての属性を導出する一連のヘルパーを提供することで、この作業を支援しようとしています。
+
+> 注意: これらすべてのヘルパーはネイティブ HTML 要素用に設計されています。 react-aria-components や Radix UI のようなカスタム UI コンポーネントを使用している場合、それらの API を通じて既に属性が設定されている可能性があるため、これらのヘルパーが不要になるかもしれません。
+
+- [getFormProps](./api/react/getFormProps.md)
+- [getFieldsetProps](./api/react/getFieldsetProps.md)
+- [getInputProps](./api/react/getInputProps.md)
+- [getSelectProps](./api/react/getSelectProps.md)
+- [getTextareaProps](./api/react/getTextareaProps.md)
+- [getCollectionProps](./api/react/getButtonProps.md)
+
+以下は、手動設定と比較した場合の例です。ヘルパーについて詳しく知りたい場合は、上記リンクの対応するドキュメントを確認してください。
+
+```tsx
+import { parseWithZod, getZodConstraint } from '@conform-to/zod';
+import { useForm } from '@conform-to/react';
+import { z } from 'zod';
+
+const schema = z.object({
+ message: z
+ .string()
+ .min(10)
+ .max(100)
+ .regex(/^[A-Za-z0-9 ]{10-100}$/),
+});
+
+function Example() {
+ const [form, fields] = useForm({
+ constraint: getZodConstraint(schema),
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+ });
+
+ return (
+
+ );
+}
+```
diff --git a/docs/ja/api/react/FormProvider.md b/docs/ja/api/react/FormProvider.md
new file mode 100644
index 00000000..1dad29c2
--- /dev/null
+++ b/docs/ja/api/react/FormProvider.md
@@ -0,0 +1,82 @@
+# FormProvider
+
+フォームコンテキストのための [Context Provider](https://react.dev/reference/react/createContext#provider) をレンダリングする React コンポーネントです。 [useField](./useField.md) や [useFormMetadata](./useFormMetadata.md) フックを使用したい場合には必須です。
+
+```tsx
+import { FormProvider, useForm } from '@conform-to/react';
+
+export default function SomeParent() {
+ const [form, fields] = useForm();
+
+ return {/* ... */};
+}
+```
+
+## プロパティ
+
+### `context`
+
+フォームコンテキストです。これは [useForm](./useForm.md) で作成され、 `form.context` を通じてアクセスできます。
+
+## Tips
+
+### FormProvider は、フォームの直接の親である必要はありません。
+
+入力が [form 属性](https://developer.mozilla.org/en-US/docs/Web/API/HTMLInputElement#instance_properties_related_to_the_parent_form) を通じて関連付けられている限り、フォームの外部のどこにでも自由に入力を配置できます。
+
+```tsx
+function Example() {
+ const [form, fields] = useForm();
+ return (
+
+
+
+
+
+
+
+
+ );
+}
+```
+
+### FormProvider はネストすることができます
+
+これは、レイアウトの制約のために 1 つのフォームを別のフォームの内部に配置する必要がある場合に便利です。
+
+```tsx
+import { FormProvider, useForm } from '@conform-to/react';
+
+function Field({ name, formId }) {
+ // formId が指定されていない場合、 useField は最も近い FormContext を探します。
+ const [meta] = useField(name, { formId });
+
+ return ;
+}
+
+function Parent() {
+ const [form, fields] = useForm({ id: 'parent' });
+ return (
+
+
+
+
+
+
+ );
+}
+
+function Child() {
+ const [form, fields] = useForm({ id: 'child' });
+
+ return (
+
+
+
+
+ {/* これは代わりに 'id' が 'parent' のフォームコンテキストを探します。 */}
+
+
+ );
+}
+```
diff --git a/docs/ja/api/react/FormStateInput.md b/docs/ja/api/react/FormStateInput.md
new file mode 100644
index 00000000..c9253cff
--- /dev/null
+++ b/docs/ja/api/react/FormStateInput.md
@@ -0,0 +1,27 @@
+# FormStateInput
+
+ドキュメントの再読み込みが発生した場合にフォームの状態を維持するために、非表示の入力をレンダリングする React コンポーネントです。
+
+```tsx
+import { FormProvider, FormStateInput, useForm } from '@conform-to/react';
+
+export default function SomeParent() {
+ const [form, fields] = useForm();
+
+ return (
+
+
+
+ );
+}
+```
+
+## プロパティ
+
+このコンポーネントはプロパティを受け入れません。
+
+## Tips
+
+### 完全なプログレッシブエンハンスメントを求めている場合にのみ、これが必要です。
+
+ドキュメントが再読み込みされると、フォームの状態の一部が失われます。例えば、 Conform は検証されたフィールドのエラーのみを表示しますが、新しいフィールドをリストに挿入するなど、サブミット以外の意図でフォームを送信している場合、この情報は失われます。 FormStateInput をレンダリングすることで、 Conform はフォームの状態を復元し、検証されたすべてのフィールドのエラーが引き続き表示されることを保証できます。
diff --git a/docs/ja/api/react/getCollectionProps.md b/docs/ja/api/react/getCollectionProps.md
new file mode 100644
index 00000000..959f811b
--- /dev/null
+++ b/docs/ja/api/react/getCollectionProps.md
@@ -0,0 +1,113 @@
+# getCollectionProps
+
+チェックボックスまたはラジオボタンのグループをアクセシブルにするために必要なすべてのプロパティを返すヘルパーです。
+
+```tsx
+const collectionProps = getCollectionProps(meta, options);
+```
+
+## 例
+
+```tsx
+import { useForm, getCollectionProps } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return (
+ <>
+ {getCollectionProps(fields.color, {
+ type: 'radio',
+ options: ['red', 'green', 'blue'],
+ }).map((props) => (
+
+ ))}
+ >
+ );
+}
+```
+
+## オプション
+
+### `type`
+
+コレクションのタイプです。 **checkbox** (チェックボックス)または **radio** (ラジオボタン)のいずれかになります。
+
+### `options`
+
+コレクションのオプションです。各オプションは入力の値として扱われ、対応する **key** または **id** を導出するために使用されます。
+
+### `value`
+
+このヘルパーは、例えばコントロールされた入力のように、これが `false` に設定されていない限り、 **defaultValue** を返します。
+
+### `ariaAttributes`
+
+結果のプロパティに `aria-invalid` と `aria-describedby` を含めるかどうかを決定します。デフォルトは **true** です。
+
+### `ariaInvalid`
+
+ARIA 属性が `meta.errors` または `meta.allErrors` に基づくべきかどうかを決定します。デフォルトは **errors** です。
+
+### `ariaDescribedBy`
+
+`aria-describedby` 属性に追加の **id** を付加します。フィールドメタデータから `meta.descriptionId` を渡すことができます。
+
+## Tips
+
+### ヘルパーは任意です
+
+このヘルパーは、定型文を減らし、読みやすくするための便利な機能です。チェックボックス要素のプロパティを設定するために、いつでもフィールドメタデータを直接使用することができます。
+
+```tsx
+// Before
+function Example() {
+ return (
+
+ );
+}
+
+// After
+function Example() {
+ return (
+
+ );
+}
+```
+
+### 自分のヘルパーを作る
+
+このヘルパーは、ネイティブのチェックボックス要素用に設計されています。カスタムコンポーネントを使用する必要がある場合は、自分自身のヘルパーを作成することができます。
diff --git a/docs/ja/api/react/getFieldsetProps.md b/docs/ja/api/react/getFieldsetProps.md
new file mode 100644
index 00000000..0385ec93
--- /dev/null
+++ b/docs/ja/api/react/getFieldsetProps.md
@@ -0,0 +1,63 @@
+# getFieldsetProps
+
+フィールドセット要素をアクセシブルにするために必要なすべてのプロパティを返すヘルパーです。
+
+```tsx
+const props = getFieldsetProps(meta, options);
+```
+
+## 例
+
+```tsx
+import { useForm, getFieldsetProps } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return ;
+}
+```
+
+## オプション
+
+### `ariaAttributes`
+
+結果のプロパティに `aria-invalid` と `aria-describedby` を含めるかどうかを決定します。デフォルトは **true** です。
+
+### `ariaInvalid`
+
+ARIA 属性が `meta.errors` または `meta.allErrors` に基づくべきかどうかを決定します。デフォルトは **errors** です。
+
+### `ariaDescribedBy`
+
+`aria-describedby` 属性に追加の **id** を付加します。フィールドメタデータから `meta.descriptionId` を渡すことができます。
+
+## Tips
+
+### ヘルパーは任意です
+
+このヘルパーは、定型文を減らし、読みやすくするための便利な機能です。入力要素のプロパティを設定するために、常にフィールドのメタデータを直接使用することもできます。
+
+```tsx
+// Before
+function Example() {
+ return (
+
+ );
+}
+
+// After
+function Example() {
+ return ;
+}
+```
+
+### 自分のヘルパーを作る
+
+このヘルパーは、ネイティブの入力要素用に設計されています。カスタムコンポーネントを使用する必要がある場合は、自分自身のヘルパーを作成することができます。
diff --git a/docs/ja/api/react/getFormProps.md b/docs/ja/api/react/getFormProps.md
new file mode 100644
index 00000000..70f5349f
--- /dev/null
+++ b/docs/ja/api/react/getFormProps.md
@@ -0,0 +1,63 @@
+# getFormProps
+
+フォーム要素をアクセシブルにするために必要なすべてのプロパティを返すヘルパーです。
+
+```tsx
+const props = getFormProps(form, options);
+```
+
+## 例
+
+```tsx
+import { useForm, getFormProps } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return ;
+}
+```
+
+## オプション
+
+### `ariaAttributes`
+
+結果のプロパティに `aria-invalid` と `aria-describedby` を含めるかどうかを決定します。デフォルトは **true** です。
+
+### `ariaInvalid`
+
+aria 属性が `meta.errors` または `meta.allErrors` に基づくべきかを決定します。デフォルトは **errors** です。
+
+### `ariaDescribedBy`
+
+追加の **id** を `aria-describedby` 属性に追加します。フィールドのメタデータから `meta.descriptionId` を渡すことができます。
+
+## Tips
+
+### ヘルパーは任意です
+
+ヘルパーは、定型文を減らし、読みやすくするための便利な関数にすぎません。フォーム要素のプロパティを設定するには、いつでもフォームのメタデータを直接使うことができます。
+
+```tsx
+// Before
+function Example() {
+ return (
+
+ );
+}
+
+// After
+function Example() {
+ return ;
+}
+```
+
+### 自分のヘルパーを作る
+
+ヘルパーはネイティブのフォーム要素のために設計されています。カスタムコンポーネントを使う必要がある場合、いつでも独自のヘルパーを作ることができます。
diff --git a/docs/ja/api/react/getInputProps.md b/docs/ja/api/react/getInputProps.md
new file mode 100644
index 00000000..d4321d5f
--- /dev/null
+++ b/docs/ja/api/react/getInputProps.md
@@ -0,0 +1,111 @@
+# getInputProps
+
+入力要素をアクセシブルにするために必要なすべてのプロパティを返すヘルパーです。
+
+```tsx
+const props = getInputProps(meta, options);
+```
+
+## 例
+
+```tsx
+import { useForm, getInputProps } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return ;
+}
+```
+
+## オプション
+
+### `type`
+
+入力のタイプです。これは、 **defaultValue** (デフォルト値)または **defaultChecked** (デフォルトでチェックされている状態)が必要かどうかを判断するために使用されます。
+
+### `value`
+
+これは主に、タイプが `checkbox` または `radio` の場合に入力の値を設定するために使用されます。しかし、 **defaultValue** や **defaultChecked** の状態の設定をスキップしたい場合、例えばコントロールされた入力の場合には、 `false` に設定することもできます。
+
+### `ariaAttributes`
+
+結果のプロパティに `aria-invalid` と `aria-describedby` を含めるかどうかを決定します。デフォルトは **true** です。
+
+### `ariaInvalid`
+
+ARIA 属性を `meta.errors` または `meta.allErrors` に基づいて設定するかどうかを決定します。デフォルトは **errors** です。
+
+### `ariaDescribedBy`
+
+`aria-describedby` 属性に追加の **id** を追加します。フィールドのメタデータから `meta.descriptionId` を渡すことができます。
+
+## Tips
+
+### ヘルパーは任意です
+
+このヘルパーは、定型文を減らし、読みやすくするための便利な機能です。入力要素のプロパティを設定するために、常にフィールドのメタデータを直接使用することもできます。
+
+```tsx
+// Before
+function Example() {
+ return (
+
+ );
+}
+
+// After
+function Example() {
+ return (
+
+ );
+}
+```
+
+### 自分のヘルパーを作る
+
+このヘルパーは、ネイティブの入力要素用に設計されています。カスタムコンポーネントを使用する必要がある場合は、自分自身のヘルパーを作成することができます。
diff --git a/docs/ja/api/react/getSelectProps.md b/docs/ja/api/react/getSelectProps.md
new file mode 100644
index 00000000..c0468f87
--- /dev/null
+++ b/docs/ja/api/react/getSelectProps.md
@@ -0,0 +1,79 @@
+# getSelectProps
+
+セレクト要素をアクセシブルにするために必要なすべてのプロパティを返すヘルパーです。
+
+```tsx
+const props = getSelectProps(meta, options);
+```
+
+## 例
+
+```tsx
+import { useForm, getSelectProps } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return ;
+}
+```
+
+## オプション
+
+### `value`
+
+このヘルパーは、例えばコントロールされた入力のように、これが `false` に設定されていない限り、 **defaultValue** を返します。
+
+### `ariaAttributes`
+
+結果のプロパティに `aria-invalid` と `aria-describedby` を含めるかどうかを決定します。デフォルトは **true** です。
+
+### `ariaInvalid`
+
+結果のプロパティに `aria-invalid` と `aria-describedby` を含めるかどうかを決定します。デフォルトは **true** です。
+
+### `ariaDescribedBy`
+
+`aria-describedby` 属性に追加の **id** を付加します。フィールドメタデータから `meta.descriptionId` を渡すことができます。
+
+## Tips
+
+### ヘルパーは任意です。
+
+このヘルパーは、定型文を減らし、読みやすくするための便利な機能です。セレクト要素のプロパティを設定するために、いつでもフィールドメタデータを直接使用することができます。
+
+```tsx
+// Before
+function Example() {
+ return (
+
+ );
+}
+
+// After
+function Example() {
+ return (
+
+ );
+}
+```
+
+### 自分のヘルパーを作る
+
+このヘルパーは、ネイティブのセレクト要素用に設計されています。カスタムコンポーネントを使用する必要がある場合は、自分自身のヘルパーを作成することができます。
diff --git a/docs/ja/api/react/getTextareaProps.md b/docs/ja/api/react/getTextareaProps.md
new file mode 100644
index 00000000..0ac4894a
--- /dev/null
+++ b/docs/ja/api/react/getTextareaProps.md
@@ -0,0 +1,80 @@
+# getTextareaProps
+
+テキストエリア要素をアクセシブルにするために必要なすべてのプロパティを返すヘルパーです。
+
+```tsx
+const props = getTextareaProps(meta, options);
+```
+
+## 例
+
+```tsx
+import { useForm, getTextareaProps } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return ;
+}
+```
+
+## オプション
+
+### `value`
+
+このヘルパーは、例えばコントロールされた入力など、これが `false` に設定されていない限り、 **defaultValue** を返します。
+
+### `ariaAttributes`
+
+結果のプロパティに `aria-invalid` と `aria-describedby` を含めるかどうかを決定します。デフォルトは **true** です。
+
+### `ariaInvalid`
+
+ARIA 属性を `meta.errors` または `meta.allErrors` に基づいて設定するかどうかを決定します。デフォルトは **errors** です。
+
+### `ariaDescribedBy`
+
+`aria-describedby` 属性に追加の **id** を付加します。フィールドメタデータから `meta.descriptionId` を渡すことができます。
+
+## Tips
+
+### ヘルパーは任意です。
+
+このヘルパーは、定型文を減らし、読みやすくするための便利な機能です。テキストエリア要素のプロパティを設定するために、いつでもフィールドメタデータを直接使用することができます。
+
+```tsx
+// Before
+function Example() {
+ return (
+
+ );
+}
+
+// After
+function Example() {
+ return (
+
+ );
+}
+```
+
+### 自分のヘルパーを作る
+
+ヘルパーはネイティブのフォーム要素のために設計されています。カスタムコンポーネントを使う必要がある場合、いつでも独自のヘルパーを作ることができます。
diff --git a/docs/ja/api/react/useField.md b/docs/ja/api/react/useField.md
new file mode 100644
index 00000000..b45c0823
--- /dev/null
+++ b/docs/ja/api/react/useField.md
@@ -0,0 +1,75 @@
+# useField
+
+[FormProvider](./FormProvider.md) に設定されたコンテキストを購読することで、フィールドメタデータを返す React フックです。これは **最も近い** [FormProvider](./FormProvider.md) に基づいています。
+
+```tsx
+const [meta, form] = useField(name, options);
+```
+
+## パラメータ
+
+### `name`
+
+フィールドの名前。
+
+### `options`
+
+現時点での **オプション** は 1 つだけです。入れ子になったフォームコンテキストがあり、最も近い [FormProvider](./FormProvider.md) からではないフィールドにアクセスしたい場合は、 `formId` を渡して、正しいフィールドメタデータが返されるようにすることができます。
+
+## 戻り値
+
+### `meta`
+
+フィールドメタデータです。これは、 [useForm](./useForm.md) フックを使用した場合の `fields.fieldName` に相当します。
+
+### `form`
+
+フォームメタデータです。これは、 [useForm](./useForm.md) または [useFormMetadata](./useFormMetadata.md) フックによって返されるオブジェクトと同じものです。
+
+## Tips
+
+### `FieldName` 型を使用することで、より良い型安全性を実現します。
+
+フィールドやフォームのメタデータの型推論を改善するために、 `string` の代わりに `FieldName` 型を使用できます。
+
+```tsx
+import { type FormName, useFormMetadata } from '@conform-to/react';
+
+type ExampleComponentProps = {
+ name: FieldName;
+};
+
+function ExampleComponent({ name }: ExampleComponentProps) {
+ // これで、 'meta.value', 'meta.errors', 'form.errors' などの型が認識されます。
+ const [meta, form] = useField(name);
+
+ return {/* ... */}
;
+}
+```
+
+コンポーネントをレンダリングする際には、 Conform によって提供される名前(例: `fields.fieldName.name` )を使用します。これは既に `FieldName` として型付けされています。これにより、 TypeScript は型が互換性があるかをチェックし、互換性がない場合に警告を出すことができます。 `string` を渡すこともできますが、型チェックの機能は失われます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return ;
+}
+```
+
+しかし、 `FieldName` 型をより具体的にすればするほど、コンポーネントの再利用は難しくなります。もしあなたのコンポーネントがジェネリックの一部を使用しないのであれば、いつでも省略することができます。
+
+```ts
+type ExampleComponentProps = {
+ // 値やエラーなどの型を気にしない場合
+ name: string;
+ // フィールドの値にアクセスしている場合
+ name: FieldName;
+ // 深くネストされたフォームがあり、トップレベルの特定のフィールドにアクセスしたい場合
+ name: FieldName;
+ // カスタムエラータイプを持っている場合
+ name: FieldName;
+};
+```
diff --git a/docs/ja/api/react/useForm.md b/docs/ja/api/react/useForm.md
new file mode 100644
index 00000000..b73dcb60
--- /dev/null
+++ b/docs/ja/api/react/useForm.md
@@ -0,0 +1,78 @@
+# useForm
+
+HTML フォームを強化するためのフォームとフィールドのメタデータを返す React フックです。
+
+```tsx
+const [form, fields] = useForm(options);
+```
+
+## オプション
+
+以下のオプションはすべて任意です。
+
+### `id`
+
+フォーム要素に設定される id 属性です。提供されない場合は、代わりにランダムな id が生成されます。これは、各フィールドの id を生成するためにも使用されます。
+
+### `lastResult`
+
+最後の送信の結果です。これは通常、サーバーから送信され、プログレッシブエンハンスメントのためのフォームの初期状態として使用されます。
+
+### `defaultValue`
+
+フォームの初期値です。
+
+### `constraint`
+
+各フィールドに設定されるバリデーション属性です。
+
+### `shouldValidate`
+
+Conform が各フィールドのバリデーションを開始するタイミングを 3 つのオプションで定義します: **onSubmit**, **onBlur**,または **onInput** 。デフォルトは **onSubmit** です。
+
+### `shouldRevalidate`
+
+フィールドがバリデーションされた後、Conform が各フィールドをいつ再バリデーションするかを定義します。デフォルトは **shouldValidate** の値です。
+
+### `shouldDirtyConsider`
+
+Conform がフィールドをダーティ状態とみなすべきかどうかを定義します。例えば、 CSRF トークンのように Conform によって管理されていないフォームフィールドを除外する場合などです。
+
+### `onValidate`
+
+フォームを(再)バリデーションする必要があるときに呼び出される関数です。
+
+### `onSubmit`
+
+フォームが送信される前に呼び出される関数です。 **onValidate** が設定されている場合、クライアントバリデーションが成功した場合にのみ呼び出されます。
+
+### `defaultNoValidate`
+
+DOM がハイドレートされる前に制約バリデーションを有効にします。デフォルトは **true** です。
+
+## Tips
+
+### クライアントバリデーションは任意です。
+
+Conform はクライアントバリデーションなしでライブバリデーション(つまり、ユーザーが入力から離れたときやタイプしたときにバリデーションする)をサポートしています。これは、バリデーションコードをクライアントバンドルに含めないようにするために便利です。ただし、ネットワークの遅延や、ユーザーがサーバーにアクセスする頻度(特にタイプするたびに再バリデーションする場合)を考慮することが重要です。
+
+### `id` が変更されたときに自動的にフォームをリセットします。
+
+異なる `id` を `useForm` フックに渡してフォームをリセットすることができます。これは、同じフォームを持つ別のページにナビゲートするときに便利です(例: `/articles/foo` から `/articles/bar` へ)。
+
+```tsx
+interface Article {
+ id: string;
+ title: string;
+ content: string;
+}
+
+function EditArticleForm({ defaultValue }: { defaultValue: Article }) {
+ const [form, fields] = useForm({
+ id: `article-${defaultValue.id}`,
+ defaultValue,
+ });
+
+ // ...
+}
+```
diff --git a/docs/ja/api/react/useFormMetadata.md b/docs/ja/api/react/useFormMetadata.md
new file mode 100644
index 00000000..16481940
--- /dev/null
+++ b/docs/ja/api/react/useFormMetadata.md
@@ -0,0 +1,70 @@
+# useFormMetadata
+
+[FormProvider](./FormProvider.md) に設定されたコンテキストを購読することで、フォームのメタデータを返す React フックです。
+
+```tsx
+const form = useFormMetadata(formId);
+```
+
+## パラメータ
+
+### `formId`
+
+フォーム要素に設定される id 属性です。
+
+## 戻り値
+
+### `form`
+
+フォームメタデータです。これは、 [useForm](./useForm.md) フックによって返されるオブジェクトと同じです。
+
+## Tips
+
+### `FormId` 型を用いたより良い型推論
+
+フォームメタデータの型推論を改善するために、 `string` の代わりに `FormId` 型を使用できます。
+
+```tsx
+import { type FormId, useFormMetadata } from '@conform-to/react';
+
+type ExampleComponentProps = {
+ formId: FormId;
+};
+
+function ExampleComponent({ formId }: ExampleComponentProps) {
+ // これで `form.errors` と `form.getFieldset()` の結果の型を認識する。
+ const form = useFormMetadata(formId);
+
+ return {/* ... */}
;
+}
+```
+
+コンポーネントをレンダリングする際には、 Conform によって提供されたフォーム ID を使用します。例えば、 `form.id` や `fields.fieldName.formId` は、既に `FormId` として型付けされています。これにより、 TypeScript は型が互換性があるかをチェックし、互換性がない場合に警告を出すことができます。 `string` を渡すこともできますが、型チェックの能力は失われます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return (
+ <>
+
+
+ >
+ );
+}
+```
+
+しかし、 `FormId` 型をより具体的にするほど、コンポーネントの再利用が難しくなります。コンポーネントが `Schema` や `FormError` のジェネリクスを使用しない場合は、それを `string` としてシンプルに保つこともできます。
+
+```ts
+type ExampleComponentProps = {
+ // スキーマやフォームエラーの型を気にしない場合
+ formId: string;
+ // フォームメタデータから特定のフィールドにアクセスしている場合
+ formId: FormId<{ fieldName: string }>;
+ // カスタムエラータイプを持っている場合
+ formId: FormId, CustomFormError>;
+};
+```
diff --git a/docs/ja/api/react/useInputControl.md b/docs/ja/api/react/useInputControl.md
new file mode 100644
index 00000000..0af9608e
--- /dev/null
+++ b/docs/ja/api/react/useInputControl.md
@@ -0,0 +1,117 @@
+# useInputControl
+
+ブラウザイベントの発火を制御できる React フックです。Conform にカスタム input を組み込みたい場合に便利です。
+
+```tsx
+const control = useInputControl(metaOrOptions);
+```
+
+## 例
+
+```tsx
+import { useForm, useInputControl } from '@conform-to/react';
+import { Select, Option } from './custom-ui';
+
+function Example() {
+ const [form, fields] = useForm();
+ const color = useInputControl(fields.color);
+
+ return (
+
+ );
+}
+```
+
+## パラメータ
+
+### `metaOrOptions`
+
+フィールドメタデータ、または `key` 、 `name` 、 `formId` 、 `initialValue` を含むオプションオブジェクトです。
+
+## 戻り値
+
+input コントロールです。これにより、入力値と 3 種類のイベントディスパッチャーの両方にアクセスできます。
+
+### `value`
+
+input の値です。これを使用して、制御された input を設定することができます。
+
+### `change(value: string)`
+
+値を変更する必要があるときに呼び出されるメソッドです。これにより、新しい値を持つ入力の代わりに [change](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/change_event) イベントと [input](https://developer.mozilla.org/en-US/docs/Web/API/Element/input_event) イベントの両方がディスパッチされます。
+
+### `blur()`
+
+ユーザーが input から離れたときに呼び出されるメソッドです。これにより、入力の代わりに [blur](https://developer.mozilla.org/en-US/docs/Web/API/Element/blur_event) イベントと [focusout](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusout_event) イベントの両方がディスパッチされます。
+
+### `focus()`
+
+このメソッドは、 input にフォーカスが当たったときに呼び出されます。これにより、入力の代わりに [focus](https://developer.mozilla.org/en-US/docs/Web/API/HTMLElement/focus) イベントと [focusin](https://developer.mozilla.org/en-US/docs/Web/API/Element/focusin_event) イベントの両方がディスパッチされます。
+
+## Tips
+
+### フォーカス移譲
+
+送信に失敗した場合、Conform は最初の無効な input 要素にフォーカスします。しかし、カスタム input を使用している場合、これは機能しないかもしれません。これを修正するには、フォーカスイベントをリスニングして、希望する要素に対して `element.focus()` をトリガーすることで、input 要素からフォーカスを転送できます。
+
+```tsx
+import { useForm, useInputControl } from '@conform-to/react';
+import { Select, Option } from './custom-ui';
+
+function Example() {
+ const [form, fields] = useForm();
+ const inputRef = useRef(null);
+ const color = useInputControl(fields.color);
+
+ return (
+ <>
+ inputRef.current?.focus()}
+ />
+
+ <>
+ );
+}
+```
+
+上記の例では、カスタム入力によってレンダリングされる内部の入力を制御できないため、カスタムセレクトコンポーネントに `name` プロパティを渡す代わりに、手動で非表示の入力を設定しています。入力は視覚的には隠されていますが、 [tailwindcss](https://tailwindcss.com/docs/screen-readers#screen-reader-only-elements) からの **sr-only** クラスのおかげで依然としてフォーカス可能です。入力がフォーカスされたとき、 `inputRef.current?.focus()` を呼び出してカスタム入力へフォーカスを委譲します。
+
+もし tailwindcss を使用していない場合は、好みのスタイリングソリューションから同様のユーティリティを探すか、または以下のスタイルを適用して **sr-only** クラスの実装に基づいた対応を行うことができます:
+
+```tsx
+const style = {
+ position: 'absolute',
+ width: '1px',
+ height: '1px',
+ padding: 0,
+ margin: '-1px',
+ overflow: 'hidden',
+ clip: 'rect(0,0,0,0)',
+ whiteSpace: 'nowrap',
+ border: 0,
+};
+```
diff --git a/docs/ja/api/validitystate.md b/docs/ja/api/validitystate.md
new file mode 100644
index 00000000..ebbd9ea1
--- /dev/null
+++ b/docs/ja/api/validitystate.md
@@ -0,0 +1,207 @@
+# @conform-to/validitystate
+
+> 現在のバージョンは、 Conform の React アダプターと互換性がありません。
+
+検証属性に基づくサーバー検証のための [Conform](https://github.com/edmundhung/conform) ヘルパーです。
+
+### parse
+
+制約に基づいてサーバー上で FormData または URLSearchParams を解析し、オプショナルなエラーフォーマッターを使用する関数です。
+
+```ts
+import { type FormConstraints, type FormatErrorArgs, parse } from '@conform-to/validitystate';
+
+const constraints = {
+ email: { type: 'email', required: true },
+ password: { type: 'password', required: true },
+ remember: { type: 'checkbox' },
+} satisify FormConstraints;
+
+function formatError({ input }: FormatErrorArgs) {
+ switch (input.name) {
+ case 'email': {
+ if (input.validity.valueMissing) {
+ return 'Email is required';
+ } else if (input.validity.typeMismatch) {
+ return 'Email is invalid';
+ }
+ break;
+ }
+ case 'password': {
+ if (input.validity.valueMissing) {
+ return 'Password is required';
+ }
+ break;
+ }
+ }
+
+ return '';
+}
+
+const submission = parse(formData, {
+ constraints,
+ formatError,
+});
+
+// エラーは、入力名を対応するエラーにマッピングする辞書になります。
+// 例: { email: 'Email is required', password: 'Password is required' }
+console.log(submission.error);
+
+if (!submission.error) {
+ // エラーがない場合、推論された型を持つ解析されたデータが利用可能になります。
+ // 例: { email: string; password: string; remember: boolean; }
+ console.log(submission.value);
+}
+```
+
+エラーフォーマッターは、複数のエラーも返すことができます。
+
+```ts
+function formatError({ input }: FormatErrorArgs) {
+ const error = [];
+
+ switch (input.name) {
+ case 'email': {
+ if (input.validity.valueMissing) {
+ error.push('Email is required');
+ }
+ if (input.validity.typeMismatch) {
+ error.push('Email is invalid');
+ }
+ break;
+ }
+ case 'password': {
+ if (input.validity.valueMissing) {
+ error.push('Password is required');
+ }
+ if (input.validity.tooShort) {
+ error.push('Password is too short');
+ }
+ break;
+ }
+ }
+
+ return error;
+}
+```
+
+エラーフォーマッターが提供されていない場合は、デフォルトの挙動について [defaultFormatError](#defaultformaterror) ヘルパーをチェックしてください。
+
+### validate
+
+クライアントのバリデーションをカスタマイズするために、制約とエラーフォーマッターを再利用するヘルパーです。エラーは `setCustomValidity` メソッドを使用してフォームコントロール要素に設定されます。新しいエラーを報告する前(つまり、 `form.reportValidity()` をトリガーする前)に呼び出すべきです。
+
+```tsx
+import { validate } from '@conform-to/validitystate';
+
+function Example() {
+ return (
+
+ );
+}
+```
+
+### defaultFormatError
+
+これは、 [parse](#parse) によって全ての失敗したバリデーション属性によるエラーを表すために使用されるデフォルトのエラーフォーマッターです。例えば:
+
+```json
+{ "email": ["required", "type"], "password": ["required"] }
+```
+
+このヘルパーは、デフォルトのエラーフォーマッターに基づいてエラーをカスタマイズしたい場合に役立ちます。
+
+```ts
+import { type FormConstraints, type FormatErrorArgs, defaultFormatError } from '@conform-to/validitystate';
+
+const constraints = {
+ email: { type: 'email', required: true },
+ password: { type: 'password', required: true },
+ confirmPassowrd: { type: 'password', required: true },
+} satisify FormConstraints;
+
+function formatError({ input }: FormatErrorArgs) {
+ const error = defaultFormatError({ input });
+
+ if (input.name === 'confirmPassword' && error.length === 0 && value.password !== value.confirmPassword) {
+ error.push('notmatch');
+ }
+
+ return error;
+}
+
+const submission = parse(formData, {
+ constraints,
+ formatError,
+});
+```
+
+### getError
+
+実際のエラーメッセージは `validationMessage` プロパティに保存されます。これは、カスタムエラーフォーマッターが複数のエラーを返す場合に必要です。
+
+```tsx
+import { getError } from '@conform-to/validitystate';
+
+function Example() {
+ const [error, setError] = useState({});
+
+ return (
+
+ );
+}
+```
+
+## サポートされる属性
+
+> `month` および `week` の入力タイプは、ブラウザのサポートが限られているため実装されていません。
+
+| サポート | type | required | minLength | maxLength | pattern | min | max | step | multiple |
+| :------------- | :--: | :------: | :-------: | :-------: | :-----: | :-: | :-: | :--: | :------: |
+| text | | 🗸 | 🗸 | 🗸 | 🗸 | | | |
+| email | 🗸 | 🗸 | 🗸 | 🗸 | 🗸 | | | |
+| password | | 🗸 | 🗸 | 🗸 | 🗸 | | | |
+| url | 🗸 | 🗸 | 🗸 | 🗸 | 🗸 | | | |
+| tel | | 🗸 | 🗸 | 🗸 | 🗸 | | | |
+| search | | 🗸 | 🗸 | 🗸 | 🗸 | | | |
+| datetime-local | | 🗸 | | | | 🗸 | 🗸 | 🗸 |
+| date | | 🗸 | | | | 🗸 | 🗸 | 🗸 |
+| time | | 🗸 | | | | 🗸 | 🗸 | 🗸 |
+| select | | 🗸 | | | | | | | 🗸 |
+| textarea | | 🗸 | 🗸 | 🗸 | | | | |
+| radio | | 🗸 | | | | | | |
+| color | | 🗸 | | | | | | |
+| checkbox | | 🗸 | | | | | | |
+| number | | 🗸 | | | | 🗸 | 🗸 | 🗸 |
+| range | | 🗸 | | | | 🗸 | 🗸 | 🗸 |
+| file | | 🗸 | | | | | | | 🗸 |
diff --git a/docs/ja/api/yup/getYupConstraint.md b/docs/ja/api/yup/getYupConstraint.md
new file mode 100644
index 00000000..42d249c6
--- /dev/null
+++ b/docs/ja/api/yup/getYupConstraint.md
@@ -0,0 +1,34 @@
+# getYupConstraint
+
+Yup スキーマをイントロスペクトすることで、各フィールドの検証属性を含むオブジェクトを返すヘルパーです。
+
+```tsx
+const constraint = getYupConstraint(schema);
+```
+
+## パラメータ
+
+### `schema`
+
+イントロスペクトされるべき Yup スキーマです。
+
+## 例
+
+```tsx
+import { getYupConstraint } from '@conform-to/yup';
+import { useForm } from '@conform-to/react';
+import * as yup from 'yup';
+
+const schema = yup.object({
+ title: yup.string().required().min(5).max(20),
+ description: yup.string().optional().min(100).max(1000),
+});
+
+function Example() {
+ const [form, fields] = useForm({
+ constraint: getYupConstraint(schema),
+ });
+
+ // ...
+}
+```
diff --git a/docs/ja/api/yup/parseWithYup.md b/docs/ja/api/yup/parseWithYup.md
new file mode 100644
index 00000000..55c8604b
--- /dev/null
+++ b/docs/ja/api/yup/parseWithYup.md
@@ -0,0 +1,46 @@
+# parseWithYup
+
+指定された yup スキーマを使用してフォームデータを解析し、送信内容の概要を返すヘルパーです。
+
+```tsx
+const submission = parseWithYup(payload, options);
+```
+
+## パラメータ
+
+### `payload`
+
+フォームの送信方法に応じて、 **FormData** オブジェクトまたは **URLSearchParams** オブジェクトのいずれかになります。
+
+### `options`
+
+#### `schema`
+
+Yup スキーマ、または Yup スキーマを返す関数のいずれかです。
+
+#### `async`
+
+**validateSync** の代わりに yup スキーマから **validate** メソッドを使用してフォームデータを解析したい場合は、 **true** に設定してください。
+
+## 例
+
+```tsx
+import { parseWithYup } from '@conform-to/zod';
+import { useForm } from '@conform-to/react';
+import * as yup from 'zod';
+
+const schema = yup.object({
+ email: yup.string().email(),
+ password: yup.string(),
+});
+
+function Example() {
+ const [form, fields] = useForm({
+ onValidate({ formData }) {
+ return parseWithYup(formData, { schema });
+ },
+ });
+
+ // ...
+}
+```
diff --git a/docs/ja/api/zod/conformZodMessage.md b/docs/ja/api/zod/conformZodMessage.md
new file mode 100644
index 00000000..5f294f68
--- /dev/null
+++ b/docs/ja/api/zod/conformZodMessage.md
@@ -0,0 +1,121 @@
+# conformZodMessage
+
+検証動作を制御するためのカスタムメッセージのセットです。これは、フィールドの一つに対して非同期検証が必要な場合に便利です。
+
+## オプション
+
+### `conformZodMessage.VALIDATION_SKIPPED`
+
+このメッセージは、検証がスキップされ、 Conform が前回の結果を代わりに使用すべきであることを示すために使用されます。
+
+### `conformZodMessage.VALIDATION_UNDEFINED`
+
+このメッセージは、検証が定義されていないことを示し、 Conform がサーバー検証にフォールバックすべきであることを示すために使用されます。
+
+## 例
+
+以下は、メールアドレスがユニークであるかを検証するサインアップフォームの例です。
+
+```tsx
+import type { Intent } from '@conform-to/react';
+import { useForm } from '@conform-to/react';
+import { parseWithZod, conformZodMessage } from '@conform-to/zod';
+import { z } from 'zod';
+
+ // スキーマを共有する代わりに、スキーマを作成する関数 `createSchema` を準備します。
+ // `intent` は `parseWithZod` ヘルパーによって提供されます。
+ intent: Intent | null,
+ options?: {
+ isEmailUnique: (email: string) => Promise;
+ },
+) {
+ return z
+ .object({
+ email: z
+ .string()
+ .email()
+ // スキーマをパイプして、メールアドレスが有効な場合にのみ実行されるようにします。
+ .pipe(
+ z.string().superRefine((email, ctx) => {
+ const isValidatingEmail =
+ intent === null ||
+ (intent.type === 'validate' && intent.payload.name === 'email');
+
+ if (!isValidatingEmail) {
+ ctx.addIssue({
+ code: 'custom',
+ message: conformZodMessage.VALIDATION_SKIPPED,
+ });
+ return;
+ }
+
+ if (typeof options?.isEmailUnique !== 'function') {
+ ctx.addIssue({
+ code: 'custom',
+ message: conformZodMessage.VALIDATION_UNDEFINED,
+ fatal: true,
+ });
+ return;
+ }
+
+ return options.isEmailUnique(email).then((isUnique) => {
+ if (!isUnique) {
+ ctx.addIssue({
+ code: 'custom',
+ message: 'Email is already used',
+ });
+ }
+ });
+ }),
+ ),
+ })
+ .and(
+ z
+ .object({
+ password: z.string({ required_error: 'Password is required' }),
+ confirmPassword: z.string({
+ required_error: 'Confirm password is required',
+ }),
+ })
+ .refine((data) => data.password === data.confirmPassword, {
+ message: 'Password does not match',
+ path: ['confirmPassword'],
+ }),
+ );
+}
+
+export async function action({ request }) {
+ const formData = await request.formData();
+ const submission = await parseWithZod(formData, {
+ schema: (intent) =>
+ // インテントに基づいてzodスキーマを作成します。
+ createSchema(intent, {
+ isEmailUnique(email) {
+ // データベースをクエリ
+ },
+ }),
+ async: true,
+ });
+
+ if (submission.status !== 'success') {
+ return submission.reply();
+ }
+
+ // ...
+}
+
+export default function Signup() {
+ const lastResult = useActionData();
+ const [form, fields] = useForm({
+ lastResult,
+ onValidate({ formData }) {
+ return parseWithZod(formData, {
+ // `isEmailUnique` が定義されていない状態でスキーマを作成します。
+ schema: (intent) => createSchema(intent),
+ });
+ },
+ });
+
+ // ...
+}
+```
diff --git a/docs/ja/api/zod/getZodConstraint.md b/docs/ja/api/zod/getZodConstraint.md
new file mode 100644
index 00000000..90381cab
--- /dev/null
+++ b/docs/ja/api/zod/getZodConstraint.md
@@ -0,0 +1,34 @@
+# getZodConstraint
+
+Zod スキーマを内省することにより、各フィールドの検証属性を含むオブジェクトを返すヘルパーです。
+
+```tsx
+const constraint = getZodConstraint(schema);
+```
+
+## パラメータ
+
+### `schema`
+
+イントロスペクトされる zod スキーマ。
+
+## 例
+
+```tsx
+import { getZodConstraint } from '@conform-to/zod';
+import { useForm } from '@conform-to/react';
+import { z } from 'zod';
+
+const schema = z.object({
+ title: z.string().min(5).max(20),
+ description: z.string().min(100).max(1000).optional(),
+});
+
+function Example() {
+ const [form, fields] = useForm({
+ constraint: getZodConstraint(schema),
+ });
+
+ // ...
+}
+```
diff --git a/docs/ja/api/zod/parseWithZod.md b/docs/ja/api/zod/parseWithZod.md
new file mode 100644
index 00000000..6ec6de4d
--- /dev/null
+++ b/docs/ja/api/zod/parseWithZod.md
@@ -0,0 +1,99 @@
+# parseWithZod
+
+提供された zod スキーマを使用してフォームデータを解析し、送信内容の概要を返すヘルパーです。
+
+```tsx
+const submission = parseWithZod(payload, options);
+```
+
+## パラメータ
+
+### `payload`
+
+フォームの送信方法に応じて、 **FormData** オブジェクトまたは **URLSearchParams** オブジェクトのいずれかになります。
+
+### `options`
+
+#### `schema`
+
+Zod スキーマ、または Zod スキーマを返す関数のいずれかです。
+
+#### `async`
+
+**safeParse** の代わりに zod スキーマから **safeParseAsync** メソッドを使用してフォームデータを解析したい場合は、 **true** に設定してください。
+
+#### `errorMap`
+
+フォームデータを解析する際に使用される zod の [エラーマップ](https://github.com/colinhacks/zod/blob/master/ERROR_HANDLING.md#contextual-error-map) です。
+
+#### `formatError`
+
+エラー構造をカスタマイズし、必要に応じて追加のメタデータを含めることができる関数です。
+
+## 例
+
+```tsx
+import { parseWithZod } from '@conform-to/zod';
+import { useForm } from '@conform-to/react';
+import { z } from 'zod';
+
+const schema = z.object({
+ email: z.string().email(),
+ password: z.string(),
+});
+
+function Example() {
+ const [form, fields] = useForm({
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+ });
+
+ // ...
+}
+```
+
+## Tips
+
+### 自動型変換
+
+Conform は空の値を除去し、スキーマを内省することでフォームデータを期待される型に強制し、追加の前処理ステップを注入します。以下のルールが適用されます:
+
+1. 値が空の文字列 / ファイルである場合、スキーマに `undefined` を渡します。
+2. スキーマが `z.string()` の場合、値をそのまま渡します。
+3. スキーマが `z.number()` の場合、値をトリムして `Number` コンストラクタでキャストします。
+4. スキーマが `z.boolean()` の場合、値が `on` に等しい場合には `true` として扱います。
+5. スキーマが `z.date()` の場合、値を `Date` コンストラクタでキャストします。
+6. スキーマが `z.bigint()` の場合、値を `BigInt` コンストラクタでキャストします。
+
+この挙動は、スキーマ内で独自の `z.preprocess` ステップを設定することで上書きすることができます。
+
+> 注意: v3.22 以降、 `z.preprocess` の挙動に関して Zod のリポジトリには複数のバグレポートがあります。例えば、 https://github.com/colinhacks/zod/issues/2671 および
https://github.com/colinhacks/zod/issues/2677 があります。問題を経験している場合は、v3.21.4 にダウングレードしてください。
+
+```tsx
+const schema = z.object({
+ amount: z.preprocess((value) => {
+ // 値が提供されていない場合は、 `undefined` を返します。
+ if (!value) {
+ return undefined;
+ }
+
+ // 書式をクリアして値を数値に変換します。
+ return Number(value.trim().replace(/,/g, ''));
+ }, z.number()),
+});
+```
+
+### デフォルト値
+
+Conform はすでに空の値を `undefined` に前処理しています。 `.default()` をスキーマに追加して、代わりに返されるデフォルト値を定義します。
+
+Zod は、前処理後の入力が `undefined` の場合、デフォルト値を返します。これはスキーマの戻り値の型を変更する効果もあります。
+
+```tsx
+const schema = z.object({
+ foo: z.string(), // string | undefined
+ bar: z.string().default('bar'), // string
+ baz: z.string().nullable().default(null), // string | null
+});
+```
diff --git a/docs/ja/checkbox-and-radio-group.md b/docs/ja/checkbox-and-radio-group.md
new file mode 100644
index 00000000..c30a8980
--- /dev/null
+++ b/docs/ja/checkbox-and-radio-group.md
@@ -0,0 +1,98 @@
+# チェックボックスとラジオグループ
+
+チェックボックスやラジオグループの設定も、他の標準的な input と変わりません。
+
+## ラジオグループ
+
+ラジオグループを設定するには、すべての入力で **name** 属性が同じであることを確認してください。また、フィールドメタデータから initialValue を使用して、ラジオボタンがチェックされるべきかを導き出すこともできます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return (
+
+ );
+}
+```
+
+## チェックボックス
+
+チェックボックスグループの設定は、ラジオグループと似ていますが、チェックボックスがブール値なのかグループなのかについてサーバーサイドで情報が不足しているため、 initialValue は文字列または配列のいずれかになります。以下に示すように、 initialValue から **defaultChecked** 値を導出できます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return (
+
+ );
+}
+```
+
+ただし、単一のチェックボックスの場合は、ブラウザによってデフォルトで **on** に設定されている入力 **value** と initialValue が一致するかどうかを確認できます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return (
+
+ );
+}
+```
diff --git a/docs/ja/complex-structures.md b/docs/ja/complex-structures.md
new file mode 100644
index 00000000..710998fd
--- /dev/null
+++ b/docs/ja/complex-structures.md
@@ -0,0 +1,95 @@
+# ネストされたオブジェクトと配列
+
+Conform は、 name 属性の命名規則を活用することで、ネストされたオブジェクトと配列の両方をサポートしています。
+
+## 命名規則
+
+Conform は、データ構造を示すために `object.property` および `array[index]` の構文を使用します。これらの表記法は、ネストされた配列に対しても組み合わせることができます。例えば、 `tasks[0].content` のようになります。フォームデータに `['tasks[0].content', 'Hello World']` というエントリがある場合、構築されるオブジェクトは `{ tasks: [{ content: 'Hello World' }] }` になります。
+
+しかし、各フィールドの name 属性を手動で設定する必要はありません。 Conform は常に名前を推測してくれるため、生成された名前を全て使用していれば、より良い型安全性が得られます。
+
+## ネストされたオブジェクト
+
+ネストされたフィールドを設定するには、親フィールドのメタデータから `getFieldset()` メソッドを呼び出し、名前が自動的に推測される各子フィールドにアクセスしてください。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+ const address = fields.address.getFieldset();
+
+ return (
+
+ );
+}
+```
+
+## 配列
+
+フィールドのリストを設定する必要がある場合は、親フィールドのメタデータから `getFieldList()` メソッドを呼び出して、名前が自動的に推測される各アイテムフィールドにアクセスできます。リスト内のアイテムを変更したい場合は、 [Intent button](./intent-button.md#insert-remove-and-reorder-intents) ページで説明されているように、 `insert` 、 `remove` 、 `reorder` のインテントも使用できます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+ const tasks = fields.tasks.getFieldList();
+
+ return (
+
+ );
+}
+```
+
+## ネストされた配列
+
+ネストされた配列に対して、 `getFieldset()` と `getFieldList()` の両方を組み合わせて使用することもできます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Example() {
+ const [form, fields] = useForm();
+ const todos = fields.todos.getFieldList();
+
+ return (
+
+ );
+}
+```
diff --git a/docs/ja/file-upload.md b/docs/ja/file-upload.md
new file mode 100644
index 00000000..4c9f373a
--- /dev/null
+++ b/docs/ja/file-upload.md
@@ -0,0 +1,83 @@
+# ファイルのアップロード
+
+ファイルアップロードを扱うには、フォームの **encType** 属性を `multipart/form-data` に設定し、メソッドは `POST` である必要があります。
+
+## 構成
+
+ファイル input の設定は、他の input と何ら変わりません。
+
+```tsx
+import { useForm } from '@conform-to/react';
+import { parseWithZod } from '@conform-to/zod';
+import { z } from 'zod';
+
+const schema = z.object({
+ profile: z.instanceof(File, { message: 'Profile is required' }),
+});
+
+function Example() {
+ const [form, fields] = useForm({
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+ });
+
+ return (
+
+ );
+}
+```
+
+## 複数のファイル
+
+複数のファイルをアップロードできるようにするには、ファイル入力に **multiple** 属性を設定する必要があります。フィールドメタデータのエラーが各ファイルのすべてのエラーを含んでいない可能性があることに注意することが重要です。 yup および zod からのエラーは、対応するパスに基づいてマッピングされ、各ファイルのエラーは、配列自体(例: `files` )ではなく、対応するインデックス(例: `files[0]` )にマッピングされます。すべてのエラーを表示したい場合は、フィールドメタデータの **allErrors** プロパティを代わりに使用することを検討できます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+import { parse } from '@conform-to/zod';
+import { z } from 'zod';
+
+const schema = z.object({
+ files: z
+ .array(
+ z
+ .instanceof(File)
+ .refine((file) => file.size < 1024, 'File size must be less than 1kb'),
+ )
+ .min(1, 'At least 1 file is required')
+ .refine(
+ (files) => files.every((file) => file.size < 1024),
+ 'File size must be less than 1kb',
+ ),
+});
+
+function Example() {
+ const [form, fields] = useForm({
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+ });
+
+ return (
+
+ );
+}
+```
diff --git a/docs/ja/integration/nextjs.md b/docs/ja/integration/nextjs.md
new file mode 100644
index 00000000..ba6d2845
--- /dev/null
+++ b/docs/ja/integration/nextjs.md
@@ -0,0 +1,80 @@
+# Next.js
+
+[Next.js](https://nextjs.org)とインテグレーションしたログインフォームの例をこちらに示します。完全な例は[こちら](../../examples/nextjs)で見ることができます。
+
+```tsx
+// schema.ts
+import { z } from 'zod';
+
+export const loginSchema = z.object({
+ email: z.string().email(),
+ password: z.string(),
+ remember: z.boolean().optional(),
+});
+
+// action.ts
+('use server');
+
+import { redirect } from 'next/navigation';
+import { parseWithZod } from '@conform-to/zod';
+import { loginSchema } from '@/app/schema';
+
+export async function login(prevState: unknown, formData: FormData) {
+ const submission = parseWithZod(formData, {
+ schema: loginSchema,
+ });
+
+ if (submission.status !== 'success') {
+ return submission.reply();
+ }
+
+ redirect('/dashboard');
+}
+
+// form.tsx
+('use client');
+
+import { useForm } from '@conform-to/react';
+import { parseWithZod } from '@conform-to/zod';
+import { useFormState } from 'react-dom';
+import { login } from '@/app/actions';
+import { loginSchema } from '@/app/schema';
+
+export function LoginForm() {
+ const [lastResult, action] = useFormState(login, undefined);
+ const [form, fields] = useForm({
+ // 前回の送信結果を同期
+ lastResult,
+
+ // クライアントでバリデーション・ロジックを再利用する
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema: loginSchema });
+ },
+
+ // blurイベント発生時にフォームを検証する
+ shouldValidate: 'onBlur',
+ });
+
+ return (
+
+ );
+}
+```
diff --git a/docs/ja/integration/remix.md b/docs/ja/integration/remix.md
new file mode 100644
index 00000000..2dc961ed
--- /dev/null
+++ b/docs/ja/integration/remix.md
@@ -0,0 +1,69 @@
+# Remix
+
+[Remix](https://remix.run/) とインテグレーションしたログインフォームの例です。完全な例は [こちら](../../examples/remix) です。
+
+```tsx
+import { getFormProps, getInputProps, useForm } from '@conform-to/react';
+import { parseWithZod } from '@conform-to/zod';
+import type { ActionArgs } from '@remix-run/node';
+import { json, redirect } from '@remix-run/node';
+import { Form, useActionData } from '@remix-run/react';
+import { z } from 'zod';
+
+const schema = z.object({
+ email: z.string().email(),
+ password: z.string(),
+ remember: z.boolean().optional(),
+});
+
+export async function action({ request }: ActionArgs) {
+ const formData = await request.formData();
+ const submission = parseWithZod(formData, { schema });
+
+ if (submission.status !== 'success') {
+ return json(submission.reply());
+ }
+
+ // ...
+}
+
+export default function Login() {
+ // サーバーから最後に返された送信結果
+ const lastResult = useActionData();
+ const [form, fields] = useForm({
+ // 前回の送信結果を同期
+ lastResult,
+
+ // クライアントでバリデーション・ロジックを再利用する
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+
+ // blurイベント発生時にフォームを検証する
+ shouldValidate: 'onBlur',
+ });
+
+ return (
+
+ );
+}
+```
diff --git a/docs/ja/integration/ui-libraries.md b/docs/ja/integration/ui-libraries.md
new file mode 100644
index 00000000..43b556a3
--- /dev/null
+++ b/docs/ja/integration/ui-libraries.md
@@ -0,0 +1,230 @@
+# UI ライブラリとのインテグレーション
+
+このガイドでは、カスタム入力コンポーネントを Conform とインテグレーションする方法を紹介します。
+
+## イベント移譲
+
+Conform は、ドキュメントに直接 **input** と **focusout** イベントリスナーをアタッチすることで、すべてのネイティブ入力をサポートしています。 `` 、 `` 、または `` 要素にイベントハンドラーを設定する必要はありません。唯一の要件は、 `` 要素にフォーム **id** を設定し、すべての入力に **name** 属性が設定されていて、 [form](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/input#form) 属性を使用するか、 `` 要素内にネストすることでフォームに関連付けられていることです。
+
+```tsx
+function Example() {
+ const [form, fields] = useForm({
+ // オプション。指定されていない場合は Conform がランダムなIDを生成します。
+ id: 'example',
+ });
+
+ return (
+
+ );
+}
+```
+
+## インテグレーションが必要かどうかの判別
+
+Conform は[イベント移譲](#event-delegation)に依存してフォームをバリデートし、フォームイベントを発行する限り、どのようなカスタム入力とも動作します。これは通常、 `` や `` のように、ネイティブの入力要素をラップするだけのシンプルなコンポーネントに対して当てはまります。しかし、 `` や `` のようなカスタム入力コンポーネントでは、ユーザーが非ネイティブのフォーム要素で操作し、隠された入力を使うため、フォームイベントが発行されない可能性が高いです。
+
+入力がネイティブ入力かどうかを識別するために、カスタム入力を操作している間にフォームイベントが発行され、バブルアップするかどうかを確認するために、イベントリスナーを添付した div で入力をラップすることができます。また、以下にはいくつかの人気のある UI ライブラリに関する [例](#examples) も掲載されています。
+
+```tsx
+import { CustomInput } from 'your-ui-library';
+
+function Example() {
+ return (
+
+
+
+ );
+}
+```
+
+## `useInputControl` を使用してカスタム入力コンポーネントを強化する
+
+この問題を解決するために、 Conform は [useInputControl](../api/react/useInputControl.md) というフックを提供しています。これにより、必要なときにフォームイベントを発行するようにカスタム入力を強化できます。このフックは以下のプロパティを持つコントロールオブジェクトを返します:
+
+- `value`: フォームの[リセットおよび更新のインテント](../intent-button.md#reset-and-update-intent)に対応した、入力の現在の値
+- `change`: 現在の値を更新し、`change` および `input` イベントの両方を発行するための関数
+- `focus`: `focus` および `focusin` イベントを発行するための関数
+- `blur`: `blur` および`focusout` イベントを発行するための関数
+
+以下は、Radix UI の[Select コンポーネント](https://www.radix-ui.com/primitives/docs/components/select)をラップする例です:
+
+```tsx
+import {
+ type FieldMetadata,
+ useForm,
+ useInputControl,
+} from '@conform-to/react';
+import * as Select from '@radix-ui/react-select';
+import {
+ CheckIcon,
+ ChevronDownIcon,
+ ChevronUpIcon,
+} from '@radix-ui/react-icons';
+
+type SelectFieldProps = {
+ // `FieldMetadata` 型を使用して `meta` プロパティを定義し、
+ // そのジェネリクスを通じて受け入れるフィールドの型を制限することができます。
+ meta: FieldMetadata;
+ options: Array;
+};
+
+function SelectField({ meta, options }: SelectFieldProps) {
+ const control = useInputControl(meta);
+
+ return (
+ {
+ control.change(value);
+ }}
+ onOpenChange={(open) => {
+ if (!open) {
+ control.blur();
+ }
+ }}
+ >
+
+
+
+
+
+
+
+
+
+
+
+
+ {options.map((option) => (
+
+ {option}
+
+
+
+
+ ))}
+
+
+
+
+
+
+
+ );
+}
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return (
+
+ );
+}
+```
+
+## フォームコンテキストでシンプルに
+
+[useField](../api/react/useField.md) フックを [FormProvider](../api/react/FormProvider.md) と共に使用することで、ラッパーコンポーネントをさらにシンプルにすることもできます。
+
+```tsx
+import {
+ type FieldName,
+ FormProvider,
+ useForm,
+ useField,
+ useInputControl,
+} from '@conform-to/react';
+import * as Select from '@radix-ui/react-select';
+import {
+ CheckIcon,
+ ChevronDownIcon,
+ ChevronUpIcon,
+} from '@radix-ui/react-icons';
+
+type SelectFieldProps = {
+ // `FieldMetadata` 型の代わりに `FieldName` 型を使用します。
+ // また、そのジェネリクスを通じて受け入れるフィールドの型を制限することもできます。
+ name: FieldName;
+ options: Array;
+};
+
+function Select({ name, options }: SelectFieldProps) {
+ const [meta] = useField(name);
+ const control = useInputControl(meta);
+
+ return (
+ {
+ control.change(value);
+ }}
+ onOpenChange={(open) => {
+ if (!open) {
+ control.blur();
+ }
+ }}
+ >
+ {/* ... */}
+
+ );
+}
+
+function Example() {
+ const [form, fields] = useForm();
+
+ return (
+
+
+
+ );
+}
+```
+
+## 例
+
+こちらでは、いくつかの人気のある UI ライブラリとの統合例を見ることができます。
+
+> Radix UI や React Aria Component など、さらに多くの UI ライブラリの例を準備するためのコントリビューターを探しています。
+
+- [Chakra UI](../../examples/chakra-ui/)
+- [Headless UI](../../examples/headless-ui/)
+- [Material UI](../../examples/material-ui/)
+- [Radix UI](../../examples/radix-ui/)
+- [Shadcn UI](../../examples/shadcn-ui/)
diff --git a/docs/ja/intent-button.md b/docs/ja/intent-button.md
new file mode 100644
index 00000000..f0788b6c
--- /dev/null
+++ b/docs/ja/intent-button.md
@@ -0,0 +1,181 @@
+# インテントボタン
+
+送信ボタンがフォームの送信を行う際、それは [submitter](https://developer.mozilla.org/en-US/docs/Web/API/SubmitEvent/submitter) として機能し、フォームデータに含まれることになります。
+
+## 送信のインテント
+
+submitter は、インテント(意図)に基づいて異なる振る舞いでフォームを拡張したい場合に特に便利です。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function Product() {
+ const [form] = useForm({
+ onSubmit(event, { formData }) {
+ event.preventDefault();
+
+ switch (formData.get('intent')) {
+ case 'add-to-cart':
+ // カートに追加
+ break;
+ case 'buy-now':
+ // 購入
+ break;
+ }
+ },
+ });
+
+ return (
+
+ );
+}
+```
+
+## フォームのコントロール
+
+Conform は、フィールドのバリデーションや削除など、すべてのフォームコントロールに対して送信のインテントを利用します。これは、ボタンに予約された名前を与え、インテントを値としてシリアライズすることで成されます。設定を簡素化するために、 Conform は `form.validate` 、 `form.reset` 、 `form.insert` などの一連のフォームコントロールヘルパーを提供します。
+
+### バリデート インテント
+
+バリデーションをトリガーするには、バリデート インテントを使用してボタンを構成できます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function EmailForm() {
+ const [form, fields] = useForm();
+
+ return (
+
+ );
+}
+```
+
+ボタンがクリックされると、 Conform は予約された名前でシリアライズされたインテントを識別し、メールフィールドを検証済みとしてマークすることによりバリデーションをトリガーし、メールが無効である場合はエラーメッセージを返します。
+
+しかし、ユーザーがフィールドを離れた時点でバリデーションをトリガーしたい場合は、バリデート インテントを直接トリガーすることもできます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+function EmailForm() {
+ const [form, fields] = useForm();
+
+ return (
+
+ );
+}
+```
+
+### reset および update インテント
+
+**reset** および **update** のインテントを使ってフィールドを変更することもできます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+export default function Tasks() {
+ const [form, fields] = useForm();
+
+ return (
+
+ );
+}
+```
+
+両方のインテントを使用するには、フィールドメタデータから **key** を使って入力を設定する必要があります。 Conform はこのキーに依存して、更新された initialValue で input を再マウントするための React への通知を行います。唯一の例外は、 [useInputControl](./api/react/useInputControl.md) フックを使用して制御された入力を設定している場合で、 key が変更されると値がリセットされます。
+
+### insert、remove、および reorder (並び替え) インテント
+
+フィールドリストを操作するには、 **insert** 、 **remove** 、 **reorder** のインテントを使用できます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+
+export default function Tasks() {
+ const [form, fields] = useForm();
+ const tasks = fields.tasks.getFieldList();
+
+ return (
+
+ );
+}
+```
diff --git a/docs/ja/overview.md b/docs/ja/overview.md
new file mode 100644
index 00000000..b019f764
--- /dev/null
+++ b/docs/ja/overview.md
@@ -0,0 +1,85 @@
+# 概要
+
+Conform は、Web 標準に基づいて HTML フォームを段階的に強化し、 [Remix](https://remix.run) や [Next.js](https://nextjs.org) のようなサーバーフレームワークを完全にサポートする、型安全なフォームバリデーションライブラリです。
+
+## 特徴
+
+- プログレッシブエンハンスメント・ファーストな API
+- 型安全なフィールド推論
+- きめ細やかなサブスクリプション
+- 組み込みのアクセシビリティ・ヘルパー
+- Zod による自動型強制
+
+## The Gist
+
+Conformは、クライアントからサーバーへのフォーム送信のライフサイクルを制御し、`useForm()` フックを通じてフォームの状態を提供します。フォームのマークアップを制限せず、有効なHTMLフォームであればどのようなものでも動作します。フォームの値は、[FormData](https://developer.mozilla.org/en-US/docs/Web/API/FormData) Web APIを使用してDOMから取得され、イベント委譲によって同期されます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+import { parseWithZod } from '@conform-to/zod';
+import { z } from 'zod';
+import { login } from './your-auth-library';
+import { useActionResult, redirect } from './your-server-framework';
+
+// フォームのスキーマを定義する
+const schema = z.object({
+ username: z.string(),
+ password: z.string(),
+});
+
+// Optional: サーバーアクションハンドラ
+export async function action({ request }) {
+ const formData = await request.formData();
+ const submission = parseWithZod(formData, { schema });
+
+ // ステータスが成功でない場合は、送信内容をクライアントに返送する
+ if (submission.status !== 'success') {
+ return submission.reply();
+ }
+
+ const session = await login(submission.value);
+
+ // ログインに失敗した場合は、追加のエラーメッセージとともに送信内容を送る
+ if (!session) {
+ return submission.reply({
+ formErrors: ['Incorrect username or password'],
+ });
+ }
+
+ return redirect('/dashboard');
+}
+
+// クライアントフォームコンポーネント
+export default function LoginForm() {
+ // サーバーアクションハンドラを定義している場合は、最後の送信結果を取得します。
+ // これはフレームワークによってはuseActionData()またはuseFormState()になります。
+ const lastResult = useActionResult();
+ const [form, fields] = useForm({
+ // 各フィールドをいつ検証するかを設定する
+ shouldValidate: 'onBlur',
+ // Optional: サーバー上で検証する場合のみ必要です。
+ lastResult,
+ // Optional: クライアント検証。提供されていない場合はサーバー検証にフォールバックします。
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+ });
+
+ return (
+
+ );
+}
+```
diff --git a/docs/ja/tutorial.md b/docs/ja/tutorial.md
new file mode 100644
index 00000000..6bfdb13f
--- /dev/null
+++ b/docs/ja/tutorial.md
@@ -0,0 +1,534 @@
+# チュートリアル
+
+このチュートリアルでは、最初に Remix と Zod だけを使用して基本的なコンタクトフォームを構築します。その後、 Conform を使用してそれを強化する方法をご紹介します。
+
+## インストール
+
+開始する前に、プロジェクトに Conform をインストールしてください。
+
+```sh
+npm install @conform-to/react @conform-to/zod --save
+```
+
+## 初期設定
+
+まず、スキーマを定義しましょう。ここでは、フォームデータの検証に使用する zod スキーマを示します:
+
+```ts
+import { z } from 'zod';
+
+const schema = z.object({
+ // zodが必要なチェックを適切に実行するためには、前処理ステップが必要です。
+ // 空の入力の値は通常、空の文字列であるためです。
+ email: z.preprocess(
+ (value) => (value === '' ? undefined : value),
+ z.string({ required_error: 'Email is required' }).email('Email is invalid'),
+ ),
+ message: z.preprocess(
+ (value) => (value === '' ? undefined : value),
+ z
+ .string({ required_error: 'Message is required' })
+ .min(10, 'Message is too short')
+ .max(100, 'Message is too long'),
+ ),
+});
+```
+
+action ハンドラでは、フォームデータを解析し、zod で検証します。エラーがある場合は、送信された値とともにクライアントに返します。
+
+```tsx
+import { type ActionFunctionArgs, redirect } from '@remix-run/node';
+import { z } from 'zod';
+import { sendMessage } from '~/message';
+
+const schema = z.object({
+ // ...
+});
+
+export async function action({ request }: ActionFunctionArgs) {
+ const formData = await request.formData();
+
+ // `Object.fromEntries` を使用してオブジェクトを構築します。
+ const payload = Object.fromEntries(formData);
+ // その後、zodでパースします。
+ const result = schema.safeParse(payload);
+
+ // データが有効でない場合は、エラーをクライアントに返します。
+ if (!result.success) {
+ const error = result.error.flatten();
+
+ return {
+ payload,
+ formErrors: error.formErrors,
+ fieldErrors: error.fieldErrors,
+ };
+ }
+
+ // チュートリアルにとって重要ではないので、実装はスキップします。
+ const message = await sendMessage(result.data);
+
+ // メッセージが送信されない場合は、フォームエラーを返します。
+ if (!message.sent) {
+ return {
+ payload,
+ formErrors: ['Failed to send the message. Please try again later.'],
+ fieldErrors: {},
+ };
+ }
+
+ return redirect('/messages');
+}
+```
+
+次に、コンタクトフォームを実装します。`useActionData()`から送信結果が返された場合、各フィールドの隣にエラーメッセージを表示します。フィールドは送信された値で初期化されるため、ドキュメントが再読み込みされた場合でもフォームデータが保持されます。
+
+```tsx
+import { type ActionFunctionArgs } from '@remix-run/node';
+import { Form, useActionData } from '@remix-run/react';
+import { z } from 'zod';
+import { sendMessage } from '~/message';
+
+const schema = z.object({
+ // ...
+});
+
+export async function action({ request }: ActionFunctionArgs) {
+ // ...
+}
+
+export default function ContactUs() {
+ const result = useActionData();
+
+ return (
+
+ );
+}
+```
+
+まだ終わっていません。アクセシビリティは決して見過ごされるべきではありません。次の属性を追加して、フォームをよりアクセシブルにしましょう:
+
+- それぞれのラベルが一意の **id** を用いて適切に入力と関連付けられていることを確認してください。
+- zod スキーマに似たバリデーション属性を設定する
+- 有効性に基づいてフォーム要素の **aria-invalid** 属性を設定する
+- エラーメッセージが **aria-describedby** 属性を用いてフォーム要素にリンクされていることを確認してください。
+
+```tsx
+import { type ActionFunctionArgs } from '@remix-run/node';
+import { Form, useActionData } from '@remix-run/react';
+import { z } from 'zod';
+import { sendMessage } from '~/message';
+
+const schema = z.object({
+ // ...
+});
+
+export async function action({ request }: ActionFunctionArgs) {
+ // ...
+}
+
+export default function ContactUs() {
+ const result = useActionData();
+
+ return (
+
+ );
+}
+```
+
+たとえシンプルなコンタクトフォームであっても、これは多くの作業を要します。また、すべての ID を維持することはエラーが発生しやすいです。これをどのように簡素化できるでしょう?
+
+## Conform の導入
+
+ここで Conform の出番です。始めるにあたり、 Conform の zod 統合機能が空文字列を自動的に除去してくれるため、 zod スキーマから前処理を削除できます。
+
+```tsx
+import { z } from 'zod';
+
+const schema = z.object({
+ email: z
+ .string({ required_error: 'Email is required' })
+ .email('Email is invalid'),
+ message: z
+ .string({ required_error: 'Message is required' })
+ .min(10, 'Message is too short')
+ .max(100, 'Message is too long'),
+});
+```
+
+次に、 `parseWithZod()` ヘルパー関数を使って action を簡素化できます。この関数はフォームデータを解析し、解析された値またはエラーを含む送信オブジェクトを返します。
+
+```tsx
+import { parseWithZod } from '@conform-to/zod';
+import { type ActionFunctionArgs } from '@remix-run/node';
+import { z } from 'zod';
+import { sendMessage } from '~/message';
+
+const schema = z.object({
+ // ...
+});
+
+export async function action({ request }: ActionFunctionArgs) {
+ const formData = await request.formData();
+
+ // `Object.fromEntries()` をparseWithZodヘルパーに置き換えます。
+ const submission = parseWithZod(formData, { schema });
+
+ // submission が成功しなかった場合、クライアントに送信結果を報告します。
+ if (submission.status !== 'success') {
+ return submission.reply();
+ }
+
+ const message = await sendMessage(submission.value);
+
+ //メッセージが送信されなかった場合は、フォームエラーを返します。
+ if (!message.sent) {
+ return submission.reply({
+ formErrors: ['Failed to send the message. Please try again later.'],
+ });
+ }
+
+ return redirect('/messages');
+}
+```
+
+これで、 [useForm](./api/react/useForm.md) フックを使って、すべてのフォームメタデータを管理できます。また、 `getZodConstraint()` ヘルパーを使用して、 zod スキーマからバリデーション属性を導出します。
+
+```tsx
+import { useForm } from '@conform-to/react';
+import { parseWithZod, getZodConstraint } from '@conform-to/zod';
+import { type ActionFunctionArgs } from '@remix-run/node';
+import { Form, useActionData } from '@remix-run/react';
+import { z } from 'zod';
+import { sendMessage } from '~/message';
+import { getUser } from '~/session';
+
+const schema = z.object({
+ // ...
+});
+
+export async function action({ request }: ActionFunctionArgs) {
+ // ...
+}
+
+export default function ContactUs() {
+ const lastResult = useActionData();
+ // useFormフックは、フォームをレンダリングするために必要なすべてのメタデータを返します。
+ // そして、フォームが送信されたときに最初の無効なフィールドにフォーカスします。
+ const [form, fields] = useForm({
+ //これにより、サーバーからのエラーが同期されるだけでなく、
+ // フォームのデフォルト値としても使用されます。
+ // プログレッシブエンハンスメントのためにドキュメントが再読み込みされた場合、
+
+ // 最後の結果からすべてのバリデーション属性を導出するために使用します。
+ constraint: getZodConstraint(schema),
+ });
+
+ return (
+
+ );
+}
+```
+
+## バリデーション体験の向上
+
+現在、コンタクトフォームはユーザーが送信したときにのみ検証されます。タイピングするたびにユーザーに早期フィードバックを提供したい場合はどうすればよいでしょうか?
+
+`shouldValidate` オプションと `shouldRevalidate` オプションを設定しましょう。
+
+```tsx
+import { useForm } from '@conform-to/react';
+import { parseWithZod } from '@conform-to/zod';
+import {
+ type ActionFunctionArgs,
+ type LoaderFunctionArgs,
+ json,
+} from '@remix-run/node';
+import { Form, useActionData, useLoaderData } from '@remix-run/react';
+import { sendMessage } from '~/message';
+import { getUser } from '~/session';
+
+const schema = z.object({
+ // ...
+});
+
+export async function loader({ request }: LoaderFunctionArgs) {
+ // ...
+}
+
+export async function action({ request }: ActionFunctionArgs) {
+ // ...
+}
+
+export default function ContactUs() {
+ const user = useLoaderData();
+ const lastResult = useActionData();
+ const [form, fields] = useForm({
+ // ... previous config
+
+ // Validate field once user leaves the field
+ shouldValidate: 'onBlur',
+ // Then, revalidate field as user types again
+ shouldRevalidate: 'onInput',
+ });
+
+ // ...
+}
+```
+
+この時点で、私たちのコンタクトフォームはサーバー上でのみ検証され、ユーザーがタイプするたびにフォームを検証するためにサーバーへの往復が発生します。クライアント検証でフィードバックループを短縮しましょう。
+
+```tsx
+import { useForm } from '@conform-to/react';
+import { parseWithZod } from '@conform-to/zod';
+import { type ActionFunctionArgs, type LoaderFunctionArgs } from '@remix-run/node';
+import { Form, useActionData, useLoaderData } from '@remix-run/react';
+import { sendMessage } from '~/message';
+import { getUser } from '~/session';
+
+const schema = z.object({
+ // ...
+});
+
+export async function action({ request }: ActionFunctionArgs) {
+ // ...
+}
+
+export default function ContactUs() {
+ const user = useLoaderData();
+ const lastResult = useActionData();
+ const [form, fields] = useForm({
+ // ... 以前の設定
+
+ //クライアント上で同じ検証ロジックを実行する
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+ });
+
+ return (
+
+ );
+}
+```
+
+## ボイラープレートの削除
+
+Conform がすべての ID とバリデーション属性を管理してくれるのは素晴らしいことです。しかし、フォームとフィールドを設定するのにはまだ多くの作業が必要です。ネイティブ入力を扱っている場合は、 [getFormProps](./api/react/getFormProps.md) や [getInputProps](./api/react/getInputProps.md) のようなヘルパーを使用してボイラープレートを最小限に抑えることができます。
+
+```tsx
+import {
+ useForm,
+ getFormProps,
+ getInputProps,
+ getTextareaProps,
+} from '@conform-to/react';
+import { parseWithZod, getZodConstraint } from '@conform-to/zod';
+import { type ActionFunctionArgs } from '@remix-run/node';
+import { Form, useActionData } from '@remix-run/react';
+import { sendMessage } from '~/message';
+
+const schema = z.object({
+ // ...
+});
+
+export async function action({ request }: ActionFunctionArgs) {
+ // ...
+}
+
+export default function ContactUs() {
+ const lastResult = useActionData();
+ const [form, fields] = useForm({
+ // ...
+ });
+
+ return (
+
+ );
+}
+```
+
+完了です!これが、このチュートリアルで構築した完全な例です:
+
+```tsx
+import {
+ useForm,
+ getFormProps,
+ getInputProps,
+ getTextareaProps,
+} from '@conform-to/react';
+import { parseWithZod, getZodConstraint } from '@conform-to/zod';
+import { type ActionFunctionArgs } from '@remix-run/node';
+import { Form, useActionData } from '@remix-run/react';
+import { z } from 'zod';
+import { sendMessage } from '~/message';
+
+const schema = z.object({
+ email: z
+ .string({ required_error: 'Email is required' })
+ .email('Email is invalid'),
+ message: z
+ .string({ required_error: 'Message is required' })
+ .min(10, 'Message is too short')
+ .max(100, 'Message is too long'),
+});
+
+export async function action({ request }: ActionFunctionArgs) {
+ const formData = await request.formData();
+ const submission = parseWithZod(formData, { schema });
+
+ if (submission.status !== 'success') {
+ return submission.reply();
+ }
+
+ const message = await sendMessage(submission.value);
+
+ if (!message.sent) {
+ return submission.reply({
+ formErrors: ['Failed to send the message. Please try again later.'],
+ });
+ }
+
+ return redirect('/messages');
+}
+
+export default function ContactUs() {
+ const lastResult = useActionData();
+ const [form, fields] = useForm({
+ lastResult,
+ constraint: getZodConstraint(schema),
+ shouldValidate: 'onBlur',
+ shouldRevalidate: 'onInput',
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+ });
+
+ return (
+
+ );
+}
+```
diff --git a/docs/ja/upgrading-v1.md b/docs/ja/upgrading-v1.md
new file mode 100644
index 00000000..c1c8ab7c
--- /dev/null
+++ b/docs/ja/upgrading-v1.md
@@ -0,0 +1,212 @@
+# v1 へのアップグレード
+
+このガイドでは、 v1 で導入されたすべての変更点を説明し、既存のコードベースをアップグレードする方法をご案内します。
+
+## 最小限必要な React バージョン
+
+Conform は現在、 React 18 以降を要求します。もし古いバージョンの React を使用している場合は、まず React のバージョンをアップグレードする必要があります。
+
+## `conform` オブジェクトは削除されました
+
+まず、すべてのヘルパーが改名され、個別にインポートできるようになりました:
+
+- `conform.input` -> [getInputProps](./api/react/getInputProps.md)
+- `conform.select` -> [getSelectProps](./api/react/getSelectProps.md)
+- `conform.textarea` -> [getTextareaProps](./api/react/getTextareaProps.md)
+- `conform.fieldset` -> [getFieldsetProps](./api/react/getFieldsetProps.md)
+- `conform.collection` -> [getCollectionProps](./api/react/getCollectionProps.md)
+
+以前 `conform.VALIDATION_UNDEFINED` および `conform.VALIDATION_SKIPPED` を使用していた場合、それらは zod インテグレーション (`@conform-to/zod`) に移されました。
+
+- `conform.VALIDATION_SKIPPED` -> [conformZodMessage.VALIDATION_SKIPPED](./api/zod/conformZodMessage.md#conformzodmessagevalidation_skipped)
+- `conform.VALIDATION_UNDEFINED` -> [conformZodMessage.VALIDATION_UNDEFINED](./api/zod/conformZodMessage.md#conformzodmessagevalidation_undefined)
+
+`conform.INTENT` はもはやエクスポートされていないことに注意してください。インテントボタンを設定する必要がある場合は、より良い型安全性のために zod の [z.discriminatedUnion()](https://zod.dev/?id=discriminated-unions) と組み合わせて、それを **intent** (または好みの何か)と名付けることができます。
+
+オプションに関してもいくつかの破壊的変更があります:
+
+- `getInputProps` における `type` オプションが現在必須になりました。
+
+```tsx
+
+```
+
+- `description` オプションは `ariaDescribedBy` に改名され、ブール値の代わりに文字列型( description 要素の `id` )になりました。
+
+```tsx
+
+```
+
+## フォーム設定の変更点
+
+まず、`form.props` が削除されました。代わりに [getFormProps()](./api/react/getFormProps.md) ヘルパーを使用できます。
+
+```tsx
+import { useForm, getFormProps } from '@conform-to/react';
+
+function Example() {
+ const [form] = useForm();
+
+ return ;
+}
+```
+
+`useFieldset` および `useFieldList` フックは削除されました。代わりにフィールドメタデータ上で `getFieldset()` または `getFieldList()` メソッドを呼び出すことができます。
+
+```tsx
+function Example() {
+ const [form, fields] = useForm();
+
+ // Before: useFieldset(form.ref, fields.address)
+ const address = fields.address.getFieldset();
+ // Before: useFieldList(form.ref, fields.tasks)
+ const tasks = fields.tasks.getFieldList();
+
+ return (
+
+ );
+}
+```
+
+`validate` と `list` のエクスポートは、フォームメタデータオブジェクトに統合されました:
+
+```tsx
+function Example() {
+ const [form, fields] = useForm();
+ const tasks = fields.tasks.getFieldList();
+
+ return (
+
+ );
+}
+```
+
+以下に、すべての同等のメソッドを示します:
+
+- `validate` -> `form.validate`
+- `list.insert` -> `form.insert`
+- `list.remove` -> `form.remove`
+- `list.reorder` -> `form.reorder`
+- `list.replace` -> `form.update`
+- `list.append` および `list.prepend` は削除されました。代わりに `form.insert` を使用できます。
+
+## スキーマ・インテグレーション
+
+混乱を避けるために、各統合における API を一意の名前に変更しました。こちらが同等のメソッドです:
+
+#### `@conform-to/zod`
+
+- `parse` -> [parseWithZod](./api/zod/parseWithZod.md)
+- `getFieldsetConstraint` -> [getZodConstraint](./api/zod/getZodConstraint.md)
+
+#### `@conform-to/yup`
+
+- `parse` -> [parseWithYup](./api/yup/parseWithYup.md)
+- `getFieldsetConstraint` -> [getYupConstraint](./api/yup/getYupConstraint.md)
+
+## 送信処理の改善
+
+セットアップを簡素化するために、送信オブジェクトを再設計しました。
+
+```tsx
+export async function action({ request }: ActionArgs) {
+ const formData = await request.formData();
+ const submission = parseWithZod(formData, { schema });
+
+ /**
+ * 送信ステータスは「success」、「error」、または undefined のいずれかになります。
+ * ステータスが undefined の場合、送信が準備されていないことを意味します(つまり、 intent が submit ではありません)。
+ */
+ if (submission.status !== 'success') {
+ return json(submission.reply(), {
+ // また、ステータスを使用してHTTPステータスコードを決定することもできます。
+ status: submission.status === 'error' ? 400 : 200,
+ });
+ }
+
+ const result = await save(submission.value);
+
+ if (!result.successful) {
+ return json(
+ submission.reply({
+ // `reply` メソッドに追加のエラーを渡すこともできます。
+ formErrors: ['Submission failed'],
+ fieldErrors: {
+ address: ['Address is invalid'],
+ },
+
+ // or avoid sending the the field value back to client by specifying the field names
+ hideFields: ['password'],
+ }),
+ );
+ }
+
+ // `resetForm` オプションを使用して送信に応答します。
+ return json(submission.reply({ resetForm: true }));
+}
+
+export default function Example() {
+ const lastResult = useActionData();
+ const [form, fields] = useForm({
+ // 混乱を避けるために、 `lastSubmission` は `lastResult` に改名されました。
+ lastResult,
+ });
+
+ // フォームメタデータからも送信のステータスを確認できるようになりました。
+ console.log(form.status); // "success", "error" or undefined
+}
+```
+
+## `useInputControl` フックを使用したインテグレーションがシンプルに
+
+`useInputEvent` フックは、いくつかの新機能を備えた [useInputControl](./api/react/useInputControl.md) フックに置き換えられました。
+
+- もはや input 要素の ref を提供する必要はありません。 DOM から入力要素を探し出し、見つからない場合は自動で挿入します。
+
+- カスタム input を制御された input として統合するために `control.value` を使用し、 `control.change(value)` を通じて値の状態を更新できるようになりました。フォームがリセットされると、値もリセットされます。
+
+```tsx
+import { useForm, useInputControl } from '@conform-to/react';
+import { CustomSelect } from './some-ui-library';
+
+function Example() {
+ const [form, fields] = useForm();
+ const control = useInputControl(fields.title);
+
+ return (
+ control.change(e.target.value)}
+ onFocus={control.focus}
+ onBlur={control.blur}
+ />
+ );
+}
+```
diff --git a/docs/ja/validation.md b/docs/ja/validation.md
new file mode 100644
index 00000000..5051d16a
--- /dev/null
+++ b/docs/ja/validation.md
@@ -0,0 +1,259 @@
+# バリデーション
+
+Conform は異なるバリデーションモードをサポートしています。このセクションでは、異なる要件に基づいてフォームをバリデーションする方法を説明します。
+
+## サーバーバリデーション
+
+フォームを**完全にサーバーサイドで**バリデーションすることができます。これはフォームの送信に限らず、ユーザーがタイピングしているときやフィールドを離れるときにも機能します。これにより、バリデーションロジックをクライアントバンドルから除外することができます。しかし、ユーザーがタイピングしている間にバリデーションを行いたい場合、ネットワークの遅延が懸念されるかもしれません。
+
+```tsx
+import { useForm } from '@conform-to/react';
+import { parseWithZod } from '@conform-to/zod';
+import { z } from 'zod';
+
+export async function action({ request }: ActionArgs) {
+ const formData = await request.formData();
+ const submission = parseWithZod(formData, {
+ schema: z.object({
+ email: z.string().email(),
+ message: z.string().max(100),
+ }),
+ });
+
+ if (submission.status !== 'success') {
+ return submission.reply();
+ }
+
+ return await signup(data);
+}
+
+export default function Signup() {
+ // サーバーによって返された最後の結果
+ const lastResult = useActionData();
+ const [form] = useForm({
+ // 最後の送信の結果を同期する
+ lastResult,
+
+ // 各フィールドをいつ検証するかを設定する
+ shouldValidate: 'onBlur',
+ shouldRevalidate: 'onInput',
+ });
+
+ // ...
+}
+```
+
+## クライアントバリデーション
+
+クライアントサイドでバリデーションロジックを再利用し、即時のフィードバックを提供することができます。
+
+```tsx
+import { useForm } from '@conform-to/react';
+import { parseWithZod } from '@conform-to/zod';
+
+// スキーマ定義をアクションの外に移動する
+const schema = z.object({
+ email: z.string().email(),
+ message: z.string().max(100),
+});
+
+export async function action({ request }: ActionArgs) {
+ const formData = await request.formData();
+ const submission = parseWithZod(formData, { schema });
+
+ // ...
+}
+
+export default function Signup() {
+ const lastResult = useActionData();
+ const [form] = useForm({
+ lastResult,
+ shouldValidate: 'onBlur',
+ shouldRevalidate: 'onInput',
+
+ // クライアント バリデーションの設定
+ onValidate({ formData }) {
+ return parseWithZod(formData, { schema });
+ },
+ });
+
+ // ...
+}
+```
+
+## 非同期バリデーション
+
+Conform は、少し異なる方法で非同期バリデーションをサポートしています。別のエンドポイントにリクエストを送る代わりに、必要に応じてサーバーバリデーションにフォールバックします。
+
+以下は、メールアドレスがユニークであるかを検証する例です。
+
+```tsx
+import { refine } from '@conform-to/zod';
+
+// スキーマを共有する代わりに、スキーマクリエーターを準備します。
+function createSchema(
+ options?: {
+ isEmailUnique: (email: string) => Promise;
+ },
+) {
+ return z
+ .object({
+ email: z
+ .string()
+ .email()
+ // メールアドレスが有効な場合にのみ実行されるようにスキーマをパイプします。
+ .pipe(
+ // 注意:ここでのコールバックは非同期にはできません。
+ // クライアント上でzodのバリデーションを同期的に実行するためです。
+ z.string().superRefine((email, ctx) => {
+ // これにより、バリデーションが定義されていないことを示すことで、
+ // Conformはサーバーバリデーションにフォールバックします。
+ if (typeof options?.isEmailUnique !== 'function') {
+ ctx.addIssue({
+ code: 'custom',
+ message: conformZodMessage.VALIDATION_UNDEFINED,
+ fatal: true,
+ });
+ return;
+ }
+
+ // ここに到達した場合、サーバー上でバリデーションが行われているはずです。
+ // 結果をプロミスとして返すことで、Zodに非同期であることを知らせます。
+ return options.isEmailUnique(email).then((isUnique) => {
+ if (!isUnique) {
+ ctx.addIssue({
+ code: 'custom',
+ message: 'Email is already used',
+ });
+ }
+ });
+ }),
+ ),
+ }),
+ // ...
+}
+
+export function action() {
+ const formData = await request.formData();
+ const submission = await parseWithZod(formData, {
+ // `isEmailUnique()` が実装された zod スキーマを作成します。
+ schema: createSchema({
+ async isEmailUnique(email) {
+ // ...
+ },
+ }),
+
+ // サーバー上で非同期バリデーションを有効にします。
+ // クライアントバリデーションは同期的でなければならないため、
+ // クライアントでは `async: true` を設定しません。
+ });
+
+ // ...
+}
+
+export default function Signup() {
+ const lastResult = useActionData();
+ const [form] = useForm({
+ lastResult,
+ onValidate({ formData }) {
+ return parseWithZod(formData, {
+ // isEmailUnique()を実装せずにスキーマを作成します。
+ schema: createSchema(),
+ });
+ },
+ });
+
+ // ...
+}
+```
+
+## バリデーションのスキップ
+
+スキーマはすべてのフィールドを一緒に検証します。これは、特に非同期バリデーションの場合、実行コストがかかることがあります。一つの解決策は、送信の意図をチェックすることにより、バリデーションを最小限に抑えることです。
+
+```tsx
+import { parseWithZod, conformZodMessage } from '@conform-to/zod';
+
+function createSchema(
+ // `intent` は `parseWithZod` ヘルパーによって提供されます。
+ intent: Intent | null,
+ options?: {
+ isEmailUnique: (email: string) => Promise;
+ },
+) {
+ return z
+ .object({
+ email: z
+ .string()
+ .email()
+ .pipe(
+ z.string().superRefine((email, ctx) => {
+ const isValidatingEmail =
+ intent === null ||
+ (intent.type === 'validate' && intent.payload.name === 'email');
+
+ // これによってバリデーションがスキップされたことを示すことで、
+ // Conformは前の結果を使用するようになります。
+ if (!isValidatingEmail) {
+ ctx.addIssue({
+ code: 'custom',
+ message: conformZodMessage.VALIDATION_SKIPPED,
+ });
+ return;
+ }
+
+ if (typeof options?.isEmailUnique !== 'function') {
+ ctx.addIssue({
+ code: 'custom',
+ message: conformZodMessage.VALIDATION_UNDEFINED,
+ fatal: true,
+ });
+ return;
+ }
+
+ return options.isEmailUnique(email).then((isUnique) => {
+ if (!isUnique) {
+ ctx.addIssue({
+ code: 'custom',
+ message: 'Email is already used',
+ });
+ }
+ });
+ }),
+ ),
+ }),
+ // ...
+}
+
+export async function action({ request }: ActionArgs) {
+ const formData = await request.formData();
+ const submission = await parseWithZod(formData, {
+ // Retrieve the intent by providing a function instead
+ schema: (intent) =>
+ createSchema(intent, {
+ async isEmailUnique(email) {
+ // ...
+ },
+ }),
+
+ async: true,
+ });
+
+ // ...
+}
+
+export default function Signup() {
+ const lastResult = useActionData();
+ const [form] = useForm({
+ lastResult,
+ onValidate({ formData }) {
+ return parseWithZod(formData, {
+ // 上記のアクションと同様です。
+ schema: (intent) => createSchema(intent),
+ });
+ },
+ });
+
+ // ...
+}
+```
diff --git a/guide/app/layout.tsx b/guide/app/layout.tsx
index 60195ab8..7a2c64aa 100644
--- a/guide/app/layout.tsx
+++ b/guide/app/layout.tsx
@@ -10,35 +10,38 @@ import { logo } from '~/util';
const menus: Menu[] = [
{
- title: 'Getting Started',
+ title: 'はじめに',
links: [
- { title: 'Overview', to: '/' },
- { title: 'Tutorial', to: '/tutorial' },
- { title: 'Upgrading to v1', to: '/upgrading-v1' },
+ { title: '概要', to: '/' },
+ { title: 'チュートリアル', to: '/tutorial' },
+ { title: 'v1 へのアップグレード', to: '/upgrading-v1' },
{ title: 'GitHub', to: 'https://github.com/edmundhung/conform' },
],
},
{
- title: 'Guides',
+ title: 'ガイド',
links: [
- { title: 'Validation', to: '/validation' },
- { title: 'Nested object and Array', to: '/complex-structures' },
- { title: 'Intent button', to: '/intent-button' },
- { title: 'Accessibility', to: '/accessibility' },
- { title: 'Checkbox and Radio Group', to: '/checkbox-and-radio-group' },
- { title: 'File Upload', to: '/file-upload' },
+ { title: 'バリデーション', to: '/validation' },
+ { title: 'ネストされたオブジェクトと配列', to: '/complex-structures' },
+ { title: 'インテントボタン', to: '/intent-button' },
+ { title: 'アクセシビリティ', to: '/accessibility' },
+ {
+ title: 'チェックボックスとラジオグループ',
+ to: '/checkbox-and-radio-group',
+ },
+ { title: 'ファイルのアップロード', to: '/file-upload' },
],
},
{
- title: 'Integration',
+ title: 'インテグレーション',
links: [
- { title: 'UI Libraries', to: '/integration/ui-libraries' },
+ { title: 'UI ライブラリ', to: '/integration/ui-libraries' },
{ title: 'Remix', to: '/integration/remix' },
{ title: 'Next.js', to: '/integration/nextjs' },
],
},
{
- title: 'API Reference',
+ title: 'API リファレンス',
links: [
{ title: 'useForm', to: '/api/react/useForm' },
{ title: 'useField', to: '/api/react/useField' },
@@ -49,7 +52,7 @@ const menus: Menu[] = [
],
},
{
- title: 'Utilities',
+ title: 'ユーティリティ',
links: [
{ title: 'getFormProps', to: '/api/react/getFormProps' },
{ title: 'getFieldsetProps', to: '/api/react/getFieldsetProps' },
@@ -60,7 +63,7 @@ const menus: Menu[] = [
],
},
{
- title: 'Schema related',
+ title: 'スキーマ関連',
links: [
{ title: 'parseWithZod', to: '/api/zod/parseWithZod' },
{ title: 'parseWithYup', to: '/api/yup/parseWithYup' },
@@ -83,7 +86,7 @@ export function Guide({
if (toc && toc.links.length > 0) {
sidemenus.push({
- title: 'On this page',
+ title: 'このページ',
links: toc.links.map((link) => ({
title: link.title,
to: `${location.pathname}${location.search}${link.to}`,
@@ -93,10 +96,10 @@ export function Guide({
if (file) {
sidemenus.push({
- title: 'Related links',
+ title: '関連リンク',
links: [
{
- title: 'Edit this page',
+ title: 'このページを編集',
to: `https://github.com/${owner}/${repo}/edit/${ref}/${file}`,
},
],
diff --git a/guide/app/routes/$.tsx b/guide/app/routes/$.tsx
index 7436f050..16533974 100644
--- a/guide/app/routes/$.tsx
+++ b/guide/app/routes/$.tsx
@@ -22,7 +22,7 @@ export const meta: MetaFunction = ({ data }) => {
};
export async function loader({ params, context }: LoaderFunctionArgs) {
- const file = `docs/${params['*']}.md`;
+ const file = `docs/ja/${params['*']}.md`;
const readme = await getFileContent(context, file);
const content = parse(readme);
diff --git a/guide/app/routes/_index.tsx b/guide/app/routes/_index.tsx
index f124e525..a0aa39b9 100644
--- a/guide/app/routes/_index.tsx
+++ b/guide/app/routes/_index.tsx
@@ -22,7 +22,7 @@ export const meta: MetaFunction = ({ data }) => {
};
export async function loader({ context }: LoaderFunctionArgs) {
- const file = 'docs/overview.md';
+ const file = 'docs/ja/overview.md';
const readme = await getFileContent(context, file);
const content = parse(readme);
diff --git a/guide/app/util.ts b/guide/app/util.ts
index a724a478..8ca44430 100644
--- a/guide/app/util.ts
+++ b/guide/app/util.ts
@@ -27,13 +27,23 @@ export function getGitHubToken(context: AppLoadContext): string | undefined {
}
export function getBranch(context: AppLoadContext): string {
- return context.env.CF_PAGES_BRANCH ?? 'main';
+ return context.env.CF_PAGES_BRANCH ?? 'ja';
}
export function getCache(context: AppLoadContext): KVNamespace {
return context.env.CACHE;
}
+// Japanese characters including UTF-8 are garbled, so convert to binary string before decoding
+function base64DecodeUtf8(base64String: string) {
+ var binaryString = atob(base64String);
+ var charCodeArray = Array.from(binaryString).map((char) =>
+ char.charCodeAt(0),
+ );
+ var uintArray = new Uint8Array(charCodeArray);
+ return new TextDecoder('utf-8').decode(uintArray);
+}
+
export async function getFileContent(
context: AppLoadContext,
path: string,
@@ -54,7 +64,7 @@ export async function getFileContent(
repo,
});
- content = atob(file.content);
+ content = base64DecodeUtf8(file.content);
context.waitUntil(
cache.put(cacheKey, content, {
expirationTtl: 3600,