Composables

useI18n

Composable principale per traduzioni reattive con cambio lingua async e pattern latest-wins.

Panoramica

useI18n è il composable da usare nei componenti Vue per accedere alle traduzioni e cambiare lingua. Lavora su uno stato globale singleton inizializzato da configureI18n: tutti i componenti che invocano useI18n condividono la stessa locale e le stesse translations. Lancia un errore se invocato prima di aver chiamato configureI18n.

Firma

function useI18n<
  TLocale extends string = string,
  TDict extends TranslationDictionary = TranslationDictionary,
>(): UseI18nReturn<TLocale, TDict>

I generics TLocale e TDict permettono di tipizzare la lingua corrente (es. 'it' | 'en' | 'de') e la struttura del dizionario (per autocompletamento sulle chiavi).

Inizializzazione

import { useI18n } from '@pzeta/vue-i18n'

const { t, locale, setLocale, isLoading, translations } = useI18n()

Tipizzato:

type AppLocale = 'it' | 'en' | 'de'
interface AppDict {
  common: { greeting: string }
  pages: { home: { title: string } }
}

const { t, locale } = useI18n<AppLocale, AppDict>()

Return

ProprietàTipoDescrizione
t(key: string, params?: InterpolationParams) => stringFunzione di traduzione
localeComputedRef<TLocale>Locale corrente (readonly)
setLocale(locale: TLocale) => Promise<void>Cambio lingua async
isLoadingComputedRef<boolean>Caricamento di una nuova lingua in corso
translationsComputedRef<TDict>Dizionario corrente (accesso diretto)

t — funzione di traduzione

Naviga il dizionario corrente tramite path dot-notation. Se la chiave non esiste, stampa un warning in console (con warnPrefix configurato) e ritorna la chiave stessa come fallback.

// Traduzione semplice
const title = t('pages.home.title')

// Traduzione con interpolazione
const greeting = t('common.greeting', { name: 'Mario' })
// dizionario: { common: { greeting: 'Ciao {name}!' } } → 'Ciao Mario!'

// Parametri numerici
const counter = t('cart.items', { count: 3, total: 10 })

I segnaposto sono nel formato {nome}. Se un parametro non viene fornito, il segnaposto resta nella stringa così com'è.

Comportamento su chiavi mancanti

const x = t('chiave.inesistente')
// Console: '[i18n] Traduzione mancante: "chiave.inesistente"'
// x === 'chiave.inesistente'

Il fallback alla chiave permette di rendere la UI usabile anche con dizionari incompleti, e di individuare facilmente le traduzioni mancanti in fase di sviluppo.

setLocale — cambio lingua

await setLocale('en')

Comportamento:

  1. Se la nuova locale è uguale a quella corrente, non fa nulla.
  2. Fast path: se le traduzioni sono già in cache (preloaded o caricate in precedenza), il cambio è sincrono — isLoading non viene mai settato a true.
  3. Slow path: se la lingua non è in cache, invoca loadLocale (lazy import) e setta isLoading=true durante il caricamento.
  4. Al termine invoca il callback onLocaleChange(locale) se configurato.

Pattern latest-wins

Se l'utente cambia lingua più volte in rapida successione (es. setLocale('en')setLocale('de') prima che en finisca di caricarsi), il composable applica solo l'ultima richiesta. Le risposte intermedie vengono scartate per evitare flicker o stati incoerenti.

// Click rapido sui flag della lingua
setLocale('en')   // requestId = 1, in caricamento
setLocale('de')   // requestId = 2, in caricamento
// → Solo 'de' verrà applicato quando entrambi i load completano

Internamente è implementato con un counter monotono incrementato a ogni setLocale: dopo l'await loader() il composable confronta requestId === latestSetLocaleId prima di scrivere lo stato.

locale — locale corrente

ComputedRef<TLocale> reattivo. Cambia automaticamente al termine di un setLocale riuscito.

<script setup lang="ts">
import { useI18n } from '@pzeta/vue-i18n'
const { locale } = useI18n()
</script>

<template>
  <span>Lingua attiva: {{ locale }}</span>
</template>

isLoading — flag di caricamento

ComputedRef<boolean> che indica se è in corso un caricamento async di traduzioni. Utile per mostrare uno spinner durante il cambio lingua:

<template>
  <Spinner v-if="isLoading" />
  <button :disabled="isLoading" @click="setLocale('en')">EN</button>
</template>

Resta false durante i cambi locali (cache hit).

translations — accesso diretto

Per casi avanzati in cui serve l'intero dizionario (es. iterare opzioni di un select):

const { translations } = useI18n<AppLocale, AppDict>()

const opzioni = computed(() =>
  Object.entries(translations.value.menu.voci),
)

Per la maggior parte dei casi è preferibile usare t(key) perché beneficia del logging delle chiavi mancanti.

Note

  • Il composable non è scoped al componente: locale e translations sono singleton globali. Tutti gli useI18n() dell'app vedono lo stesso stato.
  • L'invocazione fuori da un setup Vue è ammessa (es. in service): la reattività funziona comunque grazie ai ref globali.
  • t è una funzione "stabile" ma non reattiva di per sé — i template Vue che la chiamano sono reattivi perché leggono translations.value indirettamente. Per usare t in computed o watch, accedere prima a locale.value o translations.value per dichiarare la dipendenza:
const titolo = computed(() => {
  // dipendenza esplicita
  void locale.value
  return t('pages.home.title')
})