mirror of
https://github.com/The-Powder-Toy/The-Powder-Toy.git
synced 2025-09-02 04:22:34 +02:00
Add support for plural forms
This commit is contained in:
@@ -6,9 +6,16 @@ struct LocaleEN : public Locale
|
|||||||
{
|
{
|
||||||
String GetName() const { return "English"_ascii; }
|
String GetName() const { return "English"_ascii; }
|
||||||
|
|
||||||
|
size_t GetPluralIndex(size_t n) const
|
||||||
|
{
|
||||||
|
// 0: singular, 1: plural
|
||||||
|
return n == 1 ? 0 : 1;
|
||||||
|
}
|
||||||
|
|
||||||
void Set() const
|
void Set() const
|
||||||
{
|
{
|
||||||
using i18n::translation;
|
using i18n::translation;
|
||||||
|
using i18n::pluralForm;
|
||||||
}
|
}
|
||||||
|
|
||||||
String GetIntroText() const
|
String GetIntroText() const
|
||||||
@@ -132,7 +139,6 @@ struct LocaleEN : public Locale
|
|||||||
"\n"
|
"\n"
|
||||||
"If you have any questions about what is and isn't against the rules, feel free to contact a moderator.";
|
"If you have any questions about what is and isn't against the rules, feel free to contact a moderator.";
|
||||||
}
|
}
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
||||||
Locale const &Locale_EN = LocaleEN{};
|
Locale const &Locale_EN = LocaleEN{};
|
||||||
|
@@ -1,5 +1,7 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
|
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
#include "common/Localization.h"
|
#include "common/Localization.h"
|
||||||
|
|
||||||
extern Locale const &Locale_EN;
|
extern Locale const &Locale_EN;
|
||||||
|
@@ -32,9 +32,15 @@ namespace i18n
|
|||||||
#ifdef I18N_DEBUG
|
#ifdef I18N_DEBUG
|
||||||
std::set<std::vector<ByteString> > &activeKeys()
|
std::set<std::vector<ByteString> > &activeKeys()
|
||||||
{
|
{
|
||||||
static std::set<std::vector<ByteString> > activeKeys;
|
static std::set<std::vector<ByteString> > activeKeys{};
|
||||||
return activeKeys;
|
return activeKeys;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
std::set<ByteString> &activePlurals()
|
||||||
|
{
|
||||||
|
static std::set<ByteString> activePlurals{};
|
||||||
|
return activePlurals;
|
||||||
|
}
|
||||||
#endif // I18N_DEBUG
|
#endif // I18N_DEBUG
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -8,6 +8,8 @@
|
|||||||
#include "String.h"
|
#include "String.h"
|
||||||
#include "Localization.h"
|
#include "Localization.h"
|
||||||
|
|
||||||
|
extern Locale const *currentLocale;
|
||||||
|
|
||||||
/*
|
/*
|
||||||
We handle internationalization by maintaining a map from "key" strings, to
|
We handle internationalization by maintaining a map from "key" strings, to
|
||||||
localized versions of those strings. The "keys" are strings in English, the
|
localized versions of those strings. The "keys" are strings in English, the
|
||||||
@@ -67,6 +69,7 @@ namespace i18n
|
|||||||
|
|
||||||
#ifdef I18N_DEBUG
|
#ifdef I18N_DEBUG
|
||||||
std::set<std::vector<ByteString> > &activeKeys();
|
std::set<std::vector<ByteString> > &activeKeys();
|
||||||
|
std::set<ByteString> &activePlurals();
|
||||||
|
|
||||||
template<char... cs> struct Chars
|
template<char... cs> struct Chars
|
||||||
{
|
{
|
||||||
@@ -84,8 +87,14 @@ namespace i18n
|
|||||||
MultiKeyUsage() { activeKeys().insert({Ts::chars...}); }
|
MultiKeyUsage() { activeKeys().insert({Ts::chars...}); }
|
||||||
};
|
};
|
||||||
|
|
||||||
|
template<typename T> struct PluralUsage
|
||||||
|
{
|
||||||
|
PluralUsage() { activePlurals().insert(T::chars); }
|
||||||
|
};
|
||||||
|
|
||||||
template<char... cs> KeyUsage<cs...> keyUsage;
|
template<char... cs> KeyUsage<cs...> keyUsage;
|
||||||
template<typename... Ts> MultiKeyUsage<Ts...> multiKeyUsage;
|
template<typename... Ts> MultiKeyUsage<Ts...> multiKeyUsage;
|
||||||
|
template<typename T> PluralUsage<T> pluralUsage;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
template<size_t n> struct TranslationMap
|
template<size_t n> struct TranslationMap
|
||||||
@@ -99,6 +108,17 @@ namespace i18n
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
inline std::map<CanonicalPtr, std::vector<String>> &pluralForms()
|
||||||
|
{
|
||||||
|
static std::map<CanonicalPtr, std::vector<String>> pluralForms{};
|
||||||
|
return pluralForms;
|
||||||
|
}
|
||||||
|
|
||||||
|
template<size_t N> std::vector<String> &pluralForm(char const (&str)[N])
|
||||||
|
{
|
||||||
|
return pluralForms()[Canonicalize(str)];
|
||||||
|
}
|
||||||
|
|
||||||
template<size_t N> String &translation(char const (&str)[N])
|
template<size_t N> String &translation(char const (&str)[N])
|
||||||
{
|
{
|
||||||
return TranslationMap<1>::Map()[{Canonicalize(str)}][0];
|
return TranslationMap<1>::Map()[{Canonicalize(str)}][0];
|
||||||
@@ -145,9 +165,15 @@ namespace i18n
|
|||||||
strs[i] = lit;
|
strs[i] = lit;
|
||||||
return getMultiTranslation(strs, i + 1, std::forward<Ts>(args)...);
|
return getMultiTranslation(strs, i + 1, std::forward<Ts>(args)...);
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
extern struct Locale const *currentLocale;
|
inline String getPlural(LiteralPtr str, size_t n)
|
||||||
|
{
|
||||||
|
auto it = pluralForms().find(Canonicalize(str));
|
||||||
|
if(it == pluralForms().end() || !currentLocale)
|
||||||
|
return ByteString(str).FromUtf8();
|
||||||
|
return it->second[currentLocale->GetPluralIndex(n)];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
#ifndef I18N_DEBUG
|
#ifndef I18N_DEBUG
|
||||||
|
|
||||||
@@ -162,6 +188,11 @@ template<typename... Ts> std::array<String, sizeof...(Ts)> i18nMulti(Ts&&... arg
|
|||||||
return i18n::getMultiTranslation(strs, 0, std::forward<Ts>(args)...);
|
return i18n::getMultiTranslation(strs, 0, std::forward<Ts>(args)...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline String i18nPlural(char const *str, size_t n)
|
||||||
|
{
|
||||||
|
return i18n::getPlural(str, n);
|
||||||
|
}
|
||||||
|
|
||||||
#else // I18N_DEBUG
|
#else // I18N_DEBUG
|
||||||
|
|
||||||
template<typename T, T... cs> inline i18n::Chars<cs...> operator""_chars()
|
template<typename T, T... cs> inline i18n::Chars<cs...> operator""_chars()
|
||||||
@@ -202,4 +233,12 @@ template<typename... Ts> std::array<String, sizeof...(Ts)> i18nMultiImpl()
|
|||||||
return i18n::getMultiTranslation(strs, 0, Ts::chars...);
|
return i18n::getMultiTranslation(strs, 0, Ts::chars...);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define i18nPlural(str, n) i18nPluralImpl<decltype(str##_chars)>(n)
|
||||||
|
|
||||||
|
template<typename T> String i18nPluralImpl(size_t n)
|
||||||
|
{
|
||||||
|
(void)i18n::pluralUsage<T>;
|
||||||
|
return i18n::getPlural(T::chars, n);
|
||||||
|
}
|
||||||
|
|
||||||
#endif // I18N_DEBUG
|
#endif // I18N_DEBUG
|
||||||
|
@@ -2,18 +2,38 @@
|
|||||||
|
|
||||||
#include <functional>
|
#include <functional>
|
||||||
|
|
||||||
#include "String.h"
|
|
||||||
|
|
||||||
struct Locale
|
struct Locale
|
||||||
{
|
{
|
||||||
// The name of the language this locale is for, readable in both the native
|
// The name of the language this locale is for, readable in both the native
|
||||||
// language and in English;
|
// language and in English;
|
||||||
virtual String GetName() const = 0;
|
virtual class String GetName() const = 0;
|
||||||
|
|
||||||
// Populate the translations map.
|
// Populate the translations map.
|
||||||
virtual void Set() const = 0;
|
virtual void Set() const = 0;
|
||||||
|
|
||||||
virtual String GetIntroText() const = 0;
|
virtual class String GetIntroText() const = 0;
|
||||||
virtual String GetSavePublishingInfo() const = 0;
|
virtual class String GetSavePublishingInfo() const = 0;
|
||||||
virtual String GetRules() const = 0;
|
virtual class String GetRules() const = 0;
|
||||||
|
|
||||||
|
/*
|
||||||
|
English only has two choices for spelling a noun based on amount:
|
||||||
|
singular when there's exactly 1 object, and plural when there's 0 or 2
|
||||||
|
or more objects.
|
||||||
|
|
||||||
|
In Russian there are three choices, based on the last 2 digits of the
|
||||||
|
amount: singular nominative if the number ends in 1, except 11;
|
||||||
|
singular genitive if the number ends in 2,3,4, except 12 through 14;
|
||||||
|
plural genitive if the for everything else.
|
||||||
|
|
||||||
|
In other languages things can get more complicated, for example in
|
||||||
|
Arabic a noun can have as many as 6 spellings.
|
||||||
|
|
||||||
|
What we can do is give these various forms indices, e.g. for Russian
|
||||||
|
singular nominative=0, singular genitive=1, plural genitive=2;
|
||||||
|
and then use these indices to index into an array of spellings for the
|
||||||
|
respective word.
|
||||||
|
|
||||||
|
GetPluralIndex(n) returns the index of the word form used for n objects.
|
||||||
|
*/
|
||||||
|
virtual size_t GetPluralIndex(size_t n) const = 0;
|
||||||
};
|
};
|
||||||
|
Reference in New Issue
Block a user