Gatsby: Agrega una guía oficial para internacionalizar sitios web con Gatsby

Creado en 4 feb. 2018  ·  74Comentarios  ·  Fuente: gatsbyjs/gatsby

Al ver las reacciones a mi comentario sobre otro tema , decidí abrir este tema.

Creo que i18n es mucho más difícil de lo que debería ser. No pude encontrar ninguna documentación oficial o complemento para internacionalizar contenido en sitios web creados por Gatsby. Encontré jsLingui , que parece resolver la mayoría de los problemas, pero todavía no hay guías sobre cómo mantener, por ejemplo, archivos/páginas de descuento en diferentes idiomas.

documentation

Comentario más útil

Hola chicos, ha pasado casi un año 😅

Recientemente lancé el nuevo complemento gatsby gatsby-plugin-intl que convierte fácilmente su sitio web gatsby en un marco de internacionalización

DEMOSTRACIÓN: https://gatsby-starter-default-intl.netlify.com

  • Marco de internacionalización listo para usar impulsado por react-intl

  • Admite la redirección automática basada en el idioma preferido del usuario en el navegador

  • Admite rutas URL en varios idiomas en un solo componente de página. Esto significa que no tiene que crear páginas separadas como pages/en/index.js o pages/ko/index.js .

  • Como algunos de ustedes sugirieron anteriormente, ahora incluye solo el idioma actual durante el tiempo de compilación.

Además, quiero mencionar que muchos de los ejemplos / iniciadores de i18n en realidad se procesan en el lado del cliente. La mejor manera de verificar si la aplicación se procesa como SSR es ver el código fuente y verificar si existen los textos localizados. Vuelva a verificar este asunto cuando internacionalice su sitio web gatsby para SEO.

Todos 74 comentarios

Hay este artículo sobre el uso de i18next con GatsbyJS , que es el método más avanzado hasta ahora para mí.

Pero no siento que i18next sea la "forma estática".

Sobre la publicación del blog, tenía estas preguntas/reservas:
https://twitter.com/semdubois/status/930389055388508160

Existe https://github.com/angeloocana/gatsby-plugin-i18n pero tiene varias limitaciones y no recibe mucha actividad/atención. Podría ser útil moverlo dentro del repositorio de Gatsby. A mí también me encantaría una solución consolidada adecuada.

También me topé con js lingui y parece prometedor, especialmente con v2 recién salido.

También estoy tratando de resolver esto. Usar el método i18next en la publicación es el más conveniente y se siente intuitivo, pero me quedan dos preguntas...

  1. ¿Cómo puedo incorporar diferentes archivos de rebajas para idiomas como en la solución gatsby-plugin-i18n?

  2. ¿Esto renuncia por completo a la representación estática del contenido?

FYI @angeloocana

Escribiré un breve resumen de cómo lo manejamos en este momento usando react-intl . Esta aplicación aún no está en producción, por lo que es posible que aún encontremos algunos problemas con esta configuración, sin embargo, parece funcionar bien hasta ahora.

Mantenemos casi todo nuestro contenido (que se migró de nuestro blog de Wordpress) en Contentful . No usamos sus funciones de traducción, sino que tenemos un espacio (una especie de proyecto o carpeta) por idioma. Estamos usando el complemento gatsby-source-contentful para obtener estos datos; sin embargo, antes estábamos obteniendo y convirtiendo estos datos a archivos JSON nosotros mismos y usamos el complemento gatsby-source-filesystem (usamos una estructura de carpetas como /en/blog/... , /de/blog/... ), por lo que realmente no importa si uno está usando Contentful o no, siempre que cada nodo conozca su configuración regional.

También tenemos texto como etiquetas de botones, algunos enlaces o contenido estático que no proviene de Contentful, sino que se traduce en Transifex y se sincroniza con archivos JSON que se almacenan en el repositorio. Para esta parte, necesitábamos usar alguna biblioteca i18n y decidimos usar react-intl , solo porque ya lo conozco y sé que también maneja el formato de fecha y número. Así es como lo configuramos: https://github.com/gatsbyjs/gatsby/issues/3830#issuecomment -362710469. Entonces estamos usando, por ejemplo, intl.formatMessage al generar metaetiquetas y <FormattedMessage /> , <FormattedDate /> etc. componentes en plantillas.

@szimek Si lo entiendo correctamente, ¿tiene react-intl manejar la traducción del texto del componente mientras las publicaciones están en rutas codificadas en el directorio de páginas?

¿Eso significaría que las rutas codificadas son las únicas que se representan estáticamente? ¿Y las traducciones entre componentes i18n se representan dinámicamente?

@deltaskelta No estoy seguro de entender su pregunta.

No tenemos ningún enrutamiento personalizado del lado del cliente (como se describe, por ejemplo, aquí ) en este momento, solo páginas generadas estáticamente. Al generar una página post (también generamos páginas index y category específicas de la configuración regional y paginadas usando una versión ligeramente modificada del complemento gatsby-pagination ), estamos usando el siguiente código:

posts.edges.map(({ node }) => {
  const id = node.contentfulid;
  const locale = node.node_locale;

  return createPage({
    path: `/${locale}/blog/posts/${id}`,
    layout: locale,
    component: path.resolve('./src/templates/post-page.jsx'),
    context: {
      id,
      locale,
    },
  });
});

Incluso si tiene JS deshabilitado, todo el contenido, incluidas las etiquetas meta, se representa en el idioma correcto.

Por nuestra parte, estamos usando i18next con algunos ajustes

Los principios fundamentales son los siguientes:

  • Las configuraciones regionales están disponibles como archivos .json y simplemente se mueven al directorio /dist/ durante la compilación.
  • Estamos trabajando en una sola página que producirá tantas versiones estáticas como idiomas tengamos, usando onCreatePage (en gatsby-node.js ).
  • La información de las páginas (por ejemplo, la ruta) se centraliza en un solo archivo.

locales

Las traducciones se han agrupado principalmente por página, con un espacio de nombres (= archivo JSON) por página + un archivo global (para textos compartidos como encabezado/pie de página).

|- src/
  |- locales/
    |- en/
      |- foo.json
      |- bar.json
    |- fr/
      |- foo.json
      |- bar.json

Lamentablemente, no hay recarga en caliente en las modificaciones locales 👎

i18siguiente

i18next se inicializa con la siguiente configuración:

import i18n from "i18next";
import Backend from "i18next-xhr-backend";
import { reactI18nextModule } from "react-i18next";
import config from "config";  // Our custom configurations env-specifics

const NAMESPACES = [
  "foo",
  "bar",
  ...
];

export default function createI18n() {
  const options = {
    fallbackLng   : false,
    whitelist     : ["en", "fr"],
    ns            : NAMESPACES,
    debug         : config.debug,
    interpolation : {
      escapeValue : false
    },
    react         : {
      wait : true
    },
    backend       : {
      loadPath : `${config.pathPrefix}locales/{{lng}}/{{ns}}.json`
    },
    parseMissingKeyHandler : () => "",  // Display an empty string when missing/loading key
  };

  return i18n
    .use(Backend)
    .use(reactI18nextModule)
    .init(options);
}

Información de las páginas

Antes de generar todas las versiones i18n de nuestras páginas, necesitamos saber algunas informaciones que agrupamos en un archivo pagesInfos.js :

module.exports = {
  index : {
    id          : "index",
    namespace   : "home",
    path        : {
      fr : "/",
      en : "/en/"
    }
  },
  projects : {
    id          : "projects",
    namespace   : "projects",
    path        : {
      fr : "/nos-clients/",
      en : "/en/our-clients/"
    }
  },
  // etc...

Donde las claves son los nombres de archivo de las nombres de archivo locales . Pueden ser diferentes 🚨

En este caso:

|- src/
  |- pages/
    |- index.js
    |- projects.js
  |- locales/
    |- en/
      |- home.json
      |- projects.json
    |- fr/
      |- home.json
      |- projects.json

Y donde ruta son los nombres de ruta de nuestras futuras versiones (idiomas) de nuestras páginas.

Crear páginas

Con el mismo ejemplo anterior, nuestro objetivo aquí es construir una versión FR + EN de las páginas de inicio y proyecto.

Para que esto suceda, creamos una función dedicada:

/**
 * Generate a custom page informations
 * <strong i="15">@param</strong>  {Object} defaultInfos  Default informations generated by Gatsby
 * <strong i="16">@return</strong> {Object}               Customized page object
 */
function generatePagesInfos(defaultInfos) {
  const pageId = defaultInfos.jsonName.slice(0, -5);  // NOTE: Get pageId from "pageName.json"
  const pageInfos = pagesInfos[pageId];

  const pageFR = {
    ...defaultInfos,
    context : {
      pageId      : pageInfos.id,
      namespace   : pageInfos.namespace,
      language    : "fr"
    },
    path : pageInfos.path.fr
  };

  const pageEN = {
    ...defaultInfos,
    context : {
      pageId      : pageInfos.id,
      namespace   : pageInfos.namespace,
      language    : "en"
    },
    path : pageInfos.path.en
  };

  return [pageFR, pageEN];
}

Este ayudante se usará durante el gancho onCreatePage , seleccionando cada página a través de una expresión regular:

exports.onCreatePage = async ({ page, boundActionCreators }) => {
  const { createPage, deletePage } = boundActionCreators;

  return new Promise((resolve, reject) => {

    if (page.path.match(page.path.match(/^\/$/))) {
      const i18nPages = generatePagesInfos(page);
      deletePage(page);                         // Remove old default page
      i18nPages.map(page => createPage(page));  // Create custom i18n pages
    }

    if (page.path.match(/^\/projects\/?$/)) {
      const i18nPages = generatePagesInfos(page);
      deletePage(page);
      i18nPages.map(page => createPage(page));
    }

    // etc...

    resolve();
  });
}

Ahora tenemos dos versiones de cada página, con un nombre de ruta personalizado (de nuestro archivo de información de páginas). Es posible que haya notado que estamos pasando una información de language a cada página a través de pathContext . Este valor se utilizará en cada página para mostrar el idioma correcto.

Mostrar el idioma correcto

Estamos usando la clase React para páginas, con el siguiente decorador que conoce automáticamente el idioma de la página actual y actualiza i18n si es necesario:

import React from "react";
import PropTypes from "prop-types";
import { i18n } from "context";    // Our custom context

/**
 * <strong i="10">@returns</strong> {React.PureComponent} Component with locales as proptypes
 */
export default function setLanguageFromPage() {

  return WrappedComponent => (
    class extends React.PureComponent {

      static propTypes = {
        pathContext : PropTypes.shape({
          language : PropTypes.string.isRequired
        })
      }

      componentDidMount() {
        const currentLanguage = i18n.language;
        const pageLanguage = this.props.pathContext.language;

        // First request
        if (!currentLanguage) {
          i18n.language = pageLanguage;
        }

        // Only update on language change
        if (currentLanguage !== pageLanguage) {
          i18n.changeLanguage(pageLanguage);
        }
      }

      render() {
        return <WrappedComponent {...this.props} />;
      }

    }
  );

}

Luego llámalo en las páginas:

@setLanguageFromPage()
export default class ProjectsPage extends React.PureComponent {
// ...

Pfew, todo lo que tienes que hacer ahora es usar la función de traducción de i18next.

Conclusión

👍 Un único archivo fuente que genera tantas versiones como sea necesario durante la compilación
👍 Fácil gestión de salida de páginas
👍 Algunos esfuerzos al principio, pero luego todo es simple

👎 Sin recarga en caliente para locales

Siento que no es realmente la "forma de vida estática"... Pero eso es lo mejor que logramos obtener por ahora.

Me encantaría ver lo que piensas sobre esto, y cómo lo manejan.

PD. Poke @szimek , eso es lo que quería mostrarles esto hace unos días :)

He estado usando https://github.com/angeloocana/gatsby-plugin-i18n + React-intl de @angeloocana y no es tan complicado como los métodos anteriores, pero hay algunas limitaciones (ver problemas de repositorio) y estoy No estoy tan contento con React-intl. Me encantaría probar el https://github.com/lingui/js-lingui de @tricoder42, simplemente no he tenido tiempo.

@monsieurnebo ¿cuál es el objetivo de ejecutar deletePage justo antes de createPage ? Me gusta tu solución y he intentado implementarla, pero tengo algunos errores.

  • Obtengo pathContext.language incorrectos sin usar deletePage

  • o recibo un error de compilación cuando incluyo deletePage como su ejemplo. (dice TypeError: Cannot read property 'id' of undefined cuando la compilación llega a la etapa run graphql queries )

Esta es la mejor solución que he visto hasta ahora.

@deltaskelta ¡ Me alegro de que te guste!

deletePage se utiliza para cancelar la creación de la página predeterminada por parte de Gatsby. Si no agrega esto, obtendrá sus páginas personalizadas y la predeterminada.

Revisa tu directorio public con y sin esta línea, notarás la diferencia ;)

Acerca de sus errores, es difícil adivinar sin código. ¿Podrías hacer un repositorio con tu código? Entonces le echaría un vistazo.

EDITAR: ¿Te interesaría un ejemplo ?

Oh, no estaba relacionado con la llamada deletePage. Era un problema de no copiar el objeto de las páginas ya que había modificado su ejemplo. Siempre me tropiezo con la copia de objetos después de estar lejos de js por un tiempo;)

@monsieurnebo He estado jugando con su solución y parece que todavía necesita javascript para cargar las configuraciones regionales desde los archivos json, ¿es correcto? Además de eso, parece que todos los componentes que están envueltos con el HOC react-i18next necesitan javascript para representar cualquier cosa en el componente...

¿Puede confirmar si así es como funciona en su extremo?

EDITAR: por cierto, me perdí tu mención de un ejemplo, si tienes tiempo, me encantaría ver un ejemplo completo.

@deltaskelta Haré un ejemplo cuando tenga algo de tiempo libre :)

@monsieurnebo, ¿ puede confirmar que su método no

Sí, es por eso que no es totalmente la "forma de vida estática" 😐

Me encantaría un complemento que genere texto estático en su lugar.

Hmm ya veo. Un renderizado estático sería el camino que necesito seguir...

Estaba pensando, dado que los contextos ya se han pasado con el nombre del idioma para la mayoría de las páginas en su gatsby-node entonces no sería mucho más difícil pedir las claves de mensajes en las consultas de graphql para cada página. y pasarlos de esa manera, pero preferiría usar una herramienta i18n hasta el final si es posible...

@szimek, ¿cómo está react-intl representando las traducciones en el paso de compilación y sin usar ningún js?

Como se mencionó, por lo que puedo ver, Gatsby-plugin-i18n genera texto estático. ¿Revisaste su ejemplo?

@deltaskelta Bueno, todas las traducciones están disponibles durante el tiempo de compilación (es por eso que estoy usando varios diseños), así que "simplemente funciona" ™️ ;) Solo espero que siga funcionando en v2... Puedo preparar una aplicación de muestra si desear. Realmente no he investigado gatsby-plugin-i18n todavía, porque estoy trabajando con Contentful, no con archivos.

No he leído los detalles, pero hay una discusión (o más bien un monólogo) sobre el combo gatsby-plugin-i18n + contenido aquí: https://github.com/angeloocana/gatsby-plugin-i18n/issues/31

@szimek Me interesaría mucho un ejemplo. @sedubois Sé que gatsby-plugin-i18n también genera texto estático, pero no veo dónde ocurre la magia que falta en i18next para generar archivos totalmente estáticos....

Creo que podría ver por qué ahora... ¿Estás pasando react-intl los mensajes a través de pathContext en gatsby-node durante el procesamiento?

EDITAR: si este es el caso (lo que tiene sentido), entonces la biblioteca i18n utilizada es "algo" irrelevante porque uno está pasando mensajes a través del contexto y renderizando estáticamente, lo cual es una configuración pura de Gatsby y la biblioteca i18n solo está manejando casos especiales como fechas y plurales con js.

Luego de más pruebas de i18next y react-intl , parece que el HOC de traducción de i18next requiere javascript para cargarse, por lo que incluso si los mensajes se pasan a través del contexto y se procesan estáticamente, cualquier uso del HOC de traducción hará que todo el componente necesite javascript para poder ejecutarse.

react-intl componente FormattedMessage , por otro lado, muestra un mensaje predeterminado (que se puede basar en el contexto que se le pasa) y lo muestra estáticamente en el html en la compilación.

Por diseño, creo que la integración más natural de i18n sería con react-intl si desea lograr traducciones renderizadas estáticamente en HTML. Si no he entendido bien el flujo que están usando, corríjanme.

@mattferderer tuvo la idea correcta aquí, pero creo que necesita un pequeño ajuste. https://github.com/gatsbyjs/gatsby/issues/3830#issuecomment-362715706

los diseños se representan antes que las páginas, por lo que sin crear múltiples diseños no hay forma de pasar mensajes a través del contexto, desde la función createPages (corríjame si me equivoco aquí). Entonces, para hacer que i18n sea más fácil, creo que el diseño debería simplemente llamar a children() y luego los diferentes diseños por efecto de idioma se lograrían haciendo que gatsby-node crearan diferentes rutas por idioma. páginas de índice de pages/index que se pueden pasar mensajes a través del contexto

EDITAR: perdón por la divagación, pero todo quedó claro y tuve que grabarlo en alguna parte

EDIT2: Definitivamente estoy equivocado arriba, los encabezados y pies de página deben ir en el diseño, simplemente no sé cómo enviarles los mensajes sin crear múltiples diseños. La única otra forma en que puedo pensar sería dividir URL y expresiones regulares para la configuración regional ... pero eso se siente como algo complicado de hacer.

@KyleAMathews con v2 que tiene solo un diseño, ¿cómo podemos pasar datos como mensajes i18n al componente de diseño raíz a través del contexto basado en la ruta o una matriz de claves de idioma?

Si esto se pudiera hacer, entonces sería fácil implementar i18n, pero no veo cómo hacerlo sin hacer varios diseños.

@deltaskelta Este es un ejemplo de lo que estamos haciendo: https://github.com/szimek/gatsby-react-intl-example. Muestra cómo representar publicaciones individuales, cómo generar una página de índice para cada configuración regional, cómo usar los componentes react-intl , cómo usar react-intl injectIntl HOC para establecer un título (o cualquier otra metaetiqueta) para las páginas de índice usando intl.formatMessage helper, etc.

Las páginas generadas son:

  • /en
  • /en/hello-world
  • /pl
  • /pl/witaj-swiecie

En la aplicación real, estamos usando una versión modificada de gatsby-pagination , porque la versión original no admite la opción de diseño. También configuramos el campo post_id para cada publicación que nos permite encontrar traducciones de la misma publicación, por ejemplo, en el caso de esta aplicación de demostración, ambas publicaciones tendrían los mismos post_id .

POR CIERTO. Me acabo de dar cuenta de que lo más probable es que necesitemos generar mapas de sitio separados para cada idioma, de modo que Swiftype (el motor de búsqueda que usamos) sepa qué páginas tenemos.

@deltaskelta brevemente tendría componentes de página para cada idioma y luego tendría un componente de diseño por idioma. Aquí hay una forma en que podrías hacer esto.

// French
import React from 'react'
import FrenchLayout from '../components/layouts/french'
import ImportantPage from '../components/pages/important-page'

export default ({ data }) => (
  <FrenchLayout>
    <ImportantPage {...data} />
  </FrenchLayout>
)

// French query here
// English
import React from 'react'
import EnglishLayout from '../components/layouts/english'
import ImportantPage from '../components/pages/important-page'

export default ({ data }) => (
  <EnglishLayout>
    <ImportantPage {...data} />
  </EnglishLayout>
)

// English query here

@KyleAMathews Estos archivos son plantillas, ¿verdad? ¿Significa que si tengo 3 tipos de página y 7 idiomas, necesitaría 21 plantillas? :)

Lo anterior es la forma más optimizada de hacerlo. Si cada componente de diseño no es tan diferente, puede combinarlos en un componente de diseño y luego cambiar el diseño según el idioma que esté activo.

No he leído los detalles, pero hay una discusión (o más bien un monólogo) sobre el combo gatsby-plugin-i18n + contenido aquí: angeloocana/gatsby-plugin-i18n#31

@sedubois , jaja, sí. Resumen: funcionó e incluyó gatsby-starter-contentful-i18n repositorio inicial de https://github.com/gatsbyjs/gatsby/pull/4138

Interesado en la otra solución anterior, especialmente en cómo se compara con el complemento de la comunidad re: SEO, etc.

@mccrodp Su solución se parece bastante a la mía, la principal diferencia es que gatsby-plugin-i18n no requiere que pase explícitamente la opción layout a createPage , pero lo hace por usted entre bastidores. Sin embargo, todavía usa múltiples diseños;)

Fui con la sugerencia de @KyleAMathews e hice un components/Layout que usa una librería i18n y eliminé mi carpeta de diseño de gatsby por completo. De esta manera, en gatsby-node puedo crear páginas para mis locales y ellos tienen acceso a todo lo que tiene acceso una página, incluidas las rutas.

Luego puedo pasar la configuración regional directamente al componente de diseño que lo pasa a i18n.

Es un pequeño inconveniente tener que envolver cada componente de nivel de página con el diseño, pero ha aclarado mucha confusión y ha simplificado mucho mi código.

Hola @deltaskelta , ¿tienes un ejemplo de tu solución? Me encantaría ver si se puede aprender algo de él para impulsar el complemento i18n de la comunidad. Gracias.

todo mi proyecto está en un estado desordenado en este momento, pero creo que puedo exponer los conceptos básicos...

  1. Sin diseño, porque (si no recuerdo mal), el diseño entra en juego antes que los caminos o algo más que era muy importante, lo que me impedía darle los objetos de mensajes adecuados...

  2. Introduzca los messages y locale adecuados en el gatsby-node través del contexto...

exports.onCreatePage = ({ page, boundActionCreators }) => {
  const { createPage, deletePage } = boundActionCreators;

  if (page.path.includes('404')) {
    return; // no need for localized 404 pages
  }

  return new Promise(resolve => {
    // if it is not the app page then I need localized static pages
    const pages = localizedPages(page);
    deletePage(page);
    pages.map(page => createPage(page));

    resolve();
  });
};

// to be passed to the localized pages so it can calculate the matchPath
const getMatchPath = lang => {
  return `${locales[lang]['path']}/app/:path`;
};

// this is a helper function that makes pages in each language.
const localizedPages = (page, matchPathFunc) => {
  var pages = [];
  Object.keys(locales).map(lang => {
    const path = locales[lang]['path'] + page.path;

    pages.push({
      ...page,
      path: path,
      matchPath: matchPathFunc ? matchPathFunc(lang) : undefined,
      context: {
        locale: lang,
        messages: locales[lang],
        pathRegex: `/.pages${page.path}./` // so pages can match markdown in their dir
      }
    });
  });

  return pages;
};
  1. Ahora cree un componente de diseño global que deba llamarse en cada componente de nivel de página...
// this is the main entrypoint for the layout to the site
const GlobalLayout = ({ locale, children, path }) => {
  const theme = getTheme();
  return (
    <MuiThemeProvider theme={theme}>
      <CssBaseline>
        <IntlProvider locale={locale} messages={locales[locale]}>
          <div>
            <Header locale={locale} messages={locales[locale]} path={path} />
            {children}
          </div>
        </IntlProvider>
      </CssBaseline>
    </MuiThemeProvider>
  );
};
  1. Llame al componente de diseño global con sus componentes de nivel de página que tienen la configuración regional adecuada y los mensajes pasados ​​desde el contexto
const BlogPost = ({ data, pathContext, location }) => {
  const { locale } = pathContext;
  return (
    <GlobalLayout locale={locale} path={location.pathname}>
      <FullWidth>
        <h1>{data.markdownRemark.frontmatter.title}</h1>
        <h3>{data.markdownRemark.frontmatter.date}</h3>
        <div dangerouslySetInnerHTML={{ __html: data.markdownRemark.html }} />
      </FullWidth>
    </GlobalLayout>
  );
};

Tengo que limpiar y reorganizar este código porque lo hice rápido para demostrar que podía funcionar y lo revisaré más tarde... Espero que esto te dé la esencia.

Creé un iniciador gatsby que utiliza js-lingui para traducir mensajes:
https://github.com/dcroitoru/gatsby-starter-i18n-lingui

En la aplicación real estamos usando una versión modificada de gatsby-pagination, porque la versión original no admite la opción de diseño. También configuramos el campo post_id para cada publicación que nos permite encontrar traducciones de la misma publicación, por ejemplo, en el caso de esta aplicación de demostración, ambas publicaciones tendrían el mismo post_id.

@szimek, ¿hay alguna posibilidad de que pueda compartir su paginación modificada de Gatsby? Estaría muy interesado en verlo ya que estoy teniendo un problema similar.

@martynhoyer Mi parche se fusionó, así que volví a la versión original de gatsby-pagination . ¿Qué problema tienes?

Hola @sgoudie
Estoy usando este tutorial: https://www.gatsbyjs.org/blog/2017-10-17-building-i18n-with-gatsby/ pero no encuentro ninguno de mis locales/{lang}/*.json . ¿Alguien tiene una pista?
2018-04-25 12_04_13-o intermedium agora e banco inter

Mi configuración:
gasbty-node.js
```exportaciones javascript.onPostBuild = () => {
console.log('Copiando configuraciones regionales')
fs.copySync(
ruta.join(__dirname, '/src/locales'),
ruta.join(__dirname, '/public/locales')
)
}

```

Agregar

exports.onPostBootstrap = () => {
    console.log("Copying locales");
    fs.copySync(
        path.join(__dirname, "/src/locales"),
        path.join(__dirname, "/public/locales")
    );
};

a gatsby-node.js

@ThiagoMiranda Estaba enfrentando el mismo problema y luego me di cuenta de que gatsby develop no llama a PostBuild, solo gatsby build llama. onPostBootstrap se llama cada vez.

¿Alguien sabe cómo crear https://moz.com/learn/seo/hreflang-tag en los diseños?

@RobinHerzog Los estamos creando en plantillas usando Helmet. Son específicos del tipo de página, por lo que al menos en nuestro caso no tenía sentido crearlos en un diseño.

@szimek gracias por responder. Lo entiendo pero en mi caso seria interesante tenerlo en los layouts.

<link rel="alternate" href={Route['en-us'][this.props.data.prismicDocument.data.group]} hreflang="en-us" /> <link rel="alternate" href={Route['fr-fr'][this.props.data.prismicDocument.data.group]} hreflang="fr-fr" />

Por el momento, es como dijiste, copia esas líneas en todas las plantillas.

Recién estoy comenzando a desarrollar con React/JavaScript, pero todo lo que he visto para admitir i18n era demasiado complejo. Aquí está mi propio trabajo para el uso más común: wise-starter

La recarga en vivo, compatible con SEO y los idiomas predeterminados no usan la clave en la URL.
Todas las páginas .js se generan para todos los idiomas.
Todos los diseños y .md deben crearse para todos los idiomas para evitar errores.
Los componentes LangSelect y Link son i18n smart.

Si me pueden ayudar y explicarme como podría mejorar mi código y estilo se los agradecería.

@Tom-Pichaud, creo que dado que no está pasando mensajes a través de los componentes de la página, no se representarán de forma estática allí.

La configuración de Markdown i18n en gatsby-node parece ser similar a lo que la gente ha estado haciendo aquí, pero tengo curiosidad si está obteniendo una representación estática con javascript deshabilitado en los componentes de su página.

Sí, obtengo renderizado estático, ese era mi objetivo de todos modos, ¡ i18n-react hace el truco!

@TomPichaud, ¿ podría compartir cómo el i18n-react que mencionó es mejor que js-lingui ?

Una cosa que no entiendo es la necesidad real de paquetes externos para cargar los mensajes traducidos (aparte de la pluralización y los parientes, probablemente).

Para un sitio simple con contenido estático, solo estoy duplicando las páginas para cada idioma onCreatePage y paso la configuración regional a context :

// some file with the locales
const locales = {
  en: {
    path: 'en',
    default: true,
  },
  pt: {
    path: 'pt',
  },
}
// gatsby-node.js
exports.onCreatePage = ({ page, boundActionCreators }) => {
  const { createPage, deletePage } = boundActionCreators

  return new Promise(resolve => {
    deletePage(page)

    Object.keys(locales).map(lang => {
      const localizedPath = locales[lang].default
        ? page.path
        : locales[lang].path + page.path

      return createPage({
        ...page,
        path: localizedPath,
        context: {
          locale: lang,
        },
      })
    })

    resolve()
  })
}

Luego, en la página real, uso la configuración regional en el contexto para consultar el contenido usando el filtro de graphql.

Digamos que tengo el contenido de la casa en /data/home/en.js y /data/home/pt.js :

import React from 'react'

const IndexPage = ({ pathContext: { locale }, ...props }) => {
  const { childHomeJson: data } = props.data.allFile.edges[0].node

  return <div>{data.hello}</div>
}

export const query = graphql`
  query HomeContent($locale: String) {
    allFile(filter: { name: { eq: $locale } }) {
      edges {
        node {
          childHomeJson {
            hello
          }
        }
      }
    }
  }
`

export default IndexPage

funciona bien con netlifyCMS (aunque un poco detallado hasta que admiten i18n) e imágenes en los archivos JSON (aunque necesitamos crear un nuevo NodeField con la ruta relativa para que el sistema de archivos de Gatsby lo obtenga)

¿No es esto suficiente para la mayoría de los casos?

Todavía estoy probando y no he usado nada de esto en producción, pero estoy pensando en usar la API de contexto de react para la configuración regional para resolver algunos problemas pendientes, como enlaces localizados.

@pbrandone Este parece ser un gran enfoque para mí. Creo que algo similar debería documentarse oficialmente.

Gracias por todos los aportes, la cantidad de ideas discutidas aquí marca claramente la demanda de soporte i18n bien documentado.

@pbrandone ¿Significa que tiene que especificar explícitamente todas las claves utilizadas por cualquier componente secundario en IndexPage en esta consulta y pasar las traducciones a todos los componentes a través de accesorios?

Además, utilizo reglas de pluralización y fechas relativas, por lo que tengo que cargar datos adicionales específicos de la configuración regional de todos modos:/

Sin embargo, estoy de acuerdo en que sería genial tener una documentación oficial sobre cómo hacer i18n sin ninguna biblioteca para casos simples y luego cómo hacerlo con las bibliotecas más populares.

@szimek bueno, en este caso sí.

Es muy fácil agregar react-intl (o cualquier otra biblioteca i18n) aunque:

// in src/components/layout/index.js

import React from 'react'
import { IntlProvider, addLocaleData } from 'react-intl'

// Locale data
import enData from 'react-intl/locale-data/en'
import ptData from 'react-intl/locale-data/pt'

// Messages
import en from '../../data/en.json'
import pt from '../../data/pt.json'

const messages = { en, pt }

addLocaleData([...enData, ...ptData])

const Layout = ({ locale, children }) => (
  <IntlProvider locale={locale} messages={messages[locale]}>
    {children}
  </IntlProvider>
)

export default Layout

y luego en las paginas:

import React from 'react'
import { FormattedMessage } from 'react-intl'

import Layout from '../components/layouts'

const IndexPage = ({ pathContext: { locale } }) => (
  <Layout locale={locale}>
    <FormattedMessage id="hello" />
  </Layout>
)

export default IndexPage

Pero entonces no podría usar varios archivos JSON (por ejemplo, por página), ni tener todos los datos de CMS en esos archivos para consultar y transformar con el poder de graphql.
Con el enfoque de graphql podríamos, por ejemplo, tener algunas claves con rutas para imágenes en esos archivos JSON para cargar diferentes imágenes por configuración regional y aún así poder usar gatsby-image en ellas.
Y luego agregue netlify CMS para editar esos archivos JSON 😃

No he examinado correctamente Gatsby v2, pero aparentemente hay un componente StaticQuery que nos permite consultar componentes secundarios (¡que alguien me corrija si me equivoco!)

Si ese es el caso, podríamos crear un contexto de reacción para que la configuración regional esté disponible en cualquier lugar y luego consultar las claves necesarias en cada componente con el filtrado de configuración regional.

@pbrandone tienes razón, se renderiza estáticamente de esa manera. Recuerdo que lo probé en el pasado y fallé, pero eso fue antes de que tuviera una comprensión decente de cómo funciona Gatsby, podría haber tenido mi configuración de react-intl en el navegador Gatsby, que parece que podría no mostrarse sin javascript. Mi solución ahora se parece a la tuya

@KyleAMathews Estoy tratando de actualizar nuestra página a Gatsby a v2 y tengo un problema con nuestra configuración de react-intl y consultas de graphql.

Anteriormente expliqué cómo uso diseños específicos de idioma para cargar datos de idioma en Gatsby v1: https://github.com/szimek/gatsby-react-intl-example. En Gatsby v2 tuve la idea de reemplazar estos diseños con componentes de página específicos del idioma. Tendría un componente src/templates/Post.js independiente del idioma y luego componentes específicos del idioma como src/templates/Post.en.js , src/templates/Post.de.js que solo cargarían datos de idioma y representarían el componente independiente del idioma.

En su comentario anterior (https://github.com/gatsbyjs/gatsby/issues/3853#issuecomment-367115380) mostró un ejemplo en el que cada componente de la página tiene una consulta específica del idioma.

El problema es que cuando llamo a createPage , paso los nombres de estos componentes específicos del idioma (por ejemplo, src/templates/Post.en.js ) como opción component , pero la consulta de graphql está en el componente independiente del idioma, porque es __exactamente igual para todos los idiomas__ (depende de locale , pero lo paso en context ). Me gustaría evitar repetir exactamente la misma consulta en todos estos componentes específicos del idioma.

¿Alguna idea de cómo resolverlo? ¿Puedo extraer esta consulta a una variable? Cuando lo probé, Gatsby se queja de que los nombres de las consultas y los fragmentos son iguales...

Recientemente agregué un iniciador de Gatsby predeterminado con funciones de rutas de URL en varios idiomas y detección de idioma del navegador. (manifestación)

gatsby-starter-default-intl

Características:

  • Localización (multilenguaje) proporcionada por react-intl .

  • Redirección automática basada en el idioma preferido del usuario en el navegador proporcionado por browser-lang .

  • Admite rutas URL en varios idiomas dentro de un solo componente de página. Eso significa que no tiene que crear páginas separadas como pages/en/index.js o pages/ko/index.js .

  • Basado en gatsby-starter-default con la menor modificación.

@wiziple ¡Gracias! Parece muy interesante. No tenía idea de que puedes hacer algo como esto: https://github.com/wiziple/gatsby-starter-default-intl/blob/master/src/i18n/withIntl.js#L38 ;) Con suerte, todavía funciona en Webpack 4...

¿Es posible cargar datos de configuración regional de la misma manera aquí https://github.com/wiziple/gatsby-starter-default-intl/blob/master/src/i18n/withIntl.js#L6 ? Admitimos 6 (pronto 7 idiomas), por lo que sería genial si pudiera cargar solo el idioma para el que estoy creando la página. no es gran cosa si no es posible; afortunadamente, estos archivos de datos locales son relativamente pequeños.

También tendré que investigar cómo genera estas páginas, porque en mi caso no todas las páginas están traducidas a todos los idiomas (no hay un único idioma de "origen"), por lo que la solución con onCreatePage probablemente no funcionará. en mi caso.

Con suerte, esto resolverá mi problema con la misma consulta de graphql en cada componente de página específico del idioma.

@szimek
El sitio web que administro tiene 14 idiomas y cada archivo de idioma tiene entre 12 y 15 KB. Estoy bastante seguro de que debemos proporcionar el idioma correcto para cada enrutador de idioma en el momento de la compilación para generar datos de SEO. Así que no estoy seguro de cómo puedo manejar esto sin proporcionar todos los idiomas.

Entiendo que a veces es difícil proporcionar todas las páginas traducidas a todos los idiomas. Es posible que pueda resolver esto proporcionando alguna excepción en onCreatePage en gatsby-node.js . En mi caso, simplemente lo resolví al no ofrecer un idioma traducido independientemente del idioma del enrutador. 😆 Puede encontrar un sitio web de exhibición en producción desde el iniciador README.md y verificar su rendimiento.

@wiziple ¡ Muchas gracias!

He utilizado el withIntl componente dinámico con require truco para traducciones (no tengo ni idea de si hay desventajas en el uso que) y parece que gran trabajo. Resolvió el problema con el que estaba luchando: cómo manejar la misma consulta de graphql en varios componentes de página específicos del idioma, al tener un solo componente de página para todos los idiomas.

@wiziple gracias por compartir el repositorio. Me tienes en el camino correcto 😄 🎉

lingui parece ser una mejor alternativa . No creo que @dcroitoru obtuviera el reconocimiento adecuado por un gran ejemplo. Solo necesita un poco de amor para llevarlo a Gatsby 2.0

Estoy de acuerdo en que Lingui es realmente agradable, aunque todavía necesita un iniciador completo, con la última versión de Gatsby pero también Lingui. El iniciador mencionado no es oficial y le faltaban algunas características la última vez que lo revisé (por ejemplo, usar un cargador para ejecutar la compilación de lingui sobre la marcha). El autor de Lingui, @ tricoder42, dijo que proporcionaría documentación cuando Lingui v3 esté disponible (lo que parece ser pronto).

NB: noté que mi necesidad de una biblioteca i18n disminuyó después de integrar un CMS (DatoCMS), pero todavía necesito Lingui para algunas cadenas que no encuentran su lugar en el CMS y también para la pluralización y posiblemente otras cosas más adelante, así que definitivamente Quiero mantenerlo en mi base de código.

De todos modos, en mi caso, la existencia de gatsby-plugin-i18n hizo que las cosas fueran bastante confusas, ya que no se mantiene, tiene un nombre confuso y desvía la atención de estas otras soluciones realmente agradables como js-lingui y CMSes que luego tomé un mientras que averiguar y ensamblar juntos.

He hecho dos ejemplos de internacionalización con la integración de react-intl

El primer ejemplo se centra en agrupar solo las traducciones actuales en fragmentos js (algo que no pude encontrar en otros complementos que verifiqué).

El segundo ejemplo se centra en el uso de consultas dinámicas para proporcionar solo las traducciones solicitadas para una determinada combinación de página/idioma.

Con suerte, estos ejemplos serían útiles para alguien.

También hice una publicación mediana rápida (y olvidé publicarla aquí) con prácticamente lo que hay en https://github.com/gatsbyjs/gatsby/issues/3853#issuecomment -395432693 (aunque un poco más en profundidad).

https://blog.significa.pt/i18n-with-gatsby-528607b4da81 para cualquier persona interesada

Los problemas antiguos se cerrarán después de 30 días de inactividad. Este problema ha estado inactivo durante 20 días y se marca como obsoleto. ¡Responda aquí o agregue la etiqueta "no obsoleto" para mantener este problema abierto!

Hola chicos, ha pasado casi un año 😅

Recientemente lancé el nuevo complemento gatsby gatsby-plugin-intl que convierte fácilmente su sitio web gatsby en un marco de internacionalización

DEMOSTRACIÓN: https://gatsby-starter-default-intl.netlify.com

  • Marco de internacionalización listo para usar impulsado por react-intl

  • Admite la redirección automática basada en el idioma preferido del usuario en el navegador

  • Admite rutas URL en varios idiomas en un solo componente de página. Esto significa que no tiene que crear páginas separadas como pages/en/index.js o pages/ko/index.js .

  • Como algunos de ustedes sugirieron anteriormente, ahora incluye solo el idioma actual durante el tiempo de compilación.

Además, quiero mencionar que muchos de los ejemplos / iniciadores de i18n en realidad se procesan en el lado del cliente. La mejor manera de verificar si la aplicación se procesa como SSR es ver el código fuente y verificar si existen los textos localizados. Vuelva a verificar este asunto cuando internacionalice su sitio web gatsby para SEO.

Hola chicos, ha pasado casi un año 😅

Recientemente lancé el nuevo complemento gatsby gatsby-plugin-intl que convierte fácilmente su sitio web gatsby en un marco de internacionalización

DEMOSTRACIÓN: https://gatsby-starter-default-intl.netlify.com

  • Marco de internacionalización listo para usar impulsado por react-intl
  • Admite la redirección automática basada en el idioma preferido del usuario en el navegador
  • Admite rutas URL en varios idiomas en un solo componente de página. Esto significa que no tiene que crear páginas separadas como pages/en/index.js o pages/ko/index.js .
  • Como algunos de ustedes sugirieron anteriormente, ahora incluye solo el idioma actual durante el tiempo de compilación.

Además, quiero mencionar que muchos de los ejemplos / iniciadores de i18n en realidad se procesan en el lado del cliente. La mejor manera de verificar si la aplicación se procesa como SSR es ver el código fuente y verificar si existen los textos localizados. Vuelva a verificar este asunto cuando internacionalice su sitio web gatsby para SEO.

Hola, @wiziple, muchas gracias, me estaba volviendo loco buscando una solución para localizar a Gatsby.
Tal vez no entendí el punto pero, ¿tiene TODAS las cadenas de un idioma en un solo archivo?
¿Sería posible dividir el JSON de cada idioma en más archivos, quizás usando la misma estructura de componentes?

@ cant89 Veo su punto, pero actualmente no es posible sin cambiar el código del complemento. O puede crear una secuencia de comandos que analice el directorio src y tome todos los archivos de idioma de los componentes. Combinar en un JSON y luego enganchar en gatsby develop o gatsby build .
Una vez que obtenga todos los archivos JSON y los combine como un objeto anidado, también puede convertirlo como un objeto plano.
https://github.com/yahoo/react-intl/wiki/Upgrade-Guide#flatten-messages-object

Tenemos un buen ejemplo de configuración para i18n. https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n. Realmente no tenemos una opinión sobre los frameworks i18n. Solo elige uno a tu gusto.

Tenemos un buen ejemplo de configuración para i18n. https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n. Realmente no tenemos una opinión sobre los frameworks i18n. Solo elige uno a tu gusto.

Genial, gracias, voy a intentarlo!
De todos modos, el enlace en el archivo Léame está roto, probablemente te refieres a https://using-i18n.netlify.com/ ?

@wardpeet ¿Su ejemplo genera cadenas traducidas estáticas (en tiempo de compilación)? ¿O genera el texto durante el tiempo de ejecución?

@monsieurnebo parece tiempo de construcción

@ cant89 todavía estamos actualizando el dns, por lo que estará listo pronto por ahora

@monsieurnebo está en tiempo de construcción. Crea una copia de su sitio web para cada idioma para que todo se pueda construir estáticamente. Esto significa que su sitio web se mantiene rápido ya que todo es solo un .html.

No estoy seguro de dónde más preguntar esto, pero es un poco relevante. ¿Alguno de estos complementos es compatible con pathPrefix ?

i.e. 
// gatsby-config.js

modules.exports = {
    pathPrefix: 'bar'
}

https://foo.com => https://foo.com/bar

but now my language locales will now be https://foo.com/bar/de-DE/
when I think I would prefer it be https://foo.com/de-DE/bar if that makes sense.

Hmm, interesante, creo que lo primero suele tener más sentido, ya que pathPrefix hace que tu dominio sea domain.com/prefix así que cambia la raíz cuando hayas instalado Gatsby en un subdirectorio, si no lo instalas en un subdirectorio no lo necesita, si usa un subdirectorio cambiando el prefijo para que sea posterior al idioma lo rompería ...

Ahora surge la pregunta, ¿por qué estás usando pathPrefix en primer lugar?

Ref: documentos pathPrefix

Hola,

La mayoría de las discusiones aquí son sobre cómo i18n un sitio de Gatsby. Pero hay una diferencia entre tener un POC funcionando y tener un sistema optimizado listo para producción.

Si está interesado en leer más sobre la división de código y los archivos i18n, y por qué la mayoría de las soluciones en este hilo no están optimizadas, este problema le resultará útil.

¿Fue útil esta página
0 / 5 - 0 calificaciones

Temas relacionados

dustinhorton picture dustinhorton  ·  3Comentarios

totsteps picture totsteps  ·  3Comentarios

ghost picture ghost  ·  3Comentarios

mikestopcontinues picture mikestopcontinues  ·  3Comentarios

rossPatton picture rossPatton  ·  3Comentarios