privacy first cloud amberについて

Amber X - Smart Personal Cloud Device という製品をCloud fundingで購入?した。 これは、カリフォルニアのLattice Workという会社?の製品で、簡単に言うとNAS+VPNである。 スマートフォンなどの写真やビデオなどのコンテンツをDrop boxなどのクラウドサービスに頼らず、自分のストレージにバックアップし、シェアするというもの。 もちろん、サブスクリプション代は不要。

データをfacebookgoogleに預けることに抵抗がある人は一定数いて、かつ、その中でもデータをインターネットから自由に使いたいという人のためのもの。

amber-xとamber-proの2種類が展開されている。 xは個人向けで、PCやスマートフォンのデータバックアップや共有ができる。 proはnext cloudと組み合わせたビジネス向けのもので、連絡先やカレンダーなどの情報も自分自身で制御できるようになる(google calendarとかに預けなくて良い)。

AndroidMacOS向けのクライアントもあるのでほとんどの人のニーズに応えられる。 ストレージは500GBのSSDであるが、USB 3.0インタフェースを持っているため、手持ちのHDDをアタッチして容量を増やすこともできる。 一旦設定してしまえば、自動的にバックアップされるので楽。

購入後最初にスマートフォンとのペアリングをおこなってセットアップし、firmwareのアップグレードもスマートフォンから行う。 日本語対応はしていないが、そもそもあまり文字がないため詰まることはないだろう。

www.myamberlife.com

マクロでerlang:qlcモジュールの妙な制限を撤廃する

Qlcモジュール

erlangにはリスト内包表記でクエリを記述できるqlcモジュールというものが標準で付いており、それをelixirから利用するためのモジュールがQlcだ。hex.pmに登録してあるのでmix.exsにdepsするだけで利用できる。インストールしたのちの話だが、以下のように使うことができる。

iex> require Qlc
iex> list = [a: 1, b: 2]
iex> "[X || X = {K,V} <- L, K =/= Item]"  |>
iex>   Qlc.q(L: list, K: :b) |>
iex>   Qlc.e()
[a: 1]

ところがこうはできない。

iex> list = [a: 1, b: 2]
iex> query = "[X || X = {K,V} <- L, K =/= Item]"
iex> query |> Qlc.q(L: list, K: :b) |> Qlc.e()
** (FunctionClauseError) no function clause matching in Qlc."MACRO-q"/4    
....

なぜかというと、:qlc.q/2の第一引数はリテラルerlang文字列でなければならないという制限があるためだ。それを知らない人から

github.com

というProbrem reportが来た。全部変数にしたいらしい。たしかに。とりあえずベースのerlang:qlcの制限なのだと回答したのだが、コンパイル時に変数かリテラルかは判定できるので、マクロで解決できることに気が付いた。

いつもの変態ハック

Qlc.qがしていたことは、

  • elixirでの文字列をerlangでの文字リストへ変換すること
  • 末尾にピリオドがない場合には追加すること(erlangのリスト内包表記の構文に合わせるため)
  • :qlc.string_to_query/3を呼び出す

という一連のことをコンパイル時に行っていた。これに追加して、

  • 第一引数がリテラルでない場合は、上記3点を「実行時」に行うことにすればいい。

変数や関数呼び出しなら:qlc.string_to_query/3を実行時に呼び出すのはしょうがないし、それらの判断を実行時ではなくコンパイル時に行うことで、実行時のオーバヘッドは0になる。 これらを行うために

  defmacro q(string, bindings, opt \\ []) do
    case is_binary(string) do
      true ->
        exprl = (String.ends_with?(string, ".") && string || string <> ".")
          |> exprs()
          |> Macro.escape()
        quote bind_quoted: [exprl: exprl, bindings: bindings, opt: opt] do
          Qlc.expr_to_handle(exprl, bindings, opt)
        end
      false ->
        quote bind_quoted: [string: string, bindings: bindings, opt: opt] do
          Qlc.string_to_handle(string, bindings, opt)
        end
    end
  end

としただけ。こういうリテラルでなくてはならないとかいう変な制限を撤廃できるのもマクロの良さというわけだ。

現在elixirコミュニティでは「マクロは使うな」というのがコモンセンスなのだが、マクロがないelixirはマクロがないlispと同じだ。つまり意味がない。マクロこそがelixirの力の源泉なのだ。ただ、力が強大すぎるため注意するべきだし、乱用するべきではないのはその通りだ。 たとえば、Cのポインタやmalloc/callocといった機能を使うなというのは、初心者への忠告に過ぎず、Cを本気で武器にするならCの機能を必要に応じて全て使うべきである。

それと同様にalchemistとしてapprenticeからjourneymanへステップアップしたいならば、マクロの利用をためらってはならない。

「「サマータイム導入はコンピュータシステム的に難あり」は本当か (1/2)」は本当か

「「サマータイム導入はコンピュータシステム的に難あり」は本当か」という刺激的なエントリーが某界隈を賑わしていますが、技術者以外の人は信じてしまいそうな恐れがあるので、検証してみます。

「導入すべき理由について考えてみる」について考えてみる

サマータイムとは季節により大きく異なる日の出日の入りの時刻に、一定期間の時間をずらして揃えることで、日の当たる時間を有効活用しようとするものです。」と辞書的な意味を述べるだけで、
結局なにも主張していないので、とくに理由はないようです。
つまりは、7月22日から8月9日と言う猛暑でのオリンピックの競技日程および競技時刻とは何の関係もないということです。*1
次に、「コンピュータシステムについて」ですが、「混乱しない」と言い切っています。
これについては別途まとめて指摘します。

「これに触れる前に今回の「サマータイム」についての根本的な問題を指摘しておきます」で根本的な問題を指摘してみる

なぜ7月や8月なのかは、言及されている通りで引用すると、

アメリカ国内のテレビ放送におけるゴールデンタイムにあわせてのことです。莫大な「放映権料」を支払うアメリカの放送局の都合ということ。
また、かつての東京五輪は真夏の猛暑を避けて10月開催でしたが、これまた視聴率を稼げるアメリカのメジャースポーツや、プロサッカーのオフシーズンを避けると7月、8月の開催しかないという台所事情。

いずれにせよアスリートファーストどころかテレビ局ファーストです。このアスリートファーストとは、夜も昼もないブラック企業が、福利厚生としてマッサージ器を設置する程度の話しです。

さて、すでにその競技日程および時刻が決まってしまったわけで、これをもとにIOCは各国のテレビ局に放映権を販売することが基本的なビジネスモデルです。当然花形競技(国によって違うでしょうが)はお高い値段になるでしょう。

枠を買った後でサマータイム導入で競技時刻がずれるなんてことがあったら大問題です。マラソンなんて始まったと思ったら、ゴール直前ですよ。オリンピックが国内の都合だけで調整できる時代はとっくに終わっているのです(それは上記の引用文にもあるとおりでブログ著者も認識していると思われます)。

つまり「サマータイム導入」なる言葉は欺瞞であり、官公庁の目論見と、サマータイムファーストの銭ゲバ共の、ゲスな欲望を糊塗する詭弁に過ぎないということです。

「コンピュータシステムについて」について

ここからは技術的に看過できない記述が続きます。

コンピュータ(システムやプログラム)には「時間経過」の概念がありません。命令を受けた瞬間からの経過時間は、秒単位でカウントアップしていくだけで、つまり「その瞬間」しかコンピュータは認識していません。

今時の計算機は秒単位でカウントアップなんてしていません。内臓のクロックはナノ秒以上の精度でカウントアップしています。CPU自身は時刻を「認識」はしていませんが、プログラムでは時刻情報を元に計算の分岐はしています。そうであるからこそ2000年問題2038年問題が騒がれたのですし、数年に一度挿入/削除される「うるう秒」で世界的に障害が起きたりしているのです。*2

これをプログラミングにより擬似的に、時間経過の概念があるように見せかけているのが、エアコンなどの「タイマー機能」です。

これはその通りですが、時刻情報を扱っているものもあればないものもあります。毎日決まった時刻にエアコンが起動して、決まった時刻に停止するようなタイマーもありますよね。これらのタイマーが具体的にどういう実装になっているかはそれぞれでしょうが、ほとんど人が設定を変えるまでは自動的にサマータイムになるわけではないため、たしかに影響は少ないといえます。

ホストコンピュータなどと接続していて、連続した情報をやりとりしているシステムなら、バチっと電源を落として、その後の立ち上げで日時の変更をすればよいだけのこと。

このあたりから怪しくなってきます。連続した情報をやり取りしているシステムでバチっと電源をとしていいとする発想が理解不能ですよね。

サマータイムとなり自然時間の定義が変われば、コンピュータも同時に変えなければと「思いこみ」ますが、コンピュータが処理に用いているのは、埋め込まれたクォーツが刻む「内部時間」だけです。

これも部分的に正しいのですが、コンピュータでほんものの「内部時間」を生で使うことはほとんどなく、日付、時刻情報を永続化させるために、UTC的なものを用います。売上日などをデータベースに特定のコンピュータの起動日からの「内部時間」として格納してもバチっと電源落としてしまったら、あとでいつの情報かわかりません。
したがって、UTC(協定世界時)やJST(日本標準時)を格納します。
そしてUTCから日本標準時へはタイムゾーン情報(ハードコードかもしれませんが)を使って変換するのです。
現在主に使われているtzdataとよばれるこのタイムゾーン情報はうるう秒や夏時間およびそれらの変遷をすべて包含したもので、暦の歴史ともいえるものです。
このデータはOSのアップデートなどでひっそり更新されています。

なにより、今の時点からでも即座に対応できる程度のことです。2018年8月8日の読売新聞は「元号の変更とあわせてシステム改修が間に合わない」と書いていましたが、あまりにも無知。

突然元号の話にもなっていますが、元号についてはtzdataではなくOSや言語ランタイムの地域情報になるので全く別の話。それも行儀よく使っていればいいのですが、Javaが広く使われるようになったのが平成になってからであることを考えるとあまりにも無知。

コンピュータの内部は西暦で処理されていますが、元号表記が必要な場合、「1989年1月8日からは平成」と変換しているだけのことです。それが次は「2019年5月1日からは○○」と新元号の処理を一行加えるだけです。

それをどこで加えるかが問題で、行儀よく作られているなら、calendars.propertiesなどで定義すればよいでしょうが、そうでない場合にはプログラムの数だけやらないといけないので大変だというのが「システム改修が間に合わない」の論旨なのに。

サマータイムも同じく、「サマータイム」のフラグが立っていれば、サマータイムの開始日時を参照し、その切り換え日ならば、必要な時間調整をするというだけのもの。プログラミング初心者でもできる程度の処理です。

サマータイムのフラグはどうやって立てるのでしょうね。tzdataしかないでしょうが、個別でやるなら大変なことになります。サマータイムは一度始めたら影響は永遠に残るし、ルールは変わるのです。

たとえばアメリカは2007年からは3月8日以降の日曜日の午前2:00から、11月の第一日曜日2:00までが夏時間になります。その前は1987年からは4月の第一日曜日2:00からでした。
サマータイムを導入するということは、そういうことを未来永久に記録することになります。そのため、サマータイム導入済みの国では廃止論も出ています。*3
日本でも1948年5月第1週の日曜日2:00から1時間、9月第二週の日曜日2:00までサマータイムを実施しています。翌年の1949年には4月第一週の日曜日になり、1950年と1951年は5月第1週日曜日にもどっています。そしてそのまま1951年9月第二週日曜日で終了となりました。

すべての情報に時刻がついてまわり、それが延々と蓄積され続けている現代の社会では、そのような恣意的な暦の操作はもはや現実的ではありません。それがヨーロッパでの廃止論になっています。

一方で、ここがサマータイム導入の議論が日本で難しいところに直結します。この記事を書いた記者のように、コンピュータへの無理解と、同時にコンピュータへの過大な期待が、日本社会の一部にはあるからです。

つまり、アスリートファーストなどの詭弁では無く「不便なのは開始と終了の二日だけ。あとは慣れようよ」と、開き直ったアナウンスを政府がしてしまえば、勤勉でいてイベント好きな日本人なら、勝手に盛り上げてアジャストすることでしょう。

過去にサマータイムを導入した日本では、 制度が定着しなかったこと廃止の理由ですが、現在も健康面への影響などが懸念されているので、日本人ファースト的な考え方なら、導入しないほうがよいでしょう。この記事を書いた人は誰ファーストなのだろうか。
*4

「平成サマータイム議論」の推進は、冒頭にも記述し、この記事を書いた人も指摘しているように、オリンピックは関係ないという前提なら、全く必要のないことばかりか、社会混乱を招き、国民の健康を損なう天下の悪法を推進することになるのです。

さらに、月替わりではなく「週末」や、やたらと増えた「三連休」をサマータイムの切り換え日に当てれば、寝不足や時間調整のイベント化だってできます。

そういうのであれば、サマータイムなどで公的に導入するのではなく、勝手にシフト勤務なり営業すれば良いでしょう。自分が平気なら人も平気というのはあまりにも無神経な考え方と言えます。
変更したいなら、それなりの根拠を丁寧に説明する必要があるのに、それを怠り、「できるからやるのだ」というのは、暴論もいいところです。

その他の影響

日本標準電波(JST=UTC+9)を受信して時刻調整をしている時計は、調整をオフにしない限り、サマータイムに対応できません。標準電波のフォーマットでは予備ビットがあり、それによって夏時間の有無を定義するという仕様も予備ビットの拡張例として記載されていますが、何時間ずらすかは記載がありません*5。今回2時間ずらすので、拡張例を実装していても無理。カシオのG-Shockも日本時間だけ合わないという切ないことに。
テレビやビデオのチューナーもずれるでしょうね。まぁ今時テレビなんかみないから関係ないかな。

まとめ

炎上を狙って自分のサイトへの流入量を増やしたいとかそういう下世話なことを勘ぐってしまいます。
だって、得する人がいないんだもの。いや、いるなら教えて欲しい。
SIer元号対応やみずほ統合作業中で空前の人不足でこんなことやってる場合じゃない。
オリンピック目的は、IOCから怒られるだけだろうし。
政府は、歴史に名を残せるというのはあるでしょうけど、これは良い意味じゃないと思うんだけどいいのかな。

[perl]Spreadsheet::XLSXのeast asia localな事情について

とある事情からperlでXLSXファイルを読まなければならなくなり、cpanから
それっぽいSpreadsheet::XLSXをインストールしてテストしてみた。

|佐藤|
|鈴木|
|田代|

これをSpreadsheet::XLSXでcsvに変換したいわけだが、どういうわけか、

佐藤サトウ
鈴木スズキ
田代タシロ

となってしまうというのが今回の話。

XLSXあるいはOpen Office XMLあるいはecma-376の亡霊

こういう時はgoogle先生の出番なので調べると、というタグに
読み仮名があり、それをレンダリングするためにこうなっているらしく、
いろんな人がタグの部分を無視するようなパッチを作っていた。
これはこれで素晴らしいのだが、これでいいのだろうか? rPhは本当に
無視してしまっていいのだろうか? と思い元の規格であるecma-376
読んでみた。
18.4.6と18.4.3にrPhとphoneticProについて以下のように書かれている。

18.4.6 rPh (Phonetic hint)

This element represents a run of text which displays a phonetic hint
for this String Item (si). Phonetic hints are used to give
information about the pronunciation of an East Asian language. The
hints are displayed as text within the spreadsheet cells across the
top portion of the cell.

18.4.3 phoneticPr (Phonetic Properties)

This element represents a collection of phonetic properties that
affect the display of phonetic text for this String Item (si).
Phonetic text is used to give hints as to the pronunciation of an East
Asian language, and the hints are displayed as text within the
spreadsheet cells across the top portion of the cell. Since the
phonetic hints are text, every phonetic hint is expressed as a
phonetic run (rPh), and these properties specify how to display that
phonetic run.

つまり東アジアの言語の発音のヒントとして使われるためのものらしい。
実際、ExcelではIM入力したものは、読み仮名でソートされるが、他所から
コピぺで貼り付けたものは読み仮名情報が欠落するので文字コード順の
ソートになる。Excelきもいよ。

ということは、無下に捨てるわけにもいかないな。

いつもの変態ハック

ということで、

phonetic hint support cf ecma-376 Part1 18.4.6 rPh, 18.4.3 phoneticPr by k1complete · Pull Request #1 · k1complete/Spreadsheet-XLSX · GitHub

に勝手にプルリクを作ってみた。

これは、セルA1に'課きく 毛こ'を入れた場合(なおこれは、ecma-376で
exampleとして挙げられている由緒正しい例です)、

$cells->[0][0]->{Val}; # =>'課きく 毛こ'
$cells->[0][0]->{Rph}->[0]->{Val}; # => 'キク'
$cells->[0][0]->{Rph}->[0]->{Sb}; # => '0'
$cells->[0][0]->{Rph}->[0]->{Eb}; # => '1'
$cells->[0][0]->{Rph}->[1]->{Val}; # => 'コ'
$cells->[0][0]->{Rph}->[0]->{Sb}; # => '4'
$cells->[0][0]->{Rph}->[0]->{Eb}; # => '5'
$cells->[0][0]->{PhoneticPr}->{FontId}; # => 1

のように取り出せるものです。Sb, Ebは読み仮名に対応する文字列の開始と終了の0始まりのインデックスです。始まりは'=<'、終わりは'<'な感じのインデックスです。
ValはSpreadsheet::XLSXの値取り出しのキーとして使われているので、それを踏襲していますが、そのほかのハッシュのキーはecma-376の属性名やタグ名の先頭文字を大文字化したものを使っています。なお、PhoneticPrにはtypeという属性も定義されていて、デフォルトはfullwidthKatakanaです。

結論

なお、LibreOfficeで開いてからOpen Office XML形式で閉じると、rPhやphoneticPrといった、これらの情報は捨てられます。極東アジアでしか使われていないからね。先輩曰く「east asisaの人間は本当に捨ててもいいのか? おまえら一体どうしたいんだ? ということをwest europaの人にわかってもらわないとマージしてくれないよ」とのことで、格調高く作ってみたものの、やっぱり捨ててもいいんじゃないかと。

実行時にマクロ展開を行う

この記事は #サッポロビーム #137 でやったことの記事です。

クリス・デイトのリレーショナル言語「tutorialD」が気に入ったので、処理系を作ろうと思っていた。
erlangのmnesiaを基盤として、とりあえずできたのだが、何をするにもフルスキャン必須で、実用にならないため、実装方法を再検討することにした。qlcは、リスト内包表記のコンパイル時に、使える場合はインデックスを使うようにクエリを最適化してくれるので、その機能を使いたい。

例:

 {table2, key, mark, value}

というタプルの場合で、value列にはindexを作成していると仮定して、valueを検索したいとしよう。タプルXのvalue要素はelement(4, X)で取り出せるので、インデックスを使って欲しい場合、qlcのクエリとしては

  qlc:q("[ X || X <- Q, element(4, X) =:= Y]", 
        [{Q, mnesia:table(table2)}, {Y, x}])

のようにするとよい。しかしこの、element(4,X)の4というのはコンパイル時に決まっている必要があるが、データベースのスキーマコンパイル時には予見できない。
でもプログラミング時にはvalueという名前で4番目の要素を指定したい。

こんな時にはマクロでなんとかできないかと考えるわけだが、今回は実行時に解決しなければならない。ということで、実行時にASTを展開して、一旦文字列化してからqlc:string_to_handle/3 を呼ぶ方針にする。

もう一つの問題

relvar |> where(value == 4 and mark == :atom1) 

などと書くと、実行時に (value == 4 and mark == :atom1)を書き換えて、

    :qlc.string_to_handle("[ X || X <- Q, element(4, X) =:= Y, element(3, X) =:= Z].",
         [{Q, mnesia:table(relvar.table()), {Y, 4}, {Z, :atom}])

のようにして欲しいわけだが以下の問題がある。

  • =:=/2という中置演算子はelixirにはないのでMacro.to_string/2では :"=:="(a, b)となってしまう。
  • 同様に,/2や;/2も,(a, b), ;(a, b)となってしまう。

実はMacro.to_string/2の第2引数を使うことで、文字列化の制御が可能だ。

   def fmt(ast, x) do
    case ast do
      {:"==", m, [a, b]} ->
        Macro.to_string(a, &fmt/2) <> " =:= " <> Macro.to_string(b, &fmt/2)
      {:and, m, [a, b]} ->
        Macro.to_string(a, &fmt/2) <> ", " <> Macro.to_string(b, &fmt/2)
      {:or, m, [a, b]} ->
        Macro.to_string(a, &fmt/2) <> "; " <> Macro.to_string(b, &fmt/2)
      _ ->
        x
    end
  end

このようにすることで、必要な演算子の文字列化を制御できる。書き換えポイントで再帰的に
Macro.to_string/2を呼び出しているので、それまで構築していたものは捨てられてしまうが
しょうがない。

最後の問題

上記では、where句の引数は実行時まで評価されないため、elixirの変数を含んだ式を記述できない。
これではいくら何でも使いにくすぎる。そのため、

   a = 4
   b = :atom1
   relvar |> where ([a: a, b: b], value == a and mark == b)

のようにして変数の束縛を指定することで、コンパイル時に

   a = 4
   b = :atom1
   relvar |> where (quote bind_quoted: [a: a, b: b], do: value == a and mark == b)

のように変換し、実行時(つまりマクロ展開時)にa, bの値を決められるようにしておけばいい。
つまり「マクロを生成するマクロ」を作り、実行時に生成されたマクロを展開したうえでqlcが求めている形式にて:qlc.string_to_handle/3で最適化されたクエリを構築することになる。

elixir-v1.1.0リリース

elixirのv1.1.0がリリースされました。パチパチ。MapSetなど重要な機能が追加されています。Macro周りではimportでの名前の衝突を検出して、勝手に乗っ取ることがしずらくなっています。

Mm.exというファイルを作ってみます。

defmodule Mm do
  def h do
    IO.puts "override\n"
  end
end

これをコンパイルしてimport Mmすると昔ならIExのhコマンドを乗っ取ることができましたが、....

bash-3.2$ iex 
Eshell V6.3  (abort with ^G)
Interactive Elixir (1.1.0-beta) - press Ctrl+C to exit (type h() ENTER for help)
iex(1)> c("Mm.ex")
[Mm]
iex(2)> import Mm
nil
iex(3)> h
** (CompileError) iex:3: function h/0 imported from both Mm and IEx.Helpers, call is ambiguous
    (elixir) src/elixir_dispatch.erl:111: :elixir_dispatch.expand_import/6
    (elixir) src/elixir_dispatch.erl:82: :elixir_dispatch.dispatch_import/5
iex(3)>

MmとIEx.Helpersとでh/0が不明瞭であると怒られてしまいます。
これを回避(?)したい場合、import でexceptオプションを使い、IEx.Helpers.h/0を除外しておきます。

iex(2)> import Mm
nil
iex(3)> import IEx.Helpers, except: [h: 0]
nil
iex(4)> h
override

:ok
iex(5)> 

これと同じことをExgettext.Helperで行うと、hコマンドを乗っ取って、日本語リソースにアクセスできるようになります。

$ iex -S mix
...
iex(4)> import IEx.Helpers, except: [h: 0, h: 1]; import Exgettext.Helper
nil
iex(5)> h Enum.reverse/1

                            def reverse(collection) 

コレクションを逆順にします。

例

┃ iex> Enum.reverse([1, 2, 3]) 
┃ [3, 2, 1]

iex(6)> 

https://github.com/k1complete/l10n_elixir
https://github.com/k1complete/l10n_ex_unit
https://github.com/k1complete/l10n_iex

に言語リソースのソースを置きました。今回からex_docにパッチを当てなくても、上記のプロジェクトをcloneしてmix docsでドキュメント生成まではできるようになりました。ただし、elixir本体のドキュメント生成では、「動作中のelixir」から(ex_docが)ドキュメントを取得するため、deps/elixir/binにPATHを設定する必要があります。

http://ns.maqcsa.org/elixir/docs/v1.1.0/elixir/
http://ns.maqcsa.org/elixir/docs/v1.1.0/iex/
http://ns.maqcsa.org/elixir/docs/v1.1.0/ex_unit/

にex_docで生成したリファレンスを置きました。

enjoy!

elixir v1.1.0-betaに対応したリファレンス作った

最近盛り上がってきているelixirですが、v1.1.0-beta記念に、久しぶりに
リファレンスの翻訳を再開してみた。
いつものように elixir, iex, ex_unit だけですが、

http://ns.maqcsa.org/elixir/docs/v1.1.0-beta/

に置いてみました。これまで同様、やる気のないトップページにめげずに各リンクをクリックするとリファレンスが読めるようになります。もとのドキュメントがこの単位なので、翻訳もこの単位です(言い訳)。
なお、これまで関数のドキュメントは@doc、モジュールドキュメントは@moduledocでしたが、@typeのや@callbackにも@docをつけることでそれぞれのドキュメントになるようになっているようでした。

そのため基盤のexgettextも修正しなければならなくなったが、まぁしょうがない。以外にもex_docはほとんど修正が必要なかった。

それにしても、GNU gettextの仕組みはさすがに素晴らしくて、差分を適切にマーキングしてくれるので、fuzzyとuntranslatedのものだけを見ていけばいいw。

とか言っているうちに、v1.1.0-rc.0が出ている!
そして、本家からgettextが出てるみたいなのです。