From 9357386bbd042ab7e3409f22e0f011dd7913778c Mon Sep 17 00:00:00 2001 From: SkateSpots Date: Fri, 2 Oct 2026 15:19:38 +0700 Subject: [PATCH] =?UTF-8?q?i18n(es):=20batch=203=20part=202=20=E2=80=94=20?= =?UTF-8?q?locale-aware=20dates=20&=20relative=20times?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - New src/lib/dateFnsI18n.ts re-exports date-fns with formatDistanceToNow / formatDistanceToNowStrict / formatDistance / format defaulting to the active locale, plus dateLocaleCode() for native toLocale{Date,Time}String calls. - LanguageProvider keeps the locale in sync during render, so the first paint is already in-language. - Codemod: 11 date-fns imports now use the shim; ~39 native date/time calls pass the app locale code instead of undefined/[]/hardcoded 'en-US'. Number .toLocaleString() calls are deliberately untouched. --- src/components/ActivityNotificationCard.tsx | 2 +- src/components/CrewChat.tsx | 2 +- src/components/EscrowPanel.tsx | 3 +- src/components/EventCard.tsx | 9 +-- src/components/EventUpdates.tsx | 2 +- src/components/EventsCalendar.tsx | 7 ++- src/components/GearSwapItemCard.tsx | 2 +- src/components/GrantProCard.tsx | 3 +- src/components/HostingDashboard.tsx | 5 +- src/components/MarketplaceItemCard.tsx | 2 +- src/components/MyDataTab.tsx | 2 +- src/components/NewsletterIssueView.tsx | 3 +- src/components/NotificationBell.tsx | 3 +- src/components/RouteDialog.tsx | 3 +- src/components/SasquatchGuide.tsx | 3 +- src/components/SasquatchProDialog.tsx | 3 +- src/components/SpotChat.tsx | 2 +- src/components/SpotMap.tsx | 7 ++- src/components/TourCard.tsx | 5 +- src/components/TradeReputationPanel.tsx | 3 +- src/components/comments/Comment.tsx | 2 +- src/components/wallet/CashuWalletCard.tsx | 3 +- src/contexts/LanguageContext.tsx | 6 ++ src/hooks/useCrewAgentChat.ts | 3 +- src/hooks/usePurchasePro.ts | 3 +- src/lib/dateFnsI18n.ts | 64 +++++++++++++++++++++ src/lib/riderCard.ts | 7 ++- src/lib/routing.ts | 4 +- src/lib/tripPlanner.ts | 3 +- src/pages/CoachDetailPage.tsx | 5 +- src/pages/CollectionDetailPage.tsx | 3 +- src/pages/ConversationPage.tsx | 2 +- src/pages/DirectMessagesPage.tsx | 2 +- src/pages/EditEventPage.tsx | 5 +- src/pages/EventDetailPage.tsx | 13 +++-- src/pages/GroupConversationPage.tsx | 2 +- src/pages/ListingDetailPage.tsx | 3 +- src/pages/NIP19Page.tsx | 3 +- src/pages/NewsletterPage.tsx | 7 ++- src/pages/RiderCardPage.tsx | 3 +- src/pages/ShopDetailPage.tsx | 3 +- src/pages/SpotCollectionsPage.tsx | 3 +- src/pages/SpotDetailPage.tsx | 5 +- src/pages/TourDetailPage.tsx | 5 +- src/pages/TradesPage.tsx | 3 +- 45 files changed, 168 insertions(+), 65 deletions(-) create mode 100644 src/lib/dateFnsI18n.ts diff --git a/src/components/ActivityNotificationCard.tsx b/src/components/ActivityNotificationCard.tsx index 8816af9..63db429 100644 --- a/src/components/ActivityNotificationCard.tsx +++ b/src/components/ActivityNotificationCard.tsx @@ -1,5 +1,5 @@ import { Link } from 'react-router-dom'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import { Heart, Repeat2, Zap, AtSign, MessageCircle, CalendarCheck, Megaphone } from 'lucide-react'; import { Avatar, AvatarFallback, AvatarImage } from '@/components/ui/avatar'; import { useAuthor } from '@/hooks/useAuthor'; diff --git a/src/components/CrewChat.tsx b/src/components/CrewChat.tsx index c0f2caa..5aff88c 100644 --- a/src/components/CrewChat.tsx +++ b/src/components/CrewChat.tsx @@ -1,7 +1,7 @@ import { useEffect, useMemo, useRef, useState } from 'react'; import { Link } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import { Lock, Send, Loader2, ShieldCheck, MessageSquare, EyeOff } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; diff --git a/src/components/EscrowPanel.tsx b/src/components/EscrowPanel.tsx index f0406b9..dd07e6c 100644 --- a/src/components/EscrowPanel.tsx +++ b/src/components/EscrowPanel.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useMemo } from 'react'; import { Lock } from 'lucide-react'; import { parseEscrowToken, type EscrowTokenInfo } from '@/lib/cashuEscrow'; @@ -13,7 +14,7 @@ function hostOf(url: string): string { } function formatDate(seconds: number): string { - return new Date(seconds * 1000).toLocaleDateString(undefined, { + return new Date(seconds * 1000).toLocaleDateString(dateLocaleCode(), { year: 'numeric', month: 'short', day: 'numeric', diff --git a/src/components/EventCard.tsx b/src/components/EventCard.tsx index 5fbb7f6..a1da7ec 100644 --- a/src/components/EventCard.tsx +++ b/src/components/EventCard.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState } from 'react'; import { Link } from 'react-router-dom'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; @@ -54,7 +55,7 @@ interface EventCardProps { function formatDate(timestamp: number): string { const date = new Date(timestamp * 1000); - return date.toLocaleDateString('en-US', { + return date.toLocaleDateString(dateLocaleCode(), { weekday: 'short', month: 'short', day: 'numeric', @@ -63,7 +64,7 @@ function formatDate(timestamp: number): string { function formatTime(timestamp: number): string { const date = new Date(timestamp * 1000); - return date.toLocaleTimeString('en-US', { + return date.toLocaleTimeString(dateLocaleCode(), { hour: 'numeric', minute: '2-digit', }); @@ -99,7 +100,7 @@ export function EventCard({ event, compact = false, goingCount: goingCountProp }
- {new Date(event.start * 1000).toLocaleDateString('en-US', { month: 'short' })} + {new Date(event.start * 1000).toLocaleDateString(dateLocaleCode(), { month: 'short' })}
{new Date(event.start * 1000).getDate()} @@ -169,7 +170,7 @@ export function EventCard({ event, compact = false, goingCount: goingCountProp }
- {new Date(event.start * 1000).toLocaleDateString('en-US', { month: 'short' })} + {new Date(event.start * 1000).toLocaleDateString(dateLocaleCode(), { month: 'short' })}
{new Date(event.start * 1000).getDate()} diff --git a/src/components/EventUpdates.tsx b/src/components/EventUpdates.tsx index 5e5e5b9..ae487ce 100644 --- a/src/components/EventUpdates.tsx +++ b/src/components/EventUpdates.tsx @@ -1,5 +1,5 @@ import { useState } from 'react'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import { Megaphone, Loader2, Send, CalendarClock, MapPin, XCircle, Info } from 'lucide-react'; import { Textarea } from '@/components/ui/textarea'; import { Button } from '@/components/ui/button'; diff --git a/src/components/EventsCalendar.tsx b/src/components/EventsCalendar.tsx index bd01fc3..45c971b 100644 --- a/src/components/EventsCalendar.tsx +++ b/src/components/EventsCalendar.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useMemo, useState, useRef, useEffect } from 'react'; import { Link } from 'react-router-dom'; import { ChevronLeft, ChevronRight, Calendar as CalendarIcon, Route, MapPin, Clock, Users } from 'lucide-react'; @@ -132,11 +133,11 @@ function itemOnDay(item: CalendarItem, day: Date): boolean { } function formatTime(unix: number): string { - return new Date(unix * 1000).toLocaleTimeString([], { hour: 'numeric', minute: '2-digit' }); + return new Date(unix * 1000).toLocaleTimeString(dateLocaleCode(), { hour: 'numeric', minute: '2-digit' }); } function formatDayLong(d: Date): string { - return d.toLocaleDateString([], { weekday: 'long', month: 'long', day: 'numeric' }); + return d.toLocaleDateString(dateLocaleCode(), { weekday: 'long', month: 'long', day: 'numeric' }); } // ─── Component ──────────────────────────────────────────────────────────────── @@ -376,7 +377,7 @@ export function EventsCalendar({ events, tours, goingCounts, show, anchorUnix }: {multiDay - ? `${new Date(it.start * 1000).toLocaleDateString([], { month: 'short', day: 'numeric' })} – ${new Date((it.end as number) * 1000).toLocaleDateString([], { month: 'short', day: 'numeric' })}` + ? `${new Date(it.start * 1000).toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric' })} – ${new Date((it.end as number) * 1000).toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric' })}` : it.allDay ? t('placesUi.calendar.allDay') : formatTime(it.start)} diff --git a/src/components/GearSwapItemCard.tsx b/src/components/GearSwapItemCard.tsx index 659adf7..e4210c0 100644 --- a/src/components/GearSwapItemCard.tsx +++ b/src/components/GearSwapItemCard.tsx @@ -21,7 +21,7 @@ import { MessageCircle, MapPin, Search, CheckCircle, ShieldCheck, AlertTriangle, import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger, DropdownMenuSeparator } from '@/components/ui/dropdown-menu'; import { AlertDialog, AlertDialogAction, AlertDialogCancel, AlertDialogContent, AlertDialogDescription, AlertDialogFooter, AlertDialogHeader, AlertDialogTitle } from '@/components/ui/alert-dialog'; import { ReportDialog } from '@/components/ReportDialog'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import { useTranslation } from '@/contexts/language'; interface GearSwapItemCardProps { diff --git a/src/components/GrantProCard.tsx b/src/components/GrantProCard.tsx index 32a08d9..e6e5bba 100644 --- a/src/components/GrantProCard.tsx +++ b/src/components/GrantProCard.tsx @@ -8,6 +8,7 @@ * AI proxy both honour it. */ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState } from 'react'; import { nip19 } from 'nostr-tools'; import { useNostr } from '@nostrify/react'; @@ -87,7 +88,7 @@ export function GrantProCard() { title: t('settingsUi.grantPro.grantedTitle'), description: t('settingsUi.grantPro.grantedDesc', { npub: `${nip19.npubEncode(pubkey).slice(0, 14)}…`, - date: new Date(expiry * 1000).toLocaleDateString(), + date: new Date(expiry * 1000).toLocaleDateString(dateLocaleCode()), }), }); setTarget(''); diff --git a/src/components/HostingDashboard.tsx b/src/components/HostingDashboard.tsx index 77660c0..3f0da9d 100644 --- a/src/components/HostingDashboard.tsx +++ b/src/components/HostingDashboard.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useMemo } from 'react'; import { Link } from 'react-router-dom'; import { Calendar, Users, Pencil, Megaphone, Plus, CalendarClock } from 'lucide-react'; @@ -11,7 +12,7 @@ import { cn } from '@/lib/utils'; import type { SkateEvent } from '@/lib/types'; function formatWhen(ts: number): string { - return new Date(ts * 1000).toLocaleDateString(undefined, { + return new Date(ts * 1000).toLocaleDateString(dateLocaleCode(), { weekday: 'short', month: 'short', day: 'numeric', @@ -32,7 +33,7 @@ function HostedEventRow({ event, isPast }: { event: SkateEvent; isPast: boolean
- {new Date(event.start * 1000).toLocaleDateString(undefined, { month: 'short' })} + {new Date(event.start * 1000).toLocaleDateString(dateLocaleCode(), { month: 'short' })}
{new Date(event.start * 1000).getDate()} diff --git a/src/components/MarketplaceItemCard.tsx b/src/components/MarketplaceItemCard.tsx index e7ca5dd..6befd38 100644 --- a/src/components/MarketplaceItemCard.tsx +++ b/src/components/MarketplaceItemCard.tsx @@ -28,7 +28,7 @@ import { getLastLocation } from '@/lib/lastLocation'; import { itemDistanceKm } from '@/lib/sortOptions'; import { TrustBadge } from '@/components/TrustBadge'; import { Clock, Search, Tag, Flag, MoreVertical, ShieldCheck, Calendar, Package, AlertTriangle, CheckCircle, RotateCcw, Trash2, Loader2 } from 'lucide-react'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import type { MarketplaceItem } from '@/lib/types'; import { GEAR_CONDITIONS, GEAR_CATEGORIES, PRICE_TYPES, LISTING_REPORT_REASONS, formatPrice } from '@/lib/types'; import { useTranslation } from '@/contexts/language'; diff --git a/src/components/MyDataTab.tsx b/src/components/MyDataTab.tsx index e3f8b3d..77fa2bc 100644 --- a/src/components/MyDataTab.tsx +++ b/src/components/MyDataTab.tsx @@ -39,7 +39,7 @@ import { Heart, Zap, } from 'lucide-react'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import { useTranslation } from '@/contexts/language'; // Kind constants diff --git a/src/components/NewsletterIssueView.tsx b/src/components/NewsletterIssueView.tsx index 6b2dcb6..e26426f 100644 --- a/src/components/NewsletterIssueView.tsx +++ b/src/components/NewsletterIssueView.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useMemo } from 'react'; import { ImageWithFallback } from '@/components/ImageWithFallback'; import { Link } from 'react-router-dom'; @@ -152,7 +153,7 @@ export function NewsletterIssueView({ pubkey, identifier }: { pubkey: string; id const authorName = author.data?.metadata?.name ?? genUserName(pubkey); const authorPic = author.data?.metadata?.picture; - const date = new Date(issue.publishedAt * 1000).toLocaleDateString('en-US', { + const date = new Date(issue.publishedAt * 1000).toLocaleDateString(dateLocaleCode(), { year: 'numeric', month: 'long', day: 'numeric', diff --git a/src/components/NotificationBell.tsx b/src/components/NotificationBell.tsx index 93e9ae3..b31c9cd 100644 --- a/src/components/NotificationBell.tsx +++ b/src/components/NotificationBell.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useEffect, useMemo, useState } from 'react'; import { Link } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; @@ -148,7 +149,7 @@ function NewsletterItem({ pubkey: issue.pubkey, identifier: issue.identifier, }); - const date = new Date(issue.publishedAt * 1000).toLocaleDateString(undefined, { + const date = new Date(issue.publishedAt * 1000).toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric', }); diff --git a/src/components/RouteDialog.tsx b/src/components/RouteDialog.tsx index b19af07..a598e2d 100644 --- a/src/components/RouteDialog.tsx +++ b/src/components/RouteDialog.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useEffect, useRef, useState, useCallback, useMemo } from 'react'; import L from 'leaflet'; import { Loader2, MapPin, Navigation, AlertTriangle, LocateFixed, Play, Square, Crosshair, Volume2, VolumeX, Check, Search } from 'lucide-react'; @@ -117,7 +118,7 @@ function speedValue(distanceM: number, durationS: number): string | undefined { /** Local clock time you'd arrive if you set off now. */ function etaClock(durationS: number): string { - return new Date(Date.now() + durationS * 1000).toLocaleTimeString([], { + return new Date(Date.now() + durationS * 1000).toLocaleTimeString(dateLocaleCode(), { hour: '2-digit', minute: '2-digit', }); diff --git a/src/components/SasquatchGuide.tsx b/src/components/SasquatchGuide.tsx index 5f642ce..f1c0214 100644 --- a/src/components/SasquatchGuide.tsx +++ b/src/components/SasquatchGuide.tsx @@ -7,6 +7,7 @@ * Falls back to static FAQ if AI is unavailable. */ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState, useEffect, useRef, useCallback, useMemo } from 'react'; import { Input } from '@/components/ui/input'; @@ -1656,7 +1657,7 @@ Instructions: {isPro ? ( ⚡ Pro{proDaysLeft !== null ? ` · ${proDaysLeft}d` : ''} diff --git a/src/components/SasquatchProDialog.tsx b/src/components/SasquatchProDialog.tsx index 9f1721c..6de310e 100644 --- a/src/components/SasquatchProDialog.tsx +++ b/src/components/SasquatchProDialog.tsx @@ -6,6 +6,7 @@ * no-ads, community-funded app — Pro is the thank-you. */ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useEffect, useState } from 'react'; import QRCode from 'qrcode'; import { @@ -112,7 +113,7 @@ export function SasquatchProDialog({

{t('sasquatchUi.proSupporter')}

{expiresAt && (

- {t('sasquatchUi.activeUntil', { date: new Date(expiresAt * 1000).toLocaleDateString() })} + {t('sasquatchUi.activeUntil', { date: new Date(expiresAt * 1000).toLocaleDateString(dateLocaleCode()) })}

)}
diff --git a/src/components/SpotChat.tsx b/src/components/SpotChat.tsx index 8bf5c3a..9665dd3 100644 --- a/src/components/SpotChat.tsx +++ b/src/components/SpotChat.tsx @@ -1,5 +1,5 @@ import { useEffect, useMemo, useRef, useState } from 'react'; -import { formatDistanceToNowStrict } from 'date-fns'; +import { formatDistanceToNowStrict } from '@/lib/dateFnsI18n'; import { Link } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; import { MessageCircle, Send, Radio, Info, Globe } from 'lucide-react'; diff --git a/src/components/SpotMap.tsx b/src/components/SpotMap.tsx index 61926f9..c2d4b03 100644 --- a/src/components/SpotMap.tsx +++ b/src/components/SpotMap.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useEffect, useRef, useMemo, useCallback } from 'react'; import L from 'leaflet'; import 'leaflet.markercluster'; @@ -621,7 +622,7 @@ function createPopupContent( const dayDiv = document.createElement('div'); dayDiv.className = 'p-1'; const date = new Date(day.date); - const dayName = date.toLocaleDateString('en-US', { weekday: 'short' }); + const dayName = date.toLocaleDateString(dateLocaleCode(), { weekday: 'short' }); dayDiv.innerHTML = `
${escapeHtml(dayName)}
${escapeHtml(day.weatherIcon)}
@@ -1372,8 +1373,8 @@ export function SpotMap({ const details = document.createElement('div'); details.className = 'space-y-1.5 mb-3 text-xs text-gray-300'; const startDate = new Date(evt.start * 1000); - const dateStr = startDate.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' }); - const timeStr = startDate.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }); + const dateStr = startDate.toLocaleDateString(dateLocaleCode(), { weekday: 'short', month: 'short', day: 'numeric' }); + const timeStr = startDate.toLocaleTimeString(dateLocaleCode(), { hour: 'numeric', minute: '2-digit' }); details.appendChild(popupRow('🗓️', `${dateStr} · ${timeStr}`)); if (evt.location) { details.appendChild(popupRow('📍', evt.location)); diff --git a/src/components/TourCard.tsx b/src/components/TourCard.tsx index 8020f9e..544c882 100644 --- a/src/components/TourCard.tsx +++ b/src/components/TourCard.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState } from 'react'; import { Link } from 'react-router-dom'; import { Card, CardContent, CardHeader } from '@/components/ui/card'; @@ -72,12 +73,12 @@ function formatDateRange(startDate?: number, endDate?: number, withTime = false) }); } - const startStr = start.toLocaleDateString('en-US', { month: 'short', day: 'numeric' }); + const startStr = start.toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric' }); if (!endDate) return startStr; const end = new Date(endDate * 1000); - const endStr = end.toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' }); + const endStr = end.toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric', year: 'numeric' }); return `${startStr} - ${endStr}`; } diff --git a/src/components/TradeReputationPanel.tsx b/src/components/TradeReputationPanel.tsx index 8ce7dea..f3cefb1 100644 --- a/src/components/TradeReputationPanel.tsx +++ b/src/components/TradeReputationPanel.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { Link } from 'react-router-dom'; import type { NostrEvent } from '@nostrify/nostrify'; import { Star, ThumbsUp, ArrowUpRight } from 'lucide-react'; @@ -28,7 +29,7 @@ function ReviewRow({ ev, selfPubkey }: { ev: NostrEvent; selfPubkey: string }) { const picture = author.data?.metadata?.picture; const given = ev.pubkey === selfPubkey; - const date = new Date(ev.created_at * 1000).toLocaleDateString(); + const date = new Date(ev.created_at * 1000).toLocaleDateString(dateLocaleCode()); return (
diff --git a/src/components/comments/Comment.tsx b/src/components/comments/Comment.tsx index e42dc3d..5b963f7 100644 --- a/src/components/comments/Comment.tsx +++ b/src/components/comments/Comment.tsx @@ -13,7 +13,7 @@ import { Card, CardContent } from '@/components/ui/card'; import { Collapsible, CollapsibleContent, CollapsibleTrigger } from '@/components/ui/collapsible'; import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '@/components/ui/dropdown-menu'; import { MessageSquare, ChevronDown, ChevronRight, MoreHorizontal, Flag } from 'lucide-react'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import { genUserName } from '@/lib/genUserName'; interface CommentProps { diff --git a/src/components/wallet/CashuWalletCard.tsx b/src/components/wallet/CashuWalletCard.tsx index 5ff5b45..9d387bd 100644 --- a/src/components/wallet/CashuWalletCard.tsx +++ b/src/components/wallet/CashuWalletCard.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState, useEffect, useCallback } from 'react'; import { useCashu } from '@/hooks/wallet/useCashu'; import { useMintManager } from '@/hooks/wallet/useMintManager'; @@ -637,7 +638,7 @@ function CashuWalletCardNative() { {entry.type === 'receive' && t('walletUi.cashu.receivedToken')}
- {new Date(entry.timestamp).toLocaleDateString()} + {new Date(entry.timestamp).toLocaleDateString(dateLocaleCode())}
diff --git a/src/contexts/LanguageContext.tsx b/src/contexts/LanguageContext.tsx index 9427066..a6dfec2 100644 --- a/src/contexts/LanguageContext.tsx +++ b/src/contexts/LanguageContext.tsx @@ -7,6 +7,7 @@ import { translations, type Language, } from './language'; +import { setDateLocale } from '@/lib/dateFnsI18n'; interface LanguageProviderProps { children: ReactNode; @@ -73,6 +74,11 @@ export function LanguageProvider({ children }: LanguageProviderProps) { // Currently no RTL languages supported, but ready for future const isRTL = false; + // Keep date-fns + native toLocale* formatting in the app language. Called + // during render (before children) so the first paint is already in-language; + // it is an idempotent module assignment, not React state. + setDateLocale(language); + return ( {children} diff --git a/src/hooks/useCrewAgentChat.ts b/src/hooks/useCrewAgentChat.ts index 4773100..4f2acb8 100644 --- a/src/hooks/useCrewAgentChat.ts +++ b/src/hooks/useCrewAgentChat.ts @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState, useCallback } from 'react'; import { useCVMClient } from '@/hooks/useCVMClient'; import { callSasquatchAI, parseToolCall, type ChatTurn } from '@/lib/sasquatchBrain'; @@ -64,7 +65,7 @@ export interface CrewContext { function fmtDate(unix: number): string { try { - return new Date(unix * 1000).toLocaleDateString(undefined, { + return new Date(unix * 1000).toLocaleDateString(dateLocaleCode(), { weekday: 'short', month: 'short', day: 'numeric', diff --git a/src/hooks/usePurchasePro.ts b/src/hooks/usePurchasePro.ts index ab4c7a7..fd54152 100644 --- a/src/hooks/usePurchasePro.ts +++ b/src/hooks/usePurchasePro.ts @@ -14,6 +14,7 @@ * Verification is handled separately by useSasquatchPro. */ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useCallback, useState } from 'react'; import { useNostr } from '@nostrify/react'; import { useCurrentUser } from '@/hooks/useCurrentUser'; @@ -59,7 +60,7 @@ export function usePurchasePro() { const sendConfirmationDM = useCallback( async (tier: ProTier, expiry: number) => { if (!user?.pubkey || !user.signer?.nip44) return; - const until = new Date(expiry * 1000).toLocaleDateString(undefined, { + const until = new Date(expiry * 1000).toLocaleDateString(dateLocaleCode(), { year: 'numeric', month: 'long', day: 'numeric', }); const content = [ diff --git a/src/lib/dateFnsI18n.ts b/src/lib/dateFnsI18n.ts new file mode 100644 index 0000000..094e702 --- /dev/null +++ b/src/lib/dateFnsI18n.ts @@ -0,0 +1,64 @@ +/** + * Locale-aware date/number formatting. + * + * The app ships English + Spanish, but `date-fns` calls `formatDistanceToNow` + * with the default (English) locale and native `Date.toLocaleDateString()` + * falls back to the *runtime* locale, not the app language. This module keeps a + * single module-level locale in sync with `LanguageProvider` and re-exports all + * of `date-fns` with `formatDistanceToNow` / `formatDistance` / `format` + * overridden to apply it. Import from here instead of `date-fns` directly. + * + * `dateLocaleCode()` feeds native `toLocaleDateString/TimeString` calls so a + * Spanish user sees "5 ene" rather than "Jan 5". + */ +export * from 'date-fns'; + +import { + formatDistanceToNow as _formatDistanceToNow, + formatDistanceToNowStrict as _formatDistanceToNowStrict, + formatDistance as _formatDistance, + format as _format, +} from 'date-fns'; +import { enUS, es as esLocale } from 'date-fns/locale'; +import type { Locale } from 'date-fns'; +import type { Language } from '@/contexts/language'; + +let activeLocale: Locale = enUS; +let activeCode = 'en-US'; + +/** Called by LanguageProvider (during render) so children format in-language. */ +export function setDateLocale(language: Language): void { + activeLocale = language === 'es' ? esLocale : enUS; + activeCode = language === 'es' ? 'es-ES' : 'en-US'; +} + +/** The active date-fns locale. */ +export function dateLocale(): Locale { + return activeLocale; +} + +/** BCP-47 code for native `toLocale*` / `Intl` APIs. */ +export function dateLocaleCode(): string { + return activeCode; +} + +type DistOptions = Parameters[1]; +type DistStrictOptions = Parameters[1]; +type FormatDistanceOptions = Parameters[2]; +type FormatOptions = Parameters[2]; + +export function formatDistanceToNow(date: Date | number, options?: DistOptions) { + return _formatDistanceToNow(date, { ...options, locale: options?.locale ?? activeLocale }); +} + +export function formatDistanceToNowStrict(date: Date | number, options?: DistStrictOptions) { + return _formatDistanceToNowStrict(date, { ...options, locale: options?.locale ?? activeLocale }); +} + +export function formatDistance(date: Date | number, baseDate: Date | number, options?: FormatDistanceOptions) { + return _formatDistance(date, baseDate, { ...options, locale: options?.locale ?? activeLocale }); +} + +export function format(date: Date | number, formatStr: string, options?: FormatOptions) { + return _format(date, formatStr, { ...options, locale: options?.locale ?? activeLocale }); +} diff --git a/src/lib/riderCard.ts b/src/lib/riderCard.ts index 6b5ba5a..c7108a4 100644 --- a/src/lib/riderCard.ts +++ b/src/lib/riderCard.ts @@ -10,6 +10,7 @@ * 35679 — Park Access Card (addressable). A venue/owner signs it for a rider. * 1293 — Skill badge (regular). A witness signs it for a rider. */ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { nip19 } from 'nostr-tools'; import type { NostrEvent } from '@nostrify/nostrify'; @@ -193,11 +194,11 @@ export function roleMeta(role: string) { /** Short, locale-aware date (+ time range) for the event pass. */ export function formatCardDate(start: number, end?: number): string { const s = new Date(start * 1000); - const date = s.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); - const time = s.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' }); + const date = s.toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric', year: 'numeric' }); + const time = s.toLocaleTimeString(dateLocaleCode(), { hour: '2-digit', minute: '2-digit' }); if (end && end > start) { const e = new Date(end * 1000); - return `${date} · ${time}–${e.toLocaleTimeString(undefined, { hour: '2-digit', minute: '2-digit' })}`; + return `${date} · ${time}–${e.toLocaleTimeString(dateLocaleCode(), { hour: '2-digit', minute: '2-digit' })}`; } return `${date} · ${time}`; } diff --git a/src/lib/routing.ts b/src/lib/routing.ts index 8b65a9f..900c901 100644 --- a/src/lib/routing.ts +++ b/src/lib/routing.ts @@ -22,6 +22,8 @@ * where none is published we report that honestly instead of faking a route. */ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; + /** Travel modes offered in the UI. */ export type TravelMode = 'foot' | 'bike' | 'moto' | 'car' | 'transit'; @@ -513,7 +515,7 @@ export async function fetchStopDepartures( export function formatClock(iso: string): string { const d = new Date(iso); if (Number.isNaN(d.getTime())) return ''; - return d.toLocaleTimeString([], { hour: '2-digit', minute: '2-digit' }); + return d.toLocaleTimeString(dateLocaleCode(), { hour: '2-digit', minute: '2-digit' }); } // ─── Formatting helpers ────────────────────────────────────────────────────── diff --git a/src/lib/tripPlanner.ts b/src/lib/tripPlanner.ts index 95c852f..8cdc29b 100644 --- a/src/lib/tripPlanner.ts +++ b/src/lib/tripPlanner.ts @@ -8,6 +8,7 @@ * the real travel sites for the parts we can't (and shouldn't) do ourselves. */ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { SKATE_SPOT_KIND } from '@/lib/types'; // ─── Shared result shapes (mirror useCVMClient return items) ───────────────── @@ -127,7 +128,7 @@ export function buildItinerary(opts: { const eventStops: ItineraryStop[] = opts.events.map((e) => ({ kind: 'event' as const, title: e.title, - subtitle: e.start ? new Date(e.start).toLocaleDateString(undefined, { month: 'short', day: 'numeric' }) : e.location, + subtitle: e.start ? new Date(e.start).toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric' }) : e.location, url: e.url, })); diff --git a/src/pages/CoachDetailPage.tsx b/src/pages/CoachDetailPage.tsx index 040a747..9c4bca5 100644 --- a/src/pages/CoachDetailPage.tsx +++ b/src/pages/CoachDetailPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useSeoMeta } from '@unhead/react'; import { useParams, Link, useNavigate } from 'react-router-dom'; import { PageError } from '@/components/ErrorState'; @@ -288,7 +289,7 @@ export default function CoachDetailPage() {

- {t('labUi.detail.joined')} {new Date(profile.createdAt * 1000).toLocaleDateString()} + {t('labUi.detail.joined')} {new Date(profile.createdAt * 1000).toLocaleDateString(dateLocaleCode())}

@@ -550,7 +551,7 @@ export default function CoachDetailPage() {

{gig.title}

- {new Date(gig.start * 1000).toLocaleDateString('en-US', { month: 'short', day: 'numeric', year: 'numeric' })} + {new Date(gig.start * 1000).toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric', year: 'numeric' })} {gig.location ? ` · ${gig.location}` : ''}

diff --git a/src/pages/CollectionDetailPage.tsx b/src/pages/CollectionDetailPage.tsx index 5f88ccd..f12926f 100644 --- a/src/pages/CollectionDetailPage.tsx +++ b/src/pages/CollectionDetailPage.tsx @@ -1,4 +1,5 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useParams, Link, useNavigate } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; import { useNostr } from '@nostrify/react'; @@ -264,7 +265,7 @@ export default function CollectionDetailPage() { - {t('walletUi.collection.created', { date: new Date(collection.createdAt * 1000).toLocaleDateString() })} + {t('walletUi.collection.created', { date: new Date(collection.createdAt * 1000).toLocaleDateString(dateLocaleCode()) })}
diff --git a/src/pages/ConversationPage.tsx b/src/pages/ConversationPage.tsx index df38a2d..a3dd39d 100644 --- a/src/pages/ConversationPage.tsx +++ b/src/pages/ConversationPage.tsx @@ -20,7 +20,7 @@ import { EscrowPanel } from '@/components/EscrowPanel'; import { useDMRead } from '@/hooks/useDMRead'; import { useAuthor } from '@/hooks/useAuthor'; import { genUserName } from '@/lib/genUserName'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import { ArrowLeft, Send, diff --git a/src/pages/DirectMessagesPage.tsx b/src/pages/DirectMessagesPage.tsx index fffc757..b6be14a 100644 --- a/src/pages/DirectMessagesPage.tsx +++ b/src/pages/DirectMessagesPage.tsx @@ -18,7 +18,7 @@ import { useDMRead } from '@/hooks/useDMRead'; import { useAuthor } from '@/hooks/useAuthor'; import { genUserName } from '@/lib/genUserName'; import { useLocalStorage } from '@/hooks/useLocalStorage'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import { MessageCircle, Plus, diff --git a/src/pages/EditEventPage.tsx b/src/pages/EditEventPage.tsx index 57c4f10..62cdba5 100644 --- a/src/pages/EditEventPage.tsx +++ b/src/pages/EditEventPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState, useEffect } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useNavigate, useParams } from 'react-router-dom'; @@ -347,8 +348,8 @@ export default function EditEventPage() { if (timeChanged) { parts.push( t('placesUi.editEvent.newTime', { - date: startDateTime.toLocaleDateString(undefined, { weekday: 'short', month: 'short', day: 'numeric' }), - time: startDateTime.toLocaleTimeString(undefined, { hour: 'numeric', minute: '2-digit' }), + date: startDateTime.toLocaleDateString(dateLocaleCode(), { weekday: 'short', month: 'short', day: 'numeric' }), + time: startDateTime.toLocaleTimeString(dateLocaleCode(), { hour: 'numeric', minute: '2-digit' }), }), ); } diff --git a/src/pages/EventDetailPage.tsx b/src/pages/EventDetailPage.tsx index ca13774..512cc04 100644 --- a/src/pages/EventDetailPage.tsx +++ b/src/pages/EventDetailPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useMemo, useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useParams, Link } from 'react-router-dom'; @@ -51,7 +52,7 @@ function eventTypeLabel(t: (key: string) => string, type: string): string { function formatTime(timestamp: number): string { const date = new Date(timestamp * 1000); - return date.toLocaleTimeString('en-US', { + return date.toLocaleTimeString(dateLocaleCode(), { hour: 'numeric', minute: '2-digit', }); @@ -109,7 +110,7 @@ export default function EventDetailPage() { const bits: string[] = []; if (event.start) { bits.push( - new Date(event.start * 1000).toLocaleDateString(undefined, { + new Date(event.start * 1000).toLocaleDateString(dateLocaleCode(), { weekday: 'short', month: 'short', day: 'numeric', @@ -440,7 +441,7 @@ export default function EventDetailPage() {
- {new Date(event.start * 1000).toLocaleDateString(undefined, { month: 'short' })} + {new Date(event.start * 1000).toLocaleDateString(dateLocaleCode(), { month: 'short' })} {new Date(event.start * 1000).getDate()} @@ -449,7 +450,7 @@ export default function EventDetailPage() {

{t('placesUi.eventDetail.date')}

- {new Date(event.start * 1000).toLocaleDateString(undefined, { weekday: 'long' })} + {new Date(event.start * 1000).toLocaleDateString(dateLocaleCode(), { weekday: 'long' })}

@@ -524,13 +525,13 @@ export default function EventDetailPage() {
- {new Date(event.start * 1000).toLocaleDateString('en-US', { month: 'long' })} + {new Date(event.start * 1000).toLocaleDateString(dateLocaleCode(), { month: 'long' })}
{new Date(event.start * 1000).getDate()}
- {new Date(event.start * 1000).toLocaleDateString('en-US', { weekday: 'long' })} + {new Date(event.start * 1000).toLocaleDateString(dateLocaleCode(), { weekday: 'long' })}
{formatTime(event.start)} diff --git a/src/pages/GroupConversationPage.tsx b/src/pages/GroupConversationPage.tsx index 393e87e..78cbee7 100644 --- a/src/pages/GroupConversationPage.tsx +++ b/src/pages/GroupConversationPage.tsx @@ -14,7 +14,7 @@ import { hasNip44 as checkNip44 } from '@/lib/signerCapabilities'; import { useGroupConversation, useSendDirectMessage } from '@/hooks/useDirectMessages'; import { useAuthor } from '@/hooks/useAuthor'; import { genUserName } from '@/lib/genUserName'; -import { formatDistanceToNow } from 'date-fns'; +import { formatDistanceToNow } from '@/lib/dateFnsI18n'; import { ArrowLeft, Lock, Loader2, RefreshCw, Send, Users } from 'lucide-react'; import { cn } from '@/lib/utils'; diff --git a/src/pages/ListingDetailPage.tsx b/src/pages/ListingDetailPage.tsx index 479ceb4..4d6d160 100644 --- a/src/pages/ListingDetailPage.tsx +++ b/src/pages/ListingDetailPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState } from 'react'; import { useParams, Link, useNavigate } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; @@ -243,7 +244,7 @@ export default function ListingDetailPage() { )}
{' '} - {t('marketUi.listingDetail.listed', { date: new Date(item.createdAt * 1000).toLocaleDateString() })} + {t('marketUi.listingDetail.listed', { date: new Date(item.createdAt * 1000).toLocaleDateString(dateLocaleCode()) })}
diff --git a/src/pages/NIP19Page.tsx b/src/pages/NIP19Page.tsx index 6655dfc..04c957e 100644 --- a/src/pages/NIP19Page.tsx +++ b/src/pages/NIP19Page.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { nip19 } from 'nostr-tools'; import { useParams, Link } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; @@ -41,7 +42,7 @@ import { useTranslation } from '@/contexts/language'; /** Format a "member since" year from a kind-0 event timestamp (seconds). */ function profileEventCreatedAt(createdAt: number | undefined): string | undefined { if (!createdAt) return undefined; - return new Date(createdAt * 1000).toLocaleDateString(undefined, { year: 'numeric', month: 'short' }); + return new Date(createdAt * 1000).toLocaleDateString(dateLocaleCode(), { year: 'numeric', month: 'short' }); } /** A stat cell for the at-a-glance grid. Clickable (jumps to its section) when diff --git a/src/pages/NewsletterPage.tsx b/src/pages/NewsletterPage.tsx index 6666e3c..2e8078f 100644 --- a/src/pages/NewsletterPage.tsx +++ b/src/pages/NewsletterPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useEffect, useRef, useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { nip19 } from 'nostr-tools'; @@ -65,7 +66,7 @@ function NostrPreviewModal({ const displayName = metadata?.name ?? genUserName(user?.pubkey ?? ''); const avatar = metadata?.picture; const now = new Date(); - const timeStr = now.toLocaleTimeString('en-US', { hour: 'numeric', minute: '2-digit' }); + const timeStr = now.toLocaleTimeString(dateLocaleCode(), { hour: 'numeric', minute: '2-digit' }); const [editing, setEditing] = useState(false); const [editedText, setEditedText] = useState(issue.nostrText); @@ -397,7 +398,7 @@ function SpotOfTheWeekBlock({ spot }: { spot: SpotOfTheWeek }) { // ─── Helpers ────────────────────────────────────────────────────────────────── function formatIssueDate(iso: string): string { - return new Date(iso).toLocaleDateString('en-US', { + return new Date(iso).toLocaleDateString(dateLocaleCode(), { year: 'numeric', month: 'long', day: 'numeric', }); } @@ -547,7 +548,7 @@ function LiveIssueCard({ issue }: { issue: SkateNewsletterIssue }) { pubkey: issue.pubkey, identifier: issue.identifier, }); - const date = new Date(issue.publishedAt * 1000).toLocaleDateString('en-US', { + const date = new Date(issue.publishedAt * 1000).toLocaleDateString(dateLocaleCode(), { year: 'numeric', month: 'long', day: 'numeric', diff --git a/src/pages/RiderCardPage.tsx b/src/pages/RiderCardPage.tsx index 9a98f16..06a1b17 100644 --- a/src/pages/RiderCardPage.tsx +++ b/src/pages/RiderCardPage.tsx @@ -13,6 +13,7 @@ * Print-ready: credit-card size (85.6×54mm), @media print isolates card */ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState, useEffect, useRef, useCallback, useMemo } from 'react'; import { useSeoMeta } from '@unhead/react'; import { nip19 } from 'nostr-tools'; @@ -150,7 +151,7 @@ function BadgeEventRow({ event }: { event: NostrEvent }) {

- {new Date(event.created_at * 1000).toLocaleDateString()} + {new Date(event.created_at * 1000).toLocaleDateString(dateLocaleCode())}
); diff --git a/src/pages/ShopDetailPage.tsx b/src/pages/ShopDetailPage.tsx index 418af9c..f993b16 100644 --- a/src/pages/ShopDetailPage.tsx +++ b/src/pages/ShopDetailPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useMemo, useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useParams, Link, useNavigate } from 'react-router-dom'; @@ -480,7 +481,7 @@ export default function ShopDetailPage() {

{displayName}

- {new Date(shop.createdAt * 1000).toLocaleDateString()} + {new Date(shop.createdAt * 1000).toLocaleDateString(dateLocaleCode())}

diff --git a/src/pages/SpotCollectionsPage.tsx b/src/pages/SpotCollectionsPage.tsx index e0786f9..fe056de 100644 --- a/src/pages/SpotCollectionsPage.tsx +++ b/src/pages/SpotCollectionsPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState } from 'react'; import { Link } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; @@ -100,7 +101,7 @@ function CollectionCard({
- {new Date(collection.createdAt * 1000).toLocaleDateString()} + {new Date(collection.createdAt * 1000).toLocaleDateString(dateLocaleCode())} {t('mapUi.view')} diff --git a/src/pages/SpotDetailPage.tsx b/src/pages/SpotDetailPage.tsx index 2c65b54..67e0114 100644 --- a/src/pages/SpotDetailPage.tsx +++ b/src/pages/SpotDetailPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useMemo, useState } from 'react'; import { useSeoMeta } from '@unhead/react'; import { useParams, Link, useNavigate } from 'react-router-dom'; @@ -1047,7 +1048,7 @@ export default function SpotDetailPage() {
{weatherData.forecast.daily.map((day, idx) => { const date = new Date(day.date); - const dayName = idx === 0 ? t('spotUi.today') : idx === 1 ? t('spotUi.tomorrow') : date.toLocaleDateString('en-US', { weekday: 'short' }); + const dayName = idx === 0 ? t('spotUi.today') : idx === 1 ? t('spotUi.tomorrow') : date.toLocaleDateString(dateLocaleCode(), { weekday: 'short' }); return (
{dayName}
@@ -1149,7 +1150,7 @@ export default function SpotDetailPage() {

- {new Date(spot.createdAt * 1000).toLocaleDateString()} + {new Date(spot.createdAt * 1000).toLocaleDateString(dateLocaleCode())}

diff --git a/src/pages/TourDetailPage.tsx b/src/pages/TourDetailPage.tsx index 2660a04..f582ae5 100644 --- a/src/pages/TourDetailPage.tsx +++ b/src/pages/TourDetailPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useSeoMeta } from '@unhead/react'; import { useParams, Link, useNavigate } from 'react-router-dom'; import { nip19 } from 'nostr-tools'; @@ -149,10 +150,10 @@ export default function TourDetailPage() { minute: '2-digit', }); } - const startStr = start.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); + const startStr = start.toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric', year: 'numeric' }); if (tour.endDate) { const end = new Date(tour.endDate * 1000); - const endStr = end.toLocaleDateString(undefined, { month: 'short', day: 'numeric', year: 'numeric' }); + const endStr = end.toLocaleDateString(dateLocaleCode(), { month: 'short', day: 'numeric', year: 'numeric' }); return `${startStr} → ${endStr}`; } return startStr; diff --git a/src/pages/TradesPage.tsx b/src/pages/TradesPage.tsx index 5c11a06..aba2d98 100644 --- a/src/pages/TradesPage.tsx +++ b/src/pages/TradesPage.tsx @@ -1,3 +1,4 @@ +import { dateLocaleCode } from '@/lib/dateFnsI18n'; import { useState } from 'react'; import { Link } from 'react-router-dom'; import { useSeoMeta } from '@unhead/react'; @@ -281,7 +282,7 @@ export default function TradesPage() { {counterparty ? <>{t('marketUi.trades.with')} : t('marketUi.trades.roleTrade').toLowerCase()}

- {new Date(ev.created_at * 1000).toLocaleDateString()} + {new Date(ev.created_at * 1000).toLocaleDateString(dateLocaleCode())} {state !== 'valid' && ` · ${state}`}