When you are a bilingual person and you want to begin in the blogging world using Ghost, you could come up with the next question: ¿How can I write posts on several languages using this great open source tool which is Ghost?

That was the question which I asked myself when I was starting up this blog due to I wanted to leverage the different audiences who exist in both languages on Spanish as well as on English in my professional area which is Software Engineering, and also these audiences were benefited from my high-value content that I was going to share.

The first thing I did like a good self-taught person was to seek in Google:

¿How could I add i18n to Ghost?

¿How could I write a post in several languages using Ghost?

But ... the results weren't what I was expecting, I found this Github issue [Epic] Ghost and i18n it was open since 2014 ... just looking at the date when it was opened made me feel a bad feeling, however I kept reading comment after comment of that issue and I realized that many Ghost community members were discussing different ways to add i18n to Ghost, especially what tools to use, what patterns to follow and so forth.

Finally I realized that issue/epic was for adding support to many languages in Ghost's admin, not for overall posts.

As last resource I decided to join to Ghost's slack and ask people which have more time and experience working with Ghost than me. The answer was that still there wasn't an official support for adding i18n to our posts written in Ghost. So I decided to take necessary actions to resolve this issue and make this possible.

Creating a plugin from scratch was going to take a lot of time, because I had to analyze in a certain low level the way Ghost was built and then based on it develop the plugin, but quickly I discarded that option because of the factor time.

The second option I came up with was the one which I implemented and is the next: Create two Ghost instances in different ports and with Nginx proxies redirecting to one instance or another, then create a key/value object to save both Ghost instances URLs and adding a button with the option to change the current language.

I'm going to explain you at a very detailed level the process to achieve this second i18n option, for this sample I've used a DigitalOcean container specifically the standard 5$ monthly one on top of Ubuntu 16.04 64 bits and NodeJs v6.9.2.

We'll have to install zip and wget packages which we'll use to download Ghost, to install Ghost is recommended to place it in /var/www/ghost route, so let's first create /var/www/ directory where we'll download Ghost last version from its Github repository:

This step is only for those who have selected the 512-RAM DigitalOcean container, for those who are not following this post with that container are free to skip this step and go ahead to: Installing Ghost's npm dependencies

Before installing Ghost's npm dependencies we have to enable Swap memory, we do this to avoid the operating system running out of memory and stops the npm install process.

Swap is an area on a hard drive that has been designated as a place where the operating system can temporarily store data that it can no longer hold in RAM. Basically, this gives us the ability to increase the amount of information that our server can keep in its working "memory", with some caveats. The space on the hard drive will be used mainly when space in RAM is no longer sufficient for data.

Right now, our file is created, but our system does not know that this is supposed to be used for swap. We need to tell our system to format this file as swap and then enable it. Before we do that though, we need to adjust the permissions on our file so that it isn't readable by anyone besides root. Allowing other users to read or write to this file would be a huge security risk.

We can lock down the permissions by typing:

sudo chmod 600 /swapfile

Verify that the file has the correct permissions by typing:

ls -lh /swapfile

The previous command should have printed something like this:

-rw------- 1 root root 2.0G Feb 26 17:52 /swapfile

As you can see, only the columns for the root user have the read and write flags enabled.

Now that our file is more secure, we can tell our system to set up the swap space by typing:

sudo mkswap /swapfile

Our file is now ready to be used as a swap space. We can enable this by typing:

sudo swapon /swapfile

We can verify that the procedure was successful by checking whether our system reports swap space now:

sudo swapon -s

Our swap has been set up successfully and our operating system will begin to use it as necessary.

Ghost's configuration file should be located at /var/www/ghost/config.js. However, no such file is installed with Ghost. Instead, the installation includes config.example.js, so let's copy the example configuration file to the proper location.

Be sure to copy instead of move so we have a copy of the original configuration file in case we need to revert our changes.

sudo cp config.example.js config.js

Open the file for editing:

sudo nano config.js

You have to change the value of url to whatever your domain is (or you could use your server's IP address in case you don't want to use a domain right now). This value must be in the form of an URL. For example, http://example.com/ or http://45.55.76.126/. If this value is not formatted correctly, Ghost will not start.

Also change the value of host in the server section to 0.0.0.0.

Save the file and exit the nano text editor by pressing CTRL+X then Y and finally ENTER.

The next step is to install Nginx. Basically, it will allow connections on port 80 to connect through to the port that Ghost is running on. In simple words, you would be able to access your Ghost blog without adding the :2368. Then we'll add the corresponding proxies for each language.

Let's install it with the following command:

sudo apt-get install nginx

Next, we will have to configure Nginx by changing our directory to /etc/nginx and removing the default file in /etc/nginx/sites-enabled:

cd /etc/nginx/
sudo rm sites-enabled/default

We will create a new file in /etc/nginx/sites-available/ called ghost and open it with nano to edit it:

In case you have Ghost running, press Ctrl + C or Command + C to stop it, Now we'll create other Ghost instance, to do this, let's change our directory to /var/www/ghost and once there we'll create a new folder called en and we move all our Ghost content into it:

mkdir en
mv * en

Let's create another folder called es and we'll copy all content of our folder en into es:

mkdir es
cp -r en/* es/

We'll use our en folder for English language and es folder for Spanish. We need to modify some files into our folder es for adding the multi language support, first thing we'll modify is Ghost configuration file:

cd es/
sudo nano config.js

We have to modify the url property into production object, as well as the server property, in url property we'll add http://your_server_ip/es/blog and in the server property we'll modify the port property changing it to 2369. We do this aiming that our URLs of each Ghost instance follow a SEO good practice which is loading its content identified by country's name by the standard ISO 3166-1, as well as when starting up both instances each one run in a different port.

Save the file and exit the nano text editor by pressing CTRL+X then Y and finally ENTER.

We'll add the HTML necessary part to show a button to change user current language, we have to move into casper folder which is Ghost's default theme content/themes/casper/:

cd content/themes/casper/

We'll add the following HTML code just below of our top right Menu button into <nav></nav> tag in index.hbs, post.hbs, tag.hbs and author.hbs files, we'll do this modifications in es folder for Spanish as well as in en folder for English:

Next step is for adding corresponding styles to properly show the change language button in desktop computers, tablets as well as in smartphones.

To do this let's open the screen.css files located in es/content/themes/casper/assets/css/screen.css and en/content/themes/casper/assets/css/screen.css we'll add the next style code just below the animations section:

We already have the styles and the button, the only missing thing would be adding the functionality and it is what we're going to do, we'll need to modify the next files default.hbs which is located in content/themes/casper/default.hbs and index.js located in content/themes/casper/assets/js/index.js.

The first file we'll modify is default.hbs where we're going to add just before </body> tag and after script tags:

Let's open our preferred web navigator and go to Ghost instance route which we have defined:

http://my_domain_name_or_server_ip/es/blog/

We'll successfully see our Spanish instance is properly working...

Now let's move in to our administration section "admin", it'd be:

http://my_domain_name_or_server_ip/es/blog/admin

Once there we'll see Ghost's initial settings to set down our access credentials, fill out those fields and continue to Ghost's dashboard.

Let's click in Ghost default post "Welcome to Ghost", and then click in settings icon located at the top right corner, it'll allow us to change our first post URL. Change the value in "Post URL" field to bienvenido-a-ghost.

This way we have successfully set down our first post which is working with i18n :)

As we go creating more posts, we have to register its respective URLs in the objects which contain our URLs for both languages. This way our i18n functionality will have a map with URLs available and also it'll know which one to use depending of current active URL.

For example, let's assume that this post you are reading is new and as URL we have assigned it adding-internationalization-i18n-to-ghosts-posts for English and agregando-internacionalizacion-i18n-a-los-posts-en-ghost for Spanish, then we have to add it into our URLs map we already have:

With that we'd have registered our 2 new URLs for both languages and instances.

That'd be all for this post, I hope you find this useful to be able to have your Ghost posts in different languages, any doubt don't hesitate to ask me through the comments, as well as additional consults, opinions or overall things you think I should improve.