Fermer

décembre 8, 2023

Un guide complet pour créer des sites multilingues avec Sitecore XM Cloud et Next.js / Blogs / Perficient

Un guide complet pour créer des sites multilingues avec Sitecore XM Cloud et Next.js / Blogs / Perficient


Historiquement, il était assez difficile d’ajouter des langues personnalisées au sitecore, car cela dépendait des cultures enregistrées dans le framework .net au niveau du système d’exploitation. Bien sûr, il existait quelques solutions de contournement, comme l’enregistrement de la culture personnalisée sur Windows, mais cela n’a fait qu’ajouter d’autres défis pour des scénarios tels que la présence de plusieurs serveurs de diffusion de contenu.

Heureusement, XM Cloud et SXA ont changé notre façon de gérer le problème, sans parler du retrait des serveurs de CD. Je vais montrer l’intégralité de la procédure pas à pas, en action – tout ce que vous devez faire du côté CM et de votre application principale Next.js, sur un exemple de composant personnalisé. Alors, c’est parti !

Au début, je n’avais qu’un site Web de base fonctionnant dans un environnement multisite basé sur SXA. De ce fait, il bénéficie d’un composant Header réactif avec des éléments de navigation. Ce sera un bon endroit pour localiser un sélecteur de langue déroulante, car c’est là que les utilisateurs s’attendent traditionnellement à ce qu’il se trouve. Mais d’abord, ajoutons des langues dans le système, car l’anglais est la langue par défaut et la seule que j’ai jusqu’à présent. Puisque je vis en Amérique du Nord, je vais ajouter deux langues les plus populaires – le français et l’espagnol, couramment parlés au Québec et au Mexique respectivement.

Ajout de langues

Dans XM Cloud, les langues se trouvent sous /sitecore/system/Languages dossier. Si une langue n’y est pas présente, vous ne pourrez pas l’utiliser, ce qui est mon cas. J’aime beaucoup la boîte de dialogue de sélection de langue fonctionnelle fournie par le système :

Ajout d'une langue dans le système

Une fois la langue arrivée dans un système, nous pouvons l’ajouter à un site Web spécifique. J’apprécie vraiment beaucoup d’échafaudages dans SXA qui sont basés sur des scripts SPE et voici un cas. Pour ajouter la langue du site, choisissez Scripts dans un menu contextuel, puis sélectionnez Ajouter la langue du site:

02

Précisez ensuite la langue de votre choix, ainsi que certains paramètres, notamment l’option de retour de la langue aux valeurs par défaut.

03

Dans XM Cloud, vous pouvez trouver une nouvelle section Culture personnalisée sur l’élément de langue, qui comporte deux champs importants :

  • Base ISO Culture Code: la langue de base que vous souhaitez utiliser, par exemple : en
  • Fallback Region Display Name: nom d’affichage pouvant être utilisé dans l’éditeur de contenu ou dans les pages Sitecore.

Désormais, le système et le site Web disposent de ces nouvelles langues. La prochaine étape consisterait à introduire un sélecteur de langue déroulant, dans le coin supérieur droit d’un en-tête.

Contrairement aux versions traditionnelles sans tête des plates-formes XP/XM, XM Cloud est entièrement sans tête et dessert l’intégralité de la mise en page d’un élément de page avec tous les composants via Experience Edge ou un point de terminaison GraphQL local s’exécutant sur un conteneur CM local avec le même schéma. . Voici à quoi cela ressemble dans GraphQL IDE Playground :

04

Il comporte deux parties : context qui contient des informations utiles sur le contexte Sitecore, telles que le chemin, le site, le mode d’édition, la langue actuelle et route avec la disposition complète des espaces réservés, des composants et des valeurs de champ. Étant donné que le sélecteur de langue fait partie de l’en-tête et est affiché sur chaque page, ce serait vraiment génial (et logique) pour fournir la liste des langues disponibles pour alimenter ce composant avec un contexte. Comment pouvons-nous y parvenir ?

La bonne nouvelle est tout à fait réalisable grâce à la personnalisation de la plateforme en étendant getLayoutServiceContextpipeline et en ajoutant ContextExtension processeur:

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
    <sitecore>
        <pipelines>
            <group groupName="layoutService">
                <pipelines>
                    <getLayoutServiceContext>
                        <processor type="JumpStart.Pipelines.ContextExtension, JumpStart" resolve="true" />
                    </getLayoutServiceContext>
                </pipelines>
            </group>
        </pipelines>
    </sitecore>
</configuration>

et la mise en œuvre :

using System.Collections.Generic;
using Sitecore.Data.Items;
using Sitecore.Diagnostics;
using Sitecore.Globalization;
using Sitecore.JavaScriptServices.Configuration;
using Sitecore.LayoutService.ItemRendering.Pipelines.GetLayoutServiceContext;

namespace JumpStart.Pipelines
{
    public class ContextExtension : Sitecore.JavaScriptServices.ViewEngine.LayoutService.Pipelines.
        GetLayoutServiceContext.JssGetLayoutServiceContextProcessor
    {
        public ContextExtension(IConfigurationResolver configurationResolver) : base(configurationResolver)
        {
        }

        protected override void DoProcess(GetLayoutServiceContextArgs args, AppConfiguration application)
        {
            Assert.ArgumentNotNull(args, "args");

            var langVersions = new List<Language>();
            Item tempItem = Sitecore.Context.Item;
            foreach (var itemLanguage in tempItem.Languages)
            {
                var item = tempItem.Database.GetItem(tempItem.ID, itemLanguage);
                if (item.Versions.Count > 0 || item.IsFallback)
                {
                    langVersions.Add(itemLanguage);
                }
            }

            args.ContextData.Add("Languages", langVersions);
        }
    }
}

Pour que ce code fonctionne, nous devons faire référence Sitecore.JavaScriptServices package. Un problème s’est produit après l’ajout d’un package : le compilateur a commis une erreur en exigeant de spécifier le numéro de version exact de ce package. Cela devrait être fait à packages.props à la racine d’un dépôt mono comme ci-dessous :

<PackageReference Update="Sitecore.JavaScriptServices.ViewEngine" Version="21.0.583" />

Après le déploiement, je reçois les langues du site dans le cadre de l’objet contextuel Sitecore pour chaque page :

08

Si pour une raison quelconque tu le fais pas voir la langue dans la sortie graphQL, mais celle-ci existe à la fois dans le système et sur votre site – assurez-vous qu’elle a une langue de secours spécifiée :

05

Vous devez également configurer la langue de secours sur un système, conformément au documentation officielle. Je me suis retrouvé avec ce patch de configuration :

<?xml version="1.0"?>
<configuration xmlns:patch="http://www.sitecore.net/xmlconfig/" xmlns:set="http://www.sitecore.net/xmlconfig/set/" xmlns:role="http://www.sitecore.net/xmlconfig/role/">
  <sitecore>
    <settings>
      <setting name="ExperienceEdge.EnableItemLanguageFallback" value="true"/>
      <setting name="ExperienceEdge.EnableFieldLanguageFallback" value="true"/>
    </settings>
    <sites>
      <site name="shell">
        <patch:attribute name="contentStartItem">/Jumpstart/Jumpstart/Home</patch:attribute>
        <patch:attribute name="enableItemLanguageFallback">true</patch:attribute>
        <patch:attribute name="enableFieldLanguageFallback">true</patch:attribute>
      </site>
      <site name="website">
        <patch:attribute name="enableItemLanguageFallback">true</patch:attribute>
        <patch:attribute name="enableFieldLanguageFallback">true</patch:attribute>
      </site>
    </sites>
  </sitecore>
</configuration>

J’ai également suivi la documentation et configuré le repli de langue au niveau d’un élément de page aux valeurs standard du modèle :

dix

Désormais, lorsque j’essaie d’accéder à ma page en tapant https://jumpstart.localhost/es-MX/Demo, le navigateur m’affiche 404. Pourquoi ?

07

Cela se produit car malgré l’ajout de langues dans XM Cloud CM et même la spécification de la solution de secours, l’application principale next.js ne sait rien de ces langues et ne peut pas desservir les routes correspondantes. La bonne nouvelle est que le framework prend facilement en charge cela en ajoutant simplement les langues servies dans next.config.js d’une application JSS pertinente :

i18n: {
  locales: [
    'en',
    'fr-CA',
    'es-MX',
  ],
  defaultLocale: jssConfig.defaultLanguage,
  localeDetection: false,
}

Après le redémarrage de l’application JSS et lors de l’actualisation d’une page, il n’y a plus d’erreur 404. Si c’est bien fait, vous verrez peut-être déjà un en-tête.

Mais dans mon cas la page était vierge : pas d’erreur, mais pas d’en-tête. La raison en est assez évidente : puisque je bénéficie de composants réutilisables grâce à une architecture véritablement multisite de SXA, mon composant d’en-tête appartient à une mise en page partielle, qui à son tour appartient à un site Web partagé. Devinez quoi? Il n’a pas de langues installées, il faut donc répéter l’installation des langues pour le Shared site également. Une fois terminé, tout fonctionne comme prévu et vous voyez l’en-tête de la mise en page partielle d’un site partagé :

09

Sélecteur de langue déroulant

Maintenant, j’ai décidé d’implémenter un composant déroulant de sélection de langue dans le coin supérieur droit d’un en-tête qui récupère toutes les langues disponibles dans le contexte et permet de basculer. Cela ressemblera à ceci :

import { SitecoreContextValue } from '@sitecore-jss/sitecore-jss-nextjs';
import { useRouter } from 'next/router';
import { ParsedUrlQueryInput } from 'querystring';
import { useEffect, useState } from 'react';
import { ComponentProps } from 'lib/component-props';
import styles from './LanguageSelector.module.css';

export type HeaderContentProps = ComponentProps & {
  pathname?: string;
  asPath?: string;
  query?: string | ParsedUrlQueryInput;
  sitecoreContext: SitecoreContextValue;
};

const LanguageSelector = (props: HeaderContentProps): JSX.Element => {
  const router = useRouter();
  const [languageLabels, setLanguageLabels] = useState([]);

  const sxaStyles = `${props.params?.styles || ''}`;

  const languageNames = new Intl.DisplayNames(['en'], { type: 'language' });

  const languageList = props.sitecoreContext['Languages'] as NodeJS.Dict<string | string>[];

  useEffect(() => {
    const labels = languageList.map((language) => languageNames.of(language['Name']));

    setLanguageLabels(labels);
  }, []);

  const changeLanguage = (lang: string) => {
    if (props.pathname && props.asPath && props.query) {
      router.push(
        {
          pathname: props.pathname,
          query: props.query,
        },
        props.asPath,
        {
          locale: lang,
          shallow: false,
        }
      );
    }
  };

  const languageSelector = languageList && languageLabels.length > 0 && (
    <select
      onChange={(e) => changeLanguage(e.currentTarget.value)}
      className="languagePicker"
      value={props.sitecoreContext.language}
    >
      {languageList.map((language, index) => (
        <option
          key={index}
          value={language['Name']}
          label={languageLabels[index]}
          className="languageItem"
        >
          {languageNames.of(language['Name'])}
        </option>
      ))}
    </select>
  );

  return (
    <>
      <div className={`${styles.selector} ${sxaStyles}`}>{languageSelector}</div>
    </>
  );
};

export default LanguageSelector;

Depuis que j’ai rendu l’en-tête responsive avec un menu « hamburger » vu sur mobiles, je fais également référence aux styles responsive pour ce composant :

.selector {
    float: right;
    position: relative;
    top: 13px;
    right: 40px;
}

@media screen and (max-width: 600px) {
    .selector {
        right: 0px;
    }
}

Maintenant, il peut être utilisé depuis l’en-tête comme :

<LanguageSelector pathname={pathname} asPath={asPath} query={query} sitecoreContext={sitecoreContext} {...props} />

et en effet, cela a l’air et fonctionne bien :

11

le passage à l’espagnol exploite correctement next.js pour changer de contexte linguistique et modifier l’URL :

12

Passons maintenant à un site Web multilingue en ajoutant un composant de démonstration et en le rejouant.

Ajout d’un composant

Pour le bien de l’expérience, j’ai décidé d’utiliser quelque chose de basique : un composant de texte enrichi étendu qui, en plus d’une source de données, reçoit également la couleur d’arrière-plan des paramètres de rendu. Il y a 3 lignes avec :

  • la ligne supérieure en gras est toujours statique et n’est qu’un nom codé en dur du composant, elle ne doit pas être traduite
  • la ligne médiane est interne au rendu du composant, donc je ne peux pas la récupérer à partir de la source de données, utilisez donc plutôt le dictionnaire
  • celle du bas est la seule ligne modifiable dans Pages/EE et provient de l’élément de source de données, comme avec le RichText original

Voici à quoi cela ressemble sur une page :

13

Et voici son code (ColorRichText.tsx) :

import React from 'react';
import { Field, RichText as JssRichText } from '@sitecore-jss/sitecore-jss-nextjs';
import { useI18n } from 'next-localization';

interface Fields {
  Text: Field<string>;
}

export type RichTextProps = {
  params: { [key: string]: string };
  fields: Fields;
};

export const Default = (props: RichTextProps): JSX.Element => {
  const text = props.fields ? (
    <JssRichText field={props.fields.Text} />
  ) : (
    <span className="is-empty-hint">Rich text</span>
  );
  const id = props.params.RenderingIdentifier;

  const { t } = useI18n();

  return (
    <div
      className={`component rich-text ${props.params.styles?.trimEnd()}`}
      id={id ? id : undefined}
    >
      <div className="component-content">
        <h4>Rich Text with Background Color from Rendering Parameters</h4>
        <span>{t('Rendering Parameters') || 'fallback content also seen in EE'}: </span>
        {text}
        <style jsx>{`
          .component-content {
            background-color: ${props.params.textColor
              ? props.params.textColor?.trimEnd()
              : '#FFF'};
          }
        `}</style>
      </div>
    </div>
  );
};

Quelle est la particularité également de ce composant, j’utilise I18n pour accéder aux éléments du dictionnaire, consultez ces lignes :

import { useI18n } from 'next-localization';
const { t } = useI18n();
<span>{t('Rendering Parameters') || 'fallback content, it is also seen in EE when defaults not configured'}: </span>

Créez ensuite une version de chaque langue pour l’élément de source de données et fournissez le contenu localisé. Vous devez créer au moins une version par langue pour éviter de revenir à la langue par défaut – l’anglais. La même chose s’applique également à l’élément du dictionnaire :

14

Le résultat ressemble à ci-dessous :

16

15

Paramètres de rendu

Maintenant, vous avez probablement remarqué que la version anglaise du composant a un fond jaune. Cela vient des paramètres de rendu en action configurés par composant afin que les éditeurs puissent choisir la couleur d’arrière-plan souhaitée dans une liste déroulante (bien sûr, c’est un exemple très simplifié à des fins de démonstration).

22

Ce qui est intéressant dans la localisation, c’est que vous pouvez également personnaliser les paramètres de rendu par langue (ou gardez-les partagés par la langue de secours).

Les paramètres de rendu sont un peu délicats, car ils sont stockés dans le fichier __Renderings et __Final Renderings champs d’un élément de page (pour les mises en page partagées et versionnées en conséquence), dérivé de Standard template (/sitecore/Templates/System/Templates/Standard template). Cela signifie que lorsque vous accédez à une page avec une langue de secours, vous ne pouvez pas spécifier les paramètres de rendu pour cette langue, sauf si vous créez une version de la page entière. L’éditeur de contenu et EE vous empêcheront de le faire tant qu’il existe une langue de secours pour l’élément de page :

Éditeur de contenu

Éditeur d'expérience

La création d’une nouvelle version linguistique peut représenter un effort très excessif car, par défaut, elle vous obligera à configurer l’ensemble des composants d’ajout de mise en page (encore) et réaffectation de toutes les sources de données pour cette version. Cela pourrait être simplifié en copiant la mise en page souhaitée à partir du versionné __Final Renderings champ vers le partage __Renderings champ, de sorte que chaque fois que vous créez une nouvelle version linguistique pour un élément de page, vous « héritez » de cette conception partagée et ne la créez pas à partir de zéro. Cependant, cette approche comporte également quelques mises en garde – vous pouvez trouver des discussions à ce sujet (ici et ).

Dans tous les cas, nous avons le résultat souhaité :

19

version anglaise

20

version française

21

version espagnole

J’espère que cet article vous sera utile !






Source link

décembre 8, 2023