feat(web): i18n (#12471)

This commit is contained in:
Adam
2026-02-06 08:54:51 -06:00
committed by GitHub
parent 0ec5f6608b
commit 812597bb8b
75 changed files with 9868 additions and 726 deletions

View File

@@ -3,6 +3,7 @@ import { createAsync, query, useParams } from "@solidjs/router"
import { createSignal, For, Show } from "solid-js"
import { Database, desc, eq } from "@opencode-ai/console-core/drizzle/index.js"
import { BenchmarkTable } from "@opencode-ai/console-core/schema/benchmark.sql.js"
import { useI18n } from "~/context/i18n"
interface TaskSource {
repo: string
@@ -100,32 +101,33 @@ function formatDuration(ms: number): string {
export default function BenchDetail() {
const params = useParams()
const i18n = useI18n()
const [benchmarkId, taskId] = (params.id ?? "").split(":")
const task = createAsync(() => queryTaskDetail(benchmarkId, taskId))
return (
<main data-page="bench-detail">
<Title>Benchmark - {taskId}</Title>
<Title>{i18n.t("bench.detail.title", { task: taskId })}</Title>
<div style={{ padding: "1rem" }}>
<Show when={task()} fallback={<p>Task not found</p>}>
<Show when={task()} fallback={<p>{i18n.t("bench.detail.notFound")}</p>}>
<div style={{ "margin-bottom": "1rem" }}>
<div>
<strong>Agent: </strong>
{task()?.agent ?? "N/A"}
<strong>{i18n.t("bench.detail.labels.agent")}: </strong>
{task()?.agent ?? i18n.t("bench.detail.na")}
</div>
<div>
<strong>Model: </strong>
{task()?.model ?? "N/A"}
<strong>{i18n.t("bench.detail.labels.model")}: </strong>
{task()?.model ?? i18n.t("bench.detail.na")}
</div>
<div>
<strong>Task: </strong>
<strong>{i18n.t("bench.detail.labels.task")}: </strong>
{task()!.task.id}
</div>
</div>
<div style={{ "margin-bottom": "1rem" }}>
<div>
<strong>Repo: </strong>
<strong>{i18n.t("bench.detail.labels.repo")}: </strong>
<a
href={`https://github.com/${task()!.task.source.repo}`}
target="_blank"
@@ -136,7 +138,7 @@ export default function BenchDetail() {
</a>
</div>
<div>
<strong>From: </strong>
<strong>{i18n.t("bench.detail.labels.from")}: </strong>
<a
href={`https://github.com/${task()!.task.source.repo}/commit/${task()!.task.source.from}`}
target="_blank"
@@ -147,7 +149,7 @@ export default function BenchDetail() {
</a>
</div>
<div>
<strong>To: </strong>
<strong>{i18n.t("bench.detail.labels.to")}: </strong>
<a
href={`https://github.com/${task()!.task.source.repo}/commit/${task()!.task.source.to}`}
target="_blank"
@@ -161,11 +163,13 @@ export default function BenchDetail() {
<Show when={task()?.task.prompts && task()!.task.prompts!.length > 0}>
<div style={{ "margin-bottom": "1rem" }}>
<strong>Prompt:</strong>
<strong>{i18n.t("bench.detail.labels.prompt")}:</strong>
<For each={task()!.task.prompts}>
{(p) => (
<div style={{ "margin-top": "0.5rem" }}>
<div style={{ "font-size": "0.875rem", color: "#666" }}>Commit: {p.commit.slice(0, 7)}</div>
<div style={{ "font-size": "0.875rem", color: "#666" }}>
{i18n.t("bench.detail.labels.commit")}: {p.commit.slice(0, 7)}
</div>
<p style={{ "margin-top": "0.25rem", "white-space": "pre-wrap" }}>{p.prompt}</p>
</div>
)}
@@ -177,33 +181,35 @@ export default function BenchDetail() {
<div style={{ "margin-bottom": "1rem" }}>
<div>
<strong>Average Duration: </strong>
{task()?.averageDuration ? formatDuration(task()!.averageDuration!) : "N/A"}
<strong>{i18n.t("bench.detail.labels.averageDuration")}: </strong>
{task()?.averageDuration ? formatDuration(task()!.averageDuration!) : i18n.t("bench.detail.na")}
</div>
<div>
<strong>Average Score: </strong>
{task()?.averageScore?.toFixed(3) ?? "N/A"}
<strong>{i18n.t("bench.detail.labels.averageScore")}: </strong>
{task()?.averageScore?.toFixed(3) ?? i18n.t("bench.detail.na")}
</div>
<div>
<strong>Average Cost: </strong>
{task()?.averageUsage?.cost ? `$${task()!.averageUsage!.cost.toFixed(4)}` : "N/A"}
<strong>{i18n.t("bench.detail.labels.averageCost")}: </strong>
{task()?.averageUsage?.cost ? `$${task()!.averageUsage!.cost.toFixed(4)}` : i18n.t("bench.detail.na")}
</div>
</div>
<Show when={task()?.summary}>
<div style={{ "margin-bottom": "1rem" }}>
<strong>Summary:</strong>
<strong>{i18n.t("bench.detail.labels.summary")}:</strong>
<p style={{ "margin-top": "0.5rem", "white-space": "pre-wrap" }}>{task()!.summary}</p>
</div>
</Show>
<Show when={task()?.runs && task()!.runs!.length > 0}>
<div style={{ "margin-bottom": "1rem" }}>
<strong>Runs:</strong>
<strong>{i18n.t("bench.detail.labels.runs")}:</strong>
<table style={{ "margin-top": "0.5rem", "border-collapse": "collapse", width: "100%" }}>
<thead>
<tr>
<th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>Run</th>
<th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>
{i18n.t("bench.detail.table.run")}
</th>
<th
style={{
border: "1px solid #ccc",
@@ -212,10 +218,14 @@ export default function BenchDetail() {
"white-space": "nowrap",
}}
>
Score (Base - Penalty)
{i18n.t("bench.detail.table.score")}
</th>
<th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>
{i18n.t("bench.detail.table.cost")}
</th>
<th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>
{i18n.t("bench.detail.table.duration")}
</th>
<th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>Cost</th>
<th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>Duration</th>
<For each={task()!.runs![0]?.scoreDetails}>
{(detail) => (
<th style={{ border: "1px solid #ccc", padding: "0.5rem", "text-align": "left" }}>
@@ -234,10 +244,10 @@ export default function BenchDetail() {
{run.score.final.toFixed(3)} ({run.score.base.toFixed(3)} - {run.score.penalty.toFixed(3)})
</td>
<td style={{ border: "1px solid #ccc", padding: "0.5rem" }}>
{run.usage?.cost ? `$${run.usage.cost.toFixed(4)}` : "N/A"}
{run.usage?.cost ? `$${run.usage.cost.toFixed(4)}` : i18n.t("bench.detail.na")}
</td>
<td style={{ border: "1px solid #ccc", padding: "0.5rem" }}>
{run.duration ? formatDuration(run.duration) : "N/A"}
{run.duration ? formatDuration(run.duration) : i18n.t("bench.detail.na")}
</td>
<For each={run.scoreDetails}>
{(detail) => (
@@ -265,17 +275,17 @@ export default function BenchDetail() {
<For each={task()!.runs}>
{(run, index) => (
<div style={{ "margin-top": "1rem" }}>
<h3 style={{ margin: "0 0 0.5rem 0" }}>Run {index() + 1}</h3>
<h3 style={{ margin: "0 0 0.5rem 0" }}>{i18n.t("bench.detail.run.title", { n: index() + 1 })}</h3>
<div>
<strong>Score: </strong>
{run.score.final.toFixed(3)} (Base: {run.score.base.toFixed(3)} - Penalty:{" "}
{run.score.penalty.toFixed(3)})
<strong>{i18n.t("bench.detail.labels.score")}: </strong>
{run.score.final.toFixed(3)} ({i18n.t("bench.detail.labels.base")}: {run.score.base.toFixed(3)} -{" "}
{i18n.t("bench.detail.labels.penalty")}: {run.score.penalty.toFixed(3)})
</div>
<For each={run.scoreDetails}>
{(detail) => (
<div style={{ "margin-top": "1rem", "padding-left": "1rem", "border-left": "2px solid #ccc" }}>
<div>
{detail.criterion} (weight: {detail.weight}){" "}
{detail.criterion} ({i18n.t("bench.detail.labels.weight")}: {detail.weight}){" "}
<For each={detail.judges}>
{(judge) => (
<span
@@ -350,7 +360,7 @@ export default function BenchDetail() {
onClick={() => setJsonExpanded(!jsonExpanded())}
>
<span style={{ "margin-right": "0.5rem" }}>{jsonExpanded() ? "▼" : "▶"}</span>
Raw JSON
{i18n.t("bench.detail.rawJson")}
</button>
<Show when={jsonExpanded()}>
<pre>{JSON.stringify(task(), null, 2)}</pre>

View File

@@ -3,6 +3,7 @@ import { A, createAsync, query } from "@solidjs/router"
import { createMemo, For, Show } from "solid-js"
import { Database, desc } from "@opencode-ai/console-core/drizzle/index.js"
import { BenchmarkTable } from "@opencode-ai/console-core/schema/benchmark.sql.js"
import { useI18n } from "~/context/i18n"
interface BenchmarkResult {
averageScore: number
@@ -33,6 +34,7 @@ async function getBenchmarks() {
const queryBenchmarks = query(getBenchmarks, "benchmarks.list")
export default function Bench() {
const i18n = useI18n()
const benchmarks = createAsync(() => queryBenchmarks())
const taskIds = createMemo(() => {
@@ -47,14 +49,14 @@ export default function Bench() {
return (
<main data-page="bench" style={{ padding: "2rem" }}>
<Title>Benchmark</Title>
<h1 style={{ "margin-bottom": "1.5rem" }}>Benchmarks</h1>
<Title>{i18n.t("bench.list.title")}</Title>
<h1 style={{ "margin-bottom": "1.5rem" }}>{i18n.t("bench.list.heading")}</h1>
<table style={{ "border-collapse": "collapse", width: "100%" }}>
<thead>
<tr>
<th style={{ "text-align": "left", padding: "0.75rem" }}>Agent</th>
<th style={{ "text-align": "left", padding: "0.75rem" }}>Model</th>
<th style={{ "text-align": "left", padding: "0.75rem" }}>Score</th>
<th style={{ "text-align": "left", padding: "0.75rem" }}>{i18n.t("bench.list.table.agent")}</th>
<th style={{ "text-align": "left", padding: "0.75rem" }}>{i18n.t("bench.list.table.model")}</th>
<th style={{ "text-align": "left", padding: "0.75rem" }}>{i18n.t("bench.list.table.score")}</th>
<For each={taskIds()}>{(id) => <th style={{ "text-align": "left", padding: "0.75rem" }}>{id}</th>}</For>
</tr>
</thead>