vue-i18n $t with values attribute is not translating - vue.js

I am trying to import a .json file to use as the translation file.
<template>
<template v-slot:footer>
<div>{{ $t('menu.file.new.label', $i18n.locale, locale) }}</div> <--Issue outputs menu.file.new.label
</template>
</template>
<script>
import locale from '#/locales/modules/messaging.json'
export default {
data() {
return {
locale: locale
}
}
}
</script>
the locale from messaging.json does not have any errors and works if i instead added the following to the top
<i18n src="#/locales/modules/messaging.json"></i18n>
and changed the function parameters to exclude $i18n.locale and locale and it works. Unfortunately, this is not an option as i want to pass the data to a grandchild component. However, if i can configure the grandchild to use their grandparents translation data that works too..
how can i get either:
The above to work
Alternatively, use the grandparents translation data in the grandchild
Alternatively, dynamically import translation data in the grandchild based on a prop(location of translation file to be imported)
Thanks

First of all, you should create a plugin like below:
src/plugins/i18n.js:
import Vue from 'vue';
import VueI18n from 'vue-i18n';
const DEFAULT_LOCALE = 'en';
Vue.use(VueI18n);
const i18n = new VueI18n({
locale: yourLocale || DEFAULT_LOCALE, // set locale either from localStorage or config
fallbackLocale: DEFAULT_LOCALE,
messages: require('messages.json'), // set locale messages
sharedMessages: require('other.json if exist'),
silentFallbackWarn: true,
});
export default i18n;
Then call it from main.js to globalize:
import i18n from './plugins/i18n.js';
...
new Vue({
i18n,
router,
...
render: h => h(App),
}).$mount('#app');
Then if you want to go on with custom messages, you can set it with i18n block like:
<script>
data(){
...
}
methods: ...
i18n: {
messages: require(your json path....)
}
</script>
Then you can call it like:
$t('test');

I found a solution
<template>
<st-age v-bind:menus="menu" v-bind:locale="locale[$i18n.locale].menu">
<template v-slot:content>message: {{ $route.params }}</template>
<template v-slot:footer>
<div>{{ $t('menu.file.label') }}</div>
</template>
</st-age>
</template>
<script>
import menu from './menu'
import locale from '#/locales/modules/messaging.json'
export default {
data() {
return {
menu: menu,
locale: locale
}
},
i18n: {
messages: locale
},
components: {
'st-age': () => import('#/components/layout/stage/container')
}
}
</script>
<style>
</style>
locale[$i18n.locale].menu is passing the translation data i actually need and not the entire object(which works too)
in the child component, i just pass this data as a prop to the grandchild
in the grandchild i
mounted() {
this.$i18n.setLocaleMessage(this.$i18n.locale, this.locale)
}
where this.locale is the translation data and $t('file') yields whatever i set as en.menu.file in the global translation data originally imported

Related

Vue-i18n Single File Component Not Working

I am trying to implement v-i18n to my project with sfc method. I couldn't make it work. I will not make you confuse with my project, that's why just modified with adding 10-15 lines of code to official v-i18n sfc example.
This is very simply shows my question.
For those who prefer check this very tiny question project on github
https://github.com/berkansivri/V-i18n-Question
Component1.vue
<template>
<p>{{$t('lang')}}</p>
</template>
<i18n>
{
"en":{
"lang" : "English"
},
"es":{
"lang": "Espanol"
}
}
</i18n>
App.vue
<template>
<div id="app">
<label for="locale">locale</label>
<select v-model="locale">
<option>en</option>
<option>es</option>
</select>
<p>message: {{ $t('hello') }}</p>
<Comp1></Comp1>
</div>
</template>
<i18n>
{
"en": {
"hello": "hello"
},
"es": {
"hello": "hola"
}
}
</i18n>
<script>
import Comp1 from './components/component1'
export default {
components:{
Comp1
},
name: 'App',
data () { return { locale: 'en' } },
watch: {
locale (val) {
this.$i18n.locale = val
}
}
}
</script>
So, multiple <i18n>tag in multiple components. I just modified $i18n.locale from App.vue but it did not fire related i18n function $t('lang') on Component1, just modifies $t('hello') on itself.
How can I make it work?
using vue devtools u will find out that messages of $i18n in single file component is different from each other, so they are different objects.
u need to do is:
i18n.js
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import messages from '#/lang'
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: 'cn',
fallbackLocale: 'en',
messages
})
export default i18n
App.vue
import i18n from "./i18n.js"
i18n.locale = "en"
This is intended behavior of Single file components. If you want to change all locales of all components you can use:
locale (val) {
// this.$i18n.locale = val
this.$root.$i18n.locale = val
}
See this issue.

How do you access exported vm (main vue instance) object from a component?

If I start my vue instance from a main.js file
//main.js
var vm = new Vue({
el: '#app',
render: h => h(App),
router,
data: {
}
});
export {
vm
}
app.vue itself is a router view.
<template>
<router-view></router-view>
</template>
<script>
export default {}
</script>
So lets say one of the components that gets loaded in the router needs access
to vm? I've gotten as far as to do this in the component:
import vm from '../main.js'
It seems to find the main.js file. But how do I then access vm? An example of a problem is when I use vue-lazyload(https://github.com/hilongjw/vue-lazyload) and need to access vm like I try here:
<template>
<div class="hero-unit-bg" v-lazy:background-image="imgUrl" >
</div>
</template>
<script>
import Vue from 'vue'
import VueLazyload from 'vue-lazyload'
import vm from '../main.js'
Vue.use(VueLazyload)
vm.$Lazyload.$on('loaded', function ({ bindType, el, naturalHeight, naturalWidth, $parent, src, loading, error }, formCache) {
console.log(el, src)
})
export default {
name: 'HeroUnit',
data () {
return {
imgUrl: 'img/hero-unit-bg.png' // String
}
},
methods: {
},
}
}
</script>
Console shows vm.$Lazyload as undefined. So I don't think I'm importing vm properly. Am I missing something? Thank you.
If you are exporting like this:
export { vm }
then you need to import it like this:
import { vm } from './module.js'
For a default export, it would work like this:
export default vm
import vm from './module.js'
You'd likely create a circular dependency by importing main.js into a component. You actually don't need to reference the root instance, as the code Vue.use(VueLazyLoad) makes the plugin accessible from any component method via this.$LazyLoad.
For example, you could setup your code as follows:
main.js:
import VueLazyload from 'vue-lazyload'
Vue.use(VueLazyload)
App.vue
export default {
...
mounted() {
this.$Lazyload.$on('loaded', function ({ bindType, el, naturalHeight, naturalWidth, $parent, src, loading, error }, formCache) {
console.log(el, src)
})
}
}
MyComponent.vue
<template>
<div v-lazy-container="{ selector: 'img' }">
<img data-src="//placekitten.com/200/200">
<img data-src="//placekitten.com/200/201">
<img data-src="//placekitten.com/200/202">
</div>
</template>
demo

How to hide a paragraph using v-if through vuex?

I am trying to create a small app in which i can click on the button and hide the paragraph but i am trying to implement that using vuex.I have a paragraph in my Home.vue file and a button in my About.vue File. I want the paragraph hide conditionally in the click of the button but i want to accomplish that using vuex. How would i do that? My store.js, home.vue and About.vue are as follows.
This is how my store looks like.
import Vue from 'vue'
import Vuex from 'vuex'
Vue.use(Vuex)
export default new Vuex.Store({
state: {
show:true,
},
mutations: {
toggle : state => {
state.show = !state.show
}
},
actions: {
}
})
This is the Home.vue file
<template>
<p>This needs to disappear</p>
</template>
<script>
import {mapMutations} from "vuex"
export default {
computed : {
...mapMutations ([
"toggle"
])
}
}
</script>
This is the About.vue file
<template>
<div>
<button #click="toggle">Click Me</button>
</div>
</template>
<script>
import {mapMutations} from "vuex"
export default {
computed : {
...mapMutations ([
"toggle"
])
}
}
</script>
the mapMutations should be used in methods not in computed property :
methods:{
...mapMutations ([
"toggle"
])
}
like you see in the official docs :
You can commit mutations in components with this.$store.commit('xxx'), or use the mapMutations helper which maps component methods to store.commit calls (requires root store injection):

Passing multiple properties in vuejs

I'm quite new with VueJS. I'm working on a new project with VueCLI3 & VuetifyJS.
I'm trying to create a reusable components based on VuetifyJS components and would like to make things easier by passing multiple props in a separate file to render them at once in my new component files.
I found this article that explains a technique to achieve such thing.
https://alligator.io/vuejs/passing-multiple-properties/
Every time I need to render them I must import my seperate file.
component1.vue
<template>
<v-btn v-bind='buttonProps'>
Button 1
</v-btn>
</template>
<script>
import { buttonProps } from './props.js';
export default {
data: () => ({ buttonProps })
}
</script>
component2.vue
<template>
<v-btn v-bind='buttonProps'>
Button 2
</v-btn>
</template>
<script>
import { buttonProps } from './props.js';
export default {
data: () => ({ buttonProps })
}
</script>
Is there any way to register the file globally so it allows me to use it anywhere in the app like this?
props.js
export const buttonProps = {
color: 'primary',
small: true,
outline: true,
block: true,
ripple: true,
href: 'https://alligator.io'
}
main.js
import Props from 'props.js';
component3.vue
<template>
<v-btn v-bind='buttonProps'>
Button 3
</v-btn>
</template>
<script>
... // no import needed
</script>
You can use a mixin and register that mixin globally.
buttonPropsMixin
export default {
data() {
return {
buttonProps: {
color: 'primary',
small: true,
  outline: true,
block: true,
ripple: true,
href: 'https://alligator.io'
}
}
}
}
main.js
import buttonPropsMixin from '...';
Vue.mixin(buttonPropsMixin)
Note That each vue component has its own buttonProps, so if you change in one component the color it will not affect the other components!
If you want buttonProps to have the same state across all your components you can go the vuex way as Igor mentioned and use it with an mixin where you define the mapGetters in that mixin.
If data in props.js doesn't need to be reactive and all the components are children of some root component you could do this:
main.js:
import buttonProps from 'props.js';
new Vue({
el: rootComponentElement,
buttonProps: buttonProps,
render: h => h(rootComponent)
})
component.vue:
<template>
<v-btn v-bind='$root.$options.buttonProps'>
Button 3
</v-btn>
</template>
<script>
... // no import needed
</script>
Otherwise I would advice you to use Vuex or use the global bus method described here.

User editable Vue template

In my app, I have a template for things like Invoice, Email etc. I'd like the user to be able to edit these templates by dragging and dropping elements. I'm currently using vue-loader along with webpack to pre-compile my vue files into pure JS.
Is it possible to load a vue template from the database on the fly? I've seen this post but this isn't using vue-loader so I'm not sure how to override the template on my component via the code. Something like:
created: function () {
this.$template = '<html><p>Loaded from the DB!</p></html>'
}
would be useful. Is this possible?
Edit: I've tried the following but I get an error Failed to execute 'insertBefore' on 'Node': The node before which the new node is to be inserted is not a child of this node.:
created: function () {
document.body.innerHTML = '<html><p>I AM FROM THE DB {{total}}</p></html>'
}
This would need to be modified to pass in the templates from your database, but this works in a very simple single file component. Obviously you will want to customize, but this demonstrates the concept.
Dynamic.vue
<script>
export default {
props:["template"],
data(){
return {
message:"hello"
}
},
created(){
this.$options.template = this.template
}
}
</script>
App.vue
<template>
<div>
<dynamic
v-for="template, index of templates"
:template="template" :key="index">
</dynamic>
</div>
</template>
<script>
import Vue from "vue"
import Dynamic from "./Dynamic.vue"
export default {
name: 'app',
data () {
return {
templates: [
"<h1>{{message}}</h1>",
"<h4>{{message}}</h4>"
]
}
},
components:{
Dynamic
}
}
</script>
main.js
import Vue from 'vue'
import App from './App.vue'
new Vue({
el: '#app',
render: h => h(App)
})