From e9b9d9702e6249f4f4f64136cbab88ee7992b95a Mon Sep 17 00:00:00 2001 From: daboe01 Date: Wed, 3 Jun 2026 21:19:09 +0200 Subject: [PATCH] fixed: localizer issues --- AppKit/CPRuleEditor/CPRuleEditor.j | 32 +++++++++++++++---- AppKit/CPRuleEditor/_CPRuleEditorLocalizer.j | 22 ++++++++++--- .../CPRuleEditorTestSpanish/AppController.j | 21 ++++++++++++ .../Resources/Spanish.strings | 3 +- .../CPRuleEditorTestSpanish/RuleDelegate.j | 24 +++++++------- 5 files changed, 77 insertions(+), 25 deletions(-) diff --git a/AppKit/CPRuleEditor/CPRuleEditor.j b/AppKit/CPRuleEditor/CPRuleEditor.j index 3ac9d2b80..cbc08326f 100644 --- a/AppKit/CPRuleEditor/CPRuleEditor.j +++ b/AppKit/CPRuleEditor/CPRuleEditor.j @@ -226,6 +226,9 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType", { [[_slices objectAtIndex:i] _reconfigureSubviews]; } + + [self _updatePredicate]; + [self _sendRuleAction]; } argument:nil order:0 modes:[CPDefaultRunLoopMode]]; } } @@ -574,7 +577,7 @@ var CPRuleEditorItemPBoardType = @"CPRuleEditorItemPBoardType", if ([self rowTypeForRow:current_index] === CPRuleEditorRowTypeCompound) { var candidate = [[self _rowCacheForIndex:current_index] rowObject], - subObjects = [[self _subrowObjectsOfObject:candidate] _representedObject]; + subObjects = [self _subrowObjectsOfObject:candidate]; // Standard direct array query if ([subObjects indexOfObjectIdenticalTo:targetObject] !== CPNotFound) return current_index; @@ -641,7 +644,7 @@ TODO: implement for (var i = rowIndex + 1; i < count; i++) { var candidate = [[self _rowCacheForIndex:i] rowObject], - indexInSubrows = [[subobjects _representedObject] indexOfObjectIdenticalTo:candidate]; + indexInSubrows = [subobjects indexOfObjectIdenticalTo:candidate]; // Standard direct array query if (indexInSubrows !== CPNotFound) { @@ -810,7 +813,7 @@ TODO: implement while (current_index !== CPNotFound) { var rowObject = [[self _rowCacheForIndex:current_index] rowObject], - relativeChildIndex = [[subrows _representedObject] indexOfObjectIdenticalTo:rowObject]; + relativeChildIndex = [subrows indexOfObjectIdenticalTo:rowObject]; // Standard direct array query if (relativeChildIndex !== CPNotFound) [childsIndexes addIndex:relativeChildIndex]; @@ -867,7 +870,6 @@ TODO: implement for (i = 0; i < count; i++) { var item = [items objectAtIndex:i], - //var displayValue = [self _queryValueForItem:item inRow:aRow]; Ask the delegate or get cached value ?. displayValue = [[self displayValuesForRow:aRow] objectAtIndex:i], predpart = [self _sendDelegateRuleEditorPredicatePartsForCriterion:item withDisplayValue:displayValue inRow:aRow]; @@ -885,6 +887,7 @@ TODO: implement return nil; var current_index = [subrowsIndexes firstIndex]; + while (current_index !== CPNotFound) { var subpredicate = [self predicateForRow:current_index]; @@ -1392,8 +1395,25 @@ TODO: implement if ([self rowTypeForRow:row] === type && itemIndex < [aCriteria count]) { - var crit = [aCriteria objectAtIndex:itemIndex]; - [current_criterions addObject:crit]; + // Verify that this row's parent path matches the path currently being built + var pathMatches = true; + for (var p = 0; p < itemIndex; p++) + { + var criterionA = [aCriteria objectAtIndex:p], + criterionB = [items objectAtIndex:p]; + + if (criterionA !== criterionB && (typeof criterionA.isEqual !== "function" || ![criterionA isEqual:criterionB])) + { + pathMatches = false; + break; + } + } + + if (pathMatches) + { + var crit = [aCriteria objectAtIndex:itemIndex]; + [current_criterions addObject:crit]; + } } } diff --git a/AppKit/CPRuleEditor/_CPRuleEditorLocalizer.j b/AppKit/CPRuleEditor/_CPRuleEditorLocalizer.j index 79ab94da8..467445242 100644 --- a/AppKit/CPRuleEditor/_CPRuleEditorLocalizer.j +++ b/AppKit/CPRuleEditor/_CPRuleEditorLocalizer.j @@ -112,7 +112,7 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+) if (!originalTitle) { var rep = [selectedItem representedObject]; - if (rep && typeof rep === "object" && typeof rep.objectForKey === "function") + if (rep && typeof rep === "object" && [rep respondsToSelector:@selector(objectForKey:)]) { originalTitle = [rep objectForKey:@"value"]; } @@ -170,7 +170,7 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+) if (!item._originalTitle) { var rep = [item representedObject]; - if (rep && typeof rep === "object" && typeof rep.objectForKey === "function") + if (rep && typeof rep === "object" && [rep respondsToSelector:@selector(objectForKey:)]) { item._originalTitle = [rep objectForKey:@"value"]; } @@ -230,7 +230,9 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+) while ((match = regex.exec(localizedPattern)) !== null) { var literalText = localizedPattern.substring(lastIndex, match.index); - if (literalText.length > 0) + + // Only add a label if there are actual non-whitespace characters (like 'y') + if (literalText.length > 0 && /\S/.test(literalText)) { var label = [CPTextField labelWithTitle:literalText]; [newViews addObject:label]; @@ -257,9 +259,17 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+) [selectedItem setTitle:translatedValue]; } } - else if (typeof originalView.setStringValue === "function") + else if ([originalView respondsToSelector:@selector(setStringValue:)]) { [originalView setStringValue:translatedValue]; + + // Recalculate frame size if it is a static CPTextField to avoid visual clipping + if ([originalView isKindOfClass:[CPTextField class]] && ![originalView isEditable]) + { + var font = [originalView font] || [CPFont systemFontOfSize:[CPFont systemFontSize]], + size = [translatedValue sizeWithFont:font]; + [originalView setFrameSize:CGSizeMake(size.width + 4, CGRectGetHeight([originalView frame]))]; + } } } @@ -272,7 +282,9 @@ var LocalizerStringsRegex = new RegExp("\"(.+)\"\\s*=\\s*\"(.+)\"\\s*;\\s*(//.+) if (lastIndex < localizedPattern.length) { var literalText = localizedPattern.substring(lastIndex); - if (literalText.length > 0) + + // Only add a label if there are actual non-whitespace characters + if (literalText.length > 0 && /\S/.test(literalText)) { var label = [CPTextField labelWithTitle:literalText]; [newViews addObject:label]; diff --git a/Tests/Manual/CPRuleEditorTestSpanish/AppController.j b/Tests/Manual/CPRuleEditorTestSpanish/AppController.j index e81076955..d39cbf981 100644 --- a/Tests/Manual/CPRuleEditorTestSpanish/AppController.j +++ b/Tests/Manual/CPRuleEditorTestSpanish/AppController.j @@ -35,6 +35,7 @@ [ruleEditor setAutoresizingMask:CPViewWidthSizable]; [ruleEditor setDelegate:ruleDelegate]; [ruleEditor setEditable:YES]; + [ruleEditor setNestingMode:CPRuleEditorNestingModeList]; [ruleEditor setRowHeight:28]; [ruleEditor setTarget:self]; [ruleEditor setAction:@selector(ruleEditorAction:)]; @@ -72,6 +73,12 @@ [addBtn setAction:@selector(addRow:)]; [contentView addSubview:addBtn]; + + [[CPNotificationCenter defaultCenter] addObserver:self + selector:@selector(ruleEditorRowsDidChange:) + name:CPRuleEditorRowsDidChangeNotification + object:ruleEditor]; + [theWindow orderFront:self]; [self ruleEditorAction:nil]; } @@ -89,4 +96,18 @@ } } +- (void)ruleEditorRowsDidChange:(CPNotification)note +{ + var predicate = [ruleEditor predicate]; + + if (predicate) + { + [predicateField setStringValue:[predicate predicateFormat]]; + } + else + { + [predicateField setStringValue:@"(Incomplete Predicate)"]; + } +} + @end diff --git a/Tests/Manual/CPRuleEditorTestSpanish/Resources/Spanish.strings b/Tests/Manual/CPRuleEditorTestSpanish/Resources/Spanish.strings index 17a1738c9..75dd5e735 100644 --- a/Tests/Manual/CPRuleEditorTestSpanish/Resources/Spanish.strings +++ b/Tests/Manual/CPRuleEditorTestSpanish/Resources/Spanish.strings @@ -7,7 +7,8 @@ "%[lastName]@ %[contains]@ %@" = "%1$[Apellido]@ %2$[contiene]@ %3$@"; "%[age]@ %[is equal to]@ %@" = "%1$[Edad]@ y %3$@ %2$[son iguales]@"; +"%[age]@ is equal to %@" = "%1$[Edad]@ y %3$@ %2$[son iguales]@"; "Add row" = "Añadir regla"; "Delete row" = "Eliminar regla"; -"Add compound row" = "Añadir grupo de reglas"; \ No newline at end of file +"Add compound row" = "Añadir grupo de reglas"; diff --git a/Tests/Manual/CPRuleEditorTestSpanish/RuleDelegate.j b/Tests/Manual/CPRuleEditorTestSpanish/RuleDelegate.j index 842056535..3835b5e7f 100644 --- a/Tests/Manual/CPRuleEditorTestSpanish/RuleDelegate.j +++ b/Tests/Manual/CPRuleEditorTestSpanish/RuleDelegate.j @@ -2,6 +2,10 @@ @import @import +// Ensure the standard operator constants are explicitly defined +var CPEqualToPredicateOperatorType = 4, + CPContainsPredicateOperatorType = 99; + @implementation RuleDelegate : CPObject { } @@ -11,13 +15,11 @@ { if (criterion == nil) { - // Root criteria return [@[@"firstName", @"lastName", @"age"] objectAtIndex:index]; } if ([criterion isEqualToString:@"firstName"] || [criterion isEqualToString:@"lastName"]) { - // "is equal to" is placed at index 0 so that the reordered Spanish sentence layout loads automatically on startup return [@[@"is equal to", @"contains"] objectAtIndex:index]; } @@ -28,41 +30,39 @@ if ([criterion isEqualToString:@"contains"] || [criterion isEqualToString:@"is equal to"]) { - // The child of an operator is the leaf value node return @"value"; } return nil; } -// 2. Number of children for a given criterion +// 2. Number of children - (CPInteger)ruleEditor:(CPRuleEditor)editor numberOfChildrenForCriterion:(id)criterion withRowType:(CPRuleEditorRowType)rowType { if (criterion == nil) { - return 3; // firstName, lastName, age + return 3; } if ([criterion isEqualToString:@"firstName"] || [criterion isEqualToString:@"lastName"]) { - return 2; // contains, is equal to + return 2; } if ([criterion isEqualToString:@"age"]) { - return 1; // is equal to + return 1; } if ([criterion isEqualToString:@"contains"] || [criterion isEqualToString:@"is equal to"]) { - // Operators have 1 child representing the value node return 1; } - return 0; // Leaf nodes return 0 + return 0; } -// 3. Display values (labels, popup titles, or text input fields) +// 3. Display values - (id)ruleEditor:(CPRuleEditor)editor displayValueForCriterion:(id)criterion inRow:(CPInteger)row { if ([criterion isEqualToString:@"firstName"]) return @"firstName"; @@ -74,7 +74,6 @@ if ([criterion isEqualToString:@"value"]) { - // Return the actual editable text field view for the leaf node var textField = [[CPTextField alloc] initWithFrame:CGRectMake(0, 0, 120, 24)]; [textField setBezeled:YES]; [textField setBezelStyle:CPTextFieldSquareBezel]; @@ -86,7 +85,7 @@ return nil; } -// 4. Predicate parts mapping +// 4. Predicate parts - (CPDictionary)ruleEditor:(CPRuleEditor)editor predicatePartsForCriterion:(id)criterion withDisplayValue:(id)value inRow:(CPInteger)row { var parts = @{}; @@ -105,7 +104,6 @@ } else if ([criterion isEqualToString:@"value"]) { - // Resolve the correct active text field from the slice row on screen var activeValue = value; var slices = [editor valueForKey:@"_slices"];