Skip to content

Commit

Permalink
Qute type-safe messages - support resource bundle naming convention
Browse files Browse the repository at this point in the history
- resolves quarkusio#30382
  • Loading branch information
mkouba committed Jan 17, 2023
1 parent 5146f8a commit 6005003
Show file tree
Hide file tree
Showing 5 changed files with 68 additions and 8 deletions.
2 changes: 1 addition & 1 deletion docs/src/main/asciidoc/qute-reference.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -2451,7 +2451,7 @@ public interface GermanAppMessages extends AppMessages {
<2> The value is the localized template.

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 the locate tag (IETF).
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 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 @@ -185,6 +185,8 @@ List<MessageBundleBuildItem> processBundles(BeanArchiveIndexBuildItem beanArchiv
if (fileName.startsWith(name)) {
// msg_en.txt -> en
String locale = fileName.substring(fileName.indexOf('_') + 1, fileName.indexOf('.'));
// Support resource bundle naming convention
locale = locale.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 Expand Up @@ -629,7 +631,7 @@ private Map<String, String> generateImplementations(List<MessageBundleBuildItem>
bundle.getDefaultLocale(), bundleInterface.methods(), null);
MergeClassInfoWrapper bundleInterfaceWrapper = new MergeClassInfoWrapper(bundleInterface, null, null);

String bundleImpl = generateImplementation(null, null, bundleInterfaceWrapper,
String bundleImpl = generateImplementation(bundle, null, null, bundleInterfaceWrapper,
defaultClassOutput, messageTemplateMethods, defaultKeyToMap, null);
generatedTypes.put(bundleInterface.name().toString(), bundleImpl);
for (Entry<String, ClassInfo> entry : bundle.getLocalizedInterfaces().entrySet()) {
Expand All @@ -642,7 +644,7 @@ private Map<String, String> generateImplementations(List<MessageBundleBuildItem>
keyToMap);

generatedTypes.put(entry.getValue().name().toString(),
generateImplementation(bundleInterface, bundleImpl, localizedInterfaceWrapper,
generateImplementation(bundle, bundleInterface, bundleImpl, localizedInterfaceWrapper,
defaultClassOutput, messageTemplateMethods, keyToMap, null));
}

Expand All @@ -663,7 +665,7 @@ public String apply(String className) {
}
}));
generatedTypes.put(localizedFile.toString(),
generateImplementation(bundleInterface, bundleImpl, new SimpleClassInfoWrapper(bundleInterface),
generateImplementation(bundle, bundleInterface, bundleImpl, new SimpleClassInfoWrapper(bundleInterface),
localeAwareGizmoAdaptor, messageTemplateMethods, keyToTemplate, locale));
}
}
Expand All @@ -674,7 +676,7 @@ private Map<String, String> getLocalizedFileKeyToTemplate(MessageBundleBuildItem
ClassInfo bundleInterface, String locale, List<MethodInfo> methods, ClassInfo localizedInterface)
throws IOException {

var localizedFile = bundle.getMergeCandidates().get(locale);
Path localizedFile = bundle.getMergeCandidates().get(locale);
if (localizedFile != null) {
Map<String, String> keyToTemplate = parseKeyToTemplateFromLocalizedFile(bundleInterface, localizedFile);
if (!keyToTemplate.isEmpty()) {
Expand Down Expand Up @@ -763,7 +765,8 @@ private boolean hasMessageBundleMethod(ClassInfo bundleInterface, String name) {
return false;
}

private String generateImplementation(ClassInfo defaultBundleInterface, String defaultBundleImpl,
private String generateImplementation(MessageBundleBuildItem bundle, ClassInfo defaultBundleInterface,
String defaultBundleImpl,
ClassInfoWrapper bundleInterfaceWrapper, ClassOutput classOutput,
BuildProducer<MessageBundleMethodBuildItem> messageTemplateMethods,
Map<String, String> messageTemplates, String locale) {
Expand Down Expand Up @@ -858,7 +861,8 @@ private String generateImplementation(ClassInfo defaultBundleInterface, String d

if (messageTemplate == null) {
throw new MessageBundleException(
String.format("Message template for key [%s] is missing for default locale", key));
String.format("Message template for key [%s] is missing for default locale [%s]", key,
bundle.getDefaultLocale()));
}

String templateId = null;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package io.quarkus.qute.deployment.i18n;

import static org.junit.jupiter.api.Assertions.assertEquals;

import javax.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.Template;
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 LocalizedFileResourceBundleNameTest {

@RegisterExtension
static final QuarkusUnitTest config = new QuarkusUnitTest()
.withApplicationRoot(root -> root.addClass(Messages1.class)
.addAsResource(new StringAsset("{msg:hello}"), "templates/foo.html")
.addAsResource(new StringAsset("hello=Hello!"), "messages/msg_en_US.properties")
.addAsResource(new StringAsset("hello=Ahoj!"), "messages/msg_cs_CZ.properties"))
.overrideConfigKey("quarkus.default-locale", "en-US");

@Inject
Messages1 messages;

@Localized("cs-CZ")
Messages1 csMessages;

@Inject
Template foo;

@Test
public void testLocalizedFile() {
assertEquals("Hello!", messages.hello());
assertEquals("Ahoj!", csMessages.hello());

assertEquals("Hello!", foo.instance().render());
assertEquals("Ahoj!", foo.instance().setAttribute("locale", "cs-CZ").render());
}

@MessageBundle
public interface Messages1 {

@Message
String hello();

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@
public @interface Localized {

/**
* @return the locale tag string (IETF)
* @return the locale language tag string (IETF)
* @see Locale#forLanguageTag(String)
*/
String value();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import java.lang.annotation.Retention;
import java.lang.annotation.Target;
import java.util.Locale;

/**
* Denotes a message bundle interface.
Expand Down Expand Up @@ -62,8 +63,10 @@
String defaultKey() default Message.ELEMENT_NAME;

/**
* The language tag (IETF) of the default locale.
*
* @return the locale for the default message bundle
* @see Locale#forLanguageTag(String)
*/
String locale() default DEFAULT_LOCALE;
}

0 comments on commit 6005003

Please sign in to comment.