From 0a7da06a873b186b5d3c954321f22cdcdb501c5c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20R=C3=B8ed?= Date: Mon, 13 Apr 2026 14:28:40 +0200 Subject: [PATCH 1/2] Fix template-require-lang-attribute: validate every BCP47 subtag Port only validated parts[0] against a small set; upstream validates every hyphen-separated subtag (region/script). Add the missing country-codes table and traverse all subtags. lang="en-XX" is now correctly flagged. --- lib/rules/template-require-lang-attribute.js | 32 +- lib/utils/country-codes.js | 628 ++++++++++++++++++ .../rules/template-require-lang-attribute.js | 18 + 3 files changed, 675 insertions(+), 3 deletions(-) create mode 100644 lib/utils/country-codes.js diff --git a/lib/rules/template-require-lang-attribute.js b/lib/rules/template-require-lang-attribute.js index a2c3b31388..8876b1ffb1 100644 --- a/lib/rules/template-require-lang-attribute.js +++ b/lib/rules/template-require-lang-attribute.js @@ -1,4 +1,9 @@ -// Common valid BCP 47 language tags (not exhaustive, but covers the most common) +const COUNTRY_CODES = require('../utils/country-codes'); + +// Common valid BCP 47 primary language subtags (not exhaustive, but covers the most common). +// Used as a fast-path check for the primary subtag; full validation of every +// hyphen-separated subtag is performed against COUNTRY_CODES (mirroring +// ember-template-lint's upstream behavior). const COMMON_LANG_CODES = new Set([ 'aa', 'ab', @@ -193,9 +198,30 @@ function isValidLangTag(value) { if (!value || !value.trim()) { return false; } - const parts = value.trim().toLowerCase().split('-'); + const normalized = value.trim().toLowerCase(); + const parts = normalized.split('-'); + + // Primary subtag must be a known language code. Keep the original + // COMMON_LANG_CODES fast-path so bare tags like "zh" remain valid even + // when they are not present in the full country-codes table. + if (!COMMON_LANG_CODES.has(parts[0])) { + // Fallback: the whole value may be a registered grandfathered/irregular + // tag (e.g. "zh-hant", "sgn-gb"). + return COUNTRY_CODES.includes(normalized); + } + + // Validate every remaining hyphen-separated subtag (region / script / + // variant) against the full country-codes table, mirroring upstream + // ember-template-lint. A whole-string match against COUNTRY_CODES is + // also accepted to cover multi-part registered tags. + if (parts.length === 1) { + return true; + } - return COMMON_LANG_CODES.has(parts[0]); + return ( + parts.slice(1).every((p) => COUNTRY_CODES.includes(p)) || + COUNTRY_CODES.includes(normalized) + ); } function parseConfig(config) { diff --git a/lib/utils/country-codes.js b/lib/utils/country-codes.js new file mode 100644 index 0000000000..1d4dd90b61 --- /dev/null +++ b/lib/utils/country-codes.js @@ -0,0 +1,628 @@ +const codes = [ + '1994', + 'aas', + 'ac', + 'acn', + 'ad', + 'adlm', + 'adx', + 'aeb', + 'afak', + 'ag', + 'aghb', + 'ahom', + 'ai', + 'al', + 'alalc97', + 'ao', + 'ao1990', + 'aog', + 'apc', + 'aq', + 'arab', + 'aran', + 'armi', + 'armn', + 'art-lojban', + 'at', + 'au', + 'avst', + 'aw', + 'ax', + 'az-arab', + 'az-cyrl', + 'az-latn', + 'baku1926', + 'bali', + 'bamu', + 'bass', + 'batk', + 'bb', + 'bcg', + 'bd', + 'be', + 'be-latn', + 'beng', + 'bf', + 'bfy', + 'bg', + 'bhks', + 'bir', + 'bj', + 'bl', + 'blasl', + 'blis', + 'blo', + 'bmf', + 'bopo', + 'bpp', + 'bq', + 'brah', + 'brai', + 'bs-cyrl', + 'bs-latn', + 'bt', + 'bu', + 'bugi', + 'buhd', + 'bv', + 'bw', + 'by', + 'bz', + 'ca', + 'cakm', + 'cans', + 'cari', + 'cax', + 'cbr', + 'cc', + 'cd', + 'cel-gaulish', + 'cf', + 'cg', + 'cham', + 'cher', + 'chis', + 'chrs', + 'ci', + 'cir', + 'cirt', + 'ck', + 'cl', + 'cm', + 'cmr', + 'cn', + 'copt', + 'cp', + 'cpmn', + 'cprt', + 'cq', + 'crr', + 'cw', + 'cx', + 'cyrl', + 'cyrs', + 'cz', + 'da', + 'dd', + 'de', + 'de-1901', + 'de-1996', + 'de-at-1901', + 'de-at-1996', + 'de-ch-1901', + 'de-ch-1996', + 'de-de-1901', + 'de-de-1996', + 'dev', + 'deva', + 'dg', + 'diak', + 'dif', + 'dj', + 'djk', + 'dk', + 'dm', + 'dmw', + 'do', + 'dogr', + 'drl', + 'dsrt', + 'dtp', + 'dupl', + 'duz', + 'dz', + 'ea', + 'ec', + 'eg', + 'egyd', + 'egyh', + 'egyp', + 'eh', + 'ekavsk', + 'el', + 'elba', + 'elym', + 'ema', + 'en', + 'en-CA', + 'en-boont', + 'en-gb-oed', + 'en-scouse', + 'eo', + 'er', + 'es-419', + 'ethi', + 'eu', + 'ez', + 'fk', + 'fm', + 'fonipa', + 'fonkirsh', + 'fonnapa', + 'fonupa', + 'fonxsamp', + 'fr', + 'frm', + 'fx', + 'gal', + 'gara', + 'gb', + 'gdj', + 'ge', + 'geok', + 'geor', + 'gf', + 'gg', + 'gh', + 'gi', + 'glag', + 'gm', + 'gong', + 'gonm', + 'goth', + 'gp', + 'gq', + 'gr', + 'gran', + 'grclass', + 'grek', + 'grital', + 'grmistr', + 'gs', + 'gt', + 'gu', + 'gujr', + 'gukh', + 'guru', + 'gvr', + 'gw', + 'gy', + 'hanb', + 'hang', + 'hani', + 'hano', + 'hans', + 'hant', + 'hatr', + 'he', + 'hebr', + 'hira', + 'hk', + 'hle', + 'hluw', + 'hm', + 'hmng', + 'hmnp', + 'hn', + 'hrkt', + 'hung', + 'huw', + 'hy', + 'i-ami', + 'i-bnn', + 'i-default', + 'i-enochian', + 'i-hak', + 'i-klingon', + 'i-lux', + 'i-mingo', + 'i-navajo', + 'i-pwn', + 'i-tao', + 'i-tay', + 'i-tsu', + 'iba', + 'ic', + 'id', + 'ijekavsk', + 'il', + 'im', + 'inds', + 'iq', + 'ir', + 'ital', + 'iu-cans', + 'iu-latn', + 'ja-Latn', + 'jal', + 'jamo', + 'java', + 'je', + 'jm', + 'jo', + 'jp', + 'jpan', + 'jurc', + 'jv', + 'kak', + 'kali', + 'kana', + 'kawi', + 'kdz', + 'ke', + 'kea', + 'kh', + 'khar', + 'khk', + 'khmr', + 'khoj', + 'kitl', + 'kits', + 'kjh', + 'kl', + 'kleinsch', + 'kmb', + 'kml', + 'knda', + 'kore', + 'kp', + 'kpel', + 'krai', + 'kru', + 'ksp', + 'kthi', + 'ktz', + 'kw', + 'kwv', + 'kxr', + 'kz', + 'kzk', + 'la', + 'lana', + 'laoo', + 'latf', + 'latg', + 'latn', + 'lc', + 'lcq', + 'leke', + 'lepc', + 'limb', + 'lina', + 'linb', + 'lisu', + 'lk', + 'lld', + 'loma', + 'lr', + 'lrr', + 'ls', + 'ltg', + 'lv', + 'ly', + 'lyci', + 'lydi', + 'ma', + 'mahj', + 'maka', + 'mand', + 'mani', + 'marc', + 'maya', + 'mc', + 'md', + 'me', + 'medf', + 'mend', + 'merc', + 'mero', + 'mf', + 'mlym', + 'mm', + 'mn-cyrl', + 'mn-mong', + 'modi', + 'mom', + 'mong', + 'moon', + 'mp', + 'mq', + 'mroo', + 'mry', + 'mtei', + 'mtm', + 'mu', + 'mult', + 'mv', + 'mw', + 'mx', + 'mymr', + 'mz', + 'nagm', + 'nan-Latn', + 'nand', + 'narb', + 'nbat', + 'nbr', + 'nc', + 'newa', + 'nf', + 'ngt', + 'ngv', + 'ni', + 'nkdb', + 'nkgb', + 'nkoo', + 'nn', + 'no-bok', + 'no-nyn', + 'np', + 'nshu', + 'nt', + 'nu', + 'nun', + 'nyc', + 'nz', + 'oc', + 'ogam', + 'ola', + 'olck', + 'onao', + 'opa', + 'orkh', + 'orya', + 'osge', + 'osma', + 'ougr', + 'oyb', + 'pahawh2', + 'pahawh3', + 'pahawh4', + 'palm', + 'pauc', + 'pcun', + 'pe', + 'pelm', + 'perm', + 'pf', + 'pg', + 'ph', + 'phag', + 'phli', + 'phlp', + 'phlv', + 'phnx', + 'phr', + 'pij', + 'pinyin', + 'piqd', + 'pk', + 'pl', + 'plrd', + 'plu', + 'pm', + 'pn', + 'pr', + 'prs', + 'prt', + 'prti', + 'psin', + 'pt', + 'pt-BR', + 'pub', + 'pw', + 'py', + 'qa', + 'qaaa..qabx', + 'qm..qz', + 'quh', + 'ranj', + 'raq', + 'ras', + 're', + 'rjng', + 'rki', + 'rm', + 'rmx', + 'ro', + 'rohg', + 'roro', + 'rs', + 'ru', + 'runr', + 'sa', + 'samr', + 'sara', + 'sarb', + 'saur', + 'sb', + 'sco', + 'scv', + 'sgn-be-fr', + 'sgn-be-nl', + 'sgn-br', + 'sgn-ch-de', + 'sgn-co', + 'sgn-de', + 'sgn-dk', + 'sgn-es', + 'sgn-fr', + 'sgn-gb', + 'sgn-gr', + 'sgn-ie', + 'sgn-it', + 'sgn-jp', + 'sgn-mx', + 'sgn-ni', + 'sgn-nl', + 'sgn-no', + 'sgn-pt', + 'sgn-se', + 'sgn-us', + 'sgn-za', + 'sgnw', + 'shaw', + 'shrd', + 'shui', + 'sidd', + 'sidt', + 'simple', + 'sind', + 'sinh', + 'sj', + 'sl', + 'sl-nedis', + 'sl-rozaj', + 'snz', + 'sogd', + 'sogo', + 'sora', + 'soyo', + 'spanglis', + 'sr-cyrl', + 'sr-latn', + 'sund', + 'sunu', + 'sx', + 'sy', + 'sylo', + 'syrc', + 'syre', + 'syrj', + 'syrn', + 'sz', + 'tagb', + 'taj', + 'takr', + 'tale', + 'talu', + 'taml', + 'tang', + 'tavt', + 'tayo', + 'tc', + 'td', + 'tdf', + 'tdg', + 'telu', + 'teng', + 'tf', + 'tfng', + 'tg-arab', + 'tg-cyrl', + 'tglg', + 'thaa', + 'thai', + 'tibt', + 'tirh', + 'tj', + 'tm', + 'tnsa', + 'todr', + 'tols', + 'toto', + 'tp', + 'tpn', + 'tpo', + 'tutg', + 'tv', + 'tvd', + 'tw', + 'twm', + 'tyj', + 'tz', + 'ua', + 'ugar', + 'um', + 'umi', + 'un', + 'unifon', + 'us', + 'uy', + 'uz-cyrl', + 'uz-latn', + 'va', + 'vaii', + 'vaj', + 'vc', + 'vg', + 'visp', + 'vith', + 'vn', + 'vo', + 'vu', + 'wara', + 'waw', + 'wcho', + 'weo', + 'wf', + 'wole', + 'ws', + 'xa..xz', + 'xch', + 'xny', + 'xpeo', + 'xsux', + 'yam', + 'yd', + 'ye', + 'yezi', + 'yi', + 'yi-latn', + 'yiii', + 'yt', + 'yu', + 'yue', + 'yug', + 'zanb', + 'zbl', + 'zh-Latn', + 'zh-cmn', + 'zh-cmn-hans', + 'zh-cmn-hant', + 'zh-gan', + 'zh-guoyu', + 'zh-hakka', + 'zh-hans', + 'zh-hans-cn', + 'zh-hans-hk', + 'zh-hans-mo', + 'zh-hans-sg', + 'zh-hans-tw', + 'zh-hant', + 'zh-hant-cn', + 'zh-hant-hk', + 'zh-hant-mo', + 'zh-hant-sg', + 'zh-hant-tw', + 'zh-min', + 'zh-min-nan', + 'zh-wuu', + 'zh-xiang', + 'zh-yue', + 'zinh', + 'zko', + 'zm', + 'zmth', + 'zom', + 'zr', + 'zsye', + 'zsym', + 'zw', + 'zxxx', + 'zyyy', + 'zz', + 'zzzz', +]; +module.exports = codes; diff --git a/tests/lib/rules/template-require-lang-attribute.js b/tests/lib/rules/template-require-lang-attribute.js index d7d7685d71..7b9d95f25d 100644 --- a/tests/lib/rules/template-require-lang-attribute.js +++ b/tests/lib/rules/template-require-lang-attribute.js @@ -13,6 +13,7 @@ ruleTester.run('template-require-lang-attribute', rule, { '', '', '', + '', '', { code: '', @@ -66,6 +67,14 @@ ruleTester.run('template-require-lang-attribute', rule, { options: [{ validateValues: true }], errors: [{ message: ERROR_MESSAGE }], }, + { + // Invalid region subtag: "xx" is not a registered ISO 3166 / BCP 47 + // region code. Prior to the country-codes port, the rule only + // validated the primary subtag and incorrectly accepted this value. + code: '', + output: null, + errors: [{ message: ERROR_MESSAGE }], + }, { code: '', output: null, @@ -94,6 +103,7 @@ hbsRuleTester.run('template-require-lang-attribute', rule, { '', '', '', + '', '', { code: '', @@ -146,6 +156,14 @@ hbsRuleTester.run('template-require-lang-attribute', rule, { options: [{ validateValues: true }], errors: [{ message: ERROR_MESSAGE }], }, + { + // Invalid region subtag: "xx" is not a registered ISO 3166 / BCP 47 + // region code. Prior to the country-codes port, the rule only + // validated the primary subtag and incorrectly accepted this value. + code: '', + output: null, + errors: [{ message: ERROR_MESSAGE }], + }, { code: '', output: null, From 4fab3bea0ff31f56db82d3ad2854a2aa11a54877 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Johan=20R=C3=B8ed?= Date: Mon, 13 Apr 2026 19:40:20 +0200 Subject: [PATCH 2/2] Replace hand-maintained BCP47 table with language-tags MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - Drop lib/utils/country-codes.js (628 lines). - Use language-tags (registry-backed validation via the IANA language-subtag-registry transitive dep) instead of a per-subtag allowlist; keep the empty/whitespace early-return. - Pinned to ^1.0.9 (last CJS line; v2.x is ESM). Behavioral note: language-tags rejects deprecated grandfathered tags (e.g. `i-klingon`, preferred value `tlh`). The previous hand-rolled validator also rejected `i-klingon` (its subtags weren't in the table, nor was the whole tag), so existing tests are unchanged — but consumer templates relying on deprecated tags would now be flagged. --- lib/rules/template-require-lang-attribute.js | 217 +----- lib/utils/country-codes.js | 628 ------------------ package.json | 1 + pnpm-lock.yaml | 16 + .../rules/template-require-lang-attribute.js | 2 + 5 files changed, 21 insertions(+), 843 deletions(-) delete mode 100644 lib/utils/country-codes.js diff --git a/lib/rules/template-require-lang-attribute.js b/lib/rules/template-require-lang-attribute.js index 8876b1ffb1..fd5ac85185 100644 --- a/lib/rules/template-require-lang-attribute.js +++ b/lib/rules/template-require-lang-attribute.js @@ -1,194 +1,4 @@ -const COUNTRY_CODES = require('../utils/country-codes'); - -// Common valid BCP 47 primary language subtags (not exhaustive, but covers the most common). -// Used as a fast-path check for the primary subtag; full validation of every -// hyphen-separated subtag is performed against COUNTRY_CODES (mirroring -// ember-template-lint's upstream behavior). -const COMMON_LANG_CODES = new Set([ - 'aa', - 'ab', - 'af', - 'ak', - 'am', - 'an', - 'ar', - 'as', - 'av', - 'ay', - 'az', - 'ba', - 'be', - 'bg', - 'bh', - 'bi', - 'bm', - 'bn', - 'bo', - 'br', - 'bs', - 'ca', - 'ce', - 'ch', - 'co', - 'cr', - 'cs', - 'cu', - 'cv', - 'cy', - 'da', - 'de', - 'dv', - 'dz', - 'ee', - 'el', - 'en', - 'eo', - 'es', - 'et', - 'eu', - 'fa', - 'ff', - 'fi', - 'fj', - 'fo', - 'fr', - 'fy', - 'ga', - 'gd', - 'gl', - 'gn', - 'gu', - 'gv', - 'ha', - 'he', - 'hi', - 'ho', - 'hr', - 'ht', - 'hu', - 'hy', - 'hz', - 'ia', - 'id', - 'ie', - 'ig', - 'ii', - 'ik', - 'io', - 'is', - 'it', - 'iu', - 'ja', - 'jv', - 'ka', - 'kg', - 'ki', - 'kj', - 'kk', - 'kl', - 'km', - 'kn', - 'ko', - 'kr', - 'ks', - 'ku', - 'kv', - 'kw', - 'ky', - 'la', - 'lb', - 'lg', - 'li', - 'ln', - 'lo', - 'lt', - 'lu', - 'lv', - 'mg', - 'mh', - 'mi', - 'mk', - 'ml', - 'mn', - 'mr', - 'ms', - 'mt', - 'my', - 'na', - 'nb', - 'nd', - 'ne', - 'ng', - 'nl', - 'nn', - 'no', - 'nr', - 'nv', - 'ny', - 'oc', - 'oj', - 'om', - 'or', - 'os', - 'pa', - 'pi', - 'pl', - 'ps', - 'pt', - 'qu', - 'rm', - 'rn', - 'ro', - 'ru', - 'rw', - 'sa', - 'sc', - 'sd', - 'se', - 'sg', - 'si', - 'sk', - 'sl', - 'sm', - 'sn', - 'so', - 'sq', - 'sr', - 'ss', - 'st', - 'su', - 'sv', - 'sw', - 'ta', - 'te', - 'tg', - 'th', - 'ti', - 'tk', - 'tl', - 'tn', - 'to', - 'tr', - 'ts', - 'tt', - 'tw', - 'ty', - 'ug', - 'uk', - 'ur', - 'uz', - 've', - 'vi', - 'vo', - 'wa', - 'wo', - 'xh', - 'yi', - 'yo', - 'za', - 'zh', - 'zu', -]); +const tags = require('language-tags'); const DEFAULT_CONFIG = { validateValues: true, @@ -198,30 +8,7 @@ function isValidLangTag(value) { if (!value || !value.trim()) { return false; } - const normalized = value.trim().toLowerCase(); - const parts = normalized.split('-'); - - // Primary subtag must be a known language code. Keep the original - // COMMON_LANG_CODES fast-path so bare tags like "zh" remain valid even - // when they are not present in the full country-codes table. - if (!COMMON_LANG_CODES.has(parts[0])) { - // Fallback: the whole value may be a registered grandfathered/irregular - // tag (e.g. "zh-hant", "sgn-gb"). - return COUNTRY_CODES.includes(normalized); - } - - // Validate every remaining hyphen-separated subtag (region / script / - // variant) against the full country-codes table, mirroring upstream - // ember-template-lint. A whole-string match against COUNTRY_CODES is - // also accepted to cover multi-part registered tags. - if (parts.length === 1) { - return true; - } - - return ( - parts.slice(1).every((p) => COUNTRY_CODES.includes(p)) || - COUNTRY_CODES.includes(normalized) - ); + return tags(value.trim()).valid(); } function parseConfig(config) { diff --git a/lib/utils/country-codes.js b/lib/utils/country-codes.js deleted file mode 100644 index 1d4dd90b61..0000000000 --- a/lib/utils/country-codes.js +++ /dev/null @@ -1,628 +0,0 @@ -const codes = [ - '1994', - 'aas', - 'ac', - 'acn', - 'ad', - 'adlm', - 'adx', - 'aeb', - 'afak', - 'ag', - 'aghb', - 'ahom', - 'ai', - 'al', - 'alalc97', - 'ao', - 'ao1990', - 'aog', - 'apc', - 'aq', - 'arab', - 'aran', - 'armi', - 'armn', - 'art-lojban', - 'at', - 'au', - 'avst', - 'aw', - 'ax', - 'az-arab', - 'az-cyrl', - 'az-latn', - 'baku1926', - 'bali', - 'bamu', - 'bass', - 'batk', - 'bb', - 'bcg', - 'bd', - 'be', - 'be-latn', - 'beng', - 'bf', - 'bfy', - 'bg', - 'bhks', - 'bir', - 'bj', - 'bl', - 'blasl', - 'blis', - 'blo', - 'bmf', - 'bopo', - 'bpp', - 'bq', - 'brah', - 'brai', - 'bs-cyrl', - 'bs-latn', - 'bt', - 'bu', - 'bugi', - 'buhd', - 'bv', - 'bw', - 'by', - 'bz', - 'ca', - 'cakm', - 'cans', - 'cari', - 'cax', - 'cbr', - 'cc', - 'cd', - 'cel-gaulish', - 'cf', - 'cg', - 'cham', - 'cher', - 'chis', - 'chrs', - 'ci', - 'cir', - 'cirt', - 'ck', - 'cl', - 'cm', - 'cmr', - 'cn', - 'copt', - 'cp', - 'cpmn', - 'cprt', - 'cq', - 'crr', - 'cw', - 'cx', - 'cyrl', - 'cyrs', - 'cz', - 'da', - 'dd', - 'de', - 'de-1901', - 'de-1996', - 'de-at-1901', - 'de-at-1996', - 'de-ch-1901', - 'de-ch-1996', - 'de-de-1901', - 'de-de-1996', - 'dev', - 'deva', - 'dg', - 'diak', - 'dif', - 'dj', - 'djk', - 'dk', - 'dm', - 'dmw', - 'do', - 'dogr', - 'drl', - 'dsrt', - 'dtp', - 'dupl', - 'duz', - 'dz', - 'ea', - 'ec', - 'eg', - 'egyd', - 'egyh', - 'egyp', - 'eh', - 'ekavsk', - 'el', - 'elba', - 'elym', - 'ema', - 'en', - 'en-CA', - 'en-boont', - 'en-gb-oed', - 'en-scouse', - 'eo', - 'er', - 'es-419', - 'ethi', - 'eu', - 'ez', - 'fk', - 'fm', - 'fonipa', - 'fonkirsh', - 'fonnapa', - 'fonupa', - 'fonxsamp', - 'fr', - 'frm', - 'fx', - 'gal', - 'gara', - 'gb', - 'gdj', - 'ge', - 'geok', - 'geor', - 'gf', - 'gg', - 'gh', - 'gi', - 'glag', - 'gm', - 'gong', - 'gonm', - 'goth', - 'gp', - 'gq', - 'gr', - 'gran', - 'grclass', - 'grek', - 'grital', - 'grmistr', - 'gs', - 'gt', - 'gu', - 'gujr', - 'gukh', - 'guru', - 'gvr', - 'gw', - 'gy', - 'hanb', - 'hang', - 'hani', - 'hano', - 'hans', - 'hant', - 'hatr', - 'he', - 'hebr', - 'hira', - 'hk', - 'hle', - 'hluw', - 'hm', - 'hmng', - 'hmnp', - 'hn', - 'hrkt', - 'hung', - 'huw', - 'hy', - 'i-ami', - 'i-bnn', - 'i-default', - 'i-enochian', - 'i-hak', - 'i-klingon', - 'i-lux', - 'i-mingo', - 'i-navajo', - 'i-pwn', - 'i-tao', - 'i-tay', - 'i-tsu', - 'iba', - 'ic', - 'id', - 'ijekavsk', - 'il', - 'im', - 'inds', - 'iq', - 'ir', - 'ital', - 'iu-cans', - 'iu-latn', - 'ja-Latn', - 'jal', - 'jamo', - 'java', - 'je', - 'jm', - 'jo', - 'jp', - 'jpan', - 'jurc', - 'jv', - 'kak', - 'kali', - 'kana', - 'kawi', - 'kdz', - 'ke', - 'kea', - 'kh', - 'khar', - 'khk', - 'khmr', - 'khoj', - 'kitl', - 'kits', - 'kjh', - 'kl', - 'kleinsch', - 'kmb', - 'kml', - 'knda', - 'kore', - 'kp', - 'kpel', - 'krai', - 'kru', - 'ksp', - 'kthi', - 'ktz', - 'kw', - 'kwv', - 'kxr', - 'kz', - 'kzk', - 'la', - 'lana', - 'laoo', - 'latf', - 'latg', - 'latn', - 'lc', - 'lcq', - 'leke', - 'lepc', - 'limb', - 'lina', - 'linb', - 'lisu', - 'lk', - 'lld', - 'loma', - 'lr', - 'lrr', - 'ls', - 'ltg', - 'lv', - 'ly', - 'lyci', - 'lydi', - 'ma', - 'mahj', - 'maka', - 'mand', - 'mani', - 'marc', - 'maya', - 'mc', - 'md', - 'me', - 'medf', - 'mend', - 'merc', - 'mero', - 'mf', - 'mlym', - 'mm', - 'mn-cyrl', - 'mn-mong', - 'modi', - 'mom', - 'mong', - 'moon', - 'mp', - 'mq', - 'mroo', - 'mry', - 'mtei', - 'mtm', - 'mu', - 'mult', - 'mv', - 'mw', - 'mx', - 'mymr', - 'mz', - 'nagm', - 'nan-Latn', - 'nand', - 'narb', - 'nbat', - 'nbr', - 'nc', - 'newa', - 'nf', - 'ngt', - 'ngv', - 'ni', - 'nkdb', - 'nkgb', - 'nkoo', - 'nn', - 'no-bok', - 'no-nyn', - 'np', - 'nshu', - 'nt', - 'nu', - 'nun', - 'nyc', - 'nz', - 'oc', - 'ogam', - 'ola', - 'olck', - 'onao', - 'opa', - 'orkh', - 'orya', - 'osge', - 'osma', - 'ougr', - 'oyb', - 'pahawh2', - 'pahawh3', - 'pahawh4', - 'palm', - 'pauc', - 'pcun', - 'pe', - 'pelm', - 'perm', - 'pf', - 'pg', - 'ph', - 'phag', - 'phli', - 'phlp', - 'phlv', - 'phnx', - 'phr', - 'pij', - 'pinyin', - 'piqd', - 'pk', - 'pl', - 'plrd', - 'plu', - 'pm', - 'pn', - 'pr', - 'prs', - 'prt', - 'prti', - 'psin', - 'pt', - 'pt-BR', - 'pub', - 'pw', - 'py', - 'qa', - 'qaaa..qabx', - 'qm..qz', - 'quh', - 'ranj', - 'raq', - 'ras', - 're', - 'rjng', - 'rki', - 'rm', - 'rmx', - 'ro', - 'rohg', - 'roro', - 'rs', - 'ru', - 'runr', - 'sa', - 'samr', - 'sara', - 'sarb', - 'saur', - 'sb', - 'sco', - 'scv', - 'sgn-be-fr', - 'sgn-be-nl', - 'sgn-br', - 'sgn-ch-de', - 'sgn-co', - 'sgn-de', - 'sgn-dk', - 'sgn-es', - 'sgn-fr', - 'sgn-gb', - 'sgn-gr', - 'sgn-ie', - 'sgn-it', - 'sgn-jp', - 'sgn-mx', - 'sgn-ni', - 'sgn-nl', - 'sgn-no', - 'sgn-pt', - 'sgn-se', - 'sgn-us', - 'sgn-za', - 'sgnw', - 'shaw', - 'shrd', - 'shui', - 'sidd', - 'sidt', - 'simple', - 'sind', - 'sinh', - 'sj', - 'sl', - 'sl-nedis', - 'sl-rozaj', - 'snz', - 'sogd', - 'sogo', - 'sora', - 'soyo', - 'spanglis', - 'sr-cyrl', - 'sr-latn', - 'sund', - 'sunu', - 'sx', - 'sy', - 'sylo', - 'syrc', - 'syre', - 'syrj', - 'syrn', - 'sz', - 'tagb', - 'taj', - 'takr', - 'tale', - 'talu', - 'taml', - 'tang', - 'tavt', - 'tayo', - 'tc', - 'td', - 'tdf', - 'tdg', - 'telu', - 'teng', - 'tf', - 'tfng', - 'tg-arab', - 'tg-cyrl', - 'tglg', - 'thaa', - 'thai', - 'tibt', - 'tirh', - 'tj', - 'tm', - 'tnsa', - 'todr', - 'tols', - 'toto', - 'tp', - 'tpn', - 'tpo', - 'tutg', - 'tv', - 'tvd', - 'tw', - 'twm', - 'tyj', - 'tz', - 'ua', - 'ugar', - 'um', - 'umi', - 'un', - 'unifon', - 'us', - 'uy', - 'uz-cyrl', - 'uz-latn', - 'va', - 'vaii', - 'vaj', - 'vc', - 'vg', - 'visp', - 'vith', - 'vn', - 'vo', - 'vu', - 'wara', - 'waw', - 'wcho', - 'weo', - 'wf', - 'wole', - 'ws', - 'xa..xz', - 'xch', - 'xny', - 'xpeo', - 'xsux', - 'yam', - 'yd', - 'ye', - 'yezi', - 'yi', - 'yi-latn', - 'yiii', - 'yt', - 'yu', - 'yue', - 'yug', - 'zanb', - 'zbl', - 'zh-Latn', - 'zh-cmn', - 'zh-cmn-hans', - 'zh-cmn-hant', - 'zh-gan', - 'zh-guoyu', - 'zh-hakka', - 'zh-hans', - 'zh-hans-cn', - 'zh-hans-hk', - 'zh-hans-mo', - 'zh-hans-sg', - 'zh-hans-tw', - 'zh-hant', - 'zh-hant-cn', - 'zh-hant-hk', - 'zh-hant-mo', - 'zh-hant-sg', - 'zh-hant-tw', - 'zh-min', - 'zh-min-nan', - 'zh-wuu', - 'zh-xiang', - 'zh-yue', - 'zinh', - 'zko', - 'zm', - 'zmth', - 'zom', - 'zr', - 'zsye', - 'zsym', - 'zw', - 'zxxx', - 'zyyy', - 'zz', - 'zzzz', -]; -module.exports = codes; diff --git a/package.json b/package.json index de20f9800e..719f475e7f 100644 --- a/package.json +++ b/package.json @@ -71,6 +71,7 @@ "eslint-utils": "^3.0.0", "estraverse": "^5.3.0", "html-tags": "^3.3.1", + "language-tags": "^1.0.9", "lodash.camelcase": "^4.3.0", "lodash.kebabcase": "^4.1.1", "requireindex": "^1.2.0", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ea58f19332..17861ed207 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -35,6 +35,9 @@ importers: html-tags: specifier: ^3.3.1 version: 3.3.1 + language-tags: + specifier: ^1.0.9 + version: 1.0.9 lodash.camelcase: specifier: ^4.3.0 version: 4.3.0 @@ -2737,6 +2740,13 @@ packages: resolution: {integrity: sha512-9zy9lkjac+TR1c2tG+mkNSVlyOpInnWdSMiue4F+kq8TwJSgv6o8jhLRg8Ho6SnZ9wOYUq/yozts9qQCfk7bIw==} engines: {node: '>=18'} + language-subtag-registry@0.3.23: + resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} + + language-tags@1.0.9: + resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} + engines: {node: '>=0.10'} + latest-version@9.0.0: resolution: {integrity: sha512-7W0vV3rqv5tokqkBAFV1LbR7HPOWzXQDpDgEuib/aJ1jsZZx6x3c2mBI+TJhJzOhkGeaLbCKEHXEXLfirtG2JA==} engines: {node: '>=18'} @@ -6812,6 +6822,12 @@ snapshots: ky@1.14.3: {} + language-subtag-registry@0.3.23: {} + + language-tags@1.0.9: + dependencies: + language-subtag-registry: 0.3.23 + latest-version@9.0.0: dependencies: package-json: 10.0.1 diff --git a/tests/lib/rules/template-require-lang-attribute.js b/tests/lib/rules/template-require-lang-attribute.js index 7b9d95f25d..55956986a5 100644 --- a/tests/lib/rules/template-require-lang-attribute.js +++ b/tests/lib/rules/template-require-lang-attribute.js @@ -14,6 +14,7 @@ ruleTester.run('template-require-lang-attribute', rule, { '', '', '', + '', '', { code: '', @@ -104,6 +105,7 @@ hbsRuleTester.run('template-require-lang-attribute', rule, { '', '', '', + '', '', { code: '',