mirror of
https://github.com/SamyRai/turash.git
synced 2025-12-26 23:01:33 +00:00
289 lines
7.7 KiB
Go
289 lines
7.7 KiB
Go
package service
|
|
|
|
import (
|
|
"bugulma/backend/internal/domain"
|
|
"bugulma/backend/internal/localization"
|
|
"context"
|
|
"fmt"
|
|
"strings"
|
|
"time"
|
|
)
|
|
|
|
// TranslationWorkflowService handles the orchestration of translation workflows
|
|
type TranslationWorkflowService struct {
|
|
locRepo domain.LocalizationRepository
|
|
locService domain.LocalizationService
|
|
entityLoader *EntityLoaderService
|
|
translationService *TranslationService
|
|
cacheService *TranslationCacheService
|
|
}
|
|
|
|
// NewTranslationWorkflowService creates a new translation workflow service
|
|
func NewTranslationWorkflowService(
|
|
locRepo domain.LocalizationRepository,
|
|
locService domain.LocalizationService,
|
|
entityLoader *EntityLoaderService,
|
|
translationService *TranslationService,
|
|
cacheService *TranslationCacheService,
|
|
) *TranslationWorkflowService {
|
|
return &TranslationWorkflowService{
|
|
locRepo: locRepo,
|
|
locService: locService,
|
|
entityLoader: entityLoader,
|
|
translationService: translationService,
|
|
cacheService: cacheService,
|
|
}
|
|
}
|
|
|
|
// TranslationResult represents the result of a translation operation
|
|
type TranslationResult struct {
|
|
Success bool
|
|
Translated string
|
|
Cached bool
|
|
Error error
|
|
EntityType string
|
|
EntityID string
|
|
Field string
|
|
TargetLocale string
|
|
}
|
|
|
|
// TranslationSummary represents a summary of translation operations
|
|
type TranslationSummary struct {
|
|
TotalEntities int
|
|
ProcessedFields int
|
|
Translated int
|
|
Cached int
|
|
Errors int
|
|
Skipped int
|
|
Duration time.Duration
|
|
}
|
|
|
|
// ProcessEntityTranslation processes translation for all fields of an entity
|
|
func (s *TranslationWorkflowService) ProcessEntityTranslation(
|
|
ctx context.Context,
|
|
entityType string,
|
|
entity interface{},
|
|
targetLocale string,
|
|
dryRun bool,
|
|
) ([]TranslationResult, error) {
|
|
|
|
entityID := s.entityLoader.GetEntityID(entity)
|
|
results := []TranslationResult{}
|
|
|
|
// Get localizable fields for this entity type
|
|
fields := localization.GetFieldsForEntityType(entityType)
|
|
if len(fields) == 0 {
|
|
return results, fmt.Errorf("no localizable fields found for entity type: %s", entityType)
|
|
}
|
|
|
|
for _, field := range fields {
|
|
russianText := s.entityLoader.GetRussianContent(entity, field)
|
|
if russianText == "" {
|
|
continue
|
|
}
|
|
|
|
// Skip very short content
|
|
if len(strings.TrimSpace(russianText)) < 2 {
|
|
continue
|
|
}
|
|
|
|
result := s.processFieldTranslation(ctx, entityType, entityID, field, russianText, targetLocale, dryRun)
|
|
results = append(results, result)
|
|
}
|
|
|
|
return results, nil
|
|
}
|
|
|
|
|
|
// ProcessBatchTranslation processes translation for multiple entities
|
|
func (s *TranslationWorkflowService) ProcessBatchTranslation(
|
|
ctx context.Context,
|
|
entityType string,
|
|
entityIDs []string,
|
|
targetLocale string,
|
|
dryRun bool,
|
|
) (*TranslationSummary, error) {
|
|
|
|
startTime := time.Now()
|
|
summary := &TranslationSummary{}
|
|
|
|
for _, entityID := range entityIDs {
|
|
entity, err := s.entityLoader.LoadEntityByID(entityType, entityID)
|
|
if err != nil {
|
|
summary.Errors++
|
|
continue
|
|
}
|
|
|
|
results, err := s.ProcessEntityTranslation(ctx, entityType, entity, targetLocale, dryRun)
|
|
if err != nil {
|
|
summary.Errors++
|
|
continue
|
|
}
|
|
|
|
summary.ProcessedFields += len(results)
|
|
summary.TotalEntities++
|
|
|
|
for _, result := range results {
|
|
if result.Cached {
|
|
summary.Cached++
|
|
} else if result.Success {
|
|
summary.Translated++
|
|
} else {
|
|
summary.Errors++
|
|
}
|
|
}
|
|
}
|
|
|
|
summary.Duration = time.Since(startTime)
|
|
return summary, nil
|
|
}
|
|
|
|
// GetTranslationStats returns statistics for translations
|
|
func (s *TranslationWorkflowService) GetTranslationStats(ctx context.Context, entityTypeFilter string) (*domain.TranslationStats, error) {
|
|
entityTypes := localization.GetEntityTypesForFilter(entityTypeFilter)
|
|
|
|
stats := &domain.TranslationStats{
|
|
EntityStats: make(map[string]*domain.EntityTranslationStats),
|
|
}
|
|
|
|
totalEntities := 0
|
|
totalFields := 0
|
|
totalTranslations := 0
|
|
|
|
for _, entityType := range entityTypes {
|
|
entityStats, err := s.getEntityTranslationStats(ctx, entityType)
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
stats.EntityStats[entityType] = entityStats
|
|
totalEntities += entityStats.TotalEntities
|
|
totalFields += entityStats.TotalFields
|
|
totalTranslations += entityStats.TotalTranslations
|
|
}
|
|
|
|
stats.TotalEntities = totalEntities
|
|
stats.TotalFields = totalFields
|
|
stats.TotalTranslations = totalTranslations
|
|
|
|
return stats, nil
|
|
}
|
|
|
|
// WorkflowTranslationRequest represents a translation request in the workflow
|
|
type WorkflowTranslationRequest struct {
|
|
TargetLocale string
|
|
EntityType string
|
|
EntityID string
|
|
Field string
|
|
RussianText string
|
|
DryRun bool
|
|
}
|
|
|
|
// processFieldTranslation processes translation for a single field
|
|
func (s *TranslationWorkflowService) processFieldTranslation(
|
|
ctx context.Context,
|
|
entityType, entityID, field, russianText, targetLocale string,
|
|
dryRun bool,
|
|
) TranslationResult {
|
|
result := TranslationResult{
|
|
EntityType: entityType,
|
|
EntityID: entityID,
|
|
Field: field,
|
|
TargetLocale: targetLocale,
|
|
}
|
|
|
|
// Check if translation already exists
|
|
if existing, err := s.locService.GetLocalizedValue(entityType, entityID, field, targetLocale); err == nil && existing != "" {
|
|
result.Success = true
|
|
result.Cached = true
|
|
result.Translated = existing
|
|
return result
|
|
}
|
|
|
|
// Check translation cache
|
|
if cached, found, _ := s.cacheService.FindCachedTranslation(ctx, entityType, field, targetLocale, russianText); found {
|
|
if !dryRun {
|
|
if err := s.locService.SetLocalizedValue(entityType, entityID, field, targetLocale, cached); err != nil {
|
|
result.Error = fmt.Errorf("failed to save cached translation: %w", err)
|
|
return result
|
|
}
|
|
}
|
|
result.Success = true
|
|
result.Cached = true
|
|
result.Translated = cached
|
|
return result
|
|
}
|
|
|
|
// Perform translation
|
|
if dryRun {
|
|
result.Success = true
|
|
result.Translated = "[DRY RUN]"
|
|
return result
|
|
}
|
|
|
|
translated, err := s.translationService.Translate(russianText, "ru", targetLocale)
|
|
if err != nil {
|
|
result.Error = fmt.Errorf("translation failed: %w", err)
|
|
return result
|
|
}
|
|
|
|
if translated == "" {
|
|
result.Error = fmt.Errorf("empty translation received")
|
|
return result
|
|
}
|
|
|
|
if err := s.locService.SetLocalizedValue(entityType, entityID, field, targetLocale, translated); err != nil {
|
|
result.Error = fmt.Errorf("failed to save translation: %w", err)
|
|
return result
|
|
}
|
|
|
|
// Update cache
|
|
s.cacheService.AddToCache(entityType, field, targetLocale, russianText, translated)
|
|
|
|
result.Success = true
|
|
result.Translated = translated
|
|
return result
|
|
}
|
|
|
|
// getEntityTranslationStats gets stats for a specific entity type
|
|
func (s *TranslationWorkflowService) getEntityTranslationStats(ctx context.Context, entityType string) (*domain.EntityTranslationStats, error) {
|
|
entities, err := s.entityLoader.LoadEntities(entityType, localization.LoadOptions{})
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
fields := localization.GetFieldsForEntityType(entityType)
|
|
totalFields := len(entities) * len(fields)
|
|
|
|
// Count translations by locale
|
|
ruCount := 0
|
|
enCount := 0
|
|
ttCount := 0
|
|
|
|
for _, entity := range entities {
|
|
entityID := s.entityLoader.GetEntityID(entity)
|
|
|
|
for _, field := range fields {
|
|
if ruVal, _ := s.locService.GetLocalizedValue(entityType, entityID, field, "ru"); ruVal != "" {
|
|
ruCount++
|
|
}
|
|
if enVal, _ := s.locService.GetLocalizedValue(entityType, entityID, field, "en"); enVal != "" {
|
|
enCount++
|
|
}
|
|
if ttVal, _ := s.locService.GetLocalizedValue(entityType, entityID, field, "tt"); ttVal != "" {
|
|
ttCount++
|
|
}
|
|
}
|
|
}
|
|
|
|
return &domain.EntityTranslationStats{
|
|
EntityType: entityType,
|
|
TotalEntities: len(entities),
|
|
TotalFields: totalFields,
|
|
RussianCount: ruCount,
|
|
EnglishCount: enCount,
|
|
TatarCount: ttCount,
|
|
TotalTranslations: ruCount + enCount + ttCount,
|
|
}, nil
|
|
}
|