Shortcut and more readable syntax for `v-t`: allow `<h1 v-t>Hello</h1>` which will be similar to `<h1 v-t="Hello"></h1>`
alcalyn opened this issue · comments
Clear and concise description of the problem
I want to translate my string in templates, but some strings are just a single string, no plural nor variables:
<h1>Hello</h1>
Currently I can do either:
<h1 v-t="'Hello'"></h1>
or
<h1>{{ t('Hello') }}</h1>
I expected to have this to work also:
<h1 v-t>Hello</h1>
(would be similar to <h1 v-t="'Hello'"></h1>
)
If no value set for v-t
, just take text from tag content.
Suggested solution
I could make it work in a hacky way, by replacing the directive. It just throw a warning in console, but works:
export const customTrans: TranslationDirective<HTMLElement> = (() => {
const directive = vTDirective(i18n);
const { beforeUpdate: baseBeforeUpdate, created: baseCreated } = directive;
const setValueFromContent = (callback: DirectiveHook<HTMLElement> | undefined) => (el: HTMLElement, binding: DirectiveBinding, vnode: VNode<unknown, HTMLElement>) => {
if (undefined === callback) {
return;
}
if (!binding.value) {
binding.value = el.innerText;
}
callback(el, binding, vnode, null);
};
directive.beforeUpdate = setValueFromContent(baseBeforeUpdate);
directive.created = setValueFromContent(baseCreated);
return directive;
})();
app.directive('t', customTrans);
This way I just need to add v-t
in "simple" strings to translate.
Alternative
No response
Additional context
No response
Validations
- Read the Contributing Guidelines
- Read the Documentation
- Check that there isn't already an issue that request the same feature to avoid creating a duplicate.
I think such directive usage wouldn't work during SSR, as only the directive binding will be accessible as the node/element does not exist yet, this isn't very clearly described in the Vue docs.
Yeah I had also ssr in mind, I don't use it but I guess it is also a friction point for this
Here is a better version with no warning, and insteadn I use another directive name to prevent trying to override:
<h1 v-t="'Games'"></h1>
=>
<h1 v-trans>Games</h1>
<h3>{{ $t('Online ({n})', { n: 3 }) }}</h3>
=>
<h3 v-trans="{ n: 3 }">Online ({n})</h3>
import { TranslationDirective, vTDirective } from 'vue-i18n';
import { i18n } from '.';
import { DirectiveBinding, DirectiveHook, VNode } from 'vue';
/**
* Allow a shortcut to translate in a more readable way:
*
* - Currently:
* `<h1 v-t="'Games'"></h1>`
* - With this custom directive:
* `<h1 v-trans>Games</h1>`
*
* or
*
* - Currently:
* `<h3>{{ $t('Online ({n})', { n: 3 }) }}</h3>`
* - With this custom directive:
* `<h3 v-trans="{ n: 3 }">Online ({n})</h3>`
*
* https://github.com/intlify/vue-i18n-next/issues/1820
*/
export const customTrans: TranslationDirective<HTMLElement> = (() => {
const setValueFromContent = (callback: DirectiveHook<HTMLElement> | undefined) => (el: HTMLElement, binding: DirectiveBinding, vnode: VNode<unknown, HTMLElement>) => {
if (undefined === callback) {
return;
}
let path: null | string = null;
if (undefined !== el.dataset.baseTranslationKey) {
path = el.dataset.baseTranslationKey;
} else {
path = el.innerText;
el.dataset.baseTranslationKey = path;
}
if (!binding.value) {
binding.value = path;
} else if ('object' === typeof binding.value) {
binding.value = {
path,
args: binding.value,
};
} else {
throw new Error(`Expected either v-trans="'string'" or v-trans="{ key: value }" for element containing "${el.innerText}"`);
}
callback(el, binding, vnode, null);
};
const directive = vTDirective(i18n);
directive.beforeUpdate = setValueFromContent(directive.beforeUpdate);
directive.created = setValueFromContent(directive.created);
return directive;
})();
but actually I still hesitate using this, or use proper translation keys <p v-trans="{ date: ... }">game.created_at</p>
which lower the benefit of this feature