Nuxt JS i18n / multilingual with headless CMS - vue.js

Just started playing around with Nuxt (and Vue in general) so please forgive me if I am overlooking something obvious here.
I am trying to set up a multilingual site using DatoCMS as my content source.
I have set up the nuxt-i18n plugin, translated routes and all is working fine. Now in a page, I need to switch the content depending on the current locale and I have only found examples with the content being stored locally in json files etc.
I found a workaround which can't be the way it is supposed to be:
<template>
<div>
<div v-if="this.$metaInfo.htmlAttrs.lang === 'de-DE'">{{homepage.germanTitle}}</div>
<div v-else>{{homepage.englishTitle}}</div>
</div>
</template>
<script>
import gql from 'graphql-tag'
export default {
apollo: {
homepage: gql`
{
homepage {
id
title
englishTitle: title(locale: en)
germanTitle: title(locale: de)
}
}
`
}
}
</script>
Any pointers would be much appreciated! :)

I'd say that this is the right way to do it:
<div>
<template v-if="$i18n.locale === 'en">{{homepage.englishTitle}}</tempate>
<template v-else>{{homepage.germanTitle}}</tempate>
</div>
You shouldn't use this inside of template, check i18n API to see where you can get data for your condition (in this case I use $i18n.locale), use template instead of div (or ternary operator, in order to avoid markup issues in some cases).
BUT, if its possible with graphql, I'd better do the following:
homepage {
id
title: {
en: title(locale: en)
de: title(locale: de)
}
}
template:
<div>{{ homepage.title[$i18n.locale] }}</div>

I think that you should fetch the data differently from GraphQL.
I would do something like:
query {
homepage(locale: $locale) {
id
title
}
}
From DatoCMS docs: https://www.datocms.com/docs/content-delivery-api/localization
But then you need to decide how to pass the $locale variable. That depends on how you prefer. I would check here: https://vue-apollo.netlify.com/guide/apollo/queries.html#simple-query the alternative options that you have.

Related

NW.js with Vue.js how to open a link in a user's default browser?

Basically the name of the title is the question itself but let me explain.
I have a solution for this, but I think it's not practical and wants to find a better way of doing it. For example, if I want to open one link, no problem here, but if say I have tens or hundreds of links the task becomes cumbersome. Is there a neat way to solve this?
Here is the code:
<template>
<div>
<a #click.prevent="fireUpLink">External Link</a>
</div>
</template>
<script>
/* global nw */
export default {
methods: {
fireUpLink: function() {
nw.Shell.openExternal("http://example.com/");
}
}
};
</script>
In a Vue SFC, it expects a referenced variable to be defined or imported in the component, or be global. If you reference it from the global window object, it should work.
window.nw.Shell.openExternal('http://example.com');

vue v-for: translate with switch in vue i18n

I have a problem to translate the information with vue i18n that is called through "v-for", all the data from the template is translated without problem, but those that I export through arrays and scripts do not render
i am using vue-i18n: version 7.8.1
You're only ever setting the helpTitles property when your component is created.
I would suggest using $t() in your templates instead of within data(). Then it will automatically react to changes.
I honestly don't think using an array from the translation file is a great idea. I'd be more inclined to add them with their own keys, just like your question and info translation keys, eg
helpStartedTitle: "GETTING STARTED - MODEL",
helpMembersTitle: "MEMBERS",
helpAccountTitle: "ACCOUNT",
//etc
You could then set up the keys in your data like this
data: () => {
const keys = [
"helpStarted",
"helpMembers",
"helpAccount",
"helpPayment",
"helpSocial",
"helpFraud",
"helpSupport",
"helpStudio"
]
return {
helpInfo: keys.map((key, id) => ({
id,
title: `general.help.${key}Title`,
question: `general.help.${key}`,
answer: `general.help.${key}Info`
}))
}
}
then in your template
<div v-for="help in helpInfo" :key="help.id">
<div :id="help.id" class="help-subtitle">{{ $t(help.title) }}:</div>
<HelpItem
:question="$t(help.question)"
:answer="$t(help.answer)"
:num="help.id"
/>
</div>
Even better would be to just pass the translation keys through to your HelpItem component and use $t() with it.
<HelpItem
:question="help.question"
:answer="help.answer"
:num="help.id"
/>
and in the HelpItem component
export default {
name: "HelpItem",
props: {
question: String,
answer: String,
num: Number
},
// etc
}
<!-- just guessing here -->
<h1>{{ $t(question) }}</h1>
<p>{{ $t(answer) }}</p>
FYI, I've corrected answear to answer throughout.

Can I get Vue.js component as an instance?

I try to get the template of Vue.js component since I need it for another function.
First,
I have a function that needs a Vue.js component template because I want the data in the template to dynamically change and not just some static HTML.
Secondly,
I have a problem to get a template from Vue.js component. Maybe they are not allowed to do that but I am not sure. I am new to Vue.js.
But from my understanding of JS, maybe this can happen.
I am tried to do something like this:
let vmComponent = Vue.component('VueComponent', {
data() {
return {
title: 'I am a Vue.js component manually.'
}
},
template: `
<div>
<h2>Template Exchange</h2>
<h3>{{ title }}</h3>
</div>`,
});
console.log(vmComponent.template);
I hope to get the HTML string when I run vmComponent.template
If this way of doing things is not possible. Is there any other ways I can get Vue.js component template as HTML String.
For example, maybe in a new.Vue({})
It's possible to access component template with vmComponent.options.template.
A template can be declared as a string for reuse:
export const template = `...`;
const vmComponent = Vue.component('VueComponent', {
template,
...
});
This may not work with pre-compiled templates.

Vue-i18n change locale not updating everything

i'm new to vue-i18n, seams great, but have some challenge getting it to work probably.
All template translations are updated as expected when changing locale, but when
script
data() {
return {
locales: {
en: this.$i18n.t('topnav.lang.english'),
da: this.$i18n.t('topnav.lang.danish'),
sw: this.$i18n.t('topnav.lang.swedish'),
no: this.$i18n.t('topnav.lang.norwegian'),
}
}
},
template
WORKING
{{$t('topnav.lang.english')}}
NOT WORKING
<a class="dropdown-item">{{locales.en}}</a>
NOT WORKING
<a class="dropdown-item" #click="changeLocale(key)" v-for="(value, key) in locales">{{value}}</a>
i have tried a lot of things, eg. lazyload the languages files and so on, but with no luck.
change from data to computed, data is not inherently reactive but luckily computed is!
the alternative is to directly put your translation in the template if you do not want to use computed

Best way to load page data into div with VueJS

I have been doing a lot of VueJS tutorials including the router, event bus, and trying to use fetchival and axios to no avail.
The setup, I want there to be two sections. One where I have buttons and the second section would be updated with html data from html files that varies depending on the button pressed.
I have used event bus to be able to just update the second div with basic, static html
(i.e. <p>got it</p>) but I cannot, for the life of me, use any request to get html from another website or file and load it into the div.
I don't necessarily need anyone to build it for me, but even some guidance and direction would be infinitely appreciated.
Based on your comments above, I think you want to change your thinking from "loading html files" to "showing different parts of the Vue component."
Here's a basic example. I'm going to use Vue single-file component syntax, but it's not hard to refactor for class-based components:
<template>
<div>
<button #click="clickedShowFirst">Show First</button>
<button #click="clickedShowSecond">Show Second</button>
<div v-if="showingFirst">
This is the first section!
</div>
<div v-else>
This is the second section!
</div>
</div>
</template>
<script>
export default {
data: function () {
return {
// We default to showing the first block
showingFirst: true
}
}
methods: {
clickedShowFirst: function () {
this.showingFirst = true
},
clickedShowSecond: function () {
this.showingFirst = false
}
}
}
</script>
You could of course make each of the v-if blocks components of their own that you import (which makes sense if they are complex themselves).
Or as suggested by Phillipe, you can use vue-router and make each of those views a different page with a different URL.
One last recommendation to leave you with, I found Jeffrey Way's Laracasts series on Vue.js amazingly helpful when I was learning. His episode titled "Exercise #3: Tabs" is very similar to what you're asking here.
You could use vue-router (https://router.vuejs.org/en/). In first section put the router-link (https://router.vuejs.org/en/api/router-link.html), your buttons, in second section put the router-view (https://router.vuejs.org/en/api/router-view.html).