I have a website with 2 languages. I want to create a subdomain for each locale. For example:
en.site.com and fr.site.com.
I've googled, but no luck. I've only found solutions that extract locale name from query, for example: site.com/en/post/1
How can I implement such thing?
you can find an example in the rails guides: http://guides.rubyonrails.org/i18n.html#setting-the-locale-from-the-domain-name
its about domain names, but you can adapt it to your needs pretty easily.
keep in mind that subdomains introduce a lot of complexity into your app. cookies, javascript and ssl are sensitive to domains. make sure, that it's worth using subdomains vs. paths.
i find a simple example you can follow pretty easy in medium:
https://medium.com/unexpected-token/making-your-website-multi-regional-using-top-level-domains-cdbbdb951b65
the idea is to define a clear one-to-one mapping between the locale and the host
HOSTS_MAPPING = {
'en' => 'en.example.com',
'fr' => 'fr.example.com'
}
then use new mapping in ApplicationController
class ApplicationController < ActionController::Base
before_action :set_locale
def set_locale
I18n.locale = HOSTS_MAPPING.invert[request.host] || I18n.default_locale
end
end
it's say that your host en.example.com will use locale en
Related
I followed the great Rails guide on engines (https://guides.rubyonrails.org/engines.html) and managed to get my engine working in a host app.
However, the engine is loading only the en.yml locale, even tough the host application has I18n.default_locale and I18n.locale both set to pt-br, and I have a config/locales/pt-br.yml file in my engine.
The guide only states that "For locales, simply place the locale files in the config/locales directory, just like you would in an application.", but apparently I need to do something else.
How would I make the engine to load the correct I18n locale files, based on the host app settings? If that's not possible, how the host app could set this locale in an initializer file, like a config option?
I managed to solve it. The key is to remember that I18n.locale is a setting that should be set on every request.
So on my engine entry point (lib/cron_monitor.rb), I defined a setting:
module CronMonitor
class << self
attr_accessor :i18n
end
self.i18n = I18n.default_locale
Then in my engine's ApplicationController, following Rails i18n documentation:
module CronMonitor
class ApplicationController < ActionController::Base
around_action :switch_locale
private
def switch_locale(&action)
locale = CronMonitor.i18n || I18n.default_locale
I18n.with_locale(locale, &action)
end
end
end
Also, if you have code in models or service objects that you also want to be translated, you should use I18n as well. Example:
module CronMonitor
class Category < ApplicationRecord
def self.some_method
message = I18n.translate(
"cron_monitor.starting_or_finishing.failure",
title: self.title,
locale: CronMonitor.i18n
)
end
end
We have an CMS application which allows to create shops from a backend for multiple users.. now when we want to implement translations we face a problem. Translations are just stored with a locale but what we need would be a user.id and a locale. Because each user can have his own translations per language.
I tried to implement a i18n Backend as described in this railscast but am now stucking with
http://railscasts.com/episodes/256-i18n-backends?view=asciicast
the customization per user.
is there a way to add an additional column into the translations and ask for local and user_id?
many thanks
There is not a single best solution for your given problem. It depends on your application. Given the information I have I'd suggest something like:
app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_filter :set_locale
private
def set_locale
I18n.locale = current_user.language || I18n.default_locale
end
end
This would set the 18n.locale to the language settings of your current_user. Obviously you need a language attribute in your users model for this to work.
Please have a look at http://xyzpub.com/en/ruby-on-rails/4.0/i18n.html for more examples.
I am working with rails and am trying to implement a feature into my blogging application. I would like to have the option to choose a design for my blog. I would of course make the design and code them but after they are coded I would like to have the choice of using one of my designs.
How would/should I approach this?
Make controller for choosing design form list (of course check vaild of choise). Save choose in session and try this:
In layout:
= stylesheet_link_tag #custom_css
In application.rb
class ApplicationController < ActionController::Base
before_filter :check_css
def check_css
#custom_css = session[:css]
#custom_css ||= 'default'
end
end
I think that should work.
Other idea is change to different layout.
class ApplicationController < ActionController::Base
layout :custom_layout
def custom_layout
session[:css].nil? ? session[:css] : 'default'
end
end
My project name is clog, so I named my models and controllers like this: Clog::User Clog::Userscontroller.
Is this naming convention mandatory?
No, in a conventional Rails project, that's not necessary. Just name your models and controllers the usual way, like eg User or UsersController.
The other thing is that, when your project grows in size, you may need to organize your models into submodules. One approach to do so is extending your models with app concerns, as show eg here or here.
As for organizing controllers, one approach is to create a module in the lib directory, which you then include in your ApplicationController, like so:
In lib/authentication.rb:
module Authentication
def self.included(base)
base.send :before_filter, :login_required
base.send :helper_method, :current_user, :logged_in?
end
def current_user
#current_user ||= User.find_by_remember_token(cookies[:remember_token]) if cookies[:remember_token].present?
end
#...
end
In app/controllers/application_controller.rb
class ApplicationController < ActionController::Base
include Authentication
#...
end
For this to work, you need to add
config.autoload_paths << "#{config.root}/lib"
to your config/application.rb file
However, if you plan to build your Rails project as a Rails Engine, you may want to follow some naming convention. A good example of a Rails Engine is forem.
Yes, following the naming convention helps a great deal because not only does rails use it to generate other names, but other gems as well.
Specific to your question, you may be asking if you need to name the controller as UserController given that your model is called User. That is not necessary at all, and you may call it anything else if it better fits your purpose.
In this case, you will probably want to create a few controllers like so:
My::AccountController # for e.g.. /my/account
Admin::UsersController # for e.g. /admin/users/1
For a user, you refer to your own user record, as 'your account' so this makes more sense. However, the administrator's perspective would be to manage user records. You may also name a controller one thing and serve it under a different route. In your routes file, you may do this:
namespace :admin do
resources :users, :path => "user-accounts"
end
To reiterate, your model name need not match up to the controller name. They are only named similarly by association: UserController is understood to handle User records.
I take code from Subdomain RailsCast
module UrlHelper
def with_subdomain(subdomain)
subdomain = (subdomain || "")
subdomain += "." unless subdomain.empty?
[subdomain, request.domain, request.port_string].join
end
def url_for(options = nil)
if options.kind_of?(Hash) && options.has_key?(:subdomain)
options[:host] = with_subdomain(options.delete(:subdomain))
end
super
end
end
class ApplicationController < ActionController::Base
include UrlHelper
end
It is ok to use modified url_for in views of Controllers. But I have trouble with ActionMailer.
I tried to use following:
class Notifier < ActionMailer::Base
include UrlHelper
end
But ActionMailer views still use old unmodified url_for from ActionDispatch::Routing::RouteSet.
What is the best practice to add new url_for
Add the following code to the file app/helpers/url_helper.rb:
def set_mailer_url_options
ActionMailer::Base.default_url_options[:host] = with_subdomain(request.subdomain)
end
and modify the file app/controllers/application_controller.rb to add:
before_filter :set_mailer_url_options
Source
I have a solution for this issue but I don't think it is still the best way to do it. I have tried and will still try to come up with a better solution but here is what I have done in my email template. The reason I put this in the email template is because I'm using Devise, but I'm hoping to come up with something better.
subdomain = #resource.account.subdomain
subdomain = (subdomain || "")
subdomain += "." unless subdomain.empty?
host = [subdomain, ActionMailer::Base::default_url_options[:host]].join
You can pass the host to the url_for now like this
user_confirmation_url(:host => host)
I found the simplest solution on Rails 3.0.x was to construct the host-with-subdomain manually in every URL in my mailer views. For example:
Your account is here:
<%= account_url(:host => "#{#account.subdomain}.#{ActionMailer::Base.default_url_options[:host]}" %>
-- where your #account model knows its subdomain.
This in nice and simple, threadsafe, and isolated. You don't need to pollute other parts of your codebase. And it's easy to back out once you move to Rails 3.1.x, which should handle all this automatically I believe.