Installation
First, install the required dependencies in your Vue.js project:
npm install vue-i18n@9
For Vue 2 projects, use:
npm install vue-i18n@8
Vue.js Configuration
Vue 3 Setup
Create an i18n configuration file:
// src/i18n/index.ts
import { createI18n } from 'vue-i18n'
// Import your translation files
import en from './locales/en.json'
import pl from './locales/pl.json'
import es from './locales/es.json'
import fr from './locales/fr.json'
const messages = {
en,
pl,
es,
fr
}
const i18n = createI18n({
legacy: false, // Use Composition API mode
locale: 'en', // Default locale
fallbackLocale: 'en',
messages,
globalInjection: true, // Make $t available globally
})
export default i18n
Configure your main application file:
// src/main.ts
import { createApp } from 'vue'
import App from './App.vue'
import i18n from './i18n'
const app = createApp(App)
app.use(i18n)
app.mount('#app')
Vue 2 Setup
For Vue 2 projects:
// src/i18n/index.js
import Vue from 'vue'
import VueI18n from 'docs/integrations/vuejs/vue-i18n.mdx'
Vue.use(VueI18n)
// Import your translation files
import en from './locales/en.json'
import pl from './locales/pl.json'
import es from './locales/es.json'
import fr from './locales/fr.json'
const messages = {
en,
pl,
es,
fr
}
const i18n = new VueI18n({
locale: 'en', // Default locale
fallbackLocale: 'en',
messages,
})
export default i18n
// src/main.js
import Vue from 'vue'
import App from './App.vue'
import i18n from './i18n'
new Vue({
i18n,
render: h => h(App),
}).$mount('#app')
Translation File Structure
Create translation files in the src/i18n/locales/
directory:
// src/i18n/locales/en.json
{
"homepage": {
"title": "Welcome to our website",
"description": "This is a multilingual Vue.js application"
},
"product": {
"price": "Price: {price}",
"available": "Available since: {date}",
"inStock": "In stock: {count} | In stock: {count} items"
},
"navigation": {
"home": "Home",
"about": "About",
"contact": "Contact"
},
"user": {
"greeting": "Hello {name}!",
"profile": "User profile"
}
}
// src/i18n/locales/pl.json
{
"homepage": {
"title": "Witamy na naszej stronie",
"description": "To jest wielojęzyczna aplikacja Vue.js"
},
"product": {
"price": "Cena: {price}",
"available": "Dostępne od: {date}",
"inStock": "Na stanie: {count} sztuka | Na stanie: {count} sztuki | Na stanie: {count} sztuk"
},
"navigation": {
"home": "Strona główna",
"about": "O nas",
"contact": "Kontakt"
},
"user": {
"greeting": "Cześć {name}!",
"profile": "Profil użytkownika"
}
}
// src/i18n/locales/es.json
{
"homepage": {
"title": "Bienvenido a nuestro sitio web",
"description": "Esta es una aplicación Vue.js multiidioma"
},
"product": {
"price": "Precio: {price}",
"available": "Disponible desde: {date}",
"inStock": "En stock: {count} artículo | En stock: {count} artículos"
},
"navigation": {
"home": "Inicio",
"about": "Acerca de",
"contact": "Contacto"
},
"user": {
"greeting": "¡Hola {name}!",
"profile": "Perfil de usuario"
}
}
// src/i18n/locales/fr.json
{
"homepage": {
"title": "Bienvenue sur notre site web",
"description": "Ceci est une application Vue.js multilingue"
},
"product": {
"price": "Prix: {price}",
"available": "Disponible depuis: {date}",
"inStock": "En stock: {count} article | En stock: {count} articles"
},
"navigation": {
"home": "Accueil",
"about": "À propos",
"contact": "Contact"
},
"user": {
"greeting": "Bonjour {name}!",
"profile": "Profil utilisateur"
}
}
Usage Examples
Basic Translation (Vue 3 Composition API)
<template>
<div>
<h1>{{ $t('homepage.title') }}</h1>
<p>{{ $t('homepage.description') }}</p>
<!-- Using interpolation -->
<p>{{ $t('user.greeting', { name: 'John' }) }}</p>
</div>
</template>
<script setup>
import { useI18n } from 'vue-i18n'
const { t, locale } = useI18n()
// You can also use the composable directly
const greeting = computed(() => t('user.greeting', { name: 'John' }))
</script>
Basic Translation (Vue 3 Options API / Vue 2)
<template>
<div>
<h1>{{ $t('homepage.title') }}</h1>
<p>{{ $t('homepage.description') }}</p>
<!-- Using interpolation -->
<p>{{ $t('user.greeting', { name: userName }) }}</p>
</div>
</template>
<script>
export default {
data() {
return {
userName: 'John'
}
},
computed: {
greeting() {
return this.$t('user.greeting', { name: this.userName })
}
}
}
</script>
Language Switcher Component
<template>
<div class="language-switcher">
<select v-model="currentLocale" @change="changeLanguage">
<option v-for="lang in availableLanguages" :key="lang.code" :value="lang.code">
{{ lang.name }}
</option>
</select>
</div>
</template>
<script setup>
import { ref, computed } from 'vue'
import { useI18n } from 'vue-i18n'
const { locale, availableLocales } = useI18n()
const availableLanguages = [
{ code: 'en', name: 'English' },
{ code: 'pl', name: 'Polski' },
{ code: 'es', name: 'Español' },
{ code: 'fr', name: 'Français' }
]
const currentLocale = computed({
get: () => locale.value,
set: (value) => {
locale.value = value
// Save to localStorage for persistence
localStorage.setItem('user-locale', value)
}
})
const changeLanguage = () => {
// Language change is handled by the computed setter
}
</script>
<style scoped>
.language-switcher select {
padding: 8px 12px;
border: 1px solid #ccc;
border-radius: 4px;
background: white;
}
</style>
Advanced Formatting and Pluralization
<template>
<div class="product-card">
<h3>{{ product.name }}</h3>
<!-- Number formatting -->
<p>{{ $t('product.price', { price: $n(product.price, 'currency') }) }}</p>
<!-- Date formatting -->
<p>{{ $t('product.available', { date: $d(product.availableDate, 'short') }) }}</p>
<!-- Pluralization -->
<p>{{ $tc('product.inStock', product.stockCount, { count: product.stockCount }) }}</p>
</div>
</template>
<script setup>
import { ref } from 'vue'
const product = ref({
name: 'Example Product',
price: 29.99,
availableDate: new Date('2025-01-15'),
stockCount: 5
})
</script>
Custom Number and Date Formats
// src/i18n/index.ts
import { createI18n } from 'vue-i18n'
const numberFormats = {
en: {
currency: {
style: 'currency',
currency: 'USD',
notation: 'standard'
},
decimal: {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}
},
pl: {
currency: {
style: 'currency',
currency: 'PLN',
notation: 'standard'
},
decimal: {
style: 'decimal',
minimumFractionDigits: 2,
maximumFractionDigits: 2
}
}
}
const dateTimeFormats = {
en: {
short: {
year: 'numeric',
month: 'short',
day: 'numeric'
},
long: {
year: 'numeric',
month: 'short',
day: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: 'numeric'
}
},
pl: {
short: {
year: 'numeric',
month: 'short',
day: 'numeric'
},
long: {
year: 'numeric',
month: 'short',
day: 'numeric',
weekday: 'short',
hour: 'numeric',
minute: 'numeric'
}
}
}
const i18n = createI18n({
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages,
numberFormats,
dateTimeFormats
})
Dynamic Locale Loading
For larger applications, you might want to load translations dynamically:
// src/i18n/index.ts
import { createI18n } from 'vue-i18n'
export const SUPPORT_LOCALES = ['en', 'pl', 'es', 'fr']
export function setupI18n(options = { locale: 'en' }) {
const i18n = createI18n({
legacy: false,
globalInjection: true,
locale: options.locale,
fallbackLocale: 'en',
...options
})
setI18nLanguage(i18n, options.locale)
return i18n
}
export function setI18nLanguage(i18n, locale) {
i18n.global.locale.value = locale
document.querySelector('html').setAttribute('lang', locale)
}
export async function loadLocaleMessages(i18n, locale) {
// Load locale messages with dynamic import
const messages = await import(`./locales/${locale}.json`)
// Set locale and locale message
i18n.global.setLocaleMessage(locale, messages.default)
return nextTick()
}
// Usage in component
import { loadLocaleMessages, setI18nLanguage } from '@/i18n'
const changeLanguage = async (locale) => {
// Check if locale messages are already loaded
if (!i18n.global.availableLocales.includes(locale)) {
await loadLocaleMessages(i18n, locale)
}
setI18nLanguage(i18n, locale)
}
SimpleLocalize Configuration
To manage translations with SimpleLocalize, you need to set up the CLI tool.
# macOS / Linux / Windows (WSL)
curl -s https://get.simplelocalize.io/2.9/install | bash
# Windows (PowerShell)
. { iwr -useb https://get.simplelocalize.io/2.9/install-windows } | iex;
# npm
npm install @simplelocalize/cli
# macOS / Linux / Windows (WSL)
curl -s https://get.simplelocalize.io/2.9/install | bash
# Windows (PowerShell)
. { iwr -useb https://get.simplelocalize.io/2.9/install-windows } | iex;
# npm
npm install @simplelocalize/cli
Create simplelocalize.yml
in your project root:
apiKey: YOUR_PROJECT_API_KEY
uploadFormat: single-language-json
uploadPath: ./src/i18n/locales/en.json
uploadLanguageKey: en
uploadOptions:
- REPLACE_TRANSLATION_IF_FOUND
downloadPath: ./src/i18n/locales/{lang}.json
downloadLanguageKeys: ['en', 'pl', 'es', 'fr']
downloadFormat: single-language-json
Extraction Script (Optional)
For automatic key extraction, you can create a custom script:
// scripts/extract-i18n.js
const fs = require('fs')
const path = require('path')
function extractTranslationKeys(dir) {
const keys = new Set()
const files = fs.readdirSync(dir)
files.forEach(file => {
const filePath = path.join(dir, file)
const stat = fs.statSync(filePath)
if (stat.isDirectory()) {
extractTranslationKeys(filePath).forEach(key => keys.add(key))
} else if (file.endsWith('.vue') || file.endsWith('.js') || file.endsWith('.ts')) {
const content = fs.readFileSync(filePath, 'utf8')
// Extract $t('key') patterns
const tMatches = content.match(/\$t\(['"`]([^'"`]+)['"`]\)/g)
if (tMatches) {
tMatches.forEach(match => {
const key = match.match(/\$t\(['"`]([^'"`]+)['"`]\)/)[1]
keys.add(key)
})
}
// Extract $tc('key') patterns
const tcMatches = content.match(/\$tc\(['"`]([^'"`]+)['"`]/g)
if (tcMatches) {
tcMatches.forEach(match => {
const key = match.match(/\$tc\(['"`]([^'"`]+)['"`]/)[1]
keys.add(key)
})
}
}
})
return Array.from(keys)
}
// Extract keys and create a template
const keys = extractTranslationKeys('./src')
const template = {}
keys.forEach(key => {
const parts = key.split('.')
let current = template
for (let i = 0; i < parts.length - 1; i++) {
if (!current[parts[i]]) {
current[parts[i]] = {}
}
current = current[parts[i]]
}
current[parts[parts.length - 1]] = key
})
fs.writeFileSync('./extracted-keys.json', JSON.stringify(template, null, 2))
console.log(`Extracted ${keys.length} translation keys`)
Add the script to your package.json
:
{
"scripts": {
"extract:i18n": "node scripts/extract-i18n.js",
"i18n:upload": "npm run extract:i18n && simplelocalize upload",
"i18n:download": "simplelocalize download"
}
}
Work with the CLI to manage your translations:
# Extract translation keys from your Vue components
npm run extract:i18n
# Upload translations to SimpleLocalize
simplelocalize upload
# Download translations for all languages
simplelocalize download
Best Practices
1. Key Naming Convention
Use descriptive, hierarchical keys:
// Good
$t('navigation.menu.home')
$t('product.details.price')
$t('user.profile.settings.privacy')
// Avoid
$t('home')
$t('price')
$t('privacy')
2. Component-Scoped Translations
For component-specific translations, consider using i18n custom blocks:
<template>
<div>
<h1>{{ $t('title') }}</h1>
<p>{{ $t('description') }}</p>
</div>
</template>
<script setup>
// Component logic
</script>
<i18n>
{
"en": {
"title": "Component Title",
"description": "Component description"
},
"pl": {
"title": "Tytuł komponentu",
"description": "Opis komponentu"
}
}
</i18n>
3. Type Safety (TypeScript)
Create type definitions for your translations:
// types/i18n.ts
export interface MessageSchema {
homepage: {
title: string
description: string
}
navigation: {
home: string
about: string
contact: string
}
// ... other keys
}
// src/i18n/index.ts
import { createI18n } from 'vue-i18n'
import type { MessageSchema } from '@/types/i18n'
const i18n = createI18n<[MessageSchema], 'en' | 'pl' | 'es' | 'fr'>({
legacy: false,
locale: 'en',
fallbackLocale: 'en',
messages
})
Resources
- Vue I18n Documentation
- Vue I18n GitHub Repository
- SimpleLocalize CLI Documentation
- SimpleLocalize Translation Hosting
Was this helpful?