Multi-lingual Ghost, static translations, CDN, amazing site speed

I’m very happy to present the results of a great collaboration with an Italian community for crypto, SpazioCrypto, publishing educational content in multiple languages, starting with Italian and English.

It is actually two Ghost instances running on a virtual server, with content flowing from the Italian site. The amazing theme is Pitch.

It does not use Weglot or Localise, no monthly subscription fees. With static translations and image delivery accelerated with Bunny CDN, the site loads extremely fast, currently 1.4 seconds with both GA4 and iubenda cookie-consent activated.

Content is published in Italian and translated to English using DeepL translator over API when a scenario in Make triggers on “Post Published”.

Some challenges…

Tags must match between sites. Created a separate lookup-table for tag translation in Make using local data store.

URL structure should the slug and URL be the same on all sites, or should the URL be translated? If it’s the same you can switch between language versions, or is SEO more important, so the URL matches H1 headline?

Internal links in posts have to be rewritten to match the site-URL when translating. Make has a search-replace module which solves this.

Member sync Sites are separate, so when a user signs up on EN site they have to be added as members to the main Italian site. Make syncs subscribers between sites - triggers when a new member registers.

The footer with Categories uses locale files to adapt to site language in Ghost settings.

There is also an ad-slot which can be switched between placeholder image or embed-code from an ad-server.

Sites live at

Let me know of any questions, happy to build more multi-lingual sites - contact Magnus Helander

/magnus

7 Likes

Interesting idea of creating 2 separate Ghost instances.

On a funny note, this one reveals unedited output from GPT:

I’m also collaboratively writing articles and these sentences are just pain in the neck and instantly reveal AI-generated content :slight_smile:

The source content, italian, is organic - possibly with AI assist, the translation uses DeepL and does a really good job overall.

Hello, the integration looks great!

The Deepl API mentioned is open-sourced?

DeepL’s Free API account will let you translate 500k characters / month

Hey, thanks for your reply! And sorry but I’m not talking about pricing (I’ve Deepl pro API from a year).

Hi Magnus,

Congratulations, good job! One question: Is it semantically true to put same alternate link tags to every pages?

<link rel="alternate" hreflang="it" href="https://www.spaziocrypto.com">
<link rel="alternate" hreflang="en" href="https://en.spaziocrypto.com">

I think every url should point its translated one, not always the homepage.

Than you @muratcorlu You’re right, it should point to the translated page, our problem is, what could possibly be the URL of that page? Client choose to SEO-optimize the URLs, so they are localized,

https://www.spaziocrypto.com/guida-web3/cos-e-una-dapp-applicazioni-decentralizzate/

becomes…

https://en.spaziocrypto.com/web3-guide/what-is-a-dapp-decentralised-applications/

…at translation time, depending on what DeepL chooses. So… we don’t know the target language URL.
It gets more interesting in say Japanese with i18n, where Ghost creates the URL …

https://ja.spaziocrypto.com/web3gaido/dapptoha-fen-san-xing-apurikesiyon/

And, Ghost can not store metadata with the posts, so we can’t stash the translated canonical URL’s with the posts.

The solution was to build a URL lookup table, we store the translated URL’s in a DB table when translating and can look up the URL’s for nine languages.

Also wrote a simple API to request all language versions. Deployed as a Cloudflare Worker it only takes 200ms to look up.

These will be dynamically written to the header as rel="alternate" on DOMcontent loaded, right now we’re finalizing 7x more languages and getting closer to launch, so it’s on the to-do list.

1 Like