Which tools are available for finding errors in FTL files?

Hi

Great to see the error handling documented in https://github.com/projectfluent/fluent/wiki/Error-Handling#error-reporting that will provide errors such as Use * in front of at least one variant to mark it as primary.

Which tools are available for this?

The following example from https://github.com/projectfluent/fluent.js/tree/master/fluent-bundle#how-to-use hints that it will provide syntax errors.

import {FluentBundle, FluentResource} from "@fluent/bundle";

let resource = new FluentResource(`
-brand-name = Foo 3000
welcome = Welcome, {$name}, to {-brand-name}!
`);

let bundle = new FluentBundle("en-US");
let errors = bundle.addResource(resource);
if (errors.length) {
    // Syntax errors are per-message and don't break the whole resource
}

let welcome = bundle.getMessage("welcome");
if (welcome.value) {
    bundle.formatPattern(welcome.value, {name: "Anna"});
    // → "Welcome, Anna, to Foo 3000!"
}

However if I remove an ending curly-brace in the welcome message the errors is still empty. Same also if I refer to -brandname instead of -brand-name.

As for the example from the error handling documentation, running

import { FluentBundle, FluentResource } from "@fluent/bundle";

let resource = new FluentResource(`
liked-photo = { PLURAL($people) ->
    [1]     { $people } lubi
    [2]     { $people } lubią
    [3]     { TAKE(2, $people), "jedna inna osoba" } lubią

   *[other] { TAKE(2, $people),
              "{ PLURAL(DROP(2, $people)) ->
                  [1]    jedna inna osoba lubią
                  [few]  { LEN(DROP(2, $people)) } inne osoby lubią
                  [many] { LEN(DROP(2, $people)) } innych osób lubi
               }"
            }
} Twoje zdjęcie.`);
console.log(resource)
let bundle = new FluentBundle("en-US");
let errors = bundle.addResource(resource);
if (errors.length) {
    // Syntax errors are per-message and don't break the whole resource
    console.log("Errors ", errors)
} else {
    console.log("No errors when adding resource")
}

prints

FluentResource { body: [] }
No errors when adding resource

Hi!

That’s because our runtime in JS is not reporting parsing errors, only resolution errors.
In order to validate syntax of the source file, please use fluent-syntax package.

The rationale is performance - having full parser with correct syntax error reporting was significantly slower and for runtime scenarios we had to optimize for performance.

In the future, we may switch the backend to be generated from Rust parser via WebAssembly, and in Rust the runtime parser is performing complete error reporting.

Ah of course! Thanks!

Any difference in ease of use or recommendation when it comes to js, rust or python in this regard? The FTL files will be exposed also to non-techy translators :slight_smile:

Hmm, not really. For full syntactic parsing for tooling, the fluent-syntax in JS, Python and Rust should return comparably complete results.

Looking more into this I found some tools in fluent.js so I checked out the source code of fluent.js, built the project and ran the parse.js script on the liked-photo message in https://github.com/projectfluent/fluent/wiki/MessageFormat-vs-Fluent-Syntax#function-calls. The output is

! E0003 on line 4:
  | liked-photo = { PLURAL($people) ->
  |     [1]     { $people } lubi
  |     [2]     { $people } lubią
  |     [3]     { TAKE(2, $people), "jedna inna osoba" } lubią
  …                               ^----- Expected token: "}"
  |
  |    *[other] { TAKE(2, $people),
  |               "{ PLURAL(DROP(2, $people)) ->
  |                   [1]    jedna inna osoba lubią
  |                   [few]  { LEN(DROP(2, $people)) } inne osoby lubią
  |                  *[many] { LEN(DROP(2, $people)) } innych osób lubi
  |                }"
  |             }
  | } Twoje zdjęcie.

I expected this to compile but maybe the syntax has evolved since that example was written?

Out of curiosity, which tool made the output in the first screenshot in https://github.com/projectfluent/fluent/wiki/Error-Handling#error-reporting

Thanks!

fluent-compiler, an alternative Python implementation, is quite good at statically finding errors in FTL. Using your -brandname example:

>>> from fluent_compiler.compiler import compile_messages                             
>>> from fluent_compiler.resource import FtlResource                                  
>>> compiled = compile_messages('en', [FtlResource.from_string(""" 
... -brand-name = Foo 3000 
... welcome = Welcome, {$name}, to {-brandname}! 
... """)])                                         
>>> compiled.errors                                
[('welcome',
  fluent_compiler.errors.FluentReferenceError('<string>:3:33: Unknown term: -brandname'))]

The errors list here includes both parsing issues (syntax errors), and compilation errors like the one above. As per fluent philosophy, these are not usually fatal errors - it also generates a function for each FTL message. These functions also emit the same errors at runtime as were found at compile time, plus potentially additional ones that can only be detected at runtime. (See docs for compile_messages)

elm-fluent goes even further and does type inference on messages arguments, so that it is capable of detecting type mismatches in these arguments. However, due to the design of Elm and elm-fluent, it has a much stricter policy regarding errors and won’t produce output if errors are found. In addition, it loses some of the “dynamic typing” that FTL normally assumes, and in some cases requires explicit use of NUMBER() or DATETIME() where it wouldn’t be required for other implementations. You should be able to see the kind of inference it does, and its ability to find conflicts, from these tests:

I’m not aware of other tools that do this level of ahead-of-time checking of FTL resources.