base -> language code
$fallback_locales = array($locale);
// If locale has underscores, extract base locale (e.g., nl_NL_formal -> nl_NL)
if (strpos($locale, '_') !== false) {
$parts = explode('_', $locale);
if (count($parts) >= 2) {
$base_locale = $parts[0] . '_' . $parts[1];
if (!in_array($base_locale, $fallback_locales, true)) {
$fallback_locales[] = $base_locale;
}
}
// Also try language code only (e.g., nl_NL -> nl)
if (count($parts) >= 1) {
$lang_only = $parts[0];
if (!in_array($lang_only, $fallback_locales, true)) {
$fallback_locales[] = $lang_only;
}
}
}
// Try each locale in fallback chain
foreach ($fallback_locales as $try_locale) {
$mofile = $lang_dir . 'ai-translate-' . $try_locale . '.mo';
if (file_exists($mofile)) {
// Unload existing textdomain first if reloading
if ($force_user_locale && is_textdomain_loaded('ai-translate')) {
unload_textdomain('ai-translate');
}
load_textdomain('ai-translate', $mofile);
if (is_textdomain_loaded('ai-translate')) {
return;
}
}
}
// Final fallback to standard load_plugin_textdomain. Plugin ships its own .mo files in
// /languages/, so we cannot rely solely on WordPress 4.6+ automatic translation loading
// (which only picks up /wp-content/languages/plugins/ai-translate-*.mo files).
// phpcs:ignore PluginCheck.CodeAnalysis.DiscouragedFunctions.load_plugin_textdomainFound -- needed for bundled translations
load_plugin_textdomain(
'ai-translate',
false,
dirname(plugin_basename(__FILE__)) . '/languages/'
);
}
// Load textdomain for frontend with site locale
add_action('plugins_loaded', function() {
ai_translate_load_textdomain(); // Load with site locale for frontend
}, 1);
// For admin pages, load textdomain with user locale (this overrides the frontend load)
add_action('admin_init', function() {
if (is_user_logged_in()) {
$user_locale = get_user_locale();
$site_locale = get_locale();
// Always load user locale for admin, even if same as site locale
ai_translate_load_textdomain(true); // Force load with user locale
}
}, 1); // High priority to override any other loads
// Include core/admin and runtime classes.
define('AI_TRANSLATE_PLUGIN_FILE', __FILE__);
require_once __DIR__ . '/includes/class-ai-translate-core.php';
require_once __DIR__ . '/includes/helpers.php';
require_once __DIR__ . '/includes/request-path.php';
require_once __DIR__ . '/includes/routing-query.php';
require_once __DIR__ . '/includes/reasoning-helpers.php';
require_once __DIR__ . '/includes/api-keys.php';
require_once __DIR__ . '/includes/language-redirect.php';
require_once __DIR__ . '/includes/admin-page.php';
require_once __DIR__ . '/includes/class-ai-lang.php';
require_once __DIR__ . '/includes/class-ai-cache.php';
require_once __DIR__ . '/includes/class-ai-cache-meta.php';
require_once __DIR__ . '/includes/class-ai-dom.php';
require_once __DIR__ . '/includes/class-ai-segment-store.php';
require_once __DIR__ . '/includes/class-ai-batch.php';
require_once __DIR__ . '/includes/class-ai-seo.php';
require_once __DIR__ . '/includes/class-ai-url.php';
require_once __DIR__ . '/includes/class-ai-ob.php';
require_once __DIR__ . '/includes/class-ai-slugs.php';
require_once __DIR__ . '/includes/class-ai-prewarm.php';
require_once __DIR__ . '/includes/class-ai-rest-strings.php';
require_once __DIR__ . '/includes/class-ai-settings-sanitizer.php';
\AITranslate\AI_Prewarm::init();
require_once __DIR__ . '/includes/class-ai-404-recovery.php';
require_once __DIR__ . '/includes/class-ai-sitemap.php';
// Initialize sitemap integration
\AITranslate\AI_Sitemap::init();
require_once __DIR__ . '/includes/attr-transients.php';
require_once __DIR__ . '/includes/rewrite-rules.php';
/**
* Add a 5-star rating link to the plugin row on the Plugins screen.
*
* This renders a golden 5-star link that points to the plugin's WordPress.org page,
* similar to popular plugins. Only affects the AI Translate row.
*
* @param string[] $links Existing meta links for the row.
* @param string $file Plugin basename for the current row.
* @return string[]
*/
add_filter('plugin_row_meta', function (array $links, $file) {
if ($file !== plugin_basename(__FILE__)) {
return $links;
}
$url = 'https://wordpress.org/plugins/ai-translate/';
$label = __('Rate on WordPress.org', 'ai-translate');
$stars = '★★★★★';
$links[] = sprintf(
'%3$s',
esc_url($url),
esc_attr($label),
esc_html($stars)
);
return $links;
}, 10, 2);
/**
* Plugin activation: flush rewrite rules and create database tables.
*/
register_activation_hook(__FILE__, function () {
// Ensure rules are registered before flushing
ai_translate_register_rewrite_rules();
// Automatically set permalinks to 'post-name' if they're currently 'plain'
// since the plugin requires rewrite rules to function
$permalink_structure = get_option('permalink_structure');
if ($permalink_structure === '' || $permalink_structure === null) {
// Set to 'post-name' structure which is /%postname%/
update_option('permalink_structure', '/%postname%/');
}
flush_rewrite_rules();
// Create cache metadata table
\AITranslate\AI_Cache_Meta::create_table();
// Create the segment store and schedule its daily cleanup
\AITranslate\AI_Segment_Store::create_table();
\AITranslate\AI_Segment_Store::schedule_purge();
});
register_deactivation_hook(__FILE__, function () {
\AITranslate\AI_Segment_Store::unschedule_purge();
});
// Daily removal of segments older than the cache duration; also schedules itself for existing installs.
add_action(\AITranslate\AI_Segment_Store::purge_hook(), ['\\AITranslate\\AI_Segment_Store', 'run_purge']);
add_action('init', ['\\AITranslate\\AI_Segment_Store', 'schedule_purge']);
// Background measurement of thinking models (queued after a successful API validation).
add_action(\AITranslate\AI_Translate_Core::PROBE_HOOK, ['\\AITranslate\\AI_Translate_Core', 'run_scheduled_probe'], 10, 2);
require_once __DIR__ . '/includes/request-filters.php';
require_once __DIR__ . '/includes/frontend-output.php';
require_once __DIR__ . '/includes/catalog-strings.php';
/**
* REST endpoint for dynamic UI attribute translation.
*/
add_action('rest_api_init', ['\\AITranslate\\AI_REST_Strings', 'register']);
require_once __DIR__ . '/includes/request-routing.php';
require_once __DIR__ . '/includes/permalinks.php';
require_once __DIR__ . '/includes/cache-invalidation.php';
/**
* One-time warmup: generate translated slugs for existing top-level pages so
* language-prefixed URLs immediately resolve (prevents blank pages before first visit).
* Mirrors robustness in the original implementation by pre-seeding the slug map.
*/
// Runs in WP-Cron, a batch at a time (see AI_Slugs::run_warmup); init only queues it.
add_action(\AITranslate\AI_Slugs::WARMUP_HOOK, ['\\AITranslate\\AI_Slugs', 'run_warmup'], 10, 1);
add_action('init', ['\\AITranslate\\AI_Slugs', 'schedule_warmup'], 99);
require_once __DIR__ . '/includes/fallback-routing.php';
/**
* Add Settings quick link on the Plugins page.
*/
add_filter('plugin_action_links_' . plugin_basename(__FILE__), function ($links) {
$url = admin_url('admin.php?page=ai-translate');
$settings_link = '' . esc_html__('Instellingen', 'ai-translate') . '';
array_unshift($links, $settings_link);
return $links;
});
require_once __DIR__ . '/includes/query-filters.php';
require_once __DIR__ . '/includes/language-switcher.php';
require_once __DIR__ . '/includes/nav-menus.php';