From 55d6fd3bee06256c3679aede43136dd16155c07a Mon Sep 17 00:00:00 2001 From: syskiller Date: Wed, 26 Aug 2026 09:19:09 +0900 Subject: [PATCH] =?UTF-8?q?docs:=20README=EB=A5=BC=20=ED=95=9C=EA=B5=AD?= =?UTF-8?q?=EC=96=B4=EB=A1=9C=20=EC=A0=84=EB=A9=B4=20=EC=9E=AC=EC=9E=91?= =?UTF-8?q?=EC=84=B1,=20=EA=B2=AA=EC=9D=80=20=EB=AC=B8=EC=A0=9C=204?= =?UTF-8?q?=EA=B1=B4=20=EC=83=81=EC=84=B8=20=EA=B8=B0=EB=A1=9D=20+=20compo?= =?UTF-8?q?se=20=EC=98=88=EC=8B=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 138 ++++++++++++++++++++++++++----------- docker-compose.yml.example | 14 ++++ 2 files changed, 112 insertions(+), 40 deletions(-) create mode 100644 docker-compose.yml.example diff --git a/README.md b/README.md index 07f3170..ce9dff6 100644 --- a/README.md +++ b/README.md @@ -1,69 +1,127 @@ -# TriliumNext Notes — Korean (한국어) Translation +# TriliumNext Notes 한국어 번역 -Unofficial, community-made Korean translation for [TriliumNext Notes](https://github.com/TriliumNext/Notes). -As of this writing, TriliumNext ships with no Korean language pack, and the list of -selectable UI languages is hardcoded into the compiled server bundle rather than -derived from the `translations/` folder — so dropping these files in alone is not -enough to make "한국어" show up in the language picker. See below for the full setup. +[TriliumNext Notes](https://github.com/TriliumNext/Notes)의 비공식 커뮤니티 한국어 번역입니다. +이 글을 쓰는 시점 기준으로 TriliumNext는 한국어 언어팩이 없고, 심지어 UI 언어 선택기에 +뜨는 지원 언어 목록 자체가 `translations/` 폴더를 스캔해서 만들어지는 게 아니라 **컴파일된 +서버 번들(`main.cjs`) 안에 하드코딩된 배열**이라서, 번역 파일만 올바른 경로에 넣는다고 +"한국어"가 언어 선택기에 나타나지 않습니다. 그래서 번역 파일 외에 서버 번들을 패치하는 +스크립트도 함께 제공합니다. -- `translation.json` — client-side UI strings, 1,712 keys, 100% translated. -- `server.json` — server-side strings, 283 keys, 100% translated. -- `patch_main_cjs.js` — patches the compiled `main.cjs` server bundle to register - `ko` as a selectable language (see comments in the script for why this is needed). +## 포함된 파일 -Tested against `triliumnext/notes:latest` (build date 2025-06-07, appVersion 0.95.0). -Variable names inside `main.cjs` are minified and **not stable across builds** — if -`patch_main_cjs.js` prints `[skip]` on a newer image, the markers it searches for -have changed and need to be re-derived from that build. +- `translation.json` — 클라이언트(브라우저) UI 문자열, 1,712개 키, 100% 번역 완료. +- `server.json` — 서버 문자열, 283개 키, 100% 번역 완료. +- `patch_main_cjs.js` — 컴파일된 `main.cjs` 서버 번들에 `ko`를 선택 가능한 언어로 + 등록하는 패치 스크립트 (왜 이게 필요한지는 스크립트 상단 주석 참고). +- `docker-compose.yml.example` — 아래 설치 방법을 그대로 반영한 예시 compose 파일. -## Setup +`triliumnext/notes:latest` (빌드일 2025-06-07, appVersion 0.95.0) 기준으로 테스트했습니다. +`main.cjs` 안의 변수명은 전부 난독화(minify)되어 있고 **빌드마다 이름이 바뀔 수 있습니다** — +`patch_main_cjs.js`를 새 이미지에 돌렸을 때 `[skip]`이 출력되면, 스크립트가 찾는 문자열 +패턴이 그 빌드에서 바뀐 것이니 새 빌드에서 같은 패턴을 다시 찾아 스크립트를 갱신해야 합니다. -1. Copy the translation files into the running container (or better, bind-mount them - so they survive `docker compose down && up` — see "Important" below): +## 설치 방법 + +1. **번역 파일을 컨테이너에 반영** — 반드시 `docker-compose.yml`에 bind-mount로 등록할 것 + (아래 "주의" 항목 참고, `docker cp`로만 넣으면 안 됨): - `translation.json` → `/usr/src/app/public/translations/ko/translation.json` - `server.json` → `/usr/src/app/assets/translations/ko/server.json` -2. Extract the container's unpatched `main.cjs` and run the patch script against it: +2. **컨테이너의 원본(패치 안 된) `main.cjs`를 추출**하고 패치 스크립트 실행: ```bash docker create --name trilium-tmp triliumnext/notes:latest docker cp trilium-tmp:/usr/src/app/main.cjs ./main.cjs docker rm trilium-tmp node patch_main_cjs.js ./main.cjs - node --check ./main.cjs # always verify syntax before deploying + node --check ./main.cjs # 반드시 배포 전에 문법 검사 ``` -3. Bind-mount the patched `main.cjs` over the original in `docker-compose.yml`: +3. **패치된 `main.cjs`를 `docker-compose.yml`에서 원본 위치에 bind-mount** — + `docker-compose.yml.example` 참고: ```yaml services: trilium: image: triliumnext/notes:latest + restart: unless-stopped + ports: + - '127.0.0.1:18082:8080' + environment: + - TZ=Asia/Seoul volumes: - ./data:/home/node/trilium-data - ./main.cjs:/usr/src/app/main.cjs:ro - ./translation.json:/usr/src/app/public/translations/ko/translation.json:ro - ./server.json:/usr/src/app/assets/translations/ko/server.json:ro ``` -4. Start the container, then in the app go to Options → Localization and select "한국어". +4. 컨테이너 시작 후 앱 안에서 **옵션 → 언어 및 지역**에서 "한국어" 선택. -### Important: don't `docker cp` and stop there +## 겪었던 문제와 해결 (설치 전에 꼭 읽어보세요) -Files copied into a container with `docker cp` live only in its writable layer — -they disappear the moment the container is recreated (`docker compose down && up`, -not just `restart`). Always bind-mount the three files above from the host, as shown -in step 3, or the translation will silently vanish on the next redeploy while the -language list (which lives inside the bind-mounted `main.cjs`) keeps working — -producing a confusing partial-translation state that has nothing to do with locale -or browser and everything to do with a missing file on the server. +### 문제 1 — DB에서 `locale`을 직접 `'ko'`로 바꾸면 크래시 -### Known gap: CKEditor's own toolbar +지원 언어 목록에 없는 값을 옵션 DB에 직접 넣으면 앱이 시작하다가 죽습니다. 이 방법으로는 +안 되고, 반드시 아래처럼 서버 번들 자체를 패치해야 합니다. -CKEditor5's own UI strings (toolbar button labels like "Paragraph", "Heading 2", -"Bold") come from a separate translation system (`window.CKEDITOR_TRANSLATIONS`) -that isn't touched by this project. As of the tested build, **no** language — -not German, French, Spanish, Chinese, or any other already-"supported" language — -ships a real CKEditor dictionary either; it falls back to English UI for everyone. -This is a pre-existing gap in the upstream build, not something specific to Korean. +### 문제 2 — `main.cjs` 패치 위치를 잘못 잡으면 문법 자체가 깨짐 -## License +지원 언어 배열(테스트한 빌드에서 변수명 `K5i`)에 새 항목을 삽입할 때, 배열의 여는 대괄호 +`[` **바로 뒤**가 아니라 그 앞(변수 선언문 쪽)에 잘못 삽입하면 JS 문법 자체가 깨져서 앱이 +아예 뜨지 못하고 크래시를 반복합니다. `patch_main_cjs.js`는 이 위치를 정확히 계산해서 +삽입하지만, **패치 후에는 항상 `node --check`로 문법을 먼저 확인**하고 나서 컨테이너를 +재시작하세요. -Same license as TriliumNext Notes itself. No warranty — this patches a compiled -binary by string-matching minified variable names, which is inherently fragile -across version upgrades. +### 문제 3 — 완전 재생성(`docker compose down && up`) 시에만 나타나는 크래시 + +지원 언어 배열만 고치면 `docker restart`로는 잘 동작하는 것처럼 보이지만, 컨테이너를 완전히 +내렸다 올리면(`down && up`) 다시 크래시합니다. 원인은 Trilium 자체 번역과는 별개로, +**CKEditor 내장 다국어 리소스를 지연 로드하는 두 번째 하드코딩 맵**(테스트한 빌드에서 +변수명 `IMi`)에도 `ko` 항목이 없어서 발생하는 `TypeError`입니다. 이 리소스는 애초에 +한국어용 청크가 빌드에 포함되어 있지 않아 새로 만들 수 없으므로, `patch_main_cjs.js`는 +이 항목을 영어 로더로 대체(alias)해서 우회합니다. **`restart`뿐 아니라 완전 재생성까지 +반드시 테스트**하세요 — 재시작만으로는 이 문제가 드러나지 않습니다. + +### 문제 4 — 번역이 배포 후 시간이 지나서 "모바일에서만" 다시 영어로 보임 + +가장 까다로웠던 문제입니다. 번역을 배포하고 정상 동작까지 확인했는데, 이후 **모바일 +기기에서만** 옵션 페이지 등의 본문 텍스트가 다시 영어로 나오는 현상이 발생했습니다 +(페이지 제목이나 "한국어"라는 언어 이름 자체는 정상). 데스크톱에서는 동일 페이지가 +100% 정상이었습니다. + +처음엔 모바일 브라우저 캐시 문제로 의심했지만, 캐시를 초기화하고 서로 다른 브라우저 +엔진(Chrome, 삼성인터넷, 네이버) 세 개로 테스트해도 완전히 동일한 증상이 재현되어 +캐시 이론은 기각했습니다. + +**진짜 원인**: 번역 파일(`translation.json`, `server.json`)을 `docker cp`로 컨테이너 +내부에 직접 복사만 하고 `docker-compose.yml`에는 bind-mount로 등록하지 않았던 것이 +원인이었습니다. `docker cp`로 넣은 파일은 컨테이너의 쓰기 가능 레이어에만 존재하기 때문에, +이후 `main.cjs` 패치가 완전 재생성에도 살아남는지 테스트하려고 `docker compose down && up`을 +실행했을 때 번역 파일 두 개가 컨테이너와 함께 통째로 사라졌습니다 (bind-mount 되어 있던 +`main.cjs`만 살아남음). 서버가 `translations/ko/*.json` 요청에 404를 반환하기 시작했고, +i18next의 `fallbackLng:"en"` 설정 때문에 본문 텍스트가 조용히 영어로 대체됐습니다. +"한국어"라는 언어 이름만 정상이었던 이유는, 그 값이 이 JSON 파일이 아니라 별도의 DB 기반 +API(`options/locales`)에서 오기 때문에 영향을 받지 않았던 것입니다. + +데스크톱이 여전히 정상으로 보였던 이유는 단순히 파일이 사라지기 전에 브라우저가 이미 +받아둔 번역 응답을 캐시하고 있었기 때문입니다 — 즉 데스크톱 쪽이 오히려 "낡은 캐시를 +보여주던 쪽"이었고, 매번 새로 서버에 요청하는 모바일 쪽이 서버의 실제(망가진) 상태를 +정확히 반영하고 있었던 것입니다. 세 개의 다른 모바일 브라우저에서 전부 동일한 증상이 +나온 것도 이걸로 설명됩니다 — 캐시 종류와 무관하게 서버에 파일 자체가 없었으니까요. + +**교훈**: `docker cp`로 컨테이너 안에 넣은 파일은 컨테이너가 사라지면 함께 사라집니다. +번역 파일도 `main.cjs`와 마찬가지로 **반드시 `docker-compose.yml`의 `volumes:`에 +bind-mount로 선언**하세요 (위 설치 방법 3번, `docker-compose.yml.example` 참고). 그리고 +증상이 "특정 기기에서만 재현됨", "캐시를 지워도 재현됨"처럼 보이더라도, 실제 원인은 +클라이언트가 아니라 서버 쪽 데이터 유실일 수 있으므로 클라이언트 가설을 세우기 전에 +서버가 실제로 무엇을 응답하는지(`curl`로 정확한 URL 직접 확인)부터 검증하는 게 우선입니다. + +## 알려진 미해결 항목 — CKEditor 자체 툴바 + +CKEditor5 자체의 UI 문자열(툴바 버튼 이름 — "Paragraph", "Heading 2", "Bold" 등)은 +이 프로젝트가 건드리는 번역 시스템과 완전히 별개인 `window.CKEDITOR_TRANSLATIONS`라는 +자체 시스템에서 옵니다. 테스트한 빌드 기준으로, 독일어·프랑스어·스페인어·중국어를 +포함해 이미 "지원"되는 언어 **어느 것도** 실제 CKEditor 사전을 갖고 있지 않아서 전부 +영어 툴바로 나옵니다 — 한국어만의 문제가 아니라 업스트림 빌드 자체에 이미 있던 공백입니다. + +## 라이선스 + +TriliumNext Notes 본체와 동일한 라이선스를 따릅니다. 무보증 — 이 프로젝트는 컴파일된 +바이너리를 난독화된 변수명 문자열 매칭 방식으로 패치하는 것이라, 버전이 올라가면 +깨지기 쉬운 구조입니다. diff --git a/docker-compose.yml.example b/docker-compose.yml.example new file mode 100644 index 0000000..9c4feb1 --- /dev/null +++ b/docker-compose.yml.example @@ -0,0 +1,14 @@ +services: + trilium: + image: triliumnext/notes:latest + container_name: trilium + restart: unless-stopped + ports: + - '127.0.0.1:18082:8080' + environment: + - TZ=Asia/Seoul + volumes: + - ./data:/home/node/trilium-data + - ./main.cjs:/usr/src/app/main.cjs:ro + - ./translation.json:/usr/src/app/public/translations/ko/translation.json:ro + - ./server.json:/usr/src/app/assets/translations/ko/server.json:ro