Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ pnpm-debug.log*
lerna-debug.log*

node_modules
__pycache__/
*.pyc
dist
dist-ssr
*.local
Expand Down
4 changes: 4 additions & 0 deletions CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,10 @@ Pull Request를 제출하기 전에 다음 사항을 확인해주세요:
- [ ] `npm run build` 가 성공합니다
- [ ] 추가한 용어의 정의가 명확하고 정확합니다

## 번역 문서 사용 통계에 기여하기

용어 상세 페이지의 표기 출현 통계에 새 번역 문서 출처를 추가하려면 [사용 통계 문서](docs/usage-statistics/README.md)와 [출처 추가 절차](docs/usage-statistics/adding-source.md)를 참고해주세요. 사람과 에이전트가 동일한 집계·검증 기준을 따르도록 설계와 실행 절차를 함께 관리합니다.

## 로컬 개발 방법

프로젝트를 로컬에서 실행하여 변경사항을 확인할 수 있습니다:
Expand Down
156 changes: 156 additions & 0 deletions docs/usage-statistics/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# 번역 문서에서의 쓰임

용어 상세 페이지에 한국어 표기가 번역 문서에서 나타난 횟수와 확인 가능한 근거를 제공한다. 사전의 대표 번역·의미·동의어를 통계로 바꾸거나 추천 순위로 재정렬하지 않는다.

**새 출처를 추가하려면 [출처 추가 가이드](adding-source.md)를 먼저 읽는다.** 사람과 에이전트가 같은 절차를 사용한다. 저장소 전체의 에이전트 지침은 변경하지 않는다.

## 무엇을 세는가

통계 단위는 `출처 × 문서 × 영문 용어 항목 × 한국어 표기`다. 영문 용어는 사전 항목의 식별자이며, 실제 검색은 한국어 문자열로 한다. 영문 원문과 한국어 문장을 정렬하거나 문맥의 의미를 판별한 번역 빈도가 아니다.

예를 들어 `gradient`의 후보에 `경사`가 있으면 `경사하강법` 안의 `경사`도 센다. 같은 표기를 여러 사전 항목이 공유하면 각 항목에 독립적으로 집계한다. 따라서 모든 용어의 횟수를 합쳐 문서 전체의 고유 용어 수로 해석하면 안 된다. 문서 수 또한 표기 행끼리 더하지 않는다.

## 작은 정적 파이프라인

1. 출처 설정과 사전·추가 표기를 읽는다.
2. 선택한 출처의 로컬 Git 저장소에서 설정에 고정한 커밋을 읽는다.
3. 문서 목록·포함 조건·blob SHA를 확인한다. 변경된 본문만 다시 세고, 삭제 문서는 해당 출처의 새 상태에서 빠진다.
4. 각 출처의 호환되는 상태를 합쳐 공개 JSON과 스캔 목록을 만든다.
5. 사이트 빌드에서 데이터·해시·합계·근거를 검증한다. 상세 페이지에서만 공개 통계 JSON을 읽는다.

DB, 백엔드, 큐, 예약 실행, 크롤러는 없다. 집계 도구는 원격 fetch·commit·push·배포를 하지 않는다. 문서를 코드로 실행하지 않는다. Git에 추적되지 않은 파일과 체크아웃의 미커밋 변경도 입력으로 사용하지 않는다.

### 파일 역할

| 경로 | 역할 | 직접 편집 |
| --- | --- | --- |
| `data/*.json` | 기존 사전; 모든 의미의 한국어 번역·동의어가 기본 검색 후보 | 기존 사전 기여 절차 사용 |
| `usage/sources.json` | 출처 ID·커뮤니티·저장소·커밋·경로·어댑터·제외 조건 | 가능 |
| `usage/variants.json` | 추가 검색 표기, 미출현이어도 확인할 후보 목록 | 검토 후 가능 |
| `scripts/usage-statistics/usage_core.py` | 공통 Markdown 정제, 표기 매칭, 문서 캐시 갱신 | 규칙 변경 시 버전 관리 |
| `scripts/usage-statistics/update_usage_counts.py` | 출처 목록 확인, 선택 집계, 합산·출력 | 새 형식이 필요할 때만 확장 |
| `usage/state/<source-id>.json` | 출처별 문서 횟수·첫 근거·해시·커밋 | 생성 파일; 숫자 수기 수정 금지 |
| `public/usage/term-usage.json` | 상세 페이지용 합산 결과 | 생성 파일 |
| `public/usage/scanned.md` | 포함·제외 문서, 사유, 커밋, 집계 시각 | 생성 파일 |
| `scripts/usage-statistics/validate-usage-data.mjs` | Python/원문 저장소 없이 빌드 결과의 정합성 검사 | 스키마 변경 시 함께 수정 |

상태 파일을 출처별로 나눈 이유는 다른 커뮤니티의 원문 저장소 없이 자신의 출처만 갱신하고 검토하기 위해서다. 공개 합산 파일은 하나로 유지한다. 용량이 실제 문제가 되기 전에는 DB나 별도 배포 서비스로 확장하지 않는다.

### 커뮤니티와 출처의 화면 표시

통계 영역은 용어 상세 페이지의 **한 표**로 유지한다. PyTorch와 Hugging Face KREW를 별도 표·탭·페이지로 나누지 않는다.

- `community`: 참여 커뮤니티 표시 이름. 현재 HF 출처의 값은 `Hugging Face KREW`이며 새 PyTorch 출처는 `PyTorch`로 통일한다. **해당 용어의 출현 근거가 있는 출처**의 커뮤니티만 중복 제거해 영역 상단에 ` · `로 이어 표시한다. 기준은 출처 상태 `collected`와 해당 용어의 `bySource[id].documentCount > 0`이다. HF 근거만 있으면 HF만, PyTorch 근거만 있으면 PyTorch만, 둘 다 있으면 두 이름을 표시한다. 어디에도 근거가 없으면 이름 영역을 숨긴다.
- `label`: 문서 출처 컬럼 이름. 현재 `Transformers`, `smolagents`, `HF Blog` 옆에 새 출처의 label(예: `PyTorch Tutorials`)이 자동 추가된다. 표시 순서는 공개 JSON의 출처 순서이며 생성기는 설정 배열 순서를 유지한다.
- `id`: 캐시·숫자·근거를 연결하는 영구 키. 표시 이름이 아니므로 이름을 바꾸려고 ID를 변경하지 않는다.

커뮤니티 이름과 출처 컬럼은 모두 데이터에서 생성한다. 현재 2단 그룹 헤더나 커뮤니티별 소계는 없으며, 단순 출처 추가에 이를 구현할 필요는 없다. 전체 합계는 수집된 모든 출처의 합이다. 출처 간 같은 문서의 중복은 자동 제거하지 않으므로 중복 코퍼스를 등록하지 않는다.

미등록 커뮤니티의 이름이나 가상 통계는 표시하지 않는다. 설정만 등록하고 아직 수집하지 않은 출처는 컬럼에 `—`가 표시되지만 상단 커뮤니티 이름에는 포함하지 않는다. 이름 필터는 출처 컬럼·집계 범위 설명·전체 합계에 영향을 주지 않는다. 현재 PyTorch 출처는 미등록이며 실제 입력을 확인한 뒤 추가한다. 포함 문서가 전부 제외된 수집 출처는 현재 출처별 셀에 0이 나올 수 있으므로 아래 범위 검증 없이 미출현으로 해석하지 않는다.

## 집계 규칙: `ko-surface-v2.1`

- 파서: `markdown-it-py==3.0.0`, CommonMark와 표 지원. 현재 입력 형식은 `.md`다. `.rst`, `.mdx`, 노트북, HTML을 이 파서에 억지로 넣지 않는다.
- 포함: 제목, 문단, 목록, 인용문, 표 셀의 텍스트와 링크 표시 문구.
- 제외: frontmatter, fenced/indented/inline 코드, HTML 주석, 이미지·이미지 대체 텍스트, URL, raw HTML 블록, 자동 문서 앵커/API 지시문. 임의 HTML/MDX를 실행하지 않는다.
- 정규화: Unicode NFC, 소문자화, 연속 공백 축약. 띄어쓰기와 하이픈을 임의로 없애지 않는다.
- 매칭: 부분 문자열 검색. 같은 용어 안에서는 왼쪽부터 찾고, 같은 시작 위치에서는 긴 후보를 먼저 선택해 겹침을 막는다. 다른 용어 항목끼리는 독립적이다.
- 경계: 서로 다른 문단·표 셀·제외된 인라인 코드의 양쪽을 합쳐 가짜 표기를 만들지 않는다.
- 후보: 모든 `meanings[].korean`, `synonyms[]`, `usage/variants.json`의 `extraVariants`. 정규화 후 중복을 제거한다. 추가 후보는 사전의 권장 번역에 자동 등록되지 않는다.
- 한글 음절이 없는 영문·약어 후보는 `unsupportedVariants`로 구분하며, 0회라고 표시하지 않는다.
- 근거: 문서·표기별 전체 횟수와 **첫 출현** 주변 문맥만 저장한다. GitHub 링크의 행 범위는 해당 문단·표 영역이지 정확한 문자 위치가 아니다. 문맥은 검색에 사용한 정규화 텍스트다.

정제·검색 방식이 달라지면 `usage_core.py`의 `RULE`을 올리고 테스트 및 전체 출처를 재집계한다. 서로 다른 집계 규칙의 숫자를 같은 표에서 합하지 않는다. 원문 형식 지원을 추가할 때도 동일한 원칙을 따른다.

### 포함 범위와 어댑터

출처별로 포함 범위를 명시한다. 현재 어댑터는 다음 둘이다.

- `paired-markdown`: 번역 root 아래 `.md`를 찾고, 같은 상대 경로의 영문 일반 파일이 있는지 확인한다. 원문과 번역이 서로 다른 Git 저장소여도 된다. symlink는 따라가지 않는다.
- `krew-blog`: KREW의 `_posts` 규칙을 사용한다. 공식 HF 블로그 원문 연결, 번역 고지, 영문 파일을 확인하고 `translation_status: draft`를 제외한다. 누락된 상태 필드는 기존 정책대로 게시본으로 취급한다.

두 어댑터 모두 `exclude`에 매칭되는 문서를 제외 사유와 함께 기록한다. glob은 **저장소 기준 전체 경로에 대한 Python `fnmatchcase`**이며 `*`가 `/`도 매칭한다. Gitignore 패턴 문법이 아니다. 폴더에 모든 `.md`가 없어지거나 경로가 잘못되면 집계가 실패한다. 전체 코퍼스 제거는 설정·상태 제거를 명시적으로 리뷰하는 별도 작업이다.

영문 대응의 존재는 번역 코퍼스를 정하는 조건이지 문장별 번역 정확성의 증명이 아니다. PyTorch의 실제 저장소 구조·형식을 확인하기 전에는 같은 경로나 어댑터를 사용할 수 있다고 가정하지 않는다.

## 데이터 계약: schemaVersion 2

### 출처별 상태

`usage/state/<id>.json`에는 다음을 저장한다.

- `source`: 출처 설정, 한국어 및 영문 원본의 정확한 커밋.
- `documents`: `<source-id>:<repository-relative-path>`를 키로 한 문서별 기록.
- 문서 기록: `blobSha`, `eligible`, `reason`, `enPath`, `countedAt`, `counts[term][spelling]`, `evidence[term][spelling]`.
- `candidateHash`, `countingRuleVersion`, `policyHash`, `configHash`: 캐시 사용 및 출처 간 합산의 호환성 기준.
- `inputHash`: 커밋·목록·후보·포함 정책의 동일성. `snapshotId`: 상태 전체의 무결성 해시.

파일 수정 시각은 변경 감지에 사용하지 않는다. 커밋을 이동해도 본문 blob과 포함 조건이 같으면 문서별 결과를 재사용한다. `generatedAt`은 해당 출처 스냅샷의 생성 시각이고, `countedAt`은 각 본문을 마지막으로 실제 센 시각이다. 어느 것도 원문 작성·번역 날짜나 최신성 보증을 의미하지 않는다.

### 공개 결과

`term-usage.json`은 다음을 포함한다.

- `sources[id]`: 동적으로 UI에 표시할 이름·커뮤니티·커밋·집계 시점·상태.
- `corpus[id]`: 스캔·포함 문서 수.
- `terms[term]`: 표기별 횟수·중복 제거 문서 수·출처별 합계·문서별 첫 근거.
- `snapshotId`: 설정·검색 후보·출처별 스냅샷 ID를 묶은 식별자.

상태는 다음처럼 구분한다.

- `sources[id].status == not-collected`: 아직 상태 파일이 없는 출처. 해당 출처 수치는 `null`, UI는 `—`.
- 용어 `status == no-match`: 포함된 문서가 있고 지원하는 표기를 검색했으나 출현이 없음.
- 용어 `status == not-collected`: 지원 표기는 있으나 어느 출처에도 집계에 포함된 문서가 없음. 모든 문서가 제외된 경우도 해당한다.
- `unsupported`: 검색 가능한 한글 표기가 없음.
- 읽기·검증 실패: 결과를 0회나 미수집으로 덮지 않고 실행 실패로 처리한다. UI의 네트워크 오류도 미출현과 구분한다.

전체 횟수는 **집계된 출처만의 합계**다. 미수집 출처가 있거나 출처별 시점이 다르면 완전한 동시점 통계가 아니다. 출현한 용어는 항상 표시하고, 미출현 항목은 `showWhenUnmatched` 목록에 있는 것만 표시한다. 이 목록은 초기 HF 후보의 리뷰 경험을 보존하기 위한 표시 정책이며 집계 횟수를 바꾸지 않는다.

## 재현·갱신

기본 사이트 빌드에는 Node 의존성과 커밋된 상태·공개 JSON만 필요하다. Python과 문서 체크아웃은 재집계할 때만 필요하다.

```bash
python3 -m pip install -r scripts/usage-statistics/requirements.txt
npm run update:usage -- --sources-dir /path/to/document-checkouts
npm run test:usage
python3 scripts/usage-statistics/update_usage_counts.py --check-full --sources-dir /path/to/document-checkouts
npm run build
```

특정 출처만 갱신하려면:

```bash
npm run update:usage -- --source transformers --sources-dir /path/to/document-checkouts
python3 scripts/usage-statistics/update_usage_counts.py --source transformers --check-full --sources-dir /path/to/document-checkouts
```

`--source`는 여러 번 지정할 수 있다. 설정한 커밋이 로컬 저장소에 있어야 한다. 스크립트가 최신 main을 가져오거나 임의로 추적하지 않으므로, 최신화는 작성자가 커밋을 선택하고 `sources.json`의 `ref`를 변경하는 별도 단계다.

원문 없이 기존 상태만 합산하려면:

```bash
npm run update:usage -- --aggregate-only
npm run validate:usage
```

### 갱신 시 보장과 제한

- 새 문서·수정 문서는 집계, 삭제는 제거, 이동은 삭제+추가로 처리한다. 마지막 합계는 남은 문서별 결과를 다시 더한다.
- 선택하지 않은 출처는 로컬 문서 저장소를 열지 않고 커밋된 상태를 재사용한다.
- 후보 또는 공통 규칙이 바뀌면 **이미 수집된 모든 출처**를 같은 새 기준으로 재집계해야 한다. 일부만 갱신해 나머지가 오래된 경우 저장 전에 실패한다. 이 경우 `--aggregate-only`로 우회할 수 없다.
- 새 출처는 상태가 없어도 기존 숫자를 보존하고 미수집으로 등록할 수 있다. 출처 설정을 삭제하면 그 출처는 합계에서 제외된다. 삭제는 의도적인 코퍼스 변경이므로 관련 상태 파일도 PR에서 정리한다.
- 동일 입력은 파일·해시·집계 시각을 바꾸지 않는다. `--check-full`은 선택한 출처를 캐시 없이 다시 세어 비교하고 파일을 쓰지 않는다. 선택하지 않은 출처는 정합성만 확인하며 전체 재스캔했다고 주장하지 않는다.
- `--check-full`은 설정한 범위의 재현성 검사이며 범위의 타당성을 보장하지 않는다. 모든 영문 대응 누락 등으로 포함 문서가 0개여도 성공할 수 있다. 게시 전에 [출처 추가 가이드의 범위 검증](adding-source.md#5-검증-체크리스트)으로 포함 수·제외 사유를 반드시 확인하고 예상하지 못한 전체 제외나 급감은 중단한다.
- 계산 및 검증 성공 후 임시 파일을 교체한다. **파일 하나씩은 원자적이지만 전체 파일 묶음의 교체는 트랜잭션이 아니다.** 중단되면 집계 명령을 재실행하고 빌드 검사로 일관성을 확인한다. 출력 파일을 쓰는 프로세스는 체크아웃당 하나만 실행한다.
- 다수가 작업할 때는 별도 브랜치·체크아웃을 사용한다. 출처별 상태를 먼저 합치고 공개 JSON은 `--aggregate-only`로 재생성한다. 거대한 생성 파일의 줄을 수동 병합하지 않는다.

## 초기 HF 스냅샷 검증

현재 등록 출처는 Transformers·smolagents·HF Blog이며 **PyTorch 문서는 아직 등록·집계하지 않았다.** 2026-09-06에 모아 둔 고정 커밋을 사용했다. 출처 공통화 후 기존 로컬 Step 2와 모든 용어별 횟수·문서 수·첫 출현 근거가 같음을 확인했다.

- 사전 263개, 출현 확인 194개.
- 스캔 254개, 포함 205개: Transformers 173/186, smolagents 17/17, HF Blog 15/51.
- 전체 재집계와 캐시 결과 비교, 동일 입력 재실행, 임의의 커뮤니티 ID 및 출처 단독 갱신을 검증한다.

이 수치는 범위·규칙이 달랐던 초기 후보 채집 통계와 증감을 직접 비교하지 않는다. 정의·번역 추천과 표기 빈도 통계를 분리해서 리뷰한다.
Loading