Pyrogenesis  trunk
L10n.h
Go to the documentation of this file.
1 /* Copyright (C) 2022 Wildfire Games.
2  *
3  * Permission is hereby granted, free of charge, to any person obtaining
4  * a copy of this software and associated documentation files (the
5  * "Software"), to deal in the Software without restriction, including
6  * without limitation the rights to use, copy, modify, merge, publish,
7  * distribute, sublicense, and/or sell copies of the Software, and to
8  * permit persons to whom the Software is furnished to do so, subject to
9  * the following conditions:
10  *
11  * The above copyright notice and this permission notice shall be included
12  * in all copies or substantial portions of the Software.
13  *
14  * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15  * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16  * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
17  * IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
18  * CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
19  * TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
20  * SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21  */
22 
23 #ifndef INCLUDED_L10N
24 #define INCLUDED_L10N
25 
26 #include "lib/code_annotation.h"
28 #include "lib/file/vfs/vfs_path.h"
29 #include "ps/Singleton.h"
30 
31 #include <memory>
32 #include <string>
33 #include <vector>
34 
35 namespace tinygettext
36 {
37 class Dictionary;
38 }
39 
40 #define g_L10n L10n::GetSingleton()
41 
42 /**
43  * %Singleton for internationalization and localization.
44  *
45  * @sa http://trac.wildfiregames.com/wiki/Internationalization_and_Localization
46  */
47 class L10n : public Singleton<L10n>
48 {
49 public:
50  /**
51  * Creates an instance of L10n.
52  *
53  * L10n is a singleton. Use Instance() instead of creating you own instances
54  * of L10n.
55  */
56  L10n();
57 
58  /**
59  * Handles the destruction of L10n.
60  *
61  * Never destroy the L10n singleton manually. It must run as long as the
62  * game runs, and it is destroyed automatically when you quit the game.
63  */
64  ~L10n();
65 
66  /**
67  * Types of dates.
68  *
69  * @sa LocalizeDateTime()
70  */
72  {
73  DateTime, ///< Both date and time.
74  Date, ///< Only date.
75  Time ///< Only time.
76  };
77 
78  /**
79  * Returns the current locale.
80  *
81  * @sa GetCurrentLocaleString()
82  * @sa GetSupportedLocaleBaseNames()
83  * @sa GetAllLocales()
84  * @sa ReevaluateCurrentLocaleAndReload()
85  */
86  const icu::Locale& GetCurrentLocale() const;
87 
88  /**
89  * Returns the code of the current locale.
90  *
91  * A locale code is a string such as "de" or "pt_BR".
92  *
93  * @sa GetCurrentLocale()
94  * @sa GetSupportedLocaleBaseNames()
95  * @sa GetAllLocales()
96  * @sa ReevaluateCurrentLocaleAndReload()
97  */
98  std::string GetCurrentLocaleString() const;
99 
100  /**
101  * Returns a vector of locale codes supported by ICU.
102  *
103  * A locale code is a string such as "de" or "pt_BR".
104  *
105  * @return Vector of supported locale codes.
106  *
107  * @sa GetSupportedLocaleBaseNames()
108  * @sa GetCurrentLocale()
109  *
110  * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#a073d70df8c9c8d119c0d42d70de24137
111  */
112  std::vector<std::string> GetAllLocales() const;
113 
114  /**
115  * Saves the specified locale in the game configuration file.
116  *
117  * The next time that the game starts, the game uses the locale in the
118  * configuration file if there are translation files available for it.
119  *
120  * SaveLocale() checks the validity of the specified locale with
121  * ValidateLocale(). If the specified locale is not valid, SaveLocale()
122  * returns @c false and does not save the locale to the configuration file.
123  *
124  * @param localeCode Locale to save to the configuration file.
125  * @return Whether the specified locale is valid (@c true) or not
126  * (@c false).
127  */
128  bool SaveLocale(const std::string& localeCode) const;
129  ///@overload SaveLocale
130  bool SaveLocale(const icu::Locale& locale) const;
131 
132  /**
133  * Returns an array of supported locale codes sorted alphabetically.
134  *
135  * A locale code is a string such as "de" or "pt_BR".
136  *
137  * If yours is a development copy (the 'config/dev.cfg' file is found in the
138  * virtual filesystem), the output array may include the special "long"
139  * locale code.
140  *
141  * @return Array of supported locale codes.
142  *
143  * @sa GetSupportedLocaleDisplayNames()
144  * @sa GetAllLocales()
145  * @sa GetCurrentLocale()
146  *
147  * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
148  */
149  std::vector<std::string> GetSupportedLocaleBaseNames() const;
150 
151  /**
152  * Returns an array of supported locale names sorted alphabetically by
153  * locale code.
154  *
155  * A locale code is a string such as "de" or "pt_BR".
156  *
157  * If yours is a development copy (the 'config/dev.cfg' file is found in the
158  * virtual filesystem), the output array may include the special "Long
159  * Strings" locale name.
160  *
161  * @return Array of supported locale codes.
162  *
163  * @sa GetSupportedLocaleBaseNames()
164  *
165  * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
166  */
167  std::vector<std::wstring> GetSupportedLocaleDisplayNames() const;
168 
169  /**
170  * Returns the ISO-639 language code of the specified locale code.
171  *
172  * For example, if you specify the 'en_US' locate, it returns 'en'.
173  *
174  * @param locale Locale code.
175  * @return Language code.
176  *
177  * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#af36d821adced72a870d921ebadd0ca93
178  */
179  std::string GetLocaleLanguage(const std::string& locale) const;
180 
181  /**
182  * Returns the programmatic code of the entire locale without keywords.
183  *
184  * @param locale Locale code.
185  * @return Locale code without keywords.
186  *
187  * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#a4c1acbbdf95dc15599db5f322fa4c4d0
188  */
189  std::string GetLocaleBaseName(const std::string& locale) const;
190 
191  /**
192  * Returns the ISO-3166 country code of the specified locale code.
193  *
194  * For example, if you specify the 'en_US' locate, it returns 'US'.
195  *
196  * @param locale Locale code.
197  * @return Country code.
198  *
199  * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#ae3f1fc415c00d4f0ab33288ceadccbf9
200  */
201  std::string GetLocaleCountry(const std::string& locale) const;
202 
203  /**
204  * Returns the ISO-15924 abbreviation script code of the specified locale code.
205  *
206  * @param locale Locale code.
207  * @return Script code.
208  *
209  * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#a5e0145a339d30794178a1412dcc55abe
210  */
211  std::string GetLocaleScript(const std::string& locale) const;
212 
213  /**
214  * Returns @c true if the current locale is the special "Long Strings"
215  * locale. It returns @c false otherwise.
216  *
217  * @return Whether the current locale is the special "Long Strings"
218  * (@c true) or not (@c false).
219  */
220  bool UseLongStrings() const;
221 
222  /**
223  * Returns an array of paths to files in the virtual filesystem that provide
224  * translations for the specified locale code.
225  *
226  * @param locale Locale code.
227  * @return Array of paths to files in the virtual filesystem that provide
228  * translations for @p locale.
229  */
230  std::vector<std::wstring> GetDictionariesForLocale(const std::string& locale) const;
231 
232  std::wstring GetFallbackToAvailableDictLocale(const icu::Locale& locale) const;
233 
234  std::wstring GetFallbackToAvailableDictLocale(const std::string& locale) const;
235 
236  /**
237  * Returns the code of the recommended locale for the current user that the
238  * game supports.
239  *
240  * "That the game supports" means both that a translation file is available
241  * for that locale and that the locale code is either supported by ICU or
242  * the special "long" locale code.
243  *
244  * The mechanism to select a recommended locale follows this logic:
245  * 1. First see if the game supports the specified locale,\n
246  * @p configLocale.
247  * 2. Otherwise, check the system locale and see if the game supports\n
248  * that locale.
249  * 3. Else, return the default locale, 'en_US'.
250  *
251  * @param configLocaleString Locale to check for support first. Pass an
252  * empty string to check the system locale directly.
253  * @return Code of a locale that the game supports.
254  *
255  * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
256  */
257  std::string GetDictionaryLocale(const std::string& configLocaleString) const;
258 
259  /**
260  * Saves an instance of the recommended locale for the current user that the
261  * game supports in the specified variable.
262  *
263  * "That the game supports" means both that a translation file is available
264  * for that locale and that the locale code is either supported by ICU or
265  * the special "long" locale code.
266  *
267  * The mechanism to select a recommended locale follows this logic:
268  * 1. First see if the game supports the specified locale,\n
269  * @p configLocale.
270  * 2. Otherwise, check the system locale and see if the game supports\n
271  * that locale.
272  * 3. Else, return the default locale, 'en_US'.
273  *
274  * @param configLocaleString Locale to check for support first. Pass an
275  * empty string to check the system locale directly.
276  * @param outLocale The recommended locale.
277  *
278  * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
279  */
280  void GetDictionaryLocale(const std::string& configLocaleString, icu::Locale& outLocale) const;
281 
282  /**
283  * Determines the best, supported locale for the current user, makes it the
284  * current game locale and reloads the translations dictionary with
285  * translations for that locale.
286  *
287  * To determine the best locale, ReevaluateCurrentLocaleAndReload():
288  * 1. Checks the user game configuration.
289  * 2. If the locale is not defined there, it checks the system locale.
290  * 3. If none of those locales are supported by the game, the default\n
291  * locale, 'en_US', is used.
292  *
293  * @sa GetCurrentLocale()
294  */
295  void ReevaluateCurrentLocaleAndReload();
296 
297  /**
298  * Returns @c true if the locale is supported by both ICU and the game. It
299  * returns @c false otherwise.
300  *
301  * It returns @c true if both of these conditions are true:
302  * 1. ICU has resources for that locale (which also ensures it's a valid\n
303  * locale string).
304  * 2. Either a dictionary for language_country or for language is\n
305  * available.
306  *
307  * @param locale Locale to check.
308  * @return Whether @p locale is supported by both ICU and the game (@c true)
309  * or not (@c false).
310  */
311  bool ValidateLocale(const icu::Locale& locale) const;
312  ///@overload ValidateLocale
313  bool ValidateLocale(const std::string& localeCode) const;
314 
315  /**
316  * Returns the translation of the specified string to the
317  * @link L10n::GetCurrentLocale() current locale@endlink.
318  *
319  * @param sourceString String to translate to the current locale.
320  * @return Translation of @p sourceString to the current locale, or
321  * @p sourceString if there is no translation available.
322  */
323  std::string Translate(const std::string& sourceString) const;
324 
325  /**
326  * Returns the translation of the specified string to the
327  * @link L10n::GetCurrentLocale() current locale@endlink in the specified
328  * context.
329  *
330  * @param context Context where the string is used. See
331  * http://www.gnu.org/software/gettext/manual/html_node/Contexts.html
332  * @param sourceString String to translate to the current locale.
333  * @return Translation of @p sourceString to the current locale in the
334  * specified @p context, or @p sourceString if there is no
335  * translation available.
336  */
337  std::string TranslateWithContext(const std::string& context, const std::string& sourceString) const;
338 
339  /**
340  * Returns the translation of the specified string to the
341  * @link L10n::GetCurrentLocale() current locale@endlink based on the
342  * specified number.
343  *
344  * @param singularSourceString String to translate to the current locale,
345  * in English' singular form.
346  * @param pluralSourceString String to translate to the current locale, in
347  * English' plural form.
348  * @param number Number that determines the required form of the translation
349  * (or the English string if no translation is available).
350  * @return Translation of the source string to the current locale for the
351  * specified @p number, or either @p singularSourceString (if
352  * @p number is 1) or @p pluralSourceString (if @p number is not 1)
353  * if there is no translation available.
354  */
355  std::string TranslatePlural(const std::string& singularSourceString, const std::string& pluralSourceString, int number) const;
356 
357  /**
358  * Returns the translation of the specified string to the
359  * @link L10n::GetCurrentLocale() current locale@endlink in the specified
360  * context, based on the specified number.
361  *
362  * @param context Context where the string is used. See
363  * http://www.gnu.org/software/gettext/manual/html_node/Contexts.html
364  * @param singularSourceString String to translate to the current locale,
365  * in English' singular form.
366  * @param pluralSourceString String to translate to the current locale, in
367  * English' plural form.
368  * @param number Number that determines the required form of the translation
369  * (or the English string if no translation is available). *
370  * @return Translation of the source string to the current locale in the
371  * specified @p context and for the specified @p number, or either
372  * @p singularSourceString (if @p number is 1) or
373  * @p pluralSourceString (if @p number is not 1) if there is no
374  * translation available.
375  */
376  std::string TranslatePluralWithContext(const std::string& context, const std::string& singularSourceString, const std::string& pluralSourceString, int number) const;
377 
378  /**
379  * Translates a text line by line to the
380  * @link L10n::GetCurrentLocale() current locale@endlink.
381  *
382  * TranslateLines() is helpful when you need to translate a plain text file
383  * after you load it.
384  *
385  * @param sourceString Text to translate to the current locale.
386  * @return Line by line translation of @p sourceString to the current
387  * locale. Some of the lines in the returned text may be in English
388  * because there was not translation available for them.
389  */
390  std::string TranslateLines(const std::string& sourceString) const;
391 
392  /**
393  * Parses the date in the input string using the specified date format, and
394  * returns the parsed date as a UNIX timestamp in milliseconds (not
395  * seconds).
396  *
397  * @param dateTimeString String containing the date to parse.
398  * @param dateTimeFormat Date format string to parse the input date, defined
399  * using ICU date formatting symbols.
400  * @param locale Locale to use when parsing the input date.
401  * @return Specified date as a UNIX timestamp in milliseconds (not seconds).
402  *
403  * @sa GetCurrentLocale()
404  *
405  * @sa http://en.wikipedia.org/wiki/Unix_time
406  * @sa https://sites.google.com/site/icuprojectuserguide/formatparse/datetime?pli=1#TOC-Date-Field-Symbol-Table
407  */
408  UDate ParseDateTime(const std::string& dateTimeString, const std::string& dateTimeFormat, const icu::Locale& locale) const;
409 
410  /**
411  * Returns the specified date using the specified date format.
412  *
413  * @param dateTime Date specified as a UNIX timestamp in milliseconds
414  * (not seconds).
415  * @param type Whether the formatted date must show both the date and the
416  * time, only the date or only the time.
417  * @param style ICU style for the formatted date.
418  * @return String containing the specified date with the specified date
419  * format.
420  *
421  * @sa http://en.wikipedia.org/wiki/Unix_time
422  * @sa http://icu-project.org/apiref/icu4c521/classicu_1_1DateFormat.html
423  */
424  std::string LocalizeDateTime(const UDate dateTime, const DateTimeType& type, const icu::DateFormat::EStyle& style) const;
425 
426  /**
427  * Returns the specified date using the specified date format.
428  *
429  * @param milliseconds Date specified as a UNIX timestamp in milliseconds
430  * (not seconds).
431  * @param formatString Date format string defined using ICU date formatting
432  * symbols. Usually, you internationalize the format string and
433  * get it translated before you pass it to
434  * FormatMillisecondsIntoDateString().
435  * @param useLocalTimezone Boolean useful for durations
436  * @return String containing the specified date with the specified date
437  * format.
438  *
439  * @sa http://en.wikipedia.org/wiki/Unix_time
440  * @sa https://sites.google.com/site/icuprojectuserguide/formatparse/datetime?pli=1#TOC-Date-Field-Symbol-Table
441  */
442  std::string FormatMillisecondsIntoDateString(const UDate milliseconds, const std::string& formatString, bool useLocalTimezone) const;
443 
444  /**
445  * Returns the specified floating-point number as a string, with the number
446  * formatted as a decimal number using the
447  * @link L10n::GetCurrentLocale() current locale@endlink.
448  *
449  * @param number Number to format.
450  * @return Decimal number formatted using the current locale.
451  */
452  std::string FormatDecimalNumberIntoString(double number) const;
453 
454  /**
455  * Returns the localized version of the specified path if there is one for
456  * the @link L10n::GetCurrentLocale() current locale@endlink.
457  *
458  * If there is no localized version of the specified path, it returns the
459  * specified path.
460  *
461  * For example, if the code of the current locale is 'de_DE', LocalizePath()
462  * splits the input path into folder path and file name, and checks whether
463  * the '<folder>/l10n/de/<file>' file exists. If it does, it returns that
464  * path. Otherwise, it returns the input path, verbatim.
465  *
466  * This function is used for file localization (for example, image
467  * localization).
468  *
469  * @param sourcePath %Path to localize.
470  * @return Localized path if it exists, @c sourcePath otherwise.
471  *
472  * @sa http://trac.wildfiregames.com/wiki/Localization#LocalizingImages
473  */
474  VfsPath LocalizePath(const VfsPath& sourcePath) const;
475 
476  /**
477  * Loads @p path into the dictionary if it is a translation file of the
478  * @link L10n::GetCurrentLocale() current locale@endlink.
479  */
480  Status ReloadChangedFile(const VfsPath& path);
481 
482 private:
483  /**
484  * Dictionary that contains the translations for the
485  * @link L10n::GetCurrentLocale() current locale@endlink and the matching
486  * English strings, including contexts and plural forms.
487  *
488  * @sa LoadDictionaryForCurrentLocale()
489  */
490  std::unique_ptr<tinygettext::Dictionary> m_Dictionary;
491 
492  /**
493  * Locale that the game is currently using.
494  *
495  * To get the current locale, use its getter: GetCurrentLocale(). You can
496  * also use GetCurrentLocaleString() to get the locale code of the current
497  * locale.
498  *
499  * To change the value of this variable:
500  * 1. Save a new locale to the game configuration file with SaveLocale().
501  * 2. Reload the translation dictionary with\n
502  * ReevaluateCurrentLocaleAndReload().
503  */
504  icu::Locale m_CurrentLocale;
505 
506  /**
507  * Vector with the locales that the game supports.
508  *
509  * The list of available locales is calculated when the game starts. Call
510  * LoadListOfAvailableLocales() to refresh the list.
511  *
512  * @sa GetSupportedLocaleBaseNames()
513  * @sa GetSupportedLocaleDisplayNames()
514  */
515  std::vector<icu::Locale> m_AvailableLocales;
516 
517  /**
518  * Whether the game is using the default game locale (@c true), 'en_US', or
519  * not (@c false).
520  *
521  * This variable is used in the L10n implementation for performance reasons.
522  * Many localization steps can be skipped when this variable is @c true.
523  */
524  bool m_CurrentLocaleIsOriginalGameLocale{false};
525 
526  /**
527  * Whether the game is using the special game locale with the longest
528  * strings of each translation (@c true) or not (@c false).
529  *
530  * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
531  */
532  bool m_UseLongStrings{false};
533 
534  /**
535  * Loads the translation files for the
536  * @link L10n::GetCurrentLocale() current locale@endlink.
537  *
538  * This method loads every file in the 'l10n' folder of the game virtual
539  * filesystem that is prefixed with the code of the current locale followed
540  * by a dot.
541  *
542  * For example, if the code of the current locale code is 'de',
543  * LoadDictionaryForCurrentLocale() loads the 'l10n/de.engine.po' and
544  * 'l10n/de.public.po' translation files.
545  *
546  * @sa dictionary
547  * @sa ReadPoIntoDictionary()
548  */
549  void LoadDictionaryForCurrentLocale();
550 };
551 
552 #endif // INCLUDED_L10N
icu::Locale m_CurrentLocale
Locale that the game is currently using.
Definition: L10n.h:504
Both date and time.
Definition: L10n.h:73
Only date.
Definition: L10n.h:74
Definition: path.h:79
DateTimeType
Types of dates.
Definition: L10n.h:71
i64 Status
Error handling system.
Definition: status.h:169
Definition: L10n.h:35
std::vector< icu::Locale > m_AvailableLocales
Vector with the locales that the game supports.
Definition: L10n.h:515
std::unique_ptr< tinygettext::Dictionary > m_Dictionary
Dictionary that contains the translations for the current locale and the matching English strings...
Definition: L10n.h:490
A simple dictionary class that mimics gettext() behaviour.
Definition: dictionary.hpp:34
Singleton for internationalization and localization.
Definition: L10n.h:47
Template base class for singletons.
Definition: Singleton.h:33