Model or dataset
Aspirin0000/zhouli-translator avatar
Aspirin0000/zhouli-translator

A comedy translator built with production-grade privacy discipline

合乎周礼:DeepSeek-powered Zhouli-style Chinese translator, web app, and distributable Skill package.

313 stars30 forksTypeScriptMIT

At a glance

What is it?
This MIT web app turns plain Chinese into mock-ceremonial Zhou-ritual language and back, with a genuine offline demo mode, a documented eight-step request path, analytics off by default, and a public-release script that scans for leaked secrets before every ship.
Who is it for?
This project fits a Chinese-speaking user who wants the comedic Zhouli-style register generated or decoded on demand, either through the hosted web app or the distributable skill package, and it is also a worthwhile read for anyone building a small AI web app who wants a concrete example of doing the unglamorous parts properly.
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 TypeScript, according to GitHub's language statistics.

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

DEEP OPEN-SOURCE ANALYSIS

A comedy generator with a matching decoder

This is a Chinese-language web app and distributable skill that translates ordinary text into a comedic register modelled on the ceremonial language of the ancient Zhou dynasty ritual code, and, in the other direction, decodes that same register back into plain speech. The joke format it automates is a familiar one in Chinese internet humour: take a mundane modern complaint, wrap it in solemn invocations of ritual propriety, status and duty, and argue for it as though defending an ancient moral principle.

What sets this apart from a single clever prompt is that it treats the joke as reversible. The forward direction takes a plain sentence and produces the ornate ceremonial framing. The reverse direction takes a paragraph already written in that register and strips the ceremonial wrapping back down to the plain thing it was actually saying. The README's own example demonstrates the reverse case clearly: a florid justification about not wanting to seem inhospitable decodes to a plain admission that a rate limit exists because the API kept getting hammered.

Four tones are offered in the forward direction, from a gently persuasive register to melodramatic anguish, and four in reverse, from a plain paraphrase to a pointed unmasking of the subtext, with three length tiers on each side covering everything from a short comment to a long piece.

A demo mode that works before you have committed anything

The app runs in a genuine offline demo mode when no API key is configured, previewing the interface and interaction without ever calling the underlying model. That is a small feature with an outsized effect on how safe the project feels to try.

Most projects that need a paid API key put that requirement between the user and any first impression of the software at all: clone it, configure a key, then and only then discover whether the tool is worth the setup. Making the demo path a first-class fallback rather than an afterthought means someone evaluating the project, or reading the source before deciding whether to run it, can see the actual interface and interaction flow before spending anything or handing over a credential. It is a detail that costs the maintainer a bit of extra branching logic and gives every prospective user a genuinely free look first.

The request path, traced step by step

The README documents its production request path as eight explicit steps, and reading them in order is more informative than any summary of the architecture, because it shows a system built with real attention to failure modes rather than an optimistic happy path.

The browser submits the text along with the chosen direction, mode, plainness setting, length tier, and a bounded marker identifying the calling surface. The server validates every one of those fields, including the release channel, before doing anything else. A lightweight in-memory rate limiter checks the request next. Only if an experiment flag is enabled does the server pick between two prompt variants, and only then does it assemble the direction-specific prompt itself. The underlying model returns a candidate response, at which point the system issues a random response identifier and a signed feedback token rather than anything tied to the user. Aggregate metrics are written to the analytics store in the background, with the explicit note that a failure there never blocks the actual result reaching the user. A separate endpoint verifies that signed token before recording a copy, a regeneration, or one quality signal per result. And a case is only ever stored, with a sixty-day retention deadline, after a user has explicitly consented to sharing that specific input and output.

That ordering, validating and rate-limiting before any model call, decoupling the feedback loop from the generation path with a background write that cannot block the response, and gating the one place actual content is retained behind explicit consent, is the shape of a system built by someone who has thought carefully about what happens when each individual piece fails or is abused, not just about what happens when everything works.

Analytics that default to off and expire on their own

The README states plainly that analytics are off by default in local configuration and only activate in production once a required database migration and a specific secret have both been configured, and it points to a published privacy page describing the full boundary of what is collected.

The distinction the system draws between three different kinds of data is the part worth understanding. Aggregate generation metrics are written unconditionally, but they are metrics, not content. Interaction events, a copy, a regeneration, one quality signal, are tied only to a signed, anonymous response token rather than to any persistent user identity. Only the third category, an actual input and its generated output, is stored at all, and only when the user has explicitly agreed to share that specific exchange as a case, with a fixed sixty-day deadline after which it is deleted.

Building automatic expiry into the one place raw content is retained, rather than relying on someone remembering to purge old records manually, is the kind of unglamorous discipline that most small side projects skip because nobody is watching yet. Making it visible in the README, backed by an actual published privacy page rather than a one-line claim, is a meaningfully higher bar than most comparable projects clear.

A script that checks the repository before it goes public, not after

The repository's own quality checklist includes a dedicated public-release audit script, described as scanning for obvious API keys and private key blocks before code is published, run alongside the test suite and a type-check pass as the standard set of checks used before every release.

Scanning your own repository for accidentally committed secrets before release, rather than after a leak is reported, is exactly the kind of automated check that costs almost nothing to run and occasionally prevents a genuinely bad day. It is a small thing to include in a project's own tooling, and a large number of real-world credential leaks happen precisely because nobody had it. Running one dedicated regression script against a batch of prior inputs is a similarly modest but useful habit: it means a change to the prompt-construction logic in one of the two directions can be checked against known cases before shipping, rather than trusted on inspection alone.

Together, the audit script and the batch regression runner are the kind of infrastructure a project builds once it has actually shipped and cared about breaking something in production, rather than the kind a demo repository bothers with.

Running it, and what to weigh before you do

Local setup needs Node.js 20 or newer and, for real generation rather than the demo fallback, a DeepSeek API key:

bash
npm install
cp .env.example .env.local
npm run dev

The default model configuration disables the model's extended thinking mode and caps input length distinctly for each direction, three hundred Chinese characters for the forward ceremonial-writing direction and nine hundred for the reverse decoding direction, which makes sense given a paragraph to be decoded is naturally longer than a plain sentence to be dressed up. The API key is explicitly scoped to the server and is never sent to the browser, which is the correct default and one that a surprising number of small web apps get wrong by calling a model provider directly from client-side code.

The one operational note worth taking seriously if you deploy this yourself rather than just running it locally: the README states its in-memory rate limiter is unsuitable for a multi-instance deployment and recommends swapping in shared storage such as Redis, a key-value store, or the same database used for analytics, alongside platform-level abuse controls and billing alerts. That is an honest limitation stated plainly rather than glossed over, and anyone running this at any real scale should treat it as a required step rather than an optional hardening measure.

The project is MIT licensed, with 313 stars, 30 forks and three open issues, and the last push on 2026-09-17, so it is current. Before adopting the skill package or self-hosting the web app, three steps in order. Read the published privacy page to understand the actual data boundary rather than assuming from the feature list. Run the demo mode first if you are only evaluating the interface, since it needs no key and calls nothing. And if you deploy beyond a single instance, replace the in-memory rate limiter as the README itself instructs, rather than assuming the default configuration scales.

Editorial conclusion

This project fits a Chinese-speaking user who wants the comedic Zhouli-style register generated or decoded on demand, either through the hosted web app or the distributable skill package, and it is also a worthwhile read for anyone building a small AI web app who wants a concrete example of doing the unglamorous parts properly. Its eight-step documented request path shows validation and rate-limiting happening before any model call, a feedback loop decoupled from the generation response, and raw content retained only after explicit consent with a fixed sixty-day deletion deadline. Read the published privacy page before assuming what is collected, use the offline demo mode if you only want to evaluate the interface, and if you self-host beyond a single instance, replace the in-memory rate limiter as the README itself instructs rather than trusting the default configuration to scale.

Frequently asked questions

What does the Zhouli Translator actually do?

It works in two directions: turning plain Chinese text into a comedic register modelled on ancient ceremonial ritual language, and decoding text already written in that register back into a plain statement of what it actually means. Four tones and three length tiers are available in each direction.

Can I try it without an API key?

Yes. The app falls back to a local demo mode when no DeepSeek API key is configured, previewing the interface and interaction flow without calling the underlying model, so the interface can be evaluated before any credential or cost is involved.

What data does it collect?

Analytics are off by default locally and require explicit configuration in production. Aggregate metrics are recorded, interaction events are tied only to an anonymous signed token, and actual input and output text is stored only after explicit user consent, with a fixed sixty-day retention deadline.

Is the API key exposed to the browser?

No. The README states the API key scope is server-only and is never sent to the browser. The client submits text and settings to a server endpoint, which validates the request and calls the model provider itself.

Can this be self-hosted at scale?

The README notes its default rate limiter is in-memory and unsuitable for a multi-instance deployment, recommending shared storage such as Redis, a key-value store, or a database, along with platform-level abuse controls and billing alerts for any production deployment beyond a single instance.

Official sources

  1. Aspirin0000/zhouli-translator on GitHub
  2. Issues
  3. License: MIT
  4. Project website
  5. README
Community notes

Community notes