← All entries

Simsalasim · Brand

Formatting & Locale Standards

Slugformatting-locale
TierTier 01
Statelive
UpdatedApr 19, 2026

Simsalasim Formatting & Locale Standards

Purpose: Single source of truth for how Simsalasim displays numbers, currency, percentages, dates, times, data units, and relative time across every locale we serve. Governs all user-facing surfaces: app UI, emails, SMS, push, receipts, marketing.

Status: v1.0 — Active from 2026-04-16 Owner: Design System Related ticket: Define Global Country/Locale Formats


0. Design-Time vs Runtime (Critical)

This spec governs two contexts. Do not confuse them.

Runtime (Production)

Each user sees formatted values rendered per their system locale. A de-DE user sees 45,78 € and 16.04.2026. An en-US user sees $45.78 and 4/16/2026. The profiles in §5 are the Runtime reference.

Design-Time (Figma)

All Figma design files render formatted values in de-DE format only — regardless of UI text language. This convention exists because:

  • UI text in Figma is kept in English so the international team can read every screen
  • Germany is our v1 launch market, so Euro prices are expected in stakeholder reviews
  • Rendering multiple locales per frame is unmaintainable and C-Level-confusing

Consequences:

  • Audits against Figma check only de-DE formatting
  • English text is not a violation
  • Figma ≠ what a non-German user will actually see at runtime
  • When localizing at runtime, the locale profiles in §5 override the Figma de-DE values

Single rule for designers: if your design shows a number, price, percentage, date, time, or data unit, format it per the de-DE profile in §5. Never mix locales within a Figma file.


1. Scope & Principles

In scope

  • Number formatting (integers, decimals)
  • Currency formatting (EUR, USD, GBP primary; non-Euro EU currencies documented for display)
  • Percentage formatting
  • Date formatting (primary, secondary, numeric)
  • Time formatting (24h / 12h)
  • Data-unit formatting (GB, MB, MIN, SMS)
  • Relative time ("Yesterday", "Wednesday", time-only)
  • Date ranges

Out of scope

  • UI translation (handled by copywriter skills)
  • RTL language layout
  • Implementation code (separate dev ticket)

Guiding principles

  1. Strict locale conformance. We follow CLDR/ICU conventions. Any deviation must be explicitly listed under House Style Overrides (§6) with a written reason.
  2. Two date variants, not three in UI contexts. Primary (written out) by default. Secondary (abbreviated) only when space is tight. Numeric (DD.MM.YYYY) reserved for data/technical surfaces (invoices, CSV exports, API-rendered data).
  3. Relative time is contextual. Applies only to activity/calls/notifications — never to billing, plans, passes, or legal artefacts.
  4. Consistency over cleverness. If in doubt, match the CLDR default via Intl.NumberFormat / Intl.DateTimeFormat.
  5. Currency always shows 2 decimals. No dropping of .00 or ,00 — ever.

2. Supported Locales

Three tiers based on product readiness.

Tier 1 — Detailed spec (primary markets)

LocaleRegionLanguageCurrency
en-USUnited StatesEnglishUSD
en-GBUnited KingdomEnglishGBP
de-DEGermanyGermanEUR

Tier 2 — Major EU markets (Euro, well-defined patterns)

LocaleRegionLanguageCurrency
de-ATAustriaGermanEUR
fr-FRFranceFrenchEUR
fr-BEBelgiumFrenchEUR
nl-NLNetherlandsDutchEUR
nl-BEBelgiumDutchEUR
it-ITItalyItalianEUR
es-ESSpainSpanishEUR
pt-PTPortugalPortugueseEUR
en-IEIrelandEnglishEUR

Tier 3 — Other EU (CLDR fallback, display-only until activated)

fi-FI, sv-SE (SEK), da-DK (DKK), pl-PL (PLN), cs-CZ (CZK), sk-SK, hu-HU (HUF), ro-RO (RON), bg-BG (BGN), hr-HR, sl-SI, el-GR, el-CY, lt-LT, lv-LV, et-EE, en-MT, mt-MT, de-LU, fr-LU.

Tier 3 locales follow their CLDR defaults. Display bugs in Tier 3 → fix against CLDR, do not invent house variants.


3. Core Formatting Rules (Tier 1)

3.1 Numbers

Ruleen-USen-GBde-DE
Decimal separator..,
Thousands separator,,.
Integer example1,2341,2341.234
Decimal example12,345.6712,345.6712.345,67

Rule: Always render the thousands separator, even for 4-digit integers. CLDR permits omitting below 10,000 in some locales; we always include for reading consistency.

3.2 Currency

Ruleen-USen-GBde-DE
Symbol$£
PositionBefore amount, no spaceBefore amount, no spaceAfter amount, NBSP (U+00A0) before symbol
Decimal placesAlways 2Always 2Always 2
Small example$9.99£9.999,99 €
Large example$1,234.56£1,234.561.234,56 €
Whole example$10.00£10.0010,00 €

Never strip trailing zeros. 10 € is incorrect; 10,00 € is correct.

3.3 Percentage

Ruleen-USen-GBde-DE
Symbol positionAfter, no spaceAfter, no spaceAfter, NBSP before symbol
Default decimals1 for precision contexts, 0 for countsSameSame
Precision example56.7%56.7%56,7 %
Whole example50%50%50 %

Rule: 1 decimal when precision matters (data-usage bars, accuracy readouts). 0 decimals for rounded quota displays.

3.4 Dates

Three forms. Choose by context, not by medium.

Primary (default — written out): Use everywhere unless space is constrained.

en-USen-GBde-DE
March 7, 20267 March 20267. März 2026

Secondary (abbreviated): Only when space is tight (narrow list column, compact card, activity log older entries).

en-USen-GBde-DE
Mar 7, 20267 Mar 20267. Mär 2026

Tertiary (numeric, data/technical only): Tables, invoices, CSV exports, API responses rendered as-is.

en-USen-GBde-DE
3/7/202607/03/202607.03.2026

3.5 Time

Ruleen-USen-GBde-DE
Clock12h24h24h
Separator:::
Example9:23 PM21:2321:23
AM/PMAfter time, space, uppercase AM/PM

3.6 Data units

English unit abbreviations in all locales (house-style override, §6). Spacing and position follow locale number-unit convention.

UnitMeaning
GBGigabytes
MBMegabytes
KBKilobytes
MIN or minMinutes (call quota)
SMSText messages
Ruleen-USen-GBde-DE
Spacing number↔unitSpaceSpaceNBSP
Example25.3 GB25.3 GB25,3 GB

3.7 Date ranges

en-USen-GBde-DE
Mar 7 – 14, 20267 – 14 Mar 20267. – 14. März 2026
Mar 7 – Apr 3, 20267 Mar – 3 Apr 20267. März – 3. April 2026

Separator: en dash with NBSP on both sides (). Never hyphen -.


4. Relative Time

4.1 Thresholds

Replace absolute timestamps with relative expressions in this tiered order. Compare by calendar day, not elapsed hours.

Age of eventFormaten-USde-DE
Same calendar dayTime only9:23 PM21:23
Previous calendar dayYesterday, <time>Yesterday, 9:23 PMGestern, 21:23
2–6 days ago<Weekday>, <time>Wednesday, 9:23 PMMittwoch, 21:23
7+ days ago, same yearSecondary dateMar 77. Mär
7+ days ago, different yearSecondary date + yearMar 7, 20257. Mär 2025

No "Last week" bucket. After 6 days → absolute date.

Day/weekday translations (Tier 1):

en-USen-GBde-DE
TodayTodayTodayHeute
YesterdayYesterdayYesterdayGestern
MondayMondayMondayMontag
TuesdayTuesdayTuesdayDienstag
WednesdayWednesdayWednesdayMittwoch
ThursdayThursdayThursdayDonnerstag
FridayFridayFridayFreitag
SaturdaySaturdaySaturdaySamstag
SundaySundaySundaySonntag

4.2 Where relative time applies

SurfaceRelative time?Rationale
Call historyCasual recency
Activity logCasual recency
Notifications listCasual recency
In-app messages / chat timestampsCasual recency
Billing history / invoices❌ Always absoluteLegal/financial precision
Plan expiry / validity❌ Always absolute primary dateUser needs exact date
Next billing date❌ Always absolutePlanning context
Receipts❌ Always numeric dateAudit trail
Recap reports❌ Always absoluteReporting context
Pass start/end❌ Always absoluteCommercial validity

Rule of thumb: "When did this happen?" → relative. "When does this matter?" → absolute.


5. EU Locale Matrix

All Tier 2 + Tier 3 locales following CLDR strict. Examples: amount 12345.67, date 2026-03-07, time 21:23.

LocaleNumberCurrency%Date (primary)Time
de-DE12.345,6712.345,67 €56,7 %7. März 202621:23
de-AT12.345,67€ 12.345,6756,7 %7. März 202621:23
de-LU12.345,6712.345,67 €56,7 %7. März 202621:23
fr-FR12 345,6712 345,67 €56,7 %7 mars 202621:23
fr-BE12.345,6712.345,67 €56,7 %7 mars 202621:23
fr-LU12 345,6712 345,67 €56,7 %7 mars 202621:23
nl-NL12.345,67€ 12.345,6756,7%7 maart 202621:23
nl-BE12.345,67€ 12.345,6756,7%7 maart 202621:23
it-IT12.345,6712.345,67 €56,7%7 marzo 202621:23
es-ES12.345,6712.345,67 €56,7 %7 de marzo de 202621:23
pt-PT12 345,6712 345,67 €56,7%7 de março de 202621:23
en-IE12,345.67€12,345.6756.7%7 March 202621:23
fi-FI12 345,6712 345,67 €56,7 %7. maaliskuuta 202621.23
sv-SE12 345,6712 345,67 kr56,7 %7 mars 202621:23
da-DK12.345,6712.345,67 kr.56,7 %7. marts 202621.23
pl-PL12 345,6712 345,67 zł56,7%7 marca 202621:23
cs-CZ12 345,6712 345,67 Kč56,7 %7. března 202621:23
sk-SK12 345,6712 345,67 €56,7 %7. marca 202621:23
hu-HU12 345,6712 345,67 Ft56,7%2026. március 7.21:23
ro-RO12.345,6712.345,67 RON56,7%7 martie 202621:23
bg-BG12 345,6712 345,67 лв.56,7%7 март 2026 г.21:23
hr-HR12.345,6712.345,67 €56,7%7. ožujka 2026.21:23
sl-SI12.345,6712.345,67 €56,7 %7. marec 202621:23
el-GR12.345,6712.345,67 €56,7%7 Μαρτίου 202621:23
el-CY12.345,6712.345,67 €56,7%7 Μαρτίου 202621:23
lt-LT12 345,6712 345,67 €56,7 %2026 m. kovo 7 d.21:23
lv-LV12 345,6712 345,67 €56,7 %2026. gada 7. marts21:23
et-EE12 345,6712 345,67 €56,7%7. märts 202621:23
en-MT12,345.67€12,345.6756.7%7 March 202621:23
mt-MT12,345.67€12,345.6756.7%7 ta'' Marzu 202621:23

Matrix notes:

  • nl-* and de-AT put € before the amount with space. All other Euro locales put € after with NBSP.
  • fi-FI and da-DK use . (period) as time separator — strictly CLDR.
  • hu-HU and lv-LV use year-first date order.
  • Non-Euro EU currencies: SEK (kr), DKK (kr.), PLN (), CZK (), HUF (Ft), RON (RON/lei), BGN (лв.).
  • Relative time in Tier 2/3 follows the same threshold logic — use Intl.RelativeTimeFormat + CLDR weekday names.

6. House Style Overrides

Explicit deviations from strict locale conformance. Each with written reason.

OverrideRuleReason
Data unit abbreviationsAlways English (GB, MB, MIN, SMS) — never translated (not Go, not Mo, not min in French Min.)Brand consistency; tech convention; eSIM users recognize these internationally
Currency decimalsAlways 2, never droppedVisual consistency; avoids mixed 10 € + 9,99 € in the same list
Thousands separator for 4-digit integersAlways rendered (1.234 in de-DE, not 1234)CLDR permits either; we pick the separator variant for reading consistency
AM/PM casing (en-US)Uppercase AM/PMCLDR permits both; uppercase is more legible in small UI
No "Last week" relative bucketAfter 6 days → absolute secondary dateCleaner thresholds, no ambiguity of what "last week" means across weekday boundaries

Any future override must be added to this table with a written reason and approved by Design.


7. Examples — Correct vs Incorrect

Every Recap Email inconsistency (the trigger for this spec) mapped to the fixing rule.

Incorrect (current)Rule violatedCorrect (de-DE)
25.3 GB in a de-DE email§3.1 decimal separator25,3 GB
98,3 €§3.2 currency 2 decimals98,30 €
45,78€ (no space)§3.2 currency spacing45,78 €
23.8 % (period in de-DE)§3.1 + §3.323,8 %
7 Mar in de-DE email§3.4 date locale7. März 2026 (primary) or 7. Mär (secondary)
March 7, 2026 in de-DE email§3.4 date locale7. März 2026
10 €§3.2 currency decimals10,00 €
1234,56 € in de-DE§3.1 thousands separator (override §6)1.234,56 €
9:23pm (no space)§3.5 AM/PM spacing9:23 PM
21.23 in de-DE§3.5 time separator21:23
last week in call history§4.1 no "last week" bucketMar 7 (absolute secondary)
Yesterday at 9:23 PM§4.1 formatYesterday, 9:23 PM

8. Implementation Notes

For developers implementing this spec in frontend/backend.

  • Frontend: Prefer Intl.NumberFormat(locale, {style: ''currency'', currency: ''EUR'', minimumFractionDigits: 2, maximumFractionDigits: 2}) and Intl.DateTimeFormat(locale, {dateStyle: ''long''}) — both resolve to CLDR defaults that match this spec.
  • Secondary (abbreviated) dates: {dateStyle: ''medium''} in Intl.DateTimeFormat.
  • Tertiary (numeric) dates: {dateStyle: ''short''}.
  • Relative time: Use Intl.RelativeTimeFormat(locale, {numeric: ''auto''}) for the localized words. Threshold logic (§4.1) is ours — implement on top of the library.
  • Always set locale explicitly. Never rely on browser/system default. Pass the user''s locale preference from their profile.
  • Backend / email templates: Server pre-formats every number, price, date, and time to a string using the user''s locale before rendering into the template. Templates never do formatting themselves.
  • House overrides (§6) must be applied after Intl.*:
    • Force 2 decimals on currency (Intl respects minimumFractionDigits)
    • Swap data-unit translations back to English (Intl.NumberFormat with style: ''unit'' returns Go in French — override to GB)
    • Enforce thousands separator below 10,000 (use useGrouping: ''always'')

9. Self-Check Checklist

Run before shipping any user-facing artefact (email, screen, notification, SMS, push).

  • Every number uses the correct decimal and thousands separators for the locale
  • Every currency amount shows exactly 2 decimal places
  • Currency symbol position and spacing match the locale
  • Percentage spacing follows the locale (NBSP for de-DE, none for en-US)
  • Date uses primary (written) form unless space demands secondary
  • Time is 12h for en-US, 24h elsewhere
  • Data units use English abbreviations (GB, MB, MIN, SMS)
  • Relative time is used only in activity/calls/notifications — not billing/plans/receipts
  • No mixed conventions within the same surface (e.g., no 25.3 GB next to 45,78 € in a German email)
  • Date range uses en dash with NBSP, not hyphen

10. Maintenance & Distribution

  • Canonical source of truth: This Brand Bible entry (content_pages.slug = ''formatting-locale''), served via brand-read?slug=formatting-locale.
  • Notion mirror: Auto-synced from Supabase via simsalasim-sync-mirror. Do not edit in Notion.
  • Figma mirror: Manually maintained "Formatting & Locales" page in Simsalasim Design System (Figma). Update in same PR/session as Supabase.
  • Change process: All edits originate in Supabase. Notion mirrors automatically; Figma is updated by Design in the same session.
  • Skills that must fetch this spec when producing or reviewing content:
    • simsalasim-copywriter-pro-s
    • simsalasim-copywriter-basic-s
    • simsalasim-copywriter-partner-s
    • simsalasim-email-builder
    • simsalasim-qa-review-s

Change Log

  • 2026-04-16 — v1.0 — Initial spec. Triggered by Recap Email audit. Covers en-US, en-GB, de-DE at Tier 1 detail; all EU27 at Tier 2/3 reference. Relative-time rules added per scope extension.