Gatsby: Adicione um guia oficial para internacionalizar sites com Gatsby

Criado em 4 fev. 2018  ·  74Comentários  ·  Fonte: gatsbyjs/gatsby

Vendo as reações ao meu comentário em outra questão , decidi abrir esta questão.

Eu acho que i18n é muito mais difícil do que deveria ser. Não consegui encontrar nenhuma documentação oficial ou plugin para internacionalizar conteúdo em sites criados por Gatsby. Me deparei com jsLingui , que parece resolver a maioria dos problemas, mas ainda não há guias sobre como manter, por exemplo, arquivos/páginas de markdown em diferentes idiomas.

documentation

Comentários muito úteis

Fala galera, já faz quase um ano 😅

Recentemente, lancei o novo plugin gatsby gatsby-plugin-intl que facilmente transforma seu site gatsby em uma estrutura de internacionalização pronta para uso.

DEMO: https://gatsby-starter-default-intl.netlify.com

  • Estrutura de internacionalização pronta para uso alimentado por react-intl

  • Suporta redirecionamento automático com base no idioma preferido do usuário no navegador

  • Suporta rotas de URL em vários idiomas em um único componente de página. Isso significa que você não precisa criar páginas separadas, como pages/en/index.js ou pages/ko/index.js .

  • Como alguns de vocês sugeriram acima, agora ele agrupa apenas o idioma atual durante o tempo de compilação.

Além disso, quero mencionar que muitos dos exemplos/iniciadores do i18n são realmente renderizados no lado do cliente. A melhor maneira de verificar se o aplicativo é renderizado como SSR é visualizar o código-fonte e verificar se os textos localizados existem. Por favor, verifique este assunto quando você internacionalizar seu site gatsby para SEO.

Todos 74 comentários

Existe este artigo sobre o uso de i18next com GatsbyJS , que é o método mais avançado até agora para mim.

Mas eu não sinto que i18next seja a "maneira estática".

Sobre o post do blog, eu tinha essas dúvidas/reservas:
https://twitter.com/semdubois/status/930389055388508160

Existe https://github.com/angeloocana/gatsby-plugin-i18n mas tem várias limitações e não está recebendo muita atividade/atenção. Pode ajudar a movê-lo dentro do repositório Gatsby. Eu também adoraria uma solução consolidada adequada.

Eu também me deparei com o js lingui e parece promissor, especialmente com a v2 recém-lançada.

Também estou tentando descobrir isso. Usar o método i18next no post é o mais conveniente e intuitivo, mas fiquei com duas perguntas....

  1. como posso incorporar diferentes arquivos de remarcação para idiomas como na solução gatsby-plugin-i18n?

  2. Isso dispensa completamente a renderização estática do conteúdo?

FYI @angeloocana

Vou escrever um breve resumo de como lidamos com isso no momento usando react-intl . Este aplicativo ainda não está em produção, então ainda podemos encontrar alguns problemas com essa configuração, mas parece funcionar bem até agora.

Mantemos quase todo o nosso conteúdo (que foi migrado do nosso blog Wordpress) em Contentful . Não usamos seus recursos de tradução, mas temos um espaço (tipo de projeto ou pasta) por idioma. Estamos usando o plugin gatsby-source-contentful para buscar esses dados, no entanto, anteriormente estávamos buscando e convertendo esses dados em arquivos JSON e usamos o plugin gatsby-source-filesystem (usamos uma estrutura de pastas como /en/blog/... , /de/blog/... ), então realmente não importa se alguém está usando Contentful ou não, desde que cada nó conheça sua localidade.

Também temos algum texto como rótulos de botão, alguns links ou conteúdo estático que não vem do Contentful, mas é traduzido no Transifex e sincronizado com arquivos JSON armazenados no repositório. Para esta parte precisávamos usar alguma biblioteca i18n e decidimos usar react-intl , só porque eu já a conheço e sei que ela também lida com formatação de data e número. Veja como configuramos: https://github.com/gatsbyjs/gatsby/issues/3830#issuecomment -362710469. Estamos então usando, por exemplo, intl.formatMessage ao gerar meta tags e componentes <FormattedMessage /> , <FormattedDate /> etc. em modelos.

@szimek Se eu entendi corretamente, você tem react-intl lidando com a tradução do texto do componente enquanto as postagens estão em rotas codificadas no diretório de páginas?

Isso significaria que as rotas codificadas são as únicas que são renderizadas estaticamente? E as traduções entre componentes do i18n são renderizadas dinamicamente?

@deltaskelta Não tenho certeza se entendi sua pergunta.

Não temos nenhum roteamento personalizado do lado do cliente (como descrito, por exemplo, aqui ) no momento, apenas páginas geradas estaticamente. Ao gerar uma página post (também geramos páginas index e category paginadas e específicas de localidade usando uma versão ligeiramente modificada do plugin gatsby-pagination ), estamos usando o seguinte 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,
    },
  });
});

Mesmo se você tiver o JS desabilitado, todo o conteúdo, incluindo metatags, será renderizado no idioma correto.

Do nosso lado, estamos usando i18next com alguns ajustes

Os principais princípios são os seguintes:

  • Locales estão disponíveis como arquivos .json e simplesmente movidos para o diretório /dist/ durante a compilação.
  • Estamos trabalhando em uma única página que produzirá tantas versões estáticas quantos idiomas houver, usando onCreatePage (em gatsby-node.js ).
  • As informações das páginas (por exemplo, pathname) são centralizadas em um único arquivo.

Localidades

As traduções foram agrupadas principalmente por página, com um namespace (= arquivo JSON) por página + um arquivo global (para textos compartilhados, como cabeçalho/rodapé).

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

Infelizmente, não há recarga a quente nas modificações de localidades 👎

i18próximo

i18next é inicializado com a seguinte configuração:

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);
}

Informações da página

Antes de gerar todas as versões i18n de nossas páginas, precisamos saber algumas informações que agrupamos em um arquivo 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...

Onde as chaves são os nomes dos páginas e os namespaces são os nomes dos arquivos locais . Podem ser diferentes 🚨

Nesse caso:

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

E onde path são os nomes dos caminhos de nossas futuras versões (idiomas) de nossas páginas.

Construir páginas

Com o mesmo exemplo acima, nosso objetivo aqui é construir uma versão FR + EN das páginas inicial e do projeto.

Para que isso aconteça, criamos uma função 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 auxiliar será então usado durante o gancho onCreatePage , selecionando cada página por meio de um regex:

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();
  });
}

Agora temos duas versões de cada página, com um nome de caminho personalizado (do nosso arquivo de informações de páginas). Você pode ter notado que estamos passando uma informação language para cada página via pathContext . Esse valor será usado em cada página para exibir o idioma correto.

Exibir o idioma certo

Estamos usando a classe React para páginas, com o seguinte decorador sabendo automaticamente o idioma da página atual e atualizando o i18n se necessário:

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} />;
      }

    }
  );

}

Então chame nas páginas:

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

Pfew, tudo o que você precisa fazer agora é usar a função de tradução do i18next.

Conclusão

👍 Um único arquivo de origem que gera quantas versões forem necessárias durante a compilação
👍 Gerenciamento fácil de saída de páginas
👍 Alguns esforços no início, mas depois tudo é simples

👎 Sem hot-reload para localidades

Eu sinto que não é realmente o "modo de vida estático" ... Mas isso é o melhor que conseguimos por enquanto.

Eu adoraria ver o que você pensa sobre isso, e como vocês lidam com isso.

PS. Poke @szimek , era isso que eu queria te mostrar isso alguns dias atrás :)

Eu tenho usado o https://github.com/angeloocana/gatsby-plugin-i18n + React-intl do @angeloocana e não é tão complicado quanto os métodos acima, mas existem algumas limitações (consulte problemas de repo) e estou não tão feliz com o React-intl. adoraria experimentar o https://github.com/lingui/js-lingui do @tricoder42, só não tive tempo.

@monsieurnebo qual é o objetivo de executar deletePage antes de createPage ? Eu gosto da sua solução e tentei implementá-la, mas tenho alguns erros.

  • Ou errei pathContext.language sem usar deletePage

  • ou recebo um erro de compilação quando incluo deletePage como seu exemplo. (diz TypeError: Cannot read property 'id' of undefined quando a compilação chega ao estágio run graphql queries )

Esta é a melhor solução que vi até agora.

@deltaskelta Fico feliz em ver que você gosta!

deletePage é usado para cancelar a criação da página padrão por Gatsby. Se você não adicionar isso, obterá suas páginas personalizadas e a padrão.

Verifique seu diretório public com e sem esta linha, você notará a diferença ;)

Sobre seus erros, é difícil adivinhar sem código. Você poderia fazer um repositório com seu código? Eu daria uma olhada nele então.

EDIT: Você estaria interessado em um exemplo ?

Ah, não estava relacionado à chamada deletePage. Foi um problema de não copiar o objeto pages desde que modifiquei seu exemplo. Eu sempre tropeço com a cópia de objetos depois de ficar longe do js por um tempo;)

@monsieurnebo Eu tenho brincado com sua solução e parece que você ainda precisa de javascript para carregar as localidades dos arquivos json está correto? Além disso, parece que todos os componentes envolvidos com o react-i18next HOC precisam de javascript para renderizar qualquer coisa no componente ...

Você pode confirmar se é assim que está funcionando no seu final?

EDIT: aliás, eu perdi sua menção de um exemplo, se você tiver tempo, adoraria ver um exemplo completo.

@deltaskelta vou fazer um exemplo quando tiver algum tempo livre :)

@monsieurnebo você pode confirmar que seu método não renderiza estaticamente as strings no html e requer javascript?

Sim, é por isso que não é totalmente o "modo de vida estático" 😐

Eu adoraria um plugin que gerasse texto estático.

Ah, entendi. Uma renderização estática seria o caminho que eu preciso seguir ...

Eu estava pensando, como os contextos já são passados ​​com o nome do idioma para a maioria das páginas em seu gatsby-node então não seria muito mais difícil apenas pedir as chaves de mensagens nas consultas graphql para cada página e passá-los dessa maneira, mas prefiro usar uma ferramenta i18n até o fim, se possível ...

@szimek como react-intl renderizando as traduções na etapa de construção e não usando nenhum js?

Como mencionado, até onde posso ver, o Gatsby-plugin-i18n gera texto estático. Você verificou o exemplo dele?

@deltaskelta Bem, todas as traduções estão disponíveis durante o tempo de construção (é por isso que estou usando vários layouts), então "simplesmente funciona" ™️ ;) Espero que continue funcionando na v2... Posso preparar um aplicativo de amostra se você quer. Eu realmente não olhei gatsby-plugin-i18n ainda, porque estou trabalhando com Contentful, não com arquivos.

Eu não li os detalhes, mas há uma discussão (ou melhor, um monólogo) sobre o combo gatsby-plugin-i18n + contentful aqui: https://github.com/angeloocana/gatsby-plugin-i18n/issues/31

@szimek eu estaria muito interessado em um exemplo. @sedubois Eu sei que o gatsby-plugin-i18n também gera texto estático, mas não vejo onde acontece a mágica que está faltando no i18next para gerar arquivos totalmente estáticos ....

Acho que posso ver por que agora... você está passando react-intl as mensagens por pathContext em gatsby-node durante a renderização?

EDIT: se este for o caso (o que faz sentido), a biblioteca i18n usada é "um pouco" irrelevante porque está passando mensagens através do contexto e renderizando estaticamente, o que é uma configuração gatsby pura e a biblioteca i18n está lidando apenas com casos especiais, como datas e plurais com js.

Após testes adicionais de i18next e react-intl , parece que o HOC de tradução de i18next requer javascript para carregar, portanto, mesmo que as mensagens sejam passadas via contexto e renderizadas estaticamente, qualquer uso do translate HOC renderizará todo o componente que precisa de javascript para ser executado.

react-intl componente FormattedMessage , por outro lado, renderiza uma mensagem padrão (que pode ser baseada no contexto passado para ele) e renderiza estaticamente no html na compilação.

Por design, eu acho que a integração mais natural do i18n seria com react-intl se você quiser obter traduções renderizadas estaticamente em HTML. Se eu entendi mal o fluxo que vocês estão usando, por favor me corrija

@mattferderer teve a ideia certa aqui, mas acho que precisa de alguns ajustes. https://github.com/gatsbyjs/gatsby/issues/3830#issuecomment -362715706

os layouts são renderizados antes das páginas, portanto, sem criar vários layouts, não há como passar mensagens através do contexto, a partir da função createPages (corrija-me se estiver errado aqui). Então, para tornar o i18n mais fácil, eu acho que o layout deveria apenas chamar children() e, em seguida, os diferentes layouts por efeito de idioma seriam realizados com gatsby-node criando diferentes caminhos por idioma páginas de índice de pages/index que podem ser passadas mensagens via contexto

EDIT: desculpe a divagação, mas tudo ficou claro e eu tive que gravá-lo em algum lugar

EDIT2: Estou definitivamente errado acima, cabeçalhos e rodapés precisam ir no layout, só não sei como enviar as mensagens para eles sem criar vários layouts. A única outra maneira que posso pensar seria dividir urls e regex para locale ... mas isso parece uma coisa hacky de se fazer

@KyleAMathews com v2 tendo apenas um layout, como podemos passar dados como mensagens i18n para o componente de layout raiz por meio de contexto baseado no caminho ou em uma matriz de chaves de idioma?

Se isso pudesse ser feito, seria fácil implementar o i18n, mas não consigo ver como fazer isso sem fazer vários layouts

@deltaskelta Aqui está um exemplo do que estamos fazendo: https://github.com/szimek/gatsby-react-intl-example. Ele mostra como renderizar posts individuais, como gerar página de índice para cada localidade, como usar componentes react-intl , como usar react-intl injectIntl HOC para definir um título (ou qualquer outra metatag) para páginas de índice usando intl.formatMessage helper etc.

As páginas geradas são:

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

No aplicativo real, estamos usando uma versão modificada de gatsby-pagination , porque a versão original não suporta a opção de layout. Também definimos o campo post_id para cada postagem que nos permite encontrar traduções da mesma postagem, por exemplo, no caso deste aplicativo de demonstração, ambas as postagens teriam o mesmo post_id .

POR FALAR NISSO. Acabei de perceber que provavelmente precisaremos gerar sitemaps separados para cada idioma, para que o Swiftype (mecanismo de pesquisa que usamos) saiba quais páginas temos.

@deltaskelta brevemente você teria componentes de página para cada idioma e, em seguida, teria um componente de layout por idioma. Aqui está uma maneira de fazer isso.

// 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 Esses arquivos são modelos, certo? Isso significa que, se eu tiver 3 tipos de página e 7 idiomas, precisarei de 21 modelos? :)

O acima é a maneira mais otimizada de fazê-lo. Se cada componente de layout não for tão diferente, você poderá combiná-los em um componente de layout e alternar o layout dependendo do idioma ativo.

Eu não li os detalhes, mas há uma discussão (ou melhor, um monólogo) sobre o combo gatsby-plugin-i18n + contentful aqui: angeloocana/gatsby-plugin-i18n#31

@sedubois , haha, sim. Resumo: funcionou e incluiu o repositório inicial gatsby-starter-contentful-i18n nos documentos do Gatsby através deste PR: https://github.com/gatsbyjs/gatsby/pull/4138

Interessado na outra solução acima, especialmente como ela se compara ao plugin da comunidade re: SEO, etc.

@mccrodp Sua solução é bem parecida com a minha, a principal diferença é que gatsby-plugin-i18n não exige que você passe explicitamente a opção layout para createPage , mas faz isso para você Por trás das cenas. No entanto, ainda está usando vários layouts;)

Eu fui com a sugestão do @KyleAMathews e fiz um components/Layout que usa uma biblioteca i18n e exclui minha pasta de layout gatsby completamente. Dessa forma, em gatsby-node eu posso fazer páginas para meus locais e eles têm acesso a tudo que uma página tem acesso, incluindo caminhos.

Posso então passar locale diretamente para o componente de layout que o passa para i18n.

É um pouco inconveniente ter que envolver cada componente de nível de página com o layout, mas esclareceu muita confusão e simplificou muito meu código.

Ei @deltaskelta , você tem um exemplo da sua solução? Adoraria ver se algo pode ser aprendido com isso para enviar upstream para o plugin i18n da comunidade. Obrigado.

todo o meu projeto está em um estado confuso agora, mas acho que posso expor o básico...

  1. Sem layout - porque (se bem me lembro), o layout entra em jogo antes de caminhos ou outra coisa que era muito importante, o que me impedia de dar os objetos de mensagens apropriados ...

  2. Alimente os messages e locale apropriados no gatsby-node através do 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. Agora crie um componente de layout global que precisa ser chamado em cada componente de nível 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. Chame o componente de layout global com seus componentes de nível de página que possuem a localidade apropriada e as mensagens passadas do 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>
  );
};

Eu tenho que limpar e reorganizar este código porque eu meio que fiz isso rápido para provar que poderia funcionar e vou revisitá-lo mais tarde...

Eu criei um iniciador gatsby que faz uso de js-lingui para traduzir mensagens:
https://github.com/dcroitoru/gatsby-starter-i18n-lingui

No aplicativo real, estamos usando uma versão modificada do gatsby-pagination, porque a versão original não suporta a opção de layout. Também definimos o campo post_id para cada post que nos permite encontrar traduções do mesmo post, por exemplo, no caso deste aplicativo de demonstração, ambos os posts teriam o mesmo post_id.

@szimek existe alguma chance de você compartilhar sua paginação gatsby modificada? Gostaria muito de ver, pois estou com um problema semelhante.

@martynhoyer Meu patch foi mesclado, então voltei para a versão original de gatsby-pagination . Qual problema você está tendo?

Olá @sgoudie
Estou usando este tutorial: https://www.gatsbyjs.org/blog/2017-10-17-building-i18n-with-gatsby/ mas não consigo encontrar nenhum dos meus locales/{lang}/*.json . Alguém tem uma pista?
2018-04-25 12_04_13-o intermedium agora e banco inter

Minha configuração:
gasbty-node.js
```javascriptexports.onPostBuild = () => {
console.log('Copiando localidades')
fs.copySync(
path.join(__dirname, '/src/locales'),
path.join(__dirname, '/public/locales')
)
}

```

Adicionar

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

para gatsby-node.js

@ThiagoMiranda Eu estava enfrentando o mesmo problema, então percebi que gatsby develop não chama onPostBuild, apenas gatsby build chama. onPostBootstrap é chamado a cada vez.

Alguém sabe como criar https://moz.com/learn/seo/hreflang-tag nos layouts ?

@RobinHerzog Estamos criando-os em modelos usando o Helmet. Eles são específicos para o tipo de página, então pelo menos no nosso caso não fazia sentido criá-los em um layout.

@szimek obrigado pela resposta. Eu entendo, mas no meu caso seria interessante tê-lo nos 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 enquanto, é como você disse, copie essas linhas em todos os modelos.

Estou apenas começando a desenvolver com React/JavaScript, mas tudo o que vi para suportar i18n era muito complexo. Aqui está o meu próprio trabalho para uso mais comum: iniciante sábio

Os idiomas de recarga ao vivo, SEO amigável e padrão não usam a chave no url.
Todas as páginas .js são geradas para todos os idiomas.
Todos os layouts e .md devem ser criados para todos os idiomas para evitar erros.
O componente LangSelect e Link são i18n inteligentes.

Se você puder me ajudar e me explicar como eu poderia melhorar meu código e estilo eu ficaria grato.

@Tom-Pichaud, acredito que, como você não está passando mensagens para os componentes da página, elas não serão renderizadas estaticamente lá.

A configuração do markdown i18n em gatsby-node parece ser semelhante ao que as pessoas estão fazendo aqui, mas estou curioso para saber se você está obtendo renderização estática com o javascript desabilitado nos componentes da sua página?

Sim, eu recebo renderização estática, esse era o meu objetivo de qualquer maneira, i18n-react faz o truque!

@TomPichaud, seria possível compartilhar como o i18n -react que você mencionou é melhor que o

Uma coisa que não estou entendendo é a necessidade real de pacotes externos para carregar as mensagens traduzidas (além de pluralização e parentes, provavelmente).

Para um site simples com conteúdo estático, estou apenas duplicando as páginas para cada idioma onCreatePage e passando a localidade para 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()
  })
}

Então, na página real, eu uso a localidade no contexto para consultar o conteúdo usando o filtro do graphql.

Digamos que eu tenha o conteúdo inicial em /data/home/en.js e /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 bem com netlifyCMS (embora um pouco detalhado até que eles suportem i18n) e imagens nos arquivos JSON (embora precisemos criar um novo NodeField com o caminho relativo para que o sistema de arquivos de Gatsby o obtenha)

Isso não é suficiente para a maioria dos casos?

Ainda estou testando e não usei nada disso em produção, mas estou pensando em usar a API de contexto do react para a localidade para resolver alguns problemas pendentes, como links localizados

@pbrandone Esta parece ser uma ótima abordagem para mim. Acho que algo semelhante deveria ser documentado oficialmente.

Obrigado por todas as contribuições, a quantidade de ideias discutidas aqui marca claramente a demanda por suporte i18n bem documentado.

@pbrandone Isso significa que você precisa especificar explicitamente todas as chaves usadas por qualquer componente filho em IndexPage nesta consulta e passar traduções para todos os componentes por meio de props?

Além disso, eu uso regras de pluralização e datas relativas, então eu tenho que carregar dados específicos de localidade adicionais de qualquer maneira :/

No entanto, concordo que seria ótimo ter uma documentação oficial de como fazer i18n sem nenhuma biblioteca para casos simples e depois como fazê-lo com as bibliotecas mais populares.

@szimek bem, neste caso sim.

É muito fácil adicionar react-intl (ou qualquer outra biblioteca i18n) tho:

// 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

e depois nas páginas:

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

Mas então você não seria capaz de usar vários arquivos JSON (por exemplo, por página), nem ter todos os dados do CMS nesses arquivos para consultar e transformar com o poder do graphql.
Com a abordagem graphql poderíamos, por exemplo, ter algumas chaves com caminhos para imagens nesses arquivos JSON para carregar imagens diferentes por localidade e ainda poder usar gatsby-image nelas.
E, em seguida, adicione o netlify CMS para editar esses arquivos JSON 😃

Não examinei corretamente o gatsby v2, mas aparentemente há um componente StaticQuery que nos permite consultar componentes filhos (alguém me corrija se estiver errado!)

Se for esse o caso, poderíamos criar um React Context para disponibilizar a localidade em qualquer lugar e depois consultar as chaves necessárias em cada componente com a filtragem de localidade

@pbrandone você está certo, ele é renderizado estaticamente dessa maneira. Lembro-me de testá-lo no passado e falhar, mas isso foi antes de eu ter uma noção decente de como o gatsby funciona, eu poderia ter meu react-intl configurado no navegador gatsby, que parece que não pode renderizar sem javascript. Minha solução agora se parece com a sua

@KyleAMathews Estou tentando atualizar nossa página para Gatsby para v2 e tenho um problema com nossa configuração react-intl e consultas graphql.

Expliquei anteriormente como uso layouts específicos de idioma para carregar dados de idioma no Gatsby v1 - https://github.com/szimek/gatsby-react-intl-example. No Gatsby v2, tive a ideia de substituir esses layouts por componentes de página específicos do idioma. Eu teria um componente src/templates/Post.js agnóstico de idioma e, em seguida, componentes específicos de idioma como src/templates/Post.en.js , src/templates/Post.de.js que apenas carregariam dados de idioma e renderizariam o componente agnóstico de idioma.

No seu comentário anterior (https://github.com/gatsbyjs/gatsby/issues/3853#issuecomment-367115380) você mostrou um exemplo onde cada componente da página tem uma consulta específica do idioma.

O problema é que ao chamar createPage , estou passando nomes desses componentes específicos da linguagem (por exemplo src/templates/Post.en.js ) como opção component , mas a consulta graphql está em o componente agnóstico de idioma, porque é __exatamente o mesmo para todos os idiomas__ (depende de locale , mas estou passando em context ). Gostaria de evitar repetir exatamente a mesma consulta em todos esses componentes específicos da linguagem.

Alguma ideia de como resolver? Posso extrair esta consulta para uma variável? Quando tentei, Gatsby reclama que os nomes de consulta e fragmento são os mesmos ...

Recentemente, adicionei um inicializador padrão do Gatsby com recursos de rotas de URL em vários idiomas e detecção de idioma do navegador. (demonstração)

gatsby-starter-default-intl

Recursos:

  • Localização (multilíngue) fornecida por react-intl .

  • Redirecionamento automático baseado no idioma preferido do usuário no navegador fornecido por browser-lang .

  • Suporta rotas de URL em vários idiomas em um único componente de página. Isso significa que você não precisa criar páginas separadas, como pages/en/index.js ou pages/ko/index.js .

  • Baseado em gatsby-starter-default com menos modificações.

@wiziple Obrigado! Parece realmente interessante. Eu não tinha ideia de que você pode fazer algo assim: https://github.com/wiziple/gatsby-starter-default-intl/blob/master/src/i18n/withIntl.js#L38 ;) Espero que ainda funcione no Webpack 4...

É possível carregar dados de localidade da mesma maneira aqui https://github.com/wiziple/gatsby-starter-default-intl/blob/master/src/i18n/withIntl.js#L6 ? Suportamos 6 (em breve 7 idiomas), então seria ótimo se eu pudesse carregar apenas aquele para o qual estou construindo a página. não é grande coisa se não for possível - felizmente, esses arquivos de dados de localidade são relativamente pequenos.

Eu também vou ter que ver como você gera essas páginas, porque no meu caso nem todas as páginas são traduzidas para todos os idiomas (não existe um único idioma "fonte"), então a solução com onCreatePage provavelmente não funcionará No meu caso.

Espero que isso resolva meu problema com a mesma consulta graphql em cada componente de página específico do idioma.

@szimek
O site que gerencio tem 14 idiomas e cada arquivo de idioma tem 12-15 KB. Tenho certeza de que precisamos fornecer o idioma certo para cada roteador de idioma no momento da compilação para gerar dados de SEO. Portanto, não tenho certeza de como posso lidar com isso sem fornecer todos os idiomas.

Eu entendo que às vezes é difícil fornecer todas as páginas traduzidas em todos os idiomas. Você pode resolver isso fornecendo alguma exceção em onCreatePage em gatsby-node.js . No meu caso, simplesmente resolvi não oferecer o idioma traduzido, independentemente do roteador de idioma. 😆 Você pode encontrar o site showcase sobre produção a partir do README.md inicial e verificar seu desempenho.

@wiziple Muito obrigado!

Eu usei o withIntl componente com dinâmica require truque para traduções (não tenho idéia se existem desvantagens para usá-lo) e parece um grande trabalho. Ele resolveu o problema com o qual eu estava lutando - como lidar com a mesma consulta graphql em vários componentes de página específicos de idioma - tendo um único componente de página para todos os idiomas.

@wiziple obrigado pelo compartilhamento de repo. Me colocou no caminho certo 😄 🎉

lingui parece ser uma alternativa melhor . Não acho que @dcroitoru tenha o devido reconhecimento por um grande exemplo. Só precisa de um pouco de amor para empurrá-lo para o Gatsby 2.0

Concordo que o Lingui é realmente muito bom, embora ainda precise de um iniciador completo, com a versão mais recente do Gatsby, mas também do Lingui. O iniciador mencionado não é oficial e estava faltando alguns recursos da última vez que verifiquei (por exemplo, usar um carregador para executar a compilação lingui em tempo real). O autor do Lingui @tricoder42 disse que forneceria documentação quando o Lingui v3 fosse lançado (o que parece ser em breve).

NB: Percebi que minha necessidade de uma biblioteca i18n diminuiu depois de integrar um CMS (DatoCMS), mas ainda preciso do Lingui para algumas strings que não encontram seu lugar no CMS e também para pluralização e possivelmente outras coisas mais tarde, então definitivamente quero mantê-lo na minha base de código.

De qualquer forma, no meu caso, a existência de gatsby-plugin-i18n tornou as coisas bastante confusas, pois não é mantida, tem um nome confuso e desvia a atenção dessas outras soluções realmente legais, como js-lingui e CMSes, que eu então peguei um enquanto para descobrir e montar juntos.

Eu fiz dois exemplos de internacionalização com integração react-intl

O primeiro exemplo está focado em agrupar apenas as traduções atuais em pedaços js (algo que não consegui encontrar em outros plugins que verifiquei).

O segundo exemplo se concentra no uso de consultas dinâmicas para fornecer apenas traduções solicitadas para determinada combinação de página/idioma.

Espero que esses exemplos sejam úteis para alguém.

Também fiz um post médio rápido (e esqueci de postar aqui) com praticamente o que está em https://github.com/gatsbyjs/gatsby/issues/3853#issuecomment -395432693 (embora um pouco mais aprofundado).

https://blog.significa.pt/i18n-with-gatsby-528607b4da81 para quem estiver interessado

As edições antigas serão encerradas após 30 dias de inatividade. Este problema está parado há 20 dias e está sendo marcado como obsoleto. Responda aqui ou adicione o rótulo "not stale" para manter este problema em aberto!

Fala galera, já faz quase um ano 😅

Recentemente, lancei o novo plugin gatsby gatsby-plugin-intl que facilmente transforma seu site gatsby em uma estrutura de internacionalização pronta para uso.

DEMO: https://gatsby-starter-default-intl.netlify.com

  • Estrutura de internacionalização pronta para uso alimentado por react-intl

  • Suporta redirecionamento automático com base no idioma preferido do usuário no navegador

  • Suporta rotas de URL em vários idiomas em um único componente de página. Isso significa que você não precisa criar páginas separadas, como pages/en/index.js ou pages/ko/index.js .

  • Como alguns de vocês sugeriram acima, agora ele agrupa apenas o idioma atual durante o tempo de compilação.

Além disso, quero mencionar que muitos dos exemplos/iniciadores do i18n são realmente renderizados no lado do cliente. A melhor maneira de verificar se o aplicativo é renderizado como SSR é visualizar o código-fonte e verificar se os textos localizados existem. Por favor, verifique este assunto quando você internacionalizar seu site gatsby para SEO.

Fala galera, já faz quase um ano 😅

Recentemente, lancei o novo plugin gatsby gatsby-plugin-intl que facilmente transforma seu site gatsby em uma estrutura de internacionalização pronta para uso.

DEMO: https://gatsby-starter-default-intl.netlify.com

  • Estrutura de internacionalização pronta para uso alimentado por react-intl
  • Suporta redirecionamento automático com base no idioma preferido do usuário no navegador
  • Suporta rotas de URL em vários idiomas em um único componente de página. Isso significa que você não precisa criar páginas separadas, como pages/en/index.js ou pages/ko/index.js .
  • Como alguns de vocês sugeriram acima, agora ele agrupa apenas o idioma atual durante o tempo de compilação.

Além disso, quero mencionar que muitos dos exemplos/iniciadores do i18n são realmente renderizados no lado do cliente. A melhor maneira de verificar se o aplicativo é renderizado como SSR é visualizar o código-fonte e verificar se os textos localizados existem. Por favor, verifique este assunto quando você internacionalizar seu site gatsby para SEO.

Ei @wiziple muito obrigado por isso, eu estava ficando louco para encontrar uma solução para localizar Gatsby.
Talvez eu não tenha entendido, mas você tem TODAS as strings de um idioma em apenas um arquivo?
Seria possível dividir o JSON de cada linguagem em mais arquivos, talvez usando a mesma estrutura de componentes?

@cant89 Entendo seu ponto, mas atualmente não é possível sem alterar o código do plug-in. Ou você pode fazer um script que analise o diretório src e pegue todos os arquivos de idioma do componente. Mesclar em um JSON e depois conectar em gatsby develop ou gatsby build .
Depois de obter todos os arquivos JSON e mesclar como um objeto aninhado, você também pode convertê-lo como um objeto nivelado.
https://github.com/yahoo/react-intl/wiki/Upgrade-Guide#flatten -messages-object

Temos um bom exemplo de configuração para i18n. https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n. Nós realmente não temos uma opinião sobre os frameworks i18n. Basta escolher um ao seu gosto.

Temos um bom exemplo de configuração para i18n. https://github.com/gatsbyjs/gatsby/tree/master/examples/using-i18n. Nós realmente não temos uma opinião sobre os frameworks i18n. Basta escolher um ao seu gosto.

Legal, obrigado, vou tentar!
De qualquer forma, o link no readme está quebrado, provavelmente você quer dizer https://using-i18n.netlify.com/ ?

@wardpeet Seu exemplo gera strings traduzidas estáticas (no tempo de compilação)? Ou ele gera o texto durante o tempo de execução?

@monsieurnebo parece tempo de construção

@cant89 ainda estamos atualizando o dns, então está em breve por enquanto usando-i18n.netlify.com/ é o link correto.

@monsieurnebo está em tempo de construção. Ele cria uma cópia do seu site para cada idioma para que tudo possa ser construído estaticamente. Isso significa que seu site permanece rápido, pois tudo é apenas um .html.

não sei onde mais perguntar isso, mas um pouco relevante. algum desses plugins suporta pathPrefix do gatsby?

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 interessante, acho que o primeiro geralmente faz mais sentido, pois o pathPrefix está fazendo com que seu domínio seja domain.com/prefix portanto, altere a raiz quando você instalou o Gatsby em um subdiretório, se você não o instalar para um subdiretório, você não precisa dele, se você usar um subdiretório, alterando o prefixo para depois que o idioma o quebrar.

Agora surge a pergunta, por que você está usando o pathPrefix em primeiro lugar?

Ref: documentos pathPrefix

Oi,

A maioria das discussões aqui são sobre como i18n um site gatsby. Mas há uma diferença entre ter um POC funcionando e ter um sistema otimizado pronto para produção.

Se você estiver interessado em ler mais sobre divisão de código e arquivos i18n e por que a maioria das soluções neste tópico não é otimizada, você achará este problema útil

Esta página foi útil?
0 / 5 - 0 avaliações

Questões relacionadas

signalwerk picture signalwerk  ·  3Comentários

brandonmp picture brandonmp  ·  3Comentários

totsteps picture totsteps  ·  3Comentários

kalinchernev picture kalinchernev  ·  3Comentários

andykais picture andykais  ·  3Comentários