j18n / src / main / clojure / j18n / core.clj

(ns ^{:doc
  "Provide some Clojure sugar for internationalisation using the usual
  Java infrastructure of ResourceBundles."
      :author "Meikel Brandmeyer <>"}

(def ^{:private true} bundles (atom {}))

(defmacro defbundle
  "Creates a new bundle with given name. Attaches the Var to the metadata
  of the namespace. If several bundles are defined in one namespace, the
  last one wins as “the official bundle” for that namespace."
  ([bundle-name] `(defbundle ~bundle-name (name (ns-name *ns*))))
  ([bundle-name package]
      (declare ~bundle-name)
      (alter-meta! *ns* assoc ::bundle (symbol (name (ns-name *ns*)) ~(name bundle-name)))
      (swap! @#'bundles assoc (var ~bundle-name) ~package)
      (var ~bundle-name))))

(defn init-bundles!
  "Initialise all bundles defined with defbundle. This should only be called
  once during application startup and initialisation."
  (doseq [[v p] @bundles]
    (alter-var-root v (constantly (ResourceBundle/getBundle p)))))

(defn translate*
  "Translate the given message in the given bundle. This is basically a
  wrapper around ResourceBundle/getString."
  [^String message ^ResourceBundle bundle]
  (.getString bundle message))

(defmacro translate
  "Translate the given message. Message may be a qualified or non-qualified
  keyword or a String. The message is looked up in the given bundle. If no
  bundle is given, the official bundle of the namespace of the qualified
  keyword will be used. If the keyword is not qualified or the message key
  is given as String, the official bundle of the current namespace will be
   {:pre [(or (string? message) (keyword? message))]}
   (let [bundle (if (keyword? message)
                  (if-let [nspace (namespace message)]
                    (::bundle (meta (the-ns (symbol nspace))))
                    (::bundle (meta *ns*)))
                  (::bundle (meta *ns*)))]
     `(translate ~message ~bundle)))
  ([message bundle]
   {:pre [(or (string? message) (keyword? message))]}
   `(translate* ~(if (keyword? message) (name message) message) ~bundle)))
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.