How to detect language from URL in Sinatra - url

I have a multi-language website and I'm puting the language in the URL like domain.com/en/. When the user doesn't put the language in the URL I want to redirect him to the page in the main language like "domain.com/posts" to "domain.com/en/posts". Is there an easy way to do this with Sinatra?
I have more than one hundred routes. So doing this for every route is not a very good option.
get "/:locale/posts" do... end
get "/posts" do... end
Can someone help me?
Thanks

Use a before filter, somewhat like this:
set :locales, %w[en sv de]
set :default_locale, 'en'
set :locale_pattern, /^\/?(#{Regexp.union(settings.locals)})(\/.+)$/
helpers do
def locale
#locale || settings.default_locale
end
end
before do
#locale, request.path_info = $1, $2 if request.path_info =~ settings.locale_pattern
end
get '/example' do
case locale
when 'en' then 'Hello my friend!'
when 'de' then 'Hallo mein Freund!'
when 'sv' then 'Hallå min vän!'
else '???'
end
end
With the upcoming release of Sinatra, you will be able to do this:
before('/:locale/*') { #locale = params[:locale] }

Related

Rails: using gsub to find and make links

I've got a text area called body in a model. I'd like to change every image to a link to that image. I've got this method which is supposed to do that but it doesn't seem to work. It breaks at each image.
def get_images_in(body)
body_with_links = body.gsub( %r{http://[^\s<]+} ) do |url|
if url[/(?:png|jpe?g|gif|svg)$/]
"<a href='#{url}' target='_blank'><img src='#{url}' /></a>"
end
end
return body_with_links
end
Any ideas? Thank-you!
UPDATE
Here's a link to a gist with sample body text.
First things first, you don't need to use a return statement in ruby. Ruby will return the last thing by default. In your case, this is the string returned from the gsub:
def wrap_image_with_link(body)
body.gsub( %r{http://[^\s<]+} ) do |url|
if url[/(?:png|jpe?g|gif|svg)$/]
"<a href='#{url}' target='_blank'><img src='#{url}' /></a>"
end
end
end
This still isn't quite right. I would then focus my initial regular expression on the img tag:
def wrap_image_with_link(body)
body.gsub(/\<img.*src\=['"](.*)['"].*\/\>/) do |url|
"<a href='#{url}' target='_blank'><img src='#{url}' /></a>"
end
end
Rails has a couple helpers to clean this up.
def wrap_images_with_links(body)
body.gsub(/\<img.*src\=['"](.*)['"].*\/\>/) do
link_to(image_tag($1), $1, :target => '_blank')
end
end
You probably want to make the reg ex case insensitive:
def wrap_images_with_links(body)
body.gsub(/.*\<img.*src\=['"](.*)['"].*\/\>/i) do
link_to(image_tag($1), $1, :target => '_blank')
end
end
So gsub, as opposed to sub, changes every instance of matched reg-ex, so need to tweak the reg ex slightly to accommodate more explicit matches:
def wrap_images_with_links(body)
body.gsub(/\<img[\s\w"=]+src\=['"](http[s]?:\/\/[\w.\/]+)['"]\s?[\/]?\>/i) do
link_to(image_tag($1), $1, :target => '_blank')
end
end
Depending on the complexities of your urls, you might need to tweak the reg-ex to support ? or characters other than white space, but this work pretty well.

Rails I18n put span around translated text if fallback was used

I'm using Rails built in I18n (Simple backend). I've set the default locale to :en and enabled fallbacks. Let's say I have translations for a specific item in English and Spanish. Now a German visitor comes to my site and it falls back to English. How would I go about detecting that fallback and wrapping it in a span?
<span class="fallback">Hello</span> instead of just Hello
This way I could then use client side machine translations.
I'm hoping to avoid writing my own backend to replace "Simple".
Had to resort to over-riding the translate function in I18n::Backend::FallBacks
https://github.com/svenfuchs/i18n/blob/master/lib/i18n/backend/fallbacks.rb
module I18n
module Backend
module Fallbacks
def translate(locale, key, options = {})
return super if options[:fallback]
default = extract_non_symbol_default!(options) if options[:default]
options[:fallback] = true
I18n.fallbacks[locale].each do |fallback|
catch(:exception) do
result = super(fallback, key, options)
if locale != fallback
return "<span class=\"translation_fallback\">#{result}</span>".html_safe unless result.nil?
else
return result unless result.nil?
end
end
end
options.delete(:fallback)
return super(locale, nil, options.merge(:default => default)) if default
throw(:exception, I18n::MissingTranslation.new(locale, key, options))
end
end
end
end
I just put this code in an initializer.
It feels very messy to me... I would still love to mark someone else's better answer as correct.
A better solution, using the metadata module from I18n. Also logs in a private log file to help spot missing translations. You can replace the calls with Rails.logger or remove them.
I18n::Backend::Simple.include(I18n::Backend::Metadata)
# This work with <%= t %>,but not with <%= I18n.t %>
module ActionView
module Helpers
module TranslationHelper
alias_method :translate_basic, :translate
mattr_accessor :i18n_logger
def translate(key, options = {})
#i18n_logger ||= Logger.new("#{Rails.root}/log/I18n.log")
#i18n_logger.info "Translate key '#{key}' with options #{options.inspect}"
options.merge!(:rescue_format => :html) unless options.key?(:rescue_format)
options.merge!(:locale => I18n.locale) unless options.key?(:locale)
reqested_locale = options[:locale].to_sym
s = translate_basic(key, options)
if s.translation_metadata[:locale] != reqested_locale &&
options[:rescue_format] == :html && Rails.env.development?
#i18n_logger.error "* Translate missing for key '#{key}' with options #{options.inspect}"
missing_key = I18n.normalize_keys(reqested_locale, key, options[:scope])
#i18n_logger.error "* Add key #{missing_key.join(".")}\n"
%(<span class="translation_fallback" title="translation fallback #{reqested_locale}->#{s.translation_metadata[:locale]} for '#{key}'">#{s}</span>).html_safe
else
s
end
end
alias :t :translate
end
end
end
Then style with CSS
.translation_fallback {
background-color: yellow;
}
.translation_missing {
background-color: red;
}

Is there a way to know if a page request came from the same application?

In Rails3, is there a way to check if the page I'm rendering now was requested from the same application, without the use of the hardcoded domain name?
I currently have:
def back_link(car_id = '')
# Check if search exists
uri_obj = URI.parse(controller.request.env["HTTP_REFERER"]) if controller.request.env["HTTP_REFERER"].present?
if uri_obj.present? && ["my_domain.com", "localhost"].include?(uri_obj.host) && uri_obj.query.present? && uri_obj.query.include?('search')
link_to '◀ '.html_safe + t('back_to_search'), url_for(:back) + (car_id.present? ? '#' + car_id.to_s : ''), :class => 'button grey back'
end
end
But this doesn't check for the "www." in front of the domain and all other possible situations.
It would also be nice if I could find out the specific controller and action that were used in the previous page (the referrer).
I think you're looking at this the wrong way.
If you look around the web, find a site with a search feature, and follow the link you'll see a param showing what was searched for.
That's a good way to do it.
Doing it by HTTP_REFERER seems a bit fragile, and won't work, for example, from a bookmark, or posted link.
eg.
/cars/12?from_search=sports+cars
then you can just look up the params[:from_search]
If you really need to do it by HTTP_REFERER then you probably dont have to worry about subdomains. Just;
def http_referer_uri
request.env["HTTP_REFERER"] && URI.parse(request.env["HTTP_REFERER"])
end
def refered_from_our_site?
if uri = http_referer_uri
uri.host == request.host
end
end
def refered_from_a_search?
if refered_from_our_site?
http_referer_uri.try(:query)['search']
end
end
Try something like this:
ref = URI.parse(controller.request.env["HTTP_REFERER"])
if ref.host == ENV["HOSTNAME"]
# do something
To try and get the controller/action from the referring page:
ActionController::Routing::Routes.recognize_path(url.path)
#=> {:controller => "foo", :action => "bar"}
Create an internal_request? method utilizing request.referrer.
Compare the host and port of the request.referrer with your Application's host and port.
require 'uri' # Might be necesseary.
def internal_request?
return false if request.referrer.blank?
referrer = URI.parse( request.referrer )
application_host = Rails.application.config.action_mailer.default_url_options[ :host ]
application_port = Rails.application.config.action_mailer.default_url_options[ :port ]
return true if referrer.host == application_host && referrer.port == application_port
false
end
And then call it like this where you need it, most likely in application_controller.rb:
if internal_request?
do_something
end
Some caveats:
This might need to be modified if you're using subdomains. Easy, though.
This will require you to be setting your host and port for ActionMailer in your configuration, which is common.
You might want to make it the reverse, like external_request? since you're likely handling those situations uniquely. This would allow you to do something like this:
do_something_unique if external_request?

find untranslated locales in rails

I'm using rails 2.3.5 with i18n. I's there a way to find all not yet translated locales in all views?
Maybe a after_filter in the application controller, but which code I can use for this job?
thanks
When using the i18n gem (which Rails does), you can specify your own exception handler. Try this code:
# A simple exception handler that behaves like the default exception handler
# but additionally logs missing translations to a given log.
#
module I18n
class << self
def missing_translations_logger
##missing_translations_logger ||= Logger.new("#{RAILS_ROOT}/log/missing_translations.log")
end
def missing_translations_log_handler(exception, locale, key, options)
if MissingTranslationData === exception # use MissingTranslation in Rails 3.x !!!
puts "logging #{exception.message}"
missing_translations_logger.warn(exception.message)
return exception.message
else
raise exception
end
end
end
end
I18n.exception_handler = :missing_translations_log_handler
(put it for example into RAILS_ROOT/config/initializers/i18n.rb)
Now, whenever you try to translate a key for which you have no translation specified, a warning gets printed into RAILS_ROOT/log/missing_translations.log.
Hope this helps!
I couldn't find a simple trick to do this, so I did this. First implement a 'before_filter' in your application_controller.rb
before_filter :set_user_language
# set the language, 'zen' is a special URL parameter that makes localizations the use the 't' method visible
def set_user_language
# turn on 'zen' to see localization by adding 'zen=true' to query string, will stay on until a query with 'zen=false'
session[:zen] = (session[:zen] || params[:zen] == "true") && params[:zen] != "false"
I18n.locale = 'en'
end
The above finds 'zen=true' and 'zen=false' in the query string. Then add this method to your application_helper.rb:
def t(*args)
result = super(*args)
result = "[#{result}]" if session[:zen] && result.is_a?(String)
result
end
With this method 'zen=true' makes the 't' method display localized strings in square brackets []. To turn it off enter a query string with 'zen=false'.

i18n redirection breaks my tests

I have a big application covered by more than a thousand tests via rspec.
We just made the choice to redirect any page like :
/
/foo
/foo/4/bar/34
...
TO :
/en
/en/foo
/fr/foo/4/bar/34
....
So I made a before filter in application.rb like so :
if params[:locale].blank?
headers["Status"] = "301 Moved Permanently"
redirect_to request.env['REQUEST_URI'].sub!(%r(^(http.?://[^/]*)?(.*))) { "#{$1}/#{I18n.locale}#{$2}" }
end
It's working great but ... It's breaking a lot of my tests, ex :
it "should return 404" do
Video.should_receive(:failed_encodings).and_return([])
get :last_failed_encoding
response.status.should == "404 Not Found"
end
To fix this test, I should do :
get :last_failed_encoding, :locale => "en"
But ... seriously I don't want to fix all my test one by one ...
I tried to make the locale a default parameter like this :
class ActionController::TestCase
alias_method(:old_get, :get) unless method_defined?(:old_get)
def get(path, parameters = {}, headers = nil)
parameters.merge({:locale => "fr"}) if parameters[:locale].blank?
old_get(path, parameters, headers)
end
end
... but couldnt make this work ...
Any idea ??
Why don't define this locale if not before ?
In your applicationController :
params[:locale] = i18n.locale if params[:locale].blank?
After you application has the local define and futur link could be good.

Resources