intlify / bundle-tools

bundling for intlify i18n tools
MIT License
239 stars 37 forks source link

SFC and Cannot translate the value of keypath... #6

Closed Kouty closed 1 year ago

Kouty commented 7 years ago

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:

After that, it worked for me (and my colleagues facing the same problem).

falkartis commented 7 years ago

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>
Kouty commented 7 years ago

@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:

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.

falkartis commented 7 years ago

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.

Kouty commented 7 years ago

Sorry for the late answer. It seems that vue i18n is designed to be used in 2 ways:

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.

falkartis commented 7 years ago

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>
Kouty commented 7 years ago

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.

amaranthius commented 7 years ago

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...

Kouty commented 7 years ago

Indeed there is.

...
</template>

<i18n src="./i18n.json"></i18n>

<script>
...
TugayYaldiz commented 6 years ago

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.

maronk commented 5 years ago

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 tag that do not mention this.

kazupon commented 1 year ago

vue-i18n-loader will be deprecated near the future We can use unplugin-vue-i18n Thanks