Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 5 additions & 2 deletions src/app/layout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import "./globals.css";
import { ReactQueryDevtools } from "@tanstack/react-query-devtools";
import ReactQueryProvider from "@/providers/react-query";
import clsx from "clsx";
import { DictionaryProvider } from "@/contexts/dictionary-provider";

const jamjuree = Bai_Jamjuree({
subsets: ["latin"],
Expand All @@ -27,8 +28,10 @@ export default function RootLayout({
<html className="h-full" lang="en">
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should change lang="en" according to the current language preference. Check out this example: https://github.com/cesium/hydrogen/blob/develop/src/app/%5Blang%5D/layout.tsx

<body className={clsx(jamjuree.variable, "font-jamjuree h-full")}>
<ReactQueryProvider>
{children}
<ReactQueryDevtools initialIsOpen={false} />
<DictionaryProvider>
{children}
<ReactQueryDevtools initialIsOpen={false} />
</DictionaryProvider>
Comment on lines +33 to +34
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can move this out of the dictionary provider since it will never use it.

Suggested change
<ReactQueryDevtools initialIsOpen={false} />
</DictionaryProvider>
</DictionaryProvider>
<ReactQueryDevtools initialIsOpen={false} />

</ReactQueryProvider>
</body>
</html>
Expand Down
91 changes: 91 additions & 0 deletions src/contexts/dictionary-provider.tsx
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should move this provider into the providers/ folder instead for consistency.

Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
"use client";

import { createContext, useContext, useEffect, useState } from "react";
import type { Dictionary, Language } from "@/internationalization/dictionaries";
import { getDictionary } from "@/internationalization/dictionaries";
import { api } from "@/lib/api";
import { useGetUserInfo } from "@/lib/queries/session";

export type DictionaryLanguage = Language;

interface DictionaryContextData {
dictionary: Dictionary;
language: DictionaryLanguage;
setLanguage?: (language: DictionaryLanguage) => void;
}

const DictionaryContext = createContext<DictionaryContextData | undefined>(
undefined,
);

export async function PreferedLanguage(): Promise<DictionaryLanguage> {
const response = await api.get("/auth/preferences/language");
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could create a useGetUserPreference tanstack query that receives the desired prefrence as an argument, it would go something like this:

export function useGetUserPreference(preference: string) {
  return useQuery({
    queryKey: ["user", "preferences", preference],
    queryFn: () => {} // function that does the fetching and that receives 'preference' as argument
  })
}

This way, we can benefit from all the tanstack cool perks and have a centralized way of getting any preference.

console.log(response);
if (!response) {
throw new Error("Failed to fetch preferred language");
}
const { language } = response.data;

return language as DictionaryLanguage;
}

export function getBrowserLanguage(): DictionaryLanguage {
if (typeof navigator !== "undefined" && navigator.language) {
return navigator.language as DictionaryLanguage;
}
return "en-US";
}

export function DictionaryProvider({
children,
language: propLanguage,
}: {
children: React.ReactNode;
language?: DictionaryLanguage;
}) {
const [language, setLanguage] = useState<DictionaryLanguage>(
propLanguage || "en-US",
);

const user = useGetUserInfo();

useEffect(() => {
if (!propLanguage) {
(async () => {
try {
if (user) {
const preferredLanguage = await PreferedLanguage();
setLanguage(preferredLanguage);
} else {
setLanguage(getBrowserLanguage());
}
} catch {
setLanguage("en-US");
}
})();
}
}, [propLanguage, user]);

const dictionary = getDictionary(language);
return (
<DictionaryContext.Provider value={{ dictionary, language, setLanguage }}>
{children}
</DictionaryContext.Provider>
);
}

export function useDictionary() {
const context = useContext(DictionaryContext);
if (!context) {
throw new Error("useDictionary must be used within a DictionaryProvider");
}
return context.dictionary;
}

export function useLanguage() {
const context = useContext(DictionaryContext);
if (!context) {
throw new Error("useLanguage must be used within a DictionaryProvider");
}
return context.language;
}
17 changes: 17 additions & 0 deletions src/internationalization/dictionaries.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import en from "./dictionaries/en.json";
import pt from "./dictionaries/pt.json";

const dictionaries = {
"en-US": en,
"en-GB": en,
"en-CA": en,
"pt-PT": pt,
"pt-BR": pt,
};

export type Language = keyof typeof dictionaries;
export type Dictionary = (typeof dictionaries)[Language];

export const getDictionary = (lang: Language): Dictionary => {
return dictionaries[lang] || dictionaries["en-US"];
};
3 changes: 3 additions & 0 deletions src/internationalization/dictionaries/en.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"greeting": "Hello"
}
3 changes: 3 additions & 0 deletions src/internationalization/dictionaries/pt.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"greeting": "Olá"
}