EAV scope, fallback and translation workflows
Managing multilingual product attributes in Magento 2 starts with understanding how the EAV model stores values per store view, and when an empty store view value silently falls back to the default store. Without this fallback mechanism in mind, translation gaps arise that stay invisible in the backend but appear as foreign language text in the storefront.
Table of Contents
- 1. Where multilingual product attributes really begin
- 2. EAV attribute scope: global, website, store view
- 3. The fallback mechanism in detail
- 4. Creating a custom translatable attribute via setup patch
- 5. CSV import with a store view column
- 6. Programmatic translation maintenance with DataPatch
- 7. Don't forget categories and static blocks
- 8. Systematically finding translation gaps
- 9. Attribute scope strategies compared
- 10. Summary
- 11. FAQ
1. Where multilingual product attributes really begin
As soon as a Magento 2 shop offers products in more than one language, managing multilingual product attributes becomes one of those tasks that looks technically simple but quickly grows organizationally complex. Magento stores attribute values in the EAV model, entity attribute value, where each value is tied to a specific entity id, a specific attribute and a specific store. For translatable fields such as product name or description this means: the same product record can carry a completely different text per store view, without needing to create a second product.
The real challenge lies not in the database structure itself, but in the interplay between attribute scope configuration, fallback behavior and the editorial process that ensures every attribute is actually maintained in every language. If a translation is missing, Magento shows the default store's value by default, which looks harmless at first glance but, in multilingual catalogs with thousands of products, leads to situations where English product names appear in a French store view without any technical error being present.
2. EAV attribute scope: global, website, store view
Every product attribute in Magento has a scope setting with three possible values: global, website or store view. Only attributes with store view scope can actually carry different values per language, whereas technical attributes such as SKU or weight are usually set to global, because they must remain identical regardless of language. For multilingual product attributes such as name, description, short description and meta data, store view scope is the only sensible setting, since this content is by definition language-dependent.
A common configuration mistake occurs when an attribute meant to be translatable is accidentally set to website scope instead of store view scope. In this case different values can be maintained per website, but not per store view within the same website, which immediately becomes a problem for countries with multiple languages sharing a website, for example Belgium with Dutch and French. The scope of an existing attribute can be changed later, but it requires a full reindex and should be carefully planned before going into production.
<?php
declare(strict_types=1);
namespace Mironsoft\ProductTranslation\Setup\Patch\Data;
use Magento\Eav\Setup\EavSetup;
use Magento\Eav\Setup\EavSetupFactory;
use Magento\Framework\Setup\ModuleDataSetupInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Catalog\Model\Product;
/**
* Adds a store-view-scoped attribute for a translated marketing headline.
*/
final class AddMarketingHeadlineAttribute implements DataPatchInterface
{
/**
* @param ModuleDataSetupInterface $moduleDataSetup Setup connection wrapper
* @param EavSetupFactory $eavSetupFactory Creates EAV setup helper instances
*/
public function __construct(
private readonly ModuleDataSetupInterface $moduleDataSetup,
private readonly EavSetupFactory $eavSetupFactory
) {
}
/**
* Create the translatable "marketing_headline" attribute with store view scope.
*
* @return void
*/
public function apply(): void
{
$this->moduleDataSetup->getConnection()->startSetup();
/** @var EavSetup $eavSetup */
$eavSetup = $this->eavSetupFactory->create(['setup' => $this->moduleDataSetup]);
$eavSetup->addAttribute(
Product::ENTITY,
'marketing_headline',
[
'type' => 'varchar',
'label' => 'Marketing Headline',
'input' => 'text',
'required' => false,
'global' => \Magento\Eav\Model\Entity\Attribute\ScopedAttributeInterface::SCOPE_STORE,
'group' => 'General',
'visible' => true,
'used_in_product_listing' => true,
]
);
$this->moduleDataSetup->getConnection()->endSetup();
}
/**
* @return array Dependencies executed before this patch
*/
public static function getDependencies(): array
{
return [];
}
/**
* @return array Aliases for this patch
*/
public function getAliases(): array
{
return [];
}
}
3. The fallback mechanism in detail
The fallback mechanism for multilingual product attributes is technically anchored in the catalog_product_entity_varchar table and its sibling tables for other data types: a value with store_id = 0 counts as the global default value, values with a specific store_id override this default for the respective store view. If no specific entry exists for a store view, Magento automatically reads the value with store_id = 0 and displays it, without this being flagged as a missing translation in the backend.
This behavior is deliberately designed this way, because it prevents a product from being displayed without a title at all, just because a translation is still pending. The downside: without an additional control mechanism, it stays invisible which products are actually fully translated in which language and which merely fall back to the default value. For editorial teams that need to systematically check whether all multilingual product attributes are maintained, the standard product view in the backend is therefore not sufficient.
-- Find products where the French store view (store_id = 4) has no
-- explicit name override and therefore falls back to store_id = 0
SELECT e.entity_id, e.sku
FROM catalog_product_entity e
WHERE NOT EXISTS (
SELECT 1
FROM catalog_product_entity_varchar v
INNER JOIN eav_attribute a ON a.attribute_id = v.attribute_id
WHERE a.attribute_code = 'name'
AND v.entity_id = e.entity_id
AND v.store_id = 4
);
-- Count translation completeness per store for the "description" attribute
SELECT v.store_id, COUNT(DISTINCT v.entity_id) AS translated_products
FROM catalog_product_entity_varchar v
INNER JOIN eav_attribute a ON a.attribute_id = v.attribute_id
WHERE a.attribute_code = 'description'
GROUP BY v.store_id;
4. Creating a custom translatable attribute via setup patch
For a new custom attribute that extends multilingual product attributes with marketing-relevant content, for example a campaign-specific headline, a data patch is the clean approach. The decisive setting is global with the value ScopedAttributeInterface::SCOPE_STORE, which makes the attribute translatable at store view level. Without this explicit declaration, Magento would set the attribute to global scope by default, which is unsuitable for translation purposes.
After creating such an attribute, it should be verified in the admin backend that it is indeed separately editable in every store view, visible via the store view switch above the input field in the product form. This visual confirmation is the fastest way to spot a misconfigured scope before content editors start entering translations that could later be lost through a scope correction.
5. CSV import with a store view column
For bulk maintenance of multilingual product attributes, the native product import via bin/magento or the backend import tool is the most practical approach. The CSV file needs a store_view_code column for this, specifying per row which store view the remaining column values apply to. A row without store_view_code, or with an empty value, writes to the global default value with store_id = 0, while a row with a set code, for example fr, writes exclusively the store-view-specific translation.
A common mistake during CSV import is assuming that a fully separate CSV row with all attributes is needed for every language. In fact, a pure translation row only needs SKU, store_view_code and the attributes to be translated, all other attributes remain empty and are not overwritten. Anyone who accidentally carries all columns with empty values in a translation row risks import tools interpreting these empty values as a deliberate deletion, depending on the chosen import behavior.
sku,store_view_code,name,description,short_description
WIDGET-001,,Widget (Default),"Default English description",Short default text
WIDGET-001,fr,Widget Français,"Description en français complète",Texte court en francais
WIDGET-001,de,Widget Deutsch,"Vollstaendige deutsche Beschreibung",Kurzer deutscher Text
# Import via CLI, dry-run first to catch mapping errors before writing
bin/magento catalog:products:import --behavior=append --file=translations.csv --dry-run
bin/magento catalog:products:import --behavior=append --file=translations.csv
6. Programmatic translation maintenance with DataPatch
For translations meant to be part of a versioned deployment, for example initial catalog data for a new market, a data patch is the more robust alternative to CSV import. Such a patch loads the product via the repository, explicitly sets the store context, and saves the translated attribute version targeted at the respective store view. This method is more effort than a CSV import, but fully versioned and reproducible across environments, which is essential for multilingual product attributes in CI/CD pipelines.
An important detail: the ProductRepositoryInterface::save() call must be made with a product explicitly loaded via setStoreId() in the desired store context, otherwise Magento accidentally writes the change to the default scope instead of the intended store view. This mistake is one of the most common when programmatically maintaining multilingual content, and hard to debug because the save operation itself succeeds without any error message.
<?php
declare(strict_types=1);
namespace Mironsoft\ProductTranslation\Setup\Patch\Data;
use Magento\Catalog\Api\ProductRepositoryInterface;
use Magento\Framework\Setup\Patch\DataPatchInterface;
use Magento\Store\Api\StoreRepositoryInterface;
/**
* Sets an initial French translation for the marketing headline attribute.
*/
final class TranslateMarketingHeadlineFr implements DataPatchInterface
{
/**
* @param ProductRepositoryInterface $productRepository Loads/saves products
* @param StoreRepositoryInterface $storeRepository Resolves the "fr" store view
*/
public function __construct(
private readonly ProductRepositoryInterface $productRepository,
private readonly StoreRepositoryInterface $storeRepository
) {
}
/**
* @return void
* @throws \Magento\Framework\Exception\NoSuchEntityException
* @throws \Magento\Framework\Exception\CouldNotSaveException
*/
public function apply(): void
{
$frStore = $this->storeRepository->get('fr');
// WRONG: loading without setStoreId() writes back to store_id = 0
// $product = $this->productRepository->get('WIDGET-001');
// RIGHT: explicitly load in the target store context
$product = $this->productRepository->get('WIDGET-001', false, (int) $frStore->getId());
$product->setData('marketing_headline', 'Offre exclusive de rentree');
$this->productRepository->save($product);
}
/**
* @return array Dependencies executed before this patch
*/
public static function getDependencies(): array
{
return [];
}
/**
* @return array Aliases for this patch
*/
public function getAliases(): array
{
return [];
}
}
7. Don't forget categories and static blocks
Discussions about multilingual product attributes often focus exclusively on product data, yet category names and category descriptions follow the same EAV fallback mechanism. A frequently overlooked case: navigation in a not-yet-fully-translated store view shows English category names alongside French product names, which looks inconsistent, but is technically exactly correct fallback behavior.
Static blocks, for example for banners or legal notices on category pages, are technically not EAV attributes but their own CMS entities with store assignment via a separate mapping table. There is no automatic fallback to a default store for them, a missing block in a store view simply stays empty. Anyone wanting to offer a truly complete multilingual experience must therefore check both EAV attributes and CMS content separately for completeness.
8. Systematically finding translation gaps
For shops with several thousand products and multiple languages, manually checking translation completeness is not practical. A custom admin grid that, per attribute and store view, compares the number of actually translated products against the total number of products is the most reliable way to systematically uncover gaps in multilingual product attributes. Such a grid can be implemented with a custom collection based on the SQL queries shown earlier.
For automated quality assurance, a weekly cron job that emails a translation report to the editorial team whenever the share of untranslated products in a store view exceeds a defined threshold is also advisable. This proactive monitoring prevents translation gaps from only surfacing through customer complaints once a new product import has introduced larger amounts of untranslated items.
<?php
declare(strict_types=1);
namespace Mironsoft\ProductTranslation\Model;
use Magento\Framework\App\ResourceConnection;
/**
* Computes translation completeness ratios per store view and attribute.
*/
final class TranslationCompletenessReport
{
/**
* @param ResourceConnection $resourceConnection Direct DB access for reporting
*/
public function __construct(
private readonly ResourceConnection $resourceConnection
) {
}
/**
* @param string $attributeCode e.g. "name" or "description"
* @return array<int, array{store_id: int, translated: int, total: int}>
*/
public function getCompletenessPerStore(string $attributeCode): array
{
$connection = $this->resourceConnection->getConnection();
$select = $connection->select()
->from(['v' => 'catalog_product_entity_varchar'], ['store_id', 'translated' => 'COUNT(DISTINCT entity_id)'])
->joinInner(['a' => 'eav_attribute'], 'a.attribute_id = v.attribute_id', [])
->where('a.attribute_code = ?', $attributeCode)
->group('v.store_id');
return $connection->fetchAll($select);
}
}
9. Attribute scope strategies compared
The following overview shows which scope makes sense for different attribute types in multilingual product attributes, and what consequences a wrong choice has.
| Attribute | Wrong scope | Recommended scope | Rationale |
|---|---|---|---|
| Product name | Global | Store view | Name must be translatable per language |
| SKU | Store view | Global | Identifier must remain system-wide unique |
| Meta description | Global | Store view | SEO text must be language-specific |
| Weight | Store view | Global | Physical value independent of language |
| Safety notices | Website | Store view | Legal requirements vary per language/country |
The basic rule remains simple: everything a human reads and understands belongs at store view level. Everything the system technically identifies or physically measures belongs at global level. This rule of thumb reliably resolves the vast majority of scope decisions for multilingual product attributes.
Mironsoft
Magento 2 multi store and internationalization
Multilingual catalogs without silent translation gaps?
We review EAV attribute scopes, set up CSV import workflows with store view columns, and build monitoring grids so missing translations surface before customers see them.
Scope review
Check existing attributes for wrong global or website scope
Import workflows
Set up CSV templates with store_view_code for editorial teams
Completeness monitoring
Custom admin grid and email reports for translation gaps
10. Summary
Managing multilingual product attributes in Magento 2 stands and falls with the correct attribute scope: only attributes with store view scope can genuinely carry different values per language. The fallback mechanism to store_id = 0 prevents empty displays, but makes translation gaps invisible unless additional checks exist. CSV import with a store_view_code column and data patches with an explicit store context are the two robust ways to maintain translations.
Anyone who factors categories and static blocks into translation planning, and sets up systematic monitoring for translation completeness, avoids the most common disappointment of international rollouts: a catalog that is technically configured as multilingual but shows gaps in practice, gaps that only surface through customer feedback.
Magento 2 Multilingual Product Attributes — Key Takeaways
Store view scope is mandatory
Only attributes with SCOPE_STORE can carry different values per language. Global scope attributes always stay identical.
Fallback to store_id = 0
If a store view value is missing, Magento silently shows the default value. Without monitoring this stays invisible.
CSV import with store_view_code
Only fill in SKU, store_view_code and the attributes to translate per row, leave remaining columns empty.
Don't forget categories and CMS
Category names follow the same EAV fallback, static blocks have no fallback and stay empty without a translation.