diff --git a/modules/product/commerce_product.module b/modules/product/commerce_product.module
index 914e638b15410b04c669678cda09643430d07d24..083a0df60e354609beba57a873b4a814cff20684 100644
--- a/modules/product/commerce_product.module
+++ b/modules/product/commerce_product.module
@@ -407,3 +407,27 @@ function commerce_product_field_group_content_element_keys_alter(&$keys) {
   $keys['commerce_product'] = 'product';
   $keys['commerce_product_variation'] = 'product_variation';
 }
+
+/**
+ * Implements hook_entity_operation_alter().
+ */
+function commerce_product_entity_operation_alter(array &$operations, EntityInterface $entity): void {
+  // For the 'commerce_product_attribute' entity type when the 'translate'
+  // operation does not exist we need to check if the user has access to manage
+  // translations.
+  if ($entity->getEntityTypeId() !== 'commerce_product_attribute'
+    || !$entity->hasLinkTemplate('config-translation-overview')
+    || isset($operations['translate'])
+  ) {
+    return;
+  }
+
+  $url = $entity->toUrl('config-translation-overview');
+  if ($url->access()) {
+    $operations['translate'] = [
+      'title' => t('Translate'),
+      'weight' => 50,
+      'url' => $url,
+    ];
+  }
+}
diff --git a/modules/product/commerce_product.permissions.yml b/modules/product/commerce_product.permissions.yml
index 2f56ba5e89a2275852e6ca4da102def5c03f177f..a147db9a785aa7d794bb74d0d796697cdc68f022 100644
--- a/modules/product/commerce_product.permissions.yml
+++ b/modules/product/commerce_product.permissions.yml
@@ -2,3 +2,8 @@
   title: 'Administer product types'
   description: 'Maintain the types of products available and the fields that are associated with those types.'
   restrict access: true
+
+'translate commerce_product_attribute':
+  title: 'Translate product attribute'
+  description: 'Translate any product attribute.'
+  restrict access: true
diff --git a/modules/product/commerce_product.services.yml b/modules/product/commerce_product.services.yml
index ef2f0c9053425785715da0ee8f09c649e4bce6f1..ecdabc732ddb759cf491e830c8f3028047dce5db 100644
--- a/modules/product/commerce_product.services.yml
+++ b/modules/product/commerce_product.services.yml
@@ -11,6 +11,7 @@ services:
     tags:
       - { name: access_check, applies_to: _product_variation_create_access }
 
+
   commerce_product.attribute_field_manager:
     class: Drupal\commerce_product\ProductAttributeFieldManager
     arguments: ['@entity_field.manager', '@entity_type.bundle.info', '@entity_type.manager', '@cache.data']
diff --git a/modules/product/src/Access/ProductAttributeTranslationAccessCheck.php b/modules/product/src/Access/ProductAttributeTranslationAccessCheck.php
new file mode 100644
index 0000000000000000000000000000000000000000..779feaab7d93772af4be0554918651f88a337baa
--- /dev/null
+++ b/modules/product/src/Access/ProductAttributeTranslationAccessCheck.php
@@ -0,0 +1,31 @@
+<?php
+
+namespace Drupal\commerce_product\Access;
+
+use Drupal\config_translation\Access\ConfigTranslationOverviewAccess;
+use Drupal\config_translation\ConfigMapperInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Checks access for displaying the product attribute translation overview.
+ */
+class ProductAttributeTranslationAccessCheck extends ConfigTranslationOverviewAccess {
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doCheckAccess(AccountInterface $account, ConfigMapperInterface $mapper, $source_language = NULL) {
+    $permission_access = $account->hasPermission('translate commerce_product_attribute') ||
+      $account->hasPermission('translate configuration');
+
+    $access =
+      $permission_access &&
+      $mapper->hasSchema() &&
+      $mapper->hasTranslatable() &&
+      (!$source_language || !$source_language->isLocked());
+
+    return AccessResult::allowedIf($access)->cachePerPermissions();
+  }
+
+}
diff --git a/modules/product/src/Access/ProductAttributeTranslationFormAccessCheck.php b/modules/product/src/Access/ProductAttributeTranslationFormAccessCheck.php
new file mode 100644
index 0000000000000000000000000000000000000000..f29a449c2d39cbfa73d32437c093c0ee92a42e09
--- /dev/null
+++ b/modules/product/src/Access/ProductAttributeTranslationFormAccessCheck.php
@@ -0,0 +1,45 @@
+<?php
+
+namespace Drupal\commerce_product\Access;
+
+use Drupal\config_translation\Access\ConfigTranslationFormAccess;
+use Drupal\config_translation\ConfigMapperInterface;
+use Drupal\config_translation\ConfigMapperManagerInterface;
+use Drupal\Core\Access\AccessResult;
+use Drupal\Core\Language\LanguageManagerInterface;
+use Drupal\Core\Session\AccountInterface;
+
+/**
+ * Checks access for displaying the product attribute add, edit, delete forms.
+ */
+class ProductAttributeTranslationFormAccessCheck extends ConfigTranslationFormAccess {
+
+  /**
+   * Constructs a new ProductAttributeTranslationFormAccessCheck object.
+   *
+   * @param \Drupal\config_translation\ConfigMapperManagerInterface $config_mapper_manager
+   *   The mapper plugin discovery service.
+   * @param \Drupal\Core\Language\LanguageManagerInterface $language_manager
+   *   The language manager service.
+   * @param \Drupal\commerce_product\Access\ProductAttributeTranslationAccessCheck $translationAccessCheck
+   *   The main access check service.
+   */
+  public function __construct(ConfigMapperManagerInterface $config_mapper_manager, LanguageManagerInterface $language_manager, protected ProductAttributeTranslationAccessCheck $translationAccessCheck) {
+    parent::__construct($config_mapper_manager, $language_manager);
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  protected function doCheckAccess(AccountInterface $account, ConfigMapperInterface $mapper, $source_language = NULL, $target_language = NULL) {
+    $base_access_result = $this->translationAccessCheck->doCheckAccess($account, $mapper, $source_language);
+
+    $access =
+      $target_language &&
+      !$target_language->isLocked() &&
+      (!$source_language || ($target_language->getId() !== $source_language->getId()));
+
+    return $base_access_result->andIf(AccessResult::allowedIf($access));
+  }
+
+}
diff --git a/modules/product/src/CommerceProductServiceProvider.php b/modules/product/src/CommerceProductServiceProvider.php
index 6f5619fd659c037fdf9fae5e743675a133494c80..ba09562e404a94db9b86ef47bf3bdb8cc3ebc9a7 100644
--- a/modules/product/src/CommerceProductServiceProvider.php
+++ b/modules/product/src/CommerceProductServiceProvider.php
@@ -2,6 +2,8 @@
 
 namespace Drupal\commerce_product;
 
+use Drupal\commerce_product\Access\ProductAttributeTranslationAccessCheck;
+use Drupal\commerce_product\Access\ProductAttributeTranslationFormAccessCheck;
 use Drupal\commerce_product\EventSubscriber\VariationFieldComponentSubscriber;
 use Drupal\Core\DependencyInjection\ContainerBuilder;
 use Drupal\Core\DependencyInjection\ServiceProviderBase;
@@ -38,6 +40,19 @@ class CommerceProductServiceProvider extends ServiceProviderBase {
         ->setClass(VariationFieldComponentSubscriber::class)
         ->addTag('event_subscriber');
     }
+
+    if (isset($modules['config_translation'])) {
+      $parent = $container->getDefinition('config_translation.access.overview');
+      $container->register('access_check.product_attribute_translation', ProductAttributeTranslationAccessCheck::class)
+        ->setArguments($parent->getArguments())
+        ->addTag('access_check', ['applies_to' => '_product_attribute_translation_access']);
+
+      $parent = $container->getDefinition('config_translation.access.form');
+      $container->register('access_check.product_attribute_translation.form', ProductAttributeTranslationFormAccessCheck::class)
+        ->setArguments($parent->getArguments())
+        ->addArgument(new Reference('access_check.product_attribute_translation'))
+        ->addTag('access_check', ['applies_to' => '_product_attribute_translation_form_access']);
+    }
   }
 
 }
diff --git a/modules/product/src/ConfigTranslation/ProductAttributeMapper.php b/modules/product/src/ConfigTranslation/ProductAttributeMapper.php
index 04ba3b39d066c6a1178cbb028f9650109e0e3a87..88a5fc8a586cf3800807371d8fabcad65ad21d8a 100644
--- a/modules/product/src/ConfigTranslation/ProductAttributeMapper.php
+++ b/modules/product/src/ConfigTranslation/ProductAttributeMapper.php
@@ -3,6 +3,7 @@
 namespace Drupal\commerce_product\ConfigTranslation;
 
 use Drupal\config_translation\ConfigEntityMapper;
+use Symfony\Component\Routing\Route;
 
 /**
  * Provides a configuration mapper for product attributes.
@@ -15,6 +16,7 @@ class ProductAttributeMapper extends ConfigEntityMapper {
   public function getAddRoute() {
     $route = parent::getAddRoute();
     $route->setDefault('_form', '\Drupal\commerce_product\Form\ProductAttributeTranslationAddForm');
+    $this->addTranslationFormAccessCheck($route);
     return $route;
   }
 
@@ -24,7 +26,42 @@ class ProductAttributeMapper extends ConfigEntityMapper {
   public function getEditRoute() {
     $route = parent::getEditRoute();
     $route->setDefault('_form', '\Drupal\commerce_product\Form\ProductAttributeTranslationEditForm');
+    $this->addTranslationFormAccessCheck($route);
     return $route;
   }
 
+  /**
+   * {@inheritdoc}
+   */
+  public function getDeleteRoute() {
+    $route = parent::getDeleteRoute();
+    $this->addTranslationFormAccessCheck($route);
+    return $route;
+  }
+
+  /**
+   * {@inheritdoc}
+   */
+  public function getOverviewRoute() {
+    $route = parent::getOverviewRoute();
+    $route_requirements = $route->getRequirements();
+    unset($route_requirements['_config_translation_overview_access']);
+    $route_requirements['_product_attribute_translation_access'] = 'TRUE';
+    $route->setRequirements($route_requirements);
+    return $route;
+  }
+
+  /**
+   * Modifies route to use custom access check.
+   *
+   * @param \Symfony\Component\Routing\Route $route
+   *   The route object.
+   */
+  protected function addTranslationFormAccessCheck(Route &$route): void {
+    $route_requirements = $route->getRequirements();
+    unset($route_requirements['_config_translation_form_access']);
+    $route_requirements['_product_attribute_translation_form_access'] = 'TRUE';
+    $route->setRequirements($route_requirements);
+  }
+
 }