2

I have the following Clojure code with a render function which renders a html page using enlive-html. Depending on the selected language a different html template is used.

As you can see, there is a lot of code duplication and I would like to remove it.

I was thinking of writing some macros but, if I understand correctly, the language (i.e. lang parameter) is not available at macro execution time because it is provided in the request and that is at execution time and not at compilation time.

I also tried to modify enlive in order to add i18n support at some later point but my Clojure skills are not there yet.

So the questions are:

How can I remove the code duplication in the code below?

Is enlive-html the way to go or should I use another library? Is there a library similar to enlive with support for i18n?

Thanks!

See the code here:

(ns myapp.core
  (:require [net.cgrand.enlive-html :as e))

(deftemplate not-found-en "en/404.html"
  [{path :path}]
  [:#path] (e/content path))

(deftemplate not-found-fr "fr/404.html"
  [{path :path}]
  [:#path] (e/content path))


(defn getTemplate [page lang]
  (case lang
      :en (case page
                :page/not-found not-found-en)
      :fr (case page
                :page/not-found not-found-fr)))

(defn render [lang [page params]]
  (apply (getTemplate page lang) params))
4

2 回答 2

2

一方面,编写一个宏来为任意一组语言生成你在此处拥有的确切代码并不难。另一方面,可能有比使用更好的方法deftemplate- defd 的东西是您希望在源代码中按名称引用的东西,而您只希望自动创建和使用这个东西。但是我不熟悉 enlive API,所以我不能说你应该做什么。

如果您决定改用宏,则可以编写如下内容:

(defmacro def-language-404s [languages]
  `(do
     ~@(for [lang languages]
         `(deftemplate ~(symbol (str "not-found-" lang)) ~(str lang "/404.html")
            [{path# :path}]
            [:#path] (e/content path#)))
     (defn get-template [page# lang#]
       (case page#
         :page/not-found (case lang#
                           ~@(for [lang languages
                                   clause [(keyword lang)
                                           (symbol (str "not-found-" lang))]]
                               clause))))))

user> (macroexpand-1 '(def-language-404s [en fr]))
(do
  (deftemplate not-found-en "en/404.html"
    [{path__2275__auto__ :path}]
    [:#path] (content path__2275__auto__))
  (deftemplate not-found-fr "fr/404.html"
    [{path__2275__auto__ :path}]
    [:#path] (content path__2275__auto__))
  (defn get-template [page__2276__auto__ lang__2277__auto__]
    (case page__2276__auto__
      :page/not-found (case lang__2277__auto__
                        :en not-found-en
                        :fr not-found-fr))))
于 2017-01-11T23:55:27.980 回答
1

经过相当多的Macro-Fu后,我得到了一个令我满意的结果。在一些不错的 stackoverflowers 的帮助下,我在 enlive 之上编写了以下宏:

(ns hello-enlive
  (:require [net.cgrand.enlive-html :refer [deftemplate]]))

(defn- template-name [lang page] (symbol (str "-template-" (name page) "-" (name lang) "__")))
(defn- html-file [lang page] (str (name lang) "/" (name page) ".html"))
(defn- page-fun-name [page] (symbol (str "-page" (name page))))

(defmacro def-page [app languages [page & forms]]
  `(do
     ~@(for [lang languages]
         `(deftemplate ~(template-name lang page) ~(html-file lang page)
            ~@forms))

      (defn ~(page-fun-name page) [lang#]
         (case lang#
           ~@(for [lang languages
                   clause [(keyword lang) (template-name lang page)]]
               clause)))

      (def ^:dynamic ~app
        (assoc ~app ~page ~(page-fun-name page)))
      ))

(defmacro def-app [app-name languages pages]
  (let [app (gensym "app__")]
    `(do
       (def ~(vary-meta app merge {:dynamic true}) {})

       ~@(for [page# pages]
           `(def-page ~app ~languages ~page#))

       (defn ~app-name [lang# [page# params#]]
         (apply (apply (get ~app page#) [lang#]) params#)))))

...然后像这样使用:

html模板存储在这样的树中

html/fr/not-found.html
html/fr/index.html
html/en/not-found.html
html/en/index.html
...

...渲染逻辑如下所示:

(def-app my-app [:en :it :fr :de]
  [ [:page/index [] ]

    ;... put your rendering here

    [:page/not-found [{path :path}]
      [:#path] (content path)]])

...并且用法如下所示:

...
(render lang [:page/index {}])
(render lang [:page/not-found {:path path}])
...

结果,虽然它可能可以改进,但我认为非常好,没有重复和样板代码。

于 2017-01-16T05:33:16.953 回答