frontend-filters.php000066600000032601152132747060010557 0ustar00options['media_support'] ) { foreach ( array( 'audio', 'image', 'video' ) as $media ) { add_filter( "widget_media_{$media}_instance", array( $this, 'widget_media_instance' ), 1 ); // Since WP 4.8 } } // Strings translation ( must be applied before WordPress applies its default formatting filters ) foreach ( array( 'widget_text', 'widget_title', 'option_blogname', 'option_blogdescription', 'option_date_format', 'option_time_format' ) as $filter ) { add_filter( $filter, 'pll__', 1 ); } // Translates biography add_filter( 'get_user_metadata', array( $this, 'get_user_metadata' ), 10, 4 ); // Set posts and terms language when created from frontend ( ex with P2 theme ) add_action( 'save_post', array( $this, 'save_post' ), 200, 2 ); add_action( 'create_term', array( $this, 'save_term' ), 10, 3 ); add_action( 'edit_term', array( $this, 'save_term' ), 10, 3 ); if ( $this->options['media_support'] ) { add_action( 'add_attachment', array( $this, 'set_default_language' ) ); } // Support theme customizer // FIXME of course does not work if 'transport' is set to 'postMessage' if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { add_filter( 'pre_option_blogname', 'pll__', 20 ); add_filter( 'pre_option_blogdescription', 'pll__', 20 ); } } /** * Returns the locale based on current language * * @since 0.1 * * @param string $locale * @return string */ public function get_locale( $locale ) { return $this->curlang->locale; } /** * Filters sticky posts by current language * * @since 0.8 * * @param array $posts list of sticky posts ids * @return array modified list of sticky posts ids */ public function option_sticky_posts( $posts ) { global $wpdb; if ( $this->curlang && ! empty( $posts ) ) { $_posts = wp_cache_get( 'sticky_posts', 'options' ); // This option is usually cached in 'all_options' by WP if ( empty( $_posts ) || ! is_array( $_posts[ $this->curlang->term_taxonomy_id ] ) ) { $posts = array_map( 'intval', $posts ); $posts = implode( ',', $posts ); $languages = $this->model->get_languages_list( array( 'fields' => 'term_taxonomy_id' ) ); $_posts = array_fill_keys( $languages, array() ); // Init with empty arrays $languages = implode( ',', $languages ); $relations = $wpdb->get_results( "SELECT object_id, term_taxonomy_id FROM {$wpdb->term_relationships} WHERE object_id IN ({$posts}) AND term_taxonomy_id IN ({$languages})" ); foreach ( $relations as $relation ) { $_posts[ $relation->term_taxonomy_id ][] = $relation->object_id; } wp_cache_add( 'sticky_posts', $_posts, 'options' ); } $posts = $_posts[ $this->curlang->term_taxonomy_id ]; } return $posts; } /** * Adds language dependent cache domain when querying terms * useful as the 'lang' parameter is not included in cache key by WordPress * * @since 1.3 * * @param array $args * @return array */ public function get_terms_args( $args ) { $lang = isset( $args['lang'] ) ? $args['lang'] : $this->curlang->slug; $key = '_' . ( is_array( $lang ) ? implode( ',', $lang ) : $lang ); $args['cache_domain'] = empty( $args['cache_domain'] ) ? 'pll' . $key : $args['cache_domain'] . $key; return $args; } /** * Filters categories and post tags by language when needed * * @since 0.2 * * @param array $clauses sql clauses * @param array $taxonomies * @param array $args get_terms arguments * @return array modified sql clauses */ public function terms_clauses( $clauses, $taxonomies, $args ) { // Does nothing except on taxonomies which are filterable // Since WP 4.7, make sure not to filter wp_get_object_terms() if ( ! $this->model->is_translated_taxonomy( $taxonomies ) || ! empty( $args['object_ids'] ) ) { return $clauses; } // Adds our clauses to filter by language return $this->model->terms_clauses( $clauses, isset( $args['lang'] ) ? $args['lang'] : $this->curlang ); } /** * Modifies the sql request for wp_get_archives to filter by the current language * * @since 1.9 * * @param string $sql JOIN clause * @param array $r wp_get_archives arguments * @return string modified JOIN clause */ public function getarchives_join( $sql, $r ) { return ! empty( $r['post_type'] ) && $this->model->is_translated_post_type( $r['post_type'] ) ? $sql . $this->model->post->join_clause() : $sql; } /** * Modifies the sql request for wp_get_archives to filter by the current language * * @since 1.9 * * @param string $sql WHERE clause * @param array $r wp_get_archives arguments * @return string modified WHERE clause */ public function getarchives_where( $sql, $r ) { return ! empty( $r['post_type'] ) && $this->model->is_translated_post_type( $r['post_type'] ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql; } /** * Modifies the sql request for get_adjacent_post to filter by the current language * * @since 0.1 * * @param string $sql The JOIN clause in the SQL. * @param bool $in_same_term Whether post should be in a same taxonomy term. * @param array $excluded_terms Array of excluded term IDs. * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true. * @param WP_Post $post WP_Post object. * @return string modified JOIN clause */ public function posts_join( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) { return $this->model->is_translated_post_type( $post->post_type ) ? $sql . $this->model->post->join_clause( 'p' ) : $sql; } /** * Modifies the sql request for wp_get_archives and get_adjacent_post to filter by the current language * * @since 0.1 * * @param string $sql The WHERE clause in the SQL. * @param bool $in_same_term Whether post should be in a same taxonomy term. * @param array $excluded_terms Array of excluded term IDs. * @param string $taxonomy Taxonomy. Used to identify the term used when `$in_same_term` is true. * @param WP_Post $post WP_Post object. * @return string modified WHERE clause */ public function posts_where( $sql, $in_same_term, $excluded_terms, $taxonomy = '', $post = null ) { return $this->model->is_translated_post_type( $post->post_type ) ? $sql . $this->model->post->where_clause( $this->curlang ) : $sql; } /** * Filters the widgets according to the current language * Don't display if a language filter is set and this is not the current one * * @since 0.3 * * @param array $instance widget settings * @param object $widget WP_Widget object * @return bool|array false if we hide the widget, unmodified $instance otherwise */ public function widget_display_callback( $instance, $widget ) { return ! empty( $instance['pll_lang'] ) && $instance['pll_lang'] != $this->curlang->slug ? false : $instance; } /** * Remove widgets from sidebars if they are not visible in the current language * Needed to allow is_active_sidebar() to return false if all widgets are not for the current language. See #54 * * @since 2.1 * * @param array $sidebars_widgets An associative array of sidebars and their widgets * @return array */ public function sidebars_widgets( $sidebars_widgets ) { global $wp_registered_widgets; foreach ( $sidebars_widgets as $sidebar => $widgets ) { if ( 'wp_inactive_widgets' == $sidebar || empty( $widgets ) ) { continue; } foreach ( $widgets as $key => $widget ) { // Nothing can be done if the widget is created using pre WP2.8 API :( // There is no object, so we can't access it to get the widget options if ( ! isset( $wp_registered_widgets[ $widget ]['callback'] ) || ! is_array( $wp_registered_widgets[ $widget ]['callback'] ) || ! isset( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! is_object( $wp_registered_widgets[ $widget ]['callback'][0] ) || ! method_exists( $wp_registered_widgets[ $widget ]['callback'][0], 'get_settings' ) ) { continue; } $widget_settings = $wp_registered_widgets[ $widget ]['callback'][0]->get_settings(); $number = $wp_registered_widgets[ $widget ]['params'][0]['number']; // Remove the widget if not visible in the current language if ( ! empty( $widget_settings[ $number ]['pll_lang'] ) && $widget_settings[ $number ]['pll_lang'] !== $this->curlang->slug ) { unset( $sidebars_widgets[ $sidebar ][ $key ] ); } } } return $sidebars_widgets; } /** * Translates media in media widgets * * @since 2.1.5 * * @param array $instance Widget instance data * @return array */ public function widget_media_instance( $instance ) { if ( empty( $instance['pll_lang'] ) && $instance['attachment_id'] && $tr_id = pll_get_post( $instance['attachment_id'] ) ) { $instance['attachment_id'] = $tr_id; $attachment = get_post( $tr_id ); if ( $instance['caption'] && ! empty( $attachment->post_excerpt ) ) { $instance['caption'] = $attachment->post_excerpt; } if ( $instance['alt'] && $alt_text = get_post_meta( $tr_id, '_wp_attachment_image_alt', true ) ) { $instance['alt'] = $alt_text; } if ( $instance['image_title'] && ! empty( $attachment->post_title ) ) { $instance['image_title'] = $attachment->post_title; } } return $instance; } /** * Translates biography * Makes sure that the correct locale is used for ajax calls when the user is logged in * * @since 0.9 * * @param null $return * @param int $id User id * @param string $meta_key * @param bool $single Whether to return only the first value of the specified $meta_key * @return null|string */ public function get_user_metadata( $return, $id, $meta_key, $single ) { switch ( $meta_key ) { case 'description': if ( $this->curlang->slug !== $this->options['default_lang'] ) { $return = get_user_meta( $id, 'description_' . $this->curlang->slug, $single ); } break; case 'locale': if ( Polylang::is_ajax_on_front() ) { $return = get_locale(); } break; } return $return; } /** * Allows to set a language by default for posts if it has no language yet * * @since 1.5.4 * * @param int $post_id */ public function set_default_language( $post_id ) { if ( ! $this->model->post->get_language( $post_id ) ) { if ( isset( $_REQUEST['lang'] ) ) { $this->model->post->set_language( $post_id, $_REQUEST['lang'] ); } elseif ( ( $parent_id = wp_get_post_parent_id( $post_id ) ) && $parent_lang = $this->model->post->get_language( $parent_id ) ) { $this->model->post->set_language( $post_id, $parent_lang ); } else { $this->model->post->set_language( $post_id, $this->curlang ); } } } /** * Called when a post ( or page ) is saved, published or updated * Does nothing except on post types which are filterable * Sets the language but does not allow to modify it * * @since 1.1 * * @param int $post_id * @param object $post */ public function save_post( $post_id, $post ) { if ( $this->model->is_translated_post_type( $post->post_type ) ) { $this->set_default_language( $post_id ); } } /** * Called when a category or post tag is created or edited * Does nothing except on taxonomies which are filterable * Sets the language but does not allow to modify it * * @since 1.1 * * @param int $term_id * @param int $tt_id Term taxonomy id * @param string $taxonomy */ public function save_term( $term_id, $tt_id, $taxonomy ) { if ( $this->model->is_translated_taxonomy( $taxonomy ) && ! $this->model->term->get_language( $term_id ) ) { if ( isset( $_REQUEST['lang'] ) ) { $this->model->term->set_language( $term_id, $_REQUEST['lang'] ); } elseif ( ( $term = get_term( $term_id, $taxonomy ) ) && ! empty( $term->parent ) && $parent_lang = $this->model->term->get_language( $term->parent ) ) { $this->model->term->set_language( $term_id, $parent_lang ); } else { $this->model->term->set_language( $term_id, $this->curlang ); } } } } choose-lang-domain.php000066600000001375152132747060010742 0ustar00model->get_language( $this->links_model->get_language_from_url() ); } /** * Adds query vars to query for home pages in all languages * * @since 1.5 */ public function home_requested() { $this->set_curlang_in_query( $GLOBALS['wp_query'] ); /** This action is documented in include/choose-lang.php */ do_action( 'pll_home_requested' ); } } choose-lang-content.php000066600000010616152132747060011143 0ustar00options['media_support'] ) ) { return $this->get_preferred_language(); } if ( $var = get_query_var( 'lang' ) ) { $lang = explode( ',',$var ); $lang = $this->model->get_language( reset( $lang ) ); // choose the first queried language } elseif ( ( is_single() || is_page() || ( is_attachment() && $this->options['media_support'] ) ) && ( ( $var = get_queried_object_id() ) || ( $var = get_query_var( 'p' ) ) || ( $var = get_query_var( 'page_id' ) ) || ( $var = get_query_var( 'attachment_id' ) ) ) ) { $lang = $this->model->post->get_language( $var ); } else { foreach ( $this->model->get_translated_taxonomies() as $taxonomy ) { if ( $var = get_query_var( get_taxonomy( $taxonomy )->query_var ) ) { $lang = $this->model->term->get_language( $var, $taxonomy ); } } } /** * Filter the language before it is set from the content * * @since 0.9 * * @param bool|object $lang language object or false if none was found */ return apply_filters( 'pll_get_current_language', isset( $lang ) ? $lang : false ); } /** * sets the language for home page * add the lang query var when querying archives with no language code * * @since 1.2 * * @param object $query instance of WP_Query */ public function parse_main_query( $query ) { if ( $query !== $GLOBALS['wp_the_query'] ) { return; } $qv = $query->query_vars; // homepage is requested, let's set the language // take care to avoid posts page for which is_home = 1 if ( empty( $query->query ) && ( is_home() || is_page() ) ) { $this->home_language(); $this->home_requested(); } parent::parse_main_query( $query ); $is_archive = ( count( $query->query ) == 1 && ! empty( $qv['paged'] ) ) || $query->is_date || $query->is_author || ( ! empty( $qv['post_type'] ) && $query->is_post_type_archive && $this->model->is_translated_post_type( $qv['post_type'] ) ); // sets the language in case we hide the default language // use $query->query['s'] as is_search is not set when search is empty // http://wordpress.org/support/topic/search-for-empty-string-in-default-language if ( $this->options['hide_default'] && ! isset( $qv['lang'] ) && ( $is_archive || isset( $query->query['s'] ) || ( count( $query->query ) == 1 && ! empty( $qv['feed'] ) ) ) ) { $this->set_language( $this->model->get_language( $this->options['default_lang'] ) ); $this->set_curlang_in_query( $query ); } } /** * sets the language from content * * @since 1.2 */ public function wp() { // nothing to do if the language has already been set ( although normally the filter has been removed ) if ( ! $this->curlang && $curlang = $this->get_language_from_content() ) { parent::set_language( $curlang ); } } /** * if no language found by get_language_from_content, return the preferred one * * @since 0.9 * * @param object|bool $lang Language found in get_language_from_content * @return object Language */ public function pll_get_current_language( $lang ) { return ! $lang ? $this->get_preferred_language() : $lang; } } frontend-filters-links.php000066600000032255152132747060011702 0ustar00curlang = &$polylang->curlang; $this->cache = new PLL_Cache(); // Rewrites author and date links to filter them by language foreach ( array( 'feed_link', 'author_link', 'search_link', 'year_link', 'month_link', 'day_link' ) as $filter ) { add_filter( $filter, array( $this, 'archive_link' ), 20 ); } // Meta in the html head section add_action( 'wp_head', array( $this, 'wp_head' ) ); // Modifies the home url if ( ! defined( 'PLL_FILTER_HOME_URL' ) || PLL_FILTER_HOME_URL ) { add_filter( 'home_url', array( $this, 'home_url' ), 10, 2 ); } if ( $this->options['force_lang'] > 1 ) { // Rewrites next and previous post links when not automatically done by WordPress add_filter( 'get_pagenum_link', array( $this, 'archive_link' ), 20 ); // Rewrites ajax url add_filter( 'admin_url', array( $this, 'admin_url' ), 10, 2 ); } // Redirects to canonical url before WordPress redirect_canonical // but after Nextgen Gallery which hacks $_SERVER['REQUEST_URI'] !!! and restores it in 'template_redirect' with priority 1 add_action( 'template_redirect', array( $this, 'check_canonical_url' ), 4 ); } /** * Modifies the author and date links to add the language parameter ( as well as feed link ) * * @since 0.4 * * @param string $link * @return string modified link */ public function archive_link( $link ) { return $this->links_model->switch_language_in_link( $link, $this->curlang ); } /** * Modifies page links * and caches the result * * @since 1.7 * * @param string $link post link * @param int $post_id post ID * @return string modified post link */ public function _get_page_link( $link, $post_id ) { $sample = false !== strpos( $link, '%pagename%' ); // To avoid a conflict with plugin Custom Permalinks $cache_key = "post:{$post_id}:{$sample}"; if ( false === $_link = $this->cache->get( $cache_key ) ) { $_link = parent::_get_page_link( $link, $post_id ); $this->cache->set( $cache_key, $_link ); } return $_link; } /** * Modifies attachment links * and caches the result * * @since 1.6.2 * * @param string $link attachment link * @param int $post_id attachment link * @return string modified attachment link */ public function attachment_link( $link, $post_id ) { $cache_key = 'post:' . $post_id; if ( false === $_link = $this->cache->get( $cache_key ) ) { $_link = parent::attachment_link( $link, $post_id ); $this->cache->set( $cache_key, $_link ); } return $_link; } /** * Modifies custom posts links * and caches the result * * @since 1.6 * * @param string $link post link * @param object $post post object * @return string modified post link */ public function post_type_link( $link, $post ) { $sample = false !== strpos( $link, '%postname%' ); // To avoid a conflict with plugin Custom Permalinks $cache_key = "post:{$post->ID}:{$sample}"; if ( false === $_link = $this->cache->get( $cache_key ) ) { $_link = parent::post_type_link( $link, $post ); $this->cache->set( $cache_key, $_link ); } return $_link; } /** * Modifies filtered taxonomies ( post format like ) and translated taxonomies links * and caches the result * * @since 0.7 * * @param string $link * @param object $term term object * @param string $tax taxonomy name * @return string modified link */ public function term_link( $link, $term, $tax ) { $cache_key = 'term:' . $term->term_id; if ( false === $_link = $this->cache->get( $cache_key ) ) { if ( in_array( $tax, $this->model->get_filtered_taxonomies() ) ) { $_link = $this->links_model->switch_language_in_link( $link, $this->curlang ); /** This filter is documented in include/filters-links.php */ $_link = apply_filters( 'pll_term_link', $_link, $this->curlang, $term ); } else { $_link = parent::term_link( $link, $term, $tax ); } $this->cache->set( $cache_key, $_link ); } return $_link; } /** * Outputs references to translated pages ( if exists ) in the html head section * * @since 0.1 */ public function wp_head() { // Don't output anything on paged archives: see https://wordpress.org/support/topic/hreflang-on-page2 // Don't output anything on paged pages and paged posts if ( is_paged() || ( is_singular() && ( $page = get_query_var( 'page' ) ) && $page > 1 ) ) { return; } // Google recommends to include self link https://support.google.com/webmasters/answer/189077?hl=en foreach ( $this->model->get_languages_list() as $language ) { if ( $url = $this->links->get_translation_url( $language ) ) { $urls[ $language->get_locale( 'display' ) ] = $url; } } // Ouptputs the section only if there are translations ( $urls always contains self link ) if ( ! empty( $urls ) && count( $urls ) > 1 ) { // Prepare the list of languages to remove the country code foreach ( array_keys( $urls ) as $locale ) { $split = explode( '-', $locale ); $languages[ $locale ] = reset( $split ); } $count = array_count_values( $languages ); foreach ( $urls as $locale => $url ) { $lang = $count[ $languages[ $locale ] ] > 1 ? $locale : $languages[ $locale ]; // Output the country code only when necessary $hreflangs[ $lang ] = $url; } // Adds the site root url when the default language code is not hidden // See https://wordpress.org/support/topic/implementation-of-hreflangx-default if ( is_front_page() && ! $this->options['hide_default'] && $this->options['force_lang'] < 3 ) { $hreflangs['x-default'] = home_url( '/' ); } /** * Filters the list of rel hreflang attributes * * @since 2.1 * * @param array $hreflangs Array of urls with language codes as keys */ $hreflangs = apply_filters( 'pll_rel_hreflang_attributes', $hreflangs ); foreach ( $hreflangs as $lang => $url ) { printf( '' . "\n", esc_url( $url ), esc_attr( $lang ) ); } } } /** * Filters the home url to get the right language * * @since 0.4 * * @param string $url * @param string $path * @return string */ public function home_url( $url, $path ) { if ( ! ( did_action( 'template_redirect' ) || did_action( 'login_init' ) ) || rtrim( $url,'/' ) != $this->links_model->home ) { return $url; } static $white_list, $black_list; // Avoid evaluating this at each function call // We *want* to filter the home url in these cases if ( empty( $white_list ) ) { // On Windows get_theme_root() mixes / and \ // We want only \ for the comparison with debug_backtrace $theme_root = get_theme_root(); $theme_root = ( false === strpos( $theme_root, '\\' ) ) ? $theme_root : str_replace( '/', '\\', $theme_root ); /** * Filter the white list of the Polylang 'home_url' filter * The $args contains an array of arrays each of them having * a 'file' key and/or a 'function' key to decide which functions in * which files using home_url() calls must be filtered * * @since 1.1.2 * * @param array $args */ $white_list = apply_filters( 'pll_home_url_white_list', array( array( 'file' => $theme_root ), array( 'function' => 'wp_nav_menu' ), array( 'function' => 'login_footer' ), array( 'function' => 'get_custom_logo' ), ) ); } // We don't want to filter the home url in these cases if ( empty( $black_list ) ) { /** * Filter the black list of the Polylang 'home_url' filter * The $args contains an array of arrays each of them having * a 'file' key and/or a 'function' key to decide which functions in * which files using home_url() calls must be filtered * * @since 1.1.2 * * @param array $args */ $black_list = apply_filters( 'pll_home_url_black_list', array( array( 'file' => 'searchform.php' ), // Since WP 3.6 searchform.php is passed through get_search_form array( 'function' => 'get_search_form' ), ) ); } $traces = version_compare( PHP_VERSION, '5.2.5', '>=' ) ? debug_backtrace( false ) : debug_backtrace(); unset( $traces[0], $traces[1] ); // We don't need the last 2 calls: this function + call_user_func_array (or apply_filters on PHP7+) foreach ( $traces as $trace ) { // Black list first foreach ( $black_list as $v ) { if ( ( isset( $trace['file'], $v['file'] ) && false !== strpos( $trace['file'], $v['file'] ) ) || ( isset( $trace['function'], $v['function'] ) && $trace['function'] == $v['function'] ) ) { return $url; } } foreach ( $white_list as $v ) { if ( ( isset( $trace['function'], $v['function'] ) && $trace['function'] == $v['function'] ) || ( isset( $trace['file'], $v['file'] ) && false !== strpos( $trace['file'], $v['file'] ) && in_array( $trace['function'], array( 'home_url', 'get_home_url', 'bloginfo', 'get_bloginfo' ) ) ) ) { $ok = true; } } } return empty( $ok ) ? $url : ( empty( $path ) ? rtrim( $this->links->get_home_url( $this->curlang ), '/' ) : $this->links->get_home_url( $this->curlang ) ); } /** * Rewrites ajax url when using domains or subdomains * * @since 1.5 * * @param string $url admin url with path evaluated by WordPress * @param string $path admin path * @return string */ public function admin_url( $url, $path ) { return 'admin-ajax.php' === $path ? $this->links_model->switch_language_in_link( $url, $this->curlang ) : $url; } /** * If the language code is not in agreement with the language of the content * redirects incoming links to the proper URL to avoid duplicate content * * @since 0.9.6 * * @param string $requested_url optional * @param bool $do_redirect optional, whether to perform the redirection or not * @return string if redirect is not performed */ public function check_canonical_url( $requested_url = '', $do_redirect = true ) { global $wp_query, $post, $is_IIS; // Don't redirect in same cases as WP if ( is_trackback() || is_search() || is_admin() || is_preview() || is_robots() || ( $is_IIS && ! iis7_supports_permalinks() ) ) { return; } // Don't redirect mysite.com/?attachment_id= to mysite.com/en/?attachment_id= if ( 1 == $this->options['force_lang'] && is_attachment() && isset( $_GET['attachment_id'] ) ) { return; } // If the default language code is not hidden and the static front page url contains the page name // the customizer lands here and the code below would redirect to the list of posts if ( is_customize_preview() ) { return; } if ( empty( $requested_url ) ) { $requested_url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; } if ( is_single() || is_page() ) { if ( isset( $post->ID ) && $this->model->is_translated_post_type( $post->post_type ) ) { $language = $this->model->post->get_language( (int) $post->ID ); } } elseif ( is_category() || is_tag() || is_tax() ) { $obj = $wp_query->get_queried_object(); if ( $this->model->is_translated_taxonomy( $obj->taxonomy ) ) { $language = $this->model->term->get_language( (int) $obj->term_id ); } } elseif ( $wp_query->is_posts_page ) { $obj = $wp_query->get_queried_object(); $language = $this->model->post->get_language( (int) $obj->ID ); } elseif ( is_404() && ! empty( $wp_query->query['page_id'] ) && $id = get_query_var( 'page_id' ) ) { // Special case for page shortlinks when using subdomains or multiple domains // Needed because redirect_canonical doesn't accept to change the domain name $language = $this->model->post->get_language( (int) $id ); } if ( empty( $language ) ) { $language = $this->curlang; $redirect_url = $requested_url; } else { // First get the canonical url evaluated by WP // Workaround a WP bug wich removes the port for some urls and get it back at second call to redirect_canonical $_redirect_url = ( ! $_redirect_url = redirect_canonical( $requested_url, false ) ) ? $requested_url : $_redirect_url; $redirect_url = ( ! $redirect_url = redirect_canonical( $_redirect_url, false ) ) ? $_redirect_url : $redirect_url; // Then get the right language code in url $redirect_url = $this->options['force_lang'] ? $this->links_model->switch_language_in_link( $redirect_url, $language ) : $this->links_model->remove_language_from_link( $redirect_url ); // Works only for default permalinks } /** * Filters the canonical url detected by Polylang * * @since 1.6 * * @param bool|string $redirect_url false or the url to redirect to * @param object $language the language detected */ $redirect_url = apply_filters( 'pll_check_canonical_url', $redirect_url, $language ); // The language is not correctly set so let's redirect to the correct url for this object if ( $do_redirect && $redirect_url && $requested_url != $redirect_url ) { wp_redirect( $redirect_url, 301 ); exit; } return $redirect_url; } } frontend-static-pages.php000066600000021576152132747060011504 0ustar00links_model = &$polylang->links_model; $this->links = &$polylang->links; add_action( 'pll_language_defined', array( $this, 'pll_language_defined' ) ); add_action( 'pll_home_requested', array( $this, 'pll_home_requested' ) ); // Manages the redirection of the homepage add_filter( 'redirect_canonical', array( $this, 'redirect_canonical' ), 10, 2 ); add_filter( 'pll_pre_translation_url', array( $this, 'pll_pre_translation_url' ), 10, 3 ); add_filter( 'pll_check_canonical_url', array( $this, 'pll_check_canonical_url' ) ); add_filter( 'pll_set_language_from_query', array( $this, 'page_on_front_query' ), 10, 2 ); add_filter( 'pll_set_language_from_query', array( $this, 'page_for_posts_query' ), 10, 2 ); } /** * Init the filters * * @since 1.8 */ public function pll_language_defined() { // Translates our page on front and page for posts properties $this->init(); // Translates page for posts and page on front add_filter( 'option_page_on_front', array( $this, 'translate_page_on_front' ) ); add_filter( 'option_page_for_posts', array( $this, 'translate_page_for_posts' ) ); // Support theme customizer if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { add_filter( 'pre_option_page_on_front', 'pll_get_post', 20 ); add_filter( 'pre_option_page_for_post', 'pll_get_post', 20 ); } } /** * Translates the page_id query var when the site root page is requested * * @since 1.8 */ public function pll_home_requested() { set_query_var( 'page_id', $this->curlang->page_on_front ); } /** * Translates page on front * * @since 1.8 * * @param int $v page on front page id * @return int */ public function translate_page_on_front( $v ) { // returns the current page if there is no translation to avoid ugly notices return isset( $this->curlang->page_on_front ) ? $this->curlang->page_on_front : $v; } /** * Manages canonical redirection of the homepage when using page on front * * @since 0.1 * * @param string $redirect_url * @param string $requested_url * @return bool|string modified url, false if redirection is canceled */ public function redirect_canonical( $redirect_url, $requested_url ) { global $wp_query; if ( is_page() && ! is_feed() && isset( $wp_query->queried_object ) && $wp_query->queried_object->ID == $this->curlang->page_on_front ) { $url = is_paged() ? $this->links_model->add_paged_to_link( $this->links->get_home_url(), $wp_query->query_vars['page'] ) : $this->links->get_home_url(); // Don't forget additional query vars $query = parse_url( $redirect_url, PHP_URL_QUERY ); if ( ! empty( $query ) ) { parse_str( $query, $query_vars ); $query_vars = rawurlencode_deep( $query_vars ); // WP encodes query vars values $url = add_query_arg( $query_vars, $url ); } return $url; } return $redirect_url; } /** * Translates the url of the page on front and page for posts * * @since 1.8 * * @param string $url not used * @param object $language language in which we want the translation * @param int $queried_object_id id of the queried object * @return string */ public function pll_pre_translation_url( $url, $language, $queried_object_id ) { if ( ! empty( $queried_object_id ) ) { // Page for posts if ( $GLOBALS['wp_query']->is_posts_page && ( $id = $this->model->post->get( $queried_object_id, $language ) ) ) { $url = get_permalink( $id ); } // Page on front elseif ( is_front_page() && $language->page_on_front && ( $language->page_on_front == $this->model->post->get( $queried_object_id, $language ) ) ) { $url = $language->home_url; } } return $url; } /** * Handles canonical redirection if we are on a static front page * * @since 1.8 * * @param string $redirect_url * @return bool|string */ public function pll_check_canonical_url( $redirect_url ) { if ( ! empty( $this->curlang->page_on_front ) && is_page( $this->curlang->page_on_front ) ) { // Redirect www.mysite.fr to mysite.fr if ( 3 === $this->options['force_lang'] ) { foreach ( $this->options['domains'] as $lang => $domain ) { $host = parse_url( $domain, PHP_URL_HOST ); if ( 'www.' . $_SERVER['HTTP_HOST'] === $host || 'www.' . $host === $_SERVER['HTTP_HOST'] ) { $language = $this->model->get_language( $lang ); return $language->home_url; } } } // Prevents canonical redirection made by WP from secondary language to main language if ( $this->options['redirect_lang'] ) { return false; } } return $redirect_url; } /** * Setups query vars when requesting a static front page * * @since 1.8 * * @param bool|object $lang * @param object $query * @return bool|object */ public function page_on_front_query( $lang, $query ) { if ( ! empty( $lang ) || ! $this->page_on_front ) { return $lang; } // The home page is requested if ( did_action( 'home_requested' ) ) { $query->set( 'page_id', $lang->page_on_front ); } // Redirect the language page to the homepage when using a static front page elseif ( ( $this->options['redirect_lang'] || $this->options['hide_default'] ) && ( count( $query->query ) == 1 || ( ( is_preview() || is_paged() || ! empty( $query->query['page'] ) ) && count( $query->query ) == 2 ) || ( ( is_preview() && ( is_paged() || ! empty( $query->query['page'] ) ) ) && count( $query->query ) == 3 ) ) && is_tax( 'language' ) ) { $lang = $this->model->get_language( get_query_var( 'lang' ) ); $query->set( 'page_id', $lang->page_on_front ); $query->is_singular = $query->is_page = true; $query->is_archive = $query->is_tax = false; unset( $query->query_vars['lang'], $query->queried_object ); // Reset queried object } // Fix paged static front page in plain permalinks when Settings > Reading doesn't match the default language elseif ( ! $this->links_model->using_permalinks && count( $query->query ) === 1 && ! empty( $query->query['page'] ) ) { $lang = $this->model->get_language( $this->options['default_lang'] ); $query->set( 'page_id', $lang->page_on_front ); $query->is_singular = $query->is_page = true; $query->is_archive = $query->is_tax = false; unset( $query->query_vars['lang'], $query->queried_object ); // Reset queried object } // Set the language when requesting a static front page else { $page_id = $this->get_page_id( $query ); $languages = $this->model->get_languages_list(); $pages = wp_list_pluck( $languages, 'page_on_front' ); if ( ! empty( $page_id ) && false !== $n = array_search( $page_id, $pages ) ) { $lang = $languages[ $n ]; } } // Fix for page_on_front if ( ( $this->options['force_lang'] < 2 || ! $this->options['redirect_lang'] ) && $this->links_model->using_permalinks && ! empty( $lang ) && isset( $query->query['paged'] ) ) { $query->set( 'page', $query->query['paged'] ); unset( $query->query['paged'] ); } elseif ( ! $this->links_model->using_permalinks && ! empty( $query->query['page'] ) ) { $query->is_paged = true; } return $lang; } /** * Setups query vars when requesting a posts page * * @since 1.8 * * @param bool|object $lang * @param object $query * @return bool|object */ public function page_for_posts_query( $lang, $query ) { if ( empty( $lang ) && $this->page_for_posts ) { $page_id = $this->get_page_id( $query ); if ( ! empty( $page_id ) && in_array( $page_id, $pages = $this->model->get_languages_list( array( 'fields' => 'page_for_posts' ) ) ) ) { // Fill the cache with all pages for posts to avoid one query per page later // The posts_per_page limit is a trick to avoid splitting the query get_posts( array( 'posts_per_page' => 999, 'post_type' => 'page', 'post__in' => $pages, 'lang' => '' ) ); $lang = $this->model->post->get_language( $page_id ); $query->is_singular = $query->is_page = false; $query->is_home = $query->is_posts_page = true; } } return $lang; } /** * Get queried page_id ( if exists ) * If permalinks are used, WordPress does set and use $query->queried_object_id and sets $query->query_vars['page_id'] to 0 * and does set and use $query->query_vars['page_id'] if permalinks are not used :( * * @since 1.5 * * @param object $query instance of WP_Query * @return int page_id */ protected function get_page_id( $query ) { if ( ! empty( $query->query_vars['pagename'] ) && isset( $query->queried_object_id ) ) { return $query->queried_object_id; } if ( isset( $query->query_vars['page_id'] ) ) { return $query->query_vars['page_id']; } return 0; // No page queried } } frontend.php000066600000014267152132747060007121 0ustar00 inherited, reference to Polylang options array * model => inherited, reference to PLL_Model object * links_model => inherited, reference to PLL_Links_Model object * links => reference to PLL_Links object * static_pages => reference to PLL_Frontend_Static_Pages object * filters_links => inherited, reference to PLL_Frontend_Filters_Links object * choose_lang => reference to PLL_Choose_lang object * curlang => current language * filters => reference to PLL_Filters object * filters_search => reference to PLL_Frontend_Filters_Search object * nav_menu => reference to PLL_Frontend_Nav_Menu object * auto_translate => optional, reference to PLL_Auto_Translate object * * @since 1.2 */ class PLL_Frontend extends PLL_Base { public $curlang; public $links, $choose_lang, $filters, $filters_search, $nav_menu, $auto_translate; /** * constructor * * @since 1.2 * * @param object $links_model */ public function __construct( &$links_model ) { parent::__construct( $links_model ); add_action( 'pll_language_defined', array( $this, 'pll_language_defined' ), 1 ); // avoids the language being the queried object when querying multiple taxonomies add_action( 'parse_tax_query', array( $this, 'parse_tax_query' ), 1 ); // filters posts by language add_action( 'parse_query', array( $this, 'parse_query' ), 6 ); // not before 'check_canonical_url' if ( ! defined( 'PLL_AUTO_TRANSLATE' ) || PLL_AUTO_TRANSLATE ) { add_action( 'template_redirect', array( $this, 'auto_translate' ), 7 ); } } /** * setups the language chooser based on options * * @since 1.2 */ public function init() { $this->links = new PLL_Frontend_Links( $this ); // Static front page and page for posts if ( 'page' === get_option( 'show_on_front' ) ) { $this->static_pages = new PLL_Frontend_Static_Pages( $this ); } // setup the language chooser $c = array( 'Content', 'Url', 'Url', 'Domain' ); $class = 'PLL_Choose_Lang_' . $c[ $this->options['force_lang'] ]; $this->choose_lang = new $class( $this ); $this->choose_lang->init(); // need to load nav menu class early to correctly define the locations in the customizer when the language is set from the content $this->nav_menu = new PLL_Frontend_Nav_Menu( $this ); } /** * setups filters and nav menus once the language has been defined * * @since 1.2 */ public function pll_language_defined() { // filters $this->filters_links = new PLL_Frontend_Filters_Links( $this ); $this->filters = new PLL_Frontend_Filters( $this ); $this->filters_search = new PLL_Frontend_Filters_Search( $this ); } /** * when querying multiple taxonomies, makes sure that the language is not the queried object * * @since 1.8 * * @param object $query WP_Query object */ public function parse_tax_query( $query ) { $pll_query = new PLL_Query( $query, $this->model ); $queried_taxonomies = $pll_query->get_queried_taxonomies(); if ( ! empty( $queried_taxonomies ) && 'language' == reset( $queried_taxonomies ) ) { $query->tax_query->queried_terms['language'] = array_shift( $query->tax_query->queried_terms ); } } /** * modifies some query vars to "hide" that the language is a taxonomy and avoid conflicts * * @since 1.2 * * @param object $query WP_Query object */ public function parse_query( $query ) { $qv = $query->query_vars; $pll_query = new PLL_Query( $query, $this->model ); $taxonomies = $pll_query->get_queried_taxonomies(); // Allow filtering recent posts and secondary queries by the current language if ( ! empty( $this->curlang ) ) { $pll_query->filter_query( $this->curlang ); } // modifies query vars when the language is queried if ( ! empty( $qv['lang'] ) || ( ! empty( $taxonomies ) && array( 'language' ) == array_values( $taxonomies ) ) ) { // do we query a custom taxonomy? $taxonomies = array_diff( $taxonomies , array( 'language', 'category', 'post_tag' ) ); // remove pages query when the language is set unless we do a search // take care not to break the single page, attachment and taxonomies queries! if ( empty( $qv['post_type'] ) && ! $query->is_search && ! $query->is_page && ! $query->is_attachment && empty( $taxonomies ) ) { $query->set( 'post_type', 'post' ); } // unset the is_archive flag for language pages to prevent loading the archive template // keep archive flag for comment feed otherwise the language filter does not work if ( empty( $taxonomies ) && ! $query->is_comment_feed && ! $query->is_post_type_archive && ! $query->is_date && ! $query->is_author && ! $query->is_category && ! $query->is_tag ) { $query->is_archive = false; } // unset the is_tax flag except if another custom tax is queried if ( empty( $taxonomies ) && ($query->is_category || $query->is_tag || $query->is_author || $query->is_post_type_archive || $query->is_date || $query->is_search || $query->is_feed ) ) { $query->is_tax = false; unset( $query->queried_object ); // FIXME useless? } } } /** * auto translate posts and terms ids * * @since 1.2 */ public function auto_translate() { $this->auto_translate = new PLL_Frontend_Auto_Translate( $this ); } /** * resets some variables when switching blog * overrides parent method * * @since 1.5.1 * * @param int $new_blog * @param int $old_blog */ public function switch_blog( $new_blog, $old_blog ) { // need to check that some languages are defined when user is logged in, has several blogs, some without any languages if ( parent::switch_blog( $new_blog, $old_blog ) && did_action( 'pll_language_defined' ) && $this->model->get_languages_list() ) { static $restore_curlang; if ( empty( $restore_curlang ) ) { $restore_curlang = $this->curlang->slug; // to always remember the current language through blogs } $lang = $this->model->get_language( $restore_curlang ); $this->curlang = $lang ? $lang : $this->model->get_language( $this->options['default_lang'] ); if ( isset( $this->static_pages ) ) { $this->static_pages->init(); } $this->load_strings_translations(); } } } frontend-auto-translate.php000066600000016532152132747060012057 0ustar00model = &$polylang->model; $this->curlang = &$polylang->curlang; add_action( 'pre_get_posts', array( $this, 'pre_get_posts' ) ); // after main Polylang filter add_filter( 'get_terms_args', array( $this, 'get_terms_args' ), 10, 2 ); } /** * helper function to get the translated post in the current language * * since 1.8 * * @param int $post_id * @return int */ protected function get_post( $post_id ) { return $this->model->post->get( $post_id, $this->curlang ); } /** * helper function to get the translated term in the current language * * since 1.8 * * @param int $term_id * @return int */ protected function get_term( $term_id ) { return $this->model->term->get( $term_id, $this->curlang ); } /** * filters posts query to automatically translate included ids * * @since 1.1 * * @param object $query WP_Query object */ public function pre_get_posts( $query ) { global $wpdb; $qv = &$query->query_vars; if ( $query->is_main_query() || isset( $qv['lang'] ) || ( ! empty( $qv['post_type'] ) && ! $this->model->is_translated_post_type( $qv['post_type'] ) ) ) { return; } // /!\ always keep untranslated as is // term ids separated by a comma $arr = array(); if ( ! empty( $qv['cat'] ) ) { foreach ( explode( ',', $qv['cat'] ) as $cat ) { $tr = $this->get_term( abs( $cat ) ); $arr[] = $cat < 0 ? -$tr : $tr; } $qv['cat'] = implode( ',', $arr ); } // category_name $arr = array(); if ( ! empty( $qv['category_name'] ) ) { foreach ( explode( ',', $qv['category_name'] ) as $slug ) { $arr[] = ( ( $cat = wpcom_vip_get_category_by_slug( $slug ) ) && ( $tr_id = $this->get_term( $cat->term_id ) ) && ! is_wp_error( $tr = get_category( $tr_id ) ) ) ? $tr->slug : $slug; } $qv['category_name'] = implode( ',', $arr ); } // array of term ids foreach ( array( 'category__and', 'category__in', 'category__not_in', 'tag__and', 'tag__in', 'tag__not_in' ) as $key ) { $arr = array(); if ( ! empty( $qv[ $key ] ) ) { foreach ( $qv[ $key ] as $cat ) { $arr[] = ( $tr = $this->get_term( $cat ) ) ? $tr : $cat; } $qv[ $key ] = $arr; } } // tag $arr = array(); if ( ! empty( $qv['tag'] ) ) { $sep = strpos( $qv['tag'], ',' ) !== false ? ',' : '+'; // two possible separators for tag slugs foreach ( explode( $sep, $qv['tag'] ) as $slug ) { $arr[] = ( ( $tag = wpcom_vip_get_term_by( 'slug', $slug, 'post_tag' ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_tag( $tr_id ) ) ) ? $tr->slug : $slug; } $qv['tag'] = implode( $sep, $arr ); } // tag_id can only take one id if ( ! empty( $qv['tag_id'] ) && $tr_id = $this->get_term( $qv['tag_id'] ) ) { $qv['tag_id'] = $tr_id; } // array of tag slugs foreach ( array( 'tag_slug__and', 'tag_slug__in' ) as $key ) { $arr = array(); if ( ! empty( $qv[ $key ] ) ) { foreach ( $qv[ $key ] as $slug ) { $arr[] = ( ( $tag = wpcom_vip_get_term_by( 'slug', $slug, 'post_tag' ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_tag( $tr_id ) ) ) ? $tr->slug : $slug; } $qv[ $key ] = $arr; } } // custom taxonomies // according to codex, this type of query is deprecated as of WP 3.1 but it does not appear in WP 3.5 source code foreach ( array_intersect( $this->model->get_translated_taxonomies(), get_taxonomies( array( '_builtin' => false ) ) ) as $taxonomy ) { $tax = get_taxonomy( $taxonomy ); $arr = array(); if ( ! empty( $qv[ $tax->query_var ] ) ) { $sep = strpos( $qv[ $tax->query_var ], ',' ) !== false ? ',' : '+'; // two possible separators foreach ( explode( $sep, $qv[ $tax->query_var ] ) as $slug ) { $arr[] = ( ( $tag = wpcom_vip_get_term_by( 'slug', $slug, $taxonomy ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_term( $tr_id, $taxonomy ) ) ) ? $tr->slug : $slug; } $qv[ $tax->query_var ] = implode( $sep, $arr ); } } // tax_query since WP 3.1 if ( ! empty( $qv['tax_query'] ) && is_array( $qv['tax_query'] ) ) { $qv['tax_query'] = $this->translate_tax_query_recursive( $qv['tax_query'] ); } // p, page_id, post_parent can only take one id foreach ( array( 'p', 'page_id', 'post_parent' ) as $key ) { if ( ! empty( $qv[ $key ] ) && $tr_id = $this->get_post( $qv[ $key ] ) ) { $qv[ $key ] = $tr_id; } } // name, pagename can only take one slug foreach ( array( 'name', 'pagename' ) as $key ) { if ( ! empty( $qv[ $key ] ) ) { // no function to get post by name except get_posts itself $post_type = empty( $qv['post_type'] ) ? 'post' : $qv['post_type']; $id = $wpdb->get_var( $wpdb->prepare( "SELECT ID from $wpdb->posts WHERE post_type=%s AND post_name=%s", $post_type, $qv[ $key ] ) ); $qv[ $key ] = ( $id && ( $tr_id = $this->get_post( $id ) ) && $tr = get_post( $tr_id ) ) ? $tr->post_name : $qv[ $key ]; } } // array of post ids // post_parent__in & post_parent__not_in since WP 3.6 foreach ( array( 'post__in', 'post__not_in', 'post_parent__in', 'post_parent__not_in' ) as $key ) { $arr = array(); if ( ! empty( $qv[ $key ] ) ) { // post__in used by the 2 functions below // useless to filter them as output is already in the right language and would result in performance loss foreach ( debug_backtrace() as $trace ) { if ( in_array( $trace['function'], array( 'wp_nav_menu', 'gallery_shortcode' ) ) ) { return; } } foreach ( $qv[ $key ] as $p ) { $arr[] = ( $tr = $this->get_post( $p ) ) ? $tr : $p; } $qv[ $key ] = $arr; } } } /** * filters terms query to automatically translate included ids * * @since 1.1.1 * * @param array $args * @param array $taxonomies * @return array modified $args */ public function get_terms_args( $args, $taxonomies ) { if ( ! empty( $args['include'] ) && $this->model->is_translated_taxonomy( $taxonomies ) ) { foreach ( wp_parse_id_list( $args['include'] ) as $id ) { $arr[] = ( $tr = $this->get_term( $id ) ) ? $tr : $id; } $args['include'] = $arr; } return $args; } /** * translates tax queries * compatible with nested tax queries introduced in WP 4.1 * * @since 1.7 * * @param array $tax_queries * @return array translated tax queries */ protected function translate_tax_query_recursive( $tax_queries ) { foreach ( $tax_queries as $key => $q ) { if ( isset( $q['taxonomy'], $q['terms'] ) && $this->model->is_translated_taxonomy( $q['taxonomy'] ) ) { $arr = array(); $field = isset( $q['field'] ) && in_array( $q['field'], array( 'slug', 'name' ) ) ? $q['field'] : 'term_id'; foreach ( (array) $q['terms'] as $t ) { $arr[] = ( ( $tag = wpcom_vip_get_term_by( $field, $t, $q['taxonomy'] ) ) && ( $tr_id = $this->get_term( $tag->term_id ) ) && ! is_wp_error( $tr = get_term( $tr_id, $q['taxonomy'] ) ) ) ? $tr->$field : $t; } $tax_queries[ $key ]['terms'] = $arr; } // nested queries elseif ( is_array( $q ) ) { $tax_queries[ $key ] = $this->translate_tax_query_recursive( $q ); } } return $tax_queries; } } frontend-filters-search.php000066600000010274152132747060012024 0ustar00links_model = &$polylang->links_model; $this->curlang = &$polylang->curlang; // adds the language information in the search form // low priority in case the search form is created using the same filter as described in http://codex.wordpress.org/Function_Reference/get_search_form add_filter( 'get_search_form', array( $this, 'get_search_form' ), 99 ); // adds the language information in admin bar search form add_action( 'add_admin_bar_menus', array( $this, 'add_admin_bar_menus' ) ); // adds javascript at the end of the document // was used for WP < 3.6. kept just in case if ( defined( 'PLL_SEARCH_FORM_JS' ) && PLL_SEARCH_FORM_JS ) { add_action( 'wp_footer', array( $this, 'wp_print_footer_scripts' ) ); } } /** * adds the language information in the search form * does not work if searchform.php ( prior to WP 3.6 ) is used or if the search form is hardcoded in another template file * * @since 0.1 * * @param string $form search form * @return string modified search form */ public function get_search_form( $form ) { if ( $form ) { if ( $this->links_model->using_permalinks ) { // take care to modify only the url in the
tag preg_match( '##', $form, $matches ); $old = reset( $matches ); $new = preg_replace( '#' . esc_url( $this->links_model->home ) . '\/?#', esc_url( $this->curlang->search_url ), $old ); $form = str_replace( $old, $new, $form ); } else { $form = str_replace( '', '', $form ); } } return $form; } /** * adds the language information in admin bar search form * * @since 1.2 */ function add_admin_bar_menus() { remove_action( 'admin_bar_menu', 'wp_admin_bar_search_menu', 4 ); add_action( 'admin_bar_menu', array( $this, 'admin_bar_search_menu' ), 4 ); } /** * rewrites the admin bar search form to pass our get_search form filter. See #21342 * code base is WP 4.3.1 * * @since 0.9 * * @param object $wp_admin_bar */ public function admin_bar_search_menu( $wp_admin_bar ) { $form = '
'; $form .= ''; $form .= ''; $form .= ''; $form .= '
'; $wp_admin_bar->add_menu( array( 'parent' => 'top-secondary', 'id' => 'search', 'title' => $this->get_search_form( $form ), // pass the get_search_form filter 'meta' => array( 'class' => 'admin-bar-search', 'tabindex' => -1 ), ) ); } /** * allows modifying the search form if it does not pass get_search_form * * @since 0.1 */ public function wp_print_footer_scripts() { // don't use directly e[0] just in case there is somewhere else an element named 's' // check before if the hidden input has not already been introduced by get_search_form ( FIXME: is there a way to improve this ) ? // thanks to AndyDeGroo for improving the code for compatility with old browsers // http://wordpress.org/support/topic/development-of-polylang-version-08?replies=6#post-2645559 $lang = esc_js( $this->curlang->slug ); $js = "//"; echo ''; } } choose-lang.php000066600000025306152132747060007475 0ustar00links_model = &$polylang->links_model; $this->model = &$polylang->model; $this->options = &$polylang->options; $this->curlang = &$polylang->curlang; } /** * sets the language for ajax requests * and setup actions * any child class must call this method if it overrides it * * @since 1.8 */ public function init() { if ( PLL_AJAX_ON_FRONT || false === stripos( $_SERVER['SCRIPT_FILENAME'], 'index.php' ) ) { $this->set_language( empty( $_REQUEST['lang'] ) ? $this->get_preferred_language() : $this->model->get_language( $_REQUEST['lang'] ) ); } add_action( 'pre_comment_on_post', array( $this, 'pre_comment_on_post' ) ); // sets the language of comment add_action( 'parse_query', array( $this, 'parse_main_query' ), 2 ); // sets the language in special cases add_action( 'wp', array( $this, 'maybe_setcookie' ), 7 ); } /** * writes language cookie * loads user defined translations * fires the action 'pll_language_defined' * * @since 1.2 * * @param object $curlang current language */ protected function set_language( $curlang ) { // don't set the language a second time if ( isset( $this->curlang ) ) { return; } // final check in case $curlang has an unexpected value // see https://wordpress.org/support/topic/detect-browser-language-sometimes-setting-null-language $this->curlang = ( $curlang instanceof PLL_Language ) ? $curlang : $this->model->get_language( $this->options['default_lang'] ); $GLOBALS['text_direction'] = $this->curlang->is_rtl ? 'rtl' : 'ltr'; /** * Fires when the current language is defined * * @since 0.9.5 * * @param string $slug current language code * @param object $curlang current language object */ do_action( 'pll_language_defined', $this->curlang->slug, $this->curlang ); } /** * set a cookie to remember the language. * possibility to set PLL_COOKIE to false will disable cookie although it will break some functionalities * * @since 1.5 */ public function maybe_setcookie() { // check headers have not been sent to avoid ugly error // cookie domain must be set to false for localhost ( default value for COOKIE_DOMAIN ) thanks to Stephen Harris. if ( ! headers_sent() && PLL_COOKIE !== false && ! empty( $this->curlang ) && ( ! isset( $_COOKIE[ PLL_COOKIE ] ) || $_COOKIE[ PLL_COOKIE ] != $this->curlang->slug ) && ! is_404() ) { /** * Filter the Polylang cookie duration * * @since 1.8 * * @param int $duration cookie duration in seconds */ $expiration = apply_filters( 'pll_cookie_expiration', YEAR_IN_SECONDS ); setcookie( PLL_COOKIE, $this->curlang->slug, time() + $expiration, COOKIEPATH, 2 == $this->options['force_lang'] ? parse_url( $this->links_model->home, PHP_URL_HOST ) : COOKIE_DOMAIN, is_ssl() ); } } /** * get the preferred language according to the browser preferences * code adapted from http://www.thefutureoftheweb.com/blog/use-accept-language-header * * @since 1.8 * * @return string|bool the preferred language slug or false */ public function get_preferred_browser_language() { $accept_langs = array(); if ( isset( $_SERVER['HTTP_ACCEPT_LANGUAGE'] ) ) { // break up string into pieces ( languages and q factors ) preg_match_all( '/([a-z]{1,8}(-[a-z]{1,8})?)\s*(;\s*q\s*=\s*( 1|0\.[0-9]+))?/i', $_SERVER['HTTP_ACCEPT_LANGUAGE'], $lang_parse ); $k = $lang_parse[1]; $v = $lang_parse[4]; if ( $n = count( $k ) ) { // set default to 1 for any without q factor foreach ( $v as $key => $val ) { if ( '' === $val ) { $v[ $key ] = 1; } } // bubble sort ( need a stable sort for Android, so can't use a PHP sort function ) if ( $n > 1 ) { for ( $i = 2; $i <= $n; $i++ ) { for ( $j = 0; $j <= $n - 2; $j++ ) { if ( $v[ $j ] < $v[ $j + 1 ] ) { // swap values $temp = $v[ $j ]; $v[ $j ] = $v[ $j + 1 ]; $v[ $j + 1 ] = $temp; // Swap keys $temp = $k[ $j ]; $k[ $j ] = $k[ $j + 1 ]; $k[ $j + 1 ] = $temp; } } } } $accept_langs = array_combine( $k,$v ); } } $languages = $this->model->get_languages_list( array( 'hide_empty' => true ) ); // hides languages with no post /** * Filter the list of languages to use to match the browser preferences * * @since 1.9.3 * * @param array $languages array of PLL_Language objects */ $languages = apply_filters( 'pll_languages_for_browser_preferences', $languages ); // looks through sorted list and use first one that matches our language list foreach ( array_keys( $accept_langs ) as $accept_lang ) { // first loop to match the exact locale foreach ( $languages as $language ) { if ( 0 === strcasecmp( $accept_lang, $language->get_locale( 'display' ) ) ) { return $language->slug; } } // second loop to match the language set foreach ( $languages as $language ) { if ( 0 === stripos( $accept_lang, $language->slug ) || 0 === stripos( $language->get_locale( 'display' ), $accept_lang ) ) { return $language->slug; } } } return false; } /** * returns the language according to browser preference or the default language * * @since 0.1 * * @return object browser preferred language or default language */ public function get_preferred_language() { // check first if the user was already browsing this site if ( isset( $_COOKIE[ PLL_COOKIE ] ) ) { return $this->model->get_language( $_COOKIE[ PLL_COOKIE ] ); } /** * Filter the visitor's preferred language (normally set first by cookie * if this is not the first visit, then by the browser preferences). * If no preferred language has been found or set by this filter, * Polylang fallbacks to the default language * * @since 1.0 * * @param string $language preferred language code */ $slug = apply_filters( 'pll_preferred_language', $this->options['browser'] ? $this->get_preferred_browser_language() : false ); // return default if there is no preferences in the browser or preferences does not match our languages or it is requested not to use the browser preference return ( $lang = $this->model->get_language( $slug ) ) ? $lang : $this->model->get_language( $this->options['default_lang'] ); } /** * sets the language when home page is resquested * * @since 1.2 */ protected function home_language() { // test referer in case PLL_COOKIE is set to false // thanks to Ov3rfly http://wordpress.org/support/topic/enhance-feature-when-front-page-is-visited-set-language-according-to-browser $language = $this->options['hide_default'] && ( ( isset( $_SERVER['HTTP_REFERER'] ) && in_array( parse_url( $_SERVER['HTTP_REFERER'], PHP_URL_HOST ), $this->links_model->get_hosts() ) ) || ! $this->options['browser'] ) ? $this->model->get_language( $this->options['default_lang'] ) : $this->get_preferred_language(); // sets the language according to browser preference or default language $this->set_language( $language ); } /** * to call when the home page has been requested * make sure to call this after 'setup_theme' has been fired as we need $wp_query * performs a redirection to the home page in the current language if needed * * @since 0.9 */ public function home_requested() { // we are already on the right page if ( $this->options['default_lang'] == $this->curlang->slug && $this->options['hide_default'] ) { $this->set_curlang_in_query( $GLOBALS['wp_query'] ); /** * Fires when the site root page is requested * * @since 1.8 */ do_action( 'pll_home_requested' ); } // redirect to the home page in the right language // test to avoid crash if get_home_url returns something wrong // FIXME why this happens? http://wordpress.org/support/topic/polylang-crashes-1 // don't redirect if $_POST is not empty as it could break other plugins // don't forget the query string which may be added by plugins elseif ( is_string( $redirect = $this->curlang->home_url ) && empty( $_POST ) ) { $redirect = empty( $_SERVER['QUERY_STRING'] ) ? $redirect : $redirect . ( $this->links_model->using_permalinks ? '?' : '&' ) . $_SERVER['QUERY_STRING']; /** * When a visitor reaches the site home, Polylang redirects to the home page in the correct language. * This filter allows plugins to modify the redirected url or prevent this redirection * * @since 1.1.1 * * @param string $redirect the url the visitor will be redirected to */ if ( $redirect = apply_filters( 'pll_redirect_home', $redirect ) ) { wp_redirect( $redirect ); exit; } } } /** * set the language when posting a comment * * @since 0.8.4 * * @param int $post_id the post beeing commented */ public function pre_comment_on_post( $post_id ) { $this->set_language( $this->model->post->get_language( $post_id ) ); } /** * modifies some main query vars for home page and page for posts * to enable one home page ( and one page for posts ) per language * * @since 1.2 * * @param object $query instance of WP_Query */ public function parse_main_query( $query ) { if ( ! $query->is_main_query() ) { return; } /** * This filter allows to set the language based on information contained in the main query * * @since 1.8 * * @param bool|object $lang false or language object * @param object $query WP_Query object */ if ( $lang = apply_filters( 'pll_set_language_from_query', false, $query ) ) { $this->set_language( $lang ); $this->set_curlang_in_query( $query ); } // sets is_home on translated home page when it displays posts // is_home must be true on page 2, 3... too // as well as when searching an empty string: http://wordpress.org/support/topic/plugin-polylang-polylang-breaks-search-in-spun-theme if ( 'posts' == get_option( 'show_on_front' ) && ( count( $query->query ) == 1 || ( is_paged() && count( $query->query ) == 2 ) || ( isset( $query->query['s'] ) && ! $query->query['s'] ) ) && $lang = get_query_var( 'lang' ) ) { $lang = $this->model->get_language( $lang ); $this->set_language( $lang ); // sets the language now otherwise it will be too late to filter sticky posts ! $query->is_home = true; $query->is_archive = $query->is_tax = false; } } /** * Sets the current language in the query * * @since 2.2 * * @param object $query */ protected function set_curlang_in_query( &$query ) { $pll_query = new PLL_Query( $query, $this->model ); $pll_query->set_language( $this->curlang ); } } frontend-nav-menu.php000066600000023052152132747060010635 0ustar00curlang = &$polylang->curlang; // Split the language switcher menu item in several language menu items add_filter( 'wp_get_nav_menu_items', array( $this, 'wp_get_nav_menu_items' ), 20 ); // after the customizer menus add_filter( 'wp_nav_menu_objects', array( $this, 'wp_nav_menu_objects' ) ); add_filter( 'nav_menu_link_attributes', array( $this, 'nav_menu_link_attributes' ), 10, 2 ); // Filters menus by language add_filter( 'theme_mod_nav_menu_locations', array( $this, 'nav_menu_locations' ), 20 ); add_filter( 'wp_nav_menu_args', array( $this, 'wp_nav_menu_args' ) ); // The customizer if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { add_filter( 'wp_nav_menu_args', array( $this, 'filter_args_before_customizer' ) ); add_filter( 'wp_nav_menu_args', array( $this, 'filter_args_after_customizer' ), 2000 ); } } /** * Sort menu items by menu order * * @since 1.7.9 * * @param object $a The first object to compare * @param object $b The second object to compare * @return int -1 or 1 if $a is considered to be respectively less than or greater than $b. */ protected function usort_menu_items( $a, $b ) { return ( $a->menu_order < $b->menu_order ) ? -1 : 1; } /** * Splits the one item of backend in several items on frontend * take care to menu_order as it is used later in wp_nav_menu * * @since 1.1.1 * * @param array $items menu items * @return array modified items */ public function wp_get_nav_menu_items( $items ) { if ( doing_action( 'customize_register' ) ) { // needed since WP 4.3, doing_action available since WP 3.9 return $items; } // The customizer menus does not sort the items and we need them to be sorted before splitting the language switcher usort( $items, array( $this, 'usort_menu_items' ) ); $new_items = array(); $offset = 0; foreach ( $items as $key => $item ) { if ( $options = get_post_meta( $item->ID, '_pll_menu_item', true ) ) { $i = 0; $switcher = new PLL_Switcher; $args = array_merge( array( 'raw' => 1 ), $options ); $the_languages = $switcher->the_languages( PLL()->links, $args ); // parent item for dropdown if ( ! empty( $options['dropdown'] ) ) { $item->title = $options['show_flags'] && $options['show_names'] ? $this->curlang->flag . ' ' . esc_html( $this->curlang->name ) : ( $options['show_flags'] ? $this->curlang->flag : esc_html( $this->curlang->name ) ); $item->attr_title = ''; $item->classes = array( 'pll-parent-menu-item' ); $new_items[] = $item; $offset++; } foreach ( $the_languages as $lang ) { $lang_item = clone $item; $lang_item->ID = $lang_item->ID . '-' . $lang['slug']; // A unique ID $lang_item->title = $options['show_flags'] && $options['show_names'] ? $lang['flag'] . '' . esc_html( $lang['name'] ) . '' : ( $options['show_flags'] ? $lang['flag'] : esc_html( $lang['name'] ) ); $lang_item->attr_title = ''; $lang_item->url = $lang['url']; $lang_item->lang = $lang['locale']; // Save this for use in nav_menu_link_attributes $lang_item->classes = $lang['classes']; $lang_item->menu_order += $offset + $i++; if ( ! empty( $options['dropdown'] ) ) { $lang_item->menu_item_parent = $item->db_id; $lang_item->db_id = 0; // to avoid recursion } $new_items[] = $lang_item; } $offset += $i - 1; } else { $item->menu_order += $offset; $new_items[] = $item; } } return $new_items; } /** * Returns the ancestors of a menu item * * @since 1.1.1 * * @param object $item * @return array ancestors ids */ public function get_ancestors( $item ) { $ids = array(); $_anc_id = (int) $item->db_id; while ( ( $_anc_id = get_post_meta( $_anc_id, '_menu_item_menu_item_parent', true ) ) && ! in_array( $_anc_id, $ids ) ) { $ids[] = $_anc_id; } return $ids; } /** * Removes current-menu and current-menu-ancestor classes to lang switcher when not on the home page * * @since 1.1.1 * * @param array $items * @return array modified menu items */ public function wp_nav_menu_objects( $items ) { $r_ids = $k_ids = array(); foreach ( $items as $item ) { if ( ! empty( $item->classes ) && is_array( $item->classes ) ) { if ( in_array( 'current-lang', $item->classes ) ) { $item->current = false; $item->classes = array_diff( $item->classes, array( 'current-menu-item' ) ); $r_ids = array_merge( $r_ids, $this->get_ancestors( $item ) ); // Remove the classes for these ancestors } elseif ( in_array( 'current-menu-item', $item->classes ) ) { $k_ids = array_merge( $k_ids, $this->get_ancestors( $item ) ); // Keep the classes for these ancestors } } } $r_ids = array_diff( $r_ids, $k_ids ); foreach ( $items as $item ) { if ( ! empty( $item->db_id ) && in_array( $item->db_id, $r_ids ) ) { $item->classes = array_diff( $item->classes, array( 'current-menu-ancestor', 'current-menu-parent', 'current_page_parent', 'current_page_ancestor' ) ); } } return $items; } /** * Adds hreflang attribute for the language switcher menu items * available since WP 3.6 * * @since 1.1 * * @param array $atts * @param object $item * @return array modified $atts */ public function nav_menu_link_attributes( $atts, $item ) { if ( isset( $item->lang ) ) { $atts['lang'] = $atts['hreflang'] = esc_attr( $item->lang ); } return $atts; } /** * Fills the theme nav menus locations with the right menu in the right language * Needs to wait for the language to be defined * * @since 1.2 * * @param array|bool $menus list of nav menus locations, false if menu locations have not been filled yet * @return array|bool modified list of nav menus locations */ public function nav_menu_locations( $menus ) { if ( is_array( $menus ) && ! empty( $this->curlang ) ) { // First get multilingual menu locations from DB $theme = get_option( 'stylesheet' ); foreach ( $menus as $loc => $menu ) { $menus[ $loc ] = empty( $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ] ) ? 0 : $this->options['nav_menus'][ $theme ][ $loc ][ $this->curlang->slug ]; } // Support for theme customizer // Let's look for multilingual menu locations directly in $_POST as there are not in customizer object if ( isset( $_POST['wp_customize'], $_POST['customized'] ) ) { $customized = json_decode( wp_unslash( $_POST['customized'] ) ); if ( is_object( $customized ) ) { foreach ( $customized as $key => $c ) { if ( false !== strpos( $key, 'nav_menu_locations[' ) ) { $loc = substr( trim( $key, ']' ), 19 ); $infos = $this->explode_location( $loc ); if ( $infos['lang'] == $this->curlang->slug ) { $menus[ $infos['location'] ] = $c; } elseif ( $this->curlang->slug == $this->options['default_lang'] ) { $menus[ $loc ] = $c; } } } } } } return $menus; } /** * Attempt to translate the nav menu when it is hardcoded or when no location is defined in wp_nav_menu * * @since 1.7.10 * * @param array $args * @return array modified $args */ public function wp_nav_menu_args( $args ) { $theme = get_option( 'stylesheet' ); if ( empty( $this->curlang ) || empty( $this->options['nav_menus'][ $theme ] ) ) { return $args; } // Get the nav menu based on the requested menu $menu = wp_get_nav_menu_object( $args['menu'] ); // Attempt to find a translation of this menu // This obviously does not work if the nav menu has no associated theme location if ( $menu ) { foreach ( $this->options['nav_menus'][ $theme ] as $menus ) { if ( in_array( $menu->term_id, $menus ) && ! empty( $menus[ $this->curlang->slug ] ) ) { $args['menu'] = $menus[ $this->curlang->slug ]; return $args; } } } // Get the first menu that has items and and is in the current language if we still can't find a menu if ( ! $menu && ! $args['theme_location'] ) { $menus = wp_get_nav_menus(); foreach ( $menus as $menu_maybe ) { if ( $menu_items = wp_get_nav_menu_items( $menu_maybe->term_id, array( 'update_post_term_cache' => false ) ) ) { foreach ( $this->options['nav_menus'][ $theme ] as $menus ) { if ( in_array( $menu_maybe->term_id, $menus ) && ! empty( $menus[ $this->curlang->slug ] ) ) { $args['menu'] = $menus[ $this->curlang->slug ]; return $args; } } } } } return $args; } /** * Filters the nav menu location before the customizer so that it matches the temporary location in the customizer * * @since 1.8 * * @param array $args wp_nav_menu $args * @return array modified $args */ public function filter_args_before_customizer( $args ) { if ( ! empty( $this->curlang ) ) { $args['theme_location'] = $this->combine_location( $args['theme_location'], $this->curlang ); } return $args; } /** * Filters the nav menu location after the customizer to get back the true nav menu location for the theme * * @since 1.8 * * @param array $args wp_nav_menu $args * @return array modified $args */ public function filter_args_after_customizer( $args ) { $infos = $this->explode_location( $args['theme_location'] ); $args['theme_location'] = $infos['location']; return $args; } } choose-lang-url.php000066600000006724152132747060010300 0ustar00set_language_from_url(); } add_action( 'request', array( $this, 'request' ) ); } /** * finds the language according to information found in the url * * @since 1.2 */ public function set_language_from_url() { $host = str_replace( 'www.', '', parse_url( $this->links_model->home, PHP_URL_HOST ) ); $home_path = parse_url( $this->links_model->home, PHP_URL_PATH ); $requested_host = str_replace( 'www.', '', $_SERVER['HTTP_HOST'] ); $requested_uri = rtrim( str_replace( $this->index, '', $_SERVER['REQUEST_URI'] ), '/' ); // some PHP setups turn requests for / into /index.php in REQUEST_URI // home is resquested if ( $requested_host == $host && $requested_uri == $home_path && empty( $_SERVER['QUERY_STRING'] ) ) { $this->home_language(); add_action( 'setup_theme', array( $this, 'home_requested' ) ); } // take care to post & page preview http://wordpress.org/support/topic/static-frontpage-url-parameter-url-language-information elseif ( isset( $_GET['preview'] ) && ( ( isset( $_GET['p'] ) && $id = (int) $_GET['p'] ) || ( isset( $_GET['page_id'] ) && $id = (int) $_GET['page_id'] ) ) ) { $curlang = ( $lg = $this->model->post->get_language( $id ) ) ? $lg : $this->model->get_language( $this->options['default_lang'] ); } // take care to ( unattached ) attachments elseif ( isset( $_GET['attachment_id'] ) && $id = (int) $_GET['attachment_id'] ) { $curlang = ( $lg = $this->model->post->get_language( $id ) ) ? $lg : $this->get_preferred_language(); } elseif ( $slug = $this->links_model->get_language_from_url() ) { $curlang = $this->model->get_language( $slug ); } elseif ( $this->options['hide_default'] ) { $curlang = $this->model->get_language( $this->options['default_lang'] ); } // if no language found, check_language_code_in_url will attempt to find one and redirect to the correct url // otherwise 404 will be fired in the preferred language $this->set_language( empty( $curlang ) ? $this->get_preferred_language() : $curlang ); } /** * adds the current language in query vars * useful for subdomains and multiple domains * * @since 1.8 * * @param array $qv main request query vars * @return array modified query vars */ public function request( $qv ) { // FIXME take care not to break untranslated content // FIXME media ? // untranslated post types if ( isset( $qv['post_type'] ) && ! $this->model->is_translated_post_type( $qv['post_type'] ) ) { return $qv; } // untranslated taxonomies $tax_qv = array_filter( wp_list_pluck( get_taxonomies( array(), 'objects' ), 'query_var' ) ); // get all taxonomies query vars $tax_qv = array_intersect( $tax_qv, array_keys( $qv ) ); // get all queried taxonomies query vars if ( ! $this->model->is_translated_taxonomy( array_keys( $tax_qv ) ) ) { return $qv; } if ( isset( $this->curlang ) && empty( $qv['lang'] ) ) { $qv['lang'] = $this->curlang->slug; } return $qv; } } frontend-links.php000066600000014623152132747060010233 0ustar00curlang = &$polylang->curlang; $this->cache = new PLL_Cache(); } /** * returns the url of the translation ( if exists ) of the current page * * @since 0.1 * * @param object $language * @return string */ public function get_translation_url( $language ) { global $wp_query; if ( false !== $translation_url = $this->cache->get( 'translation_url:' . $language->slug ) ) { return $translation_url; } // make sure that we have the queried object // see https://wordpress.org/support/topic/patch-for-fixing-a-notice $queried_object_id = $wp_query->get_queried_object_id(); /** * Filter the translation url before Polylang attempts to find one * Internally used by Polylang for the static front page and posts page * * @since 1.8 * * @param string $url empty or the url of the translation of teh current page * @param object $language language of the translation * @param int $queried_object_id queried object id */ if ( ! $url = apply_filters( 'pll_pre_translation_url', '', $language, $queried_object_id ) ) { $qv = $wp_query->query_vars; $hide = $this->options['default_lang'] == $language->slug && $this->options['hide_default']; // post and attachment if ( is_single() && ( $this->options['media_support'] || ! is_attachment() ) && ( $id = $this->model->post->get( $queried_object_id, $language ) ) && $this->current_user_can_read( $id ) ) { $url = get_permalink( $id ); } // page elseif ( is_page() && ( $id = $this->model->post->get( $queried_object_id, $language ) ) && $this->current_user_can_read( $id ) ) { $url = get_page_link( $id ); } elseif ( is_search() ) { $url = $this->get_archive_url( $language ); // special case for search filtered by translated taxonomies: taxonomy terms are translated in the translation url if ( ! empty( $wp_query->tax_query->queries ) ) { foreach ( $wp_query->tax_query->queries as $tax_query ) { if ( ! empty( $tax_query['taxonomy'] ) && $this->model->is_translated_taxonomy( $tax_query['taxonomy'] ) ) { $tax = get_taxonomy( $tax_query['taxonomy'] ); $terms = get_terms( $tax->name, array( 'fields' => 'id=>slug' ) ); // filtered by current language foreach ( $tax_query['terms'] as $slug ) { $term_id = array_search( $slug, $terms ); // what is the term_id corresponding to taxonomy term? if ( $term_id && $term_id = $this->model->term->get_translation( $term_id, $language ) ) { // get the translated term_id $term = get_term( $term_id, $tax->name ); $url = str_replace( $slug, $term->slug, $url ); } } } } } } // translated taxonomy // take care that is_tax() is false for categories and tags elseif ( ( is_category() || is_tag() || is_tax() ) && ( $term = get_queried_object() ) && $this->model->is_translated_taxonomy( $term->taxonomy ) ) { $lang = $this->model->term->get_language( $term->term_id ); if ( ! $lang || $language->slug == $lang->slug ) { $url = wpcom_vip_get_term_link( $term, $term->taxonomy ); // self link } elseif ( $tr_id = $this->model->term->get_translation( $term->term_id, $language ) ) { $tr_term = get_term( $tr_id, $term->taxonomy ); // check if translated term ( or children ) have posts if ( $tr_term && ( $tr_term->count || ( is_taxonomy_hierarchical( $term->taxonomy ) && array_sum( wp_list_pluck( get_terms( $term->taxonomy, array( 'child_of' => $tr_term->term_id, 'lang' => $language->slug ) ), 'count' ) ) ) ) ) { $url = wpcom_vip_get_term_link( $tr_term, $term->taxonomy ); } } } // post type archive elseif ( is_post_type_archive() ) { if ( $this->model->is_translated_post_type( $qv['post_type'] ) && $this->model->count_posts( $language, array( 'post_type' => $qv['post_type'] ) ) ) { $url = $this->get_archive_url( $language ); } } elseif ( is_archive() ) { $keys = array( 'post_type', 'm', 'year', 'monthnum', 'day', 'author', 'author_name' ); $keys = array_merge( $keys, $this->model->get_filtered_taxonomies_query_vars() ); // check if there are existing translations before creating the url if ( $this->model->count_posts( $language, array_intersect_key( $qv, array_flip( $keys ) ) ) ) { $url = $this->get_archive_url( $language ); } } // front page when it is the list of posts elseif ( is_front_page() ) { $url = $this->get_home_url( $language ); } } /** * Filter the translation url of the current page before Polylang caches it * * @since 1.1.2 * * @param null|string $url the translation url, null if none was found * @param string $language the language code of the translation */ $translation_url = apply_filters( 'pll_translation_url', ( isset( $url ) && ! is_wp_error( $url ) ? $url : null ), $language->slug ); $this->cache->set( 'translation_url:' . $language->slug, $translation_url ); return $translation_url; } /** * get the translation of the current archive url * used also for search * * @since 1.2 * * @param object $language * @return string */ public function get_archive_url( $language ) { $url = ( is_ssl() ? 'https://' : 'http://' ) . $_SERVER['HTTP_HOST'] . $_SERVER['REQUEST_URI']; $url = $this->links_model->switch_language_in_link( $url, $language ); $url = $this->links_model->remove_paged_from_link( $url ); /** * Filter the archive url * * @since 1.6 * * @param string $url url of the archive * @param object $language language of the archive */ return apply_filters( 'pll_get_archive_url', $url, $language ); } /** * returns the home url in the right language * * @since 0.1 * * @param object $language Optional, defaults to current language * @param bool $is_search Optional, whether we need the home url for a search form, defaults to false */ public function get_home_url( $language = '', $is_search = false ) { if ( empty( $language ) ) { $language = $this->curlang; } return parent::get_home_url( $language, $is_search ); } }