From 9fd510240f8712e40264be1bb2a72d7d01427186 Mon Sep 17 00:00:00 2001 From: Devin Michael Date: Sun, 21 Jun 2026 13:33:36 +0700 Subject: [PATCH 1/2] Localize Spark starter copy defaults --- configs/settings_data.json | 15 --- configs/settings_schema.json | 59 ++++------- locales/de.json | 11 +- locales/en.default.json | 7 +- locales/es.json | 11 +- locales/fi.json | 11 +- locales/fr.json | 11 +- locales/it.json | 11 +- locales/nl.json | 11 +- locales/no.json | 11 +- locales/pt.json | 11 +- locales/sv.json | 11 +- locales/th.json | 11 +- partials/block_cart_upsell.html | 5 +- partials/section_featured_categories.html | 3 +- partials/section_featured_products.html | 3 +- tests/test_localization_contracts.py | 120 ++++++++++++++++++++++ 17 files changed, 232 insertions(+), 90 deletions(-) create mode 100644 tests/test_localization_contracts.py diff --git a/configs/settings_data.json b/configs/settings_data.json index 56b3884..38ed8ab 100644 --- a/configs/settings_data.json +++ b/configs/settings_data.json @@ -16,7 +16,6 @@ "homepage_hero_overlay_opacity": 30, "homepage_hero_alt": "", "homepage_hero_link": "", - "homepage_hero_cta": "Shop now", "homepage_hero_cta_style": "primary", "homepage_hero_cta_outline": false, "featured_product": null, @@ -24,23 +23,19 @@ "featured_product_header_size": "medium", "featured_product_header_align": "left", "featured_product_description": "", - "featured_product_cta_text": "View Product", "featured_product_cta_style": "primary", "featured_product_cta_outline": false, "featured_product_bg_color": "", "featured_products": [], - "featured_products_header": "Featured Products", "featured_products_header_size": "medium", "featured_products_header_align": "left", "featured_products_columns": 3, "featured_products_bg_color": "", "featured_products_card_bg": "", - "featured_products_cta_text": "View All", "featured_products_cta_url": "", "featured_products_cta_style": "primary", "featured_products_cta_outline": true, "featured_categories": [], - "featured_categories_header": "Shop by Category", "featured_categories_header_size": "medium", "featured_categories_header_align": "left", "featured_categories_columns": 2, @@ -56,7 +51,6 @@ "product_card_border": false, "product_reviews": true, "show_recommended": true, - "recommended_products_header": "Recommended Products", "recommended_products_header_size": "medium", "recommended_products_header_align": "left", "recommended_products_columns": 3, @@ -72,7 +66,6 @@ "ab_bg_color": "", "ab_text_color": "", "ab_link": "", - "ab_link_text": "Learn more", "ab_placement": "all", "footer_menu": "footer_menu", "footer_bg_color": "", @@ -95,23 +88,17 @@ "body_text_color": "", "body_header_color": "", "body_link_color": "", - "cart_header_title": "Your Cart", "sidecart_open_on_add": true, "enable_progress_bar": false, "usd_goal_1": 50, "usd_goal_2": 100, - "step_1_message": "You are {amount} away from FREE shipping", - "step_2_message": "You are {amount} away from a FREE gift!", - "final_step_message": "You have unlocked all rewards!", "gift_product": null, "enable_upsells": false, - "upsell_section_title": "You May Also Like", "upsell_fallback_slots": "1,2", "upsell_product_1": null, "upsell_product_2": null, "upsell_product_3": null, "show_on_sale": false, - "on_sale_header": "On Sale", "on_sale_header_size": "medium", "on_sale_header_align": "left", "on_sale_products": [], @@ -119,9 +106,7 @@ "on_sale_bg_color": "", "on_sale_card_bg": "", "show_promo_banner": false, - "promo_banner_heading": "", "promo_banner_subheading": "", - "promo_banner_cta_text": "Shop Now", "promo_banner_cta_url": "", "promo_banner_bg_color": "", "promo_banner_text_color": "", diff --git a/configs/settings_schema.json b/configs/settings_schema.json index a5f016d..4057c85 100644 --- a/configs/settings_schema.json +++ b/configs/settings_schema.json @@ -224,9 +224,8 @@ "name": "homepage_hero_cta", "label": "Hero Button Text", "type": "text", - "help_text": "Call-to-action button text.", - "max_length": 50, - "default": "Shop now" + "help_text": "Call-to-action button text. Leave blank to use the localized default.", + "max_length": 50 }, { "name": "homepage_hero_cta_style", @@ -320,9 +319,8 @@ "name": "featured_product_cta_text", "label": "Button Text", "type": "text", - "help_text": "Call-to-action button text.", - "max_length": 100, - "default": "View Product" + "help_text": "Call-to-action button text. Leave blank to use the localized default.", + "max_length": 100 }, { "name": "featured_product_cta_style", @@ -366,9 +364,8 @@ "name": "featured_products_header", "label": "Section Heading", "type": "text", - "help_text": "Heading above the featured products grid. Leave blank to hide.", - "max_length": 250, - "default": "Featured Products" + "help_text": "Heading above the featured products grid. Leave blank to use the localized default.", + "max_length": 250 }, { "name": "featured_products_header_size", @@ -439,8 +436,7 @@ "name": "featured_products_cta_text", "label": "Button Text", "type": "text", - "default": "View All", - "help_text": "Optional button text shown below the grid.", + "help_text": "Optional button text shown below the grid. Leave blank to use the localized default.", "max_length": 100 }, { @@ -480,8 +476,7 @@ "name": "on_sale_header", "label": "Section Heading", "type": "text", - "default": "On Sale", - "help_text": "Heading above the on-sale products grid.", + "help_text": "Heading above the on-sale products grid. Leave blank to use the localized default.", "max_length": 250 }, { @@ -561,7 +556,6 @@ "name": "promo_banner_heading", "label": "Heading", "type": "text", - "default": "Limited Time Offer", "help_text": "Large heading text on the promo banner.", "max_length": 100 }, @@ -576,8 +570,7 @@ "name": "promo_banner_cta_text", "label": "Button Text", "type": "text", - "default": "Shop Now", - "help_text": "Call-to-action button label.", + "help_text": "Optional button text shown in the promo banner. Leave blank to use the localized default.", "max_length": 50 }, { @@ -635,9 +628,8 @@ "name": "featured_categories_header", "label": "Section Heading", "type": "text", - "help_text": "Heading above the featured categories. Leave blank to hide.", - "max_length": 250, - "default": "Shop by Category" + "help_text": "Heading above the featured categories. Leave blank to use the localized default.", + "max_length": 250 }, { "name": "featured_categories_header_size", @@ -848,16 +840,14 @@ "name": "membership_label", "label": "Member Price Label", "type": "text", - "default": "Member price", - "help_text": "Short label shown beside the member price.", + "help_text": "Short label shown beside the member price. Leave blank to use the localized default.", "max_length": 80 }, { "name": "membership_detail_text", "label": "Member Price Detail", "type": "text", - "default": "Applied at checkout", - "help_text": "Optional detail shown on larger price surfaces.", + "help_text": "Optional detail shown on larger price surfaces. Leave blank to use the localized default.", "max_length": 120 } ], @@ -881,8 +871,7 @@ "name": "recommended_products_header", "label": "Section Heading", "type": "text", - "default": "Recommended Products", - "help_text": "Heading above the recommended products grid. Leave blank to use the default translation.", + "help_text": "Heading above recommended products. Leave blank to use the localized default.", "max_length": 250 }, { @@ -1171,8 +1160,7 @@ "name": "cart_header_title", "label": "Header Title", "type": "text", - "default": "Your Cart", - "help_text": "Title displayed at the top of the side cart.", + "help_text": "Title displayed at the top of the side cart. Leave blank to use the localized default.", "max_length": 50 }, { @@ -1209,24 +1197,21 @@ "name": "step_1_message", "label": "Free Shipping Message", "type": "text", - "default": "You are {amount} away from FREE shipping", - "help_text": "Message shown when cart is below the shipping threshold. Use {amount} for the remaining amount.", + "help_text": "Message shown when cart is below the shipping threshold. Use {amount} for the remaining amount. Leave blank to use the localized default.", "max_length": 200 }, { "name": "step_2_message", "label": "Free Gift Message", "type": "text", - "default": "You are {amount} away from a FREE gift!", - "help_text": "Message shown when cart is between shipping and gift thresholds. Use {amount} for the remaining amount.", + "help_text": "Message shown when cart is between shipping and gift thresholds. Use {amount} for the remaining amount. Leave blank to use the localized default.", "max_length": 200 }, { "name": "final_step_message", "label": "All Rewards Reached Message", "type": "text", - "default": "You have unlocked all rewards!", - "help_text": "Celebration message shown when the cart total exceeds all milestones.", + "help_text": "Celebration message shown when the cart total exceeds all milestones. Leave blank to use the localized default.", "max_length": 200 }, { @@ -1248,8 +1233,7 @@ "name": "upsell_section_title", "label": "Section Title", "type": "text", - "default": "You May Also Like", - "help_text": "Heading displayed above suggested products in the side cart.", + "help_text": "Heading displayed above suggested products in the side cart. Leave blank to use the localized default.", "max_length": 100 }, { @@ -1404,9 +1388,8 @@ "name": "ab_link_text", "label": "Link Text", "type": "text", - "help_text": "Text for the announcement link.", - "max_length": 100, - "default": "Learn more" + "help_text": "Text for the announcement link. Leave blank to use the localized default.", + "max_length": 100 }, { "name": "ab_placement", diff --git a/locales/de.json b/locales/de.json index fd676de..1178f39 100644 --- a/locales/de.json +++ b/locales/de.json @@ -56,7 +56,9 @@ "terms_conditions": "Allgemeine Geschäftsbedingungen", "privacy": "Datenschutzrichtlinie", "address": "Adresse", - "copyright": "Alle Rechte vorbehalten" + "copyright": "Alle Rechte vorbehalten", + "help": "Hilfezentrum", + "rights": "Alle Rechte vorbehalten." }, "pagination": { "next": "Weiter" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Ungültiger Gutscheincode", "shipping_progress_message": "Sie sind {amount} vom KOSTENLOSEN Versand entfernt", "gift_progress_message": "Sie sind {amount} von einem KOSTENLOSEN Geschenk entfernt", - "final_progress_message": "Sie haben alle Prämien freigeschaltet" + "final_progress_message": "Sie haben alle Prämien freigeschaltet", + "upsell_section_title": "Das könnte dir auch gefallen" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "Produkt ansehen" }, "heading": { - "on_sale": "Im Angebot" + "on_sale": "Im Angebot", + "featured_products": "Ausgewählte Produkte", + "featured_categories": "Nach Kategorie einkaufen" }, "placeholder": { "hero_image": "Hero-Bild", diff --git a/locales/en.default.json b/locales/en.default.json index 6052771..5796bcb 100644 --- a/locales/en.default.json +++ b/locales/en.default.json @@ -249,7 +249,8 @@ "invalid_coupon_code": "Invalid coupon code", "shipping_progress_message": "You are {amount} away from FREE shipping", "gift_progress_message": "You are {amount} away from a FREE gift!", - "final_progress_message": "You have unlocked all rewards!" + "final_progress_message": "You have unlocked all rewards!", + "upsell_section_title": "You may also like" } }, "footer": { @@ -279,7 +280,9 @@ "view_product": "View product" }, "heading": { - "on_sale": "On sale" + "on_sale": "On sale", + "featured_products": "Featured products", + "featured_categories": "Shop by category" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/es.json b/locales/es.json index d060eeb..160a2f7 100644 --- a/locales/es.json +++ b/locales/es.json @@ -56,7 +56,9 @@ "terms_conditions": "Términos y condiciones", "privacy": "Política de privacidad", "address": "Dirección", - "copyright": "Todos los derechos reservados" + "copyright": "Todos los derechos reservados", + "help": "Centro de ayuda", + "rights": "Todos los derechos reservados." }, "pagination": { "next": "Próximo" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Código de cupón no válido", "shipping_progress_message": "Te faltan {amount} para obtener envío GRATIS", "gift_progress_message": "Te faltan {amount} para obtener un regalo GRATIS", - "final_progress_message": "Has desbloqueado todas las recompensas" + "final_progress_message": "Has desbloqueado todas las recompensas", + "upsell_section_title": "También te puede gustar" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "Ver producto" }, "heading": { - "on_sale": "En oferta" + "on_sale": "En oferta", + "featured_products": "Productos destacados", + "featured_categories": "Comprar por categoría" }, "placeholder": { "hero_image": "Imagen principal", diff --git a/locales/fi.json b/locales/fi.json index 5365e01..21d4323 100644 --- a/locales/fi.json +++ b/locales/fi.json @@ -56,7 +56,9 @@ "terms_conditions": "Säännöt ja ehdot", "privacy": "Tietosuojakäytäntö", "address": "Osoite", - "copyright": "Kaikki oikeudet pidätetään" + "copyright": "Kaikki oikeudet pidätetään", + "help": "Ohjekeskus", + "rights": "Kaikki oikeudet pidätetään." }, "pagination": { "next": "Seuraava" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Invalid coupon code", "shipping_progress_message": "You are {amount} away from FREE shipping", "gift_progress_message": "You are {amount} away from a FREE gift!", - "final_progress_message": "You have unlocked all rewards!" + "final_progress_message": "You have unlocked all rewards!", + "upsell_section_title": "Saatat myös pitää" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "View product" }, "heading": { - "on_sale": "On sale" + "on_sale": "On sale", + "featured_products": "Esittelyssä olevat tuotteet", + "featured_categories": "Osta kategorian mukaan" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/fr.json b/locales/fr.json index a3c114f..1b0af86 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -56,7 +56,9 @@ "terms_conditions": "Conditions générales", "privacy": "Politique de confidentialité", "address": "Adresse", - "copyright": "Tous droits réservés" + "copyright": "Tous droits réservés", + "help": "Centre d'aide", + "rights": "Tous droits réservés." }, "pagination": { "next": "Suivant" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Code de coupon invalide", "shipping_progress_message": "Il vous manque {amount} pour la livraison GRATUITE", "gift_progress_message": "Il vous manque {amount} pour un cadeau GRATUIT", - "final_progress_message": "Vous avez débloqué toutes les récompenses" + "final_progress_message": "Vous avez débloqué toutes les récompenses", + "upsell_section_title": "Vous aimerez aussi" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "Voir le produit" }, "heading": { - "on_sale": "En promotion" + "on_sale": "En promotion", + "featured_products": "Produits en vedette", + "featured_categories": "Acheter par catégorie" }, "placeholder": { "hero_image": "Image principale", diff --git a/locales/it.json b/locales/it.json index 583b072..ec9dad1 100644 --- a/locales/it.json +++ b/locales/it.json @@ -56,7 +56,9 @@ "terms_conditions": "Termini e condizioni", "privacy": "Informativa sulla privacy", "address": "Indirizzo", - "copyright": "Tutti i diritti riservati" + "copyright": "Tutti i diritti riservati", + "help": "Centro assistenza", + "rights": "Tutti i diritti riservati." }, "pagination": { "next": "Successivo" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Invalid coupon code", "shipping_progress_message": "You are {amount} away from FREE shipping", "gift_progress_message": "You are {amount} away from a FREE gift!", - "final_progress_message": "You have unlocked all rewards!" + "final_progress_message": "You have unlocked all rewards!", + "upsell_section_title": "Potrebbe piacerti anche" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "View product" }, "heading": { - "on_sale": "On sale" + "on_sale": "On sale", + "featured_products": "Prodotti in evidenza", + "featured_categories": "Acquista per categoria" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/nl.json b/locales/nl.json index 6f5921c..dcb6af6 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -56,7 +56,9 @@ "terms_conditions": "Algemene voorwaarden", "privacy": "Privacybeleid", "address": "Adres", - "copyright": "Alle rechten voorbehouden" + "copyright": "Alle rechten voorbehouden", + "help": "Helpcentrum", + "rights": "Alle rechten voorbehouden." }, "pagination": { "next": "Volgende" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Invalid coupon code", "shipping_progress_message": "You are {amount} away from FREE shipping", "gift_progress_message": "You are {amount} away from a FREE gift!", - "final_progress_message": "You have unlocked all rewards!" + "final_progress_message": "You have unlocked all rewards!", + "upsell_section_title": "Misschien vind je dit ook leuk" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "View product" }, "heading": { - "on_sale": "On sale" + "on_sale": "On sale", + "featured_products": "Uitgelichte producten", + "featured_categories": "Shop op categorie" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/no.json b/locales/no.json index ef8d8cb..f671fed 100644 --- a/locales/no.json +++ b/locales/no.json @@ -56,7 +56,9 @@ "terms_conditions": "Vilkår og betingelser", "privacy": "Retningslinjer for personvern", "address": "Adresse", - "copyright": "Alle rettigheter reservert" + "copyright": "Alle rettigheter reservert", + "help": "Hjelpesenter", + "rights": "Alle rettigheter reservert." }, "pagination": { "next": "Neste" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Invalid coupon code", "shipping_progress_message": "You are {amount} away from FREE shipping", "gift_progress_message": "You are {amount} away from a FREE gift!", - "final_progress_message": "You have unlocked all rewards!" + "final_progress_message": "You have unlocked all rewards!", + "upsell_section_title": "Du vil kanskje også like" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "View product" }, "heading": { - "on_sale": "On sale" + "on_sale": "On sale", + "featured_products": "Utvalgte produkter", + "featured_categories": "Handle etter kategori" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/pt.json b/locales/pt.json index dfc1e13..a0b5993 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -56,7 +56,9 @@ "terms_conditions": "Termos e condições", "privacy": "Política de privacidade", "address": "Endereço", - "copyright": "Todos os direitos reservados" + "copyright": "Todos os direitos reservados", + "help": "Central de ajuda", + "rights": "Todos os direitos reservados." }, "pagination": { "next": "Próximo" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Invalid coupon code", "shipping_progress_message": "You are {amount} away from FREE shipping", "gift_progress_message": "You are {amount} away from a FREE gift!", - "final_progress_message": "You have unlocked all rewards!" + "final_progress_message": "You have unlocked all rewards!", + "upsell_section_title": "Você também pode gostar" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "View product" }, "heading": { - "on_sale": "On sale" + "on_sale": "On sale", + "featured_products": "Produtos em destaque", + "featured_categories": "Comprar por categoria" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/sv.json b/locales/sv.json index 4d7e854..aab716c 100644 --- a/locales/sv.json +++ b/locales/sv.json @@ -56,7 +56,9 @@ "terms_conditions": "Regler & Villkor", "privacy": "Sekretesspolicy", "address": "Adress", - "copyright": "Alla rättigheter reserverade" + "copyright": "Alla rättigheter reserverade", + "help": "Hjälpcenter", + "rights": "Alla rättigheter förbehållna." }, "pagination": { "next": "Nästa" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Invalid coupon code", "shipping_progress_message": "You are {amount} away from FREE shipping", "gift_progress_message": "You are {amount} away from a FREE gift!", - "final_progress_message": "You have unlocked all rewards!" + "final_progress_message": "You have unlocked all rewards!", + "upsell_section_title": "Du kanske också gillar" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "View product" }, "heading": { - "on_sale": "On sale" + "on_sale": "On sale", + "featured_products": "Utvalda produkter", + "featured_categories": "Handla efter kategori" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/th.json b/locales/th.json index 83eeed5..515d986 100644 --- a/locales/th.json +++ b/locales/th.json @@ -56,7 +56,9 @@ "terms_conditions": "ข้อกำหนดและเงื่อนไข", "privacy": "นโยบายความเป็นส่วนตัว", "address": "ที่อยู่", - "copyright": "สงวนลิขสิทธิ์" + "copyright": "สงวนลิขสิทธิ์", + "help": "ศูนย์ช่วยเหลือ", + "rights": "สงวนลิขสิทธิ์" }, "pagination": { "next": "ถัดไป" @@ -247,7 +249,8 @@ "invalid_coupon_code": "Invalid coupon code", "shipping_progress_message": "You are {amount} away from FREE shipping", "gift_progress_message": "You are {amount} away from a FREE gift!", - "final_progress_message": "You have unlocked all rewards!" + "final_progress_message": "You have unlocked all rewards!", + "upsell_section_title": "คุณอาจชอบ" } }, "footer": { @@ -277,7 +280,9 @@ "view_product": "View product" }, "heading": { - "on_sale": "On sale" + "on_sale": "On sale", + "featured_products": "สินค้าที่แนะนำ", + "featured_categories": "เลือกซื้อตามหมวดหมู่" }, "placeholder": { "hero_image": "Hero image", diff --git a/partials/block_cart_upsell.html b/partials/block_cart_upsell.html index ed9c641..384e956 100644 --- a/partials/block_cart_upsell.html +++ b/partials/block_cart_upsell.html @@ -7,11 +7,10 @@ {% if settings.enable_upsells %}
- {% if settings.upsell_section_title %} + {% t "side_cart.default.upsell_section_title" as default_upsell_section_title %}

- {{ settings.upsell_section_title }} + {{ settings.upsell_section_title|default:default_upsell_section_title }}

- {% endif %} {% if settings.upsell_product_1 %} {% with product=settings.upsell_product_1 slot="1" %} diff --git a/partials/section_featured_categories.html b/partials/section_featured_categories.html index 5f36570..2227da4 100644 --- a/partials/section_featured_categories.html +++ b/partials/section_featured_categories.html @@ -3,7 +3,8 @@ {% if settings.featured_categories|length > 0 %}
- {% include "partials/section_heading.html" with heading=settings.featured_categories_header size=settings.featured_categories_header_size align=settings.featured_categories_header_align %} + {% t "homepage.heading.featured_categories" as default_featured_categories_heading %} + {% include "partials/section_heading.html" with heading=settings.featured_categories_header|default:default_featured_categories_heading size=settings.featured_categories_header_size align=settings.featured_categories_header_align %}
{% for category in settings.featured_categories %} diff --git a/partials/section_featured_products.html b/partials/section_featured_products.html index 7ff961a..6e8844f 100644 --- a/partials/section_featured_products.html +++ b/partials/section_featured_products.html @@ -3,7 +3,8 @@ {% if settings.featured_products|length > 0 %}
- {% include "partials/section_heading.html" with heading=settings.featured_products_header size=settings.featured_products_header_size align=settings.featured_products_header_align %} + {% t "homepage.heading.featured_products" as default_featured_products_heading %} + {% include "partials/section_heading.html" with heading=settings.featured_products_header|default:default_featured_products_heading size=settings.featured_products_header_size align=settings.featured_products_header_align %} {% include "partials/product_grid.html" with products=settings.featured_products columns=settings.featured_products_columns card_bg=settings.featured_products_card_bg %} {% if settings.featured_products_cta_url %}
diff --git a/tests/test_localization_contracts.py b/tests/test_localization_contracts.py new file mode 100644 index 0000000..08f47ec --- /dev/null +++ b/tests/test_localization_contracts.py @@ -0,0 +1,120 @@ +import json +import re +import unittest +from pathlib import Path + + +ROOT = Path(__file__).resolve().parents[1] + +LOCALIZED_DEFAULT_OVERRIDE_SETTINGS = { + "ab_link_text", + "cart_header_title", + "featured_categories_header", + "featured_product_cta_text", + "featured_products_cta_text", + "featured_products_header", + "final_step_message", + "homepage_hero_cta", + "membership_detail_text", + "membership_label", + "on_sale_header", + "promo_banner_cta_text", + "recommended_products_header", + "step_1_message", + "step_2_message", + "upsell_section_title", +} + +STARTER_COPY_SETTINGS = LOCALIZED_DEFAULT_OVERRIDE_SETTINGS | { + "promo_banner_heading", +} + + +def load_json(path): + return json.loads(path.read_text()) + + +def flatten_leaf_paths(value, prefix=""): + if isinstance(value, dict): + paths = set() + for key, child in value.items(): + child_prefix = f"{prefix}.{key}" if prefix else key + paths.update(flatten_leaf_paths(child, child_prefix)) + return paths + return {prefix} + + +def has_path(value, path): + current = value + for part in path.split("."): + if not isinstance(current, dict) or part not in current: + return False + current = current[part] + return True + + +def setting_fields(schema): + for groups in schema.values(): + for fields in groups.values(): + for field in fields: + yield field + + +class LocalizationContractTests(unittest.TestCase): + def test_template_translation_keys_exist_in_every_locale(self): + template_dirs = ["layouts", "partials", "templates"] + key_pattern = re.compile(r"{%\s*t\s+[\"']([^\"']+)[\"']") + used_keys = set() + + for dirname in template_dirs: + for path in (ROOT / dirname).rglob("*.html"): + used_keys.update(key_pattern.findall(path.read_text())) + + self.assertGreater(len(used_keys), 0) + + for locale_path in sorted((ROOT / "locales").glob("*.json")): + locale = load_json(locale_path) + missing = sorted(key for key in used_keys if not has_path(locale, key)) + self.assertEqual(missing, [], f"{locale_path.name} is missing translation keys") + + def test_locale_files_keep_the_same_key_shape(self): + canonical_paths = flatten_leaf_paths(load_json(ROOT / "locales" / "en.default.json")) + + for locale_path in sorted((ROOT / "locales").glob("*.json")): + locale_paths = flatten_leaf_paths(load_json(locale_path)) + self.assertEqual( + sorted(canonical_paths - locale_paths), + [], + f"{locale_path.name} is missing locale keys from en.default.json", + ) + self.assertEqual( + sorted(locale_paths - canonical_paths), + [], + f"{locale_path.name} has locale keys not present in en.default.json", + ) + + def test_starter_copy_settings_do_not_define_schema_defaults(self): + schema = load_json(ROOT / "configs" / "settings_schema.json") + fields_by_name = {field["name"]: field for field in setting_fields(schema)} + + missing_fields = sorted(STARTER_COPY_SETTINGS - fields_by_name.keys()) + self.assertEqual(missing_fields, []) + + offenders = sorted( + name for name in STARTER_COPY_SETTINGS if "default" in fields_by_name[name] + ) + self.assertEqual(offenders, []) + + def test_starter_copy_settings_are_not_saved_as_starter_copy(self): + settings_data = load_json(ROOT / "configs" / "settings_data.json") + offenders = sorted( + name + for name in STARTER_COPY_SETTINGS + if settings_data.get(name) not in (None, "") + ) + + self.assertEqual(offenders, []) + + +if __name__ == "__main__": + unittest.main() From 0509bec93f903576a3ebd15036390d255b17bf1b Mon Sep 17 00:00:00 2001 From: Devin Michael Date: Sun, 21 Jun 2026 14:00:48 +0700 Subject: [PATCH 2/2] Address localization review feedback --- configs/settings_schema.json | 4 +-- layouts/base.html | 8 ++--- locales/de.json | 6 ++-- locales/en.default.json | 6 ++-- locales/es.json | 6 ++-- locales/fi.json | 6 ++-- locales/fr.json | 6 ++-- locales/it.json | 6 ++-- locales/nl.json | 6 ++-- locales/no.json | 6 ++-- locales/pt.json | 6 ++-- locales/sv.json | 6 ++-- locales/th.json | 6 ++-- partials/announcement_bar.html | 3 +- partials/block_cart_progress_bar.html | 13 +++++--- partials/block_cart_upsell.html | 42 ++++++++++++----------- partials/recommended_products.html | 7 ++-- partials/section_featured_product.html | 3 +- partials/section_promo_banner.html | 14 ++------ partials/side_cart.html | 3 +- tests/test_localization_contracts.py | 46 +++++++++++++++++++++----- 21 files changed, 128 insertions(+), 81 deletions(-) diff --git a/configs/settings_schema.json b/configs/settings_schema.json index 4057c85..cc9a746 100644 --- a/configs/settings_schema.json +++ b/configs/settings_schema.json @@ -263,7 +263,7 @@ "name": "featured_product_header", "label": "Section Heading", "type": "text", - "help_text": "Heading above the featured product. Leave blank to hide.", + "help_text": "Heading above the featured product. Leave blank to use the localized default.", "max_length": 250 }, { @@ -556,7 +556,7 @@ "name": "promo_banner_heading", "label": "Heading", "type": "text", - "help_text": "Large heading text on the promo banner.", + "help_text": "Large heading text on the promo banner. Leave blank to use the localized default.", "max_length": 100 }, { diff --git a/layouts/base.html b/layouts/base.html index b55ec13..0ce2ecc 100644 --- a/layouts/base.html +++ b/layouts/base.html @@ -194,16 +194,16 @@ {% if settings.enable_membership_pricing %} {% url 'storefrontapi:graphql' as membership_graphql_url %} diff --git a/locales/de.json b/locales/de.json index 1178f39..d13e80a 100644 --- a/locales/de.json +++ b/locales/de.json @@ -158,7 +158,7 @@ "customer_reviews": "Bewertungen von Kunden", "see_all_reviews": "Alle Bewertungen ansehen", "write_a_review": "Schreibe eine Rezension", - "recommended_products_header": "Kunden, die diesen Artikel angesehen haben, kauften auch", + "recommended_products_header": "Empfohlene Produkte", "apply_filters": "Filter anwenden", "description": "Beschreibung", "filter_reset": "Zurücksetzen", @@ -282,7 +282,9 @@ "heading": { "on_sale": "Im Angebot", "featured_products": "Ausgewählte Produkte", - "featured_categories": "Nach Kategorie einkaufen" + "featured_categories": "Nach Kategorie einkaufen", + "promo_banner": "Zeitlich begrenztes Angebot", + "featured_product": "Ausgewähltes Produkt" }, "placeholder": { "hero_image": "Hero-Bild", diff --git a/locales/en.default.json b/locales/en.default.json index 5796bcb..20ebb7a 100644 --- a/locales/en.default.json +++ b/locales/en.default.json @@ -158,7 +158,7 @@ "customer_reviews": "Customer Reviews", "see_all_reviews": "See all reviews", "write_a_review": "Write a review", - "recommended_products_header": "Customers who viewed this item also bought", + "recommended_products_header": "Recommended products", "apply_filters": "Apply filters", "description": "Description", "filter_reset": "Reset", @@ -282,7 +282,9 @@ "heading": { "on_sale": "On sale", "featured_products": "Featured products", - "featured_categories": "Shop by category" + "featured_categories": "Shop by category", + "promo_banner": "Limited-time offer", + "featured_product": "Featured product" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/es.json b/locales/es.json index 160a2f7..2cd18f0 100644 --- a/locales/es.json +++ b/locales/es.json @@ -158,7 +158,7 @@ "customer_reviews": "Opiniones de clientes", "see_all_reviews": "Ver todas las reseñas", "write_a_review": "Escribe una reseña", - "recommended_products_header": "Los clientes que vieron este artículo también compraron", + "recommended_products_header": "Productos recomendados", "apply_filters": "Aplicar filtros", "description": "Descripción", "filter_reset": "Restablecer", @@ -282,7 +282,9 @@ "heading": { "on_sale": "En oferta", "featured_products": "Productos destacados", - "featured_categories": "Comprar por categoría" + "featured_categories": "Comprar por categoría", + "promo_banner": "Oferta por tiempo limitado", + "featured_product": "Producto destacado" }, "placeholder": { "hero_image": "Imagen principal", diff --git a/locales/fi.json b/locales/fi.json index 21d4323..c996cc2 100644 --- a/locales/fi.json +++ b/locales/fi.json @@ -158,7 +158,7 @@ "customer_reviews": "Asiakkaiden arvostelut", "see_all_reviews": "Katso kaikki arvostelut", "write_a_review": "Kirjoita arvostelu", - "recommended_products_header": "Asiakkaat, jotka katsoneet tätä tuotetta, ostivat myös", + "recommended_products_header": "Suositellut tuotteet", "apply_filters": "Apply filters", "description": "Description", "filter_reset": "Reset", @@ -282,7 +282,9 @@ "heading": { "on_sale": "On sale", "featured_products": "Esittelyssä olevat tuotteet", - "featured_categories": "Osta kategorian mukaan" + "featured_categories": "Osta kategorian mukaan", + "promo_banner": "Rajoitetun ajan tarjous", + "featured_product": "Esittelyssä oleva tuote" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/fr.json b/locales/fr.json index 1b0af86..cac6edd 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -158,7 +158,7 @@ "customer_reviews": "Avis des clients", "see_all_reviews": "Voir tous les avis", "write_a_review": "Écrire un avis", - "recommended_products_header": "Les clients qui ont consulté cet article ont également acheté", + "recommended_products_header": "Produits recommandés", "apply_filters": "Appliquer les filtres", "description": "Description", "filter_reset": "Réinitialiser", @@ -282,7 +282,9 @@ "heading": { "on_sale": "En promotion", "featured_products": "Produits en vedette", - "featured_categories": "Acheter par catégorie" + "featured_categories": "Acheter par catégorie", + "promo_banner": "Offre à durée limitée", + "featured_product": "Produit en vedette" }, "placeholder": { "hero_image": "Image principale", diff --git a/locales/it.json b/locales/it.json index ec9dad1..a59aaf7 100644 --- a/locales/it.json +++ b/locales/it.json @@ -158,7 +158,7 @@ "customer_reviews": "Recensioni clienti", "see_all_reviews": "Vedi tutte le recensioni", "write_a_review": "Scrivi una recensione", - "recommended_products_header": "I clienti che hanno visto questo articolo hanno acquistato anche", + "recommended_products_header": "Prodotti consigliati", "apply_filters": "Apply filters", "description": "Description", "filter_reset": "Reset", @@ -282,7 +282,9 @@ "heading": { "on_sale": "On sale", "featured_products": "Prodotti in evidenza", - "featured_categories": "Acquista per categoria" + "featured_categories": "Acquista per categoria", + "promo_banner": "Offerta a tempo limitato", + "featured_product": "Prodotto in evidenza" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/nl.json b/locales/nl.json index dcb6af6..dd668ab 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -158,7 +158,7 @@ "customer_reviews": "Recensies van klanten", "see_all_reviews": "Bekijk alle reviews", "write_a_review": "Schrijf een review", - "recommended_products_header": "Klanten die dit artikel bekeken, kochten ook", + "recommended_products_header": "Aanbevolen producten", "apply_filters": "Apply filters", "description": "Description", "filter_reset": "Reset", @@ -282,7 +282,9 @@ "heading": { "on_sale": "On sale", "featured_products": "Uitgelichte producten", - "featured_categories": "Shop op categorie" + "featured_categories": "Shop op categorie", + "promo_banner": "Tijdelijke aanbieding", + "featured_product": "Uitgelicht product" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/no.json b/locales/no.json index f671fed..54d7b51 100644 --- a/locales/no.json +++ b/locales/no.json @@ -158,7 +158,7 @@ "customer_reviews": "Kundevurderinger", "see_all_reviews": "Se alle vurderinger", "write_a_review": "Skriv en anmeldelse", - "recommended_products_header": "Kunder som så på denne varen kjøpte også", + "recommended_products_header": "Anbefalte produkter", "apply_filters": "Apply filters", "description": "Description", "filter_reset": "Reset", @@ -282,7 +282,9 @@ "heading": { "on_sale": "On sale", "featured_products": "Utvalgte produkter", - "featured_categories": "Handle etter kategori" + "featured_categories": "Handle etter kategori", + "promo_banner": "Tidsbegrenset tilbud", + "featured_product": "Utvalgt produkt" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/pt.json b/locales/pt.json index a0b5993..367aeb8 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -158,7 +158,7 @@ "customer_reviews": "Avaliações de clientes", "see_all_reviews": "Ver todas as avaliações", "write_a_review": "Escrever um comentário", - "recommended_products_header": "Clientes que visualizaram este item também compraram", + "recommended_products_header": "Produtos recomendados", "apply_filters": "Apply filters", "description": "Description", "filter_reset": "Reset", @@ -282,7 +282,9 @@ "heading": { "on_sale": "On sale", "featured_products": "Produtos em destaque", - "featured_categories": "Comprar por categoria" + "featured_categories": "Comprar por categoria", + "promo_banner": "Oferta por tempo limitado", + "featured_product": "Produto em destaque" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/sv.json b/locales/sv.json index aab716c..a964123 100644 --- a/locales/sv.json +++ b/locales/sv.json @@ -158,7 +158,7 @@ "customer_reviews": "Kundrecensioner", "see_all_reviews": "Se alla recensioner", "write_a_review": "Skriv en recension", - "recommended_products_header": "Kunder som tittade på denna artikel köpte också", + "recommended_products_header": "Rekommenderade produkter", "apply_filters": "Apply filters", "description": "Description", "filter_reset": "Reset", @@ -282,7 +282,9 @@ "heading": { "on_sale": "On sale", "featured_products": "Utvalda produkter", - "featured_categories": "Handla efter kategori" + "featured_categories": "Handla efter kategori", + "promo_banner": "Tidsbegränsat erbjudande", + "featured_product": "Utvald produkt" }, "placeholder": { "hero_image": "Hero image", diff --git a/locales/th.json b/locales/th.json index 515d986..6d55c0b 100644 --- a/locales/th.json +++ b/locales/th.json @@ -158,7 +158,7 @@ "customer_reviews": "รีวิวจากลูกค้า", "see_all_reviews": "ดูรีวิวทั้งหมด", "write_a_review": "เขียนรีวิว", - "recommended_products_header": "ลูกค้าที่ดูไอเท็มนี้ก็ซื้อ", + "recommended_products_header": "สินค้าแนะนำ", "apply_filters": "Apply filters", "description": "Description", "filter_reset": "Reset", @@ -282,7 +282,9 @@ "heading": { "on_sale": "On sale", "featured_products": "สินค้าที่แนะนำ", - "featured_categories": "เลือกซื้อตามหมวดหมู่" + "featured_categories": "เลือกซื้อตามหมวดหมู่", + "promo_banner": "ข้อเสนอจำกัดเวลา", + "featured_product": "สินค้าที่แนะนำ" }, "placeholder": { "hero_image": "Hero image", diff --git a/partials/announcement_bar.html b/partials/announcement_bar.html index 90d8904..8974fb2 100644 --- a/partials/announcement_bar.html +++ b/partials/announcement_bar.html @@ -7,7 +7,8 @@ {{ settings.ab_text }} {% if settings.ab_link %} - {% if settings.ab_link_text %}{{ settings.ab_link_text }}{% else %}{% t "global.button.learn_more" %}{% endif %} + {% t "global.button.learn_more" as default_ab_link_text %} + {{ settings.ab_link_text|default:default_ab_link_text }} {% endif %}