Add support for plural forms

This commit is contained in:
mniip
2020-03-24 19:01:51 +03:00
parent a16a4936a5
commit bb4f242e0b
5 changed files with 83 additions and 10 deletions

View File

@@ -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{};

View File

@@ -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;

View File

@@ -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
} }

View File

@@ -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

View File

@@ -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;
}; };