summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorMichael Meeks <michael.meeks@collabora.com>2025-07-12 12:51:39 +0100
committerMichael Meeks <michael.meeks@collabora.com>2025-10-18 21:20:33 +0100
commitb8b17c866b1a253b869d8032f8b2651f9f710f56 (patch)
tree13d66f4c1aaf421743f3a3b4289fc7ef545ac0cb
parentcc582e2a2a85c515a576cb2ec5c7c5cce012af54 (diff)
Implement translation mapper for XML content.private/mmeeks/l10nmapper
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 <michael.meeks@collabora.com>
-rw-r--r--xmloff/source/core/xmlimp.cxx179
1 files changed, 178 insertions, 1 deletions
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>\nlocale\t<translated-string>\n<repeat>\n\n
+ 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("\t");
+ break;
+ }
+ }
+ if (!aLocale.isEmpty())
+ break;
+ }
+ }
+ if (aKeyString.isEmpty())
+ aKeyString = aStr;
+ else
+ {
+ if (aStr.startsWith(aLocale))
+ {
+ auto aValues = comphelper::string::split(aStr, '\t');
+ 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