Custom functions
You can add functions to the ones available to FTL authors by passing a
functions dictionary to the FluentBundle constructor or to
compile_messages:
>>> import platform
>>> def os_name():
... """Returns linux/mac/windows/other"""
... return {'Linux': 'linux',
... 'Darwin': 'mac',
... 'Windows': 'windows'}.get(platform.system(), 'other')
>>> bundle = FluentBundle.from_string(
... 'en-US',
... """
... welcome = { OS() ->
... [linux] Welcome to Linux
... [mac] Welcome to Mac
... [windows] Welcome to Windows
... *[other] Welcome
... }
... """,
... functions={'OS': os_name})
>>> print(bundle.format('welcome')[0]
Welcome to Linux
These functions can accept positional and keyword arguments, like the NUMBER
and DATETIME builtins. They must accept the following types of objects
passed as arguments:
strfluent_compiler.types.FluentTypesubclasses, namely:FluentNumber-int,floatorDecimalobjects passed in externally, or expressed as literals, are wrapped in these. Note that these objects also subclass builtinint,floatorDecimal, so can be used as numbers in the normal way.FluentDateType-dateordatetimeobjects passed in are wrapped in these. Again, these classes also subclassdateordatetime, and can be used as such.FluentNone- in error conditions, such as a message referring to an argument that hasn’t been passed in, objects of this type are passed in.
Custom functions should not raise exceptions, but return FluentNone instances to
indicate an error or missing data. Otherwise they should return strings,
or instances of a FluentType subclass as above. Returned numbers and
datetimes should be converted to FluentNumber or FluentDateType
subclasses using fluent.types.fluent_number and fluent.types.fluent_date
respectively.
If your function does raise exceptions fluent_compiler will not catch them -
they will propagate up and cause the formatting of the message to raise that
exception. This is intended behaviour, to aid debugging of the broken custom
function.
The type signatures of custom functions are checked before they are used, to
ensure the right the number of positional arguments are used, and only available
keyword arguments are used - otherwise a TypeError will be appended to the
errors list. Using *args or **kwargs to allow any number of
positional or keyword arguments is supported, but you should ensure that your
function actually does allow all positional or keyword arguments.
If you want to override the detected type signature (for example, to limit the
arguments that can be used in an FTL file, or to provide a proper signature for
a function that has a signature using *args and **kwargs but is more
restricted in reality), you can add an ftl_arg_spec attribute to the
function. The value should be a two-tuple containing 1) an integer specifying
the number of positional arguments, and 2) a list of allowed keyword arguments.
For example, for a custom function my_func the following will stop the
restricted keyword argument from being used from FTL files, while allowing
allowed, and will require that a single positional argument is passed:
def my_func(arg1, allowed=None, restricted=None):
pass
my_func.ftl_arg_spec = (1, ['allowed'])
The Fluent spec allows keyword arguments with hyphens (-) in them. These are
not valid identifiers in Python, so if you need to a custom function to accept
keyword arguments like this, you will have to use **kwargs syntax e.g.:
def my_func(kwarg1=None, **kwargs):
kwarg_with_hyphens = kwargs.pop('kwarg-with-hyphens', None)
# etc.
my_func.ftl_arg_spec = (0, ['kwarg1', 'kwarg-with-hyphens'])