I'm developing a website with Gatsby and I wanted to implement multi-language support.
So I used the gatsby-plugin-react-i18next plugin.
I followed all the steps, but it doesn't work, once I log into my website this error message shows:
error message
Right now, my code is the next one.
gatsby-config.js
module.exports = {
siteMetadata: {
title: "Space",
},
plugins: [
"gatsby-plugin-postcss",
{
resolve: `gatsby-source-filesystem`,
options: {
name: `locale`,
path: `${__dirname}/locales`
}
},
{
resolve: `gatsby-plugin-react-i18next`,
options: {
localeJsonSourceName: `locale`, // name given to `gatsby-source-filesystem` plugin.
languages: [`en`, `es`],
defaultLanguage: `en`,
// if you are using Helmet, you must include siteUrl, and make sure you add http:https
siteUrl: `https://example.com/`,
// you can pass any i18next options
// pass following options to allow message content as a key
},
pages: [
{
matchPath: '/:lang?/blog/:uid',
getLanguageFromPath: true,
excludeLanguages: ['es']
},
{
matchPath: '/preview',
languages: ['en']
}
]
}
],
};
index.js
import * as React from "react"
import { graphql } from "gatsby"
import { useTranslation } from "gatsby-plugin-react-i18next"
export default function IndexPage() {
const { t } = useTranslation();
return (
<h1>{t("Space")}</h1>
)
}
export const query = graphql`
query($language: String!) {
locales: allLocale(filter: {language: {eq: $language}}) {
edges {
node {
ns
data
language
}
}
}
}
`;
And of course I have the translation folders project structure
Im trying this plugin on a new blank project, not on my main project, so I don't understand why the plugin fails.
Any thoughts? Thanks in advice!
Edit: I add the translation.json of the two languages
English
Spanish
Your JSONs looks and the implementation too (couldn't be wrong being that simple). So to me, the issue relies on the configuration. Try something simpler such as:
{
resolve: `gatsby-plugin-react-i18next`,
options: {
localeJsonSourceName: `locale`,
path: `${__dirname}/locales`,
languages: [`en`, `es`],
defaultLanguage: `en`,
i18nextOptions: {
interpolation: {
escapeValue: false, // not needed for react as it escapes by default
},
keySeparator: false,
nsSeparator: false,
},
},
},
Related
I use LwcWebpackPlugin to load Lightning Web Components. Everything works fine but because I want to load compoennts from 'lwc' direcotry the namescape won't work. I must get components from /lwc directory and I bypysed this with AliasModuleRecord (described here) where I import all components by name. Problem is that only JS file is loading. Browser don't throw any exception but HTML body is not rendered. I know that JS file works because console log from connectedCallback is showed in browser console. What I'm doing wrong?. Maybe I haven't imported some library? Here is fragment form my webpack.config.js:
new LwcWebpackPlugin({
modules: [
{ dir: 'src/modules' },
{ npm: 'lightning-base-components' },
...glob.sync(
`./{${sfdxProjectJSON.packageDirectories?.map(package => package.path).join(',')}}/main/default/lwc/**/*.js`,
{ ignore: ['./**/__tests__/**'] }
).map(path => {
const name = path.split('/').at(-2);
return {
"name": `c/${name}`,
"path": path.replace('./', '')
};
}, {})
]
})
and this all resoults with:
[{
name: 'ui/helloWorldExampleOne',
path: 'pb-base/main/default/lwc/helloWorld/__docs__/examples/helloWorldExampleOne/helloWorldExampleOne.js'
},
{
name: 'ui/helloWorld',
path: 'pb-base/main/default/lwc/helloWorld/helloWorld.js'
},
{
name: 'ui/exampleOne',
path: 'pb-commerce/main/default/lwc/exampleOne/exampleOne.js'
}]
I have an application where I try to put the language management but I face a difficulty.
I'm on the latest version of #nuxtjs/i18n. When I change language, my URl changes, my labels change, everything is fine. When I refresh my page, however, the system reverts to the old language.
Here is my conf:
[
'#nuxtjs/i18n',
{
locales,
defaultLocale,
lazy: true,
langDir: 'locales/',
vueI18n: {
fallbackLocale: defaultLocale,
},
},
],
['~/.build/merge-and-compare-locales.js', { defaultLocale }],
export const locales = [
{
code: 'en',
file: 'en.json',
},
{
code: 'fr',
file: 'fr.json',
},
]
export const defaultLocale = 'fr'
What more do I need to do to make it keep the language before refresh?
I specify that I use this method to change the language:
changeLocale(code: string) {
this.$i18n.setLocale(code)
},
Locally it works fine. On the other hand on any other environment it does not work, the refresh makes return to the default language
i have same problem and it's come from ssr...
inside your store/index you should have a nsi (nuxt server init) function
something like this.
/** initial the store when user fetch the web app */
async nuxtServerInit(ssrContext: Context) {
if (this.$i18n.locale==='ar') {
ssrContext.redirect("/ar");
}
else if (this.$i18n.locale === 'fa')
{
ssrContext.redirect('/')
}
}
I am currently trying to resolve an absolute path in yaml file to relative so it can be query using graphql in gatsby. The absolute path are provided from netlify-cms.
When the same path are being placed in md file and uses gatsby-remark-relative-images to convert it to relative path, it has no problem at all, but the same does not apply to yaml.
The image file are placed in static/img/ and the path provided by cms is /img/xxx.jpg
src/data/pages/index.yaml
page: index
slider:
- image: /img/1_new.jpg
url: ""
- image: /img/2_new.jpg
url: ""
- image: /img/3_new.jpg
url: ""
gatsby-config.js
module.exports = {
// ...
plugins: [
// ...
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/src/data`,
name: 'data',
},
},
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/static/img`,
name: 'uploads',
},
},
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/src/pages`,
name: 'pages',
},
},
{
resolve: 'gatsby-source-filesystem',
options: {
path: `${__dirname}/src/assets/images`,
name: 'images',
},
},
{
resolve: 'gatsby-plugin-react-svg',
options: {
rule: {
include: /\.inline\.svg$/,
},
},
},
`gatsby-plugin-sharp`,
`gatsby-transformer-sharp`,
`gatsby-transformer-yaml-plus`,
{
resolve: 'gatsby-transformer-remark',
options: {
plugins: [
{
resolve: 'gatsby-remark-relative-images',
options: {
name: 'uploads',
},
},
{
resolve: 'gatsby-remark-images',
options: {
maxWidth: 2048, // must specify max width container
},
},
{
resolve: `gatsby-remark-responsive-iframe`,
options: {
wrapperStyle: `margin-bottom: 1.0725rem`,
},
},
{
resolve: 'gatsby-remark-copy-linked-files',
options: {
destinationDir: 'static',
},
},
`gatsby-remark-smartypants`,
`gatsby-remark-widows`,
],
},
},
{
resolve: 'gatsby-plugin-netlify-cms',
options: {
modulePath: `${__dirname}/src/cms/cms.js`,
},
},
'gatsby-plugin-netlify', // make sure to keep it last in the array
],
// for avoiding CORS while developing Netlify Functions locally
// read more: https://www.gatsbyjs.org/docs/api-proxy/#advanced-proxying
developMiddleware: app => {
app.use(
'/.netlify/functions/',
proxy({
target: 'http://localhost:9000',
pathRewrite: {
'/.netlify/functions/': ``,
},
})
)
},
}
Also, here is where it convert the absolute path in node into relative path
gatsby-node.js
exports.onCreateNode = ({ node, actions, getNode }) => {
const { createNodeField } = actions
fmImagesToRelative(node) // convert image paths for gatsby images
if (node.internal.type === `MarkdownRemark`) {
const value = createFilePath({ node, getNode })
createNodeField({
name: `slug`,
node,
value,
})
}
}
Finally, here is where it define the netlify-cms configuration
static/admin/config.yml
backend:
name: git-gateway
branch: master
media_folder: static/img
public_folder: /img
collections:
- label: "Data"
name: "data"
files:
- name: "index"
label: "Index"
file: "src/data/pages/index.yml"
fields:
- {label: "Page", name: "page", widget: hidden, default: "index"}
- label: "Slider"
name: "slider"
widget: list
fields:
- {label: "Image", name: "image", widget: image}
- {label: "Url", name: "url", widget: string, required: false}
Error Message
ERROR
GraphQL Error Field "image" must not have a selection since type "String" has no subfields.
file: /home/gaara/JS/iconic-starter-netlify-cms/src/pages/index.js
1 |
2 | query IndexPage {
3 | pagesYaml(page: { eq: "index" }) {
4 | id
5 | slider {
6 | desktop {
> 7 | image {
| ^
8 | childImageSharp {
9 | fluid(maxWidth: 2000, quality: 90) {
10 | aspectRatio
11 | presentationWidth
12 | src
13 | srcSet
14 | sizes
15 | }
16 | }
17 | }
⠙ extract queries from components
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.
I had already made sure that all the image exists inside the static/img/ folder. I had also made several attempt in restarting the server so to avoid image not loading issue. The image path that given from netlify-cms should stay as /img/xxx.jpg because there is a lot of other markdown files uses it and has no problem in resolving the path.
May I know is there any configuration problem which I did wrong or miss out that causing the gatsby-remark-relative-images not being able to resolve the file path?
gatsby-remark-relative-images is a plugin that only works with markdown files handled by gatsby-transformer-remark.
The recent-ish graphql schema customization update allows new solution that's independent of file types. You can explore the official docs on the topic here: Customize the Graphql Schema (gatsby docs)
Instead modifying the image paths before gatsby gets to it (like with gatsby-remark-relative-images), we'd customize the graphql schema so that the image field (slider.desktop.image in your case) resolves to the image file node instead.
Please note that the node types below are just loose examples & you should go to your graphiql endpoint (i.e. localhost:8000/graphiql) to find the correct type names.
exports.createSchemaCustomization = ({ actions }) => {
const { createTypes, createFieldExtension } = actions
createFieldExtension({
name: 'fileByStaticPath',
extend: () => ({
resolve: (src, args, ctx, info) => {
// look up original string value
const { fieldName } = info
const partialPath = src[fieldName]
// TODOS
// - join path to create the correct image file path
// - query the file node with `context.nodeModel.runQuery`
// - return the file node if exists
}
})
})
const typeDefs = `
type YamlSliderDesktop #infer {
image: File #fileByStaticPath
}
type YamlSlider #infer {
desktop: YamlSliderDesktop
}
type PagesYaml implements Node #infer {
slider: YamlSlider
}
`
createTypes(typeDefs)
}
I found myself doing this pretty often, so I wrote a plugin for it: gatsby-schema-field-absolute-path (github).
I also wrote a bit more in depth about this over here on my blog (byderek.com), but really, nothing that you won't find in the Gatsby official docs, just explained in a different language.
Hope it helps!
I am looking for help with GatsbyJS and Contentful. The docs aren't quite giving me enough info.
I am looking to programmatically create pages based on contentful data. In this case, the data type is a retail "Store" with a gatsby page at /retail_store_name
The index.js for each store is basically a couple of react components with props passed in e.g. shop name and google place ID.
Add data to contentful. Here is my example data model:
{
"name": "Store"
"displayField": "shopName",
"fields": [
{
"id": "shopName",
"name": "Shop Name",
"type": "Symbol",
"localized": false,
"required": true,
"validations": [
{
"unique": true
}
],
"disabled": false,
"omitted": false
},
{
"id": "placeId",
"name": "Place ID",
"type": "Symbol",
"localized": false,
"required": true,
"validations": [
{
"unique": true
}
],
"disabled": false,
"omitted": false
}
}
I've added the contentful site data to gatsby-config.js
// In gatsby-config.js
plugins: [
{
resolve: `gatsby-source-contentful`,
options: {
spaceId: `your_space_id`,
accessToken: `your_access_token`
},
},
];
Query contentful - I'm not sure where this should happen. I've got a template file that would be the model for each store webpage created from contentful data.
As mentioned this is just some components with props passed in. Example:
import React, { Component } from "react";
export default class IndexPage extends Component {
constructor(props) {
super(props);
this.state = {
placeId: "",
shopName: "",
};
}
render (){
return (
<ComponentExampleOne shopName={this.state.shopName} />
<ComponentExampleTwo placeId={this.state.placeId} />
);
}
I'm really not sure how to go about this. The end goal is auto publishing for non-tech users, who post new stores in Contentful to be updated on the production site.
You can create pages dynamically at build time and to do that you need to add some logic to the gatsby-node.js file. Here is a simple snippet.
const path = require('path')
exports.createPages = ({graphql, boundActionCreators}) => {
const {createPage} = boundActionCreators
return new Promise((resolve, reject) => {
const storeTemplate = path.resolve('src/templates/store.js')
resolve(
graphql(`
{
allContentfulStore (limit:100) {
edges {
node {
id
name
slug
}
}
}
}
`).then((result) => {
if (result.errors) {
reject(result.errors)
}
result.data.allContentfulStore.edges.forEach((edge) => {
createPage ({
path: edge.node.slug,
component: storeTemplate,
context: {
slug: edge.node.slug
}
})
})
return
})
)
})
}
the createPages that was exported is a Gatsby Node API function you can find the complete list in the docs here.
For the query allContentfulStore it's called like that because your contentType name is store the gatsby query will be allContentful{ContentTypeName}.
Finally, I created a youtube video series explaining how you can build a Gatsby website with Contentful. You can find it here
I hope this answer your question.
Cheers,
Khaled
I faced issue while working on vuejs application:
I have to change titles in few vuejs components, depending on routes
routes: [
{
path: '/',
components: { //with default data
FirstScreen,
Advantages,
Slider,
},
},
{
path: '/moscow',
components: { //with special data for Moscow
FirstScreen,
Advantages,
Slider,
},
},
{
path: '/berlin',
components: { //with special data for Berlin
FirstScreen,
Advantages,
Slider,
},
},
],
and data at all .vue files looks like this
data() {
return {
defaultTitle: 'some string',
defaultArray: ['defaultFirst', 'defaultSec'],
};
},
And I have about 100 cities... how can I solve this issue?
Assuming you use vue-router you can 'hook into' one of it's extremely helpful methods called beforeEach. This methods acts as a sort of middleware and runs before any given route is executed.
router.beforeEach((to, from, next) => {
document.title = to.meta.title
next();
});
If someone will need to implement data transfer depending on routes and really want to hardcode it to route file it is possible with this: https://github.com/vuejs/vue-router/blob/dev/docs/en/essentials/passing-props.md
In my case it will be looks like:
routes: [
{
path: '/',
components: {
FirstScreen,
Advantages,
BigSlider,
},
props: {
FirstScreen: {
title: 'title',
subtitle: ['arr1', 'arr2'],
},
Advantages: {
title: 'title',
advantages: 'advantages',
},
BigSlider: {
title: 'title',
slideText: 'slideText',
},
},
},
and in component you have to do something like this
export default {
props: {
title: {
type: String,
},
subtitle: {
type: Array,
},
},
It will work fine, but I agree with Kevin Karsopawiro in part that this approach is unmaintainable. So using city-component for fetching data from back-end is best in my case.