Reputation: 445
I have this Gatsby project where I'm using gatsby-source-filesystem to fetch content from YAML files within the whole site and it is not a big one (will have up to four pages with no blog).
The choice for YAML was made based on the fact that the client wouldn't like to have a CMS behind it but the files for edit instead. And since there's no way to tell if the people editing its contents will have some background with programming or such, we all agreed that YAML format would be "easier" and "simpler" to explain to the team to take over after it's done. There's also a need to support languages starting off with Portuguese - that shall be default - and English. Later they will decide if there's need to add other languages.
I've red some tutorials and browsed through Gatsby plugin directory for i18n and intl support being react-i18next and react-intl the majority of the dependencies behind these plugins over there. There's actually a very specific one called gatsby-plugin-yaml-i18n that sounds like it would be the right thing for this project, but it's poorly documented and taking a look within it's files on github I couldn't really understand what's behind it, meaning it's hard to implement.
The problem with the i18n approach is that we don't really know what will be the default text yet, meaning that the homepage content may change in the future, so translating its contents might be a problem.
We understand that what is needed is to point out in our code where each instance would be so Gatsby would reach for every language option on build, if that makes any sense.
With all that said what I believe to be the best approach is to have separate files for locales and fetch their contents based on the language the user switches to or not - in case of defaults Example:
./src
|- content
| |- homepage.pt-br.yaml
| |- homepage.en-us.yaml
|- template
| |- homepage.js
Both files would have similar structure so the editor could read both and correlate the texts in them. Something like:
# homepage.pt-br.yaml
titulo: "titulo principal"
texto: "texto corrido"
...
# homepage.en-us.yaml
titulo: "main header"
texto: "text fill"
Although I'm not sure it is possible to fetch this from a simple configuration as mine, using just gatsby-source-filesystem. This idea came from a previous experience where we used Contentful CMS that had locale separately fetch for each node and array results in the graphql query. I can't remember how it was
done but it was something like <h1>{['pt-BR'].title</h1>
or something. Then I'd have to configure gatsby-node.js to create contexts based on the languages we have, set the default, build pages with the proper slugs etc. so each page would call the proper lang and the user would be able to select it from a switch.
I'm not sure where to start. I've used createResolvers API in the past to deal with nodes that the very same Contenful wasn't able to deal with but I'm not sure if I could use that to build nodes for each language.
Is there a proper way to do it? Any documentation I'd be reading for this purpose? I'm sorry if this question seems too 'inappropriate'. I'm struggling to learn React and Gatsby in a deeper way.
Any comment, suggestion and question would be greatly appreciated.
Upvotes: 1
Views: 764
Reputation: 1
// this code can be used to create your Gatsby JS site multilingual as well as you can pass id or data which you require to send as a parameter. to make this code works create a page name colors in your pages directory and this is now you can access your single page with multiple urls.
// http://localhost:8001/en-us/color/984 => for en-us language
// http://localhost:8000/en-ca/colour/987 => for en-ca language
// http://localhost:8000/fr-ca/couleur/987 => for fr-ca language
const path = require(`path`);
// language locales
const locales = ["en-us", "en-ca", "fr-ca"];
// names of pages in different locales
const colorPageNames = ["color", "colour", "couleur"];
// this method will automatically execute on every page creation
exports.onCreatePage = ({ page, actions }) => {
// destruct createPage and deletePage methods from actions
const { createPage, deletePage } = actions;
// Grab the keys of locales and map over them
colorPageNames.map((pagename, index) => {
// condition to match page path
if (page.path.match(/^\/color/)) {
// first delete the incoming page that was automatically created by Gatsby. if path is matched
deletePage(page);
// use the values defined in "locales" to construct the path
const localizedPath = `${locales[index]}/${pagename}`;
// match the url format of page
page.matchPath = `/${locales[index]}/${pagename}/:id`;
// call a function createPage to construcut new pages with multiple routes
return createPage({
// pass on everything from the original page
...page,
// path use to create path for the new page
path: localizedPath,
// pass in the locale and pagename as context to every page
context: {
locale: `${locales[index]}`,
pagename: `${pagename}`,
},
});
}
});
};
Upvotes: 0
Reputation: 11577
I used a similar setup for a previous i18n Gatsby project, where we also used file name to separate different languages (i.e main.en.md
, main.ko.md
).
For example, querying for main.en.md
will show something like { _meta: { lang: 'en' }, title: 'hello', ...}
For your case, you can do this in createSchemaCustomization
or onSourceNode
, get the yaml's parent File node, then check if it contains the language string. We did this before createSchemaCustomization
was available, so we used createNodeField
in onSourceNode.
createPage
programmatically on pages and templatesWe have a list of known languages
['en', 'ko', 'ja'].forEach(lang => {
createPage({
// result in url such as 'site.com/ko/main'
path: path.join('/', lang, slug),
component: componentPath,
context: { lang, otherContext }
})
})
For pages in src/pages
folder, we use onCreatePages
to add language to context & url:
exports.onCreatePages = ({ page, actions }) => {
const { createPage, deletePage } = actions
if (!page.componentPath.includes('src/pages')) {
return
}
languages.forEach(lang => {
createPage({
...page,
path: path.join('/', lang, page.path),
context: { lang }
})
})
deletePage(page)
}
query SomePage($lang: String!) {
myPage( _meta: { lang: { eq: $lang } } ) {
/* stuff */
}
}
This is the messiest part, where we create a React context for language, wrap the root with it, then write a custom Link component that wraps around GatsbyLink
to make sure all links in the site points to the same language part.
Another thing is storing user language selection, then redirect on page load if they are on the 'wrong' language section of the site.
Our solution was also a little more complicated in practice, because we have pages with i18n data both in separate files (main.en.md
) and page with that information in the same file.
We also want to have a nice default url (www.site.com/about
), so our createPage
logic also take account for that.
It was definitely messy — we did this in early Gatsby 2.0, when there were no good i18n solution, otherwise I would definitely look for alternative first.
Upvotes: 3