1 | /* |
---|
2 | * ORXONOX - the hottest 3D action shooter ever to exist |
---|
3 | * > www.orxonox.net < |
---|
4 | * |
---|
5 | * |
---|
6 | * License notice: |
---|
7 | * |
---|
8 | * This program is free software; you can redistribute it and/or |
---|
9 | * modify it under the terms of the GNU General Public License |
---|
10 | * as published by the Free Software Foundation; either version 2 |
---|
11 | * of the License, or (at your option) any later version. |
---|
12 | * |
---|
13 | * This program is distributed in the hope that it will be useful, |
---|
14 | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
---|
15 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
---|
16 | * GNU General Public License for more details. |
---|
17 | * |
---|
18 | * You should have received a copy of the GNU General Public License |
---|
19 | * along with this program; if not, write to the Free Software |
---|
20 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. |
---|
21 | * |
---|
22 | * Author: |
---|
23 | * Fabian 'x3n' Landau |
---|
24 | * Co-authors: |
---|
25 | * ... |
---|
26 | * |
---|
27 | */ |
---|
28 | |
---|
29 | /** |
---|
30 | @file |
---|
31 | @brief Implementation of the Language and the LanguageEntry classes. |
---|
32 | */ |
---|
33 | |
---|
34 | #include "Language.h" |
---|
35 | |
---|
36 | #include <fstream> |
---|
37 | #include "util/Output.h" |
---|
38 | #include "util/StringUtils.h" |
---|
39 | #include "ConfigurablePaths.h" |
---|
40 | |
---|
41 | namespace orxonox |
---|
42 | { |
---|
43 | // ############################### |
---|
44 | // ### LanguageEntry ### |
---|
45 | // ############################### |
---|
46 | /** |
---|
47 | @brief Constructor: Sets the default entry. |
---|
48 | @param fallbackEntry The default entry |
---|
49 | */ |
---|
50 | LanguageEntry::LanguageEntry(const std::string& fallbackEntry) |
---|
51 | { |
---|
52 | this->fallbackEntry_ = fallbackEntry; |
---|
53 | this->localisedEntry_ = fallbackEntry; // Set the localisation to the fallback entry, for the case that no translation gets assigned |
---|
54 | this->bLocalisationSet_ = false; |
---|
55 | } |
---|
56 | |
---|
57 | /** |
---|
58 | @brief Sets the localisation of the entry. |
---|
59 | @param localisation The localisation |
---|
60 | */ |
---|
61 | void LanguageEntry::setLocalisation(const std::string& localisation) |
---|
62 | { |
---|
63 | // Check if the translation is more than just an empty string |
---|
64 | if (!localisation.empty()) |
---|
65 | { |
---|
66 | this->localisedEntry_ = localisation; |
---|
67 | this->bLocalisationSet_ = true; |
---|
68 | } |
---|
69 | else |
---|
70 | this->localisedEntry_ = this->fallbackEntry_; |
---|
71 | } |
---|
72 | |
---|
73 | /** |
---|
74 | @brief Sets the default entry. |
---|
75 | @param fallbackEntry The default entry |
---|
76 | */ |
---|
77 | void LanguageEntry::setDefault(const std::string& fallbackEntry) |
---|
78 | { |
---|
79 | // If the default entry changes and the translation wasn't set yet, use the new default entry as translation |
---|
80 | if (!this->bLocalisationSet_) |
---|
81 | this->localisedEntry_ = fallbackEntry; |
---|
82 | |
---|
83 | this->fallbackEntry_ = fallbackEntry; |
---|
84 | } |
---|
85 | |
---|
86 | // ############################### |
---|
87 | // ### Language ### |
---|
88 | // ############################### |
---|
89 | |
---|
90 | Language* Language::singletonPtr_s = nullptr; |
---|
91 | |
---|
92 | /** |
---|
93 | @brief Constructor: Reads the default language file and sets some values. |
---|
94 | */ |
---|
95 | Language::Language() |
---|
96 | { |
---|
97 | this->defaultLanguage_ = "default"; |
---|
98 | this->defaultLocalisation_ = "ERROR: LANGUAGE ENTRY DOESN'T EXIST!"; |
---|
99 | |
---|
100 | // Read the default language file to create all known LanguageEntry objects |
---|
101 | this->readDefaultLanguageFile(); |
---|
102 | } |
---|
103 | |
---|
104 | /** |
---|
105 | @brief Destructor: Deletes all language entries. |
---|
106 | */ |
---|
107 | Language::~Language() |
---|
108 | { |
---|
109 | for (std::map<std::string, LanguageEntry*>::iterator it = this->languageEntries_.begin(); it != this->languageEntries_.end(); ++it) |
---|
110 | delete (it->second); |
---|
111 | } |
---|
112 | |
---|
113 | /** |
---|
114 | @brief Creates a new LanguageEntry with a given label and a given default entry. |
---|
115 | @param label The label of the entry |
---|
116 | @param entry The default entry |
---|
117 | @return The created LanguageEntry object |
---|
118 | */ |
---|
119 | LanguageEntry* Language::createEntry(const LanguageEntryLabel& label, const std::string& entry) |
---|
120 | { |
---|
121 | std::map<std::string, LanguageEntry*>::const_iterator it = this->languageEntries_.find(label); |
---|
122 | |
---|
123 | // Make sure we don't create a duplicate entry |
---|
124 | if (it == this->languageEntries_.end()) |
---|
125 | { |
---|
126 | LanguageEntry* newEntry = new LanguageEntry(entry); |
---|
127 | newEntry->setLabel(label); |
---|
128 | this->languageEntries_[label] = newEntry; |
---|
129 | return newEntry; |
---|
130 | } |
---|
131 | |
---|
132 | orxout(internal_warning, context::language) << "Language entry " << label << " is duplicate in " << getFilename(this->defaultLanguage_) << '!' << endl; |
---|
133 | return it->second; |
---|
134 | } |
---|
135 | |
---|
136 | /** |
---|
137 | @brief Adds a new LanguageEntry, if it's not already existing. |
---|
138 | @param label The label of the entry |
---|
139 | @param entry The default entry |
---|
140 | */ |
---|
141 | void Language::addEntry(const LanguageEntryLabel& label, const std::string& entry) |
---|
142 | { |
---|
143 | orxout(verbose, context::language) << "Called addEntry with" << '\n' << "label: " << label << '\n' << "entry: " << entry << endl; |
---|
144 | std::map<std::string, LanguageEntry*>::const_iterator it = this->languageEntries_.find(label); |
---|
145 | if (it == this->languageEntries_.end()) |
---|
146 | { |
---|
147 | // The entry isn't available yet, meaning it's new, so create it |
---|
148 | this->createEntry(label, entry); |
---|
149 | } |
---|
150 | else if (it->second->getDefault().compare(entry) == 0) |
---|
151 | { |
---|
152 | // The entry is available and the default string is the same, so return because everything is fine |
---|
153 | return; |
---|
154 | } |
---|
155 | else |
---|
156 | { |
---|
157 | // The defined default entry is not the same as in the default language file - change it to the new entry |
---|
158 | it->second->setDefault(entry); |
---|
159 | } |
---|
160 | |
---|
161 | // Write the default language file because either a new entry was created or an existing entry has changed |
---|
162 | this->writeDefaultLanguageFile(); |
---|
163 | |
---|
164 | } |
---|
165 | |
---|
166 | /** |
---|
167 | @brief Returns the localisation of a given entry. |
---|
168 | @param label The label of the entry |
---|
169 | @param bError If true, an error is printed if the label doesn't exist and the default localisation is returned. If false, no error is printed and an empty string is returned. |
---|
170 | @return The localisation |
---|
171 | */ |
---|
172 | const std::string& Language::getLocalisation(const LanguageEntryLabel& label, bool bError) const |
---|
173 | { |
---|
174 | std::map<std::string, LanguageEntry*>::const_iterator it = this->languageEntries_.find(label); |
---|
175 | if (it != this->languageEntries_.end()) |
---|
176 | return it->second->getLocalisation(); |
---|
177 | else if (bError) |
---|
178 | { |
---|
179 | // Uh, oh, an undefined entry was requested: return the default string |
---|
180 | orxout(internal_warning, context::language) << "Language entry \"" << label << "\" not found!" << endl; |
---|
181 | return this->defaultLocalisation_; |
---|
182 | } |
---|
183 | else |
---|
184 | return BLANKSTRING; |
---|
185 | } |
---|
186 | |
---|
187 | /** |
---|
188 | @brief Creates the name of the language file out of the languages name. |
---|
189 | @param language The name of the language |
---|
190 | @return The filename |
---|
191 | */ |
---|
192 | std::string Language::getFilename(const std::string& language) |
---|
193 | { |
---|
194 | return std::string("translation_" + language + ".lang"); |
---|
195 | } |
---|
196 | |
---|
197 | /** |
---|
198 | @brief Reads the default language file and creates a LanguageEntry objects for every entry. |
---|
199 | */ |
---|
200 | void Language::readDefaultLanguageFile() |
---|
201 | { |
---|
202 | orxout(internal_info, context::language) << "Read default language file." << endl; |
---|
203 | |
---|
204 | const std::string& filepath = ConfigurablePaths::getConfigPathString() + getFilename(this->defaultLanguage_); |
---|
205 | |
---|
206 | // This creates the file if it's not existing |
---|
207 | std::ofstream createFile; |
---|
208 | createFile.open(filepath.c_str(), std::fstream::app); |
---|
209 | createFile.close(); |
---|
210 | |
---|
211 | // Open the file |
---|
212 | std::ifstream file; |
---|
213 | file.open(filepath.c_str(), std::fstream::in); |
---|
214 | |
---|
215 | if (!file.is_open()) |
---|
216 | { |
---|
217 | orxout(internal_error, context::language) << "An error occurred in Language.cc:" << endl; |
---|
218 | orxout(internal_error, context::language) << "Couldn't open file " << getFilename(this->defaultLanguage_) << " to read the default language entries!" << endl; |
---|
219 | return; |
---|
220 | } |
---|
221 | |
---|
222 | // Iterate through the file and create the LanguageEntries |
---|
223 | while (file.good() && !file.eof()) |
---|
224 | { |
---|
225 | std::string lineString; |
---|
226 | std::getline(file, lineString); |
---|
227 | |
---|
228 | // Check if the line is empty |
---|
229 | if (!lineString.empty()) |
---|
230 | { |
---|
231 | size_t pos = lineString.find('='); |
---|
232 | |
---|
233 | // Check if the length is at least 3 and if there's an entry before and behind the = |
---|
234 | if (pos > 0 && pos < (lineString.size() - 1) && lineString.size() >= 3) |
---|
235 | this->createEntry(lineString.substr(0, pos), lineString.substr(pos + 1)); |
---|
236 | else |
---|
237 | { |
---|
238 | orxout(internal_warning, context::language) << "Invalid language entry \"" << lineString << "\" in " << getFilename(this->defaultLanguage_) << endl; |
---|
239 | } |
---|
240 | } |
---|
241 | } |
---|
242 | |
---|
243 | file.close(); |
---|
244 | } |
---|
245 | |
---|
246 | /** |
---|
247 | @brief Reads the language file of the configured language and assigns the localisation to the corresponding LanguageEntry object. |
---|
248 | @return Returns false if the language file couldn't be found. |
---|
249 | */ |
---|
250 | bool Language::readTranslatedLanguageFile(const std::string& language) |
---|
251 | { |
---|
252 | orxout(internal_info, context::language) << "Read translated language file (" << language << ")." << endl; |
---|
253 | |
---|
254 | const std::string& filepath = ConfigurablePaths::getConfigPathString() + getFilename(language); |
---|
255 | |
---|
256 | // Open the file |
---|
257 | std::ifstream file; |
---|
258 | file.open(filepath.c_str(), std::fstream::in); |
---|
259 | |
---|
260 | if (!file.is_open()) |
---|
261 | { |
---|
262 | orxout(internal_error, context::language) << "An error occurred in Language.cc:" << endl; |
---|
263 | orxout(internal_error, context::language) << "Couldn't open file " << getFilename(language) << " to read the translated language entries!" << endl; |
---|
264 | return false; |
---|
265 | } |
---|
266 | |
---|
267 | // Iterate through the file and create the LanguageEntries |
---|
268 | while (file.good() && !file.eof()) |
---|
269 | { |
---|
270 | std::string lineString; |
---|
271 | std::getline(file, lineString); |
---|
272 | |
---|
273 | // Check if the line is empty |
---|
274 | if (!lineString.empty()) |
---|
275 | { |
---|
276 | size_t pos = lineString.find('='); |
---|
277 | |
---|
278 | // Check if the length is at least 3 and if there's an entry before and behind the = |
---|
279 | if (pos > 0 && pos < (lineString.size() - 1) && lineString.size() >= 3) |
---|
280 | { |
---|
281 | std::map<std::string, LanguageEntry*>::const_iterator it = this->languageEntries_.find(lineString.substr(0, pos)); |
---|
282 | |
---|
283 | // Check if the entry exists |
---|
284 | if (it != this->languageEntries_.end()) |
---|
285 | it->second->setLocalisation(lineString.substr(pos + 1)); |
---|
286 | else |
---|
287 | this->createEntry(lineString.substr(0, pos), this->defaultLocalisation_)->setLocalisation(lineString.substr(pos + 1)); |
---|
288 | } |
---|
289 | else |
---|
290 | { |
---|
291 | orxout(internal_warning, context::language) << "Invalid language entry \"" << lineString << "\" in " << getFilename(language) << endl; |
---|
292 | } |
---|
293 | } |
---|
294 | } |
---|
295 | |
---|
296 | file.close(); |
---|
297 | return true; |
---|
298 | } |
---|
299 | |
---|
300 | /** |
---|
301 | @brief Writes all default entries to the default language file. |
---|
302 | */ |
---|
303 | void Language::writeDefaultLanguageFile() const |
---|
304 | { |
---|
305 | orxout(verbose, context::language) << "Write default language file." << endl; |
---|
306 | |
---|
307 | const std::string& filepath = ConfigurablePaths::getConfigPathString() + getFilename(this->defaultLanguage_); |
---|
308 | |
---|
309 | // Open the file |
---|
310 | std::ofstream file; |
---|
311 | file.open(filepath.c_str(), std::fstream::out); |
---|
312 | |
---|
313 | if (!file.is_open()) |
---|
314 | { |
---|
315 | orxout(internal_error, context::language) << "An error occurred in Language.cc:" << endl; |
---|
316 | orxout(internal_error, context::language) << "Couldn't open file " << getFilename(this->defaultLanguage_) << " to write the default language entries!" << endl; |
---|
317 | return; |
---|
318 | } |
---|
319 | |
---|
320 | // Iterate through the list an write the lines into the file |
---|
321 | for (std::map<std::string, LanguageEntry*>::const_iterator it = this->languageEntries_.begin(); it != this->languageEntries_.end(); ++it) |
---|
322 | { |
---|
323 | file << it->second->getLabel() << '=' << it->second->getDefault() << endl; |
---|
324 | } |
---|
325 | |
---|
326 | file.close(); |
---|
327 | } |
---|
328 | } |
---|