1 ///////////////////////////////////////////////////////////////////////////////////////////////
2 // checkstyle: Checks Java source code and other text files for adherence to a set of rules.
3 // Copyright (C) 2001-2026 the original author or authors.
4 //
5 // This library is free software; you can redistribute it and/or
6 // modify it under the terms of the GNU Lesser General Public
7 // License as published by the Free Software Foundation; either
8 // version 2.1 of the License, or (at your option) any later version.
9 //
10 // This library is distributed in the hope that it will be useful,
11 // but WITHOUT ANY WARRANTY; without even the implied warranty of
12 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
13 // Lesser General Public License for more details.
14 //
15 // You should have received a copy of the GNU Lesser General Public
16 // License along with this library; if not, write to the Free Software
17 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
18 ///////////////////////////////////////////////////////////////////////////////////////////////
19
20 package com.puppycrawl.tools.checkstyle;
21
22 import java.text.MessageFormat;
23 import java.util.Locale;
24 import java.util.MissingResourceException;
25 import java.util.ResourceBundle;
26
27 import com.puppycrawl.tools.checkstyle.utils.UnmodifiableCollectionUtil;
28
29 /**
30 * Represents a message that can be localised. The translations come from
31 * message.properties files. The underlying implementation uses
32 * java.text.MessageFormat.
33 */
34 public class LocalizedMessage {
35
36 /** The locale to localise messages to. */
37 private static Locale messageLocale = Locale.getDefault();
38
39 /** Name of the resource bundle to get messages from. */
40 private final String bundle;
41
42 /** Class of the source for this message. */
43 private final Class<?> sourceClass;
44
45 /**
46 * Key for the message format.
47 */
48 private final String key;
49
50 /**
51 * Arguments for java.text.MessageFormat, that is why type is Object[].
52 *
53 * <p>Note: Changing types from Object[] will be huge breaking compatibility, as Module
54 * messages use some type formatting already, so better to keep it as Object[].
55 * </p>
56 */
57 private final Object[] args;
58
59 /**
60 * Creates a new {@code LocalizedMessage} instance.
61 *
62 * @param bundle resource bundle name
63 * @param sourceClass the Class that is the source of the message
64 * @param key the key to locate the translation.
65 * @param args arguments for the translation.
66 */
67 public LocalizedMessage(String bundle, Class<?> sourceClass, String key,
68 Object... args) {
69 this.bundle = bundle;
70 this.sourceClass = sourceClass;
71 this.key = key;
72 if (args == null) {
73 this.args = null;
74 }
75 else {
76 this.args = UnmodifiableCollectionUtil.copyOfArray(args, args.length);
77 }
78 }
79
80 /**
81 * Sets a locale to use for localization.
82 *
83 * @param locale the locale to use for localization
84 */
85 public static void setLocale(Locale locale) {
86 if (Locale.ENGLISH.getLanguage().equals(locale.getLanguage())) {
87 messageLocale = Locale.ROOT;
88 }
89 else {
90 messageLocale = locale;
91 }
92 }
93
94 /**
95 * Gets the translated message.
96 *
97 * @return the translated message.
98 */
99 public String getMessage() {
100 String result;
101 try {
102 // Important to use the default class loader, and not the one in
103 // the GlobalProperties object. This is because the class loader in
104 // the GlobalProperties is specified by the user for resolving
105 // custom classes.
106 final ResourceBundle resourceBundle = getBundle();
107 final String pattern = resourceBundle.getString(key);
108 final MessageFormat formatter = new MessageFormat(pattern, Locale.ROOT);
109 result = formatter.format(args);
110 }
111 catch (final MissingResourceException ignored) {
112 // If the Check author didn't provide i18n resource bundles
113 // and logs audit event messages directly, this will return
114 // the author's original message
115 final MessageFormat formatter = new MessageFormat(key, Locale.ROOT);
116 result = formatter.format(args);
117 }
118 return result;
119 }
120
121 /**
122 * Obtain the ResourceBundle. Uses the classloader
123 * of the class emitting this message, to be sure to get the correct
124 * bundle. Property files are read as UTF-8 by the JDK since Java 9,
125 * so no custom {@code ResourceBundle.Control} is needed.
126 *
127 * @return a ResourceBundle.
128 */
129 private ResourceBundle getBundle() {
130 return ResourceBundle.getBundle(bundle, messageLocale, sourceClass.getClassLoader());
131 }
132
133 }