Search code examples
javajsfgettext

GNU gettext and JSF


We are using gettext for I18N in our Java programs.

Is there a way to use gettext for JSF xhtml files?

Currently we are using Java properties files in JSF:

<p:column headerText="#{msg['bargains.columns.percent']}">

Solution

  • You can write a named bean like this:

    import java.io.Serializable;
    import java.util.Locale;
    import javax.enterprise.context.SessionScoped;
    
    import javax.faces.context.FacesContext;
    import javax.inject.Named;
    
    import org.xnap.commons.i18n.I18nFactory;
    
    /**
    * Injectable version of {@link org.xnap.commons.i18n.I18n}.
    *
    * @author Falko Menge
    * @author nico.rehwaldt
    */
    @Named("i18n")
    @SessionScoped
    public class I18n implements Serializable {
    
      private static final long serialVersionUID = 1L;
      private transient org.xnap.commons.i18n.I18n i18n;
    
      public org.xnap.commons.i18n.I18n getI18n() {
    if (i18n == null) {
      FacesContext context = FacesContext.getCurrentInstance();
      String messageBundleName = context.getApplication().getMessageBundle();
      Locale locale = context.getViewRoot().getLocale();
      setI18n(I18nFactory.getI18n(getClass(), messageBundleName, locale, I18nFactory.FALLBACK));
    }
    return i18n;
      }
    
      public void setI18n(org.xnap.commons.i18n.I18n i18n) {
    this.i18n = i18n;
      }
    
      public String marktr(String text) {
    return org.xnap.commons.i18n.I18n.marktr(text);
      }
    
      public String tr(String text, Object o1, Object o2, Object o3, Object o4) {
    return getI18n().tr(text, o1, o2, o3, o4);
      }
    
      public String tr(String text, Object o1, Object o2, Object o3) {
    return getI18n().tr(text, o1, o2, o3);
      }
    
      public String tr(String text, Object o1, Object o2) {
    return getI18n().tr(text, o1, o2);
      }
    
      public String tr(String text, Object o1) {
    if (o1 instanceof Object[]) {
      return getI18n().tr(text, (Object[]) o1);
    }
    return getI18n().tr(text, o1);
      }
    
      public String tr(String arg0) {
    return getI18n().tr(arg0);
      }
    
      public String trc(String arg0, String arg1) {
    return getI18n().trc(arg0, arg1);
      }
    
      public String trn(String text, String pluralText, long n, Object o1, Object o2, Object o3, Object o4) {
    return getI18n().trn(text, pluralText, n, o1, o2, o3, o4);
      }
    
      public String trn(String text, String pluralText, long n, Object o1, Object o2, Object o3) {
    return getI18n().trn(text, pluralText, n, o1, o2, o3);
      }
    
      public String trn(String text, String pluralText, long n, Object o1, Object o2) {
    return getI18n().trn(text, pluralText, n, o1, o2);
      }
    
      public String trn(String text, String pluralText, long n, Object o1) {
    if (o1 instanceof Object[]) {
      return getI18n().trn(text, pluralText, n, (Object[]) o1);
    }
    return getI18n().trn(text, pluralText, n, o1);
      }
    
      public String trn(String arg0, String arg1, long arg2) {
    return getI18n().trn(arg0, arg1, arg2);
      }
    
      public String trnc(String comment, String singularText, String pluralText, long n, Object obj1, Object obj2, Object obj3, Object obj4) {
    return getI18n().trnc(comment, singularText, pluralText, n, obj1, obj2, obj3, obj4);
      }
    
      public String trnc(String comment, String singularText, String pluralText, long n, Object obj1, Object obj2, Object obj3) {
    return getI18n().trnc(comment, singularText, pluralText, n, obj1, obj2, obj3);
      }
    
      public String trnc(String comment, String singularText, String pluralText, long n, Object obj1, Object obj2) {
    return getI18n().trnc(comment, singularText, pluralText, n, obj1, obj2);
      }
    
      public String trnc(String comment, String singularText, String pluralText, long n, Object obj) {
    if (obj instanceof Object[]) {
      return getI18n().trnc(comment, singularText, pluralText, n, (Object[]) obj);
    }
    return getI18n().trnc(comment, singularText, pluralText, n, obj);
      }
    
      public String trnc(String arg0, String arg1, String arg2, long arg3) {
    return getI18n().trnc(arg0, arg1, arg2, arg3);
      }
    }
    

    use it in JSF like this:

    <h:outputLabel value="#{i18n.tr('Username')}" for="usernameInput" />
    

    and run it via gettext-commons:

    <dependency>
      <!-- required for i18n with GNU gettext -->
      <groupId>org.xnap.commons</groupId>
      <artifactId>gettext-commons</artifactId>
      <version>0.9.6</version>
    </dependency>
    

    A Maven plugin can be used together with a shellscript to extract the keys from the JSF pages:

     <plugin>
        <!-- required for i18n with GNU gettext -->
        <groupId>org.xnap.commons</groupId>
        <artifactId>maven-gettext-plugin</artifactId>
        <version>1.2.3</version>
        <configuration>
          <xgettextCmd>${basedir}/src/main/po/xgettext-jsf.sh</xgettextCmd>
          <poDirectory>${basedir}/src/main/po</poDirectory>
          <targetBundle>i18n.Messages</targetBundle>
          <outputDirectory>${basedir}/src/main/resources</outputDirectory>
        </configuration>
      </plugin>
    </plugins>
    

    shellscript:

    #!/bin/sh
    
    # extract gettext strings from Java sources with options provided by Maven
    xgettext --copyright-holder='ACME Inc.' --package-name='ACME Webapp' --package-version='1.0-SNAPSHOT' --msgid-bugs-address='[email protected]' $*
    
    # replace some placeholders
    sed -i '1 s/SOME DESCRIPTIVE TITLE/This is the LANGUAGE translation of ACME Webapp/' ../po/keys.pot
    sed -i '2 s/YEAR/2011/' ../po/keys.pot
    sed -i '3 s/PACKAGE/ACME Webapp/' ../po/keys.pot
    
    # fix Java source file locations
    sed -i 's/^#: /#: ..\/java\//' ../po/keys.pot
    
    # add a newline
    echo >> ../po/keys.pot
    
    # extract gettext strings from JSF templates
    grep "i18n\.[^(]\+('[^']\+'[^)]*)" --recursive --exclude-dir=.svn --include='*.xhtml' --only-matching --with-filename --line-number ../ | sed "s/\(.*\)\:i18n.tr('\(.*\)'.*/#: \1\nmsgid \"\2\"\nmsgstr \"\"\n/" >> ../po/keys.pot
    
    # unify duplicate translations in message catalog
    msguniq --sort-by-file --output-file=../po/keys.pot ../po/keys.pot