Phase 1: Infrastructure setup - Install next-intl and create basic i18n structure
- Install next-intl package - Create LanguageProvider hook with localStorage persistence - Create IntlProvider component for next-intl integration - Create Providers wrapper component - Update layout.tsx to include providers and set default lang to 'es' - Create initial translation files (common.json) for es, en, ca - Fix pre-existing TypeScript errors in various pages All tests passing, build successful.
This commit is contained in:
parent
1a47b3643f
commit
f7553df05d
15 changed files with 940 additions and 22 deletions
54
frontend/app/hooks/useLanguage.tsx
Normal file
54
frontend/app/hooks/useLanguage.tsx
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
"use client";
|
||||
|
||||
import { useState, useEffect, createContext, useContext, ReactNode } from "react";
|
||||
|
||||
export type Locale = "es" | "en" | "ca";
|
||||
|
||||
const LOCALE_STORAGE_KEY = "arbret-locale";
|
||||
const DEFAULT_LOCALE: Locale = "es";
|
||||
|
||||
interface LanguageContextType {
|
||||
locale: Locale;
|
||||
setLocale: (locale: Locale) => void;
|
||||
}
|
||||
|
||||
const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
|
||||
|
||||
export function LanguageProvider({ children }: { children: ReactNode }) {
|
||||
const [locale, setLocaleState] = useState<Locale>(DEFAULT_LOCALE);
|
||||
const [isHydrated, setIsHydrated] = useState(false);
|
||||
|
||||
// Load locale from localStorage on mount
|
||||
useEffect(() => {
|
||||
const stored = localStorage.getItem(LOCALE_STORAGE_KEY);
|
||||
if (stored && (stored === "es" || stored === "en" || stored === "ca")) {
|
||||
setLocaleState(stored as Locale);
|
||||
}
|
||||
setIsHydrated(true);
|
||||
}, []);
|
||||
|
||||
// Update HTML lang attribute when locale changes
|
||||
useEffect(() => {
|
||||
if (isHydrated) {
|
||||
document.documentElement.lang = locale;
|
||||
}
|
||||
}, [locale, isHydrated]);
|
||||
|
||||
const setLocale = (newLocale: Locale) => {
|
||||
setLocaleState(newLocale);
|
||||
localStorage.setItem(LOCALE_STORAGE_KEY, newLocale);
|
||||
document.documentElement.lang = newLocale;
|
||||
};
|
||||
|
||||
return (
|
||||
<LanguageContext.Provider value={{ locale, setLocale }}>{children}</LanguageContext.Provider>
|
||||
);
|
||||
}
|
||||
|
||||
export function useLanguage() {
|
||||
const context = useContext(LanguageContext);
|
||||
if (context === undefined) {
|
||||
throw new Error("useLanguage must be used within a LanguageProvider");
|
||||
}
|
||||
return context;
|
||||
}
|
||||
8
frontend/app/hooks/useTranslation.ts
Normal file
8
frontend/app/hooks/useTranslation.ts
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
"use client";
|
||||
|
||||
import { useTranslations as useNextIntlTranslations } from "next-intl";
|
||||
|
||||
export function useTranslation(namespace?: string) {
|
||||
const t = useNextIntlTranslations(namespace);
|
||||
return t;
|
||||
}
|
||||
Loading…
Add table
Add a link
Reference in a new issue