tag:blogger.com,1999:blog-6654475589159546392024-03-02T17:40:48.733-06:00Code Alien - Configuration, Design & DevelopmentVadimhttp://www.blogger.com/profile/13973548080837457568noreply@blogger.comBlogger6125tag:blogger.com,1999:blog-665447558915954639.post-91581934128907256752016-02-10T22:59:00.001-06:002018-01-21T04:32:30.301-06:00Internationalization and Locale-Based URLs with Ruby on Rails 4.xIn this post we will learn how to produce the following locale-based URLs:<br />
<br />
<pre class="prettyprint lang-html"><code>Deutsch: http://example.com/de/books
English: http://example.com/en/books</code></pre>
<h4>
Introduction</h4>
No matter how small your application is, it is never too early to think of internationalization, even if you have no immediate plans to broadcast your website in multiple languages. Planning for internationalization early on will save countless hours and headache in the future. Even if your website is only using one language, the process of internationalization will force you to refactor all of your user-specific messages into one place so that they are easy to find and maintain.<br />
This post, however, is not about the benefits of internationalization (you can find many yourself with a quick Google search) or how to display your website in multiple languages. I assume that you already know how to do that and will instead delve deeper into how to serve language-based content to your users with intuitive and user-friendly URLs.<br />
<br />
<hr />
<h4>
Different locale-based URL options</h4>
Although I am only discussing one specific method of producing locale-based URLs, it is good to know other alternatives.<br />
<h5>
Locale as a URL variable: <code>http://example.com/books?locale=en</code></h5>
With this method every URL with have the <code>locale=en</code> segment to indicate the language. While, it is clear what language is being used, having the same parameter in every URL is a little verbose, not to mention aesthetically unappealing (if you happen to be a pedant). Another disadvantage of this method is that search engines may not realize that a page uses a different language and, as a result, might not index it correctly.<br />
<h5>
Locale as a subdomain: <code>http://en.example.com/books</code></h5>
This URL certainly looks much more pleasing to the eye and addresses the search engine shortcoming addressed above; however, this method requires extra configuration for every different language. That is, you will have manually add every subdomain to your website and point it correctly to your website, and, depending on how you configure these subdomains, you may have to repoint them all if the IP address of your website happens to change (which, of course, can be addresses by using canonical names). Another possible problem arises from using third-party hosting services, which may not allow you to add subdomains for free.<br />
<h5>
Locale as a host suffix: <code>http://example.com/en/books</code></h5>
This, in my opinion, is the absolute winner, as it is trivial to configure, does not require any extra configuration if another language needs to be added in the future. Search engines are well aware of this technique, and your URLs stay short and easy to understand.<br />
<h5>
Session-based locale: <code>http://example.com/books</code></h5>
In this case, there is no way to tell from the URL what language is being used. This method is typically reserved for websites where users have to log in and can then change their language of preference in website settings. Since a user specifically opts for a particular language, there is no reason to make URLs locale-based, and, therefore, this method certainly has its place and should not be discounted.
<br />
<br />
<hr />
<h4>
Creating a small application and setting up some translations</h4>
To learn to how to create pretty locale-based URLs, we will build together a tiny book inventory application. Follow my lead.
<br />
<br />
Let's create our application and a <code>books</code> resource with a title, one author and a price.
<br />
<br />
<pre class="prettyprint linenums lang-sh"><code>rails new bookapp && cd bookapp
rails generate scaffold book title author price:decimal
rake db:migrate</code></pre>
<br />
If we start our application with <code>rails server</code> and navigate to <code><a href="http://localhost:3000/" target="_blank">http://localhost:3000</a></code> we should be able to see an empty list of books. Before we start customizing URLs, let's add some translations to the application so that when the URLs are ready, we will be able to tell the difference.<br />
<h5>
Create the following internationalization files in <code>/config/locales</code>:</h5>
<code>de.yml</code><br />
<br />
<pre class="prettyprint linenums lang-rb"><code>de:
button:
back: 'Zurück'
destroy: 'Zerstören'
edit: 'Bearbeiten'
show: 'Zeigen'
listing: "%{model}liste"
editing: "%{model} bearbeiten"
helpers:
submit:
create: "%{model} erstellen"
submit: "%{model} speichern"
update: "%{model} aktualisieren"
activerecord:
models:
book:
one: 'Buch'
other: 'Bücher'
new: "Neues Buch"
attributes:
book:
title: 'Titel'
author: 'Autor'
price: 'Preis'
</code></pre>
<br />
<code>en.yml</code><br />
<br />
<pre class="prettyprint linenums lang-rb"><code>en:
button:
back: 'Back'
destroy: 'Destroy'
edit: 'Edit'
show: 'Show'
listing: "Listing %{model}"
editing: "Editing %{model}"
helpers:
submit:
create: "Create %{model}"
submit: "Save %{model}"
update: "Update %{model}"
activerecord:
models:
book:
one: 'Book'
other: 'Books'
new: "New Book"
attributes:
book:
title: 'Title'
author: 'Author'
price: 'Price'
</code></pre>
<h5>
Modify the following Book views as follows:</h5>
<code>_form.html.erb</code><br />
<br />
<pre class="prettyprint linenums lang-rb"><code><%= form_for(@book) do |f| %>
<div class="field">
<%= f.label :title, Book.human_attribute_name(:title) %><br>
<%= f.text_field :title %>
</div>
<div class="field">
<%= f.label :author, Book.human_attribute_name(:author) %><br>
<%= f.text_field :author %>
</div>
<div class="field">
<%= f.label :price, Book.human_attribute_name(:price) %><br>
<%= f.text_field :price %>
</div>
<div class="actions">
<%= f.submit class: 'button' %>
</div>
<% end %>
</code></pre>
<br />
<code>edit.html.erb</code><br />
<br />
<pre class="prettyprint linenums lang-rb"><code><h1><%= t('editing', model: Book.model_name.human) %></h1>
<%= render 'form' %>
<%= link_to t('button.back'), books_path, class: 'hollow secondary button' %>
<%= link_to t('button.show'), @book, class: 'button' %>
</code></pre>
<br />
<code>index.html.erb</code><br />
<br />
<pre class="prettyprint linenums lang-rb"><code><h1><%= t('listing', model: Book.model_name.human(:count => 2)) %></h1>
<table>
<thead>
<tr>
<th><%= Book.human_attribute_name(:title) %></th>
<th><%= Book.human_attribute_name(:author) %></th>
<th><%= Book.human_attribute_name(:price) %></th>
<th colspan="3"></th>
</tr>
</thead>
<tbody>
<% @books.each do |book| %>
<tr>
<td><%= book.title %></td>
<td><%= book.author %></td>
<td><%= book.price %></td>
<td><%= link_to t('button.show'), book %></td>
<td><%= link_to t('button.edit'), edit_book_path(book) %></td>
<td><%= link_to t('button.destroy'), book, method: :delete %></td>
</tr>
<% end %>
</tbody>
</table>
<%= link_to t('activerecord.models.book.new'), new_book_path, class: 'button' %>
</code></pre>
<br />
<code>new.html.erb</code><br />
<br />
<pre class="prettyprint linenums lang-rb"><code><h1><%= t('activerecord.models.book.new') %></h1>
<%= render 'form' %>
<%= link_to t('button.back'), books_path, class: 'hollow secondary button' %>
</code></pre>
<br />
<code>show.html.erb</code><br />
<br />
<pre class="prettyprint linenums lang-rb"><code><p>
<strong><%= Book.human_attribute_name(:title) %>:</strong>
<%= @book.title %>
</p>
<p>
<strong><%= Book.human_attribute_name(:author) %>:</strong>
<%= @book.author %>
</p>
<p>
<strong><%= Book.human_attribute_name(:price) %>:</strong>
<%= @book.price %>
</p>
<%= link_to t('button.back'), books_path, class: 'hollow secondary button' %>
<%= link_to t('button.edit'), edit_book_path(@book), class: 'button' %>
</code></pre>
<br />
Now that we've created some translations, we can change the default locale to German in <code>/config/application.rb</code> by uncommenting the line:<br />
<br />
<pre class="prettyprint lang-rb"><code>config.i18n.default_locale = :de</code></pre>
<br />
If we navigate to <code><a href="http://localhost:3000/books" target="_blank">http://localhost:3000/books</a></code>, we will see the list of books in German (remember to restart the application to see the changes).<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy_jFWTqDE7YO6-ndEwLyq8T0fk-sIe-SqhJHc6QW5SkhDrL4dKmjrTzq1udPpTRt8tZqF9fGjb8HyiAxC_Zd0A4zp-qCFG4u_6iRvx_EsAo3PIywY8-W9m1VPJyTBBF34iBeZUoSfeRA/s1600/listbooks.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjy_jFWTqDE7YO6-ndEwLyq8T0fk-sIe-SqhJHc6QW5SkhDrL4dKmjrTzq1udPpTRt8tZqF9fGjb8HyiAxC_Zd0A4zp-qCFG4u_6iRvx_EsAo3PIywY8-W9m1VPJyTBBF34iBeZUoSfeRA/s1600/listbooks.png" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: left;">
Now we're finally ready to add locales to our URLs.</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<hr />
<h4>
Setting up locale-based URLs</h4>
The first we need to do is enclose all routes that should have the locale prefix inside <code>scope</code> inside <code>routes.rb</code>.<br />
<br />
<pre class="prettyprint linenums lang-rb">Rails.application.routes.draw do
scope '(:locale)', locale: Rails.configuration.x.locale do
resources :books
# other locale-based routes defined here
end
# non-locale-based routes defined here
end</pre>
<br />
The variable <code>Rails.configuration.x.locale</code> is currently undefined, but we will use it to keep a list of all supported locales. The best place to define it is in <code>application.rb</code>:
<br />
<br />
<pre class="prettyprint linenums lang-rb"><code><code><code><code><code><code>class Application < Rails::Application
...
config.x.locale = /de|en/
...
end</code></code></code></code></code></code></pre>
<br />
Now we need to create a <code>before_action</code> in <code>application_controller.rb</code>. We also need to override the default <code>default_url_options</code> method so that all URLs always contain the locale (note that this will not affect any non-locale-based routes in case you defined some).
<br />
<br />
<pre class="prettyprint linenums lang-rb">class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :set_locale
private
def default_url_options
{locale: params[:locale]}
end
def set_locale
I18n.locale = params[:locale] || I18n.default_locale
end
end</pre>
<br />
Now we can navigate to <code><a href="http://localhost:3000/de/books" target="_blank">http://localhost:3000/de/books</a></code> and <code><a href="http://localhost:3000/en/books" target="_blank">http://localhost:3000/en/books</a></code> and see the list of books in German and English, respectively.
<br />
<br />
<hr />
<h4>
Detecting default browser language</h4>
An even better idea is to serve website content in a user's preferred language by default instead of making users change the language to the one they like best. To achieve this goal, we need only change the default application locale to the user's default browser locale. The resulting <code>application_controller.rb</code> is given below:<br />
<br />
<pre class="prettyprint linenums lang-rb">class ApplicationController < ActionController::Base
protect_from_forgery with: :exception
before_action :set_locale
private
def default_url_options
{locale: params[:locale]}
end
def preferred_locale
request.env['HTTP_ACCEPT_LANGUAGE'].scan(/^[a-z]{2}/).first if request.env['HTTP_ACCEPT_LANGUAGE'].present?
end
def set_locale
new_locale = params[:locale] || preferred_locale
I18n.locale = if I18n.locale_available? new_locale
new_locale
else
I18n.default_locale
end
end
end
</pre>
<br />
To test whether this worked, change your default browser language from English to German or vice versa, restart the browser and navigate to <code><a href="http://localhost:3000/books" target="_blank">http://localhost:3000/books</a></code>. You should now see content in two different languages. Note that we've also added a check that verifies that the preferred user language is indeed among the languages available in the application. If that is not the case, we fall back to the default locale.<br />
<br />
<hr />
<h4>
Setting page language in HTML</h4>
Search engines and web crawlers need to know which language is used by a particular page of your website. To do that simply make sure that the <code>lang</code> attribute on the <code><html></code> tag is set to change dynamically:
<br />
<br />
<pre class="prettyprint linenums lang-rb"><html lang="<%= I18n.locale %>"></pre>
<br />
<hr />
<h4>
Creating buttons that change the locale</h4>
The only thing left to do is create links that change the language of a website. Fortunately, that is the easiest part.
<br />
<br />
<pre class="prettyprint linenums lang-rb"><%= link_to 'Deutsch', params.permit(:locale).merge(locale: :de) %>
<%= link_to 'English', params.permit(:locale).merge(locale: :en) %></pre>
<br />
These links can be placed on any page of the website (and should probably be present on all pages). They will redirect users to the same page that they are currently on, but the language will be changed.
<br />
<br />
<hr />
<h4>
Conclusion</h4>
You are now well on your way to creating user-friendly locale-based URLs that are generic and easily maintainable. The only thing to remember is that when adding new languages it is necessary to add the new locale in the <code>application.rb</code> and restart the application. Everything else should work as is.<br />
<br />
Happy internationalizing!Vadimhttp://www.blogger.com/profile/13973548080837457568noreply@blogger.com0tag:blogger.com,1999:blog-665447558915954639.post-55216056522501398002014-09-05T20:53:00.000-05:002016-02-11T11:40:15.710-06:00HTML5 Pattern Regex Date Validator for 1900-2099 using YYYY-MM-DDHTML5 pattern is a wonderful tool for validating user input, and date validation is a common problem if one doesn't want to use a date plugin.<br />
<br />
This date validator has the following features:<br />
<ul>
<li>It validates dates between 1999 and 2099 in the format <code>YYYY-MM-DD</code></li>
<li>It takes leap years into account (i.e. Feb 29, 2014 is not a valid date)</li>
<li>It knows which months have 30 days and which have 31</li>
<li>It allows the following separators: period(.), comma(,), forward slash(/), hyphen(-), space( )</li>
</ul>
<div>
<br />
Use the following code to implement your date validator:</div>
<div>
<br /></div>
<div>
<pre class="prettyprint linenums lang-html"><code><input type="text" pattern="^(?:((?:19|20)[0-9]{2})[\/\-. ]?(?:(0[1-9]|1[0-2])[\/\-. ]?([0-2][1-8]|[12]0|09|19)|(0[13-9]|1[0-2])[\/\-. ]?(29|30)|(0[13578]|1[02])[\/\-. ]?(31))|(19(?:[0][48]|[2468][048]|[13579][26])|20(?:[02468][048]|[13579][26]))[\/\-. ]?(02)[\/\-. ]?(29))$"></code></pre>
<br /></div>
<div>
If you would like to restrict your dates to particular separators, simply remove the ones you do not want from the <code>[\/\-. ]</code> portion of the regular expression.<br />
<br />
If you would to like to allow partial dates, such as YYYY-MM-00, use the following regular expression instead (note that the only change was to add a 0 within <code>[12]</code>):<br />
<br />
<pre class="prettyprint linenums lang-html"><code><input type="text" pattern="^(?:((?:19|20)[0-9]{2})[\/\-. ]?(?:(0[1-9]|1[0-2])[\/\-. ]?([0-2][1-8]|[012]0|09|19)|(0[13-9]|1[0-2])[\/\-. ]?(29|30)|(0[13578]|1[02])[\/\-. ]?(31))|(19(?:[0][48]|[2468][048]|[13579][26])|20(?:[02468][048]|[13579][26]))[\/\-. ]?(02)[\/\-. ]?(29))$"></code></pre>
<br />
<span style="color: red;"><b>Warning:</b></span> Remember that your browser may not support the <code>pattern</code> attribute. Always have another option for validating user input, such as javascript that reads the <code>pattern</code> attribute and validates it using <code>RegExp</code> or other alternatives.<br />
<br />
<span style="color: red;"><b>Attribution:</b></span> This validator was originally found <a href="http://atomicnoggin.ca/blog/2012/06/07/the-ultimate-date-validation-regexp/" target="_blank">here</a>.</div>
Anonymousnoreply@blogger.com2tag:blogger.com,1999:blog-665447558915954639.post-40115334936237756772014-09-04T11:30:00.002-05:002016-09-12T23:00:08.853-05:00Expand and Collapse Textarea Automatically When TypingI was searching one day for ways to expand the textarea automatically, but couldn't find any solutions that were quite what I wanted. Some required hacks to CSS, others to HTML. As a result, I decided to write my own JQuery based script that would do the job.<br />
<br />
Here are some of its features:<br />
<ul>
<li>Textarea expands and collapses automatically when typing</li>
<li>Textarea expands automatically when pasting text into it</li>
<li>If updated dynamically, it will not expand on change but it will expand the moment it gets focus</li>
<li>Works with textareas that are added using AJAX</li>
<li>Very lightweight, as it does NOT use JQuery</li>
<li>Fully cross-browser compatible</li>
</ul>
<div>
<br />
To install the code, simply include it anywhere on your page (header or body).<br />
<br /></div>
<pre class="prettyprint linenums lang-js"><code>(function() {
function resizeTextArea(event) {
var e = event || window.event;
var o = e.target || e.srcElement;
if (!(o instanceof HTMLTextAreaElement)) return;
if (o.getAttribute('data-rows') === null)
o.setAttribute('data-rows', o.rows);
addEventListener(o, 'blur', restoreTextArea);
while (o.rows > 1 && o.rows >= o.getAttribute('data-rows') && o.scrollHeight < o.offsetHeight)
o.rows--;
for (h = 0; o.scrollHeight > o.offsetHeight && h !== o.offsetHeight; o.rows++)
h = o.offsetHeight;
}
function restoreTextArea(event) {
var e = event || window.event;
var o = e.target || e.srcElement;
if (!(o instanceof HTMLTextAreaElement)) return;
o.rows = o.getAttribute('data-rows');
}
function addEventListener(o, e, f) {
if (o.addEventListener)
o.addEventListener(e, f, false);
else if (o.attachEvent)
o.attachEvent('on' + e, f);
}
addEventListener(document, 'click', resizeTextArea);
addEventListener(document, 'keydown', resizeTextArea);
addEventListener(document, 'keyup', resizeTextArea);
}());</code></pre>Anonymousnoreply@blogger.com0tag:blogger.com,1999:blog-665447558915954639.post-65006392413427169062014-02-12T11:59:00.000-06:002016-01-07T09:17:29.472-06:00Chrome Secure Shell App Remove All SSH Hosts<b>Secure Shell</b> uses strict checking, and if the signature of your host has changed, you will not be able to connect while receiving this error message:<br /><br /><div style="background-color: black; color: white;"><code>Welcome to Secure Shell version 0.8.25.<br />Answers to Frequently Asked Questions: http://goo.gl/TK7876<br />Verbindung mit vkononov@206.45.90.140, Port ?? wird hergestellt...<br />Loading NaCl plugin... done.<br />@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<br />@ WARNING: REMOTE HOST IDENTIFICATION HAS CHANGED! @<br />@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@<br />IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY!<br />Someone could be eavesdropping on you right now (man-in-the-middle attack)!<br />It is also possible that a host key has just been changed.<br />The fingerprint for the RSA key sent by the remote host is<br />76:d5:55:de:65:0d:75:29:2a:7b:0d:fe:46:51:4d:4d.<br />Please contact your system administrator.<br />Add correct host key in /.ssh/known_hosts to get rid of this message.<br />Offending RSA key in /.ssh/known_hosts:1<br />RSA host key for 206.45.90.140 has changed and you have requested strict checking.<br />Host key verification failed.<br />NaCl plugin exited with status code 255.<br />(R)econnect, (C)hoose another connection, or E(x)it?</code></div><br />The easiest thing to do in this situation is to delete all hosts for <b>Secure Shell</b>.<br /><ol><li>Open up Secure Shell</li><li>Start the Javascript console</li><li>Enter this command in the console:</li></ol><div><div style="text-align: left;"><code>term_.command.removeDirectory('/.ssh/')</code></div><br />You should receive a confirmation "<code>Removed: /.ssh/:</code>"<br /><br /></div><div><b>WARNING:</b> If working on Chrome OS, removing hosts for the OS terminal will have no effect on Secure Shell, as they both use their own respective hosts file.</div>Vadimhttp://www.blogger.com/profile/13973548080837457568noreply@blogger.com0tag:blogger.com,1999:blog-665447558915954639.post-70125310402548464852014-02-09T12:11:00.000-06:002016-01-07T09:17:29.492-06:00Align Text Both Left and Right on the Same Line in Text EditorsSometimes we would like to align some text on the line to the left and some on the right. Occasionally we even want three types of alignments on one line: left, centre and right. All of this is easy to accomplish in most text editors by utilizing the TAB functionality.<br /><br />Here's what you need to do to achieve the desired effect:<br /><ol><li>Place the cursor at the position on the line where you would like to begin aligning the following text differently.</li><li>Press the TAB key.</li><li>Now place the cursor just before the tab (you can move the cursor with your mouse or press the LEFT key on your keyboard).</li><li>If you would like to centre the text, create a TAB that is centre-aligned. If you would like to align the text on the right, create a TAB that is right justified. Then drag the tab in the ruler all the way to the right until it coincides with the right margin.</li></ol><div>That's it! Your text should now have multiple alignments. For instructions on how to create a TAB in <b style="font-style: italic;">your</b> particular text editor you may have to do an Internet search.</div>Vadimhttp://www.blogger.com/profile/13973548080837457568noreply@blogger.com0tag:blogger.com,1999:blog-665447558915954639.post-42444486449474830862014-01-19T13:04:00.000-06:002016-01-07T09:17:29.504-06:00How to Completely Log Off Users by Destroying Sessions in PHPIn order to delete a session, two things need to be done: <br /><ul><li>The session variables need to be destroyed: <a href="http://www.php.net/manual/function.session-unset.php"><code>session_unset()</code></a>. This ensures that all the data associated with the current session is deleted. </li><li>The session id needs to be regenerated: <a href="http://www.php.net/manual/function.session-regenerate-id.php"><code>session_regenerate_id()</code></a>. This is necessary so that another session with the same id cannot be created.</li></ul><br />It goes without saying that these methods can only be called if the session has already been started. Otherwise, you need to run <a href="http://www.php.net/manual/function.session-start.php"><code>session_start()</code></a> first.Vadimhttp://www.blogger.com/profile/13973548080837457568noreply@blogger.com1