Files
fusion/packages/dashboard/app/components/settings/sections/ResearchProjectSection.tsx
gsxdsm eca96fbd0b FN-6771: localize settings section copy
Localize dashboard settings section text and restore i18n lint coverage for those files.

- Replace hardcoded settings section labels, help text, placeholders, and status copy with i18n lookups.
- Add settings translation keys across supported app and common locale catalogs and refresh generated resource types.
- Remove the settings sections from the i18n lint deferral list and document that coverage requirement.
- Add a patch changeset for the published Fusion package.

Files changed:
 .changeset/fn-6771-localize-settings-sections.md   |   5 +
 docs/i18n-contributing.md                          |   3 +-
 i18next.config.ts                                  |  26 +-
 .../settings/sections/AgentPermissionsSection.tsx  |  55 +-
 .../settings/sections/AppearanceSection.tsx        |  91 +-
 .../settings/sections/AuthenticationSection.tsx    | 457 +++-------
 .../settings/sections/BackupsSection.tsx           | 229 ++---
 .../settings/sections/CommandsSection.tsx          |  49 +-
 .../settings/sections/ExperimentalSection.tsx      | 109 +--
 .../settings/sections/GeneralSection.tsx           | 487 ++++-------
 .../settings/sections/GlobalGeneralSection.tsx     | 183 +---
 .../settings/sections/GlobalModelsSection.tsx      | 525 ++++-------
 .../components/settings/sections/MemorySection.tsx | 458 +++-------
 .../components/settings/sections/MergeSection.tsx  | 863 ++++++------------
 .../settings/sections/NodeRoutingSection.tsx       | 108 +--
 .../settings/sections/NodeSyncSection.tsx          | 105 +--
 .../settings/sections/NotificationsSection.tsx     | 484 ++++------
 .../settings/sections/PluginsSection.tsx           | 105 +--
 .../settings/sections/ProjectModelsSection.tsx     | 971 ++++++++-------------
 .../components/settings/sections/RemoteSection.tsx | 560 +++++-------
 .../settings/sections/ResearchGlobalSection.tsx    | 314 ++-----
 .../settings/sections/ResearchProjectSection.tsx   | 226 ++---
 .../settings/sections/RuntimesSections.tsx         |  40 +-
 .../settings/sections/ScheduledEvalsSection.tsx    | 169 ++--
 .../settings/sections/SchedulingSection.tsx        | 381 ++------
 .../settings/sections/WorktreesSection.tsx         | 330 ++-----
 packages/i18n/locales/en/app.json                  | 698 ++++++++++++++-
 packages/i18n/locales/en/common.json               |   8 +
 packages/i18n/locales/es/app.json                  | 698 ++++++++++++++-
 packages/i18n/locales/es/common.json               |   8 +
 packages/i18n/locales/fr/app.json                  | 698 ++++++++++++++-
 packages/i18n/locales/fr/common.json               |   8 +
 packages/i18n/locales/ko/app.json                  | 698 ++++++++++++++-
 packages/i18n/locales/ko/common.json               |   8 +
 packages/i18n/locales/zh-CN/app.json               | 698 ++++++++++++++-
 packages/i18n/locales/zh-CN/common.json            |   8 +
 packages/i18n/locales/zh-TW/app.json               | 698 ++++++++++++++-
 packages/i18n/locales/zh-TW/common.json            |   8 +
 packages/i18n/src/resources.d.ts                   | 706 ++++++++++++++-
 39 files changed, 7178 insertions(+), 5097 deletions(-)

Fusion-Task-Id: FN-6771
Fusion-Task-Lineage: cdf9b36f-414e-4084-b661-b578a4c27314
2026-06-20 08:57:45 -07:00

112 lines
6.3 KiB
TypeScript

import type { ReactNode } from "react";
import type { SectionBaseProps } from "./context";
import { useTranslation } from "react-i18next";
export interface ResearchProjectSectionProps extends SectionBaseProps {
scopeBanner: ReactNode;
researchLimitError: string | null;
}
export function ResearchProjectSection({ scopeBanner, form, setForm, researchLimitError }: ResearchProjectSectionProps) {
const { t } = useTranslation("app");
const limits = form.researchSettings?.limits;
const sources = form.researchSettings?.enabledSources;
return (<>
{scopeBanner}
<h4 className="settings-section-heading">{t("settings.researchProject.projectResearchSettings", "Project Research Settings")}</h4>
<div className="form-group">
<label htmlFor="research-project-enabled" className="checkbox-label">
<input id="research-project-enabled" type="checkbox" checked={form.researchSettings?.enabled ?? true} onChange={(event) => setForm((current) => ({
...current,
researchSettings: {
...(current.researchSettings ?? {}),
enabled: event.target.checked,
},
}))}/>{t("settings.researchProject.enableResearchInThisProject", " Enable research in this project ")}</label>
</div>
<div className="form-group">
<label>{t("settings.researchProject.enabledSources", "Enabled Sources")}</label>
<label htmlFor="research-project-source-webSearch" className="checkbox-label settings-research-source-locked">
<input id="research-project-source-webSearch" type="checkbox" checked disabled readOnly/>{t("settings.researchProject.webSearch", " Web Search ")}<span className="settings-muted">{t("settings.researchProject.alwaysOn", "Always on")}</span>
</label>
<small className="settings-muted">{t("settings.researchProject.webSearchIsAlwaysEnabledConfigureTheSearch", " Web search is always enabled. Configure the search provider under Research Defaults. ")}</small>
<div className="settings-research-source-grid">
{[
["pageFetch", t("settings.researchProject.pageFetch", "Page Fetch")],
["github", t("settings.researchProject.github", "GitHub")],
["localDocs", t("settings.researchProject.localDocs", "Local Docs")],
["llmSynthesis", t("settings.researchProject.llmSynthesis", "LLM Synthesis")],
].map(([key, label]) => (<label key={key} htmlFor={`research-project-source-${key}`} className="checkbox-label">
<input id={`research-project-source-${key}`} type="checkbox" checked={sources?.[key as keyof NonNullable<typeof sources>] ?? false} onChange={(event) => setForm((current) => ({
...current,
researchSettings: {
...(current.researchSettings ?? {}),
enabledSources: {
...(current.researchSettings?.enabledSources ?? {}),
[key]: event.target.checked,
},
},
}))}/>
{label}
</label>))}
</div>
</div>
<div className="form-group">
<div className="settings-research-limits-grid">
<div className="settings-research-limit-field">
<label htmlFor="research-project-max-concurrent">{t("settings.researchProject.maxConcurrentRuns", "Max Concurrent Runs")}</label>
<input id="research-project-max-concurrent" className="input" type="number" min={1} value={limits?.maxConcurrentRuns ?? 3} onChange={(event) => setForm((current) => ({
...current,
researchSettings: {
...(current.researchSettings ?? {}),
limits: {
...(current.researchSettings?.limits ?? {}),
maxConcurrentRuns: event.target.value === "" ? undefined : Number(event.target.value),
},
},
}))}/>
</div>
<div className="settings-research-limit-field">
<label htmlFor="research-project-max-sources">{t("settings.researchProject.maxSourcesPerRun", "Max Sources Per Run")}</label>
<input id="research-project-max-sources" className="input" type="number" min={1} value={limits?.maxSourcesPerRun ?? 20} onChange={(event) => setForm((current) => ({
...current,
researchSettings: {
...(current.researchSettings ?? {}),
limits: {
...(current.researchSettings?.limits ?? {}),
maxSourcesPerRun: event.target.value === "" ? undefined : Number(event.target.value),
},
},
}))}/>
</div>
<div className="settings-research-limit-field">
<label htmlFor="research-project-max-duration">{t("settings.researchProject.maxDurationMs", "Max Duration (ms)")}</label>
<input id="research-project-max-duration" className="input" type="number" min={1000} value={limits?.maxDurationMs ?? 300000} onChange={(event) => setForm((current) => ({
...current,
researchSettings: {
...(current.researchSettings ?? {}),
limits: {
...(current.researchSettings?.limits ?? {}),
maxDurationMs: event.target.value === "" ? undefined : Number(event.target.value),
},
},
}))}/>
</div>
<div className="settings-research-limit-field">
<label htmlFor="research-project-request-timeout">{t("settings.researchProject.requestTimeoutMs", "Request Timeout (ms)")}</label>
<input id="research-project-request-timeout" className="input" type="number" min={1000} value={limits?.requestTimeoutMs ?? 30000} onChange={(event) => setForm((current) => ({
...current,
researchSettings: {
...(current.researchSettings ?? {}),
limits: {
...(current.researchSettings?.limits ?? {}),
requestTimeoutMs: event.target.value === "" ? undefined : Number(event.target.value),
},
},
}))}/>
</div>
{researchLimitError && <small className="field-error settings-research-limits-error">{researchLimitError}</small>}
</div>
</div>
</>);
}
export default ResearchProjectSection;