Recipe API

Localized Food Taxonomies Need Stable IDs, Not Translated Strings

Recent Open Food Facts taxonomy translation work shows why recipe and nutrition APIs should separate canonical food identities from localized labels, synonyms, facets, and analytics keys.

ingredientslocalizationapi-designtaxonomy

The fresh signal

International food products expose a deceptively hard API problem: the thing a user reads is not the thing a system should join on. A nutrition label may say "folates" in English, "叶酸(总叶酸)" in Simplified Chinese, or use a brand-specific phrasing. A grocery product may use an additive code, a translated ingredient, or a regional synonym. A recipe may use an English common name while the matched packaged product arrives from a Chinese, French, or German data source.

Open Food Facts shipped a useful reminder this week. On July 9, 2026, the project merged a cluster of human-reviewed Simplified Chinese taxonomy translations for nutrient concepts, including polydextrose, folates, galactose, added salt, and erythritol. The same week, Robotoff merged a fix replacing obsolete facet URLs with the newer /facets/ prefix while preserving metric tag names, because a URL cleanup could otherwise leak into operational dashboards.

The repeated angle to avoid is the one this blog has already covered: taxonomies drift, and facet endpoints are contracts. The narrower lesson here is localization. Recipe, nutrition, meal-planning, grocery, and food-AI APIs should model translated food names as aliases attached to stable concepts, not as canonical values. If translated labels, search facets, nutrient keys, and analytics dimensions collapse into one string, every localization improvement becomes a potential breaking change.

Thesis: multilingual recipe and nutrition products need a food-entity layer that separates canonical IDs from localized labels, source-language aliases, matching evidence, facet slugs, and reporting keys, so better translations improve user experience without corrupting joins, filters, or historical metrics.

Why translation is not just display text

It is tempting to treat localization as a front-end concern: store erythritol, translate it for the UI, and move on. Food data does not stay that clean.

A recipe platform typically receives data from many places:

  • creator-authored recipe text;
  • scanned package ingredients;
  • public product databases;
  • retailer catalogs;
  • nutrition reference tables;
  • user preference rules;
  • AI extraction from images or unstructured text.

Those inputs do not share one language, spelling system, or naming convention. They also do not represent the same kind of concept. "Added salt" can be a nutrient-related label, an ingredient-analysis signal, a dietary concern, or a filterable facet. "Folates" can be a nutrient concept with units and daily-value logic. "Polydextrose" can appear as an ingredient, fiber-related nutrition term, or additive-like formulation component. "Erythritol" can matter for sugar-free search, low-calorie sweetener detection, ingredient avoidance, and product matching.

When a new translation lands, the system should gain a better way to recognize or display a concept. It should not accidentally create a new concept.

That distinction is especially important for technical buyers evaluating a recipe API. A provider may claim to support many languages, but the useful question is: do multilingual terms resolve to the same stable ingredient or nutrient identity, with evidence, or are they separate strings sprinkled through search?

The failure modes of string-level localization

A string-first design usually works until the product expands beyond one market. Then the hidden failures show up.

Failure mode What users see What broke underneath
Duplicate concepts Search shows separate facets for erythritol and 赤藓糖醇 The translated label was inserted as a new entity instead of an alias.
Lost nutrition joins A localized nutrient label does not map to nutrient calculations Display labels are being used as keys in nutrition logic.
Unstable filters Saved filters stop working after labels are improved Filter IDs, slugs, and labels are the same field.
Bad avoidance logic A user avoiding added salt still sees translated product ingredients Preference rules only match English strings.
Broken analytics Historical reports split before and after a translation rollout Metric dimensions use mutable localized names.
Over-broad synonyms A term valid in one locale matches unrelated text in another Aliases do not carry locale, source, or match type.

These are not theoretical edge cases. They are the natural result of using human-readable labels as infrastructure. Food products have too many downstream dependencies for that shortcut: search indexes, recommendation models, shopping lists, nutrition panels, substitution rules, user safety preferences, customer dashboards, and cache keys can all depend on the same concept.

A localization-aware schema sketch

A durable API shape starts with the canonical concept and treats labels as versioned, sourced aliases. The public response can still be simple by default, but the underlying model needs enough structure to explain how a term was recognized.

{
  "entityId": "nutrient:folates-total",
  "entityType": "nutrient",
  "canonicalName": "Folates (total folates)",
  "canonicalLocale": "en",
  "taxonomy": {
    "source": "openfoodfacts-taxonomy",
    "version": "2026-07-09",
    "sourceUrl": "https://github.com/openfoodfacts/openfoodfacts-server/pull/13954"
  },
  "labels": [
    {
      "locale": "en",
      "value": "Folates (total folates)",
      "role": "preferred",
      "status": "active"
    },
    {
      "locale": "zh-CN",
      "value": "叶酸(总叶酸)",
      "role": "preferred",
      "status": "active",
      "review": "human-reviewed"
    }
  ],
  "aliases": [
    {
      "locale": "en",
      "value": "total folates",
      "matchType": "synonym",
      "source": "taxonomy"
    }
  ],
  "api": {
    "filterId": "folates-total",
    "facetPath": "/facets/nutrients/folates-total",
    "analyticsKey": "nutrient_folates_total"
  }
}

The important part is not the exact field names. It is the separation of responsibilities:

  • entityId is stable for joins and internal references;
  • labels are localized and can improve over time;
  • aliases support recognition and matching, with locale and source;
  • filterId is the stable API value clients send;
  • facetPath is a routable product surface;
  • analyticsKey is preserved for reports even if URLs or labels change.

That final split echoes the Robotoff facet fix. Updating paths to /facets/ was necessary, but preserving metric tag names avoided turning a route cleanup into an analytics migration. Recipe APIs should apply the same discipline before localization work reaches customers.

Product and API decisions for builders

1. Localize output, not identity

The API should let clients request localized labels with Accept-Language, a locale parameter, or an explicit field selection. But the identifiers returned with those labels should remain stable.

A response such as this is safer than returning only translated names:

{
  "ingredient": {
    "id": "ingredient:erythritol",
    "label": "赤藓糖醇",
    "locale": "zh-CN",
    "fallbackLocale": null
  }
}

If a translation is missing, return the stable ID plus a fallback label and disclose the fallback locale. Do not invent a new localized entity. Do not force clients to infer identity from display text.

2. Store match evidence for multilingual parsing

When an ingredient line or product label is parsed, store how the match happened. Exact match, normalized spelling, translated alias, additive code, model inference, and manual correction should not be flattened into one confidence score.

For example, a recipe search system should be able to distinguish:

  • erythritol matched the English preferred label;
  • 赤藓糖醇 matched a Simplified Chinese preferred label;
  • a misspelling matched through fuzzy search;
  • an AI model inferred the ingredient from context;
  • a human reviewer corrected the entity.

Those distinctions affect trust. A health-oriented meal planner may accept exact and reviewed translated matches for exclusion filters, but require manual review for low-confidence model inferences.

3. Version translation rollouts like data migrations

A new translation looks harmless because it improves display quality. But it can also change recognition. If search analyzers, autocomplete, ingredient extraction, or preference matching begin using the new alias, the rollout can affect results.

Treat significant localization updates as data migrations:

  1. identify affected entity IDs;
  2. count recipes, products, and user rules that may rematch;
  3. reindex only the affected slices where possible;
  4. compare before-and-after facet counts;
  5. monitor unresolved terms by locale;
  6. preserve old analytics keys for historical continuity.

This is operationally heavier than adding a string to a translation file, but it prevents silent product regressions.

4. Separate locale from market

zh-CN is a language-region locale. It is not the same thing as a retail market, legal jurisdiction, or nutrition-label regime. A user may read Simplified Chinese in Singapore, buy groceries in Australia, and follow nutrition rules based on a local authority. Similarly, English labels differ across the United States, United Kingdom, India, and global packaged-food imports.

Model these separately:

  • locale for display and parsing;
  • market for availability, retailer catalogs, and grocery prices;
  • jurisdiction for labeling rules and regulatory constraints;
  • dataSource for provenance.

Collapsing them into one field makes APIs brittle. A translated nutrient label should not imply that the nutrition calculation, grocery availability, or compliance interpretation comes from the same country.

Evaluation checklist for recipe API buyers

When comparing recipe, nutrition, or grocery data providers, ask localization questions that reveal the underlying model:

  • Do ingredient and nutrient entities have stable IDs independent of language?
  • Are translated labels and synonyms stored with locale, source, and review status?
  • Can the API return both canonical IDs and localized display labels in the same response?
  • Are filter values stable when labels change?
  • Are facet URLs, filter IDs, and analytics keys separate fields?
  • Does multilingual matching expose evidence such as exact, synonym, translated, fuzzy, model-inferred, or human-reviewed?
  • Can customers see taxonomy or translation version metadata?
  • Are missing translations represented as fallbacks rather than new entities?
  • Can preference and avoidance rules match translated ingredient data safely?
  • Is there an impact-analysis process before localization changes affect search or recommendations?

A provider that answers these well is more likely to support international recipe search, grocery matching, and nutrition workflows without expensive downstream cleanup.

The practical takeaway

Localization is not a decorative layer for food-data products. It is part of entity resolution. Recent Open Food Facts translation work is small in the best possible way: one reviewed nutrient concept at a time, attached to an existing taxonomy. That is how structured food vocabularies improve. The engineering question is whether recipe APIs are designed to absorb those improvements safely.

For Recipe API-style products, the design principle is simple: make identity stable and labels replaceable. Store canonical entities, localized names, aliases, match evidence, taxonomy versions, facet identifiers, and analytics dimensions as separate pieces of data. Then a better Simplified Chinese label for folates, a corrected sweetener translation, or a facet route migration can improve the product without breaking search, nutrition joins, saved filters, or historical reporting.

Sources

Start Building

One consistent schema on every response. Get a free key and ship in minutes.