Added get-message function, with simpler parameters.
Also brought up to date, and fixed some stylistic errors noted by Kondo, and set the default locale to the runtime default locale.
This commit is contained in:
parent
47396aa261
commit
e2e06d0244
21 changed files with 2073 additions and 239 deletions
|
|
@ -1,11 +1,13 @@
|
|||
(ns ^{:doc "Internationalisation."
|
||||
:author "Simon Brooke"}
|
||||
scot.weft.i18n.core
|
||||
scot.weft.i18n.core
|
||||
(:require [clojure.java.io :as io]
|
||||
[clojure.pprint :refer [pprint]]
|
||||
[clojure.string :refer [join]]
|
||||
[instaparse.core :as insta]
|
||||
[taoensso.timbre :as timbre]))
|
||||
[taoensso.timbre :as timbre]
|
||||
[trptr.java-wrapper.locale :as locale])
|
||||
(:import [clojure.lang Keyword]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
|
|
@ -18,6 +20,14 @@
|
|||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
(def ^:dynamic *resource-path*
|
||||
"The default path within the resources space on which translation files
|
||||
will be sought."
|
||||
"i18n")
|
||||
|
||||
(def ^:dynamic *default-language*
|
||||
"The default language to seek."
|
||||
(-> (locale/get-default) locale/to-language-tag))
|
||||
|
||||
(def accept-language-grammar
|
||||
"Grammar for `Accept-Language` headers"
|
||||
|
|
@ -32,12 +42,10 @@
|
|||
Q-SEP := #';\\s*q='
|
||||
Q-VALUE := '1' | #'0.[0-9]+';")
|
||||
|
||||
|
||||
(def parse-accept-language-header
|
||||
"Parse an `Accept-Language` header"
|
||||
(insta/parser accept-language-grammar))
|
||||
|
||||
|
||||
(defn generate-accept-languages
|
||||
"From a `parse-tree` generated by the `language-specifier-grammar`, generate
|
||||
a list of maps each having a `:language` key, a `:preference` key and a
|
||||
|
|
@ -45,47 +53,46 @@
|
|||
{:doc/format :markdown}
|
||||
[parse-tree]
|
||||
(if
|
||||
(nil? parse-tree)
|
||||
(nil? parse-tree)
|
||||
nil
|
||||
(case
|
||||
(first parse-tree)
|
||||
(first parse-tree)
|
||||
:HEADER (generate-accept-languages (second parse-tree))
|
||||
:SPECIFIERS (cons
|
||||
(generate-accept-languages (second parse-tree))
|
||||
(if (>= (count parse-tree) 3)
|
||||
(generate-accept-languages (nth parse-tree 3))))
|
||||
(generate-accept-languages (second parse-tree))
|
||||
(when (>= (count parse-tree) 3)
|
||||
(generate-accept-languages (nth parse-tree 3))))
|
||||
:SPEC-SEP nil
|
||||
:SPECIFIER (assoc
|
||||
(generate-accept-languages (second parse-tree))
|
||||
:preference
|
||||
(if
|
||||
(>= (count parse-tree) 3)
|
||||
(generate-accept-languages (nth parse-tree 3))
|
||||
1))
|
||||
(generate-accept-languages (second parse-tree))
|
||||
:preference
|
||||
(if
|
||||
(>= (count parse-tree) 3)
|
||||
(generate-accept-languages (nth parse-tree 3))
|
||||
1))
|
||||
:LANGUAGE-TAG (if
|
||||
(>= (count parse-tree) 3)
|
||||
(>= (count parse-tree) 3)
|
||||
(assoc
|
||||
(generate-accept-languages (second parse-tree))
|
||||
:qualifier
|
||||
(generate-accept-languages (nth parse-tree 3)))
|
||||
(generate-accept-languages (second parse-tree))
|
||||
:qualifier
|
||||
(generate-accept-languages (nth parse-tree 3)))
|
||||
(generate-accept-languages (second parse-tree)))
|
||||
:PRIMARY-TAG {:language (second parse-tree) :qualifier "*"}
|
||||
:SUB-TAGS (if
|
||||
(>= (count parse-tree) 3)
|
||||
(>= (count parse-tree) 3)
|
||||
(str
|
||||
(generate-accept-languages (second parse-tree))
|
||||
"-"
|
||||
(generate-accept-languages (nth parse-tree 3)))
|
||||
(generate-accept-languages (second parse-tree))
|
||||
"-"
|
||||
(generate-accept-languages (nth parse-tree 3)))
|
||||
(generate-accept-languages (second parse-tree)))
|
||||
:SUB-TAG (second parse-tree)
|
||||
:Q-SEP nil
|
||||
:Q-VALUE (read-string (second parse-tree))
|
||||
;; default
|
||||
(let [formatted-tree (with-out-str (pprint parse-tree))]
|
||||
(do
|
||||
(timbre/error "Unable to parse header.")
|
||||
nil))))
|
||||
|
||||
|
||||
(defn acceptable-languages
|
||||
"Generate an ordered list of acceptable languages, most-preferred first.
|
||||
|
||||
|
|
@ -98,10 +105,10 @@
|
|||
(let [parse-tree (parse-accept-language-header accept-language-header)]
|
||||
(if (vector? parse-tree)
|
||||
(reverse
|
||||
(sort-by
|
||||
:preference
|
||||
(generate-accept-languages
|
||||
parse-tree)))
|
||||
(sort-by
|
||||
:preference
|
||||
(generate-accept-languages
|
||||
parse-tree)))
|
||||
(timbre/error "Failed to parse Accept-Language header '" accept-language-header "':\n" (str parse-tree)))))
|
||||
|
||||
|
||||
|
|
@ -111,7 +118,7 @@
|
|||
[name]
|
||||
(try
|
||||
(slurp (io/resource name))
|
||||
(catch Exception any
|
||||
(catch Exception _
|
||||
(timbre/error (str "Resource at " name " does not exist."))
|
||||
nil)))
|
||||
|
||||
|
|
@ -127,23 +134,23 @@
|
|||
Returns the name of an appropriate file if any is found, else nil."
|
||||
{:doc/format :markdown}
|
||||
[language-spec resource-path]
|
||||
(let [file-path (if
|
||||
(string? language-spec)
|
||||
(let [file-path (when
|
||||
(string? language-spec)
|
||||
(join
|
||||
java.io.File/separator
|
||||
[resource-path (str language-spec ".edn")]))
|
||||
contents (if file-path (slurp-resource file-path))]
|
||||
java.io.File/separator
|
||||
[resource-path (str language-spec ".edn")]))
|
||||
contents (when file-path (slurp-resource file-path))]
|
||||
(cond
|
||||
contents
|
||||
file-path
|
||||
(map? language-spec)
|
||||
(or
|
||||
(find-language-file-name
|
||||
(str (:language language-spec) "-" (:qualifier language-spec))
|
||||
resource-path)
|
||||
(find-language-file-name
|
||||
(:language language-spec)
|
||||
resource-path)))))
|
||||
(find-language-file-name
|
||||
(str (:language language-spec) "-" (:qualifier language-spec))
|
||||
resource-path)
|
||||
(find-language-file-name
|
||||
(:language language-spec)
|
||||
resource-path)))))
|
||||
|
||||
|
||||
(defn raw-get-messages
|
||||
|
|
@ -161,33 +168,49 @@
|
|||
{:doc/format :markdown}
|
||||
[^String accept-language-header ^String resource-path ^String default-locale]
|
||||
(let [file-path (first
|
||||
(remove
|
||||
nil?
|
||||
(map
|
||||
#(find-language-file-name % resource-path)
|
||||
(acceptable-languages accept-language-header))))]
|
||||
(remove
|
||||
nil?
|
||||
(map
|
||||
#(find-language-file-name % resource-path)
|
||||
(acceptable-languages accept-language-header))))]
|
||||
(timbre/debug (str "Found i18n file at '" file-path "'"))
|
||||
(try
|
||||
(read-string
|
||||
(slurp-resource
|
||||
(or
|
||||
file-path
|
||||
(join java.io.File/separator
|
||||
[resource-path
|
||||
(str default-locale ".edn")]))))
|
||||
(slurp-resource
|
||||
(or
|
||||
file-path
|
||||
(join java.io.File/separator
|
||||
[resource-path
|
||||
(str default-locale ".edn")]))))
|
||||
(catch Exception any
|
||||
(timbre/error (str "Failed to load internationalisation because " (.getMessage any)))
|
||||
nil))))
|
||||
|
||||
|
||||
(def get-messages
|
||||
"Return the most acceptable messages collection we have given this `accept-language-header`
|
||||
|
||||
`accept-language-header` should be the value of an RFC2616 `Accept-Language` header;
|
||||
`resource-path` should be the fully-qualified path name of the directory in which
|
||||
* `accept-language-header` should be the value of an RFC2616 `Accept-Language` header;
|
||||
* `resource-path` should be the fully-qualified path name of the directory in which
|
||||
message files are stored;
|
||||
`default-locale` should be a locale specifier to use if no acceptable locale can be
|
||||
* `default-locale` should be a locale specifier to use if no acceptable locale can be
|
||||
identified.
|
||||
|
||||
Returns a map of message keys to strings.; if no useable file is found, returns nil."
|
||||
(memoize raw-get-messages))
|
||||
|
||||
(def get-message
|
||||
"Return the message keyed by this `token` from the most acceptable messages collection
|
||||
we have given this `accept-language-header`.
|
||||
|
||||
* `token` should be a clojure keyword identifying the message to be retrieved;
|
||||
* `accept-language-header` should be the value of an RFC2616 `Accept-Language` header;
|
||||
* `resource-path` should be the fully-qualified path name of the directory in which
|
||||
message files are stored;
|
||||
* `default-locale` should be a locale specifier to use if no acceptable locale can be
|
||||
identified."
|
||||
(fn ([^Keyword token ^String accept-language-header ^String resource-path ^String default-locale]
|
||||
((get-messages accept-language-header resource-path default-locale) token))
|
||||
([^Keyword token ^String accept-language-header]
|
||||
(get-message token accept-language-header *resource-path* *default-language*))
|
||||
([^Keyword token]
|
||||
(get-message token nil *resource-path* *default-language*))))
|
||||
Loading…
Add table
Add a link
Reference in a new issue