1

I would like to use term sets for Refinement because i need the multi language functionality. So unfortunately i know that the Refiner Web Part is not MUI-enabled. Has anyone a idea of a workaround to use MUI with the Refine Web Part?

Thanks, Gerhard

2

Just as it was the case in SharePoint 2010, in SharePoint 2013 the refinement panel only shows in the default language, even if the translations were added in the term store manager.

But there is a work around:

In SharePoint 2013 we can no longer use the solution by creating a farm solution and inheriting from the WebPart class as it was the case in 2010. We should now modify the refinements behavior through javascripts and by using JSOM in the Filter Display Templates.

So let’s start by opening SharePoint Designer:

1) Add the ScriptLinks to your master page. We are going to use sp.js, sp.runtime.js and sp.taxonomy.js (optionally to provide user feedback, we can add sp.ui.dialog.js and sp.init.js) . I put them in the master page because sometimes I have issues with them not being loaded before I want to use them. As my Enterprise Search Center only exists out of a page or two, I assume this can’t be a problem nor generates extra load.

<SharePoint:ScriptLink ID="customScriptLink1" Name="sp.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
    <SharePoint:ScriptLink ID="customScriptLink2" Name="sp.runtime.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
    <SharePoint:ScriptLink ID="customScriptLink3" Name="sp.taxonomy.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
    <SharePoint:ScriptLink ID="customScriptLink4" Name="sp.init.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />
    <SharePoint:ScriptLink ID="customScriptLink5" Name="sp.ui.dialog.js" runat="server" OnDemand="false" LoadAfterUI="true" Localizable="false" />

2) Copy the default filter HTML found in

_catalogs > masterpage > Display Templates > Filters

and paste it. Rename it to something like “Filter_MultiLang.html”. Don’t mind the .js-file, it will be auto-generated by SharePoint.

3) Rename the title in the Filter_MultiLang.html

<title>Multilanguage Refinement Item</title>

This will be the title that will be shown in the refiners configuration of the Refinement webpart

4) First we define some global variables we will use. I created ‘termstorename’ which is a hardcoded string containing the name of the Managed Metadata Service. You are supposed to modify this. Next we have ‘client_language’ which will hold the current browser language of the user (LCID, such as 1033, 1036,…). Next we have ‘context’ which will hold the ClientContext object, ‘taxSession’ which will hold the Taxonomy Session Object and a variable which will hold the TermStore, called ‘termStore’.

var termstorename = "Omptranet Managed Metadata Service";
var client_language = _spPageContextInfo.currentLanguage;
var context = SP.ClientContext.get_current();
var taxSession;
var termStore;

The function that will initialize the Taxonomy Session Object is the following:

function initTax(termstorename){
taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(context);
var termStores = taxSession.get_termStores();
termStore = termStores.getByName(termstorename);
}

Notice that we just create the Term Store object and we will query this to obtain our translations. We could also use a TermSet object for this but I will explain in the next step why I choosed to use the global Term Store.

5) Now we create a function that returns the refinement GUID given the refinementName. I did not yet found a way to directly retrieve the GUID from the standard code. I thought it was the refinementToken object but that does not resemble a correct GUID so that’s why I will lookup the GUID by passing the default value of the refinement. So we are searching for strings, which is never a good idea .

function getGUID(refinername, termstorename, success, error){
initTax(termstorename);

var matchInfo = new SP.Taxonomy.LabelMatchInformation(context);
matchInfo.set_termLabel(refinername);
matchInfo.set_trimUnavailable(true);
var termMatches = termStore.getTerms(matchInfo);
context.load(termMatches);

context.executeQueryAsync(
function() {
if(termMatches.get_data().length &gt; 0){
success(termMatches.get_data()[0].get_id());
}else{
error("getGUID FAILED for " + refinername);
}
},
function(){
error("getGUID FAILED");
}
);
}

We use the getTerms(matchInfo) function of the termStore object to retrieve our data. Querying against the Term Store will search in all TermSets of the MM Service. We could also query against a separate TermSet but this would imply that each time we want to search, we should create a corresponding TermSet object. That’s why I just use the global Term Store.

Notice that when there is data found, I just take the first result as the GUID. It is possible that a result of the query actually shows a value from another Term Set. I did not see this as a problem because eventually it are the same words, so they should have the same translation.

6)After that, we create a function that searches the refinement name in a language we can specify. As refinerguid parameter we give the GUID that our previous function returns. For the language we can use our global variable which returns the language LCID (1033, 1036, etc…). We still pass it as a parameter to the function; this is actually not needed.

function getTranslation(refinerguid, language, termstorename, success, error){
initTax(termstorename);

var translated_term = "";
translated_term = termStore.getTerm(refinerguid).getAllLabels(language);
context.load(translated_term);
context.executeQueryAsync(
function() {
if(translated_term.get_count() &gt; 0){
success(translated_term.itemAt(0).get_value());
}else{
error("No translations found for " + refinerguid);
}
},
function(){
error("Failed");
}
);
}

7)Now we adjust the default outputFilter function :

<!--#_
            function outputFilter(refinementName, refinementCount, refiners, method, aClass, showCounts, refinementToken) {
                var aOnClick = "$getClientControl(this)." + method + "('" + $scriptEncode(Sys.Serialization.JavaScriptSerializer.serialize(refiners)) + "');";
                var nameClass = "ms-ref-name " + (showCounts ? "ms-displayInline" : "ms-displayInlineBlock ms-ref-ellipsis");

        if(!(method == "updateRefinersJSON")){
          var guid = refinementToken.match(/"+([^"]*)/g)[0].replace('"','').substring(2);

                getGUID(refinementName, termstorename,
                 function(refinerGUID){
                     console.log("GUID found for " + refinementName + " -> " + refinerGUID);
                     getTranslation(refinerGUID, client_language, termstorename,
                         function(translation){
                            document.getElementById("RefinementName" + guid).innerHTML = ">" + translation;
                         },
                         function(fail){
                            console.log(fail);
                         });
                 },
                 function(fail){
                    console.log(fail);
                 });
    }
_#-->
            <div id='Value' name='Item'>
                <a id='FilterLink' class='_#= $htmlEncode(aClass) =#_' onclick="_#= aOnClick =#_" href='javascript:{}' title='_#= $htmlEncode(String.format(Srch.U.loadResource("rf_RefineBy"), refinementName)) =#_'>
                    <div id='RefinementName_#= guid =#_' class='_#= nameClass =#_'> _#= $htmlEncode(refinementName) =#_ </div>
<!--#_
                if (showCounts) {
_#-->
                    <div id='RefinementCount' class='ms-ref-count ms-textSmall'> (_#= $htmlEncode(Srch.U.toFormattedNumber(refinementCount)) =#_) </div>
<!--#_
                }
_#-->
                </a>
            </div>
<!--#_

            };

Notice that I pass the refinementToken parameters as an extra (because I thought It would equals the refinement GUID) but It may be deleted.

Notice that we call our two custom functions to retrieve the translation. We show the translation on the page by calling

document.getElementById("RefinementName" + guid).innerHTML = ">" + translation;

This might not be the right way to do it. Ideally, we would pass the translated term to the outputFilter function but by using our async calls, the divs are already displayed on the page. If there is a way to let the div-output wait the finishing of the async calls, that would be great but for the moment I have not found the solution.

That is also why we concatenate the (incorrect) guid to the div’s ID so that we can get the div-object in the DOM and modify the innerHTML.

Also the if-statement that checks the method not to be “updateRefinersJSON” is there for a reason. We don’t want to force translations on the “All Items”-link (that shows after you have refined). There is a chance that otherwise the “All items”-link is overwritten by a translation. And what’s even better, that link will be translated OOTB.

7) Another thing is that when we have to modify the show / hide refiners function. By default some things are (re-)loaded and it might interact with our translations that were added by our javascripts. That’s why I modified the default code to:

</div>
                <a id='unselToggle' class='ms-ref-unsel-toggle ms-commandLink' onclick='if(this.parentNode.querySelector("#unselLongList").style.display == "none"){this.parentNode.querySelector("#unselLongList").style.display="block";for(i=0;i &#60; 5;i++){this.parentNode.querySelector("#unselLongList").querySelectorAll("#Value")[i].style.display="none";}}else{this.parentNode.querySelector("#unselLongList").style.display="none";};'>
                    <div class="ms-displayInlineBlock">_#= $htmlEncode(Srch.U.loadResource("rf_RefinementLabel_More")) =#_</div>
                </a>
<!--#_
            }
_#-->

            </div>

Actually it’s just a javascript toggle.

8) To see your code in action on the search refinements, edit your refinement panel, click Refinements… and modify the Filter Display Template per section. You should be able to select your custom Filter there (as the title you gave your custom HTML-page).

Referred from: MUI Refiners in SP2013

  • Honestly, if this works, you get the TLDR award for an epic answer. – Matthew McDermott Sep 23 '17 at 2:56

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.