Open rchrdnsh opened 5 years ago
I've found out a blog from Hiddentao that setup multilingual blog using this plugin with markdown.
It's ok with .md but I want to try with mdx files using gatsby-mdx. Taking the above blog and this doc as references, It seems doesn't work out with <MDXRenderer>
(haven't try it with <MDXProvider>
but that way is kinda hacky) and the process to setup this is cumbersome.
I ended up using using-i18n in the official example without any i18n library and works well with mdx file (in my case).
I still like the approach that react-intl
provides. Hope this plugin can further support creating pages with md/mdx.
oh nice, i'll check it out :-)
@rchrdnsh You can use react-intl with MDX in the same way as with JSX.
If you want to generate pages programmatically, and keep separate files for different languages, you should not rely on this plugin i think. It's different task.
@darekaze If I understand correctly all page translations are sent to the client. Not perfect solution for me. I personally did this by passing correct data into the context of the page that was already generated by gatsby-plugin-intl
. Also found out some bug (#43) on the way :)
If I understand you correctly, you need to transform parts of any data format, which go something like this:
into 2 pages. /en/blogPost and /es/blogPost, each using adequate subtree of data.. if you tell Gatsby to create pages for each language, this plugin creates another language mutations for each of them, so you would have to intercept and delete them in the gatsby-node. Something like onCreatePage ... if(page.url.contains('/en/de/')) deletePage(page)
I just thought that up, want to try it in few days, but might not work. I am still beginner at this :)
thinking about this more.. Let's say I have blog entry `
then en.json and de.json and some blog.js template (using this plugin with [en, de] as languages)... If blog.js has query depending on language variable data (filter: localization: { eq: $intl.language} }), would this plugin generate /en/blog/test and /de/blog/test correctly? Because we know the current language.. kind of brainstorming, but I hope something like this works. If I find some solution for this, I will let you know ;)
@sn3h Hey. Look at issue #47, it's already solved :)
@angrypie thank you, but will it work with changeLocale call?
@sn3h No, it will not work with translated slug.
Hi @darekaze,
I checked out the official 'using-i18n' example and mostly everything looks good, except for the language switcher at the top, which takes the user back to the homepage of the other language, rather than staying on the current page, which is a biiiiiiiiiiiiiiiiiiiiig UI/UX no-no. Trying to figure out how to make a switcher that stays on the same page using that example, but I don't really know how to go about it.
If you have any thoughts, I'm all ears.
@angrypie @sn3h
I got a simple solution for using the changeLocale call.
Just add intl context like the plugin when you create pages from your MDX files
Here are my codes(gatsby-node.js) based on #47
const { createFilePath } = require("gatsby-source-filesystem")
const path = require("path")
const languages = ["en", "ko"] // plugin options
function flattenMessages(nestedMessages, prefix = "") {
return Object.keys(nestedMessages).reduce((messages, key) => {
let value = nestedMessages[key]
let prefixedKey = prefix ? `${prefix}.${key}` : key
if (typeof value === "string") {
messages[prefixedKey] = value
} else {
Object.assign(messages, flattenMessages(value, prefixedKey))
}
return messages
}, {})
}
const basicPages = new Map()
// Programmatically create the pages for browsing blog posts (Create Page!)
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const getMessages = (path, language) => {
try {
// TODO load yaml here
const messages = require(`${path}/${language}.json`)
return flattenMessages(messages)
} catch (error) {
if (error.code === "MODULE_NOT_FOUND") {
process.env.NODE_ENV !== "test" &&
console.error(
`[gatsby-plugin-intl] couldn't find file "${path}/${language}.json"`
)
}
throw error
}
}
/**
* Basic Contents
*/
const resultsBasic = await graphql(`
query {
allMdx(
sort: { fields: [frontmatter___order], order: ASC }
filter: { frontmatter: { type: { eq: "basic" } } }
) {
edges {
node {
id
excerpt(pruneLength: 250)
frontmatter {
title
author
lang
}
fields {
slug
}
}
}
}
}
`)
// Handle errors
if (resultsBasic.errors) {
reporter.panicOnBuild(`🚨 Error while running GraphQL(resultsBasic) query.`)
return
}
console.log("resultsBasic", resultsBasic)
// you'll call `createPage` for each result
resultsBasic.data.allMdx.edges.forEach(({ node }, index) => {
let slug = node.fields.slug
basicPages.set(`${slug}`, {})
createPage({
path: slug,
// This component will wrap our MDX content
component: path.resolve(`./src/templates/blog-post-layout.js`),
context: {
id: node.id,
slug: slug,
prev: index - 1,
next: index + 1,
type: "basic",
// ADD INITL CONTEXT AT HERE
intl: {
language: node.frontmatter.lang,
languages,
messages: getMessages("./src/intl/", node.frontmatter.lang),
routed: true,
originalPath: slug.substr(3), // remove front /en or /ko strings
redirect: false,
},
},
})
})
}
// Create Slug!
exports.onCreateNode = async ({
node,
actions,
getNode,
store,
cache,
createNodeId,
}) => {
const { createNodeField, createNode } = actions
if (node.internal.type === "Mdx") {
const value = createFilePath({ node, getNode })
const newSlug =
"/" + node.frontmatter.lang + "/clone-apple-music/basic" + value
createNodeField({
// Individual MDX node
node,
// Name of the field you are adding
name: "slug",
// Generated value based on filepath with "blog" prefix
value: newSlug,
})
}
}
// remove duplicated init pages
exports.onCreatePage = async ({ page, actions }) => {
const { createPage, deletePage } = actions
// console.log('page.context.type', page.context.type)
const isBasicPage = page.context.type === "basic"
const hasInvalidBlogPath = !basicPages.has(page.path)
// If page is a blog page but has the wrong path
if (isBasicPage && hasInvalidBlogPath) {
deletePage(page)
}
}
hmmmm...how would you go about changing this example around to support regular mdx files, rather than content coming from a cms @ruucm? It's a bit hard for me to follow :-(
The multi language mdx content is in a folder structure in the project as follows:
src
- content
- - news /* folder of the category type, due to multiple folders of content types */
- - - hipster /* name of the article for the folder */
- - - - index.en.mdx /* or index-en.mdx or anything else that works */
- - - - index.vi.mdx /* or index-vi.mdx or anything else that works */
current node file with some of my guesses as to what should be different:
const { createFilePath } = require("gatsby-source-filesystem")
const path = require("path")
const languages = ["en", "vi"] // plugin options
function flattenMessages(nestedMessages, prefix = "") {
return Object.keys(nestedMessages).reduce((messages, key) => {
let value = nestedMessages[key]
let prefixedKey = prefix ? `${prefix}.${key}` : key
if (typeof value === "string") {
messages[prefixedKey] = value
} else {
Object.assign(messages, flattenMessages(value, prefixedKey))
}
return messages
}, {})
}
const basicPages = new Map()
// Programmatically create the pages for browsing blog posts (Create Page!)
exports.createPages = async ({ graphql, actions, reporter }) => {
const { createPage } = actions
const getMessages = (path, language) => {
try {
// TODO load yaml here
const messages = require(`${path}/${language}.json`)
return flattenMessages(messages)
} catch (error) {
if (error.code === "MODULE_NOT_FOUND") {
process.env.NODE_ENV !== "test" &&
console.error(
`[gatsby-plugin-intl] couldn't find file "${path}/${language}.json"`
)
}
throw error
}
}
/**
* News Content
*/
const news = await graphql(`
query {
allMdx(
sort: { order: DESC, fields: [frontmatter___date] }
filter: { frontmatter: { category: { eq: "news" } } }
)
{
edges {
node {
id
excerpt(pruneLength: 100)
frontmatter {
title
author
language
}
fields {
slug
}
}
}
}
}
`)
// Handle errors
if (news.errors) {
reporter.panicOnBuild(`🚨 Error while running GraphQL(news) query.`)
return
}
console.log("news", news)
// you'll call `createPage` for each result
news.data.allMdx.edges.forEach(({ node }, index) => {
let slug = node.fields.slug
basicPages.set(`${slug}`, {})
createPage({
path: slug,
// This component will wrap our MDX content
component: path.resolve(`./src/templates/article.js`),
context: {
id: node.id,
slug: slug,
prev: index - 1,
next: index + 1,
category: "news",
// ADD INITL CONTEXT AT HERE
intl: {
language: node.frontmatter.language,
languages,
messages: getMessages("./src/intl/", node.frontmatter.language),
routed: true,
originalPath: slug.substr(3), // remove front /en or /vi strings
redirect: false,
},
},
})
})
}
// Create Slug!
exports.onCreateNode = async ({
node,
actions,
getNode,
store,
cache,
createNodeId,
}) => {
const { createNodeField, createNode } = actions
if (node.internal.type === "Mdx") {
const value = createFilePath({ node, getNode })
const newSlug =
"/" + node.frontmatter.language + "/" + value
createNodeField({
// Individual MDX node
node,
// Name of the field you are adding
name: "slug",
// Generated value based on filepath with "news" prefix
value: newSlug,
})
}
}
// remove duplicated init pages
exports.onCreatePage = async ({ page, actions }) => {
const { createPage, deletePage } = actions
// console.log('page.context.type', page.context.type)
const isBasicPage = page.context.type === "basic"
const hasInvalidBlogPath = !basicPages.has(page.path)
// If page is a blog page but has the wrong path
if (isBasicPage && hasInvalidBlogPath) {
deletePage(page)
}
}
...and from this I'm getting double posts for each language in the blog posts page, and then I'm getting whacky urls like this for each post, which is not working, obviously:
http://localhost:8000/en/en/hipster/index.en/
...soooooooo, yeah...
...no idea what I'm doing...
...just shooting at bugs in the desert at night over here...
Thanks for the hot tip @ruucm! That did the trick for me 😄
Is there any real solution to avoid the duplicates??
http://localhost:8000/en/en/SLUG/index.en/
http://localhost:8000/es/es/SLUG/index.es/
Thanks :)
Got the plugin working on my site for the most part XD
The last issue I'm having is figuring out how to use the plugin for the blog/news section of the site, in which the posts are written in MDX and are multilingual as well.
Any thoughts on how to do this with the plugin?
Thank you @wiziple XD