Browse Source

Localize annotation controls

feature/canvas-chatbot-copilot
yuanzoo 2 months ago
parent
commit
5206e1472e
  1. 30
      src/components/AnnotationModal.tsx
  2. 2
      src/components/FloatingActionBar.tsx
  3. 4
      src/components/__tests__/FloatingActionBar.test.tsx
  4. 56
      src/i18n/index.tsx

30
src/components/AnnotationModal.tsx

@ -4,6 +4,7 @@ import { useCallback, useEffect, useRef, useState } from "react";
import { Stage, Layer, Image as KonvaImage, Rect, Ellipse, Arrow, Line, Text, Transformer } from "react-konva";
import { useAnnotationStore } from "@/store/annotationStore";
import { useWorkflowStore } from "@/store/workflowStore";
import { useI18n } from "@/i18n";
import {
AnnotationShape,
RectangleShape,
@ -29,6 +30,7 @@ const COLORS = [
const STROKE_WIDTHS = [2, 4, 8];
export function AnnotationModal() {
const { t } = useI18n();
const {
isModalOpen,
sourceNodeId,
@ -400,12 +402,12 @@ export function AnnotationModal() {
if (!isModalOpen) return null;
const tools: { type: ToolType; label: string }[] = [
{ type: "select", label: "Select" },
{ type: "rectangle", label: "Rect" },
{ type: "circle", label: "Circle" },
{ type: "arrow", label: "Arrow" },
{ type: "freehand", label: "Draw" },
{ type: "text", label: "Text" },
{ type: "select", label: t("annotation.tool.select") },
{ type: "rectangle", label: t("annotation.tool.rectangle") },
{ type: "circle", label: t("annotation.tool.circle") },
{ type: "arrow", label: t("annotation.tool.arrow") },
{ type: "freehand", label: t("annotation.tool.draw") },
{ type: "text", label: t("annotation.tool.text") },
];
return (
@ -429,20 +431,20 @@ export function AnnotationModal() {
<div className="w-px h-6 bg-neutral-700 mx-3" />
<button onClick={undo} className="px-3 py-1.5 text-xs text-neutral-400 hover:text-white">Undo</button>
<button onClick={redo} className="px-3 py-1.5 text-xs text-neutral-400 hover:text-white">Redo</button>
<button onClick={undo} className="px-3 py-1.5 text-xs text-neutral-400 hover:text-white">{t("annotation.undo")}</button>
<button onClick={redo} className="px-3 py-1.5 text-xs text-neutral-400 hover:text-white">{t("annotation.redo")}</button>
<div className="w-px h-6 bg-neutral-700 mx-3" />
<button onClick={clearAnnotations} className="px-3 py-1.5 text-xs text-neutral-400 hover:text-red-400">Clear</button>
<button onClick={clearAnnotations} className="px-3 py-1.5 text-xs text-neutral-400 hover:text-red-400">{t("annotation.clear")}</button>
</div>
<div className="flex items-center gap-3">
<button onClick={closeModal} className="px-4 py-1.5 text-xs font-medium text-neutral-400 hover:text-white">
Cancel
{t("annotation.cancel")}
</button>
<button onClick={handleDone} className="px-4 py-1.5 text-xs font-medium bg-white text-neutral-900 rounded hover:bg-neutral-200">
Done
{t("annotation.done")}
</button>
</div>
</div>
@ -478,7 +480,7 @@ export function AnnotationModal() {
<div className="h-14 bg-neutral-900 flex items-center justify-center gap-6 px-4 border-t border-neutral-800">
{/* Colors */}
<div className="flex items-center gap-2">
<span className="text-[10px] text-neutral-500 uppercase tracking-wide mr-1">Color</span>
<span className="text-[10px] text-neutral-500 uppercase tracking-wide mr-1">{t("annotation.color")}</span>
{COLORS.map((color) => (
<button
key={color}
@ -495,7 +497,7 @@ export function AnnotationModal() {
{/* Stroke Width */}
<div className="flex items-center gap-2">
<span className="text-[10px] text-neutral-500 uppercase tracking-wide mr-1">Size</span>
<span className="text-[10px] text-neutral-500 uppercase tracking-wide mr-1">{t("annotation.size")}</span>
{STROKE_WIDTHS.map((width) => (
<button
key={width}
@ -518,7 +520,7 @@ export function AnnotationModal() {
toolOptions.fillColor ? "bg-neutral-700 text-white" : "text-neutral-500 hover:text-white"
}`}
>
Fill
{t("annotation.fill")}
</button>
{/* Zoom */}

2
src/components/FloatingActionBar.tsx

@ -556,7 +556,7 @@ export function FloatingActionBar() {
<div className="h-px w-7 bg-neutral-700 my-1" />
<button
onClick={() => setModelSearchOpen(true)}
title="Browse models"
title={t("toolbar.browseModels")}
aria-label={t("toolbar.allModels")}
className="flex h-12 w-12 items-center justify-center rounded-lg text-neutral-300 transition-colors hover:bg-neutral-700 hover:text-neutral-100"
>

4
src/components/__tests__/FloatingActionBar.test.tsx

@ -368,7 +368,7 @@ describe("FloatingActionBar", () => {
});
describe("Browse Models Button", () => {
it("should render All models button with Browse models title", async () => {
it("should render All models button with localized Browse Models title", async () => {
render(
<TestWrapper>
<FloatingActionBar />
@ -376,7 +376,7 @@ describe("FloatingActionBar", () => {
);
await waitFor(() => {
expect(screen.getByTitle("Browse models")).toBeInTheDocument();
expect(screen.getByTitle("Browse Models")).toBeInTheDocument();
expect(screen.getByText("All models")).toBeInTheDocument();
});
});

56
src/i18n/index.tsx

@ -160,6 +160,20 @@ const en = {
"node.generateAudio": "Generate Audio",
"node.llmGenerate": "LLM Generate",
"node.annotation": "Annotate",
"annotation.tool.select": "Select",
"annotation.tool.rectangle": "Rect",
"annotation.tool.circle": "Circle",
"annotation.tool.arrow": "Arrow",
"annotation.tool.draw": "Draw",
"annotation.tool.text": "Text",
"annotation.undo": "Undo",
"annotation.redo": "Redo",
"annotation.clear": "Clear",
"annotation.cancel": "Cancel",
"annotation.done": "Done",
"annotation.color": "Color",
"annotation.size": "Size",
"annotation.fill": "Fill",
"node.splitGrid": "Split Grid",
"node.videoStitch": "Video Stitch",
"node.videoTrim": "Video Trim",
@ -587,6 +601,20 @@ const zhCN: Dictionary = {
"node.generateAudio": "生成音频",
"node.llmGenerate": "LLM 生成",
"node.annotation": "标注",
"annotation.tool.select": "选择",
"annotation.tool.rectangle": "矩形",
"annotation.tool.circle": "圆形",
"annotation.tool.arrow": "箭头",
"annotation.tool.draw": "画笔",
"annotation.tool.text": "文字",
"annotation.undo": "撤销",
"annotation.redo": "重做",
"annotation.clear": "清空",
"annotation.cancel": "取消",
"annotation.done": "完成",
"annotation.color": "颜色",
"annotation.size": "大小",
"annotation.fill": "填充",
"node.splitGrid": "拆分网格",
"node.videoStitch": "视频拼接",
"node.videoTrim": "视频裁剪",
@ -1004,6 +1032,20 @@ const zhTW: Dictionary = {
"node.generateVideo": "生成影片",
"node.generateAudio": "生成音訊",
"node.annotation": "標註",
"annotation.tool.select": "選擇",
"annotation.tool.rectangle": "矩形",
"annotation.tool.circle": "圓形",
"annotation.tool.arrow": "箭頭",
"annotation.tool.draw": "繪製",
"annotation.tool.text": "文字",
"annotation.undo": "復原",
"annotation.redo": "重做",
"annotation.clear": "清除",
"annotation.cancel": "取消",
"annotation.done": "完成",
"annotation.color": "顏色",
"annotation.size": "大小",
"annotation.fill": "填充",
"node.outputGallery": "輸出圖庫",
"node.settings": "設定",
"node.runToGenerate": "執行以生成",
@ -1305,6 +1347,20 @@ const ja: Dictionary = {
"node.generateAudio": "音声生成",
"node.llmGenerate": "LLM 生成",
"node.annotation": "注釈",
"annotation.tool.select": "選択",
"annotation.tool.rectangle": "矩形",
"annotation.tool.circle": "円形",
"annotation.tool.arrow": "矢印",
"annotation.tool.draw": "描画",
"annotation.tool.text": "テキスト",
"annotation.undo": "元に戻す",
"annotation.redo": "やり直し",
"annotation.clear": "クリア",
"annotation.cancel": "キャンセル",
"annotation.done": "完了",
"annotation.color": "色",
"annotation.size": "サイズ",
"annotation.fill": "塗りつぶし",
"node.splitGrid": "グリッド分割",
"node.videoStitch": "動画結合",
"node.videoTrim": "動画トリム",

Loading…
Cancel
Save