-
-
Save gregoriopellegrino/c94c46933d660a3db233e7a1c9c6f5e6 to your computer and use it in GitHub Desktop.
// main lang | |
if ($("html").get(0).hasAttribute("lang")) { | |
var main_lang = $("html").attr("lang"); | |
} else { | |
var main_lang = $("body").attr("lang"); | |
} | |
console.log("Main language is "+main_lang); | |
// children lang | |
var langs = []; | |
$("*[lang]").each(function() { | |
var lang = $(this).attr("lang"); | |
if ($.inArray(lang, langs) == -1) { | |
langs.push(lang) | |
} | |
}); | |
console.log("Other languages found: "+langs); |
Thanks!
No problem.
That said, trying to handle errors kinda opens a can of worms:
- I’m trimming the value (leading and trailing spaces) for empty value, but that should probably be flagged for non-empty value as well, cf. HTML standard;
- Also, in the XHTML “cheat code”, I’m adding a lang attribute if there is none (as pure commodity so that the other code can work) but I’m wondering whether that should not be used to check that if both attributes are present, their value must be the same.
I’ll try updating the code above at some point.
Here you go, this one will check if language tag is valid, and whether values of xml:lang
and lang
match.
I refrained from refactoring too much so that the process be kinda clearer/commented but turned it into functions so that it can be refactored into a JS module more easily – so anyone please feel free to do that if re-using the logic (npm, etc.) and share it.
// UTILS
// Strips leading or trailing spaces from string
const trimmer = (string) => {
return string.trim();
}
// Checks if lang is specified
const langIsSpecified = (string) => {
string = trimmer(string);
if (string.length > 1) {
return true;
} else {
return false;
}
}
// Checks if string contains leading or trailing spaces
const hasWhitespace = (string) => {
return string.length > trimmer(string).length;
}
// Find and checks validity of the lang for the given element
const findLangForEl = (el) => {
const langValue = el.lang;
if (langIsSpecified(langValue)) {
// If lang is specified
if (!hasWhitespace(langValue)) {
// If value is valid, return value
return langValue;
} else {
// If lang specified for el have a space, then it’s invalid BCP47 so we throw an error
console.error(`There is a space in '${langValue}' therefore it isn’t a valid BCP47 language tag for element:`, el);
}
}
return undefined;
}
// FUNCTIONS/METHODS
// Handle XHTML (xml namespace)
const handleXMLLang = () => {
// Query all elements in the DOM
const domEls = document.querySelectorAll("*");
// For each, check if there is an xml:lang
for (let i = 0; i < domEls.length; i++) {
const el = domEls[i];
if (el.hasAttribute("xml:lang") && el.hasAttribute("lang")) {
// if there is, and there’s also a lang, make sure their values match:
const xmlValue = el.getAttribute("xml:lang");
const langValue = el.getAttribute("lang");
if (xmlValue !== langValue) {
// If value is different then it’s an error
console.error(`Langs don’t match for element:`, el);
}
} else if (el.hasAttribute("xml:lang") && !el.hasAttribute("lang")) {
// if there is xml:lang but not lang, then add it
// Note: this function must be called first so that code below takes over
const langValue = el.getAttribute("xml:lang");
el.setAttribute("lang", langValue);
}
}
};
const checkMainLang = () => {
// Checking if lang specified for html and body
const docLang = findLangForEl(document.documentElement);
const bodyLang = findLangForEl(document.body);
if (docLang && bodyLang) {
// If both HTML and BODY langs are specified
if (docLang !== bodyLang) {
// We check if they match, and warn if they don’t
console.warn(
`HTML and BODY langs don’t match. HTML is '${docLang}' while BODY is '${bodyLang}'. Main lang will be '${bodyLang}'.`
);
} else {
// Else we use html’s
console.log(`Main lang is '${docLang}'.`);
}
} else if (docLang && !bodyLang) {
// If it’s specified for html
console.log(`Main lang is '${docLang}'.`);
} else if (bodyLang && !docLang) {
// If it’s specified for body
console.warn(`Main lang is '${bodyLang}' but only set on BODY. Elements such as <title> consequently don’t inherit a specified lang and could inherit the navigator’s default.`);
} else {
// Else we warn no lang is set and the navigator’s default will be used
console.warn(`No lang is set, it will default to the navigator’s: ${navigator.language}.`)
}
};
const checkOtherLangs = () => {
// Other languages start here, with an empty array
let langs = [];
// We check all elements in body with a lang attribute
const els = document.body.querySelectorAll(`*[lang]`);
// For each, we check if we must add the lang to the array
for (let i = 0; i < els.length; i++) {
const el = els[i];
const langToAdd = findLangForEl(el);
// If there’s a lang and it isn’t in the array yet, we add it
if (langToAdd && langs.indexOf(langToAdd) === -1) {
langs.push(langToAdd);
}
}
// Finally we log all the other languages found.
console.log(`Other languages found: ${langs.toString().replace(/,/g, ", ")}`);
};
handleXMLLang();
checkMainLang();
checkOtherLangs();
Also, if you want to check the validity of the BCP47 language tag (pattern) → https://github.com/SafetyCulture/bcp47
This has gone so far that I’m wondering whether it shouldn’t be a module (with a GitHub repo). I could even imagine having/finding some use-cases in the future.
My idea was simply to save a code that I use from time to time to check the accessibility of EPUB and PDF :) , but the work it's becoming interesting...
I’ve just pushed an invite – started working on completing this a little bit in a private repo – so it’s fair to enable access for you :-)
Off the top of my head, vanilla ES6 for HTML with a few improvements here and there – see comments.
Sorry, couldn’t test that extensively but what’s for sure is that it won’t work for xhtml because xml namespace so
element.lang
doesn’t work, and neither doesquerySelectorAll
with thexml:lang
attribute – please someone feel free to XHTML-ize it and/or use cheat code at the end.If you want to cheat your way out of XHTML, then add the following at the very top: