Open-source project
BigDawnGhost/wenyi avatar
BigDawnGhost/wenyi

Wenyi ships a no-setup desktop app and a Compose deployment in one tree

将被语言阻隔的作品,带到读者的语言中。Bringing literature into your language.

2,716 stars207 forksPythonMIT

At a glance

What is it?
Wenyi is a desktop application for translating whole books and long-form writing, with per-chapter digests, a live glossary, checkpoints, and an evidence-driven review pass. Its configuration files tell a longer story than the readme does: committed Postgres and Redis defaults, an empty API token with wildcard CORS, tag-derived versioning with a Docker placeholder, and a source distribution that contains only a manifest.
Who is it for?
Wenyi is a fit for someone translating a book who wants a single installer, per-chapter context, and a resumable batch loop rather than a server to operate, and it is not a fit for a reader who expects the workspace to be private by default, because the readme states plainly that text goes to whichever provider you configure unless you point it at a local model service. Check three things before trusting a run.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 1 day ago.
What is it written in?
Mainly Python, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 4, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The feature list promises no server setup, and the root ships a Compose path

The core features say projects stay in an independent local workspace and that packaged releases include the Python engine, with no separate Python, PostgreSQL, Redis, or Docker setup. The repository root holds `deploy/`, `docker/`, and `.dockerignore`, and `.env.example` opens by telling you to copy it to `deploy/.env` for Compose. That same file sets `DATABASE_URL=postgresql://wenyi:wenyi@localhost:5432/wenyi`, `REDIS_URL=redis://localhost:6379/0`, `DATA_DIR=./data`, and `WENYI_CONFIG_FILE=../config.yaml`, the last described as a host path relative to `deploy/` and mounted into all backend services. A committed `config.yaml` at the root is what `WENYI_CONFIG=config.yaml` points at. So the no-setup claim describes the packaged desktop app, and a second deployment shape lives in the same tree, addressed to Compose rather than to a desktop window.

Committed defaults leave CORS at a wildcard and the API token empty

`.env.example` holds the values a fresh checkout starts from. `WENYI_CORS_ORIGINS=*` is a wildcard, and `WENYI_API_TOKEN=` is empty, described in a comment as an optional token for HTTP Bearer authentication and for a WebSocket first message carrying `{"token":"..."}`. The same block notes the Web UI sends that initial authentication message automatically, so with an empty token the authentication path is present in the code and switched off in the configuration. Five provider variables are listed, `DEEPSEEK_API_KEY`, `OPENAI_API_KEY`, `OPENROUTER_API_KEY`, `GEMINI_API_KEY`, and `ORCAROUTER_API_KEY`, with a note that only the providers selected in the configuration need credentials, and a pointer for custom providers to add the variable named by `llm.providers.<name>.api_key_env`. `MINERU_API_KEY` is scoped to PDF input under `pipeline.pdf_backend: mineru`. The database line in the same file carries the user and the password `wenyi`. Nothing there is a secret, and all of it is a default.

Versions come from tags, and a Docker build without Git gets a placeholder

The Python project declares `dynamic = ["version"]` and the hatchling build reads it from `source = "vcs"`, so the version is derived from git tags at build time. The dev dependency group pulls in `hatch-vcs>=0.5` with a comment saying it exercises the same tag-derived versioning used by package builds, which makes that mechanism something the test suite touches on purpose rather than a dependency the application needs. `.env.example` then documents the gap it creates: a commented `WENYI_VERSION=0.0.0+docker`, labelled as the package version for Docker builds without Git metadata and meant to be overridden for releases. A Compose image built outside a checkout therefore reports a number no tag produced. The tags moved quickly around it: v1.0.2 on 30 September 2026 with live progress, full glossary context, and reliability fixes, v1.0.3 on 3 October 2026 with three-draft precision, revision diffs, and language policies, and v1.1.0 on 4 October 2026 titled Introducing Wenyi Desktop, against a newest commit dated 26 September 2026.

One repository, two workspaces, and five Python members

package.json names the root `wenyi-monorepo`, marks it private, and describes itself as frontend tooling for the Wenyi workspace. Its scripts address a pnpm workspace, with `pnpm -C apps/web dev` and `pnpm -C apps/desktop/frontend build` among them, while pyproject.toml declares a uv workspace whose members are `packages/core`, `packages/cli`, `packages/backend`, `apps/api`, and `apps/desktop/backend`. Two lockfiles sit at the root, `pnpm-lock.yaml` and `uv.lock`, and the tests run in two languages: `node --test packages/ui/tests/platform-boundaries.test.mjs` covers frontend boundaries, and pytest is configured against four Python directories. The root devDependencies list is short, only `@tauri-apps/cli` at `^2.10.0` and `openapi-typescript` at `^7.4.4`, so the desktop shell toolchain and the API type generator are the only tools declared above the workspaces themselves.

The generated schema file needs a running server before it can be rebuilt

`gen:schema` runs openapi-typescript against `http://localhost:8000/openapi.json` and writes the result to `packages/shared-schema/src/api.d.ts`. The TypeScript API types are therefore produced by starting the backend and exporting its OpenAPI document, not by a generator that reads source. The backend it points at is `dev:api`, running `uv run uvicorn wenyi_api.main:app --reload --port 8000`, and the queue worker is a separate process, `dev:worker`, running `uv run arq wenyi_api.workers.WorkerSettings`. Three lifecycles have to be up before the frontend types can be regenerated: the API, the worker, and either the web or the desktop frontend dev server. The desktop side is scripted separately as well, with `node scripts/desktop.mjs dev` and `node scripts/desktop.mjs build` wrapping the Tauri CLI in a Node script rather than invoking it directly, so the shell command a developer types does not match the package that performs the build.

The sdist target includes pyproject.toml and nothing else

The build section uses hatchling with hatch-vcs, and the two targets are configured very differently. The wheel sets `bypass-selection = true` while the sdist sets `only-include = ["pyproject.toml"]`, so a source distribution built from this project carries the manifest and not the code. That matches the root project being a shim: `[tool.uv] package = false`, and `dependencies = ["wenyi-cli"]` pointing at a workspace member declared in `[tool.uv.sources]` alongside four others. The root declares `requires-python = ">=3.10"` and is never installed as a package of its own. The same file offers two PDF paths as optional dependencies, `pdf-output` pulling `weasyprint>=69.0` and `pdf-output-lite` pulling `fpdf2>=2.8.5`, so the heavier and the lighter renderer are chosen by which extra is installed rather than by a switch visible in the interface.

Lint, types, and tests each bring their own tool to the root

Four separate tools guard the tree, and the Python two are configured in detail. Ruff runs with `line-length = 100` and `target-version = "py310"`, selecting the `E`, `F`, `W`, and `I` rule sets while ignoring `E501`, which is the long line rule disabled by hand even though the width is already fixed. Pytest points `testpaths` at `packages/core/tests`, `packages/backend/tests`, `apps/api/tests`, and `apps/desktop/backend/tests`, adds `packages/core` and `packages/cli` to `pythonpath`, and promotes a deprecation to an error through `filterwarnings`, matching a warning about using httpx with the Starlette test client. A `pyrightconfig.json` and a `.pre-commit-config.yaml` sit beside those two. One line is easy to misread: the dev group pins `httpx2>=2.13` as the transport for that test client while the warning filter names `httpx`, so both spellings appear in the same file.

The contents promise ten sections and the text stops inside the fourth

The table of contents names Why Wenyi, Core features, Interface preview, Quick start, Supported formats, Translation pipeline, Documentation, Limitations, Community, Support, Star history, and License. The text that follows covers the first four and ends inside the last one, Continue later, after the words Reopen Desktop, open the same pro. What is present is still the most technical part. The prescan builds per-chapter digests and a book-level synopsis injected into every batch. The glossary extracts proper names, terms, and recurring expressions as translation runs, detects conflicting translations, and feeds resolutions back into later batches. Three drafts plus synthesis creates three drafts and merges them at higher model cost, batch checkpoints survive a restart, optional polishing uses a stronger model, and the evidence-driven review can publish fixes unless automatic fixes are turned off. The formats and limitations entries in the contents have no text under them here.

Editorial conclusion

Wenyi is a fit for someone translating a book who wants a single installer, per-chapter context, and a resumable batch loop rather than a server to operate, and it is not a fit for a reader who expects the workspace to be private by default, because the readme states plainly that text goes to whichever provider you configure unless you point it at a local model service. Check three things before trusting a run. The Compose path in this repository starts from a database password of wenyi, an empty WENYI_API_TOKEN, and WENYI_CORS_ORIGINS set to a wildcard, so set a token and narrow the origins before anything but a laptop reaches it. Version numbers come from git tags, and a Docker build without Git metadata falls back to the placeholder 0.0.0+docker, so a deployed image can report a version no release ever cut. And the readme's contents promise Supported formats, Translation pipeline, Documentation, and Limitations sections that are not written where it points, so the limitations you would want before a long translation run are the ones the project states in passing: higher model cost for three drafts plus synthesis, an AppImage that needs execute permission, and an HTML export that arrives as a ZIP you have to extract.

Frequently asked questions

Does Wenyi send the book text to a cloud provider?

Text is sent to the provider you configure in Settings, unless you use a local model service, and a local workspace does not change that. Supported providers include DeepSeek, OpenAI, OpenRouter, OrcaRouter, Google Gemini, Ollama, vLLM, and generic OpenAI-compatible endpoints. Desktop stores API keys in the system credential store when one is available, and otherwise keeps the key for the current session only.

What does Wenyi's Three drafts plus synthesis mode cost?

It creates three drafts and synthesizes them, at a higher model cost than the Standard translation mode. Optional polishing with a stronger model and the evidence-driven whole-book review are separate configurable steps rather than part of that mode.

What does Wenyi need installed besides the desktop app?

Nothing, for the packaged release: it includes the Python engine, so Python, PostgreSQL, Redis, and Docker are not required. Assets are an .exe installer on Windows x64, an .AppImage, .deb, or .rpm on Linux x64, and a .dmg on macOS Apple Silicon, distributed without an outer ZIP, and an AppImage needs execute permission before it opens.

How does Wenyi keep terminology consistent across a whole book?

It prescans the source before translating, creating per-chapter digests and a book-level synopsis that is injected into every batch. The glossary then extracts proper names, terms, and recurring expressions as translation progresses, detects conflicting translations, surfaces them for resolution, and feeds those resolutions back into subsequent batches.

What does WENYI_API_TOKEN control in Wenyi?

The .env.example file describes it as an optional token providing HTTP Bearer authentication and a WebSocket first message of {"token":"..."}, and it is empty in the committed file. The Web UI sends that initial authentication message automatically. The same file sets WENYI_CORS_ORIGINS to a wildcard and stores the database password in the connection string as wenyi.

How does Wenyi handle EPUB and HTML output?

For EPUB it writes translated text back into the original XHTML templates and attempts to preserve styles, images, the table of contents, and anchors. An HTML export is saved as an HTML-and-assets ZIP that should be extracted before reading, and a bilingual edition with visually subdued source text and dark mode support is optional.

Official sources

  1. BigDawnGhost/wenyi on GitHub
  2. Issues
  3. License: MIT
  4. README
  5. Releases
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/bigdawnghost-wenyi.svg)](https://hysenlabs.com/projects/bigdawnghost-wenyi)