DEV Community

Cover image for How We Created a Multi-site Setup for a Vehicle Marketplace: Part 1

Posted on • Updated on • Originally published at

How We Created a Multi-site Setup for a Vehicle Marketplace: Part 1

This article was originally published on Codica Blog.

Today, we want to present you the first part of the article, where we describe the way we developed the first multi-vendor vehicle marketplace in Africa. The platform provides its users with an opportunity to trade new and second-hand cars, trucks, and motorbikes.

The client had a primary goal to develop a global marketplace solution which would serve many geographic areas of Africa and South America. The mission is to connect customers with private owners and dealers.

At the beginning of the development, there were certain platform websites and our goal was to develop more and extend the marketplace size. Taking into account all the benefits and drawbacks, our development team concluded to develop a multi-site setup.

Up to date, there are 89 marketplace websites functioning separately, and the number continues to grow. Let's get this show on the road and see how we achieved this result.

What is a multi-site setup?

To be precise, a multi-site setup is a composite of independent websites. Here you can see a comparison table of multi-domain and multi-site solutions to understand why we have chosen this option.

Aspect Multi-domain Multi-site
Domain Subdomains Different domains
Database One Separate database for each site/domain
Content management Via one panel Via multiple panels for every site/domain
Monitoring Easy Medium
Maintenance Hard Medium
Stability Low High
Loading speed Low High

As we can see, one of the main benefits of multi-domain option is easy content management, but our development team chose a multi-site solution. The main reason for this is that it provides undeniable benefits stability and maintenance.



Goal: Proper organization and maintenance of the setup

Shortly, our development team has adopted a composite configuration system that gives an opportunity to edit the settings of each site and its logic. It is stashed on a single Git repository. Thus, it stores all the websites’ files with the templates and CSS files of the colour scheme.

Initially, each site has its own file with the setting of supported currencies and other information.

site_name: NewSite
continent: Continent
country_code: AO
currencies: { 'AOA': 'AOA', 'USD': 'USD' }
Enter fullscreen mode Exit fullscreen mode

Moreover, we have an opportunity to edit and manage all the sites at a time. We have such a possibility because there exists a main HTML/CSS template which is common for all the sites.

Here you can see an example of a common template CSS file. In case we edit the variables in this file, then all the websites’ colour scheme will be changed accordingly. This solution simplifies the configuration adjustment pretty much.

$color-1: #0052b4;
$color-1-hover: darken($color-1, 20%);
$color-2: #cb032b;
Enter fullscreen mode Exit fullscreen mode



If you run the marketplace in many countries, then the language issue becomes challenging. It includes creating translations, as well as updates and control.

Our primary goal was to take into account all the probable languages for a particular geographic area and integrate them for the best user experience. In order to achieve this, we have created a fully-fledged localization system that supports any country and multiple languages in these countries.

Goal 1. Provide fast and easy localization

Each site provides several languages, including English and local ones. To modify the languages, we have adopted Ruby gems that allow solving many problems in a short period of time as they simplify the development process.

Our development team has used i18n gem (set by default), to provide internationalization solution for the marketplace.

Furthermore, with the help of this gem, we have an opportunity to localize such information as date and time, validation notification, etc.

Goal 2. Monitor and manage multiple translation files

At this stage of development, we perfectly realized that it is of fundamental importance for the customer to easily manage the translations.

We have developed our tailored solution: we have created an import and export function, with the help of mentioned gems (i18n, i18n- tasks) and Custom CSV import and export tasks.

Let’s define the way it works:

  • The client adds translations to a Google Document.
  • This document syncs with our Git repository .yml files and therefore updates in our locale files.

Moreover, we are able to update Google Doc by changing the data in the code base.

Below you can see a Google Doc screen with the ‘key’ value from the code base and its consequent values in different languages.

Moving right along, we have faced a new issue: in order to adjust the whole platform localization, we need to modify the locale files quite often.

To solve this problem, our development team has modified and advanced the above mentioned Custom CSV import and export tasks function. Now it gives us a possibility of writing data to separate files at one time that provides a boost in localization administration.

Let’s see how this mechanism works:

def csv_export(_opts = {})
    translations_by_path = {}
    router =, write: i18n.config['csv']['export'])


    all_locales = i18n.locales << i18n.base_locale

    all_locales.each do |locale|
      router.route(locale, i18n.data_forest) do |path, nodes|
        translations_by_path[path] ||= {}
        translations_by_path[path][locale] ||= {}
        nodes.leaves do |node|
          translations_by_path[path][locale][node.full_key(root: false)] = node.value
      import_locale_in_csv(locale, translations_by_path)
Enter fullscreen mode Exit fullscreen mode

Now, we have an opportunity to track and verify all the files within Google Drive API and export locales to Google Drive. There we can edit and manage the translations flatly and handily.

When we receive an email notification, we run an import function to get the data from Google Drive using this method:

  def csv_import_from_google
    translations = []
    file_links.each do |_local, token|
      csv ="{token}/export?format=csv"))
      import_locale(csv, translations)
    end! I18n::Tasks::Data::Tree::Siblings.from_flat_pairs(translations)

 def import_locale(csv, translations)
    CSV.parse(csv, headers: true) do |row|
      key = row['key']
      next unless key

      get_locale_from_files(row).each do |locale|
        raise "Locale missing for key #{key}! (locales in app: #{locales} / locales in file: #{row.headers.inspect})" unless row.key?(locale)
        translations << [[locale, key].join('.'), row[locale]] if row[locale].present? && skip_locale?(key)
Enter fullscreen mode Exit fullscreen mode


Goal: Create conversion system for multiple currencies

Considering a lot of locations and currencies, it was of utmost importance to support the platform users with a convenient currency conversion system.

Marketplace customers needed the opportunity to determine prices in their local currency or USD, and quickly convert them. This task was a bit complicated because the number of sites and currencies is pretty large.

For each website, we set several currencies, including USD and the local ones. A user is able to set the price in any listed currency, as well as convert it into any other available one.

To implement efficient currency conversion at the platform, we have adopted Money Gem.

Then, we have faced another problem: currency rates change quickly.

To cope with this issue, we have developed a special automated service that updates currency rates every hour. We have adopted Cron Jobs on our server - it is a tool for scheduling certain recurring actions in the Rails application.

Also, our development team has configured the tool to send a request to Money Gem API hourly, receive the data on current currency conversion rates, parse the data and then complete it on the platform.

Let’s see how this service works:

class UpdateCurrenciesRate < BaseService

  def call
    base_rate = ExchangeRate.base_currency
    Settings.currencies.to_h.values.each do |currency|
      next if currency == base_rate
      update_rate(base_rate, currency)


  def update_rate(base, target)
    currency_rate = JSON.parse(open(generate_link(base, target)).read)
    save_rate(base, target, currency_rate.values.first)
  rescue StandardError => error
    puts error

  def generate_link(base, target)

  def save_rate(base, target, rate)
    Money.default_bank.add_rate(base, target, rate)

Enter fullscreen mode Exit fullscreen mode


Here we presented you the first part of the article covering such challenges of marketplace development like configuration and localization (languages, currencies). The second part is coming soon and there we will tell you about testing and deployment challenges.

This solution is time- and cost-effective because you have a possibility to administrate the platform websites easily and efficiently. The configuration system supports you with enhancing a single website and obtaining the results over the whole platform.

Stay tuned and read the full article version here: How We Created a Multi-site Setup for a Vehicle Marketplace.

Top comments (0)