DEV Community

Discussion on: Multilingual website with Gatsby and Contentful - Part 2

Collapse
 
naxes profile image
Seán Bickmore

I originally left a comment here regarding translated slugs, but removed it in lieu of finding somewhat of a working solution. I thought I'd follow up on this in case anyone is stuck or possibly has a better one.

The Problem

In this context, the slug is assumed to be the same for each locale. For example:

/page/
/fr/page/
/de/page/
... and so on
Enter fullscreen mode Exit fullscreen mode

But, what if you want the slugs themselves to be translations of each other?

/english-page/
/fr/french-page/
/de/german-page/
Enter fullscreen mode Exit fullscreen mode

You can set this field up for localization in Contentful, but the issue I found with this is that it will generate each page in each language under every locale:

english-page/
fr/
  english-page/
  french-page/
  german-page/
de/
  english-page/
  french-page/
  german-page/
en/
  english-page/
  french-page/
  german-page/
Enter fullscreen mode Exit fullscreen mode

Meaning, if I were to visit /fr/english-page/ I'd be seeing English content, which is not ideal. Naturally, if they share the same slug this wont happen, but what we actually want here is:

english-page/
fr/
  french-page/
de/
  german-page/
en/
  english-page/
Enter fullscreen mode Exit fullscreen mode

The Solution

The basic gist of the solution to this for me was to be able to determine that the page being created was of a specific language. First thing was to pass in the node_locale of the page to the context:

// Don't forget to add 'node_locale' to your query first
result.data.allContentfulPost.edges.forEach(edge => {
  const { path, node_locale } = edge.node;

  createPage({
    path: path,
    component: blogPostTemplate,
    context: {
      slug: path,
      node_locale,
    },
  });
});

Enter fullscreen mode Exit fullscreen mode

Now in onCreatePage we know the locale. In addition to this, page.context.intl can provide us the language, meaning if we compare these two as follows we will be able to determine if the locale and language of the page match:

exports.onCreatePage = ({ page, actions }) => {
  const { createPage, deletePage } = actions;
  const { node_locale } = page.context;
  const { language } = page.context.intl;
  deletePage(page)


  /**
   * Now it will only create pages under
   * each locale of the correct language
   * as opposed to ALL languages for each.
   */
  if (node_locale === language) {
    createPage({
      ...page,
      context: {
        ...page.context,
        locale: page.context.intl.language,
      },
    });
  }
};
Enter fullscreen mode Exit fullscreen mode

This will filter out the pages for each locale, but there's still an issue with this. It also filters out /, /dev-404-page/ etc. Quick and dirty solution for this problem:

exports.onCreatePage = ({ page, actions }) => {
  const { createPage, deletePage } = actions;
  const { node_locale } = page.context;
  const { language, originalPath } = page.context.intl;
  deletePage(page)

  const pathExceptions = ['/', '/dev-404-page/'];
  const localeCheck = node_locale === language;
  const pathCheck = pathExceptions.indexOf(originalPath) !== -1;

  if (localeCheck || pathCheck) {
    createPage({
      ...page,
      context: {
        ...page.context,
        locale: page.context.intl.language,
      },
    });
  }
};

Enter fullscreen mode Exit fullscreen mode

Conclusion

This is what worked for me given a time constraint as it was a requirement to have translated slugs for the project, though the implementation is imperfect since in the case the slug is the same, it wont create a page for that locale. Good example of this would be if you had multiple English locales, meaning the slug actually wouldn't be different. Wondering if there's a better way to handle this?

Collapse
 
petriczechcom profile image
Petriczech

Thank you for this man. I was struggling a lot with this. I upgraded it a bit since I'm using the category pages too.

exports.onCreatePage = ({ page, actions }) => {
  const { createPage, deletePage } = actions;
  const { locale } = page.context;
  const { language } = page.context.intl;


  if(page.context.type === 'blog' || page.context.type === 'category') {
    deletePage(page)

    /**
     * Now it will only create pages under
     * each locale of the correct language
     * as opposed to ALL languages for each.
     */
    if (locale === language) {
      createPage({
        ...page,
        context: {
          ...page.context,
          locale: page.context.intl.language,
        },
      });
    }
  }
};

I had to add locale and type to post and category context

createPage({
        path: post.node.fields.slug,
        component: blogPostTemplate,
        context: {
          slug: post.node.fields.slug,
          previous,
          next,
          locale: post.node.frontmatter.locale,
          type: "blog",
        },
      })

and category respectively

 createPage({
        path: `/blog/${category.fieldValue}/`,
        component: categoriesTemplate,
        context: {
          category: category.fieldValue,
          locale: category.edges[0].node.frontmatter.locale,
          type: "category",
        },
      })
Collapse
 
strehlst profile image
Steven • Edited

Hi Seán, thanks a lot for your workaround!

Did you come across a solution for the case slug are actualle equal on purpose or because even translated they give the same (e.g. France is France in English and in French, Portugal as well).

That would be awesome!