Skip to content

Commit 798c8aa

Browse files
committed
Fix case-insensitive key lookups in view and data processing commands
Fixed bug where case-insensitive key matching was incorrectly matching different keys (e.g., "Next" and "next") in filtering and data operations. Changes: - ViewCommand: Fix --status filter to use culture-filtered files when --cultures is specified, and use exact key matching in FilterByStatus - ExportCommand: Use exact key matching (6 occurrences) - ImportCommand: Use exact key matching (1 occurrence) - ValidateCommand: Use exact key matching (1 occurrence) Impact: - view --status empty now correctly filters by specified culture - view --status empty excludes keys with values (e.g., "Next" with "Nextcloud") - Export/Import/Validate commands now handle case-variant keys correctly All 1,187 tests passing.
1 parent 1eefde5 commit 798c8aa

File tree

4 files changed

+22
-18
lines changed

4 files changed

+22
-18
lines changed

Commands/ExportCommand.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -230,7 +230,7 @@ private void ExportToCsv(List<ResourceFile> resourceFiles, string outputFile, Co
230230
// Add values for each language
231231
foreach (var resourceFile in resourceFiles)
232232
{
233-
var entry = resourceFile.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
233+
var entry = resourceFile.Entries.FirstOrDefault(e => e.Key == key);
234234
row.Add(entry?.Value ?? string.Empty);
235235
}
236236

@@ -242,7 +242,7 @@ private void ExportToCsv(List<ResourceFile> resourceFiles, string outputFile, Co
242242
}
243243

244244
// Add comment (from default language)
245-
var defaultEntry = defaultFile.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
245+
var defaultEntry = defaultFile.Entries.FirstOrDefault(e => e.Key == key);
246246
row.Add(defaultEntry?.Comment ?? string.Empty);
247247

248248
sb.AppendLine(EscapeCsvRow(row));
@@ -317,11 +317,11 @@ private void ExportToJson(List<ResourceFile> resourceFiles, string outputFile, C
317317
var translations = new Dictionary<string, string?>();
318318
foreach (var resourceFile in resourceFiles)
319319
{
320-
var entry = resourceFile.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
320+
var entry = resourceFile.Entries.FirstOrDefault(e => e.Key == key);
321321
translations[resourceFile.Language.Name] = entry?.Value;
322322
}
323323

324-
var defaultEntry = defaultFile.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
324+
var defaultEntry = defaultFile.Entries.FirstOrDefault(e => e.Key == key);
325325

326326
var item = new Dictionary<string, object?>
327327
{
@@ -375,13 +375,13 @@ private void ExportToSimple(List<ResourceFile> resourceFiles, string outputFile,
375375
// Add translations for each language
376376
foreach (var resourceFile in resourceFiles)
377377
{
378-
var entry = resourceFile.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
378+
var entry = resourceFile.Entries.FirstOrDefault(e => e.Key == key);
379379
var value = entry?.Value ?? "(empty)";
380380
sb.AppendLine($" {resourceFile.Language.Name}: {value}");
381381
}
382382

383383
// Add comment
384-
var defaultEntry = defaultFile.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
384+
var defaultEntry = defaultFile.Entries.FirstOrDefault(e => e.Key == key);
385385
if (!string.IsNullOrEmpty(defaultEntry?.Comment))
386386
{
387387
sb.AppendLine($" Comment: {defaultEntry.Comment}");

Commands/ImportCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -344,7 +344,7 @@ private ImportStats ImportData(
344344
}
345345

346346
var newValue = values[langName];
347-
var existingEntry = resourceFile.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
347+
var existingEntry = resourceFile.Entries.FirstOrDefault(e => e.Key == key);
348348

349349
if (existingEntry != null)
350350
{

Commands/ValidateCommand.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -698,7 +698,7 @@ private void ScanCodeForDuplicates(
698698
// Find all case variants in resource files
699699
var variants = resourceFiles
700700
.SelectMany(rf => rf.Entries)
701-
.Where(e => e.Key.Equals(normalizedKey, StringComparison.OrdinalIgnoreCase))
701+
.Where(e => e.Key == normalizedKey)
702702
.Select(e => e.Key)
703703
.Distinct()
704704
.ToList();

Commands/ViewCommand.cs

Lines changed: 14 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -263,13 +263,15 @@ public override int Execute(CommandContext context, Settings settings, Cancellat
263263
matchedKeys = matchedKeys.OrderBy(k => k).ToList();
264264
}
265265

266-
// Keep original resource files for status filtering (needs all languages)
266+
// Keep original resource files before filtering
267267
var allResourceFiles = resourceFiles;
268+
var hasCultureFilter = false;
268269

269270
// Apply culture filtering for display
270271
List<string> invalidCodes;
271272
if (!string.IsNullOrEmpty(settings.Cultures) || !string.IsNullOrEmpty(settings.ExcludeCultures))
272273
{
274+
hasCultureFilter = true;
273275
var originalCount = resourceFiles.Count;
274276
resourceFiles = FilterResourceFiles(resourceFiles, settings, out invalidCodes);
275277

@@ -294,10 +296,12 @@ public override int Execute(CommandContext context, Settings settings, Cancellat
294296
}
295297
}
296298

297-
// Apply status filtering (use original resource files for status checks)
299+
// Apply status filtering
300+
// Use filtered resource files if cultures were specified, otherwise use all
298301
if (settings.Status.HasValue)
299302
{
300-
matchedKeys = FilterByStatus(matchedKeys, defaultFile, allResourceFiles, settings.Status.Value);
303+
var filesForStatusCheck = hasCultureFilter ? resourceFiles : allResourceFiles;
304+
matchedKeys = FilterByStatus(matchedKeys, defaultFile, filesForStatusCheck, settings.Status.Value);
301305
}
302306

303307
// Apply exclusion patterns
@@ -1243,25 +1247,25 @@ public static List<string> FilterByStatus(
12431247
// Check if any NON-DEFAULT language has empty/whitespace value
12441248
includeKey = resourceFiles.Where(rf => !rf.Language.IsDefault).Any(rf =>
12451249
{
1246-
var entry = rf.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
1250+
var entry = rf.Entries.FirstOrDefault(e => e.Key == key);
12471251
return entry == null || string.IsNullOrWhiteSpace(entry.Value);
12481252
});
12491253
break;
12501254

12511255
case TranslationStatus.Missing:
12521256
// Check if key is missing in any NON-DEFAULT language file
12531257
includeKey = resourceFiles.Where(rf => !rf.Language.IsDefault).Any(rf =>
1254-
!rf.Entries.Any(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase)));
1258+
!rf.Entries.Any(e => e.Key == key));
12551259
break;
12561260

12571261
case TranslationStatus.Untranslated:
12581262
// Check if any language is missing, empty, or identical to default
1259-
var defaultEntry = defaultFile.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
1263+
var defaultEntry = defaultFile.Entries.FirstOrDefault(e => e.Key == key);
12601264
var defaultValue = defaultEntry?.Value ?? "";
12611265

12621266
includeKey = resourceFiles.Where(rf => !rf.Language.IsDefault).Any(rf =>
12631267
{
1264-
var entry = rf.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
1268+
var entry = rf.Entries.FirstOrDefault(e => e.Key == key);
12651269
// Missing, empty, or same as default
12661270
return entry == null ||
12671271
string.IsNullOrWhiteSpace(entry.Value) ||
@@ -1280,7 +1284,7 @@ public static List<string> FilterByStatus(
12801284
{
12811285
includeKey = resourceFiles.All(rf =>
12821286
{
1283-
var entry = rf.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
1287+
var entry = rf.Entries.FirstOrDefault(e => e.Key == key);
12841288
return entry != null && !string.IsNullOrWhiteSpace(entry.Value);
12851289
});
12861290
}
@@ -1289,7 +1293,7 @@ public static List<string> FilterByStatus(
12891293
case TranslationStatus.Partial:
12901294
// Has some translations but not all (or some are empty)
12911295
// First verify default entry exists and is non-empty
1292-
var defaultPartialEntry = defaultFile.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
1296+
var defaultPartialEntry = defaultFile.Entries.FirstOrDefault(e => e.Key == key);
12931297
if (defaultPartialEntry == null || string.IsNullOrWhiteSpace(defaultPartialEntry.Value))
12941298
{
12951299
// Default is broken, don't report as partial (should be caught by validate)
@@ -1302,7 +1306,7 @@ public static List<string> FilterByStatus(
13021306

13031307
foreach (var rf in resourceFiles.Where(rf => !rf.Language.IsDefault))
13041308
{
1305-
var entry = rf.Entries.FirstOrDefault(e => e.Key.Equals(key, StringComparison.OrdinalIgnoreCase));
1309+
var entry = rf.Entries.FirstOrDefault(e => e.Key == key);
13061310
if (entry != null && !string.IsNullOrWhiteSpace(entry.Value))
13071311
{
13081312
hasAnyTranslation = true;

0 commit comments

Comments
 (0)