@carbon/utilities
Version:
Utilities and helpers to drive consistency across software products using the Carbon Design System
69 lines (68 loc) • 2.22 kB
JavaScript
//#region src/documentLang/documentLang.ts
/**
* Copyright IBM Corp. 2025
*
* This source code is licensed under the Apache-2.0 license found in the
* LICENSE file in the root directory of this source tree.
*/
const isBrowser = typeof document !== "undefined";
let currentLang = isBrowser ? document.documentElement.lang : "";
let updateScheduled = false;
let subscribers = [];
let observerInitialized = false;
/**
* Internal callback for MutationObserver to dispatch lang changes.
* debounced into a microtask, and avoiding redundant notifications.
*/
function handleMutations() {
if (!isBrowser) return;
if (updateScheduled) return;
updateScheduled = true;
queueMicrotask(() => {
updateScheduled = false;
const newLang = document.documentElement.lang;
if (newLang === currentLang) return;
currentLang = newLang;
for (const callback of subscribers) callback(newLang);
});
}
/**
* Initializes a shared MutationObserver for the <html> lang attribute.
*/
function initObserver() {
if (!isBrowser || observerInitialized) return;
observerInitialized = true;
new MutationObserver(handleMutations).observe(document.documentElement, {
attributes: true,
attributeFilter: ["lang"]
});
}
/**
* Retrieves the current document language. Falls back to the browser's
* `navigator.language` if the `<html>` lang attribute is empty.
*
* @returns {string} The current document language code.
*/
function getDocumentLang() {
return isBrowser ? document.documentElement.lang || window.navigator.language || "" : "";
}
/**
* Subscribes to changes on the `<html>` element's `lang` attribute.
* Uses a shared MutationObserver under the hood to watch for attribute
* mutations.
*
* @param {Subscriber} callback - Invoked with the new language code whenever
* it changes.
* @returns {() => void} A function that, when called, removes this subscription
*/
function subscribeDocumentLangChange(callback) {
if (!isBrowser) return () => {};
if (!observerInitialized) initObserver();
subscribers.push(callback);
return () => {
subscribers = subscribers.filter((cb) => cb !== callback);
if (subscribers.length === 0) observerInitialized = false;
};
}
//#endregion
export { getDocumentLang, subscribeDocumentLangChange };