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.
Files changed (70) hide show
  1. pqcli_ja-1.1.0/.claude/settings.local.json +53 -0
  2. pqcli_ja-1.1.0/.github/workflows/ci.yml +50 -0
  3. pqcli_ja-1.1.0/.gitignore +9 -0
  4. pqcli_ja-1.1.0/.pre-commit-config.yaml +27 -0
  5. pqcli_ja-1.1.0/CONTRIBUTING.ja.md +104 -0
  6. pqcli_ja-1.1.0/CONTRIBUTING.md +107 -0
  7. pqcli_ja-1.1.0/Dockerfile +12 -0
  8. pqcli_ja-1.1.0/LICENSE.md +24 -0
  9. pqcli_ja-1.1.0/PKG-INFO +253 -0
  10. pqcli_ja-1.1.0/README.ja.md +242 -0
  11. pqcli_ja-1.1.0/README.md +220 -0
  12. pqcli_ja-1.1.0/babel.cfg +1 -0
  13. pqcli_ja-1.1.0/docker-compose.yml +26 -0
  14. pqcli_ja-1.1.0/hatch_build.py +24 -0
  15. pqcli_ja-1.1.0/pqcli/__init__.py +0 -0
  16. pqcli_ja-1.1.0/pqcli/__main__.py +117 -0
  17. pqcli_ja-1.1.0/pqcli/config.py +750 -0
  18. pqcli_ja-1.1.0/pqcli/i18n.py +71 -0
  19. pqcli_ja-1.1.0/pqcli/lingo/__init__.py +140 -0
  20. pqcli_ja-1.1.0/pqcli/lingo/en.py +140 -0
  21. pqcli_ja-1.1.0/pqcli/locale/ja/LC_MESSAGES/pqcli.mo +0 -0
  22. pqcli_ja-1.1.0/pqcli/locale/ja/LC_MESSAGES/pqcli.po +4088 -0
  23. pqcli_ja-1.1.0/pqcli/locale/pqcli.pot +4026 -0
  24. pqcli_ja-1.1.0/pqcli/mechanic.py +1147 -0
  25. pqcli_ja-1.1.0/pqcli/random.py +26 -0
  26. pqcli_ja-1.1.0/pqcli/roster.py +40 -0
  27. pqcli_ja-1.1.0/pqcli/text.py +83 -0
  28. pqcli_ja-1.1.0/pqcli/ui/__init__.py +0 -0
  29. pqcli_ja-1.1.0/pqcli/ui/base.py +22 -0
  30. pqcli_ja-1.1.0/pqcli/ui/basic/__init__.py +253 -0
  31. pqcli_ja-1.1.0/pqcli/ui/curses/__init__.py +204 -0
  32. pqcli_ja-1.1.0/pqcli/ui/curses/colors.py +18 -0
  33. pqcli_ja-1.1.0/pqcli/ui/curses/event_handler.py +14 -0
  34. pqcli_ja-1.1.0/pqcli/ui/curses/util.py +57 -0
  35. pqcli_ja-1.1.0/pqcli/ui/curses/views/__init__.py +23 -0
  36. pqcli_ja-1.1.0/pqcli/ui/curses/views/base_view.py +18 -0
  37. pqcli_ja-1.1.0/pqcli/ui/curses/views/choose_character_view.py +64 -0
  38. pqcli_ja-1.1.0/pqcli/ui/curses/views/confirm_view.py +51 -0
  39. pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/__init__.py +11 -0
  40. pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/choose_character_class_view.py +36 -0
  41. pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/choose_character_name_view.py +121 -0
  42. pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/choose_character_race_view.py +34 -0
  43. pqcli_ja-1.1.0/pqcli/ui/curses/views/create_character_view/choose_character_stats_view.py +112 -0
  44. pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/__init__.py +263 -0
  45. pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/character_sheet_window.py +67 -0
  46. pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/equipment_window.py +69 -0
  47. pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/inventory_window.py +106 -0
  48. pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/plot_window.py +68 -0
  49. pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/progress_bar_window.py +156 -0
  50. pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/quest_book_window.py +66 -0
  51. pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/spell_book_window.py +71 -0
  52. pqcli_ja-1.1.0/pqcli/ui/curses/views/game_view/task_progress_window.py +52 -0
  53. pqcli_ja-1.1.0/pqcli/ui/curses/views/menu_view.py +41 -0
  54. pqcli_ja-1.1.0/pqcli/ui/curses/views/roster_view.py +102 -0
  55. pqcli_ja-1.1.0/pqcli/ui/curses/widgets/__init__.py +20 -0
  56. pqcli_ja-1.1.0/pqcli/ui/curses/widgets/base.py +41 -0
  57. pqcli_ja-1.1.0/pqcli/ui/curses/widgets/data_table.py +86 -0
  58. pqcli_ja-1.1.0/pqcli/ui/curses/widgets/focusable.py +42 -0
  59. pqcli_ja-1.1.0/pqcli/ui/curses/widgets/label.py +14 -0
  60. pqcli_ja-1.1.0/pqcli/ui/curses/widgets/list_box.py +57 -0
  61. pqcli_ja-1.1.0/pqcli/ui/curses/widgets/menu.py +104 -0
  62. pqcli_ja-1.1.0/pqcli/ui/curses/widgets/progress_bar.py +68 -0
  63. pqcli_ja-1.1.0/pqcli/ui/curses/widgets/scrollable.py +116 -0
  64. pqcli_ja-1.1.0/pyproject.toml +67 -0
  65. pqcli_ja-1.1.0/screen-basic.png +0 -0
  66. pqcli_ja-1.1.0/screen-curses-logo.png +0 -0
  67. pqcli_ja-1.1.0/screen-curses.png +0 -0
  68. pqcli_ja-1.1.0/tools/golden.py +155 -0
  69. pqcli_ja-1.1.0/tools/golden_expected.txt +1532 -0
  70. 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,9 @@
1
+ dist/
2
+ build/
3
+ __pycache__
4
+
5
+ # Vim undo files
6
+ *.un~
7
+
8
+ # Compiled catalogs are build output; hatch_build.py generates them.
9
+ *.mo
@@ -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,12 @@
1
+ FROM python:3.12-slim
2
+
3
+ ENV PYTHONDONTWRITEBYTECODE=1 \
4
+ PYTHONUNBUFFERED=1
5
+
6
+ WORKDIR /app
7
+
8
+ COPY . .
9
+
10
+ RUN pip install --no-cache-dir .
11
+
12
+ CMD ["pqcli", "--basic"]
@@ -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.
@@ -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
+ ![Screenshot](screen-curses-logo.png)
65
+ ![Screenshot](screen-curses.png)
66
+
67
+ Basic interface:
68
+
69
+ ![Screenshot](screen-basic.png)
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
+