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 🙏

© 2025 – Pkg Stats / Ryan Hefner

@channel.io/lokalise-mcp

v0.1.0

Published

Model Context Protocol server for Lokalise integration

Readme

Lokalise MCP

Channel 팀을 위한 Lokalise MCP 도구입니다. 이 도구는 Cursor에서 번역 키 생성 및 관리를 도와주는 AI 도구입니다.

Cursor MCP 설정

Cursor에서 MCP로 사용하려면 .cursor/mcp.json 파일에 다음과 같이 설정합니다:

{
  "mcpServers": {
    "lokalise-mcp": {
      "command": "npx -y @channel.io/lokalise-mcp",
      "env": {
        "LOKALISE_TOKEN": "your-lokalise-token",
        "PROJECT_ID": "your-project-id",
        "PLATFORMS": ["web", "ios", "android"]
      }
    }
  }
}

환경 변수 설명

  • LOKALISE_TOKEN: Lokalise API에 접근하기 위한 개인 액세스 토큰입니다. Lokalise 계정 설정에서 발급받을 수 있습니다.
  • PROJECT_ID: 작업할 Lokalise 프로젝트의 고유 식별자입니다. Lokalise 프로젝트 URL이나 설정에서 확인할 수 있습니다.
  • PLATFORMS: 번역 키가 지원할 플랫폼 목록입니다.

주요 기능

이 MCP 서버는 두 가지 주요 기능을 제공합니다:

  1. 번역 키 조회: 프로젝트에 이미 존재하는 번역 키를 검색합니다
  2. 번역 키 생성: 새로운 번역 키를 생성하고 초기 번역값을 설정합니다

함께 사용하면 좋은 Cursor Rule 예시

아래의 프롬프트를 사용하면 효과적으로 번역 키 추가 작업을 진행할 수 있습니다:

이 프롬프트는 ch-desk-web에서 사용하는 프롬프트입니다. 다른 프로젝트에서 사용하려면 프롬프트를 일부 수정해야 할 수 있습니다.

당신은 cursor 내에 통합된 LLM 에이전트입니다. 하드코딩된 한글 문자열에 대한 번역 키 추가 작업을 자동화하기 위해 아래 가이드라인을 준수하세요.


## 목적
- 코드베이스에서 하드코딩된 한글 문자열을 찾아 대응하는 번역 키를 생성 및 적용합니다.
- 기본적으로 <업무 파이프라인>을 따라 작업을 진행합니다.


## [!CRITICAL] 제약 조건
- <업무 파이프라인>의 단계를 반드시 지키도록
- 만약 <업무 파이프라인>의 특정 단계에서 작업 수행을 실패하면 그 즉시 실패 원인과 함께 응답 종료


## [!CRITICAL] 필수 참고 사항

### 명령어 정의
- '/completion'이란 현재 챗을 완료하고 사용자에게 추가 프롬프트를 요청할 때 사용하는 명령어입니다.

### MCP tool 주의 사항
- lokalise_create_keys를 통한 번역키 생성은 비가역적으로 진행되며, 이미 존재하는 키를 삭제하거나 수정할 수 없음
- lokalise_create_keys 전에 lokalise_get_keys를 통한 중복 확인 단계를 수행하지 않으면 중복 키 생성 시 오류가 발생할 수 있음

### 번역 키 네이밍 규칙
- snake_case.snake_case.snake_case 네이밍 사용
- 계층 구조 준수: 코드베이스의 구조를 참조하여, 위계와 맥락에 따라 번역키에 적절히 이름을 붙이기


## 업무 파이프라인

### 1. 작업 맥락 파악
- 사용자가 참조로 제공한 "작업 범위" 내에서 하드코딩된 한글 문자열 검색 (주석 처리된 문자열 제외)
- 번역이 필요한 페이지/컴포넌트의 구조와 문맥 파악

### 2. 번역 키:값 준비
- <번역 키 네이밍 규칙>에 따라 `{ "key": "value" }` 형식으로 번역 키:값 목록 준비 (`value`는 원본 한글)
- [!IMPORTANT] 특수 요소 처리:
  * 템플릿 리터럴(`${}`) → `import { sprintf } from 'sprintf-js'`에서 parse할 수 있는 형식으로 변환 (e.g. `"${name}님 안녕하세요"` → `"%s님 안녕하세요"`)
  * HTML 태그 → 번역 값에 직접 innerHTML로 처리 (e.g. `제 이름은 <b>라온</b>입니다.`)
  * 줄바꿈 등 특수 문자 → 적절히 이스케이프 시퀀스 활용

### 3. 번역 값 중복 확인
- 동일한 번역 값(`value`)을 가진 키가 있는지 확인
  - [!IMPORTANT] 리포지토리 루트에 있는 `l10n/ko.json` 파일에서 batch로 grep 하여 추출
- 중복이 없다면 최종 번역 키:값 목록 준비 후 다음 단계로 넘어감
- 중복 발견 시 옵션 제안:
  * 기존 키 재사용 (도메인 로직이 아니라 일반적인 번역 키인 경우)
  * 새 키 생성 (도메인 의존적인 번역 키인 경우)
- [!CRITICAL] 옵션에 대한 제안은 하되, 최종 결정은 사용자에게 물어볼 것 (/completion)
  - 가독성 있게 코드블록(json)으로 제안할 것
  - 사용자의 답변을 바탕으로 최종 `{ "key": "value" }` 형식의 번역 키:값 목록 준비

### 4. 번역 키 생성 전 품질 검증
- "작업 범위" 내 놓친 번역 키가 있는지 확인
- 이전 단계가 모두 정상적으로 수행되었는지 확인

### 5. 번역 키 생성 및 통합
- `lokalise_get_keys` 도구로 번역키 중복 확인
  - [!CRITICAL] 중복 키가 있는 경우:
    * 키에 version postfix를 붙여 중복 회피 (e.g. `meet.meet_banner.title` → `meet.meet_banner.title.v1`)
    * 사용자에게 다시 최종 결정을 물어볼 것 (/completion)
- `lokalise_create_keys` 도구로 키 생성
  - [!CRITICAL] 사용자에게 번역 키 태그를 한번도 받지 못한 경우 사용자에게 직접 요청할 것 (/completion)
- `yarn download-l10n` 스크립트 실행하여 번역 파일 갱신
- 코드 적용:
  * `import useTranslator from 'Hooks/useTranslator'` 훅을 사용한 번역 키 적용
  * 기본적으로 translate 함수를 사용
  * 템플릿 변수 처리해야하는 경우, 함수의 파라미터를 적절히 사용
  * innerHTML 처리해야하는 경우, translate 대신 translateWithInnerHtml 함수를 사용


## 번역 키:값 예시

### 일반적인 번역 키:값
```json
{
  "channel_settings.manage_webhooks.webhook_list.empty": "Webhook이 없습니다. 새 Webhook을 만들어 주세요."
  "channel_settings.manage_webhooks.show_webhook.modal.title": "새 Webhook이 만들어졌습니다."
  "channel_settings.manage_webhooks.show_webhook.modal.ok": "확인"
  "channel_settings.manage_webhooks.edit_webhook.modal.title": "Webhook 관리"
  "channel_settings.manage_webhooks.delete_webhook.success": "Webhook이 삭제되었습니다."
  "channel_settings.plugin_setting.button_customize": "버튼 커스터마이징"
  "channel_settings.plugin_setting.button_customize.reset": "초기화"
  "channel_settings.integrations.messengers.line.apply_modal.locale": "언어"
  "channel_settings.integrations.messengers.line.apply_modal.locale.description": "선택하신 언어에 따라 고객에게 부재중 메시지와 같은 자동 메시지가 전송됩니다."
  "channel_settings.integrations.messengers.line.apply_modal.channel_id": "라인 Channel ID"
  "channel_settings.integrations.messengers.line.apply_modal.channel_id.placeholder": "라인 Channel ID 입력"
  "channel_settings.integrations.messengers.line.apply_modal.channel_secret": "라인 Channel Secret"
  "channel_settings.integrations.messengers.line.apply_modal.channel_secret.placeholder": "라인 Channel Secret 입력"
  "channel_settings.integrations.messengers.line.apply_modal.access_token": "Access token\n(long-lived)"
  "channel_settings.integrations.messengers.line.apply_modal.access_token.placeholder": "Access token 입력"
  "channel_settings.integrations.messengers.line.apply_modal.id": "Basic ID (또는 Premium ID)"
  "channel_settings.integrations.messengers.line.apply_modal.id.placeholder": "Basic ID 또는 Premium ID를 입력"
  "channel_settings.integrations.messengers.line.webhook_link_modal.description": "아래 링크를 복사하여 라인 Channel의 Messaging API settings - Webhook URL에 입력하고 Webhook을 활성화하면 연동이 완료됩니다."
}
```

### with common prefix
```json
{
  "common.cancel": "취소"
  "common.confirm": "확인"
  "common.close": "닫기"
  "common.delete": "삭제"
  "common.save": "저장"
  "common.copy": "복사"
  "common.apply": "적용"
  "common.me": "나"
  "common.next": "다음"
  "common.retry": "재시도"
  "common.time": "시간"
  "common.no_data": "표시할 내용이 없습니다."
  "common.not_empty": "비워둘 수 없습니다"
}
```

### with error prefix
```json
{
  "error.do_not_empty": "비워둘 수 없습니다."
  "error.max_length": "%s자까지 입력할 수 있습니다."
  "error.available_english_number": "영문, 숫자, -, _만 사용할 수 있습니다."
  "error.aspect_ratio": "가로:세로 비율이 %s이 아닌 경우 등록할 수 없습니다."
  "error.min_width": "가로 %spx 이하일 경우 등록할 수 없습니다."
  "error.max_file_size": "최대 %s까지 등록할 수 있습니다."
  "error.content_type": "%s만 등록할 수 있습니다."
  "error.require_version_update.title": "앗, 이전 버전의 앱을 사용 중이에요"
  "error.unsupported_file_extension": "파일 형식을 다시 확인해주세요."
  "error.invalid_url": "잘못된 주소입니다."
}
```

### with innerHTML & escape sequence
```json
{
  "log_message.create_chat": "<b>%(subject)s<\/b>님이 <b>%(object)s<\/b>(을)를 만들었습니다."
  "log_message.invite_chat": "<b>%(subject)s<\/b>님이 <b>%(object)s<\/b>을(를) 초대했습니다."
  "common.powered_by": "<b>채널톡<\/b> 이용중"
  "channel.invite_recommend_description": "현재 채널에 <b>%s명<\/b>의 매니저가 있습니다. <b>매니저를 추가해도 추가 과금이 없으니<\/b> 안심하고 매니저를 초대하여 대화해보세요."
  "campaign_editor.ui.operating_panel.activated_description": "언제, 어떻게 메시지를 전송할지 설정합니다. '운영' 설정을 끝으로 메시지가 전송됩니다.<br>\n예) 서울 접속자 중 가입하고 2주 동안 구매가 없으면 <b><u>매주 월요일 아침 9시에<\/u><\/b> 전송",
  "campaign_editor.ui.delay_panel.activated_description": "아래 '대기 시간' 만큼 기다렸다가 다시 대상 고객을 체크하고 '운영' 단계로 넘어갑니다.<br>\n예) 서울 접속자 중 가입하고 <b><u>2주 동안<\/u><\/b> 구매가 없으면 매주 월요일 아침 9시에 전송"
}
```

### with template variable
```json
{
  "chat_action.manager_list.header": "%s명 참여"
  "confirm_delete_member": "%s님을 채널에서 삭제하시겠습니까?"
  "invitation.sns.message": "%s님이 '%s' 채널로 당신을 초대했습니다.\n채널에 참여해 주시기 바랍니다."
  "settings.billing.order_histories.discount_tooltip": "%s%% 할인 쿠폰 적용됨"
  "support_bot.max": "최대 %s개까지 추가할 수 있습니다."
  "bot.select.create_new_bot": "'%s' 새로 만들기..."
  "teamchats.participants_list.title": "참여자 ∙ %s"
  "expressive_query.values.format.plural": "%s 또는 %s개"
  "common.append_new_value": "'%s' 입력"
  "marketing.otm.success_modal.title_content": "%s명에게 메시지를 성공적으로 전달했습니다!👏"
  "user_profile_page.menu.copy_link.tooltip": "유저 링크 복사\n%s"
  "log_message.invite_chat": "<b>%(subject)s<\/b>님이 <b>%(object)s<\/b>을(를) 초대했습니다."
  "log_message.leave_chat": "<b>%(subject)s<\/b>님이 대화를 떠났습니다."
  "log_message.change_chat_name": "<b>%(subject)s<\/b>님이 그룹이름을 <b>%(object)s<\/b>(으)로 변경하였습니다."
  "notification.upload_file.description": "%(name)s 님이 파일을 업로드했습니다."
  "log_message.remove": "<b>%(subject)s<\/b>님이 상담을 삭제하였습니다."
  "log_message.close": "<b>%(subject)s<\/b>님이 상담을 종료하였습니다."
  "log_message.unhold": "<b>%(subject)s<\/b>님이 상담을 보류 해제 하였습니다."
}
```