*/ class Falang_Public extends Falang_Rewrite{ /** * @var boolean */ var $canonical = true; private $cache; /* Variable use to store the filter loaded. * @since 1.3.42 */ private $filters = array(); /** * Initialize the class and set its properties. * * @since 1.0.0 * @param string $plugin_name The name of the plugin. * @param string $version The version of this plugin. */ public function __construct( $plugin_name, $version ) { parent::__construct($plugin_name, $version); $this->cache = new Cache(); //change the locale language, based on the lang pass on the url. add_filter('locale', array($this, 'get_locale')); add_action('plugins_loaded', array($this, 'load')); //redirect for autotetect sooner add_action('setup_theme', array($this, 'setup_theme')); } /** * add _set method to fix PHP 8.2 dynamic field deprecated * * @since 1.3.42 * */ public function __set($property, $value): void { $this->filters[$property] = $value; } /** * @from 1.3.18 * redirect the home page when auto-detect language is done before the template is loaded. */ public function setup_theme() { //front don't work with wordpress in subfolder //is_fornt_page don't work too $front = $_SERVER["REQUEST_URI"] == '/' ? true:false; if ($front && $this->model->get_option('autodetect') && $this->model->get_option('show_slug')){ //only on home page $language = $this->request_current_language(); if (!$this->canonical) { $url = home_url(); //add language slug to url to make the redirect if (get_option('permalink_structure')) { $url = $url . '/' . $language->slug . '/'; } else { $url = add_query_arg( array('lang' => $language->slug), $url); } wp_redirect($url); exit; } } } /** * @from 1.0.0 * @since 1.3.13 move WP_Strings translation * @since 1.3.33 add get_term_metadata filter * @since 1.3.35 add theme editor workaround here * @update 1.3.59 move the load_strings_translations to init * @update 1.3.60 pub back the load_strings_translations to load * @update 1.3.61 call set language to allow WPML ICL_LANGUAGE_CODE and ICL_LANGUAGE_NAME define */ public function load() { //theme_editor don't like falang with home slug - disable falang for theme editor check if (isset($_REQUEST['wp_scrape_key'])){ return; } if ($this->current_language = $this->get_current_language()) { $this->set_language($this->current_language);//allow the action falang_language_defined parent::load(); $this->load_strings_translations($this->current_language->locale); // Strings translation ( must be applied before WordPress applies its default formatting filters ) foreach ( array( 'widget_text', 'widget_title') as $filter ) { add_filter( $filter, array( $this,'falang__'), 1 ); } //use for extra filter nav_menu,enfold... add_action( 'wp_loaded', array( $this, 'add_filters' ), 10 ); // from sublanguage current (parent) add_action('parse_query', array( $this, 'parse_query' ) ); add_filter('get_object_terms', array($this, 'filter_get_object_terms'), 10, 4); add_filter('get_term', array($this, 'translate_get_term'), 10, 2); // hard translate term add_filter('get_terms', array($this, 'translate_get_terms'), 10, 3); // hard translate terms add_filter('get_the_terms', array($this, 'translate_post_terms'), 10, 3); add_filter('get_term_metadata', array($this, 'translate_get_term_metadata'), 10, 5); add_filter('list_cats', array($this, 'translate_term_name'), 10, 2); add_filter('the_posts', array($this, 'translate_the_posts'), 10, 2); add_filter('get_pages', array($this, 'translate_the_pages'), 10, 2); add_filter('falang_query_add_language', array($this, 'query_add_language')); //end of current //filter post by local only current locale from slug (language code) and all language add_filter( 'posts_join', array($this,'posts_join'), 10, 2 ); add_filter( 'posts_where', array($this,'posts_where'), 10, 2 ); add_filter('the_content', array($this, 'translate_post_content'), 7);//change 9 to 8 for display post type plugin working and 7 for embeded add_filter('the_title', array($this, 'translate_post_title'), 10, 2); add_filter('get_the_excerpt', array($this, 'translate_post_excerpt'), 9,2); add_filter('single_post_title', array($this, 'translate_single_post_title'), 10, 2); add_filter('get_post_metadata', array($this, 'translate_meta_data'), 10, 4); add_filter('wp_setup_nav_menu_item', array($this, 'translate_menu_nav_item')); add_filter('wp_nav_menu_objects', array($this, 'filter_nav_menu_objects'), 10, 2); // -> @from 1.0. Filter list for hidden items add_filter('tag_cloud_sort', array($this,'translate_tag_cloud'), 10, 2); add_filter('query_vars', array($this,'query_vars') ); add_action('init', array($this, 'init')); add_action('widgets_init', array($this, 'register_widget')); // Filters the widgets according to the current language add_filter( 'widget_display_callback', array( $this, 'widget_display_callback' ), 10, 2 ); add_filter( 'sidebars_widgets', array( $this, 'sidebars_widgets' ) ); //TODO check translate the post and the page. // add_filter('the_posts', array($this, 'translate_the_posts'), 10, 2); // add_filter('get_pages', array($this, 'translate_the_pages'), 10, 2); $this->add_options_filters(); //translate image caption add_filter('wp_get_attachment_caption', array($this,'translate_attachment_caption'), 10, 2); //add the languge input to the comment form => use for redirect correctly add_action( 'comment_form_logged_in_after', array($this,'comment_form_additional_fields') ); add_action( 'comment_form_after_fields', array($this,'comment_form_additional_fields' )); //fix comment post redirect add_filter( 'comment_post_redirect', array($this,'translate_comment_post_redirect'),10,2) ; //add language in comment meta after the comment is inserted add_action( 'comment_post', array($this,'comment_post'),10,3) ; } } /** * @from 1.0 * @update 1.3.13 call parent init to load WP Strings translation * @update 1.3.57 add action rest_api_init * @update 1.3.59 load_strings_translations is moved from load to init * @update 1.3.60 put back load_strings_translations to load (was not always working in init) */ public function init() { parent::init(); // $this->load_strings_translations($this->current_language->locale); if (get_option('permalink_structure')) { add_filter('query_vars', array($this, 'query_vars')); add_filter('request', array($this, 'catch_translation')); // detect query type and language out of query vars add_action('wp', array($this, 'redirect_uncanonical'), 11); } //link filters only after request has been parsed add_action('parse_request', array($this, 'add_links_translation_filters')); // link filter on rest request because 'parse_request' is not triggered add_action('rest_api_init', array($this, 'add_links_translation_filters')); // login add_filter('login_url', array($this, 'translate_login_url')); add_filter('lostpassword_url', array($this, 'translate_login_url')); add_filter('logout_url', array($this, 'translate_login_url')); add_filter('register_url', array($this, 'translate_login_url')); add_action('login_form', array($this, 'translate_login_form')); add_action('lostpassword_form', array($this, 'translate_login_form')); add_action('resetpass_form', array($this, 'translate_login_form')); add_action('register_form', array($this, 'translate_login_form')); add_filter('retrieve_password_message', array($this, 'translate_retrieve_password_message')); add_filter('lostpassword_redirect', array($this, 'lostpassword_redirect')); add_filter('registration_redirect', array($this, 'registration_redirect')); // print hreflang in template head add_action('wp_head', array($this, 'print_hreflang')); //API add_action('falang_print_language_switch', array($this, 'print_language_switch')); add_filter('falang_custom_translate', array($this, 'custom_translate'), 10, 3); //add shortcode add_shortcode('falang',array($this,'shortcode_falang')); //add shortcode falang add_shortcode( 'falangsw', array($this,'shortcode_falang_switcher') ); /** * Hook called after initializing most hooks and filters * * @from 1.0 * * @param Falang_public object */ do_action('falang_init', $this); } /** * Add links translation filters after all query variables for the current request have been parsed. * * @hook 'parse_request' * @from 2.0 */ public function add_links_translation_filters($wp = null) { add_filter('home_url', array($this,'translate_home_url'), 10, 4); add_filter('pre_post_link', array($this, 'pre_translate_permalink'), 10, 3); add_filter('post_link', array($this, 'translate_permalink'), 10, 3); add_filter('page_link', array($this, 'translate_page_link'), 10, 3); add_filter('post_type_link', array($this, 'translate_custom_post_link'), 9, 3); add_filter('attachment_link', array($this, 'translate_attachment_link'), 10, 2); add_filter('post_link_category', array($this, 'translate_post_link_category'), 10, 3); // not implemented yet add_filter('post_type_archive_link', array($this, 'translate_post_type_archive_link'), 10, 2); add_filter('year_link', array($this,'translate_month_link')); add_filter('month_link', array($this,'translate_month_link')); add_filter('day_link', array($this,'translate_month_link')); add_filter('term_link', array($this, 'translate_term_link'), 10, 3); add_filter('get_edit_post_link', array($this, 'translate_edit_post_link'), 10, 3); } /** * Filter for 'locale' * * @from 1.0 */ public function get_locale($locale) { if ($language = $this->get_current_language()) { return $language->locale; } return $locale; } /** * Modifies some query vars * * @since 1.0 * * @param object $query WP_Query object */ function query_vars( $query_vars ) { $query_vars[] = 'falang_slug'; $query_vars[] = 'falang_page'; $query_vars[] = 'preview_language'; return $query_vars; } /** * Intercept query_vars to find out type of query and get parent. * Must return an array of query vars * * Hook for 'request' * * @from 1.0 */ public function catch_translation($query_vars) { global $wp_rewrite; $falang_post = new \Falang\Core\Post(); $falang_taxo = new \Falang\Core\Taxonomy(); if (isset($query_vars['falang_page']) || isset($query_vars['pagename']) || isset($query_vars['name'])) { // -> page, post or custom post type $name = ''; if (isset($query_vars['falang_page'])) { $name = $query_vars['falang_page']; } else if (isset($query_vars['pagename'])) { $name = $query_vars['pagename']; } else if (isset($query_vars['name'])) { $name = $query_vars['name']; } $ancestors = explode('/', $name); // -> remove the permalink structure prefix if there is one if (isset($query_vars['falang_page']) && !empty($wp_rewrite->front) && $wp_rewrite->front !== '/' && trim($wp_rewrite->front, '/') === $ancestors[0]) { array_shift($ancestors); } $post_name = array_pop($ancestors); $post_types = isset($query_vars['post_type']) ? array($query_vars['post_type']) : array('page', 'post'); $post = $this->query_post($post_name, array( 'post_types' => $post_types, 'ancestor_names' => $ancestors )); if ($post) { $post_type_obj = get_post_type_object($post->post_type); if (isset($query_vars['falang_slug']) && $query_vars['falang_slug'] !== $falang_post->translate_cpt($post->post_type, $this->get_current_language(), $post->post_type)) { // wrong slug $this->canonical = false; } if ($post_type_obj->hierarchical) { $path = ''; $parent_id = $post->post_parent; while ($parent_id) { $parent = get_post($parent_id); $path = $parent->post_name . '/' . $path; $parent_id = $parent->post_parent; } if (isset($query_vars[$post->post_type])) { $query_vars[$post->post_type] = $path . $post->post_name; } if (isset($query_vars['name'])) { $query_vars['name'] = $path . $post->post_name; } else { $query_vars['pagename'] = $path . $post->post_name; } } else { if (isset($query_vars['pagename'])) { $query_vars['pagename'] = $post->post_name; } else { $query_vars['name'] = $post->post_name; } if (isset($query_vars[$post->post_type])) { $query_vars[$post->post_type] = $post->post_name; } } } else if (isset($query_vars['falang_page'])) { // -> nothing found. Let's pretend we did not see $query_vars['name'] = $query_vars['falang_page']; } } else if (isset($query_vars['attachment']) && $falang_post->is_post_type_translatable('attachment')) { // -> attachment (this is a child of a "post" post-type) $post = $this->query_post($query_vars['attachment'], array( 'post_types' => array('attachment') )); if ($post) { $query_vars['attachment'] = $post->post_name; } } else if (isset($query_vars['post_type'])) { // -> custom-post-type archive $post_type = $query_vars['post_type']; if ($falang_post->is_post_type_translatable($post_type)) { if (isset($query_vars['falang_slug']) && $query_vars['falang_slug'] !== $falang_post->translate_cpt_archive($post_type, $this->get_current_language())) { // wrong slug $this->canonical = false; } } } else if ($results = array_filter(array_map(array($this, 'query_var_to_taxonomy'), array_keys($query_vars)), array($this, 'is_taxonomy_translatable'))) { // -> untranslated taxonomy if (isset($query_vars['falang_slug'])) { $taxonomy = ''; foreach ($results as $r) { $taxonomy = $r; break; } if (!$taxonomy) throw new Exception('Taxonomy not found!'); $tax_obj = get_taxonomy($taxonomy); $tax_qv = $tax_obj->query_var; $term_name = $query_vars[$tax_qv]; $term = $this->query_taxonomy($term_name, $taxonomy); if ($term) { $query_vars[$tax_qv] = $term->slug; // -> restore original language name in query_var //1.3.7 not necessary to pass the $taxonomy for fallback $tax_translation = $falang_taxo->translate_taxonomy($taxonomy, $this->get_current_language()); if ($tax_translation !== $query_vars['falang_slug']) { // taxonomy should be translated $this->canonical = false; } } } } if (isset($query_vars['preview'])) { $this->canonical = true; } return $query_vars; } /** * Find original post based on query vars info. * * @from 1.0 * @update 1.3.44 fix bug with post with parent => skip looking for parent for "post" (allerdings) * clean code * * @param string $post_name * @param array $args { * Array of arguments * @type array $post_types Array of post_type strings * @type array $ancestor_names Array of ancestor post_names * @type array $exclude_ids Array of post ids * } * @return WP_Post object $post. Queried post or null */ public function query_post($post_name, $args = array()) { global $wpdb; $post_name = esc_sql($post_name); if (isset($args['post_types']) && is_array($args['post_types']) && $args['post_types']) { $post_type_sql = $wpdb->prepare(implode(',', array_fill(0, count($args['post_types']), '%s')), $args['post_types']); } if (!$this->is_default()) { $post_ids = array_map('intval', $wpdb->get_col( $wpdb->prepare( "SELECT post_id FROM $wpdb->postmeta WHERE meta_key = %s AND meta_value = %s", $this->get_prefix($this->current_language->locale).'post_name', $post_name ))); //TODO check if it's necessary to exclude non published post_name if (isset($args['exclude_ids']) && $post_ids) { $exclude_ids = array_map('intval', $args['exclude_ids']); $post_ids = array_diff($post_ids, $exclude_ids); } } if (isset($post_ids) && $post_ids) { $post_ids_sql = $wpdb->prepare(implode(',', array_fill(0, count($post_ids), '%d')), $post_ids); $wheres = array(); $wheres[] = 'post.ID IN (' . $post_ids_sql . ')'; if (isset($post_type_sql)) { $wheres[] = 'post.post_type IN (' . $post_type_sql . ')'; } $posts = $wpdb->get_results( "SELECT post.* FROM $wpdb->posts AS post WHERE ".implode(" AND ", $wheres) ); foreach ($posts as $post) { // -> Translations found if (isset($args['ancestor_names']) && $args['ancestor_names']) { // -> verify ancestors recursively $query_args = $args; $query_args['exclude_ids'][] = $post->ID; // -> exclude this post to prevent a loop hole (multiple pages can share the same slug if parented differently) $parent_name = array_pop($query_args['ancestor_names']); $parent = $this->query_post($parent_name, $query_args); if ($parent && $post->post_parent == $parent->ID) { return $post; } } else if (!$post->post_parent || $post->post_type == 'post') { // This one will just do return $post; } } } // no translation -> search untranslated posts with this name $wheres = array(); $wheres[] = $wpdb->prepare('post.post_name = %s', $post_name); if (isset($post_type_sql)) { $wheres[] = 'post.post_type IN (' . $post_type_sql . ')'; } if (isset($args['exclude_ids'])) { $exclude_ids = array_map('intval', $args['exclude_ids']); $exclude_sql = $wpdb->prepare(implode(',', array_fill(0, count($exclude_ids), '%d')), $exclude_ids); $wheres[] = 'post.ID NOT IN ('.$exclude_sql.')'; } $posts = $wpdb->get_results( "SELECT post.* FROM $wpdb->posts AS post WHERE ".implode(' AND ', $wheres) ); if ($posts) { foreach ($posts as $post) { $falang_post = new \Falang\Core\Post( $post->ID ); if ( isset( $args['ancestor_names'] ) && $args['ancestor_names'] ) { // -> verify ancestors recursively $query_args = $args; $query_args['exclude_ids'][] = $post->ID; // -> exclude this post to prevent a loop hole (multiple pages can share the same slug if parented differently) $parent_name = array_pop( $query_args['ancestor_names'] ); $parent = $this->query_post( $parent_name, $query_args ); // check if parent match and there is no specific translation... if ( $parent && $post->post_parent == $parent->ID ) { // Post found if ( $falang_post->is_post_type_translatable( $post->post_type ) && $falang_post->is_published( $this->current_language->locale ) && get_post_meta( $post->ID, $this->get_prefix( $this->current_language->locale ) . 'post_name', true ) ) { // But there is a specific translation for this post $this->canonical = false; } return $post; } } else if ( ! $post->post_parent ) { // Post found if ( $falang_post->is_post_type_translatable( $post->post_type ) && $falang_post->is_published( $this->current_language->locale ) && get_post_meta( $post->ID, $this->get_prefix( $this->current_language->locale ) . 'post_name', true ) ) { // But there is a specific translation for this post $this->canonical = false; } return $post; } } } // Nothing found. -> Search in other languages... $post_names = array(); foreach ($this->model->get_languages_list() as $language) { if (!$this->is_default($language)) { $post_names[] = esc_sql($this->get_prefix($language->locale).'post_name'); } } $post_names_sql = $wpdb->prepare(implode(',', array_fill(0, count($post_names), '%s')), $post_names); $wheres = array(); $wheres[] = 'meta.meta_key IN ('.$post_names_sql.')'; $wheres[] = $wpdb->prepare('meta.meta_value = %s', $post_name); if (isset($post_type_sql)) { $wheres[] = 'post.post_type IN (' . $post_type_sql . ')'; } $posts = $wpdb->get_results( "SELECT post.* FROM $wpdb->posts AS post INNER JOIN $wpdb->postmeta AS meta ON (post.ID = meta.post_id) WHERE ".implode(' AND ', $wheres) ); foreach ($posts as $post) { // -> Translations found if (isset($args['ancestor_names']) && $args['ancestor_names']) { // -> verify ancestors recursively $query_args = $args; $query_args['exclude_ids'][] = $post->ID; // -> exclude this post to prevent a loop hole (multiple pages can share the same slug if parented differently) $parent_name = array_pop($query_args['ancestor_names']); $parent = $this->query_post($parent_name, $query_args); if ($parent && $post->post_parent == $parent->ID) { $this->canonical = false; return $post; } } else if (!$post->post_parent) { $this->canonical = false; // This one will just do return $post; } } } /** * Find original term based on query vars info. * * @from 1.0 * * @param string $slug * @param string|array $taxonomy */ public function query_taxonomy($slug, $taxonomies) { global $wpdb; $taxonomy_string = is_array($taxonomies) ? "'".implode("','", esc_sql($taxonomies))."'" : "'".esc_sql($taxonomies)."'"; $translation_slug = $this->get_prefix($this->current_language->locale) . 'slug'; $term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM $wpdb->termmeta WHERE meta_key = %s AND meta_value = %s", $translation_slug, $slug )); if ($term_ids) { // Translations found but we're not sure about taxonomy $term = $wpdb->get_row( "SELECT t.term_id, t.slug, tt.taxonomy, tt.parent FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN ($taxonomy_string) AND t.term_id IN (".implode(",", array_map('intval', $term_ids)).")" ); return $term; } // -> no translated term for this slug $term = $wpdb->get_row( $wpdb->prepare( "SELECT t.term_id, t.slug, tt.taxonomy, tt.parent FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN ($taxonomy_string) AND t.slug = %s", $slug )); if ($term) { $falang_taxo = new \Falang\Core\Taxonomy(); if ($falang_taxo->is_taxonomy_translatable($term->taxonomy) && get_term_meta($term->term_id, $this->get_prefix($this->current_language->locale) . 'slug', true)) { // -> But there is a specific translation for this term $this->canonical = false; } return $term; } else { // Nothing found. -> Search in other languages... $language_slugs = $this->model->get_language_column('post_name'); $term_ids = $wpdb->get_col( $wpdb->prepare( "SELECT term_id FROM $wpdb->termmeta WHERE meta_key IN ('_" . implode("slug','", esc_sql(array_map(array($this, 'create_prefix'), $language_slugs))) . "slug') AND meta_value = %s", $slug )); if ($term_ids) { $term = $wpdb->get_row( "SELECT t.term_id, t.slug, tt.taxonomy, tt.parent FROM $wpdb->terms AS t INNER JOIN $wpdb->term_taxonomy AS tt ON t.term_id = tt.term_id WHERE tt.taxonomy IN ($taxonomy_string) AND t.term_id IN (".implode(",", array_map('intval', $term_ids)).")" ); if ($term) { // Term found in wrong language. $this->canonical = false; return $term; } } } return false; } /** * Redirection when not canoncal url * Must be fired after filters. * Must be fired after conditional tags are set. * * @from 1.0 */ public function redirect_uncanonical() { $query_object = get_queried_object(); if (!$this->canonical) { if (is_singular()) { $url = get_permalink($query_object->ID); } else if (is_post_type_archive()) { $url = get_post_type_archive_link($query_object->name); } else if (is_category() || is_tag() || is_tax()) { $url = get_term_link($query_object->term_id, $query_object->taxonomy); } else { $url = home_url(); } wp_redirect($url); exit; } } /** * translate posts * * @filter 'the_posts' * * @from 1.1 * @from 2.5 perform update_post_caches before translation */ public function translate_the_posts($posts, $wp_query) { return $posts; } /** * translate posts * * @filter 'get_pages' * * @from 2.0 */ public function translate_the_pages($posts, $r) { return $posts; // if (!$this->is_default()) { // // $posts = $this->translate_the_posts($posts); // // } // // return $posts; } public function posts_join( $join, $query ) { global $wpdb; $qv = &$query->query_vars; // if ( ! empty( $qv['falang_suppress_locale_query'] ) ) { // return $join; // } //todo check the slug is a supported language on the site $slug = empty( $qv['lang'] ) ? $slug = $this->current_language->slug : $qv['lang']; $language = $this->model->get_language_by_slug($slug); if ( !isset($language) ) { return $join; } if ( ! $meta_table = _get_meta_table( 'post' ) ) { return $join; } $join .= " LEFT JOIN $meta_table AS falang_postmeta ON ($wpdb->posts.ID = falang_postmeta.post_id AND falang_postmeta.meta_key = '_locale')"; return $join; } /** * filter posts * * @filter 'the_posts' * * @from 1.0 */ public function posts_where( $where, $query) { global $wpdb; $qv = $query->query_vars; // if ( ! empty( $qv['falang_suppress_locale_query'] ) ) { // return $where; // } $slug = empty( $qv['lang'] ) ? $slug = $this->current_language->slug : $qv['lang']; //todo check the slug is a supported language on the site $language = $this->model->get_language_by_slug($slug); //tODO check if slug is not set correctly if ( !isset($language) ) { return $where; } if ( ! $meta_table = _get_meta_table( 'post' ) ) { return $where; } $where .= " AND (1=0"; $where .= $wpdb->prepare( " OR falang_postmeta.meta_value LIKE %s", $language->locale ); // $where .= $wpdb->prepare( " OR falang_postmeta.meta_value LIKE %s", 'all' ); $where .= " OR falang_postmeta.meta_id IS NULL"; // if ( bogo_is_default_locale( $language->locale ) ) { // $where .= " OR falang_postmeta.meta_id IS NULL"; // } $where .= ")"; return $where; } /** * Translate title to current language * Filter for 'the_title' * * @from 1.0 * @since 1.3.6 check id as int * @since 1.3.11 * */ public function translate_post_title($title, $id = null) { //id is like ID-slug when language switcher in menu //don't translate it in this case if (strpos($id,'-')){ $id = substr($id,0, strpos($id,'-')); } $falang_menu_item = get_post_meta( $id, '_falang_menu_item', true ); if (!empty($falang_menu_item)){ return $title; } //$id not an integer when call not from get_the_title //https://developer.wordpress.org/reference/functions/the_title/ //fix for Multi-column Tag Map if (!is_int($id)){ return $title; } //normal post title translation $post = get_post($id); $falang_post = new \Falang\Core\Post($id); if ($post && !$this->is_default() && $falang_post->is_post_type_translatable($post->post_type)){ $title = $falang_post->translate_post_field($post, 'post_title', $this->current_language, $title); } return $title; } /** * Translate content to current language * Filter for 'the_content' * * @from 1.0 * @from 1.3 check wether content corresponds to global post ('the_content' filter may be used outside of The Loop or when global $post does not match) * @from 1.3.15 don't translate yootheme post_content * @from 1.3.36 add elementor preview test to not translate the content * remove $post->builder //fix bug on elementor Attempt to assign property "builder" on int * @from 1.3.41 skip post translation for Divi Post (use Divi code for the test) * @from 1.3.42 skip post translation for Elementor page (see on muziekindecathrien) * @from 1.3.50 add test on $post->ID * @from 1.3.61 add password protection support * */ public function translate_post_content($content) { global $post; if (!isset($post->ID)){return $content;} $falang_post = new \Falang\Core\Post($post->ID); //TODO add something to filter which post_content needs to be non-translated //Divi page if ( 'on' === get_post_meta( $post->ID, '_et_pb_use_builder', true ) ) { return $content; } //the previous divi test on the meta make probably the test on et_fb not necessary //Divi edition page doesn't translate content if (isset($_GET['et_fb'])){return $content;} //Elementor edition page doesn't translate content //must be done before yootheme : Attempt to assign property "builder" on int if (isset($_GET['elementor-preview'])){return $content;} //elementor content don't have to be translated //on elementor page the $content is not the $post->content (muziekindecathrien) if (preg_match('/data-elementor-type="wp-page"/', $content)) { return $content; } //yootheme builder page don't translate it if ($post and preg_match('/ rtrim($_SERVER['SCRIPT_URI'], '/') == rtrim(home_url()) $detected_language = $this->auto_detect_language(); if ($detected_language) { $language = $detected_language; } } } } if (empty($language)) { $language = $this->model->get_default_language(); } $GLOBALS['text_direction'] = $language->rtl?'rtl':'tlr'; return $language; } /** * Detect language * * @from 1.2 * @from 1.3.13 fix bug on available_langauge need to use slug and not locale * * @return WP_Post|false */ public function auto_detect_language() { $available_languages = array(); foreach ($this->model->get_languages_list() as $language) { $available_languages[] = $language->slug; } if (isset($_SERVER['HTTP_ACCEPT_LANGUAGE'])) { $prefered_language = $this->prefered_language($available_languages, $_SERVER['HTTP_ACCEPT_LANGUAGE']); if ($prefered_language) { foreach ($prefered_language as $language_slug => $value) { return $this->model->get_language_by_slug($language_slug); } } } return false; } /** * Find prefered language * source: https://stackoverflow.com/a/25749660/2086505 * * @from 1.0 * * @param array $available_languages Available languages. Eg: array("en", "zh-cn", "es"); * @param string $http_accept_language HTTP Accepted languages. Eg: $_SERVER["HTTP_ACCEPT_LANGUAGE"] = 'en-us,en;q=0.8,es-cl;q=0.5,zh-cn;q=0.3'; * @return array prefered languages. Eg: Array([en] => 0.8, [es] => 0.4, [zh-cn] => 0.3) */ private function prefered_language($available_languages, $http_accept_language) { $available_languages = array_flip($available_languages); $langs = array(); preg_match_all('~([\w-]+)(?:[^,\d]+([\d.]+))?~', strtolower($http_accept_language), $matches, PREG_SET_ORDER); foreach($matches as $match) { list($a, $b) = explode('-', $match[1]) + array('', ''); $value = isset($match[2]) ? (float) $match[2] : 1.0; if(isset($available_languages[$match[1]])) { $langs[$match[1]] = $value; continue; } if(isset($available_languages[$a])) { $langs[$a] = $value - 0.1; } } arsort($langs); return $langs; } /** * Find taxonomy by query var * * @from 1.0 */ public function query_var_to_taxonomy($taxonomy_qv) { $results = get_taxonomies(array('query_var' => $taxonomy_qv)); foreach ($results as $result) { return $result; } return false; } /** * Check if taxonomy is translatable. * * @from 1.2.3 * * @param string $taxonomy * * @return boolean */ public function is_taxonomy_translatable($taxonomy) { //TODO not the right place put it in core $falang_taxonomy = new \Falang\Core\Taxonomy(); return $falang_taxonomy->is_taxonomy_translatable($taxonomy); } /** * Register the stylesheets for the public-facing side of the site. * * @since 1.0.0 */ public function enqueue_styles() { /** * This function is provided for demonstration purposes only. * * An instance of this class should be passed to the run() function * defined in Falang_Loader as all of the hooks are defined * in that particular class. * * The Falang_Loader will then create the relationship * between the defined hooks and the functions defined in this * class. */ wp_enqueue_style( $this->plugin_name, plugin_dir_url( __FILE__ ) . 'css/falang-public.css', array(), $this->version, 'all' ); } /** * Add language slug in login url * * Filter for 'login_url', 'logout_url', 'lostpassword_url', 'register_url' * * @from 1.0 */ public function translate_login_url($login_url){ if ($this->has_language()) { $login_url = add_query_arg(array($this->language_query_var => $this->get_current_language()->slug), $login_url); } return $login_url; } /** * Add language input in login forms * * Hook for 'login_form', 'lostpassword_form', 'resetpass_form', 'register_form' * * @from 1.0 */ public function translate_login_form() { echo ''; } //use core // public function translate_home_url($url, $path, $orig_scheme, $blog_id) { // // $language = $this->get_current_language(); // // if (get_option('permalink_structure')) { // // $url = rtrim(substr($url, 0, strlen($url) - strlen($path)), '/') . '/' . $language->slug . '/' . ltrim($path, '/'); // // } else { // // $url = add_query_arg( array('lang' => $language->slug), $url); // // } // return $url; // } /** * Translate link in retrieve password message * * Filter for 'retrieve_password_message' * * @from 1.0 */ public function translate_retrieve_password_message($message) { return preg_replace('/(wp-login\.php[^>]*)/', '$1'.'&'.$this->language_query_var.'='.$this->get_current_language()->slug, $message); } /** * lostpassword redirect * * Filter for 'lostpassword_redirect' * * @from 1.2 */ public function lostpassword_redirect($redirect_to) { return 'wp-login.php?checkemail=confirm'.'&'.$this->language_query_var.'='.$this->get_current_language()->slug; } /** * registration redirect * * Filter for 'registration_redirect' * * @from 1.0 */ public function registration_redirect($redirect_to) { return 'wp-login.php?checkemail=registered'.'&'.$this->language_query_var.'='.$this->get_current_language()->slug; } /** * Print hreflang * * Filter for 'wp_head' * * @from 1.3.7 * @update 1.3.59 fix the return not used for the falang_hreflang filter */ public function print_hreflang() { $hreflangs = array(); $languages = $this->model->get_languages_list(); foreach ($languages as $language) { //hreflang code ISO 639-1 en-GB , fr-FR and not like wordpress locale en_GB,fr_FR $lang = str_replace('_','-',$language->locale); $hreflangs[ $lang ] = $this->get_translated_url($language); } // Adds the site root url when the default language code is not hidden // see polylang thread // See https://wordpress.org/support/topic/implementation-of-hreflangx-default if ( is_home() && $this->model->get_option('show_slug' ) ){ //need to remove the language locale /en_us and leave a / at the end $home_url = str_replace('/'.$this->current_language->slug,'',home_url ('/')); $hreflangs['x-default'] = $home_url; } /** * Filters the list of rel hreflang attributes * * @since 1.3.7 * @update 1.3.59 fix the return not used * * @param array $hreflangs Array of urls with language codes as keys */ $hreflangs = apply_filters('falang_hreflang', $hreflangs); foreach ( $hreflangs as $lang => $url ) { printf( '' . "\n", esc_url( $url ), esc_attr( $lang ) ); } } /** * Pre translate post link * Filter for 'pre_post_link' * * @from 1.0 */ public function pre_translate_permalink($permalink, $post, $leavename) { if (!$this->is_default()) { $permalink = str_replace('%postname%', '%translated_postname%', $permalink); $permalink = str_replace('%pagename%', '%translated_postname%', $permalink); } return $permalink; } /** * Translate permalink * Filter for 'post_link' * * @from 1.0 */ public function translate_permalink($permalink, $post, $leavename) { if (!$this->is_default()) { $falang_post = new \Falang\Core\Post($post->ID); $translated_name = $falang_post->translate_post_field($post, 'post_name',$this->get_current_language()); $permalink = str_replace('%translated_postname%', $translated_name, $permalink); } return $permalink; } /** * Translate page link * Filter for 'page_link' * * @from 1.0 * @update 1.3.44 plain permalink check (borbot) */ public function translate_page_link($link, $post_id, $sample = false) { //plain permalink nothing to do if (empty(get_option( 'permalink_structure' ))){ return $link; } $falang_post = new \Falang\Core\Post(); if (!$sample && !$this->is_default() && $falang_post->is_post_type_translatable('page')) { $original = get_post($post_id); $translated_slug = $falang_post->translate_post_field($original, 'post_name',$this->get_current_language()); // hierarchical pages while ($original->post_parent != 0) { $original = get_post($original->post_parent); $parent_slug = $falang_post->translate_post_field($original, 'post_name',$this->get_current_language()); $translated_slug = $parent_slug.'/'.$translated_slug; } $link = get_page_link($original, true, true); $link = str_replace('%pagename%', $translated_slug, $link); } return $link; } /** * Translate attachment link * Filter for 'attachment_link' * * @from 1.0 */ public function translate_attachment_link($link, $post_id) { global $wp_rewrite; $falang_post = new \Falang\Core\Post(); if (!$this->is_default()) { $link = trailingslashit($link); $post = get_post( $post_id ); $parent = ( $post->post_parent > 0 && $post->post_parent != $post->ID ) ? get_post( $post->post_parent ) : false; if ( $wp_rewrite->using_permalinks() && $parent ) { $translation_name = $falang_post->translate_post_field($post, 'post_name',$this->get_current_language()); $link = str_replace ('/'.$post->post_name.'/', '/'.$translation_name.'/', $link); do { $translation_parent_name = $falang_post->translate_post_field($parent, 'post_name',$this->get_current_language()); $link = str_replace ('/'.$parent->post_name.'/', '/'.$translation_parent_name.'/', $link); $parent = ( $parent->post_parent > 0 && $parent->post_parent != $parent->ID ) ? get_post( $parent->post_parent ) : false; } while ($parent); } } return $link; } /** * Translate post link category * Filter for 'post_link_category' * * @from 1.0 */ public function translate_post_link_category($cat, $cats, $post) { //TODO to be done... return $cat; } /** * Translation post type archive link * * Filter for 'post_type_archive_link' * * @from 1.0 * @from 1.3 use specific archive slug if any * @since 1.3.25 post_type_obj->rewrite can be set to false */ function translate_post_type_archive_link($link, $post_type) { global $wp_rewrite; $falang_post = new \Falang\Core\Post(); if ($falang_post->is_post_type_translatable($post_type)) { $post_type_obj = get_post_type_object($post_type); if ($post_type_obj && get_option( 'permalink_structure' )) { $struct = $falang_post->translate_cpt_archive($post_type,$this->get_current_language()); //$post_type_obj->rewrite can be set to false if ( isset($post_type_obj->rewrite['with_front']) && $post_type_obj->rewrite['with_front'] ) $struct = $wp_rewrite->front . $struct; else $struct = $wp_rewrite->root . $struct; $link = home_url( user_trailingslashit( $struct, 'post_type_archive' ) ); } } return $link; } /** * Translate month link * Filter for 'month_link' * * @from 1.0 */ public function translate_month_link($monthlink) { return $monthlink; } /** * Translate term link * * @filter 'term_link' * * @param string $termlink Term link URL. * @param WP_Term $term Term object. * @param string $taxonomy Taxonomy slug. * * @simimlar method for back falang/admin/class-falang-admin.php/@translate_term_link * * @from 1.0 */ public function translate_term_link($termlink, $term, $taxonomy) { global $wp_rewrite; $falang_taxo = new \Falang\Core\Taxonomy(); if (get_option('permalink_structure') && $falang_taxo->is_taxonomy_translatable($taxonomy)) { $taxonomy_obj = get_taxonomy($taxonomy); $termlink = ($taxonomy_obj->rewrite['with_front']) ? $wp_rewrite->front : $wp_rewrite->root; $termlink .= $falang_taxo->translate_taxonomy($taxonomy, $this->get_current_language(), $taxonomy); // -> todo: handle hierarchical taxonomy... $translated_slug = $falang_taxo->translate_term_field($term, $taxonomy, 'slug',$this->get_current_language()); $termlink = home_url(user_trailingslashit($termlink . '/' . $translated_slug, 'category')); } return $termlink; } /** * Add language in edit link * * Filter for 'get_edit_post_link' * * @from 1.0 */ public function translate_edit_post_link($url, $post_id, $context) { $falang_post = new \Falang\Core\Post(); if (!$this->is_default()) { $post = get_post($post_id); $language = $this->get_current_language(); if (isset($post->post_type) && $falang_post->is_post_type_translatable($post->post_type)) { $url = add_query_arg(array($this->language_query_var => $language->slug), $url); } } return $url; } /** * Handle search query * * @hook for 'parse_query' * * @from 1.0 */ public function parse_query($wp_query) { if ($this->is_query_translatable($wp_query->query_vars)) { $language = isset($wp_query->query_vars['falang']) ? $this->model->get_language_by_slug($wp_query->query_vars['falang']) : $this->get_current_language(); if (isset($wp_query->query_vars['s']) && $wp_query->query_vars['s']) { // query_vars['s'] is empty string by default $search_meta_fields = array(); if (!$this->is_default($language)) { $search_meta_fields = array( $this->get_prefix($language->locale) . 'post_title', $this->get_prefix($language->locale) . 'post_content', $this->get_prefix($language->locale) . 'post_excerpt' ); } /** * Filter meta keys searcheable for translation * * @from 1.0 * * @param array $search_meta_fields. Array of custom field keys to search into * @param WP_Query object $query * @param Post object $language * @param Falang_current object $this */ $search_meta_fields = apply_filters('falang_search_meta', $search_meta_fields, $wp_query, $language, $this); if ($search_meta_fields) { $wp_query->query_vars['falang_search_meta'] = $search_meta_fields; $wp_query->query_vars['falang_search_alias'] = 'postmeta_search'; add_filter('posts_join_request', array($this, 'meta_search_join'), 10, 2); add_filter('posts_search', array($this, 'meta_search'), 10, 2); add_filter('posts_distinct_request', array($this, 'meta_search_distinct'), 10, 2); } } /** * Hook called on parsing a query that needs translation * * @from 1.0 * * @param WP_Query object $query * @param Falang_current object $this */ do_action('falang_parse_query', $wp_query, $language, $this); } } /** * @filter 'posts_join' * * @from 1.3 */ public function meta_search_join($join, $wp_query) { global $wpdb; if (isset($wp_query->query_vars['falang_search_alias'])) { $alias = $wp_query->query_vars['falang_search_alias']; $fields = $wp_query->query_vars['falang_search_meta']; return "LEFT JOIN $wpdb->postmeta AS $alias ON ($wpdb->posts.ID = $alias.post_id AND $alias.meta_key IN ('".implode("','", esc_sql($fields))."')) " . $join; } return $join; } /** * @filter 'posts_distinct_request' * * @from 1.3 */ public function meta_search_distinct($distinct, $wp_query) { if (isset($wp_query->query_vars['falang_search_alias'])) { return 'DISTINCT'; } return $distinct; } /** * @filter 'posts_search' * * @from 1.3 */ public function meta_search($search, $wp_query) { global $wpdb; if (isset($wp_query->query_vars['falang_search_alias'])) { $alias = $wp_query->query_vars['falang_search_alias']; $q = $wp_query->query_vars; $search = ''; // added slashes screw with quote grouping when done early, so done later $q['s'] = stripslashes( $q['s'] ); if ( empty( $_GET['s'] ) && $wp_query->is_main_query() ) $q['s'] = urldecode( $q['s'] ); // there are no line breaks in fields $q['s'] = str_replace( array( "\r", "\n" ), '', $q['s'] ); $q['search_terms_count'] = 1; if ( ! empty( $q['sentence'] ) ) { $q['search_terms'] = array( $q['s'] ); } else { if ( preg_match_all( '/".*?("|$)|((?<=[\t ",+])|^)[^\t ",+]+/', $q['s'], $matches ) ) { $q['search_terms_count'] = count( $matches[0] ); $q['search_terms'] = $wp_query->parse_search_terms( $matches[0] ); // if the search string has only short terms or stopwords, or is 10+ terms long, match it as sentence if ( empty( $q['search_terms'] ) || count( $q['search_terms'] ) > 9 ) $q['search_terms'] = array( $q['s'] ); } else { $q['search_terms'] = array( $q['s'] ); } } $n = ! empty( $q['exact'] ) ? '' : '%'; $searchand = ''; $q['search_orderby_title'] = array(); /** * Filters the prefix that indicates that a search term should be excluded from results. * * @since 4.7.0 * * @param string $exclusion_prefix The prefix. Default '-'. Returning * an empty value disables exclusions. */ $exclusion_prefix = apply_filters( 'wp_query_search_exclusion_prefix', '-' ); foreach ( $q['search_terms'] as $term ) { // If there is an $exclusion_prefix, terms prefixed with it should be excluded. $exclude = $exclusion_prefix && ( $exclusion_prefix === substr( $term, 0, 1 ) ); if ( $exclude ) { $like_op = 'NOT LIKE'; $andor_op = 'AND'; $term = substr( $term, 1 ); } else { $like_op = 'LIKE'; $andor_op = 'OR'; } if ( $n && ! $exclude ) { $like = '%' . $wpdb->esc_like( $term ) . '%'; $q['search_orderby_title'][] = $wpdb->prepare( "{$wpdb->posts}.post_title LIKE %s", $like ); } $like = $n . $wpdb->esc_like( $term ) . $n; $termsearch = $wpdb->prepare( "($wpdb->posts.post_title $like_op %s) $andor_op ($wpdb->posts.post_excerpt $like_op %s) $andor_op ($wpdb->posts.post_content $like_op %s)", $like, $like, $like ); // -> modified $termsearch .= $wpdb->prepare( " $andor_op ($alias.meta_value $like_op %s)", $like ); $search .= "{$searchand}($termsearch)"; $searchand = ' AND '; } if ( ! empty( $search ) ) { $search = " AND ({$search}) "; if ( ! is_user_logged_in() ) { $search .= " AND ({$wpdb->posts}.post_password = '') "; } } } return $search; } /** * Helper for parse_query(). Check if query is to be translated * * @param array $query_vars. * * @from 1.0 */ public function is_query_translatable($query_vars) { $falang_post = new \Falang\Core\Post(); $post_type = isset($query_vars['post_type']) ? $query_vars['post_type'] : 'post'; if ($post_type === 'any') { return true; // lets pretend it is... } else if (is_string($post_type)) { return $falang_post->is_post_type_translatable($post_type); } else if (is_array($post_type)) { return array_filter($post_type, array($falang_post, 'is_post_type_translatable')); // at least one is } } /** * Enqueue terms for translation as they are queried * Filter for 'get_object_terms' * * @from 1.4.5 */ public function filter_get_object_terms($terms, $object_ids, $taxonomies, $args) { return $this->translate_get_terms($terms, $taxonomies, $args); } /** * Filter post terms. Hard translate * * @filter 'get_terms' * * @from 1.1 */ public function translate_get_terms($terms, $taxonomies, $args) { if (isset($args[$this->language_query_var])) { if (!$args[$this->language_query_var]) { // false -> do not translate // Documented below return apply_filters('falang_translate_the_terms', $terms, $terms, null, $args, $this); } else { // language slug $language = $this->model->get_language_by_slug($args[$this->language_query_var]); } } else { // language not set -> use current $language = null; } if (!$this->is_default($language)) { if (isset($args['fields']) && $args['fields'] == 'names') { // -> Added in 1.4.4 $terms = array(); // -> restart query unset($args['fields']); $results = get_terms($taxonomies, $args); foreach ($results as $term) { $terms[] = $this->translate_term_field($term, $term->taxonomy, 'name'); } return $terms; } if (empty($args['fields']) || $args['fields'] == 'all' || $args['fields'] == 'all_with_object_id') { /** * Filter the terms after translation * * @from 2.0 * * @param array of object WP_Term $translated terms. * @param array of object WP_Term $original terms. * @param object WP_Post $language. * @param object $args. * @param object Falang_core $this. */ $terms = apply_filters('falang_translate_the_terms', $this->translate_terms($terms, $language), $terms, $language, $args, $this); } } return $terms; } /** * Translate terms * * @from 2.0 * * @param array of object WP_Terms $terms * @param object language */ public function translate_terms($terms, $language = null) { $new_terms = array(); foreach ($terms as $term) { /** * Filter the term after translation * * @from 2.0 * * @param object WP_Term $translated post. * @param object WP_Term $original post. * @param object WP_Term $language. * @param object Falang_core $this. */ $new_terms[] = apply_filters('falang_translate_the_term', $this->translate_term($term, $language), $term, $language, $this); } return $new_terms; } /** * get term field translation if it exists * * @from 2.0 * * @param object WP_Term $term. * @param string $taxonomy. * @param string $field. Accepts 'name', 'slug', 'description' * @param object WP_Post $language. * @return string */ public function get_term_field_translation($term, $taxonomy, $field, $language = null) { $falang_taxo = new \Falang\Core\Taxonomy(); if (empty($language)) { $language = $this->get_current_language(); } if (!$this->is_default($language) && $falang_taxo->is_taxonomy_translatable($term->taxonomy)) { return get_term_meta($term->term_id, $this->get_prefix($language->locale) . $field, true); } else { return $term->$field; } } /** * Get term field translation * * @from 2.0 Changed parameters * @from 1.1 * * @param object WP_Term $term. * @param string $taxonomy. * @param string $field. Accepts 'name', 'slug', 'description' * @param object WP_Post $language. * @param string $fallback Defaut value to return when translation does not exist. Optional. * @return string. */ public function translate_term_field($term, $taxonomy, $field, $language = null, $fallback = null) { $value = $this->get_term_field_translation($term, $taxonomy, $field, $language); if ($value) { return $value; } else if (isset($fallback)) { return $fallback; } return $term->$field; } /** * Hard translate term * @filter 'get_term' * * @from 1.2 */ public function translate_get_term($term, $taxonomy) { if ($taxonomy == 'language' || $taxonomy == 'term_language'){return $term;} //TODO remove this object creation $falang_taxo = new \Falang\Core\Taxonomy(); if (!$this->is_default() && $falang_taxo->is_taxonomy_translatable($taxonomy)) { //TODO use $this->get_current_language() $this->translate_term($term,$this->request_current_language()); } return $term; } /** * Filter post terms * * @filter 'get_the_terms' * * @from 1.0 */ public function translate_post_terms($terms, $post_id, $taxonomy) { //TODO remove this object creation $falang_taxo = new \Falang\Core\Taxonomy(); if (!$this->is_default() && $falang_taxo->is_taxonomy_translatable($taxonomy)) { foreach ($terms as $term) { $this->translate_term($term); } } return $terms; } /** * Translate term name * Filter for 'list_cats' * * @from 1.0 */ public function translate_term_name($name, $term = null) { if (!isset($term)) { // looks like there are 2 differents list_cats filters return $name; } return $this->translate_term_field($term, $term->taxonomy, 'name', null, $name); } /** * Add language query args to url (Public API) * * @filter 'falang_query_add_language' * * @from 1.0 */ public function query_add_language($url) { if (!$this->is_default()) { $url = add_query_arg(array($this->language_query_var => $this->get_current_language()->slug), $url); } return $url; } /** * Add filters for options translation * * @from 1.0 */ public function add_options_filters() { $translations = $this->model->get_option('translations', array()); $language = $this->get_current_language(); if ($language && isset($translations['option'][$language->locale])) { foreach ($translations['option'][$language->locale] as $option => $val) { add_filter('option_' . $option, array($this, 'filter_option'), 10, 2); } } } /** * Add filters for options translation * * @from 1.0 */ public function filter_option($value, $option = null) { if (empty($option)) return $value; // $option is only defined since wp 4.4 $translations = $this->model->get_option('translations', array()); $language = $this->get_current_language(); if ($language && isset($translations['option'][$language->locale][$option])) { $this->translate_option($value, $translations['option'][$language->locale][$option]); } return $value; } /** * translate options * * @from 1.5.3 add striplashes * @from 1.5 */ private function translate_option(&$option, $translation ) { if (is_array($translation)) { foreach ($translation as $key => $value) { if (isset($option[$key])) { $item = $this->translate_option($option[$key], $value ); } } } else { $option = stripslashes($translation); } } /** * Loads user defined strings translations * * @since 1.0 * * @param string $locale Locale. Defaults to current locale. */ public function load_strings_translations($locale = '' ){ if ( empty( $locale ) ) { $locale = $this->get_current_language()->locale; } $language = $this->model->get_language_by_locale( $locale ); if ( ! empty( $language ) ) { $mo = new Falang_Mo(); $mo->import_from_db( $language ); $GLOBALS['l10n']['falang_string'] = &$mo; } else { unset( $GLOBALS['l10n']['falang_string'] ); } } /** * Append language slug to home url * Filter for 'home_url' * exist for back too * * @from 1.0 * @update 1.3.3 put a version for front and back * @update 1.3.13 fix url with double language prefix (ex widget sorting from savoy theme) jurij_c */ public function translate_home_url($url, $path, $orig_scheme, $blog_id) { $language = $this->get_current_language(); if (!$this->disable_translate_home_url && $language && ($this->model->get_option('show_slug') || !$this->is_default())) { if ( get_option( 'permalink_structure' ) ) { //fix url with double language slug (due to some widget code) $languagesprefix = $language->slug . '/'; if(substr($path, 0, strlen($languagesprefix)) === $languagesprefix) { $path = substr($path, strlen($languagesprefix)); $url= str_replace("/".$languagesprefix,"/",$url); } $url = rtrim( substr( $url, 0, strlen( $url ) - strlen( $path ) ), '/' ) . '/' . $language->slug . '/' . ltrim( $path, '/' ); } else { $url = add_query_arg( array( 'lang' => $language->slug ), $url ); } } return $url; } /** * 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 1.2 * * @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 ) { // FIXME it looks like this filter is useless, now the we use the filter sidebars_widgets $test = $this->get_current_language(); return ! empty( $instance['falang_lang'] ) && $instance['falang_lang'] != $this->get_current_language()->locale ? 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 1.2 The result is cached as the function can be very expensive in case there are a lot of widgets * * @param array $sidebars_widgets An associative array of sidebars and their widgets * @return array */ public function sidebars_widgets( $sidebars_widgets ) { global $wp_registered_widgets; if ( empty( $wp_registered_widgets ) ) { return $sidebars_widgets; } $cache_key = md5( maybe_serialize( $sidebars_widgets ) ); $_sidebars_widgets = $this->cache->get( "sidebars_widgets_{$cache_key}" ); if ( false !== $_sidebars_widgets ) { return $_sidebars_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 ]['falang_lang'] ) && $widget_settings[ $number ]['falang_lang'] !== $this->get_current_language()->locale ) { unset( $sidebars_widgets[ $sidebar ][ $key ] ); } } } $this->cache->set( "sidebars_widgets_{$cache_key}", $sidebars_widgets ); return $sidebars_widgets; } /** * Setup filters for admin pages * * @from 1.2.1 * @update 1.3.27 change flatsome test * @update 1.3.36 add shortcoder support */ public function add_filters() { $classes = array('Nav_Menu','User_Profile','Widget_Pages'); foreach ( $classes as $class ) { $obj = strtolower( $class ); /** * Filter the class to instantiate when loading admin filters * * @since 1.2.1 * * @param string $class class name */ $class = apply_filters( 'falang_' . $obj, $class ); $class = '\Falang\Filter\Site\\'.$class; $this->$obj = new $class( $this ); } //WooCommerce Framework if (in_array('woocommerce/woocommerce.php',apply_filters( 'active_plugins', get_option( 'active_plugins' )))) { $this->woocommerce = new \Falang\Filter\Site\WooCommerce(); } //Enfold Avia Framework if ( defined( 'AV_FRAMEWORK_VERSION' ) ) { $this->enfold = new \Falang\Filter\Site\Enfold(); } //Flatsome Framework use UX_BUILDER_VERSION not found other define for it if ('flatsome' == wp_get_theme()->get('Template')){ $this->flatsome = new \Falang\Filter\Site\Flatsome(); } //Rank Math Seo plugin if (in_array('seo-by-rank-math/rank-math.php',apply_filters( 'active_plugins', get_option( 'active_plugins' )))) { $this->rankmath = new \Falang\Filter\Site\RankMath(); } // Dont load if Yoast SEO is not active. if (in_array('wordpress-seo/wp-seo.php',apply_filters( 'active_plugins', get_option( 'active_plugins' )))) { $this->yoast = new \Falang\Filter\Site\Yoast(); } //support shortcode filtering by language the content translation is in pro falang version if (defined( 'SC_VERSION')){ $this->shortcoder = new \Falang\Filter\Site\Shortcoder(); } } /** * Display the shortcode only for selected language * Shortcode [falang lang="en_GB"] [/falang] * * @since 1.3.1 */ public function shortcode_falang($attr, $content = null){ extract(shortcode_atts(array( 'language' => '', ), $attr)); if ($this->current_language->locale == $attr['lang']){ $output = do_shortcode($content); } else { $output = ""; } return $output; } /** * Display the switcher by shortcode * [falangsw] * * @since 1.3.22 * @since 1.4.0 return (thanks to Frank Sundgaard Nielsen) necessary with echo set to false * @update 1.3.62 set default as false */ public function shortcode_falang_switcher($atts){ $default = array( 'dropdown' => 0, 'display_name' => 1, 'display_flags' => 0, 'hide_current' => 0, 'positioning' => 'h', 'echo' => 0, ); $attr = shortcode_atts( $default,$atts ); $lswitcher = new Language_Switcher($attr); return $lswitcher->display_switcher(); } /** * Translate attachment caption wp_get_attachment_caption * Filter for 'wp_get_attachment_caption' * Caption is stored in post_excerpt * * @from 1.3.6 */ public function translate_attachment_caption($caption, $post_id) { $falang_post = new \Falang\Core\Post($post_id); $post = get_post($post_id); if ($post && !$this->is_default() && $falang_post->is_post_type_translatable($post->post_type)) { $caption = $falang_post->translate_post_field($post, 'post_excerpt', $this->get_current_language(), $caption); } return $caption; } /** * Add language to comment form * @since 1.3.10 */ public function comment_form_additional_fields () { echo ''; } /** * Fix comment redirect * use part of admin/class-falang-admin.php * method translate_sample_permalink * TODO must be done probably before during permalink creation * * @since 1.3.10 */ public function translate_comment_post_redirect($location, $comment){ $post = get_post($comment->comment_post_ID); $falang_post = new \Falang\Core\Post($post->ID); if ($falang_post->is_post_type_translatable($post->post_type)) { $current = $this->current_language; if ( get_option( 'permalink_structure' ) ) { $translation = $falang_post->translate_cpt($post->post_type, $this->get_current_language(), $post->post_type); if ($this->model->get_option('show_slug') || !$this->is_default()) { $location = str_replace("%{$post->post_type}-slug%", $this->get_current_language()->slug . '/' .$translation, $location); } else { $location = str_replace("%{$post->post_type}-slug%", $translation, $location); } } else { $location = add_query_arg( array( 'lang' => $this->get_current_language()->slug ), $location ); } } return $location; } /* * $approved 1 if the comment is approved, 0 if not, 'spam' if spam. * @param int $comment_ID The comment ID. * @param int|string $comment_approved 1 if the comment is approved, 0 if not, 'spam' if spam. * @param array $commentdata Comment data. added in 4.5.0 * */ public function comment_post ($comment_ID,$comment_approved,$commentdata){ if (isset($_POST['lang'])){ $slug = sanitize_text_field($_POST['lang']); $language = $this->model->get_language_by_slug($slug); if (isset($language)){ return add_comment_meta($comment_ID,'language', $language->locale); } } return false; } /* * @from 1.3.33 use to translate term meta not fields (name, slug, description) * @update 1.3.35 remove notice get_term($object_id) null * @update 1.3.44 fix bug when value is an array not translated (ovronnaz.ch wpgridbuilder compatiblity) * */ public function translate_get_term_metadata($value,$object_id, $meta_key, $single, $meta_type ){ //don't translate default language and meta starting with a language translation (already translated) if( !$this->is_default() && strpos($meta_key,$this->create_prefix($this->get_current_language()->locale)) === false ){ remove_filter( 'get_term_metadata', array($this,'translate_get_term_metadata') ); $falang_taxo = new \Falang\Core\Taxonomy(); $term = get_term($object_id); //1.3.44 //fix wpgridbuilder extra field meta not set correctly in translation even if the meta are set in the term background color if (isset($term) && isset($value)) { $value = $falang_taxo->translate_term_meta($term, $meta_key, $single, $this->get_current_language()); } add_filter('get_term_metadata', array($this, 'translate_get_term_metadata'), 10, 5); } return $value; } }