bundle-tools
bundle-tools copied to clipboard
SFC and Cannot translate the value of keypath...
This is not exactly an issue of vue-i18n-loader, I am writing it as a reminder for others that may face this problem: if, inside a single file component, you get the error "Cannot translate the value of keypath...", it may be cause by npm cache.
Solution:
- Check, double check that your vue-i18n-loader has been properly configured
- Add a syntax error inside the
<i18n>{...}</i18n>
tag - Check that webpack does not compile because the loader complains about the syntax error (if it doesn't, than you didn't configure properly the vue-i18n-loader inside webpack config file)
- Remove the syntax error
- Remove node_modules folder (the whole folder, not just the i18n folders)
- run npm cache clean
- run npm install
After that, it worked for me (and my colleagues facing the same problem).
Hi @Kouty , I'm pretty stuck trying to make this i18n to work. I don't know if it's because I'm dumb, or because it's poorly documented, or because the documentation doesn't quite match to the reality?
I tried out the points you've posted, but I'm not sure about some of them. So I went directly to the remove, clean and install part.
But without success... Am I missing something?
How should the vue-i18n-loader
be configured?
This is what I have
webpack.config.js
var path = require('path')
var webpack = require('webpack')
module.exports = {
entry: './src/main.js',
output: {
path: path.resolve(__dirname, './dist'),
publicPath: '/dist/',
filename: 'build.js'
},
module: {
rules: [
{
test: /\.vue$/,
loader: 'vue-loader',
options: {
loaders: {
// copiat de http://kazupon.github.io/vue-i18n/en/sfc.html
// you need to specify `i18n` loaders key with `vue-i18n-loader` (https://github.com/kazupon/vue-i18n-loader)
i18n: '@kazupon/vue-i18n-loader'
//i18n: 'vue-i18n-loader'
}
// other vue-loader options go here
}
},
{
test: /\.js$/,
loader: 'babel-loader',
exclude: /node_modules/
},
{
test: /\.(png|jpg|gif|svg)$/,
loader: 'file-loader',
options: {
name: '[name].[ext]?[hash]'
}
}
]
},
resolve: {
alias: {
'vue$': 'vue/dist/vue.esm.js'
}
},
devServer: {
historyApiFallback: true,
noInfo: true
},
performance: {
hints: false
},
devtool: '#eval-source-map'
}
if (process.env.NODE_ENV === 'production') {
module.exports.devtool = '#source-map'
// http://vue-loader.vuejs.org/en/workflow/production.html
module.exports.plugins = (module.exports.plugins || []).concat([
new webpack.DefinePlugin({
'process.env': {
NODE_ENV: '"production"'
}
}),
new webpack.optimize.UglifyJsPlugin({
sourceMap: true,
compress: {
warnings: false
}
}),
new webpack.LoaderOptionsPlugin({
minimize: true
})
])
}
this produces following error:
ERROR in ./src/App.vue
Module not found: Error: Can't resolve '@kazupon/vue-i18n-loader' in '/my/project/path/src'
@ ./src/App.vue 21:18-131
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://localhost:8080 webpack/hot/dev-server ./src/main.js
main.js
import Vue from 'vue';
import VueI18n from 'vue-i18n'
import VueRouter from 'vue-router';
import SocialSharing from 'vue-social-sharing';
import App from './App.vue';
//import Home from './Home.vue';
Vue.use(VueI18n)
//Vue.use(Vue-i18n)
Vue.use(VueRouter);
Vue.use(SocialSharing);
const i18n = new VueI18n({
locale: 'es',
messages: {
es: {
}
}
})
const routes = [
{ path: '/', component: view('Home') },
//{ path: '/', component: Home },
// MORE PATHS HERE
]
const router = new VueRouter({
history: true,
routes,
scrollBehavior (to, from, savedPosition) {
if (to.hash) {
return {
selector: to.hash
}
}
}
});
/**
* Asynchronously load view (Webpack Lazy loading compatible)
* @param {string} name the filename (basename) of the view to load.
*/
function view(name) {
return function(resolve) {
require(['./' + name + '.vue'], resolve);
}
};
export default router;
new Vue({
i18n,
router,
el: '#app',
render: h => h(App)
});
App.vue
<template>
<div id="app">
<app-header></app-header>
<router-view></router-view>
<app-footer></app-footer>
<label for="locale">locale</label>
<select v-model="locale">
<option>ca</option>
<option>es</option>
</select>
<p>message: {{ $t('hola') }}</p>
</div>
</template>
<i18n>
{
"es": { "hola": "Hola mundo!" ], //is this enough syntax error?
"ca": { "hola": "Hola mon!" }
}
</i18n>
<script>
import Header from './Header.vue';
import Footer from './Footer.vue';
export default {
name: 'app',
components: {
'app-header': Header,
'app-footer': Footer,
},
data () {
return {
locale: 'es'
}
},
watch: {
locale (val) {
this.$i18n.locale = val
}
}
}
</script>
<style>
/*SOME CSS HERE*/
</style>
<style scoped>
/*SOME CSS HERE*/
</style>
@falkartis
ERROR in ./src/App.vue
Module not found: Error: Can't resolve '@kazupon/vue-i18n-loader' in '/my/project/path/src'
@ ./src/App.vue 21:18-131
@ ./src/main.js
@ multi (webpack)-dev-server/client?http://localhost:8080 webpack/hot/dev-server ./src/main.js
The error above gives us some hints:
- The configuration is enough OK to understand that there is a
@kazupon/vue-i18n-loader
- That module cannot be resolved. The most probable cause is that you forget to save inside package.json file
@kazupon/vue-i18n-loader
entry
In fact, by removing node modules and @kazupon/vue-i18n-loader
from package json, I got this error
bundle.js:4585 ./src/welcome/welcome.vue
Module not found: Error: Can't resolve '@kazupon/vue-i18n-loader' in '... aenigmates\src\welcome'
@ ./src/welcome/welcome.vue 20:18-67
@ ./src/index.es6
@ multi (webpack)-dev-server/client?http://localhost:8081 ./src/index.es6
which seems to be the same error.
So, just run npm i --save-dev @kazupon/vue-i18n-loader
and restart webpack-dev-server.
Nice, it works!
Unfortunately it works only in the scope of the component where I put the language selector. how can I pass the language to other components? I tried putting :locale="locale"
so it looks like this <app-header :locale="locale"></app-header>
but nothing happens.
Sorry for the late answer. It seems that vue i18n is designed to be used in 2 ways:
- Global: when you setup VueI18n you can pass
messages
option with all the global translations - Per component: every component has its own i18n file/configuration.
So passing translations to subcomponents I think it is not a good option. By the way, $t
is available to the parent component, so I think you can pass it to sub components through vue component property system.
Hi, no problem, things take time, it's normal.
Per component is my idea, each component has it's own <i18n>
tag.
This is how my setup looks like:
Is this the right way to do it? do I have to put the watch
and props
parts in every component?
This is the App.vue file
<template>
<div id="app">
<select v-model="locale" size="2"><option>ca</option><option>es</option></select>
<app-header :locale="locale"></app-header>
<p>{{ $t('hola') }}</p>
<router-view></router-view>
<p>{{ this.$i18n.locale }}</p>
<app-footer></app-footer>
</div>
</template>
<i18n>
{
"es": { "hola": "Hola mundo!" },
"ca": { "hola": "Hola mon!" }
}
</i18n>
<script>
import Header from './Header.vue';
import Footer from './Footer.vue';
export default {
name: 'app',
components: {
'app-header': Header,
'app-footer': Footer,
},
data () {
return {
locale: 'es'
}
},
watch: {
locale (val) {
this.$i18n.locale = val
}
}
}
</script>
This is the Header.vue file
<template>
<header>
<a href="./"><img src="./assets/logo.png" alt="the alt string"></a>
<nav>
<ul>
<li><router-link to="/">{{ $t('inicio') }}</router-link></li>
<li><router-link to="/exploracion-terapias">{{ $t('exploraterap') }}</router-link></li>
<li><router-link to="/contacto">{{ $t('contacto') }}</router-link></li>
</ul>
</nav>
</header>
</template>
<i18n>
{
"es": {
"inicio": "Inicio",
"exploraterap": "Exploración y terapias",
"contacto": "Contacto"
},
"ca": {
"inicio": "Inici",
"exploraterap": "Exploració i terapies",
"contacto": "Contacte"
}
}
</i18n>
<script>
export default {
watch: {
locale (val) {
this.$i18n.locale = val
}
},
props: ['locale']
}
</script>
You need to change locale (this.$i18n.locale = val
) only in the root component. It will change the translations on all subcomponents.
In your exaple, when the locale is changed in App.vue, Header.vue will change translations accordingly. No props or watch are needed in Header.vue.
In case you need to change the language from a subcomponent, just emit an event with the user selected new locale, and change the locale in the root component. But that's not the case of yout example.
Sorry for posting a bit off-topic, guys, but is there a way to externalize the messages and have them in a separate file? I didn't find anything in the Docs regarding that...
Indeed there is.
...
</template>
<i18n src="./i18n.json"></i18n>
<script>
...
As @Kouty 's say; you need to change root component's $i18n
. So there is $root
instance property (which reach to the App.vue) available in every component or sub component in your Vue instance. İnstead of changing this.$i18n.locale
you can change this.$root.$i18n.locale = newVal
in template tags just $root.$i18n.locale = newVal
.
No need to emiting events, watching props etc.
In case it helps someone had this problem and searched for hours - what I found was that this was missing from vue.config.json
// for vue.config.js (Vue CLI)
module.exports = {
chainWebpack: config => {
config.module
.rule('i18n')
.resourceQuery(/blockType=i18n/)
.type('javascript/auto')
.use('i18n')
.loader('@kazupon/vue-i18n-loader')
}
}
While this is clearly documented here https://github.com/kazupon/vue-i18n-loader there are a lot of paths online that lead you to using the
vue-i18n-loader will be deprecated near the future We can use unplugin-vue-i18n Thanks