=begin == クラス定数 === ERROR_MESSAGE エラーメッセージの Hash。キーはエラー番号を String にしたもの。 =end class HonyakuError < StandardError end class Honyaku require 'socket' v = $-v $-v = false VERSION = "0.20" RELEASE_DATE = "$Date: 1999/12/18 01:48:29 $" $-v = v =begin == クラスメソッド === new(user, mode, server = 'localhost', port = Honyaku::SERVER_PORT) mode は "ej" か "je" です。 === int_to_str(i) int を 4 byte string へ変換。 =end def int_to_str(i) raise "out of range" unless - 2 ** (32 - 1) <= i and i < 2 ** (32 - 1) i += 2 ** 32 if i < 0 ( i >> 24).chr + ((i & ~0xff000000) >> 16).chr + ((i & ~0xffff0000) >> 8).chr + ( i & ~0xffffff00 ).chr end =begin === short_to_str(i) short を 2 byte string へ変換。 =end def short_to_str(i) raise "out of range" unless - 2 ** (16 - 1) <= i and i < 2 ** (16 - 1) i += 2 ** 16 if i < 0 ((i & ~0xffff0000) >> 8).chr + ( i & ~0xffffff00 ).chr end =begin === str_to_int(str) 4 byte string を int へ変換。 =end def str_to_int(str) i = (str[0]<<24) + (str[1]<<16) + (str[2]<<8) + str[3] if 2 ** (32 - 1) <= i i - 2 ** 32 else i end end =begin === str_to_int(str) 2 byte string を short へ変換。 =end def str_to_short(str) i = (str[0]<<8) + str[1] if 2 ** (16 - 1) <= i i - 2 ** 16 else i end end module_function :int_to_str, :short_to_str, :str_to_int, :str_to_short HEAD = "\xaa\xbb\xcc\xdd" PROTOCOL_VERSION = int_to_str(0xe001) # プロトコルバージョン EG_INIT = int_to_str(0xf00001) # 使用開始 EG_END = int_to_str(0xf00002) # 使用終了 EG_TRANSLATE_ONE = int_to_str(0xf00004) # 一文翻訳 EG_BROWSE_DICT = int_to_str(0xf00015) # 辞書参照 EG_GETEQUIV = int_to_str(0xf00007) # 訳語対応 EG_GETWORD = int_to_str(0xf00008) # 訳語候補リストの取得 EG_SETWORD = int_to_str(0xf00009) # 訳語変更 EG_GETHINSHI = int_to_str(0xf0000a) # 品詞候補リストの取得 EG_SETHINSHI = int_to_str(0xf0000b) # 品詞変更 EG_GETTRNTXT = int_to_str(0xf0000c) # 訳文を返す EG_SETLEARN = int_to_str(0xf0000e) # 学習指定 EG_SETMODE = int_to_str(0xf00011) # 翻訳モードのセット EG_SETDICTNAME = int_to_str(0xf00013) # 使用辞書の設定 EG_CLEAR_CACHE = int_to_str(0xf00014) # 辞書キャッシュのクリア TRNS_GET_STATUS = int_to_str(0xf0001a) # サーバーのステータス取得 TRNS_DIVIDE_SENTENCE = int_to_str(0xf0001b) # 文分割 EG_SET_TIMEOUT = int_to_str(0xf0001c) # タイムアウトの設定 EJ = int_to_str(1) JE = int_to_str(2) SERVER_PORT = 2744 # デフォルトサーバポート LOCK_WAITTIME = 30 # デフォルト辞書ファイルロックタイムアウト値 TRANS_WAITTIME = 30 # デフォルト翻訳タイムアウト値 =begin ERROR_MESSAGE = Hash[ %w[ -114 EOF_or_Error_on_socket_to_client -126 First_command_is_not_init -137 Not_initialized_for_ej -139 Not_initialized_for_je -141 Already_initialized_for_ej -143 Already_initialized_for_je -202 Unknown_command -206 原文または訳文が長すぎます。文を短く切って再翻訳してください。 -300 サーバと接続できませんでした。 -500 ウィンドウのサイズが小さ過ぎます。 -501 サーバとの通信で致命的エラーが発生しました。 -502 指定された位置には単語が存在しません。 -503 訳語候補がありません。 -600 ライセンスが取得できませんでした。 -1003 構文解析に失敗しました。原文を修正して再翻訳してください。 -1012 原文のその位置には単語が存在しません。 -3000 翻訳で時間がかかり過ぎてタイムアウトが発生しました。 -3004 同一ユーザの学習辞書のロックでタイムアウトが発生しました。 -4000 原文または訳文が長すぎます。文を短く切って再翻訳してください。 -4002 翻訳結果が正しくありません。専門用語辞書の設定を確認してください。 -4003 翻訳結果が正しくありません。専門用語辞書の設定を確認してください。 -4004 まだ翻訳されていません。 -4016 辞書に指定した単語がありません。 -4017 辞書の読み込みができませんでした。 ]] =end =begin == メソッド === request_send(request, trans_waittime = Honyaku::TRANS_WAITTIME) リクエストの送信。返り値(String)はサーバーからのレスポンス。 =end def request_send(request, trans_waittime = TRANS_WAITTIME) IO::select(nil, [@host]) @host.syswrite(HEAD + int_to_str(request.length) + request) IO::select([@host], nil, nil, trans_waittime) status = @host.read(4) # ヘッダ確認 unless status == HEAD raise HonyakuError, status.to_s end length = str_to_int(@host.read(4)) # レスポンス長取得 status = str_to_int(@host.read(4)) # ステータス確認 unless status == 0 raise HonyakuError, status.to_s end @host.read(length - 4) # レスポンス取得 end module_function :request_send =begin === trns_get_status() サーバーのステータス取得。 =end def trns_get_status() request_send(TRNS_GET_STATUS + int_to_str(1)) end =begin === init(honyaku_version = Honyaku::PROTOCOL_VERSION) 使用開始。 =end def init(honyaku_version = PROTOCOL_VERSION) # Request # 4-byte 識別コード 0xf00001 # 4-byte ej/jeの指定 ej:1 je:2 # 4-byte 0xe001 を指定します # string ユーザのログイン名、辞書環境の構築に使用します request_send(EG_INIT + @mode + honyaku_version + @user + "\000") end =begin === setmode(setmode = nil) 翻訳モードのセット。 =end def setmode(setmode = nil) if @mode == EJ setmode = "KWD" request_send(EG_SETMODE + EJ + setmode + "\000") end end =begin === set_timeout(lock_waittime = Honyaku::LOCK_WAITTIME, trans_waittime = Honyaku::TRANS_WAITTIME) タイムアウトの設定。 =end def set_timeout(lock_waittime = LOCK_WAITTIME, trans_waittime = TRANS_WAITTIME) request_send(EG_SET_TIMEOUT + @mode + int_to_str(lock_waittime) + int_to_str(trans_waittime)) end =begin === setdictname(computer_dict_use = 1, dict_learning = 1) 使用辞書の設定。返り値(String)は辞書の種別。 + computer_dict_use: コンピュータ専門辞書使用(不使用:0) + dict_learning: 学習辞書使用(不使用:0) =end def setdictname(computer_dict_use = 1, dict_learning = 1) request_send(EG_SETDICTNAME + @mode + int_to_str(computer_dict_use) + # コンピュータ専門辞書使用(不使用:0) int_to_str(dict_learning) + # 学習辞書使用(不使用:0) int_to_str(3) + # 学習辞書の数 short_to_str(3) + # 専門用語辞書 "C" + # ID "Ccomputer\000" + # 辞書名 short_to_str(0) + # システム辞書 "S" + # ID "Sdummy\000" + # 辞書名 short_to_str(4) + # 学習辞書 "L" + # ID "L" + @user + "\000") # 辞書名 end module Sentence def is_need_trans? true end def is_normal_text? false end def is_title? false end def is_list? false end def is_paragraph? false end def is_follow? false end def is_prepared? false end end =begin === trns_divide_sentence(string, option = 1) 文分割。返り値(String を含む Array)は、文に分割された配列。 + option: 情報文字付加(付加しない:0) 次のように、1文を取り出してから翻訳します。 host.trns_divide_sentence("string").each() do |sentence| host.translate_one(sentence) end 取り出された sentence には、次の特異メソッドが定義されています。 + 第1情報 is_need_trans?, is_normal_text?, is_title?, is_list? の4つの内どれかが真。 ++ is_need_trans? 翻訳する必要があるかどうか。 ++ is_normal_text? 通常の文。 ++ is_title? タイトル文。(英大文字だけからなる文など) ++ is_list? 箇条書文。 + 第2情報 is_paragraph?, is_follow?, is_prepared? の3つの内どれかが真。 ++ is_paragraph? 段落の始まりの文。 ++ is_follow? 段落内の2文目以降の文。 ++ is_prepared? テーブルのような整形がされていて、前の文とはタブなどで区切られた文。 =end def trns_divide_sentence(string, mode, option = 1) @mode = mode.upcase == "EJ" ? Honyaku::EJ : Honyaku::JE response = request_send(TRNS_DIVIDE_SENTENCE + @mode + int_to_str(option) + # 情報文字付加(付加しない:0) string + "\000").chop!.chomp! response[0,4] = '' # 分割した文の数 (とりあえず破棄) p response if $DEBUG response = response.split("\n").filter() do |p| sentence = p[2..-1] sentence.extend(Sentence) case p[0].chr when / / def sentence.is_normal_text? true end when /L/ def sentence.is_list? true end when /T/ def sentence.is_title? true end when /[tli]/ def sentence.is_need_trans? false end end case p[1].chr when /P/ def sentence.is_paragraph? true end when / / def sentence.is_follow? true end when /S/ def sentence.is_prepared? true end end sentence end end =begin === translate_one(sentence) 一文翻訳。返り値(String)は訳文。 =end def translate_one(sentence) response = request_send(EG_TRANSLATE_ONE + @mode + int_to_str(0) + # お約束 int_to_str(4096) + # 訳文用バッファサイズ short_to_str(0) + # お約束 short_to_str(0) + # お約束 int_to_str(0) + # お約束 sentence + "\000").chop! response[0,2] = '' response end =begin === close() 使用終了。 =end def close() @mode = Honyaku::EJ request_send(EG_END + @mode) @mode = Honyaku::JE request_send(EG_END + @mode) @host.close end =begin === clear_cache() 辞書キャッシュのクリア。 =end def clear_cache() request_send(EG_CLEAR_CACHE + @mode) end =begin === user 現在のユーザー名。 === mode 現在の辞書のモード。 =end attr_reader("user", "mode") =begin === user = "user" ユーザー名の設定。 =end def user=(user) @user = user mode_init() end =begin === mode = "mode" 辞書のモードの設定。 + "mode" は "ej" か "je" =end def mode=(mode) @mode = mode.upcase == "EJ" ? Honyaku::EJ : Honyaku::JE mode_init() end def mode_init() init() # 使用開始 setdictname() # 使用辞書の設定 set_timeout() # タイムアウトの設定 setmode() # 翻訳モードのセット clear_cache() # 辞書キャッシュのクリア end def initialize(user, server = 'localhost', port = SERVER_PORT) @host = TCPSocket.open(server, port) @host.sync = true @user = user @use_mode = [] @mode = Honyaku::EJ # EJ modeの初期化 mode_init() print "EJ\n" @mode = Honyaku::JE # JE modeの初期化 mode_init() print "JE\n" end end