feat(localization): added zh_TW (#2913)

* feat(localization): added zh_TW

* fixed based on @coderabbitai

* updated false translation for zh_TW

* new workflow

* revert

* fixed a lot of translations

* turned most zh to zh-CN

* fallbacklang

* bruh

* eliminate ALL _

* fix: paths and other miscs thanks @Calcium-Ion

* fixed translation and temp fix for preferencessettings.js

* fixed translation error

* fixed issue about legacy support

* reverted stupid coderabbit's suggestion
This commit is contained in:
Oliver Tzeng
2026-02-11 20:37:53 +08:00
committed by GitHub
parent 8e8869b0c7
commit c7804fef69
15 changed files with 3724 additions and 180 deletions

View File

@@ -16,7 +16,8 @@ import (
)
const (
LangZh = "zh"
LangZhCN = "zh-CN"
LangZhTW = "zh-TW"
LangEn = "en"
DefaultLang = LangEn // Fallback to English if language not supported
)
@@ -39,7 +40,7 @@ func Init() error {
bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
// Load embedded translation files
files := []string{"locales/zh.yaml", "locales/en.yaml"}
files := []string{"locales/zh-CN.yaml", "locales/zh-TW.yaml", "locales/en.yaml"}
for _, file := range files {
_, err := bundle.LoadMessageFileFS(localeFS, file)
if err != nil {
@@ -49,7 +50,8 @@ func Init() error {
}
// Pre-create localizers for supported languages
localizers[LangZh] = i18n.NewLocalizer(bundle, LangZh)
localizers[LangZhCN] = i18n.NewLocalizer(bundle, LangZhCN)
localizers[LangZhTW] = i18n.NewLocalizer(bundle, LangZhTW)
localizers[LangEn] = i18n.NewLocalizer(bundle, LangEn)
// Set the TranslateMessage function in common package
@@ -201,8 +203,10 @@ func normalizeLang(lang string) string {
// Handle common variations
switch {
case strings.HasPrefix(lang, "zh-tw"):
return LangZhTW
case strings.HasPrefix(lang, "zh"):
return LangZh
return LangZhCN
case strings.HasPrefix(lang, "en"):
return LangEn
default:
@@ -212,7 +216,7 @@ func normalizeLang(lang string) string {
// SupportedLanguages returns a list of supported language codes
func SupportedLanguages() []string {
return []string{LangZh, LangEn}
return []string{LangZhCN, LangZhTW, LangEn}
}
// IsSupported checks if a language code is supported

252
i18n/locales/zh-TW.yaml Normal file
View File

@@ -0,0 +1,252 @@
# Chinese (Traditional) translations
# 中文(繁體)翻譯檔案
# Common messages
common.invalid_params: "無效的參數"
common.database_error: "資料庫錯誤,請稍後重試"
common.retry_later: "請稍後重試"
common.generate_failed: "生成失敗"
common.not_found: "未找到"
common.unauthorized: "未授權"
common.forbidden: "無權限"
common.invalid_id: "無效的ID"
common.id_empty: "ID 為空!"
common.feature_disabled: "該功能未啟用"
common.operation_success: "操作成功"
common.operation_failed: "操作失敗"
common.update_success: "更新成功"
common.update_failed: "更新失敗"
common.create_success: "建立成功"
common.create_failed: "建立失敗"
common.delete_success: "刪除成功"
common.delete_failed: "刪除失敗"
common.already_exists: "已存在"
common.name_cannot_be_empty: "名稱不能為空"
# Token messages
token.name_too_long: "令牌名稱過長"
token.quota_negative: "額度值不能為負數"
token.quota_exceed_max: "額度值超出有效範圍,最大值為 {{.Max}}"
token.generate_failed: "生成令牌失敗"
token.get_info_failed: "獲取令牌資訊失敗,請稍後重試"
token.expired_cannot_enable: "令牌已過期,無法啟用,請先修改令牌過期時間,或者設定為永不過期"
token.exhausted_cannot_enable: "令牌可用額度已用盡,無法啟用,請先修改令牌剩餘額度,或者設定為無限額度"
token.invalid: "無效的令牌"
token.not_provided: "未提供令牌"
token.expired: "該令牌已過期"
token.exhausted: "該令牌額度已用盡 TokenStatusExhausted[sk-{{.Prefix}}***{{.Suffix}}]"
token.status_unavailable: "該令牌狀態不可用"
token.db_error: "無效的令牌,資料庫查詢出錯,請聯繫管理員"
# Redemption messages
redemption.name_length: "兌換碼名稱長度必須在1-20之間"
redemption.count_positive: "兌換碼個數必須大於0"
redemption.count_max: "一次兌換碼批量生成的個數不能大於 100"
redemption.create_failed: "建立兌換碼失敗,請稍後重試"
redemption.invalid: "無效的兌換碼"
redemption.used: "該兌換碼已被使用"
redemption.expired: "該兌換碼已過期"
redemption.failed: "兌換失敗,請稍後重試"
redemption.not_provided: "未提供兌換碼"
redemption.expire_time_invalid: "過期時間不能早於當前時間"
# User messages
user.password_login_disabled: "管理員關閉了密碼登錄"
user.register_disabled: "管理員關閉了新使用者註冊"
user.password_register_disabled: "管理員關閉了通過密碼進行註冊,請使用第三方帳號驗證的形式進行註冊"
user.username_or_password_empty: "使用者名或密碼為空"
user.username_or_password_error: "使用者名或密碼錯誤,或使用者已被封禁"
user.email_or_password_empty: "信箱位址或密碼為空!"
user.exists: "使用者名已存在,或已註銷"
user.not_exists: "使用者不存在"
user.disabled: "該使用者已被禁用"
user.session_save_failed: "無法保存對話,請重試"
user.require_2fa: "請輸入雙重驗證碼"
user.email_verification_required: "管理員開啟了信箱驗證,請輸入信箱位址和驗證碼"
user.verification_code_error: "驗證碼錯誤或已過期"
user.input_invalid: "輸入不合法 {{.Error}}"
user.no_permission_same_level: "無權獲取同級或更高等級使用者的資訊"
user.no_permission_higher_level: "無權更新同權限等級或更高權限等級的使用者資訊"
user.cannot_create_higher_level: "無法建立權限大於等於自己的使用者"
user.cannot_delete_root_user: "不能刪除超級管理員帳號"
user.cannot_disable_root_user: "無法禁用超級管理員使用者"
user.cannot_demote_root_user: "無法降級超級管理員使用者"
user.already_admin: "該使用者已經是管理員"
user.already_common: "該使用者已經是普通使用者"
user.admin_cannot_promote: "普通管理員使用者無法提升其他使用者為管理員"
user.original_password_error: "原密碼錯誤"
user.invite_quota_insufficient: "邀請額度不足!"
user.transfer_quota_minimum: "轉移額度最小為{{.Min}}"
user.transfer_success: "劃轉成功"
user.transfer_failed: "劃轉失敗 {{.Error}}"
user.topup_processing: "充值處理中,請稍後重試"
user.register_failed: "使用者註冊失敗或使用者ID獲取失敗"
user.default_token_failed: "生成預設令牌失敗"
user.aff_code_empty: "affCode 為空!"
user.email_empty: "email 為空!"
user.github_id_empty: "GitHub id 為空!"
user.discord_id_empty: "discord id 為空!"
user.oidc_id_empty: "oidc id 為空!"
user.wechat_id_empty: "WeChat id 為空!"
user.telegram_id_empty: "Telegram id 為空!"
user.telegram_not_bound: "該 Telegram 帳號未綁定"
user.linux_do_id_empty: "Linux DO id 為空!"
# Quota messages
quota.negative: "額度不能為負數!"
quota.exceed_max: "額度值超出有效範圍"
quota.insufficient: "額度不足"
quota.warning_invalid: "無效的預警類型"
quota.threshold_gt_zero: "預警閾值必須大於0"
# Subscription messages
subscription.not_enabled: "訂閱方案未啟用"
subscription.title_empty: "訂閱方案標題不能為空"
subscription.price_negative: "價格不能為負數"
subscription.price_max: "價格不能超過9999"
subscription.purchase_limit_negative: "購買上限不能為負數"
subscription.quota_negative: "總額度不能為負數"
subscription.group_not_exists: "升級分組不存在"
subscription.reset_cycle_gt_zero: "自訂重置週期需大於0秒"
subscription.purchase_max: "已達到該訂閱方案購買上限"
subscription.invalid_id: "無效的訂閱ID"
subscription.invalid_user_id: "無效的使用者ID"
# Payment messages
payment.not_configured: "當前管理員未設定支付資訊"
payment.method_not_exists: "不存在此支付方式"
payment.callback_error: "回調位址設定錯誤"
payment.create_failed: "建立訂單失敗"
payment.start_failed: "啟用支付失敗"
payment.amount_too_low: "訂閱方案金額過低"
payment.stripe_not_configured: "Stripe 未設定或密鑰無效"
payment.webhook_not_configured: "Webhook 未設定"
payment.price_id_not_configured: "該訂閱方案未設定 StripePriceId"
payment.creem_not_configured: "該訂閱方案未設定 CreemProductId"
# Topup messages
topup.not_provided: "未提供支付單號"
topup.order_not_exists: "充值訂單不存在"
topup.order_status: "充值訂單狀態錯誤"
topup.failed: "充值失敗,請稍後重試"
topup.invalid_quota: "無效的充值額度"
# Channel messages
channel.not_exists: "管道不存在"
channel.id_format_error: "管道ID格式錯誤"
channel.no_available_key: "沒有可用的管道密鑰"
channel.get_list_failed: "獲取管道列表失敗,請稍後重試"
channel.get_tags_failed: "獲取標籤失敗,請稍後重試"
channel.get_key_failed: "獲取管道密鑰失敗"
channel.get_ollama_failed: "獲取Ollama模型失敗"
channel.query_failed: "查詢管道失敗"
channel.no_valid_upstream: "無有效上游管道"
channel.upstream_saturated: "當前分組上游負載已飽和,請稍後再試"
channel.get_available_failed: "獲取分組 {{.Group}} 下模型 {{.Model}} 的可用管道失敗"
# Model messages
model.name_empty: "模型名稱不能為空"
model.name_exists: "模型名稱已存在"
model.id_missing: "缺少模型 ID"
model.get_list_failed: "獲取模型列表失敗,請稍後重試"
model.get_failed: "獲取上游模型失敗"
model.reset_success: "重置模型倍率成功"
# Vendor messages
vendor.name_empty: "供應商名稱不能為空"
vendor.name_exists: "供應商名稱已存在"
vendor.id_missing: "缺少供應商 ID"
# Group messages
group.name_type_empty: "組名稱和類型不能為空"
group.name_exists: "組名稱已存在"
group.id_missing: "缺少組 ID"
# Checkin messages
checkin.disabled: "簽到功能未啟用"
checkin.already_today: "今日已簽到"
checkin.failed: "簽到失敗,請稍後重試"
checkin.quota_failed: "簽到失敗:更新額度出錯"
# Passkey messages
passkey.create_failed: "無法建立 Passkey 憑證"
passkey.login_abnormal: "Passkey 登錄狀態異常"
passkey.update_failed: "Passkey 憑證更新失敗"
passkey.invalid_user_id: "無效的使用者 ID"
passkey.verify_failed: "Passkey 驗證失敗,請重試或聯繫管理員"
# 2FA messages
twofa.not_enabled: "使用者未啟用2FA"
twofa.user_id_empty: "使用者ID不能為空"
twofa.already_exists: "使用者已存在2FA設定"
twofa.record_id_empty: "2FA記錄ID不能為空"
twofa.code_invalid: "驗證碼或備用碼不正確"
# Rate limit messages
rate_limit.reached: "您已達到請求數限制:{{.Minutes}}分鐘內最多請求{{.Max}}次"
rate_limit.total_reached: "您已達到總請求數限制:{{.Minutes}}分鐘內最多請求{{.Max}}次,包括失敗次數"
# Setting messages
setting.invalid_type: "無效的預警類型"
setting.webhook_empty: "Webhook位址不能為空"
setting.webhook_invalid: "無效的Webhook位址"
setting.email_invalid: "無效的信箱位址"
setting.bark_url_empty: "Bark推送URL不能為空"
setting.bark_url_invalid: "無效的Bark推送URL"
setting.gotify_url_empty: "Gotify伺服器位址不能為空"
setting.gotify_token_empty: "Gotify令牌不能為空"
setting.gotify_url_invalid: "無效的Gotify伺服器位址"
setting.url_must_http: "URL必須以http://或https://開頭"
setting.saved: "設定已更新"
# Deployment messages (io.net)
deployment.not_enabled: "io.net 模型部署功能未啟用或 API 密鑰缺失"
deployment.id_required: "deployment ID 為必填項"
deployment.container_id_required: "container ID 為必填項"
deployment.name_empty: "deployment 名稱不能為空"
deployment.name_taken: "deployment 名稱已被使用,請選擇其他名稱"
deployment.hardware_id_required: "hardware_id 參數為必填項"
deployment.hardware_invalid_id: "無效的 hardware_id 參數"
deployment.api_key_required: "api_key 為必填項"
deployment.invalid_payload: "無效的請求內容"
deployment.not_found: "未找到容器詳情"
# Performance messages
performance.disk_cache_cleared: "不活躍的磁碟快取已清理"
performance.stats_reset: "統計資訊已重置"
performance.gc_executed: "GC 已執行"
# Ability messages
ability.db_corrupted: "資料庫一致性被破壞"
ability.repair_running: "已經有一個修復任務在運行中,請稍後再試"
# OAuth messages
oauth.invalid_code: "無效的授權碼"
oauth.get_user_error: "獲取使用者資訊失敗"
oauth.account_used: "該帳號已被其他使用者綁定"
oauth.unknown_provider: "未知的 OAuth 供應者"
oauth.state_invalid: "state 參數為空或不匹配"
oauth.not_enabled: "管理員未開啟通過 {{.Provider}} 登錄以及註冊"
oauth.user_deleted: "使用者已註銷"
oauth.user_banned: "使用者已被封禁"
oauth.bind_success: "綁定成功"
oauth.already_bound: "該 {{.Provider}} 帳號已被綁定"
oauth.connect_failed: "無法連接至 {{.Provider}} 伺服器,請稍後重試"
oauth.token_failed: "{{.Provider}} 獲取 Token 失敗,請檢查設定"
oauth.user_info_empty: "{{.Provider}} 獲取使用者資訊為空,請檢查設定"
oauth.trust_level_low: "Linux DO 信任等級未達到管理員設定的最低信任等級"
# Model layer error messages
redeem.failed: "兌換失敗,請稍後重試"
user.create_default_token_error: "建立預設令牌失敗"
common.uuid_duplicate: "請重試,系統生成的 UUID 竟然重複了!"
common.invalid_input: "輸入不合法"
# Custom OAuth provider messages
custom_oauth.not_found: "自訂 OAuth 供應者不存在"
custom_oauth.slug_empty: "標識符不能為空"
custom_oauth.slug_exists: "標識符已存在"
custom_oauth.name_empty: "供應者名稱不能為空"
custom_oauth.has_bindings: "無法刪除已有使用者綁定的供應者"
custom_oauth.binding_not_found: "OAuth 綁定不存在"
custom_oauth.provider_id_field_invalid: "無法從供應者響應中提取使用者 ID"