Posting both in French and English

When addressing a larger audience, we rely more and more on the availability of Google Translate, which is never going to be perfect, in particular when you appreciate tongue-in-cheek or puns. My first experience with automatic translation was spectacular, I fed the system the front page of my website, which was in English, and requested translation into French. I found it wasn't too bad, you could make out what I meant, but I was puzzled by the conclusion. It said "Le séjour est accordé !" (which reads like you were awarded some vacation) and, as I didn't remember how I concluded my page, I had to go back to the original version to discover it said "Stay tuned !". The system didn't know the expression and did its best and considered Stay as the noun and tuned as for an instrument, which turns out to also read as something else entirely and makes it funny.

Every time I publish something, whether here or on social media, I'm having to decide whether to write it in French or in English. Anything technical I write in English, mainly because I think about these things in English but also because I expect interested French speakers to routinely read about these topics in English. Anything about restaurants, food or places, I post in French because I expect the posts to be of interest mainly to French people. All this reasoning is necessary because I have to make a choice, as there usually isn't a possibility to provide several versions. And often, I would like to provide my own translation...

So, I was very happy when Next.js 10 introduced Internationalized Routing. It may not seem like much, routing is only a small part of it, but it does what matters, it gets you started: it moves the problem from 'translating the whole site' to 'translating individual pages'. This was easy to integrate into the structure I presented last time,

The idea is that when I want to provide a translation for a post, I place it in a separate markdown file in i18n/fr-FR/{postPath}.md. This makes the process as easy as it gets.

To make it work, the first thing was to modify my webpack plugin to go check if there are any translation files. Two things get generated, a list of imports loading versions into variables and list of switch cases to map these variables to the locale names. The purpose of doing it this way is so that Next.js knows that the translation files are part of the dependency tree.

const versions = i18nConfig.locales.reduce(
  (cur,locale)=>{
    const translation = path.relative(
      basePath,
      path.join(basePath,'i18n',locale, relPath)
    )
    if (fs.existsSync(translation))
      cur[locale] = '~/'+translation

    return cur
  },
  {}
)

const imports = (
  Object.keys(versions)
  .map(
    locale=>`import ${localeVariableName(locale)}
             from '${versions[locale]}'` )
  .join('\n')
)

const versionSwitch = (
  Object.keys(versions)
  .map(
    locale => `case '${locale}':
                return ${localeVariableName(locale)}`)
  .join('\n')
)

Once I have computed these values, I can generate the javascript output (only relevant parts are shown here). The interesting bit is that Next.js will generate static versions for each locale and they will contain only the relevant data. Also to note, the translated version only need to contain the parts of the front matter that need translating (which is usually just the title)

<<<imports>>>

function getSource(locale){
  switch (locale) {
    <<<versionSwitch>>>
  }
  return null
}

export async function getStaticProps(context) {
  const defaultVersion = <<<escapedSource>>>

  const findVersion = locale=>{
    const src = getSource(locale)
    if (src) return mergeFrontmatter(locale,src,defaultVersion)
    return defaultVersion
  }

  return {
    props: {
      content: findVersion(context.locale),
      threadPosts: getPostThread(
        'posts',
        '${md.data.thread}',
        ['slug', 'title', 'date']
      )
    },
  }
}

All in all, this was fairly simple to implement and is totally trivial to use. I like it !

As previously, don't hesitate to check out the code on gitHub !

Jérôme Muffat-Méridol
February 1, 2021

Behind the scenes

  1. open-sourcing myself
  2. using Markdown as Next.js pages
  3. Posting both in French and English