R
Docs
Multi-language

Multi-language docs & content

How Restora 360 handles translations across the docs, the storefront, and the admin UI.

2 min read Updated 5/28/2026restaurant-ownerdeveloper

Principles

Restora 360's i18n is built on three rules:

  1. Slugs are locale-neutral. The URL /docs/getting-started/what-is-restora is the same in every language. Translations override the content of the article, not the URL. This keeps share links, backlinks, and SEO canonical URLs stable.
  2. Translations are additive, never destructive. A missing translation falls back to the default-locale (English) string. You can ship a partial translation without breaking anything.
  3. Currency is per-tenant, not per-locale. A French restaurant selling in Paris keeps EUR even if the customer browses in English. Locale controls language; currency is set in Settings → General Info.

How a doc article carries translations

Every DocArticle accepts an optional i18n field — a map from locale code ('fr', 'es', 'ar', …) to the subset of fields you want to override:

TypeScript
{
  slug: 'what-is-restora',
  categoryId: 'getting-started',
  title: 'What is Restora 360?',
  metaDescription: 'A one-line, English meta description.',
  summary: 'English summary.',
  sections: [/* English sections */],
  i18n: {
    fr: {
      title: 'Qu\'est-ce que Restora 360 ?',
      metaDescription: 'Une meta description française.',
      summary: 'Résumé français.',
      sections: [/* French sections */],
    },
    es: {
      title: '¿Qué es Restora 360?',
      // metaDescription + sections fall back to English.
    },
  },
}

The renderer chooses the locale from the request (cookie locale, then Accept-Language, then English). Anything missing falls back field-by-field, not article-by-article — so a Spanish article with only a translated title still benefits from the English body.

Storefront translations (tenant-facing)

Tenant-facing pages (menu, cart, checkout, blog) use the existing Translations admin panel:

  • Owner adds a locale in Admin → Translations → Locales.
  • The platform pre-translates UI chrome (buttons, errors, emails) using the bundled translation memory.
  • For free-text fields (menu item names, descriptions, blog posts) the AI translator (Anthropic) generates a draft the owner can refine.
  • The locale selector on the storefront persists the choice in a locale cookie scoped to the tenant.

The storefront URLs stay path-clean — /menu, /cart — and the locale is invisible to crawlers in the URL but advertised via the Content-Language response header and <link rel="alternate" hreflang="…"> tags.

RTL languages

Arabic, Hebrew, Persian, and Urdu are right-to-left. When the active locale is RTL, the platform:

  • Adds dir="rtl" to <html>.
  • Mirrors layout (sidebars switch sides, padding/margin flip via dir-aware Tailwind utilities).
  • Keeps numerals in the locale-native script (Arabic-Indic for ar-SA, Latin for ar-EG, configurable).

Currency symbols and dates use Intl.NumberFormat / Intl.DateTimeFormat with the active locale.

Fallback chain

When deciding which string to show, the system walks this chain top-to-bottom and stops at the first match:

  1. The exact requested locale (e.g. fr-CA).
  2. The language-only locale (e.g. fr).
  3. The tenant's default locale (set in Settings → General Info).
  4. English (en) as the platform-wide fallback.

This means a tenant whose default is it will see an Italian fallback before English, but a customer asking for pt on the same tenant gets the English fallback if no Italian → Portuguese path is configured.

Adding a new docs language

Until the docs CMS lands (P2 in the roadmap), translations are added in code:

  1. Open the article in src/lib/docs-center/articles.ts.
  2. Add a key to the i18n map keyed by the ISO 639-1 code.
  3. Ship only the fields you have. Missing fields fall back to English automatically.
  4. The search index is built from the active-locale text at render time — you don't need to maintain a separate index per language.

When the CMS adapter lands, this same shape becomes the JSON contract between the CMS and the renderer, so any work you do now ports directly.

Frequently asked

  • No. The slug is locale-neutral. The active locale is selected from the user's cookie / Accept-Language, and search engines see the same canonical URL with hreflang tags pointing at the same path. This avoids duplicate-content penalties.