fix(i18n): use English runtime text and localize system UI

This commit is contained in:
Arthur Koba
2026-10-25 19:44:15 +03:00
parent ada0550571
commit 65a96670d5
289 changed files with 8092 additions and 3426 deletions
+2 -2
View File
@@ -467,7 +467,7 @@ func (r *Runner) tryRestartOpenresty(ctx context.Context) {
func (r *Runner) tryRegister(ctx context.Context, nodeID *string) error {
if strings.TrimSpace(r.Config.DiscoveryToken) == "" {
return errors.New("agent_token 为空且未配置 discovery_token")
return errors.New("agent_token is empty and discovery_token is not configured")
}
slog.Info("agent discovery registration started")
response, err := r.HeartbeatService.Register(ctx, r.HeartbeatCycle.NodePayload(ctx, *nodeID))
@@ -475,7 +475,7 @@ func (r *Runner) tryRegister(ctx context.Context, nodeID *string) error {
return err
}
if response == nil || strings.TrimSpace(response.AccessToken) == "" || strings.TrimSpace(response.NodeID) == "" {
return errors.New("discovery register response 缺少 node_id 或 agent_token")
return errors.New("discovery registration response is missing node_id or agent_token")
}
snapshot, err := r.StateStore.Load()
if err != nil {
+9 -9
View File
@@ -352,25 +352,25 @@ func joinManagedPath(base string, relative string) string {
func validate(cfg *Config) error {
if cfg.ServerURL == "" {
return errors.New("server_url 不能为空")
return errors.New("server_url must not be empty")
}
if strings.TrimSpace(cfg.AccessToken) == "" && strings.TrimSpace(cfg.DiscoveryToken) == "" {
return errors.New("agent_token 和 discovery_token 不能同时为空")
return errors.New("agent_token and discovery_token must not both be empty")
}
if cfg.NodeName == "" {
return errors.New("node_name 不能为空")
return errors.New("node_name must not be empty")
}
if cfg.NodeIP == "" {
return errors.New("node_ip 不能为空")
return errors.New("node_ip must not be empty")
}
if cfg.OpenrestyObservabilityPort <= 0 || cfg.OpenrestyObservabilityPort > 65535 {
return errors.New("openresty_observability_port 必须在 1-65535 之间")
return errors.New("openresty_observability_port must be between 1 and 65535")
}
if cfg.ObservabilityReplayMinutes <= 0 {
return errors.New("observability_replay_minutes 必须大于 0")
return errors.New("observability_replay_minutes must be greater than 0")
}
if cfg.MMDBUpdateInterval <= 0 {
return errors.New("mmdb_update_interval 必须大于 0")
return errors.New("mmdb_update_interval must be greater than 0")
}
return nil
}
@@ -397,10 +397,10 @@ func (cfg *Config) toConfigFile() configFile {
// Save persists the current configuration back to its original file path.
func (cfg *Config) Save() error {
if cfg == nil {
return errors.New("config 不能为空")
return errors.New("config must not be empty")
}
if cfg.configPath == "" {
return errors.New("config path 未初始化")
return errors.New("configuration path is not initialized")
}
data, err := json.MarshalIndent(cfg.toConfigFile(), "", " ") //nolint:gosec // agent token must be persisted in local config file
if err != nil {
+15 -15
View File
@@ -325,7 +325,7 @@ func (m *Manager) ensureOpenRestyWorkerReadAccess() error {
func (m *Manager) activateConfig(ctx context.Context) error {
if m.Executor == nil {
return errors.New("executor 未配置")
return errors.New("Executor is not configured")
}
if err := m.Executor.Test(ctx); err != nil {
return err
@@ -407,7 +407,7 @@ func (m *Manager) EnsureRuntime(ctx context.Context, recreate bool) error {
m.runtimeMu.Lock()
defer m.runtimeMu.Unlock()
if m.Executor == nil {
return errors.New("executor 未配置")
return errors.New("Executor is not configured")
}
slog.Info("openresty ensure runtime requested", "recreate", recreate)
return m.Executor.EnsureRuntime(ctx, recreate)
@@ -422,7 +422,7 @@ func (m *Manager) EnsureSafeFallbackRuntime(ctx context.Context, reason string)
func (m *Manager) ensureSafeFallbackRuntime(ctx context.Context, reason string) error {
if m.Executor == nil {
return errors.New("executor 未配置")
return errors.New("Executor is not configured")
}
trimmedReason := strings.TrimSpace(reason)
if trimmedReason == "" {
@@ -444,7 +444,7 @@ func (m *Manager) CheckHealth(ctx context.Context) error {
m.runtimeMu.Lock()
defer m.runtimeMu.Unlock()
if m.Executor == nil {
return errors.New("executor 未配置")
return errors.New("Executor is not configured")
}
if m.MainConfigPath != "" {
if _, err := os.Stat(m.MainConfigPath); os.IsNotExist(err) {
@@ -462,7 +462,7 @@ func (m *Manager) Restart(ctx context.Context) error {
m.runtimeMu.Lock()
defer m.runtimeMu.Unlock()
if m.Executor == nil {
return errors.New("executor 未配置")
return errors.New("Executor is not configured")
}
slog.Info("openresty restart requested")
return m.Executor.Restart(ctx)
@@ -473,10 +473,10 @@ func (m *Manager) CurrentChecksum() (string, error) {
m.runtimeMu.Lock()
defer m.runtimeMu.Unlock()
if m.RouteConfigPath == "" {
return "", errors.New("route config path 不能为空")
return "", errors.New("route configuration path must not be empty")
}
if m.MainConfigPath == "" {
return "", errors.New("main config path 不能为空")
return "", errors.New("main configuration path must not be empty")
}
mainData, err := os.ReadFile(m.MainConfigPath)
if err != nil {
@@ -828,10 +828,10 @@ type managedFile struct {
func (m *Manager) backup() (*backupState, error) {
if m.MainConfigPath == "" {
return nil, errors.New("main config path 不能为空")
return nil, errors.New("main configuration path must not be empty")
}
if m.RouteConfigPath == "" {
return nil, errors.New("route config path 不能为空")
return nil, errors.New("route configuration path must not be empty")
}
if err := os.MkdirAll(filepath.Dir(m.MainConfigPath), nginxDirPerm); err != nil {
return nil, err
@@ -1143,10 +1143,10 @@ func (m *Manager) restoreRuntimeConfig(file *protocol.SupportFile, name string)
func (m *Manager) writeSafeDefaultFallbackFiles() error {
if strings.TrimSpace(m.MainConfigPath) == "" {
return errors.New("main config path 不能为空")
return errors.New("main configuration path must not be empty")
}
if strings.TrimSpace(m.RouteConfigPath) == "" {
return errors.New("route config path 不能为空")
return errors.New("route configuration path must not be empty")
}
if err := os.MkdirAll(filepath.Dir(m.MainConfigPath), nginxDirPerm); err != nil {
return err
@@ -1203,7 +1203,7 @@ func removeLegacyPowConfig(path string) error {
func (m *Manager) certFileTargetPath(relativePath string) (string, error) {
if strings.TrimSpace(m.CertDir) == "" {
return "", errors.New("cert dir 不能为空")
return "", errors.New("certificate directory must not be empty")
}
candidate := strings.TrimSpace(relativePath)
if strings.Contains(candidate, `\`) {
@@ -1211,7 +1211,7 @@ func (m *Manager) certFileTargetPath(relativePath string) (string, error) {
}
normalizedPath := filepath.Clean(filepath.FromSlash(candidate))
if normalizedPath == "." || normalizedPath == "" {
return "", errors.New("cert file path 不能为空")
return "", errors.New("certificate file path must not be empty")
}
if filepath.IsAbs(normalizedPath) || filepath.VolumeName(normalizedPath) != "" {
return "", fmt.Errorf("cert file path %q must be relative", relativePath)
@@ -1240,7 +1240,7 @@ func certFileMode(relativePath string) fs.FileMode {
func luaFileTargetPath(baseDir string, relativePath string) (string, error) {
if strings.TrimSpace(baseDir) == "" {
return "", errors.New("lua dir 不能为空")
return "", errors.New("lua directory must not be empty")
}
candidate := strings.TrimSpace(relativePath)
if strings.Contains(candidate, `\`) {
@@ -1248,7 +1248,7 @@ func luaFileTargetPath(baseDir string, relativePath string) (string, error) {
}
normalizedPath := filepath.Clean(filepath.FromSlash(candidate))
if normalizedPath == "." || normalizedPath == "" {
return "", errors.New("lua file path 不能为空")
return "", errors.New("lua file path must not be empty")
}
if filepath.IsAbs(normalizedPath) || filepath.VolumeName(normalizedPath) != "" {
return "", fmt.Errorf("lua file path %q must be relative", relativePath)
+18 -16
View File
@@ -14,6 +14,12 @@ import (
//go:embed pow_static
var powStaticFS embed.FS
//go:embed pow_static/locales/challenge.en.json
var powChallengeEnglish string
//go:embed pow_static/locales/challenge.zh-CN.json
var powChallengeChinese string
const openRestyPowRuntimeLua = `local _M = {}
local source = debug.getinfo(1, "S").source or ""
@@ -290,7 +296,7 @@ end
return require("pow.runtime").check()
`
const openRestyPowChallengeLua = `local cjson = require "cjson.safe"
var openRestyPowChallengeLua = `local cjson = require "cjson.safe"
local pow_challenges = ngx.shared.openflare_pow_challenges
local pow_config_dict = ngx.shared.openflare_pow_config
@@ -353,21 +359,17 @@ if string.find(accept_lang, "zh") then
lang = "zh-CN"
end
local t_title = "Making sure you're not a bot!"
local t_status = "Loading..."
local t_protected = "This site is protected by a Proof-of-Work challenge. Your browser will solve a small puzzle before the upstream response is shown."
local t_why = "Why am I seeing this?"
local t_why_desc = "OpenFlare is asking your browser to complete a lightweight computation to distinguish normal browser traffic from automated abuse. This should finish automatically."
local t_noscript = "JavaScript is required to pass this verification. Please enable JavaScript and reload."
if lang == "zh-CN" then
t_title = "正在确认你是不是机器人!"
t_status = "加载中..."
t_protected = "本网站受工作量证明(Proof-of-Work)挑战保护。在显示源站响应之前,您的浏览器将解决一个微型谜题。"
t_why = "为什么我会看到这个?"
t_why_desc = "OpenFlare 正在要求您的浏览器完成一项轻量级计算,以区分正常的浏览器流量和自动化的恶意请求。这应该会自动完成。"
t_noscript = "很遗憾,您必须启用 JavaScript 才能通过这项验证。请开启 JavaScript 并刷新页面。"
end
local translations = {
en = cjson.decode([==[` + powChallengeEnglish + `]==]),
["zh-CN"] = cjson.decode([==[` + powChallengeChinese + `]==])
}
local text = translations[lang] or translations.en
local t_title = text.title
local t_status = text.status
local t_protected = text.protected
local t_why = text.why
local t_why_desc = text.why_description
local t_noscript = text.noscript
ngx.header.content_type = "text/html; charset=utf-8"
ngx.say([[<!DOCTYPE html>
@@ -0,0 +1,8 @@
{
"title": "Making sure you're not a bot!",
"status": "Loading...",
"protected": "This site is protected by a Proof-of-Work challenge. Your browser will solve a small puzzle before the upstream response is shown.",
"why": "Why am I seeing this?",
"why_description": "OpenFlare is asking your browser to complete a lightweight computation to distinguish normal browser traffic from automated abuse. This should finish automatically.",
"noscript": "JavaScript is required to pass this verification. Please enable JavaScript and reload."
}
@@ -0,0 +1,8 @@
{
"title": "正在确认你是不是机器人!",
"status": "加载中...",
"protected": "本网站受工作量证明(Proof-of-Work)挑战保护。在显示源站响应之前,您的浏览器将解决一个微型谜题。",
"why": "为什么我会看到这个?",
"why_description": "OpenFlare 正在要求您的浏览器完成一项轻量级计算,以区分正常的浏览器流量和自动化的恶意请求。这应该会自动完成。",
"noscript": "很遗憾,您必须启用 JavaScript 才能通过这项验证。请开启 JavaScript 并刷新页面。"
}