diff --git a/src/i18n/index.tsx b/src/i18n/index.tsx index a23d56c..f7d4b02 100644 --- a/src/i18n/index.tsx +++ b/src/i18n/index.tsx @@ -9,15 +9,20 @@ import en from './locales/en/translation.json'; import es from './locales/es/translation.json'; import ja from './locales/ja/translation.json'; import ru from './locales/ru/translation.json'; -import zh from './locales/zh_CN/translation.json'; +import zh_CN from './locales/zh_CN/translation.json'; +import zh_TW from './locales/zh_TW/translation.json'; -export const locales = ['en', 'es', 'ja', 'ru', 'zh'] as const; +export const locales = ['en', 'es', 'ja', 'ru', 'zh_CN', 'zh_TW'] as const; export type Locale = (typeof locales)[number]; -export const isLocale = (locale: string): locale is Locale => locales.includes(locale as Locale); + +export const isLocale = (locale: string): locale is Locale => + locales.includes(locale as Locale) || + (locale.startsWith('zh-') && (locales.includes('zh_CN' as Locale) || locales.includes('zh_TW' as Locale))); export const localeOptions: Array> = [ { value: 'en', label: 'English' }, - { value: 'zh', label: '中文' }, + { value: 'zh_CN', label: '简体中文' }, + { value: 'zh_TW', label: '正體中文' }, { value: 'ru', label: 'Русский' }, { value: 'ja', label: '日本語' }, { value: 'es', label: 'Español' }, @@ -26,10 +31,18 @@ export const localeOptions: Array> = [ export const getLocaleOption = (locale?: Locale) => localeOptions.find((option) => option.value === locale) ?? localeOptions[0]; -const [browserConfigLocale] = Browser.i18n.getUILanguage().split('-'); -const defaultLocale = isLocale(browserConfigLocale) ? browserConfigLocale : 'en'; +const getDefaultLocale = (): Locale => { + const browserLocale = Browser.i18n.getUILanguage(); + if (browserLocale.startsWith('zh-')) { + // For Chinese, we need to differentiate between Simplified and Traditional + return browserLocale.toLowerCase().includes('tw') || browserLocale.toLowerCase().includes('hk') ? 'zh_TW' : 'zh_CN'; + } + return isLocale(browserLocale) ? (browserLocale as Locale) : 'en'; +}; + +const defaultLocale = getDefaultLocale(); -const messagesMap = { en, es, ja, ru, zh } as const; +const messagesMap = { en, es, ja, ru, zh_CN, zh_TW } as const; export const defaultTranslationValues: RichTranslationValues = { i: (children) => {children}, diff --git a/src/i18n/locales/zh_TW/messages.json b/src/i18n/locales/zh_TW/messages.json new file mode 100644 index 0000000..210ee9c --- /dev/null +++ b/src/i18n/locales/zh_TW/messages.json @@ -0,0 +1,8 @@ +{ + "description": { + "message": "Revoke.cash 瀏覽器擴充功能有助於保護您免受常見的加密詐騙。" + }, + "name": { + "message": "Revoke.cash - web3 詐騙防護" + } +} diff --git a/src/i18n/locales/zh_TW/translation.json b/src/i18n/locales/zh_TW/translation.json new file mode 100644 index 0000000..b7093c5 --- /dev/null +++ b/src/i18n/locales/zh_TW/translation.json @@ -0,0 +1,50 @@ +{ + "common": { + "address": "地址", + "bypass": "注意:網站試圖繞過 Revoke.cash 確認視窗,謹慎行事。", + "continue": "繼續", + "dismiss": "取消", + "error_occurred": "嘗試建立 Revoke.cash 彈出視窗時發生錯誤。請在我們的 Discord 中聯絡我們。", + "reject": "拒絕", + "unknown_asset": "未知資產" + }, + "confirm_allowance": { + "spender": "支出者", + "title": "授予代幣授權" + }, + "confirm_hash": { + "explanation": "這可用於列出待售 NFT 或授權資產轉移。請確保您信任該網站。", + "hash": "雜湊", + "title": "簽署雜湊簽名" + }, + "confirm_listing": { + "title": "簽署 NFT 市場列表" + }, + "confirm_suspected_scam": { + "title": "與可疑的詐騙地址互動" + }, + "popup": { + "about": { + "official_website": "官方網站", + "title": "關於", + "version": "版本" + }, + "color_theme": { + "themes": { + "dark": "深色", + "light": "淺色", + "system": "系統" + }, + "title": "主題顏色" + }, + "language": { + "title": "語言" + }, + "settings": { + "approvals": "代幣授權", + "hash_signatures": "雜湊簽名", + "listings": "NFT 市場列表", + "suspected_scams": "涉嫌詐騙" + } + } +}