Shopify Markets: one shop in thirteen countries
Shopify Markets in practice at XXLplant: one online shop in thirteen countries and nine languages. Translating through the API, range and price per country, VAT and feed labels for Performance Max.

XXLplant sells large houseplants and outdoor plants, from a Strelitzia two and a half metres tall to an olive tree for the patio. It is ZAZU's own online shop, and it delivers to thirteen countries: the Netherlands, Belgium, Luxembourg, Germany, Austria, France, Ireland, Italy, Spain, Denmark, Sweden, Poland and Croatia.
All of that runs on a single Shopify store, without a warehouse of its own. This article is written for developers and PPC specialists who want to take an online shop across the border. It covers how we set up Shopify Markets, how we monitor the product range and the price per country automatically, and how the product feed for each country is tied to Performance Max.
Why large plants suit selling across Europe
Nobody takes a two-metre plant home in the boot of their car, and the garden centre round the corner rarely stocks one. People who want a plant like that search for it online, in Lyon just as much as in Utrecht. In Germany, the large species are searched for three to five times as often as in the Netherlands, while there are few specialist shops.
The plants come straight from specialist growers in the Netherlands, Belgium and southern Europe, through the Everspring network. A grower only prepares the plant once the order is in, and ships it in a box made for plants, with a cold or heat pack in extreme weather. For every product and every country, Everspring supplies a delivery time, a shipping price and a purchase price. Those three figures drive everything that follows.
How to set up a Shopify store in multiple languages
All languages sit under one web presence in Shopify Markets, with a subfolder per language: /de/, /fr/, /it/ and so on, with Dutch at the root. Shopify sets the hreflang tags and the canonical for each language itself. A German visitor lands on große Zimmerpflanzen and a Dutch visitor on grote kamerplanten. It is the same collection with a translated handle.
Technically, that single web presence belongs to one market. At XXLplant that happens to be the Ireland market. If you delete that market or change its web presence, you break the URLs of every language at once. That was the first thing we wrote into the store's documentation.
Shopify's own translation app only translates two languages automatically by default. That is why we write translations directly with the Admin GraphQL mutation translationsRegister. For each field, you first fetch the digest of the source text with translatableResources. That digest has to be sent exactly as it is, or Shopify rejects the translation. If the Dutch source changes later, the translation is marked as outdated until you register it again with the new digest.
Three things do not get translated along with the rest:
- Metafields on a collection do not appear among that collection's translatable fields. You translate them per metafield, on the metafield's own GID.
- Links in an article body still point to the Dutch URL after translation, so a German reader ends up on a Dutch page. On 5 October we changed the collection links in 267 translated article versions so they point to the collection in the reader's own language.
- Text that only exists as a default value in a section's schema cannot be translated. That is covered in detail in The Shopify GitHub integration in practice.
Why you need Shopify Markets as well as translations
In Shopify, the language only decides what you read. Whether you can order, at what price and with what shipping costs is handled by a market. XXLplant has twelve of them. Each market has its own catalogue, and a catalogue has two parts: a price list and a publication that decides which products the customer sees.
A few things in the Admin API cost us time:
marketCreatecreates a market as a draft. You then have to set it to active separately withmarketUpdate.publicationUpdaterejects batches of a hundred products. Batches of twenty-five with a short pause work.autoPublish: trueon a catalogue automatically publishes every new product in that market, even when it cannot be delivered there.
That last one went wrong with a Danish order. A customer ordered a Strelitzia nicolai of 170 centimetres, and the grower refused the order because that plant does not ship to Denmark. The cause was autoPublish on the Danish catalogue. Since then autoPublish is switched off everywhere, and a script decides which product belongs in which market.
How a script updates the product range per country every morning
Every morning at 7:15 a sync runs across all catalogues. A product belongs in a market if it is active, is published in the online shop, and Everspring can deliver it to every country in that market. Deliverable means the delivery time is not set to disabled and there is a shipping price. A bundle inherits its deliverability from its components.
A product is only added with a margin of at least 15 per cent, and is only removed if it cannot be delivered or makes a loss. The script has two brakes. If there are more than sixty changes in one market, it stops for that market, and if Everspring returns data for fewer than three hundred variants, it stops altogether. That way a fault at the supplier cannot quietly take half the shop offline.
The result is a product range that differs per country. Spain gets 328 products, Sweden 321 and Germany 397. The largest sizes drop out in some countries, because no grower can deliver them there.
How to calculate the price per country in Shopify Markets
Shipping costs differ per country and per product. A box with a 250-centimetre Strelitzia costs €34 within the Netherlands and €75 to Germany. A fixed surcharge for other countries is therefore too low on one product and far too high on another. That is why XXLplant calculates a price floor for each product.
The floor is the cost price in the most expensive country of the market, divided by one minus the target margin, multiplied by the VAT. The cost price is the purchase price plus the grower's shipping. The target margin goes down as the amount goes up: 25 per cent, 21 per cent above €40 profit and 18 per cent above €80. The script then rounds it to a price ending in .95. It includes the shipping costs the customer pays, and it also checks whether two items in one order still earn enough, because the grower often sends them in one box.
Every morning the script raises any price that is too low to the floor, as a fixed price in that market's price list. It does not touch the Dutch prices. On 3 October we also recalculated all foreign prices once, this time including the customer's shipping, and prices that were too high came down. Germany has its own lower limit, because the price there has to compete with the large German plant shops. Since then a 190-centimetre Strelitzia nicolai costs a German customer €154.95 plus €39.95 shipping. Before, that was €229.95 plus €69.95.
What the OSS threshold means for your VAT
With VAT, you need to watch the threshold for distance sales. As long as your combined sales to consumers in other EU countries stay below €10,000 a year, you charge Dutch VAT, and Shopify then does that on a Danish order as well. Above it, you charge the VAT of the customer's country and declare it through OSS (the EU One-Stop Shop). The price formula has to change with it, because the rate for Denmark and Sweden is 25 per cent.
How feed labels per country connect to Performance Max
For PPC, the market is the unit. The product feed goes to Merchant Center through the Simprosys app, with its own feed label per market in the form EUR_<market-id>, DKK_<market-id> or PLN_<market-id>. Each Performance Max campaign is tied to the feed label of its market. That way the German campaign contains exactly the German range, with the German price and in German.
That has a consequence you need to know about. When we split Germany off from Austria and Luxembourg on 3 October, we kept the existing market as Germany and moved Austria to a different market. Had we done it the other way round, the German campaign would have got a new feed label and would have had to rebuild its learning history on an empty label.
Three other things from the feed:
- The feed title follows a fixed order: name, size, botanical name and feature, separated by vertical bars. The size comes second, because a Shopping card cuts the title off and with large plants the size is what makes the difference.
- Besides the feed itself, Merchant Center picked up 1,271 products by crawling the site, with different IDs and translated titles. 63 per cent of those were disapproved. That automatically discovered source is now hidden, so there is one source per product.
- The feed uses the product's SEO title and SEO description. An improvement to the SEO text therefore also ends up in Shopping after a sync.
Four decisions to make before you sell internationally
If you want to take your own online shop across the border, you will run into the same questions. Here they are, in the order we ran into them.
- Decide per country whether you only translate or actually sell there. A Spanish page without a Spanish market attracts visitors who cannot check out.
- Let the range per country follow from what you can actually deliver, and let a script keep track of it. A product that never arrives costs you more than a product that is not listed.
- Calculate the price per product and per country from your cost price including shipping. A fixed surcharge for other countries is almost never right.
- Settle the structure of your markets before you build campaigns. Moving a feed label costs a campaign its learning history.
How XXLplant also tests SEO, 3D models and a plant doctor is in Shopify SEO and 3D models with AR in practice.
A translated online shop only becomes an international online shop once someone in that country can check out, at a price that adds up.
Want to know whether your online shop is ready for a second or third country? Then we'll look at your markets, your feed and your pricing together.
What this means for your business.
Answer three questions and you will know where it makes most sense to start.
How we build online shops