linguistic/source/gciterator.cxx |   49 ++++++++++++++++++++++++++-------------
 linguistic/source/gciterator.hxx |   14 ++++++++---
 2 files changed, 44 insertions(+), 19 deletions(-)

New commits:
commit 21db28345999ee840542532224d67f1f2527f16e
Author:     Mike Kaganski <mike.kagan...@collabora.com>
AuthorDate: Sun Mar 5 11:06:29 2023 +0300
Commit:     Xisco Fauli <xiscofa...@libreoffice.org>
CommitDate: Mon Mar 6 14:49:14 2023 +0000

    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.kagan...@collabora.com>
    (cherry picked from commit e7ae5b4793fb74309d5d1f32c3c696d07071d676)
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/148232
    Reviewed-by: Xisco Fauli <xiscofa...@libreoffice.org>

diff --git a/linguistic/source/gciterator.cxx b/linguistic/source/gciterator.cxx
index 581f356f215e..993b0258db32 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?
         {
@@ -1086,8 +1108,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
@@ -1135,17 +1156,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);
     }
 }
 
@@ -1155,11 +1176,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 c4cc076b3c9b..f159a9ad968f 100644
--- a/linguistic/source/gciterator.hxx
+++ b/linguistic/source/gciterator.hxx
@@ -40,6 +40,8 @@
 #include <i18nlangtag/lang.h>
 
 #include <map>
+#include <optional>
+#include <utility>
 #include <deque>
 
 #include "defs.hxx"
@@ -97,8 +99,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
@@ -136,13 +138,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();

Reply via email to