2014-04-14 11:41:36 -04:00
# This script pulls translation files from Transifex and ensures they are in the format we need.
# You need the Transifex client installed.
# http://docs.transifex.com/developer/client/setup
#
# Don't use this script to create pull requests. Do translations in Transifex. The Discourse
# team will pull them in.
require 'open3'
2015-09-18 22:30:28 +02:00
require_relative '../lib/locale_file_walker'
2014-04-14 11:41:36 -04:00
if ` which tx ` . strip . empty?
2015-04-30 22:27:05 +02:00
puts '' , 'The Transifex client needs to be installed to use this script.'
2016-04-02 15:20:23 +02:00
puts 'Instructions are here: http://docs.transifex.com/client/setup/'
2015-04-30 22:27:05 +02:00
puts '' , 'On Mac:' , ''
2016-04-02 15:20:23 +02:00
puts ' sudo easy_install pip'
2015-04-30 22:27:05 +02:00
puts ' sudo pip install transifex-client' , ''
2014-04-14 11:41:36 -04:00
exit 1
end
2016-08-12 22:12:03 +02:00
if ARGV . include? ( 'force' )
STDERR . puts 'Usage: ruby pull_translations.rb [languages]'
STDERR . puts 'Example: ruby pull_translations.rb de it' , ''
exit 1
end
def get_languages
if ARGV . empty?
Dir . glob ( File . expand_path ( '../../config/locales/client.*.yml' , __FILE__ ) )
. map { | x | x . split ( '.' ) [ - 2 ] }
else
ARGV
end
end
languages = get_languages . select { | x | x != 'en' } . sort
2014-04-16 10:35:27 -04:00
2015-04-30 22:27:05 +02:00
puts 'Pulling new translations...' , ''
2016-08-12 22:12:03 +02:00
command = " tx pull --mode=developer --language= #{ languages . join ( ',' ) } --force "
2014-04-16 10:35:27 -04:00
Open3 . popen2e ( command ) do | stdin , stdout_err , wait_thr |
2015-04-30 22:27:05 +02:00
while ( line = stdout_err . gets )
2014-04-14 11:41:36 -04:00
puts line
end
end
2015-04-30 22:27:05 +02:00
puts ''
2014-04-14 11:41:36 -04:00
2014-04-16 10:35:27 -04:00
unless $? . success?
2015-04-30 22:27:05 +02:00
puts 'Something failed. Check the output above.' , ''
2014-04-14 11:41:36 -04:00
exit $? . exitstatus
end
2014-04-24 10:11:23 -04:00
YML_FILE_COMMENTS = <<END
2014-04-14 11:41:36 -04:00
# encoding: utf-8
#
# Never edit this file. It will be overwritten when translations are pulled from Transifex.
#
# To work with us on translations, join this project:
2014-05-20 22:24:19 +05:30
# https://www.transifex.com/projects/p/discourse-org/
2014-04-14 11:41:36 -04:00
END
2014-04-24 10:11:23 -04:00
YML_DIRS = [ 'config/locales' ,
'plugins/poll/config/locales' ,
'vendor/gems/discourse_imgur/lib/discourse_imgur/locale' ]
2015-09-18 22:30:28 +02:00
YML_FILE_PREFIXES = [ 'server' , 'client' ]
2014-04-24 10:11:23 -04:00
2015-09-18 22:30:28 +02:00
def yml_path ( dir , prefix , language )
path = " ../../ #{ dir } / #{ prefix } . #{ language } .yml "
path = File . expand_path ( path , __FILE__ )
File . exists? ( path ) ? path : nil
end
# Add comments to the top of files and replace the language (first key in YAML file)
def update_file_header ( filename , language )
lines = File . readlines ( filename )
2016-07-27 00:15:44 +02:00
lines . collect! { | line | line =~ / ^[a-z_]+:$ /i ? " #{ language } : " : line }
2015-09-18 22:30:28 +02:00
File . open ( filename , 'w+' ) do | f |
f . puts ( YML_FILE_COMMENTS , '' ) unless lines [ 0 ] [ 0 ] == '#'
f . puts ( lines )
end
end
class YamlAliasFinder < LocaleFileWalker
def initialize
@anchors = { }
@aliases = Hash . new { | hash , key | hash [ key ] = [ ] }
end
def parse_file ( filename )
document = Psych . parse_file ( filename )
handle_document ( document )
{ anchors : @anchors , aliases : @aliases }
end
private
def handle_alias ( node , depth , parents )
@aliases [ node . anchor ] << parents . dup
end
def handle_mapping ( node , depth , parents )
if node . anchor
@anchors [ parents . dup ] = node . anchor
end
end
end
class YamlAliasSynchronizer < LocaleFileWalker
def initialize ( original_alias_data )
@anchors = original_alias_data [ :anchors ]
@aliases = original_alias_data [ :aliases ]
@used_anchors = Set . new
calculate_required_keys
end
def add_to ( filename )
stream = Psych . parse_stream ( File . read ( filename ) )
stream . children . each { | document | handle_document ( document ) }
add_aliases
write_yaml ( stream , filename )
end
private
def calculate_required_keys
@required_keys = { }
@aliases . each_value do | key_sets |
key_sets . each do | keys |
until keys . empty?
add_needed_node ( keys )
keys = keys . dup
keys . pop
end
end
end
add_needed_node ( [ ] ) unless @required_keys . empty?
end
def add_needed_node ( keys )
@required_keys [ keys ] = { mapping : nil , scalar : nil , alias : nil }
end
def write_yaml ( stream , filename )
yaml = stream . to_yaml ( nil , { :line_width = > - 1 } )
File . open ( filename , 'w' ) do | file |
file . write ( yaml )
end
end
def handle_scalar ( node , depth , parents )
super ( node , depth , parents )
if @required_keys . has_key? ( parents )
@required_keys [ parents ] [ :scalar ] = node
end
end
def handle_alias ( node , depth , parents )
if @required_keys . has_key? ( parents )
@required_keys [ parents ] [ :alias ] = node
end
end
def handle_mapping ( node , depth , parents )
if @anchors . has_key? ( parents )
node . anchor = @anchors [ parents ]
@used_anchors . add ( node . anchor )
end
if @required_keys . has_key? ( parents )
@required_keys [ parents ] [ :mapping ] = node
end
end
def add_aliases
@used_anchors . each do | anchor |
@aliases [ anchor ] . each do | keys |
parents = [ ]
parent_node = @required_keys [ [ ] ]
keys . each_with_index do | key , index |
parents << key
current_node = @required_keys [ parents ]
is_last = index == keys . size - 1
add_node ( current_node , parent_node , key , is_last ? anchor : nil )
parent_node = current_node
end
end
end
end
def add_node ( node , parent_node , scalar_name , anchor )
parent_mapping = parent_node [ :mapping ]
parent_mapping . children || = [ ]
if node [ :scalar ] . nil?
node [ :scalar ] = Psych :: Nodes :: Scalar . new ( scalar_name )
parent_mapping . children << node [ :scalar ]
end
if anchor . nil?
if node [ :mapping ] . nil?
node [ :mapping ] = Psych :: Nodes :: Mapping . new
parent_mapping . children << node [ :mapping ]
end
elsif node [ :alias ] . nil?
parent_mapping . children << Psych :: Nodes :: Alias . new ( anchor )
end
end
end
def get_english_alias_data ( dir , prefix )
filename = yml_path ( dir , prefix , 'en' )
filename ? YamlAliasFinder . new . parse_file ( filename ) : nil
end
def add_anchors_and_aliases ( english_alias_data , filename )
if english_alias_data
YamlAliasSynchronizer . new ( english_alias_data ) . add_to ( filename )
end
end
2016-07-27 00:15:44 +02:00
def fix_invalid_yml_keys ( filename )
lines = File . readlines ( filename )
# fix YAML keys which are on the wrong line
lines . each { | line | line . gsub! ( / ^(.*(?:'|"))( \ s* \ S*:)$ / , " \\ 1 \n \\ 2 " ) }
File . open ( filename , 'w+' ) do | f |
f . puts ( lines )
end
end
def fix_invalid_yml ( filename )
lines = File . readlines ( filename )
lines . each_with_index do | line , i |
if line =~ / ^ \ s+ # .*$ /i
# remove comments
lines [ i ] = nil
elsif line . strip . empty?
# remove empty lines
lines [ i ] = nil
elsif line =~ / ^ \ s+.*: (?:""|''|)$ /i
# remove lines which contain empty string values
lines [ i ] = nil
elsif line =~ / ^( \ s)*.*: \ |$ /i
next_line = i + 1 < lines . size ? lines [ i + 1 ] : nil
if next_line . nil? || line [ / ^ \ s* / ] . size + 2 != next_line [ / ^ \ s* / ] . size
# remove lines which contain the value | without a string in the next line
lines [ i ] = nil
end
end
end . compact!
loop do
lines . each_with_index do | line , i |
if line =~ / ^ \ s+ \ w*: \ s*$ /i
next_line = i + 1 < lines . size ? lines [ i + 1 ] : nil
if next_line . nil? || line [ / ^ \ s* / ] . size > = next_line [ / ^ \ s* / ] . size
# remove lines which have an empty value and are not followed
# by a key on the next level
lines [ i ] = nil
end
end
end
break if lines . compact! . nil?
end
File . open ( filename , 'w+' ) do | f |
f . puts ( lines )
end
end
2015-09-18 22:30:28 +02:00
YML_DIRS . each do | dir |
YML_FILE_PREFIXES . each do | prefix |
english_alias_data = get_english_alias_data ( dir , prefix )
2015-05-06 17:03:07 +02:00
2015-09-18 22:30:28 +02:00
languages . each do | language |
filename = yml_path ( dir , prefix , language )
2015-05-06 17:03:07 +02:00
2015-09-18 22:30:28 +02:00
if filename
2016-07-27 17:25:01 -04:00
# The following methods were added to handle a bug in Transifex's yml. Should not be needed now.
# fix_invalid_yml_keys(filename)
# fix_invalid_yml(filename)
2016-07-27 00:15:44 +02:00
# TODO check if this is still needed with recent Transifex changes
2015-09-18 22:30:28 +02:00
add_anchors_and_aliases ( english_alias_data , filename )
2016-07-27 00:15:44 +02:00
2015-09-18 22:30:28 +02:00
update_file_header ( filename , language )
2014-04-24 10:11:23 -04:00
end
2014-04-14 11:41:36 -04:00
end
end
end