diff --git a/inflection/src/inflection/dialog/language/SerboCroatianCommonConceptFactory.cpp b/inflection/src/inflection/dialog/language/SerboCroatianCommonConceptFactory.cpp index 00034d57c..d25999ba3 100644 --- a/inflection/src/inflection/dialog/language/SerboCroatianCommonConceptFactory.cpp +++ b/inflection/src/inflection/dialog/language/SerboCroatianCommonConceptFactory.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include #include @@ -44,4 +45,16 @@ ::inflection::dialog::SpeakableString* SerboCroatianCommonConceptFactory::quanti return result; } +void SerboCroatianCommonConceptFactory::applyGatedGenitive( + SemanticFeatureConceptBase& clone, std::u16string_view baseCase, Agreement mode) const +{ + if (mode == Agreement::GOVERNED_PLURAL) { + // In Serbo-Croatian, integers 5+ (and indeclinable numerals like "пет") always govern the Genitive Plural ("бродова"), + // even inside an oblique case context (e.g., Locative, Instrumental, Dative). + clone.putConstraint(semanticFeatureCase, ::inflection::grammar::synthesis::GrammemeConstants::CASE_GENITIVE); + } else { + super::applyGatedGenitive(clone, baseCase, mode); + } +} + } // namespace inflection::dialog::language diff --git a/inflection/src/inflection/dialog/language/SerboCroatianCommonConceptFactory.hpp b/inflection/src/inflection/dialog/language/SerboCroatianCommonConceptFactory.hpp index 425189e5c..69205a2ee 100644 --- a/inflection/src/inflection/dialog/language/SerboCroatianCommonConceptFactory.hpp +++ b/inflection/src/inflection/dialog/language/SerboCroatianCommonConceptFactory.hpp @@ -20,4 +20,7 @@ class inflection::dialog::language::SerboCroatianCommonConceptFactory const SemanticFeatureConceptBase* semanticConcept) const override; explicit SerboCroatianCommonConceptFactory(const ::inflection::util::ULocale& language); + +protected: + void applyGatedGenitive(SemanticFeatureConceptBase& clone, std::u16string_view baseCase, Agreement mode) const override; }; diff --git a/inflection/src/inflection/dialog/language/SlavicCommonConceptFactory.cpp b/inflection/src/inflection/dialog/language/SlavicCommonConceptFactory.cpp index b106556ab..1843138c1 100644 --- a/inflection/src/inflection/dialog/language/SlavicCommonConceptFactory.cpp +++ b/inflection/src/inflection/dialog/language/SlavicCommonConceptFactory.cpp @@ -48,7 +48,7 @@ bool SlavicCommonConceptFactory::isDirectCase(std::u16string_view baseCase, std: return baseCase == GrammemeConstants::CASE_ACCUSATIVE && animacy == GrammemeConstants::ANIMACY_INANIMATE; } -void SlavicCommonConceptFactory::applyGatedGenitive(SemanticFeatureConceptBase& clone, std::u16string_view baseCase) const +void SlavicCommonConceptFactory::applyGatedGenitive(SemanticFeatureConceptBase& clone, std::u16string_view baseCase, Agreement) const { if (baseCase.empty() || baseCase == GrammemeConstants::CASE_NOMINATIVE || baseCase == GrammemeConstants::CASE_ACCUSATIVE) { clone.putConstraint(semanticFeatureCase, GrammemeConstants::CASE_GENITIVE); @@ -118,7 +118,7 @@ ::inflection::dialog::SpeakableString SlavicCommonConceptFactory::resolveQuantit // Animacy is needed here to tell the direct (singular paucal) case from the oblique plural. const std::u16string animacy(getFeature(semanticConcept, semanticFeatureAnimacy)); numberConstraint = isDirectCase(baseCase, animacy) ? GrammemeConstants::NUMBER_SINGULAR : GrammemeConstants::NUMBER_PLURAL; - applyGatedGenitive(*clone, baseCase); + applyGatedGenitive(*clone, baseCase, mode); break; } case Agreement::PAUCAL_PLURAL: { @@ -130,7 +130,7 @@ ::inflection::dialog::SpeakableString SlavicCommonConceptFactory::resolveQuantit } case Agreement::GOVERNED_PLURAL: { numberConstraint = GrammemeConstants::NUMBER_PLURAL; - applyGatedGenitive(*clone, baseCase); + applyGatedGenitive(*clone, baseCase, mode); break; } case Agreement::FRACTION_GENITIVE_SG: { diff --git a/inflection/src/inflection/dialog/language/SlavicCommonConceptFactory.hpp b/inflection/src/inflection/dialog/language/SlavicCommonConceptFactory.hpp index f0e6dfbe1..b7c32e91e 100644 --- a/inflection/src/inflection/dialog/language/SlavicCommonConceptFactory.hpp +++ b/inflection/src/inflection/dialog/language/SlavicCommonConceptFactory.hpp @@ -41,7 +41,7 @@ class inflection::dialog::language::SlavicCommonConceptFactory static std::u16string getFeature(const SemanticFeatureConceptBase& semanticConcept, const SemanticFeature& semanticFeature); Agreement agreementForCount(Plurality::Rule countType) const; bool isDirectCase(std::u16string_view baseCase, std::u16string_view animacy) const; - void applyGatedGenitive(SemanticFeatureConceptBase& clone, std::u16string_view baseCase) const; + virtual void applyGatedGenitive(SemanticFeatureConceptBase& clone, std::u16string_view baseCase, Agreement mode) const; virtual std::optional adjustCase(Plurality::Rule countType, const SemanticFeatureConceptBase& semanticConcept, std::u16string_view baseCase) const; diff --git a/inflection/test/src/inflection/grammar/synthesis/QuantifyTest.cpp b/inflection/test/src/inflection/grammar/synthesis/QuantifyTest.cpp index 83e5f1d9b..ad651db5e 100644 --- a/inflection/test/src/inflection/grammar/synthesis/QuantifyTest.cpp +++ b/inflection/test/src/inflection/grammar/synthesis/QuantifyTest.cpp @@ -584,7 +584,7 @@ TEST_CASE("QuantifyTest#testSerboCroatian") assertQuantity(conceptFactory, ::inflection::dialog::SpeakableString(u"3 брода", u"три брода"), 3, ::inflection::dialog::SpeakableString(u"брод")); assertQuantity(conceptFactory, ::inflection::dialog::SpeakableString(u"4 брода", u"четири брода"), 4, ::inflection::dialog::SpeakableString(u"брод")); assertQuantity(conceptFactory, ::inflection::dialog::SpeakableString(u"5 бродова", u"пет бродова"), 5, ::inflection::dialog::SpeakableString(u"брод")); - // A fraction falls in the CLDR "one" bucket yet must take the genitive singular. + // A fraction falls in the CLDR "other" bucket yet must take the genitive singular. #if U_ICU_VERSION_MAJOR_NUM <= 78 // TODO remove this version check after version 79 is released, and update versions.mk with the new minimum version. WARN("U_ICU_VERSION_MAJOR_NUM <= 78. Please test with a newer version of ICU."); @@ -602,5 +602,12 @@ TEST_CASE("QuantifyTest#testSerboCroatian") #else assertQuantity(conceptFactory, ::inflection::dialog::SpeakableString(u"1,1 земље", u"једна зарез једна земље"), 1.1, ::inflection::dialog::SpeakableString(u"земља")); #endif + + // Verify oblique cases for 5+ (always genitive plural: бродова / земаља) + assertQuantity(conceptFactory, u"5 бродова", u"пет бродова", 5, u"брод", u"locative"); + assertQuantity(conceptFactory, u"5 бродова", u"пет бродова", 5, u"брод", u"instrumental"); + assertQuantity(conceptFactory, u"5 бродова", u"пет бродова", 5, u"брод", u"dative"); + assertQuantity(conceptFactory, u"5 земаља", u"пет земаља", 5, u"земља", u"locative"); + assertQuantity(conceptFactory, u"5 земаља", u"пет земаља", 5, u"земља", u"instrumental"); }