9

I am using vue-i18n for localization. When I want to translate input placeholders, as in:

<input type="text" v-model="someValue" :placeholder="$t('translation.string')">

I have to use the $t() function which is executed on every re-render (library docs mention is as well). This adds thousands of unnecessary function calls in my simple booking form, which I'd like to avoid.

Is there a way to bind the attribute only once? The translated value does not change throughout the lifecycle of Vue instance. v-once is not what I am looking for, since I want to keep the component/node reactive, and only 'hardcode' the attribute.

I know I could achieve what I need by simply storing the translated strings in the data object, but I'd like to know if there is an alternative, simpler solution (not requiring massive code duplication).

Amade
  • 3,665
  • 2
  • 26
  • 54
  • 2
    Create a data property which you initialize with your translation, then pass that to `:placeholder`. – connexo Mar 01 '18 at 16:06
  • Thanks for your comment. Is there any other way? I mentioned in my question that I am looking for solutions not involving storing the translated values in the data object. – Amade Mar 01 '18 at 16:12
  • Yes, with mustache syntax it doesn't even compile - I get the `Interpolation inside attributes has been removed.` error. – Amade Mar 01 '18 at 16:17
  • `v-t` directive works only with text content, not with attributes, unfortunately. I thought about somehow getting the value out of where the directive stores it, but couldn't find a way :/ – Amade Mar 01 '18 at 16:18
  • That documentation link went the way of the dodo, here's the updated link: . – zcoop98 May 21 '21 at 19:12

1 Answers1

2

A computed property will do what you're looking for, since they only trigger a re-run when their dependencies change. Since this.$t('LOCALE.STRING') doesn't change unless your locale changes, you're guaranteed only a single run, after which the value will be cached by Vue for subsequent renders.

<template>
  ...
  <input
    ...
    :placeholder="translatedPlaceholder"
  >
  ...
</template>

<script>
  ...
  computed: {
    translatedPlaceholder() {
      return $t('translation.string');
    },
  },
  ...
</script>

The awesome part about this solution is that if the locale does change, then Vue will indeed refresh the computed property, updating it to the correct value.


I've put together an interactive snippet to help demonstrate this syntax, if you're looking for a more extensive example.

The snippet includes a simple localized greeting followed by a random number in a <p> tag, with two buttons. The localized string in the text is pulled from a computed property.

The first button will generate a new number, causing the <p> to re-render in the DOM.
The second button will switch the locale, causing Vue-i18n to refresh the localized string.

Whenever the computed localization property is re-executed, it will log to console.
I also set the script up to log to console whenever Vue updates the DOM too.

const messages = {
  en: {
    "greeting": 'Hello!',
  },
  es: {
    "greeting": '¡Hola!',
  },
};

new Vue({ 
  i18n: new VueI18n({
    locale: 'en',
    messages,
  }),
  data() {
    return {
      num: 1,
    }
  },
  computed: {
    localizedGreeting() {
      console.log('Computed executed');
      return this.$t('greeting');
    },
  },
  methods: {
    swapLocale() {
      this.$i18n.locale = (this.$i18n.locale == 'en' ? 'es' : 'en');
    },
    randomNum() {
      this.num = Math.floor(Math.random() * 10000);
    },
  },
  updated() {
    console.log('DOM updated');
  },
}).$mount('#app')
.as-console-wrapper {
  max-height: 120px !important;
}
<script src="https://unpkg.com/vue@2/dist/vue.min.js"></script>
<script src="https://unpkg.com/vue-i18n@8"></script>

<div id="app">
  <p>{{ `${localizedGreeting} #${num}` }}</p>
  <button @click="randomNum()">Re-render Greeting</button>
  <button @click="swapLocale">Swap Greeting Locale</button>
</div>

As you can see, re-rendering doesn't cause the computed property to re-execute, but swapping the locale does, which is exactly what we're looking for here.


As a final note– while there is technically a performance hit in your original code, since you're re-executing $t() calls, it's worth bearing in mind that the actual performance hit is probably tiny. Don't trade simplicity for performance gains unless it really makes sense.

Remember, premature optimization is the root of all evil!

tony19
  • 125,647
  • 18
  • 229
  • 307
zcoop98
  • 2,590
  • 1
  • 18
  • 31