fluent-transpiler
v0.7.0
Published
Transpile Fluent (ftl) files into optimized, tree-shakable, JavaScript EcmaScript Modules (esm).
Maintainers
Readme
Install
npm i -D fluent-transpilerCLI
Usage: ftl [options] <inputs...>
Compile Fluent (.ftl) files to JavaScript (.js or .mjs)
Arguments:
inputs Paths to the Fluent file(s) to compile. Multiple files are joined in order; ids must be unique across the set.
Options:
--locale <locale...> What locale(s) to be used. Multiple can be set to allow for fallback. i.e. en-CA
--comments Include comments in output file.
--include-key <includeMessageKey...> Allowed messages to be included. Default to include all.
--exclude-key <excludeMessageKey...> Ignored messages to be excluded. Default to exclude none.
--exclude-value <excludeMessageValue> Set message to an empty string when it equals this value. Default to not allowing empty strings.
--variable-notation <variableNotation> What variable notation to use with exports (choices: "camelCase", "pascalCase", "constantCase", "snakeCase", default: "camelCase")
--disable-minify If disabled, all exported messages will have the same interface `(params) => ({value, attributes})`.
--use-isolating Wrap placeable with \u2068 and \u2069.
--no-error-on-junk Skip `Junk` instead of throwing an error.
-o, --output <output> Path to store the resulting JavaScript file. Will be in ESM.
-h, --help display help for commandNodeJS
| Option | Description |
| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| locale | What locale(s) to be used. Multiple can be set to allow for fallback. i.e. en-CA |
| comments | Include comments in output file. Default: true |
| includeKey | Array of message keys to include; matches the exported name (msgOne) or the original FTL id (msg-one). Non-included messages remain private consts so references keep working. Default: [] (include all) |
| excludeKey | Array of message keys to exclude; matches the exported name (msgOne) or the original FTL id (msg-one). Excluded messages remain private consts so references keep working. Default: [] (exclude none) |
| excludeValue | Set message to an empty string when it equals this value. Default: undefined |
| disableMinify | If disabled, all exported messages will have the same interface (params) => ({value, attributes}). Default: each exported message could be a different type based on what is needed to generate the message (string, object, () => '', () => ({})) |
| errorOnJunk | Throw error when Junk is parsed. Default: true |
| variableNotation | What variable notation to use with exports. Choices: camelCase, pascalCase, snakeCase, constantCase. Default: camelCase |
| useIsolating | Wrap placeable with \u2068 and \u2069. Default: false |
| params | Parameter name used in generated message functions. Default: params |
| exportDefault | Allows the overwriting of the export default to allow for custom uses. Default: See code |
Messages and terms must be defined before they are referenced; referencing a later definition is a compile error.
import { readFile, writeFile } from 'node:fs/promises'
import fluentTranspiler from 'fluent-transpiler'
const ftl = await readFile('./path/to/en.ftl', { encoding: 'utf8' })
const js = fluentTranspiler(ftl, { locale: 'en-CA' })
await writeFile('./path/to/en.mjs', js, 'utf8')Attributes
A message that has attributes compiles to a {value, attributes} record.
{ message } resolves to its .value and { message.attr } resolves to the
named attribute. Referencing an attribute that does not exist is a compile
error: Unknown attribute "msg.missing".
login = Sign in
.title = Sign in to your account
tooltip = { login.title }export const login = {
value: `Sign in`,
attributes: {
title: `Sign in to your account`
}
}
export const tooltip = `${login.attributes.title}`Term attributes are emitted as well. A term that has attributes compiles to a
{value, attributes} record; a term without attributes stays a bare string.
Per the Fluent spec a term attribute is only valid in selector position — a bare
{ -brand.gender } in a pattern is invalid Fluent and the parser rejects it as
Junk.
-brand = Aurora
.gender = feminine
brand-updated = { -brand.gender ->
[feminine] { -brand } has been updated.
*[other] The { -brand } app has been updated.
}const brand = {
value: `Aurora`,
attributes: {
gender: `feminine`
}
}
export const brandUpdated = (params) => `${__select(
brand.attributes.gender,
{
__proto__: null,
'feminine': `${brand.value} has been updated.`
},
`The ${brand.value} app has been updated.`
)}`Joining multiple files
compile also accepts an array of source strings, and compileFiles reads and
joins files from disk. Sources are concatenated in the order supplied; top-level
message and term ids must be unique across the set.
import { writeFile } from 'node:fs/promises'
import { compileFiles } from 'fluent-transpiler'
const js = await compileFiles(
['./common.ftl', './brand.ftl', './app.ftl'],
{ locale: 'en-CA' },
)
await writeFile('./en.mjs', js, 'utf8')