Let us add localization to the electron tutorial app. We will look at translating both in the main and renderer process.
I tried some of the already existing npm-packages that helps with translating your app. They were good but didn’t feel 100% right to me. Some of them used .po-files, some of them were too advanced. So I decided to write a simpler one on my own. This is just a matter of taste. If you find another way that suits you better you should do it like that instead.
The localization script will store translations in .js files with a json-structure. It will use english as a fallback language, and try to use the users language by default. If no translation is found the original phrase will be sent back to be used.
1.Adding the script
Create a new folder in the project called translations. Create a new .js file and copy the contents below and save the file as i18n.js (why i18n?)
const path = require("path")
const electron = require('electron')
const fs = require('fs');
let loadedLanguage;
let app = electron.app ? electron.app : electron.remote.app
module.exports = i18n;
function i18n() {
if(fs.existsSync(path.join(__dirname, app.getLocale() + '.js'))) {
loadedLanguage = JSON.parse(fs.readFileSync(path.join(__dirname, app.getLocale() + '.js'), 'utf8'))
}
else {
loadedLanguage = JSON.parse(fs.readFileSync(path.join(__dirname, 'en.js'), 'utf8'))
}
}
i18n.prototype.__ = function(phrase) {
let translation = loadedLanguage[phrase]
if(translation === undefined) {
translation = phrase
}
return translation
}
In the i18n constructor the script first checks if a language file for the current users locale exists. If it does that file is loaded. If the file does not exist the script loads the fallback language in en.js.
the __ function takes a phrase as an argument and checks if that is translated in the loadedLanguage. If it’s not it returns the phrase sent to the function.
This line takes care of loading app from either the main(mainmenu.js) or renderer(from index.html) process.
let app = electron.app ? electron.app : electron.remote.app
2.Adding translation files
Since i am from Sweden I will locate the tutorial app into Swedish. So I will create en.js and sv.js in the translations folder. To know what you should name your file so that the i18n script will find it and load it you can add this to main.js which will log the locale to the terminal(just add .js to it):
console.log(app.getLocale())
This is how the structure in the translation files should look. This is what en.js will look like:
{
"Edit": "Edit",
"Undo": "Undo",
"Redo": "Redo",
"Cut": "Cut",
"Copy": "Copy",
"Paste": "Paste",
"Delete": "Delete",
"Select all": "Select all",
"View": "View",
"Speech": "Speech",
"Start speaking": "Start speaking",
"Stop speaking": "Stop speaking",
"Actual size": "Actual size",
"Zoom in": "Zoom in",
"Zoom out": "Zoom out",
"Toggle fullscreen": "Toggle fullscreen",
"Window": "Window",
"Minimize": "Minimize",
"Close": "Close",
"Help": "Help",
"Learn more": "Learn more",
"About": "About",
"Services": "Services",
"Hide": "Hide",
"Hide others": "Hide others",
"Unhide": "Unhide",
"Quit": "Quit",
"Zoom": "Zoom",
"Bring all to front": "Bring all to front"
}
And this is what my sv.js looks like:
{
"Edit": "Redigera",
"Undo": "Ångra",
"Redo": "Gör om",
"Cut": "Klipp ut",
"Copy": "Kopiera",
"Paste": "Klistra in",
"Delete": "Radera",
"Select all": "Markera allt",
"Speech": "Tal",
"Start speaking": "Börja tala",
"Stop speaking": "Sluta tala",
"View": "Visa",
"Actual size": "Faktisk storlek",
"Zoom in": "Zooma in",
"Zoom out": "Zooma ut",
"Zoom": "Zooma",
"Toggle fullscreen": "Helskärmsläge",
"Window": "Fönster",
"Minimize": "Minimera",
"Close": "Stäng",
"Help": "Hjälp",
"Learn more": "Läs mer",
"About": "Om",
"Services": "Tjänster",
"Hide" : "Göm",
"Hide others": "Göm övriga",
"Unhide": "Visa alla",
"Quit": "Avsluta",
"Bring all to front": "Flytta fram alla"
}
3.Translating the menu (Main process)
As you might have guessed when reading the translation they look like menu items. So let us translate the menu that was added in the last tutorial.
Open up mainmenu.js that is located in the menu folder and add this require statement to the top of the file:
const {Menu} = require('electron')
const electron = require('electron')
const app = electron.app
var i18n = new(require('../translations/i18n'))
Now all we need to do is call i18n.__() for every menu item. These are what the first three looks like, here is the fully translated menu file.
const {Menu} = require('electron')
const electron = require('electron')
const app = electron.app
var i18n = new(require('../translations/i18n'))
const template = [
{
label: i18n.__('Edit'),
submenu: [
{
role: 'undo', label: i18n.__('Undo')
},
{
role: 'redo', label: i18n.__('Redo')
},
....
What you do is add the label: attribute to the menu item, which gets the translated value from i18n__().
4.Translating navigation menu in index.js (Renderer process)
Create a new file in assets/js/ called translations.js. In this one we’ll use jquery to set the translated strings to our html-markup.
Open index.html and add i18n int the list of scripts.
<!-- Scripts -->
<script>window.$ = window.jQuery = require('./assets/js/jquery.min.js');</script>
<script>window.i18n = new(require('./translations/i18n'));</script>
<script src="assets/js/jquery.scrollex.min.js"></script>
<script src="assets/js/jquery.scrolly.min.js"></script>
<script src="assets/js/skel.min.js"></script>
<script src="assets/js/util.js"></script>
<script src="assets/js/main.js"></script>
<script>
require('./assets/js/menu')
require('./assets/js/translations')
</script>
</body>
</html>
In translations.js it’s plain jquery to set the text of the elements that you wish to translate:
window.localization = window.localization || {},
function(n) {
localization.translate = {
menu: function() {
$('#welcome-menu').text(i18n.__('Welcome'));
},
welcome: function() {
$('#welcome .inner p').text(i18n.__('Hopefully this helps someone to get up to speed with electron.'));
},
init: function() {
this.welcome();
this.menu();
}
};
n(function() {
localization.translate.init();
})
}(jQuery);
This is just two translations that are added. You can see the full file here.
The app now looks like this when running it on an os set to Swedish language: