nuxt / content

The file-based CMS for your Nuxt application, powered by Markdown and Vue components.
https://content.nuxt.com
MIT License
3.12k stars 623 forks source link

Nuxt content & i18n translation #642

Open daishizenSensei opened 3 years ago

daishizenSensei commented 3 years ago

Hello,

We build a Nuxt SSG app with Nuxt content. We use Nuxt i18n to translate our content. We follow this tutorial to achieve that: https://alvarosaburido.dev/blog/multi-language-blog-with-nuxt-content but it is not clean end solution 2 doesn't work. Apparently, we have to put a specific language switcher in content pages (different from our main switcher).

How can we achieve that? With a v-if with a specific switcher displayed only in content pages? We tried but doesn't work...

Thanks a lot, Kevin

StefanVDWeide commented 3 years ago

I'm facing the same issue, did you ever find a good solution?

szwacz commented 3 years ago

I'll share my solution for blog with i18n. It works with standard locale switcher, and also provides nice, locale-friendly urls for all blog posts.

For this to work you need to enable vuex store for Nuxt: https://nuxtjs.org/docs/2.x/directory-structure/store.

I have one folder with blog posts:

/content/blog/hello-world.md:

---
language: en
date: 2021-05-20
title: Hi
---

Hello world!

/content/blog/witaj-swiecie.md:

---
language: pl
date: 2021-05-20
title: Cześć
---

Witaj świecie!

In the folder all posts have the language attribute, and different language versions of same post are matched by publish date (you can of course match them by some unique ID to be more bulletproof).

And now rendering of those posts:

/pages/blog/index.vue:

<template>
  <div>
    <NuxtLink
      v-for="post in blogPosts"
      :to="localePath(post.path)"
      :key="post.path"
    >
      {{ post.title }}
    </NuxtLink>
  </div>
</template>

<script>

export default {
  async asyncData({ $content, i18n }) {
    const blogPosts = await $content("blog")
      .where({ language: i18n.locale })
      .fetch();
    return { blogPosts };
  }
};
</script>

/pages/blog/_slug.vue:

<template>
  <article>
      <h1>{{ article.title }}</h1>
      <nuxt-content :document="article" />
  </article>
</template>

<script>
const opposingLocale = currentLocale => {
  if (currentLocale === "pl") {
    return "en";
  }
  return "pl";
};

export default {
  async asyncData({ $content, params, store }) {
    const article = await $content("blog", params.slug).fetch();
    const articleOpposingLanguageList = await $content("blog")
      .where({
        date: {
          // hack, to get all articles with the same date
          $between: [
            new Date(article.date).valueOf(),
            new Date(article.date).valueOf()
          ]
        },
        language: opposingLocale(article.language)
      })
      .fetch();
    const articleOpposingLanguage = articleOpposingLanguageList[0];
    await store.dispatch("i18n/setRouteParams", {
      [article.language]: { slug: article.slug },
      [articleOpposingLanguage.language]: { slug: articleOpposingLanguage.slug }
    });
    return { article };
  }
};
</script>
cannap commented 3 years ago

hi @szwacz thanks for this idea but do you have an idea how to fallback we have much languages but not every page is ready so we want to fallback until others are ready

szwacz commented 3 years ago

@cannap didn't try this, but for my node articleOpposingLanguageList should return empty list if given article is not found, and then you can substiture the link to article with default language.

cannap commented 3 years ago

i made it like this @szwacz

export default {
  name: 'Privacy',

  async asyncData({ $content, i18n }) {
    let page = {}

    page = await $content(`legal`)
      .where({ language: i18n.locale, doc: 'privacy' })
      .fetch()

    if (page.length > 0) {
      return {
        page: page[0]
      }
    }
    // Fallback...
    page = await $content(`legal`)
      .where({ language: 'en', doc: 'privacy' })
      .fetch()

    return { page: page[0] }
  },
  data() {
    return {
      page: ''
    }
  }
}
</script>