Pastie now auto-senses if line-wrap is a bad or good idea. Feedback?
## mark a section (Learn more)
This paste will be private.
#!/usr/bin/ruby # # get-ruby-strings: interactively process ruby source file for gettext # # get-ruby-strings.rb <input-file> # # This program process a ruby source file, and output a modified file to # <input-file>.modified # The following modifications are done to string literals enclosed with '' or ""; the # program will prompt for each string asking whether they will be modified. If you # answer "gettextize" # # * The string is wrapped with _( ) # * If the string is enclosed with "", and it contains embedded expressions with the # #{ } notation, such as "The file is #{filename}", the string will be rewritten in # string modulo syntax such as _("The file is %{filename}") % {:filename => filename} # Note that this form of String#% taking a hash argument is available with ruby-gettext. # require 'highline' require 'syntax' # scan a ruby source file with Syntax/Ruby. each sequence of tokens that represent a # string literal is passed to the block, and the value of the block is written to output # all other tokens are written as-is to output def replace_strings(source, output, &block) tokenizer = Syntax.load 'ruby' current_string = [] # a ruby string literal: :open-quote (:string | :expr) * :close-quote tokenizer.tokenize(source) do |token| if token.group == :punct && %w[' "].include?(token) # FIXME a double or single quote token is always considered string opener or closer # is this correct? current_string << token if current_string.length > 1 && current_string.first == token # closing a string literal current_string << token # pass collected string literal to block, and output the return value output << yield(current_string) current_string = [] end elsif !current_string.empty? && ( token.group == :string || token.group == :expression && token =~ /\A#\{.+\}\Z/ ) # continue string body current_string << token else output << token end end end filename = ARGV[0] output = File.new("#{filename}.modified", 'w') hl = HighLine.new replace_strings(File.read(filename), output) do |tokens| hl.say "String literal: #{tokens.join}" expressions = tokens.select {|t| t.group == :expr} hl.choose do |menu| menu.layout = :one_line menu.default = 'skip' menu.choice('skip') {tokens.join} menu.choice('gettextize') do replacement = '_(' expressions = Hash.new tokens.each do |t| if t.group == :expr expression = /\A#\{(.+)\}\Z/.match(t)[1] label = hl.ask "Label for expression #{expression}? " do |q| # if the interpolated code is one word q.default = expression if expression =~ /\A\w+\Z/ q.validate = /\A\w+\Z/ end expressions[label] = expression replacement << "%{#{label}}" else replacement << t end end replacement << ')' replacement << " % {#{ expressions.map {|l, e| ":#{l} => #{e}"}.join ', ' }}" unless expressions.empty? replacement end end end
From the Design Piracy series on my blog: