--- name: Shopify i18n Basics description: 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: ```json { "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: ```json { "sections": { "featured_products": { "name": "Featured Products", "settings": { "heading": { "label": "Heading", "info": "Section heading text" }, "collection": { "label": "Collection" } } } } } ``` ## Using Translations in Liquid ### Basic Translation ```liquid {{ 'general.search' | t }} {{ 'product.add_to_cart' | t }} {{ 'cart.title' | t }} ``` ### Translation with Variables ```liquid {%- # In locale file -%} { "cart": { "item_count": "{{ count }} items" } } {%- # In Liquid -%} {{ 'cart.item_count' | t: count: cart.item_count }} ``` ### Translation with HTML ```liquid {%- # In locale file -%} { "general": { "continue_html": "Continue shopping" } } {%- # In Liquid -%} {{ 'general.continue_html' | t }} ``` ### Translation in Schema ```liquid {% 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 ```json { "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 ```json { "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 ```json { "cart": { "title": "Cart", "empty": "Your cart is empty", "item_count": "{{ count }} items", "subtotal": "Subtotal", "shipping": "Shipping", "total": "Total", "checkout": "Checkout", "remove": "Remove" } } ``` ### Forms & Errors ```json { "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): ```json { "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): ```json { "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: ```liquid {%- # Automatically shows "Search" in English, "Rechercher" in French -%} ``` ## Best Practices ### 1. Use Clear, Descriptive Keys ```json {%- # Good -%} { "product": { "add_to_cart": "Add to Cart" } } {%- # Bad -%} { "prod": { "btn1": "Add to Cart" } } ``` ### 2. Organize Logically ```json { "general": { ... }, # General UI "navigation": { ... }, # Navigation items "product": { ... }, # Product-related "cart": { ... }, # Cart-related "forms": { ... } # Forms } ``` ### 3. Keep Keys Consistent Across Languages **en.default.json**: ```json { "product": { "add_to_cart": "Add to Cart" } } ``` **fr.json** (same structure): ```json { "product": { "add_to_cart": "Ajouter au panier" } } ``` ### 4. Use Nested Objects for Clarity ```json {%- # Good -%} { "forms": { "contact": { "name": "Name", "email": "Email" } } } {%- # Less clear -%} { "forms": { "contact_name": "Name", "contact_email": "Email" } } ``` ### 5. Handle Pluralization ```json { "cart": { "item_count_one": "{{ count }} item", "item_count_other": "{{ count }} items" } } ``` ```liquid {% 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 ```json { "_comment": "{{ price }} will be replaced with the actual price", "product": { "from_price": "From {{ price }}" } } ``` ### 7. Avoid Hardcoding Text ```liquid {%- # Bad -%}
{{ 'product.from_price' | t: price: product.price | money }}