feat(auto-redirect): browser-locale runtime + SEO override hreflang#29
Open
benjaminprojas wants to merge 1 commit into
Open
feat(auto-redirect): browser-locale runtime + SEO override hreflang#29benjaminprojas wants to merge 1 commit into
benjaminprojas wants to merge 1 commit into
Conversation
…wesomemotive/universally#69) Adds the visitor-side runtime for the auto-redirect feature whose dashboard + API land in awesomemotive/universally#70. First-time visitors get sent to the language version matching their browser locale; a switcher-set cookie wins on subsequent visits. - AutoRedirect.php: wp_enqueue_scripts hook that inlines the boot config (window.universallyAuto) and registers /assets/js/auto-redirect.js into <head>. Skips when the feature is disabled, the page is excluded, or there's nothing to match against. - auto-redirect.js: browser-only matcher. Reads cookie → checks current URL prefix → matches navigator.languages against explicit mappings then enabled target languages (RFC 4647 fallback). fetch+keepalive beacon to /connect/redirect-event before navigating. credentials: 'omit', SameSite cookie writes only via the switcher. - language-switcher.js: emits data-prefix on each anchor and writes the `universally_auto_lang` cookie (1-year Max-Age, SameSite=Lax, host-only) when the user picks a language. - helpers.php: emits an extra <link rel="alternate" hreflang="…"> for each override mapping where seoHreflang=true. Resolves URLs through the same urlPrefix lookup used by the main hreflang block. Adds universally_get_auto_redirect_config() helper backed by the 15-min site-config transient. - entry.php: busts the site-config / all-languages transients on visits to the Universally settings page so dashboard edits reflect on the WP side without waiting out the TTL. - LanguageSwitcher.php: drive-by fix for a pre-existing regex backreference issue in the current-path strip ('/$1' → '/'), which was producing '//foo' for paths like '/en/foo'.
|
Warning You have reached your daily quota limit. Please wait up to 24 hours and I will start processing your requests again! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Visitor-side runtime for the auto-redirect feature. Pairs with awesomemotive/universally#70 (dashboard, API, schema, plan gates) and awesomemotive/universally#69 (issue).
Summary
AutoRedirect.php—wp_enqueue_scriptshook that, when the feature is enabled for the site and the current page isn't excluded, inlines awindow.universallyAutoboot object and registers/assets/js/auto-redirect.jsinto<head>(not the footer — must run before paint). Boot config carriescurrentLang,mappings, enabledlanguages(for RFC 4647 fallback matching),basePath(current URL path with the language prefix stripped),beaconEndpoint, and the site'spk_public API key.auto-redirect.js— browser-only matcher. Decision order: cookie short-circuit → already on a translated URL? → matchnavigator.languagesagainst explicit mappings, then enabled target languages, with both exact and language-prefix passes. On a match: fires afetchbeacon withkeepalive: true+credentials: 'omit', thenlocation.replaces. Includes a BOT_RE skip path whencfg.skipBotsis on.language-switcher.js— emitsdata-prefixon each language anchor and writes theuniversally_auto_langcookie (Max-Age=31536000; Path=/; SameSite=Lax, host-only) on click. Empty string = source language. The runtime treats this cookie as authoritative on future loads.helpers.php— adds an override-hreflang block touniversally_get_hreflang_tags()that emits an extra<link rel="alternate" hreflang="{sourceLocale}" href="{targetUrl}">for each mapping withseoHreflang=true. Resolves URLs through the same$urlsByPrefixlookup the main loop builds. Adds theuniversally_get_auto_redirect_config()helper, backed by the 15-min site-config transient.entry.php— busts the site-config / all-languages transients on visits to the Universally settings page so dashboard edits don't have to wait out the TTL.universally.php— registers theAutoRedirectclass on plugin boot.LanguageSwitcher.php:240— pre-existingpreg_replaceregex backreference bug ('/$1'produced//foofor paths like/en/foo). Fixed to'/'.Review Context
/connect/site-configreports and no-ops onenabled: false.LanguageSwitcher.phpfix is the only behavior change to existing functionality.Safety properties worth a second look
credentials: 'omit'so it never carries cookies (no CSRF surface against the API endpoint).Domainattr),SameSite=Lax, notSecurebecause plugin runs on http+https sites — but cookie value is non-sensitive (URL prefix only).currentLangderives from both PHP-inlined value AND a path-derived fallback (currentLangFromPath), and the cookie/already-on-translated-URL short-circuits sit before the matcher. Multiple independent stops against/es/es/es/...-style redirect loops.AutoRedirect::loadConfig()strips emptysourceLocale/targetUrlPrefixrows defensively, and lowercases both ends, so a mixed-case dashboard entry can't desync fromcurrentLangFromPath()(which lowercases the path segment).Test plan
Tested against an http://localhost WordPress install pointed at the staging API.
Manual happy path
pt-BR).pt-pt → pt-BRwith SEO ON./pt-br/(or whatever the BR URL prefix is), onePOST /connect/redirect-eventfires (visible in the network panel before navigation;keepalive: truelets it survive the redirect), and a row appears in/platform/redirects~1 minute later.<link rel="alternate" hreflang="pt-pt" href="…/pt-br/…">appears alongside the standard hreflang block.Edge cases
curl -A "Googlebot/2.1") does NOT trigger a redirect whenskipBots: true(the runtime should early-return).<script src=".../auto-redirect.js">tag.entry.phptransient-bust fires).Drive-by fix verification
currentLang='en', visiting/en/some-pageno longer produces a//some-pageartifact in the language switcher's path-strip logic. Old behavior://some-page. New:/some-page.Notes
enabled: truein/connect/site-config.<head>(not footer) by design — auto-redirect must happen before paint.