Managing Multilingual Product Attributes in Magento 2
AI generated
M2
di.xml
Magento 2 · Multi Store · Internationalization
Magento 2 Multilingual Product Attributes
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.

18 min read EAV · Store scope · CSV import · DataPatch Magento 2.4.x

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.

11. FAQ: Magento 2 Multilingual Product Attributes

1Which scope for translatable attributes?
Store view scope, SCOPE_STORE. Only this allows different values per store view.
2What happens with a missing translation?
Fallback to store_id = 0, the global default value, invisible as a gap in the backend.
3How do I import translations via CSV?
With the store_view_code column, only fill SKU, code and attributes to translate.
4Why does the data patch translation land wrong?
Because setStoreId() is missing before saving, so save() writes to the default scope.
5Treat category names like product attributes?
Yes, same EAV fallback mechanism, also check for completeness.
6Do static blocks have a fallback?
No, without a fallback, a missing block in a store view stays empty.
7How do I find translation gaps systematically?
Custom admin grid with SQL queries against the EAV tables per attribute and store view.
8Should SKU differ per language?
No, SKU stays global scope, a system-wide unique identifier.
9Can I change scope later?
Yes, but requires full reindex and careful testing before production use.
10How do I monitor completeness ongoing?
Weekly cron job with email report when a threshold is exceeded.