Skip to content

Issue #2814 : pin FORMAT locale and timezone on project environments - #8172

Draft
mattcasters wants to merge 5 commits into
apache:mainfrom
mattcasters:issue-2814-environment-regional
Draft

Issue #2814 : pin FORMAT locale and timezone on project environments#8172
mattcasters wants to merge 5 commits into
apache:mainfrom
mattcasters:issue-2814-environment-regional

Conversation

@mattcasters

Copy link
Copy Markdown
Contributor

Stacked on #8159. Please merge that PR first; GitHub will also list those commits here until it lands.

Incremental diff (this PR only): sramazzina:hop:bugfix/2814-regional-settings...mattcasters:hop:issue-2814-environment-regional

Addresses #2814 (environment-level FORMAT locale and timezone, on top of the DISPLAY/FORMAT split in #8159).

#8159 makes the GUI language independent of decimal/grouping/date formats and applies installation regional settings from hop-config.json in hop-gui, hop-run and hop-server. That still leaves a machine-local default: a laptop on nl_BE and a hop-server on en_US format blank fields differently. This PR pins FORMAT locale and timezone on the project lifecycle environment so the same environment produces the same conversion defaults everywhere it is enabled.

What this adds

  • LifecycleEnvironment stores optional formatLocale and timeZone. Empty means inherit the installation regional settings from the Configuration perspective (or the JVM timezone). Invalid locale or IANA ids are logged and skipped so a bad environment cannot abort enablement.
  • ProjectsUtil.enableProject applies those values after project variables are set: Locale.setDefault(FORMAT, …) and TimeZone.setDefault(…). The GUI language (DISPLAY) is untouched.
  • The Environment Properties dialog has a Regional tab: type-to-filter locale and timezone combos, Inherit as the first item, and a live preview of a sample number, long date and timezone.
  • Effective values are published as HOP_FORMAT_LOCALE and HOP_TIMEZONE so pipelines can see what they ran under. Start logging uses source=environment:name when the environment overrode the installation.
  • Field-level decimal, grouping, date locale and date timezone still win. Decimal and grouping symbols are not mandatory.
  • Resolution order is now: field → environment → installation (hop-config.json) → OS/JVM.
  • A hop-server JVM still runs one default environment for these conversion defaults (Locale/TimeZone defaults are process-wide). Beam/Spark workers never load hop-config.json or the environment; they need field-level symbols.

Tests

  • Unit tests for inherit, Italian vs US switch, invalid ids, timezone, and the published variables (EnvironmentRegionalSettingsTest).
  • Integration tests locale-us (en_US / America/New_York) and locale-eu (nl_BE / Europe/Brussels). Both projects pin CUSTOM en_US in their hop-config.json so the environment, not the installation, is what differs. Coverage: default number format/parse, explicit field-level symbols, month names, timestamp offsets, and Get Variable for the published names.
  • EU golden CSVs quote "1234,56" so the dataset reader does not split on the comma. Data Grid Number fields leave decimal/grouping blank so FORMAT (not cloned US symbols) does the conversion.

Run with scripts/run-tests-docker.sh PROJECT_NAME=locale-us and PROJECT_NAME=locale-eu.

Docs

  • Configuration perspective: environment sits between field-level and installation in the resolution list.
  • Projects and Environments: Regional table, screenshot of the dialog tab, and the “works on my machine” rationale.
  • Variables: HOP_FORMAT_LOCALE and HOP_TIMEZONE.

Thank you for your contribution! Follow this checklist to help us incorporate your contribution quickly and easily:

  • Run mvn clean install apache-rat:check to make sure basic checks pass. A more thorough check will be performed on your pull request automatically.
  • If you have a group of commits related to the same change, please squash your commits into one and force push your branch using git rebase -i.
  • Mention the appropriate issue in your description (for example: addresses #123), if applicable.

To make clear that you license your contribution under the Apache License Version 2.0, January 2004
you have to acknowledge this by using the following check-box.

sramazzina and others added 5 commits August 29, 2026 11:46
…eparator

* The interface language was installed as the JVM-wide default locale, so it
  also decided decimal separator, grouping separator, currency and date
  formats. The language now writes only Locale.Category.DISPLAY and the
  regional settings write Locale.Category.FORMAT.
* Regional settings had no representation of their own. Added RegionalSettings
  in core, resolving a single source - OPERATING_SYSTEM (the default), CUSTOM
  or LANGUAGE - from RegionalSettingsSource and RegionalSettingsLocale in
  hop-config.json; an unreadable or unknown configuration degrades to
  OPERATING_SYSTEM.
* Locale.setDefault lived in HopGui.main, so hop-run and hop-server never
  applied it and the same pipeline formatted differently in the editor and in
  production. The regional settings are now applied from HopEnvironment.init(),
  the bootstrap common to every entry point.
* Const.DEFAULT_DECIMAL_SEPARATOR and its siblings are static final, computed
  at class load before any locale was installed, and ValueMetaBase copied them
  into every value, overwriting correctly localised symbols. Added
  getDefaultDecimalSeparator(), getDefaultGroupingSeparator(),
  getDefaultCurrencySymbol() and getDefaultNumberFormat(), which read the live
  FORMAT category and cache the symbols against the locale they were built
  from; the constants are kept and deprecated because they are public API.
* The setting was not reachable from the GUI. Added a Regional settings tab
  carrying the interface language combo moved out of GUI options, two mutually
  exclusive checkboxes, a type-to-filter combo over the available locales and a
  live preview of the resulting formats.
* ValueMetaBase and ValueMetaTimestamp compared an explicitly chosen date
  format locale against Locale.getDefault(), so a field locale that happened to
  match the interface language was discarded and the date rendered with the
  regional locale instead. The comparison now reads the FORMAT category.

Fixes apache#2814
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.
…onments

A project lifecycle environment can set formatLocale and timeZone so
hop-gui, hop-run and hop-server format blank fields the same way when
they run that environment. Empty values inherit the installation
regional settings. Invalid ids are logged and skipped.

The environment dialog has a Regional tab with type-to-filter combos
and a live preview. Effective values are published as HOP_FORMAT_LOCALE
and HOP_TIMEZONE.

Integration tests locale-us (en_US / America/New_York) and locale-eu
(nl_BE / Europe/Brussels) cover default number format/parse, field-level
overrides, month names, timezone offsets, and the published variables.
The locale-eu golden CSVs used 1234,56 unquoted, so the dataset reader
took only 1234. Test 1 also kept a US decimal on the Data Grid Number
field, which Select Values cloned onto the String output.

Quote the golden values and let FORMAT parse/format the Data Grid
number when decimal/grouping are left blank.
…enshot

Add the Environment Properties Regional tab screenshot next to the
Regional table, matching the other dialog tabs.
@sramazzina

Copy link
Copy Markdown
Contributor

This looks great, and the locale-us / locale-eu integration tests are a really nice touch — that is the kind of coverage we never managed to add on our side.

One question out of curiosity: why did formatLocale and timeZone end up on the lifecycle environment rather than on the project? Just trying to understand the reasoning behind the choice.

@mattcasters

Copy link
Copy Markdown
Contributor Author

We can be honest and guess the most it will be used is to document/force the environment onto the settings used by the developer to make sure no suprises are faced when you put your project into production.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants