mirror of https://github.com/halo-dev/halo
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
parent
c5f9c766bb
commit
a9c0ecebe3
|
@ -1,7 +1,10 @@
|
||||||
package run.halo.app.theme.message;
|
package run.halo.app.theme.message;
|
||||||
|
|
||||||
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Locale;
|
import java.util.Locale;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
import java.util.Optional;
|
||||||
import org.thymeleaf.messageresolver.StandardMessageResolver;
|
import org.thymeleaf.messageresolver.StandardMessageResolver;
|
||||||
import org.thymeleaf.templateresource.ITemplateResource;
|
import org.thymeleaf.templateresource.ITemplateResource;
|
||||||
import run.halo.app.theme.ThemeContext;
|
import run.halo.app.theme.ThemeContext;
|
||||||
|
@ -22,7 +25,12 @@ public class ThemeMessageResolver extends StandardMessageResolver {
|
||||||
protected Map<String, String> resolveMessagesForTemplate(String template,
|
protected Map<String, String> resolveMessagesForTemplate(String template,
|
||||||
ITemplateResource templateResource,
|
ITemplateResource templateResource,
|
||||||
Locale locale) {
|
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
|
@Override
|
||||||
|
|
|
@ -29,16 +29,16 @@ class ThemeMessageResolutionUtilsTest {
|
||||||
void resolveMessagesForTemplateForDefault() throws URISyntaxException {
|
void resolveMessagesForTemplateForDefault() throws URISyntaxException {
|
||||||
Map<String, String> properties =
|
Map<String, String> properties =
|
||||||
ThemeMessageResolutionUtils.resolveMessagesForTemplate(Locale.CHINESE, getTheme());
|
ThemeMessageResolutionUtils.resolveMessagesForTemplate(Locale.CHINESE, getTheme());
|
||||||
assertThat(properties).hasSize(1);
|
assertThat(properties).isEqualTo(Map.of("index.welcome", "欢迎来到首页",
|
||||||
assertThat(properties).containsEntry("index.welcome", "欢迎来到首页");
|
"title", "来自 i18n/zh.properties 的标题"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
void resolveMessagesForTemplateForEnglish() throws URISyntaxException {
|
void resolveMessagesForTemplateForEnglish() throws URISyntaxException {
|
||||||
Map<String, String> properties =
|
Map<String, String> properties =
|
||||||
ThemeMessageResolutionUtils.resolveMessagesForTemplate(Locale.ENGLISH, getTheme());
|
ThemeMessageResolutionUtils.resolveMessagesForTemplate(Locale.ENGLISH, getTheme());
|
||||||
assertThat(properties).hasSize(1);
|
assertThat(properties).isEqualTo(Map.of("index.welcome", "Welcome to the index",
|
||||||
assertThat(properties).containsEntry("index.welcome", "Welcome to the index");
|
"title", "这是来自 i18n/default.properties 的标题"));
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
|
|
@ -93,6 +93,9 @@ public class ThemeMessageResolverIntegrationTest {
|
||||||
.expectStatus()
|
.expectStatus()
|
||||||
.isOk()
|
.isOk()
|
||||||
.expectBody()
|
.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[1]").isEqualTo("foo")
|
||||||
.xpath("/html/body/div[2]").isEqualTo("欢迎来到首页");
|
.xpath("/html/body/div[2]").isEqualTo("欢迎来到首页");
|
||||||
}
|
}
|
||||||
|
@ -105,7 +108,7 @@ public class ThemeMessageResolverIntegrationTest {
|
||||||
.expectStatus()
|
.expectStatus()
|
||||||
.isOk()
|
.isOk()
|
||||||
.expectBody()
|
.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[1]").isEqualTo("zh")
|
||||||
.xpath("/html/body/div[2]").isEqualTo("欢迎来到首页")
|
.xpath("/html/body/div[2]").isEqualTo("欢迎来到首页")
|
||||||
;
|
;
|
||||||
|
|
|
@ -1 +1,2 @@
|
||||||
index.welcome=\u6B22\u8FCE\u6765\u5230\u9996\u9875
|
index.welcome=欢迎来到首页
|
||||||
|
title=这是来自 i18n/default.properties 的标题
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
title=来自 i18n/zh.properties 的标题
|
|
@ -2,7 +2,7 @@
|
||||||
<html lang="en">
|
<html lang="en">
|
||||||
<head>
|
<head>
|
||||||
<meta charset="UTF-8"/>
|
<meta charset="UTF-8"/>
|
||||||
<title>Title</title>
|
<title th:text="#{title}">Title</title>
|
||||||
</head>
|
</head>
|
||||||
<body>
|
<body>
|
||||||
index
|
index
|
||||||
|
|
|
@ -0,0 +1 @@
|
||||||
|
title=Title from index.properties
|
|
@ -0,0 +1 @@
|
||||||
|
title=来自 index_zh.properties 的标题
|
Loading…
Reference in New Issue