1 /*
2   ==============================================================================
3 
4    This file is part of the JUCE library.
5    Copyright (c) 2020 - Raw Material Software Limited
6 
7    JUCE is an open source library subject to commercial or open-source
8    licensing.
9 
10    The code included in this file is provided under the terms of the ISC license
11    http://www.isc.org/downloads/software-support-policy/isc-license. Permission
12    To use, copy, modify, and/or distribute this software for any purpose with or
13    without fee is hereby granted provided that the above copyright notice and
14    this permission notice appear in all copies.
15 
16    JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17    EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
18    DISCLAIMED.
19 
20   ==============================================================================
21 */
22 
23 namespace juce
24 {
25 
26 //==============================================================================
27 /**
28     Used to convert strings to localised foreign-language versions.
29 
30     This is basically a look-up table of strings and their translated equivalents.
31     It can be loaded from a text file, so that you can supply a set of localised
32     versions of strings that you use in your app.
33 
34     To use it in your code, simply call the translate() method on each string that
35     might have foreign versions, and if none is found, the method will just return
36     the original string.
37 
38     The translation file should start with some lines specifying a description of
39     the language it contains, and also a list of ISO country codes where it might
40     be appropriate to use the file. After that, each line of the file should contain
41     a pair of quoted strings with an '=' sign.
42 
43     E.g. for a french translation, the file might be:
44 
45     @code
46     language: French
47     countries: fr be mc ch lu
48 
49     "hello" = "bonjour"
50     "goodbye" = "au revoir"
51     @endcode
52 
53     If the strings need to contain a quote character, they can use '\"' instead, and
54     if the first non-whitespace character on a line isn't a quote, then it's ignored,
55     (you can use this to add comments).
56 
57     Note that this is a singleton class, so don't create or destroy the object directly.
58     There's also a TRANS(text) macro defined to make it easy to use the this.
59 
60     E.g. @code
61     printSomething (TRANS("hello"));
62     @endcode
63 
64     This macro is used in the JUCE classes themselves, so your application has a chance to
65     intercept and translate any internal JUCE text strings that might be shown. (You can easily
66     get a list of all the messages by searching for the TRANS() macro in the JUCE source
67     code).
68 
69     @tags{Core}
70 */
71 class JUCE_API  LocalisedStrings
72 {
73 public:
74     //==============================================================================
75     /** Creates a set of translations from the text of a translation file.
76 
77         When you create one of these, you can call setCurrentMappings() to make it
78         the set of mappings that the system's using.
79     */
80     LocalisedStrings (const String& fileContents, bool ignoreCaseOfKeys);
81 
82     /** Creates a set of translations from a file.
83 
84         When you create one of these, you can call setCurrentMappings() to make it
85         the set of mappings that the system's using.
86     */
87     LocalisedStrings (const File& fileToLoad, bool ignoreCaseOfKeys);
88 
89     LocalisedStrings (const LocalisedStrings&);
90     LocalisedStrings& operator= (const LocalisedStrings&);
91 
92     /** Destructor. */
93     ~LocalisedStrings();
94 
95     //==============================================================================
96     /** Selects the current set of mappings to be used by the system.
97 
98         The object you pass in will be automatically deleted when no longer needed, so
99         don't keep a pointer to it. You can also pass in nullptr to remove the current
100         mappings.
101 
102         See also the TRANS() macro, which uses the current set to do its translation.
103 
104         @see translateWithCurrentMappings
105     */
106     static void setCurrentMappings (LocalisedStrings* newTranslations);
107 
108     /** Returns the currently selected set of mappings.
109 
110         This is the object that was last passed to setCurrentMappings(). It may
111         be nullptr if none has been created.
112     */
113     static LocalisedStrings* getCurrentMappings();
114 
115     /** Tries to translate a string using the currently selected set of mappings.
116 
117         If no mapping has been set, or if the mapping doesn't contain a translation
118         for the string, this will just return the original string.
119 
120         See also the TRANS() macro, which uses this method to do its translation.
121 
122         @see setCurrentMappings, getCurrentMappings
123     */
124     static String translateWithCurrentMappings (const String& text);
125 
126     /** Tries to translate a string using the currently selected set of mappings.
127 
128         If no mapping has been set, or if the mapping doesn't contain a translation
129         for the string, this will just return the original string.
130 
131         See also the TRANS() macro, which uses this method to do its translation.
132 
133         @see setCurrentMappings, getCurrentMappings
134     */
135     static String translateWithCurrentMappings (const char* text);
136 
137     //==============================================================================
138     /** Attempts to look up a string and return its localised version.
139         If the string isn't found in the list, the original string will be returned.
140     */
141     String translate (const String& text) const;
142 
143     /** Attempts to look up a string and return its localised version.
144         If the string isn't found in the list, the resultIfNotFound string will be returned.
145     */
146     String translate (const String& text, const String& resultIfNotFound) const;
147 
148     /** Returns the name of the language specified in the translation file.
149 
150         This is specified in the file using a line starting with "language:", e.g.
151         @code
152         language: german
153         @endcode
154     */
getLanguageName()155     String getLanguageName() const                        { return languageName; }
156 
157     /** Returns the list of suitable country codes listed in the translation file.
158 
159         These is specified in the file using a line starting with "countries:", e.g.
160         @code
161         countries: fr be mc ch lu
162         @endcode
163 
164         The country codes are supposed to be 2-character ISO compliant codes.
165     */
getCountryCodes()166     const StringArray& getCountryCodes() const            { return countryCodes; }
167 
168     /** Provides access to the actual list of mappings. */
getMappings()169     const StringPairArray& getMappings() const            { return translations; }
170 
171     //==============================================================================
172     /** Adds and merges another set of translations into this set.
173 
174         Note that the language name and country codes of the new LocalisedStrings
175         object must match that of this object - an assertion will be thrown if they
176         don't match.
177 
178         Any existing values will have their mappings overwritten by the new ones.
179     */
180     void addStrings (const LocalisedStrings&);
181 
182     /** Gives this object a set of strings to use as a fallback if a string isn't found.
183         The object that is passed-in will be owned and deleted by this object
184         when no longer needed. It can be nullptr to clear the existing fallback object.
185     */
186     void setFallback (LocalisedStrings* fallbackStrings);
187 
188 private:
189     //==============================================================================
190     String languageName;
191     StringArray countryCodes;
192     StringPairArray translations;
193     std::unique_ptr<LocalisedStrings> fallback;
194 
195     void loadFromText (const String&, bool ignoreCase);
196 
197     JUCE_LEAK_DETECTOR (LocalisedStrings)
198 };
199 
200 //==============================================================================
201 #ifndef TRANS
202  /** Uses the LocalisedStrings class to translate the given string literal.
203      This macro is provided for backwards-compatibility, and just calls the translate()
204      function. In new code, it's recommended that you just call translate() directly
205      instead, and avoid using macros.
206      @see translate(), LocalisedStrings
207  */
208  #define TRANS(stringLiteral) juce::translate (stringLiteral)
209 #endif
210 
211 /** A dummy version of the TRANS macro, used to indicate a string literal that should be
212     added to the translation file by source-code scanner tools.
213 
214     Wrapping a string literal in this macro has no effect, but by using it around strings
215     that your app needs to translate at a later stage, it lets automatic code-scanning tools
216     find this string and add it to the list of strings that need translation.
217 */
218 #define NEEDS_TRANS(stringLiteral) (stringLiteral)
219 
220 /** Uses the LocalisedStrings class to translate the given string literal.
221     @see LocalisedStrings
222 */
223 JUCE_API String translate (const String& stringLiteral);
224 
225 /** Uses the LocalisedStrings class to translate the given string literal.
226     @see LocalisedStrings
227 */
228 JUCE_API String translate (const char* stringLiteral);
229 
230 /** Uses the LocalisedStrings class to translate the given string literal.
231     @see LocalisedStrings
232 */
233 JUCE_API String translate (CharPointer_UTF8 stringLiteral);
234 
235 /** Uses the LocalisedStrings class to translate the given string literal.
236     @see LocalisedStrings
237 */
238 JUCE_API String translate (const String& stringLiteral, const String& resultIfNotFound);
239 
240 } // namespace juce
241