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')
})