Skip to content

Commit 02ab763

Browse files
v0.8.5
# v0.8.5 — Multi-Language Support & Pi SDK Upgrade ## Features - **Multi-language support (i18n)** — The entire UI is now localized. Ships with **English**, **Spanish (es)**, **Simplified Chinese (zh-Hans)**, and **Japanese (ja)** — over 1,050 translated strings covering every page, menu, toast, tooltip, and dialog. Switch languages in Settings > Appearance. Session titles and AI responses also follow the selected language. - **Pi SDK upgrade (0.56.2 → 0.66.1)** — Major upgrade spanning multiple upstream releases. Models like **GLM 5**, **GLM 5.1**, and **Minimax 2.7** should now work thanks to upstream fixes. Also picks up: Bedrock throttling no longer misidentified as context overflow, Anthropic HTTP 413 detection for compaction/retry, Z.ai tool streaming support, OpenAI streaming fixes, and bash output truncation fix. (Fixes #503, addresses #513) ## Improvements - **Canonical locale registry** — Adding a new language is now a single-file change. The registry auto-derives all language codes, display names, i18n resources, and date-fns locales. - **Unified language setting** — Removed the duplicate free-text "Language" field from Preferences. The Appearance language dropdown is now the single source of truth for both UI language and AI response language. - **i18n developer tooling** — Pre-commit hook catches hardcoded English strings in staged `.tsx` files. Locale parity test ensures all translations stay in sync. `localize-agents` skill automates adding new languages. ## Bug Fixes - **Session messages lost after PC wake** — After sleep/wake, if the server subprocess died, session messages were lost and the content area appeared empty. Fixed with retry logic, lazy-loading fallback, and active session message re-fetch. ## Known Limitations - **Headless server responses are in English** — When using the WebUI connected to a remote headless server, the UI is fully localized (based on browser language), but the AI agent still responds in English. Session titles are also generated in English. Per-client language support for the headless server will be added in a future release. ## Breaking Changes - The free-text "Language" field in Preferences has been removed. Use the language dropdown in Settings > Appearance instead. Existing `preferences.json` files with a `language` field are safely ignored.
1 parent dfb2b90 commit 02ab763

176 files changed

Lines changed: 7546 additions & 1866 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

apps/cli/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@craft-agent/cli",
3-
"version": "0.8.4",
3+
"version": "0.8.5",
44
"license": "Apache-2.0",
55
"description": "Terminal client for Craft Agent server",
66
"type": "module",

apps/electron/package.json

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@craft-agent/electron",
3-
"version": "0.8.4",
3+
"version": "0.8.5",
44
"description": "Electron desktop app for Craft Agents",
55
"main": "dist/main.cjs",
66
"private": true,
@@ -37,8 +37,8 @@
3737
},
3838
"dependencies": {
3939
"@craft-agent/core": "workspace:*",
40-
"@craft-agent/shared": "workspace:*",
4140
"@craft-agent/server-core": "workspace:*",
41+
"@craft-agent/shared": "workspace:*",
4242
"@craft-agent/ui": "workspace:*",
4343
"@dnd-kit/core": "^6.3.1",
4444
"@dnd-kit/sortable": "^10.0.0",
@@ -56,12 +56,14 @@
5656
"cmdk": "^1.1.1",
5757
"electron-log": "^5.4.3",
5858
"electron-updater": "^6.8.0",
59+
"i18next-browser-languagedetector": "^8.2.1",
5960
"jotai-family": "^1.0.1",
6061
"motion": "^12.23.26",
6162
"next-themes": "^0.4.6",
6263
"react": "^18.3.1",
6364
"react-day-picker": "^9.13.0",
6465
"react-dom": "^18.3.1",
66+
"react-i18next": "^17.0.2",
6567
"react-pdf": "^10.3.0",
6668
"react-simple-code-editor": "^0.14.1",
6769
"remark": "^15.0.1",
Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
# v0.8.5 — Multi-Language Support & Pi SDK Upgrade
2+
3+
## Features
4+
- **Multi-language support (i18n)** — The entire UI is now localized. Ships with **English**, **Spanish (es)**, **Simplified Chinese (zh-Hans)**, and **Japanese (ja)** — over 1,050 translated strings covering every page, menu, toast, tooltip, and dialog. Switch languages in Settings > Appearance. Session titles and AI responses also follow the selected language.
5+
- **Pi SDK upgrade (0.56.2 → 0.66.1)** — Major upgrade spanning multiple upstream releases. Models like **GLM 5**, **GLM 5.1**, and **Minimax 2.7** should now work thanks to upstream fixes. Also picks up: Bedrock throttling no longer misidentified as context overflow, Anthropic HTTP 413 detection for compaction/retry, Z.ai tool streaming support, OpenAI streaming fixes, and bash output truncation fix. (Fixes #503, addresses #513)
6+
7+
## Improvements
8+
- **Canonical locale registry** — Adding a new language is now a single-file change. The registry auto-derives all language codes, display names, i18n resources, and date-fns locales.
9+
- **Unified language setting** — Removed the duplicate free-text "Language" field from Preferences. The Appearance language dropdown is now the single source of truth for both UI language and AI response language.
10+
- **i18n developer tooling** — Pre-commit hook catches hardcoded English strings in staged `.tsx` files. Locale parity test ensures all translations stay in sync. `localize-agents` skill automates adding new languages.
11+
12+
## Bug Fixes
13+
- **Session messages lost after PC wake** — After sleep/wake, if the server subprocess died, session messages were lost and the content area appeared empty. Fixed with retry logic, lazy-loading fallback, and active session message re-fetch.
14+
15+
## Known Limitations
16+
- **Headless server responses are in English** — When using the WebUI connected to a remote headless server, the UI is fully localized (based on browser language), but the AI agent still responds in English. Session titles are also generated in English. Per-client language support for the headless server will be added in a future release.
17+
18+
## Breaking Changes
19+
- The free-text "Language" field in Preferences has been removed. Use the language dropdown in Settings > Appearance instead. Existing `preferences.json` files with a `language` field are safely ignored.

apps/electron/src/main/index.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,10 @@ Sentry.init({
5858
},
5959
})
6060

61+
// Initialize i18n for main process (menus, dialogs, etc.)
62+
import { setupI18n, i18n } from '@craft-agent/shared/i18n'
63+
setupI18n()
64+
6165
// Set anonymous machine ID for Sentry user tracking (no PII — just a hash).
6266
// Uses hostname + homedir to produce a stable per-machine identifier.
6367
const machineId = createHash('sha256').update(hostname() + homedir()).digest('hex').slice(0, 16)
@@ -693,6 +697,13 @@ app.whenReady().then(async () => {
693697
app.exit(0)
694698
})
695699

700+
// Language change: sync from renderer to main process and rebuild native menu
701+
ipcMain.handle('i18n:changeLanguage', async (_event, lang: string) => {
702+
i18n.changeLanguage(lang)
703+
const { rebuildMenu } = await import('./menu')
704+
await rebuildMenu()
705+
})
706+
696707
ipcMain.on('__get-ws-port', (e) => {
697708
e.returnValue = instance.port
698709
})

apps/electron/src/main/menu.ts

Lines changed: 26 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { Menu, app, shell, BrowserWindow } from 'electron'
2+
import { i18n } from '@craft-agent/shared/i18n'
23
import { RPC_CHANNELS, type BroadcastEventMap } from '../shared/types'
34
import { EDIT_MENU, VIEW_MENU, WINDOW_MENU } from '../shared/menu-schema'
45
import type { MenuItem } from '../shared/menu-schema'
@@ -63,13 +64,13 @@ export async function rebuildMenu(): Promise<void> {
6364
// Build the update menu item based on state
6465
const updateMenuItem: Electron.MenuItemConstructorOptions = updateReady
6566
? {
66-
label: `Install Update…\t【${updateInfo.latestVersion}】`,
67+
label: i18n.t("menu.installUpdateVersion", { version: updateInfo.latestVersion }),
6768
click: async () => {
6869
await installUpdate()
6970
}
7071
}
7172
: {
72-
label: 'Check for Updates…',
73+
label: i18n.t("menu.checkForUpdatesEllipsis"),
7374
click: async () => {
7475
await checkForUpdates({ autoDownload: true })
7576
}
@@ -80,36 +81,36 @@ export async function rebuildMenu(): Promise<void> {
8081
...(isMac ? [{
8182
label: 'Craft Agents',
8283
submenu: [
83-
{ role: 'about' as const, label: 'About Craft Agents' },
84+
{ role: 'about' as const, label: i18n.t('menu.aboutCraftAgents') },
8485
updateMenuItem,
8586
{ type: 'separator' as const },
8687
{
87-
label: 'Settings...',
88+
label: i18n.t("menu.settings"),
8889
accelerator: 'CmdOrCtrl+,',
8990
registerAccelerator: false, // Action registry handles the keyboard shortcut
9091
click: () => sendToRenderer(RPC_CHANNELS.menu.OPEN_SETTINGS)
9192
},
9293
{ type: 'separator' as const },
93-
{ role: 'hide' as const, label: 'Hide Craft Agents' },
94+
{ role: 'hide' as const, label: i18n.t('menu.hideCraftAgents') },
9495
{ role: 'hideOthers' as const },
9596
{ role: 'unhide' as const },
9697
{ type: 'separator' as const },
97-
{ role: 'quit' as const, label: 'Quit Craft Agents' }
98+
{ role: 'quit' as const, label: i18n.t('menu.quitCraftAgents') }
9899
]
99100
}] : []),
100101

101102
// File menu
102103
{
103-
label: 'File',
104+
label: i18n.t("menu.file"),
104105
submenu: [
105106
{
106-
label: 'New Chat',
107+
label: i18n.t("menu.newChat"),
107108
accelerator: 'CmdOrCtrl+N',
108109
registerAccelerator: false, // Action registry handles the keyboard shortcut
109110
click: () => sendToRenderer(RPC_CHANNELS.menu.NEW_CHAT)
110111
},
111112
{
112-
label: 'New Window',
113+
label: i18n.t("menu.newWindow"),
113114
accelerator: 'CmdOrCtrl+Shift+N',
114115
registerAccelerator: false, // Action registry handles the keyboard shortcut
115116
click: () => {
@@ -129,21 +130,21 @@ export async function rebuildMenu(): Promise<void> {
129130

130131
// Edit menu (from shared schema)
131132
{
132-
label: EDIT_MENU.label,
133+
label: i18n.t(EDIT_MENU.labelKey),
133134
submenu: EDIT_MENU.items.map(toElectronMenuItem),
134135
},
135136

136137
// View menu (from shared schema + dev-only items)
137138
{
138-
label: VIEW_MENU.label,
139+
label: i18n.t(VIEW_MENU.labelKey),
139140
submenu: [
140141
...VIEW_MENU.items.map(toElectronMenuItem),
141142
// Dev tools — available in dev mode or when started with --debug
142143
...(!app.isPackaged || isDebugMode ? [
143144
{ type: 'separator' as const },
144145
...(!app.isPackaged ? [
145146
{
146-
label: 'Reload',
147+
label: i18n.t("menu.reload"),
147148
accelerator: 'CmdOrCtrl+R',
148149
click: (_menuItem: Electron.MenuItem, window: Electron.BaseWindow | undefined) => {
149150
const browserWindow = window instanceof BrowserWindow ? window : BrowserWindow.getFocusedWindow()
@@ -157,7 +158,7 @@ export async function rebuildMenu(): Promise<void> {
157158
}
158159
},
159160
{
160-
label: 'Force Reload',
161+
label: i18n.t("menu.forceReload"),
161162
accelerator: 'CmdOrCtrl+Shift+R',
162163
click: (_menuItem: Electron.MenuItem, window: Electron.BaseWindow | undefined) => {
163164
const browserWindow = window instanceof BrowserWindow ? window : BrowserWindow.getFocusedWindow()
@@ -178,7 +179,7 @@ export async function rebuildMenu(): Promise<void> {
178179

179180
// Window menu (from shared schema + macOS-specific items)
180181
{
181-
label: WINDOW_MENU.label,
182+
label: i18n.t(WINDOW_MENU.labelKey),
182183
submenu: [
183184
...WINDOW_MENU.items.map(toElectronMenuItem),
184185
...(isMac ? [
@@ -190,18 +191,18 @@ export async function rebuildMenu(): Promise<void> {
190191

191192
// Debug menu (development only)
192193
...(!app.isPackaged ? [{
193-
label: 'Debug',
194+
label: i18n.t("menu.debug"),
194195
submenu: [
195196
{
196-
label: 'Check for Updates',
197+
label: i18n.t("menu.checkForUpdates"),
197198
click: async () => {
198199
const { checkForUpdates } = await import('./auto-update')
199200
const info = await checkForUpdates({ autoDownload: true })
200201
mainLog.info('[debug-menu] Update check result:', info)
201202
}
202203
},
203204
{
204-
label: 'Install Update',
205+
label: i18n.t("menu.installUpdate"),
205206
click: async () => {
206207
const { installUpdate } = await import('./auto-update')
207208
try {
@@ -213,14 +214,14 @@ export async function rebuildMenu(): Promise<void> {
213214
},
214215
{ type: 'separator' as const },
215216
{
216-
label: 'Reset to Defaults...',
217+
label: i18n.t("menu.resetToDefaults"),
217218
click: async () => {
218219
const { dialog } = await import('electron')
219220
await dialog.showMessageBox({
220221
type: 'info',
221-
message: 'Reset to Defaults',
222-
detail: 'To reset Craft Agent to defaults, quit the app and run:\n\nbun run fresh-start\n\nThis will delete all configuration, credentials, workspaces, and sessions.',
223-
buttons: ['OK']
222+
message: i18n.t("menu.resetToDefaultsTitle"),
223+
detail: i18n.t("menu.resetToDefaultsDetail"),
224+
buttons: [i18n.t("common.ok")]
224225
})
225226
}
226227
}
@@ -229,14 +230,14 @@ export async function rebuildMenu(): Promise<void> {
229230

230231
// Help menu
231232
{
232-
label: 'Help',
233+
label: i18n.t("menu.help"),
233234
submenu: [
234235
{
235-
label: 'Help & Documentation',
236+
label: i18n.t("menu.helpAndDocs"),
236237
click: () => shell.openExternal('https://agents.craft.do/docs')
237238
},
238239
{
239-
label: 'Keyboard Shortcuts',
240+
label: i18n.t("menu.keyboardShortcuts"),
240241
accelerator: 'CmdOrCtrl+/',
241242
registerAccelerator: false, // Action registry handles the keyboard shortcut
242243
click: () => sendToRenderer(RPC_CHANNELS.menu.KEYBOARD_SHORTCUTS)
@@ -281,7 +282,7 @@ function toElectronMenuItem(item: MenuItem): Electron.MenuItemConstructorOptions
281282

282283
if (item.type === 'action') {
283284
return {
284-
label: item.label,
285+
label: i18n.t(item.labelKey),
285286
accelerator: item.shortcut,
286287
registerAccelerator: false, // Action registry handles the keyboard shortcut
287288
click: () => sendToRenderer(item.ipcChannel as MenuBroadcastChannel),

apps/electron/src/preload/bootstrap.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -414,4 +414,7 @@ client.onConnectionStateChanged((state) => {
414414
downloadUrl: process.env.CRAFT_VCREDIST_URL,
415415
})
416416

417+
// i18n: sync language changes to main process (for native menus/dialogs)
418+
;(api as ElectronAPI).changeLanguage = (lang: string) => ipcRenderer.invoke('i18n:changeLanguage', lang)
419+
417420
contextBridge.exposeInMainWorld('electronAPI', api)

0 commit comments

Comments
 (0)