001package org.hl7.fhir.utilities.i18n;
002
003import com.ibm.icu.text.PluralRules;
004
005import java.text.MessageFormat;
006import java.util.Locale;
007import java.util.Objects;
008import java.util.ResourceBundle;
009
010
011/**
012 * Handles the locale, ResourceBundle and String formatting for i18n
013 * This abstract class should be extended when implementing a IWorkerContext Interface.
014 */
015public abstract class I18nBase {
016
017  public static final String PLURAL_SUFFIX = "PLURAL";
018  public static final String KEY_DELIMITER = "_";
019  private Locale locale;
020  private ResourceBundle i18nMessages;
021  private PluralRules pluralRules;
022  private boolean warnAboutMissingMessages = true;
023
024  public Locale getLocale() {
025    if (Objects.nonNull(locale)) {
026      return locale;
027    } else {
028      return Locale.US;
029    }
030  }
031
032  public void setLocale(Locale locale) {
033    this.locale = locale;
034    setValidationMessageLanguage(getLocale());
035  }
036
037  /**
038   * Verifies if a {@link ResourceBundle} has been loaded for the current {@link Locale}. If not, it triggers a load.
039   */
040  private void checkResourceBundleIsLoaded() {
041    if (i18nMessages == null) {
042      setValidationMessageLanguage(getLocale());
043    }
044  }
045
046  private void checkPluralRulesAreLoaded() {
047    if (pluralRules == null) {
048      setPluralRules(getLocale());
049    }
050  }
051
052  /**
053   * Checks the loaded {@link ResourceBundle} to see if the passed in message exists with the current loaded {@link Locale}.
054   * If no {@link Locale} is currently loaded, it will load the {@link Locale} (default {@link Locale#US} is none is
055   * specified), and search.
056   * @param message The {@link String} message to search for within the current {@link Locale}
057   * @return {@link Boolean#TRUE} if the message exists within the loaded {@link Locale}.
058   */
059  private boolean messageExistsForLocale(String message, boolean hasArgs) {
060    checkResourceBundleIsLoaded();
061    if (!i18nMessages.containsKey(message)) {
062      if (warnAboutMissingMessages && (hasArgs || !message.contains(" "))) {
063        System.out.println("Attempting to localize message " + message + ", but no such equivalent message exists for" +
064            " the local " + getLocale());
065      }
066    }
067    return i18nMessages.containsKey(message);
068  }
069
070  /**
071   * Formats the given message, if needed, with the passed in message arguments.
072   * @param theMessage Base message to format.
073   * @param theMessageArguments Placeholder arguments, if needed.
074   * @return The formatted, internationalized, {@link String}
075   */
076  public String formatMessage(String theMessage, Object... theMessageArguments) {
077    return formatMessageForLocale(theMessage, theMessageArguments);
078  }
079
080  protected String getPluralKey(Integer number, String baseKey) {
081    return baseKey + KEY_DELIMITER + pluralRules.select(number);
082  }
083
084  private String formatMessageForLocale(String theMessage, Object... theMessageArguments) {
085    String message = theMessage;
086    if (messageExistsForLocale(theMessage, (theMessageArguments != null && theMessageArguments.length > 0))) {
087      if (Objects.nonNull(theMessageArguments) && theMessageArguments.length > 0) {
088        message = MessageFormat.format(i18nMessages.getString(theMessage), theMessageArguments);
089      } else {
090        message = i18nMessages.getString(theMessage);
091      }
092    }
093    return message;
094  }
095
096  /**
097   * Formats the message with locale correct pluralization using the passed in
098   * message arguments.
099   *
100   * In the message properties files, each plural specific message will have a
101   * key consisting of a root key and a suffix denoting the plurality rule (_one
102   * for singular, _other for multiple in English, for example). Suffixes are
103   * provided by th ICU4J library from unicode.org
104   *
105   * @param plural The number that indicates the plurality of the phrase
106   * @param theMessage the root key of the phrase.
107   * @param theMessageArguments Placeholder arguments, if needed.
108   * @return The formatted, internationalized, {@link String}
109   */
110  public String formatMessagePlural(Integer plural, String theMessage, Object... theMessageArguments) {
111
112    Object[] args = new Object[theMessageArguments.length+1];
113    args[0] = plural;
114    for (int i = 0; i < theMessageArguments.length; i++) {
115      args[i+1] = theMessageArguments[i];
116    }
117    checkPluralRulesAreLoaded();
118    String pluralKey = getPluralKey(plural, theMessage);
119    return formatMessageForLocale(pluralKey, args);
120  }
121
122  /**
123   * Loads the corresponding {@link ResourceBundle} for the passed in {@link Locale}.
124   * @param locale {@link Locale} to load resources for.
125   */
126  public void setValidationMessageLanguage(Locale locale) {
127    i18nMessages = ResourceBundle.getBundle("Messages", locale);
128  }
129
130  public void setPluralRules(Locale locale) {
131    pluralRules = PluralRules.forLocale(locale);
132  }
133
134  public boolean isWarnAboutMissingMessages() {
135    return warnAboutMissingMessages;
136  }
137
138  public void setWarnAboutMissingMessages(boolean warnAboutMissingMessages) {
139    this.warnAboutMissingMessages = warnAboutMissingMessages;
140  }
141  
142  
143}