SEO Schema Enrichment

PerfLocale augments the JSON-LD structured data emitted by every built-in SEO addon so search engines and LLM crawlers see the correct per-language identity of each page and the cross-links between translated siblings. On by default; zero runtime cost - enrichment runs inside the filter callback the SEO plugin is already executing.

What gets added

For every entity whose @type is one of WebPage, ItemPage, CollectionPage, AboutPage, ContactPage, FAQPage, QAPage, ProfilePage, Article, NewsArticle, BlogPosting, TechArticle, ScholarlyArticle, Product, Recipe, HowTo, or Event:

  • inLanguage - canonical BCP 47 / RFC 5646 tag derived from the current language’s locale (e.g. fr-FR, en-US, fr). Region subtag is uppercase, language subtag lowercase; strict schema validators reject lowercase regions even though BCP 47 itself is case-insensitive.
  • workTranslation - array of sibling translation URLs wrapped as CreativeWork references

Organization, Person, and WebSite entities are left alone - those are language-neutral identities that apply to the whole site.

Example output

{
	"@context": "https://schema.org",
	"@graph": [
		{
			"@type": "WebPage",
			"@id": "https://example.com/fr/about/#webpage",
			"url": "https://example.com/fr/about/",
			"name": "À propos",
			"inLanguage": "fr-FR",
			"workTranslation": [
				{ "@type": "CreativeWork", "url": "https://example.com/about/" },
				{ "@type": "CreativeWork", "url": "https://example.com/de/uber-uns/" }
			]
		}
	]
}

Supported SEO plugins

Enrichment works out of the box with every built-in PerfLocale SEO addon:

  • Yoast SEO
  • All in One SEO
  • Rank Math
  • SEOPress
  • Slim SEO
  • The SEO Framework

When the corresponding addon is active and the enrichment setting is on, PerfLocale hooks the relevant schema filter once per request. Zero work when the SEO plugin isn’t installed or when enrichment is disabled.

Turning it off

Admin: PerfLocale → Settings → Advanced → SEO Schema Enrichment.

Programmatic:

add_filter( 'perflocale/seo/schema_enrichment_enabled', '__return_false' );

When off, PerfLocale does not register the schema-graph filters on any SEO addon - literally no enrichment callback runs.

Shared logic - SchemaEnricher

All six addons delegate to PerfLocale\Seo\SchemaEnricher. That class exposes two public static methods addon authors can use to enrich their own JSON-LD:

use PerfLocale\Seo\SchemaEnricher;

// Enrich a full {@context, @graph:[]} payload or a flat entity list.
$graph = SchemaEnricher::enrich_graph( $graph, $post_id = null );

// Single-entity version for SEO plugins that emit one blob.
$entity = SchemaEnricher::enrich_single( $entity, $post_id = null );

Sibling URLs come from UrlConverter::get_translations_for_current_page() - already per-request cached, so enriching across multiple addons on the same page reuses one sibling-URL lookup.

Performance notes

  • One memoised per-request context (current hreflang + sibling-URL map) shared across every enrichment call
  • No database queries added - sibling URLs are already cached for hreflang and the switcher
  • Multisite-safe: the memo resets on switch_blog
  • When the setting is off or the addon’s SEO plugin isn’t active, the filter isn’t registered at all