A request starts with the invariant culture
What you'll see
Currency renders with the generic currency sign ¤ instead of a real one, thousands separate with a comma where the locale uses a space, or a date comes out in English on a site that is not. It looks like a format-string problem, and changing the format string does not fix it.
Measured on a page that had not set a culture:
docly.format(1234.5, "C"); // "¤1,234.50"
docly.format(1234.5, "N2"); // "1,234.50"
docly.format(new Date(2026, 0, 5), "D"); // "Monday, 05 January 2026" The same three calls after docly.setCulture(1044):
docly.format(1234.5, "C"); // "kr 1 234,50"
docly.format(1234.5, "N2"); // "1 234,50"
docly.format(new Date(2026, 0, 5), "D"); // "mandag 5. januar 2026" What's actually happening
A request does not inherit a culture from anywhere. Not from the site, not from the browser's Accept-Language header, not from the server's own locale. It starts invariant, which is why the currency sign is the placeholder ¤ rather than any particular currency — the invariant culture deliberately has no country.
docly.setCulture() sets it for the remainder of that request and affects numbers, currency, percentages and dates together. It accepts either an LCID or a culture name, and the two are equivalent:
docly.setCulture(1044); // LCID
docly.setCulture("nb-NO"); // culture name
docly.setCulture("en-US"); // -> "$1,234.50" Because the scope is the request and not the site, the call has to be reachable on every path that formats. A master page covers the pages it wraps and nothing else, and an API endpoint is not wrapped by anything — it needs its own call.
This is also why an apparently redundant setCulture() near the top of a working page is load-bearing. Removing it does not throw and does not blank the page; it changes the thousands separator, the currency sign and the month names in every format call below it.
What to do
1. Call docly.setCulture() before the first format call on any page or endpoint that formats a number, a currency, a percentage or a date.
#{
docly.setCulture(1044);
}# 2. Set it per entry point, not once per site. The scope is the request. Put it in the master for pages the master wraps, and repeat it in every API endpoint that formats.
3. Treat an existing setCulture() as load-bearing. It is easy to read as boilerplate and delete during a tidy-up. Removing it silently changes output that nothing tests.
4. Do not work around it by hand-assembling the string. Building "kr " + value.toFixed(2) gets the symbol right and the separators wrong, and it will not follow the culture when the site adds a second language.
5. Verify the output rather than the call. That the culture was set is not the interesting fact; what docly.format then produces is. See Use a scratch hash file to test Docly functions.