Intro
I've seen a lot of questions about the best way to integrate your Shopify app content with Shopify's Translate & Adapt app and allowing merchants to translate your apps content.
For example, if you are extending Shopify Checkout and would like merchants to be able to customise the translated content.
I've worked on this for a number of Shopify apps, so thought I'd share an approach.
Content
I believe the best way to allow custom translations of your apps content is to store the content in Shopify Metaobjects.
Metaobjects are pieces of content, which can easily be used across the Shopify Platform, either in Liquid or Storefront API. You can enable them to be translatable as well, which is what we will use later!
Ensure when you create the definitions they are not using app reserved prefix, e.g. beginning with $app
, as otherwise Translate & Adapt app won't be able to access it.
Depending on how much content you want to translate you've got a couple of options here on designing your metaobject definition(s).
You may already have metaobjects or metafields with config for your app, I believe it's best to separate the content and config.
Specific Definitions
You could create one or many metaobject definitions, where each piece of content is a single_line_text_field
.
The advantages of this are that if you have different content for different sections of your app, its easily understandable from the merchant's point of view.
For example, if you have content for the checkout, and their website. You could create two separate metaobject definitions, when this is displayed in Translate & Adapt app, it will be clear to the merchant, from the definition name and description, what the content will be used for.
However, there are some limits here. Firstly, you will be using the metaobject definition limits, which are fairly limited at the time of writing. Its best to be conscious that the merchant will be using other apps, which also could use definitions, so best to not be greedy with the number of definitions you create.
Also, there is a limit of 40 field definitions per metaobject definition, so if you have a limited amount of content the merchants can translate or can consolidate it down to 40 then this will work for you. Otherwise, you may want to create multiple metaobject definitions or consider the Extensible approach below.
Here is an example of what that looks like in Admin GraphQL API 2024-10.
mutation CreateMetaobjectDefinition($definition: MetaobjectDefinitionCreateInput!) {
metaobjectDefinitionCreate(definition: $definition) {
metaobjectDefinition {
name
type
fieldDefinitions {
name
key
}
}
userErrors {
field
message
code
}
}
}
Variables:
{
"definition": {
"name": "My App Checkout Translations",
"description": "Translations for the checkout section of my app.",
"type": "my-app-checkout-translations",
"fieldDefinitions": [
{
"key": "title",
"name": "Title",
"description": "The title of the checkout section",
"required": true,
"type": "single_line_text_field"
},
{
"key": "subtitle",
"name": "Subtitle",
"description": "The subtitle of the checkout section",
"required": true,
"type": "single_line_text_field"
}
],
"capabilities": {
"translatable": {
"enabled": true
}
}
}
}
Extensible
If you have a lot of content the merchant can translate and don't want to register specific definitions. What you can do is register a single metaobject definition.
Where you would create a metaobject definition, with a key and value. You may also want to register a description field, just to help the merchant understand where the content will be used.
One thing to note here, is that you'll need to be careful as the key can be translated by the merchant, just something to watch out for when retrieving the data.
mutation CreateMetaobjectDefinition($definition: MetaobjectDefinitionCreateInput!) {
metaobjectDefinitionCreate(definition: $definition) {
metaobjectDefinition {
name
type
fieldDefinitions {
name
key
}
}
userErrors {
field
message
code
}
}
}
Variables:
{
"definition": {
"name": "My App Translations",
"description": "Translations for my app.",
"type": "my-app-translations",
"fieldDefinitions": [
{
"key": "key",
"name": "Key",
"required": true,
"type": "single_line_text_field"
},
{
"key": "value",
"name": "Value",
"required": true,
"type": "single_line_text_field"
},
],
"capabilities": {
"translatable": {
"enabled": true
}
}
}
}
Translations
Now we have our metaobject definitions, it's time to add content and translate it.
Fortunately, Shopify provides this out of the box!
Firstly, create your metaobject, based on the definition you created earlier.
mutation CreateMetaobject($metaobject: MetaobjectCreateInput!) {
metaobjectCreate(metaobject: $metaobject) {
userErrors {
field
message
code
}
}
}
Variables:
{
"metaobject": {
"type": "my-app-checkout-translations",
"fields": [
{
"key": "title",
"value": "My App Checkout"
},
{
"key": "subtitle",
"value": "Something to do with your order at checkout."
},
]
}
}
Next you need to get the translatable content for your metaobject, this will give you the digest and key that you can use when registering translations.
query {
translatableResource(resourceId: "gid://shopify/Metaobject/1234567") {
resourceId
translatableContent {
key
value
digest
locale
}
}
}
Then you can register your translations using the translationsRegister mutation. You can also remove translations via a similar mutation.
mutation CreateTranslation($id: ID!, $translations: [TranslationInput!]!) {
translationsRegister(resourceId: $id, translations: $translations) {
userErrors {
message
field
}
translations {
locale
key
value
}
}
}
Variables:
{
"id": "gid://shopify/Metaobject/1234567",
"translations": [
{
"key": "title",
"value": "Paiement de mon application",
"locale": "fr",
"translatableContentDigest": "dcf8d211f6633dac78dbd15c219a81b8931e4141204d18fba8c477afd19b75f9"
},
{
"key": "subtitle",
"value": "Quelque chose à faire avec votre commande à la caisse.",
"locale": "fr",
"translatableContentDigest": "a18b34037fda5b1afd720d4b85b86a8a75b5e389452f84f5b6d2b8e210869fd7"
}
]
}
Here you specify the language and the translated content. This allows you to provide translations if you want to. If you also want to allow merchants to translate content in your own app and interface, you can use this to store the translations in Shopify.
Gotcha
There is a gotcha with this approach. You cannot register translations in the stores default language.
This means you do have to do some management here, its best explained with an example.
If your default app content is in English, you can create your metaobject definitions.
You create the metaobject with the fields you defined, if the merchants default language is English, the content here can be English.
You can then register translations of this content in other languages, or the merchant can via Translate & Adapt app.
However, if the merchants default language is Japanese, then you would need to create the metaobject content in either Japanese, if you have that available or ensure the merchant knows they will need to change the default content to Japanese. Which they can do inside of Shopify Metaobjects section.
As you won't be able to register translations in Japanese.
It’s just a gotcha when registering translations. I’m hoping the Shopify team will remove this limitation so content can be registered in any language as it would make management of this much easier.
Displaying Content
Once you've set this all up. Merchants will be able to translate your content in Translate & Adapt app, and/or you can programmatically register them using Shopify APIs.
You can then access these translations. In Liquid the translated content will automatically be displayed based on the user's language.
If you are using the Storefront API, you can use the @inContext
directive. You set the language you'd like the content in and Shopify will automatically look it up for you, or return the default content if not available in that specific language.
You can also always get the translations from the Admin GraphQL API, using the translatableResource and the translations list returned.
Translate & Adapt
You can then deep link into the Shopify Translate & Adapt app using the Metaobject ID. For example, if your app is embedded in Shopify you can use this style of link:
shopify://admin/apps/translate-and-adapt/localize/metaobject?shopLocale=fr&id=1234567
Changing the shopLocale, market and id query parameters to the relevant ones for your metaobjects.
Summary
In summary, you can use Shopify Platform to easily allow merchants to translate and manage your apps content, wherever you might be displaying it.
This is a less known but very powerful way to extend the Shopify Platform and your apps, without having to build out your own translation engine!
Top comments (0)