yonetch Works XML化プロジェクトページ

〜XMLおよび周辺技術習得のためのメモ〜

本サイト自体のコンテンツを例題として、XMLを中心とした(必ずしもコンテンツ管理に限らない)データ処理に役立つ要素技術を集めるためのメモページです。かなり取り留めのないものになりそうな予感...
2018.11.07 XML Notepad 対策
●気に入ってはいるんだが
このサイトの制作環境として使ってきた XML Notepad だが、(フリーソフトとしては)かなり優秀なXMLエディタだと思う。だがしかし、かれこれ1年以上使い込んでみると、少々不満がないわけではない。
●クソ重い!
その中でもどうしてもガマンできないものがある。機能上の不備とか、ワタシの要求が特殊すぎるとかいうことなら仕方がないが、そうではない。それというのも、
重いXMLファイルを読み込むと、最初に編集するときだけ、10分以上もの無応答が起こる
のだ。この「編集」とは、何かテキスト入力するとか、要素等を追加するとかの、ごく普通のことである。そして、その無応答タイムをやりすごすと、その後は何の問題もなく編集ができるからワケがわからない(ちなみに無応答の間、CPU使用率は100%で張り付くので何らかの前処理的なことをしているのだろうが...)。
備考: 翻訳ツッコミで劇場版を採りあげたとき、3000余ある字幕台詞を全て事前変換しておくと重くて使い物にならないと前に述べたが、主な現象はコレだった。
備考: 本来ならXML Notepadの方での問題として何か対処法を探したいところだが、サポートサイトを覗いても作者からの返信が絶えて久しいようだし、如何ともしがたい
●省略形
今回この現状に対応すべく、ツッコミ原稿形式のマイナーチェンジを検討した。とにかく無駄な情報を減らし、絶対量を圧縮することを考える。日頃冗長と考えていたのは、ある台詞を構成する字幕情報を記述する際に、個々のIDをすべて列挙していることだ(以下参照)。
備考: 値のない<字幕id>要素は、[omit]を意味する
<原語> <字幕id>subtitle1</字幕id> <字幕id>subtitle2</字幕id> <字幕id>subtitle3</字幕id> <字幕id></字幕id> <字幕id>subtitle5</字幕id> <字幕id>subtitle6</字幕id> <字幕id>subtitle7</字幕id> <字幕id>subtitle8</字幕id> <字幕id>subtitle9</字幕id> <字幕id>subtitle10</字幕id> </原語>

【従来形式】

だが台詞引用においては、字幕IDは降順に連続するものがほとんどである。そこで以下のように連続する数を「続」という属性値に入れておくものとした。
<原語> <字幕id 続="2">subtitle1</字幕id> <字幕id></字幕id> <字幕id 続="5">subtitle5</字幕id> </原語>

【新形式】

●インプリメンテーション
さてそれを具現化するアルゴリズムだが、普通の手続き型原語ならば For ループの類で処理すればいいだけの単純なことだ。だが!XSLTの場合は再起的にやるしかない。メンドクサ〜... と嘆いても始まらないので頑張って書いてみたコードが以下のとおり(抜粋)。

<xsl:template match="字幕id">
<xsl:value-of select="concat(msxsl:node-set($字幕)//tt:p[@xml:id=$id],' ')"/> <xsl:value-of select="@続"/> <xsl:if test="@続 and @続 &gt; 0"> <xsl:call-template name="loop"> <xsl:with-param name="i" select="1"/> <xsl:with-param name="i_limit" select="@続"/> <xsl:with-param name="字幕" select="$字幕"/> <xsl:with-param name="id" select="$id"/> </xsl:call-template> </xsl:if> </xsl:template> <xsl:template name="loop"> <xsl:param name="i"/> <xsl:param name="i_limit"/> <xsl:param name="i_inc">1</xsl:param> <xsl:param name="字幕"/> <xsl:param name="id"/> <xsl:if test="$i &lt;= $i_limit"> <xsl:value-of select="concat(' ',msxsl:node-set($字幕)//tt:p[@xml:id=$id]/following-sibling::*[$i])"/> <xsl:variable name="i1" select="$i + $i_inc"/> <xsl:call-template name="loop"> <xsl:with-param name="i" select="$i1"/> <xsl:with-param name="i_limit" select="$i_limit"/> <xsl:with-param name="i_inc" select="$i_inc"/> <xsl:with-param name="字幕" select="msxsl:node-set($字幕)"/> <xsl:with-param name="id" select="$id"/> </xsl:call-template> </xsl:if>
●既存ファイルにも
次はこの形式に対応したXMLファイルを生成するスタイルシートが必要だ。TTMLから直接このように変換する(連続する字幕idを数える)ことに少し挑戦したが、挫折したorz。経緯は省くが、一旦従来形式で作ったXMLファイルに対して、再度XSLTで変換をかける方がラクだと思い、そのようにした。
<?xml version="1.0" encoding="Shift_JIS" ?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" >
  <xsl:output method="xml" encoding="Shift_JIS"/>
  <xsl:include href="..\..\xml\commonlib.xsl" />
  <xsl:template match="エピ">
    <xsl:element name="エピ">
      <xsl:attribute name="状態">下書き</xsl:attribute>
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>
  <xsl:template match="台詞">
    <xsl:element name="台詞">
      <xsl:attribute name="状態">
        <xsl:value-of select="@状態"/>
      </xsl:attribute>
      <xsl:attribute name="人物">
        <xsl:value-of select="@人物"/>
      </xsl:attribute>
      <xsl:apply-templates/>
    </xsl:element>
  </xsl:template>

  <xsl:template match="原語[字幕id!='']">
    <xsl:element name="原語">
      <xsl:element name="字幕id">
        <xsl:attribute name="続">
          <xsl:if test="count(字幕id) &gt; 1">
            <xsl:value-of select="count(字幕id)-1"/>
          </xsl:if>
        </xsl:attribute>
        <xsl:value-of select="字幕id[1]"/>
      </xsl:element>
    </xsl:element>
  </xsl:template>
  <xsl:template match="原語[not(字幕id) or 字幕id='']">
    <xsl:copy-of select="."/>
  </xsl:template>
  <xsl:template match="吹替|直訳|所感|余談|原題|更新日|字幕ソース">
    <xsl:copy-of select="."/>
  </xsl:template>
</xsl:stylesheet>
●他にもあるよ
一応できた... この方法で圧縮したところ、300KBのXMLファイルが200KBぐらいにはなった。大して変化がないようにも思うが、無応答タイムもほぼなくなったので、とりあえずの目的は達したとしておこう。ただし!XML Notepad にはなんとかしてほしいところがまだある。自分メモ的に記しておく。
備考: ちなみに今は XML Notepadのプレビュータブは使わず、IEをプレビュー用に使っている。これならAlt+Tabで切り替え、[F5]で表示することができるので多少はマシ。
2018.08.16 ついにトップが!
●更新日記とともに
RSS生成に成功して以来、更新日記とともに自動生成がかかり、ずいぶんコンテンツ更新作業が楽になった。にもかかわらず、当初目標の最後のひとつ、
トップページの What's New (直近の更新3項目のリスト)
だけは手作業での更新を続けていた。
●まだメドが...
それというのも、「直近の更新3項目」のみを拾うのが案外難しいからだ。機械的に3項目だけなら左程でもないが、一度に翻訳ツッコミ3件を更新したら?それだけがトップページに並んでいてもツマラナイではないか。これはまとめて1件の更新とし、他の更新情報をできるだけ表示するなどしたいものだ。
●考えすぎか?
などなど、今まで手作業で行ったような内容を自動化しようとゴチャゴチャ考えていると、例によってそれをインプリするにはまだまだスキルの不足をかんじる... が!ちょっと角度を変えて考えてみると、自動化をゼロイチで諦めるのではなく、手作業の前段階と捉えるべきと気づいた。すなわち、せめて日を変えた更新3日分ぐらいを自動抽出し、あとは手作業で直すようにする。
●できた!のだが...
ということで、更新情報の表示他、トップページHTMLの生成はできたのだが、アクセスカウンターのCGI部分でつまずいたorz。そもそもサイト開設当初の2004年当時から以下の形式で置いてある。
<img src="/cgi-bin/Count.cgi&df=yonetch.dat&dd=C&ft=0" alt="***" align=absmiddle>
... ちょっとコレ、XHTML的見地からすると、酷すぎだろっ!
●ひでぇ...
何が問題って、
  1. 閉じタグがない
  2. リテラルを"(ダブルクォート)で囲ってない
  3. &(アンパサンド)がそのまま
とまぁ、こんな短いタグに3個も問題が... まぁ、1.、2.はまだいい。ないなら付けりゃいいだけだ。しかし3.が問題ありすぎ!XHTML的には & は &amp;としなければならない。だがこれはCGIのパラメータのセパレータなワケで、どうすりゃいいのか皆目見当もつかなかった。
●そっちが本物?
単純に考えると&amp;と書き換えるしかないのだけど、ソレがサーバに渡って動くわけないし... と思ってググってみると、本当にそれでいい(というか、本来そうあるべき)らしい。
参考: ばっちり書けてますかhref= – 特に&(&amp;)などを含む場合 – 属性値としてのCDATA
ただ、Coralnetのサーバでは、そうはなっておらず、XHTML的に
<img src="/cgi-bin/Count.cgi&amp;df=yonetch.dat&amp;dd=C&amp;ft=0" alt="***" align="absmiddle"/>
とやるとカウンターは表示されない。それどころか、リテラルを"でかこっただけでもダメだったのだからあきれる(^^;
●また小手先で...
しかたがないので、カウンター設置部分にコメントで特定の文字列を入れておき、それを最後にPerlで文字列置換することでしのぐことにした。
備考: ちなみに、FC2のアクセス解析タグも同じく & の問題があったのだが、こちらは &amp; を受け入れるようだ。
2018.08.15 スタックオーバーフロー
●SRTソース再び
DS9ツッコミの過程で、ネトフリ字幕でなくDVD字幕をソースとして使うことにした。SRT形式のDVD字幕をTTML形式に変換する(これは去年既出のスクリプトを使えばOK)が、これでつくったTTMLから、ツッコミ原稿形式への変換でハマった。台詞がどれひとつ拾われず、エピの外枠のみ吐いて終了、だった。行末を判定している正規表現で、対象に改行(\n?の部分)が入っているかどうかでマッチ/アンマッチが変わってしまっていた(少々納得いかないが)。
  <xsl:if test="yn:contains(string(.),'[^\.][\)\]\?\.!]&quot;?\n?$')">

【行末判定部】

●システムエラー
ツッコミ原稿形式への変換でもうひとつ重大な問題が。これは前々からのことではあったのだけれど、対象TTMLによって、以下のシステムエラーが出るものがあるのだ。
C:\>msxsl -u 4.0 yn_DS9_3-12.ttml ttml2tsukkomi.xsl

Error occurred while executing stylesheet 'ttml2tsukkomi.xsl'.

Code:   0x80004005
システム エラー: -2146828260

プロパティまたはメソッド 'contains' の呼び出し中にエラーが発生しました。
'contains'の呼び出し中に、とあるからには、正規表現対応させたJScript版のyn:containsが原因... なのか?
●スタックオーバーフロー?
このシステムエラーについて、散々ググってみたが、類例は見つからなかった。エラーがでたりでなかったりというのは本当に頭を抱える。テキスト内の特定のパターンとか特殊文字とか何が悪いのか散々探したが徒労に終わった。そこでふと思い当たったのは、「兄弟要素のグループ化」ルーチンのポイントが、
再起呼び出し
だということだ。上記のコードの意味はよくわからないが、ひょっとしてスタックオーバーフローなのではないか?つまり、JScriptの関数そのものが原因なのではなく、スタック領域を食い尽くしているだけなのかもしれない。
●判定法変更
そこで、TTML自身に行末であるか否かの情報を埋め込むことにした。
<p xml:id="172" term="">
First Officer's Log,
supplemental.
</p>
<p xml:id="173" term="">
Somehow, Sisko, Dax and Bashir
have altered Earth's history.
</p>
<p xml:id="174">
We have no choice but to send
an away team into the past
</p>
<p xml:id="175" term="">
to try to find them and correct
the changes to the timeline.
</p>

【ツッコミ形式変換用TTML(DS9"PAST TENSE, PART II"より)】

変更前: <xsl:if test="yn:contains(string(.),'[^\.][\)\]\?\.!]&quot;?\n?$')">
変更後: <xsl:if test="@term">

【ttml2tsukkomi.xsl 判定部変更内容】

●すんなり
ちょっとゴチャゴチャしてしまったが、今回のミソは、本物の(?)TTMLのほかに、ツッコミ形式への変換を前提とした中間ファイル的なTTMLファイルを作ることにある。
  1. オリジナルTTML or SRT → yonetch版TTML & 中間形式TTML
  2. 中間形式TTML → ツッコミ形式ファイル(←yonetch版TTMLを参照する)
以上の手順で変換を行うとごくすんなり成功した。結果的に、スタックを消費するような部分では、JScriptは使うべきでない、というところだろうか(根拠に乏しいが)。
2018.08.04 yonetch版TTMLモドキ生成、再び
●時系列メモ更新に当たって
前回本稿で述べたとおり、TTMLをツッコミ原稿形式に変換することで、作業効率アップを図ることができた。その結果新エピのネトフリTTMLをyonetch版TTMLに変換するという作業も増えた。現状の対象はDS9、VGRで、それらのネトフリTTMLは台詞行ごとに<p>タグに分割されており、(DSCと比べて)変換のしやすい単純な構成となっていた。
<p begin="15419992503t" end="15438760002t" region="region_00" tts:extent="40.00% 5.33%" tts:origin="10.00% 79.29%" xml:id="subtitle722" agent="">What do you think</p>
<p begin="15419992503t" end="15438760002t" region="region_01" tts:extent="47.50% 5.33%" tts:origin="10.00% 84.62%" xml:id="subtitle723" agent="">you're doing, Quark?</p>
<p begin="15467955001t" end="15489645003t" region="region_00" tts:extent="27.50% 5.33%" tts:origin="47.50% 79.29%" xml:id="subtitle727" agent="">Oh, you mean</p>
<p begin="15467955001t" end="15489645003t" region="region_01" tts:extent="40.00% 5.33%" tts:origin="47.50% 84.62%" xml:id="subtitle728" agent="">this holo-imager.</p>
<p begin="15489645003t" end="15506325000t" region="region_00" tts:extent="47.50% 5.33%" tts:origin="27.50% 79.29%" xml:id="subtitle729" agent="">I was just recording</p>
<p begin="15489645003t" end="15506325000t" region="region_01" tts:extent="60.00% 5.33%" tts:origin="27.50% 84.62%" xml:id="subtitle730" agent="">an image of the Promenade</p>
<p begin="15506325000t" end="15529685003t" region="region_00" tts:extent="27.50% 5.33%" tts:origin="57.50% 68.63%" xml:id="subtitle731" agent="">to send home</p>
<p begin="15506325000t" end="15529685003t" region="region_01" tts:extent="30.00% 5.33%" tts:origin="57.50% 73.96%" xml:id="subtitle732" agent="">to my mother.</p>

【ネトフリTTML(DS9"MERIDIAN"より)】

●アチャー
しかし、 DS9"FASCINATION"シーズン3-10: 恋の感謝祭は違っていた。一画面上の台詞2行ごとに<p>タグ化され、中に<br/>、すなわち改行が入っている。さらには、2人の台詞が一画面に1行ずつ表示されるパターンもあった。
<p begin="7680589582t" end="7698524166t" region="region.after" style="defaultStyle" xml:id="subtitle210">If you need to sleep, go ahead.</p>
<p begin="7699358332t" end="7719378332t" region="region.after" style="defaultStyle" xml:id="subtitle211">-I'll understand.<br/>-No, you won't.</p>
<p begin="7720212499t" end="7731473749t" region="region.after" style="defaultStyle" xml:id="subtitle212">You'll be disappointed</p>
<p begin="7732307916t" end="7764840416t" region="region.after" style="defaultStyle" xml:id="subtitle213">and you'll start brooding<br/>and stomping around like an Andorian bull.</p>

【ネトフリTTML(DS9"FASCINATION"より)】

●ハラくくって
これはDSCとよく似たパターンだ(しかし、DSCとも若干異なっているが)。最近のツッコミ対象がDS9だったから目を逸らしていたが、仕方がないのでDSCタイプも含めてXSLTによるTTMLモドキ作成の xsl テンプレート作成にとりかかった。以前は Perl で変換スクリプトを書いた(が、多分にハンパなものだった)。その理由は、前の記事でも書いたが、台詞分割の部分を XSLT でやるだけの知識がなかったからだ。
●満を持して
だがあれから半年、アレやコレやでスタイルシートを書いてきた知識を総動員して、何とか形にすることができた(まだまだ完全ではないが)。
●御開帳〜
そういうワケで、できたのが以下のスタイルシートです。まだ対応しきれていない点として、ということがあります。
<?xml version="1.0" encoding="shift_jis"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tt="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" xmlns="http://www.w3.org/ns/ttml" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns:yn="urn:yonetch">
  <xsl:output method="xml" encoding="Shift_JIS"/>

  <!-- 07-29版 台詞内に改行が入っている場合に対応 -->
  <!-- 08-02版 DSC式に [Burnham] のように役名が入っている場合の agent化に対応 -->
  <!-- 08-04版 DS9式に SISCO: のように役名が入っている場合の agent化にに対応 -->

  <xsl:include href="..\mysite\xml\commonlib.xsl" />
  <xsl:key name="スタイル情報" match="tt:style" use="@tts:fontStyle"/>
  <xsl:variable name="ItaSty">
    <xsl:value-of select="key('スタイル情報','italic')/@xml:id"/>
  </xsl:variable>

  <xsl:template match="tt:tt">
    <tt>
      <xsl:apply-templates/>
    </tt>
  </xsl:template>

  <xsl:template match="tt:head|tt:styling|tt:layout|ttp:profile|tt:region">
    <xsl:copy>
    <xsl:for-each select="@*">
      <xsl:copy />
    </xsl:for-each>
    <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="tt:style[@tts:fontStyle='italic']">
    <xsl:copy>
    <xsl:for-each select="@*">
      <xsl:choose>
        <xsl:when test="name()='xml:id'">
          <xsl:attribute name="xml:id">italic
          </xsl:attribute>
        </xsl:when>
        <xsl:otherwise>
          <xsl:copy />
        </xsl:otherwise>
      </xsl:choose>
    </xsl:for-each>
    <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>

  <xsl:template match="tt:style[not(@tts:fontStyle)]">
    <xsl:copy>
    <xsl:for-each select="@*">
      <xsl:copy />
    </xsl:for-each>
    <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="tt:body">
    <body>
      <xsl:apply-templates/>
    </body>
  </xsl:template>

  <xsl:template match="tt:div">
    <div>
      <xsl:apply-templates/>
    </div>
  </xsl:template>

  <xsl:template match="tt:p[not(tt:br)]">
    <xsl:copy>
    <xsl:for-each select="@*">
      <xsl:copy />
    </xsl:for-each>
          <xsl:attribute name="agent">
	<xsl:value-of select="yn:replace(string(.),'\[([^\]]+)\].+','$1')"/>
	<xsl:value-of select="yn:replace(string(.),'([^:]+):.+','$1')"/>
	<!-- DSC式とDS9式が同時に使われることはないという前提で併記 -->
          </xsl:attribute>
    <xsl:apply-templates/>
    </xsl:copy>
  </xsl:template>

  <xsl:template match="tt:p/text()">
	<xsl:value-of select="yn:replace(string(.),'([^:]+:)?(.+)','$2')"/>
	<!-- DS9式の地の文から役名を削除 -->
	<!-- 役名以外の目的で:(コロン)が使われていたら誤動作するだろう -->
  </xsl:template>

  <xsl:template match="tt:p[tt:br]">
    <xsl:apply-templates select="tt:br"/>
	<!-- br要素のあるp要素について、br要素のみ評価 -->
  </xsl:template>

  <xsl:template match="tt:br">
    <xsl:choose>
      <xsl:when test="yn:contains(string(preceding-sibling::node()[last()]),'^\-')">
      <!-- 行頭が '-' で始まっていたら(正規表現 ^\-)-->
      <!-- 2つの台詞に分割 -->
        <p>
          <xsl:for-each select="../@*">
            <xsl:copy/>
          </xsl:for-each>
          <xsl:attribute name="agent">
	<xsl:value-of select="yn:replace(string(preceding-sibling::node()[last()]),'-\[([^\]]+)\].+','$1')"/>
          </xsl:attribute>
          <xsl:for-each select="preceding-sibling::node()">
          <!-- brより前の、ノードかテキストである兄弟要素についてループ -->	
            <xsl:apply-templates select="."/>
          </xsl:for-each>
        </p>
        <xsl:text>
</xsl:text>
        <p>
          <xsl:for-each select="../@*">
            <xsl:if test="not(contains(.,'xml:id'))">
              <xsl:copy />
            </xsl:if>
          </xsl:for-each>
          <xsl:attribute name="xml:id">
          <xsl:value-of select="concat(../@xml:id,'-2')"/>
          <!-- 台詞idをコピーして接尾詞'-2'をつける -->
          </xsl:attribute>
          <xsl:attribute name="agent">
	<xsl:value-of select="yn:replace(string(following-sibling::node()[1]),'-\[([^\]]+)\].+','$1')"/>
          </xsl:attribute>
          <xsl:for-each select="following-sibling::node()">
          <!-- brより後の、ノードかテキストである兄弟要素についてループ -->
            <xsl:apply-templates select="."/>
          </xsl:for-each>
        </p>
          <xsl:attribute name="agent">
	<xsl:value-of select="yn:replace(string(following-sibling::node()[1]),'-\[([^\]]+)\].+','$1')"/>
          </xsl:attribute>
      </xsl:when>
      <xsl:otherwise>
      <!-- それ以外(行頭が '-' で始まっていなかったら -->
      <!-- brノード以外をコピー(改行を削除) -->
        <p>
          <xsl:for-each select="../@*">
            <xsl:copy/>
          </xsl:for-each>
          <xsl:attribute name="agent">
	<xsl:value-of select="yn:replace(string(preceding-sibling::node()[1]),'\[([^\]]+)\].+','$1')"/>
          </xsl:attribute>
          <xsl:for-each select="preceding-sibling::node()">
            <xsl:apply-templates select="."/>
          </xsl:for-each>
          <xsl:text>
</xsl:text>
          <xsl:for-each select="following-sibling::node()">
            <xsl:apply-templates select="."/>
          </xsl:for-each>
        </p>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:template>

  <xsl:template match="tt:span">
    <span>
      <xsl:choose>
        <xsl:when test="@style=$ItaSty">
          <xsl:attribute name="style">italic
          </xsl:attribute>
        </xsl:when>
      </xsl:choose>
      <xsl:value-of select="yn:replace(string(.),'(-)?(\[[^\]]+\]\s)?([^\[\]]*)','$1$3')"/>
	<!-- DSC式のspanタグ内から役名を削除 -->
    </span>
  </xsl:template>

</xsl:stylesheet>

【make_ynttml.xsl】

2018.07.14 字幕TTMLの翻訳ツッコミ化
●ツッコミ手法の変更
スタトレ Personal Log の方で概要は述べましたが、掲題のとおり、字幕TTMLを一括して翻訳ツッコミ原稿形式に変換する手法に着手しました。たたき台として全台詞をツッコミ形式にしておき、そこに実際のツッコミを追記したり、対象外のものを削除したりするという趣旨です。
●アレが使える
アルゴリズム的には、前にとりあげた
兄弟要素をグループ化する
手法を使います。ひとことでいうと、
全要素を順番に処理して、終端要素が出たら、そこまでをグループとする
となります。フラットな字幕データを台詞(文章)としてグループ化するための終端要素は、当然ピリオド、クエスチョンマーク等です。台詞文字列の中にそれらの終端文字があるかどうかを contain() 関数でチェックします。
補足: ただし、複数の文字をひとつずつチェックするのは冗長なので、正規表現に対応した yn:contain() 関数を作りました。それについてはまた別の機会にまとめたい思います。また、このテンプレートは、DS9のように全てを<p>要素で記述しているものにしか適用できません(DSCは<p>の下に<span>を使っているのでもうひとひねり必要)。
<?xml version="1.0" encoding="Shift_JIS" ?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:tt="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" xmlns:yn="urn:yonetch" xmlns:msxsl="urn:schemas-microsoft-com:xslt" exclude-result-prefixes="msxsl" >

  <xsl:output method="xml" encoding="Shift_JIS"/>
  <xsl:include href="..\..\..\xml\commonlib.xsl" />

  <xsl:template match="/">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="tt:body">
    <body>
      <xsl:apply-templates/>
    </body>
  </xsl:template>

  <xsl:template match="div">
    <xsl:apply-templates/>
  </xsl:template>

  <xsl:template match="tt:div">
    <xsl:element name="エピ">
    <xsl:attribute name="状態">下書き
    </xsl:attribute>
    <xsl:element name="原題"/>
    <xsl:element name="更新日"/>
    <xsl:element name="字幕ソース">Netflix
    </xsl:element>
    <xsl:text></xsl:text>
    <xsl:for-each select="tt:p">
      <xsl:if test="yn:contains(string(.),'[^\.][\)\]\?\.!]&quot;?$')">
        <xsl:element name="台詞">
        <xsl:attribute name="状態">下書き
        </xsl:attribute>
        <xsl:attribute name="人物">
        </xsl:attribute>
        <xsl:element name="原語">
        <xsl:text></xsl:text>
        <xsl:if test="preceding-sibling::*[1]">
          <xsl:call-template name="func">
            <xsl:with-param name="node" select="preceding-sibling::*[1]" />
          </xsl:call-template>
          <字幕id>
            <xsl:value-of select="@xml:id" />
          </字幕id>
          <xsl:text></xsl:text>
        </xsl:if>
        </xsl:element>
        <xsl:text></xsl:text>
        <xsl:element name="吹替">
        <xsl:attribute name="評価"/>
        </xsl:element>
        <xsl:text></xsl:text>
        <xsl:element name="直訳"/>
        <xsl:text></xsl:text>
        <xsl:element name="所感"/>
        <xsl:text></xsl:text>
        </xsl:element>
        <xsl:text></xsl:text>
      </xsl:if>
    </xsl:for-each>
    </xsl:element>
  </xsl:template>

  <xsl:template name="func">
    <xsl:param name="node" />
    <xsl:if test="not(yn:contains(string($node/.),'[^\.][\)\]\?\.!]&quot;?$'))">
      <xsl:call-template name="func">
        <xsl:with-param name="node" select="$node/preceding-sibling::*[1]" />
      </xsl:call-template>
      <字幕id>
        <xsl:value-of select="$node/@xml:id" />
      </字幕id>
      <xsl:text></xsl:text>
    </xsl:if>
  </xsl:template>

</xsl:stylesheet>

【ttml2tsukkomi_DS9.xsl】

●そうは問屋が
これでめでたしめでたし、と終われればよかったのですが、そうはいきませんでした。というのも、全台詞を(非公開ながらも)ツッコミデータの中に持つとなると、各エピソードのデータ量が今の100倍は下りません。さらにシーズンごとにファイル分けしている現状では、どれほど処理が重くなることか... 言っちゃナンですが、XML Notepad ってあんまりサクサク動く方ではないですし。
●ファイル分割再び
そんなわけで、ツッコミファイルを、シーズン毎からエピソード毎にさらに分割することにしました。それ自体はそんなに深くは考えていませんでした。前回RSS生成のときと同様、DOCTYPE によるファイル結合法を多段階に使えば、エピソード>シーズン>シリーズという風にすればよいと思ったからです。ところが!これは仕様上ダメなんですね(泣)。全シリーズを結合しているファイルからみると、DOCTYPE が2か所にあることになり、エラーとなりました。
●同じ定義をそれぞれに
結局、あまり美しくないですが、シーズン毎のツッコミ本体ファイルと全シリーズをまとめた集計用ファイルで、それぞれ同じ定義を記述するしかないという結論に至りました。以下のようなカンジです。
<?xml version="1.0" encoding="shift_jis"?>
<?xml-stylesheet type="text/xsl" href="st_eval.xsl"?>
<!DOCTYPE startrek [
<!ENTITY DS9_1 SYSTEM "DS9_1.xml">
<!ENTITY DS9_2 SYSTEM "DS9_2.xml">
<!ENTITY DS9_3-1 SYSTEM "DS9_3-1.xml">
<!ENTITY DS9_3-2 SYSTEM "DS9_3-2.xml">
<!ENTITY DS9_3-3 SYSTEM "DS9_3-3.xml">
<!ENTITY DS9_3-4 SYSTEM "DS9_3-4.xml">
<!ENTITY DS9_3-5 SYSTEM "DS9_3-5.xml">
<!ENTITY DS9_3-8 SYSTEM "DS9_3-8.xml">
<!ENTITY DS9_3-9 SYSTEM "DS9_3-9.xml">
<!ENTITY DS9_3-11 SYSTEM "DS9_3-11.xml">
<!ENTITY DS9_3-12 SYSTEM "DS9_3-12.xml">
<!ENTITY DS9_3-14 SYSTEM "DS9_3-14.xml">
<!ENTITY DS9_3-15 SYSTEM "DS9_3-15.xml">
<!ENTITY DS9_3-25 SYSTEM "DS9_3-25.xml">
]>
<シーズン シーズン番号="3" 略称="DS9">
&DS9_3-1;
&DS9_3-2;
&DS9_3-3;
&DS9_3-4;
&DS9_3-5;
&DS9_3-8;
&DS9_3-9;
&DS9_3-11;
&DS9_3-12;
&DS9_3-14;
&DS9_3-15;
&DS9_3-25;
</シーズン>

【DS9第3シーズン】

<?xml version="1.0" encoding="shift_jis"?>
<?xml-stylesheet type="text/xsl" href="st_eval_tbl.xsl"?>
<!DOCTYPE startrek [
<!ENTITY 台帳 SYSTEM "ST_def.xml">
<!ENTITY TOS1 SYSTEM "TOS1.xml">
<!ENTITY TOS2 SYSTEM "TOS2.xml">
<!ENTITY TOS3 SYSTEM "TOS3.xml">
<!ENTITY TAS1 SYSTEM "TAS1.xml">
<!ENTITY TAS2 SYSTEM "TAS2.xml">
<!ENTITY TNG_1 SYSTEM "TNG_1.xml">
<!ENTITY TNG_2 SYSTEM "TNG_2.xml">
<!ENTITY TNG_3 SYSTEM "TNG_3.xml">
<!ENTITY TNG_4 SYSTEM "TNG_4.xml">
<!ENTITY TNG_5 SYSTEM "TNG_5.xml">
<!ENTITY TNG_6 SYSTEM "TNG_6.xml">
<!ENTITY TNG_7 SYSTEM "TNG_7.xml">
<!ENTITY DS9_1 SYSTEM "DS9_1.xml">
<!ENTITY DS9_2 SYSTEM "DS9_2.xml">
<!ENTITY DS9_3-1 SYSTEM "DS9_3-1.xml">
<!ENTITY DS9_3-11 SYSTEM "DS9_3-11.xml">
<!ENTITY DS9_3-12 SYSTEM "DS9_3-12.xml">
<!ENTITY DS9_3-14 SYSTEM "DS9_3-14.xml">
<!ENTITY DS9_3-15 SYSTEM "DS9_3-15.xml">
<!ENTITY DS9_3-2 SYSTEM "DS9_3-2.xml">
<!ENTITY DS9_3-25 SYSTEM "DS9_3-25.xml">
<!ENTITY DS9_3-3 SYSTEM "DS9_3-3.xml">
<!ENTITY DS9_3-4 SYSTEM "DS9_3-4.xml">
<!ENTITY DS9_3-5 SYSTEM "DS9_3-5.xml">
<!ENTITY DS9_3-8 SYSTEM "DS9_3-8.xml">
<!ENTITY DS9_3-9 SYSTEM "DS9_3-9.xml">
<!ENTITY DS9_4 SYSTEM "DS9_4.xml">
<!ENTITY DS9_5 SYSTEM "DS9_5.xml">
<!ENTITY DS9_6 SYSTEM "DS9_6.xml">
<!ENTITY DS9_7 SYSTEM "DS9_7.xml">
<!ENTITY VGR1 SYSTEM "VGR1.xml">
<!ENTITY VGR2 SYSTEM "VGR2.xml">
<!ENTITY VGR3 SYSTEM "VGR3.xml">
<!ENTITY VGR4 SYSTEM "VGR4.xml">
<!ENTITY VGR5 SYSTEM "VGR5.xml">
<!ENTITY VGR6 SYSTEM "VGR6.xml">
<!ENTITY VGR7 SYSTEM "VGR7.xml">
<!ENTITY ENT1 SYSTEM "ENT1.xml">
<!ENTITY ENT2 SYSTEM "ENT2.xml">
<!ENTITY ENT3 SYSTEM "ENT3.xml">
<!ENTITY ENT4 SYSTEM "ENT4.xml">
<!ENTITY DSC1 SYSTEM "DSC1.xml">
]>
<スタートレック xmlns:t="urn:tables">
  <?xml-stylesheet type="text/xsl" href="st_table.xsl"?>
&台帳;
    <シリーズ 略称="TOS" xmlns:t="urn:tables">&TOS1;
&TOS2;
&TOS3;
</シリーズ>
    <シリーズ 略称="TAS" xmlns:t="urn:tables">&TAS1;
&TAS2;
</シリーズ>
    <シリーズ 略称="TNG" xmlns:t="urn:tables">&TNG_1;
&TNG_2;
&TNG_3;
&TNG_4;
&TNG_5;
&TNG_6;
&TNG_7;
</シリーズ>
    <シリーズ 略称="DS9" xmlns:t="urn:tables">&DS9_1;
&DS9_2;
<シーズン シーズン番号="3" 略称="DS9">
&DS9_3-1;
&DS9_3-11;
&DS9_3-12;
&DS9_3-14;
&DS9_3-15;
&DS9_3-2;
&DS9_3-25;
&DS9_3-3;
&DS9_3-4;
&DS9_3-5;
&DS9_3-8;
&DS9_3-9;
</シーズン>
&DS9_4;
&DS9_5;
&DS9_6;
&DS9_7;
</シリーズ>
    <シリーズ 略称="VGR" xmlns:t="urn:tables">&VGR1;
&VGR2;
&VGR3;
&VGR4;
&VGR5;
&VGR6;
&VGR7;
</シリーズ>
    <シリーズ 略称="ENT" xmlns:t="urn:tables">&ENT1;
&ENT2;
&ENT3;
&ENT4;
</シリーズ>
    <シリーズ 略称="DSC" xmlns:t="urn:tables">&DSC1;
</シリーズ>
</スタートレック>

【全シリーズ結合】

●余談
ここへ行き着くまでは、結構いろいろ試したんですが、ボツ案のひとつとして
XInclude
というのがかなり有力な案でした。実際、以下のようにすると、できちゃうんですよ!... ただし XML Notepad のプレビュー上だけですが(泣)。
<?xml version="1.0" encoding="shift_jis"?>
<?xml-stylesheet type="text/xsl" href="st_eval.xsl"?>
<シーズン シーズン番号="3" 略称="DS9" xmlns:xi="http://www.w3.org/2001/XInclude">
<xi:include href="DS9_3-1.xml"/>
<xi:include href="DS9_3-2.xml"/>
<xi:include href="DS9_3-3.xml"/>
<xi:include href="DS9_3-4.xml"/>
<xi:include href="DS9_3-5.xml"/>
<xi:include href="DS9_3-8.xml"/>
<xi:include href="DS9_3-9.xml"/>
<xi:include href="DS9_3-11.xml"/>
<xi:include href="DS9_3-12.xml"/>
<xi:include href="DS9_3-14.xml"/>
<xi:include href="DS9_3-15.xml"/>
<xi:include href="DS9_3-25.xml"/>
</シーズン>

【DS9第3シーズン XInclude版】

●またか!
またしても XML Notepad と IE、msxsl.exe の間で結果が異なる!!実に憤慨でした... いろいろ検索してみましたが、MSXSLでは XIncludeはサポートしていないというのが結論のよう。XML Notepad 2.7 で独自サポートされているだけでした(なんつっても、XML Notepad では msxsl?.dllを使ってないみたいだし)。これ、バッチモードで msxsl.exe の代わりに使えるようにしてくんないかなぁ...
2018.07.09 XML Notepad プレビューと msxsl.exeコマンドによる変換結果の違い
●順当に
RDFが自動生成できるようになったところで、次は更新日記だ!という順当な流れに沿ってコーディングを行っていた。いつもの手順通り、XML Notepadのプレビューで確認し、msxsl.exeコマンドで html ファイル化を行った。
●あれ?!
なんとか形になったと思い、変換結果をサーバにアップしておいたのだが、よくよくみると間違った内容となっている部分が散見された。あれ?!と、あわててプレビューで再度確認したが、何度やっても問題ない。
●びっくり!
そう、なぜか XML Notepad と msxsl.exe コマンドの結果が異なっているのである。詳細は別の機会にまとめるつもりだが、とりあえずの備忘録として結果だけ記す。
msxsl.exe -u 4.0
というオプションを付加し、MSXSL4.0 を使うように明示する。これで結果は XML Notepad と一致した。
これに悩んで、本稿が半年以上もほったらかされることになったんだぞ!チクショー!!
備考: ちなみに、ワタシの通常環境である Windows7 Pro SP1 には msxsl4.dll がインストールされていなかったために、当初このオプションはエラーとなっていた。MSXML 4.0 Service Pack 3 (Microsoft XML Core Services) をインストールする必要があった(msxsl6.dllはあったから、てっきり上位互換のものかと思ってた...)。
2017.12.03 RSS生成
●ようやくここまできたか
さて、XML化プロジェクトである。一番最初にこれという目標をたてた。そのうちのひとつに、
(RSSを記述した)rdfファイルを生成する
というのがあった。いよいよ目途がたったのでコレに取り掛かりたいと思う。
●とうぜんXML
RSSは、サイトの更新情報をXML形式で記述したものだ。これまでは、 フリーソフトのHeadline-editor Lite版を使って記事とは別に作成していたが、もうその必要はない。XML to XML のスタイルシートによって変換をかけるだけでよい。
●更新記録総ざらえ
更新記録を作るにあたり、まずは、すべての記事をひとまとめにする必要がある。日記形式のものはまだいい。同じ形式なんだからなんとかくっつくだろう。問題は、スター・トレックの翻訳ツッコミ記事である。大分構成が違う。まずはソッチの形式変換か... と思ったところで、「ツッコミリスト」を思い出した。このリストの形式を日記と同じにすることで、他の日記と同列に扱うことができるようになった。
補足: 少々ハナシが前後してしまっているが、元々「ツッコミリスト」は日記形式ではなく、この形式変換が必要になった段で変更した、という経緯があった。
●くっつけ!
で、具体的に複数ファイルのXML文書をひとつにまとめるには?いろいろ検索してみるんだけど、スタイルシート(XSL)の方は、xsl:include、xsl:import、xsl:document等があるんだけど、文書側にはそういう方法がみつからない。結局、見様見真似で(技術的背景も理解せず)、以下のようにしている。元々ルート要素である<日記>が、<日記総合>の下に入るような恰好。スタイルシートはそのまま適用可能だ。
<?xml version="1.0" encoding="shift_jis"?>
<!DOCTYPE GeneralDialy [
<!ENTITY 雑記 SYSTEM "diary2017-2018.xml">
<!ENTITY XML化 SYSTEM "XML-ization.xml">
<!ENTITY マネー SYSTEM "money.xml">
<!ENTITY 弥生元帳印刷 SYSTEM "yayoiprint.xml">
<!ENTITY パーソナルログ SYSTEM "..\startrek\xml\personal_log.xml">
<!ENTITY ツッコミ SYSTEM "..\startrek\xml\tsukkomi_list.xml">
]>
<日記総合>
&雑記;
&XML化;
&マネー;
&弥生元帳印刷;
&パーソナルログ;
&ツッコミ;
</日記総合>
●前フリ、長っ!
以上が前提。とにかく(XML化の終わった最近の)全ての日記形式記事から、RSS形式へと変換する。例によってRSS本来の記述上の意味等は全く理解していないが、Headline-editorで作られたrdfファイルを元に、同様の形式で更新履歴に変換するスタイルシートを作った。
<?xml version="1.0" encoding="shift_jis"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0" 
  xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" 
  xmlns:dc="http://purl.org/dc/elements/1.1/" 
  xmlns="http://purl.org/rss/1.0/"
  xmlns:t="urn:tables" xmlns:yn="urn:yonetch" xmlns:msxsl="urn:schemas-microsoft-com:xslt">
  <xsl:output method="xml" encoding="UTF-8" />
  <xsl:include href="commonlib.xsl" />

  <xsl:variable name="記事">
    <xsl:for-each select="//記事[@状態!='テンプレ' and @状態!='下書き' and @日付!='']">
      <xsl:variable name="日付">
        <xsl:call-template name="日付表示">
          <xsl:with-param name="形式" select="'YYYYMMDD'" />
          <xsl:with-param name="日付">
            <xsl:value-of select="@日付" />
          </xsl:with-param>
        </xsl:call-template>
      </xsl:variable>
      <xsl:if test="$日付 &gt; 20170101">
        <xsl:copy-of select="." />
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

  <xsl:template match="/">
    <rdf:RDF xmlns="http://purl.org/rss/1.0/" xmlns:admin="http://webns.net/mvcb/" xmlns:lang="ja">
      <channel>
        <xsl:attribute name="rdf:about">
          <xsl:value-of select="concat($サイト情報//総合情報/URL,$サイト情報//総合情報/RDF)" />
        </xsl:attribute>
        <title>
          <xsl:value-of select="$サイト情報//総合情報/サイト名" />
        </title>
        <link>
          <xsl:value-of select="$サイト情報//総合情報/URL" />
        </link>
        <dc:date>
          <xsl:value-of select="yn:getDateTime()" />
        </dc:date>
        <items>
          <rdf:Seq>
            <xsl:apply-templates select="msxsl:node-set($記事)/*" mode="Seq">
              <xsl:sort select="@日付" order="descending" />
            </xsl:apply-templates>
          </rdf:Seq>
        </items>
      </channel>
      <xsl:apply-templates select="msxsl:node-set($記事)/*">
        <xsl:sort select="@日付" order="descending" />
      </xsl:apply-templates>
    </rdf:RDF>
  </xsl:template>

  <xsl:template match="記事" mode="Seq">
    <xsl:variable name="cat" select="カテゴリ" />
    <xsl:variable name="subcat" select="サブカテゴリ" />
    <xsl:variable name="en_subtitle" select="@見出し" />
    <xsl:variable name="dtlabel">
      <xsl:call-template name="日付表示">
        <xsl:with-param name="形式" select="'YYYYMMDD'" />
        <xsl:with-param name="日付">
          <xsl:value-of select="@日付" />
        </xsl:with-param>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="link">
      <xsl:choose>
        <xsl:when test="contains($subcat,'翻訳ツッコミ')">
          <xsl:value-of select="concat($サイト情報//カテゴリ情報/カテゴリ[@name=$cat]/サブカテゴリ[@name='翻訳ツッコミ']/値[@name='リンク'],'#',$台帳//t:エピ[t:原題=$en_subtitle]/t:シリーズ名,$台帳//t:エピ[t:原題=$en_subtitle]/t:シーズン番号)" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:choose>
            <xsl:when test="count($サイト情報//カテゴリ情報/カテゴリ[@name=$cat]/サブカテゴリ)">
              <xsl:value-of select="concat($サイト情報//カテゴリ情報/カテゴリ[@name=$cat]/サブカテゴリ[@name=$subcat]/値[@name='リンク'],'#',$dtlabel)" />
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="concat($サイト情報//カテゴリ情報/カテゴリ[@name=$cat]/値[@name='リンク'],'#',$dtlabel)" />
            </xsl:otherwise>
          </xsl:choose>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <rdf:li>
      <xsl:attribute name="rdf:resouce">
        <xsl:value-of select="concat($サイト情報//総合情報/URL,$link,'#he',$dtlabel)" />
      </xsl:attribute>
    </rdf:li>
  </xsl:template>

  <xsl:template match="記事">
    <xsl:variable name="cat" select="カテゴリ" />
    <xsl:variable name="subcat" select="サブカテゴリ" />
    <xsl:variable name="en_subtitle" select="@見出し" />
    <xsl:variable name="dtlabel">
      <xsl:call-template name="日付表示">
        <xsl:with-param name="形式" select="'YYYYMMDD'" />
        <xsl:with-param name="日付">
          <xsl:value-of select="@日付" />
        </xsl:with-param>
      </xsl:call-template>
    </xsl:variable>
    <xsl:variable name="link">
      <xsl:choose>
        <xsl:when test="contains($subcat,'翻訳ツッコミ')">
          <xsl:value-of select="concat($サイト情報//カテゴリ情報/カテゴリ[@name=$cat]/サブカテゴリ[@name='翻訳ツッコミ']/値[@name='リンク'],'#',$台帳//t:エピ[t:原題=$en_subtitle]/t:シリーズ名,$台帳//t:エピ[t:原題=$en_subtitle]/t:シーズン番号)" />
        </xsl:when>
        <xsl:otherwise>
          <xsl:choose>
            <xsl:when test="count($サイト情報//カテゴリ情報/カテゴリ[@name=$cat]/サブカテゴリ)">
              <xsl:value-of select="concat($サイト情報//カテゴリ情報/カテゴリ[@name=$cat]/サブカテゴリ[@name=$subcat]/値[@name='リンク'],'#',$dtlabel)" />
            </xsl:when>
            <xsl:otherwise>
              <xsl:value-of select="concat($サイト情報//カテゴリ情報/カテゴリ[@name=$cat]/値[@name='リンク'],'#',$dtlabel)" />
            </xsl:otherwise>
          </xsl:choose>
        </xsl:otherwise>
      </xsl:choose>
    </xsl:variable>
    <item>
      <xsl:attribute name="rdf:about">
        <xsl:value-of select="concat($サイト情報//総合情報/URL,$link,'#he',$dtlabel)" />
      </xsl:attribute>
      <title>
        <xsl:value-of select="concat('【',$サイト情報//カテゴリ情報/カテゴリ[@name=$cat]/値[@name='リンク名'],'】 ',$subcat)" />
      </title>
      <link>
        <xsl:value-of select="concat($サイト情報//総合情報/URL,$link)" />
      </link>
      <dc:date>
        <xsl:value-of select="@日付" />
      </dc:date>
      <description>
        <xsl:value-of select="@見出し" />
      </description>
    </item>
  </xsl:template>
  <xsl:template match="表題|副題|要旨|段落|カテゴリ|サブカテゴリ|キーワード"></xsl:template>
</xsl:stylesheet>

【スタイルシート(rdf.xsl)】

●限定するには?
上記のスタイルシートにおいてのポイントとして、
対象期間の限定
がある。全日記のXML化が完了している「ぷろぐらまyonetch」「マネーの虎yonetch」は、十数年分の記事がすべて含まれる。が、RDFで更新を告知する対象に、今更そこまで含める必要はないから、まぁ、今年分だけを... と思って日付で限定するというのが、まことにメンドウだった。
●くっつけたのに、また離す
まず、以下のようにすれば、今年の記事のみの集合を変数に入れられる。
  <xsl:variable name="記事">
    <xsl:for-each select="//記事[@状態!='テンプレ' and @状態!='下書き' and @日付!='']">
      <xsl:variable name="日付">
        <xsl:call-template name="日付表示">
          <xsl:with-param name="形式" select="'YYYYMMDD'" />
          <xsl:with-param name="日付">
            <xsl:value-of select="@日付" />
          </xsl:with-param>
        </xsl:call-template>
      </xsl:variable>
      <xsl:if test="$日付 &gt; 20170101">
        <xsl:copy-of select="." />
      </xsl:if>
    </xsl:for-each>
  </xsl:variable>

【スタイルシートより抜粋(rdf.xsl)】

後は、この部分集合変数に対して、同じようにテンプレートを適用して... と思ったらそれができないのだ。その説明は、このサイトが参考になった。以下に抜粋引用する。
Firefox 2のXSLTプロセッサはXSLT 1.0のdocument関数が使えるのはいいんですが、結果ツリーフラグメント(Result Tree Fragment)をノードセットとして評価することができないようです。

そもそも結果ツリーフラグメントって何かというと、大雑把に言えばxsl:variable要素やxsl:param要素の内容としての値です。例えば、
<xsl:variable name="result"> <level>3</level> <number>1-1</number> </xsl:variable>
上記のような変数resultがあった場合、$resultはlevelとnumberの二つのノードを持つノードセットのように見えますが、これはノードセットにはなりません。結果ツリーフラグメントと呼ばれます。結果ツリーフラグメントに対しては文字列操作しか許されず、勿論/や//、[]などのノードセットに対してのみ使える演算子は使えません。
ではどうするかというと、上の記事と異なり msxsl では、
      <xsl:apply-templates select="msxsl:node-set($記事)/*">
        <xsl:sort select="@日付" order="descending" />
      </xsl:apply-templates>

【スタイルシートより抜粋(rdf.xsl)】

というように、msxsl:node-set(ツリーフラグメント)を使用することでノードセット化できる。
●ついにでけたー!!
そんなこんなで総合日記から、以下のようにrdfファイルを生成することができた。ばんざーい!
追記: 実は、この記事は書きかけでほったらかされており、半年以上を経て公開している。この当時書いていたxslファイルの中身は、半ば忘却の彼方なのであった...(^^;;
<?xml version="1.0" encoding="UTF-8"?>
<rdf:RDF xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:t="urn:tables" xmlns:yn="urn:yonetch" xmlns:msxsl="urn:schemas-microsoft-com:xslt" xmlns="http://purl.org/rss/1.0/" xmlns:admin="http://webns.net/mvcb/" xmlns:lang="ja">
  <channel rdf:about="http://www1.coralnet.or.jp/yonetch/yonetch.rdf">
    <title>yonetch Works</title>
    <link>http://www1.coralnet.or.jp/yonetch/</link>
    <dc:date>2017-12-03T20:42:03+09:00</dc:date>
    <items>
      <rdf:Seq>
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/XML-ization.html#20171203#he20171203" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/XML-ization.html#20171202#he20171202" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20171121#he20171121" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DS91#he20171121" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171115" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/XML-ization.html#20171112#he20171112" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#TNG7#he20171111" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171110" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171104" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/##he20171028" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20171025#he20171025" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171025" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171021" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171014" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171007" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20171002#he20171002" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171002" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20170930" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20170926#he20170926" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4#he20170910" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4#he20170909" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4#he20170909" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4#he20170903" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20170902#he20170902" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170827" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170826" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170826" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170820" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170814" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170814" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170716" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170710" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170709" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170703" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/#20170622#he20170622" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170620" />
        <rdf:li rdf:resouce="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170608" />
      </rdf:Seq>
    </items>
  </channel>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/XML-ization.html#20171203#he20171203">
    <title>【XML化プロジェクト】 技術メモ</title>
    <link>http://www1.coralnet.or.jp/yonetch/XML-ization.html#20171203</link>
    <dc:date>2017-12-03</dc:date>
    <description>RSS生成</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/XML-ization.html#20171202#he20171202">
    <title>【XML化プロジェクト】 技術メモ</title>
    <link>http://www1.coralnet.or.jp/yonetch/XML-ization.html#20171202</link>
    <dc:date>2017-12-02</dc:date>
    <description>大元の変換テンプレートっす</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20171121#he20171121">
    <title>【トレッカーyonetch】 Personal Log</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20171121</link>
    <dc:date>2017-11-21</dc:date>
    <description>シリーズ中休み</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DS91#he20171121">
    <title>【トレッカーyonetch】 翻訳ツッコミ DS9第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DS91</link>
    <dc:date>2017-11-21</dc:date>
    <description>THE NAGUS</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171115">
    <title>【トレッカーyonetch】 翻訳ツッコミ DSC第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1</link>
    <dc:date>2017-11-15</dc:date>
    <description>INTO THE FOREST I GO</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/XML-ization.html#20171112#he20171112">
    <title>【XML化プロジェクト】 </title>
    <link>http://www1.coralnet.or.jp/yonetch/XML-ization.html#20171112</link>
    <dc:date>2017-11-12</dc:date>
    <description>リンクの自動生成</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#TNG7#he20171111">
    <title>【トレッカーyonetch】 翻訳ツッコミ TNG第7シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#TNG7</link>
    <dc:date>2017-11-11</dc:date>
    <description>PARALLELS</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171110">
    <title>【トレッカーyonetch】 翻訳ツッコミ DSC第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1</link>
    <dc:date>2017-11-10</dc:date>
    <description>SI VIS PACEM, PARA BELLUM</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171104">
    <title>【トレッカーyonetch】 翻訳ツッコミ DSC第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1</link>
    <dc:date>2017-11-04</dc:date>
    <description>MAGIC TO MAKE THE SANEST MAN GO MAD</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/##he20171028">
    <title>【XML化プロジェクト】 スター・トレック翻訳ツッコミ</title>
    <link>http://www1.coralnet.or.jp/yonetch/#</link>
    <dc:date>2017-10-28</dc:date>
    <description>TTML正式採用</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20171025#he20171025">
    <title>【トレッカーyonetch】 Personal Log</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20171025</link>
    <dc:date>2017-10-25</dc:date>
    <description>制作快調!</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171025">
    <title>【トレッカーyonetch】 翻訳ツッコミ DSC第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1</link>
    <dc:date>2017-10-25</dc:date>
    <description>LETHE</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171021">
    <title>【トレッカーyonetch】 翻訳ツッコミ DSC第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1</link>
    <dc:date>2017-10-21</dc:date>
    <description>CHOOSE YOUR PAIN</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171014">
    <title>【トレッカーyonetch】 翻訳ツッコミ DSC第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1</link>
    <dc:date>2017-10-14</dc:date>
    <description>THE BUTCHER'S KNIFE CARES NOT FOR THE LAMB'S CRY</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171007">
    <title>【トレッカーyonetch】 翻訳ツッコミ DSC第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1</link>
    <dc:date>2017-10-07</dc:date>
    <description>CONTEXT IS FOR KINGS</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20171002#he20171002">
    <title>【トレッカーyonetch】 Personal Log</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20171002</link>
    <dc:date>2017-10-02</dc:date>
    <description>本格始動!</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20171002">
    <title>【トレッカーyonetch】 翻訳ツッコミ DSC第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1</link>
    <dc:date>2017-10-02</dc:date>
    <description>BATTLE AT THE BINARY STARS</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1#he20170930">
    <title>【トレッカーyonetch】 翻訳ツッコミ DSC第1シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#DSC1</link>
    <dc:date>2017-09-30</dc:date>
    <description>THE VULCAN HELLO</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20170926#he20170926">
    <title>【トレッカーyonetch】 Personal Log</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20170926</link>
    <dc:date>2017-09-26</dc:date>
    <description>新作がやってきた!</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4#he20170910">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第4シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4</link>
    <dc:date>2017-09-10</dc:date>
    <description>DEMONS</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4#he20170909">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第4シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4</link>
    <dc:date>2017-09-09</dc:date>
    <description>AFFLICTION</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4#he20170909">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第4シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4</link>
    <dc:date>2017-09-09</dc:date>
    <description>DIVERGENCE</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4#he20170903">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第4シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT4</link>
    <dc:date>2017-09-03</dc:date>
    <description>THE FORGE</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20170902#he20170902">
    <title>【トレッカーyonetch】 Personal Log</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/personal_log.html#20170902</link>
    <dc:date>2017-09-02</dc:date>
    <description>新作がやってくる!</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170827">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-08-27</dc:date>
    <description>COUNTDOWN</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170826">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-08-26</dc:date>
    <description>DAMAGE</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170826">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-08-26</dc:date>
    <description>THE COUNCIL</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170820">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-08-20</dc:date>
    <description>AZATI PRIME</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170814">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-08-14</dc:date>
    <description>STRATAGEM</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170814">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-08-14</dc:date>
    <description>HATCHERY</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170716">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-07-16</dc:date>
    <description>PROVING GROUND</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170710">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-07-10</dc:date>
    <description>CHOSEN REALM</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170709">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-07-09</dc:date>
    <description>THE SHIPMENT</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170703">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-07-03</dc:date>
    <description>EXILE</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/#20170622#he20170622">
    <title>【更新日記】 </title>
    <link>http://www1.coralnet.or.jp/yonetch/#20170622</link>
    <dc:date>2017-06-22</dc:date>
    <description>Outlook - iCloud 連携に問題がッ!</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170620">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-06-20</dc:date>
    <description>IMPULSE</description>
  </item>
  <item rdf:about="http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3#he20170608">
    <title>【トレッカーyonetch】 翻訳ツッコミ ENT第3シーズン</title>
    <link>http://www1.coralnet.or.jp/yonetch/startrek/eval.html#ENT3</link>
    <dc:date>2017-06-08</dc:date>
    <description>RAJIIN</description>
  </item>
</rdf:RDF>

【生成されたRDFファイル】

●さぁて、来週のyonetchさんは?
いい感じである!この勢いを駆って、トップページの生成、更新日記の生成へと一気に行くぞ!... と言いたいところだが、更新日記に掲示している更新内容の形式は、少々やっかいな処理を必要としている。今少し時間がかかりそうだ。
2017.12.02 大元の変換テンプレートっす
●回顧録的に
ここで述べている「XML化プロジェクト」とは、(今更しつこいが)HTMLからXMLへの移行である。だが、これも何度も述べたことがあるとおり、制作に使用していた(いる)のは、ホームページビルダー v6.5で、ふっるぅ〜いHTML仕様のコンテンツを吐く。これをチマチマXMLに置き換えるなど、考えただけでも気が遠くなる。HTMLとXMLの根本的な違いは、
閉じタグの有無
だ。まともな(?) XMLへの道の第一歩は、XHTML化することだった。何が違うって、dlタグ内の、dt、ddタグに、HTMLでは閉じタグがないのである。ワタシの日記は、これをベースにしているので最も使用頻度が高い(と思う)。まずはこれを何とかせねば...
●「すっきりと」
いろいろググってみて、以下のサイト(及びツール)を見つけた。
Clean up your Web pages
with HTML TIDY
詳細は省くが、これで整形をかけるとHTMLがXHTMLになって出てくる。ほぼそのままで、XML Notepad で読みこめるようになった。
●ようやくスタートライン
ここからがスタートである。が、のっけから壁にぶつかった。XMLの基本はツリー構造だ(とワタシは認識している)が、前述のdlタグ、単純なツリーではないのだ。Definition List というくらいなのだから、定義を表すことを目的としているのは明白だ。だったら、<dl><item><dt></dt><dd></dd></item></dl>と、各項目をまず要素とし、その子要素としてタイトルと情報のペア、となっているのが自然なツリー構造だと思う。だが、実際は項目という(一種の)パラグラフは存在しないし、<dd>タグもいくつでもいいという、フラットっぽい構造だ。
●どうやってパースすればいいの?
このdlを使った形式の日記から、dtにある日付、見出しを、ddにある小見出しと本文を取り出して<段落>タグに納めたい。そういうxhtml2xmlのフィルタをxsltを使って作りたいところであるが、xslを上辺だけナメたぐらいのレベルでは、兄弟(同じレベルの)要素を走査する方法は思いつかなかった。
補足: for-eachループとposition()を使って、奇数行、偶数行を決め打ちでやってみたりもしたが、途中からタイトルと本文がずれていく。何か誤認識してるな... ボツ。
●兄弟要素へのアクセス
流れとしては、dtを始まり、ddを終わりとして順次<段落>タグの中に落とし込んでいく、という風だろうか。だがその方法は?マジメに系統だった理解をせずに何となく使ってきた報いを受けている...
●ググってみた
いろいろ検索結果をたどってみて、以下のやり取りを発見。これ、まさに兄弟要素をまとめるハナシで、<GROUP>=<段落>、<A>=<dt>、<B>=<dd>とすれば、マンマいただけるんじゃね?(<C>は不要)。
XSLTにて、同一階層にあるElementのグループ化について良い方法をご存じでしたらご教授いただけませんでしょうか?

[やりたいこと]
Aから次のAまでElementを1つのグループにする

#XML
<ROOT>
 <A>A1</A>
 <B>B1</B>
 <C>C1</C>
 <A>A2</A>
 <C>C2</C>
 <A>A3</A>
 <A>A4</A>
 <B>B2</B>
</ROOT> 


#結果の想定
<ROOT>
 <GROUP>
  <A>A1</A>
  <B>B1</B>
  <C>C1</C>
 </GROUP>
 <GROUP>
  <A>A2</A>
  <C>C2</C>
 </GROUP>
 <GROUP>
  <A>A3</A>
 </GROUP>
 <GROUP>
  <A>A4</A>
  <B>B2</B>
 </GROUP>
</ROOT> 

(略)

あ、なるほど。
matchを使っての再帰とかもできるんですね。(そりゃそうですよね。)
再帰 = call-template のイメージがあったので勉強になりました。

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
  <xsl:output method="xml" />
  <xsl:template match="/">
    <xsl:element name="ROOT">
      <xsl:for-each select="ROOT/A">
        <GROUP>
          <xsl:copy-of select="." />
          <xsl:call-template name="func">
            <xsl:with-param name="node" select="following-sibling::*[1]" />
          </xsl:call-template>
        </GROUP>
      </xsl:for-each>
    </xsl:element>
  </xsl:template>
  <xsl:template name="func">
    <xsl:param name="node" />
    <xsl:if test="name($node)='B' or name($node)='C'">
      <xsl:copy-of select="$node" />
      <xsl:call-template name="func">
        <xsl:with-param name="node" select="$node/following-sibling::*[1]" />
      </xsl:call-template>
    </xsl:if>
  </xsl:template>
</xsl:stylesheet>

【[XSLT]同一階層のElementのグループ化】

●段階を経て
以下のようにして、<記事>タグに見出しと日付、その他(=本文)という形で落とし込むことはできた。
備考: その本文を、さらに小見出しと地の文に分けて<段落>タグに落とさねばならないが、それはまた別の機会に譲る
<xsl:template match="dl">
  <xsl:for-each select="dt">
    <記事>
      <xsl:attribute name="日付">
        <xsl:value-of select="@日付"/>
      </xsl:attribute>
      <xsl:attribute name="見出し">
        <xsl:value-of select="."/>
      </xsl:attribute>
      <xsl:attribute name="状態">公開</xsl:attribute>
      <xsl:call-template name="mydl">
        <xsl:with-param name="node" select="following-sibling::*[1]" />
      </xsl:call-template>
    </記事>
  </xsl:for-each>
</xsl:template>

<xsl:template name="mydl">
  <xsl:param name="node" />
  <xsl:if test="name($node)='dd'">
    <xsl:apply-templates select="$node" />
    <xsl:call-template name="mydl">
      <xsl:with-param name="node" select="$node/following-sibling::*[1]" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>
●バツの道?
上記のコードの中で、キモとなるのは、following-sibling::*[1]の部分だろう。現時点では何のこっちゃかサッパリであるが(^^;)。ともかく、「異なる名前の兄弟要素を関連付けて処理」という目的を果たすためには、
XPath
の理解に努めること、それを避けて通ることはできなさそうだ。ちょっとWikiPedia「XML Path Language」から引用してみよう。
  • 省略構文による簡単なロケーションパスの記述例
    /A/B/C
  • 少し複雑なロケーションパスの例
    A//B/*[1]
  • 省略しない完全な構文によって書き直す
    /child::A/child::B/child::Cchild::A/descendant-or-self::node()/child::B/child::*[1]
ここまで自分が使ってきたのは、最初の省略形。ごくたまに2番目の形。最後の「完全な構文」なんぞ、全く認識したことがなかった。少なくとも上のコードを読み下せるよう、理解を進めていきたい。
●mydl
ついでといってはナンだが、このコードを別に流用して自分流のDefinition List(mydl)を実装してみた。何かの定義を掲示する際に、ワタシ流では以下の形を使っている(上でも使ったが)。
  • タイトル
    説明
  • タイトル
    説明
つまり、表現的には dl でなく ul で、一行目にタイトルを強調表示し、改行後説明を表示する。本サイトではそこら中にあるパターンだが、ul で書くのもメンドウなので、ワタシのXML内でdlを使った場合は、上記のように表現することにした。
補足: ちなみに、似たような表現をCSSでなんとかできないかとイジってみたが、(理解不足もあって)思ったようにはならなかった。
<xsl:template match="dl">
  <xsl:element name="ul">
    <xsl:for-each select="dt">
      <li>
        <b>
          <xsl:apply-templates />
        </b>
        <br />
        <xsl:call-template name="mydl">
          <xsl:with-param name="node" select="following-sibling::*[1]" />
        </xsl:call-template>
      </li>
    </xsl:for-each>
  </xsl:element>
</xsl:template>

<xsl:template name="mydl">
  <xsl:param name="node" />
  <xsl:if test="name($node)='dd'">
    <xsl:value-of select="$node" />
    <xsl:call-template name="mydl">
      <xsl:with-param name="node" select="$node/following-sibling::*[1]" />
    </xsl:call-template>
  </xsl:if>
</xsl:template>

【mydl実装部】

2017.11.12 リンクの自動生成
●遡って備忘録
スタトレ記事の更新が軌道にのり、少々こちらのメモがおろそかになっていましたが、アッチが順調なのはコッチの自前CMSが基底にあって順調なればこそ。少し突っかかってはチョコチョコと手直しをしつつ、今日に至っています。
●スタトレを前提とするがゆえに
さて本題です。本稿を含む、日記形式のXSLテンプレートは単純なところは大体の完成をみています。が、それをスタトレの Personal Log に適用しようとすると問題がありました。それは、
ツッコミ記事へのリンク
です。
●地味にメンドウ
ツッコミ記事や Personal Log では、他のエピソードを引き合いに出すことがよくあり、その時(すでに存在すれば)ツッコミ記事へのリンクも張ります。HTML版ではイチイチ他のファイルのラベルに対してのリンクを作っていましたがコレが結構大変だし、ツッコミ記事の有無によってリンクの有無も変わってきます。これをXMLを使って自動化することを考えました。
●要件定義
まず以下の通り要件を定義します。
●2パスになるか...
以上を具現化するにあたり、以下のテーブルが必要となります前者に関しては、従前のツッコミ評価表で作ったものが既にありました。問題は後者です。実際はこれを作っておく必要があるのかどうかわかりません。ただ、つくらなければ、都度ツッコミ記事の有無をスキャンするような恰好になるので何となく動作が重そうな気がしました。
●スキャン〜
そこで、全ツッコミ済み原稿ファイルから、そのタイトルだけを抜き出すようなxslファイルを作りました。ここで!タグ他の形式は、日記形式にしています(本文のない、日付と見出しだけの日記ってカンジ)。
<?xml version="1.0" encoding="shift_jis"?> <?xml-stylesheet type="text/xsl" href="diary.xsl"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:t="urn:tables" >
  <xsl:output method="xml" encoding="Shift_JIS" />
  <xsl:key name="シリーズ情報" match="t:略称" use="@name"/>
  <xsl:key name="評価情報" match="t:レベル" use="@name"/>
  <xsl:key name="エピソード情報" match="t:エピ" use="t:原題"/>
  <xsl:template match="/">
    <日記>
      <xsl:for-each select="スタートレック//エピ[(not(@状態) and ./原題!='テンプレ') or @状態='公開']">
        <xsl:sort select="更新日" order="descending"/>
        <記事>
          <xsl:attribute name="日付">
            <xsl:value-of select="更新日"/>
          </xsl:attribute>
          <xsl:attribute name="見出し">
            <xsl:value-of select="原題"/>
          </xsl:attribute>
          <xsl:attribute name="状態">公開</xsl:attribute>
          <カテゴリ>スター・トレック</カテゴリ>
          <サブカテゴリ>翻訳ツッコミ</サブカテゴリ>
        </記事>
      </xsl:for-each>
    </日記>
  </xsl:template>
</xsl:stylesheet>

【tsukkomi_list.xsl】

<日記>
  <記事 日付="2017-11-11" 見出し="PARALLELS" 状態="公開">
    <カテゴリ>スター・トレック</カテゴリ>
    <サブカテゴリ>翻訳ツッコミ</サブカテゴリ>
  </記事>
  <記事 日付="2017-11-10" 見出し="SI VIS PACEM, PARA BELLUM" 状態="公開">
    <カテゴリ>スター・トレック</カテゴリ>
    <サブカテゴリ>翻訳ツッコミ</サブカテゴリ>
  </記事>
  <記事 日付="2017-11-04" 見出し="MAGIC TO MAKE THE SANEST MAN GO MAD" 状態="公開">
    <カテゴリ>スター・トレック</カテゴリ>
    <サブカテゴリ>翻訳ツッコミ</サブカテゴリ>
  </記事>
  <記事 日付="2017-10-25" 見出し="LETHE" 状態="公開">
    <カテゴリ>スター・トレック</カテゴリ>
    <サブカテゴリ>翻訳ツッコミ</サブカテゴリ>
  </記事>
  <記事 日付="2017-10-21" 見出し="CHOOSE YOUR PAIN" 状態="公開">
    <カテゴリ>スター・トレック</カテゴリ>
    <サブカテゴリ>翻訳ツッコミ</サブカテゴリ>
  </記事>
</日記>

【tsukkomi_list.xml (変換結果)】

●道具がそろったところで
で、この 'tsukkomi_list.xml' を読み込んで、リンクを張る <ツッコミ> タグの定義が以下の通り。 "ツッコミ" というテンプレートを <ツッコミ> タグから呼ぶ、という少々紛らわしい構成となっております。
補足: ちなみに、ツッコミテンプレートの方は、もう一つ、with-quotというオプションがあります
  • option 1:邦題表示
  • option 2:シリーズ名表示
  • それ以外 :シリーズ名、邦題表示
  • with-quot 0: ダブルクォーテーションなし
  • それ以外 :ダブルクォーテーションあり
<xsl:variable name="ツッコミリスト" select="document('tsukkomi_list.xml')"/>

<xsl:template name="ツッコミ">
  <xsl:param name="en_subtitle"/>
  <xsl:param name="option">-1 
  </xsl:param>
  <xsl:param name="with-quot">1 
  </xsl:param>

  <xsl:variable name="series" select="$台帳//t:エピ[t:原題=$en_subtitle]/t:シリーズ名"/>
  <xsl:variable name="html_fn" select="concat($series,$台帳//t:シリーズ/t:略称[@name=$series]/t:値[@name='シリーズシーズンセパレータ'],$台帳//t:エピ[t:原題=$en_subtitle]/t:シーズン番号,'.html')"/>
  <xsl:variable name="ja_subtitle" select="concat('(邦題:「',$台帳//t:エピ[t:原題=$en_subtitle]/t:邦題,'」)')"/>

  <xsl:variable name="ep_subtitle">
    <xsl:choose>
      <xsl:when test="$option='0'">
        <xsl:choose>
          <xsl:when test="$with-quot='0'">
            <xsl:value-of select="$en_subtitle"/>
          </xsl:when>
          <xsl:otherwise>
            <xsl:value-of select="concat('"',$en_subtitle,'"')"/>
          </xsl:otherwise>
        </xsl:choose>
      </xsl:when>
      <xsl:when test="$option='1'">
        <xsl:value-of select="concat('"',$en_subtitle,'"',$ja_subtitle)"/>
      </xsl:when>
      <xsl:when test="$option='2'">
        <xsl:value-of select="concat($series,'"',$en_subtitle,'"')"/>
      </xsl:when>
      <xsl:otherwise>
        <xsl:value-of select="concat($series,'"',$en_subtitle,$ja_subtitle)"/>
      </xsl:otherwise>
    </xsl:choose>
  </xsl:variable>

  <xsl:choose>
    <xsl:when test="count($ツッコミリスト//記事[@見出し=$en_subtitle]) > 0">
      <A>
        <xsl:attribute name="href">
          <xsl:value-of select="concat($html_fn,'#',$en_subtitle)"/>
        </xsl:attribute>
        <xsl:value-of select="$ep_subtitle"/>
      </A>
    </xsl:when>
    <xsl:otherwise>
      <xsl:value-of select="$ep_subtitle"/>
    </xsl:otherwise>
  </xsl:choose>
</xsl:template>

<xsl:template match="ツッコミ">
  <xsl:call-template name="ツッコミ">
    <xsl:with-param name="en_subtitle">
      <xsl:value-of select="."/>
    </xsl:with-param>
    <xsl:with-param name="option">
      <xsl:value-of select="@オプション"/>
    </xsl:with-param>
  </xsl:call-template>
</xsl:template>
●余談
さてちょっと話題はかわりますが、オマケレベルの話として、ふと思い立ち旧ツッコミ集計表用のデータを更新してみようと思いました。ざっと言って、ツッコミ評価をCSVファイルとしてサーバにアップし、サーバサイドで集計表を生成する、という構成です。データ形式は以下の通りです。
htmlファイル名, 原題, 評価Aの個数, 評価Bの個数, ..., 評価Eの個数, Newの有無
このCSVデータを生成するのが、以下のXSLファイルです(字下げ位置改変)。
<?xml version="1.0" encoding="shift_jis"?> <?xml-stylesheet type="text/xsl" href="diary.xsl"?> 
<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:t="urn:tables" >
  <xsl:output method="text" encoding="Shift_JIS" />
  <xsl:include href="commonlib.xsl" />

  <xsl:template match="/">
    <xsl:for-each select="スタートレック//エピ[(not(@状態) and ./原題!='テンプレ') or @状態='公開']">
      <xsl:variable name="更新日MJD">
      <xsl:call-template name="ymd2mjd">
        <xsl:with-param name="ymd">
          <xsl:value-of select="更新日"/>
        </xsl:with-param>
      </xsl:call-template>
      </xsl:variable>
      <xsl:variable name="serif" select="台詞" />
      <xsl:value-of select="concat(../../@略称,key('シリーズ情報',../../@略称)/t:値[@name='シリーズシーズンセパレータ'] ,../@シーズン番号,'.html')"/>,&quot;
      <xsl:value-of select="原題"/>&quot;,
      <xsl:value-of select="count($serif/吹替[@評価='A'])" />,
      <xsl:value-of select="count($serif/吹替[@評価='B'])" />,
      <xsl:value-of select="count($serif/吹替[@評価='C'])" />,
      <xsl:value-of select="count($serif/吹替[@評価='D'])" />,
      <xsl:value-of select="count($serif/吹替[@評価='E' or @評価='F'])" />,
      <xsl:choose>
        <xsl:when test="$基準日MJD - $更新日MJD &lt; 180">1</xsl:when>
        <xsl:otherwise>0</xsl:otherwise>
      </xsl:choose>
      <xsl:text></xsl:text>

    </xsl:for-each>
  </xsl:template>
</xsl:stylesheet>
これで作ったCSVファイルを、サーバにアップしました。前にも別件で書いたとおり、サーバ側の集計CGIは(スクリプト作成以後にできた)TASやDSCのことを考慮していません。さてどうなるのかと思ったんですが、少々の不具合はあるものの、一応集計表としては機能できているようです。結果オーライ(^^)/
2017.10.28 TTML正式採用
●う、運命だ...
5か月も放置状態だった本稿。とはいえサイト記事更新こそサボっていたものの、XML関係の技術革新は続いている(オオゲサ^^;)。スタトレの Personal Logの方で既に触れているが、
ネトフリの字幕情報がTTMLだった
のである!
●か、感動...
なんという僥倖か。全く意識していなかったのに、たまたまデータ形式が合致するとは。これまではTTMLの仕様的な部分を理解しきれず、表面的な流用にとどまっていたが、お手本が手に入ったワケだ。このネトフリデータが正規のTTMLファイルだと仮定して、これに合致する処理系を作ることにした。
●さっそく拝見
さて、内容を見てみると、以下の部分で対応が必要だ。
●さて変換は
本来なら、ネトフリのTTMLファイルには一切手を入れず対処する方が望ましい気はするのだが、上記の台詞分割の部分でどうしても手をいれることになる。それならばと一種開き直りの境地でイジってみた。元々XMLなのだから、XML to XML となるスタイルシートを作ればいい... ハズだが、台詞の分割部分が少々難しかった。今は先を急ぐため、例によって Perl でスクリプトを書いてみた。
備考: ネトフリのTTMLは、台詞毎に一行にまとまっているため、行ベースの処理系である Perl の方が手っ取り早かった、ということもある
11/2追記: 表示タイミングをhh:mm:ss形式に変換するように変更。DSC第7話で、同じセリフが何度も出てきて映像との照らし合わせがメンドウだったから(^^;)
11/6追記: 下のスクリプト、スタイル名の置換のところでバグってましたんで暫定版に書き換えました。
#ネトフリの字幕TTMLファイルを、yonetch式TTMLモドキに変換する
##########################################################
#斜体スタイル名は "italic" と書き換える
#タイミング指定をtick単位から、hh:mm:ss形式に変換
#本部の先頭に「-(マイナス記号)」がついていて、かつ<br/>(改行)が入っている台詞を2つの要素に分割する
#台詞の先頭に役名が入っているものを、agent属性にする
#agent属性が入っていない場合も、属性のみ追加する(値はヌル)

 while(<>)
 {

	#tickRateを探す
	if(/<tt[^<>]+ttp:tickRate="(\d+)"/){
		$rate = int($1);
		print $rate;
	}

	#フォントにitalicを使っているスタイルを探す
	if(/<style[^<>]+tts:fontStyle="italic"[^<>x]+xml:id="([^<>]+)"[^<>\/]?\/>/){
		$italic_style = $1;
		#print $italic_style;
	}
		
	
	#s/$italic_style/italic/g; #斜体スタイル名は "italic" と書き換える
	s/style_3/italic/g; #style_3 を italic と書き換える
	s/<span[^<>]+\/>//g; # まれに値のない<span />タグがあるのであらかじめ削除

	#タイミング指定をtick単位から、hh:mm:ss形式に変換
	if(/^(<p )begin="(\d+)t" end="(\d+)t"(.+)$/){
		$_ = sprintf("%sbegin=\"%s\" end=\"%s\"%s\n",$1,&serial2time($2,$rate),&serial2time($3,$rate),$4);
	}

	#本部の先頭に「-(マイナス記号)」がついていて、かつ<br/>(改行)が入っている台詞を2つの要素に分割する
	# 台詞の先頭に役名が入っているものを、agent属性にする
	# agent属性が入っていない場合も、属性のみ追加する(値はヌル)
	if(/<br ?\/>/ and /<span[^<>]+>\-/){
		/^<p (.+) xml:id="([^<>]+)">(<span[^<>]+>)\-(\[([^\[\]]+)\])?(.*)(<\/span>)<br ?\/>(<span[^<>]+>)\-(\[([^\[\]]+)\])?(.*)(<\/span>)<\/p>$/;
		print "<p $1 xml:id=\"$2\" ";
		print $6 eq '' ? "agent=\"\">$3-$4$7<\/p>\n": "agent=\"$5\">$3-$6$7<\/p>\n";
		print "<p $1 xml:id=\"$2-2\" ";
		print $11 eq '' ? "agent=\"\">$8-$9$12<\/p>\n": "agent=\"$10\">$8-$11$12<\/p>\n";
	}elsif(/^<p (.+) xml:id="([^<>]+)">(<span[^<>]+>)(\[([^\[\]]+)\])?(.*)(<\/span>)<\/p>$/){
		print "<p $1 xml:id=\"$2\" ";
		print $6 eq '' ? "agent=\"\">$3$4$7<\/p>\n": "agent=\"$5\">$3$6$7<\/p>\n";
	}else{
		print;
	}

}

sub serial2time {
	my ($serial,$rate) = @_;
	my $sec = $serial/$rate;
	
	return sprintf("%02d:%02d:%02d",$sec/(60*60),$sec/60,$sec%60);
}

【make_ynttml.pl】

<tt ttp:tickRate="10000000" ttp:timeBase="media">
<head>
 <ttp:profile use="http://netflix.com/ttml/profile/dfxp-ls-sdh"/>
<styling>
 <style tts:color="white" tts:fontSize="100%" tts:fontStyle="italic" tts:fontWeight="normal" xml:id="style_3"/>
 <style tts:color="white" tts:fontSize="100%" tts:fontWeight="normal" xml:id="style_4"/>
 </styling>
<layout>
 <region tts:displayAlign="before" tts:extent="80.00% 40.00%" tts:origin="10.00% 10.00%" tts:textAlign="center" xml:id="region_1"/>
 <region tts:displayAlign="after" tts:extent="80.00% 40.00%" tts:origin="10.00% 50.00%" tts:textAlign="center" xml:id="region_2"/>
 </layout>
 </head>
<body>
<div xml:space="preserve">
(略)
<p begin="1997412084t" end="2022854167t" region="region_2" xml:id="subtitle44">
 <span style="style_4">Georgiou to </span>
 <span style="style_3">Shenzhou:</span>
 <span style="style_4"> two to transport.</span>
 </p>
<p begin="2048296250t" end="2064562500t" region="region_2" xml:id="subtitle45">
 <span style="style_4">[thunder crashing nearby]</span>
 </p>
<p begin="2065400000t" end="2085000000t" region="region_2" xml:id="subtitle46">
 <span style="style_4">[Burnham]</span>
 <br/>
 <span style="style_4">The storm is faster than I thought.</span>
 </p>
(略)
<p begin="3060560000t" end="3095600000t" region="region_2" xml:id="subtitle71">
 <span style="style_4">And you? What will you do</span>
 <br/>
 <span style="style_4">if we're trapped here for 89 years?</span>
 </p>
<p begin="3096426667t" end="3115195417t" region="region_2" xml:id="subtitle72">
 <span style="style_4">That's easy. I'd escape.</span>
 </p>
<p begin="3129380000t" end="3144810000t" region="region_2" xml:id="subtitle73">
 <span style="style_4">[Burnham] These are our footprints.</span>
 </p>
<p begin="3151064584t" end="3184014167t" region="region_2" xml:id="subtitle74">
 <span style="style_4">-You've walked us in a circle.</span>
 <br/>
 <span style="style_4">-Not exactly a circle.</span>
 </p>
(略)
 </div>
 </body>
 </tt>

【ネトフリ版TTML】

<tt ttp:tickRate="10000000" ttp:timeBase="media">
<head>
 <ttp:profile use="http://netflix.com/ttml/profile/dfxp-ls-sdh"/>
<styling>
 <style tts:color="white" tts:fontSize="100%" tts:fontStyle="italic" tts:fontWeight="normal" xml:id="italic"/>
 <style tts:color="white" tts:fontSize="100%" tts:fontWeight="normal" xml:id="style_4"/>
 </styling>
<layout>
 <region tts:displayAlign="before" tts:extent="80.00% 40.00%" tts:origin="10.00% 10.00%" tts:textAlign="center" xml:id="region_1"/>
 <region tts:displayAlign="after" tts:extent="80.00% 40.00%" tts:origin="10.00% 50.00%" tts:textAlign="center" xml:id="region_2"/>
 </layout>
 </head>
<body>
<div xml:space="preserve">
(略)
<p begin="00:03:19" end="00:03:22" region="region_2" xml:id="subtitle44" agent="">
 <span style="style_4">Georgiou to </span>
 <span style="italic">Shenzhou:</span>
 <span style="style_4"> two to transport.</span>
 </p>
<p begin="00:03:26" end="00:03:28" region="region_2" xml:id="subtitle45" agent="">
 <span style="style_4">[thunder crashing nearby]</span>
 </p>
<p begin="00:03:26" end="00:03:28" region="region_2" xml:id="subtitle46" agent="Burnham">
 <span style="style_4"/>
 <br/>
 <span style="style_4">The storm is faster than I thought.</span>
 </p>
(略)
<p begin="00:03:26" end="00:03:28" region="region_2" xml:id="subtitle71" agent="">
 <span style="style_4">And you? What will you do</span>
 <br/>
 <span style="style_4">if we're trapped here for 89 years?</span>
 </p>
<p begin="00:03:26" end="00:03:28" region="region_2" xml:id="subtitle72" agent="">
 <span style="style_4">That's easy. I'd escape.</span>
 </p>
<p begin="00:03:26" end="00:03:28 region="region_2" xml:id="subtitle73" agent="Burnham">
 <span style="style_4"> These are our footprints.</span>
 </p>
<p begin="00:03:26" end="00:03:28 region="region_2" xml:id="subtitle74" agent="">
 <span style="style_4">-You've walked us in a circle.</span>
 </p>
<p begin="00:03:26" end="00:03:28 region="region_2" xml:id="subtitle74-2" agent="">
 <span style="style_4">-Not exactly a circle.</span>
 </p>
(略)
 </div>
 </body>
 </tt>

【yonetch版TTML】

2017.05.28 三段階化の可否
●公開前のチェックが...
日記記事に関し、「状態」という属性を取り入れ、公開/下書きという値によって制御するようにした、ということは前述した。しかし、XML Notepadでプレビューしながら入力していると、それだけでは足りないことに気付いた。msxslコマンドによる変換の対象とするか否かという状態も必要なのである。すなわち、まだ公開段階ではないが、プレビューによるチェックはする必要がある、という状態だ。
●引数があるジャン!
XML Notepadにしても、msxslコマンドにしても、同じxslファイルを元に整形するから、どうにかしてどちらでの処理かを認識する必要がある。以下のように、コマンドの引数で処理しようとしたが、あえなく失敗。「そこでは変数は使えないよ」だと。
<xsl:param name="cmdline" select="cmdline"/>
<xsl:template match="/ and $cmdline='公開'">
  :
(略)
  :
</xsl:template>
matchの引数に使えなくとも、メイン部分では使えるはずだ。ということで、少々読みづらいが次のようにした。
<xsl:param name="cmdline" select="cmdline"/>
<xsl:template match="/">
<xsl:if test="not(cmdline) or not(@状態) or @状態!='下書き'">
  :
(略)
  :
</xsl:if>
</xsl:template>
msxslコマンドの引数に、cmdline=true という引数を与える(値は何でもいい)と、xslでは状態属性が下書きのものは処理しない。これにより、XML Notepadではプレビューされるが、msxslでは変換されないという目的を果たせた。
備考: ついでといってはナンだが、状態属性の値として'テンプレ'も加えた。この場合は、XML Notepadでもmsxslコマンドでも一切処理されない。
追記: ENT第2シーズンのコンテンツが空であることに気付いた(遅っ)。確認すると、この「状態」属性の扱いにバグがあった。状態属性を取り入れる前に書いた記事に後からその属性を追加することはしていない(手抜き^^;)。そのために、属性のないものは公開対象にならなかったことが原因だった。xslを修正した。
2017.05.18 字幕情報のXML化
●字幕の電子テキスト化
スタトレ翻訳ツッコミについて、最大の資料はDVDの英語字幕である。本サイト立ち上げ当初は、画面に英語字幕を表示し、リモコン片手に再生/一時停止を繰り返しつつ手入力していた。今考えると何と原始的なことをやっていたことか。まるでエアチェックと称してテレビをビデオカメラで撮影するようなものだ(^^)。
●テキストデータといえども
さすがに現在はソレはやっていない。VGRのDVDがリリースされた辺りからだったかと思うが、SRT形式の字幕ファイルを元に、コピペしながら今に至っているのだが、改めてこのコピペという作業を軽減できないかと考えた。何しろ字幕形式なので台詞はブツブツと断片化されているし、表示タイミングの情報も、翻訳ツッコミ用としてはジャマなだけであった(以下にサンプルを示す)。
1
02:51:21,184 --> 02:51:24,415
<i>Captain's Starlog, March 21, 2153.</i>

2
02:51:24,721 --> 02:51:27,212
<i>After three days exploring</i>
<i>an uninhabited planet...</i>

3
02:51:27,324 --> 02:51:30,259
<i>Commander Tucker and I have been</i>
<i>called back to</i> Enterprise...

4
02:51:30,360 --> 02:51:32,157
<i>to greet an unexpected visitor.</i>

5
02:51:32,262 --> 02:51:34,389
Maybe you were light-headed
from the altitude.

6
02:51:34,531 --> 02:51:37,898
I didn't slip. That overhang gave way
the moment I put my foot on it.

7
02:51:38,035 --> 02:51:41,004
- I walked on the same rocks you did.
- Maybe you loosened them.

【SRT形式】

●そこでXML!
だったらこの字幕ファイルもXML化してしまえばよいのでは、と考えた次第。字幕内容そのものをコピペするのではなく、別ファイルの台詞にリンクするような形にすれば、パンチミスなども減らせるだろうし、タイミング情報もうまくやれば翻訳ツッコミ側で時系列的に活用できるかもしれない、と。
●字幕データの標準とは?
で、早速字幕XML書式を考え始めたのだが... 待て待て、そもそも字幕の電子データ形式ってSRTしかないのか?既にXML化されたものもあるんじゃないのか?と思ってググってみたら、いろいろヒットした。経緯は省略するが、TTML (Timed Text Markup Language) なるものがXMLベースの字幕データ形式のようだったので、これを参考にすることにした。
●そこは参考程度に
厳密なことをいうとキリがなくなるので、翻訳ツッコミに必要な最低限のタグのみを流用するにとどめた。すなわち、 という程度。なお、2人の台詞が同時に表示される部分がある。その場合は、タイミング属性はそのままに、台詞を分割し、idを変更(とりあえず7なら7-2、というように)した(サンプルを以下に示す)。
補足: 分割した場合は、idでのソートで順番が狂わないように気をつけて附番する必要があるだろう。
7/17追記: 台詞分割の手間が割とバカにならないので、srt2xmlの時点で分割してしまうことにした(本来のTTMLからどんどん遠ざかる...^^;)
余談: 定義もスキーマ形式で書ければいいのだけれど、現状理解が追い付いてない(>_<)
<tt xmlns:tt="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" ttp:tickRate="10000000" ttp:timeBase="media" xmlns="http://www.w3.org/ns/ttml">
<head>
<ttp:profile use="http://netflix.com/ttml/profile/dfxp-ls-sdh"/>
<styling>
<style tts:color="white" tts:fontSize="100%" tts:fontWeight="normal" xml:id="style_1"/>
<style tts:color="white" tts:fontSize="100%" tts:fontStyle="italic" tts:fontWeight="normal" xml:id="italic"/>
</styling>
</head>
  <body>
    <div>
      <p xml:id="1" begin="02:51:21,184" end="02:51:24,415" agent="Archer">
        <span style="italic">Captain's Starlog, March 21, 2153.</span>
      </p>
      <p xml:id="2" begin="02:51:24,721" end="02:51:27,212" agent="Archer">
        <span style="italic">After three days exploring</span>
        <br />
        <span style="italic">an uninhabited planet...</span>
      </p>
      <p xml:id="3" begin="02:51:27,324" end="02:51:30,259" agent="Archer">
        <span style="italic">Commander Tucker and I have been</span>
        <br />
        <span style="italic">called back to</span> Enterprise...
        </p>
      <p xml:id="4" begin="02:51:30,360" end="02:51:32,157" agent="Archer">
        <span style="italic">to greet an unexpected visitor.</span>
      </p>
      <p xml:id="5" begin="02:51:32,262" end="02:51:34,389" agent="Archer">
        Maybe you were light-headed<br />
        from the altitude.
      </p>
      <p xml:id="6" begin="02:51:34,531" end="02:51:37,898" agent="Tucker">
        I didn't slip. That overhang gave way<br />
        the moment I put my foot on it.
      </p>
      <p xml:id="7" begin="02:51:38,035" end="02:51:41,004" agent="Archer">
        - I walked on the same rocks you did.</p>
      <p xml:id="7-2" begin="02:51:38,035" end="02:51:41,004" agent="Tucker">
        - Maybe you loosened them.
      </p>
    </div>
  </body>
</tt>

【ネトフリ版TTML形式(改)】

●変換は自動で
元々持っているデータはSRT形式なので、自作TTMLモドキ ネトフリTTMLモドキに変換するPerlスクリプトを書いた(例によって動くだけの適当コーディング^^;)。
7/17追記: 台詞分割は、行頭に'-(ハイフン)'がついていたら分割、とした。それに合致しないケースもあるかもしれない。盲信はしないようにしよう。
10/26追記: ヘッダー部をネトフリ仕様になるように変更。台詞分割は依然必要なので、「モドキ」である状態は変わらない(^^)
'18/7/18追記: 斜体指定の<i>タグを、TTML仕様の<span>タグに置換するよう変更
use Switch;

$flag_in = 0;
$processing = 0;
$tmp_tag ="";

#print "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n";
#print "<tt xmlns:xsl=\"http://www.w3.org/1999/XSL/Transform\">\n";
#print "<body><div>\n";

print <<"EOM";
<tt xmlns:tt="http://www.w3.org/ns/ttml" xmlns:ttm="http://www.w3.org/ns/ttml#metadata" xmlns:ttp="http://www.w3.org/ns/ttml#parameter" xmlns:tts="http://www.w3.org/ns/ttml#styling" ttp:tickRate="10000000" ttp:timeBase="media" xmlns="http://www.w3.org/ns/ttml">
<head>
<ttp:profile use="http://netflix.com/ttml/profile/dfxp-ls-sdh"/>
<styling>
<style tts:color="white" tts:fontSize="100%" tts:fontWeight="normal" xml:id="style_1"/>
<style tts:color="white" tts:fontSize="100%" tts:fontStyle="italic" tts:fontWeight="normal" xml:id="italic"/>
</styling>
</head>
<body><div>
EOM

while(<>)
{
	s/<i>/<span style="italic">/g;
	s/<\/i>/<\/span>/g;

	switch ($_){
		
		case /^(\d+)$/
		{
			$flag_in = 1;
			$id = $_+0;
			print "<p xml:id=\"$id\"";
		}
		case /-->/
		{
			s/^([\d:,]+) --> ([\d:,]+)$/\tbegin=\"\1\" end=\"\2\" agent=\"\" >/;
			$idx = $_;
			print $idx;
			
		}
		case /^$/
		{
			s/^$/<\/p>/;
			print "$line$_";
			$flag_in = 0;
			$processing = 0;
			$line="";
		}
		else
		{
			$line =~ s/(?=\n)/<tmp>/mg;
			if($line =~ /^\-/){
				$line =~ s/<tmp>/<\/p><p xml:id=\"$id-2\" $idx/mg;
			} else {
				$line =~ s/<tmp>/<br \/>/mg;
			}
			$line = "$line$_";
			$processing=$processing+1;	
		}
	}
}

print "</div></body></tt>\n";

【srt2xml.pl (ネトフリTTML対応版)】

●実データ
以上の前提を元に、ツッコミ原稿XML及び、そのXSLファイルは以下のようになっている。ポイントは、原語タグ内に字幕idタグを並べるところ。前述のTTMLファイルのidを差し、字幕内容を列挙する。
    <台詞>
      <原語>
        <字幕id>5</字幕id>
      </原語>
      <吹替 評価="">高さに足がすくんで滑ったんだろう。</吹替>
      <直訳 />
      <所感 />
    </台詞>
    <台詞>
      <原語>
        <字幕id>6</字幕id>
      </原語>
      <吹替 評価="">滑ったんじゃありません。足をかけた途端に崩れたんです。</吹替>
      <直訳 />
      <所感 />
    </台詞>
    <台詞>
      <原語>
        <字幕id>7</字幕id>
      </原語>
      <吹替 評価="">あの岩は私も踏んだ。</吹替>
      <直訳 />
      <所感 />
    </台詞>
    <台詞>
      <原語>
        <字幕id>7-2</字幕id>
        <字幕id>8</字幕id>
      </原語>
      <吹替 評価="C">だから崩れたんだ。少しダイエットした方がいいですよ。</吹替>
      <直訳>多分船長が乗ったから弛んだんですよ。船長は私より数キロ重いですからね。</直訳>
      <所感>許容範囲とは思うが... やはり端折り傾向は否めない。</所感>
    </台詞>

【ENT2.xml(抜粋)】

<xsl:template match="原語[字幕id]">
<B>●
<xsl:variable name="en_subtitle" select="../../原題"/>
<!--
<xsl:variable name="字幕" select="document($台帳//t:エピ[t:原題=$en_subtitle]/t:収録[@メディア='DVD']/字幕ファイル)"/>
-->
<xsl:variable name="字幕">
 select="document($台帳//t:エピ[t:原題=$en_subtitle]/t:収録[@メディア='DVD']/字幕ファイル)"/>
<xsl:for-each select="字幕id">
	<xsl:variable name="id" select="text()"/>
	<xsl:variable name="soushoku" select="装飾"/>
	<xsl:for-each select="$字幕//p[@xml:id=$id]">
		<xsl:choose>
		<xsl:when test="$soushoku!=''">
			<xsl:value-of select="substring-before(.,$soushoku)"/>
			<xsl:element name="{$soushoku/@tag}"><xsl:value-of select="$soushoku"/></xsl:element>
			<xsl:value-of select="substring-after(.,$soushoku)"/>
		</xsl:when>
		<xsl:otherwise>
		<xsl:apply-templates select="."/>
		</xsl:otherwise>
		</xsl:choose>
	</xsl:for-each>
	<xsl:if test="position()=last()"> [<xsl:value-of select="$字幕//p[@xml:id=$id]/@agent"/>]</xsl:if>
</xsl:for-each>
</B><BR/>
</xsl:template>

【st_eval.xsl(原語台詞を字幕ファイルとリンクする部分)】

●Maybe you were light-headed from the altitude. [Archer]
【吹替】高さに足がすくんで滑ったんだろう。
●I didn't slip. That overhang gave way the moment I put my foot on it. [Tucker]
【吹替】滑ったんじゃありません。足をかけた途端に崩れたんです。
●- I walked on the same rocks you did. [Archer]
【吹替】あの岩は私も踏んだ。
●- Maybe you loosened them. You do weigh a few kilos more than I do. [Tucker]
【吹替】だから崩れたんだ。少しダイエットした方がいいですよ。[ふーん]
【直訳】多分船長が乗ったから弛んだんですよ。船長は私より数キロ重いですからね。
【所感】許容範囲とは思うが... やはり端折り傾向は否めない。

【HTML変換結果】

2017.05.03 サイト(内部的に)リニューアル計画
●というワケで
さて、経緯の方は更新日記の方で述べましたが、概要だけ再掲しておきます。本サイトyonetch WorksをXML化します。当面の目標は以下の通りです。 以上三点です。なお、XML書式については、暫定的に作成、今このページはXMLで書いています。基本のフォーマットは押さえていますが、既存の日記では他にもいろんなHTMLタグを使っているので、その辺の対応を随時進めていきます(以下のXSLでのポイントは、<xsl:sort>を使うことで、元データの記述によらず、日付順でのソートすることでした)。
<?xml version="1.0" encoding="shift_jis"?>
<?xml-stylesheet type="text/xsl" href="diary.xsl"?>
<日記>
  <表題>yonetch Works XML化プロジェクトページ</表題>
  <副題>〜XMLおよび周辺技術習得のためのメモ〜</副題>
  <要旨>本サイト自体のコンテンツを例題として、XMLを中心とした(必ずしもコンテンツ管理に限らない)データ処理に役立つ要素技術を集めるためのメモページです。かなり取り留めのないものになりそうな予感...</要旨>
  <記事 見出し="サイト(内部的に)リニューアル計画" 日付="2017-05-03" 状態="公開">
    <段落 小見出し="というワケで">さて、経緯の方は<a href="diary2017-2018.html#20170503">更新日記</a>の方で述べましたが、概要だけ再掲しておきます。本サイトyonetch WorksをXML化します。当面の目標は以下の通りです。
  <ul><li>日記形式ページのXML書式を策定する</li><li>更新日記をXMLl化する</li><li>更新日記XMLから、以下の各HTMLファイルを自動で生成できるようにする<ul><li>更新日記</li><li>トップページの What's New (直近の更新三項目のリスト)、及び</li><li>rdfファイル</li></ul></li></ul>
  以上三点です。なお、XML書式については、暫定的に作成、今このページはXMLで書いています。基本のフォーマットは押さえていますが、既存の日記では他にもいろんなHTMLタグを使っているので、その辺の対応を随時進めていきます。</段落>
    <段落 小見出し="早速とばかり">で、ちょっと先を考えると頭が痛いハナシがあります。ワタシ的XML化の本来の目的として、情報と表現の分離、というのがあります。記事の見出しが何、小見出しが何、日付がいつ... というのは本来それをどう表示するかとは別の話です。</段落>
    <段落 小見出し="アレもコレも">ですが、最終的な目的が表示にあることも明白です。では現存のHTMLコンテンツで様々なタグを使って作った記事はどうXML化すべきなのか?どう情報と表現を分離すべきなのか?野良プログラマたるワタシは、その辺の系統だった指針とか今時流行の形式、あるいは標準仕様みたいなものには疎いのです。何かありそうな気はしますが... <追記 キャプション="参考">デジタル日記の最たるモノであるブログ、そのデータ形式であるMovable Type形式を調べてみました(<a href="https://www.sixapart.jp/movabletype/manual/3.3/f_import_format/" target="_blank">こちら</a>)。うーん、XML形式じゃないですね(^^;)。ただ、項目とかはこのままパクってウチに合わせて拡張するのでもいいかも... もしかして、この書式に従えば、トラックバックもできるのかな?(それこそ10年遅れてるが...)</追記></段落>
    <カテゴリ>XML化プロジェクト</カテゴリ>
    <サブカテゴリ>その他</サブカテゴリ>
    <キーワード></キーワード>
  </記事>
  <記事 見出し="" 日付="2017-05-03" 状態="下書き">
    <段落 小見出し=""></段落>
    <カテゴリ>XML化プロジェクト</カテゴリ>
    <サブカテゴリ></サブカテゴリ>
    <キーワード></キーワード>
  </記事>
</日記>

【XMLファイル(抜粋)】

<?xml version="1.0" encoding="shift_jis"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/xsl/transform" version="1.0">
  <?xml-stylesheet type="text/css" href="hpbsite.css"?>
  <xsl:output method="html" encoding="shift_jis" />
  <xsl:template match="/">
    <html>
      <head>
        <title>
          <xsl:value-of select="//表題" />
        </title>
        <link rel="stylesheet" type="text/css" href="http://www1.coralnet.or.jp/yonetch/hpbsite.css" />
      </head>
      <body>
        <h1>
          <xsl:value-of select="//表題" />
        </h1>
        <blockquote>
          <h2>
            <xsl:value-of select="//副題" />
          </h2>
          <xsl:value-of select="//要旨" />
        </blockquote>
        <hr />
        <dl>
          <xsl:for-each select="//記事[@状態='公開']">
            <xsl:sort select="@日付" order="descending" />
            <dt>
              <xsl:call-template name="日付表示">
                <xsl:with-param name="日付">
                  <xsl:value-of select="@日付" />
                </xsl:with-param>
              </xsl:call-template>
              <xsl:value-of select="concat(' ',@見出し)" />
            </dt>
            <xsl:apply-templates />
          </xsl:for-each>
        </dl>
        <hr />
        <br />
      </body>
    </html>
  </xsl:template>
  <xsl:template match="段落[not(@状態) or @状態='公開']">
    <dd>
      <b>●<xsl:value-of select="@小見出し" /></b>
      <br />
      <xsl:apply-templates />
    </dd>
  </xsl:template>
  <xsl:template match="ul|li">
    <xsl:copy>
      <xsl:for-each select="@*">
        <xsl:copy />
      </xsl:for-each>
      <xsl:apply-templates />
    </xsl:copy>
  </xsl:template>
  <xsl:template match="追記">
    <blockquote>
      <font size="-1">
        <xsl:if test="@日付 and @日付!=''">
          <xsl:call-template name="日付表示">
            <xsl:with-param name="日付">
              <xsl:value-of select="@日付" />
            </xsl:with-param>
            <xsl:with-param name="形式">
              <xsl:value-of select="@形式" />
            </xsl:with-param>
            <xsl:with-param name="ラベル付">
              <xsl:value-of select="@ラベル付" />
            </xsl:with-param>
          </xsl:call-template>
        </xsl:if>
        <xsl:choose>
          <xsl:when test="@キャプション!=''">
            <xsl:value-of select="@キャプション" />
          </xsl:when>
          <xsl:otherwise>追記</xsl:otherwise>
        </xsl:choose>
        <xsl:value-of select="': '" />
        <xsl:apply-templates />
      </font>
    </blockquote>
  </xsl:template>
  <xsl:template match="ソース">
    <pre>
      <xsl:apply-templates />
    </pre>
  </xsl:template>
</xsl:stylesheet>

【XSLファイル "diary.xsl"(抜粋)】

●早速とばかり
で、ちょっと先を考えると頭が痛いハナシがあります。ワタシ的XML化の本来の目的として、情報と表現の分離、というのがあります。記事の見出しが何、小見出しが何、日付がいつ... というのは本来それをどう表示するかとは別の話です。
●アレもコレも
ですが、最終的な目的が表示にあることも明白です。では現存のHTMLコンテンツで様々なタグを使って作った記事はどうXML化すべきなのか?どう情報と表現を分離すべきなのか?野良プログラマたるワタシは、その辺の系統だった指針とか今時流行の形式、あるいは標準仕様みたいなものには疎いのです。何かありそうな気はしますが...
参考: デジタル日記の最たるモノであるブログ、そのデータ形式であるMovable Type形式を調べてみました(こちら)。うーん、XML形式じゃないですね(^^;)。ただ、項目とかはこのままパクってウチに合わせて拡張するのでもいいかも... もしかして、この書式に従えば、トラックバックもできるのかな?(それこそ10年遅れてるが...)
追記: MT形式に倣い、<記事>タグの属性値に「状態」を追加。“公開”“下書き”の値によって変換対象とするかどうかを使い分けられるようにした(これまでは、適当な属性なり要素の値に“テンプレ”という値を入れるなどしていた)。