什么是国际化?
在本文中,我们将为我们的春季REST API客户端点增加国际化支持。国际化是创建可以适应不同语言和地区的应用程序的过程。我们要实现的目标是增加对多种语言的消息的支持。因此,具有不同语言偏好的客户将根据语言获取特定的消息。
默认的自动配置
默认情况下,Spring Boot在类Path的根部寻找消息资源束的存在。当默认属性文件可用时,适用自动配置(默认情况下)。 Spring Boot在SCR/MAIN/RESOSDER文件夹中搜索消息文件。可以通过属性更改,如下所示:
spring.messages.basename=messages,config.i18n.messages
如果找到了属性文件,则Spring Boot配置了一个用于解决消息的MessageRebe Bean,并支持此类消息的参数化和国际化。
Spring Boot还配置了寻找位置的LocalerSolver实现。默认实现是Acceptheaderlocalereresolver,它检查接受接受语言标头以匹配请求的语言环境。
创建消息文件
消息存储在Message_xx.properties文件中的键值对中。 XX表示语言代码(通常是两个字母代码,例如en,fr,it ...)。此外,可以添加区域/国家/地区(通常是两个字母代码),以便对地区进行更多细粒度的控制。
对于我们的应用程序,将在资源文件夹中创建两个属性文件。第一个是后备消息。带英文内容的Properties文件。当请求中未指定语言环境时,将使用它。第二种是支持西班牙语,并将被命名为messages_es.properties
每个本地化文件中的密钥相同。它们的值或描述在每种语言中都有特殊性。例如,后备文件中的英语内容contians以下条目
customer.name.required=Name is required.
customer.name.size=Name must be at least 3 characters and at most 20 characters.
customer.name.invalid=Name can only contain letters and spaces.
customer.email.required=Email is required.
customer.email.invalid=Email is invalid.
customer.dob.past=Date of Birth must be in the past.
和西班牙语文件
customer.name.required=Nombre es obligatorio.
customer.name.size=Nombre debe tener al menos 3 caracteres y 20 a lo sumo.
customer.name.invalid=Nombre solo puede estar formado por letras y espacios.
customer.email.required=Email es obligatorio.
customer.email.invalid=Email es invalido.
customer.dob.past=Fecha de nacimiento debe estar en el pasado.
使用代码中的键
现在,消息键可以在我们的应用程序中使用。客户控制器在发送帖子或投票请求时验证客户请求。由于两种情况的验证均相同,让我们看一下客户更新的情况
@PutMapping("{customerId}")
public ResponseEntity<Customer> updateCustomer(
@PathVariable("customerId") Long id,
@Valid @RequestBody CustomerRequest customerRequest) {
Customer customer = customerRepo.findById(id)
.orElseThrow(() -> new EntityNotFoundException(id,
Customer.class));
Customer updatedCustomer = CustomerUtils.convertToCustomer(
customerRequest);
updatedCustomer.setId(id);
customerRepo.save(updatedCustomer);
return ResponseEntity.ok(updatedCustomer);
}
在“客户”类中,键放在约束的消息属性中。
public record CustomerRequest(
@NotBlank(message = "{customer.name.required}")
@Size(min = 3, max = 20, message = "{customer.name.size}")
@Pattern(regexp = "[a-zA-Z\\s]+", message = "
{customer.name.invalid}") String name,
@NotBlank(message = "{customer.email.required}")
@Email(message = "{customer.email.invalid}") String email,
@Past(message = "{customer.dob.past}") LocalDate dateOfBirth) {}
最后,处理验证异常的顾问
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
RestErrorResponse handleException(MethodArgumentNotValidException ex) {
String message = ex.getFieldErrors()
.stream()
.map(e -> e.getDefaultMessage())
.reduce("Errors found:", String::concat);
return new RestErrorResponse(
HttpStatus.BAD_REQUEST.value(), message,
LocalDateTime.now());
}
现在该测试了。我们的第一个尝试是在不通知接受语言标题的情况下提出一个请求,如下图所示
它落后于消息。专业文件,因此以英语显示错误。第二个请求将发送带有价值ES的接受语言标头。结果可以在下面查看
现在,错误现在以西班牙语回来。如果我们的应用程序需要支持更多的语言,那么它就像为所需语言环境创建新的消息文件一样简单。
仍然用英语进行硬编码的一件事是“发现错误:”字面前缀作为错误消息的一部分。让我们将其移至下一节中的属性文件。
MessageReSource类
org.springframework.context的类MessageRece提供接收翻译消息的方法。 Springboot将为我们创建此豆。然后,将其注入将使用消息的类中。就我们而
@RestControllerAdvice
public class GlobalExceptionHandler {
MessageSource messageSource;
public GlobalExceptionHandler(MessageSource messageSource) {
this.messageSource = messageSource;
}
...
}
接下来,键值消息将添加到特定于语言的属性文件中。对于英语
errors.found=Errors found:
和西班牙语
errors.found=Errores encontrados :
最后一步是用处理程序方法中的密钥替换硬编码值。
@ExceptionHandler(MethodArgumentNotValidException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
RestErrorResponse handleException(MethodArgumentNotValidException ex, Locale locale) {
String message = ex.getFieldErrors().stream()
.map(e -> e.getDefaultMessage())
.reduce(messageSource.getMessage("errors.found", null,
locale), String::concat);
return new RestErrorResponse(
HttpStatus.BAD_REQUEST.value(), message,
LocalDateTime.now());
}
请注意,可以在异常处理程序中以参数作为参数传递。正如预期的那样,当重新运行测试时,值正确翻译。在告知Accept语言标题的ES值时,返回以下响应
{
"status": 400,
"message": "Errores encontrados : Nombre debe tener al menos 3 caracteres y 20 a lo sumo.Fecha de nacimiento debe estar en el pasado.Email es invalido. ",
"timestamp": "2023-05-27T19:10:57.0064035"
}
自定义异常消息
在本节中,我们将翻译自定义异常消息。在我们的应用程序中,如果不存在要更新的客户,则会抛出EntityNotFoundException。该消息在异常类中进行了硬编码。但是,我们希望此消息可用于受支持的语言。
第一步是在提出此异常时为错误添加一个新键。
--- english file
entity.notFound=Entity {0} for id {1} was not found.
--- spanish file
entity.notFound=Entity {0} for id {1} was not found.
值{0}和{1}是参数的占位符。第一个参数将进入位置{0},第二个参数将应用于{1}。
第二步是为其声明一种新的异常处理程序方法。同样,MessagesRyce类提供了获取消息的方法。参数是在GetMethodMessage作为对象数组的第二个参数中传递的。这两个值都存储在自定义中,并在Instatiation时间设置。
@ExceptionHandler(EntityNotFoundException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
RestErrorResponse handleException(EntityNotFoundException ex,
Locale locale) {
return new RestErrorResponse(HttpStatus.BAD_REQUEST.value(),
messageSource.getMessage(
"entity.notFound",
new Object[]{ ex.getTheClassName(), ex.getId()} , locale),
LocalDateTime.now());
}
自定义异常代码低于
public class EntityNotFoundException extends RuntimeException {
private Long id;
private Class theClass;
public EntityNotFoundException(@NotNull Long id,@NotNull Class
theClass) {
super(theClass.getName()+" "+id+" not found!");
this.id = id;
this.theClass = theClass;
}
public Long getId() {
return id;
}
public String getTheClassName() {
return theClass.getSimpleName();
}
}
测试时间。让我们尝试更新不存在的客户。
结论
让我们回顾一下您在本文中学到的知识的要点:
- 什么是国际化及其在Springboot中如何启用。
- 什么是消息文件。
- 创建多语言自定义约束错误消息。
- 创建多语言自定义异常错误消息。
现在,您可以在春季项目中实现自己的多语言解决方案。可以找到源代码here。
在Java和Spring上订阅更多内容!