Content & editorial
Product Localization Framework
Generated from docs/plan/core/product-localization-framework/PLAN.md in the AstroBaaS repository. Nothing described below is implemented — it is the written plan for it.
Product Localization Framework enables merchants to offer product descriptions, categories, and metadata in 20+ languages. Core stores translatable fields per collection (product.description is one field with translations for each language), merchants select default language and language fallback chains, and storefronts request products in their language. This is core GPL functionality: the interface and honest i18n implementation, with no per-country obligations or credential requirements.
The problem
Merchants selling in 20+ language markets have no way to localize product descriptions or category names. Forcing all customers to read English reduces conversion 40%+ per language tier. Merchants are losing sales because non-English speakers see only English product copy.
What it does
- Mark fields as translatable at collection schema time (product.name, product.description, category.name)
- Store translations per language per field: translatable_fields = { name: { en: ’…’, de: ’…’, fr: ’…’ } }
- Global settings: defaultLanguage (en), enabledLanguages (en, de, fr, ja), fallbackChain (ja → en)
- Admin UI: language tabs on content edit form, edit field in each language simultaneously
- Storefront API: fetch product in requested language; if translation missing, fall back to defaultLanguage
- Bulk edit mode: translate all products into target language at once (without auto-translation)
- Audit log: translation added/updated/deleted, timestamp, staff member
- Export translatable fields as CSV per language (for external translators)
- Import translated CSV back in, merge into existing translations
- Admin dashboard: translation coverage per language (% of products translated, % of categories)
What it deliberately does NOT do
Each boundary carries its reason. A boundary without a reason gets crossed by the next person who reads this.
- Automated translation (DeepL, Google Translate) — scope is framework only; automated-translation-service is a separate paid plugin
- Machine-learning-driven language detection — scope is explicit language selector in storefront; detection is storefront responsibility
- Right-to-left (RTL) text handling — scope is data model only; RTL CSS is storefront responsibility
Data model
Modify core collection schema: add translatable_fields map to any collection. Example: product = { id, name_en, name_de, name_fr, description_en, description_de, … } OR { id, name: {en, de, fr}, description: {en, de, fr} }. No schema migration if using new format; breaking migration if converting existing to i18n format.
API
- PATCH /api/collections/:collection/schema — mark field as translatable
- GET /api/settings/i18n — fetch language config (default, enabled, fallback chain)
- PUT /api/settings/i18n — update language settings
- GET /api/products/:id?language=de — fetch product in German, fallback to default if missing
- POST /api/products/:id/translations — bulk set all translations for product
- GET /api/products/export?language=de&fields=name,description — export translatable fields as CSV
- POST /api/products/import-translations — import translated CSV, merge into existing
Every route added here must also appear in src/pages/openapi.json.ts — a test
fails the build if it does not.
Admin
Collection schema editor allows marking fields as translatable. Product edit form shows language tabs: [English] [Deutsch] [Français]. Edit form filled separately per language. Translation coverage dashboard shows % translated per language, with drill-down to untranslated products. Bulk export/import UI for sending to external translators.
The seam — why this is core
Core owns: translatable field storage, language config (settings), fallback chain resolution, export/import logic, admin UI. Paid plugin (automated-translation-service) owns: credential (DeepL API), bulk auto-translation, quality assurance.
core owns the interface + an honest hand-modelled implementation — the translation schema (translatable fields, language selection, fallback chains) and i18n routing are foundational. Merchants can manually translate or integrate external services.
Dependencies
- core-settings-api (language config storage)
- core-audit-log (translation changes)
- core-csv-export-import (translatable field export)
Acceptance checks
Each of these must be able to fail. Before claiming this is done, break the code deliberately and watch each one go red.
- Product.name field marked as translatable stores en, de, fr translations independently
- GET /api/products/1?language=de returns product.name in German
- If German translation missing, fallback chain (de → en) returns English name
- Admin language tabs allow editing product.name in English and German side-by-side
- Translation coverage dashboard shows ‘42% translated to German’ (105/250 products)
- CSV export includes all translatable fields with language columns
- Imported CSV with German translations merges with existing translations without overwriting English
Risks
Storage choice (relational columns vs document-blob map) must work on all three storage drivers (lowdb, libSQL, relational). Fallback chain not validated allows circular references (de → fr → de). Export/import CSV loses non-ASCII characters if encoding not UTF-8. Breaking schema migration loses existing product data if conversion not atomic. Storefront requests language not in enabledLanguages causes null/undefined if not handled.
Commercial context
| Suggested price | Free (core) |
| Rival anchor | Shopify Markets (€27/mo, includes); Magento (free framework, translation via third-party); WooCommerce WPML (€99-399/year) |
The anchor is what the nearest equivalent charges on Shopify or Magento today. It is context for a pricing decision, not the decision.
Generated from the commerce plan. See docs/COMMERCE-PLAN.md for the full
catalogue and ../../AI-GUIDE.md for how to work on this repository.