when_exe 0.4.0 → 0.4.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -1,176 +1,177 @@
1
- # -*- coding: utf-8 -*-
2
- =begin
3
- Copyright (C) 2014 Takashi SUGA
4
-
5
- You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
- =end
7
-
8
- require 'when_exe/locales/transliteration_table'
9
-
10
- module When
11
- module Locale
12
-
13
- #
14
- # AKT: Alphabet of Katakana Transliteration
15
- #
16
- # see { http://www.asahi-net.or.jp/~dy5h-kdm/end2.htm MadKod}
17
- #
18
- AKT = {
19
- 'ja' => {
20
- "a" =>"ア", "i" =>"イ", "u" =>"ウ", "e" =>"エ", "o" =>"オ",
21
- "ka" =>"カ", "ki" =>"キ", "ku" =>"ク", "ke" =>"ケ", "ko" =>"コ", "k" =>"ク",
22
- "qa" =>"カ", "qi" =>"キ", "qu" =>"ク", "qe" =>"ケ", "qo" =>"コ", "q" =>"ク",
23
- "ga" =>"ガ", "gi" =>"ギ", "gu" =>"グ", "ge" =>"ゲ", "go" =>"ゴ", "g" =>"グ",
24
- "sa" =>"サ", "si" =>"シ", "su" =>"ス", "se" =>"セ", "so" =>"ソ", "s" =>"ス",
25
- "za" =>"ザ", "zi" =>"ヂ", "zu" =>"ズ", "ze" =>"ゼ", "zo" =>"ゾ", "z" =>"ズ",
26
- "ja" =>"ジャ", "ji" =>"ジ", "ju" =>"ジュ", "je" =>"ジェ", "jo" =>"ジョ", "j" =>"ジュ",
27
- "ca" =>"カ", "ci" =>"チ", "cu" =>"ク", "ce" =>"セ", "co" =>"コ", "c" =>"ク",
28
- "ta" =>"タ", "ti" =>"ティ", "tu" =>"トゥ", "te" =>"テ", "to" =>"ト", "t" =>"ト",
29
- "tsa"=>"ツァ", "tsi"=>"ツィ", "tsu"=>"ツ", "tse"=>"ツェ", "tso"=>"ツォ", "ts"=>"ツ",
30
- "da" =>"ダ", "di" =>"ディ", "du" =>"ドゥ", "de" =>"デ", "do" =>"ド", "d" =>"ド",
31
- "na" =>"ナ", "ni" =>"ニ", "nu" =>"ヌ", "ne" =>"ネ", "no" =>"ノ", "n" =>"ン",
32
- "ha" =>"ハ", "hi" =>"ヒ", "hu" =>"フ", "he" =>"ヘ", "ho" =>"ホ", "h" =>"フ",
33
- "ba" =>"バ", "bi" =>"ビ", "bu" =>"ブ", "be" =>"ベ", "bo" =>"ボ", "b" =>"ブ",
34
- "pa" =>"パ", "pi" =>"ピ", "pu" =>"プ", "pe" =>"ペ", "po" =>"ポ", "p" =>"プ",
35
- "fa" =>"ファ", "fi" =>"フィ", "fu" =>"フ", "fe" =>"フェ", "fo" =>"フォ", "f" =>"フ",
36
- "va" =>"ヴァ", "vi" =>"ヴィ", "vu" =>"ヴ", "ve" =>"ヴェ", "vo" =>"ヴォ", "v" =>"ヴ",
37
- "ma" =>"マ", "mi" =>"ミ", "mu" =>"ム", "me" =>"メ", "mo" =>"モ", "m" =>"ム",
38
- "mma"=>"ンマ", "mmi"=>"ンミ", "mmu"=>"ンム", "mme"=>"ンメ", "mmo"=>"ンモ",
39
- "ra" =>"ラ", "ri" =>"リ", "ru" =>"ル", "re" =>"レ", "ro" =>"ロ", "r" =>"ル",
40
- "la" =>"ラ", "li" =>"リ", "lu" =>"ル", "le" =>"レ", "lo" =>"ロ", "l" =>"ル",
41
- "wa" =>"ワ", "wi" =>"ウィ", "wu" =>"ゥ", "we" =>"ウェ", "wo" =>"ヲ", "w" =>"ゥ",
42
- "wwa"=>"ウヮ", "wwi"=>"ウィ", "wwu"=>"ウゥ", "wwe"=>"ウェ", "wwo"=>"ウォ",
43
- "xa" =>"ァ", "xi" =>"ィ", "xu" =>"ゥ", "xe" =>"ェ", "xo" =>"ォ", "x" =>"クス",
44
- "ya" =>"ヤ", "yu" =>"ユ", "yo" =>"ヨ", "y" =>"イ",
45
- "kya"=>"キャ", "kyu"=>"キュ", "kyo"=>"キョ",
46
- "kja"=>"キャ", "kvu"=>"キュ", "kjo"=>"キョ",
47
- "qja"=>"キャ", "qju"=>"キュ", "qjo"=>"キョ",
48
- "gya"=>"ギャ", "gyu"=>"ギュ", "gyo"=>"ギョ",
49
- "gja"=>"ギャ", "gju"=>"ギュ", "gjo"=>"ギョ",
50
- "sya"=>"シャ", "syu"=>"シュ", "syo"=>"ショ",
51
- "sja"=>"シャ", "sju"=>"シュ", "sjo"=>"ショ",
52
- "zya"=>"ジャ", "zyu"=>"ジュ", "zyo"=>"ジョ",
53
- "zja"=>"ヂャ", "zju"=>"ヂュ", "zjo"=>"ヂョ",
54
- "jya"=>"ジャ", "jyu"=>"ジュ", "jye"=>"ジェ", "jyo"=>"ジョ",
55
- "cyi"=>"チィ", "cyu"=>"チュ", "tyi"=>"ティ",
56
- "tyu"=>"テュ", "tye"=>"テェ", "tja"=>"チャ",
57
- "tju"=>"チュ", "tjo"=>"チョ", "-" =>"ー", "tsx"=>"ッ",
58
- "dyi"=>"ディ", "dyu"=>"デュ", "dye"=>"デェ",
59
- "dja"=>"ヂャ", "dju"=>"ヂュ", "djo"=>"ヂョ",
60
- "nya"=>"ニャ", "nyu"=>"ニュ", "nyo"=>"ニョ",
61
- "nva"=>"ニャ", "nvu"=>"ニュ", "nvo"=>"ニョ",
62
- "hya"=>"ヒャ", "hyu"=>"ヒュ", "hyo"=>"ヒョ",
63
- "bya"=>"ビャ", "byu"=>"ビュ", "byo"=>"ビョ",
64
- "pya"=>"ピャ", "pyu"=>"ピュ", "pyo"=>"ピョ",
65
- "fyu"=>"フュ", "vyu"=>"ヴュ",
66
- "mya"=>"ミャ", "myu"=>"ミュ", "myo"=>"ミョ",
67
- "mva"=>"ミャ", "mvu"=>"ミュ", "mvo"=>"ミョ",
68
- "rya"=>"リャ", "ryu"=>"リュ", "ryo"=>"リョ",
69
- "lya"=>"リャ", "lyu"=>"リュ", "lyo"=>"リョ",
70
- "wyi"=>"ウイ", "wyu"=>"ウユ", "wye"=>"ウエ",
71
- "xya"=>"ャ", "xyu"=>"ュ", "xyo"=>"ョ",
72
- "yya"=>"イャ", "yyi"=>"イィ", "yyu"=>"イュ", "yye"=>"イェ", "yyo"=>"イョ",
73
- "kwa"=>"クヮ", "kwi"=>"クィ", "kwu"=>"クゥ", "kwe"=>"クェ", "kwo"=>"クォ",
74
- "kha"=>"クァ", "khi"=>"クィ", "khu"=>"クゥ", "khe"=>"クェ", "kho"=>"クォ",
75
- "qha"=>"クァ", "qhi"=>"クィ", "qhu"=>"クゥ", "qhe"=>"クェ", "qho"=>"クォ",
76
- "gwa"=>"グヮ", "gwi"=>"グィ", "gwu"=>"グゥ", "gwe"=>"グェ", "gwo"=>"グォ",
77
- "gha"=>"グァ", "ghi"=>"グィ", "ghu"=>"グゥ", "ghe"=>"グェ", "gho"=>"グォ",
78
- "sha"=>"シャ", "shi"=>"シ", "shu"=>"シュ", "she"=>"シェ", "sho"=>"ショ", "sh"=>"シュ",
79
- "swa"=>"スワ", "swi"=>"スィ", "swu"=>"スゥ", "swe"=>"スェ", "swo"=>"スヲ",
80
- "zha"=>"ツァ", "zhi"=>"ツィ", "zhu"=>"ズゥ", "zhe"=>"ツェ", "zho"=>"ツォ",
81
- "zxa"=>"ツァ", "zxi"=>"ツィ", "zxu"=>"ズゥ", "zxe"=>"ツェ", "zxo"=>"ツォ",
82
- "jxa"=>"ジァ", "jxi"=>"ジィ", "jxu"=>"ジゥ", "jxe"=>"ジェ", "jxo"=>"ジォ",
83
- "cha"=>"チャ", "chi"=>"チ", "chu"=>"チュ", "che"=>"チェ", "cho"=>"チョ",
84
- "twa"=>"トワ", "twi"=>"ツイ", "twu"=>"トゥ", "twe"=>"ツエ", "two"=>"ツー",
85
- "tha"=>"スァ", "thi"=>"スィ", "thu"=>"スゥ", "the"=>"スェ", "tho"=>"スォ",
86
- "tza"=>"ツァ", "tzi"=>"ツィ", "tzu"=>"ズー", "tze"=>"ツェ", "tzo"=>"ツォ",
87
- "dwa"=>"ドワ", "dwi"=>"ドィ", "dwu"=>"ドゥ", "dwe"=>"ドェ", "dwo"=>"ドヲ",
88
- "dha"=>"ダー", "dhi"=>"ジー", "dhu"=>"ドフ", "dhe"=>"ドヘ", "dho"=>"ドホ",
89
- "dsa"=>"ドサ", "dsi"=>"ドシ", "dsu"=>"ズ", "dse"=>"ドセ", "dso"=>"ドソ",
90
- "nxa"=>"ニァ", "nxi"=>"ニィ", "nxu"=>"ニゥ", "nxe"=>"ニェ", "nxo"=>"ニォ",
91
- "hwa"=>"ホヮ", "hwi"=>"ホィ", "hwu"=>"ホゥ", "hwe"=>"ホェ", "hwo"=>"ホォ",
92
- "bha"=>"バー", "bhi"=>"ビー", "bhu"=>"ブー", "bhe"=>"ベー", "bho"=>"ボー",
93
- "pwa"=>"プヮ", "pwi"=>"プィ", "pwu"=>"プゥ", "pwe"=>"プェ", "pwo"=>"プォ",
94
- "pha"=>"ファ", "phi"=>"フィ", "phu"=>"フュ", "phe"=>"フェ", "pho"=>"フォ",
95
- "vha"=>"ヒャ", "vhi"=>"ヒィ", "vhu"=>"ヒュ", "vhe"=>"ヒェ", "vho"=>"ヒョ",
96
- "mha"=>"ムァ", "mhi"=>"ムィ", "mhu"=>"ムゥ", "mhe"=>"ムェ", "mho"=>"ムォ",
97
- "mwa"=>"ムヮ", "mwi"=>"ムィ", "mwu"=>"ムゥ", "mwe"=>"ムェ", "mwo"=>"ムォ",
98
- "rja"=>"リャ", "rji"=>"リィ", "rju"=>"リュ", "rje"=>"リェ", "rjo"=>"リョ",
99
- "lja"=>"リャ", "lji"=>"リィ", "lju"=>"リュ", "lje"=>"リェ", "ljo"=>"リョ",
100
- "wha"=>"ホワ", "whi"=>"ホイ", "whu"=>"ホウ", "whe"=>"ホエ", "who"=>"ホオ",
101
- "wxa"=>"ヮ", "wxi"=>"ヰ", "wxu"=>"ゥ", "wxe"=>"ヱ", "wxo"=>"ウォ",
102
- "xwa"=>"ヮ", "xwi"=>"ヰ", "xwu"=>"ゥ", "xwe"=>"ヱ", "xwo"=>"ウォ"
103
- }
104
- }
105
-
106
- # @private
107
- IAST_K = {
108
- "Ā" => "A-", "ā" => "a-",
109
- "Ī" => "I-", "ī" => "i-",
110
- "Ū" => "U-", "ū" => "u-",
111
- "E" => "e-", "e" => "e-",
112
- "Ē" => "e-", "ē" => "e-",
113
- "O" => "O-", "o" => "o-",
114
- "Ō" => "O-", "ō" => "o-",
115
- "Ṛ" => "R:", "ṛ" => "r:",
116
- "Ṝ" => "R:-", "ṝ" => "r:-",
117
- "R̥" => "R:", "r̥" => "r:",
118
- "R̥̄" => "R:-", "r̥̄" => "r:-",
119
- "Ḷ" => "L:", "ḷ" => "l:",
120
- "Ḹ" => "L:", "ḹ" => "l:",
121
- "Ṃ" => "M:", "ṃ" => "m:",
122
- "Ṁ" => "M:", "ṁ" => "m:",
123
- "Ḥ" => "H:", "ḥ" => "h:",
124
- "C" => "Ch", "c" => "ch",
125
- "Ṭ" => "T", "ṭ" => "t",
126
- "Kh" => "K", "kh" => "k",
127
- "Ch" => "Ch", "ch" => "ch",
128
- "Ṭh" => "T", "ṭh" => "t",
129
- "Th" => "T", "th" => "t",
130
- "Ph" => "P", "ph" => "p",
131
- "Ḍ" => "D", "ḍ" => "d",
132
- "Gh" => "G", "gh" => "g",
133
- "Jh" => "J", "jh" => "j",
134
- "Ḍh" => "D", "ḍh" => "d",
135
- "Dh" => "D", "dh" => "d",
136
- "Bh" => "B", "bh" => "b",
137
- "Ṅ" => "N", "ṅ" => "n",
138
- "Ñ" => "Ny", "ñ" => "ny",
139
- "Ṇ" => "N", "ṇ" => "n",
140
- "Ś" => "Sh", "ś" => "sh",
141
- "Ṣ" => "Sh", "ṣ" => "sh"
142
- }
143
-
144
- # @private
145
- AKT_keys = transliteration_keys_hash(AKT)
146
-
147
- # @private
148
- IAST_K_keys = transliteration_keys(IAST_K)
149
-
150
- #
151
- # Convert AKT string to katakana scripts
152
- #
153
- def self.akt(string, locale='ja')
154
- string.gsub(/([^aeiou])\1/, 'ッ\1').
155
- gsub(/m([fpb])/, 'n\1').
156
- gsub(AKT_keys[locale]) {|code|
157
- AKT[locale][code] || code
158
- }
159
- end
160
-
161
- #
162
- # Convert IAST string to katakana scripts
163
- #
164
- def self.iast_akt(string, locale='ja')
165
- akt(
166
- string.gsub(IAST_K_keys) {|code| IAST_K[code] || code}.
167
- downcase.gsub(/(.):-?(.)|(.):-?\z/) {|code|
168
- "aeiou".index($2||':') ? $1 + $2 :
169
- $1=='m' ? "ン#{$2}" :
170
- code.sub(':','i')
171
- }.
172
- gsub(/([rm])y/, '\1uy'),
173
- locale)
174
- end
175
- end
176
- end
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2014-2015 Takashi SUGA
4
+
5
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
+ =end
7
+
8
+ require 'when_exe/locales/transliteration_table'
9
+
10
+ module When
11
+ module Locale
12
+
13
+ #
14
+ # AKT: Alphabet of Katakana Transliteration
15
+ #
16
+ # see { http://www.asahi-net.or.jp/~dy5h-kdm/end2.htm MadKod}
17
+ #
18
+ AKT = {
19
+ 'ja' => {
20
+ "a" =>"ア", "i" =>"イ", "u" =>"ウ", "e" =>"エ", "o" =>"オ",
21
+ "ka" =>"カ", "ki" =>"キ", "ku" =>"ク", "ke" =>"ケ", "ko" =>"コ", "k" =>"ク",
22
+ "qa" =>"カ", "qi" =>"キ", "qu" =>"ク", "qe" =>"ケ", "qo" =>"コ", "q" =>"ク",
23
+ "ga" =>"ガ", "gi" =>"ギ", "gu" =>"グ", "ge" =>"ゲ", "go" =>"ゴ", "g" =>"グ",
24
+ "sa" =>"サ", "si" =>"シ", "su" =>"ス", "se" =>"セ", "so" =>"ソ", "s" =>"ス",
25
+ "za" =>"ザ", "zi" =>"ヂ", "zu" =>"ズ", "ze" =>"ゼ", "zo" =>"ゾ", "z" =>"ズ",
26
+ "ja" =>"ジャ", "ji" =>"ジ", "ju" =>"ジュ", "je" =>"ジェ", "jo" =>"ジョ", "j" =>"ジュ",
27
+ "ca" =>"カ", "ci" =>"チ", "cu" =>"ク", "ce" =>"セ", "co" =>"コ", "c" =>"ク",
28
+ "ta" =>"タ", "ti" =>"ティ", "tu" =>"トゥ", "te" =>"テ", "to" =>"ト", "t" =>"ト",
29
+ "tsa"=>"ツァ", "tsi"=>"ツィ", "tsu"=>"ツ", "tse"=>"ツェ", "tso"=>"ツォ", "ts"=>"ツ",
30
+ "da" =>"ダ", "di" =>"ディ", "du" =>"ドゥ", "de" =>"デ", "do" =>"ド", "d" =>"ド",
31
+ "na" =>"ナ", "ni" =>"ニ", "nu" =>"ヌ", "ne" =>"ネ", "no" =>"ノ", "n" =>"ン",
32
+ "ha" =>"ハ", "hi" =>"ヒ", "hu" =>"フ", "he" =>"ヘ", "ho" =>"ホ", "h" =>"フ",
33
+ "ba" =>"バ", "bi" =>"ビ", "bu" =>"ブ", "be" =>"ベ", "bo" =>"ボ", "b" =>"ブ",
34
+ "pa" =>"パ", "pi" =>"ピ", "pu" =>"プ", "pe" =>"ペ", "po" =>"ポ", "p" =>"プ",
35
+ "fa" =>"ファ", "fi" =>"フィ", "fu" =>"フ", "fe" =>"フェ", "fo" =>"フォ", "f" =>"フ",
36
+ "va" =>"ヴァ", "vi" =>"ヴィ", "vu" =>"ヴ", "ve" =>"ヴェ", "vo" =>"ヴォ", "v" =>"ヴ",
37
+ "ma" =>"マ", "mi" =>"ミ", "mu" =>"ム", "me" =>"メ", "mo" =>"モ", "m" =>"ム",
38
+ "mma"=>"ンマ", "mmi"=>"ンミ", "mmu"=>"ンム", "mme"=>"ンメ", "mmo"=>"ンモ",
39
+ "ra" =>"ラ", "ri" =>"リ", "ru" =>"ル", "re" =>"レ", "ro" =>"ロ", "r" =>"ル",
40
+ "la" =>"ラ", "li" =>"リ", "lu" =>"ル", "le" =>"レ", "lo" =>"ロ", "l" =>"ル",
41
+ "wa" =>"ワ", "wi" =>"ウィ", "wu" =>"ゥ", "we" =>"ウェ", "wo" =>"ヲ", "w" =>"ゥ",
42
+ "wwa"=>"ウヮ", "wwi"=>"ウィ", "wwu"=>"ウゥ", "wwe"=>"ウェ", "wwo"=>"ウォ",
43
+ "xa" =>"ァ", "xi" =>"ィ", "xu" =>"ゥ", "xe" =>"ェ", "xo" =>"ォ", "x" =>"クス",
44
+ "ya" =>"ヤ", "yu" =>"ユ", "yo" =>"ヨ", "y" =>"イ",
45
+ "kya"=>"キャ", "kyu"=>"キュ", "kyo"=>"キョ",
46
+ "kja"=>"キャ", "kvu"=>"キュ", "kjo"=>"キョ",
47
+ "qja"=>"キャ", "qju"=>"キュ", "qjo"=>"キョ",
48
+ "gya"=>"ギャ", "gyu"=>"ギュ", "gyo"=>"ギョ",
49
+ "gja"=>"ギャ", "gju"=>"ギュ", "gjo"=>"ギョ",
50
+ "sya"=>"シャ", "syu"=>"シュ", "syo"=>"ショ",
51
+ "sja"=>"シャ", "sju"=>"シュ", "sjo"=>"ショ",
52
+ "zya"=>"ジャ", "zyu"=>"ジュ", "zyo"=>"ジョ",
53
+ "zja"=>"ヂャ", "zju"=>"ヂュ", "zjo"=>"ヂョ",
54
+ "jya"=>"ジャ", "jyu"=>"ジュ", "jye"=>"ジェ", "jyo"=>"ジョ",
55
+ "cyi"=>"チィ", "cyu"=>"チュ", "tyi"=>"ティ",
56
+ "tyu"=>"テュ", "tye"=>"テェ", "tja"=>"チャ",
57
+ "tju"=>"チュ", "tjo"=>"チョ", "-" =>"ー", "tsx"=>"ッ",
58
+ "dyi"=>"ディ", "dyu"=>"デュ", "dye"=>"デェ",
59
+ "dja"=>"ヂャ", "dju"=>"ヂュ", "djo"=>"ヂョ",
60
+ "nya"=>"ニャ", "nyu"=>"ニュ", "nyo"=>"ニョ",
61
+ "nva"=>"ニャ", "nvu"=>"ニュ", "nvo"=>"ニョ",
62
+ "hya"=>"ヒャ", "hyu"=>"ヒュ", "hyo"=>"ヒョ",
63
+ "bya"=>"ビャ", "byu"=>"ビュ", "byo"=>"ビョ",
64
+ "pya"=>"ピャ", "pyu"=>"ピュ", "pyo"=>"ピョ",
65
+ "fyu"=>"フュ", "vyu"=>"ヴュ",
66
+ "mya"=>"ミャ", "myu"=>"ミュ", "myo"=>"ミョ",
67
+ "mva"=>"ミャ", "mvu"=>"ミュ", "mvo"=>"ミョ",
68
+ "rya"=>"リャ", "ryu"=>"リュ", "ryo"=>"リョ",
69
+ "lya"=>"リャ", "lyu"=>"リュ", "lyo"=>"リョ",
70
+ "wyi"=>"ウイ", "wyu"=>"ウユ", "wye"=>"ウエ",
71
+ "xya"=>"ャ", "xyu"=>"ュ", "xyo"=>"ョ",
72
+ "yya"=>"イャ", "yyi"=>"イィ", "yyu"=>"イュ", "yye"=>"イェ", "yyo"=>"イョ",
73
+ "kwa"=>"クヮ", "kwi"=>"クィ", "kwu"=>"クゥ", "kwe"=>"クェ", "kwo"=>"クォ",
74
+ "kha"=>"クァ", "khi"=>"クィ", "khu"=>"クゥ", "khe"=>"クェ", "kho"=>"クォ",
75
+ "qha"=>"クァ", "qhi"=>"クィ", "qhu"=>"クゥ", "qhe"=>"クェ", "qho"=>"クォ",
76
+ "gwa"=>"グヮ", "gwi"=>"グィ", "gwu"=>"グゥ", "gwe"=>"グェ", "gwo"=>"グォ",
77
+ "gha"=>"グァ", "ghi"=>"グィ", "ghu"=>"グゥ", "ghe"=>"グェ", "gho"=>"グォ",
78
+ "sha"=>"シャ", "shi"=>"シ", "shu"=>"シュ", "she"=>"シェ", "sho"=>"ショ", "sh"=>"シュ",
79
+ "swa"=>"スワ", "swi"=>"スィ", "swu"=>"スゥ", "swe"=>"スェ", "swo"=>"スヲ",
80
+ "zha"=>"ツァ", "zhi"=>"ツィ", "zhu"=>"ズゥ", "zhe"=>"ツェ", "zho"=>"ツォ",
81
+ "zxa"=>"ツァ", "zxi"=>"ツィ", "zxu"=>"ズゥ", "zxe"=>"ツェ", "zxo"=>"ツォ",
82
+ "jxa"=>"ジァ", "jxi"=>"ジィ", "jxu"=>"ジゥ", "jxe"=>"ジェ", "jxo"=>"ジォ",
83
+ "cha"=>"チャ", "chi"=>"チ", "chu"=>"チュ", "che"=>"チェ", "cho"=>"チョ",
84
+ "twa"=>"トワ", "twi"=>"ツイ", "twu"=>"トゥ", "twe"=>"ツエ", "two"=>"ツー",
85
+ "tha"=>"スァ", "thi"=>"スィ", "thu"=>"スゥ", "the"=>"スェ", "tho"=>"スォ",
86
+ "tza"=>"ツァ", "tzi"=>"ツィ", "tzu"=>"ズー", "tze"=>"ツェ", "tzo"=>"ツォ",
87
+ "dwa"=>"ドワ", "dwi"=>"ドィ", "dwu"=>"ドゥ", "dwe"=>"ドェ", "dwo"=>"ドヲ",
88
+ "dha"=>"ダー", "dhi"=>"ジー", "dhu"=>"ドフ", "dhe"=>"ドヘ", "dho"=>"ドホ",
89
+ "dsa"=>"ドサ", "dsi"=>"ドシ", "dsu"=>"ズ", "dse"=>"ドセ", "dso"=>"ドソ",
90
+ "nxa"=>"ニァ", "nxi"=>"ニィ", "nxu"=>"ニゥ", "nxe"=>"ニェ", "nxo"=>"ニォ",
91
+ "hwa"=>"ホヮ", "hwi"=>"ホィ", "hwu"=>"ホゥ", "hwe"=>"ホェ", "hwo"=>"ホォ",
92
+ "bha"=>"バー", "bhi"=>"ビー", "bhu"=>"ブー", "bhe"=>"ベー", "bho"=>"ボー",
93
+ "pwa"=>"プヮ", "pwi"=>"プィ", "pwu"=>"プゥ", "pwe"=>"プェ", "pwo"=>"プォ",
94
+ "pha"=>"ファ", "phi"=>"フィ", "phu"=>"フュ", "phe"=>"フェ", "pho"=>"フォ",
95
+ "vha"=>"ヒャ", "vhi"=>"ヒィ", "vhu"=>"ヒュ", "vhe"=>"ヒェ", "vho"=>"ヒョ",
96
+ "mha"=>"ムァ", "mhi"=>"ムィ", "mhu"=>"ムゥ", "mhe"=>"ムェ", "mho"=>"ムォ",
97
+ "mwa"=>"ムヮ", "mwi"=>"ムィ", "mwu"=>"ムゥ", "mwe"=>"ムェ", "mwo"=>"ムォ",
98
+ "rja"=>"リャ", "rji"=>"リィ", "rju"=>"リュ", "rje"=>"リェ", "rjo"=>"リョ",
99
+ "lja"=>"リャ", "lji"=>"リィ", "lju"=>"リュ", "lje"=>"リェ", "ljo"=>"リョ",
100
+ "wha"=>"ホワ", "whi"=>"ホイ", "whu"=>"ホウ", "whe"=>"ホエ", "who"=>"ホオ",
101
+ "wxa"=>"ヮ", "wxi"=>"ヰ", "wxu"=>"ゥ", "wxe"=>"ヱ", "wxo"=>"ウォ",
102
+ "xwa"=>"ヮ", "xwi"=>"ヰ", "xwu"=>"ゥ", "xwe"=>"ヱ", "xwo"=>"ウォ"
103
+ }
104
+ }
105
+
106
+ # @private
107
+ IAST_K = {
108
+ "Ā" => "A-", "ā" => "a-",
109
+ "Ī" => "I-", "ī" => "i-",
110
+ "Ū" => "U-", "ū" => "u-",
111
+ "E" => "e-", "e" => "e-",
112
+ "Ē" => "e-", "ē" => "e-",
113
+ "O" => "O-", "o" => "o-",
114
+ "Ō" => "O-", "ō" => "o-",
115
+ "Ṛ" => "R:", "ṛ" => "r:",
116
+ "Ṝ" => "R:-", "ṝ" => "r:-",
117
+ "R̥" => "R:", "r̥" => "r:",
118
+ "R̥̄" => "R:-", "r̥̄" => "r:-",
119
+ "Ḷ" => "L:", "ḷ" => "l:",
120
+ "Ḹ" => "L:", "ḹ" => "l:",
121
+ "Ṃ" => "M:", "ṃ" => "m:",
122
+ "Ṁ" => "M:", "ṁ" => "m:",
123
+ "Ḥ" => "H:", "ḥ" => "h:",
124
+ "C" => "Ch", "c" => "ch",
125
+ "Ṭ" => "T", "ṭ" => "t",
126
+ "Kh" => "K", "kh" => "k",
127
+ "Ch" => "Ch", "ch" => "ch",
128
+ "Ṭh" => "T", "ṭh" => "t",
129
+ "Th" => "T", "th" => "t",
130
+ "Ph" => "P", "ph" => "p",
131
+ "Ḍ" => "D", "ḍ" => "d",
132
+ "Gh" => "G", "gh" => "g",
133
+ "Jh" => "J", "jh" => "j",
134
+ "Ḍh" => "D", "ḍh" => "d",
135
+ "Dh" => "D", "dh" => "d",
136
+ "Bh" => "B", "bh" => "b",
137
+ "Ṅ" => "N", "ṅ" => "n",
138
+ "Ñ" => "Ny", "ñ" => "ny",
139
+ "Ṇ" => "N", "ṇ" => "n",
140
+ "Ś" => "Sh", "ś" => "sh",
141
+ "Ṣ" => "Sh", "ṣ" => "sh"
142
+ }
143
+
144
+ # @private
145
+ AKT_keys = transliteration_keys_hash(AKT)
146
+
147
+ # @private
148
+ IAST_K_keys = transliteration_keys(IAST_K)
149
+
150
+ #
151
+ # Convert AKT string to katakana scripts
152
+ #
153
+ def self.akt(string, locale='ja')
154
+ string.downcase.
155
+ gsub(/([^aeiou])\1/, 'ッ\1').
156
+ gsub(/m([fpb])/, 'n\1').
157
+ gsub(AKT_keys[locale]) {|code|
158
+ AKT[locale][code] || code
159
+ }
160
+ end
161
+
162
+ #
163
+ # Convert IAST string to katakana scripts
164
+ #
165
+ def self.iast_akt(string, locale='ja')
166
+ akt(
167
+ string.gsub(IAST_K_keys) {|code| IAST_K[code] || code}.
168
+ downcase.gsub(/(.):-?(.)|(.):-?\z/) {|code|
169
+ "aeiou".index($2||':') ? $1 + $2 :
170
+ $1=='m' ? "ン#{$2}" :
171
+ code.sub(':','i')
172
+ }.
173
+ gsub(/([rm])y/, '\1uy'),
174
+ locale)
175
+ end
176
+ end
177
+ end
@@ -1,751 +1,751 @@
1
- # -*- coding: utf-8 -*-
2
- =begin
3
- Copyright (C) 2011-2014 Takashi SUGA
4
-
5
- You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
- =end
7
-
8
- require 'when_exe/locales/encoding_conversion'
9
-
10
- module When
11
-
12
- #
13
- # Multilingualization(M17n) 対応モジュール
14
- #
15
- # When::BasicTypes::M17n の実装のうち When::BasicTypes 内部で
16
- # 定義すべきでない部分を切り出してモジュールとしている
17
- #
18
- module Locale
19
-
20
- # Locale 読み替えの初期設定
21
- DefaultAlias = {'alias'=>'ja', '日本語'=>'ja', '英語'=>'en'}
22
-
23
- # 省略時 Namespace
24
- DefaultNamespaces = Hash.new {|hash, key|
25
- hash[key] = "http://#{key}.wikipedia.org/wiki/"
26
- }.update({
27
- 'mailto' => false,
28
- 'https' => false,
29
- 'http' => false,
30
- 'ftp' => false
31
- })
32
-
33
- # 漢字の包摂
34
- DefaultUnification = {
35
- '煕'=>'熙', '廣'=>'広', '寶'=>'宝', '國'=>'国',
36
- '應'=>'応', '觀'=>'観', '龜'=>'亀', '齊'=>'斉',
37
- '靈'=>'霊', '攝'=>'摂', '壽'=>'寿', '萬'=>'万',
38
- '廢'=>'廃', '顯'=>'顕', '會'=>'会', '聰'=>'聡',
39
- '總'=>'総', '證'=>'証', '禮'=>'礼', '與'=>'与',
40
- '竜'=>'龍'
41
- }
42
-
43
- # Escape
44
- # @private
45
- Escape = {
46
- "\\\\" => "\\",
47
- "\\n" => "\n",
48
- "\\r" => "\r",
49
- "\\," => ","
50
- }
51
-
52
- # Wikipedia の URL の正規表現
53
- # @private
54
- Ref = /\Ahttp:\/\/(.+?)\.wikipedia\.org\/wiki\/([^#]+?)\z/
55
-
56
- # Wikipedia の多言語リンクの正規表現
57
- # @private
58
- Link = /<li class="interlanguage-link interwiki-(.+?)"><a href="\/\/(.+?)\.wikipedia\.org\/wiki\/(.+?)" title="(.+?) – /
59
-
60
- class << self
61
-
62
- # Wikipedia の連続的な参照を抑制するための遅延時間/秒
63
- #
64
- # @return [Numeric]
65
- #
66
- attr_accessor :wikipedia_interval
67
-
68
- # When::Locale Module のグローバルな設定を行う
69
- #
70
- # @param [Hash] options 下記の通り
71
- # @option options [Hash] :alias Locale の読み替えパターンを Hash で指定する。
72
- # @option options [String] :namespace_foramt 名前空間定義の省略時に名前空間生成に用いる書式
73
- # @option options [Hash] :unification 漢字の包摂パターンを Hash で指定する。
74
- # @option options [Numeric] :wikipedia_interval Wikipedia の連続的な参照を抑制するための遅延時間/秒
75
- #
76
- # @note
77
- # :alias の指定がない場合、aliases は DefaultAlias(モジュール定数)と解釈する。
78
- # :unification の指定がない場合、unifications は DefaultUnification(モジュール定数)と解釈する。
79
- #
80
- def _setup_(options={})
81
- @aliases = options[:alias] || DefaultAlias
82
- @namespaces = options[:namespace_foramt] || DefaultNamespaces
83
- @unifications = options[:unification] || DefaultUnification
84
- @wikipedia_interval = options[:wikipedia_interval]
85
- end
86
-
87
- # 設定情報を取得する
88
- #
89
- # @return [Hash] 設定情報
90
- #
91
- def _setup_info
92
- {:alias => _alias,
93
- :namespace_foramt => _namespaces,
94
- :unification => _unification,
95
- :wikipedia_interval => @wikipedia_interval}
96
- end
97
-
98
- # 特定 locale に対応した文字列の取得
99
- #
100
- # @param [String] source もとにする String または M17n
101
- #
102
- # @param[String] loc locale の指定 ( lang[-|_]country.encode )
103
- # [ lang - 言語 ]
104
- # [ country - 国(省略可) ]
105
- # [ encode - 文字コード(省略可) ]
106
- #
107
- # @return [String] loc に対応した文字列
108
- #
109
- # @note source が Hash や Array の場合、その構成要素を変換して返す
110
- # @note encode は通常大文字だが、大文字/小文字の変換は行わず指定されたまま使用している
111
- #
112
- def translate(source, loc='')
113
- return source unless loc
114
- case source
115
- when Hash
116
- result = {}
117
- source.each_pair do |key, value|
118
- result[translate(key, loc)] = translate(value, loc)
119
- end
120
- return result
121
- when Array
122
- return source.map {|value| translate(value, loc)}
123
- when Locale
124
- return source.translate(loc)
125
- when String
126
- return source.encode($1) if loc =~ /\.(.+)\z/
127
- end
128
- source
129
- end
130
-
131
- # 包摂リストに登録されている文字を包摂する
132
- #
133
- # @param [When::Locale] source 文字を包摂しようとする国際化文字列
134
- # @param [String] source 文字を包摂しようとする文字列
135
- # @param [Regexp] source 文字を包摂しようとする正規表現
136
- # @param [Hash] pattern 包摂ルール
137
- #
138
- # @return [When::Locale] 文字を包摂した国際化文字列
139
- # @return [String] 文字を包摂した文字列
140
- # @return [Regexp] 文字を包摂した正規表現
141
- #
142
- def ideographic_unification(source, pattern=_unification)
143
- case source
144
- when When::Locale
145
- source.ideographic_unification(pattern)
146
- when Regexp
147
- Regexp.compile(ideographic_unification(source.source.encode('UTF-8'), pattern), source.options)
148
- when String
149
- source.gsub(/./) do |c|
150
- pattern[c] ? pattern[c] : c
151
- end
152
- else
153
- source
154
- end
155
- end
156
-
157
- # 文字列で表現された namespace 指定を Hash に変換する
158
- # @private
159
- def _namespace(source=nil)
160
- case source
161
- when Hash ; source
162
- when nil ; {}
163
- when String
164
- namespace = {}
165
- source = $1 if (source=~/\A\s*\[?(.+?)\]?\s*\z/m)
166
- source.split(/[\n\r,]+/).each do |v|
167
- v.strip!
168
- next if (v=~/\A#/)
169
- pair = [''] + v.split(/\s*=\s*/, 2)
170
- namespace[pair[-2]] = pair[-1]
171
- end
172
- namespace
173
- when When::Parts::Resource::ContentLine
174
- source.object.names
175
- else ; raise TypeError, "Irregal Namespace Type: #{source.class}"
176
- end
177
- end
178
-
179
- # locale 指定を Array に変換する
180
- # @private
181
- def _locale(source=nil)
182
- # default の Locale
183
- return [[nil, '', nil]] unless source
184
-
185
- # source の配列化
186
- if source.kind_of?(String)
187
- source = $1 if (source=~/\A\s*\[?(.+?)\]?\s*\z/m)
188
- source = source.scan(/((?:[^\\\n\r,]|\\.)+)(?:[\n\r,]+(?!\z))?|[\n\r,]+/m).flatten.map {|token|
189
- (token||'').gsub(/\\./) {|escape| Escape[escape] || escape}
190
- }
191
- end
192
-
193
- # 各Localeの展開
194
- source.map {|v|
195
- if v.kind_of?(String)
196
- v = v.strip
197
- next if (v=~/\A#/)
198
- (v =~ /\A(\*)?(.*?)(?:\s*=\s*(.*))?\z/) ? $~[1..3] : [[nil, '', nil]]
199
- else
200
- v
201
- end
202
- }.compact
203
- end
204
-
205
- # 文字列 [.., .., ..] を分割する
206
- # @private
207
- def _split(source)
208
- line = source.dup
209
- return [line] unless line =~ /,/
210
- list = []
211
- b = d = s = 0
212
- (source.length-1).downto(0) do |i|
213
- bs = 0
214
- (i-1).downto(0) do |k|
215
- break unless (line[k,1] == '\\')
216
- bs += 1
217
- end
218
- next if (bs[0] == 1)
219
- case line[i,1]
220
- when "'" ; s = 1-s if (d == 0)
221
- when '"' ; d = 1-d if (s == 0)
222
- when ']','}',')' ; b += 1 if (d+s == 0)
223
- when '[','{','(' ; b -= 1 if (d+s == 0 && b > 0)
224
- when ','
225
- if (b+d+s == 0)
226
- list.unshift(line[i+1..-1])
227
- line = i > 0 ? line[0..i-1] : ''
228
- end
229
- end
230
- end
231
- list.unshift(line)
232
- end
233
-
234
- # locale 指定を解析して Hash の値を取り出す
235
- # @private
236
- def _hash_value(hash, locale, defaults=['', 'en'])
237
- locale = locale.sub(/\..*/, '')
238
- return hash[locale] if hash[locale]
239
- return _hash_value(hash, _alias[locale], defaults) if _alias[locale]
240
- language = locale.sub(/-.*/, '')
241
- return hash[language] if hash[language]
242
- defaults.each do |default|
243
- return hash[default] if hash[default]
244
- end
245
- return nil
246
- end
247
-
248
- # 漢字の包摂パターン
249
- # @private
250
- def _unification
251
- @unifications ||= DefaultUnification
252
- end
253
-
254
- # @private
255
- def _get_locale(locale, access_key)
256
- return nil unless access_key
257
- access_key = access_key.split(/\//).map {|key| key =~ /\A[0-9]+\z/ ? key.to_i : key}
258
- locale = locale.sub(/\..*/, '')
259
- [locale, locale.sub(/-.*/, '')].each do |loc|
260
- symbol = ('Locale_' + loc.sub(/-/,'_')).to_sym
261
- return {locale=>access_key.inject(const_get(symbol)) {|hash, key| hash = hash[key]}} if const_defined?(symbol)
262
- end
263
- return nil
264
- end
265
-
266
- private
267
-
268
- # Locale の読み替えパターン
269
- def _alias
270
- @aliases ||= DefaultAlias
271
- end
272
-
273
- # 名前空間定義の省略時に名前空間生成に用いる書式
274
- def _namespaces
275
- @namespaces ||= DefaultNamespaces
276
- end
277
-
278
- # wikipedia オブジェクトの生成・参照
279
- def wikipedia_object(path, options={})
280
- query = options.delete(:query)
281
- interval = options.key?(:interval) ? options.delete(:interval) : @wikipedia_interval
282
- return nil unless Object.const_defined?(:JSON) && path =~ Ref
283
- _wikipedia_relation(_wikipedia_object(path, $1, $2, query, interval, options), path, query)
284
- end
285
-
286
- # wikipedia の読み込み
287
- def _wikipedia_object(path, locale, file, query, interval, options)
288
- # 採取済みデータ
289
- title = URI.decode(file.gsub('_', ' '))
290
- mode = "".respond_to?(:force_encoding) ? ':utf-8' : ''
291
- dir = When::Parts::Resource.root_dir + '/data/wikipedia/' + locale
292
- FileUtils.mkdir_p(dir) unless FileTest.exist?(dir)
293
-
294
- open("#{dir}/#{file}.json", 'r'+mode) do |source|
295
- json = JSON.parse(source.read)
296
- json.update(Hash[*query.split('&').map {|pair| pair.split('=')}.flatten]) if query
297
- json.key?('names') ?
298
- When::BasicTypes::M17n.new(json) :
299
- When::Coordinates::Spatial.new(json)
300
- end
301
-
302
- rescue => no_file_error
303
- # 新しいデータ
304
- case interval
305
- when 0
306
- raise no_file_error
307
- when Numeric
308
- if @wikipedia_last_access
309
- delay = (@wikipedia_last_access + interval.abs - Time.now.to_f).ceil
310
- sleep(delay) if delay > 0
311
- end
312
- end
313
- contents = nil
314
- begin
315
- OpenURI
316
- source = open(path, 'r'+mode)
317
- contents = source.read
318
- ensure
319
- @wikipedia_last_access = Time.now.to_f
320
- source.close if source
321
- end
322
-
323
- # wikipedia contents
324
- raise KeyError, 'Article not found: ' + title if contents =~ /<div class="noarticletext">/
325
-
326
- # word
327
- word = {
328
- :label => title,
329
- :names => {''=>title, locale=>title},
330
- :link => {''=>path, locale=>path }
331
- }
332
- contents.scan(Link) do |link|
333
- word[:names][$1] = $4
334
- word[:link ][$1] = "http://#{$1}.wikipedia.org/wiki/#{$3}"
335
- end
336
- object = When::BasicTypes::M17n.new(word)
337
-
338
- # location
339
- if contents =~ /tools\.wmflabs\.org\/geohack\/geohack\.php\?.+?params=(.+?[NS])_(.+?[EW])/
340
- location = {
341
- :label => object
342
- }
343
- location[:lat], location[:long] = $~[1..2].map {|pos|
344
- pos.gsub(/_(\d)[._]/, '_0\1_').sub('.', '_').sub('_', '.').gsub('_', '')
345
- }
346
- object = When::Coordinates::Spatial.new(location)
347
- end
348
-
349
- # save data
350
- open("#{dir}/#{file}.json", 'w'+mode) do |source|
351
- source.write(JSON.dump(object.to_h({:method=>:to_h}).update(options)))
352
- end
353
- query ? _wikipedia_object(path, locale, file, query) : object
354
- end
355
-
356
- # wikipedia オブジェクトの関連付け
357
- def _wikipedia_relation(object, path, query)
358
- code_space = path.sub(/[^\/]+\z/, '')
359
- if object.kind_of?(When::Coordinates::Spatial)
360
- object.label._pool['..'] = object
361
- object._pool[object.label.to_s] = object.label
362
- object.send(:child=, [object.label])
363
- object.label.send(:code_space=, code_space)
364
- else
365
- object.send(:code_space=, code_space)
366
- end
367
- object._pool['..'] = path
368
- object._pool['..'] += '?' + query if query
369
- object
370
- end
371
- end
372
-
373
- # ローケール指定時の文字列
374
- #
375
- # @return [Hash]
376
- attr_reader :names
377
-
378
- # 有効なローケール指定
379
- #
380
- # @return [Array<String>]
381
- attr_reader :keys
382
-
383
- # 有効な文字列 - additional attribute
384
- #
385
- # @return [Array<String>]
386
- attr_reader :values
387
-
388
- # 文字列の説明 - additional attribute
389
- #
390
- # @return [Hash] { anyURI }
391
- attr_reader :link
392
-
393
- # Rails 用 I18n 定義へのアクセス・キー
394
- #
395
- # @return [String]
396
- attr_reader :access_key
397
- protected :access_key
398
-
399
- # 特定 locale に対応した文字列の取得
400
- #
401
- # @param [String] loc locale の指定 ( lang[-|_]country.encode )
402
- # [ lang - 言語 ]
403
- # [ country - 国(省略可) ]
404
- # [ encode - 文字コード(省略可) ]
405
- #
406
- # @return [String] loc に対応した文字列
407
- #
408
- def translate(loc='')
409
- return to_s unless loc
410
- loc = loc.sub('_', '-')
411
- lang, code = loc.split(/\./)
412
- result = _label_value(loc)
413
- return result if !code || @names.member?(loc)
414
- return result.encode(code)
415
- end
416
- alias :/ :translate
417
-
418
- # 特定 locale に対応した reference URI の取得
419
- #
420
- # @param [String] loc locale の指定
421
- #
422
- # @return [String] loc に対応した reference URI
423
- #
424
- def reference(loc='')
425
- loc ||= ''
426
- return Locale._hash_value(@link, loc.sub('_', '-'))
427
- end
428
-
429
- # 部分文字列
430
- #
431
- # @param [Range] range String#[] と同様の指定方法で範囲を指定する
432
- #
433
- # @return [When::Locale] 指定範囲に対応した部分文字列
434
- #
435
- def [](range)
436
- dup._copy({
437
- :label => to_s[range],
438
- :names => @names.keys.inject({}) {|l,k|
439
- l[k] = @names[k][range]
440
- l
441
- }
442
- })
443
- end
444
-
445
- # 文字列の一致
446
- #
447
- # @param [String, Regexp] regexp マッチする正規表現
448
- #
449
- # @return [Integer] マッチした位置のindex(いずれかの locale でマッチが成功した場合)
450
- # @return [nil] すべての locale でマッチに失敗した場合
451
- #
452
- def =~(regexp)
453
- @keys.each do |key|
454
- index = (@names[key] =~ regexp)
455
- return index if index
456
- end
457
- return nil
458
- end
459
-
460
- # 部分文字列の位置
461
- #
462
- # @param [String] other 部分文字列
463
- #
464
- # @return [Integer] 部分文字列の先頭のindex(いずれかの locale で部分文字列を含んだ場合)
465
- # @return [nil] すべての locale で部分文字列を含まない場合
466
- #
467
- def index(other)
468
- @keys.each do |key|
469
- index = @names[key].index(Locale.translate(other, key))
470
- return index if index
471
- end
472
- return nil
473
- end
474
-
475
- # 文字列の連結
476
- #
477
- # @param [String, When::Toos::Locale] other 連結する文字列
478
- #
479
- # @return [When::Toos::Locale] 連結された文字列
480
- #
481
- def +(other)
482
- names = {}
483
- case other
484
- when Locale
485
- (@names.keys + other.names.keys).uniq.each do |key|
486
- names[key] = _label_value(key) + other._label_value(key)
487
- end
488
- links = other.link
489
- else
490
- @names.keys.each do |key|
491
- names[key] = _label_value(key) + other.to_s
492
- end
493
- links = {}
494
- end
495
- return dup._copy({:names=>names, :link=>links.merge(link), :label=>to_s + other.to_s})
496
- end
497
-
498
- # 書式指定による文字列化
499
- #
500
- # @param [Array<Object>] other 文字列化する Object の Array
501
- # @param [Array<String>] locale 文字列化を行う locale の指定(デフォルト : すべて)
502
- #
503
- # @return [When::Toos::Locale] 文字列化された Object
504
- #
505
- def _printf(other, locale=nil)
506
- # 処理する配列
507
- terms = other.kind_of?(Array) ? [self] + other : [self, other]
508
-
509
- # locale key の配列
510
- if locale == []
511
- keys = []
512
- else
513
- keys = terms.inject([]) {|k,t|
514
- k += t.keys if t.kind_of?(Locale)
515
- k
516
- }.uniq
517
- if locale
518
- locale = [locale] unless locale.kind_of?(Array)
519
- keys = locale | (locale & keys)
520
- end
521
- end
522
- keys << nil if keys.include?('')
523
-
524
- # names ハッシュ
525
- names = keys.inject({}) {|l,k|
526
- l[k] = When::Coordinates::Pair._format(
527
- (block_given? ? yield(k, *terms) : terms).map {|t|
528
- t.kind_of?(Locale) ? t.translate(k) : t
529
- }
530
- )
531
- l
532
- }
533
-
534
- # link ハッシュ
535
- links = terms.reverse.inject({}) {|h,t|
536
- h.update(t.link) if t.kind_of?(Locale)
537
- h
538
- }
539
-
540
- # 生成
541
- dup._copy({
542
- :label => keys.include?('') ? names.delete(nil) : (names[''] = names[keys[0]]),
543
- :names => names,
544
- :link => links
545
- })
546
- end
547
- alias :% :_printf
548
-
549
- # ローケールの更新
550
- #
551
- # @param [Hash] options 下記の通り
552
- # @option options [String] カントリーコード 表現文字列
553
- # @option options [Hash] :link { カントリーコード => 参照URL文字列 }
554
- # @option options [String] :code_space 規格や辞書を特定するコードスペースのURL文字列
555
- # @option options [String] :label 代表文字列
556
- # @note Hashキーはすべて Optional で、存在するもののみ更新します
557
- #
558
- # @return [self] 更新された Object
559
- #
560
- def update(options={})
561
- options = options.dup
562
- @link.update(options.delete(:link)) if (options.key?(:link))
563
- @code_space = options.delete(:code_space) if (options.key?(:code_space))
564
- self[0..-1] = options.delete(:label) if (options.key?(:label))
565
- unless (options.empty?)
566
- @names.update(options)
567
- @keys = @names.keys.sort
568
- @values = @names.values.sort.reverse
569
- end
570
- return self
571
- end
572
-
573
- # 包摂リストに登録されている文字を包摂する(自己破壊)
574
- #
575
- # @param [Hash] pattern 包摂ルール
576
- #
577
- # @return [When::Locale] self
578
- # @private
579
- def ideographic_unification!(pattern=_unification)
580
- names = {}
581
- @names.each_pair do |key, value|
582
- names[key] = Locale.ideographic_unification(value, pattern)
583
- end
584
- @names = names
585
- @values = @names.values.sort.reverse
586
- self[0..-1] = Locale.ideographic_unification(self.to_s[0..-1], pattern)
587
- return self
588
- end
589
- protected :ideographic_unification!
590
-
591
- # 包摂リストに登録されている文字を包摂する(自己保存)
592
- #
593
- # @param [Hash] pattern 包摂ルール
594
- #
595
- # @return [When::Locale] 包摂結果
596
- #
597
- def ideographic_unification(pattern=_unification)
598
- dup.ideographic_unification!(pattern)
599
- end
600
-
601
- # 閏の表記を扱うための書式付整形
602
- # @private
603
- def prefix(other, locale=nil)
604
- return self.dup unless other
605
- other = m17n(other) unless other.kind_of?(Locale)
606
- other._printf(self, locale) do |k, *t|
607
- t[0] = t[0].translate(k)
608
- t[0] += "%s" unless t[0] =~ /%[-+]?[.\d]*s/
609
- t
610
- end
611
- end
612
-
613
- protected
614
-
615
- # @private
616
- def _copy(options={})
617
- if options.key?('label') # for JSON
618
- opt = {}
619
- options.each_pair do |key, value|
620
- opt[key.to_sym] = value
621
- end
622
- else
623
- opt = options
624
- end
625
-
626
- self[0..-1] = opt[:label] if opt[:label]
627
- if opt[:names]
628
- @names = opt[:names]
629
- @keys = @names.keys.sort
630
- @values = @names.values.compact.sort.reverse
631
- end
632
- @label = opt[:label] if opt[:label]
633
- @link = opt[:link] if opt[:link]
634
- @access_key = opt[:access_key] if opt[:access_key]
635
- @code_space = opt[:code_space] if opt[:code_space]
636
- return self
637
- end
638
-
639
- # @private
640
- def _copy_all(other)
641
- _copy({:label => other.to_s,
642
- :names => other.names,
643
- :link => other.link,
644
- :access_key => other.access_key,
645
- :code_space => other.code_space
646
- })
647
- end
648
-
649
- # locale 指定を解析して @names の値を取り出す
650
- # @private
651
- def _label_value(locale)
652
- label = Locale._hash_value(@names, locale, [])
653
- return label if label
654
- foreign = Locale._get_locale(locale, @access_key)
655
- return @names[''] unless foreign
656
- english = @names['en'] || @names['']
657
- addition = english.dup.sub!(/\A#{Locale._get_locale('en', @access_key)['en']}/, '')
658
- foreign[locale] += addition if addition
659
- update(foreign)
660
- return Locale._hash_value(@names, locale)
661
- end
662
-
663
- private
664
-
665
- def _names(names, namespace, default_locale)
666
-
667
- # names, link の組み立て
668
- @names = {}
669
- @link = {}
670
-
671
- if names.kind_of?(String)
672
- unless (names=~/\A\s*\[(.+?)\]\s*\z/m)
673
- names = names.strip
674
- @names[''] = names
675
- @keys = ['']
676
- @values = [names]
677
- return names
678
- end
679
- names = $1.split(/[\n\r,]+/)
680
- end
681
-
682
- mark = []
683
- asterisk = []
684
- default_locale = default_locale.dup
685
- names.each do |v|
686
- v.strip!
687
- case v
688
- when '', /\A#/ ;
689
- when /\A\/(.+)/; @access_key = $1
690
- when /\A(\*)?(?:([^=%]*?)\s*:)?\s*(.+?)\s*(=\s*([^=]+?(\?.+)?)?)?\z/
691
- asterisk[0], locale, name, assignment, ref = $~[1..5]
692
- asterisk[1], locale, default_ref = default_locale.shift unless locale
693
- locale ||= ''
694
- ref ||= default_ref unless (assignment)
695
- ref ||= ''
696
- mark[0] = locale if asterisk[0]
697
- mark[1] = locale if asterisk[1]
698
- mark[2] = locale unless mark[2]
699
- name = _replacement($1, locale, ($3 || @names['en'] || @names[''])) if name =~ /\A_([A-Z_]+)_(\((.+)\))?\z/
700
- name.gsub!(/<[0-9A-F]{2}>/i) {|code| code[1..2].to_i(16).chr}
701
- @names[locale] = name
702
- if ref =~ /\A(.+):/
703
- prefix = namespace[$1] || Locale.send(:_namespaces)[$1]
704
- ref.sub!(/\A.+:/, prefix) if prefix
705
- end
706
- ref += name =~ /\A<.+>\z/ ? '%%' + name : '%%<' + name + '>' if ref =~ /[\/#:]\z/
707
- unless ref == ''
708
- @link[locale] = _encode(ref)
709
- # When.logger.info("%s[%s]->%s" % [@names[locale], locale, @link[locale]]) if When.logger
710
- end
711
- else ; raise ArgumentError, "Irregal locale format: " + v
712
- end
713
- end
714
- if Locale.wikipedia_interval && Locale.wikipedia_interval <= 0
715
- ['en', ''].each do |lc|
716
- if Locale::Ref =~ @link[lc] && $1 == 'en'
717
- object = Locale.send(:wikipedia_object, @link[lc])
718
- if object
719
- @names = object.names.merge(@names)
720
- @link = object.link.merge(@link)
721
- end
722
- break
723
- end
724
- end
725
- end
726
-
727
- # keys, values の準備
728
- @keys = @names.keys.sort
729
- @values = @names.values.sort.reverse
730
-
731
- # 代表名
732
- @names[mark[0] || mark[1] || mark[2]]
733
- end
734
-
735
- #
736
- # 英語表記を現地表記に置き換える
737
- #
738
- def _replacement(table, locale, name)
739
- Locale.const_get(table.split('_')[-1])[locale] ?
740
- Locale.send(table.downcase, name, locale) :
741
- name
742
- end
743
-
744
- # encode URI from patterns %%(...) or %.(...)
745
- def _encode(source)
746
- source.gsub(/%.<.+?>/) do |match|
747
- URI.encode(match[3..-2]).gsub('%', match[1..1])
748
- end
749
- end
750
- end
751
- end
1
+ # -*- coding: utf-8 -*-
2
+ =begin
3
+ Copyright (C) 2011-2015 Takashi SUGA
4
+
5
+ You may use and/or modify this file according to the license described in the LICENSE.txt file included in this archive.
6
+ =end
7
+
8
+ require 'when_exe/locales/encoding_conversion'
9
+
10
+ module When
11
+
12
+ #
13
+ # Multilingualization(M17n) 対応モジュール
14
+ #
15
+ # When::BasicTypes::M17n の実装のうち When::BasicTypes 内部で
16
+ # 定義すべきでない部分を切り出してモジュールとしている
17
+ #
18
+ module Locale
19
+
20
+ # Locale 読み替えの初期設定
21
+ DefaultAlias = {'alias'=>'ja', '日本語'=>'ja', '英語'=>'en'}
22
+
23
+ # 省略時 Namespace
24
+ DefaultNamespaces = Hash.new {|hash, key|
25
+ hash[key] = "http://#{key}.wikipedia.org/wiki/"
26
+ }.update({
27
+ 'mailto' => false,
28
+ 'https' => false,
29
+ 'http' => false,
30
+ 'ftp' => false
31
+ })
32
+
33
+ # 漢字の包摂
34
+ DefaultUnification = {
35
+ '煕'=>'熙', '廣'=>'広', '寶'=>'宝', '國'=>'国',
36
+ '應'=>'応', '觀'=>'観', '龜'=>'亀', '齊'=>'斉',
37
+ '靈'=>'霊', '攝'=>'摂', '壽'=>'寿', '萬'=>'万',
38
+ '廢'=>'廃', '顯'=>'顕', '會'=>'会', '聰'=>'聡',
39
+ '總'=>'総', '證'=>'証', '禮'=>'礼', '與'=>'与',
40
+ '竜'=>'龍'
41
+ }
42
+
43
+ # Escape
44
+ # @private
45
+ Escape = {
46
+ "\\\\" => "\\",
47
+ "\\n" => "\n",
48
+ "\\r" => "\r",
49
+ "\\," => ","
50
+ }
51
+
52
+ # Wikipedia の URL の正規表現
53
+ # @private
54
+ Ref = /\Ahttp:\/\/(.+?)\.wikipedia\.org\/wiki\/([^#]+?)\z/
55
+
56
+ # Wikipedia の多言語リンクの正規表現
57
+ # @private
58
+ Link = /<li class="interlanguage-link interwiki-(.+?)"><a href="\/\/(.+?)\.wikipedia\.org\/wiki\/(.+?)" title="(.+?) – /
59
+
60
+ class << self
61
+
62
+ # Wikipedia の連続的な参照を抑制するための遅延時間/秒
63
+ #
64
+ # @return [Numeric]
65
+ #
66
+ attr_accessor :wikipedia_interval
67
+
68
+ # When::Locale Module のグローバルな設定を行う
69
+ #
70
+ # @param [Hash] options 下記の通り
71
+ # @option options [Hash] :alias Locale の読み替えパターンを Hash で指定する。
72
+ # @option options [String] :namespace_foramt 名前空間定義の省略時に名前空間生成に用いる書式
73
+ # @option options [Hash] :unification 漢字の包摂パターンを Hash で指定する。
74
+ # @option options [Numeric] :wikipedia_interval Wikipedia の連続的な参照を抑制するための遅延時間/秒
75
+ #
76
+ # @note
77
+ # :alias の指定がない場合、aliases は DefaultAlias(モジュール定数)と解釈する。
78
+ # :unification の指定がない場合、unifications は DefaultUnification(モジュール定数)と解釈する。
79
+ #
80
+ def _setup_(options={})
81
+ @aliases = options[:alias] || DefaultAlias
82
+ @namespaces = options[:namespace_foramt] || DefaultNamespaces
83
+ @unifications = options[:unification] || DefaultUnification
84
+ @wikipedia_interval = options[:wikipedia_interval]
85
+ end
86
+
87
+ # 設定情報を取得する
88
+ #
89
+ # @return [Hash] 設定情報
90
+ #
91
+ def _setup_info
92
+ {:alias => _alias,
93
+ :namespace_foramt => _namespaces,
94
+ :unification => _unification,
95
+ :wikipedia_interval => @wikipedia_interval}
96
+ end
97
+
98
+ # 特定 locale に対応した文字列の取得
99
+ #
100
+ # @param [String] source もとにする String または M17n
101
+ #
102
+ # @param[String] loc locale の指定 ( lang[-|_]country.encode )
103
+ # [ lang - 言語 ]
104
+ # [ country - 国(省略可) ]
105
+ # [ encode - 文字コード(省略可) ]
106
+ #
107
+ # @return [String] loc に対応した文字列
108
+ #
109
+ # @note source が Hash や Array の場合、その構成要素を変換して返す
110
+ # @note encode は通常大文字だが、大文字/小文字の変換は行わず指定されたまま使用している
111
+ #
112
+ def translate(source, loc='')
113
+ return source unless loc
114
+ case source
115
+ when Hash
116
+ result = {}
117
+ source.each_pair do |key, value|
118
+ result[translate(key, loc)] = translate(value, loc)
119
+ end
120
+ return result
121
+ when Array
122
+ return source.map {|value| translate(value, loc)}
123
+ when Locale
124
+ return source.translate(loc)
125
+ when String
126
+ return source.encode($1) if loc =~ /\.(.+)\z/
127
+ end
128
+ source
129
+ end
130
+
131
+ # 包摂リストに登録されている文字を包摂する
132
+ #
133
+ # @param [When::Locale] source 文字を包摂しようとする国際化文字列
134
+ # @param [String] source 文字を包摂しようとする文字列
135
+ # @param [Regexp] source 文字を包摂しようとする正規表現
136
+ # @param [Hash] pattern 包摂ルール
137
+ #
138
+ # @return [When::Locale] 文字を包摂した国際化文字列
139
+ # @return [String] 文字を包摂した文字列
140
+ # @return [Regexp] 文字を包摂した正規表現
141
+ #
142
+ def ideographic_unification(source, pattern=_unification)
143
+ case source
144
+ when When::Locale
145
+ source.ideographic_unification(pattern)
146
+ when Regexp
147
+ Regexp.compile(ideographic_unification(source.source.encode('UTF-8'), pattern), source.options)
148
+ when String
149
+ source.gsub(/./) do |c|
150
+ pattern[c] ? pattern[c] : c
151
+ end
152
+ else
153
+ source
154
+ end
155
+ end
156
+
157
+ # 文字列で表現された namespace 指定を Hash に変換する
158
+ # @private
159
+ def _namespace(source=nil)
160
+ case source
161
+ when Hash ; source
162
+ when nil ; {}
163
+ when String
164
+ namespace = {}
165
+ source = $1 if (source=~/\A\s*\[?(.+?)\]?\s*\z/m)
166
+ source.split(/[\n\r,]+/).each do |v|
167
+ v.strip!
168
+ next if (v=~/\A#/)
169
+ pair = [''] + v.split(/\s*=\s*/, 2)
170
+ namespace[pair[-2]] = pair[-1]
171
+ end
172
+ namespace
173
+ when When::Parts::Resource::ContentLine
174
+ source.object.names
175
+ else ; raise TypeError, "Irregal Namespace Type: #{source.class}"
176
+ end
177
+ end
178
+
179
+ # locale 指定を Array に変換する
180
+ # @private
181
+ def _locale(source=nil)
182
+ # default の Locale
183
+ return [[nil, '', nil]] unless source
184
+
185
+ # source の配列化
186
+ if source.kind_of?(String)
187
+ source = $1 if (source=~/\A\s*\[?(.+?)\]?\s*\z/m)
188
+ source = source.scan(/((?:[^\\\n\r,]|\\.)+)(?:[\n\r,]+(?!\z))?|[\n\r,]+/m).flatten.map {|token|
189
+ (token||'').gsub(/\\./) {|escape| Escape[escape] || escape}
190
+ }
191
+ end
192
+
193
+ # 各Localeの展開
194
+ source.map {|v|
195
+ if v.kind_of?(String)
196
+ v = v.strip
197
+ next if (v=~/\A#/)
198
+ (v =~ /\A(\*)?(.*?)(?:\s*=\s*(.*))?\z/) ? $~[1..3] : [[nil, '', nil]]
199
+ else
200
+ v
201
+ end
202
+ }.compact
203
+ end
204
+
205
+ # 文字列 [.., .., ..] を分割する
206
+ # @private
207
+ def _split(source)
208
+ line = source.dup
209
+ return [line] unless line =~ /,/
210
+ list = []
211
+ b = d = s = 0
212
+ (source.length-1).downto(0) do |i|
213
+ bs = 0
214
+ (i-1).downto(0) do |k|
215
+ break unless (line[k,1] == '\\')
216
+ bs += 1
217
+ end
218
+ next if (bs[0] == 1)
219
+ case line[i,1]
220
+ when "'" ; s = 1-s if (d == 0)
221
+ when '"' ; d = 1-d if (s == 0)
222
+ when ']','}',')' ; b += 1 if (d+s == 0)
223
+ when '[','{','(' ; b -= 1 if (d+s == 0 && b > 0)
224
+ when ','
225
+ if (b+d+s == 0)
226
+ list.unshift(line[i+1..-1])
227
+ line = i > 0 ? line[0..i-1] : ''
228
+ end
229
+ end
230
+ end
231
+ list.unshift(line)
232
+ end
233
+
234
+ # locale 指定を解析して Hash の値を取り出す
235
+ # @private
236
+ def _hash_value(hash, locale, defaults=['', 'en'])
237
+ locale = locale.sub(/\..*/, '')
238
+ return hash[locale] if hash[locale]
239
+ return _hash_value(hash, _alias[locale], defaults) if _alias[locale]
240
+ language = locale.sub(/-.*/, '')
241
+ return hash[language] if hash[language]
242
+ defaults.each do |default|
243
+ return hash[default] if hash[default]
244
+ end
245
+ return nil
246
+ end
247
+
248
+ # 漢字の包摂パターン
249
+ # @private
250
+ def _unification
251
+ @unifications ||= DefaultUnification
252
+ end
253
+
254
+ # @private
255
+ def _get_locale(locale, access_key)
256
+ return nil unless access_key
257
+ access_key = access_key.split(/\//).map {|key| key =~ /\A[0-9]+\z/ ? key.to_i : key}
258
+ locale = locale.sub(/\..*/, '')
259
+ [locale, locale.sub(/-.*/, '')].each do |loc|
260
+ symbol = ('Locale_' + loc.sub(/-/,'_')).to_sym
261
+ return {locale=>access_key.inject(const_get(symbol)) {|hash, key| hash = hash[key]}} if const_defined?(symbol)
262
+ end
263
+ return nil
264
+ end
265
+
266
+ private
267
+
268
+ # Locale の読み替えパターン
269
+ def _alias
270
+ @aliases ||= DefaultAlias
271
+ end
272
+
273
+ # 名前空間定義の省略時に名前空間生成に用いる書式
274
+ def _namespaces
275
+ @namespaces ||= DefaultNamespaces
276
+ end
277
+
278
+ # wikipedia オブジェクトの生成・参照
279
+ def wikipedia_object(path, options={})
280
+ query = options.delete(:query)
281
+ interval = options.key?(:interval) ? options.delete(:interval) : @wikipedia_interval
282
+ return nil unless Object.const_defined?(:JSON) && path =~ Ref
283
+ _wikipedia_relation(_wikipedia_object(path, $1, $2, query, interval, options), path, query)
284
+ end
285
+
286
+ # wikipedia の読み込み
287
+ def _wikipedia_object(path, locale, file, query, interval, options)
288
+ # 採取済みデータ
289
+ title = URI.decode(file.gsub('_', ' '))
290
+ mode = "".respond_to?(:force_encoding) ? ':utf-8' : ''
291
+ dir = When::Parts::Resource.root_dir + '/data/wikipedia/' + locale
292
+ FileUtils.mkdir_p(dir) unless FileTest.exist?(dir)
293
+
294
+ open("#{dir}/#{file}.json", 'r'+mode) do |source|
295
+ json = JSON.parse(source.read)
296
+ json.update(Hash[*query.split('&').map {|pair| pair.split('=')}.flatten]) if query
297
+ json.key?('names') ?
298
+ When::BasicTypes::M17n.new(json) :
299
+ When::Coordinates::Spatial.new(json)
300
+ end
301
+
302
+ rescue => no_file_error
303
+ # 新しいデータ
304
+ case interval
305
+ when 0
306
+ raise no_file_error
307
+ when Numeric
308
+ if @wikipedia_last_access
309
+ delay = (@wikipedia_last_access + interval.abs - Time.now.to_f).ceil
310
+ sleep(delay) if delay > 0
311
+ end
312
+ end
313
+ contents = nil
314
+ begin
315
+ OpenURI
316
+ source = open(path, 'r'+mode)
317
+ contents = source.read
318
+ ensure
319
+ @wikipedia_last_access = Time.now.to_f
320
+ source.close if source
321
+ end
322
+
323
+ # wikipedia contents
324
+ raise KeyError, 'Article not found: ' + title if contents =~ /<div class="noarticletext">/
325
+
326
+ # word
327
+ word = {
328
+ :label => title,
329
+ :names => {''=>title, locale=>title},
330
+ :link => {''=>path, locale=>path }
331
+ }
332
+ contents.scan(Link) do |link|
333
+ word[:names][$2] = $4
334
+ word[:link ][$2] = "http://#{$2}.wikipedia.org/wiki/#{$3}"
335
+ end
336
+ object = When::BasicTypes::M17n.new(word)
337
+
338
+ # location
339
+ if contents =~ /tools\.wmflabs\.org\/geohack\/geohack\.php\?.+?params=(.+?[NS])_(.+?[EW])/
340
+ location = {
341
+ :label => object
342
+ }
343
+ location[:lat], location[:long] = $~[1..2].map {|pos|
344
+ pos.gsub(/_(\d)[._]/, '_0\1_').sub('.', '_').sub('_', '.').gsub('_', '')
345
+ }
346
+ object = When::Coordinates::Spatial.new(location)
347
+ end
348
+
349
+ # save data
350
+ open("#{dir}/#{file}.json", 'w'+mode) do |source|
351
+ source.write(JSON.dump(object.to_h({:method=>:to_h}).update(options)))
352
+ end
353
+ query ? _wikipedia_object(path, locale, file, query) : object
354
+ end
355
+
356
+ # wikipedia オブジェクトの関連付け
357
+ def _wikipedia_relation(object, path, query)
358
+ code_space = path.sub(/[^\/]+\z/, '')
359
+ if object.kind_of?(When::Coordinates::Spatial)
360
+ object.label._pool['..'] = object
361
+ object._pool[object.label.to_s] = object.label
362
+ object.send(:child=, [object.label])
363
+ object.label.send(:code_space=, code_space)
364
+ else
365
+ object.send(:code_space=, code_space)
366
+ end
367
+ object._pool['..'] = path
368
+ object._pool['..'] += '?' + query if query
369
+ object
370
+ end
371
+ end
372
+
373
+ # ローケール指定時の文字列
374
+ #
375
+ # @return [Hash]
376
+ attr_reader :names
377
+
378
+ # 有効なローケール指定
379
+ #
380
+ # @return [Array<String>]
381
+ attr_reader :keys
382
+
383
+ # 有効な文字列 - additional attribute
384
+ #
385
+ # @return [Array<String>]
386
+ attr_reader :values
387
+
388
+ # 文字列の説明 - additional attribute
389
+ #
390
+ # @return [Hash] { anyURI }
391
+ attr_reader :link
392
+
393
+ # Rails 用 I18n 定義へのアクセス・キー
394
+ #
395
+ # @return [String]
396
+ attr_reader :access_key
397
+ protected :access_key
398
+
399
+ # 特定 locale に対応した文字列の取得
400
+ #
401
+ # @param [String] loc locale の指定 ( lang[-|_]country.encode )
402
+ # [ lang - 言語 ]
403
+ # [ country - 国(省略可) ]
404
+ # [ encode - 文字コード(省略可) ]
405
+ #
406
+ # @return [String] loc に対応した文字列
407
+ #
408
+ def translate(loc='')
409
+ return to_s unless loc
410
+ loc = loc.sub('_', '-')
411
+ lang, code = loc.split(/\./)
412
+ result = _label_value(loc)
413
+ return result if !code || @names.member?(loc)
414
+ return result.encode(code)
415
+ end
416
+ alias :/ :translate
417
+
418
+ # 特定 locale に対応した reference URI の取得
419
+ #
420
+ # @param [String] loc locale の指定
421
+ #
422
+ # @return [String] loc に対応した reference URI
423
+ #
424
+ def reference(loc='')
425
+ loc ||= ''
426
+ return Locale._hash_value(@link, loc.sub('_', '-'))
427
+ end
428
+
429
+ # 部分文字列
430
+ #
431
+ # @param [Range] range String#[] と同様の指定方法で範囲を指定する
432
+ #
433
+ # @return [When::Locale] 指定範囲に対応した部分文字列
434
+ #
435
+ def [](range)
436
+ dup._copy({
437
+ :label => to_s[range],
438
+ :names => @names.keys.inject({}) {|l,k|
439
+ l[k] = @names[k][range]
440
+ l
441
+ }
442
+ })
443
+ end
444
+
445
+ # 文字列の一致
446
+ #
447
+ # @param [String, Regexp] regexp マッチする正規表現
448
+ #
449
+ # @return [Integer] マッチした位置のindex(いずれかの locale でマッチが成功した場合)
450
+ # @return [nil] すべての locale でマッチに失敗した場合
451
+ #
452
+ def =~(regexp)
453
+ @keys.each do |key|
454
+ index = (@names[key] =~ regexp)
455
+ return index if index
456
+ end
457
+ return nil
458
+ end
459
+
460
+ # 部分文字列の位置
461
+ #
462
+ # @param [String] other 部分文字列
463
+ #
464
+ # @return [Integer] 部分文字列の先頭のindex(いずれかの locale で部分文字列を含んだ場合)
465
+ # @return [nil] すべての locale で部分文字列を含まない場合
466
+ #
467
+ def index(other)
468
+ @keys.each do |key|
469
+ index = @names[key].index(Locale.translate(other, key))
470
+ return index if index
471
+ end
472
+ return nil
473
+ end
474
+
475
+ # 文字列の連結
476
+ #
477
+ # @param [String, When::Toos::Locale] other 連結する文字列
478
+ #
479
+ # @return [When::Toos::Locale] 連結された文字列
480
+ #
481
+ def +(other)
482
+ names = {}
483
+ case other
484
+ when Locale
485
+ (@names.keys + other.names.keys).uniq.each do |key|
486
+ names[key] = _label_value(key) + other._label_value(key)
487
+ end
488
+ links = other.link
489
+ else
490
+ @names.keys.each do |key|
491
+ names[key] = _label_value(key) + other.to_s
492
+ end
493
+ links = {}
494
+ end
495
+ return dup._copy({:names=>names, :link=>links.merge(link), :label=>to_s + other.to_s})
496
+ end
497
+
498
+ # 書式指定による文字列化
499
+ #
500
+ # @param [Array<Object>] other 文字列化する Object の Array
501
+ # @param [Array<String>] locale 文字列化を行う locale の指定(デフォルト : すべて)
502
+ #
503
+ # @return [When::Toos::Locale] 文字列化された Object
504
+ #
505
+ def _printf(other, locale=nil)
506
+ # 処理する配列
507
+ terms = other.kind_of?(Array) ? [self] + other : [self, other]
508
+
509
+ # locale key の配列
510
+ if locale == []
511
+ keys = []
512
+ else
513
+ keys = terms.inject([]) {|k,t|
514
+ k += t.keys if t.kind_of?(Locale)
515
+ k
516
+ }.uniq
517
+ if locale
518
+ locale = [locale] unless locale.kind_of?(Array)
519
+ keys = locale | (locale & keys)
520
+ end
521
+ end
522
+ keys << nil if keys.include?('')
523
+
524
+ # names ハッシュ
525
+ names = keys.inject({}) {|l,k|
526
+ l[k] = When::Coordinates::Pair._format(
527
+ (block_given? ? yield(k, *terms) : terms).map {|t|
528
+ t.kind_of?(Locale) ? t.translate(k) : t
529
+ }
530
+ )
531
+ l
532
+ }
533
+
534
+ # link ハッシュ
535
+ links = terms.reverse.inject({}) {|h,t|
536
+ h.update(t.link) if t.kind_of?(Locale)
537
+ h
538
+ }
539
+
540
+ # 生成
541
+ dup._copy({
542
+ :label => keys.include?('') ? names.delete(nil) : (names[''] = names[keys[0]]),
543
+ :names => names,
544
+ :link => links
545
+ })
546
+ end
547
+ alias :% :_printf
548
+
549
+ # ローケールの更新
550
+ #
551
+ # @param [Hash] options 下記の通り
552
+ # @option options [String] カントリーコード 表現文字列
553
+ # @option options [Hash] :link { カントリーコード => 参照URL文字列 }
554
+ # @option options [String] :code_space 規格や辞書を特定するコードスペースのURL文字列
555
+ # @option options [String] :label 代表文字列
556
+ # @note Hashキーはすべて Optional で、存在するもののみ更新します
557
+ #
558
+ # @return [self] 更新された Object
559
+ #
560
+ def update(options={})
561
+ options = options.dup
562
+ @link.update(options.delete(:link)) if (options.key?(:link))
563
+ @code_space = options.delete(:code_space) if (options.key?(:code_space))
564
+ self[0..-1] = options.delete(:label) if (options.key?(:label))
565
+ unless (options.empty?)
566
+ @names.update(options)
567
+ @keys = @names.keys.sort
568
+ @values = @names.values.sort.reverse
569
+ end
570
+ return self
571
+ end
572
+
573
+ # 包摂リストに登録されている文字を包摂する(自己破壊)
574
+ #
575
+ # @param [Hash] pattern 包摂ルール
576
+ #
577
+ # @return [When::Locale] self
578
+ # @private
579
+ def ideographic_unification!(pattern=_unification)
580
+ names = {}
581
+ @names.each_pair do |key, value|
582
+ names[key] = Locale.ideographic_unification(value, pattern)
583
+ end
584
+ @names = names
585
+ @values = @names.values.sort.reverse
586
+ self[0..-1] = Locale.ideographic_unification(self.to_s[0..-1], pattern)
587
+ return self
588
+ end
589
+ protected :ideographic_unification!
590
+
591
+ # 包摂リストに登録されている文字を包摂する(自己保存)
592
+ #
593
+ # @param [Hash] pattern 包摂ルール
594
+ #
595
+ # @return [When::Locale] 包摂結果
596
+ #
597
+ def ideographic_unification(pattern=_unification)
598
+ dup.ideographic_unification!(pattern)
599
+ end
600
+
601
+ # 閏の表記を扱うための書式付整形
602
+ # @private
603
+ def prefix(other, locale=nil)
604
+ return self.dup unless other
605
+ other = m17n(other) unless other.kind_of?(Locale)
606
+ other._printf(self, locale) do |k, *t|
607
+ t[0] = t[0].translate(k)
608
+ t[0] += "%s" unless t[0] =~ /%[-+]?[.\d]*s/
609
+ t
610
+ end
611
+ end
612
+
613
+ protected
614
+
615
+ # @private
616
+ def _copy(options={})
617
+ if options.key?('label') # for JSON
618
+ opt = {}
619
+ options.each_pair do |key, value|
620
+ opt[key.to_sym] = value
621
+ end
622
+ else
623
+ opt = options
624
+ end
625
+
626
+ self[0..-1] = opt[:label] if opt[:label]
627
+ if opt[:names]
628
+ @names = opt[:names]
629
+ @keys = @names.keys.sort
630
+ @values = @names.values.compact.sort.reverse
631
+ end
632
+ @label = opt[:label] if opt[:label]
633
+ @link = opt[:link] if opt[:link]
634
+ @access_key = opt[:access_key] if opt[:access_key]
635
+ @code_space = opt[:code_space] if opt[:code_space]
636
+ return self
637
+ end
638
+
639
+ # @private
640
+ def _copy_all(other)
641
+ _copy({:label => other.to_s,
642
+ :names => other.names,
643
+ :link => other.link,
644
+ :access_key => other.access_key,
645
+ :code_space => other.code_space
646
+ })
647
+ end
648
+
649
+ # locale 指定を解析して @names の値を取り出す
650
+ # @private
651
+ def _label_value(locale)
652
+ label = Locale._hash_value(@names, locale, [])
653
+ return label if label
654
+ foreign = Locale._get_locale(locale, @access_key)
655
+ return @names[''] unless foreign
656
+ english = @names['en'] || @names['']
657
+ addition = english.dup.sub!(/\A#{Locale._get_locale('en', @access_key)['en']}/, '')
658
+ foreign[locale] += addition if addition
659
+ update(foreign)
660
+ return Locale._hash_value(@names, locale)
661
+ end
662
+
663
+ private
664
+
665
+ def _names(names, namespace, default_locale)
666
+
667
+ # names, link の組み立て
668
+ @names = {}
669
+ @link = {}
670
+
671
+ if names.kind_of?(String)
672
+ unless (names=~/\A\s*\[(.+?)\]\s*\z/m)
673
+ names = names.strip
674
+ @names[''] = names
675
+ @keys = ['']
676
+ @values = [names]
677
+ return names
678
+ end
679
+ names = $1.split(/[\n\r,]+/)
680
+ end
681
+
682
+ mark = []
683
+ asterisk = []
684
+ default_locale = default_locale.dup
685
+ names.each do |v|
686
+ v.strip!
687
+ case v
688
+ when '', /\A#/ ;
689
+ when /\A\/(.+)/; @access_key = $1
690
+ when /\A(\*)?(?:([^=%]*?)\s*:)?\s*(.+?)\s*(=\s*([^=]+?(\?.+)?)?)?\z/
691
+ asterisk[0], locale, name, assignment, ref = $~[1..5]
692
+ asterisk[1], locale, default_ref = default_locale.shift unless locale
693
+ locale ||= ''
694
+ ref ||= default_ref unless (assignment)
695
+ ref ||= ''
696
+ mark[0] = locale if asterisk[0]
697
+ mark[1] = locale if asterisk[1]
698
+ mark[2] = locale unless mark[2]
699
+ name = _replacement($1, locale, ($3 || @names['en'] || @names[''])) if name =~ /\A_([A-Z_]+)_(\((.+)\))?\z/
700
+ name.gsub!(/<[0-9A-F]{2}>/i) {|code| code[1..2].to_i(16).chr}
701
+ @names[locale] = name
702
+ if ref =~ /\A(.+):/
703
+ prefix = namespace[$1] || Locale.send(:_namespaces)[$1]
704
+ ref.sub!(/\A.+:/, prefix) if prefix
705
+ end
706
+ ref += name =~ /\A<.+>\z/ ? '%%' + name : '%%<' + name + '>' if ref =~ /[\/#:]\z/
707
+ unless ref == ''
708
+ @link[locale] = _encode(ref)
709
+ # When.logger.info("%s[%s]->%s" % [@names[locale], locale, @link[locale]]) if When.logger
710
+ end
711
+ else ; raise ArgumentError, "Irregal locale format: " + v
712
+ end
713
+ end
714
+ if Locale.wikipedia_interval && Locale.wikipedia_interval <= 0
715
+ ['en', ''].each do |lc|
716
+ if Locale::Ref =~ @link[lc] && $1 == 'en'
717
+ object = Locale.send(:wikipedia_object, @link[lc])
718
+ if object
719
+ @names = object.names.merge(@names)
720
+ @link = object.link.merge(@link)
721
+ end
722
+ break
723
+ end
724
+ end
725
+ end
726
+
727
+ # keys, values の準備
728
+ @keys = @names.keys.sort
729
+ @values = @names.values.sort.reverse
730
+
731
+ # 代表名
732
+ @names[mark[0] || mark[1] || mark[2]]
733
+ end
734
+
735
+ #
736
+ # 英語表記を現地表記に置き換える
737
+ #
738
+ def _replacement(table, locale, name)
739
+ Locale.const_get(table.split('_')[-1])[locale] ?
740
+ Locale.send(table.downcase, name, locale) :
741
+ name
742
+ end
743
+
744
+ # encode URI from patterns %%(...) or %.(...)
745
+ def _encode(source)
746
+ source.gsub(/%.<.+?>/) do |match|
747
+ URI.encode(match[3..-2]).gsub('%', match[1..1])
748
+ end
749
+ end
750
+ end
751
+ end