mirror of
https://github.com/DeusData/codebase-memory-mcp.git
synced 2026-11-04 10:48:20 +00:00
fix(graph-ui): rank Accept-Language by q instead of substring match
detectLanguage tested whether "zh" appeared anywhere in the header, so any
visitor whose Accept-Language mentions Chinese got the Chinese UI regardless of
preference.
en-US,en;q=0.9,zh;q=0.5 -> zh, expected en
zh;q=0, en -> zh, expected en
The first is a bilingual visitor who prefers English and lists Chinese as a
fallback. The second sets q=0, which RFC 7231 defines as not acceptable.
Now parses q, drops q=0, sorts descending, and matches the primary subtag rather
than a substring. Chinese still wins whenever it is actually preferred.
The two existing assertions are unchanged and still pass.
Signed-off-by: lukiod <mohakgupta0981@gmail.com>
This commit is contained in:
1 parent
97ce23f982
commit
e5c3200f43
2 files changed
+41
-2
No files matched your search
@@ -7,6 +7,31 @@ describe("i18n", () => {
|
||||
expect(detectLanguage("de-DE,de;q=0.9")).toBe("en");
|
||||
});
|
||||
|
||||
it("ranks Accept-Language by q rather than by presence", () => {
|
||||
// A bilingual visitor who prefers English but lists Chinese as a fallback.
|
||||
expect(detectLanguage("en-US,en;q=0.9,zh;q=0.5")).toBe("en");
|
||||
expect(detectLanguage("en,zh;q=0.1")).toBe("en");
|
||||
// Chinese still wins when it is actually preferred.
|
||||
expect(detectLanguage("zh;q=0.9,en;q=0.5")).toBe("zh");
|
||||
});
|
||||
|
||||
it("treats q=0 as unacceptable", () => {
|
||||
expect(detectLanguage("zh;q=0, en")).toBe("en");
|
||||
expect(detectLanguage("zh;q=0")).toBe("en");
|
||||
});
|
||||
|
||||
it("matches the language subtag, not a substring", () => {
|
||||
expect(detectLanguage("en-GB")).toBe("en");
|
||||
expect(detectLanguage("zh-Hant-TW")).toBe("zh");
|
||||
});
|
||||
|
||||
it("honours the override and handles empty input", () => {
|
||||
expect(detectLanguage("zh-CN", "en")).toBe("en");
|
||||
expect(detectLanguage("en-US", "zh")).toBe("zh");
|
||||
expect(detectLanguage(null)).toBe("en");
|
||||
expect(detectLanguage("")).toBe("en");
|
||||
});
|
||||
|
||||
it("keeps UI chrome messages in the catalog", () => {
|
||||
expect(messages.zh.tabs.projects).toBe("项目");
|
||||
expect(messages.zh.index.newIndex).toBe("新建索引");
|
||||
|
||||
@@ -152,8 +152,22 @@ export type UiMessages = (typeof messages)[UiLanguage];
|
||||
export function detectLanguage(acceptLanguage?: string | null, override?: string | null): UiLanguage {
|
||||
if (override === "zh" || override === "en") return override;
|
||||
if (!acceptLanguage) return "en";
|
||||
const normalized = acceptLanguage.toLowerCase();
|
||||
return normalized.includes("zh-cn") || normalized.includes("zh") ? "zh" : "en";
|
||||
|
||||
// Ranked by q, not by whether "zh" appears anywhere. A substring test served
|
||||
// Chinese for "en-US,en;q=0.9,zh;q=0.5", where English is clearly preferred,
|
||||
// and for "zh;q=0, en", where q=0 means Chinese is unacceptable.
|
||||
const best = acceptLanguage
|
||||
.split(",")
|
||||
.map((part) => {
|
||||
const [tag, ...params] = part.trim().split(";");
|
||||
const q = params.map((p) => /^\s*q\s*=\s*([\d.]+)\s*$/i.exec(p)).find(Boolean);
|
||||
return { tag: tag.trim().toLowerCase(), q: q ? Number(q[1]) : 1 };
|
||||
})
|
||||
.filter(({ tag, q }) => tag && Number.isFinite(q) && q > 0)
|
||||
.sort((a, b) => b.q - a.q)
|
||||
.find(({ tag }) => tag.split("-")[0] === "zh" || tag.split("-")[0] === "en");
|
||||
|
||||
return best?.tag.startsWith("zh") ? "zh" : "en";
|
||||
}
|
||||
|
||||
let cachedLanguage: UiLanguage = "en";
|
||||
|
||||
Reference in new issue
Block a user