@@ -354,40 +354,111 @@ private async Task TranslateKeysAsync(
354354 // Check if this is a plural key
355355 if ( key . IsPlural && key . PluralForms != null && key . PluralForms . Count > 0 )
356356 {
357- // Translate each plural form
358357 var translatedForms = new Dictionary < string , string > ( ) ;
359358 var anyFromCache = false ;
360359 var anyFromApi = false ;
360+ var isPo = settings . GetBackendName ( ) . Equals ( "po" , StringComparison . OrdinalIgnoreCase ) ;
361361
362- foreach ( var ( formName , formValue ) in key . PluralForms )
362+ if ( isPo && ! string . IsNullOrEmpty ( key . SourcePluralText ) )
363363 {
364- if ( string . IsNullOrWhiteSpace ( formValue ) )
365- continue ;
364+ // PO format: translate msgid (singular) and msgid_plural (plural) separately
365+ // msgid (Key) → "one" form
366+ // msgid_plural (SourcePluralText) → "other" and remaining forms
366367
367- var request = new TranslationRequest
368+ // Translate singular (msgid → "one")
369+ var singularRequest = new TranslationRequest
368370 {
369- SourceText = formValue ,
371+ SourceText = key . Key ,
370372 SourceLanguage = sourceLanguage ,
371373 TargetLanguage = targetLang ,
372374 TargetLanguageName = targetLanguageInfo . Name ,
373- Context = $ "Plural form ' { formName } ' of key ' { key . Key } ' "
375+ Context = $ "Singular form of plural key"
374376 } ;
375377
376- TranslationResponse response ;
378+ TranslationResponse singularResponse ;
379+ if ( cache != null && cache . TryGet ( singularRequest , provider . Name , out var cachedSingular ) )
380+ {
381+ singularResponse = cachedSingular ! ;
382+ anyFromCache = true ;
383+ }
384+ else
385+ {
386+ singularResponse = await provider . TranslateAsync ( singularRequest , cancellationToken ) ;
387+ cache ? . Store ( singularRequest , singularResponse ) ;
388+ anyFromApi = true ;
389+ }
390+ translatedForms [ "one" ] = singularResponse . TranslatedText ;
377391
378- if ( cache != null && cache . TryGet ( request , provider . Name , out var cachedResponse ) )
392+ // Translate plural (msgid_plural → "other")
393+ var pluralRequest = new TranslationRequest
379394 {
380- response = cachedResponse ! ;
395+ SourceText = key . SourcePluralText ,
396+ SourceLanguage = sourceLanguage ,
397+ TargetLanguage = targetLang ,
398+ TargetLanguageName = targetLanguageInfo . Name ,
399+ Context = $ "Plural form of plural key"
400+ } ;
401+
402+ TranslationResponse pluralResponse ;
403+ if ( cache != null && cache . TryGet ( pluralRequest , provider . Name , out var cachedPlural ) )
404+ {
405+ pluralResponse = cachedPlural ! ;
381406 anyFromCache = true ;
382407 }
383408 else
384409 {
385- response = await provider . TranslateAsync ( request , cancellationToken ) ;
386- cache ? . Store ( request , response ) ;
410+ pluralResponse = await provider . TranslateAsync ( pluralRequest , cancellationToken ) ;
411+ cache ? . Store ( pluralRequest , pluralResponse ) ;
387412 anyFromApi = true ;
388413 }
414+ translatedForms [ "other" ] = pluralResponse . TranslatedText ;
389415
390- translatedForms [ formName ] = response . TranslatedText ;
416+ // For languages with more plural forms (few, many, zero, two),
417+ // copy the "other" form as a reasonable default
418+ foreach ( var category in key . PluralForms . Keys )
419+ {
420+ if ( ! translatedForms . ContainsKey ( category ) )
421+ {
422+ // Use singular for "one", plural for everything else
423+ translatedForms [ category ] = category == "one"
424+ ? singularResponse . TranslatedText
425+ : pluralResponse . TranslatedText ;
426+ }
427+ }
428+ }
429+ else
430+ {
431+ // Non-PO formats: translate each plural form value directly
432+ foreach ( var ( formName , formValue ) in key . PluralForms )
433+ {
434+ if ( string . IsNullOrWhiteSpace ( formValue ) )
435+ continue ;
436+
437+ var request = new TranslationRequest
438+ {
439+ SourceText = formValue ,
440+ SourceLanguage = sourceLanguage ,
441+ TargetLanguage = targetLang ,
442+ TargetLanguageName = targetLanguageInfo . Name ,
443+ Context = $ "Plural form '{ formName } ' of key '{ key . Key } '"
444+ } ;
445+
446+ TranslationResponse response ;
447+
448+ if ( cache != null && cache . TryGet ( request , provider . Name , out var cachedResponse ) )
449+ {
450+ response = cachedResponse ! ;
451+ anyFromCache = true ;
452+ }
453+ else
454+ {
455+ response = await provider . TranslateAsync ( request , cancellationToken ) ;
456+ cache ? . Store ( request , response ) ;
457+ anyFromApi = true ;
458+ }
459+
460+ translatedForms [ formName ] = response . TranslatedText ;
461+ }
391462 }
392463
393464 // Update or add entry with plural forms
@@ -396,6 +467,8 @@ private async Task TranslateKeysAsync(
396467 targetDict [ key . Key ] . IsPlural = true ;
397468 targetDict [ key . Key ] . PluralForms = translatedForms ;
398469 targetDict [ key . Key ] . Value = translatedForms . GetValueOrDefault ( "other" ) ?? translatedForms . Values . FirstOrDefault ( ) ?? "" ;
470+ // Preserve SourcePluralText for PO format round-trip
471+ targetDict [ key . Key ] . SourcePluralText = key . SourcePluralText ;
399472 }
400473 else
401474 {
@@ -405,7 +478,9 @@ private async Task TranslateKeysAsync(
405478 Value = translatedForms . GetValueOrDefault ( "other" ) ?? translatedForms . Values . FirstOrDefault ( ) ?? "" ,
406479 Comment = key . Comment ,
407480 IsPlural = true ,
408- PluralForms = translatedForms
481+ PluralForms = translatedForms ,
482+ // Preserve SourcePluralText for PO format round-trip
483+ SourcePluralText = key . SourcePluralText
409484 } ) ;
410485 }
411486
@@ -421,9 +496,14 @@ private async Task TranslateKeysAsync(
421496 else
422497 {
423498 // Regular (non-plural) translation
499+ // For PO format, the Key (msgid) IS the source text, not the Value (msgstr)
500+ var sourceText = settings . GetBackendName ( ) . Equals ( "po" , StringComparison . OrdinalIgnoreCase )
501+ ? key . Key
502+ : ( key . Value ?? string . Empty ) ;
503+
424504 var request = new TranslationRequest
425505 {
426- SourceText = key . Value ?? string . Empty ,
506+ SourceText = sourceText ,
427507 SourceLanguage = sourceLanguage ,
428508 TargetLanguage = targetLang ,
429509 TargetLanguageName = targetLanguageInfo . Name
0 commit comments