Fix translation bug in nav bars

This commit is contained in:
MartinBraquet
2025-12-27 11:00:55 +02:00
parent a3e51f06e3
commit e8431845b1
6 changed files with 61 additions and 68 deletions

View File

@@ -1,3 +1,3 @@
{
"version": 20
"version": 21
}

View File

@@ -2,7 +2,7 @@ import Link from 'next/link'
import clsx from 'clsx'
import {MenuAlt3Icon} from '@heroicons/react/solid'
import {Dialog, Transition} from '@headlessui/react'
import {Fragment, useState, useRef, useEffect} from 'react'
import {Fragment, useEffect, useRef, useState} from 'react'
import {useRouter} from 'next/router'
import Sidebar from './sidebar'
import {Item} from './sidebar-item'
@@ -49,7 +49,7 @@ export function BottomNavBar(props: {
>
{navigationOptions.map((item) => (
<NavBarItem
key={item.name}
key={item.key} // Remove, as no key prop?
item={item}
currentPage={currentPage}
user={user}
@@ -128,6 +128,7 @@ function NavBarItem(props: {
className?: string
}) {
const {item, currentPage, children, user} = props
const t = useT()
const track = trackCallback(`navbar: ${item.trackingEventName ?? item.name}`)
const [touched, setTouched] = useState(false)
if (item.name === 'Profile' && user) {
@@ -143,6 +144,12 @@ function NavBarItem(props: {
)
}
const element = <>
{item.icon && <item.icon className="mx-auto my-1 h-6 w-6"/>}
{children}
{t(item.key, item.name)}
</>
if (!item.href) {
return (
<button
@@ -154,9 +161,7 @@ function NavBarItem(props: {
onTouchStart={() => setTouched(true)}
onTouchEnd={() => setTouched(false)}
>
{item.icon && <item.icon className="mx-auto my-1 h-6 w-6"/>}
{children}
{item.name}
{element}
</button>
)
}
@@ -176,9 +181,7 @@ function NavBarItem(props: {
onTouchStart={() => setTouched(true)}
onTouchEnd={() => setTouched(false)}
>
{item.icon && <item.icon className="mx-auto my-1 h-6 w-6"/>}
{children}
{item.name}
{element}
</Link>
)
}
@@ -249,9 +252,9 @@ export function MobileSidebar(props: {
}
const target = document.body
target.addEventListener('pointerdown', onPointerDown, { passive: false })
target.addEventListener('pointermove', onPointerMove, { passive: false })
target.addEventListener('pointerup', onPointerUp, { passive: false })
target.addEventListener('pointerdown', onPointerDown, {passive: false})
target.addEventListener('pointermove', onPointerMove, {passive: false})
target.addEventListener('pointerup', onPointerUp, {passive: false})
return () => {

View File

@@ -1,10 +1,12 @@
import clsx from 'clsx'
import Link from 'next/link'
import { track } from 'web/lib/service/analytics'
import {track} from 'web/lib/service/analytics'
import {useT} from "web/lib/locale";
export type Item = {
name: string
key: string
children?: React.ReactNode
trackingEventName?: string
href?: string
@@ -13,7 +15,9 @@ export type Item = {
}
export function SidebarItem(props: { item: Item; currentPage?: string }) {
const { item, currentPage } = props
const {item, currentPage} = props
const t = useT()
const currentBasePath = '/' + (currentPage?.split('/')[1] ?? '')
const isCurrentPage =
@@ -45,7 +49,7 @@ export function SidebarItem(props: { item: Item; currentPage?: string }) {
aria-hidden="true"
/>
)}
<span className="truncate">{item.children ?? item.name}</span>
<span className="truncate">{item.children ?? t(item.key, item.name)}</span>
</>
)

View File

@@ -12,10 +12,10 @@ import {Button, ColorType, SizeType} from 'web/components/buttons/button'
import {signupRedirect} from 'web/lib/util/signup'
import {useProfile} from 'web/hooks/use-profile'
import Image from 'next/image'
import {ANDROID_APP_URL} from "common/constants";
import {isAndroidApp} from "web/lib/util/webview";
import {ANDROID_APP_URL} from "common/constants"
import {isAndroidApp} from "web/lib/util/webview"
import {useT} from 'web/lib/locale'
import {LanguagePicker} from "web/components/language/language-picker";
import {LanguagePicker} from "web/components/language/language-picker"
export default function Sidebar(props: {
className?: string
@@ -32,7 +32,7 @@ export default function Sidebar(props: {
const navOptions = props.navigationOptions
const t = useT()
const bottomNavOptions = bottomNav(!!user, t)
const bottomNavOptions = bottomNav(!!user)
const isAndroid = isAndroidApp()
@@ -52,7 +52,7 @@ export default function Sidebar(props: {
<div className="mb-4 flex flex-col gap-1">
{navOptions.map((item) => (
<SidebarItem key={item.name} item={item} currentPage={currentPage}/>
<SidebarItem key={item.key} item={item} currentPage={currentPage}/>
))}
{!isAndroid && <Image
src="https://firebasestorage.googleapis.com/v0/b/compass-130ba.firebasestorage.app/o/misc%2FGoogle_Play_Store_badge_EN.svg.png?alt=media&token=3e0e8605-800a-422b-84d1-8ecec8af3e80"
@@ -75,7 +75,7 @@ export default function Sidebar(props: {
<div className="mb-[calc(24px+env(safe-area-inset-bottom))] mt-auto flex flex-col gap-1">
{user === null && <LanguagePicker className={'w-fit mx-3 pr-12 mb-2'}/>}
{bottomNavOptions.map((item) => (
<SidebarItem key={item.name} item={item} currentPage={currentPage}/>
<SidebarItem key={item.key} item={item} currentPage={currentPage}/>
))}
</div>
</nav>
@@ -91,11 +91,10 @@ const logout = async () => {
const bottomNav = (
loggedIn: boolean,
t: (k: string, fallback: string) => string,
) =>
buildArray<Item>(
!loggedIn && {name: t('nav.sign_in', 'Sign in'), icon: LoginIcon, href: '/signin'},
loggedIn && {name: t('nav.sign_out', 'Sign out'), icon: LogoutIcon, onClick: logout}
!loggedIn && {key: 'nav.sign_in', name: 'Sign in', icon: LoginIcon, href: '/signin'},
loggedIn && {key: 'nav.sign_out', name: 'Sign out', icon: LogoutIcon, onClick: logout}
)
export const SignUpButton = (props: {

View File

@@ -12,7 +12,7 @@ import clsx from 'clsx'
import {User} from 'common/user'
import {buildArray} from 'common/util/array'
import {useOnline} from 'web/hooks/use-online'
import {ReactNode, useEffect, useState} from 'react'
import {ReactNode, useState} from 'react'
import {Toaster} from 'react-hot-toast'
import {Col} from 'web/components/layout/col'
import {PrivateMessagesIcon} from 'web/components/messaging/messages-icon'
@@ -28,7 +28,6 @@ import {NotificationsIcon, SolidNotificationsIcon} from './notifications-icon'
import {IS_MAINTENANCE} from "common/constants"
import {MdThumbUp} from "react-icons/md"
import {FaEnvelope} from "react-icons/fa"
import {useLocale, useT} from 'web/lib/locale'
export function PageBase(props: {
trackPageView: string | false
@@ -50,9 +49,6 @@ export function PageBase(props: {
const isMobile = useIsMobile()
const profile = useProfile()
const t = useT()
const {locale} = useLocale()
const bottomNavOptions = user
? getBottomNavigation(user, profile)
: getBottomSignedOutNavigation()
@@ -61,22 +57,6 @@ export function PageBase(props: {
const mobileSidebarOptions = getMobileSidebar(user, () => setIsAddFundsModalOpen(true))
useEffect(() => {
// Add this helper function to translate navigation options
const translateNavOptions = (options: any[]) => {
options.forEach(option => {
const key = `nav.${option.key}`;
option.name = t(key, option.name)
console.log(key)
})
}
translateNavOptions(bottomNavOptions)
translateNavOptions(desktopSidebarOptions)
translateNavOptions(mobileSidebarOptions)
}, [locale, bottomNavOptions, desktopSidebarOptions, mobileSidebarOptions, t]);
// eslint-disable-next-line react-hooks/rules-of-hooks
trackPageView && useTracking(`view ${trackPageView}`, trackPageProps)
useOnline()
@@ -128,20 +108,20 @@ export function PageBase(props: {
)
}
const Profiles = {key: 'people', name: 'People', href: '/', icon: UsersIcon}
const Home = {key: 'home', name: 'Home', href: '/', icon: HomeIcon}
const faq = {key: 'faq', name: 'FAQ', href: '/faq', icon: SolidQuestionIcon}
const About = {key: 'about', name: 'About', href: '/about', icon: QuestionMarkCircleIcon}
const Signin = {key: 'signin', name: 'Sign in', href: '/signin', icon: UserCircleIcon}
const Notifs = {key: 'notifs', name: 'Notifs', href: `/notifications`, icon: NotificationsIcon}
const NotifsSolid = {key: 'notifs', name: 'Notifs', href: `/notifications`, icon: SolidNotificationsIcon}
const Messages = {key: 'messages', name: 'Messages', href: '/messages', icon: PrivateMessagesIcon}
const Social = {key: 'social', name: 'Social', href: '/social', icon: LinkIcon}
const Organization = {key: 'organization', name: 'Organization', href: '/organization', icon: GlobeAltIcon}
const Vote = {key: 'vote', name: 'Vote', href: '/vote', icon: MdThumbUp}
const Contact = {key: 'contact', name: 'Contact', href: '/contact', icon: FaEnvelope}
const News = {key: 'news', name: "What's new", href: '/news', icon: NewspaperIcon}
const Settings = {key: 'settings', name: "Settings", href: '/settings', icon: CogIcon}
const Profiles = {key: 'nav.people', name: 'People', href: '/', icon: UsersIcon}
const Home = {key: 'nav.home', name: 'Home', href: '/', icon: HomeIcon}
const faq = {key: 'nav.faq', name: 'FAQ', href: '/faq', icon: SolidQuestionIcon}
const About = {key: 'nav.about', name: 'About', href: '/about', icon: QuestionMarkCircleIcon}
const Signin = {key: 'nav.sign_in', name: 'Sign in', href: '/signin', icon: UserCircleIcon}
const Notifs = {key: 'nav.notifs', name: 'Notifs', href: `/notifications`, icon: NotificationsIcon}
const NotifsSolid = {key: 'nav.notifs', name: 'Notifs', href: `/notifications`, icon: SolidNotificationsIcon}
const Messages = {key: 'nav.messages', name: 'Messages', href: '/messages', icon: PrivateMessagesIcon}
const Social = {key: 'nav.social', name: 'Social', href: '/social', icon: LinkIcon}
const Organization = {key: 'nav.organization', name: 'Organization', href: '/organization', icon: GlobeAltIcon}
const Vote = {key: 'nav.vote', name: 'Vote', href: '/vote', icon: MdThumbUp}
const Contact = {key: 'nav.contact', name: 'Contact', href: '/contact', icon: FaEnvelope}
const News = {key: 'nav.news', name: "What's new", href: '/news', icon: NewspaperIcon}
const Settings = {key: 'nav.settings', name: "Settings", href: '/settings', icon: CogIcon}
const base = [
About,
@@ -158,13 +138,13 @@ function getBottomNavigation(user: User, profile: Profile | null | undefined) {
Profiles,
NotifsSolid,
{
key: 'profile',
key: 'nav.profile',
name: 'Profile',
href: profile === null ? '/signup' : `/${user.username}`,
icon: SolidHomeIcon,
},
{
key: 'messages',
key: 'nav.messages',
name: 'Messages',
href: '/messages',
icon: (props) => (

View File

@@ -24,21 +24,28 @@ export function useLocale() {
// return messages[locale]?.[key] ?? english
// }
const messageCache: Record<string, Record<string, string>> = {}
export function useT() {
const {locale} = useLocale()
console.log({locale})
const [messages, setMessages] = useState<Record<string, string>>({})
const [messages, setMessages] = useState<Record<string, string>>(
messageCache[locale] ?? {}
)
useEffect(() => {
if (locale === defaultLocale) return
if (messageCache[locale]) {
setMessages(messageCache[locale])
return
}
import(`web/messages/${locale}.json`)
.then((mod) => setMessages(mod.default))
.then(mod => {
messageCache[locale] = mod.default
setMessages(mod.default)
})
.catch(() => setMessages({}))
}, [locale])
return (key: string, fallback: string) => {
if (locale === defaultLocale) return fallback
return messages[key] ?? fallback
}
return (key: string, fallback: string) => locale === defaultLocale ? fallback : messages[key] ?? fallback
}