diff --git a/app/src/components/StoriesTab/StoryContent.tsx b/app/src/components/StoriesTab/StoryContent.tsx
index 0f53c2c9..10099a09 100644
--- a/app/src/components/StoriesTab/StoryContent.tsx
+++ b/app/src/components/StoriesTab/StoryContent.tsx
@@ -17,6 +17,7 @@ import { Link } from '@tanstack/react-router';
import { AnimatePresence, motion } from 'framer-motion';
import { Download, Plus } from 'lucide-react';
import { useEffect, useMemo, useRef, useState } from 'react';
+import { useTranslation } from 'react-i18next';
import Loader from 'react-loaders';
import { Button } from '@/components/ui/button';
import { Input } from '@/components/ui/input';
@@ -36,6 +37,7 @@ import { useStoryStore } from '@/stores/storyStore';
import { SortableStoryChatItem } from './StoryChatItem';
export function StoryContent() {
+ const { t } = useTranslation();
const selectedStoryId = useStoryStore((state) => state.selectedStoryId);
const { data: story, isLoading } = useStory(selectedStoryId);
const removeItem = useRemoveStoryItem();
@@ -147,7 +149,7 @@ export function StoryContent() {
{
onError: (error) => {
toast({
- title: 'Failed to remove item',
+ title: t('storyContent.toast.removeFailed'),
description: error.message,
variant: 'destructive',
});
@@ -179,7 +181,7 @@ export function StoryContent() {
{
onError: (error) => {
toast({
- title: 'Failed to reorder items',
+ title: t('storyContent.toast.reorderFailed'),
description: error.message,
variant: 'destructive',
});
@@ -199,7 +201,7 @@ export function StoryContent() {
{
onError: (error) => {
toast({
- title: 'Failed to export audio',
+ title: t('storyContent.toast.exportFailed'),
description: error.message,
variant: 'destructive',
});
@@ -223,7 +225,7 @@ export function StoryContent() {
},
onError: (error) => {
toast({
- title: 'Failed to add generation',
+ title: t('storyContent.toast.addFailed'),
description: error.message,
variant: 'destructive',
});
@@ -236,8 +238,8 @@ export function StoryContent() {
return (
-
Select a story
-
Choose a story from the list to view its content
+
{t('storyContent.selectStory.title')}
+
{t('storyContent.selectStory.hint')}
);
@@ -246,7 +248,7 @@ export function StoryContent() {
if (isLoading) {
return (
-
Loading story...
+
{t('storyContent.loading')}
);
}
@@ -255,8 +257,8 @@ export function StoryContent() {
return (
-
Story not found
-
The selected story could not be loaded
+
{t('storyContent.notFound.title')}
+
{t('storyContent.notFound.hint')}
);
@@ -291,7 +293,7 @@ export function StoryContent() {
- Generating {pendingCount} {pendingCount === 1 ? 'audio' : 'audios'}
+ {t('storyContent.generatingCount', { count: pendingCount })}
@@ -301,13 +303,13 @@ export function StoryContent() {
setSearchQuery(e.target.value)}
autoFocus
@@ -316,7 +318,9 @@ export function StoryContent() {
{availableGenerations.length === 0 ? (
- {searchQuery ? 'No matching generations found' : 'No available generations'}
+ {searchQuery
+ ? t('storyContent.searchNoMatches')
+ : t('storyContent.searchNoAvailable')}
) : (
availableGenerations.map((gen) => (
@@ -344,7 +348,7 @@ export function StoryContent() {
disabled={exportAudio.isPending}
>
- Export Audio
+ {t('storyContent.exportAudio')}
)}
@@ -358,8 +362,8 @@ export function StoryContent() {
>
{sortedItems.length === 0 ? (
-
No items in this story
-
Generate speech using the box below to add items
+
{t('storyContent.empty.title')}
+
{t('storyContent.empty.hint')}
) : (
state.selectedStoryId);
const setSelectedStoryId = useStoryStore((state) => state.setSelectedStoryId);
@@ -72,8 +74,8 @@ export function StoryList() {
const handleCreateStory = () => {
if (!newStoryName.trim()) {
toast({
- title: 'Name required',
- description: 'Please enter a story name',
+ title: t('stories.toast.nameRequired'),
+ description: t('stories.toast.nameRequiredDescription'),
variant: 'destructive',
});
return;
@@ -91,13 +93,13 @@ export function StoryList() {
setNewStoryName('');
setNewStoryDescription('');
toast({
- title: 'Story created',
- description: `"${story.name}" has been created`,
+ title: t('stories.toast.created'),
+ description: t('stories.toast.createdDescription', { name: story.name }),
});
},
onError: (error) => {
toast({
- title: 'Failed to create story',
+ title: t('stories.toast.createFailed'),
description: error.message,
variant: 'destructive',
});
@@ -116,8 +118,8 @@ export function StoryList() {
const handleUpdateStory = () => {
if (!editingStory || !newStoryName.trim()) {
toast({
- title: 'Name required',
- description: 'Please enter a story name',
+ title: t('stories.toast.nameRequired'),
+ description: t('stories.toast.nameRequiredDescription'),
variant: 'destructive',
});
return;
@@ -140,7 +142,7 @@ export function StoryList() {
},
onError: (error) => {
toast({
- title: 'Failed to update story',
+ title: t('stories.toast.updateFailed'),
description: error.message,
variant: 'destructive',
});
@@ -168,7 +170,7 @@ export function StoryList() {
},
onError: (error) => {
toast({
- title: 'Failed to delete story',
+ title: t('stories.toast.deleteFailed'),
description: error.message,
variant: 'destructive',
});
@@ -179,7 +181,7 @@ export function StoryList() {
if (isLoading) {
return (
-
Loading stories...
+
{t('stories.loading')}
);
}
@@ -195,10 +197,10 @@ export function StoryList() {
{/* Fixed Header */}
-
Stories
+
{t('stories.title')}
@@ -211,8 +213,8 @@ export function StoryList() {
{storyList.length === 0 ? (
-
No stories yet
-
Create your first story to get started
+
{t('stories.empty.title')}
+
{t('stories.empty.hint')}
) : (
@@ -225,7 +227,11 @@ export function StoryList() {
'px-5 py-3 rounded-lg transition-colors group flex items-center cursor-pointer',
selectedStoryId === story.id ? 'bg-muted' : 'hover:bg-muted/50',
)}
- aria-label={`Story ${story.name}, ${story.item_count} ${story.item_count === 1 ? 'item' : 'items'}, ${formatDate(story.updated_at)}`}
+ aria-label={t('stories.row.ariaLabel', {
+ name: story.name,
+ count: story.item_count,
+ updated: formatDate(story.updated_at),
+ })}
aria-pressed={selectedStoryId === story.id}
onClick={() => setSelectedStoryId(story.id)}
onKeyDown={(e) => {
@@ -240,9 +246,7 @@ export function StoryList() {
{story.name}
-
- {story.item_count} {story.item_count === 1 ? 'item' : 'items'}
-
+ {t('stories.row.itemCount', { count: story.item_count })}
·
{formatDate(story.updated_at)}
@@ -254,7 +258,7 @@ export function StoryList() {
size="icon"
className="h-7 w-7 opacity-0 group-hover:opacity-100 transition-opacity"
onClick={(e) => e.stopPropagation()}
- aria-label={`Actions for ${story.name}`}
+ aria-label={t('stories.row.actionsLabel', { name: story.name })}
>
@@ -262,14 +266,14 @@ export function StoryList() {
handleEditClick(story)}>
- Edit
+ {t('common.edit')}
handleDeleteClick(story.id)}
className="text-destructive focus:text-destructive"
>
- Delete
+ {t('common.delete')}
@@ -284,17 +288,15 @@ export function StoryList() {