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]

Reply via email to