prepare for google translate

This commit is contained in:
Daniel Goc
2026-03-16 08:51:35 +01:00
parent 6ff47ce1c4
commit a44885faa5
8 changed files with 198 additions and 28 deletions

View File

@@ -5,32 +5,76 @@ import (
"encoding/xml"
"fmt"
"io"
"net/http"
"log"
"os"
"slices"
"strings"
"time"
"git.ma-al.com/goc_daniel/b2b/app/config"
"git.ma-al.com/goc_daniel/b2b/app/db"
"git.ma-al.com/goc_daniel/b2b/app/model"
"git.ma-al.com/goc_daniel/b2b/app/service/langsService"
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
"google.golang.org/api/option"
"gorm.io/gorm"
"github.com/openai/openai-go/v3"
"github.com/openai/openai-go/v3/option"
"github.com/openai/openai-go/v3/responses"
// [START translate_v3_import_client_library]
"cloud.google.com/go/auth/credentials"
translate "cloud.google.com/go/translate/apiv3"
"cloud.google.com/go/translate/apiv3/translatepb"
// [END translate_v3_import_client_library]
)
type ProductDescriptionService struct {
db *gorm.DB
client openai.Client
db *gorm.DB
ctx context.Context
googleCli translate.TranslationClient
// projectID is the Google Cloud project ID used as the "parent" in API calls,
// e.g. "projects/my-project-123/locations/global"
projectID string
}
// New creates a ProductDescriptionService and authenticates against the
// Google Cloud Translation API using a service account key file.
//
// Required configuration (set in .env or environment):
//
// GOOGLE_APPLICATION_CREDENTIALS absolute path to the service account JSON key file
// GOOGLE_CLOUD_PROJECT_ID your Google Cloud project ID
//
// The service account must have the "Cloud Translation API User" role
// (roles/cloudtranslate.user) granted in Google Cloud IAM.
func New() *ProductDescriptionService {
ctx := context.Background()
cfg := config.Get()
// Read the service account key file whose path comes from config / env.
data, err := os.ReadFile(cfg.GoogleTranslate.CredentialsFile)
if err != nil {
log.Fatalf("productDescriptionService: cannot read credentials file %q: %v",
cfg.GoogleTranslate.CredentialsFile, err)
}
// Build OAuth2 credentials scoped to the Cloud Translation API.
// The correct scope for Cloud Translation v3 is "cloud-translation".
creds, err := credentials.DetectDefault(&credentials.DetectOptions{
Scopes: []string{"https://www.googleapis.com/auth/cloud-translation"},
CredentialsJSON: data,
})
if err != nil {
log.Fatalf("productDescriptionService: cannot build Google credentials: %v", err)
}
googleCli, err := translate.NewTranslationClient(ctx, option.WithAuthCredentials(creds))
if err != nil {
log.Fatalf("productDescriptionService: cannot create Translation client: %v", err)
}
return &ProductDescriptionService{
db: db.Get(),
client: openai.NewClient(option.WithAPIKey("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"),
option.WithHTTPClient(&http.Client{Timeout: 300 * time.Second})),
db: db.Get(),
ctx: ctx,
googleCli: *googleCli,
projectID: cfg.GoogleTranslate.ProjectID,
}
}
@@ -102,7 +146,12 @@ func (s *ProductDescriptionService) SaveProductDescription(userID uint, productI
return nil
}
// Updates relevant fields with the "updates" map
// TranslateProductDescription fetches the product description for productFromLangID,
// translates every text field into productToLangID using the Google Cloud
// Translation API (v3 TranslateText), and returns the translated record.
//
// The Google Cloud project must have the Cloud Translation API enabled and the
// service account must hold the "Cloud Translation API User" role.
func (s *ProductDescriptionService) TranslateProductDescription(userID uint, productID uint, productShopID uint, productFromLangID uint, productToLangID uint) (*model.ProductDescription, error) {
var ProductDescription model.ProductDescription
@@ -148,21 +197,36 @@ func (s *ProductDescriptionService) TranslateProductDescription(userID uint, pro
}
request = cleanForPrompt(request)
openai_response, _ := s.client.Responses.New(context.Background(), responses.ResponseNewParams{
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(request)},
Model: openai.ChatModelGPT4_1Mini,
// Model: openai.ChatModelGPT4_1Nano,
})
if openai_response.Status != "completed" {
return nil, responseErrors.ErrOpenAIResponseFail
// TranslateText is the standard Cloud Translation v3 endpoint.
// "parent" must be "projects/<PROJECT_ID>/locations/global" (or a specific region).
// MimeType "text/plain" is used because cleanForPrompt strips HTML attributes;
// switch to "text/html" if you want Google to preserve HTML tags automatically.
req := &translatepb.TranslateTextRequest{
Parent: fmt.Sprintf("projects/%s/locations/global", s.projectID),
TargetLanguageCode: lang.ISOCode,
MimeType: "text/plain",
Contents: []string{request},
}
responseGoogle, err := s.googleCli.TranslateText(s.ctx, req)
if err != nil {
fmt.Println(err)
return nil, err
}
// TranslateText returns one Translation per input string.
if len(responseGoogle.GetTranslations()) == 0 {
return nil, responseErrors.ErrOpenAIBadOutput
}
response := responseGoogle.GetTranslations()[0].GetTranslatedText()
for i := 0; i < len(keys); i++ {
success, resolution := resolveResponse(*fields[i], openai_response.OutputText(), keys[i])
success, resolution := resolveResponse(*fields[i], response, keys[i])
if !success {
return nil, responseErrors.ErrOpenAIBadOutput
}
*fields[i] = resolution
fmt.Println("resolution: ", resolution)
}
return &ProductDescription, nil