Files
gh-sarojpunde-shopify-dev-t…/skills/shopify-i18n-basics/SKILL.md
2025-11-30 08:54:05 +08:00

7.7 KiB

name, description
name description
Shopify i18n Basics Internationalization fundamentals for multi-language Shopify themes

Shopify i18n Basics

Locale File Structure

Shopify themes use JSON files in the locales/ directory:

locales/
├── en.default.json          # English (default language)
├── en.default.schema.json   # English schema translations
├── fr.json                  # French translations
├── fr.schema.json           # French schema translations
├── de.json                  # German translations
└── de.schema.json           # German schema translations

File Types

1. Content Translations (en.default.json)

Used for theme content displayed to customers:

{
  "general": {
    "search": "Search",
    "cart": "Cart",
    "menu": "Menu",
    "close": "Close",
    "loading": "Loading...",
    "continue_shopping": "Continue Shopping"
  },
  "product": {
    "add_to_cart": "Add to Cart",
    "sold_out": "Sold Out",
    "unavailable": "Unavailable",
    "price": "Price",
    "vendor": "Vendor"
  },
  "cart": {
    "title": "Your Cart",
    "empty": "Your cart is empty",
    "subtotal": "Subtotal",
    "checkout": "Checkout"
  }
}

2. Schema Translations (en.default.schema.json)

Used for Theme Editor labels:

{
  "sections": {
    "featured_products": {
      "name": "Featured Products",
      "settings": {
        "heading": {
          "label": "Heading",
          "info": "Section heading text"
        },
        "collection": {
          "label": "Collection"
        }
      }
    }
  }
}

Using Translations in Liquid

Basic Translation

{{ 'general.search' | t }}
{{ 'product.add_to_cart' | t }}
{{ 'cart.title' | t }}

Translation with Variables

{%- # In locale file -%}
{
  "cart": {
    "item_count": "{{ count }} items"
  }
}

{%- # In Liquid -%}
{{ 'cart.item_count' | t: count: cart.item_count }}

Translation with HTML

{%- # In locale file -%}
{
  "general": {
    "continue_html": "Continue <span>shopping</span>"
  }
}

{%- # In Liquid -%}
{{ 'general.continue_html' | t }}

Translation in Schema

{% schema %}
{
  "name": "t:sections.featured_products.name",
  "settings": [
    {
      "type": "text",
      "id": "heading",
      "label": "t:sections.featured_products.settings.heading.label"
    }
  ]
}
{% endschema %}

Common Translation Patterns

General UI

{
  "general": {
    "search": "Search",
    "cart": "Cart",
    "menu": "Menu",
    "close": "Close",
    "loading": "Loading...",
    "buttons": {
      "learn_more": "Learn More",
      "shop_now": "Shop Now",
      "view_all": "View All"
    },
    "forms": {
      "submit": "Submit",
      "email": "Email",
      "name": "Name",
      "message": "Message"
    }
  }
}

Product-Specific

{
  "product": {
    "add_to_cart": "Add to Cart",
    "sold_out": "Sold Out",
    "unavailable": "Unavailable",
    "in_stock": "In Stock",
    "out_of_stock": "Out of Stock",
    "price": {
      "from": "From {{ price }}",
      "regular_price": "Regular price",
      "sale_price": "Sale price"
    }
  }
}

Cart & Checkout

{
  "cart": {
    "title": "Cart",
    "empty": "Your cart is empty",
    "item_count": "{{ count }} items",
    "subtotal": "Subtotal",
    "shipping": "Shipping",
    "total": "Total",
    "checkout": "Checkout",
    "remove": "Remove"
  }
}

Forms & Errors

{
  "forms": {
    "contact": {
      "name": "Name",
      "email": "Email",
      "message": "Message",
      "send": "Send Message",
      "success": "Thank you for your message!",
      "error": "Please correct the errors below"
    }
  },
  "errors": {
    "general": "An error occurred",
    "required_field": "This field is required",
    "invalid_email": "Please enter a valid email"
  }
}

Adding New Languages

Step 1: Create Translation Files

locales/fr.json (French content):

{
  "general": {
    "search": "Rechercher",
    "cart": "Panier",
    "menu": "Menu",
    "close": "Fermer"
  },
  "product": {
    "add_to_cart": "Ajouter au panier",
    "sold_out": "Épuisé"
  },
  "cart": {
    "title": "Votre panier",
    "empty": "Votre panier est vide",
    "checkout": "Commander"
  }
}

locales/fr.schema.json (French schema):

{
  "sections": {
    "featured_products": {
      "name": "Produits vedettes",
      "settings": {
        "heading": {
          "label": "Titre"
        },
        "collection": {
          "label": "Collection"
        }
      }
    }
  }
}

Step 2: Use in Liquid

The | t filter automatically uses the correct translation based on the store's language:

{%- # Automatically shows "Search" in English, "Rechercher" in French -%}
<button>{{ 'general.search' | t }}</button>

Best Practices

1. Use Clear, Descriptive Keys

{%- # Good -%}
{
  "product": {
    "add_to_cart": "Add to Cart"
  }
}

{%- # Bad -%}
{
  "prod": {
    "btn1": "Add to Cart"
  }
}

2. Organize Logically

{
  "general": { ... },      # General UI
  "navigation": { ... },   # Navigation items
  "product": { ... },      # Product-related
  "cart": { ... },         # Cart-related
  "forms": { ... }         # Forms
}

3. Keep Keys Consistent Across Languages

en.default.json:

{
  "product": {
    "add_to_cart": "Add to Cart"
  }
}

fr.json (same structure):

{
  "product": {
    "add_to_cart": "Ajouter au panier"
  }
}

4. Use Nested Objects for Clarity

{%- # Good -%}
{
  "forms": {
    "contact": {
      "name": "Name",
      "email": "Email"
    }
  }
}

{%- # Less clear -%}
{
  "forms": {
    "contact_name": "Name",
    "contact_email": "Email"
  }
}

5. Handle Pluralization

{
  "cart": {
    "item_count_one": "{{ count }} item",
    "item_count_other": "{{ count }} items"
  }
}
{% if cart.item_count == 1 %}
  {{ 'cart.item_count_one' | t: count: cart.item_count }}
{% else %}
  {{ 'cart.item_count_other' | t: count: cart.item_count }}
{% endif %}

6. Document Variables

{
  "_comment": "{{ price }} will be replaced with the actual price",
  "product": {
    "from_price": "From {{ price }}"
  }
}

7. Avoid Hardcoding Text

{%- # Bad -%}
<h2>Featured Products</h2>

{%- # Good -%}
<h2>{{ 'sections.featured_products.heading' | t }}</h2>

Common Translation Keys

Navigation

{
  "navigation": {
    "home": "Home",
    "shop": "Shop",
    "collections": "Collections",
    "about": "About",
    "contact": "Contact"
  }
}

Accessibility

{
  "accessibility": {
    "skip_to_content": "Skip to content",
    "close_menu": "Close menu",
    "open_menu": "Open menu",
    "next_slide": "Next slide",
    "previous_slide": "Previous slide"
  }
}

Date & Time

{
  "date": {
    "months": {
      "january": "January",
      "february": "February"
    },
    "days": {
      "monday": "Monday",
      "tuesday": "Tuesday"
    }
  }
}

Complete Example

locales/en.default.json:

{
  "general": {
    "search": "Search",
    "cart": "Cart",
    "menu": "Menu"
  },
  "product": {
    "add_to_cart": "Add to Cart",
    "from_price": "From {{ price }}"
  },
  "cart": {
    "title": "Your Cart",
    "empty": "Your cart is empty"
  }
}

sections/featured-products.liquid:

<section>
  <h2>{{ 'sections.featured_products.heading' | t }}</h2>

  {%- for product in collection.products -%}
    <div>
      <h3>{{ product.title }}</h3>
      <p>{{ 'product.from_price' | t: price: product.price | money }}</p>
      <button>{{ 'product.add_to_cart' | t }}</button>
    </div>
  {%- endfor -%}
</section>

Use translation keys consistently to create themes that work seamlessly in multiple languages.