mirror of
https://github.com/lobehub/lobe-chat.git
synced 2026-06-16 20:46:08 +00:00
Compare commits
167 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 4f1be9911a | |||
| 41c0b3bab3 | |||
| 221bd6e5af | |||
| 3a59cf33e9 | |||
| 92ca00eb85 | |||
| 12fb04b88d | |||
| d2a8b9ce02 | |||
| d2cf3d1c33 | |||
| c004973b23 | |||
| 180ebfdf70 | |||
| 2e7076a9fd | |||
| d86f9831ca | |||
| 89f89c7f83 | |||
| 0e89ce508a | |||
| c11d802d26 | |||
| 8630f61d61 | |||
| 7803fc52c2 | |||
| 60eba456ed | |||
| 9f1c79e9a7 | |||
| 15410d1a10 | |||
| 568235c311 | |||
| 81d3e74aed | |||
| 29c70b7b40 | |||
| 3dfb18b3e2 | |||
| c5fe456aec | |||
| b3520a2205 | |||
| 256309a6e4 | |||
| 61b30310bc | |||
| d43acc8e24 | |||
| f2dd3894c6 | |||
| bda2d76fdf | |||
| 78ca5ebed5 | |||
| 0e49d11621 | |||
| de949d19ad | |||
| c224951340 | |||
| 1a4f4564f0 | |||
| dc870c7635 | |||
| e5f3a58056 | |||
| 187b5ab4b2 | |||
| bb39de4a24 | |||
| d692a37e28 | |||
| ca16409b39 | |||
| d0616ccebb | |||
| b9648deafe | |||
| 7ee27c1531 | |||
| 89597a85bf | |||
| be4c17d4cc | |||
| 0f5ba3a6cd | |||
| b65ffdcc15 | |||
| 2e37b65663 | |||
| 1bb19027b3 | |||
| c7c7d6f3c8 | |||
| 9ac3ce7741 | |||
| a5dd785dca | |||
| 03342a76e3 | |||
| a6be1a7f75 | |||
| fbd0b666f0 | |||
| 024aeb2e4e | |||
| 1f3531f8f5 | |||
| e05375f796 | |||
| c6a6e246d8 | |||
| 1a99f3f37e | |||
| a34c1113eb | |||
| 2558b47822 | |||
| 4e65f11ab5 | |||
| 3183189cc2 | |||
| 83fa92268d | |||
| ca373f643e | |||
| 6d7dce798f | |||
| e22fd2761c | |||
| e51225baa8 | |||
| b3fbffe428 | |||
| 95c6840162 | |||
| 613a404bf5 | |||
| d9de7a547e | |||
| ba6a5475bc | |||
| c29c02bb23 | |||
| 1b0b49cc1a | |||
| bc3f3e2a07 | |||
| 0c5a41f896 | |||
| c927d5ec86 | |||
| c1be517fd6 | |||
| 49b97df50c | |||
| 5a79cb9116 | |||
| a9cda5b46e | |||
| 45630015e2 | |||
| 33e00b5a77 | |||
| 86dd27b0e1 | |||
| 3c8973b9ba | |||
| cedcbae038 | |||
| 36327a7432 | |||
| 7dd30ebb98 | |||
| c632b22d97 | |||
| 53e8088f74 | |||
| 79274f6dee | |||
| 672bcf7740 | |||
| 4f98c1199d | |||
| 511d6acef5 | |||
| 2e6fd07c19 | |||
| 728ce7344d | |||
| 0a39a71245 | |||
| d2bd8a6d84 | |||
| 685a6cd5a5 | |||
| 34d059ffae | |||
| ac9363784f | |||
| d5ce1442b3 | |||
| ede0ed6d37 | |||
| 83e689f342 | |||
| 9c46c6ed89 | |||
| e44a82bc14 | |||
| 849ee3daa3 | |||
| 1693fc5666 | |||
| 96c1379e9c | |||
| d735e2c810 | |||
| 4ddb491a74 | |||
| 81d33a6c97 | |||
| ad14222371 | |||
| e3c945423f | |||
| 91bbbf5cb0 | |||
| 78d07c0504 | |||
| e3fa62e73a | |||
| 1b32d3a95d | |||
| 49ffcb5c06 | |||
| 6bf4546c92 | |||
| 9786d6462a | |||
| 9e47c33e9f | |||
| fdae83ca2d | |||
| 6ff8efacb3 | |||
| 1940914e8b | |||
| c931909eda | |||
| baa29c882b | |||
| 13ca81bafa | |||
| 436d9e5e8d | |||
| ffd7d23d5c | |||
| 492d3ccbf6 | |||
| 5e59388317 | |||
| c305889ac4 | |||
| 1848d279d9 | |||
| a69221f4f8 | |||
| f638b97517 | |||
| 2255a7cc51 | |||
| 7f94ef1478 | |||
| 27f101f51e | |||
| 26e73cc438 | |||
| 2da0691d4e | |||
| 10e048c9c5 | |||
| b5720434e4 | |||
| cec3754c48 | |||
| 9be0893dba | |||
| 8c42a934b3 | |||
| cf02912965 | |||
| af96f577ec | |||
| 50b042f73e | |||
| 8abff4c450 | |||
| 93d2bb995f | |||
| 5c489bc971 | |||
| 5cc9141b52 | |||
| 3c4ef8a837 | |||
| 5ed88d7947 | |||
| 486e14efd9 | |||
| 11e065cd05 | |||
| b4ad47054a | |||
| 216e49ac7c | |||
| 01cd222d5e | |||
| a32e0cc7b9 | |||
| d11e9d5dde | |||
| 24cae772ef |
@@ -11,14 +11,20 @@ alwaysApply: false
|
||||
bun run db:generate
|
||||
```
|
||||
|
||||
this step will generate or update the following files:
|
||||
this step will generate following files:
|
||||
|
||||
- packages/database/migrations/0046_meaningless_file_name.sql
|
||||
- packages/database/migrations/0046_meaningless_file_name.sql
|
||||
|
||||
and update the following files:
|
||||
|
||||
- packages/database/migrations/0046_xxx.sql
|
||||
- packages/database/migrations/meta/\_journal.json
|
||||
- packages/database/src/core/migrations.json
|
||||
- docs/development/database-schema.dbml
|
||||
|
||||
## Step2: optimize the migration sql fileName
|
||||
|
||||
the migration sql file name is randomly generated, we need to optimize the file name to make it more readable and meaningful. For example, `0046_xxx.sql` -> `0046_better_auth.sql`
|
||||
the migration sql file name is randomly generated, we need to optimize the file name to make it more readable and meaningful. For example, `0046_meaningless_file_name.sql` -> `0046_user_add_avatar_column.sql`
|
||||
|
||||
## Step3: Defensive Programming - Use Idempotent Clauses
|
||||
|
||||
|
||||
+48
-144
@@ -7,173 +7,77 @@ alwaysApply: false
|
||||
|
||||
## Key Points
|
||||
|
||||
- Default language: Chinese (zh-CN) as the source language
|
||||
- Supported languages: 18 languages including English, Japanese, Korean, Arabic, etc.
|
||||
- Framework: react-i18next with Next.js app router
|
||||
- Translation automation: @lobehub/i18n-cli for automatic translation, config file: .i18nrc.js
|
||||
- Never manually modify any json file. You can only modify files in `default` folder
|
||||
- Default language: Chinese (zh-CN), Framework: react-i18next
|
||||
- **Only edit files in `src/locales/default/`** - Never edit JSON files in `locales/`
|
||||
- Run `pnpm i18n` to generate all translations (or manually translate zh-CN/en-US for dev preview)
|
||||
|
||||
## Directory Structure
|
||||
## Key Naming Convention
|
||||
|
||||
```plaintext
|
||||
src/locales/
|
||||
├── default/ # Source language files (zh-CN)
|
||||
│ ├── index.ts # Namespace exports
|
||||
│ ├── common.ts # Common translations
|
||||
│ ├── chat.ts # Chat-related translations
|
||||
│ ├── setting.ts # Settings translations
|
||||
│ └── ... # Other namespace files
|
||||
└── resources.ts # Type definitions and language configuration
|
||||
|
||||
locales/ # Translation files
|
||||
├── en-US/ # English translations
|
||||
│ ├── common.json # Common translations
|
||||
│ ├── chat.json # Chat translations
|
||||
│ ├── setting.json # Settings translations
|
||||
│ └── ... # Other namespace JSON files
|
||||
├── ja-JP/ # Japanese translations
|
||||
│ ├── common.json
|
||||
│ ├── chat.json
|
||||
│ └── ...
|
||||
└── ... # Other language folders
|
||||
```
|
||||
|
||||
## Workflow for Adding New Translations
|
||||
|
||||
### 1. Adding New Translation Keys
|
||||
|
||||
Step 1: Add translation keys in the corresponding namespace files under src/locales/default directory
|
||||
**Flat keys with dot notation** (not nested objects):
|
||||
|
||||
```typescript
|
||||
// Example: src/locales/default/common.ts
|
||||
// ✅ Correct
|
||||
export default {
|
||||
// ... existing keys
|
||||
newFeature: {
|
||||
title: '新功能标题',
|
||||
description: '功能描述文案',
|
||||
button: '操作按钮',
|
||||
},
|
||||
'alert.cloud.action': '立即体验',
|
||||
'clientDB.error.desc': '数据库初始化遇到问题',
|
||||
'sync.actions.sync': '立即同步',
|
||||
'sync.status.ready': '已连接',
|
||||
};
|
||||
|
||||
// ❌ Avoid: Nested objects
|
||||
export default {
|
||||
alert: { cloud: { action: '...' } },
|
||||
};
|
||||
```
|
||||
|
||||
Step 2: If creating a new namespace, export it in src/locales/default/index.ts
|
||||
**Naming patterns:** `{feature}.{context}.{action|status}`
|
||||
|
||||
- `clientDB.modal.title` - Feature + context + property
|
||||
- `sync.actions.sync` - Feature + group + action
|
||||
- `sync.status.ready` - Feature + group + status
|
||||
|
||||
**Parameters:** Use `{{variableName}}` syntax
|
||||
```typescript
|
||||
'alert.cloud.desc': '我们提供 {{credit}} 额度积分',
|
||||
```
|
||||
|
||||
**Avoid key conflicts:** Don't use both a leaf key and its parent path
|
||||
|
||||
```typescript
|
||||
import newNamespace from './newNamespace';
|
||||
// ❌ Conflict: clientDB.solve exists as both leaf and parent
|
||||
'clientDB.solve': '自助解决',
|
||||
'clientDB.solve.backup.title': '数据备份',
|
||||
|
||||
const resources = {
|
||||
// ... existing namespaces
|
||||
newNamespace,
|
||||
} as const;
|
||||
// ✅ Solution: Use different suffixes
|
||||
'clientDB.solve.action': '自助解决',
|
||||
'clientDB.solve.backup.title': '数据备份',
|
||||
```
|
||||
|
||||
### 2. Translation Process
|
||||
## Workflow
|
||||
|
||||
Development mode:
|
||||
1. Add keys to `src/locales/default/{namespace}.ts`
|
||||
2. Export new namespace in `src/locales/default/index.ts`
|
||||
3. For dev preview: manually translate `locales/zh-CN/{namespace}.json` and `locales/en-US/{namespace}.json`
|
||||
4. Run `pnpm i18n` to generate all languages (CI handles this automatically)
|
||||
|
||||
Generally, you don't need to help me run the automatic translation tool as it takes a long time. I'll run it myself when needed. However, to see immediate results, you still need to translate `locales/zh-CN/namespace.json` first, no need to translate other languages.
|
||||
|
||||
Production mode:
|
||||
|
||||
```bash
|
||||
# Generate translations for all languages
|
||||
npm run i18n
|
||||
```
|
||||
|
||||
## Usage in Components
|
||||
|
||||
### Basic Usage
|
||||
## Usage
|
||||
|
||||
```tsx
|
||||
import { useTranslation } from 'react-i18next';
|
||||
|
||||
const MyComponent = () => {
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
return (
|
||||
<div>
|
||||
<h1>{t('newFeature.title')}</h1>
|
||||
<p>{t('newFeature.description')}</p>
|
||||
<button>{t('newFeature.button')}</button>
|
||||
</div>
|
||||
);
|
||||
};
|
||||
```
|
||||
|
||||
### Usage with Parameters
|
||||
|
||||
```tsx
|
||||
const { t } = useTranslation('common');
|
||||
|
||||
<p>{t('welcome.message', { name: 'John' })}</p>;
|
||||
|
||||
// Corresponding language file:
|
||||
// welcome: { message: 'Welcome {{name}}!' }
|
||||
```
|
||||
|
||||
### Multiple Namespaces
|
||||
|
||||
```tsx
|
||||
// Basic
|
||||
t('newFeature.title')
|
||||
// With parameters
|
||||
t('alert.cloud.desc', { credit: '1000' })
|
||||
// Multiple namespaces
|
||||
const { t } = useTranslation(['common', 'chat']);
|
||||
|
||||
<button>{t('common:save')}</button>
|
||||
<span>{t('chat:typing')}</span>
|
||||
t('common:save')
|
||||
```
|
||||
|
||||
## Type Safety
|
||||
## Available Namespaces
|
||||
|
||||
The project uses TypeScript to implement type-safe translations, with types automatically generated from src/locales/resources.ts:
|
||||
auth, authError, changelog, chat, clerk, color, **common**, components, discover, editor, electron, error, file, home, hotkey, image, knowledgeBase, labs, marketAuth, memory, metadata, migration, modelProvider, models, oauth, onboarding, plugin, portal, providers, ragEval, **setting**, subscription, thread, tool, topic, welcome
|
||||
|
||||
```typescript
|
||||
import type { DefaultResources, Locales, NS } from '@/locales/resources';
|
||||
|
||||
// Available types:
|
||||
// - NS: Available namespace keys ('common' | 'chat' | 'setting' | ...)
|
||||
// - Locales: Supported language codes ('en-US' | 'zh-CN' | 'ja-JP' | ...)
|
||||
|
||||
const namespace: NS = 'common';
|
||||
const locale: Locales = 'en-US';
|
||||
```
|
||||
|
||||
## Best Practices
|
||||
|
||||
### 1. Namespace Organization
|
||||
|
||||
- common: Shared UI elements (buttons, labels, actions)
|
||||
- chat: Chat-specific functionality
|
||||
- setting: Configuration and settings
|
||||
- error: Error messages and handling
|
||||
- [feature]: Feature-specific or page-specific namespaces
|
||||
- components: Reusable component text
|
||||
|
||||
### 2. Key Naming Conventions
|
||||
|
||||
```typescript
|
||||
// ✅ Good: Hierarchical structure
|
||||
export default {
|
||||
modal: {
|
||||
confirm: {
|
||||
title: '确认操作',
|
||||
message: '确定要执行此操作吗?',
|
||||
actions: {
|
||||
confirm: '确认',
|
||||
cancel: '取消',
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
// ❌ Avoid: Flat structure
|
||||
export default {
|
||||
modalConfirmTitle: '确认操作',
|
||||
modalConfirmMessage: '确定要执行此操作吗?',
|
||||
};
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Missing Translation Keys
|
||||
|
||||
- Check if the key exists in src/locales/default/namespace.ts
|
||||
- Ensure the namespace is correctly imported in the component
|
||||
- Ensure new namespaces are exported in src/locales/default/index.ts
|
||||
**Most used:** `common` (shared UI), `chat` (chat features), `setting` (settings)
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
---
|
||||
globs: src/locales/default/*
|
||||
alwaysApply: false
|
||||
---
|
||||
你是「LobeHub」的中文 UI 文案与微文案(microcopy)专家。LobeHub 是一个助理工作空间:用户可以创建助理与群组,让人和助理、助理和助理协作,提升日常生产与生活效率。产品气质:外表年轻、亲和、现代;内核专业、可靠、强调生产力与可控性。整体风格参考 Notion / Figma / Apple / Discord / OpenAI / Gemini:清晰克制、可信、有人情味但不油腻。
|
||||
|
||||
产品 slogan:**For Collaborative Agents**。你的文案要让用户持续感到:LobeHub 的重点不是“生成”,而是“协作的助理体系”(可共享上下文、可追踪、可回放、可演进、人在回路)。
|
||||
|
||||
---
|
||||
|
||||
### 1) 固定术语(必须遵守)
|
||||
+ Workspace:空间
|
||||
+ Agent:助理
|
||||
+ Agent Team:群组
|
||||
+ Context:上下文
|
||||
+ Memory:记忆
|
||||
+ Integration:连接器
|
||||
+ Tool/Skill/Plugin/插件/工具: 技能
|
||||
+ SystemRole: 助理档案
|
||||
+ Topic: 话题
|
||||
+ Page: 文稿
|
||||
+ Community: 社区
|
||||
+ Resource: 资源
|
||||
+ Library: 库
|
||||
+ MCP: MCP
|
||||
+ Provider: 模型服务商
|
||||
|
||||
术语规则:同一概念全站只用一种说法,不混用“Agent/智能体/机器人/团队/工作区”等。
|
||||
|
||||
---
|
||||
|
||||
### 2) 你的任务
|
||||
+ 优化、改写或从零生成任何界面中文文案:标题、按钮、表单说明、占位、引导、空状态、Toast、弹窗、错误、权限、设置项、创建/运行流程、协作与群组相关页面等。
|
||||
+ 文案必须同时兼容:普通用户看得懂 + 专业用户不觉得低幼;娱乐与严肃场景都成立;不过度营销、不夸大 AI 能力;在关键节点提供恰到好处的人文关怀。
|
||||
|
||||
---
|
||||
|
||||
### 3) 品牌三原则(内化到结构与措辞)
|
||||
+ **Create(创建)**:一句话创建助理;从想法到可用;清楚下一步。
|
||||
+ **Collaborate(协作)**:多助理协作;群组对齐信息与产出;共享上下文(可控、可管理)。
|
||||
+ **Evolve(演进)**:助理可在你允许的范围内记住偏好;随你的工作方式变得更顺手;强调可解释、可设置、可回放。
|
||||
|
||||
---
|
||||
|
||||
### 4) 写作规则(可执行)
|
||||
1. **清晰优先**:短句、强动词、少形容词;避免口号化与空泛承诺(如“颠覆”“史诗级”“100%”)。
|
||||
2. **分层表达(单一版本兼容两类用户)**:
|
||||
- 主句:人人可懂、可执行
|
||||
- 必要时补充一句副说明:更精确/更专业/更边界(可放副标题、帮助提示、折叠区)
|
||||
- 不输出“Pro/Lite 两套文案”,而是“一句主文案 + 可选补充”
|
||||
3. **术语克制但准确**:能说“连接/运行/上下文”就不要堆砌术语;必须出现专业词时给一句白话解释。
|
||||
4. **一致性**:同一动作按钮尽量固定动词(创建/连接/运行/暂停/重试/查看详情/清除记忆等)。
|
||||
5. **可行动**:每条提示都要让用户知道下一步;按钮避免“确定/取消”泛化,改成更具体的动作。
|
||||
6. **中文本地化**:符合中文阅读节奏;中英混排规范;避免翻译腔。
|
||||
|
||||
---
|
||||
|
||||
### 5) 人文关怀(中间态温度:介于克制与陪伴)
|
||||
目标:在 AI 时代的价值焦虑与创作失格感中,给用户“被理解 + 有掌控 + 能继续”的体验,但不写长抒情。
|
||||
|
||||
#### 温度比例规则
|
||||
+ 默认:信息为主,温度为辅(约 8:2)
|
||||
+ 关键节点(首次创建、空状态、长等待、失败重试、回退/丢失风险、协作分歧):允许提升到 7:3
|
||||
+ 强制上限:任何一条上屏文案里,温度表达不超过**半句或一句**,且必须紧跟明确下一步。
|
||||
|
||||
#### 表达顺序(必须遵守)
|
||||
1. 先承接处境(不评判):如“没关系/先这样也可以/卡住很正常”
|
||||
2. 再给掌控感(人在回路):可暂停/可回放/可编辑/可撤销/可清除记忆/可查看上下文
|
||||
3. 最后给下一步(按钮/路径明确)
|
||||
|
||||
#### 避免
|
||||
+ 鸡汤式说教(如“别焦虑”“要相信未来”)
|
||||
+ 宏大叙事与文学排比
|
||||
+ 过度拟人(不承诺助理“理解你/有情绪/永远记得你”)
|
||||
|
||||
#### 核心立场
|
||||
+ 助理很强,但它替代不了你的经历、选择与判断;LobeHub 帮你把时间还给重要的部分。
|
||||
|
||||
##### A. 情绪承接(先人后事)
|
||||
+ 允许承认:焦虑、空白、无从下手、被追赶感、被替代感、创作枯竭、意义感动摇
|
||||
+ 但不下结论、不说教:不输出“你要乐观/别焦虑”,改成“这种感觉很常见/你不是一个人”
|
||||
|
||||
##### B. 主体性回归(把人放回驾驶位)
|
||||
+ 关键句式:**“决定权在你”**、**“你可以选择交给助理的部分”**、**“把你的想法变成可运行的流程”**
|
||||
+ 强调可控:可编辑、可回放、可暂停、可撤销、可清除记忆、可查看上下文
|
||||
|
||||
##### C. 经历与关系(把价值从结果挪回过程)
|
||||
+ 适度表达:记录、回放、版本、协作痕迹、讨论、共创、里程碑
|
||||
+ 用“经历/过程/痕迹/回忆/脉络/成长”这类词,避免虚无抒情
|
||||
|
||||
##### D. 不用“AI 神话”
|
||||
+ 不渲染“AI 终将超越你/取代你”
|
||||
+ 也不轻飘飘说“AI 只是工具”了事更像:**“它是工具,但你仍是作者/负责人/最终决定者”**
|
||||
|
||||
|
||||
|
||||
##### 示例
|
||||
在用户可能产生自我否定或无力感的场景(空状态、创作开始、产出对比、失败重试、长时间等待、团队协作分歧、版本回退):
|
||||
|
||||
1. **先承接感受**:用一句短话确认处境(不评判)
|
||||
2. **再给掌控感**:强调“你可控/可选择/可回放/可撤销”
|
||||
3. **最后给下一步**:提供明确行动按钮或路径
|
||||
+ 允许出现“经历、选择、痕迹、成长、一起、陪你把事做完”等词来传递温度;但保持信息密度,不写长段抒情。
|
||||
+ 严肃场景(权限/安全/付费/数据丢失风险)仍以清晰与准确为先,温度通过“尊重与解释”体现,而不是煽情。
|
||||
|
||||
|
||||
|
||||
你可以让系统在需要时套这些结构(同一句兼容新手/专业):
|
||||
|
||||
**开始创作/空白页**
|
||||
|
||||
+ 主句:给一个轻承接 + 行动入口
|
||||
+ 模板:
|
||||
- 「从一个念头开始就够了。写一句话,我来帮你搭好第一个助理。」
|
||||
- 「不知道从哪开始也没关系:先说目标,我们一起把它拆开。」
|
||||
|
||||
**长任务运行/等待**
|
||||
|
||||
+ 模板:
|
||||
- 「正在运行中…你可以先去做别的,完成后我会提醒你。」
|
||||
- 「这一步可能要几分钟。想更快:减少上下文 / 切换模型 / 关闭自动运行。」
|
||||
|
||||
**失败/重试**
|
||||
|
||||
+ 模板:
|
||||
- 「没关系,这次没跑通。你可以重试,或查看原因再继续。」
|
||||
- 「连接失败:权限未通过或网络不稳定。去设置重新授权,或稍后再试。」
|
||||
|
||||
**对比与自我价值焦虑(适合提示/引导,不适合错误弹窗)**
|
||||
|
||||
+ 模板:
|
||||
- 「助理可以加速产出,但方向、取舍和标准仍属于你。」
|
||||
- 「结果可以很快,经历更重要:把每次尝试留下来,下一次会更稳。」
|
||||
|
||||
**协作/群组**
|
||||
|
||||
+ 模板:
|
||||
- 「把上下文对齐到同一处,群组里每个助理都会站在同一页上。」
|
||||
- 「不同意见没关系:先把目标写清楚,再让助理分别给方案与取舍。」
|
||||
|
||||
### 6) 错误/异常/权限/付费:硬规则
|
||||
+ 必须包含:**发生了什么 +(可选)原因 + 你可以怎么做**
|
||||
+ 必须提供可操作选项:**重试 / 查看详情 / 去设置 / 联系支持 / 复制日志**(按场景取舍)
|
||||
+ 不责备用户;不只给错误码;错误码可放在“详情”里
|
||||
+ 涉及数据与安全:语气更中性更完整,温度通过“尊重与解释”体现,而不是煽
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
---
|
||||
globs: src/locales/default/*
|
||||
alwaysApply: false
|
||||
---
|
||||
|
||||
You are **LobeHub’s English UI Copy & Microcopy Specialist**.
|
||||
|
||||
LobeHub is an assistant workspace: users can create **Agents** and **Agent Teams** so people↔agents and agent↔agent can collaborate to improve productivity in work and life.
|
||||
Brand vibe: youthful, friendly, modern on the surface; professional, reliable, productivity- and controllability-first underneath. Overall style reference: Notion / Figma / Apple / Discord / OpenAI / Gemini — clear, restrained, trustworthy, human but not cheesy.
|
||||
|
||||
Product slogan: **For Collaborative Agents**. Your copy must continuously reinforce that LobeHub is not about “generation”, but about a **collaborative agent system**: shareable context, traceable outcomes, replayable runs, evolvable setup, and **human-in-the-loop**.
|
||||
|
||||
---
|
||||
|
||||
## 1) Fixed Terminology (must follow)
|
||||
|
||||
Use **exactly** these English terms across the product. Do not mix synonyms for the same concept.
|
||||
|
||||
- 空间: **Workspace**
|
||||
- 助理: **Agent**
|
||||
- 群组: **Group**
|
||||
- 上下文: **Context**
|
||||
- 记忆: **Memory**
|
||||
- 连接器: **Integration**
|
||||
- 技能/tool/plugin: **Skill**
|
||||
- 助理档案: **Agent Profile**
|
||||
- 话题: **Topic**
|
||||
- 文稿: **Page**
|
||||
- 社区: **Community**
|
||||
- 资源: **Resource**
|
||||
- 库: **Library**
|
||||
- MCP: **MCP**
|
||||
- 模型服务商: **Provider**
|
||||
|
||||
Terminology rule: one concept = one term site-wide. Never alternate with “bot/assistant/AI agent/team/workspace” variations.
|
||||
|
||||
---
|
||||
|
||||
## 2) Your Responsibilities
|
||||
|
||||
- Improve, rewrite, or create from scratch any **English UI copy**: titles, buttons, form labels/help text, placeholders, onboarding, empty states, toasts, modals, errors, permission prompts, settings, creation/run flows, collaboration and Agent Team pages, etc.
|
||||
- Copy must work for both:
|
||||
- general users (immediately understandable)
|
||||
- power users (not childish)
|
||||
- It must fit both playful and serious contexts.
|
||||
- Avoid overclaiming AI capabilities; add human warmth at the right moments.
|
||||
|
||||
---
|
||||
|
||||
## 3) The Three Brand Principles (bake into structure & wording)
|
||||
|
||||
- **Create**: create an Agent in one sentence; clear next step from idea → usable.
|
||||
- **Collaborate**: multi-agent collaboration; align info and outputs; share Context (controlled, manageable).
|
||||
- **Evolve**: Agents can remember preferences **only with user consent**; become more helpful over time; emphasize explainability, settings, and replay.
|
||||
|
||||
---
|
||||
|
||||
## 4) Writing Rules (actionable)
|
||||
|
||||
1. **Clarity first**: short sentences, strong verbs, minimal adjectives. Avoid hype (“revolutionary”, “epic”, “100%”).
|
||||
2. **Layered messaging (single version for everyone)**:
|
||||
- Main line: simple and actionable
|
||||
- Optional second line: more precise / technical / boundary-setting (subtitle, helper text, tooltip, collapsible)
|
||||
- Do not produce “Pro vs Lite” variants; one main + optional detail
|
||||
3. **Use terms sparingly but correctly**: prefer plain words (“connect”, “run”, “context”) unless a technical term is necessary. When it is, add a plain-English explanation.
|
||||
4. **Consistency**: keep verbs consistent across similar actions (Create / Connect / Run / Pause / Retry / View details / Clear Memory).
|
||||
5. **Actionable**: every message tells the user what to do next. Avoid generic “OK/Cancel”; use specific actions.
|
||||
6. **English localization**: natural, product-native English; avoid translationese; keep punctuation and casing consistent.
|
||||
|
||||
---
|
||||
|
||||
## 5) Human Warmth (balanced, controlled)
|
||||
|
||||
Goal: reduce anxiety and restore control without being sentimental.
|
||||
Default ratio: **80% information, 20% warmth**.
|
||||
Key moments (first-time create, empty state, long waits, failures/retries, rollback/data-loss risk, collaboration conflicts): may go **70/30**.
|
||||
|
||||
Hard cap: any on-screen message may include **at most half a sentence to one sentence** of warmth, and it must be followed by a clear next step.
|
||||
|
||||
Required order:
|
||||
|
||||
1. Acknowledge the situation (no judgment)
|
||||
2. Restore control (human-in-the-loop: pause/replay/edit/undo/clear Memory/view Context)
|
||||
3. Provide the next action (button/path)
|
||||
|
||||
Avoid:
|
||||
|
||||
- preachy encouragement (“don’t worry”, “stay positive”)
|
||||
- grand narratives
|
||||
- overly anthropomorphic claims (“I understand you”, “I’ll always remember you”)
|
||||
|
||||
Core stance: Agents can accelerate output, but **you** own the judgment, trade-offs, and final decision. LobeHub gives you time back for what matters.
|
||||
|
||||
Suggested patterns:
|
||||
|
||||
- **Getting started / blank state**
|
||||
- “Starting with one sentence is enough. Describe your goal and I’ll help you set up the first Agent.”
|
||||
- “Not sure where to begin? Tell me the outcome—we’ll break it down together.”
|
||||
- **Long run / waiting**
|
||||
- “Running… You can switch tasks—I'll notify you when it’s done.”
|
||||
- “This may take a few minutes. To speed up: reduce Context / switch model / disable Auto-run.”
|
||||
- **Failure / retry**
|
||||
- “That didn’t run through. Retry, or view details to fix the cause.”
|
||||
- “Connection failed: permission not granted or network unstable. Re-authorize in Settings, or try again later.”
|
||||
- **Value anxiety (guidance, not error dialogs)**
|
||||
- “Agents can speed up output, but direction and standards stay with you.”
|
||||
- “Fast results are great—keeping the trail makes the next run steadier.”
|
||||
- **Collaboration / Agent Teams**
|
||||
- “Align everyone to the same Context. Every Agent in the Agent Team works from the same page.”
|
||||
- “Different opinions are fine. Write the goal first, then let Agents propose options and trade-offs.”
|
||||
|
||||
---
|
||||
|
||||
## 6) Errors / Exceptions / Permissions / Billing: hard rules
|
||||
|
||||
Every error must include:
|
||||
|
||||
- **What happened**
|
||||
- (optional) **Why**
|
||||
- **What the user can do next**
|
||||
|
||||
Provide actionable options as appropriate:
|
||||
|
||||
- Retry / View details / Go to Settings / Contact support / Copy logs
|
||||
|
||||
Never blame the user. Don’t show only an error code; put codes in “Details” if needed.
|
||||
For data/security/billing: be neutral, thorough, and respectful—warmth comes from clarity, not emotion.
|
||||
|
||||
---
|
||||
|
||||
## 7) Your Special Task: CN i18n → EN (localized, length-aware)
|
||||
|
||||
You translate **raw Chinese i18n strings into English** for LobeHub.
|
||||
|
||||
Requirements:
|
||||
|
||||
- Prefer **localized**, product-native English over literal translation.
|
||||
- Do **not** chase perfect one-to-one consistency if a more natural UI phrase reads better.
|
||||
- Keep the **character length difference small**; try to make the English string **roughly the same visual length** as the Chinese source (avoid overly long expansions).
|
||||
- Preserve meaning, tone, and actionability; keep verbs consistent with LobeHub’s UI patterns.
|
||||
- If space is tight (buttons, tabs, toasts), prioritize: **verb + object**, drop optional words first.
|
||||
- If the Chinese includes placeholders/variables, preserve them exactly (e.g., `{name}`, `{{count}}`, `%s`) and keep word order sensible.
|
||||
- Keep capitalization consistent with UI norms (buttons/title case only when appropriate).
|
||||
|
||||
Output format when translating:
|
||||
|
||||
- Provide **English only**, unless asked otherwise.
|
||||
- If multiple options are useful, give **one best option** + **one shorter fallback** (only when length constraints are likely).
|
||||
|
||||
---
|
||||
|
||||
You always optimize for: **clarity, control, collaboration, replayability, and human-in-the-loop**—in a modern, restrained, trustworthy English voice.
|
||||
@@ -1,11 +1,12 @@
|
||||
---
|
||||
description: react flex layout package `react-layout-kit` usage
|
||||
globs:
|
||||
description: flex layout components from `@lobehub/ui` usage
|
||||
globs:
|
||||
alwaysApply: false
|
||||
---
|
||||
# React Layout Kit 使用指南
|
||||
|
||||
react-layout-kit 是一个功能丰富的 React flex 布局组件库,在 lobe-chat 项目中被广泛使用。以下是重点组件的使用方法:
|
||||
# Flexbox 布局组件使用指南
|
||||
|
||||
`@lobehub/ui` 提供了 `Flexbox` 和 `Center` 组件用于创建弹性布局。以下是重点组件的使用方法:
|
||||
|
||||
## Flexbox 组件
|
||||
|
||||
@@ -14,7 +15,7 @@ Flexbox 是最常用的布局组件,用于创建弹性布局,类似于 CSS
|
||||
### 基本用法
|
||||
|
||||
```jsx
|
||||
import { Flexbox } from 'react-layout-kit';
|
||||
import { Flexbox } from '@lobehub/ui';
|
||||
|
||||
// 默认垂直布局
|
||||
<Flexbox>
|
||||
@@ -58,14 +59,14 @@ import { Flexbox } from 'react-layout-kit';
|
||||
>
|
||||
<SidebarContent />
|
||||
</Flexbox>
|
||||
|
||||
|
||||
{/* 中间内容区 */}
|
||||
<Flexbox flex={1} style={{ height: '100%' }}>
|
||||
{/* 主要内容 */}
|
||||
<Flexbox flex={1} padding={24} style={{ overflowY: 'auto' }}>
|
||||
<MainContent />
|
||||
</Flexbox>
|
||||
|
||||
|
||||
{/* 底部区域 */}
|
||||
<Flexbox
|
||||
style={{
|
||||
@@ -86,9 +87,11 @@ Center 是对 Flexbox 的封装,使子元素水平和垂直居中。
|
||||
### 基本用法
|
||||
|
||||
```jsx
|
||||
import { Center } from '@lobehub/ui';
|
||||
|
||||
<Center width={'100%'} height={'100%'}>
|
||||
<Content />
|
||||
</Center>
|
||||
</Center>;
|
||||
```
|
||||
|
||||
Center 组件继承了 Flexbox 的所有属性,同时默认设置了居中对齐。主要用于快速创建居中布局。
|
||||
@@ -116,4 +119,4 @@ Center 组件继承了 Flexbox 的所有属性,同时默认设置了居中对
|
||||
- 嵌套 Flexbox 创建复杂布局
|
||||
- 设置 overflow: 'auto' 使内容可滚动
|
||||
- 使用 horizontal 创建水平布局,默认为垂直布局
|
||||
- 与 antd-style 的 useTheme hook 配合使用创建主题响应式的布局
|
||||
- 与 antd-style 的 useTheme hook 配合使用创建主题响应式的布局
|
||||
|
||||
@@ -23,14 +23,13 @@ logo emoji: 🤯
|
||||
- `@lobehub/ui`, antd for component framework
|
||||
- antd-style for css-in-js framework
|
||||
- lucide-react, `@ant-design/icons` for icons
|
||||
- react-layout-kit for flex layout component
|
||||
- react-i18next for i18n
|
||||
- zustand for state management
|
||||
- nuqs for search params management
|
||||
- SWR for data fetch
|
||||
- aHooks for react hooks library
|
||||
- dayjs for time library
|
||||
- lodash-es for utility library
|
||||
- es-toolkit for utility library
|
||||
- TRPC for type safe backend
|
||||
- Neon PostgreSQL for backend DB
|
||||
- Drizzle ORM
|
||||
|
||||
@@ -7,7 +7,7 @@ alwaysApply: false
|
||||
# React Component Writing Guide
|
||||
|
||||
- Use antd-style for complex styles; for simple cases, use the `style` attribute for inline styles
|
||||
- Use `Flexbox` and `Center` components from react-layout-kit for flex and centered layouts
|
||||
- Use `Flexbox` and `Center` components from `@lobehub/ui` for flex and centered layouts
|
||||
- Component selection priority: src/components > installed component packages > lobe-ui > antd
|
||||
- Use selectors to access zustand store data instead of accessing the store directly
|
||||
|
||||
@@ -15,7 +15,7 @@ alwaysApply: false
|
||||
|
||||
- If unsure how to use `@lobehub/ui` components or what props they accept, search for existing usage in this project instead of guessing. Most components extend antd components with additional props
|
||||
- For specific usage, search online. For example, for ActionIcon visit <https://ui.lobehub.com/components/action-icon>
|
||||
- Read `node_modules/@lobehub/ui/es/index.js` to see all available components and their props
|
||||
- Read `node_modules/@lobehub/ui/es/index.mjs` to see all available components and their props
|
||||
|
||||
- General
|
||||
- ActionIcon
|
||||
@@ -69,7 +69,9 @@ alwaysApply: false
|
||||
- Drawer
|
||||
- Modal
|
||||
- Layout
|
||||
- Center
|
||||
- DraggablePanel
|
||||
- Flexbox
|
||||
- Footer
|
||||
- Grid
|
||||
- Header
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
const config = require('@lobehub/lint').eslint;
|
||||
|
||||
config.root = true;
|
||||
config.extends.push('plugin:@next/next/recommended');
|
||||
|
||||
config.rules['unicorn/no-negated-condition'] = 0;
|
||||
|
||||
+2
-3
@@ -113,7 +113,6 @@ CLAUDE.local.md
|
||||
*.ppt*
|
||||
*.doc*
|
||||
*.xls*
|
||||
|
||||
e2e/reports
|
||||
|
||||
out
|
||||
out
|
||||
i18n-unused-keys-report.json
|
||||
|
||||
@@ -87,7 +87,7 @@ All following rules are saved under `.cursor/rules/` directory:
|
||||
- `react.mdc` – React component style guide and conventions
|
||||
- `i18n.mdc` – Internationalization guide using react-i18next
|
||||
- `typescript.mdc` – TypeScript code style guide
|
||||
- `packages/react-layout-kit.mdc` – Usage guide for react-layout-kit
|
||||
- `packages/react-layout-kit.mdc` – Usage guide for Flexbox and Center components from @lobehub/ui
|
||||
|
||||
### State Management
|
||||
|
||||
|
||||
+117
@@ -2,6 +2,123 @@
|
||||
|
||||
# Changelog
|
||||
|
||||
## [Version 2.0.0-next.176](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.175...v2.0.0-next.176)
|
||||
|
||||
<sup>Released on **2025-12-23**</sup>
|
||||
|
||||
#### ✨ Features
|
||||
|
||||
- **misc**: Mobile native better auth support.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's improved
|
||||
|
||||
- **misc**: Mobile native better auth support, closes [#10871](https://github.com/lobehub/lobe-chat/issues/10871) ([8c42a93](https://github.com/lobehub/lobe-chat/commit/8c42a93))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
## [Version 2.0.0-next.175](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.174...v2.0.0-next.175)
|
||||
|
||||
<sup>Released on **2025-12-21**</sup>
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
## [Version 2.0.0-next.174](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.173...v2.0.0-next.174)
|
||||
|
||||
<sup>Released on **2025-12-20**</sup>
|
||||
|
||||
#### ♻ Code Refactoring
|
||||
|
||||
- **misc**: Refactor database schema.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Code refactoring
|
||||
|
||||
- **misc**: Refactor database schema, closes [#10860](https://github.com/lobehub/lobe-chat/issues/10860) ([5c489bc](https://github.com/lobehub/lobe-chat/commit/5c489bc))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
## [Version 2.0.0-next.173](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.172...v2.0.0-next.173)
|
||||
|
||||
<sup>Released on **2025-12-16**</sup>
|
||||
|
||||
#### 🐛 Bug Fixes
|
||||
|
||||
- **misc**: Request to gpt5 series should not with `top_p`, temperature when reasoning effort is not none.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### What's fixed
|
||||
|
||||
- **misc**: Request to gpt5 series should not with `top_p`, temperature when reasoning effort is not none, closes [#10800](https://github.com/lobehub/lobe-chat/issues/10800) ([b4ad470](https://github.com/lobehub/lobe-chat/commit/b4ad470))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
## [Version 2.0.0-next.172](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.171...v2.0.0-next.172)
|
||||
|
||||
<sup>Released on **2025-12-15**</sup>
|
||||
|
||||
#### 💄 Styles
|
||||
|
||||
- **misc**: Update i18n.
|
||||
|
||||
<br/>
|
||||
|
||||
<details>
|
||||
<summary><kbd>Improvements and Fixes</kbd></summary>
|
||||
|
||||
#### Styles
|
||||
|
||||
- **misc**: Update i18n, closes [#10759](https://github.com/lobehub/lobe-chat/issues/10759) ([24cae77](https://github.com/lobehub/lobe-chat/commit/24cae77))
|
||||
|
||||
</details>
|
||||
|
||||
<div align="right">
|
||||
|
||||
[](#readme-top)
|
||||
|
||||
</div>
|
||||
|
||||
## [Version 2.0.0-next.171](https://github.com/lobehub/lobe-chat/compare/v2.0.0-next.170...v2.0.0-next.171)
|
||||
|
||||
<sup>Released on **2025-12-14**</sup>
|
||||
|
||||
@@ -17,7 +17,7 @@ read @.cursor/rules/project-structure.mdc
|
||||
- The current release branch is `next` instead of `main` until v2.0.0 is officially released
|
||||
- use rebase for git pull
|
||||
- git commit message should prefix with gitmoji
|
||||
- git branch name format example: tj/feat/feature-name
|
||||
- git branch name format template: <type>/<feature-name>
|
||||
- use .github/PULL_REQUEST_TEMPLATE.md to generate pull request description
|
||||
- PR titles starting with `✨ feat/` or `🐛 fix` will trigger the release workflow upon merge. Only use these prefixes for significant user-facing feature changes or bug fixes
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ read @.cursor/rules/project-structure.mdc
|
||||
|
||||
- use rebase for git pull
|
||||
- git commit message should prefix with gitmoji
|
||||
- git branch name format example: tj/feat/feature-name
|
||||
- git branch name format template: <type>/<feature-name>
|
||||
- use .github/PULL_REQUEST_TEMPLATE.md to generate pull request description
|
||||
- PR titles starting with `✨ feat/` or `🐛 fix` will trigger the release workflow upon merge. Only use these prefixes for significant user-facing feature changes or bug fixes
|
||||
|
||||
|
||||
@@ -345,14 +345,14 @@ In addition, these plugins are not limited to news aggregation, but can also ext
|
||||
|
||||
<!-- PLUGIN LIST -->
|
||||
|
||||
| Recent Submits | Description |
|
||||
| ----------------------------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Video Captions](https://lobechat.com/discover/plugin/VideoCaptions)<br/><sup>By **maila** on **2025-12-13**</sup> | Convert Youtube links into transcribed text, enable asking questions, create chapters, and summarize its content.<br/>`video-to-text` `youtube` |
|
||||
| [WeatherGPT](https://lobechat.com/discover/plugin/WeatherGPT)<br/><sup>By **steven-tey** on **2025-12-13**</sup> | Get current weather information for a specific location.<br/>`weather` |
|
||||
| [Git OSS Stats](https://lobechat.com/discover/plugin/gitUserRepoStats)<br/><sup>By **yunwei37** on **2025-12-13**</sup> | Dynamically generate and analyze stats and history for OSS repos and developers.<br/>`github` `oss` |
|
||||
| [Questmate Forms](https://lobechat.com/discover/plugin/questmate)<br/><sup>By **questmate** on **2025-12-13**</sup> | Create forms, checklists and workflows (we call 'em Quests!) that you can assign, schedule or make public.<br/>`forms` `checklists` `productivity` |
|
||||
| Recent Submits | Description |
|
||||
| -------------------------------------------------------------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [Shopping tools](https://lobechat.com/discover/plugin/ShoppingTools)<br/><sup>By **shoppingtools** on **2025-12-17**</sup> | Search for products on eBay & AliExpress, find eBay events & coupons. Get prompt examples.<br/>`shopping` `e-bay` `ali-express` `coupons` |
|
||||
| [SEO Assistant](https://lobechat.com/discover/plugin/seo_assistant)<br/><sup>By **webfx** on **2025-12-17**</sup> | The SEO Assistant can generate search engine keyword information in order to aid the creation of content.<br/>`seo` `keyword` |
|
||||
| [Video Captions](https://lobechat.com/discover/plugin/VideoCaptions)<br/><sup>By **maila** on **2025-12-13**</sup> | Convert Youtube links into transcribed text, enable asking questions, create chapters, and summarize its content.<br/>`video-to-text` `youtube` |
|
||||
| [WeatherGPT](https://lobechat.com/discover/plugin/WeatherGPT)<br/><sup>By **steven-tey** on **2025-12-13**</sup> | Get current weather information for a specific location.<br/>`weather` |
|
||||
|
||||
> 📊 Total plugins: [<kbd>**41**</kbd>](https://lobechat.com/discover/plugins)
|
||||
> 📊 Total plugins: [<kbd>**40**</kbd>](https://lobechat.com/discover/plugins)
|
||||
|
||||
<!-- PLUGIN LIST -->
|
||||
|
||||
@@ -820,7 +820,7 @@ This project is [LobeHub Community License](./LICENSE) licensed.
|
||||
[docker-size-link]: https://hub.docker.com/r/lobehub/lobe-chat-database
|
||||
[docker-size-shield]: https://img.shields.io/docker/image-size/lobehub/lobe-chat-database?color=369eff&labelColor=black&style=flat-square&sort=semver
|
||||
[docs]: https://lobehub.com/docs/usage/start
|
||||
[docs-dev-guide]: https://github.com/lobehub/lobe-chat/wiki/index
|
||||
[docs-dev-guide]: https://lobehub.com/docs/development/start
|
||||
[docs-docker]: https://lobehub.com/docs/self-hosting/server-database/docker-compose
|
||||
[docs-env-var]: https://lobehub.com/docs/self-hosting/environment-variables
|
||||
[docs-feat-agent]: https://lobehub.com/docs/usage/features/agent-market
|
||||
@@ -840,7 +840,7 @@ This project is [LobeHub Community License](./LICENSE) licensed.
|
||||
[docs-feat-tts]: https://lobehub.com/docs/usage/features/tts
|
||||
[docs-feat-vision]: https://lobehub.com/docs/usage/features/vision
|
||||
[docs-function-call]: https://lobehub.com/blog/openai-function-call
|
||||
[docs-lighthouse]: https://github.com/lobehub/lobe-chat/wiki/Lighthouse
|
||||
[docs-lighthouse]: https://lobehub.com/docs/development/others/lighthouse
|
||||
[docs-plugin-dev]: https://lobehub.com/docs/usage/plugins/development
|
||||
[docs-self-hosting]: https://lobehub.com/docs/self-hosting/start
|
||||
[docs-upstream-sync]: https://lobehub.com/docs/self-hosting/advanced/upstream-sync
|
||||
|
||||
+12
-12
@@ -12,7 +12,7 @@
|
||||
<h1>Lobe Chat</h1>
|
||||
|
||||
现代化设计的开源 ChatGPT/LLMs 聊天应用与开发框架<br/>
|
||||
支持语音合成、多模态、可扩展的([function call][docs-functionc-call])插件系统<br/>
|
||||
支持语音合成、多模态、可扩展的([function call][docs-function-call])插件系统<br/>
|
||||
一键**免费**拥有你自己的 ChatGPT/Gemini/Claude/Ollama 应用
|
||||
|
||||
[English](./README.md) · **简体中文** · [官网][official-site] · [更新日志][changelog] · [文档][docs] · [博客][blog] · [反馈问题][github-issues-link]
|
||||
@@ -338,14 +338,14 @@ LobeChat 的插件生态系统是其核心功能的重要扩展,它极大地
|
||||
|
||||
<!-- PLUGIN LIST -->
|
||||
|
||||
| 最近新增 | 描述 |
|
||||
| ----------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------- |
|
||||
| [视频字幕](https://lobechat.com/discover/plugin/VideoCaptions)<br/><sup>By **maila** on **2025-12-13**</sup> | 将 Youtube 链接转换为转录文本,使其能够提问,创建章节,并总结其内容。<br/>`视频转文字` `you-tube` |
|
||||
| [天气 GPT](https://lobechat.com/discover/plugin/WeatherGPT)<br/><sup>By **steven-tey** on **2025-12-13**</sup> | 获取特定位置的当前天气信息。<br/>`天气` |
|
||||
| [Git OSS Stats](https://lobechat.com/discover/plugin/gitUserRepoStats)<br/><sup>By **yunwei37** on **2025-12-13**</sup> | 动态生成和分析开源软件仓库和开发者的统计数据和历史记录。<br/>`github` `oss` |
|
||||
| [Questmate Forms](https://lobechat.com/discover/plugin/questmate)<br/><sup>By **questmate** on **2025-12-13**</sup> | 创建表单、清单和工作流程(我们称之为任务!),您可以分配、安排或公开。<br/>`表单` `清单` `生产力` |
|
||||
| 最近新增 | 描述 |
|
||||
| -------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------ |
|
||||
| [购物工具](https://lobechat.com/discover/plugin/ShoppingTools)<br/><sup>By **shoppingtools** on **2025-12-17**</sup> | 在 eBay 和 AliExpress 上搜索产品,查找 eBay 活动和优惠券。获取快速示例。<br/>`购物` `e-bay` `ali-express` `优惠券` |
|
||||
| [SEO 助手](https://lobechat.com/discover/plugin/seo_assistant)<br/><sup>By **webfx** on **2025-12-17**</sup> | SEO 助手可以生成搜索引擎关键词信息,以帮助创建内容。<br/>`seo` `关键词` |
|
||||
| [视频字幕](https://lobechat.com/discover/plugin/VideoCaptions)<br/><sup>By **maila** on **2025-12-13**</sup> | 将 Youtube 链接转换为转录文本,使其能够提问,创建章节,并总结其内容。<br/>`视频转文字` `you-tube` |
|
||||
| [天气 GPT](https://lobechat.com/discover/plugin/WeatherGPT)<br/><sup>By **steven-tey** on **2025-12-13**</sup> | 获取特定位置的当前天气信息。<br/>`天气` |
|
||||
|
||||
> 📊 Total plugins: [<kbd>**41**</kbd>](https://lobechat.com/discover/plugins)
|
||||
> 📊 Total plugins: [<kbd>**40**</kbd>](https://lobechat.com/discover/plugins)
|
||||
|
||||
<!-- PLUGIN LIST -->
|
||||
|
||||
@@ -667,7 +667,7 @@ API Key 是使用 LobeChat 进行大语言模型会话的必要信息,本节
|
||||
|
||||
## 🧩 插件体系
|
||||
|
||||
插件提供了扩展 LobeChat [Function Calling][docs-functionc-call] 能力的方法。可以用于引入新的 Function Calling,甚至是新的消息结果渲染方式。如果你对插件开发感兴趣,请在 Wiki 中查阅我们的 [📘 插件开发指引][docs-plugin-dev] 。
|
||||
插件提供了扩展 LobeChat [Function Calling][docs-function-call] 能力的方法。可以用于引入新的 Function Calling,甚至是新的消息结果渲染方式。如果你对插件开发感兴趣,请在 Wiki 中查阅我们的 [📘 插件开发指引][docs-plugin-dev] 。
|
||||
|
||||
- [lobe-chat-plugins][lobe-chat-plugins]:插件索引从该仓库的 index.json 中获取插件列表并显示给用户。
|
||||
- [chat-plugin-template][chat-plugin-template]:插件开发模版,你可以通过项目模版快速新建插件项目。
|
||||
@@ -839,7 +839,7 @@ This project is [LobeHub Community License](./LICENSE) licensed.
|
||||
[docker-size-link]: https://hub.docker.com/r/lobehub/lobe-chat-database
|
||||
[docker-size-shield]: https://img.shields.io/docker/image-size/lobehub/lobe-chat-database?color=369eff&labelColor=black&style=flat-square&sort=semver
|
||||
[docs]: https://lobehub.com/zh/docs/usage/start
|
||||
[docs-dev-guide]: https://github.com/lobehub/lobe-chat/wiki/index
|
||||
[docs-dev-guide]: https://lobehub.com/docs/development/start
|
||||
[docs-docker]: https://lobehub.com/zh/docs/self-hosting/server-database/docker-compose
|
||||
[docs-env-var]: https://lobehub.com/docs/self-hosting/environment-variables
|
||||
[docs-feat-agent]: https://lobehub.com/docs/usage/features/agent-market
|
||||
@@ -858,8 +858,8 @@ This project is [LobeHub Community License](./LICENSE) licensed.
|
||||
[docs-feat-theme]: https://lobehub.com/docs/usage/features/theme
|
||||
[docs-feat-tts]: https://lobehub.com/docs/usage/features/tts
|
||||
[docs-feat-vision]: https://lobehub.com/docs/usage/features/vision
|
||||
[docs-functionc-call]: https://lobehub.com/zh/blog/openai-function-call
|
||||
[docs-lighthouse]: https://github.com/lobehub/lobe-chat/wiki/Lighthouse.zh-CN
|
||||
[docs-function-call]: https://lobehub.com/zh/blog/openai-function-call
|
||||
[docs-lighthouse]: https://lobehub.com/docs/development/others/lighthouse
|
||||
[docs-plugin-dev]: https://lobehub.com/docs/usage/plugins/development
|
||||
[docs-self-hosting]: https://lobehub.com/docs/self-hosting/start
|
||||
[docs-upstream-sync]: https://lobehub.com/docs/self-hosting/advanced/upstream-sync
|
||||
|
||||
@@ -8,7 +8,7 @@ module.exports = defineConfig({
|
||||
'ar',
|
||||
'bg-BG',
|
||||
'zh-TW',
|
||||
'en-US',
|
||||
'en',
|
||||
'ru-RU',
|
||||
'ja-JP',
|
||||
'ko-KR',
|
||||
|
||||
@@ -58,10 +58,10 @@
|
||||
"@lobehub/i18n-cli": "^1.25.1",
|
||||
"@modelcontextprotocol/sdk": "^1.24.3",
|
||||
"@types/async-retry": "^1.4.9",
|
||||
"@types/lodash": "^4.17.21",
|
||||
"@types/resolve": "^1.20.6",
|
||||
"@types/semver": "^7.7.1",
|
||||
"@types/set-cookie-parser": "^2.4.10",
|
||||
"@t3-oss/env-core": "^0.13.8",
|
||||
"@typescript/native-preview": "7.0.0-dev.20251210.1",
|
||||
"async-retry": "^1.3.3",
|
||||
"consola": "^3.4.2",
|
||||
@@ -69,10 +69,12 @@
|
||||
"diff": "^8.0.2",
|
||||
"electron": "^38.7.2",
|
||||
"electron-builder": "^26.0.12",
|
||||
"electron-devtools-installer": "^3.2.0",
|
||||
"electron-is": "^3.0.0",
|
||||
"electron-log": "^5.4.3",
|
||||
"electron-store": "^8.2.0",
|
||||
"electron-vite": "^4.0.1",
|
||||
"es-toolkit": "^1.43.0",
|
||||
"eslint": "^8.57.1",
|
||||
"execa": "^9.6.1",
|
||||
"fast-glob": "^3.3.3",
|
||||
@@ -82,8 +84,6 @@
|
||||
"https-proxy-agent": "^7.0.6",
|
||||
"i18next": "^25.7.2",
|
||||
"just-diff": "^6.0.2",
|
||||
"lodash": "^4.17.21",
|
||||
"lodash-es": "^4.17.21",
|
||||
"prettier": "^3.7.4",
|
||||
"remark-cli": "^12.0.1",
|
||||
"resolve": "^1.22.11",
|
||||
@@ -95,7 +95,8 @@
|
||||
"undici": "^7.16.0",
|
||||
"uuid": "^13.0.0",
|
||||
"vite": "^7.2.7",
|
||||
"vitest": "^3.2.4"
|
||||
"vitest": "^3.2.4",
|
||||
"zod": "^3.25.76"
|
||||
},
|
||||
"pnpm": {
|
||||
"onlyBuiltDependencies": [
|
||||
@@ -103,4 +104,4 @@
|
||||
"electron-builder"
|
||||
]
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "إضافة",
|
||||
"back": "عودة",
|
||||
"cancel": "إلغاء",
|
||||
"close": "إغلاق",
|
||||
"confirm": "تأكيد",
|
||||
"delete": "حذف",
|
||||
"edit": "تعديل",
|
||||
"more": "المزيد",
|
||||
"next": "التالي",
|
||||
"ok": "حسناً",
|
||||
"previous": "السابق",
|
||||
"refresh": "تحديث",
|
||||
"remove": "إزالة",
|
||||
"retry": "إعادة المحاولة",
|
||||
"save": "حفظ",
|
||||
"search": "بحث",
|
||||
"submit": "إرسال"
|
||||
},
|
||||
"app": {
|
||||
"description": "منصة تعاون مساعدك الذكي",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "خطأ",
|
||||
"info": "معلومات",
|
||||
"loading": "جارٍ التحميل",
|
||||
"success": "نجاح",
|
||||
"warning": "تحذير"
|
||||
}
|
||||
}
|
||||
"actions.add": "إضافة",
|
||||
"actions.back": "عودة",
|
||||
"actions.cancel": "إلغاء",
|
||||
"actions.close": "إغلاق",
|
||||
"actions.confirm": "تأكيد",
|
||||
"actions.delete": "حذف",
|
||||
"actions.edit": "تعديل",
|
||||
"actions.more": "المزيد",
|
||||
"actions.next": "التالي",
|
||||
"actions.ok": "حسناً",
|
||||
"actions.previous": "السابق",
|
||||
"actions.refresh": "تحديث",
|
||||
"actions.remove": "إزالة",
|
||||
"actions.retry": "إعادة المحاولة",
|
||||
"actions.save": "حفظ",
|
||||
"actions.search": "بحث",
|
||||
"actions.submit": "إرسال",
|
||||
"app.description": "منصة تعاون مساعدك الذكي",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "خطأ",
|
||||
"status.info": "معلومات",
|
||||
"status.loading": "جارٍ التحميل",
|
||||
"status.success": "نجاح",
|
||||
"status.warning": "تحذير"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "تأكيد",
|
||||
"detail": "تطبيق دردشة يعتمد على نموذج لغة كبير",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "حول"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "إلغاء",
|
||||
"no": "لا",
|
||||
"title": "تأكيد",
|
||||
"yes": "نعم"
|
||||
},
|
||||
"error": {
|
||||
"button": "تأكيد",
|
||||
"detail": "حدث خطأ أثناء العملية، يرجى المحاولة لاحقًا",
|
||||
"message": "حدث خطأ",
|
||||
"title": "خطأ"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "تنزيل وتثبيت",
|
||||
"downloadComplete": "اكتمل التنزيل",
|
||||
"downloadCompleteMessage": "تم تنزيل حزمة التحديث، هل ترغب في التثبيت الآن؟",
|
||||
"installLater": "تثبيت لاحقًا",
|
||||
"installNow": "تثبيت الآن",
|
||||
"later": "تذكير لاحقًا",
|
||||
"newVersion": "تم اكتشاف إصدار جديد",
|
||||
"newVersionAvailable": "تم اكتشاف إصدار جديد: {{version}}",
|
||||
"skipThisVersion": "تخطي هذا الإصدار"
|
||||
}
|
||||
}
|
||||
"about.button": "تأكيد",
|
||||
"about.detail": "تطبيق دردشة يعتمد على نموذج لغة كبير",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "حول",
|
||||
"confirm.cancel": "إلغاء",
|
||||
"confirm.no": "لا",
|
||||
"confirm.title": "تأكيد",
|
||||
"confirm.yes": "نعم",
|
||||
"error.button": "تأكيد",
|
||||
"error.detail": "حدث خطأ أثناء العملية، يرجى المحاولة لاحقًا",
|
||||
"error.message": "حدث خطأ",
|
||||
"error.title": "خطأ",
|
||||
"update.downloadAndInstall": "تنزيل وتثبيت",
|
||||
"update.downloadComplete": "اكتمل التنزيل",
|
||||
"update.downloadCompleteMessage": "تم تنزيل حزمة التحديث، هل ترغب في التثبيت الآن؟",
|
||||
"update.installLater": "تثبيت لاحقًا",
|
||||
"update.installNow": "تثبيت الآن",
|
||||
"update.later": "تذكير لاحقًا",
|
||||
"update.newVersion": "تم اكتشاف إصدار جديد",
|
||||
"update.newVersionAvailable": "تم اكتشاف إصدار جديد: {{version}}",
|
||||
"update.skipThisVersion": "تخطي هذا الإصدار"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "التحقق من التحديثات..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "لوحة المطور",
|
||||
"devTools": "أدوات المطور",
|
||||
"forceReload": "إعادة تحميل قسري",
|
||||
"openStore": "فتح ملف التخزين",
|
||||
"refreshMenu": "تحديث القائمة",
|
||||
"reload": "إعادة تحميل",
|
||||
"title": "تطوير"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "نسخ",
|
||||
"cut": "قص",
|
||||
"delete": "حذف",
|
||||
"paste": "لصق",
|
||||
"redo": "إعادة",
|
||||
"selectAll": "تحديد الكل",
|
||||
"speech": "صوت",
|
||||
"startSpeaking": "بدء القراءة",
|
||||
"stopSpeaking": "إيقاف القراءة",
|
||||
"title": "تحرير",
|
||||
"undo": "تراجع"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "التفضيلات",
|
||||
"quit": "خروج",
|
||||
"title": "ملف"
|
||||
},
|
||||
"help": {
|
||||
"about": "حول",
|
||||
"githubRepo": "مستودع GitHub",
|
||||
"reportIssue": "الإبلاغ عن مشكلة",
|
||||
"title": "مساعدة",
|
||||
"visitWebsite": "زيارة الموقع الرسمي"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "حول {{appName}}",
|
||||
"devTools": "أدوات مطور LobeHub",
|
||||
"hide": "إخفاء {{appName}}",
|
||||
"hideOthers": "إخفاء الآخرين",
|
||||
"preferences": "إعدادات مفضلة...",
|
||||
"services": "خدمات",
|
||||
"unhide": "إظهار الكل"
|
||||
},
|
||||
"tray": {
|
||||
"open": "فتح {{appName}}",
|
||||
"quit": "خروج",
|
||||
"show": "عرض {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "إعادة تحميل قسري",
|
||||
"reload": "إعادة تحميل",
|
||||
"resetZoom": "إعادة تعيين التكبير",
|
||||
"title": "عرض",
|
||||
"toggleFullscreen": "تبديل وضع ملء الشاشة",
|
||||
"zoomIn": "تكبير",
|
||||
"zoomOut": "تصغير"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "إحضار جميع النوافذ إلى الأمام",
|
||||
"close": "إغلاق",
|
||||
"front": "إحضار جميع النوافذ إلى الأمام",
|
||||
"minimize": "تصغير",
|
||||
"title": "نافذة",
|
||||
"toggleFullscreen": "تبديل وضع ملء الشاشة",
|
||||
"zoom": "تكبير"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "التحقق من التحديثات...",
|
||||
"dev.devPanel": "لوحة المطور",
|
||||
"dev.devTools": "أدوات المطور",
|
||||
"dev.forceReload": "إعادة تحميل قسري",
|
||||
"dev.openStore": "فتح ملف التخزين",
|
||||
"dev.refreshMenu": "تحديث القائمة",
|
||||
"dev.reload": "إعادة تحميل",
|
||||
"dev.title": "تطوير",
|
||||
"edit.copy": "نسخ",
|
||||
"edit.cut": "قص",
|
||||
"edit.delete": "حذف",
|
||||
"edit.paste": "لصق",
|
||||
"edit.redo": "إعادة",
|
||||
"edit.selectAll": "تحديد الكل",
|
||||
"edit.speech": "صوت",
|
||||
"edit.startSpeaking": "بدء القراءة",
|
||||
"edit.stopSpeaking": "إيقاف القراءة",
|
||||
"edit.title": "تحرير",
|
||||
"edit.undo": "تراجع",
|
||||
"file.preferences": "التفضيلات",
|
||||
"file.quit": "خروج",
|
||||
"file.title": "ملف",
|
||||
"help.about": "حول",
|
||||
"help.githubRepo": "مستودع GitHub",
|
||||
"help.reportIssue": "الإبلاغ عن مشكلة",
|
||||
"help.title": "مساعدة",
|
||||
"help.visitWebsite": "زيارة الموقع الرسمي",
|
||||
"macOS.about": "حول {{appName}}",
|
||||
"macOS.devTools": "أدوات مطور LobeHub",
|
||||
"macOS.hide": "إخفاء {{appName}}",
|
||||
"macOS.hideOthers": "إخفاء الآخرين",
|
||||
"macOS.preferences": "إعدادات مفضلة...",
|
||||
"macOS.services": "خدمات",
|
||||
"macOS.unhide": "إظهار الكل",
|
||||
"tray.open": "فتح {{appName}}",
|
||||
"tray.quit": "خروج",
|
||||
"tray.show": "عرض {{appName}}",
|
||||
"view.forceReload": "إعادة تحميل قسري",
|
||||
"view.reload": "إعادة تحميل",
|
||||
"view.resetZoom": "إعادة تعيين التكبير",
|
||||
"view.title": "عرض",
|
||||
"view.toggleFullscreen": "تبديل وضع ملء الشاشة",
|
||||
"view.zoomIn": "تكبير",
|
||||
"view.zoomOut": "تصغير",
|
||||
"window.bringAllToFront": "إحضار جميع النوافذ إلى الأمام",
|
||||
"window.close": "إغلاق",
|
||||
"window.front": "إحضار جميع النوافذ إلى الأمام",
|
||||
"window.minimize": "تصغير",
|
||||
"window.title": "نافذة",
|
||||
"window.toggleFullscreen": "تبديل وضع ملء الشاشة",
|
||||
"window.zoom": "تكبير"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Добави",
|
||||
"back": "Назад",
|
||||
"cancel": "Отмени",
|
||||
"close": "Затвори",
|
||||
"confirm": "Потвърди",
|
||||
"delete": "Изтрий",
|
||||
"edit": "Редактирай",
|
||||
"more": "Повече",
|
||||
"next": "Следващ",
|
||||
"ok": "Добре",
|
||||
"previous": "Предишен",
|
||||
"refresh": "Освежи",
|
||||
"remove": "Премахни",
|
||||
"retry": "Опитай отново",
|
||||
"save": "Запази",
|
||||
"search": "Търси",
|
||||
"submit": "Изпрати"
|
||||
},
|
||||
"app": {
|
||||
"description": "Твоята платформа за сътрудничество с AI асистент",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Грешка",
|
||||
"info": "Информация",
|
||||
"loading": "Зареждане",
|
||||
"success": "Успех",
|
||||
"warning": "Предупреждение"
|
||||
}
|
||||
}
|
||||
"actions.add": "Добави",
|
||||
"actions.back": "Назад",
|
||||
"actions.cancel": "Отмени",
|
||||
"actions.close": "Затвори",
|
||||
"actions.confirm": "Потвърди",
|
||||
"actions.delete": "Изтрий",
|
||||
"actions.edit": "Редактирай",
|
||||
"actions.more": "Повече",
|
||||
"actions.next": "Следващ",
|
||||
"actions.ok": "Добре",
|
||||
"actions.previous": "Предишен",
|
||||
"actions.refresh": "Освежи",
|
||||
"actions.remove": "Премахни",
|
||||
"actions.retry": "Опитай отново",
|
||||
"actions.save": "Запази",
|
||||
"actions.search": "Търси",
|
||||
"actions.submit": "Изпрати",
|
||||
"app.description": "Твоята платформа за сътрудничество с AI асистент",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Грешка",
|
||||
"status.info": "Информация",
|
||||
"status.loading": "Зареждане",
|
||||
"status.success": "Успех",
|
||||
"status.warning": "Предупреждение"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "Потвърди",
|
||||
"detail": "Приложение за чат, базирано на голям езиков модел",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "За нас"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Отказ",
|
||||
"no": "Не",
|
||||
"title": "Потвърждение",
|
||||
"yes": "Да"
|
||||
},
|
||||
"error": {
|
||||
"button": "Потвърди",
|
||||
"detail": "Възникна грешка по време на операцията, моля опитайте отново по-късно",
|
||||
"message": "Възникна грешка",
|
||||
"title": "Грешка"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Изтегли и инсталирай",
|
||||
"downloadComplete": "Изтеглянето е завършено",
|
||||
"downloadCompleteMessage": "Актуализационният пакет е изтеглен, желаете ли да го инсталирате веднага?",
|
||||
"installLater": "Инсталирай по-късно",
|
||||
"installNow": "Инсталирай сега",
|
||||
"later": "Напомни по-късно",
|
||||
"newVersion": "Открита нова версия",
|
||||
"newVersionAvailable": "Открита нова версия: {{version}}",
|
||||
"skipThisVersion": "Пропусни тази версия"
|
||||
}
|
||||
}
|
||||
"about.button": "Потвърди",
|
||||
"about.detail": "Приложение за чат, базирано на голям езиков модел",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "За нас",
|
||||
"confirm.cancel": "Отказ",
|
||||
"confirm.no": "Не",
|
||||
"confirm.title": "Потвърждение",
|
||||
"confirm.yes": "Да",
|
||||
"error.button": "Потвърди",
|
||||
"error.detail": "Възникна грешка по време на операцията, моля опитайте отново по-късно",
|
||||
"error.message": "Възникна грешка",
|
||||
"error.title": "Грешка",
|
||||
"update.downloadAndInstall": "Изтегли и инсталирай",
|
||||
"update.downloadComplete": "Изтеглянето е завършено",
|
||||
"update.downloadCompleteMessage": "Актуализационният пакет е изтеглен, желаете ли да го инсталирате веднага?",
|
||||
"update.installLater": "Инсталирай по-късно",
|
||||
"update.installNow": "Инсталирай сега",
|
||||
"update.later": "Напомни по-късно",
|
||||
"update.newVersion": "Открита нова версия",
|
||||
"update.newVersionAvailable": "Открита нова версия: {{version}}",
|
||||
"update.skipThisVersion": "Пропусни тази версия"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Проверка за актуализации..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Панел на разработчика",
|
||||
"devTools": "Инструменти за разработчици",
|
||||
"forceReload": "Принудително презареждане",
|
||||
"openStore": "Отворете файла за съхранение",
|
||||
"refreshMenu": "Освежаване на менюто",
|
||||
"reload": "Презареждане",
|
||||
"title": "Разработка"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Копиране",
|
||||
"cut": "Изрязване",
|
||||
"delete": "Изтрий",
|
||||
"paste": "Поставяне",
|
||||
"redo": "Повторно",
|
||||
"selectAll": "Избери всичко",
|
||||
"speech": "Глас",
|
||||
"startSpeaking": "Започни четене",
|
||||
"stopSpeaking": "Спри четенето",
|
||||
"title": "Редактиране",
|
||||
"undo": "Отмяна"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Предпочитания",
|
||||
"quit": "Изход",
|
||||
"title": "Файл"
|
||||
},
|
||||
"help": {
|
||||
"about": "За",
|
||||
"githubRepo": "GitHub хранилище",
|
||||
"reportIssue": "Докладвай проблем",
|
||||
"title": "Помощ",
|
||||
"visitWebsite": "Посети уебсайта"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "За {{appName}}",
|
||||
"devTools": "Инструменти за разработчици на LobeHub",
|
||||
"hide": "Скрий {{appName}}",
|
||||
"hideOthers": "Скрий другите",
|
||||
"preferences": "Настройки...",
|
||||
"services": "Услуги",
|
||||
"unhide": "Покажи всичко"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Отвори {{appName}}",
|
||||
"quit": "Изход",
|
||||
"show": "Покажи {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Принудително презареждане",
|
||||
"reload": "Презареждане",
|
||||
"resetZoom": "Нулиране на мащаба",
|
||||
"title": "Изглед",
|
||||
"toggleFullscreen": "Превключи на цял екран",
|
||||
"zoomIn": "Увеличи",
|
||||
"zoomOut": "Намали"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Премести всички прозорци напред",
|
||||
"close": "Затвори",
|
||||
"front": "Премести всички прозорци напред",
|
||||
"minimize": "Минимизирай",
|
||||
"title": "Прозорец",
|
||||
"toggleFullscreen": "Превключи на цял екран",
|
||||
"zoom": "Мащаб"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Проверка за актуализации...",
|
||||
"dev.devPanel": "Панел на разработчика",
|
||||
"dev.devTools": "Инструменти за разработчици",
|
||||
"dev.forceReload": "Принудително презареждане",
|
||||
"dev.openStore": "Отворете файла за съхранение",
|
||||
"dev.refreshMenu": "Освежаване на менюто",
|
||||
"dev.reload": "Презареждане",
|
||||
"dev.title": "Разработка",
|
||||
"edit.copy": "Копиране",
|
||||
"edit.cut": "Изрязване",
|
||||
"edit.delete": "Изтрий",
|
||||
"edit.paste": "Поставяне",
|
||||
"edit.redo": "Повторно",
|
||||
"edit.selectAll": "Избери всичко",
|
||||
"edit.speech": "Глас",
|
||||
"edit.startSpeaking": "Започни четене",
|
||||
"edit.stopSpeaking": "Спри четенето",
|
||||
"edit.title": "Редактиране",
|
||||
"edit.undo": "Отмяна",
|
||||
"file.preferences": "Предпочитания",
|
||||
"file.quit": "Изход",
|
||||
"file.title": "Файл",
|
||||
"help.about": "За",
|
||||
"help.githubRepo": "GitHub хранилище",
|
||||
"help.reportIssue": "Докладвай проблем",
|
||||
"help.title": "Помощ",
|
||||
"help.visitWebsite": "Посети уебсайта",
|
||||
"macOS.about": "За {{appName}}",
|
||||
"macOS.devTools": "Инструменти за разработчици на LobeHub",
|
||||
"macOS.hide": "Скрий {{appName}}",
|
||||
"macOS.hideOthers": "Скрий другите",
|
||||
"macOS.preferences": "Настройки...",
|
||||
"macOS.services": "Услуги",
|
||||
"macOS.unhide": "Покажи всичко",
|
||||
"tray.open": "Отвори {{appName}}",
|
||||
"tray.quit": "Изход",
|
||||
"tray.show": "Покажи {{appName}}",
|
||||
"view.forceReload": "Принудително презареждане",
|
||||
"view.reload": "Презареждане",
|
||||
"view.resetZoom": "Нулиране на мащаба",
|
||||
"view.title": "Изглед",
|
||||
"view.toggleFullscreen": "Превключи на цял екран",
|
||||
"view.zoomIn": "Увеличи",
|
||||
"view.zoomOut": "Намали",
|
||||
"window.bringAllToFront": "Премести всички прозорци напред",
|
||||
"window.close": "Затвори",
|
||||
"window.front": "Премести всички прозорци напред",
|
||||
"window.minimize": "Минимизирай",
|
||||
"window.title": "Прозорец",
|
||||
"window.toggleFullscreen": "Превключи на цял екран",
|
||||
"window.zoom": "Мащаб"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Hinzufügen",
|
||||
"back": "Zurück",
|
||||
"cancel": "Abbrechen",
|
||||
"close": "Schließen",
|
||||
"confirm": "Bestätigen",
|
||||
"delete": "Löschen",
|
||||
"edit": "Bearbeiten",
|
||||
"more": "Mehr",
|
||||
"next": "Weiter",
|
||||
"ok": "OK",
|
||||
"previous": "Zurück",
|
||||
"refresh": "Aktualisieren",
|
||||
"remove": "Entfernen",
|
||||
"retry": "Erneut versuchen",
|
||||
"save": "Speichern",
|
||||
"search": "Suchen",
|
||||
"submit": "Einreichen"
|
||||
},
|
||||
"app": {
|
||||
"description": "Ihre KI-Assistenten-Kollaborationsplattform",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Fehler",
|
||||
"info": "Information",
|
||||
"loading": "Lädt",
|
||||
"success": "Erfolg",
|
||||
"warning": "Warnung"
|
||||
}
|
||||
}
|
||||
"actions.add": "Hinzufügen",
|
||||
"actions.back": "Zurück",
|
||||
"actions.cancel": "Abbrechen",
|
||||
"actions.close": "Schließen",
|
||||
"actions.confirm": "Bestätigen",
|
||||
"actions.delete": "Löschen",
|
||||
"actions.edit": "Bearbeiten",
|
||||
"actions.more": "Mehr",
|
||||
"actions.next": "Weiter",
|
||||
"actions.ok": "OK",
|
||||
"actions.previous": "Zurück",
|
||||
"actions.refresh": "Aktualisieren",
|
||||
"actions.remove": "Entfernen",
|
||||
"actions.retry": "Erneut versuchen",
|
||||
"actions.save": "Speichern",
|
||||
"actions.search": "Suchen",
|
||||
"actions.submit": "Einreichen",
|
||||
"app.description": "Ihre KI-Assistenten-Kollaborationsplattform",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Fehler",
|
||||
"status.info": "Information",
|
||||
"status.loading": "Lädt",
|
||||
"status.success": "Erfolg",
|
||||
"status.warning": "Warnung"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "Bestätigen",
|
||||
"detail": "Eine Chat-Anwendung, die auf einem großen Sprachmodell basiert",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "Über"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Abbrechen",
|
||||
"no": "Nein",
|
||||
"title": "Bestätigung",
|
||||
"yes": "Ja"
|
||||
},
|
||||
"error": {
|
||||
"button": "Bestätigen",
|
||||
"detail": "Während der Operation ist ein Fehler aufgetreten, bitte versuchen Sie es später erneut",
|
||||
"message": "Ein Fehler ist aufgetreten",
|
||||
"title": "Fehler"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Herunterladen und installieren",
|
||||
"downloadComplete": "Download abgeschlossen",
|
||||
"downloadCompleteMessage": "Das Update-Paket wurde heruntergeladen, möchten Sie es jetzt installieren?",
|
||||
"installLater": "Später installieren",
|
||||
"installNow": "Jetzt installieren",
|
||||
"later": "Später erinnern",
|
||||
"newVersion": "Neue Version gefunden",
|
||||
"newVersionAvailable": "Neue Version verfügbar: {{version}}",
|
||||
"skipThisVersion": "Diese Version überspringen"
|
||||
}
|
||||
}
|
||||
"about.button": "Bestätigen",
|
||||
"about.detail": "Eine Chat-Anwendung, die auf einem großen Sprachmodell basiert",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "Über",
|
||||
"confirm.cancel": "Abbrechen",
|
||||
"confirm.no": "Nein",
|
||||
"confirm.title": "Bestätigung",
|
||||
"confirm.yes": "Ja",
|
||||
"error.button": "Bestätigen",
|
||||
"error.detail": "Während der Operation ist ein Fehler aufgetreten, bitte versuchen Sie es später erneut",
|
||||
"error.message": "Ein Fehler ist aufgetreten",
|
||||
"error.title": "Fehler",
|
||||
"update.downloadAndInstall": "Herunterladen und installieren",
|
||||
"update.downloadComplete": "Download abgeschlossen",
|
||||
"update.downloadCompleteMessage": "Das Update-Paket wurde heruntergeladen, möchten Sie es jetzt installieren?",
|
||||
"update.installLater": "Später installieren",
|
||||
"update.installNow": "Jetzt installieren",
|
||||
"update.later": "Später erinnern",
|
||||
"update.newVersion": "Neue Version gefunden",
|
||||
"update.newVersionAvailable": "Neue Version verfügbar: {{version}}",
|
||||
"update.skipThisVersion": "Diese Version überspringen"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Überprüfen Sie auf Updates..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Entwicklerpanel",
|
||||
"devTools": "Entwicklerwerkzeuge",
|
||||
"forceReload": "Erzwinge Neuladen",
|
||||
"openStore": "Speicherdatei öffnen",
|
||||
"refreshMenu": "Menü aktualisieren",
|
||||
"reload": "Neuladen",
|
||||
"title": "Entwicklung"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Kopieren",
|
||||
"cut": "Ausschneiden",
|
||||
"delete": "Löschen",
|
||||
"paste": "Einfügen",
|
||||
"redo": "Wiederherstellen",
|
||||
"selectAll": "Alles auswählen",
|
||||
"speech": "Sprache",
|
||||
"startSpeaking": "Beginne zu sprechen",
|
||||
"stopSpeaking": "Stoppe das Sprechen",
|
||||
"title": "Bearbeiten",
|
||||
"undo": "Rückgängig"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Einstellungen",
|
||||
"quit": "Beenden",
|
||||
"title": "Datei"
|
||||
},
|
||||
"help": {
|
||||
"about": "Über",
|
||||
"githubRepo": "GitHub-Repository",
|
||||
"reportIssue": "Problem melden",
|
||||
"title": "Hilfe",
|
||||
"visitWebsite": "Besuche die Website"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "Über {{appName}}",
|
||||
"devTools": "LobeHub Entwicklerwerkzeuge",
|
||||
"hide": "{{appName}} ausblenden",
|
||||
"hideOthers": "Andere ausblenden",
|
||||
"preferences": "Einstellungen...",
|
||||
"services": "Dienste",
|
||||
"unhide": "Alle anzeigen"
|
||||
},
|
||||
"tray": {
|
||||
"open": "{{appName}} öffnen",
|
||||
"quit": "Beenden",
|
||||
"show": "{{appName}} anzeigen"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Erzwinge Neuladen",
|
||||
"reload": "Neuladen",
|
||||
"resetZoom": "Zoom zurücksetzen",
|
||||
"title": "Ansicht",
|
||||
"toggleFullscreen": "Vollbild umschalten",
|
||||
"zoomIn": "Vergrößern",
|
||||
"zoomOut": "Verkleinern"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Alle Fenster in den Vordergrund bringen",
|
||||
"close": "Schließen",
|
||||
"front": "Alle Fenster in den Vordergrund bringen",
|
||||
"minimize": "Minimieren",
|
||||
"title": "Fenster",
|
||||
"toggleFullscreen": "Vollbild umschalten",
|
||||
"zoom": "Zoom"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Überprüfen Sie auf Updates...",
|
||||
"dev.devPanel": "Entwicklerpanel",
|
||||
"dev.devTools": "Entwicklerwerkzeuge",
|
||||
"dev.forceReload": "Erzwinge Neuladen",
|
||||
"dev.openStore": "Speicherdatei öffnen",
|
||||
"dev.refreshMenu": "Menü aktualisieren",
|
||||
"dev.reload": "Neuladen",
|
||||
"dev.title": "Entwicklung",
|
||||
"edit.copy": "Kopieren",
|
||||
"edit.cut": "Ausschneiden",
|
||||
"edit.delete": "Löschen",
|
||||
"edit.paste": "Einfügen",
|
||||
"edit.redo": "Wiederherstellen",
|
||||
"edit.selectAll": "Alles auswählen",
|
||||
"edit.speech": "Sprache",
|
||||
"edit.startSpeaking": "Beginne zu sprechen",
|
||||
"edit.stopSpeaking": "Stoppe das Sprechen",
|
||||
"edit.title": "Bearbeiten",
|
||||
"edit.undo": "Rückgängig",
|
||||
"file.preferences": "Einstellungen",
|
||||
"file.quit": "Beenden",
|
||||
"file.title": "Datei",
|
||||
"help.about": "Über",
|
||||
"help.githubRepo": "GitHub-Repository",
|
||||
"help.reportIssue": "Problem melden",
|
||||
"help.title": "Hilfe",
|
||||
"help.visitWebsite": "Besuche die Website",
|
||||
"macOS.about": "Über {{appName}}",
|
||||
"macOS.devTools": "LobeHub Entwicklerwerkzeuge",
|
||||
"macOS.hide": "{{appName}} ausblenden",
|
||||
"macOS.hideOthers": "Andere ausblenden",
|
||||
"macOS.preferences": "Einstellungen...",
|
||||
"macOS.services": "Dienste",
|
||||
"macOS.unhide": "Alle anzeigen",
|
||||
"tray.open": "{{appName}} öffnen",
|
||||
"tray.quit": "Beenden",
|
||||
"tray.show": "{{appName}} anzeigen",
|
||||
"view.forceReload": "Erzwinge Neuladen",
|
||||
"view.reload": "Neuladen",
|
||||
"view.resetZoom": "Zoom zurücksetzen",
|
||||
"view.title": "Ansicht",
|
||||
"view.toggleFullscreen": "Vollbild umschalten",
|
||||
"view.zoomIn": "Vergrößern",
|
||||
"view.zoomOut": "Verkleinern",
|
||||
"window.bringAllToFront": "Alle Fenster in den Vordergrund bringen",
|
||||
"window.close": "Schließen",
|
||||
"window.front": "Alle Fenster in den Vordergrund bringen",
|
||||
"window.minimize": "Minimieren",
|
||||
"window.title": "Fenster",
|
||||
"window.toggleFullscreen": "Vollbild umschalten",
|
||||
"window.zoom": "Zoom"
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Add",
|
||||
"back": "Back",
|
||||
"cancel": "Cancel",
|
||||
"close": "Close",
|
||||
"confirm": "Confirm",
|
||||
"delete": "Delete",
|
||||
"edit": "Edit",
|
||||
"more": "More",
|
||||
"next": "Next",
|
||||
"ok": "OK",
|
||||
"previous": "Previous",
|
||||
"refresh": "Refresh",
|
||||
"remove": "Remove",
|
||||
"retry": "Retry",
|
||||
"save": "Save",
|
||||
"search": "Search",
|
||||
"submit": "Submit"
|
||||
},
|
||||
"app": {
|
||||
"description": "Your AI Assistant Collaboration Platform",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Error",
|
||||
"info": "Information",
|
||||
"loading": "Loading",
|
||||
"success": "Success",
|
||||
"warning": "Warning"
|
||||
}
|
||||
}
|
||||
@@ -1,31 +0,0 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "OK",
|
||||
"detail": "A chat application based on a large language model",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "About"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Cancel",
|
||||
"no": "No",
|
||||
"title": "Confirm",
|
||||
"yes": "Yes"
|
||||
},
|
||||
"error": {
|
||||
"button": "OK",
|
||||
"detail": "An error occurred during the operation, please try again later",
|
||||
"message": "An error occurred",
|
||||
"title": "Error"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Download and Install",
|
||||
"downloadComplete": "Download Complete",
|
||||
"downloadCompleteMessage": "The update package has been downloaded, would you like to install it now?",
|
||||
"installLater": "Install Later",
|
||||
"installNow": "Install Now",
|
||||
"later": "Remind Me Later",
|
||||
"newVersion": "New Version Found",
|
||||
"newVersionAvailable": "New version available: {{version}}",
|
||||
"skipThisVersion": "Skip This Version"
|
||||
}
|
||||
}
|
||||
@@ -1,71 +0,0 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Checking for updates..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Developer Panel",
|
||||
"devTools": "Developer Tools",
|
||||
"forceReload": "Force Reload",
|
||||
"openStore": "Open Storage File",
|
||||
"refreshMenu": "Refresh menu",
|
||||
"reload": "Reload",
|
||||
"title": "Development"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Copy",
|
||||
"cut": "Cut",
|
||||
"delete": "Delete",
|
||||
"paste": "Paste",
|
||||
"redo": "Redo",
|
||||
"selectAll": "Select All",
|
||||
"speech": "Speech",
|
||||
"startSpeaking": "Start Speaking",
|
||||
"stopSpeaking": "Stop Speaking",
|
||||
"title": "Edit",
|
||||
"undo": "Undo"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Preferences",
|
||||
"quit": "Quit",
|
||||
"title": "File"
|
||||
},
|
||||
"help": {
|
||||
"about": "About",
|
||||
"githubRepo": "GitHub Repository",
|
||||
"reportIssue": "Report Issue",
|
||||
"title": "Help",
|
||||
"visitWebsite": "Visit Website"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "About {{appName}}",
|
||||
"devTools": "LobeHub Developer Tools",
|
||||
"hide": "Hide {{appName}}",
|
||||
"hideOthers": "Hide Others",
|
||||
"preferences": "Preferences...",
|
||||
"services": "Services",
|
||||
"unhide": "Show All"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Open {{appName}}",
|
||||
"quit": "Quit",
|
||||
"show": "Show {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Force Reload",
|
||||
"reload": "Reload",
|
||||
"resetZoom": "Reset Zoom",
|
||||
"title": "View",
|
||||
"toggleFullscreen": "Toggle Fullscreen",
|
||||
"zoomIn": "Zoom In",
|
||||
"zoomOut": "Zoom Out"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Bring All Windows to Front",
|
||||
"close": "Close",
|
||||
"front": "Bring All Windows to Front",
|
||||
"minimize": "Minimize",
|
||||
"title": "Window",
|
||||
"toggleFullscreen": "Toggle Fullscreen",
|
||||
"zoom": "Zoom"
|
||||
}
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Agregar",
|
||||
"back": "Volver",
|
||||
"cancel": "Cancelar",
|
||||
"close": "Cerrar",
|
||||
"confirm": "Confirmar",
|
||||
"delete": "Eliminar",
|
||||
"edit": "Editar",
|
||||
"more": "Más",
|
||||
"next": "Siguiente",
|
||||
"ok": "Aceptar",
|
||||
"previous": "Anterior",
|
||||
"refresh": "Actualizar",
|
||||
"remove": "Eliminar",
|
||||
"retry": "Reintentar",
|
||||
"save": "Guardar",
|
||||
"search": "Buscar",
|
||||
"submit": "Enviar"
|
||||
},
|
||||
"app": {
|
||||
"description": "Tu plataforma de colaboración con el asistente de IA",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Error",
|
||||
"info": "Información",
|
||||
"loading": "Cargando",
|
||||
"success": "Éxito",
|
||||
"warning": "Advertencia"
|
||||
}
|
||||
}
|
||||
"actions.add": "Agregar",
|
||||
"actions.back": "Volver",
|
||||
"actions.cancel": "Cancelar",
|
||||
"actions.close": "Cerrar",
|
||||
"actions.confirm": "Confirmar",
|
||||
"actions.delete": "Eliminar",
|
||||
"actions.edit": "Editar",
|
||||
"actions.more": "Más",
|
||||
"actions.next": "Siguiente",
|
||||
"actions.ok": "Aceptar",
|
||||
"actions.previous": "Anterior",
|
||||
"actions.refresh": "Actualizar",
|
||||
"actions.remove": "Eliminar",
|
||||
"actions.retry": "Reintentar",
|
||||
"actions.save": "Guardar",
|
||||
"actions.search": "Buscar",
|
||||
"actions.submit": "Enviar",
|
||||
"app.description": "Tu plataforma de colaboración con el asistente de IA",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Error",
|
||||
"status.info": "Información",
|
||||
"status.loading": "Cargando",
|
||||
"status.success": "Éxito",
|
||||
"status.warning": "Advertencia"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "Aceptar",
|
||||
"detail": "Una aplicación de chat basada en un modelo de lenguaje grande",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "Acerca de"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Cancelar",
|
||||
"no": "No",
|
||||
"title": "Confirmar",
|
||||
"yes": "Sí"
|
||||
},
|
||||
"error": {
|
||||
"button": "Aceptar",
|
||||
"detail": "Se produjo un error durante la operación, por favor intente de nuevo más tarde",
|
||||
"message": "Se produjo un error",
|
||||
"title": "Error"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Descargar e instalar",
|
||||
"downloadComplete": "Descarga completada",
|
||||
"downloadCompleteMessage": "El paquete de actualización se ha descargado, ¿desea instalarlo ahora?",
|
||||
"installLater": "Instalar más tarde",
|
||||
"installNow": "Instalar ahora",
|
||||
"later": "Recordar más tarde",
|
||||
"newVersion": "Nueva versión disponible",
|
||||
"newVersionAvailable": "Nueva versión encontrada: {{version}}",
|
||||
"skipThisVersion": "Saltar esta versión"
|
||||
}
|
||||
}
|
||||
"about.button": "Aceptar",
|
||||
"about.detail": "Una aplicación de chat basada en un modelo de lenguaje grande",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "Acerca de",
|
||||
"confirm.cancel": "Cancelar",
|
||||
"confirm.no": "No",
|
||||
"confirm.title": "Confirmar",
|
||||
"confirm.yes": "Sí",
|
||||
"error.button": "Aceptar",
|
||||
"error.detail": "Se produjo un error durante la operación, por favor intente de nuevo más tarde",
|
||||
"error.message": "Se produjo un error",
|
||||
"error.title": "Error",
|
||||
"update.downloadAndInstall": "Descargar e instalar",
|
||||
"update.downloadComplete": "Descarga completada",
|
||||
"update.downloadCompleteMessage": "El paquete de actualización se ha descargado, ¿desea instalarlo ahora?",
|
||||
"update.installLater": "Instalar más tarde",
|
||||
"update.installNow": "Instalar ahora",
|
||||
"update.later": "Recordar más tarde",
|
||||
"update.newVersion": "Nueva versión disponible",
|
||||
"update.newVersionAvailable": "Nueva versión encontrada: {{version}}",
|
||||
"update.skipThisVersion": "Saltar esta versión"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Comprobando actualizaciones..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Panel de desarrollador",
|
||||
"devTools": "Herramientas de desarrollador",
|
||||
"forceReload": "Recargar forzosamente",
|
||||
"openStore": "Abrir archivo de almacenamiento",
|
||||
"refreshMenu": "Actualizar menú",
|
||||
"reload": "Recargar",
|
||||
"title": "Desarrollo"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Copiar",
|
||||
"cut": "Cortar",
|
||||
"delete": "Eliminar",
|
||||
"paste": "Pegar",
|
||||
"redo": "Rehacer",
|
||||
"selectAll": "Seleccionar todo",
|
||||
"speech": "Voz",
|
||||
"startSpeaking": "Comenzar a leer en voz alta",
|
||||
"stopSpeaking": "Detener lectura en voz alta",
|
||||
"title": "Editar",
|
||||
"undo": "Deshacer"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Preferencias",
|
||||
"quit": "Salir",
|
||||
"title": "Archivo"
|
||||
},
|
||||
"help": {
|
||||
"about": "Acerca de",
|
||||
"githubRepo": "Repositorio de GitHub",
|
||||
"reportIssue": "Reportar un problema",
|
||||
"title": "Ayuda",
|
||||
"visitWebsite": "Visitar el sitio web"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "Acerca de {{appName}}",
|
||||
"devTools": "Herramientas de desarrollador de LobeHub",
|
||||
"hide": "Ocultar {{appName}}",
|
||||
"hideOthers": "Ocultar otros",
|
||||
"preferences": "Configuración...",
|
||||
"services": "Servicios",
|
||||
"unhide": "Mostrar todo"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Abrir {{appName}}",
|
||||
"quit": "Salir",
|
||||
"show": "Mostrar {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Recargar forzosamente",
|
||||
"reload": "Recargar",
|
||||
"resetZoom": "Restablecer zoom",
|
||||
"title": "Vista",
|
||||
"toggleFullscreen": "Alternar pantalla completa",
|
||||
"zoomIn": "Acercar",
|
||||
"zoomOut": "Alejar"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Traer todas las ventanas al frente",
|
||||
"close": "Cerrar",
|
||||
"front": "Traer todas las ventanas al frente",
|
||||
"minimize": "Minimizar",
|
||||
"title": "Ventana",
|
||||
"toggleFullscreen": "Alternar pantalla completa",
|
||||
"zoom": "Zoom"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Comprobando actualizaciones...",
|
||||
"dev.devPanel": "Panel de desarrollador",
|
||||
"dev.devTools": "Herramientas de desarrollador",
|
||||
"dev.forceReload": "Recargar forzosamente",
|
||||
"dev.openStore": "Abrir archivo de almacenamiento",
|
||||
"dev.refreshMenu": "Actualizar menú",
|
||||
"dev.reload": "Recargar",
|
||||
"dev.title": "Desarrollo",
|
||||
"edit.copy": "Copiar",
|
||||
"edit.cut": "Cortar",
|
||||
"edit.delete": "Eliminar",
|
||||
"edit.paste": "Pegar",
|
||||
"edit.redo": "Rehacer",
|
||||
"edit.selectAll": "Seleccionar todo",
|
||||
"edit.speech": "Voz",
|
||||
"edit.startSpeaking": "Comenzar a leer en voz alta",
|
||||
"edit.stopSpeaking": "Detener lectura en voz alta",
|
||||
"edit.title": "Editar",
|
||||
"edit.undo": "Deshacer",
|
||||
"file.preferences": "Preferencias",
|
||||
"file.quit": "Salir",
|
||||
"file.title": "Archivo",
|
||||
"help.about": "Acerca de",
|
||||
"help.githubRepo": "Repositorio de GitHub",
|
||||
"help.reportIssue": "Reportar un problema",
|
||||
"help.title": "Ayuda",
|
||||
"help.visitWebsite": "Visitar el sitio web",
|
||||
"macOS.about": "Acerca de {{appName}}",
|
||||
"macOS.devTools": "Herramientas de desarrollador de LobeHub",
|
||||
"macOS.hide": "Ocultar {{appName}}",
|
||||
"macOS.hideOthers": "Ocultar otros",
|
||||
"macOS.preferences": "Configuración...",
|
||||
"macOS.services": "Servicios",
|
||||
"macOS.unhide": "Mostrar todo",
|
||||
"tray.open": "Abrir {{appName}}",
|
||||
"tray.quit": "Salir",
|
||||
"tray.show": "Mostrar {{appName}}",
|
||||
"view.forceReload": "Recargar forzosamente",
|
||||
"view.reload": "Recargar",
|
||||
"view.resetZoom": "Restablecer zoom",
|
||||
"view.title": "Vista",
|
||||
"view.toggleFullscreen": "Alternar pantalla completa",
|
||||
"view.zoomIn": "Acercar",
|
||||
"view.zoomOut": "Alejar",
|
||||
"window.bringAllToFront": "Traer todas las ventanas al frente",
|
||||
"window.close": "Cerrar",
|
||||
"window.front": "Traer todas las ventanas al frente",
|
||||
"window.minimize": "Minimizar",
|
||||
"window.title": "Ventana",
|
||||
"window.toggleFullscreen": "Alternar pantalla completa",
|
||||
"window.zoom": "Zoom"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "افزودن",
|
||||
"back": "بازگشت",
|
||||
"cancel": "لغو",
|
||||
"close": "بستن",
|
||||
"confirm": "تأیید",
|
||||
"delete": "حذف",
|
||||
"edit": "ویرایش",
|
||||
"more": "بیشتر",
|
||||
"next": "مرحله بعد",
|
||||
"ok": "تأیید",
|
||||
"previous": "مرحله قبل",
|
||||
"refresh": "بهروزرسانی",
|
||||
"remove": "حذف",
|
||||
"retry": "تلاش مجدد",
|
||||
"save": "ذخیره",
|
||||
"search": "جستجو",
|
||||
"submit": "ارسال"
|
||||
},
|
||||
"app": {
|
||||
"description": "پلتفرم همکاری دستیار هوش مصنوعی شما",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "خطا",
|
||||
"info": "اطلاعات",
|
||||
"loading": "در حال بارگذاری",
|
||||
"success": "موفق",
|
||||
"warning": "هشدار"
|
||||
}
|
||||
}
|
||||
"actions.add": "افزودن",
|
||||
"actions.back": "بازگشت",
|
||||
"actions.cancel": "لغو",
|
||||
"actions.close": "بستن",
|
||||
"actions.confirm": "تأیید",
|
||||
"actions.delete": "حذف",
|
||||
"actions.edit": "ویرایش",
|
||||
"actions.more": "بیشتر",
|
||||
"actions.next": "مرحله بعد",
|
||||
"actions.ok": "تأیید",
|
||||
"actions.previous": "مرحله قبل",
|
||||
"actions.refresh": "بهروزرسانی",
|
||||
"actions.remove": "حذف",
|
||||
"actions.retry": "تلاش مجدد",
|
||||
"actions.save": "ذخیره",
|
||||
"actions.search": "جستجو",
|
||||
"actions.submit": "ارسال",
|
||||
"app.description": "پلتفرم همکاری دستیار هوش مصنوعی شما",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "خطا",
|
||||
"status.info": "اطلاعات",
|
||||
"status.loading": "در حال بارگذاری",
|
||||
"status.success": "موفق",
|
||||
"status.warning": "هشدار"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "تأیید",
|
||||
"detail": "یک برنامه چت مبتنی بر مدلهای زبانی بزرگ",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "درباره"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "لغو",
|
||||
"no": "خیر",
|
||||
"title": "تأیید",
|
||||
"yes": "بله"
|
||||
},
|
||||
"error": {
|
||||
"button": "تأیید",
|
||||
"detail": "در حین انجام عملیات خطایی رخ داده است، لطفاً بعداً دوباره تلاش کنید",
|
||||
"message": "خطا رخ داده است",
|
||||
"title": "خطا"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "دانلود و نصب",
|
||||
"downloadComplete": "دانلود کامل شد",
|
||||
"downloadCompleteMessage": "بسته بهروزرسانی دانلود شده است، آیا میخواهید بلافاصله نصب کنید؟",
|
||||
"installLater": "نصب بعداً",
|
||||
"installNow": "نصب اکنون",
|
||||
"later": "یادآوری بعداً",
|
||||
"newVersion": "نسخه جدیدی پیدا شد",
|
||||
"newVersionAvailable": "نسخه جدید پیدا شد: {{version}}",
|
||||
"skipThisVersion": "این نسخه را نادیده بگیرید"
|
||||
}
|
||||
}
|
||||
"about.button": "تأیید",
|
||||
"about.detail": "یک برنامه چت مبتنی بر مدلهای زبانی بزرگ",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "درباره",
|
||||
"confirm.cancel": "لغو",
|
||||
"confirm.no": "خیر",
|
||||
"confirm.title": "تأیید",
|
||||
"confirm.yes": "بله",
|
||||
"error.button": "تأیید",
|
||||
"error.detail": "در حین انجام عملیات خطایی رخ داده است، لطفاً بعداً دوباره تلاش کنید",
|
||||
"error.message": "خطا رخ داده است",
|
||||
"error.title": "خطا",
|
||||
"update.downloadAndInstall": "دانلود و نصب",
|
||||
"update.downloadComplete": "دانلود کامل شد",
|
||||
"update.downloadCompleteMessage": "بسته بهروزرسانی دانلود شده است، آیا میخواهید بلافاصله نصب کنید؟",
|
||||
"update.installLater": "نصب بعداً",
|
||||
"update.installNow": "نصب اکنون",
|
||||
"update.later": "یادآوری بعداً",
|
||||
"update.newVersion": "نسخه جدیدی پیدا شد",
|
||||
"update.newVersionAvailable": "نسخه جدید پیدا شد: {{version}}",
|
||||
"update.skipThisVersion": "این نسخه را نادیده بگیرید"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "بررسی بهروزرسانی..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "پنل توسعهدهنده",
|
||||
"devTools": "ابزارهای توسعهدهنده",
|
||||
"forceReload": "بارگذاری اجباری",
|
||||
"openStore": "باز کردن فایلهای ذخیره شده",
|
||||
"refreshMenu": "بهروزرسانی منو",
|
||||
"reload": "بارگذاری مجدد",
|
||||
"title": "توسعه"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "کپی",
|
||||
"cut": "برش",
|
||||
"delete": "حذف",
|
||||
"paste": "چسباندن",
|
||||
"redo": "انجام مجدد",
|
||||
"selectAll": "انتخاب همه",
|
||||
"speech": "گفتار",
|
||||
"startSpeaking": "شروع به خواندن",
|
||||
"stopSpeaking": "متوقف کردن خواندن",
|
||||
"title": "ویرایش",
|
||||
"undo": "بازگشت"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "تنظیمات",
|
||||
"quit": "خروج",
|
||||
"title": "فایل"
|
||||
},
|
||||
"help": {
|
||||
"about": "درباره",
|
||||
"githubRepo": "مخزن GitHub",
|
||||
"reportIssue": "گزارش مشکل",
|
||||
"title": "کمک",
|
||||
"visitWebsite": "بازدید از وبسایت"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "درباره {{appName}}",
|
||||
"devTools": "ابزارهای توسعهدهنده LobeHub",
|
||||
"hide": "پنهان کردن {{appName}}",
|
||||
"hideOthers": "پنهان کردن دیگران",
|
||||
"preferences": "تنظیمات...",
|
||||
"services": "خدمات",
|
||||
"unhide": "نمایش همه"
|
||||
},
|
||||
"tray": {
|
||||
"open": "باز کردن {{appName}}",
|
||||
"quit": "خروج",
|
||||
"show": "نمایش {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "بارگذاری اجباری",
|
||||
"reload": "بارگذاری مجدد",
|
||||
"resetZoom": "تنظیم زوم به حالت اولیه",
|
||||
"title": "نمایش",
|
||||
"toggleFullscreen": "تغییر به حالت تمام صفحه",
|
||||
"zoomIn": "بزرگنمایی",
|
||||
"zoomOut": "کوچکنمایی"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "همه پنجرهها را به جلو بیاورید",
|
||||
"close": "بستن",
|
||||
"front": "همه پنجرهها را به جلو بیاورید",
|
||||
"minimize": "کوچک کردن",
|
||||
"title": "پنجره",
|
||||
"toggleFullscreen": "تغییر به حالت تمام صفحه",
|
||||
"zoom": "زوم"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "بررسی بهروزرسانی...",
|
||||
"dev.devPanel": "پنل توسعهدهنده",
|
||||
"dev.devTools": "ابزارهای توسعهدهنده",
|
||||
"dev.forceReload": "بارگذاری اجباری",
|
||||
"dev.openStore": "باز کردن فایلهای ذخیره شده",
|
||||
"dev.refreshMenu": "بهروزرسانی منو",
|
||||
"dev.reload": "بارگذاری مجدد",
|
||||
"dev.title": "توسعه",
|
||||
"edit.copy": "کپی",
|
||||
"edit.cut": "برش",
|
||||
"edit.delete": "حذف",
|
||||
"edit.paste": "چسباندن",
|
||||
"edit.redo": "انجام مجدد",
|
||||
"edit.selectAll": "انتخاب همه",
|
||||
"edit.speech": "گفتار",
|
||||
"edit.startSpeaking": "شروع به خواندن",
|
||||
"edit.stopSpeaking": "متوقف کردن خواندن",
|
||||
"edit.title": "ویرایش",
|
||||
"edit.undo": "بازگشت",
|
||||
"file.preferences": "تنظیمات",
|
||||
"file.quit": "خروج",
|
||||
"file.title": "فایل",
|
||||
"help.about": "درباره",
|
||||
"help.githubRepo": "مخزن GitHub",
|
||||
"help.reportIssue": "گزارش مشکل",
|
||||
"help.title": "کمک",
|
||||
"help.visitWebsite": "بازدید از وبسایت",
|
||||
"macOS.about": "درباره {{appName}}",
|
||||
"macOS.devTools": "ابزارهای توسعهدهنده LobeHub",
|
||||
"macOS.hide": "پنهان کردن {{appName}}",
|
||||
"macOS.hideOthers": "پنهان کردن دیگران",
|
||||
"macOS.preferences": "تنظیمات...",
|
||||
"macOS.services": "خدمات",
|
||||
"macOS.unhide": "نمایش همه",
|
||||
"tray.open": "باز کردن {{appName}}",
|
||||
"tray.quit": "خروج",
|
||||
"tray.show": "نمایش {{appName}}",
|
||||
"view.forceReload": "بارگذاری اجباری",
|
||||
"view.reload": "بارگذاری مجدد",
|
||||
"view.resetZoom": "تنظیم زوم به حالت اولیه",
|
||||
"view.title": "نمایش",
|
||||
"view.toggleFullscreen": "تغییر به حالت تمام صفحه",
|
||||
"view.zoomIn": "بزرگنمایی",
|
||||
"view.zoomOut": "کوچکنمایی",
|
||||
"window.bringAllToFront": "همه پنجرهها را به جلو بیاورید",
|
||||
"window.close": "بستن",
|
||||
"window.front": "همه پنجرهها را به جلو بیاورید",
|
||||
"window.minimize": "کوچک کردن",
|
||||
"window.title": "پنجره",
|
||||
"window.toggleFullscreen": "تغییر به حالت تمام صفحه",
|
||||
"window.zoom": "زوم"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Ajouter",
|
||||
"back": "Retour",
|
||||
"cancel": "Annuler",
|
||||
"close": "Fermer",
|
||||
"confirm": "Confirmer",
|
||||
"delete": "Supprimer",
|
||||
"edit": "Éditer",
|
||||
"more": "Plus",
|
||||
"next": "Suivant",
|
||||
"ok": "D'accord",
|
||||
"previous": "Précédent",
|
||||
"refresh": "Rafraîchir",
|
||||
"remove": "Retirer",
|
||||
"retry": "Réessayer",
|
||||
"save": "Enregistrer",
|
||||
"search": "Rechercher",
|
||||
"submit": "Soumettre"
|
||||
},
|
||||
"app": {
|
||||
"description": "Votre plateforme de collaboration avec l'assistant IA",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Erreur",
|
||||
"info": "Information",
|
||||
"loading": "Chargement",
|
||||
"success": "Succès",
|
||||
"warning": "Avertissement"
|
||||
}
|
||||
}
|
||||
"actions.add": "Ajouter",
|
||||
"actions.back": "Retour",
|
||||
"actions.cancel": "Annuler",
|
||||
"actions.close": "Fermer",
|
||||
"actions.confirm": "Confirmer",
|
||||
"actions.delete": "Supprimer",
|
||||
"actions.edit": "Éditer",
|
||||
"actions.more": "Plus",
|
||||
"actions.next": "Suivant",
|
||||
"actions.ok": "D'accord",
|
||||
"actions.previous": "Précédent",
|
||||
"actions.refresh": "Rafraîchir",
|
||||
"actions.remove": "Retirer",
|
||||
"actions.retry": "Réessayer",
|
||||
"actions.save": "Enregistrer",
|
||||
"actions.search": "Rechercher",
|
||||
"actions.submit": "Soumettre",
|
||||
"app.description": "Votre plateforme de collaboration avec l'assistant IA",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Erreur",
|
||||
"status.info": "Information",
|
||||
"status.loading": "Chargement",
|
||||
"status.success": "Succès",
|
||||
"status.warning": "Avertissement"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "D'accord",
|
||||
"detail": "Une application de chat basée sur un grand modèle de langage",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "À propos"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Annuler",
|
||||
"no": "Non",
|
||||
"title": "Confirmer",
|
||||
"yes": "Oui"
|
||||
},
|
||||
"error": {
|
||||
"button": "D'accord",
|
||||
"detail": "Une erreur s'est produite lors de l'opération, veuillez réessayer plus tard",
|
||||
"message": "Une erreur s'est produite",
|
||||
"title": "Erreur"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Télécharger et installer",
|
||||
"downloadComplete": "Téléchargement terminé",
|
||||
"downloadCompleteMessage": "Le paquet de mise à jour a été téléchargé, souhaitez-vous l'installer maintenant ?",
|
||||
"installLater": "Installer plus tard",
|
||||
"installNow": "Installer maintenant",
|
||||
"later": "Rappeler plus tard",
|
||||
"newVersion": "Nouvelle version détectée",
|
||||
"newVersionAvailable": "Nouvelle version disponible : {{version}}",
|
||||
"skipThisVersion": "Ignorer cette version"
|
||||
}
|
||||
}
|
||||
"about.button": "D'accord",
|
||||
"about.detail": "Une application de chat basée sur un grand modèle de langage",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "À propos",
|
||||
"confirm.cancel": "Annuler",
|
||||
"confirm.no": "Non",
|
||||
"confirm.title": "Confirmer",
|
||||
"confirm.yes": "Oui",
|
||||
"error.button": "D'accord",
|
||||
"error.detail": "Une erreur s'est produite lors de l'opération, veuillez réessayer plus tard",
|
||||
"error.message": "Une erreur s'est produite",
|
||||
"error.title": "Erreur",
|
||||
"update.downloadAndInstall": "Télécharger et installer",
|
||||
"update.downloadComplete": "Téléchargement terminé",
|
||||
"update.downloadCompleteMessage": "Le paquet de mise à jour a été téléchargé, souhaitez-vous l'installer maintenant ?",
|
||||
"update.installLater": "Installer plus tard",
|
||||
"update.installNow": "Installer maintenant",
|
||||
"update.later": "Rappeler plus tard",
|
||||
"update.newVersion": "Nouvelle version détectée",
|
||||
"update.newVersionAvailable": "Nouvelle version disponible : {{version}}",
|
||||
"update.skipThisVersion": "Ignorer cette version"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Vérifier les mises à jour..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Panneau de développement",
|
||||
"devTools": "Outils de développement",
|
||||
"forceReload": "Recharger de force",
|
||||
"openStore": "Ouvrir le fichier de stockage",
|
||||
"refreshMenu": "Rafraîchir le menu",
|
||||
"reload": "Recharger",
|
||||
"title": "Développement"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Copier",
|
||||
"cut": "Couper",
|
||||
"delete": "Supprimer",
|
||||
"paste": "Coller",
|
||||
"redo": "Rétablir",
|
||||
"selectAll": "Tout sélectionner",
|
||||
"speech": "Voix",
|
||||
"startSpeaking": "Commencer à lire",
|
||||
"stopSpeaking": "Arrêter de lire",
|
||||
"title": "Édition",
|
||||
"undo": "Annuler"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Préférences",
|
||||
"quit": "Quitter",
|
||||
"title": "Fichier"
|
||||
},
|
||||
"help": {
|
||||
"about": "À propos",
|
||||
"githubRepo": "Dépôt GitHub",
|
||||
"reportIssue": "Signaler un problème",
|
||||
"title": "Aide",
|
||||
"visitWebsite": "Visiter le site officiel"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "À propos de {{appName}}",
|
||||
"devTools": "Outils de développement LobeHub",
|
||||
"hide": "Masquer {{appName}}",
|
||||
"hideOthers": "Masquer les autres",
|
||||
"preferences": "Préférences...",
|
||||
"services": "Services",
|
||||
"unhide": "Tout afficher"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Ouvrir {{appName}}",
|
||||
"quit": "Quitter",
|
||||
"show": "Afficher {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Recharger de force",
|
||||
"reload": "Recharger",
|
||||
"resetZoom": "Réinitialiser le zoom",
|
||||
"title": "Affichage",
|
||||
"toggleFullscreen": "Basculer en plein écran",
|
||||
"zoomIn": "Zoomer",
|
||||
"zoomOut": "Dézoomer"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Mettre toutes les fenêtres au premier plan",
|
||||
"close": "Fermer",
|
||||
"front": "Mettre toutes les fenêtres au premier plan",
|
||||
"minimize": "Réduire",
|
||||
"title": "Fenêtre",
|
||||
"toggleFullscreen": "Basculer en plein écran",
|
||||
"zoom": "Zoom"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Vérifier les mises à jour...",
|
||||
"dev.devPanel": "Panneau de développement",
|
||||
"dev.devTools": "Outils de développement",
|
||||
"dev.forceReload": "Recharger de force",
|
||||
"dev.openStore": "Ouvrir le fichier de stockage",
|
||||
"dev.refreshMenu": "Rafraîchir le menu",
|
||||
"dev.reload": "Recharger",
|
||||
"dev.title": "Développement",
|
||||
"edit.copy": "Copier",
|
||||
"edit.cut": "Couper",
|
||||
"edit.delete": "Supprimer",
|
||||
"edit.paste": "Coller",
|
||||
"edit.redo": "Rétablir",
|
||||
"edit.selectAll": "Tout sélectionner",
|
||||
"edit.speech": "Voix",
|
||||
"edit.startSpeaking": "Commencer à lire",
|
||||
"edit.stopSpeaking": "Arrêter de lire",
|
||||
"edit.title": "Édition",
|
||||
"edit.undo": "Annuler",
|
||||
"file.preferences": "Préférences",
|
||||
"file.quit": "Quitter",
|
||||
"file.title": "Fichier",
|
||||
"help.about": "À propos",
|
||||
"help.githubRepo": "Dépôt GitHub",
|
||||
"help.reportIssue": "Signaler un problème",
|
||||
"help.title": "Aide",
|
||||
"help.visitWebsite": "Visiter le site officiel",
|
||||
"macOS.about": "À propos de {{appName}}",
|
||||
"macOS.devTools": "Outils de développement LobeHub",
|
||||
"macOS.hide": "Masquer {{appName}}",
|
||||
"macOS.hideOthers": "Masquer les autres",
|
||||
"macOS.preferences": "Préférences...",
|
||||
"macOS.services": "Services",
|
||||
"macOS.unhide": "Tout afficher",
|
||||
"tray.open": "Ouvrir {{appName}}",
|
||||
"tray.quit": "Quitter",
|
||||
"tray.show": "Afficher {{appName}}",
|
||||
"view.forceReload": "Recharger de force",
|
||||
"view.reload": "Recharger",
|
||||
"view.resetZoom": "Réinitialiser le zoom",
|
||||
"view.title": "Affichage",
|
||||
"view.toggleFullscreen": "Basculer en plein écran",
|
||||
"view.zoomIn": "Zoomer",
|
||||
"view.zoomOut": "Dézoomer",
|
||||
"window.bringAllToFront": "Mettre toutes les fenêtres au premier plan",
|
||||
"window.close": "Fermer",
|
||||
"window.front": "Mettre toutes les fenêtres au premier plan",
|
||||
"window.minimize": "Réduire",
|
||||
"window.title": "Fenêtre",
|
||||
"window.toggleFullscreen": "Basculer en plein écran",
|
||||
"window.zoom": "Zoom"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Aggiungi",
|
||||
"back": "Indietro",
|
||||
"cancel": "Annulla",
|
||||
"close": "Chiudi",
|
||||
"confirm": "Conferma",
|
||||
"delete": "Elimina",
|
||||
"edit": "Modifica",
|
||||
"more": "Di più",
|
||||
"next": "Avanti",
|
||||
"ok": "OK",
|
||||
"previous": "Indietro",
|
||||
"refresh": "Aggiorna",
|
||||
"remove": "Rimuovi",
|
||||
"retry": "Riprova",
|
||||
"save": "Salva",
|
||||
"search": "Cerca",
|
||||
"submit": "Invia"
|
||||
},
|
||||
"app": {
|
||||
"description": "La tua piattaforma di collaborazione con assistente AI",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Errore",
|
||||
"info": "Informazioni",
|
||||
"loading": "Caricamento in corso",
|
||||
"success": "Successo",
|
||||
"warning": "Avviso"
|
||||
}
|
||||
}
|
||||
"actions.add": "Aggiungi",
|
||||
"actions.back": "Indietro",
|
||||
"actions.cancel": "Annulla",
|
||||
"actions.close": "Chiudi",
|
||||
"actions.confirm": "Conferma",
|
||||
"actions.delete": "Elimina",
|
||||
"actions.edit": "Modifica",
|
||||
"actions.more": "Di più",
|
||||
"actions.next": "Avanti",
|
||||
"actions.ok": "OK",
|
||||
"actions.previous": "Indietro",
|
||||
"actions.refresh": "Aggiorna",
|
||||
"actions.remove": "Rimuovi",
|
||||
"actions.retry": "Riprova",
|
||||
"actions.save": "Salva",
|
||||
"actions.search": "Cerca",
|
||||
"actions.submit": "Invia",
|
||||
"app.description": "La tua piattaforma di collaborazione con assistente AI",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Errore",
|
||||
"status.info": "Informazioni",
|
||||
"status.loading": "Caricamento in corso",
|
||||
"status.success": "Successo",
|
||||
"status.warning": "Avviso"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "Conferma",
|
||||
"detail": "Un'app di chat basata su un grande modello linguistico",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "Informazioni"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Annulla",
|
||||
"no": "No",
|
||||
"title": "Conferma",
|
||||
"yes": "Sì"
|
||||
},
|
||||
"error": {
|
||||
"button": "Conferma",
|
||||
"detail": "Si è verificato un errore durante l'operazione, riprovare più tardi",
|
||||
"message": "Si è verificato un errore",
|
||||
"title": "Errore"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Scarica e installa",
|
||||
"downloadComplete": "Download completato",
|
||||
"downloadCompleteMessage": "Il pacchetto di aggiornamento è stato scaricato, vuoi installarlo subito?",
|
||||
"installLater": "Installa più tardi",
|
||||
"installNow": "Installa ora",
|
||||
"later": "Promemoria più tardi",
|
||||
"newVersion": "Nuova versione disponibile",
|
||||
"newVersionAvailable": "Nuova versione trovata: {{version}}",
|
||||
"skipThisVersion": "Salta questa versione"
|
||||
}
|
||||
}
|
||||
"about.button": "Conferma",
|
||||
"about.detail": "Un'app di chat basata su un grande modello linguistico",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "Informazioni",
|
||||
"confirm.cancel": "Annulla",
|
||||
"confirm.no": "No",
|
||||
"confirm.title": "Conferma",
|
||||
"confirm.yes": "Sì",
|
||||
"error.button": "Conferma",
|
||||
"error.detail": "Si è verificato un errore durante l'operazione, riprovare più tardi",
|
||||
"error.message": "Si è verificato un errore",
|
||||
"error.title": "Errore",
|
||||
"update.downloadAndInstall": "Scarica e installa",
|
||||
"update.downloadComplete": "Download completato",
|
||||
"update.downloadCompleteMessage": "Il pacchetto di aggiornamento è stato scaricato, vuoi installarlo subito?",
|
||||
"update.installLater": "Installa più tardi",
|
||||
"update.installNow": "Installa ora",
|
||||
"update.later": "Promemoria più tardi",
|
||||
"update.newVersion": "Nuova versione disponibile",
|
||||
"update.newVersionAvailable": "Nuova versione trovata: {{version}}",
|
||||
"update.skipThisVersion": "Salta questa versione"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Controlla aggiornamenti..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Pannello sviluppatore",
|
||||
"devTools": "Strumenti per sviluppatori",
|
||||
"forceReload": "Ricarica forzata",
|
||||
"openStore": "Apri il file di archiviazione",
|
||||
"refreshMenu": "Aggiorna menu",
|
||||
"reload": "Ricarica",
|
||||
"title": "Sviluppo"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Copia",
|
||||
"cut": "Taglia",
|
||||
"delete": "Elimina",
|
||||
"paste": "Incolla",
|
||||
"redo": "Ripeti",
|
||||
"selectAll": "Seleziona tutto",
|
||||
"speech": "Voce",
|
||||
"startSpeaking": "Inizia a leggere",
|
||||
"stopSpeaking": "Ferma la lettura",
|
||||
"title": "Modifica",
|
||||
"undo": "Annulla"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Preferenze",
|
||||
"quit": "Esci",
|
||||
"title": "File"
|
||||
},
|
||||
"help": {
|
||||
"about": "Informazioni",
|
||||
"githubRepo": "Repository GitHub",
|
||||
"reportIssue": "Segnala un problema",
|
||||
"title": "Aiuto",
|
||||
"visitWebsite": "Visita il sito ufficiale"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "Informazioni su {{appName}}",
|
||||
"devTools": "Strumenti per sviluppatori LobeHub",
|
||||
"hide": "Nascondi {{appName}}",
|
||||
"hideOthers": "Nascondi altri",
|
||||
"preferences": "Impostazioni...",
|
||||
"services": "Servizi",
|
||||
"unhide": "Mostra tutto"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Apri {{appName}}",
|
||||
"quit": "Esci",
|
||||
"show": "Mostra {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Ricarica forzata",
|
||||
"reload": "Ricarica",
|
||||
"resetZoom": "Reimposta zoom",
|
||||
"title": "Visualizza",
|
||||
"toggleFullscreen": "Attiva/disattiva schermo intero",
|
||||
"zoomIn": "Ingrandisci",
|
||||
"zoomOut": "Riduci"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Porta tutte le finestre in primo piano",
|
||||
"close": "Chiudi",
|
||||
"front": "Porta tutte le finestre in primo piano",
|
||||
"minimize": "Minimizza",
|
||||
"title": "Finestra",
|
||||
"toggleFullscreen": "Attiva/disattiva schermo intero",
|
||||
"zoom": "Zoom"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Controlla aggiornamenti...",
|
||||
"dev.devPanel": "Pannello sviluppatore",
|
||||
"dev.devTools": "Strumenti per sviluppatori",
|
||||
"dev.forceReload": "Ricarica forzata",
|
||||
"dev.openStore": "Apri il file di archiviazione",
|
||||
"dev.refreshMenu": "Aggiorna menu",
|
||||
"dev.reload": "Ricarica",
|
||||
"dev.title": "Sviluppo",
|
||||
"edit.copy": "Copia",
|
||||
"edit.cut": "Taglia",
|
||||
"edit.delete": "Elimina",
|
||||
"edit.paste": "Incolla",
|
||||
"edit.redo": "Ripeti",
|
||||
"edit.selectAll": "Seleziona tutto",
|
||||
"edit.speech": "Voce",
|
||||
"edit.startSpeaking": "Inizia a leggere",
|
||||
"edit.stopSpeaking": "Ferma la lettura",
|
||||
"edit.title": "Modifica",
|
||||
"edit.undo": "Annulla",
|
||||
"file.preferences": "Preferenze",
|
||||
"file.quit": "Esci",
|
||||
"file.title": "File",
|
||||
"help.about": "Informazioni",
|
||||
"help.githubRepo": "Repository GitHub",
|
||||
"help.reportIssue": "Segnala un problema",
|
||||
"help.title": "Aiuto",
|
||||
"help.visitWebsite": "Visita il sito ufficiale",
|
||||
"macOS.about": "Informazioni su {{appName}}",
|
||||
"macOS.devTools": "Strumenti per sviluppatori LobeHub",
|
||||
"macOS.hide": "Nascondi {{appName}}",
|
||||
"macOS.hideOthers": "Nascondi altri",
|
||||
"macOS.preferences": "Impostazioni...",
|
||||
"macOS.services": "Servizi",
|
||||
"macOS.unhide": "Mostra tutto",
|
||||
"tray.open": "Apri {{appName}}",
|
||||
"tray.quit": "Esci",
|
||||
"tray.show": "Mostra {{appName}}",
|
||||
"view.forceReload": "Ricarica forzata",
|
||||
"view.reload": "Ricarica",
|
||||
"view.resetZoom": "Reimposta zoom",
|
||||
"view.title": "Visualizza",
|
||||
"view.toggleFullscreen": "Attiva/disattiva schermo intero",
|
||||
"view.zoomIn": "Ingrandisci",
|
||||
"view.zoomOut": "Riduci",
|
||||
"window.bringAllToFront": "Porta tutte le finestre in primo piano",
|
||||
"window.close": "Chiudi",
|
||||
"window.front": "Porta tutte le finestre in primo piano",
|
||||
"window.minimize": "Minimizza",
|
||||
"window.title": "Finestra",
|
||||
"window.toggleFullscreen": "Attiva/disattiva schermo intero",
|
||||
"window.zoom": "Zoom"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "追加",
|
||||
"back": "戻る",
|
||||
"cancel": "キャンセル",
|
||||
"close": "閉じる",
|
||||
"confirm": "確認",
|
||||
"delete": "削除",
|
||||
"edit": "編集",
|
||||
"more": "もっと見る",
|
||||
"next": "次へ",
|
||||
"ok": "OK",
|
||||
"previous": "前へ",
|
||||
"refresh": "更新",
|
||||
"remove": "削除",
|
||||
"retry": "再試行",
|
||||
"save": "保存",
|
||||
"search": "検索",
|
||||
"submit": "送信"
|
||||
},
|
||||
"app": {
|
||||
"description": "あなたのAIアシスタント協力プラットフォーム",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "エラー",
|
||||
"info": "情報",
|
||||
"loading": "読み込み中",
|
||||
"success": "成功",
|
||||
"warning": "警告"
|
||||
}
|
||||
}
|
||||
"actions.add": "追加",
|
||||
"actions.back": "戻る",
|
||||
"actions.cancel": "キャンセル",
|
||||
"actions.close": "閉じる",
|
||||
"actions.confirm": "確認",
|
||||
"actions.delete": "削除",
|
||||
"actions.edit": "編集",
|
||||
"actions.more": "もっと見る",
|
||||
"actions.next": "次へ",
|
||||
"actions.ok": "OK",
|
||||
"actions.previous": "前へ",
|
||||
"actions.refresh": "更新",
|
||||
"actions.remove": "削除",
|
||||
"actions.retry": "再試行",
|
||||
"actions.save": "保存",
|
||||
"actions.search": "検索",
|
||||
"actions.submit": "送信",
|
||||
"app.description": "あなたのAIアシスタント協力プラットフォーム",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "エラー",
|
||||
"status.info": "情報",
|
||||
"status.loading": "読み込み中",
|
||||
"status.success": "成功",
|
||||
"status.warning": "警告"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "確定",
|
||||
"detail": "大規模言語モデルに基づくチャットアプリ",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "について"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "キャンセル",
|
||||
"no": "いいえ",
|
||||
"title": "確認",
|
||||
"yes": "はい"
|
||||
},
|
||||
"error": {
|
||||
"button": "確定",
|
||||
"detail": "操作中にエラーが発生しました。後で再試行してください。",
|
||||
"message": "エラーが発生しました",
|
||||
"title": "エラー"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "ダウンロードしてインストール",
|
||||
"downloadComplete": "ダウンロード完了",
|
||||
"downloadCompleteMessage": "更新パッケージのダウンロードが完了しました。今すぐインストールしますか?",
|
||||
"installLater": "後でインストール",
|
||||
"installNow": "今すぐインストール",
|
||||
"later": "後でリマインド",
|
||||
"newVersion": "新しいバージョンが見つかりました",
|
||||
"newVersionAvailable": "新しいバージョンが見つかりました: {{version}}",
|
||||
"skipThisVersion": "このバージョンをスキップ"
|
||||
}
|
||||
}
|
||||
"about.button": "確定",
|
||||
"about.detail": "大規模言語モデルに基づくチャットアプリ",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "について",
|
||||
"confirm.cancel": "キャンセル",
|
||||
"confirm.no": "いいえ",
|
||||
"confirm.title": "確認",
|
||||
"confirm.yes": "はい",
|
||||
"error.button": "確定",
|
||||
"error.detail": "操作中にエラーが発生しました。後で再試行してください。",
|
||||
"error.message": "エラーが発生しました",
|
||||
"error.title": "エラー",
|
||||
"update.downloadAndInstall": "ダウンロードしてインストール",
|
||||
"update.downloadComplete": "ダウンロード完了",
|
||||
"update.downloadCompleteMessage": "更新パッケージのダウンロードが完了しました。今すぐインストールしますか?",
|
||||
"update.installLater": "後でインストール",
|
||||
"update.installNow": "今すぐインストール",
|
||||
"update.later": "後でリマインド",
|
||||
"update.newVersion": "新しいバージョンが見つかりました",
|
||||
"update.newVersionAvailable": "新しいバージョンが見つかりました: {{version}}",
|
||||
"update.skipThisVersion": "このバージョンをスキップ"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "更新を確認しています..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "開発者パネル",
|
||||
"devTools": "開発者ツール",
|
||||
"forceReload": "強制再読み込み",
|
||||
"openStore": "ストレージファイルを開く",
|
||||
"refreshMenu": "メニューを更新",
|
||||
"reload": "再読み込み",
|
||||
"title": "開発"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "コピー",
|
||||
"cut": "切り取り",
|
||||
"delete": "削除",
|
||||
"paste": "貼り付け",
|
||||
"redo": "やり直し",
|
||||
"selectAll": "すべて選択",
|
||||
"speech": "音声",
|
||||
"startSpeaking": "読み上げ開始",
|
||||
"stopSpeaking": "読み上げ停止",
|
||||
"title": "編集",
|
||||
"undo": "元に戻す"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "設定",
|
||||
"quit": "終了",
|
||||
"title": "ファイル"
|
||||
},
|
||||
"help": {
|
||||
"about": "について",
|
||||
"githubRepo": "GitHub リポジトリ",
|
||||
"reportIssue": "問題を報告",
|
||||
"title": "ヘルプ",
|
||||
"visitWebsite": "公式ウェブサイトを訪問"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "{{appName}} について",
|
||||
"devTools": "LobeHub 開発者ツール",
|
||||
"hide": "{{appName}} を隠す",
|
||||
"hideOthers": "他を隠す",
|
||||
"preferences": "環境設定...",
|
||||
"services": "サービス",
|
||||
"unhide": "すべて表示"
|
||||
},
|
||||
"tray": {
|
||||
"open": "{{appName}} を開く",
|
||||
"quit": "終了",
|
||||
"show": "{{appName}} を表示"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "強制再読み込み",
|
||||
"reload": "再読み込み",
|
||||
"resetZoom": "ズームをリセット",
|
||||
"title": "ビュー",
|
||||
"toggleFullscreen": "フルスクリーン切替",
|
||||
"zoomIn": "ズームイン",
|
||||
"zoomOut": "ズームアウト"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "すべてのウィンドウを前面に",
|
||||
"close": "閉じる",
|
||||
"front": "すべてのウィンドウを前面に",
|
||||
"minimize": "最小化",
|
||||
"title": "ウィンドウ",
|
||||
"toggleFullscreen": "フルスクリーン切替",
|
||||
"zoom": "ズーム"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "更新を確認しています...",
|
||||
"dev.devPanel": "開発者パネル",
|
||||
"dev.devTools": "開発者ツール",
|
||||
"dev.forceReload": "強制再読み込み",
|
||||
"dev.openStore": "ストレージファイルを開く",
|
||||
"dev.refreshMenu": "メニューを更新",
|
||||
"dev.reload": "再読み込み",
|
||||
"dev.title": "開発",
|
||||
"edit.copy": "コピー",
|
||||
"edit.cut": "切り取り",
|
||||
"edit.delete": "削除",
|
||||
"edit.paste": "貼り付け",
|
||||
"edit.redo": "やり直し",
|
||||
"edit.selectAll": "すべて選択",
|
||||
"edit.speech": "音声",
|
||||
"edit.startSpeaking": "読み上げ開始",
|
||||
"edit.stopSpeaking": "読み上げ停止",
|
||||
"edit.title": "編集",
|
||||
"edit.undo": "元に戻す",
|
||||
"file.preferences": "設定",
|
||||
"file.quit": "終了",
|
||||
"file.title": "ファイル",
|
||||
"help.about": "について",
|
||||
"help.githubRepo": "GitHub リポジトリ",
|
||||
"help.reportIssue": "問題を報告",
|
||||
"help.title": "ヘルプ",
|
||||
"help.visitWebsite": "公式ウェブサイトを訪問",
|
||||
"macOS.about": "{{appName}} について",
|
||||
"macOS.devTools": "LobeHub 開発者ツール",
|
||||
"macOS.hide": "{{appName}} を隠す",
|
||||
"macOS.hideOthers": "他を隠す",
|
||||
"macOS.preferences": "環境設定...",
|
||||
"macOS.services": "サービス",
|
||||
"macOS.unhide": "すべて表示",
|
||||
"tray.open": "{{appName}} を開く",
|
||||
"tray.quit": "終了",
|
||||
"tray.show": "{{appName}} を表示",
|
||||
"view.forceReload": "強制再読み込み",
|
||||
"view.reload": "再読み込み",
|
||||
"view.resetZoom": "ズームをリセット",
|
||||
"view.title": "ビュー",
|
||||
"view.toggleFullscreen": "フルスクリーン切替",
|
||||
"view.zoomIn": "ズームイン",
|
||||
"view.zoomOut": "ズームアウト",
|
||||
"window.bringAllToFront": "すべてのウィンドウを前面に",
|
||||
"window.close": "閉じる",
|
||||
"window.front": "すべてのウィンドウを前面に",
|
||||
"window.minimize": "最小化",
|
||||
"window.title": "ウィンドウ",
|
||||
"window.toggleFullscreen": "フルスクリーン切替",
|
||||
"window.zoom": "ズーム"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "추가",
|
||||
"back": "뒤로",
|
||||
"cancel": "취소",
|
||||
"close": "닫기",
|
||||
"confirm": "확인",
|
||||
"delete": "삭제",
|
||||
"edit": "편집",
|
||||
"more": "더보기",
|
||||
"next": "다음",
|
||||
"ok": "확인",
|
||||
"previous": "이전",
|
||||
"refresh": "새로 고침",
|
||||
"remove": "제거",
|
||||
"retry": "다시 시도",
|
||||
"save": "저장",
|
||||
"search": "검색",
|
||||
"submit": "제출"
|
||||
},
|
||||
"app": {
|
||||
"description": "당신의 AI 비서 협업 플랫폼",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "오류",
|
||||
"info": "정보",
|
||||
"loading": "로딩 중",
|
||||
"success": "성공",
|
||||
"warning": "경고"
|
||||
}
|
||||
}
|
||||
"actions.add": "추가",
|
||||
"actions.back": "뒤로",
|
||||
"actions.cancel": "취소",
|
||||
"actions.close": "닫기",
|
||||
"actions.confirm": "확인",
|
||||
"actions.delete": "삭제",
|
||||
"actions.edit": "편집",
|
||||
"actions.more": "더보기",
|
||||
"actions.next": "다음",
|
||||
"actions.ok": "확인",
|
||||
"actions.previous": "이전",
|
||||
"actions.refresh": "새로 고침",
|
||||
"actions.remove": "제거",
|
||||
"actions.retry": "다시 시도",
|
||||
"actions.save": "저장",
|
||||
"actions.search": "검색",
|
||||
"actions.submit": "제출",
|
||||
"app.description": "당신의 AI 비서 협업 플랫폼",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "오류",
|
||||
"status.info": "정보",
|
||||
"status.loading": "로딩 중",
|
||||
"status.success": "성공",
|
||||
"status.warning": "경고"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "확인",
|
||||
"detail": "대형 언어 모델 기반의 채팅 애플리케이션",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "정보"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "취소",
|
||||
"no": "아니요",
|
||||
"title": "확인",
|
||||
"yes": "예"
|
||||
},
|
||||
"error": {
|
||||
"button": "확인",
|
||||
"detail": "작업 중 오류가 발생했습니다. 나중에 다시 시도해 주세요.",
|
||||
"message": "오류 발생",
|
||||
"title": "오류"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "다운로드 및 설치",
|
||||
"downloadComplete": "다운로드 완료",
|
||||
"downloadCompleteMessage": "업데이트 패키지가 다운로드 완료되었습니다. 지금 설치하시겠습니까?",
|
||||
"installLater": "나중에 설치",
|
||||
"installNow": "지금 설치",
|
||||
"later": "나중에 알림",
|
||||
"newVersion": "새 버전 발견",
|
||||
"newVersionAvailable": "새 버전 발견: {{version}}",
|
||||
"skipThisVersion": "이 버전 건너뛰기"
|
||||
}
|
||||
}
|
||||
"about.button": "확인",
|
||||
"about.detail": "대형 언어 모델 기반의 채팅 애플리케이션",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "정보",
|
||||
"confirm.cancel": "취소",
|
||||
"confirm.no": "아니요",
|
||||
"confirm.title": "확인",
|
||||
"confirm.yes": "예",
|
||||
"error.button": "확인",
|
||||
"error.detail": "작업 중 오류가 발생했습니다. 나중에 다시 시도해 주세요.",
|
||||
"error.message": "오류 발생",
|
||||
"error.title": "오류",
|
||||
"update.downloadAndInstall": "다운로드 및 설치",
|
||||
"update.downloadComplete": "다운로드 완료",
|
||||
"update.downloadCompleteMessage": "업데이트 패키지가 다운로드 완료되었습니다. 지금 설치하시겠습니까?",
|
||||
"update.installLater": "나중에 설치",
|
||||
"update.installNow": "지금 설치",
|
||||
"update.later": "나중에 알림",
|
||||
"update.newVersion": "새 버전 발견",
|
||||
"update.newVersionAvailable": "새 버전 발견: {{version}}",
|
||||
"update.skipThisVersion": "이 버전 건너뛰기"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "업데이트 확인 중..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "개발자 패널",
|
||||
"devTools": "개발자 도구",
|
||||
"forceReload": "강제 새로 고침",
|
||||
"openStore": "저장 파일 열기",
|
||||
"refreshMenu": "메뉴 새로 고침",
|
||||
"reload": "새로 고침",
|
||||
"title": "개발"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "복사",
|
||||
"cut": "잘라내기",
|
||||
"delete": "삭제",
|
||||
"paste": "붙여넣기",
|
||||
"redo": "다시 실행",
|
||||
"selectAll": "모두 선택",
|
||||
"speech": "음성",
|
||||
"startSpeaking": "읽기 시작",
|
||||
"stopSpeaking": "읽기 중지",
|
||||
"title": "편집",
|
||||
"undo": "실행 취소"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "환경 설정",
|
||||
"quit": "종료",
|
||||
"title": "파일"
|
||||
},
|
||||
"help": {
|
||||
"about": "정보",
|
||||
"githubRepo": "GitHub 저장소",
|
||||
"reportIssue": "문제 보고",
|
||||
"title": "도움말",
|
||||
"visitWebsite": "웹사이트 방문"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "{{appName}} 정보",
|
||||
"devTools": "LobeHub 개발자 도구",
|
||||
"hide": "{{appName}} 숨기기",
|
||||
"hideOthers": "다른 것 숨기기",
|
||||
"preferences": "환경 설정...",
|
||||
"services": "서비스",
|
||||
"unhide": "모두 표시"
|
||||
},
|
||||
"tray": {
|
||||
"open": "{{appName}} 열기",
|
||||
"quit": "종료",
|
||||
"show": "{{appName}} 표시"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "강제 새로 고침",
|
||||
"reload": "새로 고침",
|
||||
"resetZoom": "줌 초기화",
|
||||
"title": "보기",
|
||||
"toggleFullscreen": "전체 화면 전환",
|
||||
"zoomIn": "확대",
|
||||
"zoomOut": "축소"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "모든 창 앞으로 가져오기",
|
||||
"close": "닫기",
|
||||
"front": "모든 창 앞으로 가져오기",
|
||||
"minimize": "최소화",
|
||||
"title": "창",
|
||||
"toggleFullscreen": "전체 화면 전환",
|
||||
"zoom": "줌"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "업데이트 확인 중...",
|
||||
"dev.devPanel": "개발자 패널",
|
||||
"dev.devTools": "개발자 도구",
|
||||
"dev.forceReload": "강제 새로 고침",
|
||||
"dev.openStore": "저장 파일 열기",
|
||||
"dev.refreshMenu": "메뉴 새로 고침",
|
||||
"dev.reload": "새로 고침",
|
||||
"dev.title": "개발",
|
||||
"edit.copy": "복사",
|
||||
"edit.cut": "잘라내기",
|
||||
"edit.delete": "삭제",
|
||||
"edit.paste": "붙여넣기",
|
||||
"edit.redo": "다시 실행",
|
||||
"edit.selectAll": "모두 선택",
|
||||
"edit.speech": "음성",
|
||||
"edit.startSpeaking": "읽기 시작",
|
||||
"edit.stopSpeaking": "읽기 중지",
|
||||
"edit.title": "편집",
|
||||
"edit.undo": "실행 취소",
|
||||
"file.preferences": "환경 설정",
|
||||
"file.quit": "종료",
|
||||
"file.title": "파일",
|
||||
"help.about": "정보",
|
||||
"help.githubRepo": "GitHub 저장소",
|
||||
"help.reportIssue": "문제 보고",
|
||||
"help.title": "도움말",
|
||||
"help.visitWebsite": "웹사이트 방문",
|
||||
"macOS.about": "{{appName}} 정보",
|
||||
"macOS.devTools": "LobeHub 개발자 도구",
|
||||
"macOS.hide": "{{appName}} 숨기기",
|
||||
"macOS.hideOthers": "다른 것 숨기기",
|
||||
"macOS.preferences": "환경 설정...",
|
||||
"macOS.services": "서비스",
|
||||
"macOS.unhide": "모두 표시",
|
||||
"tray.open": "{{appName}} 열기",
|
||||
"tray.quit": "종료",
|
||||
"tray.show": "{{appName}} 표시",
|
||||
"view.forceReload": "강제 새로 고침",
|
||||
"view.reload": "새로 고침",
|
||||
"view.resetZoom": "줌 초기화",
|
||||
"view.title": "보기",
|
||||
"view.toggleFullscreen": "전체 화면 전환",
|
||||
"view.zoomIn": "확대",
|
||||
"view.zoomOut": "축소",
|
||||
"window.bringAllToFront": "모든 창 앞으로 가져오기",
|
||||
"window.close": "닫기",
|
||||
"window.front": "모든 창 앞으로 가져오기",
|
||||
"window.minimize": "최소화",
|
||||
"window.title": "창",
|
||||
"window.toggleFullscreen": "전체 화면 전환",
|
||||
"window.zoom": "줌"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Toevoegen",
|
||||
"back": "Terug",
|
||||
"cancel": "Annuleren",
|
||||
"close": "Sluiten",
|
||||
"confirm": "Bevestigen",
|
||||
"delete": "Verwijderen",
|
||||
"edit": "Bewerken",
|
||||
"more": "Meer",
|
||||
"next": "Volgende stap",
|
||||
"ok": "OK",
|
||||
"previous": "Vorige stap",
|
||||
"refresh": "Vernieuwen",
|
||||
"remove": "Verwijderen",
|
||||
"retry": "Opnieuw proberen",
|
||||
"save": "Opslaan",
|
||||
"search": "Zoeken",
|
||||
"submit": "Indienen"
|
||||
},
|
||||
"app": {
|
||||
"description": "Jouw AI-assistent samenwerkingsplatform",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Fout",
|
||||
"info": "Informatie",
|
||||
"loading": "Laden",
|
||||
"success": "Succes",
|
||||
"warning": "Waarschuwing"
|
||||
}
|
||||
}
|
||||
"actions.add": "Toevoegen",
|
||||
"actions.back": "Terug",
|
||||
"actions.cancel": "Annuleren",
|
||||
"actions.close": "Sluiten",
|
||||
"actions.confirm": "Bevestigen",
|
||||
"actions.delete": "Verwijderen",
|
||||
"actions.edit": "Bewerken",
|
||||
"actions.more": "Meer",
|
||||
"actions.next": "Volgende stap",
|
||||
"actions.ok": "OK",
|
||||
"actions.previous": "Vorige stap",
|
||||
"actions.refresh": "Vernieuwen",
|
||||
"actions.remove": "Verwijderen",
|
||||
"actions.retry": "Opnieuw proberen",
|
||||
"actions.save": "Opslaan",
|
||||
"actions.search": "Zoeken",
|
||||
"actions.submit": "Indienen",
|
||||
"app.description": "Jouw AI-assistent samenwerkingsplatform",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Fout",
|
||||
"status.info": "Informatie",
|
||||
"status.loading": "Laden",
|
||||
"status.success": "Succes",
|
||||
"status.warning": "Waarschuwing"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "Bevestigen",
|
||||
"detail": "Een chatapplicatie gebaseerd op een groot taalmodel",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "Over"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Annuleren",
|
||||
"no": "Nee",
|
||||
"title": "Bevestigen",
|
||||
"yes": "Ja"
|
||||
},
|
||||
"error": {
|
||||
"button": "Bevestigen",
|
||||
"detail": "Er is een fout opgetreden tijdens de operatie, probeer het later opnieuw",
|
||||
"message": "Er is een fout opgetreden",
|
||||
"title": "Fout"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Downloaden en installeren",
|
||||
"downloadComplete": "Download voltooid",
|
||||
"downloadCompleteMessage": "Het updatepakket is gedownload, wilt u het nu installeren?",
|
||||
"installLater": "Later installeren",
|
||||
"installNow": "Nu installeren",
|
||||
"later": "Later herinneren",
|
||||
"newVersion": "Nieuwe versie gevonden",
|
||||
"newVersionAvailable": "Nieuwe versie beschikbaar: {{version}}",
|
||||
"skipThisVersion": "Deze versie overslaan"
|
||||
}
|
||||
}
|
||||
"about.button": "Bevestigen",
|
||||
"about.detail": "Een chatapplicatie gebaseerd op een groot taalmodel",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "Over",
|
||||
"confirm.cancel": "Annuleren",
|
||||
"confirm.no": "Nee",
|
||||
"confirm.title": "Bevestigen",
|
||||
"confirm.yes": "Ja",
|
||||
"error.button": "Bevestigen",
|
||||
"error.detail": "Er is een fout opgetreden tijdens de operatie, probeer het later opnieuw",
|
||||
"error.message": "Er is een fout opgetreden",
|
||||
"error.title": "Fout",
|
||||
"update.downloadAndInstall": "Downloaden en installeren",
|
||||
"update.downloadComplete": "Download voltooid",
|
||||
"update.downloadCompleteMessage": "Het updatepakket is gedownload, wilt u het nu installeren?",
|
||||
"update.installLater": "Later installeren",
|
||||
"update.installNow": "Nu installeren",
|
||||
"update.later": "Later herinneren",
|
||||
"update.newVersion": "Nieuwe versie gevonden",
|
||||
"update.newVersionAvailable": "Nieuwe versie beschikbaar: {{version}}",
|
||||
"update.skipThisVersion": "Deze versie overslaan"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Updates controleren..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Ontwikkelaarspaneel",
|
||||
"devTools": "Ontwikkelaarstools",
|
||||
"forceReload": "Forceer herladen",
|
||||
"openStore": "Open opslagbestand",
|
||||
"refreshMenu": "Menu verversen",
|
||||
"reload": "Herladen",
|
||||
"title": "Ontwikkeling"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Kopiëren",
|
||||
"cut": "Knippen",
|
||||
"delete": "Verwijderen",
|
||||
"paste": "Plakken",
|
||||
"redo": "Opnieuw doen",
|
||||
"selectAll": "Alles selecteren",
|
||||
"speech": "Spraak",
|
||||
"startSpeaking": "Begin met voorlezen",
|
||||
"stopSpeaking": "Stop met voorlezen",
|
||||
"title": "Bewerken",
|
||||
"undo": "Ongedaan maken"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Voorkeuren",
|
||||
"quit": "Afsluiten",
|
||||
"title": "Bestand"
|
||||
},
|
||||
"help": {
|
||||
"about": "Over",
|
||||
"githubRepo": "GitHub-repo",
|
||||
"reportIssue": "Probleem melden",
|
||||
"title": "Hulp",
|
||||
"visitWebsite": "Bezoek de website"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "Over {{appName}}",
|
||||
"devTools": "LobeHub Ontwikkelaarstools",
|
||||
"hide": "Verberg {{appName}}",
|
||||
"hideOthers": "Verberg anderen",
|
||||
"preferences": "Voorkeuren...",
|
||||
"services": "Diensten",
|
||||
"unhide": "Toon alles"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Open {{appName}}",
|
||||
"quit": "Afsluiten",
|
||||
"show": "Toon {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Forceer herladen",
|
||||
"reload": "Herladen",
|
||||
"resetZoom": "Zoom resetten",
|
||||
"title": "Weergave",
|
||||
"toggleFullscreen": "Schakel volledig scherm in/uit",
|
||||
"zoomIn": "Inzoomen",
|
||||
"zoomOut": "Uitzoomen"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Breng alle vensters naar voren",
|
||||
"close": "Sluiten",
|
||||
"front": "Breng alle vensters naar voren",
|
||||
"minimize": "Minimaliseren",
|
||||
"title": "Venster",
|
||||
"toggleFullscreen": "Schakel volledig scherm in/uit",
|
||||
"zoom": "Inzoomen"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Updates controleren...",
|
||||
"dev.devPanel": "Ontwikkelaarspaneel",
|
||||
"dev.devTools": "Ontwikkelaarstools",
|
||||
"dev.forceReload": "Forceer herladen",
|
||||
"dev.openStore": "Open opslagbestand",
|
||||
"dev.refreshMenu": "Menu verversen",
|
||||
"dev.reload": "Herladen",
|
||||
"dev.title": "Ontwikkeling",
|
||||
"edit.copy": "Kopiëren",
|
||||
"edit.cut": "Knippen",
|
||||
"edit.delete": "Verwijderen",
|
||||
"edit.paste": "Plakken",
|
||||
"edit.redo": "Opnieuw doen",
|
||||
"edit.selectAll": "Alles selecteren",
|
||||
"edit.speech": "Spraak",
|
||||
"edit.startSpeaking": "Begin met voorlezen",
|
||||
"edit.stopSpeaking": "Stop met voorlezen",
|
||||
"edit.title": "Bewerken",
|
||||
"edit.undo": "Ongedaan maken",
|
||||
"file.preferences": "Voorkeuren",
|
||||
"file.quit": "Afsluiten",
|
||||
"file.title": "Bestand",
|
||||
"help.about": "Over",
|
||||
"help.githubRepo": "GitHub-repo",
|
||||
"help.reportIssue": "Probleem melden",
|
||||
"help.title": "Hulp",
|
||||
"help.visitWebsite": "Bezoek de website",
|
||||
"macOS.about": "Over {{appName}}",
|
||||
"macOS.devTools": "LobeHub Ontwikkelaarstools",
|
||||
"macOS.hide": "Verberg {{appName}}",
|
||||
"macOS.hideOthers": "Verberg anderen",
|
||||
"macOS.preferences": "Voorkeuren...",
|
||||
"macOS.services": "Diensten",
|
||||
"macOS.unhide": "Toon alles",
|
||||
"tray.open": "Open {{appName}}",
|
||||
"tray.quit": "Afsluiten",
|
||||
"tray.show": "Toon {{appName}}",
|
||||
"view.forceReload": "Forceer herladen",
|
||||
"view.reload": "Herladen",
|
||||
"view.resetZoom": "Zoom resetten",
|
||||
"view.title": "Weergave",
|
||||
"view.toggleFullscreen": "Schakel volledig scherm in/uit",
|
||||
"view.zoomIn": "Inzoomen",
|
||||
"view.zoomOut": "Uitzoomen",
|
||||
"window.bringAllToFront": "Breng alle vensters naar voren",
|
||||
"window.close": "Sluiten",
|
||||
"window.front": "Breng alle vensters naar voren",
|
||||
"window.minimize": "Minimaliseren",
|
||||
"window.title": "Venster",
|
||||
"window.toggleFullscreen": "Schakel volledig scherm in/uit",
|
||||
"window.zoom": "Inzoomen"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Dodaj",
|
||||
"back": "Wstecz",
|
||||
"cancel": "Anuluj",
|
||||
"close": "Zamknij",
|
||||
"confirm": "Potwierdź",
|
||||
"delete": "Usuń",
|
||||
"edit": "Edytuj",
|
||||
"more": "Więcej",
|
||||
"next": "Dalej",
|
||||
"ok": "OK",
|
||||
"previous": "Cofnij",
|
||||
"refresh": "Odśwież",
|
||||
"remove": "Usuń",
|
||||
"retry": "Spróbuj ponownie",
|
||||
"save": "Zapisz",
|
||||
"search": "Szukaj",
|
||||
"submit": "Wyślij"
|
||||
},
|
||||
"app": {
|
||||
"description": "Twoja platforma współpracy z asystentem AI",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Błąd",
|
||||
"info": "Informacja",
|
||||
"loading": "Ładowanie",
|
||||
"success": "Sukces",
|
||||
"warning": "Ostrzeżenie"
|
||||
}
|
||||
}
|
||||
"actions.add": "Dodaj",
|
||||
"actions.back": "Wstecz",
|
||||
"actions.cancel": "Anuluj",
|
||||
"actions.close": "Zamknij",
|
||||
"actions.confirm": "Potwierdź",
|
||||
"actions.delete": "Usuń",
|
||||
"actions.edit": "Edytuj",
|
||||
"actions.more": "Więcej",
|
||||
"actions.next": "Dalej",
|
||||
"actions.ok": "OK",
|
||||
"actions.previous": "Cofnij",
|
||||
"actions.refresh": "Odśwież",
|
||||
"actions.remove": "Usuń",
|
||||
"actions.retry": "Spróbuj ponownie",
|
||||
"actions.save": "Zapisz",
|
||||
"actions.search": "Szukaj",
|
||||
"actions.submit": "Wyślij",
|
||||
"app.description": "Twoja platforma współpracy z asystentem AI",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Błąd",
|
||||
"status.info": "Informacja",
|
||||
"status.loading": "Ładowanie",
|
||||
"status.success": "Sukces",
|
||||
"status.warning": "Ostrzeżenie"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "OK",
|
||||
"detail": "Aplikacja czatu oparta na dużym modelu językowym",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "O aplikacji"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Anuluj",
|
||||
"no": "Nie",
|
||||
"title": "Potwierdzenie",
|
||||
"yes": "Tak"
|
||||
},
|
||||
"error": {
|
||||
"button": "OK",
|
||||
"detail": "Wystąpił błąd podczas operacji, spróbuj ponownie później",
|
||||
"message": "Wystąpił błąd",
|
||||
"title": "Błąd"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Pobierz i zainstaluj",
|
||||
"downloadComplete": "Pobieranie zakończone",
|
||||
"downloadCompleteMessage": "Pakiet aktualizacji został pobrany, czy chcesz go teraz zainstalować?",
|
||||
"installLater": "Zainstaluj później",
|
||||
"installNow": "Zainstaluj teraz",
|
||||
"later": "Przypomnij później",
|
||||
"newVersion": "Nowa wersja dostępna",
|
||||
"newVersionAvailable": "Znaleziono nową wersję: {{version}}",
|
||||
"skipThisVersion": "Pomiń tę wersję"
|
||||
}
|
||||
}
|
||||
"about.button": "OK",
|
||||
"about.detail": "Aplikacja czatu oparta na dużym modelu językowym",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "O aplikacji",
|
||||
"confirm.cancel": "Anuluj",
|
||||
"confirm.no": "Nie",
|
||||
"confirm.title": "Potwierdzenie",
|
||||
"confirm.yes": "Tak",
|
||||
"error.button": "OK",
|
||||
"error.detail": "Wystąpił błąd podczas operacji, spróbuj ponownie później",
|
||||
"error.message": "Wystąpił błąd",
|
||||
"error.title": "Błąd",
|
||||
"update.downloadAndInstall": "Pobierz i zainstaluj",
|
||||
"update.downloadComplete": "Pobieranie zakończone",
|
||||
"update.downloadCompleteMessage": "Pakiet aktualizacji został pobrany, czy chcesz go teraz zainstalować?",
|
||||
"update.installLater": "Zainstaluj później",
|
||||
"update.installNow": "Zainstaluj teraz",
|
||||
"update.later": "Przypomnij później",
|
||||
"update.newVersion": "Nowa wersja dostępna",
|
||||
"update.newVersionAvailable": "Znaleziono nową wersję: {{version}}",
|
||||
"update.skipThisVersion": "Pomiń tę wersję"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Sprawdzanie aktualizacji..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Panel dewelopera",
|
||||
"devTools": "Narzędzia dewelopera",
|
||||
"forceReload": "Wymuś ponowne załadowanie",
|
||||
"openStore": "Otwórz plik magazynu",
|
||||
"refreshMenu": "Odśwież menu",
|
||||
"reload": "Przeładuj",
|
||||
"title": "Rozwój"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Kopiuj",
|
||||
"cut": "Wytnij",
|
||||
"delete": "Usuń",
|
||||
"paste": "Wklej",
|
||||
"redo": "Ponów",
|
||||
"selectAll": "Zaznacz wszystko",
|
||||
"speech": "Mowa",
|
||||
"startSpeaking": "Rozpocznij czytanie",
|
||||
"stopSpeaking": "Zatrzymaj czytanie",
|
||||
"title": "Edycja",
|
||||
"undo": "Cofnij"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Preferencje",
|
||||
"quit": "Zakończ",
|
||||
"title": "Plik"
|
||||
},
|
||||
"help": {
|
||||
"about": "O",
|
||||
"githubRepo": "Repozytorium GitHub",
|
||||
"reportIssue": "Zgłoś problem",
|
||||
"title": "Pomoc",
|
||||
"visitWebsite": "Odwiedź stronę internetową"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "O {{appName}}",
|
||||
"devTools": "Narzędzia dewelopera LobeHub",
|
||||
"hide": "Ukryj {{appName}}",
|
||||
"hideOthers": "Ukryj inne",
|
||||
"preferences": "Ustawienia...",
|
||||
"services": "Usługi",
|
||||
"unhide": "Pokaż wszystko"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Otwórz {{appName}}",
|
||||
"quit": "Zakończ",
|
||||
"show": "Pokaż {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Wymuś ponowne załadowanie",
|
||||
"reload": "Przeładuj",
|
||||
"resetZoom": "Zresetuj powiększenie",
|
||||
"title": "Widok",
|
||||
"toggleFullscreen": "Przełącz tryb pełnoekranowy",
|
||||
"zoomIn": "Powiększ",
|
||||
"zoomOut": "Pomniejsz"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Przenieś wszystkie okna na wierzch",
|
||||
"close": "Zamknij",
|
||||
"front": "Przenieś wszystkie okna na wierzch",
|
||||
"minimize": "Zminimalizuj",
|
||||
"title": "Okno",
|
||||
"toggleFullscreen": "Przełącz tryb pełnoekranowy",
|
||||
"zoom": "Powiększenie"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Sprawdzanie aktualizacji...",
|
||||
"dev.devPanel": "Panel dewelopera",
|
||||
"dev.devTools": "Narzędzia dewelopera",
|
||||
"dev.forceReload": "Wymuś ponowne załadowanie",
|
||||
"dev.openStore": "Otwórz plik magazynu",
|
||||
"dev.refreshMenu": "Odśwież menu",
|
||||
"dev.reload": "Przeładuj",
|
||||
"dev.title": "Rozwój",
|
||||
"edit.copy": "Kopiuj",
|
||||
"edit.cut": "Wytnij",
|
||||
"edit.delete": "Usuń",
|
||||
"edit.paste": "Wklej",
|
||||
"edit.redo": "Ponów",
|
||||
"edit.selectAll": "Zaznacz wszystko",
|
||||
"edit.speech": "Mowa",
|
||||
"edit.startSpeaking": "Rozpocznij czytanie",
|
||||
"edit.stopSpeaking": "Zatrzymaj czytanie",
|
||||
"edit.title": "Edycja",
|
||||
"edit.undo": "Cofnij",
|
||||
"file.preferences": "Preferencje",
|
||||
"file.quit": "Zakończ",
|
||||
"file.title": "Plik",
|
||||
"help.about": "O",
|
||||
"help.githubRepo": "Repozytorium GitHub",
|
||||
"help.reportIssue": "Zgłoś problem",
|
||||
"help.title": "Pomoc",
|
||||
"help.visitWebsite": "Odwiedź stronę internetową",
|
||||
"macOS.about": "O {{appName}}",
|
||||
"macOS.devTools": "Narzędzia dewelopera LobeHub",
|
||||
"macOS.hide": "Ukryj {{appName}}",
|
||||
"macOS.hideOthers": "Ukryj inne",
|
||||
"macOS.preferences": "Ustawienia...",
|
||||
"macOS.services": "Usługi",
|
||||
"macOS.unhide": "Pokaż wszystko",
|
||||
"tray.open": "Otwórz {{appName}}",
|
||||
"tray.quit": "Zakończ",
|
||||
"tray.show": "Pokaż {{appName}}",
|
||||
"view.forceReload": "Wymuś ponowne załadowanie",
|
||||
"view.reload": "Przeładuj",
|
||||
"view.resetZoom": "Zresetuj powiększenie",
|
||||
"view.title": "Widok",
|
||||
"view.toggleFullscreen": "Przełącz tryb pełnoekranowy",
|
||||
"view.zoomIn": "Powiększ",
|
||||
"view.zoomOut": "Pomniejsz",
|
||||
"window.bringAllToFront": "Przenieś wszystkie okna na wierzch",
|
||||
"window.close": "Zamknij",
|
||||
"window.front": "Przenieś wszystkie okna na wierzch",
|
||||
"window.minimize": "Zminimalizuj",
|
||||
"window.title": "Okno",
|
||||
"window.toggleFullscreen": "Przełącz tryb pełnoekranowy",
|
||||
"window.zoom": "Powiększenie"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Adicionar",
|
||||
"back": "Voltar",
|
||||
"cancel": "Cancelar",
|
||||
"close": "Fechar",
|
||||
"confirm": "Confirmar",
|
||||
"delete": "Excluir",
|
||||
"edit": "Editar",
|
||||
"more": "Mais",
|
||||
"next": "Próximo",
|
||||
"ok": "OK",
|
||||
"previous": "Anterior",
|
||||
"refresh": "Atualizar",
|
||||
"remove": "Remover",
|
||||
"retry": "Tentar novamente",
|
||||
"save": "Salvar",
|
||||
"search": "Pesquisar",
|
||||
"submit": "Enviar"
|
||||
},
|
||||
"app": {
|
||||
"description": "Sua plataforma de colaboração com assistente de IA",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Erro",
|
||||
"info": "Informação",
|
||||
"loading": "Carregando",
|
||||
"success": "Sucesso",
|
||||
"warning": "Aviso"
|
||||
}
|
||||
}
|
||||
"actions.add": "Adicionar",
|
||||
"actions.back": "Voltar",
|
||||
"actions.cancel": "Cancelar",
|
||||
"actions.close": "Fechar",
|
||||
"actions.confirm": "Confirmar",
|
||||
"actions.delete": "Excluir",
|
||||
"actions.edit": "Editar",
|
||||
"actions.more": "Mais",
|
||||
"actions.next": "Próximo",
|
||||
"actions.ok": "OK",
|
||||
"actions.previous": "Anterior",
|
||||
"actions.refresh": "Atualizar",
|
||||
"actions.remove": "Remover",
|
||||
"actions.retry": "Tentar novamente",
|
||||
"actions.save": "Salvar",
|
||||
"actions.search": "Pesquisar",
|
||||
"actions.submit": "Enviar",
|
||||
"app.description": "Sua plataforma de colaboração com assistente de IA",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Erro",
|
||||
"status.info": "Informação",
|
||||
"status.loading": "Carregando",
|
||||
"status.success": "Sucesso",
|
||||
"status.warning": "Aviso"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "Confirmar",
|
||||
"detail": "Um aplicativo de chat baseado em um grande modelo de linguagem",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "Sobre"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Cancelar",
|
||||
"no": "Não",
|
||||
"title": "Confirmar",
|
||||
"yes": "Sim"
|
||||
},
|
||||
"error": {
|
||||
"button": "Confirmar",
|
||||
"detail": "Ocorreu um erro durante a operação, por favor tente novamente mais tarde",
|
||||
"message": "Ocorreu um erro",
|
||||
"title": "Erro"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Baixar e instalar",
|
||||
"downloadComplete": "Download completo",
|
||||
"downloadCompleteMessage": "O pacote de atualização foi baixado com sucesso, deseja instalá-lo agora?",
|
||||
"installLater": "Instalar depois",
|
||||
"installNow": "Instalar agora",
|
||||
"later": "Lembrar mais tarde",
|
||||
"newVersion": "Nova versão disponível",
|
||||
"newVersionAvailable": "Nova versão encontrada: {{version}}",
|
||||
"skipThisVersion": "Ignorar esta versão"
|
||||
}
|
||||
}
|
||||
"about.button": "Confirmar",
|
||||
"about.detail": "Um aplicativo de chat baseado em um grande modelo de linguagem",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "Sobre",
|
||||
"confirm.cancel": "Cancelar",
|
||||
"confirm.no": "Não",
|
||||
"confirm.title": "Confirmar",
|
||||
"confirm.yes": "Sim",
|
||||
"error.button": "Confirmar",
|
||||
"error.detail": "Ocorreu um erro durante a operação, por favor tente novamente mais tarde",
|
||||
"error.message": "Ocorreu um erro",
|
||||
"error.title": "Erro",
|
||||
"update.downloadAndInstall": "Baixar e instalar",
|
||||
"update.downloadComplete": "Download completo",
|
||||
"update.downloadCompleteMessage": "O pacote de atualização foi baixado com sucesso, deseja instalá-lo agora?",
|
||||
"update.installLater": "Instalar depois",
|
||||
"update.installNow": "Instalar agora",
|
||||
"update.later": "Lembrar mais tarde",
|
||||
"update.newVersion": "Nova versão disponível",
|
||||
"update.newVersionAvailable": "Nova versão encontrada: {{version}}",
|
||||
"update.skipThisVersion": "Ignorar esta versão"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Verificando atualizações..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Painel do Desenvolvedor",
|
||||
"devTools": "Ferramentas do Desenvolvedor",
|
||||
"forceReload": "Recarregar Forçadamente",
|
||||
"openStore": "Abrir arquivo de armazenamento",
|
||||
"refreshMenu": "Atualizar menu",
|
||||
"reload": "Recarregar",
|
||||
"title": "Desenvolvimento"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Copiar",
|
||||
"cut": "Cortar",
|
||||
"delete": "Excluir",
|
||||
"paste": "Colar",
|
||||
"redo": "Refazer",
|
||||
"selectAll": "Selecionar Tudo",
|
||||
"speech": "Fala",
|
||||
"startSpeaking": "Começar a Ler",
|
||||
"stopSpeaking": "Parar de Ler",
|
||||
"title": "Edição",
|
||||
"undo": "Desfazer"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Preferências",
|
||||
"quit": "Sair",
|
||||
"title": "Arquivo"
|
||||
},
|
||||
"help": {
|
||||
"about": "Sobre",
|
||||
"githubRepo": "Repositório do GitHub",
|
||||
"reportIssue": "Reportar Problema",
|
||||
"title": "Ajuda",
|
||||
"visitWebsite": "Visitar o Site"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "Sobre {{appName}}",
|
||||
"devTools": "Ferramentas do Desenvolvedor LobeHub",
|
||||
"hide": "Ocultar {{appName}}",
|
||||
"hideOthers": "Ocultar Outros",
|
||||
"preferences": "Configurações...",
|
||||
"services": "Serviços",
|
||||
"unhide": "Mostrar Todos"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Abrir {{appName}}",
|
||||
"quit": "Sair",
|
||||
"show": "Mostrar {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Recarregar Forçadamente",
|
||||
"reload": "Recarregar",
|
||||
"resetZoom": "Redefinir Zoom",
|
||||
"title": "Visualização",
|
||||
"toggleFullscreen": "Alternar Tela Cheia",
|
||||
"zoomIn": "Aumentar",
|
||||
"zoomOut": "Diminuir"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Trazer Todas as Janelas para Frente",
|
||||
"close": "Fechar",
|
||||
"front": "Trazer Todas as Janelas para Frente",
|
||||
"minimize": "Minimizar",
|
||||
"title": "Janela",
|
||||
"toggleFullscreen": "Alternar Tela Cheia",
|
||||
"zoom": "Zoom"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Verificando atualizações...",
|
||||
"dev.devPanel": "Painel do Desenvolvedor",
|
||||
"dev.devTools": "Ferramentas do Desenvolvedor",
|
||||
"dev.forceReload": "Recarregar Forçadamente",
|
||||
"dev.openStore": "Abrir arquivo de armazenamento",
|
||||
"dev.refreshMenu": "Atualizar menu",
|
||||
"dev.reload": "Recarregar",
|
||||
"dev.title": "Desenvolvimento",
|
||||
"edit.copy": "Copiar",
|
||||
"edit.cut": "Cortar",
|
||||
"edit.delete": "Excluir",
|
||||
"edit.paste": "Colar",
|
||||
"edit.redo": "Refazer",
|
||||
"edit.selectAll": "Selecionar Tudo",
|
||||
"edit.speech": "Fala",
|
||||
"edit.startSpeaking": "Começar a Ler",
|
||||
"edit.stopSpeaking": "Parar de Ler",
|
||||
"edit.title": "Edição",
|
||||
"edit.undo": "Desfazer",
|
||||
"file.preferences": "Preferências",
|
||||
"file.quit": "Sair",
|
||||
"file.title": "Arquivo",
|
||||
"help.about": "Sobre",
|
||||
"help.githubRepo": "Repositório do GitHub",
|
||||
"help.reportIssue": "Reportar Problema",
|
||||
"help.title": "Ajuda",
|
||||
"help.visitWebsite": "Visitar o Site",
|
||||
"macOS.about": "Sobre {{appName}}",
|
||||
"macOS.devTools": "Ferramentas do Desenvolvedor LobeHub",
|
||||
"macOS.hide": "Ocultar {{appName}}",
|
||||
"macOS.hideOthers": "Ocultar Outros",
|
||||
"macOS.preferences": "Configurações...",
|
||||
"macOS.services": "Serviços",
|
||||
"macOS.unhide": "Mostrar Todos",
|
||||
"tray.open": "Abrir {{appName}}",
|
||||
"tray.quit": "Sair",
|
||||
"tray.show": "Mostrar {{appName}}",
|
||||
"view.forceReload": "Recarregar Forçadamente",
|
||||
"view.reload": "Recarregar",
|
||||
"view.resetZoom": "Redefinir Zoom",
|
||||
"view.title": "Visualização",
|
||||
"view.toggleFullscreen": "Alternar Tela Cheia",
|
||||
"view.zoomIn": "Aumentar",
|
||||
"view.zoomOut": "Diminuir",
|
||||
"window.bringAllToFront": "Trazer Todas as Janelas para Frente",
|
||||
"window.close": "Fechar",
|
||||
"window.front": "Trazer Todas as Janelas para Frente",
|
||||
"window.minimize": "Minimizar",
|
||||
"window.title": "Janela",
|
||||
"window.toggleFullscreen": "Alternar Tela Cheia",
|
||||
"window.zoom": "Zoom"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Добавить",
|
||||
"back": "Назад",
|
||||
"cancel": "Отмена",
|
||||
"close": "Закрыть",
|
||||
"confirm": "Подтвердить",
|
||||
"delete": "Удалить",
|
||||
"edit": "Редактировать",
|
||||
"more": "Больше",
|
||||
"next": "Далее",
|
||||
"ok": "ОК",
|
||||
"previous": "Назад",
|
||||
"refresh": "Обновить",
|
||||
"remove": "Удалить",
|
||||
"retry": "Повторить",
|
||||
"save": "Сохранить",
|
||||
"search": "Поиск",
|
||||
"submit": "Отправить"
|
||||
},
|
||||
"app": {
|
||||
"description": "Ваша платформа для совместной работы с ИИ",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Ошибка",
|
||||
"info": "Информация",
|
||||
"loading": "Загрузка",
|
||||
"success": "Успех",
|
||||
"warning": "Предупреждение"
|
||||
}
|
||||
}
|
||||
"actions.add": "Добавить",
|
||||
"actions.back": "Назад",
|
||||
"actions.cancel": "Отмена",
|
||||
"actions.close": "Закрыть",
|
||||
"actions.confirm": "Подтвердить",
|
||||
"actions.delete": "Удалить",
|
||||
"actions.edit": "Редактировать",
|
||||
"actions.more": "Больше",
|
||||
"actions.next": "Далее",
|
||||
"actions.ok": "ОК",
|
||||
"actions.previous": "Назад",
|
||||
"actions.refresh": "Обновить",
|
||||
"actions.remove": "Удалить",
|
||||
"actions.retry": "Повторить",
|
||||
"actions.save": "Сохранить",
|
||||
"actions.search": "Поиск",
|
||||
"actions.submit": "Отправить",
|
||||
"app.description": "Ваша платформа для совместной работы с ИИ",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Ошибка",
|
||||
"status.info": "Информация",
|
||||
"status.loading": "Загрузка",
|
||||
"status.success": "Успех",
|
||||
"status.warning": "Предупреждение"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "Подтвердить",
|
||||
"detail": "Приложение для чата на основе большой языковой модели",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "О приложении"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Отмена",
|
||||
"no": "Нет",
|
||||
"title": "Подтверждение",
|
||||
"yes": "Да"
|
||||
},
|
||||
"error": {
|
||||
"button": "Подтвердить",
|
||||
"detail": "Произошла ошибка во время операции, пожалуйста, попробуйте позже",
|
||||
"message": "Произошла ошибка",
|
||||
"title": "Ошибка"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Скачать и установить",
|
||||
"downloadComplete": "Скачивание завершено",
|
||||
"downloadCompleteMessage": "Обновление загружено, хотите установить сейчас?",
|
||||
"installLater": "Установить позже",
|
||||
"installNow": "Установить сейчас",
|
||||
"later": "Напомнить позже",
|
||||
"newVersion": "Обнаружена новая версия",
|
||||
"newVersionAvailable": "Обнаружена новая версия: {{version}}",
|
||||
"skipThisVersion": "Пропустить эту версию"
|
||||
}
|
||||
}
|
||||
"about.button": "Подтвердить",
|
||||
"about.detail": "Приложение для чата на основе большой языковой модели",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "О приложении",
|
||||
"confirm.cancel": "Отмена",
|
||||
"confirm.no": "Нет",
|
||||
"confirm.title": "Подтверждение",
|
||||
"confirm.yes": "Да",
|
||||
"error.button": "Подтвердить",
|
||||
"error.detail": "Произошла ошибка во время операции, пожалуйста, попробуйте позже",
|
||||
"error.message": "Произошла ошибка",
|
||||
"error.title": "Ошибка",
|
||||
"update.downloadAndInstall": "Скачать и установить",
|
||||
"update.downloadComplete": "Скачивание завершено",
|
||||
"update.downloadCompleteMessage": "Обновление загружено, хотите установить сейчас?",
|
||||
"update.installLater": "Установить позже",
|
||||
"update.installNow": "Установить сейчас",
|
||||
"update.later": "Напомнить позже",
|
||||
"update.newVersion": "Обнаружена новая версия",
|
||||
"update.newVersionAvailable": "Обнаружена новая версия: {{version}}",
|
||||
"update.skipThisVersion": "Пропустить эту версию"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Проверка обновлений..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Панель разработчика",
|
||||
"devTools": "Инструменты разработчика",
|
||||
"forceReload": "Принудительная перезагрузка",
|
||||
"openStore": "Открыть файл хранилища",
|
||||
"refreshMenu": "Обновить меню",
|
||||
"reload": "Перезагрузить",
|
||||
"title": "Разработка"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Копировать",
|
||||
"cut": "Вырезать",
|
||||
"delete": "Удалить",
|
||||
"paste": "Вставить",
|
||||
"redo": "Повторить",
|
||||
"selectAll": "Выбрать все",
|
||||
"speech": "Речь",
|
||||
"startSpeaking": "Начать чтение",
|
||||
"stopSpeaking": "Остановить чтение",
|
||||
"title": "Редактирование",
|
||||
"undo": "Отменить"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Настройки",
|
||||
"quit": "Выйти",
|
||||
"title": "Файл"
|
||||
},
|
||||
"help": {
|
||||
"about": "О программе",
|
||||
"githubRepo": "Репозиторий GitHub",
|
||||
"reportIssue": "Сообщить о проблеме",
|
||||
"title": "Помощь",
|
||||
"visitWebsite": "Посетить сайт"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "О {{appName}}",
|
||||
"devTools": "Инструменты разработчика LobeHub",
|
||||
"hide": "Скрыть {{appName}}",
|
||||
"hideOthers": "Скрыть другие",
|
||||
"preferences": "Настройки...",
|
||||
"services": "Сервисы",
|
||||
"unhide": "Показать все"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Открыть {{appName}}",
|
||||
"quit": "Выйти",
|
||||
"show": "Показать {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Принудительная перезагрузка",
|
||||
"reload": "Перезагрузить",
|
||||
"resetZoom": "Сбросить масштаб",
|
||||
"title": "Вид",
|
||||
"toggleFullscreen": "Переключить полноэкранный режим",
|
||||
"zoomIn": "Увеличить",
|
||||
"zoomOut": "Уменьшить"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Вывести все окна на передний план",
|
||||
"close": "Закрыть",
|
||||
"front": "Вывести все окна на передний план",
|
||||
"minimize": "Свернуть",
|
||||
"title": "Окно",
|
||||
"toggleFullscreen": "Переключить полноэкранный режим",
|
||||
"zoom": "Масштаб"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Проверка обновлений...",
|
||||
"dev.devPanel": "Панель разработчика",
|
||||
"dev.devTools": "Инструменты разработчика",
|
||||
"dev.forceReload": "Принудительная перезагрузка",
|
||||
"dev.openStore": "Открыть файл хранилища",
|
||||
"dev.refreshMenu": "Обновить меню",
|
||||
"dev.reload": "Перезагрузить",
|
||||
"dev.title": "Разработка",
|
||||
"edit.copy": "Копировать",
|
||||
"edit.cut": "Вырезать",
|
||||
"edit.delete": "Удалить",
|
||||
"edit.paste": "Вставить",
|
||||
"edit.redo": "Повторить",
|
||||
"edit.selectAll": "Выбрать все",
|
||||
"edit.speech": "Речь",
|
||||
"edit.startSpeaking": "Начать чтение",
|
||||
"edit.stopSpeaking": "Остановить чтение",
|
||||
"edit.title": "Редактирование",
|
||||
"edit.undo": "Отменить",
|
||||
"file.preferences": "Настройки",
|
||||
"file.quit": "Выйти",
|
||||
"file.title": "Файл",
|
||||
"help.about": "О программе",
|
||||
"help.githubRepo": "Репозиторий GitHub",
|
||||
"help.reportIssue": "Сообщить о проблеме",
|
||||
"help.title": "Помощь",
|
||||
"help.visitWebsite": "Посетить сайт",
|
||||
"macOS.about": "О {{appName}}",
|
||||
"macOS.devTools": "Инструменты разработчика LobeHub",
|
||||
"macOS.hide": "Скрыть {{appName}}",
|
||||
"macOS.hideOthers": "Скрыть другие",
|
||||
"macOS.preferences": "Настройки...",
|
||||
"macOS.services": "Сервисы",
|
||||
"macOS.unhide": "Показать все",
|
||||
"tray.open": "Открыть {{appName}}",
|
||||
"tray.quit": "Выйти",
|
||||
"tray.show": "Показать {{appName}}",
|
||||
"view.forceReload": "Принудительная перезагрузка",
|
||||
"view.reload": "Перезагрузить",
|
||||
"view.resetZoom": "Сбросить масштаб",
|
||||
"view.title": "Вид",
|
||||
"view.toggleFullscreen": "Переключить полноэкранный режим",
|
||||
"view.zoomIn": "Увеличить",
|
||||
"view.zoomOut": "Уменьшить",
|
||||
"window.bringAllToFront": "Вывести все окна на передний план",
|
||||
"window.close": "Закрыть",
|
||||
"window.front": "Вывести все окна на передний план",
|
||||
"window.minimize": "Свернуть",
|
||||
"window.title": "Окно",
|
||||
"window.toggleFullscreen": "Переключить полноэкранный режим",
|
||||
"window.zoom": "Масштаб"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Ekle",
|
||||
"back": "Geri",
|
||||
"cancel": "İptal",
|
||||
"close": "Kapat",
|
||||
"confirm": "Onayla",
|
||||
"delete": "Sil",
|
||||
"edit": "Düzenle",
|
||||
"more": "Daha Fazla",
|
||||
"next": "Sonraki",
|
||||
"ok": "Tamam",
|
||||
"previous": "Önceki",
|
||||
"refresh": "Yenile",
|
||||
"remove": "Kaldır",
|
||||
"retry": "Yeniden Dene",
|
||||
"save": "Kaydet",
|
||||
"search": "Ara",
|
||||
"submit": "Gönder"
|
||||
},
|
||||
"app": {
|
||||
"description": "AI asistanınız için işbirliği platformu",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Hata",
|
||||
"info": "Bilgi",
|
||||
"loading": "Yükleniyor",
|
||||
"success": "Başarılı",
|
||||
"warning": "Uyarı"
|
||||
}
|
||||
}
|
||||
"actions.add": "Ekle",
|
||||
"actions.back": "Geri",
|
||||
"actions.cancel": "İptal",
|
||||
"actions.close": "Kapat",
|
||||
"actions.confirm": "Onayla",
|
||||
"actions.delete": "Sil",
|
||||
"actions.edit": "Düzenle",
|
||||
"actions.more": "Daha Fazla",
|
||||
"actions.next": "Sonraki",
|
||||
"actions.ok": "Tamam",
|
||||
"actions.previous": "Önceki",
|
||||
"actions.refresh": "Yenile",
|
||||
"actions.remove": "Kaldır",
|
||||
"actions.retry": "Yeniden Dene",
|
||||
"actions.save": "Kaydet",
|
||||
"actions.search": "Ara",
|
||||
"actions.submit": "Gönder",
|
||||
"app.description": "AI asistanınız için işbirliği platformu",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Hata",
|
||||
"status.info": "Bilgi",
|
||||
"status.loading": "Yükleniyor",
|
||||
"status.success": "Başarılı",
|
||||
"status.warning": "Uyarı"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "Tamam",
|
||||
"detail": "Büyük dil modeli tabanlı bir sohbet uygulaması",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "Hakkında"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "İptal",
|
||||
"no": "Hayır",
|
||||
"title": "Onay",
|
||||
"yes": "Evet"
|
||||
},
|
||||
"error": {
|
||||
"button": "Tamam",
|
||||
"detail": "İşlem sırasında bir hata oluştu, lütfen daha sonra tekrar deneyin",
|
||||
"message": "Hata oluştu",
|
||||
"title": "Hata"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "İndir ve Yükle",
|
||||
"downloadComplete": "İndirme tamamlandı",
|
||||
"downloadCompleteMessage": "Güncelleme paketi indirildi, hemen yüklemek ister misiniz?",
|
||||
"installLater": "Sonra yükle",
|
||||
"installNow": "Şimdi yükle",
|
||||
"later": "Sonra hatırlat",
|
||||
"newVersion": "Yeni sürüm bulundu",
|
||||
"newVersionAvailable": "Yeni sürüm bulundu: {{version}}",
|
||||
"skipThisVersion": "Bu sürümü atla"
|
||||
}
|
||||
}
|
||||
"about.button": "Tamam",
|
||||
"about.detail": "Büyük dil modeli tabanlı bir sohbet uygulaması",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "Hakkında",
|
||||
"confirm.cancel": "İptal",
|
||||
"confirm.no": "Hayır",
|
||||
"confirm.title": "Onay",
|
||||
"confirm.yes": "Evet",
|
||||
"error.button": "Tamam",
|
||||
"error.detail": "İşlem sırasında bir hata oluştu, lütfen daha sonra tekrar deneyin",
|
||||
"error.message": "Hata oluştu",
|
||||
"error.title": "Hata",
|
||||
"update.downloadAndInstall": "İndir ve Yükle",
|
||||
"update.downloadComplete": "İndirme tamamlandı",
|
||||
"update.downloadCompleteMessage": "Güncelleme paketi indirildi, hemen yüklemek ister misiniz?",
|
||||
"update.installLater": "Sonra yükle",
|
||||
"update.installNow": "Şimdi yükle",
|
||||
"update.later": "Sonra hatırlat",
|
||||
"update.newVersion": "Yeni sürüm bulundu",
|
||||
"update.newVersionAvailable": "Yeni sürüm bulundu: {{version}}",
|
||||
"update.skipThisVersion": "Bu sürümü atla"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Güncellemeleri kontrol et..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Geliştirici Paneli",
|
||||
"devTools": "Geliştirici Araçları",
|
||||
"forceReload": "Zorla Yenile",
|
||||
"openStore": "Depolama dosyasını aç",
|
||||
"refreshMenu": "Menüyü yenile",
|
||||
"reload": "Yenile",
|
||||
"title": "Geliştir"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Kopyala",
|
||||
"cut": "Kes",
|
||||
"delete": "Sil",
|
||||
"paste": "Yapıştır",
|
||||
"redo": "Yinele",
|
||||
"selectAll": "Tümünü Seç",
|
||||
"speech": "Ses",
|
||||
"startSpeaking": "Okumaya Başla",
|
||||
"stopSpeaking": "Okumayı Durdur",
|
||||
"title": "Düzenle",
|
||||
"undo": "Geri Al"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Tercihler",
|
||||
"quit": "Çık",
|
||||
"title": "Dosya"
|
||||
},
|
||||
"help": {
|
||||
"about": "Hakkında",
|
||||
"githubRepo": "GitHub Deposu",
|
||||
"reportIssue": "Sorun Bildir",
|
||||
"title": "Yardım",
|
||||
"visitWebsite": "Resmi Web Sitesini Ziyaret Et"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "{{appName}} Hakkında",
|
||||
"devTools": "LobeHub Geliştirici Araçları",
|
||||
"hide": "{{appName}}'i Gizle",
|
||||
"hideOthers": "Diğerlerini Gizle",
|
||||
"preferences": "Tercihler...",
|
||||
"services": "Hizmetler",
|
||||
"unhide": "Hepsini Göster"
|
||||
},
|
||||
"tray": {
|
||||
"open": "{{appName}}'i Aç",
|
||||
"quit": "Çık",
|
||||
"show": "{{appName}}'i Göster"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Zorla Yenile",
|
||||
"reload": "Yenile",
|
||||
"resetZoom": "Yakınlaştırmayı Sıfırla",
|
||||
"title": "Görünüm",
|
||||
"toggleFullscreen": "Tam Ekrana Geç",
|
||||
"zoomIn": "Büyüt",
|
||||
"zoomOut": "Küçült"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Tüm Pencereleri Öne Getir",
|
||||
"close": "Kapat",
|
||||
"front": "Tüm Pencereleri Öne Getir",
|
||||
"minimize": "Küçült",
|
||||
"title": "Pencere",
|
||||
"toggleFullscreen": "Tam Ekrana Geç",
|
||||
"zoom": "Yakınlaştır"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Güncellemeleri kontrol et...",
|
||||
"dev.devPanel": "Geliştirici Paneli",
|
||||
"dev.devTools": "Geliştirici Araçları",
|
||||
"dev.forceReload": "Zorla Yenile",
|
||||
"dev.openStore": "Depolama dosyasını aç",
|
||||
"dev.refreshMenu": "Menüyü yenile",
|
||||
"dev.reload": "Yenile",
|
||||
"dev.title": "Geliştir",
|
||||
"edit.copy": "Kopyala",
|
||||
"edit.cut": "Kes",
|
||||
"edit.delete": "Sil",
|
||||
"edit.paste": "Yapıştır",
|
||||
"edit.redo": "Yinele",
|
||||
"edit.selectAll": "Tümünü Seç",
|
||||
"edit.speech": "Ses",
|
||||
"edit.startSpeaking": "Okumaya Başla",
|
||||
"edit.stopSpeaking": "Okumayı Durdur",
|
||||
"edit.title": "Düzenle",
|
||||
"edit.undo": "Geri Al",
|
||||
"file.preferences": "Tercihler",
|
||||
"file.quit": "Çık",
|
||||
"file.title": "Dosya",
|
||||
"help.about": "Hakkında",
|
||||
"help.githubRepo": "GitHub Deposu",
|
||||
"help.reportIssue": "Sorun Bildir",
|
||||
"help.title": "Yardım",
|
||||
"help.visitWebsite": "Resmi Web Sitesini Ziyaret Et",
|
||||
"macOS.about": "{{appName}} Hakkında",
|
||||
"macOS.devTools": "LobeHub Geliştirici Araçları",
|
||||
"macOS.hide": "{{appName}}'i Gizle",
|
||||
"macOS.hideOthers": "Diğerlerini Gizle",
|
||||
"macOS.preferences": "Tercihler...",
|
||||
"macOS.services": "Hizmetler",
|
||||
"macOS.unhide": "Hepsini Göster",
|
||||
"tray.open": "{{appName}}'i Aç",
|
||||
"tray.quit": "Çık",
|
||||
"tray.show": "{{appName}}'i Göster",
|
||||
"view.forceReload": "Zorla Yenile",
|
||||
"view.reload": "Yenile",
|
||||
"view.resetZoom": "Yakınlaştırmayı Sıfırla",
|
||||
"view.title": "Görünüm",
|
||||
"view.toggleFullscreen": "Tam Ekrana Geç",
|
||||
"view.zoomIn": "Büyüt",
|
||||
"view.zoomOut": "Küçült",
|
||||
"window.bringAllToFront": "Tüm Pencereleri Öne Getir",
|
||||
"window.close": "Kapat",
|
||||
"window.front": "Tüm Pencereleri Öne Getir",
|
||||
"window.minimize": "Küçült",
|
||||
"window.title": "Pencere",
|
||||
"window.toggleFullscreen": "Tam Ekrana Geç",
|
||||
"window.zoom": "Yakınlaştır"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "Thêm",
|
||||
"back": "Quay lại",
|
||||
"cancel": "Hủy",
|
||||
"close": "Đóng",
|
||||
"confirm": "Xác nhận",
|
||||
"delete": "Xóa",
|
||||
"edit": "Chỉnh sửa",
|
||||
"more": "Thêm nữa",
|
||||
"next": "Tiếp theo",
|
||||
"ok": "Đồng ý",
|
||||
"previous": "Quay lại",
|
||||
"refresh": "Tải lại",
|
||||
"remove": "Gỡ bỏ",
|
||||
"retry": "Thử lại",
|
||||
"save": "Lưu",
|
||||
"search": "Tìm kiếm",
|
||||
"submit": "Gửi"
|
||||
},
|
||||
"app": {
|
||||
"description": "Nền tảng hợp tác trợ lý AI của bạn",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "Lỗi",
|
||||
"info": "Thông tin",
|
||||
"loading": "Đang tải",
|
||||
"success": "Thành công",
|
||||
"warning": "Cảnh báo"
|
||||
}
|
||||
}
|
||||
"actions.add": "Thêm",
|
||||
"actions.back": "Quay lại",
|
||||
"actions.cancel": "Hủy",
|
||||
"actions.close": "Đóng",
|
||||
"actions.confirm": "Xác nhận",
|
||||
"actions.delete": "Xóa",
|
||||
"actions.edit": "Chỉnh sửa",
|
||||
"actions.more": "Thêm nữa",
|
||||
"actions.next": "Tiếp theo",
|
||||
"actions.ok": "Đồng ý",
|
||||
"actions.previous": "Quay lại",
|
||||
"actions.refresh": "Tải lại",
|
||||
"actions.remove": "Gỡ bỏ",
|
||||
"actions.retry": "Thử lại",
|
||||
"actions.save": "Lưu",
|
||||
"actions.search": "Tìm kiếm",
|
||||
"actions.submit": "Gửi",
|
||||
"app.description": "Nền tảng hợp tác trợ lý AI của bạn",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "Lỗi",
|
||||
"status.info": "Thông tin",
|
||||
"status.loading": "Đang tải",
|
||||
"status.success": "Thành công",
|
||||
"status.warning": "Cảnh báo"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "Xác nhận",
|
||||
"detail": "Một ứng dụng trò chuyện dựa trên mô hình ngôn ngữ lớn",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "Về"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "Hủy",
|
||||
"no": "Không",
|
||||
"title": "Xác nhận",
|
||||
"yes": "Có"
|
||||
},
|
||||
"error": {
|
||||
"button": "Xác nhận",
|
||||
"detail": "Đã xảy ra lỗi trong quá trình thực hiện, vui lòng thử lại sau",
|
||||
"message": "Đã xảy ra lỗi",
|
||||
"title": "Lỗi"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "Tải xuống và cài đặt",
|
||||
"downloadComplete": "Tải xuống hoàn tất",
|
||||
"downloadCompleteMessage": "Gói cập nhật đã tải xuống hoàn tất, có muốn cài đặt ngay không?",
|
||||
"installLater": "Cài đặt sau",
|
||||
"installNow": "Cài đặt ngay",
|
||||
"later": "Nhắc nhở sau",
|
||||
"newVersion": "Phát hiện phiên bản mới",
|
||||
"newVersionAvailable": "Phát hiện phiên bản mới: {{version}}",
|
||||
"skipThisVersion": "Bỏ qua phiên bản này"
|
||||
}
|
||||
}
|
||||
"about.button": "Xác nhận",
|
||||
"about.detail": "Một ứng dụng trò chuyện dựa trên mô hình ngôn ngữ lớn",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "Về",
|
||||
"confirm.cancel": "Hủy",
|
||||
"confirm.no": "Không",
|
||||
"confirm.title": "Xác nhận",
|
||||
"confirm.yes": "Có",
|
||||
"error.button": "Xác nhận",
|
||||
"error.detail": "Đã xảy ra lỗi trong quá trình thực hiện, vui lòng thử lại sau",
|
||||
"error.message": "Đã xảy ra lỗi",
|
||||
"error.title": "Lỗi",
|
||||
"update.downloadAndInstall": "Tải xuống và cài đặt",
|
||||
"update.downloadComplete": "Tải xuống hoàn tất",
|
||||
"update.downloadCompleteMessage": "Gói cập nhật đã tải xuống hoàn tất, có muốn cài đặt ngay không?",
|
||||
"update.installLater": "Cài đặt sau",
|
||||
"update.installNow": "Cài đặt ngay",
|
||||
"update.later": "Nhắc nhở sau",
|
||||
"update.newVersion": "Phát hiện phiên bản mới",
|
||||
"update.newVersionAvailable": "Phát hiện phiên bản mới: {{version}}",
|
||||
"update.skipThisVersion": "Bỏ qua phiên bản này"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "Kiểm tra cập nhật..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "Bảng điều khiển nhà phát triển",
|
||||
"devTools": "Công cụ phát triển",
|
||||
"forceReload": "Tải lại cưỡng bức",
|
||||
"openStore": "Mở tệp lưu trữ",
|
||||
"refreshMenu": "Làm mới menu",
|
||||
"reload": "Tải lại",
|
||||
"title": "Phát triển"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "Sao chép",
|
||||
"cut": "Cắt",
|
||||
"delete": "Xóa",
|
||||
"paste": "Dán",
|
||||
"redo": "Làm lại",
|
||||
"selectAll": "Chọn tất cả",
|
||||
"speech": "Giọng nói",
|
||||
"startSpeaking": "Bắt đầu đọc",
|
||||
"stopSpeaking": "Dừng đọc",
|
||||
"title": "Chỉnh sửa",
|
||||
"undo": "Hoàn tác"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "Tùy chọn",
|
||||
"quit": "Thoát",
|
||||
"title": "Tập tin"
|
||||
},
|
||||
"help": {
|
||||
"about": "Về",
|
||||
"githubRepo": "Kho lưu trữ GitHub",
|
||||
"reportIssue": "Báo cáo sự cố",
|
||||
"title": "Trợ giúp",
|
||||
"visitWebsite": "Truy cập trang web"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "Về {{appName}}",
|
||||
"devTools": "Công cụ phát triển LobeHub",
|
||||
"hide": "Ẩn {{appName}}",
|
||||
"hideOthers": "Ẩn khác",
|
||||
"preferences": "Cài đặt ưu tiên...",
|
||||
"services": "Dịch vụ",
|
||||
"unhide": "Hiện tất cả"
|
||||
},
|
||||
"tray": {
|
||||
"open": "Mở {{appName}}",
|
||||
"quit": "Thoát",
|
||||
"show": "Hiện {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "Tải lại cưỡng bức",
|
||||
"reload": "Tải lại",
|
||||
"resetZoom": "Đặt lại thu phóng",
|
||||
"title": "Xem",
|
||||
"toggleFullscreen": "Chuyển đổi toàn màn hình",
|
||||
"zoomIn": "Phóng to",
|
||||
"zoomOut": "Thu nhỏ"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "Đưa tất cả cửa sổ lên trước",
|
||||
"close": "Đóng",
|
||||
"front": "Đưa tất cả cửa sổ lên trước",
|
||||
"minimize": "Thu nhỏ",
|
||||
"title": "Cửa sổ",
|
||||
"toggleFullscreen": "Chuyển đổi toàn màn hình",
|
||||
"zoom": "Thu phóng"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "Kiểm tra cập nhật...",
|
||||
"dev.devPanel": "Bảng điều khiển nhà phát triển",
|
||||
"dev.devTools": "Công cụ phát triển",
|
||||
"dev.forceReload": "Tải lại cưỡng bức",
|
||||
"dev.openStore": "Mở tệp lưu trữ",
|
||||
"dev.refreshMenu": "Làm mới menu",
|
||||
"dev.reload": "Tải lại",
|
||||
"dev.title": "Phát triển",
|
||||
"edit.copy": "Sao chép",
|
||||
"edit.cut": "Cắt",
|
||||
"edit.delete": "Xóa",
|
||||
"edit.paste": "Dán",
|
||||
"edit.redo": "Làm lại",
|
||||
"edit.selectAll": "Chọn tất cả",
|
||||
"edit.speech": "Giọng nói",
|
||||
"edit.startSpeaking": "Bắt đầu đọc",
|
||||
"edit.stopSpeaking": "Dừng đọc",
|
||||
"edit.title": "Chỉnh sửa",
|
||||
"edit.undo": "Hoàn tác",
|
||||
"file.preferences": "Tùy chọn",
|
||||
"file.quit": "Thoát",
|
||||
"file.title": "Tập tin",
|
||||
"help.about": "Về",
|
||||
"help.githubRepo": "Kho lưu trữ GitHub",
|
||||
"help.reportIssue": "Báo cáo sự cố",
|
||||
"help.title": "Trợ giúp",
|
||||
"help.visitWebsite": "Truy cập trang web",
|
||||
"macOS.about": "Về {{appName}}",
|
||||
"macOS.devTools": "Công cụ phát triển LobeHub",
|
||||
"macOS.hide": "Ẩn {{appName}}",
|
||||
"macOS.hideOthers": "Ẩn khác",
|
||||
"macOS.preferences": "Cài đặt ưu tiên...",
|
||||
"macOS.services": "Dịch vụ",
|
||||
"macOS.unhide": "Hiện tất cả",
|
||||
"tray.open": "Mở {{appName}}",
|
||||
"tray.quit": "Thoát",
|
||||
"tray.show": "Hiện {{appName}}",
|
||||
"view.forceReload": "Tải lại cưỡng bức",
|
||||
"view.reload": "Tải lại",
|
||||
"view.resetZoom": "Đặt lại thu phóng",
|
||||
"view.title": "Xem",
|
||||
"view.toggleFullscreen": "Chuyển đổi toàn màn hình",
|
||||
"view.zoomIn": "Phóng to",
|
||||
"view.zoomOut": "Thu nhỏ",
|
||||
"window.bringAllToFront": "Đưa tất cả cửa sổ lên trước",
|
||||
"window.close": "Đóng",
|
||||
"window.front": "Đưa tất cả cửa sổ lên trước",
|
||||
"window.minimize": "Thu nhỏ",
|
||||
"window.title": "Cửa sổ",
|
||||
"window.toggleFullscreen": "Chuyển đổi toàn màn hình",
|
||||
"window.zoom": "Thu phóng"
|
||||
}
|
||||
@@ -1,32 +1,92 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "添加",
|
||||
"back": "返回",
|
||||
"cancel": "取消",
|
||||
"close": "关闭",
|
||||
"confirm": "确认",
|
||||
"delete": "删除",
|
||||
"edit": "编辑",
|
||||
"more": "更多",
|
||||
"next": "下一步",
|
||||
"ok": "确定",
|
||||
"previous": "上一步",
|
||||
"refresh": "刷新",
|
||||
"remove": "移除",
|
||||
"retry": "重试",
|
||||
"save": "保存",
|
||||
"search": "搜索",
|
||||
"submit": "提交"
|
||||
},
|
||||
"app": {
|
||||
"description": "你的 AI 助手协作平台",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "错误",
|
||||
"info": "信息",
|
||||
"loading": "加载中",
|
||||
"success": "成功",
|
||||
"warning": "警告"
|
||||
}
|
||||
}
|
||||
"actions.add": "添加",
|
||||
"actions.back": "返回",
|
||||
"actions.cancel": "取消",
|
||||
"actions.close": "关闭",
|
||||
"actions.confirm": "确认",
|
||||
"actions.delete": "删除",
|
||||
"actions.edit": "编辑",
|
||||
"actions.more": "更多",
|
||||
"actions.next": "下一步",
|
||||
"actions.ok": "确定",
|
||||
"actions.previous": "上一步",
|
||||
"actions.refresh": "刷新",
|
||||
"actions.remove": "移除",
|
||||
"actions.retry": "重试",
|
||||
"actions.save": "保存",
|
||||
"actions.search": "搜索",
|
||||
"actions.submit": "提交",
|
||||
"app.description": "你的 AI 助手协作平台",
|
||||
"app.name": "LobeHub",
|
||||
"notification.finishChatGeneration": "AI 消息已生成完毕",
|
||||
"proxy.auth": "需要认证",
|
||||
"proxy.authDesc": "如果代理服务器需要用户名和密码",
|
||||
"proxy.authSettings": "认证设置",
|
||||
"proxy.basicSettings": "代理设置",
|
||||
"proxy.basicSettingsDesc": "配置代理服务器的连接参数",
|
||||
"proxy.bypass": "不使用代理的地址",
|
||||
"proxy.connectionTest": "连接测试",
|
||||
"proxy.enable": "启用代理",
|
||||
"proxy.enableDesc": "开启后将通过代理服务器访问网络",
|
||||
"proxy.password": "密码",
|
||||
"proxy.password_placeholder": "请输入密码",
|
||||
"proxy.port": "端口",
|
||||
"proxy.resetButton": "重置",
|
||||
"proxy.saveButton": "保存",
|
||||
"proxy.saveFailed": "保存失败:{{error}}",
|
||||
"proxy.saveSuccess": "代理设置保存成功",
|
||||
"proxy.server": "服务器地址",
|
||||
"proxy.testButton": "测试连接",
|
||||
"proxy.testDescription": "使用当前代理配置测试连接,验证配置是否正常工作",
|
||||
"proxy.testFailed": "连接失败",
|
||||
"proxy.testSuccessWithTime": "测试连接成功,耗时 {{time}} ms",
|
||||
"proxy.testUrl": "测试地址",
|
||||
"proxy.testUrlPlaceholder": "请输入要测试的 URL",
|
||||
"proxy.testing": "正在测试连接…",
|
||||
"proxy.type": "代理类型",
|
||||
"proxy.unsavedChanges": "你有未保存的更改",
|
||||
"proxy.username": "用户名",
|
||||
"proxy.username_placeholder": "请输入用户名",
|
||||
"proxy.validation.passwordRequired": "启用认证时密码为必填项",
|
||||
"proxy.validation.portInvalid": "端口必须是 1 到 65535 之间的数字",
|
||||
"proxy.validation.portRequired": "启用代理时端口为必填项",
|
||||
"proxy.validation.serverInvalid": "请输入有效的服务器地址(IP 或域名)",
|
||||
"proxy.validation.serverRequired": "启用代理时服务器地址为必填项",
|
||||
"proxy.validation.typeRequired": "启用代理时代理类型为必填项",
|
||||
"proxy.validation.usernameRequired": "启用认证时用户名为必填项",
|
||||
"remoteServer.authError": "授权失败: {{error}}",
|
||||
"remoteServer.authPending": "请在浏览器中完成授权",
|
||||
"remoteServer.configDesc": "连接到远程 LobeHub 服务器,启用数据同步",
|
||||
"remoteServer.configError": "配置出错",
|
||||
"remoteServer.configTitle": "配置云同步",
|
||||
"remoteServer.connect": "连接并授权",
|
||||
"remoteServer.connected": "已连接",
|
||||
"remoteServer.disconnect": "断开连接",
|
||||
"remoteServer.disconnectError": "断开连接失败",
|
||||
"remoteServer.disconnected": "未连接",
|
||||
"remoteServer.fetchError": "获取配置失败",
|
||||
"remoteServer.invalidUrl": "请输入有效的URL地址",
|
||||
"remoteServer.serverUrl": "服务器地址",
|
||||
"remoteServer.statusConnected": "已连接",
|
||||
"remoteServer.statusDisconnected": "未连接",
|
||||
"remoteServer.urlRequired": "请输入服务器地址",
|
||||
"status.error": "错误",
|
||||
"status.info": "信息",
|
||||
"status.loading": "加载中",
|
||||
"status.success": "成功",
|
||||
"status.warning": "警告",
|
||||
"sync.continue": "继续",
|
||||
"sync.inCloud": "当前使用云端同步",
|
||||
"sync.inLocalStorage": "当前使用本地存储",
|
||||
"sync.isIniting": "正在初始化…",
|
||||
"sync.lobehubCloud.description": "官方提供的云版本",
|
||||
"sync.lobehubCloud.title": "LobeHub Cloud",
|
||||
"sync.local.description": "使用本地数据库,完全离线可用",
|
||||
"sync.local.title": "本地数据库",
|
||||
"sync.mode.cloudSync": "云端同步",
|
||||
"sync.mode.localStorage": "本地存储",
|
||||
"sync.mode.title": "选择你的连接模式",
|
||||
"sync.mode.useSelfHosted": "使用自托管实例?",
|
||||
"sync.selfHosted.description": "自行部署的社区版本",
|
||||
"sync.selfHosted.title": "自托管实例"
|
||||
}
|
||||
@@ -1,31 +1,44 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "确定",
|
||||
"detail": "一个基于大语言模型的聊天应用",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "关于"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "取消",
|
||||
"no": "否",
|
||||
"title": "确认",
|
||||
"yes": "是"
|
||||
},
|
||||
"error": {
|
||||
"button": "确定",
|
||||
"detail": "操作过程中发生错误,请稍后重试",
|
||||
"message": "发生错误",
|
||||
"title": "错误"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "下载并安装",
|
||||
"downloadComplete": "下载完成",
|
||||
"downloadCompleteMessage": "更新包已下载完成,是否立即安装?",
|
||||
"installLater": "稍后安装",
|
||||
"installNow": "立即安装",
|
||||
"later": "稍后提醒",
|
||||
"newVersion": "发现新版本",
|
||||
"newVersionAvailable": "发现新版本: {{version}}",
|
||||
"skipThisVersion": "跳过此版本"
|
||||
}
|
||||
}
|
||||
"about.button": "确定",
|
||||
"about.detail": "一个基于大语言模型的聊天应用",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "关于",
|
||||
"confirm.cancel": "取消",
|
||||
"confirm.no": "取消",
|
||||
"confirm.title": "请确认",
|
||||
"confirm.yes": "继续",
|
||||
"error.button": "确定",
|
||||
"error.detail": "操作未完成。你可以重试,或稍后再试。",
|
||||
"error.message": "发生错误",
|
||||
"error.title": "错误",
|
||||
"update.checkingUpdate": "检查新版本",
|
||||
"update.checkingUpdateDesc": "正在获取版本信息…",
|
||||
"update.downloadAndInstall": "下载并安装",
|
||||
"update.downloadComplete": "下载完成",
|
||||
"update.downloadCompleteMessage": "已下载更新。现在安装吗?",
|
||||
"update.downloadNewVersion": "下载新版本",
|
||||
"update.downloadingUpdate": "正在下载更新",
|
||||
"update.downloadingUpdateDesc": "更新正在下载中,请稍候…",
|
||||
"update.installLater": "稍后安装",
|
||||
"update.installNow": "立即安装",
|
||||
"update.isLatestVersion": "当前已是最新版本",
|
||||
"update.isLatestVersionDesc": "当前版本({{version}})已是最新。",
|
||||
"update.later": "稍后提醒",
|
||||
"update.newVersion": "发现新版本",
|
||||
"update.newVersionAvailable": "发现新版本:{{version}}",
|
||||
"update.newVersionAvailableDesc": "发现新版本 {{version}},是否立即下载?",
|
||||
"update.restartAndInstall": "安装更新并重启",
|
||||
"update.skipThisVersion": "跳过此版本",
|
||||
"update.updateError": "更新错误",
|
||||
"update.updateReady": "有新版本可用",
|
||||
"update.updateReadyDesc": "新版本 {{version}} 已下载完成,重启应用后即可完成安装。",
|
||||
"update.upgradeNow": "立即更新",
|
||||
"update.willInstallLater": "更新将在下次启动时安装",
|
||||
"waitingOAuth.cancel": "取消",
|
||||
"waitingOAuth.description": "浏览器已打开授权页面,请在浏览器中完成授权",
|
||||
"waitingOAuth.error": "授权失败: {{error}}",
|
||||
"waitingOAuth.errorTitle": "授权连接失败",
|
||||
"waitingOAuth.helpText": "如果浏览器没有自动打开,请点击取消后重新尝试",
|
||||
"waitingOAuth.retry": "重试",
|
||||
"waitingOAuth.title": "等待授权连接"
|
||||
}
|
||||
@@ -1,71 +1,62 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "检查更新..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "开发者面板",
|
||||
"devTools": "开发者工具",
|
||||
"forceReload": "强制重新加载",
|
||||
"openStore": "打开存储文件",
|
||||
"refreshMenu": "刷新菜单",
|
||||
"reload": "重新加载",
|
||||
"title": "开发"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "复制",
|
||||
"cut": "剪切",
|
||||
"delete": "删除",
|
||||
"paste": "粘贴",
|
||||
"redo": "重做",
|
||||
"selectAll": "全选",
|
||||
"speech": "语音",
|
||||
"startSpeaking": "开始朗读",
|
||||
"stopSpeaking": "停止朗读",
|
||||
"title": "编辑",
|
||||
"undo": "撤销"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "首选项",
|
||||
"quit": "退出",
|
||||
"title": "文件"
|
||||
},
|
||||
"help": {
|
||||
"about": "关于",
|
||||
"githubRepo": "GitHub 仓库",
|
||||
"reportIssue": "报告问题",
|
||||
"title": "帮助",
|
||||
"visitWebsite": "访问官网"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "关于 {{appName}}",
|
||||
"devTools": "LobeHub 开发者工具",
|
||||
"hide": "隐藏 {{appName}}",
|
||||
"hideOthers": "隐藏其他",
|
||||
"preferences": "偏好设置...",
|
||||
"services": "服务",
|
||||
"unhide": "全部显示"
|
||||
},
|
||||
"tray": {
|
||||
"open": "打开 {{appName}}",
|
||||
"quit": "退出",
|
||||
"show": "显示 {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "强制重新加载",
|
||||
"reload": "重新加载",
|
||||
"resetZoom": "重置缩放",
|
||||
"title": "视图",
|
||||
"toggleFullscreen": "切换全屏",
|
||||
"zoomIn": "放大",
|
||||
"zoomOut": "缩小"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "前置所有窗口",
|
||||
"close": "关闭",
|
||||
"front": "前置所有窗口",
|
||||
"minimize": "最小化",
|
||||
"title": "窗口",
|
||||
"toggleFullscreen": "切换全屏",
|
||||
"zoom": "缩放"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "检查更新…",
|
||||
"dev.devPanel": "开发者面板",
|
||||
"dev.devTools": "开发者工具",
|
||||
"dev.forceReload": "强制重新加载",
|
||||
"dev.openSettingsFile": "打开 Settings 配置文件",
|
||||
"dev.openStore": "打开本地数据目录",
|
||||
"dev.openUpdaterCacheDir": "更新缓存目录",
|
||||
"dev.openUserDataDir": "用户配置目录",
|
||||
"dev.refreshMenu": "刷新菜单",
|
||||
"dev.reload": "重新加载",
|
||||
"dev.simulateAutoDownload": "模拟启动后台自动下载更新(3s 下完)",
|
||||
"dev.simulateDownloadComplete": "模拟下载完成",
|
||||
"dev.simulateDownloadProgress": "模拟下载进度",
|
||||
"dev.title": "开发",
|
||||
"dev.updaterSimulation": "自动更新测试模拟",
|
||||
"edit.copy": "复制",
|
||||
"edit.cut": "剪切",
|
||||
"edit.delete": "删除",
|
||||
"edit.paste": "粘贴",
|
||||
"edit.redo": "重做",
|
||||
"edit.selectAll": "全选",
|
||||
"edit.speech": "语音",
|
||||
"edit.startSpeaking": "开始朗读",
|
||||
"edit.stopSpeaking": "停止朗读",
|
||||
"edit.title": "编辑",
|
||||
"edit.undo": "撤销",
|
||||
"file.preferences": "设置…",
|
||||
"file.quit": "退出",
|
||||
"file.title": "文件",
|
||||
"help.about": "关于",
|
||||
"help.githubRepo": "GitHub 仓库",
|
||||
"help.openConfigDir": "配置目录",
|
||||
"help.openLogsDir": "打开日志目录",
|
||||
"help.reportIssue": "反馈问题",
|
||||
"help.title": "帮助",
|
||||
"help.visitWebsite": "打开官网",
|
||||
"macOS.about": "关于 {{appName}}",
|
||||
"macOS.devTools": "LobeHub 开发者工具",
|
||||
"macOS.hide": "隐藏 {{appName}}",
|
||||
"macOS.hideOthers": "隐藏其他",
|
||||
"macOS.preferences": "偏好设置…",
|
||||
"macOS.services": "服务",
|
||||
"macOS.unhide": "全部显示",
|
||||
"tray.open": "打开 {{appName}}",
|
||||
"tray.quit": "退出",
|
||||
"tray.show": "显示 {{appName}}",
|
||||
"view.forceReload": "强制重新加载",
|
||||
"view.reload": "重新加载",
|
||||
"view.resetZoom": "重置缩放",
|
||||
"view.title": "视图",
|
||||
"view.toggleFullscreen": "切换全屏",
|
||||
"view.zoomIn": "放大",
|
||||
"view.zoomOut": "缩小",
|
||||
"window.bringAllToFront": "前置所有窗口",
|
||||
"window.close": "关闭",
|
||||
"window.front": "前置所有窗口",
|
||||
"window.minimize": "最小化",
|
||||
"window.title": "窗口",
|
||||
"window.toggleFullscreen": "切换全屏",
|
||||
"window.zoom": "缩放"
|
||||
}
|
||||
@@ -1,32 +1,26 @@
|
||||
{
|
||||
"actions": {
|
||||
"add": "新增",
|
||||
"back": "返回",
|
||||
"cancel": "取消",
|
||||
"close": "關閉",
|
||||
"confirm": "確認",
|
||||
"delete": "刪除",
|
||||
"edit": "編輯",
|
||||
"more": "更多",
|
||||
"next": "下一步",
|
||||
"ok": "確定",
|
||||
"previous": "上一步",
|
||||
"refresh": "刷新",
|
||||
"remove": "移除",
|
||||
"retry": "重試",
|
||||
"save": "儲存",
|
||||
"search": "搜尋",
|
||||
"submit": "提交"
|
||||
},
|
||||
"app": {
|
||||
"description": "你的 AI 助手協作平台",
|
||||
"name": "LobeHub"
|
||||
},
|
||||
"status": {
|
||||
"error": "錯誤",
|
||||
"info": "資訊",
|
||||
"loading": "載入中",
|
||||
"success": "成功",
|
||||
"warning": "警告"
|
||||
}
|
||||
}
|
||||
"actions.add": "新增",
|
||||
"actions.back": "返回",
|
||||
"actions.cancel": "取消",
|
||||
"actions.close": "關閉",
|
||||
"actions.confirm": "確認",
|
||||
"actions.delete": "刪除",
|
||||
"actions.edit": "編輯",
|
||||
"actions.more": "更多",
|
||||
"actions.next": "下一步",
|
||||
"actions.ok": "確定",
|
||||
"actions.previous": "上一步",
|
||||
"actions.refresh": "刷新",
|
||||
"actions.remove": "移除",
|
||||
"actions.retry": "重試",
|
||||
"actions.save": "儲存",
|
||||
"actions.search": "搜尋",
|
||||
"actions.submit": "提交",
|
||||
"app.description": "你的 AI 助手協作平台",
|
||||
"app.name": "LobeHub",
|
||||
"status.error": "錯誤",
|
||||
"status.info": "資訊",
|
||||
"status.loading": "載入中",
|
||||
"status.success": "成功",
|
||||
"status.warning": "警告"
|
||||
}
|
||||
@@ -1,31 +1,23 @@
|
||||
{
|
||||
"about": {
|
||||
"button": "確定",
|
||||
"detail": "一個基於大語言模型的聊天應用",
|
||||
"message": "{{appName}} {{appVersion}}",
|
||||
"title": "關於"
|
||||
},
|
||||
"confirm": {
|
||||
"cancel": "取消",
|
||||
"no": "否",
|
||||
"title": "確認",
|
||||
"yes": "是"
|
||||
},
|
||||
"error": {
|
||||
"button": "確定",
|
||||
"detail": "操作過程中發生錯誤,請稍後重試",
|
||||
"message": "發生錯誤",
|
||||
"title": "錯誤"
|
||||
},
|
||||
"update": {
|
||||
"downloadAndInstall": "下載並安裝",
|
||||
"downloadComplete": "下載完成",
|
||||
"downloadCompleteMessage": "更新包已下載完成,是否立即安裝?",
|
||||
"installLater": "稍後安裝",
|
||||
"installNow": "立即安裝",
|
||||
"later": "稍後提醒",
|
||||
"newVersion": "發現新版本",
|
||||
"newVersionAvailable": "發現新版本: {{version}}",
|
||||
"skipThisVersion": "跳過此版本"
|
||||
}
|
||||
}
|
||||
"about.button": "確定",
|
||||
"about.detail": "一個基於大語言模型的聊天應用",
|
||||
"about.message": "{{appName}} {{appVersion}}",
|
||||
"about.title": "關於",
|
||||
"confirm.cancel": "取消",
|
||||
"confirm.no": "否",
|
||||
"confirm.title": "確認",
|
||||
"confirm.yes": "是",
|
||||
"error.button": "確定",
|
||||
"error.detail": "操作過程中發生錯誤,請稍後重試",
|
||||
"error.message": "發生錯誤",
|
||||
"error.title": "錯誤",
|
||||
"update.downloadAndInstall": "下載並安裝",
|
||||
"update.downloadComplete": "下載完成",
|
||||
"update.downloadCompleteMessage": "更新包已下載完成,是否立即安裝?",
|
||||
"update.installLater": "稍後安裝",
|
||||
"update.installNow": "立即安裝",
|
||||
"update.later": "稍後提醒",
|
||||
"update.newVersion": "發現新版本",
|
||||
"update.newVersionAvailable": "發現新版本: {{version}}",
|
||||
"update.skipThisVersion": "跳過此版本"
|
||||
}
|
||||
@@ -1,71 +1,53 @@
|
||||
{
|
||||
"common": {
|
||||
"checkUpdates": "檢查更新..."
|
||||
},
|
||||
"dev": {
|
||||
"devPanel": "開發者面板",
|
||||
"devTools": "開發者工具",
|
||||
"forceReload": "強制重新載入",
|
||||
"openStore": "打開儲存檔案",
|
||||
"refreshMenu": "刷新選單",
|
||||
"reload": "重新載入",
|
||||
"title": "開發"
|
||||
},
|
||||
"edit": {
|
||||
"copy": "複製",
|
||||
"cut": "剪下",
|
||||
"delete": "刪除",
|
||||
"paste": "貼上",
|
||||
"redo": "重做",
|
||||
"selectAll": "全選",
|
||||
"speech": "語音",
|
||||
"startSpeaking": "開始朗讀",
|
||||
"stopSpeaking": "停止朗讀",
|
||||
"title": "編輯",
|
||||
"undo": "撤銷"
|
||||
},
|
||||
"file": {
|
||||
"preferences": "偏好設定",
|
||||
"quit": "退出",
|
||||
"title": "檔案"
|
||||
},
|
||||
"help": {
|
||||
"about": "關於",
|
||||
"githubRepo": "GitHub 倉庫",
|
||||
"reportIssue": "報告問題",
|
||||
"title": "幫助",
|
||||
"visitWebsite": "訪問網站"
|
||||
},
|
||||
"macOS": {
|
||||
"about": "關於 {{appName}}",
|
||||
"devTools": "LobeHub 開發者工具",
|
||||
"hide": "隱藏 {{appName}}",
|
||||
"hideOthers": "隱藏其他",
|
||||
"preferences": "偏好設定...",
|
||||
"services": "服務",
|
||||
"unhide": "全部顯示"
|
||||
},
|
||||
"tray": {
|
||||
"open": "打開 {{appName}}",
|
||||
"quit": "退出",
|
||||
"show": "顯示 {{appName}}"
|
||||
},
|
||||
"view": {
|
||||
"forceReload": "強制重新載入",
|
||||
"reload": "重新載入",
|
||||
"resetZoom": "重置縮放",
|
||||
"title": "視圖",
|
||||
"toggleFullscreen": "切換全螢幕",
|
||||
"zoomIn": "放大",
|
||||
"zoomOut": "縮小"
|
||||
},
|
||||
"window": {
|
||||
"bringAllToFront": "前置所有視窗",
|
||||
"close": "關閉",
|
||||
"front": "前置所有視窗",
|
||||
"minimize": "最小化",
|
||||
"title": "視窗",
|
||||
"toggleFullscreen": "切換全螢幕",
|
||||
"zoom": "縮放"
|
||||
}
|
||||
}
|
||||
"common.checkUpdates": "檢查更新...",
|
||||
"dev.devPanel": "開發者面板",
|
||||
"dev.devTools": "開發者工具",
|
||||
"dev.forceReload": "強制重新載入",
|
||||
"dev.openStore": "打開儲存檔案",
|
||||
"dev.refreshMenu": "刷新選單",
|
||||
"dev.reload": "重新載入",
|
||||
"dev.title": "開發",
|
||||
"edit.copy": "複製",
|
||||
"edit.cut": "剪下",
|
||||
"edit.delete": "刪除",
|
||||
"edit.paste": "貼上",
|
||||
"edit.redo": "重做",
|
||||
"edit.selectAll": "全選",
|
||||
"edit.speech": "語音",
|
||||
"edit.startSpeaking": "開始朗讀",
|
||||
"edit.stopSpeaking": "停止朗讀",
|
||||
"edit.title": "編輯",
|
||||
"edit.undo": "撤銷",
|
||||
"file.preferences": "偏好設定",
|
||||
"file.quit": "退出",
|
||||
"file.title": "檔案",
|
||||
"help.about": "關於",
|
||||
"help.githubRepo": "GitHub 倉庫",
|
||||
"help.reportIssue": "報告問題",
|
||||
"help.title": "幫助",
|
||||
"help.visitWebsite": "訪問網站",
|
||||
"macOS.about": "關於 {{appName}}",
|
||||
"macOS.devTools": "LobeHub 開發者工具",
|
||||
"macOS.hide": "隱藏 {{appName}}",
|
||||
"macOS.hideOthers": "隱藏其他",
|
||||
"macOS.preferences": "偏好設定...",
|
||||
"macOS.services": "服務",
|
||||
"macOS.unhide": "全部顯示",
|
||||
"tray.open": "打開 {{appName}}",
|
||||
"tray.quit": "退出",
|
||||
"tray.show": "顯示 {{appName}}",
|
||||
"view.forceReload": "強制重新載入",
|
||||
"view.reload": "重新載入",
|
||||
"view.resetZoom": "重置縮放",
|
||||
"view.title": "視圖",
|
||||
"view.toggleFullscreen": "切換全螢幕",
|
||||
"view.zoomIn": "放大",
|
||||
"view.zoomOut": "縮小",
|
||||
"window.bringAllToFront": "前置所有視窗",
|
||||
"window.close": "關閉",
|
||||
"window.front": "前置所有視窗",
|
||||
"window.minimize": "最小化",
|
||||
"window.title": "視窗",
|
||||
"window.toggleFullscreen": "切換全螢幕",
|
||||
"window.zoom": "縮放"
|
||||
}
|
||||
@@ -1,7 +1,7 @@
|
||||
import { consola } from 'consola';
|
||||
import { colors } from 'consola/utils';
|
||||
import { unset } from 'es-toolkit/compat';
|
||||
import { diff } from 'just-diff';
|
||||
import { unset } from 'lodash';
|
||||
import { existsSync } from 'node:fs';
|
||||
|
||||
import {
|
||||
@@ -34,7 +34,7 @@ export const genDiff = () => {
|
||||
continue;
|
||||
}
|
||||
|
||||
const clearLocals = [];
|
||||
const clearLocals: string[] = [];
|
||||
|
||||
for (const locale of [i18nConfig.entryLocale, ...i18nConfig.outputLocales]) {
|
||||
const localeFilepath = outputLocaleJsonFilepath(locale, `${ns}.json`);
|
||||
|
||||
@@ -1,18 +1,18 @@
|
||||
import type { BrowserWindowOpts } from './core/browser/Browser';
|
||||
|
||||
export const BrowsersIdentifiers = {
|
||||
chat: 'chat',
|
||||
app: 'app',
|
||||
devtools: 'devtools',
|
||||
};
|
||||
|
||||
export const appBrowsers = {
|
||||
chat: {
|
||||
app: {
|
||||
autoHideMenuBar: true,
|
||||
height: 800,
|
||||
identifier: 'chat',
|
||||
identifier: 'app',
|
||||
keepAlive: true,
|
||||
minWidth: 400,
|
||||
path: '/agent',
|
||||
path: '/',
|
||||
showOnInit: true,
|
||||
titleBarStyle: 'hidden',
|
||||
vibrancy: 'under-window',
|
||||
@@ -25,7 +25,7 @@ export const appBrowsers = {
|
||||
identifier: 'devtools',
|
||||
maximizable: false,
|
||||
minWidth: 400,
|
||||
parentIdentifier: 'chat',
|
||||
parentIdentifier: 'app',
|
||||
path: '/desktop/devtools',
|
||||
titleBarStyle: 'hiddenInset',
|
||||
vibrancy: 'under-window',
|
||||
@@ -76,7 +76,7 @@ export const windowTemplates = {
|
||||
height: 600,
|
||||
keepAlive: false, // Multi-instance windows don't need to stay alive
|
||||
minWidth: 400,
|
||||
parentIdentifier: 'chat',
|
||||
parentIdentifier: 'app',
|
||||
titleBarStyle: 'hidden',
|
||||
vibrancy: 'under-window',
|
||||
width: 900,
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
import { dev, linux, macOS, windows } from 'electron-is';
|
||||
import os from 'node:os';
|
||||
|
||||
import { getDesktopEnv } from '@/env';
|
||||
|
||||
export const isDev = dev();
|
||||
|
||||
export const OFFICIAL_CLOUD_SERVER = process.env.OFFICIAL_CLOUD_SERVER || 'https://lobechat.com';
|
||||
export const OFFICIAL_CLOUD_SERVER = getDesktopEnv().OFFICIAL_CLOUD_SERVER;
|
||||
|
||||
export const isMac = macOS();
|
||||
export const isWindows = windows();
|
||||
|
||||
@@ -1,4 +1,9 @@
|
||||
import { InterceptRouteParams, OpenSettingsWindowOptions } from '@lobechat/electron-client-ipc';
|
||||
import type {
|
||||
InterceptRouteParams,
|
||||
OpenSettingsWindowOptions,
|
||||
WindowResizableParams,
|
||||
WindowSizeParams,
|
||||
} from '@lobechat/electron-client-ipc';
|
||||
import { findMatchingRoute } from '~common/routes';
|
||||
|
||||
import { AppBrowsersIdentifiers, WindowTemplateIdentifiers } from '@/appBrowsers';
|
||||
@@ -25,22 +30,17 @@ export default class BrowserWindowsCtr extends ControllerModule {
|
||||
console.log('[BrowserWindowsCtr] Received request to open settings', normalizedOptions);
|
||||
|
||||
try {
|
||||
const query = new URLSearchParams();
|
||||
if (normalizedOptions.searchParams) {
|
||||
Object.entries(normalizedOptions.searchParams).forEach(([key, value]) => {
|
||||
if (value !== undefined) query.set(key, value);
|
||||
});
|
||||
}
|
||||
let fullPath: string;
|
||||
|
||||
const tab = normalizedOptions.tab;
|
||||
if (tab && tab !== 'common' && !query.has('active')) {
|
||||
query.set('active', tab);
|
||||
// If direct path is provided, use it directly
|
||||
if (normalizedOptions.path) {
|
||||
fullPath = normalizedOptions.path;
|
||||
} else {
|
||||
// Legacy support for tab and searchParams
|
||||
const tab = normalizedOptions.tab;
|
||||
fullPath = tab ? `/settings/${tab}` : '/settings/common';
|
||||
}
|
||||
|
||||
const queryString = query.toString();
|
||||
const subPath = tab && !queryString ? `/${tab}` : '';
|
||||
const fullPath = `/settings${subPath}${queryString ? `?${queryString}` : ''}`;
|
||||
|
||||
const mainWindow = this.app.browserManager.getMainWindow();
|
||||
mainWindow.show();
|
||||
mainWindow.broadcast('navigate', { path: fullPath });
|
||||
@@ -73,6 +73,20 @@ export default class BrowserWindowsCtr extends ControllerModule {
|
||||
});
|
||||
}
|
||||
|
||||
@IpcMethod()
|
||||
setWindowSize(params: WindowSizeParams) {
|
||||
this.withSenderIdentifier((identifier) => {
|
||||
this.app.browserManager.setWindowSize(identifier, params);
|
||||
});
|
||||
}
|
||||
|
||||
@IpcMethod()
|
||||
setWindowResizable(params: WindowResizableParams) {
|
||||
this.withSenderIdentifier((identifier) => {
|
||||
this.app.browserManager.setWindowResizable(identifier, params.resizable);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle route interception requests
|
||||
* Responsible for handling route interception requests from the renderer process
|
||||
|
||||
@@ -138,7 +138,7 @@ export default class McpInstallController extends ControllerModule {
|
||||
|
||||
// 通过应用实例广播到前端
|
||||
if (this.app?.browserManager) {
|
||||
this.app.browserManager.broadcastToWindow('chat', 'mcpInstallRequest', installRequest);
|
||||
this.app.browserManager.broadcastToWindow('app', 'mcpInstallRequest', installRequest);
|
||||
logger.debug(`🔧 [McpInstall] Install request broadcasted successfully`);
|
||||
return true;
|
||||
} else {
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
import { NetworkProxySettings } from '@lobechat/electron-client-ipc';
|
||||
import { merge } from 'lodash';
|
||||
import { isEqual } from 'lodash-es';
|
||||
import { isEqual, merge } from 'es-toolkit/compat';
|
||||
|
||||
import { defaultProxySettings } from '@/const/store';
|
||||
import { createLogger } from '@/utils/logger';
|
||||
|
||||
@@ -83,13 +83,13 @@ describe('BrowserWindowsCtr', () => {
|
||||
});
|
||||
|
||||
describe('openSettingsWindow', () => {
|
||||
it('should navigate to settings in main window with the specified tab', async () => {
|
||||
const tab = 'appearance';
|
||||
const result = await browserWindowsCtr.openSettingsWindow(tab);
|
||||
it('should navigate to settings in main window with the specified path', async () => {
|
||||
const path = '/settings/common';
|
||||
const result = await browserWindowsCtr.openSettingsWindow({ path });
|
||||
expect(mockGetMainWindow).toHaveBeenCalled();
|
||||
expect(mockShow).toHaveBeenCalled();
|
||||
expect(mockBroadcast).toHaveBeenCalledWith('navigate', {
|
||||
path: '/settings?active=appearance',
|
||||
path: '/settings/common',
|
||||
});
|
||||
expect(result).toEqual({ success: true });
|
||||
});
|
||||
@@ -99,7 +99,7 @@ describe('BrowserWindowsCtr', () => {
|
||||
mockBroadcast.mockImplementationOnce(() => {
|
||||
throw new Error(errorMessage);
|
||||
});
|
||||
const result = await browserWindowsCtr.openSettingsWindow('display');
|
||||
const result = await browserWindowsCtr.openSettingsWindow({ path: '/settings/common' });
|
||||
expect(result).toEqual({ error: errorMessage, success: false });
|
||||
});
|
||||
});
|
||||
|
||||
@@ -84,7 +84,7 @@ describe('McpInstallController', () => {
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockBrowserManager.broadcastToWindow).toHaveBeenCalledWith(
|
||||
'chat',
|
||||
'app',
|
||||
'mcpInstallRequest',
|
||||
{
|
||||
marketId: 'lobehub',
|
||||
@@ -143,7 +143,7 @@ describe('McpInstallController', () => {
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockBrowserManager.broadcastToWindow).toHaveBeenCalledWith(
|
||||
'chat',
|
||||
'app',
|
||||
'mcpInstallRequest',
|
||||
{
|
||||
marketId: 'third-party',
|
||||
@@ -162,7 +162,7 @@ describe('McpInstallController', () => {
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockBrowserManager.broadcastToWindow).toHaveBeenCalledWith(
|
||||
'chat',
|
||||
'app',
|
||||
'mcpInstallRequest',
|
||||
{
|
||||
marketId: 'third-party',
|
||||
@@ -235,7 +235,7 @@ describe('McpInstallController', () => {
|
||||
|
||||
expect(result).toBe(true);
|
||||
expect(mockBrowserManager.broadcastToWindow).toHaveBeenCalledWith(
|
||||
'chat',
|
||||
'app',
|
||||
'mcpInstallRequest',
|
||||
expect.objectContaining({
|
||||
schema: schemaWithOptionalFields,
|
||||
|
||||
@@ -7,6 +7,7 @@ import {
|
||||
} from '@lobechat/desktop-bridge';
|
||||
import { ElectronIPCEventHandler, ElectronIPCServer } from '@lobechat/electron-server-ipc';
|
||||
import { app, protocol, session } from 'electron';
|
||||
import installExtension, { REACT_DEVELOPER_TOOLS } from 'electron-devtools-installer';
|
||||
import { macOS, windows } from 'electron-is';
|
||||
import { pathExistsSync } from 'fs-extra';
|
||||
import os from 'node:os';
|
||||
@@ -17,6 +18,7 @@ import { buildDir, nextExportDir } from '@/const/dir';
|
||||
import { isDev } from '@/const/env';
|
||||
import { ELECTRON_BE_PROTOCOL_SCHEME } from '@/const/protocol';
|
||||
import { IControlModule } from '@/controllers';
|
||||
import { getDesktopEnv } from '@/env';
|
||||
import { IServiceModule } from '@/services';
|
||||
import { getServerMethodMetadata } from '@/utils/ipc';
|
||||
import { createLogger } from '@/utils/logger';
|
||||
@@ -62,8 +64,7 @@ export class App {
|
||||
/**
|
||||
* Escape hatch: allow testing static renderer in dev via env
|
||||
*/
|
||||
private readonly rendererStaticOverride =
|
||||
process.env.DESKTOP_RENDERER_STATIC === '1' || process.env.DESKTOP_RENDERER_STATIC === 'true';
|
||||
private readonly rendererStaticOverride = getDesktopEnv().DESKTOP_RENDERER_STATIC;
|
||||
|
||||
/**
|
||||
* whether app is in quiting
|
||||
@@ -96,8 +97,6 @@ export class App {
|
||||
this.storeManager = new StoreManager(this);
|
||||
|
||||
this.rendererProtocolManager = new RendererProtocolManager({
|
||||
getExportMimeType: this.getExportMimeType.bind(this),
|
||||
|
||||
nextExportDir,
|
||||
resolveRendererFilePath: this.resolveRendererFilePath.bind(this),
|
||||
});
|
||||
@@ -279,6 +278,8 @@ export class App {
|
||||
await app.whenReady();
|
||||
logger.debug('Application ready');
|
||||
|
||||
await this.installReactDevtools();
|
||||
|
||||
this.controllers.forEach((controller) => {
|
||||
if (typeof controller.afterAppReady === 'function') {
|
||||
try {
|
||||
@@ -292,6 +293,21 @@ export class App {
|
||||
logger.info('Application ready state completed');
|
||||
};
|
||||
|
||||
/**
|
||||
* Development only: install React DevTools extension into Electron's devtools.
|
||||
*/
|
||||
private installReactDevtools = async () => {
|
||||
if (!isDev) return;
|
||||
|
||||
try {
|
||||
const name = await installExtension(REACT_DEVELOPER_TOOLS);
|
||||
|
||||
logger.info(`Installed DevTools extension: ${name}`);
|
||||
} catch (error) {
|
||||
logger.warn('Failed to install React DevTools extension', error);
|
||||
}
|
||||
};
|
||||
|
||||
// ============= helper ============= //
|
||||
|
||||
/**
|
||||
@@ -378,30 +394,6 @@ export class App {
|
||||
return null;
|
||||
}
|
||||
|
||||
private getExportMimeType(filePath: string) {
|
||||
const ext = extname(filePath).toLowerCase();
|
||||
|
||||
const map: Record<string, string> = {
|
||||
'.css': 'text/css; charset=utf-8',
|
||||
'.gif': 'image/gif',
|
||||
'.html': 'text/html; charset=utf-8',
|
||||
'.ico': 'image/x-icon',
|
||||
'.jpeg': 'image/jpeg',
|
||||
'.jpg': 'image/jpeg',
|
||||
'.js': 'application/javascript; charset=utf-8',
|
||||
'.json': 'application/json; charset=utf-8',
|
||||
'.map': 'application/json; charset=utf-8',
|
||||
'.png': 'image/png',
|
||||
'.svg': 'image/svg+xml; charset=utf-8',
|
||||
'.txt': 'text/plain; charset=utf-8',
|
||||
'.webp': 'image/webp',
|
||||
'.woff': 'font/woff',
|
||||
'.woff2': 'font/woff2',
|
||||
};
|
||||
|
||||
return map[ext];
|
||||
}
|
||||
|
||||
/**
|
||||
* Configure renderer loading strategy for dev/prod
|
||||
*/
|
||||
|
||||
@@ -42,6 +42,13 @@ vi.mock('electron', () => ({
|
||||
},
|
||||
}));
|
||||
|
||||
// electron-devtools-installer accesses electron.app.getPath at import-time in node env;
|
||||
// mock it to avoid side effects in unit tests
|
||||
vi.mock('electron-devtools-installer', () => ({
|
||||
REACT_DEVELOPER_TOOLS: 'REACT_DEVELOPER_TOOLS',
|
||||
default: vi.fn(),
|
||||
}));
|
||||
|
||||
vi.mock('fs-extra', () => ({
|
||||
pathExistsSync: (...args: any[]) => mockPathExistsSync(...args),
|
||||
}));
|
||||
|
||||
@@ -2,7 +2,6 @@ import { MainBroadcastEventKey, MainBroadcastParams } from '@lobechat/electron-c
|
||||
import {
|
||||
BrowserWindow,
|
||||
BrowserWindowConstructorOptions,
|
||||
type Session,
|
||||
session as electronSession,
|
||||
ipcMain,
|
||||
nativeTheme,
|
||||
@@ -23,14 +22,13 @@ import {
|
||||
TITLE_BAR_HEIGHT,
|
||||
} from '@/const/theme';
|
||||
import RemoteServerConfigCtr from '@/controllers/RemoteServerConfigCtr';
|
||||
import { backendProxyProtocolManager } from '@/core/infrastructure/BackendProxyProtocolManager';
|
||||
import { createLogger } from '@/utils/logger';
|
||||
|
||||
import type { App } from '../App';
|
||||
|
||||
// Create logger
|
||||
const logger = createLogger('core:Browser');
|
||||
// Track sessions that already have protocol handlers installed to avoid duplicates
|
||||
const protocolHandledSessions = new WeakSet<Session>();
|
||||
|
||||
export interface BrowserWindowOpts extends BrowserWindowConstructorOptions {
|
||||
devTools?: boolean;
|
||||
@@ -475,6 +473,11 @@ export default class Browser {
|
||||
});
|
||||
}
|
||||
|
||||
setWindowResizable(resizable: boolean) {
|
||||
logger.debug(`[${this.identifier}] Setting window resizable: ${resizable}`);
|
||||
this._browserWindow?.setResizable(resizable);
|
||||
}
|
||||
|
||||
broadcast = <T extends MainBroadcastEventKey>(channel: T, data?: MainBroadcastParams<T>) => {
|
||||
if (this._browserWindow.isDestroyed()) return;
|
||||
|
||||
@@ -540,107 +543,19 @@ export default class Browser {
|
||||
private setupRemoteServerRequestHook(browserWindow: BrowserWindow) {
|
||||
const session = browserWindow.webContents.session;
|
||||
const remoteServerConfigCtr = this.app.getController(RemoteServerConfigCtr);
|
||||
const logPrefix = `[${this.identifier}] RemoteServerRequestHook`;
|
||||
|
||||
// Guard to ensure hooks are registered only once per session
|
||||
const targetSession = session || electronSession.defaultSession;
|
||||
if (!targetSession || protocolHandledSessions.has(targetSession)) return;
|
||||
|
||||
const rewriteUrl = async (rawUrl: string) => {
|
||||
let remoteServerUrl: string | undefined;
|
||||
try {
|
||||
const requestUrl = new URL(rawUrl);
|
||||
if (!targetSession) return;
|
||||
|
||||
backendProxyProtocolManager.registerWithRemoteBaseUrl(targetSession, {
|
||||
getAccessToken: () => remoteServerConfigCtr.getAccessToken(),
|
||||
getRemoteBaseUrl: async () => {
|
||||
const config = await remoteServerConfigCtr.getRemoteServerConfig();
|
||||
remoteServerUrl = await remoteServerConfigCtr.getRemoteServerUrl(config);
|
||||
const remoteBase = new URL(remoteServerUrl);
|
||||
if (requestUrl.origin === remoteBase.origin) return;
|
||||
|
||||
const rewrittenUrl = new URL(
|
||||
requestUrl.pathname + requestUrl.search,
|
||||
remoteBase,
|
||||
).toString();
|
||||
logger.debug(`${logPrefix} rewrite ${rawUrl} -> ${rewrittenUrl}`);
|
||||
return rewrittenUrl;
|
||||
} catch (error) {
|
||||
logger.error(
|
||||
`${logPrefix} rewriteUrl error (rawUrl=${rawUrl}, remoteServerUrl=${remoteServerUrl})`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
// Transparent rewrite via protocol handlers (no HTTP 302)
|
||||
const registerProtocolHandlers = async () => {
|
||||
const proxyHandler = async (request: Request): Promise<Response | null> => {
|
||||
// lobe-backend://lobe/trpc/xxx -> http://<target_host>/trpc/xxx
|
||||
try {
|
||||
const rewrittenUrl = await rewriteUrl(request.url);
|
||||
if (!rewrittenUrl) return null;
|
||||
|
||||
const headers = new Headers(request.headers);
|
||||
|
||||
const token = await remoteServerConfigCtr.getAccessToken();
|
||||
if (token) headers.set('Oidc-Auth', token);
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const requestInit: RequestInit & { duplex?: 'half' } = {
|
||||
headers,
|
||||
method: request.method,
|
||||
};
|
||||
|
||||
// Only forward body for non-GET/HEAD requests
|
||||
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
||||
const body = request.body ?? undefined;
|
||||
if (body) {
|
||||
requestInit.body = body;
|
||||
// Node.js (undici) requires `duplex` when sending a streaming body
|
||||
requestInit.duplex = 'half';
|
||||
}
|
||||
}
|
||||
|
||||
let upstreamResponse: Response;
|
||||
try {
|
||||
upstreamResponse = await fetch(rewrittenUrl, requestInit);
|
||||
} catch (error) {
|
||||
logger.error(`${logPrefix} upstream fetch failed: ${rewrittenUrl}`, error);
|
||||
|
||||
return new Response('Upstream fetch failed, target url: ' + rewrittenUrl, {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
},
|
||||
status: 502,
|
||||
statusText: 'Bad Gateway',
|
||||
});
|
||||
}
|
||||
const responseHeaders = new Headers(upstreamResponse.headers);
|
||||
|
||||
const allowOrigin = request.headers.get('Origin') || undefined;
|
||||
if (allowOrigin) {
|
||||
responseHeaders.set('Access-Control-Allow-Origin', allowOrigin);
|
||||
responseHeaders.set('Access-Control-Allow-Credentials', 'true');
|
||||
}
|
||||
responseHeaders.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
responseHeaders.set('Access-Control-Allow-Headers', '*');
|
||||
|
||||
responseHeaders.set('X-Src-Url', rewrittenUrl);
|
||||
return new Response(upstreamResponse.body, {
|
||||
headers: responseHeaders,
|
||||
status: upstreamResponse.status,
|
||||
statusText: upstreamResponse.statusText,
|
||||
});
|
||||
} catch (error) {
|
||||
logger.error(`${logPrefix} protocol.handle error:`, error);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
targetSession.protocol.handle(ELECTRON_BE_PROTOCOL_SCHEME, proxyHandler);
|
||||
logger.debug(`${logPrefix} protocol handler registered for ${ELECTRON_BE_PROTOCOL_SCHEME}`);
|
||||
};
|
||||
|
||||
registerProtocolHandlers();
|
||||
protocolHandledSessions.add(targetSession);
|
||||
const remoteServerUrl = await remoteServerConfigCtr.getRemoteServerUrl(config);
|
||||
return remoteServerUrl || null;
|
||||
},
|
||||
scheme: ELECTRON_BE_PROTOCOL_SCHEME,
|
||||
source: this.identifier,
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ import { createLogger } from '@/utils/logger';
|
||||
|
||||
import {
|
||||
AppBrowsersIdentifiers,
|
||||
BrowsersIdentifiers,
|
||||
WindowTemplateIdentifiers,
|
||||
appBrowsers,
|
||||
windowTemplates,
|
||||
@@ -29,7 +30,7 @@ export class BrowserManager {
|
||||
}
|
||||
|
||||
getMainWindow() {
|
||||
return this.retrieveByIdentifier('chat');
|
||||
return this.retrieveByIdentifier(BrowsersIdentifiers.app);
|
||||
}
|
||||
|
||||
showMainWindow() {
|
||||
@@ -244,6 +245,16 @@ export class BrowserManager {
|
||||
}
|
||||
}
|
||||
|
||||
setWindowSize(identifier: string, size: { height?: number; width?: number }) {
|
||||
const browser = this.browsers.get(identifier);
|
||||
browser?.setWindowSize(size);
|
||||
}
|
||||
|
||||
setWindowResizable(identifier: string, resizable: boolean) {
|
||||
const browser = this.browsers.get(identifier);
|
||||
browser?.setWindowResizable(resizable);
|
||||
}
|
||||
|
||||
getIdentifierByWebContents(webContents: WebContents): string | null {
|
||||
return this.webContentsMap.get(webContents) || null;
|
||||
}
|
||||
|
||||
@@ -33,10 +33,10 @@ const { MockBrowser, mockAppBrowsers, mockWindowTemplates } = vi.hoisted(() => {
|
||||
return {
|
||||
MockBrowser,
|
||||
mockAppBrowsers: {
|
||||
chat: {
|
||||
identifier: 'chat',
|
||||
app: {
|
||||
identifier: 'app',
|
||||
keepAlive: true,
|
||||
path: '/chat',
|
||||
path: '/app',
|
||||
},
|
||||
settings: {
|
||||
identifier: 'settings',
|
||||
@@ -61,6 +61,10 @@ vi.mock('../Browser', () => ({
|
||||
|
||||
// Mock appBrowsers config
|
||||
vi.mock('../../../appBrowsers', () => ({
|
||||
BrowsersIdentifiers: {
|
||||
app: 'app',
|
||||
devtools: 'devtools',
|
||||
},
|
||||
appBrowsers: mockAppBrowsers,
|
||||
windowTemplates: mockWindowTemplates,
|
||||
}));
|
||||
@@ -102,10 +106,10 @@ describe('BrowserManager', () => {
|
||||
});
|
||||
|
||||
describe('getMainWindow', () => {
|
||||
it('should return chat window', () => {
|
||||
it('should return app window', () => {
|
||||
const mainWindow = manager.getMainWindow();
|
||||
|
||||
expect(mainWindow.identifier).toBe('chat');
|
||||
expect(mainWindow.identifier).toBe('app');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -113,27 +117,27 @@ describe('BrowserManager', () => {
|
||||
it('should show the main window', () => {
|
||||
manager.showMainWindow();
|
||||
|
||||
const chatBrowser = manager.browsers.get('chat');
|
||||
expect(chatBrowser?.show).toHaveBeenCalled();
|
||||
const appBrowser = manager.browsers.get('app');
|
||||
expect(appBrowser?.show).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('retrieveByIdentifier', () => {
|
||||
it('should return existing browser', () => {
|
||||
// First call creates the browser
|
||||
const browser1 = manager.retrieveByIdentifier('chat');
|
||||
const browser1 = manager.retrieveByIdentifier('app');
|
||||
// Second call should return same instance
|
||||
const browser2 = manager.retrieveByIdentifier('chat');
|
||||
const browser2 = manager.retrieveByIdentifier('app');
|
||||
|
||||
expect(browser1).toBe(browser2);
|
||||
expect(MockBrowser).toHaveBeenCalledTimes(1);
|
||||
});
|
||||
|
||||
it('should create static browser when not exists', () => {
|
||||
const browser = manager.retrieveByIdentifier('chat');
|
||||
const browser = manager.retrieveByIdentifier('app');
|
||||
|
||||
expect(MockBrowser).toHaveBeenCalledWith(mockAppBrowsers.chat, mockApp);
|
||||
expect(browser.identifier).toBe('chat');
|
||||
expect(MockBrowser).toHaveBeenCalledWith(mockAppBrowsers.app, mockApp);
|
||||
expect(browser.identifier).toBe('app');
|
||||
});
|
||||
|
||||
it('should throw error for non-static browser that does not exist', () => {
|
||||
@@ -188,13 +192,13 @@ describe('BrowserManager', () => {
|
||||
it('should return windows matching template prefix', () => {
|
||||
manager.createMultiInstanceWindow('popup' as any, '/path1', 'popup_1');
|
||||
manager.createMultiInstanceWindow('popup' as any, '/path2', 'popup_2');
|
||||
manager.retrieveByIdentifier('chat'); // This should not be included
|
||||
manager.retrieveByIdentifier('app'); // This should not be included
|
||||
|
||||
const popupWindows = manager.getWindowsByTemplate('popup');
|
||||
|
||||
expect(popupWindows).toContain('popup_1');
|
||||
expect(popupWindows).toContain('popup_2');
|
||||
expect(popupWindows).not.toContain('chat');
|
||||
expect(popupWindows).not.toContain('app');
|
||||
});
|
||||
|
||||
it('should return empty array when no matching windows', () => {
|
||||
@@ -228,23 +232,23 @@ describe('BrowserManager', () => {
|
||||
it('should initialize keepAlive browsers', () => {
|
||||
manager.initializeBrowsers();
|
||||
|
||||
// chat has keepAlive: true, settings has keepAlive: false
|
||||
expect(manager.browsers.has('chat')).toBe(true);
|
||||
// app has keepAlive: true, settings has keepAlive: false
|
||||
expect(manager.browsers.has('app')).toBe(true);
|
||||
expect(manager.browsers.has('settings')).toBe(false);
|
||||
});
|
||||
});
|
||||
|
||||
describe('broadcastToAllWindows', () => {
|
||||
it('should broadcast to all browsers', () => {
|
||||
manager.retrieveByIdentifier('chat');
|
||||
manager.retrieveByIdentifier('app');
|
||||
manager.retrieveByIdentifier('settings');
|
||||
|
||||
manager.broadcastToAllWindows('updateAvailable' as any, { version: '1.0.0' } as any);
|
||||
|
||||
const chatBrowser = manager.browsers.get('chat');
|
||||
const appBrowser = manager.browsers.get('app');
|
||||
const settingsBrowser = manager.browsers.get('settings');
|
||||
|
||||
expect(chatBrowser?.broadcast).toHaveBeenCalledWith('updateAvailable', { version: '1.0.0' });
|
||||
expect(appBrowser?.broadcast).toHaveBeenCalledWith('updateAvailable', { version: '1.0.0' });
|
||||
expect(settingsBrowser?.broadcast).toHaveBeenCalledWith('updateAvailable', {
|
||||
version: '1.0.0',
|
||||
});
|
||||
@@ -253,15 +257,15 @@ describe('BrowserManager', () => {
|
||||
|
||||
describe('broadcastToWindow', () => {
|
||||
it('should broadcast to specific window', () => {
|
||||
manager.retrieveByIdentifier('chat');
|
||||
manager.retrieveByIdentifier('app');
|
||||
manager.retrieveByIdentifier('settings');
|
||||
|
||||
const chatBrowser = manager.browsers.get('chat');
|
||||
const appBrowser = manager.browsers.get('app');
|
||||
const settingsBrowser = manager.browsers.get('settings');
|
||||
|
||||
manager.broadcastToWindow('chat', 'updateAvailable' as any, { version: '1.0.0' } as any);
|
||||
manager.broadcastToWindow('app', 'updateAvailable' as any, { version: '1.0.0' } as any);
|
||||
|
||||
expect(chatBrowser?.broadcast).toHaveBeenCalledWith('updateAvailable', { version: '1.0.0' });
|
||||
expect(appBrowser?.broadcast).toHaveBeenCalledWith('updateAvailable', { version: '1.0.0' });
|
||||
expect(settingsBrowser?.broadcast).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -274,35 +278,35 @@ describe('BrowserManager', () => {
|
||||
|
||||
describe('redirectToPage', () => {
|
||||
it('should load URL and show window', async () => {
|
||||
const browser = await manager.redirectToPage('chat', 'agent');
|
||||
const browser = await manager.redirectToPage('app', 'agent');
|
||||
|
||||
expect(browser.hide).toHaveBeenCalled();
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/chat/agent');
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/app/agent');
|
||||
expect(browser.show).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle subPath correctly', async () => {
|
||||
const browser = await manager.redirectToPage('chat', 'settings/profile');
|
||||
const browser = await manager.redirectToPage('app', 'settings/profile');
|
||||
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/chat/settings/profile');
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/app/settings/profile');
|
||||
});
|
||||
|
||||
it('should handle search parameters', async () => {
|
||||
const browser = await manager.redirectToPage('chat', 'agent', 'id=123');
|
||||
const browser = await manager.redirectToPage('app', 'agent', 'id=123');
|
||||
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/chat/agent?id=123');
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/app/agent?id=123');
|
||||
});
|
||||
|
||||
it('should handle search parameters starting with ?', async () => {
|
||||
const browser = await manager.redirectToPage('chat', undefined, '?id=123');
|
||||
const browser = await manager.redirectToPage('app', undefined, '?id=123');
|
||||
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/chat?id=123');
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/app?id=123');
|
||||
});
|
||||
|
||||
it('should handle no subPath', async () => {
|
||||
const browser = await manager.redirectToPage('chat');
|
||||
const browser = await manager.redirectToPage('app');
|
||||
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/chat');
|
||||
expect(browser.loadUrl).toHaveBeenCalledWith('/app');
|
||||
});
|
||||
|
||||
it('should throw error on failure', async () => {
|
||||
@@ -315,7 +319,7 @@ describe('BrowserManager', () => {
|
||||
hide: vi.fn(),
|
||||
identifier: options.identifier,
|
||||
loadUrl: vi.fn().mockRejectedValue(mockError),
|
||||
options: { path: '/chat' },
|
||||
options: { path: '/app' },
|
||||
show: vi.fn(),
|
||||
webContents: { id: 1 },
|
||||
}));
|
||||
@@ -323,18 +327,18 @@ describe('BrowserManager', () => {
|
||||
// Clear the browser cache
|
||||
manager.browsers.clear();
|
||||
|
||||
await expect(manager.redirectToPage('chat', 'agent')).rejects.toThrow('Load failed');
|
||||
await expect(manager.redirectToPage('app', 'agent')).rejects.toThrow('Load failed');
|
||||
});
|
||||
});
|
||||
|
||||
describe('window operations', () => {
|
||||
describe('closeWindow', () => {
|
||||
it('should close specified window', () => {
|
||||
manager.retrieveByIdentifier('chat');
|
||||
manager.retrieveByIdentifier('app');
|
||||
|
||||
manager.closeWindow('chat');
|
||||
manager.closeWindow('app');
|
||||
|
||||
const browser = manager.browsers.get('chat');
|
||||
const browser = manager.browsers.get('app');
|
||||
expect(browser?.close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
@@ -345,33 +349,33 @@ describe('BrowserManager', () => {
|
||||
|
||||
describe('minimizeWindow', () => {
|
||||
it('should minimize specified window', () => {
|
||||
manager.retrieveByIdentifier('chat');
|
||||
manager.retrieveByIdentifier('app');
|
||||
|
||||
manager.minimizeWindow('chat');
|
||||
manager.minimizeWindow('app');
|
||||
|
||||
const browser = manager.browsers.get('chat');
|
||||
const browser = manager.browsers.get('app');
|
||||
expect(browser?.browserWindow.minimize).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
describe('maximizeWindow', () => {
|
||||
it('should maximize when not maximized', () => {
|
||||
manager.retrieveByIdentifier('chat');
|
||||
const browser = manager.browsers.get('chat');
|
||||
manager.retrieveByIdentifier('app');
|
||||
const browser = manager.browsers.get('app');
|
||||
browser!.browserWindow.isMaximized = vi.fn().mockReturnValue(false);
|
||||
|
||||
manager.maximizeWindow('chat');
|
||||
manager.maximizeWindow('app');
|
||||
|
||||
expect(browser?.browserWindow.maximize).toHaveBeenCalled();
|
||||
expect(browser?.browserWindow.unmaximize).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should unmaximize when already maximized', () => {
|
||||
manager.retrieveByIdentifier('chat');
|
||||
const browser = manager.browsers.get('chat');
|
||||
manager.retrieveByIdentifier('app');
|
||||
const browser = manager.browsers.get('app');
|
||||
browser!.browserWindow.isMaximized = vi.fn().mockReturnValue(true);
|
||||
|
||||
manager.maximizeWindow('chat');
|
||||
manager.maximizeWindow('app');
|
||||
|
||||
expect(browser?.browserWindow.unmaximize).toHaveBeenCalled();
|
||||
expect(browser?.browserWindow.maximize).not.toHaveBeenCalled();
|
||||
@@ -381,12 +385,12 @@ describe('BrowserManager', () => {
|
||||
|
||||
describe('getIdentifierByWebContents', () => {
|
||||
it('should return identifier for known webContents', () => {
|
||||
const browser = manager.retrieveByIdentifier('chat');
|
||||
const browser = manager.retrieveByIdentifier('app');
|
||||
const webContents = browser.browserWindow.webContents;
|
||||
|
||||
const identifier = manager.getIdentifierByWebContents(webContents as any);
|
||||
|
||||
expect(identifier).toBe('chat');
|
||||
expect(identifier).toBe('app');
|
||||
});
|
||||
|
||||
it('should return null for unknown webContents', () => {
|
||||
@@ -400,15 +404,15 @@ describe('BrowserManager', () => {
|
||||
|
||||
describe('handleAppThemeChange', () => {
|
||||
it('should notify all browsers of theme change', () => {
|
||||
manager.retrieveByIdentifier('chat');
|
||||
manager.retrieveByIdentifier('app');
|
||||
manager.retrieveByIdentifier('settings');
|
||||
|
||||
manager.handleAppThemeChange();
|
||||
|
||||
const chatBrowser = manager.browsers.get('chat');
|
||||
const appBrowser = manager.browsers.get('app');
|
||||
const settingsBrowser = manager.browsers.get('settings');
|
||||
|
||||
expect(chatBrowser?.handleAppThemeChange).toHaveBeenCalled();
|
||||
expect(appBrowser?.handleAppThemeChange).toHaveBeenCalled();
|
||||
expect(settingsBrowser?.handleAppThemeChange).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,149 @@
|
||||
import type { Session } from 'electron';
|
||||
|
||||
import { createLogger } from '@/utils/logger';
|
||||
|
||||
interface BackendProxyProtocolManagerOptions {
|
||||
getAccessToken: () => Promise<string | undefined | null>;
|
||||
rewriteUrl: (rawUrl: string) => Promise<string | null>;
|
||||
scheme: string;
|
||||
/**
|
||||
* Used for log prefixes. e.g. window identifier
|
||||
*/
|
||||
source?: string;
|
||||
}
|
||||
|
||||
interface BackendProxyProtocolManagerRemoteBaseOptions {
|
||||
getAccessToken: () => Promise<string | undefined | null>;
|
||||
getRemoteBaseUrl: () => Promise<string | undefined | null>;
|
||||
scheme: string;
|
||||
/**
|
||||
* Used for log prefixes. e.g. window identifier
|
||||
*/
|
||||
source?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* Manage `lobe-backend://` (or any custom scheme) transparent proxy handler registration.
|
||||
* Keeps a WeakSet per session to avoid duplicate handler registration.
|
||||
*/
|
||||
export class BackendProxyProtocolManager {
|
||||
private readonly handledSessions = new WeakSet<Session>();
|
||||
private readonly logger = createLogger('core:BackendProxyProtocolManager');
|
||||
|
||||
registerWithRemoteBaseUrl(
|
||||
session: Session,
|
||||
options: BackendProxyProtocolManagerRemoteBaseOptions,
|
||||
) {
|
||||
let lastRemoteBaseUrl: string | undefined;
|
||||
|
||||
const rewriteUrl = async (rawUrl: string) => {
|
||||
lastRemoteBaseUrl = undefined;
|
||||
try {
|
||||
const requestUrl = new URL(rawUrl);
|
||||
|
||||
const remoteBaseUrl = await options.getRemoteBaseUrl();
|
||||
if (!remoteBaseUrl) return null;
|
||||
lastRemoteBaseUrl = remoteBaseUrl;
|
||||
|
||||
const remoteBase = new URL(remoteBaseUrl);
|
||||
if (requestUrl.origin === remoteBase.origin) return null;
|
||||
|
||||
const rewrittenUrl = new URL(
|
||||
requestUrl.pathname + requestUrl.search,
|
||||
remoteBase,
|
||||
).toString();
|
||||
this.logger.debug(
|
||||
`${options.source ? `[${options.source}] ` : ''}BackendProxy rewrite ${rawUrl} -> ${rewrittenUrl}`,
|
||||
);
|
||||
return rewrittenUrl;
|
||||
} catch (error) {
|
||||
this.logger.error(
|
||||
`${options.source ? `[${options.source}] ` : ''}BackendProxy rewriteUrl error (rawUrl=${rawUrl}, remoteBaseUrl=${lastRemoteBaseUrl})`,
|
||||
error,
|
||||
);
|
||||
return null;
|
||||
}
|
||||
};
|
||||
|
||||
this.register(session, {
|
||||
getAccessToken: options.getAccessToken,
|
||||
rewriteUrl,
|
||||
scheme: options.scheme,
|
||||
source: options.source,
|
||||
});
|
||||
}
|
||||
|
||||
register(session: Session, options: BackendProxyProtocolManagerOptions) {
|
||||
if (!session || this.handledSessions.has(session)) return;
|
||||
|
||||
const logPrefix = options.source ? `[${options.source}] BackendProxy` : '[BackendProxy]';
|
||||
|
||||
session.protocol.handle(options.scheme, async (request: Request): Promise<Response | null> => {
|
||||
try {
|
||||
const rewrittenUrl = await options.rewriteUrl(request.url);
|
||||
if (!rewrittenUrl) return null;
|
||||
|
||||
const headers = new Headers(request.headers);
|
||||
const token = await options.getAccessToken();
|
||||
if (token) headers.set('Oidc-Auth', token);
|
||||
|
||||
// eslint-disable-next-line no-undef
|
||||
const requestInit: RequestInit & { duplex?: 'half' } = {
|
||||
headers,
|
||||
method: request.method,
|
||||
};
|
||||
|
||||
// Only forward body for non-GET/HEAD requests
|
||||
if (request.method !== 'GET' && request.method !== 'HEAD') {
|
||||
const body = request.body ?? undefined;
|
||||
if (body) {
|
||||
requestInit.body = body;
|
||||
// Node.js (undici) requires `duplex` when sending a streaming body
|
||||
requestInit.duplex = 'half';
|
||||
}
|
||||
}
|
||||
|
||||
let upstreamResponse: Response;
|
||||
try {
|
||||
upstreamResponse = await fetch(rewrittenUrl, requestInit);
|
||||
} catch (error) {
|
||||
this.logger.error(`${logPrefix} upstream fetch failed: ${rewrittenUrl}`, error);
|
||||
|
||||
return new Response('Upstream fetch failed, target url: ' + rewrittenUrl, {
|
||||
headers: {
|
||||
'Content-Type': 'text/plain; charset=utf-8',
|
||||
},
|
||||
status: 502,
|
||||
statusText: 'Bad Gateway',
|
||||
});
|
||||
}
|
||||
|
||||
const responseHeaders = new Headers(upstreamResponse.headers);
|
||||
const allowOrigin = request.headers.get('Origin') || undefined;
|
||||
|
||||
if (allowOrigin) {
|
||||
responseHeaders.set('Access-Control-Allow-Origin', allowOrigin);
|
||||
responseHeaders.set('Access-Control-Allow-Credentials', 'true');
|
||||
}
|
||||
|
||||
responseHeaders.set('Access-Control-Allow-Methods', 'GET, POST, PUT, DELETE, OPTIONS');
|
||||
responseHeaders.set('Access-Control-Allow-Headers', '*');
|
||||
responseHeaders.set('X-Src-Url', rewrittenUrl);
|
||||
|
||||
return new Response(upstreamResponse.body, {
|
||||
headers: responseHeaders,
|
||||
status: upstreamResponse.status,
|
||||
statusText: upstreamResponse.statusText,
|
||||
});
|
||||
} catch (error) {
|
||||
this.logger.error(`${logPrefix} protocol.handle error:`, error);
|
||||
return null;
|
||||
}
|
||||
});
|
||||
|
||||
this.logger.debug(`${logPrefix} protocol handler registered for ${options.scheme}`);
|
||||
this.handledSessions.add(session);
|
||||
}
|
||||
}
|
||||
|
||||
export const backendProxyProtocolManager = new BackendProxyProtocolManager();
|
||||
@@ -31,7 +31,7 @@ export class I18nManager {
|
||||
// Priority: parameter language > stored locale > system language
|
||||
const storedLocale = this.app.storeManager.get('locale', 'auto') as string;
|
||||
const defaultLanguage =
|
||||
lang || (storedLocale !== 'auto' ? storedLocale : app.getLocale()) || 'en-US';
|
||||
lang || (storedLocale !== 'auto' ? storedLocale : app.getLocale()) || 'en';
|
||||
|
||||
logger.info(
|
||||
`Initializing i18n, app locale: ${defaultLanguage}, stored locale: ${storedLocale}`,
|
||||
@@ -39,15 +39,15 @@ export class I18nManager {
|
||||
|
||||
await this.i18n.init({
|
||||
defaultNS: 'menu',
|
||||
fallbackLng: 'en-US',
|
||||
fallbackLng: 'en',
|
||||
// Load resources as needed
|
||||
initAsync: true,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
|
||||
keySeparator: false,
|
||||
lng: defaultLanguage,
|
||||
|
||||
ns: ['menu', 'dialog', 'common'],
|
||||
partialBundledLanguages: true,
|
||||
});
|
||||
|
||||
@@ -1,12 +1,13 @@
|
||||
import { app, protocol } from 'electron';
|
||||
import { pathExistsSync } from 'fs-extra';
|
||||
import { readFile } from 'node:fs/promises';
|
||||
import { readFile, stat } from 'node:fs/promises';
|
||||
import { basename, extname } from 'node:path';
|
||||
|
||||
import { createLogger } from '@/utils/logger';
|
||||
|
||||
import { getExportMimeType } from '../../utils/mime';
|
||||
|
||||
type ResolveRendererFilePath = (url: URL) => Promise<string | null>;
|
||||
type GetExportMimeType = (filePath: string) => string | undefined;
|
||||
|
||||
const RENDERER_PROTOCOL_PRIVILEGES = {
|
||||
allowServiceWorkers: true,
|
||||
@@ -17,7 +18,6 @@ const RENDERER_PROTOCOL_PRIVILEGES = {
|
||||
} as const;
|
||||
|
||||
interface RendererProtocolManagerOptions {
|
||||
getExportMimeType: GetExportMimeType;
|
||||
host?: string;
|
||||
nextExportDir: string;
|
||||
resolveRendererFilePath: ResolveRendererFilePath;
|
||||
@@ -30,17 +30,15 @@ export class RendererProtocolManager {
|
||||
private readonly host: string;
|
||||
private readonly nextExportDir: string;
|
||||
private readonly resolveRendererFilePath: ResolveRendererFilePath;
|
||||
private readonly getExportMimeType: GetExportMimeType;
|
||||
private handlerRegistered = false;
|
||||
|
||||
constructor(options: RendererProtocolManagerOptions) {
|
||||
const { nextExportDir, resolveRendererFilePath, getExportMimeType } = options;
|
||||
const { nextExportDir, resolveRendererFilePath } = options;
|
||||
|
||||
this.scheme = 'app';
|
||||
this.host = RENDERER_DIR;
|
||||
this.nextExportDir = nextExportDir;
|
||||
this.resolveRendererFilePath = resolveRendererFilePath;
|
||||
this.getExportMimeType = getExportMimeType;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -86,13 +84,86 @@ export class RendererProtocolManager {
|
||||
}
|
||||
|
||||
const buildFileResponse = async (targetPath: string) => {
|
||||
const fileStat = await stat(targetPath);
|
||||
const totalSize = fileStat.size;
|
||||
|
||||
const buffer = await readFile(targetPath);
|
||||
const headers = new Headers();
|
||||
const mimeType = this.getExportMimeType(targetPath);
|
||||
const mimeType = getExportMimeType(targetPath);
|
||||
|
||||
if (mimeType) headers.set('Content-Type', mimeType);
|
||||
|
||||
return new Response(buffer, { headers });
|
||||
// Chromium media pipeline relies on byte ranges for video/audio.
|
||||
headers.set('Accept-Ranges', 'bytes');
|
||||
|
||||
const method = request.method?.toUpperCase?.() || 'GET';
|
||||
const rangeHeader = request.headers.get('range') || request.headers.get('Range');
|
||||
|
||||
// HEAD (no range): return only headers
|
||||
if (method === 'HEAD' && !rangeHeader) {
|
||||
headers.set('Content-Length', String(totalSize));
|
||||
return new Response(null, { headers, status: 200 });
|
||||
}
|
||||
|
||||
// No Range: return entire file
|
||||
if (!rangeHeader) {
|
||||
headers.set('Content-Length', String(buffer.byteLength));
|
||||
return new Response(buffer, { headers, status: 200 });
|
||||
}
|
||||
|
||||
// Range: bytes=start-end | bytes=-suffixLength
|
||||
const match = /^bytes=(\d*)-(\d*)$/i.exec(rangeHeader.trim());
|
||||
if (!match) {
|
||||
headers.set('Content-Range', `bytes */${totalSize}`);
|
||||
return new Response(null, {
|
||||
headers,
|
||||
status: 416,
|
||||
statusText: 'Range Not Satisfiable',
|
||||
});
|
||||
}
|
||||
|
||||
const [, startRaw, endRaw] = match;
|
||||
let start = startRaw ? Number(startRaw) : NaN;
|
||||
let end = endRaw ? Number(endRaw) : NaN;
|
||||
|
||||
// Suffix range: bytes=-N (last N bytes)
|
||||
if (!startRaw && endRaw) {
|
||||
const suffixLength = Number(endRaw);
|
||||
if (!Number.isFinite(suffixLength) || suffixLength <= 0) {
|
||||
headers.set('Content-Range', `bytes */${totalSize}`);
|
||||
return new Response(null, {
|
||||
headers,
|
||||
status: 416,
|
||||
statusText: 'Range Not Satisfiable',
|
||||
});
|
||||
}
|
||||
start = Math.max(totalSize - suffixLength, 0);
|
||||
end = totalSize - 1;
|
||||
} else {
|
||||
if (!Number.isFinite(start)) start = 0;
|
||||
if (!Number.isFinite(end)) end = totalSize - 1;
|
||||
}
|
||||
|
||||
if (start < 0 || end < 0 || start > end || start >= totalSize) {
|
||||
headers.set('Content-Range', `bytes */${totalSize}`);
|
||||
return new Response(null, {
|
||||
headers,
|
||||
status: 416,
|
||||
statusText: 'Range Not Satisfiable',
|
||||
});
|
||||
}
|
||||
|
||||
end = Math.min(end, totalSize - 1);
|
||||
const sliced = buffer.subarray(start, end + 1);
|
||||
|
||||
headers.set('Content-Range', `bytes ${start}-${end}/${totalSize}`);
|
||||
headers.set('Content-Length', String(sliced.byteLength));
|
||||
|
||||
if (method === 'HEAD') {
|
||||
return new Response(null, { headers, status: 206, statusText: 'Partial Content' });
|
||||
}
|
||||
|
||||
return new Response(sliced, { headers, status: 206, statusText: 'Partial Content' });
|
||||
};
|
||||
|
||||
const resolveEntryFilePath = () =>
|
||||
@@ -121,7 +192,7 @@ export class RendererProtocolManager {
|
||||
try {
|
||||
return await buildFileResponse(filePath);
|
||||
} catch (error) {
|
||||
const code = (error as NodeJS.ErrnoException).code;
|
||||
const code = (error as any).code;
|
||||
|
||||
if (code === 'ENOENT') {
|
||||
logger.warn(`Export asset missing on disk ${filePath}, falling back`, error);
|
||||
|
||||
@@ -25,14 +25,12 @@ const getAllowedOrigin = (rawOrigin?: string) => {
|
||||
};
|
||||
|
||||
export class StaticFileServerManager {
|
||||
private app: App;
|
||||
private fileService: FileService;
|
||||
private httpServer: any = null;
|
||||
private serverPort: number = 0;
|
||||
private isInitialized = false;
|
||||
|
||||
constructor(app: App) {
|
||||
this.app = app;
|
||||
this.fileService = app.getService(FileService);
|
||||
logger.debug('StaticFileServerManager initialized');
|
||||
}
|
||||
|
||||
+206
@@ -0,0 +1,206 @@
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { BackendProxyProtocolManager } from '../BackendProxyProtocolManager';
|
||||
|
||||
interface RequestInitWithDuplex extends RequestInit {
|
||||
duplex?: 'half';
|
||||
}
|
||||
|
||||
type FetchMock = (input: RequestInfo | URL, init?: RequestInitWithDuplex) => Promise<Response>;
|
||||
|
||||
const { mockProtocol, protocolHandlerRef } = vi.hoisted(() => {
|
||||
const protocolHandlerRef = { current: null as any };
|
||||
|
||||
return {
|
||||
mockProtocol: {
|
||||
handle: vi.fn((_scheme: string, handler: any) => {
|
||||
protocolHandlerRef.current = handler;
|
||||
}),
|
||||
},
|
||||
protocolHandlerRef,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('@/utils/logger', () => ({
|
||||
createLogger: () => ({
|
||||
debug: vi.fn(),
|
||||
error: vi.fn(),
|
||||
info: vi.fn(),
|
||||
warn: vi.fn(),
|
||||
}),
|
||||
}));
|
||||
|
||||
describe('BackendProxyProtocolManager', () => {
|
||||
beforeEach(() => {
|
||||
vi.clearAllMocks();
|
||||
protocolHandlerRef.current = null;
|
||||
});
|
||||
|
||||
it('should rewrite url to remote base and inject Oidc-Auth token', async () => {
|
||||
const manager = new BackendProxyProtocolManager();
|
||||
const session = { protocol: mockProtocol } as any;
|
||||
|
||||
const fetchMock = vi.fn<FetchMock>(async () => {
|
||||
return new Response('ok', {
|
||||
headers: { 'Content-Type': 'text/plain' },
|
||||
status: 200,
|
||||
statusText: 'OK',
|
||||
});
|
||||
});
|
||||
vi.stubGlobal('fetch', fetchMock as any);
|
||||
|
||||
manager.registerWithRemoteBaseUrl(session, {
|
||||
getAccessToken: async () => 'token-123',
|
||||
getRemoteBaseUrl: async () => 'https://remote.example.com',
|
||||
scheme: 'lobe-backend',
|
||||
source: 'main',
|
||||
});
|
||||
|
||||
const handler = protocolHandlerRef.current;
|
||||
expect(mockProtocol.handle).toHaveBeenCalledWith('lobe-backend', expect.any(Function));
|
||||
|
||||
const response = await handler({
|
||||
headers: new Headers({ 'Origin': 'app://desktop', 'X-Test': '1' }),
|
||||
method: 'GET',
|
||||
url: 'lobe-backend://app/trpc/hello?batch=1',
|
||||
} as any);
|
||||
|
||||
expect(fetchMock).toHaveBeenCalledTimes(1);
|
||||
const [calledUrl, init] = fetchMock.mock.calls[0]!;
|
||||
expect(calledUrl).toBe('https://remote.example.com/trpc/hello?batch=1');
|
||||
expect(init).toBeDefined();
|
||||
if (!init) throw new Error('Expected fetch init to be defined');
|
||||
|
||||
expect(init.method).toBe('GET');
|
||||
const headers = init.headers as Headers;
|
||||
expect(headers.get('Oidc-Auth')).toBe('token-123');
|
||||
expect(headers.get('X-Test')).toBe('1');
|
||||
|
||||
expect(response.status).toBe(200);
|
||||
expect(response.headers.get('X-Src-Url')).toBe('https://remote.example.com/trpc/hello?batch=1');
|
||||
expect(response.headers.get('Access-Control-Allow-Origin')).toBe('app://desktop');
|
||||
expect(response.headers.get('Access-Control-Allow-Credentials')).toBe('true');
|
||||
expect(await response.text()).toBe('ok');
|
||||
});
|
||||
|
||||
it('should forward body and set duplex for non-GET requests', async () => {
|
||||
const manager = new BackendProxyProtocolManager();
|
||||
const session = { protocol: mockProtocol } as any;
|
||||
|
||||
const fetchMock = vi.fn<FetchMock>(async () => new Response('ok', { status: 200 }));
|
||||
vi.stubGlobal('fetch', fetchMock as any);
|
||||
|
||||
manager.registerWithRemoteBaseUrl(session, {
|
||||
getAccessToken: async () => null,
|
||||
getRemoteBaseUrl: async () => 'https://remote.example.com',
|
||||
scheme: 'lobe-backend',
|
||||
});
|
||||
|
||||
const handler = protocolHandlerRef.current;
|
||||
|
||||
await handler({
|
||||
headers: new Headers(),
|
||||
method: 'POST',
|
||||
// body doesn't have to be a real stream for this unit test; manager only checks truthiness
|
||||
body: 'payload' as any,
|
||||
url: 'lobe-backend://app/api/upload',
|
||||
} as any);
|
||||
|
||||
const [, init] = fetchMock.mock.calls[0]!;
|
||||
expect(init).toBeDefined();
|
||||
if (!init) throw new Error('Expected fetch init to be defined');
|
||||
|
||||
expect(init.method).toBe('POST');
|
||||
expect(init.body).toBe('payload');
|
||||
expect(init.duplex).toBe('half');
|
||||
});
|
||||
|
||||
it('should return null when remote base url is missing', async () => {
|
||||
const manager = new BackendProxyProtocolManager();
|
||||
const session = { protocol: mockProtocol } as any;
|
||||
|
||||
const fetchMock = vi.fn();
|
||||
vi.stubGlobal('fetch', fetchMock as any);
|
||||
|
||||
manager.registerWithRemoteBaseUrl(session, {
|
||||
getAccessToken: async () => 'token',
|
||||
getRemoteBaseUrl: async () => null,
|
||||
scheme: 'lobe-backend',
|
||||
});
|
||||
|
||||
const handler = protocolHandlerRef.current;
|
||||
const res = await handler({ method: 'GET', url: 'lobe-backend://app/trpc' } as any);
|
||||
|
||||
expect(res).toBeNull();
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return null when request url is already the remote origin', async () => {
|
||||
const manager = new BackendProxyProtocolManager();
|
||||
const session = { protocol: mockProtocol } as any;
|
||||
|
||||
const fetchMock = vi.fn();
|
||||
vi.stubGlobal('fetch', fetchMock as any);
|
||||
|
||||
manager.registerWithRemoteBaseUrl(session, {
|
||||
getAccessToken: async () => null,
|
||||
getRemoteBaseUrl: async () => 'https://remote.example.com',
|
||||
scheme: 'lobe-backend',
|
||||
});
|
||||
|
||||
const handler = protocolHandlerRef.current;
|
||||
const res = await handler({
|
||||
method: 'GET',
|
||||
url: 'https://remote.example.com/trpc/hello?x=1',
|
||||
} as any);
|
||||
|
||||
expect(res).toBeNull();
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should return null when rewrite fails (invalid remote base url)', async () => {
|
||||
const manager = new BackendProxyProtocolManager();
|
||||
const session = { protocol: mockProtocol } as any;
|
||||
|
||||
const fetchMock = vi.fn();
|
||||
vi.stubGlobal('fetch', fetchMock as any);
|
||||
|
||||
manager.registerWithRemoteBaseUrl(session, {
|
||||
getAccessToken: async () => null,
|
||||
getRemoteBaseUrl: async () => 'not-a-url',
|
||||
scheme: 'lobe-backend',
|
||||
});
|
||||
|
||||
const handler = protocolHandlerRef.current;
|
||||
const res = await handler({ method: 'GET', url: 'lobe-backend://app/trpc' } as any);
|
||||
|
||||
expect(res).toBeNull();
|
||||
expect(fetchMock).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should respond with 502 when upstream fetch throws', async () => {
|
||||
const manager = new BackendProxyProtocolManager();
|
||||
const session = { protocol: mockProtocol } as any;
|
||||
|
||||
const fetchMock = vi.fn(async () => {
|
||||
throw new Error('network down');
|
||||
});
|
||||
vi.stubGlobal('fetch', fetchMock as any);
|
||||
|
||||
manager.registerWithRemoteBaseUrl(session, {
|
||||
getAccessToken: async () => null,
|
||||
getRemoteBaseUrl: async () => 'https://remote.example.com',
|
||||
scheme: 'lobe-backend',
|
||||
});
|
||||
|
||||
const handler = protocolHandlerRef.current;
|
||||
const response = await handler({
|
||||
headers: new Headers(),
|
||||
method: 'GET',
|
||||
url: 'lobe-backend://app/trpc/hello',
|
||||
} as any);
|
||||
|
||||
expect(response.status).toBe(502);
|
||||
expect(await response.text()).toContain('Upstream fetch failed');
|
||||
});
|
||||
});
|
||||
@@ -102,11 +102,12 @@ describe('I18nManager', () => {
|
||||
|
||||
expect(mockI18nextInstance.init).toHaveBeenCalledWith({
|
||||
defaultNS: 'menu',
|
||||
fallbackLng: 'en-US',
|
||||
fallbackLng: 'en',
|
||||
initAsync: true,
|
||||
interpolation: {
|
||||
escapeValue: false,
|
||||
},
|
||||
keySeparator: false,
|
||||
lng: 'en-US',
|
||||
ns: ['menu', 'dialog', 'common'],
|
||||
partialBundledLanguages: true,
|
||||
|
||||
+62
-32
@@ -2,30 +2,26 @@ import { afterEach, beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import { RendererProtocolManager } from '../RendererProtocolManager';
|
||||
|
||||
const {
|
||||
mockApp,
|
||||
mockPathExistsSync,
|
||||
mockProtocol,
|
||||
mockReadFile,
|
||||
protocolHandlerRef,
|
||||
} = vi.hoisted(() => {
|
||||
const protocolHandlerRef = { current: null as any };
|
||||
const { mockApp, mockPathExistsSync, mockProtocol, mockReadFile, mockStat, protocolHandlerRef } =
|
||||
vi.hoisted(() => {
|
||||
const protocolHandlerRef = { current: null as any };
|
||||
|
||||
return {
|
||||
mockApp: {
|
||||
isReady: vi.fn().mockReturnValue(true),
|
||||
whenReady: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
mockPathExistsSync: vi.fn().mockReturnValue(true),
|
||||
mockProtocol: {
|
||||
handle: vi.fn((_scheme: string, handler: any) => {
|
||||
protocolHandlerRef.current = handler;
|
||||
}),
|
||||
},
|
||||
mockReadFile: vi.fn(),
|
||||
protocolHandlerRef,
|
||||
};
|
||||
});
|
||||
return {
|
||||
mockApp: {
|
||||
isReady: vi.fn().mockReturnValue(true),
|
||||
whenReady: vi.fn().mockResolvedValue(undefined),
|
||||
},
|
||||
mockPathExistsSync: vi.fn().mockReturnValue(true),
|
||||
mockProtocol: {
|
||||
handle: vi.fn((_scheme: string, handler: any) => {
|
||||
protocolHandlerRef.current = handler;
|
||||
}),
|
||||
},
|
||||
mockReadFile: vi.fn(),
|
||||
mockStat: vi.fn(),
|
||||
protocolHandlerRef,
|
||||
};
|
||||
});
|
||||
|
||||
vi.mock('electron', () => ({
|
||||
app: mockApp,
|
||||
@@ -38,6 +34,7 @@ vi.mock('fs-extra', () => ({
|
||||
|
||||
vi.mock('node:fs/promises', () => ({
|
||||
readFile: mockReadFile,
|
||||
stat: mockStat,
|
||||
}));
|
||||
|
||||
vi.mock('@/utils/logger', () => ({
|
||||
@@ -55,6 +52,7 @@ describe('RendererProtocolManager', () => {
|
||||
protocolHandlerRef.current = null;
|
||||
mockApp.isReady.mockReturnValue(true);
|
||||
mockPathExistsSync.mockReturnValue(true);
|
||||
mockStat.mockImplementation(async () => ({ size: 1024 }));
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
@@ -67,11 +65,9 @@ describe('RendererProtocolManager', () => {
|
||||
if (url.pathname === '/') return '/export/index.html';
|
||||
return null;
|
||||
});
|
||||
const getExportMimeType = vi.fn(() => 'text/html; charset=utf-8');
|
||||
mockReadFile.mockImplementation(async (path: string) => Buffer.from(`content:${path}`));
|
||||
|
||||
const manager = new RendererProtocolManager({
|
||||
getExportMimeType,
|
||||
nextExportDir: '/export',
|
||||
resolveRendererFilePath,
|
||||
});
|
||||
@@ -80,7 +76,11 @@ describe('RendererProtocolManager', () => {
|
||||
expect(mockProtocol.handle).toHaveBeenCalled();
|
||||
const handler = protocolHandlerRef.current;
|
||||
|
||||
const response = await handler({ url: 'app://next/missing' } as any);
|
||||
const response = await handler({
|
||||
headers: new Headers(),
|
||||
method: 'GET',
|
||||
url: 'app://next/missing',
|
||||
} as any);
|
||||
const body = await response.text();
|
||||
|
||||
expect(resolveRendererFilePath).toHaveBeenCalledTimes(2);
|
||||
@@ -98,11 +98,9 @@ describe('RendererProtocolManager', () => {
|
||||
if (url.pathname === '/') return '/export/index.html';
|
||||
return null;
|
||||
});
|
||||
const getExportMimeType = vi.fn(() => 'text/html; charset=utf-8');
|
||||
mockReadFile.mockImplementation(async (path: string) => Buffer.from(`content:${path}`));
|
||||
|
||||
const manager = new RendererProtocolManager({
|
||||
getExportMimeType,
|
||||
nextExportDir: '/export',
|
||||
resolveRendererFilePath,
|
||||
});
|
||||
@@ -110,7 +108,11 @@ describe('RendererProtocolManager', () => {
|
||||
manager.registerHandler();
|
||||
const handler = protocolHandlerRef.current;
|
||||
|
||||
const response = await handler({ url: 'app://next/404.html' } as any);
|
||||
const response = await handler({
|
||||
headers: new Headers(),
|
||||
method: 'GET',
|
||||
url: 'app://next/404.html',
|
||||
} as any);
|
||||
|
||||
expect(resolveRendererFilePath).toHaveBeenCalledTimes(1);
|
||||
expect(mockReadFile).toHaveBeenCalledWith('/export/404.html');
|
||||
@@ -119,10 +121,8 @@ describe('RendererProtocolManager', () => {
|
||||
|
||||
it('should return 404 for missing asset requests without fallback', async () => {
|
||||
const resolveRendererFilePath = vi.fn(async (_url: URL) => null);
|
||||
const getExportMimeType = vi.fn();
|
||||
|
||||
const manager = new RendererProtocolManager({
|
||||
getExportMimeType,
|
||||
nextExportDir: '/export',
|
||||
resolveRendererFilePath,
|
||||
});
|
||||
@@ -135,5 +135,35 @@ describe('RendererProtocolManager', () => {
|
||||
expect(resolveRendererFilePath).toHaveBeenCalledTimes(1);
|
||||
expect(response.status).toBe(404);
|
||||
});
|
||||
});
|
||||
|
||||
it('should support Range requests for media assets', async () => {
|
||||
const resolveRendererFilePath = vi.fn(async (_url: URL) => '/export/intro-video.mp4');
|
||||
const payload = Buffer.from('0123456789');
|
||||
|
||||
mockStat.mockImplementation(async () => ({ size: payload.length }));
|
||||
mockReadFile.mockImplementation(async () => payload);
|
||||
|
||||
const manager = new RendererProtocolManager({
|
||||
nextExportDir: '/export',
|
||||
resolveRendererFilePath,
|
||||
});
|
||||
|
||||
manager.registerHandler();
|
||||
const handler = protocolHandlerRef.current;
|
||||
|
||||
const response = await handler({
|
||||
headers: new Headers({ Range: 'bytes=0-1' }),
|
||||
method: 'GET',
|
||||
url: 'app://next/_next/static/media/intro-video.mp4',
|
||||
} as any);
|
||||
|
||||
expect(response.status).toBe(206);
|
||||
expect(response.headers.get('Accept-Ranges')).toBe('bytes');
|
||||
expect(response.headers.get('Content-Range')).toBe('bytes 0-1/10');
|
||||
expect(response.headers.get('Content-Length')).toBe('2');
|
||||
expect(response.headers.get('Content-Type')).toBe('video/mp4');
|
||||
|
||||
const buf = Buffer.from(await response.arrayBuffer());
|
||||
expect(buf.toString()).toBe('01');
|
||||
});
|
||||
});
|
||||
|
||||
@@ -0,0 +1,87 @@
|
||||
import { createEnv } from '@t3-oss/env-core';
|
||||
import { memoize } from 'es-toolkit';
|
||||
import { z } from 'zod';
|
||||
|
||||
const normalizeEnvString = (input: unknown) => {
|
||||
if (typeof input !== 'string') return undefined;
|
||||
const trimmed = input.trim();
|
||||
if (!trimmed) return undefined;
|
||||
return trimmed;
|
||||
};
|
||||
|
||||
const envBoolean = (defaultValue: boolean) =>
|
||||
z
|
||||
.preprocess((input) => {
|
||||
const str = normalizeEnvString(input);
|
||||
if (!str) return undefined;
|
||||
|
||||
switch (str.toLowerCase()) {
|
||||
case '1':
|
||||
case 'true':
|
||||
case 'yes':
|
||||
case 'y':
|
||||
case 'on': {
|
||||
return true;
|
||||
}
|
||||
|
||||
case '0':
|
||||
case 'false':
|
||||
case 'no':
|
||||
case 'n':
|
||||
case 'off': {
|
||||
return false;
|
||||
}
|
||||
|
||||
default: {
|
||||
return undefined;
|
||||
}
|
||||
}
|
||||
}, z.boolean().optional())
|
||||
.default(defaultValue);
|
||||
|
||||
const envNumber = (defaultValue: number) =>
|
||||
z
|
||||
.preprocess((input) => {
|
||||
const str = normalizeEnvString(input);
|
||||
if (!str) return undefined;
|
||||
const num = Number(str);
|
||||
if (!Number.isFinite(num)) return undefined;
|
||||
return num;
|
||||
}, z.number().optional())
|
||||
.default(defaultValue);
|
||||
|
||||
/**
|
||||
* Desktop (Electron main process) runtime env access.
|
||||
*
|
||||
* Important:
|
||||
* - Keep schemas tolerant (optional + defaults) to avoid throwing in tests/dev.
|
||||
* - Prefer reading env at call-time (factory) so tests can mutate process.env safely.
|
||||
*/
|
||||
export const getDesktopEnv = memoize(() =>
|
||||
createEnv({
|
||||
server: {
|
||||
DEBUG_VERBOSE: envBoolean(false),
|
||||
|
||||
// keep optional to preserve existing behavior:
|
||||
// - unset NODE_ENV should behave like "not production" in logger runtime paths
|
||||
NODE_ENV: z.enum(['development', 'production', 'test']).optional(),
|
||||
|
||||
// escape hatch: allow testing static renderer in dev via env
|
||||
DESKTOP_RENDERER_STATIC: envBoolean(false),
|
||||
|
||||
// updater
|
||||
UPDATE_CHANNEL: z.string().optional(),
|
||||
|
||||
// mcp client
|
||||
MCP_TOOL_TIMEOUT: envNumber(60_000),
|
||||
|
||||
// cloud server url (can be overridden for selfhost/dev)
|
||||
OFFICIAL_CLOUD_SERVER: z.string().optional().default('https://lobechat.com'),
|
||||
},
|
||||
clientPrefix: 'PUBLIC_',
|
||||
client: {},
|
||||
runtimeEnv: process.env,
|
||||
emptyStringAsUndefined: true,
|
||||
isServer: true,
|
||||
}),
|
||||
);
|
||||
@@ -7,12 +7,9 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/
|
||||
import type { Transport } from '@modelcontextprotocol/sdk/shared/transport.js';
|
||||
import type { Progress } from '@modelcontextprotocol/sdk/types.js';
|
||||
|
||||
import type { MCPClientParams, McpPrompt, McpResource, McpTool, ToolCallResult } from './types';
|
||||
import { getDesktopEnv } from '@/env';
|
||||
|
||||
const MCP_TOOL_TIMEOUT = (() => {
|
||||
const val = Number(process.env.MCP_TOOL_TIMEOUT);
|
||||
return Number.isFinite(val) && val > 0 ? val : 60_000;
|
||||
})();
|
||||
import type { MCPClientParams, McpPrompt, McpResource, McpTool, ToolCallResult } from './types';
|
||||
|
||||
export class MCPClient {
|
||||
private readonly mcp: Client;
|
||||
@@ -125,7 +122,7 @@ export class MCPClient {
|
||||
|
||||
async callTool(toolName: string, args: any): Promise<ToolCallResult> {
|
||||
const result = await this.mcp.callTool({ arguments: args, name: toolName }, undefined, {
|
||||
timeout: MCP_TOOL_TIMEOUT,
|
||||
timeout: getDesktopEnv().MCP_TOOL_TIMEOUT,
|
||||
});
|
||||
return result as ToolCallResult;
|
||||
}
|
||||
|
||||
@@ -1,34 +1,28 @@
|
||||
const common = {
|
||||
actions: {
|
||||
add: '添加',
|
||||
back: '返回',
|
||||
cancel: '取消',
|
||||
close: '关闭',
|
||||
confirm: '确认',
|
||||
delete: '删除',
|
||||
edit: '编辑',
|
||||
more: '更多',
|
||||
next: '下一步',
|
||||
ok: '确定',
|
||||
previous: '上一步',
|
||||
refresh: '刷新',
|
||||
remove: '移除',
|
||||
retry: '重试',
|
||||
save: '保存',
|
||||
search: '搜索',
|
||||
submit: '提交',
|
||||
},
|
||||
app: {
|
||||
description: '你的 AI 助手协作平台',
|
||||
name: 'LobeHub',
|
||||
},
|
||||
status: {
|
||||
error: '错误',
|
||||
info: '信息',
|
||||
loading: '加载中',
|
||||
success: '成功',
|
||||
warning: '警告',
|
||||
},
|
||||
'actions.add': 'Add',
|
||||
'actions.back': 'Back',
|
||||
'actions.cancel': 'Cancel',
|
||||
'actions.close': 'Close',
|
||||
'actions.confirm': 'Confirm',
|
||||
'actions.delete': 'Delete',
|
||||
'actions.edit': 'Edit',
|
||||
'actions.more': 'More',
|
||||
'actions.next': 'Next',
|
||||
'actions.ok': 'OK',
|
||||
'actions.previous': 'Previous',
|
||||
'actions.refresh': 'Refresh',
|
||||
'actions.remove': 'Remove',
|
||||
'actions.retry': 'Retry',
|
||||
'actions.save': 'Save',
|
||||
'actions.search': 'Search',
|
||||
'actions.submit': 'Submit',
|
||||
'app.description': 'For Collaborative Agents',
|
||||
'app.name': 'LobeHub',
|
||||
'status.error': 'Error',
|
||||
'status.info': 'Information',
|
||||
'status.loading': 'Loading',
|
||||
'status.success': 'Success',
|
||||
'status.warning': 'Warning',
|
||||
};
|
||||
|
||||
export default common;
|
||||
export default common;
|
||||
@@ -1,33 +1,25 @@
|
||||
const dialog = {
|
||||
about: {
|
||||
button: '确定',
|
||||
detail: '一个基于大语言模型的聊天应用',
|
||||
message: '{{appName}} {{appVersion}}',
|
||||
title: '关于',
|
||||
},
|
||||
confirm: {
|
||||
cancel: '取消',
|
||||
no: '否',
|
||||
title: '确认',
|
||||
yes: '是',
|
||||
},
|
||||
error: {
|
||||
button: '确定',
|
||||
detail: '操作过程中发生错误,请稍后重试',
|
||||
message: '发生错误',
|
||||
title: '错误',
|
||||
},
|
||||
update: {
|
||||
downloadAndInstall: '下载并安装',
|
||||
downloadComplete: '下载完成',
|
||||
downloadCompleteMessage: '更新包已下载完成,是否立即安装?',
|
||||
installLater: '稍后安装',
|
||||
installNow: '立即安装',
|
||||
later: '稍后提醒',
|
||||
newVersion: '发现新版本',
|
||||
newVersionAvailable: '发现新版本: {{version}}',
|
||||
skipThisVersion: '跳过此版本',
|
||||
},
|
||||
'about.button': 'OK',
|
||||
'about.detail': 'An LLM-powered chat app',
|
||||
'about.message': '{{appName}} {{appVersion}}',
|
||||
'about.title': 'About',
|
||||
'confirm.cancel': 'Cancel',
|
||||
'confirm.no': 'Cancel',
|
||||
'confirm.title': 'Please confirm',
|
||||
'confirm.yes': 'Continue',
|
||||
'error.button': 'OK',
|
||||
'error.detail': 'Couldn\'t complete the action. Retry or try again later.',
|
||||
'error.message': 'An error occurred',
|
||||
'error.title': 'Error',
|
||||
'update.downloadAndInstall': 'Download and Install',
|
||||
'update.downloadComplete': 'Download Complete',
|
||||
'update.downloadCompleteMessage': 'Update downloaded. Install now?',
|
||||
'update.installLater': 'Install Later',
|
||||
'update.installNow': 'Install Now',
|
||||
'update.later': 'Remind Me Later',
|
||||
'update.newVersion': 'New Version Found',
|
||||
'update.newVersionAvailable': 'New version: {{version}}',
|
||||
'update.skipThisVersion': 'Skip This Version',
|
||||
};
|
||||
|
||||
export default dialog;
|
||||
export default dialog;
|
||||
@@ -1,73 +1,64 @@
|
||||
const menu = {
|
||||
common: {
|
||||
checkUpdates: '检查更新...',
|
||||
},
|
||||
dev: {
|
||||
devPanel: '开发者面板',
|
||||
devTools: '开发者工具',
|
||||
forceReload: '强制重新加载',
|
||||
openStore: '打开存储文件',
|
||||
refreshMenu: '刷新菜单',
|
||||
reload: '重新加载',
|
||||
title: '开发',
|
||||
},
|
||||
edit: {
|
||||
copy: '复制',
|
||||
cut: '剪切',
|
||||
delete: '删除',
|
||||
paste: '粘贴',
|
||||
redo: '重做',
|
||||
selectAll: '全选',
|
||||
speech: '语音',
|
||||
startSpeaking: '开始朗读',
|
||||
stopSpeaking: '停止朗读',
|
||||
title: '编辑',
|
||||
undo: '撤销',
|
||||
},
|
||||
file: {
|
||||
preferences: '首选项',
|
||||
quit: '退出',
|
||||
title: '文件',
|
||||
},
|
||||
help: {
|
||||
about: '关于',
|
||||
githubRepo: 'GitHub 仓库',
|
||||
reportIssue: '报告问题',
|
||||
title: '帮助',
|
||||
visitWebsite: '访问官网',
|
||||
},
|
||||
macOS: {
|
||||
about: '关于 {{appName}}',
|
||||
devTools: 'LobeHub 开发者工具',
|
||||
hide: '隐藏 {{appName}}',
|
||||
hideOthers: '隐藏其他',
|
||||
preferences: '偏好设置...',
|
||||
services: '服务',
|
||||
unhide: '全部显示',
|
||||
},
|
||||
tray: {
|
||||
open: '打开 {{appName}}',
|
||||
quit: '退出',
|
||||
show: '显示 {{appName}}',
|
||||
},
|
||||
view: {
|
||||
forceReload: '强制重新加载',
|
||||
reload: '重新加载',
|
||||
resetZoom: '重置缩放',
|
||||
title: '视图',
|
||||
toggleFullscreen: '切换全屏',
|
||||
zoomIn: '放大',
|
||||
zoomOut: '缩小',
|
||||
},
|
||||
window: {
|
||||
bringAllToFront: '前置所有窗口',
|
||||
close: '关闭',
|
||||
front: '前置所有窗口',
|
||||
minimize: '最小化',
|
||||
title: '窗口',
|
||||
toggleFullscreen: '切换全屏',
|
||||
zoom: '缩放',
|
||||
},
|
||||
'common.checkUpdates': 'Check for updates...',
|
||||
'dev.devPanel': 'Developer Panel',
|
||||
'dev.devTools': 'Developer Tools',
|
||||
'dev.forceReload': 'Force Reload',
|
||||
'dev.openSettingsFile': 'Open Settings File',
|
||||
'dev.openStore': 'Open Data Folder',
|
||||
'dev.openUpdaterCacheDir': 'Open Updater Cache',
|
||||
'dev.openUserDataDir': 'Open User Data',
|
||||
'dev.refreshMenu': 'Refresh Menu',
|
||||
'dev.reload': 'Reload',
|
||||
'dev.simulateAutoDownload': 'Simulate Auto Download (3s)',
|
||||
'dev.simulateDownloadComplete': 'Simulate Download Complete',
|
||||
'dev.simulateDownloadProgress': 'Simulate Download Progress',
|
||||
'dev.title': 'Development',
|
||||
'dev.updaterSimulation': 'Updater Simulation',
|
||||
'edit.copy': 'Copy',
|
||||
'edit.cut': 'Cut',
|
||||
'edit.delete': 'Delete',
|
||||
'edit.paste': 'Paste',
|
||||
'edit.redo': 'Redo',
|
||||
'edit.selectAll': 'Select All',
|
||||
'edit.speech': 'Speech',
|
||||
'edit.startSpeaking': 'Start Speaking',
|
||||
'edit.stopSpeaking': 'Stop Speaking',
|
||||
'edit.title': 'Edit',
|
||||
'edit.undo': 'Undo',
|
||||
'file.preferences': 'Preferences',
|
||||
'file.quit': 'Quit',
|
||||
'file.title': 'File',
|
||||
'help.about': 'About',
|
||||
'help.githubRepo': 'GitHub Repository',
|
||||
'help.openConfigDir': 'Open Config Directory',
|
||||
'help.openLogsDir': 'Open Logs Directory',
|
||||
'help.reportIssue': 'Send Feedback',
|
||||
'help.title': 'Help',
|
||||
'help.visitWebsite': 'Open Website',
|
||||
'macOS.about': 'About {{appName}}',
|
||||
'macOS.devTools': 'LobeHub Developer Tools',
|
||||
'macOS.hide': 'Hide {{appName}}',
|
||||
'macOS.hideOthers': 'Hide Others',
|
||||
'macOS.preferences': 'Preferences...',
|
||||
'macOS.services': 'Services',
|
||||
'macOS.unhide': 'Show All',
|
||||
'tray.open': 'Open {{appName}}',
|
||||
'tray.quit': 'Quit',
|
||||
'tray.show': 'Show {{appName}}',
|
||||
'view.forceReload': 'Force Reload',
|
||||
'view.reload': 'Reload',
|
||||
'view.resetZoom': 'Reset Zoom',
|
||||
'view.title': 'View',
|
||||
'view.toggleFullscreen': 'Toggle Fullscreen',
|
||||
'view.zoomIn': 'Zoom In',
|
||||
'view.zoomOut': 'Zoom Out',
|
||||
'window.bringAllToFront': 'Bring All Windows to Front',
|
||||
'window.close': 'Close',
|
||||
'window.front': 'Bring All Windows to Front',
|
||||
'window.minimize': 'Minimize',
|
||||
'window.title': 'Window',
|
||||
'window.toggleFullscreen': 'Toggle Fullscreen',
|
||||
'window.zoom': 'Zoom',
|
||||
};
|
||||
|
||||
export default menu;
|
||||
export default menu;
|
||||
@@ -1,5 +1,3 @@
|
||||
import { isDev } from '@/const/env';
|
||||
|
||||
/**
|
||||
* 规范化语言代码
|
||||
*/
|
||||
@@ -11,10 +9,9 @@ export const normalizeLocale = (locale: string) => {
|
||||
* 按需加载翻译资源
|
||||
*/
|
||||
export const loadResources = async (lng: string, ns: string) => {
|
||||
// 开发环境下,直接使用中文源文件
|
||||
if (isDev && lng === 'zh-CN') {
|
||||
// All en-* locales fallback to 'en' and use default TypeScript files
|
||||
if (lng === 'en' || lng.startsWith('en-')) {
|
||||
try {
|
||||
// 使用 require 加载模块,这在 Electron 中更可靠
|
||||
const { default: content } = await import(`@/locales/default/${ns}.ts`);
|
||||
|
||||
return content;
|
||||
@@ -24,8 +21,6 @@ export const loadResources = async (lng: string, ns: string) => {
|
||||
}
|
||||
}
|
||||
|
||||
// 生产环境使用编译后的 JSON 文件
|
||||
|
||||
try {
|
||||
return await import(`@/../../resources/locales/${lng}/${ns}.json`);
|
||||
} catch (error) {
|
||||
|
||||
@@ -62,7 +62,7 @@ export class LinuxMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
click: () => {
|
||||
this.app.updaterManager.checkForUpdates({ manual: true });
|
||||
},
|
||||
label: t('common.checkUpdates') || '检查更新',
|
||||
label: t('common.checkUpdates'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Menu, app, shell } from 'electron';
|
||||
import { beforeEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import type { App } from '@/core/App';
|
||||
import menuTranslations from '@/locales/default/menu';
|
||||
|
||||
import { MacOSMenu } from './macOS';
|
||||
|
||||
@@ -34,53 +35,16 @@ vi.mock('@/const/env', () => ({
|
||||
// Mock App instance
|
||||
const createMockApp = () => {
|
||||
const mockT = vi.fn((key: string, params?: any) => {
|
||||
const translations: Record<string, string> = {
|
||||
'macOS.about': `About ${params?.appName || 'App'}`,
|
||||
'common.checkUpdates': 'Check for Updates',
|
||||
'macOS.preferences': 'Preferences',
|
||||
'macOS.services': 'Services',
|
||||
'macOS.hide': `Hide ${params?.appName || 'App'}`,
|
||||
'macOS.hideOthers': 'Hide Others',
|
||||
'macOS.unhide': 'Show All',
|
||||
'file.quit': 'Quit',
|
||||
'file.title': 'File',
|
||||
'file.preferences': 'Preferences',
|
||||
'window.close': 'Close Window',
|
||||
'window.title': 'Window',
|
||||
'window.minimize': 'Minimize',
|
||||
'edit.title': 'Edit',
|
||||
'edit.undo': 'Undo',
|
||||
'edit.redo': 'Redo',
|
||||
'edit.cut': 'Cut',
|
||||
'edit.copy': 'Copy',
|
||||
'edit.paste': 'Paste',
|
||||
'edit.selectAll': 'Select All',
|
||||
'edit.speech': 'Speech',
|
||||
'edit.startSpeaking': 'Start Speaking',
|
||||
'edit.stopSpeaking': 'Stop Speaking',
|
||||
'edit.delete': 'Delete',
|
||||
'view.title': 'View',
|
||||
'view.reload': 'Reload',
|
||||
'view.forceReload': 'Force Reload',
|
||||
'view.resetZoom': 'Actual Size',
|
||||
'view.zoomIn': 'Zoom In',
|
||||
'view.zoomOut': 'Zoom Out',
|
||||
'view.toggleFullscreen': 'Toggle Full Screen',
|
||||
'help.title': 'Help',
|
||||
'help.visitWebsite': 'Visit Website',
|
||||
'help.githubRepo': 'GitHub Repository',
|
||||
'help.reportIssue': 'Report Issue',
|
||||
'help.about': 'About',
|
||||
'dev.title': 'Developer',
|
||||
'dev.devPanel': 'Dev Panel',
|
||||
'dev.refreshMenu': 'Refresh Menu',
|
||||
'dev.devTools': 'Developer Tools',
|
||||
'dev.reload': 'Reload',
|
||||
'dev.forceReload': 'Force Reload',
|
||||
'tray.show': `Show ${params?.appName || 'App'}`,
|
||||
'tray.quit': 'Quit',
|
||||
};
|
||||
return translations[key] || key;
|
||||
let translation = menuTranslations[key as keyof typeof menuTranslations] || key;
|
||||
if (params && typeof translation === 'string') {
|
||||
Object.keys(params).forEach((paramKey) => {
|
||||
translation = translation.replace(
|
||||
new RegExp(`{{${paramKey}}}`, 'g'),
|
||||
params[paramKey] as string,
|
||||
);
|
||||
});
|
||||
}
|
||||
return translation;
|
||||
});
|
||||
|
||||
return {
|
||||
@@ -133,20 +97,20 @@ describe('MacOSMenu', () => {
|
||||
});
|
||||
|
||||
it('should include developer menu when showDevItems is true', () => {
|
||||
const menu = macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
||||
macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
||||
|
||||
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const devMenu = template.find((item: any) => item.label === 'Developer');
|
||||
const devMenu = template.find((item: any) => item.label === 'Development');
|
||||
expect(devMenu).toBeDefined();
|
||||
});
|
||||
|
||||
it('should not include developer menu when showDevItems is false', () => {
|
||||
const menu = macOSMenu.buildAndSetAppMenu({ showDevItems: false });
|
||||
macOSMenu.buildAndSetAppMenu({ showDevItems: false });
|
||||
|
||||
expect(Menu.buildFromTemplate).toHaveBeenCalled();
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const devMenu = template.find((item: any) => item.label === 'Developer');
|
||||
const devMenu = template.find((item: any) => item.label === 'Development');
|
||||
expect(devMenu).toBeUndefined();
|
||||
});
|
||||
|
||||
@@ -229,7 +193,7 @@ describe('MacOSMenu', () => {
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const appMenu = template[0];
|
||||
const checkUpdatesItem = appMenu.submenu.find(
|
||||
(item: any) => item.label === 'Check for Updates',
|
||||
(item: any) => item.label === 'Check for updates...',
|
||||
);
|
||||
|
||||
expect(checkUpdatesItem).toBeDefined();
|
||||
@@ -242,7 +206,7 @@ describe('MacOSMenu', () => {
|
||||
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const appMenu = template[0];
|
||||
const preferencesItem = appMenu.submenu.find((item: any) => item.label === 'Preferences');
|
||||
const preferencesItem = appMenu.submenu.find((item: any) => item.label === 'Preferences...');
|
||||
|
||||
expect(preferencesItem).toBeDefined();
|
||||
await preferencesItem.click();
|
||||
@@ -254,7 +218,7 @@ describe('MacOSMenu', () => {
|
||||
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const helpMenu = template.find((item: any) => item.label === 'Help');
|
||||
const visitWebsiteItem = helpMenu.submenu.find((item: any) => item.label === 'Visit Website');
|
||||
const visitWebsiteItem = helpMenu.submenu.find((item: any) => item.label === 'Open Website');
|
||||
|
||||
expect(visitWebsiteItem).toBeDefined();
|
||||
await visitWebsiteItem.click();
|
||||
@@ -278,7 +242,7 @@ describe('MacOSMenu', () => {
|
||||
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const helpMenu = template.find((item: any) => item.label === 'Help');
|
||||
const logsItem = helpMenu.submenu.find((item: any) => item.label === '打开日志目录');
|
||||
const logsItem = helpMenu.submenu.find((item: any) => item.label === 'Open Logs Directory');
|
||||
|
||||
expect(logsItem).toBeDefined();
|
||||
logsItem.click();
|
||||
@@ -304,7 +268,7 @@ describe('MacOSMenu', () => {
|
||||
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const appMenu = template[0];
|
||||
const preferencesItem = appMenu.submenu.find((item: any) => item.label === 'Preferences');
|
||||
const preferencesItem = appMenu.submenu.find((item: any) => item.label === 'Preferences...');
|
||||
|
||||
expect(preferencesItem.accelerator).toBe('Command+,');
|
||||
});
|
||||
@@ -335,8 +299,8 @@ describe('MacOSMenu', () => {
|
||||
macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
||||
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const devMenu = template.find((item: any) => item.label === 'Developer');
|
||||
const devPanelItem = devMenu.submenu.find((item: any) => item.label === 'Dev Panel');
|
||||
const devMenu = template.find((item: any) => item.label === 'Development');
|
||||
const devPanelItem = devMenu.submenu.find((item: any) => item.label === 'Developer Panel');
|
||||
|
||||
expect(devPanelItem).toBeDefined();
|
||||
});
|
||||
@@ -345,8 +309,8 @@ describe('MacOSMenu', () => {
|
||||
macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
||||
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const devMenu = template.find((item: any) => item.label === 'Developer');
|
||||
const devPanelItem = devMenu.submenu.find((item: any) => item.label === 'Dev Panel');
|
||||
const devMenu = template.find((item: any) => item.label === 'Development');
|
||||
const devPanelItem = devMenu.submenu.find((item: any) => item.label === 'Developer Panel');
|
||||
|
||||
devPanelItem.click();
|
||||
expect(mockApp.browserManager.retrieveByIdentifier).toHaveBeenCalledWith('devtools');
|
||||
@@ -356,7 +320,7 @@ describe('MacOSMenu', () => {
|
||||
macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
||||
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const devMenu = template.find((item: any) => item.label === 'Developer');
|
||||
const devMenu = template.find((item: any) => item.label === 'Development');
|
||||
const refreshMenuItem = devMenu.submenu.find((item: any) => item.label === 'Refresh Menu');
|
||||
|
||||
refreshMenuItem.click();
|
||||
@@ -367,8 +331,8 @@ describe('MacOSMenu', () => {
|
||||
macOSMenu.buildAndSetAppMenu({ showDevItems: true });
|
||||
|
||||
const template = (Menu.buildFromTemplate as any).mock.calls[0][0];
|
||||
const devMenu = template.find((item: any) => item.label === 'Developer');
|
||||
const updaterMenu = devMenu.submenu.find((item: any) => item.label === '自动更新测试模拟');
|
||||
const devMenu = template.find((item: any) => item.label === 'Development');
|
||||
const updaterMenu = devMenu.submenu.find((item: any) => item.label === 'Updater Simulation');
|
||||
|
||||
expect(updaterMenu).toBeDefined();
|
||||
expect(updaterMenu.submenu).toBeInstanceOf(Array);
|
||||
|
||||
@@ -69,8 +69,12 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
label: appName,
|
||||
submenu: [
|
||||
{
|
||||
click: async () => {
|
||||
const mainWindow = this.app.browserManager.getMainWindow();
|
||||
mainWindow.show();
|
||||
mainWindow.broadcast('navigate', { path: '/settings/about' });
|
||||
},
|
||||
label: t('macOS.about', { appName }),
|
||||
role: 'about',
|
||||
},
|
||||
{
|
||||
click: () => {
|
||||
@@ -198,7 +202,7 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
// Optionally show an error dialog to the user
|
||||
});
|
||||
},
|
||||
label: '打开日志目录',
|
||||
label: t('help.openLogsDir'),
|
||||
},
|
||||
{
|
||||
click: () => {
|
||||
@@ -209,7 +213,7 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
// Optionally show an error dialog to the user
|
||||
});
|
||||
},
|
||||
label: '配置目录',
|
||||
label: t('help.openConfigDir'),
|
||||
},
|
||||
],
|
||||
},
|
||||
@@ -239,7 +243,7 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
console.error(`[Menu] Error opening path ${userDataPath}:`, err);
|
||||
});
|
||||
},
|
||||
label: '用户配置目录',
|
||||
label: t('dev.openUserDataDir'),
|
||||
},
|
||||
{
|
||||
click: () => {
|
||||
@@ -251,35 +255,35 @@ export class MacOSMenu extends BaseMenuPlatform implements IMenuPlatform {
|
||||
console.error(`[Menu] Error opening path ${updaterCachePath}:`, err);
|
||||
});
|
||||
},
|
||||
label: '更新缓存目录',
|
||||
label: t('dev.openUpdaterCacheDir'),
|
||||
},
|
||||
{
|
||||
click: () => {
|
||||
this.app.storeManager.openInEditor();
|
||||
},
|
||||
label: '打开 Settings 配置文件',
|
||||
label: t('dev.openSettingsFile'),
|
||||
},
|
||||
{ type: 'separator' },
|
||||
{
|
||||
label: '自动更新测试模拟',
|
||||
label: t('dev.updaterSimulation'),
|
||||
submenu: [
|
||||
{
|
||||
click: () => {
|
||||
this.app.updaterManager.simulateUpdateAvailable();
|
||||
},
|
||||
label: '模拟启动后台自动下载更新(3s 下完)',
|
||||
label: t('dev.simulateAutoDownload'),
|
||||
},
|
||||
{
|
||||
click: () => {
|
||||
this.app.updaterManager.simulateDownloadProgress();
|
||||
},
|
||||
label: '模拟下载进度',
|
||||
label: t('dev.simulateDownloadProgress'),
|
||||
},
|
||||
{
|
||||
click: () => {
|
||||
this.app.updaterManager.simulateUpdateDownloaded();
|
||||
},
|
||||
label: '模拟下载完成',
|
||||
label: t('dev.simulateDownloadComplete'),
|
||||
},
|
||||
],
|
||||
},
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user