transync 0.0.1
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.
- checksums.yaml +7 -0
- data/.gitignore +23 -0
- data/.ruby-version +1 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +44 -0
- data/Rakefile +1 -0
- data/bin/transync +2 -0
- data/lib/transync.rb +20 -0
- data/lib/transync/gdoc_trans/gdoc_trans.rb +5 -0
- data/lib/transync/gdoc_trans/gdoc_trans_reader.rb +30 -0
- data/lib/transync/gdoc_trans/gdoc_trans_writer.rb +28 -0
- data/lib/transync/sync/gdoc_2_xliff_main.rb +42 -0
- data/lib/transync/sync/gdoc_to_xliff.rb +52 -0
- data/lib/transync/sync/sync_util.rb +20 -0
- data/lib/transync/sync/xliff_2_gdoc_main.rb +45 -0
- data/lib/transync/sync/xliff_to_gdoc.rb +66 -0
- data/lib/transync/version.rb +3 -0
- data/lib/transync/xliff_trans/xliff_trans_reader.rb +69 -0
- data/lib/transync/xliff_trans/xliff_trans_writer.rb +45 -0
- data/settings.SAMPLE.yml +21 -0
- data/test/fixtures/test.en.xliff +43 -0
- data/transync.gemspec +26 -0
- metadata +124 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA1:
|
|
3
|
+
metadata.gz: 8c6fb462e97a836b1fd3aab65f2cc82fef40a3b9
|
|
4
|
+
data.tar.gz: 3a7dae00ef26bd34d28311599dcf06c5fe5b233e
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 1742efaff0fcd8e34d72376cc58049eb59a7348046f30186e2f48dd3151309aa2f428bd62c0779b000fc87f28a194557af1c60659774606fb572a6309dd89b04
|
|
7
|
+
data.tar.gz: 4fb9c221031ac7e94136f798abc317904f1d63293be877aeebc5f78733f8317a5bd2def9d94029bd1d3190ce6cb3f4d7f8c8a04f8bddad7371e0182ce3b4ce37
|
data/.gitignore
ADDED
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
*.gem
|
|
2
|
+
*.rbc
|
|
3
|
+
.bundle
|
|
4
|
+
.config
|
|
5
|
+
.yardoc
|
|
6
|
+
Gemfile.lock
|
|
7
|
+
InstalledFiles
|
|
8
|
+
_yardoc
|
|
9
|
+
coverage
|
|
10
|
+
doc/
|
|
11
|
+
lib/bundler/man
|
|
12
|
+
pkg
|
|
13
|
+
rdoc
|
|
14
|
+
spec/reports
|
|
15
|
+
test/tmp
|
|
16
|
+
test/version_tmp
|
|
17
|
+
tmp
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
settings.yml
|
|
21
|
+
log/*.log*
|
|
22
|
+
.idea
|
|
23
|
+
.transync_log
|
data/.ruby-version
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
2.0.0-p195
|
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (c) 2013 zigomir
|
|
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,44 @@
|
|
|
1
|
+
# Install
|
|
2
|
+
|
|
3
|
+
gem install transync
|
|
4
|
+
|
|
5
|
+
You need settings file needs to be named `settings.yml`. For example see `settings.SAMPLE.yml`.
|
|
6
|
+
|
|
7
|
+
### Example
|
|
8
|
+
|
|
9
|
+
transync x2g path/to/your/xliff/files
|
|
10
|
+
transync g2x path/to/your/xliff/files
|
|
11
|
+
|
|
12
|
+
### Example for testing within this gem
|
|
13
|
+
|
|
14
|
+
rake install && transync x2g test/fixtures
|
|
15
|
+
|
|
16
|
+
# Assumptions:
|
|
17
|
+
|
|
18
|
+
You have xliff files in one directory, named like: common.en.xliff where common is name of the file and also google doc
|
|
19
|
+
spreadsheet tab. en is language and you have google doc with structure where first row is key, language 1, language 2
|
|
20
|
+
|
|
21
|
+
# Config
|
|
22
|
+
|
|
23
|
+
Google doc
|
|
24
|
+
https://docs.google.com/a/astina.ch/spreadsheet/ccc?key=0ApAHdLDpSUFudHhuWU9vQ0ZPaWNoV3VxU045cXhHLWc#gid=3
|
|
25
|
+
key = 0ApAHdLDpSUFudHhuWU9vQ0ZPaWNoV3VxU045cXhHLWc
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
GDOC_KEY = 0ApAHdLDpSUFudHhuWU9vQ0ZPaWNoV3VxU045cXhHLWc
|
|
29
|
+
|
|
30
|
+
# Features
|
|
31
|
+
|
|
32
|
+
Updating GDoc from our xliff files. It won't delete any key, it will only add new or change existing ones.
|
|
33
|
+
|
|
34
|
+
# Source
|
|
35
|
+
|
|
36
|
+
Gem docs available at http://gimite.net/doc/google-drive-ruby
|
|
37
|
+
|
|
38
|
+
# Known issues
|
|
39
|
+
|
|
40
|
+
It won't add keys as it should if not all languages are set in settings.yml
|
|
41
|
+
|
|
42
|
+
# TODO
|
|
43
|
+
|
|
44
|
+
- support for yaml files
|
data/Rakefile
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
require "bundler/gem_tasks"
|
data/bin/transync
ADDED
data/lib/transync.rb
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require_relative 'transync/sync/xliff_2_gdoc_main'
|
|
2
|
+
require_relative 'transync/sync/gdoc_2_xliff_main'
|
|
3
|
+
|
|
4
|
+
module Transync
|
|
5
|
+
|
|
6
|
+
def self.run(mode, path)
|
|
7
|
+
FileUtils.mkdir('.transync_log') unless Dir.exist?('.transync_log')
|
|
8
|
+
|
|
9
|
+
if mode == 'x2g'
|
|
10
|
+
x2g = Xliff2GdocMain.new(path)
|
|
11
|
+
x2g.run
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
if mode == 'g2x'
|
|
15
|
+
g2x = Gdoc2XliffMain.new(path)
|
|
16
|
+
g2x.run
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'google_drive'
|
|
2
|
+
require_relative 'gdoc_trans'
|
|
3
|
+
|
|
4
|
+
class GdocTransReader
|
|
5
|
+
attr_accessor :worksheet
|
|
6
|
+
|
|
7
|
+
def initialize(gdoc_access, tab)
|
|
8
|
+
session = GoogleDrive.login(gdoc_access['email'], gdoc_access['password'])
|
|
9
|
+
|
|
10
|
+
worksheet_tabs = session.spreadsheet_by_key(gdoc_access['key']).worksheets
|
|
11
|
+
# select the right tab from worksheet
|
|
12
|
+
self.worksheet = worksheet_tabs.detect{ |w| w.title == tab }
|
|
13
|
+
abort("#{tab} tab is not defined in GDoc") if self.worksheet.nil?
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def build_trans_hash(language = 'en')
|
|
17
|
+
trans_hash = { title: worksheet.title, language: language, translations: [] }
|
|
18
|
+
|
|
19
|
+
key_column = GdocTrans::WORKSHEET_COLUMNS[:key]
|
|
20
|
+
language_column = GdocTrans::WORKSHEET_COLUMNS[language.to_sym]
|
|
21
|
+
|
|
22
|
+
(GdocTrans::START_ROW..worksheet.num_rows).to_a.each do |row|
|
|
23
|
+
translation = { key: worksheet[row, key_column], value: worksheet[row, language_column] }
|
|
24
|
+
trans_hash[:translations] << translation
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
trans_hash
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
end
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
require 'google_drive'
|
|
2
|
+
require_relative 'gdoc_trans'
|
|
3
|
+
|
|
4
|
+
class GdocTransWriter
|
|
5
|
+
attr_accessor :worksheet
|
|
6
|
+
|
|
7
|
+
def initialize(worksheet)
|
|
8
|
+
@worksheet = worksheet
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def write(row, column, data)
|
|
12
|
+
@worksheet[row, GdocTrans::WORKSHEET_COLUMNS[column.to_sym]] = data
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def shift_up(old_row_num, rows_count)
|
|
16
|
+
# shift up that many times we're missing till the end
|
|
17
|
+
(old_row_num..rows_count).to_a.each do |new_row|
|
|
18
|
+
(1..worksheet.num_cols).to_a.each do |col|
|
|
19
|
+
cell = @worksheet[new_row, col]
|
|
20
|
+
@worksheet[new_row, col] = ''
|
|
21
|
+
@worksheet[new_row + 1, col] = cell
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
@worksheet
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'logger'
|
|
3
|
+
require_relative '../gdoc_trans/gdoc_trans_reader'
|
|
4
|
+
require_relative '../xliff_trans/xliff_trans_writer'
|
|
5
|
+
require_relative 'gdoc_to_xliff'
|
|
6
|
+
require_relative 'sync_util'
|
|
7
|
+
|
|
8
|
+
class Gdoc2XliffMain
|
|
9
|
+
include SyncUtil
|
|
10
|
+
attr_reader :path, :config
|
|
11
|
+
|
|
12
|
+
def initialize(path)
|
|
13
|
+
@path = path
|
|
14
|
+
@config = YAML.load(File.open('settings.yml'))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
logger = Logger.new('.transync_log/gdoc2xliff.log', 'monthly')
|
|
19
|
+
|
|
20
|
+
@config['FILES'].each do |file|
|
|
21
|
+
xliff_translations = check_and_get_xliff_files(@config['LANGUAGES'], path, file)
|
|
22
|
+
gdoc_trans_reader = GdocTransReader.new(@config['GDOC'], file)
|
|
23
|
+
|
|
24
|
+
@config['LANGUAGES'].each do |language|
|
|
25
|
+
options = {
|
|
26
|
+
xliff_translations: xliff_translations,
|
|
27
|
+
gdoc_trans_reader: gdoc_trans_reader,
|
|
28
|
+
language: language,
|
|
29
|
+
logger: logger
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
gdoc_to_xliff = GdocToXliff.new(options)
|
|
33
|
+
dirty, new_xliff_hash = gdoc_to_xliff.sync
|
|
34
|
+
|
|
35
|
+
xliff_trans_writer = XliffTransWriter.new(path, file, new_xliff_hash)
|
|
36
|
+
xliff_trans_writer.save if dirty
|
|
37
|
+
p "#{file} (#{language}) was clean. Already has same keys and values inside Xliff" unless dirty
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
end
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
class GdocToXliff
|
|
2
|
+
attr_accessor :xliff_translations,
|
|
3
|
+
:gdoc_trans_reader,
|
|
4
|
+
:language,
|
|
5
|
+
:logger
|
|
6
|
+
|
|
7
|
+
def initialize(options = {})
|
|
8
|
+
self.xliff_translations = options[:xliff_translations]
|
|
9
|
+
self.gdoc_trans_reader = options[:gdoc_trans_reader]
|
|
10
|
+
self.language = options[:language]
|
|
11
|
+
self.logger = options[:logger]
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def sync
|
|
15
|
+
dirty = false
|
|
16
|
+
gdoc_tab_language = gdoc_trans_reader.build_trans_hash(language)
|
|
17
|
+
file = gdoc_tab_language[:title]
|
|
18
|
+
|
|
19
|
+
new_xliff_hash = {
|
|
20
|
+
language: language,
|
|
21
|
+
file: file,
|
|
22
|
+
translations: []
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
xliff_for_language = xliff_translations.detect{ |x| x[:language] == language }[:translations]
|
|
26
|
+
gdoc_tab_language[:translations].each do |gdoc_trans|
|
|
27
|
+
x_trans = xliff_for_language.detect{ |x| x[:key] == gdoc_trans[:key] }
|
|
28
|
+
|
|
29
|
+
# whole key is missing
|
|
30
|
+
if x_trans.nil?
|
|
31
|
+
p "Adding Key: #{gdoc_trans[:key]} to #{file}(#{language}) and value '#{gdoc_trans[:value]}'"
|
|
32
|
+
logger.info "Adding Key: #{gdoc_trans[:key]} to #{file}(#{language}) and value '#{gdoc_trans[:value]}'"
|
|
33
|
+
|
|
34
|
+
new_xliff_hash[:translations] << gdoc_trans
|
|
35
|
+
dirty = true
|
|
36
|
+
elsif gdoc_trans[:value] != x_trans[:value]
|
|
37
|
+
p "Changing #{file}(#{language}) #{gdoc_trans[:key]}: '#{x_trans[:value]}' to '#{gdoc_trans[:value]}'"
|
|
38
|
+
logger.info "Changing #{file}(#{language}) #{gdoc_trans[:key]}: '#{x_trans[:value]}' to '#{gdoc_trans[:value]}'"
|
|
39
|
+
|
|
40
|
+
x_trans[:value] = gdoc_trans[:value]
|
|
41
|
+
new_xliff_hash[:translations] << x_trans
|
|
42
|
+
dirty = true
|
|
43
|
+
else
|
|
44
|
+
# nothing new
|
|
45
|
+
new_xliff_hash[:translations] << gdoc_trans
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
return dirty, new_xliff_hash
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
end
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
require_relative '../xliff_trans/xliff_trans_reader'
|
|
2
|
+
|
|
3
|
+
module SyncUtil
|
|
4
|
+
|
|
5
|
+
def check_and_get_xliff_files(languages, path, file)
|
|
6
|
+
xliff_translations = []
|
|
7
|
+
|
|
8
|
+
languages.each do |language|
|
|
9
|
+
xliff_reader = XliffTransReader.new(path, file, language, languages)
|
|
10
|
+
if xliff_reader.valid?
|
|
11
|
+
xliff_translations << xliff_reader.get_translations
|
|
12
|
+
else
|
|
13
|
+
abort('Fix your Xliff translations first!')
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
xliff_translations
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'yaml'
|
|
2
|
+
require 'logger'
|
|
3
|
+
require_relative '../gdoc_trans/gdoc_trans_reader'
|
|
4
|
+
require_relative '../gdoc_trans/gdoc_trans_writer'
|
|
5
|
+
require_relative 'xliff_to_gdoc'
|
|
6
|
+
require_relative 'sync_util'
|
|
7
|
+
|
|
8
|
+
class Xliff2GdocMain
|
|
9
|
+
include SyncUtil
|
|
10
|
+
attr_reader :path, :config
|
|
11
|
+
|
|
12
|
+
def initialize(path)
|
|
13
|
+
@path = path
|
|
14
|
+
@config = YAML.load(File.open('settings.yml'))
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def run
|
|
18
|
+
logger = Logger.new('.transync_log/xliff2gdoc.log', 'monthly')
|
|
19
|
+
|
|
20
|
+
@config['FILES'].each do |file|
|
|
21
|
+
xliff_translations = check_and_get_xliff_files(@config['LANGUAGES'], path, file)
|
|
22
|
+
|
|
23
|
+
gdoc_trans_reader = GdocTransReader.new(@config['GDOC'], file)
|
|
24
|
+
gdoc_trans_writer = GdocTransWriter.new(gdoc_trans_reader.worksheet)
|
|
25
|
+
|
|
26
|
+
@config['LANGUAGES'].each do |language|
|
|
27
|
+
options = {
|
|
28
|
+
xliff_translations: xliff_translations,
|
|
29
|
+
gdoc_trans_reader: gdoc_trans_reader,
|
|
30
|
+
gdoc_trans_writer: gdoc_trans_writer,
|
|
31
|
+
language: language,
|
|
32
|
+
languages: @config['LANGUAGES'],
|
|
33
|
+
logger: logger
|
|
34
|
+
}
|
|
35
|
+
xliff_to_gdoc = XliffToGdoc.new(options)
|
|
36
|
+
dirty = xliff_to_gdoc.sync
|
|
37
|
+
|
|
38
|
+
# save it back on google drive
|
|
39
|
+
gdoc_trans_writer.worksheet.save if dirty
|
|
40
|
+
p "#{file} (#{language}) was clean. Already has same keys and values inside GDoc" unless dirty
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
class XliffToGdoc
|
|
2
|
+
attr_accessor :xliff_translations,
|
|
3
|
+
:gdoc_trans_reader,
|
|
4
|
+
:gdoc_trans_writer,
|
|
5
|
+
:language,
|
|
6
|
+
:languages,
|
|
7
|
+
:logger
|
|
8
|
+
|
|
9
|
+
def initialize(options = {})
|
|
10
|
+
self.xliff_translations = options[:xliff_translations]
|
|
11
|
+
self.gdoc_trans_reader = options[:gdoc_trans_reader]
|
|
12
|
+
self.gdoc_trans_writer = options[:gdoc_trans_writer]
|
|
13
|
+
self.language = options[:language]
|
|
14
|
+
self.languages = options[:languages]
|
|
15
|
+
self.logger = options[:logger]
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def sync
|
|
19
|
+
gdoc_tab_language = gdoc_trans_reader.build_trans_hash(language)
|
|
20
|
+
dirty = false
|
|
21
|
+
file = gdoc_tab_language[:title]
|
|
22
|
+
|
|
23
|
+
xliff_for_language = xliff_translations.detect{ |x| x[:language] == language }
|
|
24
|
+
xliff_for_language[:translations].each_with_index do |x_trans, index|
|
|
25
|
+
|
|
26
|
+
# for current xliff translation find the same trans key in google doc
|
|
27
|
+
gdoc_trans = gdoc_tab_language[:translations].detect{ |g_trans| g_trans[:key] == x_trans[:key] }
|
|
28
|
+
current_row = index + GdocTrans::START_ROW
|
|
29
|
+
|
|
30
|
+
# whole key is missing
|
|
31
|
+
if gdoc_trans.nil?
|
|
32
|
+
# heavy coupling
|
|
33
|
+
gdoc_trans_reader.worksheet = gdoc_trans_writer.shift_up(current_row, xliff_for_language[:translations].length)
|
|
34
|
+
# recalculate hashes after shift
|
|
35
|
+
gdoc_tab_language = gdoc_trans_reader.build_trans_hash(language)
|
|
36
|
+
|
|
37
|
+
gdoc_trans_writer.write(current_row, 'key', x_trans[:key])
|
|
38
|
+
gdoc_trans_writer.write(current_row, language, x_trans[:value])
|
|
39
|
+
|
|
40
|
+
p "#{current_row}) Adding Key: #{x_trans[:key]} to #{file}(#{language}) and value '#{x_trans[:value]}'"
|
|
41
|
+
logger.info "#{current_row}) Adding Key: #{x_trans[:key]} to #{file}(#{language}) and value '#{x_trans[:value]}'"
|
|
42
|
+
|
|
43
|
+
# Go for all other languages if key was missing
|
|
44
|
+
languages.each do |key_lang|
|
|
45
|
+
next if key_lang == language
|
|
46
|
+
|
|
47
|
+
xliff_lang = xliff_translations.detect{ |xt| xt[:language] == key_lang }
|
|
48
|
+
xliff_lang_value = xliff_lang[:translations].detect{ |xt| xt[:key] == x_trans[:key] }
|
|
49
|
+
|
|
50
|
+
gdoc_trans_writer.write(current_row, key_lang, xliff_lang_value[:value])
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
dirty = true
|
|
54
|
+
elsif gdoc_trans[:value] != x_trans[:value]
|
|
55
|
+
gdoc_trans_writer.write(current_row, language, x_trans[:value])
|
|
56
|
+
|
|
57
|
+
p "#{current_row}) Changing #{file}(#{language}) #{x_trans[:key]}: '#{gdoc_trans[:value]}' to '#{x_trans[:value]}'"
|
|
58
|
+
logger.info "#{current_row}) Changing #{file}(#{language}) #{x_trans[:key]}: '#{gdoc_trans[:value]}' to '#{x_trans[:value]}'"
|
|
59
|
+
dirty = true
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
dirty
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
end
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
require 'nokogiri'
|
|
2
|
+
|
|
3
|
+
class XliffTransReader
|
|
4
|
+
attr_accessor :dir, :file, :language, :languages
|
|
5
|
+
|
|
6
|
+
def initialize(dir, file, language, languages)
|
|
7
|
+
self.dir = dir
|
|
8
|
+
self.file = file
|
|
9
|
+
self.language = language
|
|
10
|
+
self.languages = languages
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
def get_translations
|
|
14
|
+
data = {
|
|
15
|
+
language: language,
|
|
16
|
+
translations: []
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
open_file do |doc|
|
|
20
|
+
# hacky hack, xliff is dirty as hell
|
|
21
|
+
doc.remove_namespaces!
|
|
22
|
+
doc.xpath('//trans-unit').each do |node|
|
|
23
|
+
data[:translations] << {
|
|
24
|
+
key: node.xpath('source').text,
|
|
25
|
+
value: node.xpath('target').text,
|
|
26
|
+
}
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
data
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
# will go through each and find if any xliff is missing keys for translations
|
|
34
|
+
def valid?
|
|
35
|
+
self.get_translations[:translations].each do |x_trans|
|
|
36
|
+
self.languages.each do |key_lang|
|
|
37
|
+
next if key_lang == language
|
|
38
|
+
|
|
39
|
+
xliff_reader = XliffTransReader.new(self.dir, self.file, key_lang, self.languages)
|
|
40
|
+
xliff_lang = xliff_reader.get_translations[:translations]
|
|
41
|
+
xliff_lang_value = xliff_lang.detect{ |xt| xt[:key] == x_trans[:key] }
|
|
42
|
+
if xliff_lang_value.nil?
|
|
43
|
+
p "#{file}.#{key_lang} is missing translation for key '#{x_trans[:key]}'"
|
|
44
|
+
return false
|
|
45
|
+
end
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
true
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
# Reading from source tags in xliff
|
|
53
|
+
def open_file
|
|
54
|
+
begin
|
|
55
|
+
xml_file = File.open(file_path)
|
|
56
|
+
doc = Nokogiri::XML(xml_file)
|
|
57
|
+
yield doc
|
|
58
|
+
rescue Errno::ENOENT => e
|
|
59
|
+
abort(e.message)
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
private
|
|
64
|
+
|
|
65
|
+
def file_path
|
|
66
|
+
"#{dir}/#{file}.#{language}.xliff"
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require 'builder'
|
|
2
|
+
|
|
3
|
+
class XliffTransWriter
|
|
4
|
+
attr_accessor :data,
|
|
5
|
+
:path,
|
|
6
|
+
:file,
|
|
7
|
+
:language
|
|
8
|
+
|
|
9
|
+
def initialize(path, file, data)
|
|
10
|
+
@path = path
|
|
11
|
+
@file = file
|
|
12
|
+
@language = data[:language]
|
|
13
|
+
@data = data
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def save
|
|
17
|
+
translations = data[:translations]
|
|
18
|
+
|
|
19
|
+
xml = Builder::XmlMarkup.new( :indent => 4 )
|
|
20
|
+
xml.instruct! :xml, :encoding => 'UTF-8'
|
|
21
|
+
xml.xliff :version => '1.2', :xmlns => 'urn:oasis:names:tc:xliff:document:1.2' do |xliff|
|
|
22
|
+
xliff.file :'source-language' => language, :datatype => 'plaintext', :original => 'file.ext' do |file|
|
|
23
|
+
file.body do |body|
|
|
24
|
+
|
|
25
|
+
translations.each do |trans|
|
|
26
|
+
body.tag! 'trans-unit', :id => trans[:key] do |trans_unit|
|
|
27
|
+
trans_unit.source trans[:key]
|
|
28
|
+
trans_unit.target trans[:value]
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
end
|
|
33
|
+
end
|
|
34
|
+
end
|
|
35
|
+
|
|
36
|
+
File.open(file_path, 'w') { |file| file.write(xml.target!) }
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
private
|
|
40
|
+
|
|
41
|
+
def file_path
|
|
42
|
+
"#{path}/#{file}.#{language}.xliff"
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
end
|
data/settings.SAMPLE.yml
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
# https://docs.google.com/document/d/2fKTnCh1uSzBjXX5ZFBCLPx7JCMlMY8V7nbzSVbVyw56/edit
|
|
2
|
+
# GDOC key from URL is 2fKTnCh1uSzBjXX5ZFBCLPx7JCMlMY8V7nbzSVbVyw56
|
|
3
|
+
GDOC:
|
|
4
|
+
key: "2fKTnCh1uSzBjXX5ZFBCLPx7JCMlMY8V7nbzSVbVyw56"
|
|
5
|
+
email: "user@example.com"
|
|
6
|
+
password: "secret"
|
|
7
|
+
|
|
8
|
+
FILES: # xliff files <=> gdoc tabs
|
|
9
|
+
- "cockpit"
|
|
10
|
+
- "common"
|
|
11
|
+
- "desktop"
|
|
12
|
+
- "error-msg"
|
|
13
|
+
- "FOSUserBundle"
|
|
14
|
+
- "home"
|
|
15
|
+
- "learn"
|
|
16
|
+
- "validators"
|
|
17
|
+
- "test"
|
|
18
|
+
|
|
19
|
+
LANGUAGES:
|
|
20
|
+
- "en"
|
|
21
|
+
- "de"
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
|
2
|
+
<xliff version="1.2" xmlns="urn:oasis:names:tc:xliff:document:1.2">
|
|
3
|
+
<file source-language="en" datatype="plaintext" original="file.ext">
|
|
4
|
+
<body>
|
|
5
|
+
<trans-unit id="title">
|
|
6
|
+
<source>title</source>
|
|
7
|
+
<target>TEST</target>
|
|
8
|
+
</trans-unit>
|
|
9
|
+
<trans-unit id="round">
|
|
10
|
+
<source>round</source>
|
|
11
|
+
<target>Attempt</target>
|
|
12
|
+
</trans-unit>
|
|
13
|
+
<trans-unit id="end_test">
|
|
14
|
+
<source>end_test</source>
|
|
15
|
+
<target>END TEST</target>
|
|
16
|
+
</trans-unit>
|
|
17
|
+
<trans-unit id="statistics.summary">
|
|
18
|
+
<source>statistics.summary</source>
|
|
19
|
+
<target>Summary of this session</target>
|
|
20
|
+
</trans-unit>
|
|
21
|
+
<trans-unit id="statistics.time.spent">
|
|
22
|
+
<source>statistics.time.spent</source>
|
|
23
|
+
<target>Time Spent</target>
|
|
24
|
+
</trans-unit>
|
|
25
|
+
<trans-unit id="statistics.time.left">
|
|
26
|
+
<source>statistics.time.left</source>
|
|
27
|
+
<target>Time Left</target>
|
|
28
|
+
</trans-unit>
|
|
29
|
+
<trans-unit id="time.up">
|
|
30
|
+
<source>time.up</source>
|
|
31
|
+
<target>Your time is up</target>
|
|
32
|
+
</trans-unit>
|
|
33
|
+
<trans-unit id="last_card_title">
|
|
34
|
+
<source>last_card_title</source>
|
|
35
|
+
<target>Click END TEST button to save your results.</target>
|
|
36
|
+
</trans-unit>
|
|
37
|
+
<trans-unit id="repeat">
|
|
38
|
+
<source>repeat</source>
|
|
39
|
+
<target>Repeat</target>
|
|
40
|
+
</trans-unit>
|
|
41
|
+
</body>
|
|
42
|
+
</file>
|
|
43
|
+
</xliff>
|
data/transync.gemspec
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
# coding: utf-8
|
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
|
4
|
+
require 'transync/version'
|
|
5
|
+
|
|
6
|
+
Gem::Specification.new do |spec|
|
|
7
|
+
spec.name = 'transync'
|
|
8
|
+
spec.version = Transync::VERSION
|
|
9
|
+
spec.authors = ['zigomir']
|
|
10
|
+
spec.email = ['zigomir@gmail.com']
|
|
11
|
+
spec.description = %q{Synchronize XLIFF translations with Google Drive document}
|
|
12
|
+
spec.summary = %q{Synchronize XLIFF translations with Google Drive document}
|
|
13
|
+
spec.homepage = ''
|
|
14
|
+
spec.license = 'MIT'
|
|
15
|
+
|
|
16
|
+
spec.files = `git ls-files`.split($/)
|
|
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_runtime_dependency 'google_drive'
|
|
22
|
+
spec.add_runtime_dependency 'builder'
|
|
23
|
+
|
|
24
|
+
spec.add_development_dependency 'bundler', '~> 1.3'
|
|
25
|
+
spec.add_development_dependency 'rake'
|
|
26
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,124 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: transync
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 0.0.1
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- zigomir
|
|
8
|
+
autorequire:
|
|
9
|
+
bindir: bin
|
|
10
|
+
cert_chain: []
|
|
11
|
+
date: 2013-05-26 00:00:00.000000000 Z
|
|
12
|
+
dependencies:
|
|
13
|
+
- !ruby/object:Gem::Dependency
|
|
14
|
+
name: google_drive
|
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
|
16
|
+
requirements:
|
|
17
|
+
- - '>='
|
|
18
|
+
- !ruby/object:Gem::Version
|
|
19
|
+
version: '0'
|
|
20
|
+
type: :runtime
|
|
21
|
+
prerelease: false
|
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
23
|
+
requirements:
|
|
24
|
+
- - '>='
|
|
25
|
+
- !ruby/object:Gem::Version
|
|
26
|
+
version: '0'
|
|
27
|
+
- !ruby/object:Gem::Dependency
|
|
28
|
+
name: builder
|
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
|
30
|
+
requirements:
|
|
31
|
+
- - '>='
|
|
32
|
+
- !ruby/object:Gem::Version
|
|
33
|
+
version: '0'
|
|
34
|
+
type: :runtime
|
|
35
|
+
prerelease: false
|
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
37
|
+
requirements:
|
|
38
|
+
- - '>='
|
|
39
|
+
- !ruby/object:Gem::Version
|
|
40
|
+
version: '0'
|
|
41
|
+
- !ruby/object:Gem::Dependency
|
|
42
|
+
name: bundler
|
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
|
44
|
+
requirements:
|
|
45
|
+
- - ~>
|
|
46
|
+
- !ruby/object:Gem::Version
|
|
47
|
+
version: '1.3'
|
|
48
|
+
type: :development
|
|
49
|
+
prerelease: false
|
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
51
|
+
requirements:
|
|
52
|
+
- - ~>
|
|
53
|
+
- !ruby/object:Gem::Version
|
|
54
|
+
version: '1.3'
|
|
55
|
+
- !ruby/object:Gem::Dependency
|
|
56
|
+
name: rake
|
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
|
58
|
+
requirements:
|
|
59
|
+
- - '>='
|
|
60
|
+
- !ruby/object:Gem::Version
|
|
61
|
+
version: '0'
|
|
62
|
+
type: :development
|
|
63
|
+
prerelease: false
|
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
|
65
|
+
requirements:
|
|
66
|
+
- - '>='
|
|
67
|
+
- !ruby/object:Gem::Version
|
|
68
|
+
version: '0'
|
|
69
|
+
description: Synchronize XLIFF translations with Google Drive document
|
|
70
|
+
email:
|
|
71
|
+
- zigomir@gmail.com
|
|
72
|
+
executables:
|
|
73
|
+
- transync
|
|
74
|
+
extensions: []
|
|
75
|
+
extra_rdoc_files: []
|
|
76
|
+
files:
|
|
77
|
+
- .gitignore
|
|
78
|
+
- .ruby-version
|
|
79
|
+
- Gemfile
|
|
80
|
+
- LICENSE.txt
|
|
81
|
+
- README.md
|
|
82
|
+
- Rakefile
|
|
83
|
+
- bin/transync
|
|
84
|
+
- lib/transync.rb
|
|
85
|
+
- lib/transync/gdoc_trans/gdoc_trans.rb
|
|
86
|
+
- lib/transync/gdoc_trans/gdoc_trans_reader.rb
|
|
87
|
+
- lib/transync/gdoc_trans/gdoc_trans_writer.rb
|
|
88
|
+
- lib/transync/sync/gdoc_2_xliff_main.rb
|
|
89
|
+
- lib/transync/sync/gdoc_to_xliff.rb
|
|
90
|
+
- lib/transync/sync/sync_util.rb
|
|
91
|
+
- lib/transync/sync/xliff_2_gdoc_main.rb
|
|
92
|
+
- lib/transync/sync/xliff_to_gdoc.rb
|
|
93
|
+
- lib/transync/version.rb
|
|
94
|
+
- lib/transync/xliff_trans/xliff_trans_reader.rb
|
|
95
|
+
- lib/transync/xliff_trans/xliff_trans_writer.rb
|
|
96
|
+
- settings.SAMPLE.yml
|
|
97
|
+
- test/fixtures/test.en.xliff
|
|
98
|
+
- transync.gemspec
|
|
99
|
+
homepage: ''
|
|
100
|
+
licenses:
|
|
101
|
+
- MIT
|
|
102
|
+
metadata: {}
|
|
103
|
+
post_install_message:
|
|
104
|
+
rdoc_options: []
|
|
105
|
+
require_paths:
|
|
106
|
+
- lib
|
|
107
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
108
|
+
requirements:
|
|
109
|
+
- - '>='
|
|
110
|
+
- !ruby/object:Gem::Version
|
|
111
|
+
version: '0'
|
|
112
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
113
|
+
requirements:
|
|
114
|
+
- - '>='
|
|
115
|
+
- !ruby/object:Gem::Version
|
|
116
|
+
version: '0'
|
|
117
|
+
requirements: []
|
|
118
|
+
rubyforge_project:
|
|
119
|
+
rubygems_version: 2.0.2
|
|
120
|
+
signing_key:
|
|
121
|
+
specification_version: 4
|
|
122
|
+
summary: Synchronize XLIFF translations with Google Drive document
|
|
123
|
+
test_files:
|
|
124
|
+
- test/fixtures/test.en.xliff
|