Hiding locale parameter in rails 3.1? - ruby-on-rails

i want to implement the following behaviour:
if the users browser-language is e.g. EN, he should be redirected to a the url http://foo.bar/hello, if the browser-language is DE then to http://foo.bar/hallo.
so how do i need to set my routes to redirect the user to the right language (e.g. when an english user requests the DE route and vice versa) and how can i set a hidden locale parameter, so i can load the right view in the controller?
i want to use the same controller for both languages (one method per page), but localized views (foo.en.html.erb etc.)
thanks in advance!

I don't think that what you want to try to get is a good idea, and I will explain that here. I don't understand why you would choose a different approach from the ones that are provided by Rails out of the box, and explained in details in the "Internationalization Guide, Sections 2.3 and further".
Here are the arguments:
Rails provides at least 3 different ways to change the locale:
Getting it from parameters: http://my.example.com/books?locale=de
Getting it from the sub-domain: http://de.example.com/books
Client supplied application, like the accept-header
All have the advantage that the controller and action will be the same, which is what you normally want to have.
There are helper methods if you want to change your behavior depending on the locate: locale, ...
However, you may localize views as a whole if you want to do: see localized views. This will perhaps lead to a duplication in view code.
Or you use the translation and localization API as in I18n.t 'store.title' or I18n.l Time.now.
A hidden locale parameter has the disadvantage that it is not obvious (for the user) which locale is used.

If you need to translate your routes in addition to set the locale, you may have a look to the translate_routes gem.
The README explains how you can set the locale from your translation hello/hallo.

Related

Ruby On Rails - Use "Format" As A URL GET Parameter?

I have a search page where I update the URL params on the page as filters are added or removed by the user. This allows me to deep link into the page (ie. going to /search?location=new+york&time=afternoon will set the location and afternoon filters).
I also have a filter named format. I noticed that passing in ?format=whatevervalue to the URL and then reloading the page with that param causes Rails to return a Completed 406 Not Acceptable error. It seems that format is a reserved Rails URL parameter.
Is there anyway to unreserve this parameter name for a particular endpoint?
In the context of an URL in Ruby on Rails there are at least four reserved parameter names: controller, method, id, format.
You cannot use these keys for anything else than for their intended purpose.
If you try to you will override the value internally set by Rails. In your example by setting ?format=whatevervalue you override the default format (html) and your application will try to find and render a whatevervalue template instead of the html formatted template. This will obviously not work.
Fun fact: Instead of using the default Rails path format like /users/123/edit you could use query parameters instead like this: /?controller=users&id=123&method=edit&format&html.
My suggestion is: Do not try to fight Rails conventions. Whenever you try to work around basic Rails conventions it will hurt you later on because it makes updates more difficult, common gems might break, unexpected side-effects will happen. Just use another name for that parameter.

Change the generated EDIT routes in Rails 3.2 to put id _after_ `edit`

As well explained in the Rails Guide on Routing, a resourceful route declaration like
resources :photos
generates a suite of routes and helpers. One of the routes is
/photos/:id/edit
and along with it comes the helper edit_photo_path(photo) which takes a photo object and returns the path to use to edit it. I use the helpers everywhere and want them to keep working as-is.
Well, for various reasons, this one application is generating custom IDs and it is possible for an ID to look like 64/edit or look like 64. This of course causes problems, because regardless of constraints or tweaks
/photos/64/edit
either gets interpreted as #edit 64 or #show 64/edit and neither one is always right.
The solution I would like to implement is to keep all the benefits of resourceful routers, in including being able to pass objects into the path helpers, and just change the Rails default edit path to be
/photos/edit/:id
Then all the paths would be unambiguous. Is there a (reasonably simple and clean) way to do this?
Note
I have a workaround in place so please do not provide workarounds.
Any solution must update the helpers so that edit_photo_path(photo) produces /photos/edit/:id and also works with nested resources. For example, edit_magazine_photo_path(#magazine, #photo) would produce /magazines/:magazine_id/photos/edit/:id.
I have multiple resources, so I'd prefer not to explode the size of my routes.rb with special overrides.
I know I can change the path name of the edit portion using the path_names option. (This is, in fact what I did, putting a Unicode character that will never appear in the ID in the renamed edit path, but now I have the users seeing Unicode URLs, which I don't like, and this will fail once IDs get expanded to Unicode strings.)
I know I can write custom match rules, but that gets very tedious and difficult with nested resources, particularly when it comes to generating path helpers that take objects.
I read the answer to the question "Remove the edit suffix to url" but that answer does not, in fact, remove the edit suffix, it changes a different part of the URL.
I really want insight into the inner workings of the resourceful route generation, especially the URL helpers, so that I can just switch the order of edit and id in the path.

URL Layout for multi-lingual CMS

So I'm trying to add a new, shiny REST webservice to our CMS. It should follow the REST "roules" pretty closely, so it shall use GET/POST/PUT/DELETE and proper, logical URLs. My design is heavily inspired by the Apigee Best Practices.
The CMS manages a tree of categories, where each category can contain a number of articles. For multi-lingual projects, the categories are duplicated for each language (so any article is uniquely identifier by its ID and the language ID). The structure is the same across all languages, only the positions can vary (so category X contains the categories Y and Z in every language, but Y can be before Z in language 1 and the other way around in language 2). Creating a new article or category always also creates copies in all languages. Deleting works the same way, so an article is always deleted in all languages.
FYI: Languages are identified by their numeric ID or their locale (like en_US).
(Please image a leading /v1 in front of all URIs; I've skipped it because it adds nothing to this question.)`
My current approach is having an URL scheme like this:
GET /articles :( returns all articles in all languages
GET /articles/:id-:langid :) returns a single article in a given language
POST /articles :) creates a new article
PUT /articles/:id-:langid :) update a single article in a given language
DELETE /articles/:id :) delete an article in all languages
But...
How to identify languages?
Currently, the locale each language is assigned does not need to be unique, so two languages can in rare cases have the same locale. Using the ID is guaranteed to be unique.
Using the locale (most likely forced to lowercase) would be nice, cause the URLs are more readable. But this would
maybe lead to projects with overlapping locales.
require clients to first fetch the locales (but I guess a client would otherwise have to fetch the language IDs anyway, so that's kind of okay).
I would like to end up with exactly one solution to this and tend towards using the locale. But is it worth risking overlapping locales?
(You can image langid=X to be interchangeable with locale=xx_xx in the following.)
Should the language be a hierarchy element?
In most cases, clients of the API will want to fetch the articles for a given language instead of all. I could solve this by allowing a GET parameter and offer GET /articles?langid=42. But since is such a common usecase, I would like to avoid the optional parameter and make it explicit.
This would lead to GET /articles/:langid, but this introduces the concept that the language is a hierarchy level inside the API. If I'm going to do so, I would like to make it consistent for the other verbs. The new URL layout would look like this:
GET /articles/:langid :) returns all articles in a given language
GET /articles/:langid/:id :) returns a single article in a given language
POST /articles :( creates a new article in all languages
PUT /articles/:langid/:id :) update a single article in a given language
DELETE /articles/:langid/:id :( delete an article in all languages
or
GET /:langid/articles :) returns all articles in a given language
GET /:langid/articles/:id :) returns a single article in a given language
POST /articles :( creates a new article in all languages
PUT /:langid/articles/:id :) update a single article in a given language
DELETE /:langid/articles/:id :( delete an article in all languages
This leads to...
What to do with global actions?
The above layout has the problem that POST and DELETE work on all languages, but the URL suggests that they work only on one language. So I could modify the layout:
GET /articles/:langid :) returns all articles in a given language
GET /articles/:langid/:id :) returns a single article in a given language
POST /articles :) creates a new article in all languages
PUT /articles/:langid/:id :) update a single article in a given language
DELETE /articles/:id :) delete an article in all languages
This makes for a somewhat confusing layout, as the language level is only sometimes present and one needs to know a lot about the system's interna. On the bright side, this matches the system very well and is very similar to the internal workings.
Sacrifices?
So where do I make sacrifies? Should I introduce alias URLs to have nice URL layouts but do maybe unintended stuff in the backgroud?
I had the exact same problem some time ago and I decided to put local in front of everything. While consuming content it makes much more sense to read URLs like:
/en/articles/1/
/en/authors/2/
/gr/articles/1/
/gr/authors/2/
than
/articles/en/1/
/authors/en/2/
/articles/gr/1/
/authors/gr/2/
In order to solve the "no specific locale" issue I would use a keyword referring to all available locales, or a default locale. So:
/global/articles/
or
/all-locales/articles/
or
/all/articles/
to be honest I like global and all because they make sense reading them.
DELETE /global/articles/:id :) delete an article in all languages
hope I helped
My initial approach would have been to have language as an optional prefix in the URL, e.g. /en_us/articles, /en_us/articles/:id, but if you don't want to 'pollute' your URLs with language identifiers you can use the Accept-language header instead, in the same way that content negotiation is defined in RFC 2616. Microsoft's WebAPI is using this approach for format negotiation.

Route for printer-formatted ERB view

Working with rails 3.1 here.
I'm wondering if there are any best practices for defining routes for views are meant to be sent to the printer. For instance, I have a report at "/daily" which has a print function that opens up a new nicely formatted printer view.
What URL should this view sit on? Couple ideas are:
/daily/print
/daily?media=print
What have other people used?
Either is fine.
Probably the main consideration is whether your app is public facing and accessible to search engines. Typically you want to prevent them from indexing (duplicate) content, which a printable version would be, and typically it's easier to exclude search engines (using the robots.txt file) from printable content if it's part of the path, as opposed to the query-string.
Otherwise, it's easier to just tack on the query string parameter and use that to set the printable version of your stylesheet and/or views. This approach saves having to create a new route, which may be more flexible.

struts2 localization by embedding the locale code in the action name rather than by using ...?request_locale=<locale_code>

hi all,
i want to make localization feature in a website written in struts 2. as far as i know, the standard way of doing so is using get in the following manner:
http://.../namespace/action_name?request_locale=<locale code>
however, my boss doesn't like such hairy url. instead, i'm required to write it in the following manner:
http://.../namespace/a_param/<locale code>/another_param...
i tried to change the action mapping in my struts.xml into something like
<action name="*/*..." ... >
<param name="locale">{2}</param>
...
</action>
it doesn't work
after i changed it into
<action name="*/*..." ...>
<param name="request_locale">{2}</param>
...
</action>
it doesn't work either T_T
by the way, i know there is trick of putting ActionContext.getContext().setLocale(new Locale(...)); in action which basically change the locale for that instance. however, it seems that the effect will only be transient (in contrast, i18n saves the chosen locale in session, which basically makes it quite persistent.
so, how to change the locale by embedding the locale code in the url?
Your help is highly appreciated =D
I have not done much with locals but i18n should automatically determine the correct local from the browser via the headers, there is no need for anything to be in the url. As long as there is a language bundle for the particular locale it will try to pull properties from that file.
This page shows an example of using basic i18n (only looked at it for a moment, personally I always start at http://struts.apache.org/2.x/ but the tutorial/guides are a bit dry.
Why do you need to refer to anything in the url at all concerning language? Personally if the user did want to override the default locale I would provide some form of control (menu) to do so. Then I would set a variable in session then I would create an interceptor which would call setLocale on the action using the local parameter on the session (if there is a value set of course). This way there would not be any need to embed parameters into individual pages and the local is out of the url all together.
There is a way to do what you want with the url... Something to do with conventions and slashes in allowing slashes in the action name I think. I'll post back if I remember. But I think the above is generally a better approach anyways.
Edit: Taking into consideration what you are trying to accomplish I can see two very different solutions.
1) You can use a proxy, the incoming URL www.example.com/en/ and www.example.com/fr/ can be mapped to different web applications or even the same web application but the url is re-written into a form that suites your application. Tools that can do this include: iptables, apache mod_rewrite, squid... and a multitude of others. This type of solution is more valuable if you handle multiple ip addressses/urls/applications on one server.
2) You can set the struts2 property struts.enable.SlashesInActionNames then using wildcards you can do something like:
<action name="*/*">
<result>/WEB-INF/content/{1}/{2}.jsp</result>
<action>
You can also pass parameters to actions each asterisk found in the action name becomes {1}, {2}, etc. It sounds like you might need this feature. If someone else knows it escapes me at the moment how you would capture parts of the url like this with struts2-conventions-plugin so the action can make use of them I would find that interesting.
#Quaternion
basically the intention is that, the website has several national "sub-website". based on user's ip address, he/she will be redirected to the national "sub-website". it's like when you open www.google.com, you may be redirected to www.google.com.country_domain.
each national "sub-website" has several languages, with 1 default language. it's just like when you open google israel website, by default you will see a website written in hebrew language, although you can override this default choice by choosing it to be in english.
in my planned website, following isreal website and hebrew language example, it is supposed to be like this:
the user is in israel
he is opening www.abcdef.com
the server is recognizing that the client is in israel. there are 3 languages can be chosen for the israel "sub-website": hebrew, arabian, english. the default one is hebrew, but client can override this choice
the user is then redirected to www.abcdef.com/il/he ("il" stands for israel country and "he" stands for hebrew language)
but the user is apparently a british tourist with no knowledge on hebrew or arabian language. so he/she chose english language
he/she will be redirected to www.abcdef.com/il/en ("en" stands for english language)
the next time that client opens www.abcdef.com in israel again (assuming the cookies & sessions are still around), he/she will be redirected to www.abcdef.com/il/en
thx fr your help =D
Definitely I would leave the responsibility to handle the Locale to an interceptor.
Here is a tutorial to Create an Interceptor.
This interceptor can be placed in a common stack shared by all (or most of) incoming requests and it will assign the locale
ActionContext.getContext().setLocale(locale);
with the proper logic that could take into account:
query-string parameters
stored user preferences
cookies
session
browser preferences (are in the request)

Resources