import { Inject , Injectable } from '@angular/core' ; import { Router , NavigationStart , ActivatedRouteSnapshot , NavigationExtras , ActivatedRoute , Event , NavigationCancel , Routes } from '@angular/router' ; import { Subject , Observable , ReplaySubject } from 'rxjs' ; import { filter , pairwise } from 'rxjs/operators' ; import { LocalizeParser } from './localize-router.parser' ; import { LocalizeRouterSettings } from './localize-router.config' ; import { LocalizedMatcherUrlSegment } from './localized-matcher-url-segment' ; import { deepCopy } from './util' ; /**
 * Localization service
 * modifyRoutes
 */ @ Injectable ( ) export class LocalizeRouterService { routerEvents : Subject < string > ; hooks : { internal _initializedSubject : ReplaySubject < boolean > ; initialized : Observable < boolean > ; } ; private latestUrl : string ; private lastExtras ? : NavigationExtras ; /**
     * CTOR
     */ constructor ( @ Inject ( LocalizeParser ) public parser , @ Inject ( LocalizeRouterSettings ) public settings , @ Inject ( Router ) private router , @ Inject ( ActivatedRoute ) private route ) { this . routerEvents = new Subject < string > ( ) ; const initializedSubject = new ReplaySubject < boolean > ( 1 ) ; this . hooks = { _initializedSubject : initializedSubject , initialized : initializedSubject . asObservable ( ) } ; } /**
     * Start up the service
     */ init ( ) { this . applyConfigToRouter ( this . parser . routes ) ; this . router . events . pipe ( filter ( event => event instanceof NavigationStart ) , pairwise ( ) ) . subscribe ( this . _routeChanged ( ) ) ; if ( this . settings . initialNavigation ) { this . router . initialNavigation ( ) ; } } /**
     * Change language and navigate to translated route
     */ changeLanguage ( lang , extras ? , useNavigateMethod ? ) { if ( lang !== this . parser . currentLang ) { const rootSnapshot = this . router . routerState . snapshot . root ; this . parser . translateRoutes ( lang ) . subscribe ( ( ) => { let url = this . traverseRouteSnapshot ( rootSnapshot ) ; url = this . translateRoute ( url ) as string ; if ( ! this . settings . alwaysSetPrefix ) { let urlSegments = url . split ( '/' ) ; const languageSegmentIndex = urlSegments . indexOf ( this . parser . currentLang ) ; if ( this . parser . currentLang === this . parser . defaultLang ) { if ( languageSegmentIndex === 0 || ( languageSegmentIndex === 1 && urlSegments [ 0 ] === '' ) ) { urlSegments = urlSegments . slice ( 0 , languageSegmentIndex ) . concat ( urlSegments . slice ( languageSegmentIndex + 1 ) ) ; } } else { if ( languageSegmentIndex === - 1 ) { const injectionIndex = urlSegments [ 0 ] === '' ? 1 : 0 ; urlSegments = urlSegments . slice ( 0 , injectionIndex ) . concat ( this . parser . currentLang , urlSegments . slice ( injectionIndex ) ) ; } } url = urlSegments . join ( '/' ) ; } url = url . replace ( /\/+/g , '/' ) ; const lastSlashIndex = url . lastIndexOf ( '/' ) ; if ( lastSlashIndex > 0 && lastSlashIndex === url . length - 1 ) { url = url . slice ( 0 , - 1 ) ; } const queryParamsObj = this . parser . chooseQueryParams ( extras , this . route . snapshot . queryParams ) ; this . applyConfigToRouter ( this . parser . routes ) ; this . lastExtras = extras ; if ( useNavigateMethod ) { const extrasToApply = extras ? { ... extras } : { } ; if ( queryParamsObj ) { extrasToApply . queryParams = queryParamsObj ; } this . router . navigate ( [ url ] , extrasToApply ) ; } else { let queryParams = this . parser . formatQueryParams ( queryParamsObj ) ; queryParams = queryParams ? `?${ queryParams }` : '' ; this . router . navigateByUrl ( `${ url }${ queryParams }` , extras ) ; } } ) ; } } /**
     * Traverses through the tree to assemble new translated url
     */ private traverseRouteSnapshot ( snapshot ) { if ( snapshot . firstChild && snapshot . routeConfig ) { return `${ this . parseSegmentValue ( snapshot ) }/${ this . traverseRouteSnapshot ( snapshot . firstChild ) }` ; } else if ( snapshot . firstChild ) { return this . traverseRouteSnapshot ( snapshot . firstChild ) ; } else { return this . parseSegmentValue ( snapshot ) ; } } /**
     * Build URL from segments and snapshot (for params)
     */ private buildUrlFromSegments ( snapshot , segments ) { return segments . map ( ( s , i ) => s . indexOf ( ':' ) === 0 ? snapshot . url [ i ] . path : s ) . join ( '/' ) ; } /**
     * Extracts new segment value based on routeConfig and url
     */ private parseSegmentValue ( snapshot ) { if ( snapshot . routeConfig && snapshot . routeConfig . matcher ) { const subPathMatchedSegments = this . parseSegmentValueMatcher ( snapshot ) ; return this . buildUrlFromSegments ( snapshot , subPathMatchedSegments ) ; } else if ( snapshot . data . localizeRouter ) { const path = snapshot . data . localizeRouter . path ; const subPathSegments = path . split ( '/' ) ; return this . buildUrlFromSegments ( snapshot , subPathSegments ) ; } else if ( snapshot . parent && snapshot . parent . parent ) { const path = snapshot . routeConfig . path ; const subPathSegments = path . split ( '/' ) ; return this . buildUrlFromSegments ( snapshot , subPathSegments ) ; } else { return '' ; } } private parseSegmentValueMatcher ( snapshot ) { const localizeMatcherParams = snapshot . data && snapshot . data . localizeMatcher && snapshot . data . localizeMatcher . params || { } ; const subPathSegments = snapshot . url . map ( ( segment ) => { const currentPath = segment . path ; const matchedParamName = segment . localizedParamName ; const val = ( matchedParamName && localizeMatcherParams [ matchedParamName ] ) ? localizeMatcherParams [ matchedParamName ] ( currentPath ) : null ; return val || `${ this . parser . getEscapePrefix ( ) }${ currentPath }` ; } ) ; return subPathSegments ; } /**
     * Translate route to current language
     * If new language is explicitly provided then replace language part in url with new language
     */ translateRoute ( path ) { if ( typeof path === 'string' ) { const url = this . parser . translateRoute ( path ) ; return ! path . indexOf ( '/' ) ? this . parser . addPrefixToUrl ( url ) : url ; } const result = [ ] ; ( path as Array < any > ) . forEach ( ( segment , index ) => { if ( typeof segment === 'string' ) { const res = this . parser . translateRoute ( segment ) ; if ( ! index && ! segment . indexOf ( '/' ) ) { result . push ( this . parser . addPrefixToUrl ( res ) ) ; } else { result . push ( res ) ; } } else { result . push ( segment ) ; } } ) ; return result ; } /**
     * Event handler to react on route change
     */ private _routeChanged ( ) { return ( [ previousEvent , currentEvent ] ) => { const previousLang = this . parser . getLocationLang ( previousEvent . url ) || this . parser . defaultLang ; const currentLang = this . parser . getLocationLang ( currentEvent . url ) || this . parser . defaultLang ; const lastExtras = this . lastExtras ; if ( currentLang !== previousLang && this . latestUrl !== currentEvent . url ) { this . latestUrl = currentEvent . url ; this . cancelCurrentNavigation ( ) ; this . parser . translateRoutes ( currentLang ) . subscribe ( ( ) => { this . applyConfigToRouter ( this . parser . routes ) ; this . lastExtras = undefined ; this . router . navigateByUrl ( currentEvent . url , lastExtras ) ; this . routerEvents . next ( currentLang ) ; } ) ; } this . latestUrl = currentEvent . url ; } ; } /**
     * Drop the current Navigation
     */ private cancelCurrentNavigation ( ) { const currentNavigation = this . router . getCurrentNavigation ( ) ; const url = this . router . serializeUrl ( currentNavigation . extractedUrl ) ; ( this . router . events as Subject < Event > ) . next ( new NavigationCancel ( currentNavigation . id , url , '' ) ) ; ( this . router as any ) . transitions . next ( { ... ( this . router as any ) . transitions . getValue ( ) , id : 0 } ) ; } param config private applyConfigToRouter ( config ) { this . router . resetConfig ( deepCopy ( config ) ) ; } }