|
| 1 | +# Translatable for Spring Boot JPA |
| 2 | + |
| 3 | +[](https://opensource.org/licenses/MIT) |
| 4 | +[](https://central.sonatype.com/artifact/com.mewebstudio/spring-boot-jpa-translatable-kotlin) |
| 5 | +[](https://javadoc.io/doc/com.mewebstudio/spring-boot-jpa-translatable-kotlin) |
| 6 | + |
| 7 | +This module provides an abstract and reusable foundation for supporting **translatable (multi-language) entities** using Spring Data JPA. |
| 8 | +It defines core interfaces, abstract repositories, and a base service class to handle translations with locale-specific logic. |
| 9 | + |
| 10 | +--- |
| 11 | + |
| 12 | +## 📦 Package Structure |
| 13 | + |
| 14 | +``` |
| 15 | +com.mewebstudio.springboot.jpa.translatable |
| 16 | +├── ITranslatable.kt |
| 17 | +├── ITranslation.kt |
| 18 | +├── JpaTranslatableRepository.kt |
| 19 | +├── JpaTranslationRepository.kt |
| 20 | +└── AbstractTranslatableService.kt |
| 21 | +``` |
| 22 | + |
| 23 | +--- |
| 24 | + |
| 25 | +## 🧩 Interfaces |
| 26 | + |
| 27 | +### `ITranslatable<ID, T extends ITranslation<ID, ?>>` |
| 28 | + |
| 29 | +Represents an entity that supports translations. |
| 30 | + |
| 31 | +```kotlin |
| 32 | +interface ITranslatable<ID, T : ITranslation<ID, *>> { |
| 33 | + val id: ID |
| 34 | + val translations: MutableList<T> |
| 35 | +} |
| 36 | +``` |
| 37 | + |
| 38 | +--- |
| 39 | + |
| 40 | +### `ITranslation<ID, T>` |
| 41 | + |
| 42 | +Represents a translation of an entity in a specific locale. |
| 43 | + |
| 44 | +```kotlin |
| 45 | +interface ITranslation<ID, T> { |
| 46 | + val id: ID |
| 47 | + val owner: T |
| 48 | + val locale: String |
| 49 | +} |
| 50 | +``` |
| 51 | + |
| 52 | +--- |
| 53 | + |
| 54 | +## 🗃 Repositories |
| 55 | + |
| 56 | +### `JpaTranslatableRepository<T : ITranslatable<ID, TR>, ID, TR : ITranslation<ID, *>> : JpaRepository<T, ID>` |
| 57 | + |
| 58 | +Generic JPA repository for translatable entities. |
| 59 | + |
| 60 | +```kotlin |
| 61 | +@NoRepositoryBean |
| 62 | +interface JpaTranslatableRepository<T : ITranslatable<ID, TR>, ID, TR : ITranslation<ID, *>> : JpaRepository<T, ID> { |
| 63 | + // ... |
| 64 | +} |
| 65 | +``` |
| 66 | + |
| 67 | +--- |
| 68 | + |
| 69 | +### `JpaTranslationRepository<T, ID, OWNER>` |
| 70 | + |
| 71 | +Generic JPA repository for translation entities. |
| 72 | + |
| 73 | +```kotlin |
| 74 | +@NoRepositoryBean |
| 75 | +interface JpaTranslationRepository<T : ITranslation<ID, OWNER>, ID, OWNER> : JpaRepository<T, ID> { |
| 76 | + // ... |
| 77 | +} |
| 78 | +``` |
| 79 | + |
| 80 | +--- |
| 81 | + |
| 82 | +## 🧠 Abstract Service |
| 83 | + |
| 84 | +### `AbstractTranslatableService<T, ID, TR>` |
| 85 | + |
| 86 | +Provides a base service class for business logic operations. |
| 87 | + |
| 88 | +```kotlin |
| 89 | +abstract class AbstractTranslatableService<T : ITranslatable<ID, TR>, ID, TR : ITranslation<ID, *>>( |
| 90 | + open val repository: JpaTranslatableRepository<T, ID, TR> |
| 91 | +) { |
| 92 | + // ... |
| 93 | +} |
| 94 | +``` |
| 95 | + |
| 96 | +### `AbstractTranslationService<T : ITranslation<ID, OWNER>, ID, OWNER>` |
| 97 | + |
| 98 | +Provides a base service class for business logic operations. |
| 99 | + |
| 100 | +```kotlin |
| 101 | +abstract class AbstractTranslationService<T : ITranslation<ID, OWNER>, ID, OWNER>( |
| 102 | + open val repository: JpaTranslationRepository<T, ID, OWNER> |
| 103 | +) { |
| 104 | + // ... |
| 105 | +} |
| 106 | +``` |
| 107 | + |
| 108 | +--- |
| 109 | + |
| 110 | +## 📥 Installation |
| 111 | + |
| 112 | +#### for maven users |
| 113 | +Add the following dependency to your `pom.xml` file: |
| 114 | +```xml |
| 115 | +<dependency> |
| 116 | + <groupId>com.mewebstudio</groupId> |
| 117 | + <artifactId>spring-boot-jpa-translatable-kotlin</artifactId> |
| 118 | + <version>0.1.0</version> |
| 119 | +</dependency> |
| 120 | +``` |
| 121 | +#### for gradle users |
| 122 | +Add the following dependency to your `build.gradle` file: |
| 123 | +```groovy |
| 124 | +implementation 'com.mewebstudio:spring-boot-jpa-translatable-kotlin:0.1.0' |
| 125 | +``` |
| 126 | + |
| 127 | +--- |
| 128 | + |
| 129 | +## 📌 Usage |
| 130 | + |
| 131 | +You can extend these interfaces and abstract class to implement your own translatable entities and services: |
| 132 | + |
| 133 | +### Translatable Entity Example |
| 134 | +```kotlin |
| 135 | +@Entity |
| 136 | +@Table(name = "categories") |
| 137 | +class Category( |
| 138 | + @Id |
| 139 | + val id: Long, |
| 140 | + |
| 141 | + @OneToMany(mappedBy = "owner", cascade = [CascadeType.ALL], orphanRemoval = true, fetch = FetchType.LAZY) |
| 142 | + @OrderBy("locale ASC") |
| 143 | + override var translations: MutableList<CategoryTranslation> = mutableListOf(), |
| 144 | +) : ITranslatable<String, CategoryTranslation> { |
| 145 | + override fun toString(): String = "${this::class.simpleName}(id = $id)" |
| 146 | +} |
| 147 | +``` |
| 148 | + |
| 149 | +### Translation Entity Example |
| 150 | +```kotlin |
| 151 | +@Entity |
| 152 | +class CategoryTranslation( |
| 153 | + @Id |
| 154 | + val id: Long, |
| 155 | + |
| 156 | + @ManyToOne(fetch = FetchType.LAZY) |
| 157 | + @JoinColumn(name = "category_id", nullable = false) |
| 158 | + @OnDelete(action = OnDeleteAction.CASCADE) |
| 159 | + override val owner: Category, |
| 160 | + |
| 161 | + @Column(name = "locale", nullable = false) |
| 162 | + override val locale: String, |
| 163 | + |
| 164 | + @Column(name = "name", nullable = false, length = 255) |
| 165 | + var name: String, |
| 166 | + |
| 167 | + @Column(name = "description", columnDefinition = "text") |
| 168 | + var description: String? = null, |
| 169 | +) : ITranslation<String, Category> { |
| 170 | + override fun toString(): String = |
| 171 | + "${this::class.simpleName}(id = $id, name = $name, locale = $locale, owner = $owner)" |
| 172 | +} |
| 173 | +``` |
| 174 | + |
| 175 | +### Translatable Repository Example |
| 176 | +```kotlin |
| 177 | +interface CategoryRepository : JpaTranslatableRepository<Category, String, CategoryTranslation> |
| 178 | +``` |
| 179 | + |
| 180 | +### Translation Repository Example |
| 181 | +```kotlin |
| 182 | +interface CategoryTranslationRepository : JpaTranslationRepository<CategoryTranslation, String, Category> |
| 183 | +``` |
| 184 | + |
| 185 | +### Translatable Service Example |
| 186 | +```kotlin |
| 187 | +@Service |
| 188 | +class CategoryService( |
| 189 | + private val categoryRepository: CategoryRepository, |
| 190 | + private val categoryTranslationRepository: CategoryTranslationRepository |
| 191 | +) : AbstractTranslatableService<Category, String, CategoryTranslation>(categoryRepository) { |
| 192 | + private val log: Logger by logger() |
| 193 | + |
| 194 | + init { |
| 195 | + log.debug("CategoryService initialized with repository: {}", repository) |
| 196 | + requireNotNull(repository) { "CategoryRepository cannot be null" } |
| 197 | + } |
| 198 | + |
| 199 | + // Custom business logic methods can be added here... |
| 200 | + |
| 201 | +} |
| 202 | +``` |
| 203 | + |
| 204 | +--- |
| 205 | + |
| 206 | +## 🛠 Requirements |
| 207 | + |
| 208 | +- Java 17+ |
| 209 | +- Kotlin 1.9.23+ |
| 210 | +- Spring Boot 3.x |
| 211 | +- Spring Data JPA |
| 212 | + |
| 213 | +--- |
| 214 | + |
| 215 | +## 🔁 Other Implementations |
| 216 | + |
| 217 | +[Spring Boot JPA Translatable (Java Maven Package)](https://github.com/mewebstudio/spring-boot-jpa-translatable) |
| 218 | + |
| 219 | +## 💡 Example Implementations |
| 220 | + |
| 221 | +[Spring Boot JPA Translatable - Kotlin Implementation](https://github.com/mewebstudio/spring-boot-jpa-translatable-kotlin-impl) |
| 222 | + |
| 223 | +[Spring Boot JPA Translatable - Java Implementation](https://github.com/mewebstudio/spring-boot-jpa-translatable-java-impl) |
| 224 | + |
| 225 | +## 📃 License |
| 226 | + |
| 227 | +MIT © [mewebstudio](https://github.com/mewebstudio) |
0 commit comments