Categories
Vue

Vue-MultiSelect — Tags and Templates

A dropdown is often something we want to add in Vue apps.

To make our lives easier, we can use a component library to add it.

In this article, we’ll look at how to create a dropdown with Vue-MultiSelect with tags and templates.

Tagging

We can make dropdowns taggable with the taggable prop.

Also, we can add a tag placeholder with the tag-placeholder prop.

The tag-position can change the position of the tag.

For example, we can write:

<template>
  <div id="app">
    <multiselect
      v-model="value"
      taggable
      multiple
      tag-placeholder="Add this as new tag"
      tag-position="bottom"
      :options="options"
      @tag="addTag"
    ></multiselect>
  </div>
</template>

<script> export default { data() { return { value: null, options: ["Vue.js", "React", "Rails"] }; }, methods: { addTag(newTag) { this.options.push(newTag); } } }; </script>


We have the `tag` event handler, which is set to the `addTag` function.

The `newTag` parameter has the new tag.

We see the ‘Add this as new tag’ message if we type in something that’s not a choice already on the list.

After we press enter or click on the message, we can select our new option as well.

### Custom Option Template

We can display the options our way.

For example, we can write:

<template> <div id="app"> <multiselect v-model="value" :options="options"> <template slot="singleLabel" slot-scope="props"> <span>{{ props.option.name }}</span> </template> <template slot="option" slot-scope="props"> <span>{{ props.option.name }} – {{ props.option.lang }}</span> </template> </multiselect> </div> </template>

<script>
export default {
  data() {
    return {
      value: null,
      options: [
        { name: "Vue.js", lang: "JavaScript" },
        { name: "React", lang: "JavaScript" },
        { name: "Rails", lang: "Ruby" }
      ]
    };
  }
};
</script>
```

In the dropdown, we see both the `name` and `lang` values because we populate the `option` slot with both.

However, when we select an option, we only see the `name` value because we populated the `singleLabel` slot with it.

### Option Groups

We can add option groups to our dropdown.

For example, we can write:

```
<template>
  <div id="app">
    <multiselect
      v-model="value"
      :options="options"
      group-values="libs"
      group-label="language"
      track-by="name"
      group-select
      :custom-label="customLabel"
    >
      <template slot="noResult">
        <span>no result found.</span>
      </template>
    </multiselect>
  </div>
</template>

<script> export default { data() { return { value: null, options: [ { language: "Javascript", libs: [ { name: "Vue.js", category: "Front-end" }, { name: "React", category: "Backend" } ] }, { language: "Ruby", libs: [ { name: "Rails", category: "Backend" }, { name: "Sinatra", category: "Backend" } ] }, { language: "Other", libs: [{ name: "Laravel", category: "Backend" }] } ] }; }, methods: { customLabel({ name, category }) { return ${name} – ${category}; } } }; </script>


We can select the whole group with the `group-select` prop.

`group-label` lets us select the field to display as the group label.

`groyup-values` lets us select the fields for the groups.

`options` has all the options.

`custom-label` has the function for displaying a custom label for each entry.

### Custom Configuration

We can add custom configuration for our `multiselect` component.

`max-height` sets its height to whatever we want in pixels.

`max` sets the maximum number of selections.

`allow-empty` doesn’t allow the user to remove the last option if it exists if it’s `false` .

`block-keys` are the keys we want to disable for triggering behavior.

`close` lets us pass in an event handler that runs when the dropdown is closing.

For instance, we can write:

<template> <div id="app"> <multiselect v-model="value" multiple :max="3" :options="options"></multiselect> </div> </template>

<script>
export default {
  data() {
    return {
      value: null,
      options: ["foo", "bar", "baz", "qux", "apple"]
    };
  }
};
</script>
```

Then up to 3 choices can be selected.

Otherwise, we’ll see an error message.

### Events

The `multiselect` component emits various events.

`input` is emitted when the selected value changes.

`select` is emitted when the user selects an option.

`remove` is emitted after removing an option.

`SearchChangew` is emitted when the search query changes.

`tag` is emitted when a user tries to add a tag.

`open` is emitted when the dropdown opens.

`close` is emitted when the dropdown closes.

### Conclusion

Vue-Multiselect is very flexible.

We can change many options like the max height, how many options we can select, and much more.

We can also add tags and change how options are displayed by populating slots.
Categories
Vue

vue-i18n — Directive and HTML

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to create multilingual Vue apps with vue-i18n.

Custom Directive Localization

We can use the v-t directive for getting translations.

For instance, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    hello: "hi"
  },
  fr: {
    hello: "bonjour"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

App.vue

<template>
  <div id="app">
    <p v-t="'hello'"></p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We just put the v-t directive in our template and we get the translation.

So bonjour is displayed on the screen.

We can also write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    hello: "hi {name}"
  },
  fr: {
    hello: "bonjour {name}"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

App.vue

<template>
  <div id="app">
    <p v-t="{ path: 'hello', args: { name: nickName } }"></p>
  </div>
</template>

<script>
export default {
  name: "App",
  computed: {
    nickName() {
      return "foo";
    }
  }
};
</script>

to interpolate translation.

The path has the path to the message, args has dynamic arguments.

Now we get bonjour foo displayed on the screen.

Use with Transitions

We can use the v-t directive with transitions.

For instance, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    hello: "hi"
  },
  fr: {
    hello: "bonjour"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

App.vue

<template>
  <div id="app">
    <button @click="toggle = !toggle">Toggle</button>
    <transition name="fade">
      <p v-t="'hello'" v-if="toggle"></p>
    </transition>
  </div>
</template>

<script>
export default {
  name: "App",
  data() {
    return {
      toggle: true
    };
  }
};
</script>

<style>
.fade-enter-active,
.fade-leave-active {
  transition: opacity 0.5s;
}
.fade-enter,
.fade-leave-to {
  opacity: 0;
}
</style>

Then we get a transition effect thanks to the transition component.

$t vs v-t

$t is more flexible in that it can be used in component code and template.

However, it’s run every time a re-render occurs, which means it’s slower.

v-t has better performance than $t because of caching.

It’s also can be used for pre-translation with vue-i18n-extensions .

However, it isn’t as flexible as $t and it’s complex.

Component Interpolation

To add translations with HTML, we may want to put them ibn between our tags:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    hello: "hi",
    world: "world"
  },
  fr: {
    hello: "bonjour",
    world: "monde"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

App.vue

<template>
  <div id="app">
    <p>
      {{ $t('hello') }}
      <b>{{ $t('world') }}</b>
    </p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

However, this is too inflexible.

We can use the i18n component to avoid this.

For instance, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    hello: "hi {0}",
    world: "world"
  },
  fr: {
    hello: "bonjour {0}",
    world: "monde"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

App.vue

<template>
  <div id="app">
    <i18n path="hello" tag="p" for="world">
      <b>{{ $t('world') }}</b>
    </i18n>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We have the {0} placeholder to interpolate what we have inside i18n into the tags.

Then we see the 2nd word in bold.

Conclusion

We can put HTML formatted text in the i18n component.

Also, we can use the v-t directive as an alternative to $t .

It’s faster and works with transitions.

Categories
Vue

vue-i18n — Fallback Localization and Local Translations

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to create multilingual Vue apps with vue-i18n.

Fallback Localization

We can set a fallbackLocale in case the string isn’t available.

vue-18n has implicit fallback locale.

A more specific locale would fall back to the more general of the same kind.

For example, de-DE would fall back to de .

We can also specify fallback locales explicitly using the fallbackLocale property.

For instances, we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    foo: "hello"
  },
  fr: {}
};

const i18n = new VueI18n({
  messages,
  locale: "fr",
  fallbackLocale: "en"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

Then we write:

<template>
  <div id="app">
    <p>{{ $t('foo') }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Then we see hello displayed because our fallback locale is en .

fallbackLocale can also be an array, so we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    foo: "hello"
  },
  fr: {},
  ja: {}
};

const i18n = new VueI18n({
  messages,
  locale: "fr",
  fallbackLocale: ["fr", "en"]
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

It can also be an object with a list of fallback locales.

The key is the locale and the value is its fallback.

For example, we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    foo: "hello"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr",
  fallbackLocale: {
    "de-CH": ["fr", "it"],
    default: ["en", "da"]
  }
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

Then de-CH falls back to fr or it . If those aren’t available, then it falls back to en or da .

Fallback Interpolation

Fallback locales can have their own interpolation.

We’ve to put the interpolation in the key.

For example, we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    "hello {name}": "hello {name}"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

Then in our template, we write:

<template>
  <div id="app">
    <p>{{ $t('hello {name}', { name: 'james' }) }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Component-Based Localization

We can localization that’s only in a component.

For instance, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    "hello {name}": "hello {name}"
  }
};

const i18n = new VueI18n({
  messages,
  locale: "fr"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

App.vue

<template>
  <div id="app">
    <p>{{ $t('message.welcome') }}</p>
  </div>
</template>

<script>
export default {
  name: "App",
  i18n: {
    messages: {
      en: { message: { welcome: "welcome" } },
      fr: { message: { welcome: "bienvenu" } }
    }
  }
};
</script>

We added an i18n prooperty in our component to provide local translations.

The messages property has the translations.

Then we define the translations as usual.

Since locale is set to 'fr' , we get bienvenu on the screen.

These translations can be saved in another module and imported.

For instance, we can write:

messages.js

export default {
  en: { message: { welcome: "welcome" } },
  fr: { message: { welcome: "bienvenu" } }
};

App.vue

<template>
  <div id="app">
    <p>{{ $t('message.welcome') }}</p>
  </div>
</template>

<script>
import messages from "./messages";
export default {
  name: "App",
  i18n: {
    messages
  }
};
</script>

We can use parent.$t to get translations in a function component.

For instance, we can write:

<div>{{ parent.$t('message.hello') }}</div>

to get the translation in a function component.

Conclusion

We can get translations in various ways.

One way is to get them from fallback locales.

We can define them as objects, arrays, or strings.

Also, we can have translations that are exclusive to some components.

Categories
Vue

vue-i18n — Formatting Text and Numbers

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to create multilingual Vue apps with vue-i18n.

Localizing Numbers

We can display numbers in the locale we want with vue-i18n.

To do that, we add the format that we want:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const numberFormats = {
  "en-ca": {
    currency: {
      style: "currency",
      currency: "CAD"
    }
  },
  fr: {
    currency: {
      style: "currency",
      currency: "EUR",
      currencyDisplay: "symbol"
    }
  }
};

const i18n = new VueI18n({
  numberFormats
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

We add the number style with the style property.

currency tells us what currency it is, and currencyDisplay tells how to display tjhe currency value.

Then we can use the $n function in our component:

<template>
  <div id="app">
    <p>{{ $n(100, 'currency', 'fr') }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

to format the number.

Now we get:

100,00 €

on the screen.

Custom Formatting

We can format currencies our way.

The i18n-n component lets us format currencies.

For instance, we can write:

<template>
  <div id="app">
    <i18n-n :value="100" format="currency" locale="fr"></i18n-n>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

value has the currency value. format tells us how to format the number.

locale is the locale.

The component also accepts content in its slots for more customization.

We can add content to the currency slot to format it as a currency:

<template>
  <div id="app">
    <i18n-n :value="100" format="currency" locale="fr">
      <template v-slot:currency="{ currency }">
        <span style="color: red">{{ currency }}</span>
      </template>
    </i18n-n>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We populate the currency slot with our own content.

We add a span into the slot and styled it red.

Now the currency symbol is red.

There’s also an integer slot for the integer part of the number.

group slot has the thousands group.

fraction slot has the fractional part.

Locale Messages Syntax

We can add locale messages in various ways.

For instance, we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    foo: "foo",
    nested: {
      message: "bar"
    },
    messages: [
      "baz",
      {
        internal: "internal message"
      },
      ["qux"]
    ]
  },
  fr: {
    //...
  }
};

const i18n = new VueI18n({
  messages,
  locale: "en"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

then we can write:

<template>
  <div id="app">
    <p>{{ $t('foo') }}</p>
    <p>{{ $t('nested.message') }}</p>
    <p>{{ $t('messages[0]') }}</p>
    <p>{{ $t('messages[1].internal') }}</p>
    <p>{{ $t('messages[2][0]') }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

in our component.

We can have messages in objects, arrays, and we can get them by their usual path with $t .

So we have:

foo

bar

baz

internal message

qux

displayed on the screen.

Linked Locale Messages

Locale messages can also reference other locale messages.

We can do that with @: .

For instance, we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    foo: "foo",
    foobar: "@:foo bar"
  },
  fr: {}
};

const i18n = new VueI18n({
  messages,
  locale: "en"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

App.vue

<template>
  <div id="app">
    <p>{{ $t('foo') }}</p>
    <p>{{ $t('foobar') }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

The @: symbol indicates that we match the message with the key that comes after it.

We can also format the linked messages.

The modifiers can be upper , lower , and capitalize .

For instance, we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);
Vue.config.productionTip = false;

const messages = {
  en: {
    foo: "foo",
    foobar: "@.upper:foo bar"
  },
  fr: {}
};

const i18n = new VueI18n({
  messages,
  locale: "en"
});

new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

to make 'foo' upper case.

Now we get:

foo

FOO bar

displayed on the screen.

Conclusion

There are many ways to format numbers and locale strings with vue-i18n.

Messages can reference other messages. Numbers can be formatted as currencies.

Categories
Vue

Creating Multilingual Apps with vue-i18n

Vue.js is an easy to use web app framework that we can use to develop interactive front end apps.

In this article, we’ll look at how to create multilingual Vue apps with vue-i18n.

Getting Started

To get started, we install the package by writing:

npm i vue-i18n

Then we can write:

main.js

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);

const messages = {
  en: {
    message: {
      hello: "hello"
    }
  },
  fr: {
    message: {
      hello: "bonjour"
    }
  }
};

const i18n = new VueI18n({
  locale: "fr",
  messages
});

Vue.config.productionTip = false;
new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

And we can display the translation by writing:

<template>
  <div id="app">
    <p>{{ $t("message.hello") }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Formatting

We can interpolate expression in our messages by writing:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);

const messages = {
  en: {
    message: {
      hello: "{msg} world"
    }
  },
  fr: {
    message: {
      hello: "bonjour"
    }
  }
};

const i18n = new VueI18n({
  locale: "en",
  messages
});

Vue.config.productionTip = false;
new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

And in our component, we can write:

<template>
  <div id="app">
    <p>{{ $t('message.hello', { msg: 'hello' })  }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Then we get hello world displayed on screen.

List Formatting

We can format lists with vue-i18n.

For example, we can write:

const messages = {
  en: {
    message: {
      hello: "{0} world"
    }
  },
  fr: {
    message: {
      hello: "bonjour"
    }
  }
};

And we can write:

<template>
  <div id="app">
    <p>{{ $t('message.hello', ['hi']) }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

to display hello world .

We can also use an array-like object instead of an array:

<template>
  <div id="app">
    <p>{{ $t('message.hello', {'0': 'hi'}) }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

And we get the same thing.

Ruby on Rails Message Format

The placeholder can also be written in Ruby on Rails format.

For instance, we can write:

const messages = {
  en: {
    message: {
      hello: "%{msg} world"
    }
  },
  fr: {
    message: {
      hello: "bonjour"
    }
  }
};

And we write:

<template>
  <div id="app">
    <p>{{ $t('message.hello', {msg: 'hi'}) }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

Custom Formatter

We can add a custom formatter class:

class CustomFormatter {
  constructor (options) {
    // ...
  }

 interpolate (message, values) {
    return ['resolved message string']
  }
}

Then we can set it in the message that we pass into the VueI18n constructor:

const i18n = new VueI18n({
  locale: 'en',
  formatter: new CustomFormatter(),
  messages: {
    en: {
      // ...
    },
    // ...
  }
})

We just set the formatter property of the object we pass into the constructor.

Pluralization

vue-18n supports pluralization.

To add single and plural words to messages , we add the words to a string separated by the | character.

For instance, we can write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);

const messages = {
  en: {
    apple: "apple | apples",
    orange: "no oranges | one orange | {count} oranges"
  }
};

const i18n = new VueI18n({
  locale: "en",
  messages
});

Vue.config.productionTip = false;
new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

Then we write the following in our component:

<template>
  <div id="app">
    <p>{{ $tc('apple', 1) }}</p>
    <p>{{ $tc('apple', 2) }}</p>

    <p>{{ $tc('orange', 0) }}</p>
    <p>{{ $tc('orange', 1) }}</p>
    <p>{{ $tc('orange', 5, { count: 5 }) }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

And we get:

apple

apples

no oranges

one orange

5 oranges

on the screen.

We used the $tc function instead of the $t function for regular translations.

The placeholder is in the braces.

DateTime Localization

Date and time can also be localized, we just have to pass in an object with the date formats we want to show.

To do that, we write:

import Vue from "vue";
import App from "./App.vue";
import VueI18n from "vue-i18n";

Vue.use(VueI18n);

const dateTimeFormats = {
  en: {
    short: {
      year: "numeric",
      month: "short",
      day: "numeric"
    },
    long: {
      year: "numeric",
      month: "short",
      day: "numeric",
      weekday: "short",
      hour: "numeric",
      minute: "numeric"
    }
  }
};

const i18n = new VueI18n({
  locale: "en",
  dateTimeFormats
});

Vue.config.productionTip = false;
new Vue({
  i18n,
  render: h => h(App)
}).$mount("#app");

Then we can use the $d function as follows:

<template>
  <div id="app">
    <p>{{ $d(new Date(), 'short') }}</p>
    <p>{{ $d(new Date(), 'long', 'en') }}</p>
  </div>
</template>

<script>
export default {
  name: "App"
};
</script>

We pass in the 'long' and 'short' format strings and optionally the language.

Conclusion

We can use vue-i18n to format localize our apps.

Text, single and plural words, and dates can be added for different locales.