Skip to content

Commit e56f403

Browse files
committed
save
1 parent 7dee2a3 commit e56f403

File tree

3 files changed

+79
-54
lines changed

3 files changed

+79
-54
lines changed

src/components/Footer/Subscribe/Subscribe.tsx

Lines changed: 3 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { useState, useEffect } from "preact/hooks"
33
import MailchimpSubscribe from "react-mailchimp-subscribe"
44
import SubscribeSvg from "~/assets/svgs/footer/subscribe.svg?react"
55
import { clsx } from "~/lib"
6-
import i18next, { changeLanguage, t } from "i18next"
6+
import { t } from "i18next"
7+
import { useI18nReady } from "~/hooks/useI18nReady"
78

89
import EmailInput from "./EmailInput.tsx"
910
import styles from "./Subscribe.module.css"
@@ -19,58 +20,7 @@ export default function Subscribe(props) {
1920
const [email, setEmail] = useState("")
2021
const [customMessage, setCustomMessage] = useState("")
2122
const [emailValid, setEmailValid] = useState(false)
22-
const [isReady, setIsReady] = useState(false)
23-
24-
useEffect(() => {
25-
const ensureResourcesLoaded = async () => {
26-
try {
27-
// Wait for i18next to be initialized
28-
if (!i18next.isInitialized) {
29-
await new Promise((resolve) => {
30-
const handler = () => {
31-
i18next.off("initialized", handler)
32-
resolve(undefined)
33-
}
34-
i18next.on("initialized", handler)
35-
})
36-
}
37-
38-
// changeLanguage will wait for resources to load if they're not already loaded
39-
if (props.lang && i18next.language !== props.lang) {
40-
await changeLanguage(props.lang)
41-
}
42-
43-
// Double-check that resources are actually loaded
44-
const targetLang = props.lang || i18next.language || "en"
45-
if (i18next.hasResourceBundle(targetLang, "translation")) {
46-
setIsReady(true)
47-
} else {
48-
// Wait for resources to be loaded
49-
await new Promise((resolve) => {
50-
const checkResources = () => {
51-
if (i18next.hasResourceBundle(targetLang, "translation")) {
52-
i18next.off("loaded", checkResources)
53-
resolve(undefined)
54-
}
55-
}
56-
i18next.on("loaded", checkResources)
57-
// Check immediately in case resources are already loaded
58-
if (i18next.hasResourceBundle(targetLang, "translation")) {
59-
i18next.off("loaded", checkResources)
60-
resolve(undefined)
61-
}
62-
})
63-
setIsReady(true)
64-
}
65-
} catch (error) {
66-
// If something goes wrong, still set ready to avoid blocking the UI
67-
console.error("Error loading i18next resources:", error)
68-
setIsReady(true)
69-
}
70-
}
71-
72-
ensureResourcesLoaded()
73-
}, [props.lang])
23+
const isReady = useI18nReady(props.lang)
7424

7525
useEffect(() => {
7626
setCustomMessage("")

src/components/RightSidebar/TableOfContents/index.tsx

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import { useStore } from "@nanostores/preact"
33
import type { FunctionalComponent } from "preact"
44
import { useState, useEffect, useRef } from "preact/hooks"
55
import { shouldUpdateToc } from "./tocStore"
6-
import i18next, { t } from "i18next"
6+
import { t } from "i18next"
7+
import { useI18nReady } from "~/hooks/useI18nReady"
78

89
export interface Heading {
910
depth: number
@@ -21,6 +22,7 @@ const TableOfContents: FunctionalComponent<{
2122
const [currentID, setCurrentID] = useState("overview")
2223
const onThisPageID = "on-this-page-heading"
2324
const $shouldUpdateToc = useStore(shouldUpdateToc)
25+
const isReady = useI18nReady()
2426

2527
useEffect(() => {
2628
if (!tableOfContents.current) return
@@ -79,6 +81,10 @@ const TableOfContents: FunctionalComponent<{
7981
setHeadings(headingList)
8082
}
8183

84+
if (!isReady) {
85+
return null
86+
}
87+
8288
return (
8389
<>
8490
<h2 className="heading">{t("rightSidebar.onThisPage")}</h2>

src/hooks/useI18nReady.ts

Lines changed: 69 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,69 @@
1+
import { useState, useEffect } from "preact/hooks"
2+
import i18next, { changeLanguage } from "i18next"
3+
4+
/**
5+
* Custom hook to ensure i18next is initialized and translation resources are loaded
6+
* before rendering components that use translations.
7+
*
8+
* This fixes the issue where Vercel build shows translation keys instead of translated text
9+
* due to SSR/CSR timing differences.
10+
*
11+
* @param lang - Optional language code to change to. If provided, will change language and wait for resources.
12+
* @returns boolean indicating if i18next is ready (initialized and resources loaded)
13+
*/
14+
export function useI18nReady(lang?: string): boolean {
15+
const [isReady, setIsReady] = useState(false)
16+
17+
useEffect(() => {
18+
const ensureResourcesLoaded = async () => {
19+
try {
20+
// Wait for i18next to be initialized
21+
if (!i18next.isInitialized) {
22+
await new Promise<void>((resolve) => {
23+
const handler = () => {
24+
i18next.off("initialized", handler)
25+
resolve()
26+
}
27+
i18next.on("initialized", handler)
28+
})
29+
}
30+
31+
// Change language if needed (this will wait for resources to load)
32+
if (lang && i18next.language !== lang) {
33+
await changeLanguage(lang)
34+
}
35+
36+
// Double-check that resources are actually loaded
37+
const targetLang = lang || i18next.language || "en"
38+
if (i18next.hasResourceBundle(targetLang, "translation")) {
39+
setIsReady(true)
40+
} else {
41+
// Wait for resources to be loaded
42+
await new Promise<void>((resolve) => {
43+
const checkResources = () => {
44+
if (i18next.hasResourceBundle(targetLang, "translation")) {
45+
i18next.off("loaded", checkResources)
46+
resolve()
47+
}
48+
}
49+
i18next.on("loaded", checkResources)
50+
// Check immediately in case resources are already loaded
51+
if (i18next.hasResourceBundle(targetLang, "translation")) {
52+
i18next.off("loaded", checkResources)
53+
resolve()
54+
}
55+
})
56+
setIsReady(true)
57+
}
58+
} catch (error) {
59+
// If something goes wrong, still set ready to avoid blocking the UI
60+
console.error("Error loading i18next resources:", error)
61+
setIsReady(true)
62+
}
63+
}
64+
65+
ensureResourcesLoaded()
66+
}, [lang])
67+
68+
return isReady
69+
}

0 commit comments

Comments
 (0)