Lightweight i18n using DataScript
A lightweight ;-) i18n solution for ClojureScript that uses a DataScript database. It's inspired by Tower, a Clojure/Script i18n & L10n library.
Update: later found the Tongue library (by Nikita Prokopov).
Database example code
1(ns yournamespace.dst
2 (:require [datascript.core :as d]))
3
4;; Database schema (only type ref entities need be specified).
5(def schema {:i18n/dictionary {:db/valueType :db.type/ref}
6 :dictionary/en-US {:db/valueType :db.type/ref}
7 :dictionary/nl-NL {:db/valueType :db.type/ref}})
8
9;; Database connection.
10(def conn (d/create-conn schema))
11
12;; Log database transactions for debug purposes. BEWARE: nil as a value
13;; is not allowed and should not show up in logs!
14#_(d/listen! conn :log
15 (fn [tx-report]
16 (println (str "DST: " (:tx-data tx-report)))))
17
18;; Initial contents of (in-memory) database.
19(defn init!
20 "Initializes database contents."
21 []
22 {:post [(not (nil? %))]}
23
24 (d/transact! conn
25 [{:db/id -1
26 :i18n/key :i18n
27 :i18n/fallback-locale :dictionary/en-US
28 :i18n/dictionary
29 {:db/id -10
30 :dictionary/en-US {:db/id -100
31 :main-title "Title"
32 :main-subtitle "Subtitle"
33 :dictionary/nl-NL {:db/id -101
34 :main-title "Titel"
35 :main-subtitle "Subtitel"
36 }}}]))
37
38;;---------------------
39;; Initialize database.
40(init!)
The i18n code
1(ns yournamespace.i18n
2 (:require [datascript.core :as d]
3 [yournamespace.dst :as dst]))
4
5(defn ^:private _get-lc
6 "Gets locale if it exists, otherwise fallback locale"
7 [lc]
8 {:pre [(not (nil? lc))]
9 :post [(not (nil? %))]}
10
11 (let [lc-is-present?(not
12 (nil?
13 (d/q '[:find ?lc .
14 :in $ ?lci
15 :where [?e :i18n/key] [?e :i18n/dictionary ?d]
16 [?d ?lci ?lc]]
17 @dst/conn lc)))]
18 (if lc-is-present?
19 lc
20 (d/q '[:find ?fl .
21 :in $
22 :where [?e :i18n/key] [?e :i18n/fallback-locale ?fl]]
23 @dst/conn))))
24
25(def ^:private get-lc (memoize _get-lc))
26
27(defn ^:private _t
28 "Returns translation for key based on supplied locale (lc)"
29 [lc key]
30 {:pre [(not (nil? lc)), (not (nil? key))]
31 :post [(not (nil? %))]}
32
33 (let [active-lc (get-lc lc)]
34 (if (nil? active-lc)
35 "***i18n - locale error***"
36 (d/q '[:find ?txt .
37 :in $ ?lc ?key
38 :where [?e :i18n/key] [?e :i18n/dictionary ?dic]
39 [?dic ?lc ?dl]
40 [?dl ?key ?txt]]
41 @dst/conn active-lc key))))
42
43(def t (memoize _t))
Caller code example
1(ns yournamespace.caller
2 (:require [yournamespace.i18n :as i18n]))
3
4(let [lc :dictionary/nl-NL]
5 (println (str "Title: " (i18n/t lc :main-title)))
6 (println (str "Subtitle: " (i18n/t lc :main-subtitle))))