Update i18n example to use vue-i18n

This commit is contained in:
Sebastien Chopin 2017-04-14 11:55:04 +02:00
parent 68de7eaa4b
commit 135df99d3a
32 changed files with 83 additions and 556 deletions

View File

@ -7,13 +7,13 @@
<nuxt-link class="Header__Link" :to="path('/')" exact>
{{ $t('links.home') }}
</nuxt-link>
<nuxt-link class="Header__Link" :to="path('/about')">
<nuxt-link class="Header__Link" :to="path('/about')" active-class="none">
{{ $t('links.about') }}
</nuxt-link>
<nuxt-link class="Header__Link" v-if="$store.state.lang.lang === 'en'" :to="`/fr` + $route.fullPath" active-class="none">
<nuxt-link class="Header__Link" v-if="$i18n.locale === 'en'" :to="`/fr` + $route.fullPath" active-class="none" exact>
{{ $t('links.french') }}
</nuxt-link>
<nuxt-link class="Header__Link" v-else :to="$route.fullPath.replace(/^\/[^\/]+/, '')" active-class="none">
<nuxt-link class="Header__Link" v-else :to="$route.fullPath.replace(/^\/[^\/]+/, '')" active-class="none" exact>
{{ $t('links.english') }}
</nuxt-link>
</nav>
@ -27,15 +27,19 @@
export default {
methods: {
path (url) {
return (this.$store.state.lang.lang === 'en' ? url : '/' + this.$store.state.lang.lang + url)
return (this.$i18n.locale === 'en' ? url : '/' + this.$i18n.locale + url)
}
}
}
</script>
<style>
html, body
{
*, *:before, *:after {
padding: 0;
margin: 0;
box-sizing: border-box;
}
html, body {
background-color: #fff;
color: #2e2f30;
letter-spacing: 0.5px;
@ -44,42 +48,30 @@ html, body
height: 100vh;
margin: 0;
}
*, *:before, *:after
{
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container
{
.container {
width: 75%;
margin: 0 auto;
}
.container:after
{
.container:after {
content: "";
display: table;
clear: both;
}
.Header
{
.Header {
color: #fff;
height: 80px;
line-height: 80px;
background-color: #2e2f30;
}
.Header__Title
{
.Header__Title {
float: left;
font-weight: 300;
font-size: 30px;
}
.Header__Menu
{
.Header__Menu {
float: right;
}
.Header__Link
{
.Header__Link {
font-size: 16px;
color: #fff;
border: 1px solid #fff;
@ -89,12 +81,19 @@ html, body
border-radius: 5px;
margin-left: 10px;
}
.Header__Link:hover
{
.Header__Link:hover {
color: #2e2f30;
background-color: #fff;
}
.nuxt-link-active {
color: cyan;
}
.Content {
padding: 50px 0;
text-align: center;
}
.Content__Title {
font-weight: 300;
padding-bottom: 30px;
}
</style>

View File

@ -1,59 +0,0 @@
<template>
<div class="error-page">
<div>
<h1 class="error-code">{{ error.statusCode }}</h1>
<div class="error-wrapper-message">
<h2 class="error-message">{{ error.message }}</h2>
</div>
<p v-if="error.statusCode === 404"><nuxt-link class="error-link" to="/">Back to the home page</nuxt-link></p>
</div>
</div>
</template>
<script>
export default {
props: ['error'],
head () {
return {
title: this.error.message || 'An error occured'
}
}
}
</script>
<style scoped>
.error-page {
color: #000;
font-family: "SF UI Text", "Helvetica Neue", "Lucida Grande";
text-align: center;
padding-top: 20%;
}
.error-code {
display: inline-block;
font-size: 24px;
font-weight: 500;
vertical-align: top;
border-right: 1px solid rgba(0, 0, 0, 0.298039);
margin: 0px 20px 0px 0px;
padding: 10px 23px;
}
.error-wrapper-message {
display: inline-block;
text-align: left;
line-height: 49px;
height: 49px;
vertical-align: middle;
margin-bottom: 20px;
}
.error-message {
font-size: 14px;
font-weight: normal;
margin: 0px;
padding: 0px;
}
.error-link {
color: #000;
font-weight: normal;
font-size: 14px;
}
</style>

View File

@ -1,8 +1,13 @@
export default async function ({ store, params, error }) {
const lang = params.lang || 'en'
if (!store.state.lang.locales.includes(lang)) {
await store.dispatch('lang/setLang', 'en')
return error({ message: 'Page not found', statusCode: 404 })
export default function ({ i18n, store, route, params, error, redirect }) {
const locale = params.lang || 'en'
if (store.state.locales.indexOf(locale) === -1) {
return error({ message: 'This page could not be found.', statusCode: 404 })
}
// Set locale
store.commit('SET_LANG', locale)
i18n.locale = store.state.locale
// If route is /en/... -> redirect to /...
if (locale === 'en' && route.fullPath.indexOf('/en') === 0) {
redirect(route.fullPath.replace(/^\/en/, '/'))
}
await store.dispatch('lang/setLang', lang)
}

View File

@ -1,12 +1,16 @@
module.exports = {
loading: {
color: 'cyan'
build: {
vendor: ['vue-i18n']
},
router: {
middleware: 'i18n'
},
build: {
vendor: ['axios']
plugins: [
// Will inject the plugin in the $root app and also in the context as `i18n`
{ src: '~plugins/i18n.js', injectAs: 'i18n' }
],
generate: {
routes: ['/', '/about', '/fr', '/fr/about']
},
plugins: ['~plugins/i18n']
loading: { color: 'cyan' },
}

View File

@ -1,12 +1,12 @@
{
"name": "nuxt-i18n",
"dependencies": {
"axios": "^0.15.3",
"nuxt": "latest"
},
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start"
"start": "nuxt start",
"generate": "nuxt generate"
}
}

View File

@ -1,13 +1,16 @@
<template>
<about/>
<div class="Content">
<div class="container">
<h1 class="Content__Title">{{ $t('about.title') }}</h1>
<p>{{ $t('about.introduction') }}</p>
</div>
</div>
</template>
<script>
import About from '~/partials/About.vue'
export default {
components: {
About
head () {
return { title: this.$t('about.title') }
}
}
</script>

View File

@ -1,13 +1,16 @@
<template>
<home/>
<div class="Content">
<div class="container">
<h1 class="Content__Title">{{ $t('home.title') }}</h1>
<p>{{ $t('home.introduction') }}</p>
</div>
</div>
</template>
<script>
import Home from '~/partials/Home.vue'
export default {
components: {
Home
head () {
return { title: this.$t('home.title') }
}
}
</script>

View File

@ -1,13 +1,4 @@
<template>
<about/>
</template>
<script>
import About from '~/partials/About.vue'
export default {
components: {
About
}
}
import About from '~pages/_lang/about'
export default About
</script>

View File

@ -1,13 +1,4 @@
<template>
<home/>
</template>
<script>
import Home from '~/partials/Home.vue'
export default {
components: {
Home
}
}
import Index from '~pages/_lang/index'
export default Index
</script>

View File

@ -1,21 +0,0 @@
<template>
<div class="Content">
<div class="container">
<h1 class="Content__Title">{{ $t('about.title') }}</h1>
<p>{{ $t('about.introduction') }}</p>
</div>
</div>
</template>
<style>
.Content
{
padding: 50px 0;
text-align: center;
}
.Content__Title
{
font-weight: 300;
padding-bottom: 30px;
}
</style>

View File

@ -1,21 +0,0 @@
<template>
<div class="Content">
<div class="container">
<h1 class="Content__Title">{{ $t('home.title') }}</h1>
<p>{{ $t('home.introduction') }}</p>
</div>
</div>
</template>
<style>
.Content
{
padding: 50px 0;
text-align: center;
}
.Content__Title
{
font-weight: 300;
padding-bottom: 30px;
}
</style>

View File

@ -1,9 +0,0 @@
import axios from 'axios'
let options = {}
// The server-side needs a full url to works
if (process.SERVER_BUILD) {
options.baseURL = `http://${process.env.HOST || 'localhost'}:${process.env.PORT || 3000}`
}
export default axios.create(options)

View File

@ -1,12 +1,16 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import store from '~store'
Vue.prototype.$t = function (key) {
const state = store.state.lang
let keys = key.split('.')
let value = state._[state.lang]
keys.forEach((k) => {
value = value[k]
})
return value
}
Vue.use(VueI18n)
const i18n = new VueI18n({
locale: store.state.locale,
fallbackLocale: 'en',
messages: {
'en': require('~/locales/en.json'),
'fr': require('~/locales/fr.json')
}
})
export default i18n

View File

@ -1,10 +1,11 @@
export const state = {
locale: 'en-US'
locales: ['en', 'fr'],
locale: 'en'
}
export const mutations = {
SET_LANG (state, locale) {
if (['en-US', 'fr-FR'].indexOf(locale) !== -1) {
if (state.locales.indexOf(locale) !== -1) {
state.locale = locale
}
}

View File

@ -1,27 +0,0 @@
import axios from '~plugins/axios'
export const state = {
locales: ['en', 'fr'], // available langages
lang: null, // current lang
_: {} // store for translations
}
export const mutations = {
SET_LANG (state, lang) {
state.lang = lang
},
SET_TRANSLATION (state, translation) {
state._[state.lang] = translation
}
}
export const actions = {
async setLang ({ state, commit }, lang) {
if (state._[lang]) {
return commit('SET_LANG', lang)
}
let res = await axios.get(`/locales/${lang}.json`)
commit('SET_LANG', lang)
commit('SET_TRANSLATION', res.data)
}
}

View File

@ -1,3 +0,0 @@
# i18n with Nuxt.js
https://nuxtjs.org/examples/i18n

View File

@ -1,100 +0,0 @@
<template>
<div>
<header class="Header">
<div class="container">
<h1 class="Header__Title">Nuxt i18n</h1>
<nav class="Header__Menu">
<nuxt-link class="Header__Link" :to="path('/')" exact>
{{ $t('links.home') }}
</nuxt-link>
<nuxt-link class="Header__Link" :to="path('/about')">
{{ $t('links.about') }}
</nuxt-link>
<nuxt-link class="Header__Link" v-if="$i18n.locale === 'en-US'" :to="`/fr-FR` + $route.fullPath" active-class="none">
{{ $t('links.french') }}
</nuxt-link>
<nuxt-link class="Header__Link" v-else :to="$route.fullPath.replace(/^\/[^\/]+/, '')" active-class="none">
{{ $t('links.english') }}
</nuxt-link>
</nav>
</div>
</header>
<nuxt/>
</div>
</template>
<script>
export default {
methods: {
path (url) {
return (this.$i18n.locale === 'en-US' ? url : '/' + this.$i18n.locale + url)
}
}
}
</script>
<style>
html, body
{
background-color: #fff;
color: #2e2f30;
letter-spacing: 0.5px;
font-size: 18px;
font-family: "Source Sans Pro", Arial, sans-serif;
height: 100vh;
margin: 0;
}
*, *:before, *:after
{
padding: 0;
margin: 0;
box-sizing: border-box;
}
.container
{
width: 75%;
margin: 0 auto;
}
.container:after
{
content: "";
display: table;
clear: both;
}
.Header
{
color: #fff;
height: 80px;
line-height: 80px;
background-color: #2e2f30;
}
.Header__Title
{
float: left;
font-weight: 300;
font-size: 30px;
}
.Header__Menu
{
float: right;
}
.Header__Link
{
font-size: 16px;
color: #fff;
border: 1px solid #fff;
padding: 7px 12px;
text-transform: uppercase;
text-decoration: none;
border-radius: 5px;
margin-left: 10px;
}
.Header__Link:hover
{
color: #2e2f30;
background-color: #fff;
}
.nuxt-link-active {
color: cyan;
}
</style>

View File

@ -1,59 +0,0 @@
<template>
<div class="error-page">
<div>
<h1 class="error-code">{{ error.statusCode }}</h1>
<div class="error-wrapper-message">
<h2 class="error-message">{{ error.message }}</h2>
</div>
<p v-if="error.statusCode === 404"><nuxt-link class="error-link" to="/">Back to the home page</nuxt-link></p>
</div>
</div>
</template>
<script>
export default {
props: ['error'],
head () {
return {
title: this.error.message || 'An error occured'
}
}
}
</script>
<style scoped>
.error-page {
color: #000;
font-family: "SF UI Text", "Helvetica Neue", "Lucida Grande";
text-align: center;
padding-top: 20%;
}
.error-code {
display: inline-block;
font-size: 24px;
font-weight: 500;
vertical-align: top;
border-right: 1px solid rgba(0, 0, 0, 0.298039);
margin: 0px 20px 0px 0px;
padding: 10px 23px;
}
.error-wrapper-message {
display: inline-block;
text-align: left;
line-height: 49px;
height: 49px;
vertical-align: middle;
margin-bottom: 20px;
}
.error-message {
font-size: 14px;
font-weight: normal;
margin: 0px;
padding: 0px;
}
.error-link {
color: #000;
font-weight: normal;
font-size: 14px;
}
</style>

View File

@ -1,16 +0,0 @@
{
"links": {
"home": "Home",
"about": "About",
"english": "English version",
"french": "French version"
},
"home": {
"title": "Welcome",
"introduction": "This is an introduction in English."
},
"about": {
"title": "About",
"introduction": "This page is made to give you more informations."
}
}

View File

@ -1,16 +0,0 @@
{
"links": {
"home": "Accueil",
"about": "À propos",
"english": "Version Anglaise",
"french": "Version Française"
},
"home": {
"title": "Bienvenue",
"introduction": "Ceci est un texte d'introduction en Français."
},
"about": {
"title": "À propos",
"introduction": "Cette page est faite pour vous donner plus d'informations."
}
}

View File

@ -1,5 +0,0 @@
export default function ({ app, store, params, error }) {
const locale = params.lang || 'en-US'
store.commit('SET_LANG', locale)
app.$i18n.locale = store.state.locale
}

View File

@ -1,14 +0,0 @@
module.exports = {
loading: {
color: 'cyan'
},
router: {
middleware: 'i18n'
},
build: {
vendor: ['vue-i18n']
},
plugins: [
{ src: '~plugins/i18n.js', injectAs: 'i18n' }
]
}

View File

@ -1,12 +0,0 @@
{
"name": "nuxt-i18n",
"dependencies": {
"axios": "^0.15.3",
"nuxt": "latest"
},
"scripts": {
"dev": "nuxt",
"build": "nuxt build",
"start": "nuxt start"
}
}

View File

@ -1,13 +0,0 @@
<template>
<about/>
</template>
<script>
import About from '~/partials/About.vue'
export default {
components: {
About
}
}
</script>

View File

@ -1,13 +0,0 @@
<template>
<home/>
</template>
<script>
import Home from '~/partials/Home.vue'
export default {
components: {
Home
}
}
</script>

View File

@ -1,13 +0,0 @@
<template>
<about/>
</template>
<script>
import About from '~/partials/About.vue'
export default {
components: {
About
}
}
</script>

View File

@ -1,13 +0,0 @@
<template>
<home/>
</template>
<script>
import Home from '~/partials/Home.vue'
export default {
components: {
Home
}
}
</script>

View File

@ -1,21 +0,0 @@
<template>
<div class="Content">
<div class="container">
<h1 class="Content__Title">{{ $t('about.title') }}</h1>
<p>{{ $t('about.introduction') }}</p>
</div>
</div>
</template>
<style>
.Content
{
padding: 50px 0;
text-align: center;
}
.Content__Title
{
font-weight: 300;
padding-bottom: 30px;
}
</style>

View File

@ -1,21 +0,0 @@
<template>
<div class="Content">
<div class="container">
<h1 class="Content__Title">{{ $t('home.title') }}</h1>
<p>{{ $t('home.introduction') }}</p>
</div>
</div>
</template>
<style>
.Content
{
padding: 50px 0;
text-align: center;
}
.Content__Title
{
font-weight: 300;
padding-bottom: 30px;
}
</style>

View File

@ -1,18 +0,0 @@
import Vue from 'vue'
import VueI18n from 'vue-i18n'
import store from '~store'
Vue.use(VueI18n)
console.log(store.state.locale)
const i18n = new VueI18n({
locale: store.state.locale,
fallbackLocale: 'en-US',
messages: {
'en-US': require('~/locales/en-US.json'),
'fr-FR': require('~/locales/fr-FR.json')
}
})
export default i18n