- {doctorState.result.ok ? "Codex looks good" : "Codex issue detected"}
+ {doctorState.result.ok
+ ? t("settings.codex.doctor.okTitle")
+ : t("settings.codex.doctor.errorTitle")}
-
Version: {doctorState.result.version ?? "unknown"}
-
App-server: {doctorState.result.appServerOk ? "ok" : "failed"}
- Node:{" "}
+ {t("settings.codex.doctor.version")}:{" "}
+ {doctorState.result.version ?? t("settings.codex.doctor.unknown")}
+
+
+ {t("settings.codex.doctor.appServer")}:{" "}
+ {doctorState.result.appServerOk
+ ? t("settings.codex.doctor.ok")
+ : t("settings.codex.doctor.failed")}
+
+
+ {t("settings.codex.doctor.node")}:{" "}
{doctorState.result.nodeOk
- ? `ok (${doctorState.result.nodeVersion ?? "unknown"})`
- : "missing"}
+ ? `${t("settings.codex.doctor.ok")} (${doctorState.result.nodeVersion ?? t("settings.codex.doctor.unknown")})`
+ : t("settings.codex.doctor.missing")}
{doctorState.result.details &&
{doctorState.result.details}
}
{doctorState.result.nodeDetails &&
{doctorState.result.nodeDetails}
}
@@ -362,25 +383,25 @@ export function SettingsCodexSection({
{codexUpdateState.result.ok
? codexUpdateState.result.upgraded
- ? "Codex updated"
- : "Codex already up-to-date"
- : "Codex update failed"}
+ ? t("settings.codex.update.updated")
+ : t("settings.codex.update.upToDate")
+ : t("settings.codex.update.failed")}
-
Method: {codexUpdateState.result.method}
+
{t("settings.codex.update.method")}: {codexUpdateState.result.method}
{codexUpdateState.result.package && (
-
Package: {codexUpdateState.result.package}
+
{t("settings.codex.update.package")}: {codexUpdateState.result.package}
)}
- Version:{" "}
+ {t("settings.codex.doctor.version")}:{" "}
{codexUpdateState.result.afterVersion ??
codexUpdateState.result.beforeVersion ??
- "unknown"}
+ t("settings.codex.doctor.unknown")}
{codexUpdateState.result.details &&
{codexUpdateState.result.details}
}
{codexUpdateState.result.output && (
- output
+ {t("settings.codex.update.output")}
{codexUpdateState.result.output}
)}
@@ -391,23 +412,23 @@ export function SettingsCodexSection({
- Default parameters
+ {t("settings.codex.defaults.sectionTitle")}
- Model
+ {t("settings.codex.defaults.model.label")}
}
subtitle={
defaultModelsConnectedWorkspaceCount === 0
- ? "Add a workspace to load available models."
+ ? t("settings.codex.defaults.model.addWorkspace")
: defaultModelsLoading
- ? "Loading models from the first workspace…"
+ ? t("settings.codex.defaults.model.loading")
: defaultModelsError
- ? `Couldn’t load models: ${defaultModelsError}`
- : "Sourced from the first workspace and used when there is no thread-specific override."
+ ? t("settings.codex.defaults.model.loadError", { error: defaultModelsError })
+ : t("settings.codex.defaults.model.help")
}
>
@@ -422,7 +443,7 @@ export function SettingsCodexSection({
lastComposerModelId: event.target.value,
})
}
- aria-label="Model"
+ aria-label={t("settings.codex.defaults.model.label")}
>
{defaultModels.map((model) => (
@@ -436,7 +457,7 @@ export function SettingsCodexSection({
onClick={onRefreshDefaultModels}
disabled={defaultModelsLoading || defaultModelsConnectedWorkspaceCount === 0}
>
- Refresh
+ {t("settings.codex.defaults.model.refresh")}
@@ -444,13 +465,13 @@ export function SettingsCodexSection({
- Reasoning effort
+ {t("settings.codex.defaults.reasoning.label")}
}
subtitle={
reasoningSupported
- ? "Available options depend on the selected model."
- : "The selected model does not expose reasoning effort options."
+ ? t("settings.codex.defaults.reasoning.supported")
+ : t("settings.codex.defaults.reasoning.unsupported")
}
>
- {!reasoningSupported && not supported }
+ {!reasoningSupported && (
+ {t("settings.codex.defaults.reasoning.notSupported")}
+ )}
{reasoningOptions.map((effort) => (
{effort}
@@ -478,10 +501,10 @@ export function SettingsCodexSection({
- Access mode
+ {t("settings.codex.defaults.access.label")}
}
- subtitle="Used when there is no thread-specific override."
+ subtitle={t("settings.codex.defaults.access.help")}
>
- Read only
- On-request
- Full access
+ {t("settings.codex.defaults.access.option.readOnly")}
+ {t("settings.codex.defaults.access.option.current")}
+ {t("settings.codex.defaults.access.option.fullAccess")}
- Review mode
+ {t("settings.codex.review.label")}
- Inline (same thread)
- Detached (new review thread)
+ {t("settings.codex.review.option.inline")}
+ {t("settings.codex.review.option.detached")}
- Choose whether /review runs in the current thread or a detached review
- thread.
+ {t("settings.codex.review.help.before")}
+ /review
+ {t("settings.codex.review.help.after")}
- Stored at ~/.codex/AGENTS.md.
+ {t("settings.codex.file.storedAt")}
+ ~/.codex/AGENTS.md
+ {t("common.punctuation.period")}
>
}
classNames={{
@@ -555,11 +581,11 @@ export function SettingsCodexSection({
/>
- Stored at ~/.codex/config.toml.
+ {t("settings.codex.file.storedAt")}
+ ~/.codex/config.toml
+ {t("common.punctuation.period")}
>
}
classNames={{
diff --git a/src/features/settings/components/sections/SettingsComposerSection.tsx b/src/features/settings/components/sections/SettingsComposerSection.tsx
index ae3014bbc..f80137c6d 100644
--- a/src/features/settings/components/sections/SettingsComposerSection.tsx
+++ b/src/features/settings/components/sections/SettingsComposerSection.tsx
@@ -1,4 +1,5 @@
import type { AppSettings } from "@/types";
+import { useI18n } from "@/i18n/useI18n";
import {
SettingsSection,
SettingsToggleRow,
@@ -24,15 +25,16 @@ export function SettingsComposerSection({
onComposerPresetChange,
onUpdateAppSettings,
}: SettingsComposerSectionProps) {
+ const { t } = useI18n();
const steerUnavailable = !appSettings.steerEnabled;
return (
-
Follow-up behavior
-
+
{t("settings.composer.followUp.label")}
+
- Queue
+
+ {t("settings.composer.followUp.option.queue")}
+
- Steer
+
+ {t("settings.composer.followUp.option.steer")}
+
- Choose the default while a run is active. Press {followUpShortcutLabel} to send the
- opposite behavior for one message.
+ {t("settings.composer.followUp.help.before")}
+ {followUpShortcutLabel}
+ {t("settings.composer.followUp.help.after")}
{steerUnavailable && (
- Steer is unavailable in the current Codex config. Follow-ups will queue.
+ {t("settings.composer.followUp.steerUnavailableHelp")}
)}
-
Presets
-
- Choose a starting point and fine-tune the toggles below.
-
+
{t("settings.composer.presets.title")}
+
{t("settings.composer.presets.subtitle")}
- Preset
+ {t("settings.composer.presets.label")}
- Presets update the toggles below. Customize any setting after selecting.
+ {t("settings.composer.presets.help")}
-
Code fences
+
{t("settings.composer.codeFences.title")}
- When enabled, Copy is plain text. Hold {optionKeyLabel} to include ``` fences.
+ {t("settings.composer.codeFences.copyNoFence.subtitle.before")}
+ {optionKeyLabel}
+ {t("settings.composer.codeFences.copyNoFence.subtitle.after")}
>
}
>
@@ -208,10 +215,10 @@ export function SettingsComposerSection({
/>
-
Pasting
+
{t("settings.composer.pasting.title")}
-
Lists
+
{t("settings.composer.lists.title")}
- Dictation model
+ {t("settings.dictation.model.label")}
- {selectedDictationModel.note} Download size: {selectedDictationModel.size}.
+ {selectedDictationModel.note} {t("settings.dictation.model.downloadSize")}:{" "}
+ {selectedDictationModel.size}.
- Preferred dictation language
+ {t("settings.dictation.language.label")}
- Auto-detect only
- English
- Spanish
- French
- German
- Italian
- Portuguese
- Dutch
- Swedish
- Norwegian
- Danish
- Finnish
- Polish
- Turkish
- Russian
- Ukrainian
- Japanese
- Korean
- Chinese
+ {t("settings.dictation.language.autoDetect")}
+ {t("settings.dictation.language.en")}
+ {t("settings.dictation.language.es")}
+ {t("settings.dictation.language.fr")}
+ {t("settings.dictation.language.de")}
+ {t("settings.dictation.language.it")}
+ {t("settings.dictation.language.pt")}
+ {t("settings.dictation.language.nl")}
+ {t("settings.dictation.language.sv")}
+ {t("settings.dictation.language.no")}
+ {t("settings.dictation.language.da")}
+ {t("settings.dictation.language.fi")}
+ {t("settings.dictation.language.pl")}
+ {t("settings.dictation.language.tr")}
+ {t("settings.dictation.language.ru")}
+ {t("settings.dictation.language.uk")}
+ {t("settings.dictation.language.ja")}
+ {t("settings.dictation.language.ko")}
+ {t("settings.dictation.language.zh")}
- Auto-detect stays on; this nudges the decoder toward your preference.
+ {t("settings.dictation.language.help")}
- Hold-to-dictate key
+ {t("settings.dictation.holdKey.label")}
- Off
+ {t("settings.dictation.holdKey.off")}
{optionKeyLabel}
Shift
Control
{metaKeyLabel}
- Hold the key to start dictation, release to stop and process.
+ {t("settings.dictation.holdKey.help")}
{dictationModelStatus && (
-
Model status ({selectedDictationModel.label})
+
+ {t("settings.dictation.status.label")} ({selectedDictationModel.label})
+
- {dictationModelStatus.state === "ready" && "Ready for dictation."}
- {dictationModelStatus.state === "missing" && "Model not downloaded yet."}
- {dictationModelStatus.state === "downloading" && "Downloading model..."}
+ {dictationModelStatus.state === "ready" && t("settings.dictation.status.ready")}
+ {dictationModelStatus.state === "missing" && t("settings.dictation.status.missing")}
+ {dictationModelStatus.state === "downloading" &&
+ t("settings.dictation.status.downloading")}
{dictationModelStatus.state === "error" &&
- (dictationModelStatus.error ?? "Download error.")}
+ (dictationModelStatus.error ?? t("settings.dictation.status.errorFallback"))}
{dictationProgress && (
@@ -203,7 +209,7 @@ export function SettingsDictationSection({
onClick={onDownloadDictationModel}
disabled={!onDownloadDictationModel}
>
- Download model
+ {t("settings.dictation.actions.download")}
)}
{dictationModelStatus.state === "downloading" && (
@@ -213,7 +219,7 @@ export function SettingsDictationSection({
onClick={onCancelDictationDownload}
disabled={!onCancelDictationDownload}
>
- Cancel download
+ {t("settings.dictation.actions.cancelDownload")}
)}
{dictationReady && (
@@ -223,7 +229,7 @@ export function SettingsDictationSection({
onClick={onRemoveDictationModel}
disabled={!onRemoveDictationModel}
>
- Remove model
+ {t("settings.dictation.actions.removeModel")}
)}
diff --git a/src/features/settings/components/sections/SettingsDisplaySection.test.tsx b/src/features/settings/components/sections/SettingsDisplaySection.test.tsx
index 2f273d437..b81be2213 100644
--- a/src/features/settings/components/sections/SettingsDisplaySection.test.tsx
+++ b/src/features/settings/components/sections/SettingsDisplaySection.test.tsx
@@ -61,6 +61,56 @@ describe("SettingsDisplaySection", () => {
expect.objectContaining({ threadTitleAutogenerationEnabled: true }),
);
});
+
+ it("updates interface language", () => {
+ const onUpdateAppSettings = vi.fn(async () => {});
+
+ render(
+
{})}
+ onResetScale={vi.fn(async () => {})}
+ onSetUiFontDraft={vi.fn() as any}
+ onCommitUiFont={vi.fn(async () => {})}
+ onSetCodeFontDraft={vi.fn() as any}
+ onCommitCodeFont={vi.fn(async () => {})}
+ onSetCodeFontSizeDraft={vi.fn() as any}
+ onCommitCodeFontSize={vi.fn(async () => {})}
+ onTestNotificationSound={vi.fn()}
+ onTestSystemNotification={vi.fn()}
+ />,
+ );
+
+ fireEvent.change(screen.getByLabelText("Interface language"), {
+ target: { value: "zh-CN" },
+ });
+
+ expect(onUpdateAppSettings).toHaveBeenCalledWith(
+ expect.objectContaining({ uiLanguage: "zh-CN" }),
+ );
+ });
it("toggles unlimited chat history", () => {
const onUpdateAppSettings = vi.fn(async () => {});
diff --git a/src/features/settings/components/sections/SettingsDisplaySection.tsx b/src/features/settings/components/sections/SettingsDisplaySection.tsx
index 0dc2b9583..8641adffd 100644
--- a/src/features/settings/components/sections/SettingsDisplaySection.tsx
+++ b/src/features/settings/components/sections/SettingsDisplaySection.tsx
@@ -16,6 +16,7 @@ import {
clampChatScrollbackItems,
isChatScrollbackPreset,
} from "@utils/chatScrollback";
+import { useI18n } from "@/i18n/useI18n";
import {
SettingsSection,
SettingsToggleRow,
@@ -69,6 +70,8 @@ export function SettingsDisplaySection({
onTestNotificationSound,
onTestSystemNotification,
}: SettingsDisplaySectionProps) {
+ const { t } = useI18n();
+ const uiLanguage = appSettings.uiLanguage ?? "system";
const scrollbackUnlimited = appSettings.chatHistoryScrollbackItems === null;
const [scrollbackDraft, setScrollbackDraft] = useState(() => {
const value = appSettings.chatHistoryScrollbackItems;
@@ -160,16 +163,16 @@ export function SettingsDisplaySection({
return (
- Display
+ {t("settings.display.subsectionDisplayTitle")}
- Adjust how the window renders backgrounds and effects.
+ {t("settings.display.subsectionDisplaySubtitle")}
- Theme
+ {t("settings.display.theme.label")}
- System
- Light
- Dark
- Dim
+ {t("settings.display.theme.option.system")}
+ {t("settings.display.theme.option.light")}
+ {t("settings.display.theme.option.dark")}
+ {t("settings.display.theme.option.dim")}
+
+
+ {t("settings.display.uiLanguage.label")}
+
+
+ void onUpdateAppSettings({
+ ...appSettings,
+ uiLanguage: event.target.value as AppSettings["uiLanguage"],
+ })
+ }
+ >
+ {t("settings.display.uiLanguage.option.system")}
+ {t("settings.display.uiLanguage.option.en")}
+ {t("settings.display.uiLanguage.option.zh-CN")}
+
+
{t("settings.display.uiLanguage.help")}
+
- Chat
-
- Control how much conversation history is retained per thread.
-
+ {t("settings.display.subsectionChatTitle")}
+ {t("settings.display.subsectionChatSubtitle")}
- Scrollback preset
+ {t("settings.display.scrollbackPreset.label")}
- Custom
+ {t("settings.display.scrollbackPreset.option.custom")}
{CHAT_SCROLLBACK_PRESETS.map((value) => (
- {value === CHAT_SCROLLBACK_DEFAULT ? `${value} (Default)` : value}
+ {value === CHAT_SCROLLBACK_DEFAULT
+ ? t("settings.display.scrollbackPreset.option.default", { value })
+ : value}
))}
-
- Higher values keep more history but may increase memory usage. Use “Sync from
- server” on a thread to re-fetch older messages.
-
+
{t("settings.display.scrollbackPreset.help")}
onToggleTransparency(!reduceTransparency)}
/>
-
-
-
Interface scale
-
- {scaleShortcutText}
-
-
+
{scaleShortcutText}}
+ >
onSetScaleDraft(event.target.value)}
onBlur={() => {
void onCommitScale();
@@ -376,13 +397,13 @@ export function SettingsDisplaySection({
void onResetScale();
}}
>
- Reset
+ {t("common.reset")}
-
+
- UI font family
+ {t("settings.display.uiFontFamily.label")}
- Reset
+ {t("common.reset")}
-
- Applies to all UI text. Leave empty to use the default system font stack.
-
+
{t("settings.display.uiFontFamily.help")}
- Code font family
+ {t("settings.display.codeFontFamily.label")}
- Reset
+ {t("common.reset")}
-
Applies to git diffs and other mono-spaced readouts.
+
{t("settings.display.codeFontFamily.help")}
- Code font size
+ {t("settings.display.codeFontSize.label")}
-
{codeFontSizeDraft}px
+
+ {t("settings.display.codeFontSize.value", { size: codeFontSizeDraft })}
+
- Reset
+ {t("common.reset")}
-
Adjusts code and diff text size.
+
{t("settings.display.codeFontSize.help")}
+
+ {t("settings.display.subsectionSoundsTitle")}
+
+ {t("settings.display.subsectionSoundsSubtitle")}
- Sounds
- Control notification audio alerts.
- Test sound
+ {t("settings.display.testSound")}
- Test notification
+ {t("settings.display.testNotification")}
diff --git a/src/features/settings/components/sections/SettingsEnvironmentsSection.tsx b/src/features/settings/components/sections/SettingsEnvironmentsSection.tsx
index a04312c67..d247eb32e 100644
--- a/src/features/settings/components/sections/SettingsEnvironmentsSection.tsx
+++ b/src/features/settings/components/sections/SettingsEnvironmentsSection.tsx
@@ -1,7 +1,8 @@
import type { Dispatch, SetStateAction } from "react";
-import { SettingsSection } from "@/features/design-system/components/settings/SettingsPrimitives";
import type { WorkspaceInfo } from "@/types";
import { pushErrorToast } from "@services/toasts";
+import { useI18n } from "@/i18n/useI18n";
+import { SettingsSection } from "@/features/design-system/components/settings/SettingsPrimitives";
type SettingsEnvironmentsSectionProps = {
mainWorkspaces: WorkspaceInfo[];
@@ -28,18 +29,19 @@ export function SettingsEnvironmentsSection({
onSetEnvironmentDraftScript,
onSaveEnvironmentSetup,
}: SettingsEnvironmentsSectionProps) {
+ const { t } = useI18n();
return (
{mainWorkspaces.length === 0 ? (
- No projects yet.
+ {t("settings.environments.emptyNoProjects")}
) : (
<>
- Project
+ {t("settings.environments.project.label")}
-
Setup script
+
{t("settings.environments.setupScript.label")}
- Runs once in a dedicated terminal after each new worktree is created.
+ {t("settings.environments.setupScript.help")}
{environmentError ? (
{environmentError}
@@ -83,24 +85,22 @@ export function SettingsEnvironmentsSection({
const clipboard = typeof navigator === "undefined" ? null : navigator.clipboard;
if (!clipboard?.writeText) {
pushErrorToast({
- title: "Copy failed",
- message:
- "Clipboard access is unavailable in this environment. Copy the script manually instead.",
+ title: t("settings.environments.clipboard.copyFailedTitle"),
+ message: t("settings.environments.clipboard.unavailable"),
});
return;
}
void clipboard.writeText(environmentDraftScript).catch(() => {
pushErrorToast({
- title: "Copy failed",
- message:
- "Could not write to the clipboard. Copy the script manually instead.",
+ title: t("settings.environments.clipboard.copyFailedTitle"),
+ message: t("settings.environments.clipboard.writeFailed"),
});
});
}}
disabled={environmentSaving || environmentDraftScript.length === 0}
>
- Copy
+ {t("settings.environments.actions.copy")}
onSetEnvironmentDraftScript(environmentSavedScript ?? "")}
disabled={environmentSaving || !environmentDirty}
>
- Reset
+ {t("settings.environments.actions.reset")}
- {environmentSaving ? "Saving..." : "Save"}
+ {environmentSaving
+ ? t("settings.environments.actions.saving")
+ : t("settings.environments.actions.save")}
diff --git a/src/features/settings/components/sections/SettingsFeaturesSection.tsx b/src/features/settings/components/sections/SettingsFeaturesSection.tsx
index 9362ef311..565b9ea08 100644
--- a/src/features/settings/components/sections/SettingsFeaturesSection.tsx
+++ b/src/features/settings/components/sections/SettingsFeaturesSection.tsx
@@ -1,82 +1,59 @@
import type { CodexFeature } from "@/types";
+import type { SettingsFeaturesSectionProps } from "@settings/hooks/useSettingsFeaturesSection";
import {
SettingsSection,
- SettingsSubsection,
SettingsToggleRow,
SettingsToggleSwitch,
} from "@/features/design-system/components/settings/SettingsPrimitives";
-import type { SettingsFeaturesSectionProps } from "@settings/hooks/useSettingsFeaturesSection";
import { fileManagerName, openInFileManagerLabel } from "@utils/platformPaths";
+import { useI18n } from "@/i18n/useI18n";
-const FEATURE_DESCRIPTION_FALLBACKS: Record = {
- undo: "Create a ghost commit at each turn.",
- shell_tool: "Enable the default shell tool.",
- unified_exec: "Use the single unified PTY-backed exec tool.",
- shell_snapshot: "Enable shell snapshotting.",
- js_repl: "Enable JavaScript REPL tools backed by a persistent Node kernel.",
- js_repl_tools_only: "Only expose js_repl tools directly to the model.",
- web_search_request: "Deprecated. Use top-level web_search instead.",
- web_search_cached: "Deprecated. Use top-level web_search instead.",
- search_tool: "Removed legacy search flag kept for backward compatibility.",
- runtime_metrics: "Enable runtime metrics snapshots via a manual reader.",
- sqlite: "Persist rollout metadata to a local SQLite database.",
- memory_tool: "Enable startup memory extraction and memory consolidation.",
- child_agents_md: "Append additional AGENTS.md guidance to user instructions.",
- apply_patch_freeform: "Include the freeform apply_patch tool.",
- use_linux_sandbox_bwrap: "Use the bubblewrap-based Linux sandbox pipeline.",
- request_rule: "Allow approval requests and exec rule proposals.",
- experimental_windows_sandbox:
- "Removed Windows sandbox flag kept for backward compatibility.",
- elevated_windows_sandbox:
- "Removed elevated Windows sandbox flag kept for backward compatibility.",
- remote_models: "Refresh remote models before AppReady.",
- powershell_utf8: "Enforce UTF-8 output in PowerShell.",
- enable_request_compression:
- "Compress streaming request bodies sent to codex-backend.",
- apps: "Enable ChatGPT Apps integration.",
- apps_mcp_gateway: "Route Apps MCP calls through the configured gateway.",
- skill_mcp_dependency_install:
- "Allow prompting and installing missing MCP dependencies.",
- skill_env_var_dependency_prompt:
- "Prompt for missing skill environment variable dependencies.",
- steer: "Enable turn steering capability when supported by Codex.",
- collaboration_modes: "Enable collaboration mode presets.",
- personality: "Enable personality selection.",
- responses_websockets:
- "Use Responses API WebSocket transport for OpenAI by default.",
- responses_websockets_v2: "Enable Responses API WebSocket v2 mode.",
-};
+function toTitleCaseFeatureName(name: string): string {
+ return name
+ .split("_")
+ .filter((part) => part.length > 0)
+ .map((part) => part[0].toUpperCase() + part.slice(1))
+ .join(" ");
+}
-function formatFeatureLabel(feature: CodexFeature): string {
+function formatFeatureLabel(
+ feature: CodexFeature,
+ t: (key: string, params?: Record) => string,
+ hasKey: (key: string) => boolean,
+): string {
const displayName = feature.displayName?.trim();
if (displayName) {
return displayName;
}
- return feature.name
- .split("_")
- .filter((part) => part.length > 0)
- .map((part) => part[0].toUpperCase() + part.slice(1))
- .join(" ");
+ const key = `settings.features.fallback.label.${feature.name}`;
+ if (hasKey(key)) {
+ return t(key);
+ }
+ return toTitleCaseFeatureName(feature.name);
}
-function featureSubtitle(feature: CodexFeature): string {
+function featureSubtitle(
+ feature: CodexFeature,
+ t: (key: string, params?: Record) => string,
+ hasKey: (key: string) => boolean,
+): string {
if (feature.description?.trim()) {
return feature.description;
}
if (feature.announcement?.trim()) {
return feature.announcement;
}
- const fallbackDescription = FEATURE_DESCRIPTION_FALLBACKS[feature.name];
- if (fallbackDescription) {
- return fallbackDescription;
+ const key = `settings.features.fallback.description.${feature.name}`;
+ if (hasKey(key)) {
+ return t(key);
}
if (feature.stage === "deprecated") {
- return "Deprecated feature flag.";
+ return t("settings.features.feature.deprecated");
}
if (feature.stage === "removed") {
- return "Legacy feature flag kept for backward compatibility.";
+ return t("settings.features.feature.removed");
}
- return `Feature key: features.${feature.name}`;
+ return t("settings.features.feature.key", { name: feature.name });
}
export function SettingsFeaturesSection({
@@ -93,30 +70,32 @@ export function SettingsFeaturesSection({
onToggleCodexFeature,
onUpdateAppSettings,
}: SettingsFeaturesSectionProps) {
+ const { t, hasKey } = useI18n();
return (
{openInFileManagerLabel()}
{openConfigError && {openConfigError}
}
-
+ {t("settings.features.stable.title")}
+ {t("settings.features.stable.subtitle")}
- Choose Codex communication style (writes top-level personality in
- config.toml).
+ {t("settings.features.personality.subtitle.before")}
+ personality
+ {t("settings.features.personality.subtitle.after")}
>
}
>
@@ -130,15 +109,15 @@ export function SettingsFeaturesSection({
personality: event.target.value as (typeof appSettings)["personality"],
})
}
- aria-label="Personality"
+ aria-label={t("settings.features.personality.title")}
>
- Friendly
- Pragmatic
+ {t("settings.features.personality.option.friendly")}
+ {t("settings.features.personality.option.pragmatic")}
(
No stable feature flags returned by Codex.
+ {t("settings.features.noStable")}
)}
-
+ {t("settings.features.experimental.title")}
+
+ {t("settings.features.experimental.subtitle")}
+
{experimentalFeatures.map((feature) => (
- No preview or under-development feature flags returned by Codex.
+ {t("settings.features.noExperimental")}
)}
{featuresLoading && (
-
Loading Codex feature flags...
+
{t("settings.features.loading")}
)}
{!hasFeatureWorkspace && !featuresLoading && (
-
- Connect a workspace to load Codex feature flags.
-
+
{t("settings.features.connectWorkspace")}
)}
{featureError &&
{featureError}
}
diff --git a/src/features/settings/components/sections/SettingsGitSection.tsx b/src/features/settings/components/sections/SettingsGitSection.tsx
index a99887221..4d44561e5 100644
--- a/src/features/settings/components/sections/SettingsGitSection.tsx
+++ b/src/features/settings/components/sections/SettingsGitSection.tsx
@@ -1,4 +1,5 @@
import type { AppSettings, ModelOption } from "@/types";
+import { useI18n } from "@/i18n/useI18n";
import {
SettingsSection,
SettingsToggleRow,
@@ -28,14 +29,12 @@ export function SettingsGitSection({
onSaveCommitMessagePrompt,
onResetCommitMessagePrompt,
}: SettingsGitSectionProps) {
+ const { t } = useI18n();
return (
-
+
-
Commit message prompt
+
{t("settings.git.commitPrompt.label")}
- Used when generating commit messages. Include {"{diff}"} to insert the
- git diff.
+ {t("settings.git.commitPrompt.help.before")}
+ {"{diff}"}
+ {t("settings.git.commitPrompt.help.after")}