quarkusio/quarkus · error · MessageBundleException

@Localized must be declared on an interface: {localized}

Error message

@Localized must be declared on an interface: {localized}

What it means

@Localized may only be placed on interfaces; it marks a translated message bundle variant. The build-time processor throws when @Localized targets a class, enum, or record, because localization only makes sense as an interface extending a message bundle interface.

Source

Thrown at extensions/qute/deployment/src/main/java/io/quarkus/qute/deployment/MessageBundleProcessor.java:268

                    bundles.add(new MessageBundleBuildItem(name, bundleClass, localeToInterface,
                            localeToFiles, localeToMergeCandidates, defaultLocale));
                } else {
                    throw new MessageBundleException("@MessageBundle must be declared on an interface: " + bundleClass);
                }
            }
        }

        // Detect interfaces annotated with @Localized that don't extend a message bundle interface
        for (AnnotationInstance localizedAnnotation : index.getAnnotations(Names.LOCALIZED)) {
            if (localizedAnnotation.target().kind() == Kind.CLASS) {
                ClassInfo localized = localizedAnnotation.target().asClass();
                if (Modifier.isInterface(localized.flags())) {
                    if (!localizedInterfaces.contains(localized.name())) {
                        throw new MessageBundleException(
                                "A localized message bundle interface must extend a message bundle interface: " + localized);
                    }
                } else {
                    throw new MessageBundleException("@Localized must be declared on an interface: " + localized);
                }
            }
        }

        // Generate implementations
        // name -> impl class
        Map<String, ClassDesc> generatedImplementations = generateImplementations(bundles, generatedClasses, generatedResources,
                generatedServiceProviders, messageTemplateMethods, index);

        // Register synthetic beans
        for (MessageBundleBuildItem bundle : bundles) {
            ClassInfo bundleInterface = bundle.getDefaultBundleInterface();
            beanRegistration.getContext().configure(bundleInterface.name())
                    .addType(bundle.getDefaultBundleInterface().name())
                    // The default message bundle - add both @Default and @Localized
                    .addQualifier(DotNames.DEFAULT).addQualifier().annotation(Names.LOCALIZED)
                    .addValue("value", getDefaultLocale(bundleInterface.declaredAnnotation(Names.BUNDLE), locales)).done()
                    .unremovable()

View on GitHub (pinned to e1c734241f)

Solutions

  1. Move @Localized (and @Locale) onto an interface that extends the message bundle interface.
  2. Remove @Localized from the class; the localized variants are generated for you from bundle interfaces plus .properties files if you prefer file-based translation.
  3. Rebuild after moving the annotation.

Example fix

// before
@Localized
@Locale("de")
public class AppMessagesDe implements AppMessages {...}

// after
@Localized
@Locale("de")
public interface AppMessagesDe extends AppMessages {
    @Override
    String hello(String name);
}
Defensive patterns

Strategy: validation

Validate before calling

Class<?> c = AppMessagesDe.class;
if (c.getAnnotation(Localized.class) != null && !c.isInterface()) {
    throw new IllegalStateException("@Localized must be declared on an interface: " + c);
}

Type guard

static boolean isLocalizedInterface(Class<?> c) {
    return c.isInterface() && c.isAnnotationPresent(Localized.class);
}

Prevention

When it happens

Trigger: Putting @Localized (with @Locale) on a concrete class or abstract class instead of an interface that extends a @MessageBundle interface.

Common situations: Mistakenly annotating the implementation class; IDE quick-fix applying annotations to the wrong type; refactorings that converted an interface to a class while keeping annotations.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/1d785dc6cd4c6d48. Report an issue: GitHub.