static fn(): bool => class_exists( 'SitePress' ) || defined( 'ICL_SITEPRESS_VERSION' ), 'Polylang' => static fn(): bool => function_exists( 'pll_current_language' ), 'TranslatePress' => static fn(): bool => class_exists( 'TRP_Translate_Press' ) || defined( 'TRP_PLUGIN_VERSION' ), ); /** * Filters the list of "plugin name => detection callback" pairs used * to detect active multilingual plugins. * * @param array $known */ $known = (array) apply_filters( 'acez_seo_known_multilingual_plugins', $known ); foreach ( $known as $name => $check ) { if ( is_callable( $check ) && $check() ) { return (string) $name; } } return null; } public static function is_active(): bool { return null !== self::detect(); } /** * BCP 47 language tag (e.g. "pl-PL") for a specific post — asks the * multilingual plugin for the post's own language (a translation is a * separate WP_Post with its own language), falling back to the site locale. */ public static function post_language( \WP_Post $post ): string { if ( function_exists( 'pll_get_post_language' ) ) { $locale = (string) pll_get_post_language( $post->ID, 'locale' ); if ( '' !== $locale ) { return self::to_bcp47( $locale ); } } if ( class_exists( 'SitePress' ) || defined( 'ICL_SITEPRESS_VERSION' ) ) { $details = apply_filters( 'wpml_post_language_details', null, $post->ID ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- this hook name belongs to WP core or another plugin (WPML/core sitemaps) being invoked/filtered, not one of ours — must stay unprefixed to match their API. if ( is_array( $details ) && '' !== (string) ( $details['locale'] ?? '' ) ) { return self::to_bcp47( (string) $details['locale'] ); } } return self::to_bcp47( (string) get_locale() ); } /** * Locales configured in the multilingual plugin (for the per-language * overrides UI). Empty when none is active or the list can't be read. * * @return string[] e.g. ['pl_PL', 'en_US'] */ public static function languages(): array { if ( function_exists( 'pll_languages_list' ) ) { return array_map( 'strval', (array) pll_languages_list( array( 'fields' => 'locale' ) ) ); } if ( class_exists( 'SitePress' ) || defined( 'ICL_SITEPRESS_VERSION' ) ) { $languages = apply_filters( 'wpml_active_languages', null ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- this hook name belongs to WP core or another plugin (WPML/core sitemaps) being invoked/filtered, not one of ours — must stay unprefixed to match their API. $locales = array(); foreach ( (array) $languages as $language ) { $locale = (string) ( $language['default_locale'] ?? '' ); if ( '' !== $locale ) { $locales[] = $locale; } } return $locales; } return array(); } /** * Current request's language as a locale string (e.g. "pl_PL") — used as * the key for per-language setting overrides. */ public static function current_locale(): string { if ( function_exists( 'pll_current_language' ) ) { $locale = (string) pll_current_language( 'locale' ); if ( '' !== $locale ) { return $locale; } } return (string) get_locale(); } /** * Current request's language in BCP 47 form (e.g. "pl-PL") — the * `inLanguage` value for graphs that have no WP_Post to ask * post_language() about (taxonomy archives, the blog index). */ public static function current_language(): string { return self::to_bcp47( self::current_locale() ); } /** * The SOURCE (original) post of a translation, resolved through the * multilingual plugin's own documented API (M35 — researched, not * guessed): Polylang = the translation in the DEFAULT language * (pll_get_post_translations + pll_default_language), WPML = the element * flagged "original" in the post's translation group * (wpml_element_trid + wpml_get_element_translations). Null when no * plugin is active, the post IS the original, or it has no source. */ public static function translation_source( \WP_Post $post ): ?\WP_Post { if ( function_exists( 'pll_get_post_translations' ) && function_exists( 'pll_default_language' ) ) { $translations = (array) pll_get_post_translations( $post->ID ); $default_slug = (string) pll_default_language( 'slug' ); $source_id = (int) ( $translations[ $default_slug ] ?? 0 ); if ( $source_id > 0 && $source_id !== $post->ID ) { $source = get_post( $source_id ); return $source instanceof \WP_Post ? $source : null; } return null; } if ( class_exists( 'SitePress' ) || defined( 'ICL_SITEPRESS_VERSION' ) ) { $element_type = 'post_' . $post->post_type; $trid = apply_filters( 'wpml_element_trid', null, $post->ID, $element_type ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- this hook name belongs to WP core or another plugin (WPML/core sitemaps) being invoked/filtered, not one of ours — must stay unprefixed to match their API. if ( empty( $trid ) ) { return null; } $translations = apply_filters( 'wpml_get_element_translations', null, $trid, $element_type ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- this hook name belongs to WP core or another plugin (WPML/core sitemaps) being invoked/filtered, not one of ours — must stay unprefixed to match their API. foreach ( (array) $translations as $translation ) { if ( ! is_object( $translation ) ) { continue; } $element_id = (int) ( $translation->element_id ?? 0 ); if ( '1' === (string) ( $translation->original ?? '' ) && $element_id > 0 && $element_id !== $post->ID ) { $source = get_post( $element_id ); return $source instanceof \WP_Post ? $source : null; } } } return null; } /** * All language versions of this post, INCLUDING itself (M56 — hreflang * sitemap tags). Same underlying plugin APIs as `translation_source()`, * but the full sibling set instead of just the default-language member. * * @return array BCP 47 language tag => absolute URL */ public static function translations( \WP_Post $post ): array { if ( function_exists( 'pll_get_post_translations' ) ) { $map = array(); foreach ( (array) pll_get_post_translations( $post->ID ) as $translated_id ) { $translated_id = (int) $translated_id; if ( $translated_id <= 0 ) { continue; } $translated_post = get_post( $translated_id ); if ( ! $translated_post instanceof \WP_Post ) { continue; } $permalink = get_permalink( $translated_post ); if ( is_string( $permalink ) ) { $map[ self::post_language( $translated_post ) ] = $permalink; } } return $map; } if ( class_exists( 'SitePress' ) || defined( 'ICL_SITEPRESS_VERSION' ) ) { $element_type = 'post_' . $post->post_type; $trid = apply_filters( 'wpml_element_trid', null, $post->ID, $element_type ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- this hook name belongs to WP core or another plugin (WPML/core sitemaps) being invoked/filtered, not one of ours — must stay unprefixed to match their API. if ( empty( $trid ) ) { return array(); } $translations = apply_filters( 'wpml_get_element_translations', null, $trid, $element_type ); // phpcs:ignore WordPress.NamingConventions.PrefixAllGlobals.NonPrefixedHooknameFound -- this hook name belongs to WP core or another plugin (WPML/core sitemaps) being invoked/filtered, not one of ours — must stay unprefixed to match their API. $map = array(); foreach ( (array) $translations as $translation ) { if ( ! is_object( $translation ) ) { continue; } $element_id = (int) ( $translation->element_id ?? 0 ); if ( $element_id <= 0 ) { continue; } $translated_post = get_post( $element_id ); if ( ! $translated_post instanceof \WP_Post ) { continue; } $permalink = get_permalink( $translated_post ); $lang = (string) ( $translation->language_code ?? '' ); if ( is_string( $permalink ) && '' !== $lang ) { $map[ $lang ] = $permalink; } } return $map; } return array(); } private static function to_bcp47( string $locale ): string { return str_replace( '_', '-', $locale ); } }