nadoka 0.8.0
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +5 -0
- data/ChangeLog.old +1553 -0
- data/Gemfile +4 -0
- data/README.org +31 -0
- data/Rakefile +1 -0
- data/bin/nadoka +13 -0
- data/lib/rss_check.rb +206 -0
- data/lib/tagparts.rb +206 -0
- data/nadoka.gemspec +29 -0
- data/nadoka.rb +123 -0
- data/nadokarc +267 -0
- data/ndk/bot.rb +241 -0
- data/ndk/client.rb +288 -0
- data/ndk/config.rb +571 -0
- data/ndk/error.rb +61 -0
- data/ndk/logger.rb +311 -0
- data/ndk/server.rb +784 -0
- data/ndk/server_state.rb +324 -0
- data/ndk/version.rb +44 -0
- data/plugins/autoawaybot.nb +66 -0
- data/plugins/autodumpbot.nb +227 -0
- data/plugins/autoop.nb +56 -0
- data/plugins/backlogbot.nb +88 -0
- data/plugins/checkbot.nb +64 -0
- data/plugins/cronbot.nb +20 -0
- data/plugins/dictbot.nb +53 -0
- data/plugins/drbcl.rb +39 -0
- data/plugins/drbot.nb +93 -0
- data/plugins/evalbot.nb +49 -0
- data/plugins/gonzuibot.nb +41 -0
- data/plugins/googlebot.nb +345 -0
- data/plugins/identifynickserv.nb +43 -0
- data/plugins/mailcheckbot.nb +0 -0
- data/plugins/marldiabot.nb +99 -0
- data/plugins/messagebot.nb +96 -0
- data/plugins/modemanager.nb +150 -0
- data/plugins/opensearchbot.nb +156 -0
- data/plugins/opshop.nb +23 -0
- data/plugins/pastebot.nb +46 -0
- data/plugins/roulettebot.nb +33 -0
- data/plugins/rss_checkbot.nb +121 -0
- data/plugins/samplebot.nb +24 -0
- data/plugins/sendpingbot.nb +17 -0
- data/plugins/shellbot.nb +59 -0
- data/plugins/sixamobot.nb +77 -0
- data/plugins/tenkibot.nb +111 -0
- data/plugins/timestampbot.nb +62 -0
- data/plugins/titlebot.nb +226 -0
- data/plugins/translatebot.nb +301 -0
- data/plugins/twitterbot.nb +138 -0
- data/plugins/weba.nb +209 -0
- data/plugins/xibot.nb +113 -0
- data/rice/irc.rb +780 -0
- metadata +102 -0
@@ -0,0 +1,62 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
#
|
3
|
+
# Copyright (c) 2004-2005 SASADA Koichi <ko1 at atdot.net>
|
4
|
+
#
|
5
|
+
# This program is free software with ABSOLUTELY NO WARRANTY.
|
6
|
+
# You can re-distribute and/or modify this program under
|
7
|
+
# the same terms of the Ruby's license.
|
8
|
+
#
|
9
|
+
#
|
10
|
+
# $Id$
|
11
|
+
#
|
12
|
+
|
13
|
+
=begin
|
14
|
+
|
15
|
+
== Abstract
|
16
|
+
|
17
|
+
Add time stamp to each log.
|
18
|
+
|
19
|
+
|
20
|
+
== Configuration
|
21
|
+
BotConfig = [
|
22
|
+
{
|
23
|
+
:name => :TimeStampBot,
|
24
|
+
:interval => 60 * 60, # sec
|
25
|
+
:stampformat => '== %y/%m/%d-%H:%M:%S ==========================================',
|
26
|
+
:client => false,
|
27
|
+
}
|
28
|
+
]
|
29
|
+
|
30
|
+
=end
|
31
|
+
|
32
|
+
class TimeStampBot < Nadoka::NDK_Bot
|
33
|
+
def bot_initialize
|
34
|
+
@interval = @bot_config.fetch(:interval, 60 * 60) # default: 1 hour
|
35
|
+
@stampformat = @bot_config.fetch(:stampformat,
|
36
|
+
'== %y/%m/%d-%H:%M:%S ==========================================')
|
37
|
+
@client = @bot_config.fetch(:client, false)
|
38
|
+
@nexttime = nexttime
|
39
|
+
end
|
40
|
+
|
41
|
+
def nexttime
|
42
|
+
t = (Time.now.to_i + @interval)
|
43
|
+
Time.at(t - (t % @interval))
|
44
|
+
end
|
45
|
+
|
46
|
+
def on_timer tm
|
47
|
+
if tm >= @nexttime
|
48
|
+
stamp_log
|
49
|
+
@nexttime = nexttime
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def stamp_log
|
54
|
+
msg = @nexttime.strftime(@stampformat)
|
55
|
+
@state.channels.each{|ch|
|
56
|
+
@logger.clog(ch, msg, true)
|
57
|
+
}
|
58
|
+
@logger.slog(msg, true) if @client
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
|
data/plugins/titlebot.nb
ADDED
@@ -0,0 +1,226 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
#
|
3
|
+
# Copyright (c) 2009, 2011 Kazuhiro NISHIYAMA
|
4
|
+
#
|
5
|
+
# This program is free software with ABSOLUTELY NO WARRANTY.
|
6
|
+
# You can re-distribute and/or modify this program under
|
7
|
+
# the same terms of the Ruby's license.
|
8
|
+
#
|
9
|
+
|
10
|
+
=begin
|
11
|
+
|
12
|
+
== Abstract
|
13
|
+
|
14
|
+
Reply title of URL.
|
15
|
+
|
16
|
+
== Configuration
|
17
|
+
|
18
|
+
BotConfig << {
|
19
|
+
:name => :TitleBot,
|
20
|
+
:ch => //,
|
21
|
+
:timeout => 10,
|
22
|
+
}
|
23
|
+
|
24
|
+
=end
|
25
|
+
|
26
|
+
require 'nkf'
|
27
|
+
require 'open-uri'
|
28
|
+
require 'timeout'
|
29
|
+
require 'tmpdir'
|
30
|
+
|
31
|
+
begin
|
32
|
+
require 'rubygems'
|
33
|
+
require 'nokogiri'
|
34
|
+
rescue LoadError
|
35
|
+
end
|
36
|
+
|
37
|
+
module URL2Title
|
38
|
+
module_function
|
39
|
+
|
40
|
+
def get_title(url)
|
41
|
+
uri = URI(url)
|
42
|
+
info = { :uri => uri }
|
43
|
+
info[:errors] = []
|
44
|
+
case uri.host
|
45
|
+
when /localhost/, /\A127\./, /\A192\.168\./, /\A10\./
|
46
|
+
info[:title] = "(ignored)"
|
47
|
+
return info
|
48
|
+
end
|
49
|
+
uri.open(:content_length_proc => proc{|x| raise Errno::EFBIG if x && x > 1048576}) do |f|
|
50
|
+
info[:uri] = f.base_uri
|
51
|
+
body = f.read
|
52
|
+
|
53
|
+
if /\.blog\d+\.fc2\.com\z/ =~ uri.host
|
54
|
+
# set last content-type only
|
55
|
+
f.meta_add_field("content-type", f.meta["content-type"].split(/, /)[-1])
|
56
|
+
end
|
57
|
+
|
58
|
+
case f.content_type
|
59
|
+
when /\Atext\//
|
60
|
+
charset = f.charset{} # without block, returns "iso-8859-1"
|
61
|
+
|
62
|
+
# Content-Encoding
|
63
|
+
case
|
64
|
+
when f.content_encoding.empty?
|
65
|
+
# ignore
|
66
|
+
when f.content_encoding.any?{|c_e| /deflate/ =~ c_e }
|
67
|
+
require "zlib"
|
68
|
+
body = Zlib::Inflate.inflate(body)
|
69
|
+
when f.content_encoding.any?{|c_e| /gzip/ =~ c_e }
|
70
|
+
require "zlib"
|
71
|
+
body = Zlib::GzipReader.new(StringIO.new(body)).read || ''
|
72
|
+
end
|
73
|
+
|
74
|
+
# encoding
|
75
|
+
if NKF.guess(body) == NKF::BINARY
|
76
|
+
info[:title] = "(binary)"
|
77
|
+
return info
|
78
|
+
end
|
79
|
+
body = NKF.nkf("-wm0x --numchar-input", body)
|
80
|
+
|
81
|
+
title = nil
|
82
|
+
case uri.host
|
83
|
+
when /\A(?:www\.so-net\.ne\.jp)\z/
|
84
|
+
if %r"<title\b(?>[^<>]*)>(.*?)</title(?>[^<>]*)>"miu =~ body
|
85
|
+
title = $1
|
86
|
+
end
|
87
|
+
if %r!<dt id="ttl">(.*?)</dt>!miu =~ body
|
88
|
+
title = $1
|
89
|
+
end
|
90
|
+
else
|
91
|
+
if %r"<title\b(?>[^<>]*)>(.*?)</title(?>[^<>]*)>"miu =~ body
|
92
|
+
title = $1
|
93
|
+
end
|
94
|
+
if uri.fragment && defined?(::Nokogiri)
|
95
|
+
begin
|
96
|
+
doc = Nokogiri::HTML(body, uri.to_s, 'utf-8')
|
97
|
+
xpath = "//*[@id='#{uri.fragment}' or @name='#{uri.fragment}']"
|
98
|
+
fragment_element = doc.xpath(xpath)
|
99
|
+
# tDiary style
|
100
|
+
unless fragment_element.xpath("span[@class='sanchor']").empty?
|
101
|
+
fragment_element = fragment_element.xpath("..")
|
102
|
+
end
|
103
|
+
info[:fragment_text] = truncate(fragment_element.text)
|
104
|
+
rescue Exception => e
|
105
|
+
info[:errors] << e
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
info[:title] = title || body
|
110
|
+
return info
|
111
|
+
when /\Aimage\//
|
112
|
+
if f.respond_to?(:path) && f.path
|
113
|
+
info[:title] = `identify '#{f.path}'`.sub(/\A#{Regexp.quote(f.path)}/, '').strip
|
114
|
+
return info
|
115
|
+
else
|
116
|
+
info[:title] = "(unknown image format)"
|
117
|
+
return info
|
118
|
+
end
|
119
|
+
else
|
120
|
+
info[:title] = "#{f.content_type} #{f.size} bytes"
|
121
|
+
return info
|
122
|
+
end
|
123
|
+
end
|
124
|
+
rescue Errno::EFBIG
|
125
|
+
info[:title] = "(too big)"
|
126
|
+
return info
|
127
|
+
end
|
128
|
+
|
129
|
+
def truncate s
|
130
|
+
if /\A(?>(.{197})....)/mu =~ s
|
131
|
+
return $1+'...'
|
132
|
+
else
|
133
|
+
return s
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def prepare_url(url)
|
138
|
+
url.sub(/\/\#!\//, '/')
|
139
|
+
end
|
140
|
+
|
141
|
+
def url2title(url)
|
142
|
+
url = prepare_url(url)
|
143
|
+
info = get_title(url)
|
144
|
+
info[:title] = truncate(info[:title])
|
145
|
+
info
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
if __FILE__ == $0
|
150
|
+
def u2t(url)
|
151
|
+
URL2Title.url2title(url)
|
152
|
+
rescue
|
153
|
+
$!.inspect
|
154
|
+
end
|
155
|
+
if ARGV.empty?
|
156
|
+
# TODO: test
|
157
|
+
else
|
158
|
+
ARGV.each do |url|
|
159
|
+
info = u2t(url)
|
160
|
+
p info
|
161
|
+
puts url
|
162
|
+
puts info[:title]
|
163
|
+
end
|
164
|
+
end
|
165
|
+
exit
|
166
|
+
end
|
167
|
+
|
168
|
+
class TitleBot < Nadoka::NDK_Bot
|
169
|
+
include URL2Title
|
170
|
+
|
171
|
+
def bot_initialize
|
172
|
+
if @bot_config.key?(:channels)
|
173
|
+
channels = '\A(?:' + @bot_config[:channels].collect{|ch|
|
174
|
+
Regexp.quote(ch)
|
175
|
+
}.join('|') + ')\z'
|
176
|
+
@available_channel = Regexp.compile(channels)
|
177
|
+
else
|
178
|
+
@available_channel = @bot_config.fetch(:ch, //)
|
179
|
+
end
|
180
|
+
|
181
|
+
@same_bot = @bot_config.fetch(:same_bot, /(?!)/)
|
182
|
+
@nkf_options = @bot_config.fetch(:nkf, "--oc=CP50221 --numchar-input --fb-xml")
|
183
|
+
@timeout = @bot_config.fetch(:timeout, 10)
|
184
|
+
@too_long_threshold = @bot_config.fetch(:too_long_threshold, 250)
|
185
|
+
end
|
186
|
+
|
187
|
+
def send_notice(ch, msg)
|
188
|
+
msg = msg.tr("\r\n", " ")
|
189
|
+
if @nkf_options
|
190
|
+
msg = NKF.nkf(@nkf_options, msg)
|
191
|
+
end
|
192
|
+
super(ch, msg)
|
193
|
+
end
|
194
|
+
|
195
|
+
def on_privmsg prefix, ch, msg
|
196
|
+
return unless @available_channel === ch
|
197
|
+
|
198
|
+
if /https?:/ === msg
|
199
|
+
return if @state.channel_users(ccn(ch)).find{|x| @same_bot =~ x }
|
200
|
+
|
201
|
+
url, = URI.extract(msg, ["http", "https"])
|
202
|
+
info = Timeout.timeout(@timeout) do
|
203
|
+
url2title(url)
|
204
|
+
end
|
205
|
+
return unless info[:title]
|
206
|
+
if url != info[:uri].to_s
|
207
|
+
send_notice(ch, "title bot: #{info[:title]} - #{info[:uri]}")
|
208
|
+
else
|
209
|
+
send_notice(ch, "title bot: #{info[:title]}")
|
210
|
+
end
|
211
|
+
if info[:fragment_text]
|
212
|
+
# ignore when fragment_text is too long
|
213
|
+
if info[:fragment_text].size < @too_long_threshold
|
214
|
+
send_notice(ch, "title bot:: #{info[:fragment_text]}")
|
215
|
+
end
|
216
|
+
end
|
217
|
+
info[:errors].each do |e|
|
218
|
+
@manager.ndk_error e
|
219
|
+
send_notice(ch, "title bot error: #{e}")
|
220
|
+
end
|
221
|
+
end
|
222
|
+
rescue Exception => e
|
223
|
+
send_notice(ch, "title bot error! #{e}")
|
224
|
+
@manager.ndk_error e
|
225
|
+
end
|
226
|
+
end
|
@@ -0,0 +1,301 @@
|
|
1
|
+
# -*-ruby-*-
|
2
|
+
#
|
3
|
+
# Copyright (c) 2010 SASADA Koichi <ko1 at atdot.net>
|
4
|
+
#
|
5
|
+
# This program is free software with ABSOLUTELY NO WARRANTY.
|
6
|
+
# You can re-distribute and/or modify this program under
|
7
|
+
# the same terms of the Ruby's license.
|
8
|
+
#
|
9
|
+
|
10
|
+
=begin
|
11
|
+
|
12
|
+
== Usage with irc client
|
13
|
+
|
14
|
+
trans> hello
|
15
|
+
-> translate hello as a English to Japanese.
|
16
|
+
|
17
|
+
trans:ja> hello
|
18
|
+
-> ditto.
|
19
|
+
|
20
|
+
trans:en,ja> hello
|
21
|
+
-> ditto.
|
22
|
+
|
23
|
+
trans:(([lang_from],)[lang_to])> [phrase]
|
24
|
+
-> translate [phrase] as lang_from to lang_to.
|
25
|
+
|
26
|
+
transj> [phrase]
|
27
|
+
-> translate to Japanese
|
28
|
+
|
29
|
+
transe> [phrase]
|
30
|
+
-> translate to English
|
31
|
+
|
32
|
+
== Configuration:
|
33
|
+
|
34
|
+
BotConfig = [
|
35
|
+
{
|
36
|
+
:name => :TranslateBot,
|
37
|
+
:ch => /.*/,
|
38
|
+
:referer => 'http://rubyforge.org/projects/nadoka/',
|
39
|
+
# Register URL at http://code.google.com/intl/ja/apis/ajaxsearch/signup.html
|
40
|
+
# and set your URL to :referer and your API key to :api_key if you want.
|
41
|
+
:to_lang => 'ja',
|
42
|
+
:to_lang2 => 'en',
|
43
|
+
:ch_kcode => :tojis,
|
44
|
+
},
|
45
|
+
]
|
46
|
+
|
47
|
+
=end
|
48
|
+
|
49
|
+
require 'iconv'
|
50
|
+
require 'kconv'
|
51
|
+
require 'shellwords'
|
52
|
+
require 'cgi'
|
53
|
+
require 'open-uri'
|
54
|
+
begin
|
55
|
+
require 'json'
|
56
|
+
rescue LoadError
|
57
|
+
require 'rubygems'
|
58
|
+
require 'json'
|
59
|
+
end
|
60
|
+
|
61
|
+
class TranslateBot < Nadoka::NDK_Bot
|
62
|
+
def bot_initialize
|
63
|
+
@available_channel = @bot_config[:ch] || /.*/
|
64
|
+
@search_default_lang = (@bot_config[:search_default_lang] || 'ja').sub(/^lang_/, '')
|
65
|
+
@referer = @bot_config[:referer] || 'http://rubyforge.org/projects/nadoka/'
|
66
|
+
@ch_kcode = @bot_config.fetch(:ch_kcode, :tojis)
|
67
|
+
@to_lang = @bot_config.fetch(:to_lang, 'ja')
|
68
|
+
@to_lang2 = @bot_config.fetch(:to_lang2, 'en')
|
69
|
+
@bing_appid = @bot_config.fetch(:bing_appid, nil)
|
70
|
+
end
|
71
|
+
|
72
|
+
def on_privmsg prefix, ch, msg
|
73
|
+
if @available_channel === ch and /^tr/ =~ msg
|
74
|
+
if response = dispatch_command(msg)
|
75
|
+
response.each{|r|
|
76
|
+
send_notice(ch, r) if r
|
77
|
+
}
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|
82
|
+
SHORT_LANG = {'e' => 'en', 'j' => 'ja'}
|
83
|
+
|
84
|
+
def dispatch_command msg
|
85
|
+
begin
|
86
|
+
case msg
|
87
|
+
when /^trans(?:late)?(:(.*?))?>\s*(.+)/o
|
88
|
+
translate($3, *parse_trans_option($2, $3))
|
89
|
+
when /^trans([ej])>\s*(.+)/o
|
90
|
+
translate($2, nil, SHORT_LANG[$1])
|
91
|
+
when /^trans(?:late)?r(:(.*?))?>\s*(.+)/o
|
92
|
+
translate_r($3, *parse_trans_option($2, $3))
|
93
|
+
when /^translang>(.+)/
|
94
|
+
lang = $1.strip
|
95
|
+
desc = 'Unknown. See http://code.google.com/intl/ja/apis/ajaxlanguage/documentation/reference.html#LangNameArray'
|
96
|
+
r = LANGUAGE_MAP_S2L.fetch(lang.downcase,
|
97
|
+
LANGUAGE_MAP.fetch(lang.upcase, desc))
|
98
|
+
"translang> #{lang} = #{r}"
|
99
|
+
end
|
100
|
+
rescue Exception => e
|
101
|
+
"translate bot: #{e.message}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def detect_lang str
|
106
|
+
uri = "http://ajax.googleapis.com/ajax/services/language/detect?v=1.0&q="
|
107
|
+
uri << CGI.escape(str.toutf8)
|
108
|
+
|
109
|
+
result = open(uri, "Referer" => @referer) do |f|
|
110
|
+
JSON.parse(f.read)
|
111
|
+
end
|
112
|
+
|
113
|
+
if result["responseData"]
|
114
|
+
result["responseData"]["language"]
|
115
|
+
end
|
116
|
+
end
|
117
|
+
|
118
|
+
def parse_trans_option opt, str
|
119
|
+
case opt
|
120
|
+
when nil
|
121
|
+
from_lang = detect_lang(str)
|
122
|
+
to_lang = @to_lang
|
123
|
+
if to_lang == from_lang
|
124
|
+
to_lang = @to_lang2
|
125
|
+
end
|
126
|
+
[from_lang, to_lang]
|
127
|
+
when /\A([\w\-]+)[, \>]([\w\-]+)\z/
|
128
|
+
[$1, $2]
|
129
|
+
when /\A([\w\-]+)\z/
|
130
|
+
[nil, $1]
|
131
|
+
else
|
132
|
+
raise "can't parse translation option: #{opt}"
|
133
|
+
end
|
134
|
+
end
|
135
|
+
|
136
|
+
def translate phrase, from_lang, to_lang
|
137
|
+
r = []
|
138
|
+
|
139
|
+
gr = translate_ggl(phrase, from_lang, to_lang)
|
140
|
+
r << "g:translate bot (#{gr[1]}): #{gr[0]}"
|
141
|
+
|
142
|
+
if @bing_appid
|
143
|
+
mr = translate_ms(phrase, from_lang, to_lang) if @bing_appid
|
144
|
+
r << "m:translate bot (#{mr[1]}): #{mr[0]}"
|
145
|
+
end
|
146
|
+
r
|
147
|
+
end
|
148
|
+
|
149
|
+
def translate_r phrase, from_lang, to_lang
|
150
|
+
rs = []
|
151
|
+
%w(ggl ms).each{|system|
|
152
|
+
r = send("translate_#{system}", phrase, from_lang, to_lang)
|
153
|
+
from_lang = r[2]
|
154
|
+
first = r[0]
|
155
|
+
if from_lang
|
156
|
+
r = send("translate_#{system}", r[0], to_lang, from_lang)
|
157
|
+
rs << "#{system.split(//)[0]}:trans_r (#{from_lang}>#{to_lang}>#{from_lang}): #{r[0]} (#{first})"
|
158
|
+
end
|
159
|
+
}
|
160
|
+
rs
|
161
|
+
end
|
162
|
+
|
163
|
+
|
164
|
+
def translate_ggl(phrase, from_lang, to_lang)
|
165
|
+
uri = 'http://ajax.googleapis.com/ajax/services/language/translate?v=1.0&q='
|
166
|
+
uri << CGI.escape(phrase.toutf8)
|
167
|
+
uri << "&langpair=#{from_lang}%7C#{to_lang}"
|
168
|
+
|
169
|
+
result = open(uri, "Referer" => @referer) do |f|
|
170
|
+
JSON.parse(f.read)
|
171
|
+
end
|
172
|
+
|
173
|
+
if result["responseData"]
|
174
|
+
text = CGI.unescapeHTML(result["responseData"]["translatedText"])
|
175
|
+
text = text.send(@ch_kcode) if @ch_kcode
|
176
|
+
from_lang ||= result["responseData"]["detectedSourceLanguage"]
|
177
|
+
opts = "#{from_lang}>#{to_lang}"
|
178
|
+
[text, opts, from_lang]
|
179
|
+
else
|
180
|
+
opts = "#{from_lang ? "from #{from_lang} to " : ''}#{to_lang}"
|
181
|
+
["#{result["responseDetails"]} (#{uri})", opts]
|
182
|
+
end
|
183
|
+
end
|
184
|
+
|
185
|
+
## ms translate
|
186
|
+
|
187
|
+
def get_result_ms result
|
188
|
+
# puts result
|
189
|
+
doc = REXML::Document.new(result)
|
190
|
+
doc.elements.map{|e| e.text}[0]
|
191
|
+
end
|
192
|
+
|
193
|
+
def translate_ms phrase, from_lang, to_lang
|
194
|
+
api_url = 'http://api.microsofttranslator.com/V2/Http.svc/Translate'
|
195
|
+
uri = "#{api_url}?appId=#{@bing_appid}&text=#{CGI.escape(phrase.toutf8)}&to=#{CGI.escape(to_lang)}"
|
196
|
+
begin
|
197
|
+
text = get_result_ms open(uri, "Referer" => @referer).read
|
198
|
+
text = text.send(@ch_kcode) if @ch_kcode
|
199
|
+
opts = "#{from_lang}>#{to_lang}"
|
200
|
+
[text, opts, from_lang]
|
201
|
+
rescue OpenURI::HTTPError => e
|
202
|
+
opts = "#{from_lang ? "from #{from_lang} to " : ''}#{to_lang}"
|
203
|
+
["#{e.message} (uri)", opts]
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# copy from http://code.google.com/intl/ja/apis/ajaxlanguage/documentation/reference.html
|
208
|
+
LANGUAGE_MAP = {
|
209
|
+
'AFRIKAANS' => 'af',
|
210
|
+
'ALBANIAN' => 'sq',
|
211
|
+
'AMHARIC' => 'am',
|
212
|
+
'ARABIC' => 'ar',
|
213
|
+
'ARMENIAN' => 'hy',
|
214
|
+
'AZERBAIJANI' => 'az',
|
215
|
+
'BASQUE' => 'eu',
|
216
|
+
'BELARUSIAN' => 'be',
|
217
|
+
'BENGALI' => 'bn',
|
218
|
+
'BIHARI' => 'bh',
|
219
|
+
'BULGARIAN' => 'bg',
|
220
|
+
'BURMESE' => 'my',
|
221
|
+
'CATALAN' => 'ca',
|
222
|
+
'CHEROKEE' => 'chr',
|
223
|
+
'CHINESE' => 'zh',
|
224
|
+
'CHINESE_SIMPLIFIED' => 'zh-CN',
|
225
|
+
'CHINESE_TRADITIONAL' => 'zh-TW',
|
226
|
+
'CROATIAN' => 'hr',
|
227
|
+
'CZECH' => 'cs',
|
228
|
+
'DANISH' => 'da',
|
229
|
+
'DHIVEHI' => 'dv',
|
230
|
+
'DUTCH'=> 'nl',
|
231
|
+
'ENGLISH' => 'en',
|
232
|
+
'ESPERANTO' => 'eo',
|
233
|
+
'ESTONIAN' => 'et',
|
234
|
+
'FILIPINO' => 'tl',
|
235
|
+
'FINNISH' => 'fi',
|
236
|
+
'FRENCH' => 'fr',
|
237
|
+
'GALICIAN' => 'gl',
|
238
|
+
'GEORGIAN' => 'ka',
|
239
|
+
'GERMAN' => 'de',
|
240
|
+
'GREEK' => 'el',
|
241
|
+
'GUARANI' => 'gn',
|
242
|
+
'GUJARATI' => 'gu',
|
243
|
+
'HEBREW' => 'iw',
|
244
|
+
'HINDI' => 'hi',
|
245
|
+
'HUNGARIAN' => 'hu',
|
246
|
+
'ICELANDIC' => 'is',
|
247
|
+
'INDONESIAN' => 'id',
|
248
|
+
'INUKTITUT' => 'iu',
|
249
|
+
'ITALIAN' => 'it',
|
250
|
+
'JAPANESE' => 'ja',
|
251
|
+
'KANNADA' => 'kn',
|
252
|
+
'KAZAKH' => 'kk',
|
253
|
+
'KHMER' => 'km',
|
254
|
+
'KOREAN' => 'ko',
|
255
|
+
'KURDISH'=> 'ku',
|
256
|
+
'KYRGYZ'=> 'ky',
|
257
|
+
'LAOTHIAN'=> 'lo',
|
258
|
+
'LATVIAN' => 'lv',
|
259
|
+
'LITHUANIAN' => 'lt',
|
260
|
+
'MACEDONIAN' => 'mk',
|
261
|
+
'MALAY' => 'ms',
|
262
|
+
'MALAYALAM' => 'ml',
|
263
|
+
'MALTESE' => 'mt',
|
264
|
+
'MARATHI' => 'mr',
|
265
|
+
'MONGOLIAN' => 'mn',
|
266
|
+
'NEPALI' => 'ne',
|
267
|
+
'NORWEGIAN' => 'no',
|
268
|
+
'ORIYA' => 'or',
|
269
|
+
'PASHTO' => 'ps',
|
270
|
+
'PERSIAN' => 'fa',
|
271
|
+
'POLISH' => 'pl',
|
272
|
+
'PORTUGUESE' => 'pt-PT',
|
273
|
+
'PUNJABI' => 'pa',
|
274
|
+
'ROMANIAN' => 'ro',
|
275
|
+
'RUSSIAN' => 'ru',
|
276
|
+
'SANSKRIT' => 'sa',
|
277
|
+
'SERBIAN' => 'sr',
|
278
|
+
'SINDHI' => 'sd',
|
279
|
+
'SINHALESE' => 'si',
|
280
|
+
'SLOVAK' => 'sk',
|
281
|
+
'SLOVENIAN' => 'sl',
|
282
|
+
'SPANISH' => 'es',
|
283
|
+
'SWAHILI' => 'sw',
|
284
|
+
'SWEDISH' => 'sv',
|
285
|
+
'TAJIK' => 'tg',
|
286
|
+
'TAMIL' => 'ta',
|
287
|
+
'TAGALOG' => 'tl',
|
288
|
+
'TELUGU' => 'te',
|
289
|
+
'THAI' => 'th',
|
290
|
+
'TIBETAN' => 'bo',
|
291
|
+
'TURKISH' => 'tr',
|
292
|
+
'UKRAINIAN' => 'uk',
|
293
|
+
'URDU' => 'ur',
|
294
|
+
'UZBEK' => 'uz',
|
295
|
+
'UIGHUR' => 'ug',
|
296
|
+
'VIETNAMESE' => 'vi',
|
297
|
+
'UNKNOWN' => ''
|
298
|
+
}
|
299
|
+
|
300
|
+
LANGUAGE_MAP_S2L = LANGUAGE_MAP.invert
|
301
|
+
end
|