URL 中的 Wordpress 语言前缀

Wordpress lang prefix in URL

在我的网站上,我有多种语言可供用户选择。这目前适用于简单的 $_GET、重定向和 cookie。但是我想将这个系统升级为使用在我的 url 中添加前缀的语言,以便它们被 Google 索引。由于我已经有一些工作,我宁愿跳过那些笨重的大插件,如 WPML 或 qTranslate(及其后代)。

所以我现在正在尝试做一个插件。在 SO 上,我找到了用户 loushou 的插件工作代码(在这里查看他的回答:)。

现在这个插件所做的是添加重写标签 %lang%,然后在多个过滤器中将其替换为请求的字符串(例如。com/en/post-name/ 设置 lang=en。它还会更新包含前缀的页面(如在导航菜单和其他地方)。

我现在想要完成的是删除默认页面语言的前缀,这样它只会在用户选择任何其他语言时显示。所以 'en' 会从 url 中隐藏,但 'de' 会出现。但在这里我有一个问题。我设法让 WP 不显示默认前缀,但突然页面和作者页面停止工作。

代码如下:

<?php (__FILE__ == $_SERVER['SCRIPT_FILENAME']) ? die(header('Location: /')) : null;
/**
 * Plugin Name: Loushou Language URLs
 * Plugin URI:  http://quadshot.com/
 * Description: Adding the ability to have language support in your frontend urls.
 * Version:     0.1-beta
 * Author:      Loushou
 * Author URI:  http://quadshot.com/
 */

class Clou_rewrite_takeover {
    protected static $add_rules = array();


    public static function pre_init() {
        // debug
        add_action('admin_footer-options-permalink.php', array(__CLASS__, 'qsart_rewrite_debug'));

        // add rw tag
        add_action('init', array(__CLASS__, 'add_directory_rewrite'));

        // rw rule adds
        add_filter(is_admin() ? 'setup_theme' : 'do_parse_request', array(__CLASS__, 'do_parse_request'), 0);
        add_filter('post_rewrite_rules', array(__CLASS__, 'post_rewrite_rules'));
        add_filter('date_rewrite_rules', array(__CLASS__, 'date_rewrite_rules'));
        add_filter('root_rewrite_rules', array(__CLASS__, 'root_rewrite_rules'));
        add_filter('comments_rewrite_rules', array(__CLASS__, 'comments_rewrite_rules'));
        add_filter('search_rewrite_rules', array(__CLASS__, 'search_rewrite_rules'));
        add_filter('author_rewrite_rules', array(__CLASS__, 'author_rewrite_rules'));
        add_filter('page_rewrite_rules', array(__CLASS__, 'page_rewrite_rules'));
        add_filter('rewrite_rules_array', array(__CLASS__, 'final_rules_correction'), PHP_INT_MAX, 1);

        // query vars
        add_filter('query_vars', array(__CLASS__, 'add_lang_query_var'), 10, 1);
        add_filter('request', array(__CLASS__, 'default_language'), 9);

        add_filter('locale', array(__CLASS__, 'set_language'));

        // fix permalinks
        $link_filters_needing_rewrite = array(
            'post_link',
            'post_type_link',
            'page_link',
            'attachment_link',
            'search_link',
            'post_type_archive_link',
            'year_link',
            'month_link',
            'day_link',
            'feed_link',
            'author_link',
            'term_link',
            'category_feed_link',
            'term_feed_link',
            'taxonomy_feed_link',
            'author_feed_link',
            'search_feed_link',
            'post_type_archive_feed_link',
        );

        add_filter('pre_post_link', array(__CLASS__, 'change_permalink_structure'), 10, 3);

        foreach ($link_filters_needing_rewrite as $link_filter) {
            add_filter($link_filter, array(__CLASS__, 'rewrite_lang_in_permalink'), 11, 3);
        }
    }


    public static function do_parse_request($cur) {
        self::get_page_permastruct();
        self::get_author_permastruct();
        self::correct_extras();
        return $cur;
    }


    public static function get_supported_langs() {
        return apply_filters('lou-get-supported-languages', array('en', 'de'));
    }


    public static function add_directory_rewrite() {
        global $wp_rewrite;
        
        $supported_languages = self::get_supported_langs();

        // add_permastruct('lang', '%lang%', [ 'ep_mask' => EP_PERMALINK ] );
        add_rewrite_tag('%lang%', '('.implode('|', $supported_languages).')?');
    }


    public static function unleadingslashit($str) {
        return ltrim($str, '/');
    }


    public static function final_rules_correction($rules) {
        global $wp_rewrite;

        // print("<pre>".print_r($rules, true)."</pre>");

        $new_rules = array();
        $supported_languages = self::get_supported_langs();
        $find = implode('|', $supported_languages);
        $find_find = '#(?<!\()('.preg_quote($find, '#').')#';
        $preg_node = str_replace('%%%', '(\d+)', preg_quote($wp_rewrite->preg_index('%%%'), '#'));

        foreach ($rules as $k => $v) {
            if (preg_match($find_find, $k)) {
                $nk = preg_replace($find_find, '('.$find.')', $k);
                $parts = explode('?', $v);
                $index = array_shift($parts);
                $pv = implode('?', $parts);
                // $pv = $v;

                $pv = preg_replace_callback('#'.$preg_node.'#', function ($matches) use ($wp_rewrite) {
                    return $wp_rewrite->preg_index($matches[1]+1);
                }, $pv);

                $nv = $index.'?lang='.$wp_rewrite->preg_index(1).(!empty($pv) ? '&'.$pv : '');
                $new_rules[$nk] = $nv;
            } else {
                $new_rules[$k] = $v;
            }
        }

        // print("<pre>".print_r($new_rules, true)."</pre>");

        return $new_rules;
    }


    public static function change_permalink_structure($struct) {
        $struct = self::unleadingslashit($struct);
        $struct = preg_replace('#^%lang%/?#', '', $struct);

        // print("<pre>".print_r($struct, true)."</pre>");

        return '/%lang%/'.$struct;
    }


    public static function extras_rewrite_rules($rules, $struct) {
        global $wp_rewrite;

        // var_dump($wp_rewrite);
        // print("<pre>".print_r($wp_rewrite, true)."</pre>");
        // var_dump($rules);
        // var_dump($struct);

        if(is_array($struct)) {
            if ( count( $struct ) == 2 ) {
                $new_rules = $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($struct[0]), $struct[1] );
            } else {
                $new_rules = $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($struct['struct']), $struct['ep_mask'], $struct['paged'], $struct['feed'], $struct['forcomments'], $struct['walk_dirs'], $struct['endpoints'] );
            }

        } else {
            $new_rules = $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($struct) );
        }

        // print("<pre>".print_r($new_rules, true)."</pre>");
        // print("<pre>".print_r($rules, true)."</pre>");

        return $new_rules + $rules;
    }


    public static function post_rewrite_rules($rules) {
        global $wp_rewrite;

        // hack to add code for extras type urls (usually created by other plugins)
        $func = array(__CLASS__, 'extras_rewrite_rules');

        foreach ($wp_rewrite->extra_permastructs as $type => $struct) {
            // $filter = ($type == 'post_tag' ? 'tag' : $type).'_rewrite_rules';
            $filter = $type.'_rewrite_rules';
            add_filter($filter, function ($rules) use ($struct, $func) { return call_user_func_array($func, array($rules, $struct)); });
        }

        return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->permalink_structure), EP_PERMALINK ) + $rules;
    }


    public static function date_rewrite_rules($rules) {
        global $wp_rewrite;
        return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->get_date_permastruct()), EP_DATE) + $rules;
    }


    public static function root_rewrite_rules($rules) {
        global $wp_rewrite;
        return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->get_date_permastruct()), EP_DATE) + $rules;
    }


    public static function comments_rewrite_rules($rules) {
        global $wp_rewrite;
        return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->root . $wp_rewrite->comments_base), EP_COMMENTS, false, true, true, false) + $rules;
    }


    public static function search_rewrite_rules($rules) {
        global $wp_rewrite;
        return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->get_search_permastruct()), EP_SEARCH) + $rules;
    }


    public static function author_rewrite_rules($rules) {
        global $wp_rewrite;
        return $wp_rewrite->generate_rewrite_rules( self::change_permalink_structure($wp_rewrite->get_author_permastruct()), EP_AUTHORS) + $rules;
    }


    public static function page_rewrite_rules($rules) {
        global $wp_rewrite;
        $page_structure = self::get_page_permastruct();
        // $page_structure = $wp_rewrite->page_structure;
        // $page_structure = $wp_rewrite->get_page_permastruct();

        // $page_structure = self::change_permalink_structure($page_structure);

       // print("<pre>".print_r($page_structure, true)."</pre>");

        return $wp_rewrite->generate_rewrite_rules( $page_structure, EP_PAGES, true, true, false, false ) + $rules;
    }


    protected static function get_page_permastruct() {
        global $wp_rewrite;

        if (empty($wp_rewrite->permalink_structure)) {
            $wp_rewrite->page_structure = '';

            return false;
        }

        $wp_rewrite->page_structure = self::change_permalink_structure($wp_rewrite->root . '%pagename%');

        // print("<pre>".print_r($wp_rewrite, true)."</pre>");

        return $wp_rewrite->page_structure;
    }


    protected static function get_author_permastruct() {
        global $wp_rewrite;

        if ( empty($wp_rewrite->permalink_structure) ) {
            $wp_rewrite->author_structure = '';
            return false;
        }

        $wp_rewrite->author_structure = self::change_permalink_structure($wp_rewrite->front . $wp_rewrite->author_base . '/%author%');

        return $wp_rewrite->author_structure;
    }


    protected static function correct_extras() {
        global $wp_rewrite;

        foreach ($wp_rewrite->extra_permastructs as $k => $v)
        $wp_rewrite->extra_permastructs[$k]['struct'] = self::change_permalink_structure($v['struct']);
    }


    public static function get_default_post_lang($post = 0) {
        // return ( $lang = get_query_var('lang') ) ? $lang : 'en';
        return ( $lang = get_query_var('lang') ) ? $lang : null;
    }


    public static function rewrite_lang_in_permalink($permalink, $post = 0, $leavename = false) {
        // find the default post language via a function you have created to 
        // determine the default language url. this could be based on the current
        // language the user has selected on the frontend, or based on the current
        // url, or based on the post itself. it is up to you
        $lang = self::get_default_post_lang($post);

        // echo '<br />';
        // echo $lang.' - '.$permalink;
        // echo '<br />';

        // once you have the default language, it is a simple search and replace
        if(!is_null($lang)) {
            return str_replace('%lang%', $lang, $permalink);
        } else {
            return str_replace('%lang%/', '', $permalink);
        }
    }


    public static function add_lang_query_var($vars) {
        // tell WP to expect the lang query_var, which you can then later use
        $vars[] = 'lang';

        // return the new list of query vars, which includes our lang param
        return array_unique($vars);
    }


    public static function default_language($vars) {
        if (array_diff( array_keys($vars), array('preview', 'page', 'paged', 'cpage') )) {
            // $vars['lang'] = !empty($vars['lang']) ? $vars['lang'] : 'en';
            $vars['lang'] = !empty($vars['lang']) ? $vars['lang'] : null;

            // print("<pre>".print_r($vars, true)."</pre>");
            // if(!empty($vars['lang']))
            // $vars['lang'] = $vars['lang'];
        }
        return $vars;
    }



    public static function set_language() {
        $lang = 'en';

        $req_uri = explode('/', $_SERVER['REQUEST_URI']);

        // strpos($_SERVER['REQUEST_URI'], '/ee/') === 0

        if(in_array('de', $req_uri)) {
            $lang = 'de';
        }

        $locale = self::convert_locale($lang);

        return $locale;
    }


    public static function qsart_rewrite_debug() {
        if (isset($_COOKIE['rwdebug']) && $_COOKIE['rwdebug'] == 1) {
            global $wp_rewrite;
            echo '<pre style="background-color:#ffffff; font-size:10px;">';
            print_r($wp_rewrite->rules);
            echo '</pre>';
        }
    }
}


if (defined('ABSPATH') && function_exists('add_action')) {
    lou_rewrite_takeover::pre_init();
}

我注意到,如果我注释掉第 70 行,执行规则刷新,然后启用此行,我得到没有前缀的页面开始工作,但是 URL 内部导航和其他地方是未更改(或者更确切地说正在更改为空 lang 字符串)。

现在,如果我不对上述行进行注释并进行刷新,那么如果我尝试访问页面示例。com/page-name/ - 我会看到 404 页面。有趣的是,如果我在第 328 行打印出 $vars var,我可以看到 WP 没有设置“pagename”键,而是设置了“name”,这意味着它试图找到 post 而不是页面,导致 404.

我认为这意味着重写规则有问题。但是检查它们并没有发现明显的错误,至少对我来说是这样。

所以我需要帮助。了解到此代码来自 2013 年,我会毫不犹豫地使用任何其他建议的解决方案。此外,如果有某种小型、非常基本的插件可以执行此特定功能,我也会使用它。也可能有一种方法可以绕过使用 .htaccess,但我认为 get_query_vars 会停止工作。

我设法通过重写 final_rules_correction() 方法并禁用 post/date/etc rewrite_rules.

的过滤器来解决我的大部分问题

这样做是重写所有生成的规则并向它们添加一个 lang 前缀正则表达式,并向最终的 link 添加一个 lang var。如果 URL 请求 URL 不包括语言变量,它还允许有一个空的语言变量。

$prefixes = self::get_supported_langs();
        $prefix_regexp = '(?:(' . implode( '|', $prefixes ) . ')/)?';
        $new_rewrite_rules = array();

        $match_pattern = '/$matches\[(\d)\]/si';

        foreach ( $rules as $rewrite_rule => $match ) {

            $parts = explode('?', $match);
            $index = array_shift($parts);
            $pv = implode('?', $parts);

            $pv = preg_replace_callback($match_pattern, function ($m) {
                return sprintf('$matches[%d]', $m[1] + 1);
            }, $pv);

            $nv = $index.'?lang='.$wp_rewrite->preg_index(1).(!empty($pv) ? '&'.$pv : '');
            $new_rewrite_rules[$prefix_regexp . $rewrite_rule] = $nv;
        }

我现在遇到的问题是 URLs 在 AJAX 调用中不包含 lang 前缀。考虑到我可能必须在 AJAX 调用中传递 lang 前缀,然后在 AJAX 调用期间为我需要检索的帖子和其他对象重写 permalinks...

注意: 这不会在 home_url 和类似函数中重写 links。