LoginSignup
0
0

More than 3 years have passed since last update.

Common Lispで多言語対応のためgettextを使う

Last updated at Posted at 2020-02-16

Common Lispで多言語対応のためgettextを使う

はじめに

普段Wordpressで、poとかmoとかを見かけるものの、ちゃんと向き合ったことがなかったのでCommon Lispの練習がてら、gettextを使ってみることにしました。

環境

今回の内容のソースコード

インストール

まずはaptで必要なツールをインストールしておきます。

$ sudo apt install gettext poedit

Poeditは、翻訳ファイルを作成するためのソフトウェアです。

プロジェクト作成

https://gitlab.com/hu.moonstone/deepspace を使って、ひとまずプロジェクトの雛形を作成します。

$ deepspace -t project -o create -n locale-sample
$ cd locale-sample
$ tree
.
├── build
├── doc
├── lib
├── locale-sample-test.asd
├── locale-sample.asd
├── resources
│   ├── data
│   ├── images
│   └── sounds
├── src
│   ├── c
│   └── lisp
│       ├── locale-sample.lisp
│       └── package.lisp
└── test
    ├── c
    └── lisp
        ├── locale-sample-test.lisp
        └── package.lisp

14 directories, 8 files

ロケール用のファイルを保存するため、

$ mkdir -p resources/locale

で、ディレクトリを先に作成しておきます。

Quicklispなどでcl-gettextを導入し、ASDファイルを次のように記述します。

locale-sample.asd
(asdf:defsystem :locale-sample
  :serial t
  :pathname "src/lisp"
  :components ((:file "package")
               (:file "locale-sample"))
  :depends-on (:asdf :alexandria :gettext))

その後、src/lisp/package.lispに以下の内容を記述します。

src/lisp/package.lisp
(in-package :cl-user)
(defpackage :locale-sample
  (:use :cl :gettext)
  (:export :main))

今回gettextを試すために書いたコードは以下のようなものになります。

src/lisp/locale-sample.lisp
(in-package :locale-sample)

(setup-gettext :locale-sample "locale-sample")
(preload-catalogs #.(asdf:system-relative-pathname :locale-sample "resources/locale/"))

(defun replace-all (string part replacement &key (test #'char=))
    (with-output-to-string (out)
      (loop with part-length = (length part)
            for old-pos = 0 then (+ pos part-length)
            for pos = (search part string
                              :start2 old-pos
                              :test test)
            do (write-string string out
                             :start old-pos
                             :end (or pos (length string)))
            when pos do (write-string replacement out)
         while pos)))

(defparameter *labels* (make-hash-table))
(dolist (label `(,(_ "Dog")
                  ,(_ "Cat")
                  ,(_ "Penguin")))

  (let ((key (alexandria:make-keyword (string-upcase (replace-all label " " "-")))))
    (setf (gethash key *labels*) label)))

(defun main ()
  (setf *current-locale* "ja")
  (format t "-------------------------------~%")
  (format t "~A~%" (_ (gethash :dog *labels*)))
  (format t "~A~%" (_ (gethash :cat *labels*)))
  (format t "~A~%" (_ (gethash :penguin *labels*))))

初期化とリソースの位置を指定

setup-gettextにより、gettextの初期化を行います。その上で、後ほど作成する翻訳ファイルの位置をresources/locale/以下にあるという形で指定します。

(setup-gettext :locale-sample "locale-sample")
(preload-catalogs #.(asdf:system-relative-pathname :locale-sample "resources/locale/"))

テキストのリストを用意

Poeditはソースコード中の特定のテキストを抽出することができるので、まずは翻訳対象のテキストの一覧を作成します。ここでは、*labels*というダイナミック変数にキーと値のペアで文字列を登録しておき、キーを呼び出すことで対応する文字列を取得できる形にしたいため、以下のような形で3つの単語をハッシュテーブルに登録します。

(defparameter *labels* (make-hash-table))
(dolist (label `(,(_ "Dog")
                  ,(_ "Cat")
                  ,(_ "Penguin")))

  (let ((key (alexandria:make-keyword (string-upcase (replace-all label " " "-")))))
    (setf (gethash key *labels*) label)))

キーの名前は自動で作るようにしています。文字列情報を全て大文字に変更し、スペースがある場合はハイフンで繋ぐようにしました。これで、Dog:dogでアクセスでき、Iron Manは、:iron-manでアクセスできるようになります。

このようにしてPoeditのソースから抽出する機能を使うと、3つの単語が一覧に表示されるため、対応する日本語のテキストを入力し保存します。mo、poファイルは/resources/locale/ja/LC_MESSAGES以下に、locale-sample.molocale-sample.poという名前で保存します。

スクリーンショット_2020-02-16_11-32-54.png

ロケールの設定

以下の箇所で、ロケールを日本語に変更しています。

(setf *current-locale* "ja")

実行

;;; End of Pass 1.-------------------------------
犬
猫
ペンギン
0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0