diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 7d45367..cd4f633 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -7,7 +7,6 @@ on: jobs: build: runs-on: ubuntu-latest - if: github.event_name != 'push' || startsWith(github.ref, 'refs/tags/') == false steps: - name: Debug dependabot condition env: @@ -43,14 +42,3 @@ jobs: env: COVERALLS_REPO_TOKEN: ${{ secrets.COVERALLS_REPO_TOKEN }} run: ./gradlew build jacocoTestReport coveralls --scan - - dependabot: - needs: [build] - runs-on: ubuntu-latest - if: ${{ github.triggering_actor == 'dependabot' && github.event_name == 'pull_request'}} - steps: - - name: Auto-merge Dependabot PRs - run: gh pr merge --auto --merge "$PR_URL" - env: - PR_URL: ${{github.event.pull_request.html_url}} - GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} \ No newline at end of file diff --git a/.github/workflows/dependabot.yml b/.github/workflows/dependabot.yml new file mode 100644 index 0000000..2045c1c --- /dev/null +++ b/.github/workflows/dependabot.yml @@ -0,0 +1,36 @@ +name: Dependabot + +on: pull_request_target + +permissions: + pull-requests: write + contents: write + +jobs: + dependabot: + needs: [build] + runs-on: ubuntu-latest + if: ${{ github.event.pull_request.user.login == 'dependabot[bot]' }} + steps: + - name: Dependabot metadata + id: dependabot-metadata + uses: dependabot/fetch-metadata@v1.3.1 + + - name: Enable auto-merge for Dependabot PRs + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Enable auto-merge Dependabot PRs + run: gh pr merge --auto --merge "$PR_URL" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + + - name: Approve patch and minor updates + if: ${{ steps.dependabot-metadata.outputs.update-type != 'version-update:semver-major' }} + run: gh pr review $PR_URL --approve -b "Pull request **approved** because **it includes a patch or minor update**" + env: + PR_URL: ${{ github.event.pull_request.html_url }} + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} \ No newline at end of file diff --git a/README.md b/README.md index 931cc91..3ae6eb3 100644 --- a/README.md +++ b/README.md @@ -31,7 +31,7 @@ dependencies { ## Basic usage ```java -I18nMessagePack messagePack=I18nMessagePack.builder() +I18nMessagePack messagePack = I18nMessagePack.builder() .scanClassPath("/i18n/messages-{locale}.yml") .setDefaultLocale(Locales.EN_US) .build(); @@ -77,7 +77,7 @@ Messages can be created in 3 ways: ### Manual messages creation ```java -I18nMessagePack messages=I18nMessagePack.builder() +I18nMessagePack messages = I18nMessagePack.builder() .addMessage(Loacles.EN_US, "hello", "Hello {0}") .addMessage(Loacles.PL_PL, "hello", "Cześć {0}") .setDefaultLocale(PL_PL) @@ -90,7 +90,7 @@ If you want to quickly load messages from a nested map (for example fetched from you can use `I18nParsers.parseEntries(map, locale)` to translate nested the map keys into localized message paths. ```java -I18nMessagePack messages=I18nMessagePack.builder() +I18nMessagePack messages = I18nMessagePack.builder() .addMessages(I18nParsers.parseEntries(Map.of("hello", "Hello {0}"), EN_US)) .build(); ``` @@ -98,7 +98,7 @@ I18nMessagePack messages=I18nMessagePack.builder() ### Loading messages from classpath or file system ```java -I18nMessagePack messages=I18nMessagePack.builder() +I18nMessagePack messages = I18nMessagePack.builder() .scanClassPath("/i18n/messages-{locale}.yml") .scanFileSystem("./overriddes/messages-{locale}.yml") .setDefaultLocale(PL_PL) @@ -158,13 +158,13 @@ If there is no match for message path and locale then less strict locale is used If there is still no match then the default locale (followed by a less strict default locale) is used. ```java -I18nMessagePack messages=I18nMessagePack.builder() +I18nMessagePack messages = I18nMessagePack.builder() .scanClassPath("/i18n/messages-{locale}.yml") .setDefaultLocale(PL_PL) .addFallbackKeyPrefix("glossary") .build(); -String message=messages.getMessage(Locales.en_US, "hello"); +String message = messages.getMessage(Locales.en_US, "hello"); ``` Locations used to find the message: @@ -179,7 +179,7 @@ Locations used to find the message: Sometimes it is useful to specify a common path prefix for all unmatched queries: ```java -I18nMessagePack messages=I18nMessagePack.builder() +I18nMessagePack messages = I18nMessagePack.builder() .scanClassPath("/i18n/messages-{locale}.yml") .setDefaultLocale(PL_PL) .addMessageFallbackKeyPrefix("common") @@ -202,7 +202,7 @@ Locations used to find the message: Sometimes it's useful to prefix all queries with some path, like in the example: ```java -I18nMessagePack messages=I18nMessagePack.builder() +I18nMessagePack messages = I18nMessagePack.builder() .scanClassPath("/i18n/messages-{locale}.yml") .setDefaultLocale(PL_PL) .build(); @@ -226,7 +226,7 @@ Locations used to find the message: Sometimes it's useful to apply common locale to all queries: ```java -I18nMessagePack messagePack=I18nMessagePack.builder() +I18nMessagePack messagePack = I18nMessagePack.builder() .scanClassPath("/i18n/messages-{locale}.yml") .setDefaultLocale(PL_PL) .build(); @@ -240,7 +240,7 @@ String subtitle = messages.getMessage("subtitle"); Query localization mechanism can be used together with query prefixes: ```java -I18nMessages messages=messagePack +I18nMessages messages = messagePack .prefixQueries("pages.homepage") .localize(req.getLocale()) ``` @@ -277,7 +277,7 @@ messages.getMessage("about-company") == "ACME was established on 1988" Let's configure messages: ```java -I18nMessagePack messagePack=I18nMessagePack.builder() +I18nMessagePack messagePack = I18nMessagePack.builder() .addMessage(EN_US, "msg", "${company.name} was established on 1988") .scanClassPath("/i18n/messages-{locale}.yml") .setDefaultLocale(PL_PL) @@ -299,7 +299,7 @@ Locations used to find the message: If the reference is defined in a message stored in a prefixed file it will be automatically prefixed: ```java -I18nMessagePack messagePack=I18nMessagePack.builder() +I18nMessagePack messagePack = I18nMessagePack.builder() .scanClassPathLocation("i18n/{prefix}/message_{locale}.yml") .setDefaultLocale(PL_PL) .addFallbackKeyPrefix("fallback") diff --git a/build.gradle b/build.gradle index 21cf805..89d962d 100644 --- a/build.gradle +++ b/build.gradle @@ -13,7 +13,7 @@ description = 'Coditory Quark I18N - Message Internationalization Library' dependencies { api 'org.slf4j:slf4j-api:2.0.6' - api 'org.jetbrains:annotations:23.1.0' + api 'org.jetbrains:annotations:24.0.0' implementation 'org.yaml:snakeyaml:1.33' implementation 'com.google.code.gson:gson:2.10.1' implementation 'com.ibm.icu:icu4j:72.1' diff --git a/src/main/java/com/coditory/quark/i18n/I18nMessagePackBuilder.java b/src/main/java/com/coditory/quark/i18n/I18nMessagePackBuilder.java index a14f7bc..3449a05 100755 --- a/src/main/java/com/coditory/quark/i18n/I18nMessagePackBuilder.java +++ b/src/main/java/com/coditory/quark/i18n/I18nMessagePackBuilder.java @@ -1,7 +1,5 @@ package com.coditory.quark.i18n; -import com.coditory.quark.i18n.loader.I18nClassPathLoader; -import com.coditory.quark.i18n.loader.I18nFileSystemLoader; import com.coditory.quark.i18n.loader.I18nLoader; import com.coditory.quark.i18n.loader.I18nMessageBundle; import org.jetbrains.annotations.NotNull; @@ -85,8 +83,7 @@ public I18nMessagePackBuilder scanFileSystemWithPrefix(@NotNull I18nPath prefix, expectNonNull(prefix, "prefix"); expectNonBlank(firstPattern, "firstPattern"); expectNonNull(fileSystem, "fileSystem"); - I18nLoader loader = I18nFileSystemLoader - .builder(fileSystem) + I18nLoader loader = I18nLoader.fileSystemLoader(fileSystem) .scanPathPattern(firstPattern) .scanPathPatterns(others) .staticKeyPrefix(prefix) @@ -128,8 +125,8 @@ public I18nMessagePackBuilder scanClassPathWithPrefix(@NotNull I18nPath prefix, expectNonNull(prefix, "prefix"); expectNonBlank(firstPattern, "firstPattern"); expectNonNull(classLoader, "classLoader"); - I18nLoader loader = I18nClassPathLoader - .builder(classLoader) + I18nLoader loader = I18nLoader + .classPathLoader(classLoader) .scanPathPattern(firstPattern) .scanPathPatterns(others) .staticKeyPrefix(prefix) diff --git a/src/main/java/com/coditory/quark/i18n/I18nMessages.java b/src/main/java/com/coditory/quark/i18n/I18nMessages.java index 5df5364..589bc83 100755 --- a/src/main/java/com/coditory/quark/i18n/I18nMessages.java +++ b/src/main/java/com/coditory/quark/i18n/I18nMessages.java @@ -1,6 +1,7 @@ package com.coditory.quark.i18n; import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; import java.util.Locale; import java.util.Map; @@ -25,12 +26,25 @@ public String getMessage(@NotNull I18nPath path, Object... args) { return messagePack.getMessage(locale, path, args); } + @Nullable + public String getMessageOrNull(@NotNull I18nPath path, Object... args) { + expectNonNull(path, "path"); + expectNonNull(args, "args"); + return messagePack.getMessageOrNull(locale, path, args); + } + @NotNull public String getMessage(@NotNull I18nPath path) { expectNonNull(path, "path"); return messagePack.getMessage(locale, path); } + @Nullable + public String getMessageOrNull(@NotNull I18nPath path) { + expectNonNull(path, "path"); + return messagePack.getMessageOrNull(locale, path); + } + @NotNull public String getMessage(@NotNull String key, Object... args) { expectNonBlank(key, "key"); @@ -38,6 +52,13 @@ public String getMessage(@NotNull String key, Object... args) { return messagePack.getMessage(locale, key, args); } + @Nullable + public String getMessageOrNull(@NotNull String key, Object... args) { + expectNonBlank(key, "key"); + expectNonNull(args, "args"); + return messagePack.getMessageOrNull(locale, key, args); + } + @NotNull public String getMessage(@NotNull String key, @NotNull Map args) { expectNonBlank(key, "key"); @@ -45,14 +66,27 @@ public String getMessage(@NotNull String key, @NotNull Map args) return messagePack.getMessage(locale, key, args); } + @Nullable + public String getMessageOrNull(@NotNull String key, @NotNull Map args) { + expectNonBlank(key, "key"); + expectNonNull(args, "args"); + return messagePack.getMessageOrNull(locale, key, args); + } + @NotNull public String getMessage(@NotNull String key) { expectNonNull(key, "key"); return getMessage(key, EMPTY_ARGS); } + @Nullable + public String getMessageOrNull(@NotNull String key) { + expectNonBlank(key, "key"); + return messagePack.getMessageOrNull(locale, key); + } + @NotNull - public I18nMessages addPrefix(@NotNull String prefix) { + public I18nMessages prefixQueries(@NotNull String prefix) { return messagePack.prefixQueries(prefix).localize(locale); } diff --git a/src/main/java/com/coditory/quark/i18n/loader/FileWatcher.java b/src/main/java/com/coditory/quark/i18n/loader/FileWatcher.java index c8744f7..6babccb 100644 --- a/src/main/java/com/coditory/quark/i18n/loader/FileWatcher.java +++ b/src/main/java/com/coditory/quark/i18n/loader/FileWatcher.java @@ -30,7 +30,7 @@ import static java.nio.file.StandardWatchEventKinds.ENTRY_MODIFY; import static java.util.Objects.requireNonNull; -public final class FileWatcher implements Runnable { +final class FileWatcher implements Runnable { private static final int MAX_DIRS_TO_WATCH = 1_000; private final Logger logger = LoggerFactory.getLogger(this.getClass()); private final Map watchedDirKeys = new HashMap<>(); diff --git a/src/main/java/com/coditory/quark/i18n/loader/I18nClassPathLoader.java b/src/main/java/com/coditory/quark/i18n/loader/I18nClassPathLoader.java index 8fa73f4..185e9c3 100644 --- a/src/main/java/com/coditory/quark/i18n/loader/I18nClassPathLoader.java +++ b/src/main/java/com/coditory/quark/i18n/loader/I18nClassPathLoader.java @@ -19,12 +19,12 @@ import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; -public final class I18nClassPathLoader implements I18nLoader { - public static I18nFileLoaderBuilder builder() { +final class I18nClassPathLoader implements I18nLoader { + static I18nFileLoaderBuilder builder() { return builder(Thread.currentThread().getContextClassLoader()); } - public static I18nFileLoaderBuilder builder(ClassLoader classLoader) { + static I18nFileLoaderBuilder builder(ClassLoader classLoader) { return new I18nFileLoaderBuilder() .scanClassPath(classLoader); } diff --git a/src/main/java/com/coditory/quark/i18n/loader/I18nFileSystemLoader.java b/src/main/java/com/coditory/quark/i18n/loader/I18nFileSystemLoader.java index accbe4d..8ede81b 100644 --- a/src/main/java/com/coditory/quark/i18n/loader/I18nFileSystemLoader.java +++ b/src/main/java/com/coditory/quark/i18n/loader/I18nFileSystemLoader.java @@ -26,12 +26,12 @@ import static java.util.Collections.unmodifiableList; import static java.util.Objects.requireNonNull; -public final class I18nFileSystemLoader implements WatchableI18nLoader { - public static I18nFileLoaderBuilder builder() { +final class I18nFileSystemLoader implements WatchableI18nLoader { + static I18nFileLoaderBuilder builder() { return builder(FileSystems.getDefault()); } - public static I18nFileLoaderBuilder builder(FileSystem fileSystem) { + static I18nFileLoaderBuilder builder(FileSystem fileSystem) { return new I18nFileLoaderBuilder() .scanFileSystem(fileSystem); } diff --git a/src/main/java/com/coditory/quark/i18n/loader/I18nLoader.java b/src/main/java/com/coditory/quark/i18n/loader/I18nLoader.java index 1b6f723..bd65f12 100644 --- a/src/main/java/com/coditory/quark/i18n/loader/I18nLoader.java +++ b/src/main/java/com/coditory/quark/i18n/loader/I18nLoader.java @@ -2,10 +2,30 @@ import org.jetbrains.annotations.NotNull; +import java.nio.file.FileSystem; +import java.nio.file.FileSystems; import java.util.List; @FunctionalInterface public interface I18nLoader { + static I18nFileLoaderBuilder classPathLoader() { + return classPathLoader(Thread.currentThread().getContextClassLoader()); + } + + static I18nFileLoaderBuilder classPathLoader(ClassLoader classLoader) { + return new I18nFileLoaderBuilder() + .scanClassPath(classLoader); + } + + static I18nFileLoaderBuilder fileSystemLoader() { + return fileSystemLoader(FileSystems.getDefault()); + } + + static I18nFileLoaderBuilder fileSystemLoader(FileSystem fileSystem) { + return new I18nFileLoaderBuilder() + .scanFileSystem(fileSystem); + } + @NotNull List load(); }