Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
44 commits
Select commit Hold shift + click to select a range
a5e3916
setInternational cookie for languages-selector as per news config and…
Nov 13, 2025
1d9d7f0
Updating for apac cookievalue for cases like news site
Nov 20, 2025
abe14c7
Language banner flow for BACOM
Nov 24, 2025
5680da5
Language banner flow for BACOM
Nov 24, 2025
4b145a7
add lang banner
nishantka Nov 25, 2025
d22e47c
Changes for fetching markets from DA BACOM and early loading of json
Nov 26, 2025
4bed2c9
Updated for config and to fetch css
Nov 26, 2025
ec56318
update akamai locale func
nishantka Dec 3, 2025
b07521d
lang banner UI updates
nishantka Dec 9, 2025
b031f1e
add min height
nishantka Dec 9, 2025
1c1162d
Revert language-selector to stage version
Dec 10, 2025
db684de
Revert script.js to stage version
Dec 10, 2025
c9376dd
fix alignment
Dec 10, 2025
7b2c504
add analytics + ui udates
nishantka Dec 11, 2025
e2140f4
remove analytics testing changes
nishantka Dec 12, 2025
33967c1
Adding unit tests and fix for setting us cookie
Dec 14, 2025
913ee7f
Adding unit tests and fix for setting us cookie
Dec 14, 2025
890cc3f
Code improvements and cleanup
Dec 14, 2025
9312ed0
lang banner fix cls
nishantka Dec 15, 2025
79fbdc5
hide other promos when lang banner is shown
nishantka Dec 17, 2025
fae722a
test by adding as preload links
nishantka Dec 17, 2025
8bcb5f3
dont show banner only if pref market not present
nishantka Dec 18, 2025
b1a59f0
Language banner load (#5264)
sonawanesnehal3 Dec 18, 2025
501fed7
Merge branch 'stage' into language-banner
nishantka Dec 18, 2025
dba6d12
add opt in for preload market json
nishantka Dec 18, 2025
d91fe2d
Updating for params and ut
Dec 18, 2025
9b0b00c
Updated for config
Dec 18, 2025
72823e6
Fix for es-lint error
Dec 18, 2025
c90d56c
Adding getMarketsUrl
Dec 19, 2025
2deaf7d
resolving conflicts
Jan 5, 2026
141d119
fix UT and analytics
nishantka Jan 5, 2026
8878d03
Accommodating review comments
Jan 5, 2026
aa34b0c
fix padding
nishantka Jan 5, 2026
ee0cb86
fix focus ring
nishantka Jan 5, 2026
cb7be11
Adding fix for marketssource metadata
Jan 5, 2026
0b249ce
updating banner logic
nishantka Jan 5, 2026
43d9705
remove unused variable from lang banner unit test
nishantka Jan 5, 2026
7852f00
update gnav position based on lang banner
nishantka Jan 6, 2026
29601ce
Updated for refactoring and addAndShow markets
Jan 6, 2026
06fecb3
update css for lang banner
nishantka Jan 6, 2026
2434c94
update gnav position based on lang banner
nishantka Jan 6, 2026
e9d31c4
Load lang banner head calls together + lana logging + gnav mweb fixes
nishantka Jan 7, 2026
2235aaa
Merge branch 'stage' into language-banner
nishantka Jan 7, 2026
80c95f5
Adding lana logs for lingoProjectSuccessLogging
Jan 8, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion libs/blocks/caas/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -664,7 +664,7 @@ export async function getCountryAndLang({ autoCountryLang, country, language, so
|| pageConfigHelper().mep?.countryIP;

if (!geoCountry) {
const { getAkamaiCode } = await import('../../features/georoutingv2/georoutingv2.js');
const { default: getAkamaiCode } = await import('../../utils/geo.js');
geoCountry = await getAkamaiCode(true);
}

Expand Down
17 changes: 12 additions & 5 deletions libs/blocks/global-navigation/global-navigation.js
Original file line number Diff line number Diff line change
Expand Up @@ -1178,7 +1178,7 @@ class Gnav {
const promoPath = getMetadata('gnav-promo-source');
const fedsPromoWrapper = document.querySelector('.feds-promo-aside-wrapper');

if (!promoPath || !asideJsPromise) {
if (!promoPath || !asideJsPromise || !fedsPromoWrapper) {
fedsPromoWrapper?.remove();
this.block.classList.remove('has-promo');
return this.elements.aside;
Expand Down Expand Up @@ -1274,17 +1274,24 @@ class Gnav {
if (!popup) return;
const hasPromo = this.block.classList.contains('has-promo');
const promoHeight = this.elements.aside?.clientHeight;
const languageBanner = document.querySelector('.language-banner');
const languageBannerHeight = languageBanner?.offsetHeight || 0;

const isLocalNav = this.isLocalNav();
if (!isLocalNav && hasPromo) {
popup.style.top = `calc(0px - var(--feds-height-nav) - ${promoHeight}px)`;
const yOffset = window.scrollY || Math.abs(parseInt(document.body.style.top, 10)) || 0;

// Handle initial positioning for non-local nav (promo + visible language banner)
if (!isLocalNav && (hasPromo || languageBanner)) {
const langBannerNewHeight = languageBanner ? Math.max(0, languageBannerHeight - yOffset) : 0;
const totalOffsetHeight = (hasPromo ? promoHeight : 0) + langBannerNewHeight;
popup.style.top = `calc(0px - var(--feds-height-nav) - ${totalOffsetHeight}px)`;
if (isSmallScreen) return;
}
const yOffset = window.scrollY || Math.abs(parseInt(document.body.style.top, 10)) || 0;
const navOffset = hasPromo ? `var(--feds-height-nav) - ${promoHeight}px` : 'var(--feds-height-nav)';
popup.removeAttribute('style');
if (isLocalNav) {
popup.style.top = `calc(${yOffset}px - ${navOffset} - 2px)`;
const langBannerOffset = languageBanner ? languageBannerHeight : 0;
popup.style.top = `calc(${yOffset}px - ${navOffset} - ${langBannerOffset}px - 2px)`;
}
const { isPresent, isSticky, height } = getBranchBannerInfo();
if (isPresent) {
Expand Down
4 changes: 4 additions & 0 deletions libs/blocks/global-navigation/utilities/utilities.js
Original file line number Diff line number Diff line change
Expand Up @@ -954,6 +954,10 @@ export const [branchBannerLoadCheck, getBranchBannerInfo] = (() => {
// Optional: Disconnect the observer if you no longer need to track it
observer.disconnect();
}
if (node.classList?.contains('language-banner')) {
// Update the popup position when the language banner is removed
updatePopupPosition();
}
});
}
});
Expand Down
2 changes: 1 addition & 1 deletion libs/blocks/merch/merch.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,7 +227,7 @@ export async function getGeoLocaleSettings(miloLocale) {
|| sessionStorage.getItem('akamai');
if (!country) {
try {
const { getAkamaiCode } = await import('../../features/georoutingv2/georoutingv2.js');
const { getAkamaiCode } = await import('../../utils/geo.js');
country = await getAkamaiCode(true);
} catch (error) {
window.lana?.log(`Error getting Akamai code (will go with default country): ${error}`);
Expand Down
3 changes: 2 additions & 1 deletion libs/blocks/mobile-app-banner/mobile-app-banner.js
Original file line number Diff line number Diff line change
Expand Up @@ -78,7 +78,8 @@ async function branchInit(key) {
/* eslint-enable */
export default async function init(el) {
const header = document.querySelector('.global-navigation');
if (!header || header.classList.contains('has-promo')) return;
const languageBanner = document.querySelector('.language-banner');
if (!header || header.classList.contains('has-promo') || languageBanner) return;
const classListArray = Array.from(el.classList);
const product = classListArray.find((token) => token.startsWith('product-')).split('-')[1];
const key = await getKey(product);
Expand Down
28 changes: 2 additions & 26 deletions libs/features/georoutingv2/georoutingv2.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { getFederatedContentRoot, getCountry } from '../../utils/utils.js';
import { getFederatedContentRoot } from '../../utils/utils.js';
import { getAkamaiCode } from '../../utils/geo.js';

const OLD_GEOROUTING = 'oldgeorouting';

Expand Down Expand Up @@ -75,31 +76,6 @@ export const getCookie = (name) => document.cookie
.find((row) => row.startsWith(`${name}=`))
?.split('=')[1];

export const getAkamaiCode = (checkedParams = false) => new Promise((resolve, reject) => {
let akamaiLocale = null;
if (!checkedParams) {
akamaiLocale = getCountry();
}
if (akamaiLocale !== null) {
resolve(akamaiLocale.toLowerCase());
} else {
/* c8 ignore next 5 */
fetch('https://geo2.adobe.com/json/', { cache: 'no-cache' }).then((resp) => {
if (resp.ok) {
resp.json().then((data) => {
const code = data.country.toLowerCase();
sessionStorage.setItem('akamai', code);
resolve(code);
});
} else {
reject(new Error(`Something went wrong getting the akamai Code. Response status text: ${resp.statusText}`));
}
}).catch((error) => {
reject(new Error(`Something went wrong getting the akamai Code. ${error.message}`));
});
}
});

// Determine if any of the locales can be linked to.
async function getAvailableLocales(locales) {
const fallback = getMetadata('fallbackrouting') || config.fallbackRouting;
Expand Down
80 changes: 80 additions & 0 deletions libs/features/language-banner/language-banner.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
.language-banner {
display: flex;
padding: 12px 20px;
justify-content: center;
align-items: center;
min-height: var(--feds-language-banner-height);
gap: 8px;
align-self: stretch;
width: 100%;
background-color: #3B63FB;
box-sizing: border-box;
visibility: visible;
position: relative;
z-index: 11;
}

.language-banner-content {
display: flex;
align-items: center;
justify-content: flex-start;
gap: 4px;
flex: 1 0 0;
}

.language-banner-text {
color: #FFF;
font-family: var(--body-font-family);
font-size: 12px;
font-style: normal;
font-weight: 400;
line-height: 18px;
}

.language-banner-link {
color: #FFF;
font-family: var(--body-font-family);
font-size: 12px;
font-style: normal;
font-weight: 700;
line-height: 18px;
text-decoration: underline;
text-decoration-skip-ink: none;
text-underline-position: from-font;
cursor: pointer;
}

.language-banner-link:hover {
color: #FFF;
}

.language-banner-close {
display: flex;
align-items: center;
justify-content: center;
width: 20px;
height: 20px;
border: none;
cursor: pointer;
padding: 0;
background-color: transparent;
}

@media (min-width: 600px) {
.language-banner {
gap: 6px;
}

.language-banner-content {
justify-content: center;
}

.language-banner-close {
position: absolute;
right: 24px;
top: 50%;
transform: translateY(-50%);
flex-direction: column;
width: auto;
}
}
92 changes: 92 additions & 0 deletions libs/features/language-banner/language-banner.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
/* eslint-disable no-underscore-dangle */
import { getConfig, createTag, loadStyle, getTargetMarket } from '../../utils/utils.js';

function buildBanner(market, translatedUrl) {
const banner = document.body.querySelector('.language-banner');
if (!banner) return banner;
const messageContainer = createTag('div', { class: 'language-banner-content' });
const messageText = createTag('span', { class: 'language-banner-text' }, `${market.text} ${market.languageName}.`);
const link = createTag('a', { class: 'language-banner-link', href: translatedUrl, 'daa-ll': `${market.prefix || 'us'}|Continue` }, market.continueText || 'Continue');
const closeButton = createTag('button', { class: 'language-banner-close', 'aria-label': 'Close', 'daa-ll': 'Close' });
closeButton.innerHTML = `
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="none">
<path d="M10 0.5C15.2467 0.5 19.5 4.75329 19.5 10C19.5 15.2467 15.2467 19.5 10 19.5C4.75329 19.5 0.5 15.2467 0.5 10C0.5 4.75329 4.75329 0.5 10 0.5Z" stroke="white"/>
<path d="M6 14.0002L14 6.00024" stroke="white" stroke-width="2"/>
<path d="M14 14.0002L6 6.00024" stroke="white" stroke-width="2"/>
</svg>
`;

messageContainer.append(messageText, link);
banner.append(messageContainer, closeButton);
return banner;
}

function fireAnalyticsEvent(event) {
const data = {
xdm: {},
data: { web: { webInteraction: { name: event?.type } } },
};
if (event?.data) data.data._adobe_corpnew = { digitalData: event.data };
window._satellite?.track('event', data);
}

export function sendAnalytics(event) {
if (window._satellite?.track) {
fireAnalyticsEvent(event);
} else {
window.addEventListener('alloy_sendEvent', () => {
fireAnalyticsEvent(event);
}, { once: true });
}
}

async function showBanner(market, config) {
if (!market) return;

const { pathname } = window.location;
const currentPrefix = config.locale.prefix;
const pagePath = currentPrefix ? pathname.replace(currentPrefix, '') : pathname;
const translatedUrl = market.prefix
? `${window.location.origin}/${market.prefix}${pagePath}`
: `${window.location.origin}${pagePath}`;

const banner = buildBanner(market, translatedUrl);
if (!banner) return;
const { codeRoot, miloLibs } = config;
loadStyle(`${miloLibs || codeRoot}/features/language-banner/language-banner.css`);

const pagePrefix = config.locale.prefix?.replace('/', '') || 'us';
// eventName = "suggestedSite-currentSite|language-banner"
const eventName = `${market.prefix || 'us'}-${pagePrefix}|language-banner`;

banner.querySelector('.language-banner-link').addEventListener('click', async (e) => {
e.preventDefault();
const { setInternational } = await import('../../utils/utils.js');
setInternational(market.prefix || 'us');
if (config.lingoProjectSuccessLogging === 'on') {
window.lana.log(`Click: ${eventName}`, { sampleRate: 100, tags: 'lingo, lingo-language-banner-click' });
}
window.open(translatedUrl, '_self');
});

banner.querySelector('.language-banner-close').addEventListener('click', () => {
const domain = window.location.host.endsWith('.adobe.com') ? 'domain=adobe.com;' : '';
document.cookie = `international=${pagePrefix};path=/;${domain}`;
if (config.lingoProjectSuccessLogging === 'on') {
window.lana.log(`Close: ${eventName}`, { sampleRate: 100, tags: 'lingo, lingo-language-banner-close' });
}
banner.remove();
});

sendAnalytics(new Event(eventName));
if (config.lingoProjectSuccessLogging === 'on') {
window.lana.log(`Load: ${eventName}`, { sampleRate: 100, tags: 'lingo, lingo-language-banner-load' });
}
}

export default async function init() {
const market = getTargetMarket();
if (market) {
await showBanner(market, getConfig());
}
}
6 changes: 6 additions & 0 deletions libs/styles/styles.css
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
--global-height-navPromo: 72px;
--feds-totalheight-nav: calc(var(--feds-height-nav, --global-height-nav) + var(--feds-height-breadcrumbs, --global-height-breadcrumbs));
--feds-localnav-height: 40px;
--feds-language-banner-height: 44px;

/* Colors */
--link-color: rgb(39, 77, 234);
Expand Down Expand Up @@ -764,6 +765,11 @@ header.global-navigation + .feds-localnav {
height: var(--global-height-navPromo);
}

.language-banner {
height: var(--feds-language-banner-height);
visibility: hidden;
}

sr-only, .feds-sr-only {
position: absolute;
width: 1px;
Expand Down
34 changes: 34 additions & 0 deletions libs/utils/geo.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
import { getCountry } from './utils.js';
/* eslint-disable no-console */

/**
* Gets the Akamai country code from the geo2.adobe.com service.
* @param {boolean} [checkParams=false] - If true, checks URL parameters and session storage first.
* @returns {Promise<string|null>} A promise that resolves to the lowercase Akamai country code.
*/
export const getAkamaiCode = (checkedParams = false) => new Promise((resolve, reject) => {
let akamaiLocale = null;
if (!checkedParams) {
akamaiLocale = getCountry();
}
if (akamaiLocale !== null) {
resolve(akamaiLocale.toLowerCase());
} else {
/* c8 ignore next 5 */
fetch('https://geo2.adobe.com/json/', { cache: 'no-cache' }).then((resp) => {
if (resp.ok) {
resp.json().then((data) => {
const code = data.country.toLowerCase();
sessionStorage.setItem('akamai', code);
resolve(code);
});
} else {
reject(new Error(`Something went wrong getting the akamai Code. Response status text: ${resp.statusText}`));
}
}).catch((error) => {
reject(new Error(`Something went wrong getting the akamai Code. ${error.message}`));
});
}
});

export default getAkamaiCode;
Loading
Loading