Creation:2026-03-20Last update:2026-05-31

    Intlayer con Storybook

    Tabla de Contenidos

    ¿Por qué Intlayer en lugar de alternativas?

    En comparación con soluciones principales como storybook-react-i18next o i18next, Intlayer es una solución que viene con optimizaciones integradas como:

    Intlayer está optimizado para funcionar perfectamente con Storybook al ofrecer decoradores de historias multilingües, cambio de configuración regional y todas las funciones necesarias para escalar la internacionalización (i18n) en todo su sistema de diseño.

    En lugar de cargar archivos JSON masivos en sus páginas, cargue solo el contenido necesario. Intlayer ayuda a reducir el tamaño de su bundle y de sus páginas hasta en un 50%.

    Determinar el alcance del contenido de su aplicación facilita el mantenimiento para aplicaciones a gran escala. Puede duplicar o eliminar una sola carpeta de funciones sin la carga mental de revisar todo el código base de contenido. Además, Intlayer está completamente escrito para garantizar la precisión de su contenido.

    La ubicación conjunta de contenido reduce el contexto necesario para los modelos de lenguajes grandes (LLM). Intlayer también viene con un conjunto de herramientas, como una CLI para comprobar si faltan traducciones,LSP, MCP y agent skills, para que la experiencia del desarrollador (DX) sea aún más fluida para los agentes de IA.

    Utilice la automatización para traducir su canal de CI/CD utilizando el LLM de su elección al costo de su proveedor de IA. Intlayer también ofrece un compilador para automatizar la extracción de contenido, así como una plataforma web para ayudar a traducir en segundo plano.

    La conexión de archivos JSON masivos a componentes puede provocar problemas de rendimiento y reactividad. Intlayer optimiza la carga de su contenido en el momento de la compilación.

    Más que una simple solución i18n, Intlayer proporciona un [editor visual] autohospedado(/es/doc/concept/editor) y un CMS completo para ayudarle a administrar su contenido multilingüe en tiempo real, lo que facilita la colaboración con traductores, redactores y otros miembros del equipo. El contenido se puede almacenar de forma local y/o remota.


    ¿Por qué usar Intlayer con Storybook?

    Storybook es la herramienta estándar de la industria para desarrollar y documentar componentes de interfaz de usuario de forma aislada. Combinarlo con Intlayer te permite:

    • Previsualizar cada idioma directamente dentro del canvas de Storybook usando un selector en la barra de herramientas.
    • Detectar traducciones faltantes antes de que lleguen a producción.
    • Documentar componentes multilingües con contenido real y seguro en cuanto a tipos, en lugar de cadenas de texto codificadas permanentemente.

    Configuración Paso a Paso

    1. Instalar Dependencias

      bash
      npm install intlayer react-intlayernpm install vite-intlayer --save-dev
      Paquete Rol
      intlayer Núcleo - configuración, compilación de contenido, CLI
      react-intlayer Enlaces para React - IntlayerProvider, hook useIntlayer
      vite-intlayer Complemento de Vite - vigila y compila archivos de contenido

    2. Crear una Configuración de Intlayer

      Crea intlayer.config.ts en la raíz de tu proyecto (o dentro de tu paquete de sistema de diseño):

      intlayer.config.ts
      import { Locales, type IntlayerConfig } from "intlayer";const config: IntlayerConfig = {  internationalization: {    locales: [      Locales.ENGLISH,      Locales.FRENCH,      Locales.SPANISH,      // agrega más idiomas según sea necesario    ],    defaultLocale: Locales.ENGLISH,  },  content: {    contentDir: ["./src"], // donde viven tus archivos *.content.ts  },};export default config;
      Para obtener la lista completa de opciones, consulta la referencia de configuración.

    3. Agregar el Complemento de Vite a Storybook

      El hook viteFinal de Storybook te permite extender la configuración interna de Vite. Importa y agrega el complemento intlayer() allí:

      .storybook/main.ts
      import type { StorybookConfig } from "@storybook/react-vite";import { defineConfig, mergeConfig } from "vite";import { intlayer } from "vite-intlayer";const config: StorybookConfig = {  stories: ["../src/**/*.stories.@(js|jsx|ts|tsx)"],  addons: [    "@storybook/addon-essentials",    // …otros complementos  ],  framework: {    name: "@storybook/react-vite",    options: {},  },  async viteFinal(baseConfig, { configType }) {    const env = {      command: configType === "DEVELOPMENT" ? "serve" : "build",      mode: configType === "DEVELOPMENT" ? "development" : "production",    } as const;    const viteConfig = defineConfig(() => ({      plugins: [intlayer()],    }));    return mergeConfig(baseConfig, viteConfig(env));  },};export default config;

      El complemento intlayer() vigila tus archivos *.content.ts y vuelve a generar los diccionarios automáticamente cada vez que cambian durante el desarrollo de Storybook.


    4. Agregar el Decorador `IntlayerProvider` y una Barra de Herramientas de Idioma

    5. El archivo preview de Storybook es el lugar adecuado para envolver cada historia con el IntlayerProvider y exponer un selector de idioma en la barra de herramientas:

      .storybook/preview.tsx
      import type { Preview, StoryContext } from "@storybook/react";import { IntlayerProvider } from "react-intlayer";const preview: Preview = {  // Envolver cada historia dentro del IntlayerProvider  decorators: [    (Story, context: StoryContext) => {      const locale = context.globals.locale ?? "en";      return (        <IntlayerProvider locale={locale}>          <Story />        </IntlayerProvider>      );    },  ],  // Exponer un selector de idioma en la barra de herramientas de Storybook  globalTypes: {    locale: {      description: "Idioma activo",      defaultValue: "en",      toolbar: {        title: "Idioma",        icon: "globe",        items: [          { value: "en", title: "English" },          { value: "fr", title: "Français" },          { value: "es", title: "Español" },        ],        dynamicTitle: true,      },    },  },  parameters: {    controls: {      matchers: {        color: /(background|color)$/i,        date: /Date$/i,      },    },  },};export default preview;
      Los valores de locale deben coincidir con los idiomas declarados en tu intlayer.config.ts.

      </Step>

      </Steps>

      Declaración de Contenido

      Crea un archivo *.content.ts al lado de cada componente. Intlayer lo detecta automáticamente durante la compilación.

      src/components/CopyButton/CopyButton.content.ts
      import { type Dictionary, t } from "intlayer";
      
      const copyButtonContent = {
        key: "copy-button",
        content: {
          label: t({
            en: "Copy content",
            fr: "Copier le contenu",
            es: "Copiar contenido",
          }),
        },
      } satisfies Dictionary;
      
      export default copyButtonContent;
      Para obtener más formatos y funciones de declaración de contenido, consulta la documentación de declaración de contenido.

      Uso de useIntlayer en un Componente

      src/components/CopyButton/index.tsx
      "use client";import { type FC } from "react";import { useIntlayer } from "react-intlayer";type CopyButtonProps = {  content: string;};export const CopyButton: FC<CopyButtonProps> = ({ content }) => {  const { label } = useIntlayer("copy-button");  return (    <button      onClick={() => navigator.clipboard.writeText(content)}      aria-label={label.value}      title={label.value}    >      Copiar    </button>  );};

      useIntlayer devuelve el diccionario compilado para el idioma actual proporcionado por el IntlayerProvider más cercano. Cambiar el idioma en la barra de herramientas de Storybook vuelve a renderizar automáticamente la historia con las traducciones actualizadas.


      Escritura de Historias para Componentes Internacionalizados

      Con el decorador IntlayerProvider en su lugar, tus historias funcionan exactamente como antes. La barra de herramientas de idioma controla el idioma activo para todo el canvas:

      src/components/CopyButton/CopyButton.stories.tsx
      import type { Meta, StoryObj } from "@storybook/react";import { CopyButton } from ".";const meta: Meta<typeof CopyButton> = {  title: "Components/CopyButton",  component: CopyButton,  tags: ["autodocs"],  argTypes: {    content: { control: "text" },  },};export default meta;type Story = StoryObj<typeof CopyButton>;/** Historia predeterminada - cambia el idioma en la barra de herramientas para previsualizar las traducciones. */export const Default: Story = {  args: {    content: "npm install intlayer react-intlayer",  },};/** Renderiza el botón dentro de un bloque de código, un caso de uso común en el mundo real. */export const InsideCodeBlock: Story = {  render: (args) => (    <div style={{ position: "relative", display: "inline-block" }}>      <pre style={{ background: "#1e1e1e", color: "#fff", padding: "1rem" }}>        <code>{args.content}</code>      </pre>      <CopyButton        content={args.content}        style={{ position: "absolute", top: 8, right: 8 }}      />    </div>  ),  args: {    content: "npx intlayer init",  },};
      Cada historia hereda el global locale de la barra de herramientas, por lo que puedes verificar cada idioma sin cambiar ningún código de historia.

      Prueba de Traducciones en Historias

      Usa las funciones play de Storybook para asegurar que se renderice el texto traducido correctamente para un idioma determinado:

      src/components/CopyButton/CopyButton.stories.tsx
      import type { Meta, StoryObj } from "@storybook/react";import { expect, within } from "@storybook/test";import { CopyButton } from ".";const meta: Meta<typeof CopyButton> = {  title: "Components/CopyButton",  component: CopyButton,  tags: ["autodocs"],};export default meta;type Story = StoryObj<typeof CopyButton>;export const AccessibleLabel: Story = {  args: { content: "Hello World" },  play: async ({ canvasElement }) => {    const canvas = within(canvasElement);    const button = canvas.getByRole("button");    // Verificar que el botón tenga un nombre accesible no vacío    await expect(button).toHaveAccessibleName();    // Verificar que el botón no esté deshabilitado    await expect(button).not.toBeDisabled();    // Verificar la accesibilidad del teclado    await expect(button).toHaveAttribute("tabindex", "0");  },};

      Recursos Adicionales