IceFire Studios

IceFire Blog

Tips on using SharePoint in a multilingual environment

How to make SharePoint API calls language-independent

SharePoint's API has gone through several iterations, from the 2010 API, to the 2013 API, and now Graph API.  Throughout, it has struggled with being language-independent, particularly when dealing with lists and libraries.  Everything works fine when the site is in English and the user's language is English, but many things don't work like the examples when the API encounters a different language.  If you are writing software, or even a Power Automate flow that uses the API, you need to be conscious of the fact that that the API may stop working if the user is using a different language, or the site was created in a different language.

Let's use a simple problem to illustrate.  Suppose you want to know whether a user is a site administrator.  That information is kept in a handy hidden list on the site, called (in English) the User Information List.  Let's retrieve that information using the SharePoint 2010 API, the one that starts with "_vti_bin/ListData.svc/".  The REST API call would be:

{Site URL}_vti_bin/ListData.svc/UserInformationList(4)/IsSiteAdmin

Here, I am using a "4" to indicate a specific user's ID on this site.  This query returns a single line of xml with (in my case) the value "true".  Simple, right?

Unfortunately, that only works if the user's current UI language is English.  If it's French, that API call fails.  Instead you have to use this call 

_vti_bin/ListData.svc/ListeDInformationsUtilisateur(4)/EstLAdministrateurDuSite

 

This is because the list name and the column name used by the list webservice are localized by the MUI

In Dutch, it's 

_vti_bin/ListData.svc/LijstMetGebruikersgegevens(4)/IsBeheerderVanDeSite

 

In Hindi, 

_vti_bin/ListData.svc/उपयोगकर्ताजानकारीसूची(4)/साइटव्यवस्थापनहै

 

Don't forget to URLencode those Hindi Devanagari characters! 

_vti_bin/ListData.svc/%E0%A4%89%E0%A4%AA%E0%A4%AF%E0%A5%8B%E0%A4%97%E0%A4%95%E0%A4%B0
%E0%A5%8D%E0%A4%A4%E0%A4%BE%E0%A4%9C%E0%A4%BE%E0%A4%A8%E0%A4%95%E0%A4%BE%E0%A4%B0
%E0%A5%80%E0%A4%B8%E0%A5%82%E0%A4%9A%E0%A5%80(4)/%E0%A4%B8%E0%A4%BE%E0%A4%87%E0%A4%9F
%E0%A4%B5%E0%A5%8D%E0%A4%AF%E0%A4%B5%E0%A4%B8%E0%A5%8D%E0%A4%A5%E0%A4%BE%E0%A4%AA
%E0%A4%A8%E0%A4%B9%E0%A5%88

 (line breaks added for formatting)

All of that means that your code has to be ready with 50 different versions of that API call depending on the language of the end user.

It's a little better when you are using the 2013 API.  You still have to give it the list's title, but it's the list's internal name, not the display title for the current language.  To get the list, the 2013 API call is 

/_api/web/lists/getbytitle('<list title>')

 

Better, right?  Unfortunately, the internal name of this and many other standard lists and libraries still depends on the language in which the site has been created.  When you create a new site, whether classic or modern, the site has a base language.  So if the site was created in English, you can get the information with this call 

_api/web/lists/getByTitle('User Information List')/items(4)/IsSiteAdmin

 

That call will work no matter what the user's current language is.  But getByTitle() is still not language-independent.  Using getByTitle is always a bad idea unless you know that no one is creating sites in other languages, but there aren't a lot of other options.  If your site was created in Hindi, the call has to be 

_api/web/lists/getByTitle('%E0%A4%89%E0%A4%AA%E0%A4%AF%E0%A5%8B%E0%A4%97%E0%A4%95
%E0%A4%B0%E0%A5%8D%E0%A4%A4%E0%A4%BE%20%E0%A4%9C%E0%A4%BE%E0%A4%A8%E0%A4%95%E0%A4%BE
%E0%A4%B0%E0%A5%80%20%E0%A4%B8%E0%A5%82%E0%A4%9A%E0%A5%80')/items(4)/IsSiteAdmin

(line breaks added for formatting) 

It's only a little better than the 2010 API.  The API call depends on the site, not the user, and the "IsSiteAdmin" column is not localized.  Why the internal name of a hidden list, something that only programmers will ever see, is localized is anybody's guess.  So compared to the 2010 API, you're not much better off.  You still need to have 50 different versions of the API call, even though it's based on the site language and not the user language.


How do you make the API call independent of language?  One way is to find the list using another characteristic that is not language-dependent. One such characteristic is the EntityTypeName, essentially the template for the list or library.  There will only be one User Information List per site, and its Entity Type is "UserInfo".  There could be several pages libraries, but there typically is only one, similarly for Site Assets and so forth.  So the trick it to find the list using an OData filter like 

/_api/web/lists?$filter=EntityTypeName%20eq%20%27UserInfo%27

 

and use the result to retrieve the list Id, which is a GUID, and then use that GUID to retrieve the information from the list, for example 

_api/web/lists('0040809f-5e97-4920-9068-0b36e2aa4b16')/items(4)/IsSiteAdmin

 

That's two API calls rather than just one, which is unfortunate if you're trying to cut down on queries, but at least you don't have to code 50 different possibilities.

For this specific list, the User Information List, there is also a second solution.  It's based on the fact that this particular list also has a fixed URL that does not include the list name, at "_catalogs/users/detail.aspx".  We use this trick to retrieve the list by URL, using the less well known GetList method.  On my ”/sites/Hi” Hindi site, the API call could be

_api/web/GetList(@listUrl)/?@listUrl=%27%2Fsites%2FHi%2F_catalogs%2Fusers%2Fdetail.aspx%27&FilterField1=ID&FilterValue1=4

 

Neither of those tricks is completely general for all lists and libraries, but I haven't found one that applies in every case.

How about the Graph API?  The Graph API suffers from the same problems as the 2013 API, but does not support the same solutions to those problems.  So for instance after you retrieve the Site ID, you can use it to retrieve the list by name in the same way as for the 2013 API.  If the site is in English, it would be

https://graph.microsoft.com/v1.0/sites/{Site ID}/lists('User Information List')


If it’s in Hindi, you would have to use

https://graph.microsoft.com/v1.0/sites/{Site ID}/lists('उपयोगकर्ता जानकारी सूची')


or you can use

lists?$filter=displayName eq 'उपयोगकर्ता जानकारी सूची'

 

I have not yet succeeded in getting it to filter by EntityTypeName (Graph API  calls it “list/template”) or by URL, or even to find the isSiteAdmin property.  I have only succeeded in collecting an impressive set of OData filter expressions that aren't supported, so I'm afraid I don't yet have a good solution for the Graph API.  Maybe someone more clever than me will find one.

Office365™ - office.microsoft.com IceFire Studios is a Microsoft Partner SharePoint™ 2013 - Microsoft.com