when_exe 0.4.0 → 0.4.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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