feat(groups): configure message language per group instead of per route

Route.lang is removed; Group.lang now drives the message language for
every route in the group (default en, custom via KV i18n:<lang>).
dispatch.ts loads translations per group and falls back to en for
groups without a lang. Admin UI: GroupEditor gains the language field,
RouteEditor/RouteCard drop theirs; docs and config example updated.
This commit is contained in:
RhenCloud 2026-08-12 00:43:47 +08:00
parent c73b642504
commit 7e45b0a09c
No known key found for this signature in database
GPG key ID: A574A617378C4E0B
12 changed files with 114 additions and 40 deletions

View file

@ -259,4 +259,62 @@ describe("dispatchEvent fallback routing", () => {
expect(sent.filter((u) => u.includes("/222/"))).toHaveLength(1);
expect(sent.filter((u) => u.includes("/111/"))).toHaveLength(0);
});
it("uses the group's message language (not the route's)", async () => {
const bodies: string[] = [];
mockFetch((url, init) => {
bodies.push(String(init?.body ?? ""));
return new Response("{}", { status: 200 });
});
const kv = createMockKV();
await kv.put(
"config:groups",
JSON.stringify([
{ id: "zh-group", name: "中文组", adminIds: [], lang: "zh" },
{ id: "en-group", name: "English", adminIds: [] },
]),
);
const env = createEnv({ KV: kv, DB: createMockDB() });
const routes: Route[] = [
{
id: "zh-push",
name: "ZH",
enabled: true,
groupId: "zh-group",
filters: [{ type: "event", match: "push" }],
targets: [{ channelId: "111" }],
},
{
id: "en-push",
name: "EN",
enabled: true,
groupId: "en-group",
filters: [{ type: "event", match: "push" }],
targets: [{ channelId: "222" }],
},
];
await dispatchEvent(
{ ...baseConfig, routes },
{
event: "push",
payload: {
repository: { full_name: "owner/repo" },
commits: [{ id: "abc", message: "fix", author: { name: "a" } }],
ref: "refs/heads/main",
compare: "https://example.com/compare",
},
},
env,
);
expect(bodies).toHaveLength(2);
const titles = bodies.map((b) => {
const parsed = JSON.parse(b) as { embeds?: Array<{ title?: string }> };
return parsed.embeds?.[0]?.title ?? "";
});
expect(titles.sort()).toEqual(
["owner/repo: 推送了 1 个提交", "owner/repo: Pushed 1 commit"].sort(),
);
});
});