scratch-blocks 2.1.19 → 2.1.21
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.
- package/AGENTS.md +20 -0
- package/CLAUDE.md +1 -0
- package/dist/main.mjs +1 -1
- package/dist/types/src/index.d.ts +1 -1
- package/dist/types/src/index.d.ts.map +1 -1
- package/dist/types/src/renderer/renderer.d.ts.map +1 -1
- package/dist/types/src/scratch_msgs.d.ts +32 -0
- package/dist/types/src/scratch_msgs.d.ts.map +1 -0
- package/eslint.config.mjs +0 -1
- package/msg/json/en.json +6 -0
- package/package.json +17 -14
- package/scripts/validate-en-json.mjs +27 -0
- package/src/fields/scratch_field_variable.ts +2 -2
- package/src/index.ts +2 -2
- package/src/renderer/constants.ts +1 -1
- package/src/renderer/drawer.ts +1 -1
- package/src/renderer/renderer.ts +2 -1
- package/src/scratch_c_block_wrap.ts +3 -2
- package/src/scratch_comment_bubble.ts +1 -1
- package/src/scratch_connection_checker.ts +2 -1
- package/src/scratch_msgs.ts +70 -0
- package/tsconfig.json +1 -0
- package/types/scratch-l10n.d.ts +15 -0
- package/dist/types/msg/scratch_msgs.d.ts +0 -9
- package/dist/types/msg/scratch_msgs.d.ts.map +0 -1
- package/i18n/common.py +0 -234
- package/i18n/create_messages.py +0 -162
- package/i18n/create_scratch_msgs.js +0 -68
- package/i18n/dedup_json.py +0 -73
- package/i18n/js_to_json.js +0 -54
- package/i18n/js_to_json.py +0 -136
- package/i18n/json_to_js.js +0 -52
- package/i18n/json_to_js.py +0 -185
- package/i18n/sync_tx_translations.js +0 -110
- package/i18n/test_scratch_msgs.js +0 -90
- package/i18n/tests.py +0 -47
- package/i18n/xliff_to_json.py +0 -232
- package/msg/js/en.js +0 -293
- package/msg/messages.js +0 -356
- package/msg/scratch_msgs.js +0 -23058
package/i18n/create_messages.py
DELETED
|
@@ -1,162 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
|
-
|
|
3
|
-
# Generate .js files defining Blockly core and language messages.
|
|
4
|
-
#
|
|
5
|
-
# Copyright 2013 Google Inc.
|
|
6
|
-
# https://developers.google.com/blockly/
|
|
7
|
-
#
|
|
8
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
-
# you may not use this file except in compliance with the License.
|
|
10
|
-
# You may obtain a copy of the License at
|
|
11
|
-
#
|
|
12
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
-
#
|
|
14
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
15
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
-
# See the License for the specific language governing permissions and
|
|
18
|
-
# limitations under the License.
|
|
19
|
-
|
|
20
|
-
import argparse
|
|
21
|
-
import codecs
|
|
22
|
-
import os
|
|
23
|
-
import re
|
|
24
|
-
import sys
|
|
25
|
-
from common import read_json_file
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
_NEWLINE_PATTERN = re.compile('[\n\r]')
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
def string_is_ascii(s):
|
|
32
|
-
# This approach is better for compatibility
|
|
33
|
-
return all(ord(c) < 128 for c in s)
|
|
34
|
-
|
|
35
|
-
def load_constants(filename):
|
|
36
|
-
"""Read in constants file, which must be output in every language."""
|
|
37
|
-
constant_defs = read_json_file(filename);
|
|
38
|
-
constants_text = '\n'
|
|
39
|
-
for key in constant_defs:
|
|
40
|
-
value = constant_defs[key]
|
|
41
|
-
value = value.replace('"', '\\"')
|
|
42
|
-
constants_text += '\nBlockly.Msg["{0}"] = "{1}";'.format(key, value)
|
|
43
|
-
return constants_text
|
|
44
|
-
|
|
45
|
-
def main():
|
|
46
|
-
"""Generate .js files defining Blockly core and language messages."""
|
|
47
|
-
|
|
48
|
-
# Process command-line arguments.
|
|
49
|
-
parser = argparse.ArgumentParser(description='Convert JSON files to JS.')
|
|
50
|
-
parser.add_argument('--source_lang', default='en',
|
|
51
|
-
help='ISO 639-1 source language code')
|
|
52
|
-
parser.add_argument('--source_lang_file',
|
|
53
|
-
default=os.path.join('json', 'en.json'),
|
|
54
|
-
help='Path to .json file for source language')
|
|
55
|
-
parser.add_argument('--source_synonym_file',
|
|
56
|
-
default=os.path.join('json', 'synonyms.json'),
|
|
57
|
-
help='Path to .json file with synonym definitions')
|
|
58
|
-
parser.add_argument('--source_constants_file',
|
|
59
|
-
default=os.path.join('json', 'constants.json'),
|
|
60
|
-
help='Path to .json file with constant definitions')
|
|
61
|
-
parser.add_argument('--output_dir', default='js/',
|
|
62
|
-
help='relative directory for output files')
|
|
63
|
-
parser.add_argument('--key_file', default='keys.json',
|
|
64
|
-
help='relative path to input keys file')
|
|
65
|
-
parser.add_argument('--quiet', action='store_true', default=False,
|
|
66
|
-
help='do not write anything to standard output')
|
|
67
|
-
parser.add_argument('files', nargs='+', help='input files')
|
|
68
|
-
args = parser.parse_args()
|
|
69
|
-
if not args.output_dir.endswith(os.path.sep):
|
|
70
|
-
args.output_dir += os.path.sep
|
|
71
|
-
|
|
72
|
-
# Read in source language .json file, which provides any values missing
|
|
73
|
-
# in target languages' .json files.
|
|
74
|
-
source_defs = read_json_file(os.path.join(os.curdir, args.source_lang_file))
|
|
75
|
-
# Make sure the source file doesn't contain a newline or carriage return.
|
|
76
|
-
for key, value in source_defs.items():
|
|
77
|
-
if _NEWLINE_PATTERN.search(value):
|
|
78
|
-
print('ERROR: definition of {0} in {1} contained a newline character.'.
|
|
79
|
-
format(key, args.source_lang_file))
|
|
80
|
-
sys.exit(1)
|
|
81
|
-
sorted_keys = sorted(source_defs.keys())
|
|
82
|
-
|
|
83
|
-
# Read in synonyms file, which must be output in every language.
|
|
84
|
-
synonym_defs = read_json_file(os.path.join(
|
|
85
|
-
os.curdir, args.source_synonym_file))
|
|
86
|
-
# synonym_defs is also being sorted to ensure the same order is kept
|
|
87
|
-
synonym_text = '\n'.join(['Blockly.Msg.{0} = Blockly.Msg.{1};'.format(
|
|
88
|
-
key, synonym_defs[key]) for key in sorted(synonym_defs)])
|
|
89
|
-
|
|
90
|
-
# Read in constants file, which must be output in every language.
|
|
91
|
-
constants_text = load_constants(os.path.join(os.curdir, args.source_constants_file))
|
|
92
|
-
|
|
93
|
-
# Create each output file.
|
|
94
|
-
for arg_file in args.files:
|
|
95
|
-
(_, filename) = os.path.split(arg_file)
|
|
96
|
-
target_lang = filename[:filename.index('.')]
|
|
97
|
-
if target_lang not in ('qqq', 'keys', 'synonyms', 'constants'):
|
|
98
|
-
target_defs = read_json_file(os.path.join(os.curdir, arg_file))
|
|
99
|
-
|
|
100
|
-
# Verify that keys are 'ascii'
|
|
101
|
-
bad_keys = [key for key in target_defs if not string_is_ascii(key)]
|
|
102
|
-
if bad_keys:
|
|
103
|
-
print(u'These keys in {0} contain non ascii characters: {1}'.format(
|
|
104
|
-
filename, ', '.join(bad_keys)))
|
|
105
|
-
|
|
106
|
-
# If there's a '\n' or '\r', remove it and print a warning.
|
|
107
|
-
for key, value in target_defs.items():
|
|
108
|
-
if _NEWLINE_PATTERN.search(value):
|
|
109
|
-
print(u'WARNING: definition of {0} in {1} contained '
|
|
110
|
-
'a newline character.'.
|
|
111
|
-
format(key, arg_file))
|
|
112
|
-
target_defs[key] = _NEWLINE_PATTERN.sub(' ', value)
|
|
113
|
-
|
|
114
|
-
# Output file.
|
|
115
|
-
outname = os.path.join(os.curdir, args.output_dir, target_lang + '.js')
|
|
116
|
-
with codecs.open(outname, 'w', 'utf-8') as outfile:
|
|
117
|
-
outfile.write(
|
|
118
|
-
"""// This file was automatically generated. Do not modify.
|
|
119
|
-
|
|
120
|
-
'use strict';
|
|
121
|
-
|
|
122
|
-
goog.provide('Blockly.Msg.{0}');
|
|
123
|
-
|
|
124
|
-
goog.require('Blockly.Msg');
|
|
125
|
-
|
|
126
|
-
""".format(target_lang.replace('-', '.')))
|
|
127
|
-
# For each key in the source language file, output the target value
|
|
128
|
-
# if present; otherwise, output the source language value with a
|
|
129
|
-
# warning comment.
|
|
130
|
-
for key in sorted_keys:
|
|
131
|
-
if key in target_defs:
|
|
132
|
-
value = target_defs[key]
|
|
133
|
-
comment = ''
|
|
134
|
-
del target_defs[key]
|
|
135
|
-
else:
|
|
136
|
-
value = source_defs[key]
|
|
137
|
-
comment = ' // untranslated'
|
|
138
|
-
value = value.replace('"', '\\"')
|
|
139
|
-
outfile.write(u'Blockly.Msg["{0}"] = "{1}";{2}\n'.format(
|
|
140
|
-
key, value, comment))
|
|
141
|
-
|
|
142
|
-
# Announce any keys defined only for target language.
|
|
143
|
-
if target_defs:
|
|
144
|
-
extra_keys = [key for key in target_defs if key not in synonym_defs]
|
|
145
|
-
synonym_keys = [key for key in target_defs if key in synonym_defs]
|
|
146
|
-
if not args.quiet:
|
|
147
|
-
if extra_keys:
|
|
148
|
-
print(u'These extra keys appeared in {0}: {1}'.format(
|
|
149
|
-
filename, ', '.join(extra_keys)))
|
|
150
|
-
if synonym_keys:
|
|
151
|
-
print(u'These synonym keys appeared in {0}: {1}'.format(
|
|
152
|
-
filename, ', '.join(synonym_keys)))
|
|
153
|
-
|
|
154
|
-
outfile.write(synonym_text)
|
|
155
|
-
outfile.write(constants_text)
|
|
156
|
-
|
|
157
|
-
if not args.quiet:
|
|
158
|
-
print('Created {0}.'.format(outname))
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
if __name__ == '__main__':
|
|
162
|
-
main()
|
|
@@ -1,68 +0,0 @@
|
|
|
1
|
-
const fs = require('fs')
|
|
2
|
-
const path = require('path')
|
|
3
|
-
const glob = require('glob')
|
|
4
|
-
const assert = require('assert')
|
|
5
|
-
|
|
6
|
-
// Globals
|
|
7
|
-
const PATH_INPUT = path.resolve(__dirname, '../msg/json/*.json')
|
|
8
|
-
const PATH_OUTPUT = path.resolve(__dirname, '../msg')
|
|
9
|
-
|
|
10
|
-
let en = fs.readFileSync(path.resolve(__dirname, '../msg/json/en.json'))
|
|
11
|
-
en = JSON.parse(en)
|
|
12
|
-
const enKeys = Object.keys(en).sort().toString()
|
|
13
|
-
|
|
14
|
-
// Check that translation is valid:
|
|
15
|
-
// entry: array [key, translation] corresponding to a single string from <locale>.json
|
|
16
|
-
// - messages with placeholders have the same number of placeholders
|
|
17
|
-
// - messages must not have newlines embedded
|
|
18
|
-
const validateEntry = function (entry) {
|
|
19
|
-
const re = /(%\d)/g
|
|
20
|
-
const [key, translation] = entry
|
|
21
|
-
const enMatch = en[key].match(re)
|
|
22
|
-
const tMatch = translation.match(re)
|
|
23
|
-
const enCount = enMatch ? enMatch.length : 0
|
|
24
|
-
const tCount = tMatch ? tMatch.length : 0
|
|
25
|
-
assert.strictEqual(tCount, enCount, `${key}:${en[key]} - "${translation}" placeholder mismatch`)
|
|
26
|
-
if (enCount > 0) {
|
|
27
|
-
assert.notStrictEqual(tMatch, null, `${key} is missing a placeholder: ${translation}`)
|
|
28
|
-
assert.strictEqual(
|
|
29
|
-
tMatch.sort().toString(),
|
|
30
|
-
enMatch.sort().toString(),
|
|
31
|
-
`${key} is missing or has duplicate placeholders: ${translation}`,
|
|
32
|
-
)
|
|
33
|
-
}
|
|
34
|
-
assert.strictEqual(translation.match(/[\n]/), null, `${key} contains a newline character ${translation}`)
|
|
35
|
-
}
|
|
36
|
-
|
|
37
|
-
const validate = function (json, name) {
|
|
38
|
-
assert.strictEqual(Object.keys(json).sort().toString(), enKeys, `${name}: Locale json keys do not match en.json`)
|
|
39
|
-
Object.entries(json).forEach(validateEntry)
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
let file = `// This file was automatically generated. Do not modify.
|
|
43
|
-
|
|
44
|
-
'use strict';
|
|
45
|
-
|
|
46
|
-
goog.provide('Blockly.ScratchMsgs.allLocales');
|
|
47
|
-
|
|
48
|
-
goog.require('Blockly.ScratchMsgs');
|
|
49
|
-
|
|
50
|
-
`
|
|
51
|
-
|
|
52
|
-
let files = glob.sync(PATH_INPUT)
|
|
53
|
-
files.forEach(function (uri) {
|
|
54
|
-
const name = path.parse(uri).name
|
|
55
|
-
if (name !== 'qqq' && name !== 'synonyms') {
|
|
56
|
-
let body = fs.readFileSync(uri)
|
|
57
|
-
// Convert file body into an object (let this throw if invalid JSON)
|
|
58
|
-
body = JSON.parse(body)
|
|
59
|
-
validate(body, name)
|
|
60
|
-
file += '\n'
|
|
61
|
-
file += `Blockly.ScratchMsgs.locales["${name}"] =\n`
|
|
62
|
-
file += JSON.stringify(body, null, 4)
|
|
63
|
-
file += ';\n'
|
|
64
|
-
}
|
|
65
|
-
})
|
|
66
|
-
|
|
67
|
-
// write combined file
|
|
68
|
-
fs.writeFileSync(`${PATH_OUTPUT}/scratch_msgs.js`, file)
|
package/i18n/dedup_json.py
DELETED
|
@@ -1,73 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
|
-
|
|
3
|
-
# Consolidates duplicate key-value pairs in a JSON file.
|
|
4
|
-
# If the same key is used with different values, no warning is given,
|
|
5
|
-
# and there is no guarantee about which key-value pair will be output.
|
|
6
|
-
# There is also no guarantee as to the order of the key-value pairs
|
|
7
|
-
# output.
|
|
8
|
-
#
|
|
9
|
-
# Copyright 2013 Google Inc.
|
|
10
|
-
# https://developers.google.com/blockly/
|
|
11
|
-
#
|
|
12
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
13
|
-
# you may not use this file except in compliance with the License.
|
|
14
|
-
# You may obtain a copy of the License at
|
|
15
|
-
#
|
|
16
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
17
|
-
#
|
|
18
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
19
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
20
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
21
|
-
# See the License for the specific language governing permissions and
|
|
22
|
-
# limitations under the License.
|
|
23
|
-
|
|
24
|
-
import argparse
|
|
25
|
-
import codecs
|
|
26
|
-
import json
|
|
27
|
-
from common import InputError
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
def main():
|
|
31
|
-
"""Parses arguments and iterates over files.
|
|
32
|
-
|
|
33
|
-
Raises:
|
|
34
|
-
IOError: An I/O error occurred with an input or output file.
|
|
35
|
-
InputError: Input JSON could not be parsed.
|
|
36
|
-
"""
|
|
37
|
-
|
|
38
|
-
# Set up argument parser.
|
|
39
|
-
parser = argparse.ArgumentParser(
|
|
40
|
-
description='Removes duplicate key-value pairs from JSON files.')
|
|
41
|
-
parser.add_argument('--suffix', default='',
|
|
42
|
-
help='optional suffix for output files; '
|
|
43
|
-
'if empty, files will be changed in place')
|
|
44
|
-
parser.add_argument('files', nargs='+', help='input files')
|
|
45
|
-
args = parser.parse_args()
|
|
46
|
-
|
|
47
|
-
# Iterate over files.
|
|
48
|
-
for filename in args.files:
|
|
49
|
-
# Read in json using Python libraries. This eliminates duplicates.
|
|
50
|
-
print('Processing ' + filename + '...')
|
|
51
|
-
try:
|
|
52
|
-
with codecs.open(filename, 'r', 'utf-8') as infile:
|
|
53
|
-
j = json.load(infile)
|
|
54
|
-
except ValueError as e:
|
|
55
|
-
print('Error reading ' + filename)
|
|
56
|
-
raise InputError(filename, str(e))
|
|
57
|
-
|
|
58
|
-
# Built up output strings as an array to make output of delimiters easier.
|
|
59
|
-
output = []
|
|
60
|
-
for key in j:
|
|
61
|
-
if key != '@metadata':
|
|
62
|
-
output.append('\t"' + key + '": "' +
|
|
63
|
-
j[key].replace('\n', '\\n') + '"')
|
|
64
|
-
|
|
65
|
-
# Output results.
|
|
66
|
-
with codecs.open(filename + args.suffix, 'w', 'utf-8') as outfile:
|
|
67
|
-
outfile.write('{\n')
|
|
68
|
-
outfile.write(',\n'.join(output))
|
|
69
|
-
outfile.write('\n}\n')
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
if __name__ == '__main__':
|
|
73
|
-
main()
|
package/i18n/js_to_json.js
DELETED
|
@@ -1,54 +0,0 @@
|
|
|
1
|
-
const es = require('event-stream')
|
|
2
|
-
const fs = require('fs')
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const assert = require('assert')
|
|
5
|
-
|
|
6
|
-
// Storage object
|
|
7
|
-
const storage = {}
|
|
8
|
-
|
|
9
|
-
// File paths
|
|
10
|
-
const PATH_INPUT = path.resolve(__dirname, '../msg/messages.js')
|
|
11
|
-
const PATH_OUTPUT = path.resolve(__dirname, '../msg/json/en.json')
|
|
12
|
-
|
|
13
|
-
// Match function
|
|
14
|
-
const match = function (str) {
|
|
15
|
-
if (str.indexOf('Blockly.Msg.') !== 0) return false
|
|
16
|
-
assert.notStrictEqual(
|
|
17
|
-
str.indexOf('";'),
|
|
18
|
-
str.length - 2,
|
|
19
|
-
`[${str}] uses double quoted string, should use single quotes.`,
|
|
20
|
-
)
|
|
21
|
-
if (str.indexOf("';") !== str.length - 2) return false
|
|
22
|
-
return true
|
|
23
|
-
}
|
|
24
|
-
|
|
25
|
-
// Extract key and value from message definition
|
|
26
|
-
const extract = function (str) {
|
|
27
|
-
str = str.split('Blockly.Msg.')[1].split(' ')
|
|
28
|
-
return {
|
|
29
|
-
key: str[0],
|
|
30
|
-
value: str
|
|
31
|
-
.splice(2, str.length)
|
|
32
|
-
.join(' ')
|
|
33
|
-
.slice(1, -2) // strip off initial ', and ending ';
|
|
34
|
-
.replace(/\\'/g, "'"),
|
|
35
|
-
}
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Stream input and push each match to the storage object
|
|
39
|
-
const stream = fs.createReadStream(PATH_INPUT)
|
|
40
|
-
stream
|
|
41
|
-
.pipe(es.split('\n'))
|
|
42
|
-
.pipe(
|
|
43
|
-
es.mapSync(function (str) {
|
|
44
|
-
if (!match(str)) return
|
|
45
|
-
const result = extract(str)
|
|
46
|
-
storage[result.key] = result.value
|
|
47
|
-
}),
|
|
48
|
-
)
|
|
49
|
-
.pipe(
|
|
50
|
-
es.wait(function (err) {
|
|
51
|
-
if (err) throw new Error(err)
|
|
52
|
-
fs.writeFileSync(PATH_OUTPUT, JSON.stringify(storage, null, 4))
|
|
53
|
-
}),
|
|
54
|
-
)
|
package/i18n/js_to_json.py
DELETED
|
@@ -1,136 +0,0 @@
|
|
|
1
|
-
#!/usr/bin/python
|
|
2
|
-
|
|
3
|
-
# Gives the translation status of the specified apps and languages.
|
|
4
|
-
#
|
|
5
|
-
# Copyright 2013 Google Inc.
|
|
6
|
-
# https://developers.google.com/blockly/
|
|
7
|
-
#
|
|
8
|
-
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
9
|
-
# you may not use this file except in compliance with the License.
|
|
10
|
-
# You may obtain a copy of the License at
|
|
11
|
-
#
|
|
12
|
-
# http://www.apache.org/licenses/LICENSE-2.0
|
|
13
|
-
#
|
|
14
|
-
# Unless required by applicable law or agreed to in writing, software
|
|
15
|
-
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
16
|
-
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
17
|
-
# See the License for the specific language governing permissions and
|
|
18
|
-
# limitations under the License.
|
|
19
|
-
|
|
20
|
-
"""Extracts messages from .js files into .json files for translation.
|
|
21
|
-
|
|
22
|
-
Specifically, lines with the following formats are extracted:
|
|
23
|
-
|
|
24
|
-
/// Here is a description of the following message.
|
|
25
|
-
Blockly.SOME_KEY = 'Some value';
|
|
26
|
-
|
|
27
|
-
Adjacent "///" lines are concatenated.
|
|
28
|
-
|
|
29
|
-
There are two output files, each of which is proper JSON. For each key, the
|
|
30
|
-
file en.json would get an entry of the form:
|
|
31
|
-
|
|
32
|
-
"Blockly.SOME_KEY", "Some value",
|
|
33
|
-
|
|
34
|
-
The file qqq.json would get:
|
|
35
|
-
|
|
36
|
-
"Blockly.SOME_KEY", "Here is a description of the following message.",
|
|
37
|
-
|
|
38
|
-
Commas would of course be omitted for the final entry of each value.
|
|
39
|
-
|
|
40
|
-
@author Ellen Spertus (ellen.spertus@gmail.com)
|
|
41
|
-
"""
|
|
42
|
-
|
|
43
|
-
import argparse
|
|
44
|
-
import codecs
|
|
45
|
-
import json
|
|
46
|
-
import os
|
|
47
|
-
import re
|
|
48
|
-
from common import write_files
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
_INPUT_DEF_PATTERN = re.compile("""Blockly.Msg.(\w*)\s*=\s*'(.*)';?\r?$""")
|
|
52
|
-
|
|
53
|
-
_INPUT_SYN_PATTERN = re.compile(
|
|
54
|
-
"""Blockly.Msg.(\w*)\s*=\s*Blockly.Msg.(\w*);""")
|
|
55
|
-
|
|
56
|
-
_CONSTANT_DESCRIPTION_PATTERN = re.compile(
|
|
57
|
-
"""{{Notranslate}}""", re.IGNORECASE)
|
|
58
|
-
|
|
59
|
-
def main():
|
|
60
|
-
# Set up argument parser.
|
|
61
|
-
parser = argparse.ArgumentParser(description='Create translation files.')
|
|
62
|
-
parser.add_argument(
|
|
63
|
-
'--author',
|
|
64
|
-
default='Ellen Spertus <ellen.spertus@gmail.com>',
|
|
65
|
-
help='name and email address of contact for translators')
|
|
66
|
-
parser.add_argument('--lang', default='en',
|
|
67
|
-
help='ISO 639-1 source language code')
|
|
68
|
-
parser.add_argument('--output_dir', default='json',
|
|
69
|
-
help='relative directory for output files')
|
|
70
|
-
parser.add_argument('--input_file', default='messages.js',
|
|
71
|
-
help='input file')
|
|
72
|
-
parser.add_argument('--quiet', action='store_true', default=False,
|
|
73
|
-
help='only display warnings, not routine info')
|
|
74
|
-
args = parser.parse_args()
|
|
75
|
-
if (not args.output_dir.endswith(os.path.sep)):
|
|
76
|
-
args.output_dir += os.path.sep
|
|
77
|
-
|
|
78
|
-
# Read and parse input file.
|
|
79
|
-
results = []
|
|
80
|
-
synonyms = {}
|
|
81
|
-
constants = {} # Values that are constant across all languages.
|
|
82
|
-
description = ''
|
|
83
|
-
infile = codecs.open(args.input_file, 'r', 'utf-8')
|
|
84
|
-
for line in infile:
|
|
85
|
-
if line.startswith('///'):
|
|
86
|
-
if description:
|
|
87
|
-
description = description + ' ' + line[3:].strip()
|
|
88
|
-
else:
|
|
89
|
-
description = line[3:].strip()
|
|
90
|
-
else:
|
|
91
|
-
match = _INPUT_DEF_PATTERN.match(line)
|
|
92
|
-
if match:
|
|
93
|
-
key = match.group(1)
|
|
94
|
-
value = match.group(2).replace("\\'", "'")
|
|
95
|
-
if not description:
|
|
96
|
-
print('Warning: No description for ' + result['meaning'])
|
|
97
|
-
if (description and _CONSTANT_DESCRIPTION_PATTERN.search(description)):
|
|
98
|
-
constants[key] = value
|
|
99
|
-
else:
|
|
100
|
-
result = {}
|
|
101
|
-
result['meaning'] = key
|
|
102
|
-
result['source'] = value
|
|
103
|
-
result['description'] = description
|
|
104
|
-
results.append(result)
|
|
105
|
-
description = ''
|
|
106
|
-
else:
|
|
107
|
-
match = _INPUT_SYN_PATTERN.match(line)
|
|
108
|
-
if match:
|
|
109
|
-
if description:
|
|
110
|
-
print('Warning: Description preceding definition of synonym {0}.'.
|
|
111
|
-
format(match.group(1)))
|
|
112
|
-
description = ''
|
|
113
|
-
synonyms[match.group(1)] = match.group(2)
|
|
114
|
-
infile.close()
|
|
115
|
-
|
|
116
|
-
# Create <lang_file>.json, keys.json, and qqq.json.
|
|
117
|
-
write_files(args.author, args.lang, args.output_dir, results, False)
|
|
118
|
-
|
|
119
|
-
# Create synonyms.json.
|
|
120
|
-
synonym_file_name = os.path.join(os.curdir, args.output_dir, 'synonyms.json')
|
|
121
|
-
with open(synonym_file_name, 'w') as outfile:
|
|
122
|
-
json.dump(synonyms, outfile)
|
|
123
|
-
if not args.quiet:
|
|
124
|
-
print("Wrote {0} synonym pairs to {1}.".format(
|
|
125
|
-
len(synonyms), synonym_file_name))
|
|
126
|
-
|
|
127
|
-
# Create constants.json
|
|
128
|
-
constants_file_name = os.path.join(os.curdir, args.output_dir, 'constants.json')
|
|
129
|
-
with open(constants_file_name, 'w') as outfile:
|
|
130
|
-
json.dump(constants, outfile)
|
|
131
|
-
if not args.quiet:
|
|
132
|
-
print("Wrote {0} constant pairs to {1}.".format(
|
|
133
|
-
len(constants), synonym_file_name))
|
|
134
|
-
|
|
135
|
-
if __name__ == '__main__':
|
|
136
|
-
main()
|
package/i18n/json_to_js.js
DELETED
|
@@ -1,52 +0,0 @@
|
|
|
1
|
-
const async = require('async')
|
|
2
|
-
const fs = require('fs')
|
|
3
|
-
const path = require('path')
|
|
4
|
-
const glob = require('glob')
|
|
5
|
-
|
|
6
|
-
// Globals
|
|
7
|
-
const PATH_INPUT = path.resolve(__dirname, '../msg/json/en.json')
|
|
8
|
-
// If you want to generate js files for other languages, comment out the line above,
|
|
9
|
-
// and use the one below instead.
|
|
10
|
-
// const PATH_INPUT = path.resolve(__dirname, '../msg/json/*.json');
|
|
11
|
-
const PATH_OUTPUT = path.resolve(__dirname, '../msg/js')
|
|
12
|
-
const CONCURRENCY_LIMIT = 4
|
|
13
|
-
|
|
14
|
-
// Processing task
|
|
15
|
-
const work = function (uri, callback) {
|
|
16
|
-
fs.readFile(uri, function (err, body) {
|
|
17
|
-
const name = path.parse(uri).name
|
|
18
|
-
if (err) return callback(err)
|
|
19
|
-
|
|
20
|
-
// Convert file body into an object (let this throw if invalid JSON)
|
|
21
|
-
body = JSON.parse(body)
|
|
22
|
-
|
|
23
|
-
// File storage object and preamble
|
|
24
|
-
let file = ''
|
|
25
|
-
file += '// This file was automatically generated. Do not modify.\n'
|
|
26
|
-
file += '\n'
|
|
27
|
-
file += "'use strict';\n"
|
|
28
|
-
file += '\n'
|
|
29
|
-
file += `goog.provide(\'Blockly.Msg.${name}\');\n`
|
|
30
|
-
file += "goog.require('Blockly.Msg');\n"
|
|
31
|
-
file += '\n'
|
|
32
|
-
|
|
33
|
-
// Iterate over object and build up file
|
|
34
|
-
for (let i in body) {
|
|
35
|
-
file += `Blockly.Msg["${i}"] = "${body[i].replace(/"/g, '\\"')}";\n`
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
// Write file to disk
|
|
39
|
-
fs.writeFile(`${PATH_OUTPUT}/${name}.js`, file, callback)
|
|
40
|
-
})
|
|
41
|
-
}
|
|
42
|
-
|
|
43
|
-
// Create async processing queue
|
|
44
|
-
const q = async.queue(work, CONCURRENCY_LIMIT)
|
|
45
|
-
|
|
46
|
-
// Handle errors
|
|
47
|
-
q.error = function (err) {
|
|
48
|
-
throw new Error(err)
|
|
49
|
-
}
|
|
50
|
-
|
|
51
|
-
// Get all JSON files in input directory and add to queue
|
|
52
|
-
q.push(glob.sync(PATH_INPUT))
|