i18n_yaml_sorter 0.1.1 → 0.2.0
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.
- data/Gemfile +13 -0
- data/Gemfile.lock +18 -0
- data/LICENSE +1 -1
- data/README.rdoc +126 -23
- data/Rakefile +17 -13
- data/VERSION +1 -1
- data/bin/sort_yaml +34 -2
- data/i18n_yaml_sorter.gemspec +44 -34
- data/lib/i18n_yaml_sorter.rb +2 -128
- data/lib/i18n_yaml_sorter/railtie.rb +10 -0
- data/lib/i18n_yaml_sorter/sorter.rb +128 -0
- data/lib/tasks/i18n_yaml_sorter.rake +16 -0
- data/test/helper.rb +10 -1
- data/test/in.yml +1 -1
- data/test/in_rails.yml +239 -0
- data/test/in_simple.yml +28 -0
- data/test/out.yml +1 -0
- data/test/out_rails.yml +220 -0
- data/test/out_simple.yml +28 -0
- data/test/test_i18n_yaml_sorter.rb +29 -11
- data/textmate/YAML Sort.tmbundle/Commands/Sort YAML.tmCommand +22 -0
- data/textmate/YAML Sort.tmbundle/info.plist +10 -0
- metadata +81 -46
- data/.gitignore +0 -21
@@ -0,0 +1,128 @@
|
|
1
|
+
module I18nYamlSorter
|
2
|
+
class Sorter
|
3
|
+
def initialize(io_input)
|
4
|
+
@io_input = io_input
|
5
|
+
end
|
6
|
+
|
7
|
+
def sort
|
8
|
+
@array = break_blocks_into_array
|
9
|
+
@current_array_index = 0
|
10
|
+
sorted_yaml_from_blocks_array
|
11
|
+
end
|
12
|
+
|
13
|
+
private
|
14
|
+
|
15
|
+
def break_blocks_into_array
|
16
|
+
array = []
|
17
|
+
|
18
|
+
loop do
|
19
|
+
|
20
|
+
maybe_next_line = @read_line_again || @io_input.gets || break
|
21
|
+
@read_line_again = nil
|
22
|
+
maybe_next_line.chomp!
|
23
|
+
|
24
|
+
#Is it blank? Discard!
|
25
|
+
next if maybe_next_line.match(/^\s*$/)
|
26
|
+
|
27
|
+
#Does it look like a key: value line?
|
28
|
+
key_value_parse = maybe_next_line.match(/^(\s*)(["']?[\w\-]+["']?)(: )(\s*)(\S.*\S)(\s*)$/)
|
29
|
+
if key_value_parse
|
30
|
+
array << maybe_next_line.concat("\n") #yes, it is the beginning of a key:value block
|
31
|
+
|
32
|
+
#Special cases when it should add extra lines to the array element (multi line quoted strings)
|
33
|
+
|
34
|
+
#Is the value surrounded by quotes?
|
35
|
+
starts_with_quote = key_value_parse[5].match(/^["']/)[0] rescue nil
|
36
|
+
ends_with_quote = key_value_parse[5].match(/[^\\](["'])$/)[1] rescue nil
|
37
|
+
if starts_with_quote and !(starts_with_quote == ends_with_quote)
|
38
|
+
|
39
|
+
loop do #Append next lines until we find the closing quote
|
40
|
+
content_line = @io_input.gets || break
|
41
|
+
content_line.chomp!
|
42
|
+
array.last << content_line.concat("\n")
|
43
|
+
break if content_line.match(/[^\\][#{starts_with_quote}]\s*$/)
|
44
|
+
end
|
45
|
+
|
46
|
+
end # if starts_with_quote
|
47
|
+
|
48
|
+
next
|
49
|
+
end # if key_value_parse
|
50
|
+
|
51
|
+
# Is it a | or > string alue?
|
52
|
+
is_special_string = maybe_next_line.match(/^(\s*)(["']?[\w\-]+["']?)(: )(\s*)([|>])(\s*)$/)
|
53
|
+
if is_special_string
|
54
|
+
array << maybe_next_line.concat("\n") #yes, it is the beginning of a key block
|
55
|
+
indentation = is_special_string[1]
|
56
|
+
#Append the next lines until we find one that is not indented
|
57
|
+
loop do
|
58
|
+
content_line = @io_input.gets || break
|
59
|
+
processed_line = content_line.chomp
|
60
|
+
this_indentation = processed_line.match(/^\s*/)[0] rescue ""
|
61
|
+
if indentation.size < this_indentation.size
|
62
|
+
array.last << processed_line.concat("\n")
|
63
|
+
else
|
64
|
+
@read_line_again = content_line
|
65
|
+
break
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
next
|
70
|
+
end #if is_special_string
|
71
|
+
|
72
|
+
# Is it the begining of a multi level hash?
|
73
|
+
is_start_of_hash = maybe_next_line.match(/^(\s*)(["']?[\w\-]+["']?)(:)(\s*)$/)
|
74
|
+
if is_start_of_hash
|
75
|
+
array << maybe_next_line.concat("\n")
|
76
|
+
next
|
77
|
+
end
|
78
|
+
|
79
|
+
#If we got here and nothing was done, this line
|
80
|
+
# should probably be merged with the previous one.
|
81
|
+
if array.last
|
82
|
+
array.last << maybe_next_line.concat("\n")
|
83
|
+
else
|
84
|
+
array << maybe_next_line.concat("\n")
|
85
|
+
end
|
86
|
+
end #loop
|
87
|
+
|
88
|
+
#debug:
|
89
|
+
#puts array.join("$$$$$$$$$$$$$$$$$$$$$$\n")
|
90
|
+
|
91
|
+
array
|
92
|
+
end
|
93
|
+
|
94
|
+
def sorted_yaml_from_blocks_array(current_block = nil)
|
95
|
+
|
96
|
+
unless current_block
|
97
|
+
current_block = @array[@current_array_index]
|
98
|
+
@current_array_index += 1
|
99
|
+
end
|
100
|
+
|
101
|
+
out_array = []
|
102
|
+
current_match = current_block.match(/^(\s*)(["']?[\w\-]+["']?)(:)/)
|
103
|
+
current_level = current_match[1] rescue ''
|
104
|
+
current_key = current_match[2].downcase.tr(%q{"'}, "") rescue ''
|
105
|
+
out_array << [current_key, current_block]
|
106
|
+
|
107
|
+
loop do
|
108
|
+
next_block = @array[@current_array_index] || break
|
109
|
+
@current_array_index += 1
|
110
|
+
|
111
|
+
current_match = next_block.match(/^(\s*)(["']?[\w\-]+["']?)(:)/) || next
|
112
|
+
current_key = current_match[2].downcase.tr(%q{"'}, "")
|
113
|
+
next_level = current_match[1]
|
114
|
+
|
115
|
+
if current_level.size < next_level.size
|
116
|
+
out_array.last.last << sorted_yaml_from_blocks_array(next_block)
|
117
|
+
elsif current_level.size == next_level.size
|
118
|
+
out_array << [current_key, next_block]
|
119
|
+
elsif current_level.size > next_level.size
|
120
|
+
@current_array_index -= 1
|
121
|
+
break
|
122
|
+
end
|
123
|
+
end
|
124
|
+
|
125
|
+
return out_array.sort.map(&:last).join
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'i18n_yaml_sorter'
|
2
|
+
|
3
|
+
namespace :i18n do
|
4
|
+
|
5
|
+
desc "Sort locales keys in alphabetic order. Sort all locales if no path parameter given"
|
6
|
+
|
7
|
+
task :sort, [:path_to_file] => :environment do |t , args|
|
8
|
+
locales = args[:path_to_file] || Dir.glob("#{Rails.root}/config/locales/**/*.yml")
|
9
|
+
|
10
|
+
locales.each do |locale_path|
|
11
|
+
sorted_contents = File.open(locale_path) { |f| I18nYamlSorter::Sorter.new(f).sort }
|
12
|
+
File.open(locale_path, 'w') { |f| f << sorted_contents}
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
end
|
data/test/helper.rb
CHANGED
@@ -1,8 +1,17 @@
|
|
1
1
|
require 'rubygems'
|
2
|
+
require 'bundler'
|
3
|
+
begin
|
4
|
+
Bundler.setup(:default, :development)
|
5
|
+
rescue Bundler::BundlerError => e
|
6
|
+
$stderr.puts e.message
|
7
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
8
|
+
exit e.status_code
|
9
|
+
end
|
2
10
|
require 'test/unit'
|
11
|
+
require 'yaml'
|
3
12
|
|
4
|
-
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
5
13
|
$LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
|
14
|
+
$LOAD_PATH.unshift(File.dirname(__FILE__))
|
6
15
|
require 'i18n_yaml_sorter'
|
7
16
|
|
8
17
|
class Test::Unit::TestCase
|
data/test/in.yml
CHANGED
data/test/in_rails.yml
ADDED
@@ -0,0 +1,239 @@
|
|
1
|
+
# encoding: UTF-8
|
2
|
+
# pt-BR translations for Ruby on Rails
|
3
|
+
"pt-BR":
|
4
|
+
date:
|
5
|
+
# formatos de data e hora
|
6
|
+
formats:
|
7
|
+
default: "%d/%m/%Y"
|
8
|
+
short: "%d de %B"
|
9
|
+
long: "%d de %B de %Y"
|
10
|
+
|
11
|
+
day_names:
|
12
|
+
- Domingo
|
13
|
+
- Segunda
|
14
|
+
- Terça
|
15
|
+
- Quarta
|
16
|
+
- Quinta
|
17
|
+
- Sexta
|
18
|
+
- Sábado
|
19
|
+
abbr_day_names:
|
20
|
+
- Dom
|
21
|
+
- Seg
|
22
|
+
- Ter
|
23
|
+
- Qua
|
24
|
+
- Qui
|
25
|
+
- Sex
|
26
|
+
- Sáb
|
27
|
+
|
28
|
+
month_names:
|
29
|
+
- ~
|
30
|
+
- Janeiro
|
31
|
+
- Fevereiro
|
32
|
+
- Março
|
33
|
+
- Abril
|
34
|
+
- Maio
|
35
|
+
- Junho
|
36
|
+
- Julho
|
37
|
+
- Agosto
|
38
|
+
- Setembro
|
39
|
+
- Outubro
|
40
|
+
- Novembro
|
41
|
+
- Dezembro
|
42
|
+
abbr_month_names:
|
43
|
+
- ~
|
44
|
+
- Jan
|
45
|
+
- Fev
|
46
|
+
- Mar
|
47
|
+
- Abr
|
48
|
+
- Mai
|
49
|
+
- Jun
|
50
|
+
- Jul
|
51
|
+
- Ago
|
52
|
+
- Set
|
53
|
+
- Out
|
54
|
+
- Nov
|
55
|
+
- Dez
|
56
|
+
order:
|
57
|
+
- :day
|
58
|
+
- :month
|
59
|
+
- :year
|
60
|
+
|
61
|
+
time:
|
62
|
+
formats:
|
63
|
+
default: "%A, %d de %B de %Y, %H:%M h"
|
64
|
+
short: "%d/%m, %H:%M h"
|
65
|
+
long: "%A, %d de %B de %Y, %H:%M h"
|
66
|
+
am: ''
|
67
|
+
pm: ''
|
68
|
+
|
69
|
+
support:
|
70
|
+
array:
|
71
|
+
# Usado no Array.to_sentence
|
72
|
+
words_connector: ", "
|
73
|
+
two_words_connector: " e "
|
74
|
+
last_word_connector: " e "
|
75
|
+
|
76
|
+
select:
|
77
|
+
prompt: "Por favor selecione"
|
78
|
+
|
79
|
+
number:
|
80
|
+
format:
|
81
|
+
separator: ','
|
82
|
+
delimiter: '.'
|
83
|
+
precision: 3
|
84
|
+
significant: false
|
85
|
+
strip_insignificant_zeros: false
|
86
|
+
|
87
|
+
currency:
|
88
|
+
format:
|
89
|
+
format: '%u %n'
|
90
|
+
unit: 'R$'
|
91
|
+
separator: ','
|
92
|
+
delimiter: '.'
|
93
|
+
precision: 2
|
94
|
+
significant: false
|
95
|
+
strip_insignificant_zeros: false
|
96
|
+
|
97
|
+
percentage:
|
98
|
+
format:
|
99
|
+
delimiter: '.'
|
100
|
+
|
101
|
+
precision:
|
102
|
+
format:
|
103
|
+
delimiter: '.'
|
104
|
+
|
105
|
+
human:
|
106
|
+
format:
|
107
|
+
delimiter: '.'
|
108
|
+
precision: 2
|
109
|
+
significant: true
|
110
|
+
strip_insignificant_zeros: true
|
111
|
+
storage_units:
|
112
|
+
format: "%n %u"
|
113
|
+
units:
|
114
|
+
byte:
|
115
|
+
one: "Byte"
|
116
|
+
other: "Bytes"
|
117
|
+
kb: "KB"
|
118
|
+
mb: "MB"
|
119
|
+
gb: "GB"
|
120
|
+
tb: "TB"
|
121
|
+
decimal_units:
|
122
|
+
# number_to_human()
|
123
|
+
# new in rails 3: please add to other locales
|
124
|
+
format: "%n %u"
|
125
|
+
units:
|
126
|
+
unit: ""
|
127
|
+
thousand: "mil"
|
128
|
+
million:
|
129
|
+
one: milhão
|
130
|
+
other: milhões
|
131
|
+
billion:
|
132
|
+
one: bilhão
|
133
|
+
other: bilhões
|
134
|
+
trillion:
|
135
|
+
one: trilhão
|
136
|
+
other: trilhões
|
137
|
+
quadrillion:
|
138
|
+
one: quatrilhão
|
139
|
+
other: quatrilhões
|
140
|
+
|
141
|
+
datetime:
|
142
|
+
distance_in_words:
|
143
|
+
# distancia do tempo em palavras
|
144
|
+
half_a_minute: 'meio minuto'
|
145
|
+
less_than_x_seconds:
|
146
|
+
one: 'menos de 1 segundo'
|
147
|
+
other: 'menos de %{count} segundos'
|
148
|
+
x_seconds:
|
149
|
+
one: '1 segundo'
|
150
|
+
other: '%{count} segundos'
|
151
|
+
less_than_x_minutes:
|
152
|
+
one: 'menos de um minuto'
|
153
|
+
other: 'menos de %{count} minutos'
|
154
|
+
x_minutes:
|
155
|
+
one: '1 minuto'
|
156
|
+
other: '%{count} minutos'
|
157
|
+
about_x_hours:
|
158
|
+
one: 'aproximadamente 1 hora'
|
159
|
+
other: 'aproximadamente %{count} horas'
|
160
|
+
x_days:
|
161
|
+
one: '1 dia'
|
162
|
+
other: '%{count} dias'
|
163
|
+
about_x_months:
|
164
|
+
one: 'aproximadamente 1 mês'
|
165
|
+
other: 'aproximadamente %{count} meses'
|
166
|
+
x_months:
|
167
|
+
one: '1 mês'
|
168
|
+
other: '%{count} meses'
|
169
|
+
about_x_years:
|
170
|
+
one: 'aproximadamente 1 ano'
|
171
|
+
other: 'aproximadamente %{count} anos'
|
172
|
+
over_x_years:
|
173
|
+
one: 'mais de 1 ano'
|
174
|
+
other: 'mais de %{count} anos'
|
175
|
+
almost_x_years:
|
176
|
+
one: 'quase 1 ano'
|
177
|
+
other: 'quase %{count} anos'
|
178
|
+
prompts:
|
179
|
+
year: "Ano"
|
180
|
+
month: "Mês"
|
181
|
+
day: "Dia"
|
182
|
+
hour: "Hora"
|
183
|
+
minute: "Minuto"
|
184
|
+
second: "Segundo"
|
185
|
+
|
186
|
+
helpers:
|
187
|
+
select:
|
188
|
+
prompt: "Por favor selecione"
|
189
|
+
|
190
|
+
submit:
|
191
|
+
create: 'Criar %{model}'
|
192
|
+
update: 'Atualizar %{model}'
|
193
|
+
submit: 'Salvar %{model}'
|
194
|
+
|
195
|
+
errors:
|
196
|
+
format: "%{attribute} %{message}"
|
197
|
+
|
198
|
+
template:
|
199
|
+
header:
|
200
|
+
one: "Não foi possível gravar %{model}: 1 erro"
|
201
|
+
other: "Não foi possível gravar %{model}: %{count} erros."
|
202
|
+
body: "Por favor, verifique o(s) seguinte(s) campo(s):"
|
203
|
+
|
204
|
+
messages: &errors_messages
|
205
|
+
inclusion: "não está incluído na lista"
|
206
|
+
exclusion: "não está disponível"
|
207
|
+
invalid: "não é válido"
|
208
|
+
confirmation: "não está de acordo com a confirmação"
|
209
|
+
accepted: "deve ser aceito"
|
210
|
+
empty: "não pode ficar vazio"
|
211
|
+
blank: "não pode ficar em branco"
|
212
|
+
too_long: "é muito longo (máximo: %{count} caracteres)"
|
213
|
+
too_short: "é muito curto (mínimo: %{count} caracteres)"
|
214
|
+
wrong_length: "não possui o tamanho esperado (%{count} caracteres)"
|
215
|
+
not_a_number: "não é um número"
|
216
|
+
not_an_integer: "não é um número inteiro"
|
217
|
+
greater_than: "deve ser maior que %{count}"
|
218
|
+
greater_than_or_equal_to: "deve ser maior ou igual a %{count}"
|
219
|
+
equal_to: "deve ser igual a %{count}"
|
220
|
+
less_than: "deve ser menor que %{count}"
|
221
|
+
less_than_or_equal_to: "deve ser menor ou igual a %{count}"
|
222
|
+
odd: "deve ser ímpar"
|
223
|
+
even: "deve ser par"
|
224
|
+
|
225
|
+
activerecord:
|
226
|
+
errors:
|
227
|
+
template:
|
228
|
+
header:
|
229
|
+
one: "Não foi possível gravar %{model}: 1 erro"
|
230
|
+
other: "Não foi possível gravar %{model}: %{count} erros."
|
231
|
+
body: "Por favor, verifique o(s) seguinte(s) campo(s):"
|
232
|
+
|
233
|
+
messages:
|
234
|
+
taken: "já está em uso"
|
235
|
+
record_invalid: "A validação falhou: %{errors}"
|
236
|
+
<<: *errors_messages
|
237
|
+
|
238
|
+
full_messages:
|
239
|
+
format: "%{attribute} %{message}"
|
data/test/in_simple.yml
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
pt-BR:
|
2
|
+
# Note how this is a nice way of inputing
|
3
|
+
# paragraphs of text in YAML.
|
4
|
+
apples: >
|
5
|
+
Maçãs são boas,
|
6
|
+
só não coma
|
7
|
+
seus iPods!
|
8
|
+
grapes: Não comemos elas.
|
9
|
+
bananas: |
|
10
|
+
Bananas são "legais":
|
11
|
+
- Elas são <b> doces </b>.
|
12
|
+
isto: não é chave
|
13
|
+
|
14
|
+
Por isto todos gostam de bananas!
|
15
|
+
en-US:
|
16
|
+
# Note that our comments are important:
|
17
|
+
# Don't let your yaml sorter delete them!
|
18
|
+
grapes: We dont' eat them.
|
19
|
+
bananas: |
|
20
|
+
Bananas are "nice":
|
21
|
+
- They are <b> sweet </b>.
|
22
|
+
this: not a key
|
23
|
+
|
24
|
+
That is why everyone like bananas!
|
25
|
+
apples: >
|
26
|
+
Apples are fine,
|
27
|
+
just don't eat your
|
28
|
+
iPods!
|