Jadpo is experimental and not ready for production. These docs follow the compiler's main branch as of 30 Oct 2026. What's built · Install 0.1.0
jadpo
Reference: Configuration and time

Configuration and time

Packages' text in an app

On this page

To show a package's or Jadpo's message in a locale, Jadpo looks first in your app's root wording for that locale, then in the owner's own. For an in-progress locale it then tries the regional sibling, in the same order, and then the default locale, in the same order.

Your app's supported locales must be complete for every package and Jadpo message the app can show. So an English-only package in an app that supports French is an error until the root file supplies the French.

Here an app supporting English and French installs a package that words its text in English only, calls its message, and words the French itself:

example
/// Shown under a postcode field.public type PostcodeHint = Message {}
example
translations {    invalid_postcode: "That isn't a UK postcode."    postcode_hint: "Write it as it is on a letter, such as SW1A 1AA."}
example
use packages/postcodes/// What the postcode field says, in the reader's language.action postcode_hint_text(reader_locale: Locale) -> PresentationText {    return Postcodes.translations.postcode_hint(locale: reader_locale)}
example
translations {    postcodes: {        invalid_postcode: "Ce code postal n'existe pas."        postcode_hint: "Écrivez-le comme sur une lettre."    }}
example
test "the package words the English and the app the French" {    assert postcode_hint_text(Locale.en_gb) == "Write it as it is on a letter, such as SW1A 1AA."    assert postcode_hint_text(Locale.fr_fr) == "Écrivez-le comme sur une lettre."}

Without translations/fr_fr.jadpo, French has nothing to show:

example
application {}locales {    default: Locale.en_gb    supported: [Locale.en_gb, Locale.fr_fr]}
text
error: `Locale.fr_fr` is supported but package `Postcodes` has no wording in it for `postcode_hint` --> app.jadpo:5:31  |5 |     supported: [Locale.en_gb, Locale.fr_fr]  |                               ^^^^^^^^^^^^ no `translations/fr_fr.jadpo`  reason: Package `Postcodes` ships no `Locale.fr_fr` wording of `postcode_hint`, and the application supports `Locale.fr_fr`, so the application words it in its root file, under the package's folder name. A locale that isn't finished yet is listed in `in_progress:`, and falls back to the default until it is.  next: Add `translations/fr_fr.jadpo` wording `postcode_hint` under `postcodes: { }` (agent)  rule: translation.file_missing  help: https://jadpo.dev/docs/errors/translation.file_missingcheck failed: 1 error, 4 files

To show a package failure under a different code, not just in different words, map it to your app's own failure at the call with attempt, as for any package failure (Calling).

When a package changes

A package that changes a message's or a failure's fields makes a breaking change to its surface, the list of what it offers (The surface). So your app's rewording fails to compile until you update it.

A package that changes its own wording marks your app's rewording stale, through sources.jadpo_record (Stale translations), and the same holds for Jadpo's wording. The root folder's sources.jadpo_record records, for each wording your app gives outside the default locale, a hash of the default wording it was made from. That is your app's own default-locale wording of the message or, if the app gives none, what Jadpo or the package ships. A default locale Jadpo doesn't ship and the app doesn't word has nothing to compare with, so its translations aren't judged.

A library package is published with complete wording in its own default locale and a /// description on every public message.

Wording two versions of a package

While your app holds two versions of a library package side by side (Where packages live), it words each under a key carrying the version, postcodes_v2: { } and postcodes_v3: { }, as its names carry it. With one version held, the key is the folder's name. Here the app's own Postcodes is held at v2 and v3 while callers move across, and the French words each:

example
translations {    postcode_hint: "Write it as it is on a letter, spaces and all."}
example
use shared/providers/postcodes/// The hint as callers showed it before the upgrade.action old_hint(reader_locale: Locale) -> PresentationText {    return Postcodes.v2.translations.postcode_hint(locale: reader_locale)}
example
use shared/providers/postcodes/// The hint with the next version.action new_hint(reader_locale: Locale) -> PresentationText {    return Postcodes.v3.translations.postcode_hint(locale: reader_locale)}
example
translations {    postcodes_v2: {        postcode_hint: "Écrivez-le comme sur une lettre."    }    postcodes_v3: {        postcode_hint: "Écrivez-le comme sur une lettre, espaces compris."    }}
example
test "the earlier version is worded under its key" {    assert old_hint(Locale.fr_fr) == "Écrivez-le comme sur une lettre."}
example
test "the next version is worded under its key" {    assert new_hint(Locale.fr_fr) == "Écrivez-le comme sur une lettre, espaces compris."    assert new_hint(Locale.en_gb) == "Write it as it is on a letter, spaces and all."}

When the second version is added, the app's wording under the folder's name no longer says which version it words. It was written while the earlier version was held alone, so the check's edit moves it to that version's key; the new version is worded afresh. When one of two is removed, the other's key goes back to the folder's name, and the removed version's group is the app's to delete:

example
translations {    postcodes: {        postcode_hint: "Écrivez-le comme sur une lettre."    }    postcodes_v3: {        postcode_hint: "Écrivez-le comme sur une lettre, espaces compris."    }}
text
error: `postcodes` words package `Postcodes`, which is held in two versions --> translations/fr_fr.jadpo:2:5  |2 |     postcodes: {  |     ^^^^^^^^^ which version?  reason: While two versions of a package are held side by side, the application words each under its folder name and version, as its names carry the version: `postcodes_v2: { }` for `Postcodes.v2` and `postcodes_v3: { }` for `Postcodes.v3`. This group was written while one version was held, so it words the earlier one unless it says otherwise.  next: Rename the group to `postcodes_v2`, or to the other version's key if it words that one (agent)  rule: translation.group_unknown  help: https://jadpo.dev/docs/errors/translation.group_unknowncheck failed: 1 error, 9 files

Packages' text: groups, calls and the surface

  • A package's group is its folder's name. The root file words packages/postcodes/v2/'s text under postcodes: { }, the name a domain can't share (package.name_taken); the record's lines and jadpo translations missing name its members postcodes.<member>.
  • What the app can word and call. A package's public messages, its enums with wording and its failures, each by the name its own files use; a member naming anything else, or something the package keeps private, is translation.member_unknown, and a call of a message that isn't public is package.not_public. A package's own code doesn't call its messages: the app does, and translations.postcodes.<message>(...) is translation.message_unknown with the call through the package as its edit.
  • The package's own files are checked by themselves, each against the locale its name gives, whether or not the app supports it: a package is published apart from any app. Their wording, shapes, placeholders and plural forms are checked as a domain's are.
  • What a supported locale needs. Every public message, an enum once the package or the app words it, and a failure some route answers with, in the root file or the package's own file for that locale. With a root file for the locale, each one missing is translation.wording_missing; without one, the locale is one translation.file_missing naming them. A locale in progress falls back, as for the app's own messages.
  • Every language a package ships is complete. A package has no default locale, so the publishing bar, the checks a package must pass to be published, holds every language it ships to completeness. Each language file words every public message, and an enum once any file words it (a wording row, shown when the package carries translations). A package with messages and no language file fails it.
  • The surface. The package's entry has a locale line for each language it words; each public message is an entry, message translations.<message> with its fields, after the functions. A new locale or message is the next minor version, a message's changed fields or a dropped locale are breaking, and changed wording is a patch.
  • The commands. missing, update and accept take a package's text in, and so do export, import and unused, below.

Packages' text in the exchange

  • What is exchanged. What a supported locale must word of a package (above) and anything else of it the app words in its root file, each unit postcodes.<member> with .<value> or .<form>, in an XLIFF <file id="postcodes" original="translations"> after the app's own and before Jadpo's. It is translated from the app's default-locale wording of it, else the package's, the wording the record's from hashes. A package in a folder named jadpo or like a scope of the app's would share unit ids, so export refuses it, as it does a domain named jadpo.
  • What a locale has. The app's root wording, else the package's own for that locale, which is what the app shows; the package's own is translated, never reviewed or stale, since no record of the app's judges it. Exporting and importing unchanged writes nothing.
  • What comes back is the app's: written into the root file for the locale, in the package's group (created, beside jadpo: { }, when the file lacks one), the whole member when one plural form or value changed, its other forms from the package's wording, and a line in the root record. The package's files are never written: a translator who changes the package's wording makes the app reword it. Import's rows name a package's units postcodes.<member>, as missing does.
  • unused lists, after the app's own and under Package wording, each member of the root files' package groups that shows nothing, with its place and why: a message or enum of the package no code of the app's calls (no code calls it), a name the package no longer has (not in Postcodes) or keeps private (private to Postcodes), and a group no installed library package words text under (no such package). These are the members to delete. A package's message the app neither calls nor words is the package's, and is not listed; a failure is shown by being reachable, as the app's are; Jadpo's group is not a package's. --json gives them as packageWording, each with a reason (uncalled, notInPackage, private, noPackage).

Two versions of a package

How your app's wording follows a library package held in two versions side by side (Two versions side by side, lazy loading and the package cache):

  • The key. Exactly while names through the package carry the version, the app words each version's text under <folder>_<version>, postcodes_v3: { }, even when only one of the two carries translations; otherwise under the folder's name. The key is the same everywhere the folder's name was: the root files' groups, the root record's members (de_de.postcodes_v3.postcode_hint), jadpo translations missing, export's <file id="postcodes_v3"> and unit ids, what import writes into, and unused, which names the package with its version (not in Postcodes.v3). The errors name the package as code does, Postcodes.v3. A domain named like a version's key is package.name_taken while two versions are held, as one named like the folder always is.

  • Adding a second version moves nothing by itself. The app's wording under the folder's name was written while one version was held, so jadpo check reports the group (translation.group_unknown, "held in two versions") with the edit that renames it to the earlier version's key, unless the file already has that group, when its members are moved by hand. The new version starts with none of the app's wording: it may have changed its messages, so nothing is copied, and the check's missing wording lists what each supported locale needs of it.

  • Removing one of two puts the other's key back to the folder's name, reported with the edit that renames it ("held in one version"); the removed version's group words nothing (translation.group_unknown, "which the app doesn't hold"), and is the author's to delete. Neither jadpo package add nor remove rewrites the app's root files, as they don't rewrite its names, and jadpo fix is still only for type clashes (Commands for records, findings and review).

  • The record follows the wording. A translation with no line under its new key takes the line it had under the package's other key in the same locale, the folder's name or another version's, when nothing is worded under that key any more and the wording is the one the line hashes; so it keeps its acceptance, and is stale when the version it now words has different default wording from what it was translated from. Wording changed on the way is a new translation, recorded afresh. jadpo translations update writes the line under the new key (moved with its wording to the package's new key) and drops the old one.

  • The publishing bar checks one package in its own folder, where one version is held, so its wording row is unchanged.

Markdown for agents: /docs/packages-text-in-an-app.md.Built from Jadpo main at a707efb.