npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@dropshot/i18n-toolkit

v0.2.0

Published

PO 카탈로그 파이프라인 CLI — 완성도 검사·초기 번역·초안 승격·원문 수정

Readme

@dropshot/i18n-toolkit

PO 카탈로그 파이프라인 CLI 2종(check · draft)이다.

pnpm add -D @dropshot/i18n-toolkit

카탈로그를 소유한 앱에 설치한다 — 워크플로우가 그 앱의 node_modules/.bin에서 CLI를 찾는다. @dropshot/i18n-po는 이 패키지가 이미 의존하므로 직접 쓰는 경우가 아니면 따로 넣지 않아도 된다.

CLI만으로는 반쪽이다. PR 게이트·초기 번역 백스톱을 쓰려면 워크플로우 2종을 함께 가져와야 한다.

경고 두 가지.

  1. npx를 쓰지 않는다. npm에 i18n-toolkit이라는 이름만 같은 무관한 패키지가 실재한다. (자세히)
  2. I18N_CATALOG_DIR는 쓸 거면 반드시 절대경로로. 상대경로를 주면 check·draft가 존재하지 않는 경로를 읽으려다 ENOENT로 죽는다. (자세히)

명령 표면

i18n-toolkit --help 출력 그대로다.

| 명령 | 동작 | |---|---| | check | 번역 완성도 검사 — 빈 msgstr가 있으면 실패(exit 1). fuzzy 항목은 경고만 하고 통과시킨다 | | draft --list [--csv] | 미번역 항목을 JSON(기본) 또는 CSV로 출력. CSV는 번역 시트 핸드오프용(BOM 포함) | | draft --apply <file> | [{key, translation}]을 fuzzy로 반영. 파일 확장자가 .csv면 CSV로, 그 외엔 JSON으로 읽는다 | | draft --claude | claude CLI로 초기 번역 생성 후 즉시 반영(#, fuzzy로 표시) |

인자 없이 실행하거나 알 수 없는 명령을 주면 위 사용법을 출력하고 exit 1. --help/-h는 exit 0.

package.json에 스크립트로 걸어두면 로컬에서도 같은 게이트를 돌릴 수 있다.

{
  "scripts": {
    "i18n:check": "i18n-toolkit check",
    "i18n:draft": "i18n-toolkit draft"
  }
}

경로 주입

| 대상 | 우선순위 | 기본값 | |---|---|---| | 카탈로그 디렉터리 | I18N_CATALOG_DIR > 기본값 | <cwd>/messages | | 초기 번역 명령(draft 전용) | I18N_DRAFT_CMD > 기본값 | claude -p (프롬프트는 stdin) | | 원본 로케일 | I18N_SOURCE_LOCALE > 기본값 | ko<카탈로그>/ko.po | | 번역 로케일 | I18N_TARGET_LOCALE > 기본값 | en<카탈로그>/en.po. 워크플로우 사용 시 주의 |

카탈로그 디렉터리를 지정하는 CLI 플래그는 없다 — 환경변수 아니면 기본값뿐이다. 상대경로는 모두 cwd 기준으로 해석된다(path.resolve(cwd, value)).

프로젝트 번역 가이드 주입

카탈로그 디렉터리에 translation-guide.md가 있으면 draft --claude가 그 전문을 프롬프트에 덧붙인다. 없으면 현행과 동일하게 동작한다.

  • 용어집·톤 가이드처럼 사람도 읽는 번역 문서를 그대로 쓴다 — 앱과 함께 버전관리되고, CI 설정이 필요 없으며, 변경이 PR 리뷰로 추적된다
  • 기본 프롬프트 교체는 지원하지 않는다. 구조 규칙(ICU 보존, JSON 배열만 출력)은 응답 파싱·검증과 한 몸인 계약이라, 가이드는 그 뒤에 덧붙기만 하고 출력 형식 지시가 항상 마지막에 온다 — 가이드가 출력 형식을 깨는 지시를 담아도 구조 규칙이 이긴다

워크플로우 설치

두 워크플로우는 패키지에 동봉하지 않는다 — GitHub Actions는 워크플로우가 저장소 루트의 .github/workflows/에 있어야 하고, node_modules 안의 파일을 읽지 않는다. 이 저장소에서 복사해 온다.

| 파일 | 역할 | |---|---| | i18n-check.yml | PR 게이트 | | draft-translate.yml | 초기 번역 백스톱 |

둘 다 workflow-level env: APP_DIR: apps/example 한 줄로 앱 경로를 모아 뒀다. 그 줄만 실제 앱 경로로 바꾼다.

I18N_TARGET_LOCALE을 기본값(en)이 아닌 값으로 쓸 거라면 한 곳 더: 두 워크플로우 모두 messages/en.po를 하드코딩한다(diff 검사와 git add). CLI는 <번역>.po에 쓰는데 워크플로우가 en.po만 보므로, 안 바꾸면 초기 번역이 조용히 버려지고 CI는 성공으로 끝난다. 워크플로우 안의 en.po를 실제 번역 로케일 파일명으로 함께 바꾼다.

draft-translate.yml은 추가로 on.push.paths: ['apps/example/messages/**']가 하드코딩돼 있다 — GitHub Actions는 트리거 조건에 표현식(${{ }})을 쓸 수 없으므로 APP_DIR와 별개로 함께 고쳐야 한다. 이 워크플로우에는 concurrency 블록도 있다(i18n-check.yml은 PR별로 독립 실행되므로 없다) — 지우면 같은 base를 대상으로 한 실행이 동시에 돌 때 서로 "열린 PR 없음"으로 판단해 각자 새로 시작한 뒤 서로의 커밋을 force-push로 덮어쓴다.

지우면 안 되는 것 둘

경고 1: npx를 쓰지 않는다

npm 공개 레지스트리에는 i18n-toolkit이라는 이름만 같은 무관한 패키지([email protected])가 실재한다. 이 패키지의 bin 이름이 i18n-toolkit이라 겹친다.

npx는 로컬에 bin이 없으면 레지스트리에서 내려받아 실행하므로, 의존 선언을 빠뜨린 repo의 CI가 에러 없이 조용히 남의 도구를 돌리게 된다. pnpm exec는 로컬 node_modules/.bin만 보고, 없으면 받아오지 않고 즉시 실패한다 — 그래서 워크플로우가 이쪽을 쓴다.

같은 이유로 설치는 반드시 스코프 이름(@dropshot/i18n-toolkit)으로 한다.

경고 2: I18N_CATALOG_DIR는 절대경로로만

워크플로우 스텝은 working-directory: ${{ env.APP_DIR }}에서 실행된다. resolveCatalogDir는 상대경로를 그 스텝의 cwd(즉 APP_DIR 안) 기준으로 다시 해석한다. repo 루트 기준 상대경로(apps/example/messages 같은 값)를 그대로 넣으면 APP_DIR 안에서 한 번 더 풀려 존재하지 않는 경로를 가리키게 된다.

check는 그 경로를 fs.readdirSync로, draft<원본>.po/<번역>.pofs.readFileSync로 읽으므로 잘못된 경로는 ENOENT로 즉시 죽는다 — CI가 빨간불로 실패하므로 알아채지 못할 일은 없지만, 에러 메시지만 보면 어느 경로가 잘못됐는지 바로 와닿지 않는다. 앱 트리 밖의 카탈로그를 가리키려는 경우가 아니면 애초에 이 값을 설정하지 않는 편이 안전하고, 설정한다면 반드시 절대경로를 쓴다.

전제

  • pnpm. 두 워크플로우 모두 pnpm/action-setup@v4를 쓰고, 버전은 워크플로우가 아니라 루트 package.jsonpackageManager 필드가 고정한다. npm/yarn을 쓰는 repo라면 워크플로우의 pnpm/action-setup 스텝과 packageManager 필드 두 곳을 함께 바꿔야 한다.
  • Node 22. 두 워크플로우 모두 actions/setup-nodenode-version: 22를 박아 뒀다.
  • 루트 pnpm test. i18n-check.yml의 마지막 두 스텝은 working-directory 지정 없이 repo 루트에서 돈다. 그중 pnpm test가 동작하려면 루트 package.jsontest 스크립트와 vitest devDependency, 그 스크립트가 읽는 vitest.config.ts가 있어야 한다 — 이건 CLI가 아니라 repo 루트 셋업이므로 패키지 설치로 따라오지 않는다. 루트 단위 vitest 스위트가 없다면 이 스텝을 지운다.
  • 루트 i18n:check 스크립트의 --filter 대상. 이 저장소의 루트 스크립트는 pnpm --filter @i18n/example i18n:check로 카탈로그를 소유한 패키지 이름을 그대로 박아 뒀다. 워크플로우를 복사해 온 repo에는 그 이름의 패키지가 없으므로 그대로 두면 --filter가 대상을 못 찾아 "No projects matched"로 실패한다 — 게이트가 깨진 것처럼 보이지만 실은 이 한 줄을 안 바꾼 탓이다. 앱 패키지 이름으로 바꾼다.

초기 번역 LLM 설정

I18N_DRAFT_CMD(저장소 변수) 또는 ANTHROPIC_API_KEY(시크릿) 중 하나라도 있으면 초기 번역이 돈다. 명령은 프롬프트를 stdin으로 받아 결과를 stdout으로 뱉기만 하면 되고, 셸을 거치지 않으므로 공백으로만 갈린다(따옴표 파싱 없음).

둘 다 없어도 게이트 자체는 동작하지만, 워크플로우마다 실패 방식이 다르다.

| 워크플로우 | 둘 다 없을 때 | |---|---| | i18n-check.yml(PR 게이트) | 초기 번역 스텝만 건너뛰고(exit 0) 이어서 진행 — 완성도 검사는 그대로 돈다 | | draft-translate.yml(백스톱) | 미번역이 있으면 그 스텝이 실패한다(exit 1) — 사람이 직접 부른 실행이라 조용히 넘기지 않는다 |

설정 없이 운영하려면 draft-translate.yml이 실패 상태로 쌓이는 것을 감수하거나, 그 워크플로우를 비활성화한다. 번역 자체는 draft --list--apply 경로로 계속 채울 수 있다.

부록: 복사(vendoring)로 도입하기

외부 패키지 반입이 막힌 저장소를 위한 대안이다. 기본 경로는 위의 npm 설치이고, 이쪽은 그게 불가능할 때만 쓴다.

| 경로 | 내용 | |---|---| | packages/po | PO 엔진 — 툴킷이 의존하므로 함께 간다 | | packages/toolkit | CLI 2종 + lib + bin | | .github/workflows/*.yml | 위와 동일하게 2종 |

  1. 두 패키지 디렉터리를 복사한다.
  2. 대상 repo가 pnpm workspace여야 하고, pnpm-workspace.yamlpackages 글롭이 복사해 넣은 위치를 포함해야 한다.
  3. 카탈로그를 소유한 앱의 package.json"@dropshot/i18n-toolkit": "workspace:*"를 넣는다.
  4. pnpm install로 링크한다.

3번을 건너뛰면 워크플로우가 Command not found로 실패한다. 워크플로우는 working-directory: $APP_DIR에서 pnpm exec i18n-toolkit ...으로 부르므로, 그 앱이 의존성을 가져야 로컬 bin을 찾는다.

복사본의 유일한 실질 비용은 upstream 개선을 손으로 반영해야 한다는 것이다. 복사 시점의 커밋 SHA를 대상 repo에 기록해두면 다음 반영 때 diff 범위가 분명해진다. 대상 repo에서 스크립트를 직접 고치는 대신, 이 저장소에 고치고 다시 복사하는 흐름을 유지한다.

라이선스

MIT