Skip to content

[selectors-5] Clarify :lang() behavior when the language range is not a well-formed BCP 47 code #8720

Open

Description

@jfkthame

According to https://www.w3.org/TR/selectors-4/#the-lang-pseudo,

An element’s content language matches a language range if, when represented in BCP 47 syntax [BCP47], it matches that language range in an extended filtering operation per [RFC4647] Matching of Language Tags (section 3.3.2).

The text also goes on to mention that

The language range does not need to be a valid language code to perform this comparison.

[my emphasis] which implies, as I understand it, that something like :lang("qq") will match content tagged with lang="qq" even though qq is not a valid language tag (as listed in the IANA registry).

However, the Selectors spec does not specifically address how ill-formed (not merely invalid) tags should be handled.

According to the language tag syntax given in https://www.rfc-editor.org/rfc/rfc5646#section-2.1, a tag like åå (containing non-ASCII characters) would be ill-formed ("the language tags described in this document are sequences of characters from the US-ASCII [ISO646] repertoire"), as would a tag like en--- (the various subtags following the primary language subtag are optional, but the grammar does not allow for them to be empty; if they're not present, the corresponding hyphen delimiters should also be omitted).

So how does :lang() matching work in the presence of ill-formed codes? It seems to me that a literal reading of the spec requires that such codes never match, because its definition of "matches" depends on "when represented in BCP 47 syntax", and such ill-formed codes cannot be represented in BCP 47 at all; they conflict with its basic grammar.

A possible alternative interpretation might be that the handling of ill-formed codes is simply undefined (because the spec only addresses what it means to "match" for codes "represented in BCP 47 syntax".

I'm not aware of any compelling use case for ill-formed language codes. So in the interests of clarity and interoperability I would like to ask the WG to confirm (and explicitly note in the spec) that :lang() matching is based strictly on BCP 47 and RFC4647, and as such, ill-formed codes never match.

(Note that the current implementation in WebKit does allow ill-formed tags to match. Thus if content is tagged with lang="SomeRandomCode-Latn-US", which is ill-formed because the primary language subtag is too long, it is nevertheless matched by :lang(SomeRandomCode), :lang("*-US"), etc. I think this should be considered a bug in the implementation.)

Activity

  1. changed the title [-][css-selectors-4] Clarify :lang() behavior when the language range is not a well-formed BCP 47 code[/-] [+][selectors-4] Clarify :lang() behavior when the language range is not a well-formed BCP 47 code[/+] on Apr 14, 2023
  2. added
    i18n-trackerGroup bringing to attention of Internationalization, or tracked by i18n but not needing response.
    on Apr 14, 2023
  3. tabatkins commented on Apr 14, 2023

    @tabatkins
    Member

    Right, this is undefined in the spec right now. If the :lang() argument can't be interpreted in BCP-47 syntax, it should definitely just fail to match straight away.

  4. zcorpan commented on Apr 19, 2023

    @zcorpan
    Contributor

    @tabatkins what if the :lang() argument is well-formed language range but the language tag for an element isn't BCT-47 syntax? e.g. lang="SomeRandomCode-Latn-US"

  5. jfkthame commented on Apr 19, 2023

    @jfkthame
    ContributorAuthor

    lang="SomeRandomCode-Latn-US"

    That tag cannot be parsed according to BCP47 syntax, and therefore we have no basis to assume anything about any part of it, such as the meaning of the Latn or US substrings in it, or even that the hyphens delimit "subtags" at all.

    Therefore, IMO it should not match any :lang() pseudo, not even :lang("*").

  6. Crissov commented on Apr 19, 2023

    @Crissov
    Contributor

    Twenty years ago, I briefly argued that implementations should be free to use external knowledge about the data format in order map random language information to BCP-47.

    https://lists.w3.org/Archives/Public/www-style/2003Oct/0234.html

  7. svgeesus commented on Mar 10, 2026

    @svgeesus
    Collaborator

    Poking this sleeping issue in the context of

    @tabatkins suggested:

    Right, this is undefined in the spec right now. If the :lang() argument can't be interpreted in BCP-47 syntax, it should definitely just fail to match straight away.

    Proposed async resolution: Selectors 4 should state that any :lang() argument that cannot be parsed with the BCP-47 syntax causes the selector to not match

  8. astearns commented on Mar 16, 2026

    @astearns
    Member

    The CSSWG will automatically accept this resolution one week from now if no objections are raised here. Anyone can add an emoji to this comment to express support. If you do not support this resolution, please add a new comment.

    Proposed Resolution: Any :lang() argument that cannot be parsed with the BCP-47 syntax causes the selector to not match

  9. 47 remaining items

  10. aphillips commented on May 20, 2026

    @aphillips
    Contributor

    @jfkthame mentioned:

    An alternative would be to treat fr-x as an ill-formed range, just like it is an ill-formed tag. To match "any French with a private use sequence", the appropriate range to use would be fr-x-*. This avoids the unintuitive situation where a "valid" range fails to match an apparently-identical tag.

    I agree with you. However... RFC4647 didn't do that. We might impose additional requirements in Selectors, of course, but that would change the range canonicalization operation (the "non-initial wildcards are ignored and thus usually omitted" bits).

  11. svgeesus commented on May 20, 2026

    @svgeesus
    Collaborator

    I will raise this with I18N in our call tomorrow (@jsahleen for visibility). This issue is currently tracked rather than "needs resolution": we might wish to raise its priority, even while agreeing to slip it.

    I agree. Note that Selectors 5 is being actively worked on; Selectors 4 is the "should have been Rec already" one. This is not a "someday pile".

    I think it would be okay to move this to Selectors 5, so long as no one is going to cry back compat about it later.

    I'm hoping that making that sentence informative will avoid that sort of "Selectors 5 contradicts Selectors 4" issue down the line.

  12. Crissov commented on May 21, 2026

    @Crissov
    Contributor

    @jfkthame I don’t think it would be surprising to those few authors who have to deal with singletons in lang in the first place. If I wanted to select all elements that use any Unicode extensions, for instance, I would probably use :lang("*-u") intuitively (or the more verbose :lang("*-u-*")) and I would not want incomplete / truncated attribute values like lang="en-u" to match.

  13. added
    i18n-needs-resolutionIssue the Internationalization Group has raised and looks for a response on.
    and removed
    i18n-trackerGroup bringing to attention of Internationalization, or tracked by i18n but not needing response.
    on May 21, 2026
  14. aphillips commented on May 21, 2026

    @aphillips
    Contributor

    We discussed this in the I18N call today and will come back again next week to cover more detail. However, the consensus was that slipping to selectors-5 was fine.

  15. changed the title [-][selectors-4] Clarify :lang() behavior when the language range is not a well-formed BCP 47 code[/-] [+][selectors-5] Clarify :lang() behavior when the language range is not a well-formed BCP 47 code[/+] on May 22, 2026
  16. added a commit that references this issue on Jun 4, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

Closed Accepted by CSSWG ResolutionChanges backed by a CSSWG resolution.Commenter SatisfiedCommenter has indicated satisfaction with the resolution / edits.Needs Testcase (WPT)i18n-needs-resolutionIssue the Internationalization Group has raised and looks for a response on.selectors-5spec-test-mismatchIssues about tests that seem to assume things that contradict the spec or are not specified at all

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions