codeconsole commented on code in PR #15987:
URL: https://github.com/apache/grails-core/pull/15987#discussion_r3608975821
##########
grails-gsp/plugin/src/main/groovy/org/grails/plugins/web/taglib/FormTagLib.groovy:
##########
@@ -963,39 +965,145 @@ class FormTagLib implements ApplicationContextAware,
InitializingBean, TagLibrar
}
/**
- * A helper tag for creating locale selects.<br/>
+ * A helper tag for locale selection.<br/>
*
- * eg. <g:localeSelect name="myLocale" value="${locale}" />
+ * <p>With no body it renders a control: a native {@code <select>} by
default, or a plain list of
+ * {@code <a>} links with {@code type="links"}. With a body it becomes an
iterating tag — it
+ * resolves the locales once and renders the body for each, exposing a
per-locale model under the
+ * {@code var} attribute so the caller supplies its own markup (a
Bootstrap dropdown, a footer
+ * list, etc.). The model exposes: {@code locale}, {@code tag}
(BCP‑47), {@code code}
+ * ({@code language_COUNTRY}), {@code autonym} (the name in its own
language), {@code name} (the
+ * name in the display locale), {@code label}, {@code active} (matches the
current locale),
+ * {@code default} (matches the configured default) and {@code index}.
*
- * @emptyTag
+ * eg. <g:localeSelect name="myLocale" value="${locale}"
labelType="autonym" />
*
- * @attr name REQUIRED The name of the select
- * @attr value The set locale, defaults to the current request locale if
not specified
- * @attr locale The locale to use for formatting the locale names.
Defaults to the current request locale and then the system default locale if
not specified
+ * @attr name The name of the select (select mode)
+ * @attr value The selected locale, defaults to the current request locale
if not specified
* @attr available If <code>true</code>, list only the locales the
application is translated into
* (those with a <code>messages_*.properties</code> bundle, as published
to the servlet context by
* the i18n plugin) instead of every locale the JVM knows about. Defaults
to <code>false</code>.
+ * @attr type <code>select</code> (default) or <code>links</code>. Ignored
when a body is supplied.
+ * @attr labelType The option/link label: <code>autonym</code> (each
locale in its own language),
+ * <code>name</code> (in the display locale), <code>both</code>, or
omitted for the legacy
+ * <code>"language, [COUNTRY,] name"</code> label.
+ * @attr sort If <code>true</code>, order the locales by their label using
a locale-independent collator.
+ * @attr tags If <code>true</code>, option/link keys are BCP‑47
language tags (<code>en-US</code>)
+ * rather than the legacy <code>en_US</code> form.
+ * @attr pinDefault If <code>true</code> (body mode), the configured
default locale is emitted first.
+ * @attr param The request parameter name used for <code>links</code>-mode
hrefs. Defaults to <code>lang</code>.
+ * @attr var Enables body mode: the name of the per-locale model variable
exposed to the body.
*/
- def localeSelect(Map attrs) {
- def availableAttr = attrs.remove('available')
- boolean availableOnly = availableAttr != null &&
Boolean.valueOf(availableAttr.toString())
+ def localeSelect(Map attrs, Closure body) {
+ boolean availableOnly =
Boolean.valueOf(attrs.remove('available')?.toString())
+ List locales
if (availableOnly) {
- def availableLocales =
request.servletContext?.getAttribute('availableLocales')
- attrs.from = availableLocales ?: [RCU.getLocale(request)]
+ def published =
request.servletContext?.getAttribute('availableLocales')
+ locales = published ? new ArrayList(published) :
[RCU.getLocale(request)]
}
else {
- attrs.from = Locale.getAvailableLocales()
+ locales = Locale.getAvailableLocales() as List
}
- attrs.value = (attrs.value ?: RCU.getLocale(request))?.toString()
- // set the key as a closure that formats the locale
- attrs.optionKey = { it.country ? "${it.language}_${it.country}" :
it.language }
- // set the option value as a closure that formats the locale for
display
- attrs.optionValue = { it.country ? "${it.language}, ${it.country},
${it.displayName}" : "${it.language}, ${it.displayName}" }
- // use generic select
+ Locale current = RCU.getLocale(request)
+ def valueAttr = attrs.value
+ if (valueAttr instanceof Locale) {
+ current = valueAttr
+ }
+ else if (valueAttr) {
+ current = StringUtils.parseLocale(valueAttr.toString()) ?: current
+ }
+
+ boolean useTags = Boolean.valueOf(attrs.remove('tags')?.toString())
+ Closure label = localeLabel(attrs.remove('labelType'), current)
+
+ if (Boolean.valueOf(attrs.remove('sort')?.toString())) {
+ Collator collator = Collator.getInstance(Locale.ROOT)
+ locales = locales.sort(false) { a, b ->
collator.compare(label(a).toString(), label(b).toString()) }
+ }
+
+ String varName = attrs.remove('var')
+ if (varName) {
+ boolean pinDefault =
Boolean.valueOf(attrs.remove('pinDefault')?.toString())
+ Locale defaultLocale = configuredDefaultLocale()
+ if (pinDefault) {
+ Locale pinned = locales.find { it.language ==
defaultLocale.language }
+ if (pinned) {
+ locales = [pinned] + locales.findAll { it.language !=
defaultLocale.language }
+ }
+ }
+ // A country-qualified request locale (e.g. Accept-Language:
en-US) must still mark the
+ // language-only entry (en) active when the list offers no exact
country match.
+ boolean exactActiveMatch = locales.any { it.language ==
current.language && it.country == current.country }
+ locales.eachWithIndex { locale, i ->
+ out << body([(varName): [
+ locale: locale,
+ tag: locale.toLanguageTag(),
+ code: localeKey(locale),
+ autonym: locale.getDisplayName(locale),
+ name: locale.getDisplayName(current),
+ label: label(locale),
+ active: exactActiveMatch ?
Review Comment:
Good catch — fixed in 47e8ebe933. `active` (and `default`) now elect a
single winner per language via the exact match → bare-language entry → first
language variant chain you suggested, so a bare `pt` or unlisted `zh-HK`
request highlights exactly one row. Added a regression test using the
shipped-bundle shape (`pt_BR`/`pt_PT` with no bare `pt`).
--
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]