How to create one page site with multi languages routes?

1k Views Asked by At

I'm using Gatsby and I would like to create a one site using multilanguage, so far I've defined pages/index.js which contains this:

import React from "react"
import Layout from "../components/layout/layout"

import BGTState from "../context/bgt/bgtState"
import { Router } from "@reach/router"
import Home from "../components/pages/home"
import Collection from "../components/pages/collection"
import NotFound from "../components/pages/404"

const IndexPage = () => {
  return (
    <BGTState>
      <Layout>
        <Router>
          <Home path="/" />
          <Collection path="collection/:id" />
          <NotFound default />
        </Router>
      </Layout>
    </BGTState>
  )
}

export default IndexPage

and I have modified gatsby-node.js as:

// Implement the Gatsby API onCreatePage. This is
// called after every page is created.
exports.onCreatePage = async ({ page, actions }) => {
  const { createPage } = actions

  if (page.path === "/") {
    page.matchPath = "/*"
    createPage(page)
  }
}

each request is forwarded on index.js, but there is a problem. I'm using the plugin gatsby-plugin-intl that add to the url a dynamic prefix like: http://localhost:3001/en/

If I visit http://localhost:3001/en/, then I get the NotFound component displayed because no Route match the url. Is there a way to prefix the url and reroute everything to the correct component?

1

There are 1 best solutions below

7
On BEST ANSWER

Why you are using client-only routes/wrapping everything inside the <Router>?

I don't know what's the goal in your scenario to change the gatsby-node.js with:

// Implement the Gatsby API onCreatePage. This is
// called after every page is created.
exports.onCreatePage = async ({ page, actions }) => {
  const { createPage } = actions

  if (page.path === "/") {
    page.matchPath = "/*"
    createPage(page)
  }
}

If you are not using client-only routes, you can remove them.

It's a broad question but, just define your languages and translation files. In your gatsby-config.js:

plugins: [
  {
    resolve: `gatsby-plugin-intl`,
    options: {
      // language JSON resource path
      path: `${__dirname}/src/intl`,
      // supported language
      languages: [`en`,`es`],
      // language file path
      defaultLanguage: `en`,
      // option to redirect to `/en` when connecting `/`
      redirect: true,
    },
  },
]

The useIntl hook will capture the internal requests so, you just need to worry about the views, forgetting the routing:

import React from "react"
import { useIntl, Link, FormattedMessage } from "gatsby-plugin-intl"

const IndexPage = () => {
  const intl = useIntl()
  return (
    <Layout>
      <SEO title={intl.formatMessage({ id: "title" })}/>
      <Link to="/page-2/">
       <FormattedMessage id="go_page2" />
      </Link>
    </Layout>
  )
}
export default IndexPage

Your Collection component should be a page, wrapped inside /page folder, or a custom collection with a specific id. If that page is dynamically created, you should manage the customizations in your gatsby-node.js, and, in that case, it should be a template of collections in that scenario.

To link between pages, I would recommend using page-queries to get the needed data to create your components. Your page should look like:

const IndexPage = () => {
  return (
    <BGTState>
      <Layout>
        <Link to="/"> // home path
        <Link to="collection/1">
      </Layout>
    </BGTState>
  )
}

export default IndexPage

The 404-page will automatically be handled by Gatsby, redirecting all wrong requests (in development will show a list of pages). Your other routing should be managed using the built-in <Link> component (extended from @reach/router from React).

To make dynamic the <Link to="collection/1"> link, you should make a page query, as I said, to get the proper link to build a custom dynamic <Link> from your data.

Once you installed the gatsby-plugin-intl plugin, all your pages will be prefixed automatically, however, to point to them using <Link> or navigate you need to get the current language and prefix it:

export const YourComponent = props => {
  const { locale } = useIntl(); // here you are getting the current language

  return <Link to={`${locale}/your/path`}>Your Link</Link>;
};

Because useIntl() is a custom hook provided by the plugin, the value of locale will be automatically set as you change the language.