diff --git a/messages/de.json b/messages/de.json index 92405f8..9f76ae5 100644 --- a/messages/de.json +++ b/messages/de.json @@ -69,7 +69,11 @@ "selectSpeciesFirst": "Wähle zuerst eine Tierart aus", "noBreedsFound": "Keine Rassen gefunden.", "bio": "Bio", + "bioPlaceholder": "Erzähl uns von {petName}…", + "bioPlaceholderFallback": "Erzähl uns von deinem Tier…", "adoptionStory": "Adoptionsgeschichte", + "adoptionStoryPlaceholder": "Wie kam {petName} zu deiner Familie?", + "adoptionStoryPlaceholderFallback": "Wie kam dein Tier zu deiner Familie?", "birthday": "Geburtstag", "birthdayHint": "Für Geburtstags-Erinnerungen", "adoptedAt": "Adoptionsdatum", @@ -425,5 +429,58 @@ }, "RepostCard": { "pawedBackBy": "Paw-Back von" + }, + "PetsPage": { + "title": "Meine Tiere", + "addPet": "Tier anlegen", + "emptyTitle": "Füge dein erstes Tier hinzu", + "emptyDesc": "Gib deinem Tier ein eigenes Profil auf PawFeed." + }, + "PetProfile": { + "editProfile": "Profil bearbeiten", + "health": "Gesundheit", + "managedBy": "Verwaltet von @{username}", + "statsPosts": "Posts", + "statsFollowers": "Follower", + "statsFollowing": "Folgt", + "adoptionStory": "Adoptionsgeschichte" + }, + "FollowersPage": { + "title": "{count} Follower", + "empty": "{petName} hat noch keine Follower." + }, + "FollowingPage": { + "title": "Folgt {count}", + "empty": "{petName} folgt noch niemandem." + }, + "ProfileActions": { + "message": "Nachricht", + "cannotStartConversation": "Unterhaltung konnte nicht gestartet werden.", + "moreOptions": "Weitere Optionen", + "reportProfile": "Profil melden", + "blockPet": "{petName} blockieren" + }, + "ReportSheet": { + "title": "Melden", + "reasonQuestion": "Warum meldest du das?", + "reasonSpam": "Spam", + "reasonHarassment": "Belästigung oder Mobbing", + "reasonInappropriate": "Unangemessene Inhalte", + "reasonMisinformation": "Falsche oder irreführende Informationen", + "reasonOther": "Etwas anderes", + "notesPlaceholder": "Details hinzufügen (optional)", + "charactersRemaining": "Noch {count} Zeichen", + "submit": "Meldung absenden", + "cancel": "Nicht melden", + "successToast": "Meldung übermittelt. Wir prüfen sie in Kürze.", + "errorToast": "Deine Meldung konnte nicht übermittelt werden. Bitte versuch es erneut." + }, + "BlockDialog": { + "title": "{petName} blockieren?", + "description": "{petName}s Posts und Profil werden für dich ausgeblendet. Niemand erfährt, dass du blockiert hast.", + "cancel": "Nicht blockieren", + "confirm": "Blockieren", + "successToast": "Du hast {petName} blockiert", + "errorToast": "Tier konnte nicht blockiert werden. Bitte versuch es erneut." } } diff --git a/messages/en.json b/messages/en.json index f9e86c0..6c0b5d9 100644 --- a/messages/en.json +++ b/messages/en.json @@ -69,7 +69,11 @@ "selectSpeciesFirst": "Select a species first", "noBreedsFound": "No breeds found.", "bio": "Bio", + "bioPlaceholder": "Tell us about {petName}…", + "bioPlaceholderFallback": "Tell us about your pet…", "adoptionStory": "Adoption story", + "adoptionStoryPlaceholder": "How did {petName} join your family?", + "adoptionStoryPlaceholderFallback": "How did your pet join your family?", "birthday": "Birthday", "birthdayHint": "For birthday reminders", "adoptedAt": "Adoption date", @@ -425,5 +429,58 @@ }, "RepostCard": { "pawedBackBy": "Pawed-back by" + }, + "PetsPage": { + "title": "My Pets", + "addPet": "Add Pet", + "emptyTitle": "Add your first pet", + "emptyDesc": "Give your pet their own profile on PawFeed." + }, + "PetProfile": { + "editProfile": "Edit profile", + "health": "Health", + "managedBy": "Managed by @{username}", + "statsPosts": "posts", + "statsFollowers": "followers", + "statsFollowing": "following", + "adoptionStory": "Adoption story" + }, + "FollowersPage": { + "title": "{count} Followers", + "empty": "{petName} doesn't have any followers yet." + }, + "FollowingPage": { + "title": "Following {count}", + "empty": "{petName} isn't following anyone yet." + }, + "ProfileActions": { + "message": "Message", + "cannotStartConversation": "Cannot start conversation.", + "moreOptions": "More options", + "reportProfile": "Report profile", + "blockPet": "Block {petName}" + }, + "ReportSheet": { + "title": "Report", + "reasonQuestion": "Why are you reporting this?", + "reasonSpam": "Spam", + "reasonHarassment": "Harassment or bullying", + "reasonInappropriate": "Inappropriate content", + "reasonMisinformation": "False or misleading information", + "reasonOther": "Something else", + "notesPlaceholder": "Add details (optional)", + "charactersRemaining": "{count} characters remaining", + "submit": "Submit Report", + "cancel": "Don't report", + "successToast": "Report submitted. We'll review it shortly.", + "errorToast": "Couldn't submit your report. Please try again." + }, + "BlockDialog": { + "title": "Block {petName}?", + "description": "{petName}'s posts and profile will be hidden from you. They won't know you blocked them.", + "cancel": "Don't block", + "confirm": "Block", + "successToast": "You've blocked {petName}", + "errorToast": "Couldn't block this pet. Please try again." } } diff --git a/src/app/(app)/pets/PetGrid.tsx b/src/app/(app)/pets/PetGrid.tsx index ea13c9b..5c1880c 100644 --- a/src/app/(app)/pets/PetGrid.tsx +++ b/src/app/(app)/pets/PetGrid.tsx @@ -2,6 +2,7 @@ import Link from "next/link"; import { useRouter } from "next/navigation"; +import { useTranslations } from "next-intl"; import { PetCard } from "@/components/pet/PetCard"; import { Button } from "@/components/ui/button"; import { useActivePet } from "@/context/ActivePetContext"; @@ -24,21 +25,22 @@ interface PetGridProps { } export function PetGrid({ pets }: PetGridProps) { + const t = useTranslations("PetsPage"); const { activePetId, setActivePet } = useActivePet(); const router = useRouter(); if (pets.length === 0) { return (
-

Add your first pet

+

{t("emptyTitle")}

- Give your pet their own profile on PawFeed. + {t("emptyDesc")}

); @@ -63,7 +65,7 @@ export function PetGrid({ pets }: PetGridProps) { className="flex flex-col items-center justify-center gap-2 rounded-xl border-2 border-dashed border-muted p-6 text-sm text-muted-foreground hover:border-muted-foreground/50 transition-colors min-h-[180px]" > + - Add Pet + {t("addPet")} ); diff --git a/src/app/(app)/pets/[petId]/ProfileActions.tsx b/src/app/(app)/pets/[petId]/ProfileActions.tsx index 2b24b2e..e79b762 100644 --- a/src/app/(app)/pets/[petId]/ProfileActions.tsx +++ b/src/app/(app)/pets/[petId]/ProfileActions.tsx @@ -2,6 +2,7 @@ import { useState } from "react"; import { useRouter } from "next/navigation"; +import { useTranslations } from "next-intl"; import { MoreHorizontal, MessageCircle, Loader2 } from "lucide-react"; import { useMutation } from "@tanstack/react-query"; import { toast } from "sonner"; @@ -40,6 +41,7 @@ interface ProfileActionsProps { } export function ProfileActions({ petId, petName, ownerId }: ProfileActionsProps) { + const t = useTranslations("ProfileActions"); const { activePetId } = useActivePet(); const router = useRouter(); const trpc = useTRPC(); @@ -49,7 +51,7 @@ export function ProfileActions({ petId, petName, ownerId }: ProfileActionsProps) const getOrCreateMutation = useMutation( trpc.conversations.getOrCreate.mutationOptions({ onSuccess: (conv) => router.push(`/messages/${conv.id}`), - onError: (err) => toast.error(err.message ?? "Cannot start conversation."), + onError: (err) => toast.error(err.message ?? t("cannotStartConversation")), }) ); @@ -88,7 +90,7 @@ export function ProfileActions({ petId, petName, ownerId }: ProfileActionsProps) ) : ( )} - Message + {t("message")} )} @@ -98,7 +100,7 @@ export function ProfileActions({ petId, petName, ownerId }: ProfileActionsProps) @@ -107,14 +109,14 @@ export function ProfileActions({ petId, petName, ownerId }: ProfileActionsProps) onClick={() => setReportOpen(true)} className="cursor-pointer" > - Report profile + {t("reportProfile")} setBlockOpen(true)} className="cursor-pointer text-destructive focus:text-destructive" > - Block {petName} + {t("blockPet", { petName })} diff --git a/src/app/(app)/pets/[petId]/followers/page.tsx b/src/app/(app)/pets/[petId]/followers/page.tsx index 3cc26dc..db4d924 100644 --- a/src/app/(app)/pets/[petId]/followers/page.tsx +++ b/src/app/(app)/pets/[petId]/followers/page.tsx @@ -1,4 +1,5 @@ import { notFound } from "next/navigation"; +import { getTranslations } from "next-intl/server"; import { createTRPCCaller } from "@/trpc/server"; import { ScrollArea } from "@/components/ui/scroll-area"; import { FollowersList } from "@/app/(app)/pets/[petId]/followers/FollowersList"; @@ -20,6 +21,7 @@ interface PageProps { export default async function FollowersPage({ params }: PageProps) { const { petId } = await params; + const t = await getTranslations("FollowersPage"); const trpc = await createTRPCCaller(); let pet; @@ -36,12 +38,12 @@ export default async function FollowersPage({ params }: PageProps) { return (

- {followers.length} Followers + {t("title", { count: followers.length })}

{followers.length === 0 ? (

- {pet.name} doesn't have any followers yet. + {t("empty", { petName: pet.name })}

) : ( diff --git a/src/app/(app)/pets/[petId]/following/page.tsx b/src/app/(app)/pets/[petId]/following/page.tsx index fd22c61..77a68a2 100644 --- a/src/app/(app)/pets/[petId]/following/page.tsx +++ b/src/app/(app)/pets/[petId]/following/page.tsx @@ -1,4 +1,5 @@ import { notFound } from "next/navigation"; +import { getTranslations } from "next-intl/server"; import { createTRPCCaller } from "@/trpc/server"; import { ScrollArea } from "@/components/ui/scroll-area"; import { FollowingList } from "@/app/(app)/pets/[petId]/following/FollowingList"; @@ -20,6 +21,7 @@ interface PageProps { export default async function FollowingPage({ params }: PageProps) { const { petId } = await params; + const t = await getTranslations("FollowingPage"); const trpc = await createTRPCCaller(); let pet; @@ -36,12 +38,12 @@ export default async function FollowingPage({ params }: PageProps) { return (

- Following {following.length} + {t("title", { count: following.length })}

{following.length === 0 ? (

- {pet.name} isn't following anyone yet. + {t("empty", { petName: pet.name })}

) : ( diff --git a/src/app/(app)/pets/[petId]/page.tsx b/src/app/(app)/pets/[petId]/page.tsx index 4610468..27d36f8 100644 --- a/src/app/(app)/pets/[petId]/page.tsx +++ b/src/app/(app)/pets/[petId]/page.tsx @@ -1,6 +1,7 @@ import { notFound } from "next/navigation"; import Link from "next/link"; import { currentUser } from "@clerk/nextjs/server"; +import { getTranslations } from "next-intl/server"; import { createTRPCCaller } from "@/trpc/server"; import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"; import { Badge } from "@/components/ui/badge"; @@ -28,6 +29,7 @@ function getInitials(name: string): string { export default async function PetProfilePage({ params }: PageProps) { const { petId } = await params; + const t = await getTranslations("PetProfile"); const trpc = await createTRPCCaller(); let pet; @@ -74,7 +76,7 @@ export default async function PetProfilePage({ params }: PageProps) { {/* Owner attribution — only shown on own pet */} {isOwnPet && ownerUsername && (

- Managed by @{ownerUsername} + {t("managedBy", { username: ownerUsername })}

)} @@ -85,13 +87,13 @@ export default async function PetProfilePage({ params }: PageProps) { href={`/pets/${pet.id}/edit`} className={buttonVariants({ variant: "outline" })} > - Edit profile + {t("editProfile")} - Health + {t("health")}
)} @@ -101,21 +103,21 @@ export default async function PetProfilePage({ params }: PageProps) {
0 - posts + {t("statsPosts")}
{followers.length} - followers + {t("statsFollowers")} {following.length} - following + {t("statsFollowing")}
@@ -135,7 +137,7 @@ export default async function PetProfilePage({ params }: PageProps) { <>
-

Adoption story

+

{t("adoptionStory")}

{pet.adoptionStory}

diff --git a/src/app/(app)/pets/page.tsx b/src/app/(app)/pets/page.tsx index 7b2d9e2..2e0ec38 100644 --- a/src/app/(app)/pets/page.tsx +++ b/src/app/(app)/pets/page.tsx @@ -1,4 +1,5 @@ import Link from "next/link"; +import { getTranslations } from "next-intl/server"; import { createTRPCCaller } from "@/trpc/server"; import { buttonVariants } from "@/components/ui/button"; import { PetGrid } from "./PetGrid"; @@ -15,6 +16,7 @@ export default async function PetsPage({ }: { searchParams: Promise<{ deleteAccount?: string }>; }) { + const t = await getTranslations("PetsPage"); const trpc = await createTRPCCaller(); const pets = await trpc.pets.list(); const { deleteAccount } = await searchParams; @@ -22,10 +24,10 @@ export default async function PetsPage({ return (
-

My Pets

+

{t("title")}

{/* base-nova Button doesn't support asChild — use Link with button styles */} - Add Pet + {t("addPet")}
diff --git a/src/components/pet/PetForm.tsx b/src/components/pet/PetForm.tsx index 4e52316..3f91e0e 100644 --- a/src/components/pet/PetForm.tsx +++ b/src/components/pet/PetForm.tsx @@ -312,7 +312,7 @@ export function PetForm({