Skip to content

Commit

Permalink
Improve query prefixes
Browse files Browse the repository at this point in the history
  • Loading branch information
ogesaku committed Apr 27, 2024
1 parent 677b585 commit 41d9b5f
Show file tree
Hide file tree
Showing 12 changed files with 148 additions and 139 deletions.
9 changes: 4 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -182,7 +182,6 @@ If there is still no match then the default locale (followed by a less strict de
I18nMessagePack messages = I18nMessagePack.builder()
.scanClassPath("/i18n/messages-{locale}.yml")
.setDefaultLocale(PL_PL)
.addFallbackKeyPrefix("glossary")
.build();

String message = messages.getMessage(Locales.en_US, "hello");
Expand All @@ -203,7 +202,7 @@ Sometimes it is useful to specify a common path prefix for all unmatched queries
I18nMessagePack messages = I18nMessagePack.builder()
.scanClassPath("/i18n/messages-{locale}.yml")
.setDefaultLocale(PL_PL)
.addMessageFallbackKeyPrefix("common")
.prefixQueries("", "common")
.build();

String message = messages.getMessage(Locales.en_US, "hello");
Expand Down Expand Up @@ -304,7 +303,7 @@ I18nMessagePack messagePack = I18nMessagePack.builder()
.addMessage(EN_US, "msg", "${company.name} was established on 1988")
.scanClassPath("/i18n/messages-{locale}.yml")
.setDefaultLocale(PL_PL)
.addFallbackKeyPrefix("fallback")
.prefixQueries("", "fallback")
.build();
```

Expand All @@ -325,7 +324,7 @@ If the reference is defined in a message stored in a prefixed file it will be au
I18nMessagePack messagePack = I18nMessagePack.builder()
.scanClassPathLocation("i18n/{prefix}/message_{locale}.yml")
.setDefaultLocale(PL_PL)
.addFallbackKeyPrefix("fallback")
.prefixQueries("", "fallback")
.build();
```

Expand Down Expand Up @@ -442,7 +441,7 @@ You can skip them using a custom missing message detector:
I18nMissingMessagesDetector detector = I18nMissingMessagesDetector.builder()
.skipPath(skipPath)
.logMissingMessages()
.build()
.build();

I18nMessagePack.

Expand Down
34 changes: 19 additions & 15 deletions build-logic/src/main/kotlin/build.version.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ data class Semver(
fun nextPatchSnapshot(): Semver {
return copy(
patch = patch + 1,
suffix = "SNAPSHOT"
suffix = "SNAPSHOT",
)
}

Expand All @@ -60,7 +60,10 @@ data class Semver(
companion object {
private val REGEX = Regex("v?([0-9]+)\\.([0-9]+)\\.([0-9]+)(-.+)?")

fun parse(text: String, strict: Boolean = true): Semver? {
fun parse(
text: String,
strict: Boolean = true,
): Semver? {
val groups = REGEX.matchEntire(text)?.groups ?: return null
if (groups.size < 4 || groups.size > 5) return null
if (strict && groups[0]?.value?.startsWith("v") == true) return null
Expand All @@ -84,19 +87,20 @@ data class Semver(
command: String,
workingDir: File = File("."),
timeoutAmount: Long = 60,
timeoutUnit: TimeUnit = TimeUnit.SECONDS
): String = ProcessBuilder("sh", "-c", command)
.directory(workingDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start()
.apply { waitFor(timeoutAmount, timeoutUnit) }
.run {
val error = errorStream.bufferedReader().readText().trim()
if (error.isNotEmpty()) {
throw IOException(error)
timeoutUnit: TimeUnit = TimeUnit.SECONDS,
): String =
ProcessBuilder("sh", "-c", command)
.directory(workingDir)
.redirectOutput(ProcessBuilder.Redirect.PIPE)
.redirectError(ProcessBuilder.Redirect.PIPE)
.start()
.apply { waitFor(timeoutAmount, timeoutUnit) }
.run {
val error = errorStream.bufferedReader().readText().trim()
if (error.isNotEmpty()) {
throw IOException(error)
}
inputStream.bufferedReader().readText().trim()
}
inputStream.bufferedReader().readText().trim()
}
}
}
64 changes: 33 additions & 31 deletions src/main/java/com/coditory/quark/i18n/I18nKeyGenerator.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,67 +8,69 @@

final class I18nKeyGenerator {
private final List<Locale> defaultLocales;
private final List<I18nPath> globalPrefixes;
private final List<I18nPath> defaultPrefixes;
private final LocaleResolver localeResolver;

public I18nKeyGenerator(Locale defaultLocale, List<I18nPath> globalPrefixes, LocaleResolver localeResolver) {
public I18nKeyGenerator(Locale defaultLocale, List<I18nPath> prefixes, LocaleResolver localeResolver) {
this(defaultLocale != null ? localeResolver.getLocaleHierarchy(defaultLocale) : List.of(), prefixes, localeResolver);
}

private I18nKeyGenerator(List<Locale> defaultLocales, List<I18nPath> prefixes, LocaleResolver localeResolver) {
expectNonNull(defaultLocales, "defaultLocales");
expectNonNull(localeResolver, "localeResolver");
expectNonNull(globalPrefixes, "globalPrefixes");
this.defaultLocales = defaultLocale != null
? localeResolver.getLocaleHierarchy(defaultLocale)
: List.of();
this.globalPrefixes = List.copyOf(globalPrefixes);
expectNonNull(prefixes, "prefixes");
this.defaultLocales = defaultLocales;
this.defaultPrefixes = prefixes.isEmpty() ? List.of(I18nPath.root()) : List.copyOf(prefixes);
this.localeResolver = localeResolver;
}

I18nKeyGenerator withPrefixes(List<I18nPath> prefixes) {
expectNonNull(prefixes, "prefixes");
return new I18nKeyGenerator(defaultLocales, prefixes, localeResolver);
}

List<I18nKey> keys(I18nKey key) {
expectNonNull(key, "key");
return keys(key, List.of());
}

List<I18nKey> keys(I18nKey key, I18nPath prefix) {
expectNonNull(key, "key");
return prefix == null || prefix.isRoot()
? keys(key)
: keys(key, List.of(prefix));
return keys(key, List.of(prefix));
}

List<I18nKey> keys(I18nKey key, List<I18nPath> prefixes) {
expectNonNull(key, "key");
expectNonNull(prefixes, "prefixes");
List<Locale> locales = localeResolver.getLocaleHierarchy(key.locale());
I18nPath path = key.path();
List<I18nKey> keys = new ArrayList<>(6 * (1 + prefixes.size() + globalPrefixes.size()));
// locales x prefix + path
List<I18nKey> keys = new ArrayList<>(6 * (1 + prefixes.size() + this.defaultPrefixes.size()));
// locales x (prefix + path)
for (I18nPath prefix : prefixes) {
I18nPath prefixed = prefix.child(path);
for (Locale loc : locales) {
keys.add(I18nKey.of(loc, prefix.child(path)));
keys.add(I18nKey.of(loc, prefixed));
}
}
// locales x path
for (Locale loc : locales) {
keys.add(I18nKey.of(loc, path));
}
// locales x globalPrefixes
for (I18nPath prefix : globalPrefixes) {
// locales * (defaultPrefixes + path)
for (I18nPath prefix : this.defaultPrefixes) {
I18nPath prefixed = prefix.child(path);
for (Locale loc : locales) {
keys.add(I18nKey.of(loc, prefix.child(path)));
keys.add(I18nKey.of(loc, prefixed));
}
}
// defaultLocales x prefix + path
// defaultLocales x (prefix + path)
for (I18nPath prefix : prefixes) {
for (Locale loc : defaultLocales) {
keys.add(I18nKey.of(loc, prefix.child(path)));
I18nPath prefixed = prefix.child(path);
for (Locale loc : this.defaultLocales) {
keys.add(I18nKey.of(loc, prefixed));
}
}
// defaultLocales x path
for (Locale loc : defaultLocales) {
keys.add(I18nKey.of(loc, path));
}
// defaultLocales x globalPrefixes
for (I18nPath prefix : globalPrefixes) {
for (Locale loc : defaultLocales) {
keys.add(I18nKey.of(loc, prefix.child(path)));
// defaultLocales * (defaultPrefixes + path)
for (I18nPath prefix : this.defaultPrefixes) {
I18nPath prefixed = prefix.child(path);
for (Locale loc : this.defaultLocales) {
keys.add(I18nKey.of(loc, prefixed));
}
}
return keys;
Expand Down
17 changes: 13 additions & 4 deletions src/main/java/com/coditory/quark/i18n/I18nMessagePack.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,8 @@
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

import java.util.Arrays;
import java.util.List;
import java.util.Locale;
import java.util.Map;

Expand Down Expand Up @@ -114,11 +116,18 @@ default I18nMessages localize(@NotNull String locale) {
}

@NotNull
I18nMessagePack prefixQueries(I18nPath prefix);
I18nMessagePack prefixQueries(@NotNull List<I18nPath> prefixes);

@NotNull
default I18nMessagePack prefixQueries(@NotNull String prefix) {
expectNonNull(prefix, "prefix");
return prefixQueries(I18nPath.of(prefix));
default I18nMessagePack prefixQueries(@NotNull I18nPath... prefixes) {
expectNonNull(prefixes, "prefixes");
return prefixQueries(List.of(prefixes));
}

@NotNull
default I18nMessagePack prefixQueries(@NotNull String... prefixes) {
expectNonNull(prefixes, "prefix");
List<I18nPath> paths = Arrays.stream(prefixes).map(I18nPath::of).toList();
return prefixQueries(paths);
}
}
80 changes: 26 additions & 54 deletions src/main/java/com/coditory/quark/i18n/I18nMessagePackBuilder.java
Original file line number Diff line number Diff line change
Expand Up @@ -21,14 +21,14 @@

public final class I18nMessagePackBuilder {
private final AggregatedI18nLoader loader = new AggregatedI18nLoader();
private final List<I18nPath> referenceFallbackPaths = new ArrayList<>();
private final List<I18nPath> messageFallbackPaths = new ArrayList<>();
private final List<I18nArgTransformer<?>> argTransformers = new ArrayList<>();
private I18nMissingMessageHandler missingMessageHandler = I18nMissingMessageHandler.errorThrowingHandler();
private Locale defaultLocale;
private boolean transformJava8TimeTypes = true;
private boolean normalizeWhitespaces = false;
private boolean resolveReferences = true;
private List<I18nPath> referencePrefixes = new ArrayList<>();
private List<I18nPath> queryPrefixes = new ArrayList<>();
private I18nMissingMessagesDetector missingMessagesDetector;

I18nMessagePackBuilder() {
Expand All @@ -38,8 +38,8 @@ public final class I18nMessagePackBuilder {
private I18nMessagePackBuilder copy() {
I18nMessagePackBuilder builder = new I18nMessagePackBuilder();
builder.loader.addLoader(loader.copy());
builder.referenceFallbackPaths.addAll(referenceFallbackPaths);
builder.messageFallbackPaths.addAll(messageFallbackPaths);
builder.referencePrefixes.addAll(referencePrefixes);
builder.queryPrefixes.addAll(queryPrefixes);
builder.argTransformers.addAll(argTransformers);
builder.missingMessageHandler = missingMessageHandler;
builder.defaultLocale = defaultLocale;
Expand Down Expand Up @@ -251,71 +251,43 @@ public I18nMessagePackBuilder setDefaultLocale(@NotNull Locale defaultLocale) {
}

@NotNull
public I18nMessagePackBuilder addReferenceFallbackKeyPrefixes(@NotNull List<String> keyPrefixes) {
expectNonNull(keyPrefixes, "keyPrefixes");
keyPrefixes.forEach(this::addReferenceFallbackKeyPrefix);
public I18nMessagePackBuilder prefixReferenceQueries(@NotNull List<I18nPath> prefixes) {
expectNonNull(prefixes, "prefixes");
this.referencePrefixes = List.copyOf(prefixes);
return this;
}

@NotNull
public I18nMessagePackBuilder addReferenceFallbackKeyPrefixes(@NotNull String... keyPrefixes) {
expectNonNull(keyPrefixes, "keyPrefixes");
Arrays.stream(keyPrefixes).forEach(this::addReferenceFallbackKeyPrefix);
return this;
}

@NotNull
public I18nMessagePackBuilder addReferenceFallbackKeyPrefix(@NotNull String keyPrefix) {
expectNonBlank(keyPrefix, "keyPrefix");
I18nPath i18nPath = I18nPath.of(keyPrefix);
this.referenceFallbackPaths.add(i18nPath);
return this;
}

@NotNull
public I18nMessagePackBuilder addMessageFallbackKeyPrefixes(@NotNull List<String> keyPrefixes) {
expectNonNull(keyPrefixes, "keyPrefixes");
keyPrefixes.forEach(this::addMessageFallbackKeyPrefix);
return this;
}

@NotNull
public I18nMessagePackBuilder addMessageFallbackKeyPrefixes(@NotNull String... keyPrefixes) {
expectNonNull(keyPrefixes, "keyPrefixes");
Arrays.stream(keyPrefixes).forEach(this::addMessageFallbackKeyPrefix);
return this;
public I18nMessagePackBuilder prefixReferenceQueries(@NotNull I18nPath... prefixes) {
expectNonNull(prefixes, "prefixes");
return prefixReferenceQueries(List.of(prefixes));
}

@NotNull
public I18nMessagePackBuilder addMessageFallbackKeyPrefix(@NotNull String keyPrefix) {
expectNonBlank(keyPrefix, "keyPrefix");
I18nPath i18nPath = I18nPath.of(keyPrefix);
this.messageFallbackPaths.add(i18nPath);
return this;
public I18nMessagePackBuilder prefixReferenceQueries(@NotNull String... prefixes) {
expectNonNull(prefixes, "prefixes");
List<I18nPath> mapped = Arrays.stream(prefixes).map(I18nPath::of).toList();
return prefixReferenceQueries(mapped);
}

@NotNull
public I18nMessagePackBuilder addFallbackKeyPrefixes(@NotNull List<String> keyPrefixes) {
expectNonNull(keyPrefixes, "keyPrefixes");
addMessageFallbackKeyPrefixes(keyPrefixes);
addReferenceFallbackKeyPrefixes(keyPrefixes);
public I18nMessagePackBuilder prefixQueries(@NotNull List<I18nPath> prefixes) {
expectNonNull(prefixes, "prefixes");
this.queryPrefixes = List.copyOf(prefixes);
return this;
}

@NotNull
public I18nMessagePackBuilder addFallbackKeyPrefixes(@NotNull String... keyPrefixes) {
expectNonNull(keyPrefixes, "keyPrefixes");
addMessageFallbackKeyPrefixes(keyPrefixes);
addReferenceFallbackKeyPrefixes(keyPrefixes);
return this;
public I18nMessagePackBuilder prefixQueries(@NotNull String... prefixes) {
expectNonNull(prefixes, "prefixes");
List<I18nPath> mapped = Arrays.stream(prefixes).map(I18nPath::of).toList();
return prefixQueries(mapped);
}

@NotNull
public I18nMessagePackBuilder addFallbackKeyPrefix(@NotNull String keyPrefix) {
expectNonBlank(keyPrefix, "keyPrefix");
addMessageFallbackKeyPrefix(keyPrefix);
addReferenceFallbackKeyPrefix(keyPrefix);
return this;
public I18nMessagePackBuilder prefixQueries(@NotNull I18nPath... prefixes) {
expectNonNull(prefixes, "prefixes");
return prefixQueries(List.of(prefixes));
}

@NotNull
Expand Down Expand Up @@ -349,14 +321,14 @@ private I18nMessagePack build(List<I18nMessageBundle> bundles) {
bundles = TemplatesBundlePrefixes.prefix(bundles);
detectMissingMessages(bundles);
LocaleResolver localeResolver = LocaleResolver.of(defaultLocale, bundles);
I18nKeyGenerator messageKeyGenerator = new I18nKeyGenerator(defaultLocale, messageFallbackPaths, localeResolver);
I18nKeyGenerator messageKeyGenerator = new I18nKeyGenerator(defaultLocale, queryPrefixes, localeResolver);
MessageTemplateParser parser = buildMessageTemplateParser(bundles, localeResolver);
Map<I18nKey, MessageTemplate> templates = parser.parseTemplates(bundles);
return new ImmutableI18nMessagePack(templates, parser, missingMessageHandler, messageKeyGenerator);
}

private MessageTemplateParser buildMessageTemplateParser(List<I18nMessageBundle> bundles, LocaleResolver localeResolver) {
I18nKeyGenerator referenceKeyGenerator = new I18nKeyGenerator(defaultLocale, referenceFallbackPaths, localeResolver);
I18nKeyGenerator referenceKeyGenerator = new I18nKeyGenerator(defaultLocale, referencePrefixes, localeResolver);
ReferenceResolver referenceResolver = new ReferenceResolver(bundles, referenceKeyGenerator, resolveReferences);
ArgumentResolver argumentResolver = buildArgumentResolver();
MessageTemplateNormalizer messageTemplateNormalizer = new MessageTemplateNormalizer(normalizeWhitespaces);
Expand Down
2 changes: 1 addition & 1 deletion src/main/java/com/coditory/quark/i18n/I18nMessages.java
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@ public String getMessageOrNull(@NotNull String key) {
}

@NotNull
public I18nMessages prefixQueries(@NotNull String prefix) {
public I18nMessages addMessagePrefix(@NotNull String prefix) {
return messagePack.prefixQueries(prefix).localize(locale);
}

Expand Down
Loading

0 comments on commit 41d9b5f

Please sign in to comment.