Sample 01
Payment Failed
Context: The user taps Pay Now and the payment fails. Their single biggest fear in this moment: "Did I just get charged?"
✕ Before
⚠️ Error
Transaction failed. Please try again later.
There is no cause, no next step, and the only button is a dead end. Worst of all it never answers the money in question. Almost always, the user leaves the app to check their bank balance, and the sale is lost.
✓ After
Payment didn't go through
Don't worry, no money was taken. Please verify your card or check your balance.
Why it works
- Reassure first. "No money was taken" answers the top fear before anything else. No anxiety, no leaving the app.
- Plain cause, no blame. The next instruction explains without making the user feel stupid or accused.
- Two real exits. Primary action assumes the method was the problem (most common fix); secondary lets the confident user retry. No dead-end "OK."
- Title states the fact, not "Error." "Payment didn't go through" is understood in one glance; "Error" lets the user assumes what went wrong.
Pembayaran belum berhasil
Tenang, saldo kamu tidak terpotong. Coba verifikasi kartumu atau cek saldomu.
Coba metode lain Ulangi
"Belum berhasil" (not yet successful) instead of a hard "gagal" keeps the door open and feels less fatal. "Kamu" holds the warm, peer tone Indonesian shoppers expect; "Tenang" does the reassurance work "Don't worry" does in English.
Sample 02
Empty Cart
Context: The user opens their cart and it's empty. An empty screen is a missed moment, it can be a wall or a doorway.
✕ Before
Your cart is empty.
A full stop. It describes a state and offers nothing to do next.
✓ After
Your cart's still empty.
Saw something you liked earlier? Let's add here.
Why it works
- "still empty" reframes emptiness as temporary, not a failure.
- It adds action words. The action word "add here" recovers intent the user already showed; "Browse deals" gives a fresh start. Both point to a path forward.
- One primary action. Empty states should make the next tap obvious, not offer a menu.
Keranjang kamu masih kosong
Ada barang yang sempat kamu lirik? Yuk tambahkan sekarang.
"Sempat kamu lirik" (that you glanced at) is casual and natural in Indonesian e-commerce, more human than a literal "yang kamu suka." "Promo" outperforms "deals/diskon" as the word Indonesian shoppers actually search and tap.
Sample 03
Notification Priming
Context: A new user finishes signup. The OS notification prompt is one-shot. If they tap "Don't Allow," it's very hard to recover. So we earn the yes before the system asks.
✕ Before — firing the raw system prompt cold
"Shopee" Would Like to Send You Notifications
No reason given, no purpose known. Most users reflexively decline which causes lost opportunity.
✓ After - a priming screen first
Want first dibs on flash sales?
Turn on notifications and we'll ping you when your favorite items are on sale and when your order's on the move. No spam, promise.
Why it works
- Asks in value terms, not permission terms. "First dibs on flash sales" is a benefit while "send you notifications" is a chore.
- Names the two things users actually want: items on sale and order updates.
- "No spam, promise" pre-empts the real objection.
- "Maybe later" keeps the door open. Declining here doesn't burn the system prompt, so we can ask again at a better moment (e.g. right after they place an order).
Mau tahu duluan kalau ada flash sale?
Nyalakan notifikasi, nanti kami kabari pas barang incaranmu turun harga dan pas pesananmu jalan. Bebas spam, janji.
"Tahu duluan" (know first) captures the FOMO that drives Indonesian flash-sale culture better than a literal translation. "Nanti aja" is the natural casual decline softer and more honest than a formal "Lain kali."
Sample 04
Inline Form Validation: Wrong Phone Number
Context: The user is typing their shipping address at checkout and leaves the phone field in a bad format. A confusing error here stalls the single most important flow in the app.
✕ Before
❌ Invalid input.
Which field? Why invalid? How do I fix it? It tells the user they're wrong without telling them how to be right.
✓ After - inline, under the phone field
Add your number so the courier can reach you (e.g. 0812 3456 7890)
Why it works
- Guidance is clear. Says whats needed, not what's wrong.
- Gives the reason ("so the courier can reach you"). It's easier for people to follow rules they understand.
- Shows the format with a real example instead of describing it. An example parses faster than "must be 10-13 digits."
- No red "Invalid" shouting unless the user actively submits a broken value.
Isi nomor hp kamu biar kurir gampang menghubungi (contoh: 0812 3456 7890)
"Biar kurir gampang menghubungi" uses everyday "biar/gampang" instead of formal "agar/mudah" — it matches how Indonesians actually speak and keeps checkout feeling frictionless rather than bureaucratic.
Sample 05
Variations of Order - Status Notifications
Context: A push series across the order lifecycle. The craft here is consistency and rhythm so the messages should feel like one voice guiding you through every step.
| Stage | Before (flat) | After (voice + value) |
|---|---|---|
| Order confirmed | Your order has been placed. | Order's in! 🎉 We're letting the seller know. We'll ping you when it ships. |
| Shipped | Your order has been shipped. | On its way! Your order just left the warehouse. Track it any time from My Orders. |
| Out for delivery | Your order is out for delivery. | Almost there! Your courier's out with your order today. Keep your phone close. |
| Delivered | Your order has been delivered. | Delivered ✅ Your order is in your hand safely. Tap to rate it and help other shoppers. |
| Delayed | Your order is delayed. | Running a little late. Your order's held up in transit, we're on it. New estimate: {date}. |
Why it works
- One arc, one voice. Each message picks up where the last left off, so the series feels like a person, not a system.
- Every message earns the next open by adding a small piece of value like a track link, a heads-up, a new estimate.
- The delay message leads with honesty and ownership ("we're on it") and ends with the one thing the user wants: a new date. Bad news handled well builds more trust than good news.
- Emoji used sparingly and only where the moment is genuinely positive (confirmed, delivered). Never on the delay to avoid raising fear or uncertainty.
| Tahapan | Sebelum (datar) | Setelah (voice + value) |
|---|---|---|
| Pesanan masuk | Pesananmu sudah diterima. | Pesanan masuk! 🎉 Penjual akan menyiapkan pesananmu. Tunggu info pengirimannya ya! |
| Paket dikirimkan | Paketmu sudah dikirimkan. | Paket dalam perjalanan! Paket dalam proses pengiriman. Cek status paket di menu Pesanan Saya. |
| Paket di perjalanan | Paketmu sedang diantar. | Pesanan hampir sampai! Kurir sedang dalam perjalanan. Cek hp kamu secara berkala. |
| Terkirim | Pesananmu sudah tiba. | Terkirim ✅ Paket sudah diterima dengan aman. Klik di sini untuk memberikan penilaian. |
| Terlambat | Pesananmu mengalami keterlambatan. | Pengiriman terkendala. Paketmu mengalami kendala pengiriman, kami pantau secara berkala. Estimasi tiba: {tanggal}. |
Sample 06
Search: no results
Context: The user searches for something with no match. A blank "0 results" is the fastest way to lose a shopper who came with intent.
✕ Before
No results found for "{query}".
A dead stop that blames the search. 50:50 chance the user bounces and leaves the app.
✓ After
No result for "{query}", yet
Check the spelling, try a more general word, or browse what's close below.
Why it works
- "yet" softens the miss and hints the catalog grows.
- Three concrete recovery moves (spelling, broaden, browse) cover the real causes of a zero-result search.
- It still offers product, not just advice. Phrase "related items" keeps the shopping going instead of ending the session.
Belum ada yang pas untuk "{query}"
Coba cek ejaan, pakai kata yang lebih umum, atau lihat barang serupa di bawah.
"Yang pas" (something that fits) is warmer and more natural than a literal "hasil" (results). Indonesian shoppers respond to "barang serupa" as more concrete and familiar over an abstract "rekomendasi."
Sample 07
Voucher Won't Apply: Constraint Feedback
Context: The user pastes a voucher code and it's rejected. A hot moment. They expected to save money but failed. Vague copy here feels like a bait-and-switch.
✕ Before
Voucher invalid.
"Invalid" could mean expired, wrong code, minimum not met, or wrong category. The user can't tell if they made a typo or if Shopee tricked them.
✓ After - inline under the voucher field
This voucher needs a minimum order of Rp150,000. Shop Rp32.000 more to unlock Rp50,000 off.
Why it works
- Names the exact reason (minimum not met) instead of a catch-all "invalid."
- Does the math for them ("Shop Rp32,000 more") and reframes the gap as an unlock. A clear reward after a task.
- Turns a stall into momentum with a forward action.
For an expired or wrong code, the same pattern applies: state the real cause "This code expired on {date}", so the user knows clearly what the issue is.
Voucher ini berlaku untuk minimal belanja Rp150.000. Belanja Rp32.000 lagi buat dapat potongan Rp50.000.
State the amount gap clearly and tell the next step directly to avoid ambiguity. Then "buat dapat" keeps the reward casual and motivating.
Sample 08
Cancel Order - Destructive Action Confirmation
Context: The user taps "Cancel Order." This is destructive and often irreversible. UX writing's job here is to make sure they mean it without nagging the people who do.
✕ Before
Are you sure you want to cancel?
"Are you sure?" carries no information. "Yes/No" forces the user to map the question back to the action which is basically a classic source of mis-taps. And it states no consequence.
✓ After
Cancel this order?
Your refund of Rp200,000 goes back to ShopeePay within 24 hours. This can't be undone.
Why it works
- Buttons name the action, not Yes/No. The user can act on the buttons alone without re-reading the question. Also the safe choice ("Keep my order") is phrased to feel like the easy default.
- States the real consequence: refund amount, where it goes, how long. Exactly what the user needs to know.
- "This can't be undone" sets the right weight. An honest friction on an irreversible action.
Mau batalkan pesanan ini?
Dana Rp200.000 akan dikembalikan ke ShopeePay dalam 24 jam. Tindakan ini tidak bisa dibatalkan.
"Dana … dikembalikan" is the standard, trusted phrasing for refunds in Indonesian e-commerce. Money and irreversible actions are where users want clarity and seriousness, not jokes.
Sample 09
OTP Verification: entry + error
Context: The user is logging in and must enter a one-time code sent by SMS. OTP screens are pure friction, the writing's only job is to remove confusion fast.
✕ Before
Enter OTP.
Wrong OTP. (on error)
"OTP" is jargon many users don't know. "Wrong OTP" doesn't say what to do: re-type? Resend? Wait?
✓ After
Enter the 6-digit code
We sent it by SMS to •••• •••• 7890. It can take a few seconds to arrive.
(on wrong entry) That code doesn't match. Double-check the SMS, or resend a new one.
Why it works
- "6-digit code" instead of "OTP": plain language anyone understands.
- Shows where it went (masked number) so the user knows which phone to check, and sets the expectation that it's not instant.
- The resend timer in button to help the user navigates the help in an obvious way so the user won't get stuck.
- The error says what to do, not just that they're wrong.
Masukkan 6 digit kode
Kode sudah dikirimkan lewat SMS ke •••• •••• 7890. Butuh beberapa detik untuk sampai.
"Kode 6 digit" drops the English "OTP" jargon for clarity. "Belum dapat?" (haven't got it yet?) is friendlier and more natural than a literal "Tidak menerima?"
The system behind the samples
Tone of Voice Mini-Guide
A one-page voice guide with a set of rules for the team to write by.
Our voice in three words
Clear Warm On your side
| We are… | We're not… | In practice |
|---|---|---|
| Clear | Clever for its own sake | Say "Payment didn't go through," not "Oops, something went wrong!" |
| Warm | Overfamiliar | "kamu," contractions, the occasional emoji but never slang that excludes |
| On your side | Corporate | Reassure first ("no money was taken"), explain the why, and always give a next step |
Three house rules
- Lead with the user's biggest question. In a payment error, that's "is my money safe?" Then answer it in the first line.
- Never a dead end. Every message has a way forward. No dead-end "OK."
- Say what to do, not what went wrong. "Add your number so the courier can reach you" beats "Invalid input."
Words we use / words we avoid
| Use | Avoid | Why |
|---|---|---|
| didn't go through | failed / error | softer, less final |
| no money was taken | (silence) | answers the top fear |
| Try another method | OK | a real next step |
| Running a little late | DELAYED | honest without alarming |
Spec/concept work by Elmi Rahmatika. It's created for portfolio purposes, not shipped Shopee copy. Written in English and Bahasa Indonesia.