From e7ae5b4793fb74309d5d1f32c3c696d07071d676 Mon Sep 17 00:00:00 2001 From: Mike Kaganski Date: Sun, 5 Mar 2023 11:06:29 +0300 Subject: tdf#152459: fallback to language-only when looking for grammar checker LanguageTool registers itself for languages like "en-US", "es-AR", "de-DE"; but also for generic "en", "es", "de". When the requested language, like "fr-CH" or "es-PH", is not found in the registered grammar checkers, the fallback strings will be attempted. Change-Id: Id460db8d378f246ea98191d22bdb3537fd1aee1c Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148201 Tested-by: Jenkins Reviewed-by: Mike Kaganski --- linguistic/source/gciterator.cxx | 49 +++++++++++++++++++++++++++------------- linguistic/source/gciterator.hxx | 14 +++++++++--- 2 files changed, 44 insertions(+), 19 deletions(-) (limited to 'linguistic') diff --git a/linguistic/source/gciterator.cxx b/linguistic/source/gciterator.cxx index 5bca138e6517..a43a990a886f 100644 --- a/linguistic/source/gciterator.cxx +++ b/linguistic/source/gciterator.cxx @@ -488,8 +488,30 @@ void GrammarCheckingIterator::ProcessResult( } +std::pair> +GrammarCheckingIterator::getServiceForLocale(const lang::Locale& rLocale) const +{ + if (!rLocale.Language.isEmpty()) + { + const OUString sBcp47 = LanguageTag::convertToBcp47(rLocale, false); + GCImplNames_t::const_iterator aLangIt(m_aGCImplNamesByLang.find(sBcp47)); + if (aLangIt != m_aGCImplNamesByLang.end()) + return { aLangIt->second, {} }; + + for (const auto& sFallbackBcp47 : LanguageTag(rLocale).getFallbackStrings(false)) + { + aLangIt = m_aGCImplNamesByLang.find(sFallbackBcp47); + if (aLangIt != m_aGCImplNamesByLang.end()) + return { aLangIt->second, sFallbackBcp47 }; + } + } + + return {}; +} + + uno::Reference< linguistic2::XProofreader > GrammarCheckingIterator::GetGrammarChecker( - const lang::Locale &rLocale ) + lang::Locale &rLocale ) { uno::Reference< linguistic2::XProofreader > xRes; @@ -503,11 +525,11 @@ uno::Reference< linguistic2::XProofreader > GrammarCheckingIterator::GetGrammarC m_bGCServicesChecked = true; } - const LanguageType nLang = LanguageTag::convertToLanguageType( rLocale, false); - GCImplNames_t::const_iterator aLangIt( m_aGCImplNamesByLang.find( nLang ) ); - if (aLangIt != m_aGCImplNamesByLang.end()) // matching configured language found? + if (const auto& [aSvcImplName, oFallbackBcp47] = getServiceForLocale(rLocale); + !aSvcImplName.isEmpty()) // matching configured language found? { - OUString aSvcImplName( aLangIt->second ); + if (oFallbackBcp47) + rLocale = LanguageTag::convertToLocale(*oFallbackBcp47, false); GCReferences_t::const_iterator aImplNameIt( m_aGCReferencesByService.find( aSvcImplName ) ); if (aImplNameIt != m_aGCReferencesByService.end()) // matching impl name found? { @@ -1088,8 +1110,7 @@ void GrammarCheckingIterator::GetConfiguredGCSvcs_Impl() { // only the first entry is used, there should be only one grammar checker per language const OUString aImplName( aImplNames[0] ); - const LanguageType nLang = LanguageTag::convertToLanguageType( rElementName ); - aTmpGCImplNamesByLang[ nLang ] = aImplName; + aTmpGCImplNamesByLang[rElementName] = aImplName; } } else @@ -1137,17 +1158,17 @@ void GrammarCheckingIterator::SetServiceList( { ::osl::Guard< ::osl::Mutex > aGuard( MyMutex() ); - LanguageType nLanguage = LinguLocaleToLanguage( rLocale ); + OUString sBcp47 = LanguageTag::convertToBcp47(rLocale, false); OUString aImplName; if (rSvcImplNames.hasElements()) aImplName = rSvcImplNames[0]; // there is only one grammar checker per language - if (!LinguIsUnspecified(nLanguage) && nLanguage != LANGUAGE_DONTKNOW) + if (!LinguIsUnspecified(sBcp47) && !sBcp47.isEmpty()) { if (!aImplName.isEmpty()) - m_aGCImplNamesByLang[ nLanguage ] = aImplName; + m_aGCImplNamesByLang[sBcp47] = aImplName; else - m_aGCImplNamesByLang.erase( nLanguage ); + m_aGCImplNamesByLang.erase(sBcp47); } } @@ -1157,11 +1178,7 @@ uno::Sequence< OUString > GrammarCheckingIterator::GetServiceList( { ::osl::Guard< ::osl::Mutex > aGuard( MyMutex() ); - OUString aImplName; // there is only one grammar checker per language - LanguageType nLang = LinguLocaleToLanguage( rLocale ); - GCImplNames_t::const_iterator aIt( m_aGCImplNamesByLang.find( nLang ) ); - if (aIt != m_aGCImplNamesByLang.end()) - aImplName = aIt->second; + const OUString aImplName = getServiceForLocale(rLocale).first; // there is only one grammar checker per language if (!aImplName.isEmpty()) return { aImplName }; diff --git a/linguistic/source/gciterator.hxx b/linguistic/source/gciterator.hxx index f35c8b412ef9..18984d398165 100644 --- a/linguistic/source/gciterator.hxx +++ b/linguistic/source/gciterator.hxx @@ -39,6 +39,8 @@ #include #include +#include +#include #include #include "defs.hxx" @@ -96,8 +98,8 @@ class GrammarCheckingIterator: DocMap_t m_aDocIdMap; - // language -> implname mapping - typedef std::map< LanguageType, OUString > GCImplNames_t; + // BCP-47 language tag -> implname mapping + typedef std::map< OUString, OUString > GCImplNames_t; GCImplNames_t m_aGCImplNamesByLang; // implname -> UNO reference mapping @@ -135,13 +137,19 @@ class GrammarCheckingIterator: sal_Int32 GetSuggestedEndOfSentence( const OUString &rText, sal_Int32 nSentenceStartPos, const css::lang::Locale &rLocale ); void GetConfiguredGCSvcs_Impl(); - css::uno::Reference< css::linguistic2::XProofreader > GetGrammarChecker( const css::lang::Locale & rLocale ); + css::uno::Reference< css::linguistic2::XProofreader > GetGrammarChecker( css::lang::Locale & rLocale ); css::uno::Reference< css::util::XChangesBatch > const & GetUpdateAccess() const; GrammarCheckingIterator( const GrammarCheckingIterator & ) = delete; GrammarCheckingIterator & operator = ( const GrammarCheckingIterator & ) = delete; + // Gets the grammar checker service, using fallback locales if necessary, + // and the BCP-47 tag for the updated locale, if the fallback was used. + // Precondition: MyMutex() is locked. + std::pair> + getServiceForLocale(const css::lang::Locale& rLocale) const; + public: void DequeueAndCheck(); -- cgit