intlify / vue-i18n

Vue I18n for Vue 3

Home Page:https://vue-i18n.intlify.dev/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

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

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