Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

createPages according to the translation set in the CMS #47

Closed
visualcookie opened this issue Jul 10, 2019 · 8 comments
Closed

createPages according to the translation set in the CMS #47

visualcookie opened this issue Jul 10, 2019 · 8 comments

Comments

@visualcookie
Copy link

Hey.

I have gatsby-plugin-intl along with gatsby-sourcre-directus7, which already comes with a translation interface for dynamic content.

Now the problem is, that when doing createPages for a single blog post, I would end up having the same URLs.

Example:

  1. Post 1 has a custom slug in the translations
  2. When starting up createPages will be executed
  3. URLs will be generated as followed: /blog/10-tips-to-manage-your-finances /blog/10-tipps-wie-sie-ihre-finanzen-verwalten
  4. Visit the page and go to /en/blog/10-tips-to-manage-your-finances
  5. Visit the page and go to /de/blog/10-tips-to-manage-your-finances
  6. Both end up having the same results and not the translated slug

My gatsby-node.js file looks as followed:

const path = require('path')

exports.createPages = async ({ actions, graphql }) => {
  const { createPage } = actions

  // GET Posts and create URL
  try {
    const result = await graphql(`
      {
        allDirectusArticle {
          edges {
            node {
              translations {
                article {
                  directusId
                }
                language
                title
                slug
              }
            }
          }
        }
      }
    `)

    result.data.allDirectusArticle.edges.map(({ node }) => {
      node.translations.map(a => {
        try {
          const url = `blog/${a.slug}`

          createPage({
            path: url,
            component: path.resolve('src/templates/blog/post.js'),
            context: {
              id: a.article.directusId,
            },
          })

          console.log(`Generated url '${a.title}' to path '/${url}'`)
        } catch (error) {
          console.error(`Failed to generate url: ${error}`)
        }
      })
    })
  } catch (error) {
    console.error(`GraphQL query returned error: ${error}`)
  }
}

The JSON returned from the above GraphQL request:

{
  "data": {
    "allDirectusArticle": {
      "edges": [
        {
          "node": {
            "translations": [
              {
                "title": "10 tips to manage your finances",
                "slug": "10-tips-to-manage-your-finances",
                "language": "en",
                "article": {
                  "directusId": 1
                }
              },
              {
                "title": "10 Tipps, wie Sie Ihre Finanzen verwalten können",
                "slug": "10-tipps-wie-sie-ihre-finanzen-verwalten-konnen",
                "language": "de",
                "article": {
                  "directusId": 1
                }
              }
            ]
          }
        },
        {
          "node": {
            "translations": [
              {
                "title": "Non modo carum sibi quemque, verum etiam vehementer carum esse?",
                "slug": "non-modo-carum-sibi-quemque-verum-etiam-vehementer-carum-esse",
                "language": "en",
                "article": {
                  "directusId": 2
                }
              }
            ]
          }
        }
      ]
    }
  }
}

I already did try to add ${a.language} to the url, but this resulted in having /en/en/ or /de/de/. Is there something I'm missing out here?

@visualcookie visualcookie changed the title createPages according to the translation on the CMS createPages according to the translation set in the CMS Jul 10, 2019
@angrypie
Copy link
Contributor

@visualcookie This plugin creates copy of each page with url prefixed by language code and each such page has corresponding react-intl context.

So if you creates two posts then you got two copies of each page:

  1. /en/blog/10-tips-to-manage-your-finances (maybe you want this one, with correct react-intl context and content)
  2. /de/blog/10-tips-to-manage-your-finances
  3. /en/blog/10-tipps-wie-sie-ihre-finanzen-verwalten
  4. /de/blog/10-tipps-wie-sie-ihre-finanzen-verwalten (and this one)

I hope, I didn't confuse anything 🧐

@visualcookie
Copy link
Author

Well, the thing is, the CMS I'm using already has a translation interface (see in JSON response above) and I don't want people to access a German article by just using the German slug with the locale set to en.

So basically /en/blog/10-tipps-wie-sie-ihre-finanzen-verwalten should not work (maybe redirect to /de/blog/10-tipps-wie-sie-ihre-finanzen-verwalten automatically).

@angrypie
Copy link
Contributor

angrypie commented Jul 11, 2019

@visualcookie You need to delete unwanted pages. For example your can do it in this way:

  1. Create global Map() to store blog post paths.
  2. Before page creation, add page path (prefixed by lang code) to map.
  3. In onCreatePage delete page if it belong to blog but path not exist in previously created map.

Hope this helps :)

@visualcookie
Copy link
Author

@visualcookie You need to delete unwanted pages. For example your can do it in this way:

1. Create global `Map()` to store blog post paths.
2. Before page creation, add page path (prefixed by lang code) to map.
3. In `onCreatePage` delete page if it belong to blog but path not exist in previously created map.

Hope this helps :)

Not sure if I can follow you right there. So basically what you are referring to, is to execute the GraphQL query outside of exports.createPages to Map() these paths and then in exports.createPages create these pages, whereas having also an exports.onCreatePage which filters for the correct paths?

But will /de/SLUG-FOR-GERMAN-POST and /en/SLUG-FOR-ENGLISH-POST work? And how will I filter for the language correctly?

@angrypie
Copy link
Contributor

angrypie commented Jul 12, 2019

@visualcookie Something like this but you may store in a Map only page paths, not a whole GraphQL query.
With your code above I would do it like this:

// 1. Global scope
const blogPages = new Map()
// ...

//2. Before your createPage call
blogPages.set(`/${a.language}/blog/${a.slug}`, {})
createPage({
  //...
  context: {
    type: 'blog',
  }
})
//..

//3. Filter unneccesary  pages.
// The point is that you want to keep only pages you store in a Map
// and delete pages that gatsby-plugin-intl creates for your in addition.
exports.onCreatePage = async ({ page, actions }) => {
  const isBlogPage = page.context.type === 'blog'
  const hasInvalidBlogPath = !blogPages.has(page.path)

  //If page is a blog page but has the wrong path
  if(isBlogPage && hasUnvalidBlogPath) {
    deletePage(page)
  }
}

@visualcookie
Copy link
Author

@angrypie Set this up now and it works. Amazing.

@cjost1988
Copy link

Hi,

I've tried this but I am facing one issue that the hook is injecting defaultLang to all pages.

e.g. I am creating /de/test1 and /en/test2 for the same content in different locales:
URLs to remove /de/de/test1, /en/de/test1, /de/en/test2 and /en/en/test2 which are created by the hook and injecting the proper locale context to them.

But my manually created routes to not have any locale information so that they default to the defaultLanguage.

I can't see any solution around this.
In my opinion it would be nice if the plugin would respect a pages context locale and add additional intl information in relation to that.
Sadly I am not a JS/NPM pro to enhance the plugin by myself.

Would be nice if you've any workaround or hint what I am missing or doing wrong :D

Cheers,
Chris

@wasurocks
Copy link

So I've been wondering about this and after reading through a bit of Gatsby's node APIs here's my solution (in case it would be of help to anyone):

There are two APIs I will be showing here that are going to make this possible - createPages and onCreatePage.

The first part is in the createPages API. Here, we have to inject a context to the page representing the language of the slug in question. For instance,

exports.createPages = async ({ graphql, actions }) => {
   // ...
   // Logic to create pages 
   createPage ({
     path: // your path
     component: // your template component
     context: {
        slug: // your slug
        langKey: 'en' // replace 'en' with whatever language your slug is supposed to represent  
     }
   })
   // ...
}

Now, as for the second part, it's comparable to the process of post-processing. We deal with the pages we created by checking if they are valid (based on their page) or not. If they aren't valid, we just delete them.

exports.onCreatePage = ({ page, actions }) => {
    const { deletePage } = actions;

    // for deleting programatically created pages
    // that have a langKey context not matching the path
    if (
        page.context.langKey &&
        // the function below, shouldPageBeDeleted, should be implemented on your own with whatever logic you want to use
        // for instance you may split the pagePath into parts and check if the first part is a language (locale) not matching your               
        // langKey - if that's the case you can return true
        shouldPageBeDeleted({
            pagePath: page.path,
            actualLanguage: page.context.langKey,
        })
    ) {
        console.log('deleting', page.path);
        deletePage(page);
    }
};

Good luck with your site.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

4 participants