Support resolving i18n message with standard way (#6648)

#### What type of PR is this?

/kind feature
/area theme
/sig docs
/milestone 2.20.x

#### What this PR does / why we need it:

After this PR, we can define i18n message files next to the template file.

```yaml
i18n:
    default.properties
templates:
    index.html
    index.properties # Higher properties than default.properties
    index_zh.properties # Higher properties than index.properties
    index_zh_CN.properties # Higher priority than index_zh.properties
```

It's convenient for plugins that define the template files.

See https://www.thymeleaf.org/doc/tutorials/3.1/usingthymeleaf.html#standard-message-resolver for more.

#### Does this PR introduce a user-facing change?

```release-note
支持在主题中通过 Thymeleaf 默认行为实现国际化
```
pull/6671/head
John Niang 2024-09-14 10:52:30 +08:00 committed by GitHub
parent c5f9c766bb
commit a9c0ecebe3
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
8 changed files with 23 additions and 8 deletions

View File

@ -1,7 +1,10 @@
package run.halo.app.theme.message;
import java.util.Collections;
import java.util.HashMap;
import java.util.Locale;
import java.util.Map;
import java.util.Optional;
import org.thymeleaf.messageresolver.StandardMessageResolver;
import org.thymeleaf.templateresource.ITemplateResource;
import run.halo.app.theme.ThemeContext;
@ -22,7 +25,12 @@ public class ThemeMessageResolver extends StandardMessageResolver {
protected Map<String, String> resolveMessagesForTemplate(String template,
ITemplateResource templateResource,
Locale locale) {
return ThemeMessageResolutionUtils.resolveMessagesForTemplate(locale, theme);
var properties = new HashMap<String, String>();
Optional.ofNullable(ThemeMessageResolutionUtils.resolveMessagesForTemplate(locale, theme))
.ifPresent(properties::putAll);
Optional.ofNullable(super.resolveMessagesForTemplate(template, templateResource, locale))
.ifPresent(properties::putAll);
return Collections.unmodifiableMap(properties);
}
@Override

View File

@ -29,16 +29,16 @@ class ThemeMessageResolutionUtilsTest {
void resolveMessagesForTemplateForDefault() throws URISyntaxException {
Map<String, String> properties =
ThemeMessageResolutionUtils.resolveMessagesForTemplate(Locale.CHINESE, getTheme());
assertThat(properties).hasSize(1);
assertThat(properties).containsEntry("index.welcome", "欢迎来到首页");
assertThat(properties).isEqualTo(Map.of("index.welcome", "欢迎来到首页",
"title", "来自 i18n/zh.properties 的标题"));
}
@Test
void resolveMessagesForTemplateForEnglish() throws URISyntaxException {
Map<String, String> properties =
ThemeMessageResolutionUtils.resolveMessagesForTemplate(Locale.ENGLISH, getTheme());
assertThat(properties).hasSize(1);
assertThat(properties).containsEntry("index.welcome", "Welcome to the index");
assertThat(properties).isEqualTo(Map.of("index.welcome", "Welcome to the index",
"title", "这是来自 i18n/default.properties 的标题"));
}
@Test

View File

@ -93,6 +93,9 @@ public class ThemeMessageResolverIntegrationTest {
.expectStatus()
.isOk()
.expectBody()
// make sure the "templates/index.properties" file is precedence over the
// "i18n/default.properties".
.xpath("/html/head/title").isEqualTo("Title from index.properties")
.xpath("/html/body/div[1]").isEqualTo("foo")
.xpath("/html/body/div[2]").isEqualTo("欢迎来到首页");
}
@ -105,7 +108,7 @@ public class ThemeMessageResolverIntegrationTest {
.expectStatus()
.isOk()
.expectBody()
.xpath("/html/head/title").isEqualTo("Title")
.xpath("/html/head/title").isEqualTo("来自 index_zh.properties 的标题")
.xpath("/html/body/div[1]").isEqualTo("zh")
.xpath("/html/body/div[2]").isEqualTo("欢迎来到首页")
;

View File

@ -1 +1,2 @@
index.welcome=\u6B22\u8FCE\u6765\u5230\u9996\u9875
index.welcome=欢迎来到首页
title=这是来自 i18n/default.properties 的标题

View File

@ -0,0 +1 @@
title=来自 i18n/zh.properties 的标题

View File

@ -2,7 +2,7 @@
<html lang="en">
<head>
<meta charset="UTF-8"/>
<title>Title</title>
<title th:text="#{title}">Title</title>
</head>
<body>
index

View File

@ -0,0 +1 @@
title=Title from index.properties

View File

@ -0,0 +1 @@
title=来自 index_zh.properties 的标题