Rebased ref, commits from common ancestor:
commit b8b17c866b1a253b869d8032f8b2651f9f710f56
Author:     Michael Meeks <[email protected]>
AuthorDate: Sat Jul 12 12:51:39 2025 +0100
Commit:     Michael Meeks <[email protected]>
CommitDate: Sat Oct 18 21:20:33 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]>

diff --git a/xmloff/source/core/xmlimp.cxx b/xmloff/source/core/xmlimp.cxx
index e5edc29a41cd..5bfdc86b3af1 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);
+
+                    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 + 
OUString::createFromAscii("      ");
+                                        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(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);
+        }
+
+        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(OUString(u"IgnoreMissingNSDecl"_ustr)),
+        css::uno::Any(xMap) };
+    xInit->initialize(args);
 }
 
 // XServiceInfo

Reply via email to