🔧 chore: 설치 스크립트에서 .gitignore 관련 코드 제거 및 주석 수정
This commit is contained in:
@@ -56,18 +56,13 @@ git submodule update --init --recursive
|
|||||||
bash .claude/common/scripts/install.sh https://git.sginfra.net/sgp-web-d/gameservice-fe-agent.git
|
bash .claude/common/scripts/install.sh https://git.sginfra.net/sgp-web-d/gameservice-fe-agent.git
|
||||||
```
|
```
|
||||||
|
|
||||||
> `install.sh`는 `.gitignore`에 `.claude/common/`를 자동으로 추가합니다.
|
|
||||||
> `.gitignore`가 없으면 새로 생성합니다.
|
|
||||||
|
|
||||||
설치 후 프로젝트 구조는 다음과 같이 됩니다.
|
설치 후 프로젝트 구조는 다음과 같이 됩니다.
|
||||||
|
|
||||||
```
|
```
|
||||||
your-project/
|
your-project/
|
||||||
├── .claude/
|
├── .claude/
|
||||||
│ ├── common/ ← submodule (gameservice-fe-agent)
|
│ ├── common/ ← submodule (gameservice-fe-agent)
|
||||||
│ ├── project/ ← 프로젝트 고유 지침
|
│ └── project/ ← 프로젝트 고유 지침
|
||||||
│ └── skills/ ← 공통 skill 심볼릭 링크
|
|
||||||
├── .gitignore ← .claude/common/ 자동 추가됨
|
|
||||||
├── CLAUDE.md ← 공통 + 프로젝트 지침을 @import
|
├── CLAUDE.md ← 공통 + 프로젝트 지침을 @import
|
||||||
└── ...
|
└── ...
|
||||||
```
|
```
|
||||||
@@ -114,7 +109,6 @@ bash .claude/common/scripts/update.sh
|
|||||||
```bash
|
```bash
|
||||||
bash .claude/common/scripts/install.sh https://git.sginfra.net/sgp-web-d/gameservice-fe-agent.git
|
bash .claude/common/scripts/install.sh https://git.sginfra.net/sgp-web-d/gameservice-fe-agent.git
|
||||||
# → .claude/common 에 submodule 설치
|
# → .claude/common 에 submodule 설치
|
||||||
# → .gitignore 에 .claude/common/ 추가
|
|
||||||
# → templates/project/*.md 를 .claude/project/ 로 복사
|
# → templates/project/*.md 를 .claude/project/ 로 복사
|
||||||
# → templates/CLAUDE.md.tpl 을 루트 CLAUDE.md 로 복사
|
# → templates/CLAUDE.md.tpl 을 루트 CLAUDE.md 로 복사
|
||||||
```
|
```
|
||||||
|
|||||||
@@ -43,22 +43,7 @@ else
|
|||||||
echo "✅ submodule 추가 완료: $TARGET_PATH"
|
echo "✅ submodule 추가 완료: $TARGET_PATH"
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 2) .gitignore 에 .claude/common 추가
|
# 2) 프로젝트 지침 양식 복사 (templates/project/ → .claude/project/)
|
||||||
GITIGNORE=".gitignore"
|
|
||||||
IGNORE_ENTRY=".claude/common/"
|
|
||||||
if [[ -f "$GITIGNORE" ]]; then
|
|
||||||
if grep -qF "$IGNORE_ENTRY" "$GITIGNORE" || grep -qF ".claude/common" "$GITIGNORE"; then
|
|
||||||
echo "ℹ️ .gitignore 에 '$IGNORE_ENTRY' 가 이미 있습니다. 건너뜁니다."
|
|
||||||
else
|
|
||||||
printf "\n# gameservice-fe-agent submodule\n%s\n" "$IGNORE_ENTRY" >> "$GITIGNORE"
|
|
||||||
echo "✅ .gitignore 에 '$IGNORE_ENTRY' 를 추가했습니다."
|
|
||||||
fi
|
|
||||||
else
|
|
||||||
printf "# gameservice-fe-agent submodule\n%s\n" "$IGNORE_ENTRY" > "$GITIGNORE"
|
|
||||||
echo "✅ .gitignore 를 생성하고 '$IGNORE_ENTRY' 를 추가했습니다."
|
|
||||||
fi
|
|
||||||
|
|
||||||
# 4) 프로젝트 지침 양식 복사 (templates/project/ → .claude/project/)
|
|
||||||
mkdir -p "$PROJECT_PATH"
|
mkdir -p "$PROJECT_PATH"
|
||||||
|
|
||||||
TEMPLATE_DIR="$TARGET_PATH/templates/project"
|
TEMPLATE_DIR="$TARGET_PATH/templates/project"
|
||||||
@@ -79,7 +64,7 @@ else
|
|||||||
echo "⚠️ $TEMPLATE_DIR 를 찾지 못했습니다. 공통 저장소의 templates 가 오래됐을 수 있습니다."
|
echo "⚠️ $TEMPLATE_DIR 를 찾지 못했습니다. 공통 저장소의 templates 가 오래됐을 수 있습니다."
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 5) 루트 CLAUDE.md 템플릿 복사
|
# 3) 루트 CLAUDE.md 템플릿 복사
|
||||||
if [[ ! -f "CLAUDE.md" ]]; then
|
if [[ ! -f "CLAUDE.md" ]]; then
|
||||||
TPL_FILE="$TARGET_PATH/templates/CLAUDE.md.tpl"
|
TPL_FILE="$TARGET_PATH/templates/CLAUDE.md.tpl"
|
||||||
if [[ -f "$TPL_FILE" ]]; then
|
if [[ -f "$TPL_FILE" ]]; then
|
||||||
@@ -112,7 +97,7 @@ else
|
|||||||
echo ""
|
echo ""
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 6) 공통 skill 심볼릭 링크 (.claude/common/skills/* → .claude/skills/*)
|
# 4) 공통 skill 심볼릭 링크 (.claude/common/skills/* → .claude/skills/*)
|
||||||
SKILLS_SRC="$TARGET_PATH/skills"
|
SKILLS_SRC="$TARGET_PATH/skills"
|
||||||
SKILLS_DEST=".claude/skills"
|
SKILLS_DEST=".claude/skills"
|
||||||
|
|
||||||
@@ -136,33 +121,6 @@ if [[ -d "$SKILLS_SRC" ]]; then
|
|||||||
done
|
done
|
||||||
fi
|
fi
|
||||||
|
|
||||||
# 7) pre-commit 훅: .claude/common 변경 커밋 방지
|
|
||||||
HOOK_DIR="$(git rev-parse --git-dir)/hooks"
|
|
||||||
HOOK_FILE="$HOOK_DIR/pre-commit"
|
|
||||||
HOOK_MARKER="# gameservice-fe-agent: block .claude/common commit"
|
|
||||||
|
|
||||||
mkdir -p "$HOOK_DIR"
|
|
||||||
|
|
||||||
if [[ -f "$HOOK_FILE" ]] && grep -qF "$HOOK_MARKER" "$HOOK_FILE"; then
|
|
||||||
echo "ℹ️ pre-commit 훅에 .claude/common 보호 규칙이 이미 있습니다. 건너뜁니다."
|
|
||||||
else
|
|
||||||
# 훅이 없으면 새로 생성, 있으면 기존 훅 뒤에 추가
|
|
||||||
if [[ ! -f "$HOOK_FILE" ]]; then
|
|
||||||
echo "#!/usr/bin/env bash" > "$HOOK_FILE"
|
|
||||||
fi
|
|
||||||
cat >> "$HOOK_FILE" <<'HOOK'
|
|
||||||
|
|
||||||
# gameservice-fe-agent: block .claude/common commit
|
|
||||||
if git diff --cached --name-only | grep -q "^\.claude/common"; then
|
|
||||||
echo "❌ .claude/common (gameservice-fe-agent submodule) 변경은 커밋할 수 없습니다."
|
|
||||||
echo " 공통 지침 수정은 gameservice-fe-agent 저장소에서 PR을 통해 진행해 주세요."
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
HOOK
|
|
||||||
chmod +x "$HOOK_FILE"
|
|
||||||
echo "✅ pre-commit 훅을 설치했습니다. (.claude/common 변경 커밋 방지)"
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo ""
|
echo ""
|
||||||
echo "🎉 설치가 완료되었습니다."
|
echo "🎉 설치가 완료되었습니다."
|
||||||
echo " - 공통 지침: $TARGET_PATH/CLAUDE.md"
|
echo " - 공통 지침: $TARGET_PATH/CLAUDE.md"
|
||||||
@@ -173,5 +131,5 @@ echo ""
|
|||||||
echo "다음 작업을 진행해 주세요:"
|
echo "다음 작업을 진행해 주세요:"
|
||||||
echo " 1) $PROJECT_PATH/*.md 내용을 프로젝트에 맞게 채우기"
|
echo " 1) $PROJECT_PATH/*.md 내용을 프로젝트에 맞게 채우기"
|
||||||
echo " 2) 변경 사항을 커밋하기"
|
echo " 2) 변경 사항을 커밋하기"
|
||||||
echo " git add .gitmodules .gitignore .claude CLAUDE.md"
|
echo " git add .gitmodules .claude CLAUDE.md"
|
||||||
echo " git commit -m 'chore: add gameservice-fe-agent submodule'"
|
echo " git commit -m 'chore: add gameservice-fe-agent submodule'"
|
||||||
|
|||||||
165
skills/project-init/SKILL.md
Normal file
165
skills/project-init/SKILL.md
Normal file
@@ -0,0 +1,165 @@
|
|||||||
|
---
|
||||||
|
name: project-init
|
||||||
|
description: >
|
||||||
|
현재 프로젝트를 분석하여 .claude/project/ 하위 문서(overview.md, conventions.md, architecture.md)를
|
||||||
|
실제 내용으로 채웁니다. 사용자가 "/init", "프로젝트 초기화", "프로젝트 문서 작성해줘" 등을
|
||||||
|
요청할 때 트리거됩니다.
|
||||||
|
---
|
||||||
|
|
||||||
|
# Project Init — 프로젝트 문서 자동 작성
|
||||||
|
|
||||||
|
이 skill 은 현재 Git 저장소를 탐색하여 `.claude/project/` 하위 세 파일을
|
||||||
|
실제 프로젝트 정보로 채웁니다.
|
||||||
|
|
||||||
|
## 언제 사용하는가
|
||||||
|
|
||||||
|
- `/init` 실행 시
|
||||||
|
- `.claude/project/*.md` 가 템플릿 상태(placeholder 가 남아 있음)일 때
|
||||||
|
- 새로운 프로젝트에 gameservice-fe-agent 를 처음 적용할 때
|
||||||
|
|
||||||
|
## 작업 순서
|
||||||
|
|
||||||
|
### 1. 환경 파악 (읽기 전용)
|
||||||
|
|
||||||
|
아래 파일들을 순서대로 읽어 프로젝트 정보를 수집한다.
|
||||||
|
파일이 없으면 건너뛴다.
|
||||||
|
|
||||||
|
| 파일 / 명령 | 수집 정보 |
|
||||||
|
|---|---|
|
||||||
|
| `package.json` | 프로젝트 이름, 의존성, 스크립트, 패키지 매니저 |
|
||||||
|
| `pnpm-lock.yaml` / `yarn.lock` / `package-lock.json` | 패키지 매니저 확정 |
|
||||||
|
| `nuxt.config.ts` / `next.config.*` / `vite.config.*` | 프레임워크, 모듈, 빌드 설정 |
|
||||||
|
| `tsconfig.json` | TypeScript strict 여부 |
|
||||||
|
| `tailwind.config.*` | CSS 프레임워크 |
|
||||||
|
| `.eslintrc.*` / `eslint.config.*` | 린터 설정 |
|
||||||
|
| `README.md` | 서비스 설명, 팀 정보 |
|
||||||
|
| `app/` / `src/` / `pages/` 디렉토리 구조 | 레이어, 주요 기능 |
|
||||||
|
| `server/api/` 또는 `src/api/` | 외부 의존성, API 패턴 |
|
||||||
|
| `stores/` / `composables/` | 상태 관리 패턴 |
|
||||||
|
|
||||||
|
### 2. 기존 문서 상태 확인
|
||||||
|
|
||||||
|
`.claude/project/overview.md`, `conventions.md`, `architecture.md` 를 읽어
|
||||||
|
이미 작성된 내용이 있으면 덮어쓰지 않고 **비어 있는 섹션만 채운다**.
|
||||||
|
|
||||||
|
- placeholder(`<...>`) 가 남아 있는 줄 → 채움 대상
|
||||||
|
- 실제 내용이 작성된 줄 → 유지
|
||||||
|
|
||||||
|
### 3. 사용자 확인 후 작성
|
||||||
|
|
||||||
|
탐색이 끝나면 아래 요약을 사용자에게 먼저 보여준다.
|
||||||
|
|
||||||
|
```
|
||||||
|
📋 감지된 프로젝트 정보
|
||||||
|
이름: <name>
|
||||||
|
프레임워크: <framework>
|
||||||
|
패키지 매니저: <pm>
|
||||||
|
언어: TypeScript (strict: <yes/no>)
|
||||||
|
CSS: <css>
|
||||||
|
상태관리: <state>
|
||||||
|
테스트: <test>
|
||||||
|
|
||||||
|
✍️ 아래 파일을 업데이트하려고 합니다:
|
||||||
|
- CLAUDE.md (제목 · 주요 명령어 · 주의사항)
|
||||||
|
- .claude/project/overview.md
|
||||||
|
- .claude/project/conventions.md
|
||||||
|
- .claude/project/architecture.md
|
||||||
|
|
||||||
|
진행할까요? (y/n)
|
||||||
|
```
|
||||||
|
|
||||||
|
사용자가 **y** 또는 별도 지시 없이 진행을 허락할 때만 파일을 수정한다.
|
||||||
|
|
||||||
|
### 4. 각 파일 작성 규칙
|
||||||
|
|
||||||
|
#### CLAUDE.md
|
||||||
|
|
||||||
|
루트 `CLAUDE.md` 는 Claude 가 대화 시작 시 **항상** 읽는 파일이므로, 매번 하위 파일까지 탐색하지 않아도 되는 핵심 정보만 간결하게 기재한다.
|
||||||
|
|
||||||
|
- **제목** (`# <프로젝트 이름>`): `package.json` 의 `name` 필드로 교체
|
||||||
|
예) `# epic7-esports` → `# Epic7 Esports E7WC`
|
||||||
|
`name` 이 slug 형태면 사람이 읽기 좋은 표기로 변환한다.
|
||||||
|
|
||||||
|
- **`## 주요 명령어`** 섹션 (필수 추가):
|
||||||
|
`package.json` 의 `scripts` 에서 아래 유형을 추출하여 그룹별로 정리한다.
|
||||||
|
주석은 스크립트 이름만으로 의도를 알기 어려울 때만 달고, 뻔한 것은 생략한다.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 주요 명령어
|
||||||
|
|
||||||
|
# 개발 서버
|
||||||
|
<dev 계열 스크립트>
|
||||||
|
|
||||||
|
# 빌드
|
||||||
|
<build 계열 스크립트>
|
||||||
|
|
||||||
|
# 린트 / 포맷
|
||||||
|
<lint 계열 스크립트>
|
||||||
|
```
|
||||||
|
|
||||||
|
- `dev`, `start`, `local` 등 → **개발 서버** 그룹
|
||||||
|
- `build`, `build:*` → **빌드** 그룹
|
||||||
|
- `lint`, `lint:*`, `format`, `prettier` → **린트 / 포맷** 그룹
|
||||||
|
- `test`, `test:*` → **테스트** 그룹 (존재할 때만)
|
||||||
|
- 나머지 잡다한 스크립트는 포함하지 않는다.
|
||||||
|
|
||||||
|
- **`## 주의사항`** 섹션 (선택 추가):
|
||||||
|
프로젝트 고유의 "함정"이 될 수 있는 항목을 2-4개 이내로 요약한다.
|
||||||
|
소스는 `conventions.md` 의 **금지 사항** 섹션 + CSS/API 탐색 결과.
|
||||||
|
없으면 이 섹션은 추가하지 않는다.
|
||||||
|
|
||||||
|
```markdown
|
||||||
|
## 주의사항
|
||||||
|
|
||||||
|
- CSS: <탐색 결과에 따라 기재. 예: Tailwind 아님, SCSS 사용 — Tailwind 클래스 금지>
|
||||||
|
- API: <API 호출 패턴 금지/허용 규칙>
|
||||||
|
- 환경: <.env 파일 분기 규칙>
|
||||||
|
```
|
||||||
|
|
||||||
|
- 이미 제목이 실제 이름으로 바뀌어 있거나 섹션이 존재하면 덮어쓰지 않고 유지한다.
|
||||||
|
|
||||||
|
#### overview.md
|
||||||
|
|
||||||
|
- **서비스 > 이름**: `package.json` 의 `name` 필드 사용
|
||||||
|
- **서비스 > 설명**: `README.md` 첫 단락 또는 `package.json` 의 `description`
|
||||||
|
- **기술 스택**: 탐색에서 수집한 실제 값으로 채움. 모르면 `<확인 필요>` 유지
|
||||||
|
- **주요 기능**: `pages/` 또는 `app/pages/` 디렉토리 목록을 기반으로 유추
|
||||||
|
- **참고 문서**: `README.md` 에 링크가 있으면 옮기고, 없으면 `<확인 필요>` 유지
|
||||||
|
|
||||||
|
#### conventions.md
|
||||||
|
|
||||||
|
- **디렉토리 규칙**: 실제 디렉토리 구조에서 확인된 폴더만 설명, 없는 항목은 삭제
|
||||||
|
- **API 호출 창구**: `composables/` 또는 `api/` 디렉토리 패턴에서 유추
|
||||||
|
- **테스트 러너 / 위치**: `package.json` devDependencies + `vitest.config.*` 참고
|
||||||
|
- 공통 지침과 다른 점이 없다면 "공통 지침을 따른다" 로 간단히 기재
|
||||||
|
|
||||||
|
#### architecture.md
|
||||||
|
|
||||||
|
- **레이어 구조**: 실제 디렉토리 목록으로 ASCII 다이어그램을 업데이트
|
||||||
|
- **상태 관리 가이드**: 탐색에서 확인한 라이브러리로 표의 권장 위치를 채움
|
||||||
|
- **외부 의존성**: `server/api/` 파일명 또는 `useFetch` 호출에서 유추. 불확실하면 `<확인 필요>`
|
||||||
|
|
||||||
|
### 5. 완료 안내
|
||||||
|
|
||||||
|
```
|
||||||
|
✅ 문서 업데이트 완료
|
||||||
|
|
||||||
|
변경된 파일:
|
||||||
|
- CLAUDE.md
|
||||||
|
- .claude/project/overview.md
|
||||||
|
- .claude/project/conventions.md
|
||||||
|
- .claude/project/architecture.md
|
||||||
|
|
||||||
|
다음 단계:
|
||||||
|
1) 각 파일에서 <확인 필요> 항목을 직접 채워주세요.
|
||||||
|
2) 변경 사항을 커밋해 주세요:
|
||||||
|
git add CLAUDE.md .claude/project/
|
||||||
|
git commit -m "docs: 프로젝트 초기 문서 작성"
|
||||||
|
```
|
||||||
|
|
||||||
|
## 주의사항
|
||||||
|
|
||||||
|
- **파일 삭제 금지**: 기존 `.claude/project/*.md` 는 내용을 수정할 뿐 절대 삭제하지 않는다.
|
||||||
|
- **추측 표시**: 확실하지 않은 정보는 `(추정)` 을 붙이고, 사용자가 쉽게 찾아 수정할 수 있게 한다.
|
||||||
|
- **최소 변경**: 이미 올바르게 작성된 섹션은 건드리지 않는다.
|
||||||
|
- **커밋 금지**: 파일 수정 후 git commit 은 사용자 명시 요청이 없으면 실행하지 않는다.
|
||||||
115
skills/translation-keys/SKILL.md
Normal file
115
skills/translation-keys/SKILL.md
Normal file
@@ -0,0 +1,115 @@
|
|||||||
|
---
|
||||||
|
name: translation-keys
|
||||||
|
description: 번역 요청 엑셀 파일의 EN 셀을 기반으로 '번역코드' 컬럼에 함축적인 코드를 자동 생성합니다. 사용자가 "번역코드 만들어줘", "translation key 생성", "번역 키 추출" 등을 요청하면 트리거됩니다.
|
||||||
|
---
|
||||||
|
|
||||||
|
# 번역 코드 생성 (Translation Key Generator)
|
||||||
|
|
||||||
|
이 skill 은 번역 요청 엑셀 파일의 **EN 컬럼** 텍스트를 분석하여
|
||||||
|
`번역코드` 컬럼에 함축적이고 일관된 코드를 자동으로 작성합니다.
|
||||||
|
|
||||||
|
## 코드 생성 규칙
|
||||||
|
|
||||||
|
| 규칙 | 설명 | 예시 |
|
||||||
|
|------|------|------|
|
||||||
|
| **단어 수** | 3단어 이하 | `NAV-main` ✅ / `NAV-main-section-title` ❌ |
|
||||||
|
| **구분자** | 단어 사이 `-` (하이픈) | `EVENT-title` |
|
||||||
|
| **첫 단어** | 카테고리를 나타내는 **대문자** | `HERO`, `NAV`, `TOAST` |
|
||||||
|
| **함축성** | 텍스트의 핵심 의미를 최대한 압축 | `HERO-czn-title` (CHAOS ZERO NIGHTMARE → czn) |
|
||||||
|
|
||||||
|
## 카테고리 가이드
|
||||||
|
|
||||||
|
| 카테고리 | 사용 상황 | 예시 |
|
||||||
|
|----------|----------|------|
|
||||||
|
| `BRAND` | 브랜드명, 서비스 이름 | `BRAND-smilegate-ax` |
|
||||||
|
| `NAV` | 내비게이션, 메뉴 항목 | `NAV-main`, `NAV-location` |
|
||||||
|
| `EVENT` | 이벤트 정보 (제목/날짜/장소) | `EVENT-title`, `EVENT-date`, `EVENT-venue` |
|
||||||
|
| `HERO` | 히어로 배너 텍스트 | `HERO-miresi-title` |
|
||||||
|
| `SECTION` | 섹션 헤더/소제목 | `SECTION-intro` |
|
||||||
|
| `BTN` | 버튼 레이블 | `BTN-register`, `BTN-more` |
|
||||||
|
| `TOAST` | 토스트/알림 메시지 | `TOAST-link-copied`, `TOAST-paste-hint` |
|
||||||
|
| `LABEL` | 폼 레이블, 태그 | `LABEL-date`, `LABEL-venue` |
|
||||||
|
| `MSG` | 일반 안내 메시지 | `MSG-loading`, `MSG-empty` |
|
||||||
|
| `MODAL` | 모달/팝업 내 텍스트 | `MODAL-confirm-title` |
|
||||||
|
|
||||||
|
## 작업 순서
|
||||||
|
|
||||||
|
1. **파일 확인**
|
||||||
|
- 사용자가 엑셀 파일 경로를 제공했는지 확인
|
||||||
|
- 미제공 시: 파일 경로를 먼저 요청
|
||||||
|
|
||||||
|
2. **데이터 파악**
|
||||||
|
```python
|
||||||
|
import openpyxl
|
||||||
|
wb = openpyxl.load_workbook('파일경로.xlsx')
|
||||||
|
ws = wb.active
|
||||||
|
# 헤더 행에서 '번역코드', 'EN' 컬럼 인덱스 찾기
|
||||||
|
```
|
||||||
|
- 1행(헤더)에서 `번역코드` 컬럼과 `EN` 컬럼 위치를 동적으로 탐지
|
||||||
|
- 데이터가 있는 행(EN 값이 None이 아닌 행)만 처리
|
||||||
|
|
||||||
|
3. **코드 생성**
|
||||||
|
- EN 텍스트를 분석해 카테고리와 핵심 키워드 추출
|
||||||
|
- 이미 `번역코드` 값이 있는 행은 **덮어쓰지 않음** (사용자 확인 후 진행)
|
||||||
|
- 생성한 코드 목록을 사용자에게 미리 보여주고 승인 요청
|
||||||
|
|
||||||
|
4. **중복 코드 검사** ← 저장 전 반드시 수행
|
||||||
|
- 기존에 이미 작성된 코드 + 이번에 새로 생성한 코드를 **전체 합산**하여 중복 여부 검사
|
||||||
|
- 중복이 발견되면 **저장을 중단**하고 사용자에게 아래 형식으로 보고:
|
||||||
|
```
|
||||||
|
⚠️ 중복 코드 발견
|
||||||
|
- HERO-title: 3행, 12행 (2건)
|
||||||
|
- EVENT-date: 8행, 15행 (2건)
|
||||||
|
```
|
||||||
|
- 중복 해소 방법을 제안하고 사용자 승인 후 재생성
|
||||||
|
- 중복이 없으면 "중복 없음 확인" 메시지 출력 후 저장 진행
|
||||||
|
|
||||||
|
5. **엑셀 저장**
|
||||||
|
- 사용자 승인 후 `번역코드` 컬럼에 값 기입 및 저장
|
||||||
|
- 저장 완료 메시지와 변경 내역 요약 출력
|
||||||
|
|
||||||
|
## 코드 작성 예시 (실제 케이스)
|
||||||
|
|
||||||
|
| EN 텍스트 | 생성 코드 | 근거 |
|
||||||
|
|-----------|----------|------|
|
||||||
|
| `Smilegate x AX` | `BRAND-smilegate-ax` | 브랜드명 전체 |
|
||||||
|
| `Main` | `NAV-main` | 내비 메인 항목 |
|
||||||
|
| `Program & Events` | `NAV-program-events` | 내비 항목, 특수문자 제거 |
|
||||||
|
| `Smilegate x Anime Expo` | `EVENT-title` | 이벤트 대표 제목 |
|
||||||
|
| `07/02/2026 (THU)~ 07/05/2026 (SUN)` | `EVENT-date` | 날짜 데이터 |
|
||||||
|
| `Los Angeles Convention Center` | `EVENT-venue` | 행사 장소 |
|
||||||
|
| `The link has been copied.` | `TOAST-link-copied` | 토스트 알림, 링크 복사 완료 |
|
||||||
|
| `You may now paste it elsewhere.` | `TOAST-paste-hint` | 토스트 알림, 붙여넣기 안내 |
|
||||||
|
| `MIRESI ARRIVES AT <br> ANIME EXPO!` | `HERO-miresi-title` | 히어로 배너, 캐릭터명 |
|
||||||
|
| `CHAOS ZERO NIGHTMARE ARRIVES AT <br> ANIME EXPO!` | `HERO-czn-title` | 히어로 배너, 약어 처리 |
|
||||||
|
|
||||||
|
## 처리 스크립트
|
||||||
|
|
||||||
|
스크립트 파일: `scripts/generate_translation_keys.py`
|
||||||
|
|
||||||
|
```bash
|
||||||
|
python scripts/generate_translation_keys.py <엑셀파일경로>
|
||||||
|
```
|
||||||
|
|
||||||
|
스크립트 주요 함수:
|
||||||
|
|
||||||
|
| 함수 | 역할 |
|
||||||
|
|------|------|
|
||||||
|
| `load_existing_codes(ws, code_col)` | 기존 번역 코드를 `{행번호: 코드}` 로 수집 |
|
||||||
|
| `check_duplicates(existing, new)` | 기존+신규 코드 합산 중복 검사, 결과 반환 |
|
||||||
|
| `run(filepath, new_codes)` | 중복 검사 → 저장 실행 (중복 시 중단) |
|
||||||
|
|
||||||
|
> `new_codes` 딕셔너리는 Claude AI가 EN 텍스트를 분석하여 직접 채웁니다.
|
||||||
|
> 스크립트는 중복 검사와 저장만 담당합니다.
|
||||||
|
|
||||||
|
## 주의사항
|
||||||
|
|
||||||
|
- **행 순서 고정**: 엑셀의 기존 행 순서를 절대 변경하지 않는다. 코드는 원래 행 위치에 그대로 기입한다.
|
||||||
|
- **이미지 셀 보존**: 셀에 이미지가 삽입된 경우 해당 셀 및 시트의 이미지를 덮어쓰거나 삭제하지 않는다. `openpyxl.load_workbook(path, keep_vba=True)` 옵션을 사용하고, 이미지 객체(`ws._images`)를 건드리지 않는다.
|
||||||
|
- **기존 코드 보존**: 이미 `번역코드` 값이 있는 셀은 덮어쓰지 않는다.
|
||||||
|
- **중복 코드 방지**: 동일한 코드가 두 행에 생기지 않도록 확인한다.
|
||||||
|
- **HTML 태그 무시**: `<br>`, `<b>` 등 마크업 태그는 의미 분석에서 제외한다.
|
||||||
|
- **날짜/숫자 데이터**: 날짜 형식 셀은 `EVENT-date`, `LABEL-date` 등 의미 기반으로 처리한다.
|
||||||
|
- **약어 처리**: 3단어 초과 시 캐릭터명·고유명사를 약어(초성/이니셜)로 압축한다.
|
||||||
|
- 예) `CHAOS ZERO NIGHTMARE` → `czn`
|
||||||
|
- 사용자가 코드를 직접 수정 요청하면 수정 후 재저장한다.
|
||||||
142
skills/translation-keys/scripts/generate_translation_keys.py
Normal file
142
skills/translation-keys/scripts/generate_translation_keys.py
Normal file
@@ -0,0 +1,142 @@
|
|||||||
|
"""
|
||||||
|
번역 코드 자동 생성 스크립트
|
||||||
|
사용법: python generate_translation_keys.py <엑셀파일경로>
|
||||||
|
|
||||||
|
주의:
|
||||||
|
- 행 순서를 변경하지 않는다 (원래 행 위치에만 값 기입)
|
||||||
|
- 셀에 삽입된 이미지를 보존한다 (ws._images 미수정)
|
||||||
|
"""
|
||||||
|
import sys
|
||||||
|
import openpyxl
|
||||||
|
|
||||||
|
|
||||||
|
def generate_translation_key(en_text: str) -> str:
|
||||||
|
"""
|
||||||
|
EN 텍스트를 분석하여 번역 코드를 생성합니다.
|
||||||
|
규칙:
|
||||||
|
- 3단어 이하 (CATEGORY-word1-word2)
|
||||||
|
- 단어 사이 '-' 구분
|
||||||
|
- 첫 단어는 카테고리 대문자
|
||||||
|
- 함축적인 의미로 생성
|
||||||
|
|
||||||
|
NOTE: 이 함수는 템플릿입니다.
|
||||||
|
실제 코드 생성은 Claude AI가 EN 텍스트의 의미를 분석하여 수행합니다.
|
||||||
|
"""
|
||||||
|
raise NotImplementedError("AI가 EN 텍스트를 분석하여 코드를 직접 생성합니다.")
|
||||||
|
|
||||||
|
|
||||||
|
def load_existing_codes(ws, code_col: int) -> dict[int, str]:
|
||||||
|
"""기존에 입력된 번역 코드를 {행번호: 코드} 형태로 반환합니다."""
|
||||||
|
return {
|
||||||
|
row: ws.cell(row=row, column=code_col).value
|
||||||
|
for row in range(2, ws.max_row + 1)
|
||||||
|
if ws.cell(row=row, column=code_col).value
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
def check_duplicates(existing: dict[int, str], new: dict[int, str]) -> dict[str, list[int]]:
|
||||||
|
"""
|
||||||
|
기존 코드 + 신규 코드 전체를 합산하여 중복 검사합니다.
|
||||||
|
반환: {중복코드: [행번호, ...]}
|
||||||
|
"""
|
||||||
|
seen: dict[str, int] = {}
|
||||||
|
duplicates: dict[str, list[int]] = {}
|
||||||
|
|
||||||
|
for row_num, code in {**existing, **new}.items():
|
||||||
|
if code in seen:
|
||||||
|
duplicates.setdefault(code, [seen[code]]).append(row_num)
|
||||||
|
else:
|
||||||
|
seen[code] = row_num
|
||||||
|
|
||||||
|
return duplicates
|
||||||
|
|
||||||
|
|
||||||
|
def run(filepath: str, new_codes: dict[int, str]) -> None:
|
||||||
|
"""
|
||||||
|
신규 코드를 중복 검사 후 엑셀에 저장합니다.
|
||||||
|
|
||||||
|
Args:
|
||||||
|
filepath: 엑셀 파일 경로
|
||||||
|
new_codes: {행번호: 생성된코드} 딕셔너리
|
||||||
|
"""
|
||||||
|
# keep_vba=True: 이미지·VBA 등 파일 내 임베딩 요소를 보존
|
||||||
|
wb = openpyxl.load_workbook(filepath, keep_vba=True)
|
||||||
|
ws = wb.active
|
||||||
|
|
||||||
|
# 이미지가 있는 셀 좌표를 미리 수집 (덮어쓰기 방지)
|
||||||
|
image_cells: set[tuple[int, int]] = set()
|
||||||
|
for img in getattr(ws, '_images', []):
|
||||||
|
anchor = img.anchor
|
||||||
|
if hasattr(anchor, '_from'):
|
||||||
|
image_cells.add((anchor._from.row + 1, anchor._from.col + 1)) # 1-based
|
||||||
|
|
||||||
|
# 헤더에서 컬럼 인덱스 탐지
|
||||||
|
headers = [cell.value for cell in ws[1]]
|
||||||
|
if '번역코드' not in headers or 'EN' not in headers:
|
||||||
|
print("❌ 헤더에 '번역코드' 또는 'EN' 컬럼이 없습니다.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
code_col = headers.index('번역코드') + 1 # 1-based
|
||||||
|
en_col = headers.index('EN') + 1 # 1-based
|
||||||
|
|
||||||
|
# 기존 코드 수집
|
||||||
|
existing_codes = load_existing_codes(ws, code_col)
|
||||||
|
|
||||||
|
# 빈 행 / 이미 코드 있는 행 필터링
|
||||||
|
rows_to_fill: list[tuple[int, str]] = []
|
||||||
|
for row in ws.iter_rows(min_row=2):
|
||||||
|
row_num = row[0].row
|
||||||
|
en_val = row[en_col - 1].value
|
||||||
|
code_val = row[code_col - 1].value
|
||||||
|
|
||||||
|
if not en_val:
|
||||||
|
continue # 빈 행 스킵
|
||||||
|
if code_val:
|
||||||
|
continue # 기존 코드 보존
|
||||||
|
if (row_num, code_col) in image_cells:
|
||||||
|
print(f" ⚠️ Row {row_num}: 이미지 셀 — 스킵 (보존)")
|
||||||
|
continue # 이미지 있는 셀 보존
|
||||||
|
|
||||||
|
if row_num in new_codes:
|
||||||
|
rows_to_fill.append((row_num, new_codes[row_num]))
|
||||||
|
|
||||||
|
if not rows_to_fill:
|
||||||
|
print("ℹ️ 입력할 신규 코드가 없습니다.")
|
||||||
|
return
|
||||||
|
|
||||||
|
# 중복 검사
|
||||||
|
new_codes_filtered = dict(rows_to_fill)
|
||||||
|
duplicates = check_duplicates(existing_codes, new_codes_filtered)
|
||||||
|
|
||||||
|
if duplicates:
|
||||||
|
print("⚠️ 중복 코드 발견 — 저장 중단")
|
||||||
|
for code, rows in duplicates.items():
|
||||||
|
print(f" - {code}: {', '.join(map(str, rows))}행 ({len(rows)}건)")
|
||||||
|
print("\n중복을 해소한 뒤 다시 실행해 주세요.")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# 중복 없음 → 저장
|
||||||
|
print("✅ 중복 없음 확인")
|
||||||
|
for row_num, code in rows_to_fill:
|
||||||
|
ws.cell(row=row_num, column=code_col, value=code)
|
||||||
|
en_text = ws.cell(row=row_num, column=en_col).value
|
||||||
|
print(f" Row {row_num}: {code:<30} ← \"{en_text}\"")
|
||||||
|
|
||||||
|
wb.save(filepath)
|
||||||
|
print(f"\n저장 완료: {filepath}")
|
||||||
|
|
||||||
|
|
||||||
|
if __name__ == '__main__':
|
||||||
|
if len(sys.argv) < 2:
|
||||||
|
print("사용법: python generate_translation_keys.py <엑셀파일경로>")
|
||||||
|
sys.exit(1)
|
||||||
|
|
||||||
|
# new_codes는 Claude AI가 EN 텍스트를 분석하여 채워줍니다.
|
||||||
|
# 예시:
|
||||||
|
# new_codes = {
|
||||||
|
# 2: 'BRAND-smilegate-ax',
|
||||||
|
# 3: 'NAV-main',
|
||||||
|
# }
|
||||||
|
new_codes: dict[int, str] = {}
|
||||||
|
|
||||||
|
run(sys.argv[1], new_codes)
|
||||||
Reference in New Issue
Block a user