Skip to content
fluenti

Configuration

Fluenti is configured via a fluenti.config.ts file at your project root. The file is loaded at build time using jiti, so you can use TypeScript, ESM imports, and top-level await without any extra setup.

Wrap your config in defineConfig() for full type inference and IDE autocompletion. It is an identity function with no runtime effect.

fluenti.config.ts
import { defineConfig } from '@fluenti/cli'
export default defineConfig({
sourceLocale: 'en',
locales: ['en', 'ja', 'zh-CN'],
catalogDir: './locales',
format: 'po',
include: ['./src/**/*.{vue,tsx,ts}'],
compileOutDir: './src/locales/compiled',
})

defineConfig is also exported from @fluenti/core if you prefer not to depend on the CLI package.

When no explicit path is provided, the config loader searches the working directory for:

  1. fluenti.config.ts
  2. fluenti.config.js
  3. fluenti.config.mjs

The first file found is used. If none exists, built-in defaults apply.

These options control extraction, compilation, and output.

Typestring
Default'en'

The locale used in your source code. Messages written in source files are treated as this locale. This locale’s catalog is auto-populated during extraction.

TypeLocaleDefinition[]
Default['en']

All locales your app supports. Each entry can be a plain string or a metadata object:

locales: [
'en',
{ code: 'ja', name: '日本語' },
{ code: 'ar', name: 'العربية', dir: 'rtl' },
{ code: 'zh-CN', name: '简体中文', iso: 'zh-Hans-CN' },
]

The LocaleObject shape:

PropertyTypeDescription
codestringLocale code (e.g. 'en', 'ja', 'zh-CN')
namestring?Human-readable display name
isostring?BCP 47 language tag for SEO (e.g. 'en-US')
dir'ltr' | 'rtl'?Text direction
domainstring?Domain for domain-based routing
Typestring?
DefaultValue of sourceLocale

Default locale for routing and detection. Usually the same as sourceLocale, but can differ when your source language is not the primary user-facing locale.

Typestring
Default'./locales'

Directory where translation catalog files are stored. The CLI reads from and writes to this directory during extract and compile commands.

Type'po' | 'json'
Default'po'

Catalog file format.

  • 'po' (recommended) — gettext PO format. Compatible with Poedit, Weblate, Crowdin, and other translation tools.
  • 'json' — Simple key-value JSON. Useful for lightweight setups or when integrating with JSON-based translation services.
Typestring[]
Default['./src/**/*.{vue,tsx,jsx,ts,js}']

Glob patterns for source files to scan during message extraction.

// Vue project
include: ['./src/**/*.{vue,ts}']
// Next.js project
include: ['./src/**/*.{tsx,ts}']
// Nuxt project (no src directory)
include: ['./pages/**/*.vue', './components/**/*.vue', './plugins/**/*.ts']
Typestring[]
Default['**/*.test.*', '**/*.spec.*', '**/__tests__/**', '**/*.d.ts']

Glob patterns to exclude from extraction. Test files and type declarations are excluded by default.

Typestring
Default'./src/locales/compiled'

Output directory for compiled message modules. The compiler writes one file per locale here (e.g. en.ts, ja.ts). These files are imported by framework integrations at runtime.

Type'.js' | '.ts'
Default'.js'

File extension for compiled catalog output files. Use '.ts' if your build pipeline handles TypeScript directly.

Type'dynamic' | 'static' | false
Defaultfalse

Code-splitting strategy for compiled message modules.

  • false — All messages are bundled into a single module per locale. Simplest setup.
  • 'static' — A single locale is inlined at build time. Requires defaultBuildLocale.
  • 'dynamic' — Default locale in the bundle, other locales loaded on demand. Best for large apps with many locales.

See Code Splitting for details.

Typestring?
Default

The locale embedded directly in the bundle when using the 'static' splitting strategy. Other locales are loaded at runtime.

Type(message: string, context?: string) => string
DefaultBuilt-in hash function

Custom message ID generator. By default, Fluenti generates deterministic hash-based IDs from message content and optional context. Override this to use your own ID scheme.

idGenerator: (message, context) => {
const base = context ? `${context}::${message}` : message
return myCustomHash(base)
}

Options that control behavior during development.

Typeboolean
Defaulttrue

Automatically run extract + compile when source files change in dev mode. The Vite plugin watches for file changes and triggers recompilation.

Typeboolean
Defaulttrue

Automatically run extract + compile before production builds. Set to false if you run fluenti extract && fluenti compile as a separate CI step.

Typenumber
Default500

Debounce delay in milliseconds for dev auto-compile. Prevents excessive recompilation when multiple files change in rapid succession.

Typeboolean
Defaultfalse

Enable parallel compilation across locales using worker threads. Useful for projects with many target locales where compilation is a bottleneck.

Typeboolean?
Default

Enable development warnings for missing translations. When true, missing messages return a [!]-prefixed fallback and emit console.warn. Also activatable via the FLUENTI_DEBUG environment variable.

Type() => boolean | void | Promise<boolean | void>

Called before auto-compile runs. Return false to skip compilation for this cycle.

onBeforeCompile: () => {
console.log('Compiling translations...')
// Return false to skip
}
Type() => void | Promise<void>

Called after auto-compile completes successfully.

onAfterCompile: () => {
console.log('Translation compilation complete')
}

These options are stored in the config file but consumed at runtime by framework integrations.

TypeRecord<string, string[]>
Default{}

Locale fallback chains. When a message is missing in a locale, the chain is followed in order. Use '*' as a wildcard for all locales.

fallbackChain: {
'zh-TW': ['zh-CN', 'en'], // Traditional Chinese falls back to Simplified, then English
'pt-BR': ['pt', 'en'], // Brazilian Portuguese falls back to Portuguese, then English
'*': ['en'], // All other locales fall back to English
}

See Fallback Chains for details.

TypeRecord<string, Intl.DateTimeFormatOptions | 'relative'>
Default

Named date format presets used by the d() formatter and <DateTime> components.

dateFormats: {
short: { dateStyle: 'short' },
long: { dateStyle: 'long', timeStyle: 'short' },
relative: 'relative',
}
TypeRecord<string, Intl.NumberFormatOptions | ((locale: string) => Intl.NumberFormatOptions)>
Default

Named number format presets used by the n() formatter and <NumberFormat> components. Values can be static options or a function that returns locale-specific options.

numberFormats: {
currency: (locale) => ({
style: 'currency',
currency: locale.startsWith('ja') ? 'JPY' : 'USD',
}),
percent: { style: 'percent', maximumFractionDigits: 1 },
}
TypeFluentiPlugin[]
Default

Plugins that hook into the extract and compile pipelines. Each plugin is an object with a name and optional lifecycle hooks.

import { pseudoLocalePlugin } from '@fluenti/core/plugins/pseudo-locale'
import { messageValidatorPlugin } from '@fluenti/core/plugins/message-validator'
export default defineConfig({
// ...
plugins: [
pseudoLocalePlugin({ locale: 'en-XA' }),
messageValidatorPlugin(),
],
})

The FluentiPlugin interface:

HookSignatureDescription
namestringPlugin identifier
onAfterExtract(context) => voidCalled after message extraction
onBeforeCompile(context) => voidCalled before compilation per locale
onAfterCompile(context) => voidCalled after compilation per locale
transformMessages(messages, locale) => messagesTransform message map before compilation
formattersRecord<string, CustomFormatter>Custom ICU formatters provided by the plugin
Typestring?
Default

Path to a parent config file (relative to the current config’s directory). The child config inherits all options from the parent and can override any of them. Path-based fields (catalogDir, compileOutDir, include, exclude) are automatically rebased to the child directory.

my-monorepo/
├── fluenti.config.ts # Shared base config
├── packages/
│ ├── app-a/
│ │ └── fluenti.config.ts # extends ../../fluenti.config.ts
│ └── app-b/
│ └── fluenti.config.ts # extends ../../fluenti.config.ts

Root config (shared settings):

my-monorepo/fluenti.config.ts
import { defineConfig } from '@fluenti/cli'
export default defineConfig({
sourceLocale: 'en',
locales: ['en', 'ja'],
format: 'po',
})

App config (inherits and overrides):

my-monorepo/packages/app-a/fluenti.config.ts
import { defineConfig } from '@fluenti/cli'
export default defineConfig({
extends: '../../fluenti.config.ts',
catalogDir: '../../locales',
include: ['./src/**/*.{tsx,ts}'],
compileOutDir: './src/locales/compiled',
})

The extends chain has a maximum depth of 10 and circular references are detected with a clear error message. The extends path must be relative — absolute paths are rejected for security.

These options are available when creating a runtime instance (e.g. via createFluentiCore(), createFluenti(), or I18nProvider), but not in fluenti.config.ts:

OptionTypeDescription
localestringActive locale
fallbackLocalestring?Fallback locale when a key is missing
messagesAllMessagesPre-loaded message catalogs
missing(locale, id) => string | undefinedCalled when a translation key is missing. Return a string to use as fallback.
transform(result, id, locale) => stringPost-translation transform applied to every resolved message
onLocaleChange(newLocale, prevLocale) => voidCallback fired when locale changes via setLocale()
formattersRecord<string, CustomFormatter>Custom ICU function formatters (e.g. {items, list})
devWarningsboolean?Show [!] prefix and console.warn for missing translations

See the @fluenti/core API reference for full details on runtime configuration.

Load and resolve a config file asynchronously. Supports extends chain resolution.

import { loadConfig } from '@fluenti/core'
const config = await loadConfig() // Auto-discover from cwd
const config = await loadConfig('./custom.config.ts') // Explicit path
const config = await loadConfig(undefined, '/my/app') // Custom working directory

Synchronous variant for contexts where await is not available (e.g. webpack config). Falls back to defaults on error.

import { loadConfigSync } from '@fluenti/core'
const config = loadConfigSync()

Validate and normalize a config object. Useful for tools and plugins that construct config programmatically.

import { normalizeConfig } from '@fluenti/core'
const config = normalizeConfig({
sourceLocale: 'en',
locales: ['en', 'ja'],
catalogDir: './locales',
format: 'po',
include: ['./src/**/*.tsx'],
compileOutDir: './src/locales/compiled',
})

When no config file is found, or for any omitted options, these defaults apply:

{
sourceLocale: 'en',
locales: ['en'],
catalogDir: './locales',
format: 'po',
include: ['./src/**/*.{vue,tsx,jsx,ts,js}'],
exclude: ['**/*.test.*', '**/*.spec.*', '**/__tests__/**', '**/*.d.ts'],
compileOutDir: './src/locales/compiled',
devAutoCompile: true,
buildAutoCompile: true,
devAutoCompileDelay: 500,
}