mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-18 14:44:40 +00:00
chore(app): i18n sync (#17283)
This commit is contained in:
@@ -35,14 +35,14 @@ function isInitError(error: unknown): error is InitError {
|
||||
)
|
||||
}
|
||||
|
||||
function safeJson(value: unknown): string {
|
||||
function safeJson(value: unknown, circular: string): string {
|
||||
const seen = new WeakSet<object>()
|
||||
const json = JSON.stringify(
|
||||
value,
|
||||
(_key, val) => {
|
||||
if (typeof val === "bigint") return val.toString()
|
||||
if (typeof val === "object" && val) {
|
||||
if (seen.has(val)) return "[Circular]"
|
||||
if (seen.has(val)) return circular
|
||||
seen.add(val)
|
||||
}
|
||||
return val
|
||||
@@ -54,14 +54,15 @@ function safeJson(value: unknown): string {
|
||||
|
||||
function formatInitError(error: InitError, t: Translator): string {
|
||||
const data = error.data
|
||||
const json = (value: unknown) => safeJson(value, t("error.page.circular"))
|
||||
switch (error.name) {
|
||||
case "MCPFailed": {
|
||||
const name = typeof data.name === "string" ? data.name : ""
|
||||
return t("error.chain.mcpFailed", { name })
|
||||
}
|
||||
case "ProviderAuthError": {
|
||||
const providerID = typeof data.providerID === "string" ? data.providerID : "unknown"
|
||||
const message = typeof data.message === "string" ? data.message : safeJson(data.message)
|
||||
const providerID = typeof data.providerID === "string" ? data.providerID : t("common.unknown")
|
||||
const message = typeof data.message === "string" ? data.message : json(data.message)
|
||||
return t("error.chain.providerAuthFailed", { provider: providerID, message })
|
||||
}
|
||||
case "APIError": {
|
||||
@@ -101,24 +102,24 @@ function formatInitError(error: InitError, t: Translator): string {
|
||||
].join("\n")
|
||||
}
|
||||
case "ProviderInitError": {
|
||||
const providerID = typeof data.providerID === "string" ? data.providerID : "unknown"
|
||||
const providerID = typeof data.providerID === "string" ? data.providerID : t("common.unknown")
|
||||
return t("error.chain.providerInitFailed", { provider: providerID })
|
||||
}
|
||||
case "ConfigJsonError": {
|
||||
const path = typeof data.path === "string" ? data.path : safeJson(data.path)
|
||||
const path = typeof data.path === "string" ? data.path : json(data.path)
|
||||
const message = typeof data.message === "string" ? data.message : ""
|
||||
if (message) return t("error.chain.configJsonInvalidWithMessage", { path, message })
|
||||
return t("error.chain.configJsonInvalid", { path })
|
||||
}
|
||||
case "ConfigDirectoryTypoError": {
|
||||
const path = typeof data.path === "string" ? data.path : safeJson(data.path)
|
||||
const dir = typeof data.dir === "string" ? data.dir : safeJson(data.dir)
|
||||
const suggestion = typeof data.suggestion === "string" ? data.suggestion : safeJson(data.suggestion)
|
||||
const path = typeof data.path === "string" ? data.path : json(data.path)
|
||||
const dir = typeof data.dir === "string" ? data.dir : json(data.dir)
|
||||
const suggestion = typeof data.suggestion === "string" ? data.suggestion : json(data.suggestion)
|
||||
return t("error.chain.configDirectoryTypo", { dir, path, suggestion })
|
||||
}
|
||||
case "ConfigFrontmatterError": {
|
||||
const path = typeof data.path === "string" ? data.path : safeJson(data.path)
|
||||
const message = typeof data.message === "string" ? data.message : safeJson(data.message)
|
||||
const path = typeof data.path === "string" ? data.path : json(data.path)
|
||||
const message = typeof data.message === "string" ? data.message : json(data.message)
|
||||
return t("error.chain.configFrontmatterError", { path, message })
|
||||
}
|
||||
case "ConfigInvalidError": {
|
||||
@@ -126,7 +127,7 @@ function formatInitError(error: InitError, t: Translator): string {
|
||||
? data.issues.filter(isIssue).map((issue) => "↳ " + issue.message + " " + issue.path.join("."))
|
||||
: []
|
||||
const message = typeof data.message === "string" ? data.message : ""
|
||||
const path = typeof data.path === "string" ? data.path : safeJson(data.path)
|
||||
const path = typeof data.path === "string" ? data.path : json(data.path)
|
||||
|
||||
const line = message
|
||||
? t("error.chain.configInvalidWithMessage", { path, message })
|
||||
@@ -135,14 +136,15 @@ function formatInitError(error: InitError, t: Translator): string {
|
||||
return [line, ...issues].join("\n")
|
||||
}
|
||||
case "UnknownError":
|
||||
return typeof data.message === "string" ? data.message : safeJson(data)
|
||||
return typeof data.message === "string" ? data.message : json(data)
|
||||
default:
|
||||
if (typeof data.message === "string") return data.message
|
||||
return safeJson(data)
|
||||
return json(data)
|
||||
}
|
||||
}
|
||||
|
||||
function formatErrorChain(error: unknown, t: Translator, depth = 0, parentMessage?: string): string {
|
||||
const json = (value: unknown) => safeJson(value, t("error.page.circular"))
|
||||
if (!error) return t("error.chain.unknown")
|
||||
|
||||
if (isInitError(error)) {
|
||||
@@ -204,7 +206,7 @@ function formatErrorChain(error: unknown, t: Translator, depth = 0, parentMessag
|
||||
}
|
||||
|
||||
const indent = depth > 0 ? `\n${CHAIN_SEPARATOR}${t("error.chain.causedBy")}\n` : ""
|
||||
return indent + safeJson(error)
|
||||
return indent + json(error)
|
||||
}
|
||||
|
||||
function formatError(error: unknown, t: Translator): string {
|
||||
|
||||
@@ -2159,7 +2159,7 @@ export default function Layout(props: ParentProps) {
|
||||
{language.t("command.provider.connect")}
|
||||
</Button>
|
||||
<Button size="large" variant="ghost" onClick={() => setStore("gettingStartedDismissed", true)}>
|
||||
Not yet
|
||||
{language.t("toast.update.action.notYet")}
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -956,13 +956,15 @@ export default function Page() {
|
||||
return (
|
||||
<div class={input.emptyClass}>
|
||||
<div class="flex flex-col gap-3">
|
||||
<div class="text-14-medium text-text-strong">Create a Git repository</div>
|
||||
<div class="text-14-medium text-text-strong">{language.t("session.review.noVcs.createGit.title")}</div>
|
||||
<div class="text-14-regular text-text-base max-w-md" style={{ "line-height": "var(--line-height-normal)" }}>
|
||||
Track, review, and undo changes in this project
|
||||
{language.t("session.review.noVcs.createGit.description")}
|
||||
</div>
|
||||
</div>
|
||||
<Button size="large" disabled={ui.git} onClick={initGit}>
|
||||
{ui.git ? "Creating Git repository..." : "Create Git repository"}
|
||||
{ui.git
|
||||
? language.t("session.review.noVcs.createGit.actionLoading")
|
||||
: language.t("session.review.noVcs.createGit.action")}
|
||||
</Button>
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -196,7 +196,6 @@ export function SessionComposerRegion(props: {
|
||||
<SessionTodoDock
|
||||
sessionID={route.params.id}
|
||||
todos={props.state.todos()}
|
||||
title={language.t("session.todo.title")}
|
||||
collapseLabel={language.t("session.todo.collapse")}
|
||||
expandLabel={language.t("session.todo.expand")}
|
||||
dockProgress={value()}
|
||||
|
||||
@@ -38,7 +38,7 @@ export const SessionQuestionDock: Component<{ request: QuestionRequest; onSubmit
|
||||
|
||||
const summary = createMemo(() => {
|
||||
const n = Math.min(store.tab + 1, total())
|
||||
return `${n} of ${total()} questions`
|
||||
return language.t("session.question.progress", { current: n, total: total() })
|
||||
})
|
||||
|
||||
const last = createMemo(() => store.tab >= total() - 1)
|
||||
|
||||
@@ -9,6 +9,10 @@ import { TextStrikethrough } from "@opencode-ai/ui/text-strikethrough"
|
||||
import { Index, createEffect, createMemo, on, onCleanup } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { composerEnabled, composerProbe } from "@/testing/session-composer"
|
||||
import { useLanguage } from "@/context/language"
|
||||
|
||||
const doneToken = "\u0000done\u0000"
|
||||
const totalToken = "\u0000total\u0000"
|
||||
|
||||
function dot(status: Todo["status"]) {
|
||||
if (status !== "in_progress") return undefined
|
||||
@@ -38,11 +42,11 @@ function dot(status: Todo["status"]) {
|
||||
export function SessionTodoDock(props: {
|
||||
sessionID?: string
|
||||
todos: Todo[]
|
||||
title: string
|
||||
collapseLabel: string
|
||||
expandLabel: string
|
||||
dockProgress: number
|
||||
}) {
|
||||
const language = useLanguage()
|
||||
const [store, setStore] = createStore({
|
||||
collapsed: false,
|
||||
height: 320,
|
||||
@@ -52,7 +56,12 @@ export function SessionTodoDock(props: {
|
||||
|
||||
const total = createMemo(() => props.todos.length)
|
||||
const done = createMemo(() => props.todos.filter((todo) => todo.status === "completed").length)
|
||||
const label = createMemo(() => `${done()} of ${total()} ${props.title.toLowerCase()} completed`)
|
||||
const label = createMemo(() => language.t("session.todo.progress", { done: done(), total: total() }))
|
||||
const progress = createMemo(() =>
|
||||
language
|
||||
.t("session.todo.progress", { done: doneToken, total: totalToken })
|
||||
.split(/(\u0000done\u0000|\u0000total\u0000)/),
|
||||
)
|
||||
|
||||
const active = createMemo(
|
||||
() =>
|
||||
@@ -137,10 +146,17 @@ export function SessionTodoDock(props: {
|
||||
opacity: `${Math.max(0, Math.min(1, 1 - shut()))}`,
|
||||
}}
|
||||
>
|
||||
<AnimatedNumber value={done()} />
|
||||
<span class="mx-1">of</span>
|
||||
<AnimatedNumber value={total()} />
|
||||
<span> {props.title.toLowerCase()} completed</span>
|
||||
<Index each={progress()}>
|
||||
{(item) =>
|
||||
item() === doneToken ? (
|
||||
<AnimatedNumber value={done()} />
|
||||
) : item() === totalToken ? (
|
||||
<AnimatedNumber value={total()} />
|
||||
) : (
|
||||
<span>{item()}</span>
|
||||
)
|
||||
}
|
||||
</Index>
|
||||
</span>
|
||||
<div
|
||||
data-slot="session-todo-preview"
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { isDefaultTitle as isDefaultTerminalTitle } from "@/context/terminal-title"
|
||||
|
||||
export const terminalTabLabel = (input: {
|
||||
title?: string
|
||||
titleNumber?: number
|
||||
@@ -5,9 +7,7 @@ export const terminalTabLabel = (input: {
|
||||
}) => {
|
||||
const title = input.title ?? ""
|
||||
const number = input.titleNumber ?? 0
|
||||
const match = title.match(/^Terminal (\d+)$/)
|
||||
const parsed = match ? Number(match[1]) : undefined
|
||||
const isDefaultTitle = Number.isFinite(number) && number > 0 && Number.isFinite(parsed) && parsed === number
|
||||
const isDefaultTitle = Number.isFinite(number) && number > 0 && isDefaultTerminalTitle(title, number)
|
||||
|
||||
if (title && !isDefaultTitle) return title
|
||||
if (number > 0) return input.t("terminal.title.numbered", { number })
|
||||
|
||||
Reference in New Issue
Block a user