Canonical și og:url rescrise de aplicație pe /en și /ru, spre versiunea română
Serverul trimite corect adresa canonică pentru fiecare limbă, dar imediat după ce pornește JavaScript-ul aplicația o suprascrie cu varianta fără prefix de limbă. Rezultatul: fiecare pagină în engleză și în rusă îi spune motorului de căutare că adevărata pagină e cea română. Google randează paginile cu JavaScript înainte să le indexeze, deci vede exact versiunea rescrisă, iar hreflang-ul corect emis de server e anulat de contradicție. Sunt 254 din cele 381 de adrese din sitemap, adică practic tot conținutul în două limbi din trei.
Ce am găsit
Serverul scrie canonical-ul corect, DOM-ul randat îl are greșit
Cerut prin curl (fără JavaScript):
/ru/therapy/recovery → <link rel="canonical" href="https://hyperice.md/ru/therapy/recovery" />
/en/about → <link rel="canonical" href="https://hyperice.md/en/about" />
/ru/ → <link rel="canonical" href="https://hyperice.md/ru" />
Citit din DOM-ul randat, în browser cu JavaScript pornit:
/ru/therapy/recovery → canonical = https://hyperice.md/therapy/recovery og:url = același
/en/about → canonical = https://hyperice.md/about og:url = același
/ru/ → canonical = https://hyperice.md/
Atributul lang de pe <html> rămâne corect (en pe /en/about) — se pierde doar adresa canonică și og:url.
Mecanismul, la linie
frontend/src/main.tsx:17 execută readLanguage(window.location.pathname) și dă rezultatul ca basename lui BrowserRouter (frontend/src/main.tsx:52). Prin urmare useLocation().pathname, citit în frontend/src/components/Seo.tsx:116 (const path = canonicalPath ?? pathname;), nu conține niciodată /ru sau /en. La frontend/src/components/Seo.tsx:121, absolute(path) lipește doar window.location.origin, iar frontend/src/components/Seo.tsx:129 apelează upsertCanonical(canonical). Funcția de la liniile 48-57 selectează link[rel="canonical"] fără niciun filtru, deci găsește exact tag-ul scris de server și îi suprascrie href. Același lucru se întâmplă cu og:url la frontend/src/components/Seo.tsx:131.
Toate paginile trimit canonicalPath fără prefix de limbă: Home.tsx:27 ("/"), ProductDetail.tsx:183, CollectionRoute.tsx:118, BlogArticlePage.tsx:68, AboutUs.tsx:20, Contact.tsx:83, TherapyLandingPage.tsx:123, legal/Terms.tsx:30, legal/Privacy.tsx:24, support/Faq.tsx:430, GiftCards.tsx:163, RecoveryRooms.tsx:23, HyperhubIndexPage.tsx:41, GiftGuide.tsx:48 (căi relative la frontend/src/pages/, respectiv frontend/src/components/ pentru ultima).
Partea de server e deja corectă și poate servi ca referință: backend/src/services/render-page.ts:103 construiește canonicalul prin localizePath(meta.canonicalPath, locale) din backend/src/lib/url-language.ts.
De făcut
-
În frontend/src/components/Seo.tsx, construiește canonicalul cu prefixul limbii curente, la fel ca pe server: prefixul dedus dini18n.resolvedLanguage(disponibil la linia 117) aplicat pestecanonicalPath. Variantă mai curată, de preferat:Seo.tsxnu mai atinge deloclink[rel="canonical"]șiog:url, pentru că serverul le scrie deja corect pe toate rutele. -
Acoperă cazul cu prefix de limbă în frontend/src/test/Seo.test.tsx.
Criteriu de acceptare
- Pe
/ru,/en,/ru/therapy/recovery,/en/products/hypervolt-3-proși/ru/collections/hypervolt,canonicalșiog:urlcitite din DOM-ul randat conțin prefixul limbii și coincid cu adresa vizitată. -
frontend/src/test/Seo.test.tsxconține un test care pică dacă prefixul de limbă dispare din canonical.