Pass context and resource string down to boost::locale separately

because this is often on a hot path, and we can avoid the splitting and
joining of strings like this.

Change-Id: I2a24a123a64b762fd0741c45eaca3ad4bdd5580d
Reviewed-on: https://gerrit.libreoffice.org/c/core/+/119884
Tested-by: Jenkins
Reviewed-by: Noel Grandin <noel.grandin@collabora.co.uk>
diff --git a/include/unotools/resmgr.hxx b/include/unotools/resmgr.hxx
index 244963f..0795c3f 100644
--- a/include/unotools/resmgr.hxx
+++ b/include/unotools/resmgr.hxx
@@ -33,7 +33,8 @@ struct UNOTOOLS_DLLPUBLIC TranslateId
    const char* mpContext;
    const char* mpId;

    TranslateId() {}
    TranslateId()
        : mpContext(nullptr), mpId(nullptr) {}
    TranslateId(const char* pContext, const char* pId)
        : mpContext(pContext), mpId(pId) {}

@@ -43,12 +44,30 @@ struct UNOTOOLS_DLLPUBLIC TranslateId
    bool operator!=(const TranslateId& other) const { return !operator==(other); }
};

struct UNOTOOLS_DLLPUBLIC TranslateNId
{
    const char* mpContext;
    const char* mpSingular;
    const char* mpPlural;

    TranslateNId()
        : mpContext(nullptr), mpSingular(nullptr), mpPlural(nullptr) {}
    TranslateNId(const char* pContext, const char* pSingular, const char* pPlural)
        : mpContext(pContext), mpSingular(pSingular), mpPlural(pPlural) {}

    operator bool() const { return mpContext != nullptr; }

    bool operator==(const TranslateNId& other) const;
    bool operator!=(const TranslateNId& other) const { return !operator==(other); }
};

namespace Translate
{
    UNOTOOLS_DLLPUBLIC std::locale Create(std::string_view aPrefixName, const LanguageTag& rLocale = SvtSysLocale().GetUILanguageTag());
    UNOTOOLS_DLLPUBLIC OUString get(std::string_view aId, const std::locale &loc);
    UNOTOOLS_DLLPUBLIC OUString nget(std::string_view aId, int n, const std::locale &loc);
    UNOTOOLS_DLLPUBLIC OUString get(TranslateId sContextAndId, const std::locale &loc);
    UNOTOOLS_DLLPUBLIC OUString nget(TranslateNId aContextSingularPlural, int n, const std::locale &loc);
    UNOTOOLS_DLLPUBLIC void SetReadStringHook( ResHookProc pProc );
    UNOTOOLS_DLLPUBLIC ResHookProc GetReadStringHook();
    UNOTOOLS_DLLPUBLIC OUString ExpandVariables(const OUString& rString);
diff --git a/include/vcl/font/Feature.hxx b/include/vcl/font/Feature.hxx
index 13c798f..b16843a 100644
--- a/include/vcl/font/Feature.hxx
+++ b/include/vcl/font/Feature.hxx
@@ -12,6 +12,7 @@

#include <vcl/dllapi.h>
#include <rtl/ustring.hxx>
#include <unotools/resmgr.hxx>
#include <vector>

namespace vcl::font
@@ -41,11 +42,11 @@ struct VCL_DLLPUBLIC FeatureParameter
private:
    uint32_t m_nCode;
    OUString m_sDescription;
    const char* m_pDescriptionID;
    TranslateId m_pDescriptionID;

public:
    FeatureParameter(uint32_t nCode, OUString aDescription);
    FeatureParameter(uint32_t nCode, const char* pDescriptionID);
    FeatureParameter(uint32_t nCode, TranslateId pDescriptionID);

    uint32_t getCode() const;
    OUString getDescription() const;
@@ -55,7 +56,7 @@ class VCL_DLLPUBLIC FeatureDefinition
{
private:
    OUString m_sDescription;
    const char* m_pDescriptionID;
    TranslateId m_pDescriptionID;
    OUString m_sNumericPart;
    uint32_t m_nCode;
    uint32_t m_nDefault;
@@ -70,9 +71,9 @@ public:
                      std::vector<FeatureParameter> const& rEnumParameters
                      = std::vector<FeatureParameter>{},
                      uint32_t nDefault = 0);
    FeatureDefinition(uint32_t nCode, const char* pDescriptionID,
    FeatureDefinition(uint32_t nCode, TranslateId pDescriptionID,
                      OUString const& rNumericPart = OUString());
    FeatureDefinition(uint32_t nCode, const char* pDescriptionID,
    FeatureDefinition(uint32_t nCode, TranslateId pDescriptionID,
                      std::vector<FeatureParameter> aEnumParameters);

    const std::vector<FeatureParameter>& getEnumParameters() const;
diff --git a/unotools/source/i18n/resmgr.cxx b/unotools/source/i18n/resmgr.cxx
index 9d3aec3..7095316 100644
--- a/unotools/source/i18n/resmgr.cxx
+++ b/unotools/source/i18n/resmgr.cxx
@@ -313,6 +313,29 @@ namespace Translate
        return result;
    }

    OUString nget(TranslateNId aContextSingularPlural, int n, const std::locale &loc)
    {
        //if it's a key id locale, generate it here
        if (std::use_facet<boost::locale::info>(loc).language() == "qtz")
        {
            OString sKeyId(genKeyId(OString::Concat(aContextSingularPlural.mpContext) + "|" + aContextSingularPlural.mpSingular));
            const char* pForm = n == 0 ? aContextSingularPlural.mpSingular : aContextSingularPlural.mpPlural;
            return OUString::fromUtf8(sKeyId) + u"\u2016" + createFromUtf8(pForm, strlen(pForm));
        }

        //otherwise translate it
        const std::string ret = boost::locale::npgettext(aContextSingularPlural.mpContext, aContextSingularPlural.mpSingular, aContextSingularPlural.mpPlural, n, loc);
        OUString result(ExpandVariables(createFromUtf8(ret.data(), ret.size())));

        if (comphelper::LibreOfficeKit::isActive())
        {
            if (std::use_facet<boost::locale::info>(loc).country() == "CH" &&
                std::use_facet<boost::locale::info>(loc).language() == "de")
                result = result.replaceAll(OUString::fromUtf8("\xC3\x9F"), "ss");
        }
        return result;
    }

    static ResHookProc pImplResHookProc = nullptr;

    OUString ExpandVariables(const OUString& rString)
@@ -338,5 +361,11 @@ bool TranslateId::operator==(const TranslateId& other) const
    return strcmp(mpContext, other.mpContext) == 0 && strcmp(mpId,other.mpId) == 0;
}

bool TranslateNId::operator==(const TranslateNId& other) const
{
    return strcmp(mpContext, other.mpContext) == 0
        && strcmp(mpSingular, other.mpSingular) == 0
        && strcmp(mpPlural, other.mpPlural) == 0;
}

/* vim:set shiftwidth=4 softtabstop=4 expandtab: */
diff --git a/vcl/inc/font/OpenTypeFeatureStrings.hrc b/vcl/inc/font/OpenTypeFeatureStrings.hrc
index 9fa782b..5e1960b 100644
--- a/vcl/inc/font/OpenTypeFeatureStrings.hrc
+++ b/vcl/inc/font/OpenTypeFeatureStrings.hrc
@@ -20,7 +20,7 @@
#ifndef INCLUDED_VCL_INC_FONT_OPENTYPEFEATRESTRINGS_HRC
#define INCLUDED_VCL_INC_FONT_OPENTYPEFEATRESTRINGS_HRC

#define NC_(Context, String) reinterpret_cast<char const *>(Context "\004" u8##String)
#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))

#define STR_FONT_FEATURE_ID_AALT          NC_("STR_FONT_FEATURE_ID_AALT", "Access All Alternates")
#define STR_FONT_FEATURE_ID_AFRC          NC_("STR_FONT_FEATURE_ID_AFRC", "Alternative (Vertical) Fractions")
diff --git a/vcl/inc/print.hrc b/vcl/inc/print.hrc
index 49dbcea..ddc0e3e 100644
--- a/vcl/inc/print.hrc
+++ b/vcl/inc/print.hrc
@@ -20,9 +20,9 @@
#ifndef INCLUDED_VCL_INC_PRINT_HRC
#define INCLUDED_VCL_INC_PRINT_HRC

#define NC_(Context, String) reinterpret_cast<char const *>(Context "\004" u8##String)
#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))

const char* RID_STR_PAPERNAMES[] =
const TranslateId RID_STR_PAPERNAMES[] =
{
    // To translators: This is the first entry of a sequence of paper size names
    NC_("RID_STR_PAPERNAMES", "A0"),
diff --git a/vcl/inc/printaccessoryview.hrc b/vcl/inc/printaccessoryview.hrc
index 936c5b2..c041d78 100644
--- a/vcl/inc/printaccessoryview.hrc
+++ b/vcl/inc/printaccessoryview.hrc
@@ -20,9 +20,9 @@
#ifndef INCLUDED_VCL_INC_PRINTACCESSORYVIEW_HRC
#define INCLUDED_VCL_INC_PRINTACCESSORYVIEW_HRC

#define NC_(Context, String) reinterpret_cast<char const *>(Context "\004" u8##String)
#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))

const char* SV_PRINT_NATIVE_STRINGS[] =
const TranslateId SV_PRINT_NATIVE_STRINGS[] =
{
    NC_("SV_PRINT_NATIVE_STRINGS", "Preview"),
    NC_("SV_PRINT_NATIVE_STRINGS", "Page number"),
diff --git a/vcl/inc/qt5/Qt5FilePicker.hxx b/vcl/inc/qt5/Qt5FilePicker.hxx
index 5fef2aa..4c59bd1 100644
--- a/vcl/inc/qt5/Qt5FilePicker.hxx
+++ b/vcl/inc/qt5/Qt5FilePicker.hxx
@@ -33,6 +33,7 @@

#include <osl/conditn.hxx>
#include <osl/mutex.hxx>
#include <unotools/resmgr.hxx>

#include <QtCore/QObject>
#include <QtCore/QString>
@@ -158,7 +159,7 @@ private:
    Qt5FilePicker(const Qt5FilePicker&) = delete;
    Qt5FilePicker& operator=(const Qt5FilePicker&) = delete;

    static QString getResString(const char* pRedId);
    static QString getResString(TranslateId pRedId);
    static css::uno::Any handleGetListValue(const QComboBox* pWidget, sal_Int16 nControlAction);
    static void handleSetListValue(QComboBox* pQComboBox, sal_Int16 nAction,
                                   const css::uno::Any& rValue);
diff --git a/vcl/inc/strings.hrc b/vcl/inc/strings.hrc
index 95468d1..d08446ba 100644
--- a/vcl/inc/strings.hrc
+++ b/vcl/inc/strings.hrc
@@ -20,7 +20,7 @@
#ifndef INCLUDED_VCL_INC_STRINGS_HRC
#define INCLUDED_VCL_INC_STRINGS_HRC

#define NC_(Context, String) reinterpret_cast<char const *>(Context "\004" u8##String)
#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))

#define SV_RESID_STRING_NOSELECTIONPOSSIBLE          NC_("SV_RESID_STRING_NOSELECTIONPOSSIBLE", "[No selection possible]")

diff --git a/vcl/inc/svdata.hxx b/vcl/inc/svdata.hxx
index 0a22d21..2f96229 100644
--- a/vcl/inc/svdata.hxx
+++ b/vcl/inc/svdata.hxx
@@ -31,6 +31,7 @@
#include <vcl/window.hxx>
#include <vcl/task.hxx>
#include <LibreOfficeKit/LibreOfficeKitTypes.h>
#include <unotools/resmgr.hxx>

#include <com/sun/star/lang/XComponent.hpp>
#include <com/sun/star/i18n/XCharacterClassification.hpp>
@@ -429,7 +430,7 @@ VCL_PLUGIN_PUBLIC basegfx::SystemDependentDataManager& ImplGetSystemDependentDat
VCL_PLUGIN_PUBLIC vcl::Window* ImplGetDefaultWindow();
vcl::Window* ImplGetDefaultContextWindow();
const std::locale& ImplGetResLocale();
VCL_PLUGIN_PUBLIC OUString VclResId(std::string_view aId);
VCL_PLUGIN_PUBLIC OUString VclResId(TranslateId sContextAndId);
DockingManager*     ImplGetDockingManager();
BlendFrameCache*    ImplGetBlendFrameCache();

diff --git a/vcl/inc/units.hrc b/vcl/inc/units.hrc
index 74c4496..79e433f 100644
--- a/vcl/inc/units.hrc
+++ b/vcl/inc/units.hrc
@@ -20,9 +20,9 @@
#ifndef INCLUDED_VCL_INC_UNITS_HRC
#define INCLUDED_VCL_INC_UNITS_HRC

#define NC_(Context, String) reinterpret_cast<char const *>(Context "\004" u8##String)
#define NC_(Context, String) TranslateId(Context, reinterpret_cast<char const *>(u8##String))

std::pair<const char*, FieldUnit> SV_FUNIT_STRINGS[] =
std::pair<TranslateId, FieldUnit> SV_FUNIT_STRINGS[] =
{
    // To translators: This is the first entry of a sequence of measurement unit names
    { NC_("SV_FUNIT_STRINGS", "mm"), FieldUnit::MM },
diff --git a/vcl/qt5/Qt5FilePicker.cxx b/vcl/qt5/Qt5FilePicker.cxx
index 18e2458..9a174a0 100644
--- a/vcl/qt5/Qt5FilePicker.cxx
+++ b/vcl/qt5/Qt5FilePicker.cxx
@@ -576,11 +576,11 @@ OUString SAL_CALL Qt5FilePicker::getLabel(sal_Int16 controlId)
    return toOUString(label);
}

QString Qt5FilePicker::getResString(const char* pResId)
QString Qt5FilePicker::getResString(TranslateId pResId)
{
    QString aResString;

    if (pResId == nullptr)
    if (!pResId)
        return aResString;

    aResString = toQString(VclResId(pResId));
@@ -592,7 +592,7 @@ void Qt5FilePicker::addCustomControl(sal_Int16 controlId)
{
    QWidget* widget = nullptr;
    QLabel* label = nullptr;
    const char* resId = nullptr;
    TranslateId resId;
    QCheckBox* pCheckbox = nullptr;

    switch (controlId)
@@ -805,7 +805,7 @@ void SAL_CALL Qt5FilePicker::initialize(const uno::Sequence<uno::Any>& args)
                                                 static_cast<XFilePicker2*>(this), 1);
    }

    const char* resId = nullptr;
    TranslateId resId;
    switch (acceptMode)
    {
        case QFileDialog::AcceptOpen:
diff --git a/vcl/source/app/stdtext.cxx b/vcl/source/app/stdtext.cxx
index f5b603e..bef130d 100644
--- a/vcl/source/app/stdtext.cxx
+++ b/vcl/source/app/stdtext.cxx
@@ -92,7 +92,7 @@ OUString GetStandardQueryBoxText()

OUString GetStandardText(StandardButtonType eButton)
{
    static const char* aResIdAry[static_cast<int>(StandardButtonType::Count)] =
    static TranslateId aResIdAry[static_cast<int>(StandardButtonType::Count)] =
    {
        // http://lists.freedesktop.org/archives/libreoffice/2013-January/044513.html
        // Under windows we don't want accelerators on ok/cancel but do on other
diff --git a/vcl/source/app/svapp.cxx b/vcl/source/app/svapp.cxx
index 7e1ad08..1a8c1d0 100644
--- a/vcl/source/app/svapp.cxx
+++ b/vcl/source/app/svapp.cxx
@@ -1144,7 +1144,7 @@ OUString Application::GetAppName()

enum {hwAll=0, hwEnv=1, hwUI=2};

static OUString Localize(std::string_view aId, const bool bLocalize)
static OUString Localize(TranslateId aId, const bool bLocalize)
{
    if (bLocalize)
        return VclResId(aId);
diff --git a/vcl/source/app/svdata.cxx b/vcl/source/app/svdata.cxx
index 8b4e5fc..147d010 100644
--- a/vcl/source/app/svdata.cxx
+++ b/vcl/source/app/svdata.cxx
@@ -256,7 +256,7 @@ const std::locale& ImplGetResLocale()
    return pSVData->maResLocale;
}

OUString VclResId(std::string_view aId)
OUString VclResId(TranslateId aId)
{
    return Translate::get(aId, ImplGetResLocale());
}
diff --git a/vcl/source/font/Feature.cxx b/vcl/source/font/Feature.cxx
index 99a3910..3a8c20b 100644
--- a/vcl/source/font/Feature.cxx
+++ b/vcl/source/font/Feature.cxx
@@ -62,11 +62,10 @@ FeatureSetting::FeatureSetting(OString feature)
FeatureParameter::FeatureParameter(uint32_t nCode, OUString aDescription)
    : m_nCode(nCode)
    , m_sDescription(std::move(aDescription))
    , m_pDescriptionID(nullptr)
{
}

FeatureParameter::FeatureParameter(uint32_t nCode, const char* pDescriptionID)
FeatureParameter::FeatureParameter(uint32_t nCode, TranslateId pDescriptionID)
    : m_nCode(nCode)
    , m_pDescriptionID(pDescriptionID)
{
@@ -89,8 +88,7 @@ uint32_t FeatureParameter::getCode() const { return m_nCode; }
// FeatureDefinition

FeatureDefinition::FeatureDefinition()
    : m_pDescriptionID(nullptr)
    , m_nCode(0)
    : m_nCode(0)
    , m_nDefault(0)
    , m_eType(FeatureParameterType::BOOL)
{
@@ -101,7 +99,6 @@ FeatureDefinition::FeatureDefinition(uint32_t nCode, OUString const& rDescriptio
                                     std::vector<FeatureParameter> const& rEnumParameters,
                                     uint32_t nDefault)
    : m_sDescription(rDescription)
    , m_pDescriptionID(nullptr)
    , m_nCode(nCode)
    , m_nDefault(nDefault)
    , m_eType(eType)
@@ -109,7 +106,7 @@ FeatureDefinition::FeatureDefinition(uint32_t nCode, OUString const& rDescriptio
{
}

FeatureDefinition::FeatureDefinition(uint32_t nCode, const char* pDescriptionID,
FeatureDefinition::FeatureDefinition(uint32_t nCode, TranslateId pDescriptionID,
                                     OUString const& rNumericPart)
    : m_pDescriptionID(pDescriptionID)
    , m_sNumericPart(rNumericPart)
@@ -119,7 +116,7 @@ FeatureDefinition::FeatureDefinition(uint32_t nCode, const char* pDescriptionID,
{
}

FeatureDefinition::FeatureDefinition(uint32_t nCode, const char* pDescriptionID,
FeatureDefinition::FeatureDefinition(uint32_t nCode, TranslateId pDescriptionID,
                                     std::vector<FeatureParameter> aEnumParameters)
    : m_pDescriptionID(pDescriptionID)
    , m_nCode(nCode)
diff --git a/vcl/source/window/brdwin.cxx b/vcl/source/window/brdwin.cxx
index 0f3ee44..76b07ed 100644
--- a/vcl/source/window/brdwin.cxx
+++ b/vcl/source/window/brdwin.cxx
@@ -288,7 +288,7 @@ OUString ImplBorderWindowView::ImplRequestHelp( ImplBorderFrameData const * pDat
                                              const Point& rPos,
                                              tools::Rectangle& rHelpRect )
{
    const char* pHelpId = nullptr;
    TranslateId pHelpId;
    OUString aHelpStr;
    BorderWindowHitTest nHitTest = ImplHitTest( pData, rPos );
    if ( nHitTest != BorderWindowHitTest::NONE )
diff --git a/vcl/source/window/builder.cxx b/vcl/source/window/builder.cxx
index 2c34c47..723c28c 100644
--- a/vcl/source/window/builder.cxx
+++ b/vcl/source/window/builder.cxx
@@ -2963,9 +2963,7 @@ void VclBuilder::handleRow(xmlreader::XmlReader &reader, const OString &rID)
                OUString sFinalValue;
                if (bTranslated)
                {
                    if (!sContext.isEmpty())
                        sValue = sContext + "\004" + sValue;
                    sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
                    sFinalValue = Translate::get({sContext.getStr(), sValue.getStr()}, m_pParserState->m_aResLocale);
                }
                else
                    sFinalValue = OUString::fromUtf8(sValue);
@@ -3128,9 +3126,7 @@ std::vector<ComboBoxTextItem> VclBuilder::handleItems(xmlreader::XmlReader &read
                OUString sFinalValue;
                if (bTranslated)
                {
                    if (!sContext.isEmpty())
                        sValue = sContext + "\004" + sValue;
                    sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
                    sFinalValue = Translate::get({sContext.getStr(), sValue.getStr()}, m_pParserState->m_aResLocale);
                }
                else
                    sFinalValue = OUString::fromUtf8(sValue);
@@ -3943,9 +3939,7 @@ void VclBuilder::collectProperty(xmlreader::XmlReader &reader, stringmap &rMap) 
    OUString sFinalValue;
    if (bTranslated)
    {
        if (!sContext.isEmpty())
            sValue = sContext + "\004" + sValue;
        sFinalValue = Translate::get(sValue.getStr(), m_pParserState->m_aResLocale);
        sFinalValue = Translate::get({sContext.getStr(), sValue.getStr()}, m_pParserState->m_aResLocale);
    }
    else
        sFinalValue = OUString::fromUtf8(sValue);
diff --git a/vcl/source/window/splitwin.cxx b/vcl/source/window/splitwin.cxx
index b82769d..ed32ca8 100644
--- a/vcl/source/window/splitwin.cxx
+++ b/vcl/source/window/splitwin.cxx
@@ -2083,7 +2083,7 @@ void SplitWindow::RequestHelp( const HelpEvent& rHEvt )
    {
        Point       aMousePosPixel = ScreenToOutputPixel( rHEvt.GetMousePosPixel() );
        tools::Rectangle   aHelpRect;
        const char* pHelpResId = nullptr;
        TranslateId pHelpResId;

        ImplGetFadeInRect( aHelpRect, true );
        if ( aHelpRect.IsInside( aMousePosPixel ) )
diff --git a/vcl/unx/gtk3/fpicker/resourceprovider.cxx b/vcl/unx/gtk3/fpicker/resourceprovider.cxx
index 95c098a7..f656a93 100644
--- a/vcl/unx/gtk3/fpicker/resourceprovider.cxx
+++ b/vcl/unx/gtk3/fpicker/resourceprovider.cxx
@@ -32,7 +32,7 @@ using namespace ::com::sun::star::ui::dialogs::CommonFilePickerElementIds;
const struct
{
    sal_Int32 ctrlId;
    const char *resId;
    TranslateId resId;
} CtrlIdToResIdTable[] = {
    { CHECKBOX_AUTOEXTENSION,                   STR_FPICKER_AUTO_EXTENSION },
    { CHECKBOX_PASSWORD,                        STR_FPICKER_PASSWORD },
@@ -57,21 +57,21 @@ const struct
    { FILE_PICKER_FILE_TYPE,                    STR_FPICKER_TYPE }
};

static const char* CtrlIdToResId( sal_Int32 aControlId )
static TranslateId CtrlIdToResId( sal_Int32 aControlId )
{
    for (auto & i : CtrlIdToResIdTable)
    {
        if ( i.ctrlId == aControlId )
            return i.resId;
    }
    return nullptr;
    return {};
}

OUString SalGtkPicker::getResString( sal_Int32 aId )
{
    OUString aResString;
    // translate the control id to a resource id
    const char *pResId = CtrlIdToResId( aId );
    TranslateId pResId = CtrlIdToResId( aId );
    if (pResId)
        aResString = VclResId(pResId);
    return aResString.replace('~', '_');