@@ -243,6 +243,7 @@ public function onAfterInitialise(AfterInitialiseEvent $event): void
243243
244244 // Attach parse rule.
245245 $ router ->attachParseRule ([$ this , 'parseRule ' ], Router::PROCESS_BEFORE );
246+ $ router ->attachParseRule ([$ this , 'setLanguageApplicationState ' ], Router::PROCESS_BEFORE );
246247 }
247248
248249 /**
@@ -304,7 +305,6 @@ public function buildRule(&$router, &$uri)
304305 if (
305306 !$ this ->params ->get ('remove_default_prefix ' , 0 )
306307 || $ lang !== $ this ->default_lang
307- || $ lang !== $ this ->current_lang
308308 ) {
309309 $ uri ->setPath ($ uri ->getPath () . '/ ' . $ sef . '/ ' );
310310 }
@@ -356,179 +356,135 @@ public function postprocessNonSEFBuildRule(&$router, &$uri)
356356 */
357357 public function parseRule (&$ router , &$ uri )
358358 {
359- $ app = $ this ->getApplication ();
360-
361- // Did we find the current and existing language yet?
362- $ found = false ;
363-
364359 // Are we in SEF mode or not?
365360 if ($ this ->mode_sef ) {
366361 $ path = $ uri ->getPath ();
367362 $ parts = explode ('/ ' , $ path );
368-
369- $ sef = StringHelper::strtolower ($ parts [0 ]);
370-
371- // Do we have a URL Language Code ?
372- if (!isset ($ this ->sefs [$ sef ])) {
373- // Check if remove default URL language code is set
374- if ($ this ->params ->get ('remove_default_prefix ' , 0 )) {
375- if ($ parts [0 ]) {
376- // We load a default site language page
377- $ lang_code = $ this ->default_lang ;
378- } else {
379- // We check for an existing language cookie
380- $ lang_code = $ this ->getLanguageCookie ();
381- }
382- } else {
383- $ lang_code = $ this ->getLanguageCookie ();
384- }
385-
386- // No language code. Try using browser settings or default site language
387- if (!$ lang_code && $ this ->params ->get ('detect_browser ' , 0 ) == 1 ) {
388- $ lang_code = LanguageHelper::detectLanguage ();
389- }
390-
391- if (!$ lang_code ) {
392- $ lang_code = $ this ->default_lang ;
393- }
394-
395- if ($ lang_code === $ this ->default_lang && $ this ->params ->get ('remove_default_prefix ' , 0 )) {
396- $ found = true ;
363+ $ sef = StringHelper::strtolower ($ parts [0 ]);
364+ $ lang = $ uri ->getVar ('lang ' );
365+
366+ if (isset ($ this ->sefs [$ sef ])) {
367+ // We found a matching language to the lang code
368+ $ uri ->setVar ('lang ' , $ this ->sefs [$ sef ]->lang_code );
369+ array_shift ($ parts );
370+ $ uri ->setPath (implode ('/ ' , $ parts ));
371+
372+ // We were called with the default language code and want to redirect
373+ if ($ this ->params ->get ('remove_default_prefix ' , 0 ) && $ uri ->getVar ('lang ' ) == $ this ->default_lang ) {
374+ $ router ->setTainted ();
397375 }
376+ } elseif ($ this ->params ->get ('remove_default_prefix ' , 0 )) {
377+ // We don't have a prefix for the default language
378+ $ uri ->setVar ('lang ' , $ this ->default_lang );
398379 } else {
399- // We found our language
400- $ found = true ;
401- $ lang_code = $ this ->sefs [$ sef ]->lang_code ;
402-
403- // If we found our language, but it's the default language and we don't want a prefix for that, we are on a wrong URL.
404- // Or we try to change the language back to the default language. We need a redirect to the proper URL for the default language.
405- if ($ lang_code === $ this ->default_lang && $ this ->params ->get ('remove_default_prefix ' , 0 )) {
406- // Create a cookie.
407- $ this ->setLanguageCookie ($ lang_code );
408-
409- $ found = false ;
410- array_shift ($ parts );
411- $ path = implode ('/ ' , $ parts );
412- }
413-
414- // We have found our language and the first part of our URL is the language prefix
415- if ($ found ) {
416- array_shift ($ parts );
417-
418- // Empty parts array when "index.php" is the only part left.
419- if (\count ($ parts ) === 1 && $ parts [0 ] === 'index.php ' ) {
420- $ parts = [];
421- }
422-
423- $ uri ->setPath (implode ('/ ' , $ parts ));
424- }
380+ // No language is set, so we want to redirect to the right language
381+ $ router ->setTainted ();
425382 }
426- } else {
427- // We are not in SEF mode
428- $ lang_code = $ this ->getLanguageCookie ();
429383
430- if (!$ lang_code && $ this ->params ->get ('detect_browser ' , 1 )) {
431- $ lang_code = LanguageHelper::detectLanguage ();
384+ // The language was set both per SEF path and per query parameter. Query parameter takes precedence
385+ if ($ lang ) {
386+ $ uri ->setVar ('lang ' , $ lang );
387+ $ router ->setTainted ();
432388 }
389+ } elseif ($ uri ->hasVar ('lang ' )) {
390+ // We are not in SEF mode. Do we have a language set?
391+ $ lang_code = $ uri ->getVar ('lang ' );
433392
434- if (!isset ($ this ->lang_codes [$ lang_code ])) {
435- $ lang_code = $ this ->default_lang ;
393+ if (isset ($ this ->sefs [$ lang_code ])) {
394+ // We found a matching language to the lang code
395+ $ uri ->setVar ('lang ' , $ this ->sefs [$ lang_code ]->lang_code );
396+ } else {
397+ // The language is not installed on our site
398+ $ uri ->delVar ('lang ' );
436399 }
437400 }
401+ }
438402
439- $ lang = $ uri ->getVar ('lang ' , $ lang_code );
403+ /**
404+ * Parse rule to set the applications language state.
405+ * This rule is removed after being executed the first time, since
406+ * it does redirects and thus disallows parsing more than one URL per page call
407+ *
408+ * @param Router &$router Router object.
409+ * @param Uri &$uri Uri object.
410+ *
411+ * @return void
412+ *
413+ * @since __DEPLOY_VERSION__
414+ */
415+ public function setLanguageApplicationState (&$ router , &$ uri )
416+ {
417+ // We check if the parseRule is still attached to keep this b/c
418+ if (!\in_array ([$ this , 'parseRule ' ], $ router ->getRules ()['parsepreprocess ' ])) {
419+ $ router ->detachRule ('parse ' , [$ this , 'setLanguageApplicationState ' ], $ router ::PROCESS_BEFORE );
440420
441- if (isset ($ this ->sefs [$ lang ])) {
442- // We found our language
443- $ found = true ;
444- $ lang_code = $ this ->sefs [$ lang ]->lang_code ;
421+ return ;
445422 }
446423
447- // We are called via POST or the nolangfilter url parameter was set. We don't care about the language
448- // and simply set the default language as our current language.
449- if (
450- $ app ->getInput ()->getMethod () === 'POST '
451- || $ app ->getInput ()->get ('nolangfilter ' , 0 ) == 1
452- || \count ($ app ->getInput ()->post ) > 0
453- || \count ($ app ->getInput ()->files ) > 0
454- ) {
455- $ found = true ;
456-
457- if (!isset ($ lang_code )) {
458- $ lang_code = $ this ->getLanguageCookie ();
459- }
460-
461- if (!$ lang_code && $ this ->params ->get ('detect_browser ' , 1 )) {
462- $ lang_code = LanguageHelper::detectLanguage ();
463- }
424+ $ lang_code = false ;
464425
465- if (!isset ($ this ->lang_codes [$ lang_code ])) {
426+ // Our parse rule discovered a language
427+ if ($ uri ->hasVar ('lang ' )) {
428+ $ lang_code = $ uri ->getVar ('lang ' );
429+ } else {
430+ /**
431+ * We don't know the language yet and want to discover it.
432+ * If we remove the default prefix, call by POST or have nolangfilter set,
433+ * we simply take the default language.
434+ */
435+ if (
436+ $ this ->params ->get ('remove_default_prefix ' , 0 )
437+ || $ this ->getApplication ()->getInput ()->getMethod () === 'POST '
438+ || $ this ->getApplication ()->getInput ()->get ('nolangfilter ' , 0 ) == 1
439+ || \count ($ this ->getApplication ()->getInput ()->post ) > 0
440+ || \count ($ this ->getApplication ()->getInput ()->files ) > 0
441+ ) {
466442 $ lang_code = $ this ->default_lang ;
467- }
468- }
469-
470- // We have not found the language and thus need to redirect
471- if (!$ found ) {
472- // Lets find the default language for this user
473- if (!isset ($ lang_code ) || !isset ($ this ->lang_codes [$ lang_code ])) {
474- $ lang_code = false ;
443+ } else {
444+ $ lang_code = $ this ->getLanguageCookie ();
475445
476- if ($ this ->params ->get ('detect_browser ' , 1 )) {
446+ // No language code. Try using browser settings or default site language
447+ if (!$ lang_code && $ this ->params ->get ('detect_browser ' , 0 ) == 1 ) {
477448 $ lang_code = LanguageHelper::detectLanguage ();
478-
479- if (!isset ($ this ->lang_codes [$ lang_code ])) {
480- $ lang_code = false ;
481- }
482449 }
483450
484451 if (!$ lang_code ) {
485452 $ lang_code = $ this ->default_lang ;
486453 }
487- }
488-
489- if ($ this ->mode_sef ) {
490- // Use the current language sef or the default one.
491- if (
492- $ lang_code !== $ this ->default_lang
493- || !$ this ->params ->get ('remove_default_prefix ' , 0 )
494- ) {
495- $ path = $ this ->lang_codes [$ lang_code ]->sef . '/ ' . $ path ;
496- }
497454
498- $ uri ->setPath ($ path );
455+ if (!$ this ->params ->get ('remove_default_prefix ' , 0 ) && $ uri ->getPath () == '' ) {
456+ if ($ this ->mode_sef ) {
457+ $ path = $ this ->lang_codes [$ lang_code ]->sef . '/ ' . $ uri ->getPath ();
499458
500- if (!$ app ->get ('sef_rewrite ' )) {
501- $ uri ->setPath ('index.php/ ' . $ uri ->getPath ());
502- }
503-
504- $ redirectUri = $ uri ->base () . $ uri ->toString (['path ' , 'query ' , 'fragment ' ]);
505- } else {
506- $ uri ->setVar ('lang ' , $ this ->lang_codes [$ lang_code ]->sef );
507- $ redirectUri = $ uri ->base () . 'index.php? ' . $ uri ->getQuery ();
508- }
459+ if (!$ this ->getApplication ()->get ('sef_rewrite ' )) {
460+ $ path = 'index.php/ ' . $ path ;
461+ }
509462
510- // Set redirect HTTP code to "302 Found".
511- $ redirectHttpCode = 302 ;
463+ $ uri ->setPath ($ path );
464+ } else {
465+ $ uri ->setPath ('index.php ' );
466+ $ uri ->setVar ('lang ' , $ this ->lang_codes [$ lang_code ]->sef );
467+ }
468+ $ redirectHttpCode = 301 ;
469+ $ redirectUri = $ uri ->base () . $ uri ->toString (['path ' , 'query ' , 'fragment ' ]);
512470
513- // If selected language is the default language redirect code is "301 Moved Permanently".
514- if ($ lang_code === $ this ->default_lang ) {
515- $ redirectHttpCode = 301 ;
471+ // We cannot cache this redirect in browser. 301 is cacheable by default so we need to force to not cache it in browsers.
472+ $ this ->getApplication ()->setHeader ('Expires ' , 'Wed, 17 Aug 2005 00:00:00 GMT ' , true );
473+ $ this ->getApplication ()->setHeader ('Last-Modified ' , gmdate ('D, d M Y H:i:s ' ) . ' GMT ' , true );
474+ $ this ->getApplication ()->setHeader ('Cache-Control ' , 'no-store, no-cache, must-revalidate ' , false );
475+ $ this ->getApplication ()->sendHeaders ();
516476
517- // We cannot cache this redirect in browser. 301 is cacheable by default so we need to force to not cache it in browsers.
518- $ app ->setHeader ('Expires ' , 'Wed, 17 Aug 2005 00:00:00 GMT ' , true );
519- $ app ->setHeader ('Last-Modified ' , gmdate ('D, d M Y H:i:s ' ) . ' GMT ' , true );
520- $ app ->setHeader ('Cache-Control ' , 'no-store, no-cache, must-revalidate ' , false );
521- $ app ->sendHeaders ();
477+ // Redirect to language.
478+ $ this ->getApplication ()->redirect ($ redirectUri , $ redirectHttpCode );
479+ }
522480 }
523-
524- // Redirect to language.
525- $ app ->redirect ($ redirectUri , $ redirectHttpCode );
526481 }
527482
528483 // We have found our language and now need to set the cookie and the language value in our system
529484 $ this ->current_lang = $ lang_code ;
530485
531486 // Set the request var.
487+ $ app = $ this ->getApplication ();
532488 $ app ->getInput ()->set ('language ' , $ lang_code );
533489 $ app ->set ('language ' , $ lang_code );
534490 $ language = $ app ->getLanguage ();
@@ -554,9 +510,9 @@ public function parseRule(&$router, &$uri)
554510 }
555511
556512 // Create a cookie.
557- if ( $ this ->getLanguageCookie () !== $ lang_code ) {
558- $ this -> setLanguageCookie ( $ lang_code );
559- }
513+ $ this ->setLanguageCookie ( $ lang_code );
514+
515+ $ router -> detachRule ( ' parse ' , [ $ this , ' setLanguageApplicationState ' ], $ router :: PROCESS_BEFORE );
560516 }
561517
562518 /**
0 commit comments