如何在使用 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 上:

https://github.com/jestevez/springboot-i18n-database

这里可能有用的抽象是 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()));
    }
  }
}

以下是重要方面:

  1. 我们定义一个注释来触发 i18n。
  2. I18nModule 注册了一个 BeanSerializerModifier 来检查编写者是否使用所述注释注释了 bean 属性,并为那些将通过 [=12] 解析 属性 的属性注册了一个自定义序列化程序=] 交入模块。

在 Spring 引导应用程序中,您只需在配置 class:

中将其注册为 Spring bean 即可使该模块工作
@Bean
I18nModule i18nModule(MessageSource messageSource) {
  return new I18nModule(messageSource);
}

如您所见,这需要一点仪式感。我要把它带回团队,看看我们是否可以稍微改善开箱即用的体验。