Thymeleaf 教程

多语言版 welcome

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

这个页面的第一个版本将非常简单:只有一个标题和一条欢迎信息。下面是我们的 /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 文档,因为 HTML5 规范不允许我们在 th:* 表单中使用这些非标准属性。事实上,我们甚至在<html>标签中添加了一个 xmlns:th 属性,这是绝对不是一个 HTML5 属性:

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

但这在模板处理中没有任何影响,但它就像一个咒语,可以防止 IDE 提示缺少所有 th:* 属性的命名空间定义。

那么,如果我们想让这个模板成为 html5 有效的呢?这个简单,切换到 Thymeleaf 的数据属性语法,使用 data- 前缀作为属性名,使用连字符(-)分隔符代替分号(:)。如下:

<!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>

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

注意:

这两种表示法是完全等价和可互换的,但是为了代码示例的简单性和紧凑性,本教程将使用名称空间表示法(th:*)。此外,th:* 表示法更通用,在所有 Thymeleaf 模板模式(XML、TEXT……)中都允许使用,而 data- 表示法只允许在 HTML 模式中使用。

使用 th:text 和外部化文本

外部化文本是将模板代码的片段从模板文件中提取出来,以便将它们保存在单独的文件中(通常是 .properties 文件),并且可以很容易地用其他语言编写等效文本替换(这个过程称为国际化或简称 i18n),外部化的文本片段通常被称为 "消息"。

消息总是有一个键来标识它们,Thymeeaf 允许您使用 #{…} 语法指定文本应与特定消息相对应:

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

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

  • th:text 属性,它计算其值(表达式)并将结果设置为标记的主体,有效地替换“Welcome to our grocery store!”文本。

  • #{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 流程模板所需的全部内容。让我们创建我们的主控制器。

上下文(Contexts)

为了处理我们的模板,我们将创建一个 HomeController 类,需要实现前面看到的 IGTVGController 接口,重写 process() 方法:

import java.io.Writer;
import java.util.Calendar;
import org.thymeleaf.ITemplateEngine;
import org.thymeleaf.context.WebContext;
import org.thymeleaf.web.IWebExchange;

/**
 * 主页控制器
 * @author Administrator
 */
public class HomeController implements IGTVGController {

    public HomeController() {
        super();
    }

    @Override
    public void process(final IWebExchange webExchange, final ITemplateEngine templateEngine,
            final Writer writer) throws Exception {
        WebContext ctx = new WebContext(webExchange, webExchange.getLocale());
        ctx.setVariable("today", Calendar.getInstance());
        templateEngine.process("home", ctx, writer);
    }

}

我们首先看到的是上下文的创建。Thymeleaf 上下文是一个实现 org.thymeleaf.context.IContext 接口的对象。上下文应包含在变量映射中执行模板引擎所需的所有数据,并引用必须用于外部化消息的区域设置。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 接口,旨在用于基于 Servlet API 的 Web 应用程序(如:Spring MVC)。

public interface IWebContext extends IContext {
	// 返回与模板执行关联的 IWebExchange 对象
	public IWebExchange getExchange();
}

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

  • org.thymeleaf.context.Context 实现了 IContext 接口

  • org.thymeleaf.context.WebContext 实现了 IWebContext 接口

正如你在控制器代码中看到的,我们需要使用 WebContext。事实上,我们必须这样做,因为使用 WebApplicationTemplateResolver 要求我们使用实现 IWebContext 的上下文。如下:

WebContext ctx = new WebContext(webExchange, webExchange.getLocale());

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

  • ${x}  将返回存储在 thymeleaf 上下文中或作为请求属性的变量 x。

  • ${param.x}  将返回一个名为 x 的请求参数(它可能是多值的)。

  • ${session.x}  将返回一个名为 x 的会话属性。

  • ${application.x}  将返回一个名为 x 的 Servlet 上下文属性。

执行模板引擎

context 对象准备好了,现在我们可以告诉模板引擎使用 context 来处理模板(通过它的名字),并将响应写入器(writer)传递给它,以便将响应写入它:

templateEngine.process("home", ctx, writer);

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

<!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>
说说我的看法
全部评论(
没有评论
关于
本网站专注于 Java、数据库(MySQL、Oracle)、Linux、软件架构及大数据等多领域技术知识分享。涵盖丰富的原创与精选技术文章,助力技术传播与交流。无论是技术新手渴望入门,还是资深开发者寻求进阶,这里都能为您提供深度见解与实用经验,让复杂编码变得轻松易懂,携手共赴技术提升新高度。如有侵权,请来信告知:hxstrive@outlook.com
公众号