> */ private static array $cache = []; /** * Return the native generation languages, keyed by lowercase 2-letter code. * * @param bool $force If true, bypass transient + static cache. * @return array */ public static function get_languages(bool $force = false): array { if (!$force && !empty(self::$cache)) { return self::$cache; } if (!$force) { $cached = get_transient(self::TRANSIENT); if (is_array($cached) && !empty($cached)) { self::$cache = $cached; return $cached; } } $languages = []; $url = add_query_arg(['model' => self::MODEL], AIGUDE_Tools_Plugin::get_native_languages_url()); // No apikey header: this endpoint is public, so the list works before a server is configured. $response = wp_remote_get($url, ['timeout' => 5]); if (!is_wp_error($response) && (int) wp_remote_retrieve_response_code($response) === 200) { $json = json_decode(wp_remote_retrieve_body($response), true); foreach ((array) ($json['languages'] ?? []) as $entry) { if (!is_array($entry) || empty($entry['code'])) { continue; } $code = strtolower(trim((string) $entry['code'])); if ($code === '') { continue; } $languages[$code] = [ 'code' => $code, 'name' => (string) ($entry['name'] ?? strtoupper($code)), 'description' => (string) ($entry['description'] ?? ''), 'locale' => (string) ($entry['locale'] ?? ''), 'default_prompt' => (string) ($entry['default_prompt'] ?? ''), ]; } } if (empty($languages)) { foreach (self::FALLBACK_CODES as $code) { $languages[$code] = [ 'code' => $code, 'name' => strtoupper($code), 'description' => '', 'locale' => '', 'default_prompt' => '', ]; } self::log_language_source(false, count($languages)); } else { set_transient(self::TRANSIENT, $languages, self::TTL); self::log_language_source(true, count($languages)); } self::$cache = $languages; return $languages; } /** * Whether AiGude generates natively in the given language. */ public static function is_supported(string $code): bool { $base = self::base_code($code); if ($base === '') { return false; } return isset(self::get_languages()[$base]); } /** * Reduce any locale or provider language code to a lowercase 2-letter base code. * * Handles WordPress locales including formal variants (de_DE_formal → de, * nl_NL_formal → nl) and provider codes (EN-US → en). Returns an empty string * for 'auto', empty input, or anything that does not yield two letters. */ public static function base_code(string $code): string { $trimmed = strtolower(trim($code)); if ($trimmed === '' || $trimmed === 'auto') { return ''; } $base = preg_replace('/[^a-z]/', '', substr($trimmed, 0, 2)); return strlen((string) $base) === 2 ? (string) $base : ''; } /** * The site locale reduced to a native base code. */ public static function site_base_code(): string { return self::base_code(get_locale()); } /** * Language a prompt defaults to: the site language when AiGude generates natively in it, * otherwise English. Every prompt must declare a language, so there is no "unset" state. */ public static function default_prompt_lang(): string { $base = self::site_base_code(); return self::is_supported($base) ? $base : 'en'; } /** * Emit a debug log describing where the language list originated. */ private static function log_language_source(bool $fromApi, int $count): void { AIGUDE_Tools_Plugin::log( sprintf( 'Native languages loaded from %s (%d entries).', $fromApi ? 'API' : 'fallback list', $count ) ); } }