feat: add i18n sync script (#1538)
* feat: add i18n sync script * chore
This commit is contained in:
parent
e99f253d48
commit
8739c49634
@ -44,7 +44,8 @@
|
||||
"generate:agents": "yarn workspace @cherry-studio/database agents",
|
||||
"generate:icons": "electron-icon-builder --input=./build/logo.png --output=build",
|
||||
"analyze:renderer": "VISUALIZER_RENDERER=true yarn build",
|
||||
"analyze:main": "VISUALIZER_MAIN=true yarn build"
|
||||
"analyze:main": "VISUALIZER_MAIN=true yarn build",
|
||||
"check": "node scripts/check-i18n.js"
|
||||
},
|
||||
"dependencies": {
|
||||
"@electron-toolkit/preload": "^3.0.0",
|
||||
|
||||
104
scripts/check-i18n.js
Normal file
104
scripts/check-i18n.js
Normal file
@ -0,0 +1,104 @@
|
||||
'use strict'
|
||||
Object.defineProperty(exports, '__esModule', { value: true })
|
||||
var fs = require('fs')
|
||||
var path = require('path')
|
||||
var translationsDir = path.join(__dirname, '../src/renderer/src/i18n/locales')
|
||||
var baseLocale = 'zh-CN'
|
||||
var baseFileName = ''.concat(baseLocale, '.json')
|
||||
var baseFilePath = path.join(translationsDir, baseFileName)
|
||||
/**
|
||||
* 递归同步 target 对象,使其与 template 对象保持一致
|
||||
* 1. 如果 template 中存在 target 中缺少的 key,则添加('[to be translated]')
|
||||
* 2. 如果 target 中存在 template 中不存在的 key,则删除
|
||||
* 3. 对于子对象,递归同步
|
||||
*
|
||||
* @param target 目标对象(需要更新的语言对象)
|
||||
* @param template 主模板对象(中文)
|
||||
* @returns 返回是否对 target 进行了更新
|
||||
*/
|
||||
function syncRecursively(target, template) {
|
||||
var isUpdated = false
|
||||
// 添加 template 中存在但 target 中缺少的 key
|
||||
for (var key in template) {
|
||||
if (!(key in target)) {
|
||||
target[key] =
|
||||
typeof template[key] === 'object' && template[key] !== null ? {} : '[to be translated]:'.concat(template[key])
|
||||
console.log('\u6DFB\u52A0\u65B0\u5C5E\u6027\uFF1A'.concat(key))
|
||||
isUpdated = true
|
||||
}
|
||||
if (typeof template[key] === 'object' && template[key] !== null) {
|
||||
if (typeof target[key] !== 'object' || target[key] === null) {
|
||||
target[key] = {}
|
||||
isUpdated = true
|
||||
}
|
||||
// 递归同步子对象
|
||||
var childUpdated = syncRecursively(target[key], template[key])
|
||||
if (childUpdated) {
|
||||
isUpdated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
// 删除 target 中存在但 template 中没有的 key
|
||||
for (var targetKey in target) {
|
||||
if (!(targetKey in template)) {
|
||||
console.log('\u79FB\u9664\u591A\u4F59\u5C5E\u6027\uFF1A'.concat(targetKey))
|
||||
delete target[targetKey]
|
||||
isUpdated = true
|
||||
}
|
||||
}
|
||||
return isUpdated
|
||||
}
|
||||
function syncTranslations() {
|
||||
if (!fs.existsSync(baseFilePath)) {
|
||||
console.error(
|
||||
'\u4E3B\u6A21\u677F\u6587\u4EF6 '.concat(
|
||||
baseFileName,
|
||||
' \u4E0D\u5B58\u5728\uFF0C\u8BF7\u68C0\u67E5\u8DEF\u5F84\u6216\u6587\u4EF6\u540D\u3002'
|
||||
)
|
||||
)
|
||||
return
|
||||
}
|
||||
var baseContent = fs.readFileSync(baseFilePath, 'utf-8')
|
||||
var baseJson = {}
|
||||
try {
|
||||
baseJson = JSON.parse(baseContent)
|
||||
} catch (error) {
|
||||
console.error('\u89E3\u6790 '.concat(baseFileName, ' \u51FA\u9519:'), error)
|
||||
return
|
||||
}
|
||||
var files = fs.readdirSync(translationsDir).filter(function (file) {
|
||||
return file.endsWith('.json') && file !== baseFileName
|
||||
})
|
||||
for (var _i = 0, files_1 = files; _i < files_1.length; _i++) {
|
||||
var file = files_1[_i]
|
||||
var filePath = path.join(translationsDir, file)
|
||||
var targetJson = {}
|
||||
try {
|
||||
var fileContent = fs.readFileSync(filePath, 'utf-8')
|
||||
targetJson = JSON.parse(fileContent)
|
||||
} catch (error) {
|
||||
console.error(
|
||||
'\u89E3\u6790 '.concat(
|
||||
file,
|
||||
' \u51FA\u9519\uFF0C\u8DF3\u8FC7\u6B64\u6587\u4EF6\u3002\u9519\u8BEF\u4FE1\u606F:'
|
||||
),
|
||||
error
|
||||
)
|
||||
continue
|
||||
}
|
||||
var isUpdated = syncRecursively(targetJson, baseJson)
|
||||
if (isUpdated) {
|
||||
try {
|
||||
fs.writeFileSync(filePath, JSON.stringify(targetJson, null, 2), 'utf-8')
|
||||
console.log(
|
||||
'\u6587\u4EF6 '.concat(file, ' \u5DF2\u66F4\u65B0\u540C\u6B65\u4E3B\u6A21\u677F\u7684\u5185\u5BB9\u3002')
|
||||
)
|
||||
} catch (error) {
|
||||
console.error('\u5199\u5165 '.concat(file, ' \u51FA\u9519:'), error)
|
||||
}
|
||||
} else {
|
||||
console.log('\u6587\u4EF6 '.concat(file, ' \u65E0\u9700\u66F4\u65B0\u3002'))
|
||||
}
|
||||
}
|
||||
}
|
||||
syncTranslations()
|
||||
98
scripts/check-i18n.ts
Normal file
98
scripts/check-i18n.ts
Normal file
@ -0,0 +1,98 @@
|
||||
import * as fs from 'fs'
|
||||
import * as path from 'path'
|
||||
|
||||
const translationsDir = path.join(__dirname, '../src/renderer/src/i18n/locales')
|
||||
const baseLocale = 'zh-CN'
|
||||
const baseFileName = `${baseLocale}.json`
|
||||
const baseFilePath = path.join(translationsDir, baseFileName)
|
||||
|
||||
/**
|
||||
* 递归同步 target 对象,使其与 template 对象保持一致
|
||||
* 1. 如果 template 中存在 target 中缺少的 key,则添加('[to be translated]')
|
||||
* 2. 如果 target 中存在 template 中不存在的 key,则删除
|
||||
* 3. 对于子对象,递归同步
|
||||
*
|
||||
* @param target 目标对象(需要更新的语言对象)
|
||||
* @param template 主模板对象(中文)
|
||||
* @returns 返回是否对 target 进行了更新
|
||||
*/
|
||||
function syncRecursively(target: any, template: any): boolean {
|
||||
let isUpdated = false
|
||||
|
||||
// 添加 template 中存在但 target 中缺少的 key
|
||||
for (const key in template) {
|
||||
if (!(key in target)) {
|
||||
target[key] =
|
||||
typeof template[key] === 'object' && template[key] !== null ? {} : `[to be translated]:${template[key]}`
|
||||
console.log(`添加新属性:${key}`)
|
||||
isUpdated = true
|
||||
}
|
||||
if (typeof template[key] === 'object' && template[key] !== null) {
|
||||
if (typeof target[key] !== 'object' || target[key] === null) {
|
||||
target[key] = {}
|
||||
isUpdated = true
|
||||
}
|
||||
// 递归同步子对象
|
||||
const childUpdated = syncRecursively(target[key], template[key])
|
||||
if (childUpdated) {
|
||||
isUpdated = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 删除 target 中存在但 template 中没有的 key
|
||||
for (const targetKey in target) {
|
||||
if (!(targetKey in template)) {
|
||||
console.log(`移除多余属性:${targetKey}`)
|
||||
delete target[targetKey]
|
||||
isUpdated = true
|
||||
}
|
||||
}
|
||||
|
||||
return isUpdated
|
||||
}
|
||||
|
||||
function syncTranslations() {
|
||||
if (!fs.existsSync(baseFilePath)) {
|
||||
console.error(`主模板文件 ${baseFileName} 不存在,请检查路径或文件名。`)
|
||||
return
|
||||
}
|
||||
|
||||
const baseContent = fs.readFileSync(baseFilePath, 'utf-8')
|
||||
let baseJson: Record<string, any> = {}
|
||||
try {
|
||||
baseJson = JSON.parse(baseContent)
|
||||
} catch (error) {
|
||||
console.error(`解析 ${baseFileName} 出错:`, error)
|
||||
return
|
||||
}
|
||||
|
||||
const files = fs.readdirSync(translationsDir).filter((file) => file.endsWith('.json') && file !== baseFileName)
|
||||
|
||||
for (const file of files) {
|
||||
const filePath = path.join(translationsDir, file)
|
||||
let targetJson: Record<string, any> = {}
|
||||
try {
|
||||
const fileContent = fs.readFileSync(filePath, 'utf-8')
|
||||
targetJson = JSON.parse(fileContent)
|
||||
} catch (error) {
|
||||
console.error(`解析 ${file} 出错,跳过此文件。错误信息:`, error)
|
||||
continue
|
||||
}
|
||||
|
||||
const isUpdated = syncRecursively(targetJson, baseJson)
|
||||
|
||||
if (isUpdated) {
|
||||
try {
|
||||
fs.writeFileSync(filePath, JSON.stringify(targetJson, null, 2), 'utf-8')
|
||||
console.log(`文件 ${file} 已更新同步主模板的内容。`)
|
||||
} catch (error) {
|
||||
console.error(`写入 ${file} 出错:`, error)
|
||||
}
|
||||
} else {
|
||||
console.log(`文件 ${file} 无需更新。`)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
syncTranslations()
|
||||
@ -171,7 +171,8 @@
|
||||
"select": "Select",
|
||||
"topics": "Topics",
|
||||
"warning": "Warning",
|
||||
"you": "You"
|
||||
"you": "You",
|
||||
"footnote": "Reference content"
|
||||
},
|
||||
"error": {
|
||||
"backup.file_format": "Backup file format error",
|
||||
@ -337,8 +338,6 @@
|
||||
"error.enter.model": "Please select a model first",
|
||||
"error.enter.name": "Please enter the name of the knowledge base",
|
||||
"error.get_embedding_dimensions": "Failed to get embedding dimensions",
|
||||
"error.invalid.enter.api.host": "Please enter a valid API host",
|
||||
"error.invalid.enter.api.key": "Please enter a valid API key",
|
||||
"error.invalid.enter.model": "Please select a model",
|
||||
"error.invalid.proxy.url": "Invalid proxy URL",
|
||||
"error.invalid.webdav": "Invalid WebDAV settings",
|
||||
@ -369,7 +368,9 @@
|
||||
"upgrade.success.button": "Restart",
|
||||
"upgrade.success.content": "Please restart the application to complete the upgrade",
|
||||
"upgrade.success.title": "Upgrade successfully",
|
||||
"warn.notion.exporting": "Notion is importing, please do not import repeatedly"
|
||||
"warn.notion.exporting": "Notion is importing, please do not import repeatedly",
|
||||
"error.invalid.api.host": "Invalid API Host",
|
||||
"error.invalid.api.key": "Invalid API Key"
|
||||
},
|
||||
"minapp": {
|
||||
"sidebar.add.title": "Add to sidebar",
|
||||
@ -498,7 +499,6 @@
|
||||
"zhipu": "ZHIPU AI"
|
||||
},
|
||||
"settings": {
|
||||
"": "MinApp that have been added to the sidebar do not support hiding. If you want to hide them, please remove them from the sidebar first.",
|
||||
"about": "About & Feedback",
|
||||
"about.checkingUpdate": "Checking for updates...",
|
||||
"about.checkUpdate": "Check Update",
|
||||
@ -545,10 +545,8 @@
|
||||
"webdav.backup.button": "Backup to WebDAV",
|
||||
"webdav.host": "WebDAV Host",
|
||||
"webdav.host.placeholder": "http://localhost:8080",
|
||||
"webdav.hour": "Hour",
|
||||
"webdav.hours": "Hours",
|
||||
"webdav.lastSync": "Last Backup",
|
||||
"webdav.minute": "Minute",
|
||||
"webdav.minutes": "Minutes",
|
||||
"webdav.noSync": "Waiting for next backup",
|
||||
"webdav.password": "WebDAV Password",
|
||||
@ -662,17 +660,6 @@
|
||||
"search_placeholder": "Search model id or name",
|
||||
"title": "Model Provider"
|
||||
},
|
||||
"provider.api.url.preview": "Preview: {{url}}",
|
||||
"provider.api.url.reset": "Reset",
|
||||
"provider.api.url.tip": "Ending with / ignores v1, ending with # forces use of input address",
|
||||
"provider.api_host": "API Host",
|
||||
"provider.api_key": "API Key",
|
||||
"provider.api_key.tip": "Multiple keys separated by commas",
|
||||
"provider.api_version": "API Version",
|
||||
"provider.check": "Check",
|
||||
"provider.docs_check": "Check",
|
||||
"provider.docs_more_details": "for more details",
|
||||
"provider.search_placeholder": "Search model id or name",
|
||||
"proxy": {
|
||||
"mode": {
|
||||
"custom": "Custom Proxy",
|
||||
|
||||
@ -47,7 +47,12 @@
|
||||
"settings.model": "モデル設定",
|
||||
"settings.preset_messages": "プリセットメッセージ",
|
||||
"settings.prompt": "プロンプト設定",
|
||||
"title": "アシスタント"
|
||||
"title": "アシスタント",
|
||||
"settings.reasoning_effort": "思考連鎖の長さ",
|
||||
"settings.reasoning_effort.high": "長い",
|
||||
"settings.reasoning_effort.low": "短い",
|
||||
"settings.reasoning_effort.medium": "中程度",
|
||||
"settings.reasoning_effort.tip": "この設定は推論モデルのみサポートしています"
|
||||
},
|
||||
"auth": {
|
||||
"error": "APIキーの自動取得に失敗しました。手動で取得してください",
|
||||
@ -166,7 +171,8 @@
|
||||
"select": "選択",
|
||||
"topics": "トピック",
|
||||
"warning": "警告",
|
||||
"you": "あなた"
|
||||
"you": "あなた",
|
||||
"footnote": "引用内容"
|
||||
},
|
||||
"error": {
|
||||
"backup.file_format": "バックアップファイルの形式エラー",
|
||||
@ -331,8 +337,6 @@
|
||||
"error.enter.api.key": "APIキーを入力してください",
|
||||
"error.enter.model": "モデルを選択してください",
|
||||
"error.get_embedding_dimensions": "埋込み次元を取得できませんでした",
|
||||
"error.invalid.enter.api.host": "APIホストを入力してください",
|
||||
"error.invalid.enter.api.key": "APIキーを入力してください",
|
||||
"error.invalid.enter.model": "モデルを選択してください",
|
||||
"error.invalid.proxy.url": "無効なプロキシURL",
|
||||
"error.invalid.webdav": "無効なWebDAV設定",
|
||||
@ -363,7 +367,10 @@
|
||||
"upgrade.success.button": "再起動",
|
||||
"upgrade.success.content": "アップグレードを完了するためにアプリケーションを再起動してください",
|
||||
"upgrade.success.title": "アップグレードに成功しました",
|
||||
"warn.notion.exporting": "Notion 正在インポート中です。重複インポートしないでください。"
|
||||
"warn.notion.exporting": "Notion 正在インポート中です。重複インポートしないでください。",
|
||||
"error.enter.name": "ナレッジベース名を入力してください",
|
||||
"error.invalid.api.host": "無効なAPIアドレスです",
|
||||
"error.invalid.api.key": "無効なAPIキーです"
|
||||
},
|
||||
"minapp": {
|
||||
"sidebar.add.title": "サイドバーに追加",
|
||||
@ -488,7 +495,8 @@
|
||||
"together": "Together",
|
||||
"yi": "零一万物",
|
||||
"zhinao": "360智脳",
|
||||
"zhipu": "智譜AI"
|
||||
"zhipu": "智譜AI",
|
||||
"ppio": "PPIO パイオウクラウド"
|
||||
},
|
||||
"settings": {
|
||||
"about": "について",
|
||||
|
||||
@ -47,7 +47,12 @@
|
||||
"settings.model": "Настройки модели",
|
||||
"settings.preset_messages": "Предустановленные сообщения",
|
||||
"settings.prompt": "Настройки промптов",
|
||||
"title": "Ассистенты"
|
||||
"title": "Ассистенты",
|
||||
"settings.reasoning_effort": "Длина цепочки рассуждений",
|
||||
"settings.reasoning_effort.high": "Длинная",
|
||||
"settings.reasoning_effort.low": "Короткая",
|
||||
"settings.reasoning_effort.medium": "Средняя",
|
||||
"settings.reasoning_effort.tip": "Эта настройка поддерживается только моделями с рассуждением"
|
||||
},
|
||||
"auth": {
|
||||
"error": "Автоматический получение ключа API не удалось, пожалуйста, получите ключ вручную",
|
||||
@ -166,7 +171,8 @@
|
||||
"select": "Выбрать",
|
||||
"topics": "Топики",
|
||||
"warning": "Предупреждение",
|
||||
"you": "Вы"
|
||||
"you": "Вы",
|
||||
"footnote": "Цитируемый контент"
|
||||
},
|
||||
"error": {
|
||||
"backup.file_format": "Ошибка формата файла резервной копии",
|
||||
@ -332,8 +338,6 @@
|
||||
"error.enter.model": "Пожалуйста, выберите модель",
|
||||
"error.enter.name": "Пожалуйста, введите название базы знаний",
|
||||
"error.get_embedding_dimensions": "Не удалось получить размерность встраивания",
|
||||
"error.invalid.enter.api.host": "Пожалуйста, введите правильный API хост",
|
||||
"error.invalid.enter.api.key": "Пожалуйста, введите правильный API ключ",
|
||||
"error.invalid.enter.model": "Пожалуйста, выберите модель",
|
||||
"error.invalid.proxy.url": "Неверный URL прокси",
|
||||
"error.invalid.webdav": "Неверные настройки WebDAV",
|
||||
@ -364,7 +368,9 @@
|
||||
"upgrade.success.button": "Перезапустить",
|
||||
"upgrade.success.content": "Пожалуйста, перезапустите приложение для завершения обновления",
|
||||
"upgrade.success.title": "Обновление успешно",
|
||||
"warn.notion.exporting": "Идет импорт в Notion, пожалуйста, не повторяйте импорт"
|
||||
"warn.notion.exporting": "Идет импорт в Notion, пожалуйста, не повторяйте импорт",
|
||||
"error.invalid.api.host": "Неверный API адрес",
|
||||
"error.invalid.api.key": "Неверный API ключ"
|
||||
},
|
||||
"minapp": {
|
||||
"sidebar.add.title": "Добавить в боковую панель",
|
||||
@ -489,7 +495,8 @@
|
||||
"together": "Together",
|
||||
"yi": "Yi",
|
||||
"zhinao": "360AI",
|
||||
"zhipu": "ZHIPU AI"
|
||||
"zhipu": "ZHIPU AI",
|
||||
"ppio": "PPIO"
|
||||
},
|
||||
"settings": {
|
||||
"about": "О программе и обратная связь",
|
||||
@ -590,7 +597,6 @@
|
||||
"input.target_language.chinese-traditional": "Китайский традиционный",
|
||||
"input.target_language.english": "Английский",
|
||||
"input.target_language.japanese": "Японский",
|
||||
"input.target_language.russianinput.translate": "Русский",
|
||||
"messages.divider": "Показывать разделитель между сообщениями",
|
||||
"messages.input.paste_long_text_as_file": "Вставлять длинный текст как файл",
|
||||
"messages.input.paste_long_text_threshold": "Длина вставки длинного текста",
|
||||
@ -653,17 +659,6 @@
|
||||
"search_placeholder": "Поиск по ID или имени модели",
|
||||
"title": "Провайдеры моделей"
|
||||
},
|
||||
"provider.api.url.preview": "Предпросмотр: {{url}}",
|
||||
"provider.api.url.reset": "Сброс",
|
||||
"provider.api.url.tip": "Заканчивая на / игнорирует v1, заканчивая на # принудительно использует введенный адрес",
|
||||
"provider.api_host": "Хост API",
|
||||
"provider.api_key": "Ключ API",
|
||||
"provider.api_key.tip": "Несколько ключей, разделенных запятыми",
|
||||
"provider.api_version": "Версия API",
|
||||
"provider.check": "Проверить",
|
||||
"provider.docs_check": "Проверить",
|
||||
"provider.docs_more_details": "для получения дополнительной информации",
|
||||
"provider.search_placeholder": "Поиск по ID или имени модели",
|
||||
"proxy": {
|
||||
"mode": {
|
||||
"custom": "Пользовательский прокси",
|
||||
@ -715,7 +710,8 @@
|
||||
"topic.position.left": "Слева",
|
||||
"topic.position.right": "Справа",
|
||||
"topic.show.time": "Показывать время топика",
|
||||
"tray.title": "Включить значок системного трея"
|
||||
"tray.title": "Включить значок системного трея",
|
||||
"input.target_language.russian": "Русский"
|
||||
},
|
||||
"translate": {
|
||||
"any.language": "Любой язык",
|
||||
|
||||
@ -171,7 +171,8 @@
|
||||
"select": "選擇",
|
||||
"topics": "話題",
|
||||
"warning": "警告",
|
||||
"you": "您"
|
||||
"you": "您",
|
||||
"footnote": "引用內容"
|
||||
},
|
||||
"error": {
|
||||
"backup.file_format": "備份文件格式錯誤",
|
||||
@ -337,8 +338,6 @@
|
||||
"error.enter.model": "請先選擇一個模型",
|
||||
"error.enter.name": "請先輸入知識庫名稱",
|
||||
"error.get_embedding_dimensions": "獲取嵌入維度失敗",
|
||||
"error.invalid.enter.api.host": "請輸入有效的 API 主機地址",
|
||||
"error.invalid.enter.api.key": "請輸入有效的 API 密鑰",
|
||||
"error.invalid.enter.model": "請選擇一個模型",
|
||||
"error.invalid.proxy.url": "無效的代理 URL",
|
||||
"error.invalid.webdav": "無效的 WebDAV 設定",
|
||||
@ -369,7 +368,9 @@
|
||||
"upgrade.success.button": "重新啟動",
|
||||
"upgrade.success.content": "請重新啟動應用以完成升級",
|
||||
"upgrade.success.title": "升級成功",
|
||||
"warn.notion.exporting": "Notion 正在匯入,請勿重複匯入"
|
||||
"warn.notion.exporting": "Notion 正在匯入,請勿重複匯入",
|
||||
"error.invalid.api.host": "無效的 API 位址",
|
||||
"error.invalid.api.key": "無效的 API 密鑰"
|
||||
},
|
||||
"minapp": {
|
||||
"sidebar.add.title": "添加到側邊欄",
|
||||
@ -532,8 +533,6 @@
|
||||
"success": "緩存清除成功",
|
||||
"title": "清除緩存"
|
||||
},
|
||||
"data.app_data": "應用數據",
|
||||
"data.app_logs": "應用日誌",
|
||||
"data.title": "數據目錄",
|
||||
"notion.api_key": "Notion 金鑰",
|
||||
"notion.database_id": "Notion 資料庫 ID",
|
||||
@ -557,7 +556,9 @@
|
||||
"webdav.syncError": "備份錯誤",
|
||||
"webdav.syncStatus": "備份狀態",
|
||||
"webdav.title": "WebDAV",
|
||||
"webdav.user": "WebDAV 使用者名稱"
|
||||
"webdav.user": "WebDAV 使用者名稱",
|
||||
"app_data": "應用數據",
|
||||
"app_logs": "應用日誌"
|
||||
},
|
||||
"display.custom.css": "自定義 CSS",
|
||||
"display.custom.css.placeholder": "/* 這裡寫自定義 CSS */",
|
||||
@ -604,7 +605,6 @@
|
||||
"messages.input.show_estimated_tokens": "顯示預估 Token 數",
|
||||
"messages.input.title": "輸入設定",
|
||||
"messages.math_engine": "Markdown 渲染輸入訊息",
|
||||
"messages.math_render_engine": "數學公式引擎",
|
||||
"messages.metrics": "首字時延 {{time_first_token_millsec}}ms | 每秒 {{token_speed}} tokens",
|
||||
"messages.model.title": "模型設定",
|
||||
"messages.title": "訊息設定",
|
||||
@ -710,7 +710,8 @@
|
||||
"topic.position.left": "左側",
|
||||
"topic.position.right": "右側",
|
||||
"topic.show.time": "顯示話題時間",
|
||||
"tray.title": "啟用系統托盤圖標"
|
||||
"tray.title": "啟用系統托盤圖標",
|
||||
"messages.markdown_rendering_input_message": "Markdown 渲染輸入訊息"
|
||||
},
|
||||
"translate": {
|
||||
"any.language": "任意語言",
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user