HomeAbout

Adding SharePoint Translations using REST and Cloud Flows

By Denis Molodtsov
Published in SharePoint
February 08, 2024
3 min read
Adding SharePoint Translations using REST and Cloud Flows

Table Of Contents

01
The Problem
02
PowerShell PnP examples
03
Inspecting PnP PowerShell API Requests using Fiddler
04
Get a sample Flow
05
Conclusion

Today I chatted about multilingual SharePoint sites with a fellow consultant, Syed Hanifa. We were both wondering if it’s possible to deploy translations using Power Automate without premium connectors and without using PnP PowerShell, Azure Functions and Azure Automation.

The Problem

There seems to be no official way to deploy translations to SharePoint Online sites via the REST API or the Graph API.

However, I do know that we can do it with the help the Client Object Model (CSOM) and the PnP PowerShell module.

There is a SetValueForUICulture method that allows you to set the translation value for a specific language. It is available for many SharePoint objects such as Content type, Field, Site, Web and Navigation Node.

# Set a title
$SOME_SHAREPOINT_OBJECT.TitleResource.SetValueForUICulture('fr-FR','Title in French')
# Set a description
$SOME_SHAREPOINT_OBJECT.DescriptionResource.SetValueForUICulture('fr-FR','Description in French')

Consider the following examples.

PowerShell PnP examples

Add a new translation for the site title and site description in French

Connect-PnPOnline -Url https://contoso.sharepoint.com/sites/MultilingualTeamSite -ClientId ca5e9978-cf4c-4bc4-981c-67e34b55d810 -ClientSecret '...' -WarningAction Ignore
$web = Get-pnpWeb
$web.TitleResource.SetValueForUICulture('fr-FR','Site Title French')
$web.DescriptionResource.SetValueForUICulture('fr-FR','French Site Description ')
$web.Update()
Invoke-PnPQuery

Add a new translation for the list title and list description in French

$list = Get-PnPList MyList
$list.TitleResource.SetValueForUICulture('fr-FR','Multilingual List French')
$list.DescriptionResource.SetValueForUICulture('fr-FR','French Description')
$list.Update()
Invoke-PnPQuery

Add a french translation to the ‘Documents’ quick navigation node:

$nodes = Get-PnPNavigationNode -Location QuickLaunch
$node = $nodes | Where-Object {$_.Title -eq "Documents"}
$node.TitleResource.SetValueForUICulture('fr-FR','French Documents translation')
$node.Update()
Invoke-PnPQuery

Get existing translations

How do I know what translations are already set? Should I open the SharePoint site using different browser profile with different language settings? No, you don’t have to. Simply use the GetResourceEntries() method. The GetResourceEntries() method will get you translations for all sorts of SharePoint objects:

# Check existing translations for a list
$ResourceEntriesForTitle = $list.TitleResource.GetResourceEntries()
$ResourceEntriesForDescription = $list.DescriptionResource.GetResourceEntries()
Invoke-PnPQuery
$ResourceEntriesForTitle
$ResourceEntriesForDescription
# Check existing translations for a field
$ResourceEntriesForTitle = $field.TitleResource.GetResourceEntries()
$ResourceEntriesForDescription = $field.DescriptionResource.GetResourceEntries()
Invoke-PnPQuery
$ResourceEntriesForTitle
$ResourceEntriesForDescription

Inspecting PnP PowerShell API Requests using Fiddler

OK, these examples are great, but how does PnP PowerShell do it? Since I wanted to see what’s going on under the hood, I used Fiddler to inspect the requests made by PnP PowerShell.

$web.TitleResource.SetValueForUICulture('fr-FR','Site Title French') results in the following POST request:

POST /_vti_bin/client.svc/ProcessQuery
BODY:
<Request AddExpandoFieldTypeSuffix="true"
SchemaVersion="15.0.0.0" LibraryVersion="16.0.0.0"
ApplicationName="SharePoint PnP PowerShell Library"
xmlns="http://schemas.microsoft.com/sharepoint/clientquery/2009">
<Actions>
<ObjectPath Id="8" ObjectPathId="7" />
<Method Name="SetValueForUICulture" Id="9" ObjectPathId="7">
<Parameters>
<Parameter Type="String">fr-FR</Parameter>
<Parameter Type="String">Site Title French</Parameter>
</Parameters>
</Method>
<Method Name="Update" Id="10" ObjectPathId="3" />
</Actions>
<ObjectPaths>
<Property Id="7" ParentId="3" Name="TitleResource" />
<Identity Id="3"
Name="67df09a1-0018-4000-e1c1-8ca67d1a058f|740c6a0b-85e2-48a0-a494-e0f1759d4aa7:site:5900d0c7-5c7c-47c0-97f3-ca5ac9c392a3:web:7cfedc29-bb5b-4500-a774-e11edb25f54e" />
</ObjectPaths>
</Request>

The ProcessQuery endpoint is used by the CSOM to send a batch of requests to SharePoint. The Actions element contains the requests that we want to send. The ObjectPath element is used to identify the object that we are working with.

The SetValueForUICulture method in the XML request is the action we are looking for. It sets the translation value of for a specific language, for a specific object

<Method Name="SetValueForUICulture" Id="9" ObjectPathId="7">
<Parameters>
<Parameter Type="String">fr-FR</Parameter>
<Parameter Type="String">Site Title French</Parameter>
</Parameters>
</Method>

The Identity element in the XML request is used to identify the object that we are translating. It is a series of GUIDs that represent the object in SharePoint.

<Identity Id="3"
Name="67df09a1-0018-4000-e1c1-8ca67d1a058f|
740c6a0b-85e2-48a0-a494-e0f1759d4aa7:
site:5900d0c7-5c7c-47c0-97f3-ca5ac9c392a3:
web:7cfedc29-bb5b-4500-a774-e11edb25f54e" />

Let’s break it down:

  • 67df09a1-0018-4000-e1c1-8ca67d1a058f - a Web Request ID. This can be a random GUID and won’t change the result of the request.
  • 740c6a0b-85e2-48a0-a494-e0f1759d4aa7 - is a constant value that stands for the Microsoft.SharePoint.SPObjectFactory class (Microsoft.SharePoint assembly). It is used to create objects in SharePoint. All you need to know is that it is a constant value and it won’t change.
  • site:5900d0c7-5c7c-47c0-97f3-ca5ac9c392a3 - is the GUID of the site collection.
  • web:7cfedc29-bb5b-4500-a774-e11edb25f54e - is the GUID of the web (subsite).

If you want to translate a list, you will also need to set the ‘list:GUID’ part of the Identity element. Same goes for the fields, navigation nodes and other SharePoint objects. It all depends on the object you are translating. The best way to get the Identity is to use the PnP PowerShell and Fiddler to capture the requests.

Using this knowledge, we can now create our own REST API requests to set translations for SharePoint objects. Let’s send a request using ‘Power Automate’s Send an HTTP request to SharePoint’ action:

Sending a HTTP REST request to SharePoint
Sending a HTTP REST request to SharePoint

Sending a HTTP REST request to SharePoint
Sending a HTTP REST request to SharePoint

I was delighted to see that the translation was successfully added to the site title.

Then using Fiddler, I captured the requests for other translation operations and created a Power Automate flow that sets translations for the site title, site description, list title, list description and quick navigation nodes.

First, I wanted to get important GUIDs:

Site Collection ID

GET /_api/site/id
accept: application/json; odata=nometadata

Web ID

GET /_api/web/id
accept: application/json; odata=nometadata

List ID

GET _api/web/lists/getByTitle('MyList')/id
accept: application/json; odata=nometadata

Power Automate Flow actions

Sample flow
Sample flow

I am using the application/json;odata=nometadata for the Accept header. This is because I don’t need the metadata in the response.

Then, using the Compose action, I prepared a full object path for the SharePoint translation:

Compose action
Compose action

You might notice that the Field ID is hardcoded. This is because this ID was already known to me. But you can get it using a separate REST call.

Finally, to set the translation, I used the ‘Send an HTTP request to SharePoint’ action:

Adding french translation to a field
Adding french translation to a field

Similarly, I added a translation for the list title:

Adding translation to a list
Adding translation to a list

And for the site:

Translating the site title
Translating the site title

Translating the quick navigation node was a bit more complicated. I had to get the navigation node ID first:

Get all quick navigation node IDs
Get all quick navigation node IDs
_ObjectIdentity_ property contains the full path to the navigation node. I used it to set the translation for the quick navigation node.

"_Child_Items_": [
{
"_ObjectType_": "SP.NavigationNode",
"_ObjectIdentity_": "09bf09a1-f062-4000-c72f-d287bb8974ca|740c6a0b-85e2-48a0-a494-e0f1759d4aa7:site:5900d0c7-5c7c-47c0-97f3-ca5ac9c392a3:web:7cfedc29-bb5b-4500-a774-e11edb25f54e:navnode:1031",
"Title": "Sample Page",
"Url": "/sites/MultilingualTeamSite"
},
{
"_ObjectType_": "SP.NavigationNode",
"_ObjectIdentity_": "09bf09a1-f062-4000-c72f-d287bb8974ca|740c6a0b-85e2-48a0-a494-e0f1759d4aa7:site:5900d0c7-5c7c-47c0-97f3-ca5ac9c392a3:web:7cfedc29-bb5b-4500-a774-e11edb25f54e:navnode:2002",
"Title": "Notebook",
"Url": "/sites/MultilingualTeamSite/_layouts/15/Doc.aspx?sourcedoc={382b7dc2-4009-4025-8aeb-5a7bec436c84}&action=editnew"
},
{
"_ObjectType_": "SP.NavigationNode",
"_ObjectIdentity_": "09bf09a1-f062-4000-c72f-d287bb8974ca|740c6a0b-85e2-48a0-a494-e0f1759d4aa7:site:5900d0c7-5c7c-47c0-97f3-ca5ac9c392a3:web:7cfedc29-bb5b-4500-a774-e11edb25f54e:navnode:2001",
"Title": "Documents",
"Url": "/sites/MultilingualTeamSite/Shared Documents/Forms/AllItems.aspx"
},
{
"_ObjectType_": "SP.NavigationNode",
"_ObjectIdentity_": "09bf09a1-f062-4000-c72f-d287bb8974ca|740c6a0b-85e2-48a0-a494-e0f1759d4aa7:site:5900d0c7-5c7c-47c0-97f3-ca5ac9c392a3:web:7cfedc29-bb5b-4500-a774-e11edb25f54e:navnode:2003",
"Title": "Pages",
"Url": "/sites/MultilingualTeamSite/SitePages/Forms/ByAuthor.aspx"
}
]

Then I used the quick navigation link’s _ObjectIdentity_ to set the translation:

09bf09a1-f062-4000-c72f-d287bb8974ca|740c6a0b-85e2-48a0-a494-e0f1759d4aa7:site:5900d0c7-5c7c-47c0-97f3-ca5ac9c392a3:web:7cfedc29-bb5b-4500-a774-e11edb25f54e:navnode:2001

Setting the translation for the quick navigation node:

Translate a Quick navigation link
Translate a Quick navigation link

Get a sample Flow

I decided to share the flow with you. You can download it and import it into your environment. You can use it as a starting point for your own multilingual SharePoint sites.

👉 Click here to download a sample Flow 👈

Conclusion

I was able to set translations for SharePoint objects using Power Automate and the REST API. This is a great alternative to using PnP PowerShell, Azure Functions and Azure Automation. Although it requires some knowledge of the SharePoint REST API, Fiddler and XML, it is a great way to automate translations without using premium connectors.


Tags

LanguagesSharePointSharePoint Online

Share

Previous Article
Using Selenium for Web Automation with PowerShell
Denis Molodtsov

Denis Molodtsov

Microsoft 365 Architect

Related Posts

Archive SharePoint Online Sites Using M365 Archive Feature
Archive SharePoint Online Sites Using M365 Archive Feature
April 02, 2024
4 min

Quick Links

About

Social Media