Skip to content
Merged
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
71 changes: 71 additions & 0 deletions .claude/skills/lich-changelog-i18n/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
---
name: lich-changelog-i18n
description: Translate the latest release's CHANGELOG.md section into every interface language lich ships, writing changelog/<locale>.md, the files the What's new popup reads. Use when cutting a release (right after its section moves out of [Unreleased]), when TestTranslations in internal/patchnotes fails, or when a language is added.
---

# lich changelog translations

The What's new popup shows the running build's `CHANGELOG.md` section (`internal/patchnotes`). A user whose
interface language is not English reads it from `changelog/<locale>.md` instead, embedded in the binary
next to `CHANGELOG.md`. Each file holds **only the latest release**: a build only ever reads its own version,
and git keeps the old translations. `CHANGELOG.md` stays English and canonical; the release page reads it.

## When

Cutting a release (`CLAUDE.md` › Releases): once the section is under its `## [X.Y.Z] - YYYY-MM-DD` heading and
its text is final, run this before tagging, in the same commit. A fix to the released English text afterwards
means running it again.

## Steps

1. **Slice the source.** The release is the first `## [` heading in `CHANGELOG.md` below `## [Unreleased]`.
Its section runs from that heading to the next `## ` heading.
2. **One file per locale, except English.** The locales are `prompt.Langs` (`internal/prompt/prompt.go`),
file names are their tags as written there: `pt-BR.md`, `es.md`, `zh-CN.md`, `ja.md`, `ko.md`, `ru.md`.
A language added to lich gets its file in the same PR.
3. **Overwrite each file** with the translated section and nothing else: no `# Changelog` title, no
`[Unreleased]`, no older release, no compare links.
4. **Translate from the English**, never from another translation. The locales are independent, so each
one can go to its own subagent in parallel, on a cheap model (Sonnet); hand each the English section and
the rules below.
5. **Run the check** and finish only when it is green (needs nothing built):
`go test -run TestTranslations ./internal/patchnotes`

## What stays in English, byte for byte

The parser and both windows read these, so `TestTranslations` fails when one is touched:

- The version heading, date included: `## [0.69.0] - 2026-10-16`.
- The group headings: `### Added`, `### Changed`, `### Deprecated`, `### Removed`, `### Fixed`,
`### Security`. The windows translate the label on screen and color it by the English word.
- The alert openers: `> [!IMPORTANT]`, `> [!WARNING]`, `> [!NOTE]`. The text under them is translated, each
line still starting with `> `.
- Every `code` span, exactly as written. Their order inside an item may follow the language's grammar.

## How to translate

- **Same structure.** Same groups in the same order, same number of `- ` items in each, same alert blocks.
An item may wrap over as many lines as it needs; a blank line between items is fine; a blank line inside
an item ends it.
- **The bold lead-in stays bold.** `**The native window opens on Windows.**` becomes one `**…**` in the
translation, and the rest of the item follows it as plain text. No bold, italics or links the English
does not have.
- **Names stay names:** lich, the providers (Claude Code, Codex, Antigravity, opencode, oh-my-pi, Crush,
Cursor CLI, Kiro CLI), OS and app names (Linux, Windows, macOS, Wayland, Terminal.app), commands and key
names, plus the rest of `lich-i18n`'s literal list.
- **Interface words match the interface.** A setting, menu or button named in the English (Settings ›
Appearance › Window, Remove worktree, Undo) is written the way that language's catalog shows it on
screen: look it up in `native/i18n/locales/<lang>/` or `frontend/src/lib/i18n/locales/<lang>/` instead of
translating it afresh. `›` stays as the path separator.
- **Plain release-note register** for that language, written for a developer: what changed for them, in
the same tone and length as the English. Do not add, drop or explain.

## If the check fails

- `version headings = …`: the file holds another release, or more than one. Rewrite it from step 1.
- `N groups` / `N items`: an item was merged, split or lost, often a blank line inside an item.
- `group N is "…"`: a `### ` heading was translated.
- `code spans …` or `bold markers …`: a code span was changed or a `**` was lost; the message prints the
item.

The check is the contract. Fix the translation, never the test.
5 changes: 4 additions & 1 deletion .claude/skills/lich-i18n/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,8 @@ Classify every string first. Who reads it?
| An agent, typed by the frontend into a PTY | `locales/<lang>/prompts.ts`, read with `prompt()` from `lib/i18n/prompt.ts` | prompt |
| An agent, typed by the native window into a PTY | `native/i18n/locales/<lang>/prompts.json`, read with `i18n.Prompt()` | prompt |
| An agent, added by a lich-plugin mod | `hooks/prompt-text.js` in `lichdotdev/lich-plugin` | prompt |
| Nobody localizes it | error text through `errorText()`, CHANGELOG, MCP tool descriptions, CLI `--help`, identifiers | none |
| The user, in the What's new dialog | `changelog/<locale>.md`, one release, written by the `lich-changelog-i18n` skill when a release is cut | interface |
| Nobody localizes it | error text through `errorText()`, `CHANGELOG.md`, MCP tool descriptions, CLI `--help`, identifiers | none |

Then, in the same PR:

Expand Down Expand Up @@ -112,6 +113,8 @@ language at the **end** of every list so parallel additions merge as a union.
**Docs**
- `docs/hooks/README.md`: the accepted `LICH_PROMPT_LANG` values.
- `CHANGELOG.md` `[Unreleased]` › Added.
- `changelog/<locale>.md`: the latest release translated with the `lich-changelog-i18n` skill;
`TestTranslations` (`internal/patchnotes`) fails until it exists.
- README.md and each README.<lang>.md only if they enumerate languages.

**Companion repo** `lichdotdev/lich-plugin` (separate PR, after lich's)
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -18,3 +18,4 @@ frontend/coverage
.claude/skills/*
!.claude/skills/lich-i18n/
!.claude/skills/lich-build-dev/
!.claude/skills/lich-changelog-i18n/
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0

### Added

- **What's new speaks your language.** The dialog that opens after an update
shows the release notes, and their group names, in the interface language
you picked, in both windows. A build whose notes have no translation yet
shows them in English.
- **Fork a session in the native window.** Right-click a card and pick
Fork › Here to carry its conversation on in a new card right under it, in
the same checkout, or Fork › Worktree… to take it to a new worktree along
Expand Down
7 changes: 5 additions & 2 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -41,8 +41,8 @@ lives in the code, `docs/` and `CHANGELOG.md` — never restate any of it here.
(`internal/terminal`).
- No literal user-visible or agent-facing text: a string a person reads on a lich screen, or that lich types
into a session, goes through the catalogs and lands in **every** locale in the same PR (`lich-i18n` skill).
English is the source; tests stay English. Error text, CHANGELOG, MCP tool descriptions and CLI help stay
English by design.
English is the source; tests stay English. Error text, `CHANGELOG.md`, MCP tool descriptions and CLI help
stay English by design; the What's new dialog reads a release's translations from `changelog/<locale>.md`.
- Service shapes are hand-owned in `frontend/src/lib/api-types.ts`: touch a Go struct's JSON tags and that mirror
moves in the same change. There is no codegen.
- A fact more than one place shows (git status, a pull request, plan quota, a session's profile or status) has
Expand Down Expand Up @@ -119,6 +119,9 @@ The version comes from the git tag (`git describe` in the Taskfile, env `VERSION
greps `^## \[X.Y.Z\]` and ships empty notes if it misses. Keep the sections in their canonical order
(Added, Changed, Deprecated, Removed, Fixed, Security), and refresh the compare links, `[Unreleased]`
included.
- [ ] Translate the cut section for the What's new dialog with the `lich-changelog-i18n` skill, alert blocks
included, in the same commit: `TestTranslations` (`internal/patchnotes`) fails until every
`changelog/<locale>.md` holds this release.
- [ ] Want the release to announce itself? Write GitHub alert blocks right under its heading, before the first
`###`: `> [!IMPORTANT]` is the release's headline and opens the What's new dialog (one per release — a
second one is demoted to a callout, which is the signal to fold it into the groups); `> [!WARNING]` is
Expand Down
51 changes: 51 additions & 0 deletions changelog/es.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
## [0.69.0] - 2026-10-16

### Added

- **La ventana nativa en macOS, experimental.** Lich.app la incluye junto a lich
en Apple Silicon e Intel, y Ajustes › Apariencia › Ventana la ofrece igual
que en Linux y Windows. Option escribe caracteres en su terminal, como en
Terminal.app, así que los atajos Option+letra de un agente no llegan hasta
ella. Por ahora solo se ha ejecutado en las máquinas macOS de GitHub: las
notificaciones son simples y no se puede hacer clic en ellas, y no hay
elemento en la barra de menús.
- **El árbol de archivos reconoce muchos más tipos de archivo.** Java, Gradle,
C y C++, C#, PHP, Ruby, Swift, SQL, Vue, Svelte, Terraform, Dockerfile,
.gitignore y unos treinta más ahora muestran su propio icono en el árbol de
archivos y en los resultados de búsqueda, donde antes mostraban un glifo de
archivo genérico.

### Fixed

- **`lich quit` cierra la ventana nativa.** Se quedaba abierta mostrando lich
como perdido una vez que el backend había terminado; Salir de lich en la
bandeja y una señal de parada también la cierran, mientras que un reinicio
tras una actualización sigue conservándola.
- **La ventana nativa ofrece eliminar una worktree cuando se cierra su última sesión.** El
aviso Se cerró gana Eliminar worktree junto a Deshacer; una worktree que lich no creó,
o una con cambios sin confirmar, se nombra en un diálogo antes de eliminarla.
- **El terminal de la ventana nativa toma la celda bajo el puntero.** Un clic,
una selección con arrastre, un enlace y un programa que sigue el ratón leían
todos el puntero desplazado por el relleno del terminal, así que la celda
que tomaban quedaba por debajo y a la derecha de la señalada, por casi una
celda entera en una pantalla escalada.
- **El terminal de la ventana nativa dibuja emoji.** Una fuente de emoji a
color como Noto Color Emoji guarda cada emoji como una imagen y no como un
contorno, y el terminal solo dibujaba contornos, así que cada emoji aparecía
como un hueco en blanco; ahora se dibujan a color, ocupando dos celdas.
- **El árbol de archivos de la ventana nativa muestra el icono de lenguaje de cada archivo.**
Todos los archivos dibujaban el mismo glifo de archivo genérico; ahora cada
uno lleva el icono que le da la ventana del navegador.
- **Un pull request que abre un agente aparece en su tarjeta al terminar el turno.**
La insignia solo volvía a consultarse cuando cambiaba la rama o su último
commit, y abrir un pull request no cambia ninguno de los dos, así que
esperaba a que la ventana perdiera y recuperara el foco.
- **La ventana nativa se abre en Windows.** Buscaba las fuentes mediante
fontconfig, que Windows no incluye, así que se cerraba al iniciar y lich
recurría a la ventana web. Ahora, en Windows, encuentra por sí misma las
fuentes instaladas, con Cascadia Mono o Consolas para el terminal por
defecto, y los selectores de fuente de Ajustes las listan.
- **Cambiar entre un tema claro y uno oscuro ya no congela la ventana
nativa.** A una sesión que sigue el esquema de color del terminal, como hace
Claude Code, se le avisaba del cambio mientras la ventana todavía conservaba
sus terminales, así que la ventana se colgaba y se cerraba.
18 changes: 18 additions & 0 deletions changelog/ja.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
## [0.69.0] - 2026-10-16

### Added

- **macOS でネイティブウィンドウを利用可能に(実験的)。** Lich.app は Apple Silicon と Intel の両方で lich と並べてネイティブウィンドウを同梱しており、Linux や Windows と同じく設定 › 外観 › ウィンドウから選べます。ターミナルでは Option キーが文字入力に使われます(Terminal.app と同じ動作)。そのため、エージェントの Option+文字 のショートカットは届きません。これまでのところ GitHub の macOS マシンでしか動作しておらず、通知はクリックできない通常の通知で、メニューバー項目もありません。
- **ファイルツリーが対応するファイル形式が大幅に増えました。** Java、Gradle、C と C++、C#、PHP、Ruby、Swift、SQL、Vue、Svelte、Terraform、Dockerfile、.gitignore のほか約 30 種類が、これまでの無地のファイルアイコンに代わって、ファイルツリーと検索結果で専用のアイコンで表示されます。

### Fixed

- **`lich quit` がネイティブウィンドウを閉じるようになりました。** バックエンドが終了したあとも、ウィンドウは lich を接続切れとして表示したまま開いたままでした。トレイの終了や停止シグナルでも閉じます。一方、アップデート後の再起動ではウィンドウは維持されます。
- **ネイティブウィンドウで、最後のセッションを閉じたときにワークツリーを削除できるようになりました。** 「閉じました」トーストに「元に戻す」と並んで「ワークツリーを削除」が加わります。lich が作成していないワークツリーや未コミットの変更があるワークツリーは、削除する前にダイアログで知らされます。

- **ネイティブウィンドウのターミナルが、ポインターの下のセルを正しく扱うようになりました。** クリック、ドラッグ選択、リンク、マウスを追跡するプログラムのいずれも、ターミナルのパディング分ずれた位置のポインターを読み取っていたため、実際に指しているセルの右下のセルが対象になっていました。スケーリングされたディスプレイでは、そのずれはほぼセル 1 つ分でした。
- **ネイティブウィンドウのターミナルが絵文字を描画するようになりました。** Noto Color Emoji などのカラー絵文字フォントは、各絵文字をアウトラインではなく画像として保持しています。ターミナルはアウトラインしか描画しなかったため、すべての絵文字が空白になっていました。現在は 2 セル幅のカラーで描画されます。
- **ネイティブウィンドウのファイルツリーが、ファイルごとの言語アイコンを表示するようになりました。** これまではどのファイルも同じ無地のファイルアイコンでしたが、ブラウザウィンドウと同じアイコンが付きます。
- **エージェントが開いたプルリクエストが、ターンの終了時にカードへ表示されるようになりました。** バッジはブランチまたは最後のコミットが動いたときにしか再確認されず、プルリクエストを開いてもどちらも動かないため、ウィンドウのフォーカスが外れて戻るまで待たされていました。
- **Windows でネイティブウィンドウが開くようになりました。** フォントの検索に Windows には含まれない fontconfig を使っていたため、起動時に閉じてしまい、lich はウェブウィンドウに切り替わっていました。Windows ではインストール済みのフォントを自前で見つけるようになり、ターミナルの既定は Cascadia Mono または Consolas、設定のフォントピッカーにも一覧表示されます。
- **ライトテーマとダークテーマを切り替えてもネイティブウィンドウがフリーズしなくなりました。** Claude Code のように、ターミナルの配色に追従するセッションへ、ウィンドウがまだターミナルを保持している間に変更が通知されていたため、ウィンドウがハングして閉じていました。
39 changes: 39 additions & 0 deletions changelog/ko.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
## [0.69.0] - 2026-10-16

### Added

- **macOS에서 네이티브 창을 사용할 수 있습니다(실험).** Lich.app이 Apple Silicon과 Intel 모두에서
lich와 함께 네이티브 창을 제공하며, 설정 › 모양 › 창에서 Linux, Windows와 같이 선택할 수
있습니다. 터미널에서는 Option 키가 Terminal.app처럼 문자를 입력하므로, 에이전트의 Option+글자
단축키는 터미널에 전달되지 않습니다. 지금까지는 GitHub의 macOS 머신에서만 실행해 보았습니다.
알림은 클릭할 수 없는 일반 알림이며, 메뉴 막대 항목은 없습니다.
- **파일 트리가 훨씬 더 많은 파일 형식을 인식합니다.** Java, Gradle, C와 C++, C#, PHP, Ruby,
Swift, SQL, Vue, Svelte, Terraform, Dockerfile, .gitignore를 비롯해 약 서른 가지가 더 이제
파일 트리와 검색 결과에서 일반 파일 아이콘 대신 각자의 아이콘으로 표시됩니다.

### Fixed

- **`lich quit`이 네이티브 창을 닫습니다.** 백엔드가 사라진 뒤에도 창이 열린 채 lich를 연결이
끊긴 것으로 표시했습니다. 이제 트레이의 종료와 중지 신호도 창을 닫으며, 업데이트 후 재시작할
때는 창이 그대로 유지됩니다.
- **네이티브 창이 마지막 세션을 닫을 때 worktree 삭제를 제안합니다.** 닫힘 토스트에 실행 취소
옆으로 worktree 삭제 버튼이 생겼으며, lich가 만들지 않은 worktree나 커밋하지 않은 변경 사항이
있는 worktree는 삭제하기 전에 대화상자에서 알려 줍니다.

- **네이티브 창의 터미널이 포인터 아래의 셀을 선택합니다.** 클릭, 드래그 선택, 링크, 마우스를
추적하는 프로그램이 모두 터미널 패딩만큼 어긋난 포인터 위치를 읽었기 때문에, 가리킨 셀이 아니라
오른쪽 아래의 셀이 선택되었습니다. 배율이 적용된 디스플레이에서는 어긋남이 셀 하나에 가까웠습니다.
- **네이티브 창의 터미널이 이모지를 그립니다.** Noto Color Emoji 같은 컬러 이모지 글꼴은 각
이모지를 윤곽선이 아니라 그림으로 저장하는데, 터미널은 윤곽선만 그릴 수 있어서 모든 이모지가
빈 칸으로 보였습니다. 이제 컬러로, 두 셀 너비로 그려집니다.
- **네이티브 창의 파일 트리가 파일마다 언어 아이콘을 표시합니다.** 모든 파일이 똑같은 일반 파일
아이콘으로 그려졌는데, 이제 웹 창이 붙여 주는 것과 같은 아이콘이 붙습니다.
- **에이전트가 연 풀 리퀘스트가 턴이 끝나면 카드에 표시됩니다.** 배지는 브랜치나 마지막 커밋이
바뀔 때만 다시 확인했는데, 풀 리퀘스트를 열어도 둘 다 바뀌지 않아서 창이 포커스를 잃었다가
되찾을 때까지 기다려야 했습니다.
- **Windows에서 네이티브 창이 열립니다.** 글꼴을 Windows에 없는 fontconfig로 찾으려다 시작할 때
창이 닫혔고, lich는 웹 창으로 대체되었습니다. 이제 Windows에서는 설치된 글꼴을 직접 찾으며,
터미널 기본 글꼴은 Cascadia Mono 또는 Consolas이고 설정의 글꼴 선택기에도 목록이 표시됩니다.
- **밝은 테마와 어두운 테마를 전환해도 네이티브 창이 더 이상 멈추지 않습니다.** Claude Code처럼
터미널의 색 구성표를 따르는 세션이, 창이 아직 터미널을 붙잡고 있는 동안 변경 사실을 전달받아
창이 멈춘 뒤 닫혔습니다.
Loading
Loading