@geogirafe/lib-geoportal 1.2.0-dev.2775426316 → 1.2.0-dev.2776642665

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -32,6 +32,7 @@
32
32
  "Basemap visible": "Hintergrundkartenauswahl sichtbar",
33
33
  "Basemaps": "Hintergrundkarten",
34
34
  "Batch creation mode": "Batch-Modus",
35
+ "Browser or application default": "Browser- oder Applikationseinstellung",
35
36
  "Build": "Build",
36
37
  "by selecting a theme": "wählen Sie ein Thema",
37
38
  "Cancel": "Abbrechen",
@@ -32,6 +32,7 @@
32
32
  "Basemap visible": "Basemap selection visible",
33
33
  "Basemaps": "Basemaps",
34
34
  "Batch creation mode": "Batch creation mode",
35
+ "Browser or application default": "Browser or application default",
35
36
  "Build": "Build",
36
37
  "by selecting a theme": "by selecting a theme",
37
38
  "Cancel": "Cancel",
@@ -32,6 +32,7 @@
32
32
  "Basemap visible": "Sélection de la carte de base visible",
33
33
  "Basemaps": "Fonds de carte",
34
34
  "Batch creation mode": "mode batch",
35
+ "Browser or application default": "Paramètres du navigateur ou de l'application",
35
36
  "Build": "Build",
36
37
  "by selecting a theme": "en sélectionnant un thème",
37
38
  "Cancel": "Annuler",
@@ -32,6 +32,7 @@
32
32
  "Basemap visible": "Selezione mappa di base visibile",
33
33
  "Basemaps": "Mappe di base",
34
34
  "Batch creation mode": "Modalità di creazione batch",
35
+ "Browser or application default": "Impostazioni del browser o dell'applicazione",
35
36
  "Build": "Build",
36
37
  "by selecting a theme": "selezionando un tema",
37
38
  "Cancel": "Annulla",
@@ -30,6 +30,7 @@ export default class UserPreferencesComponent extends GirafeHTMLElement implemen
30
30
  For preferences that have options to choose from, readout all possible select options to show in the dropdowns
31
31
  */
32
32
  private initPreferenceOptions;
33
+ private refreshLanguageOptions;
33
34
  /**
34
35
  * Collect all possible options for the default theme preference. This has to be repeatable, since the user
35
36
  * can create new custom themes at any time.
@@ -71,10 +72,6 @@ export default class UserPreferencesComponent extends GirafeHTMLElement implemen
71
72
  Save the user preference as a config override in user data storage
72
73
  */
73
74
  private updatePreferenceInStorage;
74
- /**
75
- Delete user preference in the local browser storage
76
- */
77
- private deletePreferenceInStorage;
78
75
  /**
79
76
  * Reset user preferences back to defaults. This will delete the configOverrides object in storage and set the
80
77
  * current state back to the defaults from config.json
@@ -37,7 +37,15 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
37
37
  initPreferences() {
38
38
  // Define all settings that the user can change
39
39
  this.preferences = {
40
- language: new UserPreference('languages.defaultLanguage', 'language', 'system', 'select'),
40
+ language: new UserPreference('languages.userLanguage', 'language', 'system', 'select', (lang) => {
41
+ if (lang === null) {
42
+ // No user preference, reset language to default
43
+ return this.context.stateManager.decideLanguage();
44
+ }
45
+ else {
46
+ return lang;
47
+ }
48
+ }),
41
49
  logLevel: new UserPreference('general.logLevel', null, 'system', 'select'),
42
50
  theme: new UserPreference('themes.defaultTheme', 'theme', 'map', 'select', (themeName) => {
43
51
  const themeId = Object.keys(this.state.themes._allThemes).find((themeKey) => this.state.themes._allThemes[Number(themeKey)].name === themeName);
@@ -72,8 +80,8 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
72
80
  delete this.preferences[key];
73
81
  }
74
82
  }
75
- this.subscribe('themes.isLoaded', (_, isLoaded) => {
76
- if (isLoaded) {
83
+ this.subscribe('application.isReady', (_, isReady) => {
84
+ if (isReady) {
77
85
  this.initPreferenceOptions();
78
86
  this.initCurrentPreferenceValues();
79
87
  this.initDarkModeDefaults();
@@ -127,11 +135,7 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
127
135
  For preferences that have options to choose from, readout all possible select options to show in the dropdowns
128
136
  */
129
137
  initPreferenceOptions() {
130
- if (this.preferences.language) {
131
- this.preferences.language.options = Object.keys(this.context.configManager.Config.languages.translations).map((key) => {
132
- return { label: key, value: key };
133
- });
134
- }
138
+ this.refreshLanguageOptions();
135
139
  if (this.preferences.logLevel) {
136
140
  this.preferences.logLevel.options = [
137
141
  { label: 'debug', value: 'debug' },
@@ -172,6 +176,19 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
172
176
  ];
173
177
  }
174
178
  }
179
+ refreshLanguageOptions() {
180
+ if (this.preferences.language) {
181
+ this.preferences.language.options = Object.keys(this.context.configManager.Config.languages.translations).map((key) => {
182
+ return { label: key, value: key };
183
+ });
184
+ const defaultOptionLabel = this.context.i18nManager.getTranslation('Browser or application default');
185
+ const fallbackLanguage = this.context.i18nManager.getTranslation(this.context.stateManager.fallbackLanguage() ?? '?');
186
+ this.preferences.language.options = [
187
+ { label: `${defaultOptionLabel} (${fallbackLanguage})`, value: null },
188
+ ...this.preferences.language.options
189
+ ];
190
+ }
191
+ }
175
192
  /**
176
193
  * Collect all possible options for the default theme preference. This has to be repeatable, since the user
177
194
  * can create new custom themes at any time.
@@ -238,6 +255,10 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
238
255
  this.refreshThemeOptions();
239
256
  this.refreshRender();
240
257
  }
258
+ else if (preferenceKey === 'language') {
259
+ this.refreshLanguageOptions();
260
+ this.refreshRender();
261
+ }
241
262
  }
242
263
  /**
243
264
  Called when user changes a preference in the panel
@@ -285,12 +306,6 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
285
306
  // Values are saved under 'configOverrides' in the same structure as in the config
286
307
  this.context.userDataManager.saveUserData(`${this.storagePath}.${preference.configPath}`, preference.currentValue);
287
308
  }
288
- /**
289
- Delete user preference in the local browser storage
290
- */
291
- deletePreferenceInStorage(preferenceKey) {
292
- this.context.userDataManager.deleteUserData(`${this.storagePath}.${this.preferences[preferenceKey].configPath}`);
293
- }
294
309
  /**
295
310
  * Reset user preferences back to defaults. This will delete the configOverrides object in storage and set the
296
311
  * current state back to the defaults from config.json
@@ -302,8 +317,8 @@ ${this.htmlUnsafe(this.feedbackTemplateHtml ?? '')}`;
302
317
  }
303
318
  }
304
319
  resetAll() {
320
+ this.context.userDataManager.deleteUserData(this.storagePath);
305
321
  for (const preferenceKey in this.preferences) {
306
- this.deletePreferenceInStorage(preferenceKey);
307
322
  this.preferences[preferenceKey].currentValue = this.context.configManager.getDefaultConfigValue(this.preferences[preferenceKey].configPath);
308
323
  this.updatePreferenceInState(preferenceKey);
309
324
  this.updatePreferenceInConfig(preferenceKey);
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  "name": "GeoGirafe PSC",
6
6
  "url": "https://doc.geogirafe.org"
7
7
  },
8
- "version": "1.2.0-dev.2775426316",
8
+ "version": "1.2.0-dev.2776642665",
9
9
  "type": "module",
10
10
  "engines": {
11
11
  "node": ">=20.19.0"
@@ -1 +1 @@
1
- {"version":"1.2.0-dev.2775426316", "build":"2775426316", "date":"20/08/2026"}
1
+ {"version":"1.2.0-dev.2776642665", "build":"2776642665", "date":"20/08/2026"}
@@ -14,6 +14,7 @@ declare class GirafeConfig {
14
14
  [key: string]: string[];
15
15
  };
16
16
  defaultLanguage: string;
17
+ userLanguage: string | null;
17
18
  };
18
19
  interface: {
19
20
  defaultSelectionComponent: string;
@@ -339,6 +339,7 @@ class GirafeConfig {
339
339
  if (!config.languages) {
340
340
  throw new Error(`Configuration for languages is required. See https://doc.geogirafe.org/docs/configuration.`);
341
341
  }
342
+ config.languages.userLanguage = config.languages.userLanguage ?? null;
342
343
  return config.languages;
343
344
  }
344
345
  initConfigInterface(config) {
@@ -10,6 +10,15 @@ declare class StateManager extends GirafeSingleton {
10
10
  constructor(context: IGirafeContext);
11
11
  initializeSingleton(): void;
12
12
  private setDefaultValues;
13
+ /**
14
+ * Get the language based on the following priority:
15
+ * 1. user preferences, 2. browser language, 3. app default language
16
+ */
17
+ decideLanguage(): string | null;
18
+ /**
19
+ * The fallback language is based on the browser language or app default language
20
+ */
21
+ fallbackLanguage(): string | null;
13
22
  private onChange;
14
23
  subscribe(path: string, callback: Callback): Callback;
15
24
  subscribe(path: RegExp, callback: Callback): Callback;
@@ -26,10 +26,32 @@ class StateManager extends GirafeSingleton {
26
26
  if (this.state && config) {
27
27
  this.state.application.isConfigurationLoaded = true;
28
28
  this.state.projection = config.map.srid;
29
- this.state.language = config.languages.defaultLanguage;
29
+ this.state.language = this.decideLanguage();
30
30
  this.state.interface.selectionComponent = config.interface.defaultSelectionComponent;
31
31
  }
32
32
  }
33
+ /**
34
+ * Get the language based on the following priority:
35
+ * 1. user preferences, 2. browser language, 3. app default language
36
+ */
37
+ decideLanguage() {
38
+ return this.context.configManager.Config?.languages.userLanguage ?? this.fallbackLanguage();
39
+ }
40
+ /**
41
+ * The fallback language is based on the browser language or app default language
42
+ */
43
+ fallbackLanguage() {
44
+ const languageConfig = this.context.configManager.Config?.languages;
45
+ if (!languageConfig) {
46
+ return null;
47
+ }
48
+ const browserLanguage = navigator.language.toLowerCase();
49
+ const languageCode = browserLanguage.substring(0, 2);
50
+ if (Object.keys(languageConfig.translations).includes(languageCode)) {
51
+ return languageCode;
52
+ }
53
+ return languageConfig.defaultLanguage;
54
+ }
33
55
  onChange(property, oldValue, value, parents) {
34
56
  const path = property.trim();
35
57
  for (const key in this.callbacks) {