Gatsby/GraphQL newbie building a bilingual site. My site (navbar, footer, body content etc) has all bilingual content in en
and zh
json files respectively, and calls the t
function from react-18next
. However, there are several pages in my website that have not been 'internationalised' yet - my markdown pages. As of current, the file structure looks like this:
src
├── posts
│ └── positions
│ ├── en
│ │ ├── accounting-en.md
│ │ ├── socialmedia-en.md
│ │ └── swe-en.md
│ └── zh
│ ├── accounting-zh.md
│ ├── socialmedia-zh.md
│ └── swe-zh.md
where the .md
files are rendered onto the page with MDXRenderer
, and i'd like only the contents in either the en
/zh
folders to load when either language is selected. I'd like to have these markdown files editable on Netlify CMS, so this solution and react-markdown
aren't things i'm considering.
so i was wondering: how can data from i18n.language
be passed into a graphql query? is this the best way to internationalise markdown pages?
edits:
gatsby-node.js
const { createFilePath } = require(`gatsby-source-filesystem`);
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
if (node.internal.type === `Mdx`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value: `/posts${value}`,
language,
})
}
}
const path = require("path")
exports.createPages = async ({ graphql, actions, reporter }) => { // Create blog pages dynamically
const { createPage } = actions
const result = await graphql(`
query {
allMdx {
edges {
node {
id
fields {
slug
language
}
}
}
}
}
`)
if (result.errors) {
reporter.panicOnBuild('🚨 ERROR: Loading "createPages" query')
}
// Create blog post pages.
const posts = result.data.allMdx.edges
posts.forEach(({ node }, index) => {
createPage({
path: node.fields.slug,
component: path.resolve(`./src/components/post-page-template.js`),
context: { id: node.id },
})
})
}
posts.js (where all markdown files are rendered, but i'd like only markdown files ending in either -en or -zh to show up depending on the language selected. i'm open to changing this file structure in favour of nicer code)
import React from "react";
import styled from "styled-components";
import { graphql, Link } from "gatsby";
import Navbar from "../components/Navbar.js";
import FooterContainer from "../containers/footer";
import { useTranslation } from "react-i18next";
import i18next from "i18next";
const Button = styled.button`
background-color: #ec1b2f;
border: none;
color: white;
padding: 10px 40px;
text-align: center;
text-decoration: none;
font-size: 16px;
border-radius: 10px;
`;
const Body = styled.body`
margin-left: 6%;
`;
const Divider = styled.hr`
margin-top: 20px;
line-height: 0;
border-top: 1px;
margin-left: 0px;
&:last-child {
margin-bottom: 30px;
}
`;
const Title = styled.h1`
font-family: "Arial";
color: #ec1b2f;
font-size: 25px;
font-weight: 400;
`;
// console.log('----')
// console.log(typeof i18next.language)// type string
// console.log(i18next.language) // output: i18next: languageChanged zh-Hant
const selectedLanguage = i18next.language // this doesn't work despite i18next.language being type string??
const selectedLangRegex = selectedLanguage.split(/languageChanged (.*)/gm)
export const query = (selectedLangRegex) => graphql`
query SITE_INDEX_QUERY {
site {
siteMetadata {
title
description
language
}
}
allMdx(
sort: {fields: [frontmatter___date], order: DESC},
filter: {frontmatter: {published: {eq: true}, language: {eq: selectedLangRegex}}} //trying to regex selectedLangRegex
){
nodes {
id
excerpt(pruneLength: 250)
frontmatter {
title
date(formatString: "DD MMM YYYY")
}
fields {
slug
}
}
}
}
`;
const Careers = ({ props, data }) => {
const { t } = useTranslation();
return (
<div>
<Navbar />
{data.allMdx.nodes.map(({ excerpt, frontmatter, fields }) => (
<Body>
<Title>{frontmatter.title}</Title>
<p>{frontmatter.date}</p>
<p>{excerpt}</p>
<Link to={fields.slug}>
<Button>{t("careers.apply_button")}</Button>
</Link>
<Divider />
</Body>
))}
<FooterContainer />
</div>
);
};
export default Careers;
so i was wondering: how can data from
i18n.language
be passed into a graphql query? is this the best way to internationalise markdown pages?
Yes, of course. To me, passing a GraphQL variable to the template it's the cleanest and best way. In that way, you can add a fallback language (or leave it empty) to pick the non-internationalized file if it's not translated yet.
Something like:
const path = require("path")
exports.createPages = async ({ graphql, actions }) => {
const { createPage } = actions
const queryResults = await graphql(`
query getAllPosts {
allMarkdownRemark {
nodes {
slug
language #use relative/absolute path if needed
}
}
}
`)
const postTemplate = path.resolve(`src/templates/posts.js`)
queryResults.data.allProducts.nodes.forEach(node => {
createPage({
path: `/blog/${node.slug}`,
component: postTemplate,
context: {
slug: node.slug,
language: node.language || 'en' // the fallback
},
})
})
}
Note: without knowing how your gatsby-node.js
looks like you may need to tweak it a little bit the query as well as the createPage
API.
The idea is to query for a language
field, which I'm assuming is a field of each post. If it's not, you can query for the file path (relative or absolute), which will contain the en
or zh
variable in its name. Adding the fallback language will force you to change all file names to send a valid variable.
Then, in your template (posts.js
) you can use the language
field as you are using the slug
to filter each entity.