EIP.tools

EIP.tools

⚠️ DraftStandards Track: ERC

ERC-8346: Translation Files for ERC-7730 Descriptors

Translation file format and integrity mechanism for ERC-7730 Clear Signing descriptors localization.

Authors
Created2026-07-22
Discussion Linkhttps://ethereum-magicians.org/t/erc-8346-translation-files-for-erc-7730-descriptors/29072
Requires

Markdown

https://raw.githubusercontent.com/ethereum/ERCs/re...
Pull Request#1906PR open

EIP-GPT summary

Contents
AbstractMotivationSpecificationTranslation file formatKey formatNamespaces and includesPlaceholdersObject-valued translationsTranslation resource integrityTranslation hash computationCanonical EAS schemaDiscoveryValidityLookup semanticsRationaleDot-namespaced snakecaseAll-or-nothing validityIndependent attestation instead of descriptor-pinned hashesNo plural or gender formsEAS attestations instead of a generic integrity standardBackwards CompatibilitySecurity ConsiderationsCopyright

Abstract

ERC-7730 defines a schema format that allows describing intents and inputs of Ethereum transactions in a human-readable way. This format necessarily includes a significant amount of plain text in strings and templates, which are authored in English.

This specification defines the translation file format for ERC-7730 descriptors, how a translation resource is bound to the descriptor and locale it translates, how its integrity is checked, and how wallets look up localized strings at render time.

Motivation

The main purpose of the Clear Signing standard is to define a mechanism that can explain the true meaning and impact behind the bytes being signed by the users' wallets. This equally includes both software and hardware wallets and must not introduce unnecessary trusted parties like automated translation services into the transaction signing process.

This means that the text-based part of the Clear Signing description needs to be provided in a language the user is able to understand to achieve the level of clarity required for making an important financial decision. English-only approach to Clear Signing may prove insufficient for a very significant portion of Ethereum users worldwide. Therefore, ERC-7730 needs to support translation of Clear Signing descriptors into multiple languages without embedding the full translation directly in ERC-7730 descriptor file format.

Translations may also be produced by parties other than the descriptor author. A single descriptor may eventually be translated into dozens of languages, and requiring the descriptor author to review and republish the descriptor for each of them would not scale.

Specification

Translation file format

A translation file is a JSON document with the following fields:

FieldRequiredDescription
$schemaYesURI of the translation file schema.
$localeYesCanonical BCP-47 language tag.
descriptorHashSee belowHash binding this file to the descriptor it translates.
includesNoMap from namespace to candidate references to a shared package.
translationsYesFlat map from translation key to translated value.
{
    "$schema": "./erc8346-v3.0.0-next.schema.json",
    "$locale": "fr",
    "descriptorHash": "0x14da251e322186245d812ff3fd0b1e9b404c5896964f9dbce2bd2bd898abbe84",
    "includes": {
        "erc20": [
            { "uri": "./example-erc20.fr.json" }
        ]
    },
    "translations": {
        "mytoken.transfer.interpolated_intent": "Envoyer {value} à {to}",
        "erc20.transfer.to_label":              "Bénéficiaire"
    }
}

In this example, the local translations map carries the descriptor's own key (mytoken.transfer.interpolated_intent — the interpolated intent embeds this descriptor's placeholders and phrasing, so no shared package can provide it), while the descriptor's erc20.* keys resolve through the included package. The one erc20.transfer.to_label entry is an override: this descriptor prefers "Bénéficiaire" over the package's "Destinataire", per the precedence in Namespaces and includes.

See example-main.fr.json for a complete example, example-erc20.fr.json for the shared package it includes, and erc8346-v3.0.0-next.schema.json for the JSON schema of the translation file format. The schema's version tag strictly tracks ERC-7730's schema versioning.

All BCP-47 language tags — in $locale and anywhere else tags appear in descriptors or translation files — MUST use canonical BCP-47 casing (e.g. fr, zh-Hant, pt-BR).

A translation resource used as the translation of a descriptor MUST carry a descriptorHash equal to the hash of that descriptor, computed according to ERC-8176.

Key format

Translation keys MUST match the pattern:

^[a-z][a-z0-9_]*(\.[a-z][a-z0-9_]*)*$

That is: one or more dot-separated segments, each segment starting with a lowercase ASCII letter and containing only lowercase ASCII letters, digits, and underscores (e.g. erc20.transfer.amount_label, swap.confirm_intent).

Keys are only in scope for the descriptor being translated, meaning that uniqueness across unrelated descriptors is not required and carries no meaning: the same key in two descriptors refers to two unrelated strings, each resolved against that descriptor's own translation resources. Within a single descriptor, after resolving ERC-7730 includes, each key MUST be associated with exactly one literal. Validators MUST reject descriptors that attach the same key to different literals.

A <field>Key property is only valid alongside its literal sibling — labelKey with label, intentKey with intent, and so on. The literal is the authoritative English source text and the terminal fallback, so validators MUST reject descriptors carrying a <field>Key without the corresponding literal.

Namespaces and includes

A namespace is the leading dot-segment of a key — i.e. erc20 in erc20.transfer.intent, swap in swap.confirm_intent.

The includes field lets a translation file delegate a namespace to a shared package — a translation file maintained separately, for example, one canonical French vocabulary for ERC-20 descriptors. This allows many descriptors' translations to reuse all translations in a namespace instead of each repeating its own copy. Each includes entry maps a namespace to an array of one or more candidate references to the same package; a wallet uses the first candidate it can resolve to a valid package.

A shared package is a translation file in the format defined by this specification, with these constraints:

  • Its $locale MUST equal the including file's $locale.
  • It carries no descriptorHash.
  • It MUST NOT contain an includes field of its own: includes resolve one level deep.
  • It is subject to the same integrity requirements as any translation resource.

A key k with namespace ns resolves as follows:

  1. If the including file's own translations map defines k, use that value. This lets a file override a shared term when its descriptor's context requires different wording.
  2. Otherwise, if includes has an entry for ns, look up k in the resolved shared package's translations map. Only entries whose namespace is ns are considered.

There is no further fallback: a key that resolves through neither step renders the whole resource invalid, per Validity.

Placeholders

Some strings contain field-value placeholders in the form {fieldPath}. The set of placeholders in a translated string MUST be exactly the set of placeholders in its English source: each placeholder MUST appear verbatim, none may be removed, and none may be added. Word order may be rearranged around placeholders to suit the target language. For example:

"pool.add_liquidity.interpolated_intent":
    en: "You are providing {amount} as liquidity to {poolName}"
    uk: "Ви надаєте ліквідність до {poolName} на суму {amount}"

This requirement is a security measure: a translation that omits a placeholder withholds information from the user, and one that adds a placeholder causes the wallet to interpolate field values that the descriptor author did not reference in that string.

Object-valued translations

Some ERC-7730 constructs carry structured display text rather than a single string. Their translation entries are objects instead of strings.

Object-form intents. An ERC-7730 intent may be a JSON object of label/value pairs. The translation value for its intentKey is an object mapping each key of the English intent object to an object with label and value members holding the translated pair:

{
    "staking.withdraw.intent": {
        "Native Staking": { "label": "Staking natif", "value": "Retirer" },
        "Rewards":        { "label": "Récompenses", "value": "Consensus et exécution" }
    }
}

The translation object's key set MUST exactly equal the key set of the English intent object.

Enumerations. The display values of an ERC-7730 metadata.enums entry are translated as an object mapping each raw enumeration value to its translated display string; the key set MUST exactly equal the key set of the English enumeration.

{
    "lending.interest_rate_mode": {
        "1": "stable",
        "2": "variable"
    }
}

A shape mismatch — a string entry where an object is expected or vice versa, or a key set differing from the English source — violates this section's constraints.

Translation resource integrity

Translation resource producers attest resources using the Ethereum Attestation Service similarly to ERC-8176's descriptor attestation mechanism, under a dedicated attestation schema.

Wallets MUST NOT display strings from a translation resource — including a shared package — that lacks a valid attestation of its translationHash from an attester the wallet trusts.

Translation hash computation

To compute the translationHash of a translation resource:

  1. Let T be the parsed JSON translation resource object.
  2. Serialize T to a byte string using the JSON Canonicalization Scheme.
  3. Compute the Keccak-256 hash of the resulting byte string.
  4. Encode the hash as a 0x-prefixed lowercase hexadecimal string (66 characters total).

Wallets MUST compute the translationHash of the fetched resource themselves and match it against attested hashes.

Canonical EAS schema

FieldValue
Schemabytes32 translationHash
Schema UIDTBD
Resolver0x0000000000000000000000000000000000000000
Revocabletrue
EAS Contract0xA1207F3BBa224E2c9c3c6D5aF63D0eb1582Ce587
ChainEthereum mainnet (chainId = 1)

Discovery

Wallets may obtain candidate translation resources for a descriptor from any source. Because descriptor binding and integrity are enforced on the resource itself, discovery channels need not be trusted, and third parties can publish and attest translations for new locales without any change to the descriptor.

Validity

A translation resource R is valid as the translation of descriptor D into locale L if and only if all of the following hold:

  1. R validates against the translation file schema.
  2. R.$locale is a canonically cased BCP-47 tag equal to L.
  3. R.descriptorHash equals the hash of D computed according to ERC-8176.
  4. R carries a valid attestation per Translation resource integrity.
  5. Every entry of R.includes resolves to a shared package that itself satisfies the shared package constraints and integrity requirements.
  6. R is complete: every translation key referenced by D (after resolving ERC-7730 includes) resolves per Namespaces and includes.
  7. Every resolved value passes the placeholder and shape checks against its English source.

Wallets MUST evaluate these checks themselves at render time rather than relying on publication-time validation. Any single failure invalidates R entirely, and wallets MUST NOT display individual strings from an invalid resource: translated screens are all-or-nothing, and strings from different locales are never mixed within one descriptor rendering.

Lookup semantics

  1. The wallet builds an ordered locale preference chain using BCP-47 matching and custom preferences.
    The default BCP-47 tag resolution relies on widening the match conditions, e.g.: zh-Hant-HK → zh-Hant → zh.
    Users SHOULD be able to define their own preferences in their wallets, e.g.: sk → cs → pl.
    The chain always terminates in the descriptor's literal English strings.
  2. For each locale L in the chain, the wallet enumerates candidate resources for (D, L) from its discovery sources, fetches each in turn, and checks validity. The first valid resource is selected. Any failure — unreachable URI, schema violation, hash or attestation mismatch, incompleteness — moves on to the next candidate, then to the next locale.
  3. If a resource was selected, every field carrying a <field>Key property is displayed using its resolved translation. Fields with no <field>Key property are not translatable under this specification and are always displayed using their literal value; descriptor authors SHOULD therefore key either all of a descriptor's user-facing strings or none, so that translated renderings are not mixed-language.
  4. If no valid resource exists for any preferred locale, the wallet displays the descriptor's literal English strings and SHOULD show a single notice that no translation was available for the user's preferred locales — not a per-string warning.

Rationale

Dot-namespaced snake_case

Namespacing keys by descriptor family makes keys self-describing and gives includes a routing prefix: a wallet can tell which package a key like erc20.transfer.intent may resolve against just from its leading segment. snake_case segments match existing key-naming conventions and keep the Key format pattern ASCII-only and unambiguous regardless of case. Because keys are descriptor-scoped, authors are free to pick readable names without coordinating with anyone.

All-or-nothing validity

Falling back to the English literal for each individually missing key would produce mixed-language renderings on the signing screen: some fields in the user's language, others in English. This degrades comprehension in the exact context this specification exists to protect, and hardware wallets lack the screen space for meaningful per-string warnings. Treating the entire resource as valid or invalid guarantees that every rendering is in a single language. It also makes completeness a property that publication tooling can verify mechanically before a translation is distributed.

Independent attestation instead of descriptor-pinned hashes

An alternative design would pin the expected translationHash of each translation inside the descriptor's $localization references, so that translation integrity is covered by the descriptor's own ERC-8176 attestation. This couples every translation to the descriptor release cycle: adding a language or correcting a translation would require editing and re-attesting the descriptor, which does not scale to many locales maintained by independent translation services. Binding in the opposite direction — the translation carries the descriptor's hash and is attested on its own — provides the same descriptor-to-translation binding while allowing any party to publish and attest a new locale without involving the descriptor author.

No plural or gender forms

The flat string map with verbatim placeholders deliberately excludes ICU-MessageFormat-style plural and gender selection. Placeholder values in ERC-7730 are formatted field values — amounts, addresses, dates — where grammatical agreement rarely affects meaning, and the flat form is implementable on constrained hardware wallets and mechanically verifiable against the English source. Translators should phrase strings so they read correctly regardless of the interpolated value. A future revision may add plural support if practice shows it is needed.

EAS attestations instead of a generic integrity standard

ERC-7730 descriptors are expected to be attested via ERC-8176 using the Ethereum Attestation Service. Mirroring that same mechanism for translation resources allows wallets to share the attestation-verification code path with the same multi-attester, revocation, and wallet-trust-policy semantics for both descriptors and their translations.

Backwards Compatibility

This specification is new and additive only. The $localization field and the <field>Key properties it builds on are optional in ERC-7730. Existing ERC-7730 descriptors continue to function and are not affected by this proposal.

Security Considerations

Translation resources are fetched from the same kind of untrusted hosts as the descriptors that reference them and are subject to the same registry poisoning concerns discussed in ERC-7730. Two requirements defend against these attacks: a resource without a valid attestation from a trusted attester is never displayed, and the descriptorHash binding prevents replaying an attested translation against a descriptor other than the one its attester reviewed. A malicious discovery mechanism or host can therefore deny a translation but not alter what the user is shown.

Because wallets evaluate validity at render time, a translation that slips past publication-time tooling — for example, one violating the exact-set placeholder rule — is still caught before anything is displayed.

A shared package included by a translation file is attested independently and may be re-attested with changed content after the including file was reviewed, changing the combined rendering. Wallets accept this by construction — both attesters must be trusted — but attesters of including files should be aware that their attestation covers the reference to the shared package, not the package's future contents.

Attestation verification MUST occur within the trust domain of the display. A hardware wallet that receives pre-resolved strings from a companion application is trusting that application unless verification happens on-device or the strings are delivered through a channel the device can authenticate, such as vendor-signed translation packs.

Copyright and related rights waived via CC0.

EIP.tools

EIP.tools

Search, read, and map Ethereum improvement proposals, ERCs, RIPs, and CAIPs from one focused interface.

Farcaster
by @apoorveth