32

The CSS language pseudo-class to allow us specify different styles for different languages, like so:

html:lang(en) .foo { ... }

However, this doesn't work in IE7, so I've been using an attribute selector:

html[lang="en"] .foo { ... }

They seem to do the same thing, but are there any subtle differences? And if not, why does CSS even have a language pseudo-class, when the attribute selector does the exact same thing?

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
john
  • 903
  • 3
  • 9
  • 9

6 Answers6

42

In HTML, both the :lang() pseudo-class and the attribute selector will match an element with the corresponding lang attribute.

The difference is that a browser may have other ways of determining the language of a given element when testing against the :lang() pseudo-class which may be defined by the document language and/or the implementation, whereas an attribute selector will only check an element for that given attribute, without any accompanying document-based semantics.

For example, in HTML, the pseudo-class will also match any of the element's descendants for which there isn't a different lang, depending on how a browser determines the language for those descendants. Usually, the descendants will inherit the language attribute from their ancestor if it is not explicitly set.

Here's what the spec says:

The difference between :lang(C) and the ‘|=’ operator is that the ‘|=’ operator only performs a comparison against a given attribute on the element, while the :lang(C) pseudo-class uses the UAs knowledge of the document's semantics to perform the comparison.

In this HTML example, only the BODY matches [lang|=fr] (because it has a LANG attribute) but both the BODY and the P match :lang(fr) (because both are in French). The P does not match the [lang|=fr] because it does not have a LANG attribute.

<body lang=fr>
  <p>Je suis français.</p>
</body>

Notice the specific phrasings of "has a LANG attribute" and "are in French". These two phrases have very different meanings in English, as you might imagine.

In your example, the following selector will also match your .foo element:

.foo:lang(en)

But the following selectors won't, if it doesn't have its own lang attribute set:

.foo[lang="en"]
.foo[lang|="en"]

As for browser support, the :lang() pseudo-class is supported starting from IE8, so IE7 really is the only browser you will be unable to support by using the pseudo-class over the attribute selector.

Based on this understanding you can then answer the question "which should I use": you should always use the :lang() pseudo-class by default, unless certain quirks (or the need to support IE7) require working around by using an attribute selector instead.


Selectors 4 not only brings enhanced functionality to the :lang() pseudo-class (thereby widening the gap in functionality between it and attribute selectors), but also introduces the :dir() pseudo-class for matching elements based on their directionality. Because directionality is a language-related property, the dir and lang attributes work similarly in HTML, and the difference between :dir() and its corresponding attribute selector is analogous to that between :lang() and its corresponding attribute selector — to the point where the first sentence of the following quotation is in fact a word-for-word copy of the same paragraph in the section describing :lang():

The difference between :dir(C) and ''[dir=C]'' is that ''[dir=C]'' only performs a comparison against a given attribute on the element, while the :dir(C) pseudo-class uses the UAs knowledge of the document’s semantics to perform the comparison. For example, in HTML, the directionality of an element inherits so that a child without a dir attribute will have the same directionality as its closest ancestor with a valid dir attribute. As another example, in HTML, an element that matches ''[dir=auto]'' will match either :dir(ltr) or :dir(rtl) depending on the resolved directionality of the elements as determined by its contents. [HTML5]

BoltClock
  • 700,868
  • 160
  • 1,392
  • 1,356
  • Is the `xml:lang` attribute invisible to web browser (since a web browser is an HTML parser and not an XML parser)? I've been trying to use the selectors listed in [this w3 article](http://www.w3.org/International/questions/qa-css-lang) via user CSS but they don't work on [this webpage](http://www7a.biglobe.ne.jp/~nifongo/av/t23_1.htm). The `` element on that Japanese webpage only specifies `xml:lang` attribute and not the `lang` attribute. – 3 to 5 business days Aug 15 '14 at 22:32
  • 2
    @3to5businessdays: Yes, although I should point out that modern browsers actually have both HTML and XML parsers - which one is used is determined by the content-type given by the server. Regardless, if the `` element only contains an `xml:lang` attribute and not a `lang` attribute, then it is invalid. This is because, in HTML, only the `lang` attribute applies, and that is the reason why the `:lang()` pseudo isn't matching. – BoltClock Aug 15 '14 at 23:23
  • I accidentally down-voted, could you please make a trivial change so I can remove the vote ? – Florian Jun 06 '17 at 06:02
  • @Florian: Done. – BoltClock Jun 06 '17 at 07:45
  • TL;DR: "you should always use the :lang() pseudo-class by default" – Prid Apr 13 '23 at 16:26
6

One more thing that wasn't mentioned by anybody else - :lang() pseudoclass isn't interested in how one sets the element's language. In XHTML document (true XHTML with XML MIME type), you can use xml:lang="en" and the element will match :lang(en) but not [lang="en"].

duri
  • 14,991
  • 3
  • 44
  • 49
4

According to the spec,

If the document language specifies how the human language of an element is determined, it is possible to write selectors in CSS that match an element based on its language. For example, in HTML [HTML4], the language is determined by a combination of the "lang" attribute, the META element, and possibly by information from the protocol (such as HTTP headers). XML uses an attribute called xml:lang, and there may be other document language-specific methods for determining the language.

The pseudo-class ':lang(C)' matches if the element is in language C. Whether there is a match is based solely on the identifier C being either equal to, or a hyphen-separated substring of, the element's language value, in the same way as if performed by the '|=' operator. The matching of C against the element's language value is performed case-insensitively for characters within the ASCII range. The identifier C does not have to be a valid language name.

That is:

  1. It works for the many other ways of specifying a language besides the simple lang attribute.
    • Most importantly, as detailed in @BoltClock's answer, it will use the language specified (in whatever manner) on a container element, not just on the element itself, since language is inherited by child elements.
  2. It uses |= semantics, i.e. :lang(en) and :lang(us) will both match span[lang=en-us].
  3. It is guaranteed to be case-insensitive, whereas

The case-sensitivity of attribute names and values in selectors depends on the document language.

Community
  • 1
  • 1
Domenic
  • 110,262
  • 41
  • 219
  • 271
0

TL;DR

You should always use the :lang() pseudo-class by default

Browser Support for :lang(): from IE8 and up


General Difference:

Setup:

<html lang="en">
  <body>
    <p>Text</p>
  </body>
</html>

[lang=en]:

p[lang="en"]

❌ This will NOT target the p tag unless it's redefined as <p lang="en">Text</p>!


:lang(en):

p:lang(en)

✅ This WILL target the p tag since <html> has lang="en" set.

Prid
  • 1,272
  • 16
  • 20
0

:lang(en) pseudo class selector effect in descendant elements but [lang|=en] attr selector not.

Think this sample. lets assume that <html> element has lang="en" attr.

<div>
  div element
  <p lang="fr"><p/&gt elementi</p>
</div>
div:lang(en) {
  // <!--
  //  <div> element will be inherit styles in ancestor element (In here <html> element)
  // -->
  color: orange;
}

div[lang|="en"] {
  // <!--
  // This rule set not work because not defiend lang attr in <div> element and also not inherited styles on ancestor (In here <html>) element.
  // -->
  color: red;
}
0

Css supports attribtue selectors for all elements, not just for the html tags lang attribute. For example html like <a title="Jeeha" href="foo.html">bar</a> could be selected like a[title=Jeeha] { ... } in css.

See this link for more details on partial matches and variants.

kontur
  • 4,934
  • 2
  • 36
  • 62