Files
picoclaw/web/backend/i18n.go
T
zeed zhao 6ea364e67d feat(web): protect launcher dashboard with token and SPA login (#1953)
Add token-based authentication for the Launcher's embedded Web Dashboard.

- Ephemeral token generated in-memory each run (or via PICOCLAW_LAUNCHER_TOKEN env var)
- HMAC-SHA256 session cookie (HttpOnly, SameSite=Lax, Secure when HTTPS)
- Bearer token support for API/script access
- Rate limiting on login (10 attempts/IP/min)
- Referrer-Policy: no-referrer on all responses
- POST-only logout with JSON content-type (CSRF-safe)
- System tray "Copy dashboard token" action
- Login page shows contextual help (console/tray/log file path)
- Path traversal protection via path.Clean
- X-Forwarded-Host/Port/Proto support for reverse proxy deployments
- Full i18n support (English, Chinese)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-29 13:11:43 +08:00

127 lines
3.8 KiB
Go

package main
import (
"fmt"
"os"
"strings"
)
// Language represents the supported languages
type Language string
const (
LanguageEnglish Language = "en"
LanguageChinese Language = "zh"
)
// current language (default: English)
var currentLang Language = LanguageEnglish
// TranslationKey represents a translation key used for i18n
type TranslationKey string
const (
AppTooltip TranslationKey = "AppTooltip"
MenuOpen TranslationKey = "MenuOpen"
MenuOpenTooltip TranslationKey = "MenuOpenTooltip"
MenuCopyToken TranslationKey = "MenuCopyToken"
MenuCopyTokenHint TranslationKey = "MenuCopyTokenHint"
MenuAbout TranslationKey = "MenuAbout"
MenuAboutTooltip TranslationKey = "MenuAboutTooltip"
MenuVersion TranslationKey = "MenuVersion"
MenuVersionTooltip TranslationKey = "MenuVersionTooltip"
MenuGitHub TranslationKey = "MenuGitHub"
MenuDocs TranslationKey = "MenuDocs"
MenuRestart TranslationKey = "MenuRestart"
MenuRestartTooltip TranslationKey = "MenuRestartTooltip"
MenuQuit TranslationKey = "MenuQuit"
MenuQuitTooltip TranslationKey = "MenuQuitTooltip"
Exiting TranslationKey = "Exiting"
DocUrl TranslationKey = "DocUrl"
)
// Translation tables
// Chinese translations intentionally contain Han script
//
//nolint:gosmopolitan
var translations = map[Language]map[TranslationKey]string{
LanguageEnglish: {
AppTooltip: "%s - Web Console",
MenuOpen: "Open Console",
MenuOpenTooltip: "Open PicoClaw console in browser",
MenuCopyToken: "Copy dashboard token",
MenuCopyTokenHint: "Copy the current web console access token to the clipboard",
MenuAbout: "About",
MenuAboutTooltip: "About PicoClaw",
MenuVersion: "Version: %s",
MenuVersionTooltip: "Current version number",
MenuGitHub: "GitHub",
MenuDocs: "Documentation",
MenuRestart: "Restart Service",
MenuRestartTooltip: "Restart Gateway service",
MenuQuit: "Quit",
MenuQuitTooltip: "Exit PicoClaw",
Exiting: "Exiting PicoClaw...",
DocUrl: "https://docs.picoclaw.io/docs/",
},
LanguageChinese: {
AppTooltip: "%s - Web Console",
MenuOpen: "打开控制台",
MenuOpenTooltip: "在浏览器中打开 PicoClaw 控制台",
MenuCopyToken: "复制控制台口令",
MenuCopyTokenHint: "将当前 Web 控制台访问口令复制到剪贴板",
MenuAbout: "关于",
MenuAboutTooltip: "关于 PicoClaw",
MenuVersion: "版本: %s",
MenuVersionTooltip: "当前版本号",
MenuGitHub: "GitHub",
MenuDocs: "文档",
MenuRestart: "重启服务",
MenuRestartTooltip: "重启核心服务",
MenuQuit: "退出",
MenuQuitTooltip: "退出 PicoClaw",
Exiting: "正在退出 PicoClaw...",
DocUrl: "https://docs.picoclaw.io/zh-Hans/docs/",
},
}
// SetLanguage sets the current language
func SetLanguage(lang string) {
lang = strings.ToLower(strings.TrimSpace(lang))
// Extract language code before first underscore or dot
// e.g., "en_US.UTF-8" -> "en", "zh_CN" -> "zh"
if idx := strings.IndexAny(lang, "_."); idx > 0 {
lang = lang[:idx]
}
if lang == "zh" || lang == "zh-cn" || lang == "chinese" {
currentLang = LanguageChinese
} else {
currentLang = LanguageEnglish
}
}
// GetLanguage returns the current language
func GetLanguage() Language {
return currentLang
}
// T translates a key to the current language
func T(key TranslationKey, args ...any) string {
if trans, ok := translations[currentLang][key]; ok {
if len(args) > 0 {
return fmt.Sprintf(trans, args...)
}
return trans
}
return string(key)
}
// Initialize i18n from environment variable
func init() {
if lang := os.Getenv("LANG"); lang != "" {
SetLanguage(lang)
}
}