feat(i18n): expand Life OS to ten locales

This commit is contained in:
Codex committed 2026-09-28 20:26:39 +08:00
1 parent a88a7f550f
commit 87276a051e
25 files changed
+6080 -87

No files matched your search

+13 -3
View File
@@ -1,4 +1,4 @@
import { addonsJs, bootGuardJs, i18nJs, localeEn, localeZh, localeEnJs, localeZhJs, tasksJs, calendarJs, notificationsJs, i18nRenderersJs, systemNavigationJs, browserAppJs, dataActionsJs, terminalAppJs, dbAdminJs, indexHtml, manifest, serviceWorker, icon } from "./assets.js";
import { addonsJs, bootGuardJs, i18nJs, localeEn, localeZh, localeEnJs, localeZhJs, localeScripts, localeData, tasksJs, calendarJs, notificationsJs, i18nRenderersJs, systemNavigationJs, browserAppJs, dataActionsJs, terminalAppJs, dbAdminJs, indexHtml, manifest, serviceWorker, icon } from "./assets.js";
import { cloudApi } from "./cloud-state.js";
import { dbAdminApi } from "./db-admin.js";
@@ -16,10 +16,18 @@ function headers(type, cache = "public, max-age=3600") { return { "content-type"
function userId(request) { return request.headers.get("oai-authenticated-user-id"); }
function loginPage(request) {
const language = request.headers.get("accept-language")?.toLowerCase().startsWith("zh") ? localeZh : localeEn;
const preferred = (request.headers.get("accept-language") || "en").split(",").map(part => part.split(";")[0].trim().toLowerCase());
const code = preferred.map(value => {
if (value.startsWith("zh-hk")) return "zh-HK";
if (value.startsWith("zh-mo")) return "zh-MO";
if (value.startsWith("zh-tw") || value.startsWith("zh-hant")) return "zh-TW";
if (value.startsWith("zh")) return "zh-CN";
return ["ar", "fr", "ru", "es", "ja", "en"].find(item => value === item || value.startsWith(item + "-"));
}).find(Boolean) || "en";
const language = code === "zh-CN" ? localeZh : code === "en" ? localeEn : localeData[code];
const text = language.login;
const escape = (value) => String(value).replace(/[&<>"']/g, (char) => ({ "&": "&amp;", "<": "&lt;", ">": "&gt;", "\"": "&quot;", "'": "&#39;" }[char]));
return `<!doctype html><html lang="${language === localeZh ? "zh-CN" : "en"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Life OS</title><style>html,body{margin:0;min-height:100%;font-family:ui-sans-serif,system-ui,sans-serif;background:#121513;color:#f4f6f1}body{display:grid;place-items:center;padding:24px;box-sizing:border-box}.panel{width:min(430px,100%);border:1px solid #414942;border-radius:22px;padding:34px;background:#1a201b;box-shadow:0 28px 70px #0008}.mark{font:600 11px ui-monospace,monospace;letter-spacing:.2em;color:#b8ff3d}.title{font-size:36px;letter-spacing:-.06em;margin:18px 0 10px}.copy{color:#afb9b1;line-height:1.65;margin:0 0 26px}.sign{display:flex;align-items:center;justify-content:center;text-decoration:none;background:#b8ff3d;color:#11170d;padding:14px 18px;border-radius:12px;font-size:14px;font-weight:700}.fine{margin:18px 0 0;color:#7e8a81;font:11px ui-monospace,monospace;letter-spacing:.04em;text-align:center}</style></head><body><main class="panel"><div class="mark">LIFE OS</div><h1 class="title">${text.title}</h1><p class="copy">${escape(text.description)}</p><a class="sign" href="/signin-with-chatgpt?return_to=/" target="_top">${escape(text.continue)}</a><p class="fine">${escape(text.footnote)}</p></main></body></html>`;
return `<!doctype html><html lang="${code}" dir="${code === "ar" ? "rtl" : "ltr"}"><head><meta charset="utf-8"><meta name="viewport" content="width=device-width,initial-scale=1"><title>Life OS</title><style>html,body{margin:0;min-height:100%;font-family:ui-sans-serif,system-ui,sans-serif;background:#121513;color:#f4f6f1}body{display:grid;place-items:center;padding:24px;box-sizing:border-box}.panel{width:min(430px,100%);border:1px solid #414942;border-radius:22px;padding:34px;background:#1a201b;box-shadow:0 28px 70px #0008}.mark{font:600 11px ui-monospace,monospace;letter-spacing:.2em;color:#b8ff3d}.title{font-size:36px;letter-spacing:-.06em;margin:18px 0 10px}.copy{color:#afb9b1;line-height:1.65;margin:0 0 26px}.sign{display:flex;align-items:center;justify-content:center;text-decoration:none;background:#b8ff3d;color:#11170d;padding:14px 18px;border-radius:12px;font-size:14px;font-weight:700}.fine{margin:18px 0 0;color:#7e8a81;font:11px ui-monospace,monospace;letter-spacing:.04em;text-align:center}</style></head><body><main class="panel"><div class="mark">LIFE OS</div><h1 class="title">${text.title}</h1><p class="copy">${escape(text.description)}</p><a class="sign" href="/signin-with-chatgpt?return_to=/" target="_top">${escape(text.continue)}</a><p class="fine">${escape(text.footnote)}</p></main></body></html>`;
}
export default {
@@ -47,6 +55,8 @@ export default {
if (url.pathname === "/i18n.js") return new Response(i18nJs, { headers: headers("application/javascript; charset=utf-8", "no-cache") });
if (url.pathname === "/locales/en.js") return new Response(localeEnJs, { headers: headers("application/javascript; charset=utf-8", "no-cache") });
if (url.pathname === "/locales/zh-CN.js") return new Response(localeZhJs, { headers: headers("application/javascript; charset=utf-8", "no-cache") });
const extraLocale = /^\/locales\/(zh-HK|zh-MO|zh-TW|ar|fr|ru|es|ja)\.js$/.exec(url.pathname)?.[1];
if (extraLocale) return new Response(localeScripts[extraLocale], { headers: headers("application/javascript; charset=utf-8", "no-cache") });
if (url.pathname === "/tasks.js") return new Response(tasksJs, { headers: headers("application/javascript; charset=utf-8", "no-cache") });
if (url.pathname === "/calendar.js") return new Response(calendarJs, { headers: headers("application/javascript; charset=utf-8", "no-cache") });
if (url.pathname === "/data-actions.js") return new Response(dataActionsJs, { headers: headers("application/javascript; charset=utf-8", "no-cache") });