Skip to content
This repository has been archived by the owner on May 19, 2022. It is now read-only.

Commit

Permalink
Merge pull request #41 from panter/features/wait
Browse files Browse the repository at this point in the history
Features/wait
  • Loading branch information
claudiocro authored Sep 24, 2018
2 parents 7ef21d6 + cc33965 commit d141a7f
Show file tree
Hide file tree
Showing 7 changed files with 370 additions and 54 deletions.
31 changes: 30 additions & 1 deletion docs/guide/directive.md
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
# Directive
# Directives

## v-t

Full Featured properties:

Expand Down Expand Up @@ -33,3 +35,30 @@ Vue.component("app", {
template: `<p ref="text" v-t="{ path: 'helloWithName', language: 'en', args: { name: 'Hans' } }"></p>`
});
```


## v-waitForT

Wait for the i18next fot be initialized. If not initialized it sets the element to `hidden = true` and wait
for i18next to be initialized.

```javascript
const locales = {
en: {
hello: "Hello"
}
};

i18next.init({
lng: "en",
resources: {
en: { translation: locales.en }
}
});

const i18n = new VueI18next(i18next);

Vue.component("app", {
template: `<p v-waitForT>$t("hello")</p>`
});
```
45 changes: 27 additions & 18 deletions examples/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,28 +31,32 @@ const i18n = new VueI18next(i18next);

Vue.component('app', {
template: `
<div>
<div>
<div>
<h3>Translation</h3>
<language-changer></language-changer><load-bundle></load-bundle>
<p>$t: {{ $t("message.hello") }}</p>
</div>
<div>
<h3>Interpolation</h3>
<i18next path="term" tag="label" for="tos">
<a href="#" target="_blank">{{ $t("tos") }}</a>
<strong>a</strong>
</i18next>
</div>
<div>
<h3>Prefix</h3>
<key-prefix></key-prefix>
</div>
<div>
<h3>Interpolation</h3>
<inline-translations></inline-translations>
</div>
</div>`,
</div>
<div>
<h3>Interpolation</h3>
<i18next path="term" tag="label" for="tos">
<a href="#" target="_blank">{{ $t("tos") }}</a>
<strong>a</strong>
</i18next>
</div>
<div>
<h3>Prefix</h3>
<key-prefix></key-prefix>
</div>
<div>
<h3>Inline translations</h3>
<inline-translations></inline-translations>
</div>
<div>
<h3>Directive</h3>
<with-directive></with-directive>
</div>
</div>`,
});

Vue.component('language-changer', {
Expand Down Expand Up @@ -113,6 +117,11 @@ Vue.component('inline-translations', {
</div>`,
});

Vue.component('with-directive', {
template: `
<div v-t="{path:'message.hello'}"></div>`,
});

new Vue({
i18n,
}).$mount('#app');
5 changes: 5 additions & 0 deletions src/directive.js
Original file line number Diff line number Diff line change
Expand Up @@ -86,3 +86,8 @@ export function update(el, binding, vnode, oldVNode) {

t(el, binding, vnode);
}

export default {
bind,
update,
};
77 changes: 42 additions & 35 deletions src/install.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
/* eslint-disable import/no-mutable-exports */
import deepmerge from 'deepmerge';
import component from './component';
import { bind, update } from './directive';
import directive from './directive';
import waitDirective from './wait';

export let Vue;

Expand All @@ -15,10 +16,10 @@ export function install(_Vue) {

const getByKey = (i18nOptions, i18nextOptions) => (key) => {
if (
i18nOptions &&
i18nOptions.keyPrefix &&
!key.includes(i18nextOptions.nsSeparator)
) {
i18nOptions &&
i18nOptions.keyPrefix &&
!key.includes(i18nextOptions.nsSeparator)
) {
return `${i18nOptions.keyPrefix}.${key}`;
}
return key;
Expand All @@ -39,38 +40,18 @@ export function install(_Vue) {
};

Vue.mixin({
computed: {
$t() {
const getKey = getByKey(
this._i18nOptions,
this.$i18n ? this.$i18n.i18next.options : {},
);

if (this._i18nOptions && this._i18nOptions.namespaces) {
const { lng, namespaces } = this._i18nOptions;

const fixedT = this.$i18n.i18next.getFixedT(lng, namespaces);
return (key, options) =>
fixedT(getKey(key), options, this.$i18n.i18nLoadedAt);
}

return (key, options) =>
this.$i18n.i18next.t(getKey(key), options, this.$i18n.i18nLoadedAt);
},
},

beforeCreate() {
const options = this.$options;
if (options.i18n) {
this.$i18n = options.i18n;
this._i18n = options.i18n;
} else if (options.parent && options.parent.$i18n) {
this.$i18n = options.parent.$i18n;
this._i18n = options.parent.$i18n;
}
let inlineTranslations = {};

if (this.$i18n) {
if (this._i18n) {
const getNamespace =
this.$i18n.options.getComponentNamespace || getComponentNamespace;
this._i18n.options.getComponentNamespace || getComponentNamespace;
const { namespace, loadNamespace } = getNamespace(this);

if (options.__i18n) {
Expand All @@ -89,7 +70,7 @@ export function install(_Vue) {
messages,
} = this.$options.i18nOptions;
let { namespaces } = this.$options.i18nOptions;
namespaces = namespaces || this.$i18n.i18next.options.defaultNS;
namespaces = namespaces || this._i18n.i18next.options.defaultNS;

if (typeof namespaces === 'string') namespaces = [namespaces];
const namespacesToLoad = namespaces.concat([namespace]);
Expand All @@ -99,7 +80,7 @@ export function install(_Vue) {
}

this._i18nOptions = { lng, namespaces: namespacesToLoad, keyPrefix };
this.$i18n.i18next.loadNamespaces(namespaces);
this._i18n.i18next.loadNamespaces(namespaces);
} else if (options.parent && options.parent._i18nOptions) {
this._i18nOptions = { ...options.parent._i18nOptions };
this._i18nOptions.namespaces = [
Expand All @@ -110,13 +91,13 @@ export function install(_Vue) {
this._i18nOptions = { namespaces: [namespace] };
}

if (loadNamespace && this.$i18n.options.loadComponentNamespace) {
this.$i18n.i18next.loadNamespaces([namespace]);
if (loadNamespace && this._i18n.options.loadComponentNamespace) {
this._i18n.i18next.loadNamespaces([namespace]);
}

const languages = Object.keys(inlineTranslations);
languages.forEach((lang) => {
this.$i18n.i18next.addResourceBundle(
this._i18n.i18next.addResourceBundle(
lang,
namespace,
{ ...inlineTranslations[lang] },
Expand All @@ -125,9 +106,35 @@ export function install(_Vue) {
);
});
}

const getKey = getByKey(
this._i18nOptions,
this._i18n ? this._i18n.i18next.options : {},
);

if (this._i18nOptions && this._i18nOptions.namespaces) {
const { lng, namespaces } = this._i18nOptions;

const fixedT = this._i18n.i18next.getFixedT(lng, namespaces);
this._getI18nKey = (key, i18nextOptions) =>
fixedT(getKey(key), i18nextOptions, this._i18n.i18nLoadedAt);
} else {
this._getI18nKey = (key, i18nextOptions) =>
this._i18n.t(getKey(key), i18nextOptions, this._i18n.i18nLoadedAt);
}
},
});

// extend Vue.js
Object.defineProperty(Vue.prototype, '$i18n', {
get() { return this._i18n; },
});

Vue.prototype.$t = function t(key, options) {
return this._getI18nKey(key, options);
};

Vue.component(component.name, component);
Vue.directive('t', { bind, update });
Vue.directive('t', directive);
Vue.directive('waitForT', waitDirective);
}
51 changes: 51 additions & 0 deletions src/wait.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
/* eslint-disable no-param-reassign, no-unused-vars */

import { warn } from './utils';

function assert(vnode) {
const vm = vnode.context;

if (!vm.$i18n) {
warn('No VueI18Next instance found in the Vue instance');
return false;
}

return true;
}

function waitForIt(el, vnode) {
if (vnode.context.$i18n.i18next.isInitialized) {
el.hidden = false;
} else {
el.hidden = true;
const initialized = () => {
vnode.context.$forceUpdate();
// due to emitter removing issue in i18next we need to delay remove
setTimeout(() => {
if (vnode.context && vnode.context.$i18n) {
vnode.context.$i18n.i18next.off('initialized', initialized);
}
}, 1000);
};
vnode.context.$i18n.i18next.on('initialized', initialized);
}
}

export function bind(el, binding, vnode) {
if (!assert(vnode)) {
return;
}

waitForIt(el, vnode);
}

export function update(el, binding, vnode, oldVNode) {
if (vnode.context.$i18n.i18next.isInitialized) {
el.hidden = false;
}
}

export default {
bind,
update,
};
9 changes: 9 additions & 0 deletions test/unit/component.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,15 @@ describe('Components with backend', () => {

expect(root.textContent).to.equal('dev__common__test');
});

it('should wait for translation to be ready', async () => {
const root = vm.$refs.hello;
expect(root.textContent).to.equal('key1');
backend.flush();
await nextTick();

expect(root.textContent).to.equal('dev__common__test');
});
});

describe('Nested namespaces', () => {
Expand Down
Loading

0 comments on commit d141a7f

Please sign in to comment.