179179 private bool _isSaving ;
180180 private bool _hasUnsavedChanges ;
181181 private bool _drawerOpen ;
182- private TranslationGridRow ? _selectedRow ;
182+ private TranslationGridRow ? _selectedRow ; // Clone for drawer editing (isolated)
183+ private TranslationGridRow ? _originalSelectedRow ; // Original row reference (in grid)
183184 private AddKeyDialog ? _addKeyDialog ;
184185 private ConfirmDeleteDialog ? _deleteDialog ;
185186 private IEnumerable <TranslationGridRow >? _pendingDeleteRows ;
@@ -519,14 +520,16 @@ else
519520
520521 private void OnRowSelected (TranslationGridRow row )
521522 {
522- _selectedRow = row ;
523+ _originalSelectedRow = row ;
524+ _selectedRow = row .Clone (); // Drawer works on a clone to isolate changes
523525 _drawerOpen = true ;
524526 }
525527
526528 private void CloseDrawer ()
527529 {
528530 _drawerOpen = false ;
529531 _selectedRow = null ;
532+ _originalSelectedRow = null ;
530533 }
531534
532535 private void OnCellChanged ((TranslationGridRow Row , string LanguageCode , string Value ) args )
@@ -554,12 +557,17 @@ else
554557 if (_project == null ) return ;
555558
556559 var rowList = rows .ToList ();
560+
561+ // Build source texts from current UI state (may include unsaved edits)
562+ var sourceTexts = BuildSourceTextsFromRows (rowList , _project .DefaultLanguage );
563+
557564 var parameters = new DialogParameters
558565 {
559566 { nameof (TranslateDialog .ProjectId ), ProjectId },
560567 { nameof (TranslateDialog .SourceLanguage ), _project .DefaultLanguage },
561568 { nameof (TranslateDialog .AvailableLanguages ), _languages },
562- { nameof (TranslateDialog .KeysToTranslate ), rowList .Select (r => r .KeyName ).ToList () }
569+ { nameof (TranslateDialog .KeysToTranslate ), rowList .Select (r => r .KeyName ).ToList () },
570+ { nameof (TranslateDialog .SourceTexts ), sourceTexts }
563571 };
564572
565573 var options = new DialogOptions
582590
583591 /// <summary >
584592 /// Applies translation results to the UI state, marking cells as dirty so Save button enables.
593+ /// Handles both non-plural and plural keys.
585594 /// </summary >
586595 private void ApplyTranslationsToUI (List < TranslationGridRow > rows , TranslateResponseDto translationResult )
587596 {
@@ -595,16 +604,22 @@ else
595604 continue ;
596605 }
597606
598- if (! row .Translations .TryGetValue (translationRes .TargetLanguage , out var cell ))
607+ // Build the cell key: "lang" for non-plural, "lang:pluralForm" for plural
608+ var cellKey = string .IsNullOrEmpty (translationRes .PluralForm )
609+ ? translationRes .TargetLanguage
610+ : TranslationGridRow .GetKey (translationRes .TargetLanguage , translationRes .PluralForm );
611+
612+ if (! row .Translations .TryGetValue (cellKey , out var cell ))
599613 {
600- // Create new cell for this language
614+ // Create new cell for this language/plural form
601615 cell = new TranslationCell
602616 {
603617 LanguageCode = translationRes .TargetLanguage ,
618+ PluralForm = translationRes .PluralForm ,
604619 Value = null ,
605620 OriginalValue = null
606621 };
607- row .Translations [translationRes . TargetLanguage ] = cell ;
622+ row .Translations [cellKey ] = cell ;
608623 }
609624
610625 // Apply translated text and mark as dirty
@@ -667,10 +682,16 @@ else
667682 // Stage changes in the editor (don't save to DB yet)
668683 // User must click the main Save button to persist with optional snapshot
669684
670- // Ensure row is tracked in edited rows
671- if (! _editedRows . ContainsKey ( row . KeyName ) )
685+ // Apply changes from the drawer's clone to the original row in the grid
686+ if (_originalSelectedRow != null )
672687 {
673- _editedRows [row .KeyName ] = row ;
688+ _originalSelectedRow .ApplyFrom (row );
689+
690+ // Track the original row (not the clone) in edited rows
691+ if (! _editedRows .ContainsKey (_originalSelectedRow .KeyName ))
692+ {
693+ _editedRows [_originalSelectedRow .KeyName ] = _originalSelectedRow ;
694+ }
674695 }
675696
676697 // Mark that we have unsaved changes
679700 // Close the drawer
680701 _drawerOpen = false ;
681702 _selectedRow = null ;
703+ _originalSelectedRow = null ;
682704
683705 Snackbar .Add (" Changes staged. Click Save to persist." , Severity .Info );
684706 }
@@ -687,12 +709,16 @@ else
687709 {
688710 if (_project == null ) return ;
689711
712+ // Build source texts from current UI state (may include unsaved edits)
713+ var sourceTexts = BuildSourceTextsFromRows (new List <TranslationGridRow > { row }, _project .DefaultLanguage );
714+
690715 var parameters = new DialogParameters
691716 {
692717 { nameof (TranslateDialog .ProjectId ), ProjectId },
693718 { nameof (TranslateDialog .SourceLanguage ), _project .DefaultLanguage },
694719 { nameof (TranslateDialog .AvailableLanguages ), _languages },
695- { nameof (TranslateDialog .KeysToTranslate ), new List <string > { row .KeyName } }
720+ { nameof (TranslateDialog .KeysToTranslate ), new List <string > { row .KeyName } },
721+ { nameof (TranslateDialog .SourceTexts ), sourceTexts }
696722 };
697723
698724 var options = new DialogOptions
@@ -717,26 +743,26 @@ else
717743 /// <summary >
718744 /// Applies translation results to the currently selected drawer row.
719745 /// Unlike ApplyTranslationsToUI, this does NOT stage to _editedRows - the user must click Apply.
746+ /// Handles both non-plural and plural keys.
720747 /// </summary >
721748 private void ApplyTranslationsToDrawerRow (TranslationGridRow row , TranslateResponseDto translationResult )
722749 {
723750 var appliedCount = 0 ;
724751
725752 foreach (var translationRes in translationResult .Results .Where (r => r .Success && r .Key == row .KeyName ))
726753 {
727- // For non-plural keys, use language code directly
728- // For plural keys, the API currently doesn't return plural form, so default to "other"
729- var cellKey = row .IsPlural
730- ? TranslationGridRow .GetKey (translationRes .TargetLanguage , PluralForms .Other )
731- : translationRes .TargetLanguage ;
754+ // Build the cell key: "lang" for non-plural, "lang:pluralForm" for plural
755+ var cellKey = string .IsNullOrEmpty (translationRes .PluralForm )
756+ ? translationRes .TargetLanguage
757+ : TranslationGridRow .GetKey (translationRes .TargetLanguage , translationRes .PluralForm );
732758
733759 if (! row .Translations .TryGetValue (cellKey , out var cell ))
734760 {
735- // Create new cell for this language
761+ // Create new cell for this language/plural form
736762 cell = new TranslationCell
737763 {
738764 LanguageCode = translationRes .TargetLanguage ,
739- PluralForm = row . IsPlural ? PluralForms . Other : " " ,
765+ PluralForm = translationRes . PluralForm ,
740766 Value = null ,
741767 OriginalValue = null
742768 };
@@ -756,6 +782,44 @@ else
756782 }
757783 }
758784
785+ /// <summary >
786+ /// Builds a dictionary of source texts from the current UI state.
787+ /// This allows translating unsaved edits (what the user sees in the editor).
788+ /// For plural keys, includes all plural forms with "keyName:pluralForm" keys.
789+ /// </summary >
790+ private Dictionary <string , string > BuildSourceTextsFromRows (List < TranslationGridRow > rows , string defaultLanguage )
791+ {
792+ var sourceTexts = new Dictionary <string , string >();
793+
794+ foreach (var row in rows )
795+ {
796+ if (row .IsPlural )
797+ {
798+ // For plural keys, include all plural forms
799+ foreach (var pluralForm in PluralForms .All )
800+ {
801+ var cell = row .GetCell (defaultLanguage , pluralForm );
802+ if (cell != null && ! string .IsNullOrEmpty (cell .Value ))
803+ {
804+ // Use "keyName:pluralForm" format for plural keys
805+ sourceTexts [$" {row .KeyName }:{pluralForm }" ] = cell .Value ;
806+ }
807+ }
808+ }
809+ else
810+ {
811+ // For non-plural keys, just use the key name
812+ var cell = row .Translations .GetValueOrDefault (defaultLanguage );
813+ if (cell != null && ! string .IsNullOrEmpty (cell .Value ))
814+ {
815+ sourceTexts [row .KeyName ] = cell .Value ;
816+ }
817+ }
818+ }
819+
820+ return sourceTexts ;
821+ }
822+
759823 private async Task DeleteKey (TranslationGridRow row )
760824 {
761825 _pendingDeleteRows = new [] { row };
@@ -767,14 +831,9 @@ else
767831
768832 private void OnKeyTypeChanged ((TranslationGridRow Row , bool IsPlural ) args )
769833 {
770- // Track the edited row for the type change
771- if (! _editedRows .ContainsKey (args .Row .KeyName ))
772- {
773- _editedRows [args .Row .KeyName ] = args .Row ;
774- }
775- _hasUnsavedChanges = true ;
776-
777- // Force grid to re-render to update the plural indicator
834+ // Type change is on the clone - just update UI to show changes in the drawer.
835+ // The actual change will be applied when the user clicks "Apply" via SaveKeyDetails.
836+ // Don't track or mark as dirty here - that happens in SaveKeyDetails.
778837 StateHasChanged ();
779838 }
780839
0 commit comments