ODTDocumentTextTranslator.java
/*
* Copyright (C) 2000 - 2026 Silverpeas
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation, either version 3 of the
* License, or (at your option) any later version.
*
* As a special exception to the terms and conditions of version 3.0 of
* the GPL, you may redistribute this Program in connection with Free/Libre
* Open Source Software ("FLOSS") applications as described in Silverpeas's
* FLOSS exception. You should have received a copy of the text describing
* the FLOSS exception, and it is also available here:
* "http://www.silverpeas.org/docs/core/legal/floss_exception.html"
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package org.silverpeas.components.kmelia.export;
import java.util.Iterator;
import java.util.List;
import java.util.MissingResourceException;
import org.odftoolkit.odfdom.dom.element.table.TableTableElement;
import org.odftoolkit.simple.TextDocument;
import org.odftoolkit.simple.table.Column;
import org.odftoolkit.simple.table.Table;
import org.odftoolkit.simple.text.Footer;
import org.odftoolkit.simple.text.Header;
import org.odftoolkit.simple.text.Paragraph;
import org.odftoolkit.simple.text.Section;
import org.silverpeas.kernel.bundle.LocalizationBundle;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
/**
* It is a translator of text containing in some parts of an ODT documents.
* It is instanciated for a given localized resource bundle from which it will fetch the translated
* text.
*
* Currently it translates all the header, footer, paragraphs and tables in an ODT document.
*
* Instances of this class are mainly used to translate predefined text in templates in the ODT
* format. The text to translate is refered in the document as a key in the backed resource bundle.
* If the key exists, it is replaced in the ODT document by its associated translation.
*/
public class ODTDocumentTextTranslator {
private final LocalizationBundle bundle;
/**
* Creates a translator of ODT texts with the specified localized resource bundle.
* @param bundle the bundle to use for translating texts.
* @return an ODTDocumentTextTranslator instance.
*/
public static ODTDocumentTextTranslator aTranslatorWith(final LocalizationBundle bundle) {
return new ODTDocumentTextTranslator(bundle);
}
private ODTDocumentTextTranslator(final LocalizationBundle bundle) {
this.bundle = bundle;
}
/**
* Translates the specified ODT document.
*
* Each text to translate in the document should be actually a key in the resource bundle to the
* translated text to use; the key in the document is then replaced by the associated translated
* text. If the text is not a key in the resource bundle, then it isn't translated and it is kept
* as such in the document.
* @param document the document to translate.
*/
public void translate(final TextDocument document) {
translateHeader(document.getHeader());
translateFooter(document.getFooter());
translateParagraphs(document.getParagraphIterator());
translateTables(document.getTableList().iterator());
translateSections(document.getSectionIterator());
}
private void translateTables(final Iterator<Table> tables) {
while (tables.hasNext()) {
Table table = tables.next();
List<Column> columns = table.getColumnList();
for (Column column : columns) {
for (int i = 0; i < column.getCellCount(); i++) {
String translatedTextKey = column.getCellByIndex(i).getStringValue();
if (!translatedTextKey.isEmpty()) {
String translatedText = translateText(translatedTextKey);
column.getCellByIndex(i).setStringValue(translatedText);
}
}
}
}
}
/**
* Specific processes for the header and the footer of an ODT document.
* The header and the footer text can contain fields and it is important not erasing them by
* rewritting the text with translated words. So, the text of headers and footers are taken as
* an XML element and their childs are treated differently according to their type (text to
* translate is the content of the XML elements text:span).
* @param tables the tables that made a header or a footer.
*/
private void translateFooterHeaderTables(final Iterator<Table> tables) {
while (tables.hasNext()) {
Table table = tables.next();
TableTableElement elt = table.getOdfElement();
NodeList spanNodes = elt.getElementsByTagName("text:span");
for (int i = 0; i < spanNodes.getLength(); i++) {
Node text = spanNodes.item(i);
String content = text.getTextContent();
if (content !=null && !content.isEmpty()) {
text.setTextContent(translateText(text.getTextContent()));
}
}
}
}
private void translateParagraphs(final Iterator<Paragraph> paragraphs) {
while (paragraphs.hasNext()) {
Paragraph p = paragraphs.next();
String translatedTextKey = p.getTextContent();
String translatedText = translateText(translatedTextKey);
p.setTextContent(translatedText);
}
}
private void translateSections(final Iterator<Section> sections) {
while (sections.hasNext()) {
Section section = sections.next();
translateParagraphs(section.getParagraphIterator());
}
}
private void translateHeader(final Header header) {
List<Table> tables = header.getTableList();
translateFooterHeaderTables(tables.listIterator());
}
private void translateFooter(final Footer footer) {
List<Table> tables = footer.getTableList();
translateFooterHeaderTables(tables.listIterator());
}
private String translateText(String text) {
StringBuilder translatedText = new StringBuilder();
String[] words = text.split(" ");
for (String aWord : words) {
if (!aWord.isEmpty()) {
String translation;
try {
translation = getBundle().getString(aWord.trim());
} catch(MissingResourceException ex) {
translation = aWord;
}
translatedText.append(translation);
}
translatedText.append(" ");
}
return translatedText.toString();
}
private LocalizationBundle getBundle() {
return this.bundle;
}
}