Using HTML attributes for translations
stumbling my way to getting things to work
By Zhian N. Kamvar in example
December 13, 2023
I have just released an update to both {sandpaper} (lesson engine) and {varnish} (HTML templates, CSS, and JavaScript) which enables translation of navigation elements for Workbench websites, which means that when you visit a lesson written in any language, the navigation elements such as menu items (“Key Points”, “Instructor View”, etc.) and buttons (“Collapse”, “Expand”) will appear in the same language as the lesson is written in. For example, the Spanish version of “Version Control in Git” now has everything displayed in Spanish: El Control de Versiones con Git.
Implementation
With help from Joel Nitta, using a guide to translating messages in R packages by Maëlle Salmon, we were able to implement translations in both {sandpaper} and {varnish} by translating a string to a variable and passing that variable to {varnish}:
data <- list(
...,
translate = list(
...
KeyPoints = tr_("Key Points"),
...
)
)
pkgdown::render_page(pkg, data = data)
In {varnish}, the HTML would contain mustache templating, where values from {sandpaper} would be inserted. In the case of the Key Points menu item, it would look like this (simplified)
<li>
<a href="key-points.html">{{ translate.KeyPoints }}</a>
</li>
When a lesson was in English, it would render to be:
<li>
<a href="key-points.html">Key Points</a>
</li>
In Japanese, it would be:
<li>
<a href="key-points.html">まとめ</a>
</li>
Challenge: JavaScript
One of the challenges we had came in the form of buttons:
<button role="button"
aria-label="{{ translate.CloseMenu }}"
alt="{{ translate.CloseMenu }}"
aria-expanded="true"
aria-controls="sidebar"
class="collapse-toggle">
<i class="search-icon" data-feather="x" role="img"></i>
</button>
This button is controlled by this bit of JavaScript (simplified):
$('.collapse-toggle').click(function(){
if ( sidebarIsVisible() ) {
hideSidebarDesktop()
} else {
showSidebarDesktop()
}
})
function showSidebarDesktop() {
...
var $collapseToggle = $('.collapse-toggle');
$collapseToggle.html("Collapse " + feather.icons['chevron-left'].toSvg());
...
}
function hideSidebarDesktop() {
...
var $collapseToggle = $('.collapse-toggle');
$collapseToggle.html("Episodes " + feather.icons['chevron-left'].toSvg());
...
}
Do you see the problem? In a non-English context, this button will display English text because the JavaScript dictates what the text of the button should be. Unfortunately, our templating could not touch the JavaScript, so we had to find a way to do this inside the HTML.
Solution: data attributes
I found that I could use HTML data attributes to encode the options using templating. I’ve encoded this using the jQuery template in JSfiddle where the button text changes depending on whether it’s on or off:
https://jsfiddle.net/zkamvar/wv9b30hc/1
In terms of how The Workbench uses it, I updated the template to include the data attributes:
<button role="button"
data-collapse="{{ translate.Collapse }}"
data-episodes="{{ translate.Episodes }}"
aria-label="{{ translate.CloseMenu }}"
alt="{{ translate.CloseMenu }}"
aria-expanded="true"
aria-controls="sidebar"
class="collapse-toggle">
<i class="search-icon" data-feather="x" role="img"></i>
</button>
and I updated the JavaScript to use those attributes to set the inner HTML.
$('.collapse-toggle').click(function(){
if ( sidebarIsVisible() ) {
hideSidebarDesktop()
} else {
showSidebarDesktop()
}
})
function showSidebarDesktop() {
...
var $collapseToggle = $('.collapse-toggle');
$collapseToggle.html($collapseToggle.attr('data-collapse')+ feather.icons['chevron-left'].toSvg());
...
}
function hideSidebarDesktop() {
...
var $collapseToggle = $('.collapse-toggle');
$collapseToggle.html($collapseToggle.attr('data-episodes') + feather.icons['chevron-right'].toSvg());
...
}
Conclusion
Is this the correct way of doing this? I’m not sure, but I know that hard-coding values into JavaScript is a worse way of going about it. Other methods could have been creating a JSON file with translation keys that could be read in when the document was loaded and saved into localStorage, but that would have required a lot more fiddly engineering on the JS side.
- Posted on:
- December 13, 2023
- Length:
- 3 minute read, 539 words
- Categories:
- example
- Tags:
- JavaScript HTML web
- See Also: