Number formatting is a frequently requested feature, and it is important for typesetting documents that require consistent numeric precision, localized formats, or scientific notation.
Recently, I noticed that most related discussions on GitHub and Discord are over a year old. Since I have a current need for more flexible number formatting, I decided to explore implementing this feature myself.
This post serves as a collection of resources and ideas; even if I am unable to complete the implementation or if it doesn’t meet expectations, I hope this summary will be useful for anyone interested in improving number formatting in Typst in the future.
Resources
GitHub issues
- Number formatting #3269
- Locale-aware number formatting #1093
- (Closed) Number formatting with trailing/leading zeroes #180
- Related: Convenient string formatting #2267
Discord thread
Universe packages
Standards & Conventions
- NIST guide for scientific writing
- Decimal separator(including thousand separator and radix character) wikipedia
Existing tools
What do we want
Basically:
- Format a float (or decimal) to a user-friendly string for display purposes.
- Automatically round to a specified precision, and optionally pad with trailing zeros if needed.
- Allow formatting style to be controlled via
show
andset
rules, specifically:- Customize the thousands separator and grouping size (e.g., not necessarily grouping by thousands).
- Customize the decimal separator.
- Optionally format numbers using SI prefixes (e.g., k for thousands, M for millions).
- Optionally format numbers using scientific notation.
- Provide an intuitive and easy-to-use interface.
(Many existing solutions, such as Python’s format strings, are overly complex and resemble small DSLs. They introduce nontrivial learning and memory costs while not being worth the learning effort.)
My Design
I am introducing a new element (currently called digits
, though the name is still open for discussion) to wrap decimal numbers. This element can then be styled and controlled via show
and set
.
All values that can be converted into a decimal will be automatically wrapped as digits
when directly inserted into the document, and will be affected by show
and set
rules (similar to how #type(x)
is currently controlled by raw
). Here’s a simple example of what it should be like:
#set text(lang: "en", region: "fr") // by default 1.00005e5 should be "10 000,5"
#(12345.6789) // This should be 12 345,6789
#set digits(grouping-marker: ".")
#(12345.6789) // This should be 12.345,6789
Progress
At present, the functionality for fine-grained control over the mentioned formatting behaviors is largely implemented. However, localization based on language and region is not yet handled, and documentation is still missing.
The source code is available here
Known Issues
- The fractional part of a number typically does not group digits, but my current implementation does not reflect this behavior.
- Decimals are not suitable for scientific values(e.g.
1e-50
) - The default formatting behavior should depend on
text.lang
andtext.region
, but defining a clear and appropriate standard is challenging. - Using a space as a grouping separator may break proper layout in right-to-left (RTL) writing systems.
- The current code quality is not very high, particularly in field naming and logic structure, and could benefit from further refinement.
Conclusion
I would love to hear your thoughts, suggestions, and questions—whether about the ideas discussed in this post or the current implementation itself.
Feedback on both the design and the code is very welcome!