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``: .. code-block:: python >>> 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: - ``str`` - ``fluent_compiler.types.FluentType`` subclasses, namely: - ``FluentNumber`` - ``int``, ``float`` or ``Decimal`` objects passed in externally, or expressed as literals, are wrapped in these. Note that these objects also subclass builtin ``int``, ``float`` or ``Decimal``, so can be used as numbers in the normal way. - ``FluentDateType`` - ``date`` or ``datetime`` objects passed in are wrapped in these. Again, these classes also subclass ``date`` or ``datetime``, 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: .. code-block:: python 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.: .. code-block:: python 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'])