diff --git a/lib/localization/locales/manifest.json b/lib/localization/locales/manifest.json index 2138d2d..e446484 100644 --- a/lib/localization/locales/manifest.json +++ b/lib/localization/locales/manifest.json @@ -1,3 +1,3 @@ { - "supportedLanguages": ["en", "fr", "es", "pt-BR", "de", "tr", "zh-TW"] + "supportedLanguages": ["en", "fr", "es", "pt-BR", "de", "tr", "zh-CN", "zh-TW"] } diff --git a/lib/localization/locales/zh-CN.json b/lib/localization/locales/zh-CN.json new file mode 100644 index 0000000..11c7f31 --- /dev/null +++ b/lib/localization/locales/zh-CN.json @@ -0,0 +1,63 @@ +{ + "loading": "加载中...", + "why_am_i_seeing": "为什么我会看到这个?", + "protected_by": "保护由", + "made_with": "在 🇨🇦 用 ❤️ 制作", + "mascot_design": "吉祥物由", + "ai_companies_explanation": "您会看到这个画面,是因为网站管理员启用了 Anubis 来保护服务器,避免 AI 公司大量爬取网站内容。这类行为会导致网站崩溃,让所有用户都无法正常访问资源。", + "anubis_compromise": "Anubis 是一种折中做法。它采用了类似 Hashcash 的工作量证明机制(Proof-of-Work),该机制最初是为了减少垃圾邮件而提出。其核心概念是:对个别用户而言,额外的计算负担可以忽略,但对大规模爬虫来说,累积起来的成本将大幅增加,从而让爬取行为变得更困难。", + "hack_purpose": "本质上,这是一种权宜的解法,目的是提供一个“够用”的暂时性防护措施,好让开发者有更多时间针对无头浏览器进行指纹特征识别(例如:分析其字体渲染方式),以便未来不再需要对可能为合法用户的访客展示工作量证明页面。", + "jshelter_note": "请注意,Anubis 需要使用现代 JavaScript 功能,而像 JShelter 这类插件可能会阻挡这些功能。请为此域名停用 JShelter 或类似的插件。", + "version_info": "这个网站正在运行的 Anubis 版本为", + "try_again": "再试一次", + "go_home": "返回首页", + "contact_webmaster": "或者您觉得您不应该被封锁,请联系网站管理员于", + "connection_security": "请稍等,我们需要在继续之前检查您的连接安全性。", + "javascript_required": "很遗憾,您必须启用 JavaScript 才能通过这项验证。这是因为 AI 公司已经改变了网站托管的社会契约,因此我们必须采取这样的保护机制。无需 JavaScript 的解决方案仍在开发中。", + "benchmark_requires_js": "运行基准测试工具需要启用 JavaScript。", + "difficulty": "难度:", + "algorithm": "算法:", + "compare": "比较:", + "time": "时间", + "iters": "迭代", + "time_a": "时间 A", + "iters_a": "迭代 A", + "time_b": "时间 B", + "iters_b": "迭代 B", + "static_check_endpoint": "这是提供给您的反向代理服务器使用的检查端点。", + "authorization_required": "需要认证", + "cookies_disabled": "您的浏览器目前已禁用 Cookie,为了确认您是合法用户,Anubis 需要启用 Cookie。 请您为此域名启用 Cookie", + "access_denied": "拒绝访问:错误代码", + "dronebl_entry": "DroneBL 报告了一条记录", + "see_dronebl_lookup": "见", + "internal_server_error": "内部服务器错误:管理员错误地配置了 Anubis。 请联系管理员要求他们检查日志", + "invalid_redirect": "无效的重定向", + "redirect_not_parseable": "重定向 URL 无法解析", + "redirect_domain_not_allowed": "重定向的域名并不允许", + "failed_to_sign_jwt": "签署 JWT 失败", + "invalid_invocation": "无效的 MakeChallenge 调用", + "client_error_browser": "客户端错误:请确保您的浏览器是最新版本并稍候再试。", + "oh_noes": "哎呀糟糕了!", + "benchmarking_anubis": "正在进行 Anubis 性能测试!", + "you_are_not_a_bot": "你不是机器人!", + "making_sure_not_bot": "正在确认你是不是机器人!", + "celphase": "CELPHASE 设计", + "js_web_crypto_error": "您的浏览器无法正常使用 web.crypto 组件。您是否通过安全连接(HTTPS)查看此网站?", + "js_web_workers_error": "您的浏览器并不支持 Web workers (Anubis 使用这个来避免冻结您的浏览器 )您有安装像是 JShelter 之类的插件吗?", + "js_cookies_error": "您的浏览器无法存储 Cookie。 Anubis 会使用 Cookie 存储签署的凭证,以判断用户是否已通过验证。请为此域名启用 Cookie 存储功能。 请注意,Anubis 存储的 Cookie 名称可能会变动,且其名称与内容不属于公开 API 的一部分。", + "js_context_not_secure": "您的内容并不安全", + "js_context_not_secure_msg": "请尝试使用 HTTPS 连接,或联系网站管理员设置 HTTPS。更多信息请参见 MDN。", + "js_calculating": "计算中...", + "js_missing_feature": "缺少功能", + "js_challenge_error": "挑战错误!", + "js_challenge_error_msg": "解决检查算法失败。 您可能会想要刷新页面。", + "js_calculating_difficulty": "计算中...
难度:", + "js_speed": "速度:", + "js_verification_longer": "验证所花的时间高于预期。 请不要刷新页面。", + "js_success": "成功!", + "js_done_took": "完成! 花费", + "js_iterations": "迭代", + "js_finished_reading": "我读完了,继续 →", + "js_calculation_error": "计算错误!", + "js_calculation_error_msg": "计算挑战失败:" +} diff --git a/lib/localization/localization_test.go b/lib/localization/localization_test.go index cdfdf5b..ee9a113 100644 --- a/lib/localization/localization_test.go +++ b/lib/localization/localization_test.go @@ -2,6 +2,7 @@ package localization import ( "encoding/json" + "fmt" "sort" "testing" @@ -11,105 +12,42 @@ import ( func TestLocalizationService(t *testing.T) { service := NewLocalizationService() - t.Run("English localization", func(t *testing.T) { - localizer := service.GetLocalizer("en") - result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"}) - if result != "Loading..." { - t.Errorf("Expected 'Loading...', got '%s'", result) - } - }) + loadingStrMap := map[string]string{ + "en": "Loading...", + "fr": "Chargement...", + "de": "Ladevorgang...", + "tr": "Yükleniyor...", + "zh-CN": "加载中...", + "zh-TW": "載入中...", + } - t.Run("French localization", func(t *testing.T) { - localizer := service.GetLocalizer("fr") - result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"}) - if result != "Chargement..." { - t.Errorf("Expected 'Chargement...', got '%s'", result) - } - }) - - t.Run("German localization", func(t *testing.T) { - localizer := service.GetLocalizer("de") - result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"}) - if result != "Ladevorgang..." { - t.Errorf("Expected 'Ladevorgang...', got '%s'", result) - } - }) - - t.Run("Turkish localization", func(t *testing.T) { - localizer := service.GetLocalizer("tr") - result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"}) - if result != "Yükleniyor..." { - t.Errorf("Expected 'Yükleniyor...', got '%s'", result) - } - }) - - t.Run("Traditional Chinese localization", func(t *testing.T) { - localizer := service.GetLocalizer("zh-TW") - result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"}) - if result != "載入中..." { - t.Errorf("Expected '載入中...', got '%s'", result) - } - }) - - t.Run("All required keys exist in English", func(t *testing.T) { - localizer := service.GetLocalizer("en") - requiredKeys := []string{ - "loading", "why_am_i_seeing", "protected_by", "made_with", - "mascot_design", "try_again", "go_home", "javascript_required", - } - - for _, key := range requiredKeys { - result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: key}) - if result == "" { - t.Errorf("Key '%s' returned empty string", key) + for lang, expected := range loadingStrMap { + t.Run(fmt.Sprintf("%s localization", lang), func(t *testing.T) { + localizer := service.GetLocalizer(lang) + result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: "loading"}) + if result != expected { + t.Errorf("Expected '%s', got '%s'", expected, result) } - } - }) + }) + } - t.Run("All required keys exist in French", func(t *testing.T) { - localizer := service.GetLocalizer("fr") - requiredKeys := []string{ - "loading", "why_am_i_seeing", "protected_by", "made_with", - "mascot_design", "try_again", "go_home", "javascript_required", - } + // Test for requiredKeys localization + requiredKeys := []string{ + "loading", "why_am_i_seeing", "protected_by", "made_with", + "mascot_design", "try_again", "go_home", "javascript_required", + } - for _, key := range requiredKeys { - result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: key}) - if result == "" { - t.Errorf("Key '%s' returned empty string", key) + for lang := range loadingStrMap { + t.Run(fmt.Sprintf("All required keys exist in %s", lang), func(t *testing.T) { + loc := service.GetLocalizer(lang) + for _, key := range requiredKeys { + result := loc.MustLocalize(&i18n.LocalizeConfig{MessageID: key}) + if result == "" { + t.Errorf("Key '%s' returned empty string", key) + } } - } - }) - - t.Run("All required keys exist in Turkish", func(t *testing.T) { - localizer := service.GetLocalizer("tr") - requiredKeys := []string{ - "loading", "why_am_i_seeing", "protected_by", "made_with", - "mascot_design", "try_again", "go_home", "javascript_required", - } - - for _, key := range requiredKeys { - result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: key}) - if result == "" { - t.Errorf("Key '%s' returned empty string", key) - } - } - }) - - t.Run("All required keys exist in Traditional Chinese", func(t *testing.T) { - localizer := service.GetLocalizer("zh-TW") - requiredKeys := []string{ - "loading", "why_am_i_seeing", "protected_by", "made_with", - "mascot_design", "try_again", "go_home", "javascript_required", - } - - for _, key := range requiredKeys { - result := localizer.MustLocalize(&i18n.LocalizeConfig{MessageID: key}) - if result == "" { - t.Errorf("Key '%s' returned empty string", key) - } - } - }) + }) + } } type manifest struct {