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 <michael.meeks@collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/193627
Tested-by: Jenkins CollaboraOffice <jenkinscollaboraoffice@gmail.com>
Reviewed-by: Andras Timar <andras.timar@collabora.com>
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/195192
Tested-by: Jenkins
Reviewed-by: Caolán McNamara <caolan.mcnamara@collabora.com>
This commit is contained in:
Michael Meeks
2025-12-08 09:42:29 +01:00
committed by Caolán McNamara
parent d66a2686d0
commit 50e0a48105
+178 -1
View File
@@ -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/xmlgrhlp.hxx>
@@ -66,10 +67,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>
@@ -79,6 +82,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;
@@ -457,6 +472,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>\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 + u"\t"_ustr;
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::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,
@@ -478,6 +649,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 )
@@ -1085,7 +1257,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