The easiest way to translate a string or variable is to enclose it between {_
and _}
:
{_ var _} {_ "hello" _}
Translates a variable or string.
For example:
{{ var | trans }} {{ "my string" | trans }}
To choose the language to use, set the *CURRENT-LANGUAGE*
variable.
For example:
(let ((djula:*current-language* :es)) (djula:render-template* +translation.html+))
Djula supports several backends for doing translations: cl-locale1, gettext2, translate3.
For translations to work, one of the translation backend systems need to be loaded and configured. Otherwise translate
function will signal an error.
The ASDF systems are: djula-gettext, djula-locale and djula-translate.
Please have a look at the demo and the documentation of those packages to figure out how to use them.
For working with gettext
library, messages to be translated first need to be extracted from Djula templates, generate a Lisp file with Gettext entries from it, and then extracted again from that file with xgettext
library.
Create a Makefile similar to this:
gettext-extract: ## Extract gettext translations from source files sbcl --eval '(ql:quickload :my-project)' --eval '(djula::xgettext-templates :my-project-package (asdf:system-relative-pathname :my-project "i18n/xgettext.lisp"))' --quit find src -iname "*.lisp" | xargs xgettext --from-code=UTF-8 --keyword=_ --output=i18n/my-project.pot --sort-output gettext-edit: ## Edit the extracted gettext translations msgmerge --update i18n/nb.po i18n/my-project.pot xdg-open i18n/nb.po gettext-compile: ## Compile the edited gettext translations msgfmt --output-file=i18n/nb/LC_MESSAGES/ie.mo i18n/nb.po gettext-init: ## Initialize gettext translations msginit --input=i18n/my-project.pot --locale=nb --output=i18n/nb.po
make gettext-extract
first uses the Lisp compiler to extract translated strings from templates. That generates a Lisp file that can be used with xgettext
command line utility. This Lisp file is basically dead code that should not be loaded into the project, just contains Gettext entries that xgettext
utility can understand. Looks like this:
;; THIS FILE IS AUTOGENERATED. DON'T CHANGE BY HAND. USE XGETTEXT-TEMPLATES FUNCTION. (INVOICE-ENGINE::GETTEXT "Activity") (INVOICE-ENGINE::GETTEXT "Address 1") (INVOICE-ENGINE::GETTEXT "Address 2") (INVOICE-ENGINE::GETTEXT "Amount") (INVOICE-ENGINE::GETTEXT "API") (INVOICE-ENGINE::GETTEXT "Balance") (INVOICE-ENGINE::GETTEXT "Close") (INVOICE-ENGINE::GETTEXT "Company") (INVOICE-ENGINE::GETTEXT "Contact person")
Then xgettext
can be used to extract the messages from that file.
Use GETTEXT:PRELOAD-CATALOGS
to inspect current gettext settings:
(gettext:preload-catalogs ;; Tell gettext where to find the .mo files #.(asdf:system-relative-pathname :my-project "locale/"))
And that returns Gettext information:
#S(GETTEXT::CATALOG :KEY ("fr_fr" :LC_MESSAGES "bookshops") :HEADERS ((:PROJECT-ID-VERSION . "PACKAGE VERSION") (:REPORT-MSGID-BUGS-TO . "") (:PO-REVISION-DATE . "YEAR-MO-DA HO:MI +ZONE") (:LAST-TRANSLATOR . "FULL NAME <EMAIL@ADDRESS>") (:LANGUAGE-TEAM . "LANGUAGE <LL@li.org>") (:LANGUAGE . "") (:MIME-VERSION . "1.0") (:CONTENT-TYPE . "text/plain; charset=CHARSET") (:CONTENT-TRANSFER-ENCODING . "8bit")) :NPLURALS 2 :PLURALS-FUNCTION #<FUNCTION (LAMBDA (GETTEXT::N)) {542C149B}> :MESSAGES (SERAPEUM:DICT "Login" '("Se connecter") "Password" '("Mot de passe") "Please login to continue" '("Veuillez vous identifier pour continuer") "Results: ~a. Page: ~a/~a~&" '("Resultats: ~a. Page: ~a/~a~&") "Welcome to OpenBookStore" '("Bienvenue dans OpenBookStore") ) )
Run the following function to reload the translations when developing:
(defun reload-translations () ;; Clear gettext's cache (clrhash gettext::*catalog-cache*) (gettext:preload-catalogs ;; Tell gettext where to find the .mo files #.(asdf:system-relative-pathname :my-project "locale/")))
https://github.com/arielnetworks/cl-locale
https://github.com/copyleft/gettext
https://gitlab.common-lisp.net/dkochmanski/translate