Skip to content

Commit b932755

Browse files
committed
Issue #2814 : FORMAT date locale, log effective regional settings
ValueMetaBase copied Locale.getDefault() into every field, which is the interface language once DISPLAY and FORMAT are split. New fields now take the FORMAT category so month names follow regional settings, not the GUI language. hop-gui, hop-run and hop-server log the effective language, FORMAT locale and timezone at start so a machine-local mismatch is visible. Docs: field-level symbols still win; default OS source still differs across machines unless Override is set; Beam/Spark workers do not load hop-config.json.
1 parent d5bb10d commit b932755

4 files changed

Lines changed: 68 additions & 3 deletions

File tree

core/src/main/java/org/apache/hop/core/row/value/ValueMetaBase.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -316,7 +316,10 @@ protected ValueMetaBase(
316316
this.decimalSymbol = "" + Const.getDefaultDecimalSeparator();
317317
this.groupingSymbol = "" + Const.getDefaultGroupingSeparator();
318318
this.currencySymbol = "" + Const.getDefaultCurrencySymbol();
319-
this.dateFormatLocale = Locale.getDefault();
319+
// FORMAT, not Locale.getDefault(): the latter is the interface language once DISPLAY and
320+
// FORMAT are split, and a field with no explicit date locale must follow the regional
321+
// settings rather than the GUI language.
322+
this.dateFormatLocale = Locale.getDefault(Locale.Category.FORMAT);
320323
this.collatorDisabled = true;
321324
this.collatorLocale = Locale.getDefault();
322325
this.collator = Collator.getInstance(this.collatorLocale);

core/src/main/java/org/apache/hop/i18n/RegionalSettings.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,9 @@
1919

2020
import java.util.Arrays;
2121
import java.util.Locale;
22+
import java.util.TimeZone;
2223
import org.apache.hop.core.config.HopConfig;
24+
import org.apache.hop.core.logging.ILogChannel;
2325
import org.apache.hop.core.logging.LogChannel;
2426
import org.apache.hop.core.util.EnvUtil;
2527
import org.apache.hop.core.util.Utils;
@@ -141,6 +143,7 @@ public void applyHeadless() {
141143
return;
142144
}
143145
Locale.setDefault(Locale.Category.FORMAT, formatLocale);
146+
logEffective(LogChannel.GENERAL, "installation:" + source.name());
144147
}
145148

146149
/**
@@ -160,6 +163,31 @@ public void applyGui() {
160163
return;
161164
}
162165
Locale.setDefault(Locale.Category.FORMAT, formatLocale);
166+
logEffective(LogChannel.GENERAL, "installation:" + source.name());
167+
}
168+
169+
/**
170+
* Writes the language, FORMAT locale and default timezone currently in effect. hop-gui, hop-run
171+
* and hop-server all log this so a machine-local mismatch is visible without inspecting
172+
* configuration files.
173+
*
174+
* @param log channel to write to; {@link LogChannel#GENERAL} when none is available yet
175+
* @param sourceDescription where the FORMAT locale came from, for example {@code
176+
* installation:CUSTOM}
177+
*/
178+
public static void logEffective(ILogChannel log, String sourceDescription) {
179+
if (log == null) {
180+
return;
181+
}
182+
log.logBasic(
183+
"Regional settings: language="
184+
+ Locale.getDefault()
185+
+ " format="
186+
+ Locale.getDefault(Locale.Category.FORMAT)
187+
+ " timezone="
188+
+ TimeZone.getDefault().getID()
189+
+ " source="
190+
+ sourceDescription);
163191
}
164192

165193
private static boolean isUsable(Locale locale) {

core/src/test/java/org/apache/hop/core/row/value/ValueMetaDateFormatLocaleTest.java

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,20 @@ void withoutAnExplicitDateLocaleTheRegionalSettingsWin() {
8484
assertEquals("gennaio 2025", valueMeta.getDateFormat().format(DATE));
8585
}
8686

87+
/**
88+
* A newly constructed field must pick FORMAT as its date locale. Using DISPLAY (the interface
89+
* language) here would format month names in English while numbers followed Italian separators.
90+
*/
91+
@Test
92+
void constructorDateLocaleFollowsTheRegionalSettingsNotTheLanguage() {
93+
languageAndRegionalSettings(Locale.US, Locale.ITALY);
94+
95+
ValueMetaDate valueMeta = new ValueMetaDate("d");
96+
valueMeta.setConversionMask(MASK);
97+
98+
assertEquals("gennaio 2025", valueMeta.getDateFormat().format(DATE));
99+
}
100+
87101
@Test
88102
void timestampExplicitDateLocaleIsHonouredEvenWhenItEqualsTheInterfaceLanguage() {
89103
languageAndRegionalSettings(Locale.ITALY, Locale.US);
@@ -105,4 +119,14 @@ void timestampWithoutAnExplicitDateLocaleFollowsTheRegionalSettings() {
105119

106120
assertEquals("gennaio 2025", valueMeta.getDateFormat().format(DATE));
107121
}
122+
123+
@Test
124+
void timestampConstructorDateLocaleFollowsTheRegionalSettingsNotTheLanguage() {
125+
languageAndRegionalSettings(Locale.US, Locale.ITALY);
126+
127+
ValueMetaTimestamp valueMeta = new ValueMetaTimestamp("t");
128+
valueMeta.setConversionMask(MASK);
129+
130+
assertEquals("gennaio 2025", valueMeta.getDateFormat().format(DATE));
131+
}
108132
}

docs/hop-user-manual/modules/ROOT/pages/hop-gui/perspective-configuration.adoc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -141,7 +141,9 @@ a|use an explicitly chosen locale for number, currency and date formats, regardl
141141

142142
Two preview panels below the options show sample dates, times, numbers and currency for the current selection, updated live as options change, before anything is saved.
143143

144-
Whichever source is in effect, operating system or an explicit override, applies both to Hop GUI and to headless runs: `hop-run` and `hop-server` read the regional configuration of the machine they run on and honor it the same way the GUI does.
144+
Whichever source is in effect, operating system or an explicit override, applies both to Hop GUI and to headless runs: `hop-run` and `hop-server` read the same `hop-config.json` and honor it the same way the GUI does.
145+
146+
At start, hop-gui, `hop-run` and `hop-server` log the effective settings, for example `Regional settings: language=en_US format=it_IT timezone=Europe/Rome source=installation:CUSTOM`. Use that line to confirm the process you are looking at is formatting the way you think it is.
145147

146148
NOTE: Because this setting applies to the whole running process, in Hop Web it belongs to the installation rather than to each connected user. This is already true of the preferred language today, so it is not a new limitation introduced by this feature. Saving this tab applies the preferred language to the whole running process as well, so in Hop Web it affects every connected session until the server restarts.
147149

@@ -174,7 +176,15 @@ On an existing installation whose configuration is left untouched, nothing chang
174176

175177
The reason to untick both options is reproducibility against the preferred language: it makes Hop GUI, `hop-run` and `hop-server` agree on that language, so a number converted to a string reads the same in the editor and in production, and any hash computed over such a string stays stable between them.
176178

177-
The setting belongs to the Hop installation, not to a project or a pipeline, and it applies to the whole running process rather than to an individual project or session. In Hop Web that means the whole server, and therefore every connected session, shares one set of regional settings, updated live whenever the configuration tab is saved.
179+
Resolution order, most specific first:
180+
181+
. Field-level decimal, grouping, currency, date locale and date timezone on a transform field. These always win.
182+
. This installation tab (operating system, an explicit override, or the preferred language).
183+
. The JVM / operating system default.
184+
185+
The default source is the operating system. Hop GUI, `hop-run` and `hop-server` then agree *on the same machine*, but a laptop on `nl_BE` and a hop-server on `en_US` still format blank fields differently. *Override regional settings* (and copying that `hop-config.json`) is the way to pin a locale for an installation. Transform field symbols remain the way to handle a file that does not match that locale.
186+
187+
Distributed Beam and Spark workers never load `hop-config.json`. They format with their own JVM locale and timezone unless the pipeline sets field-level symbols.
178188

179189
=== Plugins
180190

0 commit comments

Comments
 (0)