Package hirondelle.web4j.ui.translate

Source Code of hirondelle.web4j.ui.translate.TextFlow

package hirondelle.web4j.ui.translate;

import java.text.CharacterIterator;
import java.text.StringCharacterIterator;
import java.util.Locale;
import java.util.regex.*;

import hirondelle.web4j.BuildImpl;
import hirondelle.web4j.request.LocaleSource;
import hirondelle.web4j.ui.tag.TagHelper;
import hirondelle.web4j.util.EscapeChars;
import hirondelle.web4j.util.Util;
import hirondelle.web4j.util.Regex;

/**
Custom tag for translating regular text flow in large sections of a web page.
<P><span class="highlight">This tag treats every piece of free flow text delimited by a 
tag as a unit of translatable base text</span>, and passes it to {@link Translator}.
That is, all tags are treated as <em>delimiters</em> of units of translatable text.
 
<P>This tag is suitable for translating most, but not all, of the
regular text flow in a web page. <span class="highlight">It is suitable for translating
markup that contains short, isolated snippets of text, that have no "structure", and
no dynamic data</span>, such as the labels in a form, the column headers in a listing,
and so on. (For many intranet applications, this
makes up most of the free flow text appearing in the application.) Instead of using many
separate <tt>&lt;w:txt&gt;</tt> {@link Text} tags to translate each item one by one,
a single <tt>&lt;w:txtFlow&gt;</tt> tag can often be used to do the same thing in a single step.
<P><span class="highlight">Using this class has two strong advantages</span> :
<ul>
<li>the effort needed to internationalize a page is greatly reduced
<li>the markup will be significantly easier to read and maintain, since most of the free flow text
remains unchanged from the single-language case
</ul>
<P>
This tag is <em>not suitable</em> when the base text to be translated :
<ul>
<li>contains markup
<li>has dynamic data of any sort
<li>contains a <tt>TEXTAREA</tt> with a <em>non-empty</em> body. Such text will be seen as a translatable
unit, which is usually undesired, since such text is usually not fixed, but dynamic (that is, from the database).
(To avoid this, simply nest this tag <em>inside</em> the <tt>&lt;w:populate&gt;</tt> tag surrounding the
form that contains the <tt>TEXTAREA</tt>. This ensures that the population is not affected by the action of this
tag.)
</ul>
 
<P>For example, given this text containing markup :
<PRE>The &lt;EM&gt;raison-d'etre&lt;/EM&gt; for this...</PRE>
then this tag will split the text into three separate pieces, delimited by the <tt>EM</tt> tags.
Then, each piece will be translated. For such items, this is almost always undesirable. Instead,
one must use a <tt>&lt;w:txt&gt;</tt> {@link Text} tag, which can treat such items as
a single unit of translatable text, without chopping it up into three pieces.
<P><b>Example</b><br>
Here, all of the <tt>LABEL</tt> tags in this form will have their content translated by the
<tt>&lt;w:txtFlow&gt;</tt> tag :
<PRE>
&lt;w:populate style='edit' using="myUser"&gt;
&lt;w:txtFlow&gt;
&lt;form action='blah.do' method='post' class="user-input"&gt;
&lt;table align="center"&gt;
&lt;tr&gt;
&lt;td&gt;
  &lt;label class="mandatory"&gt;Email&lt;/label&gt;
&lt;/td&gt;
&lt;td&gt;
  &lt;input type="text" name="Email Address" size='30'&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;
  &lt;label&gt;Age&lt;/label&gt;
&lt;/td&gt;
&lt;td&gt;
  &lt;input type="text" name="Age" size="30"&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;
  &lt;label&gt;Desired Salary&lt;/label&gt;
&lt;/td&gt;
&lt;td&gt;
  &lt;input type="text" name="Desired Salary" size="30"&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;
  &lt;label&gt; Birth Date &lt;/label&gt;
&lt;/td&gt;
&lt;td&gt;
  &lt;input type="text" name="Birth Date" size="30"&gt;
&lt;/td&gt;
&lt;/tr&gt;

&lt;tr&gt;
&lt;td&gt;
  &lt;input type='submit' value='UPDATE'&gt;
&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;/form&gt;
&lt;/w:txtFlow&gt;
&lt;/w:populate&gt;
</PRE>
*/
public final class TextFlow extends TagHelper {

  /**
   By default, this tag will escape any special characters appearing in the
   text flow, using {@link EscapeChars#forHTML(String)}. To change that default 
   behaviour, set this value to <tt>false</tt>.
  
   <P><span class="highlight">Exercise care that text is not doubly escaped.</span>
   For instance, if the text already contains
   character entities, and <tt>setEscapeChars</tt> is true, then the text <tt>&amp;amp;</tt>
   will be emitted by this tag as <tt>&amp;amp;amp;</tt>, for example.
  */
  public void setEscapeChars(boolean aValue){
    fEscapeChars = aValue;
  }
 
  /**
   Translate each piece of free flow text appearing in <tt>aOriginalBody</tt>.
  
   <P>Each piece of text is delimited by one or more tags, and is translated using the configured
   {@link Translator}. Leading or trailing white space is preserved.
  */
  @Override protected String getEmittedText(String aOriginalBody) {
    final StringBuffer result = new StringBuffer();
    final StringBuffer snippet = new StringBuffer();
    boolean isInsideTag = false;
   
    final StringCharacterIterator iterator = new StringCharacterIterator(aOriginalBody);
    char character =  iterator.current();
    while (character != CharacterIterator.DONE ){
      if (character == '<') {
        doStartTag(result, snippet, character);
        isInsideTag = true;
      }
      else if (character == '>') {
        doEndTag(result, character);
        isInsideTag = false;
      }
      else {
        doRegularCharacter(result, snippet, isInsideTag, character);
      }
      character = iterator.next();
    }
    if( Util.textHasContent(snippet.toString()) ) {
      appendTranslation(snippet, result);
    }
    return result.toString();
  }
 
  // PRIVATE //
  static Pattern TRIMMED_TEXT = Pattern.compile("((?:\\S(?:.)*\\S)|(?:\\S))");
 
  private boolean fEscapeChars = true;
  private LocaleSource fLocaleSource = BuildImpl.forLocaleSource();
  private Translator fTranslator = BuildImpl.forTranslator();
 
  private void doStartTag(StringBuffer aResult, StringBuffer aSnippet, char aCharacter) {
    if (Util.textHasContent(aSnippet.toString()) ){
      appendTranslation(aSnippet, aResult);
    }
    else {
      //often contains just spaces and/or new lines, which are just appended
      aResult.append(aSnippet.toString());
    }
    aSnippet.setLength(0);
    aResult.append(aCharacter);
  }
 
  private void doEndTag(StringBuffer aResult, char aCharacter) {
    aResult.append(aCharacter);
  }
 
  private void doRegularCharacter(StringBuffer aResult, StringBuffer aSnippet, boolean aIsInsideTag, char aCharacter) {
    if( aIsInsideTag ){
      aResult.append(aCharacter);
    }
    else {
      aSnippet.append(aCharacter);
    }
    //fLogger.fine("Snippet : " + aSnippet);
  }

  /**
   The snippet may contain leading or trailing white space, or control chars (new lines),
   which must be preserved.
  */
  private void appendTranslation(StringBuffer aSnippet, StringBuffer aResult){
    if( Util.textHasContent(aSnippet.toString()) ) {
      StringBuffer translatedSnippet = new StringBuffer();
     
      Matcher matcher = TRIMMED_TEXT.matcher(aSnippet.toString());
      while ( matcher.find() ) {
        matcher.appendReplacement(translatedSnippet, getReplacement(matcher));
      }
      matcher.appendTail(translatedSnippet);
     
      if( fEscapeChars ) {
        aResult.append(EscapeChars.forHTML(translatedSnippet.toString()));
      }
      else {
        aResult.append(translatedSnippet);
      }
    }
    else {
      aResult.append(aSnippet.toString());
    }
  }
 
  private String getReplacement(Matcher aMatcher){
    String result = null;
    String baseText = aMatcher.group(Regex.FIRST_GROUP);
    if (Util.textHasContent(baseText)){
      Locale locale = fLocaleSource.get(getRequest());
      result = fTranslator.get(baseText, locale);
    }
    else {
      result = baseText;
    }
    return EscapeChars.forReplacementString(result);
  }
}
TOP

Related Classes of hirondelle.web4j.ui.translate.TextFlow

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.