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 <mike.kaganski@collabora.com>
diff --git a/linguistic/source/gciterator.cxx b/linguistic/source/gciterator.cxx
index 5bca138..a43a990 100644
--- a/linguistic/source/gciterator.cxx
+++ b/linguistic/source/gciterator.cxx
@@ -488,8 +488,30 @@ void GrammarCheckingIterator::ProcessResult(
}
std::pair<OUString, std::optional<OUString>>
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 f35c8b4..18984d3 100644
--- a/linguistic/source/gciterator.hxx
+++ b/linguistic/source/gciterator.hxx
@@ -39,6 +39,8 @@
#include <i18nlangtag/lang.h>
#include <map>
#include <optional>
#include <utility>
#include <deque>
#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<OUString, std::optional<OUString>>
getServiceForLocale(const css::lang::Locale& rLocale) const;
public:
void DequeueAndCheck();