Commit c7804fef by Oliver Tzeng Committed by GitHub

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
parent 8e8869b0
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
🍥 **Passerelle de modèles étendus de nouvelle génération et système de gestion d'actifs d'IA** 🍥 **Passerelle de modèles étendus de nouvelle génération et système de gestion d'actifs d'IA**
<p align="center"> <p align="center">
<a href="./README.zh.md">中文</a> | <a href="./README.zh_CN.md">简体中文</a> |
<a href="./README.md">English</a> | <a href="./README.zh_TW.md">繁體中文</a> |
<strong>Français</strong> | <a href="./README.md">English</a> |
<strong>Français</strong> |
<a href="./README.ja.md">日本語</a> <a href="./README.ja.md">日本語</a>
</p> </p>
...@@ -56,10 +57,10 @@ ...@@ -56,10 +57,10 @@
## 📝 Description du projet ## 📝 Description du projet
> [!NOTE] > [!NOTE]
> Il s'agit d'un projet open-source développé sur la base de [One API](https://github.com/songquanpeng/one-api) > Il s'agit d'un projet open-source développé sur la base de [One API](https://github.com/songquanpeng/one-api)
> [!IMPORTANT] > [!IMPORTANT]
> - Ce projet est uniquement destiné à des fins d'apprentissage personnel, sans garantie de stabilité ni de support technique. > - Ce projet est uniquement destiné à des fins d'apprentissage personnel, sans garantie de stabilité ni de support technique.
> - Les utilisateurs doivent se conformer aux [Conditions d'utilisation](https://openai.com/policies/terms-of-use) d'OpenAI et aux **lois et réglementations applicables**, et ne doivent pas l'utiliser à des fins illégales. > - Les utilisateurs doivent se conformer aux [Conditions d'utilisation](https://openai.com/policies/terms-of-use) d'OpenAI et aux **lois et réglementations applicables**, et ne doivent pas l'utiliser à des fins illégales.
> - Conformément aux [《Mesures provisoires pour la gestion des services d'intelligence artificielle générative》](http://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm), veuillez ne fournir aucun service d'IA générative non enregistré au public en Chine. > - Conformément aux [《Mesures provisoires pour la gestion des services d'intelligence artificielle générative》](http://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm), veuillez ne fournir aucun service d'IA générative non enregistré au public en Chine.
...@@ -189,7 +190,7 @@ docker run --name new-api -d --restart always \ ...@@ -189,7 +190,7 @@ docker run --name new-api -d --restart always \
| Fonctionnalité | Description | | Fonctionnalité | Description |
|------|------| |------|------|
| 🎨 Nouvelle interface utilisateur | Conception d'interface utilisateur moderne | | 🎨 Nouvelle interface utilisateur | Conception d'interface utilisateur moderne |
| 🌍 Multilingue | Prend en charge le chinois, l'anglais, le français, le japonais | | 🌍 Multilingue | Prend en charge le chinois simplifié, le chinois traditionnel, l'anglais, le français et le japonais |
| 🔄 Compatibilité des données | Complètement compatible avec la base de données originale de One API | | 🔄 Compatibilité des données | Complètement compatible avec la base de données originale de One API |
| 📈 Tableau de bord des données | Console visuelle et analyse statistique | | 📈 Tableau de bord des données | Console visuelle et analyse statistique |
| 🔒 Gestion des permissions | Regroupement de jetons, restrictions de modèles, gestion des utilisateurs | | 🔒 Gestion des permissions | Regroupement de jetons, restrictions de modèles, gestion des utilisateurs |
...@@ -375,7 +376,7 @@ docker run --name new-api -d --restart always \ ...@@ -375,7 +376,7 @@ docker run --name new-api -d --restart always \
calciumion/new-api:latest calciumion/new-api:latest
``` ```
> **💡 Explication du chemin:** > **💡 Explication du chemin:**
> - `./data:/data` - Chemin relatif, données sauvegardées dans le dossier data du répertoire actuel > - `./data:/data` - Chemin relatif, données sauvegardées dans le dossier data du répertoire actuel
> - Vous pouvez également utiliser un chemin absolu, par exemple : `/your/custom/path:/data` > - Vous pouvez également utiliser un chemin absolu, par exemple : `/your/custom/path:/data`
......
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
🍥 **次世代大規模モデルゲートウェイとAI資産管理システム** 🍥 **次世代大規模モデルゲートウェイとAI資産管理システム**
<p align="center"> <p align="center">
<a href="./README.zh.md">中文</a> | <a href="./README.zh_CN.md">简体中文</a> |
<a href="./README.md">English</a> | <a href="./README.zh_TW.md">繁體中文</a> |
<a href="./README.fr.md">Français</a> | <a href="./README.md">English</a> |
<a href="./README.fr.md">Français</a> |
<strong>日本語</strong> <strong>日本語</strong>
</p> </p>
...@@ -56,10 +57,10 @@ ...@@ -56,10 +57,10 @@
## 📝 プロジェクト説明 ## 📝 プロジェクト説明
> [!NOTE] > [!NOTE]
> 本プロジェクトは、[One API](https://github.com/songquanpeng/one-api)をベースに二次開発されたオープンソースプロジェクトです > 本プロジェクトは、[One API](https://github.com/songquanpeng/one-api)をベースに二次開発されたオープンソースプロジェクトです
> [!IMPORTANT] > [!IMPORTANT]
> - 本プロジェクトは個人学習用のみであり、安定性の保証や技術サポートは提供しません。 > - 本プロジェクトは個人学習用のみであり、安定性の保証や技術サポートは提供しません。
> - ユーザーは、OpenAIの[利用規約](https://openai.com/policies/terms-of-use)および**法律法規**を遵守する必要があり、違法な目的で使用してはいけません。 > - ユーザーは、OpenAIの[利用規約](https://openai.com/policies/terms-of-use)および**法律法規**を遵守する必要があり、違法な目的で使用してはいけません。
> - [《生成式人工智能服务管理暂行办法》](http://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm)の要求に従い、中国地域の公衆に未登録の生成式AI サービスを提供しないでください。 > - [《生成式人工智能服务管理暂行办法》](http://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm)の要求に従い、中国地域の公衆に未登録の生成式AI サービスを提供しないでください。
...@@ -189,7 +190,7 @@ docker run --name new-api -d --restart always \ ...@@ -189,7 +190,7 @@ docker run --name new-api -d --restart always \
| 機能 | 説明 | | 機能 | 説明 |
|------|------| |------|------|
| 🎨 新しいUI | モダンなユーザーインターフェースデザイン | | 🎨 新しいUI | モダンなユーザーインターフェースデザイン |
| 🌍 多言語 | 中国語、英語、フランス語、日本語をサポート | | 🌍 多言語 | 簡体字中国語、繁体字中国語、英語、フランス語、日本語をサポート |
| 🔄 データ互換性 | オリジナルのOne APIデータベースと完全に互換性あり | | 🔄 データ互換性 | オリジナルのOne APIデータベースと完全に互換性あり |
| 📈 データダッシュボード | ビジュアルコンソールと統計分析 | | 📈 データダッシュボード | ビジュアルコンソールと統計分析 |
| 🔒 権限管理 | トークングループ化、モデル制限、ユーザー管理 | | 🔒 権限管理 | トークングループ化、モデル制限、ユーザー管理 |
...@@ -377,7 +378,7 @@ docker run --name new-api -d --restart always \ ...@@ -377,7 +378,7 @@ docker run --name new-api -d --restart always \
calciumion/new-api:latest calciumion/new-api:latest
``` ```
> **💡 パス説明:** > **💡 パス説明:**
> - `./data:/data` - 相対パス、データは現在のディレクトリのdataフォルダに保存されます > - `./data:/data` - 相対パス、データは現在のディレクトリのdataフォルダに保存されます
> - 絶対パスを使用することもできます:`/your/custom/path:/data` > - 絶対パスを使用することもできます:`/your/custom/path:/data`
......
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
🍥 **Next-Generation LLM Gateway and AI Asset Management System** 🍥 **Next-Generation LLM Gateway and AI Asset Management System**
<p align="center"> <p align="center">
<a href="./README.zh.md">中文</a> | <a href="./README.zh_CN.md">简体中文</a> |
<strong>English</strong> | <a href="./README.zh_TW.md">繁體中文</a> |
<a href="./README.fr.md">Français</a> | <strong>English</strong> |
<a href="./README.fr.md">Français</a> |
<a href="./README.ja.md">日本語</a> <a href="./README.ja.md">日本語</a>
</p> </p>
...@@ -56,10 +57,10 @@ ...@@ -56,10 +57,10 @@
## 📝 Project Description ## 📝 Project Description
> [!NOTE] > [!NOTE]
> This is an open-source project developed based on [One API](https://github.com/songquanpeng/one-api) > This is an open-source project developed based on [One API](https://github.com/songquanpeng/one-api)
> [!IMPORTANT] > [!IMPORTANT]
> - This project is for personal learning purposes only, with no guarantee of stability or technical support > - This project is for personal learning purposes only, with no guarantee of stability or technical support
> - Users must comply with OpenAI's [Terms of Use](https://openai.com/policies/terms-of-use) and **applicable laws and regulations**, and must not use it for illegal purposes > - Users must comply with OpenAI's [Terms of Use](https://openai.com/policies/terms-of-use) and **applicable laws and regulations**, and must not use it for illegal purposes
> - According to the [《Interim Measures for the Management of Generative Artificial Intelligence Services》](http://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm), please do not provide any unregistered generative AI services to the public in China. > - According to the [《Interim Measures for the Management of Generative Artificial Intelligence Services》](http://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm), please do not provide any unregistered generative AI services to the public in China.
...@@ -189,7 +190,7 @@ docker run --name new-api -d --restart always \ ...@@ -189,7 +190,7 @@ docker run --name new-api -d --restart always \
| Feature | Description | | Feature | Description |
|------|------| |------|------|
| 🎨 New UI | Modern user interface design | | 🎨 New UI | Modern user interface design |
| 🌍 Multi-language | Supports Chinese, English, French, Japanese | | 🌍 Multi-language | Supports Simplified Chinese, Traditional Chinese, English, French, Japanese |
| 🔄 Data Compatibility | Fully compatible with the original One API database | | 🔄 Data Compatibility | Fully compatible with the original One API database |
| 📈 Data Dashboard | Visual console and statistical analysis | | 📈 Data Dashboard | Visual console and statistical analysis |
| 🔒 Permission Management | Token grouping, model restrictions, user management | | 🔒 Permission Management | Token grouping, model restrictions, user management |
...@@ -375,7 +376,7 @@ docker run --name new-api -d --restart always \ ...@@ -375,7 +376,7 @@ docker run --name new-api -d --restart always \
calciumion/new-api:latest calciumion/new-api:latest
``` ```
> **💡 Path explanation:** > **💡 Path explanation:**
> - `./data:/data` - Relative path, data saved in the data folder of the current directory > - `./data:/data` - Relative path, data saved in the data folder of the current directory
> - You can also use absolute path, e.g.: `/your/custom/path:/data` > - You can also use absolute path, e.g.: `/your/custom/path:/data`
......
...@@ -7,9 +7,10 @@ ...@@ -7,9 +7,10 @@
🍥 **新一代大模型网关与AI资产管理系统** 🍥 **新一代大模型网关与AI资产管理系统**
<p align="center"> <p align="center">
<strong>中文</strong> | 简体中文 |
<a href="./README.md">English</a> | <a href="./README.zh_TW.md">繁體中文</a> |
<a href="./README.fr.md">Français</a> | <a href="./README.md">English</a> |
<a href="./README.fr.md">Français</a> |
<a href="./README.ja.md">日本語</a> <a href="./README.ja.md">日本語</a>
</p> </p>
...@@ -56,10 +57,10 @@ ...@@ -56,10 +57,10 @@
## 📝 项目说明 ## 📝 项目说明
> [!NOTE] > [!NOTE]
> 本项目为开源项目,在 [One API](https://github.com/songquanpeng/one-api) 的基础上进行二次开发 > 本项目为开源项目,在 [One API](https://github.com/songquanpeng/one-api) 的基础上进行二次开发
> [!IMPORTANT] > [!IMPORTANT]
> - 本项目仅供个人学习使用,不保证稳定性,且不提供任何技术支持 > - 本项目仅供个人学习使用,不保证稳定性,且不提供任何技术支持
> - 使用者必须在遵循 OpenAI 的 [使用条款](https://openai.com/policies/terms-of-use) 以及**法律法规**的情况下使用,不得用于非法用途 > - 使用者必须在遵循 OpenAI 的 [使用条款](https://openai.com/policies/terms-of-use) 以及**法律法规**的情况下使用,不得用于非法用途
> - 根据 [《生成式人工智能服务管理暂行办法》](http://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm) 的要求,请勿对中国地区公众提供一切未经备案的生成式人工智能服务 > - 根据 [《生成式人工智能服务管理暂行办法》](http://www.cac.gov.cn/2023-07/13/c_1690898327029107.htm) 的要求,请勿对中国地区公众提供一切未经备案的生成式人工智能服务
...@@ -375,7 +376,7 @@ docker run --name new-api -d --restart always \ ...@@ -375,7 +376,7 @@ docker run --name new-api -d --restart always \
calciumion/new-api:latest calciumion/new-api:latest
``` ```
> **💡 路径说明:** > **💡 路径说明:**
> - `./data:/data` - 相对路径,数据保存在当前目录的 data 文件夹 > - `./data:/data` - 相对路径,数据保存在当前目录的 data 文件夹
> - 也可使用绝对路径,如:`/your/custom/path:/data` > - 也可使用绝对路径,如:`/your/custom/path:/data`
......
This diff is collapsed. Click to expand it.
...@@ -29,7 +29,7 @@ const ( ...@@ -29,7 +29,7 @@ const (
func normalizeLocale(locale string) (string, bool) { func normalizeLocale(locale string) (string, bool) {
l := strings.ToLower(strings.TrimSpace(locale)) l := strings.ToLower(strings.TrimSpace(locale))
switch l { switch l {
case "en", "zh", "ja": case "en", "zh-CN", "zh-TW", "ja":
return l, true return l, true
default: default:
return "", false return "", false
......
...@@ -16,7 +16,8 @@ import ( ...@@ -16,7 +16,8 @@ import (
) )
const ( const (
LangZh = "zh" LangZhCN = "zh-CN"
LangZhTW = "zh-TW"
LangEn = "en" LangEn = "en"
DefaultLang = LangEn // Fallback to English if language not supported DefaultLang = LangEn // Fallback to English if language not supported
) )
...@@ -39,7 +40,7 @@ func Init() error { ...@@ -39,7 +40,7 @@ func Init() error {
bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal) bundle.RegisterUnmarshalFunc("yaml", yaml.Unmarshal)
// Load embedded translation files // 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 { for _, file := range files {
_, err := bundle.LoadMessageFileFS(localeFS, file) _, err := bundle.LoadMessageFileFS(localeFS, file)
if err != nil { if err != nil {
...@@ -49,7 +50,8 @@ func Init() error { ...@@ -49,7 +50,8 @@ func Init() error {
} }
// Pre-create localizers for supported languages // 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) localizers[LangEn] = i18n.NewLocalizer(bundle, LangEn)
// Set the TranslateMessage function in common package // Set the TranslateMessage function in common package
...@@ -201,8 +203,10 @@ func normalizeLang(lang string) string { ...@@ -201,8 +203,10 @@ func normalizeLang(lang string) string {
// Handle common variations // Handle common variations
switch { switch {
case strings.HasPrefix(lang, "zh-tw"):
return LangZhTW
case strings.HasPrefix(lang, "zh"): case strings.HasPrefix(lang, "zh"):
return LangZh return LangZhCN
case strings.HasPrefix(lang, "en"): case strings.HasPrefix(lang, "en"):
return LangEn return LangEn
default: default:
...@@ -212,7 +216,7 @@ func normalizeLang(lang string) string { ...@@ -212,7 +216,7 @@ func normalizeLang(lang string) string {
// SupportedLanguages returns a list of supported language codes // SupportedLanguages returns a list of supported language codes
func SupportedLanguages() []string { func SupportedLanguages() []string {
return []string{LangZh, LangEn} return []string{LangZhCN, LangZhTW, LangEn}
} }
// IsSupported checks if a language code is supported // IsSupported checks if a language code is supported
......
...@@ -29,12 +29,17 @@ const LanguageSelector = ({ currentLang, onLanguageChange, t }) => { ...@@ -29,12 +29,17 @@ const LanguageSelector = ({ currentLang, onLanguageChange, t }) => {
<Dropdown.Menu className='!bg-semi-color-bg-overlay !border-semi-color-border !shadow-lg !rounded-lg dark:!bg-gray-700 dark:!border-gray-600'> <Dropdown.Menu className='!bg-semi-color-bg-overlay !border-semi-color-border !shadow-lg !rounded-lg dark:!bg-gray-700 dark:!border-gray-600'>
{/* Language sorting: Order by English name (Chinese, English, French, Japanese, Russian) */} {/* Language sorting: Order by English name (Chinese, English, French, Japanese, Russian) */}
<Dropdown.Item <Dropdown.Item
onClick={() => onLanguageChange('zh')} onClick={() => onLanguageChange('zh-CN')}
className={`!px-3 !py-1.5 !text-sm !text-semi-color-text-0 dark:!text-gray-200 ${currentLang === 'zh' ? '!bg-semi-color-primary-light-default dark:!bg-blue-600 !font-semibold' : 'hover:!bg-semi-color-fill-1 dark:hover:!bg-gray-600'}`} className={`!px-3 !py-1.5 !text-sm !text-semi-color-text-0 dark:!text-gray-200 ${currentLang === 'zh-CN' ? '!bg-semi-color-primary-light-default dark:!bg-blue-600 !font-semibold' : 'hover:!bg-semi-color-fill-1 dark:hover:!bg-gray-600'}`}
> >
中文 简体中文
</Dropdown.Item> </Dropdown.Item>
<Dropdown.Item <Dropdown.Item
onClick={() => onLanguageChange('zh-TW')}
className={`!px-3 !py-1.5 !text-sm !text-semi-color-text-0 dark:!text-gray-200 ${currentLang === 'zh-TW' ? '!bg-semi-color-primary-light-default dark:!bg-blue-600 !font-semibold' : 'hover:!bg-semi-color-fill-1 dark:hover:!bg-gray-600'}`}
>
繁體中文
</Dropdown.Item> <Dropdown.Item
onClick={() => onLanguageChange('en')} onClick={() => onLanguageChange('en')}
className={`!px-3 !py-1.5 !text-sm !text-semi-color-text-0 dark:!text-gray-200 ${currentLang === 'en' ? '!bg-semi-color-primary-light-default dark:!bg-blue-600 !font-semibold' : 'hover:!bg-semi-color-fill-1 dark:hover:!bg-gray-600'}`} className={`!px-3 !py-1.5 !text-sm !text-semi-color-text-0 dark:!text-gray-200 ${currentLang === 'en' ? '!bg-semi-color-primary-light-default dark:!bg-blue-600 !font-semibold' : 'hover:!bg-semi-color-fill-1 dark:hover:!bg-gray-600'}`}
> >
......
...@@ -24,14 +24,14 @@ import { useIsMobile } from '../../../../hooks/common/useIsMobile'; ...@@ -24,14 +24,14 @@ import { useIsMobile } from '../../../../hooks/common/useIsMobile';
const SyncWizardModal = ({ visible, onClose, onConfirm, loading, t }) => { const SyncWizardModal = ({ visible, onClose, onConfirm, loading, t }) => {
const [step, setStep] = useState(0); const [step, setStep] = useState(0);
const [option, setOption] = useState('official'); const [option, setOption] = useState('official');
const [locale, setLocale] = useState('zh'); const [locale, setLocale] = useState('zh-CN');
const isMobile = useIsMobile(); const isMobile = useIsMobile();
useEffect(() => { useEffect(() => {
if (visible) { if (visible) {
setStep(0); setStep(0);
setOption('official'); setOption('official');
setLocale('zh'); setLocale('zh-CN');
} }
}, [visible]); }, [visible]);
...@@ -113,13 +113,16 @@ const SyncWizardModal = ({ visible, onClose, onConfirm, loading, t }) => { ...@@ -113,13 +113,16 @@ const SyncWizardModal = ({ visible, onClose, onConfirm, loading, t }) => {
name='sync-locale-selection' name='sync-locale-selection'
> >
<Radio value='en' extra='English'> <Radio value='en' extra='English'>
EN en
</Radio> </Radio>
<Radio value='zh' extra='中文'> <Radio value='zh-CN' extra='简体中文'>
ZH zh-CN
</Radio>
<Radio value='zh-TW' extra='繁體中文'>
zh-TW
</Radio> </Radio>
<Radio value='ja' extra='日本語'> <Radio value='ja' extra='日本語'>
JA ja
</Radio> </Radio>
</RadioGroup> </RadioGroup>
</div> </div>
......
...@@ -23,7 +23,8 @@ import LanguageDetector from 'i18next-browser-languagedetector'; ...@@ -23,7 +23,8 @@ import LanguageDetector from 'i18next-browser-languagedetector';
import enTranslation from './locales/en.json'; import enTranslation from './locales/en.json';
import frTranslation from './locales/fr.json'; import frTranslation from './locales/fr.json';
import zhTranslation from './locales/zh.json'; import zhCNTranslation from './locales/zh-CN.json';
import zhTWTranslation from './locales/zh-TW.json';
import ruTranslation from './locales/ru.json'; import ruTranslation from './locales/ru.json';
import jaTranslation from './locales/ja.json'; import jaTranslation from './locales/ja.json';
import viTranslation from './locales/vi.json'; import viTranslation from './locales/vi.json';
...@@ -32,16 +33,17 @@ i18n ...@@ -32,16 +33,17 @@ i18n
.use(LanguageDetector) .use(LanguageDetector)
.use(initReactI18next) .use(initReactI18next)
.init({ .init({
load: 'languageOnly', load: 'currentOnly',
resources: { resources: {
en: enTranslation, en: enTranslation,
zh: zhTranslation, 'zh-CN': zhCNTranslation,
'zh-TW': zhTWTranslation,
fr: frTranslation, fr: frTranslation,
ru: ruTranslation, ru: ruTranslation,
ja: jaTranslation, ja: jaTranslation,
vi: viTranslation, vi: viTranslation,
}, },
fallbackLng: 'zh', fallbackLng: 'zh-CN',
nsSeparator: false, nsSeparator: false,
interpolation: { interpolation: {
escapeValue: false, escapeValue: false,
......
This source diff could not be displayed because it is too large. You can view the blob instead.
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or sign in to comment