From 0b5dfd870bb2b5231d450e59f8798edeba859ff3 Mon Sep 17 00:00:00 2001 From: b84330808 Date: Sun, 25 Oct 2026 16:40:58 +0900 Subject: [PATCH] Keep numberFormat patterns as written Since 4.5.0 the numberFormat helper upper-cased the format argument before using it as a DecimalFormat pattern, so literal text changed case: "#,##0.00 'items'" rendered "3.14 ITEMS". Upper-case only the copy used to look up the integer/percent/currency/default styles, and pass the original string to DecimalFormat as before 4.5.0. The style lookup and the roundingMode lookup now use Locale.ROOT, so "integer" and roundingMode="ceiling" keep working when the default locale is Turkish. Co-Authored-By: Claude Opus 5.5 --- .../handlebars/helper/StringHelpers.java | 7 +++--- .../jknack/handlebars/NumberFormatTest.java | 24 +++++++++++++++++++ 2 files changed, 28 insertions(+), 3 deletions(-) diff --git a/handlebars/src/main/java/com/github/jknack/handlebars/helper/StringHelpers.java b/handlebars/src/main/java/com/github/jknack/handlebars/helper/StringHelpers.java index d194bfd5..e67fb140 100644 --- a/handlebars/src/main/java/com/github/jknack/handlebars/helper/StringHelpers.java +++ b/handlebars/src/main/java/com/github/jknack/handlebars/helper/StringHelpers.java @@ -662,7 +662,8 @@ protected CharSequence safeApply(final Object value, final Options options) { String roundingMode = options.hash("roundingMode"); if (roundingMode != null) { - numberFormat.setRoundingMode(RoundingMode.valueOf(roundingMode.toUpperCase().trim())); + numberFormat.setRoundingMode( + RoundingMode.valueOf(roundingMode.toUpperCase(Locale.ROOT).trim())); } return numberFormat.format(number); @@ -678,7 +679,7 @@ private NumberFormat build(final Options options) { final String format = getFormatArgument(options); final Locale locale = getLocaleArgument(options); try { - NumberStyle style = NumberStyle.valueOf(format); + NumberStyle style = NumberStyle.valueOf(format.toUpperCase(Locale.ROOT).trim()); return style.numberFormat(locale); } catch (IllegalArgumentException ex) { return new DecimalFormat(format, new DecimalFormatSymbols(locale)); @@ -690,7 +691,7 @@ private String getFormatArgument(final Options options) { if (stringifiedFormat == null || stringifiedFormat.isBlank()) { return NumberStyle.DEFAULT.name(); } - return stringifiedFormat.toUpperCase().trim(); + return stringifiedFormat; } private Locale getLocaleArgument(final Options options) { diff --git a/handlebars/src/test/java/com/github/jknack/handlebars/NumberFormatTest.java b/handlebars/src/test/java/com/github/jknack/handlebars/NumberFormatTest.java index ceb56392..959694e2 100644 --- a/handlebars/src/test/java/com/github/jknack/handlebars/NumberFormatTest.java +++ b/handlebars/src/test/java/com/github/jknack/handlebars/NumberFormatTest.java @@ -61,6 +61,30 @@ public void pattern() throws IOException { shouldCompileTo("{{numberFormat this \"" + pattern + "\"}}", number, expected); } + @Test + public void patternWithLiteralText() throws IOException { + Number number = Math.PI; + String pattern = "#,##0.00 'items'"; + String expected = + new DecimalFormat(pattern, new DecimalFormatSymbols(Locale.getDefault())).format(number); + shouldCompileTo("{{numberFormat this \"" + pattern + "\"}}", number, expected); + } + + @Test + public void namesDoNotDependOnDefaultLocale() throws IOException { + Locale defaultLocale = Locale.getDefault(); + Locale.setDefault(Locale.forLanguageTag("tr-TR")); + try { + Number number = Math.PI; + String expected = NumberFormat.getIntegerInstance(Locale.US).format(number); + shouldCompileTo("{{numberFormat this \"integer\" \"en_US\"}}", number, expected); + shouldCompileTo( + "{{numberFormat this \"0.0\" \"en_US\" roundingMode=\"ceiling\"}}", number, "3.2"); + } finally { + Locale.setDefault(defaultLocale); + } + } + @Test public void frLocale() throws IOException { Number number = Math.PI;