mirror of
https://gitea.toothfairyai.com/ToothFairyAI/tf_code.git
synced 2026-04-09 02:09:12 +00:00
chore(app): i18n sync (#17283)
This commit is contained in:
@@ -1,5 +1,6 @@
|
||||
import { createEffect, For, Match, on, onCleanup, Show, Switch, type JSX } from "solid-js"
|
||||
import { animate, type AnimationPlaybackControls } from "motion"
|
||||
import { useI18n } from "../context/i18n"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { Collapsible } from "./collapsible"
|
||||
import type { IconProps } from "./icon"
|
||||
@@ -233,12 +234,14 @@ export function GenericTool(props: {
|
||||
hideDetails?: boolean
|
||||
input?: Record<string, unknown>
|
||||
}) {
|
||||
const i18n = useI18n()
|
||||
|
||||
return (
|
||||
<BasicTool
|
||||
icon="mcp"
|
||||
status={props.status}
|
||||
trigger={{
|
||||
title: `Called \`${props.tool}\``,
|
||||
title: i18n.t("ui.basicTool.called", { tool: props.tool }),
|
||||
subtitle: label(props.input),
|
||||
args: args(props.input),
|
||||
}}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import { Portal } from "solid-js/web"
|
||||
import { useI18n } from "../context/i18n"
|
||||
import { Icon } from "./icon"
|
||||
|
||||
export function FileSearchBar(props: {
|
||||
@@ -13,6 +14,8 @@ export function FileSearchBar(props: {
|
||||
onPrev: () => void
|
||||
onNext: () => void
|
||||
}) {
|
||||
const i18n = useI18n()
|
||||
|
||||
return (
|
||||
<Portal>
|
||||
<div
|
||||
@@ -26,7 +29,7 @@ export function FileSearchBar(props: {
|
||||
<Icon name="magnifying-glass" size="small" class="text-text-weak shrink-0" />
|
||||
<input
|
||||
ref={props.setInput}
|
||||
placeholder="Find"
|
||||
placeholder={i18n.t("ui.fileSearch.placeholder")}
|
||||
value={props.query()}
|
||||
class="w-40 bg-transparent outline-none text-14-regular text-text-strong placeholder:text-text-weak"
|
||||
onInput={(e) => props.onInput(e.currentTarget.value)}
|
||||
@@ -40,7 +43,7 @@ export function FileSearchBar(props: {
|
||||
type="button"
|
||||
class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none"
|
||||
disabled={props.count() === 0}
|
||||
aria-label="Previous match"
|
||||
aria-label={i18n.t("ui.fileSearch.previousMatch")}
|
||||
onClick={props.onPrev}
|
||||
>
|
||||
<Icon name="chevron-down" size="small" class="rotate-180" />
|
||||
@@ -49,7 +52,7 @@ export function FileSearchBar(props: {
|
||||
type="button"
|
||||
class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong disabled:opacity-40 disabled:pointer-events-none"
|
||||
disabled={props.count() === 0}
|
||||
aria-label="Next match"
|
||||
aria-label={i18n.t("ui.fileSearch.nextMatch")}
|
||||
onClick={props.onNext}
|
||||
>
|
||||
<Icon name="chevron-down" size="small" />
|
||||
@@ -58,7 +61,7 @@ export function FileSearchBar(props: {
|
||||
<button
|
||||
type="button"
|
||||
class="size-6 grid place-items-center rounded text-text-weak hover:bg-surface-base-hover hover:text-text-strong"
|
||||
aria-label="Close search"
|
||||
aria-label={i18n.t("ui.fileSearch.close")}
|
||||
onClick={props.onClose}
|
||||
>
|
||||
<Icon name="close-small" size="small" />
|
||||
|
||||
@@ -2,6 +2,7 @@ import { type DiffLineAnnotation, type SelectedLineRange } from "@pierre/diffs"
|
||||
import { createEffect, createMemo, createSignal, onCleanup, Show, type Accessor, type JSX } from "solid-js"
|
||||
import { createStore } from "solid-js/store"
|
||||
import { render as renderSolid } from "solid-js/web"
|
||||
import { useI18n } from "../context/i18n"
|
||||
import { createHoverCommentUtility } from "../pierre/comment-hover"
|
||||
import { cloneSelectedLineRange, formatSelectedLineLabel, lineInSelectedRange } from "../pierre/selection-bridge"
|
||||
import { LineComment, LineCommentEditor } from "./line-comment"
|
||||
@@ -341,6 +342,7 @@ export function createLineCommentController<T extends LineCommentShape>(
|
||||
export function createLineCommentController<T extends LineCommentShape>(
|
||||
props: LineCommentControllerProps<T> | LineCommentControllerWithSideProps<T>,
|
||||
) {
|
||||
const i18n = useI18n()
|
||||
const note = createLineCommentState<string>(props.state)
|
||||
|
||||
const annotations =
|
||||
@@ -376,7 +378,7 @@ export function createLineCommentController<T extends LineCommentShape>(
|
||||
return note.isOpen(comment.id) || note.isEditing(comment.id)
|
||||
},
|
||||
comment: comment.comment,
|
||||
selection: formatSelectedLineLabel(comment.selection),
|
||||
selection: formatSelectedLineLabel(comment.selection, i18n.t),
|
||||
get actions() {
|
||||
return props.renderCommentActions?.(comment, { edit, remove })
|
||||
},
|
||||
@@ -386,7 +388,7 @@ export function createLineCommentController<T extends LineCommentShape>(
|
||||
get value() {
|
||||
return note.draft()
|
||||
},
|
||||
selection: formatSelectedLineLabel(comment.selection),
|
||||
selection: formatSelectedLineLabel(comment.selection, i18n.t),
|
||||
onInput: note.setDraft,
|
||||
onCancel: note.cancelDraft,
|
||||
onSubmit: (value: string) => {
|
||||
@@ -412,7 +414,7 @@ export function createLineCommentController<T extends LineCommentShape>(
|
||||
get value() {
|
||||
return note.draft()
|
||||
},
|
||||
selection: formatSelectedLineLabel(range),
|
||||
selection: formatSelectedLineLabel(range, i18n.t),
|
||||
onInput: note.setDraft,
|
||||
onCancel: note.cancelDraft,
|
||||
onSubmit: (comment) => {
|
||||
|
||||
@@ -322,7 +322,7 @@ export function getToolInfo(tool: string, input: any = {}): ToolInfo {
|
||||
case "skill":
|
||||
return {
|
||||
icon: "brain",
|
||||
title: input.name || "skill",
|
||||
title: input.name || i18n.t("ui.tool.skill"),
|
||||
}
|
||||
default:
|
||||
return {
|
||||
@@ -924,15 +924,12 @@ export function UserMessageDisplay(props: { message: UserMessage; parts: PartTyp
|
||||
const match = data.store.provider?.all?.find((p) => p.id === providerID)
|
||||
return match?.models?.[modelID]?.name ?? modelID
|
||||
})
|
||||
const timefmt = createMemo(() => new Intl.DateTimeFormat(i18n.locale(), { timeStyle: "short" }))
|
||||
|
||||
const stamp = createMemo(() => {
|
||||
const created = props.message.time?.created
|
||||
if (typeof created !== "number") return ""
|
||||
const date = new Date(created)
|
||||
const hours = date.getHours()
|
||||
const hour12 = hours % 12 || 12
|
||||
const minute = String(date.getMinutes()).padStart(2, "0")
|
||||
return `${hour12}:${minute} ${hours < 12 ? "AM" : "PM"}`
|
||||
return timefmt().format(created)
|
||||
})
|
||||
|
||||
const metaHead = createMemo(() => {
|
||||
@@ -1318,6 +1315,7 @@ PART_MAPPING["compaction"] = function CompactionPartDisplay() {
|
||||
PART_MAPPING["text"] = function TextPartDisplay(props) {
|
||||
const data = useData()
|
||||
const i18n = useI18n()
|
||||
const numfmt = createMemo(() => new Intl.NumberFormat(i18n.locale()))
|
||||
const part = () => props.part as TextPart
|
||||
const interrupted = createMemo(
|
||||
() =>
|
||||
@@ -1343,10 +1341,13 @@ PART_MAPPING["text"] = function TextPartDisplay(props) {
|
||||
: -1
|
||||
if (!(ms >= 0)) return ""
|
||||
const total = Math.round(ms / 1000)
|
||||
if (total < 60) return `${total}s`
|
||||
if (total < 60) return i18n.t("ui.message.duration.seconds", { count: numfmt().format(total) })
|
||||
const minutes = Math.floor(total / 60)
|
||||
const seconds = total % 60
|
||||
return `${minutes}m ${seconds}s`
|
||||
return i18n.t("ui.message.duration.minutesSeconds", {
|
||||
minutes: numfmt().format(minutes),
|
||||
seconds: numfmt().format(seconds),
|
||||
})
|
||||
})
|
||||
|
||||
const meta = createMemo(() => {
|
||||
@@ -2206,7 +2207,8 @@ ToolRegistry.register({
|
||||
ToolRegistry.register({
|
||||
name: "skill",
|
||||
render(props) {
|
||||
const title = createMemo(() => props.input.name || "skill")
|
||||
const i18n = useI18n()
|
||||
const title = createMemo(() => props.input.name || i18n.t("ui.tool.skill"))
|
||||
const running = createMemo(() => props.status === "pending" || props.status === "running")
|
||||
|
||||
const titleContent = () => <TextShimmer text={title()} active={running()} />
|
||||
|
||||
@@ -30,7 +30,7 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||
list: "ui.tool.list",
|
||||
glob: "ui.tool.glob",
|
||||
grep: "ui.tool.grep",
|
||||
task: "Task",
|
||||
task: "ui.tool.task",
|
||||
webfetch: "ui.tool.webfetch",
|
||||
websearch: "ui.tool.websearch",
|
||||
codesearch: "ui.tool.codesearch",
|
||||
@@ -54,10 +54,10 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||
const subtitle = createMemo(() => {
|
||||
if (split.subtitle) return split.subtitle
|
||||
const parts = tail().split(": ")
|
||||
if (parts.length <= 1) return "Failed"
|
||||
if (parts.length <= 1) return i18n.t("ui.toolErrorCard.failed")
|
||||
const head = (parts[0] ?? "").trim()
|
||||
if (!head) return "Failed"
|
||||
return head[0] ? head[0].toUpperCase() + head.slice(1) : "Failed"
|
||||
if (!head) return i18n.t("ui.toolErrorCard.failed")
|
||||
return head[0] ? head[0].toUpperCase() + head.slice(1) : i18n.t("ui.toolErrorCard.failed")
|
||||
})
|
||||
|
||||
const body = createMemo(() => {
|
||||
@@ -116,7 +116,11 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||
<div data-slot="tool-error-card-content">
|
||||
<Show when={open()}>
|
||||
<div data-slot="tool-error-card-copy">
|
||||
<Tooltip value={copied() ? i18n.t("ui.message.copied") : "Copy error"} placement="top" gutter={4}>
|
||||
<Tooltip
|
||||
value={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.toolErrorCard.copyError")}
|
||||
placement="top"
|
||||
gutter={4}
|
||||
>
|
||||
<IconButton
|
||||
icon={copied() ? "check" : "copy"}
|
||||
size="normal"
|
||||
@@ -126,7 +130,7 @@ export function ToolErrorCard(props: ToolErrorCardProps) {
|
||||
e.stopPropagation()
|
||||
copy()
|
||||
}}
|
||||
aria-label={copied() ? i18n.t("ui.message.copied") : "Copy error"}
|
||||
aria-label={copied() ? i18n.t("ui.message.copied") : i18n.t("ui.toolErrorCard.copyError")}
|
||||
/>
|
||||
</Tooltip>
|
||||
</div>
|
||||
|
||||
Reference in New Issue
Block a user