diff --git a/.github/workflows/maven-test.yml b/.github/workflows/maven-test.yml
new file mode 100644
index 0000000..b4404f3
--- /dev/null
+++ b/.github/workflows/maven-test.yml
@@ -0,0 +1,28 @@
+name: "Maven Test"
+run-name: Maven Test [${{ github.ref_type }}][${{ github.ref }}]
+on:
+ push:
+ branches:
+ - main
+ - stage
+ - dev
+# workflow_dispatch:
+jobs:
+ tests:
+ name: Run tests
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Setup Java
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-package: 'jdk'
+ java-version: '17'
+ check-latest: true
+ cache: 'maven'
+ - name: Install & Run Rests
+ run: |
+ mvn install
+ mvn test
diff --git a/.github/workflows/ossrh-release.yml b/.github/workflows/ossrh-release.yml
new file mode 100644
index 0000000..5dd1042
--- /dev/null
+++ b/.github/workflows/ossrh-release.yml
@@ -0,0 +1,35 @@
+# Action configuration is based on the following articles:
+# https://blogs.itemis.com/en/github-actions-releasing-artifacts-into-maven-central
+name: "OSSRH Release"
+run-name: OSSRH Release [${{ github.ref_type }}][${{ github.ref }}]
+on:
+ workflow_dispatch:
+jobs:
+ release:
+ name: Release
+ runs-on: ubuntu-22.04
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v3
+ - name: Setup Java
+ uses: actions/setup-java@v3
+ with:
+ distribution: 'temurin'
+ java-package: 'jdk'
+ java-version: '17'
+ check-latest: true
+ server-id: 'ossrh'
+ server-username: OSSRH_USERNAME
+ server-password: OSSRH_PASSWORD
+ gpg-private-key: ${{ secrets.GPG_PRIVATE_KEY }}
+ gpg-passphrase: GPG_PRIVATE_KEY_PASSPHRASE
+ cache: 'maven'
+ - name: Build & Deploy
+ run: |
+ mvn -U -B clean deploy -P release
+ env:
+ GPG_PRIVATE_KEY_PASSPHRASE: ${{ secrets.GPG_PRIVATE_KEY_PASSPHRASE }}
+ GPG_PRIVATE_KEY: ${{ secrets.GPG_PRIVATE_KEY }}
+ OSSRH_USERNAME: ${{ secrets.OSSRH_USERNAME }}
+ OSSRH_PASSWORD: ${{ secrets.OSSRH_PASSWORD }}
+
diff --git a/.github/workflows/sonarcloud.yml b/.github/workflows/sonarcloud.yml
new file mode 100644
index 0000000..2db72e9
--- /dev/null
+++ b/.github/workflows/sonarcloud.yml
@@ -0,0 +1,57 @@
+name: "SonarCloud Analyze"
+run-name: SonarCloud Analyze [${{ github.ref_type }}][${{ github.ref }}]
+on:
+ # push:
+ # branches:
+ # - master
+ # pull_request:
+ # types: [opened, synchronize, reopened]
+ workflow_dispatch:
+jobs:
+ build:
+ name: Build and analyze
+ runs-on: ubuntu-latest
+ env:
+ GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+ SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
+ # SONAR_PROJECT_KEY: ${{ vars.SONAR_PROJECT_KEY }} # -Dsonar.projectKey=$SONAR_PROJECTKEY
+ steps:
+ - uses: actions/checkout@v3
+ with:
+ fetch-depth: 0
+ - name: Get branch name
+ id: branch-name
+ uses: tj-actions/branch-names@v7
+ - name: Set up JDK 11
+ uses: actions/setup-java@v3
+ with:
+ java-version: 17
+ distribution: 'zulu'
+ - name: Cache SonarCloud packages
+ uses: actions/cache@v3
+ with:
+ path: ~/.sonar/cache
+ key: ${{ runner.os }}-sonar
+ restore-keys: ${{ runner.os }}-sonar
+ - name: Cache Maven packages
+ uses: actions/cache@v3
+ with:
+ path: ~/.m2
+ key: ${{ runner.os }}-m2-${{ hashFiles('**/pom.xml') }}
+ restore-keys: ${{ runner.os }}-m2
+ - name: Build and analyze (dev)
+ run: |
+ mvn test -P test-coverage
+ mvn -B verify org.sonarsource.scanner.maven:sonar-maven-plugin:sonar \
+ -Dsonar.projectName=${{ vars.SONAR_PROJECT_NAME }} \
+ -Dsonar.projectKey=${{ vars.SONAR_PROJECT_KEY }} \
+ -Dsonar.branch.name=${{ steps.branch-name.outputs.ref_branch }} \
+ -Dsonar.organization=${{ vars.SONAR_ORGANIZATION }} \
+ -Dsonar.host.url=${{ vars.SONAR_HOST_URL }} \
+ -Dsonar.coverage.jacoco.xmlReportPaths=${{ vars.SONAR_COVERAGE_JACOCO_XML_REPORT_PATHS }} \
+ -Dsonar.sources=${{ vars.SONAR_SOURCES }} \
+ -Dsonar.tests=${{ vars.SONAR_TESTS }} \
+ -Dsonar.exclusions=${{ vars.SONAR_EXCLUSIONS }} \
+ -Dsonar.java.binaries=${{ vars.SONAR_JAVA_BINARIES }} \
+ -Dsonar.coverage.exclusions=${{ vars.SONAR_COVERAGE_EXCLUSIONS }} \
+ -Dsonar.java.source=${{ vars.SONAR_JAVA_SOURCE }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..d491469
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,13 @@
+HELP.md
+target/
+!**/src/main/**/target/
+!**/src/test/**/target/
+mvnw
+mvnw.cmd
+/.mvn/
+
+### IntelliJ IDEA ###
+.idea
+*.iws
+*.iml
+*.ipr
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..6fa477e
--- /dev/null
+++ b/README.md
@@ -0,0 +1,409 @@
+# XLIFF translation support for Spring Boot and Spring
+
+This package provides a **MessageSource** for using translations from XLIFF files. The package support XLIFF versions 1.2, 2.0 and 2.1.
+
+**Table of content**
+
+1. [Version](#Versions)
+2. [Dependency](#Dependency)
+3. [MessageSource Configuration](#MessageSource-Configuration)
+4. [Minimal CacheManager Configuration](#Minimal-CacheManager-configuration)
+5. [CacheManager with supported Cache Providers](#CacheManager-with-supported-Cache-Providers)
+6. [Cache warming with an ApplicationRunner (recommended)](#Cache-warming-with-an-ApplicationRunner-recommended)
+7. [Xliff Translations files](#Xliff-Translations-files)
+8. [Example with Translations files](#Example-with-Translations-files)
+9. [Full Example](#Full-Example)
+10. [Support](#Support)
+## Versions
+
+| Version | Description |
+|:--------|:--------------------------------------------|
+| 1.0.0 | First public version |
+
+
+[![Quality Gate Status](https://sonarcloud.io/api/project_badges/measure?project=alaugks_spring-xliff-translation&metric=alert_status)](https://sonarcloud.io/summary/overall?id=alaugks_spring-xliff-translation) [![Maven Central](https://img.shields.io/maven-central/v/io.github.alaugks/spring-messagesource-xliff.svg?label=Maven%20Central)](https://central.sonatype.com/artifact/io.github.alaugks/spring-messagesource-xliff/1.0.0)
+
+## Dependency
+
+**Maven**
+```xml
+
+ io.github.alaugks
+ spring-messagesource-xliff
+ 1.0.0
+
+```
+
+**Gradle**
+```text
+implementation group: 'io.github.alaugks', name: 'spring-messagesource-xliff', version: '1.0.0'
+```
+
+## MessageSource Configuration
+
+The class XliffTranslationMessageSource implements the [MessageSource](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/context/MessageSource.html) interface. An instance of the [CacheManager](https://docs.spring.io/spring-boot/docs/2.1.6.RELEASE/reference/html/boot-features-caching.html#boot-features-caching-provider) is required for caching the translations.
+
+### XliffTranslationMessageSource
+
+`setBasenamePattern(String basename)` or `setBasenamesPattern(Iterable basenames)` (*mandatory*)
+
+* Defines the pattern used to select the XLIFF files.
+* The package uses the [PathMatchingResourcePatternResolver](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/core/io/support/PathMatchingResourcePatternResolver.html) to select the XLIFF files. So you can use the supported patterns.
+* Files with the extension `xliff` and `xlf` are filtered from the result list.
+
+`setDefaultLocale(Locale locale)` (*mandatory*)
+* Defines the default language.
+
+`setDefaultDomain(String defaultDomain)`
+* Defines the default domain. Default is 'messages'. For more information, see Xliff translations files.
+
+> Please note the [Minimal CacheManager Configuration](#Minimal-CacheManager-configuration).
+
+```java
+import de.alaugks.spring.XliffTranslationMessageSource;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.cache.CacheManager;
+import org.springframework.context.MessageSource;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Locale;
+
+@Configuration
+public class MessageConfig {
+
+ @Bean("messageSource")
+ public MessageSource messageSource(CacheManager cacheManager) {
+ XliffMessageSourcePatternResolver messageSource = new XliffTranslationMessageSource(cacheManager);
+ messageSource.setDefaultLocale(Locale.forLanguageTag("en"));
+ messageSource.setBasenamePattern("translations/*");
+ return messageSource;
+ }
+
+}
+```
+
+## Minimal CacheManager Configuration
+
+You may already have an existing CacheManager configuration. If not, the following minimum CacheManager configuration is required.
+
+The CacheName must be set with the constant `CatalogCache.CACHE_NAME`. The specific cache identifier is stored in the constant. Currently you cannot set a custom cache name.
+
+[ConcurrentMapCacheManager](https://docs.spring.io/spring-framework/docs/current/javadoc-api/org/springframework/cache/concurrent/ConcurrentMapCacheManager.html) is the default cache in Spring Boot and Spring.
+
+### CacheConfig
+
+```java
+import io.github.alaugks.spring.messagesource.xliff.catalog.CatalogCache;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.concurrent.ConcurrentMapCacheManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.List;
+
+@Configuration
+@EnableCaching
+public class CacheConfig {
+ @Bean
+ public CacheManager cacheManager() {
+ ConcurrentMapCacheManager cacheManager = new ConcurrentMapCacheManager();
+ cacheManager.setCacheNames(List.of(CatalogCache.CACHE_NAME));
+ return cacheManager;
+ }
+}
+```
+
+## CacheManager with supported Cache Providers
+
+A [supported Cache Providers](https://docs.spring.io/spring-boot/docs/3.1.1/reference/html/io.html#io.caching.provider) can also be used. Here is an example using [Caffeine](https://github.com/ben-manes/caffeine):
+
+### CacheConfig
+The CacheName must be set with the constant `CatalogCache.CACHE_NAME`. No ExpireDate should be set for the XLIFF Translations cache.
+
+```java
+import com.github.benmanes.caffeine.cache.Caffeine;
+import io.github.alaugks.spring.messagesource.xliff.catalog.CatalogCache;
+import org.springframework.cache.CacheManager;
+import org.springframework.cache.annotation.EnableCaching;
+import org.springframework.cache.caffeine.CaffeineCacheManager;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.util.Collection;
+import java.util.List;
+
+@Configuration
+@EnableCaching
+class CacheConfig {
+ @Bean
+ public Caffeine