-
Notifications
You must be signed in to change notification settings - Fork 482
/
sync-codeorg-in.rb
executable file
·209 lines (179 loc) · 7.13 KB
/
sync-codeorg-in.rb
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
#!/usr/bin/env ruby
# Pulls in all strings that need to be translated. Pulls source
# files from blockly-core, apps, pegasus, and dashboard
# as well as instructions for levelbuilder supported levels and
# collects them to the single source folder i18n/locales/source.
require File.expand_path('../../../dashboard/config/environment', __FILE__)
require File.expand_path('../../../pegasus/src/env', __FILE__)
require 'fileutils'
require 'json'
require 'yaml'
require 'tempfile'
require_relative 'i18n_script_utils'
def sync_in
localize_level_content
localize_block_content
run_bash_script "bin/i18n-codeorg/in.sh"
redact_level_content
redact_block_content
end
def copy_to_yml(label, data)
File.open("dashboard/config/locales/#{label}.en.yml", "w+") do |f|
f.write(to_crowdin_yaml({"en" => {"data" => {label => data}}}))
end
end
# sanitize a string before uploading to crowdin. Currently only performs
# CRLF -> LF conversion, but could be extended to do more
def sanitize(string)
return string.gsub(/\r(\n)?/, "\n")
end
def redact_translated_data(path, plugins = nil)
source = "i18n/locales/source/#{path}"
backup = "i18n/locales/original/#{path}"
FileUtils.mkdir_p(File.dirname(backup))
FileUtils.cp(source, backup)
redact(source, source, plugins)
end
def redact_block_content
redact_translated_data('dashboard/blocks.yml', 'blockfield')
end
def get_dsl_i18n_strings(level)
return {} unless level.is_a?(DSLDefined)
text = level.dsl_text
return {} unless text
_, i18n = level.class.dsl_class.parse(text, '')
i18n
end
def localize_level_content
# Rewrite autogenerated 'dsls.en.yml' i18n file with new master-copy English strings
yml_file = Rails.root.join("config/locales/dsls.en.yml")
dsl_i18n_strings = {}
# We have to run this specifically from the Rails directory because
# get_dsl_i18n_strings relies on level.dsl_text which relies on
# level.filename which relies on running a shell command
Dir.chdir(Rails.root) do
Script.all.each do |script|
next unless ScriptConstants.i18n? script.name
script_strings = {
"display_name" => {},
"short_instructions" => {},
"long_instructions" => {},
"failure_message_overrides" => {},
"authored_hints" => {},
"callouts" => {},
"block_categories" => {},
"function_names" => {}
}
script.levels.each do |level|
url = get_level_url_key(script, level)
# TODO: we also need to deal with contained levels that are not
# DSL-Defined, like FreeResponse. That, or just get rid of contained
# levels entirely.
level.contained_levels.each do |contained_level|
dsl_i18n_strings.deep_merge! get_dsl_i18n_strings(contained_level) if contained_level.is_a?(DSLDefined)
end
if level.is_a?(DSLDefined)
dsl_i18n_strings.deep_merge! get_dsl_i18n_strings(level)
else
# display_name
if level.display_name
script_strings['display_name'][url] = level.display_name
end
# short_instructions
if level.short_instructions
script_strings['short_instructions'][url] = level.short_instructions
end
# long_instructions
if level.long_instructions
script_strings['long_instructions'][url] = level.long_instructions
end
# failure_message_overrides
if level.class <= Blockly && level.failure_message_override
script_strings['failure_message_overrides'][url] = level.failure_message_override
end
# authored_hints
if level.authored_hints
authored_hints = JSON.parse(level.authored_hints)
script_strings['authored_hints'][url] = Hash.new unless authored_hints.empty?
authored_hints.each do |hint|
script_strings['authored_hints'][url][hint['hint_id']] = hint['hint_markdown']
end
end
# callouts
if level.callout_json
callouts = JSON.parse(level.callout_json)
script_strings['callouts'][url] = Hash.new unless callouts.empty?
callouts.each do |callout|
script_strings['callouts'][url][callout['localization_key']] = callout['callout_text']
end
end
level_xml = Nokogiri::XML(level.to_xml, &:noblanks)
blocks = level_xml.xpath('//blocks').first
if blocks
## Categories
block_categories = blocks.xpath('//category')
script_strings['block_categories'][url] = Hash.new unless block_categories.empty?
block_categories.each do |category|
name = category.attr('name')
script_strings['block_categories'][url][name] = name if name
end
## Function Names
functions = blocks.xpath("//block[@type=\"procedures_defnoreturn\"]")
script_strings['function_names'][url] = Hash.new unless functions.empty?
functions.each do |function|
name = function.at_xpath('./title[@name="NAME"]')
script_strings['function_names'][url][name.content] = name.content if name
end
end
end
end
script_strings.delete_if {|_key, value| value.nil? || value.empty?}
script_i18n_directory = "../i18n/locales/source/course_content"
script_i18n_directory =
if script.version_year
"#{script_i18n_directory}/#{script.version_year}"
elsif ScriptConstants.script_in_category?(:hoc, script.name)
"#{script_i18n_directory}/Hour of Code"
else
"#{script_i18n_directory}/other"
end
FileUtils.mkdir_p script_i18n_directory unless Dir.exist? script_i18n_directory
script_i18n_filename = "#{script_i18n_directory}/#{script.name}.json"
File.open(script_i18n_filename, 'w') do |file|
file.write(JSON.pretty_generate(script_strings))
end
end
end
i18n_warning = "# Autogenerated English-language level-definition locale file. Do not edit by hand or commit to version control.\n"
File.write(yml_file, i18n_warning + dsl_i18n_strings.deep_sort.to_yaml(line_width: -1))
end
# Pull in various fields for custom blocks from .json files and save them to
# blocks.en.yml.
def localize_block_content
blocks = {}
Dir.glob('dashboard/config/blocks/**/*.json').sort.each do |file|
name = File.basename(file, '.*')
config = JSON.parse(File.read(file))['config']
blocks[name] = {
'text' => config['blockText'],
}
next unless config['args']
args_with_options = {}
config['args'].each do |arg|
next if !arg['options'] || arg['options'].empty?
options = args_with_options[arg['name']] = {}
arg['options'].each do |option_tuple|
options[option_tuple.last] = option_tuple.first
end
end
blocks[name]['options'] = args_with_options unless args_with_options.empty?
end
copy_to_yml('blocks', blocks)
end
def redact_level_content
Dir.glob("i18n/locales/source/course_content/**/*.json").each do |source|
backup = source.sub("source", "original")
redact_course_content(source, source, backup)
end
end
sync_in if __FILE__ == $0