diff --git a/src/main/app/create-window.ts b/src/main/app/create-window.ts index 7637a22b..c383e648 100644 --- a/src/main/app/create-window.ts +++ b/src/main/app/create-window.ts @@ -6,6 +6,8 @@ export interface CreateWindowOptions { preloadPath: string; rendererDist: string; devServerUrl?: string; + /** UI language for renderer strings; injected synchronously via argv. */ + language: string; } export function createMainWindow(options: CreateWindowOptions): BrowserWindow { @@ -27,6 +29,7 @@ export function createMainWindow(options: CreateWindowOptions): BrowserWindow { sandbox: true, webSecurity: true, allowRunningInsecureContent: false, + additionalArguments: [`--app-language=${options.language}`], }, }); diff --git a/src/main/index.ts b/src/main/index.ts index 1b6fbd96..670beed2 100644 --- a/src/main/index.ts +++ b/src/main/index.ts @@ -23,7 +23,8 @@ if (!app.requestSingleInstanceLock()) { // strings, date formatting) is only settable pre-ready, and it follows the // stored language preference instead of the OS locale. const connection = new DatabaseConnection(join(profileDir, "agentcompany.db")); - app.commandLine.appendSwitch("lang", storedLanguage(connection.db) === "zh" ? "zh-CN" : "en-US"); + const language = storedLanguage(connection.db); + app.commandLine.appendSwitch("lang", language === "zh" ? "zh-CN" : "en-US"); app.whenReady().then(() => { connection.transaction(() => recoverInterruptedWork(connection.db)); @@ -31,6 +32,7 @@ if (!app.requestSingleInstanceLock()) { preloadPath: join(__dirname, "../preload/index.js"), rendererDist: join(__dirname, "../renderer"), devServerUrl: process.env.ELECTRON_RENDERER_URL, + language, }); registerIpcHandlers({ diff --git a/src/preload/index.ts b/src/preload/index.ts index cb3eccc7..6a0d9b9e 100644 --- a/src/preload/index.ts +++ b/src/preload/index.ts @@ -94,6 +94,7 @@ const api: DesktopApi = { return () => ipcRenderer.removeListener(IPC_CHANNELS.rendererEvent, listener); }, platform: process.platform as DesktopApi["platform"], + language: (process.argv.find((arg) => arg.startsWith("--app-language="))?.slice("--app-language=".length) === "zh" ? "zh" : "en") as DesktopApi["language"], windowMinimize: () => invoke(IPC_CHANNELS.windowMinimize), windowMaximize: () => invoke(IPC_CHANNELS.windowMaximize), windowClose: () => invoke(IPC_CHANNELS.windowClose), diff --git a/src/renderer/src/app/Onboarding.tsx b/src/renderer/src/app/Onboarding.tsx index facd6e07..73d39a24 100644 --- a/src/renderer/src/app/Onboarding.tsx +++ b/src/renderer/src/app/Onboarding.tsx @@ -8,6 +8,7 @@ import { Button, Field, Icon, IconButton, Pill } from "../shared/ui"; import type { SnapshotActions } from "./useSnapshot"; import { ProviderStep } from "./OnboardingProviderStep"; import { ConnectorsStep } from "./OnboardingConnectorsStep"; +import { t } from "../i18n/i18n"; type Step = "welcome" | "provider" | "company" | "mode" | "mission" | "connectors"; @@ -26,12 +27,12 @@ export interface Draft { } const STEPS: Array<{ id: Step; label: string }> = [ - { id: "welcome", label: "Start" }, - { id: "provider", label: "Provider" }, - { id: "company", label: "Company" }, - { id: "mode", label: "Mode" }, - { id: "mission", label: "Goal" }, - { id: "connectors", label: "Connectors" }, + { id: "welcome", label: t("Start") }, + { id: "provider", label: t("Provider") }, + { id: "company", label: t("Company") }, + { id: "mode", label: t("Mode") }, + { id: "mission", label: t("Goal") }, + { id: "connectors", label: t("Connectors") }, ]; export function Onboarding({ @@ -133,9 +134,9 @@ export function Onboarding({
{!isMac && ( - void window.agentCompany.windowMinimize()} /> - void window.agentCompany.windowMaximize()} /> - void window.agentCompany.windowClose()} /> + void window.agentCompany.windowMinimize()} /> + void window.agentCompany.windowMaximize()} /> + void window.agentCompany.windowClose()} /> )}
@@ -147,13 +148,13 @@ export function Onboarding({
AgentCompany
{onCancel && step !== "connectors" && ( )} -

Start your AI company.

+

{t("Start your AI company.")}

- Stand up the workspace, bind the provider, and queue the first operating loop. + {t("Stand up the workspace, bind the provider, and queue the first operating loop.")}

{STEPS.map((entry) => ( @@ -162,14 +163,14 @@ export function Onboarding({
-
Runtime selected
+
{t("Runtime selected")}
{selectedProvider.label} {draft.providerModel && {draft.providerModel}}
- We never embed the key in the build. Credentials encrypt locally via OS-protected storage. + {t("We never embed the key in the build. Credentials encrypt locally via OS-protected storage.")}
@@ -233,19 +234,18 @@ interface StepProps { function WelcomeStep({ onNext }: { onNext: () => void }) { return (
-

Welcome

+

{t("Welcome")}

- AgentCompany sets up your operating loop in six steps. Bring an API key, pick a runtime - for the founding CEO, and we'll wire up the first goal, project, and task. + {t("AgentCompany sets up your operating loop in six steps. Bring an API key, pick a runtime for the founding CEO, and we'll wire up the first goal, project, and task.")}

    -
  • Connect one model provider (OpenAI, Anthropic, xAI, or Groq).
  • -
  • Name the company and the human board chair.
  • -
  • Pick execution mode and bind the workspace path.
  • -
  • State the first goal — we kick off the rest.
  • +
  • {t("Connect one model provider (OpenAI, Anthropic, xAI, or Groq).")}
  • +
  • {t("Name the company and the human board chair.")}
  • +
  • {t("Pick execution mode and bind the workspace path.")}
  • +
  • {t("State the first goal — we kick off the rest.")}
- +
); @@ -259,20 +259,20 @@ function CompanyStep({ draft, onChange, onBack, onNext }: StepProps) { }; return (
-

Company

- +

{t("Company")}

+ onChange({ companyName: event.target.value })} autoFocus required /> - + onChange({ employeeName: event.target.value })} required /> - +