pqcli-ja 1.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- pqcli_ja-1.1.0/.claude/settings.local.json +53 -0
- pqcli_ja-1.1.0/.github/workflows/ci.yml +50 -0
- pqcli_ja-1.1.0/.gitignore +9 -0
- pqcli_ja-1.1.0/.pre-commit-config.yaml +27 -0
- pqcli_ja-1.1.0/CONTRIBUTING.ja.md +104 -0
- pqcli_ja-1.1.0/CONTRIBUTING.md +107 -0
- pqcli_ja-1.1.0/Dockerfile +12 -0
- pqcli_ja-1.1.0/LICENSE.md +24 -0
- pqcli_ja-1.1.0/PKG-INFO +253 -0
- pqcli_ja-1.1.0/README.ja.md +242 -0
- pqcli_ja-1.1.0/README.md +220 -0
- pqcli_ja-1.1.0/babel.cfg +1 -0
- pqcli_ja-1.1.0/docker-compose.yml +26 -0
- pqcli_ja-1.1.0/hatch_build.py +24 -0
- pqcli_ja-1.1.0/pqcli/__init__.py +0 -0
- pqcli_ja-1.1.0/pqcli/__main__.py +117 -0
- pqcli_ja-1.1.0/pqcli/config.py +750 -0
- pqcli_ja-1.1.0/pqcli/i18n.py +71 -0
- pqcli_ja-1.1.0/pqcli/lingo/__init__.py +140 -0
- pqcli_ja-1.1.0/pqcli/lingo/en.py +140 -0
- pqcli_ja-1.1.0/pqcli/locale/ja/LC_MESSAGES/pqcli.mo +0 -0
- pqcli_ja-1.1.0/pqcli/locale/ja/LC_MESSAGES/pqcli.po +4088 -0
- pqcli_ja-1.1.0/pqcli/locale/pqcli.pot +4026 -0
- pqcli_ja-1.1.0/pqcli/mechanic.py +1147 -0
- pqcli_ja-1.1.0/pqcli/random.py +26 -0
- pqcli_ja-1.1.0/pqcli/roster.py +40 -0
- pqcli_ja-1.1.0/pqcli/text.py +83 -0
- pqcli_ja-1.1.0/pqcli/ui/__init__.py +0 -0
- pqcli_ja-1.1.0/pqcli/ui/base.py +22 -0
- pqcli_ja-1.1.0/pqcli/ui/basic/__init__.py +253 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/__init__.py +204 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/colors.py +18 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/event_handler.py +14 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/util.py +57 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/__init__.py +23 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/base_view.py +18 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/choose_character_view.py +64 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/confirm_view.py +51 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/__init__.py +11 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/choose_character_class_view.py +36 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/choose_character_name_view.py +121 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/choose_character_race_view.py +34 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/choose_character_stats_view.py +112 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/__init__.py +263 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/character_sheet_window.py +67 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/equipment_window.py +69 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/inventory_window.py +106 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/plot_window.py +68 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/progress_bar_window.py +156 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/quest_book_window.py +66 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/spell_book_window.py +71 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/task_progress_window.py +52 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/menu_view.py +41 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/views/roster_view.py +102 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/widgets/__init__.py +20 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/widgets/base.py +41 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/widgets/data_table.py +86 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/widgets/focusable.py +42 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/widgets/label.py +14 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/widgets/list_box.py +57 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/widgets/menu.py +104 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/widgets/progress_bar.py +68 -0
- pqcli_ja-1.1.0/pqcli/ui/curses/widgets/scrollable.py +116 -0
- pqcli_ja-1.1.0/pyproject.toml +67 -0
- pqcli_ja-1.1.0/screen-basic.png +0 -0
- pqcli_ja-1.1.0/screen-curses-logo.png +0 -0
- pqcli_ja-1.1.0/screen-curses.png +0 -0
- pqcli_ja-1.1.0/tools/golden.py +155 -0
- pqcli_ja-1.1.0/tools/golden_expected.txt +1532 -0
- pqcli_ja-1.1.0/uv.lock +237 -0
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"permissions": {
|
|
3
|
+
"allow": [
|
|
4
|
+
"Bash(python3 -c \"import babel; print\\(babel.__version__\\)\")",
|
|
5
|
+
"Bash(grep -rn 'caption = \\\\|Task\\(\\\\s*$\\\\|Task\\(\"' pqcli/mechanic.py)",
|
|
6
|
+
"Bash(python3 -)",
|
|
7
|
+
"Bash(python3 -c 'import ast,sys,pathlib *)",
|
|
8
|
+
"Bash(uv sync *)",
|
|
9
|
+
"Bash(uv run *)",
|
|
10
|
+
"Bash(PQCLI_LANG=en uv run python /tmp/smoke.py)",
|
|
11
|
+
"Bash(PQCLI_LANG=ja uv run python /tmp/smoke.py)",
|
|
12
|
+
"Bash(env -u PQCLI_LANG -u LANG -u LC_ALL -u LANGUAGE uv run python -c ' *)",
|
|
13
|
+
"Bash(PQCLI_LANG=zz_ZZ uv run python -c ' *)",
|
|
14
|
+
"Bash(LANG=ja_JP.UTF-8 env -u PQCLI_LANG uv run python -c ' *)",
|
|
15
|
+
"Bash(rm -rf /tmp/pqhome)",
|
|
16
|
+
"Bash(mkdir -p /tmp/pqhome/pqcli)",
|
|
17
|
+
"Bash(PQCLI_LANG=en uv run python /tmp/mkchar.py /tmp/pqhome/pqcli/save.dat)",
|
|
18
|
+
"Bash(python3 -c ' *)",
|
|
19
|
+
"Bash(git stash *)",
|
|
20
|
+
"Bash(grep -v '^| *|$')",
|
|
21
|
+
"Bash(rm -rf pqhome2)",
|
|
22
|
+
"Bash(mkdir -p pqhome2/pqcli)",
|
|
23
|
+
"Bash(cp pqhome/pqcli/save.dat pqhome2/pqcli/)",
|
|
24
|
+
"Bash(XDG_CONFIG_HOME=/tmp/pqhome2 PQCLI_LANG=ja timeout 8 uv run python -m pqcli --basic)",
|
|
25
|
+
"Bash(grep -rn --include='*.py' -E '\\(^|[^a-zA-Z0-9_]\\)_\\([^a-zA-Z0-9_\\(]|$\\)' pqcli/)",
|
|
26
|
+
"Bash(XDG_CONFIG_HOME=/tmp/pqhome2 PQCLI_LANG=ja timeout 12 uv run python -m pqcli --basic --no-save)",
|
|
27
|
+
"Bash(XDG_CONFIG_HOME=/tmp/pqhome2 PQCLI_LANG=en timeout 12 uv run python -m pqcli --basic --no-save)",
|
|
28
|
+
"Bash(PQCLI_LANG=ja uv run python -m pqcli --help)",
|
|
29
|
+
"Bash(PQCLI_LANG=en uv run python -m pqcli --help)",
|
|
30
|
+
"Bash(XDG_CONFIG_HOME=/tmp/pqfresh PQCLI_LANG=ja timeout 10 uv run python -m pqcli --basic)",
|
|
31
|
+
"Bash(uv build *)",
|
|
32
|
+
"Bash(unzip -l /tmp/wheeltest/*.whl)",
|
|
33
|
+
"Bash(uv venv *)",
|
|
34
|
+
"Bash(VIRTUAL_ENV=/tmp/cleanenv uv pip install --quiet /tmp/wheeltest/*.whl)",
|
|
35
|
+
"Bash(XDG_CONFIG_HOME=/tmp/pqhome2 PQCLI_LANG=ja timeout 10 /tmp/cleanenv/bin/pqcli --basic --no-save)",
|
|
36
|
+
"Bash(XDG_CONFIG_HOME=/tmp/pqhome2 PQCLI_LANG=en timeout 10 /tmp/cleanenv/bin/pqcli --basic --no-save)",
|
|
37
|
+
"Bash(/tmp/cleanenv/bin/python -c \"import babel\")",
|
|
38
|
+
"Bash(brew upgrade *)",
|
|
39
|
+
"Bash(echo \" \\(uv run pqcli: exit=$? — no saves in default location is expected\\)\")",
|
|
40
|
+
"Bash(git log *)",
|
|
41
|
+
"Bash(git rm *)",
|
|
42
|
+
"Bash(unzip -l /tmp/wt2/*.whl)",
|
|
43
|
+
"Bash(PQCLI_LANG=ja uv run python -c \"from pqcli.lingo import act_name; print\\(' ja ->', act_name\\(2\\)\\)\")",
|
|
44
|
+
"Bash(PQCLI_LANG=en uv run python -c \"from pqcli.lingo import act_name; print\\(' en ->', act_name\\(2\\)\\)\")",
|
|
45
|
+
"Bash(git checkout *)",
|
|
46
|
+
"Bash(git add *)",
|
|
47
|
+
"Bash(git restore *)",
|
|
48
|
+
"Bash(git commit *)",
|
|
49
|
+
"Bash(git ls-remote *)",
|
|
50
|
+
"Bash(curl *)"
|
|
51
|
+
]
|
|
52
|
+
}
|
|
53
|
+
}
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
name: CI
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main]
|
|
6
|
+
pull_request:
|
|
7
|
+
|
|
8
|
+
jobs:
|
|
9
|
+
checks:
|
|
10
|
+
name: lint, catalogs and golden output
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
steps:
|
|
13
|
+
- uses: actions/checkout@v4
|
|
14
|
+
- uses: astral-sh/setup-uv@v5
|
|
15
|
+
with:
|
|
16
|
+
enable-cache: true
|
|
17
|
+
- run: uv sync
|
|
18
|
+
# isort, black, the compiled catalogs and the golden dump, in one pass.
|
|
19
|
+
- run: uv run pre-commit run --all-files --show-diff-on-failure
|
|
20
|
+
# .mo files are not in git, so prove the build hook still puts them in
|
|
21
|
+
# the distribution -- otherwise a release would silently ship no
|
|
22
|
+
# translations at all.
|
|
23
|
+
- run: rm -f pqcli/locale/*/LC_MESSAGES/*.mo && uv build
|
|
24
|
+
- name: the built wheel carries the compiled catalogs
|
|
25
|
+
run: |
|
|
26
|
+
python -c "
|
|
27
|
+
import glob, zipfile, sys
|
|
28
|
+
wheel = sorted(glob.glob('dist/*.whl'))[-1]
|
|
29
|
+
names = zipfile.ZipFile(wheel).namelist()
|
|
30
|
+
found = [n for n in names if n.endswith('.mo')]
|
|
31
|
+
print('catalogs in wheel:', found)
|
|
32
|
+
sys.exit(0 if found else 1)
|
|
33
|
+
"
|
|
34
|
+
|
|
35
|
+
supported-pythons:
|
|
36
|
+
name: python ${{ matrix.python }}
|
|
37
|
+
runs-on: ubuntu-latest
|
|
38
|
+
strategy:
|
|
39
|
+
fail-fast: false
|
|
40
|
+
matrix:
|
|
41
|
+
python: ["3.10", "3.11", "3.12", "3.13"]
|
|
42
|
+
steps:
|
|
43
|
+
- uses: actions/checkout@v4
|
|
44
|
+
- uses: astral-sh/setup-uv@v5
|
|
45
|
+
with:
|
|
46
|
+
enable-cache: true
|
|
47
|
+
- run: uv sync --python ${{ matrix.python }}
|
|
48
|
+
# The classifiers claim these versions, so prove the game runs on them.
|
|
49
|
+
- run: uv run --python ${{ matrix.python }} python tools/golden.py --check
|
|
50
|
+
- run: uv run --python ${{ matrix.python }} pqcli --help
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
repos:
|
|
2
|
+
|
|
3
|
+
- repo: https://github.com/pycqa/isort
|
|
4
|
+
rev: 5.13.2
|
|
5
|
+
hooks:
|
|
6
|
+
- id: isort
|
|
7
|
+
additional_dependencies: [toml]
|
|
8
|
+
- repo: https://github.com/psf/black
|
|
9
|
+
rev: 24.4.2
|
|
10
|
+
hooks:
|
|
11
|
+
- id: black
|
|
12
|
+
- repo: local
|
|
13
|
+
hooks:
|
|
14
|
+
- id: compile-catalogs
|
|
15
|
+
name: compile gettext catalogs
|
|
16
|
+
description: the game loads the .mo files, so a stale one silently ships the old text
|
|
17
|
+
entry: uv run pybabel compile -d pqcli/locale -D pqcli
|
|
18
|
+
language: system
|
|
19
|
+
files: ^pqcli/locale/.*\.(po|pot)$
|
|
20
|
+
pass_filenames: false
|
|
21
|
+
- id: golden-output
|
|
22
|
+
name: golden output unchanged
|
|
23
|
+
description: catches unintended changes to what the game displays in English
|
|
24
|
+
entry: uv run python tools/golden.py --check
|
|
25
|
+
language: system
|
|
26
|
+
files: ^(pqcli/.*\.py|tools/golden.*)$
|
|
27
|
+
pass_filenames: false
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
# 開発・翻訳への参加
|
|
2
|
+
|
|
3
|
+
*[English](CONTRIBUTING.md) | 日本語*
|
|
4
|
+
|
|
5
|
+
貢献の種類によって必要な準備が大きく違うので、目的に合う方を読んでください。
|
|
6
|
+
|
|
7
|
+
## 翻訳する
|
|
8
|
+
|
|
9
|
+
必要なのは `.po` エディタだけです。Python もビルドツールも要りません。
|
|
10
|
+
[Poedit](https://poedit.net/) は無料で Windows・macOS・Linux で動きます。
|
|
11
|
+
普通のテキストエディタでも構いません。
|
|
12
|
+
|
|
13
|
+
1. `pqcli/locale/<言語>/LC_MESSAGES/pqcli.po` を開きます。日本語なら
|
|
14
|
+
`pqcli/locale/ja/LC_MESSAGES/pqcli.po` です。
|
|
15
|
+
2. 訳したい `msgstr` を埋めます。
|
|
16
|
+
3. その `.po` だけを含む pull request を送ります。
|
|
17
|
+
|
|
18
|
+
これで全部です。**コンパイルは不要**です。`.mo` はビルド生成物で git 管理外、
|
|
19
|
+
リリースをビルドする時に生成されます。
|
|
20
|
+
|
|
21
|
+
途中まででも歓迎します。未訳の項目は英語にフォールバックするので、10件だけ
|
|
22
|
+
訳して止めてもゲームは問題なく遊べます。現時点で日本語は 982 件中 207 件です。
|
|
23
|
+
|
|
24
|
+
### 何を訳すことになるか
|
|
25
|
+
|
|
26
|
+
100件ほどが UI のラベルと文の骨格で、残りはゲームデータ(モンスター名・呪文名・
|
|
27
|
+
種族名・クラス名・アイテム名・装備名)です。本番はこちらで、Progress Quest は
|
|
28
|
+
RPG のパロディなので中身は言葉遊びです。`Cone of Annoyance`、`Slime Finger`、
|
|
29
|
+
`Battle-Ghoul` といった調子で、笑いを他言語で保つのは翻訳というより創作です。
|
|
30
|
+
直訳より「面白さが伝わる名前」を優先してください。同じ名前は出現箇所すべてで
|
|
31
|
+
統一してください。
|
|
32
|
+
|
|
33
|
+
文の骨格は `Executing {monster}` のような形です。`{...}` は残したまま、
|
|
34
|
+
日本語として自然な位置に動かして構いません。語順を入れ替えられるようにするために
|
|
35
|
+
文字列連結ではなくプレースホルダにしてあります。
|
|
36
|
+
|
|
37
|
+
### `pqcli/config.py` は編集しないでください
|
|
38
|
+
|
|
39
|
+
このファイルの英語文字列は表示用ではなく**識別子**です。コードもセーブファイルも
|
|
40
|
+
msgid も、すべてこの文字列を共通の名前として参照しています。ここを書き換えると
|
|
41
|
+
全カタログとの対応が壊れます。翻訳は必ず `msgstr` の追加で行ってください。
|
|
42
|
+
|
|
43
|
+
### 新しい言語を追加する
|
|
44
|
+
|
|
45
|
+
```console
|
|
46
|
+
uv run pybabel init -i pqcli/locale/pqcli.pot -d pqcli/locale -l de -D pqcli
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
言語によっては、カタログだけでは表現できない文法(冠詞・複数形・助数詞・形容詞の
|
|
50
|
+
位置)が必要になります。それは `pqcli/lingo/<言語>.py` に書き、
|
|
51
|
+
`pqcli/lingo/__init__.py` の `_BACKENDS` に登録します。**自分の言語で実際に
|
|
52
|
+
異なる規則だけ**を定義すれば十分で、それ以外は英語にフォールバックします。
|
|
53
|
+
関数2つだけのバックエンドでも動きます。
|
|
54
|
+
|
|
55
|
+
## コードを変更する
|
|
56
|
+
|
|
57
|
+
```console
|
|
58
|
+
git clone https://github.com/hannibal414/pq-cli.git
|
|
59
|
+
cd pq-cli
|
|
60
|
+
uv sync
|
|
61
|
+
uv run pre-commit install
|
|
62
|
+
|
|
63
|
+
# 英語以外で起動するために一度だけ実行:
|
|
64
|
+
uv run pybabel compile -d pqcli/locale -D pqcli
|
|
65
|
+
|
|
66
|
+
uv run pqcli
|
|
67
|
+
```
|
|
68
|
+
|
|
69
|
+
テストスイートはありません。代わりに `tools/golden.py` があります。シードを
|
|
70
|
+
固定した5本のシミュレーションを走らせて表示される文字列をすべてダンプし、
|
|
71
|
+
`golden-output` フックが `tools/golden_expected.txt` と突き合わせます。
|
|
72
|
+
|
|
73
|
+
差分が出ても即バグとは限りません。それは**あなたの変更が表示に与えた影響の
|
|
74
|
+
一覧**です。読んだうえで意図どおりなら記録し直してください。
|
|
75
|
+
|
|
76
|
+
```console
|
|
77
|
+
uv run python tools/golden.py --update
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
翻訳作業でこのダンプが変わることはないので、カタログを編集している間フックは
|
|
81
|
+
静かなままです。
|
|
82
|
+
|
|
83
|
+
### ソースに文字列を追加する
|
|
84
|
+
|
|
85
|
+
`pqcli.i18n` の `_()` で囲み、文字列を連結せずに**1文まるごとを1つの msgid**に
|
|
86
|
+
して名前付きプレースホルダを使ってください。
|
|
87
|
+
|
|
88
|
+
```python
|
|
89
|
+
# 良い例 — msgid が1つで、翻訳者が語順を変えられる
|
|
90
|
+
_("Selling {item}").format(item=name)
|
|
91
|
+
|
|
92
|
+
# 悪い例 — 英語の語順がコードに焼き付く
|
|
93
|
+
_("Selling ") + name
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
セーブファイルに保存されるテキストは、描画済みの文字列ではなく `pqcli.text` の
|
|
97
|
+
フレーズにしてください。言語切替のあとに再描画できるようにするためです。
|
|
98
|
+
`_` は gettext の関数なので、これを import しているモジュールで
|
|
99
|
+
使い捨て変数名(`for _ in range(...)`)として使わないでください。
|
|
100
|
+
|
|
101
|
+
## 本家について
|
|
102
|
+
|
|
103
|
+
このリポジトリは [rr-/pq-cli](https://github.com/rr-/pq-cli) のフォークです。
|
|
104
|
+
日本語化に関係しない変更は、本家に送った方が良い場合があります。
|
|
@@ -0,0 +1,107 @@
|
|
|
1
|
+
# Contributing
|
|
2
|
+
|
|
3
|
+
*English | [日本語](CONTRIBUTING.ja.md)*
|
|
4
|
+
|
|
5
|
+
Two kinds of contribution need very different setups, so pick the one you are
|
|
6
|
+
here for.
|
|
7
|
+
|
|
8
|
+
## Translating
|
|
9
|
+
|
|
10
|
+
You need a `.po` editor and nothing else — no Python, no build tools.
|
|
11
|
+
[Poedit](https://poedit.net/) is free and runs on Windows, macOS and Linux;
|
|
12
|
+
any text editor works too.
|
|
13
|
+
|
|
14
|
+
1. Open `pqcli/locale/<language>/LC_MESSAGES/pqcli.po` — for Japanese that is
|
|
15
|
+
`pqcli/locale/ja/LC_MESSAGES/pqcli.po`.
|
|
16
|
+
2. Fill in the `msgstr` lines you want to translate.
|
|
17
|
+
3. Open a pull request containing only that `.po` file.
|
|
18
|
+
|
|
19
|
+
That is the whole process. You do **not** need to compile anything: `.mo`
|
|
20
|
+
files are build output, are not in git, and are generated when a release is
|
|
21
|
+
built.
|
|
22
|
+
|
|
23
|
+
Partial work is welcome. Anything left untranslated falls back to English, so
|
|
24
|
+
translating ten entries and stopping leaves the game perfectly playable. As of
|
|
25
|
+
writing, Japanese covers 207 of 982 messages.
|
|
26
|
+
|
|
27
|
+
### What the strings are
|
|
28
|
+
|
|
29
|
+
Roughly a hundred are interface text and sentence frames. The rest are game
|
|
30
|
+
data — monster, spell, race, class, item and equipment names — and those are
|
|
31
|
+
where the work is, because Progress Quest is a parody: `Cone of Annoyance`,
|
|
32
|
+
`Slime Finger`, `Battle-Ghoul`. Keeping a joke funny in another language is
|
|
33
|
+
rewriting, not lookup. Prefer a name that lands over one that is literal, and
|
|
34
|
+
keep a given name identical everywhere it appears.
|
|
35
|
+
|
|
36
|
+
A sentence frame looks like `Executing {monster}`. Keep the `{...}` markers,
|
|
37
|
+
but move them wherever your language needs them — that is exactly why they are
|
|
38
|
+
markers instead of glued-together words.
|
|
39
|
+
|
|
40
|
+
### Do not edit `pqcli/config.py`
|
|
41
|
+
|
|
42
|
+
The English strings in that file are identities, not display text: the code,
|
|
43
|
+
the save files and the msgids all agree on them. Changing one there breaks the
|
|
44
|
+
link to every catalog. Translate by adding a `msgstr`, always.
|
|
45
|
+
|
|
46
|
+
### Starting a new language
|
|
47
|
+
|
|
48
|
+
```console
|
|
49
|
+
uv run pybabel init -i pqcli/locale/pqcli.pot -d pqcli/locale -l de -D pqcli
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
Some languages also need grammar the catalog cannot express — articles,
|
|
53
|
+
plurals, counters, adjective placement. Those live in `pqcli/lingo/<lang>.py`,
|
|
54
|
+
registered in `_BACKENDS` in `pqcli/lingo/__init__.py`. Define only the rules
|
|
55
|
+
your language actually differs on; everything else falls back to English, so a
|
|
56
|
+
two-function backend is a perfectly good start.
|
|
57
|
+
|
|
58
|
+
## Changing the code
|
|
59
|
+
|
|
60
|
+
```console
|
|
61
|
+
git clone https://github.com/hannibal414/pq-cli.git
|
|
62
|
+
cd pq-cli
|
|
63
|
+
uv sync
|
|
64
|
+
uv run pre-commit install
|
|
65
|
+
|
|
66
|
+
# Once, so the game can run in a language other than English:
|
|
67
|
+
uv run pybabel compile -d pqcli/locale -D pqcli
|
|
68
|
+
|
|
69
|
+
uv run pqcli
|
|
70
|
+
```
|
|
71
|
+
|
|
72
|
+
There is no test suite. `tools/golden.py` stands in for one: it runs five
|
|
73
|
+
seeded simulations and dumps every string they display, and the
|
|
74
|
+
`golden-output` pre-commit hook diffs that against `tools/golden_expected.txt`.
|
|
75
|
+
|
|
76
|
+
A diff there is not automatically a bug — it is the list of display changes
|
|
77
|
+
your commit makes. Read it. If it is what you intended, record it:
|
|
78
|
+
|
|
79
|
+
```console
|
|
80
|
+
uv run python tools/golden.py --update
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
Translation work never changes that dump, so the hook stays quiet while you
|
|
84
|
+
are editing catalogs.
|
|
85
|
+
|
|
86
|
+
### Adding a string to the source
|
|
87
|
+
|
|
88
|
+
Wrap it with `_()` from `pqcli.i18n`, and keep a whole sentence in one msgid
|
|
89
|
+
with named placeholders rather than concatenating pieces:
|
|
90
|
+
|
|
91
|
+
```python
|
|
92
|
+
# good -- one msgid, the translator can reorder it
|
|
93
|
+
_("Selling {item}").format(item=name)
|
|
94
|
+
|
|
95
|
+
# bad -- English word order is now baked into the code
|
|
96
|
+
_("Selling ") + name
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
Text that gets stored in a save file should be a phrase from `pqcli.text`
|
|
100
|
+
rather than a rendered string, so it can be re-rendered after a language
|
|
101
|
+
change. `_` is the gettext function: never use it as a throwaway variable in a
|
|
102
|
+
module that imports it.
|
|
103
|
+
|
|
104
|
+
## Upstream
|
|
105
|
+
|
|
106
|
+
This is a fork of [rr-/pq-cli](https://github.com/rr-/pq-cli). Changes that are
|
|
107
|
+
not about localization are often better sent there.
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
=====================
|
|
3
|
+
|
|
4
|
+
Copyright © `2018` `Marcin Kurczewski`
|
|
5
|
+
|
|
6
|
+
Copyright © `2026` `hannibal414` (Japanese localization and related changes)
|
|
7
|
+
|
|
8
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
|
9
|
+
this software and associated documentation files (the “Software”), to deal in
|
|
10
|
+
the Software without restriction, including without limitation the rights to
|
|
11
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies
|
|
12
|
+
of the Software, and to permit persons to whom the Software is furnished to do
|
|
13
|
+
so, subject to the following conditions:
|
|
14
|
+
|
|
15
|
+
The above copyright notice and this permission notice shall be included in all
|
|
16
|
+
copies or substantial portions of the Software.
|
|
17
|
+
|
|
18
|
+
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
19
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
20
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
21
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
22
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
23
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
24
|
+
SOFTWARE.
|
pqcli_ja-1.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,253 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: pqcli-ja
|
|
3
|
+
Version: 1.1.0
|
|
4
|
+
Summary: Progress Quest: the CLI edition, with Japanese localization
|
|
5
|
+
Project-URL: Repository, https://github.com/hannibal414/pq-cli.git
|
|
6
|
+
Project-URL: Upstream project, https://github.com/rr-/pq-cli
|
|
7
|
+
Author-email: Laura Kurczewska <dash@wind.garden>
|
|
8
|
+
Maintainer-email: hannibal414 <207813470+hannibal414@users.noreply.github.com>
|
|
9
|
+
License-Expression: MIT
|
|
10
|
+
License-File: LICENSE.md
|
|
11
|
+
Classifier: Development Status :: 4 - Beta
|
|
12
|
+
Classifier: Environment :: Console
|
|
13
|
+
Classifier: Intended Audience :: End Users/Desktop
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Natural Language :: English
|
|
16
|
+
Classifier: Natural Language :: Japanese
|
|
17
|
+
Classifier: Operating System :: MacOS
|
|
18
|
+
Classifier: Operating System :: Microsoft :: Windows
|
|
19
|
+
Classifier: Operating System :: POSIX :: Linux
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
22
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
23
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
24
|
+
Classifier: Programming Language :: Python :: 3.13
|
|
25
|
+
Classifier: Topic :: Games/Entertainment :: Role-Playing
|
|
26
|
+
Classifier: Topic :: Games/Entertainment :: Simulation
|
|
27
|
+
Requires-Python: <4.0,>=3.10
|
|
28
|
+
Requires-Dist: wcwidth<1.0.0,>=0.2.5
|
|
29
|
+
Requires-Dist: xdg-base-dirs<7.0.0,>=6.0.0
|
|
30
|
+
Provides-Extra: windows
|
|
31
|
+
Requires-Dist: windows-curses<3.0.0,>=2.3.0; extra == 'windows'
|
|
32
|
+
Description-Content-Type: text/markdown
|
|
33
|
+
|
|
34
|
+
<p align="center">
|
|
35
|
+
<img alt="Progress Quest" src="http://progressquest.com/pq.png">
|
|
36
|
+
</p>
|
|
37
|
+
|
|
38
|
+
*English | [日本語](README.ja.md)*
|
|
39
|
+
|
|
40
|
+
Relive the great adventure… this time in the terminal realm!
|
|
41
|
+
|
|
42
|
+
- Progress Quest site: http://progressquest.com/
|
|
43
|
+
- Online version: http://progressquest.com/play/
|
|
44
|
+
- Original version: https://bitbucket.org/grumdrig/pq
|
|
45
|
+
|
|
46
|
+
This is a fork of [rr-/pq-cli](https://github.com/rr-/pq-cli) by Laura
|
|
47
|
+
Kurczewska, adding Japanese localization and the machinery that makes the whole
|
|
48
|
+
game — not just its prose — translatable. The original MIT licence and
|
|
49
|
+
copyright are kept in `LICENSE.md`.
|
|
50
|
+
|
|
51
|
+
## Features
|
|
52
|
+
|
|
53
|
+
- Faithful port of the game logic
|
|
54
|
+
- Saves (with backups) to `$XDG_CONFIG_HOME/pqcli/save.dat`
|
|
55
|
+
- Terminal interface that comes in 2 flavors:
|
|
56
|
+
- Rich and colorful (`--curses`, default)
|
|
57
|
+
- Minimal, suitable for raw grind (`--basic`)
|
|
58
|
+
- Ideal to run on your server
|
|
59
|
+
|
|
60
|
+
## How it looks like
|
|
61
|
+
|
|
62
|
+
Curses interface:
|
|
63
|
+
|
|
64
|
+

|
|
65
|
+

|
|
66
|
+
|
|
67
|
+
Basic interface:
|
|
68
|
+
|
|
69
|
+

|
|
70
|
+
|
|
71
|
+
## How to install
|
|
72
|
+
|
|
73
|
+
If you have Python 3.10 or newer, just run `pip install --user pqcli-ja` and
|
|
74
|
+
you're good to go! Then type `pqcli` to run the game.
|
|
75
|
+
|
|
76
|
+
This is the Japanese edition, published to PyPI as `pqcli-ja`. The original
|
|
77
|
+
English-only game is a separate package named `pqcli`; both install a command
|
|
78
|
+
called `pqcli`, so install only one of them in a given environment.
|
|
79
|
+
|
|
80
|
+
In case if you want to use the git version, the process is just a bit more complex:
|
|
81
|
+
|
|
82
|
+
```console
|
|
83
|
+
$ git clone https://github.com/hannibal414/pq-cli.git
|
|
84
|
+
$ cd pq-cli
|
|
85
|
+
$ pip install --user .
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
## Docker / Docker Compose
|
|
89
|
+
|
|
90
|
+
The repository includes a `Dockerfile` and `docker-compose.yml` that run the
|
|
91
|
+
game with `--basic` and keep save data in a persistent named volume.
|
|
92
|
+
|
|
93
|
+
There is no registry pipeline required for this setup: build locally, then run
|
|
94
|
+
with Compose.
|
|
95
|
+
|
|
96
|
+
```console
|
|
97
|
+
# Build image locally from the Dockerfile
|
|
98
|
+
docker compose build
|
|
99
|
+
|
|
100
|
+
# First run (interactive): create your character in the shared save volume
|
|
101
|
+
docker compose run --rm pqcli-init
|
|
102
|
+
|
|
103
|
+
# Later runs on a server (detached, default save slot 1):
|
|
104
|
+
docker compose up -d pqcli
|
|
105
|
+
|
|
106
|
+
# List existing saves in the same volume:
|
|
107
|
+
docker compose run --rm pqcli-init pqcli --basic --list-saves
|
|
108
|
+
|
|
109
|
+
# Change detached slot if needed (example: slot 2):
|
|
110
|
+
PQCLI_SAVE_SLOT=2 docker compose up -d pqcli
|
|
111
|
+
|
|
112
|
+
# Follow logs / stop detached container:
|
|
113
|
+
docker compose logs -f pqcli
|
|
114
|
+
docker compose stop pqcli
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
On first run, if no save data exists in the mounted volume, `pqcli` will
|
|
118
|
+
automatically launch an interactive character-creation bootstrap in CLI mode
|
|
119
|
+
before starting the normal interface.
|
|
120
|
+
|
|
121
|
+
For server usage, run first-time setup without a forced slot (`pqcli-init`),
|
|
122
|
+
then run the long-lived detached service (`pqcli`) that loads slot `1` by
|
|
123
|
+
default.
|
|
124
|
+
|
|
125
|
+
## Languages
|
|
126
|
+
|
|
127
|
+
The game ships with English (the source language) and Japanese. Pick one with
|
|
128
|
+
the `PQCLI_LANG` environment variable:
|
|
129
|
+
|
|
130
|
+
```console
|
|
131
|
+
PQCLI_LANG=ja pqcli
|
|
132
|
+
PQCLI_LANG=en pqcli
|
|
133
|
+
```
|
|
134
|
+
|
|
135
|
+
When `PQCLI_LANG` is unset, the usual gettext environment is consulted
|
|
136
|
+
(`LANGUAGE`, `LC_ALL`, `LC_MESSAGES`, `LANG`). If no catalog matches, the game
|
|
137
|
+
falls back to English.
|
|
138
|
+
|
|
139
|
+
Everything the game shows is translatable, including the game-data tables in
|
|
140
|
+
`pqcli/config.py` — monster, spell, race, class, item and equipment names. Those
|
|
141
|
+
English strings are *identities*, not display text: they are what the code, the
|
|
142
|
+
save files and the msgids all agree on, and the display layer resolves them
|
|
143
|
+
through the catalog at render time. Translating one therefore means adding a
|
|
144
|
+
msgstr, never editing `pqcli/config.py`.
|
|
145
|
+
|
|
146
|
+
Text is stored as a phrase (`pqcli/text.py`) — a msgid plus its parameters —
|
|
147
|
+
rather than as a finished sentence, so a language change reaches text that was
|
|
148
|
+
generated earlier, and translators can reorder the parts of a sentence in the
|
|
149
|
+
catalog. Text written by versions before phrases existed keeps the language it
|
|
150
|
+
was generated in, since the msgid it came from cannot be recovered.
|
|
151
|
+
|
|
152
|
+
English grammar (articles, plurals, the adjective stack) lives in
|
|
153
|
+
`pqcli/lingo/en.py`. `pqcli/lingo/__init__.py` picks a backend from the active
|
|
154
|
+
catalog and falls back to English, so adding `pqcli/lingo/<lang>.py` is how a
|
|
155
|
+
language gets its own rules.
|
|
156
|
+
|
|
157
|
+
### Working on translations
|
|
158
|
+
|
|
159
|
+
Translation tooling needs the dev dependencies (`uv sync`).
|
|
160
|
+
|
|
161
|
+
```console
|
|
162
|
+
# 1. Re-extract msgids from the source into the .pot template
|
|
163
|
+
uv run pybabel extract -F babel.cfg -k N_ -k Term -k phrase \
|
|
164
|
+
-o pqcli/locale/pqcli.pot \
|
|
165
|
+
--project=pqcli --version=1.1.0 \
|
|
166
|
+
--copyright-holder="pq-cli contributors" \
|
|
167
|
+
--msgid-bugs-address="https://github.com/hannibal414/pq-cli/issues" .
|
|
168
|
+
|
|
169
|
+
# 2. Merge the template into the existing catalogs
|
|
170
|
+
uv run pybabel update -i pqcli/locale/pqcli.pot -d pqcli/locale -D pqcli
|
|
171
|
+
|
|
172
|
+
# 3. Edit pqcli/locale/<lang>/LC_MESSAGES/pqcli.po, then compile to .mo
|
|
173
|
+
uv run pybabel compile -d pqcli/locale -D pqcli --statistics
|
|
174
|
+
```
|
|
175
|
+
|
|
176
|
+
To start a new language (`de` shown here):
|
|
177
|
+
|
|
178
|
+
```console
|
|
179
|
+
uv run pybabel init -i pqcli/locale/pqcli.pot -d pqcli/locale -l de -D pqcli
|
|
180
|
+
```
|
|
181
|
+
|
|
182
|
+
The compiled `.mo` files are what the game loads at runtime, so re-run step 3
|
|
183
|
+
after editing any `.po`. The `compile-catalogs` pre-commit hook does this for
|
|
184
|
+
you whenever a `.po` changes.
|
|
185
|
+
|
|
186
|
+
`.mo` files are build output and are not in git: `hatch_build.py` compiles them
|
|
187
|
+
into the wheel and sdist. A translation pull request therefore carries only the
|
|
188
|
+
`.po` you edited — no binary, and nothing to go stale in review.
|
|
189
|
+
|
|
190
|
+
`-k N_ -k Term -k phrase` is what pulls the game data in: `N_()` marks a string
|
|
191
|
+
in `pqcli/config.py` for extraction without translating it there, `Term()` names
|
|
192
|
+
one of those strings at the point of use, and `phrase()` builds a deferred
|
|
193
|
+
sentence. When adding translatable strings to the source, wrap
|
|
194
|
+
them with `_()` from `pqcli.i18n` and prefer a single `.format()` template over
|
|
195
|
+
concatenation, so translators can reorder the parts:
|
|
196
|
+
|
|
197
|
+
```python
|
|
198
|
+
# good -- one msgid, placeholders can move
|
|
199
|
+
_("Selling {item}").format(item=indefinite(item.name, item.quantity))
|
|
200
|
+
|
|
201
|
+
# bad -- word order is baked into the code
|
|
202
|
+
_("Selling ") + indefinite(item.name, item.quantity)
|
|
203
|
+
```
|
|
204
|
+
|
|
205
|
+
Beware that `_` is the gettext function: never use it as a throwaway variable
|
|
206
|
+
name (`for _ in range(...)`) in a module that imports it.
|
|
207
|
+
|
|
208
|
+
### Checking the display did not change
|
|
209
|
+
|
|
210
|
+
There are no tests, so `tools/golden.py` stands in for one: it runs five seeded
|
|
211
|
+
simulations and dumps every string they display. `--check` diffs that against
|
|
212
|
+
`tools/golden_expected.txt` and fails on any difference; `--update` re-records
|
|
213
|
+
it once you have read the diff and want it. The `golden-output` pre-commit hook
|
|
214
|
+
runs the check for you. Translating does not change the English dump, so the
|
|
215
|
+
hook stays quiet during translation work and speaks up when code does.
|
|
216
|
+
|
|
217
|
+
## Contributing
|
|
218
|
+
|
|
219
|
+
Translating needs only a `.po` editor — see [CONTRIBUTING.md](CONTRIBUTING.md).
|
|
220
|
+
To work on the code:
|
|
221
|
+
|
|
222
|
+
```sh
|
|
223
|
+
# Clone the repository:
|
|
224
|
+
git clone https://github.com/hannibal414/pq-cli.git
|
|
225
|
+
cd pq-cli
|
|
226
|
+
|
|
227
|
+
# Install dependencies into a local venv:
|
|
228
|
+
uv sync
|
|
229
|
+
|
|
230
|
+
# Install pre-commit hooks:
|
|
231
|
+
uv run pre-commit install
|
|
232
|
+
|
|
233
|
+
# Compile the catalogs once, so the game can run in a language other than
|
|
234
|
+
# English (the hooks keep them current from here on):
|
|
235
|
+
uv run pybabel compile -d pqcli/locale -D pqcli
|
|
236
|
+
|
|
237
|
+
# Run commands inside the venv:
|
|
238
|
+
uv run pqcli
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
This project uses [uv](https://docs.astral.sh/uv/) for packaging.
|
|
242
|
+
Install instructions are available at [uv installation](https://docs.astral.sh/uv/getting-started/installation/).
|
|
243
|
+
|
|
244
|
+
## Troubleshooting
|
|
245
|
+
|
|
246
|
+
### `_curses.error: init_pair() returned ERR`
|
|
247
|
+
|
|
248
|
+
If running on Linux and you get the error `_curses.error: init_pair() returned ERR`,
|
|
249
|
+
try making sure that your `$TERM` variable is set to a value which supports
|
|
250
|
+
256 colors, such as via the following:
|
|
251
|
+
|
|
252
|
+
TERM=xterm-256color pqcli
|
|
253
|
+
|