Part 7 · 2 chapters · ~12 min
Accessible APIs and Docs, Internationalisation on the Server
API documentation that people with screen readers and low bandwidth can use, plain-language errors, examples in several languages, server-side internationalisation (locale negotiation, ICU messages, plural rules), money, dates and time zones, names and addresses across cultures, and Unicode handling.
12
Docs and errors people can use
API consumers include developers using screen readers, slow connections and translation tools. Accessible docs use real headings and semantic HTML, text alternatives for diagrams, code samples as text (never images), sufficient contrast, keyboard-navigable consoles, and pages that work on 3G. Errors should say what went wrong and what to do, in plain language, with a stable machine-readable code:
code
{ "type": "https://api.example.com/errors/insufficient-funds",
"title": "Insufficient funds", "status": 422, "code": "insufficient_funds",
"detail": "The account has ₦4,200.00 available; the transfer needs ₦5,000.00.",
"instance": "/v1/transfers/tr_81" } // RFC 9457 problem details13
Internationalisation on the server
code
// locale negotiation, ICU plurals and currency formatting
const locale = negotiate(req.headers['accept-language'], ['en-NG', 'yo-NG', 'ha-NG', 'fr']) ?? 'en-NG';
const fmt = new Intl.NumberFormat(locale, { style: 'currency', currency: 'NGN' });
fmt.format(4200) // "₦4,200.00" in en-NG
msg(locale, 'pending_transfers', { count: 3 }) // ICU: "{count, plural, one {# pending transfer} other {# pending transfers}}"
// store instants in UTC, render in the user's zone; Nigeria is Africa/Lagos (UTC+1, no DST)
new Intl.DateTimeFormat('en-NG', { timeZone: 'Africa/Lagos', dateStyle: 'medium', timeStyle: 'short' }).format(new Date());
// Unicode: normalise before comparing (NFC), count graphemes not bytes for limits
'Adéọlá'.normalize('NFC'); [...new Intl.Segmenter('yo', { granularity: 'grapheme' }).segment(name)].length| pitfall | practice |
|---|---|
| first name / last name fields | one full-name field plus an optional preferred name; no assumptions about order |
| names with diacritics (Yoruba tone marks) | UTF-8 end to end, NFC normalisation, no ASCII-only validation |
| addresses | free-form lines; postcodes optional (many Nigerian addresses have none) |
| money | integer minor units plus ISO 4217 currency; format only at the edge |