We haven't looked much into how i18n should work in Gridsome yet, but we are open for discussion if anyone has a good idea :)
Hugo is doing it really well, maybe also have a look at JSLingui, v3 is promising. A lot of users are waiting for an easy/fully working way to do it with Gatsby... will you be the first ?
I think this is a must-have nowadays. Would definitely consider Gridsome if i18n would be available.
This blog nails it
https://blog.davidpaternina.com/choosing-a-static-site-generator/
Having used Vuepress myself, I didn't had any major complaint with it, so I think its good enough as a starting point
https://vuepress.vuejs.org/guide/i18n.html
I think we can use different domains (or subdomains) for internationalization. If the website is deployed by netlify we can use netlify build variables to determine what language key to use for specific domain. Something like APP_LANG=de.
I am using Vuepress now and I like the way they handle it:
Downside (from a SEO perspective):
The only thing that stops me from using Gridsome right now is internationalization. I know guys from Nuxt have their own i18n implementation. I'll leave a link for future references. https://nuxt-community.github.io/nuxt-i18n/
What about vue-i18n?
Here is my suggestion on how to solve i18n problem with Gridsome:
This is how markdown settings look in gridsome.config.js:
plugins: [
{
use: '@gridsome/source-filesystem',
options: {
path: 'src/articles/blog/**/*.md',
typeName: 'BlogPost',
route: '/blog/:slug'
}
}
]
We don't need to add any new functionality to change our organizational structure for multilingual content. Lets say that we want to add 2 markdown sources for 2 languages, where English is set as default, while Serbian is set as secondary language. Source for content can be kept is separate folders:
src/articles/blog/en/src/articles/blog/sr/Settings can look like this:
plugins: [
{
use: '@gridsome/source-filesystem',
options: {
path: 'src/articles/blog/en/**/*.md', // Added `en` in source path
typeName: 'BlogPostEn', // Added `En` at the end of the value
route: '/blog/:slug' // Nothing is added to route if we want to keep it as default
}
},
{
use: '@gridsome/source-filesystem',
options: {
path: 'src/articles/blog/sr/**/*.md', // Added `sr` in source path
typeName: 'BlogPostSr', // Added `Sr` at the end of the value
route: '/sr/blog/:slug' // Added `sr` to URL route, since everything related to this language should be in routes that start with `/sr/`
}
}
]
We can organize our pages in the similar manner. In our src/pages/ folder, everything we keep in root folder can be used for default language, while translated pages can be moved to folders with language names.
Root folder for pages is /src/pages/ and inside of it we can have structure like this one
About.vue
Index.vue
/sr/Opsirnije.vue <-- translated `About.vue`
/sr/Index.vue <-- Translated `Index.vue`
What we lack in both cases is option to connect these languages logically. For this to work, we will need to have some kind of language router file where we can connect translated content.
Lets say that routing can be handled as simple as this:
[ // Array of all paths
{
en: "/", // `src/pages/Index.vue
sr: "/sr/" // `src/pages/sr/Index.vue
},
{
en: "/about/", // `src/pages/About.vue`
sr: "/sr/opsirnije/" // `src/pages/sr/Opsirnije.vue`
},
{
en: "/blog/", // `src/articles/blog/en/**/*.md`
sr: "/sr/blog/" // `src/articles/blog/sr/**/*.md`
}
]
This can be useful for language switcher component that would need to be developed.
This routing would handle basic routes, we can even add routes for specific blog articles in here without any advanced logic behind it, but for the larger sites this may prove to be cumbersome.
[
// ... Other routes...
{
en: "/blog/blog-article-1",
sr: "/sr/blog/blog-clanak-1"
}
]
Important thing to remember is that some sites will be deliberately missing some of the content for some languages. Lets say that we have third language here... Spanish, and that for Spanish we don't want to have blogging component. In that case we would leave out that route from this file.
When Language Switcher component is developed, selecting different language should switch route for route that was set in this file. If the route is not set, best option would be to change it to index page / root URL for that language.
In some cases we want to develop components that are not translated as a whole, like articles or blog posts, but only have strings that need to be translated. In this case we would need to have files with string translations. Lets say that we would keep them in /src/localization/ separated by folders with short language names en, sr, es...
Pluralization rules: http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html?id=l10n/pluralforms
Another explanation for pluralization rules: http://i18njs.com/#pluralisation
Lets say that you want to create a component that counts the number of articles. English translation for the number of articles could be:
There is 1 articleThere are 5 articlesSerbian translation would be different:
Postoji 1 članakPostoji 3 člankaPostoji 12 članakaEach language file can created to be something like this:
/src/localization/en.js
export default {
en: {
dateFormat: "YYYY-MM-DD",
plural: "nplurals=2; plural=(n != 1);",
strings: {
"_ARTICLE_1": [
"There is",
"There are"
],
"_ARTICLE_2": [
"article",
"articles"
]
}
}
}
Serbian should look like this
/src/localization/sr.js
export default {
sr: {
dateFormat: "DD. MM. YYYY.",
plural: "nplurals=3; plural=(n%10==1 && n%100!=11 ? 0 : n%10>=2 && n%10<=4 && (n%100<10 || n%100>=20) ? 1 : 2);",
strings: {
"_ARTICLE_1": [
"Postoji",
"Postoji", // Same for all situations
"Postoji",
],
"_ARTICLE_2": [
"članak",
"članka",
"članaka"
]
}
}
}
Ideally, this is how would translateable strings look in our component:
<template>
<p>{{ t('_ARTICLE_1', this.articleCount) this.articleCount t('_ARTICLE_1', this.articleCount) }}</p>
</template>
<script>
export default {
data(): {
return {
language: "en",
articleCount: 2
}
}
}
</script>
Result for English would be
<p>There are 2 articles</p>
Result for Serbian would be
<p>Postoji 2 članka</p>
Translation function t(string, no) should take 2 parameters
string - string identifier that returns translation based on pluralization rulesno - optional number used for pluralization. Based on plural rule defined in language file, it will select the correct string to display. If this number is omitted, then only the first translated string should be used in output. t('_ARTICLE_1') for en would return "There is".We can use htmlAttrs to set page language.
<script>
export default {
data(): {
return {
language: "en",
articleCount: 2
}
},
metaInfo() {
return {
htmlAttrs: {
lang: this.language
}
}
}
}
</script>
I hope that these ideas may help with further development.
Will i18n support be on the official roadmap?
This is the only requirement for us in order to migrate from Nuxt, which we would love to do, any updates on progress?
What's about this feature? Is there any plans about the i18n feature for Gridsome?
Sorry to spam, but been watching for a long time and would really like to get this on the roadmap (and I think a lot of others do too)
Really need this indeed! (and fast)
This feature would indeed be great, or at least a guide line to how you would implement a multi language site.
Considering moving my personal site, a custom Angular app with WordPress over to Gridsome with Markdown files, but I need multi language in order to get this to work.
Can't be "Ready for global domination" without internationalization built in.
Remove me from this thread
On Wed, Aug 7, 2019 at 4:45 PM jxlstudio notifications@github.com wrote:
Can't be "Ready for global domination" without internationalization built
in.—
You are receiving this because you authored the thread.
Reply to this email directly, view it on GitHub
https://github.com/gridsome/gridsome/issues/23?email_source=notifications&email_token=AANGRWVJDR7GFC4RALKITATQDLU2FA5CNFSM4GAPAOHKYY3PNVWWK3TUL52HS4DFVREXG43VMVBW63LNMVXHJKTDN5WW2ZLOORPWSZGOD3Y24EQ#issuecomment-519155218,
or mute the thread
https://github.com/notifications/unsubscribe-auth/AANGRWSNPJW6YBENWTV7WQDQDLU2FANCNFSM4GAPAOHA
.>
Ali Gajani
Founder at Mr. Geek
www.mrgeek.me
www.aligajani.com
:+1: for i18n support.
Nuxt has official i18n support, perhaps look at how they're doing it. I was pretty happy with how it works there.
@tomtev / @hjvedvik any updates here? 😇
@tomtev @hjvedvik i18n has 5-10x more votes than other issues in both the "to prioritize" lists and also in the "prioritized" lists. Seems like it should get more attention, no?
i'd pay to make it a priority. We'd love to use Gridsome but we are building everything with Vuepress because we make multi-lingual sites: example
@chug2k @Maxhodges I know some people are creating i18n sites with Gridsome using the Pages API, but there is no built-in solution that works for all sources automatically yet, unfortunately. This issue is something we are working towards by making the required changes in core first. We are not quite ready to implement it yet but are getting much closer with the 0.7 release :)
Very good @hjvedvik, thank you for the update :-)
As far as authoring translated content is concerned, most of the translators I have worked with prefer this kind of approach to organizing content:
title:
en: hello
fr: bonjour
sp: hola
vi: xin chào
rather than:
en:
title: hello
fr:
title: bonjour
sp:
title: hola
vi:
xin chào
...where translation is implemented for each piece of content, rather than for each language.This would mean that rather than having a different ( YAML, please :-) file for each language, there would be a different file for each 'page' or 'view' and the translations would be under each piece of content. There is even an app designed to manage translated content that uses this approach:
https://www.codeandweb.com/babeledit
I don't know what that would mean for i18n in Gridsome, or if that kind of authoring experience is even possible (although, why not? ), but I wanted to share this concept and put it in everybody's minds :-)
Another few thoughts to add from the user experience perspective:
Most of the time when a user switches languages on a site these days the whole page reloads and scrolls back to the top of the page, which is a bad user experience.
Ideally when the user switches languages the page does NOT reload, does NOT scroll, and ONLY the current language text fades out, then the new language text fades in again. This limits cognitive dissonance, keeps the position/context of the user, and the fade animation signals what has changed, giving the user the UI/UX information they need.
Check out the example hero section on Format JS:
...when you switch languages the page does NOT reload, only the language text that is changing fades out, then the new language text fades in, creating a great user experience. I bring this up now so that as you design and implement i18n these thoughts may impact your design choices in a positive way. Hopefully these things are doable after hydration, where the URL can reflect the current language, but the app does not reload, only swapping out text in an animated transition.
Hi, I've created my starter blog with Vue-I18n & Gridsome & Vuetify.
My goal is not create multiple templates for every pages.
See at work: www.dinamicamente.org
My github project: https://github.com/menteora/gridsome-blog
If you are interested I can create articles to explain how to do that.
Hi, I've created my starter blog with Vue-I18n & Gridsome & Vuetify.
My goal is not create multiple templates for every pages.
- Only one index that redirect on it/ and en/ language.
- Only one page template for it tags and same template for en tags.
- Same for Internal pages (for now, I must translate english page, but the mechanism works)
See at work: www.dinamicamente.org
My github project: https://github.com/menteora/gridsome-blogIf you are interested I can create articles to explain how to do that.
@menteora I'm looking at it right now and nice work. There is one thing that is not working properly. The build index.html file lang attribute is not changed properly. For SEO that attribute do count.
It's been more than a year now that this issue was created, Gridsome 0.7.0 is out, but priority of the integration hasn't moved since.
Any words on what the plan is (if any)? ;)
@menteora I'm looking at it right now and nice work. There is one thing that is not working properly. The build index.html file lang attribute is not changed properly. For SEO that attribute do count.
Thank you very much @MichelChouinard for your feedback! I've fixed it with HtmlAttr!
If you are interested I can create articles to explain how to do that.
@menteora I'm very interested in a detailed explanation as to how you went through that.
Currently starting a new project based on thousand of records stored multilingual in Airtable combined with multilingual content from a CMS. A good method to work with Gridsome i18n could still make me choose Gridsome.
Would very much love to see native support of course!
I've done advanced implementations of translations through Javascript and Vue before. Gunna be experimenting with my own build over the next few weeks most likely using Vue i18n (https://www.npmjs.com/package/vue-i18n) and PO/MO files through a library that exports to JSON on build. Will post my progress on this ticket
i18n would be great, especially for people using WordPress. Millions of sites use WPML or Polylang.
Does anybody know if its even possible to use WPML with the current version of gridsome?
Does anybody know if its even possible to use WPML with the current version of gridsome?
anything is possible, but that doesn't really make sense for Gridsome. It would be like trying to get spark plugs to work with a Tesla. WPML is designed for the WordPress architectural model. There's no real reason to try and make that work with Gridsome. There are more straightforward approaches.
Hi all,
I'm try to create a plugin based on my use case: https://github.com/daaru00/gridsome-plugin-i18n. I don't think it follow all the best practice of Gridsome plugins (sorry about that, I'm new to Gridsome community). I managed to make "path prefix" work and successfully integrate Vue I18n for string translation.
Any advice or feedback about it?
Hello @daaru00!
Thanks for creating this plugin, very useful (and easy to install/setup). :+1:
I only have one piece of feedback for now, as I'm still starting to use the plugin and the whole Gridsome environment.
Do you think a dynamic reload of i18n files would be doable? When I add a key/value entry to the file for now, I have to restart the server to take the changes into account.
I don't know whether this is possible or not, so I'm just asking :)
Hi @cfecherolle,
You totally right! During test phase I didn't realize this uncomfortable behaviour.. I updated the doc adding a tip for enabling hot reload: https://github.com/daaru00/gridsome-plugin-i18n#hot-reload using a different approach to load translation strings from "client side". Could this solve your problem?
(this waiting for a way to automatically load language files using some webpack tricks without the need of declaring it into main.js)
Hi @daaru00! Thank your for replying so quickly.
The workaround you suggested is great, now I don't have to restart the server everytime I update messages :)
just wondering if this will ever make it out of the "to prioritize" list @tomtev @hjvedvik
Great plugin, @daaru00 :)
We are currently working on some APIs which I hope will make i18n easier. Soon, you'll be able to use a new api.onCreatePage() hook which will let you programmatically duplicate pages for each language. It will be very useful for plugins like gridsome-plugin-i18n. It will work like this:
const myLocales = ['de', 'fr', 'it']
api.onCreatePage((options, { removePage, createPage }) => {
removePage(options) // if necessary
myLocales.forEach(locale => {
createPage({
...options,
path: `/${locale}/${options.path}`,
context: {
...options.context,
locale // to have the locale as variable in page-query
}
})
})
})
And we are working on making src for page queries working. Which will be helpful if you, for example, have different page components for each language, but they share the same query. And I'll look into if it's possible to have additional query variables as attributes. For example:
<!-- src/pages/no/om-oss.vue -->
<template>
<AboutUs locale="no" :data="$page" />
</template>
<page-query src="~/queries/AboutUs.gql" locale="no" />
<script>
import AboutUs from '~/components/AboutUs.vue'
export default {
components: {
AboutUs
}
}
</script>
Hi @daaru00, thanks from me as well for this plugin. Another piece of feedback: It works great for our "pages". But when we try to use this with @gridsome/source-wordpress for "posts", the combination of the two plugins, out of the box, prevents the server from starting up.
I think it is trying to create a page with the same path for every language in gridsome-plugin-i18n. In my gridsome.config.js I have templates: {WordPressPost: "/:slug"}. Note I don't have the same slug twice in the source data, for example this also happens when using the post :id as the path template.
Gridsome v0.7.14
Initializing plugins...
Loading data from http://wordpress.local
Load sources - 2.12s
Create GraphQL schema - 0.16s
Error: Duplicate key for property path: /versionshinweise-april-2020
at UniqueIndex.set (/Users/ronald/work/code/startmail/website/gridsome/node_modules/lokijs/src/lokijs.js:7329:17)
at Collection.add (/Users/ronald/work/code/startmail/website/gridsome/node_modules/lokijs/src/lokijs.js:5974:31)
at Collection.insertOne (/Users/ronald/work/code/startmail/website/gridsome/node_modules/lokijs/src/lokijs.js:5738:17)
at Collection.insert (/Users/ronald/work/code/startmail/website/gridsome/node_modules/lokijs/src/lokijs.js:5677:21)
at Route.addPage (/Users/ronald/work/code/startmail/website/gridsome/node_modules/gridsome/lib/pages/pages.js:473:19)
at Pages.createPage (/Users/ronald/work/code/startmail/website/gridsome/node_modules/gridsome/lib/pages/pages.js:176:18)
at createPage (/Users/ronald/work/code/startmail/website/gridsome/node_modules/gridsome/lib/app/actions.js:260:24)
at VueI18n.createManagedPages (/Users/ronald/work/code/startmail/website/gridsome/node_modules/gridsome-plugin-i18n/gridsome.server.js:63:7)
at Plugins.run (/Users/ronald/work/code/startmail/website/gridsome/node_modules/gridsome/lib/app/Plugins.js:141:17)
at processTicksAndRejections (internal/process/task_queues.js:97:5)
at async Plugins.createPages (/Users/ronald/work/code/startmail/website/gridsome/node_modules/gridsome/lib/app/Plugins.js:116:5)
@ronaldevers that problem has been fixed by one of the branches. Check it out in one of the issues raised in the plugin repo.
Hello,
Is there any updates on this?
https://gridsome.org/plugins/gridsome-plugin-i18n is not working for me.
I can't make custom path/ route translation to work.
Thanks in advance.
BR
Hello @pmfeo ,
which kind of error/issue do you have using https://gridsome.org/plugins/gridsome-plugin-i18n? Maybe we can help with it...
Hi @giuseppeaiello, Thank you very much for your interest.
Test site: https://vjpnegociosdevelop.netlify.app/
Github repo: https://github.com/pmfeo/VJP-Negocios-frontend/tree/feature_i18n
routes.js
module.exports = {
en: [
{
path: '/en/',
component: './src/pages/Index.vue'
},
{
path: '/en/about/',
component: './src/pages/About.vue'
}
],
es: [
{
path: '/',
component: './src/pages/Index.vue'
},
{
path: '/es/sobre/',
component: './src/pages/About.vue'
}
]
};
gridsome.config.js
plugins: [{
use: "gridsome-plugin-i18n",
options: {
locales: [
'es-es',
'en-us'
],
pathAliases: {
'es-es': 'es',
'en-us': 'en'
},
defaultLocale: 'es',
fallbackLocale: 'en',
enablePathRewrite: true,
rewriteDefaultLanguage: false,
enablePathGeneration: false,
route: require('./routes.js'),
messages: {},
}
}]
main.js
import VueI18n from 'vue-i18n'
Vue.use(VueI18n);
...
appOptions.i18n.setLocaleMessage('es-es', require('./locales/es-es.json'));
appOptions.i18n.setLocaleMessage('en-us', require('./locales/en-us.json'));
The custom routes are not being writed and giving 404. Am I missing something here?
Thanks again. BR
Hi @pmfeo,
is it possible that your issue could be the same solved here: https://github.com/daaru00/gridsome-plugin-i18n/issues/17#issuecomment-691509295 ?
I see you're also missing the "slug" in your routes definition, and maybe that could be the missing part...
Hello @giuseppeaiello ,
Thanks for your reply.
I added the context slug to the route.js. Also added path translation strings on each json file. Added LanguageSwitcher component and some debug info in the frontend. Site and repo updated.
It's not working. Could have something to do with the fact that the locale seems not to be changing ? https://www.screencast.com/t/eN8ftbbtBeJ
Thanks & BR
Most helpful comment
Here is my suggestion on how to solve i18n problem with Gridsome:
Organizing static content
This is how markdown settings look in
gridsome.config.js:We don't need to add any new functionality to change our organizational structure for multilingual content. Lets say that we want to add 2 markdown sources for 2 languages, where English is set as default, while Serbian is set as secondary language. Source for content can be kept is separate folders:
src/articles/blog/en/src/articles/blog/sr/Settings can look like this:
We can organize our pages in the similar manner. In our
src/pages/folder, everything we keep in root folder can be used for default language, while translated pages can be moved to folders with language names.Root folder for pages is
/src/pages/and inside of it we can have structure like this oneWhat we lack in both cases is option to connect these languages logically. For this to work, we will need to have some kind of language router file where we can connect translated content.
Lets say that routing can be handled as simple as this:
This can be useful for language switcher component that would need to be developed.
This routing would handle basic routes, we can even add routes for specific blog articles in here without any advanced logic behind it, but for the larger sites this may prove to be cumbersome.
Important thing to remember is that some sites will be deliberately missing some of the content for some languages. Lets say that we have third language here... Spanish, and that for Spanish we don't want to have blogging component. In that case we would leave out that route from this file.
When Language Switcher component is developed, selecting different language should switch route for route that was set in this file. If the route is not set, best option would be to change it to index page / root URL for that language.
Pluralization
In some cases we want to develop components that are not translated as a whole, like articles or blog posts, but only have strings that need to be translated. In this case we would need to have files with string translations. Lets say that we would keep them in
/src/localization/separated by folders with short language namesen,sr,es...Pluralization rules: http://docs.translatehouse.org/projects/localization-guide/en/latest/l10n/pluralforms.html?id=l10n/pluralforms
Another explanation for pluralization rules: http://i18njs.com/#pluralisation
Lets say that you want to create a component that counts the number of articles. English translation for the number of articles could be:
There is 1 articleThere are 5 articlesSerbian translation would be different:
Postoji 1 članakPostoji 3 člankaPostoji 12 članakaEach language file can created to be something like this:
/src/localization/en.jsSerbian should look like this
/src/localization/sr.jsIdeally, this is how would translateable strings look in our component:
Result for English would be
Result for Serbian would be
Translation function
t(string, no)should take 2 parametersstring- string identifier that returns translation based on pluralization rulesno- optional number used for pluralization. Based onpluralrule defined in language file, it will select the correct string to display. If this number is omitted, then only the first translated string should be used in output.t('_ARTICLE_1')forenwould return "There is".Setting proper HTML tags
We can use
htmlAttrsto set page language.I hope that these ideas may help with further development.