monitoring stron www Cyberapis - Changelog #7

Cyberapis v0.14.0 — sitemap XML, hreflang i wielojęzyczne SEO

Jak Google indeksuje strony wielojęzyczne — sitemap XML i hreflang od podszewki

Wielojęzyczne SEO to jeden z najbardziej niedocenianych aspektów technicznej optymalizacji strony. Wiele serwisów po prostu serwuje treść w różnych językach pod tym samym URL-em (przez Accept-Language), nie zdając sobie sprawy, że Googlebot wysyła nagłówek Accept-Language: en — i nigdy nie zobaczy wersji strony w języku alternatywnym.

Rozwiązaniem jest sitemap XML z tagami xhtml:link rel="alternate" hreflang="pl/en" — mechanizm, który mówi Google: "ten sam artykuł istnieje pod dwoma różnymi URL-ami, w dwóch językach. Oto oba. Nie traktuj ich jako duplicate content — to są warianty językowe." Każdy URL w sitemapie zawiera odnośniki do wszystkich swoich wariantów językowych, tworząc siatkę wzajemnych powiązań.

Dzięki temu hreflang nie wymusza przedrostków w URL — sitemap XML dostarcza Google te same informacje bez zmiany struktury adresów. Dla pełnej poprawności warto jednak dorzucić też <link rel="alternate" hreflang="..."> w <head> każdej strony. Google rekomenduje oba mechanizmy równolegle — sitemap do odkrywania wariantów, HTML <link> jako silniejszy sygnał.

Cyberapis v0.14.0 przebudowuje cały system sitemap od zera. Zamiast polegać na crawlerze (który generował URL-e z localhosta i wciągał strony admina), sitemap jest teraz budowany manualnie z Post::indexable(), DocsPage::indexable() i stron statycznych. Każdy wpis bloga i strona dokumentacji dostaje dwa wpisy url — dla slugu EN i PL — z wzajemnymi hreflang alternatywami. Do tego dochodzi auto-regeneracja przy każdej zmianie w Filament i humans-readable HTML sitemap pod /sitemap.

Dodatkowo system slugów został przepisany: PostController::show() szuka teraz przez slug->en OR slug->pl, a nie tylko slug dla bieżącego locale. To oznacza, że Googlebot (który zawsze wysyła Accept-Language: en) może odkryć i zindeksować polskie URLe.

Nowe funkcjonalności

Przebudowany sitemap XML

Ręczna konstrukcja URL-i zamiast crawlingu — filtruje tylko is_published=true AND is_indexable=true, obejmuje 6 stron statycznych + wszystkie posty bloga + wszystkie strony dokumentacji. XSL stylesheet do czytelnego widoku w przeglądarce.

Wielojęzyczny sitemap z hreflang

Każdy wpis bloga i strona dokumentacji dostaje dwa wpisy url (EN slug + PL slug) z wzajemnymi tagami xhtml:link rel="alternate" hreflang="en/pl". Wyszukiwarki mogą teraz odkrywać i indeksować treści w obu językach.

Auto-regeneracja sitemap

Modele Post i DocsPage wyzwalają sitemap:generate przy eventach saved/deleted — sitemap jest zawsze aktualny w ciągu sekund od zmiany w Filament. Codzienny cron działa jako fallback.

Pole is_indexable

Dodane do tabel posts i docs_pages. Przełączane w Filament. Gdy wyłączone, strona dostaje meta name="robots" content="noindex" i jest wykluczana z sitemap.

Polskie URLe stron prawnych

/regulamin-serwisu i /polityka-prywatnosci z hreflang alternatywami do angielskich /terms i /privacy-policy. Linki w stopce używają lokalnie odpowiednich URL-i.

HTML sitemap dla ludzi

Strona /sitemap renderuje live'ową, zawsze aktualną listę wszystkich indeksowalnych stron pogrupowanych według sekcji (statyczne, blog, dokumentacja).

Zlokalizowana stopka

Klucz tłumaczenia footer.legal zastępuje zahardcodowany nagłówek. Linki prawne dostosowane do języka (PL vs EN URL-e).

Naprawione błędy

Post::toSitemapTag() — martwy kod

Przedwczesny return route('blog.show', $this) generował URLe z ID zamiast slugów (/blog/5 zamiast /blog/the-slug). Blok setLastModificationDate/setPriority nigdy się nie wykonywał. Przepisane na poprawne obiekty Url z hreflang alternatywami.

Localhost w sitemap

Usunięto generację opartą na crawlerze (SitemapGenerator::create()), który pobierał APP_URL z env (często localhost w dev). Wszystkie URLe używają teraz helpera route(), który rozwiązuje się poprawnie per środowisko.

/admin/* w sitemap

Crawler wciągał strony administracyjne. Ręczna konstrukcja URL-i obejmuje tylko publiczne routy.

Zmiany i ulepszenia

Wyszukiwanie slugów cross-locale

PostController::show() i DocsController::show() szukają teraz przez slug->en OR slug->pl zamiast tylko locale sesji. Polskie slugi działają nawet gdy przeglądarka ma Accept-Language: en (np. Googlebot). Dopasowane locale ustawiane przez app()->setLocale(), żeby UI pasował do języka treści.