這篇文章是「Thymeleaf擴充套件「的後續教學。 本文中的程式碼來自相同的範例應用程式,您可以從其GitHub倉庫檢視或下載該應用程式。
到目前為止,HelloDialect允許將此變為:
<p hello:sayto="World">Hi ya!</p>
它工作得很好,但這裡需要新增一些附加功能,作為學習演示。 例如:
hello:sayto="${user.name}"
國際化輸出:對英語說「Hello」,對西班牙語說「Hola」,對葡萄牙語說「Olá」等。
上面已經準備好了所有的工作,這裡希望能夠建立一個名為「saytoplanet」的新屬性,並向太陽系中的所有行星問候,其模板如下:
<ul>
<li th:each="planet : ${planets}" hello:saytoplanet="${planet}">Hello Planet!</li>
</ul>
它由一個Spring MVC控制器支援,該控制器包括所有這些行星作為一個叫作planets
的模型屬性:
@Controller
public class SayHelloController {
public SayHelloController() {
super();
}
@ModelAttribute("planets")
public List<String> populatePlanets() {
return Arrays.asList(new String[] {
"Mercury", "Venus", "Earth", "Mars", "Jupiter", "Saturn", "Uranus", "Neptune"
});
}
@RequestMapping({"/","/sayhello"})
public String showSayHello() {
return "sayhello";
}
}
這裡做的第一件事是新增一個新的處理器到現有的HelloDialect
中。 為此,需要修改方言的getProcessors()
方法,以包含新的SayToPlanetAttrProcessor
類:
public class HelloDialect extends AbstractProcessorDialect {
...
/*
* Initialize the dialect's processors.
*
* Note the dialect prefix is passed here because, although we set
* "hello" to be the dialect's prefix at the constructor, that only
* works as a default, and at engine configuration time the user
* might have chosen a different prefix to be used.
*/
public Set<IProcessor> getProcessors(final String dialectPrefix) {
final Set<IProcessor> processors = new HashSet<IProcessor>();
processors.add(new SayToAttributeTagProcessor(dialectPrefix));
processors.add(new SayToPlanetAttributeTagProcessor(dialectPrefix));
return processors;
}
...
}
現在想要在新處理器中新增解析和執行表示式的能力,就像在Standard
和SpringStandard
方言中所做的那樣,下面是Thymeleaf
標準表示式:
${...}
- Spring EL變數表示式。#{...}
- 訊息的外部化。@{...}
- 連結規範。(cond)? (then) : (else)
- 條件/預設表示式。為了實現這一點,將使用標準表示式解析器,它將解析屬性值為一個可執行的表示式物件:
public class SayToPlanetAttributeTagProcessor extends AbstractAttributeTagProcessor {
private static final String ATTR_NAME = "saytoplanet";
private static final int PRECEDENCE = 10000;
private static final String SAYTO_PLANET_MESSAGE = "msg.helloplanet";
public SayToPlanetAttributeTagProcessor(final String dialectPrefix) {
super(
TemplateMode.HTML, // This processor will apply only to HTML mode
dialectPrefix, // Prefix to be applied to name for matching
null, // No tag name: match any tag name
false, // No prefix to be applied to tag name
ATTR_NAME, // Name of the attribute that will be matched
true, // Apply dialect prefix to attribute name
PRECEDENCE, // Precedence (inside dialect's precedence)
true); // Remove the matched attribute afterwards
}
protected void doProcess(
final ITemplateContext context, final IProcessableElementTag tag,
final AttributeName attributeName, final String attributeValue,
final IElementTagStructureHandler structureHandler) {
/*
* In order to evaluate the attribute value as a Thymeleaf Standard Expression,
* we first obtain the parser, then use it for parsing the attribute value into
* an expression object, and finally execute this expression object.
*/
final IEngineConfiguration configuration = context.getConfiguration();
final IStandardExpressionParser parser =
StandardExpressions.getExpressionParser(configuration);
final IStandardExpression expression = parser.parseExpression(context, attributeValue);
final String planet = (String) expression.execute(context);
/*
* Set the salutation as the body of the tag, HTML-escaped and
* non-processable (hence the 'false' argument)
*/
structureHandler.setBody("Hello, planet " + planet, false);
}
}
請注意,正如在前一篇文章中所做的,擴充套件AbstractAttributeTagProcessor
抽象類。
現在要將屬性處理器返回的訊息國際化。這意味著替換這個僅是英文的訊息構建程式碼:
"Hello, planet " + planet;
從外部化的字串構建的訊息,x必須以某種方式從程式碼中獲得。 上下文物件(ITemplateContext)提供需要的東西:
public String getMessage(
final Class<?> origin,
final String key,
final Object[] messageParameters,
final boolean useAbsentMessageRepresentation);
它的引數有以下含義:
所以下面用它來實現一些國際化。 首先,需要一些.properties
檔案,如西班牙語的SayToPlanetAttributeTagProcessor_es.properties
:
msg.helloplanet=?Hola, planeta {0}!
葡萄牙語的SayToPlanetAttributeTagProcessor_pt.properties
:
msg.helloplanet=Olá, planeta {0}!
等等,其它語言。
現在將不得不修改SayToPlanetAttributeTagProcessor
處理器類來使用這些訊息:
protected void doProcess(
final ITemplateContext context, final IProcessableElementTag tag,
final AttributeName attributeName, final String attributeValue,
final IElementTagStructureHandler structureHandler) {
/*
* In order to evaluate the attribute value as a Thymeleaf Standard Expression,
* we first obtain the parser, then use it for parsing the attribute value into
* an expression object, and finally execute this expression object.
*/
final IEngineConfiguration configuration = context.getConfiguration();
final IStandardExpressionParser parser =
StandardExpressions.getExpressionParser(configuration);
final IStandardExpression expression = parser.parseExpression(context, attributeValue);
final String planet = (String) expression.execute(context);
/*
* This 'getMessage(...)' method will first try to resolve the message
* from the configured Spring Message Sources (because this is a Spring
* -enabled application).
*
* If not found, it will try to resolve it from a classpath-bound
* .properties with the same name as the specified 'origin', which
* in this case is this processor's class itself. This allows resources
* to be packaged if needed in the same .jar files as the processors
* they are used in.
*/
final String i18nMessage =
context.getMessage(
SayToPlanetAttributeTagProcessor.class,
SAYTO_PLANET_MESSAGE,
new Object[] {planet},
true);
/*
* Set the computed message as the body of the tag, HTML-escaped and
* non-processable (hence the 'false' argument)
*/
structureHandler.setBody(HtmlEscape.escapeHtml5(i18nMessage), false);
}protected void doProcess(
final ITemplateContext context, final IProcessableElementTag tag,
final AttributeName attributeName, final String attributeValue,
final IElementTagStructureHandler structureHandler) {
/*
* In order to evaluate the attribute value as a Thymeleaf Standard Expression,
* we first obtain the parser, then use it for parsing the attribute value into
* an expression object, and finally execute this expression object.
*/
final IEngineConfiguration configuration = context.getConfiguration();
final IStandardExpressionParser parser =
StandardExpressions.getExpressionParser(configuration);
final IStandardExpression expression = parser.parseExpression(context, attributeValue);
final String planet = (String) expression.execute(context);
/*
* This 'getMessage(...)' method will first try to resolve the message
* from the configured Spring Message Sources (because this is a Spring
* -enabled application).
*
* If not found, it will try to resolve it from a classpath-bound
* .properties with the same name as the specified 'origin', which
* in this case is this processor's class itself. This allows resources
* to be packaged if needed in the same .jar files as the processors
* they are used in.
*/
final String i18nMessage =
context.getMessage(
SayToPlanetAttributeTagProcessor.class,
SAYTO_PLANET_MESSAGE,
new Object[] {planet},
true);
/*
* Set the computed message as the body of the tag, HTML-escaped and
* non-processable (hence the 'false' argument)
*/
structureHandler.setBody(HtmlEscape.escapeHtml5(i18nMessage), false);
}
接下來看看使用西班牙語區域設定執行模板的結果: