Skip to content
fluenti

Fallback Chains

Applications that support regional variants (e.g., pt-BR and pt-PT) often share most of their translations. Without fallback chains, you would need to duplicate the entire catalog for each variant. Fallback chains let you maintain only the differences in regional catalogs and fall back to a shared base locale for the rest.

When a message is not found in the current locale, Fluenti resolves it in this order:

  1. Current locale — the active locale’s catalog
  2. Fallback chain — locale-specific chain from fallbackChain[currentLocale], then the wildcard fallbackChain['*']
  3. fallbackLocale — the global fallback locale (if set)
  4. missing handler — custom function for programmatic fallback
  5. Message ID — returns the raw ID as a last resort

Define fallbackChain in your fluenti.config.ts:

fluenti.config.ts
import { defineConfig } from '@fluenti/core'
export default defineConfig({
sourceLocale: 'en',
locales: ['en', 'pt', 'pt-BR', 'zh-CN', 'zh-TW', 'es', 'es-MX'],
fallbackChain: {
'pt-BR': ['pt', 'en'],
'zh-TW': ['zh-CN', 'en'],
'es-MX': ['es', 'en'],
'*': ['en'],
},
// ...
})

Or pass it directly to the runtime instance:

const i18n = createFluentiCore({
locale: 'pt-BR',
fallbackLocale: 'en',
fallbackChain: {
'pt-BR': ['pt', 'en'],
'zh-TW': ['zh-CN', 'en'],
'es-MX': ['es', 'en'],
},
messages: { en, pt, 'pt-BR': ptBR, 'zh-CN': zhCN, 'zh-TW': zhTW, es, 'es-MX': esMX },
})

Common fallback patterns for regional variants:

  • Chinese: zh-TW falls back to zh-CN before en. The zh-TW catalog only needs entries where Traditional Chinese characters or phrasing differ from Simplified.
  • Spanish: es-MX falls back to es before en. Override region-specific terms (e.g., “computadora” vs “ordenador”) while inheriting everything else.
  • Portuguese: pt-BR falls back to pt before en. Maintain a full pt catalog for European Portuguese and a partial pt-BR catalog with Brazilian-specific overrides.

Use per-locale chains for regional variants that share a common base:

fallbackChain: {
'en-GB': ['en'],
'en-AU': ['en-GB', 'en'],
'fr-CA': ['fr'],
}

Use the wildcard '*' as a catch-all for any locale without an explicit chain:

fallbackChain: {
'pt-BR': ['pt', 'en'],
'*': ['en'], // all other locales fall back to English
}

The wildcard chain is checked only when no locale-specific chain exists for the current locale.

For custom behavior when a message is not found after all fallback attempts:

const i18n = createFluentiCore({
locale: 'en',
messages,
missing: (locale, id) => {
console.warn(`Missing translation: ${locale}/${id}`)
return undefined // or return a fallback string
},
})

The missing handler runs after the full chain is exhausted, giving you a final opportunity to provide a dynamic fallback or log the gap for translators.

Test with partial catalogs to verify resolution at each level of the chain:

const i18n = createFluentiCore({
locale: 'pt-BR',
fallbackLocale: 'en',
fallbackChain: { 'pt-BR': ['pt', 'en'] },
messages: {
en: { greeting: 'Hello', color: 'Color', cancel: 'Cancel' },
pt: { greeting: 'Olá', color: 'Cor' },
'pt-BR': { color: 'Cor' },
},
})
i18n.t('color') // 'Cor' — found in pt-BR
i18n.t('greeting') // 'Olá' — pt-BR → pt
i18n.t('cancel') // 'Cancel' — pt-BR → pt → en