some debugging
This commit is contained in:
8
app/service/productTranslationService/google_out.txt
Normal file
8
app/service/productTranslationService/google_out.txt
Normal file
File diff suppressed because one or more lines are too long
165
app/service/productTranslationService/openai_out.txt
Normal file
165
app/service/productTranslationService/openai_out.txt
Normal file
@@ -0,0 +1,165 @@
|
||||
<translation_of_product_description><p>The use of rehabilitation rollers in various types of exercises and treatments positively affects the alleviation of injuries and increases the chances for the patient to return to full physical fitness. They are used in movement rehabilitation, during corrective gymnastics, traditional and sports massages, as they are ideal for lifting and separating limbs. They can also be used to support the knees, feet, arms, and shoulders of the patient. Rehabilitation rollers are also recommended for children; using them during play significantly supports the development of gross motor skills.</p>
|
||||
<p>Thanks to a wide range of colors and varied sizes, it is possible to compose a set for exercises necessary in every physiotherapy office, massage salon, as well as in schools and kindergartens.</p>
|
||||
<p>The rehabilitation roller is a medical device in accordance with the essential requirements for medical devices and within the meaning of the Medical Devices Act, registered in the Register of Medical Devices maintained by the Office for Registration of Medicinal Products, Medical Devices, and Biocidal Products, equipped with the manufacturer's declaration of conformity and marked with the CE mark.</p>
|
||||
<p></p>
|
||||
<p><img alt="Medical device"></img></p>
|
||||
<h4><strong>Recommended use:</strong></h4>
|
||||
<ul>
|
||||
<li>in rehabilitation</li>
|
||||
<li>during massages (traditional, sports)</li>
|
||||
<li>in corrective gymnastics (especially for children)</li>
|
||||
<li>for alleviating injuries of specific parts of the body</li>
|
||||
<li>for support: knees, ankles, patient's head</li>
|
||||
<li>in exercises developing children's motor skills</li>
|
||||
<li>in beauty salons</li>
|
||||
<li>in children's playrooms</li>
|
||||
</ul>
|
||||
<p></p>
|
||||
<h4><strong>Material specification:</strong></h4>
|
||||
<p><strong>Cover:</strong> material with a PVC coating intended for medical devices, making it very easy to clean and disinfect:</p>
|
||||
<ul>
|
||||
<li>material compliant with the REACH regulation, certified with the STANDARD 100 by OEKO-TEX ® certificate</li>
|
||||
<li>phthalate-free</li>
|
||||
<li>fire-resistant</li>
|
||||
<li>resistant to bodily fluids (blood, urine, sweat) and alcohol</li>
|
||||
<li>UV-resistant, therefore suitable for outdoor use</li>
|
||||
<li>scratch-resistant</li>
|
||||
<li>oil-resistant</li>
|
||||
</ul>
|
||||
<p><img alt="REACH"></img><img alt="Oeko Tex Standard 100 Certificate"></img><img alt="Phthalate-Free"></img><img alt="Fire-Resistant"></img><img alt="Alcohol-Resistant"></img><img alt="UV-Resistant"></img><img alt="Suitable for Outdoor Use"></img><img alt="Scratch-Resistant"></img><img alt="Oil-Resistant"></img></p>
|
||||
<p><strong>Filling:</strong> medium-hard polyurethane foam with enhanced resistance to deformation:</p>
|
||||
<ul>
|
||||
<li>has a HYGIENIC CERTIFICATE issued by the Institute of Maritime and Tropical Medicine in Gdynia</li>
|
||||
<li>has the STANDARD 100 by OEKO-TEX ® certificate – product class I, issued by the Textile Research Institute in Łódź</li>
|
||||
<li>produced from high-quality raw materials that do not deplete the ozone layer</li>
|
||||
</ul>
|
||||
<p><img alt="Oeko Tex Standard 100 Certificate"></img><img alt="Hygienic Certificate"></img><img alt="Hygienic Certificate"></img></p>
|
||||
<p></p>
|
||||
<p></p></translation_of_product_description>
|
||||
|
||||
<translation_of_product_short_description><p>Rehabilitation rollers find their use in various types of exercises. They are used in movement rehabilitation, during corrective gymnastics, traditional and sports massages, as they are ideal for lifting and separating limbs. They can also be used to support the knees, feet, arms, and shoulders of the patient. Rehabilitation rollers are also recommended for children; using them during play significantly supports the development of gross motor skills. The product is certified as a medical device.</p></translation_of_product_short_description>
|
||||
|
||||
<translation_of_product_meta_description></translation_of_product_meta_description>
|
||||
|
||||
<translation_of_product_meta_title></translation_of_product_meta_title>
|
||||
|
||||
<translation_of_product_name>Rehabilitation Roller 10 x 30 cm</translation_of_product_name>
|
||||
|
||||
<translation_of_product_available_now>available</translation_of_product_available_now>
|
||||
|
||||
<translation_of_product_available_later>on order</translation_of_product_available_later>
|
||||
|
||||
<translation_of_product_usage><p>I. Cleaning and maintenance</p>
|
||||
<p>The upholstery should be cleaned superficially using permitted agents:</p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><span><b>Type of dirt</b></span></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span><span><b>Permitted agents</b></span></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span><span><b>Procedure</b></span></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><b><span>Everyday dirt<o:p></o:p></span></b></span></p>
|
||||
<p><span> </span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Mild detergent, preferably gray soap solution<o:p></o:p></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Clean regularly using a sponge or soft brush. Finally, wipe the cleaned area with a damp cloth and then dry it (to remove detergent residues).<o:p></o:p></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><b><span>Local, stronger dirt<o:p></o:p></span></b></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>25% ethyl alcohol solution<o:p></o:p></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Gently wipe with a gauze tampon soaked in the solution. Finally, wipe the cleaned area with a damp cloth and then dry it (to remove detergent residues).<o:p></o:p></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><b><span>Disinfection<o:p></o:p></span></b></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Widely available disinfectants containing:</span></p>
|
||||
<p><span>- active chlorine – sodium dichloroisocyanurate, max concentration 10000 ppm </span></p>
|
||||
<p><span>- active chlorine - chlorine dioxide in solution up to 20,000 ppm </span></p>
|
||||
<p><span>- isopropyl alcohol max concentration 70 %</span> </p>
|
||||
<p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Disinfect according to the recommendations of the manufacturer of the used agent.<o:p></o:p></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><b><span>Before using any agent other than mild detergent, test the effect in an inconspicuous place, and perform cleaning very carefully.</span></b></span><span><o:p></o:p></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><br></br>II. Information</p>
|
||||
<p></p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><img alt=""></img></td>
|
||||
<td>
|
||||
<p>Shampoo using a sponge<o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><o:p> <img alt=""></img></o:p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p>Do not wash!!! (delicate products) <o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><o:p><img></img></o:p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p>Do not bleach!!! (do not use bleaching agents that release free chlorine)<o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><o:p> <img alt=""></img></o:p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p>Do not iron!!! (avoid contact with hot surfaces e.g., radiators)<o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><o:p> <img alt=""></img></o:p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p>Do not dry clean!!!<o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p></p>
|
||||
<p>III. Warranty conditions</p>
|
||||
<p>Warranty does not cover:</p>
|
||||
<ul>
|
||||
<li>Permanent discolorations caused by contact with clothing containing active, migrating dyes (e.g., jeans, suede, etc.)</li>
|
||||
<li>Marks from pens, inks, markers, etc. containing active dyes</li>
|
||||
<li>Damage caused by high temperature, corrosive liquids, fire</li>
|
||||
<li>Mechanical damages caused by pets and other users</li>
|
||||
<li>Defects resulting from improper maintenance</li>
|
||||
</ul></translation_of_product_usage>
|
||||
@@ -0,0 +1,447 @@
|
||||
package productTranslationService
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/xml"
|
||||
"fmt"
|
||||
"io"
|
||||
"log"
|
||||
"net/http"
|
||||
"os"
|
||||
"slices"
|
||||
"strings"
|
||||
"time"
|
||||
|
||||
"cloud.google.com/go/auth/credentials"
|
||||
translate "cloud.google.com/go/translate/apiv3"
|
||||
"cloud.google.com/go/translate/apiv3/translatepb"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/config"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/model"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/repos/productDescriptionRepo"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/service/langsService"
|
||||
"git.ma-al.com/goc_daniel/b2b/app/utils/responseErrors"
|
||||
"github.com/openai/openai-go/v3"
|
||||
"github.com/openai/openai-go/v3/option"
|
||||
"github.com/openai/openai-go/v3/responses"
|
||||
googleopt "google.golang.org/api/option"
|
||||
)
|
||||
|
||||
type ProductTranslationService struct {
|
||||
productDescriptionRepo productDescriptionRepo.UIProductDescriptionRepo
|
||||
ctx context.Context
|
||||
googleCli translate.TranslationClient
|
||||
projectID string
|
||||
openAIClient openai.Client
|
||||
}
|
||||
|
||||
// New creates a ProductTranslationService 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() *ProductTranslationService {
|
||||
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("ProductTranslationService: 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("ProductTranslationService: cannot build Google credentials: %v", err)
|
||||
}
|
||||
|
||||
googleCli, err := translate.NewTranslationClient(ctx, googleopt.WithAuthCredentials(creds))
|
||||
if err != nil {
|
||||
log.Fatalf("ProductTranslationService: cannot create Translation client: %v", err)
|
||||
}
|
||||
|
||||
openAIClient := openai.NewClient(option.WithAPIKey(os.Getenv("OPENAI_KEY")),
|
||||
option.WithHTTPClient(&http.Client{Timeout: 300 * time.Second})) // five minutes timeout
|
||||
|
||||
return &ProductTranslationService{
|
||||
productDescriptionRepo: productDescriptionRepo.New(),
|
||||
ctx: ctx,
|
||||
openAIClient: openAIClient,
|
||||
googleCli: *googleCli,
|
||||
projectID: cfg.GoogleTranslate.ProjectID,
|
||||
}
|
||||
}
|
||||
|
||||
func (s *ProductTranslationService) GetProductDescription(userID uint, productID uint, productLangID uint) (*model.ProductDescription, error) {
|
||||
return s.productDescriptionRepo.GetProductDescription(productID, productLangID)
|
||||
}
|
||||
|
||||
// Updates relevant fields with the "updates" map
|
||||
func (s *ProductTranslationService) SaveProductDescription(userID uint, productID uint, productLangID uint, updates map[string]string) error {
|
||||
// only some fields can be affected
|
||||
allowedFields := []string{"description", "description_short", "meta_description", "meta_title", "name", "available_now", "available_later", "usage"}
|
||||
for key := range updates {
|
||||
if !slices.Contains(allowedFields, key) {
|
||||
return responseErrors.ErrBadField
|
||||
}
|
||||
}
|
||||
|
||||
// check that fields description, description_short and usage, if they exist, have a valid html format
|
||||
mustBeHTML := []string{"description", "description_short", "usage"}
|
||||
for i := 0; i < len(mustBeHTML); i++ {
|
||||
if text, exists := updates[mustBeHTML[i]]; exists {
|
||||
if !isValidXHTML(text) {
|
||||
return responseErrors.ErrInvalidXHTML
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
err := s.productDescriptionRepo.CreateIfDoesNotExist(productID, productLangID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
return s.productDescriptionRepo.UpdateFields(productID, productLangID, updates)
|
||||
}
|
||||
|
||||
// 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 *ProductTranslationService) TranslateProductDescription(userID uint, productID uint, productFromLangID uint, productToLangID uint, aiModel string) (*model.ProductDescription, error) {
|
||||
|
||||
productDescription, err := s.productDescriptionRepo.GetProductDescription(productID, productFromLangID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
productDescription.LangID = productToLangID
|
||||
|
||||
// we translate all changeable fields, and we keep the exact same HTML structure in relevant fields.
|
||||
lang, err := langsService.LangSrv.GetLanguageById(productToLangID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
fields := []*string{&productDescription.Description,
|
||||
&productDescription.DescriptionShort,
|
||||
&productDescription.MetaDescription,
|
||||
&productDescription.MetaTitle,
|
||||
&productDescription.Name,
|
||||
&productDescription.AvailableNow,
|
||||
&productDescription.AvailableLater,
|
||||
&productDescription.Usage,
|
||||
}
|
||||
keys := []string{"translation_of_product_description",
|
||||
"translation_of_product_short_description",
|
||||
"translation_of_product_meta_description",
|
||||
"translation_of_product_meta_title",
|
||||
"translation_of_product_name",
|
||||
"translation_of_product_available_now",
|
||||
"translation_of_product_available_later",
|
||||
"translation_of_product_usage",
|
||||
}
|
||||
|
||||
request := ""
|
||||
if aiModel == "OpenAI" {
|
||||
request = "Translate to " + lang.ISOCode + " without changing the html structure.\n"
|
||||
}
|
||||
for i := 0; i < len(keys); i++ {
|
||||
request += "\n<" + keys[i] + ">"
|
||||
request += *fields[i]
|
||||
request += "</" + keys[i] + ">\n"
|
||||
}
|
||||
if aiModel == "OpenAI" {
|
||||
request = cleanForPrompt(request)
|
||||
}
|
||||
|
||||
if aiModel == "OpenAI" {
|
||||
response, _ := s.openAIClient.Responses.New(context.Background(), responses.ResponseNewParams{
|
||||
Input: responses.ResponseNewParamsInputUnion{OfString: openai.String(request)},
|
||||
Model: openai.ChatModelGPT4_1Mini,
|
||||
// Model: openai.ChatModelGPT4_1Nano,
|
||||
})
|
||||
if response.Status != "completed" {
|
||||
return nil, responseErrors.ErrAIResponseFail
|
||||
}
|
||||
|
||||
for i := 0; i < len(keys); i++ {
|
||||
success, resolution := resolveResponse(*fields[i], response.OutputText(), keys[i])
|
||||
if !success {
|
||||
return nil, responseErrors.ErrAIBadOutput
|
||||
}
|
||||
*fields[i] = resolution
|
||||
|
||||
// fmt.Println(resolution)
|
||||
}
|
||||
|
||||
} else if aiModel == "Google" {
|
||||
// TranslateText is the standard Cloud Translation v3 endpoint.
|
||||
req := &translatepb.TranslateTextRequest{
|
||||
Parent: fmt.Sprintf("projects/%s/locations/global", s.projectID),
|
||||
TargetLanguageCode: lang.ISOCode,
|
||||
MimeType: "text/html",
|
||||
Contents: []string{request},
|
||||
}
|
||||
responseGoogle, err := s.googleCli.TranslateText(s.ctx, req)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// TranslateText returns one Translation per input string.
|
||||
if len(responseGoogle.GetTranslations()) == 0 {
|
||||
return nil, responseErrors.ErrAIBadOutput
|
||||
}
|
||||
response := responseGoogle.GetTranslations()[0].GetTranslatedText()
|
||||
|
||||
for i := 0; i < len(keys); i++ {
|
||||
success, match := getStringInBetween(response, "<"+keys[i]+">", "</"+keys[i]+">")
|
||||
if !success || !isValidXHTML(match) {
|
||||
return nil, responseErrors.ErrAIBadOutput
|
||||
}
|
||||
*fields[i] = match
|
||||
|
||||
// fmt.Println(match)
|
||||
}
|
||||
}
|
||||
|
||||
return productDescription, nil
|
||||
}
|
||||
|
||||
func cleanForPrompt(s string) string {
|
||||
r := strings.NewReader(s)
|
||||
d := xml.NewDecoder(r)
|
||||
|
||||
prompt := ""
|
||||
|
||||
// Configure the decoder for HTML; leave off strict and autoclose for XHTML
|
||||
d.Strict = true
|
||||
d.AutoClose = xml.HTMLAutoClose
|
||||
d.Entity = xml.HTMLEntity
|
||||
for {
|
||||
token, err := d.Token()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
|
||||
switch v := token.(type) {
|
||||
case xml.StartElement:
|
||||
prompt += "<" + attrName(v.Name)
|
||||
|
||||
for _, attr := range v.Attr {
|
||||
if v.Name.Local == "img" && attr.Name.Local == "alt" {
|
||||
prompt += fmt.Sprintf(` %s="%s"`, attrName(attr.Name), attr.Value)
|
||||
}
|
||||
}
|
||||
|
||||
prompt += ">"
|
||||
case xml.EndElement:
|
||||
prompt += "</" + attrName(v.Name) + ">"
|
||||
case xml.CharData:
|
||||
prompt += string(v)
|
||||
case xml.Comment:
|
||||
case xml.ProcInst:
|
||||
case xml.Directive:
|
||||
}
|
||||
}
|
||||
|
||||
return prompt
|
||||
}
|
||||
|
||||
func resolveResponse(original string, response string, key string) (bool, string) {
|
||||
success, match := getStringInBetween(response, "<"+key+">", "</"+key+">")
|
||||
if !success || !isValidXHTML(match) {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
success, resolution := rebuildFromResponse("<"+key+">"+original+"</"+key+">", "<"+key+">"+match+"</"+key+">")
|
||||
if !success {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
return true, resolution[2+len(key) : len(resolution)-3-len(key)]
|
||||
}
|
||||
|
||||
// getStringInBetween returns empty string if no start or end string found
|
||||
func getStringInBetween(str string, start string, end string) (success bool, result string) {
|
||||
s := strings.Index(str, start)
|
||||
if s == -1 {
|
||||
return false, ""
|
||||
}
|
||||
s += len(start)
|
||||
e := strings.Index(str[s:], end)
|
||||
if e == -1 {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
return true, str[s : s+e]
|
||||
}
|
||||
|
||||
// isValidXHTML checks if the string obeys the XHTML format
|
||||
func isValidXHTML(s string) bool {
|
||||
r := strings.NewReader(s)
|
||||
d := xml.NewDecoder(r)
|
||||
|
||||
// Configure the decoder for HTML; leave off strict and autoclose for XHTML
|
||||
d.Strict = true
|
||||
d.AutoClose = xml.HTMLAutoClose
|
||||
d.Entity = xml.HTMLEntity
|
||||
for {
|
||||
_, err := d.Token()
|
||||
switch err {
|
||||
case io.EOF:
|
||||
return true // We're done, it's valid!
|
||||
case nil:
|
||||
default:
|
||||
return false // Oops, something wasn't right
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rebuilds HTML using the original HTML as a template and the response as a source
|
||||
// Assumes that both original and response have the exact same XML structure
|
||||
func rebuildFromResponse(s_original string, s_response string) (bool, string) {
|
||||
|
||||
r_original := strings.NewReader(s_original)
|
||||
d_original := xml.NewDecoder(r_original)
|
||||
|
||||
r_response := strings.NewReader(s_response)
|
||||
d_response := xml.NewDecoder(r_response)
|
||||
|
||||
result := ""
|
||||
|
||||
// Configure the decoder for HTML; leave off strict and autoclose for XHTML
|
||||
d_original.Strict = true
|
||||
d_original.AutoClose = xml.HTMLAutoClose
|
||||
d_original.Entity = xml.HTMLEntity
|
||||
|
||||
d_response.Strict = true
|
||||
d_response.AutoClose = xml.HTMLAutoClose
|
||||
d_response.Entity = xml.HTMLEntity
|
||||
|
||||
token_original, err_original := d_original.Token()
|
||||
token_response, err_response := d_response.Token()
|
||||
|
||||
for {
|
||||
// err_original can only be EOF or nil.
|
||||
if err_original != nil || err_response != nil {
|
||||
if err_original != err_response {
|
||||
return false, ""
|
||||
}
|
||||
return true, result
|
||||
}
|
||||
|
||||
switch v_original := token_original.(type) {
|
||||
case xml.StartElement:
|
||||
switch v_response := token_response.(type) {
|
||||
case xml.StartElement:
|
||||
if v_original.Name.Space != v_response.Name.Space || v_original.Name.Local != v_response.Name.Local {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
result += "<" + attrName(v_original.Name)
|
||||
|
||||
for _, attr := range v_original.Attr {
|
||||
if v_original.Name.Local != "img" || attr.Name.Local != "alt" {
|
||||
result += fmt.Sprintf(` %s="%s"`, attrName(attr.Name), attr.Value)
|
||||
}
|
||||
}
|
||||
|
||||
for _, attr := range v_response.Attr {
|
||||
if v_response.Name.Local == "img" && attr.Name.Local == "alt" {
|
||||
result += fmt.Sprintf(` %s="%s"`, attrName(attr.Name), attr.Value)
|
||||
}
|
||||
}
|
||||
result += ">"
|
||||
|
||||
case xml.CharData:
|
||||
result += string(v_response)
|
||||
token_response, err_response = d_response.Token()
|
||||
continue
|
||||
|
||||
default:
|
||||
return false, ""
|
||||
}
|
||||
|
||||
case xml.EndElement:
|
||||
switch v_response := token_response.(type) {
|
||||
case xml.EndElement:
|
||||
if v_original.Name.Space != v_response.Name.Space || v_original.Name.Local != v_response.Name.Local {
|
||||
return false, ""
|
||||
}
|
||||
|
||||
if v_original.Name.Local != "img" {
|
||||
result += "</" + attrName(v_original.Name) + ">"
|
||||
}
|
||||
|
||||
case xml.CharData:
|
||||
result += string(v_response)
|
||||
token_response, err_response = d_response.Token()
|
||||
continue
|
||||
|
||||
default:
|
||||
return false, ""
|
||||
}
|
||||
|
||||
case xml.CharData:
|
||||
switch v_response := token_response.(type) {
|
||||
case xml.CharData:
|
||||
result += string(v_response)
|
||||
|
||||
case xml.StartElement:
|
||||
result += string(v_original)
|
||||
token_original, err_original = d_original.Token()
|
||||
continue
|
||||
|
||||
case xml.EndElement:
|
||||
result += string(v_original)
|
||||
token_original, err_original = d_original.Token()
|
||||
continue
|
||||
|
||||
default:
|
||||
return false, ""
|
||||
}
|
||||
|
||||
case xml.Comment:
|
||||
result += "<!--" + string(v_original) + "-->"
|
||||
token_original, err_original = d_original.Token()
|
||||
continue
|
||||
|
||||
case xml.ProcInst:
|
||||
if len(v_original.Inst) == 0 {
|
||||
result += "<?" + v_original.Target + "?>"
|
||||
} else {
|
||||
result += "<?" + v_original.Target + " " + string(v_original.Inst) + "?>"
|
||||
}
|
||||
token_original, err_original = d_original.Token()
|
||||
continue
|
||||
|
||||
case xml.Directive:
|
||||
result += "<!" + string(v_original) + ">"
|
||||
token_original, err_original = d_original.Token()
|
||||
continue
|
||||
}
|
||||
|
||||
token_original, err_original = d_original.Token()
|
||||
token_response, err_response = d_response.Token()
|
||||
}
|
||||
}
|
||||
|
||||
func attrName(name xml.Name) string {
|
||||
if name.Space == "" {
|
||||
return name.Local
|
||||
} else {
|
||||
return name.Space + ":" + name.Local
|
||||
}
|
||||
}
|
||||
167
app/service/productTranslationService/test_in.txt
Normal file
167
app/service/productTranslationService/test_in.txt
Normal file
@@ -0,0 +1,167 @@
|
||||
Translate to en without changing the html structure.
|
||||
|
||||
<translation_of_product_description><p>Zastosowanie wałków rehabilitacyjnych w różnego rodzaju ćwiczeniach oraz zabiegach wpływa pozytywnie na łagodzenie urazów oraz zwiększa szanse na powrót pacjenta do pełnej sprawności fizycznej. Stosowane są w rehabilitacji ruchowej, podczas gimnastyki korekcyjnej, masaży tradycyjnych i sportowych, gdyż idealnie nadają się do unoszenia i separacji kończyn. Można je wykorzystać także do podpierania kolan, stóp, ramion, a także barków pacjenta. Wałki rehabilitacyjne polecane są także dla dzieci, wykorzystanie ich podczas zabawy znacznie wspiera rozwój dużej motoryki.</p>
|
||||
<p>Dzięki szerokiej ofercie kolorystycznej oraz zróżnicowanym rozmiarom, możliwe jest skomponowanie zestawu do ćwiczeń niezbędnego w każdym gabinecie fizjoterapeutycznym, gabinecie masażu czy też szkole i przedszkolu. </p>
|
||||
<p>Wałek rehabilitacyjny jest wyrobem medycznym zgodnie z wymaganiami zasadniczymi dla wyrobów medycznych i w rozumieniu ustawy o wyrobach medycznych, zgłoszonym do Rejestru Wyrobów Medycznych prowadzonego przez Urząd Rejestracji Produktów Leczniczych, Wyrobów Medycznych i Produktów Biobójczych, wyposażonym w deklarację zgodności producenta i opatrzonym znakiem CE.</p>
|
||||
<p></p>
|
||||
<p><img alt="Wyrób medyczny"></img></p>
|
||||
<h4><strong>Polecane zastosowanie:</strong></h4>
|
||||
<ul>
|
||||
<li>w rehabilitacji</li>
|
||||
<li>podczas masaży (tradycyjnych, sportowych)</li>
|
||||
<li>w gimnastyce korekcyjnej (w tym zwłaszcza dzieci)</li>
|
||||
<li>w łagodzeniu urazów poszczególnych części ciała</li>
|
||||
<li>dla podparcia: kolan, kostek, głowy pacjenta</li>
|
||||
<li>w ćwiczeniach rozwijających motorykę dzieci</li>
|
||||
<li>w salonach kosmetycznych</li>
|
||||
<li>w salach zabaw dla dzieci</li>
|
||||
</ul>
|
||||
<p></p>
|
||||
<h4><strong>Specyfikacja materiału:</strong></h4>
|
||||
<p><strong>Pokrowiec:</strong> materiał z powłoką PCV przeznaczony dla wyrobów medycznych, dzięki czemu jest bardzo łatwy w czyszczeniu oraz dezynfekcji:</p>
|
||||
<ul>
|
||||
<li>materiał zgodny z rozporządzeniem REACH, posiada atest Certyfikat STANDARD 100 by OEKO-TEX ®</li>
|
||||
<li>nie zawiera ftalanów</li>
|
||||
<li>ognioodporny</li>
|
||||
<li>odporny na płyny fizjologiczne (krew, mocz, pot) oraz na alkohol</li>
|
||||
<li>odporny na UV, przez co może być także używany na zewnątrz</li>
|
||||
<li>odporny na zadrapania</li>
|
||||
<li>olejoodporny</li>
|
||||
</ul>
|
||||
<p><img alt="REACH"></img><img alt="Certyfikat Oeko Tex Standard 100"></img><img alt="Nie zwiera ftalanów"></img><img alt="Ognioodporny"></img><img alt="Odporny na alkohol"></img><img alt="Odporny na UV"></img><img alt="Przeznaczony na zewnątrz"></img><img alt="Odporny na zadrapania"></img><img alt="Olejoodporny"></img></p>
|
||||
<p><strong>Wypełnienie:</strong> średnio twarda pianka poliuretanowa o podwyższonej odporności na odkształcenia:</p>
|
||||
<ul>
|
||||
<li>posiada ATEST HIGIENICZNY wydany przez Instytut Medycyny Morskiej i Tropikalnej w Gdyni</li>
|
||||
<li>posiada atest Certyfikat STANDARD 100 by OEKO-TEX ® – klasa produktów I wydany przez Instytut Włókiennictwa w Łodzi</li>
|
||||
<li>produkowana z surowców o podwyższonej jakości, nie powodujących zubożenia warstwy ozonowej </li>
|
||||
</ul>
|
||||
<p><img alt="Certyfikat Oeko Tex Standard 100"></img><img alt="Atest higieniczny"></img><img alt="Atest higieniczny"></img></p>
|
||||
<p></p>
|
||||
<p></p></translation_of_product_description>
|
||||
|
||||
<translation_of_product_short_description><p>Wałki rehabilitacyjne znajdują swoje zastosowanie w różnego rodzaju ćwiczeniach. Stosowane są w rehabilitacji ruchowej, podczas gimnastyki korekcyjnej, masaży tradycyjnych i sportowych, gdyż idealnie nadają się do unoszenia i separacji kończyn. Można je wykorzystać także do podpierania kolan, stóp, ramion, a także barków pacjenta. Wałki rehabilitacyjne polecane są także dla dzieci, wykorzystanie ich podczas zabawy, znacznie wspiera rozwój dużej motoryki. Produkt posiada certyfikację jako wyrób medyczny. </p></translation_of_product_short_description>
|
||||
|
||||
<translation_of_product_meta_description></translation_of_product_meta_description>
|
||||
|
||||
<translation_of_product_meta_title></translation_of_product_meta_title>
|
||||
|
||||
<translation_of_product_name>Wałek rehabilitacyjny 10 x 30 cm</translation_of_product_name>
|
||||
|
||||
<translation_of_product_available_now>dostępny</translation_of_product_available_now>
|
||||
|
||||
<translation_of_product_available_later>na zamówienie</translation_of_product_available_later>
|
||||
|
||||
<translation_of_product_usage><p>I. Czyszczenie i konserwacja</p>
|
||||
<p>Tapicerkę należy czyścić powierzchniowo stosując dozwolone środki:</p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><span><b>Rodzaj zabrudzenia</b></span></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span><span><b>Dozwolone środki</b></span></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span><span><b>Postępowanie</b></span></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><b><span>Codzienne zabrudzenia<o:p></o:p></span></b></span></p>
|
||||
<p><span> </span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Łagodny detergent najlepiej roztwór szarego mydła<o:p></o:p></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Czyścić regularnie z użyciem gąbki lub miękkiej szczotki. Na koniec przetrzeć czyszczone miejsce wilgotną szmatką po czym wytrzeć do sucha (w celu usunięcia pozostałości detergentu).<o:p></o:p></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><b><span>Miejscowe, silniejsze zabrudzenia<o:p></o:p></span></b></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>25% roztwór alkoholu etylowego<o:p></o:p></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Delikatnie przecierać nasączonym tamponem z gazy. Na koniec przetrzeć czyszczone miejsce wilgotną szmatką po czym wytrzeć do sucha (w celu usunięcia pozostałości detergentu).<o:p></o:p></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><b><span>Dezynfekcja<o:p></o:p></span></b></span></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Ogólnodostępne środki do dezynfekcji zawierające:</span></p>
|
||||
<p><span>- aktywny chlor – dichloroizocyjanuran sodu, max stężenie 10000 ppm </span></p>
|
||||
<p><span>- aktywny chlor - dwutlenek chloru w roztworze do 20 000 ppm </span></p>
|
||||
<p><span>- alkohol izopropylowy max stężenie 70 %</span> </p>
|
||||
<p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p><span>Dezynfekować zgodnie z zaleceniami producenta używanego środka.<o:p></o:p></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><span><b><span>Przed użyciem środka innego niż łagodny detergent trzeba sprawdzić efekt w niewidocznym miejscu, a samo czyszczenie wykonać bardzo ostrożnie.</span></b></span><span><o:p></o:p></span></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p><br></br>II. Informacje</p>
|
||||
<p></p>
|
||||
<table>
|
||||
<tbody>
|
||||
<tr>
|
||||
<td><img alt=""></img></td>
|
||||
<td>
|
||||
<p>Szamponować przy użyciu gąbki<o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><o:p> <img alt=""></img></o:p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p>Nie prać!!! (delikatne wyroby) <o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><o:p><img></img></o:p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p>Nie chlorować!!! (nie stosować do bielenia związków wydzielających wolny chlor)<o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><o:p> <img alt=""></img></o:p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p>Nie prasować!!! (nie dopuszczać do kontaktu z nagrzanymi powierzchniami np. kaloryfer)<o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td>
|
||||
<p><o:p> <img alt=""></img></o:p></p>
|
||||
</td>
|
||||
<td>
|
||||
<p>Nie czyścić chemicznie!!!<o:p></o:p></p>
|
||||
</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
<p></p>
|
||||
<p>III. Warunki gwarancji</p>
|
||||
<p>Gwarancji nie podlegają:</p>
|
||||
<ul>
|
||||
<li>Trwałe przebarwienia powstałe wskutek kontaktu z odzieżą zawierającą aktywne, migrujące barwniki (np. jeans, zamsz itp.)</li>
|
||||
<li>Ślady z długopisu, tuszu, mazaków itp. zawierające aktywne barwniki</li>
|
||||
<li>Uszkodzenia wywołane przez wysoką temperaturę, płyny żrące, ogień</li>
|
||||
<li>Uszkodzenia mechaniczne spowodowane przez zwierzęta domowe i innych użytkowników</li>
|
||||
<li>Wady powstałe wskutek niewłaściwej konserwacji</li>
|
||||
</ul></translation_of_product_usage>
|
||||
Reference in New Issue
Block a user