2026-08-12

A New Native i18n Layer for mORMot 2

Internationalization (aka i18n) is now part of the mORMot 2 core with the new mormot.core.i18n unit, introduced by PR #510 - and deeply rewritten and completed, as always. The goal is not to create yet another translation format, but to provide a small, fast and framework-native i18n layer for both Delphi and Free Pascal.

The basic principle is deliberately simple: the original English text is the translation key. For example, "Hello" can be translated to "Bonjour". If a translation is missing, the original text remains available as the natural fallback. This keeps application code readable and makes the translation tables straightforward to maintain.

One language, or all languages

The implementation is built around two complementary classes.

TLanguageFile represents one language. It contains a thread-safe dictionary mapping the original English text to its translation, together with optional date and date/time formatting settings. It can load translations from GNU gettext .po and .mo files, as well as INI, YAML and JSON variants.

TLanguageFiles is the application-level container: it manages all the TLanguageFile instances for all languages. It can load a complete directory of language files, expose the languages currently available, and provide a default language. Most importantly for server applications, the current language can be selected per thread.

This makes the model particularly natural for an HTTP server: load all translations once at startup, then call SetThreadLanguage() when a request starts, based for example on a cookie, URI parameter or user preference. Translation calls automatically use the language selected for the current request.

Of course, if you don't like the per-thread context trick, you could just store the proper TLanguageFile in your server-side-connection state instead. And since TLanguageFiles.Language lookup is very fast, so you can just store the per-connection TLanguage enumeration if you prefer.

A native .m18n deployment format

TLanguageFiles also goes beyond simply loading individual translation files.

Because it inherits from TObjectStore, the complete collection of language tables can be persisted as a compressed binary representation. mORMot defines .m18n as its canonical extension for this format, and the same binary data can be embedded directly into an executable as a resource.

This creates a useful separation between development and deployment. Translators can work with familiar .po or .json files, while developers can package all the application's translations into one compact .m18n file or executable resource. A multilingual application can therefore be deployed with a single self-contained translation database instead of a collection of separate files.

resourcestring support on both Delphi and FPC

Another important aspect is the support for Delphi/FPC resourcestring values.

TLanguageFiles.TranslateResourceStrings() can translate the whole executable resourcestring table using the same language dictionaries. The original resourcestring values are expected to be English text and therefore naturally become the translation keys.

This is implemented according to the actual RTL mechanisms of each compiler rather than pretending that Delphi and FPC handle resourcestrings identically. On Delphi, mORMot redirects LoadResString() through its own translation/cache mechanism. On FPC, resourcestring values are kept in per-unit writable tables, which mORMot updates through objpas.SetResourceStrings() once when the method is called. The language can consequently be changed at runtime, with the original English values restored when no translation language is selected.

This is a particularly nice feature for existing Delphi and FPC code: applications can continue using the familiar resourcestring mechanism, while the actual translations are managed by the same TLanguageFiles infrastructure used by the rest of the application.

Designed to fit mORMot

The translation engine can also be connected to several existing mORMot hooks, including Mustache translation tags, framework caption translation and language-specific date/time formatting. GNU gettext .po and .mo remain supported, so existing translation workflows can be reused rather than replaced.

The difference from many traditional Delphi/FPC localization libraries is therefore mainly integration and scope. It is not intended to be a large desktop form-localization framework. Instead, it provides a small common translation engine in the mORMot core, usable by both Delphi and FPC and especially convenient for multilingual server applications.

The resulting workflow is simple:

  1. load all languages once
  2. select the language per request/thread
  3. translate through the shared tables
  4. optionally deploy everything as one compressed .m18n resource

This combination of gettext compatibility, native resourcestring support on both Delphi and FPC, per-thread language selection, framework integration and a compact multi-language .m18n deployment format makes mormot.core.i18n a useful addition to the mORMot 2 core.

See mormot.core.i18n.pas for the implementation unit.

Show me some code

Here is an extract from the regression tests:

resourcestring
  MyResource = 'Hello';

  // each TLanguageFiles instance has an internal name
  langs := TLanguageFiles.Create('ProjectV1');
  try
    // manuall add some entries, here as JSON
    CheckEqual(langs.AddFromJson(lngFrench, '{"Hello":"Bonjour"}'), 1);
    CheckEqual(langs.AddFromJson(lngChinese, '{"Hello":"NiHao"}'), 1);

    // per-thread selection
    TLanguageFiles.SetThreadLanguage(lngFrench);
    Check(TLanguageFiles.ThreadLanguage = lngFrench);
    Check(langs.Current = langs.Language[lngFrench]);
    u := 'Hello';
    Check(langs.Translate(u));
    CheckEqual(u, 'Bonjour');

    // fallback to DefaultLanguage when no thread language is set
    TLanguageFiles.SetThreadLanguage(lngUndefined);
    langs.DefaultLanguage := lngChinese;
    Check(langs.Current = langs.Language[lngChinese]);
    u := 'Hello';
    Check(langs.Translate(u));
    CheckEqual(u, 'NiHao');

    // Mustache {{"text}} channel end-to-end
    TLanguageFiles.SetThreadLanguage(lngFrench);
    m := TSynMustache.Parse('{{"Hello}} {{name}}!');
    CheckEqual(m.Render(_ObjFast(['name', 'world']), nil, nil,
      langs.TranslateString), 'Bonjour world!');

    // trigger globally resourcestring translations
    Check(MyResource = 'Hello');
    langs.TranslateResourceStrings(lngFrench);
    Check(MyResource = 'Bonjour');
  finally
    langs.Free;
  end;

More is available in the TTestCoreProcess._i18n method.

Two Breaking Changes

During the development of this feature, two breaking changes were introduced.
We would like to document them:

  1. The TObjectStore.fReader field is now a PFastReader and not a TFastReader. You may need to fix compilation by writing fReader^ in some places.
  2. The { { " English text } } Mustache tag now properly escape into clean HTML - as it should have been. But if you already escaped manually the text in your resources, you may need to use plain UTF-8 text instead.

2026-08-07

A 2 GB/s XML Parser for Delphi and Free Pascal: SAX, Streaming and DOM Without the Usual Trade-offs

XML may no longer be fashionable, but it is still everywhere.

Scientific datasets, configuration files, industry standards, legacy systems, government data, document formats and countless integration interfaces continue to produce XML. And when XML documents become large, the choice of parser can make a surprisingly large difference.

For mORMot 2, we have recently written a new XML parser, TXmlParser, with a slightly different goal from the traditional XML libraries found in the Delphi and Free Pascal ecosystems. It started from an AI-driven pull request from zen010101, then it was fully rewritten by hand for performance and enhanced to support several API modes.

Instead of choosing between a low-level SAX parser and a convenient DOM parser, the same parser provides several levels of access:

  • a zero-allocation SAX/pull API;
  • a streaming/navigation API for selecting only the parts of the document that matter;
  • a hybrid SAX/DOM API, where selected subtrees can be materialized as TDocVariantData;
  • and a full XML-to-TDocVariant DOM representation, when that is what the application actually needs.

The interesting part is that the performance remains very high at all these levels.

Using the 24 MB nasa.xml reference document from the University of Washington XML repository, the raw parser reaches about 2.1 GB/s, while the full DOM conversion reaches about 424 MB/s. Selective streaming reaches about 1.6 GB/s, and once the DOM exists, traversing it through the new TDocVariantData.Product() enumerator reaches about 7 GB/s.

There is also an interesting memory optimization: enabling dvoInternNames reduces the complete DOM footprint from approximately 60 MB to 40 MB, with essentially no performance penalty.

The complete benchmark is shown below.

Continue reading

2026-07-06

Object Pascal Is Still a Serious Contender for High-Performance REST Servers

In this article, we will deep-dive into the architecture that lets our Open Source mORMot 2 library compete with (and sometimes outperform) nginx, Kestrel, and raw C++ implementations.

The library offers several HTTP servers.
The mormot.net.server unit contains the historical mORMot 1 web servers - mainly THttpServer (thread-pool for HTTP/1.0 short requests + one thread per kept-alive HTTP/1.1 connection), and THttpApiServer (using the efficient http.sys kernel API on Windows).
But since mORMot 2, our mormot.net.async unit is our genuine cross-platform, modern async implementation.

Continue reading

2026-06-02

TPipeStream: A High-Performance In-Memory Pipe for Delphi and FPC

The latest version of mORMot 2 introduces TPipeStream, a new TStream descendant designed to efficiently transmit data between two threads.

At first glance, the class looks deceptively simple: one thread writes data using Write(), another thread reads it using Read().
Behind this familiar interface lies a fast, lock-protected ring buffer with blocking semantics, making it suitable for streaming large amounts of data between independent producer and consumer threads.

Continue reading

2026-05-19

Delphi Linux & macOS Support for mORMot 2

We are pleased to share that mORMot 2 now offers initial compatibility with Delphi's Linux and macOS x86_64 compilers for console/server applications.
This effort originated from GitHub issue #442.

Thanks to community testing and contributions, core units now compile and run on these platforms.

Continue reading

2026-03-03

Rencontre entre Pascaliens à Nantes

Si vous êtes dans l'Ouest de la France, vous êtes les bienvenus pour une escale Delphi/Lazarus le jeudi 9 avril fin d'après midi à Nantes!

J'y serais avec toute l'équipe dev de Tranquil IT pour partager des idées, et certainement quelques petites choses plus solides (ou liquides) ! :)

Continue reading

2026-01-14

mORMot 2 Generics Scalability

In the world of Delphi development, generics have become a cornerstone for writing flexible, type-safe code. However, as projects grow in complexity—with hundreds or even thousands of generic specializations—the scalability of these implementations can become a critical bottleneck.
If you're dealing with large-scale applications, these insights could save you hours of build time and frustration.

Continue reading

2026-01-12

New mORMot 2.4 Release

After more than one year since our latest release, it is time for a new release!

Continue reading

- page 1 of 52