The Ocsigen team is happy to announce the release of ocsigen-i18n 5.0, a small but practical internationalisation library for OCaml.
The big change in this release: ocsigen-i18n is no longer tied to Eliom. It can now be used in any OCaml project, with optional extensions for Tyxml and Eliom when you need them.
Ocsigen-i18n was initially written by @sagotch from Be Sport. This release has been made possible thanks to the work of Habib, funded by IRILL.
opam install ocsigen-i18n
How it works
Translations are written in a plain TSV file (one key per line, one column per language):
foo This is a simple key. Ceci est une clé toute simple.
a_human a human un humain
bar I am {{x}}. Je suis {{x}}.
baz There {{{c?are||is an}}} apple{{{c?s||}}} here! Il y a {{{c?des||une}}} pomme{{{c?s||}}} ici !
bu I am {{x %s}} ({{n %d}}). Je suis {{x %s}} ({{n %d}}).
The mini-templating language supports:
{{x}}a string variable~x{{x %d}}a typed variable using the given format specifier{{{c?yes||no}}}an optional boolean?cswitching between two strings
Then in your code, a PPX extension turns [%i18n key] into the right call:
print_endline [%i18n foo];
print_endline [%i18n bar ~x:[%i18n a_human]];
print_endline [%i18n baz ~c:(nb > 1)];
print_endline [%i18n bu ~x:"Jean-Michel" ~n:42];
(* Switching language explicitly *)
print_endline [%i18n foo ~lang:My_i18n.Fr];
The current language is held in a mutable reference you can swap (or replace with an Eliom scoped reference if you need per-session/tab languages).
Dune integration
The tool plugs into Dune very naturally. Generate the OCaml module from your TSV file with a rule:
(rule
(target example_i18n.ml)
(deps example_i18n.tsv)
(action
(run %{bin:ocsigen-i18n} --languages en,fr --input-file %{deps}
--output-file %{target})))
And wire the PPX in your library/executable:
(preprocess (pps ocsigen-i18n -- --default-module Example_i18n))
That’s all you need for a plain OCaml project.
Tyxml support
Pass --tyxml to the generator and the same [%i18n key] expression now produces a list of Tyxml HTML elements instead of a string:
(* Builds an HTML fragment, ready to drop into a Tyxml tree *)
let greeting = [%i18n bar ~x:[%i18n a_human]]
Variables can themselves be lists of HTML nodes, so you can mix translated text with markup naturally:
[%i18n bar ~x:[ txt "Jean-Michel ("
; txt (string_of_int id)
; txt ")" ]]
If you need a plain string in Tyxml mode (for an attribute, for instance), just prefix with S.:
[%i18n S.bar ~x:[%i18n S.a_human]] (* string output *)
Eliom support
For client–server Eliom apps, pass --eliom. The generator emits an .eliom file (so the same translations are available on both sides), implies --tyxml, and adds [@@deriving json] on the language type so you can serialise it across the wire:
(rule
(target example_i18n.eliom)
(deps example_i18n.tsv)
(action
(run %{bin:ocsigen-i18n} --eliom --languages en,fr --input-file %{deps}
--output-file %{target})))
Multiple TSV files
You can split translations across several files. The PPX uses your module path to find the right one:
[%i18n foo] (* default module *)
[%i18n MyI18n.foo] (* MyI18n.foo *)
[%i18n MyI18n.S.bar ~x:[%i18n S.foo]]
Optional --prefix / --suffix flags let you keep call-sites concise ([%i18n Feature.foo] → Pr_Feature_i18n.foo ()).
- Sources: GitHub - ocsigen/ocsigen-i18n: Internationalisation library for Web applications (server and/or client) · GitHub
- Documentation: README on the repo
- Bug reports: Issues · ocsigen/ocsigen-i18n · GitHub
Happy translating!