masatoの日記

やっていきます

Text::XslateでTMを作ってみた

TMつくりが地味に大変

ある程度成果物がたまったら、訳文を翻訳メモリにして、再利用できるようにまとめておきたい。

CATツールにはOmegaTを使っていて、翻訳メモリはTMX形式だ。中身には原文、訳文、原文言語、訳文言語などが含まれていて、これらはXMLで構造化されている。

翻訳メモリをつくるためのツールはいろいろあって、いくつか試してはみた。

しかしうまくいくときもあるけど、訳文と原文のペアがズレることが多くて悩ましい。

普段はOmegaTのAlignerというテキスト整合用プラグインを使ってるのだけれど、これだと分節化する箇所をうまく設定できない(とういか、やり方がわからない)。

たとえば、「Mr.」とか「etc.」の後のピリオドで分節化するのはよしてほしいのだけど、OmegaTはそこで区切ってしまうのだ。

分節化規則で区切らないように正規表現を入れてはいる。それで正しいはず。はずだが、うまくいかない。

同数の分節からなる原文と訳文ファイルを合体させたい、だけなのに。

前置きが長くなりましたが、余計な分節化が入らないように自作ツールを作ってみました。備忘録的にまとめます。

テンプレートエンジン

TMXファイルの中身はXML。テンプレートを使って自由にテキストファイルを生成できるText::Xslateを使いました。 まず、ひな形を用意します。

材料1:TMXファイルひな形(XML

OmegaTが自動生成するTMXファイルはこんな構造。

<tu>
  <tuv lang="<: $source_lang :>">
    <seg><: $source_body :></seg>
  </tuv>
  <tuv lang="<: $target_lang :>" changeid="<: $translator :>" changedate="<: $change_date :>" creationid="<: $id :>" creationdate="<: $createn_date :>">
    <seg><: target_body :></seg>
  </tuv>
</tu>
<tu>

<: $hoge :>の部分は、Text::XslateのKolonシンタックスで書いた変数展開パターンです。 TMXの要素である言語指定、原文、訳文などをこれらの箇所に挿入していきます。

材料2:スクリプト本体

ファイル名指定

このコードではシェルから実行して、訳文ファイル、原文ファイルをタイプする形にしています。 ファイルは半角スペース区切りでsource.txt target.txtのように入力すると、それぞれ$src$target変数に入ってくれます。 $targetの方はchompしないと改行コードが入るので注意。

#!/usr/bin/perl
use strict;
use warnings;
use utf8;
use Text::Xslate;
use Time::Piece;

# ソース、ターゲットファイルを読み込む
print "Type file name (source, target):\n";
my ($src, $target ) = split(/ /, <STDIN>);
chomp($target);

言語指定

#言語設定
print "Type languages (source, target. e.g. JA EN):\n";
my ( $srclang, $targetlang ) = split(/ /, <STDIN>);
chomp($targetlang);

テキストファイルを読み込む

シェルでタイプしたテキストファイルを読み込むサブルーチン。 原文と訳文テキストをこれで読み取ります。

sub get_body {
    my $file = shift;
    my @body = ();

    open (my $fh, '<', $file) or die qq/ Can't open "$file": $!/;
    while (<$fh>) {
        chomp;
        push (@body, $_);
    }
    close ($fh);

    return @body;
}

次、読んだテキストをText::Xslateに渡せる形にします。 引数は配列レファレンスにする必要があるようでした。 なので、配列レファレンスにハッシュレファレンスを入れてやります。

こんな感じ。

my $segments = [
    {
      srcbody    => 'Apple',
      targetbody => 'りんご',
      srclang    => 'EN',
      targetlang => 'JA',
    },
    .......
];

ハッシュレフ一つが1文節のペアに対応します。 なので、たとえば100文節のTMを作る場合はこの配列レファレンスに100個のハッシュレフを入れればいいですね!

sub generate_template_arg {
    my ( $src, $target ) = @_;

    # 配列レファレンスにハッシュレフを入れる
    my $segments;
    for my $segment (@$src) {
        my $hash_ref = {
            srcbody  => $segment,
            targetbody => shift @$target,
        };
        push (@$segments, $hash_ref);
    }

    p $segments;
    return $segments;
}

ここまででテキストファイルの中身をText::Xslateに渡せる形にできました。 以下でテンプレートをもとにTMXファイルを作ります。

sub generate_tmx {
    my ( $segments, $srclang, $targetlang ) = @_;

  # イニシャライズ
    my $tx = Text::Xslate->new();

    # 生成するTMXのファイル名を指定
    my $tmx = 'translation_memory.tmx';

    open (my $out, '>', $tmx) or die $!;
    print $out $tx->render('test_template.tx', {
                            segments   => $segments,
                            srclang    => $srclang,
                            targetlang => $targetlang,
                         });
    close ($out);
}

この例では、$segmentsの中にすべての分節が収まっています。 TMXのファイル名はハードコードじゃなくて、引数で指定できるようにしたいところです。

順番が前後しましたが、上記のサブルーチン呼び出し部です。

# ハッシュの配列をもらう(Xslateで展開するため)
my @srcbody = get_body($src);
my @targetbody = get_body($target);

# 原文、訳文をXslateに渡す形にまとめる
my $segments = generate_template_arg( \@srcbody, \@targetbody );

# テンプレートエンジン起動
&generate_tmx( $segments, $srclang, $targetlang );

補足

Excelからソースを取ってくる場合、セル内の改行を適当な符号に置き換えること。

でないと、分節の対応がズレます。

Office TanakaのサイトExcelのセル内改行を置換する方法が詳しく説明されています。

ざっくりいうと、置換窓の検索欄でCtrl+Jを押すと、改行コードが入力できます。(見た目は変化ないです。透明。)訂正:Ctrl+Jするとドットが点滅する状態になりました。Office 365のExcelWindows 10で確認)

それで置換欄になんでもいいのでちょうどよい文字列を入れて置換します。正規表現のメタ文字にならって\nでいいのではないでしょうか。

で、改行コードを\nと置換したファイルを材料に上記のPerlスクリプトでTMXを生成します。

そいつをテキストエディタで開き、\nを本物の改行コードに置換してやれば完成です。

お疲れさまでした。