Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Qute: Allow to specify default bundle message file without language tag #32667

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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();
}

}