> ## Documentation Index
> Fetch the complete documentation index at: https://docs.devinenterprise.com/llms.txt
> Use this file to discover all available pages before exploring further.

# Agregar japonés y español a una aplicación de Next.js

export const UseCaseHero = ({title, description, prompt, category, features, devinUrl, agent, intent, playbookId, type}) => {
  const encodedPrompt = encodeURIComponent(prompt || '');
  const tag = 'docs-use-case-gallery';
  const utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=hero-cta';
  const agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  const devinHref = type === 'schedule' ? 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + (prompt ? '&prompt=' + encodedPrompt : '') : type === 'review' ? 'https://app.devin.ai/review?' + utm : agent === 'ada' ? 'https://app.devin.ai/search?' + utm + '&noSubmit=true' + (prompt ? '&prompt=' + encodedPrompt : '') : devinUrl ? devinUrl.includes('?') ? devinUrl + '&' + utm + agentParams : devinUrl + '?' + utm + agentParams : prompt ? 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encodedPrompt : 'https://app.devin.ai/?' + utm + agentParams;
  const buttonLabel = type === 'schedule' ? 'Schedule in Devin ↗' : type === 'review' ? 'Set Up Devin Review ↗' : agent === 'advanced' ? 'Try in Devin ↗' : agent === 'dana' ? 'Try in Dana ↗' : agent === 'ada' ? 'Try in Ask Devin ↗' : 'Try in Devin ↗';
  const featureList = features ? features.split(',').map(f => f.trim()) : [];
  return <div className="uc-hero">
      <div className="uc-hero-inner">
        <div className="uc-hero-left">
          <h1 className="uc-hero-title">{title}</h1>
          <p className="uc-hero-desc">{description}</p>
          <div>
            <a href={devinHref} target="_blank" rel="noopener noreferrer" className="try-in-devin-btn">
              {buttonLabel}
            </a>
          </div>
        </div>
        <div className="uc-hero-meta">
          <div className="uc-meta-item">
            <span className="uc-meta-label">Author</span>
            <span className="uc-meta-value">Cognition</span>
          </div>
          <div className="uc-meta-item">
            <span className="uc-meta-label">Category</span>
            <span className="uc-meta-value">{category}</span>
          </div>
          {featureList.length > 0 && <div className="uc-meta-item">
              <span className="uc-meta-label">Features</span>
              <span className="uc-meta-value">{featureList.join(', ')}</span>
            </div>}
        </div>
      </div>
    </div>;
};

export const PromptBlock = ({children, type, agent, intent, playbookId}) => {
  var utm = 'utm_source=docs&utm_medium=use-case-gallery&utm_campaign=prompt-block';
  var tag = 'docs-use-case-gallery';
  var agentParams = (agent ? '&agent=' + agent : '') + (intent ? '&intent=' + intent : '') + (playbookId ? '&playbookId=' + playbookId : '');
  var label = type === 'schedule' ? 'Schedule in Devin' : type === 'playbook' ? 'Create Playbook' : type === 'knowledge' ? 'Add to Knowledge' : agent === 'advanced' ? 'Try in Devin' : agent === 'dana' ? 'Try in Dana' : agent === 'ada' ? 'Try in Ask Devin' : 'Try in Devin';
  var buildUrl = function (text) {
    var encoded = encodeURIComponent(text);
    if (type === 'schedule') return 'https://app.devin.ai/settings/schedules/create?' + utm + agentParams + '&prompt=' + encoded;
    if (type === 'playbook') return 'https://app.devin.ai/settings/playbooks/create?' + utm + '&body=' + encoded;
    if (type === 'knowledge') return 'https://app.devin.ai/knowledge?' + utm + '&body=' + encoded;
    if (agent === 'ada') return 'https://app.devin.ai/search?' + utm + '&noSubmit=true&prompt=' + encoded;
    return 'https://app.devin.ai/?tags=' + tag + '&' + utm + agentParams + '&prompt=' + encoded;
  };
  const ref = React.useRef(null);
  const [href, setHref] = React.useState('#');
  React.useEffect(() => {
    if (!ref.current) return;
    var codeEl = ref.current.querySelector('pre code');
    if (codeEl) {
      var text = codeEl.textContent.trim();
      if (text) setHref(buildUrl(text));
    }
    var header = ref.current.querySelector('[data-component-part="code-block-header"]');
    if (header && !header.querySelector('.prompt-block-devin-link')) {
      var link = document.createElement('a');
      link.href = href;
      link.target = '_blank';
      link.rel = 'noopener noreferrer';
      link.className = 'prompt-block-devin-link';
      link.style.cssText = 'display:inline-flex;align-items:center;gap:6px;text-decoration:none;color:#fff;font-size:11px;font-weight:500;padding:4px 10px;border-radius:6px;white-space:nowrap;background:#317CFF;transition:background 0.2s;margin-left:8px;';
      link.innerHTML = '<svg xmlns="http://www.w3.org/2000/svg" width="12" height="12" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round"><path d="M18 13v6a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2V8a2 2 0 0 1 2-2h6"/><polyline points="15 3 21 3 21 9"/><line x1="10" y1="14" x2="21" y2="3"/></svg> ' + label;
      link.onmouseenter = function () {
        link.style.background = '#2968D9';
      };
      link.onmouseleave = function () {
        link.style.background = '#317CFF';
      };
      header.appendChild(link);
    }
    var existingLink = ref.current.querySelector('.prompt-block-devin-link');
    if (existingLink && href !== '#') existingLink.href = href;
  });
  return <div className="prompt-block" ref={ref}>{children}</div>;
};

<UseCaseHero title="Agregar japonés y español a una app de Next.js" description="Devin instala next-i18next, extrae cadenas codificadas directamente a archivos JSON de locales, añade traducciones en japonés y español, y verifica el cambio de idioma en el navegador." prompt="Añade internacionalización a nuestra app de Next.js usando next-i18next. Instala next-i18next, i18next y react-i18next. Configura el enrutamiento de locales con defaultLocale 'en' y locales ['en', 'es', 'ja']. Escanea components/ y pages/ en busca de todas las cadenas en inglés codificadas directamente (texto JSX, placeholders, aria-labels, mensajes de error, tooltips). Crea archivos de locales estructurados en public/locales/{en,es,ja}/common.json con claves con espacio de nombres. Reemplaza cada cadena codificada directamente con una llamada a t(). Añade serverSideTranslations a cada página. Añade un selector de idioma a la barra de navegación. Usa traducciones provisionales para español y japonés. Inicia el servidor de desarrollo y prueba cada página en los tres idiomas: revisa si quedan cadenas en inglés, desbordes de diseño y problemas de enrutamiento. Sigue iterando hasta que todo funcione. No abras una PR hasta que los tres idiomas se muestren correctamente en todas las páginas." category="Desarrollo de funcionalidades" features="" />

<div className="uc-detail-wrapper">
  <Tip>¿Prefieres no configurarlo manualmente? Pega un enlace a esta página en una sesión de Devin y pídele que lo configure todo por ti.</Tip>

  <Steps>
    <Step title="(Opcional) Delimita el alcance de la base de código con Ask Devin">
      Si no tienes claro cómo está estructurada tu app de Next.js o qué componentes contienen cadenas hardcodeadas, usa [Ask Devin](https://app.devin.ai/search?utm_source=docs\&utm_medium=use-case-gallery) para investigarlo primero:

      <PromptBlock agent="ada">
        ```txt Scope the i18n work theme={null}
        Ayúdame a entender el alcance de agregar i18n a nuestra app de Next.js:
        1. ¿Estamos usando el Pages Router o el App Router?
        2. ¿Cuántos componentes y páginas tienen cadenas de texto en inglés hardcodeadas?
        3. ¿Ya tenemos alguna configuración de i18n o archivos de `locale`?
        4. ¿Qué componentes compartidos (botones, formularios, modales) contienen texto?
        5. ¿Hay cadenas que deban permanecer en inglés (nombres de marca, códigos de API)?
        ```
      </PromptBlock>

      También puedes iniciar una sesión de Devin directamente desde Ask Devin, y se conservará todo lo que haya aprendido como contexto.
    </Step>

    <Step title="Inicia la configuración de i18n">
      Agregar internacionalización a una app de Next.js implica configurar el enrutamiento por idioma/locales, definir el middleware, extraer cada cadena codificada de forma estática y crear archivos de traducción, todo antes de poder probar un solo cambio de idioma. Devin se encarga de todo el pipeline: instalar `next-i18next`, configurar el enrutamiento de locales de Next.js, extraer cadenas de docenas de componentes y verificar el resultado en el navegador.

      <PromptBlock>
        ```txt Add Japanese and Spanish to our Next.js app theme={null}
        Add internationalization to our Next.js app using next-i18next.

        - Install next-i18next, i18next, and react-i18next
        - Configure next-i18next.config.js with defaultLocale "en" and
          locales ["en", "es", "ja"]
        - Update next.config.js to include the i18n config
        - Scan components/ and pages/ for all hardcoded English strings
          (JSX text, placeholder attributes, aria-labels, error messages, tooltips)
        - Create structured locale files under public/locales/{en,es,ja}/common.json
          with namespaced keys (e.g. common.save, auth.loginButton, errors.notFound)
        - Replace every hardcoded string with a t() call using useTranslation('common')
        - Add serverSideTranslations to every page's getStaticProps / getServerSideProps
        - Add a language switcher dropdown to components/Navbar.tsx using next/router locale switching
        - Use placeholder translations for Spanish and Japanese — we'll swap in
          professional translations later
        - Run the app and switch between all three languages to confirm nothing breaks

        ## Testing & verification
        - Start the dev server and switch between /en, /es, and /ja routes
        - Check every page for leftover untranslated English strings
        - Verify the language switcher toggles correctly
        - Test that interpolated values (names, counts) render in each locale
        - Check that layouts don't break with longer Spanish strings or
          wider Japanese characters
        - If anything is broken — missing translations, layout overflow,
          routing errors — fix it and re-test. Keep iterating until all
          three languages render correctly on every page.
        - Do not open a PR until everything works end-to-end
        ```
      </PromptBlock>

      Usa el comando slash **`/plan`** al inicio de la sesión para que Devin analice primero tu codebase: determinará si estás usando el Pages Router o el App Router, identificará la librería de i18n adecuada y esbozará su enfoque antes de escribir cualquier código. Revisa el plan y sugiere cambios antes de que empiece.
    </Step>

    <Step title="Cómo gestiona Devin el proceso de configuración">
      Devin trabaja de forma metódica en tu base de código de Next.js: configura el pipeline de i18n, extrae cadenas y crea archivos de locales. Así es como funciona:

      1. **Instala dependencias** — Ejecuta `npm install next-i18next i18next react-i18next` y crea los archivos de configuración:

      ```js theme={null}
      // next-i18next.config.js
      module.exports = {
        i18n: {
          defaultLocale: 'en',
          locales: ['en', 'es', 'ja'],
        },
      };
      ```

      ```js theme={null}
      // next.config.js
      const { i18n } = require('./next-i18next.config');

      module.exports = {
        i18n,
        // ...tu configuración existente
      };
      ```

      2. **Analiza cada componente** — Lee cada archivo en `components/` y `pages/`, identificando texto incrustado (hardcoded) en JSX, atributos como `placeholder` y `aria-label`, literales de plantilla y cadenas de error

      3. **Genera archivos de idioma** — Crea JSON estructurado en `public/locales/`:

      ```json theme={null}
      // public/locales/en/common.json
      {
        "common": {
          "save": "Save",
          "cancel": "Cancel",
          "loading": "Loading..."
        },
        "auth": {
          "login": "Log in",
          "signup": "Create account",
          "forgotPassword": "Forgot your password?"
        },
        "dashboard": {
          "welcome": "Welcome back, {{name}}",
          "items_one": "{{count}} item",
          "items_other": "{{count}} items"
        }
      }
      ```

      ```json theme={null}
      // public/locales/ja/common.json
      {
        "common": {
          "save": "保存",
          "cancel": "キャンセル",
          "loading": "読み込み中..."
        },
        "auth": {
          "login": "ログイン",
          "signup": "アカウント作成",
          "forgotPassword": "パスワードをお忘れですか？"
        },
        "dashboard": {
          "welcome": "おかえりなさい、{{name}}",
          "items_one": "{{count}} 件",
          "items_other": "{{count}} 件"
        }
      }
      ```

      ```json theme={null}
      // public/locales/es/common.json
      {
        "common": {
          "save": "Guardar",
          "cancel": "Cancelar",
          "loading": "Cargando..."
        },
        "auth": {
          "login": "Iniciar sesión",
          "signup": "Crear cuenta",
          "forgotPassword": "¿Olvidaste tu contraseña?"
        },
        "dashboard": {
          "welcome": "Bienvenido de nuevo, {{name}}",
          "items_one": "{{count}} elemento",
          "items_other": "{{count}} elementos"
        }
      }
      ```

      4. **Reemplaza las cadenas directamente** — Envuelve cada componente con `useTranslation('common')` y sustituye el texto hardcodeado por llamadas a `t()`, gestionando la interpolación para valores dinámicos como `t('dashboard.welcome', { name: user.name })`

      5. **Configura la carga del lado del servidor** — Añade `serverSideTranslations` a cada página para que las traducciones estén disponibles en el momento del renderizado:

      ```tsx theme={null}
      // pages/dashboard.tsx
      import { serverSideTranslations } from 'next-i18next/serverSideTranslations';

      export const getStaticProps = async ({ locale }) => ({
        props: {
          ...(await serverSideTranslations(locale, ['common'])),
        },
      });
      ```

      6. **Agrega el selector de idioma** — Crea un menú desplegable en la barra de navegación que usa la conmutación de `locale` del router de Next.js:

      ```tsx theme={null}
      // components/LanguageSwitcher.tsx
      import { useRouter } from 'next/router';

      export default function LanguageSwitcher() {
        const router = useRouter();

        const switchLocale = (locale: string) => {
          router.push(router.pathname, router.asPath, { locale });
        };

        return (
          <select
            value={router.locale}
            onChange={(e) => switchLocale(e.target.value)}
          >
            <option value="en">English</option>
            <option value="es">Español</option>
            <option value="ja">日本語</option>
          </select>
        );
      }
      ```
    </Step>

    <Step title="Prueba e itera">
      Devin ejecuta `npm run dev`, abre la app en su [navegador integrado](/es/work-with-devin/devin-session-tools) y navega entre las rutas `/en`, `/es` y `/ja` para verificar que todo funcione. Si algo falla — una traducción faltante, un diseño que se desborda, una ruta que arroja un 404 — Devin lo corrige y vuelve a hacer las pruebas automáticamente.

      Usa el comando slash **`/test`** en cualquier momento para decirle a Devin que vuelva a ejecutar todas las pruebas y vuelva a verificar en el navegador. Usa **`/review`** antes de la PR final (pull request) para que Devin revise su propio código en busca de cadenas omitidas, nombres de claves inconsistentes o llamadas faltantes a `serverSideTranslations`.

      Devin verifica:

      * Todo el texto visible está traducido (no quedan cadenas en inglés en el modo español/japonés)
      * Los diseños no se rompen con cadenas más largas en español o caracteres japoneses más anchos
      * El selector de idioma permite alternar entre los tres locales
      * Los valores interpolados (nombres de usuario, cantidades) se muestran correctamente en cada locale
      * Las reglas de pluralización funcionan (por ejemplo, "1 item" → "1 件" en japonés, "1 elemento" en español)
      * El enrutamiento de locales de Next.js funciona — `/ja/dashboard` muestra la versión en japonés
    </Step>

    <Step title="Iterar a partir de aquí">
      Una vez que la configuración base de i18n esté lista, continúa ampliándola.

      <PromptBlock>
        ```txt Sustituir por traducciones profesionales theme={null}
        Sustituye las traducciones de relleno en español y japonés por estas
        traducciones profesionales de nuestro proveedor. [Adjunta los archivos JSON traducidos.]
        Asegúrate de que todas las claves en public/locales/en/common.json estén cubiertas y
        no queden marcadores de posición en los archivos es y ja.
        ```
      </PromptBlock>

      <PromptBlock>
        ```txt Añadir coreano como cuarto locale theme={null}
        Añade coreano (ko) como un nuevo locale. Actualiza next-i18next.config.js para incluir
        "ko" en el array de locales, crea public/locales/ko/common.json con
        traducciones de relleno, añade la opción a LanguageSwitcher y
        verifica que las rutas /ko funcionen en el navegador.
        ```
      </PromptBlock>

      <PromptBlock>
        ```txt Dividir traducciones por namespace de página theme={null}
        Nuestro common.json está creciendo demasiado. Divídelo en archivos de namespace separados:
        public/locales/{en,es,ja}/auth.json, dashboard.json y settings.json.
        Actualiza la llamada a serverSideTranslations de cada página para que cargue solo los
        namespaces que necesita.
        ```
      </PromptBlock>
    </Step>

    <Step title="Actualiza la configuración de tu repositorio">
      Esta tarea instala nuevas dependencias (`next-i18next`, `i18next`, `react-i18next`). Después de que se haga merge del PR, actualiza la [configuración de Environment](/es/onboard-devin/environment) para que la instantánea del entorno de Devin incluya estos paquetes en futuras sesiones:

      1. Ve a [**Settings > Devin's Environment > Repositories**](https://app.devin.ai/settings/machine?utm_source=docs\&utm_medium=use-case-gallery)
      2. Selecciona tu repo y haz clic en **Configure**
      3. En **Upkeep > Maintain dependencies**, asegúrate de que tu comando de instalación (por ejemplo, `npm ci`) esté configurado
      4. Haz clic en **Save** para actualizar la instantánea

      También puedes configurar una **Upkeep Frequency** para que Devin vuelva a ejecutar automáticamente la instalación de dependencias de forma periódica: esto mantiene la instantánea actualizada a medida que tu `package.json` evoluciona, de modo que las futuras sesiones no pierdan tiempo instalando paquetes al inicio.
    </Step>

    <Step title="Revisa la PR con Devin Review">
      Una vez que Devin abra el PR, utiliza [Devin Review](https://app.devin.ai/review?utm_source=docs\&utm_medium=use-case-gallery) para revisar los cambios de i18n. Devin Review puede detectar cadenas codificadas de forma fija que se hayan pasado por alto, nombres incoherentes de claves de traducción y llamadas que falten a `serverSideTranslations` en las distintas páginas.
    </Step>
  </Steps>
</div>
