YesTonite - Global from day one: 44 languages and regional pricing in 63 countries
How we took a new dating app international from its first release: 44 languages, regional prices for 63 countries, and the same price on the website and in Google Play.
See YesTonite in your language
Part 2 of the YesTonite series. New here? Start with the main case study: YesTonite - Building a casual dating app for web and Android.
Project snapshot
|
Field |
Detail |
|---|---|
|
Project |
YesTonite: languages and regional pricing |
|
Type |
Client project · localisation, pricing and payments |
|
Client |
City Dev, the Netherlands (operator of YesTonite) |
|
Industry |
Online dating and social discovery (18+) |
|
Built |
2026 |
|
Timeline |
About one week to the live web app · Android in Google Play closed testing from mid-October 2026 |
|
Started |
6 October 2026 |
|
Team |
1 CTO, 1 developer, 1 designer |
|
Delivery |
Built end to end by techlead.fit |
|
Status |
Live on the web in 44 languages with prices for 63 countries · Google Play prices synced for the Android launch |
|
Stack |
Next.js 16 · Expo SDK 57 · Directus 12 · PostgreSQL · Stripe Checkout · Google Play Billing · Google Play Developer API |
The situation
YesTonite is a casual dating app for meeting people nearby, tonight. People go out everywhere, so the product could not be built for one market and translated later.
City Dev wanted two things from the first release: people should be able to use the app in their own language, and the optional paid extras should cost something that makes sense where they live.
A chat pack priced for the United States is cheap in Switzerland and expensive in Indonesia. A single global price is simple to build and wrong almost everywhere.
The challenge
Languages and prices both sound like content problems. In practice they are system problems.
-
Two platforms, one voice. The web app and the Android app had to show the same text in every language, including errors and e-mails.
-
Fair prices without a pricing team. Setting 63 country prices by hand for six products is slow and drifts out of date.
-
Two stores, one price. Stripe on the web and Google Play in the app each keep their own prices, with different tax rules.
-
Payment fees. In cheaper countries the smallest products cost less than it takes to process them.
Decisions before code
The backend owns every price. Prices live in Directus. The website charges exactly what the backend says, the app shows what the backend says, and Google Play is synced from it. The browser or the app can never send a price.
Compute first, override when needed. Every country gets a computed price from a price index and exchange rates. Where City Dev wants a different number, they set an explicit price for that country in Directus Studio. Explicit prices always win.
Use a public, explainable index. Instead of guessing what people can pay, the index compares what two widely used subscription services charge in each country against their US price.
Prices include tax. The price a member sees is the price they pay. No surprise at checkout.
Write texts once. The app's locale files are the source. A script syncs them to the web app, so a text is never translated twice or differently.
Language and country are separate. The language follows the member's choice. The price follows where they are. Someone can read the app in one language and pay in the currency of the country they are in.
How it works
1. Pick the language
On the web, an explicit choice wins, then the browser's language on a first visit, then English. Search engines always get English at the plain address, and every language has its own URL. In the app, the member's language is stored on the account, so it follows them between web and Android.
2. Find the country
The country comes from the hosting edge, not from the browser. The web server signs the country before passing it to the backend, so a member cannot claim to be somewhere cheaper by changing a request.
3. Choose the price
For each product the backend takes the first match:
-
an explicit price for the buyer's country;
-
an explicit price for the currency of the buyer's region;
-
the US base price multiplied by the country's price index and the exchange rate, rounded to a natural price point;
-
the US price in US dollars for any country not on the list.
The index for each country is the average of the local-to-US price ratio of Netflix Standard and YouTube Premium. Euro countries with an index of 0.9 or higher use the US numbers in euros.
4. Hide what is too cheap to sell
Nothing is sold below a minimum charge of EUR 0.80, and never below the payment provider's own minimum for a currency. Below that, card fees take most of the payment. Those products are not shown to that buyer, on the web or in the app. In Indonesia, for example, the smallest offer is the 5-chat pack. Purchases that already happened are always honoured.
5. Charge
On the web, Stripe Checkout receives the amount at payment time. There are no products to maintain in the Stripe dashboard, and a price change in Studio applies to the next checkout. In the app, Google Play Billing handles one-time products (chat packs) and subscriptions (the weekly and monthly Hide my profile plans).
The build
Pricing data. Five collections in Directus: the chat products, the plans, explicit prices per country, price regions with their index and rounding rule, and currencies with exchange rates. A research file holds the index data for all 63 countries and seeds the computed prices. Rows edited by hand in Studio are marked as manual and are never overwritten by a later reseed.
Google Play price sync. Google Play keeps its own price per product and country. A script reads the live prices from the backend and from Google Play, shows a preview per product and country, and writes the changes only when told to apply them. A second run should show nothing left to change.
Tax needed care. In almost every country, Google Play prices include VAT, so the Play price equals the website price. In the United States and Canada, Play adds sales tax at checkout. There, the script sets the Play price to the website price divided by an average tax rate, so buyers pay about the same on both. Existing subscribers keep their price; moving them is a deliberate step in Play Console.
Languages. 44 languages in the web app and the Android app, 45 on the backend for e-mails and notifications, and the blog in 43. A check runs over every locale file and fails when a key is missing anywhere. Prices are formatted in the member's language and the country's currency.
Testing and validation
-
The language check runs over all locale files on every change.
-
Browser tests load the site in different languages in a real headless Chrome and check routing and texts.
-
Stripe checkout was tested against a local fake Stripe and against Stripe test mode.
-
The Google Play sync has a dry run that prints every target price before anything is written.
-
Google Play purchases were tested with license testers, which are recorded at zero amount.
Outcome
YesTonite went live on the web in 44 languages with prices for 63 countries. The Google Play prices match the website for the Android launch.
City Dev changes prices in one place. Changing one country is one row in Studio. Changing a whole country's level is one index value. Changing every country at once is the US base price. Then one command brings Google Play in line.
What we learned
A price index beats guesswork. Using what people already pay for well-known subscriptions gave defensible starting prices for 63 countries, and Studio overrides handle the exceptions.
The two stores are never really the same. Different tax rules, different currencies in some countries and different fees mean "the same price" has to be defined carefully, then enforced by a script, not by memory.
Small payments need a floor. Without the minimum charge, the cheapest products in cheaper countries would have cost more to process than they earned.
One text source saves weeks. Syncing locale files from the app to the web removed a whole class of mismatches.
Why this is a techlead.fit case study
Going international is usually planned for "later", when changing it means rebuilding checkout and every screen.
Here we made the decisions up front: one source of truth for prices, a computed baseline with manual overrides, tax-inclusive prices, a minimum charge, and one text source for both platforms. The result is a product that City Dev can run in 63 countries without a pricing team or a translation workflow per platform.
Decide where the truth lives.
Compute what you can.
Override what you must.
Keep the stores in sync automatically.
The YesTonite series
-
Part 1: YesTonite - Building a casual dating app for web and Android
-
Part 2: Global from day one: 44 languages and regional pricing in 63 countries (this post)
-
Part 6: A growth engine without an ad budget, and the first round
Looking to launch in more than one market?
If your product needs to work in several languages and countries, the pricing, tax and payment decisions are much cheaper to make before development than after launch.
At techlead.fit, we define where prices and texts live, how they reach every platform, and what has to be validated before you open a new market.
Relevant services: CTO Sprint · MVP Build