clauderipple 0.8.3 → 0.8.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -167,6 +167,310 @@ const STRINGS = {
167
167
  updatePackaged: "독립 실행 앱입니다. 릴리스 페이지에서 새 버전을 받으세요.",
168
168
  updateCheckout: "소스 체크아웃입니다. git pull로 업데이트하세요.",
169
169
  },
170
+ zh: {
171
+ healthy: "运行正常",
172
+ attentionNeeded: "需要处理",
173
+ routerNotRunning: "路由服务未运行",
174
+ slotsMapped: (n) => `已映射 ${n} 个槽位`,
175
+ inFlight: "进行中",
176
+ connected: "Claude Desktop 已连接",
177
+ notConnected: "Claude Desktop 未连接",
178
+ percentOfWeekUsed: (percent, plan) => `ChatGPT ${plan} 本周已用 ${percent}%`,
179
+ cliVersion: (v) => `Claude Code CLI ${v}`,
180
+ restartRouter: "重启路由服务",
181
+ startRouter: "启动路由服务",
182
+ pickerOn: "在 Code 标签页的模型选择器中显示 GPT 模型…",
183
+ pickerOff: "不再在模型选择器中显示 GPT 模型…",
184
+ pickerState: (on) => (on ? "模型选择器:以真实名称显示 GPT 模型 ✓" : "模型选择器:通过 Claude 模型名使用 GPT 模型(别名模式)"),
185
+ pickerOnConfirm: "macOS 会要求输入登录密码以信任 ClaudeRipple 证书(ClaudeRipple 永远不会看到密码)。之后请退出并重新打开 Claude Desktop。",
186
+ pickerDone: "完成。现在请完全退出 Claude Desktop 再重新打开,然后查看 Code 标签页的模型选择器。",
187
+ pickerOffDone: "完成。退出并重新打开 Claude Desktop 后生效。",
188
+ cancel: "取消",
189
+ agentTitleOn: "在子代理上显示模型名称",
190
+ agentTitleOff: "不再在子代理上显示模型名称",
191
+ agentTitleDone: (on) => (on ? "已开启。新的子代理标题将类似于“Terra·high · …”。" : "已关闭。"),
192
+ signInChatgpt: "登录 ChatGPT…",
193
+ connectClaudeSubscription: "连接 Claude 订阅…",
194
+ copyStatus: "复制状态",
195
+ showLogs: "查看日志",
196
+ rerunSetup: "重新运行 ClaudeRipple 设置…",
197
+ setupTitle: "设置 ClaudeRipple",
198
+ setupPrompt: "ClaudeRipple 将创建本地证书、配置 Claude Code 以连接 ClaudeRipple(~/.claude/settings.json),并注册后台路由服务。是否继续?",
199
+ setupLater: "稍后",
200
+ setupContinue: "继续",
201
+ setupDone: "ClaudeRipple 设置完成。",
202
+ setupFailed: "设置未完成",
203
+ updatedTitle: (v) => `ClaudeRipple ${v}`,
204
+ updatedRestarting: (from) => `正在运行的路由服务版本为 ${from},正在切换到当前版本…`,
205
+ updatedFailed: "无法将路由服务切换到当前版本",
206
+ problem: (code) => code === "settings" ? "Claude Code 未指向 ClaudeRipple"
207
+ : code === "upstream" ? "无法连接 Anthropic"
208
+ : code === "picker-ca" ? "选择器模式:证书未被信任"
209
+ : code === "picker-proxy" ? "选择器模式:未设置 Claude Desktop 代理"
210
+ : code.startsWith("provider:") ? `无法连接供应商 ${code.slice(9)}`
211
+ : code,
212
+ about: "关于 ClaudeRipple",
213
+ aboutDetail: "在 Claude Desktop 中运行 GPT 和其他模型,无需关闭 Claude。\n\n独立开源项目(GPL-3.0)。与 Anthropic 和 OpenAI 均无关联,未获其认可或赞助。Claude 和 Claude Code 是 Anthropic, PBC 的商标。",
214
+ quit: "退出",
215
+ tooltip: (state) => `ClaudeRipple ${state}`,
216
+ tooltipDown: "ClaudeRipple:路由服务未运行 — Claude Desktop 无法连接",
217
+ proxyLine: (host, port, slots, flight) => `代理 ${host}:${port} · ${slots} · ${flight}`,
218
+ startAtLogin: "登录时启动",
219
+ downNotifyTitle: "Claude Desktop 无法连接",
220
+ downNotifyBody: "ClaudeRipple 路由服务未运行,因此 Claude Desktop 无法向外发送请求。点击即可启动路由服务。",
221
+ starting: "正在启动路由服务…",
222
+ openDashboard: "在浏览器中打开控制面板…",
223
+ offlineHeading: "路由服务未运行",
224
+ offlineBody: "Claude Desktop 的所有流量都经过 ClaudeRipple,因此路由服务停止期间,应用会显示空白窗口并报 ERR_PROXY_CONNECTION_FAILED。请启动路由服务,然后重新加载 Claude Desktop。",
225
+ setupNeeded: "设置未完成",
226
+ setupNeededDetail: "ClaudeRipple 已安装,但尚未完成设置",
227
+ runSetup: "完成 ClaudeRipple 设置…",
228
+ claudeCurrent: "当前登录",
229
+ claudeUsage: (label, windows) => `Claude ${label} · ${windows}`,
230
+ window5h: "5 小时",
231
+ windowWeek: "每周",
232
+ checkUpdates: "检查更新…",
233
+ updateAvailableItem: (v) => `更新到 ClaudeRipple ${v}…`,
234
+ upToDate: (v) => `ClaudeRipple ${v} 已是最新版本。`,
235
+ updateAvailable: (latest, current) => `ClaudeRipple ${latest} 已发布(当前版本 ${current})。`,
236
+ updateDetail: "进行中的请求处理完毕后,路由服务会以新版本重启。托盘应用会自动重新打开。",
237
+ updateNow: "更新",
238
+ later: "稍后",
239
+ updating: (v) => `正在更新到 ClaudeRipple ${v}…`,
240
+ updateDone: (v) => `已更新到 ClaudeRipple ${v}`,
241
+ updateFailed: "更新未完成",
242
+ updateCheckFailed: "无法检查更新",
243
+ updatePackaged: "这是独立打包的应用。请从发布页面下载新版本。",
244
+ updateCheckout: "这是从仓库检出的源码版本,请用 git pull 更新。",
245
+ },
246
+ ja: {
247
+ healthy: "正常",
248
+ attentionNeeded: "要確認",
249
+ routerNotRunning: "ルーター停止中",
250
+ slotsMapped: (n) => `${n}個のスロットをマッピング済み`,
251
+ inFlight: "処理中",
252
+ connected: "Claude Desktop 接続済み",
253
+ notConnected: "Claude Desktop 未接続",
254
+ percentOfWeekUsed: (percent, plan) => `ChatGPT ${plan} 今週 ${percent}% 使用`,
255
+ cliVersion: (v) => `Claude Code CLI ${v}`,
256
+ restartRouter: "ルーターを再起動",
257
+ startRouter: "ルーターを起動",
258
+ pickerOn: "CodeタブのピッカーにGPTモデルを表示…",
259
+ pickerOff: "ピッカーのGPTモデルを非表示にする…",
260
+ pickerState: (on) => (on ? "ピッカー:GPTモデルを実際の名前で表示 ✓" : "ピッカー:Claudeの名前経由でGPTモデルを使用(エイリアスモード)"),
261
+ pickerOnConfirm: "ClaudeRippleの証明書を信頼するため、macOSがログインパスワードを求めます(ClaudeRippleがパスワードを見ることはありません)。その後、Claude Desktopを終了して開き直してください。",
262
+ pickerDone: "完了しました。Claude Desktopを完全に終了してから開き直し、Codeタブのピッカーを確認してください。",
263
+ pickerOffDone: "完了しました。反映するには、Claude Desktopを終了して開き直してください。",
264
+ cancel: "キャンセル",
265
+ agentTitleOn: "サブエージェントにモデル名を表示",
266
+ agentTitleOff: "サブエージェントのモデル名を非表示にする",
267
+ agentTitleDone: (on) => (on ? "オンにしました。新しいサブエージェントには「Terra·high · …」のようなタイトルが付きます。" : "オフにしました。"),
268
+ signInChatgpt: "ChatGPTにログイン…",
269
+ connectClaudeSubscription: "Claudeサブスクリプションを接続…",
270
+ copyStatus: "ステータスをコピー",
271
+ showLogs: "ログを表示",
272
+ rerunSetup: "ClaudeRippleのセットアップを再実行…",
273
+ setupTitle: "ClaudeRippleのセットアップ",
274
+ setupPrompt: "ClaudeRippleがローカル証明書を作成し、Claude Codeの接続設定(~/.claude/settings.json)を行い、バックグラウンドのルーターを登録します。続けますか?",
275
+ setupLater: "後で",
276
+ setupContinue: "続ける",
277
+ setupDone: "ClaudeRippleのセットアップが完了しました。",
278
+ setupFailed: "セットアップが完了しませんでした",
279
+ updatedTitle: (v) => `ClaudeRipple ${v}`,
280
+ updatedRestarting: (from) => `実行中のルーターは ${from} です。このバージョンに切り替えています…`,
281
+ updatedFailed: "ルーターをこのバージョンに切り替えられませんでした",
282
+ problem: (code) => code === "settings" ? "Claude Codeの接続先がClaudeRippleになっていません"
283
+ : code === "upstream" ? "Anthropicに接続できません"
284
+ : code === "picker-ca" ? "ピッカーモード:証明書が信頼されていません"
285
+ : code === "picker-proxy" ? "ピッカーモード:Claude Desktopのプロキシが未設定です"
286
+ : code.startsWith("provider:") ? `プロバイダー ${code.slice(9)} に接続できません`
287
+ : code,
288
+ about: "ClaudeRippleについて",
289
+ aboutDetail: "Claudeをオフにすることなく、Claude Desktopの中でGPTなどのモデルを動かします。\n\n独立したオープンソースプロジェクトです(GPL-3.0)。AnthropicおよびOpenAIとは提携関係になく、両社による承認や後援も受けていません。ClaudeおよびClaude Codeは、Anthropic, PBCの商標です。",
290
+ quit: "終了",
291
+ tooltip: (state) => `ClaudeRipple ${state}`,
292
+ tooltipDown: "ClaudeRipple:ルーター停止中 — Claude Desktopは接続できません",
293
+ proxyLine: (host, port, slots, flight) => `プロキシ ${host}:${port} · ${slots} · ${flight}`,
294
+ startAtLogin: "ログイン時に起動",
295
+ downNotifyTitle: "Claude Desktopが接続できません",
296
+ downNotifyBody: "ClaudeRippleのルーターが停止しているため、Claude Desktopは外部と通信できません。クリックして起動してください。",
297
+ starting: "ルーターを起動中…",
298
+ openDashboard: "ブラウザでダッシュボードを開く…",
299
+ offlineHeading: "ルーターが停止しています",
300
+ offlineBody: "Claude Desktopはすべての通信をClaudeRipple経由で送るため、ルーターが停止している間はERR_PROXY_CONNECTION_FAILEDと表示された空白のウィンドウになります。ルーターを起動してから、Claude Desktopを再読み込みしてください。",
301
+ setupNeeded: "セットアップ未完了",
302
+ setupNeededDetail: "ClaudeRippleはインストール済みですが、まだセットアップされていません",
303
+ runSetup: "ClaudeRippleのセットアップを完了…",
304
+ claudeCurrent: "現在のログイン",
305
+ claudeUsage: (label, windows) => `Claude ${label} · ${windows}`,
306
+ window5h: "5時間",
307
+ windowWeek: "週間",
308
+ checkUpdates: "アップデートを確認…",
309
+ updateAvailableItem: (v) => `ClaudeRipple ${v} にアップデート…`,
310
+ upToDate: (v) => `ClaudeRipple ${v} は最新バージョンです。`,
311
+ updateAvailable: (latest, current) => `ClaudeRipple ${latest} が利用可能です(現在のバージョン:${current})。`,
312
+ updateDetail: "処理中のリクエストが終わり次第、ルーターは新しいバージョンで再起動します。トレイアプリは自動で再起動します。",
313
+ updateNow: "アップデート",
314
+ later: "後で",
315
+ updating: (v) => `ClaudeRipple ${v} にアップデート中…`,
316
+ updateDone: (v) => `ClaudeRipple ${v} にアップデートしました`,
317
+ updateFailed: "アップデートが完了しませんでした",
318
+ updateCheckFailed: "アップデートを確認できませんでした",
319
+ updatePackaged: "これはスタンドアロン版のアプリです。リリースページから新しいバージョンをダウンロードしてください。",
320
+ updateCheckout: "これはソースから実行している環境です。git pullで更新してください。",
321
+ },
322
+ es: {
323
+ healthy: "en buen estado",
324
+ attentionNeeded: "requiere atención",
325
+ routerNotRunning: "router detenido",
326
+ slotsMapped: (n) => `${n} ${n === 1 ? "slot asignado" : "slots asignados"}`,
327
+ inFlight: "en curso",
328
+ connected: "Claude Desktop conectado",
329
+ notConnected: "Claude Desktop no conectado",
330
+ percentOfWeekUsed: (percent, plan) => `ChatGPT ${plan} ${percent}% esta semana`,
331
+ cliVersion: (v) => `Claude Code CLI ${v}`,
332
+ restartRouter: "Reiniciar router",
333
+ startRouter: "Iniciar router",
334
+ pickerOn: "Mostrar modelos GPT en el selector de la pestaña Code…",
335
+ pickerOff: "Dejar de mostrar modelos GPT en el selector…",
336
+ pickerState: (on) => (on ? "Selector: modelos GPT con su nombre ✓" : "Selector: modelos GPT bajo nombres de Claude (modo alias)"),
337
+ pickerOnConfirm: "macOS te pedirá tu contraseña de inicio de sesión para confiar en el certificado de ClaudeRipple (ClaudeRipple nunca la ve). Después, cierra Claude Desktop por completo y vuelve a abrirlo.",
338
+ pickerDone: "Listo. Ahora cierra Claude Desktop por completo, vuelve a abrirlo y revisa el selector de la pestaña Code.",
339
+ pickerOffDone: "Listo. Cierra Claude Desktop por completo y vuelve a abrirlo para aplicarlo.",
340
+ cancel: "Cancelar",
341
+ agentTitleOn: "Mostrar nombres de modelo en los subagentes",
342
+ agentTitleOff: "Dejar de mostrar nombres de modelo en los subagentes",
343
+ agentTitleDone: (on) => (on ? "Activado. Los subagentes nuevos tendrán títulos como \"Terra·high · …\"." : "Desactivado."),
344
+ signInChatgpt: "Iniciar sesión en ChatGPT…",
345
+ connectClaudeSubscription: "Conectar suscripción de Claude…",
346
+ copyStatus: "Copiar estado",
347
+ showLogs: "Mostrar registros",
348
+ rerunSetup: "Volver a ejecutar la configuración de ClaudeRipple…",
349
+ setupTitle: "Configurar ClaudeRipple",
350
+ setupPrompt: "ClaudeRipple creará un certificado local, conectará la configuración de Claude Code (~/.claude/settings.json) y registrará el router en segundo plano. ¿Continuar?",
351
+ setupLater: "Más tarde",
352
+ setupContinue: "Continuar",
353
+ setupDone: "Configuración de ClaudeRipple completada.",
354
+ setupFailed: "La configuración no se completó",
355
+ updatedTitle: (v) => `ClaudeRipple ${v}`,
356
+ updatedRestarting: (from) => `El router en ejecución está en la versión ${from}. Cambiándolo a esta versión…`,
357
+ updatedFailed: "No se pudo cambiar el router a esta versión",
358
+ problem: (code) => code === "settings" ? "Claude Code no apunta a ClaudeRipple"
359
+ : code === "upstream" ? "No se puede acceder a Anthropic"
360
+ : code === "picker-ca" ? "modo selector: el certificado no es de confianza"
361
+ : code === "picker-proxy" ? "modo selector: proxy de Claude Desktop sin configurar"
362
+ : code.startsWith("provider:") ? `no se puede acceder al proveedor ${code.slice(9)}`
363
+ : code,
364
+ about: "Acerca de ClaudeRipple",
365
+ aboutDetail: "Usa GPT y otros modelos dentro de Claude Desktop sin desactivar Claude.\n\nProyecto independiente de código abierto (GPL-3.0). No está afiliado a Anthropic ni a OpenAI, ni cuenta con su respaldo ni con su patrocinio. Claude y Claude Code son marcas comerciales de Anthropic, PBC.",
366
+ quit: "Salir",
367
+ tooltip: (state) => `ClaudeRipple ${state}`,
368
+ tooltipDown: "ClaudeRipple: router detenido — Claude Desktop no puede conectarse",
369
+ proxyLine: (host, port, slots, flight) => `Proxy ${host}:${port} · ${slots} · ${flight}`,
370
+ startAtLogin: "Abrir al iniciar sesión",
371
+ downNotifyTitle: "Claude Desktop no puede conectarse",
372
+ downNotifyBody: "El router de ClaudeRipple no está en ejecución, así que Claude Desktop no puede conectarse. Haz clic para iniciarlo.",
373
+ starting: "iniciando el router…",
374
+ openDashboard: "Abrir el panel en el navegador…",
375
+ offlineHeading: "El router no está en ejecución",
376
+ offlineBody: "Claude Desktop envía todo su tráfico a través de ClaudeRipple, así que mientras el router esté detenido la app muestra una ventana en blanco con ERR_PROXY_CONNECTION_FAILED. Inicia el router y recarga Claude Desktop.",
377
+ setupNeeded: "configuración pendiente",
378
+ setupNeededDetail: "ClaudeRipple está instalado, pero aún no está configurado",
379
+ runSetup: "Terminar de configurar ClaudeRipple…",
380
+ claudeCurrent: "sesión actual",
381
+ claudeUsage: (label, windows) => `Claude ${label} · ${windows}`,
382
+ window5h: "5 h",
383
+ windowWeek: "semanal",
384
+ checkUpdates: "Buscar actualizaciones…",
385
+ updateAvailableItem: (v) => `Actualizar a ClaudeRipple ${v}…`,
386
+ upToDate: (v) => `ClaudeRipple ${v} es la última versión.`,
387
+ updateAvailable: (latest, current) => `ClaudeRipple ${latest} está disponible (tienes la ${current}).`,
388
+ updateDetail: "El router se reinicia con la nueva versión cuando terminan las solicitudes en curso. La app de la bandeja se vuelve a abrir sola.",
389
+ updateNow: "Actualizar",
390
+ later: "Más tarde",
391
+ updating: (v) => `Actualizando a ClaudeRipple ${v}…`,
392
+ updateDone: (v) => `Actualizado a ClaudeRipple ${v}`,
393
+ updateFailed: "La actualización no se completó",
394
+ updateCheckFailed: "No se pudo comprobar si hay actualizaciones",
395
+ updatePackaged: "Esta es la app independiente. Descarga la nueva versión desde la página de versiones.",
396
+ updateCheckout: "Esta es una copia del código fuente. Actualízala con git pull.",
397
+ },
398
+ pt: {
399
+ healthy: "funcionando",
400
+ attentionNeeded: "requer atenção",
401
+ routerNotRunning: "roteador parado",
402
+ slotsMapped: (n) => `${n} slot${n === 1 ? "" : "s"} mapeado${n === 1 ? "" : "s"}`,
403
+ inFlight: "em andamento",
404
+ connected: "Claude Desktop conectado",
405
+ notConnected: "Claude Desktop não conectado",
406
+ percentOfWeekUsed: (percent, plan) => `ChatGPT ${plan} ${percent}% nesta semana`,
407
+ cliVersion: (v) => `Claude Code CLI ${v}`,
408
+ restartRouter: "Reiniciar roteador",
409
+ startRouter: "Iniciar roteador",
410
+ pickerOn: "Mostrar modelos GPT no seletor da aba Code…",
411
+ pickerOff: "Parar de mostrar modelos GPT no seletor…",
412
+ pickerState: (on) => (on ? "Seletor: modelos GPT exibidos pelo nome ✓" : "Seletor: modelos GPT via nomes do Claude (modo alias)"),
413
+ pickerOnConfirm: "O macOS vai pedir sua senha de login para confiar no certificado do ClaudeRipple (o ClaudeRipple nunca a vê). Depois, encerre o Claude Desktop e abra-o de novo.",
414
+ pickerDone: "Pronto. Agora encerre o Claude Desktop por completo, abra-o de novo e confira o seletor da aba Code.",
415
+ pickerOffDone: "Pronto. Encerre o Claude Desktop e abra-o de novo para aplicar.",
416
+ cancel: "Cancelar",
417
+ agentTitleOn: "Mostrar nomes de modelo nos subagentes",
418
+ agentTitleOff: "Parar de mostrar nomes de modelo nos subagentes",
419
+ agentTitleDone: (on) => (on ? "Ativado. Novos subagentes terão títulos como \"Terra·high · …\"." : "Desativado."),
420
+ signInChatgpt: "Entrar no ChatGPT…",
421
+ connectClaudeSubscription: "Conectar assinatura do Claude…",
422
+ copyStatus: "Copiar status",
423
+ showLogs: "Mostrar logs",
424
+ rerunSetup: "Configurar o ClaudeRipple novamente…",
425
+ setupTitle: "Configurar o ClaudeRipple",
426
+ setupPrompt: "O ClaudeRipple vai criar um certificado local, conectar as configurações do Claude Code (~/.claude/settings.json) e registrar o roteador em segundo plano. Continuar?",
427
+ setupLater: "Depois",
428
+ setupContinue: "Continuar",
429
+ setupDone: "Configuração do ClaudeRipple concluída.",
430
+ setupFailed: "A configuração não foi concluída",
431
+ updatedTitle: (v) => `ClaudeRipple ${v}`,
432
+ updatedRestarting: (from) => `O roteador em execução está na versão ${from}. Mudando para esta versão…`,
433
+ updatedFailed: "Não foi possível mudar o roteador para esta versão",
434
+ problem: (code) => code === "settings" ? "O Claude Code não está configurado para usar o ClaudeRipple"
435
+ : code === "upstream" ? "A Anthropic está inacessível"
436
+ : code === "picker-ca" ? "modo seletor: certificado não confiável"
437
+ : code === "picker-proxy" ? "modo seletor: proxy do Claude Desktop não configurado"
438
+ : code.startsWith("provider:") ? `provedor ${code.slice(9)} inacessível`
439
+ : code,
440
+ about: "Sobre o ClaudeRipple",
441
+ aboutDetail: "Rode o GPT e outros modelos dentro do Claude Desktop, sem desligar o Claude.\n\nProjeto open source independente (GPL-3.0). Não tem afiliação com a Anthropic nem com a OpenAI, nem é endossado ou patrocinado por nenhuma delas. Claude e Claude Code são marcas comerciais da Anthropic, PBC.",
442
+ quit: "Sair",
443
+ tooltip: (state) => `ClaudeRipple ${state}`,
444
+ tooltipDown: "ClaudeRipple: roteador parado — o Claude Desktop não consegue se conectar",
445
+ proxyLine: (host, port, slots, flight) => `Proxy ${host}:${port} · ${slots} · ${flight}`,
446
+ startAtLogin: "Iniciar ao fazer login",
447
+ downNotifyTitle: "O Claude Desktop não consegue se conectar",
448
+ downNotifyBody: "O roteador do ClaudeRipple não está rodando, então o Claude Desktop não consegue acessar a rede. Clique para iniciá-lo.",
449
+ starting: "iniciando o roteador…",
450
+ openDashboard: "Abrir painel no navegador…",
451
+ offlineHeading: "O roteador não está rodando",
452
+ offlineBody: "O Claude Desktop envia todo o tráfego pelo ClaudeRipple, então, enquanto o roteador estiver parado, o app mostra uma janela em branco com ERR_PROXY_CONNECTION_FAILED. Inicie o roteador e recarregue o Claude Desktop.",
453
+ setupNeeded: "configuração não concluída",
454
+ setupNeededDetail: "O ClaudeRipple está instalado, mas ainda não foi configurado",
455
+ runSetup: "Concluir a configuração do ClaudeRipple…",
456
+ claudeCurrent: "login atual",
457
+ claudeUsage: (label, windows) => `Claude ${label} · ${windows}`,
458
+ window5h: "5h",
459
+ windowWeek: "semanal",
460
+ checkUpdates: "Verificar atualizações…",
461
+ updateAvailableItem: (v) => `Atualizar para o ClaudeRipple ${v}…`,
462
+ upToDate: (v) => `O ClaudeRipple ${v} é a versão mais recente.`,
463
+ updateAvailable: (latest, current) => `O ClaudeRipple ${latest} está disponível (você tem a ${current}).`,
464
+ updateDetail: "O roteador reinicia na nova versão assim que as requisições em andamento terminarem. O app da bandeja reabre sozinho.",
465
+ updateNow: "Atualizar",
466
+ later: "Depois",
467
+ updating: (v) => `Atualizando para o ClaudeRipple ${v}…`,
468
+ updateDone: (v) => `Atualizado para o ClaudeRipple ${v}`,
469
+ updateFailed: "A atualização não foi concluída",
470
+ updateCheckFailed: "Não foi possível verificar atualizações",
471
+ updatePackaged: "Este é o app independente. Baixe a nova versão na página de releases.",
472
+ updateCheckout: "Isto é um checkout do código-fonte. Atualize com git pull.",
473
+ },
170
474
  };
171
475
  let L = STRINGS.en;
172
476
  function readConfigPorts() {
@@ -236,7 +540,9 @@ function adminUrl() {
236
540
  function uiLang() {
237
541
  const candidates = [...electron_1.app.getPreferredSystemLanguages(), electron_1.app.getSystemLocale(), electron_1.app.getLocale()];
238
542
  const first = candidates.find((l) => typeof l === "string" && l.length > 0) ?? "en";
239
- return first.toLowerCase().startsWith("ko") ? "ko" : "en";
543
+ // Every Chinese locale gets the Simplified text and every Portuguese one the Brazilian text.
544
+ const base = first.toLowerCase().split(/[-_]/)[0];
545
+ return base in STRINGS ? base : "en";
240
546
  }
241
547
  /** The GUI follows the OS language by default; a language the user picked in the GUI itself wins (it is stored in the page). */
242
548
  function guiUrl() {
@@ -1,5 +1,5 @@
1
1
  {
2
2
  "private": true,
3
- "version": "0.8.3",
3
+ "version": "0.8.4",
4
4
  "type": "commonjs"
5
5
  }
@@ -5,4 +5,4 @@
5
5
  // from the manifests. Until 0.1.1 the router and the CLI each carried their own literal, and both
6
6
  // still said "0.1.0" in the 0.1.1 release: after an update nobody could tell which router was
7
7
  // running, and the tray app had nothing to compare (2026-09-15, reported from a Windows install).
8
- export const VERSION = "0.8.3";
8
+ export const VERSION = "0.8.4";
package/dist/ui/app.js CHANGED
@@ -170,11 +170,17 @@ window.addEventListener("hashchange", () => showView((location.hash || "#health"
170
170
  showView((location.hash || "#health").slice(1) || "health");
171
171
 
172
172
  (function setupLanguage() {
173
- const button = $("#lang-toggle");
173
+ const select = $("#lang-toggle");
174
174
  const lang = (typeof CURRENT_LANG !== "undefined" && CURRENT_LANG) || "en";
175
- button.textContent = lang === "ko" ? t("lang.toggleEn") : t("lang.toggleKo");
176
- button.addEventListener("click", () => {
177
- try { localStorage.setItem("clauderipple_lang", lang === "ko" ? "en" : "ko"); } catch { /* unavailable */ }
175
+ for (const [code, name] of LANGUAGES) {
176
+ const option = document.createElement("option");
177
+ option.value = code;
178
+ option.textContent = name;
179
+ option.selected = code === lang;
180
+ select.append(option);
181
+ }
182
+ select.addEventListener("change", () => {
183
+ try { localStorage.setItem("clauderipple_lang", select.value); } catch { /* unavailable */ }
178
184
  location.reload();
179
185
  });
180
186
  })();