Skip to content

Commit

Permalink
i18n: Switch to useTranslation so I can translate in loops
Browse files Browse the repository at this point in the history
  • Loading branch information
danielhjacobs committed Nov 15, 2024
1 parent ef986a1 commit 41bd54e
Show file tree
Hide file tree
Showing 5 changed files with 45 additions and 63 deletions.
3 changes: 2 additions & 1 deletion src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ import Image from "next/image";
import { IconCheck } from "@tabler/icons-react";
import React from "react";
import { getLatestReleases } from "@/app/downloads/github";
import { t } from "@/app/translate";
import { useTranslation } from "@/app/translate";
import { GithubRelease } from "./downloads/config";

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

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

React.useEffect(() => {
Expand Down
94 changes: 35 additions & 59 deletions src/app/translate.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
"use client";

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

const languages = {
Expand Down Expand Up @@ -58,82 +58,58 @@ const getNestedTranslation = (
break;
}
}
if (typeof acc === "string") {
return acc;
}
return undefined;
return typeof acc === "string" ? acc : undefined;
};

async function translate(translationKey: string) {
const language = await getAvailableLanguage();

// Helper function to load translations
const loadTranslations = async (lang: string) => {
try {
return await import(`@/i18n/translations.${lang}.json`);
} catch {
console.warn(`Translation file for language "${lang}" not found.`);
return null;
}
};

// Load translations for the selected language and the default language
const translations = await loadTranslations(language);

// Attempt to get the translation in the selected language, then fall back to default
const translation =
getNestedTranslation(translations, translationKey) ||
getNestedTranslation(defaultTranslations, translationKey);

// Render the translation if found; otherwise, return the key
return translation || translationKey;
async function fetchTranslations(lang: string) {
try {
const translations = await import(`@/i18n/translations.${lang}.json`);
return translations;
} catch {
console.warn(`Translation file for language "${lang}" not found.`);
return null;
}
}

export const t = (translationKey: string): string => {
const defaultTranslation =
getNestedTranslation(defaultTranslations, translationKey) || translationKey;
const [translation, setTranslation] = useState(defaultTranslation);

const updateTranslation = async () => {
const translatedText = await translate(translationKey);
setTranslation(translatedText);
};
export function useTranslation() {
const [translations, setTranslations] =
useState<TranslationObject>(defaultTranslations);

useEffect(() => {
// Initial translation update
updateTranslation();

// Update translation when the language in localStorage changes from other browsing context
const handleStorageChange = (event: StorageEvent) => {
if (event.key === "next-export-i18n-lang") {
updateTranslation();
}
const fetchLanguageAndTranslations = async () => {
const lang = await getAvailableLanguage();
const loadedTranslations = await fetchTranslations(lang);
setTranslations(loadedTranslations || defaultTranslations);
};

// Update translation when the language in localStorage changes from current browsing context
const handleLocalStorageLangChange = () => {
updateTranslation();
};
fetchLanguageAndTranslations();

const handleLocalStorageLangChange = () => fetchLanguageAndTranslations();

// Listen for localStorage changes
window.addEventListener("storage", handleStorageChange);
window.addEventListener(
"localStorageLangChange",
handleLocalStorageLangChange,
);

// Clean up listener on component unmount
return () => {
window.removeEventListener("storage", handleStorageChange);
return () =>
window.removeEventListener(
"localStorageLangChange",
handleLocalStorageLangChange,
);
};
}, [translationKey]);
}, []);

return translation;
};
const t = useCallback(
(translationKey: string): string => {
return (
getNestedTranslation(translations, translationKey) ||
getNestedTranslation(defaultTranslations, translationKey) ||
translationKey
);
},
[translations],
);

return { t };
}

export const LanguageSelector: React.FC<LanguageSelectorProps> = ({
className,
Expand Down
3 changes: 2 additions & 1 deletion src/components/footer.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import { Container, Group, ActionIcon, rem, Text } from "@mantine/core";
import Link from "next/link";
import { t } from "@/app/translate";
import { useTranslation } from "@/app/translate";

import {
IconBrandX,
Expand Down Expand Up @@ -49,6 +49,7 @@ const allSocials = [
];

export function FooterSocial() {
const { t } = useTranslation();
const socials = allSocials.map((social, i) => (
<ActionIcon
key={i}
Expand Down
3 changes: 2 additions & 1 deletion src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import classes from "./header.module.css";
import Image from "next/image";
import Link from "next/link";
import { usePathname } from "next/navigation";
import { LanguageSelector, t } from "@/app/translate";
import { LanguageSelector, useTranslation } from "@/app/translate";
import React from "react";

const links = [
Expand All @@ -29,6 +29,7 @@ const links = [
];

export function Header() {
const { t } = useTranslation();
const [opened, { toggle, close }] = useDisclosure(false);
const pathname = usePathname();

Expand Down
5 changes: 4 additions & 1 deletion src/components/logo.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import React, { useEffect, useRef, useState } from "react";
import Image from "next/image";
import Script from "next/script";
import classes from "../app/index.module.css";
import { t } from "@/app/translate";
import { useTranslation } from "@/app/translate";

declare global {
interface Window {
Expand All @@ -29,6 +29,7 @@ interface LogoProps {
}

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

Expand Down Expand Up @@ -59,7 +60,9 @@ export default function InteractiveLogo({ className }: LogoProps) {
splashScreen: false,
preferredRenderer: "canvas",
})
.then(() => console.log("LOADED"))
.catch(() => {
console.log("Catch error");
removeRufflePlayer();
});
rufflePlayer.style.width = "100%";
Expand Down

0 comments on commit 41bd54e

Please sign in to comment.