1+ package io .github .mtbarr .translatica ;
2+
3+ import org .jetbrains .annotations .NotNull ;
4+
5+ import java .nio .charset .StandardCharsets ;
6+ import java .text .MessageFormat ;
7+ import java .util .*;
8+ import java .util .concurrent .ConcurrentHashMap ;
9+ import java .util .concurrent .CopyOnWriteArrayList ;
10+
11+ /**
12+ * A registry of keyed messages with the source {@link Locale}.
13+ *
14+ * @author Matheus Barreto
15+ */
16+ public final class MessageRegistry {
17+
18+
19+ private static MessageRegistry instance ;
20+
21+ private final Map <Locale , List <ResourceBundle >> bundles = new ConcurrentHashMap <>();
22+
23+ public MessageRegistry (@ NotNull String propertiesFileName ) {
24+ this .registerMessages (PropertyResourceBundle .getBundle (propertiesFileName ));
25+ }
26+
27+ /**
28+ * Default constructor using 'messages' file
29+ */
30+ public MessageRegistry () {
31+ this ("messages" );
32+ }
33+
34+
35+ /**
36+ * Register all messages present in the provided resource bundle.
37+ *
38+ * @param resourceBundle ResourceBundle containing all messages.
39+ **/
40+ public void registerMessages (@ NotNull ResourceBundle resourceBundle ) {
41+ List <ResourceBundle > bundleList = bundles .computeIfAbsent (resourceBundle .getLocale (), key -> new CopyOnWriteArrayList <>());
42+ bundleList .add (resourceBundle );
43+ }
44+
45+ /**
46+ * Register all messages from the provided ResourceBundle that are registered.
47+ *
48+ * @param resourceBundle provided resouce bundle.
49+ */
50+ public void unregisterMessages (@ NotNull ResourceBundle resourceBundle ) {
51+ List <ResourceBundle > bundleList = bundles .get (resourceBundle .getLocale ());
52+ if (bundleList != null ) {
53+ bundleList .removeIf (bundle -> bundle .getLocale ().equals (resourceBundle .getLocale ()));
54+ }
55+ }
56+
57+ /**
58+ * Gets a keyed message according to the provided Locale.
59+ *
60+ * @param locale message locale.
61+ * @param key message key.
62+ * @return a translated message valued based on {@param locale} provided.
63+ * @throws IllegalArgumentException if the message value is not found.
64+ */
65+ public String get (Locale locale , String key ) {
66+ List <ResourceBundle > bundleList = bundles .get (locale );
67+ if (bundleList == null ) {
68+ throw new IllegalArgumentException ("Cannot found resource bundle for this locale." );
69+ }
70+
71+ for (ResourceBundle bundle : bundleList ) {
72+ return this .findBundleMessage (key , bundle );
73+ }
74+
75+ throw new NullPointerException ("Cannot found message keyed with " + key + " key on locale " + locale .getDisplayName ());
76+ }
77+
78+
79+ /**
80+ * Gets a keyed message according to the default Locale.
81+ *
82+ * @param key message key.
83+ * @return a translated message valued based on default locale.
84+ * @throws IllegalArgumentException if the message value is not found.
85+ */
86+ @ NotNull
87+ public String get (String key ) {
88+ return get (Locale .getDefault (), key );
89+ }
90+
91+ /**
92+ * Equivalent to {@link MessageRegistry#get(Locale, String)}
93+ * but with replacements being provided.
94+ *
95+ * @param locale message locale
96+ * @param key message key
97+ * @param replacements message replacements
98+ * @return a final replaced version of initial keyed message.
99+ * @throws IllegalArgumentException if the message value is not found.
100+ */
101+ @ NotNull
102+ public String get (Locale locale , String key , Object ... replacements ) {
103+ return String .format (get (locale , key ), replacements );
104+ }
105+
106+
107+ /**
108+ * Equivalent to {@link MessageRegistry#get(String)}
109+ * but with replacements being provided.
110+ *
111+ * @param key message key
112+ * @param replacements message replacements
113+ * @return a final replaced version of initial keyed message.
114+ * @throws IllegalArgumentException if the message value is not found.
115+ */
116+ @ NotNull
117+ public String get (String key , Object ... replacements ) {
118+ return get (Locale .getDefault (), key , replacements );
119+ }
120+
121+ /**
122+ * Gets the main instance of the message registry.
123+ */
124+ public static MessageRegistry getInstance () {
125+ if (instance == null ) {
126+ instance = new MessageRegistry ();
127+ }
128+
129+ return instance ;
130+ }
131+
132+ /**
133+ * Gets a keyed message according to the provided Locale.
134+ *
135+ * @param locale message locale.
136+ * @param key message key.
137+ * @return a translated message valued based on {@param locale} provided.
138+ * @throws IllegalArgumentException if the message value is not found.
139+ */
140+ @ NotNull
141+ public static String getMessage (@ NotNull Locale locale , @ NotNull String key ) {
142+ return getInstance ().get (locale , key );
143+ }
144+
145+ /**
146+ * Equivalent to {@link MessageRegistry#get(Locale, String)}
147+ * but with replacements being provided.
148+ *
149+ * @param locale message locale
150+ * @param key message key
151+ * @param replacements message replacements
152+ * @return a final replaced version of initial keyed message.
153+ * @throws IllegalArgumentException if the message value is not found.
154+ */
155+ @ NotNull
156+ public static String getMessage (@ NotNull Locale locale , @ NotNull String key , @ NotNull Object ... replacements ) {
157+ return getInstance ().get (locale , key , replacements );
158+ }
159+
160+ /**
161+ * Gets a keyed message according to the default Locale.
162+ *
163+ * @param key message key.
164+ * @return a translated message valued based on default locale.
165+ * @throws IllegalArgumentException if the message value is not found.
166+ */
167+ @ NotNull
168+ public static String getMessage (@ NotNull String key ) {
169+ return getInstance ().get (key );
170+ }
171+
172+ /**
173+ * Equivalent to {@link MessageRegistry#get(String)}
174+ * but with replacements being provided.
175+ *
176+ * @param key message key
177+ * @param replacements message replacements
178+ * @return a final replaced version of initial keyed message.
179+ * @throws IllegalArgumentException if the message value is not found.
180+ */
181+ @ NotNull
182+ public static String getMessage (@ NotNull String key , @ NotNull Object ... replacements ) {
183+ return getInstance ().get (key , replacements );
184+ }
185+
186+ /**
187+ * Equivalent to {@link MessageRegistry#get(String, Object...)}
188+ * but using {@link MessageFormat#format(String, Object...)}
189+ *
190+ * @param key message key
191+ * @param replacements message replacements
192+ * @return a final replaced version of initial keyed message.
193+ * @throws IllegalArgumentException if the message value is not found.
194+ */
195+ @ NotNull
196+ public static String getFormattedMessage (@ NotNull String key , Object ... replacements ) {
197+ return MessageFormat .format (getMessage (key ), replacements );
198+ }
199+
200+ /**
201+ * Get a section of the message registry.
202+ * <p>
203+ * Example:
204+ * foo.bar.baz
205+ * <p></>
206+ * Where foo is the section, bar is the subsection and baz is the key.
207+ *
208+ * @param sectionName the section name.
209+ * @return a section of the message registry.
210+ */
211+ public static MessageSection getSection (String sectionName ) {
212+ return new MessageSection (sectionName );
213+ }
214+
215+ private static String encodeToUtf8 (String value ) {
216+ byte [] bytes = value .getBytes ();
217+ return new String (bytes , StandardCharsets .UTF_8 );
218+ }
219+
220+ private String findBundleMessage (String key , ResourceBundle bundle ) {
221+ try {
222+ return bundle .getString (key );
223+ } catch (Exception e ) {
224+ throw new NullPointerException ("Cannot found message keyed with " + key + " key on bundle " + bundle .getBaseBundleName ());
225+ }
226+ }
227+ }
0 commit comments