2 ==============================================================================
4 This file is part of the JUCE library.
5 Copyright (c) 2022 - Raw Material Software Limited
7 JUCE is an open source library subject to commercial or open-source
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.
16 JUCE IS PROVIDED "AS IS" WITHOUT ANY WARRANTY, AND ALL WARRANTIES, WHETHER
17 EXPRESSED OR IMPLIED, INCLUDING MERCHANTABILITY AND FITNESS FOR PURPOSE, ARE
20 ==============================================================================
26 LocalisedStrings::LocalisedStrings (const String
& fileContents
, bool ignoreCase
)
28 loadFromText (fileContents
, ignoreCase
);
31 LocalisedStrings::LocalisedStrings (const File
& fileToLoad
, bool ignoreCase
)
33 loadFromText (fileToLoad
.loadFileAsString(), ignoreCase
);
36 LocalisedStrings::LocalisedStrings (const LocalisedStrings
& other
)
37 : languageName (other
.languageName
), countryCodes (other
.countryCodes
),
38 translations (other
.translations
), fallback (createCopyIfNotNull (other
.fallback
.get()))
42 LocalisedStrings
& LocalisedStrings::operator= (const LocalisedStrings
& other
)
44 languageName
= other
.languageName
;
45 countryCodes
= other
.countryCodes
;
46 translations
= other
.translations
;
47 fallback
.reset (createCopyIfNotNull (other
.fallback
.get()));
51 //==============================================================================
52 String
LocalisedStrings::translate (const String
& text
) const
54 if (fallback
!= nullptr && ! translations
.containsKey (text
))
55 return fallback
->translate (text
);
57 return translations
.getValue (text
, text
);
60 String
LocalisedStrings::translate (const String
& text
, const String
& resultIfNotFound
) const
62 if (fallback
!= nullptr && ! translations
.containsKey (text
))
63 return fallback
->translate (text
, resultIfNotFound
);
65 return translations
.getValue (text
, resultIfNotFound
);
70 #if JUCE_CHECK_MEMORY_LEAKS
71 // By using this object to force a LocalisedStrings object to be created
72 // before the currentMappings object, we can force the static order-of-destruction to
73 // delete the currentMappings object first, which avoids a bogus leak warning.
74 // (Oddly, just creating a LocalisedStrings on the stack doesn't work in gcc, it
75 // has to be created with 'new' for this to work..)
76 struct LeakAvoidanceTrick
80 const std::unique_ptr
<LocalisedStrings
> dummy (new LocalisedStrings (String(), false));
84 LeakAvoidanceTrick leakAvoidanceTrick
;
87 SpinLock currentMappingsLock
;
88 std::unique_ptr
<LocalisedStrings
> currentMappings
;
90 static int findCloseQuote (const String
& text
, int startPos
)
92 juce_wchar lastChar
= 0;
93 auto t
= text
.getCharPointer() + startPos
;
97 auto c
= t
.getAndAdvance();
99 if (c
== 0 || (c
== '"' && lastChar
!= '\\'))
109 static String
unescapeString (const String
& s
)
111 return s
.replace ("\\\"", "\"")
112 .replace ("\\\'", "\'")
113 .replace ("\\t", "\t")
114 .replace ("\\r", "\r")
115 .replace ("\\n", "\n");
119 void LocalisedStrings::loadFromText (const String
& fileContents
, bool ignoreCase
)
121 translations
.setIgnoresCase (ignoreCase
);
124 lines
.addLines (fileContents
);
126 for (auto& l
: lines
)
128 auto line
= l
.trim();
130 if (line
.startsWithChar ('"'))
132 auto closeQuote
= findCloseQuote (line
, 1);
133 auto originalText
= unescapeString (line
.substring (1, closeQuote
));
135 if (originalText
.isNotEmpty())
137 auto openingQuote
= findCloseQuote (line
, closeQuote
+ 1);
138 closeQuote
= findCloseQuote (line
, openingQuote
+ 1);
139 auto newText
= unescapeString (line
.substring (openingQuote
+ 1, closeQuote
));
141 if (newText
.isNotEmpty())
142 translations
.set (originalText
, newText
);
145 else if (line
.startsWithIgnoreCase ("language:"))
147 languageName
= line
.substring (9).trim();
149 else if (line
.startsWithIgnoreCase ("countries:"))
151 countryCodes
.addTokens (line
.substring (10).trim(), true);
153 countryCodes
.removeEmptyStrings();
157 translations
.minimiseStorageOverheads();
160 void LocalisedStrings::addStrings (const LocalisedStrings
& other
)
162 jassert (languageName
== other
.languageName
);
163 jassert (countryCodes
== other
.countryCodes
);
165 translations
.addArray (other
.translations
);
168 void LocalisedStrings::setFallback (LocalisedStrings
* f
)
173 //==============================================================================
174 void LocalisedStrings::setCurrentMappings (LocalisedStrings
* newTranslations
)
176 const SpinLock::ScopedLockType
sl (currentMappingsLock
);
177 currentMappings
.reset (newTranslations
);
180 LocalisedStrings
* LocalisedStrings::getCurrentMappings()
182 return currentMappings
.get();
185 String
LocalisedStrings::translateWithCurrentMappings (const String
& text
) { return juce::translate (text
); }
186 String
LocalisedStrings::translateWithCurrentMappings (const char* text
) { return juce::translate (text
); }
188 JUCE_API String
translate (const String
& text
) { return juce::translate (text
, text
); }
189 JUCE_API String
translate (const char* text
) { return juce::translate (String (text
)); }
190 JUCE_API String
translate (CharPointer_UTF8 text
) { return juce::translate (String (text
)); }
192 JUCE_API String
translate (const String
& text
, const String
& resultIfNotFound
)
194 const SpinLock::ScopedLockType
sl (currentMappingsLock
);
196 if (auto* mappings
= LocalisedStrings::getCurrentMappings())
197 return mappings
->translate (text
, resultIfNotFound
);
199 return resultIfNotFound
;