@juit/vue-i18n
| Install | |
|---|---|
npm install @juit/vue-i18n |
|
| Latest Version: | 1.0.4 |
| Vue: | 3 |
| License: | Apache-2.0 |
| Last Updated: | Oct 16, 2026 |
| Links: | GitHub · npm |
Juit I18n for Vue
The @juit/vue-i18n package provides a minimal plugin for Vue 3
to support basic internationalization (translations, number formatting, and
date formatting).
It relies heavily on the Intl.Locale, Intl.NumberFormat, and
Intl.DateTimeFormat global objects, which are widely supported by modern
browsers.
It also integrates with TypeScript to provide compile-time checks for required translation languages and translation keys.
Table of Contents
- Installation
- Configuration
- Usage
- Translating messages
- Formatting numbers
- Formatting dates
- Configuring Types
- Updating Translations
- Language Matching
- Remarks
- Legal Stuff
Installation
As usual, install with npm (or the cool package manager du jour):
npm install '@juit/vue-i18n'
Then 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!',
},
}
})
Configuration
The plugin can be configured with a language code string (the default language) or an object containing the following options:
defaultLanguage: (required) the default language to use; all translations should be available in this language.defaultTimeZone: the default time zone to use when formatting dates (defaults to the local time zone).translations: an object containing the translations for the messages to translate, keyed by message identifier.dateTimeFormats: date and time format aliases used when formatting dates.numberFormats: number format aliases used when formatting numbers.
Date and Time Format Aliases
Date and time format aliases can be configured using string keys and
Intl.DateTimeFormatOptions values:
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:
const dateTimeFormats = {
// used when no alias or date and 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' },
}
Number Format Aliases
Similarly, number format aliases can be configured using string keys and
Intl.NumberFormatOptions values:
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',
}
}
})
By default, numbers use the locale's standard formatting. 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.
Usage
In any component's setup() method, you can use the useTranslator() function
to retrieve the Translator configured for the current app.
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator()
Switching language
To switch languages, simply set the language, region, or locale property
on the translator instance:
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator() // assuming the default locale is "en-US"
translator.region = 'CA' // we have switched to Canada, and the locale is now "en-CA"
translator.language = 'fr' // we have switched to French, and the locale is now "fr-CA"
// or set the full `locale`
translator.locale = new Intl.Locale('de-AT')
Because of reactivity, all translations will be updated to the new locale.
Translating messages
The function for translating messages is exposed as translator.t(...) or
(within components) the $t(...) function.
This function takes a translation key specified during configuration (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 pangram = translator.t({
en: 'The quick brown fox jumps over the lazy dog',
de: 'Franz jagt im komplett verwahrlosten Taxi quer durch Bayern'
})
Parameterizing translations
Translations can include parameters by enclosing them in curly braces {param}.
Parameter names are case-sensitive: {name} and {NAME} refer to different parameters.
Whitespace around parameter names is ignored. If a parameter is missing,
its placeholder is kept in normalized form: { name } becomes {name}.
Escape a placeholder's opening brace with a backslash to display it literally:
\{ name } becomes { name }, even when no parameter is supplied. In a normal
JavaScript string, write the backslash as \\, or use a String.raw template.
Inserted parameter values are treated as literal text and are never parsed again.
Backslashes before placeholders follow the same rule as those before pipes:
each pair produces one literal backslash, and an odd remaining backslash
escapes the placeholder. With two actual backslashes before {name}, one
backslash is displayed and name is substituted; with three, one backslash
is displayed and {name} stays literal.
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"
Numeric parameters are formatted according to the current locale:
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"
Pluralization
The translator supports basic pluralization rules by separating
translation messages with the | (pipe) character.
Messages can contain two variants, singular|plural, or three variants,
zero|singular|plural. The singular variant is used for one, and the plural
variant for other numbers. If a zero variant is provided, it is used for zero
instead of the plural variant.
To specify the number used for pluralization, either use the n parameter
or pass the number as the second argument to tc(...).
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
This is equivalent to:
const string = translator.tc({
en: 'no cats | one cat | {n} cats',
de: 'keine Katzen | eine Katze | {n} Katzen'
}, n)
Messages can also use explicit tuples instead of pipe-delimited strings:
const message = {
en: ['no cats', 'one cat', '{n} cats'],
de: ['keine Katzen', 'eine Katze', '{n} Katzen'],
} as const
translator.tc(message, 2) // '2 cats' or '2 Katzen'
A tuple with one element is used for every count. Two elements mean
[singular, plural], with zero using the plural variant. Three elements mean
[zero, singular, plural].
Tuple elements still support placeholders and escaped placeholders, but skip
pipe splitting and pipe unescaping entirely: ['A | B'] displays A | B.
Tuples work in base and regional translations, inline messages, and
updateTranslations(...). They are copied when stored, so changing the
original array later cannot change stored messages or cached results.
Formatting numbers
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", etc., depending on the locale
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" string will be "$1,234.50" in en-US
An Intl.NumberFormatOptions object can also be specified
as a second parameter to fine-tune the formatting.
The default format can be specified using numberFormats.default when
configuring the plugin.
Formatting dates
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 set up.
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 timeOnly = translator.d(new Date(), 'time') // e.g. '18:08:05' in de-DE
An Intl.DateTimeFormatOptions object can also be specified
as a second parameter to fine-tune the formatting.
The third parameter, if specified, can be used to override the time zone used when formatting the date. This is useful when time zones are specified in the definitions of date and time format aliases (see above):
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator()
translator.d(new Date(), 'full', 'Europe/Berlin')
// e.g. Monday, 3 February 2025 at 18:08:03 Central European Standard Time
Configuring Types
One of the key features of this package is compile-time safety for all translation languages (we don't want to forget to translate a message into 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 application-specific configuration. The following properties can
be defined:
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 thetandtcmethods ofTranslator.dateTimeFormats: the date and time format aliases used by the application.numberFormats: the number format 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 string other than hello to t(...) or
tc(...), TypeScript will report an invalid key.
Date, time, and number format alias types will also be augmented using the
customizations specified in dateTimeFormats and numberFormats.
Updating Translations
Use translator.utils.updateTranslations(...) to add or change translations
after the plugin has been configured, for example when loading messages from
a server. Pass an object keyed by translation key, with language codes and
their translated messages as values:
import { useTranslator } from '@juit/vue-i18n'
const translator = useTranslator()
translator.utils.updateTranslations({
hello: {
en: 'Hello again!',
},
})
translator.language = 'en'
translator.t('hello') // 'Hello again!'
Updates are merged into the existing translations: only the supplied
key-language pairs are overwritten. In this example, the German translation
of hello is preserved. New keys and language variants can also be added.
Both translation keys and languages are optional in an update, so you do not
need to supply every configured language. If you have configured
I18nConfiguration, TypeScript checks the supplied keys and languages against
those types.
Empty strings and undefined values are ignored; they cannot be used to
delete an existing translation. The method returns nothing and clears the
translation cache when an update is applied, so subsequent calls to t(...)
and tc(...) use the updated messages. Updating translations alone does not
trigger a Vue component re-render.
Language Matching
The LanguageMatcher class selects a supported language from a user's
preferences. It can be used independently of the Vue plugin, for example to
choose the initial language from the browser's navigator.languages:
import { LanguageMatcher } from '@juit/vue-i18n'
const matcher = new LanguageMatcher([ 'en', 'de', 'ja' ])
matcher.defaultLanguage // 'en'
matcher.availableLanguages // [ 'en', 'de', 'ja' ]
const app = createApp(MyApp).use(i18n, {
defaultLanguage: matcher.match([ ...navigator.languages ]),
translations,
})
The constructor accepts a single ISO 639-1 language code (such as 'en') or
a non-empty array of codes. The first valid language is the default, used
whenever no preference matches.
The match(...) method accepts a string, an array of strings in preference
order, null, or undefined. It returns the first supported preference,
regardless of the order of the available languages:
matcher.match('de') // 'de'
matcher.match('JA-JP') // 'ja'
matcher.match('de_AT') // 'de'
matcher.match([ 'fr', 'ja-JP', 'de' ]) // 'ja'
matcher.match('fr') // 'en' (default)
matcher.match([]) // 'en' (default)
matcher.match(null) // 'en' (default)
matcher.match(undefined) // 'en' (default)
const englishOnly = new LanguageMatcher('en')
englishOnly.match('de') // 'en'
Remarks
To keep the package small, the translator supports only the language and
optional region of a locale. Both defaultLanguage and assignments to
translator.locale discard script subtags and Unicode extensions, including
calendar and numbering-system preferences. For example,
zh-Hant-TW-u-nu-hanidec becomes zh-TW.
Changing translator.language preserves the current region: switching from
en-CA to fr produces fr-CA. Changing translator.region preserves the
language; setting it to undefined removes the region.
Legal Stuff
Related Packages
Version History
| Version | Released | License |
|---|---|---|
| 1.0.4 | Apache-2.0 | |
| 1.0.3 | Apache-2.0 | |
| 1.0.2 | Apache-2.0 | |
| 1.0.1 | Apache-2.0 | |
| 1.0.0 | Apache-2.0 | |
| 0.4.1 | Apache-2.0 | |
| 0.4.0 | Apache-2.0 | |
| 0.3.8 | Apache-2.0 | |
| 0.3.7 | Apache-2.0 | |
| 0.3.6 | Apache-2.0 | |
| 0.3.5 | Apache-2.0 | |
| 0.3.4 | Apache-2.0 | |
| 0.3.3 | Apache-2.0 | |
| 0.3.2 | Apache-2.0 | |
| 0.3.1 | Apache-2.0 | |
| 0.3.0 | Apache-2.0 | |
| 0.2.65 | Apache-2.0 | |
| 0.2.64 | Apache-2.0 | |
| 0.2.63 | Apache-2.0 | |
| 0.2.62 | Apache-2.0 | |
| 0.2.61 | Apache-2.0 | |
| 0.2.58 | Apache-2.0 | |
| 0.2.54 | Apache-2.0 | |
| 0.2.53 | Apache-2.0 | |
| 0.2.52 | Apache-2.0 | |
| 0.2.51 | Apache-2.0 | |
| 0.2.50 | Apache-2.0 | |
| 0.2.49 | Apache-2.0 | |
| 0.2.48 | Apache-2.0 | |
| 0.2.47 | Apache-2.0 |
Showing the latest 30 of 80. See every release on npm