Skip to content

Comments

V10.3.1/service update#150

Open
gimlichael wants to merge 5 commits intomainfrom
v10.3.1/service-update
Open

V10.3.1/service update#150
gimlichael wants to merge 5 commits intomainfrom
v10.3.1/service-update

Conversation

@gimlichael
Copy link
Member

@gimlichael gimlichael commented Feb 22, 2026

This pull request makes improvements to the handling of cultures and regions in the World class, with a focus on filtering out invalid entries and ensuring backward compatibility with ISO region codes. It also adds a comprehensive test to verify that all expected ISO region codes are present. The main changes are grouped below by theme.

Enhancements to region and culture handling:

  • Updated the filtering logic in World.SpecificCultures to exclude cultures with LCID 127, which are incorrectly categorized as specific cultures on some Linux systems.
  • Improved the region collection in World.Regions by skipping statistical regions (numeric codes), using TryAdd for uniqueness, and ordering the results by region name.
  • Added a comment to clarify the exclusion of cultures with LCID 127 for cross-platform consistency.

Codebase improvements:

  • Refactored the addition of cultures and regions to use Decorator.Enclose(...).TryAdd(...) for safer and cleaner collection handling. [1] [2]
  • Added a new test Regions_ShouldContainAllExpectedIsoRegionCodes_ForBackwardCompatibility in WorldTest.cs to verify that all expected ISO region codes are present, ensuring backward compatibility and providing detailed output for missing or added codes.

Summary by CodeRabbit

  • Bug Fixes

    • Improved culture and region data filtering to exclude edge-case and numeric/statistical entries for cleaner, more consistent region lists.
    • Safer mapping of country ISO codes to region data, reducing errors when lookups are missing.
  • Tests

    • Updated region-related tests to validate expected ISO codes and provide more detailed diagnostics for divergences across frameworks.

@gimlichael gimlichael self-assigned this Feb 22, 2026
Copilot AI review requested due to automatic review settings February 22, 2026 14:08
@coderabbitai
Copy link

coderabbitai bot commented Feb 22, 2026

📝 Walkthrough

Walkthrough

Updates adjust how World populates culture and region dictionaries (using Decorator.Enclose(...).TryAdd), filter out edge-case cultures and numeric region names, change region ordering, modify WorldTest to a backward-compatibility-focused ISO region code test with richer diagnostics, and switch UnM49DataContainer to use a Region lookup cache built from World.Regions for safer RegionInfo resolution.

Changes

Cohort / File(s) Summary
World population and ordering
src/Cuemon.Core/Globalization/World.cs
Replaces direct dictionary Add with Decorator.Enclose(...).TryAdd(...), adds using Cuemon.Collections.Generic, filters out cultures with LCID == 127, skips numeric Region.Name entries, and orders Regions by RegionInfo.Name then RegionInfo.NativeName.
Tests — ISO region compatibility
test/Cuemon.Core.Tests/Globalization/WorldTest.cs
Removes Regions_ShouldReturnAllRegions; adds Regions_ShouldContainAllExpectedIsoRegionCodes_ForBackwardCompatibility that compares expected ISO Alpha-2 set to World.Regions with conditional NET48 vs non-NET48 assertions, plus richer logging of missing/added codes and region frequency diagnostics.
Region lookup safety in data container
src/Cuemon.Core/Globalization/UnM49DataContainer.cs
Adds a regionsByIsoAlpha2 cache built from World.Regions and replaces the previous FirstOrDefault/try-catch RegionInfo lookup with a safe dictionary lookup keyed by ISO Alpha-2 when present.

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Poem

🐇 I hopped through cultures, tidy and spry,

Skipping numeric names that made me sigh.
I cached my regions, neat in a row,
Tests now whisper what I already know.
A rabbit's wink — the world can grow. 🥕

🚥 Pre-merge checks | ✅ 1 | ❌ 2

❌ Failed checks (1 warning, 1 inconclusive)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
Title check ❓ Inconclusive The title 'V10.3.1/service update' is vague and generic, using non-descriptive version/branch naming without conveying the actual changes made to cultures, regions, or test improvements. Replace with a more descriptive title such as 'Refactor region and culture handling with improved filtering and caching' or 'Add ISO region code validation and optimize region lookup'.
✅ Passed checks (1 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
  • 📝 Generate docstrings (stacked PR)
  • 📝 Generate docstrings (commit on current branch)
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch v10.3.1/service-update

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This pull request aims to improve the World class's handling of cultures and regions for better cross-platform consistency and backward compatibility with ISO region codes. However, the implementation contains critical bugs that prevent it from achieving its goals.

Changes:

  • Added filtering of cultures with LCID 127 to prevent incorrectly categorized invariant cultures on Linux systems
  • Refactored dictionary operations to use Decorator.Enclose().TryAdd() for safer collection handling
  • Added filtering of statistical regions (numeric codes) from the regions collection
  • Added ordering of regions by ISO code name and a comprehensive backward compatibility test

Reviewed changes

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

File Description
src/Cuemon.Core/Globalization/World.cs Modified culture and region collection logic, added filtering for LCID 127 and statistical regions, changed dictionary keys and added ordering
test/Cuemon.Core.Tests/Globalization/WorldTest.cs Added comprehensive test to verify all expected ISO region codes are present for backward compatibility
Comments suppressed due to low confidence (2)

src/Cuemon.Core/Globalization/World.cs:18

  • The comment is excessively long and should be more concise. Additionally, it uses "should not happen" which is informal phrasing.

Consider shortening to:

// Filter out cultures with LCID 127 (invariant culture) that may be incorrectly categorized as specific cultures on some Linux systems.
            foreach (var c in specificCultures.Where(ci => ci.LCID != 127)) // *should* not happen for specific cultures, but on some linux based systems there are some cultures with LCID 127 (invariant culture) that are incorrectly categorized as specific cultures, so we need to filter those out.

src/Cuemon.Core/Globalization/World.cs:20

  • The key for the cultures dictionary has been changed from c.DisplayName to c.Name. While c.Name is a more appropriate key (it's the culture identifier like "en-US"), this change differs from the previous behavior.

The old code used c.DisplayName as the key, which could be non-unique across different locales (e.g., "English (United States)" could appear differently depending on the current culture). Using c.Name (the culture identifier) as the key is more correct and ensures uniqueness.

However, verify that this change doesn't break any existing code that might depend on the ordering or content of the collection. The SortedList will now sort by culture name (e.g., "ar-SA", "en-US", "zh-CN") instead of display name.

                Decorator.Enclose(cultures).TryAdd(c.Name, c);

Comment on lines +60 to +64
TestOutput.WriteLine($"Missing: {code} - {World.Regions.SingleOrDefault(info => info.Name == code).EnglishName}");
}
foreach (var code in added)
{
TestOutput.WriteLine($"Added: {code} - {World.Regions.Last(info => info.Name == code).EnglishName}");
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

The test iterates through missing codes and tries to find the region info with World.Regions.SingleOrDefault(info => info.Name == code). However, when a code is missing, this will return null, and attempting to access .EnglishName on null will cause a NullReferenceException. The same issue exists on line 64 with the added codes, though using .Last() instead of .SingleOrDefault().

Consider changing line 60 to handle null values:

TestOutput.WriteLine($"Missing: {code} - {World.Regions.SingleOrDefault(info => info.Name == code)?.EnglishName ?? "Not found"}");

And line 64 should use .LastOrDefault() to be safe:

TestOutput.WriteLine($"Added: {code} - {World.Regions.LastOrDefault(info => info.Name == code)?.EnglishName ?? "Unknown"}");
Suggested change
TestOutput.WriteLine($"Missing: {code} - {World.Regions.SingleOrDefault(info => info.Name == code).EnglishName}");
}
foreach (var code in added)
{
TestOutput.WriteLine($"Added: {code} - {World.Regions.Last(info => info.Name == code).EnglishName}");
TestOutput.WriteLine($"Missing: {code} - {World.Regions.SingleOrDefault(info => info.Name == code)?.EnglishName ?? "Not found"}");
}
foreach (var code in added)
{
TestOutput.WriteLine($"Added: {code} - {World.Regions.LastOrDefault(info => info.Name == code)?.EnglishName ?? "Unknown"}");

Copilot uses AI. Check for mistakes.
var region = new RegionInfo(c.Name);
if (!regions.ContainsKey(region.EnglishName)) { regions.Add(region.EnglishName, region); }
if (int.TryParse(region.Name, out _)) { continue; } // Skip statistical regions (why would Microsoft even consider having these as part of RegionInfo? No valid ISO 3166-1 alpha-2/3 code can be all digits, so these are not actual regions or countries.)
Decorator.Enclose(regions).TryAdd(c.Name, region);
Copy link

Copilot AI Feb 22, 2026

Choose a reason for hiding this comment

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

The key for the regions dictionary has been changed from region.EnglishName to c.Name (the culture name). This creates duplicate RegionInfo entries in the final collection.

When RegionInfo is created from a culture name like "en-US", the resulting region.Name property is "US" (the ISO region code). However, the dictionary key is set to c.Name ("en-US"), not region.Name ("US"). This means:

  • "en-US" -> RegionInfo for "US"
  • "es-US" -> RegionInfo for "US" (duplicate)

The final collection will contain multiple RegionInfo objects for the same region. While line 34 orders by info.Name (the ISO code), it doesn't deduplicate.

To fix this and ensure one region per ISO code while maintaining backward compatibility, the key should be region.Name (the ISO code):

Decorator.Enclose(regions).TryAdd(region.Name, region);

This would ensure only one RegionInfo per unique ISO region code, which aligns with the test's expectation of checking unique r.Name values.

Suggested change
Decorator.Enclose(regions).TryAdd(c.Name, region);
Decorator.Enclose(regions).TryAdd(region.Name, region);

Copilot uses AI. Check for mistakes.
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 2

🧹 Nitpick comments (1)
src/Cuemon.Core/Globalization/World.cs (1)

34-34: Materialise the OrderBy result to avoid re-sorting on every enumeration.

regions.Values.OrderBy(...) returns a lazy IOrderedEnumerable<RegionInfo>. This lazy sequence is stored in the Lazy<> value and then directly in the Regions static property, meaning every caller that iterates World.Regions triggers a full sort pass over all entries. A single .ToList() (or .ToArray()) call inside the Lazy initializer would sort once and amortise the cost.

⚡ Proposed fix — materialise once
-            return regions.Values.OrderBy(info => info.Name);
+            return regions.Values.OrderBy(info => info.Name).ToList();
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Cuemon.Core/Globalization/World.cs` at line 34, The current Lazy
initializer returns regions.Values.OrderBy(info => info.Name) which yields a
lazy IOrderedEnumerable and causes World.Regions to re-sort on each enumeration;
update the Lazy value produced by the initializer (the expression that builds
Regions) to materialize the ordered sequence (e.g., append .ToList() or
.ToArray() to regions.Values.OrderBy(info => info.Name)) so the sort is
performed once and subsequent enumerations use the cached materialized
collection.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/Cuemon.Core/Globalization/World.cs`:
- Around line 31-34: World.Regions is adding duplicate RegionInfo entries
because TryAdd is using the culture key c.Name (e.g., "en-US") instead of the
region key; change the TryAdd call in the loop that iterates
SpecificCultures.Value so it keys the SortedList by the region identifier (use
region.Name, or better region.TwoLetterISORegionName) instead of c.Name,
ensuring Decorator.Enclose(regions).TryAdd(...) deduplicates actual regions and
preserves StringComparer.OrdinalIgnoreCase semantics.

In `@test/Cuemon.Core.Tests/Globalization/WorldTest.cs`:
- Around line 56-65: The test throws a NullReferenceException because missing
codes come from expectedTwoLetterIsoCodes but aren't present in World.Regions,
and the code calls .EnglishName on a null result while also re-enumerating
World.Regions; fix by reusing the materialized collection sut1 (the list derived
from World.Regions) for lookups and perform a null-safe lookup when writing
TestOutput.WriteLine for both missing and added: retrieve the matching
RegionInfo from sut1 (e.g., via FirstOrDefault/SingleOrDefault), check for null
and fall back to printing the code or a placeholder instead of accessing
.EnglishName on null, and avoid re-enumerating World.Regions inside the loops.

---

Nitpick comments:
In `@src/Cuemon.Core/Globalization/World.cs`:
- Line 34: The current Lazy initializer returns regions.Values.OrderBy(info =>
info.Name) which yields a lazy IOrderedEnumerable and causes World.Regions to
re-sort on each enumeration; update the Lazy value produced by the initializer
(the expression that builds Regions) to materialize the ordered sequence (e.g.,
append .ToList() or .ToArray() to regions.Values.OrderBy(info => info.Name)) so
the sort is performed once and subsequent enumerations use the cached
materialized collection.

Comment on lines 31 to 34
if (int.TryParse(region.Name, out _)) { continue; } // Skip statistical regions (why would Microsoft even consider having these as part of RegionInfo? No valid ISO 3166-1 alpha-2/3 code can be all digits, so these are not actual regions or countries.)
Decorator.Enclose(regions).TryAdd(c.Name, region);
}
return regions.Values;
return regions.Values.OrderBy(info => info.Name);
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

World.Regions will contain duplicate RegionInfo entries for the same country.

The SortedList key is c.Name (e.g., "en-US", "es-US", "haw-US"), so all three map to the same RegionInfo("US") but are stored under different keys. Because SpecificCultures.Value already guarantees unique culture names, TryAdd(c.Name, ...) can never encounter a collision here — it provides no deduplication benefit whatsoever. Every distinct culture that shares a region will add another RegionInfo clone to regions.Values, and the OrderBy on line 34 does not deduplicate them.

For true region-level uniqueness the key must be region.Name (the two-letter ISO code), which is also what StringComparer.OrdinalIgnoreCase on the SortedList is semantically suited for. This matches the PR's stated intent ("use TryAdd for uniqueness") and what the AI-generated summary describes.

Additionally, the TwoLetterISORegionName should be preferred over RegionInfo.Name as key since Name can occasionally be a locale-qualified tag on some runtimes, while TwoLetterISORegionName is always the two-letter ISO 3166-1 alpha-2 code — though region.Name is what the test validates against.

🐛 Proposed fix — key regions by region.Name
-                Decorator.Enclose(regions).TryAdd(c.Name, region);
+                Decorator.Enclose(regions).TryAdd(region.Name, region);
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/Cuemon.Core/Globalization/World.cs` around lines 31 - 34, World.Regions
is adding duplicate RegionInfo entries because TryAdd is using the culture key
c.Name (e.g., "en-US") instead of the region key; change the TryAdd call in the
loop that iterates SpecificCultures.Value so it keys the SortedList by the
region identifier (use region.Name, or better region.TwoLetterISORegionName)
instead of c.Name, ensuring Decorator.Enclose(regions).TryAdd(...) deduplicates
actual regions and preserves StringComparer.OrdinalIgnoreCase semantics.

Comment on lines +56 to +65
var missing = expectedTwoLetterIsoCodes.Except(actualCodes).OrderBy(c => c).ToList();
var added = actualCodes.Except(expectedTwoLetterIsoCodes).OrderBy(c => c).ToList();
foreach (var code in missing)
{
TestOutput.WriteLine($"Missing: {code} - {World.Regions.SingleOrDefault(info => info.Name == code).EnglishName}");
}
foreach (var code in added)
{
TestOutput.WriteLine($"Added: {code} - {World.Regions.Last(info => info.Name == code).EnglishName}");
}
Copy link

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor

NullReferenceException on line 60 when a code is absent from World.Regions.

missing contains ISO codes that are in expectedTwoLetterIsoCodes but not in actualCodes (which comes from World.Regions). Calling World.Regions.SingleOrDefault(info => info.Name == code) for such a code returns null (RegionInfo is a reference type), and then .EnglishName throws before Assert.Empty(missing) is ever reached — replacing a clean test failure with an opaque NullReferenceException.

Additionally, World.Regions is re-enumerated inside both loops (lines 60 and 64) instead of reusing the already-materialized sut1.

🐛 Proposed fix — null-safe lookup, reuse sut1
-            foreach (var code in missing)
-            {
-                TestOutput.WriteLine($"Missing: {code} - {World.Regions.SingleOrDefault(info => info.Name == code).EnglishName}");
-            }
-            foreach (var code in added)
-            {
-                TestOutput.WriteLine($"Added: {code} - {World.Regions.Last(info => info.Name == code).EnglishName}");
-            }
+            foreach (var code in missing)
+            {
+                TestOutput.WriteLine($"Missing: {code} - {sut1.SingleOrDefault(info => info.Name == code)?.EnglishName ?? "(not found)"}");
+            }
+            foreach (var code in added)
+            {
+                TestOutput.WriteLine($"Added: {code} - {sut1.Last(info => info.Name == code).EnglishName}");
+            }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
var missing = expectedTwoLetterIsoCodes.Except(actualCodes).OrderBy(c => c).ToList();
var added = actualCodes.Except(expectedTwoLetterIsoCodes).OrderBy(c => c).ToList();
foreach (var code in missing)
{
TestOutput.WriteLine($"Missing: {code} - {World.Regions.SingleOrDefault(info => info.Name == code).EnglishName}");
}
foreach (var code in added)
{
TestOutput.WriteLine($"Added: {code} - {World.Regions.Last(info => info.Name == code).EnglishName}");
}
var missing = expectedTwoLetterIsoCodes.Except(actualCodes).OrderBy(c => c).ToList();
var added = actualCodes.Except(expectedTwoLetterIsoCodes).OrderBy(c => c).ToList();
foreach (var code in missing)
{
TestOutput.WriteLine($"Missing: {code} - {sut1.SingleOrDefault(info => info.Name == code)?.EnglishName ?? "(not found)"}");
}
foreach (var code in added)
{
TestOutput.WriteLine($"Added: {code} - {sut1.Last(info => info.Name == code).EnglishName}");
}
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@test/Cuemon.Core.Tests/Globalization/WorldTest.cs` around lines 56 - 65, The
test throws a NullReferenceException because missing codes come from
expectedTwoLetterIsoCodes but aren't present in World.Regions, and the code
calls .EnglishName on a null result while also re-enumerating World.Regions; fix
by reusing the materialized collection sut1 (the list derived from
World.Regions) for lookups and perform a null-safe lookup when writing
TestOutput.WriteLine for both missing and added: retrieve the matching
RegionInfo from sut1 (e.g., via FirstOrDefault/SingleOrDefault), check for null
and fall back to printing the code or a placeholder instead of accessing
.EnglishName on null, and avoid re-enumerating World.Regions inside the loops.

@codecov
Copy link

codecov bot commented Feb 22, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 80.69%. Comparing base (32af67f) to head (80b86a5).

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #150      +/-   ##
==========================================
+ Coverage   80.66%   80.69%   +0.02%     
==========================================
  Files         600      600              
  Lines       18932    18939       +7     
  Branches     1949     1951       +2     
==========================================
+ Hits        15272    15282      +10     
+ Misses       3592     3589       -3     
  Partials       68       68              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

Comment on lines +146 to +152
foreach (var r in World.Regions)
{
if (!regionsByIsoAlpha2.ContainsKey(r.TwoLetterISORegionName))
{
regionsByIsoAlpha2[r.TwoLetterISORegionName] = r;
}
}
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Duplicate comments:
In `@src/Cuemon.Core/Globalization/World.cs`:
- Line 32: The current dedupe key uses $"{region.Name}:{region.NativeName}"
which allows multiple RegionInfo entries per ISO code; change the uniqueness key
in the Decorator.Enclose(regions).TryAdd call to use the ISO code only (e.g.
region.TwoLetterISORegionName or region.Name) so entries are unique by ISO code,
or if multi-entry per ISO is intended update the World.Regions XML doc to
explicitly state that Regions may contain multiple RegionInfo objects per ISO
code and describe implications for consumers; locate the
Decorator.Enclose(regions).TryAdd invocation in World.cs and update either the
key expression to region.TwoLetterISORegionName (or region.Name) or the XML
comment for Regions accordingly.

In `@test/Cuemon.Core.Tests/Globalization/WorldTest.cs`:
- Around line 58-65: The test currently dereferences null when logging
missing/added entries and re-enumerates World.Regions; change the diagnostic
loops to use the already-materialized sut1 collection (instead of World.Regions)
and perform a null-safe lookup (e.g., FirstOrDefault/SingleOrDefault into a
local variable) before accessing EnglishName, writing a clear message that
handles null (e.g., "not found") via TestOutput.WriteLine for each code, and
ensure these logs run before Assert.Empty(missing) so the assertion failure is
visible.

@sonarqubecloud
Copy link

Sign up for free to 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.

1 participant