Phase 6: Translate User Pages - exchange, trades, invites, profile

- Expand exchange.json with all exchange page strings (page, steps, detailsStep, bookingStep, confirmationStep, priceDisplay)
- Create trades.json translation files for es, en, ca
- Create invites.json translation files for es, en, ca
- Create profile.json translation files for es, en, ca
- Translate exchange page and all components (ExchangeDetailsStep, BookingStep, ConfirmationStep, StepIndicator, PriceDisplay)
- Translate trades page (titles, sections, buttons, status labels)
- Translate invites page (titles, sections, status badges, copy button)
- Translate profile page (form labels, hints, placeholders, messages)
- Update IntlProvider to load all new namespaces
- All frontend tests passing
This commit is contained in:
counterweight 2025-12-25 22:19:13 +01:00
parent 7dd13292a0
commit 246553c402
Signed by: counterweight
GPG key ID: 883EDBAA726BD96C
22 changed files with 559 additions and 115 deletions

View file

@ -6,6 +6,7 @@ import { components } from "../../generated/api";
import { formatDate, formatTime } from "../../utils/date";
import { formatEur } from "../../utils/exchange";
import { bannerStyles } from "../../styles/shared";
import { useTranslation } from "../../hooks/useTranslation";
type BookableSlot = components["schemas"]["BookableSlot"];
type ExchangeResponse = components["schemas"]["ExchangeResponse"];
@ -215,14 +216,15 @@ export function BookingStep({
onSlotSelect,
onBackToDetails,
}: BookingStepProps) {
const t = useTranslation("exchange");
return (
<>
{/* Trade Summary Card */}
<div style={styles.summaryCard}>
<div style={styles.summaryHeader}>
<span style={styles.summaryTitle}>Your Exchange</span>
<span style={styles.summaryTitle}>{t("bookingStep.yourExchange")}</span>
<button onClick={onBackToDetails} style={styles.editButton}>
Edit
{t("bookingStep.edit")}
</button>
</div>
<div style={styles.summaryDetails}>
@ -232,7 +234,7 @@ export function BookingStep({
color: direction === "buy" ? "#4ade80" : "#f87171",
}}
>
{direction === "buy" ? "Buy" : "Sell"} BTC
{direction === "buy" ? t("bookingStep.buy") : t("bookingStep.sell")} BTC
</span>
<span style={styles.summaryDivider}></span>
<span>{formatEur(eurAmount)}</span>
@ -242,15 +244,17 @@ export function BookingStep({
</span>
<span style={styles.summaryDivider}></span>
<span style={styles.summaryPaymentMethod}>
{direction === "buy" ? "Receive via " : "Send via "}
{bitcoinTransferMethod === "onchain" ? "Onchain" : "Lightning"}
{direction === "buy" ? t("bookingStep.receiveVia") : t("bookingStep.sendVia")}{" "}
{bitcoinTransferMethod === "onchain"
? t("transferMethod.onchain")
: t("transferMethod.lightning")}
</span>
</div>
</div>
{/* Date Selection */}
<div style={styles.section}>
<h2 style={styles.sectionTitle}>Select a Date</h2>
<h2 style={styles.sectionTitle}>{t("bookingStep.selectDate")}</h2>
<div style={styles.dateGrid}>
{dates.map((date) => {
const dateStr = formatDate(date);
@ -291,15 +295,13 @@ export function BookingStep({
{/* Warning for existing trade on selected date */}
{existingTradeOnSelectedDate && (
<div style={bannerStyles.errorBanner}>
<div>
You already have a trade booked on this day. You can only book one trade per day.
</div>
<div>{t("bookingStep.existingTradeWarning")}</div>
<div style={styles.errorLink}>
<a
href={`/trades/${existingTradeOnSelectedDate.public_id}`}
style={styles.errorLinkAnchor}
>
View your existing trade
{t("bookingStep.viewExistingTrade")}
</a>
</div>
</div>
@ -309,7 +311,7 @@ export function BookingStep({
{selectedDate && !existingTradeOnSelectedDate && (
<div style={styles.section}>
<h2 style={styles.sectionTitle}>
Available Slots for{" "}
{t("bookingStep.availableSlots")}{" "}
{selectedDate.toLocaleDateString("en-US", {
weekday: "long",
month: "long",
@ -318,9 +320,9 @@ export function BookingStep({
</h2>
{isLoadingSlots ? (
<div style={styles.emptyState}>Loading slots...</div>
<div style={styles.emptyState}>{t("bookingStep.loadingSlots")}</div>
) : availableSlots.length === 0 ? (
<div style={styles.emptyState}>No available slots for this date</div>
<div style={styles.emptyState}>{t("bookingStep.noSlots")}</div>
) : (
<div style={styles.slotGrid}>
{availableSlots.map((slot) => {