tcl-msgcat 0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 1d08c896d423e775ef1b282c5399bd57087c3ef7
4
+ data.tar.gz: c66f9414471f9a514af85e9ea1cf7f6eadf36204
5
+ SHA512:
6
+ metadata.gz: e290d35a7c371da396f1a6705b7d464fddecaab9ee44748415f9d0a7569781ed3b8128995f678acb7214e291c92c90145b3a2dc3ee3e40308e5541d94ff934bf
7
+ data.tar.gz: b93c2286e49c180b27d5635918b993cecc804f48a357bfa400fa961dbf80a657fc5527b0ef043285648b9427d3b9e277864b985ad741869b80bbe5ab9f67082a
data/.gitignore ADDED
@@ -0,0 +1,14 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /Gemfile.lock
4
+ /_yardoc/
5
+ /coverage/
6
+ /doc/
7
+ /pkg/
8
+ /spec/reports/
9
+ /tmp/
10
+ *.bundle
11
+ *.so
12
+ *.o
13
+ *.a
14
+ mkmf.log
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in tcl-msgcat.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2015 Robert McLeod
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,62 @@
1
+ # Tcl::Msgcat
2
+
3
+ Library for parsing, rendering and merging TCL msgcat (read i18n) files.
4
+
5
+ TCL msgcat files are yuk, and a legacy format. Unfortunately some of us are stuck with systems that still use these files
6
+ for i18n in TCL applications. Managing these files can be a bit annoying, especially when they are large and you have
7
+ many different people translating them. What's more, TCL doesn't report line numbers in it's error messages!!! So if you have
8
+ an error in your 7000 line msgcat file, good luck finding it!
9
+
10
+ This library aims to solve that problem by storing the translations in JSON and compiling them to the TCL msgcat format. JSON
11
+ is easily lintable and most syntax checkers tell you which line any errors are on. Plus it is a modern standard and supported
12
+ by lots of languages which are better than TCL so you can programmatically update changes between your root message file and
13
+ your translation files. Which is another thing this library does.
14
+
15
+ ## Installation
16
+
17
+ Add this line to your application's Gemfile:
18
+
19
+ ```ruby
20
+ gem 'tcl-msgcat'
21
+ ```
22
+
23
+ And then execute:
24
+
25
+ $ bundle
26
+
27
+ Or install it yourself as:
28
+
29
+ $ gem install tcl-msgcat
30
+
31
+ ## Usage
32
+
33
+ You'll want to convert your existing files first. You can do this with the following command:
34
+
35
+ msgcat parse root.msg > root.json
36
+ msgcat parse zh_tw.msg > zh_tw.json
37
+
38
+ And render it back to the msgcat file:
39
+
40
+ msgcat render root.json > root.test.msg
41
+
42
+ OK, now say you have added a bunch of strings to your `root.json` for some new stuff in the application. You
43
+ will want to update all your other files with those strings, and send them off to your translators. For this
44
+ there is the `merge` action with a few ways to use it:
45
+
46
+ msgcat merge root.json fr.json de.json es.json # specify files individually
47
+ msgcat merge root.json *.json # or glob them (root.json is automatically excluded)
48
+
49
+ Now you have got your files back from the translators, put them through a [linter](http://jsonlint.com/)
50
+ and found no errors. You are ready to put those new translations back into your application. Use the `compile`
51
+ action to run the render command on all your json files:
52
+
53
+ msgcat compile ui/msgs # only one arg needed if you don't segregate source/compiled
54
+ msgcat compile ui/msgs/source ui/msgs # otherwise specify the source and compile paths seperately
55
+
56
+ ## Contributing
57
+
58
+ 1. Fork it ( https://github.com/AutogrowSystems/tcl-msgcat/fork )
59
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
60
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
61
+ 4. Push to the branch (`git push origin my-new-feature`)
62
+ 5. Create a new Pull Request
data/Rakefile ADDED
@@ -0,0 +1,2 @@
1
+ require "bundler/gem_tasks"
2
+
data/bin/msgcat ADDED
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'tcl-msgcat'
4
+
5
+ case ARGV[0]
6
+ when "parse", "p"
7
+ file = ARGV[1]
8
+ puts Tcl::Msgcat.parse(file).to_json
9
+ when "render", "r"
10
+ file = ARGV[1]
11
+ puts Tcl::Msgcat.render(file).to_s
12
+ when "merge", "m"
13
+ root = ARGV[1]
14
+ files = ARGV[2..-1]
15
+ Tcl::Msgcat.merge(root, files)
16
+ when "compile"
17
+ source_path = ARGV[1]
18
+ target_path = ARGV[2] || ARGV[1]
19
+
20
+ Tcl::Msgcat.compile(source_path, target_path)
21
+ else
22
+ puts <<-EOH
23
+ Usage: msgcat <action> <arguments>
24
+
25
+ Actions:
26
+ parse <file> Parse a msgcat file and output JSON
27
+ render <file> Parse a JSON file and output msgcat
28
+ merge <root> <files> Merge new keys from root into the files (glob pattern)
29
+ compile <source> [target] Compile from the source into the target
30
+
31
+ Examples:
32
+ msgcat parse ROOT.msg > root.json
33
+ msgcat render root.json > ROOT.msg
34
+ msgcat merge root.json path/to/translations/*.json
35
+ msgcat merge root.json zh_tw.json fr.json de.json
36
+ msgcat compile path/to/my/json/files path/to/my/msgcat/files
37
+ msgcat compile path/to/my/json/msgcat/files
38
+ EOH
39
+ end
@@ -0,0 +1,20 @@
1
+ module Tcl
2
+ module Msgcat
3
+ class Catalog
4
+ attr_reader :msgs
5
+ def initialize(msgs)
6
+ @msgs = msgs
7
+ end
8
+
9
+ def merge!(catalog)
10
+ merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
11
+ @msgs = catalog.msgs.merge(@msgs, &merger)
12
+ end
13
+
14
+ def self.load(file)
15
+ msgs = JSON.parse(File.read(file))
16
+ Tcl::Msgcat::Catalog.new(msgs)
17
+ end
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,91 @@
1
+ module Tcl
2
+ module Msgcat
3
+
4
+ # Parses a msgcat file into a ruby hash
5
+ # which can then be converted to whatever
6
+ class Parser
7
+ attr_reader :msgs
8
+
9
+ def initialize(file)
10
+ raise ArgumentError, "File not found" unless File.exist? file
11
+
12
+ @msgs = {}
13
+ @file = file
14
+ end
15
+
16
+ def parse
17
+ lines = File.read(@file).lines
18
+ scopes = []
19
+
20
+ # strip the comments out
21
+ lines.reject! {|l| l.match(/^\s?#/) }
22
+
23
+ # run through each line and do an action
24
+ # depending on the kind of line it is
25
+ lines.each do |line|
26
+
27
+ # start of a message group/namespace definition
28
+ if match = line.match(/msgs (\S+) \{/) # msg ::site::irrigation {
29
+ scopes << match[1]
30
+ next
31
+ end
32
+
33
+ # end of the message group/namespace definition
34
+ if match = line.match(/\}/) # }
35
+ scopes.pop
36
+ next
37
+ end
38
+
39
+ # an actual message definition
40
+ if match = line.match(/m (\S+) "(.+)"/) # m label "the actual string"
41
+ add(scopes, match[1], match[2])
42
+ end
43
+ end
44
+
45
+ self
46
+ end
47
+
48
+ # Adds a key and translation to the msg hash
49
+ #
50
+ # Uses eval to dynamically create a multi dimensional
51
+ # hash from the scopes array that is given
52
+ #
53
+ def add(scopes, name, string)
54
+ chain = []
55
+
56
+ # run through each scope
57
+ scopes.each_with_index do |k, i|
58
+ chain = []
59
+
60
+ # build the chain of send commands from the top level
61
+ # to the level of the current scope, for each iteration
62
+ #
63
+ # e.g. for iteration:
64
+ #
65
+ # first: hash[:scope1]
66
+ # second: hash[:scope1][:scope2]
67
+ # third: hash[:scope1][:scope2][:scope3]
68
+ subkeys = scopes[0..i]
69
+ subkeys.each do |k|
70
+ chain << "send('[]', '#{k}')"
71
+ end
72
+
73
+ # if the (sub)key doesn't exist for this (chain of) scopes
74
+ # then set it to an empty hash
75
+ if eval("@msgs.#{chain.join(".")}").nil?
76
+ set_chain = chain[0..-2] + ["send('[]=', '#{subkeys.last}', {})"]
77
+ eval("@msgs.#{set_chain.join(".")}")
78
+ end
79
+ end
80
+
81
+ # set the value a the end of the chain
82
+ chain << "send('[]=', '#{name}', %q[#{string}])"
83
+ eval("@msgs.#{chain.join(".")}")
84
+ end
85
+
86
+ def to_json(pretty=true)
87
+ pretty ? JSON.pretty_generate(@msgs) : @msgs.to_json
88
+ end
89
+ end
90
+ end
91
+ end
@@ -0,0 +1,47 @@
1
+ module Tcl
2
+ module Msgcat
3
+ class Renderer
4
+ attr_reader :msgs, :lines
5
+ def initialize(msgs)
6
+ @msgs = msgs
7
+ @lines = []
8
+ end
9
+
10
+ def render
11
+ @lines << "package require mchelper"
12
+ @lines << "msg_lang {}"
13
+ @lines << ""
14
+ _render(@msgs)
15
+
16
+ self
17
+ end
18
+
19
+ def to_s
20
+ @lines.join("\n")
21
+ end
22
+
23
+ private
24
+
25
+ # A recursive function that calls itself
26
+ # when the value of a key is a hash so it can
27
+ # iterate through every dimension of the hash
28
+ # to get at the translation labels and strings
29
+ def _render(msgs, level=0)
30
+ msgs.each do |key, value|
31
+ if value.is_a? Hash
32
+ @lines << "msgs #{key} {".rpad(" ", level)
33
+ level+=1
34
+ _render(value, level) # render any child hashes or translation labels/strings
35
+ level-=1
36
+ @lines << "}".rpad(" ", level)
37
+ @lines << ""
38
+ end
39
+
40
+ if value.is_a? String
41
+ @lines << "m #{key} \"#{value}\"".rpad(" ", level)
42
+ end
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,5 @@
1
+ module Tcl
2
+ module Msgcat
3
+ VERSION = "0.5"
4
+ end
5
+ end
data/lib/tcl/msgcat.rb ADDED
@@ -0,0 +1,50 @@
1
+ require 'json'
2
+ require "tcl/msgcat/version"
3
+ require "tcl/msgcat/catalog"
4
+ require "tcl/msgcat/parser"
5
+ require "tcl/msgcat/renderer"
6
+
7
+ module Tcl
8
+ module Msgcat
9
+ def parse(msgcat_file)
10
+ Tcl::Msgcat::Parser.new(msgcat_file).parse
11
+ end
12
+
13
+ def render(json_file)
14
+ raise ArgumentError, "File not found: #{json_file}" unless File.exist? json_file
15
+ msgs = JSON.parse(File.read(json_file))
16
+ Tcl::Msgcat::Renderer.new(msgs).render
17
+ end
18
+
19
+ def merge(root_file, translation_files=[])
20
+ if translation_files.is_a? String
21
+ translation_files = Dir[translation_files]
22
+ end
23
+
24
+ root = Tcl::Msgcat::Catalog.load(root_file)
25
+
26
+ translation_files.each do |file|
27
+ next if File.basename(file) == File.basename(root_file)
28
+ print "Merging new translations into #{file}... "
29
+ catalog = Tcl::Msgcat::Catalog.load(file)
30
+ catalog.merge!(root)
31
+ File.open(file, "w") {|f| f.write catalog.to_json }
32
+ puts "done"
33
+ end
34
+ end
35
+
36
+ def compile(source, target)
37
+ raise ArgumentError, "Not a directory: #{source}" unless File.directory? source
38
+ raise ArgumentError, "Not a directory: #{target}" unless File.directory? target
39
+
40
+ Dir["#{source}/*.json"].each do |src|
41
+ dst = File.basename(src, ".json")+".msg"
42
+ print "Compiling #{src} to #{target}/#{dst}... "
43
+ msgs = JSON.parse(File.read(src))
44
+ renderer = Tcl::Msgcat::Renderer.new(msgs).render
45
+ File.write("#{target}/#{dst}") {|f| f.write renderer.to_s }
46
+ puts "done"
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'tcl/msgcat/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "tcl-msgcat"
8
+ spec.version = Tcl::Msgcat::VERSION
9
+ spec.authors = ["Robert McLeod"]
10
+ spec.email = ["robert@autogrow.com"]
11
+ spec.summary = %q{Library for parsing, rendering and merging TCL msgcat files}
12
+ spec.description = %q{Library for parsing, rendering and merging TCL msgcat files}
13
+ spec.homepage = "https://github.com/AutogrowSystems/tcl-msgcat"
14
+ spec.license = "MIT"
15
+
16
+ spec.files = `git ls-files -z`.split("\x0")
17
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
18
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
19
+ spec.require_paths = ["lib"]
20
+
21
+ spec.add_development_dependency "bundler", "~> 1.7"
22
+ spec.add_development_dependency "rake", "~> 10.0"
23
+ end
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: tcl-msgcat
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.5'
5
+ platform: ruby
6
+ authors:
7
+ - Robert McLeod
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-01-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.7'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.7'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ description: Library for parsing, rendering and merging TCL msgcat files
42
+ email:
43
+ - robert@autogrow.com
44
+ executables:
45
+ - msgcat
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - ".gitignore"
50
+ - Gemfile
51
+ - LICENSE.txt
52
+ - README.md
53
+ - Rakefile
54
+ - bin/msgcat
55
+ - lib/tcl/msgcat.rb
56
+ - lib/tcl/msgcat/catalog.rb
57
+ - lib/tcl/msgcat/parser.rb
58
+ - lib/tcl/msgcat/renderer.rb
59
+ - lib/tcl/msgcat/version.rb
60
+ - tcl-msgcat.gemspec
61
+ homepage: https://github.com/AutogrowSystems/tcl-msgcat
62
+ licenses:
63
+ - MIT
64
+ metadata: {}
65
+ post_install_message:
66
+ rdoc_options: []
67
+ require_paths:
68
+ - lib
69
+ required_ruby_version: !ruby/object:Gem::Requirement
70
+ requirements:
71
+ - - ">="
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ requirements:
76
+ - - ">="
77
+ - !ruby/object:Gem::Version
78
+ version: '0'
79
+ requirements: []
80
+ rubyforge_project:
81
+ rubygems_version: 2.2.2
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: Library for parsing, rendering and merging TCL msgcat files
85
+ test_files: []