Agile Web Development with Rails, Edition 4
16.4 Task K4: Add a locale switcher.
16.2 Task K2: translating the store front
16.3 Task K3: Translating Checkout
Edit the new order page
edit app/views/orders/new.html.erb
<div class="depot_form">
<fieldset>
<legend><%= t('.legend') %></legend>
<%= render 'form', order: @order %>
</fieldset>
</div>
Edit the form used by the new order page
edit app/views/orders/_form.html.erb
<%= form_for(@order) do |f| %>
<% if @order.errors.any? %>
<div id="error_explanation">
<h2><%= pluralize(@order.errors.count, "error") %>
prohibited this order from being saved:</h2>
<ul>
<% @order.errors.full_messages.each do |message| %>
<li><%= message %></li>
<% end %>
</ul>
</div>
<% end %>
<div class="field">
<%= f.label :name, t('.name') %><br>
<%= f.text_field :name, size: 40 %>
</div>
<div class="field">
<%= f.label :address, t('.address_html') %><br>
<%= f.text_area :address, rows: 3, cols: 40 %>
</div>
<div class="field">
<%= f.label :email, t('.email') %><br>
<%= f.email_field :email, size: 40 %>
</div>
<div class="field">
<%= f.label :pay_type, t('.pay_type') %><br>
<%= f.select :pay_type, Order.pay_types.keys,
prompt: t('.pay_prompt_html') %>
</div>
<div class="actions">
<%= f.submit t('.submit') %>
</div>
<% end %>
Install i18n-js
edit Gemfile
bundle install --local
Resolving dependencies...
Using rake 12.0.0
Using i18n 0.8.4
Using json 1.8.6
Using minitest 5.3.3
Using thread_safe 0.3.6
Using builder 3.2.3
Using erubis 2.7.0
Using rack 1.5.5
Using mime-types-data 3.2016.0521
Using arel 5.0.1.20140414130214
Using bundler 1.15.1
Using thor 0.19.4
Using hike 1.2.3
Using multi_json 1.12.1
Using tilt 1.4.1
Using sqlite3 1.3.13
Using sass 3.2.19
Using execjs 2.7.0
Using coffee-script-source 1.12.2
Using turbolinks-source 5.0.3
Using rdoc 4.3.0
Using spring 1.7.2
Using bcrypt 3.1.11
Using i18n-js 3.0.0
Using tzinfo 1.2.3
Using rack-test 0.6.3
Using mime-types 3.1
Using sprockets 2.12.4
Using uglifier 3.2.0
Using coffee-script 2.4.1
Using turbolinks 5.0.1
Using sdoc 0.4.2
Using activesupport 4.1.16 from source at `/home/rubys/git/rails`
Using mail 2.6.5
Using actionview 4.1.16 from source at `/home/rubys/git/rails`
Using activemodel 4.1.16 from source at `/home/rubys/git/rails`
Using jbuilder 2.6.4
Using actionpack 4.1.16 from source at `/home/rubys/git/rails`
Using activerecord 4.1.16 from source at `/home/rubys/git/rails`
Using actionmailer 4.1.16 from source at `/home/rubys/git/rails`
Using railties 4.1.16 from source at `/home/rubys/git/rails`
Using sprockets-rails 2.3.3
Using coffee-rails 4.0.1
Using jquery-rails 3.1.4
Using jquery-ui-rails 6.0.1
Using rails 4.1.16 from source at `/home/rubys/git/rails`
Using sass-rails 4.0.5
Bundle complete! 13 Gemfile dependencies, 47 gems now installed.
Use `bundle info [gemname]` to see where a bundled gem is installed.
edit config/application.rb
require File.expand_path('../boot', __FILE__)
require 'rails/all'
# Require the gems listed in Gemfile, including any gems
# you've limited to :test, :development, or :production.
Bundler.require(*Rails.groups)
module Depot
class Application < Rails::Application
# Settings in config/environments/* take precedence over those specified here.
# Application configuration should go into files in config/initializers
# -- all .rb files in that directory are automatically loaded.
# Set Time.zone default to the specified zone and make Active Record auto-convert to this zone.
# Run "rake -D time" for a list of tasks for finding time zone names. Default is UTC.
# config.time_zone = 'Central Time (US & Canada)'
# The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded.
# config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s]
# config.i18n.default_locale = :de
config.middleware.use I18n::JS::Middleware
end
end
Restart the server.
edit app/assets/javascripts/application.js
// This is a manifest file that'll be compiled into application.js, which will
// include all the files listed below.
//
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts,
// vendor/assets/javascripts, or vendor/assets/javascripts of plugins, if any,
// can be referenced here using a relative path.
//
// It's not advisable to add code directly here, but if you do, it'll appear at
// the bottom of the compiled file.
//
// Read Sprockets README
// (https://github.com/rails/sprockets#sprockets-directives) for details about
// supported directives.
//
//= require jquery
//= require jquery-ui/effects/effect-blind
//= require jquery_ujs
//= require turbolinks
//= require i18n
//= require i18n/translations
//= require_tree .
edit app/views/layouts/application.html.erb
<!DOCTYPE html>
<html>
<head>
<title>Pragprog Books Online Store</title>
<%= csrf_meta_tags %>
<%= csrf_meta_tags %>
<%= stylesheet_link_tag 'application', media: 'all',
'data-turbolinks-track': 'reload' %>
<%= javascript_include_tag 'application', 'data-turbolinks-track': 'reload' %>
<script type="text/javascript">
I18n.defaultLocale = "<%= I18n.default_locale %>";
I18n.locale = "<%= I18n.locale %>";
</script>
</head>
<body class="<%= controller.controller_name %>">
<div id="banner">
<%= image_tag 'logo.svg', alt: 'The Pragmatic Bookshelf' %>
<span class="title"><%= @page_title %></span>
</div>
<div id="columns">
<div id="side">
<% if @cart %>
<%= hidden_div_if(@cart.line_items.empty?, id: 'cart') do %>
<%= render @cart %>
<% end %>
<% end %>
<%= render Order.find(session[:order_id]) if session[:order_id] -%>
<ul>
<li><a href="http://www...."><%= t('.home') %></a></li>
<li><a href="http://www..../faq"><%= t('.questions') %></a></li>
<li><a href="http://www..../news"><%= t('.news') %></a></li>
<li><a href="http://www..../contact"><%= t('.contact') %></a></li>
</ul>
<% if session[:user_id] %>
<ul>
<li><%= link_to 'Orders', orders_path %></li>
<li><%= link_to 'Products', products_path %></li>
<li><%= link_to 'Users', users_path %></li>
</ul>
<%= button_to 'Logout', logout_path, method: :delete %>
<% end %>
</div>
<div id="main">
<%= yield %>
</div>
</div>
</body>
</html>
edit app/javascript/PayTypeSelector/index.jsx
#<Errno::ENOENT: No such file or directory @ rb_sysopen - app/javascript/PayTypeSelector/index.jsx>
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `initialize'
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `open'
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `edit'
makedepot.rb:4282:in `block in <main>'
/home/rubys/git/gorp/lib/gorp/output.rb:59:in `call'
/home/rubys/git/gorp/lib/gorp/output.rb:59:in `block (4 levels) in <top (required)>'
/home/rubys/git/gorp/lib/gorp/output.rb:49:in `each'
/home/rubys/git/gorp/lib/gorp/output.rb:49:in `block (3 levels) in <top (required)>'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `call'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:68:in `tag!'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:93:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:22:in `block (2 levels) in <top (required)>'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `call'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:68:in `tag!'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:93:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:11:in `block in <top (required)>'
edit app/javascript/PayTypeSelector/CheckPayType.jsx
#<Errno::ENOENT: No such file or directory @ rb_sysopen - app/javascript/PayTypeSelector/CheckPayType.jsx>
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `initialize'
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `open'
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `edit'
makedepot.rb:4310:in `block in <main>'
/home/rubys/git/gorp/lib/gorp/output.rb:59:in `call'
/home/rubys/git/gorp/lib/gorp/output.rb:59:in `block (4 levels) in <top (required)>'
/home/rubys/git/gorp/lib/gorp/output.rb:49:in `each'
/home/rubys/git/gorp/lib/gorp/output.rb:49:in `block (3 levels) in <top (required)>'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `call'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:68:in `tag!'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:93:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:22:in `block (2 levels) in <top (required)>'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `call'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:68:in `tag!'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:93:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:11:in `block in <top (required)>'
edit app/javascript/PayTypeSelector/CreditCardPayType.jsx
#<Errno::ENOENT: No such file or directory @ rb_sysopen - app/javascript/PayTypeSelector/CreditCardPayType.jsx>
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `initialize'
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `open'
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `edit'
makedepot.rb:4322:in `block in <main>'
/home/rubys/git/gorp/lib/gorp/output.rb:59:in `call'
/home/rubys/git/gorp/lib/gorp/output.rb:59:in `block (4 levels) in <top (required)>'
/home/rubys/git/gorp/lib/gorp/output.rb:49:in `each'
/home/rubys/git/gorp/lib/gorp/output.rb:49:in `block (3 levels) in <top (required)>'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `call'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:68:in `tag!'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:93:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:22:in `block (2 levels) in <top (required)>'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `call'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:68:in `tag!'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:93:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:11:in `block in <top (required)>'
edit app/javascript/PayTypeSelector/PurchaseOrderPayType.jsx
#<Errno::ENOENT: No such file or directory @ rb_sysopen - app/javascript/PayTypeSelector/PurchaseOrderPayType.jsx>
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `initialize'
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `open'
/home/rubys/git/gorp/lib/gorp/edit.rb:177:in `edit'
makedepot.rb:4334:in `block in <main>'
/home/rubys/git/gorp/lib/gorp/output.rb:59:in `call'
/home/rubys/git/gorp/lib/gorp/output.rb:59:in `block (4 levels) in <top (required)>'
/home/rubys/git/gorp/lib/gorp/output.rb:49:in `each'
/home/rubys/git/gorp/lib/gorp/output.rb:49:in `block (3 levels) in <top (required)>'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `call'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:68:in `tag!'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:93:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:22:in `block (2 levels) in <top (required)>'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `call'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:175:in `_nested_structures'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:68:in `tag!'
/home/rubys/.rvm/gems/ruby-2.2.6/gems/builder-3.2.3/lib/builder/xmlbase.rb:93:in `method_missing'
/home/rubys/git/gorp/lib/gorp/output.rb:11:in `block in <top (required)>'
Restart the server.
Define some translations for the new order.
edit config/locales/en.yml
en:
orders:
new:
legend: "Please Enter Your Details"
form:
name: "Name"
address_html: "Address"
email: "E-mail"
pay_type: "Pay with"
pay_prompt_html: "Select a payment method"
submit: "Place Order"
pay_types:
check: "Check"
credit_card: "Credit Card"
purchase_order: "Purchase Order"
check_pay_type:
routing_number: "Routing #"
account_number: "Account #"
credit_card_pay_type:
cc_number: "CC #"
expiration_date: "Expiry"
purchase_order_pay_type:
po_number: "PO #"
edit config/locales/es.yml
es:
orders:
new:
legend: "Por favor, introduzca sus datos"
form:
name: "Nombre"
address_html: "Dirección"
email: "E-mail"
pay_type: "Forma de pago"
pay_prompt_html: "Seleccione un método de pago"
submit: "Realizar Pedido"
pay_types:
check: "Cheque"
credit_card: "Tarjeta de Crédito"
purchase_order: "Orden de Compra"
check_pay_type:
routing_number: "# de Enrutamiento"
account_number: "# de Cuenta"
credit_card_pay_type:
cc_number: "Número"
expiration_date: "Expiración"
purchase_order_pay_type:
po_number: "Número"
Add to cart
get /es
Carrito de la Compra
1×
Rails, Angular, Postgres, and Bootstrap
45,00 $US
Total
45,00 $US
Su Catálogo de Pragmatic
Rails, Angular, Postgres, and Bootstrap
Powerful, Effective, and Efficient Full-Stack Web Development
As a Rails developer, you care about user experience and performance,
but you also want simple and maintainable code. Achieve all that by
embracing the full stack of web development, from styling with
Bootstrap, building an interactive user interface with AngularJS, to
storing data quickly and reliably in PostgreSQL. Take a holistic view of
full-stack development to create usable, high-performing applications,
and learn to use these technologies effectively in a Ruby on Rails
environment.
Ruby Performance Optimization
Why Ruby Is Slow, and How to Fix It
You don’t have to accept slow Ruby or Rails performance. In this
comprehensive guide to Ruby optimization, you’ll learn how to write
faster Ruby code—but that’s just the beginning. See exactly what makes
Ruby and Rails code slow, and how to fix it. Alex Dymo will guide you
through perils of memory and CPU optimization, profiling, measuring,
performance testing, garbage collection, and tuning. You’ll find that
all those “hard” things aren’t so difficult after all, and your code
will run orders of magnitude faster.
Seven Mobile Apps in Seven Weeks
Native Apps, Multiple Platforms
Answer the question “Can we build this for ALL the devices?” with a
resounding YES. This book will help you get there with a real-world
introduction to seven platforms, whether you’re new to mobile or an
experienced developer needing to expand your options. Plus, you’ll find
out which cross-platform solution makes the most sense for your needs.
post /es/line_items?product_id=2
You are being
redirected .
get http://localhost:3000/es/store/index
Carrito de la Compra
2×
Rails, Angular, Postgres, and Bootstrap
90,00 $US
Total
90,00 $US
Su Catálogo de Pragmatic
Rails, Angular, Postgres, and Bootstrap
Powerful, Effective, and Efficient Full-Stack Web Development
As a Rails developer, you care about user experience and performance,
but you also want simple and maintainable code. Achieve all that by
embracing the full stack of web development, from styling with
Bootstrap, building an interactive user interface with AngularJS, to
storing data quickly and reliably in PostgreSQL. Take a holistic view of
full-stack development to create usable, high-performing applications,
and learn to use these technologies effectively in a Ruby on Rails
environment.
Ruby Performance Optimization
Why Ruby Is Slow, and How to Fix It
You don’t have to accept slow Ruby or Rails performance. In this
comprehensive guide to Ruby optimization, you’ll learn how to write
faster Ruby code—but that’s just the beginning. See exactly what makes
Ruby and Rails code slow, and how to fix it. Alex Dymo will guide you
through perils of memory and CPU optimization, profiling, measuring,
performance testing, garbage collection, and tuning. You’ll find that
all those “hard” things aren’t so difficult after all, and your code
will run orders of magnitude faster.
Seven Mobile Apps in Seven Weeks
Native Apps, Multiple Platforms
Answer the question “Can we build this for ALL the devices?” with a
resounding YES. This book will help you get there with a real-world
introduction to seven platforms, whether you’re new to mobile or an
experienced developer needing to expand your options. Plus, you’ll find
out which cross-platform solution makes the most sense for your needs.
Show mixed validation errors
get /es/orders/new
Carrito de la Compra
2×
Rails, Angular, Postgres, and Bootstrap
90,00 $US
Total
90,00 $US
post /es/orders
order[name] =>
submit => Realizar Pedido
Translate the errors to human names.
edit config/locales/es.yml
es:
activerecord:
errors:
messages:
inclusion: "no está incluido en la lista"
blank: "no puede quedar en blanco"
errors:
template:
body: "Hay problemas con los siguientes campos:"
header:
one: "1 error ha impedido que este %{model} se guarde"
other: "%{count} errores han impedido que este %{model} se guarde"
Display messages in raw form, and translate error messages
edit app/views/orders/_form.html.erb
<%= form_for(@order) do |f| %>
<% if @order.errors.any? %>
<div id="error_explanation">
<h2><%=raw t('errors.template.header', count: @order.errors.count,
model: t('activerecord.models.order')) %>.</h2>
<p><%= t('errors.template.body') %></p>
<ul>
<% @order.errors.full_messages.each do |message| %>
<li><%=raw message %></li>
<% end %>
</ul>
</div>
<% end %>
<!-- ... -->
<div class="field">
<%= f.label :name, t('.name') %><br>
<%= f.text_field :name, size: 40 %>
</div>
<div class="field">
<%= f.label :address, t('.address_html') %><br>
<%= f.text_area :address, rows: 3, cols: 40 %>
</div>
<div class="field">
<%= f.label :email, t('.email') %><br>
<%= f.email_field :email, size: 40 %>
</div>
<div class="field">
<%= f.label :pay_type, t('.pay_type') %><br>
<%= f.select :pay_type, Order.pay_types.keys,
prompt: t('.pay_prompt_html') %>
</div>
<div class="actions">
<%= f.submit t('.submit') %>
</div>
<% end %>
Translate the model names to human names.
edit config/locales/es.yml
es:
activerecord:
models:
order: "pedido"
attributes:
order:
address: "Dirección"
name: "Nombre"
email: "E-mail"
pay_type: "Forma de pago"
Show validation errors
get /es/orders/new
post /es/orders
order[name] =>
submit => Realizar Pedido
Replace translatable text with calls out to translation functions.
edit app/controllers/orders_controller.rb
def create
@order = Order.new(order_params)
@order.add_line_items_from_cart(@cart)
respond_to do |format|
if @order.save
Cart.destroy(session[:cart_id])
session[:cart_id] = nil
session[:order_id] = @order.id
OrderMailer.received(@order).deliver
format.html { redirect_to store_index_url(locale: I18n.locale),
notice: I18n.t('.thanks') }
format.json { render :show, status: :created,
location: @order }
else
format.html { render :new }
format.json { render json: @order.errors,
status: :unprocessable_entity }
end
end
end
Modify the test to reflect the new redirect
edit test/controllers/orders_controller_test.rb
test "should create order" do
assert_difference('Order.count') do
post :create, order: { address: @order.address,
email: @order.email, name: @order.name,
pay_type: @order.pay_type }
end
assert_redirected_to store_index_path(locale: 'en')
end
Define some translations for the flash.
edit config/locales/en.yml
en:
thanks: "Thank you for your order"
edit config/locales/es.yml
es:
thanks: "Gracias por su pedido"
Place the order
get /es/orders/new
post /es/orders
order[name] => Joe User
order[address] => 123 Main St., Anytown USA
order[email] => juser@hotmail.com
order[pay_type] => Check
You are being
redirected .
get http://localhost:3000/es/store/index
Gracias por su pedido
Su Catálogo de Pragmatic
Rails, Angular, Postgres, and Bootstrap
Powerful, Effective, and Efficient Full-Stack Web Development
As a Rails developer, you care about user experience and performance,
but you also want simple and maintainable code. Achieve all that by
embracing the full stack of web development, from styling with
Bootstrap, building an interactive user interface with AngularJS, to
storing data quickly and reliably in PostgreSQL. Take a holistic view of
full-stack development to create usable, high-performing applications,
and learn to use these technologies effectively in a Ruby on Rails
environment.
Ruby Performance Optimization
Why Ruby Is Slow, and How to Fix It
You don’t have to accept slow Ruby or Rails performance. In this
comprehensive guide to Ruby optimization, you’ll learn how to write
faster Ruby code—but that’s just the beginning. See exactly what makes
Ruby and Rails code slow, and how to fix it. Alex Dymo will guide you
through perils of memory and CPU optimization, profiling, measuring,
performance testing, garbage collection, and tuning. You’ll find that
all those “hard” things aren’t so difficult after all, and your code
will run orders of magnitude faster.
Seven Mobile Apps in Seven Weeks
Native Apps, Multiple Platforms
Answer the question “Can we build this for ALL the devices?” with a
resounding YES. This book will help you get there with a real-world
introduction to seven platforms, whether you’re new to mobile or an
experienced developer needing to expand your options. Plus, you’ll find
out which cross-platform solution makes the most sense for your needs.
16.4 Task K4: Add a locale switcher.
16.2 Task K2: translating the store front