gettext-one_sky 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,5 @@
1
+ pkg/*
2
+ rdoc/*
3
+ *.gem
4
+ .bundle
5
+ spec/po/**/simple_client.po_from_one_sky
@@ -0,0 +1,5 @@
1
+ = Change log
2
+
3
+ == 0.0.1 (31 July 2011)
4
+ * Initial release.
5
+
data/CREDITS ADDED
@@ -0,0 +1,6 @@
1
+ = Credits
2
+
3
+ Eddie Lau, http://3dd13.me http://github.com/3dd13
4
+
5
+ * Initial code.
6
+
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in gettext-one_sky.gemspec
4
+ gemspec
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ gettext-one_sky (0.0.1)
5
+ gettext (~> 0.5.0)
6
+ one_sky (~> 0.0.2)
7
+ module GetText (~> 0.14.4)
8
+
9
+ GEM
10
+ remote: http://rubygems.org/
11
+ specs:
12
+ diff-lcs (1.1.2)
13
+ gettext (0.5.0)
14
+ json (1.4.6)
15
+ mime-types (1.16)
16
+ one_sky (0.0.2)
17
+ json (~> 1.4.6)
18
+ rest-client (~> 1.6.1)
19
+ rest-client (1.6.1)
20
+ mime-types (>= 1.16)
21
+ rspec (2.2.0)
22
+ rspec-core (~> 2.2)
23
+ rspec-expectations (~> 2.2)
24
+ rspec-mocks (~> 2.2)
25
+ rspec-core (2.2.1)
26
+ rspec-expectations (2.2.0)
27
+ diff-lcs (~> 1.1.2)
28
+ rspec-mocks (2.2.0)
29
+ module GetText (0.14.6)
30
+
31
+ PLATFORMS
32
+ ruby
33
+
34
+ DEPENDENCIES
35
+ bundler (~> 1.0.0)
36
+ gettext (~> 0.5.0)
37
+ gettext-one_sky!
38
+ one_sky (~> 0.0.2)
39
+ rspec (~> 2.2.0)
40
+ module GetText (~> 0.14.4)
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010-2011 {OneSky, Inc.}[link:http://www.oneskyapp.com]
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUmodule GetTextS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,131 @@
1
+ = Gettext OneSky
2
+
3
+
4
+ OneSky[link:http://www.oneskyapp.com] is a new service that lets developers reach a global audience by easily enabling them to translate their websites and applications to different languages through the help of the community.
5
+
6
+ This gem integrates OneSky and gettext. At its most basic, it allows you to take the phrases defined under gettext .pot file and upload it to OneSky[link:http://www.oneskyapp.com] for translation by the community. Afterwards, you can download available translations and save them as .po files.
7
+
8
+ This gem is supporting Ruby projects using gettext gem to handle localization. If you are using i18n, checkout i18n-one_sky gem
9
+
10
+
11
+ = PO file support
12
+
13
+ 1. One layer of Context
14
+ 2. Text Domain as page
15
+ 3. Reserved Context: Singular, Plural and General
16
+
17
+
18
+ --- One layer of Context
19
+
20
+ Onesky currently only support 1 layer of context. That means only either "msgctxt" / "msgstr[0]" / "msgstr[1]" will be supported but not both.
21
+
22
+
23
+ --- Text Domain as page
24
+
25
+ Regarding the text domain, please use the file name as the page parameter. That means if the po file is abc.po, just add the string with parameter page=abc.po.
26
+
27
+
28
+ --- Reserved Context: Singular, Plural and General
29
+
30
+ "Singular" / "Plural" / "General" are reserved context words.
31
+
32
+ Onesky only support "Singular" and "Plural" for po files. That means only [0] and [1] are used.
33
+ Which will return in result:
34
+
35
+ {"key": { "Singular": "Word", "Plural": "Words"}}
36
+
37
+ For the stuff without context, you may use "General" if there are other strings with same string key but different context:
38
+
39
+ {"key_of_book": { "General": "book", "verb": "book", "noun": "book"}}
40
+
41
+
42
+ = Using inside a Rails project
43
+
44
+ 1. Generate OneSky API key
45
+ 2. Generate .pot
46
+ 3. Upload .pot to OneSky
47
+ 4. Upload translated .po to OneSky
48
+ 5. Download latest translated files .po from OneSky
49
+ 6. Overwrite old .po files
50
+ 7. Generate .mo files
51
+
52
+
53
+ --- Generate OneSky API key
54
+
55
+ If you haven't done so yet, log in to your account at OneSky[link:http://www.oneskyapp.com] and create a new project for this Rails app. Also, find the API key and secret which you will supply here:
56
+
57
+ rails generate one_sky:init my_api_key my_api_secret my_project
58
+
59
+ This will create the configuration file config/one_sky.yml.
60
+
61
+
62
+ --- Generate .pot
63
+
64
+ Use the gettext rake task
65
+
66
+ rake updatepo
67
+
68
+
69
+ --- Upload .pot to OneSky
70
+
71
+ So assuming that you already have your .pot file generated, it's time now to submit these phrases to OneSky:
72
+
73
+ rake one_sky:upload_phrases
74
+
75
+
76
+ --- Upload translated .po to OneSky
77
+
78
+ You may have some translated .po files in your repository. Upload them to Onesky:
79
+
80
+ rake one_sky:upload_translated_phrases
81
+
82
+
83
+ --- Download latest translated files .po from OneSky
84
+
85
+ Then ask your translators to provide the translations using the OneSky[link:http://www.oneskyapp.com] website. When they're done and a few new languages are available, download them by:
86
+
87
+ rake one_sky:download_translations
88
+
89
+ This will save the translations inside po/**/from_one_sky.po files.
90
+
91
+
92
+ --- Overwrite old .po files
93
+
94
+ Overwrite your own .po files by the downloaded from_one_sky.po file.
95
+
96
+
97
+ You now have more languages supported and it's just a matter of pushing again to production. You can call upload and download over and over as you incrementally internationalize your site.
98
+
99
+
100
+ = Using outside Rails
101
+
102
+ The steps are similar. Install it like any regular gem:
103
+
104
+ gem install gettext-one_sky
105
+
106
+ And see the gettext-one_sky.rake and GetText::OneSky::SimpleClient for the methods available in Ruby.
107
+
108
+
109
+ = TODO
110
+
111
+ * merge downloaded translation with .pot to generate the .po files
112
+ * upload existing .po files to onesky service
113
+ * handle textdomain.po
114
+
115
+
116
+ = History
117
+
118
+ See CHANGELOG[link:CHANGELOG.html]
119
+
120
+
121
+ = Credits
122
+
123
+ See CREDITS[link:CREDITS.html]
124
+
125
+
126
+ = License
127
+
128
+ Copyright 2010-2011 {OneSky, Inc.}[link:http://www.oneskyapp.com]
129
+
130
+ Licensed under the {MIT License}[link:MIT-LICENSE.html].
131
+
@@ -0,0 +1,13 @@
1
+ require 'bundler'
2
+ Bundler::GemHelper.install_tasks
3
+
4
+ # = RDoc
5
+ require 'rake/rdoctask'
6
+
7
+ Rake::RDocTask.new do |t|
8
+ t.rdoc_dir = 'rdoc'
9
+ t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
10
+ t.options << '--charset' << 'utf-8'
11
+ t.rdoc_files.include('README.rdoc', 'MIT-LICENSE', 'CHANGELOG', 'CREDITS', 'lib/**/*.rb')
12
+ end
13
+
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "gettext-one_sky/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "gettext-one_sky"
7
+ s.version = GetText::Onesky::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Eddie Lau"]
10
+ s.email = ["tatonlto@gmail.com"]
11
+ s.homepage = "http://rubygems.org/gems/gettext-one_sky"
12
+ s.summary = %q{gettext extensions using OneSky -- the community-powered translation service.}
13
+ s.description = %q{This is a backport of i18n-one_sky-ruby to support gettext. i18n is not included in Rails before Rails 2.2. This gem handles the downloading and uploading translation files (.po) to Onesky server by calling Onesky API.}
14
+
15
+ s.rubyforge_project = "gettext-one_sky"
16
+
17
+ s.add_dependency "gettext", "~> 2.0.0"
18
+ s.add_dependency "one_sky", "~> 0.0.2"
19
+ s.add_dependency "thor", "~> 0.14.4"
20
+
21
+ s.add_development_dependency "rspec", "~> 2.2.0"
22
+ s.add_development_dependency "bundler", "~> 1.0.0"
23
+
24
+ s.files = `git ls-files`.split("\n")
25
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
26
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
27
+ s.require_paths = ["lib"]
28
+ end
@@ -0,0 +1,30 @@
1
+ module OneSky
2
+ module Generators
3
+ class InitGenerator < ::Rails::Generators::Base
4
+ desc "This generator configures gettext-one_sky for use on this Rails project."
5
+ argument :api_key, :type => :string, :desc => "The API key you got from OneSky"
6
+ argument :api_secret, :type => :string, :desc => "The API secret you got from OneSky"
7
+ argument :project, :type => :string, :desc => "The name of the OneSky project"
8
+ class_option :force, :type => :boolean, :default => false, :desc => "Overwrite if config file already exists"
9
+ @@config_file = [Rails.root.to_s, 'config', 'one_sky.yml'].join('/')
10
+
11
+ def remove_config_file
12
+ if File.exists? @@config_file
13
+ if options.force?
14
+ say_status("warning", "config file already exists and is being overwritten.", :yellow)
15
+ remove_file @@config_file
16
+ else
17
+ say_status("error", "config file already exists. Use --force to overwrite.", :red)
18
+ raise "Error: OneSky config file exists."
19
+ end
20
+ end
21
+ end
22
+
23
+ def create_config_file
24
+ config_hash = {"api_key" => api_key, "api_secret" => api_secret, "project" => project}
25
+ create_file(@@config_file, config_hash.to_yaml)
26
+ say_status("info", "config file #{@@config_file} created.", :green)
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,10 @@
1
+ require 'gettext'
2
+ require 'one_sky'
3
+ require 'gettext-one_sky/rails/railtie.rb' if defined? Rails
4
+
5
+ module GetText
6
+ module OneSky
7
+ autoload :SimpleClient, 'gettext-one_sky/simple_client'
8
+ class DefaultLocaleMismatchError < StandardError; end
9
+ end
10
+ end
@@ -0,0 +1,44 @@
1
+ module GetText
2
+ module OneSky
3
+ class MyPoData < GetText::RMsgMerge::PoData
4
+
5
+ def generate_po_entry(msgid)
6
+ str = ""
7
+ str << @msgid2comment[msgid]
8
+ if str[-1] != "\n"[0]
9
+ str << "\n"
10
+ end
11
+
12
+ id = msgid.gsub(/"/, '\"').gsub(/\r/, '')
13
+ msgstr = @msgid2msgstr[msgid].gsub(/"/, '\"').gsub(/\r/, '')
14
+
15
+ # MONKEY PATCHING START
16
+ if id.include?("\004")
17
+ ids = id.split(/\004/)
18
+ context = ids[0]
19
+ id = ids[1]
20
+ str << "msgctxt " << __conv(context) << "\n"
21
+ end
22
+ # MONKEY PATCHING END
23
+
24
+ if id.include?("\000")
25
+ ids = id.split(/\000/)
26
+ str << "msgid " << __conv(ids[0]) << "\n"
27
+ ids[1..-1].each do |single_id|
28
+ str << "msgid_plural " << __conv(single_id) << "\n"
29
+ end
30
+
31
+ msgstr.split("\000").each_with_index do |m, n|
32
+ str << "msgstr[#{n}] " << __conv(m) << "\n"
33
+ end
34
+ else
35
+ str << "msgid " << __conv(id) << "\n"
36
+ str << "msgstr " << __conv(msgstr) << "\n"
37
+ end
38
+
39
+ str << "\n"
40
+ str
41
+ end
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,103 @@
1
+ require 'gettext/tools/poparser'
2
+ require 'gettext/tools/rmsgmerge'
3
+ require 'gettext-one_sky/my_po_data'
4
+
5
+ module GetText
6
+ module OneSky
7
+ class MyPoParser < GetText::PoParser
8
+
9
+ # MONKEY PATCHING START
10
+ # override to include blank trsanslation messages
11
+ def on_message(msgid, msgstr)
12
+ @data[msgid] = msgstr
13
+ @data.set_comment(msgid, @comments.join("\n"))
14
+
15
+ @comments.clear
16
+ @msgctxt = ""
17
+ end
18
+ # MONKEY PATCHING END
19
+
20
+ def parse_po_file(path, lang_code=nil)
21
+ gettext_formatted_messages = self.parse_file(path, GetText::RMsgMerge::PoData.new, false)
22
+
23
+ page_name = File.basename(path).gsub(/.pot$/, '.po')
24
+ to_onesky_format(gettext_formatted_messages, page_name, lang_code)
25
+ end
26
+
27
+ def from_onesky_format(pot_file_path, new_phrases)
28
+ original_phrases = self.parse_file(pot_file_path, GetText::OneSky::MyPoData.new, false)
29
+
30
+ original_phrases.each_msgid do |full_msg_id|
31
+ msg_str = original_phrases.msgstr(full_msg_id)
32
+
33
+ if full_msg_id.include? "\004"
34
+ msg_context, msg_id = full_msg_id.split(/\004/)
35
+
36
+ if new_phrases.has_key?(msg_id) && new_phrases[msg_id].has_key?(msg_context)
37
+ value = new_phrases[msg_id][msg_context]
38
+ elsif msg_id.include? "\000"
39
+ msg_singular, msg_plural = msg_id.split(/\000/)
40
+ value = new_phrases[msg_singular]["Singular"] + "\000" + new_phrases[msg_plural]["Plural"]
41
+ else
42
+ value = ""
43
+ end
44
+ elsif full_msg_id.include? "\000"
45
+ msg_singular, msg_plural = full_msg_id.split(/\000/)
46
+ value = new_phrases[msg_singular]["Singular"] + "\000" + new_phrases[msg_plural]["Plural"]
47
+ else
48
+ if new_phrases[full_msg_id].is_a?(Hash)
49
+ value = new_phrases[full_msg_id]["General"]
50
+ else
51
+ value = new_phrases[full_msg_id]
52
+ end
53
+ end
54
+
55
+ original_phrases[full_msg_id] = value
56
+ original_phrases.set_comment(full_msg_id, "")
57
+ end
58
+
59
+ original_phrases
60
+ end
61
+
62
+ def to_onesky_format(messages, file_name, lang_code=nil)
63
+ slices = []
64
+ lang_options = Hash.new
65
+ lang_options[:language] = lang_code if lang_code
66
+
67
+ messages.each_msgid do |full_msg_id|
68
+ msg_id = full_msg_id
69
+ msg_str = messages.msgstr(msg_id)
70
+
71
+ components = Hash.new
72
+ if msg_id.include? "\004"
73
+ msg_context, msg_id = msg_id.split(/\004/)
74
+ components[:context] = msg_context
75
+ end
76
+
77
+ if msg_id.include? "\000"
78
+ msg_singular, msg_plural = msg_id.split(/\000/)
79
+ trans_singular, trans_plural = msg_str.split(/\000/)
80
+
81
+ slices << {:context => "Singular", :string => msg_singular, :"string-key" => msg_singular, :page => file_name, :translation => trans_singular || ""}.merge(lang_options)
82
+ slices << {:context => "Plural", :string => msg_plural, :"string-key" => msg_plural, :page => file_name, :translation => trans_plural || ""}.merge(lang_options)
83
+ else
84
+ slices << components.merge(:string => msg_id, :"string-key" => msg_id, :translation => msg_str, :page => file_name).merge(lang_options)
85
+ end
86
+ end
87
+
88
+ assign_general_context(slices)
89
+
90
+ slices
91
+ end
92
+
93
+ def assign_general_context(slices)
94
+ general_slices = slices.select{|slice| !slice.has_key?(:context)}
95
+ general_slices.each do |general_slice|
96
+ if slices.select{|slice| slice[:string] == general_slice[:string] && slice.has_key?(:context)}.any?
97
+ general_slice[:context] = "General"
98
+ end
99
+ end
100
+ end
101
+ end
102
+ end
103
+ end
@@ -0,0 +1,9 @@
1
+ module GetText
2
+ module Onesky
3
+ class Railtie < ::Rails::Railtie
4
+ rake_tasks do
5
+ load "gettext-one_sky/rails/tasks/gettext-one_sky.rake"
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,31 @@
1
+ namespace :one_sky do
2
+ desc "Upload phrases (.pot files) for translation to OneSky."
3
+ task :upload_phrases => :environment do
4
+ client = GetText::OneSky::SimpleClient.new
5
+
6
+ client.load_phrases
7
+ client.upload_phrases
8
+
9
+ puts "Phrases in .pot file uploaded to OneSky. Please ask your translators to... well... get translating."
10
+ end
11
+
12
+ desc "Upload translated phrases (.po files) to OneSky."
13
+ task :upload_translated_phrases => :environment do
14
+ client = GetText::OneSky::SimpleClient.new
15
+
16
+ client.load_translations
17
+ client.upload_translations
18
+
19
+ puts "Translated phrases in .po files uploaded to OneSky."
20
+ end
21
+
22
+ desc "Download available translations from OneSky and save as .po_from_one_sky."
23
+ task :download_translations do
24
+ client = GetText::OneSky::SimpleClient.new
25
+
26
+ client.download_translations
27
+ client.save_translations
28
+
29
+ puts "Latest translations downloaded and saved to po/**/*.po_from_one_sky files."
30
+ end
31
+ end
@@ -0,0 +1,175 @@
1
+ require 'gettext-one_sky/my_po_parser'
2
+
3
+ module GetText
4
+ module OneSky
5
+
6
+ =begin
7
+ This class is the bridge between the OneSky service and the gettext file storgage.
8
+ It takes the phrases defined in gettext's default locale and uploads them to OneSky for translation.
9
+ Then it downloads available translations and saves them as .po files.
10
+
11
+ A regular workflow would then look like:
12
+ initialize -> load_phrases -> upload_phrases -> download_translations
13
+
14
+
15
+ * Initilaize *
16
+
17
+ When you initialize a client inside a Rails project,
18
+ it will take the OneSky configuration variables supplied when you called rails generate one_sky:init.
19
+
20
+ When you use this client outside of Rails,
21
+ credentials are expected to come from environment variables: ONESKY_API_KEY, ONESKY_API_SECRET, ONESKY_PROJECT.
22
+ You can override these defaults by providing a hash of options:
23
+ {:api_key => ..., :api_secret => ..., :project => ...}
24
+
25
+ =end
26
+ class SimpleClient
27
+ attr_reader :phrases_nested, :phrases_flat, :translations_flat, :translations_from_onesky
28
+ # The base OneSky project. Gives you low-level access to the API gem.
29
+ attr_reader :project
30
+
31
+ def initialize(options = {})
32
+ options = default_options.merge!(options)
33
+ @project = ::OneSky::Project.new(options[:api_key], options[:api_secret], options[:project])
34
+ @one_sky_locale = @project.details["base_locale"]
35
+ @one_sky_languages = @project.languages
36
+ end
37
+
38
+ # Parse and load phrases from .pot file.
39
+ # If not a Rails project, manually supply the path where the .pot file located.
40
+ def load_phrases(path=nil)
41
+ @phrases_flat = []
42
+
43
+ Dir.glob("#{po_dir_path(path)}/**/*.pot").each do |file_path|
44
+ @phrases_flat += parse_phrase_file(file_path)
45
+ end
46
+ end
47
+
48
+ # Upload phrases to Onesky server
49
+ def upload_phrases(path = nil)
50
+ load_phrases(path) unless @phrases_flat
51
+
52
+ @project.input_bulk(@phrases_flat, :page => @phrases_flat.first[:page])
53
+ end
54
+
55
+ # Parse and load translated phrases from .po files
56
+ # If not a Rails project, manually supply the path where the .po file path located.
57
+ def load_translations(path=nil)
58
+ @translations_flat = []
59
+
60
+ Dir.glob("#{po_dir_path(path)}/**/*.po").each do |file_path|
61
+ lang_code = File.dirname(file_path).split("/").last
62
+
63
+ @translations_flat += parse_phrase_file(file_path, lang_code)
64
+ end
65
+ end
66
+
67
+ # Upload translated phrases to Onesky server
68
+ def upload_translations(path=nil)
69
+ load_translations(path) unless @translations_flat
70
+
71
+ @translations_flat.each do |values|
72
+ @project.send(:post, "/string/translate", values)
73
+ end
74
+ end
75
+
76
+ # Download all available translations from Onesky server
77
+ def download_translations
78
+ @translations_from_onesky = @project.output
79
+ end
80
+
81
+ # Download all available translations from Onesky server and save them as *.po files.
82
+ def save_translations(path=nil)
83
+ download_translations unless @translations_from_onesky
84
+
85
+ update_translation_files_from_onesky(po_dir_path(path), @translations_from_onesky)
86
+ end
87
+
88
+
89
+
90
+ protected
91
+
92
+ def default_options
93
+ if defined? Rails
94
+ YAML.load_file([Rails.root.to_s, 'config', 'one_sky.yml'].join('/')).symbolize_keys
95
+ else
96
+ {:api_key => ENV["ONESKY_API_KEY"], :api_secret => ENV["ONESKY_API_SECRET"], :project => ENV["ONESKY_PROJECT"]}
97
+ end
98
+ end
99
+
100
+ def po_dir_path(path)
101
+ if defined? Rails
102
+ path ||= [Rails.root.to_s, "po"].join("/")
103
+ else
104
+ raise ArgumentError, "Please supply the po directory path where locale files are to be downloaded." unless path && File.directory?(path)
105
+ path = path.chop if path =~ /\/$/
106
+ end
107
+
108
+ path
109
+ end
110
+
111
+ def update_translation_files_from_onesky(po_dir_path, translations)
112
+ # Delete all existing one_sky translation files before downloading a new set.
113
+ File.delete(*Dir.glob("#{po_dir_path}/**/*.po_from_one_sky"))
114
+
115
+ # Process each locale and save to file
116
+ translations.each_pair do |text_domain, values|
117
+ file_name = text_domain.gsub(/\.po$/, '')
118
+ pot_file_path = [po_dir_path, file_name + ".pot"].join('/')
119
+
120
+ values.each_pair do |lang_code, translated_phrases|
121
+ language_dir = "#{po_dir_path}/#{lang_code}"
122
+ Dir.mkdir(language_dir) unless File.exists?(language_dir)
123
+ po_from_one_sky_file = "#{language_dir}/#{file_name}.po_from_one_sky"
124
+
125
+ save_locale(pot_file_path, po_from_one_sky_file, lang_code, translated_phrases)
126
+ end
127
+ end
128
+ end
129
+
130
+ def save_locale(pot_file_path, po_file_name, lang_code, new_phrases)
131
+ my_po_parser = GetText::OneSky::MyPoParser.new
132
+ updated_phrases = my_po_parser.from_onesky_format(pot_file_path, new_phrases)
133
+ updated_phrases.set_comment(:last, "# END")
134
+
135
+ lang = @one_sky_languages.find { |e| e["locale"] == lang_code }
136
+
137
+ File.open(po_file_name, 'w') do |f|
138
+ f.print onesky_header(lang)
139
+ f.print updated_phrases.generate_po
140
+ end
141
+ po_file_name
142
+ end
143
+
144
+ def onesky_header(lang)
145
+ "# PLEASE DO NOT EDIT THIS FILE.\n" +
146
+ "# This was downloaded from OneSky. Log in to your OneSky account to manage translations on their website.\n" +
147
+ "# Language code: #{lang['locale']}\n" +
148
+ "# Language name: #{lang['locale_name']}\n" +
149
+ "# Language English name: #{lang['eng_name']}\n" +
150
+ "#\n"
151
+ "#\n"
152
+ end
153
+
154
+ def parse_translated_file(path=nil)
155
+ if defined? Rails
156
+ path ||= File.join(RAILS_ROOT, '/po', "**/*.po")
157
+ else
158
+ raise ArgumentError, "Please supply the path where the .po file is located." unless path
159
+ path = path.chop if path =~ /\/$/
160
+ end
161
+
162
+ parser = GetText::OneSky::MyPoParser.new
163
+ parser.parse_po_file(path)
164
+ end
165
+
166
+ def parse_phrase_file(path=nil, lang_code=nil)
167
+ raise ArgumentError, "Please supply the path where the .pot file is located." unless path
168
+
169
+ parser = GetText::OneSky::MyPoParser.new
170
+ parser.parse_po_file(path, lang_code)
171
+ end
172
+ end
173
+ end
174
+ end
175
+
@@ -0,0 +1,5 @@
1
+ module GetText
2
+ module Onesky
3
+ VERSION = "0.0.1"
4
+ end
5
+ end
@@ -0,0 +1,27 @@
1
+ require 'helpers'
2
+
3
+ describe "SimpleClient End 2 End workflow" do
4
+ include GetTextOneSkySpecHelpers
5
+
6
+ before do
7
+ delete_onesky_project
8
+ create_onesky_project
9
+
10
+ @client = create_simple_client_and_load
11
+ end
12
+
13
+ describe "#upload_phrases" do
14
+ it "" do
15
+ @client.upload_phrases
16
+ @client.upload_transalcations
17
+
18
+ po_dir_path = [File.dirname(__FILE__), 'po'].join('/')
19
+ pot_file_path = [File.dirname(__FILE__), "po", "simple_client.pot"].join('/')
20
+
21
+ @client.download_translations(po_dir_path, pot_file_path).should be_a_kind_of Array
22
+ Dir.glob("#{po_dir_path}/**/from_one_sky.po").size.should >= 1
23
+
24
+ @client.translations["Hello"].should == "Too"
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,64 @@
1
+ $:.unshift File.expand_path('..', __FILE__)
2
+ $:.unshift File.expand_path('../../lib', __FILE__)
3
+
4
+ require 'gettext-one_sky'
5
+
6
+ module GetTextOneSkySpecHelpers
7
+ def create_simple_client
8
+ raise "Please set environment variables: ONESKY_API_KEY, ONESKY_API_SECRET and ONESKY_SPEC_PROJ (default: gettextoneskyspec) before running spec." unless [ENV["ONESKY_API_KEY"], ENV["ONESKY_API_SECRET"]].all?
9
+ GetText.locale = "en_US"
10
+ client = GetText::OneSky::SimpleClient.new(options)
11
+ end
12
+
13
+ def create_simple_project
14
+ OneSky::Project.new(options[:api_key], options[:api_secret], options[:project])
15
+ end
16
+
17
+ def create_simple_client_and_load
18
+ client = create_simple_client
19
+
20
+ load_phrases(client)
21
+
22
+ client
23
+ end
24
+
25
+ def delete_onesky_project(project)
26
+ begin
27
+ project.send(:post, "/project/delete", {:project => test_project_code})
28
+ rescue RestClient::BadRequest
29
+ retry
30
+ rescue OneSky::ApiError
31
+ end
32
+ end
33
+
34
+ def create_onesky_project(project)
35
+ project.send(:post, "/project/add", {:project => test_project_code, :type => "ruby", :"base-language-code" => "en_US"})
36
+ end
37
+
38
+ protected
39
+
40
+ def load_phrases(client)
41
+ client.load_phrases(po_dir_path)
42
+ end
43
+
44
+ def load_translations(client)
45
+ client.load_translations(po_dir_path)
46
+ end
47
+
48
+ def po_dir_path
49
+ [File.dirname(__FILE__), "po"].join("/")
50
+ end
51
+
52
+ # hopefully not accidentally delete other real translation project
53
+ def test_project_code
54
+ "ONESKY_TEST_#{ENV["ONESKY_SPEC_PROJ"]}"
55
+ end
56
+
57
+ def options
58
+ {
59
+ :api_key => ENV["ONESKY_API_KEY"],
60
+ :api_secret => ENV["ONESKY_API_SECRET"],
61
+ :project => test_project_code || "gettextoneskyspec"
62
+ }
63
+ end
64
+ end
@@ -0,0 +1,53 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: sample_client 1.0.0\n"
9
+ "POT-Creation-Date: 2011-07-30 23:34+0800\n"
10
+ "PO-Revision-Date: 2011-07-30 22:48+0800\n"
11
+ "Last-Translator: \n"
12
+ "Language-Team: LANGUAGE <LL@li.org>\n"
13
+ "Language: \n"
14
+ "MIME-Version: 1.0\n"
15
+ "Content-Type: text/plain; charset=UTF-8\n"
16
+ "Content-Transfer-Encoding: 8bit\n"
17
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
18
+
19
+ #: general msg
20
+ msgid "Hello"
21
+ msgstr "Updated Hello Plain"
22
+
23
+
24
+ #: single and plural msg
25
+ msgid "Stock"
26
+ msgid_plural "Stocks"
27
+ msgstr[0] "Updated Singular Stock"
28
+ msgstr[1] "Updated Plural Stocks"
29
+
30
+
31
+ #: msg with same name as context-ed msg
32
+ msgid "Good to Enter"
33
+ msgstr "Updated Time to enter order"
34
+
35
+
36
+ #: msg with context
37
+ msgctxt "Long"
38
+ msgid "Good to Enter"
39
+ msgstr "Updated Expect it to raise. Time to enter order"
40
+
41
+
42
+ #: msg with context
43
+ msgctxt "Short"
44
+ msgid "Good to Enter"
45
+ msgstr "Updated Expect it to drop. Time to enter order"
46
+
47
+
48
+ #: app/views/demo/index.rhtml:5
49
+ msgctxt "Not Supporting multi level of context and plural"
50
+ msgid "Should upload this singular phrase without context"
51
+ msgid_plural "Should upload this plural phrase without context"
52
+ msgstr[0] "Updated CONTEXT AND SING"
53
+ msgstr[1] "Updated CONTEXT AND PLURAL"
@@ -0,0 +1,54 @@
1
+ # SOME DESCRIPTIVE TITLE.
2
+ # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER
3
+ # This file is distributed under the same license as the PACKAGE package.
4
+ # FIRST AUTHOR <EMAIL@ADDRESS>, YEAR.
5
+ #
6
+ msgid ""
7
+ msgstr ""
8
+ "Project-Id-Version: sample_client 1.0.0\n"
9
+ "POT-Creation-Date: 2011-07-30 23:34+0800\n"
10
+ "PO-Revision-Date: 2011-07-30 22:48+0800\n"
11
+ "Last-Translator: \n"
12
+ "Language-Team: LANGUAGE <LL@li.org>\n"
13
+ "Language: \n"
14
+ "MIME-Version: 1.0\n"
15
+ "Content-Type: text/plain; charset=UTF-8\n"
16
+ "Content-Transfer-Encoding: 8bit\n"
17
+ "Plural-Forms: nplurals=INTEGER; plural=EXPRESSION;\n"
18
+
19
+ #: general msg
20
+ msgid "Hello"
21
+ msgstr ""
22
+
23
+
24
+ #: single and plural msg
25
+ msgid "Stock"
26
+ msgid_plural "Stocks"
27
+ msgstr[0] ""
28
+ msgstr[1] ""
29
+
30
+
31
+ #: msg with same name as context-ed msg
32
+ msgid "Good to Enter"
33
+ msgstr ""
34
+
35
+
36
+ #: msg with context
37
+ msgctxt "Long"
38
+ msgid "Good to Enter"
39
+ msgstr ""
40
+
41
+
42
+ #: msg with context
43
+ msgctxt "Short"
44
+ msgid "Good to Enter"
45
+ msgstr ""
46
+
47
+
48
+ #: app/views/demo/index.rhtml:5
49
+ msgctxt "Not Supporting multi level of context and plural"
50
+ msgid "Should upload this singular phrase without context"
51
+ msgid_plural "Should upload this plural phrase without context"
52
+ msgstr[0] ""
53
+ msgstr[1] ""
54
+
@@ -0,0 +1,142 @@
1
+ require 'helpers'
2
+
3
+ describe "SimpleClient" do
4
+ it "raises an error when initialization values are nil." do
5
+ lambda { @client = GetText::OneSky::SimpleClient.new(:api_key => nil, :api_secret => nil, :project => nil) }.should raise_error ArgumentError
6
+ end
7
+ end
8
+
9
+ describe "SimpleClient" do
10
+ include GetTextOneSkySpecHelpers
11
+
12
+ before(:all) do
13
+ @project = create_simple_project
14
+ delete_onesky_project(@project)
15
+ create_onesky_project(@project)
16
+
17
+ @pot_file_path = [File.dirname(__FILE__), "po"].join('/')
18
+ end
19
+
20
+ before do
21
+ @client = create_simple_client_and_load
22
+ end
23
+
24
+ describe "project with phrases inside .pot file" do
25
+ describe "#load_phrases" do
26
+ it "should load flattened phrases." do
27
+ expected = [{:"string-key"=>"Hello", :page=>"simple_client.po", :string=>"Hello", :translation=>""},
28
+ {:"string-key"=>"Stock", :page=>"simple_client.po", :string=>"Stock", :context=>"Singular", :translation=>""},
29
+ {:"string-key"=>"Stocks", :page=>"simple_client.po", :string=>"Stocks", :context=>"Plural", :translation=>""},
30
+ {:"string-key"=>"Good to Enter", :page=>"simple_client.po", :string=>"Good to Enter", :context=>"General", :translation=>""},
31
+ {:"string-key"=>"Good to Enter", :page=>"simple_client.po", :string=>"Good to Enter", :context=>"Long", :translation=>""},
32
+ {:"string-key"=>"Good to Enter", :page=>"simple_client.po", :string=>"Good to Enter", :context=>"Short", :translation=>""},
33
+ {:"string-key"=>"Should upload this singular phrase without context", :page=>"simple_client.po", :string=>"Should upload this singular phrase without context", :context=>"Singular", :translation=>""},
34
+ {:"string-key"=>"Should upload this plural phrase without context", :page=>"simple_client.po", :string=>"Should upload this plural phrase without context", :context=>"Plural", :translation=>""}]
35
+
36
+ @client.phrases_flat.should == expected
37
+ end
38
+ end
39
+
40
+ describe "Translation output from Onesky" do
41
+ before do
42
+ @client.upload_phrases.should be_true
43
+ @client.download_translations
44
+ end
45
+
46
+ describe "#download_translations" do
47
+ it "transforms translation result from Onesky" do
48
+ expected = { "simple_client.po" =>
49
+ {"en_US"=>
50
+ {"Good to Enter"=>
51
+ {"General" => "Good to Enter", "Short"=>"Good to Enter", "Long"=>"Good to Enter"},
52
+ "Stock"=>
53
+ {"Singular"=>"Stock"},
54
+ "Should upload this singular phrase without context"=>
55
+ {"Singular"=>"Should upload this singular phrase without context"},
56
+ "Should upload this plural phrase without context"=>
57
+ {"Plural"=>"Should upload this plural phrase without context"},
58
+ "Hello"=>"Hello",
59
+ "Stocks"=>
60
+ {"Plural"=>"Stocks"}
61
+ }
62
+ }
63
+ }
64
+
65
+ @client.translations_from_onesky.should == expected
66
+ end
67
+ end
68
+
69
+ describe "#save_translations" do
70
+ it "saves the translation into .po files of all available languages." do
71
+ @client.save_translations(@pot_file_path)
72
+
73
+ Dir.glob("#{po_dir_path}/**/simple_client.po_from_one_sky").size.should >= 1
74
+ end
75
+ end
76
+ end
77
+ end
78
+
79
+ describe "with translated .po files" do
80
+ before do
81
+ load_translations(@client)
82
+ end
83
+
84
+ describe "#load_translations" do
85
+ it "should load flattened translations." do
86
+ expected =
87
+ [{:"string-key"=>"Hello", :page=>"simple_client.po", :string=>"Hello", :translation=>"Updated Hello Plain", :language => "en_US"},
88
+ {:"string-key"=>"Stock", :page=>"simple_client.po", :string=>"Stock", :context=>"Singular", :translation=>"Updated Singular Stock", :language => "en_US"},
89
+ {:"string-key"=>"Stocks", :page=>"simple_client.po", :string=>"Stocks", :context=>"Plural", :translation=>"Updated Plural Stocks", :language => "en_US"},
90
+ {:"string-key"=>"Good to Enter", :page=>"simple_client.po", :string=>"Good to Enter", :context=>"General", :translation=>"Updated Time to enter order", :language => "en_US"},
91
+ {:"string-key"=>"Good to Enter", :page=>"simple_client.po", :string=>"Good to Enter", :context=>"Long", :translation=>"Updated Expect it to raise. Time to enter order", :language => "en_US"},
92
+ {:"string-key"=>"Good to Enter", :page=>"simple_client.po", :string=>"Good to Enter", :context=>"Short", :translation=>"Updated Expect it to drop. Time to enter order", :language => "en_US"},
93
+ {:"string-key"=>"Should upload this singular phrase without context", :page=>"simple_client.po", :string=>"Should upload this singular phrase without context", :context=>"Singular", :translation=>"Updated CONTEXT AND SING", :language => "en_US"},
94
+ {:"string-key"=>"Should upload this plural phrase without context", :page=>"simple_client.po", :string=>"Should upload this plural phrase without context", :context=>"Plural", :translation=>"Updated CONTEXT AND PLURAL", :language => "en_US"}]
95
+
96
+ @client.translations_flat.should == expected
97
+ end
98
+ end
99
+
100
+ describe "retrieving updated translation output from Onesky" do
101
+ before do
102
+ @client.upload_translations.should be_true
103
+ @client.download_translations
104
+ end
105
+
106
+ ########
107
+ # this test case does not work, because the original translation is always the default values returned
108
+ ########
109
+
110
+ # describe "#download_translations" do
111
+ # it "transforms updated translation result from Onesky" do
112
+ # expected = { "simple_client.po" =>
113
+ # {"en_US"=>
114
+ # {"Good to Enter"=>
115
+ # {"General" => "Updated Time to enter order", "Short"=>"Updated Expect it to drop. Time to enter order", "Long"=>"Updated Expect it to raise. Time to enter order"},
116
+ # "Stock"=>
117
+ # {"Singular"=>"Updated Singular Stock"},
118
+ # "Should upload this singular phrase without context"=>
119
+ # {"Singular"=>"Updated CONTEXT AND SING"},
120
+ # "Should upload this plural phrase without context"=>
121
+ # {"Plural"=>"Updated CONTEXT AND PLURAL"},
122
+ # "Hello"=>"Updated Hello Plain",
123
+ # "Stocks"=>
124
+ # {"Plural"=>"Updated Plural Stocks"}
125
+ # }
126
+ # }
127
+ # }
128
+ #
129
+ # @client.translations_from_onesky.should == expected
130
+ # end
131
+ # end
132
+ #
133
+ # describe "#save_translations" do
134
+ # it "saves the updated translation into .po files of all available languages." do
135
+ # @client.save_translations(@pot_file_path)
136
+ #
137
+ # Dir.glob("#{po_dir_path}/**/simple_client.po_from_one_sky").size.should >= 1
138
+ # end
139
+ # end
140
+ end
141
+ end
142
+ end
metadata ADDED
@@ -0,0 +1,172 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: gettext-one_sky
3
+ version: !ruby/object:Gem::Version
4
+ hash: 29
5
+ prerelease:
6
+ segments:
7
+ - 0
8
+ - 0
9
+ - 1
10
+ version: 0.0.1
11
+ platform: ruby
12
+ authors:
13
+ - Eddie Lau
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2011-08-09 00:00:00 +08:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: gettext
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ~>
28
+ - !ruby/object:Gem::Version
29
+ hash: 15
30
+ segments:
31
+ - 2
32
+ - 0
33
+ - 0
34
+ version: 2.0.0
35
+ type: :runtime
36
+ version_requirements: *id001
37
+ - !ruby/object:Gem::Dependency
38
+ name: one_sky
39
+ prerelease: false
40
+ requirement: &id002 !ruby/object:Gem::Requirement
41
+ none: false
42
+ requirements:
43
+ - - ~>
44
+ - !ruby/object:Gem::Version
45
+ hash: 27
46
+ segments:
47
+ - 0
48
+ - 0
49
+ - 2
50
+ version: 0.0.2
51
+ type: :runtime
52
+ version_requirements: *id002
53
+ - !ruby/object:Gem::Dependency
54
+ name: thor
55
+ prerelease: false
56
+ requirement: &id003 !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ~>
60
+ - !ruby/object:Gem::Version
61
+ hash: 47
62
+ segments:
63
+ - 0
64
+ - 14
65
+ - 4
66
+ version: 0.14.4
67
+ type: :runtime
68
+ version_requirements: *id003
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec
71
+ prerelease: false
72
+ requirement: &id004 !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ~>
76
+ - !ruby/object:Gem::Version
77
+ hash: 7
78
+ segments:
79
+ - 2
80
+ - 2
81
+ - 0
82
+ version: 2.2.0
83
+ type: :development
84
+ version_requirements: *id004
85
+ - !ruby/object:Gem::Dependency
86
+ name: bundler
87
+ prerelease: false
88
+ requirement: &id005 !ruby/object:Gem::Requirement
89
+ none: false
90
+ requirements:
91
+ - - ~>
92
+ - !ruby/object:Gem::Version
93
+ hash: 23
94
+ segments:
95
+ - 1
96
+ - 0
97
+ - 0
98
+ version: 1.0.0
99
+ type: :development
100
+ version_requirements: *id005
101
+ description: This is a backport of i18n-one_sky-ruby to support gettext. i18n is not included in Rails before Rails 2.2. This gem handles the downloading and uploading translation files (.po) to Onesky server by calling Onesky API.
102
+ email:
103
+ - tatonlto@gmail.com
104
+ executables: []
105
+
106
+ extensions: []
107
+
108
+ extra_rdoc_files: []
109
+
110
+ files:
111
+ - .gitignore
112
+ - CHANGELOG
113
+ - CREDITS
114
+ - Gemfile
115
+ - Gemfile.lock
116
+ - MIT-LICENSE
117
+ - README.rdoc
118
+ - Rakefile
119
+ - gettext-one_sky.gemspec
120
+ - lib/generators/one_sky/init/init_generator.rb
121
+ - lib/gettext-one_sky.rb
122
+ - lib/gettext-one_sky/my_po_data.rb
123
+ - lib/gettext-one_sky/my_po_parser.rb
124
+ - lib/gettext-one_sky/rails/railtie.rb
125
+ - lib/gettext-one_sky/rails/tasks/gettext-one_sky.rake
126
+ - lib/gettext-one_sky/simple_client.rb
127
+ - lib/gettext-one_sky/version.rb
128
+ - spec/end_to_end_spec.rb
129
+ - spec/helpers.rb
130
+ - spec/po/en_US/simple_client.po
131
+ - spec/po/simple_client.pot
132
+ - spec/simple_client_spec.rb
133
+ has_rdoc: true
134
+ homepage: http://rubygems.org/gems/gettext-one_sky
135
+ licenses: []
136
+
137
+ post_install_message:
138
+ rdoc_options: []
139
+
140
+ require_paths:
141
+ - lib
142
+ required_ruby_version: !ruby/object:Gem::Requirement
143
+ none: false
144
+ requirements:
145
+ - - ">="
146
+ - !ruby/object:Gem::Version
147
+ hash: 3
148
+ segments:
149
+ - 0
150
+ version: "0"
151
+ required_rubygems_version: !ruby/object:Gem::Requirement
152
+ none: false
153
+ requirements:
154
+ - - ">="
155
+ - !ruby/object:Gem::Version
156
+ hash: 3
157
+ segments:
158
+ - 0
159
+ version: "0"
160
+ requirements: []
161
+
162
+ rubyforge_project: gettext-one_sky
163
+ rubygems_version: 1.5.3
164
+ signing_key:
165
+ specification_version: 3
166
+ summary: gettext extensions using OneSky -- the community-powered translation service.
167
+ test_files:
168
+ - spec/end_to_end_spec.rb
169
+ - spec/helpers.rb
170
+ - spec/po/en_US/simple_client.po
171
+ - spec/po/simple_client.pot
172
+ - spec/simple_client_spec.rb