PHP Logo PHP でパンくずリストを出力(JSON-LDも)

PHP でパンくずリストを出力する方法の覚書です。

以下では PHP を使って動的にパンくずリストや JSON-LD での構造化マークアップを出力したり、パンくずリストを外部ファイル化する例などを掲載しています。

WordPress でパンくずリストや JSON-LD の構造化マークアップを出力する方法は以下を御覧ください。

WordPress パンくずリストの作成

更新日:2021年09月23日

作成日:2021年6月5日

JSON-LD での構造化マークアップを出力

検索エンジンにパンくずリストを認識させる方法として、microdata または JSON-LD を使った構造化マークアップがあります。

関連項目:JSON-LD で構造化マークアップ

以下は JSON-LD を使った構造化マークアップを動的に出力する例です。

この方法の場合、パンくずリストはサイトのフォルダ構造に一致していることが前提です。

以下はこの例のサイトのフォルダ構造で、ディレクトリごとにインデックスファイルがある構成になっています。

.
├── about   //会社概要
│   └── index.php
├── contact  //お問い合わせ
│   └── index.php
├── inc 
│   ├── footer.php
│   ├── head.php
│   ├── header.php
│   ├── side.php
│   └── vars.php //変数や関数を記述するファイル
├── index.php  //トップページ(HOME)
└── news  //ニュース
    ├── index.php
    ├── news1.php
    ├── tech  //テクノロジー
    │   ├── index.php
    │   └── tech1.php
    └── world //ワールド
        ├── index.php
        └── world1.php

この例では、パンくずリストとその構造化マークアップは関数を作成して出力します。共通で使える変数などがあるので共通のファイル(この例では vars.php)に記述します。

以下は共通で使用する変数とパンくずリストを出力する関数 breadcrumbs() の定義です。

breadcrumbs() の内容は リンクを相対パス(URL)で出力 での例とほぼ同じですが、構造化マークアップを出力する関数と共通で使用する値は関数外に定義して、関数内では $GLOBALS[ ] でアクセスしています。また、ホームではパンくずリストを出力しないようにしています。

vars.php 抜粋
//サイトをインストールしているディレクトリ(ルートディレクトリの場合は空文字 '')
//先頭にスラッシュ(/)を付け、最後はスラッシュなし
$intalled_dir = '/example/test';

//このスクリプトを実行しているページのホームディレクトリからのパス(ファイル名を含む)
$file_path = $_SERVER['SCRIPT_NAME']; 

//ディレクトリ名の代わりに表示する文字列を指定した配列
$dir_titles = [
  'about' => '会社案内',
  'contact' => 'お問い合わせ',
  'news' => 'ニュース',
  'tech' => 'テクノロジー',
  'world' => 'ワールド',
];

function breadcrumbs( $separator=' / ') {
  //ホームディレクトリからのパス(関数外で定義してあるので $GLOBALS でアクセス)
  $file_path = $GLOBALS['file_path'];

  //$_SERVER['SCRIPT_NAME'] のパス($file_path)からインストールされたディレクトリまでの文字を削除
  if($GLOBALS['intalled_dir'] !== '') {
    $file_path = str_replace($GLOBALS['intalled_dir'], "", $file_path);
  }
  // パスをスラッシュで区切った値(ディレクトリ名)の配列
  $dirs = explode("/", $file_path); 
  // 配列の空の要素を削除
  $dirs = array_values(array_filter($dirs,"strlen")); 
  
  //このページのルートディレクトリへのパス(各ページの先頭で定義)
  $path = $GLOBALS['path'];
  
  $li = '<li class="breadcrumb-item">';
  $li_current = '<li class="breadcrumb-item active" aria-current="page">';
  //リンクを相対パス($path)に
  $html = $li. '<a href="' .$path . '">HOME</a></li>';
  $url = "";
  
  //ディレクトリ名の代わりに表示する文字列
  $dir_titles = $GLOBALS['dir_titles'];
  
  for($i = 0; $i < count($dirs); $i++ ) {
    if($i === 0 ) {
      //リンク文字列の先頭に相対パスを追加
      $url .= $path .$dirs[$i];
    } else {
      $url .= "/".$dirs[$i];
    }
    
    //ファイルが index.php の場合はスキップ(index.php を表示しない)
    if($i === count($dirs) -2) {
      if(strtolower($dirs[$i + 1]) === 'index.php') {
        //それぞれのページに設定してある $title を表示
        $html .= $separator. $li_current .$dir_titles[$dirs[$i]] .'</li>';
        break;
      }else{
        $html .=  $separator . $li. '<a href="' .$url.'">' .$dir_titles[$dirs[$i]].'</a></li>';
      }
    } else {
      if($i === count($dirs) -1) {
      //それぞれのページに設定してある $title を表示
      $html .= $separator. $li_current . $GLOBALS['title'] .'</li>';
      } else {
        $html .= $separator . $li. '<a href="' .$url.'">' .$dir_titles[$dirs[$i]].'</a></li>';
      } 
    } 
  }
  //ol 要素で囲んでマークアップを作成して出力(ホームでは出力しない)
  if(!isset($GLOBALS['is_home'])) {  
    echo '<ol class="breadcrumbs">'. $html .'</ol>'; 
  }
}

以下は JSON-LD を使った構造化マークアップを動的に出力する関数 breadcrumbs_jsonld() です。

breadcrumbs_jsonld() では前述の関数外で定義されている変数($intalled_dir、$file_path、$dir_titles) も使用します。

2〜4行目はサーバーが $_SERVER['HTTPS'] の値を返さない環境用で、URL の組み立ての際、プロトコル部分の判定に使用します。

構造化マークアップにホームを含めるかや、ホームで構造化マークアップを出力するか、インデックスファイルの拡張子、ホームの文字(ラベル)などを設定し、更に関数外で定義されている変数の値を関数内で使用する変数に代入しています。

大まかな内容としては、ドキュメントルート以外にインストールした場合も考慮して $_SERVER['SCRIPT_NAME'] から取得したパスからサイトをインストールしたパスの部分を削除します。

そしてそのパスをスラッシュで分割して取得したディレクトリ名(ファイル名を含む)を配列に入れます。

構造化マークアップの @id には URL を指定するのでホームの URL を $_SERVER['HTTP_HOST'] を使って組み立てておきます。

ディレクトリ名(及びファイル名)の配列の各要素を for 文でループし、ディレクトリ名の場合は表示する文字列の配列($dir_titles)の対応する値を name に設定しています。

ファイル名の場合は、ページで設定してある変数 $title の値を name に設定していますが、ファイル名がインデックスファイルの場合はその項目はすでにディレクトリ名で出力しているので、何もしません。

また、ページの拡張子を表示しないようにしている場合は78行目のコメントアウトを外します。

詳細はコードにコメントを入れてあります。

vars.php 抜粋
//サーバーが $_SERVER['HTTPS'] の値を返さない場合で、SSL化されいる場合は $_SERVER['HTTPS'] に 'on' を設定
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and $_SERVER['HTTP_X_FORWARDED_PROTO'] === "https") {
  $_SERVER['HTTPS'] = 'on';
} 
//Home の URL の組み立て
$host_url = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://'). $_SERVER['HTTP_HOST'];

function breadcrumbs_jsonld() {
  //構造化マークアップにホームを含めるかどうか(含めない場合は false に変更)
  $include_home = true;
  //ホームで構造化マークアップを出力するかどうか(出力する場合は true に変更)
  $show_at_home = false;
  //ファイルの拡張子(必要に応じて変更)
  $extension = '.php';
  //インデックスファイル名
  $index_file = 'index' . $extension;
  //ホームの文字(必要に応じて変更)
  $home = 'HOME';
  //Home の URL 
  $host_url = $GLOBALS['host_url'];
  //ディレクトリ名の代わりに表示する文字列(関数外で定義→ $GLOBALS[] でアクセス)
  $dir_titles = $GLOBALS['dir_titles'];
  //ホームディレクトリからのパス(関数外で定義)
  $file_path = $GLOBALS['file_path'];
  //インストールされたディレクトリまでの文字(関数外で定義)
  $intalled_dir = $GLOBALS['intalled_dir'];
  //そのページのタイトル(各ページで定義)
  $page_title =  $GLOBALS['title'];
  //ホームかどうかの判定用変数(ホームのみで定義)
  $is_home = isset($GLOBALS['is_home']) ? true : false;
  
  //出力文字列の初期化(構造化マークアップの先頭部分)
  $str ='{
  "@context": "http://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement":
  ['."\n"; 
  //position カウンター
  $count = 1;
  
  //パス($file_path)からインストールされたディレクトリまでの文字を削除
  if($intalled_dir !== '') {
    $file_path = str_replace($intalled_dir, "", $file_path);
  }
  // パスをスラッシュで区切った値(ディレクトリ名)の配列
  $dirs = explode("/", $file_path); 
  // 配列の空の要素を削除(先頭は常に空)
  $dirs = array_values(array_filter($dirs)); 
  
  //ホスト($host_url)以降のパス
  //ルートディレクトリにサイトがインストールされていれば $intalled_dir は空 ''
  $url_path = $intalled_dir;
  
  //カンマ部分に使用する変数
  $comma =',';
  
  if($include_home) {
    //構造化マークアップにホームを含める場合(デフォルト)
    if($show_at_home && $is_home)  $comma ='';
    $home_str = '{"@type": "ListItem",
    "position": '.$count.',
    "item":{
      "@id":"'. $host_url. $url_path . '/",
      "name":"'.$home. '"}}' .$comma. "\n";
    $count++;
    $str.= $home_str; 
  }
  for ( $i = 0; $i < count( $dirs ); $i++ ) {
    //ホスト($host_url)以降のパス
    $url_path = $url_path . '/' . $dirs[ $i ];
    //"name" に表示する値(ホームの場合は $home に指定した文字)
    $name = '';
    //インデックスファイル以外の場合
    //インデックスファイルの場合はそのディレクトリで出力されるので何も出力しない
    if(strpos($dirs[$i], $index_file) === false) { 
      if($i === count($dirs) - 1) { 
        //ファイルの拡張子を表示しない場合は、以下のコメントアウトを外す
        //$url_path = str_replace( $extension, '' , $url_path );
        $str.= '{"@type": "ListItem",
        "position": '.$count.',
        "item":{
          "@id":"'. $host_url. $url_path. '",
          "name":"'.$page_title. '"}}' ."\n";
          //ファイルの場合はそのページで設定されているタイトル($page_title)を使用
      }else{
        $comma =',';
        if($i === count($dirs) - 2 && (strpos($dirs[$i + 1], 'index.php') !== false)) $comma ='';
        $str.= '{"@type": "ListItem",
        "position": '.$count.',
        "item":{
          "@id":"'. $host_url. $url_path. '/",
          "name":"'.$dir_titles[$dirs[$i]]. '"}}' .$comma. "\n";
          //配列 $dir_titles からディレクトリ名をキーとして表示する文字列を取得
      }
      $count ++;
    }
  }
  $str.= "\n".']'."\n".'}'."\n";
  
  if($show_at_home) {
    echo  '<script type="application/ld+json">' ."\n". $str . '</script>'."\n";
  }else{
    //ホームでは表示しない
    if(!$is_home) {  
      echo  '<script type="application/ld+json">' ."\n". $str . '</script>'."\n";
    }
  }
}

使い方は出力したい場所で breadcrumbs_jsonld() を呼び出します。

例えば、全てのページの共通ファイル footer.php などに記述します。

footer.php
<footer>
  <div class="container">
    <p><small>&copy;Copyright</small></p>
  </div>
</footer>
<script src="<?php echo $path; ?>js/main.js?<?php echo filemtime($path.'js/main.js'); ?>"></script>
<?php 
breadcrumbs_jsonld();  //構造化マークアップを出力
?>

出力例

以下はローカル環境(http://localhost/example/news/)での出力例です。$intalled_dir にはサイトをインストールした '/example' を指定しています。

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement":
  [
{"@type": "ListItem",
    "position": 1,
    "item":{
      "@id":"http://localhost/example/",
      "name":"HOME"}},
{"@type": "ListItem",
        "position": 2,
        "item":{
          "@id":"http://localhost/example/news/",
          "name":"ニュース"}}
]
}
</script>

以下は http://localhost/example/news/world/world1.php での出力例です。

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement":
  [
{"@type": "ListItem",
    "position": 1,
    "item":{
      "@id":"http://localhost/example/",
      "name":"HOME"}},
{"@type": "ListItem",
        "position": 2,
        "item":{
          "@id":"http://localhost/example/news/",
          "name":"ニュース"}},
{"@type": "ListItem",
        "position": 3,
        "item":{
          "@id":"http://localhost/example/news/world/",
          "name":"ワールド"}},
{"@type": "ListItem",
        "position": 4,
        "item":{
          "@id":"http://localhost/example/news/world/world1.php",
          "name":"World ニュース 1 (日本語)"}}

]
}
</script>

サンプルページ(出力はページのソースを表示して確認できます。但し、ホームでは出力していません。)

出力のテスト

構造化マークアップに問題がないかは Google の「リッチリザルトテスト」で確認することができます。

テストページでは URL を指定することも、構造化マークアップを入力することもできます。

テストを実行してエラーが表示されなければ問題ありません。

エラーが表示されたら、詳細を確認して修正します。

詳細の行番号が表示されているところをクリックするとその箇所がハイライトされます。

以下の例の場合、「行 12: 1」とあり、原因は11行目の最後にカンマがないことでした。

2ヶ国語サイトの例

パンくずリストの出力の 2ヶ国語サイトの例 と同じ構成のサイトでの、構造化マークアップを出力する例です。

以下がサイトの構成です。

.
├── about  //日本語(会社概要)
│   └── index.php
├── contact  //日本語(お問い合わせ)
│   └── index.php
├── en  //英語ページ
│   ├── about  //英語(About Us)
│   │   └── index.php
│   ├── contact  //英語(Contact Us)
│   │   └── index.php
│   ├── index.php //英語トップページ(Top)
│   └── news  //英語(News)
│       ├── index.php
│       ├── news1.php
│       ├── tech  //英語(Technology)
│       │   ├── index.php
│       │   └── tech1.php
│       └── world  //英語(World)
│           ├── index.php
│           └── world1.php
├── index.php  //日本語トップページ(HOME)
└── news  //日本語(ニュース)
    ├── index.php
    ├── news1.php
    ├── tech  //日本語(テクノロジー)
    │   ├── index.php
    │   └── tech1.php
    └── world  //日本語(ワールド)
        ├── index.php
        └── world1.php

構造化マークアップの出力方法は、日本語だけの場合とほとんど同じです。

以下の例では、全ての英語ページで英語ページかどうかの判定用の変数 $is_en = true; を設定してあります。また、パンくずリストを出力する関数と構造化マークアップを出力する関数では共通の値を使用するので、関数の外側でそれらの変数を定義し、関数の中では $GLOBALS[ ] でアクセスするようにしています。

パンくずリストを出力する関数 breadcrumbs() と構造化マークアップを出力する関数 breadcrumbs_jsonld() は同じファイル(vars.php)に記述してあります。

vars.php
//サイトをインストールしているディレクトリ(ルートディレクトリの場合は空文字 '')
//先頭にスラッシュ(/)を付け、最後はスラッシュなし
$intalled_dir = '/example/test';

//このスクリプトを実行しているページのホームディレクトリからのパス(ファイル名を含む)
$file_path = $_SERVER['SCRIPT_NAME']; 

//$_SERVER['SCRIPT_NAME'] のパス($file_path)からインストールされたディレクトリまでの文字を削除
if($intalled_dir !== '') {
  $file_path = str_replace($intalled_dir, "", $file_path);
}
// パスをスラッシュで区切った値(ディレクトリ名)の配列
$dirs = explode("/", $file_path); 
// 配列の空の要素を削除(先頭は常に空)
$dirs = array_values(array_filter($dirs,"strlen")); 

//ディレクトリ名の代わりに表示する文字列を指定した配列
//日本語ページ用
$dir_titles = [
  'about' => '会社案内',
  'contact' => 'お問い合わせ',
  'news' => 'ニュース',
  'tech' => 'テクノロジー',
  'world' => 'ワールド',
  'en' => 'Top'
];

if(isset($is_en)) {
  //英語ページ用
  $dir_titles = [
    'about' => 'About Us',
    'contact' => 'Contact Us',
    'news' => 'News',
    'tech' => 'Technology',
    'world' => 'World',
    'en' => 'Top'
  ];
}

function breadcrumbs( $separator=' / ') {
  //ホームディレクトリからのパス(関数外で定義)
  $file_path = $GLOBALS['file_path'];
  
  // パスをスラッシュで区切った値(ディレクトリ名の配列。関数外で定義)
  $dirs = $GLOBALS['dirs'];
  
  // 英語ページかどうかの判定用変数(全ての英語ページでは $is_en を設定してある)
  $is_en = isset($GLOBALS['is_en']) ? true: false;

  //このページのルートディレクトリへのパス(各ページの先頭で定義)
  $path = $GLOBALS['path'];
  
  $li = '<li class="breadcrumb-item">';
  $li_current = '<li class="breadcrumb-item active" aria-current="page">';
  //リンクを相対パス($path)に
  $html = $li. '<a href="' .$path . '">HOME</a></li>';
  if($is_en) {   
    //英語ページでは HOME を表示しない
    $html = '';
  }
  $url = "";
  
  //ディレクトリ名の代わりに表示する文字列を指定した配列(関数外で定義)
  $dir_titles = $GLOBALS['dir_titles'];

  for($i = 0; $i < count($dirs); $i++ ) {
    if($i === 0 ) {
      //リンク文字列の先頭に相対パスを追加
      $url .= $path .$dirs[$i];
    } else {
      $url .= "/".$dirs[$i];
    }
    //ファイルが index.php の場合はスキップ(index.php を表示しない)
    if($i === count($dirs) -2) {
      if(strtolower($dirs[$i + 1]) === 'index.php') {
        //それぞれのページに設定してある $title を表示
        $html .= $separator. $li_current .$dir_titles[$dirs[$i]] .'</li>';
        if($is_en && $i === 0 ){
          //英語トップページ
          $html =$dir_titles[$dirs[$i]] ;
        }
        break;
      }else{
        $html .=  $separator . $li. '<a href="' .$url.'">' .$dir_titles[$dirs[$i]].'</a></li>';
      }
    } else {
      //英語ページの先頭ではセパレータを非表示
      if($is_en && $i === 0 ){
        $html .=  $li. '<a href="' .$url.'">' .$dir_titles[$dirs[$i]].'</a></li>';
      }else{
        if($i === count($dirs) -1) {
        //それぞれのページに設定してある $title を表示
        $html .= $separator. $li_current . $GLOBALS['title'] .'</li>';
        } else {
          $html .= $separator . $li. '<a href="' .$url.'">' .$dir_titles[$dirs[$i]].'</a></li>';
        } 
      } 
    } 
  }
  //ol 要素で囲んでマークアップを作成して出力(ホームでは出力しない)
  if(!isset($GLOBALS['is_home'])) {  
    echo '<ol class="breadcrumbs">'. $html .'</ol>'; 
  }
}

//SSL化されいる場合は $_SERVER['HTTPS'] に 'on' を設定
if(isset($_SERVER['HTTP_X_FORWARDED_PROTO']) and $_SERVER['HTTP_X_FORWARDED_PROTO'] === "https") {
  $_SERVER['HTTPS'] = 'on';
} 
//Home の URL の組み立て
$host_url = (empty($_SERVER['HTTPS']) ? 'http://' : 'https://'). $_SERVER['HTTP_HOST'];
 
function breadcrumbs_jsonld() {
  //構造化マークアップにホームを含めるかどうか(含めない場合は false に変更)
  $include_home = true;
  //ホームで構造化マークアップを出力するかどうか(出力する場合は true に変更)
  $show_at_home = false;
  //ファイルの拡張子(必要に応じて変更)
  $extension = '.php';
  //インデックスファイル名
  $index_file = 'index' . $extension;
  //ホームの文字(必要に応じて変更)
  $home = 'HOME';
  //Home の URL 
  $host_url = $GLOBALS['host_url'];
  //ディレクトリ名の代わりに表示する文字列(関数外で定義→ $GLOBALS[] でアクセス)
  $dir_titles = $GLOBALS['dir_titles'];
  //ホームディレクトリからのパス(関数外で定義)
  $file_path = $GLOBALS['file_path'];
  //インストールされたディレクトリまでの文字(関数外で定義)
  $intalled_dir = $GLOBALS['intalled_dir'];
  //そのページのタイトル(各ページで定義)
  $page_title =  $GLOBALS['title'];
  //ホームかどうかの判定用変数(ホームのみで定義)
  $is_home = isset($GLOBALS['is_home']) ? true : false;
  // 英語ページかどうかの判定用変数(★★追加★★)
  $is_en = isset($GLOBALS['is_en']) ? true: false;
  
  //出力文字列の初期化(構造化マークアップの先頭部分)
  $str ='{
  "@context": "http://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement":
  ['."\n"; 
  //position カウンター
  $count = 1;
  
  //パス($file_path)からインストールされたディレクトリまでの文字を削除
  if($intalled_dir !== '') {
    $file_path = str_replace($intalled_dir, "", $file_path);
  }
  // パスをスラッシュで区切った値(ディレクトリ名)の配列
  $dirs = explode("/", $file_path); 
  // 配列の空の要素を削除(先頭は常に空)
  $dirs = array_values(array_filter($dirs)); 
  
  //ホスト($host_url)以降のパス
  //ルートディレクトリにサイトがインストールされていれば $intalled_dir は空 ''
  $url_path = $intalled_dir;
  
  //カンマ部分に使用する変数
  $comma =',';
  
  //$include_home(ホームを含めるかどうか)が true(デフォルト)で、英語ページでない場合
  if($include_home && !$is_en) {
    if($show_at_home && $is_home)  $comma ='';
    $home_str = '{"@type": "ListItem",
    "position": '.$count.',
    "item":{
      "@id":"'. $host_url. $url_path . '/",
      "name":"'.$home. '"}}' .$comma. "\n";
    $count++;
    $str.= $home_str; 
  }
  for ( $i = 0; $i < count( $dirs ); $i++ ) {
    //ホスト($host_url)以降のパス
    $url_path = $url_path . '/' . $dirs[ $i ];
    //"name" に表示する値(ホームの場合は $home に指定した文字)
    $name = '';
    //インデックスファイル以外の場合
    //インデックスファイルの場合はそのディレクトリで出力されるので何も出力しない
    if(strpos($dirs[$i], $index_file) === false) { 
      if($i === count($dirs) - 1) { 
        //ファイルの拡張子を表示しない場合は、以下のコメントアウトを外す
        //$url_path = str_replace( $extension, '' , $url_path );
        $str.= '{"@type": "ListItem",
        "position": '.$count.',
        "item":{
          "@id":"'. $host_url. $url_path. '",
          "name":"'.$page_title. '"}}' ."\n";
          //ファイルの場合はそのページで設定されているタイトル($page_title)を使用
      }else{
        $comma =',';
        if($i === count($dirs) - 2 && (strpos($dirs[$i + 1], 'index.php') !== false)) $comma ='';
        $str.= '{"@type": "ListItem",
        "position": '.$count.',
        "item":{
          "@id":"'. $host_url. $url_path. '/",
          "name":"'.$dir_titles[$dirs[$i]]. '"}}' .$comma. "\n";
          //配列 $dir_titles からディレクトリ名をキーとして表示する文字列を取得
      }
      $count ++;
    }
  }
  $str.= "\n".']'."\n".'}'."\n";
  
  if($show_at_home) {
    echo  '<script type="application/ld+json">' ."\n". $str . '</script>'."\n";
  }else{
    //ホームでは表示しない
    if(!$is_home) {  
      echo  '<script type="application/ld+json">' ."\n". $str . '</script>'."\n";
    }
  }
}

出力例

以下はローカル環境にバーチャルホストを設定した場合の http://example.localhost/en/news/tech/tech-news1.php での出力例です。$intalled_dir には空文字列 '' を指定しています。

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement":
  [
{"@type": "ListItem",
        "position": 1,
        "item":{
          "@id":"http://example.localhost/en/",
          "name":"Top"}},
{"@type": "ListItem",
        "position": 2,
        "item":{
          "@id":"http://example.localhost/en/news/",
          "name":"News"}},
{"@type": "ListItem",
        "position": 3,
        "item":{
          "@id":"http://example.localhost/en/news/tech/",
          "name":"Technology"}},
{"@type": "ListItem",
        "position": 4,
        "item":{
          "@id":"http://example.localhost/en/news/tech/tech-news1.php",
          "name":"Tech News 1  (English)"}}

]
}
</script>

以下は日本語ページ http://example.localhost/news/tech/tech-news1.php での出力例です。

<script type="application/ld+json">
{
  "@context": "http://schema.org",
  "@type": "BreadcrumbList",
  "itemListElement":
  [
{"@type": "ListItem",
    "position": 1,
    "item":{
      "@id":"http://example.localhost/",
      "name":"HOME"}},
{"@type": "ListItem",
        "position": 2,
        "item":{
          "@id":"http://example.localhost/news/",
          "name":"ニュース"}},
{"@type": "ListItem",
        "position": 3,
        "item":{
          "@id":"http://example.localhost/news/tech/",
          "name":"テクノロジー"}},
{"@type": "ListItem",
        "position": 4,
        "item":{
          "@id":"http://example.localhost/news/tech/tech-news1.php",
          "name":"Tech ニュース 1(日本語)"}}

]
}
</script>

サンプルページ(出力はページのソースを表示して確認できます。但し、ホームでは出力していません。)