translatomatic 0.1.3 → 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.
Files changed (139) hide show
  1. checksums.yaml +5 -5
  2. data/.gitattributes +20 -20
  3. data/.gitignore +19 -15
  4. data/.rspec +3 -3
  5. data/.rubocop.yml +28 -0
  6. data/.translatomatic/config.yml +4 -0
  7. data/.travis.yml +4 -6
  8. data/.yardopts +9 -9
  9. data/Gemfile +8 -4
  10. data/Guardfile +4 -5
  11. data/README.de.md +55 -50
  12. data/README.en.md +177 -0
  13. data/README.es.md +53 -48
  14. data/README.fr.md +53 -48
  15. data/README.it.md +54 -49
  16. data/README.ja.md +63 -58
  17. data/README.ko.md +59 -54
  18. data/README.md +17 -13
  19. data/README.ms.md +50 -45
  20. data/README.pt.md +54 -49
  21. data/README.ru.md +57 -52
  22. data/README.sv.md +51 -46
  23. data/README.zh.md +60 -55
  24. data/Rakefile +3 -3
  25. data/TODO.txt +6 -0
  26. data/bin/console +3 -3
  27. data/bin/translatomatic +4 -2
  28. data/config/i18n-tasks.yml +130 -0
  29. data/config/locales/translatomatic/de.yml +141 -99
  30. data/config/locales/translatomatic/en.yml +129 -89
  31. data/config/locales/translatomatic/es.yml +136 -99
  32. data/config/locales/translatomatic/fr.yml +139 -100
  33. data/config/locales/translatomatic/it.yml +135 -97
  34. data/config/locales/translatomatic/ja.yml +137 -98
  35. data/config/locales/translatomatic/ko.yml +138 -98
  36. data/config/locales/translatomatic/ms.yml +138 -100
  37. data/config/locales/translatomatic/pt.yml +137 -101
  38. data/config/locales/translatomatic/ru.yml +136 -98
  39. data/config/locales/translatomatic/sv.yml +134 -96
  40. data/config/locales/translatomatic/zh.yml +136 -97
  41. data/db/migrate/201712170000_initial.rb +2 -3
  42. data/lib/translatomatic.rb +40 -25
  43. data/lib/translatomatic/cli.rb +5 -1
  44. data/lib/translatomatic/cli/base.rb +61 -58
  45. data/lib/translatomatic/cli/common_options.rb +14 -11
  46. data/lib/translatomatic/cli/config.rb +96 -91
  47. data/lib/translatomatic/cli/database.rb +85 -23
  48. data/lib/translatomatic/cli/main.rb +158 -104
  49. data/lib/translatomatic/cli/thor.rb +29 -0
  50. data/lib/translatomatic/cli/translate.rb +134 -157
  51. data/lib/translatomatic/config.rb +10 -301
  52. data/lib/translatomatic/config/display.rb +78 -0
  53. data/lib/translatomatic/config/files.rb +60 -0
  54. data/lib/translatomatic/config/location_settings.rb +133 -0
  55. data/lib/translatomatic/config/options.rb +68 -0
  56. data/lib/translatomatic/config/selector.rb +127 -0
  57. data/lib/translatomatic/config/settings.rb +148 -0
  58. data/lib/translatomatic/converter.rb +40 -28
  59. data/lib/translatomatic/database.rb +127 -110
  60. data/lib/translatomatic/define_options.rb +4 -5
  61. data/lib/translatomatic/escaped_unicode.rb +86 -76
  62. data/lib/translatomatic/extractor.rb +5 -2
  63. data/lib/translatomatic/extractor/base.rb +12 -12
  64. data/lib/translatomatic/extractor/ruby.rb +7 -6
  65. data/lib/translatomatic/file_translator.rb +101 -244
  66. data/lib/translatomatic/flattenation.rb +39 -0
  67. data/lib/translatomatic/http.rb +13 -0
  68. data/lib/translatomatic/http/client.rb +144 -0
  69. data/lib/translatomatic/http/exception.rb +43 -0
  70. data/lib/translatomatic/http/file_param.rb +27 -0
  71. data/lib/translatomatic/http/param.rb +37 -0
  72. data/lib/translatomatic/http/request.rb +91 -0
  73. data/lib/translatomatic/i18n.rb +43 -0
  74. data/lib/translatomatic/locale.rb +71 -59
  75. data/lib/translatomatic/logger.rb +43 -28
  76. data/lib/translatomatic/metadata.rb +58 -0
  77. data/lib/translatomatic/model.rb +4 -2
  78. data/lib/translatomatic/model/locale.rb +5 -5
  79. data/lib/translatomatic/model/text.rb +5 -5
  80. data/lib/translatomatic/option.rb +57 -34
  81. data/lib/translatomatic/path_utils.rb +126 -0
  82. data/lib/translatomatic/progress_updater.rb +13 -16
  83. data/lib/translatomatic/provider.rb +101 -0
  84. data/lib/translatomatic/provider/base.rb +136 -0
  85. data/lib/translatomatic/provider/frengly.rb +55 -0
  86. data/lib/translatomatic/provider/google.rb +78 -0
  87. data/lib/translatomatic/provider/google_web.rb +50 -0
  88. data/lib/translatomatic/provider/microsoft.rb +144 -0
  89. data/lib/translatomatic/provider/my_memory.rb +75 -0
  90. data/lib/translatomatic/provider/yandex.rb +61 -0
  91. data/lib/translatomatic/resource_file.rb +59 -53
  92. data/lib/translatomatic/resource_file/base.rb +171 -237
  93. data/lib/translatomatic/resource_file/csv.rb +176 -24
  94. data/lib/translatomatic/resource_file/html.rb +21 -42
  95. data/lib/translatomatic/resource_file/key_value_support.rb +117 -0
  96. data/lib/translatomatic/resource_file/markdown.rb +36 -38
  97. data/lib/translatomatic/resource_file/plist.rb +121 -126
  98. data/lib/translatomatic/resource_file/po.rb +104 -82
  99. data/lib/translatomatic/resource_file/properties.rb +48 -77
  100. data/lib/translatomatic/resource_file/properties.treetop +87 -0
  101. data/lib/translatomatic/resource_file/resw.rb +56 -41
  102. data/lib/translatomatic/resource_file/subtitle.rb +86 -54
  103. data/lib/translatomatic/resource_file/text.rb +18 -18
  104. data/lib/translatomatic/resource_file/xcode_strings.rb +32 -63
  105. data/lib/translatomatic/resource_file/xcode_strings.treetop +85 -0
  106. data/lib/translatomatic/resource_file/xml.rb +94 -81
  107. data/lib/translatomatic/resource_file/yaml.rb +54 -68
  108. data/lib/translatomatic/retry_executor.rb +37 -0
  109. data/lib/translatomatic/slurp.rb +32 -0
  110. data/lib/translatomatic/string_batcher.rb +50 -0
  111. data/lib/translatomatic/string_escaping.rb +61 -0
  112. data/lib/translatomatic/text.rb +263 -0
  113. data/lib/translatomatic/text_collection.rb +66 -0
  114. data/lib/translatomatic/tmx.rb +5 -3
  115. data/lib/translatomatic/tmx/document.rb +107 -82
  116. data/lib/translatomatic/tmx/translation_unit.rb +19 -18
  117. data/lib/translatomatic/translation.rb +8 -28
  118. data/lib/translatomatic/translation/collection.rb +199 -0
  119. data/lib/translatomatic/translation/fetcher.rb +123 -0
  120. data/lib/translatomatic/translation/munging.rb +112 -0
  121. data/lib/translatomatic/translation/result.rb +50 -0
  122. data/lib/translatomatic/translation/sharer.rb +32 -0
  123. data/lib/translatomatic/translation/stats.rb +44 -0
  124. data/lib/translatomatic/translator.rb +91 -88
  125. data/lib/translatomatic/type_cast.rb +63 -0
  126. data/lib/translatomatic/util.rb +37 -33
  127. data/lib/translatomatic/version.rb +2 -2
  128. data/translatomatic.gemspec +57 -46
  129. metadata +136 -59
  130. data/lib/translatomatic/http_request.rb +0 -162
  131. data/lib/translatomatic/string.rb +0 -188
  132. data/lib/translatomatic/translation_result.rb +0 -86
  133. data/lib/translatomatic/translation_stats.rb +0 -31
  134. data/lib/translatomatic/translator/base.rb +0 -128
  135. data/lib/translatomatic/translator/frengly.rb +0 -62
  136. data/lib/translatomatic/translator/google.rb +0 -37
  137. data/lib/translatomatic/translator/microsoft.rb +0 -41
  138. data/lib/translatomatic/translator/my_memory.rb +0 -68
  139. data/lib/translatomatic/translator/yandex.rb +0 -56
@@ -1,36 +1,48 @@
1
- # Converts files from one format to another
2
- class Translatomatic::Converter
1
+ module Translatomatic
2
+ # Converts files from one format to another
3
+ class Converter
4
+ def initialize(options = {})
5
+ @options = options
6
+ end
3
7
 
4
- def initialize(options = {})
5
- @options = options
6
- end
8
+ # Convert a resource file from one format to another.
9
+ # @param source [String] Path to source file. File must exist
10
+ # @param target [String] Path to target file. File will be created
11
+ # or overwritten if it already exists.
12
+ # @return [Translatomatic::ResourceFile] The converted file.
13
+ def convert(source, target)
14
+ source_file = load_file(source)
15
+ target_file = load_file(target)
16
+ raise t('file.not_found', file: source) unless source_file.path.file?
7
17
 
8
- # Convert a resource file from one format to another.
9
- # @param source [String] Path to source file. File must exist
10
- # @param target [String] Path to target file. File will be created
11
- # or overwritten if it already exists.
12
- # @return [Translatomatic::ResourceFile] The converted file.
13
- def convert(source, target)
14
- source_path = Pathname.new(source.to_s)
15
- target_path = Pathname.new(target.to_s)
16
- raise t("file.not_found", file: source.to_s) unless source_path.file?
17
- raise t("file.directory", file: target.to_s) if target_path.directory?
18
+ if source_file.type == target_file.type
19
+ # if same file type, modify source.
20
+ # this retains internal structure
21
+ target_file = source_file
22
+ else
23
+ # different file type, copy properties from source file to target
24
+ target_file.properties = source_file.properties
25
+ end
18
26
 
19
- source_file = load_file(source_path)
20
- target_file = load_file(target_path)
27
+ target_file.save(Pathname.new(target), @options)
28
+ target_file
29
+ end
21
30
 
22
- # copy properties from source file to target
23
- target_file.properties = source_file.properties
24
- target_file.save(target_path, @options)
25
- target_file
26
- end
31
+ private
27
32
 
28
- private
33
+ include Translatomatic::Util
29
34
 
30
- def load_file(path)
31
- file = Translatomatic::ResourceFile.load(path)
32
- raise t("file.unsupported", file: path) unless file
33
- file
34
- end
35
+ def load_file(path)
36
+ path = Pathname.new(path.to_s)
37
+ raise t('file.directory', file: path) if path.directory?
35
38
 
39
+ file = if path.exist?
40
+ Translatomatic::ResourceFile.load(path)
41
+ else
42
+ Translatomatic::ResourceFile.create(path)
43
+ end
44
+ raise t('file.unsupported', file: path) unless file
45
+ file
46
+ end
47
+ end
36
48
  end
@@ -1,143 +1,160 @@
1
1
  require 'active_record'
2
2
 
3
- # Database functions
4
- class Translatomatic::Database
5
-
6
- class << self
7
- # @param options [Hash<Symbol,Object>] Database options
8
- # @return [boolean] True if we can connect to the database
9
- def enabled?(options = {})
10
- new(options).connect
3
+ module Translatomatic
4
+ # Database functions
5
+ class Database
6
+ class << self
7
+ # @param options [Hash<Symbol,Object>] Database options
8
+ # @return [boolean] True if we can connect to the database
9
+ def enabled?(options = {})
10
+ new(options).connect
11
+ end
11
12
  end
12
- end
13
13
 
14
- def initialize(options = {})
15
- @env = options[:database_env] || DEFAULT_ENV
16
- @db_config = database_config(@env, options)
17
- @env_config = @db_config
18
- raise t("database.no_environment",
19
- env: @env, file: db_config_path) unless @env_config[@env]
20
- @env_config = @env_config[@env] || {}
21
-
22
- ActiveRecord::Base.configurations = @db_config
23
- ActiveRecord::Tasks::DatabaseTasks.env = @env
24
- ActiveRecord::Tasks::DatabaseTasks.db_dir = DB_PATH
25
- ActiveRecord::Tasks::DatabaseTasks.root = DB_PATH
26
- ActiveRecord::Tasks::DatabaseTasks.database_configuration = @db_config
27
- create unless exists?
28
- migrate
29
- end
14
+ def initialize(options = {})
15
+ @env = options[:database_env] || DEFAULT_ENV
16
+ @db_config = database_config(@env, options)
17
+ env_config = @db_config
18
+ unless env_config[@env]
19
+ raise t('database.no_environment', env: @env, file: db_config_path)
20
+ end
21
+ @env_config = env_config[@env] || {}
22
+ init_active_record
23
+ create unless exists?
24
+ end
30
25
 
31
- # Connect to the database
32
- # @return [boolean] True if the connection was established
33
- def connect
34
- begin
26
+ # Connect to the database
27
+ # @return [boolean] True if the connection was established
28
+ def connect
35
29
  ActiveRecord::Base.establish_connection(@env_config)
30
+ migrate
36
31
  true
37
32
  rescue LoadError
38
33
  false
39
34
  end
40
- end
41
35
 
42
- # Disconnect from the database
43
- # @return [void]
44
- def disconnect
45
- ActiveRecord::Base.remove_connection
46
- end
36
+ # Disconnect from the database
37
+ # @return [void]
38
+ def disconnect
39
+ ActiveRecord::Base.remove_connection
40
+ end
47
41
 
48
- # Test if the database exists
49
- # @return [Boolean] true if the database exists
50
- def exists?
51
- begin
52
- return true if sqlite_database_exists?
53
- return false unless connect
54
- ActiveRecord::Base.connection.tables
55
- rescue
56
- return false
42
+ # Test if the database exists
43
+ # @return [Boolean] true if the database exists
44
+ def exists?
45
+ begin
46
+ return true if sqlite_database_exists?
47
+ return false unless connect
48
+ ActiveRecord::Base.connection.tables
49
+ rescue StandardError
50
+ return false
51
+ end
52
+ true
57
53
  end
58
- true
59
- end
60
54
 
61
- # Run outstanding migrations against the database
62
- # @return [void]
63
- def migrate
64
- return false unless connect
65
- ActiveRecord::Migrator.migrate(MIGRATIONS_PATH)
66
- ActiveRecord::Base.clear_cache!
67
- log.debug t("database.migrated")
68
- end
55
+ # Run outstanding migrations against the database
56
+ # @return [void]
57
+ def migrate
58
+ return if @migrated
59
+ ActiveRecord::Migrator.migrate(MIGRATIONS_PATH)
60
+ ActiveRecord::Base.clear_cache!
61
+ log.debug t('database.migrated')
62
+ @migrated = true
63
+ end
69
64
 
70
- # Create the database
71
- # @return [boolean] True if the database was created
72
- def create
73
- begin
65
+ # Create the database
66
+ # @return [boolean] True if the database was created
67
+ def create
74
68
  ActiveRecord::Tasks::DatabaseTasks.create(@env_config)
75
- log.debug t("database.created")
69
+ log.debug t('database.created')
76
70
  true
77
71
  rescue LoadError => e
78
- log.debug t("database.could_not_create")
72
+ log.debug t('database.could_not_create')
79
73
  log.error e.message
80
74
  false
81
75
  end
82
- end
83
76
 
84
- # Drop the database
85
- # @return [void]
86
- def drop
87
- disconnect
88
- ActiveRecord::Tasks::DatabaseTasks.drop(@env_config)
89
- log.debug t("database.deleted")
90
- end
77
+ # Drop the database
78
+ # @return [void]
79
+ def drop
80
+ disconnect
81
+ ActiveRecord::Tasks::DatabaseTasks.drop(@env_config)
82
+ log.debug t('database.deleted')
83
+ end
91
84
 
92
- private
85
+ # Shortcut to text model class
86
+ # @return [Class] Translatomatic::Model::Text
87
+ def text
88
+ Translatomatic::Model::Text
89
+ end
93
90
 
94
- include Translatomatic::Util
95
- include Translatomatic::DefineOptions
91
+ # Shortcut to locale model class
92
+ # @return [Class] Translatomatic::Model::Locale
93
+ def locale
94
+ Translatomatic::Model::Locale
95
+ end
96
96
 
97
- def sqlite_database_exists?
98
- @env_config['adapter'] == 'sqlite3' && File.exist?(@env_config['database'])
99
- end
97
+ private
100
98
 
101
- def self.join_path(*parts)
102
- File.realpath(File.join(*parts))
103
- end
99
+ include Translatomatic::Util
100
+ include Translatomatic::DefineOptions
101
+
102
+ class << self
103
+ private
104
104
 
105
- DB_PATH = join_path(File.dirname(__FILE__), "..", "..", "db")
106
- INTERNAL_CONFIG = File.join(DB_PATH, "database.yml")
107
- CUSTOM_CONFIG = File.join(Dir.home, ".translatomatic", "database.yml")
108
- DEFAULT_CONFIG = File.exist?(CUSTOM_CONFIG) ? CUSTOM_CONFIG : INTERNAL_CONFIG
109
- MIGRATIONS_PATH = File.join(DB_PATH, "migrate")
110
- GEM_ROOT = join_path(File.dirname(__FILE__), "..", "..")
111
- DEFAULT_ENV = "production"
112
-
113
- define_options(
114
- { name: :database_config, desc: t("database.config_file"),
115
- default: DEFAULT_CONFIG },
116
- { name: :database_env, desc: t("database.env"),
117
- default: DEFAULT_ENV })
118
-
119
- # return path to database config
120
- def database_config_path(options)
121
- if options[:database_env] == "test"
122
- INTERNAL_CONFIG # rspec
123
- elsif options[:database_config]
124
- return options[:database_config]
125
- else
126
- DEFAULT_CONFIG
105
+ def join_path(*parts)
106
+ File.realpath(File.join(*parts))
107
+ end
127
108
  end
128
- end
129
109
 
130
- # return database config as a hash
131
- def database_config(env, options)
132
- if options[:database_config].kind_of?(Hash)
133
- return { env => options[:database_config] }
110
+ def init_active_record
111
+ ActiveRecord::Base.configurations = @db_config
112
+ ActiveRecord::Tasks::DatabaseTasks.env = @env
113
+ ActiveRecord::Tasks::DatabaseTasks.db_dir = DB_PATH
114
+ ActiveRecord::Tasks::DatabaseTasks.root = DB_PATH
115
+ ActiveRecord::Tasks::DatabaseTasks.database_configuration = @db_config
134
116
  end
135
117
 
136
- db_config_path = database_config_path(options)
137
- dbconfig = File.read(db_config_path)
138
- dbconfig.gsub!(/\$HOME/, Dir.home)
139
- dbconfig.gsub!(/\$GEM_ROOT/, GEM_ROOT)
140
- YAML::load(dbconfig) || {}
141
- end
118
+ def sqlite_database_exists?
119
+ @env_config['adapter'] == 'sqlite3' &&
120
+ File.exist?(@env_config['database'])
121
+ end
122
+
123
+ DB_PATH = join_path(File.dirname(__FILE__), '..', '..', 'db')
124
+ INTERNAL_CONF = File.join(DB_PATH, 'database.yml')
125
+ CUSTOM_CONF = File.join(Dir.home, '.translatomatic', 'database.yml')
126
+ DEFAULT_CONF = File.exist?(CUSTOM_CONF) ? CUSTOM_CONF : INTERNAL_CONF
127
+ MIGRATIONS_PATH = File.join(DB_PATH, 'migrate')
128
+ GEM_ROOT = join_path(File.dirname(__FILE__), '..', '..')
129
+ DEFAULT_ENV = 'production'.freeze
130
+
131
+ define_option :database_config, desc: t('database.config_file'),
132
+ default: DEFAULT_CONF, type: :path
133
+ define_option :database_env, desc: t('database.env'),
134
+ default: DEFAULT_ENV
135
+
136
+ # return path to database config
137
+ def database_config_path(options)
138
+ if options[:database_env] == 'test'
139
+ INTERNAL_CONF # used for rspec
140
+ elsif options[:database_config]
141
+ options[:database_config]
142
+ else
143
+ DEFAULT_CONF
144
+ end
145
+ end
142
146
 
147
+ # return database config as a hash
148
+ def database_config(env, options)
149
+ if options[:database_config].is_a?(Hash)
150
+ return { env => options[:database_config] }
151
+ end
152
+
153
+ db_config_path = database_config_path(options)
154
+ dbconfig = File.read(db_config_path)
155
+ dbconfig.gsub!(/\$HOME/, Dir.home)
156
+ dbconfig.gsub!(/\$GEM_ROOT/, GEM_ROOT)
157
+ YAML.safe_load(dbconfig) || {}
158
+ end
159
+ end
143
160
  end
@@ -1,20 +1,19 @@
1
1
  module Translatomatic
2
2
  # @!visibility private
3
3
  module DefineOptions
4
-
5
4
  # @!visibility private
6
5
  module ClassMethods
7
6
  attr_reader :options
8
7
 
9
8
  private
10
9
 
11
- def define_options(*options)
12
- @options = options.collect { |i| Translatomatic::Option.new(i) }
10
+ def define_option(name, attributes = {})
11
+ @options ||= []
12
+ @options << Translatomatic::Option.new(attributes.merge(name: name))
13
13
  end
14
14
  end
15
15
 
16
- private
17
-
16
+ # @!visibility private
18
17
  def self.included(klass)
19
18
  klass.extend(ClassMethods)
20
19
  end
@@ -1,90 +1,100 @@
1
- # Module to encode and decode unicode chars.
2
- # This code is highly influced by Florian Frank's JSON gem
3
- # @see https://github.com/jnbt/java-properties
4
- # @see https://github.com/flori/json/
1
+ module Translatomatic
2
+ # Class to encode and decode unicode chars.
3
+ # This code is highly influenced by Florian Frank's JSON gem
4
+ # @see https://github.com/jnbt/java-properties
5
+ # @see https://github.com/flori/json/
6
+ class EscapedUnicode
7
+ class << self
8
+ # Decodes all unicode chars from escape sequences
9
+ # @param text [String]
10
+ # @return [String] The encoded text
11
+ def unescape(text)
12
+ string = text.gsub(/(?:\\[uU](?:[A-Fa-f\d]{4}))+/) do |c|
13
+ unescape_char(c)
14
+ end
15
+ string.force_encoding(::Encoding::UTF_8)
16
+ string
17
+ end
18
+
19
+ # Decodes all unicode chars into escape sequences
20
+ # @param text [String]
21
+ # @return [String] The decoded text
22
+ def escape(text)
23
+ string = text.dup
24
+ string.force_encoding(::Encoding::ASCII_8BIT)
25
+ string.gsub!(/["\\\x0-\x1f]/n) { |c| MAP[c] || c }
26
+ string.gsub!(ESCAPE_REGEX) { |c| escape_char(c) }
27
+ string.force_encoding(::Encoding::UTF_8)
28
+ string
29
+ end
5
30
 
6
- module Translatomatic::EscapedUnicode
31
+ private
7
32
 
8
- # Decodes all unicode chars from escape sequences
9
- # @param text [String]
10
- # @return [String] The encoded text for chaining
11
- def self.unescape(text)
12
- string = text.dup
13
- string = string.gsub(%r((?:\\[uU](?:[A-Fa-f\d]{4}))+)) do |c|
14
- c.downcase!
15
- bytes = EMPTY_8BIT_STRING.dup
16
- i = 0
17
- while c[6 * i] == ?\\ && c[6 * i + 1] == ?u
18
- bytes << c[6 * i + 2, 2].to_i(16) << c[6 * i + 4, 2].to_i(16)
19
- i += 1
33
+ def unescape_char(c)
34
+ c.downcase!
35
+ bytes = EMPTY_8BIT_STRING.dup
36
+ i = 0
37
+ while c[i] == '\\' && c[i + 1] == 'u'
38
+ (1..2).each do |j|
39
+ bytes << c[i + j * 2, 2].to_i(16)
40
+ end
41
+ i += 6
42
+ end
43
+ bytes.encode('utf-8', 'utf-16be')
20
44
  end
21
- bytes.encode("utf-8", "utf-16be")
22
- end
23
- string.force_encoding(::Encoding::UTF_8)
24
45
 
25
- text.replace string
26
- text
27
- end
46
+ def escape_char(c)
47
+ (c.size == 1) && raise(t('unicode.invalid_byte', byte: c))
48
+ s = c.encode('utf-16be', 'utf-8').unpack('H*')[0]
49
+ s.force_encoding(::Encoding::ASCII_8BIT)
50
+ s.gsub!(/.{4}/n, '\\\\u\&')
51
+ s.force_encoding(::Encoding::UTF_8)
52
+ end
53
+ end
28
54
 
29
- # Decodes all unicode chars into escape sequences
30
- # @param text [String]
31
- # @return [String] The decoded text for chaining
32
- def self.escape(text)
33
- string = text.dup
34
- string.force_encoding(::Encoding::ASCII_8BIT)
35
- string.gsub!(/["\\\x0-\x1f]/n) { |c| MAP[c] || c }
36
- string.gsub!(/(
55
+ # @private
56
+ ESCAPE_REGEX = /(
37
57
  (?:
38
58
  [\xc2-\xdf][\x80-\xbf] |
39
59
  [\xe0-\xef][\x80-\xbf]{2} |
40
60
  [\xf0-\xf4][\x80-\xbf]{3}
41
61
  )+ |
42
62
  [\x80-\xc1\xf5-\xff] # invalid
43
- )/nx) { |c|
44
- c.size == 1 and raise t("unicode.invalid_byte", byte: c)
45
- s = c.encode("utf-16be", "utf-8").unpack('H*')[0]
46
- s.force_encoding(::Encoding::ASCII_8BIT)
47
- s.gsub!(/.{4}/n, '\\\\u\&')
48
- s.force_encoding(::Encoding::UTF_8)
49
- }
50
- string.force_encoding(::Encoding::UTF_8)
51
- text.replace string
52
- text
53
- end
54
-
55
- private
63
+ )/nx
56
64
 
57
- MAP = {
58
- "\x0" => '\u0000',
59
- "\x1" => '\u0001',
60
- "\x2" => '\u0002',
61
- "\x3" => '\u0003',
62
- "\x4" => '\u0004',
63
- "\x5" => '\u0005',
64
- "\x6" => '\u0006',
65
- "\x7" => '\u0007',
66
- "\xb" => '\u000b',
67
- "\xe" => '\u000e',
68
- "\xf" => '\u000f',
69
- "\x10" => '\u0010',
70
- "\x11" => '\u0011',
71
- "\x12" => '\u0012',
72
- "\x13" => '\u0013',
73
- "\x14" => '\u0014',
74
- "\x15" => '\u0015',
75
- "\x16" => '\u0016',
76
- "\x17" => '\u0017',
77
- "\x18" => '\u0018',
78
- "\x19" => '\u0019',
79
- "\x1a" => '\u001a',
80
- "\x1b" => '\u001b',
81
- "\x1c" => '\u001c',
82
- "\x1d" => '\u001d',
83
- "\x1e" => '\u001e',
84
- "\x1f" => '\u001f',
85
- }
86
-
87
- EMPTY_8BIT_STRING = ''
88
- EMPTY_8BIT_STRING.force_encoding(::Encoding::ASCII_8BIT)
65
+ # @private
66
+ MAP = {
67
+ "\x0" => '\u0000',
68
+ "\x1" => '\u0001',
69
+ "\x2" => '\u0002',
70
+ "\x3" => '\u0003',
71
+ "\x4" => '\u0004',
72
+ "\x5" => '\u0005',
73
+ "\x6" => '\u0006',
74
+ "\x7" => '\u0007',
75
+ "\xb" => '\u000b',
76
+ "\xe" => '\u000e',
77
+ "\xf" => '\u000f',
78
+ "\x10" => '\u0010',
79
+ "\x11" => '\u0011',
80
+ "\x12" => '\u0012',
81
+ "\x13" => '\u0013',
82
+ "\x14" => '\u0014',
83
+ "\x15" => '\u0015',
84
+ "\x16" => '\u0016',
85
+ "\x17" => '\u0017',
86
+ "\x18" => '\u0018',
87
+ "\x19" => '\u0019',
88
+ "\x1a" => '\u001a',
89
+ "\x1b" => '\u001b',
90
+ "\x1c" => '\u001c',
91
+ "\x1d" => '\u001d',
92
+ "\x1e" => '\u001e',
93
+ "\x1f" => '\u001f'
94
+ }.freeze
95
+ private_constant :MAP
89
96
 
97
+ # @private
98
+ EMPTY_8BIT_STRING = ''.force_encoding(::Encoding::ASCII_8BIT).freeze
99
+ end
90
100
  end