使用文本


3.1多语言欢迎

我们的第一个任务是为我们的杂货网站创建一个主页。

这个页面的第一个版本非常简单:只是标题和欢迎信息。这是我们的/WEB-INF/templates/home.html文件:

<!DOCTYPE html>

<html xmlns:th="http://www.thymeleaf.org">

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all"
          href="../../css/gtvg.css" th:href="@{/css/gtvg.css}" />
  </head>

  <body>

    <p th:text="#{home.welcome}">Welcome to our grocery store!</p>

  </body>

</html>

您将注意到的第一件事是该文件是HTML5,任何浏览器都可以正确显示它,因为它不包含任何非HTML标记(浏览器会忽略它们不理解的所有属性,例如th:text)。

但您可能还注意到此模板实际上不是有效的 HTML5文档,因为th:* HTML5规范不允许我们在表单中使用这些非标准属性。事实上,我们甚至xmlns:th在我们的标签中添加了一个属性,这绝对不是HTML5-ish:

<html xmlns:th="http://www.thymeleaf.org">

...在模板处理中根本没有任何影响,但作为一个咒语,阻止我们的IDE抱怨缺少所有这些th:* 属性的命名空间定义。

那么如果我们想让这个模板HTML5有效呢?简单:切换到Thymeleaf的数据属性语法,使用data-属性名称和hyphen(-)分隔符的前缀而不是分号(:):

<!DOCTYPE html>

<html>

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta http-equiv="Content-Type" content="text/html; charset=UTF-8" />
    <link rel="stylesheet" type="text/css" media="all"
          href="../../css/gtvg.css" data-th-href="@{/css/gtvg.css}" />
  </head>

  <body>

    <p data-th-text="#{home.welcome}">Welcome to our grocery store!</p>

  </body>

</html>

ata-HTML5规范允许使用自定义前缀属性,因此,使用上面的代码,我们的模板将是有效的HTML5文档。

两种表示法都是完全等效且可互换的,但为了代码示例的简单性和紧凑性,本教程将使用名称空间表示法(th: )。此外,th: 符号更通用,并且在每个Thymeleaf模板模式(XML,TEXT...)中data-都允许使用,而符号仅在HTML模式下允许。

使用th:文本和外化文本

外化文本是从模板文件中提取模板代码的片段,以便它们可以保存在单独的文件(通常是.properties文件)中,并且可以使用其他语言编写的等效文本(称为国际化或简称i18n)轻松替换它们。外化的文本片段通常称为“消息”。

消息总是有一个标识它们的键,而Thymeleaf允许您指定文本应该与具有以下#{...}语法的特定消息相对应:

<p th:text="#{home.welcome}">Welcome to our grocery store!</p>

我们在这里看到的实际上是Thymeleaf标准方言的两个不同特征:

  • 该th:text属性评估其值表达式并将结果设置为主机标签的主体,有效地替换了我们在代码中看到的“欢迎使用我们的杂货店!”文本。
  • #{home.welcome}表达,在指定的标准表达式语法,指示要由所使用的文本th:text属性应与该消息home.welcome对应于哪个语言环境,我们正在处理与模板键。

现在,这个外化文本在哪里?

Thymeleaf中外化文本的位置是完全可配置的,它取决于org.thymeleaf.messageresolver.IMessageResolver所使用的具体实现。通常,.properties将使用基于文件的实现,但是如果我们想要从数据库获取消息,我们可以创建自己的实现。

但是,我们在初始化期间没有为模板引擎指定消息解析器,这意味着我们的应用程序正在使用标准消息解析器,由org.thymeleaf.messageresolver.StandardMessageResolver。实现。

标准消息解析程序希望/WEB-INF/templates/home.html在属性文件中查找相同文件夹中的消息,并使用与模板相同的名称,如:

  • /WEB-INF/templates/home_en.properties 用于英文文本。
  • /WEB-INF/templates/home_es.properties 西班牙语文本。
  • /WEB-INF/templates/home_pt_BR.properties 用于葡萄牙语(巴西)语言文本。
  • /WEB-INF/templates/home.properties 对于默认文本(如果区域设置不匹配)。

我们来看看我们的home_es.properties文件:

home.welcome=¡Bienvenido a nuestra tienda de comestibles!

这就是我们将Thymeleaf流程作为模板所需的全部内容。让我们创建我们的Home控制器。

Contexts

为了处理我们的模板,我们将创建一个HomeController实现IGTVGController我们之前看到的接口的类:

public class HomeController implements IGTVGController {

    public void process(
            final HttpServletRequest request, final HttpServletResponse response,
            final ServletContext servletContext, final ITemplateEngine templateEngine)
            throws Exception {

        WebContext ctx =
                new WebContext(request, response, servletContext, request.getLocale());

        templateEngine.process("home", ctx, response.getWriter());

    }

}

我们看到的第一件事是创建一个上下文。Thymeleaf上下文是实现org.thymeleaf.context.IContext接口的对象。上下文应包含在变量映射中执行模板引擎所需的所有数据,并且还引用必须用于外部化消息的语言环境。

public interface IContext {

    public Locale getLocale();
    public boolean containsVariable(final String name);
    public Set<String> getVariableNames();
    public Object getVariable(final String name);

}

这个接口有一个专门的扩展,org.thymeleaf.context.IWebContext用于基于ServletAPI的Web应用程序(如SpringMVC)。

public interface IWebContext extends IContext {

    public HttpServletRequest getRequest();
    public HttpServletResponse getResponse();
    public HttpSession getSession();
    public ServletContext getServletContext();

}

Thymeleaf核心库提供了以下每个接口的实现:

org.thymeleaf.context.Context 器物 IContext org.thymeleaf.context.WebContext 器物 IWebContext 正如您在控制器代码中看到的那样,WebContext是我们使用的那个。实际上我们必须这样做,因为使用a ServletContextTemplateResolver要求我们使用上下文实现IWebContext。

  • org.thymeleaf.context.Context implements IContext
  • org.thymeleaf.context.WebContext implements IWebContext

正如您在控制器代码中看到的那样,WebContext是我们使用的那个。实际上我们必须这样做,因为使用a ServletContextTemplateResolver要求我们使用上下文实现IWebContext。

WebContext ctx = new WebContext(request, response, servletContext, request.getLocale());

这四个构造函数参数中只有三个是必需的,因为如果没有指定系统,将使用系统的默认语言环境(尽管在实际应用程序中不应该发生这种情况)。

我们可以使用一些专门的表达式来从WebContext我们的模板中获取请求参数以及请求,会话和应用程序属性。例如:

  • ${x}将返回x存储在Thymeleaf上下文中的变量或作为请求属性。
  • ${param.x}将返回一个被调用的请求参数x(可能是多值的)。
  • ${session.x}将返回一个名为的会话属性x。
  • ${application.x}将返回一个名为的servlet上下文属性x。

执行模板引擎

准备好上下文对象后,现在我们可以告诉模板引擎使用上下文处理模板(通过其名称),并将响应编写器传递给它,以便可以将响应写入它:

templateEngine.process("home", ctx, response.getWriter());

让我们看看使用西班牙语语言环境的结果:

<!DOCTYPE html>

<html>

  <head>
    <title>Good Thymes Virtual Grocery</title>
    <meta content="text/html; charset=UTF-8" http-equiv="Content-Type"/>
    <link rel="stylesheet" type="text/css" media="all" href="/gtvg/css/gtvg.css" />
  </head>

  <body>

    <p>¡Bienvenido a nuestra tienda de comestibles!</p>

  </body>

</html>

3.2有关文本和变量的更多信息

未转义的文字

我们主页的最简单版本现在似乎已经准备就绪,但是有一些我们没有想过的......如果我们有这样的消息怎么办?

home.welcome=Welcome to our <b>fantastic</b> grocery store!

如果我们像以前一样执行此模板,我们将获得:

<p>Welcome to our &lt;b&gt;fantastic&lt;/b&gt; grocery store!</p>

这不完全符合我们的预期,因为我们的标签已被转义,因此它将显示在浏览器中。

这是th:text属性的默认行为。如果我们希望Thymeleaf尊重我们的HTML标签而不是逃避它们,我们将不得不使用不同的属性:( th:utext对于“未转义的文本”):

<p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

这将输出我们的消息,就像我们想要的那样:

<p>Welcome to our <b>fantastic</b> grocery store!</p>

使用和显示变量

现在让我们在主页上添加更多内容。例如,我们可能希望在欢迎消息下方显示日期,如下所示:

Welcome to our fantastic grocery store!

Today is: 12 july 2010

首先,我们必须修改控制器,以便将该日期添加为上下文变量:

public void process(
            final HttpServletRequest request, final HttpServletResponse response,
            final ServletContext servletContext, final ITemplateEngine templateEngine)
            throws Exception {

    SimpleDateFormat dateFormat = new SimpleDateFormat("dd MMMM yyyy");
    Calendar cal = Calendar.getInstance();

    WebContext ctx =
            new WebContext(request, response, servletContext, request.getLocale());
    ctx.setVariable("today", dateFormat.format(cal.getTime()));

    templateEngine.process("home", ctx, response.getWriter());

}

我们添加了一个String调用today我们上下文的变量,现在我们可以在模板中显示它:

<body>

  <p th:utext="#{home.welcome}">Welcome to our grocery store!</p>

  <p>Today is: <span th:text="${today}">13 February 2011</span></p>

</body>

正如您所看到的,我们仍在使用th:text作业的属性(这是正确的,因为我们想要替换标签的主体),但这次语法有点不同而不是#{...}表达式值,我们使用的是${...}一。这是一个变量表达式,它包含一个名为OGNL(对象图形导航语言)的语言表达式,该表达式将在我们之前讨论过的上下文变量映射上执行。

该${today}表达式只是表示“今天拿到称为变量”,但这些表述可能更加复杂(如${user.name}“获取被叫用户的变量,并调用它的getName()方法”)。

属性值有很多可能性:消息,变量表达式......还有很多。下一章将向我们展示所有这些可能性。