Skip to content

Instantly share code, notes, and snippets.

@susimsek
Last active March 12, 2022 11:28
Show Gist options
  • Save susimsek/819f0df3ed1fac9d8d795d1cf44ec874 to your computer and use it in GitHub Desktop.
Save susimsek/819f0df3ed1fac9d8d795d1cf44ec874 to your computer and use it in GitHub Desktop.
Spring Boot i18n with database stored messages
package io.github.susimsek.springbootdemorest.config;
import io.swagger.v3.oas.models.parameters.Parameter;
import org.springdoc.core.customizers.OperationCustomizer;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
public class ApiDocConfig {
@Bean
public OperationCustomizer customize() {
return (operation, handlerMethod) -> operation.addParametersItem(
new Parameter()
.in("header")
.required(false)
.example("en")
.description("Language Header")
.name("Accept-Language"));
}
}
spring:
datasource:
url: jdbc:h2:mem:testdb;DB_CLOSE_DELAY=-1;DB_CLOSE_ON_EXIT=FALSE
driver-class-name: org.h2.Driver
username: sa
password: password
jpa:
hibernate:
ddl-auto: update
properties:
javax:
persistence:
validation:
mode: none
open-in-view: false
defer-datasource-initialization: true
h2:
console:
enabled: true
INSERT INTO messages (locale, code, content)
VALUES ('en', 'error.invalid.size', 'size must be between {min} and {max}');
INSERT INTO messages (locale, code, content)
VALUES ('tr', 'error.invalid.size', 'boyut {min} ile {max} arasında olmalıdır');
package io.github.susimsek.springbootdemorest.message;
import io.github.susimsek.springbootdemorest.domain.Message;
import io.github.susimsek.springbootdemorest.repository.MessageRepository;
import lombok.AccessLevel;
import lombok.RequiredArgsConstructor;
import lombok.experimental.FieldDefaults;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.support.ReloadableResourceBundleMessageSource;
import org.springframework.stereotype.Component;
import java.text.MessageFormat;
import java.util.Locale;
@Slf4j
@Component("messageSource")
@FieldDefaults(level = AccessLevel.PRIVATE)
@RequiredArgsConstructor
public class DatabaseMessageSource extends ReloadableResourceBundleMessageSource {
final MessageRepository messageRepository;
@Override
protected MessageFormat resolveCode(String code, Locale locale) {
return messageRepository.findByCodeAndLocale(code, locale.getLanguage())
.map(message -> new MessageFormat(message.getContent(), locale))
.orElse(super.resolveCode(code, locale));
}
@Override
protected String resolveCodeWithoutArguments(String code, Locale locale) {
return messageRepository.findByCodeAndLocale(code, locale.getLanguage())
.map(Message::getContent)
.orElse(super.resolveCodeWithoutArguments(code, locale));
}
}
package io.github.susimsek.springbootdemorest.domain;
import lombok.*;
import lombok.experimental.FieldDefaults;
import javax.persistence.*;
@Entity
@Table(name = "messages")
@FieldDefaults(level = AccessLevel.PRIVATE)
@Getter
@Setter
@ToString
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Message {
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Id
Long id;
String locale;
String code;
String content;
}
package io.github.susimsek.springbootdemorest.repository;
import io.github.susimsek.springbootdemorest.domain.Message;
import org.springframework.data.jpa.repository.JpaRepository;
import java.util.Optional;
public interface MessageRepository extends JpaRepository<Message, Long> {
Optional<Message> findByCodeAndLocale(String code, String locale);
}
<properties>
<java.version>17</java.version>
<springdoc.version>1.6.6</springdoc.version>
</properties>
<dependencies>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi-ui</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-actuator</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-web</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-validation</artifactId>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-data-jpa</artifactId>
</dependency>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
<scope>runtime</scope>
</dependency>
<dependency>
<groupId>org.projectlombok</groupId>
<artifactId>lombok</artifactId>
<optional>true</optional>
</dependency>
<dependency>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-test</artifactId>
<scope>test</scope>
</dependency>
</dependencies>
<dependencyManagement>
<dependencies>
<dependency>
<groupId>org.springdoc</groupId>
<artifactId>springdoc-openapi</artifactId>
<version>${springdoc.version}</version>
<type>pom</type>
<scope>import</scope>
</dependency>
</dependencies>
</dependencyManagement>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment