Reputation: 398
Rails 4.2 application.
In the footer of the page I have such code:
footer.footer
.container
= form_tag set_locale_path, method: :post, id: 'set_locale' do
= select_tag "locale", options_for_select(I18n.available_locales, I18n.locale.to_s), onchange: ("$('#set_locale').submit();")
And Locales controller with set_locale
action.
class LocalesController < ApplicationController
skip_before_filter :authenticate_user!
def set_locale
I18n.locale = params[:locale]
# redirect_to request.referrer
end
end
I have access to previous url via request.referrer
. That looks like: http://localhost:3000/en/users
.
Is there any way to change locale without gsub
ing referrer string? Because if not - application will switch locale back to :en
from referrer url.
Upvotes: 3
Views: 1680
Reputation: 282
A better solution is to use the params available to build a new path
url_for(
controller: params["controller"],
action: params["action"],
locale: locale,
only_path: true
)
Used in a loop like
<% I18n.available_locales.each do | locale | %>
<%= link_to
t("locale_#{locale}"),
url_for(controller: params["controller"],
action: params["action"],
locale: locale,
only_path: true)
%>
<% end %>
Upvotes: 1
Reputation: 23
The answer I wrote above is not the best way to do this I have a newer way that I ended up using which I think solves multiple issues. Depending on how much you really want to translate your app.
In my cause I want to make sure I have the content translated as well as the urls translated which makes it friendlier for users and for marketing purposes if you are for example doing a landing pages or something.
For this to work you need to add the "route_translator" gem
Inside the Application controller add as a private method (this need to be called in a before action.)
def set_current_url_and_params
set_params = params.except("authenticity_token", "controller","action")
@path = ""
@current_page_params = set_params.to_unsafe_h
Rails.application.routes.router.recognize(request) do |route, matches, param|
@path = route.name
break
end
end
what this does is sets up the @path variable which has the path that you are currently on. This will be used inside the view for example were your menu is so you can switch languages.
like this:
=button_to "ES", set_location_path(locale:'es', current_path:@path, current_page_params:@current_page_params), class: "btn btn-primary-soft btn-block mb-1"
=button_to "EN", set_location_path(locale:'en', current_path:@path, current_page_params:@current_page_params), class: "btn btn-primary-soft btn-block mb-1"
This button send a post request to an end point that sets your "locale". It will also send the current_path which we set in the application controller.
In your routes file you will use the gem i mention above.
localized do
get "register"=> "users#new", :as=> :new_user
end
that gem will make some new paths for you depending on the locales/languages u want to use. So in this example it creates
new_user_en
new_user_es
new_user_pt
So now lets say you are on the new_user_en_path
the block set this up so now it knows where you are. It knows the named route it will give you back a string "new_user_en"
Rails.application.routes.router.recognize(request) do |route, matches, param|
@path = route.name
break
end
When sending a request to change the locale you are also sending it with the @path(with the string value) you need to grab that and process that. I used the same service and method from my other answer but with some changes use this one its better.
class LanguageUrlHelper
#you need to include url helpers in service for this to work
class << self
include Rails.application.routes.url_helpers
end
def self.get_url(request, params)
prefix_path = params[:current_path]
locale = "_"+ params[:locale]
if prefix_path.include?("_es")
new_path = prefix_path.gsub("_es", locale)
elsif prefix_path.include?("_en")
new_path = prefix_path.gsub("_en", locale)
elsif prefix_path.include?("_pt")
new_path = prefix_path.gsub("_pt", locale)
else
new_path = prefix_path + locale
end
url = eval("#{new_path}_path(params.to_unsafe_h)")
end
end
In the change locale action controller add
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
http_accept_language.compatible_language_from(I18n.available_locales)
session[:currency] = params[:currency]
flash[:success] = t("application_controller.set_location.success")
redirect_to LanguageUrlHelper.get_url(request, params)
end
What the get_url does is it swaps out the last bit of the url_helper path that contains the locale and changes it to the new one you want.
So for example it grabs new_user_en and changes it to new_user_es if I wanted spanish.
I then interpolate it and then eval the method which gives me the actual URL. with this line
url = eval("#{new_path}_path(params.to_unsafe_h)")
you will also want to make sure that you send the current pages params if there are any so I also store the current params inside a variable and is passed in the button as well which gets passes into this service.
Also this is important for this to work the @path need to be set from the very beginning and adding the "_en" etc needs to be a real path. so you will need to make sure the root path also uses the gem so you have to add (use your own obviously) Which give you root_en_path etc etc
root :to => 'landing_pages#buyers'
localized do
root :to => 'landing_pages#buyers'
end
So doing all this
if you are for example on
domain.com/en/new_user and want to change it to spanish it now changes it to domain.com/es/nuevo_usario and redirect to that page which happens to be the translated page that u were already on while translating the URL as well.
This is much better than using regex or gsubing urls which seems more brittle to me. I hope this was helpful to anyone out there that has been trying to mess with translations. I couldn't find anything out there that dealt with this and spent a bunch of time trying to figure it out :)
Upvotes: 0
Reputation: 23
So I have been dealing with this myself. I dont want to remove the language from being part of the URL cause it does affect SEO if you are going after different languages. So I also have the language/locale as part of the url so its looks like www.website.com/en/more_url/even_more_url etc etc
I haven't found an answer anywhere else so this is my solution. I made a service that creates the url swapping out the current language or setting it if there is none picked and is not part of the URL for example when its "/"
I just grab the end part of the URL and look for the language and swap them out depending on its length and then build the URL as a string then gets returned. and used in the controller.
It works as intended the only weird edge case that I dont know how to fix though is that... lets say you are on a page that has a form. You submit the form with validation errors. Rails grabs the post url and changes the browser url to the post URL which is a rails behavior. If you then try to change the language if the browser url is on the form URL then you will be making a get request with a new language on a post URL. So it will result in an error. I thought maybe you can add an if statement saying only run this code if request.get BUT then that means when changing the language u cant use a post request to set the language so it doesnt work.
So you gotta hope the user doesnt submit a rails form with errors and then decide to change the language LOL (which seems like weird behavior anyways)
Anyways I hope this helps anyone out there that has been dealing with this :)
class LanguageUrlHelper
def self.get_url(request, locale_param)
uri= URI(request.referer).path
long_local_url = "/" + locale_param+ "/"
short_local_url = "/" + locale_param
if uri.length > 3
if uri.include?("/es/")
new_uri = uri.gsub("/es/", long_local_url)
elsif uri.include?("/en/")
new_uri = uri.gsub("/en/", long_local_url)
elsif uri.include?("/pt/")
new_uri = uri.gsub("/pt/", long_local_url)
end
elsif uri.length <= 3
if uri == "/"
new_uri = uri.gsub("/", short_local_url)
elsif uri == "/en"
new_uri = uri.gsub("/en", short_local_url)
elsif uri == "/es"
new_uri = uri.gsub("/es", short_local_url)
elsif uri == "/pt"
new_uri = uri.gsub("/pt", short_local_url)
end
end
new_url = request.base_url + new_uri
return new_url
end
end
In your controller add
redirect_to LanguageUrlHelper.get_url(request, params[:locale])
Upvotes: 0
Reputation: 141
I can't see an easy solution without gsubing the string. If the locale would be set with a get parameter instead of being part of the route it would be easier.
Example
http://localhost:3000/users?locale=en // Sets the locale to english
In that case the url would stay the same but the locale would change.
This is how i do it in my projects:
before_action :set_locale
def set_locale
I18n.locale = locale || I18n.default_locale
end
def locale
return params[:locale] if ["sv", "en"].include? params[:locale]
end
Upvotes: 0