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}