diff options
| author | Michael Meeks <michael.meeks@collabora.com> | 2025-07-12 12:51:39 +0100 |
|---|---|---|
| committer | Michael Meeks <michael.meeks@collabora.com> | 2025-10-18 21:20:33 +0100 |
| commit | b8b17c866b1a253b869d8032f8b2651f9f710f56 (patch) | |
| tree | 13d66f4c1aaf421743f3a3b4289fc7ef545ac0cb | |
| parent | cc582e2a2a85c515a576cb2ec5c7c5cce012af54 (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.cxx | 179 |
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 |
