@@ -24,6 +24,10 @@ const themeDarkControl = document.getElementById('themeDark');
2424const themeLightControl = document . getElementById ( 'themeLight' ) ;
2525const editorIDControl = document . getElementById ( 'editorID' ) ;
2626const editorJOSMControl = document . getElementById ( 'editorJOSM' ) ;
27+ const localizationDisabledControl = document . getElementById ( 'localizationDisabled' ) ;
28+ const localizationAutomaticControl = document . getElementById ( 'localizationAutomatic' ) ;
29+ const localizationCustomControl = document . getElementById ( 'localizationCustom' ) ;
30+ const localizationCustomLanguageControl = document . getElementById ( 'localizationCustomLanguage' ) ;
2731const backgroundMapContainer = document . getElementById ( 'background-map' ) ;
2832const legend = document . getElementById ( 'legend' ) ;
2933const legendMapContainer = document . getElementById ( 'legend-map' ) ;
@@ -50,7 +54,16 @@ function getFlagEmoji(countryCode) {
5054 return String . fromCodePoint ( ...codePoints ) ;
5155}
5256
53- const locale = new Intl . Locale ( navigator . language ) ;
57+ let locale = new Intl . Locale ( navigator . language ) ;
58+ window . addEventListener ( 'languagechange' , ( ) => {
59+ locale = new Intl . Locale ( navigator . language ) ;
60+ console . info ( `Browser language changed to ${ locale . language } ` ) ;
61+
62+ const localization = configuration . localization ?? defaultConfiguration . localization ;
63+ if ( localization === 'automatic' ) {
64+ onStyleChange ( ) ;
65+ }
66+ } )
5467
5568const icons = {
5669 railway : {
@@ -92,7 +105,10 @@ function registerLastSearchResults(results) {
92105
93106function facilitySearchUrl ( type , term , language ) {
94107 const url = new URL ( `${ location . origin } /api/facility` )
95- url . searchParams . set ( 'lang' , language )
108+
109+ if ( language ) {
110+ url . searchParams . set ( 'lang' , language )
111+ }
96112
97113 switch ( type ) {
98114 case 'name' :
@@ -286,6 +302,19 @@ function showConfiguration() {
286302 stationLabelNameControl . checked = true
287303 }
288304
305+ const localization = configuration . localization ?? defaultConfiguration . localization ;
306+ if ( localization === 'automatic' ) {
307+ localizationAutomaticControl . checked = true ;
308+ localizationCustomLanguageControl . disabled = true ;
309+ } else if ( localization === 'disabled' ) {
310+ localizationDisabledControl . checked = true ;
311+ localizationCustomLanguageControl . disabled = true ;
312+ } else if ( localization === 'custom' ) {
313+ localizationCustomControl . checked = true ;
314+ localizationCustomLanguageControl . disabled = false ;
315+ }
316+ localizationCustomLanguageControl . value = configuration . localizationCustomLanguage ?? locale . language ;
317+
289318 configurationBackdrop . style . display = 'block' ;
290319}
291320
@@ -361,7 +390,7 @@ searchFacilitiesForm.addEventListener('submit', event => {
361390 event . preventDefault ( ) ;
362391 const formData = new FormData ( event . target ) ;
363392 const data = Object . fromEntries ( formData ) ;
364- searchForFacilities ( data . type , data . term , locale . language )
393+ searchForFacilities ( data . type , data . term , configuredLanguage ( ) )
365394} )
366395searchMilestonesForm . addEventListener ( 'submit' , event => {
367396 event . preventDefault ( ) ;
@@ -648,6 +677,33 @@ function onStationLabelChange(stationlabel) {
648677 }
649678}
650679
680+ function disableLocalization ( ) {
681+ updateConfiguration ( 'localization' , 'disabled' ) ;
682+ onStyleChange ( ) ;
683+ }
684+
685+ function automaticLocalization ( ) {
686+ updateConfiguration ( 'localization' , 'automatic' ) ;
687+ onStyleChange ( ) ;
688+ }
689+
690+ function customLocalization ( language ) {
691+ updateConfiguration ( 'localization' , 'custom' ) ;
692+ updateConfiguration ( 'localizationCustomLanguage' , language ) ;
693+ onStyleChange ( ) ;
694+ }
695+
696+ function configuredLanguage ( ) {
697+ const localization = configuration . localization ?? defaultConfiguration . localization ;
698+ if ( localization === 'automatic' ) {
699+ return locale . language ;
700+ } else if ( localization === 'disabled' ) {
701+ return null ;
702+ } else if ( localization === 'custom' ) {
703+ return configuration . localizationCustomLanguage ;
704+ }
705+ }
706+
651707function resolveTheme ( configuredTheme ) {
652708 return configuredTheme === 'system'
653709 ? ( window . matchMedia ( '(prefers-color-scheme: dark)' ) . matches ? 'dark' : 'light' )
@@ -937,7 +993,8 @@ const defaultConfiguration = {
937993 theme : 'system' ,
938994 editor : 'id' ,
939995 view : { } ,
940- stationLowZoomLabel : 'label'
996+ stationLowZoomLabel : 'label' ,
997+ localization : 'automatic' ,
941998} ;
942999let configuration = readConfiguration ( localStorage ) ;
9431000configuration = migrateConfiguration ( localStorage , configuration ) ;
@@ -1045,13 +1102,17 @@ function rewriteStylePathsToOrigin(style) {
10451102}
10461103
10471104// Rewrite source URLs to append the language query parameter
1048- function addLanguageToSupportedSources ( style ) {
1105+ function addLanguageToSupportedSources ( style , language ) {
10491106 style . sources = Object . fromEntries (
10501107 Object . entries ( style . sources )
10511108 . map ( ( [ key , source ] ) => {
10521109 if ( source && source . url && ( ( source . metadata ?? { } ) . supports ?? [ ] ) . includes ( 'language' ) ) {
10531110 const parsedUrl = new URL ( source . url )
1054- parsedUrl . searchParams . set ( 'lang' , locale . language )
1111+
1112+ if ( language ) {
1113+ parsedUrl . searchParams . set ( 'lang' , language )
1114+ }
1115+
10551116 return [
10561117 key ,
10571118 {
@@ -1086,28 +1147,31 @@ function toggleHillShadeLayer(style) {
10861147}
10871148
10881149let lastSetMapStyle = null ;
1089- const onStyleChange = ( ) => {
1150+ let lastSetMapLanguage = null ;
1151+ function onStyleChange ( ) {
10901152 const supportsDate = knownStyles [ selectedStyle ] . styles . date ;
10911153 const dateActive = supportsDate && dateControl . active ;
10921154 const mapStyle = dateActive
10931155 ? knownStyles [ selectedStyle ] . styles . date
10941156 : knownStyles [ selectedStyle ] . styles . default
1157+ const language = configuredLanguage ( ) ;
10951158
1096- if ( mapStyle !== lastSetMapStyle ) {
1097- lastSetMapStyle = mapStyle ;
1098-
1159+ if ( mapStyle !== lastSetMapStyle || language != lastSetMapLanguage ) {
10991160 // Change styles
11001161 map . setStyle ( mapStyles [ mapStyle ] , {
11011162 validate : false ,
11021163 transformStyle : ( previous , next ) => {
11031164 rewriteStylePathsToOrigin ( next )
1104- addLanguageToSupportedSources ( next )
1165+ addLanguageToSupportedSources ( next , language )
11051166 rewriteGlobalStateDefaults ( next )
11061167 toggleHillShadeLayer ( next )
11071168 return next ;
11081169 } ,
11091170 } ) ;
1171+ }
11101172
1173+ if ( mapStyle !== lastSetMapStyle ) {
1174+ // Change legend styles
11111175 legendMap . setStyle ( legendStyles [ mapStyle ] , {
11121176 validate : false ,
11131177 // Do not calculate a diff because of the large structural layer differences causing a blocking performance hit
@@ -1127,6 +1191,9 @@ const onStyleChange = () => {
11271191 dateControl . hide ( ) ;
11281192 }
11291193
1194+ lastSetMapStyle = mapStyle ;
1195+ lastSetMapLanguage = language ;
1196+
11301197 onPageParametersChange ( ) ;
11311198}
11321199
0 commit comments