| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145 |
- // Package i18n translates the backend's user-facing Chinese strings into
- // English when the persisted UI language is English.
- //
- // The SPA keeps its own Chinese-keyed dictionary (web/src/lib/i18n-en.ts) for
- // static UI copy. This package covers the strings the Go backend generates as
- // DATA — status text, error messages, probe hints, and country names — which
- // the SPA renders verbatim and therefore cannot translate client-side.
- //
- // The product serves a single administrator whose language is persisted in the
- // settings store, so a process-level language value is sufficient; the server
- // refreshes it on startup and whenever the preference is read or written.
- package i18n
- import (
- "fmt"
- "sync/atomic"
- )
- // current holds the active UI language: "en" or "zh". It defaults to Chinese so
- // the backend behaves exactly as it always has unless the persisted preference
- // is explicitly English — the server syncs it from the settings store on
- // startup and on every preferences read/write.
- var current atomic.Value // stores string
- func init() {
- current.Store("zh")
- }
- // Set records the active UI language. Anything other than "zh" is treated as
- // English, matching the SPA's fallback.
- func Set(language string) {
- if language == "zh" {
- current.Store("zh")
- return
- }
- current.Store("en")
- }
- // Lang reports the active UI language ("en" or "zh").
- func Lang() string {
- if language, ok := current.Load().(string); ok {
- return language
- }
- return "zh"
- }
- // T translates a Chinese string to English when the active language is
- // English; otherwise it returns the input unchanged. Strings with no entry are
- // returned as-is (Chinese), mirroring the SPA dictionary's fallback.
- func T(zh string) string {
- if Lang() != "en" {
- return zh
- }
- if en, ok := zhToEn[zh]; ok {
- return en
- }
- return zh
- }
- // Tf translates a Chinese printf-style template and then formats it with args,
- // so interpolated values land inside the translated sentence.
- func Tf(template string, args ...any) string {
- return fmt.Sprintf(T(template), args...)
- }
- // zhToEn maps every user-facing Chinese string the backend emits onto its
- // English equivalent. Keys must match the source string byte-for-byte.
- var zhToEn = map[string]string{
- // ---- eSIM ----
- "已启用": "Enabled",
- "已禁用": "Disabled",
- "该 eSIM 操作暂未实现:当前仅支持列出 Profile 与切卡(启用已安装的 Profile),不支持下载/删除/改名": "This eSIM operation is not available: only listing profiles and switching (enabling an already-installed profile) are supported; download, delete, and rename are not.",
- // ---- devices ----
- "设备数量已达上限,最多只能添加 %d 台设备": "Device limit reached; at most %d devices can be added.",
- "SIM 卡归属地为%s(MCC %s),本服务不向该地区卡片提供数据/短信/VoWiFi": "The SIM's home region is %s (MCC %s); this service does not provide data, SMS, or VoWiFi to cards from that region.",
- // ---- settings / update ----
- "未配置受信任的软件更新源;不会从未知地址下载或执行文件。": "No trusted update source is configured; no files will be downloaded or executed from unknown addresses.",
- "未配置受信任的软件更新源;未执行任何更新。": "No trusted update source is configured; no update was performed.",
- // ---- proxy probe / save ----
- "检查地址、端口、防火墙与上游代理监听状态。": "Check the address, port, firewall, and that the upstream proxy is listening.",
- "该代理不能承载 ePDG 所需的 UDP;启用上游 SOCKS5 UDP 转发后重试。": "This proxy cannot carry the UDP that ePDG requires; enable upstream SOCKS5 UDP forwarding and retry.",
- "TCP 握手、认证和 UDP ASSOCIATE 均通过。": "TCP handshake, authentication, and UDP ASSOCIATE all passed.",
- "代理已保存;UDP ASSOCIATE 尚未通过。": "Proxy saved; UDP ASSOCIATE has not passed yet.",
- "代理已保存,SOCKS5 认证与 UDP ASSOCIATE 均通过。": "Proxy saved; SOCKS5 authentication and UDP ASSOCIATE both passed.",
- "代理不能承载 VoWiFi 所需的 UDP。": "The proxy cannot carry the UDP that VoWiFi requires.",
- "SOCKS5 认证与 UDP ASSOCIATE 探测通过。": "SOCKS5 authentication and UDP ASSOCIATE probe passed.",
- // ---- country names (upstream proxy country rules) ----
- "中国": "China",
- "中国香港": "Hong Kong (China)",
- "中国澳门": "Macau (China)",
- "中国台湾": "Taiwan (China)",
- "美国": "United States",
- "加拿大": "Canada",
- "英国": "United Kingdom",
- "德国": "Germany",
- "法国": "France",
- "意大利": "Italy",
- "西班牙": "Spain",
- "葡萄牙": "Portugal",
- "荷兰": "Netherlands",
- "比利时": "Belgium",
- "瑞士": "Switzerland",
- "奥地利": "Austria",
- "爱尔兰": "Ireland",
- "丹麦": "Denmark",
- "瑞典": "Sweden",
- "挪威": "Norway",
- "芬兰": "Finland",
- "波兰": "Poland",
- "捷克": "Czechia",
- "希腊": "Greece",
- "罗马尼亚": "Romania",
- "匈牙利": "Hungary",
- "乌克兰": "Ukraine",
- "俄罗斯": "Russia",
- "土耳其": "Türkiye",
- "日本": "Japan",
- "韩国": "South Korea",
- "新加坡": "Singapore",
- "马来西亚": "Malaysia",
- "泰国": "Thailand",
- "越南": "Vietnam",
- "菲律宾": "Philippines",
- "印度尼西亚": "Indonesia",
- "印度": "India",
- "巴基斯坦": "Pakistan",
- "阿联酋": "United Arab Emirates",
- "沙特阿拉伯": "Saudi Arabia",
- "以色列": "Israel",
- "澳大利亚": "Australia",
- "新西兰": "New Zealand",
- "巴西": "Brazil",
- "墨西哥": "Mexico",
- "阿根廷": "Argentina",
- "智利": "Chile",
- "哥伦比亚": "Colombia",
- "南非": "South Africa",
- "埃及": "Egypt",
- "尼日利亚": "Nigeria",
- "肯尼亚": "Kenya",
- }
|