mirror of
https://github.com/vcmi/vcmi.git
synced 2024-12-28 23:06:24 +02:00
Implemented basic support for plural forms in translations
This commit is contained in:
parent
7391f2a6ee
commit
7d54f6a9c0
@ -238,6 +238,18 @@
|
||||
"vcmi.optionsTab.simturnsAI.hover" : "(Experimental) Simultaneous AI Turns",
|
||||
"vcmi.optionsTab.simturns.help" : "Play simultaneously at least for specified number of days, after which simultaneous turns will stay active until specified day or until players make contact. Contacts between players during this period are blocked",
|
||||
"vcmi.optionsTab.simturnsAI.help" : "",
|
||||
|
||||
// Translation note: translate strings below using form that is correct for "0 days", "1 day" and "2 days" in your language
|
||||
// Using this information, VCMI will automatically select correct plural form for every possible amount
|
||||
"vcmi.optionsTab.simturns.days.0" : " %d days",
|
||||
"vcmi.optionsTab.simturns.days.1" : " %d day",
|
||||
"vcmi.optionsTab.simturns.days.2" : " %d days",
|
||||
"vcmi.optionsTab.simturns.weeks.0" : " %d weeks",
|
||||
"vcmi.optionsTab.simturns.weeks.1" : " %d week",
|
||||
"vcmi.optionsTab.simturns.weeks.2" : " %d weeks",
|
||||
"vcmi.optionsTab.simturns.months.0" : " %d months",
|
||||
"vcmi.optionsTab.simturns.months.1" : " %d month",
|
||||
"vcmi.optionsTab.simturns.months.2" : " %d months",
|
||||
|
||||
// Custom victory conditions for H3 campaigns and HotA maps
|
||||
"vcmi.map.victoryCondition.daysPassed.toOthers" : "The enemy has managed to survive till this day. Victory is theirs!",
|
||||
|
@ -18,6 +18,7 @@
|
||||
#include "../CGameInfo.h"
|
||||
|
||||
#include "../../lib/StartInfo.h"
|
||||
#include "../../lib/Languages.h"
|
||||
#include "../../lib/MetaString.h"
|
||||
#include "../../lib/CGeneralTextHandler.h"
|
||||
|
||||
@ -189,6 +190,32 @@ OptionsTabBase::OptionsTabBase(const JsonPath & configPath)
|
||||
|
||||
void OptionsTabBase::recreate()
|
||||
{
|
||||
auto const & generateSimturnsDurationText = [](int days) -> std::string
|
||||
{
|
||||
bool canUseMonth = days % 28 == 0 && days >= 28*2;
|
||||
bool canUseWeek = days % 7 == 0 && days >= 7*2;
|
||||
|
||||
MetaString message;
|
||||
int value = days;
|
||||
std::string text = "vcmi.optionsTab.simturns.days";
|
||||
|
||||
if (canUseWeek)
|
||||
{
|
||||
value = days / 7;
|
||||
text = "vcmi.optionsTab.simturns.weeks";
|
||||
}
|
||||
|
||||
if (canUseMonth)
|
||||
{
|
||||
value = days / 28;
|
||||
text = "vcmi.optionsTab.simturns.months";
|
||||
}
|
||||
|
||||
message.appendTextID(Languages::getPluralFormTextID( CGI->generaltexth->getPreferredLanguage(), value, text));
|
||||
message.replaceNumber(value);
|
||||
return message.toString();
|
||||
};
|
||||
|
||||
//Simultaneous turns
|
||||
if(auto turnSlider = widget<CSlider>("simturnsDurationMin"))
|
||||
turnSlider->scrollTo(SEL->getStartInfo()->simturnsInfo.requiredTurns);
|
||||
@ -197,20 +224,10 @@ void OptionsTabBase::recreate()
|
||||
turnSlider->scrollTo(SEL->getStartInfo()->simturnsInfo.optionalTurns);
|
||||
|
||||
if(auto w = widget<CLabel>("labelSimturnsDurationValueMin"))
|
||||
{
|
||||
MetaString message;
|
||||
message.appendRawString("%d days");
|
||||
message.replaceNumber(SEL->getStartInfo()->simturnsInfo.requiredTurns);
|
||||
w->setText(message.toString());
|
||||
}
|
||||
w->setText(generateSimturnsDurationText(SEL->getStartInfo()->simturnsInfo.requiredTurns));
|
||||
|
||||
if(auto w = widget<CLabel>("labelSimturnsDurationValueMax"))
|
||||
{
|
||||
MetaString message;
|
||||
message.appendRawString("%d days");
|
||||
message.replaceNumber(SEL->getStartInfo()->simturnsInfo.optionalTurns);
|
||||
w->setText(message.toString());
|
||||
}
|
||||
w->setText(generateSimturnsDurationText(SEL->getStartInfo()->simturnsInfo.optionalTurns));
|
||||
|
||||
const auto & turnTimerRemote = SEL->getStartInfo()->turnTimerInfo;
|
||||
|
||||
|
100
lib/Languages.h
100
lib/Languages.h
@ -12,6 +12,17 @@
|
||||
namespace Languages
|
||||
{
|
||||
|
||||
enum class EPluralForms
|
||||
{
|
||||
NONE,
|
||||
VI_1, // Single plural form, (Vietnamese)
|
||||
EN_2, // Two forms, singular used for one only (English)
|
||||
FR_2, // Two forms, singular used for zero and one (French)
|
||||
UK_3, // Three forms, special cases for numbers ending in 1 and 2, 3, 4, except those ending in 1[1-4] (Ukrainian)
|
||||
CZ_3, // Three forms, special cases for 1 and 2, 3, 4 (Czech)
|
||||
PL_3, // Three forms, special case for one and some numbers ending in 2, 3, or 4 (Polish)
|
||||
};
|
||||
|
||||
enum class ELanguages
|
||||
{
|
||||
CZECH,
|
||||
@ -57,6 +68,9 @@ struct Options
|
||||
/// primary IETF language tag
|
||||
std::string tagIETF;
|
||||
|
||||
/// Ruleset for plural forms in this language
|
||||
EPluralForms pluralForms = EPluralForms::NONE;
|
||||
|
||||
/// VCMI supports translations into this language
|
||||
bool hasTranslation = false;
|
||||
};
|
||||
@ -65,27 +79,27 @@ inline const auto & getLanguageList()
|
||||
{
|
||||
static const std::array<Options, 20> languages
|
||||
{ {
|
||||
{ "czech", "Czech", "Čeština", "CP1250", "cs", true },
|
||||
{ "chinese", "Chinese", "简体中文", "GBK", "zh", true }, // Note: actually Simplified Chinese
|
||||
{ "english", "English", "English", "CP1252", "en", true },
|
||||
{ "finnish", "Finnish", "Suomi", "CP1252", "fi", true },
|
||||
{ "french", "French", "Français", "CP1252", "fr", true },
|
||||
{ "german", "German", "Deutsch", "CP1252", "de", true },
|
||||
{ "hungarian", "Hungarian", "Magyar", "CP1250", "hu", true },
|
||||
{ "italian", "Italian", "Italiano", "CP1250", "it", true },
|
||||
{ "korean", "Korean", "한국어", "CP949", "ko", true },
|
||||
{ "polish", "Polish", "Polski", "CP1250", "pl", true },
|
||||
{ "portuguese", "Portuguese", "Português", "CP1252", "pt", true }, // Note: actually Brazilian Portuguese
|
||||
{ "russian", "Russian", "Русский", "CP1251", "ru", true },
|
||||
{ "spanish", "Spanish", "Español", "CP1252", "es", true },
|
||||
{ "swedish", "Swedish", "Svenska", "CP1252", "sv", true },
|
||||
{ "turkish", "Turkish", "Türkçe", "CP1254", "tr", true },
|
||||
{ "ukrainian", "Ukrainian", "Українська", "CP1251", "uk", true },
|
||||
{ "vietnamese", "Vietnamese", "Tiếng Việt", "UTF-8", "vi", true }, // Fan translation uses special encoding
|
||||
{ "czech", "Czech", "Čeština", "CP1250", "cs", EPluralForms::CZ_3, true },
|
||||
{ "chinese", "Chinese", "简体中文", "GBK", "zh", EPluralForms::VI_1, true }, // Note: actually Simplified Chinese
|
||||
{ "english", "English", "English", "CP1252", "en", EPluralForms::EN_2, true },
|
||||
{ "finnish", "Finnish", "Suomi", "CP1252", "fi", EPluralForms::EN_2, true },
|
||||
{ "french", "French", "Français", "CP1252", "fr", EPluralForms::FR_2, true },
|
||||
{ "german", "German", "Deutsch", "CP1252", "de", EPluralForms::EN_2, true },
|
||||
{ "hungarian", "Hungarian", "Magyar", "CP1250", "hu", EPluralForms::EN_2, true },
|
||||
{ "italian", "Italian", "Italiano", "CP1250", "it", EPluralForms::EN_2, true },
|
||||
{ "korean", "Korean", "한국어", "CP949", "ko", EPluralForms::VI_1, true },
|
||||
{ "polish", "Polish", "Polski", "CP1250", "pl", EPluralForms::PL_3, true },
|
||||
{ "portuguese", "Portuguese", "Português", "CP1252", "pt", EPluralForms::EN_2, true }, // Note: actually Brazilian Portuguese
|
||||
{ "russian", "Russian", "Русский", "CP1251", "ru", EPluralForms::UK_3, true },
|
||||
{ "spanish", "Spanish", "Español", "CP1252", "es", EPluralForms::EN_2, true },
|
||||
{ "swedish", "Swedish", "Svenska", "CP1252", "sv", EPluralForms::EN_2, true },
|
||||
{ "turkish", "Turkish", "Türkçe", "CP1254", "tr", EPluralForms::EN_2, true },
|
||||
{ "ukrainian", "Ukrainian", "Українська", "CP1251", "uk", EPluralForms::UK_3, true },
|
||||
{ "vietnamese", "Vietnamese", "Tiếng Việt", "UTF-8", "vi", EPluralForms::VI_1, true }, // Fan translation uses special encoding
|
||||
|
||||
{ "other_cp1250", "Other (East European)", "", "CP1250", "", false },
|
||||
{ "other_cp1251", "Other (Cyrillic Script)", "", "CP1251", "", false },
|
||||
{ "other_cp1252", "Other (West European)", "", "CP1252", "", false }
|
||||
{ "other_cp1250", "Other (East European)", "", "CP1250", "", EPluralForms::NONE, false },
|
||||
{ "other_cp1251", "Other (Cyrillic Script)", "", "CP1251", "", EPluralForms::NONE, false },
|
||||
{ "other_cp1252", "Other (West European)", "", "CP1252", "", EPluralForms::NONE, false }
|
||||
} };
|
||||
static_assert(languages.size() == static_cast<size_t>(ELanguages::COUNT), "Languages array is missing a value!");
|
||||
|
||||
@ -109,4 +123,50 @@ inline const Options & getLanguageOptions(const std::string & language)
|
||||
return emptyValue;
|
||||
}
|
||||
|
||||
template<typename Numeric>
|
||||
inline constexpr int getPluralFormIndex(EPluralForms form, Numeric value)
|
||||
{
|
||||
// Based on https://www.gnu.org/software/gettext/manual/html_node/Plural-forms.html
|
||||
switch(form)
|
||||
{
|
||||
case EPluralForms::NONE:
|
||||
case EPluralForms::VI_1:
|
||||
return 0;
|
||||
case EPluralForms::EN_2:
|
||||
if (value == 1)
|
||||
return 1;
|
||||
return 2;
|
||||
case EPluralForms::FR_2:
|
||||
if (value == 1 || value == 0)
|
||||
return 1;
|
||||
return 2;
|
||||
case EPluralForms::UK_3:
|
||||
if (value % 10 == 1 && value % 100 != 11)
|
||||
return 1;
|
||||
if (value%10>=2 && value%10<=4 && (value%100<10 || value%100>=20))
|
||||
return 2;
|
||||
return 0;
|
||||
case EPluralForms::CZ_3:
|
||||
if (value == 1)
|
||||
return 1;
|
||||
if (value>=2 && value<=4)
|
||||
return 2;
|
||||
return 0;
|
||||
case EPluralForms::PL_3:
|
||||
if (value == 1)
|
||||
return 1;
|
||||
if (value%10>=2 && value%10<=4 && (value%100<10 || value%100>=20))
|
||||
return 2;
|
||||
return 0;
|
||||
}
|
||||
throw std::runtime_error("Invalid plural form enumeration received!");
|
||||
}
|
||||
|
||||
template<typename Numeric>
|
||||
inline std::string getPluralFormTextID(std::string languageName, Numeric value, std::string textID)
|
||||
{
|
||||
int formIndex = getPluralFormIndex(getLanguageOptions(languageName).pluralForms, value);
|
||||
return textID + '.' + std::to_string(formIndex);
|
||||
}
|
||||
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user