star back image
people4
電飾 電飾
moon
men

【shopify】メタタグのDescriptionを出し分ける

BLOG shopifyWEBログ
読了約:50分

shopifyはECに特化したCMSなので、ガチ固定の従来のホームページ制作のように直にHTMLソースにdescriptionをバチバチと入力する雰囲気ではありません。

googleスプレットシートにズラッと並んだtitleやdescriptionをとにかく早く仕込みたいです。

そういう要件はありますでしょうか。

検索しました。すると、

スニペットで以下のように/snippets/meta-tags.liquidファイルを作成。

{% render 'meta-tags' %}

で呼び出す方法がたくさん出てきます。

ではmeta-tags.liquidの中身はどういう内容なのでしょう。

管理画面から簡単にできないのでしょうか。

意外と時間がかかるかもです。

今はAIにも聞けます。

【共有】meta-tags.lquidの編集

情けない話、AIと一緒に作成したコードはとても長く見ずらいものでした。
この共有ファイルは、shopifyに詳しい人が技術的リファクタリングをしてくれましたソースコードになります。

とても見やすく、デバッグ情報のHTMLコメント付き。

要件は以下です。

  • theme.lquidにmeta-tags.liquidをrenderさせたい
  • 言語切り替えに対応したい

theme.liquid

<!doctype html>
<html class="no-js" lang="{{ request.locale.iso_code }}">
  <head>
    {% render 'meta-tags' %}
  </head>

メタタグなので<head>内にmeta-tags.liquidを読み込みさせます。

meta-tags.liquidsnippets

{%- liquid
  assign og_title = page_title | default: shop.name
  assign og_url = canonical_url | default: request.origin
  assign og_type = 'website'
  assign og_description = page_description | default: shop.description | default: shop.name

  assign og_image = page_image;

  # キーワードの設定
  assign meta_keywords = ''
  if request.page_type == 'product'
    if product.tags != blank
      assign meta_keywords = product.title | append: ', ' | append: product.tags | join: ', ' | append: ', astro,wave,アストロウェーブ,ASTROWAVE,氷河期世代,株'
    else
      assign meta_keywords = product.title | append: ', astro,wave,アストロウェーブ,ASTROWAVE,氷河期世代,株'
    endif
  elsif request.page_type == 'collection'
    assign meta_keywords = collection.title | append: ', astro,wave,アストロウェーブ,ASTROWAVE,氷河期世代,株'
  elsif request.page_type == 'article'
    if request.path contains '/blogs/feature/' or request.path contains 'feature'
      assign meta_keywords = 'FEATURE, astro,wave,アストロウェーブ,ASTROWAVE,氷河期世代,株'
    elsif request.path contains '/blogs/news/' or request.path contains 'news'
      assign meta_keywords = 'NEWS, astro,wave,アストロウェーブ,ASTROWAVE,氷河期世代,株'
    elsif article.blog.handle == 'feature'
      assign meta_keywords = 'FEATURE, astro,wave,アストロウェーブ,ASTROWAVE,氷河期世代,株'
    elsif article.blog.handle == 'news'
      assign meta_keywords = 'NEWS, astro,wave,アストロウェーブ,ASTROWAVE,氷河期世代,株'
    else
      assign meta_keywords = article.tags | join: ', ' | append: ', astro,wave,アストロウェーブ,ASTROWAVE,氷河期世代,株'
    endif
  else
    assign meta_keywords = 'astro,wave,アストロウェーブ,ASTROWAVE'
  endif

  # Descriptionの設定
  assign meta_description = ''
  if request.page_type == 'index'
    assign meta_description = 'meta.default_description' | t
  elsif request.page_type == 'collection'
    if collection.handle contains 'all-items'
      assign meta_description = 'meta.all_product_description' | t
    elsif collection.handle contains 'new-arrivals'
      assign meta_description = 'meta.new_arrivals_description' | t
    elsif collection.handle contains 'all-jeans'
      assign meta_description = 'meta.all_jeans_description' | t
    else
      # 一般的なコレクションページ(WOMEN'S TOPSなど)
      assign meta_description = 'meta.general_collection_description' | t
    endif
  elsif request.page_type == 'product'
    # デバッグ用変数
    assign debug_metafield = product.metafields.custom.detail
    assign debug_description = product.description
    
    # アイテム詳細メタフィールドを優先して使用
    if product.metafields.custom.detail != blank
      # JSONリッチテキストからテキスト部分を抽出
      assign detail_json = product.metafields.custom.detail
      assign detail_text = ''
      
      # "value"フィールドからテキストを抽出
      assign value_parts = detail_json | split: '"value":"'
      if value_parts.size > 1
        for i in (1..value_parts.size)
          assign part = value_parts[i] | split: '"' | first
          if part != blank and part.size > 5
            assign detail_text = detail_text | append: part | append: ' '
          endif
        endfor
      endif
      
      # 抽出したテキストをクリーンアップ
      assign detail_text = detail_text | replace: '\n', ' ' | replace: '\\n', ' ' | strip
      
      if detail_text != blank and detail_text.size > 20
        assign meta_description = detail_text | truncate: 100
      else
        # メタフィールドからテキスト抽出に失敗した場合は商品説明を使用
        if product.description != blank
          assign meta_description = product.description | strip_html | truncate: 100
        else
          assign items_desc = 'meta.items_description' | t
          assign meta_description = product.title | append: ' - ' | append: items_desc
        endif
      endif
    elsif product.description != blank
      assign meta_description = product.description | strip_html | truncate: 100
    else
      assign items_desc = 'meta.items_description' | t
      assign meta_description = product.title | append: ' - ' | append: items_desc
    endif
  elsif request.page_type == 'cart' or request.path contains '/cart'
    assign meta_description = 'meta.cart_description' | t
  elsif request.page_type == 'customers/login'
    assign meta_description = 'meta.login_description' | t
  elsif request.page_type == 'customers/register'
    assign meta_description = 'meta.register_description' | t
  elsif request.page_type == 'customers/reset_password'
    assign meta_description = 'meta.reset_password_description' | t
  elsif request.page_type == 'customers/account'
    assign meta_description = 'meta.account_description' | t
  elsif request.page_type == 'customers/addresses'
    assign meta_description = 'meta.addresses_description' | t
  elsif request.page_type == 'customers/order'
    assign meta_description = 'meta.order_description' | t
  elsif request.page_type == 'page' and page.handle == 'contact'
    assign meta_description = 'meta.contact_description' | t
  elsif request.page_type == 'blog' and blog.handle == 'news'
    assign meta_description = 'meta.news_description' | t
  elsif request.page_type == 'article' and article.blog.handle == 'news'
    if page_description != blank
      assign meta_description = page_description
    else
      assign meta_description = ''
    endif
  elsif request.page_type == 'blog' and blog.handle == 'feature'
    assign meta_description = 'meta.feature_description' | t
  elsif request.page_type == 'article' and article.blog.handle == 'feature'
    if page_description != blank
      assign meta_description = page_description
    else
      assign meta_description = ''
    endif
  elsif request.page_type == 'article'
    # その他の記事ページ(ブログハンドルが空の場合など)
    if page_description != blank
      assign meta_description = page_description
    else
      assign meta_description = ''
    endif
  elsif request.page_type == 'page' and page.handle contains 'store' and page.handle != 'store'
    # 個別店舗ページの処理 - メタオブジェクトから動的に取得
    assign store_description = ''
    assign found_store = false
    assign debug_info = ''
    assign store_name_test = ''
    
    # theme.liquidと同じ方法でアクセス
    for store in shop.metaobjects.store.values
      assign store_page_handle = 'store/' | append: store.system.handle
      assign debug_info = debug_info | append: 'Checking: ' | append: store_page_handle | append: ' against ' | append: page.handle | append: '; '
      if page.handle == store_page_handle
        assign found_store = true
        # 店舗名が取得できるかチェック(デバッグ用)
        assign store_name_test = store.name.value
        assign debug_info = debug_info | append: 'Found store: ' | append: store_name_test | append: '; '
        
        # store-detail.liquidと同じ方法でアクセス
        if store.seo_description.value != blank
          assign store_description = store.seo_description.value
          assign debug_info = debug_info | append: 'SEO description found: ' | append: store_description | append: '; '
        else
          assign debug_info = debug_info | append: 'SEO description empty or not found; '
        endif
        break
      endif
    endfor
    
    # デバッグ情報をHTMLコメントとして出力
    assign debug_output = '<!-- Store Debug: page.handle=' | append: page.handle | append: ', found_store=' | append: found_store | append: ', store_name=' | append: store_name_test | append: ', store_description=' | append: store_description | append: ', debug_info=' | append: debug_info | append: ' -->'
    
    
    # メタオブジェクトにseo_descriptionがある場合は使用、なければデフォルト
    if store_description != blank
      assign meta_description = store_description
    else
      assign meta_description = 'meta.store_description' | t
    endif
  elsif template contains 'metaobject/store'
    # メタオブジェクト店舗ページの処理 - 直接metaobjectからアクセス
    if metaobject and metaobject.seo_description.value != blank
      assign meta_description = metaobject.seo_description.value
    else
      assign meta_description = 'meta.store_description' | t
    endif
  elsif request.page_type == 'page' and page.handle == 'store'
    assign meta_description = 'meta.store_description' | t
  elsif request.page_type == 'page'
    # 一般的なページの場合、管理画面で設定されたSEO descriptionを優先
    if page_description != blank
      assign meta_description = page_description
    else
      assign meta_description = shop.description | default: shop.name
    endif
  elsif request.page_type == 'search'
    assign meta_description = 'meta.search_description' | t
  elsif request.page_type == '404'
    assign meta_description = 'meta.404_description' | t
  elsif page_description != blank and page.handle contains 'store'
    assign meta_description = 'meta.store_description' | t
  elsif page_description != blank
    assign meta_description = page_description
  else
    assign meta_description = shop.description | default: shop.name
  endif

  if request.page_type == 'product'
    assign og_type = 'product'
  elsif request.page_type == 'article'
    assign og_type = 'article'
  elsif request.page_type == 'password'
    assign og_url = request.origin
  elsif request.page_type == 'collection'
    assign og_image = images['ogp.jpg']
  endif
-%}

{% ### TITLE ### %}
{% if template == 'index' %}<title>{{ page_title }}</title>
{% elsif template == 'collection' %}<title>{{ collection.title }} | {{ shop.name }}</title>
{% elsif template == 'customers/login' %}<title>LOGIN | {{ shop.name }}</title>
{% elsif template == 'customers/register' %}<title>REGISTER | {{ shop.name }}</title>
{% elsif template == 'customers/account' %}<title>MY ACCOUNT | {{ shop.name }}</title>
{% elsif template == 'customers/addresses' %}<title>MY ACCOUNT | {{ shop.name }}</title>
{% elsif template == 'blog' %}<title>{{ blog.title }} | {{ shop.name }}</title>
{% elsif template == 'article' %}<title>{{ article.title }} | {{ shop.name }}</title>
{% elsif template == 'product' %}<title>{{ product.title }} | {{ shop.name }}</title>
{% elsif template contains 'page' %}<title>{{ page.title }} | {{ shop.name }}</title>
{% elsif template == 'search' %}<title>SEARCH | {{ shop.name }}</title>
{% elsif template == '404' %}<title>404 | {{ shop.name }}</title>
{% elsif template == 'metaobject/store' %}<title>{{ og_title | escape }} | {{ shop.name }}</title>
{% endif %}

{%- ### DESCRIPTION ### -%}
{% if meta_description != blank %}<meta name="description" content="{{ meta_description | escape }}">{% elsif request.page_type != 'article' %}<meta name="description" content="{{ shop.description | default: shop.name | escape }}">{% endif %}
{% if meta_keywords != blank %}<meta name="keywords" content="{{ meta_keywords | escape }}">{% endif %}

{% ### OG ### %}
<meta property="og:site_name" content="{{ shop.name }}">
<meta property="og:url" content="{{ og_url }}">
<meta property="og:title" content="{{ og_title | escape }}">
<meta property="og:type" content="{{ og_type }}">
{% if meta_description != blank %}<meta property="og:description" content="{{ meta_description | escape }}">{% elsif request.page_type != 'article' %}<meta property="og:description" content="{{ shop.description | default: shop.name | escape }}">{% endif %}

{% ### OG:IMAGE ### %}
{% if og_image %}<meta property="og:image" content="http:{{ og_image | image_url }}">
<meta property="og:image:secure_url" content="https:{{ og_image | image_url }}">
<meta property="og:image:width" content="{{ og_image.width }}">
<meta property="og:image:height" content="{{ og_image.height }}">{% endif %}
{% if request.page_type == 'product' %}<meta property="og:price:amount" content="{{ product.price | money_without_currency | strip_html }}">
<meta property="og:price:currency" content="{{ cart.currency.iso_code }}">{% endif %}
{% if settings.social_twitter_link != blank %}<meta name="twitter:site" content="{{ settings.social_twitter_link | split: 'x.com/' | last | prepend: '@' }}">{% endif %}
<meta name="twitter:card" content="summary_large_image">
<meta name="twitter:title" content="{{ og_title | escape }}">
{% if meta_description != blank %}<meta name="twitter:description" content="{{ meta_description | escape }}">{% elsif request.page_type != 'article' %}<meta name="twitter:description" content="{{ shop.description | default: shop.name | escape }}">{% endif %}

テンプレートにあった記述、
{% if template ==や、<meta property=”og:や、{% if og_image %}など、丸ごと載せました。

SEOに必要なものがまとめられているファイルですね。

メモ:すぐ忘れそうな見てほしい内容は以下です。

request.page_typeでURLの判断でmeta_descriptionを出し分けすることろ。
・商品メタフィールドからの説明抽出のエラーバンドリング。
・動的に作成したページのshop.metaobjects.store.valuesを引っ張ってくるところ。

特徴的な機能の抜粋

商品Description処理

# JSONリッチテキストからテキスト部分を抽出
assign detail_json = product.metafields.custom.detail
assign detail_text = ''

# "value"フィールドからテキストを抽出
assign value_parts = detail_json | split: '"value":"'
if value_parts.size > 1
  for i in (1..value_parts.size)
    assign part = value_parts[i] | split: '"' | first
    if part != blank and part.size > 5
      assign detail_text = detail_text | append: part | append: ' '
    endif
  endfor
endif
# エラーハンドリング

# 抽出したテキストをクリーンアップ
assign detail_text = detail_text | replace: '\n', ' ' | replace: '\\n', ' ' | strip
      
if detail_text != blank and detail_text.size > 20
  assign meta_description = detail_text | truncate: 100
else
  # フォールバック処理
endif
  • JSONパース処理をLiquidで実装
  • 文字列分割による値抽出
  • ループ処理でテキスト結合
  • フォールバック機能(メタフィールド→商品説明→デフォルト)

メタオブジェクト対応

for store in shop.metaobjects.store.values
  assign store_page_handle = 'store/' | append: store.system.handle
  if page.handle == store_page_handle
    if store.seo_description.value != blank
      assign store_description = store.seo_description.value
# デバッグ情報をHTMLコメントとして出力
assign debug_output = '<!-- Store Debug: page.handle=' | append: page.handle | append: ', found_store=' | append: found_store | append: ', store_name=' | append: store_name_test | append: ', store_description=' | append: store_description | append: ', debug_info=' | append: debug_info | append: ' -->'
  • メタオブジェクトからの動的データ取得
  • デバッグ情報の出力機能
  • 複数店舗への対応

切り貼りして、再利用で効率を上げましょう。

解説はAIに放り込んでください。

言語切り替えに対応したい(jsonを追加する)

descriptionなどのテキストを言語切り替えに対応させたいです。
以下のローカルにあるjsonファイルを編集します。

/locales/ja.json ←日本語のテキストの登録用ファイル
/locales/en.default.json ←英語のテキストの登録用

meta-tags.liquidには、
'meta.◯◯◯' | t、のような記述があります。

↓ 例えば92行目

elsif request.page_type == 'cart' or request.path contains '/cart'
assign meta_description = 'meta.cart_description' | t

requestでcartページに適用させたい場合の状況の記述です。cart_descriptionが、キー(Key)でjsonのキー名の値(Value)を引っ張ってきます。

---

それぞれの言語のjsonファイルに"meta":のjsonを追加します。
↓日本語テキストのサンプル(ja.json

"meta": {
"cart_description": "カートページです。",
"login_description":"ログインページです。"
}

/locales/ja.json ←日本語用のテキストの登録

  },
  "meta": {
    "cart_description": "カートページです。",
    "login_description": "ログインページです。",
    "register_description": "会員登録ページです。",
    "reset_password_description": "パスワードリセットページです。",
    "account_description": "マイアカウントページです。",
    "addresses_description": "住所管理ページです。",
    "order_description": "注文履歴ページです。",
    "contact_description": "お問い合わせページです。",
    "news_description": "ニュース・お知らせページです。",
    "feature_description": "特集記事ページです。",
    "store_description": "店舗一覧ページです。",
    "404_description": "お探しのページが見つかりません。",
    "items_description": "商品詳細ページです。",
    "all_product_description": "全商品一覧ページです。",
    "new_arrivals_description": "新着商品一覧ページです。",
    "all_jeans_description": "ジーンズ商品一覧ページです。",
    "general_collection_description": "商品一覧ページです。",
    "search_description": "検索結果ページです。",
    "default_description": "公式オンラインストアです。"
  }
}

jsonファイルのお尻に”meta”を追記します。

/locales/en.default.json ←英語用のテキストの登録

  },
  "meta": {
    "cart_description": "Shopping cart page.",
    "login_description": "Login page.",
    "register_description": "Member registration page.",
    "reset_password_description": "Password reset page.",
    "account_description": "My account page.",
    "addresses_description": "Address management page.",
    "order_description": "Order history page.",
    "contact_description": "Contact page.",
    "news_description": "News and announcements page.",
    "feature_description": "Feature articles page.",
    "store_description": "Store locations page.",
    "404_description": "Page not found.",
    "items_description": "Product details page.",
    "all_product_description": "All products page.",
    "new_arrivals_description": "New arrivals page.",
    "all_jeans_description": "All jeans page.",
    "general_collection_description": "Product collection page.",
    "search_description": "Search results page.",
    "default_description": "Official online store."
  }
}

英語のjsonファイルにも”meta”をお尻に追記しましょう。
以上です。

言語切り替えの対応はけっこう面倒ですね。

そう思います。。

【AI】イラストを描いてもらった

今回の記事のキャッチ画像で使わせてもらいます「Google ImageFX」で作成した画像です。誰でもgoogleアカウントでログインして使えます。

この記事にピッタリなイラストのための考えたリクエストは、
「電子空間を自在に走り廻る光る猫型ロボット。
目当ての明るい円形のソースコードに乗って飛び始めるシーン。
左に空間を浮遊する体がポリゴンの女性が驚いている表情をする。」です。

古いプログラムを探して電子空間でロボットと飛び回るイメージです。

星間旅路のメロディ

「宇宙の静けさに包まれながら、漂流する過去の音楽を捜し求め、銀河の奥底でその旋律に耳を傾ける。」

「この電波はどこの星からきたのだろうか。」

どこかで聞いたことがあるような。