Merge 'remotes/trunk'
[0ad.git] / source / i18n / L10n.h
bloba57e8be85dfe51ff6e07a903d4cf4823932e01cd
1 /* Copyright (C) 2022 Wildfire Games.
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:
11 * The above copyright notice and this permission notice shall be included
12 * in all copies or substantial portions of the Software.
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.
23 #ifndef INCLUDED_L10N
24 #define INCLUDED_L10N
26 #include "lib/code_annotation.h"
27 #include "lib/external_libraries/icu.h"
28 #include "lib/file/vfs/vfs_path.h"
29 #include "ps/Singleton.h"
31 #include <memory>
32 #include <string>
33 #include <vector>
35 namespace tinygettext
37 class Dictionary;
40 #define g_L10n L10n::GetSingleton()
42 /**
43 * %Singleton for internationalization and localization.
45 * @sa http://trac.wildfiregames.com/wiki/Internationalization_and_Localization
47 class L10n : public Singleton<L10n>
49 public:
50 /**
51 * Creates an instance of L10n.
53 * L10n is a singleton. Use Instance() instead of creating you own instances
54 * of L10n.
56 L10n();
58 /**
59 * Handles the destruction of L10n.
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.
64 ~L10n();
66 /**
67 * Types of dates.
69 * @sa LocalizeDateTime()
71 enum DateTimeType
73 DateTime, ///< Both date and time.
74 Date, ///< Only date.
75 Time ///< Only time.
78 /**
79 * Returns the current locale.
81 * @sa GetCurrentLocaleString()
82 * @sa GetSupportedLocaleBaseNames()
83 * @sa GetAllLocales()
84 * @sa ReevaluateCurrentLocaleAndReload()
86 const icu::Locale& GetCurrentLocale() const;
88 /**
89 * Returns the code of the current locale.
91 * A locale code is a string such as "de" or "pt_BR".
93 * @sa GetCurrentLocale()
94 * @sa GetSupportedLocaleBaseNames()
95 * @sa GetAllLocales()
96 * @sa ReevaluateCurrentLocaleAndReload()
98 std::string GetCurrentLocaleString() const;
101 * Returns a vector of locale codes supported by ICU.
103 * A locale code is a string such as "de" or "pt_BR".
105 * @return Vector of supported locale codes.
107 * @sa GetSupportedLocaleBaseNames()
108 * @sa GetCurrentLocale()
110 * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#a073d70df8c9c8d119c0d42d70de24137
112 std::vector<std::string> GetAllLocales() const;
115 * Saves the specified locale in the game configuration file.
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.
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.
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).
128 bool SaveLocale(const std::string& localeCode) const;
129 ///@overload SaveLocale
130 bool SaveLocale(const icu::Locale& locale) const;
133 * Returns an array of supported locale codes sorted alphabetically.
135 * A locale code is a string such as "de" or "pt_BR".
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.
141 * @return Array of supported locale codes.
143 * @sa GetSupportedLocaleDisplayNames()
144 * @sa GetAllLocales()
145 * @sa GetCurrentLocale()
147 * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
149 std::vector<std::string> GetSupportedLocaleBaseNames() const;
152 * Returns an array of supported locale names sorted alphabetically by
153 * locale code.
155 * A locale code is a string such as "de" or "pt_BR".
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.
161 * @return Array of supported locale codes.
163 * @sa GetSupportedLocaleBaseNames()
165 * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
167 std::vector<std::wstring> GetSupportedLocaleDisplayNames() const;
170 * Returns the ISO-639 language code of the specified locale code.
172 * For example, if you specify the 'en_US' locate, it returns 'en'.
174 * @param locale Locale code.
175 * @return Language code.
177 * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#af36d821adced72a870d921ebadd0ca93
179 std::string GetLocaleLanguage(const std::string& locale) const;
182 * Returns the programmatic code of the entire locale without keywords.
184 * @param locale Locale code.
185 * @return Locale code without keywords.
187 * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#a4c1acbbdf95dc15599db5f322fa4c4d0
189 std::string GetLocaleBaseName(const std::string& locale) const;
192 * Returns the ISO-3166 country code of the specified locale code.
194 * For example, if you specify the 'en_US' locate, it returns 'US'.
196 * @param locale Locale code.
197 * @return Country code.
199 * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#ae3f1fc415c00d4f0ab33288ceadccbf9
201 std::string GetLocaleCountry(const std::string& locale) const;
204 * Returns the ISO-15924 abbreviation script code of the specified locale code.
206 * @param locale Locale code.
207 * @return Script code.
209 * @sa http://www.icu-project.org/apiref/icu4c/classicu_1_1Locale.html#a5e0145a339d30794178a1412dcc55abe
211 std::string GetLocaleScript(const std::string& locale) const;
214 * Returns @c true if the current locale is the special "Long Strings"
215 * locale. It returns @c false otherwise.
217 * @return Whether the current locale is the special "Long Strings"
218 * (@c true) or not (@c false).
220 bool UseLongStrings() const;
223 * Returns an array of paths to files in the virtual filesystem that provide
224 * translations for the specified locale code.
226 * @param locale Locale code.
227 * @return Array of paths to files in the virtual filesystem that provide
228 * translations for @p locale.
230 std::vector<std::wstring> GetDictionariesForLocale(const std::string& locale) const;
232 std::wstring GetFallbackToAvailableDictLocale(const icu::Locale& locale) const;
234 std::wstring GetFallbackToAvailableDictLocale(const std::string& locale) const;
237 * Returns the code of the recommended locale for the current user that the
238 * game supports.
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.
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'.
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.
255 * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
257 std::string GetDictionaryLocale(const std::string& configLocaleString) const;
260 * Saves an instance of the recommended locale for the current user that the
261 * game supports in the specified variable.
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.
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'.
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.
278 * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
280 void GetDictionaryLocale(const std::string& configLocaleString, icu::Locale& outLocale) const;
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.
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.
293 * @sa GetCurrentLocale()
295 void ReevaluateCurrentLocaleAndReload();
298 * Returns @c true if the locale is supported by both ICU and the game. It
299 * returns @c false otherwise.
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.
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).
311 bool ValidateLocale(const icu::Locale& locale) const;
312 ///@overload ValidateLocale
313 bool ValidateLocale(const std::string& localeCode) const;
316 * Returns the translation of the specified string to the
317 * @link L10n::GetCurrentLocale() current locale@endlink.
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.
323 std::string Translate(const std::string& sourceString) const;
326 * Returns the translation of the specified string to the
327 * @link L10n::GetCurrentLocale() current locale@endlink in the specified
328 * context.
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.
337 std::string TranslateWithContext(const std::string& context, const std::string& sourceString) const;
340 * Returns the translation of the specified string to the
341 * @link L10n::GetCurrentLocale() current locale@endlink based on the
342 * specified number.
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.
355 std::string TranslatePlural(const std::string& singularSourceString, const std::string& pluralSourceString, int number) const;
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.
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.
376 std::string TranslatePluralWithContext(const std::string& context, const std::string& singularSourceString, const std::string& pluralSourceString, int number) const;
379 * Translates a text line by line to the
380 * @link L10n::GetCurrentLocale() current locale@endlink.
382 * TranslateLines() is helpful when you need to translate a plain text file
383 * after you load it.
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.
390 std::string TranslateLines(const std::string& sourceString) const;
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).
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).
403 * @sa GetCurrentLocale()
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
408 UDate ParseDateTime(const std::string& dateTimeString, const std::string& dateTimeFormat, const icu::Locale& locale) const;
411 * Returns the specified date using the specified date format.
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.
421 * @sa http://en.wikipedia.org/wiki/Unix_time
422 * @sa http://icu-project.org/apiref/icu4c521/classicu_1_1DateFormat.html
424 std::string LocalizeDateTime(const UDate dateTime, const DateTimeType& type, const icu::DateFormat::EStyle& style) const;
427 * Returns the specified date using the specified date format.
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.
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
442 std::string FormatMillisecondsIntoDateString(const UDate milliseconds, const std::string& formatString, bool useLocalTimezone) const;
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.
449 * @param number Number to format.
450 * @return Decimal number formatted using the current locale.
452 std::string FormatDecimalNumberIntoString(double number) const;
455 * Returns the localized version of the specified path if there is one for
456 * the @link L10n::GetCurrentLocale() current locale@endlink.
458 * If there is no localized version of the specified path, it returns the
459 * specified path.
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.
466 * This function is used for file localization (for example, image
467 * localization).
469 * @param sourcePath %Path to localize.
470 * @return Localized path if it exists, @c sourcePath otherwise.
472 * @sa http://trac.wildfiregames.com/wiki/Localization#LocalizingImages
474 VfsPath LocalizePath(const VfsPath& sourcePath) const;
477 * Loads @p path into the dictionary if it is a translation file of the
478 * @link L10n::GetCurrentLocale() current locale@endlink.
480 Status ReloadChangedFile(const VfsPath& path);
482 private:
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.
488 * @sa LoadDictionaryForCurrentLocale()
490 std::unique_ptr<tinygettext::Dictionary> m_Dictionary;
493 * Locale that the game is currently using.
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.
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().
504 icu::Locale m_CurrentLocale;
507 * Vector with the locales that the game supports.
509 * The list of available locales is calculated when the game starts. Call
510 * LoadListOfAvailableLocales() to refresh the list.
512 * @sa GetSupportedLocaleBaseNames()
513 * @sa GetSupportedLocaleDisplayNames()
515 std::vector<icu::Locale> m_AvailableLocales;
518 * Whether the game is using the default game locale (@c true), 'en_US', or
519 * not (@c false).
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.
524 bool m_CurrentLocaleIsOriginalGameLocale{false};
527 * Whether the game is using the special game locale with the longest
528 * strings of each translation (@c true) or not (@c false).
530 * @sa http://trac.wildfiregames.com/wiki/Implementation_of_Internationalization_and_Localization#LongStringsLocale
532 bool m_UseLongStrings{false};
535 * Loads the translation files for the
536 * @link L10n::GetCurrentLocale() current locale@endlink.
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.
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.
546 * @sa dictionary
547 * @sa ReadPoIntoDictionary()
549 void LoadDictionaryForCurrentLocale();
552 #endif // INCLUDED_L10N