diff --git a/README.md b/README.md index 8f36ea2..3e18d80 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ A Clojure library designed to provide simple interationalisation of user-facing To use this library in your project, add the following leiningen dependency: - [org.clojars.simon_brooke/internationalisation "1.0.4"] + [org.clojars.simon_brooke/internationalisation "1.0.5"] To use it in your namespace, require: diff --git a/doc/intro.md b/doc/intro.md index 309c761..3e18d80 100644 --- a/doc/intro.md +++ b/doc/intro.md @@ -6,7 +6,7 @@ A Clojure library designed to provide simple interationalisation of user-facing To use this library in your project, add the following leiningen dependency: - [org.clojars.simon_brooke/internationalisation "1.0.4"] + [org.clojars.simon_brooke/internationalisation "1.0.5"] To use it in your namespace, require: @@ -62,21 +62,21 @@ For example: (get-message :pipe "de-DE" "i18n" "ru") ``` -So how does this work? When one calls `(get-message token accept-language-header)`, how does it know where to find resources? The answer is that there are two dynamic variables: +So how does this work? When one calls +`(get-message token accept-language-header)`, how does it know where to find resources? The answer is that there is a `*config*` map, with (currently) two significant keys: -* `*resource-path*`, the default path within the resources space on which - translation files will be sought. Initialised to `i18n`. -* `*default-language*`, the language tag for the language to use when no +* `:resource-path`, whose value should be a string representation of the default + path within the resources space on which translation files will be sought. Initialised to `i18n`. +* `:default-language`, the language tag for the language to use when no otherwise suitable language can be identified. Initialised to the default language of the runtime session, so this may well be different on your machine from someone elses running identical software. Thus ```clojure -(binding [*resource-path* "language-files" - *default-language* "en-CA"] - (get-message :pipe "en-GB;q=0.9, fr-FR") -) +(binding [*config* {:resource-path "language-files" + :default-language "en-CA"}] + (get-message :pipe "en-GB;q=0.9, fr-FR")) ``` and ```clojure @@ -116,10 +116,27 @@ In this project you will find two very simple example files, which should give y ## Documentation -Documentation may be generated by running +Documentation can be found here. It may be generated by running lein codox +## Future direction + +It's likely that in future configuration will be extended + +1. To read per-language keys/messages from CSV files; +2. To read per-language keys/messages from database tables; +3. potentially, to read per-language keys/messages from other sources. + +Pull requests implementing any of these things will be welcomed. + +## Deprecated features + +There are still two dynamic configuration variables, `*default-language*` +and `*resource-path*`, but these are now superceded by the `*config*` map, +which is extensible. Consequently, if you are using these configuration +variables in production, you should bind `*config*` to `nil`. + ## License Copyright © 2017 Simon Brooke diff --git a/docs/cloverage/codecov.json b/docs/cloverage/codecov.json index 08624a5..bf9bb45 100644 --- a/docs/cloverage/codecov.json +++ b/docs/cloverage/codecov.json @@ -2,17 +2,19 @@ {"scot/weft/i18n/core.clj": [null, 1, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, null, 1, - null, null, null, null, 1, null, 1, null, 1, null, null, null, null, - null, null, null, null, null, null, null, null, 1, null, 1, null, 1, - null, null, null, null, null, true, 119, null, true, 119, 6, 16, 16, - 16, 8, null, 18, 18, null, 18, 18, 9, null, 19, 19, 15, 15, null, - 15, 4, 20, 15, 15, 0, 0, null, 0, 15, 15, null, 10, null, 0, 0, - null, null, 1, null, null, null, null, null, null, null, null, 6, 6, - 5, 5, null, 5, 5, 1, null, null, 1, null, null, null, 13, 13, null, - 7, null, null, null, 1, null, null, null, null, null, null, null, - null, null, null, 13, 13, 8, 8, 8, 13, 13, 13, 2, 11, 5, 5, 5, 5, 3, - 3, 3, null, null, 1, null, null, null, null, null, null, null, null, - null, null, null, null, null, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 5, 3, - 3, 3, null, 1, null, null, 1, null, null, null, null, null, null, - null, null, null, 1, null, 1, null, null, null, null, null, null, - null, null, null, 1, 3, null, 1, null, 1]}} + null, null, null, null, 1, null, 1, null, 1, null, 1, null, null, 1, + null, null, null, null, null, null, null, null, null, null, null, + null, 1, null, 1, null, 1, null, null, null, null, null, true, 159, + null, true, 159, 11, 22, 22, 22, 9, null, 24, 24, null, 24, 24, 10, + null, 25, 25, 20, 20, null, 20, 5, 26, 20, 20, 0, 0, null, 0, 20, + 20, null, 11, null, 0, 0, null, null, 1, null, null, null, null, + null, null, null, null, 10, 10, 10, 10, null, 10, 10, 0, null, null, + 1, null, null, null, 24, 24, null, 11, null, null, null, 1, null, + null, null, null, null, null, null, null, null, null, 25, 25, 15, + 15, 15, 25, 25, 25, 6, 19, 10, 10, 10, 10, 5, 5, 5, null, null, 1, + null, null, null, null, null, null, null, null, null, null, null, + null, null, 9, 9, 9, 10, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, 9, true, 7, + null, 0, 0, 0, null, null, 2, 2, null, null, 1, null, null, null, + null, null, null, null, null, null, 1, null, 1, null, null, null, + null, null, null, null, null, null, null, 1, 8, 8, null, 7, 7, 7, 7, + null, 5, 5]}} diff --git a/docs/cloverage/coverage.xml b/docs/cloverage/coverage.xml index 51eff4d..1412116 100644 --- a/docs/cloverage/coverage.xml +++ b/docs/cloverage/coverage.xml @@ -1 +1 @@ - \ No newline at end of file + \ No newline at end of file diff --git a/docs/cloverage/index.html b/docs/cloverage/index.html index 7592895..8e6bf1b 100644 --- a/docs/cloverage/index.html +++ b/docs/cloverage/index.html @@ -16,26 +16,26 @@ scot.weft.i18n.core
333
34
-90.74 % + style="width:81.84019370460048%; + float:left;"> 338
75
+81.84 %
86
2
5
-94.62 % -2162393 + style="width:88.67924528301887%; + float:left;"> 94
3
9
+91.51 % +23524106 Totals: -90.74 % +81.84 % -94.62 % +91.51 % diff --git a/docs/cloverage/scot/weft/i18n/core.clj.html b/docs/cloverage/scot/weft/i18n/core.clj.html index 01c1586..8554b08 100644 --- a/docs/cloverage/scot/weft/i18n/core.clj.html +++ b/docs/cloverage/scot/weft/i18n/core.clj.html @@ -77,7 +77,7 @@ 024    "The default path within the resources space on which translation files 
- 025     will be sought." + 025     will be sought. Deprecated, prefer `(:resource-path *config*)`."
026    "i18n") @@ -89,7 +89,7 @@ 028  (def ^:dynamic *default-language*
- 029    "The default language to seek." + 029    "The default language to seek. Deprecated, prefer `(:default-language *config*)`."
030    (-> (locale/get-default) locale/to-language-tag)) @@ -97,560 +97,617 @@ 031  
- - 032  (def accept-language-grammar + + 032  (def ^:dynamic *config*
- 033    "Grammar for `Accept-Language` headers" + 033    "Extensible configuration for i18n." +
+ + 034    {:default-language (-> (locale/get-default) locale/to-language-tag)
- 034    "HEADER := SPECIFIER | SPECIFIERS; -
- - 035    SPECIFIERS:= SPECIFIER | SPECIFIER SPEC-SEP SPECIFIERS; -
- - 036    SPEC-SEP := #',\\s*'; -
- - 037    SPECIFIER := LANGUAGE-TAG | LANGUAGE-TAG Q-SEP Q-VALUE; -
- - 038    LANGUAGE-TAG := PRIMARY-TAG | PRIMARY-TAG '-' SUB-TAGS; -
- - 039    PRIMARY-TAG := #'[a-zA-Z]+'; -
- - 040    SUB-TAGS := SUB-TAG | SUB-TAG '-' SUB-TAGS; -
- - 041    SUB-TAG := #'[a-zA-Z0-9]+'; -
- - 042    Q-SEP := #';\\s*q=' -
- - 043    Q-VALUE := '1' | #'0.[0-9]+';") + 035     :resource-path "i18n"})
- 044   + 036  
- - 045  (def parse-accept-language-header + + 037  (def accept-language-grammar
- 046    "Parse an `Accept-Language` header" + 038    "Grammar for `Accept-Language` headers" +
+ + 039    "HEADER := SPECIFIER | SPECIFIERS; +
+ + 040    SPECIFIERS:= SPECIFIER | SPECIFIER SPEC-SEP SPECIFIERS; +
+ + 041    SPEC-SEP := #',\\s*'; +
+ + 042    SPECIFIER := LANGUAGE-TAG | LANGUAGE-TAG Q-SEP Q-VALUE; +
+ + 043    LANGUAGE-TAG := PRIMARY-TAG | PRIMARY-TAG '-' SUB-TAGS; +
+ + 044    PRIMARY-TAG := #'[a-zA-Z]+'; +
+ + 045    SUB-TAGS := SUB-TAG | SUB-TAG '-' SUB-TAGS; +
+ + 046    SUB-TAG := #'[a-zA-Z0-9]+'; +
+ + 047    Q-SEP := #';\\s*q=' +
+ + 048    Q-VALUE := '1' | #'0.[0-9]+';") +
+ + 049   +
+ + 050  (def parse-accept-language-header +
+ + 051    "Parse an `Accept-Language` header"
- 047    (insta/parser accept-language-grammar)) + 052    (insta/parser accept-language-grammar))
- 048   + 053  
- 049  (defn generate-accept-languages + 054  (defn generate-accept-languages
- 050    "From a `parse-tree` generated by the `language-specifier-grammar`, generate + 055    "From a `parse-tree` generated by the `language-specifier-grammar`, generate
- 051    a list of maps each having a `:language` key, a `:preference` key and a + 056    a list of maps each having a `:language` key, a `:preference` key and a
- 052    `:qualifier` key." + 057    `:qualifier` key."
- 053    {:doc/format :markdown} + 058    {:doc/format :markdown}
- 054    [parse-tree] + 059    [parse-tree]
- 055    (if + 060    (if
- 056     (nil? parse-tree) + 061     (nil? parse-tree)
- 057      nil + 062      nil
- 058      (case + 063      (case
- 059       (first parse-tree) + 064       (first parse-tree)
- 060        :HEADER (generate-accept-languages (second parse-tree)) + 065        :HEADER (generate-accept-languages (second parse-tree))
- 061        :SPECIFIERS (cons + 066        :SPECIFIERS (cons
- 062                     (generate-accept-languages (second parse-tree)) + 067                     (generate-accept-languages (second parse-tree))
- 063                     (when (>= (count parse-tree) 3) + 068                     (when (>= (count parse-tree) 3)
- 064                       (generate-accept-languages (nth parse-tree 3)))) + 069                       (generate-accept-languages (nth parse-tree 3))))
- 065        :SPEC-SEP nil + 070        :SPEC-SEP nil
- 066        :SPECIFIER (assoc + 071        :SPECIFIER (assoc
- 067                    (generate-accept-languages (second parse-tree)) + 072                    (generate-accept-languages (second parse-tree))
- 068                    :preference + 073                    :preference
- 069                    (if + 074                    (if
- 070                     (>= (count parse-tree) 3) + 075                     (>= (count parse-tree) 3)
- 071                      (generate-accept-languages (nth parse-tree 3)) + 076                      (generate-accept-languages (nth parse-tree 3))
- 072                      1)) + 077                      1))
- 073        :LANGUAGE-TAG (if + 078        :LANGUAGE-TAG (if
- 074                       (>= (count parse-tree) 3) + 079                       (>= (count parse-tree) 3)
- 075                        (assoc + 080                        (assoc
- 076                         (generate-accept-languages (second parse-tree)) + 081                         (generate-accept-languages (second parse-tree))
- 077                         :qualifier + 082                         :qualifier
- 078                         (generate-accept-languages (nth parse-tree 3))) + 083                         (generate-accept-languages (nth parse-tree 3)))
- 079                        (generate-accept-languages (second parse-tree))) + 084                        (generate-accept-languages (second parse-tree)))
- 080        :PRIMARY-TAG {:language (second parse-tree) :qualifier "*"} + 085        :PRIMARY-TAG {:language (second parse-tree) :qualifier "*"}
- 081        :SUB-TAGS (if + 086        :SUB-TAGS (if
- 082                   (>= (count parse-tree) 3) + 087                   (>= (count parse-tree) 3)
- 083                    (str + 088                    (str
- 084                     (generate-accept-languages (second parse-tree)) + 089                     (generate-accept-languages (second parse-tree))
- 085                     "-" + 090                     "-"
- 086                     (generate-accept-languages (nth parse-tree 3))) + 091                     (generate-accept-languages (nth parse-tree 3)))
- 087                    (generate-accept-languages (second parse-tree))) + 092                    (generate-accept-languages (second parse-tree)))
- 088        :SUB-TAG (second parse-tree) + 093        :SUB-TAG (second parse-tree)
- 089        :Q-SEP nil + 094        :Q-SEP nil
- 090        :Q-VALUE (read-string (second parse-tree)) + 095        :Q-VALUE (read-string (second parse-tree))
- 091        ;; default + 096        ;; default
- 092        (do + 097        (do
- 093          (timbre/error "Unable to parse header.") + 098          (timbre/error "Unable to parse header.")
- 094          nil)))) -
- - 095   -
- - 096  (defn acceptable-languages -
- - 097    "Generate an ordered list of acceptable languages, most-preferred first. -
- - 098   -
- - 099    * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header. + 099          nil))))
100  
- - 101    Returns a list of maps as generated by `generate-accept-languages`, in descending order + + 101  (defn acceptable-languages
- 102    of preference." + 102    "Generate an ordered list of acceptable languages, most-preferred first. +
+ + 103  
- 103    {:doc/format :markdown} + 104    * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header. +
+ + 105  
- 104    [accept-language-header] + 106    Returns a list of maps as generated by `generate-accept-languages`, in descending order +
+ + 107    of preference." +
+ + 108    {:doc/format :markdown} +
+ + 109    [accept-language-header]
- 105    (let [parse-tree (parse-accept-language-header accept-language-header)] + 110    (let [parse-tree (parse-accept-language-header accept-language-header)]
- 106      (if (vector? parse-tree) + 111      (if (vector? parse-tree)
- 107        (reverse + 112        (reverse
- 108         (sort-by + 113         (sort-by
- 109          :preference + 114          :preference
- 110          (generate-accept-languages + 115          (generate-accept-languages
- 111           parse-tree))) + 116           parse-tree)))
- - 112        (timbre/error "Failed to parse Accept-Language header '" accept-language-header "':\n" (str parse-tree))))) + + 117        (timbre/error "Failed to parse Accept-Language header '" accept-language-header "':\n" (str parse-tree)))))
- 113   + 118  
- 114   + 119  
- 115  (defn slurp-resource + 120  (defn slurp-resource
- 116    "Slurp the resource of this name and return its contents as a string; but if it doesn't + 121    "Slurp the resource of this name and return its contents as a string; but if it doesn't
- 117     exist log the fact and return nil, rather than throwing an exception." + 122     exist log the fact and return nil, rather than throwing an exception."
- 118    [name] + 123    [name]
- 119    (try + 124    (try
- 120      (slurp (io/resource name)) + 125      (slurp (io/resource name))
- 121      (catch Exception _ + 126      (catch Exception _
- 122        (timbre/error (str "Resource at " name " does not exist.")) + 127        (timbre/warn (str "Resource at " name " does not exist."))
- 123        nil))) + 128        nil)))
- 124   + 129  
- 125   + 130  
- 126  (defn find-language-file-name + 131  (defn find-language-file-name
- 127    "Find the name of a messages file on this resource path which matches this `language-spec`. -
- - 128   -
- - 129    * `language-spec` should be either a map as generated by `generate-accept-languages`, or -
- - 130    else a string; -
- - 131    * `resource-path` should be the path name of the directory in which message files are stored, -
- - 132    within the resources on the classpath. + 132    "Find the name of a messages file on this resource path which matches this `language-spec`.
133  
- 134    Returns the name of an appropriate file if any is found, else nil." + 134    * `language-spec` should be either a map as generated by `generate-accept-languages`, or
- 135    {:doc/format :markdown} + 135    else a string;
- 136    [language-spec resource-path] + 136    * `resource-path` should be the path name of the directory in which message files are stored, +
+ + 137    within the resources on the classpath. +
+ + 138   +
+ + 139    Returns the name of an appropriate file if any is found, else nil." +
+ + 140    {:doc/format :markdown} +
+ + 141    [language-spec resource-path]
- 137    (let [file-path (when + 142    (let [file-path (when
- 138                     (string? language-spec) + 143                     (string? language-spec)
- 139                      (join + 144                      (join
- 140                       java.io.File/separator + 145                       java.io.File/separator
- 141                       [resource-path (str language-spec ".edn")])) + 146                       [resource-path (str language-spec ".edn")]))
- 142          contents (when file-path (slurp-resource file-path))] + 147          contents (when file-path (slurp-resource file-path))]
- 143      (cond + 148      (cond
- 144        contents + 149        contents
- 145        file-path + 150        file-path
- 146        (map? language-spec) + 151        (map? language-spec)
- 147        (or + 152        (or
- 148         (find-language-file-name + 153         (find-language-file-name
- 149          (str (:language language-spec) "-" (:qualifier language-spec)) + 154          (str (:language language-spec) "-" (:qualifier language-spec))
- 150          resource-path) + 155          resource-path)
- 151         (find-language-file-name + 156         (find-language-file-name
- 152          (:language language-spec) + 157          (:language language-spec)
- 153          resource-path))))) + 158          resource-path)))))
- 154   -
- - 155   -
- - 156  (defn raw-get-messages -
- - 157    "Return the most acceptable messages collection we have given this `accept-language-header`. -
- - 158    Do not use this function directly, use the memoized variant `get-messages`, as performance -
- - 159    will be very much better. + 159  
160  
- - 161    * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header; + + 161  (defn raw-get-messages
- 162    * `resource-path` should be the fully-qualified path name of the directory in which + 162    "Return the most acceptable messages collection we have given this `accept-language-header`.
- 163    message files are stored; + 163    Do not use this function directly, use the memoized variant `get-messages`, as performance
- 164    * `default-locale` should be a locale specifier to use if no acceptable locale can be -
- - 165    identified. + 164    will be very much better.
- 166   + 165  
- 167    Returns a map of message keys to strings; if no useable file is found, returns nil." + 166    * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header;
- 168    {:doc/format :markdown} + 167    * `resource-path` should be the fully-qualified path name of the directory in which
- 169    [^String accept-language-header ^String resource-path ^String default-locale] + 168    message files are stored; +
+ + 169    * `default-locale` should be a locale specifier to use if no acceptable locale can be +
+ + 170    identified. +
+ + 171   +
+ + 172    Returns a map of message keys to strings; if no useable file is found, returns nil." +
+ + 173    {:doc/format :markdown} +
+ + 174    [^String accept-language-header ^String resource-path ^String default-locale]
- 170    (let [file-path (first -
- - 171                     (remove + 175    (let [file-paths (remove
- 172                      nil? + 176                      empty?
- 173                      (map + 177                      (map
- 174                       #(find-language-file-name % resource-path) + 178                       #(find-language-file-name % resource-path)
- 175                       (acceptable-languages accept-language-header))))] + 179                       (acceptable-languages accept-language-header)))
- - 176      (timbre/debug (str "Found i18n file at '" file-path "'")) + + 180          default-path (join java.io.File/separator
- 177      (try -
- - 178        (read-string -
- - 179         (slurp-resource -
- - 180          (or -
- - 181           file-path -
- - 182           (join java.io.File/separator -
- - 183                 [resource-path + 181                 [resource-path
- 184                  (str default-locale ".edn")])))) + 182                  (str default-locale ".edn")])
- - 185        (catch Exception any + + 183          paths (concat file-paths (list default-path))
- - 186          (timbre/error (str "Failed to load internationalisation because " (.getMessage any))) -
- - 187          nil)))) -
- - 188   -
- - 189  (def get-messages -
- - 190    "Return the most acceptable messages collection we have given this `accept-language-header` -
- - 191   -
- - 192    * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header; -
- - 193    * `resource-path` should be the fully-qualified path name of the directory in which -
- - 194    message files are stored; -
- - 195    * `default-locale` should be a locale specifier to use if no acceptable locale can be -
- - 196    identified. -
- - 197   -
- - 198    Returns a map of message keys to strings.; if no useable file is found, returns nil." + + 184          text (first 
- 199    (memoize raw-get-messages)) + 185                (remove empty? +
+ + 186                       (map +
+ + 187                        slurp-resource +
+ + 188                        paths)))] +
+ + 189      (if text +
+ + 190        (try +
+ + 191          (read-string text) +
+ + 192          (catch Exception any +
+ + 193            (timbre/error  "Failed to load internationalisation because " +
+ + 194                           (.getName (.getClass any)) +
+ + 195                           (.getMessage any)) +
+ + 196            nil)) +
+ + 197        ;; else +
+ + 198        (doall +
+ + 199          (timbre/error "No valid i18n files found, not even default. Tried" paths) +
+ + 200          nil))))
- 200   + 201  
- 201  (def get-message + 202  (def get-messages
- 202    "Return the message keyed by this `token` from the most acceptable messages collection   + 203    "Return the most acceptable messages collection we have given this `accept-language-header` +
+ + 204  
- 203     we have given this `accept-language-header`. + 205    * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header;
- 204      + 206    * `resource-path` should be the fully-qualified path name of the directory in which
- 205     * `token` should be a clojure keyword identifying the message to be retrieved; + 207    message files are stored;
- 206     * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header; + 208    * `default-locale` should be a locale specifier to use if no acceptable locale can be
- 207     * `resource-path` should be the fully-qualified path name of the directory in which + 209    identified. +
+ + 210  
- 208       message files are stored; + 211    Returns a map of message keys to strings.; if no useable file is found, returns nil."
- - 209     * `default-locale` should be a locale specifier to use if no acceptable locale can be + + 212    (memoize raw-get-messages))
- - 210       identified." + + 213  
- 211    (fn ([^Keyword token ^String accept-language-header ^String resource-path ^String default-locale] -
- - 212         ((get-messages accept-language-header resource-path default-locale) token)) + 214  (def get-message
- 213      ([^Keyword token ^String accept-language-header] -
- - 214       (get-message token accept-language-header *resource-path* *default-language*)) + 215    "Return the message keyed by this `token` from the most acceptable messages collection  
- 215      ([^Keyword token] + 216     we have given this `accept-language-header`, if passed, or the current default language 
- - 216       (get-message token nil *resource-path* *default-language*)))) + + 217     otherwise. If no message is found, return the token. +
+ + 218      +
+ + 219     * `token` should be a clojure keyword identifying the message to be retrieved; +
+ + 220     * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header; +
+ + 221     * `resource-path` should be the fully-qualified path name of the directory in which +
+ + 222       message files are stored; +
+ + 223     * `default-locale` should be a locale specifier to use if no acceptable locale can be +
+ + 224       identified." +
+ + 225    (fn ([^Keyword token ^String accept-language-header ^String resource-path ^String default-locale] +
+ + 226         (let [message (token (get-messages accept-language-header resource-path default-locale))] +
+ + 227           (or message (name token)))) +
+ + 228      ([^Keyword token ^String accept-language-header] +
+ + 229       (get-message token  +
+ + 230                    accept-language-header  +
+ + 231                    (or (:resource-path *config*) *resource-path*)  +
+ + 232                    (or (:default-language *config*) *default-language*))) +
+ + 233      ([^Keyword token] +
+ + 234       (get-message token  +
+ + 235                    (or (:default-language *config*) *default-language*)))))
diff --git a/docs/codox/index.html b/docs/codox/index.html index 9250ac2..95276b2 100644 --- a/docs/codox/index.html +++ b/docs/codox/index.html @@ -1,3 +1,3 @@ -Internationalisation 1.0.3-SNAPSHOT

Internationalisation 1.0.3-SNAPSHOT

Released under the Eclipse Public License

Internationalisation library for Clojure.

Installation

To install, add the following dependency to your project or build file:

[org.clojars.simon_brooke/internationalisation "1.0.3-SNAPSHOT"]

Topics

Namespaces

\ No newline at end of file +Internationalisation 1.0.5

Internationalisation 1.0.5

Released under the Eclipse Public License

Internationalisation library for Clojure.

Installation

To install, add the following dependency to your project or build file:

[org.clojars.simon_brooke/internationalisation "1.0.5"]

Topics

Namespaces

\ No newline at end of file diff --git a/docs/codox/intro.html b/docs/codox/intro.html index a580553..8fa9f1c 100644 --- a/docs/codox/intro.html +++ b/docs/codox/intro.html @@ -1,10 +1,10 @@ -internationalisation

internationalisation

+internationalisation

internationalisation

A Clojure library designed to provide simple interationalisation of user-facing messages.

Usage

To use this library in your project, add the following leiningen dependency:

-
[org.clojars.simon_brooke/internationalisation "1.0.4"]
+
[org.clojars.simon_brooke/internationalisation "1.0.5"]
 

To use it in your namespace, require:

[scot.weft.i18n.core :refer [get-message get-messages]]
@@ -45,16 +45,15 @@
 
 (get-message :pipe "de-DE" "i18n" "ru")
 
-

So how does this work? When one calls (get-message token accept-language-header), how does it know where to find resources? The answer is that there are two dynamic variables:

+

So how does this work? When one calls (get-message token accept-language-header), how does it know where to find resources? The answer is that there is a *config* map, with (currently) two significant keys:

    -
  • *resource-path*, the default path within the resources space on which translation files will be sought. Initialised to i18n.
  • -
  • *default-language*, the language tag for the language to use when no otherwise suitable language can be identified. Initialised to the default language of the runtime session, so this may well be different on your machine from someone elses running identical software.
  • +
  • :resource-path, whose value should be a string representation of the default path within the resources space on which translation files will be sought. Initialised to i18n.
  • +
  • :default-language, the language tag for the language to use when no otherwise suitable language can be identified. Initialised to the default language of the runtime session, so this may well be different on your machine from someone elses running identical software.

Thus

-
(binding [*resource-path* "language-files"
-          *default-language* "en-CA"]
-    (get-message :pipe "en-GB;q=0.9, fr-FR")
-)
+
(binding [*config* {:resource-path "language-files"
+                    :default-language "en-CA"}]
+    (get-message :pipe "en-GB;q=0.9, fr-FR"))
 

and

(get-message :pipe "en-GB;q=0.9, fr-FR" "language-files" "en-CA")
@@ -79,9 +78,19 @@
 {:pipe "Ceci n'est pas une pipe."}
 

Documentation

-

Documentation may be generated by running

+

Documentation can be found here. It may be generated by running

lein codox
 
+

Future direction

+

It’s likely that in future configuration will be extended

+
    +
  1. To read per-language keys/messages from CSV files;
  2. +
  3. To read per-language keys/messages from database tables;
  4. +
  5. potentially, to read per-language keys/messages from other sources.
  6. +
+

Pull requests implementing any of these things will be welcomed.

+

Deprecated features

+

There are still two dynamic configuration variables, *default-language* and *resource-path*, but these are now superceded by the *config* map, which is extensible. Consequently, if you are using these configuration variables in production, you should bind *config* to nil.

License

Copyright © 2017 Simon Brooke

Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.

\ No newline at end of file diff --git a/docs/codox/scot.weft.i18n.core.html b/docs/codox/scot.weft.i18n.core.html index 085edec..d0ca178 100644 --- a/docs/codox/scot.weft.i18n.core.html +++ b/docs/codox/scot.weft.i18n.core.html @@ -1,30 +1,30 @@ -scot.weft.i18n.core documentation

scot.weft.i18n.core

Internationalisation.

*default-language*

dynamic

The default language to seek.

*resource-path*

dynamic

The default path within the resources space on which translation files will be sought.

accept-language-grammar

Grammar for Accept-Language headers

acceptable-languages

(acceptable-languages accept-language-header)

Generate an ordered list of acceptable languages, most-preferred first.

+scot.weft.i18n.core documentation

scot.weft.i18n.core

Internationalisation.

*config*

dynamic

Extensible configuration for i18n.

*default-language*

dynamic

The default language to seek. Deprecated, prefer (:default-language *config*).

*resource-path*

dynamic

The default path within the resources space on which translation files will be sought. Deprecated, prefer (:resource-path *config*).

accept-language-grammar

Grammar for Accept-Language headers

acceptable-languages

(acceptable-languages accept-language-header)

Generate an ordered list of acceptable languages, most-preferred first.

  • accept-language-header should be the value of an RFC2616 Accept-Language header.
-

Returns a list of maps as generated by generate-accept-languages, in descending order of preference.

find-language-file-name

(find-language-file-name language-spec resource-path)

Find the name of a messages file on this resource path which matches this language-spec.

+

Returns a list of maps as generated by generate-accept-languages, in descending order of preference.

find-language-file-name

(find-language-file-name language-spec resource-path)

Find the name of a messages file on this resource path which matches this language-spec.

  • language-spec should be either a map as generated by generate-accept-languages, or else a string;
  • resource-path should be the path name of the directory in which message files are stored, within the resources on the classpath.
-

Returns the name of an appropriate file if any is found, else nil.

generate-accept-languages

(generate-accept-languages parse-tree)

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 :qualifier key.

get-message

Return the message keyed by this token from the most acceptable messages collection
we have given this accept-language-header.

+

Returns the name of an appropriate file if any is found, else nil.

generate-accept-languages

(generate-accept-languages parse-tree)

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 :qualifier key.

get-message

Return the message keyed by this token from the most acceptable messages collection
we have given this accept-language-header, if passed, or the current default language otherwise. If no message is found, return the token.

  • 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.
  • -

get-messages

Return the most acceptable messages collection we have given this accept-language-header

+

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 message files are stored;
  • 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.

parse-accept-language-header

Parse an Accept-Language header

raw-get-messages

(raw-get-messages accept-language-header resource-path default-locale)

Return the most acceptable messages collection we have given this accept-language-header. Do not use this function directly, use the memoized variant get-messages, as performance will be very much better.

+

Returns a map of message keys to strings.; if no useable file is found, returns nil.

parse-accept-language-header

Parse an Accept-Language header

raw-get-messages

(raw-get-messages accept-language-header resource-path default-locale)

Return the most acceptable messages collection we have given this accept-language-header. Do not use this function directly, use the memoized variant get-messages, as performance will be very much better.

  • 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.
-

Returns a map of message keys to strings; if no useable file is found, returns nil.

slurp-resource

(slurp-resource name)

Slurp the resource of this name and return its contents as a string; but if it doesn’t exist log the fact and return nil, rather than throwing an exception.

\ No newline at end of file +

Returns a map of message keys to strings; if no useable file is found, returns nil.

slurp-resource

(slurp-resource name)

Slurp the resource of this name and return its contents as a string; but if it doesn’t exist log the fact and return nil, rather than throwing an exception.

\ No newline at end of file diff --git a/project.clj b/project.clj index 185748f..576e379 100644 --- a/project.clj +++ b/project.clj @@ -1,4 +1,4 @@ -(defproject org.clojars.simon_brooke/internationalisation "1.0.5-SNAPSHOT" +(defproject org.clojars.simon_brooke/internationalisation "1.0.5" :cloverage {:output "docs/cloverage" :codecov? true :emma-xml? true}