Auteur:
    Création:2026-07-08Dernière mise à jour:2026-08-22

    Documentation Analytique Intlayer

    @intlayer/analytics est un package compagnon optionnel qui vous indique quel contenu est réellement affiché à vos visiteurs — quelle page, dans quelle locale, et quel élément de contenu traduit spécifique — afin que vous puissiez comprendre votre audience et exécuter des tests A/B sur le contenu.

    Table des matières

    Ce qu'il suit

    @intlayer/analytics regroupe trois types d'événements anonymes en lots :

    ÉvénementCapturé oùCe qu'il vous indique
    page_viewNiveau Provider (IntlayerProvider)Quelle page et locale une session a consulté, lors du chargement initial, du changement de route ou du changement de locale.
    content_exposureNiveau Nœud (useIntlayer / plugins d'interpréteur)Quelle clé de dictionnaire / chemin de clé a été réellement résolu et affiché — et, si faisant partie d'une expérience, quelle variante.
    conversionPartout où vous appelez useConversion()Un objectif atteint (inscription, clic, achat…) attribué à la variante A/B à laquelle la session a été exposée.

    Les événements sont collectés en mémoire et envoyés sous forme d'une seule requête groupée (batch) environ toutes les 20 secondes — jamais à chaque frappe au clavier ou rendu — donc l'analytique n'impacte jamais le temps de premier rendu et n'ajoute pas une requête par interaction.

    Comment cela propulse les tests A/B sur le contenu

    Intlayer vous permet déjà de déclarer des Variantes de contenu (par exemple un dictionnaire hero-banner avec une variante control et une variante black_friday). @intlayer/analytics boucle la boucle :

    1. getVariant(experimentKey, variants) attribue de manière déterministe chaque session anonyme à une variante — une fonction pure de l'ID de session et de la clé d'expérience, donc l'attribution est stable dans toute la session et ne nécessite aucun aller-retour serveur avant le premier rendu (pas de scintillement, pas de décalage de mise en page).
    2. Chaque événement content_exposure transporte la variant qui a été affichée.
    3. useConversion() vous permet d'attribuer un objectif (par exemple "cta_click") à cette variante.
    4. L'endpoint des résultats d'expérience du tableau de bord compare les taux de conversion par variante, y compris la signification statistique (un test z).

    Installation

    @intlayer/analytics est une dépendance optionnelle de chaque package de framework (react-intlayer, next-intlayer, vue-intlayer, …) : la plupart des projets l'ont donc déjà. Installez-la explicitement si votre configuration ignore les dépendances optionnelles (npm install --no-optional, …) :

    bash
    npm install @intlayer/analytics
    

    Installer le package suffit à activer l'analytique : analytics.enabled vaut true par défaut, et @intlayer/config le résout à false dès que le package est introuvable dans votre projet. Si vous ne l'installez pas, chaque point d'intégration se résout en une opération vide (no-op) — voir Zéro coût quand non installé ci-dessous.

    Configuration

    L'analytique ne nécessite aucune configuration pour démarrer : elle est activée par défaut et réutilise le bloc de configuration editor existant pour son endpoint et sa clé de projet.

    intlayer.config.ts
    import type { IntlayerConfig } from "intlayer";
    
    const config: IntlayerConfig = {
      editor: {
        backendURL: "https://back.intlayer.org", // Également utilisé comme endpoint d'ingestion d'analytique
        clientId: "your-client-id", // Aussi utilisé comme clé de projet analytique
        clientSecret: "your-client-secret",
      },
    };
    
    export default config;
    
    • editor.backendURL — l'URL de base vers laquelle les événements analytiques sont envoyés (POST {backendURL}/api/analytics/events).
    • editor.clientId — la clé de projet publique attribuée à chaque événement ingéré. Elle agit également comme interrupteur d'activation : l'analytique reste complètement désactivée (et éliminée du code par tree-shaking, voir ci-dessous) jusqu'à ce que clientId soit configuré.

    Si vous auto-hébergez (self-host) Intlayer, l'analytique pointe automatiquement vers votre propre instance puisqu'elle partage editor.backendURL.

    Appeler l'API depuis le navigateur

    Le même jeton alimente un petit client sans identifiants, ce qui permet à un site statique ou une SPA de lire le contenu de son CMS au runtime sans serveur, sans server action et sans aucun secret dans le bundle :

    content.ts
    import { createPublicClient } from "@intlayer/api/public";
    
    const client = createPublicClient();
    
    const keys = await client.getDictionaryKeys();
    const [navbar] = await client.getDictionaries(["navbar"]);
    

    Il s'authentifie lui-même à partir de editor.clientId, l'échange, la mise en cache et le renouvellement sont gérés en interne. Les scopes délimitent ce qu'il peut atteindre : le contenu de dictionnaire publié et l'ingestion d'analytique. Tout le reste (pousser des dictionnaires, lire un projet, dépenser des crédits IA) nécessite un identifiant réel, donc un serveur ou un utilisateur connecté.

    Désactiver l'analytique

    Le bloc optionnel analytics permet d'ajuster — ou de désactiver — la collecte :

    intlayer.config.ts
    import type { IntlayerConfig } from "intlayer";
    
    const config: IntlayerConfig = {
      analytics: {
        enabled: false, // Par défaut : true — exclut toute l'intégration du bundle
        flushInterval: 20_000, // Millisecondes entre deux envois groupés
        sampleRate: 1, // Fraction des sessions enregistrées, de 0 (aucune) à 1 (toutes)
      },
    };
    
    export default config;
    

    Désinstaller @intlayer/analytics produit le même effet que enabled: false. Consultez la référence de configuration pour la liste complète des champs.

    Utilisation

    Suivi automatique au niveau du provider

    Aucune modification de code n'est requise. Une fois que @intlayer/analytics est installé et que editor.clientId est configuré, IntlayerProvider effectue automatiquement les actions suivantes :

    • initialise le client d'analytique au montage (mount),
    • enregistre un page_view au chargement initial,
    • enregistre un page_view à chaque changement de locale,
    • démarre la boucle de flush d'environ 20s et vide tous les événements restants au démontage / fermeture d'onglet (via navigator.sendBeacon, avec un repli vers fetch(..., { keepalive: true })).

    Le point d'entrée diffère selon le framework, mais dans tous les cas c'est celui que vous utilisez déjà pour configurer Intlayer, il n'y a donc rien de plus à ajouter :

    IntlayerProvider monte le provider d'analytique en interne.

    App.tsx
    import { IntlayerProvider } from "react-intlayer";
    
    const App = () => (
    <IntlayerProvider>
      <Router />
    </IntlayerProvider>
    );
    

    next-intlayer réexporte le IntlayerProvider de React, donc l'analytique est câblée de la même manière.

    app/[locale]/layout.tsx
    import { IntlayerProvider } from "next-intlayer";
    
    const LocaleLayout = ({ children }) => (
    <IntlayerProvider>{children}</IntlayerProvider>
    );
    
    export default LocaleLayout;
    

    Le plugin intlayer enregistre les hooks d'analytique sur le cycle de vie du composant racine.

    main.js
    import { createApp } from "vue";
    import { intlayer } from "vue-intlayer";
    import App from "./App.vue";
    
    const app = createApp(App);
    
    app.use(intlayer);
    
    app.mount("#app");
    
    Avec Nuxt, nuxt-intlayer installe le plugin pour vous : il n'y a rien à faire.

    setupIntlayer() démarre l'analytique depuis le composant qui configure Intlayer.

    src/routes/[[locale=locale]]/+layout.svelte
    <script lang="ts">
    import { setupIntlayer } from "svelte-intlayer";
    import type { Snippet } from "svelte";
    
    let { children, data }: { children: Snippet, data: LayoutData } = $props();
    
    $effect(() => {
      setupIntlayer(data.locale);
    });
    </script>
    
    {@render children()}
    

    IntlayerProvider monte le provider d'analytique en interne.

    app.tsx
    import { IntlayerProvider } from "preact-intlayer";
    
    const App = () => (
    <IntlayerProvider>
      <Router />
    </IntlayerProvider>
    );
    

    IntlayerProvider monte le provider d'analytique de façon paresseuse (lazy), afin que ce chunk reste hors du chemin critique.

    App.tsx
    import { IntlayerProvider } from "solid-intlayer";
    
    const App = () => (
    <IntlayerProvider>
      <Router />
    </IntlayerProvider>
    );
    

    provideIntlayer() inclut déjà provideIntlayerAnalytics().

    app.config.ts
    import { provideIntlayer } from "angular-intlayer";
    import type { ApplicationConfig } from "@angular/core";
    
    export const appConfig: ApplicationConfig = {
    providers: [provideIntlayer()],
    };
    
    N'utilisez provideIntlayerAnalytics() seul que si vous gérez les providers individuellement.

    Suivi automatique au niveau des nœuds

    Chaque fois que useIntlayer résout un élément de contenu pour l'affichage, l'interpréteur signale un événement content_exposure pour ce(tte) dictionaryKey + chemin de clé + locale exacte — là encore, aucune modification de code n'est requise. Les expositions répétées du même nœud dans une fenêtre de flush sont fusionnées en un seul événement avec un count, donc une liste qui se re-rend 50 fois n'envoie pas 50 événements.

    Suivi des conversions pour les tests A/B

    Utilisez useConversion() pour attribuer un objectif à la variante qu'une session a vue :

    CTAButton.tsx
    import { useConversion } from "react-intlayer";
    
    const CTAButton = () => {
    const trackConversion = useConversion();
    
    return (
      <button
        onClick={() =>
          trackConversion({
            experimentKey: "homepage-hero",
            variant: "black_friday",
            goal: "cta_click",
          })
        }
      >
        Commencer
      </button>
    );
    };
    
    CTAButton.tsx
    "use client";
    
    import { useConversion } from "next-intlayer";
    
    const CTAButton = () => {
    const trackConversion = useConversion();
    
    return (
      <button
        onClick={() =>
          trackConversion({
            experimentKey: "homepage-hero",
            variant: "black_friday",
            goal: "cta_click",
          })
        }
      >
        Commencer
      </button>
    );
    };
    
    useConversion est un hook client : marquez le composant "use client".
    CTAButton.vue
    <script setup lang="ts">
    import { useConversion } from "vue-intlayer";
    
    const trackConversion = useConversion();
    </script>
    
    <template>
    <button
      @click="
        trackConversion({
          experimentKey: 'homepage-hero',
          variant: 'black_friday',
          goal: 'cta_click',
        })
      "
    >
      Commencer
    </button>
    </template>
    
    CTAButton.svelte
    <script lang="ts">
    import { useConversion } from "svelte-intlayer";
    
    const trackConversion = useConversion();
    </script>
    
    <button
    onclick={() =>
      trackConversion({
        experimentKey: "homepage-hero",
        variant: "black_friday",
        goal: "cta_click",
      })}
    >
    Commencer
    </button>
    
    CTAButton.tsx
    import { useConversion } from "preact-intlayer";
    
    const CTAButton = () => {
    const trackConversion = useConversion();
    
    return (
      <button
        onClick={() =>
          trackConversion({
            experimentKey: "homepage-hero",
            variant: "black_friday",
            goal: "cta_click",
          })
        }
      >
        Commencer
      </button>
    );
    };
    
    CTAButton.tsx
    import { useConversion } from "solid-intlayer";
    
    const CTAButton = () => {
    const trackConversion = useConversion();
    
    return (
      <button
        onClick={() =>
          trackConversion({
            experimentKey: "homepage-hero",
            variant: "black_friday",
            goal: "cta_click",
          })
        }
      >
        Commencer
      </button>
    );
    };
    
    cta-button.component.ts
    import { Component } from "@angular/core";
    import { useConversion } from "angular-intlayer";
    
    @Component({
    selector: "app-cta-button",
    template: `<button (click)="onClick()">Commencer</button>`,
    })
    export class CtaButtonComponent {
    private trackConversion = useConversion();
    
    onClick() {
      this.trackConversion({
        experimentKey: "homepage-hero",
        variant: "black_friday",
        goal: "cta_click",
      });
    }
    }
    

    Résolution d'une variante côté client

    useExperiment() attribue une variante à la session et enregistre l'exposition qui devient le dénominateur du taux de conversion. Conditionnez l'affichage du sous-arbre dépendant de la variante à isAssigned afin qu'aucun visiteur ne voie l'éclair du témoin (control) avant que l'attribution ne soit résolue :

    variant est une simple chaîne de caractères.

    Hero.tsx
    import { useExperiment } from "react-intlayer";
    import { HeroBanner } from "./HeroBanner";
    
    export const Hero = () => {
    const { variant, isAssigned } = useExperiment("homepage-hero", [
      "default",
      "black_friday",
    ]);
    
    if (!isAssigned) return null;
    
    return <HeroBanner variant={variant} />;
    };
    

    variant est une simple chaîne de caractères. L'attribution se produit dans le navigateur, donc le composant doit être un composant client.

    Hero.tsx
    "use client";
    
    import { useExperiment } from "next-intlayer";
    import { HeroBanner } from "./HeroBanner";
    
    export const Hero = () => {
    const { variant, isAssigned } = useExperiment("homepage-hero", [
      "default",
      "black_friday",
    ]);
    
    if (!isAssigned) return null;
    
    return <HeroBanner variant={variant} />;
    };
    

    variant et isAssigned sont des Refs.

    Hero.vue
    <script setup lang="ts">
    import { useExperiment } from "vue-intlayer";
    import HeroBanner from "./HeroBanner.vue";
    
    const { variant, isAssigned } = useExperiment("homepage-hero", [
    "default",
    "black_friday",
    ]);
    </script>
    
    <template>
    <HeroBanner v-if="isAssigned" :variant="variant" />
    </template>
    

    variant et isAssigned sont des stores : lisez-les avec le préfixe $.

    Hero.svelte
    <script lang="ts">
    import { useExperiment } from "svelte-intlayer";
    import HeroBanner from "./HeroBanner.svelte";
    
    const { variant, isAssigned } = useExperiment("homepage-hero", [
      "default",
      "black_friday",
    ]);
    </script>
    
    {#if $isAssigned}
    <HeroBanner variant={$variant} />
    {/if}
    

    variant est une simple chaîne de caractères.

    Hero.tsx
    import { useExperiment } from "preact-intlayer";
    import { HeroBanner } from "./HeroBanner";
    
    export const Hero = () => {
    const { variant, isAssigned } = useExperiment("homepage-hero", [
      "default",
      "black_friday",
    ]);
    
    if (!isAssigned) return null;
    
    return <HeroBanner variant={variant} />;
    };
    

    variant et isAssigned sont des Accessors : appelez-les pour lire la valeur.

    Hero.tsx
    import { useExperiment } from "solid-intlayer";
    import { Show } from "solid-js";
    import { HeroBanner } from "./HeroBanner";
    
    export const Hero = () => {
    const { variant, isAssigned } = useExperiment("homepage-hero", [
      "default",
      "black_friday",
    ]);
    
    return (
      <Show when={isAssigned()}>
        <HeroBanner variant={variant()} />
      </Show>
    );
    };
    

    variant et isAssigned sont des Signals — appelez-les pour lire la valeur.

    hero.component.ts
    import { Component } from "@angular/core";
    import { useExperiment } from "angular-intlayer";
    import { HeroBannerComponent } from "./hero-banner.component";
    
    @Component({
    selector: "app-hero",
    imports: [HeroBannerComponent],
    template: `@if (experiment.isAssigned()) {
      <app-hero-banner [variant]="experiment.variant()" />
    }`,
    })
    export class HeroComponent {
    experiment = useExperiment("homepage-hero", ["default", "black_friday"]);
    }
    

    Les poids sont optionnels — passez-en un par variante pour biaiser la répartition, par exemple useExperiment("homepage-hero", ["default", "black_friday"], [9, 1]).

    L'enfant lit ensuite la Variante du dictionnaire qui correspond :

    HeroBanner.tsx
    import { useIntlayer } from "react-intlayer";
    
    export const HeroBanner = ({ variant }: { variant: string }) => {
      const { headline, cta } = useIntlayer("hero-banner", { variant });
    
      return (
        <section>
          <h1>{headline}</h1>
          <a>{cta}</a>
        </section>
      );
    };
    
    Lire la variante dans un composant enfant est ce qui fait que cela fonctionne en dehors de React : dans Vue, Svelte, Solid et Angular, le sélecteur passé à useIntlayer est capturé lors de la configuration du composant, donc la lecture doit se faire dans un composant qui ne se monte qu'une fois la variante connue.

    Si l'expérience couvre une page entière plutôt qu'un seul dictionnaire, remontez la variante sur le fournisseur à la place — voir Variante ambiante. Chaque useIntlayer ci-dessous se résout alors contre celle-ci sans modification du site d'appel.

    Si vous avez besoin de l'assignment brut en dehors d'un composant, accédez directement au client :

    getVariant assigne uniquement — il n'enregistre pas l'exposition. Préférez useExperiment(), sinon le taux de conversion n'a pas de dénominateur.

    Confidentialité & performance

    • Anonyme par conception : les sessions sont identifiées par un ID rotatif ; le backend ne stocke jamais qu'un hash SHA-256 de cet ID — jamais l'ID brut, jamais une adresse IP.
    • La localisation est approximative (coarse) : uniquement un code pays, dérivé des en-têtes de géolocalisation CDN (cf-ipcountry, x-vercel-ip-country, …) — aucune IP n'est lue ou stockée.
    • Les URL excluent les paramètres de recherche par défaut, les chaînes de requête (query strings) ne sont donc jamais capturées.
    • Échantillonnage (Sampling) : sampleRate vous permet de conserver seulement une fraction des événements d'exposition de contenu sur les applications à fort trafic.
    • En lots (Batched) : une requête environ toutes les 20 secondes (flushInterval), ou plus tôt si le tampon se remplit (maxBufferSize) — jamais une requête par événement.

    Zéro coût quand non installé

    @intlayer/analytics suit exactement le même modèle de dépendance optionnelle que @intlayer/editor :

    • chaque point d'intégration charge le package via un import() dynamique enveloppé dans un try/catch — une application qui n'installe jamais @intlayer/analytics ne paie aucun coût en taille de bundle ou à l'exécution, et ne voit jamais d'erreur ;
    • une variable d'environnement au moment de la compilation (INTLAYER_ANALYTICS_ENABLED), définie automatiquement à 'false' par @intlayer/config dès que le package n'est pas installé, que analytics.enabled vaut false ou que editor.clientId n'est pas configuré, permet aux bundlers d'éliminer le code mort (dead-code-eliminate) de toute l'intégration ;
    • l'analytique est désactivée à l'intérieur de l'iframe de prévisualisation de l'éditeur/CMS Intlayer, afin que les sessions d'éditeur ne soient jamais comptées comme du vrai trafic.

    Tableau de bord : Page Analytique

    Une fois que votre projet a collecté des événements, la page Analytics dans le tableau de bord Intlayer (visible dans la barre latérale une fois qu'un projet est sélectionné) affiche :

    • Utilisateurs actifs — visiteurs uniques sur la fenêtre glissante sélectionnée (7 / 30 / 90 jours).
    • Utilisateurs aujourd'hui et utilisateurs au cours des 7 derniers jours.
    • Pages vues sur la fenêtre sélectionnée.
    • Un graphique d'évolution des visiteurs uniques quotidiens.
    • Des onglets de répartition par Locales et par Emplacement (Location), classant votre audience par locale et par pays.

    Référence API Backend

    Tous les endpoints de lecture nécessitent une authentification ; l'ingestion est publique et attribuée par clientId dans le corps (body).

    MéthodeEndpointDescription
    POST/api/analytics/eventsIngérer un lot d'événements (public, attribué par clientId dans le body).
    GET/api/analytics/overviewTotaux de pages/locales pour le projet authentifié.
    GET/api/analytics/audience?days=30Visiteurs uniques, pages vues, série quotidienne, répartitions locale + pays.
    GET/api/analytics/content-statsTotaux d'exposition par contenu, groupés par clé de dictionnaire/chemin/locale.
    GET/api/analytics/experiments/:experimentKeyTaux de conversion par variante et signification statistique pour un test A/B.

    Vous pouvez également appeler ces endpoints de manière programmatique avec le SDK CMS :

    analytics.ts
    import { createIntlayerCMS } from "@intlayer/api";
    import { analyticsEndpoint } from "@intlayer/api/analytics";
    
    const cms = createIntlayerCMS();
    
    const { data: audience } = await analyticsEndpoint(cms).getAudience(30);
    
    Côté serveur uniquement. createIntlayerCMS() s'authentifie avec clientId + clientSecret, et le secret n'est jamais disponible dans le navigateur, ce snippet émettrait des requêtes non authentifiées s'il s'exécutait là. Conservez-le dans un gestionnaire de route, une action serveur ou un script.

    Liens utiles