How to set locale for errors.po? - phoenix-framework

How to set a locale in Phoenix, to get to use priv/gettext/{lang}/LC_MESSAGES/errors.po?
As a test, I built the locale file with mix gettext.merge priv/gettext --locale ja, and translated some words in it.
It works if I explicitly call put_locale/2 in web/views/error_helpers.ex and <%= translate_error(message) %> in a template file, but it's a bit ugly, in a point of DRY.
def translate_error(msg) do
Gettext.put_locale(LoginStudy.Gettext, "ja")
Gettext.dgettext(LoginStudy.Gettext, "errors", msg)
end
Is there any better way to set the default locale? I specified the default_locale in config/config.ex, but it doesn't work.
config :login_study, LoginStudy.Endpoint, default_locale: "ja",
Best Regards,

Great questions #hykw! Since Gettext support in Phoenix is new, documentation is just now starting to show up.
A good starting point is this excellent blog post by Rebecca Skinner: http://sevenseacat.net/2015/12/20/i18n-in-phoenix-apps.html
For example, if you want to set the locale to japanese only for some of your web requests, you can define a plug, as she did with MyApp.Locale, and run it at the beginning of your request lifecycle. I just don't advise to store the locale in the session but keep it rather as part of the URL or some other parameter.
However, if you want the locale to always be japanese, you can write in your config file:
config :my_app, MyApp.Gettext, default_locale: "ja"
You can find more information about this on the Gettext docs: http://hexdocs.pm/gettext/Gettext.html

Related

How do I programmatically set a content_security_policy?

I'm configuring the Content Security Policy for our Rails 5.2 app. I need to whitelist some domains in our CSP. I'd like to put the list of domains elsewhere so I can reference them in other places in the application, then generate the CSP headers programmatically from that list.
Looking at the source code for the Content Security Policy configuration mechanisms in Rails 5, it looks like there's some magic metaprogramming going on, so it's not clear to me how to accomplish what I need to do. It looks like the functions I need to call to set headers might be picky about how exactly they want to be called. In particular, it's not clear to me if I can pass them arrays or safely call them multiple times, or if they do some metaprogramming magic that only works if the domains are passed in as individual function arguments.
Can I pass in an array to the header I want to set, like this?
whitelisted_domains = ['https://example.com', 'self']
Rails.application.configure do
config.content_security_policy do |csp|
csp.child_src whitelisted_domains
end
end
Or can I call the same function multiple times, like this?
whitelisted_domains = ['https://example.com', 'self']
Rails.application.configure do
config.content_security_policy do |csp|
whitelisted_domains.each {|domain| csp.child_src domain}
end
end
If neither of those will work, what's the best way of accomplishing what I want to do?
From what I can tell from sourcecode and documentation, it takes an array. From the edgeguides at rails, posting following
Rails.application.config.content_security_policy do |policy|
policy.default_src :self, :https
...
end
and the sourcecode, using *sources as param; it believe it takes any number of arguments, meaning you could do something along the lines of;
whitelisted_domains = ['https://example.com', 'self']
Rails.application.configure do
config.content_security_policy do |csp|
csp.child_src(*whitelisted_domains)
end
end
https://blog.sqreen.io/integrating-content-security-policy-into-your-rails-applications-4f883eed8f45/
https://edgeguides.rubyonrails.org/security.html#content-security-policy
Sourcecode of define_method for each directive
https://github.com/rails/rails/blob/master/actionpack/lib/action_dispatch/http/content_security_policy.rb#L151
(note: None of this has been tested in a Rails app, simple looking guides and source code of Rails)

Parameters in route are not resolved

I have this configuration in the controller in Padrino
MyProject::App.controllers do
get '/' do
handlebars :index
end
get :file, :with => :tokenId do
tokenId = params[:tokenId]
[extra logic]
end
end
GET / works.
GET /file/abc doesn't.
GET /file/:tokenId works!
It looks like :token is not recognized as a parameter placeholder in the route definition.
I've tried
get "/file/:tokenId"
too but with no luck.
I can't find any information on any similar issue, anybody can help? Happy to add more information if needed.
Okay so I am unsure why the change made a difference but camelCase is generally considered poor syntax for variables in ruby.(Padrino may be calling a method such as underscore on your variable i.e.
"tokenID".underscore.to_sym
#=>:token_id
Using underscored_variables instead. (e.g. :tokenID becomes :token_id. This structure also allows for interacting with databases in a nicer way as well since your columns will have names such as token_id not tokenID.
There are uses for camelCasing in ruby and rails such as class naming and generators but trying keep all local and instance variables in lowercase underscore format.
I don't do much work in padrino so I am not 100% sure why this change helped but I am glad I could help.

How to reuse Spring or Java's locale lookup code?

I am doing a Spring web application.
I need to support a few locales such as ko (Korean), ru (Russian), en (English), etc.
I am able to catch what locale is from a browser via ways such as RequestContextUtils.getLocale(request) or LocaleChangeInterceptor.
However, the browser's locale may not be what my web app supports. I have to resolve it to the closet or default locale.
Basically, I need to know how to get the resolved locale given the browser's locale AND a few locale values such as ko, ru, and en.
My understanding is that Spring has such locale resolution code because it is able to find right resource bundles given a browser's locale. I am hoping to reuse Spring's code for locale resolution, but not sure how to do it. Please note that this question has nothing to do with finding the brwoser's locale or displaying proper messages.
EDIT
Based on my tracing Spring's code, it appears that Spring depends on JDK to find the exact or closest locale. I just found out this and think this is what I am looking for:
Resource Bundle Lookup Order
https://sites.google.com/site/openjdklocale/design-notes/resource-bundle-lookup-order
Please note that I don't need to find right resource bundle. I just need to get the locale the existing JDK code returns given a locale in question and a few known locales. So I am hoping to reuse existing JDK's lookup code. Any idea?
I am using JDK 7.
Thanks for any help and input!
Regards.
Short answer
Have you checked the official documentation (chapter 17.8 Using locales)? You need to configure LocaleResolver and possibly a LocaleChangeInterceptor (or write your own).
Longer description about how Spring works
Note, that resolving client's locale is different task from getting a correct resource bundle.
Spring uses LocaleResolver to get or set the current locale. There are several implementations for different strategies to LocaleResolver:
FixedLocaleResolver - will always resolve locale to predefined value (not capable of setting different locale)
SessionLocaleResolver - stores and resolves locale to value store on session under special key
AcceptHeaderLocaleResolver - this is the resolver which actually tries to get locale from the browser (not capable of setting different locale)
CookieLocaleResolver - stores and resolves locale to value stored in a browser cooke
LocaleResolver is used to populate LocaleContextHolder (btw. that is the class you should be getting locale from).
There is a second mechanism LocaleChangeInterceptor, which is able to set locale via your selected LocaleResolver based on user request parameter.
Now this infrastructure is unrelated to your resource bundles (messages.properties, messages_en.properties, ...) and the mechanism used to resolve your messages. The following examples will show why.
Example scenarios
Lets assume your resource bundles are:
messages.properties - with ru messages (default messages)
messages_ko.properties - with ko messages
Lets assume you have configured SessionLocaleResolver with default locale ru
And lets assume you have configured LocaleChangeInterceptor
SCENARIO I - First requets:
User makes first request to the application
As soon as the request reaches Spring's DispatcherServlet it queries LocaleResolver to get locale for the request
No locale is set on the session, so the locale is resolved to ru (default)
...handler stuff...
Now you are rendering the webpage and you want to use <spring:message> tag...
The tag tries to resolve translation code using preconfigured MessageSource (ResourceBundleMessageSource) with request locale (this is the one resolved by your resolver).
Message source tries to load translation code from messages_ru.properties which does not exist, so it moves to more general file messages.properties (which "by accident" contains your default language - ru)
User get his page in russian language
SCENARIO II - User clicks link to change his language to ko:
Second request is made with query parameter locale=ko
DispatcherServlet resolves request locale to ru (this is what your locale resolver returns)
Before the request is handed over to your handler it goes through the LocaleChangeInterceptor handler interceptor.
LocaleChangeInterceptor detects locale query parameter and calls setLocale method on your LocaleResolver, which leads to changing request locale and storing new locale on the session for future requests.
...handler stuff...
...view stuff...
Now <spring:message> is calling MessageSource with ko locale.
Message source tries to load messages_ko.properties and succeeds.
User get his page in korean language
SCENARIO III - User tries to change to invalid locale:
User makes request with query parameter locale=en.
...dispatcher stuff... (locale is resolved to ko from the session)
Handler interceptor changes the locale to en (this will be stored on the session as well)
...handler stuff...
...view stuff...
Now <spring:message> is calling MessageSource with en locale.
Message source tries to load messages_en.properties which does not exist so it moves to a more general file messages.properties and messages are translated to ru, even thou the request locale is set to en.
User get his page in rusian language
Summary
Now the last example is probably what bothers you - there is no check whether the locale user selects is supported or not. If you don't want to allow user to switch to unsupported locale, then you need to either subclass some LocaleResolver or write your own LocaleChangeInterceptor.
Complete Reuse
To reuse the JDK logic, you could create a property file within the classpath for each of the known locales (such as test_fr_CA.properties, test_fr.properties, test_en_US.properties, test_en.properties, test.properties). Don't forget the root locale (test.properties) if you want to be able to match to it. Then simply create a resource bundle for the locale in question and inspect it to see the actual locale used.
ResourceBundle rb = ResourceBundle.getBundle("test", Locale.FRENCH);
System.out.println("Locale used is:"+rb.getLocale().toString());
The files can be created dynamically and cleaned up after the test.
High Level Code Replication, Low Level Reuse
You could replicate the high level code in java.util.ResourceBundle.getBundleImpl(...). This is basically going through looking for a match (using your own matching logic like equal toString() representations) in a candidate list of locales reusing java.util.ResourceBundle.Control.getCandidateLocales(...) on the locale in question. If there is no match you get the next fallback locale for the locale in question by reusing java.util.ResourceBundle.Control.getFallbackLocale(...) For each fallback locale you try to match a locale in it's candidate list repeating the fallback in a loop until there are no fallback locales left. Note that the root locale will be the last candidate in each candidate list but it should be skipped unless you have exhausted all fallback locales.
This method does not require creating files. You use a non-existent baseName in the getCandidateLocales(...) and get FallbackLocale(...) calls and compare each candidate locale to your list of known locales looking for a match.
A simple example of this would be like the following:
ResourceBundle.Control rbControl = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_PROPERTIES);
Locale localeInQuestion = Locale.CHINA;
List<Locale> knownLocales = Arrays.asList(new Locale[] {Locale.CANADA_FRENCH, Locale.FRENCH, Locale.US, Locale.UK, Locale.ENGLISH, Locale.ROOT});
String nonExistentBaseName = "bogus";
Locale matchingLocale = null;
Boolean reachedRootLocaleMatch = false;
outerloop:
for (Locale targetLocale = localeInQuestion;
targetLocale != null;
targetLocale = rbControl.getFallbackLocale(nonExistentBaseName, targetLocale)) {
List<Locale> candidateLocales = rbControl.getCandidateLocales(nonExistentBaseName, targetLocale);
for (Iterator iterator = candidateLocales.iterator(); iterator.hasNext();) {
Locale currentCandidateLocale = (Locale) iterator.next();
if (knownLocales.contains(currentCandidateLocale)) {
if (currentCandidateLocale.equals(Locale.ROOT)) {
reachedRootLocaleMatch = true;
}
else {
matchingLocale = currentCandidateLocale;
break outerloop;
}
}
}
}
if (matchingLocale == null && reachedRootLocaleMatch) {
matchingLocale = Locale.ROOT;
}
if (matchingLocale != null) {
System.out.println("The matching locale is: "+matchingLocale.toString());
}
else {
System.out.println("There was no matching locale");
}

How to extend redcarpet to support auto linking user mentions?

On my rails3 application I want to use redcarpet to handle user's posts and the user comment section. As such I'd like to extend redcarpet to support turning #username into a link to a user on my site. I know redcarpet is written in C but is there anyway easy way to extend it in ruby? How hard would it be to write it in C? Should I just do this outside of redcarpet?
Also I'm intrested in some other extensions of redcarpet that would be shorthand for linking to other models in my app. I'm not sure the syntax yet but I'm guessing it would be similar to how github handles linking to issues.
I found it pretty easy to extend redcarpet's parser in Ruby for my rails 3 app. It wasn't scary at all.
First, start by deriving a class from Redcarpet's HTML renderer and override the preprocess method as recommended in the docs. In Rails 3.2 and Rails 4, this file can go anywhere and you don't need to require it. I use a 'services' folder to hold code like this.
# app/services/my_flavored_markdown.rb
class MyFlavoredMarkdown < Redcarpet::Render::HTML
def preprocess(text)
text
end
end
Next step is to add methods that do text substitutions you want. Here I use regex to wrap text that looks like "#mention" in an HTML span tag with a css class 'mention'.
# app/services/my_flavored_markdown.rb
class MyFlavoredMarkdown < Redcarpet::Render::HTML
def preprocess(text)
wrap_mentions(text)
end
def wrap_mentions(text)
text.gsub! /(^|\s)(#\w+)/ do
"#{$1}<span class='mention'>#{$2}</span>"
end
text
end
end
You could just as easily look up a user's profile page and wrap the #mention in an anchor tag instead. In my case, I also made methods for emoticons and hashtags that worked the same way and chained the methods together.
The last step is to add a helper that accepts some text, creates an instance of your Redcarpet-derived class, passes the text into that for processing, and returns the html result.
# app/helpers/application_helper.rb
def flavored_markdown_to_html(text)
renderer = MyFlavoredMarkdown.new()
# These options might be helpful but are not required
options = {
safe_links_only: true,
no_intra_emphasis: true,
autolink: true
}
Redcarpet::Markdown.new(renderer, options).render(text)
}
In your views you can call it like this:
<%= flavored_markdown_to_html("This is something worth #mentioning") %>
The output would then be:
This is something worth <span class='mention'>#mentioning</span>.
I once tried to extend redcarpet, but found it very difficult. If there are no other dependencies on redcarpet I'd recommend you try rpeg-markdown which is a (somewhat outdated) Ruby gem providing bindings to the excellent peg-markdown.
peg-markdown is a markdown interpreter written as a formal grammar. This means that it is very easy to extend it with own syntax. I've successfully extended peg-markdown for my own projects (see my fork here) and I found it to be much simpler than fiddling with redcarpet's custom parser code.
I also found peg-markdown to have fewer bugs.
The Ruby bindings may have to be made current by updating the git submodule. (I'm planning to submit a pull request to update rpeg-markdown to the latest version of peg-markdown.)

i18n markdown files in Rails 3 views

I am currently working through Michael Hartl's Rails Tutorial while experimenting with some other things not covered in the book. After completing Chapter 5, where the static pages are created, I decided change the view code to HAML, internationalize the pages, and put the static content into separate (non-partial) Markdown files, using the RDiscount gem to render them. For example:
app/views/static_pages/about.html.haml
- provide(:title, t('.about_us'))
:markdown
#{render file: "static_pages/about.#{params[:locale]}.md"}
Under the static_pages directory, I have Markdown files like about.en.md, about.it.md, about.ja.md etc, so interpolating in the :locale parameter is what determines which language Markdown file gets rendered.
My questions are:
The static_pages directory is a bit crowded with Markdown files, so are there any sensible default/best practice locations (perhaps outside of the app directory) to keep these Markdown files, where they could be presumably be edited by people who don't need to know about the inner workings of the app?
What better ways are there to implement rendering multi-lingual Markdown files in views? My use of :locale and the double string-interpolation seems inelegant.
Is there a way to change this code so that I can pass Ruby variables into the Markdown file? I know I can, for example, use a #{language} variable in the Markdown by just changing about.en.md into a HAML partial (_about.en.html.haml) and change the code to look something like:
app/views/static_pages/about.html.haml
- provide(:title, t('.about_us'))
:markdown
#{render "about.#{params[:locale]}", language: 'Markdown!'}
But, is there a way to do this without changing the Markdown file into another type of file? If such a way exists, is it recommended/feasible?
After having a look at this StackOverflow answer, it seemed that the best location for i18n Markdown files would be their own action name directories under the config/locales directory, and that there was a good opportunity to refactor the render code on all views for the StaticPagesController. So, using about.html.haml as an example below, the call to render in the home, help, about, and contact views has been changed to the exact same code:
app/views/static_pages/about.html.haml
- provide(:title, t('.about_us'))
:markdown
#{render file: localized_page_for(action_name, params[:locale])}
The localized_page_for method is defined in the StaticPagesHelper:
app/helpers/static_pages_helper.rb
module StaticPagesHelper
def localized_page_for(action, locale)
"#{Rails.root}/config/locales/#{action}/#{action}.#{locale.to_s}.md"
end
end
So, now all the Markdown files have been taken out of the app/views/static_pages directory and are called from their respective logical directories (eg. config/locales/about/about.en.md etc) using ActionController's action_name attribute and the locale, making for less clutter.
As for question 2 above, string-interpolation seems to be common enough for this kind of problem, so I'll consider it "elegant" enough as well.
As for question 3 above, after exhaustive searching, I haven't found a way anyone has passed in variables in to a pure Markdown file, and the documentation doesn't seem to say anything about supporting them, so I'm going to conclude that it's not possible. If passing Ruby variables in to Markdown is absolutely necessary, the file will need to be run through another interpreter, kind of like is described in this StackOverflow answer.
Update:
After running security scanner Brakeman against the app, it came up with a potential Dynamic Render Path security warning (albeit a weak one) due to dynamically passing in params[:locale] to the render call instead of passing it a static string. So, I moved the call to the localized_page method out of the views, moved the method itself out of the StaticPagesHelper (so that file is now empty) and in to the StaticPagesController and then instantiated a #page instance variable in each method to pass to the view. In summary, the code now looks like this, which doesn't get the security warning:
app/controllers/static_pages_controller.rb
class StaticPagesController < ApplicationController
before_filter :localized_page, only: [:help, :about, :contact]
def home
if signed_in?
#micropost = current_user.microposts.build
#feed_items = current_user.feed.paginate(page: params[:page])
else
localized_page
end
end
def help
end
def about
end
def contact
end
private
def localized_page
#page = "#{Rails.root}/config/locales/"\
"#{action_name}/#{action_name}.#{params[:locale].to_s}.md"
end
end
app/views/static_pages/about.html.haml
- provide(:title, t('.about_us'))
:markdown
#{render file: #page}

Resources