Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(rule): xml-lang-mismatch #999

Merged
merged 14 commits into from
Aug 16, 2018
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
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
Prev Previous commit
Next Next commit
fix: refactor based on review
  • Loading branch information
jeeyyy committed Aug 14, 2018
commit 30fcefc6b8224bc5f1a99ea687b31ef2e5032311
4 changes: 2 additions & 2 deletions lib/checks/language/valid-lang.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
var langs, invalid;

langs = (options ? options : axe.commons.utils.validLangs()).map(
axe.utils.getBaseLang
axe.commons.utils.getBaseLang
);

invalid = ['lang', 'xml:lang'].reduce(function(invalid, langAttr) {
Expand All @@ -10,7 +10,7 @@ invalid = ['lang', 'xml:lang'].reduce(function(invalid, langAttr) {
return invalid;
}

var baselangVal = axe.utils.getBaseLang(langVal);
var baselangVal = axe.commons.utils.getBaseLang(langVal);

// Edge sets lang to an empty string when xml:lang is set
// so we need to ignore empty strings here
Expand Down
37 changes: 4 additions & 33 deletions lib/checks/language/xml-lang-mismatch.js
Original file line number Diff line number Diff line change
@@ -1,34 +1,5 @@
// construct array of language sub values specified for either lang and xml:lang attributes
let langSubTagsToValidate = [];
const { getBaseLang } = axe.commons.utils;
const primaryLangValue = getBaseLang(node.getAttribute('lang') || '');
const primaryXmlLangValue = getBaseLang(node.getAttribute('xml:lang') || '');

// get value of lang attribute
const langValue = node.getAttribute('lang');
// if no lang is specified, can't compute mismatch - return false
if (!langValue) {
return false;
}
langSubTagsToValidate.push(axe.utils.getBaseLang(langValue));

// get value of xml:lang attribute
const xmlLangValue = node.getAttribute('xml:lang');
// if no xml:lang is specified, can't compute mismatch - return false
if (!xmlLangValue) {
return false;
}
langSubTagsToValidate.push(axe.utils.getBaseLang(langValue));

// unique the produced array, if identical primary subtag values were supplied, this would result in an array of length 1.
// eg: if lang='en-GB' and xml:lang='en-US', the below will produce langSubTagsToValidate = ['en']
langSubTagsToValidate = axe.utils.uniqueArray(langSubTagsToValidate, []);

// if the length of this array > 1 - the specified values for lang and xml:lang were different
// eg: lang='en-GB' and xml:lang='fr-FR' will yeild langSubTagsToValidate = ['en', 'fr']
if (langSubTagsToValidate.length > 1) {
return false;
}

// ensure that the lang subtag value is valid
const isValid = axe.utils.validLangs().includes(langSubTagsToValidate[0]);

// return
return isValid;
return primaryLangValue === primaryXmlLangValue;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This could be the cleanest looking check we've got. Great job Jey.

6 changes: 3 additions & 3 deletions lib/checks/language/xml-lang-mismatch.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@
"metadata": {
"impact": "moderate",
"messages": {
"pass": "Value of lang or xml:lang attribute match if both are specified and included in the list of valid languages",
"fail": "Value of lang or xml:lang attribute either does not match or not included in the list of valid languages"
"pass": "Lang and xml:lang attributes have the same base language",
"fail": "Lang and xml:lang attributes do not have the same base language"
}
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
/**
* Convenience function to extract primary language subtag from a given value
* @method getBaseLang
* @memberof axe.utils
* @memberof axe.commons.utils
* @instance
* @param {String} value value specified as lang or xml:lang attribute
* @return {String}
Expand Down
24 changes: 10 additions & 14 deletions lib/rules/xml-lang-mismatch-matches.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,19 +2,15 @@
// The rule checks that for the html element
// There is no mismatch between the primary language in non-empty lang and xml:lang attributes, if BOTH are used.

// filter only html nodes
const isHtmlNode = node.nodeName.toUpperCase() === 'HTML';
// using -> "selector": "html[lang][xml\\:lang]" to narrow down html with lang and xml:lang attributes

// filter node attributes that are either lang or xml:lang and has a value
const langAndXmlLangAttrs = Array.from(node.attributes).filter(attr => {
const attrName = attr.nodeName.toLowerCase();
const attrValue = attr.nodeValue;
if (!attrValue) {
return false;
}
return ['lang', 'xml:lang'].includes(attrName);
});
// get primary base language for each of the attributes
const { getBaseLang } = axe.commons.utils;
const primaryLangValue = getBaseLang(node.getAttribute('lang') || '');
const primaryXmlLangValue = getBaseLang(node.getAttribute('xml:lang') || '');

// return
// ensure node is HTML and has both lang and xml:lang specified (length of 2 for )
return isHtmlNode && langAndXmlLangAttrs.length === 2;
// ensure that the value specified is valid lang
return (
axe.utils.validLangs().includes(primaryLangValue) &&
axe.utils.validLangs().includes(primaryXmlLangValue)
);
5 changes: 3 additions & 2 deletions lib/rules/xml-lang-mismatch.json
Original file line number Diff line number Diff line change
@@ -1,14 +1,15 @@
{
"id": "xml-lang-mismatch",
"selector": "html[lang][xml\\:lang]",
"matches": "xml-lang-mismatch-matches.js",
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could simplify the matches by offloading some of the work to the selector: "selector": "html[lang][xml:lang]"

"tags": [
"cat.language",
"wcag2a",
"wcag311"
],
"metadata": {
"description": "Ensures that for the html element, there is no mismatch between the primary language in non-empty lang and xml:lang attributes, if both are used",
"help": "The value of the primary language subtag for the lang and xml:lang attributes should be the same for the html element"
"description": "Ensure that HTML elements with both valid lang and xml:lang attributes agree on the base language of the page",
"help": "HTML elements with lang and xml:lang must have the same base language"
},
"all": [
"xml-lang-mismatch"
Expand Down