import type { Locale } from "discord.js" import frLocale from "@/locales/fr.json" import enLocale from "@/locales/en.json" import dbGuild from "@/schemas/guild" import { logConsoleError } from "./console" // Variables d'environnement pour les locales avec valeurs par défaut const DEFAULT_LOCALE = process.env.DEFAULT_LOCALE ?? 'fr' const CONSOLE_LOCALE = process.env.CONSOLE_LOCALE ?? 'en' // Types pour l'internationalisation type LocaleData = Record type ReplacementParams = Record type TranslationKey = string /** * Récupère la locale configurée pour un serveur * @param guildId - L'ID du serveur Discord * @returns La locale configurée ou 'fr' par défaut */ export async function getGuildLocale(guildId: string): Promise { try { const guildProfile = await dbGuild.findOne({ guildId }) return guildProfile?.guildLocale ?? 'fr' } catch (error) { logConsoleError('mongoose', 'locale.fetch_error', { guildId }, error as Error) return 'fr' } } // Conversion des imports en type LocaleData const frLocaleData = frLocale as unknown as LocaleData const enLocaleData = enLocale as unknown as LocaleData // Locales supportées const locales = { 'fr': frLocaleData, 'en-US': enLocaleData, 'en-GB': enLocaleData } as const type SupportedLocale = keyof typeof locales /** * Récupère une traduction basée sur la locale et la clé */ function getNestedValue(obj: LocaleData, path: string): string | undefined { try { const keys = path.split('.') let current: unknown = obj for (const key of keys) { if (current !== null && typeof current === 'object' && key in current) current = (current as Record)[key] else return undefined } return typeof current === 'string' ? current : undefined } catch { return undefined } } /** * Remplace les paramètres dans une chaîne de caractères * Exemple: "Hello {name}" avec {name: "World"} devient "Hello World" */ function replaceParams(text: string, params: ReplacementParams = {}): string { return text.replace(/\{(\w+)\}/g, (match, key: string) => { if (Object.prototype.hasOwnProperty.call(params, key)) return params[key].toString() return match }) } /** * Fonction principale de localisation * @param locale - La locale Discord de l'utilisateur * @param key - La clé de traduction (ex: "player.not_in_voice") * @param params - Les paramètres à remplacer dans la traduction * @returns La chaîne traduite */ export function t(locale: Locale | string, key: TranslationKey, params: ReplacementParams = {}): string { // Détermine la locale à utiliser (par défaut de la config) const supportedLocale: SupportedLocale = (Object.keys(locales).includes(locale)) ? locale as SupportedLocale : DEFAULT_LOCALE as SupportedLocale // Récupère les données de locale const localeData = locales[supportedLocale] // Récupère la traduction const translation = getNestedValue(localeData, key) if (!translation) { console.warn(`[Localization] Translation not found for key: ${key} in locale: ${supportedLocale}`) return key // Retourne la clé si la traduction n'est pas trouvée } // Remplace les paramètres et retourne la traduction return replaceParams(translation, params) } /** * Fonction helper pour obtenir la locale française par défaut */ export function fr(key: TranslationKey, params: ReplacementParams = {}): string { return t('fr', key, params) } /** * Fonction helper pour obtenir la locale anglaise */ export function en(key: TranslationKey, params: ReplacementParams = {}): string { return t('en-US', key, params) } /** * Obtient les locales supportées pour une commande * Utilisé pour les localisations des commandes slash */ export function getCommandLocalizations(baseKey: string) { return { fr: getNestedValue(frLocaleData, baseKey) ?? baseKey, 'en-US': getNestedValue(enLocaleData, baseKey) ?? baseKey, 'en-GB': getNestedValue(enLocaleData, baseKey) ?? baseKey } } /** * Fonction de localisation utilisant la locale du serveur * @param guildLocale - La locale configurée du serveur * @param key - La clé de traduction (ex: "player.not_in_voice") * @param params - Les paramètres à remplacer dans la traduction * @returns La chaîne traduite */ export function tGuild(guildLocale: string, key: TranslationKey, params: ReplacementParams = {}): string { return t(guildLocale, key, params) } /** * Fonction helper pour obtenir la locale appropriée (serveur ou utilisateur) * @param guildId - L'ID du serveur (optionnel) * @param userLocale - La locale de l'utilisateur * @returns La locale à utiliser */ export async function getLocaleForContext(guildId: string | null, userLocale: string): Promise { if (guildId) return await getGuildLocale(guildId) return userLocale } /** * Fonction de traduction intelligente qui utilise automatiquement la locale du serveur * @param interaction - L'interaction Discord * @param key - La clé de traduction * @param params - Les paramètres de remplacement * @returns La chaîne traduite */ export async function tSmart(interaction: { guild: { id: string } | null; locale: string }, key: TranslationKey, params: ReplacementParams = {}): Promise { const locale = await getLocaleForContext(interaction.guild?.id ?? null, interaction.locale) return t(locale, key, params) } // Export des constantes de locale export { DEFAULT_LOCALE, CONSOLE_LOCALE } // Export des types pour utilisation externe export type { TranslationKey, ReplacementParams, SupportedLocale }