From 0895439173af3c98dc75c7ab11786625a0ce5383 Mon Sep 17 00:00:00 2001 From: AlanBacker Date: Tue, 14 Jul 2026 15:24:54 +0800 Subject: [PATCH] Add complete Chinese English and Japanese localization --- .env.example | 3 + .github/workflows/ci.yml | 7 +- CHANGELOG.md | 8 + CONTRIBUTING.md | 6 +- README.ja-JP.md | 252 ++++++++++++++++++++++ README.md | 319 +++++++++++++--------------- README.zh-CN.md | 249 ++++++++++++++++++++++ docs/MIGRATION-v1.ja-JP.md | 124 +++++++++++ docs/MIGRATION-v1.md | 82 +++++--- docs/MIGRATION-v1.zh-CN.md | 120 +++++++++++ docs/RELEASE-v2.0.1.md | 84 ++++++++ locales/en_US.sh | 155 ++++++++++++++ locales/ja_JP.sh | 155 ++++++++++++++ locales/zh_CN.sh | 155 ++++++++++++++ palworld-manager.sh | 420 +++++++++++++++++++++++-------------- tests/i18n-smoke.sh | 92 ++++++++ 16 files changed, 1872 insertions(+), 359 deletions(-) create mode 100644 README.ja-JP.md create mode 100644 README.zh-CN.md create mode 100644 docs/MIGRATION-v1.ja-JP.md create mode 100644 docs/MIGRATION-v1.zh-CN.md create mode 100644 docs/RELEASE-v2.0.1.md create mode 100644 locales/en_US.sh create mode 100644 locales/ja_JP.sh create mode 100644 locales/zh_CN.sh create mode 100755 tests/i18n-smoke.sh diff --git a/.env.example b/.env.example index 71b363d..dccdf77 100644 --- a/.env.example +++ b/.env.example @@ -13,3 +13,6 @@ REST_API_PORT=8212 # External backup retention policy. BACKUP_RETENTION_DAYS=14 BACKUP_MAX_COUNT=168 + +# Interface language: auto, zh_CN, en_US, or ja_JP. +PALDSMS_LANG=auto diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 4d45c71..1722c63 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,10 +19,13 @@ jobs: run: sudo apt-get update && sudo apt-get install -y shellcheck - name: Check shell syntax - run: bash -n palworld-manager.sh && sh -n helper.sh + run: bash -n palworld-manager.sh locales/*.sh tests/*.sh && sh -n helper.sh - name: Run ShellCheck - run: shellcheck palworld-manager.sh helper.sh + run: shellcheck palworld-manager.sh helper.sh locales/*.sh tests/*.sh + + - name: Test Chinese, English, and Japanese interfaces + run: bash tests/i18n-smoke.sh - name: Validate Compose model run: cp .env.example .env && docker compose config --quiet diff --git a/CHANGELOG.md b/CHANGELOG.md index dabe782..a5ed362 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,13 @@ # Changelog +## 2.0.1 - 2026-07-14 + +- Added complete Simplified Chinese, English, and Japanese CLI localization. +- Added system-locale auto-detection plus `--lang`, `PALDSMS_LANG`, and `.env` overrides. +- Localized menus, prompts, validation errors, diagnostics, backup/restore messages, and updates. +- Added full English, Simplified Chinese, and Japanese README and v1 migration guides. +- Added CI smoke tests that verify language selection and catalog parity. + ## 2.0.0 - 2026-07-14 - Rebuilt the project around Pocketpair's official Palworld server image. diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 8b61c94..b0cd70e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -10,13 +10,15 @@ support are welcome. 3. Do not add bundled credentials, save data, backups, or unreviewed executables. 4. Keep the REST API loopback-only by default. 5. Update the README and changelog when user-visible behavior changes. +6. Add every user-facing CLI string to all three locale catalogs and keep their key sets identical. Run the same checks used by CI: ```bash -bash -n palworld-manager.sh +bash -n palworld-manager.sh locales/*.sh tests/*.sh sh -n helper.sh -shellcheck palworld-manager.sh helper.sh +shellcheck palworld-manager.sh helper.sh locales/*.sh tests/*.sh +bash tests/i18n-smoke.sh cp .env.example .env docker compose config --quiet ``` diff --git a/README.ja-JP.md b/README.ja-JP.md new file mode 100644 index 0000000..ab6ccca --- /dev/null +++ b/README.ja-JP.md @@ -0,0 +1,252 @@ +# PalDSMS — Palworld Docker サーバー管理ツール + +[English](README.md) | [简体中文](README.zh-CN.md) | **日本語** + +[![Latest release](https://img.shields.io/github/v/release/AlanBacker/PalWorld-Docker-Server-Management-System-Script?display_name=tag&sort=semver)](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest) +[![Downloads](https://img.shields.io/github/downloads/AlanBacker/PalWorld-Docker-Server-Management-System-Script/total)](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases) +[![CI](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/actions/workflows/ci.yml/badge.svg)](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Palworld server](https://img.shields.io/badge/Palworld_Server-1.0.0-38bdf8)](https://docs.palworldgame.com/) + +Linux 上で Palworld 専用サーバーをデプロイ、管理、バックアップ、復元、安全に更新するためのツールです。 + +PalDSMS v2 は Pocketpair 公式イメージと公式 REST API を使用します。旧 v1.1 パッケージの +サードパーティ製イメージや同梱 `mcrcon` バイナリには依存しません。 + +**[最新版をダウンロード](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest)** · +**[公式サーバードキュメント](https://docs.palworldgame.com/)** · +**[v1.1 から移行](docs/MIGRATION-v1.ja-JP.md)** + +> [!IMPORTANT] +> v1.1 はサードパーティ製イメージを使用しているため、上書きアップグレードはできません。 +> セーブデータをバックアップし、[移行ガイド](docs/MIGRATION-v1.ja-JP.md)に従ってください。 +> `legacy/` 内の v1.1 ZIP は履歴保存専用です。 + +## 対応状況 + +| 項目 | 対応内容 | +|---|---| +| Pocketpair サーバードキュメント | 1.0.0 系列 | +| 既定の公式イメージ | `ghcr.io/pocketpairjp/palserver:v1.0.0.100427` | +| ゲームポート | 既定 `8211/udp`、変更可能 | +| 管理 API | 公式 REST API、ホストのループバックのみにバインド | +| 対象ホスト | x86_64 Debian/Ubuntu、Docker Compose v2+ | +| セーブデータ | `./Saved`、直接参照・移行可能 | +| 自動化 | 毎時バックアップと毎日の更新確認を行う systemd timer | +| UI 言語 | 日本語、英語、簡体字中国語 | + +## 主な機能 + +- Pocketpair 公式 Compose ファイルで公開されたイメージタグを取得します。 +- 更新前に検証済みバックアップを作成し、起動失敗時は以前のタグへ戻します。 +- 停止やバックアップの前に公式 REST API でセーブの書き込みを要求します。 +- REST API は `127.0.0.1` のみに公開し、インターネットには公開しません。 +- 復元前に `tar.gz` のパスとリンクを検証し、危険なアーカイブを拒否します。 +- 復元前にもバックアップを作成し、復元後の起動失敗時は元のデータへ戻します。 +- ゲーム内ローテーションバックアップ `bIsUseBackupSaveData=True` を有効にします。 +- crontab を書き換え続けず、systemd timer で定期処理を管理します。 +- 設定、セーブ、バックアップを通常のディレクトリに保存します。 +- メニュー、入力案内、検証エラー、診断、更新表示のすべてが日英中の三言語に対応します。 + +## 必要環境 + +- x86_64 Linux(Debian または Ubuntu を推奨) +- 4 コア以上の CPU +- 16 GiB 以上のメモリ。大規模サーバーでは 32 GiB 以上を推奨 +- SSD と 20 GiB 以上の空き容量 +- ホストのファイアウォール、クラウドのセキュリティグループ、ルーターで `8211/udp` を許可 + +最初に環境チェックを実行してください。 + +```bash +./palworld-manager.sh --lang ja_JP doctor +``` + +## ダウンロード + +[最新リリース](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest)から +`PalDSMS-v2.0.1.zip` と `SHA256SUMS.txt` をダウンロードし、検証します。 + +```bash +sha256sum -c SHA256SUMS.txt +``` + +今後 `git pull` で更新したい場合は、リポジトリを直接クローンする方法も利用できます。 + +## クイックスタート + +```bash +git clone https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script.git +cd PalWorld-Docker-Server-Management-System-Script +chmod +x palworld-manager.sh helper.sh tests/i18n-smoke.sh +cp .env.example .env + +# Docker Engine と Compose v2 がすでにある場合は省略できます。 +sudo ./palworld-manager.sh --lang ja_JP install-docker + +# 公式サーバーをデプロイします。 +sudo ./palworld-manager.sh --lang ja_JP deploy + +# 管理者パスワード、ローカル REST API、ゲーム内バックアップを設定します。 +sudo ./palworld-manager.sh --lang ja_JP set-password +``` + +日本語の対話メニューを開きます。 + +```bash +sudo ./palworld-manager.sh --lang ja_JP +``` + +プレイヤーは既定で `サーバーの公開 IP:8211` に接続します。初回のダウンロードと初期化には +数分かかる場合があります。`sudo ./palworld-manager.sh logs` で進行状況を確認できます。 + +## 言語の選択 + +PalDSMS は `LC_ALL`、`LC_MESSAGES`、`LANG` から言語を自動判定します。次の優先順位で +明示的に上書きできます。 + +```bash +# 1. このコマンドだけに指定 +./palworld-manager.sh --lang ja_JP help + +# 2. 環境変数で指定 +PALDSMS_LANG=zh_CN ./palworld-manager.sh help + +# 3. .env に保存 +PALDSMS_LANG=en_US +``` + +`zh_CN` / `zh`、`en_US` / `en`、`ja_JP` / `ja` が使用できます。`.env` に `auto` を指定すると +自動判定を維持します。優先順位は `--lang` → 環境変数 → `.env` → システムロケールです。 + +## コマンド + +| コマンド | 用途 | +|---|---| +| `doctor` | CPU、メモリ、ディスク、Docker、重要設定を確認 | +| `install-docker` | Debian/Ubuntu に Docker Engine と Compose v2 を導入 | +| `deploy` | 公式イメージを取得してサーバーをデプロイ | +| `set-password` | 管理者パスワード、ローカル REST API、バックアップを設定 | +| `config` | `PalWorldSettings.ini` を編集して再起動 | +| `start` / `stop` / `restart` | サーバーの起動、停止、再起動 | +| `status` / `logs` | 状態表示またはリアルタイムログ追跡 | +| `backup` | セーブを書き込んで検証済み外部バックアップを作成 | +| `restore <ファイル>` | 失敗時の自動ロールバック付きで安全に復元 | +| `check-update` | 現在のイメージと Pocketpair 公式タグを比較 | +| `update` | バックアップ後に更新し、失敗時はロールバック | +| `install-maintenance` | 自動バックアップ・更新用 systemd timer を設定 | +| `remove-maintenance` | セーブを削除せず自動メンテナンスを削除 | + +完全なヘルプは `./palworld-manager.sh --lang ja_JP help` で表示できます。 + +## 設定 + +デプロイ設定は `.env` で変更します。 + +```dotenv +PALWORLD_IMAGE_REPOSITORY=ghcr.io/pocketpairjp/palserver +PALWORLD_IMAGE=ghcr.io/pocketpairjp/palserver:v1.0.0.100427 +PALWORLD_PORT=8211 +PALWORLD_BIND_IP=0.0.0.0 +REST_API_PORT=8212 +BACKUP_RETENTION_DAYS=14 +BACKUP_MAX_COUNT=168 +PALDSMS_LANG=auto +``` + +信頼できるミラーが必要な場合だけ `PALWORLD_IMAGE_REPOSITORY` を変更してください。タグは +引き続き Pocketpair 公式リポジトリから取得し、指定したミラーから同じタグを取得します。 + +ゲームルールを編集するには次を実行します。 + +```bash +sudo ./palworld-manager.sh config +``` + +設定ファイルは `Saved/Config/LinuxServer/PalWorldSettings.ini` です。公式の +`DefaultPalWorldSettings.ini` から作成されます。イメージ内の既定ファイルを直接編集しても反映されません。 + +## 自動メンテナンス + +```bash +sudo ./palworld-manager.sh install-maintenance +systemctl list-timers 'paldsms-*' +``` + +既定では毎時バックアップし、毎日 04:15 以降に最大 30 分のランダム遅延を加えて公式タグを +確認します。新しいタグが見つかった場合だけプレイヤーへ通知してコンテナを再作成します。 + +```bash +journalctl -u paldsms-backup.service +journalctl -u paldsms-update.service +``` + +## 既存の v2 を更新 + +```bash +git pull --ff-only +sudo ./palworld-manager.sh check-update +sudo ./palworld-manager.sh update +``` + +実行中のサーバーはバックアップ後に更新されます。停止中のサーバーはイメージタグだけ更新され、 +勝手に起動しません。新しいコンテナが準備完了にならない場合は以前のタグへ戻します。 + +## セキュリティ + +- 公開するのはゲーム用 UDP ポートだけです。`8212/tcp` をインターネットへ公開しないでください。 +- Compose は REST API をホストの `127.0.0.1` に固定します。 +- 管理者パスワードは `.paldsms-admin-password` にモード `0600` で保存され、Git の対象外です。 +- 大型更新、設定変更、復元の前には別の場所にもバックアップを保存してください。 +- 実行中の `Saved/` と `backups/` を低速または信頼できないネットワークストレージに置かないでください。 + +脆弱性の報告方法は [SECURITY.md](SECURITY.md) を参照してください。 + +## v1.1 からの移行 + +旧コンテナを上書きしないでください。旧サーバーを停止して `palworld_saved` ボリュームを v2 の +`Saved/` にコピーし、ワールド、キャラクター、接続を確認してから旧リソースを削除します。詳しくは +[v1.1 移行ガイド](docs/MIGRATION-v1.ja-JP.md)を参照してください。 + +## トラブルシューティング + +**コンテナは動作中だが接続できない:** クラウドのセキュリティグループ、ホストのファイアウォール、 +ルーターが TCP ではなく UDP を転送していることを確認してください。 + +**`status`、`backup`、アナウンスで REST API エラーになる:** +`sudo ./palworld-manager.sh set-password` を実行し、`RESTAPIEnabled=True` を確認してください。 +トラブルシューティングのために API ポートを公開してはいけません。 + +**メモリ不足で終了する:** `doctor` を実行してください。公式最低要件は 16 GiB で、大規模サーバーは +32 GiB 以上を推奨します。 + +**GHCR や自動更新へ接続できない:** まずホストのネットワークを修正してください。信頼できるミラーが +必要な場合だけ `PALWORLD_IMAGE_REPOSITORY` を変更します。 + +## プロジェクト構成 + +```text +. +├── locales/ # zh_CN、en_US、ja_JP メッセージカタログ +├── tests/ # 多言語スモークテスト +├── docs/ # 三言語の移行ガイド +├── compose.yaml # 公式イメージ用 Compose 定義 +├── helper.sh # 公式エントリーポイント互換ヘルパー +├── palworld-manager.sh # 管理ツール本体 +├── .env.example # 実行設定テンプレート +├── CHANGELOG.md # 変更履歴 +├── SECURITY.md # セキュリティポリシー +├── CONTRIBUTING.md # コントリビューションガイド +└── legacy/ # v1.1 履歴パッケージ +``` + +## 参考資料 + +- [Pocketpair Palworld Server Guide](https://docs.palworldgame.com/) +- [Pocketpair official dedicated-server Docker image](https://github.com/pocketpairjp/palworld-dedicated-server-docker) +- [Configuration parameters](https://docs.palworldgame.com/settings-and-operation/configuration/) +- [Server startup arguments](https://docs.palworldgame.com/settings-and-operation/arguments/) + +## ライセンス + +[MIT License](LICENSE) © AlanBacker diff --git a/README.md b/README.md index afe78b0..24745db 100644 --- a/README.md +++ b/README.md @@ -1,136 +1,148 @@ -# PalDSMS — 幻兽帕鲁 Docker 服务器管理系统 +# PalDSMS — Palworld Docker Server Manager + +**English** | [简体中文](README.zh-CN.md) | [日本語](README.ja-JP.md) [![Latest release](https://img.shields.io/github/v/release/AlanBacker/PalWorld-Docker-Server-Management-System-Script?display_name=tag&sort=semver)](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest) [![Downloads](https://img.shields.io/github/downloads/AlanBacker/PalWorld-Docker-Server-Management-System-Script/total)](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases) [![CI](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/actions/workflows/ci.yml/badge.svg)](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/actions/workflows/ci.yml) [![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) [![Palworld server](https://img.shields.io/badge/Palworld_Server-1.0.0-38bdf8)](https://docs.palworldgame.com/) -[![Platform](https://img.shields.io/badge/Platform-x86__64_Linux-0f172a)](#服务器要求) +[![Platform](https://img.shields.io/badge/Platform-x86__64_Linux-0f172a)](#requirements) -面向 Linux 专用服务器的《幻兽帕鲁》部署、备份、恢复与自动更新工具。 +Deploy, manage, back up, restore, and safely update a Palworld dedicated server on Linux. -PalDSMS v2 已按 Pocketpair **服务器 1.0.0** 文档重写,默认使用其官方镜像 -`ghcr.io/pocketpairjp/palserver:v1.0.0.100427`。管理操作使用官方 REST API, -不再依赖旧版 ZIP 中随附的第三方镜像和 `mcrcon` 二进制文件。 +PalDSMS v2 uses Pocketpair's official image and official REST API. It does not depend on the +third-party image or bundled `mcrcon` binary shipped by the historical v1.1 packages. -**[下载最新稳定版](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest)** · -**[查看官方服务器文档](https://docs.palworldgame.com/)** · -**[从 v1.1 迁移](docs/MIGRATION-v1.md)** +**[Download the latest release](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest)** · +**[Official server documentation](https://docs.palworldgame.com/)** · +**[Migrate from v1.1](docs/MIGRATION-v1.md)** > [!IMPORTANT] -> v1.1 使用第三方镜像,不能直接覆盖升级到 v2。请先完成存档备份,再按照 -> [迁移指南](docs/MIGRATION-v1.md)切换。仓库中的 v1.1 ZIP 只用于历史留档。 +> v1.1 used a third-party image and cannot be upgraded in place. Back up your save and follow the +> [migration guide](docs/MIGRATION-v1.md). The v1.1 ZIP files in `legacy/` are archival only. -## 适配状态 +## Compatibility -| 项目 | 当前状态 | +| Item | Supported configuration | |---|---| -| Pocketpair 服务器文档 | 1.0.0 | -| 默认官方镜像 | `ghcr.io/pocketpairjp/palserver:v1.0.0.100427` | -| 游戏端口 | `8211/udp`,可配置 | -| 管理接口 | 官方 REST API,仅映射到 `127.0.0.1` | -| 目标系统 | x86_64 Debian / Ubuntu,Docker Compose v2+ | -| 存档目录 | `./Saved`,直接可见、可迁移 | -| 自动维护 | systemd timer:每小时备份、每日检查官方更新 | - -## v2 的主要改进 - -- 使用 Pocketpair 官方镜像与官方 `Saved` 目录布局。 -- `update` 自动读取 Pocketpair 官方 Compose 文件中的当前镜像标签。 -- 更新前强制备份;新镜像启动失败时自动回滚镜像标签。 -- REST API 只映射到 `127.0.0.1`,不会把管理接口暴露到公网。 -- 外部 `tar.gz` 备份经过完整性检查,并按时间和数量双重清理。 -- 恢复前自动再备份;恢复失败时自动换回原存档。 -- 同时启用游戏内建滚动备份 `bIsUseBackupSaveData`。 -- 使用 systemd timer 代替重复写入 crontab:每小时备份、每日检查更新。 -- 支持 Debian、Ubuntu 的 Docker Engine / Compose v2 安装。 -- 不再每 4 小时强制重启;仅在管理员操作或实际更新时重启。 -- 配置、存档、备份均为普通目录,避免硬编码 Docker 内部卷路径。 - -## 服务器要求 - -Pocketpair 当前给出的基础要求: - -- x86_64 Linux(Debian / Ubuntu 等) -- 4 核以上 CPU -- 至少 16 GiB 内存,人数较多时推荐 32 GiB 以上 -- SSD,并预留足够的存档和备份空间 -- 对外开放 `8211/udp`(修改端口时同步调整防火墙和路由器) - -先运行环境检查可以发现常见问题: +| Pocketpair server documentation | 1.0.0 line | +| Default image | `ghcr.io/pocketpairjp/palserver:v1.0.0.100427` | +| Game port | Configurable; `8211/udp` by default | +| Management API | Official REST API, bound to host loopback only | +| Host | x86_64 Debian/Ubuntu with Docker Compose v2+ | +| Save data | `./Saved`, directly visible and portable | +| Automation | systemd timers for hourly backups and daily update checks | +| Interface languages | English, Simplified Chinese, Japanese | + +## Why PalDSMS + +- Tracks the image tag published in Pocketpair's official Compose file. +- Creates a verified backup before updates and rolls the image tag back if startup fails. +- Flushes saves through the official REST API before graceful stop and backup operations. +- Exposes the REST API only through `127.0.0.1`; it is not published to the internet. +- Validates `tar.gz` backups and rejects unsafe paths and links before restoring. +- Backs up the existing save before restore and automatically rolls back a failed restore. +- Enables Palworld's built-in rolling backup with `bIsUseBackupSaveData=True`. +- Uses systemd timers instead of repeatedly appending cron entries. +- Keeps configuration, save data, and backups in normal directories. +- Provides complete English, Simplified Chinese, and Japanese terminal interfaces. + +## Requirements + +- x86_64 Linux, with Debian or Ubuntu recommended +- 4 or more CPU cores +- At least 16 GiB RAM; 32 GiB or more is recommended for larger servers +- SSD storage with at least 20 GiB free +- `8211/udp` allowed through the host firewall, cloud security group, and router + +Run the built-in checks first: ```bash ./palworld-manager.sh doctor ``` -## 下载 - -推荐从 [GitHub Releases](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest) -下载 `PalDSMS-v2.0.0.zip`,并使用同一 Release 中的 `SHA256SUMS.txt` 校验文件。 +## Download -Linux 校验示例: +Download `PalDSMS-v2.0.1.zip` and `SHA256SUMS.txt` from the +[latest release](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest), +then verify the archive: ```bash sha256sum -c SHA256SUMS.txt ``` -也可以直接克隆仓库,适合希望持续使用 `git pull` 更新管理器的用户。 +Cloning the repository is also supported and is convenient for future `git pull` updates. -## 快速开始 +## Quick start ```bash git clone https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script.git cd PalWorld-Docker-Server-Management-System-Script -chmod +x palworld-manager.sh helper.sh +chmod +x palworld-manager.sh helper.sh tests/i18n-smoke.sh cp .env.example .env -# 尚未安装 Docker 时执行;已有 Compose v2 可跳过 -sudo ./palworld-manager.sh install-docker +# Skip this when Docker Engine and Compose v2 are already installed. +sudo ./palworld-manager.sh --lang en_US install-docker -# 部署官方服务器 -sudo ./palworld-manager.sh deploy +# Deploy the official server. +sudo ./palworld-manager.sh --lang en_US deploy -# 设置管理员密码,同时启用本机 REST API 和游戏内建备份 -sudo ./palworld-manager.sh set-password +# Set the administrator password and enable the local REST API and built-in backups. +sudo ./palworld-manager.sh --lang en_US set-password ``` -直接运行脚本会打开中文菜单: +Open the interactive menu: ```bash -sudo ./palworld-manager.sh +sudo ./palworld-manager.sh --lang en_US ``` -客户端默认通过 `服务器公网 IP:8211` 连接。 +Players connect to `PUBLIC_SERVER_IP:8211` by default. Initial image download and server setup can +take several minutes; use `sudo ./palworld-manager.sh logs` to follow progress. -首次启动可能需要数分钟下载和初始化服务器文件。不要因为容器刚进入 `running` -状态就立即强制终止;可使用 `sudo ./palworld-manager.sh logs` 查看进度。 +## Language selection -## 常用命令 - -| 命令 | 作用 | -|---|---| -| `doctor` | 检查架构、内存、磁盘、Docker 和关键配置 | -| `deploy` | 拉取官方镜像并部署服务器 | -| `set-password` | 设置管理员密码,启用本地 REST API 与内建备份 | -| `config` | 编辑 `PalWorldSettings.ini`,保存后重启 | -| `start` / `stop` / `restart` | 管理服务器生命周期 | -| `status` / `logs` | 查看游戏版本、容器状态或实时日志 | -| `backup` | 请求游戏落盘后创建外部备份 | -| `restore <文件>` | 安全恢复备份,失败时自动回滚 | -| `check-update` | 对比当前与 Pocketpair 官方镜像标签 | -| `update` | 备份、拉取、更新,并在失败时回滚 | -| `install-maintenance` | 安装自动备份和更新 systemd timers | -| `remove-maintenance` | 卸载自动维护,不删除存档和备份 | - -完整帮助: +PalDSMS detects `LC_ALL`, `LC_MESSAGES`, or `LANG`. Unsupported system locales fall back to English. +You can override the language in three ways, in this priority order: ```bash -./palworld-manager.sh help +# 1. One command only +./palworld-manager.sh --lang ja_JP help + +# 2. Environment variable +PALDSMS_LANG=zh_CN ./palworld-manager.sh help + +# 3. Persistent setting in .env +PALDSMS_LANG=en_US ``` -## 配置 +Accepted values and aliases: `zh_CN` / `zh`, `en_US` / `en`, and `ja_JP` / `ja`. Use `auto` in +`.env` to keep system-locale detection. -服务器运行参数在 `.env` 中修改: +## Commands + +| Command | Purpose | +|---|---| +| `doctor` | Check architecture, memory, disk, Docker, and critical settings | +| `install-docker` | Install Docker Engine and Compose v2 on Debian/Ubuntu | +| `deploy` | Pull the official image and deploy the server | +| `set-password` | Set the admin password and enable local REST API and backups | +| `config` | Edit `PalWorldSettings.ini`, then restart | +| `start` / `stop` / `restart` | Manage the server lifecycle | +| `status` / `logs` | Show server state or follow logs | +| `backup` | Flush the save and create a verified external backup | +| `restore ` | Safely restore a backup with automatic rollback | +| `check-update` | Compare the current image with Pocketpair's official tag | +| `update` | Back up, update, and roll back on startup failure | +| `install-maintenance` | Install automatic backup and update systemd timers | +| `remove-maintenance` | Remove automation without deleting saves or backups | + +Run `./palworld-manager.sh --lang en_US help` for the complete built-in help. + +## Configuration + +Edit `.env` for deployment settings: ```dotenv PALWORLD_IMAGE_REPOSITORY=ghcr.io/pocketpairjp/palserver @@ -140,52 +152,38 @@ PALWORLD_BIND_IP=0.0.0.0 REST_API_PORT=8212 BACKUP_RETENTION_DAYS=14 BACKUP_MAX_COUNT=168 +PALDSMS_LANG=auto ``` -网络环境需要可信镜像代理时,只修改 `PALWORLD_IMAGE_REPOSITORY`。`update` 会继续从 -Pocketpair 官方仓库读取版本标签,再从指定仓库拉取同名标签;不要使用来源不明的镜像。 +Only change `PALWORLD_IMAGE_REPOSITORY` when a trusted mirror is required. PalDSMS still obtains +the version tag from Pocketpair's official repository and pulls that same tag from your mirror. -游戏规则使用以下命令编辑: +Edit game rules with: ```bash sudo ./palworld-manager.sh config ``` -对应文件为: - -```text -Saved/Config/LinuxServer/PalWorldSettings.ini -``` - -脚本从镜像内的 `DefaultPalWorldSettings.ini` 创建初始配置。不要直接修改镜像内的 -默认文件,因为游戏不会读取那里的改动。 - -服务器 1.0.0 文档指出,不设置旧的多线程参数有时性能更好,因此 v2 默认只传入 -`-port`,不再强制使用 `-useperfthreads -NoAsyncLoadingThread -UseMultithreadForDS`。 +The resulting file is `Saved/Config/LinuxServer/PalWorldSettings.ini`. PalDSMS creates it from the +official `DefaultPalWorldSettings.ini`; editing the default file inside the image has no effect. -## 自动维护 +## Automatic maintenance ```bash sudo ./palworld-manager.sh install-maintenance systemctl list-timers 'paldsms-*' ``` -默认计划: - -- 每小时创建一次外部备份,最多保存 14 天 / 168 份。 -- 每天 04:15 后随机延迟最多 30 分钟,检查 Pocketpair 官方镜像标签。 -- 只有发现新镜像时才备份、通知玩家并重建容器。 - -查看执行记录: +The default schedule creates an external backup every hour and checks the official image tag daily +after 04:15 with up to 30 minutes of randomized delay. An update only restarts the server when a new +tag is available. ```bash journalctl -u paldsms-backup.service journalctl -u paldsms-update.service ``` -## 升级现有 v2 安装 - -先更新管理器,再让脚本执行存档备份和官方镜像升级: +## Upgrade an existing v2 installation ```bash git pull --ff-only @@ -193,89 +191,64 @@ sudo ./palworld-manager.sh check-update sudo ./palworld-manager.sh update ``` -`update` 会保留服务器原来的运行状态:正在运行的服务器会在备份后更新;原本停止的 -服务器只更新镜像标签,不会被意外启动。新镜像未能就绪时会自动恢复旧标签。 - -## 安全说明 - -- **只开放游戏 UDP 端口。** 不要把 `8212/tcp` 暴露到公网。 -- Compose 将 REST API 强制绑定到宿主机 `127.0.0.1`。 -- 管理员密码保存在 `.paldsms-admin-password`,权限为 `0600`,且已加入 - `.gitignore`。不要把它提交或复制到公开位置。 -- 任何升级、改配置或恢复操作前,仍建议额外保存一份异地备份。 -- `Saved/` 与 `backups/` 不应放在低性能网络盘上运行。 - -## 从 v1.1 迁移 +An already-running server is backed up and updated. A stopped server receives the new image tag but +stays stopped. If the new container does not become ready, PalDSMS restores the previous image tag. -不要直接在旧容器上覆盖部署。先备份,再把旧 Docker 卷中的存档复制到 v2 的 -`Saved/`。完整步骤见 [v1.1 迁移指南](docs/MIGRATION-v1.md)。 +## Security -## 常见问题 +- Open only the game UDP port. Never expose `8212/tcp` publicly. +- Compose binds the REST API to the host's `127.0.0.1` address. +- The admin password is stored in `.paldsms-admin-password` with mode `0600` and is ignored by Git. +- Keep an off-site backup before major game updates, configuration changes, or restores. +- Do not run `Saved/` or `backups/` from slow or untrusted network storage. -
-容器在运行,但客户端无法连接 +See [SECURITY.md](SECURITY.md) for vulnerability reporting. -确认云安全组、主机防火墙和路由器端口转发开放的是 **UDP** 端口。默认只需 -`8211/udp`,不是 TCP。修改 `.env` 中的 `PALWORLD_PORT` 后需同步修改外部规则。 +## Migration from v1.1 -
+Do not overwrite the old container. Stop it, copy the old `palworld_saved` volume into v2's +`Saved/` directory, and validate the world before removing old resources. Follow the complete +[v1.1 migration guide](docs/MIGRATION-v1.md). -
-status、backup 或广播提示 REST API 失败 +## Troubleshooting -执行 `sudo ./palworld-manager.sh set-password`,确认 -`Saved/Config/LinuxServer/PalWorldSettings.ini` 中启用了 `RESTAPIEnabled=True`。 -不要为了排障把 `8212/tcp` 映射到公网。 +**The container is running, but players cannot connect:** confirm that the cloud security group, +host firewall, and router forward the configured port as UDP, not TCP. -
+**`status`, `backup`, or announcements report a REST API error:** run +`sudo ./palworld-manager.sh set-password` and confirm `RESTAPIEnabled=True`. Do not publish the REST +API port while troubleshooting. -
-服务器频繁因内存不足退出 +**The server exits because of memory pressure:** run `doctor`. Pocketpair requires at least 16 GiB; +larger servers should use 32 GiB or more. -运行 `./palworld-manager.sh doctor`。Pocketpair 要求至少 16 GiB 内存,大型服务器 -建议 32 GiB 以上;同时检查宿主机是否还有其他高内存服务。 +**GHCR or automatic update cannot be reached:** fix host connectivity first. If a trusted registry +mirror is necessary, change only `PALWORLD_IMAGE_REPOSITORY`. -
- -
-无法访问 GHCR 或自动更新失败 - -优先修复宿主机到 GitHub/GHCR 的网络连接。确需可信代理仓库时,只修改 `.env` 中的 -`PALWORLD_IMAGE_REPOSITORY`;脚本仍会从 Pocketpair 官方 Compose 文件读取版本标签。 - -
- -## 项目结构 +## Project layout ```text . -├── compose.yaml # 官方镜像部署定义 -├── helper.sh # 与 Pocketpair 官方入口保持一致 -├── palworld-manager.sh # 管理器源码 -├── .env.example # 可复制的运行参数 -├── CHANGELOG.md # 版本变更记录 -├── SECURITY.md # 安全策略与报告方式 -├── CONTRIBUTING.md # 贡献与验证要求 -├── Saved/ # 运行后生成,不纳入 Git -├── backups/ # 外部备份,不纳入 Git -└── legacy/ # v1.1 历史 ZIP +├── locales/ # zh_CN, en_US, and ja_JP message catalogs +├── tests/ # localization smoke tests +├── docs/ # migration guides in three languages +├── compose.yaml # official-image deployment model +├── helper.sh # helper compatible with the official entrypoint +├── palworld-manager.sh # manager source +├── .env.example # runtime settings template +├── CHANGELOG.md # release history +├── SECURITY.md # security policy +├── CONTRIBUTING.md # contribution and validation guide +└── legacy/ # archived v1.1 packages ``` -## Compatibility / English summary - -PalDSMS v2 targets the current Pocketpair dedicated-server 1.0.0 line and uses -Pocketpair's official GHCR image. It supports deployment, local-only REST API -management, validated backups, rollback-safe restores, official-tag updates, -and systemd automation on x86_64 Debian/Ubuntu hosts. Run -`./palworld-manager.sh help` for the command list. - -## 参考资料 +## References - [Pocketpair Palworld Server Guide](https://docs.palworldgame.com/) - [Pocketpair official dedicated-server Docker image](https://github.com/pocketpairjp/palworld-dedicated-server-docker) - [Configuration parameters](https://docs.palworldgame.com/settings-and-operation/configuration/) - [Server startup arguments](https://docs.palworldgame.com/settings-and-operation/arguments/) -## 许可 +## License [MIT License](LICENSE) © AlanBacker diff --git a/README.zh-CN.md b/README.zh-CN.md new file mode 100644 index 0000000..c478860 --- /dev/null +++ b/README.zh-CN.md @@ -0,0 +1,249 @@ +# PalDSMS — 幻兽帕鲁 Docker 服务器管理系统 + +[English](README.md) | **简体中文** | [日本語](README.ja-JP.md) + +[![Latest release](https://img.shields.io/github/v/release/AlanBacker/PalWorld-Docker-Server-Management-System-Script?display_name=tag&sort=semver)](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest) +[![Downloads](https://img.shields.io/github/downloads/AlanBacker/PalWorld-Docker-Server-Management-System-Script/total)](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases) +[![CI](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/actions/workflows/ci.yml/badge.svg)](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/actions/workflows/ci.yml) +[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](LICENSE) +[![Palworld server](https://img.shields.io/badge/Palworld_Server-1.0.0-38bdf8)](https://docs.palworldgame.com/) + +用于在 Linux 上部署、管理、备份、恢复和安全更新《幻兽帕鲁》专用服务器。 + +PalDSMS v2 使用 Pocketpair 官方镜像与官方 REST API,不再依赖 v1.1 历史包中的第三方 +镜像和 `mcrcon` 二进制文件。 + +**[下载最新稳定版](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest)** · +**[官方服务器文档](https://docs.palworldgame.com/)** · +**[从 v1.1 迁移](docs/MIGRATION-v1.zh-CN.md)** + +> [!IMPORTANT] +> v1.1 使用第三方镜像,不能直接覆盖升级。请先备份存档,再按 +> [迁移指南](docs/MIGRATION-v1.zh-CN.md)操作。`legacy/` 中的 v1.1 ZIP 仅作历史留档。 + +## 适配状态 + +| 项目 | 当前状态 | +|---|---| +| Pocketpair 服务器文档 | 1.0.0 系列 | +| 默认官方镜像 | `ghcr.io/pocketpairjp/palserver:v1.0.0.100427` | +| 游戏端口 | 默认 `8211/udp`,可配置 | +| 管理接口 | 官方 REST API,仅绑定宿主机回环地址 | +| 目标系统 | x86_64 Debian/Ubuntu,Docker Compose v2+ | +| 存档目录 | `./Saved`,直接可见、方便迁移 | +| 自动维护 | systemd timer:每小时备份、每日检查更新 | +| 界面语言 | 简体中文、英文、日文 | + +## 主要能力 + +- 自动读取 Pocketpair 官方 Compose 文件中的镜像标签。 +- 更新前创建经过校验的备份;新镜像启动失败时回滚旧标签。 +- 停服与备份前通过官方 REST API 请求游戏落盘。 +- REST API 仅映射到 `127.0.0.1`,不暴露到公网。 +- 恢复前检查压缩包路径与链接,拒绝危险备份。 +- 恢复前再备份一次,恢复后启动失败会自动换回原存档。 +- 启用游戏内建滚动备份 `bIsUseBackupSaveData=True`。 +- 使用 systemd timer 管理定时任务,不重复污染 crontab。 +- 配置、存档和备份均保存在普通目录中。 +- 所有菜单、提示、校验错误、诊断与更新信息均支持中英日三语。 + +## 服务器要求 + +- x86_64 Linux,推荐 Debian 或 Ubuntu +- 4 核以上 CPU +- 至少 16 GiB 内存,大型服务器推荐 32 GiB 以上 +- SSD,并至少预留 20 GiB 空间 +- 在主机防火墙、云安全组和路由器上开放 `8211/udp` + +先运行环境检查: + +```bash +./palworld-manager.sh --lang zh_CN doctor +``` + +## 下载 + +从 [最新 Release](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/releases/latest) +下载 `PalDSMS-v2.0.1.zip` 和 `SHA256SUMS.txt`,然后校验: + +```bash +sha256sum -c SHA256SUMS.txt +``` + +也可以直接克隆仓库,方便以后通过 `git pull` 更新管理器。 + +## 快速开始 + +```bash +git clone https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script.git +cd PalWorld-Docker-Server-Management-System-Script +chmod +x palworld-manager.sh helper.sh tests/i18n-smoke.sh +cp .env.example .env + +# 已安装 Docker Engine 与 Compose v2 时可跳过 +sudo ./palworld-manager.sh --lang zh_CN install-docker + +# 部署官方服务器 +sudo ./palworld-manager.sh --lang zh_CN deploy + +# 设置管理员密码,并启用本机 REST API 和游戏内建备份 +sudo ./palworld-manager.sh --lang zh_CN set-password +``` + +打开中文交互菜单: + +```bash +sudo ./palworld-manager.sh --lang zh_CN +``` + +客户端默认通过 `服务器公网 IP:8211` 连接。首次下载和初始化可能需要数分钟,可使用 +`sudo ./palworld-manager.sh logs` 查看进度。 + +## 语言选择 + +PalDSMS 会读取 `LC_ALL`、`LC_MESSAGES` 或 `LANG` 自动判断语言。也可以按以下优先级覆盖: + +```bash +# 1. 仅当前命令 +./palworld-manager.sh --lang ja_JP help + +# 2. 环境变量 +PALDSMS_LANG=zh_CN ./palworld-manager.sh help + +# 3. 在 .env 中持久设置 +PALDSMS_LANG=en_US +``` + +支持 `zh_CN` / `zh`、`en_US` / `en`、`ja_JP` / `ja`。`.env` 中使用 `auto` 可保持 +自动识别。优先级为 `--lang` → 环境变量 → `.env` → 系统区域设置。 + +## 常用命令 + +| 命令 | 作用 | +|---|---| +| `doctor` | 检查架构、内存、磁盘、Docker 与关键配置 | +| `install-docker` | 在 Debian/Ubuntu 安装 Docker Engine 与 Compose v2 | +| `deploy` | 拉取官方镜像并部署服务器 | +| `set-password` | 设置管理员密码,启用本地 REST API 与内建备份 | +| `config` | 编辑 `PalWorldSettings.ini`,保存后重启 | +| `start` / `stop` / `restart` | 管理服务器生命周期 | +| `status` / `logs` | 查看状态或跟踪实时日志 | +| `backup` | 请求游戏落盘后创建外部备份 | +| `restore <文件>` | 安全恢复备份,失败时自动回滚 | +| `check-update` | 对比当前与 Pocketpair 官方镜像标签 | +| `update` | 备份、拉取、更新,并在失败时回滚 | +| `install-maintenance` | 安装自动备份与更新 systemd timers | +| `remove-maintenance` | 移除自动维护,不删除存档和备份 | + +完整帮助:`./palworld-manager.sh --lang zh_CN help`。 + +## 配置 + +服务器运行参数位于 `.env`: + +```dotenv +PALWORLD_IMAGE_REPOSITORY=ghcr.io/pocketpairjp/palserver +PALWORLD_IMAGE=ghcr.io/pocketpairjp/palserver:v1.0.0.100427 +PALWORLD_PORT=8211 +PALWORLD_BIND_IP=0.0.0.0 +REST_API_PORT=8212 +BACKUP_RETENTION_DAYS=14 +BACKUP_MAX_COUNT=168 +PALDSMS_LANG=auto +``` + +只有在确实需要可信镜像代理时才修改 `PALWORLD_IMAGE_REPOSITORY`。脚本仍会从 Pocketpair +官方仓库获取标签,再从指定仓库拉取同名标签。 + +使用以下命令编辑游戏规则: + +```bash +sudo ./palworld-manager.sh config +``` + +配置文件为 `Saved/Config/LinuxServer/PalWorldSettings.ini`。脚本会从官方 +`DefaultPalWorldSettings.ini` 创建初始配置;直接改镜像内的默认文件不会生效。 + +## 自动维护 + +```bash +sudo ./palworld-manager.sh install-maintenance +systemctl list-timers 'paldsms-*' +``` + +默认每小时创建一次外部备份,每天 04:15 后随机延迟最多 30 分钟检查官方标签。只有 +发现新镜像时才会通知玩家并重建容器。 + +```bash +journalctl -u paldsms-backup.service +journalctl -u paldsms-update.service +``` + +## 升级现有 v2 安装 + +```bash +git pull --ff-only +sudo ./palworld-manager.sh check-update +sudo ./palworld-manager.sh update +``` + +原本运行中的服务器会先备份再更新;原本停止的服务器只更新镜像标签,不会被意外启动。 +新容器未能就绪时会自动恢复旧标签。 + +## 安全说明 + +- 只开放游戏 UDP 端口,绝不要把 `8212/tcp` 暴露到公网。 +- Compose 会把 REST API 强制绑定到宿主机 `127.0.0.1`。 +- 管理员密码保存在 `.paldsms-admin-password`,权限为 `0600`,且已被 Git 忽略。 +- 大版本更新、改配置或恢复前,请额外保留一份异地备份。 +- 不要把运行中的 `Saved/` 和 `backups/` 放在低性能或不可信的网络盘上。 + +漏洞报告方式见 [SECURITY.md](SECURITY.md)。 + +## 从 v1.1 迁移 + +不要覆盖旧容器。停止旧服务器后,把 `palworld_saved` 卷复制到 v2 的 `Saved/`,验证世界、 +角色和连接均正常后再清理旧资源。完整步骤见 +[v1.1 迁移指南](docs/MIGRATION-v1.zh-CN.md)。 + +## 常见问题 + +**容器运行但客户端无法连接:** 确认云安全组、主机防火墙和路由器转发的是 UDP 端口, +不是 TCP。 + +**`status`、`backup` 或广播提示 REST API 失败:** 运行 +`sudo ./palworld-manager.sh set-password`,确认 `RESTAPIEnabled=True`,不要为了排障公开 API 端口。 + +**服务器因内存不足退出:** 运行 `doctor`。官方最低要求是 16 GiB,大型服务器建议 +32 GiB 以上。 + +**无法访问 GHCR 或自动更新失败:** 优先修复主机网络。确需可信代理时只修改 +`PALWORLD_IMAGE_REPOSITORY`。 + +## 项目结构 + +```text +. +├── locales/ # zh_CN、en_US、ja_JP 语言包 +├── tests/ # 国际化冒烟测试 +├── docs/ # 三语迁移指南 +├── compose.yaml # 官方镜像部署定义 +├── helper.sh # 与官方入口兼容的辅助脚本 +├── palworld-manager.sh # 管理器源码 +├── .env.example # 运行参数模板 +├── CHANGELOG.md # 版本变更记录 +├── SECURITY.md # 安全策略 +├── CONTRIBUTING.md # 贡献与验证要求 +└── legacy/ # v1.1 历史包 +``` + +## 参考资料 + +- [Pocketpair Palworld Server Guide](https://docs.palworldgame.com/) +- [Pocketpair official dedicated-server Docker image](https://github.com/pocketpairjp/palworld-dedicated-server-docker) +- [Configuration parameters](https://docs.palworldgame.com/settings-and-operation/configuration/) +- [Server startup arguments](https://docs.palworldgame.com/settings-and-operation/arguments/) + +## 许可 + +[MIT License](LICENSE) © AlanBacker diff --git a/docs/MIGRATION-v1.ja-JP.md b/docs/MIGRATION-v1.ja-JP.md new file mode 100644 index 0000000..d7f635d --- /dev/null +++ b/docs/MIGRATION-v1.ja-JP.md @@ -0,0 +1,124 @@ +# PalDSMS v1.1 から v2 への移行 + +[English](MIGRATION-v1.md) | [简体中文](MIGRATION-v1.zh-CN.md) | **日本語** + +v1.1 はサードパーティ製 `kagurazakanyaa/palworld` イメージと `palworld_saved` という +Docker ボリュームを使用していました。v2 は Pocketpair 公式イメージを使用し、同じ階層の +データをリポジトリ内の `Saved/` に保存します。 + +セーブ形式の変換は不要ですが、コピー前に旧サーバーを停止する必要があります。v2 を完全に +確認するまで、旧ボリュームと別の場所に保存したアーカイブを残してください。 + +## 1. v1.1 の最終バックアップを作成 + +最初に v1.1 の手動バックアップを実行し、Docker ボリューム全体も保存します。 + +```bash +sudo docker stop palworld-server +sudo tar -C /var/lib/docker/volumes/palworld_saved/_data \ + -czf "$HOME/palworld-v1-volume-$(date +%Y%m%d-%H%M%S).tar.gz" . +``` + +Docker のデータルートが `/var/lib/docker` ではない場合は確認します。 + +```bash +sudo docker info --format '{{.DockerRootDir}}' +``` + +続行前にアーカイブを読み取れることを確認します。 + +```bash +tar -tzf "$HOME"/palworld-v1-volume-*.tar.gz | head +``` + +## 2. 旧コンテナを保持 + +v2 も `palworld-server` という名前を使用します。削除せず、停止した v1 コンテナを変更します。 + +```bash +sudo docker rename palworld-server palworld-server-v1 +``` + +## 3. v2 を準備 + +```bash +git clone https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script.git +cd PalWorld-Docker-Server-Management-System-Script +chmod +x palworld-manager.sh helper.sh tests/i18n-smoke.sh +cp .env.example .env +mkdir -p Saved +``` + +新しいサーバーを起動する前に、`.env` の `PALWORLD_PORT` などを調整してください。 + +## 4. 旧ボリュームをコピー + +Docker にボリュームをマウントさせるため、データルートが `/var/lib/docker` 以外でも動作します。 + +```bash +sudo docker run --rm \ + -v palworld_saved:/source:ro \ + -v "$PWD/Saved:/target" \ + --entrypoint /bin/sh \ + ghcr.io/pocketpairjp/palserver:v1.0.0.100427 \ + -c 'cp -a /source/. /target/' +``` + +少なくとも次のパスが存在することを確認します。 + +```text +Saved/Config/LinuxServer/PalWorldSettings.ini +Saved/SaveGames/0//Level.sav +``` + +どちらかが存在しない場合、旧ボリュームを削除または上書きしないでください。 + +## 5. v2 を起動して確認 + +```bash +sudo ./palworld-manager.sh --lang ja_JP start +sudo ./palworld-manager.sh --lang ja_JP set-password +sudo ./palworld-manager.sh --lang ja_JP status +sudo ./palworld-manager.sh --lang ja_JP logs +``` + +旧リソースを削除する前に、次をすべて確認します。 + +- クライアントが想定したサーバーへ接続できる。 +- ワールド、ギルド、プレイヤー、キャラクターデータが正しい。 +- `status` が公式 REST API を読み取れる。 +- 停止して再起動しても同じセーブが読み込まれる。 + +確認後、すぐに新しい v2 バックアップを作成します。 + +```bash +sudo ./palworld-manager.sh --lang ja_JP backup +``` + +## 6. 安定稼働後にクリーンアップ + +次のコマンドは旧コンテナを完全に削除します。v2 の確認と別場所へのバックアップが完了した後だけ +実行してください。 + +```bash +sudo docker rm palworld-server-v1 +``` + +旧 `palworld_saved` ボリュームはさらに一定期間残すことを推奨します。削除は元に戻せません。 + +```bash +sudo docker volume rm palworld_saved +``` + +## v1.1 へ戻す + +クリーンアップ前に v2 の使用を中止する場合、v2 コンテナを停止して名前を変更し、保持していた +v1 コンテナを元の名前に戻して起動します。同じポートや書き込み可能なセーブを両方のサーバーで +同時に使用しないでください。 + +```bash +sudo docker stop palworld-server +sudo docker rename palworld-server palworld-server-v2-failed +sudo docker rename palworld-server-v1 palworld-server +sudo docker start palworld-server +``` diff --git a/docs/MIGRATION-v1.md b/docs/MIGRATION-v1.md index 97b05b1..14390fe 100644 --- a/docs/MIGRATION-v1.md +++ b/docs/MIGRATION-v1.md @@ -1,15 +1,17 @@ -# 从 PalDSMS v1.1 迁移到 v2 +# Migrate from PalDSMS v1.1 to v2 -v1.1 使用第三方 `kagurazakanyaa/palworld` 镜像和名为 `palworld_saved` 的 Docker -卷。v2 使用 Pocketpair 官方镜像,并把同一层级的存档保存在仓库的 `Saved/` 目录。 +**English** | [简体中文](MIGRATION-v1.zh-CN.md) | [日本語](MIGRATION-v1.ja-JP.md) -迁移不会改变存档格式,但必须停止旧服务器后再复制数据,避免得到不一致的存档。 +v1.1 used the third-party `kagurazakanyaa/palworld` image and a Docker volume named +`palworld_saved`. v2 uses the official Pocketpair image and stores the equivalent data in the +repository's `Saved/` directory. -## 1. 在旧版本创建最后备份 +The save format does not need conversion, but the old server must be stopped before copying data. +Keep the old volume and an off-site archive until the v2 server has been fully verified. -进入旧脚本目录,先使用 v1.1 的手动备份功能。确认备份文件可读取后再继续。 +## 1. Create the final v1.1 backup -还可以额外保存整个 Docker 卷: +Use the v1.1 manual backup function first. Also preserve the whole Docker volume: ```bash sudo docker stop palworld-server @@ -17,33 +19,41 @@ sudo tar -C /var/lib/docker/volumes/palworld_saved/_data \ -czf "$HOME/palworld-v1-volume-$(date +%Y%m%d-%H%M%S).tar.gz" . ``` -如果 Docker 的数据根目录不是 `/var/lib/docker`,请先执行: +If Docker uses a different data root, locate it with: ```bash sudo docker info --format '{{.DockerRootDir}}' ``` -## 2. 保留旧容器名称 +Verify that the archive can be listed before continuing: -v2 也使用 `palworld-server` 作为容器名。不要立即删除旧容器,先重命名以便回退: +```bash +tar -tzf "$HOME"/palworld-v1-volume-*.tar.gz | head +``` + +## 2. Preserve the old container + +v2 also uses the name `palworld-server`. Rename the stopped v1 container instead of deleting it: ```bash sudo docker rename palworld-server palworld-server-v1 ``` -## 3. 准备 v2 +## 3. Prepare v2 ```bash git clone https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script.git cd PalWorld-Docker-Server-Management-System-Script -chmod +x palworld-manager.sh helper.sh +chmod +x palworld-manager.sh helper.sh tests/i18n-smoke.sh cp .env.example .env mkdir -p Saved ``` -## 4. 复制旧卷 +Adjust `PALWORLD_PORT` or other deployment values in `.env` before starting the new server. -使用 Docker 自己解析卷位置,避免依赖 `/var/lib/docker`: +## 4. Copy the old volume + +Let Docker mount the volume so this works even when Docker's data root is not `/var/lib/docker`: ```bash sudo docker run --rm \ @@ -54,38 +64,60 @@ sudo docker run --rm \ -c 'cp -a /source/. /target/' ``` -复制完成后确认以下内容存在: +Confirm that at least these paths exist: ```text Saved/Config/LinuxServer/PalWorldSettings.ini Saved/SaveGames/0//Level.sav ``` -## 5. 启动并检查 +Do not delete or overwrite the old volume if either path is missing. + +## 5. Start and verify v2 ```bash -sudo ./palworld-manager.sh start -sudo ./palworld-manager.sh set-password -sudo ./palworld-manager.sh status -sudo ./palworld-manager.sh logs +sudo ./palworld-manager.sh --lang en_US start +sudo ./palworld-manager.sh --lang en_US set-password +sudo ./palworld-manager.sh --lang en_US status +sudo ./palworld-manager.sh --lang en_US logs ``` -确认客户端可以连接、角色和世界正确后,立即创建一份 v2 备份: +Verify all of the following before cleanup: + +- The client connects to the expected server. +- The world, guilds, players, and character data are correct. +- `status` can read the official REST API. +- A stop/start cycle keeps the same save. + +Create a fresh v2 backup immediately after verification: ```bash -sudo ./palworld-manager.sh backup +sudo ./palworld-manager.sh --lang en_US backup ``` -## 6. 确认稳定后清理旧资源 +## 6. Clean up only after a stable period -以下命令会删除旧容器,只有在 v2 已验证并且异地备份存在时才执行: +The following command permanently removes the old container. Run it only after v2 is verified and +an off-site backup exists: ```bash sudo docker rm palworld-server-v1 ``` -旧的 `palworld_saved` 卷建议再保留一段时间。需要删除时必须明确指定: +Keep the old `palworld_saved` volume for an additional safety period. Its deletion is permanent: ```bash sudo docker volume rm palworld_saved ``` + +## Roll back to v1.1 + +If v2 must be abandoned before cleanup, stop and rename the v2 container, rename the preserved v1 +container back, then start it. Do not run both servers against the same port or writable save data. + +```bash +sudo docker stop palworld-server +sudo docker rename palworld-server palworld-server-v2-failed +sudo docker rename palworld-server-v1 palworld-server +sudo docker start palworld-server +``` diff --git a/docs/MIGRATION-v1.zh-CN.md b/docs/MIGRATION-v1.zh-CN.md new file mode 100644 index 0000000..cc584bf --- /dev/null +++ b/docs/MIGRATION-v1.zh-CN.md @@ -0,0 +1,120 @@ +# 从 PalDSMS v1.1 迁移到 v2 + +[English](MIGRATION-v1.md) | **简体中文** | [日本語](MIGRATION-v1.ja-JP.md) + +v1.1 使用第三方 `kagurazakanyaa/palworld` 镜像和名为 `palworld_saved` 的 Docker 卷。 +v2 使用 Pocketpair 官方镜像,并把同一层级的数据保存在仓库的 `Saved/` 目录。 + +存档格式不需要转换,但复制数据前必须停止旧服务器。完整验证 v2 之前,请保留旧卷和异地压缩包。 + +## 1. 创建最后一份 v1.1 备份 + +先使用 v1.1 的手动备份功能,再额外保存整个 Docker 卷: + +```bash +sudo docker stop palworld-server +sudo tar -C /var/lib/docker/volumes/palworld_saved/_data \ + -czf "$HOME/palworld-v1-volume-$(date +%Y%m%d-%H%M%S).tar.gz" . +``` + +如果 Docker 数据根目录不是 `/var/lib/docker`,先查询: + +```bash +sudo docker info --format '{{.DockerRootDir}}' +``` + +继续之前确认压缩包能够读取: + +```bash +tar -tzf "$HOME"/palworld-v1-volume-*.tar.gz | head +``` + +## 2. 保留旧容器 + +v2 也会使用 `palworld-server` 这个名称。不要删除旧容器,先重命名: + +```bash +sudo docker rename palworld-server palworld-server-v1 +``` + +## 3. 准备 v2 + +```bash +git clone https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script.git +cd PalWorld-Docker-Server-Management-System-Script +chmod +x palworld-manager.sh helper.sh tests/i18n-smoke.sh +cp .env.example .env +mkdir -p Saved +``` + +启动新服务器前,在 `.env` 中调整 `PALWORLD_PORT` 等部署参数。 + +## 4. 复制旧卷 + +让 Docker 自己挂载卷,因此即使数据根目录不是 `/var/lib/docker` 也能工作: + +```bash +sudo docker run --rm \ + -v palworld_saved:/source:ro \ + -v "$PWD/Saved:/target" \ + --entrypoint /bin/sh \ + ghcr.io/pocketpairjp/palserver:v1.0.0.100427 \ + -c 'cp -a /source/. /target/' +``` + +确认至少存在以下路径: + +```text +Saved/Config/LinuxServer/PalWorldSettings.ini +Saved/SaveGames/0//Level.sav +``` + +任一路径缺失时都不要删除或覆盖旧卷。 + +## 5. 启动并验证 v2 + +```bash +sudo ./palworld-manager.sh --lang zh_CN start +sudo ./palworld-manager.sh --lang zh_CN set-password +sudo ./palworld-manager.sh --lang zh_CN status +sudo ./palworld-manager.sh --lang zh_CN logs +``` + +清理旧资源前逐项确认: + +- 客户端连接到预期的服务器。 +- 世界、公会、玩家和角色数据正确。 +- `status` 能读取官方 REST API。 +- 停止再启动后仍然加载同一存档。 + +验证后立即创建一份新的 v2 备份: + +```bash +sudo ./palworld-manager.sh --lang zh_CN backup +``` + +## 6. 稳定运行一段时间后再清理 + +以下命令会永久删除旧容器。只有 v2 已验证且异地备份存在时才执行: + +```bash +sudo docker rm palworld-server-v1 +``` + +建议继续保留旧的 `palworld_saved` 卷一段时间。删除操作不可恢复: + +```bash +sudo docker volume rm palworld_saved +``` + +## 回退到 v1.1 + +如果清理前需要放弃 v2,请停止并重命名 v2 容器,把保留的 v1 容器改回原名后启动。不要让 +两台服务器同时使用同一个端口或可写存档。 + +```bash +sudo docker stop palworld-server +sudo docker rename palworld-server palworld-server-v2-failed +sudo docker rename palworld-server-v1 palworld-server +sudo docker start palworld-server +``` diff --git a/docs/RELEASE-v2.0.1.md b/docs/RELEASE-v2.0.1.md new file mode 100644 index 0000000..e961eed --- /dev/null +++ b/docs/RELEASE-v2.0.1.md @@ -0,0 +1,84 @@ +# PalDSMS v2.0.1 — Complete multilingual support + +[English](#english) · [简体中文](#简体中文) · [日本語](#日本語) + +## English + +PalDSMS now provides a complete English, Simplified Chinese, and Japanese experience across the +interactive menu, prompts, diagnostics, validation errors, backup and restore output, and update +workflow. + +### Highlights + +- Added complete `zh_CN`, `en_US`, and `ja_JP` message catalogs. +- Added automatic detection from `LC_ALL`, `LC_MESSAGES`, and `LANG`. +- Added explicit selection through `--lang`, `PALDSMS_LANG`, or `.env`. +- Added full README and v1.1 migration guides in all three languages. +- Added CI coverage for language selection, catalog parity, and missing translation keys. +- Kept the v2.0.0 official-image deployment, loopback-only REST API, verified backups, and + rollback-safe update behavior unchanged. + +Upgrade from v2.0.0: + +```bash +git pull --ff-only +./palworld-manager.sh --lang en_US help +``` + +No save-data or configuration migration is required. Release archives include `SHA256SUMS.txt` for +verification. v1.1 users must still follow the +[migration guide](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/blob/v2.0.1/docs/MIGRATION-v1.md). + +## 简体中文 + +PalDSMS 现已为交互菜单、输入提示、环境诊断、参数校验、备份恢复和更新流程提供完整的 +简体中文、英文与日文界面。 + +### 更新亮点 + +- 新增完整的 `zh_CN`、`en_US`、`ja_JP` 语言包。 +- 支持通过 `LC_ALL`、`LC_MESSAGES`、`LANG` 自动识别系统语言。 +- 支持使用 `--lang`、`PALDSMS_LANG` 或 `.env` 明确选择语言。 +- 补齐三套完整 README 与 v1.1 迁移指南。 +- CI 会检查语言选择、三套语言键一致性和脚本引用漏翻问题。 +- 完整保留 v2.0.0 的官方镜像部署、本机 REST API、安全备份与失败回滚机制。 + +从 v2.0.0 升级: + +```bash +git pull --ff-only +./palworld-manager.sh --lang zh_CN help +``` + +无需迁移存档或配置。Release 压缩包附带 `SHA256SUMS.txt` 用于校验。v1.1 用户仍需按照 +[迁移指南](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/blob/v2.0.1/docs/MIGRATION-v1.zh-CN.md)切换到官方镜像布局。 + +## 日本語 + +PalDSMS の対話メニュー、入力案内、環境診断、検証エラー、バックアップ・復元、更新処理が、 +日本語・英語・簡体字中国語の完全な三言語表示に対応しました。 + +### 主な変更 + +- 完全な `zh_CN`、`en_US`、`ja_JP` メッセージカタログを追加しました。 +- `LC_ALL`、`LC_MESSAGES`、`LANG` からの自動言語判定に対応しました。 +- `--lang`、`PALDSMS_LANG`、`.env` による明示的な言語選択に対応しました。 +- 三言語の完全な README と v1.1 移行ガイドを追加しました。 +- 言語選択、カタログのキー一致、翻訳漏れを CI で検証します。 +- v2.0.0 の公式イメージ、ローカル限定 REST API、検証済みバックアップ、失敗時の + ロールバック動作はそのまま維持されています。 + +v2.0.0 からの更新: + +```bash +git pull --ff-only +./palworld-manager.sh --lang ja_JP help +``` + +セーブデータや設定の移行は不要です。リリースアーカイブには検証用の `SHA256SUMS.txt` が +含まれます。v1.1 の利用者は引き続き +[移行ガイド](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/blob/v2.0.1/docs/MIGRATION-v1.ja-JP.md)に従ってください。 + +--- + +Full changelog: [v2.0.0...v2.0.1](https://github.com/AlanBacker/PalWorld-Docker-Server-Management-System-Script/compare/v2.0.0...v2.0.1) diff --git a/locales/en_US.sh b/locales/en_US.sh new file mode 100644 index 0000000..1860be5 --- /dev/null +++ b/locales/en_US.sh @@ -0,0 +1,155 @@ +# shellcheck shell=bash +# shellcheck disable=SC2034 + +declare -Ag MSG + +MSG[label_info]='INFO' +MSG[label_success]='DONE' +MSG[label_warning]='WARNING' +MSG[label_error]='ERROR' + +MSG[missing_command]='Required command not found: %s' +MSG[root_required]='This operation requires root privileges. Run it with sudo.' +MSG[file_not_found]='File not found: %s' +MSG[env_created]='Created .env from .env.example.' +MSG[compose_v2_required]='Docker Compose v2 (docker compose) is required.' +MSG[docker_unavailable]='Docker is unavailable, or the current user cannot access it.' + +MSG[server_not_running_for_config]='The server is not running, so the official default configuration cannot be read.' +MSG[default_config_read_failed]='Could not read DefaultPalWorldSettings.ini from the official image.' +MSG[default_config_empty]='The official default configuration is empty.' +MSG[config_generated]='Generated %s' +MSG[prompt_new_password]='Enter a new administrator password (input is hidden): ' +MSG[prompt_confirm_password]='Enter it again: ' +MSG[password_mismatch]='The passwords do not match.' +MSG[password_noninteractive]='In non-interactive mode, provide the password through PALWORLD_ADMIN_PASSWORD.' +MSG[password_empty]='The administrator password cannot be empty.' +MSG[password_invalid_chars]='The password cannot contain double quotes or line breaks.' +MSG[password_short]='Use a random administrator password of at least 12 characters.' +MSG[rest_port_invalid]='Invalid REST_API_PORT: %s' +MSG[rest_port_out_of_range]='REST_API_PORT is out of range: %s' +MSG[config_key_missing]='The official default configuration does not contain %s.' +MSG[password_write_check_failed]='Password verification after writing failed. The original configuration backup is still available.' +MSG[rest_api_write_check_failed]='REST API configuration verification failed.' +MSG[rest_port_write_check_failed]='REST API port verification failed.' +MSG[rcon_write_check_failed]='RCON disablement verification failed.' +MSG[builtin_backup_write_check_failed]='Built-in backup configuration verification failed.' +MSG[rest_api_enabled]='The REST API is enabled and exposed by Docker only on 127.0.0.1:%s.' + +MSG[pulling_official_image]='Pulling the official Pocketpair server image...' +MSG[container_start_timeout]='The container did not reach the running state within %s seconds. Run logs for details.' +MSG[server_deployed]='Server deployed. Game port: UDP %s' +MSG[first_deploy_password]='For a first deployment, run: ./palworld-manager.sh set-password' +MSG[server_ready_timeout]='The server was not ready within %s seconds. Check the logs.' +MSG[server_started]='Server started.' +MSG[save_requested]='Requested an in-game save flush.' +MSG[save_failed_before_stop]='The REST API save request failed. Docker graceful shutdown will be used.' +MSG[server_stopped]='Server stopped.' +MSG[announce_restart]='Server restart in 10 seconds' +MSG[api_unavailable_restart]='The REST API is unavailable. A normal Docker restart will be performed.' +MSG[restart_ready_timeout]='The server was not ready within %s seconds after restarting. Check the logs.' +MSG[server_restarted]='Server restarted.' +MSG[api_status_failed]='The container is running, but the REST API status request failed. Check the administrator password and configuration.' +MSG[editor_not_found]='Editor %s was not found. Set the EDITOR environment variable.' +MSG[config_saved_restart]='Configuration saved. Restarting the server to apply it.' + +MSG[backup_retention_invalid]='BACKUP_RETENTION_DAYS must be a non-negative integer.' +MSG[backup_count_invalid]='BACKUP_MAX_COUNT must be a positive integer.' +MSG[save_failed_backup]='The REST API save request failed. A file-level backup will still be created, with weaker consistency guarantees.' +MSG[backup_create_failed]='Failed to create the backup archive.' +MSG[backup_integrity_failed]='Backup integrity verification failed.' +MSG[backup_created]='Backup created: %s' +MSG[backup_invalid_archive]='The backup is damaged or is not a tar.gz archive.' +MSG[backup_outside_saved]='The backup contains a path outside the Saved directory: %s' +MSG[backup_parent_path]='The backup contains an unsafe parent-directory path.' +MSG[backup_absolute_path]='The backup contains an absolute path.' +MSG[backup_links_rejected]='For safety, backups containing symbolic or hard links cannot be restored.' +MSG[restore_usage]='Usage: %s restore ' +MSG[backup_file_not_found]='Backup file not found.' +MSG[backup_inside_saved]='The backup file cannot be stored inside the Saved directory being replaced.' +MSG[backup_extract_failed]='Backup extraction failed. The original save data was not changed.' +MSG[rollback_dir_exists]='The rollback directory already exists: %s' +MSG[restore_complete_old]='Save data restored. The previous data remains at: %s' +MSG[restore_complete]='Save data restored.' +MSG[restore_start_failed]='The restored server failed to start. Rolling back automatically.' +MSG[restore_rolled_back]='Restore failed and the original save data was restored. Failed data remains at: %s' + +MSG[official_image_parse_failed]='Could not parse a valid image tag from the official Pocketpair Compose file.' +MSG[image_repository_invalid]='Invalid PALWORLD_IMAGE_REPOSITORY: %s' +MSG[image_status]='Current image: %s\nOfficial tag: %s\nUpdate target: %s' +MSG[image_current]='The current image tag is already the latest official tag.' +MSG[update_available]='An update is available.' +MSG[image_current_value]='Already using the latest official image tag: %s' +MSG[official_update_found]='Official update found: %s -> %s' +MSG[announce_update]='Server update in 30 seconds' +MSG[pulling_new_image]='Pulling the new image...' +MSG[compose_validation_rollback]='Compose validation failed after writing the new image tag. The .env change was rolled back.' +MSG[image_updated_stopped]='Image updated to %s. The server remains stopped.' +MSG[server_updated]='Server updated to: %s' +MSG[new_version_failed_rollback]='The new version failed to start. Rolling back to: %s' +MSG[update_failed_rolled_back]='Update failed and rollback was attempted. Check the logs.' + +MSG[no_password_for_maintenance]='No administrator password is saved. Scheduled backups cannot flush the save through the REST API first.' +MSG[maintenance_enabled]='Automatic maintenance enabled: hourly backups and daily official-image update checks.' +MSG[maintenance_removed]='Automatic maintenance removed. Save data and backups were not deleted.' +MSG[distro_unknown]='Could not identify the Linux distribution.' +MSG[distro_unsupported]='Automatic installation currently supports Debian and Ubuntu. Current system: %s' +MSG[distro_codename_unknown]='Could not identify VERSION_CODENAME for this distribution.' +MSG[docker_installed]='Docker Engine and Compose v2 installed.' + +MSG[doctor_header]='PalDSMS v%s environment check' +MSG[cpu_arch]='CPU architecture: %s' +MSG[cpu_arch_unsupported]='CPU architecture is %s. The Pocketpair Linux dedicated server primarily targets x86_64.' +MSG[memory_ok]='Memory: approximately %s GiB' +MSG[memory_low]='Only approximately %s GiB of memory is available. The official requirement is 16 GiB; 32 GiB or more is recommended.' +MSG[disk_ok]='Available disk space: approximately %s GiB' +MSG[disk_low]='Only approximately %s GiB of disk space is available. Use an SSD and keep at least 20 GiB free.' +MSG[compose_available]='Docker Compose v2 is available.' +MSG[compose_unavailable]='Docker Compose v2 is unavailable.' +MSG[rest_api_config_enabled]='The REST API is enabled in the game configuration.' +MSG[rest_api_config_disabled]='The REST API is not enabled. Run set-password.' +MSG[builtin_backup_enabled]='The built-in rolling backup is enabled.' +MSG[builtin_backup_disabled]='The built-in rolling backup is not enabled.' +MSG[server_config_missing]='The server configuration has not been generated yet.' +MSG[doctor_passed]='All critical environment checks passed.' +MSG[doctor_failures]='Found %s issue(s) that need attention.' + +MSG[app_title]='PalDSMS v%s - Palworld Docker Server Manager' +MSG[usage]='Usage: %s [--lang zh_CN|en_US|ja_JP] ' +MSG[help_menu]='Open the interactive menu (default with no command)' +MSG[help_doctor]='Check CPU, memory, disk, and Docker' +MSG[help_install_docker]='Install Docker Engine on Debian/Ubuntu' +MSG[help_deploy]='Pull the official image and deploy the server' +MSG[help_set_password]='Set the admin password and enable local REST API and backups' +MSG[help_config]='Edit PalWorldSettings.ini and restart' +MSG[help_server_actions]='Start, stop, or restart the server' +MSG[help_status_logs]='Show status or follow logs' +MSG[help_backup]='Create a verified external backup' +MSG[help_restore]='Restore a backup with automatic rollback on failure' +MSG[help_check_update]='Compare against the official Pocketpair image tag' +MSG[help_update]='Back up and update, rolling back on startup failure' +MSG[help_install_maintenance]='Install systemd backup and update timers' +MSG[help_remove_maintenance]='Remove maintenance without deleting saves' +MSG[help_version]='Show the version' +MSG[language_help]='Language: use --lang, PALDSMS_LANG, or .env to select zh_CN, en_US, or ja_JP. System locale is detected by default.' + +MSG[prompt_return_menu]='Press Enter to return to the menu...' +MSG[menu_title]=' PalDSMS v%s - Palworld Docker Server Manager' +MSG[menu_subtitle]=' Official Pocketpair image / Safe backups / Automatic updates' +MSG[menu_row_1]=' 1. Environment check 9. Create manual backup' +MSG[menu_row_2]=' 2. Install Docker 10. Restore backup' +MSG[menu_row_3]=' 3. Deploy server 11. Check official update' +MSG[menu_row_4]=' 4. Set admin password 12. Update server' +MSG[menu_row_5]=' 5. Edit game config 13. Install maintenance' +MSG[menu_row_6]=' 6. Start/stop/restart 14. Remove maintenance' +MSG[menu_row_7]=' 7. Show status 15. Follow live logs' +MSG[menu_row_8]=' 8. Broadcast message 0. Exit' +MSG[prompt_choice]='Choose an option: ' +MSG[prompt_server_action]='Enter start, stop, or restart: ' +MSG[invalid_action]='Invalid action.' +MSG[prompt_announcement]='Announcement: ' +MSG[announcement_sent]='Announcement sent.' +MSG[announcement_failed]='Announcement failed.' +MSG[prompt_backup_path]='Enter the full path to the backup to restore: ' +MSG[invalid_option]='Invalid option.' +MSG[unknown_command]='Unknown command: %s' diff --git a/locales/ja_JP.sh b/locales/ja_JP.sh new file mode 100644 index 0000000..3a200eb --- /dev/null +++ b/locales/ja_JP.sh @@ -0,0 +1,155 @@ +# shellcheck shell=bash +# shellcheck disable=SC2034 + +declare -Ag MSG + +MSG[label_info]='情報' +MSG[label_success]='完了' +MSG[label_warning]='警告' +MSG[label_error]='エラー' + +MSG[missing_command]='必要なコマンドが見つかりません:%s' +MSG[root_required]='この操作には root 権限が必要です。sudo を使用してください。' +MSG[file_not_found]='ファイルが見つかりません:%s' +MSG[env_created]='.env.example から .env を作成しました。' +MSG[compose_v2_required]='Docker Compose v2(docker compose)が必要です。' +MSG[docker_unavailable]='Docker が利用できないか、現在のユーザーにアクセス権がありません。' + +MSG[server_not_running_for_config]='サーバーが起動していないため、公式の既定設定を読み取れません。' +MSG[default_config_read_failed]='公式イメージから DefaultPalWorldSettings.ini を読み取れませんでした。' +MSG[default_config_empty]='公式の既定設定が空です。' +MSG[config_generated]='%s を生成しました' +MSG[prompt_new_password]='新しい管理者パスワードを入力してください(入力内容は表示されません):' +MSG[prompt_confirm_password]='もう一度入力してください:' +MSG[password_mismatch]='パスワードが一致しません。' +MSG[password_noninteractive]='非対話モードでは PALWORLD_ADMIN_PASSWORD 環境変数でパスワードを指定してください。' +MSG[password_empty]='管理者パスワードを空にはできません。' +MSG[password_invalid_chars]='パスワードに二重引用符や改行は使用できません。' +MSG[password_short]='12 文字以上のランダムな管理者パスワードを推奨します。' +MSG[rest_port_invalid]='REST_API_PORT が無効です:%s' +MSG[rest_port_out_of_range]='REST_API_PORT が範囲外です:%s' +MSG[config_key_missing]='公式の既定設定に %s がありません。' +MSG[password_write_check_failed]='管理者パスワードの書き込み確認に失敗しました。元の設定のバックアップは保持されています。' +MSG[rest_api_write_check_failed]='REST API 設定の書き込み確認に失敗しました。' +MSG[rest_port_write_check_failed]='REST API ポートの書き込み確認に失敗しました。' +MSG[rcon_write_check_failed]='RCON 無効化設定の書き込み確認に失敗しました。' +MSG[builtin_backup_write_check_failed]='ゲーム内バックアップ設定の書き込み確認に失敗しました。' +MSG[rest_api_enabled]='REST API を有効にし、Docker では 127.0.0.1:%s のみに公開しました。' + +MSG[pulling_official_image]='Pocketpair 公式サーバーイメージを取得しています……' +MSG[container_start_timeout]='コンテナが %s 秒以内に実行状態になりませんでした。logs で原因を確認してください。' +MSG[server_deployed]='サーバーをデプロイしました。ゲームポート:UDP %s' +MSG[first_deploy_password]='初回デプロイ後に実行してください:./palworld-manager.sh set-password' +MSG[server_ready_timeout]='サーバーが %s 秒以内に準備完了になりませんでした。ログを確認してください。' +MSG[server_started]='サーバーを起動しました。' +MSG[save_requested]='ゲーム内セーブの書き込みを要求しました。' +MSG[save_failed_before_stop]='REST API の保存要求に失敗しました。Docker の正常停止処理に任せます。' +MSG[server_stopped]='サーバーを停止しました。' +MSG[announce_restart]='サーバーは 10 秒後に再起動します' +MSG[api_unavailable_restart]='REST API を利用できないため、Docker の通常の再起動を行います。' +MSG[restart_ready_timeout]='再起動後、サーバーが %s 秒以内に準備完了になりませんでした。ログを確認してください。' +MSG[server_restarted]='サーバーを再起動しました。' +MSG[api_status_failed]='コンテナは実行中ですが、REST API の状態取得に失敗しました。管理者パスワードと設定を確認してください。' +MSG[editor_not_found]='エディター %s が見つかりません。EDITOR 環境変数を設定してください。' +MSG[config_saved_restart]='設定を保存しました。反映するためサーバーを再起動します。' + +MSG[backup_retention_invalid]='BACKUP_RETENTION_DAYS は 0 以上の整数で指定してください。' +MSG[backup_count_invalid]='BACKUP_MAX_COUNT は正の整数で指定してください。' +MSG[save_failed_backup]='REST API の保存要求に失敗しました。ファイル単位のバックアップは続行しますが、整合性の保証は弱くなります。' +MSG[backup_create_failed]='バックアップアーカイブの作成に失敗しました。' +MSG[backup_integrity_failed]='バックアップの整合性確認に失敗しました。' +MSG[backup_created]='バックアップを作成しました:%s' +MSG[backup_invalid_archive]='バックアップが破損しているか、tar.gz 形式ではありません。' +MSG[backup_outside_saved]='バックアップに Saved ディレクトリ外のパスが含まれています:%s' +MSG[backup_parent_path]='バックアップに安全でない親ディレクトリへのパスが含まれています。' +MSG[backup_absolute_path]='バックアップに絶対パスが含まれています。' +MSG[backup_links_rejected]='安全のため、シンボリックリンクまたはハードリンクを含むバックアップは復元できません。' +MSG[restore_usage]='使い方:%s restore <バックアップファイル.tar.gz>' +MSG[backup_file_not_found]='バックアップファイルが見つかりません。' +MSG[backup_inside_saved]='バックアップファイルを置換対象の Saved ディレクトリ内に置くことはできません。' +MSG[backup_extract_failed]='バックアップの展開に失敗しました。元のセーブデータは変更されていません。' +MSG[rollback_dir_exists]='ロールバック用ディレクトリがすでに存在します:%s' +MSG[restore_complete_old]='セーブデータを復元しました。復元前のデータは次に保持されています:%s' +MSG[restore_complete]='セーブデータを復元しました。' +MSG[restore_start_failed]='復元後のサーバーを起動できませんでした。自動的にロールバックします。' +MSG[restore_rolled_back]='復元に失敗し、元のセーブデータへ戻しました。失敗したデータは次に保持されています:%s' + +MSG[official_image_parse_failed]='Pocketpair 公式 Compose ファイルから有効なイメージタグを取得できませんでした。' +MSG[image_repository_invalid]='PALWORLD_IMAGE_REPOSITORY の形式が無効です:%s' +MSG[image_status]='現在のイメージ:%s\n公式タグ:%s\n更新先:%s' +MSG[image_current]='現在のイメージタグは公式の最新版です。' +MSG[update_available]='利用可能な更新があります。' +MSG[image_current_value]='すでに公式の最新イメージタグを使用しています:%s' +MSG[official_update_found]='公式更新を検出しました:%s -> %s' +MSG[announce_update]='サーバーは 30 秒後に更新されます' +MSG[pulling_new_image]='新しいイメージを取得しています……' +MSG[compose_validation_rollback]='新しいイメージタグの書き込み後に Compose の検証に失敗したため、.env を元に戻しました。' +MSG[image_updated_stopped]='イメージを %s に更新しました。サーバーは停止状態のままです。' +MSG[server_updated]='サーバーを次のバージョンに更新しました:%s' +MSG[new_version_failed_rollback]='新しいバージョンを起動できなかったため、次へロールバックします:%s' +MSG[update_failed_rolled_back]='更新に失敗し、ロールバックを試行しました。ログを確認してください。' + +MSG[no_password_for_maintenance]='管理者パスワードが保存されていないため、定期バックアップ前に REST API でセーブを書き込めません。' +MSG[maintenance_enabled]='自動メンテナンスを有効にしました。毎時バックアップし、公式イメージの更新を毎日確認します。' +MSG[maintenance_removed]='自動メンテナンスを削除しました。セーブデータとバックアップは削除していません。' +MSG[distro_unknown]='Linux ディストリビューションを識別できません。' +MSG[distro_unsupported]='自動インストールは現在 Debian と Ubuntu に対応しています。現在のシステム:%s' +MSG[distro_codename_unknown]='ディストリビューションの VERSION_CODENAME を識別できません。' +MSG[docker_installed]='Docker Engine と Compose v2 をインストールしました。' + +MSG[doctor_header]='PalDSMS v%s 環境チェック' +MSG[cpu_arch]='CPU アーキテクチャ:%s' +MSG[cpu_arch_unsupported]='CPU アーキテクチャは %s です。Pocketpair の Linux 専用サーバーは主に x86_64 を対象としています。' +MSG[memory_ok]='メモリ:約 %s GiB' +MSG[memory_low]='メモリは約 %s GiB しかありません。公式要件は 16 GiB、推奨は 32 GiB 以上です。' +MSG[disk_ok]='空きディスク容量:約 %s GiB' +MSG[disk_low]='空きディスク容量は約 %s GiB しかありません。SSD を使用し、20 GiB 以上確保してください。' +MSG[compose_available]='Docker Compose v2 を利用できます。' +MSG[compose_unavailable]='Docker Compose v2 を利用できません。' +MSG[rest_api_config_enabled]='ゲーム設定で REST API が有効です。' +MSG[rest_api_config_disabled]='REST API が有効ではありません。set-password を実行してください。' +MSG[builtin_backup_enabled]='ゲーム内のローテーションバックアップが有効です。' +MSG[builtin_backup_disabled]='ゲーム内のローテーションバックアップが有効ではありません。' +MSG[server_config_missing]='サーバー設定はまだ生成されていません。' +MSG[doctor_passed]='重要な環境チェックにすべて合格しました。' +MSG[doctor_failures]='対応が必要な問題が %s 件見つかりました。' + +MSG[app_title]='PalDSMS v%s - Palworld Docker サーバー管理ツール' +MSG[usage]='使い方:%s [--lang zh_CN|en_US|ja_JP] <コマンド>' +MSG[help_menu]='対話メニューを開く(コマンド省略時の既定)' +MSG[help_doctor]='CPU、メモリ、ディスク、Docker を確認' +MSG[help_install_docker]='Debian/Ubuntu に Docker Engine をインストール' +MSG[help_deploy]='公式イメージを取得して初回デプロイ' +MSG[help_set_password]='管理者パスワード、ローカル REST API、バックアップを設定' +MSG[help_config]='PalWorldSettings.ini を編集して再起動' +MSG[help_server_actions]='サーバーを起動、停止、再起動' +MSG[help_status_logs]='状態表示またはログ追跡' +MSG[help_backup]='検証済みの外部バックアップを作成' +MSG[help_restore]='バックアップを復元し、失敗時は自動ロールバック' +MSG[help_check_update]='Pocketpair 公式イメージタグと比較' +MSG[help_update]='バックアップ後に更新し、起動失敗時はロールバック' +MSG[help_install_maintenance]='systemd のバックアップ・更新タイマーを設定' +MSG[help_remove_maintenance]='セーブを削除せず自動メンテナンスを削除' +MSG[help_version]='バージョンを表示' +MSG[language_help]='言語:--lang、PALDSMS_LANG、または .env で zh_CN、en_US、ja_JP を選択できます。既定ではシステム言語を自動判定します。' + +MSG[prompt_return_menu]='Enter キーでメニューに戻ります……' +MSG[menu_title]=' PalDSMS v%s - Palworld Docker サーバー管理ツール' +MSG[menu_subtitle]=' Pocketpair 公式イメージ / 安全なバックアップ / 自動更新' +MSG[menu_row_1]=' 1. 環境チェック 9. 手動バックアップ作成' +MSG[menu_row_2]=' 2. Docker インストール 10. バックアップ復元' +MSG[menu_row_3]=' 3. サーバーデプロイ 11. 公式更新の確認' +MSG[menu_row_4]=' 4. 管理者パスワード設定 12. サーバー更新' +MSG[menu_row_5]=' 5. ゲーム設定編集 13. 自動メンテナンス設定' +MSG[menu_row_6]=' 6. 起動/停止/再起動 14. 自動メンテナンス削除' +MSG[menu_row_7]=' 7. 状態表示 15. リアルタイムログ' +MSG[menu_row_8]=' 8. サーバー内アナウンス 0. 終了' +MSG[prompt_choice]='選択してください:' +MSG[prompt_server_action]='start、stop、restart のいずれかを入力してください:' +MSG[invalid_action]='無効な操作です。' +MSG[prompt_announcement]='アナウンス内容:' +MSG[announcement_sent]='アナウンスを送信しました。' +MSG[announcement_failed]='アナウンスの送信に失敗しました。' +MSG[prompt_backup_path]='復元するバックアップの完全なパスを入力してください:' +MSG[invalid_option]='無効な選択です。' +MSG[unknown_command]='不明なコマンド:%s' diff --git a/locales/zh_CN.sh b/locales/zh_CN.sh new file mode 100644 index 0000000..c820d66 --- /dev/null +++ b/locales/zh_CN.sh @@ -0,0 +1,155 @@ +# shellcheck shell=bash +# shellcheck disable=SC2034 + +declare -Ag MSG + +MSG[label_info]='信息' +MSG[label_success]='完成' +MSG[label_warning]='警告' +MSG[label_error]='错误' + +MSG[missing_command]='缺少命令:%s' +MSG[root_required]='此操作需要 root 权限,请使用 sudo。' +MSG[file_not_found]='找不到文件:%s' +MSG[env_created]='已从 .env.example 创建 .env。' +MSG[compose_v2_required]='需要 Docker Compose v2(docker compose)。' +MSG[docker_unavailable]='Docker 服务不可用,或当前用户无权访问 Docker。' + +MSG[server_not_running_for_config]='服务器未运行,无法读取官方默认配置。' +MSG[default_config_read_failed]='无法从官方镜像读取 DefaultPalWorldSettings.ini。' +MSG[default_config_empty]='官方默认配置为空。' +MSG[config_generated]='已生成 %s' +MSG[prompt_new_password]='请输入新的管理员密码(不会显示):' +MSG[prompt_confirm_password]='请再次输入:' +MSG[password_mismatch]='两次输入的密码不一致。' +MSG[password_noninteractive]='非交互模式请通过 PALWORLD_ADMIN_PASSWORD 环境变量提供密码。' +MSG[password_empty]='管理员密码不能为空。' +MSG[password_invalid_chars]='密码不能包含双引号或换行符。' +MSG[password_short]='建议使用至少 12 位的随机管理员密码。' +MSG[rest_port_invalid]='REST_API_PORT 无效:%s' +MSG[rest_port_out_of_range]='REST_API_PORT 超出范围:%s' +MSG[config_key_missing]='官方默认配置中没有 %s。' +MSG[password_write_check_failed]='管理员密码写入校验失败,原配置备份仍然保留。' +MSG[rest_api_write_check_failed]='REST API 配置写入校验失败。' +MSG[rest_port_write_check_failed]='REST API 端口写入校验失败。' +MSG[rcon_write_check_failed]='RCON 禁用配置写入校验失败。' +MSG[builtin_backup_write_check_failed]='游戏内建备份配置写入校验失败。' +MSG[rest_api_enabled]='REST API 已启用,并仅通过 Docker 映射到 127.0.0.1:%s。' + +MSG[pulling_official_image]='正在拉取 Pocketpair 官方服务器镜像……' +MSG[container_start_timeout]='容器未能在 %s 秒内进入运行状态,请执行 logs 查看原因。' +MSG[server_deployed]='服务器已部署。游戏端口:UDP %s' +MSG[first_deploy_password]='首次部署请执行:./palworld-manager.sh set-password' +MSG[server_ready_timeout]='服务器未能在 %s 秒内就绪,请查看日志。' +MSG[server_started]='服务器已启动。' +MSG[save_requested]='已请求服务器落盘存档。' +MSG[save_failed_before_stop]='REST API 保存请求失败,将依赖容器的正常停止流程。' +MSG[server_stopped]='服务器已停止。' +MSG[announce_restart]='服务器将在 10 秒后重启' +MSG[api_unavailable_restart]='REST API 不可用,将直接执行 Docker 的正常重启。' +MSG[restart_ready_timeout]='服务器重启后未能在 %s 秒内就绪,请查看日志。' +MSG[server_restarted]='服务器已重启。' +MSG[api_status_failed]='容器正在运行,但 REST API 状态读取失败。请检查管理员密码和配置。' +MSG[editor_not_found]='找不到编辑器 %s,请设置 EDITOR 环境变量。' +MSG[config_saved_restart]='配置已保存,正在重启服务器使其生效。' + +MSG[backup_retention_invalid]='BACKUP_RETENTION_DAYS 必须是非负整数。' +MSG[backup_count_invalid]='BACKUP_MAX_COUNT 必须是正整数。' +MSG[save_failed_backup]='REST API 保存请求失败;仍会创建文件级备份,但一致性保障较弱。' +MSG[backup_create_failed]='创建备份包失败。' +MSG[backup_integrity_failed]='备份完整性校验失败。' +MSG[backup_created]='备份已创建:%s' +MSG[backup_invalid_archive]='备份包损坏或不是 tar.gz 文件。' +MSG[backup_outside_saved]='备份包包含 Saved 目录以外的路径:%s' +MSG[backup_parent_path]='备份包包含不安全的上级目录路径。' +MSG[backup_absolute_path]='备份包包含绝对路径。' +MSG[backup_links_rejected]='为安全起见,拒绝恢复包含符号链接或硬链接的备份包。' +MSG[restore_usage]='用法:%s restore <备份文件.tar.gz>' +MSG[backup_file_not_found]='找不到备份文件。' +MSG[backup_inside_saved]='备份文件不能放在待替换的 Saved 目录内。' +MSG[backup_extract_failed]='备份解压失败,原存档未改动。' +MSG[rollback_dir_exists]='回滚目录已存在:%s' +MSG[restore_complete_old]='存档恢复完成。恢复前数据保留在:%s' +MSG[restore_complete]='存档恢复完成。' +MSG[restore_start_failed]='恢复后的服务器未能启动,正在自动回滚。' +MSG[restore_rolled_back]='恢复失败,已回滚原存档;失败数据保留在:%s' + +MSG[official_image_parse_failed]='无法从 Pocketpair 官方 Compose 文件解析有效镜像标签。' +MSG[image_repository_invalid]='PALWORLD_IMAGE_REPOSITORY 格式无效:%s' +MSG[image_status]='当前镜像:%s\n官方标签:%s\n更新目标:%s' +MSG[image_current]='当前已是官方最新镜像标签。' +MSG[update_available]='发现可用更新。' +MSG[image_current_value]='当前已是官方最新镜像标签:%s' +MSG[official_update_found]='发现官方更新:%s -> %s' +MSG[announce_update]='服务器将在 30 秒后更新' +MSG[pulling_new_image]='正在拉取新镜像……' +MSG[compose_validation_rollback]='新镜像标签写入后 Compose 校验失败,已回滚 .env。' +MSG[image_updated_stopped]='镜像已更新到 %s;服务器保持停止状态。' +MSG[server_updated]='服务器已更新到:%s' +MSG[new_version_failed_rollback]='新版本启动失败,正在回滚到:%s' +MSG[update_failed_rolled_back]='更新失败,已尝试回滚。请查看日志。' + +MSG[no_password_for_maintenance]='尚未保存管理员密码;定时备份无法先调用 REST API 落盘。' +MSG[maintenance_enabled]='自动维护已启用:每小时备份,每日检查官方镜像更新。' +MSG[maintenance_removed]='自动维护已卸载;游戏存档和备份未删除。' +MSG[distro_unknown]='无法识别 Linux 发行版。' +MSG[distro_unsupported]='自动安装目前支持 Debian 和 Ubuntu;当前系统:%s' +MSG[distro_codename_unknown]='无法识别发行版代号 VERSION_CODENAME。' +MSG[docker_installed]='Docker Engine 与 Compose v2 已安装。' + +MSG[doctor_header]='PalDSMS v%s 环境检查' +MSG[cpu_arch]='CPU 架构:%s' +MSG[cpu_arch_unsupported]='CPU 架构为 %s;Pocketpair Linux 专用服务器主要面向 x86_64。' +MSG[memory_ok]='内存:约 %s GiB' +MSG[memory_low]='内存仅约 %s GiB;官方要求 16 GiB,推荐 32 GiB 以上。' +MSG[disk_ok]='可用磁盘:约 %s GiB' +MSG[disk_low]='可用磁盘仅约 %s GiB;建议使用 SSD 并预留至少 20 GiB。' +MSG[compose_available]='Docker Compose v2 可用。' +MSG[compose_unavailable]='Docker Compose v2 不可用。' +MSG[rest_api_config_enabled]='REST API 已在游戏配置中启用。' +MSG[rest_api_config_disabled]='REST API 尚未启用,请运行 set-password。' +MSG[builtin_backup_enabled]='游戏内建滚动备份已启用。' +MSG[builtin_backup_disabled]='游戏内建滚动备份尚未启用。' +MSG[server_config_missing]='尚未生成服务器配置。' +MSG[doctor_passed]='关键环境检查通过。' +MSG[doctor_failures]='发现 %s 项需要处理的问题。' + +MSG[app_title]='PalDSMS v%s - 幻兽帕鲁 Docker 服务器管理器' +MSG[usage]='用法:%s [--lang zh_CN|en_US|ja_JP] <命令>' +MSG[help_menu]='打开交互式菜单(无参数时默认)' +MSG[help_doctor]='检查 CPU、内存、磁盘和 Docker 环境' +MSG[help_install_docker]='在 Debian/Ubuntu 安装 Docker Engine' +MSG[help_deploy]='拉取官方镜像并首次部署' +MSG[help_set_password]='设置管理员密码,启用本地 REST API 与内建备份' +MSG[help_config]='编辑 PalWorldSettings.ini 并重启' +MSG[help_server_actions]='启动、停止或重启服务器' +MSG[help_status_logs]='查看状态或跟踪日志' +MSG[help_backup]='创建经过校验的外部备份' +MSG[help_restore]='恢复备份;失败时自动回滚' +MSG[help_check_update]='对比 Pocketpair 官方镜像标签' +MSG[help_update]='备份后更新;启动失败时回滚镜像标签' +MSG[help_install_maintenance]='安装 systemd 自动备份/更新定时器' +MSG[help_remove_maintenance]='移除自动维护(不删除存档)' +MSG[help_version]='显示版本' +MSG[language_help]='语言:使用 --lang、PALDSMS_LANG 或 .env 选择 zh_CN、en_US、ja_JP;默认自动识别系统语言。' + +MSG[prompt_return_menu]='按回车键返回菜单……' +MSG[menu_title]=' PalDSMS v%s - 幻兽帕鲁 Docker 服务器管理系统' +MSG[menu_subtitle]=' Pocketpair 官方镜像 / 安全备份 / 自动更新' +MSG[menu_row_1]=' 1. 环境检查 9. 创建手动备份' +MSG[menu_row_2]=' 2. 安装 Docker 10. 恢复备份' +MSG[menu_row_3]=' 3. 部署服务器 11. 检查官方更新' +MSG[menu_row_4]=' 4. 设置管理员密码 12. 更新服务器' +MSG[menu_row_5]=' 5. 编辑游戏配置 13. 安装自动维护' +MSG[menu_row_6]=' 6. 启动/停止/重启 14. 卸载自动维护' +MSG[menu_row_7]=' 7. 查看状态 15. 查看实时日志' +MSG[menu_row_8]=' 8. 广播服务器消息 0. 退出' +MSG[prompt_choice]='请选择:' +MSG[prompt_server_action]='输入 start、stop 或 restart:' +MSG[invalid_action]='无效操作。' +MSG[prompt_announcement]='广播内容:' +MSG[announcement_sent]='广播已发送。' +MSG[announcement_failed]='广播失败。' +MSG[prompt_backup_path]='请输入要恢复的备份文件完整路径:' +MSG[invalid_option]='无效选项。' +MSG[unknown_command]='未知命令:%s' diff --git a/palworld-manager.sh b/palworld-manager.sh index d76bd98..e953dc7 100755 --- a/palworld-manager.sh +++ b/palworld-manager.sh @@ -3,7 +3,7 @@ set -Eeuo pipefail umask 077 -readonly PALDSMS_VERSION="2.0.0" +readonly PALDSMS_VERSION="2.0.1" readonly OFFICIAL_COMPOSE_URL="${PALDSMS_OFFICIAL_COMPOSE_URL:-https://raw.githubusercontent.com/pocketpairjp/palworld-dedicated-server-docker/main/compose/compose.yaml}" readonly DEFAULT_IMAGE="ghcr.io/pocketpairjp/palserver:v1.0.0.100427" @@ -17,6 +17,80 @@ SAVE_DIR="$ROOT_DIR/Saved" SETTINGS_FILE="$SAVE_DIR/Config/LinuxServer/PalWorldSettings.ini" BACKUP_DIR="$ROOT_DIR/backups" PASSWORD_FILE="$ROOT_DIR/.paldsms-admin-password" +LOCALE_DIR="$SCRIPT_DIR/locales" + +CLI_LANGUAGE="" +CURRENT_LANGUAGE="" +declare -a MAIN_ARGS=() +declare -A MSG=() + +normalize_language() { + local value=${1:-} + value="${value//-/_}" + value="${value,,}" + case "$value" in + zh|zh_cn|zh_hans|zh_hans_cn) printf 'zh_CN' ;; + en|en_us|en_gb|c|posix) printf 'en_US' ;; + ja|ja_jp) printf 'ja_JP' ;; + auto|"") return 1 ;; + *) return 2 ;; + esac +} + +read_configured_language() { + local value="" + if [[ -f "$ENV_FILE" ]]; then + value="$(sed -n -E 's/^PALDSMS_LANG=(.*)$/\1/p' "$ENV_FILE" | tail -n 1)" + value="${value%$'\r'}" + value="${value%\"}" + value="${value#\"}" + fi + printf '%s' "$value" +} + +parse_global_options() { + while (( $# > 0 )); do + case "$1" in + --lang) + (( $# >= 2 )) || { printf 'PalDSMS: --lang requires a value (zh_CN, en_US, or ja_JP).\n' >&2; exit 2; } + CLI_LANGUAGE=$2 + shift 2 + ;; + --lang=*) + CLI_LANGUAGE=${1#*=} + shift + ;; + *) + MAIN_ARGS+=("$1") + shift + ;; + esac + done +} + +initialize_language() { + local requested="${CLI_LANGUAGE:-${PALDSMS_LANG:-}}" normalized locale_hint + [[ -n "$requested" ]] || requested="$(read_configured_language)" + + if normalized="$(normalize_language "$requested")"; then + CURRENT_LANGUAGE=$normalized + elif [[ -n "$requested" && "${requested,,}" != auto ]]; then + printf 'PalDSMS: unsupported language "%s". Use zh_CN, en_US, or ja_JP.\n' "$requested" >&2 + exit 2 + else + locale_hint="${LC_ALL:-${LC_MESSAGES:-${LANG:-en_US}}}" + if ! CURRENT_LANGUAGE="$(normalize_language "${locale_hint%%.*}")"; then + CURRENT_LANGUAGE=en_US + fi + fi + + [[ -f "$LOCALE_DIR/${CURRENT_LANGUAGE}.sh" ]] || { + printf 'PalDSMS: locale file not found: %s\n' "$LOCALE_DIR/${CURRENT_LANGUAGE}.sh" >&2 + exit 1 + } + # shellcheck source=/dev/null + source "$LOCALE_DIR/${CURRENT_LANGUAGE}.sh" +} if [[ -t 1 ]]; then readonly C_RESET=$'\033[0m' @@ -28,23 +102,48 @@ else readonly C_RESET="" C_RED="" C_GREEN="" C_YELLOW="" C_BLUE="" fi -info() { printf '%s[信息]%s %s\n' "$C_BLUE" "$C_RESET" "$*"; } -success() { printf '%s[完成]%s %s\n' "$C_GREEN" "$C_RESET" "$*"; } -warn() { printf '%s[警告]%s %s\n' "$C_YELLOW" "$C_RESET" "$*" >&2; } -die() { printf '%s[错误]%s %s\n' "$C_RED" "$C_RESET" "$*" >&2; exit 1; } +message() { + local key=$1 + shift + # Locale catalogs are trusted project code; remaining arguments are values, not format strings. + # shellcheck disable=SC2059 + printf "${MSG[$key]:-$key}" "$@" +} + +log_message() { + local color=$1 label=$2 stream=$3 key=$4 + shift 4 + if [[ "$stream" == stderr ]]; then + printf '%s[%s]%s ' "$color" "${MSG[$label]:-$label}" "$C_RESET" >&2 + message "$key" "$@" >&2 + printf '\n' >&2 + else + printf '%s[%s]%s ' "$color" "${MSG[$label]:-$label}" "$C_RESET" + message "$key" "$@" + printf '\n' + fi +} + +info() { log_message "$C_BLUE" label_info stdout "$@"; } +success() { log_message "$C_GREEN" label_success stdout "$@"; } +warn() { log_message "$C_YELLOW" label_warning stderr "$@"; } +die() { log_message "$C_RED" label_error stderr "$@"; exit 1; } require_command() { - command -v "$1" >/dev/null 2>&1 || die "缺少命令:$1" + command -v "$1" >/dev/null 2>&1 || die missing_command "$1" } require_root() { - [[ ${EUID:-$(id -u)} -eq 0 ]] || die "此操作需要 root 权限,请使用 sudo。" + [[ ${EUID:-$(id -u)} -eq 0 ]] || die root_required } ensure_project_files() { - [[ -f "$COMPOSE_FILE" ]] || die "找不到 $COMPOSE_FILE" - [[ -f "$ENV_EXAMPLE" ]] || die "找不到 $ENV_EXAMPLE" - [[ -f "$ROOT_DIR/helper.sh" ]] || die "找不到 $ROOT_DIR/helper.sh" + [[ -f "$COMPOSE_FILE" ]] || die file_not_found "$COMPOSE_FILE" + [[ -f "$ENV_EXAMPLE" ]] || die file_not_found "$ENV_EXAMPLE" + [[ -f "$ROOT_DIR/helper.sh" ]] || die file_not_found "$ROOT_DIR/helper.sh" + [[ -f "$LOCALE_DIR/zh_CN.sh" ]] || die file_not_found "$LOCALE_DIR/zh_CN.sh" + [[ -f "$LOCALE_DIR/en_US.sh" ]] || die file_not_found "$LOCALE_DIR/en_US.sh" + [[ -f "$LOCALE_DIR/ja_JP.sh" ]] || die file_not_found "$LOCALE_DIR/ja_JP.sh" } ensure_env() { @@ -52,7 +151,7 @@ ensure_env() { if [[ ! -f "$ENV_FILE" ]]; then cp -- "$ENV_EXAMPLE" "$ENV_FILE" chmod 600 "$ENV_FILE" - info "已从 .env.example 创建 .env。" + info env_created fi } @@ -97,8 +196,8 @@ compose() { require_runtime() { require_command docker - docker compose version >/dev/null 2>&1 || die "需要 Docker Compose v2(docker compose)。" - docker info >/dev/null 2>&1 || die "Docker 服务不可用,或当前用户无权访问 Docker。" + docker compose version >/dev/null 2>&1 || die compose_v2_required + docker info >/dev/null 2>&1 || die docker_unavailable require_command curl require_command tar } @@ -207,19 +306,19 @@ api_announce() { ensure_default_config() { local tmp [[ -s "$SETTINGS_FILE" ]] && return 0 - container_running || die "服务器未运行,无法读取官方默认配置。" + container_running || die server_not_running_for_config mkdir -p -- "$(dirname -- "$SETTINGS_FILE")" tmp="$(mktemp "$ROOT_DIR/.PalWorldSettings.XXXXXX")" if ! compose exec -T palworld-server \ sh -c 'cat /pal/Package/DefaultPalWorldSettings.ini' >"$tmp"; then rm -f -- "$tmp" - die "无法从官方镜像读取 DefaultPalWorldSettings.ini。" + die default_config_read_failed fi - [[ -s "$tmp" ]] || { rm -f -- "$tmp"; die "官方默认配置为空。"; } + [[ -s "$tmp" ]] || { rm -f -- "$tmp"; die default_config_empty; } install -m 600 "$tmp" "$SETTINGS_FILE" rm -f -- "$tmp" - success "已生成 $SETTINGS_FILE" + success config_generated "$SETTINGS_FILE" } set_admin_password() { @@ -230,23 +329,23 @@ set_admin_password() { if [[ -n "${PALWORLD_ADMIN_PASSWORD:-}" ]]; then password="$PALWORLD_ADMIN_PASSWORD" elif [[ -t 0 ]]; then - read -r -s -p "请输入新的管理员密码(不会显示): " password + read -r -s -p "$(message prompt_new_password)" password printf '\n' - read -r -s -p "请再次输入: " confirm + read -r -s -p "$(message prompt_confirm_password)" confirm printf '\n' - [[ "$password" == "$confirm" ]] || die "两次输入的密码不一致。" + [[ "$password" == "$confirm" ]] || die password_mismatch else - die "非交互模式请通过 PALWORLD_ADMIN_PASSWORD 环境变量提供密码。" + die password_noninteractive fi - [[ -n "$password" ]] || die "管理员密码不能为空。" + [[ -n "$password" ]] || die password_empty [[ "$password" != *\"* && "$password" != *$'\n'* && "$password" != *$'\r'* ]] \ - || die "密码不能包含双引号或换行符。" - (( ${#password} >= 12 )) || warn "建议使用至少 12 位的随机管理员密码。" + || die password_invalid_chars + (( ${#password} >= 12 )) || warn password_short port="$(env_get REST_API_PORT 8212)" - [[ "$port" =~ ^[0-9]{1,5}$ ]] || die "REST_API_PORT 无效:$port" - (( port >= 1 && port <= 65535 )) || die "REST_API_PORT 超出范围:$port" + [[ "$port" =~ ^[0-9]{1,5}$ ]] || die rest_port_invalid "$port" + (( port >= 1 && port <= 65535 )) || die rest_port_out_of_range "$port" timestamp="$(date +%Y%m%d-%H%M%S-%N)" cp -a -- "$SETTINGS_FILE" "${SETTINGS_FILE}.before-paldsms-${timestamp}" @@ -255,12 +354,12 @@ set_admin_password() { escaped=${escaped//&/\\&} escaped=${escaped//|/\\|} - grep -q 'AdminPassword=' "$SETTINGS_FILE" || die "官方默认配置中没有 AdminPassword。" - grep -q 'RESTAPIEnabled=' "$SETTINGS_FILE" || die "官方默认配置中没有 RESTAPIEnabled。" - grep -q 'RESTAPIPort=' "$SETTINGS_FILE" || die "官方默认配置中没有 RESTAPIPort。" - grep -q 'RCONEnabled=' "$SETTINGS_FILE" || die "官方默认配置中没有 RCONEnabled。" + grep -q 'AdminPassword=' "$SETTINGS_FILE" || die config_key_missing AdminPassword + grep -q 'RESTAPIEnabled=' "$SETTINGS_FILE" || die config_key_missing RESTAPIEnabled + grep -q 'RESTAPIPort=' "$SETTINGS_FILE" || die config_key_missing RESTAPIPort + grep -q 'RCONEnabled=' "$SETTINGS_FILE" || die config_key_missing RCONEnabled grep -q 'bIsUseBackupSaveData=' "$SETTINGS_FILE" \ - || die "官方默认配置中没有 bIsUseBackupSaveData。" + || die config_key_missing bIsUseBackupSaveData sed -i -E \ -e "s|AdminPassword=\"[^\"]*\"|AdminPassword=\"${escaped}\"|" \ @@ -271,20 +370,20 @@ set_admin_password() { "$SETTINGS_FILE" grep -Fq "AdminPassword=\"${password}\"" "$SETTINGS_FILE" \ - || die "管理员密码写入校验失败,原配置备份仍然保留。" + || die password_write_check_failed grep -q 'RESTAPIEnabled=True' "$SETTINGS_FILE" \ - || die "REST API 配置写入校验失败。" + || die rest_api_write_check_failed grep -q "RESTAPIPort=${port}" "$SETTINGS_FILE" \ - || die "REST API 端口写入校验失败。" + || die rest_port_write_check_failed grep -q 'RCONEnabled=False' "$SETTINGS_FILE" \ - || die "RCON 禁用配置写入校验失败。" + || die rcon_write_check_failed grep -q 'bIsUseBackupSaveData=True' "$SETTINGS_FILE" \ - || die "游戏内建备份配置写入校验失败。" + || die builtin_backup_write_check_failed printf '%s' "$password" >"$PASSWORD_FILE" chmod 600 "$PASSWORD_FILE" "$SETTINGS_FILE" - info "REST API 已启用,并仅通过 Docker 映射到 127.0.0.1:${port}。" + info rest_api_enabled "$port" restart_server } @@ -294,50 +393,50 @@ deploy_server() { mkdir -p -- "$SAVE_DIR" "$BACKUP_DIR" chmod 700 "$SAVE_DIR" "$BACKUP_DIR" - info "拉取 Pocketpair 官方服务器镜像……" + info pulling_official_image compose pull palworld-server compose up -d - wait_until_running 120 || die "容器未能在 120 秒内进入运行状态,请执行 logs 查看原因。" + wait_until_running 120 || die container_start_timeout 120 ensure_default_config - success "服务器已部署。游戏端口:UDP $(env_get PALWORLD_PORT 8211)" - info "首次部署请执行:./palworld-manager.sh set-password" + success server_deployed "$(env_get PALWORLD_PORT 8211)" + info first_deploy_password } start_server() { require_runtime mkdir -p -- "$SAVE_DIR" "$BACKUP_DIR" compose up -d - wait_until_ready 180 || die "服务器未能在 180 秒内就绪,请查看日志。" - success "服务器已启动。" + wait_until_ready 180 || die server_ready_timeout 180 + success server_started } stop_server() { require_runtime if container_running; then if api_save; then - info "已请求服务器落盘存档。" + info save_requested sleep 3 else - warn "REST API 保存请求失败,将依赖容器的正常停止流程。" + warn save_failed_before_stop fi fi compose stop -t 120 - success "服务器已停止。" + success server_stopped } restart_server() { require_runtime if container_running; then if api_save; then - api_announce "Server restart in 10 seconds" || true + api_announce "$(message announce_restart)" || true sleep 10 else - warn "REST API 不可用,将直接执行 Docker 的正常重启。" + warn api_unavailable_restart fi fi compose restart -t 120 palworld-server - wait_until_ready 180 || die "服务器重启后未能在 180 秒内就绪,请查看日志。" - success "服务器已重启。" + wait_until_ready 180 || die restart_ready_timeout 180 + success server_restarted } show_status() { @@ -346,7 +445,7 @@ show_status() { if container_running; then printf '\n' if ! api_request GET info; then - warn "容器正在运行,但 REST API 状态读取失败。请检查管理员密码和配置。" + warn api_status_failed fi printf '\n' fi @@ -362,11 +461,11 @@ edit_config() { require_runtime ensure_default_config editor="${EDITOR:-nano}" - command -v "$editor" >/dev/null 2>&1 || die "找不到编辑器 $editor,请设置 EDITOR 环境变量。" + command -v "$editor" >/dev/null 2>&1 || die editor_not_found "$editor" timestamp="$(date +%Y%m%d-%H%M%S)" cp -a -- "$SETTINGS_FILE" "${SETTINGS_FILE}.before-edit-${timestamp}" "$editor" "$SETTINGS_FILE" - info "配置已保存,正在重启服务器使其生效。" + info config_saved_restart restart_server } @@ -375,8 +474,8 @@ prune_backups() { local -a backups=() days="$(env_get BACKUP_RETENTION_DAYS 14)" max_count="$(env_get BACKUP_MAX_COUNT 168)" - [[ "$days" =~ ^[0-9]+$ ]] || die "BACKUP_RETENTION_DAYS 必须是非负整数。" - [[ "$max_count" =~ ^[1-9][0-9]*$ ]] || die "BACKUP_MAX_COUNT 必须是正整数。" + [[ "$days" =~ ^[0-9]+$ ]] || die backup_retention_invalid + [[ "$max_count" =~ ^[1-9][0-9]*$ ]] || die backup_count_invalid find "$BACKUP_DIR" -maxdepth 1 -type f -name 'palworld-*.tar.gz' \ -mtime "+${days}" -delete @@ -399,10 +498,10 @@ create_backup() { if container_running; then if api_save; then - info "已请求服务器落盘存档。" + info save_requested sleep 5 else - warn "REST API 保存请求失败;仍会创建文件级备份,但一致性保障较弱。" + warn save_failed_backup fi fi @@ -411,38 +510,38 @@ create_backup() { temp_file="$BACKUP_DIR/.palworld-${timestamp}.tmp" if ! tar -C "$ROOT_DIR" -czf "$temp_file" Saved; then rm -f -- "$temp_file" - die "创建备份包失败。" + die backup_create_failed fi if ! tar -tzf "$temp_file" >/dev/null; then rm -f -- "$temp_file" - die "备份完整性校验失败。" + die backup_integrity_failed fi mv -- "$temp_file" "$final_file" chmod 600 "$final_file" prune_backups - success "备份已创建:$final_file" + success backup_created "$final_file" } validate_backup_archive() { local archive=$1 entry - tar -tzf "$archive" >/dev/null 2>&1 || die "备份包损坏或不是 tar.gz 文件。" + tar -tzf "$archive" >/dev/null 2>&1 || die backup_invalid_archive while IFS= read -r entry; do [[ "$entry" == "Saved" || "$entry" == "Saved/" || "$entry" == Saved/* ]] \ - || die "备份包包含 Saved 目录以外的路径:$entry" - [[ "/$entry/" != *"/../"* ]] || die "备份包包含不安全的上级目录路径。" - [[ "$entry" != /* ]] || die "备份包包含绝对路径。" + || die backup_outside_saved "$entry" + [[ "/$entry/" != *"/../"* ]] || die backup_parent_path + [[ "$entry" != /* ]] || die backup_absolute_path done < <(tar -tzf "$archive") if tar -tvzf "$archive" | grep -E '^[lh]' >/dev/null; then - die "为安全起见,拒绝恢复包含符号链接或硬链接的备份包。" + die backup_links_rejected fi } restore_backup() { local archive=${1:-} stage old failed timestamp had_original=false - [[ -n "$archive" ]] || die "用法:$0 restore <备份文件.tar.gz>" + [[ -n "$archive" ]] || die restore_usage "$0" archive="$(readlink -f "$archive" 2>/dev/null || true)" - [[ -f "$archive" ]] || die "找不到备份文件。" - [[ "$archive" != "$SAVE_DIR"/* ]] || die "备份文件不能放在待替换的 Saved 目录内。" + [[ -f "$archive" ]] || die backup_file_not_found + [[ "$archive" != "$SAVE_DIR"/* ]] || die backup_inside_saved require_runtime validate_backup_archive "$archive" @@ -460,10 +559,10 @@ restore_backup() { if ! tar -xzf "$archive" -C "$stage" || [[ ! -d "$stage/Saved" ]]; then rm -rf -- "$stage" compose up -d || true - die "备份解压失败,原存档未改动。" + die backup_extract_failed fi - [[ ! -e "$old" ]] || die "回滚目录已存在:$old" + [[ ! -e "$old" ]] || die rollback_dir_exists "$old" if [[ "$had_original" == true ]]; then mv -- "$SAVE_DIR" "$old" fi @@ -472,14 +571,14 @@ restore_backup() { if compose up -d && wait_until_running 120; then if [[ "$had_original" == true ]]; then - success "存档恢复完成。恢复前数据保留在:$old" + success restore_complete_old "$old" else - success "存档恢复完成。" + success restore_complete fi return 0 fi - warn "恢复后的服务器未能启动,正在自动回滚。" + warn restore_start_failed compose stop -t 30 >/dev/null 2>&1 || true mv -- "$SAVE_DIR" "$failed" if [[ "$had_original" == true ]]; then @@ -488,7 +587,7 @@ restore_backup() { mkdir -p -- "$SAVE_DIR" fi compose up -d || true - die "恢复失败,已回滚原存档;失败数据保留在:$failed" + die restore_rolled_back "$failed" } fetch_official_image() { @@ -497,7 +596,7 @@ fetch_official_image() { "$OFFICIAL_COMPOSE_URL" \ | awk '/^[[:space:]]*image:[[:space:]]*ghcr\.io\/pocketpairjp\/palserver:/ {print $2; exit}')" [[ "$image" =~ ^ghcr\.io/pocketpairjp/palserver:v[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+$ ]] \ - || die "无法从 Pocketpair 官方 Compose 文件解析有效镜像标签。" + || die official_image_parse_failed printf '%s' "$image" } @@ -506,7 +605,7 @@ resolve_target_image() { repository="$(env_get PALWORLD_IMAGE_REPOSITORY ghcr.io/pocketpairjp/palserver)" tag="${official_image##*:}" [[ "$repository" =~ ^[A-Za-z0-9._:/-]+$ && "$repository" != *: ]] \ - || die "PALWORLD_IMAGE_REPOSITORY 格式无效:$repository" + || die image_repository_invalid "$repository" printf '%s:%s' "$repository" "$tag" } @@ -517,12 +616,13 @@ check_update() { current="$(env_get PALWORLD_IMAGE "$DEFAULT_IMAGE")" official="$(fetch_official_image)" latest="$(resolve_target_image "$official")" - printf '当前镜像:%s\n官方标签:%s\n更新目标:%s\n' "$current" "${official##*:}" "$latest" + message image_status "$current" "${official##*:}" "$latest" + printf '\n' if [[ "$current" == "$latest" ]]; then - success "当前已是官方最新镜像标签。" + success image_current return 0 fi - info "发现可用更新。" + info update_available return 10 } @@ -535,43 +635,43 @@ update_server() { latest="$(resolve_target_image "$official")" if [[ "$current" == "$latest" ]]; then - success "当前已是官方最新镜像标签:$current" + success image_current_value "$current" return 0 fi - info "发现官方更新:$current -> $latest" + info official_update_found "$current" "$latest" if container_running; then was_running=true fi create_backup - if [[ "$was_running" == true ]] && api_announce "Server update in 30 seconds"; then + if [[ "$was_running" == true ]] && api_announce "$(message announce_update)"; then sleep 30 fi - info "拉取新镜像……" + info pulling_new_image docker pull "$latest" env_set PALWORLD_IMAGE "$latest" if ! compose config --quiet; then env_set PALWORLD_IMAGE "$current" - die "新镜像标签写入后 Compose 校验失败,已回滚 .env。" + die compose_validation_rollback fi if [[ "$was_running" == false ]]; then - success "镜像已更新到 $latest;服务器保持停止状态。" + success image_updated_stopped "$latest" return 0 fi if compose up -d --force-recreate && wait_until_ready 240; then - success "服务器已更新到:$latest" + success server_updated "$latest" return 0 fi - warn "新版本启动失败,正在回滚到:$current" + warn new_version_failed_rollback "$current" env_set PALWORLD_IMAGE "$current" compose up -d --force-recreate || true wait_until_ready 240 || true - die "更新失败,已尝试回滚。请查看日志。" + die update_failed_rolled_back } install_maintenance() { @@ -579,7 +679,7 @@ install_maintenance() { require_root require_runtime require_command systemctl - [[ -s "$PASSWORD_FILE" ]] || warn "尚未保存管理员密码;定时备份无法先调用 REST API 落盘。" + [[ -s "$PASSWORD_FILE" ]] || warn no_password_for_maintenance script_quoted="$(printf '%q' "$SCRIPT_PATH")" printf '#!/usr/bin/env bash\nexec /bin/bash %s "$@"\n' "$script_quoted" \ @@ -642,7 +742,7 @@ EOF systemctl daemon-reload systemctl enable --now paldsms-backup.timer paldsms-update.timer - success "自动维护已启用:每小时备份,每日检查官方镜像更新。" + success maintenance_enabled systemctl list-timers 'paldsms-*' --no-pager } @@ -657,22 +757,22 @@ uninstall_maintenance() { /etc/systemd/system/paldsms-update.timer \ /usr/local/sbin/paldsms systemctl daemon-reload - success "自动维护已卸载;游戏存档和备份未删除。" + success maintenance_removed } install_docker() { local distro codename arch require_root - [[ -r /etc/os-release ]] || die "无法识别 Linux 发行版。" + [[ -r /etc/os-release ]] || die distro_unknown # shellcheck disable=SC1091 source /etc/os-release distro="${ID:-}" codename="${VERSION_CODENAME:-}" case "$distro" in debian|ubuntu) ;; - *) die "自动安装目前支持 Debian 和 Ubuntu;当前系统:${distro:-unknown}" ;; + *) die distro_unsupported "${distro:-unknown}" ;; esac - [[ -n "$codename" ]] || die "无法识别发行版代号 VERSION_CODENAME。" + [[ -n "$codename" ]] || die distro_codename_unknown arch="$(dpkg --print-architecture)" apt-get update @@ -687,118 +787,122 @@ install_docker() { apt-get install -y docker-ce docker-ce-cli containerd.io \ docker-buildx-plugin docker-compose-plugin curl tar nano systemctl enable --now docker - success "Docker Engine 与 Compose v2 已安装。" + success docker_installed } doctor() { local failures=0 arch memory_kib memory_gib available_kib available_gib - printf 'PalDSMS v%s 环境检查\n\n' "$PALDSMS_VERSION" + message doctor_header "$PALDSMS_VERSION" + printf '\n\n' arch="$(uname -m)" if [[ "$arch" == "x86_64" || "$arch" == "amd64" ]]; then - success "CPU 架构:$arch" + success cpu_arch "$arch" else - warn "CPU 架构为 $arch;Pocketpair Linux 专用服务器主要面向 x86_64。" + warn cpu_arch_unsupported "$arch" ((failures += 1)) fi memory_kib="$(awk '/MemTotal:/ {print $2}' /proc/meminfo 2>/dev/null || printf '0')" memory_gib=$((memory_kib / 1024 / 1024)) if (( memory_gib >= 16 )); then - success "内存:约 ${memory_gib} GiB" + success memory_ok "$memory_gib" else - warn "内存仅约 ${memory_gib} GiB;官方要求 16 GiB,推荐 32 GiB 以上。" + warn memory_low "$memory_gib" ((failures += 1)) fi available_kib="$(df -Pk "$ROOT_DIR" | awk 'NR==2 {print $4}')" available_gib=$((available_kib / 1024 / 1024)) if (( available_gib >= 20 )); then - success "可用磁盘:约 ${available_gib} GiB" + success disk_ok "$available_gib" else - warn "可用磁盘仅约 ${available_gib} GiB;建议使用 SSD 并预留至少 20 GiB。" + warn disk_low "$available_gib" ((failures += 1)) fi if command -v docker >/dev/null 2>&1 && docker compose version >/dev/null 2>&1; then - success "Docker Compose v2 可用。" + success compose_available else - warn "Docker Compose v2 不可用。" + warn compose_unavailable ((failures += 1)) fi if [[ -s "$SETTINGS_FILE" ]]; then if grep -q 'RESTAPIEnabled=True' "$SETTINGS_FILE"; then - success "REST API 已在游戏配置中启用。" + success rest_api_config_enabled else - warn "REST API 尚未启用,请运行 set-password。" + warn rest_api_config_disabled fi if grep -q 'bIsUseBackupSaveData=True' "$SETTINGS_FILE"; then - success "游戏内建滚动备份已启用。" + success builtin_backup_enabled else - warn "游戏内建滚动备份尚未启用。" + warn builtin_backup_disabled fi else - warn "尚未生成服务器配置。" + warn server_config_missing fi printf '\n' if (( failures == 0 )); then - success "关键环境检查通过。" + success doctor_passed else - warn "发现 ${failures} 项需要处理的问题。" + warn doctor_failures "$failures" return 1 fi } show_help() { - cat < - - menu 打开交互式中文菜单(无参数时默认) - doctor 检查 CPU、内存、磁盘和 Docker 环境 - install-docker 在 Debian/Ubuntu 安装 Docker Engine - deploy 拉取官方镜像并首次部署 - set-password 设置管理员密码,启用本地 REST API 与内建备份 - config 编辑 PalWorldSettings.ini 并重启 - start|stop|restart 管理服务器 - status|logs 查看状态或跟踪日志 - backup 创建经过校验的外部备份 - restore <文件> 恢复备份;失败时自动回滚 - check-update 对比 Pocketpair 官方镜像标签 - update 备份后更新;启动失败时回滚镜像标签 - install-maintenance 安装 systemd 自动备份/更新定时器 - remove-maintenance 移除自动维护(不删除存档) - version 显示版本 -EOF + local index + local -a commands=( + 'menu' 'doctor' 'install-docker' 'deploy' 'set-password' 'config' + 'start|stop|restart' 'status|logs' 'backup' 'restore ' + 'check-update' 'update' 'install-maintenance' 'remove-maintenance' + 'version' + ) + local -a keys=( + help_menu help_doctor help_install_docker help_deploy help_set_password help_config + help_server_actions help_status_logs help_backup help_restore + help_check_update help_update help_install_maintenance help_remove_maintenance + help_version + ) + message app_title "$PALDSMS_VERSION" + printf '\n\n' + message usage "$0" + printf '\n\n' + for ((index=0; index<${#commands[@]}; index++)); do + printf ' %-23s ' "${commands[$index]}" + message "${keys[$index]}" + printf '\n' + done + printf '\n' + message language_help + printf '\n' } pause_menu() { [[ -t 0 ]] || return 0 - read -r -p "按回车键返回菜单……" _ + read -r -p "$(message prompt_return_menu)" _ } menu() { local choice backup_file while true; do clear 2>/dev/null || true - cat </dev/null || true - read -r -p "请输入要恢复的备份文件完整路径: " backup_file + read -r -p "$(message prompt_backup_path)" backup_file restore_backup "$backup_file" ;; 11) check_update || [[ $? -eq 10 ]] ;; @@ -835,7 +939,7 @@ EOF 14) uninstall_maintenance ;; 15) show_logs ;; 0) exit 0 ;; - *) warn "无效选项。" ;; + *) warn invalid_option ;; esac pause_menu done @@ -863,8 +967,10 @@ main() { remove-maintenance) uninstall_maintenance ;; help|-h|--help) show_help ;; version|-v|--version) printf 'PalDSMS %s\n' "$PALDSMS_VERSION" ;; - *) show_help; die "未知命令:$command" ;; + *) show_help; die unknown_command "$command" ;; esac } -main "$@" +parse_global_options "$@" +initialize_language +main "${MAIN_ARGS[@]}" diff --git a/tests/i18n-smoke.sh b/tests/i18n-smoke.sh new file mode 100755 index 0000000..994befd --- /dev/null +++ b/tests/i18n-smoke.sh @@ -0,0 +1,92 @@ +#!/usr/bin/env bash + +set -Eeuo pipefail + +ROOT_DIR="$(cd -- "$(dirname -- "${BASH_SOURCE[0]}")/.." && pwd)" +readonly ROOT_DIR +readonly MANAGER="$ROOT_DIR/palworld-manager.sh" + +tmp_dir="$(mktemp -d)" +trap 'rm -rf -- "$tmp_dir"' EXIT + +for language in zh_CN en_US ja_JP; do + bash "$MANAGER" --lang "$language" help >"$tmp_dir/help-$language.txt" +done + +grep -q '用法' "$tmp_dir/help-zh_CN.txt" +grep -q 'Usage' "$tmp_dir/help-en_US.txt" +grep -q '使い方' "$tmp_dir/help-ja_JP.txt" +grep -q '简体中文\|zh_CN' "$tmp_dir/help-zh_CN.txt" +grep -q 'English\|en_US' "$tmp_dir/help-en_US.txt" +grep -q '日本語\|ja_JP' "$tmp_dir/help-ja_JP.txt" + +for language in zh_CN en_US ja_JP; do + printf '0\n' | bash "$MANAGER" --lang "$language" menu >"$tmp_dir/menu-$language.txt" +done +grep -q '幻兽帕鲁' "$tmp_dir/menu-zh_CN.txt" +grep -q 'Palworld Docker Server Manager' "$tmp_dir/menu-en_US.txt" +grep -q 'Palworld Docker サーバー管理ツール' "$tmp_dir/menu-ja_JP.txt" + +if bash "$MANAGER" --lang zh_CN not-a-command >"$tmp_dir/unknown-zh.txt" 2>&1; then + printf 'An unknown command unexpectedly succeeded.\n' >&2 + exit 1 +fi +if bash "$MANAGER" --lang en_US not-a-command >"$tmp_dir/unknown-en.txt" 2>&1; then + printf 'An unknown command unexpectedly succeeded.\n' >&2 + exit 1 +fi +if bash "$MANAGER" --lang ja_JP not-a-command >"$tmp_dir/unknown-ja.txt" 2>&1; then + printf 'An unknown command unexpectedly succeeded.\n' >&2 + exit 1 +fi +grep -q '未知命令' "$tmp_dir/unknown-zh.txt" +grep -q 'Unknown command' "$tmp_dir/unknown-en.txt" +grep -q '不明なコマンド' "$tmp_dir/unknown-ja.txt" + +LANG=zh_CN.UTF-8 bash "$MANAGER" help >"$tmp_dir/auto-zh.txt" +LANG=ja_JP.UTF-8 bash "$MANAGER" help >"$tmp_dir/auto-ja.txt" +LANG=C bash "$MANAGER" help >"$tmp_dir/auto-en.txt" +grep -q '用法' "$tmp_dir/auto-zh.txt" +grep -q '使い方' "$tmp_dir/auto-ja.txt" +grep -q 'Usage' "$tmp_dir/auto-en.txt" + +mkdir -p "$tmp_dir/home" +printf 'PALDSMS_LANG=ja_JP\n' >"$tmp_dir/home/.env" +PALDSMS_HOME="$tmp_dir/home" LANG=zh_CN.UTF-8 bash "$MANAGER" help >"$tmp_dir/env-file.txt" +grep -q '使い方' "$tmp_dir/env-file.txt" + +PALDSMS_HOME="$tmp_dir/home" PALDSMS_LANG=en_US bash "$MANAGER" help >"$tmp_dir/env-var.txt" +grep -q 'Usage' "$tmp_dir/env-var.txt" + +PALDSMS_HOME="$tmp_dir/home" PALDSMS_LANG=zh_CN bash "$MANAGER" help --lang ja >"$tmp_dir/override.txt" +grep -q '使い方' "$tmp_dir/override.txt" + +for language in zh_CN en_US ja_JP; do + sed -n -E 's/^MSG\[([^]]+)\]=.*/\1/p' "$ROOT_DIR/locales/$language.sh" \ + | sort -u >"$tmp_dir/keys-$language.txt" +done +cmp "$tmp_dir/keys-zh_CN.txt" "$tmp_dir/keys-en_US.txt" +cmp "$tmp_dir/keys-en_US.txt" "$tmp_dir/keys-ja_JP.txt" + +{ + grep -oE '(^|[^[:alnum:]_])(message|info|success|warn|die)[[:space:]]+[a-z][a-z0-9_]*' "$MANAGER" \ + | awk '{print $NF}' + grep -oE 'help_[a-z_]+' "$MANAGER" + printf '%s\n' label_info label_success label_warning label_error +} | sort -u >"$tmp_dir/used-keys.txt" + +if ! missing_keys="$(comm -23 "$tmp_dir/used-keys.txt" "$tmp_dir/keys-en_US.txt")"; then + printf 'Could not compare used and translated message keys.\n' >&2 + exit 1 +fi +if [[ -n "$missing_keys" ]]; then + printf 'Missing translations for these message keys:\n%s\n' "$missing_keys" >&2 + exit 1 +fi + +if grep -E 'help_[a-z_]+|label_(info|success|warning|error)' "$tmp_dir"/help-*.txt; then + printf 'An untranslated message key appeared in help output.\n' >&2 + exit 1 +fi + +printf 'i18n smoke tests passed for zh_CN, en_US, and ja_JP.\n'