The @juit/vue-i18n
package provides a minimalistic plugin for VueJS (v. 3)
to support basic internationalization (translations, numbers, and date formats).
It heavily relies on the Intl.Locale
, Intl.NumberFormat
, and
Intl.DateTimeFormat
global objects widely supported by modern browsers.
It also deeply integrates with TypeScript to provide compile-time checking on required translation languages and translation keys.
- Installation
- Configuration
- Usage
- Translating messages
- Formatting numbers
- Formatting dates
- Configuring Types
- Legal Stuff
As usual, install with NPM (or the cool package-manager du jour):
npm install '@juit/vue-i18n'
And add the plugin to your Vue app:
import { createApp } from 'vue'
import { i18n } from '@juit/vue-i18n'
import MyApp from './app.vue'
const app = createApp(MyApp).use(i18n, {
defaultLanguage: 'en',
translations: {
hello: {
en: 'Hello, world!',
de: 'Hallo, Welt!',
},
}
})
The plugin can be configured with a simple string (the defaultLanguage
)
described below, or some options:
defaultLanguage
: the default language to use; all translations should be available in this language.translations
: an object containing the translations for the messages to translate, keyed by its identifier.dateTimeFormats
: date and time format aliases used formatting dates.numberFormats
: number format aliases used formatting numbers.
Date time formatting aliases can be configured keyed by a simple string and
values as Intl.DateTimeFormatOptions
import { createApp } from 'vue'
import { i18n } from '@juit/vue-i18n'
import MyApp from './app.vue'
const app = createApp(MyApp).use(i18n, {
defaultLanguage: 'en',
dateTimeFormats: {
custom: {
year: 'numeric',
month: '2-digit',
day: '2-digit',
},
}
})
The default (overridable) formats are as follows:
{
// used when no alias or date time format is specified
default: { dateStyle: 'medium', timeStyle: 'medium' },
// generic formats
short: { dateStyle: 'short', timeStyle: 'short' },
medium: { dateStyle: 'medium', timeStyle: 'medium' },
long: { dateStyle: 'long', timeStyle: 'long' },
full: { dateStyle: 'full', timeStyle: 'full' },
// date only formats
date: { dateStyle: 'medium' },
shortDate: { dateStyle: 'short' },
mediumDate: { dateStyle: 'medium' },
longDate: { dateStyle: 'long' },
fullDate: { dateStyle: 'full' },
// time only formats
time: { timeStyle: 'medium' },
shortTime: { timeStyle: 'short' },
mediumTime: { timeStyle: 'medium' },
longTime: { timeStyle: 'long' },
fullTime: { timeStyle: 'full' },
}
Similarly to date time, also number formatting aliases can be configured keyed
by a simple string and values as Intl.NumberFormatOptions
import { createApp } from 'vue'
import { i18n } from '@juit/vue-i18n'
import MyApp from './app.vue'
const app = createApp(MyApp).use(i18n, {
defaultLanguage: 'en',
numberFormats: {
speed: {
style: 'unit',
unit: 'kilometer-per-hour',
}
}
})
While there is no intrinsic default, each valid ISO-4217 currency code
(e.g. EUR
, USD
, ...) can be used as an alias.
To configure the default number format use the default
key.
In any component setup()
method, you can use the useTranslator()
function
to get a hold on the Translator
configured for the current app.
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator()
The base function to translate messages is exposed as translator.t(...)
or
(within components) the $t(...)
function.
This function takes a translation key (specified in the configuration phase, see above).
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator()
const hello = translator.t('hello')
// the "hello" string will be "Hello, world!" or "Hallo, Welt!"
It can also take an inline translation:
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator()
const panagram = translator.t({
en: 'The quick fox jumped over the lazy dog',
de: 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'
})
Translations can include parameters by enclosing them in curly braces {param}
.
For example:
const name = 'John Doe'
const string: translator.t({
en: 'Your name is {name}'
de: 'Ihr Name ist {name}'
}, { name })
// This will result in either "Your name is John Doe" or "Ihr Name ist John Doe"
When parameters are numbers, those will be formatted as numbers:
const string: translator.t({
en: 'Score {points} points'
de: 'Punktestand {points} Punkte'
}, { points: 1234.56 })
// This will result in "Score 1,234.56 points" or "Punktestand 1.234,56 Punkte"
The translator supports minimal rules for pluralization by separating
translation messages with the |
(pipe) character.
Messages can contain two variants singular|plural
or three variants
zero|singular|plural
, with each variant used when the reference number to
pluralize is either zero, one, or another number:
To contextualize the number, either use the n
parameter, or use the tc(...)
function which will take, as a second parameter, the reference number.
For example:
const string: translator.t({
en: 'no cats | one cat | {n} cats'
de: 'keine Katzen | eine Katze | {n} Katzen'
}, { n })
// This will result in "no cats" or "keine katzen" when "n" is zero,
// "one cat" or "eine Katze" when "n" is 1, or
// "1,234.56 cats" or "1.234,56 Katzen" when "n" is 1234.56
Is equivalent to:
const string: translator.tc({
en: 'no cats | one cat | {n} cats'
de: 'keine Katzen | eine Katze | {n} Katzen'
}, n)
The n(...)
function can be used to format numbers in the current locale:
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator()
const number = translator.n(1234.5)
// the "number" string will be "1,234.5", "1.234,5" or whatever locale specified
A currency can be specified as a second parameter for quick formatting:
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator()
const amount = translator.n(1234.5, 'USD')
// the "amount" will be "$1,234.5", "1.234,5 $" or whatever locale specified
A full Intl.NumberFormatOptions
set of options can also be specified
as a second parameter to fine-tune the formatting.
The default format can be specified when configuring the plugin as the
formats.numberFormat
option (intentionally, there is no default).
The d(...)
function can be used to format date-and-time values in the current
locale.
When the second parameter is a string, it is considered to be one of the aliases configured when the plugin is setup.
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator()
const dateTime = translator.d(new Date()) // e.g. '03.02.2025, 18:08:05' in de-DE
const dateOnly = translator.d(new Date(), 'date') // e.g. '03.02.2025' in de-DE
const dateTime = translator.d(new Date(), 'time') // e.g. '18:08:05' in de-DE
A full Intl.DateTimeFormatOptions
set of options can also be specified
as a second parameter to fine-tune the formatting.
One of the keys to this package is to provide compile-time safety for all translation languages (we don't want to forget to translate a message in a new language) and translation keys (we don't want to mistype a translation key by accident).
To do so, we can merge the I18nConfiguration
interface of this package
with our specific configurations. Two properties are expected to be defined
in the configuration:
languages
: the list of supported languages for the application. These are ISO 639-1 language codes, and when specified, every translation must include a translation for each.translationKeys
: the list of translation keys known by the application. These are the arbitrary keys used to identify the messages to be translated with thet
andtc
methods ofTranslator
.dateTimeFormats
: the date and time formats aliases used by the application.numberFormats
: the number formats aliases used by the application.
To configure the types, follow the example below:
const translations = {
hello: { en: 'Hello, world!', de: 'Hallo, Welt!' }
} as const satisfies Translations
const dateTimeFormats = {
// override the default format
default: { dateStyle: 'short', timeStyle: 'short' },
// add a new custom format
custom: {
day: '2-digit',
month: '2-digit',
year: 'numeric',
weekday: 'short',
timeZone: 'UTC',
},
} as const satisfies DateTimeFormats
const numberFormats = {
speed: { style: 'unit', unit: 'kilometer-per-hour' },
} as const satisfies NumberFormats
declare module '@juit/vue-i18n' {
export interface I18nConfiguration {
languages: 'de' | 'en',
translationKeys: keyof typeof translations,
dateTimeFormats: keyof typeof dateTimeFormats,
numberFormats: keyof typeof numberFormats,
}
}
const app = createApp(MyApp).use(i18n, {
defaultLanguage: 'en',
translations,
dateTimeFormats,
numberFormats,
})
In the example above, if any of the translation objects in our app is missing
a language (either en
or de
), TypeScript will complain.
In the same way, if we pass any other string but hello
to t(...)
or
tc(...)
, TypeScript will report the wrong key.
Also, date time format aliases will be augumented using the customizations
specified in dateTimeFormats
and numberFormats
.