Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Shortcut and more readable syntax for v-t: allow <h1 v-t>Hello</h1> which will be similar to <h1 v-t="Hello"></h1> #1820

Open
3 tasks done
alcalyn opened this issue Apr 23, 2024 · 4 comments
Labels
Status: Proposal Request for comments

Comments

@alcalyn
Copy link

alcalyn commented Apr 23, 2024

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

@alcalyn alcalyn added the Status: Proposal Request for comments label Apr 23, 2024
@BobbieGoede
Copy link
Member

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.

@alcalyn
Copy link
Author

alcalyn commented Apr 24, 2024

Yeah I had also ssr in mind, I don't use it but I guess it is also a friction point for this

@kazupon
Copy link
Member

kazupon commented Apr 24, 2024

@alcalyn
Copy link
Author

alcalyn commented May 7, 2024

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Status: Proposal Request for comments
Projects
None yet
Development

No branches or pull requests

3 participants