Skip to content
Snippets Groups Projects
Verified Commit b976975d authored by Volker Schukai's avatar Volker Schukai :alien:
Browse files

feat: new language control #276

parent 7fa3cf45
No related branches found
No related tags found
No related merge requests found
/**
* Copyright © schukai GmbH and all contributing authors, {{copyRightYear}}. All rights reserved.
* Node module: @schukai/monster
*
* This source code is licensed under the GNU Affero General Public License version 3 (AGPLv3).
* The full text of the license can be found at: https://www.gnu.org/licenses/agpl-3.0.en.html
*
* For those who do not wish to adhere to the AGPLv3, a commercial license is available.
* Acquiring a commercial license allows you to use this software without complying with the AGPLv3 terms.
* For more information about purchasing a commercial license, please contact schukai GmbH.
*
* SPDX-License-Identifier: AGPL-3.0
*/
import {languages} from "./map/languages.mjs";
/**
* Determines the user's preferred language based on browser settings and available language options.
*
* It evaluates the current HTML document language, the browser's defined languages, and
* the language options from `<link>` elements with `hreflang` attributes in the document.
*
* @return {Object} An object containing information about the detected language, preferred language, and available languages.
*/
export function detectUserLanguagePreference() {
const currentLang = document.documentElement.lang;
let preferredLanguages = [];
if (typeof navigator.language === "string" && navigator.language.length > 0) {
preferredLanguages = [navigator.language];
}
if (Array.isArray(navigator.languages) && navigator.languages.length > 0) {
preferredLanguages = navigator.languages;
}
// add to preferredLanguages all the base languages of the preferred languages
preferredLanguages = preferredLanguages.concat(preferredLanguages.map(lang => lang.split("-")[0]));
if (!currentLang && preferredLanguages.length === 0) {
return {
message: "No language information available.",
};
}
const linkTags = document.querySelectorAll("link[hreflang]");
if (linkTags.length === 0) {
return {
current: currentLang || null,
message: "No <link> tags with hreflang available.",
};
}
const availableLanguages = [...linkTags].map((link) => {
const fullLang = link.hreflang;
const baseLang = fullLang.split("-")[0];
let label = link.getAttribute('data-monster-label')
if (!label) {
label = languages?.[fullLang]
if (!label) {
label = languages?.[baseLang]
}
}
return {
fullLang,
baseLang,
label,
href: link.href,
};
});
// filter availableLanguages to only include languages that are in the preferredLanguages array
const offerableLanguages = availableLanguages.filter(lang => preferredLanguages.includes(lang.fullLang) || preferredLanguages.includes(lang.baseLang));
if (offerableLanguages.length === 0) {
return {
current: currentLang || null,
message: "No available languages match the user's preferences.",
available: availableLanguages.map((lang) => ({
...lang,
weight: 1,
})),
};
}
// Helper function to determine the "weight" of a language match
function getWeight(langEntry) {
// Full match has priority 3
if (preferredLanguages.includes(langEntry.fullLang)) return 3;
// Base language match has priority 2
if (preferredLanguages.includes(langEntry.baseLang)) return 2;
// No match is priority 1
return 1;
}
// Sort the available languages by descending weight
offerableLanguages.sort((a, b) => getWeight(b) - getWeight(a));
// The best match is the first in the sorted list
const bestMatch = offerableLanguages[0];
const bestMatchWeight = getWeight(bestMatch);
const currentLabel = languages?.[currentLang] || currentLang
// If we found a language that matches user preferences (weight > 1)
if (bestMatchWeight > 0) {
return {
current: currentLang || null,
currentLabel: currentLabel,
preferred: {
full: bestMatch.fullLang,
base: bestMatch.baseLang,
label: bestMatch.label,
href : bestMatch.href,
},
available: availableLanguages.map((lang) => ({
...lang,
weight: getWeight(lang),
})),
offerable: offerableLanguages.map((lang) => ({
...lang,
weight: getWeight(lang),
})),
};
}
// If no language matched the user's preferences
return {
current: currentLang || null,
message: "None of the preferred languages are available.",
available: availableLanguages.map((lang) => ({
...lang,
weight: getWeight(lang),
})),
};
}
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment