SkateSpots/src/contexts/language.ts

77 lines
2.3 KiB
TypeScript

import { createContext, useContext } from 'react';
import en from '@/i18n/en';
import es from '@/i18n/es';
import th from '@/i18n/th';
import ja from '@/i18n/ja';
/**
* Language context + types + hooks. Split from the `LanguageProvider` component
* so the component file exports only a component (React Fast Refresh), per the
* `react-refresh/only-export-components` rule.
*/
export type Language = 'en' | 'es' | 'th' | 'ja';
type TranslationKey = string;
type TranslationValue = string | { [key: string]: TranslationValue };
export type Translations = {
[key: string]: TranslationValue;
};
export interface LanguageContextType {
/** Current language */
language: Language;
/** Set the current language */
setLanguage: (lang: Language) => void;
/** Translate a key - supports dot notation for nested keys */
t: (key: TranslationKey, params?: Record<string, string | number>) => string;
/** Check if current language is RTL (for future Arabic support, etc.) */
isRTL: boolean;
}
export const LanguageContext = createContext<LanguageContextType | undefined>(undefined);
export const translations: Record<Language, Translations> = { en, es, th, ja };
/** Get a nested value from an object using dot notation (`nav.home`). */
export function getNestedValue(obj: Translations, path: string): string | undefined {
const keys = path.split('.');
let current: TranslationValue = obj;
for (const key of keys) {
if (current && typeof current === 'object' && key in current) {
current = current[key];
} else {
return undefined;
}
}
return typeof current === 'string' ? current : undefined;
}
/** Replace `{{param}}` placeholders in a translation string. */
export function interpolate(str: string, params?: Record<string, string | number>): string {
if (!params) return str;
return str.replace(/\{\{(\w+)\}\}/g, (_, key) => {
return params[key]?.toString() ?? `{{${key}}}`;
});
}
/** Hook to access translation functions and language state. */
export function useTranslation() {
const context = useContext(LanguageContext);
if (context === undefined) {
throw new Error('useTranslation must be used within a LanguageProvider');
}
return context;
}
/** Hook to get just the translation function (convenience). */
export function useT() {
const { t } = useTranslation();
return t;
}