detect_locale(); $this->load_translations(); } /** * Detect current locale from WordPress */ private function detect_locale() { $wp_locale = get_locale(); // Map WordPress locale to our locale $locale_map = array( 'vi' => 'vi', 'vi_VN' => 'vi', 'en_US' => 'en', 'en_GB' => 'en', 'de_DE' => 'de', 'es_ES' => 'es', 'fr_FR' => 'fr', 'it_IT' => 'it', 'ja' => 'ja', 'nl_NL' => 'nl', 'pt_BR' => 'pt' ); if (isset($locale_map[$wp_locale])) { $this->locale = $locale_map[$wp_locale]; } else { // Try to extract language code from locale $parts = explode('_', $wp_locale); if (in_array($parts[0], self::$supported_languages)) { $this->locale = $parts[0]; } } } /** * Load translations from JSON files */ private function load_translations() { $lang_dir = AIFLAB_CHATWEB_PLUGIN_PATH . 'languages/'; // Load fallback (English) first $fallback_file = $lang_dir . 'en.json'; if (file_exists($fallback_file)) { $content = file_get_contents($fallback_file); $this->fallback = json_decode($content, true) ?: array(); } // Load current locale if ($this->locale !== 'en') { $locale_file = $lang_dir . $this->locale . '.json'; if (file_exists($locale_file)) { $content = file_get_contents($locale_file); $this->translations = json_decode($content, true) ?: array(); } } else { $this->translations = $this->fallback; } } /** * Get translation by key * * @param string $key Dot notation key (e.g., "common.loading") * @param array $args Optional sprintf arguments * @return string Translated string */ public function get($key, $args = array()) { $value = $this->get_nested_value($this->translations, $key); // Fallback to English if not found if ($value === null) { $value = $this->get_nested_value($this->fallback, $key); } // Return key if still not found if ($value === null) { return $key; } // Apply sprintf if args provided if (!empty($args)) { $value = vsprintf($value, $args); } return $value; } /** * Get nested value from array using dot notation * * @param array $array Source array * @param string $key Dot notation key * @return mixed|null Value or null if not found */ private function get_nested_value($array, $key) { $keys = explode('.', $key); $value = $array; foreach ($keys as $k) { if (!isset($value[$k])) { return null; } $value = $value[$k]; } return $value; } /** * Get all translations for a section (for JS) * * @param string $section Section name (e.g., "common", "api_key") * @return array Section translations */ public function get_section($section) { $result = array(); if (isset($this->translations[$section])) { $result = $this->translations[$section]; } elseif (isset($this->fallback[$section])) { $result = $this->fallback[$section]; } return $result; } /** * Get all translations for JavaScript * * @return array Flattened translations for JS */ public function get_js_translations() { $js_trans = array(); $source = !empty($this->translations) ? $this->translations : $this->fallback; foreach ($source as $section => $values) { if ($section === '_meta') continue; if (is_array($values)) { foreach ($values as $key => $value) { $js_trans[$section . '.' . $key] = $value; } } } return $js_trans; } /** * Get current locale * * @return string Current locale code */ public function get_locale() { return $this->locale; } /** * Static shorthand for get() * * @param string $key Dot notation key * @param array $args Optional sprintf arguments * @return string Translated string */ public static function t($key, $args = array()) { return self::get_instance()->get($key, $args); } /** * Static shorthand for escaped output * * @param string $key Dot notation key * @param array $args Optional sprintf arguments * @return string Escaped translated string */ public static function e($key, $args = array()) { return esc_html(self::t($key, $args)); } /** * Static shorthand for attribute escaped output * * @param string $key Dot notation key * @param array $args Optional sprintf arguments * @return string Attribute escaped translated string */ public static function a($key, $args = array()) { return esc_attr(self::t($key, $args)); } }