Skip to content

Instantly share code, notes, and snippets.

@cdlm
Last active January 3, 2016 19:59
Show Gist options
  • Save cdlm/8512372 to your computer and use it in GitHub Desktop.
Save cdlm/8512372 to your computer and use it in GitHub Desktop.
Script to help translators of http://thecodelesscode.com
#!/usr/bin/env ruby
require 'cgi'
input = ARGV.empty? ? $stdin : StringIO(ARGV.join(' '))
input.each_line do |line|
puts CGI::escape(line.chomp)
end
#!/usr/bin/env ruby
#
# Checks staged contents of changed files for normal spaces left where french
# typography rules would require a non-breaking space:
# - inside guillemots « and »
# - before double punctuation signs :;!?
exit if ENV['CODELESS_TRANSLATION'].nil?
begin
require 'paint'
Paint::SHORTCUTS[:highlight] = {
file: Paint.color(:blue),
warn: Paint.color(nil, :red)
}
include Paint::Highlight
rescue LoadError
def no_paint(s) s end
alias file no_paint
alias warn no_paint
end
FILE_GLOB = "the-codeless-code/#{ENV['CODELESS_TRANSLATION']}/case-*.txt"
HEADER_PATTERN = /^([[:alnum:]]|\.)+: /
NON_NBSP = /[^\u00A0]/ # anything but a  
ENTITY = /(?<entity>&[^\s;]+;)/
BAD_AFTER = /(?<=«)#{NON_NBSP}/
BAD_BEFORE = /#{NON_NBSP}(?=[:;?!»])/
PATTERN = Regexp.union ENTITY, BAD_BEFORE, BAD_AFTER
def files_to_check
IO.popen(%w(git diff --cached --name-only --diff-filter=ACM)) do |io|
io.each_line.map(&:chomp).select { |each| File.fnmatch? FILE_GLOB, each }
end
end
def staged_contents filename, &block
IO.popen(['git', 'show', ":#{filename}"]) do |io|
io.each_line.with_index &block
end
end
problems_in_total = 0
files_to_check.each do |name|
in_header = true
first_in_file = true
staged_contents(name) do |line,num|
line.chomp!
next if in_header && HEADER_PATTERN === line
in_header = false
problems_on_line = 0
line = line.gsub PATTERN do
match = Regexp.last_match
if match[:entity].nil?
problems_on_line += 1
warn(match)
else
match
end
end
unless problems_on_line.zero?
puts file(name) if first_in_file
puts " #{num + 1}: #{line}"
problems_in_total += problems_on_line
first_in_file = false
end
end
end
if problems_in_total > 0
puts "\n#{problems_in_total} occurrences of bad or missing spaces near punctuation"
exit 42
end
#!/usr/bin/env ruby
require 'cri' # gem install cri
require 'fileutils'
def translation_dir translation
d = File.join(ROOT, translation)
raise "No such translation #{translation}" unless File.directory? d
d
end
def case_file case_num, translation
File.join(translation_dir(translation), "case-#{case_num}.txt")
end
def available? case_num, translation
File.exist?(case_file(case_num, translation))
end
def case_numbers translation
cases = Dir.glob(File.join(translation_dir(translation), 'case-[0-9]*.txt'))
cases.map { |each| /(\d+)\.txt/.match(each)[1].to_i }.sort
end
def untranslated_cases src, dest
case_numbers(src) - case_numbers(dest)
end
def root_command
Cri::Command.define do
name NAME
usage "#{NAME} [case] [src=#{DEFAULT_SOURCE}] [dest=#{DEFAULT_DESTINATION}]"
summary "copies a case before starting its translation"
description <<EOS
Copies selected case from an authoritative translation src to an in-progress translation dest.
Case can be specified either by explicit number, or selected among available cases by a keyword.
Available cases are those that have a translation in src but not in dest.
Recognized selection keywords are:
current (latest translated case),
next (first available case),
last (latest available case),
random.
Environment variables: you must set CODELESS_TRANSLATION to the name of the translation directory to copy cases to. If you work from another translation, setting CODELESS_SOURCE overrides the default (en-qi).
EOS
flag :h, :help, "show command usage" do |_, cmd|
puts cmd.help
exit
end
flag nil, :dryrun, "only say what would be done"
flag nil, :overwrite, "copy even if destination case exists"
flag :e, :edit, "open case in $EDITOR"
run do |opts, args, cmd|
selection = args[0] || DEFAULT_SELECTION
src = args[1] || DEFAULT_SOURCE
dest = args[2] || DEFAULT_DESTINATION
case_num = case selection
when /^\d+$/ then args[0].to_i
when /^c(ur(rent)?)?$/ then case_numbers(dest).last
when /^n(ext)?$/ then untranslated_cases(src, dest).first
when /^l(a(te)?st)?$/ then untranslated_cases(src, dest).last
when /^r(and(om)?)?$/ then untranslated_cases(src, dest).sample
else raise "Invalid case selector #{selection}"
end
raise "No available cases to translate from #{src} to #{dest}" if case_num.nil?
raise "No case ##{case_num} in translation #{src}" unless available?(case_num, src)
raise "Case ##{case_num} already exists in translation #{dest}" if available?(case_num, dest) && !(opts[:overwrite] || opts[:edit])
unless available?(case_num, dest) && opts[:edit]
puts "Copying case ##{case_num} (#{src} -> #{dest})"
FileUtils.cp case_file(case_num, src), case_file(case_num, dest) unless opts[:dryrun]
end
if opts[:edit]
exec ENV['EDITOR'], case_file(case_num, dest) unless opts[:dryrun]
end
end
end
end
# ...and here we go
begin
NAME = File.basename(__FILE__)
ROOT = File.join(File.expand_path(File.dirname(__FILE__)), 'the-codeless-code')
DEFAULT_SELECTION = ENV['CODELESS_SELECTION'] || 'next'
DEFAULT_SOURCE = ENV['CODELESS_SOURCE'] || 'en-qi'
DEFAULT_DESTINATION = ENV['CODELESS_TRANSLATION'] || raise(StandardError, "No destination translation given, please set $CODELESS_TRANSLATION")
root_command.run(ARGV)
rescue StandardError => e
puts e.message
exit 1
end
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment