mirror of
https://github.com/TracksApp/tracks.git
synced 2025-12-18 08:10:13 +01:00
forgot to add these
This commit is contained in:
parent
86afd42148
commit
fde64e0b3d
145 changed files with 9044 additions and 0 deletions
20
backup.rails2.3/plugins/translate/MIT-LICENSE
Normal file
20
backup.rails2.3/plugins/translate/MIT-LICENSE
Normal file
|
|
@ -0,0 +1,20 @@
|
|||
Copyright (c) 2009 [name of plugin creator]
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
63
backup.rails2.3/plugins/translate/README
Normal file
63
backup.rails2.3/plugins/translate/README
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
Translate
|
||||
=========
|
||||
|
||||
This plugin provides a web interface for translating Rails I18n texts (requires Rails 2.2 or higher) from one locale to another. The plugin has been tested only with the simple I18n backend that ships with Rails. I18n texts are read from and written to YAML files under config/locales.
|
||||
|
||||
To translate to a new locale you need to add a YAML file for that locale that contains the locale as the top key and at least one translation.
|
||||
|
||||
Please note that there are certain I18n keys that map to Array objects rather than strings and those are currently not dealt with by the translation UI. This means that Rails built in keys such as date.day_names need to be translated manually directly in the YAML file.
|
||||
|
||||
To get the translation UI to write the YAML files in UTF8 you need to install the ya2yaml gem.
|
||||
|
||||
The translation UI finds all I18n keys by extracting them from I18n lookups in your application source code. In addition it adds all :en and default locale keys from the I18n backend.
|
||||
|
||||
- Updated: Each string in the UI now has an "Auto Translate" link which will send the original text to Google Translate and will input the returned translation into the form field for further clean up and review prior to saving.
|
||||
|
||||
|
||||
Rake Tasks
|
||||
=========
|
||||
|
||||
In addition to the web UI this plugin adds the following rake tasks:
|
||||
|
||||
translate:untranslated
|
||||
translate:missing
|
||||
translate:remove_obsolete_keys
|
||||
translate:merge_keys
|
||||
translate:google
|
||||
translate:changed
|
||||
|
||||
The missing task shows you any I18n keys in your code that do not have translations in the YAML file for your default locale, i.e. config/locales/sv.yml.
|
||||
|
||||
The merge_keys task is supposed to be used in conjunction with Sven Fuch's Rails I18n TextMate bundle (http://github.com/svenfuchs/rails-i18n/tree/master). Texts and keys extracted with the TextMate bundle end up in the temporary file log/translations.yml. When you run the merge_keys rake task the keys are moved over to the corresponding I18n locale file, i.e. config/locales/sv.yml. The merge_keys task also checks for overwrites of existing keys by warning you that one of your extracted keys already exists with a different translation.
|
||||
|
||||
The google task is used for auto translating from one locale to another using Google Translate.
|
||||
|
||||
The changed rake task can show you between one YAML file to another which keys have had their texts changed.
|
||||
|
||||
Installation
|
||||
=========
|
||||
Obtain the source with:
|
||||
|
||||
./script/plugin install git://github.com/newsdesk/translate.git
|
||||
|
||||
To mount the plugin, add the following to your config/routes.rb file:
|
||||
|
||||
Translate::Routes.translation_ui(map) if RAILS_ENV != "production"
|
||||
|
||||
Now visit /translate in your web browser to start translating.
|
||||
|
||||
Dependencies
|
||||
=========
|
||||
|
||||
- Rails 2.2 or higher
|
||||
- The ya2yaml gem if you want your YAML files written in UTF8 encoding.
|
||||
|
||||
Authors
|
||||
=========
|
||||
|
||||
- Peter Marklund (programming)
|
||||
- Joakim Westerlund (web design)
|
||||
|
||||
Many thanks to http://newsdesk.se for sponsoring the development of this plugin.
|
||||
|
||||
Copyright (c) 2009 Peter Marklund, released under the MIT license
|
||||
11
backup.rails2.3/plugins/translate/Rakefile
Normal file
11
backup.rails2.3/plugins/translate/Rakefile
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
require 'rake'
|
||||
require 'spec/rake/spectask'
|
||||
|
||||
desc 'Default: run specs.'
|
||||
task :default => :spec
|
||||
|
||||
desc 'Run the specs'
|
||||
Spec::Rake::SpecTask.new(:spec) do |t|
|
||||
t.spec_opts = ['--colour --format progress --loadby mtime --reverse']
|
||||
t.spec_files = FileList['spec/**/*_spec.rb']
|
||||
end
|
||||
8
backup.rails2.3/plugins/translate/init.rb
Normal file
8
backup.rails2.3/plugins/translate/init.rb
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
require 'translate'
|
||||
|
||||
# TODO: Use new method available_locales once Rails is upgraded, see:
|
||||
# http://github.com/svenfuchs/i18n/commit/411f8fe7c8f3f89e9b6b921fa62ed66cb92f3af4
|
||||
def I18n.valid_locales
|
||||
I18n.backend.send(:init_translations) unless I18n.backend.initialized?
|
||||
backend.send(:translations).keys.reject { |locale| locale == :root }
|
||||
end
|
||||
8
backup.rails2.3/plugins/translate/lib/translate.rb
Normal file
8
backup.rails2.3/plugins/translate/lib/translate.rb
Normal file
|
|
@ -0,0 +1,8 @@
|
|||
module Translate
|
||||
end
|
||||
|
||||
require File.join(File.dirname(__FILE__), "translate_controller")
|
||||
require File.join(File.dirname(__FILE__), "translate_helper")
|
||||
Dir[File.join(File.dirname(__FILE__), "translate", "*.rb")].each do |file|
|
||||
require file
|
||||
end
|
||||
35
backup.rails2.3/plugins/translate/lib/translate/file.rb
Normal file
35
backup.rails2.3/plugins/translate/lib/translate/file.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
require 'fileutils'
|
||||
|
||||
class Translate::File
|
||||
attr_accessor :path
|
||||
|
||||
def initialize(path)
|
||||
self.path = path
|
||||
end
|
||||
|
||||
def write(keys)
|
||||
FileUtils.mkdir_p File.dirname(path)
|
||||
File.open(path, "w") do |file|
|
||||
file.puts keys_to_yaml(Translate::File.deep_stringify_keys(keys))
|
||||
end
|
||||
end
|
||||
|
||||
def read
|
||||
File.exists?(path) ? YAML::load(IO.read(path)) : {}
|
||||
end
|
||||
|
||||
# Stringifying keys for prettier YAML
|
||||
def self.deep_stringify_keys(hash)
|
||||
hash.inject({}) { |result, (key, value)|
|
||||
value = deep_stringify_keys(value) if value.is_a? Hash
|
||||
result[(key.to_s rescue key) || key] = value
|
||||
result
|
||||
}
|
||||
end
|
||||
|
||||
private
|
||||
def keys_to_yaml(keys)
|
||||
# Using ya2yaml, if available, for UTF8 support
|
||||
keys.respond_to?(:ya2yaml) ? keys.ya2yaml(:escape_as_utf8 => true) : keys.to_yaml
|
||||
end
|
||||
end
|
||||
152
backup.rails2.3/plugins/translate/lib/translate/keys.rb
Normal file
152
backup.rails2.3/plugins/translate/lib/translate/keys.rb
Normal file
|
|
@ -0,0 +1,152 @@
|
|||
require 'pathname'
|
||||
|
||||
class Translate::Keys
|
||||
# Allows keys extracted from lookups in files to be cached
|
||||
def self.files
|
||||
@@files ||= Translate::Keys.new.files
|
||||
end
|
||||
|
||||
# Allows flushing of the files cache
|
||||
def self.files=(files)
|
||||
@@files = files
|
||||
end
|
||||
|
||||
def files
|
||||
@files ||= extract_files
|
||||
end
|
||||
alias_method :to_hash, :files
|
||||
|
||||
def keys
|
||||
files.keys
|
||||
end
|
||||
alias_method :to_a, :keys
|
||||
|
||||
def i18n_keys(locale)
|
||||
I18n.backend.send(:init_translations) unless I18n.backend.initialized?
|
||||
Translate::Keys.to_shallow_hash(I18n.backend.send(:translations)[locale.to_sym]).keys.sort
|
||||
end
|
||||
|
||||
def untranslated_keys
|
||||
Translate::Keys.translated_locales.inject({}) do |missing, locale|
|
||||
missing[locale] = i18n_keys(I18n.default_locale).map do |key|
|
||||
I18n.backend.send(:lookup, locale, key).nil? ? key : nil
|
||||
end.compact
|
||||
missing
|
||||
end
|
||||
end
|
||||
|
||||
def missing_keys
|
||||
locale = I18n.default_locale; yaml_keys = {}
|
||||
yaml_keys = Translate::Storage.file_paths(locale).inject({}) do |keys, path|
|
||||
keys = keys.deep_merge(Translate::File.new(path).read[locale.to_s])
|
||||
end
|
||||
files.reject { |key, file| self.class.contains_key?(yaml_keys, key) }
|
||||
end
|
||||
|
||||
def self.translated_locales
|
||||
I18n.available_locales.reject { |locale| [:root, I18n.default_locale.to_sym].include?(locale) }
|
||||
end
|
||||
|
||||
# Checks if a nested hash contains the keys in dot separated I18n key.
|
||||
#
|
||||
# Example:
|
||||
#
|
||||
# hash = {
|
||||
# :foo => {
|
||||
# :bar => {
|
||||
# :baz => 1
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# contains_key?("foo", key) # => true
|
||||
# contains_key?("foo.bar", key) # => true
|
||||
# contains_key?("foo.bar.baz", key) # => true
|
||||
# contains_key?("foo.bar.baz.bla", key) # => false
|
||||
#
|
||||
def self.contains_key?(hash, key)
|
||||
keys = key.to_s.split(".")
|
||||
return false if keys.empty?
|
||||
!keys.inject(HashWithIndifferentAccess.new(hash)) do |memo, key|
|
||||
memo.is_a?(Hash) ? memo.try(:[], key) : nil
|
||||
end.nil?
|
||||
end
|
||||
|
||||
# Convert something like:
|
||||
#
|
||||
# {
|
||||
# :pressrelease => {
|
||||
# :label => {
|
||||
# :one => "Pressmeddelande"
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
#
|
||||
# to:
|
||||
#
|
||||
# {'pressrelease.label.one' => "Pressmeddelande"}
|
||||
#
|
||||
def self.to_shallow_hash(hash)
|
||||
hash.inject({}) do |shallow_hash, (key, value)|
|
||||
if value.is_a?(Hash)
|
||||
to_shallow_hash(value).each do |sub_key, sub_value|
|
||||
shallow_hash[[key, sub_key].join(".")] = sub_value
|
||||
end
|
||||
else
|
||||
shallow_hash[key.to_s] = value
|
||||
end
|
||||
shallow_hash
|
||||
end
|
||||
end
|
||||
|
||||
# Convert something like:
|
||||
#
|
||||
# {'pressrelease.label.one' => "Pressmeddelande"}
|
||||
#
|
||||
# to:
|
||||
#
|
||||
# {
|
||||
# :pressrelease => {
|
||||
# :label => {
|
||||
# :one => "Pressmeddelande"
|
||||
# }
|
||||
# }
|
||||
# }
|
||||
def self.to_deep_hash(hash)
|
||||
hash.inject({}) do |deep_hash, (key, value)|
|
||||
keys = key.to_s.split('.').reverse
|
||||
leaf_key = keys.shift
|
||||
key_hash = keys.inject({leaf_key.to_sym => value}) { |hash, key| {key.to_sym => hash} }
|
||||
deep_merge!(deep_hash, key_hash)
|
||||
deep_hash
|
||||
end
|
||||
end
|
||||
|
||||
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
||||
def self.deep_merge!(hash1, hash2)
|
||||
merger = proc { |key, v1, v2| Hash === v1 && Hash === v2 ? v1.merge(v2, &merger) : v2 }
|
||||
hash1.merge!(hash2, &merger)
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def extract_files
|
||||
files_to_scan.inject(HashWithIndifferentAccess.new) do |files, file|
|
||||
IO.read(file).scan(i18n_lookup_pattern).flatten.map(&:to_sym).each do |key|
|
||||
files[key] ||= []
|
||||
path = Pathname.new(File.expand_path(file)).relative_path_from(Pathname.new(Rails.root)).to_s
|
||||
files[key] << path if !files[key].include?(path)
|
||||
end
|
||||
files
|
||||
end
|
||||
end
|
||||
|
||||
def i18n_lookup_pattern
|
||||
/\b(?:I18n\.t|I18n\.translate|t)(?:\s|\():?'([a-z0-9_]+.[a-z0-9_.]+)'\)?/
|
||||
end
|
||||
|
||||
def files_to_scan
|
||||
Dir.glob(File.join(Translate::Storage.root_dir, "{app,config,lib}", "**","*.{rb,erb,rhtml}")) +
|
||||
Dir.glob(File.join(Translate::Storage.root_dir, "public", "javascripts", "**","*.js"))
|
||||
end
|
||||
end
|
||||
35
backup.rails2.3/plugins/translate/lib/translate/log.rb
Normal file
35
backup.rails2.3/plugins/translate/lib/translate/log.rb
Normal file
|
|
@ -0,0 +1,35 @@
|
|||
class Translate::Log
|
||||
attr_accessor :from_locale, :to_locale, :keys
|
||||
|
||||
def initialize(from_locale, to_locale, keys)
|
||||
self.from_locale = from_locale
|
||||
self.to_locale = to_locale
|
||||
self.keys = keys
|
||||
end
|
||||
|
||||
def write_to_file
|
||||
current_texts = File.exists?(file_path) ? file.read : {}
|
||||
current_texts.merge!(from_texts)
|
||||
file.write(current_texts)
|
||||
end
|
||||
|
||||
def read
|
||||
file.read
|
||||
end
|
||||
|
||||
private
|
||||
def file
|
||||
@file ||= Translate::File.new(file_path)
|
||||
end
|
||||
|
||||
def from_texts
|
||||
Translate::File.deep_stringify_keys(Translate::Keys.to_deep_hash(keys.inject({}) do |hash, key|
|
||||
hash[key] = I18n.backend.send(:lookup, from_locale, key)
|
||||
hash
|
||||
end))
|
||||
end
|
||||
|
||||
def file_path
|
||||
File.join(Rails.root, "config", "locales", "log", "from_#{from_locale}_to_#{to_locale}.yml")
|
||||
end
|
||||
end
|
||||
11
backup.rails2.3/plugins/translate/lib/translate/routes.rb
Normal file
11
backup.rails2.3/plugins/translate/lib/translate/routes.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
module Translate
|
||||
class Routes
|
||||
def self.translation_ui(map)
|
||||
map.with_options(:controller => 'translate') do |t|
|
||||
t.translate_list 'translate'
|
||||
t.translate 'translate/translate', :action => 'translate'
|
||||
t.translate_reload 'translate/reload', :action => 'reload'
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
28
backup.rails2.3/plugins/translate/lib/translate/storage.rb
Normal file
28
backup.rails2.3/plugins/translate/lib/translate/storage.rb
Normal file
|
|
@ -0,0 +1,28 @@
|
|||
class Translate::Storage
|
||||
attr_accessor :locale
|
||||
|
||||
def initialize(locale)
|
||||
self.locale = locale.to_sym
|
||||
end
|
||||
|
||||
def write_to_file
|
||||
Translate::File.new(file_path).write(keys)
|
||||
end
|
||||
|
||||
def self.file_paths(locale)
|
||||
Dir.glob(File.join(root_dir, "config", "locales", "**","#{locale}.yml"))
|
||||
end
|
||||
|
||||
def self.root_dir
|
||||
Rails.root
|
||||
end
|
||||
|
||||
private
|
||||
def keys
|
||||
{locale => I18n.backend.send(:translations)[locale]}
|
||||
end
|
||||
|
||||
def file_path
|
||||
File.join(Translate::Storage.root_dir, "config", "locales", "#{locale}.yml")
|
||||
end
|
||||
end
|
||||
165
backup.rails2.3/plugins/translate/lib/translate_controller.rb
Normal file
165
backup.rails2.3/plugins/translate/lib/translate_controller.rb
Normal file
|
|
@ -0,0 +1,165 @@
|
|||
class TranslateController < ActionController::Base
|
||||
# It seems users with active_record_store may get a "no :secret given" error if we don't disable csrf protection,
|
||||
skip_before_filter :verify_authenticity_token
|
||||
|
||||
prepend_view_path(File.join(File.dirname(__FILE__), "..", "views"))
|
||||
layout 'translate'
|
||||
|
||||
before_filter :init_translations
|
||||
before_filter :set_locale
|
||||
|
||||
def index
|
||||
initialize_keys
|
||||
filter_by_key_pattern
|
||||
filter_by_text_pattern
|
||||
filter_by_translated_or_changed
|
||||
sort_keys
|
||||
paginate_keys
|
||||
@total_entries = @keys.size
|
||||
end
|
||||
|
||||
def translate
|
||||
I18n.backend.store_translations(@to_locale, Translate::Keys.to_deep_hash(params[:key]))
|
||||
Translate::Storage.new(@to_locale).write_to_file
|
||||
Translate::Log.new(@from_locale, @to_locale, params[:key].keys).write_to_file
|
||||
force_init_translations # Force reload from YAML file
|
||||
flash[:notice] = "Translations stored"
|
||||
redirect_to params.slice(:filter, :sort_by, :key_type, :key_pattern, :text_type, :text_pattern).merge({:action => :index})
|
||||
end
|
||||
|
||||
def reload
|
||||
Translate::Keys.files = nil
|
||||
redirect_to :action => 'index'
|
||||
end
|
||||
|
||||
private
|
||||
def initialize_keys
|
||||
@files = Translate::Keys.files
|
||||
@keys = (@files.keys.map(&:to_s) + Translate::Keys.new.i18n_keys(@from_locale)).uniq
|
||||
@keys.reject! do |key|
|
||||
from_text = lookup(@from_locale, key)
|
||||
# When translating from one language to another, make sure there is a text to translate from.
|
||||
# Always exclude non string translation objects as we don't support editing them in the UI.
|
||||
(@from_locale != @to_locale && !from_text.present?) || (from_text.present? && !from_text.is_a?(String))
|
||||
end
|
||||
end
|
||||
|
||||
def lookup(locale, key)
|
||||
I18n.backend.send(:lookup, locale, key)
|
||||
end
|
||||
helper_method :lookup
|
||||
|
||||
def filter_by_translated_or_changed
|
||||
params[:filter] ||= 'all'
|
||||
return if params[:filter] == 'all'
|
||||
@keys.reject! do |key|
|
||||
case params[:filter]
|
||||
when 'untranslated'
|
||||
lookup(@to_locale, key).present?
|
||||
when 'translated'
|
||||
lookup(@to_locale, key).blank?
|
||||
when 'changed'
|
||||
old_from_text(key).blank? || lookup(@from_locale, key) == old_from_text(key)
|
||||
else
|
||||
raise "Unknown filter '#{params[:filter]}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def filter_by_key_pattern
|
||||
return if params[:key_pattern].blank?
|
||||
@keys.reject! do |key|
|
||||
case params[:key_type]
|
||||
when "starts_with"
|
||||
!key.starts_with?(params[:key_pattern])
|
||||
when "contains"
|
||||
key.index(params[:key_pattern]).nil?
|
||||
else
|
||||
raise "Unknown key_type '#{params[:key_type]}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def filter_by_text_pattern
|
||||
return if params[:text_pattern].blank?
|
||||
@keys.reject! do |key|
|
||||
case params[:text_type]
|
||||
when 'contains'
|
||||
!lookup(@from_locale, key).present? || !lookup(@from_locale, key).to_s.downcase.index(params[:text_pattern].downcase)
|
||||
when 'equals'
|
||||
!lookup(@from_locale, key).present? || lookup(@from_locale, key).to_s.downcase != params[:text_pattern].downcase
|
||||
else
|
||||
raise "Unknown text_type '#{params[:text_type]}'"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def sort_keys
|
||||
params[:sort_by] ||= "key"
|
||||
case params[:sort_by]
|
||||
when "key"
|
||||
@keys.sort!
|
||||
when "text"
|
||||
@keys.sort! do |key1, key2|
|
||||
if lookup(@from_locale, key1).present? && lookup(@from_locale, key2).present?
|
||||
lookup(@from_locale, key1).to_s.downcase <=> lookup(@from_locale, key2).to_s.downcase
|
||||
elsif lookup(@from_locale, key1).present?
|
||||
-1
|
||||
else
|
||||
1
|
||||
end
|
||||
end
|
||||
else
|
||||
raise "Unknown sort_by '#{params[:sort_by]}'"
|
||||
end
|
||||
end
|
||||
|
||||
def paginate_keys
|
||||
params[:page] ||= 1
|
||||
@paginated_keys = @keys[offset, per_page]
|
||||
end
|
||||
|
||||
def offset
|
||||
(params[:page].to_i - 1) * per_page
|
||||
end
|
||||
|
||||
def per_page
|
||||
50
|
||||
end
|
||||
helper_method :per_page
|
||||
|
||||
def init_translations
|
||||
I18n.backend.send(:init_translations) unless I18n.backend.initialized?
|
||||
end
|
||||
|
||||
def force_init_translations
|
||||
I18n.backend.send(:init_translations)
|
||||
end
|
||||
|
||||
def default_locale
|
||||
I18n.default_locale
|
||||
end
|
||||
|
||||
def set_locale
|
||||
session[:from_locale] ||= default_locale
|
||||
session[:to_locale] ||= :en
|
||||
session[:from_locale] = params[:from_locale] if params[:from_locale].present?
|
||||
session[:to_locale] = params[:to_locale] if params[:to_locale].present?
|
||||
@from_locale = session[:from_locale].to_sym
|
||||
@to_locale = session[:to_locale].to_sym
|
||||
end
|
||||
|
||||
def old_from_text(key)
|
||||
return @old_from_text[key] if @old_from_text && @old_from_text[key]
|
||||
@old_from_text = {}
|
||||
text = key.split(".").inject(log_hash) do |hash, k|
|
||||
hash ? hash[k] : nil
|
||||
end
|
||||
@old_from_text[key] = text
|
||||
end
|
||||
helper_method :old_from_text
|
||||
|
||||
def log_hash
|
||||
@log_hash ||= Translate::Log.new(@from_locale, @to_locale, {}).read
|
||||
end
|
||||
end
|
||||
45
backup.rails2.3/plugins/translate/lib/translate_helper.rb
Normal file
45
backup.rails2.3/plugins/translate/lib/translate_helper.rb
Normal file
|
|
@ -0,0 +1,45 @@
|
|||
module TranslateHelper
|
||||
def simple_filter(labels, param_name = 'filter', selected_value = nil)
|
||||
selected_value ||= params[param_name]
|
||||
filter = []
|
||||
labels.each do |item|
|
||||
if item.is_a?(Array)
|
||||
type, label = item
|
||||
else
|
||||
type = label = item
|
||||
end
|
||||
if type.to_s == selected_value.to_s
|
||||
filter << "<i>#{label}</i>"
|
||||
else
|
||||
link_params = params.merge({param_name.to_s => type})
|
||||
link_params.merge!({"page" => nil}) if param_name.to_s != "page"
|
||||
filter << link_to(label, link_params)
|
||||
end
|
||||
end
|
||||
filter.join(" | ")
|
||||
end
|
||||
|
||||
def n_lines(text, line_size)
|
||||
n_lines = 1
|
||||
if text.present?
|
||||
n_lines = text.split("\n").size
|
||||
if n_lines == 1 && text.length > line_size
|
||||
n_lines = text.length / line_size + 1
|
||||
end
|
||||
end
|
||||
n_lines
|
||||
end
|
||||
|
||||
def translate_javascript_includes
|
||||
sources = []
|
||||
if File.exists?(File.join(Rails.root, "public", "javascripts", "prototype.js"))
|
||||
sources << "/javascripts/prototype.js"
|
||||
else
|
||||
sources << "http://ajax.googleapis.com/ajax/libs/prototype/1.6.1.0/prototype.js"
|
||||
end
|
||||
sources << "http://www.google.com/jsapi"
|
||||
sources.map do |src|
|
||||
%Q{<script src="#{src}" type="text/javascript"></script>}
|
||||
end.join("\n")
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,129 @@
|
|||
require File.expand_path(File.dirname(__FILE__) + '/../spec_helper')
|
||||
|
||||
describe TranslateController do
|
||||
describe "index" do
|
||||
before(:each) do
|
||||
controller.stub!(:per_page).and_return(1)
|
||||
I18n.backend.stub!(:translations).and_return(i18n_translations)
|
||||
I18n.backend.instance_eval { @initialized = true }
|
||||
keys = mock(:keys)
|
||||
keys.stub!(:i18n_keys).and_return(['vendor.foobar'])
|
||||
Translate::Keys.should_receive(:new).and_return(keys)
|
||||
Translate::Keys.should_receive(:files).and_return(files)
|
||||
I18n.stub!(:valid_locales).and_return([:en, :sv])
|
||||
I18n.stub!(:default_locale).and_return(:sv)
|
||||
end
|
||||
|
||||
it "shows sorted paginated keys from the translate from locale and extracted keys by default" do
|
||||
get_page :index
|
||||
assigns(:from_locale).should == :sv
|
||||
assigns(:to_locale).should == :en
|
||||
assigns(:files).should == files
|
||||
assigns(:keys).sort.should == ['articles.new.page_title', 'home.page_title', 'vendor.foobar']
|
||||
assigns(:paginated_keys).should == ['articles.new.page_title']
|
||||
end
|
||||
|
||||
it "can be paginated with the page param" do
|
||||
get_page :index, :page => 2
|
||||
assigns(:files).should == files
|
||||
assigns(:paginated_keys).should == ['home.page_title']
|
||||
end
|
||||
|
||||
it "accepts a key_pattern param with key_type=starts_with" do
|
||||
get_page :index, :key_pattern => 'articles', :key_type => 'starts_with'
|
||||
assigns(:files).should == files
|
||||
assigns(:paginated_keys).should == ['articles.new.page_title']
|
||||
assigns(:total_entries).should == 1
|
||||
end
|
||||
|
||||
it "accepts a key_pattern param with key_type=contains" do
|
||||
get_page :index, :key_pattern => 'page_', :key_type => 'contains'
|
||||
assigns(:files).should == files
|
||||
assigns(:total_entries).should == 2
|
||||
assigns(:paginated_keys).should == ['articles.new.page_title']
|
||||
end
|
||||
|
||||
it "accepts a filter=untranslated param" do
|
||||
get_page :index, :filter => 'untranslated'
|
||||
assigns(:total_entries).should == 2
|
||||
assigns(:paginated_keys).should == ['articles.new.page_title']
|
||||
end
|
||||
|
||||
it "accepts a filter=translated param" do
|
||||
get_page :index, :filter => 'translated'
|
||||
assigns(:total_entries).should == 1
|
||||
assigns(:paginated_keys).should == ['vendor.foobar']
|
||||
end
|
||||
|
||||
it "accepts a filter=changed param" do
|
||||
log = mock(:log)
|
||||
old_translations = {:home => {:page_title => "Skapar ny artikel"}}
|
||||
log.should_receive(:read).and_return(Translate::File.deep_stringify_keys(old_translations))
|
||||
Translate::Log.should_receive(:new).with(:sv, :en, {}).and_return(log)
|
||||
get_page :index, :filter => 'changed'
|
||||
assigns(:total_entries).should == 1
|
||||
assigns(:keys).should == ["home.page_title"]
|
||||
end
|
||||
|
||||
def i18n_translations
|
||||
HashWithIndifferentAccess.new({
|
||||
:en => {
|
||||
:vendor => {
|
||||
:foobar => "Foo Baar"
|
||||
}
|
||||
},
|
||||
:sv => {
|
||||
:articles => {
|
||||
:new => {
|
||||
:page_title => "Skapa ny artikel"
|
||||
}
|
||||
},
|
||||
:home => {
|
||||
:page_title => "Välkommen till I18n"
|
||||
},
|
||||
:vendor => {
|
||||
:foobar => "Fobar"
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
def files
|
||||
HashWithIndifferentAccess.new({
|
||||
:'home.page_title' => ["app/views/home/index.rhtml"],
|
||||
:'general.back' => ["app/views/articles/new.rhtml", "app/views/categories/new.rhtml"],
|
||||
:'articles.new.page_title' => ["app/views/articles/new.rhtml"]
|
||||
})
|
||||
end
|
||||
end
|
||||
|
||||
describe "translate" do
|
||||
it "should store translations to I18n backend and then write them to a YAML file" do
|
||||
session[:from_locale] = :sv
|
||||
session[:to_locale] = :en
|
||||
translations = {
|
||||
:articles => {
|
||||
:new => {
|
||||
:title => "New Article"
|
||||
}
|
||||
},
|
||||
:category => "Category"
|
||||
}
|
||||
key_param = {'articles.new.title' => "New Article", "category" => "Category"}
|
||||
I18n.backend.should_receive(:store_translations).with(:en, translations)
|
||||
storage = mock(:storage)
|
||||
storage.should_receive(:write_to_file)
|
||||
Translate::Storage.should_receive(:new).with(:en).and_return(storage)
|
||||
log = mock(:log)
|
||||
log.should_receive(:write_to_file)
|
||||
Translate::Log.should_receive(:new).with(:sv, :en, key_param.keys).and_return(log)
|
||||
post :translate, "key" => key_param
|
||||
response.should be_redirect
|
||||
end
|
||||
end
|
||||
|
||||
def get_page(*args)
|
||||
get(*args)
|
||||
response.should be_success
|
||||
end
|
||||
end
|
||||
54
backup.rails2.3/plugins/translate/spec/file_spec.rb
Normal file
54
backup.rails2.3/plugins/translate/spec/file_spec.rb
Normal file
|
|
@ -0,0 +1,54 @@
|
|||
require 'fileutils'
|
||||
require File.dirname(__FILE__) + '/spec_helper'
|
||||
|
||||
describe Translate::File do
|
||||
describe "write" do
|
||||
before(:each) do
|
||||
@file = Translate::File.new(file_path)
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
FileUtils.rm(file_path)
|
||||
end
|
||||
|
||||
it "writes all I18n messages for a locale to YAML file" do
|
||||
@file.write(translations)
|
||||
@file.read.should == Translate::File.deep_stringify_keys(translations)
|
||||
end
|
||||
|
||||
def translations
|
||||
{
|
||||
:en => {
|
||||
:article => {
|
||||
:title => "One Article"
|
||||
},
|
||||
:category => "Category"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "deep_stringify_keys" do
|
||||
it "should convert all keys in a hash to strings" do
|
||||
Translate::File.deep_stringify_keys({
|
||||
:en => {
|
||||
:article => {
|
||||
:title => "One Article"
|
||||
},
|
||||
:category => "Category"
|
||||
}
|
||||
}).should == {
|
||||
"en" => {
|
||||
"article" => {
|
||||
"title" => "One Article"
|
||||
},
|
||||
"category" => "Category"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def file_path
|
||||
File.join(File.dirname(__FILE__), "files", "en.yml")
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1,12 @@
|
|||
class Article < ActiveRecord::Base
|
||||
def validate
|
||||
# t('li')
|
||||
errors.add_to_base([t(:'article.key1') + "#{t('article.key2')}"])
|
||||
I18n.t 'article.key3'
|
||||
I18n.t 'article.key3'
|
||||
I18n.t :'article.key4'
|
||||
I18n.translate :'article.key5'
|
||||
'bla bla t' + "blubba bla" + ' foobar'
|
||||
'bla bla t ' + "blubba bla" + ' foobar'
|
||||
end
|
||||
end
|
||||
|
|
@ -0,0 +1 @@
|
|||
<%= t(:'category_erb.key1') %>
|
||||
|
|
@ -0,0 +1 @@
|
|||
t(:'category_html.key1')
|
||||
|
|
@ -0,0 +1 @@
|
|||
<%= t(:'category_html_erb.key1') %>
|
||||
|
|
@ -0,0 +1,5 @@
|
|||
<script>
|
||||
document.createElement('li');
|
||||
</script>
|
||||
|
||||
<%= t(:'category_rhtml.key1') %>
|
||||
|
|
@ -0,0 +1 @@
|
|||
I18n.t('js.alert')
|
||||
179
backup.rails2.3/plugins/translate/spec/keys_spec.rb
Normal file
179
backup.rails2.3/plugins/translate/spec/keys_spec.rb
Normal file
|
|
@ -0,0 +1,179 @@
|
|||
require File.dirname(__FILE__) + '/spec_helper'
|
||||
require 'fileutils'
|
||||
|
||||
describe Translate::Keys do
|
||||
before(:each) do
|
||||
I18n.stub!(:default_locale).and_return(:en)
|
||||
@keys = Translate::Keys.new
|
||||
Translate::Storage.stub!(:root_dir).and_return(i18n_files_dir)
|
||||
end
|
||||
|
||||
describe "to_a" do
|
||||
it "extracts keys from I18n lookups in .rb, .html.erb, and .rhtml files" do
|
||||
@keys.to_a.map(&:to_s).sort.should == ['article.key1', 'article.key2', 'article.key3', 'article.key4', 'article.key5',
|
||||
'category_erb.key1', 'category_html_erb.key1', 'category_rhtml.key1', 'js.alert']
|
||||
end
|
||||
end
|
||||
|
||||
describe "to_hash" do
|
||||
it "return a hash with I18n keys and file lists" do
|
||||
@keys.to_hash[:'article.key3'].should == ["vendor/plugins/translate/spec/files/translate/app/models/article.rb"]
|
||||
end
|
||||
end
|
||||
|
||||
describe "i18n_keys" do
|
||||
before(:each) do
|
||||
I18n.backend.send(:init_translations) unless I18n.backend.initialized?
|
||||
end
|
||||
|
||||
it "should return all keys in the I18n backend translations hash" do
|
||||
I18n.backend.should_receive(:translations).and_return(translations)
|
||||
@keys.i18n_keys(:en).should == ['articles.new.page_title', 'categories.flash.created', 'empty', 'home.about']
|
||||
end
|
||||
|
||||
describe "untranslated_keys" do
|
||||
before(:each) do
|
||||
I18n.backend.stub!(:translations).and_return(translations)
|
||||
end
|
||||
|
||||
it "should return a hash with keys with missing translations in each locale" do
|
||||
@keys.untranslated_keys.should == {
|
||||
:sv => ['articles.new.page_title', 'categories.flash.created', 'empty']
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "missing_keys" do
|
||||
before(:each) do
|
||||
@file_path = File.join(i18n_files_dir, "config", "locales", "en.yml")
|
||||
Translate::File.new(@file_path).write({
|
||||
:en => {
|
||||
:home => {
|
||||
:page_title => false,
|
||||
:intro => {
|
||||
:one => "intro one",
|
||||
:other => "intro other"
|
||||
}
|
||||
}
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
FileUtils.rm(@file_path)
|
||||
end
|
||||
|
||||
it "should return a hash with keys that are not in the locale file" do
|
||||
@keys.stub!(:files).and_return({
|
||||
:'home.page_title' => "app/views/home/index.rhtml",
|
||||
:'home.intro' => 'app/views/home/index.rhtml',
|
||||
:'home.signup' => "app/views/home/_signup.rhtml",
|
||||
:'about.index.page_title' => "app/views/about/index.rhtml"
|
||||
})
|
||||
@keys.missing_keys.should == {
|
||||
:'home.signup' => "app/views/home/_signup.rhtml",
|
||||
:'about.index.page_title' => "app/views/about/index.rhtml"
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
describe "contains_key?" do
|
||||
it "works" do
|
||||
hash = {
|
||||
:foo => {
|
||||
:bar => {
|
||||
:baz => false
|
||||
}
|
||||
}
|
||||
}
|
||||
Translate::Keys.contains_key?(hash, "").should be_false
|
||||
Translate::Keys.contains_key?(hash, "foo").should be_true
|
||||
Translate::Keys.contains_key?(hash, "foo.bar").should be_true
|
||||
Translate::Keys.contains_key?(hash, "foo.bar.baz").should be_true
|
||||
Translate::Keys.contains_key?(hash, :"foo.bar.baz").should be_true
|
||||
Translate::Keys.contains_key?(hash, "foo.bar.baz.bla").should be_false
|
||||
end
|
||||
end
|
||||
|
||||
describe "translated_locales" do
|
||||
before(:each) do
|
||||
I18n.stub!(:default_locale).and_return(:en)
|
||||
I18n.stub!(:available_locales).and_return([:sv, :no, :en, :root])
|
||||
end
|
||||
|
||||
it "returns all avaiable except :root and the default" do
|
||||
Translate::Keys.translated_locales.should == [:sv, :no]
|
||||
end
|
||||
end
|
||||
|
||||
describe "to_deep_hash" do
|
||||
it "convert shallow hash with dot separated keys to deep hash" do
|
||||
Translate::Keys.to_deep_hash(shallow_hash).should == deep_hash
|
||||
end
|
||||
end
|
||||
|
||||
describe "to_shallow_hash" do
|
||||
it "converts a deep hash to a shallow one" do
|
||||
Translate::Keys.to_shallow_hash(deep_hash).should == shallow_hash
|
||||
end
|
||||
end
|
||||
|
||||
##########################################################################
|
||||
#
|
||||
# Helper Methods
|
||||
#
|
||||
##########################################################################
|
||||
|
||||
def translations
|
||||
{
|
||||
:en => {
|
||||
:home => {
|
||||
:about => "This site is about making money"
|
||||
},
|
||||
:articles => {
|
||||
:new => {
|
||||
:page_title => "New Article"
|
||||
}
|
||||
},
|
||||
:categories => {
|
||||
:flash => {
|
||||
:created => "Category created"
|
||||
}
|
||||
},
|
||||
:empty => nil
|
||||
},
|
||||
:sv => {
|
||||
:home => {
|
||||
:about => false
|
||||
}
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def shallow_hash
|
||||
{
|
||||
'pressrelease.label.one' => "Pressmeddelande",
|
||||
'pressrelease.label.other' => "Pressmeddelanden",
|
||||
'article' => "Artikel",
|
||||
'category' => ''
|
||||
}
|
||||
end
|
||||
|
||||
def deep_hash
|
||||
{
|
||||
:pressrelease => {
|
||||
:label => {
|
||||
:one => "Pressmeddelande",
|
||||
:other => "Pressmeddelanden"
|
||||
}
|
||||
},
|
||||
:article => "Artikel",
|
||||
:category => ''
|
||||
}
|
||||
end
|
||||
|
||||
def i18n_files_dir
|
||||
File.join(ENV['PWD'], "spec", "files", "translate")
|
||||
end
|
||||
end
|
||||
47
backup.rails2.3/plugins/translate/spec/log_spec.rb
Normal file
47
backup.rails2.3/plugins/translate/spec/log_spec.rb
Normal file
|
|
@ -0,0 +1,47 @@
|
|||
require 'fileutils'
|
||||
require File.dirname(__FILE__) + '/spec_helper'
|
||||
|
||||
describe Translate::Log do
|
||||
describe "write_to_file" do
|
||||
before(:each) do
|
||||
I18n.locale = :sv
|
||||
I18n.backend.store_translations(:sv, from_texts)
|
||||
keys = Translate::Keys.new
|
||||
@log = Translate::Log.new(:sv, :en, Translate::Keys.to_shallow_hash(from_texts).keys)
|
||||
@log.stub!(:file_path).and_return(file_path)
|
||||
FileUtils.rm_f file_path
|
||||
end
|
||||
|
||||
after(:each) do
|
||||
FileUtils.rm_f file_path
|
||||
end
|
||||
|
||||
it "writes new log file with from texts" do
|
||||
File.exists?(file_path).should be_false
|
||||
@log.write_to_file
|
||||
File.exists?(file_path).should be_true
|
||||
Translate::File.new(file_path).read.should == Translate::File.deep_stringify_keys(from_texts)
|
||||
end
|
||||
|
||||
it "merges from texts with current texts in log file and re-writes the log file" do
|
||||
@log.write_to_file
|
||||
I18n.backend.store_translations(:sv, {:category => "Kategori ny"})
|
||||
@log.keys = ['category']
|
||||
@log.write_to_file
|
||||
Translate::File.new(file_path).read['category'].should == "Kategori ny"
|
||||
end
|
||||
|
||||
def file_path
|
||||
File.join(File.dirname(__FILE__), "files", "from_sv_to_en.yml")
|
||||
end
|
||||
|
||||
def from_texts
|
||||
{
|
||||
:article => {
|
||||
:title => "En artikel"
|
||||
},
|
||||
:category => "Kategori"
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
11
backup.rails2.3/plugins/translate/spec/spec_helper.rb
Normal file
11
backup.rails2.3/plugins/translate/spec/spec_helper.rb
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
begin
|
||||
# Using PWD here instead of File.dirname(__FILE__) to be able to symlink to plugin
|
||||
# from within a Rails app.
|
||||
require File.expand_path(ENV['PWD'] + '/../../../spec/spec_helper')
|
||||
rescue LoadError => e
|
||||
puts "You need to install rspec in your base app\n#{e.message}: #{e.backtrace.join("\n")}"
|
||||
exit
|
||||
end
|
||||
|
||||
plugin_spec_dir = File.dirname(__FILE__)
|
||||
ActiveRecord::Base.logger = Logger.new(plugin_spec_dir + "/debug.log")
|
||||
33
backup.rails2.3/plugins/translate/spec/storage_spec.rb
Normal file
33
backup.rails2.3/plugins/translate/spec/storage_spec.rb
Normal file
|
|
@ -0,0 +1,33 @@
|
|||
require File.dirname(__FILE__) + '/spec_helper'
|
||||
|
||||
describe Translate::Storage do
|
||||
describe "write_to_file" do
|
||||
before(:each) do
|
||||
@storage = Translate::Storage.new(:en)
|
||||
end
|
||||
|
||||
it "writes all I18n messages for a locale to YAML file" do
|
||||
I18n.backend.should_receive(:translations).and_return(translations)
|
||||
@storage.stub!(:file_path).and_return(file_path)
|
||||
file = mock(:file)
|
||||
file.should_receive(:write).with(translations)
|
||||
Translate::File.should_receive(:new).with(file_path).and_return(file)
|
||||
@storage.write_to_file
|
||||
end
|
||||
|
||||
def file_path
|
||||
File.join(File.dirname(__FILE__), "files", "en.yml")
|
||||
end
|
||||
|
||||
def translations
|
||||
{
|
||||
:en => {
|
||||
:article => {
|
||||
:title => "One Article"
|
||||
},
|
||||
:category => "Category"
|
||||
}
|
||||
}
|
||||
end
|
||||
end
|
||||
end
|
||||
178
backup.rails2.3/plugins/translate/tasks/translate.rake
Normal file
178
backup.rails2.3/plugins/translate/tasks/translate.rake
Normal file
|
|
@ -0,0 +1,178 @@
|
|||
require 'yaml'
|
||||
|
||||
class Hash
|
||||
def deep_merge(other)
|
||||
# deep_merge by Stefan Rusterholz, see http://www.ruby-forum.com/topic/142809
|
||||
merger = proc { |key, v1, v2| (Hash === v1 && Hash === v2) ? v1.merge(v2, &merger) : v2 }
|
||||
merge(other, &merger)
|
||||
end
|
||||
|
||||
def set(keys, value)
|
||||
key = keys.shift
|
||||
if keys.empty?
|
||||
self[key] = value
|
||||
else
|
||||
self[key] ||= {}
|
||||
self[key].set keys, value
|
||||
end
|
||||
end
|
||||
|
||||
if ENV['SORT']
|
||||
# copy of ruby's to_yaml method, prepending sort.
|
||||
# before each so we get an ordered yaml file
|
||||
def to_yaml( opts = {} )
|
||||
YAML::quick_emit( self, opts ) do |out|
|
||||
out.map( taguri, to_yaml_style ) do |map|
|
||||
sort.each do |k, v| #<- Adding sort.
|
||||
map.add( k, v )
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
namespace :translate do
|
||||
desc "Show untranslated keys for locale LOCALE"
|
||||
task :untranslated => :environment do
|
||||
from_locale = I18n.default_locale
|
||||
untranslated = Translate::Keys.new.untranslated_keys
|
||||
|
||||
messages = []
|
||||
untranslated.each do |locale, keys|
|
||||
keys.each do |key|
|
||||
from_text = I18n.backend.send(:lookup, from_locale, key)
|
||||
messages << "#{locale}.#{key} (#{from_locale}.#{key}='#{from_text}')"
|
||||
end
|
||||
end
|
||||
|
||||
if messages.present?
|
||||
messages.each { |m| puts m }
|
||||
else
|
||||
puts "No untranslated keys"
|
||||
end
|
||||
end
|
||||
|
||||
desc "Show I18n keys that are missing in the config/locales/default_locale.yml YAML file"
|
||||
task :missing => :environment do
|
||||
missing = Translate::Keys.new.missing_keys.inject([]) do |keys, (key, filename)|
|
||||
keys << "#{key} in \t #{filename} is missing"
|
||||
end
|
||||
puts missing.present? ? missing.join("\n") : "No missing translations in the default locale file"
|
||||
end
|
||||
|
||||
desc "Remove all translation texts that are no longer present in the locale they were translated from"
|
||||
task :remove_obsolete_keys => :environment do
|
||||
I18n.backend.send(:init_translations)
|
||||
master_locale = ENV['LOCALE'] || I18n.default_locale
|
||||
Translate::Keys.translated_locales.each do |locale|
|
||||
texts = {}
|
||||
Translate::Keys.new.i18n_keys(locale).each do |key|
|
||||
if I18n.backend.send(:lookup, master_locale, key).to_s.present?
|
||||
texts[key] = I18n.backend.send(:lookup, locale, key)
|
||||
end
|
||||
end
|
||||
I18n.backend.send(:translations)[locale] = nil # Clear out all current translations
|
||||
I18n.backend.store_translations(locale, Translate::Keys.to_deep_hash(texts))
|
||||
Translate::Storage.new(locale).write_to_file
|
||||
end
|
||||
end
|
||||
|
||||
desc "Merge I18n keys from log/translations.yml into config/locales/*.yml (for use with the Rails I18n TextMate bundle)"
|
||||
task :merge_keys => :environment do
|
||||
I18n.backend.send(:init_translations)
|
||||
new_translations = YAML::load(IO.read(File.join(Rails.root, "log", "translations.yml")))
|
||||
raise("Can only merge in translations in single locale") if new_translations.keys.size > 1
|
||||
locale = new_translations.keys.first
|
||||
|
||||
overwrites = false
|
||||
Translate::Keys.to_shallow_hash(new_translations[locale]).keys.each do |key|
|
||||
new_text = key.split(".").inject(new_translations[locale]) { |hash, sub_key| hash[sub_key] }
|
||||
existing_text = I18n.backend.send(:lookup, locale.to_sym, key)
|
||||
if existing_text && new_text != existing_text
|
||||
puts "ERROR: key #{key} already exists with text '#{existing_text.inspect}' and would be overwritten by new text '#{new_text}'. " +
|
||||
"Set environment variable OVERWRITE=1 if you really want to do this."
|
||||
overwrites = true
|
||||
end
|
||||
end
|
||||
|
||||
if !overwrites || ENV['OVERWRITE']
|
||||
I18n.backend.store_translations(locale, new_translations[locale])
|
||||
Translate::Storage.new(locale).write_to_file
|
||||
end
|
||||
end
|
||||
|
||||
desc "Apply Google translate to auto translate all texts in locale ENV['FROM'] to locale ENV['TO']"
|
||||
task :google => :environment do
|
||||
raise "Please specify FROM and TO locales as environment variables" if ENV['FROM'].blank? || ENV['TO'].blank?
|
||||
|
||||
# Depends on httparty gem
|
||||
# http://www.robbyonrails.com/articles/2009/03/16/httparty-goes-foreign
|
||||
class GoogleApi
|
||||
include HTTParty
|
||||
base_uri 'ajax.googleapis.com'
|
||||
def self.translate(string, to, from)
|
||||
tries = 0
|
||||
begin
|
||||
get("/ajax/services/language/translate",
|
||||
:query => {:langpair => "#{from}|#{to}", :q => string, :v => 1.0},
|
||||
:format => :json)
|
||||
rescue
|
||||
tries += 1
|
||||
puts("SLEEPING - retrying in 5...")
|
||||
sleep(5)
|
||||
retry if tries < 10
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
I18n.backend.send(:init_translations)
|
||||
|
||||
start_at = Time.now
|
||||
translations = {}
|
||||
Translate::Keys.new.i18n_keys(ENV['FROM']).each do |key|
|
||||
from_text = I18n.backend.send(:lookup, ENV['FROM'], key).to_s
|
||||
to_text = I18n.backend.send(:lookup, ENV['TO'], key)
|
||||
if !from_text.blank? && to_text.blank?
|
||||
print "#{key}: '#{from_text[0, 40]}' => "
|
||||
if !translations[from_text]
|
||||
response = GoogleApi.translate(from_text, ENV['TO'], ENV['FROM'])
|
||||
translations[from_text] = response["responseData"] && response["responseData"]["translatedText"]
|
||||
end
|
||||
if !(translation = translations[from_text]).blank?
|
||||
translation.gsub!(/\(\(([a-z_.]+)\)\)/i, '{{\1}}')
|
||||
# Google translate sometimes replaces {{foobar}} with (()) foobar. We skip these
|
||||
if translation !~ /\(\(\)\)/
|
||||
puts "'#{translation[0, 40]}'"
|
||||
I18n.backend.store_translations(ENV['TO'].to_sym, Translate::Keys.to_deep_hash({key => translation}))
|
||||
else
|
||||
puts "SKIPPING since interpolations were messed up: '#{translation[0,40]}'"
|
||||
end
|
||||
else
|
||||
puts "NO TRANSLATION - #{response.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
puts "\nTime elapsed: #{(((Time.now - start_at) / 60) * 10).to_i / 10.to_f} minutes"
|
||||
Translate::Storage.new(ENV['TO'].to_sym).write_to_file
|
||||
end
|
||||
|
||||
desc "List keys that have changed I18n texts between YAML file ENV['FROM_FILE'] and YAML file ENV['TO_FILE']. Set ENV['VERBOSE'] to see changes"
|
||||
task :changed => :environment do
|
||||
from_hash = Translate::Keys.to_shallow_hash(Translate::File.new(ENV['FROM_FILE']).read)
|
||||
to_hash = Translate::Keys.to_shallow_hash(Translate::File.new(ENV['TO_FILE']).read)
|
||||
from_hash.each do |key, from_value|
|
||||
if (to_value = to_hash[key]) && to_value != from_value
|
||||
key_without_locale = key[/^[^.]+\.(.+)$/, 1]
|
||||
if ENV['VERBOSE']
|
||||
puts "KEY: #{key_without_locale}"
|
||||
puts "FROM VALUE: '#{from_value}'"
|
||||
puts "TO VALUE: '#{to_value}'"
|
||||
else
|
||||
puts key_without_locale
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
359
backup.rails2.3/plugins/translate/views/layouts/translate.rhtml
Normal file
359
backup.rails2.3/plugins/translate/views/layouts/translate.rhtml
Normal file
|
|
@ -0,0 +1,359 @@
|
|||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Strict//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd">
|
||||
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
|
||||
<head>
|
||||
<meta http-equiv="Content-Type" content="text/html;charset=utf-8" />
|
||||
<title><%= h(@page_title) %></title>
|
||||
|
||||
<%= translate_javascript_includes %>
|
||||
<script type="text/javascript">
|
||||
google.load("language", "1");
|
||||
|
||||
function getGoogleTranslation(id, text, from_language, to_language) {
|
||||
text = text.replace(/\{\{/, '__').replace(/\}\}/, '__')
|
||||
google.language.translate(text, from_language, to_language, function(result) {
|
||||
if (!result.error) {
|
||||
result_text = result.translation.unescapeHTML().gsub(/__(.+)__/, function(match){
|
||||
return '{{' + match[1] + '}}';
|
||||
});
|
||||
Form.Element.setValue(id, result_text);
|
||||
}
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
/*
|
||||
prototypeUtils.js from http://jehiah.com/
|
||||
Licensed under Creative Commons.
|
||||
version 1.0 December 20 2005
|
||||
|
||||
Contains:
|
||||
+ Form.Element.setValue()
|
||||
+ unpackToForm()
|
||||
|
||||
*/
|
||||
|
||||
/* Form.Element.setValue("fieldname/id","valueToSet") */
|
||||
Form.Element.setValue = function(element,newValue) {
|
||||
element_id = element;
|
||||
element = $(element);
|
||||
if (!element){element = document.getElementsByName(element_id)[0];}
|
||||
if (!element){return false;}
|
||||
var method = element.tagName.toLowerCase();
|
||||
var parameter = Form.Element.SetSerializers[method](element,newValue);
|
||||
}
|
||||
|
||||
Form.Element.SetSerializers = {
|
||||
input: function(element,newValue) {
|
||||
switch (element.type.toLowerCase()) {
|
||||
case 'submit':
|
||||
case 'hidden':
|
||||
case 'password':
|
||||
case 'text':
|
||||
return Form.Element.SetSerializers.textarea(element,newValue);
|
||||
case 'checkbox':
|
||||
case 'radio':
|
||||
return Form.Element.SetSerializers.inputSelector(element,newValue);
|
||||
}
|
||||
return false;
|
||||
},
|
||||
|
||||
inputSelector: function(element,newValue) {
|
||||
fields = document.getElementsByName(element.name);
|
||||
for (var i=0;i<fields.length;i++){
|
||||
if (fields[i].value == newValue){
|
||||
fields[i].checked = true;
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
textarea: function(element,newValue) {
|
||||
element.value = newValue;
|
||||
},
|
||||
|
||||
select: function(element,newValue) {
|
||||
var value = '', opt, index = element.selectedIndex;
|
||||
for (var i=0;i< element.options.length;i++){
|
||||
if (element.options[i].value == newValue){
|
||||
element.selectedIndex = i;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function unpackToForm(data){
|
||||
for (i in data){
|
||||
Form.Element.setValue(i,data[i].toString());
|
||||
}
|
||||
}
|
||||
|
||||
</script>
|
||||
|
||||
|
||||
<style type="text/css">
|
||||
/*reset.css*/
|
||||
/* v1.0 | 20080212 */
|
||||
html, body, div, span, applet, object, iframe,
|
||||
h1, h2, h3, h4, h5, h6, p, blockquote, pre,
|
||||
a, abbr, acronym, address, big, cite, code,
|
||||
del, dfn, em, font, img, ins, kbd, q, s, samp,
|
||||
small, strike, strong, sub, sup, tt, var,
|
||||
b, u, i, center,
|
||||
dl, dt, dd, ol, ul, li,
|
||||
fieldset, form, label, legend,
|
||||
table, caption, tbody, tfoot, thead, tr, th, td {
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border: 0;
|
||||
outline: 0;
|
||||
font-size: 100%;
|
||||
vertical-align: baseline;
|
||||
background: transparent;
|
||||
}
|
||||
body {
|
||||
line-height: 1;
|
||||
}
|
||||
ol, ul {
|
||||
list-style: none;
|
||||
}
|
||||
blockquote, q {
|
||||
quotes: none;
|
||||
}
|
||||
blockquote:before, blockquote:after,
|
||||
q:before, q:after {
|
||||
content: '';
|
||||
content: none;
|
||||
}
|
||||
|
||||
/* remember to define focus styles! */
|
||||
:focus {
|
||||
outline: 0;
|
||||
}
|
||||
|
||||
/* remember to highlight inserts somehow! */
|
||||
ins {
|
||||
text-decoration: none;
|
||||
}
|
||||
del {
|
||||
text-decoration: line-through;
|
||||
}
|
||||
|
||||
/* tables still need 'cellspacing="0"' in the markup */
|
||||
table {
|
||||
border-collapse: collapse;
|
||||
border-spacing: 0;
|
||||
}
|
||||
/*clear fix*/
|
||||
.clearfix:after{content:".";display:block;height:0;clear:both;visibility:hidden;}
|
||||
.clearfix{display:inline-block;}
|
||||
html[xmlns] .clearfix {
|
||||
display: block;
|
||||
}
|
||||
* html .clearfix{height:1%;}
|
||||
/*start layout*/
|
||||
body{
|
||||
background:#fff;
|
||||
color:#333;
|
||||
font-size:75%;
|
||||
font-family:Arial;
|
||||
margin:2em auto;
|
||||
line-height:1.5em;
|
||||
}
|
||||
textarea,input,select{
|
||||
font-family:Arial;
|
||||
font-size:1em;
|
||||
}
|
||||
h1{
|
||||
color:#d46021;
|
||||
font-size:2em;
|
||||
margin-bottom:0.5em;
|
||||
}
|
||||
h2{
|
||||
text-align:left;
|
||||
color:#d46021;
|
||||
font-size:1.3em;
|
||||
padding-left:0;
|
||||
}
|
||||
a{
|
||||
color:#2158C7;
|
||||
}
|
||||
div#container{
|
||||
width:960px;
|
||||
margin:0 auto;
|
||||
font-size:1em;
|
||||
}
|
||||
/*paging*/
|
||||
div.paging{
|
||||
margin-bottom:1em;
|
||||
text-align:left;
|
||||
}
|
||||
div.paging div{
|
||||
border:solid 1px red;
|
||||
margin:1em 1em 0;
|
||||
padding:0.5em;
|
||||
border:solid 1px #d5d6d5;
|
||||
background:#f1f1f1;
|
||||
}
|
||||
ul.paging{
|
||||
display:inline-block;
|
||||
}
|
||||
ul.paging li{
|
||||
display:block;
|
||||
margin:0.2em 0;
|
||||
float:left;
|
||||
}
|
||||
ul.paging li.selected a{
|
||||
color:#fff;
|
||||
background:#2158C7;
|
||||
font-weight:bold;
|
||||
padding:0.5em 0.7em;
|
||||
}
|
||||
ul.paging li a{
|
||||
display:inline-block;
|
||||
line-height:1em;
|
||||
padding:0.5em 0.5em;
|
||||
}
|
||||
/*forms filter*/
|
||||
fieldset{
|
||||
padding:1em;
|
||||
margin:1em;
|
||||
border:solid 2px #d46021;
|
||||
}
|
||||
legend{
|
||||
font-size:1.2em;
|
||||
font-weight:bold;
|
||||
padding:0 1em;
|
||||
padding-bottom:0.5em;
|
||||
}
|
||||
label{
|
||||
font-weight:bold;
|
||||
}
|
||||
fieldset span{padding-right:0.5em;}
|
||||
div#show-sort label,
|
||||
div#languages label,
|
||||
div#filter-pattern label{
|
||||
display:inline-block;
|
||||
width:100px;
|
||||
line-height:2em;
|
||||
}
|
||||
div#show-sort select,
|
||||
div#languages select,
|
||||
div#filter-pattern select{
|
||||
width:120px;
|
||||
margin-right:0.5em;
|
||||
}
|
||||
div#show-sort input.text-default,
|
||||
div#languages input.text-default,
|
||||
div#filter-pattern input.text-default{
|
||||
width:200px;
|
||||
}
|
||||
p.hits{
|
||||
margin-top:1em;
|
||||
}
|
||||
/*translation edit*/
|
||||
div.translations{
|
||||
margin:1em;
|
||||
padding:1em;
|
||||
border:solid 2px #d46021;
|
||||
}
|
||||
div.translations h2{
|
||||
margin-bottom:1em;
|
||||
}
|
||||
p.translate{
|
||||
background:red;
|
||||
border:solid 1px #d5d6d5;
|
||||
background:#f1f1f1;
|
||||
margin:0.5em;
|
||||
padding:0.7em 0.5em 0.5em 1.5em;
|
||||
}
|
||||
div.translation{
|
||||
padding:1em;
|
||||
border-bottom:solid 0.2em #d46021;
|
||||
margin:0 1em 1em 1.6em;
|
||||
}
|
||||
div.translation input, div.translation textarea{
|
||||
width:98%;
|
||||
margin:1em 0;
|
||||
display:inline-block;
|
||||
padding:0.3em;
|
||||
}
|
||||
div.translation textarea{
|
||||
height:50px;
|
||||
}
|
||||
div.translation em strong{
|
||||
color:#333;
|
||||
padding-right:0.5em;
|
||||
}
|
||||
p.translation em{
|
||||
display:block;
|
||||
font-size:0.8333em;
|
||||
}
|
||||
div.translation a{
|
||||
padding:1em;
|
||||
}
|
||||
div.translation input.btnDefault{
|
||||
margin:0 0 1em;
|
||||
width:auto;
|
||||
}
|
||||
.focus-text{
|
||||
font-weight:bold;
|
||||
}
|
||||
div.selected{
|
||||
margin:0 1em 1em 1em;
|
||||
border-left:solid 0.6em #d46021;
|
||||
border-right:solid 0.2em #d46021;
|
||||
border-top:solid 0.2em #d46021;
|
||||
background:#f1f1f1;
|
||||
}
|
||||
.display{display:block !important;}
|
||||
/*feedback*/
|
||||
div#notice, div#error {
|
||||
font-size:1em;
|
||||
margin:1em;
|
||||
padding: 1em;
|
||||
border: 1px solid red;
|
||||
}
|
||||
div#notice span, div#error span{
|
||||
font-size:1.5em;
|
||||
}
|
||||
|
||||
div#error {
|
||||
background-color: #F3C6CC;
|
||||
color: red;
|
||||
}
|
||||
div#notice {
|
||||
border-color: #72A974;
|
||||
color: #597B5C;
|
||||
background-color: #BCFFBD;
|
||||
}
|
||||
</style>
|
||||
<script type="text/javascript">
|
||||
onload = function (){
|
||||
$$("div.translation input, div.translation textarea").each(function (e){
|
||||
Event.observe(e,'focus', function (elm){
|
||||
this.up(1).down(".translation-text").addClassName("focus-text");
|
||||
this.up(1).addClassName("selected");
|
||||
});
|
||||
Event.observe(e,'blur', function (elm,e){
|
||||
this.up(1).down(".translation-text").removeClassName("focus-text");
|
||||
this.up(1).removeClassName("selected");
|
||||
});
|
||||
});
|
||||
}
|
||||
</script>
|
||||
</head>
|
||||
<body>
|
||||
<div id="container">
|
||||
<% if @page_title -%><h1><%=h @page_title %></h1><% end -%>
|
||||
<% [:notice, :error].each do |message| %>
|
||||
<%if flash[message] %>
|
||||
<div id="<%= message %>">
|
||||
<span><%= h(flash[message]) if flash[message] %></span>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
<%= yield %>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
|
|
@ -0,0 +1,24 @@
|
|||
<%
|
||||
# Expects locals:
|
||||
#
|
||||
# total_entries
|
||||
# per_page
|
||||
|
||||
n_pages = total_entries/per_page + (total_entries % per_page > 0 ? 1 : 0)
|
||||
current_page = (params[:page] || 1).to_i
|
||||
%>
|
||||
|
||||
<% if n_pages > 1 %>
|
||||
<h2>Pages:</h2>
|
||||
<div class="clearfix">
|
||||
<ul class="paging">
|
||||
<% (1..n_pages).each do |page_number| %>
|
||||
<% if current_page == page_number %>
|
||||
<li class="selected"><%= link_to(page_number, params.merge(:page => page_number), :title => "Page #{page_number}" ) %></li>
|
||||
<% else %>
|
||||
<li><%= link_to(page_number, params.merge(:page => page_number), :title => "Page #{page_number}") %></li>
|
||||
<% end %>
|
||||
<% end %>
|
||||
</ul>
|
||||
</div>
|
||||
<% end %>
|
||||
114
backup.rails2.3/plugins/translate/views/translate/index.rhtml
Normal file
114
backup.rails2.3/plugins/translate/views/translate/index.rhtml
Normal file
|
|
@ -0,0 +1,114 @@
|
|||
<%
|
||||
@page_title = "Translate"
|
||||
show_filters = ["all", "untranslated", "translated"]
|
||||
show_filters << "changed" if @from_locale != @to_locale
|
||||
%>
|
||||
|
||||
<fieldset>
|
||||
<legend>Search filter</legend>
|
||||
<div id="show-sort">
|
||||
<p>
|
||||
<label>Show:</label> <%= simple_filter(show_filters) %>
|
||||
</p>
|
||||
<p>
|
||||
<label>Sort by:</label> <%= simple_filter(["key", "text"], 'sort_by') %>
|
||||
</p>
|
||||
</div>
|
||||
<% form_tag(params, :method => :get) do %>
|
||||
<div id="languages">
|
||||
<p>
|
||||
<%= hidden_field_tag(:filter, params[:filter]) %>
|
||||
<%= hidden_field_tag(:sort_by, params[:sort_by]) %>
|
||||
<label>Translate from</label>
|
||||
<%= select_tag(:from_locale, options_for_select(I18n.valid_locales, @from_locale.to_sym)) %> <span>to</span>
|
||||
<%= select_tag(:to_locale, options_for_select(I18n.valid_locales, @to_locale.to_sym)) %>
|
||||
<%= submit_tag "Display" %>
|
||||
</p>
|
||||
</div>
|
||||
<div id="filter-pattern">
|
||||
<p>
|
||||
<label for="key_pattern_value">Key</label>
|
||||
<%= select_tag(:key_type, options_for_select([["contains", 'contains'], ["starts with", 'starts_with']], params[:key_type])) %>
|
||||
<%= text_field_tag(:key_pattern, params[:key_pattern], :size => 50, :id => "key_pattern_value", :class => "text-default") %>
|
||||
</p>
|
||||
<p>
|
||||
<label for="text_pattern_value">Text</label>
|
||||
<%= select_tag(:text_type, options_for_select(['contains', 'equals'], params[:text_type])) %>
|
||||
<%= text_field_tag(:text_pattern, params[:text_pattern], :size => 50, :id => "text_pattern_value", :class => "text-default") %>
|
||||
</p>
|
||||
<p>
|
||||
<%= submit_tag "Search" %>
|
||||
<%= link_to "clear", params.merge({:text_pattern => nil, :key_pattern => nil}) %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
<p class="hits">
|
||||
Found <strong><%= @total_entries %></strong> messages
|
||||
</p>
|
||||
<p>
|
||||
<%= link_to "Reload messages", translate_reload_path %>
|
||||
</p>
|
||||
</fieldset>
|
||||
|
||||
|
||||
<div class="paging">
|
||||
<%= render :partial => 'pagination', :locals => {:total_entries => @total_entries, :per_page => per_page} %>
|
||||
</div>
|
||||
|
||||
<% if @total_entries > 0 %>
|
||||
<% form_tag(translate_path) do %>
|
||||
<div>
|
||||
<%= hidden_field_tag(:filter, params[:filter], :id => "hid_filter") %>
|
||||
<%= hidden_field_tag(:sort_by, params[:sort_by], :id => "hid_sort_by") %>
|
||||
<%= hidden_field_tag(:key_type, params[:key_type], :id => "hid_key_type") %>
|
||||
<%= hidden_field_tag(:key_pattern, params[:key_pattern], :id => "hid_key_pattern") %>
|
||||
<%= hidden_field_tag(:text_type, params[:text_type], :id => "hid_text_type") %>
|
||||
<%= hidden_field_tag(:text_pattern, params[:text_pattern], :id => "hid_text_pattern") %>
|
||||
</div>
|
||||
<div class="translations">
|
||||
<h2>Translations from <%= @from_locale %> to <%= @to_locale %></h2>
|
||||
<p class="translate">
|
||||
<%= submit_tag "Save Translations" %>
|
||||
</p>
|
||||
<% @paginated_keys.each do |key|
|
||||
from_text = lookup(@from_locale, key)
|
||||
to_text = lookup(@to_locale, key)
|
||||
line_size = 100
|
||||
n_lines = n_lines(from_text, line_size)
|
||||
field_name = "key[#{key}]"
|
||||
%>
|
||||
<div class="translation">
|
||||
<% if from_text.present? %>
|
||||
<p class="translation-text">
|
||||
<%= simple_format(h(from_text)) %>
|
||||
</p>
|
||||
<% end %>
|
||||
<p class="edit-form">
|
||||
<% if n_lines > 1 %>
|
||||
<%= text_area_tag(field_name, to_text, :size => "#{line_size}x#{n_lines}", :id => key) %>
|
||||
<% else %>
|
||||
<%= text_field_tag(field_name, to_text, :size => line_size, :id => key) %>
|
||||
<% end %>
|
||||
</p>
|
||||
<p>
|
||||
<em>
|
||||
<%= link_to_function 'Auto Translate', "getGoogleTranslation('#{key}', \"#{escape_javascript(from_text)}\", '#{@from_locale}', '#{@to_locale}')", :style => 'padding: 0; margin: 0;' %>
|
||||
<br/>
|
||||
<strong>Key:</strong><%=h key %><br/>
|
||||
<% if @files[key] %>
|
||||
<strong>File:</strong><%= @files[key].join("<br/>") %>
|
||||
<% end %>
|
||||
</em>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
<p class="translate">
|
||||
<%= submit_tag "Save Translations" %>
|
||||
</p>
|
||||
</div>
|
||||
<% end %>
|
||||
<% end %>
|
||||
|
||||
<div class="paging">
|
||||
<%= render :partial => 'pagination', :locals => {:total_entries => @total_entries, :per_page => per_page} %>
|
||||
</div>
|
||||
Loading…
Add table
Add a link
Reference in a new issue