Pinnacle { locale = locale.toLowerCase() const aliases = { ‘zh-cn’: ‘zh-CN’, ‘zh-tw’: ‘zh-TW’, ‘pt-br’: ‘pt’ } return aliases[locale] || locale }) const DEFAULT_BROWSER_LOCALE = (() => { const tld = document.location.hostname.split(“.”).reverse()[0] if (tld === “se”) return “sv” const browserLang = window.navigator.userLanguage || window.navigator.language const languages = window.navigator.languages || (browserLang.length === 5 ? [browserLang.slice(0, 2), browserLang] : [“en”, “en-GB”]) // some browsers include the region so we need to slice it unless for chinese const cultureCountry = languages.find(str => str.length === 5) || “” const lang = browserLang.slice(0, 2) === “zh” ? cultureCountry.toLowerCase() // eg: zh-tw, zh-cn : browserLang.slice(0, 2) const match = locales.find(l => l.toLowerCase() === lang) // fallback to english if locale is unsupported return match || ‘en’ })() // recall user language const prefs = window.localStorage && localStorage.getItem(“Main:Preferences”) const userPrefs = prefs && JSON.parse(prefs) const userLangCode = userPrefs && userPrefs.Language && userPrefs.Language.culture // contruct new url const parsed = new URL(window.location.href); const pathname = parsed.pathname.replace(/(/)$/g,”); const segments = pathname.split(‘/’).filter(_=>_); // first get locale from the uri as a “source of truth”. // if not present, check localStorage, otherwise fallback to browser const matchInLocales = segments.length >= 1 && locales.some(l => l.toLowerCase() === segments[0].toLowerCase()) const locale = matchInLocales ? sanitizeLocale(segments[0]) : userLangCode && ( [“zh-cn”, “zh-tw”, “zh-CN”, “zh-TW”].includes(userLangCode) ? sanitizeLocale(userLangCode) : sanitizeLocale(userLangCode.substr(0,2)) ) || DEFAULT_BROWSER_LOCALE // segs = segments without the language code if pressent in pathname let segs = segments; if (segments.length > 1 && matchInLocales) [, …segs] = segments // construct new urls, append any query strings if present const decorate = (locale, queryString = parsed.search) => path => `${locale}/${path}${queryString}` const build = decorate(locale) const ensureFwdSlash = url => { if (url.startsWith(‘http’)) return url return url.startsWith(‘/’) ? url : `/${url}` } // surmize where the user came from. direct url type in (aka no referrer) // means that they probably came from a live stream, otherwise it was // from an external banner link const referrer = document.referrer && new URL(document.referrer) const utmMedium = (referrer && referrer.hostname !== parsed.hostname) ? “banner_link” : “live_stream”; // redirects const langRegex = “^(/(” + locales.join(‘|’) + “))?” const redirects = { // legacy routes ————- // /:language?/payment-options -> /:language/future/legal-payments [`${langRegex}/payment-options$`]: build(‘future/legal-payments’), // /:language?/odds/match/:sport/:country/:league -> /:language/:sport/:league/matchups [`${langRegex}/odds/match/[a-zA-Z0-9-_]+/[a-zA-Z0-9-_]+/[a-zA-Z0-9-_]+$`]: build(`${segs[2]}/${segs[4]}/matchups`), // /:language?/odds/specials/:sport/:league -> /:language/:sport/:league/matchups [`${langRegex}/odds/specials/[a-zA-Z0-9-_]+/[a-zA-Z0-9-_]+$`]: build(`${segs[2]}/${segs[3]}`), // /:language?/odds-list/countries/:sport -> /:language/:sport/leagues [`${langRegex}/odds-list/countries/[a-zA-Z0-9-_]+$`]: build(`${segs[2]}/leagues`), // /signup/desktop/:culture -> /:language/account/signup/step1 [`${langRegex}/signup/desktop(.*)`]: build(`account/signup/step1`), // /signup/mobile/:culture -> /:language/account/signup/step1 [`${langRegex}/signup/mobile(.*)`]: build(`account/signup/step1`), // /:language?/odds/today/:sport -> /:language/:sport/matchups [`${langRegex}/odds/today/[a-zA-Z0-9-_]+$`]: build(`${segs[2]}/matchups`), // /:language?/football/ncaa-c/matchups -> /:language/football/ncaa/matchups [`${langRegex}/football/ncaa-c/matchups$`]: build(`football/ncaa/matchups`), // sports book routes ——– // /:language?/e-sports -> /:language/esports/matchups/highlights [`${langRegex}/e-sports$(/)?`]: build(`esports/matchups/highlights`), // /:language?/e-sports/matchups -> /:language/esports/matchups/highlights [`${langRegex}/e-sports/matchups$(/)?`]: build(`esports/matchups/highlights`), // /:language?/esports/matchups -> /:language/esports/matchups/highlights [`${langRegex}/esports/matchups$(/)?`]: build(`esports/matchups/highlights`), // /:language?/soccer/matchups -> /:language/soccer/matchups/highlights [`${langRegex}/soccer/matchups$(/)?`]: build(`soccer/matchups/highlights`), // /:language?/login/:param+ -> /:language/account/login [`${langRegex}/login(.*)`]: build(`account/login`), // campaigns —————– // CS:GO – PinnacleCup -> /en/esports/games/csgo/pinnacle-cup/matchups [`${langRegex}/Odds$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=odds&btag=pinn_odds&refer=Q2PinnacleCup21CSGOspshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup/matchups`), [`${langRegex}/CSGO-Banner-Landing$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=banner_landing&btag=pinn_banner_landing&refer=Q2PinnacleCup21CSGOspshp&bounce=1’ )(`landing/pinnacle-cup-csgo`), [`${langRegex}/CSGO-Odds$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=banner_odds&btag=pinn_banner_odds&refer=Q2PinnacleCup21CSGOspshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup/matchups`), [`${langRegex}/CSGO-Banner-Odds$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=chatbot&btag=pinn_chatbot&refer=Q2PinnacleCup21CSGOspshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup/matchups`), [`${langRegex}/Welcome$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=welcome&btag=pinn_welcome&refer=Q2PinnacleCup21CSGOspshp&bounce=1’ )(`landing/pinnacle-cup-csgo`), // CS:GO – PinnacleCup -> /pt/esports/games/csgo/pinnacle-cup/matchups [`${langRegex}/Probabilidades$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=odds&btag=pinn_odds&refer=Q2PinnacleCup21CSGOspshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup/matchups`), [`${langRegex}/BR-CSGO-Banner-Landing$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=banner_landing&btag=pinn_banner_landing&refer=Q2PinnacleCup21CSGOspshp&bounce=1’ )(`landing/pinnacle-cup-csgo`), [`${langRegex}/BR-CSGO-Banner-Odds$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=banner_odds&btag=pinn_banner_odds&refer=Q2PinnacleCup21CSGOspshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup/matchups`), [`${langRegex}/Bate-papo$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=chatbot&btag=pinn_chatbot&refer=Q2PinnacleCup21CSGOspshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup/matchups`), [`${langRegex}/Boas-vindas$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=welcome&btag=pinn_welcome&refer=Q2PinnacleCup21CSGOspshp&bounce=1’ )(`landing/pinnacle-cup-csgo`), // CS:GO – PinnacleCup -> /ru/esports/games/csgo/pinnacle-cup/matchups [`${langRegex}/RU-Odds$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=odds&btag=pinn_odds&refer=Q2PinnacleCup21CSGOspshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup/matchups`), [`${langRegex}/RU-CSGO-Banner-Landing$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=banner_landing&btag=pinn_banner_landing&refer=Q2PinnacleCup21CSGOspshp&bounce=1’ )(`landing/pinnacle-cup-csgo`), [`${langRegex}/RU-CSGO-Banner-Odds$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=banner_odds&btag=pinn_banner_odds&refer=Q2PinnacleCup21CSGOspshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup/matchups`), [`${langRegex}/RU-Chat$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=chatbot&btag=pinn_chatbot&refer=Q2PinnacleCup21CSGOspshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup/matchups`), [`${langRegex}/RU-Welcome(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=q2_pinnaclecup_csgo&utm_content=welcome&btag=pinn_welcome&refer=Q2PinnacleCup21CSGOspshp&bounce=1’ )(`landing/pinnacle-cup-csgo`), // CS:GO – PinnacleCup -> /en/esports/games/csgo/pinnacle-cup-europe/matchups [`${langRegex}/PinnacleCup$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=vanitylink&btag=pinn_vanitylink&refer=PinnacleCup21EuropeQ1spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup-europe/matchups`), [`${langRegex}/PinnacleCupBanner$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=banner&btag=pinn_banner&refer=PinnacleCup21EuropeQ1spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup-europe/matchups`), [`${langRegex}/PinnacleCupChat$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=chatbot&btag=pinn_chatbot&refer=PinnacleCup21EuropeQ1spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup-europe/matchups`), // CS:GO – PinnacleCup -> /pt/esports/games/csgo/pinnacle-cup-europe/matchups [`${langRegex}/PinnacleCupBR$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=vanitylink&btag=pinn_vanitylink&refer=PinnacleCup21EuropeQ1spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup-europe/matchups`), [`${langRegex}/PinnacleCupBRBanner$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=banner&btag=pinn_banner&refer=PinnacleCup21EuropeQ1spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup-europe/matchups`), [`${langRegex}/PinnacleCupBRChatbot$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=chatbot&btag=pinn_chatbot&refer=PinnacleCup21EuropeQ1spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup-europe/matchups`), // CS:GO – PinnacleCup -> /ru/esports/games/csgo/pinnacle-cup-europe/matchups [`${langRegex}/PinnacleCupRU$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=vanitylink&btag=pinn_vanitylink&refer=PinnacleCup21EuropeQ1spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup-europe/matchups`), [`${langRegex}/PinnacleCupRUBanner$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=banner&btag=pinn_banner&refer=PinnacleCup21EuropeQ1spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup-europe/matchups`), [`${langRegex}/PinnacleCupRUChatbot$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=chatbot&btag=pinn_chatbot&refer=PinnacleCup21EuropeQ1spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/csgo/pinnacle-cup-europe/matchups`), // DOTA 2 – Pinnacle Cup -> /en/esports/games/dota-2/pinnacle-cup/matchups [`${langRegex}/Dota-Banner-Landing$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup_dota&utm_content=banner_landing&btag=pinn_banner_landing&refer=PinnacleCup21DotaEuropeQ2spshp&bounce=1’ )(`landing/pinnacle-cup-dota`), [`${langRegex}/Dota-Banner-Odds$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup_dota&utm_content=banner_odds&btag=pinn_banner_odds&refer=PinnacleCup21DotaEuropeQ2spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/dota-2/pinnacle-cup/matchups`), [`${langRegex}/Dota-Odds$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup_dota&utm_content=chatbot&btag=pinn_chatbot&refer=PinnacleCup21DotaEuropeQ2spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/dota-2/pinnacle-cup/matchups`), // DOTA 2 – Pinnacle Cup -> /pt/esports/games/dota-2/pinnacle-cup/matchups [`${langRegex}/BR-Dota-Banner-Landing$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup_dota&utm_content=banner_landing&btag=pinn_banner_landing&refer=PinnacleCup21DotaEuropeQ2spshp&bounce=1’ )(`landing/pinnacle-cup-dota`), [`${langRegex}/BR-Dota-Banner-Odds$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup_dota&utm_content=banner_odds&btag=pinn_banner_odds&refer=PinnacleCup21DotaEuropeQ2spshp&bounce=1’ )(`landing/pinnacle-cup-dota`), // DOTA 2 – Pinnacle Cup -> /ru/esports/games/dota-2/pinnacle-cup/matchups [`${langRegex}/RU-Banner-Landing$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup_dota&utm_content=banner_landing&btag=pinn_banner_landing&refer=PinnacleCup21DotaEuropeQ2spshp&bounce=1’ )(`landing/pinnacle-cup-dota`), [`${langRegex}/RU-Banner-Odds$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup_dota&utm_content=banner_odds&btag=pinn_banner_odds&refer=PinnacleCup21DotaEuropeQ2spshp&rp=/esports/leagues&fwd_search=1’ )(`esports/games/dota-2/pinnacle-cup/matchups`), // !!!! README !!!! // the following append a &bounce=1 param which forces a hard redirect // as these urls are external // /raffle -> /en/landing/esports-csgo-flashpoint-raffle [`${langRegex}/raffle$(/)?`]: decorate( ‘en’, ‘?utm_source=EN_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=raffle&btag=raffle&refer=PinnacleCup21EuropeQ1spshp&bounce=1’ )(`landing/pinnacle-cup-csgo-trustly`), // /raffle -> /ru/landing/esports-csgo-flashpoint-raffle [`${langRegex}/raffleRU$(/)?`]: decorate( ‘ru’, ‘?utm_source=RU_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=raffle&btag=raffle&refer=PinnacleCup21EuropeQ1spshp&bounce=1’ )(`landing/pinnacle-cup-csgo`), // /sorteio -> /pt/landing/esports-csgo-flashpoint-raffle [`${langRegex}/sorteio$(/)?`]: decorate( ‘pt’, ‘?utm_source=BR_twitch_tv&utm_medium=live_stream&utm_campaign=pinnaclecup&utm_content=raffle&btag=raffle&refer=PinnacleCup21EuropeQ1spshp&bounce=1’ )(`landing/pinnacle-cup-csgo`), // Triggy redirect —- [`(.*)st=trg(.*)`]: decorate(‘en’, [parsed.search ? parsed.search : “?”, ‘fwd_search=1’].join(“&”))(‘service/triggy-handler’) } // check if it’s a redirect route const match = Object.keys(redirects).find(pattern => { const regex = new RegExp(pattern, “i”); return regex.test(pathname) || regex.test(parsed.search); }) // found redirect match; send them off if (match) { const redirectUrl = ensureFwdSlash(redirects[match]); // if &bounce=1 is present, we need to do a full // page reload as it sends them an external page const queryStringParams = new URLSearchParams(redirectUrl); if (queryStringParams.get(‘bounce’)) { window.location.href = redirectUrl; } else { window.history.pushState(null, “”, redirectUrl) } } // otherwise, replace url with path that has the language code else if (segments.length === 0 || (segments.length > 0 && !matchInLocales)) { const newPath = ‘/’+ locale + parsed.pathname + parsed.search const newUrl = parsed.origin +’/’+ newPath window.history.pushState(null, “”, newPath) }]]> Continue reading from the Source