|
| 1 | +import React, { createContext, useCallback, useMemo } from "react"; |
| 2 | +import type { ReactNode } from "react"; |
| 3 | +import { useSearchParams } from "react-router-dom"; |
| 4 | +import { LOCALES, type Locale } from "../../../domain/criteria.ts"; |
| 5 | + |
| 6 | +type LocaleContextType = { |
| 7 | + locale: Locale; |
| 8 | + setLocale: (locale: Locale) => void; |
| 9 | + toggleLocale: () => void; |
| 10 | +}; |
| 11 | + |
| 12 | +const LocaleContext = createContext<LocaleContextType | undefined>(undefined); |
| 13 | + |
| 14 | +const isLocale = (value: string): value is Locale => LOCALES.some((l) => l === value); |
| 15 | + |
| 16 | +const resolveLocale = (raw: string | null): Locale => { |
| 17 | + if (!raw) return "en"; |
| 18 | + return isLocale(raw) ? raw : "en"; |
| 19 | +}; |
| 20 | + |
| 21 | +export const LocaleProvider: React.FC<{ children: ReactNode }> = ({ children }) => { |
| 22 | + const [searchParams, setSearchParams] = useSearchParams(); |
| 23 | + const locale = useMemo(() => resolveLocale(searchParams.get("lang")), [searchParams]); |
| 24 | + |
| 25 | + const setLocale = useCallback( |
| 26 | + (next: Locale) => { |
| 27 | + setSearchParams( |
| 28 | + (prev) => { |
| 29 | + const updated = new URLSearchParams(prev); |
| 30 | + updated.set("lang", next); |
| 31 | + return updated; |
| 32 | + }, |
| 33 | + { replace: false }, |
| 34 | + ); |
| 35 | + }, |
| 36 | + [setSearchParams], |
| 37 | + ); |
| 38 | + |
| 39 | + const toggleLocale = useCallback(() => { |
| 40 | + setLocale(locale === "ja" ? "en" : "ja"); |
| 41 | + }, [locale, setLocale]); |
| 42 | + |
| 43 | + const value = useMemo( |
| 44 | + () => ({ locale, setLocale, toggleLocale }), |
| 45 | + [locale, setLocale, toggleLocale], |
| 46 | + ); |
| 47 | + |
| 48 | + return <LocaleContext.Provider value={value}>{children}</LocaleContext.Provider>; |
| 49 | +}; |
| 50 | + |
| 51 | +export default LocaleContext; |
0 commit comments