View Javadoc
1   package com.acumenvelocity.ath.steps;
2   
3   import java.util.ArrayList;
4   import java.util.List;
5   
6   import com.acumenvelocity.ath.common.Const;
7   import com.acumenvelocity.ath.common.ControllerUtil;
8   import com.acumenvelocity.ath.common.Log;
9   import com.acumenvelocity.ath.common.OkapiUtil;
10  import com.acumenvelocity.ath.gct.v3.AthTranslation;
11  
12  import net.sf.okapi.common.Event;
13  import net.sf.okapi.common.IParameters;
14  import net.sf.okapi.common.IResource;
15  import net.sf.okapi.common.MimeTypeMapper;
16  import net.sf.okapi.common.Util;
17  import net.sf.okapi.common.annotation.AltTranslationsAnnotation;
18  import net.sf.okapi.common.query.MatchType;
19  import net.sf.okapi.common.resource.ISegments;
20  import net.sf.okapi.common.resource.ITextUnit;
21  import net.sf.okapi.common.resource.Segment;
22  import net.sf.okapi.common.resource.TextContainer;
23  import net.sf.okapi.common.resource.TextFragment;
24  import net.sf.okapi.common.resource.TextFragmentUtil;
25  import net.sf.okapi.lib.translation.QueryUtil;
26  
27  public class BatchMtStep extends BaseTuBatchProcessingStep {
28  
29    private BatchMtParameters params = new BatchMtParameters();
30    private final List<String> sourceSegments = new ArrayList<>();
31    private final List<SegmentInfo> segmentInfos = new ArrayList<>();
32    private final QueryUtil qutil = new QueryUtil();
33  
34    @Override
35    public String getName() {
36      return "Batch Google Cloud Translation v3";
37    }
38  
39    @Override
40    public String getDescription() {
41      return "Translates a batch of collected TU segments with the "
42          + "Google Cloud Translate v3 service";
43    }
44  
45    @Override
46    public BatchMtParameters getParameters() {
47      return params;
48    }
49  
50    @Override
51    public void setParameters(IParameters params) {
52      this.params = (BatchMtParameters) params;
53    }
54  
55    @Override
56    protected void clear() {
57      sourceSegments.clear();
58      segmentInfos.clear();
59    }
60  
61    /**
62     * Pre-process text units: collect source segments
63     */
64    private void preProcessTextUnit(ITextUnit tu) {
65      TextContainer source = tu.getSource();
66  
67      if (source == null) {
68        Log.error(getClass(), "Source of TU '{}' is null", tu.getId());
69        return;
70      }
71  
72      TextContainer target = tu.getTarget(getTargetLocale());
73      ISegments targetSegments = target == null ? null : target.getSegments();
74  
75      // Process each segment in the text unit
76      for (Segment segment : source.getSegments()) {
77        TextFragment content = segment.getContent();
78  
79        // Skip empty segments
80        if (content == null || content.isEmpty()) {
81          Log.trace(getClass(), "Skipping empty segment in TU '{}'", tu.getId());
82          continue;
83        }
84  
85        if (targetSegments != null) {
86          Segment tseg = targetSegments.get(segment.getId());
87  
88          if (tseg != null && tseg.getContent() != null && !tseg.getContent().isEmpty()) {
89            // Already translated by a previous leveraging step
90            continue;
91          }
92        }
93  
94        String sourceText = null;
95  
96        if (params.isMtSendPlainText()) {
97          sourceText = content.getText();
98          params.setMimeType(MimeTypeMapper.PLAIN_TEXT_MIME_TYPE);
99  
100       } else {
101         sourceText = qutil.toCodedHTML(content);
102         params.setMimeType(MimeTypeMapper.HTML_MIME_TYPE);
103       }
104 
105       sourceSegments.add(sourceText);
106 
107       // Store segment info for later mapping
108       segmentInfos.add(new SegmentInfo(tu, segment.getId()));
109 
110       Log.trace(getClass(), "Collected segment [{}]: '{}'",
111           sourceSegments.size() - 1, sourceText);
112     }
113 
114   }
115 
116   /**
117    * Post-process text units: set target segments
118    */
119   private void postProcessTextUnits(List<String> targetSegments) {
120     if (Util.isEmpty(targetSegments)) {
121       Log.warn(getClass(), "No evaluation results available");
122       return;
123     }
124 
125     if (targetSegments.size() != segmentInfos.size()) {
126       Log.error(getClass(), "Mismatch: {} target segments, but {} segment infos",
127           targetSegments.size(), segmentInfos.size());
128 
129       return;
130     }
131 
132     // Process each target segment
133     for (int i = 0; i < targetSegments.size(); i++) {
134       String targetText = targetSegments.get(i);
135       SegmentInfo segInfo = segmentInfos.get(i);
136 
137       ITextUnit tu = segInfo.textUnit;
138       TextContainer source = tu.getSource();
139       String segmentId = segInfo.segmentId;
140 
141       // Get or create target container
142       TextContainer target = tu.getTarget(getTargetLocale());
143 
144       if (target == null) {
145         target = tu.createTarget(getTargetLocale(), false, IResource.COPY_SEGMENTATION);
146         Log.trace(getClass(), "Created target container for TU '{}'", tu.getId());
147       }
148 
149       // Get or create target segment
150       Segment sseg = source.getSegments().get(segmentId);
151       Segment tseg = target.getSegments().get(segmentId);
152 
153       if (tseg == null) {
154         tseg = new Segment(segmentId);
155         target.append(tseg);
156 
157         Log.trace(getClass(), "Created target segment '{}' in TU '{}'", segmentId, tu.getId());
158       }
159 
160       TextFragment targetTf;
161 
162       if (params.isMtSendPlainText()) {
163         targetTf = new TextFragment(targetText);
164 
165       } else {
166         targetTf = qutil.fromCodedHTMLToFragment(targetText, null);
167       }
168 
169       TextFragment segSource = sseg.getContent();
170 
171       OkapiUtil.removeExtraCodes(segSource.getCodes(), targetTf);
172 
173       // Align codes and copy metadata from source to target
174       TextFragmentUtil.alignAndCopyCodeMetadata(segSource, targetTf, true, true);
175 
176       // Rearrange opening and closing codes
177       OkapiUtil.rearrangeCodes(segSource.getCodes(), targetTf);
178 
179       tseg.setContent(targetTf);
180 
181       // Mark as MT
182       AltTranslationsAnnotation ata = new AltTranslationsAnnotation();
183 
184       ata.add(getSourceLocale(), getTargetLocale(), sseg.getContent(), sseg.getContent(), targetTf,
185           MatchType.MT, 95, Const.MT_PROVIDER_GOOGLE_MT_V3, 95, 95);
186 
187       tseg.setAnnotation(ata);
188     }
189 
190   }
191 
192   @Override
193   protected void processTuEvents(List<Event> tuEvents) {
194     for (Event tue : tuEvents) {
195       ITextUnit tu = tue.getTextUnit();
196       preProcessTextUnit(tu);
197     }
198 
199     Log.info(getClass(), "Collected {} source segments from {} text units",
200         sourceSegments.size(), tuEvents.size());
201 
202     List<String> targetSegments = AthTranslation.translateBatch(
203         sourceSegments,
204         getSourceLocale().toString(),
205         getTargetLocale().toString(),
206 
207         params.isMtSendPlainText()
208             ? MimeTypeMapper.PLAIN_TEXT_MIME_TYPE
209             : MimeTypeMapper.HTML_MIME_TYPE,
210 
211         ControllerUtil.getProjectId(),
212         params.getProjectLocation(),
213         params.getModelId(),
214         params.getGlossaryId());
215 
216     // translateBatchWithModel(
217     // AthTranslation.getClient(),
218     // sourceSegments,
219     // getSourceLocale().toString(),
220     // getTargetLocale().toString());
221 
222     postProcessTextUnits(targetSegments);
223   }
224 
225 }