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