← All changes
|
includes/integrations/class-multilingual-manager.php
+53
-1
1.26.0
→
2.7.0
View file →
| @@ -80,8 +80,9 @@ | ||
| 80 | 80 | // Keep sitemap queries covering every language. Registered for admin |
| 81 | 81 | // and front end alike: the sitemap can be generated from either. |
| 82 | 82 | add_filter('thinkrank_sitemap_query_args', [$this, 'filter_sitemap_query_args']); |
| 83 | 83 | add_filter('thinkrank_sitemap_term_query_args', [$this, 'filter_sitemap_term_query_args']); |
| 84 | + add_filter('thinkrank_sitemap_post_permalink', [$this, 'localize_sitemap_permalink'], 10, 2); | |
| 84 | 85 | |
| 85 | 86 | if (!is_admin()) { |
| 86 | 87 | // WPML prints at wp_head priority 1 and Polylang at 10, so run |
| 87 | 88 | // after both: by then we know whether anything was printed. |
| @@ -322,14 +323,17 @@ | ||
| 322 | 323 | * |
| 323 | 324 | * @return array<int, array{code: string, locale: string, url: string, is_default: bool}> |
| 324 | 325 | */ |
| 325 | 326 | private function get_wpml_alternates(): array { |
| 327 | + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML/core hook, not ours to name. | |
| 326 | 328 | $languages = apply_filters('wpml_active_languages', null, ['skip_missing' => 1]); |
| 327 | 329 | if (!is_array($languages) || empty($languages)) { |
| 328 | 330 | return []; |
| 329 | 331 | } |
| 330 | 332 | |
| 333 | + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML/core hook, not ours to name. | |
| 331 | 334 | $default = (string) apply_filters('wpml_default_language', null); |
| 335 | + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- WPML/core hook, not ours to name. | |
| 332 | 336 | $current = (string) apply_filters('wpml_current_language', null); |
| 333 | 337 | $out = []; |
| 334 | 338 | |
| 335 | 339 | foreach ($languages as $language) { |
| @@ -445,9 +449,11 @@ | ||
| 445 | 449 | $default = (string) ($settings['default-language'] ?? ''); |
| 446 | 450 | |
| 447 | 451 | // TranslatePress tracks the language being rendered on a global rather |
| 448 | 452 | // than through an accessor. |
| 453 | + // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase, WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedVariableFound -- TranslatePress' own global; the name is theirs. | |
| 449 | 454 | global $TRP_LANGUAGE; |
| 455 | + // phpcs:ignore WordPress.NamingConventions.ValidVariableName.VariableNotSnakeCase -- TranslatePress' own global; the name is theirs. | |
| 450 | 456 | $current = is_string($TRP_LANGUAGE) ? $TRP_LANGUAGE : ''; |
| 451 | 457 | |
| 452 | 458 | $out = []; |
| 453 | 459 | |
| @@ -593,8 +599,54 @@ | ||
| 593 | 599 | * |
| 594 | 600 | * @param array<string, mixed> $args Query args. |
| 595 | 601 | * @return array<string, mixed> |
| 596 | 602 | */ |
| 603 | + /** | |
| 604 | + * Resolve a sitemap entry's permalink in the post's own language. | |
| 605 | + * | |
| 606 | + * The sitemap query runs with suppress_filters pinned (see | |
| 607 | + * filter_sitemap_query_args) so every language's rows are fetched — but | |
| 608 | + * that also strips WPML's chance to contextualise the permalink, and the | |
| 609 | + * debounced cron rebuild runs with no language context at all. Each | |
| 610 | + * translation therefore resolved to the default-language URL: N sitemap | |
| 611 | + * entries with different lastmod and images sharing one identical <loc> | |
| 612 | + * (#409). | |
| 613 | + * | |
| 614 | + * WPML's stateless conversion API fixes it per row: look up the post's | |
| 615 | + * own language, then ask wpml_permalink for the URL in that language. | |
| 616 | + * Both are documented WPML hooks and no-op safely when absent. Polylang | |
| 617 | + * needs none of this — its permalink filtering rides post_link, which | |
| 618 | + * get_permalink() applies regardless of suppress_filters — and | |
| 619 | + * TranslatePress translates rendered output without duplicating posts. | |
| 620 | + * | |
| 621 | + * @since 2.0.1 | |
| 622 | + * @param string $url Permalink as WordPress resolved it. | |
| 623 | + * @param \WP_Post $post Post the entry describes. | |
| 624 | + * @return string | |
| 625 | + */ | |
| 626 | + public function localize_sitemap_permalink(string $url, \WP_Post $post): string { | |
| 627 | + if ($this->provider !== 'wpml') { | |
| 628 | + return $url; | |
| 629 | + } | |
| 630 | + | |
| 631 | + // WPML's own documented filters; the prefix rule does not apply to a | |
| 632 | + // third-party hook we are consuming rather than declaring. | |
| 633 | + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound | |
| 634 | + $lang = apply_filters('wpml_element_language_code', null, [ | |
| 635 | + 'element_id' => $post->ID, | |
| 636 | + 'element_type' => 'post_' . $post->post_type, | |
| 637 | + ]); | |
| 638 | + | |
| 639 | + if (!is_string($lang) || $lang === '') { | |
| 640 | + return $url; | |
| 641 | + } | |
| 642 | + | |
| 643 | + // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound | |
| 644 | + $localized = apply_filters('wpml_permalink', $url, $lang, true); | |
| 645 | + | |
| 646 | + return is_string($localized) && $localized !== '' ? $localized : $url; | |
| 647 | + } | |
| 648 | + | |
| 597 | 649 | public function filter_sitemap_query_args(array $args): array { |
| 598 | 650 | if ($this->provider === 'polylang') { |
| 599 | 651 | // Polylang filters through parse_query, which suppress_filters does |
| 600 | 652 | // not bypass. An empty language disables its language clause. |
| @@ -618,8 +670,9 @@ | ||
| 618 | 670 | // |
| 619 | 671 | // Measured against WPML 4.9.5: setting suppress_filters => false cut the |
| 620 | 672 | // sitemap down to the active language, and a 'lang' => 'all' argument |
| 621 | 673 | // was ignored outright, so neither is used here. |
| 674 | + // phpcs:ignore WordPressVIPMinimum.Performance.WPQueryParams.SuppressFilters_suppress_filters -- Dropping it narrows the sitemap to the active language under WPML; see the note above. | |
| 622 | 675 | $args['suppress_filters'] = true; |
| 623 | 676 | |
| 624 | 677 | return $args; |
| 625 | 678 | } |
| @@ -640,6 +693,5 @@ | ||
| 640 | 693 | // so there is nothing to add for it here. TranslatePress does not |
| 641 | 694 | // duplicate terms per language at all, so likewise nothing to do. |
| 642 | 695 | return $args; |
| 643 | 696 | } |
| 644 | - | |
| 645 | 697 | } |