diff --git a/.github/workflows/translate-review.yml b/.github/workflows/translate-review.yml
new file mode 100644
index 000000000..f3714c42a
--- /dev/null
+++ b/.github/workflows/translate-review.yml
@@ -0,0 +1,243 @@
+name: Translation Review (GitHub Models)
+
+on:
+ pull_request:
+ types: [opened, synchronize]
+ paths:
+ - "src/content/**/*.mdx"
+
+permissions:
+ contents: read
+ pull-requests: write
+ models: read
+
+jobs:
+ review:
+ runs-on: ubuntu-latest
+ steps:
+ - name: Checkout repository
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+
+ - name: Get changed MDX files
+ id: changed
+ run: |
+ FILES=$(gh pr diff ${{ github.event.pull_request.number }} --name-only | grep '\.mdx$' || true)
+ if [ -z "$FILES" ]; then
+ echo "skip=true" >> "$GITHUB_OUTPUT"
+ echo "No MDX files changed."
+ else
+ echo "skip=false" >> "$GITHUB_OUTPUT"
+ echo "$FILES" > /tmp/changed-files.txt
+ fi
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Save PR diff to file
+ if: steps.changed.outputs.skip == 'false'
+ run: |
+ # 일반 diff (라인 번호 파악용)
+ gh pr diff ${{ github.event.pull_request.number }} > /tmp/pr-diff.txt
+
+ # word-diff (단어 단위 변경점 파악용)
+ # [-삭제된 단어-] {+추가된 단어+} 형식으로 표시
+ BASE_SHA=$(gh pr view ${{ github.event.pull_request.number }} --json baseRefName -q '.baseRefName')
+ git fetch origin "$BASE_SHA" --depth=1 2>/dev/null || true
+ git diff --word-diff origin/"$BASE_SHA"..HEAD -- 'src/content/**/*.mdx' > /tmp/pr-word-diff.txt || \
+ gh pr diff ${{ github.event.pull_request.number }} > /tmp/pr-word-diff.txt
+ env:
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+
+ - name: Review with GitHub Models
+ if: steps.changed.outputs.skip == 'false'
+ env:
+ GH_MODELS_TOKEN: ${{ secrets.GH_MODELS_TOKEN }}
+ GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ PR_NUMBER: ${{ github.event.pull_request.number }}
+ REPO: ${{ github.repository }}
+ HEAD_SHA: ${{ github.event.pull_request.head.sha }}
+ run: |
+ set -Eeuo pipefail
+
+ DIFF=$(cat /tmp/pr-diff.txt)
+ WORD_DIFF=$(cat /tmp/pr-word-diff.txt)
+ AGENTS=$(cat AGENTS.md)
+
+ read -r -d '' SYSTEM_PROMPT << 'SYSPROMPT' || true
+ 당신은 React Hook Form 한국어 번역 프로젝트의 번역 품질 리뷰어입니다.
+ 반드시 한국어로 응답하세요.
+
+ ## 번역 규칙
+ PLACEHOLDER_AGENTS
+
+ ## Word-Diff 읽는 법
+ - `[-삭제된 텍스트-]`: 이전 버전
+ - `{+추가된 텍스트+}`: 새 버전
+ - 예: `[-수동으로-]{+직접+}` = "수동으로"가 "직접"으로 변경됨
+
+ ## 리뷰 방법
+
+ **각 변경에 대해 다음을 분석하세요:**
+
+ 1. **변경 내용 나열**: word-diff에서 모든 `[-...-]{+...+}` 쌍을 찾아 나열
+ 2. **변경 이유 추론**: 왜 이렇게 변경했는지 파악
+ 3. **적절성 판단**:
+ - 번역 규칙에 맞는가?
+ - 더 자연스러운 한국어인가?
+ - 원래 의미를 잘 전달하는가?
+ 4. **결론**: 좋은 변경인지, 불필요한 변경인지, 오히려 나빠진 변경인지
+
+ ## 응답 형식 (JSON)
+
+ {
+ "summary": "전체 요약 (1-2문장)",
+ "comments": [
+ {
+ "path": "src/content/.../파일.mdx",
+ "line": 변경된 라인 번호 (숫자),
+ "body": "**변경 내용:**\n- `이전` → `이후`\n- `이전2` → `이후2`\n\n**평가:** 좋은/나쁜 변경인 이유를 구체적으로 설명"
+ }
+ ]
+ }
+
+ ## 코멘트 기준 (실제 리뷰어처럼)
+
+ **코멘트를 다는 경우:**
+ - 번역 규칙 위반 (용어표 미준수)
+ - 오역 또는 의미 왜곡
+ - 어색한 번역투
+ - 정말 잘한 변경 (구체적 이유와 함께 칭찬)
+
+ **코멘트를 달지 않는 경우:**
+ - 변경이 납득됨, 이해됨, 괜찮음 → 굳이 안 달아도 됨
+ - summary에서 간단히 언급하면 충분
+
+ ## 필수 규칙
+ - 코멘트의 body에는 반드시:
+ 1. 변경 내용 (`이전` → `이후` 형식)
+ 2. 왜 문제인지 / 왜 좋은지 구체적 이유
+ - "좋아 보입니다" 같은 애매한 표현 금지. 구체적 근거 필수.
+ - 워크플로우(.yml) 파일 변경은 무시하세요
+ - 문제 없으면 comments를 빈 배열 []로 하세요
+ SYSPROMPT
+
+ SYSTEM_PROMPT="${SYSTEM_PROMPT//PLACEHOLDER_AGENTS/$AGENTS}"
+
+ USER_MSG="다음 PR의 번역 변경사항을 리뷰하고 JSON 형식으로 응답하세요.
+
+ ## Word Diff (단어 단위 변경점)
+ [-삭제-]와 {+추가+} 표시를 확인하여 정확히 무엇이 변경되었는지 파악하세요.
+
+ \`\`\`diff
+ $WORD_DIFF
+ \`\`\`
+
+ ## 일반 Diff (라인 번호 참조용)
+ 인라인 코멘트의 line 번호는 이 diff를 참고하세요.
+
+ \`\`\`diff
+ $DIFF
+ \`\`\`"
+
+ PAYLOAD=$(jq -n \
+ --arg model "openai/gpt-4o" \
+ --arg system "$SYSTEM_PROMPT" \
+ --arg user "$USER_MSG" \
+ '{model: $model, messages: [
+ {role: "system", content: $system},
+ {role: "user", content: $user}
+ ], max_tokens: 4096, response_format: {type: "json_object"}}')
+
+ echo "Calling GitHub Models API..."
+
+ HTTP_RESPONSE=$(curl -s -w "\n%{http_code}" --max-time 120 \
+ "https://models.github.ai/inference/chat/completions" \
+ -H "Content-Type: application/json" \
+ -H "Authorization: Bearer $GH_MODELS_TOKEN" \
+ -d "$PAYLOAD")
+
+ HTTP_STATUS=$(echo "$HTTP_RESPONSE" | tail -n1)
+ RESPONSE=$(echo "$HTTP_RESPONSE" | sed '$d')
+
+ echo "HTTP Status: $HTTP_STATUS"
+
+ if [ "$HTTP_STATUS" != "200" ]; then
+ echo "API Error Response: $RESPONSE"
+ echo "::error::GitHub Models API returned status $HTTP_STATUS"
+ exit 1
+ fi
+
+ REVIEW_JSON=$(echo "$RESPONSE" | jq -r '.choices[0].message.content // empty')
+
+ if [ -z "$REVIEW_JSON" ]; then
+ echo "Response body: $RESPONSE"
+ echo "::error::No review content in response"
+ exit 1
+ fi
+
+ echo "Review JSON: $REVIEW_JSON"
+
+ INPUT_TOKENS=$(echo "$RESPONSE" | jq -r '.usage.prompt_tokens // 0')
+ OUTPUT_TOKENS=$(echo "$RESPONSE" | jq -r '.usage.completion_tokens // 0')
+ echo "Tokens used - input: $INPUT_TOKENS, output: $OUTPUT_TOKENS"
+
+ SUMMARY=$(echo "$REVIEW_JSON" | jq -r '.summary // "요약 없음"')
+ GOOD_POINTS=$(echo "$REVIEW_JSON" | jq -r '.good_points // ""')
+ COMMENTS_COUNT=$(echo "$REVIEW_JSON" | jq '.comments | length')
+
+ echo "Summary: $SUMMARY"
+ echo "Good points: $GOOD_POINTS"
+ echo "Comments count: $COMMENTS_COUNT"
+
+ if [ "$COMMENTS_COUNT" -gt 0 ]; then
+ echo "Creating PR review with inline comments..."
+
+ REVIEW_COMMENTS=$(echo "$REVIEW_JSON" | jq '[.comments[] | {path: .path, line: .line, body: .body}]')
+
+ REVIEW_BODY="## 번역 리뷰 (GitHub Models)
+
+ ### 요약
+ $SUMMARY
+
+ ### 잘된 점
+ ${GOOD_POINTS:-"특별히 언급할 사항 없음"}
+
+ ---
+ GPT-4o via GitHub Models | tokens: ${INPUT_TOKENS} in / ${OUTPUT_TOKENS} out | 인라인 코멘트: ${COMMENTS_COUNT}개"
+
+ REVIEW_PAYLOAD=$(jq -n \
+ --arg body "$REVIEW_BODY" \
+ --arg commit_id "$HEAD_SHA" \
+ --arg event "COMMENT" \
+ --argjson comments "$REVIEW_COMMENTS" \
+ '{body: $body, commit_id: $commit_id, event: $event, comments: $comments}')
+
+ echo "Submitting review..."
+ gh api \
+ --method POST \
+ -H "Accept: application/vnd.github+json" \
+ "/repos/$REPO/pulls/$PR_NUMBER/reviews" \
+ --input - <<< "$REVIEW_PAYLOAD"
+
+ else
+ echo "No inline comments, posting summary only..."
+
+ COMMENT_BODY="## 번역 리뷰 (GitHub Models)
+
+ ### 요약
+ $SUMMARY
+
+ ### 잘된 점
+ ${GOOD_POINTS:-"특별히 언급할 사항 없음"}
+
+ ### 수정 제안
+ 수정 제안 없음
+
+ ---
+ GPT-4o via GitHub Models | tokens: ${INPUT_TOKENS} in / ${OUTPUT_TOKENS} out"
+
+ gh pr comment "$PR_NUMBER" --body "$COMMENT_BODY"
+ fi
+
+ echo "Review posted successfully."
diff --git a/.github/workflows/trnaslate-review.yml b/.github/workflows/trnaslate-review.yml
deleted file mode 100644
index be05d0eba..000000000
--- a/.github/workflows/trnaslate-review.yml
+++ /dev/null
@@ -1,151 +0,0 @@
-name: Translation Review (GitHub Models)
-
-on:
- pull_request:
- types: [opened, synchronize]
- paths:
- - "src/content/**/*.mdx"
-
-permissions:
- contents: read
- pull-requests: write
- models: read
-
-jobs:
- review:
- runs-on: ubuntu-latest
- steps:
- - name: Checkout repository
- uses: actions/checkout@v4
- with:
- fetch-depth: 0
-
- - name: Get changed MDX files
- id: changed
- run: |
- FILES=$(gh pr diff ${{ github.event.pull_request.number }} --name-only | grep '\.mdx$' || true)
- if [ -z "$FILES" ]; then
- echo "skip=true" >> "$GITHUB_OUTPUT"
- echo "No MDX files changed."
- else
- echo "skip=false" >> "$GITHUB_OUTPUT"
- fi
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Save PR diff to file
- if: steps.changed.outputs.skip == 'false'
- run: |
- gh pr diff ${{ github.event.pull_request.number }} > /tmp/pr-diff.txt
- env:
- GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
-
- - name: Review with GitHub Models
- if: steps.changed.outputs.skip == 'false'
- uses: actions/github-script@v7
- env:
- GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- with:
- script: |
- const fs = require("fs");
-
- const diff = fs.readFileSync("/tmp/pr-diff.txt", "utf8");
- const agents = fs.readFileSync("AGENTS.md", "utf8");
- const reviewInstructions = fs.readFileSync(
- ".github/copilot-review-instructions.md",
- "utf8"
- );
-
- const systemPrompt = [
- "당신은 React Hook Form 한국어 번역 프로젝트의 코드 리뷰어입니다.",
- "반드시 한국어로 응답하세요.",
- "",
- "## 번역 규칙",
- agents,
- "",
- "## 리뷰 지침",
- reviewInstructions,
- "",
- "## 리뷰 형식",
- "",
- "PR diff를 분석하고 번역 품질을 리뷰하세요.",
- "다음 항목을 중심으로 검토합니다:",
- "",
- "1. **번역 용어 규칙 준수**: AGENTS.md의 번역 용어 표를 따르는지",
- "2. **자연스러운 한국어 표현**: 어색한 번역투가 없는지",
- "3. **기술적 정확성**: 원문의 의미가 정확히 전달되는지",
- "4. **문서 구조**: MDX frontmatter, 섹션 제목 규칙을 따르는지",
- "5. **코드 블록**: 코드 예제가 변경되지 않았는지 (주석 번역은 허용)",
- "",
- "리뷰 결과를 다음 형식으로 작성하세요:",
- "",
- "### 요약",
- "전체적인 번역 품질에 대한 간단한 평가 (1-2문장)",
- "",
- "### 수정 제안",
- "수정이 필요한 부분이 있다면 각각:",
- "- **파일**: 파일 경로",
- "- **위치**: 해당 내용",
- "- **현재**: 현재 번역",
- "- **제안**: 수정 제안",
- "- **이유**: 수정 이유",
- "",
- '수정할 부분이 없으면 "수정 제안 없음"이라고 작성하세요.',
- "",
- "### 잘된 점",
- "번역이 잘 된 부분이 있다면 간단히 언급하세요.",
- ].join("\n");
-
- const userMessage = "다음 PR diff를 리뷰해주세요:\n\n```diff\n" + diff + "\n```";
-
- const response = await fetch("https://models.github.ai/inference/chat/completions", {
- method: "POST",
- headers: {
- "Content-Type": "application/json",
- "Authorization": `Bearer ${process.env.GITHUB_TOKEN}`,
- },
- body: JSON.stringify({
- model: "openai/gpt-4o",
- messages: [
- { role: "system", content: systemPrompt },
- { role: "user", content: userMessage },
- ],
- max_tokens: 4096,
- }),
- });
-
- if (!response.ok) {
- const error = await response.text();
- core.setFailed(`GitHub Models API error: ${response.status} ${error}`);
- return;
- }
-
- const data = await response.json();
- const reviewText = data.choices?.[0]?.message?.content;
-
- if (!reviewText) {
- core.setFailed("No review text returned from GitHub Models.");
- return;
- }
-
- const usage = data.usage || {};
- const inputTokens = usage.prompt_tokens || 0;
- const outputTokens = usage.completion_tokens || 0;
- core.info(`Tokens used - input: ${inputTokens}, output: ${outputTokens}`);
-
- // Post review as PR comment
- await github.rest.issues.createComment({
- owner: context.repo.owner,
- repo: context.repo.repo,
- issue_number: context.payload.pull_request.number,
- body: [
- "## 번역 리뷰 (GitHub Models)",
- "",
- reviewText,
- "",
- "---",
- `GPT-4o via GitHub Models | tokens: ${inputTokens} in / ${outputTokens} out`,
- ].join("\n"),
- });
-
- core.info("Review comment posted successfully.");
diff --git a/src/content/docs/useform/reset.mdx b/src/content/docs/useform/reset.mdx
index bec0c18f3..b43fe316b 100644
--- a/src/content/docs/useform/reset.mdx
+++ b/src/content/docs/useform/reset.mdx
@@ -6,7 +6,7 @@ sidebar: apiLinks
## \> `reset:` `(values?: T | ResetAction, options?: Record) => void`
-전체 폼 상태, 필드 참조 및 구독을 초기화합니다. 선택적 인자가 있으며, 부분적인 폼 상태 초기화를 허용할 수 있습니다.
+전체 폼 상태, 필드 참조 및 구독을 초기화합니다. 선택적 인자를 통해 부분적인 폼 상태만 초기화할 수도 있습니다.
### Props
diff --git a/src/content/docs/useform/seterror.mdx b/src/content/docs/useform/seterror.mdx
index a06e1de9a..98271854a 100644
--- a/src/content/docs/useform/seterror.mdx
+++ b/src/content/docs/useform/seterror.mdx
@@ -6,7 +6,7 @@ sidebar: apiLinks
## \> `setError:` `(name: string, error: FieldError, { shouldFocus?: boolean }) => void`
-이 함수는 하나 이상의 에러를 수동으로 설정할 수 있도록 합니다.
+이 함수를 사용하면 하나 이상의 에러를 직접 설정할 수 있습니다.
### Props
diff --git a/src/content/docs/useform/trigger.mdx b/src/content/docs/useform/trigger.mdx
index 5198f4fd7..4384f02b8 100644
--- a/src/content/docs/useform/trigger.mdx
+++ b/src/content/docs/useform/trigger.mdx
@@ -6,7 +6,7 @@ sidebar: apiLinks
## `trigger:` `(name?: string | string[]) => Promise`
-폼 또는 인풋의 유효성 검사를 수동으로 트리거합니다. 이 메서드는 의존적인 유효성 검사가 있는 경우(입력 유효성 검사가 다른 입력 값에 의존하는 경우)에도 유용합니다.
+폼 또는 인풋의 유효성 검사를 직접 트리거합니다. 이 메서드는 의존적인 유효성 검사가 있을 때(한 입력의 유효성 검사가 다른 입력 값에 의존하는 경우) 유용합니다.
### Props