Created
June 5, 2021 22:30
-
-
Save adrai/a2587dbc9f9e87d05881f55fa35aaae3 to your computer and use it in GitHub Desktop.
vue-i18next for vue v3
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
const i18nextPromise = i18next.use(i18nextHttpBackend).init({ | |
debug: true, | |
lng: 'en', | |
fallbackLng: 'en', | |
backend: { | |
loadPath: './locales/{{lng}}/{{ns}}.json' | |
} | |
}); | |
const app = Vue.createApp({}); | |
const i18n = VueI18next.createI18n(i18next); | |
app.use(i18n); | |
app.component('app', { | |
// used in combination with Suspense. | |
// useful when translations are not in-memory... | |
async setup() { | |
console.log('i18next.isInitialized', i18next.isInitialized) | |
await i18nextPromise; | |
return {}; | |
}, | |
template: `<div> | |
<div> | |
<h3>Translation</h3> | |
<language-changer></language-changer> | |
<p>$t: {{ $t("message.hello") }}</p> | |
</div> | |
<div> | |
<h3>Interpolation</h3> | |
<i18next i18nKey="term" tag="label" for="tos"> | |
<a href="#" target="_blank">{{ $t("tos") }}</a> | |
</i18next> | |
</div> | |
<div> | |
<h3>Directive</h3> | |
<with-directive></with-directive> | |
</div> | |
</div>` | |
}); | |
app.component('language-changer', { | |
template: ` | |
<div> | |
<a v-if="$i18n.language === 'en'" v-on:click="changeLanguage('de')"> | |
DE | |
</a> | |
<strong v-if="$i18n.language === 'de'"> | |
DE | |
</strong> | |
| | |
<a v-if="$i18n.language === 'de'" v-on:click="changeLanguage('en')"> | |
EN | |
</a> | |
<strong v-if="$i18n.language === 'en'"> | |
EN | |
</strong> | |
</div>`, | |
methods: { | |
changeLanguage(lang) { | |
this.$i18n.changeLanguage(lang); | |
}, | |
}, | |
}); | |
app.component('with-directive', { | |
template: ` | |
<div v-t="{key:'message.hello'}"></div> | |
<div v-t="'message.hello'"></div>`, | |
}); | |
app.mount("#app"); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
<!DOCTYPE html> | |
<html lang="en"> | |
<head> | |
<meta charset="UTF-8"> | |
<title>vue-i18next Example</title> | |
<script src="https://unpkg.com/vue@3.0.11"></script> | |
<script src="https://unpkg.com/i18next@20.3.1/i18next.js"></script> | |
<script src="./vueI18next.js"></script> | |
<script src="https://cdn.jsdelivr.net/gh/i18next/i18next-http-backend/i18nextHttpBackend.js"></script> | |
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css" integrity="sha384-BVYiiSIFeK1dGmJRAkycuHAHRg32OmUcww7on3RYdg4Va+PmSTsz/K68vbdEjh4u" crossorigin="anonymous"> | |
<style media="screen"> | |
body { | |
height: 100%; | |
} | |
#app { | |
max-width: 800px; | |
margin: 40px; | |
} | |
</style> | |
</head> | |
<body> | |
<div id="app"> | |
<Suspense> | |
<template #default> | |
<app /> | |
</template> | |
<template #fallback> | |
<span>Loading...</span> | |
</template> | |
</Suspense> | |
</div> | |
<script src="app.js"></script> | |
</body> | |
</html> |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"message": { | |
"hello": "Hallo!! - DE" | |
}, | |
"tos": "Nutzungsbedingungen", | |
"term": "Ich akzeptiere die {{0}}.", | |
"loadbundle": "Bundle Laden {{lang}}" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
{ | |
"message": { | |
"hello": "Hello!! - EN" | |
}, | |
"tos": "Term of Service", | |
"term": "I accept the {{0}}.", | |
"loadbundle": "Load Bundle {{lang}}" | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(function (global, factory) { | |
typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() : | |
typeof define === 'function' && define.amd ? define(factory) : | |
(global.VueI18next = factory()); | |
}(this, (function () { 'use strict'; | |
function parseValue(value) { | |
if (typeof value === 'string') { | |
return { key: value }; | |
} else if (typeof value === 'object') { | |
if (!value.key && value.path) { | |
value.key = value.path; | |
delete value.path; | |
} | |
if (!value.key) { | |
throw new Error('no key in value'); | |
} | |
return value; | |
} else { | |
throw new Error(); | |
} | |
} | |
function getInterpolateArg( | |
{ slots }, | |
keys | |
) { | |
if (keys.length === 1 && keys[0] === 'default') { | |
// default slot only | |
return slots.default ? slots.default() : [] | |
} else { | |
// named slots | |
return keys.reduce((arg, key) => { | |
const slot = slots[key] | |
if (slot) { | |
arg[key] = slot() | |
} | |
return arg | |
}, {}) | |
} | |
} | |
const VueI18next = { | |
createI18n: (i18next) => ({ | |
install: (app, options = {}) => { | |
options.bindI18n = options.bindI18n || 'languageChanged loaded'; | |
options.bindStore = options.bindStore || 'added removed'; | |
// add some reactivity... | |
app.mixin({ | |
created() { | |
if (options.bindI18n) { | |
i18next.on(options.bindI18n, () => this.$forceUpdate()); | |
} | |
if (options.bindStore && i18next.store) { | |
i18next.store.on(options.bindStore, () => this.$forceUpdate()); | |
} | |
} | |
}); | |
// install globalProperties | |
app.config.globalProperties.$i18n = i18next; | |
app.config.globalProperties.$t = (...args) => i18next.t.apply(i18next, args); | |
// install directive | |
const bind = (el, { instance, value, modifiers }) => { | |
const parsedValue = parseValue(value); | |
el.textContent = i18next.t(parsedValue.key, parsedValue); | |
}; | |
app.directive('t', { | |
beforeMount: bind, | |
beforeUpdate: bind | |
}); | |
// install component | |
app.component('i18next', { | |
props: { | |
tag: { | |
type: String, | |
default: 'span' | |
}, | |
i18nKey: { | |
type: String, | |
required: true | |
}, | |
options: { | |
type: Object | |
} | |
}, | |
setup(props, context) { | |
return () => { | |
const { slots, attrs } = context; | |
const keys = Object.keys(slots).filter(key => key !== '_') | |
const children = getInterpolateArg(context, keys) | |
const key = props.i18nKey; | |
const tag = props.tag; | |
const REGEXP = i18next.services.interpolator.regexp; | |
const i18nextOptions = { | |
...(props.options || {}), | |
interpolation: { prefix: '#$?', suffix: '?$#' } | |
}; | |
const format = i18next.t(key, i18nextOptions); | |
const tchildren = []; | |
format.split(REGEXP).reduce((memo, match, index) => { | |
let child; | |
if (index % 2 === 0) { | |
if (match.length === 0) return memo; | |
child = match; | |
} else { | |
const place = match.trim(); | |
// eslint-disable-next-line no-restricted-globals | |
if (isNaN(parseFloat(place)) || !isFinite(place)) { | |
children.forEach(e => { | |
if ( | |
!child && | |
e.data.attrs && | |
e.data.attrs.place && | |
e.data.attrs.place === place | |
) { | |
child = e; | |
} | |
}); | |
} else { | |
child = children[parseInt(match, 10)]; | |
} | |
} | |
memo.push(child); | |
return memo; | |
}, tchildren); | |
return Vue.h(tag, attrs, tchildren); | |
}; | |
} | |
}) | |
} | |
}) | |
}; | |
return VueI18next; | |
}))); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment