when_exe 0.3.6 → 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
Files changed (117) hide show
  1. checksums.yaml +7 -0
  2. data/README.md +171 -0
  3. data/lib/when_exe.rb +78 -47
  4. data/lib/when_exe/basictypes.rb +752 -747
  5. data/lib/when_exe/calendarnote.rb +805 -801
  6. data/lib/when_exe/calendartypes.rb +1583 -1531
  7. data/lib/when_exe/coordinates.rb +16 -15
  8. data/lib/when_exe/core/duration.rb +114 -110
  9. data/lib/when_exe/core/extension.rb +504 -504
  10. data/lib/when_exe/ephemeris.rb +1917 -1913
  11. data/lib/when_exe/ephemeris/moon.rb +333 -333
  12. data/lib/when_exe/ephemeris/notes.rb +389 -387
  13. data/lib/when_exe/ephemeris/planets.rb +585 -585
  14. data/lib/when_exe/ephemeris/sun.rb +214 -214
  15. data/lib/when_exe/googlecalendar.rb +144 -140
  16. data/lib/when_exe/icalendar.rb +1636 -1636
  17. data/lib/when_exe/inspect.rb +46 -22
  18. data/lib/when_exe/locales/akt.rb +176 -176
  19. data/lib/when_exe/locales/encoding_conversion.rb +134 -126
  20. data/lib/when_exe/locales/iast.rb +90 -90
  21. data/lib/when_exe/locales/locale.rb +750 -746
  22. data/lib/when_exe/locales/transliteration_table.rb +62 -62
  23. data/lib/when_exe/mini_application.rb +307 -305
  24. data/lib/when_exe/parts/enumerator.rb +2 -2
  25. data/lib/when_exe/parts/geometric_complex.rb +397 -397
  26. data/lib/when_exe/parts/method_cash.rb +224 -224
  27. data/lib/when_exe/parts/resource.rb +1069 -1071
  28. data/lib/when_exe/parts/timezone.rb +240 -230
  29. data/lib/when_exe/region/armenian.rb +56 -56
  30. data/lib/when_exe/region/babylonian.rb +405 -0
  31. data/lib/when_exe/region/bahai.rb +146 -146
  32. data/lib/when_exe/region/balinese.rb +622 -622
  33. data/lib/when_exe/region/chinese.rb +95 -25
  34. data/lib/when_exe/region/chinese/calendars.rb +1016 -1016
  35. data/lib/when_exe/region/chinese/epochs.rb +1 -1
  36. data/lib/when_exe/region/chinese/twins.rb +803 -795
  37. data/lib/when_exe/region/christian.rb +824 -824
  38. data/lib/when_exe/region/coptic.rb +106 -87
  39. data/lib/when_exe/region/discordian.rb +225 -225
  40. data/lib/when_exe/region/far_east.rb +188 -188
  41. data/lib/when_exe/region/french.rb +56 -56
  42. data/lib/when_exe/region/geologicalage.rb +639 -639
  43. data/lib/when_exe/region/goddess.rb +58 -58
  44. data/lib/when_exe/region/indian.rb +1254 -1251
  45. data/lib/when_exe/region/iranian.rb +8 -8
  46. data/lib/when_exe/region/islamic.rb +3 -3
  47. data/lib/when_exe/region/japanese.rb +93 -99
  48. data/lib/when_exe/region/japanese/calendars.rb +396 -397
  49. data/lib/when_exe/region/japanese/epochs.rb +26 -26
  50. data/lib/when_exe/region/japanese/nihon_shoki.rb +71 -71
  51. data/lib/when_exe/region/japanese/notes.rb +1383 -1386
  52. data/lib/when_exe/region/japanese/residues.rb +1306 -1306
  53. data/lib/when_exe/region/japanese/twins.rb +225 -225
  54. data/lib/when_exe/region/japanese/weeks.rb +112 -0
  55. data/lib/when_exe/region/javanese.rb +230 -230
  56. data/lib/when_exe/region/jewish.rb +126 -126
  57. data/lib/when_exe/region/korean.rb +378 -378
  58. data/lib/when_exe/region/m17n.rb +114 -113
  59. data/lib/when_exe/region/martian.rb +258 -255
  60. data/lib/when_exe/region/mayan.rb +32 -32
  61. data/lib/when_exe/region/residue.rb +89 -89
  62. data/lib/when_exe/region/roman.rb +36 -24
  63. data/lib/when_exe/region/ryukyu.rb +97 -97
  64. data/lib/when_exe/region/shire.rb +240 -240
  65. data/lib/when_exe/region/soviet.rb +209 -0
  66. data/lib/when_exe/region/symmetry.rb +50 -50
  67. data/lib/when_exe/region/thai.rb +336 -335
  68. data/lib/when_exe/region/tibetan.rb +316 -315
  69. data/lib/when_exe/region/vietnamese.rb +440 -439
  70. data/lib/when_exe/region/weekdate.rb +80 -80
  71. data/lib/when_exe/region/world.rb +175 -175
  72. data/lib/when_exe/region/yerm.rb +14 -14
  73. data/lib/when_exe/region/zoroastrian.rb +203 -203
  74. data/lib/when_exe/timestandard.rb +707 -681
  75. data/lib/when_exe/tmduration.rb +338 -330
  76. data/lib/when_exe/tmobjects.rb +1346 -1325
  77. data/lib/when_exe/tmposition.rb +2115 -2072
  78. data/lib/when_exe/tmreference.rb +1693 -1669
  79. data/lib/when_exe/version.rb +1 -1
  80. data/link_to_online_documents +1 -1
  81. data/test/examples/JapanHolidaysRFC6350.ics +1 -1
  82. data/test/test.rb +67 -61
  83. data/test/test/basictypes.rb +409 -409
  84. data/test/test/calendarnote.rb +86 -69
  85. data/test/test/calendartypes.rb +97 -97
  86. data/test/test/coordinates.rb +396 -396
  87. data/test/test/ephemeris.rb +83 -74
  88. data/test/test/ephemeris/moon.rb +14 -14
  89. data/test/test/ephemeris/planets.rb +14 -14
  90. data/test/test/ephemeris/sun.rb +14 -14
  91. data/test/test/googlecalendar.rb +194 -176
  92. data/test/test/icalendar.rb +867 -858
  93. data/test/test/inspect.rb +117 -117
  94. data/test/test/parts.rb +487 -487
  95. data/test/test/region/balinese.rb +34 -0
  96. data/test/test/region/chinese.rb +218 -206
  97. data/test/test/region/christian.rb +245 -245
  98. data/test/test/region/coptic.rb +27 -27
  99. data/test/test/region/french.rb +33 -33
  100. data/test/test/region/geologicalage.rb +17 -17
  101. data/test/test/region/indian.rb +57 -57
  102. data/test/test/region/iran.rb +54 -54
  103. data/test/test/region/islamic.rb +18 -18
  104. data/test/test/region/japanese.rb +237 -219
  105. data/test/test/region/jewish.rb +61 -61
  106. data/test/test/region/m17n.rb +184 -184
  107. data/test/test/region/mayan.rb +195 -195
  108. data/test/test/region/residue.rb +147 -139
  109. data/test/test/region/thai.rb +116 -116
  110. data/test/test/region/tibetan.rb +30 -30
  111. data/test/test/region/vietnamese.rb +102 -102
  112. data/test/test/region/yerm.rb +146 -146
  113. data/test/test/timestandard.rb +81 -81
  114. data/test/test/tmobjects.rb +328 -328
  115. data/test/test/tmposition.rb +397 -284
  116. data/test/test/tmreference.rb +157 -157
  117. metadata +13 -10
@@ -1,126 +1,134 @@
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
- module When
9
-
10
- #
11
- # エンコーディングの変換
12
- #
13
- module EncodingConversion
14
-
15
- class << self
16
- if String.method_defined?(:encode)
17
- #
18
- # 内部エンコーディング文字列化
19
- #
20
- # @param [Object] source もとにする Object
21
- #
22
- # @return [Object] 内部エンコーディング化した Object
23
- #
24
- # @private
25
- def to_internal_encoding(source)
26
- default_internal = Encoding.default_internal||'UTF-8'
27
- case source
28
- when Locale ; source.to_internal_encoding
29
- when String ; (source.encoding == default_internal) ?
30
- source :
31
- source.encode(default_internal)
32
- when Regexp ; (source.encoding == default_internal) ?
33
- source :
34
- Regexp.new(source.to_s.encode(default_internal))
35
- when Array ; source.map {|e| to_internal_encoding(e)}
36
- when Hash
37
- hash = {}
38
- source.each_pair do |key, value|
39
- hash[to_internal_encoding(key)] = to_internal_encoding(value)
40
- end
41
- hash
42
- else ; source.respond_to?(:to_internal_encoding) ? source.to_internal_encoding : source
43
- end
44
- end
45
-
46
- #
47
- # 外部エンコーディング文字列化
48
- #
49
- # @param [Object] source もとにする Object
50
- #
51
- # @return [Object] 外部エンコーディング化した Object
52
- #
53
- # @private
54
- def to_external_encoding(source)
55
- default_external = Encoding.default_external
56
- case source
57
- when Locale ; source.to_external_encoding
58
- when String ; (source.encoding == default_external) ?
59
- source :
60
- source.encode(default_external)
61
- when Regexp ; (source.encoding == default_external) ?
62
- source :
63
- Regexp.new(source.to_s.encode(default_external))
64
- when Array ; source.map {|e| to_external_encoding(e)}
65
- when Hash
66
- hash = {}
67
- source.each_pair do |key, value|
68
- hash[to_external_encoding(key)] = to_external_encoding(value)
69
- end
70
- hash
71
- else ; source.respond_to?(:to_external_encoding) ? source.to_external_encoding : source
72
- end
73
- end
74
-
75
- else
76
- # 内部エンコーディング文字列化(ダミー)
77
- #
78
- # @param [String] string もとにする String または M17n
79
- #
80
- # @return [String] そのまま返す
81
- #
82
- def to_internal_encoding(string)
83
- string
84
- end
85
- #
86
- # 外部エンコーディング文字列化(ダミー)
87
- #
88
- # @param [String] string もとにする String または M17n
89
- #
90
- # @return [String] そのまま返す
91
- #
92
- def to_external_encoding(string)
93
- string
94
- end
95
- end
96
- end
97
-
98
- #
99
- # 内部エンコーディング文字列化
100
- #
101
- def to_internal_encoding
102
- EncodingConversion.to_internal_encoding(self)
103
- end
104
-
105
- #
106
- # 外部エンコーディング文字列化
107
- #
108
- def to_external_encoding
109
- EncodingConversion.to_external_encoding(self)
110
- end
111
-
112
- #
113
- # 内部エンコーディング文字列化(単項)
114
- #
115
- def +@
116
- to_internal_encoding
117
- end
118
-
119
- #
120
- # 外部エンコーディング文字列化(単項)
121
- #
122
- def -@
123
- to_external_encoding
124
- end
125
- end
126
- end
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
+ module When
9
+
10
+ #
11
+ # エンコーディングの変換
12
+ #
13
+ module EncodingConversion
14
+
15
+ class << self
16
+ if String.method_defined?(:encode)
17
+ #
18
+ # 内部エンコーディング文字列化
19
+ #
20
+ # @param [Object] source もとにする Object
21
+ #
22
+ # @return [Object] 内部エンコーディング化した Object
23
+ #
24
+ # @private
25
+ def to_internal_encoding(source)
26
+ default_internal = Encoding.default_internal||'UTF-8'
27
+ case source
28
+ when Locale ; source.to_internal_encoding
29
+ when String ; case source.encoding
30
+ when default_internal ; source
31
+ when Encoding::ASCII_8BIT ; source.dup.force_encoding(default_internal)
32
+ else ; source.encode(default_internal)
33
+ end
34
+ when Regexp ; case source.encoding
35
+ when default_internal ; source
36
+ when Encoding::ASCII_8BIT ; Regexp.new(source.dup.force_encoding(default_internal))
37
+ else ; Regexp.new(source.to_s.encode(default_internal))
38
+ end
39
+ when Array ; source.map {|e| to_internal_encoding(e)}
40
+ when Hash
41
+ hash = {}
42
+ source.each_pair do |key, value|
43
+ hash[to_internal_encoding(key)] = to_internal_encoding(value)
44
+ end
45
+ hash
46
+ else ; source.respond_to?(:to_internal_encoding) ? source.to_internal_encoding : source
47
+ end
48
+ end
49
+
50
+ #
51
+ # 外部エンコーディング文字列化
52
+ #
53
+ # @param [Object] source もとにする Object
54
+ #
55
+ # @return [Object] 外部エンコーディング化した Object
56
+ #
57
+ # @private
58
+ def to_external_encoding(source)
59
+ default_external = Encoding.default_external
60
+ case source
61
+ when Locale ; source.to_external_encoding
62
+ when String ; case source.encoding
63
+ when default_external ; source
64
+ when Encoding::ASCII_8BIT ; source.dup.force_encoding(default_external)
65
+ else ; source.encode(default_external)
66
+ end
67
+ when Regexp ; case source.encoding
68
+ when default_external ; source
69
+ when Encoding::ASCII_8BIT ; Regexp.new(source.dup.force_encoding(default_external))
70
+ else ; Regexp.new(source.to_s.encode(default_external))
71
+ end
72
+ when Array ; source.map {|e| to_external_encoding(e)}
73
+ when Hash
74
+ hash = {}
75
+ source.each_pair do |key, value|
76
+ hash[to_external_encoding(key)] = to_external_encoding(value)
77
+ end
78
+ hash
79
+ else ; source.respond_to?(:to_external_encoding) ? source.to_external_encoding : source
80
+ end
81
+ end
82
+
83
+ else
84
+ # 内部エンコーディング文字列化(ダミー)
85
+ #
86
+ # @param [String] string もとにする String または M17n
87
+ #
88
+ # @return [String] そのまま返す
89
+ #
90
+ def to_internal_encoding(string)
91
+ string
92
+ end
93
+ #
94
+ # 外部エンコーディング文字列化(ダミー)
95
+ #
96
+ # @param [String] string もとにする String または M17n
97
+ #
98
+ # @return [String] そのまま返す
99
+ #
100
+ def to_external_encoding(string)
101
+ string
102
+ end
103
+ end
104
+ end
105
+
106
+ #
107
+ # 内部エンコーディング文字列化
108
+ #
109
+ def to_internal_encoding
110
+ EncodingConversion.to_internal_encoding(self)
111
+ end
112
+
113
+ #
114
+ # 外部エンコーディング文字列化
115
+ #
116
+ def to_external_encoding
117
+ EncodingConversion.to_external_encoding(self)
118
+ end
119
+
120
+ #
121
+ # 内部エンコーディング文字列化(単項)
122
+ #
123
+ def +@
124
+ to_internal_encoding
125
+ end
126
+
127
+ #
128
+ # 外部エンコーディング文字列化(単項)
129
+ #
130
+ def -@
131
+ to_external_encoding
132
+ end
133
+ end
134
+ end
@@ -1,90 +1,90 @@
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
- _IAST_Part_I = {
14
- "A" => "अ", "a" => "अ",
15
- "Ā" => "आ", "ā" => "आ",
16
- "I" => "इ", "i" => "इ",
17
- "Ī" => "ई", "ī" => "ई",
18
- "U" => "उ", "u" => "उ",
19
- "Ū" => "ऊ", "ū" => "ऊ",
20
- "Ṛ" => "ऋ", "ṛ" => "ऋ",
21
- "Ṝ" => "ॠ", "ṝ" => "ॠ",
22
- "Ḷ" => "ऌ", "ḷ" => "ऌ",
23
- "Ḹ" => "ॡ", "ḹ" => "ॡ",
24
- "E" => "ए", "e" => "ए",
25
- "Ai" => "ऐ", "ai" => "ऐ",
26
- "O" => "ओ", "o" => "ओ",
27
- "Au" => "औ", "au" => "औ",
28
- "Ṃ" => "अं", "ṃ" => "अं",
29
- "Ḥ" => "अः", "ḥ" => "अः",
30
- "'" => "अऽ",
31
-
32
- "K" => "क", "k" => "क",
33
- "C" => "च", "c" => "च",
34
- "Ṭ" => "ट", "ṭ" => "ट",
35
- "T" => "त", "t" => "त",
36
- "P" => "प", "p" => "प",
37
- "Kh" => "ख", "kh" => "ख",
38
- "Ch" => "छ", "ch" => "छ",
39
- "Ṭh" => "ठ", "ṭh" => "ठ",
40
- "Th" => "थ", "th" => "थ",
41
- "Ph" => "फ", "ph" => "फ",
42
- "G" => "ग", "g" => "ग",
43
- "J" => "ज", "j" => "ज",
44
- "Ḍ" => "ड", "ḍ" => "ड",
45
- "D" => "द", "d" => "द",
46
- "B" => "ब", "b" => "ब",
47
- "Gh" => "घ", "gh" => "घ",
48
- "Jh" => "झ", "jh" => "झ",
49
- "Ḍh" => "ढ", "ḍh" => "ढ",
50
- "Dh" => "ध", "dh" => "ध",
51
- "Bh" => "भ", "bh" => "भ",
52
- "Ṅ" => "ङ", "ṅ" => "ङ",
53
- "Ñ" => "ञ", "ñ" => "ञ",
54
- "Ṇ" => "ण", "ṇ" => "ण",
55
- "N" => "न", "n" => "न",
56
- "M" => "म", "m" => "म",
57
- "H" => "ह", "h" => "ह",
58
- "Y" => "य", "y" => "य",
59
- "R" => "र", "r" => "र",
60
- "L" => "ल", "l" => "ल",
61
- "V" => "व", "v" => "व",
62
- "Ś" => "श", "ś" => "श",
63
- "Ṣ" => "ष", "ṣ" => "ष",
64
- "S" => "स", "s" => "स"
65
- }
66
-
67
- _IAST_Part_II = {
68
- "R̥" => "ऋ", "r̥" => "ऋ",
69
- "R̥̄" => "ॠ", "r̥̄" => "ॠ",
70
- "Ē" => "ए", "ē" => "ए",
71
- "Ō" => "ओ", "ō" => "ओ",
72
- "Ṁ" => "अं", "ṁ" => "अं"
73
- }
74
-
75
- #
76
- # IAST: International Alphabet of Sanskrit Transliteration
77
- #
78
- IAST = {
79
- 'hi' => _IAST_Part_I.merge(_IAST_Part_II)
80
- }
81
-
82
- #
83
- # IASTR: International Alphabet of Sanskrit Transliteration - Reverse
84
- #
85
- IASTR = {
86
- 'hi' => _IAST_Part_I.invert
87
- }
88
-
89
- end
90
- end
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
+ _IAST_Part_I = {
14
+ "A" => "अ", "a" => "अ",
15
+ "Ā" => "आ", "ā" => "आ",
16
+ "I" => "इ", "i" => "इ",
17
+ "Ī" => "ई", "ī" => "ई",
18
+ "U" => "उ", "u" => "उ",
19
+ "Ū" => "ऊ", "ū" => "ऊ",
20
+ "Ṛ" => "ऋ", "ṛ" => "ऋ",
21
+ "Ṝ" => "ॠ", "ṝ" => "ॠ",
22
+ "Ḷ" => "ऌ", "ḷ" => "ऌ",
23
+ "Ḹ" => "ॡ", "ḹ" => "ॡ",
24
+ "E" => "ए", "e" => "ए",
25
+ "Ai" => "ऐ", "ai" => "ऐ",
26
+ "O" => "ओ", "o" => "ओ",
27
+ "Au" => "औ", "au" => "औ",
28
+ "Ṃ" => "अं", "ṃ" => "अं",
29
+ "Ḥ" => "अः", "ḥ" => "अः",
30
+ "'" => "अऽ",
31
+
32
+ "K" => "क", "k" => "क",
33
+ "C" => "च", "c" => "च",
34
+ "Ṭ" => "ट", "ṭ" => "ट",
35
+ "T" => "त", "t" => "त",
36
+ "P" => "प", "p" => "प",
37
+ "Kh" => "ख", "kh" => "ख",
38
+ "Ch" => "छ", "ch" => "छ",
39
+ "Ṭh" => "ठ", "ṭh" => "ठ",
40
+ "Th" => "थ", "th" => "थ",
41
+ "Ph" => "फ", "ph" => "फ",
42
+ "G" => "ग", "g" => "ग",
43
+ "J" => "ज", "j" => "ज",
44
+ "Ḍ" => "ड", "ḍ" => "ड",
45
+ "D" => "द", "d" => "द",
46
+ "B" => "ब", "b" => "ब",
47
+ "Gh" => "घ", "gh" => "घ",
48
+ "Jh" => "झ", "jh" => "झ",
49
+ "Ḍh" => "ढ", "ḍh" => "ढ",
50
+ "Dh" => "ध", "dh" => "ध",
51
+ "Bh" => "भ", "bh" => "भ",
52
+ "Ṅ" => "ङ", "ṅ" => "ङ",
53
+ "Ñ" => "ञ", "ñ" => "ञ",
54
+ "Ṇ" => "ण", "ṇ" => "ण",
55
+ "N" => "न", "n" => "न",
56
+ "M" => "म", "m" => "म",
57
+ "H" => "ह", "h" => "ह",
58
+ "Y" => "य", "y" => "य",
59
+ "R" => "र", "r" => "र",
60
+ "L" => "ल", "l" => "ल",
61
+ "V" => "व", "v" => "व",
62
+ "Ś" => "श", "ś" => "श",
63
+ "Ṣ" => "ष", "ṣ" => "ष",
64
+ "S" => "स", "s" => "स"
65
+ }
66
+
67
+ _IAST_Part_II = {
68
+ "R̥" => "ऋ", "r̥" => "ऋ",
69
+ "R̥̄" => "ॠ", "r̥̄" => "ॠ",
70
+ "Ē" => "ए", "ē" => "ए",
71
+ "Ō" => "ओ", "ō" => "ओ",
72
+ "Ṁ" => "अं", "ṁ" => "अं"
73
+ }
74
+
75
+ #
76
+ # IAST: International Alphabet of Sanskrit Transliteration
77
+ #
78
+ IAST = {
79
+ 'hi' => _IAST_Part_I.merge(_IAST_Part_II)
80
+ }
81
+
82
+ #
83
+ # IASTR: International Alphabet of Sanskrit Transliteration - Reverse
84
+ #
85
+ IASTR = {
86
+ 'hi' => _IAST_Part_I.invert
87
+ }
88
+
89
+ end
90
+ end
@@ -1,746 +1,750 @@
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
- # Escape
43
- # @private
44
- Escape = {
45
- "\\\\" => "\\",
46
- "\\n" => "\n",
47
- "\\r" => "\r",
48
- "\\," => ","
49
- }
50
-
51
- # Wikipedia の URL の正規表現
52
- # @private
53
- Ref = /^http:\/\/(.+?)\.wikipedia\.org\/wiki\/([^#]+?)$/
54
-
55
- # Wikipedia の多言語リンクの正規表現
56
- # @private
57
- Link = /<li class="interlanguage-link interwiki-(.+?)"><a href="\/\/(.+?)\.wikipedia\.org\/wiki\/(.+?)" title="(.+?) – /
58
-
59
- class << self
60
-
61
- # Wikipedia の連続的な参照を抑制するための遅延時間/秒
62
- #
63
- # @return [Numeric]
64
- #
65
- attr_accessor :wikipedia_interval
66
-
67
- # When::Locale Module のグローバルな設定を行う
68
- #
69
- # @param [Hash] options 下記の通り
70
- # @option options [Hash] :alias Locale の読み替えパターンを Hash で指定する。
71
- # @option options [String] :namespaces 名前空間定義の省略時に名前空間生成に用いる書式
72
- # @option options [Hash] :unification 漢字の包摂パターンを Hash で指定する。
73
- # @option options [Numeric] :wikipedia_interval Wikipedia の連続的な参照を抑制するための遅延時間/秒
74
- #
75
- # @note
76
- # :alias の指定がない場合、aliases は DefaultAlias(モジュール定数)と解釈する。
77
- # :unification の指定がない場合、unifications は DefaultUnification(モジュール定数)と解釈する。
78
- #
79
- def _setup_(options={})
80
- @aliases = options[:alias] || DefaultAlias
81
- @namespaces = options[:namespaces] || DefaultNamespaces
82
- @unifications = options[:unification] || DefaultUnification
83
- @wikipedia_interval = options[:wikipedia_interval]
84
- end
85
-
86
- # 設定情報を取得する
87
- #
88
- # @return [Hash] 設定情報
89
- #
90
- def _setup_info
91
- {:alias => _alias,
92
- :namespaces => _namespaces,
93
- :unification => _unification,
94
- :wikipedia_interval => @wikipedia_interval}
95
- end
96
-
97
- # 特定 locale に対応した文字列の取得
98
- #
99
- # @param [String] source もとにする String または M17n
100
- #
101
- # @param[String] loc locale の指定 ( lang[-|_]country.encode )
102
- # [ lang - 言語 ]
103
- # [ country - 国(省略可) ]
104
- # [ encode - 文字コード(省略可) ]
105
- #
106
- # @return [String] loc に対応した文字列
107
- #
108
- # @note source が Hash や Array の場合、その構成要素を変換して返す
109
- # @note encode は通常大文字だが、大文字/小文字の変換は行わず指定されたまま使用している
110
- #
111
- def translate(source, loc='')
112
- return source unless loc
113
- case source
114
- when Hash
115
- result = {}
116
- source.each_pair do |key, value|
117
- result[translate(key, loc)] = translate(value, loc)
118
- end
119
- return result
120
- when Array
121
- return source.map {|value| translate(value, loc)}
122
- when Locale
123
- return source.translate(loc)
124
- when String
125
- return source.encode($1) if loc =~ /\.(.+)$/
126
- end
127
- source
128
- end
129
-
130
- # 包摂リストに登録されている文字を包摂する
131
- #
132
- # @param [When::Locale] source 文字を包摂しようとする国際化文字列
133
- # @param [String] source 文字を包摂しようとする文字列
134
- # @param [Regexp] source 文字を包摂しようとする正規表現
135
- # @param [Hash] pattern 包摂ルール
136
- #
137
- # @return [When::Locale] 文字を包摂した国際化文字列
138
- # @return [String] 文字を包摂した文字列
139
- # @return [Regexp] 文字を包摂した正規表現
140
- #
141
- def ideographic_unification(source, pattern=_unification)
142
- case source
143
- when When::Locale
144
- source.ideographic_unification(pattern)
145
- when Regexp
146
- Regexp.compile(ideographic_unification(source.source.encode('UTF-8'), pattern), source.options)
147
- when String
148
- source.gsub(/./) do |c|
149
- pattern[c] ? pattern[c] : c
150
- end
151
- else
152
- source
153
- end
154
- end
155
-
156
- # 文字列で表現された namespace 指定を Hash に変換する
157
- # @private
158
- def _namespace(source=nil)
159
- case source
160
- when Hash ; source
161
- when nil ; {}
162
- when String
163
- namespace = {}
164
- source = $1 if (source=~/\A\s*\[?(.+?)\]?\s*\z/m)
165
- source.split(/[\n\r,]+/).each do |v|
166
- v.strip!
167
- next if (v=~/^#/)
168
- pair = [''] + v.split(/\s*=\s*/, 2)
169
- namespace[pair[-2]] = pair[-1]
170
- end
171
- namespace
172
- when When::Parts::Resource::ContentLine
173
- source.object.names
174
- else ; raise TypeError, "Irregal Namespace Type: #{source.class}"
175
- end
176
- end
177
-
178
- # locale 指定を Array に変換する
179
- # @private
180
- def _locale(source=nil)
181
- # default の Locale
182
- return [[nil, '', nil]] unless source
183
-
184
- # source の配列化
185
- if source.kind_of?(String)
186
- source = $1 if (source=~/\A\s*\[?(.+?)\]?\s*\z/m)
187
- source = source.scan(/((?:[^\\\n\r,]|\\.)+)(?:[\n\r,]+(?!\z))?|[\n\r,]+/m).flatten.map {|token|
188
- (token||'').gsub(/\\./) {|escape| Escape[escape] || escape}
189
- }
190
- end
191
-
192
- # 各Localeの展開
193
- source.map {|v|
194
- if v.kind_of?(String)
195
- v = v.strip
196
- next if (v=~/^#/)
197
- (v =~ /^(\*)?(.*?)(?:\s*=\s*(.*))?$/) ? $~[1..3] : [[nil, '', nil]]
198
- else
199
- v
200
- end
201
- }.compact
202
- end
203
-
204
- # 文字列 [.., .., ..] を分割する
205
- # @private
206
- def _split(source)
207
- line = source.dup
208
- return [line] unless line =~ /,/
209
- list = []
210
- b = d = s = 0
211
- (source.length-1).downto(0) do |i|
212
- bs = 0
213
- (i-1).downto(0) do |k|
214
- break unless (line[k,1] == '\\')
215
- bs += 1
216
- end
217
- next if (bs[0] == 1)
218
- case line[i,1]
219
- when "'" ; s = 1-s if (d == 0)
220
- when '"' ; d = 1-d if (s == 0)
221
- when ']','}',')' ; b += 1 if (d+s == 0)
222
- when '[','{','(' ; b -= 1 if (d+s == 0 && b > 0)
223
- when ','
224
- if (b+d+s == 0)
225
- list.unshift(line[i+1..-1])
226
- line = i > 0 ? line[0..i-1] : ''
227
- end
228
- end
229
- end
230
- list.unshift(line)
231
- end
232
-
233
- # locale 指定を解析して Hash の値を取り出す
234
- # @private
235
- def _hash_value(hash, locale, defaults=['', 'en'])
236
- locale = locale.sub(/\..*/, '')
237
- return hash[locale] if hash[locale]
238
- return _hash_value(hash, _alias[locale], defaults) if _alias[locale]
239
- language = locale.sub(/-.*/, '')
240
- return hash[language] if hash[language]
241
- defaults.each do |default|
242
- return hash[default] if hash[default]
243
- end
244
- return nil
245
- end
246
-
247
- # 漢字の包摂パターン
248
- # @private
249
- def _unification
250
- @unifications ||= DefaultUnification
251
- end
252
-
253
- # @private
254
- def _get_locale(locale, access_key)
255
- return nil unless access_key
256
- access_key = access_key.split(/\//).map {|key| key =~ /^[0-9]+$/ ? key.to_i : key}
257
- locale = locale.sub(/\..*/, '')
258
- [locale, locale.sub(/-.*/, '')].each do |loc|
259
- symbol = ('Locale_' + loc.sub(/-/,'_')).to_sym
260
- return {loc=>access_key.inject(const_get(symbol)) {|hash, key| hash = hash[key]}} if const_defined?(symbol)
261
- end
262
- return nil
263
- end
264
-
265
- private
266
-
267
- # Locale の読み替えパターン
268
- def _alias
269
- @aliases ||= DefaultAlias
270
- end
271
-
272
- # 名前空間定義の省略時に名前空間生成に用いる書式
273
- def _namespaces
274
- @namespaces ||= DefaultNamespaces
275
- end
276
-
277
- # wikipedia オブジェクトの生成・参照
278
- def wikipedia_object(path, options={})
279
- query = options.delete(:query)
280
- interval = options.key?(:interval) ? options.delete(:interval) : @wikipedia_interval
281
- return nil unless Object.const_defined?(:JSON) && path =~ Ref
282
- _wikipedia_relation(_wikipedia_object(path, $~[1], $~[2], query, interval, options), path, query)
283
- end
284
-
285
- # wikipedia の読み込み
286
- def _wikipedia_object(path, locale, file, query, interval, options)
287
- # 採取済みデータ
288
- title = URI.decode(file.gsub('_', ' '))
289
- mode = "".respond_to?(:force_encoding) ? ':utf-8' : ''
290
- dir = When::Parts::Resource.root_dir + '/data/wikipedia/' + locale
291
- FileUtils.mkdir_p(dir) unless FileTest.exist?(dir)
292
-
293
- open("#{dir}/#{file}.json", 'r'+mode) do |source|
294
- json = JSON.parse(source.read)
295
- json.update(Hash[*query.split('&').map {|pair| pair.split('=')}.flatten]) if query
296
- json.key?('names') ?
297
- When::BasicTypes::M17n.new(json) :
298
- When::Coordinates::Spatial.new(json)
299
- end
300
-
301
- rescue => no_file_error
302
- # 新しいデータ
303
- case interval
304
- when 0
305
- raise no_file_error
306
- when Numeric
307
- if @wikipedia_last_access
308
- delay = (@wikipedia_last_access + interval.abs - Time.now.to_f).ceil
309
- sleep(delay) if delay > 0
310
- end
311
- end
312
- contents = nil
313
- begin
314
- OpenURI
315
- source = open(path, 'r'+mode)
316
- contents = source.read
317
- ensure
318
- @wikipedia_last_access = Time.now.to_f
319
- source.close if source
320
- end
321
-
322
- # wikipedia contents
323
- raise KeyError, 'Article not found: ' + title if contents =~ /<div class="noarticletext">/
324
-
325
- # word
326
- word = {
327
- :label => title,
328
- :names => {''=>title, locale=>title},
329
- :link => {''=>path, locale=>path }
330
- }
331
- contents.scan(Link) do |link|
332
- word[:names][$~[1]] = $~[4]
333
- word[:link ][$~[1]] = "http://#{$~[1]}.wikipedia.org/wiki/#{$~[3]}"
334
- end
335
- object = When::BasicTypes::M17n.new(word)
336
-
337
- # location
338
- if contents =~ /tools\.wmflabs\.org\/geohack\/geohack\.php\?.+?params=(.+?[NS])_(.+?[EW])/
339
- location = {
340
- :label => object
341
- }
342
- location[:lat], location[:long] = $~[1..2].map {|pos|
343
- pos.gsub(/_(\d)[._]/, '_0\1_').sub('.', '_').sub('_', '.').gsub('_', '')
344
- }
345
- object = When::Coordinates::Spatial.new(location)
346
- end
347
-
348
- # save data
349
- open("#{dir}/#{file}.json", 'w'+mode) do |source|
350
- source.write(JSON.dump(object.to_h({:method=>:to_h}).update(options)))
351
- end
352
- query ? _wikipedia_object(path, locale, file, query) : object
353
- end
354
-
355
- # wikipedia オブジェクトの関連付け
356
- def _wikipedia_relation(object, path, query)
357
- code_space = path.sub(/[^\/]+$/, '')
358
- if object.kind_of?(When::Coordinates::Spatial)
359
- object.label._pool['..'] = object
360
- object._pool[object.label.to_s] = object.label
361
- object.send(:child=, [object.label])
362
- object.label.send(:code_space=, code_space)
363
- else
364
- object.send(:code_space=, code_space)
365
- end
366
- object._pool['..'] = path
367
- object._pool['..'] += '?' + query if query
368
- object
369
- end
370
- end
371
-
372
- # ローケール指定時の文字列
373
- #
374
- # @return [Hash]
375
- attr_reader :names
376
-
377
- # 有効なローケール指定
378
- #
379
- # @return [Array<String>]
380
- attr_reader :keys
381
-
382
- # 有効な文字列 - additional attribute
383
- #
384
- # @return [Array<String>]
385
- attr_reader :values
386
-
387
- # 文字列の説明 - additional attribute
388
- #
389
- # @return [Hash] { anyURI }
390
- attr_reader :link
391
-
392
- # Rails 用 I18n 定義へのアクセス・キー
393
- #
394
- # @return [String]
395
- attr_reader :access_key
396
- protected :access_key
397
-
398
- # 特定 locale に対応した文字列の取得
399
- #
400
- # @param [String] loc locale の指定 ( lang[-|_]country.encode )
401
- # [ lang - 言語 ]
402
- # [ country - 国(省略可) ]
403
- # [ encode - 文字コード(省略可) ]
404
- #
405
- # @return [String] loc に対応した文字列
406
- #
407
- def translate(loc='')
408
- return to_s unless loc
409
- loc = loc.sub('_', '-')
410
- lang, code = loc.split(/\./)
411
- result = _label_value(loc)
412
- return result if !code || @names.member?(loc)
413
- return result.encode(code)
414
- end
415
- alias :/ :translate
416
-
417
- # 特定 locale に対応した reference URI の取得
418
- #
419
- # @param [String] loc locale の指定
420
- #
421
- # @return [String] loc に対応した reference URI
422
- #
423
- def reference(loc='')
424
- loc ||= ''
425
- return Locale._hash_value(@link, loc.sub('_', '-'))
426
- end
427
-
428
- # 部分文字列
429
- #
430
- # @param [Range] range String#[] と同様の指定方法で範囲を指定する
431
- #
432
- # @return [When::Locale] 指定範囲に対応した部分文字列
433
- #
434
- def [](range)
435
- dup._copy({
436
- :label => to_s[range],
437
- :names => @names.keys.inject({}) {|l,k|
438
- l[k] = @names[k][range]
439
- l
440
- }
441
- })
442
- end
443
-
444
- # 文字列の一致
445
- #
446
- # @param [String, Regexp] regexp マッチする正規表現
447
- #
448
- # @return [Integer] マッチした位置のindex(いずれかの locale でマッチが成功した場合)
449
- # @return [nil] すべての locale でマッチに失敗した場合
450
- #
451
- def =~(regexp)
452
- @keys.each do |key|
453
- index = (@names[key] =~ regexp)
454
- return index if index
455
- end
456
- return nil
457
- end
458
-
459
- # 部分文字列の位置
460
- #
461
- # @param [String] other 部分文字列
462
- #
463
- # @return [Integer] 部分文字列の先頭のindex(いずれかの locale で部分文字列を含んだ場合)
464
- # @return [nil] すべての locale で部分文字列を含まない場合
465
- #
466
- def index(other)
467
- @keys.each do |key|
468
- index = @names[key].index(Locale.translate(other, key))
469
- return index if index
470
- end
471
- return nil
472
- end
473
-
474
- # 文字列の連結
475
- #
476
- # @param [String, When::Toos::Locale] other 連結する文字列
477
- #
478
- # @return [When::Toos::Locale] 連結された文字列
479
- #
480
- def +(other)
481
- names = {}
482
- case other
483
- when Locale
484
- (@names.keys + other.names.keys).uniq.each do |key|
485
- names[key] = _label_value(key) + other._label_value(key)
486
- end
487
- links = other.link
488
- else
489
- @names.keys.each do |key|
490
- names[key] = _label_value(key) + other.to_s
491
- end
492
- links = {}
493
- end
494
- return dup._copy({:names=>names, :link=>links.merge(link), :label=>to_s + other.to_s})
495
- end
496
-
497
- # 書式指定による文字列化
498
- #
499
- # @param [Array<Object>] other 文字列化する Object の Array
500
- # @param [Array<String>] locale 文字列化を行う locale の指定(デフォルト : すべて)
501
- #
502
- # @return [When::Toos::Locale] 文字列化された Object
503
- #
504
- def _printf(other, locale=nil)
505
- # 処理する配列
506
- terms = other.kind_of?(Array) ? [self] + other : [self, other]
507
-
508
- # locale key の配列
509
- if locale == []
510
- keys = []
511
- else
512
- keys = terms.inject([]) {|k,t|
513
- k += t.keys if t.kind_of?(Locale)
514
- k
515
- }.uniq
516
- if locale
517
- locale = [locale] unless locale.kind_of?(Array)
518
- keys = locale | (locale & keys)
519
- end
520
- end
521
- keys << nil if keys.include?('')
522
-
523
- # names ハッシュ
524
- names = keys.inject({}) {|l,k|
525
- l[k] = When::Coordinates::Pair._format(
526
- (block_given? ? yield(k, *terms) : terms).map {|t|
527
- t.kind_of?(Locale) ? t.translate(k) : t
528
- }
529
- )
530
- l
531
- }
532
-
533
- # link ハッシュ
534
- links = terms.reverse.inject({}) {|h,t|
535
- h.update(t.link) if t.kind_of?(Locale)
536
- h
537
- }
538
-
539
- # 生成
540
- dup._copy({
541
- :label => keys.include?('') ? names.delete(nil) : (names[''] = names[keys[0]]),
542
- :names => names,
543
- :link => links
544
- })
545
- end
546
- alias :% :_printf
547
-
548
- # ローケールの更新
549
- #
550
- # @param [Hash] options 下記の通り
551
- # @option options [String] カントリーコード 表現文字列
552
- # @option options [Hash] :link { カントリーコード => 参照URL文字列 }
553
- # @option options [String] :code_space 規格や辞書を特定するコードスペースのURL文字列
554
- # @option options [String] :label 代表文字列
555
- # @note Hashキーはすべて Optional で、存在するもののみ更新します
556
- #
557
- # @return [self] 更新された Object
558
- #
559
- def update(options={})
560
- options = options.dup
561
- @link.update(options.delete(:link)) if (options.key?(:link))
562
- @code_space = options.delete(:code_space) if (options.key?(:code_space))
563
- self[0..-1] = options.delete(:label) if (options.key?(:label))
564
- unless (options.empty?)
565
- @names.update(options)
566
- @keys = @names.keys.sort
567
- @values = @names.values.sort.reverse
568
- end
569
- return self
570
- end
571
-
572
- # 包摂リストに登録されている文字を包摂する(自己破壊)
573
- #
574
- # @param [Hash] pattern 包摂ルール
575
- #
576
- # @return [When::Locale] self
577
- # @private
578
- def ideographic_unification!(pattern=_unification)
579
- names = {}
580
- @names.each_pair do |key, value|
581
- names[key] = Locale.ideographic_unification(value, pattern)
582
- end
583
- @names = names
584
- @values = @names.values.sort.reverse
585
- self[0..-1] = Locale.ideographic_unification(self.to_s[0..-1], pattern)
586
- return self
587
- end
588
- protected :ideographic_unification!
589
-
590
- # 包摂リストに登録されている文字を包摂する(自己保存)
591
- #
592
- # @param [Hash] pattern 包摂ルール
593
- #
594
- # @return [When::Locale] 包摂結果
595
- #
596
- def ideographic_unification(pattern=_unification)
597
- dup.ideographic_unification!(pattern)
598
- end
599
-
600
- # 閏の表記を扱うための書式付整形
601
- # @private
602
- def prefix(other, locale=nil)
603
- return self.dup unless other
604
- other = m17n(other) unless other.kind_of?(Locale)
605
- other._printf(self, locale) do |k, *t|
606
- t[0] = t[0].translate(k)
607
- t[0] += "%s" unless t[0] =~ /%[-+]?[.\d]*s/
608
- t
609
- end
610
- end
611
-
612
- protected
613
-
614
- # @private
615
- def _copy(options={})
616
- if options.key?('label') # for JSON
617
- opt = {}
618
- options.each_pair do |key, value|
619
- opt[key.to_sym] = value
620
- end
621
- else
622
- opt = options
623
- end
624
-
625
- self[0..-1] = opt[:label] if opt[:label]
626
- if opt[:names]
627
- @names = opt[:names]
628
- @keys = @names.keys.sort
629
- @values = @names.values.compact.sort.reverse
630
- end
631
- @label = opt[:label] if opt[:label]
632
- @link = opt[:link] if opt[:link]
633
- @access_key = opt[:access_key] if opt[:access_key]
634
- @code_space = opt[:code_space] if opt[:code_space]
635
- return self
636
- end
637
-
638
- # @private
639
- def _copy_all(other)
640
- _copy({:label => other.to_s,
641
- :names => other.names,
642
- :link => other.link,
643
- :access_key => other.access_key,
644
- :code_space => other.code_space
645
- })
646
- end
647
-
648
- # locale 指定を解析して @names の値を取り出す
649
- # @private
650
- def _label_value(locale)
651
- label = Locale._hash_value(@names, locale, [])
652
- return label if label
653
- foreign = Locale._get_locale(locale, @access_key)
654
- return @names[''] unless foreign
655
- english = @names['en'] || @names['']
656
- addition = english.dup.sub!(/^#{Locale._get_locale('en', @access_key)['en']}/, '')
657
- foreign[locale] += addition if addition
658
- update(foreign)
659
- return Locale._hash_value(@names, locale)
660
- end
661
-
662
- private
663
-
664
- def _names(names, namespace, default_locale)
665
-
666
- # names, link の組み立て
667
- @names = {}
668
- @link = {}
669
-
670
- if names.kind_of?(String)
671
- unless (names=~/\A\s*\[(.+?)\]\s*\z/m)
672
- names = names.strip
673
- @names[''] = names
674
- @keys = ['']
675
- @values = [names]
676
- return names
677
- end
678
- names = $1.split(/[\n\r,]+/)
679
- end
680
-
681
- mark = []
682
- asterisk = []
683
- default_locale = default_locale.dup
684
- names.each do |v|
685
- v.strip!
686
- case v
687
- when '', /^#/ ;
688
- when /^\/(.+)/; @access_key = $1
689
- when /^(\*)?(?:([^=%]*?)\s*:)?\s*(.+?)\s*(=\s*([^=]+?(\?.+)?)?)?$/
690
- asterisk[0], locale, name, assignment, ref = $~[1..5]
691
- asterisk[1], locale, default_ref = default_locale.shift unless locale
692
- locale ||= ''
693
- ref ||= default_ref unless (assignment)
694
- ref ||= ''
695
- mark[0] = locale if asterisk[0]
696
- mark[1] = locale if asterisk[1]
697
- mark[2] = locale unless mark[2]
698
- name = _replacement($1, locale, ($3 || @names['en'] || @names[''])) if name =~ /^_([A-Z_]+)_(\((.+)\))?$/
699
- @names[locale] = name
700
- if ref =~ /^(.+):/
701
- prefix = namespace[$1] || Locale.send(:_namespaces)[$1]
702
- ref.sub!(/^.+:/, prefix) if prefix
703
- end
704
- ref += '%%<' + name + '>' if ref =~ /[\/#:]$/
705
- @link[locale] = _encode(ref) unless ref == ''
706
- else ; raise ArgumentError, "Irregal locale format: " + v
707
- end
708
- end
709
- if Locale.wikipedia_interval && Locale.wikipedia_interval <= 0
710
- ['en', ''].each do |lc|
711
- if Locale::Ref =~ @link[lc] && $~[1] == 'en'
712
- object = Locale.send(:wikipedia_object, @link[lc])
713
- if object
714
- @names = object.names.merge(@names)
715
- @link = object.link.merge(@link)
716
- end
717
- break
718
- end
719
- end
720
- end
721
-
722
- # keys, values の準備
723
- @keys = @names.keys.sort
724
- @values = @names.values.sort.reverse
725
-
726
- # 代表名
727
- @names[mark[0] || mark[1] || mark[2]]
728
- end
729
-
730
- #
731
- # 英語表記を現地表記に置き換える
732
- #
733
- def _replacement(table, locale, name)
734
- Locale.const_get(table.split('_')[-1])[locale] ?
735
- Locale.send(table.downcase, name, locale) :
736
- name
737
- end
738
-
739
- # encode URI from patterns %%(...) or %.(...)
740
- def _encode(source)
741
- source.gsub(/%.<.+?>/) do |match|
742
- URI.encode(match[3..-2]).gsub('%', match[1..1])
743
- end
744
- end
745
- end
746
- end
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
+ # Escape
43
+ # @private
44
+ Escape = {
45
+ "\\\\" => "\\",
46
+ "\\n" => "\n",
47
+ "\\r" => "\r",
48
+ "\\," => ","
49
+ }
50
+
51
+ # Wikipedia の URL の正規表現
52
+ # @private
53
+ Ref = /\Ahttp:\/\/(.+?)\.wikipedia\.org\/wiki\/([^#]+?)\z/
54
+
55
+ # Wikipedia の多言語リンクの正規表現
56
+ # @private
57
+ Link = /<li class="interlanguage-link interwiki-(.+?)"><a href="\/\/(.+?)\.wikipedia\.org\/wiki\/(.+?)" title="(.+?) – /
58
+
59
+ class << self
60
+
61
+ # Wikipedia の連続的な参照を抑制するための遅延時間/秒
62
+ #
63
+ # @return [Numeric]
64
+ #
65
+ attr_accessor :wikipedia_interval
66
+
67
+ # When::Locale Module のグローバルな設定を行う
68
+ #
69
+ # @param [Hash] options 下記の通り
70
+ # @option options [Hash] :alias Locale の読み替えパターンを Hash で指定する。
71
+ # @option options [String] :namespace_foramt 名前空間定義の省略時に名前空間生成に用いる書式
72
+ # @option options [Hash] :unification 漢字の包摂パターンを Hash で指定する。
73
+ # @option options [Numeric] :wikipedia_interval Wikipedia の連続的な参照を抑制するための遅延時間/秒
74
+ #
75
+ # @note
76
+ # :alias の指定がない場合、aliases は DefaultAlias(モジュール定数)と解釈する。
77
+ # :unification の指定がない場合、unifications は DefaultUnification(モジュール定数)と解釈する。
78
+ #
79
+ def _setup_(options={})
80
+ @aliases = options[:alias] || DefaultAlias
81
+ @namespaces = options[:namespace_foramt] || DefaultNamespaces
82
+ @unifications = options[:unification] || DefaultUnification
83
+ @wikipedia_interval = options[:wikipedia_interval]
84
+ end
85
+
86
+ # 設定情報を取得する
87
+ #
88
+ # @return [Hash] 設定情報
89
+ #
90
+ def _setup_info
91
+ {:alias => _alias,
92
+ :namespace_foramt => _namespaces,
93
+ :unification => _unification,
94
+ :wikipedia_interval => @wikipedia_interval}
95
+ end
96
+
97
+ # 特定 locale に対応した文字列の取得
98
+ #
99
+ # @param [String] source もとにする String または M17n
100
+ #
101
+ # @param[String] loc locale の指定 ( lang[-|_]country.encode )
102
+ # [ lang - 言語 ]
103
+ # [ country - 国(省略可) ]
104
+ # [ encode - 文字コード(省略可) ]
105
+ #
106
+ # @return [String] loc に対応した文字列
107
+ #
108
+ # @note source が Hash や Array の場合、その構成要素を変換して返す
109
+ # @note encode は通常大文字だが、大文字/小文字の変換は行わず指定されたまま使用している
110
+ #
111
+ def translate(source, loc='')
112
+ return source unless loc
113
+ case source
114
+ when Hash
115
+ result = {}
116
+ source.each_pair do |key, value|
117
+ result[translate(key, loc)] = translate(value, loc)
118
+ end
119
+ return result
120
+ when Array
121
+ return source.map {|value| translate(value, loc)}
122
+ when Locale
123
+ return source.translate(loc)
124
+ when String
125
+ return source.encode($1) if loc =~ /\.(.+)\z/
126
+ end
127
+ source
128
+ end
129
+
130
+ # 包摂リストに登録されている文字を包摂する
131
+ #
132
+ # @param [When::Locale] source 文字を包摂しようとする国際化文字列
133
+ # @param [String] source 文字を包摂しようとする文字列
134
+ # @param [Regexp] source 文字を包摂しようとする正規表現
135
+ # @param [Hash] pattern 包摂ルール
136
+ #
137
+ # @return [When::Locale] 文字を包摂した国際化文字列
138
+ # @return [String] 文字を包摂した文字列
139
+ # @return [Regexp] 文字を包摂した正規表現
140
+ #
141
+ def ideographic_unification(source, pattern=_unification)
142
+ case source
143
+ when When::Locale
144
+ source.ideographic_unification(pattern)
145
+ when Regexp
146
+ Regexp.compile(ideographic_unification(source.source.encode('UTF-8'), pattern), source.options)
147
+ when String
148
+ source.gsub(/./) do |c|
149
+ pattern[c] ? pattern[c] : c
150
+ end
151
+ else
152
+ source
153
+ end
154
+ end
155
+
156
+ # 文字列で表現された namespace 指定を Hash に変換する
157
+ # @private
158
+ def _namespace(source=nil)
159
+ case source
160
+ when Hash ; source
161
+ when nil ; {}
162
+ when String
163
+ namespace = {}
164
+ source = $1 if (source=~/\A\s*\[?(.+?)\]?\s*\z/m)
165
+ source.split(/[\n\r,]+/).each do |v|
166
+ v.strip!
167
+ next if (v=~/\A#/)
168
+ pair = [''] + v.split(/\s*=\s*/, 2)
169
+ namespace[pair[-2]] = pair[-1]
170
+ end
171
+ namespace
172
+ when When::Parts::Resource::ContentLine
173
+ source.object.names
174
+ else ; raise TypeError, "Irregal Namespace Type: #{source.class}"
175
+ end
176
+ end
177
+
178
+ # locale 指定を Array に変換する
179
+ # @private
180
+ def _locale(source=nil)
181
+ # default の Locale
182
+ return [[nil, '', nil]] unless source
183
+
184
+ # source の配列化
185
+ if source.kind_of?(String)
186
+ source = $1 if (source=~/\A\s*\[?(.+?)\]?\s*\z/m)
187
+ source = source.scan(/((?:[^\\\n\r,]|\\.)+)(?:[\n\r,]+(?!\z))?|[\n\r,]+/m).flatten.map {|token|
188
+ (token||'').gsub(/\\./) {|escape| Escape[escape] || escape}
189
+ }
190
+ end
191
+
192
+ # 各Localeの展開
193
+ source.map {|v|
194
+ if v.kind_of?(String)
195
+ v = v.strip
196
+ next if (v=~/\A#/)
197
+ (v =~ /\A(\*)?(.*?)(?:\s*=\s*(.*))?\z/) ? $~[1..3] : [[nil, '', nil]]
198
+ else
199
+ v
200
+ end
201
+ }.compact
202
+ end
203
+
204
+ # 文字列 [.., .., ..] を分割する
205
+ # @private
206
+ def _split(source)
207
+ line = source.dup
208
+ return [line] unless line =~ /,/
209
+ list = []
210
+ b = d = s = 0
211
+ (source.length-1).downto(0) do |i|
212
+ bs = 0
213
+ (i-1).downto(0) do |k|
214
+ break unless (line[k,1] == '\\')
215
+ bs += 1
216
+ end
217
+ next if (bs[0] == 1)
218
+ case line[i,1]
219
+ when "'" ; s = 1-s if (d == 0)
220
+ when '"' ; d = 1-d if (s == 0)
221
+ when ']','}',')' ; b += 1 if (d+s == 0)
222
+ when '[','{','(' ; b -= 1 if (d+s == 0 && b > 0)
223
+ when ','
224
+ if (b+d+s == 0)
225
+ list.unshift(line[i+1..-1])
226
+ line = i > 0 ? line[0..i-1] : ''
227
+ end
228
+ end
229
+ end
230
+ list.unshift(line)
231
+ end
232
+
233
+ # locale 指定を解析して Hash の値を取り出す
234
+ # @private
235
+ def _hash_value(hash, locale, defaults=['', 'en'])
236
+ locale = locale.sub(/\..*/, '')
237
+ return hash[locale] if hash[locale]
238
+ return _hash_value(hash, _alias[locale], defaults) if _alias[locale]
239
+ language = locale.sub(/-.*/, '')
240
+ return hash[language] if hash[language]
241
+ defaults.each do |default|
242
+ return hash[default] if hash[default]
243
+ end
244
+ return nil
245
+ end
246
+
247
+ # 漢字の包摂パターン
248
+ # @private
249
+ def _unification
250
+ @unifications ||= DefaultUnification
251
+ end
252
+
253
+ # @private
254
+ def _get_locale(locale, access_key)
255
+ return nil unless access_key
256
+ access_key = access_key.split(/\//).map {|key| key =~ /\A[0-9]+\z/ ? key.to_i : key}
257
+ locale = locale.sub(/\..*/, '')
258
+ [locale, locale.sub(/-.*/, '')].each do |loc|
259
+ symbol = ('Locale_' + loc.sub(/-/,'_')).to_sym
260
+ return {locale=>access_key.inject(const_get(symbol)) {|hash, key| hash = hash[key]}} if const_defined?(symbol)
261
+ end
262
+ return nil
263
+ end
264
+
265
+ private
266
+
267
+ # Locale の読み替えパターン
268
+ def _alias
269
+ @aliases ||= DefaultAlias
270
+ end
271
+
272
+ # 名前空間定義の省略時に名前空間生成に用いる書式
273
+ def _namespaces
274
+ @namespaces ||= DefaultNamespaces
275
+ end
276
+
277
+ # wikipedia オブジェクトの生成・参照
278
+ def wikipedia_object(path, options={})
279
+ query = options.delete(:query)
280
+ interval = options.key?(:interval) ? options.delete(:interval) : @wikipedia_interval
281
+ return nil unless Object.const_defined?(:JSON) && path =~ Ref
282
+ _wikipedia_relation(_wikipedia_object(path, $1, $2, query, interval, options), path, query)
283
+ end
284
+
285
+ # wikipedia の読み込み
286
+ def _wikipedia_object(path, locale, file, query, interval, options)
287
+ # 採取済みデータ
288
+ title = URI.decode(file.gsub('_', ' '))
289
+ mode = "".respond_to?(:force_encoding) ? ':utf-8' : ''
290
+ dir = When::Parts::Resource.root_dir + '/data/wikipedia/' + locale
291
+ FileUtils.mkdir_p(dir) unless FileTest.exist?(dir)
292
+
293
+ open("#{dir}/#{file}.json", 'r'+mode) do |source|
294
+ json = JSON.parse(source.read)
295
+ json.update(Hash[*query.split('&').map {|pair| pair.split('=')}.flatten]) if query
296
+ json.key?('names') ?
297
+ When::BasicTypes::M17n.new(json) :
298
+ When::Coordinates::Spatial.new(json)
299
+ end
300
+
301
+ rescue => no_file_error
302
+ # 新しいデータ
303
+ case interval
304
+ when 0
305
+ raise no_file_error
306
+ when Numeric
307
+ if @wikipedia_last_access
308
+ delay = (@wikipedia_last_access + interval.abs - Time.now.to_f).ceil
309
+ sleep(delay) if delay > 0
310
+ end
311
+ end
312
+ contents = nil
313
+ begin
314
+ OpenURI
315
+ source = open(path, 'r'+mode)
316
+ contents = source.read
317
+ ensure
318
+ @wikipedia_last_access = Time.now.to_f
319
+ source.close if source
320
+ end
321
+
322
+ # wikipedia contents
323
+ raise KeyError, 'Article not found: ' + title if contents =~ /<div class="noarticletext">/
324
+
325
+ # word
326
+ word = {
327
+ :label => title,
328
+ :names => {''=>title, locale=>title},
329
+ :link => {''=>path, locale=>path }
330
+ }
331
+ contents.scan(Link) do |link|
332
+ word[:names][$1] = $4
333
+ word[:link ][$1] = "http://#{$1}.wikipedia.org/wiki/#{$3}"
334
+ end
335
+ object = When::BasicTypes::M17n.new(word)
336
+
337
+ # location
338
+ if contents =~ /tools\.wmflabs\.org\/geohack\/geohack\.php\?.+?params=(.+?[NS])_(.+?[EW])/
339
+ location = {
340
+ :label => object
341
+ }
342
+ location[:lat], location[:long] = $~[1..2].map {|pos|
343
+ pos.gsub(/_(\d)[._]/, '_0\1_').sub('.', '_').sub('_', '.').gsub('_', '')
344
+ }
345
+ object = When::Coordinates::Spatial.new(location)
346
+ end
347
+
348
+ # save data
349
+ open("#{dir}/#{file}.json", 'w'+mode) do |source|
350
+ source.write(JSON.dump(object.to_h({:method=>:to_h}).update(options)))
351
+ end
352
+ query ? _wikipedia_object(path, locale, file, query) : object
353
+ end
354
+
355
+ # wikipedia オブジェクトの関連付け
356
+ def _wikipedia_relation(object, path, query)
357
+ code_space = path.sub(/[^\/]+\z/, '')
358
+ if object.kind_of?(When::Coordinates::Spatial)
359
+ object.label._pool['..'] = object
360
+ object._pool[object.label.to_s] = object.label
361
+ object.send(:child=, [object.label])
362
+ object.label.send(:code_space=, code_space)
363
+ else
364
+ object.send(:code_space=, code_space)
365
+ end
366
+ object._pool['..'] = path
367
+ object._pool['..'] += '?' + query if query
368
+ object
369
+ end
370
+ end
371
+
372
+ # ローケール指定時の文字列
373
+ #
374
+ # @return [Hash]
375
+ attr_reader :names
376
+
377
+ # 有効なローケール指定
378
+ #
379
+ # @return [Array<String>]
380
+ attr_reader :keys
381
+
382
+ # 有効な文字列 - additional attribute
383
+ #
384
+ # @return [Array<String>]
385
+ attr_reader :values
386
+
387
+ # 文字列の説明 - additional attribute
388
+ #
389
+ # @return [Hash] { anyURI }
390
+ attr_reader :link
391
+
392
+ # Rails 用 I18n 定義へのアクセス・キー
393
+ #
394
+ # @return [String]
395
+ attr_reader :access_key
396
+ protected :access_key
397
+
398
+ # 特定 locale に対応した文字列の取得
399
+ #
400
+ # @param [String] loc locale の指定 ( lang[-|_]country.encode )
401
+ # [ lang - 言語 ]
402
+ # [ country - 国(省略可) ]
403
+ # [ encode - 文字コード(省略可) ]
404
+ #
405
+ # @return [String] loc に対応した文字列
406
+ #
407
+ def translate(loc='')
408
+ return to_s unless loc
409
+ loc = loc.sub('_', '-')
410
+ lang, code = loc.split(/\./)
411
+ result = _label_value(loc)
412
+ return result if !code || @names.member?(loc)
413
+ return result.encode(code)
414
+ end
415
+ alias :/ :translate
416
+
417
+ # 特定 locale に対応した reference URI の取得
418
+ #
419
+ # @param [String] loc locale の指定
420
+ #
421
+ # @return [String] loc に対応した reference URI
422
+ #
423
+ def reference(loc='')
424
+ loc ||= ''
425
+ return Locale._hash_value(@link, loc.sub('_', '-'))
426
+ end
427
+
428
+ # 部分文字列
429
+ #
430
+ # @param [Range] range String#[] と同様の指定方法で範囲を指定する
431
+ #
432
+ # @return [When::Locale] 指定範囲に対応した部分文字列
433
+ #
434
+ def [](range)
435
+ dup._copy({
436
+ :label => to_s[range],
437
+ :names => @names.keys.inject({}) {|l,k|
438
+ l[k] = @names[k][range]
439
+ l
440
+ }
441
+ })
442
+ end
443
+
444
+ # 文字列の一致
445
+ #
446
+ # @param [String, Regexp] regexp マッチする正規表現
447
+ #
448
+ # @return [Integer] マッチした位置のindex(いずれかの locale でマッチが成功した場合)
449
+ # @return [nil] すべての locale でマッチに失敗した場合
450
+ #
451
+ def =~(regexp)
452
+ @keys.each do |key|
453
+ index = (@names[key] =~ regexp)
454
+ return index if index
455
+ end
456
+ return nil
457
+ end
458
+
459
+ # 部分文字列の位置
460
+ #
461
+ # @param [String] other 部分文字列
462
+ #
463
+ # @return [Integer] 部分文字列の先頭のindex(いずれかの locale で部分文字列を含んだ場合)
464
+ # @return [nil] すべての locale で部分文字列を含まない場合
465
+ #
466
+ def index(other)
467
+ @keys.each do |key|
468
+ index = @names[key].index(Locale.translate(other, key))
469
+ return index if index
470
+ end
471
+ return nil
472
+ end
473
+
474
+ # 文字列の連結
475
+ #
476
+ # @param [String, When::Toos::Locale] other 連結する文字列
477
+ #
478
+ # @return [When::Toos::Locale] 連結された文字列
479
+ #
480
+ def +(other)
481
+ names = {}
482
+ case other
483
+ when Locale
484
+ (@names.keys + other.names.keys).uniq.each do |key|
485
+ names[key] = _label_value(key) + other._label_value(key)
486
+ end
487
+ links = other.link
488
+ else
489
+ @names.keys.each do |key|
490
+ names[key] = _label_value(key) + other.to_s
491
+ end
492
+ links = {}
493
+ end
494
+ return dup._copy({:names=>names, :link=>links.merge(link), :label=>to_s + other.to_s})
495
+ end
496
+
497
+ # 書式指定による文字列化
498
+ #
499
+ # @param [Array<Object>] other 文字列化する Object の Array
500
+ # @param [Array<String>] locale 文字列化を行う locale の指定(デフォルト : すべて)
501
+ #
502
+ # @return [When::Toos::Locale] 文字列化された Object
503
+ #
504
+ def _printf(other, locale=nil)
505
+ # 処理する配列
506
+ terms = other.kind_of?(Array) ? [self] + other : [self, other]
507
+
508
+ # locale key の配列
509
+ if locale == []
510
+ keys = []
511
+ else
512
+ keys = terms.inject([]) {|k,t|
513
+ k += t.keys if t.kind_of?(Locale)
514
+ k
515
+ }.uniq
516
+ if locale
517
+ locale = [locale] unless locale.kind_of?(Array)
518
+ keys = locale | (locale & keys)
519
+ end
520
+ end
521
+ keys << nil if keys.include?('')
522
+
523
+ # names ハッシュ
524
+ names = keys.inject({}) {|l,k|
525
+ l[k] = When::Coordinates::Pair._format(
526
+ (block_given? ? yield(k, *terms) : terms).map {|t|
527
+ t.kind_of?(Locale) ? t.translate(k) : t
528
+ }
529
+ )
530
+ l
531
+ }
532
+
533
+ # link ハッシュ
534
+ links = terms.reverse.inject({}) {|h,t|
535
+ h.update(t.link) if t.kind_of?(Locale)
536
+ h
537
+ }
538
+
539
+ # 生成
540
+ dup._copy({
541
+ :label => keys.include?('') ? names.delete(nil) : (names[''] = names[keys[0]]),
542
+ :names => names,
543
+ :link => links
544
+ })
545
+ end
546
+ alias :% :_printf
547
+
548
+ # ローケールの更新
549
+ #
550
+ # @param [Hash] options 下記の通り
551
+ # @option options [String] カントリーコード 表現文字列
552
+ # @option options [Hash] :link { カントリーコード => 参照URL文字列 }
553
+ # @option options [String] :code_space 規格や辞書を特定するコードスペースのURL文字列
554
+ # @option options [String] :label 代表文字列
555
+ # @note Hashキーはすべて Optional で、存在するもののみ更新します
556
+ #
557
+ # @return [self] 更新された Object
558
+ #
559
+ def update(options={})
560
+ options = options.dup
561
+ @link.update(options.delete(:link)) if (options.key?(:link))
562
+ @code_space = options.delete(:code_space) if (options.key?(:code_space))
563
+ self[0..-1] = options.delete(:label) if (options.key?(:label))
564
+ unless (options.empty?)
565
+ @names.update(options)
566
+ @keys = @names.keys.sort
567
+ @values = @names.values.sort.reverse
568
+ end
569
+ return self
570
+ end
571
+
572
+ # 包摂リストに登録されている文字を包摂する(自己破壊)
573
+ #
574
+ # @param [Hash] pattern 包摂ルール
575
+ #
576
+ # @return [When::Locale] self
577
+ # @private
578
+ def ideographic_unification!(pattern=_unification)
579
+ names = {}
580
+ @names.each_pair do |key, value|
581
+ names[key] = Locale.ideographic_unification(value, pattern)
582
+ end
583
+ @names = names
584
+ @values = @names.values.sort.reverse
585
+ self[0..-1] = Locale.ideographic_unification(self.to_s[0..-1], pattern)
586
+ return self
587
+ end
588
+ protected :ideographic_unification!
589
+
590
+ # 包摂リストに登録されている文字を包摂する(自己保存)
591
+ #
592
+ # @param [Hash] pattern 包摂ルール
593
+ #
594
+ # @return [When::Locale] 包摂結果
595
+ #
596
+ def ideographic_unification(pattern=_unification)
597
+ dup.ideographic_unification!(pattern)
598
+ end
599
+
600
+ # 閏の表記を扱うための書式付整形
601
+ # @private
602
+ def prefix(other, locale=nil)
603
+ return self.dup unless other
604
+ other = m17n(other) unless other.kind_of?(Locale)
605
+ other._printf(self, locale) do |k, *t|
606
+ t[0] = t[0].translate(k)
607
+ t[0] += "%s" unless t[0] =~ /%[-+]?[.\d]*s/
608
+ t
609
+ end
610
+ end
611
+
612
+ protected
613
+
614
+ # @private
615
+ def _copy(options={})
616
+ if options.key?('label') # for JSON
617
+ opt = {}
618
+ options.each_pair do |key, value|
619
+ opt[key.to_sym] = value
620
+ end
621
+ else
622
+ opt = options
623
+ end
624
+
625
+ self[0..-1] = opt[:label] if opt[:label]
626
+ if opt[:names]
627
+ @names = opt[:names]
628
+ @keys = @names.keys.sort
629
+ @values = @names.values.compact.sort.reverse
630
+ end
631
+ @label = opt[:label] if opt[:label]
632
+ @link = opt[:link] if opt[:link]
633
+ @access_key = opt[:access_key] if opt[:access_key]
634
+ @code_space = opt[:code_space] if opt[:code_space]
635
+ return self
636
+ end
637
+
638
+ # @private
639
+ def _copy_all(other)
640
+ _copy({:label => other.to_s,
641
+ :names => other.names,
642
+ :link => other.link,
643
+ :access_key => other.access_key,
644
+ :code_space => other.code_space
645
+ })
646
+ end
647
+
648
+ # locale 指定を解析して @names の値を取り出す
649
+ # @private
650
+ def _label_value(locale)
651
+ label = Locale._hash_value(@names, locale, [])
652
+ return label if label
653
+ foreign = Locale._get_locale(locale, @access_key)
654
+ return @names[''] unless foreign
655
+ english = @names['en'] || @names['']
656
+ addition = english.dup.sub!(/\A#{Locale._get_locale('en', @access_key)['en']}/, '')
657
+ foreign[locale] += addition if addition
658
+ update(foreign)
659
+ return Locale._hash_value(@names, locale)
660
+ end
661
+
662
+ private
663
+
664
+ def _names(names, namespace, default_locale)
665
+
666
+ # names, link の組み立て
667
+ @names = {}
668
+ @link = {}
669
+
670
+ if names.kind_of?(String)
671
+ unless (names=~/\A\s*\[(.+?)\]\s*\z/m)
672
+ names = names.strip
673
+ @names[''] = names
674
+ @keys = ['']
675
+ @values = [names]
676
+ return names
677
+ end
678
+ names = $1.split(/[\n\r,]+/)
679
+ end
680
+
681
+ mark = []
682
+ asterisk = []
683
+ default_locale = default_locale.dup
684
+ names.each do |v|
685
+ v.strip!
686
+ case v
687
+ when '', /\A#/ ;
688
+ when /\A\/(.+)/; @access_key = $1
689
+ when /\A(\*)?(?:([^=%]*?)\s*:)?\s*(.+?)\s*(=\s*([^=]+?(\?.+)?)?)?\z/
690
+ asterisk[0], locale, name, assignment, ref = $~[1..5]
691
+ asterisk[1], locale, default_ref = default_locale.shift unless locale
692
+ locale ||= ''
693
+ ref ||= default_ref unless (assignment)
694
+ ref ||= ''
695
+ mark[0] = locale if asterisk[0]
696
+ mark[1] = locale if asterisk[1]
697
+ mark[2] = locale unless mark[2]
698
+ name = _replacement($1, locale, ($3 || @names['en'] || @names[''])) if name =~ /\A_([A-Z_]+)_(\((.+)\))?\z/
699
+ name.gsub!(/<[0-9A-F]{2}>/i) {|code| code[1..2].to_i(16).chr}
700
+ @names[locale] = name
701
+ if ref =~ /\A(.+):/
702
+ prefix = namespace[$1] || Locale.send(:_namespaces)[$1]
703
+ ref.sub!(/\A.+:/, prefix) if prefix
704
+ end
705
+ ref += '%%<' + name + '>' if ref =~ /[\/#:]\z/
706
+ unless ref == ''
707
+ @link[locale] = _encode(ref)
708
+ # When.logger.info("%s[%s]->%s" % [@names[locale], locale, @link[locale]]) if When.logger
709
+ end
710
+ else ; raise ArgumentError, "Irregal locale format: " + v
711
+ end
712
+ end
713
+ if Locale.wikipedia_interval && Locale.wikipedia_interval <= 0
714
+ ['en', ''].each do |lc|
715
+ if Locale::Ref =~ @link[lc] && $1 == 'en'
716
+ object = Locale.send(:wikipedia_object, @link[lc])
717
+ if object
718
+ @names = object.names.merge(@names)
719
+ @link = object.link.merge(@link)
720
+ end
721
+ break
722
+ end
723
+ end
724
+ end
725
+
726
+ # keys, values の準備
727
+ @keys = @names.keys.sort
728
+ @values = @names.values.sort.reverse
729
+
730
+ # 代表名
731
+ @names[mark[0] || mark[1] || mark[2]]
732
+ end
733
+
734
+ #
735
+ # 英語表記を現地表記に置き換える
736
+ #
737
+ def _replacement(table, locale, name)
738
+ Locale.const_get(table.split('_')[-1])[locale] ?
739
+ Locale.send(table.downcase, name, locale) :
740
+ name
741
+ end
742
+
743
+ # encode URI from patterns %%(...) or %.(...)
744
+ def _encode(source)
745
+ source.gsub(/%.<.+?>/) do |match|
746
+ URI.encode(match[3..-2]).gsub('%', match[1..1])
747
+ end
748
+ end
749
+ end
750
+ end