Compare commits

...

8 Commits
master ... i18n

48 changed files with 841 additions and 81 deletions

View File

@ -11,6 +11,7 @@ gem 'devise', '1.3.4'
gem 'devise_bushido_authenticatable', '1.0.0.alpha10', :require => 'devise_cas_authenticatable'
gem 'mongoid', '~> 2.0.2'
gem 'mongoid_i18n', :require => 'mongoid/i18n'
gem 'bson_ext', '~> 1.3.0'
gem 'locomotive_mongoid_acts_as_tree', '0.1.5.7', :require => 'mongoid_acts_as_tree'
gem 'will_paginate', '~> 3.0.0'

View File

@ -173,6 +173,9 @@ GEM
activemodel (~> 3.0)
mongo (~> 1.3)
tzinfo (~> 0.3.22)
mongoid_i18n (0.3.1)
bson_ext
mongoid (>= 2.0.0)
net-ssh (2.1.4)
nokogiri (1.5.0)
open4 (1.1.0)
@ -316,6 +319,7 @@ DEPENDENCIES
mimetype-fu
mocha (= 0.9.12)
mongoid (~> 2.0.2)
mongoid_i18n
pickle
rack-cache
rails (= 3.0.10)

View File

@ -0,0 +1,11 @@
#site-locale-picker
%span.hand
 
%ul
- @locales.each_with_index do |locale, index|
%li{ :class => "#{'first' if index == 0}" }
= image_tag "admin/icons/flags/#{locale}.png", :class => 'flag'
%span.text= locale

View File

@ -0,0 +1,15 @@
class Admin::SiteLocalePickerCell < Cell::Base
def show(args)
site = args[:site]
locale = args[:locale].to_s
if site.locales.empty? || site.locales.size < 2
''
else
@locales = [locale] + (site.locales - [locale])
render
end
end
end

View File

@ -15,6 +15,8 @@ module Admin
before_filter :set_locale
before_filter :set_site_locale
helper_method :sections, :current_site_url, :site_url, :page_url, :current_ability
# https://rails.lighthouseapp.com/projects/8994/tickets/1905-apphelpers-within-plugin-not-being-mixed-in
@ -71,7 +73,25 @@ module Admin
end
def set_locale
I18n.locale = current_admin.locale rescue Locomotive.config.default_locale
I18n.locale = current_admin.locale rescue Locomotive.config.default_locale # for the back-office
end
def set_site_locale
I18n.default_site_locale = current_site.default_locale
if params[:site_locale].present?
session[:site_locale] = params[:site_locale]
end
unless current_site.locales.include?(session[:site_locale])
session[:site_locale] = current_site.default_locale
end
I18n.site_locale = session[:site_locale]
end
def back_to_default_site_locale
session[:site_locale] = I18n.site_locale = current_site.default_locale
end
# ___ site/page urls builder ___

View File

@ -11,6 +11,8 @@ module Admin
before_filter :authorize_content
before_filter :back_to_default_site_locale, :only => %w(new create)
helper_method :breadcrumb_root, :breadcrumb_url, :back_url
def index

View File

@ -5,6 +5,8 @@ module Admin
respond_to :json, :only => [:update, :sort, :get_path]
before_filter :back_to_default_site_locale, :only => %w(new create)
def index
@pages = current_site.all_pages_in_once
end

View File

@ -30,4 +30,7 @@ module Admin::SitesHelper
Locomotive.config.multi_sites?
end
def ordered_site_locales(site)
site.locales + (Locomotive.config.site_locales - site.locales)
end
end

View File

@ -2,12 +2,15 @@ class ContentInstance
include Mongoid::Document
include Mongoid::Timestamps
include Mongoid::I18n
## extensions ##
include CustomFields::ProxyClassEnabler
include Extensions::Shared::Seo
## fields (dynamic fields) ##
localized_fields :_position_in_list, /^custom_field_/
field :_slug
field :_position_in_list, :type => Integer, :default => 0
field :_visible, :type => Boolean, :default => true

View File

@ -1,27 +1,28 @@
class EditableElement
include Mongoid::Document
include Mongoid::Document
include Mongoid::I18n
## fields ##
field :slug
field :block
field :default_content
field :default_attribute
field :hint
field :priority, :type => Integer, :default => 0
field :disabled, :type => Boolean, :default => false
field :assignable, :type => Boolean, :default => true
field :from_parent, :type => Boolean, :default => false
## fields ##
field :slug
field :block
localized_field :default_content
field :default_attribute
field :hint
field :priority, :type => Integer, :default => 0
field :disabled, :type => Boolean, :default => false
field :assignable, :type => Boolean, :default => true
field :from_parent, :type => Boolean, :default => false
## associations ##
embedded_in :page, :inverse_of => :editable_elements
## associations ##
embedded_in :page, :inverse_of => :editable_elements
## validations ##
validates_presence_of :slug
## validations ##
validates_presence_of :slug
## scopes ##
scope :by_priority, :order_by => [[:priority, :desc]]
## scopes ##
scope :by_priority, :order_by => [[:priority, :desc]]
## methods ##
## methods ##
end

View File

@ -1,7 +1,11 @@
class EditableFile < EditableElement
localized_fields :source_filename
mount_uploader :source, EditableFileUploader
# localized_field :source
def content
self.source? ? self.source.url : self.default_content
end

View File

@ -1,12 +1,19 @@
class EditableShortText < EditableElement
## fields ##
field :content
localized_field :content
## methods ##
def content
self.read_attribute(:content).blank? ? self.default_content : self.read_attribute(:content)
# def content
# self.read_attribute(:content).blank? ? self.default_content : self.read_attribute(:content)
# end
def content_with_localization
value = self.content_without_localization
value.blank? ? self.default_content : value
end
alias_method_chain :content, :localization
end

View File

@ -5,9 +5,9 @@ module Extensions
extend ActiveSupport::Concern
included do
field :serialized_template, :type => Binary
field :template_dependencies, :type => Array, :default => []
field :snippet_dependencies, :type => Array, :default => []
localized_field :serialized_template, :type => Binary
localized_field :template_dependencies, :type => Array, :default => []
localized_field :snippet_dependencies, :type => Array, :default => []
attr_reader :template_changed
@ -22,7 +22,7 @@ module Extensions
module InstanceMethods
def template
@template ||= Marshal.load(read_attribute(:serialized_template).to_s) rescue nil
@template ||= Marshal.load(self.serialized_template.to_s) rescue nil
end
protected

View File

@ -4,9 +4,9 @@ module Extensions
extend ActiveSupport::Concern
included do
field :seo_title, :type => String
field :meta_keywords, :type => String
field :meta_description, :type => String
localized_field :seo_title
localized_field :meta_keywords
localized_field :meta_description
end
end # Seo

View File

@ -0,0 +1,51 @@
module Extensions
module Site
module I18n
extend ActiveSupport::Concern
included do
## fields ##
field :locales, :type => Array, :default => []
## callbacks ##
after_validation :add_missing_locales_for_all_pages
end
module InstanceMethods
def locales=(array)
array = [] if array.blank?; super(array)
end
def default_locale
self.locales.first || Locomotive.config.site_locales.first
end
protected
def add_missing_locales_for_all_pages
if self.locales_changed?
list = self.pages.to_a
while !list.empty? do
page = list.pop
begin
page.send(:set_slug_and_fullpath_for_all_locales, self.locales)
page.save
rescue TypeError => e
list.insert(0, page)
end
end
end
end
end
end
end
end

View File

@ -1,6 +1,7 @@
class Page
include Locomotive::Mongoid::Document
include Mongoid::I18n
## Extensions ##
include Extensions::Page::Tree
@ -13,10 +14,10 @@ class Page
include Extensions::Shared::Seo
## fields ##
field :title
field :slug
field :fullpath
field :raw_template
localized_field :title
localized_field :slug
localized_field :fullpath
localized_field :raw_template
field :published, :type => Boolean, :default => false
field :cache_strategy, :default => 'none'
@ -32,6 +33,7 @@ class Page
after_initialize :set_default_raw_template
before_validation :normalize_slug
before_save { |p| p.fullpath = p.fullpath(true) }
before_create :set_slug_and_fullpath_for_all_locales
before_destroy :do_not_remove_index_and_404_pages
## validations ##
@ -61,9 +63,13 @@ class Page
self.index? || self.not_found?
end
def fullpath(force = false)
if read_attribute(:fullpath).present? && !force
return read_attribute(:fullpath)
def default_slug
I18n.with_site_locale(I18n.default_site_locale) { self.slug }
end
def fullpath_with_building(force = false)
if self.fullpath_without_building.present? && !force
return self.fullpath_without_building
end
if self.index? || self.not_found?
@ -75,6 +81,18 @@ class Page
end
end
alias_method_chain :fullpath, :building
def fullpath_with_locale(locale)
url, locale = self.fullpath(true), locale.to_s
if locale != self.site.default_locale
url = File.join(locale, url)
end
url
end
def with_cache?
self.cache_strategy != 'none'
end
@ -104,4 +122,14 @@ class Page
self.raw_template ||= I18n.t('attributes.defaults.pages.other.body')
end
def set_slug_and_fullpath_for_all_locales(locales = nil)
(locales || self.site.locales).each do |locale|
I18n.with_site_locale(locale) do
%w(slug fullpath).each do |meth|
self.send(:"#{meth}=", self.send(meth.to_sym))
end
end
end
end
end

View File

@ -1,10 +1,12 @@
class Site
include Locomotive::Mongoid::Document
include Mongoid::I18n
## Extensions ##
extend Extensions::Site::SubdomainDomains
extend Extensions::Site::FirstInstallation
include Extensions::Site::I18n
include Extensions::Shared::Seo
## fields ##

View File

@ -9,6 +9,17 @@
= f.input :meta_keywords
= f.input :meta_description
- if can?(:manage, current_site)
= f.foldable_inputs :name => :locales, :class => 'language locales' do
= f.custom_input :locales, { :css => 'full', :with_label => false } do
#locales
- ordered_site_locales(@site).each do |locale|
%span
= check_box_tag 'site[locales][]', locale, @site.locales.include?(locale), :id => "site-locales-#{locale}"
%label{ :for => "site-locales-#{locale}" }
= image_tag "admin/icons/flags/#{locale}.png"
= t("admin.locales.#{locale}")
- if can?(:point, Site)
- if manage_subdomain_or_domains?
= f.foldable_inputs :name => :access_points, :class => 'editable-list off' do

View File

@ -2,6 +2,9 @@
- content_for :submenu do
= render_cell 'admin/settings_menu', :show
- content_for :actions do
= render_cell 'admin/site_locale_picker', :show, :site => current_site, :locale => I18n.site_locale
- content_for :buttons do
- if can?(:manage, @site)

View File

@ -40,8 +40,7 @@
= f.radio_button :locale, locale
%label{ :for => "my_account_locale_#{locale.downcase}" }
= image_tag "admin/icons/flags/#{locale}.png"
= t(".#{locale}")
/ &nbsp;
= t("admin.locales.#{locale}")
= render 'admin/shared/form_actions', :button_label => :update

View File

@ -1,2 +1,4 @@
- if can? :manage, ContentType
= link_to content_tag(:em) + content_tag(:span, t('admin.content_types.index.new')), new_admin_content_type_url
= link_to content_tag(:em) + content_tag(:span, t('admin.content_types.index.new')), new_admin_content_type_url
= render_cell 'admin/site_locale_picker', :show, :site => current_site, :locale => I18n.site_locale

View File

@ -93,6 +93,7 @@ stylesheets:
- public/stylesheets/admin/formtastic_changes.css
- public/stylesheets/admin/assets.css
- public/stylesheets/admin/sites_picker.css
- public/stylesheets/admin/site_locale_picker.css
- public/stylesheets/admin/application.css
- public/stylesheets/admin/safari.css
- public/stylesheets/admin/guiders-1.1.0.css

View File

@ -1,5 +1,15 @@
de:
admin:
locales:
en: Englisch
de: Deutsch
fr: Französisch
pt-BR: "Bras. Portugisisch"
it: "Italienisch"
nl: "Holländer"
es: "Spanisch"
ru: "Russisch"
buttons:
login: Einloggen
send_password: Senden
@ -157,14 +167,6 @@ de:
edit:
help: "Deinen Namen kannst du durch darauf klicken ändern."
new_site: Neue Webseite
en: Englisch
de: Deutsch
fr: Französisch
pt-BR: "Bras. Portugisisch"
it: "Italienisch"
nl: "Holländer"
es: "Spanisch"
ru: "Russisch"
ask_for_name: "Bitte gib deinen neuen Namen an"
theme_assets:

View File

@ -1,5 +1,15 @@
en:
admin:
locales:
en: English
de: German
fr: French
pt-BR: "Brazilian Portuguese"
it: Italian
nl: Dutch
es: Spanish
ru: Russian
buttons:
login: Log in
send_password: Send
@ -168,14 +178,6 @@ en:
edit:
help: "Your name can be updated by clicking it. To apply your changes, click on the \"Save\" button."
new_site: new site
en: English
de: German
fr: French
pt-BR: "Brazilian Portuguese"
it: Italian
nl: Dutch
es: Spanish
ru: Russian
ask_for_name: "Please type your new name"
theme_assets:

View File

@ -1,5 +1,15 @@
fr:
admin:
locales:
en: en Anglais
de: en Allemand
fr: en Français
pt-BR: "en Portugais"
it: "en Italien"
nl: "en Hollandais"
es: en Espagnol
ru: en Russe
errors:
"500":
title: Erreur applicative
@ -169,15 +179,6 @@ fr:
edit:
help: "Votre nom est modifiable en cliquant dessus. Pour appliquer votre modification, cliquez après sur le bouton \"Modifier\""
new_site: nouveau site
en: en Anglais
de: en Allemand
fr: en Français
pt-BR: "en Portugais"
it: "en Italien"
nl: "en Hollandais"
es: en Espagnol
ru: en Russe
ask_for_name: "Veuillez entrer le nouveau nom"
theme_assets:

View File

@ -10,6 +10,7 @@ en:
raw_template: Template
credentials: Credentials
language: Language
locales: Locales
sites: Sites
access_points: Access points
memberships: Accounts
@ -67,6 +68,7 @@ en:
seo_title: "Define a global value here which should be used as the value for the title tag in the head section."
meta_keywords: "Meta keywords used within the head tag of the page. They are separated by a comma. Required for SEO."
meta_description: "Meta description used within the head tag of the page. Required for SEO."
locales: "Drag&drop a flag to the first position to make it the default one."
domain_name: "ex: locomotiveapp.org"
robots_txt: "Content of the <span class='code'>/robots.txt</span> file. Check the following <a href='http://www.w3.org/TR/html4/appendix/notes.html#h-B.4.1.1'>url</a> for more information."
theme_asset:

View File

@ -9,11 +9,11 @@ defaults: &defaults
development:
<<: *defaults
database: locomotive_dev
database: locomotive_i18n_dev
test:
<<: *defaults
database: locomotive_test
database: locomotive_i18n_test
# set these environment variables on your prod server
production:

View File

@ -1,5 +1,42 @@
BOARD:
- i18n (in progress)
x site locales
x model
x ui
x locale picker
x patches
x i18n
x mongoid-i18n
x rendering engine
x get locale
x render the right version
x contents
x store the site locale value
x pages
x title / slug / fullpath / seo
x template
x editable contents
x custom contents (models)
(- snippets)
x site
x seo
x locale picker
- liquid tags:
x locale switcher
x nav
(- link_to (new)
- others ?) -
- theme_assets
- other problems to solve:
x If you create a new page it shall always be created in the default_language, not depending on the used language in the backend.
- set the page slug for all the site locales
- default site locale even if the i18n feature is disabled (the disable procedure is missing btw)
- redirect for the default site locale if urls like /<default_site_locale>/....
- inline editing
- with_scope is broken
- refactor
BACKLOG:
- custom_fields:

View File

@ -9,6 +9,7 @@ require 'locomotive/dragonfly'
require 'locomotive/liquid'
require 'locomotive/mongoid'
require 'locomotive/carrierwave'
require 'locomotive/i18n'
require 'locomotive/custom_fields'
require 'locomotive/httparty'
require 'locomotive/inherited_resources'

View File

@ -8,6 +8,7 @@ module Locomotive
# :forbidden_paths => %w{layouts snippets stylesheets javascripts assets admin system api},
:reserved_slugs => %w{stylesheets javascripts assets admin images api pages edit},
:locales => %w{en de fr pt-BR it nl es ru},
:site_locales => %w{en de fr pt-BR it nl es ru},
:cookie_key => '_locomotive_session',
:enable_logs => false,
:hosting => :auto,
@ -23,7 +24,7 @@ module Locomotive
:entitystore => URI.encode("file:#{Rails.root}/tmp/dragonfly/cache/body")
},
:devise_modules => [:database_authenticatable, :recoverable, :rememberable, :trackable, :validatable, :encryptable, { :encryptor => :sha1 }],
:context_assign_extensions => { }
:context_assign_extensions => {}
}
cattr_accessor :settings

View File

@ -23,6 +23,13 @@ module CustomFields
module Category
class Item
include Mongoid::I18n
localized_fields :name, :position
field :name
field :position, :type => Integer, :default => 0
def to_liquid
{ 'id' => self._id.to_s, 'name' => self.name }
end

View File

@ -5,6 +5,7 @@ require 'json/pure'
require 'devise'
require 'mongoid'
require 'mongoid_acts_as_tree'
require 'mongoid/i18n'
require 'will_paginate'
require 'haml'
require 'liquid'

213
lib/locomotive/i18n.rb Normal file
View File

@ -0,0 +1,213 @@
# require 'ruby-debug'
## patches for the i18n support (aka multi languages support)
require 'i18n'
module I18n
class Config
def site_locale
@site_locale ||= default_site_locale
end
def site_locale=(site_locale)
@site_locale = site_locale.to_sym rescue nil
end
def default_site_locale
@@default_site_locale ||= :en
end
def default_site_locale=(site_locale)
@@default_site_locale = site_locale.to_sym rescue nil
end
end
class << self
# Write methods which delegates to the configuration object
%w(site_locale default_site_locale).each do |method|
module_eval <<-DELEGATORS, __FILE__, __LINE__ + 1
def #{method}
config.#{method}
end
def #{method}=(value)
config.#{method} = (value)
end
DELEGATORS
end
# Executes block with given I18n.site_locale set.
def with_site_locale(tmp_locale = nil)
if tmp_locale
current_locale = self.site_locale
self.site_locale = tmp_locale
end
yield
ensure
self.site_locale = current_locale if tmp_locale
end
end
end
## CARRIERWAVE ##
require 'carrierwave/orm/mongoid'
module CarrierWave
module Mongoid
def mount_uploader_with_localization(column, uploader=nil, options={}, &block)
mount_uploader_without_localization(column, uploader, options, &block)
define_method(:read_uploader) { |name| self.send(name.to_sym) }
define_method(:write_uploader) { |name, value| self.send(:"#{name.to_sym}=", value) }
end
alias_method_chain :mount_uploader, :localization
end
end
## MONGOID-I18n ##
# TODO: fork https://github.com/Papipo/mongoid_i18n
module Mongoid
module I18n
class LocalizedField < Hash
attr_accessor :type
def initialize(type = nil)
self.type = type || Object
super nil
end
def [](key)
self.type.set(super(key))
end
def []=(key, value)
super(key, self.type.set(value))
end
def merge(other_hash, &block)
# puts "merging #{other_hash.inspect}"
converted_hash = {}
other_hash.each { |k, v| converted_hash[k] = self.type.set(v) }
# puts "converted_hash = #{converted_hash.inspect}"
super(converted_hash, &block)
end
end
end
end
# DIRTY MONKEY PATCHING BEFORE THE REFACTORING
module Mongoid
module Criterion
class Selector< Hash
def []=(key, value)
key = "#{key}.#{::I18n.site_locale}" if fields[key.to_s].try(:type) == Mongoid::I18n::LocalizedField
super
end
end
end
module I18n
included do
cattr_accessor :localized_fields_list
end
module ClassMethods
def localized_fields(*args)
self.localized_fields_list = [*args].collect
end
def field(name, options = {})
if localized_field?(name)
options.merge!(:type => LocalizedField, :default => LocalizedField.new(options[:type]))
end
super
end
protected
def localized_field?(name)
(self.localized_fields_list || []).any? do |rule|
case rule
when String, Symbol then name.to_s == rule.to_s
when Regexp then !(name.to_s =~ rule).nil?
else
false
end
end.tap do |result|
# options[:type] = LocalizedField if result
# puts "#{name.inspect}... localized ? #{result.inspect}"
end
end
def create_accessors(name, meth, options = {})
if options[:type] == LocalizedField
if options[:use_default_if_empty] != false # either nil or true
define_method(meth) do
value = read_attribute(name)
if value.is_a?(Hash)
converted_value = options[:default].merge(value)
value = converted_value[::I18n.site_locale.to_s]
value.to_s.empty? ? converted_value[::I18n.default_site_locale.to_s] : value
else
value
end
end
else
define_method(meth) do
value = read_attribute(name)
if value.is_a?(Hash)
converted_value = options[:default].merge(value)
options[:default].merge(value)[::I18n.site_locale.to_s]
else
value
end
end
end
define_method("#{meth}=") do |value|
if !@attributes[name].nil? && !@attributes[name].is_a?(Hash)
# existing value but not localized yet
old_value = @attributes[name]
@attributes[name] = options[:default].merge(::I18n.default_site_locale.to_s => old_value)
end
@attributes[name] ||= options[:default]
@attributes[name] = options[:default].merge(@attributes[name]) unless @attributes[name].is_a?(LocalizedField)
value = if value.is_a?(Hash)
@attributes[name].merge(value)
else
@attributes[name].merge(::I18n.site_locale.to_s => value)
end
value = value.delete_if { |key, value| value.to_s.empty? } if options[:clear_empty_values] != false
write_attribute(name, value)
end
define_method("#{meth}_translations") { read_attribute(name) }
if options[:clear_empty_values] != false
define_method("#{meth}_translations=") { |value| write_attribute(name, value.delete_if { |key, value| value.blank? }) }
else
define_method("#{meth}_translations=") { |value| write_attribute(name, value) }
end
else
super
end
end
end
end
end

View File

@ -21,6 +21,10 @@ module Locomotive
self._source.highlighted_field_value
end
def _permalink
self._source._permalink
end
end
end
end

View File

@ -0,0 +1,76 @@
module Locomotive
module Liquid
module Tags
# Display the links to change the locale of the current page
#
# Usage:
#
# {% locale_switcher %} => <div id="locale-switcher"><a href="/features" class="current en">Features</a><a href="/fr/fonctionnalites" class="fr">Fonctionnalités</a></div>
#
# {% locale_switcher label: locale, sep: ' - ' }
#
# options:
# - label: iso (de, fr, en, ...etc), locale (Deutsch, Français, English, ...etc), title (page title)
# - sep: piece of html code seperating 2 locales
#
# notes:
# - "iso" is the default choice for label
# - " | " is the default seperating code
#
class LocaleSwitcher < ::Liquid::Tag
Syntax = /(#{::Liquid::Expression}+)?/
def initialize(tag_name, markup, tokens, context)
@options = { :label => 'iso', :sep => ' | ' }
if markup =~ Syntax
markup.scan(::Liquid::TagAttributes) { |key, value| @options[key.to_sym] = value.gsub(/"|'/, '') }
@options[:exclude] = Regexp.new(@options[:exclude]) if @options[:exclude]
else
raise ::Liquid::SyntaxError.new("Syntax Error in 'locale_switcher' - Valid syntax: locale_switcher <options>")
end
super
end
def render(context)
site = context.registers[:site]
current_page = context.registers[:page]
output = %(<div id="locale-switcher">)
output += site.locales.collect do |locale|
I18n.with_site_locale(locale) do
url = current_page.fullpath_with_locale(locale)
if current_page.templatized?
url.gsub!('content_type_template', context['content_instance']._permalink)
end
%(<a href="/#{url}" class="#{locale} #{'current' if locale == context['current_locale']}">#{link_label(current_page)}</a>)
end
end.join(@options[:sep])
output += %(</div>)
end
private
def link_label(current_page)
case @options[:label]
when :iso then I18n.site_locale
when :locale then I18n.t("admin.locales.#{I18n.site_locale}", :locale => I18n.site_locale)
when :title then current_page.title
else
I18n.site_locale
end
end
end
::Liquid::Template.register_tag('locale_switcher', LocaleSwitcher)
end
end
end

View File

@ -56,14 +56,15 @@ module Locomotive
# Determines root node for the list
def fetch_entries(context)
@current_site = context.registers[:site]
@current_page = context.registers[:page]
children = (case @source
when 'site' then context.registers[:site].pages.root.minimal_attributes.first # start from home page
when 'site' then @current_site.pages.root.minimal_attributes.first # start from home page
when 'parent' then @current_page.parent || @current_page
when 'page' then @current_page
else
context.registers[:site].pages.fullpath(@source).minimal_attributes.first
@current_site.pages.fullpath(@source).minimal_attributes.first
end).children_with_minimal_attributes.to_a
children.delete_if { |p| !include_page?(p) }
@ -73,11 +74,13 @@ module Locomotive
def render_entry_link(page, css, depth)
selected = @current_page.fullpath =~ /^#{page.fullpath}/ ? ' on' : ''
page.site = @current_site
icon = @options[:icon] ? '<span></span>' : ''
label = %{#{icon if @options[:icon] != 'after' }#{page.title}#{icon if @options[:icon] == 'after' }}
output = %{<li id="#{page.slug.dasherize}-link" class="link#{selected} #{css}">}
output << %{<a href="/#{page.fullpath}">#{label}</a>}
output = %{<li id="#{page.default_slug.dasherize}-link" class="link#{selected} #{css}">}
output << %{<a href="/#{page.fullpath_with_locale(I18n.site_locale)}">#{label}</a>}
output << render_entry_children(page, depth.succ) if (depth.succ <= @options[:depth].to_i)
output << %{</li>}
@ -90,7 +93,7 @@ module Locomotive
children = page.children_with_minimal_attributes.reject { |c| !include_page?(c) }
if children.present?
output = %{<ul id="#{@options[:id]}-#{page.slug.dasherize}">}
output = %{<ul id="#{@options[:id]}-#{page.default_slug.dasherize}">}
children.each do |c, page|
css = []
css << 'first' if children.first == c
@ -115,8 +118,9 @@ module Locomotive
end
end
::Liquid::Template.register_tag('nav', Nav)
end
::Liquid::Template.register_tag('nav', Nav)
end
end
end

View File

@ -42,7 +42,7 @@ module Locomotive
page_count, current_page = pagination['total_pages'], pagination['current_page']
path = context.registers[:page].fullpath
path = context.registers[:page].fullpath_with_locale(I18n.site_locale)
pagination['previous'] = link(I18n.t('pagination.previous'), current_page - 1, path) if pagination['previous_page']
pagination['next'] = link(I18n.t('pagination.next'), current_page + 1, path) if pagination['next_page']

View File

@ -17,7 +17,9 @@ module Locomotive
render_no_page_error and return if @page.nil?
output = @page.render(locomotive_context)
output = I18n.with_locale(I18n.site_locale) do
@page.render(locomotive_context)
end
self.prepare_and_set_response(output)
end
@ -33,6 +35,12 @@ module Locomotive
path.gsub!(/\.[a-zA-Z][a-zA-Z0-9]{2,}$/, '') # remove the page extension
path.gsub!(/^\//, '') # remove the leading slash
# extract the site locale
if path =~ /^(#{current_site.locales.join('|')})+(\/|$)/
I18n.site_locale = $1
path.gsub!($1 + $2, '')
end
path = 'index' if path.blank?
if path != 'index'
@ -67,7 +75,10 @@ module Locomotive
'params' => self.params,
'url' => request.url,
'now' => Time.now.utc,
'today' => Date.today
'today' => Date.today,
'default_locale' => I18n.default_site_locale.to_s,
'current_locale' => I18n.site_locale.to_s,
'locales' => current_site.locales
}
assigns.merge!(Locomotive.config.context_assign_extensions)
@ -87,7 +98,7 @@ module Locomotive
:current_admin => current_admin
}
::Liquid::Context.new({}, assigns, registers)
::Liquid::Context.new({}, assigns, registers, true)
end
def prepare_and_set_response(output)

View File

@ -29,6 +29,27 @@ namespace :locomotive do
end
end
desc 'Prepare for i18n'
task :prepare_for_i18n => :environment do
Site.all.each do |site|
if site.locales.empty?
site.locales = [Locomotive.config.site_locales.first]
site.save
end
end
Page.skip_callback(:validate, :before)
Page.skip_callback(:save, :after)
I18n.with_site_locale(ENV['LOCALE'] || Locomotive.config.site_locales.first) do
Page.all.each do |page|
page.template_dependencies = page.template_dependencies
page.snippet_dependencies = page.snippet_dependencies
page.save(:validate => false)
end
end
end
desc 'Import a remote template described by its URL -- 2 options: SITE=name or id, RESET=by default false'
task :import => :environment do
url, site_name_or_id, reset = ENV['URL'], ENV['SITE'], Boolean.set(ENV['RESET']) || false

View File

@ -22,6 +22,7 @@ Gem::Specification.new do |s|
s.add_dependency 'devise', '1.3.4'
s.add_dependency 'devise_bushido_authenticatable', '1.0.0.alpha10'
s.add_dependency 'mongoid', '2.0.2'
s.add_dependency 'mongoid_i18n'
s.add_dependency 'bson_ext', '~> 1.3.0'
s.add_dependency 'locomotive_mongoid_acts_as_tree', '0.1.5.7'
s.add_dependency 'will_paginate', '~> 3.0.0'

View File

@ -165,4 +165,11 @@ $(document).ready(function() {
.trigger('refresh');
$('.formtastic fieldset.inputs ol li:not(.item)').last().addClass('last');
// site locale picker
$('#site-locale-picker').hover(function() { $(this).addClass('open'); }, function() { $(this).removeClass('open'); })
.find('li:gt(0)').click(function(e) {
var locale = $(this).find('.text').html();
addParameterToURL('site_locale', locale);
});
});

View File

@ -70,5 +70,6 @@ $(document).ready(function() {
});
}).hide();
// locales
$('#locales').sortable({ items: 'span', handle: 'label', tolerance: 'pointer', appendTo: 'body' });
});

View File

@ -13,6 +13,27 @@ function makeSlug(val, sep) { // code largely inspired by http://www.thewebsitet
return val.toLowerCase();
}
function addParameterToURL(key, value) { // code from http://stackoverflow.com/questions/486896/adding-a-parameter-to-the-url-with-javascript
key = encodeURIComponent(key); value = encodeURIComponent(value);
var kvp = document.location.search.substr(1).split('&');
var i = kvp.length; var x; while(i--) {
x = kvp[i].split('=');
if (x[0] == key) {
x[1] = value;
kvp[i] = x.join('=');
break;
}
}
if (i < 0) { kvp[kvp.length] = [key,value].join('='); }
//this will reload the page, it's likely better to store this until finished
document.location.search = kvp.join('&');
}
(function() {
String.prototype.trim = function() {
return this.replace(/^\s+/g, '').replace(/\s+$/g, '');

View File

@ -508,7 +508,7 @@ form.formtastic fieldset ol li.has-many ul li.template span.actions button span
font-size: 0.8em;
}
/* ___ my account ___ */
/* ___ language / locales ___ */
form.formtastic fieldset.language li.full span {
display: inline-block;
@ -535,6 +535,14 @@ form.formtastic fieldset.language li.full span input {
margin-right: 2px;
}
form.formtastic fieldset.locales li.full span:first-child label {
color: #000;
}
form.formtastic fieldset.locales p.inline-hints {
margin-top: 15px;
}
/* ___ membership ___ */
form.formtastic fieldset.email li.full input {

View File

@ -107,7 +107,8 @@
background: transparent url(/images/admin/menu/submenu/action-border.png) repeat-y left 0; }
#submenu > .action a {
margin-top: 18px;
display: inline-block;
display: block;
float: left;
background: rgba(0, 0, 0, 0.4);
border-radius: 16px;
-moz-border-radius: 16px;

View File

@ -0,0 +1,62 @@
/* ___ AUTOMATICALLY GENERATED: see admin/site_locale_picker.scss for the source file */
/* ___ rounded ___ */
/* ___ box shadow ___ */
/* ___ others ___ */
#site-locale-picker {
position: relative;
float: right;
margin: 18px 0 0 10px;
padding: 0px 25px 0 10px;
border: 1px solid transparent;
background: rgba(0, 0, 0, 0.4);
border-radius: 16px;
-moz-border-radius: 16px;
-webkit-border-radius: 16px; }
#site-locale-picker:hover {
border-color: black;
border-radius: 12px;
-moz-border-radius: 12px;
-webkit-border-radius: 12px; }
#site-locale-picker .hand {
position: absolute;
top: 7px;
right: 7px;
height: 7px;
width: 12px;
background: transparent url(/images/admin/menu/icons.png) no-repeat 0px -16px; }
#site-locale-picker ul {
list-style: none;
margin: 0px;
padding: 0px; }
#site-locale-picker ul li {
position: relative;
display: none;
margin: 5px 0;
height: 22px;
line-height: 20px;
cursor: pointer; }
#site-locale-picker ul li img.flag {
position: relative;
top: 1px;
width: 20px;
height: 20px; }
#site-locale-picker ul li span.text {
position: relative;
position: relative;
top: -6px;
color: #fff;
font-size: 0.7em;
text-shadow: 1px 1px 1px #000;
text-transform: uppercase; }
#site-locale-picker ul li:first-child {
display: block;
margin: 0px;
cursor: default; }
#site-locale-picker ul li:last-child {
margin-bottom: 0px; }
#site-locale-picker ul li:hover span.text {
text-decoration: underline; }
#site-locale-picker.open ul li {
display: block; }
#site-locale-picker.open ul li:first-child {
margin-bottom: 5px; }

View File

@ -112,7 +112,8 @@
a {
margin-top: 18px;
display: inline-block;
display: block;
float: left;
background: rgba(0, 0, 0, 0.4);
@include full-rounded(16px);
padding: 0px 10px 0 15px;

View File

@ -0,0 +1,105 @@
/* ___ AUTOMATICALLY GENERATED: see admin/site_locale_picker.scss for the source file */
@import "helpers";
#site-locale-picker {
position: relative;
float: right;
margin: 18px 0 0 10px;
padding: 0px 25px 0 10px;
border: 1px solid transparent;
background: rgba(0, 0, 0, 0.4);
@include full-rounded(16px);
&:hover {
border-color: rgba(0, 0, 0, 1);
@include full-rounded(12px);
}
.hand {
position: absolute;
top: 7px;
right: 7px;
height: 7px;
width: 12px;
background: transparent url(/images/admin/menu/icons.png) no-repeat 0px -16px;
}
ul {
list-style: none;
margin: 0px;
padding: 0px;
li {
position: relative;
display: none;
margin: 5px 0;
height: 22px;
line-height: 20px;
cursor: pointer;
img.flag {
position: relative;
top: 1px;
width: 20px;
height: 20px;
}
span.text {
position: relative;
position: relative;
top: -6px;
color: #fff;
font-size: 0.7em;
text-shadow: 1px 1px 1px #000;
text-transform: uppercase;
}
&:first-child {
display: block;
margin: 0px;
cursor: default;
}
&:last-child {
margin-bottom: 0px;
}
&:hover {
span.text {
text-decoration: underline;
}
}
}
}
&.open {
ul {
li {
display: block;
&:first-child {
margin-bottom: 5px;
}
}
}
}
}