i18n_I18nProvider.jsx

import React, { useState } from "react";
import { I18nContext } from "./I18nContext";
import en from "../locales/en.json";
import es from "../locales/es.json";

/**
 * Object containing all available translations.
 * @constant {Object} translations
 * @property {Object} en - English translations.
 * @property {Object} es - Spanish translations.
 */
const translations = { en, es };

/**
 * I18nProvider Component
 *
 * Provides internationalization support for the application by managing locale
 * selection and translations. Uses React Context API to provide access to the
 * current locale and translation function (`t`).
 *
 * @param {Object} props - Component props.
 * @param {React.ReactNode} props.children - The child components to be wrapped within the provider.
 * @param {string} [props.defaultLocale="en"] - The default language locale (must exist in `translations`).
 * @returns {JSX.Element} I18nContext Provider with locale and translation functions.
 *
 * @example
 * <I18nProvider defaultLocale="es">
 *   <App />
 * </I18nProvider>
 */
export const I18nProvider = ({ children, defaultLocale = "en" }) => {
  const [locale, setLocale] = useState(defaultLocale);
  const [messages, setMessages] = useState(translations[defaultLocale]);

  /**
   * Change the current locale and update translations.
   *
   * @param {string} newLocale - The new locale to switch to. Must exist in `translations`.
   * @example
   * const { changeLocale } = useI18n();
   * changeLocale("es");
   */
  const changeLocale = (newLocale) => {
    if (translations[newLocale]) {
      setLocale(newLocale);
      setMessages(translations[newLocale]);
    } else {
      console.warn(`Locale "${newLocale}" not found. Falling back to default.`);
    }
  };

  /**
   * Retrieve a translated string by key, supporting interpolation.
   *
   * @param {string} key - The translation key.
   * @param {...string} args - Optional arguments for interpolation.
   * @returns {string} Translated and interpolated string.
   *
   * @example
   * // en.json -> { "greeting": "Hello, {0}!" }
   * t("greeting", "John"); // Returns: "Hello, John!"
   */
  const t = (key, ...args) => {
    let translation = messages[key] || key;
    args.forEach((value, index) => {
      translation = translation.replace(
        new RegExp(`\\{${index}\\}`, "g"),
        value
      );
    });
    return translation;
  };

  return (
    <I18nContext.Provider value={{ locale, t, changeLocale }}>
      {children}
    </I18nContext.Provider>
  );
};