ricketywrecked
Aktives Mitglied
Hallo zusammen,
beim Update auf 5.8.0 haben wir einen Sprachfehler diagnostiziert, der vermutlich alle mehrsprachigen Shops betrifft, aber im Alltag fast unsichtbar ist — weil er nur cookielose Erstaufrufe trifft. Genau die sehen aber Suchmaschinen-Crawler bei jedem Zugriff.
Umgebung: JTL- Shop 5.8.0 (der betroffene Code ist byte-identisch bereits in 5.7.2 vorhanden), mehrsprachig DE (Standardsprache) / EN / IT, NOVA- Child-Template, Standard-Objektcache aktiv.
Symptom: Ruft man ohne Session-Cookie (Inkognito-Fenster — oder eben als Googlebot) direkt eine EN-/IT-SEO-URL auf, ist die Seite korrekt lokalisiert (Kategorien, Artikel, Bilder, Sprachwahl) — aber alle Texte, die aus Link-Objekten stammen, kommen in der Standardsprache:
- Footer-Linkgruppen-Labels (Impressum, AGB, Datenschutz, Widerrufsrecht … statt Note legali, Termini e condizioni …)
- Breadcrumb auf Link-/Spezialseiten
- H1/Titel von Spezialseiten (bei uns aufgefallen an der neuen Gewährleistungsseite aus 5.8.0: /Garanzia-legale zeigte H1 „Gesetzliche Gewährleistung")
- die Links im Consent-Manager
Ab dem zweiten Aufruf (Session-Cookie vorhanden) ist alles korrekt — deshalb fällt es beim normalen Testen im Browser nie auf.
Reproduktion:
1. Mehrsprachigen Shop mit aktivem Objektcache verwenden, Standardsprache ≠ Zielsprache.
2. Inkognito-Fenster öffnen, direkt eine SEO-URL der Nicht-Standardsprache aufrufen (z. B. die EN-Gewährleistungsseite).
3. Footer-Labels/Breadcrumb/H1 erscheinen in der Standardsprache. Nach einem weiteren Klick (Session existiert) ist alles korrekt.
Ursache (Code-Analyse, 5.8.0 = 5.7.2):
LinkGroupList lädt den Cache-Eintrag „linkgroups" (CACHING_GROUP_CORE) sehr früh im Request. Beim Unserialisieren ruft JTL\Link\Link::__wakeup() → JTL\Router\RoutableTrait::initLanguageID() → Shop::getLanguageID() auf. Zu diesem Zeitpunkt ist die Request-Sprache aus dem SEO-Slug noch nicht ermittelt — es wird also die Standardsprache in currentLanguageID jedes Link-Objekts eingefroren. Alle parameterlosen Aufrufe von getName() / getTitle() / getMetaTitle() liefern anschließend die Standardsprache, obwohl Shop::getLanguageID() zur Renderzeit längst korrekt ist. (DB und Cache-Inhalt sind dabei nachweislich korrekt — es ist rein die eingefrorene Sprach-ID.)
Historie im Issuetracker — dreimal Symptom, nie die Wurzel:
- SHOP-6552 (2022, abgewiesen): beschreibt exakt dieses Verhalten — „Wird eine Sprachseite das allererste Mal geöffnet (noch keine Session), wird der Content in der Standardsprache ausgegeben; beim Aktualisieren dann korrekt" (dort auf der Startseite getestet, ab 5.1.3). Im Ticket bat bereits 07/2022 ein Partner um Prio-Behandlung (B2B-Kunde verlinkt direkt auf Sprachstartseiten) — das Ticket wurde dennoch abgewiesen, der Ablehnungsgrund ist nicht öffentlich einsehbar. Was damals offenbar fehlte, liefere ich unten nach: die konkrete Ursache im Code. Und ein Aspekt, der die Bewertung ändern dürfte: Suchmaschinen-Crawler sind IMMER sessionlos und sehen die falsche Sprache dauerhaft — das ist SEO-relevant, nicht kosmetisch.
- SHOP-3687 (gelöst 5.0.0): gleiche Baustelle, Auslöser „nach Anmeldung".
- SHOP-8352 (gelöst 5.5.0): Consent-Manager-Links — gefixt wurde nur der Sprachwechsel-Fall; im sessionlosen Erstaufruf sind die Consent-Links weiterhin in der Standardsprache (heute in 5.8.0 reproduziert).
- Historisch außerdem SHOP-1751 (2017, gelöst 4.06): CMS-Seiten-Sichtbarkeit griff erst beim zweiten Aufruf nach Login — dieselbe Mechanik-Familie (Link-Zustand hinkt dem Request-Kontext hinterher), damals beim Sichtbarkeits- statt Sprach-Aspekt.
Fix-Vorschlag: currentLanguageID beim __wakeup nicht einfrieren, sondern die parameterlosen Getter zur Laufzeit auf Shop::getLanguageID() zurückfallen lassen — oder die Link-Objekte nach der Sprachermittlung des Routers einmal re-initialisieren.
Workaround (bei uns produktiv im Einsatz und verifiziert): Im Child-Template-Bootstrap einen Listener registrieren, der die Link-Objekte re-initialisiert, sobald die Sprache feststeht:
(HOOK_SEOCHECK_ENDE feuert unmittelbar nachdem Shop::$kSprache gesetzt wurde; HOOK_LETZTERINCLUDE_INC in preRender() direkt vor dem Breadcrumb-Aufbau.) Damit sind Footer, Breadcrumb, Spezialseiten-Titel und Consent-Manager auch für cookielose Zugriffe korrekt lokalisiert.
Viele Grüße
beim Update auf 5.8.0 haben wir einen Sprachfehler diagnostiziert, der vermutlich alle mehrsprachigen Shops betrifft, aber im Alltag fast unsichtbar ist — weil er nur cookielose Erstaufrufe trifft. Genau die sehen aber Suchmaschinen-Crawler bei jedem Zugriff.
Umgebung: JTL- Shop 5.8.0 (der betroffene Code ist byte-identisch bereits in 5.7.2 vorhanden), mehrsprachig DE (Standardsprache) / EN / IT, NOVA- Child-Template, Standard-Objektcache aktiv.
Symptom: Ruft man ohne Session-Cookie (Inkognito-Fenster — oder eben als Googlebot) direkt eine EN-/IT-SEO-URL auf, ist die Seite korrekt lokalisiert (Kategorien, Artikel, Bilder, Sprachwahl) — aber alle Texte, die aus Link-Objekten stammen, kommen in der Standardsprache:
- Footer-Linkgruppen-Labels (Impressum, AGB, Datenschutz, Widerrufsrecht … statt Note legali, Termini e condizioni …)
- Breadcrumb auf Link-/Spezialseiten
- H1/Titel von Spezialseiten (bei uns aufgefallen an der neuen Gewährleistungsseite aus 5.8.0: /Garanzia-legale zeigte H1 „Gesetzliche Gewährleistung")
- die Links im Consent-Manager
Ab dem zweiten Aufruf (Session-Cookie vorhanden) ist alles korrekt — deshalb fällt es beim normalen Testen im Browser nie auf.
Reproduktion:
1. Mehrsprachigen Shop mit aktivem Objektcache verwenden, Standardsprache ≠ Zielsprache.
2. Inkognito-Fenster öffnen, direkt eine SEO-URL der Nicht-Standardsprache aufrufen (z. B. die EN-Gewährleistungsseite).
3. Footer-Labels/Breadcrumb/H1 erscheinen in der Standardsprache. Nach einem weiteren Klick (Session existiert) ist alles korrekt.
Ursache (Code-Analyse, 5.8.0 = 5.7.2):
LinkGroupList lädt den Cache-Eintrag „linkgroups" (CACHING_GROUP_CORE) sehr früh im Request. Beim Unserialisieren ruft JTL\Link\Link::__wakeup() → JTL\Router\RoutableTrait::initLanguageID() → Shop::getLanguageID() auf. Zu diesem Zeitpunkt ist die Request-Sprache aus dem SEO-Slug noch nicht ermittelt — es wird also die Standardsprache in currentLanguageID jedes Link-Objekts eingefroren. Alle parameterlosen Aufrufe von getName() / getTitle() / getMetaTitle() liefern anschließend die Standardsprache, obwohl Shop::getLanguageID() zur Renderzeit längst korrekt ist. (DB und Cache-Inhalt sind dabei nachweislich korrekt — es ist rein die eingefrorene Sprach-ID.)
Historie im Issuetracker — dreimal Symptom, nie die Wurzel:
- SHOP-6552 (2022, abgewiesen): beschreibt exakt dieses Verhalten — „Wird eine Sprachseite das allererste Mal geöffnet (noch keine Session), wird der Content in der Standardsprache ausgegeben; beim Aktualisieren dann korrekt" (dort auf der Startseite getestet, ab 5.1.3). Im Ticket bat bereits 07/2022 ein Partner um Prio-Behandlung (B2B-Kunde verlinkt direkt auf Sprachstartseiten) — das Ticket wurde dennoch abgewiesen, der Ablehnungsgrund ist nicht öffentlich einsehbar. Was damals offenbar fehlte, liefere ich unten nach: die konkrete Ursache im Code. Und ein Aspekt, der die Bewertung ändern dürfte: Suchmaschinen-Crawler sind IMMER sessionlos und sehen die falsche Sprache dauerhaft — das ist SEO-relevant, nicht kosmetisch.
- SHOP-3687 (gelöst 5.0.0): gleiche Baustelle, Auslöser „nach Anmeldung".
- SHOP-8352 (gelöst 5.5.0): Consent-Manager-Links — gefixt wurde nur der Sprachwechsel-Fall; im sessionlosen Erstaufruf sind die Consent-Links weiterhin in der Standardsprache (heute in 5.8.0 reproduziert).
- Historisch außerdem SHOP-1751 (2017, gelöst 4.06): CMS-Seiten-Sichtbarkeit griff erst beim zweiten Aufruf nach Login — dieselbe Mechanik-Familie (Link-Zustand hinkt dem Request-Kontext hinterher), damals beim Sichtbarkeits- statt Sprach-Aspekt.
Fix-Vorschlag: currentLanguageID beim __wakeup nicht einfrieren, sondern die parameterlosen Getter zur Laufzeit auf Shop::getLanguageID() zurückfallen lassen — oder die Link-Objekte nach der Sprachermittlung des Routers einmal re-initialisieren.
Workaround (bei uns produktiv im Einsatz und verifiziert): Im Child-Template-Bootstrap einen Listener registrieren, der die Link-Objekte re-initialisiert, sobald die Sprache feststeht:
PHP:
// templates/<Child>/Bootstrap.php, in boot() nach parent::boot():
$reInit = static function (): void {
try {
foreach (Shop::Container()->getLinkService()->getAllLinkGroups() as $linkGroup) {
foreach ($linkGroup->getLinks() as $link) {
if (\method_exists($link, 'initLanguageID')) {
$link->initLanguageID();
}
}
}
} catch (\Throwable) {
}
};
$dispatcher = \JTL\Events\Dispatcher::getInstance();
$dispatcher->listen('shop.hook.' . \HOOK_SEOCHECK_ENDE, $reInit);
$dispatcher->listen('shop.hook.' . \HOOK_LETZTERINCLUDE_INC, $reInit);
(HOOK_SEOCHECK_ENDE feuert unmittelbar nachdem Shop::$kSprache gesetzt wurde; HOOK_LETZTERINCLUDE_INC in preRender() direkt vor dem Breadcrumb-Aufbau.) Damit sind Footer, Breadcrumb, Spezialseiten-Titel und Consent-Manager auch für cookielose Zugriffe korrekt lokalisiert.
Viele Grüße