mirror of
https://github.com/jamiepine/voicebox.git
synced 2026-10-03 09:05:17 -07:00
feat(i18n): localize history item and story item dropdown menus
Covers the "..." action menu on both the History table (Play, Export Audio, Export Package, Apply Effects, Regenerate, Delete) and on individual story chat items (Play from here, Remove from Story). Co-Authored-By: Claude Opus 4.7 (1M context) <[email protected]>
This commit is contained in:
co-authored by
Claude Opus 4.7
parent
992ad96a29
commit
ffa68dc356
@@ -673,7 +673,7 @@ export function HistoryTable() {
|
|||||||
variant="ghost"
|
variant="ghost"
|
||||||
size="icon"
|
size="icon"
|
||||||
className="h-6 w-6 text-muted-foreground/50 hover:bg-muted-foreground/20 hover:text-muted-foreground"
|
className="h-6 w-6 text-muted-foreground/50 hover:bg-muted-foreground/20 hover:text-muted-foreground"
|
||||||
aria-label="Actions"
|
aria-label={t('history.actions.menu')}
|
||||||
disabled={isGenerating}
|
disabled={isGenerating}
|
||||||
>
|
>
|
||||||
<MoreHorizontal className="h-2 w-2" />
|
<MoreHorizontal className="h-2 w-2" />
|
||||||
@@ -684,37 +684,36 @@ export function HistoryTable() {
|
|||||||
onClick={() => handlePlay(gen.id, gen.text, gen.profile_id)}
|
onClick={() => handlePlay(gen.id, gen.text, gen.profile_id)}
|
||||||
>
|
>
|
||||||
<Play className="mr-2 h-4 w-4" />
|
<Play className="mr-2 h-4 w-4" />
|
||||||
Play
|
{t('history.actions.play')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleDownloadAudio(gen.id, gen.text)}
|
onClick={() => handleDownloadAudio(gen.id, gen.text)}
|
||||||
disabled={exportGenerationAudio.isPending}
|
disabled={exportGenerationAudio.isPending}
|
||||||
>
|
>
|
||||||
<Download className="mr-2 h-4 w-4" />
|
<Download className="mr-2 h-4 w-4" />
|
||||||
Export Audio
|
{t('history.actions.exportAudio')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleExportPackage(gen.id, gen.text)}
|
onClick={() => handleExportPackage(gen.id, gen.text)}
|
||||||
disabled={exportGeneration.isPending}
|
disabled={exportGeneration.isPending}
|
||||||
>
|
>
|
||||||
<FileArchive className="mr-2 h-4 w-4" />
|
<FileArchive className="mr-2 h-4 w-4" />
|
||||||
Export Package
|
{t('history.actions.exportPackage')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => handleApplyEffects(gen.id)}>
|
<DropdownMenuItem onClick={() => handleApplyEffects(gen.id)}>
|
||||||
<Wand2 className="mr-2 h-4 w-4" />
|
<Wand2 className="mr-2 h-4 w-4" />
|
||||||
Apply Effects
|
{t('history.actions.applyEffects')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem onClick={() => handleRegenerate(gen.id)}>
|
<DropdownMenuItem onClick={() => handleRegenerate(gen.id)}>
|
||||||
<RotateCcw className="mr-2 h-4 w-4" />
|
<RotateCcw className="mr-2 h-4 w-4" />
|
||||||
Regenerate
|
{t('history.actions.regenerate')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={() => handleDeleteClick(gen.id, gen.profile_name)}
|
onClick={() => handleDeleteClick(gen.id, gen.profile_name)}
|
||||||
disabled={deleteGeneration.isPending}
|
disabled={deleteGeneration.isPending}
|
||||||
// className="text-destructive focus:text-destructive"
|
|
||||||
>
|
>
|
||||||
<Trash2 className="mr-2 h-4 w-4" />
|
<Trash2 className="mr-2 h-4 w-4" />
|
||||||
Delete
|
{t('common.delete')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import { useSortable } from '@dnd-kit/sortable';
|
|||||||
import { CSS } from '@dnd-kit/utilities';
|
import { CSS } from '@dnd-kit/utilities';
|
||||||
import { GripVertical, Mic, MoreHorizontal, Play, Trash2 } from 'lucide-react';
|
import { GripVertical, Mic, MoreHorizontal, Play, Trash2 } from 'lucide-react';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
import { Button } from '@/components/ui/button';
|
import { Button } from '@/components/ui/button';
|
||||||
import {
|
import {
|
||||||
DropdownMenu,
|
DropdownMenu,
|
||||||
@@ -34,6 +35,7 @@ export function StoryChatItem({
|
|||||||
dragHandleProps,
|
dragHandleProps,
|
||||||
isDragging,
|
isDragging,
|
||||||
}: StoryChatItemProps) {
|
}: StoryChatItemProps) {
|
||||||
|
const { t } = useTranslation();
|
||||||
const seek = useStoryStore((state) => state.seek);
|
const seek = useStoryStore((state) => state.seek);
|
||||||
const serverUrl = useServerStore((state) => state.serverUrl);
|
const serverUrl = useServerStore((state) => state.serverUrl);
|
||||||
const [avatarError, setAvatarError] = useState(false);
|
const [avatarError, setAvatarError] = useState(false);
|
||||||
@@ -118,21 +120,26 @@ export function StoryChatItem({
|
|||||||
<div className="shrink-0">
|
<div className="shrink-0">
|
||||||
<DropdownMenu>
|
<DropdownMenu>
|
||||||
<DropdownMenuTrigger asChild>
|
<DropdownMenuTrigger asChild>
|
||||||
<Button variant="ghost" size="icon" className="h-8 w-8" aria-label="Actions">
|
<Button
|
||||||
|
variant="ghost"
|
||||||
|
size="icon"
|
||||||
|
className="h-8 w-8"
|
||||||
|
aria-label={t('history.actions.menu')}
|
||||||
|
>
|
||||||
<MoreHorizontal className="h-4 w-4" />
|
<MoreHorizontal className="h-4 w-4" />
|
||||||
</Button>
|
</Button>
|
||||||
</DropdownMenuTrigger>
|
</DropdownMenuTrigger>
|
||||||
<DropdownMenuContent align="end">
|
<DropdownMenuContent align="end">
|
||||||
<DropdownMenuItem onClick={handlePlay}>
|
<DropdownMenuItem onClick={handlePlay}>
|
||||||
<Play className="mr-2 h-4 w-4" />
|
<Play className="mr-2 h-4 w-4" />
|
||||||
Play from here
|
{t('storyContent.itemActions.playFromHere')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
<DropdownMenuItem
|
<DropdownMenuItem
|
||||||
onClick={onRemove}
|
onClick={onRemove}
|
||||||
className="text-destructive focus:text-destructive"
|
className="text-destructive focus:text-destructive"
|
||||||
>
|
>
|
||||||
<Trash2 className="mr-2 h-4 w-4" />
|
<Trash2 className="mr-2 h-4 w-4" />
|
||||||
Remove from Story
|
{t('storyContent.itemActions.removeFromStory')}
|
||||||
</DropdownMenuItem>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuContent>
|
</DropdownMenuContent>
|
||||||
</DropdownMenu>
|
</DropdownMenu>
|
||||||
|
|||||||
@@ -104,6 +104,10 @@
|
|||||||
"title": "No items in this story",
|
"title": "No items in this story",
|
||||||
"hint": "Generate speech using the box below to add items"
|
"hint": "Generate speech using the box below to add items"
|
||||||
},
|
},
|
||||||
|
"itemActions": {
|
||||||
|
"playFromHere": "Play from here",
|
||||||
|
"removeFromStory": "Remove from Story"
|
||||||
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"removeFailed": "Failed to remove item",
|
"removeFailed": "Failed to remove item",
|
||||||
"reorderFailed": "Failed to reorder items",
|
"reorderFailed": "Failed to reorder items",
|
||||||
@@ -112,6 +116,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"history": {
|
"history": {
|
||||||
|
"actions": {
|
||||||
|
"menu": "Actions",
|
||||||
|
"play": "Play",
|
||||||
|
"exportAudio": "Export Audio",
|
||||||
|
"exportPackage": "Export Package",
|
||||||
|
"applyEffects": "Apply Effects",
|
||||||
|
"regenerate": "Regenerate"
|
||||||
|
},
|
||||||
"deleteDialog": {
|
"deleteDialog": {
|
||||||
"title": "Delete Generation",
|
"title": "Delete Generation",
|
||||||
"body": "Are you sure you want to delete this generation from \"{{name}}\"? This action cannot be undone.",
|
"body": "Are you sure you want to delete this generation from \"{{name}}\"? This action cannot be undone.",
|
||||||
|
|||||||
@@ -104,6 +104,10 @@
|
|||||||
"title": "此故事暂无项目",
|
"title": "此故事暂无项目",
|
||||||
"hint": "使用下方输入框生成语音以添加项目"
|
"hint": "使用下方输入框生成语音以添加项目"
|
||||||
},
|
},
|
||||||
|
"itemActions": {
|
||||||
|
"playFromHere": "从此处播放",
|
||||||
|
"removeFromStory": "从故事中移除"
|
||||||
|
},
|
||||||
"toast": {
|
"toast": {
|
||||||
"removeFailed": "移除项目失败",
|
"removeFailed": "移除项目失败",
|
||||||
"reorderFailed": "重新排序项目失败",
|
"reorderFailed": "重新排序项目失败",
|
||||||
@@ -112,6 +116,14 @@
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
"history": {
|
"history": {
|
||||||
|
"actions": {
|
||||||
|
"menu": "操作",
|
||||||
|
"play": "播放",
|
||||||
|
"exportAudio": "导出音频",
|
||||||
|
"exportPackage": "导出包",
|
||||||
|
"applyEffects": "应用效果",
|
||||||
|
"regenerate": "重新生成"
|
||||||
|
},
|
||||||
"deleteDialog": {
|
"deleteDialog": {
|
||||||
"title": "删除生成",
|
"title": "删除生成",
|
||||||
"body": "确定要删除来自 \"{{name}}\" 的这次生成吗?此操作不可撤销。",
|
"body": "确定要删除来自 \"{{name}}\" 的这次生成吗?此操作不可撤销。",
|
||||||
|
|||||||
Reference in New Issue
Block a user