Skip to content

The language promise

Every extension we sell speaks one translation convention, and this page is the commitment attached to it: which strings a named native speaker has actually read and signed off, per extension and per language, with everything unreviewed shipping in English.

It is written for the merchant deciding whether to run a shop in a language other than English on the strength of what a marketplace listing says. That decision is usually taken on a claim nobody can check, so this page publishes the numbers instead, including the ones that do not flatter us.

Note what is not claimed here. There is no count of languages, no badge and no "available in four languages". Anybody can put a language directory in an archive, and most of this marketplace has: the majority of the translation packs on sale were uploaded in bulk by a handful of vendors, which makes a pack exists worth nothing. What is scarce is somebody having read it. So that is what this page records, and it is the only thing it records.

What is promised

Four things, in order of how hard they are to come by.

  • A notification email goes out in the language its recipient asked in. Not the language of the store, and not the language of whoever happened to trigger the send. When somebody signs up for a back-in-stock alert or is asked for a review, the language they were using is captured with the request and used when the mail is written, however much later that is.
  • A string we have not had reviewed reads as English. Never blank, never a raw identifier like text_button, never the previous shopper's language. This is the one guarantee written against a measured failure rather than an assumed one: a half-finished pack showing entry_email where a label belongs is the normal state of this market, and it cannot happen here, because an unreviewed string is the English string in the archive you install.
  • A single-language store pays nothing. No extra query, no extra file read, no extra setting to leave wrong. If your shop runs in one language, the convention is invisible to it. This is a rule the build enforces on us, not a side effect we noticed.
  • Your own wording overrides ours, per language, and survives an update. Where an extension lets you write your own text for a shopper-facing string, that text is yours per language, is stored as your store's data rather than as our file, and an update does not touch it. See What this page does not promise for the boundary, because there is one and merchants get it wrong.

Which strings have been read, and by how many

Two raw counts per cell: how many of that side's strings are signed off, out of how many there are. Not a percentage, and not rounded. A rounded number would go stale far less often, which is exactly the argument against it: a figure that only moves when it crosses a boundary is a figure that hides the week it slipped.

Nothing is on this table yet: no extension declares a language beyond English.

The table is generated from the sign-off records committed beside each extension's own code, across every extension at once. Nobody types a row into it and nobody can leave one out.

A language not on this table is a language no extension of ours claims. An extension does not declare a language until its storefront strings have been signed off (see The floor is asymmetric), so there is no half-declared, preview or coming-soon language to ask about. Absence here is the whole answer.

A storefront cell reading — means the extension has no shopper-facing text at all: it is an admin-side tool that adds a screen for you and nothing a customer reads. It does not mean nothing has been reviewed, and it does not mean everything has. Both a 0 and a full count would be a lie there, in opposite directions, so the absence is printed as an absence. Each extension's Limits and guarantees page says which of the two it is for that extension.

The two columns are split by where a string lives, not by who reads it. Everything in an extension's storefront language files, plus any data files it ships per language, counts as storefront; everything in its admin language files counts as merchant screens. That is a line the build can see, and it is drawn deliberately in the direction that costs us more: a handful of strings written for a merchant (the alert mails an extension sends the shop owner) live in storefront files and are therefore counted, reviewed and paid for as if a shopper read them. The count is never the other way round.

The floor is asymmetric, and that is deliberate

The two columns are held to different standards, and reading only the numbers would suggest that is an oversight to be tidied up. It is not.

  • Storefront: every string reviewed, or the language is not declared at all. There is no such thing as a partly translated storefront here. A shopper cannot switch language back, cannot read documentation about it and has no alternative to what the page in front of them says, so a mixed-language checkout is a defect we will not ship. This is not a separate mechanism. It is a statement about the order of the work, since declaring the language is the last act.
  • Merchant screens: no floor, and a live number instead. A merchant has all three of the things a shopper does not: a language switch, this page, and the English documentation. So merchant-side prose is translated as budget allows and the number is published as it stands, moving up, and down when the English moves.

The three states a string is in

There is one field behind every number on this page: the hash of the English text as it stood when a reviewer signed the string off. There is no separate flag, no expiry date and no "needs attention" queue, because they would be three ways of writing down what that one hash already says.

The record says The string is What is served
nothing about it unverified the English text
a sign-off matching today's English reviewed the translation
a sign-off against English that has since moved unverified again the English text

The third row is why fixing a typo in an English sentence throws nothing away. The sign-off lapses for that one string, that string falls back to English, and the translation somebody was paid for stays on disk waiting for the next review. Correcting a full stop does not retire a language, and it does not silently ship a translation of a sentence we no longer say.

What this page does not promise

  • The documentation is not translated. These pages, including this one, are English, and will be for the foreseeable future. Our admin screens follow the admin language you select; the documentation describes the English ones.
  • No language beyond the table above, anywhere. We do not name a language or a language count in a listing, on the marketing site, or in prose on this page, only in the generated table, which is folded out of the extensions' own records. A written-out four is false the day a fifth lands and nothing would catch it, so there is nowhere to write one.
  • Your edits to our language files do not survive an update. This is the one merchants get wrong. The copy fields an extension offers (where a screen invites you to write your own wording) are your store's data, are per language, and are untouched by an update. Editing the .php files inside extension/ is a different act entirely: an update replaces those files, and your change is gone. If you want different wording, use the field.
  • No implication of completeness. A number on this page is what has been reviewed on the day it was regenerated, and nothing more. There is no finishing line being worked towards, no roadmap of languages, and no reading of a high merchant-side count as "done".
  • Your shop will not be entirely in the shopper's language. Our strings follow the shopper. OpenCart's own (the checkout labels, the account pages, the error messages, the emails core sends) follow whatever language pack you installed, and we install none. Around a translated widget of ours you will see whatever core is doing on your store, and the first screenshot you take will show you exactly that. This is a limit of buying an extension rather than a shop, and it is stated here rather than left for you to discover.
  • The string served is not guaranteed to be the string signed off. OpenCart ships a translation feature of its own, and it runs after ours: rows entered in core's Language Editor are merged over everything an extension ships, per route and per language. If somebody on your team has edited one of our strings there, that edit wins, as it should. This page is a promise about what we ship. A string your store has edited is your store's.

How the promise is kept, and not just written

The numbers are checked by the build.

Each sign-off is a committed record naming the reviewer, the date and the English it was made against, kept beside the extension's own code. The table above is folded out of those records on every build, and a build whose committed page no longer matches them fails, so a number cannot go stale unnoticed, and an English edit that lapses twelve reviewed strings shows up in the documentation diff of the change that caused it, in front of the person who made it.

The installation test goes further. It builds each extension's archive, installs it on a real OpenCart store, adds a second language to that store part-way through, and asserts two things a static check cannot see: that no page renders one of our identifiers as visible text, and that a string only the second language's files carry actually reaches the page. A declared language with nothing reviewed fails there, because it packages as pure English.

What the build cannot check is the review itself. Whether a sentence reads naturally to somebody who grew up with the language is a judgement no diff makes. That is held by naming the reviewer, which is what makes a sign-off cost something to make. It is also why this page counts sign-offs rather than files.