diff options
| author | Shipwreckt <me@shipwreckt.co.uk> | 2025-10-31 20:02:14 +0000 |
|---|---|---|
| committer | Shipwreckt <me@shipwreckt.co.uk> | 2025-10-31 20:02:14 +0000 |
| commit | 7a52ddeba2a68388b544f529d2d92104420f77b0 (patch) | |
| tree | 15ddd47457a2cb4a96060747437d36474e4f6b4e /node_modules/bcp-47-match | |
| parent | 53d6ae2b5568437afa5e4995580a3fb679b7b91b (diff) | |
Changed from static to 11ty!
Diffstat (limited to 'node_modules/bcp-47-match')
| -rw-r--r-- | node_modules/bcp-47-match/index.d.ts | 47 | ||||
| -rw-r--r-- | node_modules/bcp-47-match/index.js | 234 | ||||
| -rw-r--r-- | node_modules/bcp-47-match/license | 22 | ||||
| -rw-r--r-- | node_modules/bcp-47-match/package.json | 88 | ||||
| -rw-r--r-- | node_modules/bcp-47-match/readme.md | 315 |
5 files changed, 706 insertions, 0 deletions
diff --git a/node_modules/bcp-47-match/index.d.ts b/node_modules/bcp-47-match/index.d.ts new file mode 100644 index 0000000..a3dd6b5 --- /dev/null +++ b/node_modules/bcp-47-match/index.d.ts @@ -0,0 +1,47 @@ +export function basicFilter( + tags: string | Tags, + ranges: string | Ranges | undefined +): Tags +export function extendedFilter( + tags: string | Tags, + ranges: string | Ranges | undefined +): Tags +export function lookup( + tags: string | Tags, + ranges: string | Ranges | undefined +): string | undefined +/** + * BCP-47 tag. + */ +export type Tag = string +/** + * List of BCP-47 tags. + */ +export type Tags = Array<Tag> +/** + * RFC 4647 range. + */ +export type Range = string +/** + * List of RFC 4647 range. + */ +export type Ranges = Array<Range> +/** + * An internal check. + */ +export type Check = (tag: Tag, range: Range) => boolean +/** + * Filter: yields all tags that match a range. + */ +export type Filter = FilterOrLookup<true> +/** + * Lookup: yields the best tag that matches a range. + */ +export type Lookup = FilterOrLookup<false> +/** + * A check. + */ +export type FilterOrLookup<IsFilter extends boolean> = ( + tags: Tag | Tags, + ranges?: Range | Ranges | undefined +) => IsFilter extends true ? Tags : Tag | undefined diff --git a/node_modules/bcp-47-match/index.js b/node_modules/bcp-47-match/index.js new file mode 100644 index 0000000..6d12bfc --- /dev/null +++ b/node_modules/bcp-47-match/index.js @@ -0,0 +1,234 @@ +/** + * See <https://tools.ietf.org/html/rfc4647#section-3.1> + * for more info on the algorithms. + */ + +/** + * @typedef {string} Tag + * BCP-47 tag. + * @typedef {Array<Tag>} Tags + * List of BCP-47 tags. + * @typedef {string} Range + * RFC 4647 range. + * @typedef {Array<Range>} Ranges + * List of RFC 4647 range. + * + * @callback Check + * An internal check. + * @param {Tag} tag + * BCP-47 tag. + * @param {Range} range + * RFC 4647 range. + * @returns {boolean} + * Whether the range matches the tag. + * + * @typedef {FilterOrLookup<true>} Filter + * Filter: yields all tags that match a range. + * @typedef {FilterOrLookup<false>} Lookup + * Lookup: yields the best tag that matches a range. + */ + +/** + * @template {boolean} IsFilter + * Whether to filter or perform a lookup. + * @callback FilterOrLookup + * A check. + * @param {Tag|Tags} tags + * One or more BCP-47 tags. + * @param {Range|Ranges|undefined} [ranges='*'] + * One or more RFC 4647 ranges. + * @returns {IsFilter extends true ? Tags : Tag|undefined} + * Result. + */ + +/** + * Factory to perform a filter or a lookup. + * + * This factory creates a function that accepts a list of tags and a list of + * ranges, and contains logic to exit early for lookups. + * `check` just has to deal with one tag and one range. + * This match function iterates over ranges, and for each range, + * iterates over tags. + * That way, earlier ranges matching any tag have precedence over later ranges. + * + * @template {boolean} IsFilter + * @param {Check} check + * A check. + * @param {IsFilter} filter + * Whether to filter or perform a lookup. + * @returns {FilterOrLookup<IsFilter>} + * Filter or lookup. + */ +function factory(check, filter) { + /** + * @param {Tag|Tags} tags + * One or more BCP-47 tags. + * @param {Range|Ranges|undefined} [ranges='*'] + * One or more RFC 4647 ranges. + * @returns {IsFilter extends true ? Tags : Tag|undefined} + * Result. + */ + return function (tags, ranges) { + let left = cast(tags, 'tag') + const right = cast( + ranges === null || ranges === undefined ? '*' : ranges, + 'range' + ) + /** @type {Tags} */ + const matches = [] + let rightIndex = -1 + + while (++rightIndex < right.length) { + const range = right[rightIndex].toLowerCase() + + // Ignore wildcards in lookup mode. + if (!filter && range === '*') continue + + let leftIndex = -1 + /** @type {Tags} */ + const next = [] + + while (++leftIndex < left.length) { + if (check(left[leftIndex].toLowerCase(), range)) { + // Exit if this is a lookup and we have a match. + if (!filter) { + return /** @type {IsFilter extends true ? Tags : Tag|undefined} */ ( + left[leftIndex] + ) + } + + matches.push(left[leftIndex]) + } else { + next.push(left[leftIndex]) + } + } + + left = next + } + + // If this is a filter, return the list. If it’s a lookup, we didn’t find + // a match, so return `undefined`. + return /** @type {IsFilter extends true ? Tags : Tag|undefined} */ ( + filter ? matches : undefined + ) + } +} + +/** + * Basic Filtering (Section 3.3.1) matches a language priority list consisting + * of basic language ranges (Section 2.1) to sets of language tags. + * + * @param {Tag|Tags} tags + * One or more BCP-47 tags. + * @param {Range|Ranges|undefined} [ranges='*'] + * One or more RFC 4647 ranges. + * @returns {Tags} + * List of BCP-47 tags. + */ +export const basicFilter = factory(function (tag, range) { + return range === '*' || tag === range || tag.includes(range + '-') +}, true) + +/** + * Extended Filtering (Section 3.3.2) matches a language priority list + * consisting of extended language ranges (Section 2.2) to sets of language + * tags. + * + * @param {Tag|Tags} tags + * One or more BCP-47 tags. + * @param {Range|Ranges|undefined} [ranges='*'] + * One or more RFC 4647 ranges. + * @returns {Tags} + * List of BCP-47 tags. + */ +export const extendedFilter = factory(function (tag, range) { + // 3.3.2.1 + const left = tag.split('-') + const right = range.split('-') + let leftIndex = 0 + let rightIndex = 0 + + // 3.3.2.2 + if (right[rightIndex] !== '*' && left[leftIndex] !== right[rightIndex]) { + return false + } + + leftIndex++ + rightIndex++ + + // 3.3.2.3 + while (rightIndex < right.length) { + // 3.3.2.3.A + if (right[rightIndex] === '*') { + rightIndex++ + continue + } + + // 3.3.2.3.B + if (!left[leftIndex]) return false + + // 3.3.2.3.C + if (left[leftIndex] === right[rightIndex]) { + leftIndex++ + rightIndex++ + continue + } + + // 3.3.2.3.D + if (left[leftIndex].length === 1) return false + + // 3.3.2.3.E + leftIndex++ + } + + // 3.3.2.4 + return true +}, true) + +/** + * Lookup (Section 3.4) matches a language priority list consisting of basic + * language ranges to sets of language tags to find the one exact language tag + * that best matches the range. + * + * @param {Tag|Tags} tags + * One or more BCP-47 tags. + * @param {Range|Ranges|undefined} [ranges='*'] + * One or more RFC 4647 ranges. + * @returns {Tag|undefined} + * BCP-47 tag. + */ +export const lookup = factory(function (tag, range) { + let right = range + + /* eslint-disable-next-line no-constant-condition */ + while (true) { + if (right === '*' || tag === right) return true + + let index = right.lastIndexOf('-') + + if (index < 0) return false + + if (right.charAt(index - 2) === '-') index -= 2 + + right = right.slice(0, index) + } +}, false) + +/** + * Validate tags or ranges, and cast them to arrays. + * + * @param {string|Array<string>} values + * @param {string} name + * @returns {Array<string>} + */ +function cast(values, name) { + const value = values && typeof values === 'string' ? [values] : values + + if (!value || typeof value !== 'object' || !('length' in value)) { + throw new Error( + 'Invalid ' + name + ' `' + value + '`, expected non-empty string' + ) + } + + return value +} diff --git a/node_modules/bcp-47-match/license b/node_modules/bcp-47-match/license new file mode 100644 index 0000000..7f0be77 --- /dev/null +++ b/node_modules/bcp-47-match/license @@ -0,0 +1,22 @@ +(The MIT License) + +Copyright (c) 2018 Titus Wormer <tituswormer@gmail.com> + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +'Software'), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/node_modules/bcp-47-match/package.json b/node_modules/bcp-47-match/package.json new file mode 100644 index 0000000..aeac783 --- /dev/null +++ b/node_modules/bcp-47-match/package.json @@ -0,0 +1,88 @@ +{ + "name": "bcp-47-match", + "version": "2.0.3", + "description": "Match BCP 47 language tags with language ranges per RFC 4647", + "license": "MIT", + "keywords": [ + "bcp", + "47", + "bcp47", + "bcp-47", + "language", + "tag", + "match", + "matching", + "check", + "rfc", + "4647" + ], + "repository": "wooorm/bcp-47-match", + "bugs": "https://github.com/wooorm/bcp-47-match/issues", + "funding": { + "type": "github", + "url": "https://github.com/sponsors/wooorm" + }, + "author": "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)", + "contributors": [ + "Titus Wormer <tituswormer@gmail.com> (https://wooorm.com)" + ], + "sideEffects": false, + "type": "module", + "main": "index.js", + "types": "index.d.ts", + "files": [ + "index.d.ts", + "index.js" + ], + "devDependencies": { + "@types/node": "^18.0.0", + "c8": "^7.0.0", + "chalk": "^5.0.0", + "prettier": "^2.0.0", + "remark-cli": "^11.0.0", + "remark-preset-wooorm": "^9.0.0", + "type-coverage": "^2.0.0", + "typescript": "^4.0.0", + "xo": "^0.52.0" + }, + "scripts": { + "prepack": "npm run build && npm run format", + "build": "tsc --build --clean && tsc --build && type-coverage", + "format": "remark . -qfo && prettier . -w --loglevel warn && xo --fix", + "test-api": "node --conditions development test.js", + "test-coverage": "c8 --check-coverage --100 --reporter lcov npm run test-api", + "test": "npm run build && npm run format && npm run test-coverage" + }, + "prettier": { + "tabWidth": 2, + "useTabs": false, + "singleQuote": true, + "bracketSpacing": false, + "semi": false, + "trailingComma": "none" + }, + "xo": { + "prettier": true + }, + "remarkConfig": { + "plugins": [ + "preset-wooorm", + [ + "gfm", + { + "tablePipeAlign": false + } + ], + [ + "lint-table-pipe-alignment", + false + ] + ] + }, + "typeCoverage": { + "atLeast": 100, + "detail": true, + "strict": true, + "ignoreCatch": true + } +} diff --git a/node_modules/bcp-47-match/readme.md b/node_modules/bcp-47-match/readme.md new file mode 100644 index 0000000..c07c286 --- /dev/null +++ b/node_modules/bcp-47-match/readme.md @@ -0,0 +1,315 @@ +<!--lint disable no-html--> + +# bcp-47-match + +[![Build][build-badge]][build] +[![Coverage][coverage-badge]][coverage] +[![Downloads][downloads-badge]][downloads] +[![Size][size-badge]][size] + +Match BCP 47 language tags with language ranges per RFC 4647. + +## Contents + +* [What is this?](#what-is-this) +* [When should I use this?](#when-should-i-use-this) +* [Install](#install) +* [Use](#use) +* [API](#api) + * [`basicFilter(tags[, ranges='*'])`](#basicfiltertags-ranges) + * [`extendedFilter(tags[, ranges='*'])`](#extendedfiltertags-ranges) + * [`lookup(tags, ranges)`](#lookuptags-ranges) +* [Types](#types) +* [Compatibility](#compatibility) +* [Security](#security) +* [Related](#related) +* [Contribute](#contribute) +* [License](#license) + +## What is this? + +This package can match [BCP 47][spec] language tags with “language ranges” per +[RFC 4647][match]. +This is done by the `:lang()` pseudo class in CSS, the `Accept-Language` HTTP +header, and a few other places. + +## When should I use this? + +You can use this package if you want to choose a certain document based on +language tags. + +## Install + +This package is [ESM only][esm]. +In Node.js (version 14.14+, 16.0+), install with [npm][]: + +```sh +npm install bcp-47-match +``` + +In Deno with [`esm.sh`][esmsh]: + +```js +import * as bcp47Match from 'https://esm.sh/bcp-47-match@2' +``` + +In browsers with [`esm.sh`][esmsh]: + +```html +<script type="module"> + import * as bcp47Match from 'https://esm.sh/bcp-47-match@2?bundle' +</script> +``` + +## Use + +```js +import {basicFilter, extendedFilter, lookup} from 'bcp-47-match' + +const tags = ['en-GB', 'de-CH', 'en', 'de'] + +console.log(basicFilter(tags, '*')) // => [ 'en-GB', 'de-CH', 'en', 'de' ] +console.log(basicFilter(tags, 'en')) // => [ 'en-GB', 'en' ] +console.log(basicFilter(tags, 'en-GB')) // => [ 'en-GB' ] +console.log(basicFilter(tags, ['en-GB', 'en'])) // => [ 'en-GB', 'en' ] +console.log(basicFilter(tags, 'jp')) // => [] + +console.log(extendedFilter(tags, '*')) // => [ 'en-GB', 'de-CH', 'en', 'de' ] +console.log(extendedFilter(tags, 'en')) // => [ 'en-GB', 'en' ] +console.log(extendedFilter(tags, 'en-GB')) // => [ 'en-GB' ] +console.log(extendedFilter(tags, '*-GB')) // => [ 'en-GB' ] +console.log(extendedFilter(tags, ['en-GB', 'en'])) // => [ 'en-GB', 'en' ] +console.log(extendedFilter(tags, 'jp')) // => [] + +console.log(lookup(tags, 'en')) // => 'en' +console.log(lookup(tags, 'en-GB')) // => 'en-GB' +console.log(lookup(tags, ['en-GB', 'en'])) // => 'en-GB' +console.log(lookup(tags, ['en', 'en-GB'])) // => 'en' +console.log(lookup(tags, 'jp')) // => undefined +``` + +## API + +This package exports the identifier `basicFilter`, `extendedFilter`, and +`lookup`. +There is no default export. + +### `basicFilter(tags[, ranges='*'])` + +> 👉 **Note**: See +> [Basic Filtering spec](https://tools.ietf.org/html/rfc4647#section-3.3.1) + +Match language tags to a list of simple ranges. +Searches for matches between the first range and all tags, and continues +with further ranges. +Returns a list of matching tags in the order they matched. + +<details><summary>View matching table</summary> + +| Basic Filter | `*` | `de` | `de-CH` | `de-DE` | `de-*-DE` | `*-CH` | +| - | - | - | - | - | - | - | +| `de` | ✔︎ | ✔︎ | | | | | +| `de-CH` | ✔︎ | ✔︎ | ✔︎ | | | | +| `de-CH-1996` | ✔︎ | ✔︎ | ✔︎ | | | | +| `de-DE` | ✔︎ | ✔︎ | | ✔︎ | | | +| `de-DE-1996` | ✔︎ | ✔︎ | | ✔︎ | | | +| `de-DE-x-goethe` | ✔︎ | ✔︎ | | ✔︎ | | | +| `de-Deva` | ✔︎ | ✔︎ | | | | | +| `de-Deva-DE` | ✔︎ | ✔︎ | | | | | +| `de-Latf-DE` | ✔︎ | ✔︎ | | | | | +| `de-Latn-DE` | ✔︎ | ✔︎ | | | | | +| `de-Latn-DE-1996` | ✔︎ | ✔︎ | | | | | +| `de-x-DE` | ✔︎ | ✔︎ | | | | | +| `en` | ✔︎ | | | | | | +| `en-GB` | ✔︎ | | | | | | +| `zh` | ✔︎ | | | | | | +| `zh-Hans` | ✔︎ | | | | | | +| `zh-Hant` | ✔︎ | | | | | | + +</details> + +###### Parameters + +* `tags` (`string` or `Array<string>`) + — list of BCP 47 tags +* `ranges` (`string` or `Array<string>`, default: `'*'`) + — list of RFC 4647 + [basic ranges][basic-range] + (aka, matching `/^(\*|[a-z]{1,8}(-[a-z0-9]{1,8})*)$/i`) + +###### Returns + +Possibly empty list of matching tags in the order they matched +(`Array<string>`). + +### `extendedFilter(tags[, ranges='*'])` + +> 👉 **Note**: See +> [Extended Filtering spec](https://tools.ietf.org/html/rfc4647#section-3.3.2) + +Match language tags to a list of extended ranges. +Searches for matches between the first range and all tags, and continues +with further ranges. + +<details><summary>View matching table</summary> + +| Extended Filter | `*` | `de` | `de-CH` | `de-DE` | `de-*-DE` | `*-CH` | +| - | - | - | - | - | - | - | +| `de` | ✔︎ | ✔︎ | | | | | +| `de-CH` | ✔︎ | ✔︎ | ✔︎ | | | ✔︎ | +| `de-CH-1996` | ✔︎ | ✔︎ | ✔︎ | | | ✔︎ | +| `de-DE` | ✔︎ | ✔︎ | | ✔︎ | ✔︎ | | +| `de-DE-1996` | ✔︎ | ✔︎ | | ✔︎ | ✔︎ | | +| `de-DE-x-goethe` | ✔︎ | ✔︎ | | ✔︎ | ✔︎ | | +| `de-Deva` | ✔︎ | ✔︎ | | | | | +| `de-Deva-DE` | ✔︎ | ✔︎ | | ✔︎ | ✔︎ | | +| `de-Latf-DE` | ✔︎ | ✔︎ | | ✔︎ | ✔︎ | | +| `de-Latn-DE` | ✔︎ | ✔︎ | | ✔︎ | ✔︎ | | +| `de-Latn-DE-1996` | ✔︎ | ✔︎ | | ✔︎ | ✔︎ | | +| `de-x-DE` | ✔︎ | ✔︎ | | | | | +| `en` | ✔︎ | | | | | | +| `en-GB` | ✔︎ | | | | | | +| `zh` | ✔︎ | | | | | | +| `zh-Hans` | ✔︎ | | | | | | +| `zh-Hant` | ✔︎ | | | | | | + +</details> + +###### Parameters + +* `tags` (`string` or `Array<string>`) + — list of BCP 47 tags +* `ranges` (`string` or `Array<string>`, default: `'*'`) + — list of RFC 4647 [extended ranges][extended-range] + (aka, matching `/^(\*|[a-z]{1,8})(-(\*|[a-z0-9]{1,8}))*$/i`) + +###### Returns + +Possibly empty list of matching tags in the order they matched +(`Array<string>`). + +### `lookup(tags, ranges)` + +> 👉 **Note**: See +> [Lookup spec](https://tools.ietf.org/html/rfc4647#section-3.4) + +Find the best language tag that matches a list of ranges. +Searches for a match between the first range and all tags, and continues +with further ranges. +Returns the first match, if any. + +<details><summary>View matching table</summary> + +| Lookup | `*` | `de` | `de-CH` | `de-DE` | `de-*-DE` | `*-CH` | +| - | - | - | - | - | - | - | +| `de` | | ✔︎︎ | ✔︎︎ | ✔︎ | ✔︎ | ✔︎ | +| `de-CH` | | | ✔︎ | | | ✔︎ | +| `de-CH-1996` | | | | | | ✔︎ | +| `de-DE` | | | | ✔︎ | | ✔︎ | +| `de-DE-1996` | | | | | | ✔︎ | +| `de-DE-x-goethe` | | | | | | ✔︎ | +| `de-Deva` | | | | | | ✔︎ | +| `de-Deva-DE` | | | | | | ✔︎ | +| `de-Latf-DE` | | | | | | ✔︎ | +| `de-Latn-DE` | | | | | | ✔︎ | +| `de-Latn-DE-1996` | | | | | | ✔︎ | +| `de-x-DE` | | | | | | ✔︎ | +| `en` | | | | | | ✔︎ | +| `en-GB` | | | | | | ✔︎ | +| `zh` | | | | | | ✔︎ | +| `zh-Hans` | | | | | | ✔︎ | +| `zh-Hant` | | | | | | ✔︎ | + +</details> + +###### Parameters + +* `tags` (`string` or `Array<string>`) + — list of BCP 47 tags +* `ranges` (`string` or `Array<string>`) + — list of RFC 4647 basic ranges (but `*` is ignored) + +###### Returns + +First matching tag in `tags`, `undefined` otherwise (`string?`). + +## Types + +This package is fully typed with [TypeScript][]. +It exports no additional types. + +## Compatibility + +This package is at least compatible with all maintained versions of Node.js. +As of now, that is Node.js 14.14+ and 16.0+. +It also works in Deno and modern browsers. + +## Security + +This package is safe. + +## Related + +* [`wooorm/bcp-47`](https://github.com/wooorm/bcp-47) + — parse and serialize BCP 47 language tags +* [`wooorm/bcp-47-normalize`](https://github.com/wooorm/bcp-47-normalize) + — normalize, canonicalize, and format BCP 47 tags +* [`wooorm/iso-3166`](https://github.com/wooorm/iso-3166) + — ISO 3166 codes +* [`wooorm/iso-639-2`](https://github.com/wooorm/iso-639-2) + — ISO 639-2 codes +* [`wooorm/iso-639-3`](https://github.com/wooorm/iso-639-3) + — ISO 639-3 codes +* [`wooorm/iso-15924`](https://github.com/wooorm/iso-15924) + — ISO 15924 codes +* [`wooorm/un-m49`](https://github.com/wooorm/un-m49) + — UN M49 codes + +## Contribute + +Yes please! +See [How to Contribute to Open Source][contribute]. + +## License + +[MIT][license] © [Titus Wormer][author] + +<!-- Definitions --> + +[build-badge]: https://github.com/wooorm/bcp-47-match/workflows/main/badge.svg + +[build]: https://github.com/wooorm/bcp-47-match/actions + +[coverage-badge]: https://img.shields.io/codecov/c/github/wooorm/bcp-47-match.svg + +[coverage]: https://codecov.io/github/wooorm/bcp-47-match + +[downloads-badge]: https://img.shields.io/npm/dm/bcp-47-match.svg + +[downloads]: https://www.npmjs.com/package/bcp-47-match + +[size-badge]: https://img.shields.io/bundlephobia/minzip/bcp-47-match.svg + +[size]: https://bundlephobia.com/result?p=bcp-47-match + +[npm]: https://docs.npmjs.com/cli/install + +[esmsh]: https://esm.sh + +[license]: license + +[author]: https://wooorm.com + +[esm]: https://gist.github.com/sindresorhus/a39789f98801d908bbc7ff3ecc99d99c + +[typescript]: https://www.typescriptlang.org + +[contribute]: https://opensource.guide/how-to-contribute/ + +[spec]: https://tools.ietf.org/html/bcp47 + +[match]: https://tools.ietf.org/html/rfc4647 + +[basic-range]: https://tools.ietf.org/html/rfc4647#section-2.1 + +[extended-range]: https://tools.ietf.org/html/rfc4647#section-2.2 |
