diff --git a/web/ui/app.js b/web/ui/app.js
index bcff449d..93f71ee8 100644
--- a/web/ui/app.js
+++ b/web/ui/app.js
@@ -1,4 +1,12 @@
const collapsedWorkspacesStorageKey = "openpi.collapsed-workspaces";
+const languageStorageKey = "openpi.language";
+function readLanguage() {
+ try {
+ const value = localStorage.getItem(languageStorageKey);
+ if (value === "en" || value === "zh") return value;
+ } catch {}
+ return navigator.language?.toLowerCase().startsWith("zh") ? "zh" : "en";
+}
function readCollapsedWorkspaces() {
try {
const value = JSON.parse(sessionStorage.getItem(collapsedWorkspacesStorageKey) || "[]");
@@ -32,7 +40,7 @@ const state = {
liveRetry: null,
query: "",
selectedWorkspace: null,
- language: navigator.language?.toLowerCase().startsWith("zh") ? "zh" : "en",
+ language: readLanguage(),
};
const translations = {
@@ -125,6 +133,12 @@ function applyLanguage() {
}
}
+function toggleLanguage() {
+ state.language = state.language === "zh" ? "en" : "zh";
+ try { localStorage.setItem(languageStorageKey, state.language); } catch {}
+ applyLanguage();
+}
+
const $ = (id) => document.getElementById(id);
const tokenStorageKey = "openpi.web.token";
const fragmentToken = new URLSearchParams(location.hash.slice(1)).get("token");
@@ -1395,4 +1409,5 @@ $("prompt-input")?.addEventListener("keydown", (event) => {
});
applyLanguage();
+$("language-toggle")?.addEventListener("click", toggleLanguage);
void connectEvents();
diff --git a/web/ui/index.html b/web/ui/index.html
index 7aa60047..cbc4799f 100644
--- a/web/ui/index.html
+++ b/web/ui/index.html
@@ -85,6 +85,7 @@
New session
Choose a workspace and describe the work
+
Connecting