Manage app translations with the GraphQL Admin API
This guide explains how to use the GraphQL Admin API to create and retrieve translated content for Shopify resources. It also covers how to retrieve translated content using the Storefront API. For example, you might add translations of product information and email notification templates using the GraphQL Admin API so a merchant can send customers email notifications in multiple languages. Those translations can then be retrieved by either the GraphQL Admin API or the Storefront API.
GraphQL Admin API translation access scopes
To use the GraphQL Admin API to create or retrieve translated content, your app needs to request the read_translations
and write_translations
access scopes for a Shopify store. For more information on requesting access scopes when your app is installed, see OAuth.
Storefront API translation header
To use the Storefront API to retrieve translated content, your app needs to use the Accept-Language
HTTP request header when sending queries. The following example header enables Spanish translations to be returned using the Storefront API: Accept-Language: es
GraphQL Admin API translatable resource types
The following resource types and fields are translatable:
Type | Fields |
---|---|
Collection | title , body_html , meta_title , meta_description |
Email template | title , body_html |
Link | title |
Metafields (non-private) | value |
Online store article | title , body_html , summary_html , meta_title , meta_description |
Online store blog | title , meta_title , meta_description |
Online store page | title , body_html , meta_title , meta_description |
Online store theme | dynamic keys based on theme data |
Payment gateway | name |
Product | title , body_html , meta_title , meta_description |
Product option | name |
Product variant | title , option 1 , option 2 , option 3 |
Shop | meta_title , meta_description |
Shop policy | body |
SMS template | body |
Retrieve a list of translatable resource types
TranslatableResourceType contains a list of the resource types that are translatable. You can use the following query to list each of those types:
Query
{
__type(name: "TranslatableResourceType") {
enumValues {
name
}
}
}
Response
{
"data": {
"__type": {
"enumValues": [
{
"name": "PRODUCT"
},
{
"name": "PRODUCT_VARIANT"
},
...
]
}
}
}
Storefront API translation resources
The following resources include properties that can be retrieved using the Storefront API:
Type | Fields |
---|---|
Collection | title , descriptionHtml , description |
Metafield | value |
Article | title , content , contentHtml , excerpt , excerptHtml |
Blog | title |
Page | title , body |
Product | title , descriptionHtml , description |
ProductOption | name , values |
ProductVariant | title , selectedOptions |
ShopPolicy | body |
Retrieving translations
You can use the translateableResources connection to retrieve a list of translatable resources, their translatable content, and their existing translations in various languages. You can also retrieve all translations for a locale, regardless of resource type.
- Retrieve a list of translatable resource types
- Retrieve a list of translatable resources by their type
- Retrieve translations for a resource type and locale
- Retrieve a single translatable resource by its ID
- Retrieve translations from a resource object
Retrieve a list of translatable resources by their type
The following query retrieves a list of translatable resources that are products. For each product, the query retrieves the content that can be translated in the translatableContent
field.
Query
{
translatableResources(first: 10, resourceType: PRODUCT) {
edges {
node {
resourceId
translatableContent {
key
value
digest
locale
}
}
}
}
}
Response
{
"translatableResources": {
"edges": [
{
"node": {
"resourceId": "gid://shopify/Product/1",
"translatableContent": [
{
"key": "title",
"value": "White T-Shirt",
"digest": "2bb4bfd1fe83ba39eaca5669e35bfb2042476ca6e8d794b7efd814d6c0cd702c",
"locale": "en-US"
},
{
"key": "body_html",
"value": "A classic white crew neck t-shirt made from 100% cotton.",
"digest": "3abcaa3c9f48a4c7613d5479aee69be82fe2818b9d38553b74cc80d20b080b22",
"locale": "en-US"
},
...
]
}
},
...
]
}
}
The response shows that the White T-Shirt product with the ID "gid://shopify/Product/1"
has content that can be translated, including its title and body HTML.
The key
field associates translatable content with its translations. The value
field shows the translatable content itself. The digest
field is the translatable content's unique identifier. When you create a translation, the mutation needs to include the translatable content's digest value in the translatableContentDigest field.
Retrieve translations for a resource type and locale
Like the example above, this query retrieves a list of products and their translatable content. This query also includes the translations
field for each product, which retrieves any existing translations.
The field takes the argument locale
, which specifies the language of the translations to retrieve. In the following example, the language is Spanish.
Query
{
translatableResources(first:10, resourceType: PRODUCT) {
edges {
node {
resourceId
translatableContent {
key
value
digest
locale
}
translations(locale: "es-ES"){
locale
key
value
}
}
}
}
}
Response
{
"data" {
"translatableResources": {
"edges": [
{
"node": {
"resourceId": "gid://shopify/Product/1",
"translatableContent": [
{
"key": "title",
"value": "White T-Shirt",
"digest": "2bb4bfd1fe83ba39eaca5669e35bfb2042476ca6e8d794b7efd814d6c0cd702c",
"locale": "en-US"
},
...
],
"translations": [
{
"locale": "es-ES",
"key": "title",
"value": "Camiseta blanca"
},
...
]
}
},
...
]
}
}
}
Retrieve a single translatable resource by its ID
The following query retrieves a single translatable resource by passing its ID in the resourceId
argument.
Query
{
translatableResource(resourceId: "gid://shopify/Product/1") {
resourceId
translatableContent {
key
value
digest
locale
}
translations(locale: "ja") {
key
value
locale
}
}
}
Response
{
"data": {
"translatableResource": {
"resourceId": "gid://shopify/Product/1",
"translatableContent": [
{
"key": "title",
"value": "White T-Shirt",
"digest": "2bb4bfd1fe83ba39eaca5669e35bfb2042476ca6e8d794b7efd814d6c0cd702c",
"locale": "en-US",
},
...
],
"translations": [
{
"key": "title",
"value": "白いティーシャツ",
"locale": "ja",
},
...
]
}
}
}
Retrieve translations from a resource object
If your app only needs to read translations, then you can query translations directly from a translatable resource object.
Query
query {
products(first: 1) {
edges {
node{
id,
title,
translations(locale: "ja") {
key,
locale,
value
}
}
}
}
}
Response
{
"data": {
"products": {
"edges": [
{
"node": {
"id": "gid://shopify/Product/355272313",
"title": "T-shirt blanc",
"translations": [
{
"key": "title",
"locale": "ja",
"value": "白いティーシャツ"
}
]
}
}
]
}
}
}
Creating translations
GraphQL uses mutations to create, update, or delete data in a database. Mutations do the equivalent function of the REST data-modifying action verbs, such as POST, PUT, and DELETE.
You can use the translationsRegister mutation to create new translations for a resource. When you create a translation, the mutation needs to include the translatable content's digest
value in the translatableContentDigest
input field.
Variables
{
"id": "gid://shopify/Product/1",
"translations": [
{
"key": "title",
"value": "Camiseta blanca",
"locale": "es-ES",
"translatableContentDigest": "2bb4bfd1fe83ba39eaca5669e35bfb2042476ca6e8d794b7efd814d6c0cd702c"
}
]
}
Mutation
mutation CreateTranslation($id: ID!, $translations: [TranslationInput!]!) {
translationsRegister(resourceId: $id, translations: $translations) {
userErrors {
message
field
}
translations {
locale
key
value
}
}
}
Response
{
"data": {
"translationsRegister": {
"userErrors": [],
"translations": [
{
"locale": "es-ES",
"key": "title",
"value": "Camiseta buena"
}
]
}
}
}
Mutation errors
The following table shows codes and descriptions for errors that you might see when you create translations:
Error message | Description |
---|---|
RESOURCE_NOT_FOUND |
A resource with the specified ID doesn't exist. |
TOO_MANY_KEYS_FOR_RESOURCE |
There are too many translation keys for the resource. You can include up to 100 translation keys per mutation. |
INVALID_KEY_FOR_MODEL |
The key provided for the translation is invalid. |
FAILS_RESOURCE_VALIDATION |
The value for the translation is invalid. |
INVALID_TRANSLATABLE_CONTENT |
The translatable content doesn't match the original content on the resource. |
INVALID_CODE |
The language code is invalid. |
INVALID_FORMAT |
The locale code is invalid. |
Retrieving Storefront API translations
You can query the supported resources using the Accept-Language
HTTP header, to return translated content to the Storefront API.
The following example query returns translated content for Product title in Spanish:
Header
Accept-Language: es
Variables
{
"ids": [
"Z2lkOi8vc2hvcGlmeS9Qcm9kdWN0LzgwOTI1NDg5OTg="
]
}
Query
query translatedThings($ids: [ID!]!) {
nodes(ids: $ids) {
... on Product {
__typename
title
description
descriptionHtml
options {
__typename
name
values
}
}
}
}
Response
{
"data": {
"nodes": [
{
"__typename": "Product",
"title": "Camiseta blanca",
"description": "primeiro producto de teste",
"descriptionHtml": "<p> primero producto de teste </p>",
"options": [
{
"__typename": "ProductOption",
"name": "Talla",
"values": [
"Pequeña",
"Media",
"Grande"
]
}
]
}
]
}
}
Removing translations
You can use the translationsRemove mutation to remove translations from a resource. In the following example, the Spanish and Japanese title translations are removed from the product with the ID "gid://shopify/Product/1"
.
Variables
{
"id": "gid://shopify/Product/1",
"keys": [
"title"
],
"locales": [
"es-ES",
"ja-JP"
]
}
Mutation
mutation RemoveTranslations($id: ID!, $keys: [String!]!, $locales: [String!]!) {
translationsRemove(
resourceId: $id,
translationKeys: $keys,
locales: $locales
) {
userErrors {
message
field
}
translations {
locale
key
value
}
}
}
Response
{
"data": {
"translationsRemove": {
"userErrors": [],
"translations": [
{
"locale": "es-ES",
"key": "title",
"value": "Camiseta blanca",
},
{
"locale": "es-ES",
"key": "body_html",
"value": "Camiseta blanca",
},
]
}
}
}
Limitations
The following are known translation limitations:
- A resource's
tags
field can't be translated. For example, you can't translate a product's tags. - Email and SMS notification template fields cannot be translated (unless you edit the default template content).
- The translation response that's returned for menu links and email and SMS notification templates doesn't include the
handle
field. - If the merchant changes a product's
handle
, then URL redirects for that product won't be supported for language-specific URLs. - You can translate
metafields
only if they are publicly accessible. - App proxies don't have multi-language support.