如何在使用 Spring Boot 的 REST API 构建中本地化域对象的各个值?
How to localize individual values of domain objects in a REST API build with Spring Boot?
我想实现一个 API Rest 来验证国际化数据库并根据区域配置将其替换为在 MESSAGES table 中找到的数据库。数据库中的产品:
Insert into PRODUCTS (ID, NAME, PRICE) VALUES (1, 'products.name.generatedproductcode', 100.00);
其中 NAME 是 MESSAGE 的 i18n 键 table
在消息中table
Insert into MESSAGES (LOCALE,KEY_REFERENCE,VALUE) values ('es_ES','products.name.generatedproductcode','teléfono móvil');
Insert into MESSAGES (LOCALE,KEY_REFERENCE,VALUE) values ('en_GB','products.name.generatedproductcode','mobile phone');
我想遇到这样一种情况,我必须复制可以用注释标记的代码,例如 in18n 字段或使用 aspectj 将值替换为 Locale
中存在的语言
@Entity
public class Product {
private Long id;
@CustomI18nResource
private String name;
private Double price;
}
有一些限制,例如 Locale 只能在 HTTP 中使用它。我目前正在从控制器和值中手动替换(因为有多个实体,所以在代码级别上有些昂贵)。我在想我应该怎么做才能将 KEY 发送到前端并在前端管理 i18n。有没有人遇到过这个问题,是如何解决的?
解法:
Oliver 的回答正是我想要做的,非常感谢
我已经编写了解决方案并将其发布在 Github 上:
这里可能有用的抽象是 MessageSource
。在 Spring 框架中,它通常用于根据资源包(属性文件)解析消息,但您可以轻松实现数据库支持的变体,可能更愿意使用 JDBC 来避免开销和大量缓存值。
如果有,您可以使用 MessageSourceAccessor
例如在您的控制器代码中准备要为手头的域 object 呈现的模型,该模型知道要 i18nize 的值,查找翻译后的值并将其放入模型中。 …Accessor
抽象从任何配置的 LocaleResolver
中获取语言环境。 IIRC,在 Spring Boot 中,这是一个 AcceptHeaderLocaleResolver
,即 Accept-Language
HTTP 请求中给出的值 header 定义了选择的语言。
如果你想完全自动化,你可能想插入你的序列化机制来为你透明地进行翻译。 Jackson 允许自定义序列化程序,以便那些可以使用 MessageSourceAccessor
自动翻译配备特定注释的所有字段。
@Retention(RetentionPolicy.RUNTIME)
@interface JsonInternationalized {}
@RequiredArgsConstructor
class I18nModule extends SimpleModule {
private final MessageSource messageSource;
@Override
public void setupModule(SetupContext context) {
MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource);
InternationalizedSerializer serializer = new InternationalizedSerializer(accessor);
context.addBeanSerializerModifier(new InternationalizingBeanSerializerModifier(serializer));
}
@RequiredArgsConstructor
static class InternationalizingBeanSerializerModifier extends BeanSerializerModifier {
private final InternationalizedSerializer serializer;
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter writer : beanProperties) {
if (writer.getAnnotation(JsonInternationalized.class) != null) {
writer.assignSerializer(serializer);
}
}
return beanProperties;
}
}
@RequiredArgsConstructor
static class InternationalizedSerializer extends ToStringSerializer {
private static final long serialVersionUID = -2391442803792997283L;
private final MessageSourceAccessor accessor;
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(accessor.getMessage(value.toString()));
}
}
}
以下是重要方面:
- 我们定义一个注释来触发 i18n。
I18nModule
注册了一个 BeanSerializerModifier
来检查编写者是否使用所述注释注释了 bean 属性,并为那些将通过 [=12] 解析 属性 的属性注册了一个自定义序列化程序=] 交入模块。
在 Spring 引导应用程序中,您只需在配置 class:
中将其注册为 Spring bean 即可使该模块工作
@Bean
I18nModule i18nModule(MessageSource messageSource) {
return new I18nModule(messageSource);
}
如您所见,这需要一点仪式感。我要把它带回团队,看看我们是否可以稍微改善开箱即用的体验。
我想实现一个 API Rest 来验证国际化数据库并根据区域配置将其替换为在 MESSAGES table 中找到的数据库。数据库中的产品:
Insert into PRODUCTS (ID, NAME, PRICE) VALUES (1, 'products.name.generatedproductcode', 100.00);
其中 NAME 是 MESSAGE 的 i18n 键 table
在消息中table
Insert into MESSAGES (LOCALE,KEY_REFERENCE,VALUE) values ('es_ES','products.name.generatedproductcode','teléfono móvil');
Insert into MESSAGES (LOCALE,KEY_REFERENCE,VALUE) values ('en_GB','products.name.generatedproductcode','mobile phone');
我想遇到这样一种情况,我必须复制可以用注释标记的代码,例如 in18n 字段或使用 aspectj 将值替换为 Locale
中存在的语言@Entity
public class Product {
private Long id;
@CustomI18nResource
private String name;
private Double price;
}
有一些限制,例如 Locale 只能在 HTTP 中使用它。我目前正在从控制器和值中手动替换(因为有多个实体,所以在代码级别上有些昂贵)。我在想我应该怎么做才能将 KEY 发送到前端并在前端管理 i18n。有没有人遇到过这个问题,是如何解决的?
解法:
Oliver 的回答正是我想要做的,非常感谢
我已经编写了解决方案并将其发布在 Github 上:
这里可能有用的抽象是 MessageSource
。在 Spring 框架中,它通常用于根据资源包(属性文件)解析消息,但您可以轻松实现数据库支持的变体,可能更愿意使用 JDBC 来避免开销和大量缓存值。
如果有,您可以使用 MessageSourceAccessor
例如在您的控制器代码中准备要为手头的域 object 呈现的模型,该模型知道要 i18nize 的值,查找翻译后的值并将其放入模型中。 …Accessor
抽象从任何配置的 LocaleResolver
中获取语言环境。 IIRC,在 Spring Boot 中,这是一个 AcceptHeaderLocaleResolver
,即 Accept-Language
HTTP 请求中给出的值 header 定义了选择的语言。
如果你想完全自动化,你可能想插入你的序列化机制来为你透明地进行翻译。 Jackson 允许自定义序列化程序,以便那些可以使用 MessageSourceAccessor
自动翻译配备特定注释的所有字段。
@Retention(RetentionPolicy.RUNTIME)
@interface JsonInternationalized {}
@RequiredArgsConstructor
class I18nModule extends SimpleModule {
private final MessageSource messageSource;
@Override
public void setupModule(SetupContext context) {
MessageSourceAccessor accessor = new MessageSourceAccessor(messageSource);
InternationalizedSerializer serializer = new InternationalizedSerializer(accessor);
context.addBeanSerializerModifier(new InternationalizingBeanSerializerModifier(serializer));
}
@RequiredArgsConstructor
static class InternationalizingBeanSerializerModifier extends BeanSerializerModifier {
private final InternationalizedSerializer serializer;
@Override
public List<BeanPropertyWriter> changeProperties(SerializationConfig config, BeanDescription beanDesc,
List<BeanPropertyWriter> beanProperties) {
for (BeanPropertyWriter writer : beanProperties) {
if (writer.getAnnotation(JsonInternationalized.class) != null) {
writer.assignSerializer(serializer);
}
}
return beanProperties;
}
}
@RequiredArgsConstructor
static class InternationalizedSerializer extends ToStringSerializer {
private static final long serialVersionUID = -2391442803792997283L;
private final MessageSourceAccessor accessor;
@Override
public void serialize(Object value, JsonGenerator gen, SerializerProvider provider) throws IOException {
gen.writeString(accessor.getMessage(value.toString()));
}
}
}
以下是重要方面:
- 我们定义一个注释来触发 i18n。
I18nModule
注册了一个BeanSerializerModifier
来检查编写者是否使用所述注释注释了 bean 属性,并为那些将通过 [=12] 解析 属性 的属性注册了一个自定义序列化程序=] 交入模块。
在 Spring 引导应用程序中,您只需在配置 class:
中将其注册为 Spring bean 即可使该模块工作@Bean
I18nModule i18nModule(MessageSource messageSource) {
return new I18nModule(messageSource);
}
如您所见,这需要一点仪式感。我要把它带回团队,看看我们是否可以稍微改善开箱即用的体验。