POSIX 準拠の二つの国際化機能 gettext と catgets について調べてみた

## はじめに

POSIX には二つの国際化機能が標準化されています。一つはあまり使われていない catgets で、もう一つは広く普及している gettext です。catgets は POSIX.1-2001 (Issue 6) に XSI 拡張機能として追加され、POSIX.1-2008 (Issue 7) で基本機能となりました。gettext は 2023 年に改定される予定の Issue 8 で POSIX に追加されます。この二つについて簡単にですが調べてまとめてみました。

注意点があります。まず情報自体が少なく古い情報が多いため内容の正確性に自信がありません。私自身が知りたい情報だったので調べてまとめましたが、できれば他の詳しい人にまとめ直してほしいです。（昔の国際化の話を知っている人なら常識に近い話だと思うんですよね……）

参考にした情報

- [GNU gettext utilities](https://www.gnu.org/software/gettext/manual/html_node/index.html) 0.21.1（[日本語訳 0.19.8.1](https://ayatakesi.github.io/gettext/0.19.8.1/html/index.html)）
- [History of gettext() et al? - comp.unix.solaris".](https://web.archive.org/web/20120323032211/http://compgroups.net/comp.unix.solaris/History-of-gettext-et-al)
- [LinuxWorld Online：LinuxとSolarisの違いを知る：第11回　gettextの紆余曲折](
https://web.archive.org/web/20041217063809/http://www.idg.co.jp:80/lw/news/back/index20020824_01_solaris.html)
  - 「月刊 LinuxWorld 2002年10月号」に掲載されていた記事のウェブ掲載版
  - 2007 年 1 月号を最後に休刊となり現在はページが削除されている
- [4.7.2. ロカールを動かすメカニズム](https://linuxjf.osdn.jp/JFdocs/Secure-Programs-HOWTO.txt)
- [Introduction to Internationalization Programming](https://www.linuxjournal.com/article/6176)
- [Solaris 共通デスクトップ環境 Motif への移行  > 付録 B 国際化と CDE](https://docs.oracle.com/cd/E19620-01/805-5830/6j5gimsdj/index.html)
- [Origin Of The Abbreviation I18n](http://www.i18nguy.com/origini18n.html) （あまり関係ない）

## gettext と catgets の違い

gettext は様々なプログラミング言語で広く使われています。使われている理由はプログラマにとって使いやすいからです。使いやすい理由は文字列（メッセージ）をキーとして使っているからです。

[wikipedia - gettext](https://en.wikipedia.org/wiki/Gettext) より引用

```c
printf(_("My name is %s.\n"), my_name);
```

gettext を使った翻訳は、英語の文字列（正確には翻訳が見つからなかったときの文字列）をそのままソースコードに書き、その部分が各言語に翻訳されるというイメージです。この英語の文字列をキーとしてメッセージカタログから翻訳文を取得するという形になっています。

一方で catgets の場合はソースコードに書くのはメッセージに対応した ID（番号）です。

[IBM - catgets() — Retrieve a Message from a Message Catalog](https://www.ibm.com/docs/en/i/7.3?topic=functions-catgets-retrieve-message-from-message-catalog) より引用

```c
#include <stdio.h>
#include <nl_types.h>
#include <locale.h>
 
/* Name of the message catalog is "/qsys.lib/mylib.lib/msgs.usrspc" */
 
int main(void) {
   nl_catd msg_file;
   char * my_msg;
   char * my_locale;
 
   setlocale(LC_ALL, NULL);
   msg_file = catopen("/qsys.lib/mylib.lib/msgs.usrspc", 0);
   if (msg_file != CATD_ERR)  {
     my_msg = catgets(msg_file, 1, 2, "oops");
     printf("%s\n", my_msg);
     catclose(msg_file);
   }
}
```

上記のコードの `catgets(msg_file, 1, 2, "oops");` の部分がメッセージを取得している部分で、メッセージファイル (msg_file) のセット番号 1 のメッセージ番号 2 のメッセージを取得するという意味です。もし見つからなければ "oops" を返します。このコードは番号を直接使っているので分かりづらいです。定数にすれば少しはマシになると思いますが、メッセージと ID の対応付けの管理からは逃れられません。メッセージが多くなるほど ID の管理が大変になることは容易に想像できます。

catgets には gettext よりも効率が良いなどメリットは一応あるのですが、それよりも管理に手間がかかるというデメリットのほうが大きいです。効率に関して言えば現在のコンピュータではその差はわかりません。GNU gettext 開発者は[個人的なコメント](https://www.gnu.org/software/gettext/manual/html_node/catgets.html)として、catgets はそのインターフェースを作れた人が一部のメンバーだけで、彼らは catgets を使って実際にプログラミングをしようとはしなかったと主張しています。高速でメモリを節約できたことは認めていますが、現在のコンピュータは当時の 1000 倍以上になっています。

## gettext と catgets の誕生

私は gettext は GNU プロジェクトのソフトウェアだと思っていたのですが、他の多くのソフトウェアと同じように GNU が後から再実装したというのが正しいようです。[日本語版 wikipedia の gettext](https://ja.wikipedia.org/wiki/Gettext) には「開発元: GNU プロジェクト」と書かれているのですが、[英語版 wikipedia の gettext](https://en.wikipedia.org/wiki/Gettext) では 2012 年頃から「Original author(s): Sun Microsystems」 と修正されています。

テキストメッセージのローカライズ機能は 1990 年になる少し前、Uniforum の gettext (1988) と X/Open の catgets (1989) の二つの仕様が提案されました。[wikipedia - gettext (en)](https://en.wikipedia.org/wiki/Gettext) にはサン・マイクロシステムが 1993 年に実装したと書かれているのですが、同ページの参考リンクにある [History of gettext() et al? - comp.unix.solaris".](https://web.archive.org/web/20120323032211/http://compgroups.net/comp.unix.solaris/History-of-gettext-et-al) を読むと 1990 年の SunOS 4.1 には含まれていたようです。「[LinuxWorld Online：LinuxとSolarisの違いを知る：第11回　gettextの紆余曲折](
https://web.archive.org/web/20041217063809/http://www.idg.co.jp:80/lw/news/back/index20020824_01_solaris.html)」では SunOS 固有の関数だったと書かれているのですが、いろいろ（[GNU gettext - 11.2 gettextについて](https://ayatakesi.github.io/gettext/0.19.8.1/html/gettext.html) など）読むと、Uniforum の提案をサンが実装したというのが最初に思えます。gettext の実装は 1988 年に登場した GUI ツールキットの [Sun XView](https://en.wikipedia.org/wiki/XView) の一部として含まれていたようです。（UNIX 戦争における UI -  UNIX International 陣営の）サンと AT&T の GUI 仕様である OPEN LOOK で XView が使われていました。

一方で X/Open が提案した catgets ですが、なぜこれが初期の UNIX で選ばれていたのか、その経緯ははっきりとはわかりませんでした。現在の主流は gettext ですが一時期は catgets のほうが UNIX で普及していたように見えます。catgets そのものが選ばれたというよりも UNIX 関係のいざこざの中で、たまたま catgets が残ったのだろうと思っています。UNIX 戦争では UNIX は UI 陣営（サンと AT&T など）と OSF 陣営（DEC、IBM など）に分かれて派閥争いを繰り広げていました。X/Open は catgets を提案したとされる 1989 年時点では中立的な立場にいました。GUI のデスクトップ環境として、UI 陣営 OPEN LOOK（gettext 採用）に対して、OSF 陣営では Motif（catgets 採用）を開発していました。1993 年に AT&T は UNIX の商標権を X/Open に譲渡します。1994年 に UI と OSF は合併し名前としては OSF が残りました。この時にデスクトップ環境は、商用 UNIX のデスクトップ環境の統一を目的とした [共通デスクトップ環境 (CDE)](https://ja.wikipedia.org/wiki/Common_Desktop_Environment) となり、ベースに Motif が採用されました。1996 年に新 OSF は X/Open と合併し The Open Group となりました。現在 The Open Group は UNIX の認定を行っており、UNIX の標準規格である Single UNIX Specification (SUS) の開発を行っています。

一連の流れを国際化機能の点から見ると、gettext を採用したサンとそのデスクトップ環境の影響力が合併とともに小さくなり、catgets を採用した OSF 陣営の影響力が大きくなっていることがわかります。X/Open が自分たちの標準規格 X/Open Portability Guide (XPG) に catgets を組み込むのは当然のことであり、X/Open が OSF と合併して最終的に SUS へとつながっていきます。Motif が catgets を採用した理由はわかりませんが、あるとしたらおそらくパフォーマンスでしょう。当時のコンピュータの性能はかなり低かったからです。そのあとは UNIX 関係のいざこざで最終的に catgets を提案した X/Open の元へ UNIX の権利が移され、共通デスクトップ環境の統一に伴ってそれまで使われていた catgets が残ったのだろうというのが私の理解です。

ちなみにですが Solaris 11 (2011) でデスクトップ環境は、共通デスクトップ環境 (CDE) から Oracle Solaris デスクトップ (GNOME 2.30) に置き換えられています（参考 [Oracle Solaris 10 から Oracle Solaris 11 - 削除されたデスクトップ機能](https://docs.oracle.com/cd/E26924_01/html/E25934/glmbw.html)）。AIX では CDE がデフォルトのようですが KDE と GNOME も使えるようですね。結局、デスクトップ環境の統一を目指した CDE はデスクトップ環境を統一することはできませんでした。

## POSIX 標準化までの歴史（年表）

UNIX の標準規格である Single UNIX Specification (SUS) 周りの話に POSIX を加えると以下のような歴史になります。

| Year | POSIX        | Single UNIX Specification  | X/Open             | 規格提案 |
| ---- | ------------ | -------------------------- | ------------------ | ------- |
| 1987 |              |	                           | XPG2 (Issue 2) [A] |         |
| 1988 | POSIX.1-1988 |	                           |                    | gettext |
| 1989 |              |	                           |                    | catgets |
| 1990 |              |	                           | XPG3 (Issue 3) [B] |         |
| 1992 | POSIX.2-1992 |	                           |                    |         |
| 1994 |              |	                           | XPG4 (Issue 4)     |         |
| 1995 |              | SUSv1 (Issue 4, Version 2) |                    |         |
| 1997 |              | SUSv2 (Issue 5)            |                    |         |
| 2001 | POSIX.1-2001 |	= SUSv3 (Issue 6) [C]      |                    |         |
| 2008 | POSIX.1-2008 |	= SUSv4 (Issue 7) [D]      |                    |         |
| 2023 | 次期POSIX予定 |	= Issue 8 [E]              |                    |         |

- A: `catopen()`, `catgets()`, `catclose()` が XPG に追加
- B: `gencat` コマンドが XPG に追加
- C: catgets が XSI オプションとして POSIX で標準化
- D: catgets が XSI オプションから 基本機能 (Base) へ変更
- E: gettext が POSIX で標準化（予定）
  - POSIX API: `[dn|dc|dcn|n]gettext[_l]`, `textdomain`, `bindtextdomain`
  - POSIX コマンド: `gettext`, `ngettext`, `msgfmt`, `xgettext`

最初は gettext と catgets のどちらも POSIX で標準化されませんでした。どちらを標準規格に取り込むかに合意が取れなかったようです。しかしながら catgets を提案した X/Open では自分たちの標準規格として XPG2 および XPG3 にて API とコマンドを標準化しました。

1996 年に X/Open は OSF と合併し The Open Group となり、Single UNIX Specification (SUS) を開発することなります。SUS のベースとして XPG が採用されたため、必然的に catgets は SUS の一部となります。2001 年に複数の UNIX 標準規格が POSIX として統合されます。SUS 固有の規格は XSI オプションとして POSIX 基本規格の追加規格という形で組み込まれます。さらに 2008 年に XSI オプションの一部が基本規格へと移動し catgets は基本規格の一部となりました。

一方で広く普及している gettext を POSIX 標準規格に含めるべきだという[提案](https://www.austingroupbugs.net/view.php?id=1122) が 2017 年にあり、2023 年予定の Issue 8 で追加される予定です。

## さいごに

国際化の機能は世界中で使われるソフトウェア、特にエンドユーザーが使うような（GUI の）ソフトウェアとって必要不可欠な機能だと思いますが、誕生してから 30 年以上経ってようやく gettext は POSIX で標準化されます。念のためですが標準化されたのが今であって、規格ができたというわけではありません。 Uniforum による gettext 仕様の提案は 1988 年、最初の実装は 1990 年頃、GNU gettext が誕生した 1995 年以降、Linux の普及と共に広く使われるようになりました。仮に catgets がなくて CDE あたりが gettext を採用していればもっと早くに標準化されていたことでしょう。

いまさら標準化かよ！思うかもしれませんが POSIX というのは基本的にこのようなものです。POSIX の原則は新しいものを発明するのではなく、広く普及し移植性があると認められたものを採用するというのが標準化です。標準化の時に既存の実装の間に互換性がない場合は調整が入ることがありますが、可能な限り既存の実装を変えなくても良いような形で標準化されます。当時の判断としては国際化の標準規格にはどちらも十分な移植性がなかったということなのでしょう。

使いやすさはともかく catgets が主流になりつつあった 1990年代半ば頃から Linux が誕生し勢力を伸ばし始めます。catgets が POSIX に追加された 2001 年以降、Linux が UNIX のシェアを奪っていきます。2010 年頃には完全に Linux 優位の流れになっていたと思いますが、POSIX の前回の改定は 2008 年なので、当時はまだ時期尚早だったのでしょう。POSIX の大規模な改定は 15 年ぶりと今回は時間が空いてしまったので、標準化するタイミングがここしか無かったということです。

古い UNIX を考えれば今も gettext に一本化できるわけではないのかもしれませんが、少なくとも POSIX Issue 8 以降に準拠する UNIX 系 OS であれば gettext をサポートするはずなので、標準化されたということは将来的に catgets は切り捨てても良くなったことを意味しています。gettext はとっくにデファクトスタンダードになっているわけで、UNIX (POSIX) の標準規格に準拠していると主張するためだけに catgets に対応するのは無駄な話です。もっとも世の中の UNIX がちゃんと POSIX Issue 8 に準拠してくるのか？という問題が気になるところではありますが。そもそも gettext に対応してない（できない）UNIX は現実に存在しているのでしょうか？ 調べたり試したりすることが簡単にできない（商用）UNIX はよくわかりません。そういった閉鎖性から商用 UNIX は衰退し影響力を小さくし、オープンな Linux (GNU) の世界で広く普及した gettext が POSIX で標準化されるまでになりました。