Language Translation Support in SailPoint’s UI

Supporting international customers can be a daunting task if the User Interface (UI) is not designed right from the beginning. As SailPoint continues to expand into different markets, we need to be able to support new languages quickly while continuously creating new UI content. Translations also open an opportunity for security vulnerabilities. To provide a simple and consistent user experience, SailPoint created a shared UI component library we call Armada. Armada components are designed to safely support translations out of the box, so SailPoint engineering teams in any part of the organization can use them with confidence. 

Figure 1. SailPoint login page in French. 

How it all started 

It was 2017 when we began thinking about translations in Angular2. 

Early in Angular2 days, there were two main choices for how to do translations: the default Angular translation system or ngx-translate. There were a few requirements we had when choosing a translation framework.  

  • We wanted the flexibility to have translated content defined anywhere, not only with inline HTML.  
  • The framework had to avoid dangerous implementation strategies that used things like javascript’s eval.  
  • We needed to be able to include dynamic parameters in the evaluation of the translation. 

Ngx-translate won out. Angular’s internationalization is still only available in templates which is a big gap. It does not support dynamic forms or web pages that serve dynamic content. Angular also requires a different build for each locale whereas ngx-translate can serve different locale resources with a single web app build. Ultimately, ngx-translate provided a much more flexible internationalization solution. 

We also use Choice Parser to handle pluralization, parameterized translations, and other cases where we need to evaluate a translated expression. It’s not the most robust option, but it was already used elsewhere in the product and that made it easier to use existing translation files. 

How it works 

A uniform translation approach makes building internationalized components feel easy. 

Armada uses a wrapper service, pipe, and a custom translation interface to provide translations. This keeps Armada from being tightly coupled to a particular translation library because we can change how the wrapping services work under the hood and maintain their public contract with the components that display internationalized content.  

We had a scare with Angular 10 that ngx-translate might no longer be supported due to the IVY architecture changes. If ngx-translate lost developer support, we could transition to a new library while making changes only to the Armada project and maybe some adjustments in setting up the application; effectively minimizing the blast radius. Luckily, the community stepped up to maintain ngx-translate and we did not have to put our design to the test. 

How we stay safe 

HTML can be injected into translations to deliver more customized experiences. The trick is to do so cautiously. 

Occasionally, parts of the translation need individual styling to provide emphasis or links. This can be accomplished by using the element’s innerHtml attribute with the translated content. Blindly applying this technique can create a situation where user input could be interpreted as HTML and expose us to an XSS attack. 

Figure 2. Translation JSON with English content, inline HTML, and dynamic input using the Choice Parser format. 

Figure 3. Translation example taken from the Armada Developer Demo application. 

Angular does some routine detection and sanitization on input values. Therefore, our solution is to mark the parts of the translation that we provide as SafeHtml but keep Angular’s sanitization for the user content. 

Key Takeaways 

I appreciate the chance I’ve had these past few years to learn more about translations in component libraries by working on Armada. While some of the tips that are described in this post are specific to Angular, there are valuable concepts that can apply to component libraries of any framework: 

  • Use an intermediary layer between your code and 3rd party translation libraries to insulate your downstream projects from implementation details. 
  • innerHtml is a powerful and vulnerable mechanism to provide inline styling on translated content. Make sure to apply the proper sanitization techniques to any user content in the translation. 

Discussion