Merge branch 'develop'
This commit is contained in:
commit
e2fa7b98bb
78
README.md
78
README.md
|
@ -6,28 +6,92 @@ 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.0"]
|
||||
[org.clojars.simon_brooke/internationalisation "1.0.4"]
|
||||
|
||||
To use it in your namespace, require:
|
||||
|
||||
[scot.weft.i18n.core :refer [get-messages]]
|
||||
[scot.weft.i18n.core :refer [get-message get-messages]]
|
||||
|
||||
There is only one function you should need to use:
|
||||
There is only two functions you may need to use:
|
||||
|
||||
### get-messages
|
||||
|
||||
```clojure
|
||||
(get-messages accept-language-header resource-path default-locale)
|
||||
```
|
||||
Return the most acceptable messages collection we have given this `accept-language-header`.
|
||||
|
||||
Return the most acceptable messages collection we have given this accept-language-header. Use this function instrad of the unmemoized variant raw-get-messages, as performance will be very much better.
|
||||
* `accept-language-header` should be a string representing the value of an RFC 2616 Accept-Language header;
|
||||
* `resource-path` should be a string representing the fully-qualified path name of the directory in which message files are stored;
|
||||
* `default-locale` should be a string representing a locale specifier to use if no acceptable locale can be identified.
|
||||
|
||||
* `accept-language-header` should be the value of an RFC 2616 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.
|
||||
For example:
|
||||
```clojure
|
||||
(get-messages "en-GB;q=0.9, fr-FR" "i18n" "en-GB")
|
||||
```
|
||||
|
||||
Returns a map of message keys to strings.
|
||||
|
||||
See [RFC 2616](https://www.ietf.org/rfc/rfc2616.txt).
|
||||
|
||||
**NOTE THAT** `get-messages` is [memoized](https://clojuredocs.org/clojure.core/memoize) resulting in faster response when called repeatedly with similar arguments.
|
||||
|
||||
### get-message
|
||||
|
||||
A wrapper around `get-messages` to resolve a single, particular message.
|
||||
```clojure
|
||||
(get-message token)
|
||||
|
||||
(get-message token accept-language-header)
|
||||
|
||||
(get-message token accept-language-header resource-path default-locale)
|
||||
```
|
||||
|
||||
where
|
||||
|
||||
* `token` is the Clojure [keyword](https://clojuredocs.org/clojure.core/keyword) which identifies the particular message you want to retrieve;
|
||||
|
||||
and all the other arguments are as defined above.
|
||||
|
||||
For example:
|
||||
```clojure
|
||||
(get-message :pipe)
|
||||
|
||||
(get-message :pipe "en-GB;q=0.9, fr-FR")
|
||||
|
||||
(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:
|
||||
|
||||
* `*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.
|
||||
|
||||
Thus
|
||||
```clojure
|
||||
(binding [*resource-path* "language-files"
|
||||
*default-language* "en-CA"]
|
||||
(get-message :pipe "en-GB;q=0.9, fr-FR")
|
||||
)
|
||||
```
|
||||
and
|
||||
```clojure
|
||||
(get-message :pipe "en-GB;q=0.9, fr-FR" "language-files" "en-CA")
|
||||
```
|
||||
are effectively the same.
|
||||
|
||||
The intelligent reader will note that if one calls
|
||||
```clojure
|
||||
(get-message :pipe)
|
||||
```
|
||||
there's no mechanism to set the `accept-header`. This is true. The expected use case for this arity of the function is in desktop applications where the locale of the local machine will always be the correct locale to use. The two-argument arity of the function is intended for web applications, where different clients may have different linguistic needs.
|
||||
|
||||
**NOTE THAT** `get-message` is also memoized, and for the same reason.
|
||||
|
||||
## The translation files
|
||||
|
||||
Obviously, this only works if you provide files with translations of your interesting strings. These files should contain Clojure maps, and the file names should be the locale string for which the file is relevent followed by the extension ".edn". All the translation files should be in the same directory.
|
||||
|
|
82
doc/intro.md
82
doc/intro.md
|
@ -1,31 +1,97 @@
|
|||
# Introduction to 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.0"]
|
||||
[org.clojars.simon_brooke/internationalisation "1.0.4"]
|
||||
|
||||
To use it in your namespace, require:
|
||||
|
||||
[scot.weft.i18n.core :refer [get-messages]]
|
||||
[scot.weft.i18n.core :refer [get-message get-messages]]
|
||||
|
||||
There is only one function you should need to use:
|
||||
There is only two functions you may need to use:
|
||||
|
||||
### get-messages
|
||||
|
||||
```clojure
|
||||
(get-messages accept-language-header resource-path default-locale)
|
||||
```
|
||||
Return the most acceptable messages collection we have given this `accept-language-header`.
|
||||
|
||||
Return the most acceptable messages collection we have given this accept-language-header. Use this function instrad of the unmemoized variant raw-get-messages, as performance will be very much better.
|
||||
* `accept-language-header` should be a string representing the value of an RFC 2616 Accept-Language header;
|
||||
* `resource-path` should be a string representing the fully-qualified path name of the directory in which message files are stored;
|
||||
* `default-locale` should be a string representing a locale specifier to use if no acceptable locale can be identified.
|
||||
|
||||
* `accept-language-header` should be the value of an RFC 2616 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.
|
||||
For example:
|
||||
```clojure
|
||||
(get-messages "en-GB;q=0.9, fr-FR" "i18n" "en-GB")
|
||||
```
|
||||
|
||||
Returns a map of message keys to strings.
|
||||
|
||||
See [RFC 2616](https://www.ietf.org/rfc/rfc2616.txt).
|
||||
|
||||
**NOTE THAT** `get-messages` is [memoized](https://clojuredocs.org/clojure.core/memoize) resulting in faster response when called repeatedly with similar arguments.
|
||||
|
||||
### get-message
|
||||
|
||||
A wrapper around `get-messages` to resolve a single, particular message.
|
||||
```clojure
|
||||
(get-message token)
|
||||
|
||||
(get-message token accept-language-header)
|
||||
|
||||
(get-message token accept-language-header resource-path default-locale)
|
||||
```
|
||||
|
||||
where
|
||||
|
||||
* `token` is the Clojure [keyword](https://clojuredocs.org/clojure.core/keyword) which identifies the particular message you want to retrieve;
|
||||
|
||||
and all the other arguments are as defined above.
|
||||
|
||||
For example:
|
||||
```clojure
|
||||
(get-message :pipe)
|
||||
|
||||
(get-message :pipe "en-GB;q=0.9, fr-FR")
|
||||
|
||||
(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:
|
||||
|
||||
* `*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.
|
||||
|
||||
Thus
|
||||
```clojure
|
||||
(binding [*resource-path* "language-files"
|
||||
*default-language* "en-CA"]
|
||||
(get-message :pipe "en-GB;q=0.9, fr-FR")
|
||||
)
|
||||
```
|
||||
and
|
||||
```clojure
|
||||
(get-message :pipe "en-GB;q=0.9, fr-FR" "language-files" "en-CA")
|
||||
```
|
||||
are effectively the same.
|
||||
|
||||
The intelligent reader will note that if one calls
|
||||
```clojure
|
||||
(get-message :pipe)
|
||||
```
|
||||
there's no mechanism to set the `accept-header`. This is true. The expected use case for this arity of the function is in desktop applications where the locale of the local machine will always be the correct locale to use. The two-argument arity of the function is intended for web applications, where different clients may have different linguistic needs.
|
||||
|
||||
**NOTE THAT** `get-message` is also memoized, and for the same reason.
|
||||
|
||||
## The translation files
|
||||
|
||||
Obviously, this only works if you provide files with translations of your interesting strings. These files should contain Clojure maps, and the file names should be the locale string for which the file is relevent followed by the extension ".edn". All the translation files should be in the same directory.
|
||||
|
|
BIN
docs/MagrittePipe.jpeg
Normal file
BIN
docs/MagrittePipe.jpeg
Normal file
Binary file not shown.
After Width: | Height: | Size: 34 KiB |
18
docs/cloverage/codecov.json
Normal file
18
docs/cloverage/codecov.json
Normal file
|
@ -0,0 +1,18 @@
|
|||
{"coverage":
|
||||
{"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]}}
|
40
docs/cloverage/coverage.css
Normal file
40
docs/cloverage/coverage.css
Normal file
|
@ -0,0 +1,40 @@
|
|||
.covered {
|
||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
||||
background-color: #558B55;
|
||||
}
|
||||
|
||||
.not-covered {
|
||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
||||
background-color: red;
|
||||
}
|
||||
|
||||
.partial {
|
||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
||||
background-color: orange;
|
||||
}
|
||||
|
||||
.not-tracked {
|
||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
||||
}
|
||||
|
||||
.blank {
|
||||
font-family: 'Bitstream Vera Sans Mono', 'Courier', monospace;
|
||||
}
|
||||
|
||||
td {
|
||||
padding-right: 10px;
|
||||
}
|
||||
|
||||
td.with-bar {
|
||||
width: 250px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
td.with-number {
|
||||
text-align: right;
|
||||
}
|
||||
|
||||
td.ns-name {
|
||||
min-width: 150px;
|
||||
padding-right: 25px;
|
||||
}
|
1
docs/cloverage/coverage.xml
Normal file
1
docs/cloverage/coverage.xml
Normal file
|
@ -0,0 +1 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?><report><stats><packages value="1"/><methods value="367"/><srcfiles value="1"/><srclines value="93"/></stats><data><all name="total"><coverage type="class, %" value="0% (0/1)"/><coverage type="method, %" value="0% (0/1)"/><coverage type="block, %" value="91% (333/367)"/><coverage type="line, %" value="92% (86/93)"/><package name="scot.weft.i18n.core"><coverage type="class, %" value="0% (0/1)"/><coverage type="method, %" value="0% (0/1)"/><coverage type="block, %" value="91% (333/367)"/><coverage type="line, %" value="92% (86/93)"/></package></all></data></report>
|
42
docs/cloverage/index.html
Normal file
42
docs/cloverage/index.html
Normal file
|
@ -0,0 +1,42 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="./coverage.css"/>
|
||||
<title>Coverage Summary</title>
|
||||
</head>
|
||||
<body>
|
||||
<table>
|
||||
<thead><tr>
|
||||
<td class="ns-name"> Namespace </td>
|
||||
<td class="with-bar"> Forms </td>
|
||||
<td class="with-number">Forms %</td>
|
||||
<td class="with-bar"> Lines </td>
|
||||
<td class="with-number">Lines %</td>
|
||||
<td class="with-number">Total</td><td class="with-number">Blank</td><td class="with-number">Instrumented</td>
|
||||
</tr></thead>
|
||||
<tr>
|
||||
<td><a href="scot/weft/i18n/core.clj.html">scot.weft.i18n.core</a></td><td class="with-bar"><div class="covered"
|
||||
style="width:90.73569482288828%;
|
||||
float:left;"> 333 </div><div class="not-covered"
|
||||
style="width:9.264305177111716%;
|
||||
float:left;"> 34 </div></td>
|
||||
<td class="with-number">90.74 %</td>
|
||||
<td class="with-bar"><div class="covered"
|
||||
style="width:92.47311827956989%;
|
||||
float:left;"> 86 </div><div class="partial"
|
||||
style="width:2.150537634408602%;
|
||||
float:left;"> 2 </div><div class="not-covered"
|
||||
style="width:5.376344086021505%;
|
||||
float:left;"> 5 </div></td>
|
||||
<td class="with-number">94.62 %</td>
|
||||
<td class="with-number">216</td><td class="with-number">23</td><td class="with-number">93</td>
|
||||
</tr>
|
||||
<tr><td>Totals:</td>
|
||||
<td class="with-bar"></td>
|
||||
<td class="with-number">90.74 %</td>
|
||||
<td class="with-bar"></td>
|
||||
<td class="with-number">94.62 %</td>
|
||||
</tr>
|
||||
</table>
|
||||
</body>
|
||||
</html>
|
656
docs/cloverage/scot/weft/i18n/core.clj.html
Normal file
656
docs/cloverage/scot/weft/i18n/core.clj.html
Normal file
|
@ -0,0 +1,656 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<link rel="stylesheet" href="../../../coverage.css"/> <title> scot/weft/i18n/core.clj </title>
|
||||
</head>
|
||||
<body>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
001 (ns ^{:doc "Internationalisation."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
002 :author "Simon Brooke"}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
003 scot.weft.i18n.core
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
004 (:require [clojure.java.io :as io]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
005 [clojure.pprint :refer [pprint]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
006 [clojure.string :refer [join]]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
007 [instaparse.core :as insta]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
008 [taoensso.timbre :as timbre]
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
009 [trptr.java-wrapper.locale :as locale])
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
010 (:import [clojure.lang Keyword]))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
011
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
012 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
013 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
014 ;;;; scot.weft.i18n: a simple internationalisation library for Clojure.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
015 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
016 ;;;; This library is distributed under the Eclipse Licence in the hope
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
017 ;;;; that it may be useful, but without guarantee.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
018 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
019 ;;;; Copyright (C) 2017 Simon Brooke
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
020 ;;;;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
021 ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
022
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
023 (def ^:dynamic *resource-path*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
024 "The default path within the resources space on which translation files
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
025 will be sought."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
026 "i18n")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
027
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
028 (def ^:dynamic *default-language*
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
029 "The default language to seek."
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
030 (-> (locale/get-default) locale/to-language-tag))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
031
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
032 (def accept-language-grammar
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
033 "Grammar for `Accept-Language` headers"
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
034 "HEADER := SPECIFIER | SPECIFIERS;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
035 SPECIFIERS:= SPECIFIER | SPECIFIER SPEC-SEP SPECIFIERS;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
036 SPEC-SEP := #',\\s*';
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
037 SPECIFIER := LANGUAGE-TAG | LANGUAGE-TAG Q-SEP Q-VALUE;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
038 LANGUAGE-TAG := PRIMARY-TAG | PRIMARY-TAG '-' SUB-TAGS;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
039 PRIMARY-TAG := #'[a-zA-Z]+';
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
040 SUB-TAGS := SUB-TAG | SUB-TAG '-' SUB-TAGS;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
041 SUB-TAG := #'[a-zA-Z0-9]+';
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
042 Q-SEP := #';\\s*q='
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
043 Q-VALUE := '1' | #'0.[0-9]+';")
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
044
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
045 (def parse-accept-language-header
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
046 "Parse an `Accept-Language` header"
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
047 (insta/parser accept-language-grammar))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
048
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
049 (defn generate-accept-languages
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
050 "From a `parse-tree` generated by the `language-specifier-grammar`, generate
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
051 a list of maps each having a `:language` key, a `:preference` key and a
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
052 `:qualifier` key."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
053 {:doc/format :markdown}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
054 [parse-tree]
|
||||
</span><br/>
|
||||
<span class="partial" title="1 out of 2 forms covered">
|
||||
055 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
056 (nil? parse-tree)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
057 nil
|
||||
</span><br/>
|
||||
<span class="partial" title="2 out of 4 forms covered">
|
||||
058 (case
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
059 (first parse-tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
060 :HEADER (generate-accept-languages (second parse-tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
061 :SPECIFIERS (cons
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
062 (generate-accept-languages (second parse-tree))
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
063 (when (>= (count parse-tree) 3)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
064 (generate-accept-languages (nth parse-tree 3))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
065 :SPEC-SEP nil
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
066 :SPECIFIER (assoc
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
067 (generate-accept-languages (second parse-tree))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
068 :preference
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
069 (if
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
070 (>= (count parse-tree) 3)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
071 (generate-accept-languages (nth parse-tree 3))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
072 1))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
073 :LANGUAGE-TAG (if
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
074 (>= (count parse-tree) 3)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
075 (assoc
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
076 (generate-accept-languages (second parse-tree))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
077 :qualifier
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
078 (generate-accept-languages (nth parse-tree 3)))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
079 (generate-accept-languages (second parse-tree)))
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
080 :PRIMARY-TAG {:language (second parse-tree) :qualifier "*"}
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
081 :SUB-TAGS (if
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
082 (>= (count parse-tree) 3)
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 3 forms covered">
|
||||
083 (str
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
084 (generate-accept-languages (second parse-tree))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
085 "-"
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 5 forms covered">
|
||||
086 (generate-accept-languages (nth parse-tree 3)))
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
087 (generate-accept-languages (second parse-tree)))
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
088 :SUB-TAG (second parse-tree)
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
089 :Q-SEP nil
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
090 :Q-VALUE (read-string (second parse-tree))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
091 ;; default
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 2 forms covered">
|
||||
092 (do
|
||||
</span><br/>
|
||||
<span class="not-covered" title="0 out of 16 forms covered">
|
||||
093 (timbre/error "Unable to parse header.")
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
094 nil))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
095
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
096 (defn acceptable-languages
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
097 "Generate an ordered list of acceptable languages, most-preferred first.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
098
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
099 * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
100
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
101 Returns a list of maps as generated by `generate-accept-languages`, in descending order
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
102 of preference."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
103 {:doc/format :markdown}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
104 [accept-language-header]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
105 (let [parse-tree (parse-accept-language-header accept-language-header)]
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
106 (if (vector? parse-tree)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
107 (reverse
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
108 (sort-by
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
109 :preference
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
110 (generate-accept-languages
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
111 parse-tree)))
|
||||
</span><br/>
|
||||
<span class="covered" title="21 out of 21 forms covered">
|
||||
112 (timbre/error "Failed to parse Accept-Language header '" accept-language-header "':\n" (str parse-tree)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
113
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
114
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
115 (defn slurp-resource
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
116 "Slurp the resource of this name and return its contents as a string; but if it doesn't
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
117 exist log the fact and return nil, rather than throwing an exception."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
118 [name]
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
119 (try
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
120 (slurp (io/resource name))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
121 (catch Exception _
|
||||
</span><br/>
|
||||
<span class="covered" title="20 out of 20 forms covered">
|
||||
122 (timbre/error (str "Resource at " name " does not exist."))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
123 nil)))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
124
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
125
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
126 (defn find-language-file-name
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
127 "Find the name of a messages file on this resource path which matches this `language-spec`.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
128
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
129 * `language-spec` should be either a map as generated by `generate-accept-languages`, or
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
130 else a string;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
131 * `resource-path` should be the path name of the directory in which message files are stored,
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
132 within the resources on the classpath.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
133
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
134 Returns the name of an appropriate file if any is found, else nil."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
135 {:doc/format :markdown}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
136 [language-spec resource-path]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
137 (let [file-path (when
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
138 (string? language-spec)
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
139 (join
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
140 java.io.File/separator
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
141 [resource-path (str language-spec ".edn")]))
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
142 contents (when file-path (slurp-resource file-path))]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
143 (cond
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
144 contents
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
145 file-path
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
146 (map? language-spec)
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
147 (or
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
148 (find-language-file-name
|
||||
</span><br/>
|
||||
<span class="covered" title="9 out of 9 forms covered">
|
||||
149 (str (:language language-spec) "-" (:qualifier language-spec))
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
150 resource-path)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
151 (find-language-file-name
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
152 (:language language-spec)
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
153 resource-path)))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
154
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
155
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
156 (defn raw-get-messages
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
157 "Return the most acceptable messages collection we have given this `accept-language-header`.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
158 Do not use this function directly, use the memoized variant `get-messages`, as performance
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
159 will be very much better.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
160
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
161 * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
162 * `resource-path` should be the fully-qualified path name of the directory in which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
163 message files are stored;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
164 * `default-locale` should be a locale specifier to use if no acceptable locale can be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
165 identified.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
166
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
167 Returns a map of message keys to strings; if no useable file is found, returns nil."
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
168 {:doc/format :markdown}
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
169 [^String accept-language-header ^String resource-path ^String default-locale]
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
170 (let [file-path (first
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
171 (remove
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
172 nil?
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
173 (map
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
174 #(find-language-file-name % resource-path)
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
175 (acceptable-languages accept-language-header))))]
|
||||
</span><br/>
|
||||
<span class="covered" title="20 out of 20 forms covered">
|
||||
176 (timbre/debug (str "Found i18n file at '" file-path "'"))
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
177 (try
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
178 (read-string
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
179 (slurp-resource
|
||||
</span><br/>
|
||||
<span class="covered" title="5 out of 5 forms covered">
|
||||
180 (or
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
181 file-path
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
182 (join java.io.File/separator
|
||||
</span><br/>
|
||||
<span class="covered" title="2 out of 2 forms covered">
|
||||
183 [resource-path
|
||||
</span><br/>
|
||||
<span class="covered" title="4 out of 4 forms covered">
|
||||
184 (str default-locale ".edn")]))))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
185 (catch Exception any
|
||||
</span><br/>
|
||||
<span class="covered" title="19 out of 19 forms covered">
|
||||
186 (timbre/error (str "Failed to load internationalisation because " (.getMessage any)))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
187 nil))))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
188
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
189 (def get-messages
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
190 "Return the most acceptable messages collection we have given this `accept-language-header`
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
191
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
192 * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
193 * `resource-path` should be the fully-qualified path name of the directory in which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
194 message files are stored;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
195 * `default-locale` should be a locale specifier to use if no acceptable locale can be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
196 identified.
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
197
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
198 Returns a map of message keys to strings.; if no useable file is found, returns nil."
|
||||
</span><br/>
|
||||
<span class="covered" title="3 out of 3 forms covered">
|
||||
199 (memoize raw-get-messages))
|
||||
</span><br/>
|
||||
<span class="blank" title="0 out of 0 forms covered">
|
||||
200
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
201 (def get-message
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
202 "Return the message keyed by this `token` from the most acceptable messages collection
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
203 we have given this `accept-language-header`.
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
204
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
205 * `token` should be a clojure keyword identifying the message to be retrieved;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
206 * `accept-language-header` should be the value of an RFC2616 `Accept-Language` header;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
207 * `resource-path` should be the fully-qualified path name of the directory in which
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
208 message files are stored;
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
209 * `default-locale` should be a locale specifier to use if no acceptable locale can be
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
210 identified."
|
||||
</span><br/>
|
||||
<span class="covered" title="1 out of 1 forms covered">
|
||||
211 (fn ([^Keyword token ^String accept-language-header ^String resource-path ^String default-locale]
|
||||
</span><br/>
|
||||
<span class="covered" title="7 out of 7 forms covered">
|
||||
212 ((get-messages accept-language-header resource-path default-locale) token))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
213 ([^Keyword token ^String accept-language-header]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
214 (get-message token accept-language-header *resource-path* *default-language*))
|
||||
</span><br/>
|
||||
<span class="not-tracked" title="0 out of 0 forms covered">
|
||||
215 ([^Keyword token]
|
||||
</span><br/>
|
||||
<span class="covered" title="6 out of 6 forms covered">
|
||||
216 (get-message token nil *resource-path* *default-language*))))
|
||||
</span><br/>
|
||||
</body>
|
||||
</html>
|
551
docs/codox/css/default.css
Normal file
551
docs/codox/css/default.css
Normal file
|
@ -0,0 +1,551 @@
|
|||
body {
|
||||
font-family: Helvetica, Arial, sans-serif;
|
||||
font-size: 15px;
|
||||
}
|
||||
|
||||
pre, code {
|
||||
font-family: Monaco, DejaVu Sans Mono, Consolas, monospace;
|
||||
font-size: 9pt;
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
h1 {
|
||||
font-weight: normal;
|
||||
font-size: 29px;
|
||||
margin: 10px 0 2px 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
h2 {
|
||||
font-weight: normal;
|
||||
font-size: 25px;
|
||||
}
|
||||
|
||||
h5.license {
|
||||
margin: 9px 0 22px 0;
|
||||
color: #555;
|
||||
font-weight: normal;
|
||||
font-size: 12px;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.document h1, .namespace-index h1 {
|
||||
font-size: 32px;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
#header, #content, .sidebar {
|
||||
position: fixed;
|
||||
}
|
||||
|
||||
#header {
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
height: 22px;
|
||||
color: #f5f5f5;
|
||||
padding: 5px 7px;
|
||||
}
|
||||
|
||||
#content {
|
||||
top: 32px;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
background: #fff;
|
||||
color: #333;
|
||||
padding: 0 18px;
|
||||
}
|
||||
|
||||
.sidebar {
|
||||
position: fixed;
|
||||
top: 32px;
|
||||
bottom: 0;
|
||||
overflow: auto;
|
||||
}
|
||||
|
||||
.sidebar.primary {
|
||||
background: #e2e2e2;
|
||||
border-right: solid 1px #cccccc;
|
||||
left: 0;
|
||||
width: 250px;
|
||||
}
|
||||
|
||||
.sidebar.secondary {
|
||||
background: #f2f2f2;
|
||||
border-right: solid 1px #d7d7d7;
|
||||
left: 251px;
|
||||
width: 200px;
|
||||
}
|
||||
|
||||
#content.namespace-index, #content.document {
|
||||
left: 251px;
|
||||
}
|
||||
|
||||
#content.namespace-docs {
|
||||
left: 452px;
|
||||
}
|
||||
|
||||
#content.document {
|
||||
padding-bottom: 10%;
|
||||
}
|
||||
|
||||
#header {
|
||||
background: #3f3f3f;
|
||||
box-shadow: 0 0 8px rgba(0, 0, 0, 0.4);
|
||||
z-index: 100;
|
||||
}
|
||||
|
||||
#header h1 {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
font-size: 18px;
|
||||
font-weight: lighter;
|
||||
text-shadow: -1px -1px 0px #333;
|
||||
}
|
||||
|
||||
#header h1 .project-version {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.project-version {
|
||||
padding-left: 0.15em;
|
||||
}
|
||||
|
||||
#header a, .sidebar a {
|
||||
display: block;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
#header a {
|
||||
color: #f5f5f5;
|
||||
}
|
||||
|
||||
.sidebar a {
|
||||
color: #333;
|
||||
}
|
||||
|
||||
#header h2 {
|
||||
float: right;
|
||||
font-size: 9pt;
|
||||
font-weight: normal;
|
||||
margin: 4px 3px;
|
||||
padding: 0;
|
||||
color: #bbb;
|
||||
}
|
||||
|
||||
#header h2 a {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.sidebar h3 {
|
||||
margin: 0;
|
||||
padding: 10px 13px 0 13px;
|
||||
font-size: 19px;
|
||||
font-weight: lighter;
|
||||
}
|
||||
|
||||
.sidebar h3 a {
|
||||
color: #444;
|
||||
}
|
||||
|
||||
.sidebar h3.no-link {
|
||||
color: #636363;
|
||||
}
|
||||
|
||||
.sidebar ul {
|
||||
padding: 7px 0 6px 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.sidebar ul.index-link {
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.sidebar li {
|
||||
display: block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.sidebar li a, .sidebar li .no-link {
|
||||
border-left: 3px solid transparent;
|
||||
padding: 0 10px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.sidebar li .no-link {
|
||||
display: block;
|
||||
color: #777;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.sidebar li .inner {
|
||||
display: inline-block;
|
||||
padding-top: 7px;
|
||||
height: 24px;
|
||||
}
|
||||
|
||||
.sidebar li a, .sidebar li .tree {
|
||||
height: 31px;
|
||||
}
|
||||
|
||||
.depth-1 .inner { padding-left: 2px; }
|
||||
.depth-2 .inner { padding-left: 6px; }
|
||||
.depth-3 .inner { padding-left: 20px; }
|
||||
.depth-4 .inner { padding-left: 34px; }
|
||||
.depth-5 .inner { padding-left: 48px; }
|
||||
.depth-6 .inner { padding-left: 62px; }
|
||||
|
||||
.sidebar li .tree {
|
||||
display: block;
|
||||
float: left;
|
||||
position: relative;
|
||||
top: -10px;
|
||||
margin: 0 4px 0 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
.sidebar li.depth-1 .tree {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.sidebar li .tree .top, .sidebar li .tree .bottom {
|
||||
display: block;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
width: 7px;
|
||||
}
|
||||
|
||||
.sidebar li .tree .top {
|
||||
border-left: 1px solid #aaa;
|
||||
border-bottom: 1px solid #aaa;
|
||||
height: 19px;
|
||||
}
|
||||
|
||||
.sidebar li .tree .bottom {
|
||||
height: 22px;
|
||||
}
|
||||
|
||||
.sidebar li.branch .tree .bottom {
|
||||
border-left: 1px solid #aaa;
|
||||
}
|
||||
|
||||
.sidebar.primary li.current a {
|
||||
border-left: 3px solid #a33;
|
||||
color: #a33;
|
||||
}
|
||||
|
||||
.sidebar.secondary li.current a {
|
||||
border-left: 3px solid #33a;
|
||||
color: #33a;
|
||||
}
|
||||
|
||||
.namespace-index h2 {
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
|
||||
.namespace-index h3 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.namespace-index .topics {
|
||||
padding-left: 30px;
|
||||
margin: 11px 0 0 0;
|
||||
}
|
||||
|
||||
.namespace-index .topics li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.namespace-docs h3 {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.public h3 {
|
||||
margin: 0;
|
||||
float: left;
|
||||
}
|
||||
|
||||
.usage {
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.public {
|
||||
margin: 0;
|
||||
border-top: 1px solid #e0e0e0;
|
||||
padding-top: 14px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
|
||||
.public:last-child {
|
||||
margin-bottom: 20%;
|
||||
}
|
||||
|
||||
.members .public:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.members {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.members h4 {
|
||||
color: #555;
|
||||
font-weight: normal;
|
||||
font-variant: small-caps;
|
||||
margin: 0 0 5px 0;
|
||||
}
|
||||
|
||||
.members .inner {
|
||||
padding-top: 5px;
|
||||
padding-left: 12px;
|
||||
margin-top: 2px;
|
||||
margin-left: 7px;
|
||||
border-left: 1px solid #bbb;
|
||||
}
|
||||
|
||||
#content .members .inner h3 {
|
||||
font-size: 12pt;
|
||||
}
|
||||
|
||||
.members .public {
|
||||
border-top: none;
|
||||
margin-top: 0;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 0;
|
||||
}
|
||||
|
||||
.members .public:first-child {
|
||||
padding-top: 0;
|
||||
}
|
||||
|
||||
h4.type,
|
||||
h4.dynamic,
|
||||
h4.added,
|
||||
h4.deprecated {
|
||||
float: left;
|
||||
margin: 3px 10px 15px 0;
|
||||
font-size: 15px;
|
||||
font-weight: bold;
|
||||
font-variant: small-caps;
|
||||
}
|
||||
|
||||
.public h4.type,
|
||||
.public h4.dynamic,
|
||||
.public h4.added,
|
||||
.public h4.deprecated {
|
||||
font-size: 13px;
|
||||
font-weight: bold;
|
||||
margin: 3px 0 0 10px;
|
||||
}
|
||||
|
||||
.members h4.type,
|
||||
.members h4.added,
|
||||
.members h4.deprecated {
|
||||
margin-top: 1px;
|
||||
}
|
||||
|
||||
h4.type {
|
||||
color: #717171;
|
||||
}
|
||||
|
||||
h4.dynamic {
|
||||
color: #9933aa;
|
||||
}
|
||||
|
||||
h4.added {
|
||||
color: #508820;
|
||||
}
|
||||
|
||||
h4.deprecated {
|
||||
color: #880000;
|
||||
}
|
||||
|
||||
.namespace {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.namespace:last-child {
|
||||
margin-bottom: 10%;
|
||||
}
|
||||
|
||||
.index {
|
||||
padding: 0;
|
||||
font-size: 80%;
|
||||
margin: 15px 0;
|
||||
line-height: 16px;
|
||||
}
|
||||
|
||||
.index * {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.index p {
|
||||
padding-right: 3px;
|
||||
}
|
||||
|
||||
.index li {
|
||||
padding-right: 5px;
|
||||
}
|
||||
|
||||
.index ul {
|
||||
padding-left: 0;
|
||||
}
|
||||
|
||||
.type-sig {
|
||||
clear: both;
|
||||
color: #088;
|
||||
}
|
||||
|
||||
.type-sig pre {
|
||||
padding-top: 10px;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.usage code {
|
||||
display: block;
|
||||
color: #008;
|
||||
margin: 2px 0;
|
||||
}
|
||||
|
||||
.usage code:first-child {
|
||||
padding-top: 10px;
|
||||
}
|
||||
|
||||
p {
|
||||
margin: 15px 0;
|
||||
}
|
||||
|
||||
.public p:first-child, .public pre.plaintext {
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.doc {
|
||||
margin: 0 0 26px 0;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.public .doc {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.namespace-index .doc {
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.namespace-index .namespace .doc {
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.markdown p, .markdown li, .markdown dt, .markdown dd, .markdown td {
|
||||
line-height: 22px;
|
||||
}
|
||||
|
||||
.markdown li {
|
||||
padding: 2px 0;
|
||||
}
|
||||
|
||||
.markdown h2 {
|
||||
font-weight: normal;
|
||||
font-size: 25px;
|
||||
margin: 30px 0 10px 0;
|
||||
}
|
||||
|
||||
.markdown h3 {
|
||||
font-weight: normal;
|
||||
font-size: 20px;
|
||||
margin: 30px 0 0 0;
|
||||
}
|
||||
|
||||
.markdown h4 {
|
||||
font-size: 15px;
|
||||
margin: 22px 0 -4px 0;
|
||||
}
|
||||
|
||||
.doc, .public, .namespace .index {
|
||||
max-width: 680px;
|
||||
overflow-x: visible;
|
||||
}
|
||||
|
||||
.markdown pre > code {
|
||||
display: block;
|
||||
padding: 10px;
|
||||
}
|
||||
|
||||
.markdown pre > code, .src-link a {
|
||||
border: 1px solid #e4e4e4;
|
||||
border-radius: 2px;
|
||||
}
|
||||
|
||||
.markdown code:not(.hljs), .src-link a {
|
||||
background: #f6f6f6;
|
||||
}
|
||||
|
||||
pre.deps {
|
||||
display: inline-block;
|
||||
margin: 0 10px;
|
||||
border: 1px solid #e4e4e4;
|
||||
border-radius: 2px;
|
||||
padding: 10px;
|
||||
background-color: #f6f6f6;
|
||||
}
|
||||
|
||||
.markdown hr {
|
||||
border-style: solid;
|
||||
border-top: none;
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.doc ul, .doc ol {
|
||||
padding-left: 30px;
|
||||
}
|
||||
|
||||
.doc table {
|
||||
border-collapse: collapse;
|
||||
margin: 0 10px;
|
||||
}
|
||||
|
||||
.doc table td, .doc table th {
|
||||
border: 1px solid #dddddd;
|
||||
padding: 4px 6px;
|
||||
}
|
||||
|
||||
.doc table th {
|
||||
background: #f2f2f2;
|
||||
}
|
||||
|
||||
.doc dl {
|
||||
margin: 0 10px 20px 10px;
|
||||
}
|
||||
|
||||
.doc dl dt {
|
||||
font-weight: bold;
|
||||
margin: 0;
|
||||
padding: 3px 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
}
|
||||
|
||||
.doc dl dd {
|
||||
padding: 5px 0;
|
||||
margin: 0 0 5px 10px;
|
||||
}
|
||||
|
||||
.doc abbr {
|
||||
border-bottom: 1px dotted #333;
|
||||
font-variant: none;
|
||||
cursor: help;
|
||||
}
|
||||
|
||||
.src-link {
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.src-link a {
|
||||
font-size: 70%;
|
||||
padding: 1px 4px;
|
||||
text-decoration: none;
|
||||
color: #5555bb;
|
||||
}
|
97
docs/codox/css/highlight.css
Normal file
97
docs/codox/css/highlight.css
Normal file
|
@ -0,0 +1,97 @@
|
|||
/*
|
||||
github.com style (c) Vasily Polovnyov <vast@whiteants.net>
|
||||
*/
|
||||
|
||||
.hljs {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 0.5em;
|
||||
color: #333;
|
||||
background: #f8f8f8;
|
||||
}
|
||||
|
||||
.hljs-comment,
|
||||
.hljs-quote {
|
||||
color: #998;
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-keyword,
|
||||
.hljs-selector-tag,
|
||||
.hljs-subst {
|
||||
color: #333;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-number,
|
||||
.hljs-literal,
|
||||
.hljs-variable,
|
||||
.hljs-template-variable,
|
||||
.hljs-tag .hljs-attr {
|
||||
color: #008080;
|
||||
}
|
||||
|
||||
.hljs-string,
|
||||
.hljs-doctag {
|
||||
color: #d14;
|
||||
}
|
||||
|
||||
.hljs-title,
|
||||
.hljs-section,
|
||||
.hljs-selector-id {
|
||||
color: #900;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-subst {
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-type,
|
||||
.hljs-class .hljs-title {
|
||||
color: #458;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-tag,
|
||||
.hljs-name,
|
||||
.hljs-attribute {
|
||||
color: #000080;
|
||||
font-weight: normal;
|
||||
}
|
||||
|
||||
.hljs-regexp,
|
||||
.hljs-link {
|
||||
color: #009926;
|
||||
}
|
||||
|
||||
.hljs-symbol,
|
||||
.hljs-bullet {
|
||||
color: #990073;
|
||||
}
|
||||
|
||||
.hljs-built_in,
|
||||
.hljs-builtin-name {
|
||||
color: #0086b3;
|
||||
}
|
||||
|
||||
.hljs-meta {
|
||||
color: #999;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.hljs-deletion {
|
||||
background: #fdd;
|
||||
}
|
||||
|
||||
.hljs-addition {
|
||||
background: #dfd;
|
||||
}
|
||||
|
||||
.hljs-emphasis {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.hljs-strong {
|
||||
font-weight: bold;
|
||||
}
|
3
docs/codox/index.html
Normal file
3
docs/codox/index.html
Normal file
|
@ -0,0 +1,3 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>Internationalisation 1.0.3-SNAPSHOT</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Internationalisation</span> <span class="project-version">1.0.3-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 current"><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>internationalisation</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1 "><a href="scot.weft.i18n.core.html"><div class="inner"><span>scot.weft.i18n.core</span></div></a></li></ul></div><div class="namespace-index" id="content"><h1><span class="project-title"><span class="project-name">Internationalisation</span> <span class="project-version">1.0.3-SNAPSHOT</span></span></h1><h5 class="license">Released under the <a href="http://www.eclipse.org/legal/epl-v10.html">Eclipse Public License</a></h5><div class="doc"><p>Internationalisation library for Clojure.</p></div><h2>Installation</h2><p>To install, add the following dependency to your project or build file:</p><pre class="deps">[org.clojars.simon_brooke/internationalisation "1.0.3-SNAPSHOT"]</pre><h2>Topics</h2><ul class="topics"><li><a href="intro.html">internationalisation</a></li></ul><h2>Namespaces</h2><div class="namespace"><h3><a href="scot.weft.i18n.core.html">scot.weft.i18n.core</a></h3><div class="doc"><div class="markdown"><p>Internationalisation.</p></div></div><div class="index"><p>Public variables and functions:</p><ul><li> <a href="scot.weft.i18n.core.html#var-*default-language*">*default-language*</a> </li><li> <a href="scot.weft.i18n.core.html#var-*resource-path*">*resource-path*</a> </li><li> <a href="scot.weft.i18n.core.html#var-accept-language-grammar">accept-language-grammar</a> </li><li> <a href="scot.weft.i18n.core.html#var-acceptable-languages">acceptable-languages</a> </li><li> <a href="scot.weft.i18n.core.html#var-find-language-file-name">find-language-file-name</a> </li><li> <a href="scot.weft.i18n.core.html#var-generate-accept-languages">generate-accept-languages</a> </li><li> <a href="scot.weft.i18n.core.html#var-get-message">get-message</a> </li><li> <a href="scot.weft.i18n.core.html#var-get-messages">get-messages</a> </li><li> <a href="scot.weft.i18n.core.html#var-parse-accept-language-header">parse-accept-language-header</a> </li><li> <a href="scot.weft.i18n.core.html#var-raw-get-messages">raw-get-messages</a> </li><li> <a href="scot.weft.i18n.core.html#var-slurp-resource">slurp-resource</a> </li></ul></div></div></div></body></html>
|
87
docs/codox/intro.html
Normal file
87
docs/codox/intro.html
Normal file
|
@ -0,0 +1,87 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>internationalisation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Internationalisation</span> <span class="project-version">1.0.3-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 current"><a href="intro.html"><div class="inner"><span>internationalisation</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1 "><a href="scot.weft.i18n.core.html"><div class="inner"><span>scot.weft.i18n.core</span></div></a></li></ul></div><div class="document" id="content"><div class="doc"><div class="markdown"><h1><a href="#internationalisation" name="internationalisation"></a>internationalisation</h1>
|
||||
<p>A Clojure library designed to provide simple interationalisation of user-facing messages.</p>
|
||||
<h2><a href="#usage" name="usage"></a>Usage</h2>
|
||||
<p>To use this library in your project, add the following leiningen dependency:</p>
|
||||
<pre><code>[org.clojars.simon_brooke/internationalisation "1.0.4"]
|
||||
</code></pre>
|
||||
<p>To use it in your namespace, require:</p>
|
||||
<pre><code>[scot.weft.i18n.core :refer [get-message get-messages]]
|
||||
</code></pre>
|
||||
<p>There is only two functions you may need to use:</p>
|
||||
<h3><a href="#get-messages" name="get-messages"></a>get-messages</h3>
|
||||
<pre><code class="clojure">(get-messages accept-language-header resource-path default-locale)
|
||||
</code></pre>
|
||||
<p>Return the most acceptable messages collection we have given this <code>accept-language-header</code>.</p>
|
||||
<ul>
|
||||
<li><code>accept-language-header</code> should be a string representing the value of an RFC 2616 Accept-Language header;</li>
|
||||
<li><code>resource-path</code> should be a string representing the fully-qualified path name of the directory in which message files are stored;</li>
|
||||
<li><code>default-locale</code> should be a string representing a locale specifier to use if no acceptable locale can be identified.</li>
|
||||
</ul>
|
||||
<p>For example:</p>
|
||||
<pre><code class="clojure">(get-messages "en-GB;q=0.9, fr-FR" "i18n" "en-GB")
|
||||
</code></pre>
|
||||
<p>Returns a map of message keys to strings.</p>
|
||||
<p>See <a href="https://www.ietf.org/rfc/rfc2616.txt">RFC 2616</a>.</p>
|
||||
<p><strong>NOTE THAT</strong> <code>get-messages</code> is <a href="https://clojuredocs.org/clojure.core/memoize">memoized</a> resulting in faster response when called repeatedly with similar arguments.</p>
|
||||
<h3><a href="#get-message" name="get-message"></a>get-message</h3>
|
||||
<p>A wrapper around <code>get-messages</code> to resolve a single, particular message. </p>
|
||||
<pre><code class="clojure">(get-message token)
|
||||
|
||||
(get-message token accept-language-header)
|
||||
|
||||
(get-message token accept-language-header resource-path default-locale)
|
||||
</code></pre>
|
||||
<p>where</p>
|
||||
<ul>
|
||||
<li><code>token</code> is the Clojure <a href="https://clojuredocs.org/clojure.core/keyword">keyword</a> which identifies the particular message you want to retrieve;</li>
|
||||
</ul>
|
||||
<p>and all the other arguments are as defined above.</p>
|
||||
<p>For example:</p>
|
||||
<pre><code class="clojure">(get-message :pipe)
|
||||
|
||||
(get-message :pipe "en-GB;q=0.9, fr-FR")
|
||||
|
||||
(get-message :pipe "de-DE" "i18n" "ru")
|
||||
</code></pre>
|
||||
<p>So how does this work? When one calls <code>(get-message token accept-language-header)</code>, how does it know where to find resources? The answer is that there are two dynamic variables:</p>
|
||||
<ul>
|
||||
<li><code>*resource-path*</code>, the default path within the resources space on which translation files will be sought. Initialised to <code>i18n</code>.</li>
|
||||
<li><code>*default-language*</code>, 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.</li>
|
||||
</ul>
|
||||
<p>Thus</p>
|
||||
<pre><code class="clojure">(binding [*resource-path* "language-files"
|
||||
*default-language* "en-CA"]
|
||||
(get-message :pipe "en-GB;q=0.9, fr-FR")
|
||||
)
|
||||
</code></pre>
|
||||
<p>and</p>
|
||||
<pre><code class="clojure">(get-message :pipe "en-GB;q=0.9, fr-FR" "language-files" "en-CA")
|
||||
</code></pre>
|
||||
<p>are effectively the same.</p>
|
||||
<p>The intelligent reader will note that if one calls</p>
|
||||
<pre><code class="clojure">(get-message :pipe)
|
||||
</code></pre>
|
||||
<p>there’s no mechanism to set the <code>accept-header</code>. This is true. The expected use case for this arity of the function is in desktop applications where the locale of the local machine will always be the correct locale to use. The two-argument arity of the function is intended for web applications, where different clients may have different linguistic needs.</p>
|
||||
<p><strong>NOTE THAT</strong> <code>get-message</code> is also memoized, and for the same reason.</p>
|
||||
<h2><a href="#the-translation-files" name="the-translation-files"></a>The translation files</h2>
|
||||
<p>Obviously, this only works if you provide files with translations of your interesting strings. These files should contain Clojure maps, and the file names should be the locale string for which the file is relevent followed by the extension “.edn”. All the translation files should be in the same directory.</p>
|
||||
<p>In this project you will find two very simple example files, which should give you the idea:</p>
|
||||
<h3><a href="#en-gb-edn" name="en-gb-edn"></a>en-GB.edn</h3>
|
||||
<pre><code>;;;; This is a British English translation file.
|
||||
|
||||
{:pipe "This is not a pipe"}
|
||||
</code></pre>
|
||||
<h3><a href="#fr-fr-edn" name="fr-fr-edn"></a>fr-FR.edn</h3>
|
||||
<pre><code>;;;; This is a French translation file.
|
||||
|
||||
{:pipe "Ceci n'est pas une pipe."}
|
||||
</code></pre>
|
||||
<h2><a href="#documentation" name="documentation"></a>Documentation</h2>
|
||||
<p>Documentation may be generated by running</p>
|
||||
<pre><code>lein codox
|
||||
</code></pre>
|
||||
<h2><a href="#license" name="license"></a>License</h2>
|
||||
<p>Copyright © 2017 Simon Brooke</p>
|
||||
<p>Distributed under the Eclipse Public License either version 1.0 or (at your option) any later version.</p></div></div></div></body></html>
|
2
docs/codox/js/highlight.min.js
vendored
Normal file
2
docs/codox/js/highlight.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
4
docs/codox/js/jquery.min.js
vendored
Normal file
4
docs/codox/js/jquery.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
112
docs/codox/js/page_effects.js
Normal file
112
docs/codox/js/page_effects.js
Normal file
|
@ -0,0 +1,112 @@
|
|||
function visibleInParent(element) {
|
||||
var position = $(element).position().top
|
||||
return position > -50 && position < ($(element).offsetParent().height() - 50)
|
||||
}
|
||||
|
||||
function hasFragment(link, fragment) {
|
||||
return $(link).attr("href").indexOf("#" + fragment) != -1
|
||||
}
|
||||
|
||||
function findLinkByFragment(elements, fragment) {
|
||||
return $(elements).filter(function(i, e) { return hasFragment(e, fragment)}).first()
|
||||
}
|
||||
|
||||
function scrollToCurrentVarLink(elements) {
|
||||
var elements = $(elements);
|
||||
var parent = elements.offsetParent();
|
||||
|
||||
if (elements.length == 0) return;
|
||||
|
||||
var top = elements.first().position().top;
|
||||
var bottom = elements.last().position().top + elements.last().height();
|
||||
|
||||
if (top >= 0 && bottom <= parent.height()) return;
|
||||
|
||||
if (top < 0) {
|
||||
parent.scrollTop(parent.scrollTop() + top);
|
||||
}
|
||||
else if (bottom > parent.height()) {
|
||||
parent.scrollTop(parent.scrollTop() + bottom - parent.height());
|
||||
}
|
||||
}
|
||||
|
||||
function setCurrentVarLink() {
|
||||
$('.secondary a').parent().removeClass('current')
|
||||
$('.anchor').
|
||||
filter(function(index) { return visibleInParent(this) }).
|
||||
each(function(index, element) {
|
||||
findLinkByFragment(".secondary a", element.id).
|
||||
parent().
|
||||
addClass('current')
|
||||
});
|
||||
scrollToCurrentVarLink('.secondary .current');
|
||||
}
|
||||
|
||||
var hasStorage = (function() { try { return localStorage.getItem } catch(e) {} }())
|
||||
|
||||
function scrollPositionId(element) {
|
||||
var directory = window.location.href.replace(/[^\/]+\.html$/, '')
|
||||
return 'scroll::' + $(element).attr('id') + '::' + directory
|
||||
}
|
||||
|
||||
function storeScrollPosition(element) {
|
||||
if (!hasStorage) return;
|
||||
localStorage.setItem(scrollPositionId(element) + "::x", $(element).scrollLeft())
|
||||
localStorage.setItem(scrollPositionId(element) + "::y", $(element).scrollTop())
|
||||
}
|
||||
|
||||
function recallScrollPosition(element) {
|
||||
if (!hasStorage) return;
|
||||
$(element).scrollLeft(localStorage.getItem(scrollPositionId(element) + "::x"))
|
||||
$(element).scrollTop(localStorage.getItem(scrollPositionId(element) + "::y"))
|
||||
}
|
||||
|
||||
function persistScrollPosition(element) {
|
||||
recallScrollPosition(element)
|
||||
$(element).scroll(function() { storeScrollPosition(element) })
|
||||
}
|
||||
|
||||
function sidebarContentWidth(element) {
|
||||
var widths = $(element).find('.inner').map(function() { return $(this).innerWidth() })
|
||||
return Math.max.apply(Math, widths)
|
||||
}
|
||||
|
||||
function calculateSize(width, snap, margin, minimum) {
|
||||
if (width == 0) {
|
||||
return 0
|
||||
}
|
||||
else {
|
||||
return Math.max(minimum, (Math.ceil(width / snap) * snap) + (margin * 2))
|
||||
}
|
||||
}
|
||||
|
||||
function resizeSidebars() {
|
||||
var primaryWidth = sidebarContentWidth('.primary')
|
||||
var secondaryWidth = 0
|
||||
|
||||
if ($('.secondary').length != 0) {
|
||||
secondaryWidth = sidebarContentWidth('.secondary')
|
||||
}
|
||||
|
||||
// snap to grid
|
||||
primaryWidth = calculateSize(primaryWidth, 32, 13, 160)
|
||||
secondaryWidth = calculateSize(secondaryWidth, 32, 13, 160)
|
||||
|
||||
$('.primary').css('width', primaryWidth)
|
||||
$('.secondary').css('width', secondaryWidth).css('left', primaryWidth + 1)
|
||||
|
||||
if (secondaryWidth > 0) {
|
||||
$('#content').css('left', primaryWidth + secondaryWidth + 2)
|
||||
}
|
||||
else {
|
||||
$('#content').css('left', primaryWidth + 1)
|
||||
}
|
||||
}
|
||||
|
||||
$(window).ready(resizeSidebars)
|
||||
$(window).ready(setCurrentVarLink)
|
||||
$(window).ready(function() { persistScrollPosition('.primary')})
|
||||
$(window).ready(function() {
|
||||
$('#content').scroll(setCurrentVarLink)
|
||||
$(window).resize(setCurrentVarLink)
|
||||
})
|
30
docs/codox/scot.weft.i18n.core.html
Normal file
30
docs/codox/scot.weft.i18n.core.html
Normal file
|
@ -0,0 +1,30 @@
|
|||
<!DOCTYPE html PUBLIC ""
|
||||
"">
|
||||
<html><head><meta charset="UTF-8" /><title>scot.weft.i18n.core documentation</title><link rel="stylesheet" type="text/css" href="css/default.css" /><link rel="stylesheet" type="text/css" href="css/highlight.css" /><script type="text/javascript" src="js/highlight.min.js"></script><script type="text/javascript" src="js/jquery.min.js"></script><script type="text/javascript" src="js/page_effects.js"></script><script>hljs.initHighlightingOnLoad();</script></head><body><div id="header"><h2>Generated by <a href="https://github.com/weavejester/codox">Codox</a></h2><h1><a href="index.html"><span class="project-title"><span class="project-name">Internationalisation</span> <span class="project-version">1.0.3-SNAPSHOT</span></span></a></h1></div><div class="sidebar primary"><h3 class="no-link"><span class="inner">Project</span></h3><ul class="index-link"><li class="depth-1 "><a href="index.html"><div class="inner">Index</div></a></li></ul><h3 class="no-link"><span class="inner">Topics</span></h3><ul><li class="depth-1 "><a href="intro.html"><div class="inner"><span>internationalisation</span></div></a></li></ul><h3 class="no-link"><span class="inner">Namespaces</span></h3><ul><li class="depth-1 current"><a href="scot.weft.i18n.core.html"><div class="inner"><span>scot.weft.i18n.core</span></div></a></li></ul></div><div class="sidebar secondary"><h3><a href="#top"><span class="inner">Public Vars</span></a></h3><ul><li class="depth-1"><a href="scot.weft.i18n.core.html#var-*default-language*"><div class="inner"><span>*default-language*</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-*resource-path*"><div class="inner"><span>*resource-path*</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-accept-language-grammar"><div class="inner"><span>accept-language-grammar</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-acceptable-languages"><div class="inner"><span>acceptable-languages</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-find-language-file-name"><div class="inner"><span>find-language-file-name</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-generate-accept-languages"><div class="inner"><span>generate-accept-languages</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-get-message"><div class="inner"><span>get-message</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-get-messages"><div class="inner"><span>get-messages</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-parse-accept-language-header"><div class="inner"><span>parse-accept-language-header</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-raw-get-messages"><div class="inner"><span>raw-get-messages</span></div></a></li><li class="depth-1"><a href="scot.weft.i18n.core.html#var-slurp-resource"><div class="inner"><span>slurp-resource</span></div></a></li></ul></div><div class="namespace-docs" id="content"><h1 class="anchor" id="top">scot.weft.i18n.core</h1><div class="doc"><div class="markdown"><p>Internationalisation.</p></div></div><div class="public anchor" id="var-*default-language*"><h3>*default-language*</h3><h4 class="dynamic">dynamic</h4><div class="usage"></div><div class="doc"><div class="markdown"><p>The default language to seek.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L28">view source</a></div></div><div class="public anchor" id="var-*resource-path*"><h3>*resource-path*</h3><h4 class="dynamic">dynamic</h4><div class="usage"></div><div class="doc"><div class="markdown"><p>The default path within the resources space on which translation files will be sought.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L23">view source</a></div></div><div class="public anchor" id="var-accept-language-grammar"><h3>accept-language-grammar</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Grammar for <code>Accept-Language</code> headers</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L32">view source</a></div></div><div class="public anchor" id="var-acceptable-languages"><h3>acceptable-languages</h3><div class="usage"><code>(acceptable-languages accept-language-header)</code></div><div class="doc"><div class="markdown"><p>Generate an ordered list of acceptable languages, most-preferred first.</p>
|
||||
<ul>
|
||||
<li><code>accept-language-header</code> should be the value of an RFC2616 <code>Accept-Language</code> header.</li>
|
||||
</ul>
|
||||
<p>Returns a list of maps as generated by <code>generate-accept-languages</code>, in descending order of preference.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L96">view source</a></div></div><div class="public anchor" id="var-find-language-file-name"><h3>find-language-file-name</h3><div class="usage"><code>(find-language-file-name language-spec resource-path)</code></div><div class="doc"><div class="markdown"><p>Find the name of a messages file on this resource path which matches this <code>language-spec</code>.</p>
|
||||
<ul>
|
||||
<li><code>language-spec</code> should be either a map as generated by <code>generate-accept-languages</code>, or else a string;</li>
|
||||
<li><code>resource-path</code> should be the path name of the directory in which message files are stored, within the resources on the classpath.</li>
|
||||
</ul>
|
||||
<p>Returns the name of an appropriate file if any is found, else nil.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L126">view source</a></div></div><div class="public anchor" id="var-generate-accept-languages"><h3>generate-accept-languages</h3><div class="usage"><code>(generate-accept-languages parse-tree)</code></div><div class="doc"><div class="markdown"><p>From a <code>parse-tree</code> generated by the <code>language-specifier-grammar</code>, generate a list of maps each having a <code>:language</code> key, a <code>:preference</code> key and a <code>:qualifier</code> key.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L49">view source</a></div></div><div class="public anchor" id="var-get-message"><h3>get-message</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Return the message keyed by this <code>token</code> from the most acceptable messages collection<br />we have given this <code>accept-language-header</code>.</p>
|
||||
<ul>
|
||||
<li><code>token</code> should be a clojure keyword identifying the message to be retrieved;</li>
|
||||
<li><code>accept-language-header</code> should be the value of an RFC2616 <code>Accept-Language</code> header;</li>
|
||||
<li><code>resource-path</code> should be the fully-qualified path name of the directory in which message files are stored;</li>
|
||||
<li><code>default-locale</code> should be a locale specifier to use if no acceptable locale can be identified.</li>
|
||||
</ul></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L201">view source</a></div></div><div class="public anchor" id="var-get-messages"><h3>get-messages</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Return the most acceptable messages collection we have given this <code>accept-language-header</code></p>
|
||||
<ul>
|
||||
<li><code>accept-language-header</code> should be the value of an RFC2616 <code>Accept-Language</code> header;</li>
|
||||
<li><code>resource-path</code> should be the fully-qualified path name of the directory in which message files are stored;</li>
|
||||
<li><code>default-locale</code> should be a locale specifier to use if no acceptable locale can be identified.</li>
|
||||
</ul>
|
||||
<p>Returns a map of message keys to strings.; if no useable file is found, returns nil.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L189">view source</a></div></div><div class="public anchor" id="var-parse-accept-language-header"><h3>parse-accept-language-header</h3><div class="usage"></div><div class="doc"><div class="markdown"><p>Parse an <code>Accept-Language</code> header</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L45">view source</a></div></div><div class="public anchor" id="var-raw-get-messages"><h3>raw-get-messages</h3><div class="usage"><code>(raw-get-messages accept-language-header resource-path default-locale)</code></div><div class="doc"><div class="markdown"><p>Return the most acceptable messages collection we have given this <code>accept-language-header</code>. Do not use this function directly, use the memoized variant <code>get-messages</code>, as performance will be very much better.</p>
|
||||
<ul>
|
||||
<li><code>accept-language-header</code> should be the value of an RFC2616 <code>Accept-Language</code> header;</li>
|
||||
<li><code>resource-path</code> should be the fully-qualified path name of the directory in which message files are stored;</li>
|
||||
<li><code>default-locale</code> should be a locale specifier to use if no acceptable locale can be identified.</li>
|
||||
</ul>
|
||||
<p>Returns a map of message keys to strings; if no useable file is found, returns nil.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L156">view source</a></div></div><div class="public anchor" id="var-slurp-resource"><h3>slurp-resource</h3><div class="usage"><code>(slurp-resource name)</code></div><div class="doc"><div class="markdown"><p>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.</p></div></div><div class="src-link"><a href="https://github.com/simon-brooke/internationalisation/blob/master/src/scot/weft/i18n/core.clj#L115">view source</a></div></div></div></body></html>
|
17
docs/index.html
Normal file
17
docs/index.html
Normal file
|
@ -0,0 +1,17 @@
|
|||
<html>
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
|
||||
<title>Internationalisation: Documentation</title>
|
||||
<link rel="stylesheet" type="text/css" href="codox/css/default.css" />
|
||||
</head>
|
||||
<body>
|
||||
<h1>Internationalisation: Documentation</h1>
|
||||
<img src="MagrittePipe.jpeg">
|
||||
<p>Illustration: <i><a href="https://en.wikipedia.org/wiki/The_Treachery_of_Images">La Trahison des Images</a> by
|
||||
<a href="https://en.wikipedia.org/wiki/Ren%C3%A9_Magritte">René Magritte</a></i></p>
|
||||
<ul>
|
||||
<li><a href="codox/index.html">Primary documentaion</a></li>
|
||||
<li><a href="cloverage/index.html">Test coverage</a></li>
|
||||
</ul>
|
||||
</body>
|
||||
</html>
|
24
project.clj
24
project.clj
|
@ -1,13 +1,21 @@
|
|||
(defproject org.clojars.simon_brooke/internationalisation "1.0.3"
|
||||
(defproject org.clojars.simon_brooke/internationalisation "1.0.3-SNAPSHOT"
|
||||
:cloverage {:output "docs/cloverage"
|
||||
:codecov? true
|
||||
:emma-xml? true}
|
||||
:codox {:metadata {:doc "**TODO**: write docs"
|
||||
:doc/format :markdown}
|
||||
:output-path "docs/codox"
|
||||
:source-uri "https://github.com/simon-brooke/internationalisation/blob/master/{filepath}#L{line}"}
|
||||
:dependencies [[org.clojure/clojure "1.11.1"]
|
||||
[com.taoensso/timbre "6.0.4"]
|
||||
[instaparse "1.4.12"]
|
||||
[trptr/java-wrapper "0.2.3"]]
|
||||
:description "Internationalisation library for Clojure"
|
||||
:url "https://github.com/simon-brooke/internationalisation"
|
||||
:lein-release {:deploy-via :clojars}
|
||||
:license {:name "Eclipse Public License"
|
||||
:url "http://www.eclipse.org/legal/epl-v10.html"}
|
||||
:dependencies [[org.clojure/clojure "1.7.0"]
|
||||
[com.taoensso/timbre "4.10.0"]
|
||||
[instaparse "1.4.7"]]
|
||||
:plugins [[lein-codox "0.10.3"]]
|
||||
:plugins [[lein-cloverage "1.2.2"]
|
||||
[lein-codox "0.10.7"]]
|
||||
:profiles {:dev {:resource-paths ["resources"]}}
|
||||
:lein-release {:deploy-via :clojars}
|
||||
:signing {:gpg-key "Simon Brooke (Stultus in monte) <simon@journeyman.cc>"}
|
||||
)
|
||||
:url "https://github.com/simon-brooke/internationalisation")
|
||||
|
|
3
resources/i18n/ru.edn
Normal file
3
resources/i18n/ru.edn
Normal file
|
@ -0,0 +1,3 @@
|
|||
;;;; This is a Russian translation file.
|
||||
|
||||
{:pipe "это не труба."}
|
|
@ -2,9 +2,12 @@
|
|||
:author "Simon Brooke"}
|
||||
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]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
|
@ -17,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"
|
||||
|
@ -31,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
|
||||
|
@ -51,7 +60,7 @@
|
|||
:HEADER (generate-accept-languages (second parse-tree))
|
||||
:SPECIFIERS (cons
|
||||
(generate-accept-languages (second parse-tree))
|
||||
(if (>= (count parse-tree) 3)
|
||||
(when (>= (count parse-tree) 3)
|
||||
(generate-accept-languages (nth parse-tree 3))))
|
||||
:SPEC-SEP nil
|
||||
:SPECIFIER (assoc
|
||||
|
@ -78,8 +87,11 @@
|
|||
(generate-accept-languages (second parse-tree)))
|
||||
:SUB-TAG (second parse-tree)
|
||||
:Q-SEP nil
|
||||
:Q-VALUE (read-string (second parse-tree)))))
|
||||
|
||||
:Q-VALUE (read-string (second parse-tree))
|
||||
;; default
|
||||
(do
|
||||
(timbre/error "Unable to parse header.")
|
||||
nil))))
|
||||
|
||||
(defn acceptable-languages
|
||||
"Generate an ordered list of acceptable languages, most-preferred first.
|
||||
|
@ -90,11 +102,14 @@
|
|||
of preference."
|
||||
{:doc/format :markdown}
|
||||
[accept-language-header]
|
||||
(let [parse-tree (parse-accept-language-header accept-language-header)]
|
||||
(if (vector? parse-tree)
|
||||
(reverse
|
||||
(sort-by
|
||||
:preference
|
||||
(generate-accept-languages
|
||||
(parse-accept-language-header accept-language-header)))))
|
||||
parse-tree)))
|
||||
(timbre/error "Failed to parse Accept-Language header '" accept-language-header "':\n" (str parse-tree)))))
|
||||
|
||||
|
||||
(defn slurp-resource
|
||||
|
@ -103,7 +118,7 @@
|
|||
[name]
|
||||
(try
|
||||
(slurp (io/resource name))
|
||||
(catch Exception any
|
||||
(catch Exception _
|
||||
(timbre/error (str "Resource at " name " does not exist."))
|
||||
nil)))
|
||||
|
||||
|
@ -119,12 +134,12 @@
|
|||
Returns the name of an appropriate file if any is found, else nil."
|
||||
{:doc/format :markdown}
|
||||
[language-spec resource-path]
|
||||
(let [file-path (if
|
||||
(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))]
|
||||
contents (when file-path (slurp-resource file-path))]
|
||||
(cond
|
||||
contents
|
||||
file-path
|
||||
|
@ -171,15 +186,31 @@
|
|||
(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*))))
|
|
@ -1,7 +1,12 @@
|
|||
(ns ^{:doc "Tests for Internationalisation."
|
||||
:author "Simon Brooke"} scot.weft.i18n.test.core
|
||||
(:require [clojure.test :refer :all]
|
||||
[scot.weft.i18n.core :refer :all]))
|
||||
(:require [clojure.test :refer [deftest is testing]]
|
||||
[scot.weft.i18n.core :refer [*default-language*
|
||||
acceptable-languages
|
||||
generate-accept-languages
|
||||
get-message
|
||||
get-messages
|
||||
parse-accept-language-header]]))
|
||||
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
;;;;
|
||||
|
@ -14,7 +19,6 @@
|
|||
;;;;
|
||||
;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;
|
||||
|
||||
|
||||
(deftest test-generator
|
||||
(testing "Generating normalised maps from parse trees"
|
||||
(is
|
||||
|
@ -181,7 +185,11 @@
|
|||
[:SPECIFIERS
|
||||
[:SPECIFIER [:LANGUAGE-TAG [:PRIMARY-TAG "fr"]]]]]]
|
||||
(parse-accept-language-header "en, fr"))
|
||||
"Space after comma should be tolerated.")))
|
||||
"Space after comma should be tolerated.")
|
||||
(is (vector? (parse-accept-language-header "en, fr"))
|
||||
"If the header is valid, we should get a (parse tree) vector")
|
||||
(is (not (vector? (parse-accept-language-header "")))
|
||||
"If the header is invalid, we should get a failure object not a vector")))
|
||||
|
||||
|
||||
(deftest test-ordering
|
||||
|
@ -206,4 +214,10 @@
|
|||
(:pipe (get-messages "en-GB;q=0.9, fr-FR" "i18n" "en-GB"))))
|
||||
(is
|
||||
(= nil (get-messages "xx-XX;q=0.5, yy-YY" "i18n" "zz-ZZ"))
|
||||
"If no usable file is found, an exception should not be thrown.")))
|
||||
"If no usable file is found, an exception should not be thrown.")
|
||||
(binding [*default-language* "en-GB"]
|
||||
(is (= "This is not a pipe" (get-message :pipe)))
|
||||
(is
|
||||
(=
|
||||
"Ceci n'est pas une pipe." (get-message :pipe "en-GB;q=0.9, fr-FR")))
|
||||
(is (= "это не труба." (get-message :pipe "de-DE" "i18n" "ru"))))))
|
||||
|
|
Loading…
Reference in a new issue