safflower

A Statically Allocated Format-Friendly Localiser

crates.io docs.rs github.com

Purpose

aims to be a easy-to-use localising crate for rust projects, providing the simplest possible way to interface with text in multiple languages or dialects (or any other kind of variation).

Usage

By using the load! macro, the source text is loaded in all variations. The simplest way to access it is then through the text! macro, which automatically picks the variant corresponding to the currently set locale.

Features

Minimal setup

The load! macro generates code that statically allocates the text you want to use. This means that once your code has been compiled nothing has to be checked for errors, and nothing has to be loaded. There is some slight overhead for this, of course, see benchmarks below. Specifically, if your code compiles, it is guaranteed that:

Flexibility and limitations

The aim is to be as flexible as possible, while requiring the least amount of time and user effort. The intented use is for text localisation, i.e. providing UI text in different languages or dialects, but you might find a different use.
Often having more options means weaker assumptions, and so, for instance, it is not possible to not have any locales. It is also not possible to have a vector of strings as a value, though that might change if deemed useful and uncostly.
If there's any feature you feel is missing, please submit an issue on GitHub or drop an email.

Formattability

The text can also be formatted, following rust's usual format! syntax. checks that all locale variants in an entry use the same number of parameters, and with the same names. It does not check how you format them however.
Due to practical limitations, unnamed or numbered parameters like {} or {0} are given names when parsed, starting at `arg0` and counting upwards. This means a line containing both an unnamed or numbered parameter and a parameter named e.g. `arg0` will cause a conflict. Since this is a rather minor issue, and one should probably avoid not naming parameters in the first place, there is no plan to do anything about it; the effort and possible computational overhead is not worth it.

Hierarchy

Sometimes it may be nice (or necessary) to split text entries up. There are two, non-exclusive ways to do so.
The first is to use separate files with !include, e.g. to have only one locale per file

# file main.txt
!include en.txt it.txt

# file en.txt
!locales en
greet: en "Hi!"

# file it.txt
!locales it
greet: it "Ciao!"


The second is to create scopes with !scope, e.g. to group texts by use case.

!locale en it

!scope menu-items
new-file:
  en "New"
  it "Nuovo"

!scope errors
empty-field:
  en "\"{field}\" cannot be empty"
  it "\"{field}\" non può essere vuoto"

It should be noted that including a file after creating a scope will import that file into that scope. Every scope also re-imports the Locale enum, so that it is available without traversing the whole path.

Benchmarks

Coming soon...