Skip to content

Commit fbf9fd2

Browse files
ChengaDevclaude
andcommitted
Persist language preference, fix lang attribute flash, localize SEO titles
- LanguageProvider now initializes language from URL path on first render, so the html lang attribute is correct immediately (no flash of 'en') - Save language choice to localStorage on every change - EnglishLayout redirects from / to saved language if user has a preference - All page SEO titles and descriptions now derived from locals translations Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 108df4e commit fbf9fd2

7 files changed

Lines changed: 33 additions & 12 deletions

File tree

src/App.tsx

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,13 +38,22 @@ const PageContent: React.FC<PageContentProps> = ({ children, title }) => {
3838
)
3939
}
4040

41+
const LANG_KEY = 'sc_lang'
42+
4143
// Layout for English routes — no URL lang prefix
4244
const EnglishLayout = () => {
4345
const { changeLocale } = useLocalization()
46+
const navigate = useNavigate()
47+
const location = useLocation()
4448
const fadeIn = useSpring({ from: { opacity: 0 }, to: { opacity: 1 }, config: { duration: 800 } })
4549

4650
useEffect(() => {
47-
changeLocale('en')
51+
const savedLang = localStorage.getItem(LANG_KEY)
52+
if (savedLang && savedLang !== 'en' && NON_ENGLISH_LANGS.includes(savedLang) && location.pathname === '/') {
53+
navigate(`/${savedLang}`, { replace: true })
54+
} else {
55+
changeLocale('en')
56+
}
4857
// eslint-disable-next-line react-hooks/exhaustive-deps
4958
}, [])
5059

src/components/AboutUs.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -20,8 +20,8 @@ const AboutUs = () => {
2020
return (
2121
<Container>
2222
<SEO
23-
title="About Us | ShotClock Pro"
24-
description="Learn about ShotClock Pro – a free basketball shot clock training app built for referees, scorekeepers, and basketball enthusiasts who want to master FIBA shot clock rules."
23+
title={`${locals.aboutTitle} | ShotClock Pro`}
24+
description={locals.aboutContent[0]}
2525
/>
2626
<AnimatedTitle>{locals.aboutTitle}</AnimatedTitle>
2727

src/components/FAQ.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -33,8 +33,8 @@ const FAQ = () => {
3333
return (
3434
<Container>
3535
<SEO
36-
title="FAQ | ShotClock Pro"
37-
description="Frequently asked questions about basketball shot clock rules, the 14-second reset, and how to use the ShotClock Pro training simulator."
36+
title={`${locals.faqTitle} | ShotClock Pro`}
37+
description={locals.faqDescription}
3838
schema={faqSchema}
3939
/>
4040

src/components/FIBAResources.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,8 +44,8 @@ const FIBAResources = () => {
4444
return (
4545
<Container>
4646
<SEO
47-
title="FIBA Resources | ShotClock Pro"
48-
description="Access official FIBA basketball rules, shot clock operation guidelines, referee training materials, and competition manuals to stay up to date with international basketball regulations."
47+
title={`${locals.fibaResourcesTitle} | ShotClock Pro`}
48+
description={locals.fibaResourcesDescription}
4949
/>
5050
<AnimatedTitle>{locals.fibaResourcesTitle}</AnimatedTitle>
5151

src/components/Instructions.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,8 @@ const Instructions = () => {
3737
return (
3838
<Container>
3939
<SEO
40-
title="Shot Clock Instructions | ShotClock Pro"
41-
description="Step-by-step instructions for operating a basketball shot clock. Learn the 24-second and 14-second reset rules, basic controls, and game situations per FIBA regulations."
40+
title={`${locals.instructionsTitle} | ShotClock Pro`}
41+
description={locals.instructionsDescription}
4242
schema={howToSchema}
4343
/>
4444
<AnimatedTitle>{locals.instructionsTitle}</AnimatedTitle>

src/components/ShotClockPage.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,8 @@ const ShotClockPage = () => {
1111
return (
1212
<PageContainer>
1313
<SEO
14-
title="ShotClock Pro – Free Online Basketball Shot Clock"
15-
description="Free online basketball shot clock — start, stop, and reset in seconds. Practice on any device, no installation needed."
14+
title={locals.title}
15+
description={locals.pageBlurb}
1616
/>
1717
<GlobalStyle />
1818
<ShotClock />

src/contexts/Language/LanguageProvider.tsx

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,17 @@
11
import React, { useState, createContext, useContext, FC, PropsWithChildren } from 'react'
2+
import { useLocation } from 'react-router-dom'
23
import { LocalizationContextType } from './types'
34
import Localization from '../../localization/locailzation'
45
import LanguageCodes from '../../constants/LanguageCodes'
56

7+
const LANG_KEY = 'sc_lang'
8+
const VALID_NON_ENGLISH = ['it', 'es', 'fr', 'el']
9+
10+
const getLangFromPath = (pathname: string): string => {
11+
const maybeLang = pathname.split('/')[1]
12+
return VALID_NON_ENGLISH.includes(maybeLang) ? maybeLang : LanguageCodes.English
13+
}
14+
615
const languageContextDefaultValues: LocalizationContextType = {
716
languageCode: LanguageCodes.English,
817
locals: Localization[LanguageCodes.English],
@@ -14,12 +23,15 @@ export const LocalizationContext = createContext<LocalizationContextType>(
1423
)
1524

1625
const LanguageProvider: FC<PropsWithChildren> = ({ children }) => {
26+
const { pathname } = useLocation()
27+
1728
const [languageCode, setLanguageCode] = useState<string>(
18-
LanguageCodes.English
29+
() => getLangFromPath(pathname)
1930
)
2031

2132
const changeLocale = (newLanguageCode: string) => {
2233
setLanguageCode(newLanguageCode)
34+
localStorage.setItem(LANG_KEY, newLanguageCode)
2335
}
2436

2537
return (

0 commit comments

Comments
 (0)