# ai-builder/2.2.3/includes/class-translation-manager.php

AI Builder – Generate pages, blocks, images &amp; translate with AI, version 2.2.3. 591 lines.

- Page: https://pluginprobe.com/plugins/ai-builder/2.2.3/code/includes/class-translation-manager.php
- Raw: https://pluginprobe.com/plugins/ai-builder/2.2.3/raw/includes/class-translation-manager.php
- Modified: 2025-11-30T10:16:52+00:00

Line numbers below start at 1. Link to a line or a range by appending a fragment to the
page URL, for example `https://pluginprobe.com/plugins/ai-builder/2.2.3/code/includes/class-translation-manager.php#L10-L20`.

```php
<?php

if (!defined('ABSPATH')) {
    exit;
}

class AIBUI_Translation_Manager
{
    private $settings;
    private $current_lang;
    private $has_explicit_lang_preference = false;

    public function __construct()
    {
        $this->settings = AIBUI_Translation_Settings::get_settings();
        $this->current_lang = $this->determine_current_language();

        if (is_admin()) {
            add_filter('manage_edit-post_columns', array($this, 'add_language_column'));
            add_filter('manage_edit-page_columns', array($this, 'add_language_column'));
            add_filter('manage_edit-wp_template_part_columns', array($this, 'add_language_column'));

            add_action('manage_post_posts_custom_column', array($this, 'render_language_column'), 10, 2);
            add_action('manage_page_posts_custom_column', array($this, 'render_language_column'), 10, 2);
            add_action('manage_wp_template_part_posts_custom_column', array($this, 'render_language_column'), 10, 2);

            add_action('restrict_manage_posts', array($this, 'add_language_filter'));
            add_action('pre_get_posts', array($this, 'filter_admin_by_language'));
            add_action('admin_head', array($this, 'print_admin_styles'));

            // Ensure "View" links in admin (list + editor) include the correct ai_lang
            add_filter('post_type_link', array($this, 'add_lang_query_to_admin_view_link'), 10, 2);
            add_filter('post_link', array($this, 'add_lang_query_to_admin_view_link'), 10, 3);
            add_filter('page_link', array($this, 'add_lang_query_to_admin_view_link'), 10, 3);
        } else {
            add_filter('query_vars', array($this, 'register_query_var'));
            // Handle homepage redirect early, before WP decides what to show
            add_action('template_redirect', array($this, 'maybe_redirect_homepage'), 1);
            add_action('template_redirect', array($this, 'maybe_redirect_to_translation'), 10);
            add_action('pre_get_posts', array($this, 'filter_front_queries'));
            add_action('wp_head', array($this, 'output_hreflang_tags'), 5);
            add_filter('language_attributes', array($this, 'filter_language_attributes'), 10, 2);
            add_filter('pre_render_block', array($this, 'maybe_swap_template_part'), 10, 2);
        }
    }

    public function get_current_language()
    {
        return $this->current_lang;
    }

    public function filter_language_attributes($output, $doctype)
    {
        if ($doctype !== 'html') {
            return $output;
        }

        $lang = $this->current_lang;
        $dir = in_array($lang, array('ar', 'he', 'fa'), true) ? 'rtl' : 'ltr';

        return sprintf('lang="%s" dir="%s"', esc_attr($lang), esc_attr($dir));
    }

    public function get_default_language()
    {
        return $this->settings['default_lang'];
    }

    public function is_switcher_enabled()
    {
        return !empty($this->settings['enable_switcher']);
    }

    public function get_available_languages()
    {
        return $this->settings['available_langs'];
    }

    public function get_settings()
    {
        return $this->settings;
    }

    /**
     * In the admin, make sure "View" links include the post language as ?ai_lang=...
     * so that designers land on the matching front-end version and are not redirected.
     */
    public function add_lang_query_to_admin_view_link($permalink, $post, $leavename = null)
    {
        if (!is_admin() || empty($post)) {
            return $permalink;
        }

        // $post may be an object or an ID depending on the filter (post_link/page_link/post_type_link)
        if (!is_object($post)) {
            $post = get_post($post);
        }
        if (!$post) {
            return $permalink;
        }

        $default_lang = $this->settings['default_lang'];
        $lang = get_post_meta($post->ID, '_ai_lang', true) ?: $default_lang;

        // Avoid duplicating the parameter if it's already there.
        $permalink = remove_query_arg('ai_lang', $permalink);
        return add_query_arg('ai_lang', $lang, $permalink);
    }

    private function determine_current_language()
    {
        $available = $this->settings['available_langs'];
        $default = $this->settings['default_lang'];
        $lang = $default;

        if (isset($_GET['ai_lang'])) {
            $candidate = sanitize_text_field(wp_unslash($_GET['ai_lang']));
            if (in_array($candidate, $available, true)) {
                $lang = $candidate;
                $this->has_explicit_lang_preference = true;
                setcookie('aibui_lang', $lang, time() + MONTH_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN);
            }
        } elseif (isset($_COOKIE['aibui_lang'])) {
            $candidate = sanitize_text_field(wp_unslash($_COOKIE['aibui_lang']));
            if (in_array($candidate, $available, true)) {
                $lang = $candidate;
                $this->has_explicit_lang_preference = true;
            }
        }

        return $lang;
    }

    /** Admin columns & filters */
    public function add_language_column($columns)
    {
        $columns['aibui_translations'] = __('Translations', 'ai-builder');
        return $columns;
    }

    public function render_language_column($column, $post_id)
    {
        if ($column !== 'aibui_translations') {
            return;
        }

        $default = $this->settings['default_lang'];
        $current_lang = get_post_meta($post_id, '_ai_lang', true) ?: $default;
        $current_label = AIBUI_Translation_Handler::SUPPORTED_LANGUAGES[$current_lang] ?? strtoupper($current_lang);

        $items = array(
            sprintf('<span class="aibui-current-lang">%s</span>', esc_html($current_label))
        );

        $group = get_post_meta($post_id, '_ai_translation_group', true);
        if ($group) {
            $translations = $this->get_group_posts($group, get_post_type($post_id));
            foreach ($translations as $translation) {
                if ((int) $translation->ID === (int) $post_id) {
                    continue;
                }
                $lang = get_post_meta($translation->ID, '_ai_lang', true) ?: $default;
                $label = AIBUI_Translation_Handler::SUPPORTED_LANGUAGES[$lang] ?? strtoupper($lang);
                $items[] = sprintf(
                    '<a class="aibui-lang-link" href="%s">%s</a>',
                    esc_url(get_edit_post_link($translation->ID)),
                    esc_html($label)
                );
            }
        }

        echo implode(' | ', $items);
    }

    public function print_admin_styles()
    {
        $screen = get_current_screen();
        if (!$screen || !in_array($screen->id, array('edit-page', 'edit-post', 'edit-wp_template_part'), true)) {
            return;
        }

        ?>
        <style>
            .column-aibui_translations {
                width: 10%;
            }
            .aibui-current-lang {
                font-weight: 600;
                color: #007cba;
            }
            .aibui-lang-link {
                text-decoration: none;
            }
        </style>
        <?php
    }

    public function add_language_filter($post_type)
    {
        if (!in_array($post_type, array('post', 'page', 'wp_template_part'), true)) {
            return;
        }

        $selected = isset($_GET['aibui_lang']) ? sanitize_text_field(wp_unslash($_GET['aibui_lang'])) : '';
        ?>
        <select name="aibui_lang" id="aibui_lang">
            <option value=""><?php esc_html_e('All languages', 'ai-builder'); ?></option>
            <?php foreach (AIBUI_Translation_Handler::SUPPORTED_LANGUAGES as $code => $label) : ?>
                <option value="<?php echo esc_attr($code); ?>" <?php selected($selected, $code); ?>>
                    <?php echo esc_html($label); ?>
                </option>
            <?php endforeach; ?>
        </select>
        <?php
    }

    public function filter_admin_by_language($query)
    {
        if (!is_admin() || !$query->is_main_query()) {
            return;
        }

        global $pagenow;
        if (!in_array($pagenow, array('edit.php'), true)) {
            return;
        }

        $lang = isset($_GET['aibui_lang']) ? sanitize_text_field(wp_unslash($_GET['aibui_lang'])) : '';
        if ($lang === '') {
            return;
        }

        $meta_query = (array) $query->get('meta_query');
        $meta_query[] = array(
            'key' => '_ai_lang',
            'value' => $lang,
            'compare' => '='
        );
        $query->set('meta_query', $meta_query);
    }

    /** Front language handling */
    public function register_query_var($vars)
    {
        $vars[] = 'ai_lang';
        return $vars;
    }

    /**
     * Handle homepage translation redirect
     * This runs early to catch the homepage case before WP shows the wrong content
     */
    public function maybe_redirect_homepage()
    {
        // Only process if we have an explicit language preference
        if (!$this->has_explicit_lang_preference) {
            return;
        }

        // Check if we're on the site root (homepage)
        $request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
        $parsed = parse_url($request_uri);
        $path = isset($parsed['path']) ? trim($parsed['path'], '/') : '';

        // If path is empty or just the WordPress subdirectory, we're on homepage
        $home_path = trim(parse_url(home_url(), PHP_URL_PATH) ?: '', '/');
        $is_homepage = ($path === $home_path || $path === '');

        if (!$is_homepage) {
            return;
        }

        $default_lang = $this->settings['default_lang'];
        $desired_lang = $this->current_lang;

        // Get the front page ID from WordPress settings
        $show_on_front = get_option('show_on_front');
        $front_page_id = get_option('page_on_front');

        // If WordPress is set to show a static page as homepage
        if ($show_on_front === 'page' && $front_page_id) {
            $front_page_lang = get_post_meta($front_page_id, '_ai_lang', true) ?: $default_lang;

            // If desired language matches the front page language, no redirect needed
            // Just ensure we stay on the homepage (the pre_get_posts hook will handle forcing the page)
            if ($desired_lang === $front_page_lang) {
                return;
            }

            // Find the translation of the front page
            $group = get_post_meta($front_page_id, '_ai_translation_group', true);
            if ($group) {
                $translated = $this->get_translation_in_lang($group, 'page', $desired_lang);
                if ($translated) {
                    // Redirect to the translated page
                    wp_safe_redirect(get_permalink($translated->ID), 302);
                    exit;
                }
            }
        }

        // If show_on_front is 'posts' (blog mode), or no static page is set,
        // look for a page marked as homepage translation for the desired language
        // This handles the case where the site uses blog mode but has translated "landing" pages
        // But skip this for default language (let WordPress show the blog/homepage normally)
        if ($desired_lang !== $default_lang) {
            $homepage_translation = $this->find_homepage_for_language($desired_lang);
            if ($homepage_translation) {
                wp_safe_redirect(get_permalink($homepage_translation->ID), 302);
                exit;
            }
        }
    }

    /**
     * Find a page that serves as homepage for a specific language
     * Looks for pages with _ai_lang matching and _ai_is_homepage = 1
     * Or pages that are translations of the default homepage
     */
    private function find_homepage_for_language($lang)
    {
        $default_lang = $this->settings['default_lang'];

        // If looking for default language, don't redirect (stay on blog/homepage)
        if ($lang === $default_lang) {
            return null;
        }

        // First, try to find a page explicitly marked as homepage for this language
        $query = new WP_Query(array(
            'post_type' => 'page',
            'post_status' => 'publish',
            'posts_per_page' => 1,
            'meta_query' => array(
                'relation' => 'AND',
                array(
                    'key' => '_ai_lang',
                    'value' => $lang,
                    'compare' => '='
                ),
                array(
                    'key' => '_ai_is_homepage',
                    'value' => '1',
                    'compare' => '='
                )
            )
        ));

        if ($query->have_posts()) {
            return $query->posts[0];
        }

        // Fallback: find a page that is a translation of the page_on_front (if any)
        $front_page_id = get_option('page_on_front');
        if ($front_page_id) {
            $group = get_post_meta($front_page_id, '_ai_translation_group', true);
            if ($group) {
                return $this->get_translation_in_lang($group, 'page', $lang);
            }
        }

        return null;
    }

    public function maybe_redirect_to_translation()
    {
        if (!is_singular()) {
            return;
        }

        global $post;
        if (!$post) {
            return;
        }

        $default_lang = $this->settings['default_lang'];
        $post_lang = get_post_meta($post->ID, '_ai_lang', true) ?: $default_lang;

        // If the user has no explicit language preference (no GET, no cookie),
        // and lands directly on a page, we treat that page's language as their
        // preference and simply store it in the cookie without redirecting.
        if (!$this->has_explicit_lang_preference) {
            setcookie('aibui_lang', $post_lang, time() + MONTH_IN_SECONDS, COOKIEPATH, COOKIE_DOMAIN);
            return;
        }

        $desired_lang = $this->current_lang;

        if ($desired_lang === $post_lang) {
            return;
        }

        $group = get_post_meta($post->ID, '_ai_translation_group', true);
        if (!$group) {
            return;
        }

        $translated = $this->get_translation_in_lang($group, get_post_type($post), $desired_lang);
        if ($translated) {
            wp_safe_redirect(get_permalink($translated->ID), 302);
            exit;
        }
    }

    public function filter_front_queries($query)
    {
        if (is_admin() || !$query->is_main_query()) {
            return;
        }

        // Force homepage detection when on root with ?ai_lang= parameter
        $request_uri = isset($_SERVER['REQUEST_URI']) ? $_SERVER['REQUEST_URI'] : '';
        $parsed = parse_url($request_uri);
        $path = isset($parsed['path']) ? trim($parsed['path'], '/') : '';
        $home_path = trim(parse_url(home_url(), PHP_URL_PATH) ?: '', '/');
        $is_homepage = ($path === $home_path || $path === '');

        if ($is_homepage && isset($_GET['ai_lang'])) {
            $show_on_front = get_option('show_on_front');
            $front_page_id = get_option('page_on_front');

            // If WordPress is set to show a static page as homepage
            if ($show_on_front === 'page' && $front_page_id) {
                // Force the query to show the front page
                $query->set('page_id', $front_page_id);
                $query->is_home = false;
                $query->is_page = true;
                $query->is_singular = true;
            }
        }

        $current_lang = $this->current_lang;
        $default_lang = $this->settings['default_lang'];

        if ($current_lang === $default_lang) {
            return;
        }

        if ($query->is_singular()) {
            return;
        }

        $meta_query = (array) $query->get('meta_query');
        $meta_query[] = array(
            'key' => '_ai_lang',
            'value' => $current_lang,
            'compare' => '='
        );
        $query->set('meta_query', $meta_query);
    }

    /** Hreflang output */
    public function output_hreflang_tags()
    {
        if (!is_singular()) {
            return;
        }

        global $post;
        if (!$post) {
            return;
        }

        $group = get_post_meta($post->ID, '_ai_translation_group', true);
        if (!$group) {
            return;
        }

        $translations = $this->get_group_posts($group, get_post_type($post));
        if (empty($translations)) {
            return;
        }

        $links = array();
        foreach ($translations as $translation) {
            $lang = get_post_meta($translation->ID, '_ai_lang', true);
            if (!$lang) {
                continue;
            }
            $links[$lang] = get_permalink($translation->ID);
        }

        if (empty($links)) {
            return;
        }

        foreach ($links as $lang => $url) {
            printf(
                '<link rel="alternate" hreflang="%s" href="%s" />' . "\n",
                esc_attr($lang),
                esc_url($url)
            );
        }

        if (isset($links[$this->settings['default_lang']])) {
            printf(
                '<link rel="alternate" hreflang="x-default" href="%s" />' . "\n",
                esc_url($links[$this->settings['default_lang']])
            );
        }
    }

    /** Helpers */
    public function get_translation_in_lang($group, $post_type, $lang)
    {
        $posts = $this->get_group_posts($group, $post_type);
        foreach ($posts as $post) {
            $post_lang = get_post_meta($post->ID, '_ai_lang', true);
            if ($post_lang === $lang) {
                return $post;
            }
        }
        return null;
    }

    public function get_group_posts($group, $post_type)
    {
        $cache_key = 'aibui_group_' . md5($group . $post_type);
        $cached = wp_cache_get($cache_key, 'aibui_translations');
        if ($cached !== false) {
            return $cached;
        }

        $posts = get_posts(array(
            'post_type' => $post_type,
            'posts_per_page' => -1,
            'post_status' => array('publish', 'draft', 'pending', 'future'),
            'meta_query' => array(
                array(
                    'key' => '_ai_translation_group',
                    'value' => $group,
                    'compare' => '='
                )
            )
        ));

        wp_cache_set($cache_key, $posts, 'aibui_translations', MINUTE_IN_SECONDS);
        return $posts;
    }

    public function maybe_swap_template_part($pre_render, $block)
    {
        if (!is_array($block) || ($block['blockName'] ?? '') !== 'core/template-part') {
            return $pre_render;
        }

        $slug = $block['attrs']['slug'] ?? '';
        if (!$slug) {
            return $pre_render;
        }

        $theme = $block['attrs']['theme'] ?? wp_get_theme()->get_stylesheet();
        $base_part = $this->get_template_part_post($slug, $theme);
        if (!$base_part) {
            return $pre_render;
        }

        $group = get_post_meta($base_part->ID, '_ai_translation_group', true);
        if (!$group) {
            return $pre_render;
        }

        $translated = $this->get_translation_in_lang($group, 'wp_template_part', $this->current_lang);
        if (!$translated) {
            return $pre_render;
        }

        return do_blocks($translated->post_content);
    }

    private function get_template_part_post($slug, $theme)
    {
        $parts = get_posts(array(
            'post_type' => 'wp_template_part',
            'name' => $slug,
            'posts_per_page' => 1,
            'post_status' => array('publish', 'draft', 'pending', 'future'),
            'tax_query' => array(
                array(
                    'taxonomy' => 'wp_theme',
                    'field' => 'name',
                    'terms' => $theme,
                )
            )
        ));

        return $parts ? $parts[0] : null;
    }
}


```
