babelyoda 1.6.0 → 2.0.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/.gitignore +5 -0
- data/CHANGELOG +1 -0
- data/Gemfile +6 -0
- data/LICENSE +1 -1
- data/README.rdoc +2 -2
- data/Rakefile +1 -0
- data/babelyoda.gemspec +28 -0
- data/lib/babelyoda/file.rb +20 -0
- data/lib/babelyoda/genstrings.rb +27 -0
- data/lib/babelyoda/git.rb +99 -0
- data/lib/babelyoda/git_versions.rb +39 -0
- data/lib/babelyoda/ibtool.rb +51 -0
- data/lib/babelyoda/keyset.rb +58 -0
- data/lib/babelyoda/localization_key.rb +56 -0
- data/lib/babelyoda/localization_value.rb +26 -0
- data/lib/babelyoda/logger.rb +16 -0
- data/lib/babelyoda/rake.rb +10 -0
- data/lib/babelyoda/specification.rb +40 -0
- data/lib/babelyoda/specification_loader.rb +35 -0
- data/lib/babelyoda/strings.rb +64 -8
- data/lib/babelyoda/strings_lexer.rb +12 -0
- data/lib/babelyoda/strings_parser.rb +72 -0
- data/lib/babelyoda/tanker.rb +190 -0
- data/lib/babelyoda/version.rb +3 -0
- data/lib/babelyoda/xib.rb +94 -0
- data/lib/babelyoda.rb +207 -0
- data/templates/Babelfile.erb +15 -0
- metadata +162 -70
- data/VERSION +0 -1
- data/bin/babelyoda +0 -399
- data/data/empty.strings +0 -1
- data/lib/babelyoda/options.rb +0 -42
data/CHANGELOG
CHANGED
data/Gemfile
ADDED
data/LICENSE
CHANGED
data/README.rdoc
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
= babelyoda
|
2
2
|
|
3
|
-
A simple utility to push/pull l10n resources of an
|
3
|
+
A simple utility to push/pull l10n resources of an Xcode project to/from the translators.
|
4
4
|
|
5
5
|
== Note on Patches/Pull Requests
|
6
6
|
|
@@ -14,4 +14,4 @@ A simple utility to push/pull l10n resources of an iPhone project to/from the tr
|
|
14
14
|
|
15
15
|
== Copyright
|
16
16
|
|
17
|
-
Copyright (c) 2010 Andrey Subbotin. See LICENSE for details.
|
17
|
+
Copyright (c) 2010-2012 Andrey Subbotin. See LICENSE for details.
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/babelyoda.gemspec
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
$:.push File.expand_path("../lib", __FILE__)
|
3
|
+
require "babelyoda/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "babelyoda"
|
7
|
+
s.version = Babelyoda::VERSION
|
8
|
+
s.authors = ["Andrey Subbotin"]
|
9
|
+
s.email = ["andrey@subbotin.me"]
|
10
|
+
s.homepage = "http://github.com/eploko/babelyoda"
|
11
|
+
s.summary = "Xcode project localization made easy"
|
12
|
+
s.description = "A simple utility to push/pull l10n resources of an Xcode project to/from the translators"
|
13
|
+
|
14
|
+
s.rubyforge_project = "babelyoda"
|
15
|
+
|
16
|
+
s.files = `git ls-files`.split("\n")
|
17
|
+
s.require_paths = ["lib"]
|
18
|
+
|
19
|
+
# specify any dependencies here; for example:
|
20
|
+
s.add_development_dependency "rspec", '~> 2.8', '>= 2.8.0'
|
21
|
+
s.add_runtime_dependency "awesome_print", '~> 1.0', '>= 1.0.2'
|
22
|
+
s.add_runtime_dependency "rake", '~> 0.9', '>= 0.9.2.2'
|
23
|
+
s.add_runtime_dependency "active_support", '~> 3.0', '>= 3.0.0'
|
24
|
+
s.add_runtime_dependency "rchardet19", '~> 1.3', '>= 1.3.5'
|
25
|
+
s.add_runtime_dependency "builder", '~> 3.0', '>= 3.0.0'
|
26
|
+
s.add_runtime_dependency "nokogiri", '~> 1.5', '>= 1.5.0'
|
27
|
+
s.add_runtime_dependency "term-ansicolor", '~> 1.0', '>= 1.0.7'
|
28
|
+
end
|
@@ -0,0 +1,20 @@
|
|
1
|
+
class File
|
2
|
+
def self.lproj_part(filename)
|
3
|
+
filename.split('/').each do |part|
|
4
|
+
return part if part =~ /^.*\.lproj$/
|
5
|
+
end
|
6
|
+
nil
|
7
|
+
end
|
8
|
+
|
9
|
+
def self.omit_lproj(filename)
|
10
|
+
File.join(filename.split('/').delete_if { |p| p.match(/^.*\.lproj$/) })
|
11
|
+
end
|
12
|
+
|
13
|
+
def self.localized(filename, language)
|
14
|
+
if lproj_part(filename)
|
15
|
+
File.join(filename.split('/').map { |p| p.match(/^.*\.lproj$/) ? "#{language}.lproj" : p })
|
16
|
+
else
|
17
|
+
File.join(File.dirname(filename), "#{language}.lproj", File.basename(filename))
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'tmpdir'
|
3
|
+
|
4
|
+
require_relative 'keyset'
|
5
|
+
require_relative 'strings'
|
6
|
+
|
7
|
+
module Babelyoda
|
8
|
+
class Genstrings
|
9
|
+
def self.run(files = [], language, &block)
|
10
|
+
keysets = {}
|
11
|
+
files.each do |fn|
|
12
|
+
Dir.mktmpdir do |dir|
|
13
|
+
raise "ERROR: genstrings failed." unless Kernel.system("genstrings -littleEndian -o '#{dir}' '#{fn}'")
|
14
|
+
Dir.glob(File.join(dir, '*.strings')).each do |strings_file|
|
15
|
+
strings = Babelyoda::Strings.new(strings_file, language).read!
|
16
|
+
strings.name = File.join('Resources', File.basename(strings.name))
|
17
|
+
keysets[strings.name] ||= Keyset.new(strings.name)
|
18
|
+
keysets[strings.name].merge!(strings)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
keysets.each_value do |item|
|
23
|
+
yield(item)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,99 @@
|
|
1
|
+
require_relative 'git_versions'
|
2
|
+
require_relative 'logger'
|
3
|
+
require_relative 'specification_loader'
|
4
|
+
|
5
|
+
module Babelyoda
|
6
|
+
class Git
|
7
|
+
include Babelyoda::SpecificationLoader
|
8
|
+
|
9
|
+
def version_exist?(filename)
|
10
|
+
versions.exist?(filename)
|
11
|
+
end
|
12
|
+
|
13
|
+
def store_version!(filename)
|
14
|
+
@versions[filename] = git_ls_sha1(filename)
|
15
|
+
should_add = !File.exist?(versions.filename)
|
16
|
+
versions.save!
|
17
|
+
end
|
18
|
+
|
19
|
+
def fetch_versions!(*filenames, &block)
|
20
|
+
Dir.mktmpdir do |dir|
|
21
|
+
results = []
|
22
|
+
filenames.each do |fn|
|
23
|
+
full_fn = File.join(dir, fn)
|
24
|
+
dirname = File.dirname(full_fn)
|
25
|
+
FileUtils.mkdir_p dirname
|
26
|
+
git_show(@versions[fn], full_fn)
|
27
|
+
results << full_fn
|
28
|
+
end
|
29
|
+
block.call(results)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def transaction(msg)
|
34
|
+
check_requirements!
|
35
|
+
yield if block_given?
|
36
|
+
if git_status.size > 0
|
37
|
+
git_add!('.')
|
38
|
+
git_add!('-u')
|
39
|
+
git_commit!(msg)
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def versions
|
46
|
+
@versions ||= GitVersions.new
|
47
|
+
end
|
48
|
+
|
49
|
+
def check_requirements!
|
50
|
+
$logger.error "GIT: The working copy is not clean. Please commit your work before running Babelyoda tasks." unless clean?
|
51
|
+
end
|
52
|
+
|
53
|
+
def git_modified?(filename)
|
54
|
+
git_status.has_key?(filename)
|
55
|
+
end
|
56
|
+
|
57
|
+
def git_status
|
58
|
+
result = {}
|
59
|
+
`git status --porcelain`.scan(/^(\sM|\?\?)\s+(.*)$/).each do |m|
|
60
|
+
result[m[1]] = m[0]
|
61
|
+
end
|
62
|
+
result
|
63
|
+
end
|
64
|
+
|
65
|
+
def git_add!(filename)
|
66
|
+
ncmd = ['git', 'add', filename]
|
67
|
+
rc = Kernel.system(*ncmd)
|
68
|
+
$logger.error "GIT ERROR: #{ncmd}" unless rc
|
69
|
+
end
|
70
|
+
|
71
|
+
def git_commit!(msg)
|
72
|
+
ncmd = ['git', 'commit', '-m', msg]
|
73
|
+
rc = Kernel.system(*ncmd)
|
74
|
+
$logger.error "GIT ERROR: #{ncmd}" unless rc
|
75
|
+
end
|
76
|
+
|
77
|
+
def git_show(sha1, filename = nil)
|
78
|
+
ncmd = ['git', 'show', sha1]
|
79
|
+
IO.popen(ncmd) { |io|
|
80
|
+
blob = io.read
|
81
|
+
if filename
|
82
|
+
File.open(filename, 'w') {|f| f.write(blob) }
|
83
|
+
end
|
84
|
+
blob
|
85
|
+
}
|
86
|
+
$logger.error "GIT ERROR: #{ncmd}" unless $? == 0
|
87
|
+
end
|
88
|
+
|
89
|
+
def git_ls_sha1(filename)
|
90
|
+
matches = `git ls-files -s '#{filename}'`.match(/^\d{6}\s+([^\s]+)\s+.*$/)
|
91
|
+
$logger.error "GIT ERROR: Couldn't get SHA1 for: #{filename}" unless matches
|
92
|
+
matches[1]
|
93
|
+
end
|
94
|
+
|
95
|
+
def clean?
|
96
|
+
`git status 2>&1`.match(/^nothing to commit \(working directory clean\)$/)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'fileutils'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
module Babelyoda
|
5
|
+
class GitVersions
|
6
|
+
|
7
|
+
def initialize
|
8
|
+
@versions = load || {}
|
9
|
+
end
|
10
|
+
|
11
|
+
def exist?(filename)
|
12
|
+
@versions.has_key?(filename)
|
13
|
+
end
|
14
|
+
|
15
|
+
def filename
|
16
|
+
'.babelyoda/git_versions.yml'
|
17
|
+
end
|
18
|
+
|
19
|
+
def save!
|
20
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
21
|
+
File.open(filename, 'w') {|f| f.write(@versions.to_yaml) }
|
22
|
+
end
|
23
|
+
|
24
|
+
def [](filename)
|
25
|
+
@versions[filename]
|
26
|
+
end
|
27
|
+
|
28
|
+
def []=(filename, value)
|
29
|
+
@versions[filename] = value
|
30
|
+
end
|
31
|
+
|
32
|
+
private
|
33
|
+
|
34
|
+
def load
|
35
|
+
@versions = YAML::load_file(filename) if File.exist?(filename)
|
36
|
+
end
|
37
|
+
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
require_relative 'logger'
|
2
|
+
require_relative 'strings'
|
3
|
+
|
4
|
+
module Babelyoda
|
5
|
+
class Ibtool
|
6
|
+
def self.extract_strings(xib_filename, language)
|
7
|
+
Dir.mktmpdir do |dir|
|
8
|
+
basename = File.basename(xib_filename, '.xib')
|
9
|
+
strings_filename = File.join(dir, "#{basename}.strings")
|
10
|
+
cmd = "ibtool --generate-strings-file '#{strings_filename}' '#{xib_filename}'"
|
11
|
+
$logger.error "IBTOOL ERROR: #{cmd}" unless Kernel.system(cmd)
|
12
|
+
return Babelyoda::Strings.new(strings_filename, language).read!
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.localize(source_xib_fn, target_xib_fn, strings_fn)
|
17
|
+
# ibtool
|
18
|
+
# --strings-file path_to_strings/fr/MainWindow.strings # The latest localized strings for the French XIB
|
19
|
+
# --write path_to_project/fr.lproj/MainWindow.xib # The new French XIB that will be created
|
20
|
+
# path_to_project/English.lproj/MainWindow.new.xib # The new English XIB
|
21
|
+
|
22
|
+
ncmd = ['ibtool', '--strings-file', strings_fn, '--write', target_xib_fn, source_xib_fn]
|
23
|
+
rc = Kernel.system(*ncmd)
|
24
|
+
$logger.error "IBTOOL ERROR: #{ncmd}" unless rc
|
25
|
+
end
|
26
|
+
|
27
|
+
def self.localize_incrementally(source_xib_fn, target_xib_fn, strings_fn, old_source_xib_fn, old_target_xib_fn)
|
28
|
+
# ibtool
|
29
|
+
# --previous-file path_to_project/English.lproj/MainWindow.old.xib # The old English XIB
|
30
|
+
# --incremental-file path_to_project/fr.lproj/MainWindow.old.xib # The old French XIB
|
31
|
+
# --strings-file path_to_strings/fr/MainWindow.strings # The latest localized strings for the French XIB
|
32
|
+
# --localize-incremental
|
33
|
+
# --write path_to_project/fr.lproj/MainWindow.xib # The new French XIB that will be created
|
34
|
+
# path_to_project/English.lproj/MainWindow.new.xib # The new English XIB
|
35
|
+
|
36
|
+
ncmd = ['ibtool', '--previous-file', old_source_xib_fn, '--incremental-file', old_target_xib_fn,
|
37
|
+
'--strings-file', strings_fn, '--localize-incremental', '--write', target_xib_fn, source_xib_fn]
|
38
|
+
rc = Kernel.system(*ncmd)
|
39
|
+
$logger.error "IBTOOL ERROR: #{ncmd}" unless rc
|
40
|
+
end
|
41
|
+
|
42
|
+
def self.import_strings(filename, strings_filename)
|
43
|
+
ncmd = ['ibtool', '--import-strings-file', strings_filename, filename]
|
44
|
+
rc = Kernel.system(*ncmd)
|
45
|
+
$logger.error "IBTOOL ERROR: #{ncmd}" unless rc
|
46
|
+
end
|
47
|
+
|
48
|
+
private
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
@@ -0,0 +1,58 @@
|
|
1
|
+
module Babelyoda
|
2
|
+
class Keyset
|
3
|
+
attr_accessor :name
|
4
|
+
attr_accessor :keys
|
5
|
+
|
6
|
+
def self.keyset_name(filename)
|
7
|
+
raise ArgumentError.new("Invlaid filename for a .strings file: #{filename}") unless filename.match(/\.strings$/)
|
8
|
+
parts = File.join(File.dirname(filename), File.basename(filename, '.strings')).split('/')
|
9
|
+
parts.delete_if { |part| part.match(/\.lproj$/) }
|
10
|
+
File.join(parts)
|
11
|
+
end
|
12
|
+
|
13
|
+
def initialize(name)
|
14
|
+
@name = name
|
15
|
+
@keys = {}
|
16
|
+
end
|
17
|
+
|
18
|
+
def to_s ; "<#{self.class}: name = #{name}, keys.size = #{keys.size}>" ; end
|
19
|
+
|
20
|
+
def empty? ; keys.size == 0 ; end
|
21
|
+
|
22
|
+
def merge!(keyset, options = {})
|
23
|
+
result = { :new => 0, :updated => 0 }
|
24
|
+
keyset.keys.each_pair do |id, key|
|
25
|
+
if @keys.has_key?(id)
|
26
|
+
result[:updated] += 1 if @keys[id].merge!(key, options)
|
27
|
+
else
|
28
|
+
@keys[id] = key.dup
|
29
|
+
result[:new] += 1
|
30
|
+
end
|
31
|
+
end
|
32
|
+
return result
|
33
|
+
end
|
34
|
+
|
35
|
+
def merge_key!(localization_key)
|
36
|
+
if @keys.has_key?(localization_key.id)
|
37
|
+
@keys[localization_key.id].merge!(localization_key)
|
38
|
+
else
|
39
|
+
@keys[localization_key.id] = localization_key
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def ensure_languages!(languages = [])
|
44
|
+
@keys.each_value do |key|
|
45
|
+
languages.each do |language|
|
46
|
+
key.values[language] ||= Babelyoda::LocalizationValue.new(language, '')
|
47
|
+
end
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
def drop_empty!
|
52
|
+
@keys.delete_if do |id, key|
|
53
|
+
key.drop_empty!
|
54
|
+
key.empty?
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
@@ -0,0 +1,56 @@
|
|
1
|
+
module Babelyoda
|
2
|
+
class LocalizationKey
|
3
|
+
attr_reader :id
|
4
|
+
attr_reader :context
|
5
|
+
attr_reader :values
|
6
|
+
|
7
|
+
def initialize(id, context)
|
8
|
+
@id = id
|
9
|
+
@context = context
|
10
|
+
@values = {}
|
11
|
+
end
|
12
|
+
|
13
|
+
def <<(localization_value)
|
14
|
+
@values[localization_value.language.to_sym] = localization_value.dup
|
15
|
+
self
|
16
|
+
end
|
17
|
+
|
18
|
+
def merge!(localization_key, options = {})
|
19
|
+
updated = false
|
20
|
+
|
21
|
+
context_changed = false
|
22
|
+
if @context != localization_key.context
|
23
|
+
@context = localization_key.context
|
24
|
+
updated = context_changed = true
|
25
|
+
end
|
26
|
+
|
27
|
+
localization_key.values.each_value do |value|
|
28
|
+
if @values.has_key?(value.language.to_sym)
|
29
|
+
updated = true if @values[value.language.to_sym].merge!(value, options)
|
30
|
+
else
|
31
|
+
@values[value.language.to_sym] = value.dup
|
32
|
+
updated = true
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Mark all values as requiring translation if the context has changed.
|
37
|
+
if context_changed
|
38
|
+
@values.each_value do |value|
|
39
|
+
value.status = :requires_translation
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
return updated
|
44
|
+
end
|
45
|
+
|
46
|
+
def drop_empty!
|
47
|
+
@values.delete_if do |id, value|
|
48
|
+
value.text.empty?
|
49
|
+
end
|
50
|
+
end
|
51
|
+
|
52
|
+
def empty?
|
53
|
+
@values.empty?
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Babelyoda
|
2
|
+
class LocalizationValue
|
3
|
+
attr_accessor :language
|
4
|
+
attr_accessor :status
|
5
|
+
attr_accessor :text
|
6
|
+
|
7
|
+
def initialize(language, text, status = :requires_translation)
|
8
|
+
@language, @text, @status = language.to_sym, text, status.to_sym
|
9
|
+
end
|
10
|
+
|
11
|
+
def merge!(other_value, options = {})
|
12
|
+
updated = false
|
13
|
+
options = { preserve: false }.merge!(options)
|
14
|
+
unless @language.to_sym == other_value.language.to_sym
|
15
|
+
raise RuntimeError.new("Can't merge values in different languages: #{@language.to_sym} and #{other_value.language.to_sym}")
|
16
|
+
end
|
17
|
+
if (!options[:preserve] || @status.to_sym == :requires_translation)
|
18
|
+
unless @text == other_value.text
|
19
|
+
@text = other_value.text
|
20
|
+
updated = true
|
21
|
+
end
|
22
|
+
end
|
23
|
+
return updated
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
require 'term/ansicolor'
|
2
|
+
|
3
|
+
module Babelyoda
|
4
|
+
class Logger
|
5
|
+
include Term::ANSIColor
|
6
|
+
|
7
|
+
def exe(cmd) ; putcmd cmd ; system cmd ; end
|
8
|
+
def putcmd(cmd) ; print magenta, "CMD: #{cmd}", reset, "\n" ; end
|
9
|
+
def status(msg) ; print blue, "--- #{msg} ---", reset, "\n" ; end
|
10
|
+
def success(msg) ; print green, bold, 'SUCCESS: ', msg, reset, "\n" ; end
|
11
|
+
def error(msg) ; print red, bold, 'ERROR: ', msg, reset, "\n" ; exit 1 ; end
|
12
|
+
def escape_cmd_args(args) ; args.collect{ |arg| "'#{arg}'"}.join(' ') ; end
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
$logger ||= Babelyoda::Logger.new
|
@@ -0,0 +1,40 @@
|
|
1
|
+
require 'erb'
|
2
|
+
|
3
|
+
require_relative 'specification_loader'
|
4
|
+
|
5
|
+
module Babelyoda
|
6
|
+
class Specification
|
7
|
+
include Babelyoda::SpecificationLoader
|
8
|
+
|
9
|
+
attr_accessor :name
|
10
|
+
attr_accessor :development_language
|
11
|
+
attr_accessor :localization_languages
|
12
|
+
attr_accessor :engine
|
13
|
+
attr_accessor :source_files
|
14
|
+
attr_accessor :resources_folder
|
15
|
+
attr_accessor :xib_files
|
16
|
+
attr_accessor :strings_files
|
17
|
+
attr_accessor :scm
|
18
|
+
|
19
|
+
FILENAME = 'Babelfile'
|
20
|
+
|
21
|
+
def self.generate_default_babelfile
|
22
|
+
template_file_name = File.join(BABELYODA_PATH, 'templates', 'Babelfile.erb')
|
23
|
+
template = File.read(template_file_name)
|
24
|
+
File.open(FILENAME, "w+") do |f|
|
25
|
+
f.write(ERB.new(template).result())
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.load
|
30
|
+
trace_spec = @spec.nil? && ::Rake.application.options.trace
|
31
|
+
@spec ||= load_from_file(filename = FILENAME)
|
32
|
+
@spec.dump if trace_spec && @spec
|
33
|
+
return @spec
|
34
|
+
end
|
35
|
+
|
36
|
+
def all_languages
|
37
|
+
[ development_language, localization_languages].flatten!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
require 'awesome_print'
|
2
|
+
|
3
|
+
module Babelyoda
|
4
|
+
module SpecificationLoader
|
5
|
+
def self.included(klass)
|
6
|
+
klass.extend ClassMethods
|
7
|
+
end
|
8
|
+
|
9
|
+
def initialize(*args)
|
10
|
+
super
|
11
|
+
yield(self) if block_given?
|
12
|
+
end
|
13
|
+
|
14
|
+
def method_missing(method_name, *args, &block)
|
15
|
+
msg = "You tried to call the method #{method_name}. There is no such method."
|
16
|
+
raise msg
|
17
|
+
end
|
18
|
+
|
19
|
+
def dump
|
20
|
+
ap self, :indent => -2
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
|
25
|
+
def load_from_file(filename)
|
26
|
+
return nil unless File.exist?(filename)
|
27
|
+
spec = eval(File.read(filename))
|
28
|
+
raise "Wrong specification class: #{spec.class.to_s}" unless spec.instance_of?(self)
|
29
|
+
return spec
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
data/lib/babelyoda/strings.rb
CHANGED
@@ -1,11 +1,67 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
1
|
+
require 'rchardet19'
|
2
|
+
|
3
|
+
require_relative 'keyset'
|
4
|
+
require_relative 'strings_lexer'
|
5
|
+
require_relative 'strings_parser'
|
6
|
+
|
7
|
+
module Babelyoda
|
8
|
+
class Strings < Keyset
|
9
|
+
attr_reader :filename
|
10
|
+
attr_reader :language
|
11
|
+
|
12
|
+
def initialize(filename, language)
|
13
|
+
super(Babelyoda::Keyset.keyset_name(filename))
|
14
|
+
@filename, @language = filename, language
|
15
|
+
end
|
16
|
+
|
17
|
+
def read!
|
18
|
+
raise ArgumentError.new("File not found: #{filename}") unless File.exist?(@filename)
|
19
|
+
read
|
20
|
+
end
|
21
|
+
|
22
|
+
def read
|
23
|
+
if File.exist?(@filename)
|
24
|
+
File.open(@filename, read_mode) do |f|
|
25
|
+
lexer = StringsLexer.new
|
26
|
+
parser = StringsParser.new(lexer, @language)
|
27
|
+
parser.parse(f.read) do |localization_key|
|
28
|
+
merge_key!(localization_key)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
self
|
33
|
+
end
|
34
|
+
|
35
|
+
def save!
|
36
|
+
FileUtils.mkdir_p(File.dirname(filename))
|
37
|
+
File.open(filename, "wb") do |f|
|
38
|
+
keys.each_pair do |id, key|
|
39
|
+
next unless key.values[language]
|
40
|
+
f << "/* #{key.context} */\n" if key.context
|
41
|
+
f << "\"#{id}\" = \"#{key.values[language].text}\";\n"
|
42
|
+
f << "\n"
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def self.save_keyset(keyset, filename, language)
|
48
|
+
strings = self.new(filename, language)
|
49
|
+
strings.merge!(keyset)
|
50
|
+
strings.save!
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
|
55
|
+
def read_mode
|
56
|
+
cd = CharDet.detect(File.read(@filename))
|
57
|
+
encoding_str = Encoding.aliases[cd.encoding] || cd.encoding
|
58
|
+
encoding_str = 'UTF-8' if encoding_str == 'utf-8'
|
59
|
+
if (encoding_str != "UTF-8")
|
60
|
+
"rb:#{encoding_str}:UTF-8"
|
61
|
+
else
|
62
|
+
"r"
|
8
63
|
end
|
9
64
|
end
|
10
|
-
|
65
|
+
|
66
|
+
end
|
11
67
|
end
|
@@ -0,0 +1,12 @@
|
|
1
|
+
module Babelyoda
|
2
|
+
class StringsLexer
|
3
|
+
TOKENS = [ :multiline_comment, :singleline_comment, :string, :reserved0, :equal_sign, :semicolon ].freeze
|
4
|
+
|
5
|
+
def lex(str)
|
6
|
+
str.scan(/(\/\*.*\*\/)|(\s*\/\/.*\n)|((["])(?:\\?+.)*?\4)|(\s*=\s*)|(;)/).each do |m|
|
7
|
+
idx = m.index { |x| x }
|
8
|
+
yield TOKENS[idx], m[idx].strip
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
end
|