View Javadoc
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 }