Skip to content

Commit

Permalink
Merge pull request #32667 from michalvavrik/feature/qute-default-loca…
Browse files Browse the repository at this point in the history
…lized-msg-bundle-file

Qute: Allow to specify default bundle message file without language tag
  • Loading branch information
mkouba authored Apr 17, 2023
2 parents c5be7ba + 506e3e2 commit b687fbb
Show file tree
Hide file tree
Showing 5 changed files with 235 additions and 7 deletions.
3 changes: 3 additions & 0 deletions docs/src/main/asciidoc/qute-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2596,6 +2596,9 @@ public interface GermanAppMessages extends AppMessages {

Message bundle files must be encoded in _UTF-8_.
The file name consists of the relevant bundle name (e.g. `msg`) and underscore followed by a language tag (IETF; e.g. `en-US`).
The language tag may be omitted, in which case the language tag of the default bundle locale is used.
For example, if bundle `msg` has default locale `en`, then `msg.properties` is going to be treated as `msg_en.properties`.
If both `msg.properties` and `msg_en.properties` are detected, an exception is thrown and build fails.
The file format is very simple: each line represents either a key/value pair with the equals sign used as a separator or a comment (line starts with `#`).
Blank lines are ignored.
Keys are _mapped to method names_ from the corresponding message bundle interface.
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -206,13 +206,24 @@ List<MessageBundleBuildItem> processBundles(BeanArchiveIndexBuildItem beanArchiv
for (Path messageFile : messageFiles) {
String fileName = messageFile.getFileName().toString();
if (fileName.startsWith(name)) {
// msg_en.txt -> en
// msg_Views_Index_cs.properties -> cs
// msg_Views_Index_cs-CZ.properties -> cs-CZ
// msg_Views_Index_cs_CZ.properties -> cs_CZ
String locale = fileName.substring(name.length() + 1, fileName.indexOf('.'));
// Support resource bundle naming convention
locale = locale.replace('_', '-');
final String locale;
int postfixIdx = fileName.indexOf('.');
if (postfixIdx == name.length()) {

// msg.txt -> use bundle default locale
locale = defaultLocale;
} else {

locale = fileName
// msg_en.txt -> en
// msg_Views_Index_cs.properties -> cs
// msg_Views_Index_cs-CZ.properties -> cs-CZ
// msg_Views_Index_cs_CZ.properties -> cs_CZ
.substring(name.length() + 1, postfixIdx)
// Support resource bundle naming convention
.replace('_', '-');
}

ClassInfo localizedInterface = localeToInterface.get(locale);
if (defaultLocale.equals(locale) || localizedInterface != null) {
// both file and interface exist for one locale, therefore we need to merge them
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,111 @@
package io.quarkus.qute.deployment.i18n;

import static io.quarkus.qute.i18n.MessageBundle.DEFAULT_NAME;
import static org.junit.jupiter.api.Assertions.assertEquals;

import jakarta.inject.Inject;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.qute.i18n.Localized;
import io.quarkus.qute.i18n.Message;
import io.quarkus.qute.i18n.MessageBundle;
import io.quarkus.test.QuarkusUnitTest;

public class DefaultFileBundleLocaleMergeTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(Messages.class, EnMessages.class, DeMessages.class)
.addAsResource(new StringAsset("hello_world=Hi!"), "messages/msg_en.properties")
.addAsResource(new StringAsset("farewell=Abschied\ngoodbye=Freilos"), "messages/msg_de.properties")
.addAsResource(new StringAsset("goodbye=Mej se!\nfarewell=Sbohem!\nhello_world=Zdravim svete!"),
"messages/msg.properties"))
.overrideConfigKey("quarkus.default-locale", "cs");

@Localized("en")
Messages enMessages;

@Localized("de")
Messages deMessages;

@Inject
Messages csMessages;

/**
* Default message template method is not overridden and message was set with {@link Message#value()}.
*/
@Test
void testDefaultFromAnnotationIsUsedAsFallback() {
assertEquals("Nazdar!", enMessages.hello());
}

/**
* Default message template method is not overridden and message was set in default 'msg.properties' file.
*/
@Test
void testDefaultFromFileIsUsedAsFallback() {
assertEquals("Mej se!", enMessages.goodbye());
}

/**
* Localized message template method is provided without {@link Message#value()}
*/
@Test
void testDefaultIsUsedAsFallback2() {
assertEquals("Greetings!", enMessages.greetings());
}

@Test
void testLocalizedFileIsMerged() {
assertEquals("Freilos", deMessages.goodbye());
}

/**
* Default message set with {@link Message#value()} has priority over message from 'msg.properties'.
*/
@Test
void testDefaultInterfaceHasPriority() {
assertEquals("Ahoj svete!", csMessages.hello_world());
}

@Test
void testBothDefaultAndLocalizedFromFile() {
assertEquals("Abschied", deMessages.farewell());
}

@MessageBundle(DEFAULT_NAME)
public interface Messages {

@Message("Ahoj svete!")
String hello_world();

@Message("Nazdar!")
String hello();

String goodbye();

@Message("Greetings!")
String greetings();

@Message
String farewell();
}

@Localized("en")
public interface EnMessages extends Messages {

@Message
String greetings();

}

@Localized("de")
public interface DeMessages extends Messages {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package io.quarkus.qute.deployment.i18n;

import static io.quarkus.qute.i18n.MessageBundle.DEFAULT_NAME;
import static org.junit.jupiter.api.Assertions.assertEquals;

import jakarta.inject.Inject;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.qute.i18n.Localized;
import io.quarkus.qute.i18n.Message;
import io.quarkus.qute.i18n.MessageBundle;
import io.quarkus.test.QuarkusUnitTest;

public class DefaultFileDefaultBundleNameTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot((jar) -> jar
.addClasses(Messages.class)
.addAsResource(new StringAsset("goodbye=Mej se!\nfarewell=Sbohem!"), "messages/msg.properties"))
.overrideConfigKey("quarkus.default-locale", "cs");

@Inject
Messages csMessages1;

@Localized("cs")
Messages csMessages2;

@Test
void unannotatedMessageMethod() {
assertEquals("Mej se!", csMessages1.goodbye());
}

@Test
void annotatedMessageMethod() {
assertEquals("Sbohem!", csMessages2.farewell());
}

@MessageBundle(DEFAULT_NAME)
public interface Messages {

String goodbye();

@Message
String farewell();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package io.quarkus.qute.deployment.i18n;

import static io.quarkus.qute.i18n.MessageBundle.DEFAULT_NAME;
import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
import static org.junit.jupiter.api.Assertions.fail;

import org.jboss.shrinkwrap.api.asset.StringAsset;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.RegisterExtension;

import io.quarkus.qute.deployment.MessageBundleException;
import io.quarkus.qute.i18n.Message;
import io.quarkus.qute.i18n.MessageBundle;
import io.quarkus.test.QuarkusUnitTest;

public class DefaultFileDuplicateFoundTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root
.addAsResource(new StringAsset("hi=Nazdar!"), "messages/msg.properties")
.addAsResource(new StringAsset("hi=Ahoj!"), "messages/msg_cs.properties"))
.overrideConfigKey("quarkus.default-locale", "cs")
.assertException(t -> {
Throwable e = t;
MessageBundleException mbe = null;
while (e != null) {
if (e instanceof MessageBundleException) {
mbe = (MessageBundleException) e;
break;
}
e = e.getCause();
}
assertNotNull(mbe);
assertTrue(mbe.getMessage().contains("localized file already exists for locale [cs]"), mbe.getMessage());
assertTrue(mbe.getMessage().contains("msg_cs.properties"), mbe.getMessage());
assertTrue(mbe.getMessage().contains("msg.properties"), mbe.getMessage());
});

@Test
public void testValidation() {
fail();
}

@MessageBundle(DEFAULT_NAME)
public interface Messages {
@Message
String hi();
}

}

0 comments on commit b687fbb

Please sign in to comment.