sax/source/fastparser/fastparser.cxx |   29 ++++-
 xmloff/source/core/xmlimp.cxx        |  181 ++++++++++++++++++++++++++++++++++-
 2 files changed, 201 insertions(+), 9 deletions(-)

New commits:
commit e016f1378c10bbb685519c1349579a466be0de01
Author:     Tor Lillqvist <[email protected]>
AuthorDate: Fri Nov 7 15:53:16 2025 +0200
Commit:     Andras Timar <[email protected]>
CommitDate: Sun Nov 9 10:23:41 2025 +0100

    Don't let the last translation act as a default
    
    Change-Id: Ia75743ad23ce651c9aab392098686d1978b49fe2
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193629
    Reviewed-by: Tor Lillqvist <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/xmloff/source/core/xmlimp.cxx b/xmloff/source/core/xmlimp.cxx
index d0b0f6e855a8..fd0d1cc8baf1 100644
--- a/xmloff/source/core/xmlimp.cxx
+++ b/xmloff/source/core/xmlimp.cxx
@@ -524,7 +524,9 @@ namespace {
                                     break;
                             }
                         }
-                        if (aKeyString.isEmpty())
+                        if (aLocale.isEmpty())
+                            ; // Do nothing, we don't have a matching 
translation
+                        else if (aKeyString.isEmpty())
                             aKeyString = aStr;
                         else
                         {
commit 48665c433dd203443710c5e5bd9b395a7f4ea002
Author:     Rashesh Padia <[email protected]>
AuthorDate: Thu Nov 6 13:26:53 2025 +0530
Commit:     Andras Timar <[email protected]>
CommitDate: Sun Nov 9 10:23:41 2025 +0100

    l10nMapper: update cleanDomain to use "_" prefix
    
    Change-Id: I2c7a1393281768f89978f0090ac6e2e0b8866ad1
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193628
    Reviewed-by: Andras Timar <[email protected]>
    Tested-by: Jenkins CollaboraOffice <[email protected]>

diff --git a/xmloff/source/core/xmlimp.cxx b/xmloff/source/core/xmlimp.cxx
index 70275f072fdf..d0b0f6e855a8 100644
--- a/xmloff/source/core/xmlimp.cxx
+++ b/xmloff/source/core/xmlimp.cxx
@@ -568,8 +568,8 @@ namespace {
 
         static OUString cleanDomain(const OUString &str)
         {
-            sal_Int32 idx = str.indexOf('|');
-            if (idx > 0)
+            sal_Int32 idx = str.indexOf('_');
+            if (idx > -1)
                 return str.copy(idx+1);
             else
                 return str;
commit f62fd2add313bcba28240e5db3d60d5cea82f48a
Author:     Michael Meeks <[email protected]>
AuthorDate: Sat Jul 12 12:51:39 2025 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Sun Nov 9 10:23:41 2025 +0100

    Implement translation mapper for XML content.
    
    Loads content from a stream with this syntax:
    
     # list of supported bcp47 lang tags
     en-US,de-DE,fr-FR,en-GB
     # then each string
     This is a string
     [en-US] This is stil a string
     [en-GB] I say old boy, what a string
    
    etc.
    
    Change-Id: Id6e3c0de03f57c68246fd3eee7b4912f9adc218e
    Signed-off-by: Michael Meeks <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193627
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Andras Timar <[email protected]>

diff --git a/xmloff/source/core/xmlimp.cxx b/xmloff/source/core/xmlimp.cxx
index 25454277728a..70275f072fdf 100644
--- a/xmloff/source/core/xmlimp.cxx
+++ b/xmloff/source/core/xmlimp.cxx
@@ -29,6 +29,7 @@
 #include <utility>
 #include <vcl/embeddedfontsmanager.hxx>
 #include <vcl/graph.hxx>
+#include <vcl/svapp.hxx>
 #include <xmloff/unointerfacetouniqueidentifiermapper.hxx>
 #include <xmloff/namespacemap.hxx>
 #include <xmloff/xmluconv.hxx>
@@ -65,10 +66,12 @@
 #include <cppuhelper/implbase.hxx>
 #include <cppuhelper/supportsservice.hxx>
 #include <comphelper/extract.hxx>
+#include <comphelper/processfactory.hxx>
 #include <comphelper/documentconstants.hxx>
 #include <comphelper/documentinfo.hxx>
 #include <comphelper/storagehelper.hxx>
 #include <comphelper/attributelist.hxx>
+#include <comphelper/string.hxx>
 #include <unotools/fontcvt.hxx>
 #include <fasttokenhandler.hxx>
 #include <vcl/GraphicExternalLink.hxx>
@@ -78,6 +81,18 @@
 #include <com/sun/star/rdf/XRepositorySupplier.hpp>
 #include <RDFaImportHelper.hxx>
 
+// L10nMapper bits to split out ...
+#include <com/sun/star/uno/Type.hxx>
+#include <com/sun/star/lang/XInitialization.hpp>
+#include <com/sun/star/io/XTextInputStream2.hpp>
+#include <com/sun/star/io/TextInputStream.hpp>
+#include <com/sun/star/container/XMap.hpp>
+#include <cppuhelper/compbase.hxx>
+#include <i18nlangtag/languagetag.hxx>
+#include <comphelper/lok.hxx>
+#include <com/sun/star/lang/XLocalizable.hpp>
+#include <com/sun/star/configuration/theDefaultProvider.hpp>
+
 using ::com::sun::star::beans::XPropertySetInfo;
 
 using namespace ::com::sun::star;
@@ -436,6 +451,162 @@ void SvXMLImport::InitCtor_()
     }
 }
 
+namespace {
+
+    class L10nMapper : public cppu::WeakImplHelper<css::container::XMap>
+    {
+        std::unordered_map<OUString, OUString> maMap;
+
+        L10nMapper() { }
+        virtual ~L10nMapper() override {}
+
+    public:
+        static css::uno::Reference< XMap > load(const 
uno::Reference<css::embed::XStorage> &xStorage)
+        {
+            try {
+                OUString streamName = u"l10n"_ustr;
+                if (xStorage && xStorage->isStreamElement(streamName))
+                {
+                    auto xIn = xStorage->openStreamElement(streamName, 
css::embed::ElementModes::READ|css::embed::ElementModes::NOCREATE);
+                    if (!xIn)
+                        return css::uno::Reference< XMap >();
+
+                    auto xInStrm = uno::Reference<css::io::XInputStream>(xIn, 
UNO_QUERY_THROW);
+
+                    rtl::Reference< L10nMapper > pMap = new L10nMapper();
+
+                    auto xContext = comphelper::getProcessComponentContext();
+
+                    Reference< XTextInputStream2 > xTextStrm;
+                    xTextStrm = css::io::TextInputStream::create(xContext);
+                    xTextStrm->setInputStream(xInStrm);
+                    xTextStrm->setEncoding(u"utf8"_ustr);
+
+                    ::std::vector<OUString> aLocales;
+                    if (comphelper::LibreOfficeKit::isActive())
+                        aLocales = 
comphelper::LibreOfficeKit::getLanguageTag().getFallbackStrings(true);
+                    else
+                        aLocales = LanguageTag(css::uno::Reference< 
css::lang::XLocalizable >(
+                                                   
css::configuration::theDefaultProvider::get(xContext),
+                                                   
css::uno::UNO_QUERY_THROW)->getLocale()).getFallbackStrings(true);
+                    OUString aLocale; // cached best locale match
+
+                    // Format is <opt-prefix|string>
locale  <translated-string>
<repeat>


+                    OUString aKeyString;
+                    size_t nLine = 0;
+                    while (!xTextStrm->isEOF())
+                    {
+                        OUString aStr = xTextStrm->readLine();
+                        nLine++;
+                        if (aStr.getLength() < 1 || aStr[0] == '#')
+                        {
+                            aKeyString = "";
+                            continue;
+                        }
+
+                        // Read ',' separated locale list at the start
+                        if (aLocale.isEmpty())
+                        {
+                            auto aFileLocales = 
comphelper::string::split(aStr, ',');
+
+                            // find and cache the best match
+                            for (const auto &f : aFileLocales)
+                            {
+                                for (const auto &i : aLocales)
+                                {
+                                    if (f == i)
+                                    {
+                                        aLocale = f + u"       "_ustr;
+                                        break;
+                                    }
+                                }
+                                if (!aLocale.isEmpty())
+                                    break;
+                            }
+                        }
+                        if (aKeyString.isEmpty())
+                            aKeyString = aStr;
+                        else
+                        {
+                            if (aStr.startsWith(aLocale))
+                            {
+                                auto aValues = comphelper::string::split(aStr, 
'       ');
+                                if (aValues.size() > 1)
+                                    pMap->maMap[aKeyString] = aValues[1];
+                                else
+                                    pMap->maMap[aKeyString] = 
OUString::Concat(u"invalid line "_ustr) + OUString::number(nLine);
+                            }
+                        }
+                    }
+
+                    xTextStrm->closeInput();
+
+                    auto xMap = css::uno::Reference< XMap >(pMap);
+
+                    return xMap;
+                }
+            }
+            catch (Exception const&)
+            {
+                DBG_UNHANDLED_EXCEPTION("xmloff.core", "exception getting 
BuildId");
+            }
+            return css::uno::Reference< XMap >();
+        }
+
+        // XMap
+        virtual Type SAL_CALL getKeyType() override   { return 
cppu::UnoType<OUString>::get(); };
+        virtual Type SAL_CALL getValueType() override { return 
cppu::UnoType<OUString>::get(); };
+        virtual void SAL_CALL clear() override { maMap.clear(); }
+        virtual sal_Bool SAL_CALL containsKey( const Any& ) override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+        virtual sal_Bool SAL_CALL containsValue( const Any& ) override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+
+        static OUString cleanDomain(const OUString &str)
+        {
+            sal_Int32 idx = str.indexOf('|');
+            if (idx > 0)
+                return str.copy(idx+1);
+            else
+                return str;
+        }
+
+        virtual Any SAL_CALL get( const Any& key ) override
+        {
+            auto it = maMap.find(key.get<OUString>());
+            if (it == maMap.end())
+            {
+                SAL_WARN("xmloff", "Key '" << key << "' with no translation");
+                return css::uno::Any(cleanDomain(key.get<OUString>()));
+            }
+            else
+                return css::uno::Any(it->second);
+        }
+        virtual Any SAL_CALL put( const Any&, const Any& ) override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+        virtual Any SAL_CALL remove( const Any& ) override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+
+        // XElementAccess (base)
+        virtual Type SAL_CALL getElementType() override
+        {
+            throw css::uno::RuntimeException(u"not implemented"_ustr);
+        }
+        virtual sal_Bool SAL_CALL hasElements() override
+        {
+            return !maMap.empty();
+        }
+};
+}
+
 SvXMLImport::SvXMLImport(
     const css::uno::Reference< css::uno::XComponentContext >& xContext,
     OUString const & implementationName,
@@ -457,6 +628,7 @@ SvXMLImport::SvXMLImport(
     SAL_WARN_IF( !xContext.is(), "xmloff.core", "got no service manager" );
     InitCtor_();
     mxParser = xml::sax::FastParser::create( xContext );
+
     setNamespaceHandler( maNamespaceHandler );
     setTokenHandler( xTokenHandler  );
     if ( !bIsNSMapsInitialized )
@@ -1067,7 +1239,12 @@ void SAL_CALL SvXMLImport::initialize( const 
uno::Sequence< uno::Any >& aArgumen
     }
 
     uno::Reference<lang::XInitialization> const xInit(mxParser, 
uno::UNO_QUERY_THROW);
-    xInit->initialize( { Any(u"IgnoreMissingNSDecl"_ustr) });
+
+    css::uno::Reference< XMap > xMap = L10nMapper::load(GetSourceStorage());
+    css::uno::Sequence< css::uno::Any > args{
+        css::uno::Any(u"IgnoreMissingNSDecl"_ustr),
+        css::uno::Any(xMap) };
+    xInit->initialize(args);
 }
 
 // XServiceInfo
commit 7609b64a5ec34a1bd6f322b54705523cc9fea3de
Author:     Michael Meeks <[email protected]>
AuthorDate: Sat Jul 12 12:03:15 2025 +0100
Commit:     Andras Timar <[email protected]>
CommitDate: Sun Nov 9 10:23:41 2025 +0100

    Add content mapping into XFastParser.
    
    Ideally this gets done for free in the parsing thread.
    
    Change-Id: I2392fd51c8152c48cf03ac9ed26e6f62aa6ac4f8
    Signed-off-by: Michael Meeks <[email protected]>
    Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193626
    Tested-by: Jenkins CollaboraOffice <[email protected]>
    Reviewed-by: Andras Timar <[email protected]>

diff --git a/sax/source/fastparser/fastparser.cxx 
b/sax/source/fastparser/fastparser.cxx
index 8815cd58329e..5b217224e6b2 100644
--- a/sax/source/fastparser/fastparser.cxx
+++ b/sax/source/fastparser/fastparser.cxx
@@ -26,6 +26,7 @@
 #include <com/sun/star/lang/DisposedException.hpp>
 #include <com/sun/star/lang/IllegalArgumentException.hpp>
 #include <com/sun/star/uno/XComponentContext.hpp>
+#include <com/sun/star/container/XMap.hpp>
 #include <com/sun/star/xml/sax/FastToken.hpp>
 #include <com/sun/star/xml/sax/SAXParseException.hpp>
 #include <com/sun/star/xml/sax/XFastContextHandler.hpp>
@@ -37,6 +38,7 @@
 #include <sal/log.hxx>
 #include <salhelper/thread.hxx>
 #include <comphelper/diagnose_ex.hxx>
+#include <comphelper/string.hxx>
 #include <o3tl/string_view.hxx>
 
 #include <queue>
@@ -270,6 +272,7 @@ public:
     void produce( bool bForceFlush = false );
     bool m_bIgnoreMissingNSDecl;
     bool m_bDisableThreadedParser;
+    css::uno::Reference<css::container::XMap> mxMap; /// _ prefix string 
mapper for translation
 
 private:
     bool consume(EventList&);
@@ -1351,6 +1354,10 @@ void FastSaxParserImpl::sendPendingCharacters()
 {
     Entity& rEntity = getEntity();
     OUString sChars( pendingCharacters.data(), pendingCharacters.size(), 
RTL_TEXTENCODING_UTF8 );
+
+    if (sChars[0] == '_' && mxMap)
+        mxMap->get(uno::Any(sChars)) >>= sChars;
+
     if (rEntity.mbEnableThreads)
     {
         Event& rEvent = rEntity.getEvent( CallbackType::CHARACTERS );
@@ -1454,15 +1461,21 @@ FastSaxParser::initialize(css::uno::Sequence< 
css::uno::Any > const& rArguments)
     if ( !(rArguments[0] >>= str) )
         throw IllegalArgumentException();
 
-    if ( str == "IgnoreMissingNSDecl" )
-        mpImpl->m_bIgnoreMissingNSDecl = true;
-    else if ( str == "DoSmeplease" )
-        ; //just ignore as this is already immune to billion laughs
-    else if ( str == "DisableThreadedParser" )
-        mpImpl->m_bDisableThreadedParser = true;
-    else
-        throw IllegalArgumentException();
+    auto opts = comphelper::string::split(str, ',');
+    for (auto &s : opts)
+    {
+        if ( s == "IgnoreMissingNSDecl" )
+            mpImpl->m_bIgnoreMissingNSDecl = true;
+        else if ( s == "DoSmeplease" )
+            ; //just ignore as this is already immune to billion laughs
+        else if ( s == "DisableThreadedParser" )
+            mpImpl->m_bDisableThreadedParser = true;
+        else
+            throw IllegalArgumentException();
+    }
 
+    if (rArguments.size() > 1)
+        rArguments[1] >>= mpImpl->mxMap;
 }
 
 void FastSaxParser::parseStream( const xml::sax::InputSource& aInputSource )

Reply via email to