codeconsole opened a new pull request, #16102:
URL: https://github.com/apache/grails-core/pull/16102
Grails registered its own `messageSource` bean
(`PluginAwareResourceBundleMessageSource`), so Spring Boot's
`MessageSourceAutoConfiguration` backed off. Grails then discovered bundles by
scanning `classpath*:*.properties` at runtime — which GraalVM native images
cannot do, and which costs start-up time on the JVM. `spring.messages.*` was
ignored entirely as a result.
Spring Boot now owns the `messageSource` bean outright. Grails supplies only
the one thing Boot cannot know: which base names the application and its
plugins contribute. That is recorded at build time, so nothing scans the
classpath at runtime.
## `spring.messages.*` now works
All of Boot's message-source properties apply, where previously they were
silently ignored:
```yaml
spring:
messages:
encoding: UTF-8
cache-duration: 5s
fallback-to-system-locale: false
use-code-as-default-message: true
```
Grails contributes its own defaults at the lowest precedence —
`fallback-to-system-locale: false`, `spring.messages.encoding` from
`grails.views.gsp.encoding`, and a short `cache-duration` in development so
bundle edits still reload without a restart.
`grails.i18n.cache.seconds` and `grails.i18n.filecache.seconds` are removed
in favour of `spring.messages.cache-duration`.
## Base names are discovered for you
You do not normally set `spring.messages.basename`. The Grails Gradle plugin
records each artifact's bundles into `META-INF/grails/i18n.properties`, and an
`EnvironmentPostProcessor` composes the effective list at boot: your
application's base names first, then each plugin's.
Setting it yourself still works and is how you reach a bundle outside
`grails-app/i18n` — whatever you declare is kept and outranks the discovered
names:
```yaml
spring:
messages:
basename: config/i18n/custom
```
## Plugin bundles must be namespaced
A plugin's base names must be its plugin name, or that name followed by a
hyphen:
```
grails-app/i18n/spring-security-core.properties
grails-app/i18n/spring-security-core_fr.properties
grails-app/i18n/spring-security-core-validation.properties
```
Spring resolves a base name to the first matching resource on the classpath,
so two plugins sharing a base name would shadow one another rather than merge.
The plugin build now rejects a bundle outside the plugin's namespace.
Applications are unaffected — there is only one application, so its base names
cannot collide.
Within this repository that meant renaming three bundles:
`spring-security-oauth2`, `spring-security-ui` (whose dotted
`messages.spring-security-ui` base name is unreachable under `ResourceBundle`,
which maps `.` to `/`), and the `loadfirst` test plugin.
## Ambiguous file names
An application may use any base name, with one reservation: a name ending in
a valid locale identifier is ambiguous, since `api_fr.properties` reads as base
name `api` in French. Declare the base name when the inference is wrong:
```groovy
grails {
i18n {
basenames = ['api', 'api_errors']
}
}
```
## Native image
Because the bundles are known ahead of time, Grails registers the GraalVM
resource hints for them — covering plugin base names and any base name an
application configures itself, neither of which Boot's own registrar handles
(it registers two hardcoded `messages*` patterns and derives nothing from the
configured base names).
## Limitations
- **Adding or removing a base name requires a restart.** Spring Boot reads
the configured base names once, when it builds the message source. Editing
values in an existing bundle, and adding a locale file for a base name that
already exists, both still work without one.
- **A plugin shipping a root `messages.properties` no longer contributes
messages.** It previously acted as a fallback behind the application's bundle;
that behaviour depended on Grails' own merge tier and cannot be reproduced
under first-match-wins resolution.
- **Every bundle needs a locale-independent file.** Boot's
auto-configuration only activates when a configured base name has an unsuffixed
bundle, and it contributes the whole `messageSource` bean — so an application
shipping only `messages_de.properties` would have had no message source at all.
The build now rejects that rather than letting it fail at runtime.
- **Only `grails-app/i18n` is indexed.** Bundles elsewhere need an explicit
`spring.messages.basename`; they are still covered by the native-image hints,
which read the effective property rather than the descriptors.
- **The GraalVM bundle-hint behaviour is not verified by a native test.**
Hints use resource patterns rather than `registerResourceBundle`, following
Spring Boot's own choice for this case; that is a design signal, not something
this PR proves with a native image build.
Inter-plugin precedence follows the reverse of Grails' plugin topological
order, preserving what the previous message source did when two plugins define
the same code. An application's own bundle always overrides every plugin's.
--
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.
To unsubscribe, e-mail: [email protected]
For queries about this service, please contact Infrastructure at:
[email protected]