Skip to content

Commit 8ef7835

Browse files
committed
i18n: Switch to useTranslation so I can translate in loops
1 parent 43d2e21 commit 8ef7835

File tree

5 files changed

+45
-63
lines changed

5 files changed

+45
-63
lines changed

src/app/page.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ import Image from "next/image";
1515
import { IconCheck } from "@tabler/icons-react";
1616
import React from "react";
1717
import { getLatestReleases } from "@/app/downloads/github";
18-
import { t } from "@/app/translate";
18+
import { useTranslation } from "@/app/translate";
1919
import { GithubRelease } from "./downloads/config";
2020

2121
const InteractiveLogo = dynamic(() => import("../components/logo"), {
@@ -27,6 +27,7 @@ const Installers = dynamic(() => import("./installers"), {
2727
});
2828

2929
export default function Home() {
30+
const { t } = useTranslation();
3031
const [latest, setLatest] = React.useState<GithubRelease | null>(null);
3132

3233
React.useEffect(() => {

src/app/translate.tsx

Lines changed: 35 additions & 59 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
"use client";
22

3-
import React, { useEffect, useState } from "react";
3+
import React, { useEffect, useState, useCallback } from "react";
44
import defaultTranslations from "@/i18n/translations.en.json";
55

66
const languages = {
@@ -58,82 +58,58 @@ const getNestedTranslation = (
5858
break;
5959
}
6060
}
61-
if (typeof acc === "string") {
62-
return acc;
63-
}
64-
return undefined;
61+
return typeof acc === "string" ? acc : undefined;
6562
};
6663

67-
async function translate(translationKey: string) {
68-
const language = await getAvailableLanguage();
69-
70-
// Helper function to load translations
71-
const loadTranslations = async (lang: string) => {
72-
try {
73-
return await import(`@/i18n/translations.${lang}.json`);
74-
} catch {
75-
console.warn(`Translation file for language "${lang}" not found.`);
76-
return null;
77-
}
78-
};
79-
80-
// Load translations for the selected language and the default language
81-
const translations = await loadTranslations(language);
82-
83-
// Attempt to get the translation in the selected language, then fall back to default
84-
const translation =
85-
getNestedTranslation(translations, translationKey) ||
86-
getNestedTranslation(defaultTranslations, translationKey);
87-
88-
// Render the translation if found; otherwise, return the key
89-
return translation || translationKey;
64+
async function fetchTranslations(lang: string) {
65+
try {
66+
const translations = await import(`@/i18n/translations.${lang}.json`);
67+
return translations;
68+
} catch {
69+
console.warn(`Translation file for language "${lang}" not found.`);
70+
return null;
71+
}
9072
}
9173

92-
export const t = (translationKey: string): string => {
93-
const defaultTranslation =
94-
getNestedTranslation(defaultTranslations, translationKey) || translationKey;
95-
const [translation, setTranslation] = useState(defaultTranslation);
96-
97-
const updateTranslation = async () => {
98-
const translatedText = await translate(translationKey);
99-
setTranslation(translatedText);
100-
};
74+
export function useTranslation() {
75+
const [translations, setTranslations] =
76+
useState<TranslationObject>(defaultTranslations);
10177

10278
useEffect(() => {
103-
// Initial translation update
104-
updateTranslation();
105-
106-
// Update translation when the language in localStorage changes from other browsing context
107-
const handleStorageChange = (event: StorageEvent) => {
108-
if (event.key === "next-export-i18n-lang") {
109-
updateTranslation();
110-
}
79+
const fetchLanguageAndTranslations = async () => {
80+
const lang = await getAvailableLanguage();
81+
const loadedTranslations = await fetchTranslations(lang);
82+
setTranslations(loadedTranslations || defaultTranslations);
11183
};
11284

113-
// Update translation when the language in localStorage changes from current browsing context
114-
const handleLocalStorageLangChange = () => {
115-
updateTranslation();
116-
};
85+
fetchLanguageAndTranslations();
86+
87+
const handleLocalStorageLangChange = () => fetchLanguageAndTranslations();
11788

118-
// Listen for localStorage changes
119-
window.addEventListener("storage", handleStorageChange);
12089
window.addEventListener(
12190
"localStorageLangChange",
12291
handleLocalStorageLangChange,
12392
);
124-
125-
// Clean up listener on component unmount
126-
return () => {
127-
window.removeEventListener("storage", handleStorageChange);
93+
return () =>
12894
window.removeEventListener(
12995
"localStorageLangChange",
13096
handleLocalStorageLangChange,
13197
);
132-
};
133-
}, [translationKey]);
98+
}, []);
13499

135-
return translation;
136-
};
100+
const t = useCallback(
101+
(translationKey: string): string => {
102+
return (
103+
getNestedTranslation(translations, translationKey) ||
104+
getNestedTranslation(defaultTranslations, translationKey) ||
105+
translationKey
106+
);
107+
},
108+
[translations],
109+
);
110+
111+
return { t };
112+
}
137113

138114
export const LanguageSelector: React.FC<LanguageSelectorProps> = ({
139115
className,

src/components/footer.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@
22

33
import { Container, Group, ActionIcon, rem, Text } from "@mantine/core";
44
import Link from "next/link";
5-
import { t } from "@/app/translate";
5+
import { useTranslation } from "@/app/translate";
66

77
import {
88
IconBrandX,
@@ -49,6 +49,7 @@ const allSocials = [
4949
];
5050

5151
export function FooterSocial() {
52+
const { t } = useTranslation();
5253
const socials = allSocials.map((social, i) => (
5354
<ActionIcon
5455
key={i}

src/components/header.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@ import classes from "./header.module.css";
66
import Image from "next/image";
77
import Link from "next/link";
88
import { usePathname } from "next/navigation";
9-
import { LanguageSelector, t } from "@/app/translate";
9+
import { LanguageSelector, useTranslation } from "@/app/translate";
1010
import React from "react";
1111

1212
const links = [
@@ -29,6 +29,7 @@ const links = [
2929
];
3030

3131
export function Header() {
32+
const { t } = useTranslation();
3233
const [opened, { toggle, close }] = useDisclosure(false);
3334
const pathname = usePathname();
3435

src/components/logo.tsx

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import React, { useEffect, useRef, useState } from "react";
44
import Image from "next/image";
55
import Script from "next/script";
66
import classes from "../app/index.module.css";
7-
import { t } from "@/app/translate";
7+
import { useTranslation } from "@/app/translate";
88

99
declare global {
1010
interface Window {
@@ -29,6 +29,7 @@ interface LogoProps {
2929
}
3030

3131
export default function InteractiveLogo({ className }: LogoProps) {
32+
const { t } = useTranslation();
3233
const container = useRef<HTMLDivElement>(null);
3334
const [player, setPlayer] = useState<RufflePlayer | null>(null);
3435

@@ -59,7 +60,9 @@ export default function InteractiveLogo({ className }: LogoProps) {
5960
splashScreen: false,
6061
preferredRenderer: "canvas",
6162
})
63+
.then(() => console.log("LOADED"))
6264
.catch(() => {
65+
console.log("Catch error");
6366
removeRufflePlayer();
6467
});
6568
rufflePlayer.style.width = "100%";

0 commit comments

Comments
 (0)