diff --git a/src/i18n/i18n-types.ts b/src/i18n/i18n-types.ts index 4c95f941..c962c270 100644 --- a/src/i18n/i18n-types.ts +++ b/src/i18n/i18n-types.ts @@ -11,6 +11,7 @@ export type Locales = | 'ja' | 'pt-br' | 'tr' + | 'zh-cn' export type Translation = RootTranslation diff --git a/src/i18n/i18n-util.async.ts b/src/i18n/i18n-util.async.ts index ef6fe9fc..c51e671f 100644 --- a/src/i18n/i18n-util.async.ts +++ b/src/i18n/i18n-util.async.ts @@ -11,6 +11,7 @@ const localeTranslationLoaders = { ja: () => import('./ja'), 'pt-br': () => import('./pt-br'), tr: () => import('./tr'), + 'zh-cn': () => import('./zh-cn'), } const updateDictionary = (locale: Locales, dictionary: Partial): Translations => diff --git a/src/i18n/i18n-util.sync.ts b/src/i18n/i18n-util.sync.ts index 4c506fff..4a6a6401 100644 --- a/src/i18n/i18n-util.sync.ts +++ b/src/i18n/i18n-util.sync.ts @@ -10,6 +10,7 @@ import es from './es' import ja from './ja' import pt_br from './pt-br' import tr from './tr' +import zh_cn from './zh-cn' const localeTranslations = { en, @@ -17,6 +18,7 @@ const localeTranslations = { ja, 'pt-br': pt_br, tr, + zh_cn, } export const loadLocale = (locale: Locales): void => { diff --git a/src/i18n/i18n-util.ts b/src/i18n/i18n-util.ts index 9aa70b00..b6706b4d 100644 --- a/src/i18n/i18n-util.ts +++ b/src/i18n/i18n-util.ts @@ -15,7 +15,8 @@ export const locales: Locales[] = [ 'es', 'ja', 'pt-br', - 'tr' + 'tr', + 'zh-cn', ] export const isLocale = (locale: string): locale is Locales => locales.includes(locale as Locales) diff --git a/src/i18n/zh-cn/index.ts b/src/i18n/zh-cn/index.ts new file mode 100644 index 00000000..56bca8fc --- /dev/null +++ b/src/i18n/zh-cn/index.ts @@ -0,0 +1,134 @@ +import en from '../en'; +import type { BaseTranslation, Translation } from '../i18n-types'; + +const zh_cn = { + ...(en as Translation), + allowConnections: '更改您的服务器设置以允许连接:', + allowMixedContent: '允许混合内容', + areYouSureYouWantToDeleteAllKnowledge: '确定删除所有的知识吗?', + areYouSureYouWantToDeleteAllSessions: '确定删除所有的会话吗?', + areYouSureYouWantToDeleteAllSettings: '确定删除所有的设置吗?', + areYouSureYouWantToLeave: '确定离开吗?\n当前进度将中断', + assistant: '助手', + automatic: '自动', + automaticallyCheckForUpdates: '自动检查更新', + availableModels: '可用模型', + browseModels: '浏览可用模型', + cancel: '取消', + cantConnectToOllamaServer: "无法连接到 Ollama 服务器", + checkBrowserExtensions: '请检查并确认浏览器扩展没有阻塞连接', + checkingForUpdates: '检查更新中...', + checkNow: '立刻检查', + clear: '清除', + confirmDeletion: '确定删除', + connected: '已连接', + content: '内容', + controls: '控制器', + copy: '复制', + couldntCheckForUpdates: "无法自动检查更新", + couldntConnectToOllamaServer: "无法连接到 Ollama 服务器", + creatingTunnel: '正在创建隧道', + currentVersion: '当前版本', + dangerZone: '危险区域', + dark: '暗色', + deleteAllKnowledge: '删除所有知识', + deleteAllSessions: '删除所有会话', + deleteAllSettings: '删除所有设置', + deleteKnowledge: '删除知识', + deleteSession: '删除会话', + disconnected: '已断开连接', + dismiss: '忽略', + downloadModel: '下载模型', + edit: '编辑', + emptyKnowledge: '无知识', + emptySessions: '无会话', + error: '错误', + // f16Kv: 'F16 KV', + frequencyPenalty: 'Frequency penalty', + genericError: '抱歉,发生了错误', + goToDownloads: '前往下载页', + goToKnowledge: '前往知识页', + goToReleases: '前往发布页', + howToUpdateDocker: '如何更新 Docker 容器?', + interface: '界面', + internalServerError: '发生了内部错误,请稍后重试', + isCurrentVersionLatest: '您已经是最新版本了', + isLatestVersion: '有新版本可用', + knowledge: '知识', + knowledgeSaved: '知识已保存', + language: '语言', + lastUsedModels: '最近使用的模型', + light: '浅色', + // lowVram: '低 VRAM', 个人倾向于技术名词不做翻译,下方注释同理 + mainGpu: '主要 GPU', + messageOfTheDay: '今日消息', + messages: '消息', + // minP: 'Min P', + // mirostat: 'Mirostat', + // mirostatEta: 'Mirostat ETA', + // mirostatTau: 'Mirostat Tau', + modelOptions: '模型设置', + modelWasDownloaded: '{model:string} 已下载', + motd: '今日消息', + name: '名称', + newKnowledge: '新建知识', + newSession: '新建会话', + noKnowledgeChosen: '新建知识或从列表中选择', + noRecentModels: '无近期使用的模型', + noSessionChosen: '新建会话或从列表中选择已存在的会话', + notFound: '您正在查看的页面不存在', + // numBatch: 'Num batch', + // numCtx: 'Context size', + // numGpu: 'Num GPU', + // numKeep: 'Num keep', + // numPredict: 'Num predict', + // numThread: 'Number of threads', + // numa: 'NUMA', + ollamaLibrary: "Ollama 库", + otherModels: '其他模型', + // penalizeNewline: 'Penalize newline', + // presencePenalty: 'Presence penalty', + prompt: '提示词', + promptPlaceholder: '请输入任何内容', + pullModel: '下拉模型', + pullModelPlaceholder: '模型标签(例如 llama3.1)', + pullingModel: '正在下拉模型', + random: '随机', + refreshToUpdate: '刷新以更新', + releaseHistory: '发布历史', + repeatLastN: 'Repeat last N', + repeatPenalty: 'Repeat penalty', + retry: '重试', + run: '运行', + runtimeOptions: '运行时设置', + save: '保存', + search: '搜索', + searchEmpty: '无结果', + seed: '种子', + seeDocs: '查看文档', + server: '服务器', + session: '会话', + sessions: '会话', + settings: '设置', + somethingWentWrong: '抱歉,发生了错误', + // stop: 'Stop sequence', + stopResponse: '停止回复', + success: '成功', + system: '系统', + systemPrompt: '系统提示词', + // temperature: 'Temperature', + // tfsZ: 'TFS Z', + // topK: 'Top K', + // topP: 'Top P', + tryingToConnectNotLocalhost: + '如果您想连接到 {hostname:string} 或 {ip:string} 上不可用的Ollama服务器,请尝试', + // typicalP: 'Typical P', + // useMlock: 'Use MLOCK', + // useMmap: 'Use MMAP', + version: '版本', + // vocabOnly: 'Vocab only', + writePromptToStart: '写一段提示词以开始新会话', + you: '你' +} satisfies BaseTranslation; + +export default zh_cn; diff --git a/src/routes/+layout.svelte b/src/routes/+layout.svelte index 382b2d18..fbdc60a9 100644 --- a/src/routes/+layout.svelte +++ b/src/routes/+layout.svelte @@ -31,7 +31,7 @@ if (!$settingsStore.userLanguage) $settingsStore.userLanguage = detectLocale( 'en', - ['en', 'es', 'ja', 'tr', 'pt-br'], + ['en', 'es', 'ja', 'tr', 'pt-br', 'zh-cn'], navigatorDetector ) as Locales; diff --git a/src/routes/motd/motd.md b/src/routes/motd/motd.md index ed5de509..19d041b7 100644 --- a/src/routes/motd/motd.md +++ b/src/routes/motd/motd.md @@ -1,4 +1,4 @@ -`2024-10-1` +`2024-10-22` ### Message of the day @@ -8,6 +8,7 @@ - **Desktop app** is now available for [Windows, macOS & Linux](https://github.com/fmaclen/hollama/releases) - **Olá mundo!** UI is now available in [Portuguese (Brasil)](/settings) +- **你好,世界!** 界面现已支持 [中文(简体)](/settings) #### Previously, in Hollama diff --git a/src/routes/settings/Interface.svelte b/src/routes/settings/Interface.svelte index 2dbe5938..d04cd653 100644 --- a/src/routes/settings/Interface.svelte +++ b/src/routes/settings/Interface.svelte @@ -33,6 +33,7 @@ { value: 'ja', label: '日本語' }, { value: 'tr', label: 'Türkçe' }, { value: 'pt-br', label: 'Português (Brasil)' } + { value: 'zh-cn', label: '中文 (简体)' } ]} /> diff --git a/tests/settings.test.ts b/tests/settings.test.ts index c9425e06..991aab2a 100644 --- a/tests/settings.test.ts +++ b/tests/settings.test.ts @@ -248,4 +248,20 @@ test.describe('Locales', () => { ); }); }); + + test.describe('Simplified Chinese', () => { + test.use({ locale: 'zh-CN' }); + test('default language is simplified chinese', async ({ page }) => { + await page.goto('/settings'); + expect(await page.evaluate(() => navigator.language)).toBe('zh-CN'); + + await page.evaluate(() => window.localStorage.clear()); + await page.reload(); + await expect(page.getByText('Server')).not.toBeVisible(); + await expect(page.getByText('服务器')).toBeVisible(); + expect(await page.evaluate(() => window.localStorage.getItem('hollama-settings'))).toContain( + '"userLanguage":"zh-cn"' + ); + }); + }); });