Add UN M.49 region containment support - #193

Merged
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175
Jun 26, 2026
Merged

Add UN M.49 region containment support#193
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Resolves#175.

What

Matching a UN M.49 region group against a contained region now works, e.g. es-419 (Latin America and the Caribbean) matches es-MX (Mexico).

Changes

  • New UnM49Data dataset sourced from Unicode CLDR territoryContainment, parsed with XmlReader (reflection-free, keeps the library AOT compatible — XmlSerializer is avoided), then JSON converted and code generated following the existing ISO/RFC dataset pattern.
  • LanguageLookup.IsMatch(prefix, tag, regionContainment) — opt-in overload. The existing two-argument IsMatch is unchanged and delegates with regionContainment: false, so there is no behaviour change and no extra work in the default path. Matching is directional: the broad group in the prefix matches the specific region in the tag, not the reverse.
  • LanguageLookup.ExpandRegion — expands a tag region into the tag plus a variant per containing UN M.49 group, e.g. es-MXes-013, es-419, es-019, es-001.
  • Parser fixValidateExtendedLanguage now requires 3 alpha per RFC 5646 (extlang = 3ALPHA), so a numeric region following the language parses as a region instead of an extended language, e.g. es-419.
  • Restored previously trimmed control-flow comments in the parser and lookup.
  • Docs: README usage example + references, HISTORY entry, version.json bumped to 1.4.

Design notes

  • 419 is a grouping="true" overlay in CLDR (not a canonical tree node), so the loader keeps grouping overlays and skips only status="deprecated" entries — dropping overlays would remove 419 entirely.
  • 001 (World) is the universal ancestor, so es-001 matches any es tag with a region; documented in the API and covered by a test.

Testing

  • New UnM49Tests (dataset round-trip, Find, Contains, GetAncestors) and LanguageLookupTests theories covering the containment matching and ExpandRegion, including the directional and backward-compatibility cases.
  • Full suite: 293/293 passing. Build clean with AOT reference verification enabled.

🤖 Generated with Claude Code

Resolve#175 - match a UN M.49 region group against a contained region,
e.g. es-419 (Latin America) matches es-MX (Mexico).
- Add UnM49Data sourced from the CLDR territoryContainment data, parsed
with XmlReader to keep the library AOT compatible, generated and
embedded following the existing dataset pattern.
- Add LanguageLookup.IsMatch(prefix, tag, regionContainment) as an opt-in
overload; the existing two argument IsMatch is unchanged.
- Add LanguageLookup.ExpandRegion to expand a region into its containing
UN M.49 groups.
- Fix ValidateExtendedLanguage to require 3 alpha so a numeric region
following the language parses as a region, e.g. es-419.
- Restore previously trimmed control flow comments in the parser and lookup.
- Update README and HISTORY, add UN M.49 references, bump version to 1.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings June 26, 2026 16:11

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds UN M.49 (CLDR territoryContainment) region containment support to the LanguageTags library so numeric region groups (e.g., 419) can be matched against contained regions (e.g., MX), and enables region expansion to ancestor group regions.

Changes:

  • Introduces UnM49Data (XML loader + JSON/codegen + embedded generated dataset) sourced from Unicode CLDR territory containment.
  • Adds LanguageLookup.IsMatch(prefix, tag, regionContainment) overload and LanguageLookup.ExpandRegion() for containment-based matching/expansion.
  • Fixes parsing so extlang requires 3ALPHA, ensuring numeric regions like es-419 parse as a region.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
version.jsonBumps library version to 1.4.
README.mdDocuments UN M.49 containment matching and region expansion; updates links and release-notes snippet.
HISTORY.mdAdds 1.4 release entry describing containment support and parser fix.
LanguageTagsCreate/CreateTagData.csExtends codegen pipeline to download/convert/generate UN M.49 data and code.
LanguageTags/UnM49Data.csImplements UN M.49 loader (XmlReader), JSON serialization, code generation, and query APIs (Find/Contains/GetAncestors).
LanguageTags/UnM49DataGen.csAdds generated embedded UN M.49 dataset used by UnM49Data.Create().
LanguageData/unm49.jsonAdds generated JSON form of the UN M.49 dataset.
LanguageTags/LanguageSchema.csRegisters UnM49Data in the source-generated JsonSerializerContext.
LanguageTags/LanguageLookup.csAdds containment-aware matching overload and ExpandRegion(); wires in UN M.49 dataset usage.
LanguageTags/LanguageTagParser.csTightens extlang validation to 3ALPHA so numeric regions don’t misparse as extlang; restores control-flow comments.
LanguageTagsTests/UnM49Tests.csAdds tests for UN M.49 dataset loading/round-tripping and basic query behavior.
LanguageTagsTests/LanguageLookupTests.csAdds tests for containment matching (directional + opt-in) and ExpandRegion().

Comment threadLanguageTags/LanguageLookup.cs
Comment threadLanguageTags/LanguageLookup.cs Outdated
Comment threadLanguageTags/UnM49Data.cs
Comment threadLanguageTags/UnM49Data.cs
- Region containment matching now substitutes the candidate region and
reuses the plain matcher, preserving variant, extension, and private use
semantics to avoid false positives, e.g. es-419-nedis no longer matches
es-MX while es-419 still matches es-MX-nedis.
- Clarify UnM49Data.Find returns the first of possibly multiple records for
a code, and point to GetAncestors/Contains for full transitive containment.
- Clarify UnM49Record.Code may be an alphabetic CLDR grouping code (EU, EZ, UN).
- Add region containment tests for script preservation and prefix variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Comment threadLanguageTags/LanguageLookup.cs
Comment threadREADME.md Outdated
"variant" has a specific RFC 5646 meaning, so describe the expanded
entries as region substituted tags instead, in the XML doc and README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 10cc912 into developJun 26, 2026
12 checks passed
@ptr727
ptr727 deleted the feature/unm49-containment-175 branch June 26, 2026 16:53
ptr727 added a commit that referenced this pull request Jun 26, 2026
Release: promote develop to main
- UN M.49 region containment (#193, version floor 1.4)
- Release-workflow fixes re-synced from template (#196: #213/#214/#217)
Conflicts in three workflow files were the parallel setup-dotnet dependabot
bump (identical on both branches); resolved to develop's authoritative
versions (newer template re-sync + the workflow fixes).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Add UN M.49 region containment support - #193

Merged
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175
Jun 26, 2026
Merged

Add UN M.49 region containment support#193
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Resolves#175.

What

Matching a UN M.49 region group against a contained region now works, e.g. es-419 (Latin America and the Caribbean) matches es-MX (Mexico).

Changes

  • New UnM49Data dataset sourced from Unicode CLDR territoryContainment, parsed with XmlReader (reflection-free, keeps the library AOT compatible — XmlSerializer is avoided), then JSON converted and code generated following the existing ISO/RFC dataset pattern.
  • LanguageLookup.IsMatch(prefix, tag, regionContainment) — opt-in overload. The existing two-argument IsMatch is unchanged and delegates with regionContainment: false, so there is no behaviour change and no extra work in the default path. Matching is directional: the broad group in the prefix matches the specific region in the tag, not the reverse.
  • LanguageLookup.ExpandRegion — expands a tag region into the tag plus a variant per containing UN M.49 group, e.g. es-MXes-013, es-419, es-019, es-001.
  • Parser fixValidateExtendedLanguage now requires 3 alpha per RFC 5646 (extlang = 3ALPHA), so a numeric region following the language parses as a region instead of an extended language, e.g. es-419.
  • Restored previously trimmed control-flow comments in the parser and lookup.
  • Docs: README usage example + references, HISTORY entry, version.json bumped to 1.4.

Design notes

  • 419 is a grouping="true" overlay in CLDR (not a canonical tree node), so the loader keeps grouping overlays and skips only status="deprecated" entries — dropping overlays would remove 419 entirely.
  • 001 (World) is the universal ancestor, so es-001 matches any es tag with a region; documented in the API and covered by a test.

Testing

  • New UnM49Tests (dataset round-trip, Find, Contains, GetAncestors) and LanguageLookupTests theories covering the containment matching and ExpandRegion, including the directional and backward-compatibility cases.
  • Full suite: 293/293 passing. Build clean with AOT reference verification enabled.

🤖 Generated with Claude Code

Resolve#175 - match a UN M.49 region group against a contained region,
e.g. es-419 (Latin America) matches es-MX (Mexico).
- Add UnM49Data sourced from the CLDR territoryContainment data, parsed
with XmlReader to keep the library AOT compatible, generated and
embedded following the existing dataset pattern.
- Add LanguageLookup.IsMatch(prefix, tag, regionContainment) as an opt-in
overload; the existing two argument IsMatch is unchanged.
- Add LanguageLookup.ExpandRegion to expand a region into its containing
UN M.49 groups.
- Fix ValidateExtendedLanguage to require 3 alpha so a numeric region
following the language parses as a region, e.g. es-419.
- Restore previously trimmed control flow comments in the parser and lookup.
- Update README and HISTORY, add UN M.49 references, bump version to 1.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings June 26, 2026 16:11

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds UN M.49 (CLDR territoryContainment) region containment support to the LanguageTags library so numeric region groups (e.g., 419) can be matched against contained regions (e.g., MX), and enables region expansion to ancestor group regions.

Changes:

  • Introduces UnM49Data (XML loader + JSON/codegen + embedded generated dataset) sourced from Unicode CLDR territory containment.
  • Adds LanguageLookup.IsMatch(prefix, tag, regionContainment) overload and LanguageLookup.ExpandRegion() for containment-based matching/expansion.
  • Fixes parsing so extlang requires 3ALPHA, ensuring numeric regions like es-419 parse as a region.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
version.jsonBumps library version to 1.4.
README.mdDocuments UN M.49 containment matching and region expansion; updates links and release-notes snippet.
HISTORY.mdAdds 1.4 release entry describing containment support and parser fix.
LanguageTagsCreate/CreateTagData.csExtends codegen pipeline to download/convert/generate UN M.49 data and code.
LanguageTags/UnM49Data.csImplements UN M.49 loader (XmlReader), JSON serialization, code generation, and query APIs (Find/Contains/GetAncestors).
LanguageTags/UnM49DataGen.csAdds generated embedded UN M.49 dataset used by UnM49Data.Create().
LanguageData/unm49.jsonAdds generated JSON form of the UN M.49 dataset.
LanguageTags/LanguageSchema.csRegisters UnM49Data in the source-generated JsonSerializerContext.
LanguageTags/LanguageLookup.csAdds containment-aware matching overload and ExpandRegion(); wires in UN M.49 dataset usage.
LanguageTags/LanguageTagParser.csTightens extlang validation to 3ALPHA so numeric regions don’t misparse as extlang; restores control-flow comments.
LanguageTagsTests/UnM49Tests.csAdds tests for UN M.49 dataset loading/round-tripping and basic query behavior.
LanguageTagsTests/LanguageLookupTests.csAdds tests for containment matching (directional + opt-in) and ExpandRegion().

Comment threadLanguageTags/LanguageLookup.cs
Comment threadLanguageTags/LanguageLookup.cs Outdated
Comment threadLanguageTags/UnM49Data.cs
Comment threadLanguageTags/UnM49Data.cs
- Region containment matching now substitutes the candidate region and
reuses the plain matcher, preserving variant, extension, and private use
semantics to avoid false positives, e.g. es-419-nedis no longer matches
es-MX while es-419 still matches es-MX-nedis.
- Clarify UnM49Data.Find returns the first of possibly multiple records for
a code, and point to GetAncestors/Contains for full transitive containment.
- Clarify UnM49Record.Code may be an alphabetic CLDR grouping code (EU, EZ, UN).
- Add region containment tests for script preservation and prefix variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Comment threadLanguageTags/LanguageLookup.cs
Comment threadREADME.md Outdated
"variant" has a specific RFC 5646 meaning, so describe the expanded
entries as region substituted tags instead, in the XML doc and README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 10cc912 into developJun 26, 2026
12 checks passed
@ptr727
ptr727 deleted the feature/unm49-containment-175 branch June 26, 2026 16:53
ptr727 added a commit that referenced this pull request Jun 26, 2026
Release: promote develop to main
- UN M.49 region containment (#193, version floor 1.4)
- Release-workflow fixes re-synced from template (#196: #213/#214/#217)
Conflicts in three workflow files were the parallel setup-dotnet dependabot
bump (identical on both branches); resolved to develop's authoritative
versions (newer template re-sync + the workflow fixes).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add UN M.49 region containment support - #193

Merged
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175
Jun 26, 2026
Merged

Add UN M.49 region containment support#193
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Resolves#175.

What

Matching a UN M.49 region group against a contained region now works, e.g. es-419 (Latin America and the Caribbean) matches es-MX (Mexico).

Changes

  • New UnM49Data dataset sourced from Unicode CLDR territoryContainment, parsed with XmlReader (reflection-free, keeps the library AOT compatible — XmlSerializer is avoided), then JSON converted and code generated following the existing ISO/RFC dataset pattern.
  • LanguageLookup.IsMatch(prefix, tag, regionContainment) — opt-in overload. The existing two-argument IsMatch is unchanged and delegates with regionContainment: false, so there is no behaviour change and no extra work in the default path. Matching is directional: the broad group in the prefix matches the specific region in the tag, not the reverse.
  • LanguageLookup.ExpandRegion — expands a tag region into the tag plus a variant per containing UN M.49 group, e.g. es-MXes-013, es-419, es-019, es-001.
  • Parser fixValidateExtendedLanguage now requires 3 alpha per RFC 5646 (extlang = 3ALPHA), so a numeric region following the language parses as a region instead of an extended language, e.g. es-419.
  • Restored previously trimmed control-flow comments in the parser and lookup.
  • Docs: README usage example + references, HISTORY entry, version.json bumped to 1.4.

Design notes

  • 419 is a grouping="true" overlay in CLDR (not a canonical tree node), so the loader keeps grouping overlays and skips only status="deprecated" entries — dropping overlays would remove 419 entirely.
  • 001 (World) is the universal ancestor, so es-001 matches any es tag with a region; documented in the API and covered by a test.

Testing

  • New UnM49Tests (dataset round-trip, Find, Contains, GetAncestors) and LanguageLookupTests theories covering the containment matching and ExpandRegion, including the directional and backward-compatibility cases.
  • Full suite: 293/293 passing. Build clean with AOT reference verification enabled.

🤖 Generated with Claude Code

Resolve#175 - match a UN M.49 region group against a contained region,
e.g. es-419 (Latin America) matches es-MX (Mexico).
- Add UnM49Data sourced from the CLDR territoryContainment data, parsed
with XmlReader to keep the library AOT compatible, generated and
embedded following the existing dataset pattern.
- Add LanguageLookup.IsMatch(prefix, tag, regionContainment) as an opt-in
overload; the existing two argument IsMatch is unchanged.
- Add LanguageLookup.ExpandRegion to expand a region into its containing
UN M.49 groups.
- Fix ValidateExtendedLanguage to require 3 alpha so a numeric region
following the language parses as a region, e.g. es-419.
- Restore previously trimmed control flow comments in the parser and lookup.
- Update README and HISTORY, add UN M.49 references, bump version to 1.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings June 26, 2026 16:11

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds UN M.49 (CLDR territoryContainment) region containment support to the LanguageTags library so numeric region groups (e.g., 419) can be matched against contained regions (e.g., MX), and enables region expansion to ancestor group regions.

Changes:

  • Introduces UnM49Data (XML loader + JSON/codegen + embedded generated dataset) sourced from Unicode CLDR territory containment.
  • Adds LanguageLookup.IsMatch(prefix, tag, regionContainment) overload and LanguageLookup.ExpandRegion() for containment-based matching/expansion.
  • Fixes parsing so extlang requires 3ALPHA, ensuring numeric regions like es-419 parse as a region.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
version.jsonBumps library version to 1.4.
README.mdDocuments UN M.49 containment matching and region expansion; updates links and release-notes snippet.
HISTORY.mdAdds 1.4 release entry describing containment support and parser fix.
LanguageTagsCreate/CreateTagData.csExtends codegen pipeline to download/convert/generate UN M.49 data and code.
LanguageTags/UnM49Data.csImplements UN M.49 loader (XmlReader), JSON serialization, code generation, and query APIs (Find/Contains/GetAncestors).
LanguageTags/UnM49DataGen.csAdds generated embedded UN M.49 dataset used by UnM49Data.Create().
LanguageData/unm49.jsonAdds generated JSON form of the UN M.49 dataset.
LanguageTags/LanguageSchema.csRegisters UnM49Data in the source-generated JsonSerializerContext.
LanguageTags/LanguageLookup.csAdds containment-aware matching overload and ExpandRegion(); wires in UN M.49 dataset usage.
LanguageTags/LanguageTagParser.csTightens extlang validation to 3ALPHA so numeric regions don’t misparse as extlang; restores control-flow comments.
LanguageTagsTests/UnM49Tests.csAdds tests for UN M.49 dataset loading/round-tripping and basic query behavior.
LanguageTagsTests/LanguageLookupTests.csAdds tests for containment matching (directional + opt-in) and ExpandRegion().

Comment threadLanguageTags/LanguageLookup.cs
Comment threadLanguageTags/LanguageLookup.cs Outdated
Comment threadLanguageTags/UnM49Data.cs
Comment threadLanguageTags/UnM49Data.cs
- Region containment matching now substitutes the candidate region and
reuses the plain matcher, preserving variant, extension, and private use
semantics to avoid false positives, e.g. es-419-nedis no longer matches
es-MX while es-419 still matches es-MX-nedis.
- Clarify UnM49Data.Find returns the first of possibly multiple records for
a code, and point to GetAncestors/Contains for full transitive containment.
- Clarify UnM49Record.Code may be an alphabetic CLDR grouping code (EU, EZ, UN).
- Add region containment tests for script preservation and prefix variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Comment threadLanguageTags/LanguageLookup.cs
Comment threadREADME.md Outdated
"variant" has a specific RFC 5646 meaning, so describe the expanded
entries as region substituted tags instead, in the XML doc and README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 10cc912 into developJun 26, 2026
12 checks passed
@ptr727
ptr727 deleted the feature/unm49-containment-175 branch June 26, 2026 16:53
ptr727 added a commit that referenced this pull request Jun 26, 2026
Release: promote develop to main
- UN M.49 region containment (#193, version floor 1.4)
- Release-workflow fixes re-synced from template (#196: #213/#214/#217)
Conflicts in three workflow files were the parallel setup-dotnet dependabot
bump (identical on both branches); resolved to develop's authoritative
versions (newer template re-sync + the workflow fixes).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add UN M.49 region containment support - #193

Merged
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175
Jun 26, 2026
Merged

Add UN M.49 region containment support#193
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Resolves#175.

What

Matching a UN M.49 region group against a contained region now works, e.g. es-419 (Latin America and the Caribbean) matches es-MX (Mexico).

Changes

  • New UnM49Data dataset sourced from Unicode CLDR territoryContainment, parsed with XmlReader (reflection-free, keeps the library AOT compatible — XmlSerializer is avoided), then JSON converted and code generated following the existing ISO/RFC dataset pattern.
  • LanguageLookup.IsMatch(prefix, tag, regionContainment) — opt-in overload. The existing two-argument IsMatch is unchanged and delegates with regionContainment: false, so there is no behaviour change and no extra work in the default path. Matching is directional: the broad group in the prefix matches the specific region in the tag, not the reverse.
  • LanguageLookup.ExpandRegion — expands a tag region into the tag plus a variant per containing UN M.49 group, e.g. es-MXes-013, es-419, es-019, es-001.
  • Parser fixValidateExtendedLanguage now requires 3 alpha per RFC 5646 (extlang = 3ALPHA), so a numeric region following the language parses as a region instead of an extended language, e.g. es-419.
  • Restored previously trimmed control-flow comments in the parser and lookup.
  • Docs: README usage example + references, HISTORY entry, version.json bumped to 1.4.

Design notes

  • 419 is a grouping="true" overlay in CLDR (not a canonical tree node), so the loader keeps grouping overlays and skips only status="deprecated" entries — dropping overlays would remove 419 entirely.
  • 001 (World) is the universal ancestor, so es-001 matches any es tag with a region; documented in the API and covered by a test.

Testing

  • New UnM49Tests (dataset round-trip, Find, Contains, GetAncestors) and LanguageLookupTests theories covering the containment matching and ExpandRegion, including the directional and backward-compatibility cases.
  • Full suite: 293/293 passing. Build clean with AOT reference verification enabled.

🤖 Generated with Claude Code

Resolve#175 - match a UN M.49 region group against a contained region,
e.g. es-419 (Latin America) matches es-MX (Mexico).
- Add UnM49Data sourced from the CLDR territoryContainment data, parsed
with XmlReader to keep the library AOT compatible, generated and
embedded following the existing dataset pattern.
- Add LanguageLookup.IsMatch(prefix, tag, regionContainment) as an opt-in
overload; the existing two argument IsMatch is unchanged.
- Add LanguageLookup.ExpandRegion to expand a region into its containing
UN M.49 groups.
- Fix ValidateExtendedLanguage to require 3 alpha so a numeric region
following the language parses as a region, e.g. es-419.
- Restore previously trimmed control flow comments in the parser and lookup.
- Update README and HISTORY, add UN M.49 references, bump version to 1.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings June 26, 2026 16:11

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds UN M.49 (CLDR territoryContainment) region containment support to the LanguageTags library so numeric region groups (e.g., 419) can be matched against contained regions (e.g., MX), and enables region expansion to ancestor group regions.

Changes:

  • Introduces UnM49Data (XML loader + JSON/codegen + embedded generated dataset) sourced from Unicode CLDR territory containment.
  • Adds LanguageLookup.IsMatch(prefix, tag, regionContainment) overload and LanguageLookup.ExpandRegion() for containment-based matching/expansion.
  • Fixes parsing so extlang requires 3ALPHA, ensuring numeric regions like es-419 parse as a region.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
version.jsonBumps library version to 1.4.
README.mdDocuments UN M.49 containment matching and region expansion; updates links and release-notes snippet.
HISTORY.mdAdds 1.4 release entry describing containment support and parser fix.
LanguageTagsCreate/CreateTagData.csExtends codegen pipeline to download/convert/generate UN M.49 data and code.
LanguageTags/UnM49Data.csImplements UN M.49 loader (XmlReader), JSON serialization, code generation, and query APIs (Find/Contains/GetAncestors).
LanguageTags/UnM49DataGen.csAdds generated embedded UN M.49 dataset used by UnM49Data.Create().
LanguageData/unm49.jsonAdds generated JSON form of the UN M.49 dataset.
LanguageTags/LanguageSchema.csRegisters UnM49Data in the source-generated JsonSerializerContext.
LanguageTags/LanguageLookup.csAdds containment-aware matching overload and ExpandRegion(); wires in UN M.49 dataset usage.
LanguageTags/LanguageTagParser.csTightens extlang validation to 3ALPHA so numeric regions don’t misparse as extlang; restores control-flow comments.
LanguageTagsTests/UnM49Tests.csAdds tests for UN M.49 dataset loading/round-tripping and basic query behavior.
LanguageTagsTests/LanguageLookupTests.csAdds tests for containment matching (directional + opt-in) and ExpandRegion().

Comment threadLanguageTags/LanguageLookup.cs
Comment threadLanguageTags/LanguageLookup.cs Outdated
Comment threadLanguageTags/UnM49Data.cs
Comment threadLanguageTags/UnM49Data.cs
- Region containment matching now substitutes the candidate region and
reuses the plain matcher, preserving variant, extension, and private use
semantics to avoid false positives, e.g. es-419-nedis no longer matches
es-MX while es-419 still matches es-MX-nedis.
- Clarify UnM49Data.Find returns the first of possibly multiple records for
a code, and point to GetAncestors/Contains for full transitive containment.
- Clarify UnM49Record.Code may be an alphabetic CLDR grouping code (EU, EZ, UN).
- Add region containment tests for script preservation and prefix variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Comment threadLanguageTags/LanguageLookup.cs
Comment threadREADME.md Outdated
"variant" has a specific RFC 5646 meaning, so describe the expanded
entries as region substituted tags instead, in the XML doc and README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 10cc912 into developJun 26, 2026
12 checks passed
@ptr727
ptr727 deleted the feature/unm49-containment-175 branch June 26, 2026 16:53
ptr727 added a commit that referenced this pull request Jun 26, 2026
Release: promote develop to main
- UN M.49 region containment (#193, version floor 1.4)
- Release-workflow fixes re-synced from template (#196: #213/#214/#217)
Conflicts in three workflow files were the parallel setup-dotnet dependabot
bump (identical on both branches); resolved to develop's authoritative
versions (newer template re-sync + the workflow fixes).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Add UN M.49 region containment support - #193

Merged
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175
Jun 26, 2026
Merged

Add UN M.49 region containment support#193
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Resolves#175.

What

Matching a UN M.49 region group against a contained region now works, e.g. es-419 (Latin America and the Caribbean) matches es-MX (Mexico).

Changes

  • New UnM49Data dataset sourced from Unicode CLDR territoryContainment, parsed with XmlReader (reflection-free, keeps the library AOT compatible — XmlSerializer is avoided), then JSON converted and code generated following the existing ISO/RFC dataset pattern.
  • LanguageLookup.IsMatch(prefix, tag, regionContainment) — opt-in overload. The existing two-argument IsMatch is unchanged and delegates with regionContainment: false, so there is no behaviour change and no extra work in the default path. Matching is directional: the broad group in the prefix matches the specific region in the tag, not the reverse.
  • LanguageLookup.ExpandRegion — expands a tag region into the tag plus a variant per containing UN M.49 group, e.g. es-MXes-013, es-419, es-019, es-001.
  • Parser fixValidateExtendedLanguage now requires 3 alpha per RFC 5646 (extlang = 3ALPHA), so a numeric region following the language parses as a region instead of an extended language, e.g. es-419.
  • Restored previously trimmed control-flow comments in the parser and lookup.
  • Docs: README usage example + references, HISTORY entry, version.json bumped to 1.4.

Design notes

  • 419 is a grouping="true" overlay in CLDR (not a canonical tree node), so the loader keeps grouping overlays and skips only status="deprecated" entries — dropping overlays would remove 419 entirely.
  • 001 (World) is the universal ancestor, so es-001 matches any es tag with a region; documented in the API and covered by a test.

Testing

  • New UnM49Tests (dataset round-trip, Find, Contains, GetAncestors) and LanguageLookupTests theories covering the containment matching and ExpandRegion, including the directional and backward-compatibility cases.
  • Full suite: 293/293 passing. Build clean with AOT reference verification enabled.

🤖 Generated with Claude Code

Resolve#175 - match a UN M.49 region group against a contained region,
e.g. es-419 (Latin America) matches es-MX (Mexico).
- Add UnM49Data sourced from the CLDR territoryContainment data, parsed
with XmlReader to keep the library AOT compatible, generated and
embedded following the existing dataset pattern.
- Add LanguageLookup.IsMatch(prefix, tag, regionContainment) as an opt-in
overload; the existing two argument IsMatch is unchanged.
- Add LanguageLookup.ExpandRegion to expand a region into its containing
UN M.49 groups.
- Fix ValidateExtendedLanguage to require 3 alpha so a numeric region
following the language parses as a region, e.g. es-419.
- Restore previously trimmed control flow comments in the parser and lookup.
- Update README and HISTORY, add UN M.49 references, bump version to 1.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings June 26, 2026 16:11

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds UN M.49 (CLDR territoryContainment) region containment support to the LanguageTags library so numeric region groups (e.g., 419) can be matched against contained regions (e.g., MX), and enables region expansion to ancestor group regions.

Changes:

  • Introduces UnM49Data (XML loader + JSON/codegen + embedded generated dataset) sourced from Unicode CLDR territory containment.
  • Adds LanguageLookup.IsMatch(prefix, tag, regionContainment) overload and LanguageLookup.ExpandRegion() for containment-based matching/expansion.
  • Fixes parsing so extlang requires 3ALPHA, ensuring numeric regions like es-419 parse as a region.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
version.jsonBumps library version to 1.4.
README.mdDocuments UN M.49 containment matching and region expansion; updates links and release-notes snippet.
HISTORY.mdAdds 1.4 release entry describing containment support and parser fix.
LanguageTagsCreate/CreateTagData.csExtends codegen pipeline to download/convert/generate UN M.49 data and code.
LanguageTags/UnM49Data.csImplements UN M.49 loader (XmlReader), JSON serialization, code generation, and query APIs (Find/Contains/GetAncestors).
LanguageTags/UnM49DataGen.csAdds generated embedded UN M.49 dataset used by UnM49Data.Create().
LanguageData/unm49.jsonAdds generated JSON form of the UN M.49 dataset.
LanguageTags/LanguageSchema.csRegisters UnM49Data in the source-generated JsonSerializerContext.
LanguageTags/LanguageLookup.csAdds containment-aware matching overload and ExpandRegion(); wires in UN M.49 dataset usage.
LanguageTags/LanguageTagParser.csTightens extlang validation to 3ALPHA so numeric regions don’t misparse as extlang; restores control-flow comments.
LanguageTagsTests/UnM49Tests.csAdds tests for UN M.49 dataset loading/round-tripping and basic query behavior.
LanguageTagsTests/LanguageLookupTests.csAdds tests for containment matching (directional + opt-in) and ExpandRegion().

Comment threadLanguageTags/LanguageLookup.cs
Comment threadLanguageTags/LanguageLookup.cs Outdated
Comment threadLanguageTags/UnM49Data.cs
Comment threadLanguageTags/UnM49Data.cs
- Region containment matching now substitutes the candidate region and
reuses the plain matcher, preserving variant, extension, and private use
semantics to avoid false positives, e.g. es-419-nedis no longer matches
es-MX while es-419 still matches es-MX-nedis.
- Clarify UnM49Data.Find returns the first of possibly multiple records for
a code, and point to GetAncestors/Contains for full transitive containment.
- Clarify UnM49Record.Code may be an alphabetic CLDR grouping code (EU, EZ, UN).
- Add region containment tests for script preservation and prefix variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Comment threadLanguageTags/LanguageLookup.cs
Comment threadREADME.md Outdated
"variant" has a specific RFC 5646 meaning, so describe the expanded
entries as region substituted tags instead, in the XML doc and README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 10cc912 into developJun 26, 2026
12 checks passed
@ptr727
ptr727 deleted the feature/unm49-containment-175 branch June 26, 2026 16:53
ptr727 added a commit that referenced this pull request Jun 26, 2026
Release: promote develop to main
- UN M.49 region containment (#193, version floor 1.4)
- Release-workflow fixes re-synced from template (#196: #213/#214/#217)
Conflicts in three workflow files were the parallel setup-dotnet dependabot
bump (identical on both branches); resolved to develop's authoritative
versions (newer template re-sync + the workflow fixes).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add UN M.49 region containment support - #193

Merged
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175
Jun 26, 2026
Merged

Add UN M.49 region containment support#193
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Resolves#175.

What

Matching a UN M.49 region group against a contained region now works, e.g. es-419 (Latin America and the Caribbean) matches es-MX (Mexico).

Changes

  • New UnM49Data dataset sourced from Unicode CLDR territoryContainment, parsed with XmlReader (reflection-free, keeps the library AOT compatible — XmlSerializer is avoided), then JSON converted and code generated following the existing ISO/RFC dataset pattern.
  • LanguageLookup.IsMatch(prefix, tag, regionContainment) — opt-in overload. The existing two-argument IsMatch is unchanged and delegates with regionContainment: false, so there is no behaviour change and no extra work in the default path. Matching is directional: the broad group in the prefix matches the specific region in the tag, not the reverse.
  • LanguageLookup.ExpandRegion — expands a tag region into the tag plus a variant per containing UN M.49 group, e.g. es-MXes-013, es-419, es-019, es-001.
  • Parser fixValidateExtendedLanguage now requires 3 alpha per RFC 5646 (extlang = 3ALPHA), so a numeric region following the language parses as a region instead of an extended language, e.g. es-419.
  • Restored previously trimmed control-flow comments in the parser and lookup.
  • Docs: README usage example + references, HISTORY entry, version.json bumped to 1.4.

Design notes

  • 419 is a grouping="true" overlay in CLDR (not a canonical tree node), so the loader keeps grouping overlays and skips only status="deprecated" entries — dropping overlays would remove 419 entirely.
  • 001 (World) is the universal ancestor, so es-001 matches any es tag with a region; documented in the API and covered by a test.

Testing

  • New UnM49Tests (dataset round-trip, Find, Contains, GetAncestors) and LanguageLookupTests theories covering the containment matching and ExpandRegion, including the directional and backward-compatibility cases.
  • Full suite: 293/293 passing. Build clean with AOT reference verification enabled.

🤖 Generated with Claude Code

Resolve#175 - match a UN M.49 region group against a contained region,
e.g. es-419 (Latin America) matches es-MX (Mexico).
- Add UnM49Data sourced from the CLDR territoryContainment data, parsed
with XmlReader to keep the library AOT compatible, generated and
embedded following the existing dataset pattern.
- Add LanguageLookup.IsMatch(prefix, tag, regionContainment) as an opt-in
overload; the existing two argument IsMatch is unchanged.
- Add LanguageLookup.ExpandRegion to expand a region into its containing
UN M.49 groups.
- Fix ValidateExtendedLanguage to require 3 alpha so a numeric region
following the language parses as a region, e.g. es-419.
- Restore previously trimmed control flow comments in the parser and lookup.
- Update README and HISTORY, add UN M.49 references, bump version to 1.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings June 26, 2026 16:11

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds UN M.49 (CLDR territoryContainment) region containment support to the LanguageTags library so numeric region groups (e.g., 419) can be matched against contained regions (e.g., MX), and enables region expansion to ancestor group regions.

Changes:

  • Introduces UnM49Data (XML loader + JSON/codegen + embedded generated dataset) sourced from Unicode CLDR territory containment.
  • Adds LanguageLookup.IsMatch(prefix, tag, regionContainment) overload and LanguageLookup.ExpandRegion() for containment-based matching/expansion.
  • Fixes parsing so extlang requires 3ALPHA, ensuring numeric regions like es-419 parse as a region.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
version.jsonBumps library version to 1.4.
README.mdDocuments UN M.49 containment matching and region expansion; updates links and release-notes snippet.
HISTORY.mdAdds 1.4 release entry describing containment support and parser fix.
LanguageTagsCreate/CreateTagData.csExtends codegen pipeline to download/convert/generate UN M.49 data and code.
LanguageTags/UnM49Data.csImplements UN M.49 loader (XmlReader), JSON serialization, code generation, and query APIs (Find/Contains/GetAncestors).
LanguageTags/UnM49DataGen.csAdds generated embedded UN M.49 dataset used by UnM49Data.Create().
LanguageData/unm49.jsonAdds generated JSON form of the UN M.49 dataset.
LanguageTags/LanguageSchema.csRegisters UnM49Data in the source-generated JsonSerializerContext.
LanguageTags/LanguageLookup.csAdds containment-aware matching overload and ExpandRegion(); wires in UN M.49 dataset usage.
LanguageTags/LanguageTagParser.csTightens extlang validation to 3ALPHA so numeric regions don’t misparse as extlang; restores control-flow comments.
LanguageTagsTests/UnM49Tests.csAdds tests for UN M.49 dataset loading/round-tripping and basic query behavior.
LanguageTagsTests/LanguageLookupTests.csAdds tests for containment matching (directional + opt-in) and ExpandRegion().

Comment threadLanguageTags/LanguageLookup.cs
Comment threadLanguageTags/LanguageLookup.cs Outdated
Comment threadLanguageTags/UnM49Data.cs
Comment threadLanguageTags/UnM49Data.cs
- Region containment matching now substitutes the candidate region and
reuses the plain matcher, preserving variant, extension, and private use
semantics to avoid false positives, e.g. es-419-nedis no longer matches
es-MX while es-419 still matches es-MX-nedis.
- Clarify UnM49Data.Find returns the first of possibly multiple records for
a code, and point to GetAncestors/Contains for full transitive containment.
- Clarify UnM49Record.Code may be an alphabetic CLDR grouping code (EU, EZ, UN).
- Add region containment tests for script preservation and prefix variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Comment threadLanguageTags/LanguageLookup.cs
Comment threadREADME.md Outdated
"variant" has a specific RFC 5646 meaning, so describe the expanded
entries as region substituted tags instead, in the XML doc and README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 10cc912 into developJun 26, 2026
12 checks passed
@ptr727
ptr727 deleted the feature/unm49-containment-175 branch June 26, 2026 16:53
ptr727 added a commit that referenced this pull request Jun 26, 2026
Release: promote develop to main
- UN M.49 region containment (#193, version floor 1.4)
- Release-workflow fixes re-synced from template (#196: #213/#214/#217)
Conflicts in three workflow files were the parallel setup-dotnet dependabot
bump (identical on both branches); resolved to develop's authoritative
versions (newer template re-sync + the workflow fixes).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Add UN M.49 region containment support - #193

Merged
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175
Jun 26, 2026
Merged

Add UN M.49 region containment support#193
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Resolves#175.

What

Matching a UN M.49 region group against a contained region now works, e.g. es-419 (Latin America and the Caribbean) matches es-MX (Mexico).

Changes

  • New UnM49Data dataset sourced from Unicode CLDR territoryContainment, parsed with XmlReader (reflection-free, keeps the library AOT compatible — XmlSerializer is avoided), then JSON converted and code generated following the existing ISO/RFC dataset pattern.
  • LanguageLookup.IsMatch(prefix, tag, regionContainment) — opt-in overload. The existing two-argument IsMatch is unchanged and delegates with regionContainment: false, so there is no behaviour change and no extra work in the default path. Matching is directional: the broad group in the prefix matches the specific region in the tag, not the reverse.
  • LanguageLookup.ExpandRegion — expands a tag region into the tag plus a variant per containing UN M.49 group, e.g. es-MXes-013, es-419, es-019, es-001.
  • Parser fixValidateExtendedLanguage now requires 3 alpha per RFC 5646 (extlang = 3ALPHA), so a numeric region following the language parses as a region instead of an extended language, e.g. es-419.
  • Restored previously trimmed control-flow comments in the parser and lookup.
  • Docs: README usage example + references, HISTORY entry, version.json bumped to 1.4.

Design notes

  • 419 is a grouping="true" overlay in CLDR (not a canonical tree node), so the loader keeps grouping overlays and skips only status="deprecated" entries — dropping overlays would remove 419 entirely.
  • 001 (World) is the universal ancestor, so es-001 matches any es tag with a region; documented in the API and covered by a test.

Testing

  • New UnM49Tests (dataset round-trip, Find, Contains, GetAncestors) and LanguageLookupTests theories covering the containment matching and ExpandRegion, including the directional and backward-compatibility cases.
  • Full suite: 293/293 passing. Build clean with AOT reference verification enabled.

🤖 Generated with Claude Code

Resolve#175 - match a UN M.49 region group against a contained region,
e.g. es-419 (Latin America) matches es-MX (Mexico).
- Add UnM49Data sourced from the CLDR territoryContainment data, parsed
with XmlReader to keep the library AOT compatible, generated and
embedded following the existing dataset pattern.
- Add LanguageLookup.IsMatch(prefix, tag, regionContainment) as an opt-in
overload; the existing two argument IsMatch is unchanged.
- Add LanguageLookup.ExpandRegion to expand a region into its containing
UN M.49 groups.
- Fix ValidateExtendedLanguage to require 3 alpha so a numeric region
following the language parses as a region, e.g. es-419.
- Restore previously trimmed control flow comments in the parser and lookup.
- Update README and HISTORY, add UN M.49 references, bump version to 1.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings June 26, 2026 16:11

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds UN M.49 (CLDR territoryContainment) region containment support to the LanguageTags library so numeric region groups (e.g., 419) can be matched against contained regions (e.g., MX), and enables region expansion to ancestor group regions.

Changes:

  • Introduces UnM49Data (XML loader + JSON/codegen + embedded generated dataset) sourced from Unicode CLDR territory containment.
  • Adds LanguageLookup.IsMatch(prefix, tag, regionContainment) overload and LanguageLookup.ExpandRegion() for containment-based matching/expansion.
  • Fixes parsing so extlang requires 3ALPHA, ensuring numeric regions like es-419 parse as a region.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
version.jsonBumps library version to 1.4.
README.mdDocuments UN M.49 containment matching and region expansion; updates links and release-notes snippet.
HISTORY.mdAdds 1.4 release entry describing containment support and parser fix.
LanguageTagsCreate/CreateTagData.csExtends codegen pipeline to download/convert/generate UN M.49 data and code.
LanguageTags/UnM49Data.csImplements UN M.49 loader (XmlReader), JSON serialization, code generation, and query APIs (Find/Contains/GetAncestors).
LanguageTags/UnM49DataGen.csAdds generated embedded UN M.49 dataset used by UnM49Data.Create().
LanguageData/unm49.jsonAdds generated JSON form of the UN M.49 dataset.
LanguageTags/LanguageSchema.csRegisters UnM49Data in the source-generated JsonSerializerContext.
LanguageTags/LanguageLookup.csAdds containment-aware matching overload and ExpandRegion(); wires in UN M.49 dataset usage.
LanguageTags/LanguageTagParser.csTightens extlang validation to 3ALPHA so numeric regions don’t misparse as extlang; restores control-flow comments.
LanguageTagsTests/UnM49Tests.csAdds tests for UN M.49 dataset loading/round-tripping and basic query behavior.
LanguageTagsTests/LanguageLookupTests.csAdds tests for containment matching (directional + opt-in) and ExpandRegion().

Comment threadLanguageTags/LanguageLookup.cs
Comment threadLanguageTags/LanguageLookup.cs Outdated
Comment threadLanguageTags/UnM49Data.cs
Comment threadLanguageTags/UnM49Data.cs
- Region containment matching now substitutes the candidate region and
reuses the plain matcher, preserving variant, extension, and private use
semantics to avoid false positives, e.g. es-419-nedis no longer matches
es-MX while es-419 still matches es-MX-nedis.
- Clarify UnM49Data.Find returns the first of possibly multiple records for
a code, and point to GetAncestors/Contains for full transitive containment.
- Clarify UnM49Record.Code may be an alphabetic CLDR grouping code (EU, EZ, UN).
- Add region containment tests for script preservation and prefix variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Comment threadLanguageTags/LanguageLookup.cs
Comment threadREADME.md Outdated
"variant" has a specific RFC 5646 meaning, so describe the expanded
entries as region substituted tags instead, in the XML doc and README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 10cc912 into developJun 26, 2026
12 checks passed
@ptr727
ptr727 deleted the feature/unm49-containment-175 branch June 26, 2026 16:53
ptr727 added a commit that referenced this pull request Jun 26, 2026
Release: promote develop to main
- UN M.49 region containment (#193, version floor 1.4)
- Release-workflow fixes re-synced from template (#196: #213/#214/#217)
Conflicts in three workflow files were the parallel setup-dotnet dependabot
bump (identical on both branches); resolved to develop's authoritative
versions (newer template re-sync + the workflow fixes).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ptr727
, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Add UN M.49 region containment support - #193

Merged
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175
Jun 26, 2026
Merged

Add UN M.49 region containment support#193
ptr727 merged 3 commits into
developfrom
feature/unm49-containment-175

Conversation

@ptr727

Copy link
Copy Markdown
Owner

Resolves#175.

What

Matching a UN M.49 region group against a contained region now works, e.g. es-419 (Latin America and the Caribbean) matches es-MX (Mexico).

Changes

  • New UnM49Data dataset sourced from Unicode CLDR territoryContainment, parsed with XmlReader (reflection-free, keeps the library AOT compatible — XmlSerializer is avoided), then JSON converted and code generated following the existing ISO/RFC dataset pattern.
  • LanguageLookup.IsMatch(prefix, tag, regionContainment) — opt-in overload. The existing two-argument IsMatch is unchanged and delegates with regionContainment: false, so there is no behaviour change and no extra work in the default path. Matching is directional: the broad group in the prefix matches the specific region in the tag, not the reverse.
  • LanguageLookup.ExpandRegion — expands a tag region into the tag plus a variant per containing UN M.49 group, e.g. es-MXes-013, es-419, es-019, es-001.
  • Parser fixValidateExtendedLanguage now requires 3 alpha per RFC 5646 (extlang = 3ALPHA), so a numeric region following the language parses as a region instead of an extended language, e.g. es-419.
  • Restored previously trimmed control-flow comments in the parser and lookup.
  • Docs: README usage example + references, HISTORY entry, version.json bumped to 1.4.

Design notes

  • 419 is a grouping="true" overlay in CLDR (not a canonical tree node), so the loader keeps grouping overlays and skips only status="deprecated" entries — dropping overlays would remove 419 entirely.
  • 001 (World) is the universal ancestor, so es-001 matches any es tag with a region; documented in the API and covered by a test.

Testing

  • New UnM49Tests (dataset round-trip, Find, Contains, GetAncestors) and LanguageLookupTests theories covering the containment matching and ExpandRegion, including the directional and backward-compatibility cases.
  • Full suite: 293/293 passing. Build clean with AOT reference verification enabled.

🤖 Generated with Claude Code

Resolve#175 - match a UN M.49 region group against a contained region,
e.g. es-419 (Latin America) matches es-MX (Mexico).
- Add UnM49Data sourced from the CLDR territoryContainment data, parsed
with XmlReader to keep the library AOT compatible, generated and
embedded following the existing dataset pattern.
- Add LanguageLookup.IsMatch(prefix, tag, regionContainment) as an opt-in
overload; the existing two argument IsMatch is unchanged.
- Add LanguageLookup.ExpandRegion to expand a region into its containing
UN M.49 groups.
- Fix ValidateExtendedLanguage to require 3 alpha so a numeric region
following the language parses as a region, e.g. es-419.
- Restore previously trimmed control flow comments in the parser and lookup.
- Update README and HISTORY, add UN M.49 references, bump version to 1.4.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
CopilotAI review requested due to automatic review settings June 26, 2026 16:11

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Adds UN M.49 (CLDR territoryContainment) region containment support to the LanguageTags library so numeric region groups (e.g., 419) can be matched against contained regions (e.g., MX), and enables region expansion to ancestor group regions.

Changes:

  • Introduces UnM49Data (XML loader + JSON/codegen + embedded generated dataset) sourced from Unicode CLDR territory containment.
  • Adds LanguageLookup.IsMatch(prefix, tag, regionContainment) overload and LanguageLookup.ExpandRegion() for containment-based matching/expansion.
  • Fixes parsing so extlang requires 3ALPHA, ensuring numeric regions like es-419 parse as a region.

Reviewed changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
FileDescription
version.jsonBumps library version to 1.4.
README.mdDocuments UN M.49 containment matching and region expansion; updates links and release-notes snippet.
HISTORY.mdAdds 1.4 release entry describing containment support and parser fix.
LanguageTagsCreate/CreateTagData.csExtends codegen pipeline to download/convert/generate UN M.49 data and code.
LanguageTags/UnM49Data.csImplements UN M.49 loader (XmlReader), JSON serialization, code generation, and query APIs (Find/Contains/GetAncestors).
LanguageTags/UnM49DataGen.csAdds generated embedded UN M.49 dataset used by UnM49Data.Create().
LanguageData/unm49.jsonAdds generated JSON form of the UN M.49 dataset.
LanguageTags/LanguageSchema.csRegisters UnM49Data in the source-generated JsonSerializerContext.
LanguageTags/LanguageLookup.csAdds containment-aware matching overload and ExpandRegion(); wires in UN M.49 dataset usage.
LanguageTags/LanguageTagParser.csTightens extlang validation to 3ALPHA so numeric regions don’t misparse as extlang; restores control-flow comments.
LanguageTagsTests/UnM49Tests.csAdds tests for UN M.49 dataset loading/round-tripping and basic query behavior.
LanguageTagsTests/LanguageLookupTests.csAdds tests for containment matching (directional + opt-in) and ExpandRegion().

Comment threadLanguageTags/LanguageLookup.cs
Comment threadLanguageTags/LanguageLookup.cs Outdated
Comment threadLanguageTags/UnM49Data.cs
Comment threadLanguageTags/UnM49Data.cs
- Region containment matching now substitutes the candidate region and
reuses the plain matcher, preserving variant, extension, and private use
semantics to avoid false positives, e.g. es-419-nedis no longer matches
es-MX while es-419 still matches es-MX-nedis.
- Clarify UnM49Data.Find returns the first of possibly multiple records for
a code, and point to GetAncestors/Contains for full transitive containment.
- Clarify UnM49Record.Code may be an alphabetic CLDR grouping code (EU, EZ, UN).
- Add region containment tests for script preservation and prefix variants.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated 2 comments.

Comment threadLanguageTags/LanguageLookup.cs
Comment threadREADME.md Outdated
"variant" has a specific RFC 5646 meaning, so describe the expanded
entries as region substituted tags instead, in the XML doc and README.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>

CopilotAI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 12 out of 13 changed files in this pull request and generated no new comments.

@ptr727
ptr727 merged commit 10cc912 into developJun 26, 2026
12 checks passed
@ptr727
ptr727 deleted the feature/unm49-containment-175 branch June 26, 2026 16:53
ptr727 added a commit that referenced this pull request Jun 26, 2026
Release: promote develop to main
- UN M.49 region containment (#193, version floor 1.4)
- Release-workflow fixes re-synced from template (#196: #213/#214/#217)
Conflicts in three workflow files were the parallel setup-dotnet dependabot
bump (identical on both branches); resolved to develop's authoritative
versions (newer template re-sync + the workflow fixes).
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants

@ptr727